Regards,
Philipp
drivers/platform/x86/ideapad-laptop.c | 109 +++++++++++++++++++-------
1 file changed, 80 insertions(+), 29 deletions(-)
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index 33b3dfdd1b08..6d35a9e961cf 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -30,6 +30,7 @@
#include <linux/seq_file.h>
#include <linux/sysfs.h>
#include <linux/types.h>
+#include <linux/wmi.h>
#include <acpi/video.h>
@@ -38,10 +39,19 @@
#define IDEAPAD_RFKILL_DEV_NUM 3
#if IS_ENABLED(CONFIG_ACPI_WMI)
-static const char *const ideapad_wmi_fnesc_events[] = {
- "26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6", /* Yoga 3 */
- "56322276-8493-4CE8-A783-98C991274F5E", /* Yoga 700 */
- "8FC0DE0C-B4E4-43FD-B0F3-8871711C1294", /* Legion 5 */
+enum ideapad_wmi_event_type {
+ IDEAPAD_WMI_EVENT_ESC,
+ IDEAPAD_WMI_EVENT_FN_KEYS,
+};
+
+enum ideapad_wmi_event_type ideapad_wmi_esc = IDEAPAD_WMI_EVENT_ESC,
+enum ideapad_wmi_event_type ideapad_wmi_fn_keys = IDEAPAD_WMI_EVENT_FN_KEYS;
+
+static const struct wmi_device_id ideapad_wmi_id_table[] = {
+ { "26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6", &ideapad_wmi_esc }, /* Yoga 3 */
+ { "56322276-8493-4CE8-A783-98C991274F5E", &ideapad_wmi_esc }, /* Yoga 700 */
+ { "8FC0DE0C-B4E4-43FD-B0F3-8871711C1294", &ideapad_wmi_fn_keys }, /* Legion 5 */
+ {}
};
#endif
@@ -130,7 +140,7 @@ struct ideapad_private {
struct ideapad_dytc_priv *dytc;
struct dentry *debug;
unsigned long cfg;
- const char *fnesc_guid;
+ struct wmi_driver wmi_drv;
struct {
bool conservation_mode : 1;
bool dytc : 1;
@@ -1074,6 +1084,7 @@ static void ideapad_sysfs_exit(struct ideapad_private *priv)
/*
* input device
*/
+#define IDEAPAD_WMI_KEY 0x100
static const struct key_entry ideapad_keymap[] = {
{ KE_KEY, 6, { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, 7, { KEY_CAMERA } },
@@ -1087,6 +1098,26 @@ static const struct key_entry ideapad_keymap[] = {
{ KE_KEY, 66, { KEY_TOUCHPAD_OFF } },
{ KE_KEY, 67, { KEY_TOUCHPAD_ON } },
{ KE_KEY, 128, { KEY_ESC } },
+
+ /*
+ * WMI keys
+ */
+
+ /* FnLock (handled by the firmware) */
+ { KE_IGNORE, 0x02 | IDEAPAD_WMI_KEY },
+ /* Customizable Lenovo Hotkey ("star" with 'S' inside) */
+ { KE_KEY, 0x01 | IDEAPAD_WMI_KEY, { KEY_FAVORITES } },
+ /* Dark mode toggle */
+ { KE_KEY, 0x13 | IDEAPAD_WMI_KEY, { KEY_PROG1 } },
+ /* Sound profile switch */
+ { KE_KEY, 0x12 | IDEAPAD_WMI_KEY, { KEY_PROG2 } },
+ /* Lenovo Virtual Background application */
+ { KE_KEY, 0x28 | IDEAPAD_WMI_KEY, { KEY_PROG3 } },
+ /* Lenovo Support */
+ { KE_KEY, 0x27 | IDEAPAD_WMI_KEY, { KEY_HELP } },
+ /* Refresh Rate Toggle */
+ { KE_KEY, 0x0a | IDEAPAD_WMI_KEY, { KEY_DISPLAYTOGGLE } },
+
{ KE_END },
};
@@ -1491,25 +1522,47 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
}
#if IS_ENABLED(CONFIG_ACPI_WMI)
-static void ideapad_wmi_notify(u32 value, void *context)
+static int ideapad_wmi_probe(struct wmi_device *wdev, const void *context)
{
- struct ideapad_private *priv = context;
+ dev_set_drvdata(&wdev->dev, (void *) context);
+ return 0;
+}
+
+static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data)
+{
+ struct wmi_driver *wdrv = container_of(wdev->dev.driver,
+ struct wmi_driver,
+ driver);
+ struct ideapad_private *priv = container_of(wdrv,
+ struct ideapad_private,
+ wmi_drv);
+ const enum ideapad_wmi_event_type *event = dev_get_drvdata(&wdev->dev);
unsigned long result;
- switch (value) {
- case 128:
- ideapad_input_report(priv, value);
+ switch (*event) {
+ case IDEAPAD_WMI_EVENT_ESC:
+ ideapad_input_report(priv, 128);
break;
- case 208:
+ case IDEAPAD_WMI_EVENT_FN_KEYS:
if (!eval_hals(priv->adev->handle, &result)) {
bool state = test_bit(HALS_FNLOCK_STATE_BIT, &result);
exec_sals(priv->adev->handle, state ? SALS_FNLOCK_ON : SALS_FNLOCK_OFF);
}
+
+ if (data->type != ACPI_TYPE_INTEGER) {
+ dev_warn(&wdev->dev,
+ "WMI event data is not an integer\n");
+ break;
+ }
+
+ dev_dbg(&wdev->dev, "WMI fn-key event: 0x%llx\n",
+ data->integer.value);
+
+ ideapad_input_report(priv,
+ data->integer.value | IDEAPAD_WMI_KEY);
+
break;
- default:
- dev_info(&priv->platform_device->dev,
- "Unknown WMI event: %u\n", value);
}
}
#endif
@@ -1671,25 +1724,24 @@ static int ideapad_acpi_add(struct platform_device *pdev)
}
#if IS_ENABLED(CONFIG_ACPI_WMI)
- for (i = 0; i < ARRAY_SIZE(ideapad_wmi_fnesc_events); i++) {
- status = wmi_install_notify_handler(ideapad_wmi_fnesc_events[i],
- ideapad_wmi_notify, priv);
- if (ACPI_SUCCESS(status)) {
- priv->fnesc_guid = ideapad_wmi_fnesc_events[i];
- break;
- }
- }
+ priv->wmi_drv = (struct wmi_driver) {
+ .driver = {
+ .name = "ideapad-wmi-fn-keys",
+ },
+ .id_table = ideapad_wmi_id_table,
+ .probe = ideapad_wmi_probe,
+ .notify = ideapad_wmi_notify,
+ };
- if (ACPI_FAILURE(status) && status != AE_NOT_EXIST) {
- err = -EIO;
- goto notification_failed_wmi;
- }
+ err = wmi_driver_register(&priv->wmi_drv);
+ if (err)
+ goto register_failed_wmi;
#endif
return 0;
#if IS_ENABLED(CONFIG_ACPI_WMI)
-notification_failed_wmi:
+register_failed_wmi:
acpi_remove_notify_handler(priv->adev->handle,
ACPI_DEVICE_NOTIFY,
ideapad_acpi_notify);
@@ -1720,8 +1772,7 @@ static int ideapad_acpi_remove(struct platform_device *pdev)
int i;
#if IS_ENABLED(CONFIG_ACPI_WMI)
- if (priv->fnesc_guid)
- wmi_remove_notify_handler(priv->fnesc_guid);
+ wmi_driver_unregister(&priv->wmi_drv);
#endif
acpi_remove_notify_handler(priv->adev->handle,
--
2.38.1