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
chuwi_hi8_init(struct device * dev)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
czc_p10t_init(struct device * dev)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
xiaomi_mipad2_brightness_set(struct led_classdev * led_cdev,enum led_brightness val)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
xiaomi_mipad2_init(struct device * dev)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
xiaomi_mipad2_exit(void)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