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 
yogabook_work(struct work_struct * work)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 
yogabook_toggle_digitizer_mode(struct yogabook_data * data)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 
yogabook_backside_hall_irq(int irq,void * _data)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 
kbd_brightness_get(struct led_classdev * cdev)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 
kbd_brightness_set(struct led_classdev * cdev,enum led_brightness value)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 
yogabook_probe(struct device * dev,struct yogabook_data * data,const char * kbd_bl_led_name)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 
yogabook_remove(struct yogabook_data * data)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 
yogabook_suspend(struct device * dev)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 
yogabook_resume(struct device * dev)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  */
yogabook_wmi_set_kbd_backlight(struct yogabook_data * data,uint8_t level)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 = &param;
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 
yogabook_wmi_probe(struct wmi_device * wdev,const void * context)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 
yogabook_wmi_remove(struct wmi_device * wdev)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 
yogabook_wmi_notify(struct wmi_device * wdev,union acpi_object * dummy)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 
yogabook_pdev_set_kbd_backlight(struct yogabook_data * data,u8 level)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 
438a10c3d5fSSean 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 
yogabook_pen_touch_irq(int irq,void * data)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 
yogabook_pdev_probe(struct platform_device * pdev)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 
yogabook_pdev_remove(struct platform_device * pdev)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 
yogabook_module_init(void)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 
yogabook_module_exit(void)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