asus-laptop: Add support for Keyboard backlight
[profile/ivi/kernel-adaptation-intel-automotive.git] / drivers / platform / x86 / asus-laptop.c
index bfc1a88..0fb4e59 100644 (file)
@@ -33,6 +33,8 @@
  *  Sam Lin        - GPS support
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #define ASUS_HOTK_NAME          "Asus Laptop Support"
 #define ASUS_HOTK_CLASS         "hotkey"
 #define ASUS_HOTK_DEVICE_NAME   "Hotkey"
-#define ASUS_HOTK_FILE          "asus-laptop"
+#define ASUS_HOTK_FILE          KBUILD_MODNAME
 #define ASUS_HOTK_PREFIX        "\\_SB.ATKD."
 
+
 /*
  * Some events we use, same for all Asus
  */
@@ -83,6 +86,7 @@
 #define GLED_ON     0x40       //Gaming LED
 #define LCD_ON      0x80       //LCD backlight
 #define GPS_ON      0x100      //GPS
+#define KEY_ON      0x200       //Keyboard backlight
 
 #define ASUS_LOG    ASUS_HOTK_FILE ": "
 #define ASUS_ERR    KERN_ERR    ASUS_LOG
@@ -169,6 +173,10 @@ ASUS_HANDLE(gps_on, ASUS_HOTK_PREFIX "SDON");      /* R2H */
 ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF"); /* R2H */
 ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST");
 
+/* Keyboard light */
+ASUS_HANDLE(kled_set, ASUS_HOTK_PREFIX "SLKB");
+ASUS_HANDLE(kled_get, ASUS_HOTK_PREFIX "GLKB");
+
 /*
  * This is the main structure, we can use it to store anything interesting
  * about the hotk device
@@ -207,13 +215,17 @@ MODULE_DEVICE_TABLE(acpi, asus_device_ids);
 
 static int asus_hotk_add(struct acpi_device *device);
 static int asus_hotk_remove(struct acpi_device *device, int type);
+static void asus_hotk_notify(struct acpi_device *device, u32 event);
+
 static struct acpi_driver asus_hotk_driver = {
        .name = ASUS_HOTK_NAME,
        .class = ASUS_HOTK_CLASS,
        .ids = asus_device_ids,
+       .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
        .ops = {
                .add = asus_hotk_add,
                .remove = asus_hotk_remove,
+               .notify = asus_hotk_notify,
                },
 };
 
@@ -236,22 +248,27 @@ static struct backlight_ops asusbl_ops = {
  * potentially bad time, such as a timer interrupt. */
 static struct workqueue_struct *led_workqueue;
 
-#define ASUS_LED(object, ledname)                                      \
+#define ASUS_LED(object, ledname, max)                                 \
        static void object##_led_set(struct led_classdev *led_cdev,     \
                                     enum led_brightness value);        \
+       static enum led_brightness object##_led_get(                    \
+               struct led_classdev *led_cdev);                         \
        static void object##_led_update(struct work_struct *ignored);   \
        static int object##_led_wk;                                     \
        static DECLARE_WORK(object##_led_work, object##_led_update);    \
        static struct led_classdev object##_led = {                     \
                .name           = "asus::" ledname,                     \
                .brightness_set = object##_led_set,                     \
+               .brightness_get = object##_led_get,                     \
+               .max_brightness = max                                   \
        }
 
