Linux Audio

Check our new training course

Linux debugging, profiling, tracing and performance analysis training

Mar 24-27, 2025, special US time zones
Register
Loading...
Note: File does not exist in v3.1.
  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/leds.h>
 15#include <linux/pci.h>
 16#include <linux/platform_device.h>
 17#include <linux/pwm.h>
 18
 19#include <dt-bindings/leds/common.h>
 20
 21#include "shared-psy-info.h"
 22#include "x86-android-tablets.h"
 23
 24/* Acer Iconia One 7 B1-750 has an Android factory image with everything hardcoded */
 25static const char * const acer_b1_750_mount_matrix[] = {
 26	"-1", "0", "0",
 27	"0", "1", "0",
 28	"0", "0", "1"
 29};
 30
 31static const struct property_entry acer_b1_750_bma250e_props[] = {
 32	PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", acer_b1_750_mount_matrix),
 33	{ }
 34};
 35
 36static const struct software_node acer_b1_750_bma250e_node = {
 37	.properties = acer_b1_750_bma250e_props,
 38};
 39
 40static const struct x86_i2c_client_info acer_b1_750_i2c_clients[] __initconst = {
 41	{
 42		/* Novatek NVT-ts touchscreen */
 43		.board_info = {
 44			.type = "nt11205-ts",
 45			.addr = 0x34,
 46			.dev_name = "NVT-ts",
 47		},
 48		.adapter_path = "\\_SB_.I2C4",
 49		.irq_data = {
 50			.type = X86_ACPI_IRQ_TYPE_GPIOINT,
 51			.chip = "INT33FC:02",
 52			.index = 3,
 53			.trigger = ACPI_EDGE_SENSITIVE,
 54			.polarity = ACPI_ACTIVE_LOW,
 55			.con_id = "NVT-ts_irq",
 56		},
 57	}, {
 58		/* BMA250E accelerometer */
 59		.board_info = {
 60			.type = "bma250e",
 61			.addr = 0x18,
 62			.swnode = &acer_b1_750_bma250e_node,
 63		},
 64		.adapter_path = "\\_SB_.I2C3",
 65		.irq_data = {
 66			.type = X86_ACPI_IRQ_TYPE_GPIOINT,
 67			.chip = "INT33FC:02",
 68			.index = 25,
 69			.trigger = ACPI_LEVEL_SENSITIVE,
 70			.polarity = ACPI_ACTIVE_HIGH,
 71			.con_id = "bma250e_irq",
 72		},
 73	},
 74};
 75
 76static struct gpiod_lookup_table acer_b1_750_nvt_ts_gpios = {
 77	.dev_id = "i2c-NVT-ts",
 78	.table = {
 79		GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_LOW),
 80		{ }
 81	},
 82};
 83
 84static struct gpiod_lookup_table * const acer_b1_750_gpios[] = {
 85	&acer_b1_750_nvt_ts_gpios,
 86	&int3496_reference_gpios,
 87	NULL
 88};
 89
 90const struct x86_dev_info acer_b1_750_info __initconst = {
 91	.i2c_client_info = acer_b1_750_i2c_clients,
 92	.i2c_client_count = ARRAY_SIZE(acer_b1_750_i2c_clients),
 93	.pdev_info = int3496_pdevs,
 94	.pdev_count = 1,
 95	.gpiod_lookup_tables = acer_b1_750_gpios,
 96};
 97
 98/*
 99 * Advantech MICA-071
100 * This is a standard Windows tablet, but it has an extra "quick launch" button
101 * which is not described in the ACPI tables in anyway.
102 * Use the x86-android-tablets infra to create a gpio-keys device for this.
103 */
104static const struct x86_gpio_button advantech_mica_071_button __initconst = {
105	.button = {
106		.code = KEY_PROG1,
107		.active_low = true,
108		.desc = "prog1_key",
109		.type = EV_KEY,
110		.wakeup = false,
111		.debounce_interval = 50,
112	},
113	.chip = "INT33FC:00",
114	.pin = 2,
115};
116
117const struct x86_dev_info advantech_mica_071_info __initconst = {
118	.gpio_button = &advantech_mica_071_button,
119	.gpio_button_count = 1,
120};
121
122/*
123 * When booted with the BIOS set to Android mode the Chuwi Hi8 (CWI509) DSDT
124 * contains a whole bunch of bogus ACPI I2C devices and is missing entries
125 * for the touchscreen and the accelerometer.
126 */
127static const struct property_entry chuwi_hi8_gsl1680_props[] = {
128	PROPERTY_ENTRY_U32("touchscreen-size-x", 1665),
129	PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
130	PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
131	PROPERTY_ENTRY_BOOL("silead,home-button"),
132	PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi8.fw"),
133	{ }
134};
135
136static const struct software_node chuwi_hi8_gsl1680_node = {
137	.properties = chuwi_hi8_gsl1680_props,
138};
139
140static const char * const chuwi_hi8_mount_matrix[] = {
141	"1", "0", "0",
142	"0", "-1", "0",
143	"0", "0", "1"
144};
145
146static const struct property_entry chuwi_hi8_bma250e_props[] = {
147	PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", chuwi_hi8_mount_matrix),
148	{ }
149};
150
151static const struct software_node chuwi_hi8_bma250e_node = {
152	.properties = chuwi_hi8_bma250e_props,
153};
154
155static const struct x86_i2c_client_info chuwi_hi8_i2c_clients[] __initconst = {
156	{
157		/* Silead touchscreen */
158		.board_info = {
159			.type = "gsl1680",
160			.addr = 0x40,
161			.swnode = &chuwi_hi8_gsl1680_node,
162		},
163		.adapter_path = "\\_SB_.I2C4",
164		.irq_data = {
165			.type = X86_ACPI_IRQ_TYPE_APIC,
166			.index = 0x44,
167			.trigger = ACPI_EDGE_SENSITIVE,
168			.polarity = ACPI_ACTIVE_HIGH,
169		},
170	}, {
171		/* BMA250E accelerometer */
172		.board_info = {
173			.type = "bma250e",
174			.addr = 0x18,
175			.swnode = &chuwi_hi8_bma250e_node,
176		},
177		.adapter_path = "\\_SB_.I2C3",
178		.irq_data = {
179			.type = X86_ACPI_IRQ_TYPE_GPIOINT,
180			.chip = "INT33FC:02",
181			.index = 23,
182			.trigger = ACPI_LEVEL_SENSITIVE,
183			.polarity = ACPI_ACTIVE_HIGH,
184			.con_id = "bma250e_irq",
185		},
186	},
187};
188
189static int __init chuwi_hi8_init(struct device *dev)
190{
191	/*
192	 * Avoid the acpi_unregister_gsi() call in x86_acpi_irq_helper_get()
193	 * breaking the touchscreen + logging various errors when the Windows
194	 * BIOS is used.
195	 */
196	if (acpi_dev_present("MSSL0001", NULL, 1))
197		return -ENODEV;
198
199	return 0;
200}
201
202const struct x86_dev_info chuwi_hi8_info __initconst = {
203	.i2c_client_info = chuwi_hi8_i2c_clients,
204	.i2c_client_count = ARRAY_SIZE(chuwi_hi8_i2c_clients),
205	.init = chuwi_hi8_init,
206};
207
208/*
209 * Cyberbook T116 Android version
210 * This comes in both Windows and Android versions and even on Android
211 * the DSDT is mostly sane. This tablet has 2 extra general purpose buttons
212 * in the button row with the power + volume-buttons labeled P and F.
213 * Use the x86-android-tablets infra to create a gpio-keys device for these.
214 */
215static const struct x86_gpio_button cyberbook_t116_buttons[] __initconst = {
216	{
217		.button = {
218			.code = KEY_PROG1,
219			.active_low = true,
220			.desc = "prog1_key",
221			.type = EV_KEY,
222			.wakeup = false,
223			.debounce_interval = 50,
224		},
225		.chip = "INT33FF:00",
226		.pin = 30,
227	},
228	{
229		.button = {
230			.code = KEY_PROG2,
231			.active_low = true,
232			.desc = "prog2_key",
233			.type = EV_KEY,
234			.wakeup = false,
235			.debounce_interval = 50,
236		},
237		.chip = "INT33FF:03",
238		.pin = 48,
239	},
240};
241
242const struct x86_dev_info cyberbook_t116_info __initconst = {
243	.gpio_button = cyberbook_t116_buttons,
244	.gpio_button_count = ARRAY_SIZE(cyberbook_t116_buttons),
245};
246
247#define CZC_EC_EXTRA_PORT	0x68
248#define CZC_EC_ANDROID_KEYS	0x63
249
250static int __init czc_p10t_init(struct device *dev)
251{
252	/*
253	 * The device boots up in "Windows 7" mode, when the home button sends a
254	 * Windows specific key sequence (Left Meta + D) and the second button
255	 * sends an unknown one while also toggling the Radio Kill Switch.
256	 * This is a surprising behavior when the second button is labeled "Back".
257	 *
258	 * The vendor-supplied Android-x86 build switches the device to a "Android"
259	 * mode by writing value 0x63 to the I/O port 0x68. This just seems to just
260	 * set bit 6 on address 0x96 in the EC region; switching the bit directly
261	 * seems to achieve the same result. It uses a "p10t_switcher" to do the
262	 * job. It doesn't seem to be able to do anything else, and no other use
263	 * of the port 0x68 is known.
264	 *
265	 * In the Android mode, the home button sends just a single scancode,
266	 * which can be handled in Linux userspace more reasonably and the back
267	 * button only sends a scancode without toggling the kill switch.
268	 * The scancode can then be mapped either to Back or RF Kill functionality
269	 * in userspace, depending on how the button is labeled on that particular
270	 * model.
271	 */
272	outb(CZC_EC_ANDROID_KEYS, CZC_EC_EXTRA_PORT);
273	return 0;
274}
275
276const struct x86_dev_info czc_p10t __initconst = {
277	.init = czc_p10t_init,
278};
279
280/* Medion Lifetab S10346 tablets have an Android factory image with everything hardcoded */
281static const char * const medion_lifetab_s10346_accel_mount_matrix[] = {
282	"0", "1", "0",
283	"1", "0", "0",
284	"0", "0", "1"
285};
286
287static const struct property_entry medion_lifetab_s10346_accel_props[] = {
288	PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", medion_lifetab_s10346_accel_mount_matrix),
289	{ }
290};
291
292static const struct software_node medion_lifetab_s10346_accel_node = {
293	.properties = medion_lifetab_s10346_accel_props,
294};
295
296/* Note the LCD panel is mounted upside down, this is correctly indicated in the VBT */
297static const struct property_entry medion_lifetab_s10346_touchscreen_props[] = {
298	PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"),
299	PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
300	{ }
301};
302
303static const struct software_node medion_lifetab_s10346_touchscreen_node = {
304	.properties = medion_lifetab_s10346_touchscreen_props,
305};
306
307static const struct x86_i2c_client_info medion_lifetab_s10346_i2c_clients[] __initconst = {
308	{
309		/* kxtj21009 accelerometer */
310		.board_info = {
311			.type = "kxtj21009",
312			.addr = 0x0f,
313			.dev_name = "kxtj21009",
314			.swnode = &medion_lifetab_s10346_accel_node,
315		},
316		.adapter_path = "\\_SB_.I2C3",
317		.irq_data = {
318			.type = X86_ACPI_IRQ_TYPE_GPIOINT,
319			.chip = "INT33FC:02",
320			.index = 23,
321			.trigger = ACPI_EDGE_SENSITIVE,
322			.polarity = ACPI_ACTIVE_HIGH,
323			.con_id = "kxtj21009_irq",
324		},
325	}, {
326		/* goodix touchscreen */
327		.board_info = {
328			.type = "GDIX1001:00",
329			.addr = 0x14,
330			.dev_name = "goodix_ts",
331			.swnode = &medion_lifetab_s10346_touchscreen_node,
332		},
333		.adapter_path = "\\_SB_.I2C4",
334		.irq_data = {
335			.type = X86_ACPI_IRQ_TYPE_APIC,
336			.index = 0x44,
337			.trigger = ACPI_EDGE_SENSITIVE,
338			.polarity = ACPI_ACTIVE_LOW,
339		},
340	},
341};
342
343static struct gpiod_lookup_table medion_lifetab_s10346_goodix_gpios = {
344	.dev_id = "i2c-goodix_ts",
345	.table = {
346		GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH),
347		GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH),
348		{ }
349	},
350};
351
352static struct gpiod_lookup_table * const medion_lifetab_s10346_gpios[] = {
353	&medion_lifetab_s10346_goodix_gpios,
354	NULL
355};
356
357const struct x86_dev_info medion_lifetab_s10346_info __initconst = {
358	.i2c_client_info = medion_lifetab_s10346_i2c_clients,
359	.i2c_client_count = ARRAY_SIZE(medion_lifetab_s10346_i2c_clients),
360	.gpiod_lookup_tables = medion_lifetab_s10346_gpios,
361};
362
363/* Nextbook Ares 8 (BYT) tablets have an Android factory image with everything hardcoded */
364static const char * const nextbook_ares8_accel_mount_matrix[] = {
365	"0", "-1", "0",
366	"-1", "0", "0",
367	"0", "0", "1"
368};
369
370static const struct property_entry nextbook_ares8_accel_props[] = {
371	PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", nextbook_ares8_accel_mount_matrix),
372	{ }
373};
374
375static const struct software_node nextbook_ares8_accel_node = {
376	.properties = nextbook_ares8_accel_props,
377};
378
379static const struct property_entry nextbook_ares8_touchscreen_props[] = {
380	PROPERTY_ENTRY_U32("touchscreen-size-x", 800),
381	PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
382	{ }
383};
384
385static const struct software_node nextbook_ares8_touchscreen_node = {
386	.properties = nextbook_ares8_touchscreen_props,
387};
388
389static const struct x86_i2c_client_info nextbook_ares8_i2c_clients[] __initconst = {
390	{
391		/* Freescale MMA8653FC accelerometer */
392		.board_info = {
393			.type = "mma8653",
394			.addr = 0x1d,
395			.dev_name = "mma8653",
396			.swnode = &nextbook_ares8_accel_node,
397		},
398		.adapter_path = "\\_SB_.I2C3",
399	}, {
400		/* FT5416DQ9 touchscreen controller */
401		.board_info = {
402			.type = "edt-ft5x06",
403			.addr = 0x38,
404			.dev_name = "ft5416",
405			.swnode = &nextbook_ares8_touchscreen_node,
406		},
407		.adapter_path = "\\_SB_.I2C4",
408		.irq_data = {
409			.type = X86_ACPI_IRQ_TYPE_GPIOINT,
410			.chip = "INT33FC:02",
411			.index = 3,
412			.trigger = ACPI_EDGE_SENSITIVE,
413			.polarity = ACPI_ACTIVE_LOW,
414			.con_id = "ft5416_irq",
415		},
416	},
417};
418
419static struct gpiod_lookup_table * const nextbook_ares8_gpios[] = {
420	&int3496_reference_gpios,
421	NULL
422};
423
424const struct x86_dev_info nextbook_ares8_info __initconst = {
425	.i2c_client_info = nextbook_ares8_i2c_clients,
426	.i2c_client_count = ARRAY_SIZE(nextbook_ares8_i2c_clients),
427	.pdev_info = int3496_pdevs,
428	.pdev_count = 1,
429	.gpiod_lookup_tables = nextbook_ares8_gpios,
430};
431
432/* Nextbook Ares 8A (CHT) tablets have an Android factory image with everything hardcoded */
433static const char * const nextbook_ares8a_accel_mount_matrix[] = {
434	"1", "0", "0",
435	"0", "-1", "0",
436	"0", "0", "1"
437};
438
439static const struct property_entry nextbook_ares8a_accel_props[] = {
440	PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", nextbook_ares8a_accel_mount_matrix),
441	{ }
442};
443
444static const struct software_node nextbook_ares8a_accel_node = {
445	.properties = nextbook_ares8a_accel_props,
446};
447
448static const struct x86_i2c_client_info nextbook_ares8a_i2c_clients[] __initconst = {
449	{
450		/* Freescale MMA8653FC accelerometer */
451		.board_info = {
452			.type = "mma8653",
453			.addr = 0x1d,
454			.dev_name = "mma8653",
455			.swnode = &nextbook_ares8a_accel_node,
456		},
457		.adapter_path = "\\_SB_.PCI0.I2C3",
458	}, {
459		/* FT5416DQ9 touchscreen controller */
460		.board_info = {
461			.type = "edt-ft5x06",
462			.addr = 0x38,
463			.dev_name = "ft5416",
464			.swnode = &nextbook_ares8_touchscreen_node,
465		},
466		.adapter_path = "\\_SB_.PCI0.I2C6",
467		.irq_data = {
468			.type = X86_ACPI_IRQ_TYPE_GPIOINT,
469			.chip = "INT33FF:01",
470			.index = 17,
471			.trigger = ACPI_EDGE_SENSITIVE,
472			.polarity = ACPI_ACTIVE_LOW,
473			.con_id = "ft5416_irq",
474		},
475	},
476};
477
478static struct gpiod_lookup_table nextbook_ares8a_ft5416_gpios = {
479	.dev_id = "i2c-ft5416",
480	.table = {
481		GPIO_LOOKUP("INT33FF:01", 25, "reset", GPIO_ACTIVE_LOW),
482		{ }
483	},
484};
485
486static struct gpiod_lookup_table * const nextbook_ares8a_gpios[] = {
487	&nextbook_ares8a_ft5416_gpios,
488	NULL
489};
490
491const struct x86_dev_info nextbook_ares8a_info __initconst = {
492	.i2c_client_info = nextbook_ares8a_i2c_clients,
493	.i2c_client_count = ARRAY_SIZE(nextbook_ares8a_i2c_clients),
494	.gpiod_lookup_tables = nextbook_ares8a_gpios,
495};
496
497/*
498 * Peaq C1010
499 * This is a standard Windows tablet, but it has a special Dolby button.
500 * This button has a WMI interface, but that is broken. Instead of trying to
501 * use the broken WMI interface, instantiate a gpio-keys device for this.
502 */
503static const struct x86_gpio_button peaq_c1010_button __initconst = {
504	.button = {
505		.code = KEY_SOUND,
506		.active_low = true,
507		.desc = "dolby_key",
508		.type = EV_KEY,
509		.wakeup = false,
510		.debounce_interval = 50,
511	},
512	.chip = "INT33FC:00",
513	.pin = 3,
514};
515
516const struct x86_dev_info peaq_c1010_info __initconst = {
517	.gpio_button = &peaq_c1010_button,
518	.gpio_button_count = 1,
519};
520
521/*
522 * Whitelabel (sold as various brands) TM800A550L tablets.
523 * These tablet's DSDT contains a whole bunch of bogus ACPI I2C devices
524 * (removed through acpi_quirk_skip_i2c_client_enumeration()) and
525 * the touchscreen firmware node has the wrong GPIOs.
526 */
527static const char * const whitelabel_tm800a550l_accel_mount_matrix[] = {
528	"-1", "0", "0",
529	"0", "1", "0",
530	"0", "0", "1"
531};
532
533static const struct property_entry whitelabel_tm800a550l_accel_props[] = {
534	PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", whitelabel_tm800a550l_accel_mount_matrix),
535	{ }
536};
537
538static const struct software_node whitelabel_tm800a550l_accel_node = {
539	.properties = whitelabel_tm800a550l_accel_props,
540};
541
542static const struct property_entry whitelabel_tm800a550l_goodix_props[] = {
543	PROPERTY_ENTRY_STRING("firmware-name", "gt912-tm800a550l.fw"),
544	PROPERTY_ENTRY_STRING("goodix,config-name", "gt912-tm800a550l.cfg"),
545	PROPERTY_ENTRY_U32("goodix,main-clk", 54),
546	{ }
547};
548
549static const struct software_node whitelabel_tm800a550l_goodix_node = {
550	.properties = whitelabel_tm800a550l_goodix_props,
551};
552
553static const struct x86_i2c_client_info whitelabel_tm800a550l_i2c_clients[] __initconst = {
554	{
555		/* goodix touchscreen */
556		.board_info = {
557			.type = "GDIX1001:00",
558			.addr = 0x14,
559			.dev_name = "goodix_ts",
560			.swnode = &whitelabel_tm800a550l_goodix_node,
561		},
562		.adapter_path = "\\_SB_.I2C2",
563		.irq_data = {
564			.type = X86_ACPI_IRQ_TYPE_APIC,
565			.index = 0x44,
566			.trigger = ACPI_EDGE_SENSITIVE,
567			.polarity = ACPI_ACTIVE_HIGH,
568		},
569	}, {
570		/* kxcj91008 accelerometer */
571		.board_info = {
572			.type = "kxcj91008",
573			.addr = 0x0f,
574			.dev_name = "kxcj91008",
575			.swnode = &whitelabel_tm800a550l_accel_node,
576		},
577		.adapter_path = "\\_SB_.I2C3",
578	},
579};
580
581static struct gpiod_lookup_table whitelabel_tm800a550l_goodix_gpios = {
582	.dev_id = "i2c-goodix_ts",
583	.table = {
584		GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH),
585		GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH),
586		{ }
587	},
588};
589
590static struct gpiod_lookup_table * const whitelabel_tm800a550l_gpios[] = {
591	&whitelabel_tm800a550l_goodix_gpios,
592	NULL
593};
594
595const struct x86_dev_info whitelabel_tm800a550l_info __initconst = {
596	.i2c_client_info = whitelabel_tm800a550l_i2c_clients,
597	.i2c_client_count = ARRAY_SIZE(whitelabel_tm800a550l_i2c_clients),
598	.gpiod_lookup_tables = whitelabel_tm800a550l_gpios,
599};
600
601/*
602 * Vexia EDU ATLA 10 tablet, Android 4.2 / 4.4 + Guadalinex Ubuntu tablet
603 * distributed to schools in the Spanish Andalucía region.
604 */
605static const char * const crystal_cove_pwrsrc_psy[] = { "crystal_cove_pwrsrc" };
606
607static const struct property_entry vexia_edu_atla10_ulpmc_props[] = {
608	PROPERTY_ENTRY_STRING_ARRAY("supplied-from", crystal_cove_pwrsrc_psy),
609	{ }
610};
611
612static const struct software_node vexia_edu_atla10_ulpmc_node = {
613	.properties = vexia_edu_atla10_ulpmc_props,
614};
615
616static const char * const vexia_edu_atla10_accel_mount_matrix[] = {
617	"0", "-1", "0",
618	"1", "0", "0",
619	"0", "0", "1"
620};
621
622static const struct property_entry vexia_edu_atla10_accel_props[] = {
623	PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", vexia_edu_atla10_accel_mount_matrix),
624	{ }
625};
626
627static const struct software_node vexia_edu_atla10_accel_node = {
628	.properties = vexia_edu_atla10_accel_props,
629};
630
631static const struct property_entry vexia_edu_atla10_touchscreen_props[] = {
632	PROPERTY_ENTRY_U32("hid-descr-addr", 0x0000),
633	PROPERTY_ENTRY_U32("post-reset-deassert-delay-ms", 120),
634	{ }
635};
636
637static const struct software_node vexia_edu_atla10_touchscreen_node = {
638	.properties = vexia_edu_atla10_touchscreen_props,
639};
640
641static const struct property_entry vexia_edu_atla10_pmic_props[] = {
642	PROPERTY_ENTRY_BOOL("linux,register-pwrsrc-power_supply"),
643	{ }
644};
645
646static const struct software_node vexia_edu_atla10_pmic_node = {
647	.properties = vexia_edu_atla10_pmic_props,
648};
649
650static const struct x86_i2c_client_info vexia_edu_atla10_i2c_clients[] __initconst = {
651	{
652		/* I2C attached embedded controller, used to access fuel-gauge */
653		.board_info = {
654			.type = "vexia_atla10_ec",
655			.addr = 0x76,
656			.dev_name = "ulpmc",
657			.swnode = &vexia_edu_atla10_ulpmc_node,
658		},
659		.adapter_path = "0000:00:18.1",
660	}, {
661		/* RT5642 audio codec */
662		.board_info = {
663			.type = "rt5640",
664			.addr = 0x1c,
665			.dev_name = "rt5640",
666		},
667		.adapter_path = "0000:00:18.2",
668		.irq_data = {
669			.type = X86_ACPI_IRQ_TYPE_GPIOINT,
670			.chip = "INT33FC:02",
671			.index = 4,
672			.trigger = ACPI_EDGE_SENSITIVE,
673			.polarity = ACPI_ACTIVE_HIGH,
674			.con_id = "rt5640_irq",
675		},
676	}, {
677		/* kxtj21009 accelerometer */
678		.board_info = {
679			.type = "kxtj21009",
680			.addr = 0x0f,
681			.dev_name = "kxtj21009",
682			.swnode = &vexia_edu_atla10_accel_node,
683		},
684		.adapter_path = "0000:00:18.5",
685	}, {
686		/* FT5416DQ9 touchscreen controller */
687		.board_info = {
688			.type = "hid-over-i2c",
689			.addr = 0x38,
690			.dev_name = "FTSC1000",
691			.swnode = &vexia_edu_atla10_touchscreen_node,
692		},
693		.adapter_path = "0000:00:18.6",
694		.irq_data = {
695			.type = X86_ACPI_IRQ_TYPE_APIC,
696			.index = 0x45,
697			.trigger = ACPI_LEVEL_SENSITIVE,
698			.polarity = ACPI_ACTIVE_HIGH,
699		},
700	}, {
701		/* Crystal Cove PMIC */
702		.board_info = {
703			.type = "intel_soc_pmic_crc",
704			.addr = 0x6e,
705			.dev_name = "intel_soc_pmic_crc",
706			.swnode = &vexia_edu_atla10_pmic_node,
707		},
708		.adapter_path = "0000:00:18.7",
709		.irq_data = {
710			.type = X86_ACPI_IRQ_TYPE_APIC,
711			.index = 0x43,
712			.trigger = ACPI_LEVEL_SENSITIVE,
713			.polarity = ACPI_ACTIVE_HIGH,
714		},
715	}
716};
717
718static struct gpiod_lookup_table vexia_edu_atla10_ft5416_gpios = {
719	.dev_id = "i2c-FTSC1000",
720	.table = {
721		GPIO_LOOKUP("INT33FC:00", 60, "reset", GPIO_ACTIVE_LOW),
722		{ }
723	},
724};
725
726static struct gpiod_lookup_table * const vexia_edu_atla10_gpios[] = {
727	&vexia_edu_atla10_ft5416_gpios,
728	NULL
729};
730
731static int __init vexia_edu_atla10_init(struct device *dev)
732{
733	struct pci_dev *pdev;
734	int ret;
735
736	/* Enable the Wifi module by setting the wifi_enable pin to 1 */
737	ret = x86_android_tablet_get_gpiod("INT33FC:02", 20, "wifi_enable",
738					   false, GPIOD_OUT_HIGH, NULL);
739	if (ret)
740		return ret;
741
742	/* Reprobe the SDIO controller to enumerate the now enabled Wifi module */
743	pdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0x11, 0));
744	if (!pdev)
745		return -EPROBE_DEFER;
746
747	ret = device_reprobe(&pdev->dev);
748	if (ret)
749		pci_warn(pdev, "Reprobing error: %d\n", ret);
750
751	pci_dev_put(pdev);
752	return 0;
753}
754
755const struct x86_dev_info vexia_edu_atla10_info __initconst = {
756	.i2c_client_info = vexia_edu_atla10_i2c_clients,
757	.i2c_client_count = ARRAY_SIZE(vexia_edu_atla10_i2c_clients),
758	.gpiod_lookup_tables = vexia_edu_atla10_gpios,
759	.init = vexia_edu_atla10_init,
760	.use_pci_devname = true,
761};
762
763/*
764 * The firmware node for ktd2026 on Xaomi pad2. It composed of a RGB LED node
765 * with three subnodes for each color (B/G/R). The RGB LED node is named
766 * "multi-led" to align with the name in the device tree.
767 */
768
769/* Main firmware node for ktd2026 */
770static const struct software_node ktd2026_node = {
771	.name = "ktd2026",
772};
773
774static const struct property_entry ktd2026_rgb_led_props[] = {
775	PROPERTY_ENTRY_U32("reg", 0),
776	PROPERTY_ENTRY_U32("color", LED_COLOR_ID_RGB),
777	PROPERTY_ENTRY_STRING("label", "mipad2:rgb:indicator"),
778	PROPERTY_ENTRY_STRING("linux,default-trigger", "bq27520-0-charging-orange-full-green"),
779	{ }
780};
781
782static const struct software_node ktd2026_rgb_led_node = {
783	.name = "multi-led",
784	.properties = ktd2026_rgb_led_props,
785	.parent = &ktd2026_node,
786};
787
788static const struct property_entry ktd2026_blue_led_props[] = {
789	PROPERTY_ENTRY_U32("reg", 0),
790	PROPERTY_ENTRY_U32("color", LED_COLOR_ID_BLUE),
791	{ }
792};
793
794static const struct software_node ktd2026_blue_led_node = {
795	.properties = ktd2026_blue_led_props,
796	.parent = &ktd2026_rgb_led_node,
797};
798
799static const struct property_entry ktd2026_green_led_props[] = {
800	PROPERTY_ENTRY_U32("reg", 1),
801	PROPERTY_ENTRY_U32("color", LED_COLOR_ID_GREEN),
802	{ }
803};
804
805static const struct software_node ktd2026_green_led_node = {
806	.properties = ktd2026_green_led_props,
807	.parent = &ktd2026_rgb_led_node,
808};
809
810static const struct property_entry ktd2026_red_led_props[] = {
811	PROPERTY_ENTRY_U32("reg", 2),
812	PROPERTY_ENTRY_U32("color", LED_COLOR_ID_RED),
813	{ }
814};
815
816static const struct software_node ktd2026_red_led_node = {
817	.properties = ktd2026_red_led_props,
818	.parent = &ktd2026_rgb_led_node,
819};
820
821static const struct software_node *ktd2026_node_group[] = {
822	&ktd2026_node,
823	&ktd2026_rgb_led_node,
824	&ktd2026_red_led_node,
825	&ktd2026_green_led_node,
826	&ktd2026_blue_led_node,
827	NULL
828};
829
830/*
831 * For the LEDs which backlight the Menu / Home / Back capacitive buttons on
832 * the bottom bezel. These are attached to a TPS61158 LED controller which
833 * is controlled by the "pwm_soc_lpss_2" PWM output.
834 */
835#define XIAOMI_MIPAD2_LED_PERIOD_NS		19200
836#define XIAOMI_MIPAD2_LED_MAX_DUTY_NS		 6000 /* From Android kernel */
837
838static struct pwm_device *xiaomi_mipad2_led_pwm;
839
840static int xiaomi_mipad2_brightness_set(struct led_classdev *led_cdev,
841					enum led_brightness val)
842{
843	struct pwm_state state = {
844		.period = XIAOMI_MIPAD2_LED_PERIOD_NS,
845		.duty_cycle = XIAOMI_MIPAD2_LED_MAX_DUTY_NS * val / LED_FULL,
846		/* Always set PWM enabled to avoid the pin floating */
847		.enabled = true,
848	};
849
850	return pwm_apply_might_sleep(xiaomi_mipad2_led_pwm, &state);
851}
852
853static int __init xiaomi_mipad2_init(struct device *dev)
854{
855	struct led_classdev *led_cdev;
856	int ret;
857
858	xiaomi_mipad2_led_pwm = devm_pwm_get(dev, "pwm_soc_lpss_2");
859	if (IS_ERR(xiaomi_mipad2_led_pwm))
860		return dev_err_probe(dev, PTR_ERR(xiaomi_mipad2_led_pwm), "getting pwm\n");
861
862	led_cdev = devm_kzalloc(dev, sizeof(*led_cdev), GFP_KERNEL);
863	if (!led_cdev)
864		return -ENOMEM;
865
866	led_cdev->name = "mipad2:white:touch-buttons-backlight";
867	led_cdev->max_brightness = LED_FULL;
868	led_cdev->default_trigger = "input-events";
869	led_cdev->brightness_set_blocking = xiaomi_mipad2_brightness_set;
870	/* Turn LED off during suspend */
871	led_cdev->flags = LED_CORE_SUSPENDRESUME;
872
873	ret = devm_led_classdev_register(dev, led_cdev);
874	if (ret)
875		return dev_err_probe(dev, ret, "registering LED\n");
876
877	return software_node_register_node_group(ktd2026_node_group);
878}
879
880static void xiaomi_mipad2_exit(void)
881{
882	software_node_unregister_node_group(ktd2026_node_group);
883}
884
885/*
886 * If the EFI bootloader is not Xiaomi's own signed Android loader, then the
887 * Xiaomi Mi Pad 2 X86 tablet sets OSID in the DSDT to 1 (Windows), causing
888 * a bunch of devices to be hidden.
889 *
890 * This takes care of instantiating the hidden devices manually.
891 */
892static const struct x86_i2c_client_info xiaomi_mipad2_i2c_clients[] __initconst = {
893	{
894		/* BQ27520 fuel-gauge */
895		.board_info = {
896			.type = "bq27520",
897			.addr = 0x55,
898			.dev_name = "bq27520",
899			.swnode = &fg_bq25890_supply_node,
900		},
901		.adapter_path = "\\_SB_.PCI0.I2C1",
902	}, {
903		/* KTD2026 RGB notification LED controller */
904		.board_info = {
905			.type = "ktd2026",
906			.addr = 0x30,
907			.dev_name = "ktd2026",
908			.swnode = &ktd2026_node,
909		},
910		.adapter_path = "\\_SB_.PCI0.I2C3",
911	},
912};
913
914const struct x86_dev_info xiaomi_mipad2_info __initconst = {
915	.i2c_client_info = xiaomi_mipad2_i2c_clients,
916	.i2c_client_count = ARRAY_SIZE(xiaomi_mipad2_i2c_clients),
917	.init = xiaomi_mipad2_init,
918	.exit = xiaomi_mipad2_exit,
919};