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