-ASUS_LED(mled, "mail");
-ASUS_LED(tled, "touchpad");
-ASUS_LED(rled, "record");
-ASUS_LED(pled, "phone");
-ASUS_LED(gled, "gaming");
+ASUS_LED(mled, "mail", 1);
+ASUS_LED(tled, "touchpad", 1);
+ASUS_LED(rled, "record", 1);
+ASUS_LED(pled, "phone", 1);
+ASUS_LED(gled, "gaming", 1);
+ASUS_LED(kled, "kbd_backlight", 3);
 
 struct key_entry {
        char type;
@@ -275,6 +292,9 @@ static struct key_entry asus_keymap[] = {
        {KE_KEY, 0x51, KEY_WWW},
        {KE_KEY, 0x5C, KEY_SCREENLOCK},  /* Screenlock */
        {KE_KEY, 0x5D, KEY_WLAN},
+       {KE_KEY, 0x5E, KEY_WLAN},
+       {KE_KEY, 0x5F, KEY_WLAN},
+       {KE_KEY, 0x60, KEY_SWITCHVIDEOMODE},
        {KE_KEY, 0x61, KEY_SWITCHVIDEOMODE},
        {KE_KEY, 0x6B, BTN_TOUCH}, /* Lock Mouse */
        {KE_KEY, 0x82, KEY_CAMERA},
@@ -323,7 +343,7 @@ static int read_wireless_status(int mask)
 
        rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status);
        if (ACPI_FAILURE(rv))
-               printk(ASUS_WARNING "Error reading Wireless status\n");
+               pr_warning("Error reading Wireless status\n");
        else
                return (status & mask) ? 1 : 0;
 
@@ -337,7 +357,7 @@ static int read_gps_status(void)
 
        rv = acpi_evaluate_integer(gps_status_handle, NULL, NULL, &status);
        if (ACPI_FAILURE(rv))
-               printk(ASUS_WARNING "Error reading GPS status\n");
+               pr_warning("Error reading GPS status\n");
        else
                return status ? 1 : 0;
 
@@ -377,7 +397,7 @@ static void write_status(acpi_handle handle, int out, int mask)
        }
 
        if (write_acpi_int(handle, NULL, out, NULL))
-               printk(ASUS_WARNING " write failed %x\n", mask);
+               pr_warning(" write failed %x\n", mask);
 }
 
 /* /sys/class/led handlers */
@@ -392,6 +412,11 @@ static void write_status(acpi_handle handle, int out, int mask)
        {                                                               \
                int value = object##_led_wk;                            \
                write_status(object##_set_handle, value, (mask));       \
+       }                                                               \
+       static enum led_brightness object##_led_get(                    \
+               struct led_classdev *led_cdev)                          \
+       {                                                               \
+               return led_cdev->brightness;                            \
        }
 
 ASUS_LED_HANDLER(mled, MLED_ON);
@@ -400,6 +425,60 @@ ASUS_LED_HANDLER(rled, RLED_ON);
 ASUS_LED_HANDLER(tled, TLED_ON);
 ASUS_LED_HANDLER(gled, GLED_ON);
 
+/*
+ * Keyboard backlight
+ */
+static int get_kled_lvl(void)
+{
+       unsigned long long kblv;
+       struct acpi_object_list params;
+       union acpi_object in_obj;
+       acpi_status rv;
+
+       params.count = 1;
+       params.pointer = &in_obj;
+       in_obj.type = ACPI_TYPE_INTEGER;
+       in_obj.integer.value = 2;
+
+       rv = acpi_evaluate_integer(kled_get_handle, NULL, &params, &kblv);
+       if (ACPI_FAILURE(rv)) {
+               pr_warning("Error reading kled level\n");
+               return 0;
+       }
+       return kblv;
+}
+
+static int set_kled_lvl(int kblv)
+{
+       if (kblv > 0)
+               kblv = (1 << 7) | (kblv & 0x7F);
+       else
+               kblv = 0;
+
+       if (write_acpi_int(kled_set_handle, NULL, kblv, NULL)) {
+               pr_warning("Keyboard LED display write failed\n");
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static void kled_led_set(struct led_classdev *led_cdev,
+                        enum led_brightness value)
+{
+       kled_led_wk = value;
+       queue_work(led_workqueue, &kled_led_work);
+}
+
+static void kled_led_update(struct work_struct *ignored)
+{
+       set_kled_lvl(kled_led_wk);
+}
+
+static enum led_brightness kled_led_get(struct led_classdev *led_cdev)
+{
+       return get_kled_lvl();
+}
+
 static int get_lcd_state(void)
 {
        return read_status(LCD_ON);
@@ -420,7 +499,7 @@ static int set_lcd_state(int value)
                                              NULL, NULL, NULL);
 
                if (ACPI_FAILURE(status))
-                       printk(ASUS_WARNING "Error switching LCD\n");
+                       pr_warning("Error switching LCD\n");
        }
 
        write_status(NULL, lcd, LCD_ON);
@@ -444,7 +523,7 @@ static int read_brightness(struct backlight_device *bd)
 
        rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value);
        if (ACPI_FAILURE(rv))
