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