Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[pandora-kernel.git] / drivers / platform / x86 / ideapad-laptop.c
index 42b9ba7..0c59541 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/input.h>
 #include <linux/input/sparse-keymap.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
 
 #define IDEAPAD_RFKILL_DEV_NUM (3)
 
 #define CFG_BT_BIT     (16)
 #define CFG_3G_BIT     (17)
 #define CFG_WIFI_BIT   (18)
+#define CFG_CAMERA_BIT (19)
 
 struct ideapad_private {
        struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
        struct platform_device *platform_device;
        struct input_dev *inputdev;
+       struct backlight_device *blightdev;
        unsigned long cfg;
 };
 
@@ -208,7 +212,24 @@ static struct attribute *ideapad_attributes[] = {
        NULL
 };
 
+static mode_t ideapad_is_visible(struct kobject *kobj,
+                                struct attribute *attr,
+                                int idx)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct ideapad_private *priv = dev_get_drvdata(dev);
+       bool supported;
+
+       if (attr == &dev_attr_camera_power.attr)
+               supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));
+       else
+               supported = true;
+
+       return supported ? attr->mode : 0;
+}
+
 static struct attribute_group ideapad_attribute_group = {
+       .is_visible = ideapad_is_visible,
        .attrs = ideapad_attributes
 };
 
@@ -291,8 +312,7 @@ static int __devinit ideapad_register_rfkill(struct acpi_device *adevice,
        return 0;
 }
 
-static void __devexit ideapad_unregister_rfkill(struct acpi_device *adevice,
-                                               int dev)
+static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)
 {
        struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
 
@@ -386,7 +406,7 @@ err_free_dev:
        return error;
 }
 
-static void __devexit ideapad_input_exit(struct ideapad_private *priv)
+static void ideapad_input_exit(struct ideapad_private *priv)
 {
        sparse_keymap_free(priv->inputdev);
        input_unregister_device(priv->inputdev);
@@ -399,6 +419,98 @@ static void ideapad_input_report(struct ideapad_private *priv,
        sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
 }
 
+/*
+ * backlight
+ */
+static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
+{
+       unsigned long now;
+
+       if (read_ec_data(ideapad_handle, 0x12, &now))
+               return -EIO;
+       return now;
+}
+
+static int ideapad_backlight_update_status(struct backlight_device *blightdev)
+{
+       if (write_ec_cmd(ideapad_handle, 0x13, blightdev->props.brightness))
+               return -EIO;
+       if (write_ec_cmd(ideapad_handle, 0x33,
+                        blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))
+               return -EIO;
+
+       return 0;
+}
+
+static const struct backlight_ops ideapad_backlight_ops = {
+       .get_brightness = ideapad_backlight_get_brightness,
+       .update_status = ideapad_backlight_update_status,
+};
+
+static int ideapad_backlight_init(struct ideapad_private *priv)
+{
+       struct backlight_device *blightdev;
+       struct backlight_properties props;
+       unsigned long max, now, power;
+
+       if (read_ec_data(ideapad_handle, 0x11, &max))
+               return -EIO;
+       if (read_ec_data(ideapad_handle, 0x12, &now))
+               return -EIO;
+       if (read_ec_data(ideapad_handle, 0x18, &power))
+               return -EIO;
+
+       memset(&props, 0, sizeof(struct backlight_properties));
+       props.max_brightness = max;
+       props.type = BACKLIGHT_PLATFORM;
+       blightdev = backlight_device_register("ideapad",
+                                             &priv->platform_device->dev,
+                                             priv,
+                                             &ideapad_backlight_ops,
+                                             &props);
+       if (IS_ERR(blightdev)) {
+               pr_err("Could not register backlight device\n");
+               return PTR_ERR(blightdev);
+       }
+
+       priv->blightdev = blightdev;
+       blightdev->props.brightness = now;
+       blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
+       backlight_update_status(blightdev);
+
+       return 0;
+}
+
+static void ideapad_backlight_exit(struct ideapad_private *priv)
+{
+       if (priv->blightdev)
+               backlight_device_unregister(priv->blightdev);
+       priv->blightdev = NULL;
+}
+
+static void ideapad_backlight_notify_power(struct ideapad_private *priv)
+{
+       unsigned long power;
+       struct backlight_device *blightdev = priv->blightdev;
+
+       if (read_ec_data(ideapad_handle, 0x18, &power))
+               return;
+       blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
+}
+
+static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
+{
+       unsigned long now;
+
+       /* if we control brightness via acpi video driver */
+       if (priv->blightdev == NULL) {
+               read_ec_data(ideapad_handle, 0x12, &now);
+               return;
+       }
+
+       backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY);
+}
+
 /*
  * module init/exit
  */
@@ -440,8 +552,18 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
        }
        ideapad_sync_rfk_state(adevice);
 
+       if (!acpi_video_backlight_support()) {
+               ret = ideapad_backlight_init(priv);
+               if (ret && ret != -ENODEV)
+                       goto backlight_failed;
+       }
+
        return 0;
 
+backlight_failed:
+       for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
+               ideapad_unregister_rfkill(adevice, i);
+       ideapad_input_exit(priv);
 input_failed:
        ideapad_platform_exit(priv);
 platform_failed:
@@ -454,6 +576,7 @@ static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type)
        struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
        int i;
 
+       ideapad_backlight_exit(priv);
        for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
                ideapad_unregister_rfkill(adevice, i);
        ideapad_input_exit(priv);
@@ -478,12 +601,19 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
        vpc1 = (vpc2 << 8) | vpc1;
        for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
                if (test_bit(vpc_bit, &vpc1)) {
-                       if (vpc_bit == 9)
+                       switch (vpc_bit) {
+                       case 9:
                                ideapad_sync_rfk_state(adevice);
-                       else if (vpc_bit == 4)
-                               read_ec_data(handle, 0x12, &vpc2);
-                       else
+                               break;
+                       case 4:
+                               ideapad_backlight_notify_brightness(priv);
+                               break;
+                       case 2:
+                               ideapad_backlight_notify_power(priv);
+                               break;
+                       default:
                                ideapad_input_report(priv, vpc_bit);
+                       }
                }
        }
 }