platform/x86: asus-wmi: Add fn-lock mode switch support
[sfrench/cifs-2.6.git] / drivers / platform / x86 / asus-wmi.c
index ee1fa93708ec7ba970a0a166938ee97c1a4287de..f94691615881e68515215242fd957d7dff6df393 100644 (file)
@@ -66,10 +66,13 @@ MODULE_LICENSE("GPL");
 #define NOTIFY_BRNUP_MAX               0x1f
 #define NOTIFY_BRNDOWN_MIN             0x20
 #define NOTIFY_BRNDOWN_MAX             0x2e
+#define NOTIFY_FNLOCK_TOGGLE           0x4e
 #define NOTIFY_KBD_BRTUP               0xc4
 #define NOTIFY_KBD_BRTDWN              0xc5
 #define NOTIFY_KBD_BRTTOGGLE           0xc7
 
+#define ASUS_WMI_FNLOCK_BIOS_DISABLED  BIT(0)
+
 #define ASUS_FAN_DESC                  "cpu_fan"
 #define ASUS_FAN_MFUN                  0x13
 #define ASUS_FAN_SFUN_READ             0x06
@@ -177,6 +180,8 @@ struct asus_wmi {
        struct workqueue_struct *hotplug_workqueue;
        struct work_struct hotplug_work;
 
+       bool fnlock_locked;
+
        struct asus_wmi_debug debug;
 
        struct asus_wmi_driver *driver;
@@ -1619,6 +1624,23 @@ static int is_display_toggle(int code)
        return 0;
 }
 
+static bool asus_wmi_has_fnlock_key(struct asus_wmi *asus)
+{
+       u32 result;
+
+       asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FNLOCK, &result);
+
+       return (result & ASUS_WMI_DSTS_PRESENCE_BIT) &&
+               !(result & ASUS_WMI_FNLOCK_BIOS_DISABLED);
+}
+
+static void asus_wmi_fnlock_update(struct asus_wmi *asus)
+{
+       int mode = asus->fnlock_locked;
+
+       asus_wmi_set_devstate(ASUS_WMI_DEVID_FNLOCK, mode, NULL);
+}
+
 static void asus_wmi_notify(u32 value, void *context)
 {
        struct asus_wmi *asus = context;
@@ -1680,6 +1702,12 @@ static void asus_wmi_notify(u32 value, void *context)
                goto exit;
        }
 
+       if (code == NOTIFY_FNLOCK_TOGGLE) {
+               asus->fnlock_locked = !asus->fnlock_locked;
+               asus_wmi_fnlock_update(asus);
+               goto exit;
+       }
+
        if (is_display_toggle(code) &&
            asus->driver->quirks->no_display_toggle)
                goto exit;
@@ -2134,6 +2162,11 @@ static int asus_wmi_add(struct platform_device *pdev)
        } else
                err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, 2, NULL);
 
+       if (asus_wmi_has_fnlock_key(asus)) {
+               asus->fnlock_locked = true;
+               asus_wmi_fnlock_update(asus);
+       }
+
        status = wmi_install_notify_handler(asus->driver->event_guid,
                                            asus_wmi_notify, asus);
        if (ACPI_FAILURE(status)) {
@@ -2213,6 +2246,8 @@ static int asus_hotk_resume(struct device *device)
        if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
                kbd_led_update(asus);
 
+       if (asus_wmi_has_fnlock_key(asus))
+               asus_wmi_fnlock_update(asus);
        return 0;
 }
 
@@ -2249,6 +2284,8 @@ static int asus_hotk_restore(struct device *device)
        if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
                kbd_led_update(asus);
 
+       if (asus_wmi_has_fnlock_key(asus))
+               asus_wmi_fnlock_update(asus);
        return 0;
 }