-               printk(ASUS_WARNING "Error reading brightness\n");
+               pr_warning("Error reading brightness\n");
 
        return value;
 }
@@ -457,7 +536,7 @@ static int set_brightness(struct backlight_device *bd, int value)
        /* 0 <= value <= 15 */
 
        if (write_acpi_int(brightness_set_handle, NULL, value, NULL)) {
-               printk(ASUS_WARNING "Error changing brightness\n");
+               pr_warning("Error changing brightness\n");
                ret = -EIO;
        }
 
@@ -509,7 +588,17 @@ static ssize_t show_infos(struct device *dev,
         */
        rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp);
        if (!ACPI_FAILURE(rv))
-               len += sprintf(page + len, "SFUN value         : 0x%04x\n",
+               len += sprintf(page + len, "SFUN value         : %#x\n",
+                              (uint) temp);
+       /*
+        * The HWRS method return informations about the hardware.
+        * 0x80 bit is for WLAN, 0x100 for Bluetooth.
+        * The significance of others is yet to be found.
+        * If we don't find the method, we assume the device are present.
+        */
+       rv = acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &temp);
+       if (!ACPI_FAILURE(rv))
+               len += sprintf(page + len, "HRWS value         : %#x\n",
                               (uint) temp);
        /*
         * Another value for userspace: the ASYM method returns 0x02 for
@@ -520,7 +609,7 @@ static ssize_t show_infos(struct device *dev,
         */
        rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp);
        if (!ACPI_FAILURE(rv))
