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_goodix_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_goodix_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 struct x86_gpio_button advantech_mica_071_button = { 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 }; 113 114 /* 115 * When booted with the BIOS set to Android mode the Chuwi Hi8 (CWI509) DSDT 116 * contains a whole bunch of bogus ACPI I2C devices and is missing entries 117 * for the touchscreen and the accelerometer. 118 */ 119 static const struct property_entry chuwi_hi8_gsl1680_props[] = { 120 PROPERTY_ENTRY_U32("touchscreen-size-x", 1665), 121 PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), 122 PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), 123 PROPERTY_ENTRY_BOOL("silead,home-button"), 124 PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi8.fw"), 125 { } 126 }; 127 128 static const struct software_node chuwi_hi8_gsl1680_node = { 129 .properties = chuwi_hi8_gsl1680_props, 130 }; 131 132 static const char * const chuwi_hi8_mount_matrix[] = { 133 "1", "0", "0", 134 "0", "-1", "0", 135 "0", "0", "1" 136 }; 137 138 static const struct property_entry chuwi_hi8_bma250e_props[] = { 139 PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", chuwi_hi8_mount_matrix), 140 { } 141 }; 142 143 static const struct software_node chuwi_hi8_bma250e_node = { 144 .properties = chuwi_hi8_bma250e_props, 145 }; 146 147 static const struct x86_i2c_client_info chuwi_hi8_i2c_clients[] __initconst = { 148 { 149 /* Silead touchscreen */ 150 .board_info = { 151 .type = "gsl1680", 152 .addr = 0x40, 153 .swnode = &chuwi_hi8_gsl1680_node, 154 }, 155 .adapter_path = "\\_SB_.I2C4", 156 .irq_data = { 157 .type = X86_ACPI_IRQ_TYPE_APIC, 158 .index = 0x44, 159 .trigger = ACPI_EDGE_SENSITIVE, 160 .polarity = ACPI_ACTIVE_HIGH, 161 }, 162 }, { 163 /* BMA250E accelerometer */ 164 .board_info = { 165 .type = "bma250e", 166 .addr = 0x18, 167 .swnode = &chuwi_hi8_bma250e_node, 168 }, 169 .adapter_path = "\\_SB_.I2C3", 170 .irq_data = { 171 .type = X86_ACPI_IRQ_TYPE_GPIOINT, 172 .chip = "INT33FC:02", 173 .index = 23, 174 .trigger = ACPI_LEVEL_SENSITIVE, 175 .polarity = ACPI_ACTIVE_HIGH, 176 }, 177 }, 178 }; 179 180 static int __init chuwi_hi8_init(void) 181 { 182 /* 183 * Avoid the acpi_unregister_gsi() call in x86_acpi_irq_helper_get() 184 * breaking the touchscreen + logging various errors when the Windows 185 * BIOS is used. 186 */ 187 if (acpi_dev_present("MSSL0001", NULL, 1)) 188 return -ENODEV; 189 190 return 0; 191 } 192 193 const struct x86_dev_info chuwi_hi8_info __initconst = { 194 .i2c_client_info = chuwi_hi8_i2c_clients, 195 .i2c_client_count = ARRAY_SIZE(chuwi_hi8_i2c_clients), 196 .init = chuwi_hi8_init, 197 }; 198 199 #define CZC_EC_EXTRA_PORT 0x68 200 #define CZC_EC_ANDROID_KEYS 0x63 201 202 static int __init czc_p10t_init(void) 203 { 204 /* 205 * The device boots up in "Windows 7" mode, when the home button sends a 206 * Windows specific key sequence (Left Meta + D) and the second button 207 * sends an unknown one while also toggling the Radio Kill Switch. 208 * This is a surprising behavior when the second button is labeled "Back". 209 * 210 * The vendor-supplied Android-x86 build switches the device to a "Android" 211 * mode by writing value 0x63 to the I/O port 0x68. This just seems to just 212 * set bit 6 on address 0x96 in the EC region; switching the bit directly 213 * seems to achieve the same result. It uses a "p10t_switcher" to do the 214 * job. It doesn't seem to be able to do anything else, and no other use 215 * of the port 0x68 is known. 216 * 217 * In the Android mode, the home button sends just a single scancode, 218 * which can be handled in Linux userspace more reasonably and the back 219 * button only sends a scancode without toggling the kill switch. 220 * The scancode can then be mapped either to Back or RF Kill functionality 221 * in userspace, depending on how the button is labeled on that particular 222 * model. 223 */ 224 outb(CZC_EC_ANDROID_KEYS, CZC_EC_EXTRA_PORT); 225 return 0; 226 } 227 228 const struct x86_dev_info czc_p10t __initconst = { 229 .init = czc_p10t_init, 230 }; 231 232 /* Medion Lifetab S10346 tablets have an Android factory img with everything hardcoded */ 233 static const char * const medion_lifetab_s10346_accel_mount_matrix[] = { 234 "0", "1", "0", 235 "1", "0", "0", 236 "0", "0", "1" 237 }; 238 239 static const struct property_entry medion_lifetab_s10346_accel_props[] = { 240 PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", medion_lifetab_s10346_accel_mount_matrix), 241 { } 242 }; 243 244 static const struct software_node medion_lifetab_s10346_accel_node = { 245 .properties = medion_lifetab_s10346_accel_props, 246 }; 247 248 /* Note the LCD panel is mounted upside down, this is correctly indicated in the VBT */ 249 static const struct property_entry medion_lifetab_s10346_touchscreen_props[] = { 250 PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"), 251 PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), 252 { } 253 }; 254 255 static const struct software_node medion_lifetab_s10346_touchscreen_node = { 256 .properties = medion_lifetab_s10346_touchscreen_props, 257 }; 258 259 static const struct x86_i2c_client_info medion_lifetab_s10346_i2c_clients[] __initconst = { 260 { 261 /* kxtj21009 accel */ 262 .board_info = { 263 .type = "kxtj21009", 264 .addr = 0x0f, 265 .dev_name = "kxtj21009", 266 .swnode = &medion_lifetab_s10346_accel_node, 267 }, 268 .adapter_path = "\\_SB_.I2C3", 269 .irq_data = { 270 .type = X86_ACPI_IRQ_TYPE_GPIOINT, 271 .chip = "INT33FC:02", 272 .index = 23, 273 .trigger = ACPI_EDGE_SENSITIVE, 274 .polarity = ACPI_ACTIVE_HIGH, 275 }, 276 }, { 277 /* goodix touchscreen */ 278 .board_info = { 279 .type = "GDIX1001:00", 280 .addr = 0x14, 281 .dev_name = "goodix_ts", 282 .swnode = &medion_lifetab_s10346_touchscreen_node, 283 }, 284 .adapter_path = "\\_SB_.I2C4", 285 .irq_data = { 286 .type = X86_ACPI_IRQ_TYPE_APIC, 287 .index = 0x44, 288 .trigger = ACPI_EDGE_SENSITIVE, 289 .polarity = ACPI_ACTIVE_LOW, 290 }, 291 }, 292 }; 293 294 static struct gpiod_lookup_table medion_lifetab_s10346_goodix_gpios = { 295 .dev_id = "i2c-goodix_ts", 296 .table = { 297 GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH), 298 GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH), 299 { } 300 }, 301 }; 302 303 static struct gpiod_lookup_table * const medion_lifetab_s10346_gpios[] = { 304 &medion_lifetab_s10346_goodix_gpios, 305 NULL 306 }; 307 308 const struct x86_dev_info medion_lifetab_s10346_info __initconst = { 309 .i2c_client_info = medion_lifetab_s10346_i2c_clients, 310 .i2c_client_count = ARRAY_SIZE(medion_lifetab_s10346_i2c_clients), 311 .gpiod_lookup_tables = medion_lifetab_s10346_gpios, 312 }; 313 314 /* Nextbook Ares 8 tablets have an Android factory img with everything hardcoded */ 315 static const char * const nextbook_ares8_accel_mount_matrix[] = { 316 "0", "-1", "0", 317 "-1", "0", "0", 318 "0", "0", "1" 319 }; 320 321 static const struct property_entry nextbook_ares8_accel_props[] = { 322 PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", nextbook_ares8_accel_mount_matrix), 323 { } 324 }; 325 326 static const struct software_node nextbook_ares8_accel_node = { 327 .properties = nextbook_ares8_accel_props, 328 }; 329 330 static const struct property_entry nextbook_ares8_touchscreen_props[] = { 331 PROPERTY_ENTRY_U32("touchscreen-size-x", 800), 332 PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), 333 { } 334 }; 335 336 static const struct software_node nextbook_ares8_touchscreen_node = { 337 .properties = nextbook_ares8_touchscreen_props, 338 }; 339 340 static const struct x86_i2c_client_info nextbook_ares8_i2c_clients[] __initconst = { 341 { 342 /* Freescale MMA8653FC accel */ 343 .board_info = { 344 .type = "mma8653", 345 .addr = 0x1d, 346 .dev_name = "mma8653", 347 .swnode = &nextbook_ares8_accel_node, 348 }, 349 .adapter_path = "\\_SB_.I2C3", 350 }, { 351 /* FT5416DQ9 touchscreen controller */ 352 .board_info = { 353 .type = "edt-ft5x06", 354 .addr = 0x38, 355 .dev_name = "ft5416", 356 .swnode = &nextbook_ares8_touchscreen_node, 357 }, 358 .adapter_path = "\\_SB_.I2C4", 359 .irq_data = { 360 .type = X86_ACPI_IRQ_TYPE_GPIOINT, 361 .chip = "INT33FC:02", 362 .index = 3, 363 .trigger = ACPI_EDGE_SENSITIVE, 364 .polarity = ACPI_ACTIVE_LOW, 365 }, 366 }, 367 }; 368 369 static struct gpiod_lookup_table * const nextbook_ares8_gpios[] = { 370 &int3496_reference_gpios, 371 NULL 372 }; 373 374 const struct x86_dev_info nextbook_ares8_info __initconst = { 375 .i2c_client_info = nextbook_ares8_i2c_clients, 376 .i2c_client_count = ARRAY_SIZE(nextbook_ares8_i2c_clients), 377 .pdev_info = int3496_pdevs, 378 .pdev_count = 1, 379 .gpiod_lookup_tables = nextbook_ares8_gpios, 380 .invalid_aei_gpiochip = "INT33FC:02", 381 }; 382 383 /* 384 * Peaq C1010 385 * This is a standard Windows tablet, but it has a special Dolby button. 386 * This button has a WMI interface, but that is broken. Instead of trying to 387 * use the broken WMI interface, instantiate a gpio_keys device for this. 388 */ 389 static struct x86_gpio_button peaq_c1010_button = { 390 .button = { 391 .code = KEY_SOUND, 392 .active_low = true, 393 .desc = "dolby_key", 394 .type = EV_KEY, 395 .wakeup = false, 396 .debounce_interval = 50, 397 }, 398 .chip = "INT33FC:00", 399 .pin = 3, 400 }; 401 402 const struct x86_dev_info peaq_c1010_info __initconst = { 403 .gpio_button = &peaq_c1010_button, 404 /* 405 * Move the ACPI event handler used by the broken WMI interface out of 406 * the way. This is the only event handler on INT33FC:00. 407 */ 408 .invalid_aei_gpiochip = "INT33FC:00", 409 }; 410 411 /* 412 * Whitelabel (sold as various brands) TM800A550L tablets. 413 * These tablet's DSDT contains a whole bunch of bogus ACPI I2C devices 414 * (removed through acpi_quirk_skip_i2c_client_enumeration()) and 415 * the touchscreen fwnode has the wrong GPIOs. 416 */ 417 static const char * const whitelabel_tm800a550l_accel_mount_matrix[] = { 418 "-1", "0", "0", 419 "0", "1", "0", 420 "0", "0", "1" 421 }; 422 423 static const struct property_entry whitelabel_tm800a550l_accel_props[] = { 424 PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", whitelabel_tm800a550l_accel_mount_matrix), 425 { } 426 }; 427 428 static const struct software_node whitelabel_tm800a550l_accel_node = { 429 .properties = whitelabel_tm800a550l_accel_props, 430 }; 431 432 static const struct property_entry whitelabel_tm800a550l_goodix_props[] = { 433 PROPERTY_ENTRY_STRING("firmware-name", "gt912-tm800a550l.fw"), 434 PROPERTY_ENTRY_STRING("goodix,config-name", "gt912-tm800a550l.cfg"), 435 PROPERTY_ENTRY_U32("goodix,main-clk", 54), 436 { } 437 }; 438 439 static const struct software_node whitelabel_tm800a550l_goodix_node = { 440 .properties = whitelabel_tm800a550l_goodix_props, 441 }; 442 443 static const struct x86_i2c_client_info whitelabel_tm800a550l_i2c_clients[] __initconst = { 444 { 445 /* goodix touchscreen */ 446 .board_info = { 447 .type = "GDIX1001:00", 448 .addr = 0x14, 449 .dev_name = "goodix_ts", 450 .swnode = &whitelabel_tm800a550l_goodix_node, 451 }, 452 .adapter_path = "\\_SB_.I2C2", 453 .irq_data = { 454 .type = X86_ACPI_IRQ_TYPE_APIC, 455 .index = 0x44, 456 .trigger = ACPI_EDGE_SENSITIVE, 457 .polarity = ACPI_ACTIVE_HIGH, 458 }, 459 }, { 460 /* kxcj91008 accel */ 461 .board_info = { 462 .type = "kxcj91008", 463 .addr = 0x0f, 464 .dev_name = "kxcj91008", 465 .swnode = &whitelabel_tm800a550l_accel_node, 466 }, 467 .adapter_path = "\\_SB_.I2C3", 468 }, 469 }; 470 471 static struct gpiod_lookup_table whitelabel_tm800a550l_goodix_gpios = { 472 .dev_id = "i2c-goodix_ts", 473 .table = { 474 GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH), 475 GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH), 476 { } 477 }, 478 }; 479 480 static struct gpiod_lookup_table * const whitelabel_tm800a550l_gpios[] = { 481 &whitelabel_tm800a550l_goodix_gpios, 482 NULL 483 }; 484 485 const struct x86_dev_info whitelabel_tm800a550l_info __initconst = { 486 .i2c_client_info = whitelabel_tm800a550l_i2c_clients, 487 .i2c_client_count = ARRAY_SIZE(whitelabel_tm800a550l_i2c_clients), 488 .gpiod_lookup_tables = whitelabel_tm800a550l_gpios, 489 }; 490 491 /* 492 * If the EFI bootloader is not Xiaomi's own signed Android loader, then the 493 * Xiaomi Mi Pad 2 X86 tablet sets OSID in the DSDT to 1 (Windows), causing 494 * a bunch of devices to be hidden. 495 * 496 * This takes care of instantiating the hidden devices manually. 497 */ 498 static const struct x86_i2c_client_info xiaomi_mipad2_i2c_clients[] __initconst = { 499 { 500 /* BQ27520 fuel-gauge */ 501 .board_info = { 502 .type = "bq27520", 503 .addr = 0x55, 504 .dev_name = "bq27520", 505 .swnode = &fg_bq25890_supply_node, 506 }, 507 .adapter_path = "\\_SB_.PCI0.I2C1", 508 }, { 509 /* KTD2026 RGB notification LED controller */ 510 .board_info = { 511 .type = "ktd2026", 512 .addr = 0x30, 513 .dev_name = "ktd2026", 514 }, 515 .adapter_path = "\\_SB_.PCI0.I2C3", 516 }, 517 }; 518 519 const struct x86_dev_info xiaomi_mipad2_info __initconst = { 520 .i2c_client_info = xiaomi_mipad2_i2c_clients, 521 .i2c_client_count = ARRAY_SIZE(xiaomi_mipad2_i2c_clients), 522 }; 523