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