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, Android 4.2 / 4.4 + Guadalinex Ubuntu tablet
603 * distributed to schools in the Spanish Andalucía region.
604 */
605 const char * const crystal_cove_pwrsrc_psy[] = { "crystal_cove_pwrsrc" };
606
607 static const struct property_entry vexia_edu_atla10_ulpmc_props[] = {
608 PROPERTY_ENTRY_STRING_ARRAY("supplied-from", crystal_cove_pwrsrc_psy),
609 { }
610 };
611
612 const struct software_node vexia_edu_atla10_ulpmc_node = {
613 .properties = vexia_edu_atla10_ulpmc_props,
614 };
615
616 static const char * const vexia_edu_atla10_accel_mount_matrix[] = {
617 "0", "-1", "0",
618 "1", "0", "0",
619 "0", "0", "1"
620 };
621
622 static 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
627 static const struct software_node vexia_edu_atla10_accel_node = {
628 .properties = vexia_edu_atla10_accel_props,
629 };
630
631 static 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
637 static const struct software_node vexia_edu_atla10_touchscreen_node = {
638 .properties = vexia_edu_atla10_touchscreen_props,
639 };
640
641 static const struct property_entry vexia_edu_atla10_pmic_props[] = {
642 PROPERTY_ENTRY_BOOL("linux,register-pwrsrc-power_supply"),
643 { }
644 };
645
646 static const struct software_node vexia_edu_atla10_pmic_node = {
647 .properties = vexia_edu_atla10_pmic_props,
648 };
649
650 static 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
718 static 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
726 static struct gpiod_lookup_table * const vexia_edu_atla10_gpios[] = {
727 &vexia_edu_atla10_ft5416_gpios,
728 NULL
729 };
730
vexia_edu_atla10_init(struct device * dev)731 static 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
755 const 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 */
770 static const struct software_node ktd2026_node = {
771 .name = "ktd2026",
772 };
773
774 static 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
782 static const struct software_node ktd2026_rgb_led_node = {
783 .name = "multi-led",
784 .properties = ktd2026_rgb_led_props,
785 .parent = &ktd2026_node,
786 };
787
788 static 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
794 static const struct software_node ktd2026_blue_led_node = {
795 .properties = ktd2026_blue_led_props,
796 .parent = &ktd2026_rgb_led_node,
797 };
798
799 static 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
805 static const struct software_node ktd2026_green_led_node = {
806 .properties = ktd2026_green_led_props,
807 .parent = &ktd2026_rgb_led_node,
808 };
809
810 static 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
816 static const struct software_node ktd2026_red_led_node = {
817 .properties = ktd2026_red_led_props,
818 .parent = &ktd2026_rgb_led_node,
819 };
820
821 static 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
838 static struct pwm_device *xiaomi_mipad2_led_pwm;
839
xiaomi_mipad2_brightness_set(struct led_classdev * led_cdev,enum led_brightness val)840 static 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
xiaomi_mipad2_init(struct device * dev)853 static 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
xiaomi_mipad2_exit(void)880 static 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 */
892 static 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
914 const 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 };
920