106ffe5b2SHans de Goede // SPDX-License-Identifier: GPL-2.0 206ffe5b2SHans de Goede /* 306ffe5b2SHans de Goede * Platform driver for Lenovo Yoga Book YB1-X90F/L tablets (Android model) 406ffe5b2SHans de Goede * WMI driver for Lenovo Yoga Book YB1-X91F/L tablets (Windows model) 506ffe5b2SHans de Goede * 606ffe5b2SHans de Goede * The keyboard half of the YB1 models can function as both a capacitive 706ffe5b2SHans de Goede * touch keyboard or as a Wacom digitizer, but not at the same time. 806ffe5b2SHans de Goede * 906ffe5b2SHans de Goede * This driver takes care of switching between the 2 functions. 1006ffe5b2SHans de Goede * 1106ffe5b2SHans de Goede * Copyright 2023 Hans de Goede <hansg@kernel.org> 1206ffe5b2SHans de Goede */ 1306ffe5b2SHans de Goede 1406ffe5b2SHans de Goede #include <linux/acpi.h> 1506ffe5b2SHans de Goede #include <linux/gpio/consumer.h> 1606ffe5b2SHans de Goede #include <linux/gpio/machine.h> 1706ffe5b2SHans de Goede #include <linux/i2c.h> 1806ffe5b2SHans de Goede #include <linux/interrupt.h> 1906ffe5b2SHans de Goede #include <linux/leds.h> 2006ffe5b2SHans de Goede #include <linux/module.h> 2106ffe5b2SHans de Goede #include <linux/platform_device.h> 2206ffe5b2SHans de Goede #include <linux/pwm.h> 2306ffe5b2SHans de Goede #include <linux/wmi.h> 2406ffe5b2SHans de Goede #include <linux/workqueue.h> 2506ffe5b2SHans de Goede 2606ffe5b2SHans de Goede #define YB_MBTN_EVENT_GUID "243FEC1D-1963-41C1-8100-06A9D82A94B4" 2706ffe5b2SHans de Goede 2806ffe5b2SHans de Goede #define YB_KBD_BL_DEFAULT 128 2906ffe5b2SHans de Goede #define YB_KBD_BL_MAX 255 3006ffe5b2SHans de Goede #define YB_KBD_BL_PWM_PERIOD 13333 3106ffe5b2SHans de Goede 3206ffe5b2SHans de Goede #define YB_PDEV_NAME "yogabook-touch-kbd-digitizer-switch" 3306ffe5b2SHans de Goede 3406ffe5b2SHans de Goede /* flags */ 3506ffe5b2SHans de Goede enum { 3606ffe5b2SHans de Goede YB_KBD_IS_ON, 3706ffe5b2SHans de Goede YB_DIGITIZER_IS_ON, 3806ffe5b2SHans de Goede YB_DIGITIZER_MODE, 3906ffe5b2SHans de Goede YB_TABLET_MODE, 4006ffe5b2SHans de Goede YB_SUSPENDED, 4106ffe5b2SHans de Goede }; 4206ffe5b2SHans de Goede 4306ffe5b2SHans de Goede struct yogabook_data { 4406ffe5b2SHans de Goede struct device *dev; 4506ffe5b2SHans de Goede struct acpi_device *kbd_adev; 4606ffe5b2SHans de Goede struct acpi_device *dig_adev; 4706ffe5b2SHans de Goede struct device *kbd_dev; 4806ffe5b2SHans de Goede struct device *dig_dev; 4906ffe5b2SHans de Goede struct led_classdev *pen_led; 5006ffe5b2SHans de Goede struct gpio_desc *pen_touch_event; 5106ffe5b2SHans de Goede struct gpio_desc *kbd_bl_led_enable; 5206ffe5b2SHans de Goede struct gpio_desc *backside_hall_gpio; 5306ffe5b2SHans de Goede struct pwm_device *kbd_bl_pwm; 5406ffe5b2SHans de Goede int (*set_kbd_backlight)(struct yogabook_data *data, uint8_t level); 5506ffe5b2SHans de Goede int pen_touch_irq; 5606ffe5b2SHans de Goede int backside_hall_irq; 5706ffe5b2SHans de Goede struct work_struct work; 5806ffe5b2SHans de Goede struct led_classdev kbd_bl_led; 5906ffe5b2SHans de Goede unsigned long flags; 6006ffe5b2SHans de Goede uint8_t brightness; 6106ffe5b2SHans de Goede }; 6206ffe5b2SHans de Goede 6306ffe5b2SHans de Goede static void yogabook_work(struct work_struct *work) 6406ffe5b2SHans de Goede { 6506ffe5b2SHans de Goede struct yogabook_data *data = container_of(work, struct yogabook_data, work); 6606ffe5b2SHans de Goede bool kbd_on, digitizer_on; 6706ffe5b2SHans de Goede int r; 6806ffe5b2SHans de Goede 6906ffe5b2SHans de Goede if (test_bit(YB_SUSPENDED, &data->flags)) 7006ffe5b2SHans de Goede return; 7106ffe5b2SHans de Goede 7206ffe5b2SHans de Goede if (test_bit(YB_TABLET_MODE, &data->flags)) { 7306ffe5b2SHans de Goede kbd_on = false; 7406ffe5b2SHans de Goede digitizer_on = false; 7506ffe5b2SHans de Goede } else if (test_bit(YB_DIGITIZER_MODE, &data->flags)) { 7606ffe5b2SHans de Goede digitizer_on = true; 7706ffe5b2SHans de Goede kbd_on = false; 7806ffe5b2SHans de Goede } else { 7906ffe5b2SHans de Goede kbd_on = true; 8006ffe5b2SHans de Goede digitizer_on = false; 8106ffe5b2SHans de Goede } 8206ffe5b2SHans de Goede 8306ffe5b2SHans de Goede if (!kbd_on && test_bit(YB_KBD_IS_ON, &data->flags)) { 8406ffe5b2SHans de Goede /* 8506ffe5b2SHans de Goede * Must be done before releasing the keyboard touchscreen driver, 8606ffe5b2SHans de Goede * so that the keyboard touchscreen dev is still in D0. 8706ffe5b2SHans de Goede */ 8806ffe5b2SHans de Goede data->set_kbd_backlight(data, 0); 8906ffe5b2SHans de Goede device_release_driver(data->kbd_dev); 9006ffe5b2SHans de Goede clear_bit(YB_KBD_IS_ON, &data->flags); 9106ffe5b2SHans de Goede } 9206ffe5b2SHans de Goede 9306ffe5b2SHans de Goede if (!digitizer_on && test_bit(YB_DIGITIZER_IS_ON, &data->flags)) { 9406ffe5b2SHans de Goede led_set_brightness(data->pen_led, LED_OFF); 9506ffe5b2SHans de Goede device_release_driver(data->dig_dev); 9606ffe5b2SHans de Goede clear_bit(YB_DIGITIZER_IS_ON, &data->flags); 9706ffe5b2SHans de Goede } 9806ffe5b2SHans de Goede 9906ffe5b2SHans de Goede if (kbd_on && !test_bit(YB_KBD_IS_ON, &data->flags)) { 10006ffe5b2SHans de Goede r = device_reprobe(data->kbd_dev); 10106ffe5b2SHans de Goede if (r) 10206ffe5b2SHans de Goede dev_warn(data->dev, "Reprobe of keyboard touchscreen failed: %d\n", r); 10306ffe5b2SHans de Goede 10406ffe5b2SHans de Goede data->set_kbd_backlight(data, data->brightness); 10506ffe5b2SHans de Goede set_bit(YB_KBD_IS_ON, &data->flags); 10606ffe5b2SHans de Goede } 10706ffe5b2SHans de Goede 10806ffe5b2SHans de Goede if (digitizer_on && !test_bit(YB_DIGITIZER_IS_ON, &data->flags)) { 10906ffe5b2SHans de Goede r = device_reprobe(data->dig_dev); 11006ffe5b2SHans de Goede if (r) 11106ffe5b2SHans de Goede dev_warn(data->dev, "Reprobe of digitizer failed: %d\n", r); 11206ffe5b2SHans de Goede 11306ffe5b2SHans de Goede led_set_brightness(data->pen_led, LED_FULL); 11406ffe5b2SHans de Goede set_bit(YB_DIGITIZER_IS_ON, &data->flags); 11506ffe5b2SHans de Goede } 11606ffe5b2SHans de Goede } 11706ffe5b2SHans de Goede 11806ffe5b2SHans de Goede static void yogabook_toggle_digitizer_mode(struct yogabook_data *data) 11906ffe5b2SHans de Goede { 12006ffe5b2SHans de Goede if (test_bit(YB_SUSPENDED, &data->flags)) 12106ffe5b2SHans de Goede return; 12206ffe5b2SHans de Goede 12306ffe5b2SHans de Goede if (test_bit(YB_DIGITIZER_MODE, &data->flags)) 12406ffe5b2SHans de Goede clear_bit(YB_DIGITIZER_MODE, &data->flags); 12506ffe5b2SHans de Goede else 12606ffe5b2SHans de Goede set_bit(YB_DIGITIZER_MODE, &data->flags); 12706ffe5b2SHans de Goede 12806ffe5b2SHans de Goede /* 12906ffe5b2SHans de Goede * We are called from the ACPI core and the driver [un]binding which is 13006ffe5b2SHans de Goede * done also needs ACPI functions, use a workqueue to avoid deadlocking. 13106ffe5b2SHans de Goede */ 13206ffe5b2SHans de Goede schedule_work(&data->work); 13306ffe5b2SHans de Goede } 13406ffe5b2SHans de Goede 13506ffe5b2SHans de Goede static irqreturn_t yogabook_backside_hall_irq(int irq, void *_data) 13606ffe5b2SHans de Goede { 13706ffe5b2SHans de Goede struct yogabook_data *data = _data; 13806ffe5b2SHans de Goede 13906ffe5b2SHans de Goede if (gpiod_get_value(data->backside_hall_gpio)) 14006ffe5b2SHans de Goede set_bit(YB_TABLET_MODE, &data->flags); 14106ffe5b2SHans de Goede else 14206ffe5b2SHans de Goede clear_bit(YB_TABLET_MODE, &data->flags); 14306ffe5b2SHans de Goede 14406ffe5b2SHans de Goede schedule_work(&data->work); 14506ffe5b2SHans de Goede 14606ffe5b2SHans de Goede return IRQ_HANDLED; 14706ffe5b2SHans de Goede } 14806ffe5b2SHans de Goede 14906ffe5b2SHans de Goede #define kbd_led_to_yogabook(cdev) container_of(cdev, struct yogabook_data, kbd_bl_led) 15006ffe5b2SHans de Goede 15106ffe5b2SHans de Goede static enum led_brightness kbd_brightness_get(struct led_classdev *cdev) 15206ffe5b2SHans de Goede { 15306ffe5b2SHans de Goede struct yogabook_data *data = kbd_led_to_yogabook(cdev); 15406ffe5b2SHans de Goede 15506ffe5b2SHans de Goede return data->brightness; 15606ffe5b2SHans de Goede } 15706ffe5b2SHans de Goede 15806ffe5b2SHans de Goede static int kbd_brightness_set(struct led_classdev *cdev, 15906ffe5b2SHans de Goede enum led_brightness value) 16006ffe5b2SHans de Goede { 16106ffe5b2SHans de Goede struct yogabook_data *data = kbd_led_to_yogabook(cdev); 16206ffe5b2SHans de Goede 16306ffe5b2SHans de Goede if ((value < 0) || (value > YB_KBD_BL_MAX)) 16406ffe5b2SHans de Goede return -EINVAL; 16506ffe5b2SHans de Goede 16606ffe5b2SHans de Goede data->brightness = value; 16706ffe5b2SHans de Goede 16806ffe5b2SHans de Goede if (!test_bit(YB_KBD_IS_ON, &data->flags)) 16906ffe5b2SHans de Goede return 0; 17006ffe5b2SHans de Goede 17106ffe5b2SHans de Goede return data->set_kbd_backlight(data, data->brightness); 17206ffe5b2SHans de Goede } 17306ffe5b2SHans de Goede 17406ffe5b2SHans de Goede static struct gpiod_lookup_table yogabook_gpios = { 17506ffe5b2SHans de Goede .table = { 17606ffe5b2SHans de Goede GPIO_LOOKUP("INT33FF:02", 18, "backside_hall_sw", GPIO_ACTIVE_LOW), 17706ffe5b2SHans de Goede {} 17806ffe5b2SHans de Goede }, 17906ffe5b2SHans de Goede }; 18006ffe5b2SHans de Goede 18106ffe5b2SHans de Goede static struct led_lookup_data yogabook_pen_led = { 18206ffe5b2SHans de Goede .provider = "platform::indicator", 18306ffe5b2SHans de Goede .con_id = "pen-icon-led", 18406ffe5b2SHans de Goede }; 18506ffe5b2SHans de Goede 18606ffe5b2SHans de Goede static int yogabook_probe(struct device *dev, struct yogabook_data *data, 18706ffe5b2SHans de Goede const char *kbd_bl_led_name) 18806ffe5b2SHans de Goede { 18906ffe5b2SHans de Goede int r; 19006ffe5b2SHans de Goede 19106ffe5b2SHans de Goede data->dev = dev; 19206ffe5b2SHans de Goede data->brightness = YB_KBD_BL_DEFAULT; 19306ffe5b2SHans de Goede set_bit(YB_KBD_IS_ON, &data->flags); 19406ffe5b2SHans de Goede set_bit(YB_DIGITIZER_IS_ON, &data->flags); 19506ffe5b2SHans de Goede INIT_WORK(&data->work, yogabook_work); 19606ffe5b2SHans de Goede 19706ffe5b2SHans de Goede yogabook_pen_led.dev_id = dev_name(dev); 19806ffe5b2SHans de Goede led_add_lookup(&yogabook_pen_led); 19906ffe5b2SHans de Goede data->pen_led = devm_led_get(dev, "pen-icon-led"); 20006ffe5b2SHans de Goede led_remove_lookup(&yogabook_pen_led); 20106ffe5b2SHans de Goede 20206ffe5b2SHans de Goede if (IS_ERR(data->pen_led)) 20306ffe5b2SHans de Goede return dev_err_probe(dev, PTR_ERR(data->pen_led), "Getting pen icon LED\n"); 20406ffe5b2SHans de Goede 20506ffe5b2SHans de Goede yogabook_gpios.dev_id = dev_name(dev); 20606ffe5b2SHans de Goede gpiod_add_lookup_table(&yogabook_gpios); 20706ffe5b2SHans de Goede data->backside_hall_gpio = devm_gpiod_get(dev, "backside_hall_sw", GPIOD_IN); 20806ffe5b2SHans de Goede gpiod_remove_lookup_table(&yogabook_gpios); 20906ffe5b2SHans de Goede 21006ffe5b2SHans de Goede if (IS_ERR(data->backside_hall_gpio)) 21106ffe5b2SHans de Goede return dev_err_probe(dev, PTR_ERR(data->backside_hall_gpio), 21206ffe5b2SHans de Goede "Getting backside_hall_sw GPIO\n"); 21306ffe5b2SHans de Goede 21406ffe5b2SHans de Goede r = gpiod_to_irq(data->backside_hall_gpio); 21506ffe5b2SHans de Goede if (r < 0) 21606ffe5b2SHans de Goede return dev_err_probe(dev, r, "Getting backside_hall_sw IRQ\n"); 21706ffe5b2SHans de Goede 21806ffe5b2SHans de Goede data->backside_hall_irq = r; 21906ffe5b2SHans de Goede 22006ffe5b2SHans de Goede /* Set default brightness before enabling the IRQ */ 22106ffe5b2SHans de Goede data->set_kbd_backlight(data, YB_KBD_BL_DEFAULT); 22206ffe5b2SHans de Goede 22306ffe5b2SHans de Goede r = request_irq(data->backside_hall_irq, yogabook_backside_hall_irq, 22406ffe5b2SHans de Goede IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 22506ffe5b2SHans de Goede "backside_hall_sw", data); 22606ffe5b2SHans de Goede if (r) 22706ffe5b2SHans de Goede return dev_err_probe(dev, r, "Requesting backside_hall_sw IRQ\n"); 22806ffe5b2SHans de Goede 22906ffe5b2SHans de Goede schedule_work(&data->work); 23006ffe5b2SHans de Goede 23106ffe5b2SHans de Goede data->kbd_bl_led.name = kbd_bl_led_name; 23206ffe5b2SHans de Goede data->kbd_bl_led.brightness_set_blocking = kbd_brightness_set; 23306ffe5b2SHans de Goede data->kbd_bl_led.brightness_get = kbd_brightness_get; 23406ffe5b2SHans de Goede data->kbd_bl_led.max_brightness = YB_KBD_BL_MAX; 23506ffe5b2SHans de Goede 23606ffe5b2SHans de Goede r = devm_led_classdev_register(dev, &data->kbd_bl_led); 23706ffe5b2SHans de Goede if (r < 0) { 23806ffe5b2SHans de Goede dev_err_probe(dev, r, "Registering backlight LED device\n"); 23906ffe5b2SHans de Goede goto error_free_irq; 24006ffe5b2SHans de Goede } 24106ffe5b2SHans de Goede 24206ffe5b2SHans de Goede dev_set_drvdata(dev, data); 24306ffe5b2SHans de Goede return 0; 24406ffe5b2SHans de Goede 24506ffe5b2SHans de Goede error_free_irq: 24606ffe5b2SHans de Goede free_irq(data->backside_hall_irq, data); 24706ffe5b2SHans de Goede cancel_work_sync(&data->work); 24806ffe5b2SHans de Goede return r; 24906ffe5b2SHans de Goede } 25006ffe5b2SHans de Goede 25106ffe5b2SHans de Goede static void yogabook_remove(struct yogabook_data *data) 25206ffe5b2SHans de Goede { 25306ffe5b2SHans de Goede int r = 0; 25406ffe5b2SHans de Goede 25506ffe5b2SHans de Goede free_irq(data->backside_hall_irq, data); 25606ffe5b2SHans de Goede cancel_work_sync(&data->work); 25706ffe5b2SHans de Goede 25806ffe5b2SHans de Goede if (!test_bit(YB_KBD_IS_ON, &data->flags)) 25906ffe5b2SHans de Goede r |= device_reprobe(data->kbd_dev); 26006ffe5b2SHans de Goede 26106ffe5b2SHans de Goede if (!test_bit(YB_DIGITIZER_IS_ON, &data->flags)) 26206ffe5b2SHans de Goede r |= device_reprobe(data->dig_dev); 26306ffe5b2SHans de Goede 26406ffe5b2SHans de Goede if (r) 26506ffe5b2SHans de Goede dev_warn(data->dev, "Reprobe of devices failed\n"); 26606ffe5b2SHans de Goede } 26706ffe5b2SHans de Goede 26806ffe5b2SHans de Goede static int yogabook_suspend(struct device *dev) 26906ffe5b2SHans de Goede { 27006ffe5b2SHans de Goede struct yogabook_data *data = dev_get_drvdata(dev); 27106ffe5b2SHans de Goede 27206ffe5b2SHans de Goede set_bit(YB_SUSPENDED, &data->flags); 27306ffe5b2SHans de Goede flush_work(&data->work); 27406ffe5b2SHans de Goede 27506ffe5b2SHans de Goede if (test_bit(YB_KBD_IS_ON, &data->flags)) 27606ffe5b2SHans de Goede data->set_kbd_backlight(data, 0); 27706ffe5b2SHans de Goede 27806ffe5b2SHans de Goede return 0; 27906ffe5b2SHans de Goede } 28006ffe5b2SHans de Goede 28106ffe5b2SHans de Goede static int yogabook_resume(struct device *dev) 28206ffe5b2SHans de Goede { 28306ffe5b2SHans de Goede struct yogabook_data *data = dev_get_drvdata(dev); 28406ffe5b2SHans de Goede 28506ffe5b2SHans de Goede if (test_bit(YB_KBD_IS_ON, &data->flags)) 28606ffe5b2SHans de Goede data->set_kbd_backlight(data, data->brightness); 28706ffe5b2SHans de Goede 28806ffe5b2SHans de Goede clear_bit(YB_SUSPENDED, &data->flags); 28906ffe5b2SHans de Goede 29006ffe5b2SHans de Goede /* Check for YB_TABLET_MODE changes made during suspend */ 29106ffe5b2SHans de Goede schedule_work(&data->work); 29206ffe5b2SHans de Goede 29306ffe5b2SHans de Goede return 0; 29406ffe5b2SHans de Goede } 29506ffe5b2SHans de Goede 29606ffe5b2SHans de Goede static DEFINE_SIMPLE_DEV_PM_OPS(yogabook_pm_ops, yogabook_suspend, yogabook_resume); 29706ffe5b2SHans de Goede 29806ffe5b2SHans de Goede /********** WMI driver code **********/ 29906ffe5b2SHans de Goede 30006ffe5b2SHans de Goede /* 30106ffe5b2SHans de Goede * To control keyboard backlight, call the method KBLC() of the TCS1 ACPI 30206ffe5b2SHans de Goede * device (Goodix touchpad acts as virtual sensor keyboard). 30306ffe5b2SHans de Goede */ 30406ffe5b2SHans de Goede static int yogabook_wmi_set_kbd_backlight(struct yogabook_data *data, 30506ffe5b2SHans de Goede uint8_t level) 30606ffe5b2SHans de Goede { 30706ffe5b2SHans de Goede struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 30806ffe5b2SHans de Goede struct acpi_object_list input; 30906ffe5b2SHans de Goede union acpi_object param; 31006ffe5b2SHans de Goede acpi_status status; 31106ffe5b2SHans de Goede 31206ffe5b2SHans de Goede dev_dbg(data->dev, "Set KBLC level to %u\n", level); 31306ffe5b2SHans de Goede 31406ffe5b2SHans de Goede /* Ensure keyboard touchpad is on before we call KBLC() */ 31506ffe5b2SHans de Goede acpi_device_set_power(data->kbd_adev, ACPI_STATE_D0); 31606ffe5b2SHans de Goede 31706ffe5b2SHans de Goede input.count = 1; 31806ffe5b2SHans de Goede input.pointer = ¶m; 31906ffe5b2SHans de Goede 32006ffe5b2SHans de Goede param.type = ACPI_TYPE_INTEGER; 32106ffe5b2SHans de Goede param.integer.value = YB_KBD_BL_MAX - level; 32206ffe5b2SHans de Goede 32306ffe5b2SHans de Goede status = acpi_evaluate_object(acpi_device_handle(data->kbd_adev), "KBLC", 32406ffe5b2SHans de Goede &input, &output); 32506ffe5b2SHans de Goede if (ACPI_FAILURE(status)) { 32606ffe5b2SHans de Goede dev_err(data->dev, "Failed to call KBLC method: 0x%x\n", status); 32706ffe5b2SHans de Goede return status; 32806ffe5b2SHans de Goede } 32906ffe5b2SHans de Goede 33006ffe5b2SHans de Goede kfree(output.pointer); 33106ffe5b2SHans de Goede return 0; 33206ffe5b2SHans de Goede } 33306ffe5b2SHans de Goede 33406ffe5b2SHans de Goede static int yogabook_wmi_probe(struct wmi_device *wdev, const void *context) 33506ffe5b2SHans de Goede { 33606ffe5b2SHans de Goede struct device *dev = &wdev->dev; 33706ffe5b2SHans de Goede struct yogabook_data *data; 33806ffe5b2SHans de Goede int r; 33906ffe5b2SHans de Goede 34006ffe5b2SHans de Goede data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 34106ffe5b2SHans de Goede if (data == NULL) 34206ffe5b2SHans de Goede return -ENOMEM; 34306ffe5b2SHans de Goede 34406ffe5b2SHans de Goede data->kbd_adev = acpi_dev_get_first_match_dev("GDIX1001", NULL, -1); 34506ffe5b2SHans de Goede if (!data->kbd_adev) 34606ffe5b2SHans de Goede return dev_err_probe(dev, -ENODEV, "Cannot find the touchpad device in ACPI tables\n"); 34706ffe5b2SHans de Goede 34806ffe5b2SHans de Goede data->dig_adev = acpi_dev_get_first_match_dev("WCOM0019", NULL, -1); 34906ffe5b2SHans de Goede if (!data->dig_adev) { 35006ffe5b2SHans de Goede r = dev_err_probe(dev, -ENODEV, "Cannot find the digitizer device in ACPI tables\n"); 35106ffe5b2SHans de Goede goto error_put_devs; 35206ffe5b2SHans de Goede } 35306ffe5b2SHans de Goede 35406ffe5b2SHans de Goede data->kbd_dev = get_device(acpi_get_first_physical_node(data->kbd_adev)); 35506ffe5b2SHans de Goede if (!data->kbd_dev || !data->kbd_dev->driver) { 35606ffe5b2SHans de Goede r = -EPROBE_DEFER; 35706ffe5b2SHans de Goede goto error_put_devs; 35806ffe5b2SHans de Goede } 35906ffe5b2SHans de Goede 36006ffe5b2SHans de Goede data->dig_dev = get_device(acpi_get_first_physical_node(data->dig_adev)); 36106ffe5b2SHans de Goede if (!data->dig_dev || !data->dig_dev->driver) { 36206ffe5b2SHans de Goede r = -EPROBE_DEFER; 36306ffe5b2SHans de Goede goto error_put_devs; 36406ffe5b2SHans de Goede } 36506ffe5b2SHans de Goede 36606ffe5b2SHans de Goede data->set_kbd_backlight = yogabook_wmi_set_kbd_backlight; 36706ffe5b2SHans de Goede 36806ffe5b2SHans de Goede r = yogabook_probe(dev, data, "ybwmi::kbd_backlight"); 36906ffe5b2SHans de Goede if (r) 37006ffe5b2SHans de Goede goto error_put_devs; 37106ffe5b2SHans de Goede 37206ffe5b2SHans de Goede return 0; 37306ffe5b2SHans de Goede 37406ffe5b2SHans de Goede error_put_devs: 37506ffe5b2SHans de Goede put_device(data->dig_dev); 37606ffe5b2SHans de Goede put_device(data->kbd_dev); 37706ffe5b2SHans de Goede acpi_dev_put(data->dig_adev); 37806ffe5b2SHans de Goede acpi_dev_put(data->kbd_adev); 37906ffe5b2SHans de Goede return r; 38006ffe5b2SHans de Goede } 38106ffe5b2SHans de Goede 38206ffe5b2SHans de Goede static void yogabook_wmi_remove(struct wmi_device *wdev) 38306ffe5b2SHans de Goede { 38406ffe5b2SHans de Goede struct yogabook_data *data = dev_get_drvdata(&wdev->dev); 38506ffe5b2SHans de Goede 38606ffe5b2SHans de Goede yogabook_remove(data); 38706ffe5b2SHans de Goede 38806ffe5b2SHans de Goede put_device(data->dig_dev); 38906ffe5b2SHans de Goede put_device(data->kbd_dev); 39006ffe5b2SHans de Goede acpi_dev_put(data->dig_adev); 39106ffe5b2SHans de Goede acpi_dev_put(data->kbd_adev); 39206ffe5b2SHans de Goede } 39306ffe5b2SHans de Goede 39406ffe5b2SHans de Goede static void yogabook_wmi_notify(struct wmi_device *wdev, union acpi_object *dummy) 39506ffe5b2SHans de Goede { 39606ffe5b2SHans de Goede yogabook_toggle_digitizer_mode(dev_get_drvdata(&wdev->dev)); 39706ffe5b2SHans de Goede } 39806ffe5b2SHans de Goede 39906ffe5b2SHans de Goede static const struct wmi_device_id yogabook_wmi_id_table[] = { 40006ffe5b2SHans de Goede { 40106ffe5b2SHans de Goede .guid_string = YB_MBTN_EVENT_GUID, 40206ffe5b2SHans de Goede }, 40306ffe5b2SHans de Goede { } /* Terminating entry */ 40406ffe5b2SHans de Goede }; 40506ffe5b2SHans de Goede MODULE_DEVICE_TABLE(wmi, yogabook_wmi_id_table); 40606ffe5b2SHans de Goede 40706ffe5b2SHans de Goede static struct wmi_driver yogabook_wmi_driver = { 40806ffe5b2SHans de Goede .driver = { 40906ffe5b2SHans de Goede .name = "yogabook-wmi", 41006ffe5b2SHans de Goede .pm = pm_sleep_ptr(&yogabook_pm_ops), 41106ffe5b2SHans de Goede }, 41206ffe5b2SHans de Goede .no_notify_data = true, 41306ffe5b2SHans de Goede .id_table = yogabook_wmi_id_table, 41406ffe5b2SHans de Goede .probe = yogabook_wmi_probe, 41506ffe5b2SHans de Goede .remove = yogabook_wmi_remove, 41606ffe5b2SHans de Goede .notify = yogabook_wmi_notify, 41706ffe5b2SHans de Goede }; 41806ffe5b2SHans de Goede 41906ffe5b2SHans de Goede /********** platform driver code **********/ 42006ffe5b2SHans de Goede 42106ffe5b2SHans de Goede static struct gpiod_lookup_table yogabook_pdev_gpios = { 42206ffe5b2SHans de Goede .dev_id = YB_PDEV_NAME, 42306ffe5b2SHans de Goede .table = { 42406ffe5b2SHans de Goede GPIO_LOOKUP("INT33FF:00", 95, "pen_touch_event", GPIO_ACTIVE_HIGH), 42506ffe5b2SHans de Goede GPIO_LOOKUP("INT33FF:03", 52, "enable_keyboard_led", GPIO_ACTIVE_HIGH), 42606ffe5b2SHans de Goede {} 42706ffe5b2SHans de Goede }, 42806ffe5b2SHans de Goede }; 42906ffe5b2SHans de Goede 43006ffe5b2SHans de Goede static int yogabook_pdev_set_kbd_backlight(struct yogabook_data *data, u8 level) 43106ffe5b2SHans de Goede { 43206ffe5b2SHans de Goede struct pwm_state state = { 43306ffe5b2SHans de Goede .period = YB_KBD_BL_PWM_PERIOD, 43406ffe5b2SHans de Goede .duty_cycle = YB_KBD_BL_PWM_PERIOD * level / YB_KBD_BL_MAX, 43506ffe5b2SHans de Goede .enabled = level, 43606ffe5b2SHans de Goede }; 43706ffe5b2SHans de Goede 438*a10c3d5fSSean Young pwm_apply_might_sleep(data->kbd_bl_pwm, &state); 43906ffe5b2SHans de Goede gpiod_set_value(data->kbd_bl_led_enable, level ? 1 : 0); 44006ffe5b2SHans de Goede return 0; 44106ffe5b2SHans de Goede } 44206ffe5b2SHans de Goede 44306ffe5b2SHans de Goede static irqreturn_t yogabook_pen_touch_irq(int irq, void *data) 44406ffe5b2SHans de Goede { 44506ffe5b2SHans de Goede yogabook_toggle_digitizer_mode(data); 44606ffe5b2SHans de Goede return IRQ_HANDLED; 44706ffe5b2SHans de Goede } 44806ffe5b2SHans de Goede 44906ffe5b2SHans de Goede static int yogabook_pdev_probe(struct platform_device *pdev) 45006ffe5b2SHans de Goede { 45106ffe5b2SHans de Goede struct device *dev = &pdev->dev; 45206ffe5b2SHans de Goede struct yogabook_data *data; 45306ffe5b2SHans de Goede int r; 45406ffe5b2SHans de Goede 45506ffe5b2SHans de Goede data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 45606ffe5b2SHans de Goede if (data == NULL) 45706ffe5b2SHans de Goede return -ENOMEM; 45806ffe5b2SHans de Goede 45906ffe5b2SHans de Goede data->kbd_dev = bus_find_device_by_name(&i2c_bus_type, NULL, "i2c-goodix_ts"); 46006ffe5b2SHans de Goede if (!data->kbd_dev || !data->kbd_dev->driver) { 46106ffe5b2SHans de Goede r = -EPROBE_DEFER; 46206ffe5b2SHans de Goede goto error_put_devs; 46306ffe5b2SHans de Goede } 46406ffe5b2SHans de Goede 46506ffe5b2SHans de Goede data->dig_dev = bus_find_device_by_name(&i2c_bus_type, NULL, "i2c-wacom"); 46606ffe5b2SHans de Goede if (!data->dig_dev || !data->dig_dev->driver) { 46706ffe5b2SHans de Goede r = -EPROBE_DEFER; 46806ffe5b2SHans de Goede goto error_put_devs; 46906ffe5b2SHans de Goede } 47006ffe5b2SHans de Goede 47106ffe5b2SHans de Goede gpiod_add_lookup_table(&yogabook_pdev_gpios); 47206ffe5b2SHans de Goede data->pen_touch_event = devm_gpiod_get(dev, "pen_touch_event", GPIOD_IN); 47306ffe5b2SHans de Goede data->kbd_bl_led_enable = devm_gpiod_get(dev, "enable_keyboard_led", GPIOD_OUT_HIGH); 47406ffe5b2SHans de Goede gpiod_remove_lookup_table(&yogabook_pdev_gpios); 47506ffe5b2SHans de Goede 47606ffe5b2SHans de Goede if (IS_ERR(data->pen_touch_event)) { 47706ffe5b2SHans de Goede r = dev_err_probe(dev, PTR_ERR(data->pen_touch_event), 47806ffe5b2SHans de Goede "Getting pen_touch_event GPIO\n"); 47906ffe5b2SHans de Goede goto error_put_devs; 48006ffe5b2SHans de Goede } 48106ffe5b2SHans de Goede 48206ffe5b2SHans de Goede if (IS_ERR(data->kbd_bl_led_enable)) { 48306ffe5b2SHans de Goede r = dev_err_probe(dev, PTR_ERR(data->kbd_bl_led_enable), 48406ffe5b2SHans de Goede "Getting enable_keyboard_led GPIO\n"); 48506ffe5b2SHans de Goede goto error_put_devs; 48606ffe5b2SHans de Goede } 48706ffe5b2SHans de Goede 48806ffe5b2SHans de Goede data->kbd_bl_pwm = devm_pwm_get(dev, "pwm_soc_lpss_2"); 48906ffe5b2SHans de Goede if (IS_ERR(data->kbd_bl_pwm)) { 49006ffe5b2SHans de Goede r = dev_err_probe(dev, PTR_ERR(data->kbd_bl_pwm), 49106ffe5b2SHans de Goede "Getting keyboard backlight PWM\n"); 49206ffe5b2SHans de Goede goto error_put_devs; 49306ffe5b2SHans de Goede } 49406ffe5b2SHans de Goede 49506ffe5b2SHans de Goede r = gpiod_to_irq(data->pen_touch_event); 49606ffe5b2SHans de Goede if (r < 0) { 49706ffe5b2SHans de Goede dev_err_probe(dev, r, "Getting pen_touch_event IRQ\n"); 49806ffe5b2SHans de Goede goto error_put_devs; 49906ffe5b2SHans de Goede } 50006ffe5b2SHans de Goede data->pen_touch_irq = r; 50106ffe5b2SHans de Goede 50206ffe5b2SHans de Goede r = request_irq(data->pen_touch_irq, yogabook_pen_touch_irq, IRQF_TRIGGER_FALLING, 50306ffe5b2SHans de Goede "pen_touch_event", data); 50406ffe5b2SHans de Goede if (r) { 50506ffe5b2SHans de Goede dev_err_probe(dev, r, "Requesting pen_touch_event IRQ\n"); 50606ffe5b2SHans de Goede goto error_put_devs; 50706ffe5b2SHans de Goede } 50806ffe5b2SHans de Goede 50906ffe5b2SHans de Goede data->set_kbd_backlight = yogabook_pdev_set_kbd_backlight; 51006ffe5b2SHans de Goede 51106ffe5b2SHans de Goede r = yogabook_probe(dev, data, "yogabook::kbd_backlight"); 51206ffe5b2SHans de Goede if (r) 51306ffe5b2SHans de Goede goto error_free_irq; 51406ffe5b2SHans de Goede 51506ffe5b2SHans de Goede return 0; 51606ffe5b2SHans de Goede 51706ffe5b2SHans de Goede error_free_irq: 51806ffe5b2SHans de Goede free_irq(data->pen_touch_irq, data); 51906ffe5b2SHans de Goede cancel_work_sync(&data->work); 52006ffe5b2SHans de Goede error_put_devs: 52106ffe5b2SHans de Goede put_device(data->dig_dev); 52206ffe5b2SHans de Goede put_device(data->kbd_dev); 52306ffe5b2SHans de Goede return r; 52406ffe5b2SHans de Goede } 52506ffe5b2SHans de Goede 52606ffe5b2SHans de Goede static void yogabook_pdev_remove(struct platform_device *pdev) 52706ffe5b2SHans de Goede { 52806ffe5b2SHans de Goede struct yogabook_data *data = platform_get_drvdata(pdev); 52906ffe5b2SHans de Goede 53006ffe5b2SHans de Goede yogabook_remove(data); 53106ffe5b2SHans de Goede free_irq(data->pen_touch_irq, data); 53206ffe5b2SHans de Goede cancel_work_sync(&data->work); 53306ffe5b2SHans de Goede put_device(data->dig_dev); 53406ffe5b2SHans de Goede put_device(data->kbd_dev); 53506ffe5b2SHans de Goede } 53606ffe5b2SHans de Goede 53706ffe5b2SHans de Goede static struct platform_driver yogabook_pdev_driver = { 53806ffe5b2SHans de Goede .probe = yogabook_pdev_probe, 53906ffe5b2SHans de Goede .remove_new = yogabook_pdev_remove, 54006ffe5b2SHans de Goede .driver = { 54106ffe5b2SHans de Goede .name = YB_PDEV_NAME, 54206ffe5b2SHans de Goede .pm = pm_sleep_ptr(&yogabook_pm_ops), 54306ffe5b2SHans de Goede }, 54406ffe5b2SHans de Goede }; 54506ffe5b2SHans de Goede 54606ffe5b2SHans de Goede static int __init yogabook_module_init(void) 54706ffe5b2SHans de Goede { 54806ffe5b2SHans de Goede int r; 54906ffe5b2SHans de Goede 55006ffe5b2SHans de Goede r = wmi_driver_register(&yogabook_wmi_driver); 55106ffe5b2SHans de Goede if (r) 55206ffe5b2SHans de Goede return r; 55306ffe5b2SHans de Goede 55406ffe5b2SHans de Goede r = platform_driver_register(&yogabook_pdev_driver); 55506ffe5b2SHans de Goede if (r) 55606ffe5b2SHans de Goede wmi_driver_unregister(&yogabook_wmi_driver); 55706ffe5b2SHans de Goede 55806ffe5b2SHans de Goede return r; 55906ffe5b2SHans de Goede } 56006ffe5b2SHans de Goede 56106ffe5b2SHans de Goede static void __exit yogabook_module_exit(void) 56206ffe5b2SHans de Goede { 56306ffe5b2SHans de Goede platform_driver_unregister(&yogabook_pdev_driver); 56406ffe5b2SHans de Goede wmi_driver_unregister(&yogabook_wmi_driver); 56506ffe5b2SHans de Goede } 56606ffe5b2SHans de Goede 56706ffe5b2SHans de Goede module_init(yogabook_module_init); 56806ffe5b2SHans de Goede module_exit(yogabook_module_exit); 56906ffe5b2SHans de Goede 57006ffe5b2SHans de Goede MODULE_ALIAS("platform:" YB_PDEV_NAME); 57106ffe5b2SHans de Goede MODULE_AUTHOR("Yauhen Kharuzhy"); 57206ffe5b2SHans de Goede MODULE_DESCRIPTION("Lenovo Yoga Book driver"); 57306ffe5b2SHans de Goede MODULE_LICENSE("GPL v2"); 574