platform/x86: asus-wmi: Add fn-lock mode switch support
authorChris Chiu <chiu@endlessm.com>
Thu, 18 Apr 2019 06:46:48 +0000 (14:46 +0800)
committerAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Wed, 8 May 2019 21:33:03 +0000 (00:33 +0300)
Some of latest ASUS laptops support new fn-lock mode switching.
This commit detect whether if the fn-lock option is enabled in
BIOS setting, and toggle the fn-lock mode via a new WMI DEVID
0x00100023 when the corresponding notify code captured.

The ASUS fn-lock mode switch is activated by pressing Fn+Esc.
When on, keys F1 to F12 behave as applicable, with meanings
defined by the application being used at the time. When off,
F1 to F12 directly triggers hardware features, well known audio
volume up/down, brightness up/down...etc, which were triggered
by holding down Fn key and F-keys.

Because there's no way to retrieve the fn-lock mode via existing
WMI methods per ASUS spec, driver need to initialize and keep the
fn-lock mode by itself.

Signed-off-by: Chris Chiu <chiu@endlessm.com>
Reviewed-by: Daniel Drake <drake@endlessm.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
drivers/platform/x86/asus-wmi.c
include/linux/platform_data/x86/asus-wmi.h

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;
 }
 
index 53dfc25419603e666d1d6da8fad9a0ef419b36e7..bfba245636a715cb25b6d154979f403e319a6a5d 100644 (file)
@@ -67,6 +67,7 @@
 /* Input */
 #define ASUS_WMI_DEVID_TOUCHPAD                0x00100011
 #define ASUS_WMI_DEVID_TOUCHPAD_LED    0x00100012
+#define ASUS_WMI_DEVID_FNLOCK          0x00100023
 
 /* Fan, Thermal */
 #define ASUS_WMI_DEVID_THERMAL_CTRL    0x00110011