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

source code of linux/drivers/platform/x86/x86-android-tablets/other.c