1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Platform driver for Lenovo Yoga Book YB1-X90F/L tablets (Android model) 4 * WMI driver for Lenovo Yoga Book YB1-X91F/L tablets (Windows model) 5 * 6 * The keyboard half of the YB1 models can function as both a capacitive 7 * touch keyboard or as a Wacom digitizer, but not at the same time. 8 * 9 * This driver takes care of switching between the 2 functions. 10 * 11 * Copyright 2023 Hans de Goede <hansg@kernel.org> 12 */ 13 14 #include <linux/acpi.h> 15 #include <linux/gpio/consumer.h> 16 #include <linux/gpio/machine.h> 17 #include <linux/i2c.h> 18 #include <linux/interrupt.h> 19 #include <linux/leds.h> 20 #include <linux/module.h> 21 #include <linux/platform_device.h> 22 #include <linux/pwm.h> 23 #include <linux/wmi.h> 24 #include <linux/workqueue.h> 25 26 #define YB_MBTN_EVENT_GUID "243FEC1D-1963-41C1-8100-06A9D82A94B4" 27 28 #define YB_KBD_BL_DEFAULT 128 29 #define YB_KBD_BL_MAX 255 30 #define YB_KBD_BL_PWM_PERIOD 13333 31 32 #define YB_PDEV_NAME "yogabook-touch-kbd-digitizer-switch" 33 34 /* flags */ 35 enum { 36 YB_KBD_IS_ON, 37 YB_DIGITIZER_IS_ON, 38 YB_DIGITIZER_MODE, 39 YB_TABLET_MODE, 40 YB_SUSPENDED, 41 }; 42 43 struct yogabook_data { 44 struct device *dev; 45 struct acpi_device *kbd_adev; 46 struct acpi_device *dig_adev; 47 struct device *kbd_dev; 48 struct device *dig_dev; 49 struct led_classdev *pen_led; 50 struct gpio_desc *pen_touch_event; 51 struct gpio_desc *kbd_bl_led_enable; 52 struct gpio_desc *backside_hall_gpio; 53 struct pwm_device *kbd_bl_pwm; 54 int (*set_kbd_backlight)(struct yogabook_data *data, uint8_t level); 55 int pen_touch_irq; 56 int backside_hall_irq; 57 struct work_struct work; 58 struct led_classdev kbd_bl_led; 59 unsigned long flags; 60 uint8_t brightness; 61 }; 62 63 static void yogabook_work(struct work_struct *work) 64 { 65 struct yogabook_data *data = container_of(work, struct yogabook_data, work); 66 bool kbd_on, digitizer_on; 67 int r; 68 69 if (test_bit(YB_SUSPENDED, &data->flags)) 70 return; 71 72 if (test_bit(YB_TABLET_MODE, &data->flags)) { 73 kbd_on = false; 74 digitizer_on = false; 75 } else if (test_bit(YB_DIGITIZER_MODE, &data->flags)) { 76 digitizer_on = true; 77 kbd_on = false; 78 } else { 79 kbd_on = true; 80 digitizer_on = false; 81 } 82 83 if (!kbd_on && test_bit(YB_KBD_IS_ON, &data->flags)) { 84 /* 85 * Must be done before releasing the keyboard touchscreen driver, 86 * so that the keyboard touchscreen dev is still in D0. 87 */ 88 data->set_kbd_backlight(data, 0); 89 device_release_driver(data->kbd_dev); 90 clear_bit(YB_KBD_IS_ON, &data->flags); 91 } 92 93 if (!digitizer_on && test_bit(YB_DIGITIZER_IS_ON, &data->flags)) { 94 led_set_brightness(data->pen_led, LED_OFF); 95 device_release_driver(data->dig_dev); 96 clear_bit(YB_DIGITIZER_IS_ON, &data->flags); 97 } 98 99 if (kbd_on && !test_bit(YB_KBD_IS_ON, &data->flags)) { 100 r = device_reprobe(data->kbd_dev); 101 if (r) 102 dev_warn(data->dev, "Reprobe of keyboard touchscreen failed: %d\n", r); 103 104 data->set_kbd_backlight(data, data->brightness); 105 set_bit(YB_KBD_IS_ON, &data->flags); 106 } 107 108 if (digitizer_on && !test_bit(YB_DIGITIZER_IS_ON, &data->flags)) { 109 r = device_reprobe(data->dig_dev); 110 if (r) 111 dev_warn(data->dev, "Reprobe of digitizer failed: %d\n", r); 112 113 led_set_brightness(data->pen_led, LED_FULL); 114 set_bit(YB_DIGITIZER_IS_ON, &data->flags); 115 } 116 } 117 118 static void yogabook_toggle_digitizer_mode(struct yogabook_data *data) 119 { 120 if (test_bit(YB_SUSPENDED, &data->flags)) 121 return; 122 123 if (test_bit(YB_DIGITIZER_MODE, &data->flags)) 124 clear_bit(YB_DIGITIZER_MODE, &data->flags); 125 else 126 set_bit(YB_DIGITIZER_MODE, &data->flags); 127 128 /* 129 * We are called from the ACPI core and the driver [un]binding which is 130 * done also needs ACPI functions, use a workqueue to avoid deadlocking. 131 */ 132 schedule_work(&data->work); 133 } 134 135 static irqreturn_t yogabook_backside_hall_irq(int irq, void *_data) 136 { 137 struct yogabook_data *data = _data; 138 139 if (gpiod_get_value(data->backside_hall_gpio)) 140 set_bit(YB_TABLET_MODE, &data->flags); 141 else 142 clear_bit(YB_TABLET_MODE, &data->flags); 143 144 schedule_work(&data->work); 145 146 return IRQ_HANDLED; 147 } 148 149 #define kbd_led_to_yogabook(cdev) container_of(cdev, struct yogabook_data, kbd_bl_led) 150 151 static enum led_brightness kbd_brightness_get(struct led_classdev *cdev) 152 { 153 struct yogabook_data *data = kbd_led_to_yogabook(cdev); 154 155 return data->brightness; 156 } 157 158 static int kbd_brightness_set(struct led_classdev *cdev, 159 enum led_brightness value) 160 { 161 struct yogabook_data *data = kbd_led_to_yogabook(cdev); 162 163 if ((value < 0) || (value > YB_KBD_BL_MAX)) 164 return -EINVAL; 165 166 data->brightness = value; 167 168 if (!test_bit(YB_KBD_IS_ON, &data->flags)) 169 return 0; 170 171 return data->set_kbd_backlight(data, data->brightness); 172 } 173 174 static struct gpiod_lookup_table yogabook_gpios = { 175 .table = { 176 GPIO_LOOKUP("INT33FF:02", 18, "backside_hall_sw", GPIO_ACTIVE_LOW), 177 {} 178 }, 179 }; 180 181 static struct led_lookup_data yogabook_pen_led = { 182 .provider = "platform::indicator", 183 .con_id = "pen-icon-led", 184 }; 185 186 static int yogabook_probe(struct device *dev, struct yogabook_data *data, 187 const char *kbd_bl_led_name) 188 { 189 int r; 190 191 data->dev = dev; 192 data->brightness = YB_KBD_BL_DEFAULT; 193 set_bit(YB_KBD_IS_ON, &data->flags); 194 set_bit(YB_DIGITIZER_IS_ON, &data->flags); 195 INIT_WORK(&data->work, yogabook_work); 196 197 yogabook_pen_led.dev_id = dev_name(dev); 198 led_add_lookup(&yogabook_pen_led); 199 data->pen_led = devm_led_get(dev, "pen-icon-led"); 200 led_remove_lookup(&yogabook_pen_led); 201 202 if (IS_ERR(data->pen_led)) 203 return dev_err_probe(dev, PTR_ERR(data->pen_led), "Getting pen icon LED\n"); 204 205 yogabook_gpios.dev_id = dev_name(dev); 206 gpiod_add_lookup_table(&yogabook_gpios); 207 data->backside_hall_gpio = devm_gpiod_get(dev, "backside_hall_sw", GPIOD_IN); 208 gpiod_remove_lookup_table(&yogabook_gpios); 209 210 if (IS_ERR(data->backside_hall_gpio)) 211 return dev_err_probe(dev, PTR_ERR(data->backside_hall_gpio), 212 "Getting backside_hall_sw GPIO\n"); 213 214 r = gpiod_to_irq(data->backside_hall_gpio); 215 if (r < 0) 216 return dev_err_probe(dev, r, "Getting backside_hall_sw IRQ\n"); 217 218 data->backside_hall_irq = r; 219 220 /* Set default brightness before enabling the IRQ */ 221 data->set_kbd_backlight(data, YB_KBD_BL_DEFAULT); 222 223 r = request_irq(data->backside_hall_irq, yogabook_backside_hall_irq, 224 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 225 "backside_hall_sw", data); 226 if (r) 227 return dev_err_probe(dev, r, "Requesting backside_hall_sw IRQ\n"); 228 229 schedule_work(&data->work); 230 231 data->kbd_bl_led.name = kbd_bl_led_name; 232 data->kbd_bl_led.brightness_set_blocking = kbd_brightness_set; 233 data->kbd_bl_led.brightness_get = kbd_brightness_get; 234 data->kbd_bl_led.max_brightness = YB_KBD_BL_MAX; 235 236 r = devm_led_classdev_register(dev, &data->kbd_bl_led); 237 if (r < 0) { 238 dev_err_probe(dev, r, "Registering backlight LED device\n"); 239 goto error_free_irq; 240 } 241 242 dev_set_drvdata(dev, data); 243 return 0; 244 245 error_free_irq: 246 free_irq(data->backside_hall_irq, data); 247 cancel_work_sync(&data->work); 248 return r; 249 } 250 251 static void yogabook_remove(struct yogabook_data *data) 252 { 253 int r = 0; 254 255 free_irq(data->backside_hall_irq, data); 256 cancel_work_sync(&data->work); 257 258 if (!test_bit(YB_KBD_IS_ON, &data->flags)) 259 r |= device_reprobe(data->kbd_dev); 260 261 if (!test_bit(YB_DIGITIZER_IS_ON, &data->flags)) 262 r |= device_reprobe(data->dig_dev); 263 264 if (r) 265 dev_warn(data->dev, "Reprobe of devices failed\n"); 266 } 267 268 static int yogabook_suspend(struct device *dev) 269 { 270 struct yogabook_data *data = dev_get_drvdata(dev); 271 272 set_bit(YB_SUSPENDED, &data->flags); 273 flush_work(&data->work); 274 275 if (test_bit(YB_KBD_IS_ON, &data->flags)) 276 data->set_kbd_backlight(data, 0); 277 278 return 0; 279 } 280 281 static int yogabook_resume(struct device *dev) 282 { 283 struct yogabook_data *data = dev_get_drvdata(dev); 284 285 if (test_bit(YB_KBD_IS_ON, &data->flags)) 286 data->set_kbd_backlight(data, data->brightness); 287 288 clear_bit(YB_SUSPENDED, &data->flags); 289 290 /* Check for YB_TABLET_MODE changes made during suspend */ 291 schedule_work(&data->work); 292 293 return 0; 294 } 295 296 static DEFINE_SIMPLE_DEV_PM_OPS(yogabook_pm_ops, yogabook_suspend, yogabook_resume); 297 298 /********** WMI driver code **********/ 299 300 /* 301 * To control keyboard backlight, call the method KBLC() of the TCS1 ACPI 302 * device (Goodix touchpad acts as virtual sensor keyboard). 303 */ 304 static int yogabook_wmi_set_kbd_backlight(struct yogabook_data *data, 305 uint8_t level) 306 { 307 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 308 struct acpi_object_list input; 309 union acpi_object param; 310 acpi_status status; 311 312 dev_dbg(data->dev, "Set KBLC level to %u\n", level); 313 314 /* Ensure keyboard touchpad is on before we call KBLC() */ 315 acpi_device_set_power(data->kbd_adev, ACPI_STATE_D0); 316 317 input.count = 1; 318 input.pointer = ¶m; 319 320 param.type = ACPI_TYPE_INTEGER; 321 param.integer.value = YB_KBD_BL_MAX - level; 322 323 status = acpi_evaluate_object(acpi_device_handle(data->kbd_adev), "KBLC", 324 &input, &output); 325 if (ACPI_FAILURE(status)) { 326 dev_err(data->dev, "Failed to call KBLC method: 0x%x\n", status); 327 return status; 328 } 329 330 kfree(output.pointer); 331 return 0; 332 } 333 334 static int yogabook_wmi_probe(struct wmi_device *wdev, const void *context) 335 { 336 struct device *dev = &wdev->dev; 337 struct yogabook_data *data; 338 int r; 339 340 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 341 if (data == NULL) 342 return -ENOMEM; 343 344 data->kbd_adev = acpi_dev_get_first_match_dev("GDIX1001", NULL, -1); 345 if (!data->kbd_adev) 346 return dev_err_probe(dev, -ENODEV, "Cannot find the touchpad device in ACPI tables\n"); 347 348 data->dig_adev = acpi_dev_get_first_match_dev("WCOM0019", NULL, -1); 349 if (!data->dig_adev) { 350 r = dev_err_probe(dev, -ENODEV, "Cannot find the digitizer device in ACPI tables\n"); 351 goto error_put_devs; 352 } 353 354 data->kbd_dev = get_device(acpi_get_first_physical_node(data->kbd_adev)); 355 if (!data->kbd_dev || !data->kbd_dev->driver) { 356 r = -EPROBE_DEFER; 357 goto error_put_devs; 358 } 359 360 data->dig_dev = get_device(acpi_get_first_physical_node(data->dig_adev)); 361 if (!data->dig_dev || !data->dig_dev->driver) { 362 r = -EPROBE_DEFER; 363 goto error_put_devs; 364 } 365 366 data->set_kbd_backlight = yogabook_wmi_set_kbd_backlight; 367 368 r = yogabook_probe(dev, data, "ybwmi::kbd_backlight"); 369 if (r) 370 goto error_put_devs; 371 372 return 0; 373 374 error_put_devs: 375 put_device(data->dig_dev); 376 put_device(data->kbd_dev); 377 acpi_dev_put(data->dig_adev); 378 acpi_dev_put(data->kbd_adev); 379 return r; 380 } 381 382 static void yogabook_wmi_remove(struct wmi_device *wdev) 383 { 384 struct yogabook_data *data = dev_get_drvdata(&wdev->dev); 385 386 yogabook_remove(data); 387 388 put_device(data->dig_dev); 389 put_device(data->kbd_dev); 390 acpi_dev_put(data->dig_adev); 391 acpi_dev_put(data->kbd_adev); 392 } 393 394 static void yogabook_wmi_notify(struct wmi_device *wdev, union acpi_object *dummy) 395 { 396 yogabook_toggle_digitizer_mode(dev_get_drvdata(&wdev->dev)); 397 } 398 399 static const struct wmi_device_id yogabook_wmi_id_table[] = { 400 { 401 .guid_string = YB_MBTN_EVENT_GUID, 402 }, 403 { } /* Terminating entry */ 404 }; 405 MODULE_DEVICE_TABLE(wmi, yogabook_wmi_id_table); 406 407 static struct wmi_driver yogabook_wmi_driver = { 408 .driver = { 409 .name = "yogabook-wmi", 410 .pm = pm_sleep_ptr(&yogabook_pm_ops), 411 }, 412 .no_notify_data = true, 413 .id_table = yogabook_wmi_id_table, 414 .probe = yogabook_wmi_probe, 415 .remove = yogabook_wmi_remove, 416 .notify = yogabook_wmi_notify, 417 }; 418 419 /********** platform driver code **********/ 420 421 static struct gpiod_lookup_table yogabook_pdev_gpios = { 422 .dev_id = YB_PDEV_NAME, 423 .table = { 424 GPIO_LOOKUP("INT33FF:00", 95, "pen_touch_event", GPIO_ACTIVE_HIGH), 425 GPIO_LOOKUP("INT33FF:03", 52, "enable_keyboard_led", GPIO_ACTIVE_HIGH), 426 {} 427 }, 428 }; 429 430 static int yogabook_pdev_set_kbd_backlight(struct yogabook_data *data, u8 level) 431 { 432 struct pwm_state state = { 433 .period = YB_KBD_BL_PWM_PERIOD, 434 .duty_cycle = YB_KBD_BL_PWM_PERIOD * level / YB_KBD_BL_MAX, 435 .enabled = level, 436 }; 437 438 pwm_apply_state(data->kbd_bl_pwm, &state); 439 gpiod_set_value(data->kbd_bl_led_enable, level ? 1 : 0); 440 return 0; 441 } 442 443 static irqreturn_t yogabook_pen_touch_irq(int irq, void *data) 444 { 445 yogabook_toggle_digitizer_mode(data); 446 return IRQ_HANDLED; 447 } 448 449 static int yogabook_pdev_probe(struct platform_device *pdev) 450 { 451 struct device *dev = &pdev->dev; 452 struct yogabook_data *data; 453 int r; 454 455 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 456 if (data == NULL) 457 return -ENOMEM; 458 459 data->kbd_dev = bus_find_device_by_name(&i2c_bus_type, NULL, "i2c-goodix_ts"); 460 if (!data->kbd_dev || !data->kbd_dev->driver) { 461 r = -EPROBE_DEFER; 462 goto error_put_devs; 463 } 464 465 data->dig_dev = bus_find_device_by_name(&i2c_bus_type, NULL, "i2c-wacom"); 466 if (!data->dig_dev || !data->dig_dev->driver) { 467 r = -EPROBE_DEFER; 468 goto error_put_devs; 469 } 470 471 gpiod_add_lookup_table(&yogabook_pdev_gpios); 472 data->pen_touch_event = devm_gpiod_get(dev, "pen_touch_event", GPIOD_IN); 473 data->kbd_bl_led_enable = devm_gpiod_get(dev, "enable_keyboard_led", GPIOD_OUT_HIGH); 474 gpiod_remove_lookup_table(&yogabook_pdev_gpios); 475 476 if (IS_ERR(data->pen_touch_event)) { 477 r = dev_err_probe(dev, PTR_ERR(data->pen_touch_event), 478 "Getting pen_touch_event GPIO\n"); 479 goto error_put_devs; 480 } 481 482 if (IS_ERR(data->kbd_bl_led_enable)) { 483 r = dev_err_probe(dev, PTR_ERR(data->kbd_bl_led_enable), 484 "Getting enable_keyboard_led GPIO\n"); 485 goto error_put_devs; 486 } 487 488 data->kbd_bl_pwm = devm_pwm_get(dev, "pwm_soc_lpss_2"); 489 if (IS_ERR(data->kbd_bl_pwm)) { 490 r = dev_err_probe(dev, PTR_ERR(data->kbd_bl_pwm), 491 "Getting keyboard backlight PWM\n"); 492 goto error_put_devs; 493 } 494 495 r = gpiod_to_irq(data->pen_touch_event); 496 if (r < 0) { 497 dev_err_probe(dev, r, "Getting pen_touch_event IRQ\n"); 498 goto error_put_devs; 499 } 500 data->pen_touch_irq = r; 501 502 r = request_irq(data->pen_touch_irq, yogabook_pen_touch_irq, IRQF_TRIGGER_FALLING, 503 "pen_touch_event", data); 504 if (r) { 505 dev_err_probe(dev, r, "Requesting pen_touch_event IRQ\n"); 506 goto error_put_devs; 507 } 508 509 data->set_kbd_backlight = yogabook_pdev_set_kbd_backlight; 510 511 r = yogabook_probe(dev, data, "yogabook::kbd_backlight"); 512 if (r) 513 goto error_free_irq; 514 515 return 0; 516 517 error_free_irq: 518 free_irq(data->pen_touch_irq, data); 519 cancel_work_sync(&data->work); 520 error_put_devs: 521 put_device(data->dig_dev); 522 put_device(data->kbd_dev); 523 return r; 524 } 525 526 static void yogabook_pdev_remove(struct platform_device *pdev) 527 { 528 struct yogabook_data *data = platform_get_drvdata(pdev); 529 530 yogabook_remove(data); 531 free_irq(data->pen_touch_irq, data); 532 cancel_work_sync(&data->work); 533 put_device(data->dig_dev); 534 put_device(data->kbd_dev); 535 } 536 537 static struct platform_driver yogabook_pdev_driver = { 538 .probe = yogabook_pdev_probe, 539 .remove_new = yogabook_pdev_remove, 540 .driver = { 541 .name = YB_PDEV_NAME, 542 .pm = pm_sleep_ptr(&yogabook_pm_ops), 543 }, 544 }; 545 546 static int __init yogabook_module_init(void) 547 { 548 int r; 549 550 r = wmi_driver_register(&yogabook_wmi_driver); 551 if (r) 552 return r; 553 554 r = platform_driver_register(&yogabook_pdev_driver); 555 if (r) 556 wmi_driver_unregister(&yogabook_wmi_driver); 557 558 return r; 559 } 560 561 static void __exit yogabook_module_exit(void) 562 { 563 platform_driver_unregister(&yogabook_pdev_driver); 564 wmi_driver_unregister(&yogabook_wmi_driver); 565 } 566 567 module_init(yogabook_module_init); 568 module_exit(yogabook_module_exit); 569 570 MODULE_ALIAS("platform:" YB_PDEV_NAME); 571 MODULE_AUTHOR("Yauhen Kharuzhy"); 572 MODULE_DESCRIPTION("Lenovo Yoga Book driver"); 573 MODULE_LICENSE("GPL v2"); 574