1c3d3586dSKate Hsuan // SPDX-License-Identifier: GPL-2.0+
2c3d3586dSKate Hsuan /*
3c3d3586dSKate Hsuan * Intel HID event & 5 button array driver
4c3d3586dSKate Hsuan *
5c3d3586dSKate Hsuan * Copyright (C) 2015 Alex Hung <alex.hung@canonical.com>
6c3d3586dSKate Hsuan * Copyright (C) 2015 Andrew Lutomirski <luto@kernel.org>
7c3d3586dSKate Hsuan */
8c3d3586dSKate Hsuan
9c3d3586dSKate Hsuan #include <linux/acpi.h>
10c3d3586dSKate Hsuan #include <linux/dmi.h>
11c3d3586dSKate Hsuan #include <linux/input.h>
12c3d3586dSKate Hsuan #include <linux/input/sparse-keymap.h>
13c3d3586dSKate Hsuan #include <linux/kernel.h>
14c3d3586dSKate Hsuan #include <linux/module.h>
15c3d3586dSKate Hsuan #include <linux/platform_device.h>
16c3d3586dSKate Hsuan #include <linux/suspend.h>
17c3d3586dSKate Hsuan #include "../dual_accel_detect.h"
18c3d3586dSKate Hsuan
19e32354bbSHans de Goede enum intel_hid_tablet_sw_mode {
20e32354bbSHans de Goede TABLET_SW_AUTO = -1,
21e32354bbSHans de Goede TABLET_SW_OFF = 0,
22e32354bbSHans de Goede TABLET_SW_AT_EVENT,
23e32354bbSHans de Goede TABLET_SW_AT_PROBE,
24e32354bbSHans de Goede };
25e32354bbSHans de Goede
26e32354bbSHans de Goede static bool enable_5_button_array;
27e32354bbSHans de Goede module_param(enable_5_button_array, bool, 0444);
28e32354bbSHans de Goede MODULE_PARM_DESC(enable_5_button_array,
29e32354bbSHans de Goede "Enable 5 Button Array support. "
30e32354bbSHans de Goede "If you need this please report this to: platform-driver-x86@vger.kernel.org");
31e32354bbSHans de Goede
32e32354bbSHans de Goede static int enable_sw_tablet_mode = TABLET_SW_AUTO;
33e32354bbSHans de Goede module_param(enable_sw_tablet_mode, int, 0444);
34e32354bbSHans de Goede MODULE_PARM_DESC(enable_sw_tablet_mode,
35e32354bbSHans de Goede "Enable SW_TABLET_MODE reporting -1:auto 0:off 1:at-first-event 2:at-probe. "
36e32354bbSHans de Goede "If you need this please report this to: platform-driver-x86@vger.kernel.org");
37e32354bbSHans de Goede
38c3d3586dSKate Hsuan /* When NOT in tablet mode, VGBS returns with the flag 0x40 */
39c3d3586dSKate Hsuan #define TABLET_MODE_FLAG BIT(6)
40c3d3586dSKate Hsuan
41c3d3586dSKate Hsuan MODULE_LICENSE("GPL");
42c3d3586dSKate Hsuan MODULE_AUTHOR("Alex Hung");
43c3d3586dSKate Hsuan
44c3d3586dSKate Hsuan static const struct acpi_device_id intel_hid_ids[] = {
45c3d3586dSKate Hsuan {"INT33D5", 0},
46c3d3586dSKate Hsuan {"INTC1051", 0},
47c3d3586dSKate Hsuan {"INTC1054", 0},
48c3d3586dSKate Hsuan {"INTC1070", 0},
49a977ece5SIvan Hu {"INTC1076", 0},
50a977ece5SIvan Hu {"INTC1077", 0},
51a977ece5SIvan Hu {"INTC1078", 0},
52c3d3586dSKate Hsuan {"", 0},
53c3d3586dSKate Hsuan };
54c3d3586dSKate Hsuan MODULE_DEVICE_TABLE(acpi, intel_hid_ids);
55c3d3586dSKate Hsuan
56c3d3586dSKate Hsuan /* In theory, these are HID usages. */
57c3d3586dSKate Hsuan static const struct key_entry intel_hid_keymap[] = {
58c3d3586dSKate Hsuan /* 1: LSuper (Page 0x07, usage 0xE3) -- unclear what to do */
59c3d3586dSKate Hsuan /* 2: Toggle SW_ROTATE_LOCK -- easy to implement if seen in wild */
60c3d3586dSKate Hsuan { KE_KEY, 3, { KEY_NUMLOCK } },
61c3d3586dSKate Hsuan { KE_KEY, 4, { KEY_HOME } },
62c3d3586dSKate Hsuan { KE_KEY, 5, { KEY_END } },
63c3d3586dSKate Hsuan { KE_KEY, 6, { KEY_PAGEUP } },
64c3d3586dSKate Hsuan { KE_KEY, 7, { KEY_PAGEDOWN } },
65c3d3586dSKate Hsuan { KE_KEY, 8, { KEY_RFKILL } },
66c3d3586dSKate Hsuan { KE_KEY, 9, { KEY_POWER } },
67c3d3586dSKate Hsuan { KE_KEY, 11, { KEY_SLEEP } },
68c3d3586dSKate Hsuan /* 13 has two different meanings in the spec -- ignore it. */
69c3d3586dSKate Hsuan { KE_KEY, 14, { KEY_STOPCD } },
70c3d3586dSKate Hsuan { KE_KEY, 15, { KEY_PLAYPAUSE } },
71c3d3586dSKate Hsuan { KE_KEY, 16, { KEY_MUTE } },
72c3d3586dSKate Hsuan { KE_KEY, 17, { KEY_VOLUMEUP } },
73c3d3586dSKate Hsuan { KE_KEY, 18, { KEY_VOLUMEDOWN } },
74c3d3586dSKate Hsuan { KE_KEY, 19, { KEY_BRIGHTNESSUP } },
75c3d3586dSKate Hsuan { KE_KEY, 20, { KEY_BRIGHTNESSDOWN } },
76c3d3586dSKate Hsuan /* 27: wake -- needs special handling */
77c3d3586dSKate Hsuan { KE_END },
78c3d3586dSKate Hsuan };
79c3d3586dSKate Hsuan
80c3d3586dSKate Hsuan /* 5 button array notification value. */
81c3d3586dSKate Hsuan static const struct key_entry intel_array_keymap[] = {
82c3d3586dSKate Hsuan { KE_KEY, 0xC2, { KEY_LEFTMETA } }, /* Press */
83c3d3586dSKate Hsuan { KE_IGNORE, 0xC3, { KEY_LEFTMETA } }, /* Release */
84c3d3586dSKate Hsuan { KE_KEY, 0xC4, { KEY_VOLUMEUP } }, /* Press */
85c3d3586dSKate Hsuan { KE_IGNORE, 0xC5, { KEY_VOLUMEUP } }, /* Release */
86c3d3586dSKate Hsuan { KE_KEY, 0xC6, { KEY_VOLUMEDOWN } }, /* Press */
87c3d3586dSKate Hsuan { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } }, /* Release */
88c3d3586dSKate Hsuan { KE_KEY, 0xC8, { KEY_ROTATE_LOCK_TOGGLE } }, /* Press */
89c3d3586dSKate Hsuan { KE_IGNORE, 0xC9, { KEY_ROTATE_LOCK_TOGGLE } }, /* Release */
90c3d3586dSKate Hsuan { KE_KEY, 0xCE, { KEY_POWER } }, /* Press */
91c3d3586dSKate Hsuan { KE_IGNORE, 0xCF, { KEY_POWER } }, /* Release */
92c3d3586dSKate Hsuan { KE_END },
93c3d3586dSKate Hsuan };
94c3d3586dSKate Hsuan
95c3d3586dSKate Hsuan static const struct dmi_system_id button_array_table[] = {
96c3d3586dSKate Hsuan {
97c3d3586dSKate Hsuan .ident = "Wacom MobileStudio Pro 13",
98c3d3586dSKate Hsuan .matches = {
99c3d3586dSKate Hsuan DMI_MATCH(DMI_SYS_VENDOR, "Wacom Co.,Ltd"),
100c3d3586dSKate Hsuan DMI_MATCH(DMI_PRODUCT_NAME, "Wacom MobileStudio Pro 13"),
101c3d3586dSKate Hsuan },
102c3d3586dSKate Hsuan },
103c3d3586dSKate Hsuan {
104c3d3586dSKate Hsuan .ident = "Wacom MobileStudio Pro 16",
105c3d3586dSKate Hsuan .matches = {
106c3d3586dSKate Hsuan DMI_MATCH(DMI_SYS_VENDOR, "Wacom Co.,Ltd"),
107c3d3586dSKate Hsuan DMI_MATCH(DMI_PRODUCT_NAME, "Wacom MobileStudio Pro 16"),
108c3d3586dSKate Hsuan },
109c3d3586dSKate Hsuan },
110c3d3586dSKate Hsuan {
111c3d3586dSKate Hsuan .ident = "HP Spectre x2 (2015)",
112c3d3586dSKate Hsuan .matches = {
113c3d3586dSKate Hsuan DMI_MATCH(DMI_SYS_VENDOR, "HP"),
114c3d3586dSKate Hsuan DMI_MATCH(DMI_PRODUCT_NAME, "HP Spectre x2 Detachable"),
115c3d3586dSKate Hsuan },
116c3d3586dSKate Hsuan },
117c3d3586dSKate Hsuan {
118c3d3586dSKate Hsuan .ident = "Lenovo ThinkPad X1 Tablet Gen 2",
119c3d3586dSKate Hsuan .matches = {
120c3d3586dSKate Hsuan DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
121c3d3586dSKate Hsuan DMI_MATCH(DMI_PRODUCT_FAMILY, "ThinkPad X1 Tablet Gen 2"),
122c3d3586dSKate Hsuan },
123c3d3586dSKate Hsuan },
1247d0c0090SAlex Hung {
1257d0c0090SAlex Hung .ident = "Microsoft Surface Go 3",
1267d0c0090SAlex Hung .matches = {
1277d0c0090SAlex Hung DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
1287d0c0090SAlex Hung DMI_MATCH(DMI_PRODUCT_NAME, "Surface Go 3"),
1297d0c0090SAlex Hung },
1307d0c0090SAlex Hung },
131c3d3586dSKate Hsuan { }
132c3d3586dSKate Hsuan };
133c3d3586dSKate Hsuan
134c3d3586dSKate Hsuan /*
135c3d3586dSKate Hsuan * Some convertible use the intel-hid ACPI interface to report SW_TABLET_MODE,
136c3d3586dSKate Hsuan * these need to be compared via a DMI based authorization list because some
137c3d3586dSKate Hsuan * models have unreliable VGBS return which could cause incorrect
138c3d3586dSKate Hsuan * SW_TABLET_MODE report.
139c3d3586dSKate Hsuan */
140c3d3586dSKate Hsuan static const struct dmi_system_id dmi_vgbs_allow_list[] = {
141c3d3586dSKate Hsuan {
142c3d3586dSKate Hsuan .matches = {
143c3d3586dSKate Hsuan DMI_MATCH(DMI_SYS_VENDOR, "HP"),
144c3d3586dSKate Hsuan DMI_MATCH(DMI_PRODUCT_NAME, "HP Spectre x360 Convertible 15-df0xxx"),
145c3d3586dSKate Hsuan },
146c3d3586dSKate Hsuan },
147d4fe9cc4SDuke Lee {
148d4fe9cc4SDuke Lee .matches = {
149d4fe9cc4SDuke Lee DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
150d4fe9cc4SDuke Lee DMI_MATCH(DMI_PRODUCT_NAME, "Surface Go"),
151d4fe9cc4SDuke Lee },
152d4fe9cc4SDuke Lee },
153*7783e97fSMaxim Mikityanskiy {
154*7783e97fSMaxim Mikityanskiy .matches = {
155*7783e97fSMaxim Mikityanskiy DMI_MATCH(DMI_SYS_VENDOR, "HP"),
156*7783e97fSMaxim Mikityanskiy DMI_MATCH(DMI_PRODUCT_NAME, "HP Elite Dragonfly G2 Notebook PC"),
157*7783e97fSMaxim Mikityanskiy },
158*7783e97fSMaxim Mikityanskiy },
159c3d3586dSKate Hsuan { }
160c3d3586dSKate Hsuan };
161c3d3586dSKate Hsuan
162b201cb0eSJosé Expósito /*
163b201cb0eSJosé Expósito * Some devices, even non convertible ones, can send incorrect SW_TABLET_MODE
164b201cb0eSJosé Expósito * reports. Accept such reports only from devices in this list.
165b201cb0eSJosé Expósito */
166b201cb0eSJosé Expósito static const struct dmi_system_id dmi_auto_add_switch[] = {
167b201cb0eSJosé Expósito {
168b201cb0eSJosé Expósito .matches = {
169b201cb0eSJosé Expósito DMI_EXACT_MATCH(DMI_CHASSIS_TYPE, "31" /* Convertible */),
170b201cb0eSJosé Expósito },
171b201cb0eSJosé Expósito },
172b201cb0eSJosé Expósito {
173b201cb0eSJosé Expósito .matches = {
174b201cb0eSJosé Expósito DMI_EXACT_MATCH(DMI_CHASSIS_TYPE, "32" /* Detachable */),
175b201cb0eSJosé Expósito },
176b201cb0eSJosé Expósito },
177b201cb0eSJosé Expósito {} /* Array terminator */
178b201cb0eSJosé Expósito };
179b201cb0eSJosé Expósito
180c3d3586dSKate Hsuan struct intel_hid_priv {
181c3d3586dSKate Hsuan struct input_dev *input_dev;
182c3d3586dSKate Hsuan struct input_dev *array;
183c3d3586dSKate Hsuan struct input_dev *switches;
184c3d3586dSKate Hsuan bool wakeup_mode;
185c3d3586dSKate Hsuan };
186c3d3586dSKate Hsuan
187c3d3586dSKate Hsuan #define HID_EVENT_FILTER_UUID "eeec56b3-4442-408f-a792-4edd4d758054"
188c3d3586dSKate Hsuan
189c3d3586dSKate Hsuan enum intel_hid_dsm_fn_codes {
190c3d3586dSKate Hsuan INTEL_HID_DSM_FN_INVALID,
191c3d3586dSKate Hsuan INTEL_HID_DSM_BTNL_FN,
192c3d3586dSKate Hsuan INTEL_HID_DSM_HDMM_FN,
193c3d3586dSKate Hsuan INTEL_HID_DSM_HDSM_FN,
194c3d3586dSKate Hsuan INTEL_HID_DSM_HDEM_FN,
195c3d3586dSKate Hsuan INTEL_HID_DSM_BTNS_FN,
196c3d3586dSKate Hsuan INTEL_HID_DSM_BTNE_FN,
197c3d3586dSKate Hsuan INTEL_HID_DSM_HEBC_V1_FN,
198c3d3586dSKate Hsuan INTEL_HID_DSM_VGBS_FN,
199c3d3586dSKate Hsuan INTEL_HID_DSM_HEBC_V2_FN,
200c3d3586dSKate Hsuan INTEL_HID_DSM_FN_MAX
201c3d3586dSKate Hsuan };
202c3d3586dSKate Hsuan
203c3d3586dSKate Hsuan static const char *intel_hid_dsm_fn_to_method[INTEL_HID_DSM_FN_MAX] = {
204c3d3586dSKate Hsuan NULL,
205c3d3586dSKate Hsuan "BTNL",
206c3d3586dSKate Hsuan "HDMM",
207c3d3586dSKate Hsuan "HDSM",
208c3d3586dSKate Hsuan "HDEM",
209c3d3586dSKate Hsuan "BTNS",
210c3d3586dSKate Hsuan "BTNE",
211c3d3586dSKate Hsuan "HEBC",
212c3d3586dSKate Hsuan "VGBS",
213c3d3586dSKate Hsuan "HEBC"
214c3d3586dSKate Hsuan };
215c3d3586dSKate Hsuan
216c3d3586dSKate Hsuan static unsigned long long intel_hid_dsm_fn_mask;
217c3d3586dSKate Hsuan static guid_t intel_dsm_guid;
218c3d3586dSKate Hsuan
intel_hid_execute_method(acpi_handle handle,enum intel_hid_dsm_fn_codes fn_index,unsigned long long arg)219c3d3586dSKate Hsuan static bool intel_hid_execute_method(acpi_handle handle,
220c3d3586dSKate Hsuan enum intel_hid_dsm_fn_codes fn_index,
221c3d3586dSKate Hsuan unsigned long long arg)
222c3d3586dSKate Hsuan {
223c3d3586dSKate Hsuan union acpi_object *obj, argv4, req;
224c3d3586dSKate Hsuan acpi_status status;
225c3d3586dSKate Hsuan char *method_name;
226c3d3586dSKate Hsuan
227c3d3586dSKate Hsuan if (fn_index <= INTEL_HID_DSM_FN_INVALID ||
228c3d3586dSKate Hsuan fn_index >= INTEL_HID_DSM_FN_MAX)
229c3d3586dSKate Hsuan return false;
230c3d3586dSKate Hsuan
231c3d3586dSKate Hsuan method_name = (char *)intel_hid_dsm_fn_to_method[fn_index];
232c3d3586dSKate Hsuan
233c3d3586dSKate Hsuan if (!(intel_hid_dsm_fn_mask & BIT(fn_index)))
234c3d3586dSKate Hsuan goto skip_dsm_exec;
235c3d3586dSKate Hsuan
236c3d3586dSKate Hsuan /* All methods expects a package with one integer element */
237c3d3586dSKate Hsuan req.type = ACPI_TYPE_INTEGER;
238c3d3586dSKate Hsuan req.integer.value = arg;
239c3d3586dSKate Hsuan
240c3d3586dSKate Hsuan argv4.type = ACPI_TYPE_PACKAGE;
241c3d3586dSKate Hsuan argv4.package.count = 1;
242c3d3586dSKate Hsuan argv4.package.elements = &req;
243c3d3586dSKate Hsuan
244c3d3586dSKate Hsuan obj = acpi_evaluate_dsm(handle, &intel_dsm_guid, 1, fn_index, &argv4);
245c3d3586dSKate Hsuan if (obj) {
246c3d3586dSKate Hsuan acpi_handle_debug(handle, "Exec DSM Fn code: %d[%s] success\n",
247c3d3586dSKate Hsuan fn_index, method_name);
248c3d3586dSKate Hsuan ACPI_FREE(obj);
249c3d3586dSKate Hsuan return true;
250c3d3586dSKate Hsuan }
251c3d3586dSKate Hsuan
252c3d3586dSKate Hsuan skip_dsm_exec:
253c3d3586dSKate Hsuan status = acpi_execute_simple_method(handle, method_name, arg);
254c3d3586dSKate Hsuan if (ACPI_SUCCESS(status))
255c3d3586dSKate Hsuan return true;
256c3d3586dSKate Hsuan
257c3d3586dSKate Hsuan return false;
258c3d3586dSKate Hsuan }
259c3d3586dSKate Hsuan
intel_hid_evaluate_method(acpi_handle handle,enum intel_hid_dsm_fn_codes fn_index,unsigned long long * result)260c3d3586dSKate Hsuan static bool intel_hid_evaluate_method(acpi_handle handle,
261c3d3586dSKate Hsuan enum intel_hid_dsm_fn_codes fn_index,
262c3d3586dSKate Hsuan unsigned long long *result)
263c3d3586dSKate Hsuan {
264c3d3586dSKate Hsuan union acpi_object *obj;
265c3d3586dSKate Hsuan acpi_status status;
266c3d3586dSKate Hsuan char *method_name;
267c3d3586dSKate Hsuan
268c3d3586dSKate Hsuan if (fn_index <= INTEL_HID_DSM_FN_INVALID ||
269c3d3586dSKate Hsuan fn_index >= INTEL_HID_DSM_FN_MAX)
270c3d3586dSKate Hsuan return false;
271c3d3586dSKate Hsuan
272c3d3586dSKate Hsuan method_name = (char *)intel_hid_dsm_fn_to_method[fn_index];
273c3d3586dSKate Hsuan
2741620c80bSMichael Niewöhner if (!(intel_hid_dsm_fn_mask & BIT(fn_index)))
275c3d3586dSKate Hsuan goto skip_dsm_eval;
276c3d3586dSKate Hsuan
277c3d3586dSKate Hsuan obj = acpi_evaluate_dsm_typed(handle, &intel_dsm_guid,
278c3d3586dSKate Hsuan 1, fn_index,
279c3d3586dSKate Hsuan NULL, ACPI_TYPE_INTEGER);
280c3d3586dSKate Hsuan if (obj) {
281c3d3586dSKate Hsuan *result = obj->integer.value;
282c3d3586dSKate Hsuan acpi_handle_debug(handle,
283c3d3586dSKate Hsuan "Eval DSM Fn code: %d[%s] results: 0x%llx\n",
284c3d3586dSKate Hsuan fn_index, method_name, *result);
285c3d3586dSKate Hsuan ACPI_FREE(obj);
286c3d3586dSKate Hsuan return true;
287c3d3586dSKate Hsuan }
288c3d3586dSKate Hsuan
289c3d3586dSKate Hsuan skip_dsm_eval:
290c3d3586dSKate Hsuan status = acpi_evaluate_integer(handle, method_name, NULL, result);
291c3d3586dSKate Hsuan if (ACPI_SUCCESS(status))
292c3d3586dSKate Hsuan return true;
293c3d3586dSKate Hsuan
294c3d3586dSKate Hsuan return false;
295c3d3586dSKate Hsuan }
296c3d3586dSKate Hsuan
intel_hid_init_dsm(acpi_handle handle)297c3d3586dSKate Hsuan static void intel_hid_init_dsm(acpi_handle handle)
298c3d3586dSKate Hsuan {
299c3d3586dSKate Hsuan union acpi_object *obj;
300c3d3586dSKate Hsuan
301c3d3586dSKate Hsuan guid_parse(HID_EVENT_FILTER_UUID, &intel_dsm_guid);
302c3d3586dSKate Hsuan
303c3d3586dSKate Hsuan obj = acpi_evaluate_dsm_typed(handle, &intel_dsm_guid, 1, 0, NULL,
304c3d3586dSKate Hsuan ACPI_TYPE_BUFFER);
305c3d3586dSKate Hsuan if (obj) {
306c3d3586dSKate Hsuan switch (obj->buffer.length) {
307c3d3586dSKate Hsuan default:
308c3d3586dSKate Hsuan case 2:
309c3d3586dSKate Hsuan intel_hid_dsm_fn_mask = *(u16 *)obj->buffer.pointer;
310c3d3586dSKate Hsuan break;
311c3d3586dSKate Hsuan case 1:
312c3d3586dSKate Hsuan intel_hid_dsm_fn_mask = *obj->buffer.pointer;
313c3d3586dSKate Hsuan break;
314c3d3586dSKate Hsuan case 0:
315c3d3586dSKate Hsuan acpi_handle_warn(handle, "intel_hid_dsm_fn_mask length is zero\n");
316c3d3586dSKate Hsuan intel_hid_dsm_fn_mask = 0;
317c3d3586dSKate Hsuan break;
318c3d3586dSKate Hsuan }
319c3d3586dSKate Hsuan ACPI_FREE(obj);
320c3d3586dSKate Hsuan }
321c3d3586dSKate Hsuan
322c3d3586dSKate Hsuan acpi_handle_debug(handle, "intel_hid_dsm_fn_mask = %llx\n",
323c3d3586dSKate Hsuan intel_hid_dsm_fn_mask);
324c3d3586dSKate Hsuan }
325c3d3586dSKate Hsuan
intel_hid_set_enable(struct device * device,bool enable)326c3d3586dSKate Hsuan static int intel_hid_set_enable(struct device *device, bool enable)
327c3d3586dSKate Hsuan {
328c3d3586dSKate Hsuan acpi_handle handle = ACPI_HANDLE(device);
329c3d3586dSKate Hsuan
330c3d3586dSKate Hsuan /* Enable|disable features - power button is always enabled */
331c3d3586dSKate Hsuan if (!intel_hid_execute_method(handle, INTEL_HID_DSM_HDSM_FN,
332c3d3586dSKate Hsuan enable)) {
333c3d3586dSKate Hsuan dev_warn(device, "failed to %sable hotkeys\n",
334c3d3586dSKate Hsuan enable ? "en" : "dis");
335c3d3586dSKate Hsuan return -EIO;
336c3d3586dSKate Hsuan }
337c3d3586dSKate Hsuan
338c3d3586dSKate Hsuan return 0;
339c3d3586dSKate Hsuan }
340c3d3586dSKate Hsuan
intel_button_array_enable(struct device * device,bool enable)341c3d3586dSKate Hsuan static void intel_button_array_enable(struct device *device, bool enable)
342c3d3586dSKate Hsuan {
343c3d3586dSKate Hsuan struct intel_hid_priv *priv = dev_get_drvdata(device);
344c3d3586dSKate Hsuan acpi_handle handle = ACPI_HANDLE(device);
345c3d3586dSKate Hsuan unsigned long long button_cap;
346c3d3586dSKate Hsuan acpi_status status;
347c3d3586dSKate Hsuan
348c3d3586dSKate Hsuan if (!priv->array)
349c3d3586dSKate Hsuan return;
350c3d3586dSKate Hsuan
351c3d3586dSKate Hsuan /* Query supported platform features */
352c3d3586dSKate Hsuan status = acpi_evaluate_integer(handle, "BTNC", NULL, &button_cap);
353c3d3586dSKate Hsuan if (ACPI_FAILURE(status)) {
354c3d3586dSKate Hsuan dev_warn(device, "failed to get button capability\n");
355c3d3586dSKate Hsuan return;
356c3d3586dSKate Hsuan }
357c3d3586dSKate Hsuan
358c3d3586dSKate Hsuan /* Enable|disable features - power button is always enabled */
359c3d3586dSKate Hsuan if (!intel_hid_execute_method(handle, INTEL_HID_DSM_BTNE_FN,
360c3d3586dSKate Hsuan enable ? button_cap : 1))
361c3d3586dSKate Hsuan dev_warn(device, "failed to set button capability\n");
362c3d3586dSKate Hsuan }
363c3d3586dSKate Hsuan
intel_hid_pm_prepare(struct device * device)364c3d3586dSKate Hsuan static int intel_hid_pm_prepare(struct device *device)
365c3d3586dSKate Hsuan {
366c3d3586dSKate Hsuan if (device_may_wakeup(device)) {
367c3d3586dSKate Hsuan struct intel_hid_priv *priv = dev_get_drvdata(device);
368c3d3586dSKate Hsuan
369c3d3586dSKate Hsuan priv->wakeup_mode = true;
370c3d3586dSKate Hsuan }
371c3d3586dSKate Hsuan return 0;
372c3d3586dSKate Hsuan }
373c3d3586dSKate Hsuan
intel_hid_pm_complete(struct device * device)374c3d3586dSKate Hsuan static void intel_hid_pm_complete(struct device *device)
375c3d3586dSKate Hsuan {
376c3d3586dSKate Hsuan struct intel_hid_priv *priv = dev_get_drvdata(device);
377c3d3586dSKate Hsuan
378c3d3586dSKate Hsuan priv->wakeup_mode = false;
379c3d3586dSKate Hsuan }
380c3d3586dSKate Hsuan
intel_hid_pl_suspend_handler(struct device * device)381c3d3586dSKate Hsuan static int intel_hid_pl_suspend_handler(struct device *device)
382c3d3586dSKate Hsuan {
383c3d3586dSKate Hsuan intel_button_array_enable(device, false);
384c3d3586dSKate Hsuan
385c3d3586dSKate Hsuan if (!pm_suspend_no_platform())
386c3d3586dSKate Hsuan intel_hid_set_enable(device, false);
387c3d3586dSKate Hsuan
388c3d3586dSKate Hsuan return 0;
389c3d3586dSKate Hsuan }
390c3d3586dSKate Hsuan
intel_hid_pl_resume_handler(struct device * device)391c3d3586dSKate Hsuan static int intel_hid_pl_resume_handler(struct device *device)
392c3d3586dSKate Hsuan {
393c3d3586dSKate Hsuan intel_hid_pm_complete(device);
394c3d3586dSKate Hsuan
395c3d3586dSKate Hsuan if (!pm_suspend_no_platform())
396c3d3586dSKate Hsuan intel_hid_set_enable(device, true);
397c3d3586dSKate Hsuan
398c3d3586dSKate Hsuan intel_button_array_enable(device, true);
399c3d3586dSKate Hsuan return 0;
400c3d3586dSKate Hsuan }
401c3d3586dSKate Hsuan
402c3d3586dSKate Hsuan static const struct dev_pm_ops intel_hid_pl_pm_ops = {
403c3d3586dSKate Hsuan .prepare = intel_hid_pm_prepare,
404c3d3586dSKate Hsuan .complete = intel_hid_pm_complete,
405c3d3586dSKate Hsuan .freeze = intel_hid_pl_suspend_handler,
406c3d3586dSKate Hsuan .thaw = intel_hid_pl_resume_handler,
407c3d3586dSKate Hsuan .restore = intel_hid_pl_resume_handler,
408c3d3586dSKate Hsuan .suspend = intel_hid_pl_suspend_handler,
409c3d3586dSKate Hsuan .resume = intel_hid_pl_resume_handler,
410c3d3586dSKate Hsuan };
411c3d3586dSKate Hsuan
intel_hid_input_setup(struct platform_device * device)412c3d3586dSKate Hsuan static int intel_hid_input_setup(struct platform_device *device)
413c3d3586dSKate Hsuan {
414c3d3586dSKate Hsuan struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);
415c3d3586dSKate Hsuan int ret;
416c3d3586dSKate Hsuan
417c3d3586dSKate Hsuan priv->input_dev = devm_input_allocate_device(&device->dev);
418c3d3586dSKate Hsuan if (!priv->input_dev)
419c3d3586dSKate Hsuan return -ENOMEM;
420c3d3586dSKate Hsuan
421c3d3586dSKate Hsuan ret = sparse_keymap_setup(priv->input_dev, intel_hid_keymap, NULL);
422c3d3586dSKate Hsuan if (ret)
423c3d3586dSKate Hsuan return ret;
424c3d3586dSKate Hsuan
425c3d3586dSKate Hsuan priv->input_dev->name = "Intel HID events";
426c3d3586dSKate Hsuan priv->input_dev->id.bustype = BUS_HOST;
427c3d3586dSKate Hsuan
428c3d3586dSKate Hsuan return input_register_device(priv->input_dev);
429c3d3586dSKate Hsuan }
430c3d3586dSKate Hsuan
intel_button_array_input_setup(struct platform_device * device)431c3d3586dSKate Hsuan static int intel_button_array_input_setup(struct platform_device *device)
432c3d3586dSKate Hsuan {
433c3d3586dSKate Hsuan struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);
434c3d3586dSKate Hsuan int ret;
435c3d3586dSKate Hsuan
436c3d3586dSKate Hsuan /* Setup input device for 5 button array */
437c3d3586dSKate Hsuan priv->array = devm_input_allocate_device(&device->dev);
438c3d3586dSKate Hsuan if (!priv->array)
439c3d3586dSKate Hsuan return -ENOMEM;
440c3d3586dSKate Hsuan
441c3d3586dSKate Hsuan ret = sparse_keymap_setup(priv->array, intel_array_keymap, NULL);
442c3d3586dSKate Hsuan if (ret)
443c3d3586dSKate Hsuan return ret;
444c3d3586dSKate Hsuan
445c3d3586dSKate Hsuan priv->array->name = "Intel HID 5 button array";
446c3d3586dSKate Hsuan priv->array->id.bustype = BUS_HOST;
447c3d3586dSKate Hsuan
448c3d3586dSKate Hsuan return input_register_device(priv->array);
449c3d3586dSKate Hsuan }
450c3d3586dSKate Hsuan
intel_hid_switches_setup(struct platform_device * device)451c3d3586dSKate Hsuan static int intel_hid_switches_setup(struct platform_device *device)
452c3d3586dSKate Hsuan {
453c3d3586dSKate Hsuan struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);
454c3d3586dSKate Hsuan
455c3d3586dSKate Hsuan /* Setup input device for switches */
456c3d3586dSKate Hsuan priv->switches = devm_input_allocate_device(&device->dev);
457c3d3586dSKate Hsuan if (!priv->switches)
458c3d3586dSKate Hsuan return -ENOMEM;
459c3d3586dSKate Hsuan
460c3d3586dSKate Hsuan __set_bit(EV_SW, priv->switches->evbit);
461c3d3586dSKate Hsuan __set_bit(SW_TABLET_MODE, priv->switches->swbit);
462c3d3586dSKate Hsuan
463c3d3586dSKate Hsuan priv->switches->name = "Intel HID switches";
464c3d3586dSKate Hsuan priv->switches->id.bustype = BUS_HOST;
465c3d3586dSKate Hsuan return input_register_device(priv->switches);
466c3d3586dSKate Hsuan }
467c3d3586dSKate Hsuan
report_tablet_mode_state(struct platform_device * device)468c3d3586dSKate Hsuan static void report_tablet_mode_state(struct platform_device *device)
469c3d3586dSKate Hsuan {
470c3d3586dSKate Hsuan struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);
471c3d3586dSKate Hsuan acpi_handle handle = ACPI_HANDLE(&device->dev);
472c3d3586dSKate Hsuan unsigned long long vgbs;
473c3d3586dSKate Hsuan int m;
474c3d3586dSKate Hsuan
475c3d3586dSKate Hsuan if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_VGBS_FN, &vgbs))
476c3d3586dSKate Hsuan return;
477c3d3586dSKate Hsuan
478c3d3586dSKate Hsuan m = !(vgbs & TABLET_MODE_FLAG);
479c3d3586dSKate Hsuan input_report_switch(priv->switches, SW_TABLET_MODE, m);
480c3d3586dSKate Hsuan input_sync(priv->switches);
481c3d3586dSKate Hsuan }
482c3d3586dSKate Hsuan
report_tablet_mode_event(struct input_dev * input_dev,u32 event)483c3d3586dSKate Hsuan static bool report_tablet_mode_event(struct input_dev *input_dev, u32 event)
484c3d3586dSKate Hsuan {
485c3d3586dSKate Hsuan if (!input_dev)
486c3d3586dSKate Hsuan return false;
487c3d3586dSKate Hsuan
488c3d3586dSKate Hsuan switch (event) {
489c3d3586dSKate Hsuan case 0xcc:
490c3d3586dSKate Hsuan input_report_switch(input_dev, SW_TABLET_MODE, 1);
491c3d3586dSKate Hsuan input_sync(input_dev);
492c3d3586dSKate Hsuan return true;
493c3d3586dSKate Hsuan case 0xcd:
494c3d3586dSKate Hsuan input_report_switch(input_dev, SW_TABLET_MODE, 0);
495c3d3586dSKate Hsuan input_sync(input_dev);
496c3d3586dSKate Hsuan return true;
497c3d3586dSKate Hsuan default:
498c3d3586dSKate Hsuan return false;
499c3d3586dSKate Hsuan }
500c3d3586dSKate Hsuan }
501c3d3586dSKate Hsuan
notify_handler(acpi_handle handle,u32 event,void * context)502c3d3586dSKate Hsuan static void notify_handler(acpi_handle handle, u32 event, void *context)
503c3d3586dSKate Hsuan {
504c3d3586dSKate Hsuan struct platform_device *device = context;
505c3d3586dSKate Hsuan struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);
506c3d3586dSKate Hsuan unsigned long long ev_index;
507c3d3586dSKate Hsuan int err;
508c3d3586dSKate Hsuan
509c3d3586dSKate Hsuan /*
510c3d3586dSKate Hsuan * Some convertible have unreliable VGBS return which could cause incorrect
511c3d3586dSKate Hsuan * SW_TABLET_MODE report, in these cases we enable support when receiving
512c3d3586dSKate Hsuan * the first event instead of during driver setup.
513c3d3586dSKate Hsuan */
514e32354bbSHans de Goede if (!priv->switches && enable_sw_tablet_mode == TABLET_SW_AT_EVENT &&
515e32354bbSHans de Goede (event == 0xcc || event == 0xcd)) {
516c3d3586dSKate Hsuan dev_info(&device->dev, "switch event received, enable switches supports\n");
517c3d3586dSKate Hsuan err = intel_hid_switches_setup(device);
518c3d3586dSKate Hsuan if (err)
519c3d3586dSKate Hsuan pr_err("Failed to setup Intel HID switches\n");
520c3d3586dSKate Hsuan }
521c3d3586dSKate Hsuan
522c3d3586dSKate Hsuan if (priv->wakeup_mode) {
523c3d3586dSKate Hsuan /*
524c3d3586dSKate Hsuan * Needed for wakeup from suspend-to-idle to work on some
525c3d3586dSKate Hsuan * platforms that don't expose the 5-button array, but still
526c3d3586dSKate Hsuan * send notifies with the power button event code to this
527c3d3586dSKate Hsuan * device object on power button actions while suspended.
528c3d3586dSKate Hsuan */
529c3d3586dSKate Hsuan if (event == 0xce)
530c3d3586dSKate Hsuan goto wakeup;
531c3d3586dSKate Hsuan
532c3d3586dSKate Hsuan /*
533c3d3586dSKate Hsuan * Some devices send (duplicate) tablet-mode events when moved
534c3d3586dSKate Hsuan * around even though the mode has not changed; and they do this
535c3d3586dSKate Hsuan * even when suspended.
536c3d3586dSKate Hsuan * Update the switch state in case it changed and then return
537c3d3586dSKate Hsuan * without waking up to avoid spurious wakeups.
538c3d3586dSKate Hsuan */
539c3d3586dSKate Hsuan if (event == 0xcc || event == 0xcd) {
540c3d3586dSKate Hsuan report_tablet_mode_event(priv->switches, event);
541c3d3586dSKate Hsuan return;
542c3d3586dSKate Hsuan }
543c3d3586dSKate Hsuan
544c3d3586dSKate Hsuan /* Wake up on 5-button array events only. */
545c3d3586dSKate Hsuan if (event == 0xc0 || !priv->array)
546c3d3586dSKate Hsuan return;
547c3d3586dSKate Hsuan
548c3d3586dSKate Hsuan if (!sparse_keymap_entry_from_scancode(priv->array, event)) {
549c3d3586dSKate Hsuan dev_info(&device->dev, "unknown event 0x%x\n", event);
550c3d3586dSKate Hsuan return;
551c3d3586dSKate Hsuan }
552c3d3586dSKate Hsuan
553c3d3586dSKate Hsuan wakeup:
554c3d3586dSKate Hsuan pm_wakeup_hard_event(&device->dev);
555c3d3586dSKate Hsuan
556c3d3586dSKate Hsuan return;
557c3d3586dSKate Hsuan }
558c3d3586dSKate Hsuan
559c3d3586dSKate Hsuan /*
560c3d3586dSKate Hsuan * Needed for suspend to work on some platforms that don't expose
561c3d3586dSKate Hsuan * the 5-button array, but still send notifies with power button
562c3d3586dSKate Hsuan * event code to this device object on power button actions.
563c3d3586dSKate Hsuan *
564c3d3586dSKate Hsuan * Report the power button press and release.
565c3d3586dSKate Hsuan */
566c3d3586dSKate Hsuan if (!priv->array) {
567c3d3586dSKate Hsuan if (event == 0xce) {
568c3d3586dSKate Hsuan input_report_key(priv->input_dev, KEY_POWER, 1);
569c3d3586dSKate Hsuan input_sync(priv->input_dev);
570c3d3586dSKate Hsuan return;
571c3d3586dSKate Hsuan }
572c3d3586dSKate Hsuan
573c3d3586dSKate Hsuan if (event == 0xcf) {
574c3d3586dSKate Hsuan input_report_key(priv->input_dev, KEY_POWER, 0);
575c3d3586dSKate Hsuan input_sync(priv->input_dev);
576c3d3586dSKate Hsuan return;
577c3d3586dSKate Hsuan }
578c3d3586dSKate Hsuan }
579c3d3586dSKate Hsuan
580c3d3586dSKate Hsuan if (report_tablet_mode_event(priv->switches, event))
581c3d3586dSKate Hsuan return;
582c3d3586dSKate Hsuan
583c3d3586dSKate Hsuan /* 0xC0 is for HID events, other values are for 5 button array */
584c3d3586dSKate Hsuan if (event != 0xc0) {
585c3d3586dSKate Hsuan if (!priv->array ||
586c3d3586dSKate Hsuan !sparse_keymap_report_event(priv->array, event, 1, true))
587c3d3586dSKate Hsuan dev_dbg(&device->dev, "unknown event 0x%x\n", event);
588c3d3586dSKate Hsuan return;
589c3d3586dSKate Hsuan }
590c3d3586dSKate Hsuan
591c3d3586dSKate Hsuan if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_HDEM_FN,
592c3d3586dSKate Hsuan &ev_index)) {
593c3d3586dSKate Hsuan dev_warn(&device->dev, "failed to get event index\n");
594c3d3586dSKate Hsuan return;
595c3d3586dSKate Hsuan }
596c3d3586dSKate Hsuan
597c3d3586dSKate Hsuan if (!sparse_keymap_report_event(priv->input_dev, ev_index, 1, true))
598c3d3586dSKate Hsuan dev_dbg(&device->dev, "unknown event index 0x%llx\n",
599c3d3586dSKate Hsuan ev_index);
600c3d3586dSKate Hsuan }
601c3d3586dSKate Hsuan
button_array_present(struct platform_device * device)602c3d3586dSKate Hsuan static bool button_array_present(struct platform_device *device)
603c3d3586dSKate Hsuan {
604c3d3586dSKate Hsuan acpi_handle handle = ACPI_HANDLE(&device->dev);
605c3d3586dSKate Hsuan unsigned long long event_cap;
606c3d3586dSKate Hsuan
607c3d3586dSKate Hsuan if (intel_hid_evaluate_method(handle, INTEL_HID_DSM_HEBC_V2_FN,
608c3d3586dSKate Hsuan &event_cap)) {
609c3d3586dSKate Hsuan /* Check presence of 5 button array or v2 power button */
610c3d3586dSKate Hsuan if (event_cap & 0x60000)
611c3d3586dSKate Hsuan return true;
612c3d3586dSKate Hsuan }
613c3d3586dSKate Hsuan
614c3d3586dSKate Hsuan if (intel_hid_evaluate_method(handle, INTEL_HID_DSM_HEBC_V1_FN,
615c3d3586dSKate Hsuan &event_cap)) {
616c3d3586dSKate Hsuan if (event_cap & 0x20000)
617c3d3586dSKate Hsuan return true;
618c3d3586dSKate Hsuan }
619c3d3586dSKate Hsuan
620e32354bbSHans de Goede if (enable_5_button_array || dmi_check_system(button_array_table))
621c3d3586dSKate Hsuan return true;
622c3d3586dSKate Hsuan
623c3d3586dSKate Hsuan return false;
624c3d3586dSKate Hsuan }
625c3d3586dSKate Hsuan
intel_hid_probe(struct platform_device * device)626c3d3586dSKate Hsuan static int intel_hid_probe(struct platform_device *device)
627c3d3586dSKate Hsuan {
628c3d3586dSKate Hsuan acpi_handle handle = ACPI_HANDLE(&device->dev);
629e3ab18deSHans de Goede unsigned long long mode, dummy;
630c3d3586dSKate Hsuan struct intel_hid_priv *priv;
631c3d3586dSKate Hsuan acpi_status status;
632c3d3586dSKate Hsuan int err;
633c3d3586dSKate Hsuan
634c3d3586dSKate Hsuan intel_hid_init_dsm(handle);
635c3d3586dSKate Hsuan
636c3d3586dSKate Hsuan if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_HDMM_FN, &mode)) {
637c3d3586dSKate Hsuan dev_warn(&device->dev, "failed to read mode\n");
638c3d3586dSKate Hsuan return -ENODEV;
639c3d3586dSKate Hsuan }
640c3d3586dSKate Hsuan
641c3d3586dSKate Hsuan if (mode != 0) {
642c3d3586dSKate Hsuan /*
643c3d3586dSKate Hsuan * This driver only implements "simple" mode. There appear
644c3d3586dSKate Hsuan * to be no other modes, but we should be paranoid and check
645c3d3586dSKate Hsuan * for compatibility.
646c3d3586dSKate Hsuan */
647c3d3586dSKate Hsuan dev_info(&device->dev, "platform is not in simple mode\n");
648c3d3586dSKate Hsuan return -ENODEV;
649c3d3586dSKate Hsuan }
650c3d3586dSKate Hsuan
651c3d3586dSKate Hsuan priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
652c3d3586dSKate Hsuan if (!priv)
653c3d3586dSKate Hsuan return -ENOMEM;
654c3d3586dSKate Hsuan dev_set_drvdata(&device->dev, priv);
655c3d3586dSKate Hsuan
656b201cb0eSJosé Expósito /* See dual_accel_detect.h for more info on the dual_accel check. */
657e32354bbSHans de Goede if (enable_sw_tablet_mode == TABLET_SW_AUTO) {
658e32354bbSHans de Goede if (dmi_check_system(dmi_vgbs_allow_list))
659e32354bbSHans de Goede enable_sw_tablet_mode = TABLET_SW_AT_PROBE;
660e32354bbSHans de Goede else if (dmi_check_system(dmi_auto_add_switch) && !dual_accel_detect())
661e32354bbSHans de Goede enable_sw_tablet_mode = TABLET_SW_AT_EVENT;
662e32354bbSHans de Goede else
663e32354bbSHans de Goede enable_sw_tablet_mode = TABLET_SW_OFF;
664e32354bbSHans de Goede }
665c3d3586dSKate Hsuan
666c3d3586dSKate Hsuan err = intel_hid_input_setup(device);
667c3d3586dSKate Hsuan if (err) {
668c3d3586dSKate Hsuan pr_err("Failed to setup Intel HID hotkeys\n");
669c3d3586dSKate Hsuan return err;
670c3d3586dSKate Hsuan }
671c3d3586dSKate Hsuan
672c3d3586dSKate Hsuan /* Setup 5 button array */
673c3d3586dSKate Hsuan if (button_array_present(device)) {
674c3d3586dSKate Hsuan dev_info(&device->dev, "platform supports 5 button array\n");
675c3d3586dSKate Hsuan err = intel_button_array_input_setup(device);
676c3d3586dSKate Hsuan if (err)
677c3d3586dSKate Hsuan pr_err("Failed to setup Intel 5 button array hotkeys\n");
678c3d3586dSKate Hsuan }
679c3d3586dSKate Hsuan
680c3d3586dSKate Hsuan /* Setup switches for devices that we know VGBS return correctly */
681e32354bbSHans de Goede if (enable_sw_tablet_mode == TABLET_SW_AT_PROBE) {
682c3d3586dSKate Hsuan dev_info(&device->dev, "platform supports switches\n");
683c3d3586dSKate Hsuan err = intel_hid_switches_setup(device);
684c3d3586dSKate Hsuan if (err)
685c3d3586dSKate Hsuan pr_err("Failed to setup Intel HID switches\n");
686c3d3586dSKate Hsuan else
687c3d3586dSKate Hsuan report_tablet_mode_state(device);
688c3d3586dSKate Hsuan }
689c3d3586dSKate Hsuan
690c3d3586dSKate Hsuan status = acpi_install_notify_handler(handle,
691c3d3586dSKate Hsuan ACPI_DEVICE_NOTIFY,
692c3d3586dSKate Hsuan notify_handler,
693c3d3586dSKate Hsuan device);
694c3d3586dSKate Hsuan if (ACPI_FAILURE(status))
695c3d3586dSKate Hsuan return -EBUSY;
696c3d3586dSKate Hsuan
697c3d3586dSKate Hsuan err = intel_hid_set_enable(&device->dev, true);
698c3d3586dSKate Hsuan if (err)
699c3d3586dSKate Hsuan goto err_remove_notify;
700c3d3586dSKate Hsuan
701c3d3586dSKate Hsuan intel_button_array_enable(&device->dev, true);
702c3d3586dSKate Hsuan
703e3ab18deSHans de Goede /*
704e3ab18deSHans de Goede * Call button load method to enable HID power button
705e3ab18deSHans de Goede * Always do this since it activates events on some devices without
706e3ab18deSHans de Goede * a button array too.
707e3ab18deSHans de Goede */
708e3ab18deSHans de Goede if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_BTNL_FN, &dummy))
709e3ab18deSHans de Goede dev_warn(&device->dev, "failed to enable HID power button\n");
710c3d3586dSKate Hsuan
711c3d3586dSKate Hsuan device_init_wakeup(&device->dev, true);
712c3d3586dSKate Hsuan /*
713c3d3586dSKate Hsuan * In order for system wakeup to work, the EC GPE has to be marked as
714c3d3586dSKate Hsuan * a wakeup one, so do that here (this setting will persist, but it has
715c3d3586dSKate Hsuan * no effect until the wakeup mask is set for the EC GPE).
716c3d3586dSKate Hsuan */
717c3d3586dSKate Hsuan acpi_ec_mark_gpe_for_wake();
718c3d3586dSKate Hsuan return 0;
719c3d3586dSKate Hsuan
720c3d3586dSKate Hsuan err_remove_notify:
721c3d3586dSKate Hsuan acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
722c3d3586dSKate Hsuan
723c3d3586dSKate Hsuan return err;
724c3d3586dSKate Hsuan }
725c3d3586dSKate Hsuan
intel_hid_remove(struct platform_device * device)72663b89016SUwe Kleine-König static void intel_hid_remove(struct platform_device *device)
727c3d3586dSKate Hsuan {
728c3d3586dSKate Hsuan acpi_handle handle = ACPI_HANDLE(&device->dev);
729c3d3586dSKate Hsuan
730c3d3586dSKate Hsuan device_init_wakeup(&device->dev, false);
731c3d3586dSKate Hsuan acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
732c3d3586dSKate Hsuan intel_hid_set_enable(&device->dev, false);
733c3d3586dSKate Hsuan intel_button_array_enable(&device->dev, false);
734c3d3586dSKate Hsuan }
735c3d3586dSKate Hsuan
736c3d3586dSKate Hsuan static struct platform_driver intel_hid_pl_driver = {
737c3d3586dSKate Hsuan .driver = {
738c3d3586dSKate Hsuan .name = "intel-hid",
739c3d3586dSKate Hsuan .acpi_match_table = intel_hid_ids,
740c3d3586dSKate Hsuan .pm = &intel_hid_pl_pm_ops,
741c3d3586dSKate Hsuan },
742c3d3586dSKate Hsuan .probe = intel_hid_probe,
74363b89016SUwe Kleine-König .remove_new = intel_hid_remove,
744c3d3586dSKate Hsuan };
745c3d3586dSKate Hsuan
746c3d3586dSKate Hsuan /*
747c3d3586dSKate Hsuan * Unfortunately, some laptops provide a _HID="INT33D5" device with
748c3d3586dSKate Hsuan * _CID="PNP0C02". This causes the pnpacpi scan driver to claim the
749c3d3586dSKate Hsuan * ACPI node, so no platform device will be created. The pnpacpi
750c3d3586dSKate Hsuan * driver rejects this device in subsequent processing, so no physical
751c3d3586dSKate Hsuan * node is created at all.
752c3d3586dSKate Hsuan *
753c3d3586dSKate Hsuan * As a workaround until the ACPI core figures out how to handle
754c3d3586dSKate Hsuan * this corner case, manually ask the ACPI platform device code to
755c3d3586dSKate Hsuan * claim the ACPI node.
756c3d3586dSKate Hsuan */
757c3d3586dSKate Hsuan static acpi_status __init
check_acpi_dev(acpi_handle handle,u32 lvl,void * context,void ** rv)758c3d3586dSKate Hsuan check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv)
759c3d3586dSKate Hsuan {
760c3d3586dSKate Hsuan const struct acpi_device_id *ids = context;
761f7e62c58SRafael J. Wysocki struct acpi_device *dev = acpi_fetch_acpi_dev(handle);
762c3d3586dSKate Hsuan
763f7e62c58SRafael J. Wysocki if (dev && acpi_match_device_ids(dev, ids) == 0)
764c3d3586dSKate Hsuan if (!IS_ERR_OR_NULL(acpi_create_platform_device(dev, NULL)))
765c3d3586dSKate Hsuan dev_info(&dev->dev,
766c3d3586dSKate Hsuan "intel-hid: created platform device\n");
767c3d3586dSKate Hsuan
768c3d3586dSKate Hsuan return AE_OK;
769c3d3586dSKate Hsuan }
770c3d3586dSKate Hsuan
intel_hid_init(void)771c3d3586dSKate Hsuan static int __init intel_hid_init(void)
772c3d3586dSKate Hsuan {
773c3d3586dSKate Hsuan acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
774c3d3586dSKate Hsuan ACPI_UINT32_MAX, check_acpi_dev, NULL,
775c3d3586dSKate Hsuan (void *)intel_hid_ids, NULL);
776c3d3586dSKate Hsuan
777c3d3586dSKate Hsuan return platform_driver_register(&intel_hid_pl_driver);
778c3d3586dSKate Hsuan }
779c3d3586dSKate Hsuan module_init(intel_hid_init);
780c3d3586dSKate Hsuan
intel_hid_exit(void)781c3d3586dSKate Hsuan static void __exit intel_hid_exit(void)
782c3d3586dSKate Hsuan {
783c3d3586dSKate Hsuan platform_driver_unregister(&intel_hid_pl_driver);
784c3d3586dSKate Hsuan }
785c3d3586dSKate Hsuan module_exit(intel_hid_exit);
786