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