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 */
25 static const char * const acer_b1_750_mount_matrix[] = {
26 "-1", "0", "0",
27 "0", "1", "0",
28 "0", "0", "1"
29 };
30
31 static const struct property_entry acer_b1_750_bma250e_props[] = {
32 PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", acer_b1_750_mount_matrix),
33 { }
34 };
35
36 static const struct software_node acer_b1_750_bma250e_node = {
37 .properties = acer_b1_750_bma250e_props,
38 };
39
40 static 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
76 static 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
84 static struct gpiod_lookup_table * const acer_b1_750_gpios[] = {
85 &acer_b1_750_nvt_ts_gpios,
86 &int3496_reference_gpios,
87 NULL
88 };
89
90 const 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 */
104 static 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
117 const 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 */
127 static 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
136 static const struct software_node chuwi_hi8_gsl1680_node = {
137 .properties = chuwi_hi8_gsl1680_props,
138 };
139
140 static const char * const chuwi_hi8_mount_matrix[] = {
141 "1", "0", "0",
142 "0", "-1", "0",
143 "0", "0", "1"
144 };
145
146 static const struct property_entry chuwi_hi8_bma250e_props[] = {
147 PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", chuwi_hi8_mount_matrix),
148 { }
149 };
150
151 static const struct software_node chuwi_hi8_bma250e_node = {
152 .properties = chuwi_hi8_bma250e_props,
153 };
154
155 static 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
chuwi_hi8_init(struct device * dev)189 static 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
202 const 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 */
215 static 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
242 const 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
czc_p10t_init(struct device * dev)250 static 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
276 const 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 */
281 static const char * const medion_lifetab_s10346_accel_mount_matrix[] = {
282 "0", "1", "0",
283 "1", "0", "0",
284 "0", "0", "1"
285 };
286
287 static 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
292 static 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 */
297 static 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
303 static const struct software_node medion_lifetab_s10346_touchscreen_node = {
304 .properties = medion_lifetab_s10346_touchscreen_props,
305 };
306
307 static 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
343 static 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
352 static struct gpiod_lookup_table * const medion_lifetab_s10346_gpios[] = {
353 &medion_lifetab_s10346_goodix_gpios,
354 NULL
355 };
356
357 const 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 */
364 static const char * const nextbook_ares8_accel_mount_matrix[] = {
365 "0", "-1", "0",
366 "-1", "0", "0",
367 "0", "0", "1"
368 };
369
370 static const struct property_entry nextbook_ares8_accel_props[] = {
371 PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", nextbook_ares8_accel_mount_matrix),
372 { }
373 };
374
375 static const struct software_node nextbook_ares8_accel_node = {
376 .properties = nextbook_ares8_accel_props,
377 };
378
379 static 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
385 static const struct software_node nextbook_ares8_touchscreen_node = {
386 .properties = nextbook_ares8_touchscreen_props,
387 };
388
389 static 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
419 static struct gpiod_lookup_table * const nextbook_ares8_gpios[] = {
420 &int3496_reference_gpios,
421 NULL
422 };
423
424 const 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 */
433 static const char * const nextbook_ares8a_accel_mount_matrix[] = {
434 "1", "0", "0",
435 "0", "-1", "0",
436 "0", "0", "1"
437 };
438
439 static const struct property_entry nextbook_ares8a_accel_props[] = {
440 PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", nextbook_ares8a_accel_mount_matrix),
441 { }
442 };
443
444 static const struct software_node nextbook_ares8a_accel_node = {
445 .properties = nextbook_ares8a_accel_props,
446 };
447
448 static 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
478 static 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
486 static struct gpiod_lookup_table * const nextbook_ares8a_gpios[] = {
487 &nextbook_ares8a_ft5416_gpios,
488 NULL
489 };
490
491 const 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 */
503 static 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
516 const 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 */
527 static const char * const whitelabel_tm800a550l_accel_mount_matrix[] = {
528 "-1", "0", "0",
529 "0", "1", "0",
530 "0", "0", "1"
531 };
532
533 static const struct property_entry whitelabel_tm800a550l_accel_props[] = {
534 PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", whitelabel_tm800a550l_accel_mount_matrix),
535 { }
536 };
537
538 static const struct software_node whitelabel_tm800a550l_accel_node = {
539 .properties = whitelabel_tm800a550l_accel_props,
540 };
541
542 static 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
549 static const struct software_node whitelabel_tm800a550l_goodix_node = {
550 .properties = whitelabel_tm800a550l_goodix_props,
551 };
552
553 static 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
581 static 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
590 static struct gpiod_lookup_table * const whitelabel_tm800a550l_gpios[] = {
591 &whitelabel_tm800a550l_goodix_gpios,
592 NULL
593 };
594
595 const 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 5V, Android 4.4 + Guadalinex Ubuntu tablet
603 * distributed to schools in the Spanish Andalucía region.
604 */
605 static const struct property_entry vexia_edu_atla10_5v_touchscreen_props[] = {
606 PROPERTY_ENTRY_U32("hid-descr-addr", 0x0000),
607 PROPERTY_ENTRY_U32("post-reset-deassert-delay-ms", 120),
608 { }
609 };
610
611 static const struct software_node vexia_edu_atla10_5v_touchscreen_node = {
612 .properties = vexia_edu_atla10_5v_touchscreen_props,
613 };
614
615 static const struct x86_i2c_client_info vexia_edu_atla10_5v_i2c_clients[] __initconst = {
616 {
617 /* kxcjk1013 accelerometer */
618 .board_info = {
619 .type = "kxcjk1013",
620 .addr = 0x0f,
621 .dev_name = "kxcjk1013",
622 },
623 .adapter_path = "\\_SB_.I2C3",
624 }, {
625 /* touchscreen controller */
626 .board_info = {
627 .type = "hid-over-i2c",
628 .addr = 0x38,
629 .dev_name = "FTSC1000",
630 .swnode = &vexia_edu_atla10_5v_touchscreen_node,
631 },
632 .adapter_path = "\\_SB_.I2C4",
633 .irq_data = {
634 .type = X86_ACPI_IRQ_TYPE_APIC,
635 .index = 0x44,
636 .trigger = ACPI_LEVEL_SENSITIVE,
637 .polarity = ACPI_ACTIVE_HIGH,
638 },
639 }
640 };
641
642 static struct gpiod_lookup_table vexia_edu_atla10_5v_ft5416_gpios = {
643 .dev_id = "i2c-FTSC1000",
644 .table = {
645 GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_LOW),
646 { }
647 },
648 };
649
650 static struct gpiod_lookup_table * const vexia_edu_atla10_5v_gpios[] = {
651 &vexia_edu_atla10_5v_ft5416_gpios,
652 NULL
653 };
654
655 const struct x86_dev_info vexia_edu_atla10_5v_info __initconst = {
656 .i2c_client_info = vexia_edu_atla10_5v_i2c_clients,
657 .i2c_client_count = ARRAY_SIZE(vexia_edu_atla10_5v_i2c_clients),
658 .gpiod_lookup_tables = vexia_edu_atla10_5v_gpios,
659 };
660
661 /*
662 * Vexia EDU ATLA 10 tablet 9V, Android 4.2 + Guadalinex Ubuntu tablet
663 * distributed to schools in the Spanish Andalucía region.
664 */
665 static const char * const crystal_cove_pwrsrc_psy[] = { "crystal_cove_pwrsrc" };
666
667 static const struct property_entry vexia_edu_atla10_9v_ulpmc_props[] = {
668 PROPERTY_ENTRY_STRING_ARRAY("supplied-from", crystal_cove_pwrsrc_psy),
669 { }
670 };
671
672 static const struct software_node vexia_edu_atla10_9v_ulpmc_node = {
673 .properties = vexia_edu_atla10_9v_ulpmc_props,
674 };
675
676 static const char * const vexia_edu_atla10_9v_accel_mount_matrix[] = {
677 "0", "-1", "0",
678 "1", "0", "0",
679 "0", "0", "1"
680 };
681
682 static const struct property_entry vexia_edu_atla10_9v_accel_props[] = {
683 PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", vexia_edu_atla10_9v_accel_mount_matrix),
684 { }
685 };
686
687 static const struct software_node vexia_edu_atla10_9v_accel_node = {
688 .properties = vexia_edu_atla10_9v_accel_props,
689 };
690
691 static const struct property_entry vexia_edu_atla10_9v_touchscreen_props[] = {
692 PROPERTY_ENTRY_U32("hid-descr-addr", 0x0000),
693 PROPERTY_ENTRY_U32("post-reset-deassert-delay-ms", 120),
694 { }
695 };
696
697 static const struct software_node vexia_edu_atla10_9v_touchscreen_node = {
698 .properties = vexia_edu_atla10_9v_touchscreen_props,
699 };
700
701 static const struct property_entry vexia_edu_atla10_9v_pmic_props[] = {
702 PROPERTY_ENTRY_BOOL("linux,register-pwrsrc-power_supply"),
703 { }
704 };
705
706 static const struct software_node vexia_edu_atla10_9v_pmic_node = {
707 .properties = vexia_edu_atla10_9v_pmic_props,
708 };
709
710 static const struct x86_i2c_client_info vexia_edu_atla10_9v_i2c_clients[] __initconst = {
711 {
712 /* I2C attached embedded controller, used to access fuel-gauge */
713 .board_info = {
714 .type = "vexia_atla10_ec",
715 .addr = 0x76,
716 .dev_name = "ulpmc",
717 .swnode = &vexia_edu_atla10_9v_ulpmc_node,
718 },
719 .adapter_path = "0000:00:18.1",
720 }, {
721 /* RT5642 audio codec */
722 .board_info = {
723 .type = "rt5640",
724 .addr = 0x1c,
725 .dev_name = "rt5640",
726 },
727 .adapter_path = "0000:00:18.2",
728 .irq_data = {
729 .type = X86_ACPI_IRQ_TYPE_GPIOINT,
730 .chip = "INT33FC:02",
731 .index = 4,
732 .trigger = ACPI_EDGE_SENSITIVE,
733 .polarity = ACPI_ACTIVE_HIGH,
734 .con_id = "rt5640_irq",
735 },
736 }, {
737 /* kxtj21009 accelerometer */
738 .board_info = {
739 .type = "kxtj21009",
740 .addr = 0x0f,
741 .dev_name = "kxtj21009",
742 .swnode = &vexia_edu_atla10_9v_accel_node,
743 },
744 .adapter_path = "0000:00:18.5",
745 }, {
746 /* FT5416DQ9 touchscreen controller */
747 .board_info = {
748 .type = "hid-over-i2c",
749 .addr = 0x38,
750 .dev_name = "FTSC1000",
751 .swnode = &vexia_edu_atla10_9v_touchscreen_node,
752 },
753 .adapter_path = "0000:00:18.6",
754 .irq_data = {
755 .type = X86_ACPI_IRQ_TYPE_APIC,
756 .index = 0x45,
757 .trigger = ACPI_LEVEL_SENSITIVE,
758 .polarity = ACPI_ACTIVE_HIGH,
759 },
760 }, {
761 /* Crystal Cove PMIC */
762 .board_info = {
763 .type = "intel_soc_pmic_crc",
764 .addr = 0x6e,
765 .dev_name = "intel_soc_pmic_crc",
766 .swnode = &vexia_edu_atla10_9v_pmic_node,
767 },
768 .adapter_path = "0000:00:18.7",
769 .irq_data = {
770 .type = X86_ACPI_IRQ_TYPE_APIC,
771 .index = 0x43,
772 .trigger = ACPI_LEVEL_SENSITIVE,
773 .polarity = ACPI_ACTIVE_HIGH,
774 },
775 }
776 };
777
778 static const struct x86_serdev_info vexia_edu_atla10_9v_serdevs[] __initconst = {
779 {
780 .ctrl.pci.devfn = PCI_DEVFN(0x1e, 3),
781 .ctrl_devname = "serial0",
782 .serdev_hid = "OBDA8723",
783 },
784 };
785
786 static struct gpiod_lookup_table vexia_edu_atla10_9v_ft5416_gpios = {
787 .dev_id = "i2c-FTSC1000",
788 .table = {
789 GPIO_LOOKUP("INT33FC:00", 60, "reset", GPIO_ACTIVE_LOW),
790 { }
791 },
792 };
793
794 static struct gpiod_lookup_table * const vexia_edu_atla10_9v_gpios[] = {
795 &vexia_edu_atla10_9v_ft5416_gpios,
796 NULL
797 };
798
vexia_edu_atla10_9v_init(struct device * dev)799 static int __init vexia_edu_atla10_9v_init(struct device *dev)
800 {
801 struct pci_dev *pdev;
802 int ret;
803
804 /* Enable the Wifi module by setting the wifi_enable pin to 1 */
805 ret = x86_android_tablet_get_gpiod("INT33FC:02", 20, "wifi_enable",
806 false, GPIOD_OUT_HIGH, NULL);
807 if (ret)
808 return ret;
809
810 /* Reprobe the SDIO controller to enumerate the now enabled Wifi module */
811 pdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0x11, 0));
812 if (!pdev)
813 return -EPROBE_DEFER;
814
815 ret = device_reprobe(&pdev->dev);
816 if (ret)
817 pci_warn(pdev, "Reprobing error: %d\n", ret);
818
819 pci_dev_put(pdev);
820 return 0;
821 }
822
823 const struct x86_dev_info vexia_edu_atla10_9v_info __initconst = {
824 .i2c_client_info = vexia_edu_atla10_9v_i2c_clients,
825 .i2c_client_count = ARRAY_SIZE(vexia_edu_atla10_9v_i2c_clients),
826 .serdev_info = vexia_edu_atla10_9v_serdevs,
827 .serdev_count = ARRAY_SIZE(vexia_edu_atla10_9v_serdevs),
828 .gpiod_lookup_tables = vexia_edu_atla10_9v_gpios,
829 .init = vexia_edu_atla10_9v_init,
830 .use_pci = true,
831 };
832
833 /*
834 * The firmware node for ktd2026 on Xaomi pad2. It composed of a RGB LED node
835 * with three subnodes for each color (B/G/R). The RGB LED node is named
836 * "multi-led" to align with the name in the device tree.
837 */
838
839 /* Main firmware node for ktd2026 */
840 static const struct software_node ktd2026_node = {
841 .name = "ktd2026",
842 };
843
844 static const struct property_entry ktd2026_rgb_led_props[] = {
845 PROPERTY_ENTRY_U32("reg", 0),
846 PROPERTY_ENTRY_U32("color", LED_COLOR_ID_RGB),
847 PROPERTY_ENTRY_STRING("label", "mipad2:rgb:indicator"),
848 PROPERTY_ENTRY_STRING("linux,default-trigger", "bq27520-0-charging-orange-full-green"),
849 { }
850 };
851
852 static const struct software_node ktd2026_rgb_led_node = {
853 .name = "multi-led",
854 .properties = ktd2026_rgb_led_props,
855 .parent = &ktd2026_node,
856 };
857
858 static const struct property_entry ktd2026_blue_led_props[] = {
859 PROPERTY_ENTRY_U32("reg", 0),
860 PROPERTY_ENTRY_U32("color", LED_COLOR_ID_BLUE),
861 { }
862 };
863
864 static const struct software_node ktd2026_blue_led_node = {
865 .properties = ktd2026_blue_led_props,
866 .parent = &ktd2026_rgb_led_node,
867 };
868
869 static const struct property_entry ktd2026_green_led_props[] = {
870 PROPERTY_ENTRY_U32("reg", 1),
871 PROPERTY_ENTRY_U32("color", LED_COLOR_ID_GREEN),
872 { }
873 };
874
875 static const struct software_node ktd2026_green_led_node = {
876 .properties = ktd2026_green_led_props,
877 .parent = &ktd2026_rgb_led_node,
878 };
879
880 static const struct property_entry ktd2026_red_led_props[] = {
881 PROPERTY_ENTRY_U32("reg", 2),
882 PROPERTY_ENTRY_U32("color", LED_COLOR_ID_RED),
883 { }
884 };
885
886 static const struct software_node ktd2026_red_led_node = {
887 .properties = ktd2026_red_led_props,
888 .parent = &ktd2026_rgb_led_node,
889 };
890
891 static const struct software_node *ktd2026_node_group[] = {
892 &ktd2026_node,
893 &ktd2026_rgb_led_node,
894 &ktd2026_red_led_node,
895 &ktd2026_green_led_node,
896 &ktd2026_blue_led_node,
897 NULL
898 };
899
900 /*
901 * For the LEDs which backlight the Menu / Home / Back capacitive buttons on
902 * the bottom bezel. These are attached to a TPS61158 LED controller which
903 * is controlled by the "pwm_soc_lpss_2" PWM output.
904 */
905 #define XIAOMI_MIPAD2_LED_PERIOD_NS 19200
906 #define XIAOMI_MIPAD2_LED_MAX_DUTY_NS 6000 /* From Android kernel */
907
908 static struct pwm_device *xiaomi_mipad2_led_pwm;
909
xiaomi_mipad2_brightness_set(struct led_classdev * led_cdev,enum led_brightness val)910 static int xiaomi_mipad2_brightness_set(struct led_classdev *led_cdev,
911 enum led_brightness val)
912 {
913 struct pwm_state state = {
914 .period = XIAOMI_MIPAD2_LED_PERIOD_NS,
915 .duty_cycle = XIAOMI_MIPAD2_LED_MAX_DUTY_NS * val / LED_FULL,
916 /* Always set PWM enabled to avoid the pin floating */
917 .enabled = true,
918 };
919
920 return pwm_apply_might_sleep(xiaomi_mipad2_led_pwm, &state);
921 }
922
xiaomi_mipad2_init(struct device * dev)923 static int __init xiaomi_mipad2_init(struct device *dev)
924 {
925 struct led_classdev *led_cdev;
926 int ret;
927
928 xiaomi_mipad2_led_pwm = devm_pwm_get(dev, "pwm_soc_lpss_2");
929 if (IS_ERR(xiaomi_mipad2_led_pwm))
930 return dev_err_probe(dev, PTR_ERR(xiaomi_mipad2_led_pwm), "getting pwm\n");
931
932 led_cdev = devm_kzalloc(dev, sizeof(*led_cdev), GFP_KERNEL);
933 if (!led_cdev)
934 return -ENOMEM;
935
936 led_cdev->name = "mipad2:white:touch-buttons-backlight";
937 led_cdev->max_brightness = LED_FULL;
938 led_cdev->default_trigger = "input-events";
939 led_cdev->brightness_set_blocking = xiaomi_mipad2_brightness_set;
940 /* Turn LED off during suspend */
941 led_cdev->flags = LED_CORE_SUSPENDRESUME;
942
943 ret = devm_led_classdev_register(dev, led_cdev);
944 if (ret)
945 return dev_err_probe(dev, ret, "registering LED\n");
946
947 return software_node_register_node_group(ktd2026_node_group);
948 }
949
xiaomi_mipad2_exit(void)950 static void xiaomi_mipad2_exit(void)
951 {
952 software_node_unregister_node_group(ktd2026_node_group);
953 }
954
955 /*
956 * If the EFI bootloader is not Xiaomi's own signed Android loader, then the
957 * Xiaomi Mi Pad 2 X86 tablet sets OSID in the DSDT to 1 (Windows), causing
958 * a bunch of devices to be hidden.
959 *
960 * This takes care of instantiating the hidden devices manually.
961 */
962 static const struct x86_i2c_client_info xiaomi_mipad2_i2c_clients[] __initconst = {
963 {
964 /* BQ27520 fuel-gauge */
965 .board_info = {
966 .type = "bq27520",
967 .addr = 0x55,
968 .dev_name = "bq27520",
969 .swnode = &fg_bq25890_supply_node,
970 },
971 .adapter_path = "\\_SB_.PCI0.I2C1",
972 }, {
973 /* KTD2026 RGB notification LED controller */
974 .board_info = {
975 .type = "ktd2026",
976 .addr = 0x30,
977 .dev_name = "ktd2026",
978 .swnode = &ktd2026_node,
979 },
980 .adapter_path = "\\_SB_.PCI0.I2C3",
981 },
982 };
983
984 const struct x86_dev_info xiaomi_mipad2_info __initconst = {
985 .i2c_client_info = xiaomi_mipad2_i2c_clients,
986 .i2c_client_count = ARRAY_SIZE(xiaomi_mipad2_i2c_clients),
987 .init = xiaomi_mipad2_init,
988 .exit = xiaomi_mipad2_exit,
989 };
990