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("MSSL0001", NULL, 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