lenovo-yogabook-wmi.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. // SPDX-License-Identifier: GPL-2.0
  2. /* WMI driver for Lenovo Yoga Book YB1-X90* / -X91* tablets */
  3. #include <linux/acpi.h>
  4. #include <linux/gpio/consumer.h>
  5. #include <linux/gpio/machine.h>
  6. #include <linux/interrupt.h>
  7. #include <linux/module.h>
  8. #include <linux/leds.h>
  9. #include <linux/wmi.h>
  10. #include <linux/workqueue.h>
  11. #define YB_MBTN_EVENT_GUID "243FEC1D-1963-41C1-8100-06A9D82A94B4"
  12. #define YB_MBTN_METHOD_GUID "742B0CA1-0B20-404B-9CAA-AEFCABF30CE0"
  13. #define YB_PAD_ENABLE 1
  14. #define YB_PAD_DISABLE 2
  15. #define YB_LIGHTUP_BTN 3
  16. #define YB_KBD_BL_DEFAULT 128
  17. /* flags */
  18. enum {
  19. YB_KBD_IS_ON,
  20. YB_DIGITIZER_IS_ON,
  21. YB_DIGITIZER_MODE,
  22. YB_TABLET_MODE,
  23. YB_SUSPENDED,
  24. };
  25. struct yogabook_wmi {
  26. struct wmi_device *wdev;
  27. struct acpi_device *kbd_adev;
  28. struct acpi_device *dig_adev;
  29. struct device *kbd_dev;
  30. struct device *dig_dev;
  31. struct gpio_desc *backside_hall_gpio;
  32. int backside_hall_irq;
  33. struct work_struct work;
  34. struct led_classdev kbd_bl_led;
  35. unsigned long flags;
  36. uint8_t brightness;
  37. };
  38. static int yogabook_wmi_do_action(struct wmi_device *wdev, int action)
  39. {
  40. struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
  41. struct acpi_buffer input;
  42. acpi_status status;
  43. u32 dummy_arg = 0;
  44. dev_dbg(&wdev->dev, "Do action: %d\n", action);
  45. input.pointer = &dummy_arg;
  46. input.length = sizeof(dummy_arg);
  47. status = wmi_evaluate_method(YB_MBTN_METHOD_GUID, 0, action, &input,
  48. &output);
  49. if (ACPI_FAILURE(status)) {
  50. dev_err(&wdev->dev, "Calling WMI method failure: 0x%x\n",
  51. status);
  52. return status;
  53. }
  54. kfree(output.pointer);
  55. return 0;
  56. }
  57. /*
  58. * To control keyboard backlight, call the method KBLC() of the TCS1 ACPI
  59. * device (Goodix touchpad acts as virtual sensor keyboard).
  60. */
  61. static int yogabook_wmi_set_kbd_backlight(struct wmi_device *wdev,
  62. uint8_t level)
  63. {
  64. struct yogabook_wmi *data = dev_get_drvdata(&wdev->dev);
  65. struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
  66. struct acpi_object_list input;
  67. union acpi_object param;
  68. acpi_status status;
  69. if (data->kbd_adev->power.state != ACPI_STATE_D0) {
  70. dev_warn(&wdev->dev, "keyboard touchscreen not in D0, cannot set brightness\n");
  71. return -ENXIO;
  72. }
  73. dev_dbg(&wdev->dev, "Set KBLC level to %u\n", level);
  74. input.count = 1;
  75. input.pointer = &param;
  76. param.type = ACPI_TYPE_INTEGER;
  77. param.integer.value = 255 - level;
  78. status = acpi_evaluate_object(acpi_device_handle(data->kbd_adev), "KBLC",
  79. &input, &output);
  80. if (ACPI_FAILURE(status)) {
  81. dev_err(&wdev->dev, "Failed to call KBLC method: 0x%x\n", status);
  82. return status;
  83. }
  84. kfree(output.pointer);
  85. return 0;
  86. }
  87. static void yogabook_wmi_work(struct work_struct *work)
  88. {
  89. struct yogabook_wmi *data = container_of(work, struct yogabook_wmi, work);
  90. struct device *dev = &data->wdev->dev;
  91. bool kbd_on, digitizer_on;
  92. int r;
  93. if (test_bit(YB_SUSPENDED, &data->flags))
  94. return;
  95. if (test_bit(YB_TABLET_MODE, &data->flags)) {
  96. kbd_on = false;
  97. digitizer_on = false;
  98. } else if (test_bit(YB_DIGITIZER_MODE, &data->flags)) {
  99. digitizer_on = true;
  100. kbd_on = false;
  101. } else {
  102. kbd_on = true;
  103. digitizer_on = false;
  104. }
  105. if (!kbd_on && test_bit(YB_KBD_IS_ON, &data->flags)) {
  106. /*
  107. * Must be done before releasing the keyboard touchscreen driver,
  108. * so that the keyboard touchscreen dev is still in D0.
  109. */
  110. yogabook_wmi_set_kbd_backlight(data->wdev, 0);
  111. device_release_driver(data->kbd_dev);
  112. clear_bit(YB_KBD_IS_ON, &data->flags);
  113. }
  114. if (!digitizer_on && test_bit(YB_DIGITIZER_IS_ON, &data->flags)) {
  115. yogabook_wmi_do_action(data->wdev, YB_PAD_DISABLE);
  116. device_release_driver(data->dig_dev);
  117. clear_bit(YB_DIGITIZER_IS_ON, &data->flags);
  118. }
  119. if (kbd_on && !test_bit(YB_KBD_IS_ON, &data->flags)) {
  120. r = device_reprobe(data->kbd_dev);
  121. if (r)
  122. dev_warn(dev, "Reprobe of keyboard touchscreen failed: %d\n", r);
  123. yogabook_wmi_set_kbd_backlight(data->wdev, data->brightness);
  124. set_bit(YB_KBD_IS_ON, &data->flags);
  125. }
  126. if (digitizer_on && !test_bit(YB_DIGITIZER_IS_ON, &data->flags)) {
  127. r = device_reprobe(data->dig_dev);
  128. if (r)
  129. dev_warn(dev, "Reprobe of digitizer failed: %d\n", r);
  130. yogabook_wmi_do_action(data->wdev, YB_PAD_ENABLE);
  131. set_bit(YB_DIGITIZER_IS_ON, &data->flags);
  132. }
  133. }
  134. static void yogabook_wmi_notify(struct wmi_device *wdev, union acpi_object *dummy)
  135. {
  136. struct yogabook_wmi *data = dev_get_drvdata(&wdev->dev);
  137. if (test_bit(YB_SUSPENDED, &data->flags))
  138. return;
  139. if (test_bit(YB_DIGITIZER_MODE, &data->flags))
  140. clear_bit(YB_DIGITIZER_MODE, &data->flags);
  141. else
  142. set_bit(YB_DIGITIZER_MODE, &data->flags);
  143. /*
  144. * We are called from the ACPI core and the driver [un]binding which is
  145. * done also needs ACPI functions, use a workqueue to avoid deadlocking.
  146. */
  147. schedule_work(&data->work);
  148. }
  149. static irqreturn_t yogabook_backside_hall_irq(int irq, void *_data)
  150. {
  151. struct yogabook_wmi *data = _data;
  152. if (gpiod_get_value(data->backside_hall_gpio))
  153. set_bit(YB_TABLET_MODE, &data->flags);
  154. else
  155. clear_bit(YB_TABLET_MODE, &data->flags);
  156. schedule_work(&data->work);
  157. return IRQ_HANDLED;
  158. }
  159. static enum led_brightness kbd_brightness_get(struct led_classdev *cdev)
  160. {
  161. struct yogabook_wmi *data =
  162. container_of(cdev, struct yogabook_wmi, kbd_bl_led);
  163. return data->brightness;
  164. }
  165. static int kbd_brightness_set(struct led_classdev *cdev,
  166. enum led_brightness value)
  167. {
  168. struct yogabook_wmi *data =
  169. container_of(cdev, struct yogabook_wmi, kbd_bl_led);
  170. struct wmi_device *wdev = data->wdev;
  171. if ((value < 0) || (value > 255))
  172. return -EINVAL;
  173. data->brightness = value;
  174. if (data->kbd_adev->power.state != ACPI_STATE_D0)
  175. return 0;
  176. return yogabook_wmi_set_kbd_backlight(wdev, data->brightness);
  177. }
  178. static struct gpiod_lookup_table yogabook_wmi_gpios = {
  179. .dev_id = "243FEC1D-1963-41C1-8100-06A9D82A94B4",
  180. .table = {
  181. GPIO_LOOKUP("INT33FF:02", 18, "backside_hall_sw", GPIO_ACTIVE_LOW),
  182. {}
  183. },
  184. };
  185. static void yogabook_wmi_rm_gpio_lookup(void *unused)
  186. {
  187. gpiod_remove_lookup_table(&yogabook_wmi_gpios);
  188. }
  189. static int yogabook_wmi_probe(struct wmi_device *wdev, const void *context)
  190. {
  191. struct yogabook_wmi *data;
  192. int r;
  193. data = devm_kzalloc(&wdev->dev, sizeof(struct yogabook_wmi), GFP_KERNEL);
  194. if (data == NULL)
  195. return -ENOMEM;
  196. dev_set_drvdata(&wdev->dev, data);
  197. data->wdev = wdev;
  198. data->brightness = YB_KBD_BL_DEFAULT;
  199. set_bit(YB_KBD_IS_ON, &data->flags);
  200. set_bit(YB_DIGITIZER_IS_ON, &data->flags);
  201. INIT_WORK(&data->work, yogabook_wmi_work);
  202. data->kbd_adev = acpi_dev_get_first_match_dev("GDIX1001", NULL, -1);
  203. if (!data->kbd_adev) {
  204. dev_err(&wdev->dev, "Cannot find the touchpad device in ACPI tables\n");
  205. return -ENODEV;
  206. }
  207. data->dig_adev = acpi_dev_get_first_match_dev("WCOM0019", NULL, -1);
  208. if (!data->dig_adev) {
  209. dev_err(&wdev->dev, "Cannot find the digitizer device in ACPI tables\n");
  210. r = -ENODEV;
  211. goto error_put_devs;
  212. }
  213. data->kbd_dev = get_device(acpi_get_first_physical_node(data->kbd_adev));
  214. if (!data->kbd_dev || !data->kbd_dev->driver) {
  215. r = -EPROBE_DEFER;
  216. goto error_put_devs;
  217. }
  218. data->dig_dev = get_device(acpi_get_first_physical_node(data->dig_adev));
  219. if (!data->dig_dev || !data->dig_dev->driver) {
  220. r = -EPROBE_DEFER;
  221. goto error_put_devs;
  222. }
  223. gpiod_add_lookup_table(&yogabook_wmi_gpios);
  224. r = devm_add_action_or_reset(&wdev->dev, yogabook_wmi_rm_gpio_lookup, NULL);
  225. if (r)
  226. goto error_put_devs;
  227. data->backside_hall_gpio =
  228. devm_gpiod_get(&wdev->dev, "backside_hall_sw", GPIOD_IN);
  229. if (IS_ERR(data->backside_hall_gpio)) {
  230. r = PTR_ERR(data->backside_hall_gpio);
  231. dev_err_probe(&wdev->dev, r, "Getting backside_hall_sw GPIO\n");
  232. goto error_put_devs;
  233. }
  234. r = gpiod_to_irq(data->backside_hall_gpio);
  235. if (r < 0) {
  236. dev_err_probe(&wdev->dev, r, "Getting backside_hall_sw IRQ\n");
  237. goto error_put_devs;
  238. }
  239. data->backside_hall_irq = r;
  240. /* Set default brightness before enabling the IRQ */
  241. yogabook_wmi_set_kbd_backlight(data->wdev, YB_KBD_BL_DEFAULT);
  242. r = request_irq(data->backside_hall_irq, yogabook_backside_hall_irq,
  243. IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
  244. "backside_hall_sw", data);
  245. if (r) {
  246. dev_err_probe(&wdev->dev, r, "Requesting backside_hall_sw IRQ\n");
  247. goto error_put_devs;
  248. }
  249. schedule_work(&data->work);
  250. data->kbd_bl_led.name = "ybwmi::kbd_backlight";
  251. data->kbd_bl_led.brightness_set_blocking = kbd_brightness_set;
  252. data->kbd_bl_led.brightness_get = kbd_brightness_get;
  253. data->kbd_bl_led.max_brightness = 255;
  254. r = devm_led_classdev_register(&wdev->dev, &data->kbd_bl_led);
  255. if (r < 0) {
  256. dev_err_probe(&wdev->dev, r, "Registering backlight LED device\n");
  257. goto error_free_irq;
  258. }
  259. return 0;
  260. error_free_irq:
  261. free_irq(data->backside_hall_irq, data);
  262. cancel_work_sync(&data->work);
  263. error_put_devs:
  264. put_device(data->dig_dev);
  265. put_device(data->kbd_dev);
  266. acpi_dev_put(data->dig_adev);
  267. acpi_dev_put(data->kbd_adev);
  268. return r;
  269. }
  270. static void yogabook_wmi_remove(struct wmi_device *wdev)
  271. {
  272. struct yogabook_wmi *data = dev_get_drvdata(&wdev->dev);
  273. int r = 0;
  274. free_irq(data->backside_hall_irq, data);
  275. cancel_work_sync(&data->work);
  276. if (!test_bit(YB_KBD_IS_ON, &data->flags))
  277. r |= device_reprobe(data->kbd_dev);
  278. if (!test_bit(YB_DIGITIZER_IS_ON, &data->flags))
  279. r |= device_reprobe(data->dig_dev);
  280. if (r)
  281. dev_warn(&wdev->dev, "Reprobe of devices failed\n");
  282. put_device(data->dig_dev);
  283. put_device(data->kbd_dev);
  284. acpi_dev_put(data->dig_adev);
  285. acpi_dev_put(data->kbd_adev);
  286. }
  287. static int __maybe_unused yogabook_wmi_suspend(struct device *dev)
  288. {
  289. struct wmi_device *wdev = container_of(dev, struct wmi_device, dev);
  290. struct yogabook_wmi *data = dev_get_drvdata(dev);
  291. set_bit(YB_SUSPENDED, &data->flags);
  292. flush_work(&data->work);
  293. /* Turn off the pen button at sleep */
  294. if (test_bit(YB_DIGITIZER_IS_ON, &data->flags))
  295. yogabook_wmi_do_action(wdev, YB_PAD_DISABLE);
  296. return 0;
  297. }
  298. static int __maybe_unused yogabook_wmi_resume(struct device *dev)
  299. {
  300. struct wmi_device *wdev = container_of(dev, struct wmi_device, dev);
  301. struct yogabook_wmi *data = dev_get_drvdata(dev);
  302. if (test_bit(YB_KBD_IS_ON, &data->flags)) {
  303. /* Ensure keyboard touchpad is on before we call KBLC() */
  304. acpi_device_set_power(data->kbd_adev, ACPI_STATE_D0);
  305. yogabook_wmi_set_kbd_backlight(wdev, data->brightness);
  306. }
  307. if (test_bit(YB_DIGITIZER_IS_ON, &data->flags))
  308. yogabook_wmi_do_action(wdev, YB_PAD_ENABLE);
  309. clear_bit(YB_SUSPENDED, &data->flags);
  310. /* Check for YB_TABLET_MODE changes made during suspend */
  311. schedule_work(&data->work);
  312. return 0;
  313. }
  314. static const struct wmi_device_id yogabook_wmi_id_table[] = {
  315. {
  316. .guid_string = YB_MBTN_EVENT_GUID,
  317. },
  318. { } /* Terminating entry */
  319. };
  320. static SIMPLE_DEV_PM_OPS(yogabook_wmi_pm_ops,
  321. yogabook_wmi_suspend, yogabook_wmi_resume);
  322. static struct wmi_driver yogabook_wmi_driver = {
  323. .driver = {
  324. .name = "yogabook-wmi",
  325. .pm = &yogabook_wmi_pm_ops,
  326. },
  327. .no_notify_data = true,
  328. .id_table = yogabook_wmi_id_table,
  329. .probe = yogabook_wmi_probe,
  330. .remove = yogabook_wmi_remove,
  331. .notify = yogabook_wmi_notify,
  332. };
  333. module_wmi_driver(yogabook_wmi_driver);
  334. MODULE_DEVICE_TABLE(wmi, yogabook_wmi_id_table);
  335. MODULE_AUTHOR("Yauhen Kharuzhy");
  336. MODULE_DESCRIPTION("Lenovo Yoga Book WMI driver");
  337. MODULE_LICENSE("GPL v2");