1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * DMI based code to deal with broken DSDTs on X86 tablets which ship with
4 * Android as (part of) the factory image. The factory kernels shipped on these
5 * devices typically have a bunch of things hardcoded, rather than specified
6 * in their DSDT.
7 *
8 * Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com>
9 */
10
11 #include <linux/acpi.h>
12 #include <linux/gpio/machine.h>
13 #include <linux/input.h>
14 #include <linux/platform_device.h>
15
16 #include "shared-psy-info.h"
17 #include "x86-android-tablets.h"
18
19 /* Acer Iconia One 7 B1-750 has an Android factory img with everything hardcoded */
20 static const char * const acer_b1_750_mount_matrix[] = {
21 "-1", "0", "0",
22 "0", "1", "0",
23 "0", "0", "1"
24 };
25
26 static const struct property_entry acer_b1_750_bma250e_props[] = {
27 PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", acer_b1_750_mount_matrix),
28 { }
29 };
30
31 static const struct software_node acer_b1_750_bma250e_node = {
32 .properties = acer_b1_750_bma250e_props,
33 };
34
35 static const struct x86_i2c_client_info acer_b1_750_i2c_clients[] __initconst = {
36 {
37 /* Novatek NVT-ts touchscreen */
38 .board_info = {
39 .type = "NVT-ts",
40 .addr = 0x34,
41 .dev_name = "NVT-ts",
42 },
43 .adapter_path = "\\_SB_.I2C4",
44 .irq_data = {
45 .type = X86_ACPI_IRQ_TYPE_GPIOINT,
46 .chip = "INT33FC:02",
47 .index = 3,
48 .trigger = ACPI_EDGE_SENSITIVE,
49 .polarity = ACPI_ACTIVE_LOW,
50 },
51 }, {
52 /* BMA250E accelerometer */
53 .board_info = {
54 .type = "bma250e",
55 .addr = 0x18,
56 .swnode = &acer_b1_750_bma250e_node,
57 },
58 .adapter_path = "\\_SB_.I2C3",
59 .irq_data = {
60 .type = X86_ACPI_IRQ_TYPE_GPIOINT,
61 .chip = "INT33FC:02",
62 .index = 25,
63 .trigger = ACPI_LEVEL_SENSITIVE,
64 .polarity = ACPI_ACTIVE_HIGH,
65 },
66 },
67 };
68
69 static struct gpiod_lookup_table acer_b1_750_nvt_ts_gpios = {
70 .dev_id = "i2c-NVT-ts",
71 .table = {
72 GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_LOW),
73 { }
74 },
75 };
76
77 static struct gpiod_lookup_table * const acer_b1_750_gpios[] = {
78 &acer_b1_750_nvt_ts_gpios,
79 &int3496_reference_gpios,
80 NULL
81 };
82
83 const struct x86_dev_info acer_b1_750_info __initconst = {
84 .i2c_client_info = acer_b1_750_i2c_clients,
85 .i2c_client_count = ARRAY_SIZE(acer_b1_750_i2c_clients),
86 .pdev_info = int3496_pdevs,
87 .pdev_count = 1,
88 .gpiod_lookup_tables = acer_b1_750_gpios,
89 };
90
91 /*
92 * Advantech MICA-071
93 * This is a standard Windows tablet, but it has an extra "quick launch" button
94 * which is not described in the ACPI tables in anyway.
95 * Use the x86-android-tablets infra to create a gpio-button device for this.
96 */
97 static const struct x86_gpio_button advantech_mica_071_button __initconst = {
98 .button = {
99 .code = KEY_PROG1,
100 .active_low = true,
101 .desc = "prog1_key",
102 .type = EV_KEY,
103 .wakeup = false,
104 .debounce_interval = 50,
105 },
106 .chip = "INT33FC:00",
107 .pin = 2,
108 };
109
110 const struct x86_dev_info advantech_mica_071_info __initconst = {
111 .gpio_button = &advantech_mica_071_button,
112 .gpio_button_count = 1,
113 };
114
115 /*
116 * When booted with the BIOS set to Android mode the Chuwi Hi8 (CWI509) DSDT
117 * contains a whole bunch of bogus ACPI I2C devices and is missing entries
118 * for the touchscreen and the accelerometer.
119 */
120 static const struct property_entry chuwi_hi8_gsl1680_props[] = {
121 PROPERTY_ENTRY_U32("touchscreen-size-x", 1665),
122 PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
123 PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
124 PROPERTY_ENTRY_BOOL("silead,home-button"),
125 PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi8.fw"),
126 { }
127 };
128
129 static const struct software_node chuwi_hi8_gsl1680_node = {
130 .properties = chuwi_hi8_gsl1680_props,
131 };
132
133 static const char * const chuwi_hi8_mount_matrix[] = {
134 "1", "0", "0",
135 "0", "-1", "0",
136 "0", "0", "1"
137 };
138
139 static const struct property_entry chuwi_hi8_bma250e_props[] = {
140 PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", chuwi_hi8_mount_matrix),
141 { }
142 };
143
144 static const struct software_node chuwi_hi8_bma250e_node = {
145 .properties = chuwi_hi8_bma250e_props,
146 };
147
148 static const struct x86_i2c_client_info chuwi_hi8_i2c_clients[] __initconst = {
149 {
150 /* Silead touchscreen */
151 .board_info = {
152 .type = "gsl1680",
153 .addr = 0x40,
154 .swnode = &chuwi_hi8_gsl1680_node,
155 },
156 .adapter_path = "\\_SB_.I2C4",
157 .irq_data = {
158 .type = X86_ACPI_IRQ_TYPE_APIC,
159 .index = 0x44,
160 .trigger = ACPI_EDGE_SENSITIVE,
161 .polarity = ACPI_ACTIVE_HIGH,
162 },
163 }, {
164 /* BMA250E accelerometer */
165 .board_info = {
166 .type = "bma250e",
167 .addr = 0x18,
168 .swnode = &chuwi_hi8_bma250e_node,
169 },
170 .adapter_path = "\\_SB_.I2C3",
171 .irq_data = {
172 .type = X86_ACPI_IRQ_TYPE_GPIOINT,
173 .chip = "INT33FC:02",
174 .index = 23,
175 .trigger = ACPI_LEVEL_SENSITIVE,
176 .polarity = ACPI_ACTIVE_HIGH,
177 },
178 },
179 };
180
chuwi_hi8_init(void)181 static int __init chuwi_hi8_init(void)
182 {
183 /*
184 * Avoid the acpi_unregister_gsi() call in x86_acpi_irq_helper_get()
185 * breaking the touchscreen + logging various errors when the Windows
186 * BIOS is used.
187 */
188 if (acpi_dev_present("MSSL0001", NULL, 1))
189 return -ENODEV;
190
191 return 0;
192 }
193
194 const struct x86_dev_info chuwi_hi8_info __initconst = {
195 .i2c_client_info = chuwi_hi8_i2c_clients,
196 .i2c_client_count = ARRAY_SIZE(chuwi_hi8_i2c_clients),
197 .init = chuwi_hi8_init,
198 };
199
200 /*
201 * Cyberbook T116 Android version
202 * This comes in both Windows and Android versions and even on Android
203 * the DSDT is mostly sane. This tablet has 2 extra general purpose buttons
204 * in the button row with the power + volume-buttons labeled P and F.
205 * Use the x86-android-tablets infra to create a gpio-button device for these.
206 */
207 static const struct x86_gpio_button cyberbook_t116_buttons[] __initconst = {
208 {
209 .button = {
210 .code = KEY_PROG1,
211 .active_low = true,
212 .desc = "prog1_key",
213 .type = EV_KEY,
214 .wakeup = false,
215 .debounce_interval = 50,
216 },
217 .chip = "INT33FF:00",
218 .pin = 30,
219 },
220 {
221 .button = {
222 .code = KEY_PROG2,
223 .active_low = true,
224 .desc = "prog2_key",
225 .type = EV_KEY,
226 .wakeup = false,
227 .debounce_interval = 50,
228 },
229 .chip = "INT33FF:03",
230 .pin = 48,
231 },
232 };
233
234 const struct x86_dev_info cyberbook_t116_info __initconst = {
235 .gpio_button = cyberbook_t116_buttons,
236 .gpio_button_count = ARRAY_SIZE(cyberbook_t116_buttons),
237 };
238
239 #define CZC_EC_EXTRA_PORT 0x68
240 #define CZC_EC_ANDROID_KEYS 0x63
241
czc_p10t_init(void)242 static int __init czc_p10t_init(void)
243 {
244 /*
245 * The device boots up in "Windows 7" mode, when the home button sends a
246 * Windows specific key sequence (Left Meta + D) and the second button
247 * sends an unknown one while also toggling the Radio Kill Switch.
248 * This is a surprising behavior when the second button is labeled "Back".
249 *
250 * The vendor-supplied Android-x86 build switches the device to a "Android"
251 * mode by writing value 0x63 to the I/O port 0x68. This just seems to just
252 * set bit 6 on address 0x96 in the EC region; switching the bit directly
253 * seems to achieve the same result. It uses a "p10t_switcher" to do the
254 * job. It doesn't seem to be able to do anything else, and no other use
255 * of the port 0x68 is known.
256 *
257 * In the Android mode, the home button sends just a single scancode,
258 * which can be handled in Linux userspace more reasonably and the back
259 * button only sends a scancode without toggling the kill switch.
260 * The scancode can then be mapped either to Back or RF Kill functionality
261 * in userspace, depending on how the button is labeled on that particular
262 * model.
263 */
264 outb(CZC_EC_ANDROID_KEYS, CZC_EC_EXTRA_PORT);
265 return 0;
266 }
267
268 const struct x86_dev_info czc_p10t __initconst = {
269 .init = czc_p10t_init,
270 };
271
272 /* Medion Lifetab S10346 tablets have an Android factory img with everything hardcoded */
273 static const char * const medion_lifetab_s10346_accel_mount_matrix[] = {
274 "0", "1", "0",
275 "1", "0", "0",
276 "0", "0", "1"
277 };
278
279 static const struct property_entry medion_lifetab_s10346_accel_props[] = {
280 PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", medion_lifetab_s10346_accel_mount_matrix),
281 { }
282 };
283
284 static const struct software_node medion_lifetab_s10346_accel_node = {
285 .properties = medion_lifetab_s10346_accel_props,
286 };
287
288 /* Note the LCD panel is mounted upside down, this is correctly indicated in the VBT */
289 static const struct property_entry medion_lifetab_s10346_touchscreen_props[] = {
290 PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"),
291 PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
292 { }
293 };
294
295 static const struct software_node medion_lifetab_s10346_touchscreen_node = {
296 .properties = medion_lifetab_s10346_touchscreen_props,
297 };
298
299 static const struct x86_i2c_client_info medion_lifetab_s10346_i2c_clients[] __initconst = {
300 {
301 /* kxtj21009 accel */
302 .board_info = {
303 .type = "kxtj21009",
304 .addr = 0x0f,
305 .dev_name = "kxtj21009",
306 .swnode = &medion_lifetab_s10346_accel_node,
307 },
308 .adapter_path = "\\_SB_.I2C3",
309 .irq_data = {
310 .type = X86_ACPI_IRQ_TYPE_GPIOINT,
311 .chip = "INT33FC:02",
312 .index = 23,
313 .trigger = ACPI_EDGE_SENSITIVE,
314 .polarity = ACPI_ACTIVE_HIGH,
315 },
316 }, {
317 /* goodix touchscreen */
318 .board_info = {
319 .type = "GDIX1001:00",
320 .addr = 0x14,
321 .dev_name = "goodix_ts",
322 .swnode = &medion_lifetab_s10346_touchscreen_node,
323 },
324 .adapter_path = "\\_SB_.I2C4",
325 .irq_data = {
326 .type = X86_ACPI_IRQ_TYPE_APIC,
327 .index = 0x44,
328 .trigger = ACPI_EDGE_SENSITIVE,
329 .polarity = ACPI_ACTIVE_LOW,
330 },
331 },
332 };
333
334 static struct gpiod_lookup_table medion_lifetab_s10346_goodix_gpios = {
335 .dev_id = "i2c-goodix_ts",
336 .table = {
337 GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH),
338 GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH),
339 { }
340 },
341 };
342
343 static struct gpiod_lookup_table * const medion_lifetab_s10346_gpios[] = {
344 &medion_lifetab_s10346_goodix_gpios,
345 NULL
346 };
347
348 const struct x86_dev_info medion_lifetab_s10346_info __initconst = {
349 .i2c_client_info = medion_lifetab_s10346_i2c_clients,
350 .i2c_client_count = ARRAY_SIZE(medion_lifetab_s10346_i2c_clients),
351 .gpiod_lookup_tables = medion_lifetab_s10346_gpios,
352 };
353
354 /* Nextbook Ares 8 (BYT) tablets have an Android factory img with everything hardcoded */
355 static const char * const nextbook_ares8_accel_mount_matrix[] = {
356 "0", "-1", "0",
357 "-1", "0", "0",
358 "0", "0", "1"
359 };
360
361 static const struct property_entry nextbook_ares8_accel_props[] = {
362 PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", nextbook_ares8_accel_mount_matrix),
363 { }
364 };
365
366 static const struct software_node nextbook_ares8_accel_node = {
367 .properties = nextbook_ares8_accel_props,
368 };
369
370 static const struct property_entry nextbook_ares8_touchscreen_props[] = {
371 PROPERTY_ENTRY_U32("touchscreen-size-x", 800),
372 PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
373 { }
374 };
375
376 static const struct software_node nextbook_ares8_touchscreen_node = {
377 .properties = nextbook_ares8_touchscreen_props,
378 };
379
380 static const struct x86_i2c_client_info nextbook_ares8_i2c_clients[] __initconst = {
381 {
382 /* Freescale MMA8653FC accel */
383 .board_info = {
384 .type = "mma8653",
385 .addr = 0x1d,
386 .dev_name = "mma8653",
387 .swnode = &nextbook_ares8_accel_node,
388 },
389 .adapter_path = "\\_SB_.I2C3",
390 }, {
391 /* FT5416DQ9 touchscreen controller */
392 .board_info = {
393 .type = "edt-ft5x06",
394 .addr = 0x38,
395 .dev_name = "ft5416",
396 .swnode = &nextbook_ares8_touchscreen_node,
397 },
398 .adapter_path = "\\_SB_.I2C4",
399 .irq_data = {
400 .type = X86_ACPI_IRQ_TYPE_GPIOINT,
401 .chip = "INT33FC:02",
402 .index = 3,
403 .trigger = ACPI_EDGE_SENSITIVE,
404 .polarity = ACPI_ACTIVE_LOW,
405 },
406 },
407 };
408
409 static struct gpiod_lookup_table * const nextbook_ares8_gpios[] = {
410 &int3496_reference_gpios,
411 NULL
412 };
413
414 const struct x86_dev_info nextbook_ares8_info __initconst = {
415 .i2c_client_info = nextbook_ares8_i2c_clients,
416 .i2c_client_count = ARRAY_SIZE(nextbook_ares8_i2c_clients),
417 .pdev_info = int3496_pdevs,
418 .pdev_count = 1,
419 .gpiod_lookup_tables = nextbook_ares8_gpios,
420 };
421
422 /* Nextbook Ares 8A (CHT) tablets have an Android factory img with everything hardcoded */
423 static const char * const nextbook_ares8a_accel_mount_matrix[] = {
424 "1", "0", "0",
425 "0", "-1", "0",
426 "0", "0", "1"
427 };
428
429 static const struct property_entry nextbook_ares8a_accel_props[] = {
430 PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", nextbook_ares8a_accel_mount_matrix),
431 { }
432 };
433
434 static const struct software_node nextbook_ares8a_accel_node = {
435 .properties = nextbook_ares8a_accel_props,
436 };
437
438 static const struct x86_i2c_client_info nextbook_ares8a_i2c_clients[] __initconst = {
439 {
440 /* Freescale MMA8653FC accel */
441 .board_info = {
442 .type = "mma8653",
443 .addr = 0x1d,
444 .dev_name = "mma8653",
445 .swnode = &nextbook_ares8a_accel_node,
446 },
447 .adapter_path = "\\_SB_.PCI0.I2C3",
448 }, {
449 /* FT5416DQ9 touchscreen controller */
450 .board_info = {
451 .type = "edt-ft5x06",
452 .addr = 0x38,
453 .dev_name = "ft5416",
454 .swnode = &nextbook_ares8_touchscreen_node,
455 },
456 .adapter_path = "\\_SB_.PCI0.I2C6",
457 .irq_data = {
458 .type = X86_ACPI_IRQ_TYPE_GPIOINT,
459 .chip = "INT33FF:01",
460 .index = 17,
461 .trigger = ACPI_EDGE_SENSITIVE,
462 .polarity = ACPI_ACTIVE_LOW,
463 },
464 },
465 };
466
467 static struct gpiod_lookup_table nextbook_ares8a_ft5416_gpios = {
468 .dev_id = "i2c-ft5416",
469 .table = {
470 GPIO_LOOKUP("INT33FF:01", 25, "reset", GPIO_ACTIVE_LOW),
471 { }
472 },
473 };
474
475 static struct gpiod_lookup_table * const nextbook_ares8a_gpios[] = {
476 &nextbook_ares8a_ft5416_gpios,
477 NULL
478 };
479
480 const struct x86_dev_info nextbook_ares8a_info __initconst = {
481 .i2c_client_info = nextbook_ares8a_i2c_clients,
482 .i2c_client_count = ARRAY_SIZE(nextbook_ares8a_i2c_clients),
483 .gpiod_lookup_tables = nextbook_ares8a_gpios,
484 };
485
486 /*
487 * Peaq C1010
488 * This is a standard Windows tablet, but it has a special Dolby button.
489 * This button has a WMI interface, but that is broken. Instead of trying to
490 * use the broken WMI interface, instantiate a gpio_keys device for this.
491 */
492 static const struct x86_gpio_button peaq_c1010_button __initconst = {
493 .button = {
494 .code = KEY_SOUND,
495 .active_low = true,
496 .desc = "dolby_key",
497 .type = EV_KEY,
498 .wakeup = false,
499 .debounce_interval = 50,
500 },
501 .chip = "INT33FC:00",
502 .pin = 3,
503 };
504
505 const struct x86_dev_info peaq_c1010_info __initconst = {
506 .gpio_button = &peaq_c1010_button,
507 .gpio_button_count = 1,
508 /*
509 * Move the ACPI event handler used by the broken WMI interface out of
510 * the way. This is the only event handler on INT33FC:00.
511 */
512 .invalid_aei_gpiochip = "INT33FC:00",
513 };
514
515 /*
516 * Whitelabel (sold as various brands) TM800A550L tablets.
517 * These tablet's DSDT contains a whole bunch of bogus ACPI I2C devices
518 * (removed through acpi_quirk_skip_i2c_client_enumeration()) and
519 * the touchscreen fwnode has the wrong GPIOs.
520 */
521 static const char * const whitelabel_tm800a550l_accel_mount_matrix[] = {
522 "-1", "0", "0",
523 "0", "1", "0",
524 "0", "0", "1"
525 };
526
527 static const struct property_entry whitelabel_tm800a550l_accel_props[] = {
528 PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", whitelabel_tm800a550l_accel_mount_matrix),
529 { }
530 };
531
532 static const struct software_node whitelabel_tm800a550l_accel_node = {
533 .properties = whitelabel_tm800a550l_accel_props,
534 };
535
536 static const struct property_entry whitelabel_tm800a550l_goodix_props[] = {
537 PROPERTY_ENTRY_STRING("firmware-name", "gt912-tm800a550l.fw"),
538 PROPERTY_ENTRY_STRING("goodix,config-name", "gt912-tm800a550l.cfg"),
539 PROPERTY_ENTRY_U32("goodix,main-clk", 54),
540 { }
541 };
542
543 static const struct software_node whitelabel_tm800a550l_goodix_node = {
544 .properties = whitelabel_tm800a550l_goodix_props,
545 };
546
547 static const struct x86_i2c_client_info whitelabel_tm800a550l_i2c_clients[] __initconst = {
548 {
549 /* goodix touchscreen */
550 .board_info = {
551 .type = "GDIX1001:00",
552 .addr = 0x14,
553 .dev_name = "goodix_ts",
554 .swnode = &whitelabel_tm800a550l_goodix_node,
555 },
556 .adapter_path = "\\_SB_.I2C2",
557 .irq_data = {
558 .type = X86_ACPI_IRQ_TYPE_APIC,
559 .index = 0x44,
560 .trigger = ACPI_EDGE_SENSITIVE,
561 .polarity = ACPI_ACTIVE_HIGH,
562 },
563 }, {
564 /* kxcj91008 accel */
565 .board_info = {
566 .type = "kxcj91008",
567 .addr = 0x0f,
568 .dev_name = "kxcj91008",
569 .swnode = &whitelabel_tm800a550l_accel_node,
570 },
571 .adapter_path = "\\_SB_.I2C3",
572 },
573 };
574
575 static struct gpiod_lookup_table whitelabel_tm800a550l_goodix_gpios = {
576 .dev_id = "i2c-goodix_ts",
577 .table = {
578 GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH),
579 GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH),
580 { }
581 },
582 };
583
584 static struct gpiod_lookup_table * const whitelabel_tm800a550l_gpios[] = {
585 &whitelabel_tm800a550l_goodix_gpios,
586 NULL
587 };
588
589 const struct x86_dev_info whitelabel_tm800a550l_info __initconst = {
590 .i2c_client_info = whitelabel_tm800a550l_i2c_clients,
591 .i2c_client_count = ARRAY_SIZE(whitelabel_tm800a550l_i2c_clients),
592 .gpiod_lookup_tables = whitelabel_tm800a550l_gpios,
593 };
594
595 /*
596 * If the EFI bootloader is not Xiaomi's own signed Android loader, then the
597 * Xiaomi Mi Pad 2 X86 tablet sets OSID in the DSDT to 1 (Windows), causing
598 * a bunch of devices to be hidden.
599 *
600 * This takes care of instantiating the hidden devices manually.
601 */
602 static const struct x86_i2c_client_info xiaomi_mipad2_i2c_clients[] __initconst = {
603 {
604 /* BQ27520 fuel-gauge */
605 .board_info = {
606 .type = "bq27520",
607 .addr = 0x55,
608 .dev_name = "bq27520",
609 .swnode = &fg_bq25890_supply_node,
610 },
611 .adapter_path = "\\_SB_.PCI0.I2C1",
612 }, {
613 /* KTD2026 RGB notification LED controller */
614 .board_info = {
615 .type = "ktd2026",
616 .addr = 0x30,
617 .dev_name = "ktd2026",
618 },
619 .adapter_path = "\\_SB_.PCI0.I2C3",
620 },
621 };
622
623 const struct x86_dev_info xiaomi_mipad2_info __initconst = {
624 .i2c_client_info = xiaomi_mipad2_i2c_clients,
625 .i2c_client_count = ARRAY_SIZE(xiaomi_mipad2_i2c_clients),
626 };
627