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