-               len += sprintf(page + len, "ASYM value         : 0x%04x\n",
+               len += sprintf(page + len, "ASYM value         : %#x\n",
                               (uint) temp);
        if (asus_info) {
                snprintf(buf, 16, "%d", asus_info->length);
@@ -587,7 +676,7 @@ static ssize_t store_ledd(struct device *dev, struct device_attribute *attr,
        rv = parse_arg(buf, count, &value);
        if (rv > 0) {
                if (write_acpi_int(ledd_set_handle, NULL, value, NULL))
-                       printk(ASUS_WARNING "LED display write failed\n");
+                       pr_warning("LED display write failed\n");
                else
                        hotk->ledd_status = (u32) value;
        }
@@ -632,7 +721,7 @@ static void set_display(int value)
 {
        /* no sanity check needed for now */
        if (write_acpi_int(display_set_handle, NULL, value, NULL))
-               printk(ASUS_WARNING "Error setting display\n");
+               pr_warning("Error setting display\n");
        return;
 }
 
@@ -647,7 +736,7 @@ static int read_display(void)
                rv = acpi_evaluate_integer(display_get_handle, NULL,
                                           NULL, &value);
                if (ACPI_FAILURE(rv))
-                       printk(ASUS_WARNING "Error reading display status\n");
+                       pr_warning("Error reading display status\n");
        }
 
        value &= 0x0F;          /* needed for some models, shouldn't hurt others */
@@ -689,7 +778,7 @@ static ssize_t store_disp(struct device *dev, struct device_attribute *attr,
 static void set_light_sens_switch(int value)
 {
        if (write_acpi_int(ls_switch_handle, NULL, value, NULL))
-               printk(ASUS_WARNING "Error setting light sensor switch\n");
+               pr_warning("Error setting light sensor switch\n");
        hotk->light_switch = value;
 }
 
@@ -714,7 +803,7 @@ static ssize_t store_lssw(struct device *dev, struct device_attribute *attr,
 static void set_light_sens_level(int value)
 {
        if (write_acpi_int(ls_level_handle, NULL, value, NULL))
-               printk(ASUS_WARNING "Error setting light sensor level\n");
+               pr_warning("Error setting light sensor level\n");
        hotk->light_level = value;
 }
 
@@ -812,7 +901,7 @@ static int asus_setkeycode(struct input_dev *dev, int scancode, int keycode)
        return -EINVAL;
 }
 
-static void asus_hotk_notify(acpi_handle handle, u32 event, void *data)
+static void asus_hotk_notify(struct acpi_device *device, u32 event)
 {
        static struct key_entry *key;
        u16 count;
@@ -975,11 +1064,11 @@ static int asus_hotk_get_info(void)
         */
        status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info);
        if (ACPI_FAILURE(status))
-               printk(ASUS_WARNING "Couldn't get the DSDT table header\n");
+               pr_warning("Couldn't get the DSDT table header\n");
 
        /* We have to write 0 on init this far for all ASUS models */
        if (write_acpi_int(hotk->handle, "INIT", 0, &buffer)) {
-               printk(ASUS_ERR "Hotkey initialization failed\n");
+               pr_err("Hotkey initialization failed\n");
                return -ENODEV;
        }
 
@@ -987,9 +1076,9 @@ static int asus_hotk_get_info(void)
        status =
            acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result);
        if (ACPI_FAILURE(status))
-               printk(ASUS_WARNING "Error calling BSTS\n");
+               pr_warning("Error calling BSTS\n");
        else if (bsts_result)
-               printk(ASUS_NOTICE "BSTS called, 0x%02x returned\n",
+               pr_notice("BSTS called, 0x%02x returned\n",
                       (uint) bsts_result);
 
        /* This too ... */
@@ -1020,7 +1109,7 @@ static int asus_hotk_get_info(void)
                return -ENOMEM;
 
        if (*string)
-               printk(ASUS_NOTICE "  %s model detected\n", string);
+               pr_notice("  %s model detected\n", string);
 
        ASUS_HANDLE_INIT(mled_set);
        ASUS_HANDLE_INIT(tled_set);
@@ -1030,6 +1119,9 @@ static int asus_hotk_get_info(void)
 
        ASUS_HANDLE_INIT(ledd_set);
 
+       ASUS_HANDLE_INIT(kled_set);
+       ASUS_HANDLE_INIT(kled_get);
+
        /*
         * The HWRS method return informations about the hardware.
         * 0x80 bit is for WLAN, 0x100 for Bluetooth.
@@ -1077,7 +1169,7 @@ static int asus_input_init(void)
 
        hotk->inputdev = input_allocate_device();
        if (!hotk->inputdev) {
-               printk(ASUS_INFO "Unable to allocate input device\n");
+               pr_info("Unable to allocate input device\n");
                return 0;
        }
        hotk->inputdev->name = "Asus Laptop extra buttons";
@@ -1096,7 +1188,7 @@ static int asus_input_init(void)
        }
        result = input_register_device(hotk->inputdev);
        if (result) {
-               printk(ASUS_INFO "Unable to register input device\n");
+               pr_info("Unable to register input device\n");
                input_free_device(hotk->inputdev);
        }
        return result;
@@ -1113,7 +1205,7 @@ static int asus_hotk_check(void)
        if (hotk->device->status.present) {
                result = asus_hotk_get_info();
        } else {
-               printk(ASUS_ERR "Hotkey device not present, aborting\n");
+               pr_err("Hotkey device not present, aborting\n");
                return -EINVAL;
        }
 
@@ -1124,13 +1216,12 @@ static int asus_hotk_found;
 
 static int asus_hotk_add(struct acpi_device *device)
 {
-       acpi_status status = AE_OK;
        int result;
 
        if (!device)
                return -EINVAL;
 
-       printk(ASUS_NOTICE "Asus Laptop Support version %s\n",
+       pr_notice("Asus Laptop Support version %s\n",
               ASUS_LAPTOP_VERSION);
 
        hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL);
@@ -1149,15 +1240,6 @@ static int asus_hotk_add(struct acpi_device *device)
 
        asus_hotk_add_fs();
 
-       /*
-        * We install the handler, it will receive the hotk in parameter, so, we
-        * could add other data to the hotk struct
-        */
-       status = acpi_install_notify_handler(hotk->handle, ACPI_ALL_NOTIFY,
-                                            asus_hotk_notify, hotk);
-       if (ACPI_FAILURE(status))
-               printk(ASUS_ERR "Error installing notify handler\n");
-
        asus_hotk_found = 1;
 
        /* WLED and BLED are on by default */
@@ -1171,6 +1253,10 @@ static int asus_hotk_add(struct acpi_device *device)
        /* LCD Backlight is on by default */
        write_status(NULL, 1, LCD_ON);
 
+       /* Keyboard Backlight is on by default */
+       if (kled_set_handle)
+               set_kled_lvl(1);
+
        /* LED display is off by default */
        hotk->ledd_status = 0xFFF;
 
@@ -1198,16 +1284,9 @@ end:
 
 static int asus_hotk_remove(struct acpi_device *device, int type)
 {
-       acpi_status status = 0;
-
        if (!device || !acpi_driver_data(device))
                return -EINVAL;
 
-       status = acpi_remove_notify_handler(hotk->handle, ACPI_ALL_NOTIFY,
-                                           asus_hotk_notify);
-       if (ACPI_FAILURE(status))
-               printk(ASUS_ERR "Error removing notify handler\n");
-
        kfree(hotk->name);
        kfree(hotk);
 
@@ -1232,6 +1311,7 @@ static void asus_led_exit(void)
        ASUS_LED_UNREGISTER(pled);
        ASUS_LED_UNREGISTER(rled);
        ASUS_LED_UNREGISTER(gled);
+       ASUS_LED_UNREGISTER(kled);
 }
 
 static void asus_input_exit(void)
@@ -1260,8 +1340,7 @@ static int asus_backlight_init(struct device *dev)
                bd = backlight_device_register(ASUS_HOTK_FILE, dev,
                                               NULL, &asusbl_ops);
                if (IS_ERR(bd)) {
-                       printk(ASUS_ERR
-                              "Could not register asus backlight device\n");
+                       pr_err("Could not register asus backlight device\n");
                        asus_backlight_device = NULL;
                        return PTR_ERR(bd);
                }
@@ -1312,13 +1391,20 @@ static int asus_led_init(struct device *dev)
        if (rv)
                goto out4;
 
+       if (kled_set_handle && kled_get_handle)
+               rv = ASUS_LED_REGISTER(kled, dev);
+       if (rv)
+               goto out5;
+
        led_workqueue = create_singlethread_workqueue("led_workqueue");
        if (!led_workqueue)
-               goto out5;
+               goto out6;
 
        return 0;
-out5:
+out6:
        rv = -ENOMEM;
+       ASUS_LED_UNREGISTER(kled);
+out5:
        ASUS_LED_UNREGISTER(gled);
 out4:
        ASUS_LED_UNREGISTER(pled);
@@ -1334,7 +1420,6 @@ out:
 
 static int __init asus_laptop_init(void)
 {
-       struct device *dev;
        int result;
 
        if (acpi_disabled)
@@ -1356,24 +1441,10 @@ static int __init asus_laptop_init(void)
                return -ENODEV;
        }
 
-       dev = acpi_get_physical_device(hotk->device->handle);
-
-       if (!acpi_video_backlight_support()) {
-               result = asus_backlight_init(dev);
-               if (result)
-                       goto fail_backlight;
-       } else
-               printk(ASUS_INFO "Brightness ignored, must be controlled by "
-                      "ACPI video driver\n");
-
        result = asus_input_init();
        if (result)
                goto fail_input;
 
-       result = asus_led_init(dev);
-       if (result)
-               goto fail_led;
-
        /* Register platform stuff */
        result = platform_driver_register(&asuspf_driver);
        if (result)
@@ -1394,8 +1465,27 @@ static int __init asus_laptop_init(void)
        if (result)
                goto fail_sysfs;
 
+       result = asus_led_init(&asuspf_device->dev);
+       if (result)
+               goto fail_led;
+
+       if (!acpi_video_backlight_support()) {
+               result = asus_backlight_init(&asuspf_device->dev);
+               if (result)
+                       goto fail_backlight;
+       } else
+               pr_info("Brightness ignored, must be controlled by "
+                      "ACPI video driver\n");
+
        return 0;
 
+fail_backlight:
+       asus_led_exit();
+
+fail_led:
+       sysfs_remove_group(&asuspf_device->dev.kobj,
+                         &asuspf_attribute_group);
+
 fail_sysfs:
        platform_device_del(asuspf_device);
 
@@ -1406,15 +1496,9 @@ fail_platform_device1:
        platform_driver_unregister(&asuspf_driver);
 
 fail_platform_driver:
-       asus_led_exit();
-
-fail_led:
        asus_input_exit();
 
 fail_input:
-       asus_backlight_exit();
-
-fail_backlight:
 
        return result;
 }