xref: /openbmc/linux/drivers/platform/x86/intel/hid.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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