1 // SPDX-License-Identifier: GPL-2.0+
3 * Intel Virtual Button driver for Windows 8.1+
5 * Copyright (C) 2016 AceLan Kao <acelan.kao@canonical.com>
6 * Copyright (C) 2016 Alex Hung <alex.hung@canonical.com>
9 #include <linux/acpi.h>
10 #include <linux/cleanup.h>
11 #include <linux/dmi.h>
12 #include <linux/input.h>
13 #include <linux/input/sparse-keymap.h>
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/mutex.h>
17 #include <linux/platform_device.h>
18 #include <linux/suspend.h>
19 #include "../dual_accel_detect.h"
21 /* Returned when NOT in tablet mode on some HP Stream x360 11 models */
22 #define VGBS_TABLET_MODE_FLAG_ALT 0x10
23 /* When NOT in tablet mode, VGBS returns with the flag 0x40 */
24 #define VGBS_TABLET_MODE_FLAG 0x40
25 #define VGBS_DOCK_MODE_FLAG 0x80
27 #define VGBS_TABLET_MODE_FLAGS (VGBS_TABLET_MODE_FLAG | VGBS_TABLET_MODE_FLAG_ALT)
29 MODULE_DESCRIPTION("Intel Virtual Button driver");
30 MODULE_LICENSE("GPL");
31 MODULE_AUTHOR("AceLan Kao");
33 static const struct acpi_device_id intel_vbtn_ids
[] = {
37 MODULE_DEVICE_TABLE(acpi
, intel_vbtn_ids
);
39 /* In theory, these are HID usages. */
40 static const struct key_entry intel_vbtn_keymap
[] = {
41 { KE_KEY
, 0xC0, { KEY_POWER
} }, /* power key press */
42 { KE_IGNORE
, 0xC1, { KEY_POWER
} }, /* power key release */
43 { KE_KEY
, 0xC2, { KEY_LEFTMETA
} }, /* 'Windows' key press */
44 { KE_KEY
, 0xC3, { KEY_LEFTMETA
} }, /* 'Windows' key release */
45 { KE_KEY
, 0xC4, { KEY_VOLUMEUP
} }, /* volume-up key press */
46 { KE_IGNORE
, 0xC5, { KEY_VOLUMEUP
} }, /* volume-up key release */
47 { KE_KEY
, 0xC6, { KEY_VOLUMEDOWN
} }, /* volume-down key press */
48 { KE_IGNORE
, 0xC7, { KEY_VOLUMEDOWN
} }, /* volume-down key release */
49 { KE_KEY
, 0xC8, { KEY_ROTATE_LOCK_TOGGLE
} }, /* rotate-lock key press */
50 { KE_KEY
, 0xC9, { KEY_ROTATE_LOCK_TOGGLE
} }, /* rotate-lock key release */
54 static const struct key_entry intel_vbtn_switchmap
[] = {
56 * SW_DOCK should only be reported for docking stations, but DSDTs using the
57 * intel-vbtn code, always seem to use this for 2-in-1s / convertibles and set
58 * SW_DOCK=1 when in laptop-mode (in tandem with setting SW_TABLET_MODE=0).
59 * This causes userspace to think the laptop is docked to a port-replicator
60 * and to disable suspend-on-lid-close, which is undesirable.
61 * Map the dock events to KEY_IGNORE to avoid this broken SW_DOCK reporting.
63 { KE_IGNORE
, 0xCA, { .sw
= { SW_DOCK
, 1 } } }, /* Docked */
64 { KE_IGNORE
, 0xCB, { .sw
= { SW_DOCK
, 0 } } }, /* Undocked */
65 { KE_SW
, 0xCC, { .sw
= { SW_TABLET_MODE
, 1 } } }, /* Tablet */
66 { KE_SW
, 0xCD, { .sw
= { SW_TABLET_MODE
, 0 } } }, /* Laptop */
70 struct intel_vbtn_priv
{
71 struct mutex mutex
; /* Avoid notify_handler() racing with itself */
72 struct input_dev
*buttons_dev
;
73 struct input_dev
*switches_dev
;
80 static void detect_tablet_mode(struct device
*dev
)
82 struct intel_vbtn_priv
*priv
= dev_get_drvdata(dev
);
83 acpi_handle handle
= ACPI_HANDLE(dev
);
84 unsigned long long vgbs
;
88 status
= acpi_evaluate_integer(handle
, "VGBS", NULL
, &vgbs
);
89 if (ACPI_FAILURE(status
))
92 m
= !(vgbs
& VGBS_TABLET_MODE_FLAGS
);
93 input_report_switch(priv
->switches_dev
, SW_TABLET_MODE
, m
);
94 m
= (vgbs
& VGBS_DOCK_MODE_FLAG
) ? 1 : 0;
95 input_report_switch(priv
->switches_dev
, SW_DOCK
, m
);
97 input_sync(priv
->switches_dev
);
101 * Note this unconditionally creates the 2 input_dev-s and sets up
102 * the sparse-keymaps. Only the registration is conditional on
103 * have_buttons / have_switches. This is done so that the notify
104 * handler can always call sparse_keymap_entry_from_scancode()
105 * on the input_dev-s do determine the event type.
107 static int intel_vbtn_input_setup(struct platform_device
*device
)
109 struct intel_vbtn_priv
*priv
= dev_get_drvdata(&device
->dev
);
112 priv
->buttons_dev
= devm_input_allocate_device(&device
->dev
);
113 if (!priv
->buttons_dev
)
116 ret
= sparse_keymap_setup(priv
->buttons_dev
, intel_vbtn_keymap
, NULL
);
120 priv
->buttons_dev
->dev
.parent
= &device
->dev
;
121 priv
->buttons_dev
->name
= "Intel Virtual Buttons";
122 priv
->buttons_dev
->id
.bustype
= BUS_HOST
;
124 if (priv
->has_buttons
) {
125 ret
= input_register_device(priv
->buttons_dev
);
130 priv
->switches_dev
= devm_input_allocate_device(&device
->dev
);
131 if (!priv
->switches_dev
)
134 ret
= sparse_keymap_setup(priv
->switches_dev
, intel_vbtn_switchmap
, NULL
);
138 priv
->switches_dev
->dev
.parent
= &device
->dev
;
139 priv
->switches_dev
->name
= "Intel Virtual Switches";
140 priv
->switches_dev
->id
.bustype
= BUS_HOST
;
142 if (priv
->has_switches
) {
143 ret
= input_register_device(priv
->switches_dev
);
151 static void notify_handler(acpi_handle handle
, u32 event
, void *context
)
153 struct platform_device
*device
= context
;
154 struct intel_vbtn_priv
*priv
= dev_get_drvdata(&device
->dev
);
155 unsigned int val
= !(event
& 1); /* Even=press, Odd=release */
156 const struct key_entry
*ke
, *ke_rel
;
157 struct input_dev
*input_dev
;
161 guard(mutex
)(&priv
->mutex
);
163 if ((ke
= sparse_keymap_entry_from_scancode(priv
->buttons_dev
, event
))) {
164 if (!priv
->has_buttons
) {
165 dev_warn(&device
->dev
, "Warning: received 0x%02x button event on a device without buttons, please report this.\n",
169 input_dev
= priv
->buttons_dev
;
170 } else if ((ke
= sparse_keymap_entry_from_scancode(priv
->switches_dev
, event
))) {
171 if (!priv
->has_switches
) {
172 /* See dual_accel_detect.h for more info */
173 if (priv
->dual_accel
)
176 dev_info(&device
->dev
, "Registering Intel Virtual Switches input-dev after receiving a switch event\n");
177 ret
= input_register_device(priv
->switches_dev
);
181 priv
->has_switches
= true;
183 input_dev
= priv
->switches_dev
;
185 dev_dbg(&device
->dev
, "unknown event index 0x%x\n", event
);
189 if (priv
->wakeup_mode
) {
190 pm_wakeup_hard_event(&device
->dev
);
193 * Skip reporting an evdev event for button wake events,
194 * mirroring how the drivers/acpi/button.c code skips this too.
196 if (ke
->type
== KE_KEY
)
201 * Even press events are autorelease if there is no corresponding odd
202 * release event, or if the odd event is KE_IGNORE.
204 ke_rel
= sparse_keymap_entry_from_scancode(input_dev
, event
| 1);
205 autorelease
= val
&& (!ke_rel
|| ke_rel
->type
== KE_IGNORE
);
207 sparse_keymap_report_event(input_dev
, event
, val
, autorelease
);
211 * There are several laptops (non 2-in-1) models out there which support VGBS,
212 * but simply always return 0, which we translate to SW_TABLET_MODE=1. This in
213 * turn causes userspace (libinput) to suppress events from the builtin
214 * keyboard and touchpad, making the laptop essentially unusable.
216 * Since the problem of wrongly reporting SW_TABLET_MODE=1 in combination
217 * with libinput, leads to a non-usable system. Where as OTOH many people will
218 * not even notice when SW_TABLET_MODE is not being reported, a DMI based allow
219 * list is used here. This list mainly matches on the chassis-type of 2-in-1s.
221 * There are also some 2-in-1s which use the intel-vbtn ACPI interface to report
222 * SW_TABLET_MODE with a chassis-type of 8 ("Portable") or 10 ("Notebook"),
223 * these are matched on a per model basis, since many normal laptops with a
224 * possible broken VGBS ACPI-method also use these chassis-types.
226 static const struct dmi_system_id dmi_switches_allow_list
[] = {
229 DMI_EXACT_MATCH(DMI_CHASSIS_TYPE
, "31" /* Convertible */),
234 DMI_EXACT_MATCH(DMI_CHASSIS_TYPE
, "32" /* Detachable */),
239 DMI_MATCH(DMI_SYS_VENDOR
, "Dell Inc."),
240 DMI_MATCH(DMI_PRODUCT_NAME
, "Venue 11 Pro 7130"),
245 DMI_MATCH(DMI_SYS_VENDOR
, "Hewlett-Packard"),
246 DMI_MATCH(DMI_PRODUCT_NAME
, "HP Pavilion 13 x360 PC"),
251 DMI_MATCH(DMI_SYS_VENDOR
, "Acer"),
252 DMI_MATCH(DMI_PRODUCT_NAME
, "Switch SA5-271"),
257 DMI_MATCH(DMI_SYS_VENDOR
, "Dell Inc."),
258 DMI_MATCH(DMI_PRODUCT_NAME
, "Inspiron 7352"),
261 {} /* Array terminator */
264 static bool intel_vbtn_has_switches(acpi_handle handle
, bool dual_accel
)
266 /* See dual_accel_detect.h for more info */
270 if (!dmi_check_system(dmi_switches_allow_list
))
273 return acpi_has_method(handle
, "VGBS");
276 static int intel_vbtn_probe(struct platform_device
*device
)
278 acpi_handle handle
= ACPI_HANDLE(&device
->dev
);
279 bool dual_accel
, has_buttons
, has_switches
;
280 struct intel_vbtn_priv
*priv
;
284 dual_accel
= dual_accel_detect();
285 has_buttons
= acpi_has_method(handle
, "VBDL");
286 has_switches
= intel_vbtn_has_switches(handle
, dual_accel
);
288 if (!has_buttons
&& !has_switches
) {
289 dev_warn(&device
->dev
, "failed to read Intel Virtual Button driver\n");
293 priv
= devm_kzalloc(&device
->dev
, sizeof(*priv
), GFP_KERNEL
);
296 dev_set_drvdata(&device
->dev
, priv
);
298 err
= devm_mutex_init(&device
->dev
, &priv
->mutex
);
302 priv
->dual_accel
= dual_accel
;
303 priv
->has_buttons
= has_buttons
;
304 priv
->has_switches
= has_switches
;
306 err
= intel_vbtn_input_setup(device
);
308 pr_err("Failed to setup Intel Virtual Button\n");
312 status
= acpi_install_notify_handler(handle
,
316 if (ACPI_FAILURE(status
))
320 status
= acpi_evaluate_object(handle
, "VBDL", NULL
, NULL
);
321 if (ACPI_FAILURE(status
))
322 dev_err(&device
->dev
, "Error VBDL failed with ACPI status %d\n", status
);
324 // Check switches after buttons since VBDL may have side effects.
326 detect_tablet_mode(&device
->dev
);
328 device_init_wakeup(&device
->dev
, true);
330 * In order for system wakeup to work, the EC GPE has to be marked as
331 * a wakeup one, so do that here (this setting will persist, but it has
332 * no effect until the wakeup mask is set for the EC GPE).
334 acpi_ec_mark_gpe_for_wake();
338 static void intel_vbtn_remove(struct platform_device
*device
)
340 acpi_handle handle
= ACPI_HANDLE(&device
->dev
);
342 device_init_wakeup(&device
->dev
, false);
343 acpi_remove_notify_handler(handle
, ACPI_DEVICE_NOTIFY
, notify_handler
);
346 static int intel_vbtn_pm_prepare(struct device
*dev
)
348 if (device_may_wakeup(dev
)) {
349 struct intel_vbtn_priv
*priv
= dev_get_drvdata(dev
);
351 priv
->wakeup_mode
= true;
356 static void intel_vbtn_pm_complete(struct device
*dev
)
358 struct intel_vbtn_priv
*priv
= dev_get_drvdata(dev
);
360 priv
->wakeup_mode
= false;
363 static int intel_vbtn_pm_resume(struct device
*dev
)
365 struct intel_vbtn_priv
*priv
= dev_get_drvdata(dev
);
367 intel_vbtn_pm_complete(dev
);
369 if (priv
->has_switches
)
370 detect_tablet_mode(dev
);
375 static const struct dev_pm_ops intel_vbtn_pm_ops
= {
376 .prepare
= intel_vbtn_pm_prepare
,
377 .complete
= intel_vbtn_pm_complete
,
378 .resume
= intel_vbtn_pm_resume
,
379 .restore
= intel_vbtn_pm_resume
,
380 .thaw
= intel_vbtn_pm_resume
,
383 static struct platform_driver intel_vbtn_pl_driver
= {
385 .name
= "intel-vbtn",
386 .acpi_match_table
= intel_vbtn_ids
,
387 .pm
= &intel_vbtn_pm_ops
,
389 .probe
= intel_vbtn_probe
,
390 .remove
= intel_vbtn_remove
,
393 static acpi_status __init
394 check_acpi_dev(acpi_handle handle
, u32 lvl
, void *context
, void **rv
)
396 const struct acpi_device_id
*ids
= context
;
397 struct acpi_device
*dev
= acpi_fetch_acpi_dev(handle
);
399 if (dev
&& acpi_match_device_ids(dev
, ids
) == 0)
400 if (!IS_ERR_OR_NULL(acpi_create_platform_device(dev
, NULL
)))
402 "intel-vbtn: created platform device\n");
407 static int __init
intel_vbtn_init(void)
409 acpi_walk_namespace(ACPI_TYPE_DEVICE
, ACPI_ROOT_OBJECT
,
410 ACPI_UINT32_MAX
, check_acpi_dev
, NULL
,
411 (void *)intel_vbtn_ids
, NULL
);
413 return platform_driver_register(&intel_vbtn_pl_driver
);
415 module_init(intel_vbtn_init
);
417 static void __exit
intel_vbtn_exit(void)
419 platform_driver_unregister(&intel_vbtn_pl_driver
);
421 module_exit(intel_vbtn_exit
);