]>
Commit | Line | Data |
---|---|---|
b886d83c | 1 | // SPDX-License-Identifier: GPL-2.0-only |
61cd4822 LZ |
2 | /* |
3 | * Supports for the button array on SoC tablets originally running | |
4 | * Windows 8. | |
5 | * | |
6 | * (C) Copyright 2014 Intel Corporation | |
61cd4822 LZ |
7 | */ |
8 | ||
9 | #include <linux/module.h> | |
10 | #include <linux/input.h> | |
11 | #include <linux/init.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/acpi.h> | |
14 | #include <linux/gpio/consumer.h> | |
15 | #include <linux/gpio_keys.h> | |
be8e7a7e | 16 | #include <linux/gpio.h> |
61cd4822 | 17 | #include <linux/platform_device.h> |
61cd4822 | 18 | |
61cd4822 LZ |
19 | struct soc_button_info { |
20 | const char *name; | |
21 | int acpi_index; | |
22 | unsigned int event_type; | |
23 | unsigned int event_code; | |
24 | bool autorepeat; | |
25 | bool wakeup; | |
26 | }; | |
27 | ||
28 | /* | |
29 | * Some of the buttons like volume up/down are auto repeat, while others | |
30 | * are not. To support both, we register two platform devices, and put | |
31 | * buttons into them based on whether the key should be auto repeat. | |
32 | */ | |
33 | #define BUTTON_TYPES 2 | |
34 | ||
35 | struct soc_button_data { | |
36 | struct platform_device *children[BUTTON_TYPES]; | |
37 | }; | |
38 | ||
39 | /* | |
40 | * Get the Nth GPIO number from the ACPI object. | |
41 | */ | |
42 | static int soc_button_lookup_gpio(struct device *dev, int acpi_index) | |
43 | { | |
44 | struct gpio_desc *desc; | |
45 | int gpio; | |
46 | ||
a01cd170 | 47 | desc = gpiod_get_index(dev, NULL, acpi_index, GPIOD_ASIS); |
61cd4822 LZ |
48 | if (IS_ERR(desc)) |
49 | return PTR_ERR(desc); | |
50 | ||
51 | gpio = desc_to_gpio(desc); | |
52 | ||
53 | gpiod_put(desc); | |
54 | ||
55 | return gpio; | |
56 | } | |
57 | ||
58 | static struct platform_device * | |
042e1c79 | 59 | soc_button_device_create(struct platform_device *pdev, |
61cd4822 LZ |
60 | const struct soc_button_info *button_info, |
61 | bool autorepeat) | |
62 | { | |
63 | const struct soc_button_info *info; | |
64 | struct platform_device *pd; | |
65 | struct gpio_keys_button *gpio_keys; | |
66 | struct gpio_keys_platform_data *gpio_keys_pdata; | |
67 | int n_buttons = 0; | |
68 | int gpio; | |
69 | int error; | |
70 | ||
7283b47d HG |
71 | for (info = button_info; info->name; info++) |
72 | if (info->autorepeat == autorepeat) | |
73 | n_buttons++; | |
74 | ||
61cd4822 LZ |
75 | gpio_keys_pdata = devm_kzalloc(&pdev->dev, |
76 | sizeof(*gpio_keys_pdata) + | |
7283b47d | 77 | sizeof(*gpio_keys) * n_buttons, |
61cd4822 | 78 | GFP_KERNEL); |
91cf07cd PG |
79 | if (!gpio_keys_pdata) |
80 | return ERR_PTR(-ENOMEM); | |
81 | ||
61cd4822 | 82 | gpio_keys = (void *)(gpio_keys_pdata + 1); |
7283b47d | 83 | n_buttons = 0; |
61cd4822 LZ |
84 | |
85 | for (info = button_info; info->name; info++) { | |
86 | if (info->autorepeat != autorepeat) | |
87 | continue; | |
88 | ||
89 | gpio = soc_button_lookup_gpio(&pdev->dev, info->acpi_index); | |
be8e7a7e | 90 | if (!gpio_is_valid(gpio)) |
61cd4822 LZ |
91 | continue; |
92 | ||
93 | gpio_keys[n_buttons].type = info->event_type; | |
94 | gpio_keys[n_buttons].code = info->event_code; | |
95 | gpio_keys[n_buttons].gpio = gpio; | |
96 | gpio_keys[n_buttons].active_low = 1; | |
97 | gpio_keys[n_buttons].desc = info->name; | |
98 | gpio_keys[n_buttons].wakeup = info->wakeup; | |
5c4fa2a6 HG |
99 | /* These devices often use cheap buttons, use 50 ms debounce */ |
100 | gpio_keys[n_buttons].debounce_interval = 50; | |
61cd4822 LZ |
101 | n_buttons++; |
102 | } | |
103 | ||
104 | if (n_buttons == 0) { | |
105 | error = -ENODEV; | |
106 | goto err_free_mem; | |
107 | } | |
108 | ||
109 | gpio_keys_pdata->buttons = gpio_keys; | |
110 | gpio_keys_pdata->nbuttons = n_buttons; | |
111 | gpio_keys_pdata->rep = autorepeat; | |
112 | ||
113 | pd = platform_device_alloc("gpio-keys", PLATFORM_DEVID_AUTO); | |
114 | if (!pd) { | |
115 | error = -ENOMEM; | |
116 | goto err_free_mem; | |
117 | } | |
118 | ||
119 | error = platform_device_add_data(pd, gpio_keys_pdata, | |
120 | sizeof(*gpio_keys_pdata)); | |
121 | if (error) | |
122 | goto err_free_pdev; | |
123 | ||
124 | error = platform_device_add(pd); | |
125 | if (error) | |
126 | goto err_free_pdev; | |
127 | ||
128 | return pd; | |
129 | ||
130 | err_free_pdev: | |
131 | platform_device_put(pd); | |
132 | err_free_mem: | |
133 | devm_kfree(&pdev->dev, gpio_keys_pdata); | |
134 | return ERR_PTR(error); | |
135 | } | |
136 | ||
4c3362f4 HG |
137 | static int soc_button_get_acpi_object_int(const union acpi_object *obj) |
138 | { | |
139 | if (obj->type != ACPI_TYPE_INTEGER) | |
140 | return -1; | |
141 | ||
142 | return obj->integer.value; | |
143 | } | |
144 | ||
145 | /* Parse a single ACPI0011 _DSD button descriptor */ | |
146 | static int soc_button_parse_btn_desc(struct device *dev, | |
147 | const union acpi_object *desc, | |
148 | int collection_uid, | |
149 | struct soc_button_info *info) | |
150 | { | |
151 | int upage, usage; | |
152 | ||
153 | if (desc->type != ACPI_TYPE_PACKAGE || | |
154 | desc->package.count != 5 || | |
155 | /* First byte should be 1 (control) */ | |
156 | soc_button_get_acpi_object_int(&desc->package.elements[0]) != 1 || | |
157 | /* Third byte should be collection uid */ | |
158 | soc_button_get_acpi_object_int(&desc->package.elements[2]) != | |
159 | collection_uid) { | |
160 | dev_err(dev, "Invalid ACPI Button Descriptor\n"); | |
161 | return -ENODEV; | |
162 | } | |
163 | ||
164 | info->event_type = EV_KEY; | |
165 | info->acpi_index = | |
166 | soc_button_get_acpi_object_int(&desc->package.elements[1]); | |
167 | upage = soc_button_get_acpi_object_int(&desc->package.elements[3]); | |
168 | usage = soc_button_get_acpi_object_int(&desc->package.elements[4]); | |
169 | ||
170 | /* | |
171 | * The UUID: fa6bd625-9ce8-470d-a2c7-b3ca36c4282e descriptors use HID | |
172 | * usage page and usage codes, but otherwise the device is not HID | |
173 | * compliant: it uses one irq per button instead of generating HID | |
174 | * input reports and some buttons should generate wakeups where as | |
175 | * others should not, so we cannot use the HID subsystem. | |
176 | * | |
177 | * Luckily all devices only use a few usage page + usage combinations, | |
178 | * so we can simply check for the known combinations here. | |
179 | */ | |
180 | if (upage == 0x01 && usage == 0x81) { | |
181 | info->name = "power"; | |
182 | info->event_code = KEY_POWER; | |
183 | info->wakeup = true; | |
39be9b6d HG |
184 | } else if (upage == 0x01 && usage == 0xca) { |
185 | info->name = "rotation lock switch"; | |
186 | info->event_type = EV_SW; | |
187 | info->event_code = SW_ROTATE_LOCK; | |
4c3362f4 HG |
188 | } else if (upage == 0x07 && usage == 0xe3) { |
189 | info->name = "home"; | |
dd224085 | 190 | info->event_code = KEY_LEFTMETA; |
4c3362f4 HG |
191 | info->wakeup = true; |
192 | } else if (upage == 0x0c && usage == 0xe9) { | |
193 | info->name = "volume_up"; | |
194 | info->event_code = KEY_VOLUMEUP; | |
195 | info->autorepeat = true; | |
196 | } else if (upage == 0x0c && usage == 0xea) { | |
197 | info->name = "volume_down"; | |
198 | info->event_code = KEY_VOLUMEDOWN; | |
199 | info->autorepeat = true; | |
200 | } else { | |
201 | dev_warn(dev, "Unknown button index %d upage %02x usage %02x, ignoring\n", | |
202 | info->acpi_index, upage, usage); | |
203 | info->name = "unknown"; | |
204 | info->event_code = KEY_RESERVED; | |
205 | } | |
206 | ||
207 | return 0; | |
208 | } | |
209 | ||
210 | /* ACPI0011 _DSD btns descriptors UUID: fa6bd625-9ce8-470d-a2c7-b3ca36c4282e */ | |
211 | static const u8 btns_desc_uuid[16] = { | |
212 | 0x25, 0xd6, 0x6b, 0xfa, 0xe8, 0x9c, 0x0d, 0x47, | |
213 | 0xa2, 0xc7, 0xb3, 0xca, 0x36, 0xc4, 0x28, 0x2e | |
214 | }; | |
215 | ||
216 | /* Parse ACPI0011 _DSD button descriptors */ | |
217 | static struct soc_button_info *soc_button_get_button_info(struct device *dev) | |
218 | { | |
219 | struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; | |
220 | const union acpi_object *desc, *el0, *uuid, *btns_desc = NULL; | |
221 | struct soc_button_info *button_info; | |
222 | acpi_status status; | |
223 | int i, btn, collection_uid = -1; | |
224 | ||
225 | status = acpi_evaluate_object_typed(ACPI_HANDLE(dev), "_DSD", NULL, | |
226 | &buf, ACPI_TYPE_PACKAGE); | |
227 | if (ACPI_FAILURE(status)) { | |
228 | dev_err(dev, "ACPI _DSD object not found\n"); | |
229 | return ERR_PTR(-ENODEV); | |
230 | } | |
231 | ||
232 | /* Look for the Button Descriptors UUID */ | |
233 | desc = buf.pointer; | |
234 | for (i = 0; (i + 1) < desc->package.count; i += 2) { | |
235 | uuid = &desc->package.elements[i]; | |
236 | ||
237 | if (uuid->type != ACPI_TYPE_BUFFER || | |
238 | uuid->buffer.length != 16 || | |
239 | desc->package.elements[i + 1].type != ACPI_TYPE_PACKAGE) { | |
240 | break; | |
241 | } | |
242 | ||
243 | if (memcmp(uuid->buffer.pointer, btns_desc_uuid, 16) == 0) { | |
244 | btns_desc = &desc->package.elements[i + 1]; | |
245 | break; | |
246 | } | |
247 | } | |
248 | ||
249 | if (!btns_desc) { | |
250 | dev_err(dev, "ACPI Button Descriptors not found\n"); | |
779f19ac HG |
251 | button_info = ERR_PTR(-ENODEV); |
252 | goto out; | |
4c3362f4 HG |
253 | } |
254 | ||
255 | /* The first package describes the collection */ | |
256 | el0 = &btns_desc->package.elements[0]; | |
257 | if (el0->type == ACPI_TYPE_PACKAGE && | |
258 | el0->package.count == 5 && | |
259 | /* First byte should be 0 (collection) */ | |
260 | soc_button_get_acpi_object_int(&el0->package.elements[0]) == 0 && | |
261 | /* Third byte should be 0 (top level collection) */ | |
262 | soc_button_get_acpi_object_int(&el0->package.elements[2]) == 0) { | |
263 | collection_uid = soc_button_get_acpi_object_int( | |
264 | &el0->package.elements[1]); | |
265 | } | |
266 | if (collection_uid == -1) { | |
267 | dev_err(dev, "Invalid Button Collection Descriptor\n"); | |
779f19ac HG |
268 | button_info = ERR_PTR(-ENODEV); |
269 | goto out; | |
4c3362f4 HG |
270 | } |
271 | ||
272 | /* There are package.count - 1 buttons + 1 terminating empty entry */ | |
273 | button_info = devm_kcalloc(dev, btns_desc->package.count, | |
274 | sizeof(*button_info), GFP_KERNEL); | |
779f19ac HG |
275 | if (!button_info) { |
276 | button_info = ERR_PTR(-ENOMEM); | |
277 | goto out; | |
278 | } | |
4c3362f4 HG |
279 | |
280 | /* Parse the button descriptors */ | |
281 | for (i = 1, btn = 0; i < btns_desc->package.count; i++, btn++) { | |
282 | if (soc_button_parse_btn_desc(dev, | |
283 | &btns_desc->package.elements[i], | |
284 | collection_uid, | |
779f19ac HG |
285 | &button_info[btn])) { |
286 | button_info = ERR_PTR(-ENODEV); | |
287 | goto out; | |
288 | } | |
4c3362f4 HG |
289 | } |
290 | ||
779f19ac HG |
291 | out: |
292 | kfree(buf.pointer); | |
4c3362f4 HG |
293 | return button_info; |
294 | } | |
295 | ||
042e1c79 | 296 | static int soc_button_remove(struct platform_device *pdev) |
61cd4822 | 297 | { |
042e1c79 JY |
298 | struct soc_button_data *priv = platform_get_drvdata(pdev); |
299 | ||
61cd4822 LZ |
300 | int i; |
301 | ||
302 | for (i = 0; i < BUTTON_TYPES; i++) | |
303 | if (priv->children[i]) | |
304 | platform_device_unregister(priv->children[i]); | |
042e1c79 JY |
305 | |
306 | return 0; | |
61cd4822 LZ |
307 | } |
308 | ||
042e1c79 | 309 | static int soc_button_probe(struct platform_device *pdev) |
61cd4822 | 310 | { |
042e1c79 JY |
311 | struct device *dev = &pdev->dev; |
312 | const struct acpi_device_id *id; | |
313 | struct soc_button_info *button_info; | |
61cd4822 LZ |
314 | struct soc_button_data *priv; |
315 | struct platform_device *pd; | |
316 | int i; | |
317 | int error; | |
318 | ||
042e1c79 JY |
319 | id = acpi_match_device(dev->driver->acpi_match_table, dev); |
320 | if (!id) | |
321 | return -ENODEV; | |
322 | ||
4c3362f4 HG |
323 | if (!id->driver_data) { |
324 | button_info = soc_button_get_button_info(dev); | |
325 | if (IS_ERR(button_info)) | |
326 | return PTR_ERR(button_info); | |
327 | } else { | |
328 | button_info = (struct soc_button_info *)id->driver_data; | |
329 | } | |
042e1c79 | 330 | |
c5097538 AS |
331 | error = gpiod_count(dev, NULL); |
332 | if (error < 0) { | |
aa45590a | 333 | dev_dbg(dev, "no GPIO attached, ignoring...\n"); |
d912366a | 334 | return -ENODEV; |
3d5a9437 BT |
335 | } |
336 | ||
aa45590a | 337 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
61cd4822 LZ |
338 | if (!priv) |
339 | return -ENOMEM; | |
340 | ||
042e1c79 | 341 | platform_set_drvdata(pdev, priv); |
61cd4822 LZ |
342 | |
343 | for (i = 0; i < BUTTON_TYPES; i++) { | |
344 | pd = soc_button_device_create(pdev, button_info, i == 0); | |
345 | if (IS_ERR(pd)) { | |
346 | error = PTR_ERR(pd); | |
347 | if (error != -ENODEV) { | |
348 | soc_button_remove(pdev); | |
349 | return error; | |
350 | } | |
7740fc52 | 351 | continue; |
61cd4822 LZ |
352 | } |
353 | ||
354 | priv->children[i] = pd; | |
355 | } | |
356 | ||
357 | if (!priv->children[0] && !priv->children[1]) | |
358 | return -ENODEV; | |
359 | ||
4c3362f4 HG |
360 | if (!id->driver_data) |
361 | devm_kfree(dev, button_info); | |
362 | ||
61cd4822 LZ |
363 | return 0; |
364 | } | |
365 | ||
7283b47d HG |
366 | /* |
367 | * Definition of buttons on the tablet. The ACPI index of each button | |
368 | * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC | |
369 | * Platforms" | |
370 | */ | |
61cd4822 LZ |
371 | static struct soc_button_info soc_button_PNP0C40[] = { |
372 | { "power", 0, EV_KEY, KEY_POWER, false, true }, | |
791738be | 373 | { "home", 1, EV_KEY, KEY_LEFTMETA, false, true }, |
61cd4822 LZ |
374 | { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false }, |
375 | { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false }, | |
e9eb788f | 376 | { "rotation_lock", 4, EV_KEY, KEY_ROTATE_LOCK_TOGGLE, false, false }, |
61cd4822 LZ |
377 | { } |
378 | }; | |
379 | ||
042e1c79 JY |
380 | static const struct acpi_device_id soc_button_acpi_match[] = { |
381 | { "PNP0C40", (unsigned long)soc_button_PNP0C40 }, | |
4c3362f4 | 382 | { "ACPI0011", 0 }, |
042e1c79 | 383 | { } |
61cd4822 | 384 | }; |
61cd4822 | 385 | |
042e1c79 JY |
386 | MODULE_DEVICE_TABLE(acpi, soc_button_acpi_match); |
387 | ||
388 | static struct platform_driver soc_button_driver = { | |
389 | .probe = soc_button_probe, | |
61cd4822 | 390 | .remove = soc_button_remove, |
042e1c79 JY |
391 | .driver = { |
392 | .name = KBUILD_MODNAME, | |
042e1c79 JY |
393 | .acpi_match_table = ACPI_PTR(soc_button_acpi_match), |
394 | }, | |
61cd4822 | 395 | }; |
042e1c79 | 396 | module_platform_driver(soc_button_driver); |
61cd4822 LZ |
397 | |
398 | MODULE_LICENSE("GPL"); |