xref: /linux/drivers/input/touchscreen/wm831x-ts.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
200cfa730SMark Brown /*
300cfa730SMark Brown  * Touchscreen driver for WM831x PMICs
400cfa730SMark Brown  *
500cfa730SMark Brown  * Copyright 2011 Wolfson Microelectronics plc.
600cfa730SMark Brown  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
700cfa730SMark Brown  */
800cfa730SMark Brown 
900cfa730SMark Brown #include <linux/module.h>
1000cfa730SMark Brown #include <linux/moduleparam.h>
1100cfa730SMark Brown #include <linux/kernel.h>
1200cfa730SMark Brown #include <linux/string.h>
1300cfa730SMark Brown #include <linux/pm.h>
1400cfa730SMark Brown #include <linux/input.h>
1500cfa730SMark Brown #include <linux/interrupt.h>
1600cfa730SMark Brown #include <linux/io.h>
1700cfa730SMark Brown #include <linux/mfd/wm831x/core.h>
1800cfa730SMark Brown #include <linux/mfd/wm831x/irq.h>
1900cfa730SMark Brown #include <linux/mfd/wm831x/pdata.h>
2000cfa730SMark Brown #include <linux/platform_device.h>
2100cfa730SMark Brown #include <linux/slab.h>
2200cfa730SMark Brown #include <linux/types.h>
2300cfa730SMark Brown 
2400cfa730SMark Brown /*
2500cfa730SMark Brown  * R16424 (0x4028) - Touch Control 1
2600cfa730SMark Brown  */
2700cfa730SMark Brown #define WM831X_TCH_ENA                          0x8000  /* TCH_ENA */
2800cfa730SMark Brown #define WM831X_TCH_CVT_ENA                      0x4000  /* TCH_CVT_ENA */
2900cfa730SMark Brown #define WM831X_TCH_SLPENA                       0x1000  /* TCH_SLPENA */
3000cfa730SMark Brown #define WM831X_TCH_Z_ENA                        0x0400  /* TCH_Z_ENA */
3100cfa730SMark Brown #define WM831X_TCH_Y_ENA                        0x0200  /* TCH_Y_ENA */
3200cfa730SMark Brown #define WM831X_TCH_X_ENA                        0x0100  /* TCH_X_ENA */
3300cfa730SMark Brown #define WM831X_TCH_DELAY_MASK                   0x00E0  /* TCH_DELAY - [7:5] */
3400cfa730SMark Brown #define WM831X_TCH_DELAY_SHIFT                       5  /* TCH_DELAY - [7:5] */
3500cfa730SMark Brown #define WM831X_TCH_DELAY_WIDTH                       3  /* TCH_DELAY - [7:5] */
3600cfa730SMark Brown #define WM831X_TCH_RATE_MASK                    0x001F  /* TCH_RATE - [4:0] */
3700cfa730SMark Brown #define WM831X_TCH_RATE_SHIFT                        0  /* TCH_RATE - [4:0] */
3800cfa730SMark Brown #define WM831X_TCH_RATE_WIDTH                        5  /* TCH_RATE - [4:0] */
3900cfa730SMark Brown 
4000cfa730SMark Brown /*
4100cfa730SMark Brown  * R16425 (0x4029) - Touch Control 2
4200cfa730SMark Brown  */
4300cfa730SMark Brown #define WM831X_TCH_PD_WK                        0x2000  /* TCH_PD_WK */
4400cfa730SMark Brown #define WM831X_TCH_5WIRE                        0x1000  /* TCH_5WIRE */
4500cfa730SMark Brown #define WM831X_TCH_PDONLY                       0x0800  /* TCH_PDONLY */
4600cfa730SMark Brown #define WM831X_TCH_ISEL                         0x0100  /* TCH_ISEL */
4700cfa730SMark Brown #define WM831X_TCH_RPU_MASK                     0x000F  /* TCH_RPU - [3:0] */
4800cfa730SMark Brown #define WM831X_TCH_RPU_SHIFT                         0  /* TCH_RPU - [3:0] */
4900cfa730SMark Brown #define WM831X_TCH_RPU_WIDTH                         4  /* TCH_RPU - [3:0] */
5000cfa730SMark Brown 
5100cfa730SMark Brown /*
5200cfa730SMark Brown  * R16426-8 (0x402A-C) - Touch Data X/Y/X
5300cfa730SMark Brown  */
5400cfa730SMark Brown #define WM831X_TCH_PD                           0x8000  /* TCH_PD1 */
5500cfa730SMark Brown #define WM831X_TCH_DATA_MASK                    0x0FFF  /* TCH_DATA - [11:0] */
5600cfa730SMark Brown #define WM831X_TCH_DATA_SHIFT                        0  /* TCH_DATA - [11:0] */
5700cfa730SMark Brown #define WM831X_TCH_DATA_WIDTH                       12  /* TCH_DATA - [11:0] */
5800cfa730SMark Brown 
5900cfa730SMark Brown struct wm831x_ts {
6000cfa730SMark Brown 	struct input_dev *input_dev;
6100cfa730SMark Brown 	struct wm831x *wm831x;
6200cfa730SMark Brown 	unsigned int data_irq;
6300cfa730SMark Brown 	unsigned int pd_irq;
6400cfa730SMark Brown 	bool pressure;
6500cfa730SMark Brown 	bool pen_down;
66f5346668SMark Brown 	struct work_struct pd_data_work;
6700cfa730SMark Brown };
6800cfa730SMark Brown 
wm831x_pd_data_work(struct work_struct * work)69f5346668SMark Brown static void wm831x_pd_data_work(struct work_struct *work)
70f5346668SMark Brown {
71f5346668SMark Brown 	struct wm831x_ts *wm831x_ts =
72f5346668SMark Brown 		container_of(work, struct wm831x_ts, pd_data_work);
73f5346668SMark Brown 
74f5346668SMark Brown 	if (wm831x_ts->pen_down) {
75f5346668SMark Brown 		enable_irq(wm831x_ts->data_irq);
76f5346668SMark Brown 		dev_dbg(wm831x_ts->wm831x->dev, "IRQ PD->DATA done\n");
77f5346668SMark Brown 	} else {
78f5346668SMark Brown 		enable_irq(wm831x_ts->pd_irq);
79f5346668SMark Brown 		dev_dbg(wm831x_ts->wm831x->dev, "IRQ DATA->PD done\n");
80f5346668SMark Brown 	}
81f5346668SMark Brown }
82f5346668SMark Brown 
wm831x_ts_data_irq(int irq,void * irq_data)8300cfa730SMark Brown static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data)
8400cfa730SMark Brown {
8500cfa730SMark Brown 	struct wm831x_ts *wm831x_ts = irq_data;
8600cfa730SMark Brown 	struct wm831x *wm831x = wm831x_ts->wm831x;
8700cfa730SMark Brown 	static int data_types[] = { ABS_X, ABS_Y, ABS_PRESSURE };
8800cfa730SMark Brown 	u16 data[3];
89723d9284SMark Brown 	int count;
9000cfa730SMark Brown 	int i, ret;
9100cfa730SMark Brown 
92723d9284SMark Brown 	if (wm831x_ts->pressure)
93723d9284SMark Brown 		count = 3;
94723d9284SMark Brown 	else
95723d9284SMark Brown 		count = 2;
96723d9284SMark Brown 
9700cfa730SMark Brown 	wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
9800cfa730SMark Brown 			WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT);
9900cfa730SMark Brown 
10000cfa730SMark Brown 	ret = wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count,
10100cfa730SMark Brown 			       data);
10200cfa730SMark Brown 	if (ret != 0) {
10300cfa730SMark Brown 		dev_err(wm831x->dev, "Failed to read touch data: %d\n",
10400cfa730SMark Brown 			ret);
10500cfa730SMark Brown 		return IRQ_NONE;
10600cfa730SMark Brown 	}
10700cfa730SMark Brown 
10800cfa730SMark Brown 	/*
10900cfa730SMark Brown 	 * We get a pen down reading on every reading, report pen up if any
11000cfa730SMark Brown 	 * individual reading does so.
11100cfa730SMark Brown 	 */
11200cfa730SMark Brown 	wm831x_ts->pen_down = true;
11300cfa730SMark Brown 	for (i = 0; i < count; i++) {
11400cfa730SMark Brown 		if (!(data[i] & WM831X_TCH_PD)) {
11500cfa730SMark Brown 			wm831x_ts->pen_down = false;
11600cfa730SMark Brown 			continue;
11700cfa730SMark Brown 		}
11800cfa730SMark Brown 		input_report_abs(wm831x_ts->input_dev, data_types[i],
11900cfa730SMark Brown 				 data[i] & WM831X_TCH_DATA_MASK);
12000cfa730SMark Brown 	}
12100cfa730SMark Brown 
12200cfa730SMark Brown 	if (!wm831x_ts->pen_down) {
123f5346668SMark Brown 		/* Switch from data to pen down */
124f5346668SMark Brown 		dev_dbg(wm831x->dev, "IRQ DATA->PD\n");
125f5346668SMark Brown 
12600cfa730SMark Brown 		disable_irq_nosync(wm831x_ts->data_irq);
12700cfa730SMark Brown 
12800cfa730SMark Brown 		/* Don't need data any more */
12900cfa730SMark Brown 		wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
13000cfa730SMark Brown 				WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
13100cfa730SMark Brown 				WM831X_TCH_Z_ENA, 0);
13200cfa730SMark Brown 
13300cfa730SMark Brown 		/* Flush any final samples that arrived while reading */
13400cfa730SMark Brown 		wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
13500cfa730SMark Brown 				WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT);
13600cfa730SMark Brown 
13700cfa730SMark Brown 		wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, data);
13800cfa730SMark Brown 
13900cfa730SMark Brown 		if (wm831x_ts->pressure)
14000cfa730SMark Brown 			input_report_abs(wm831x_ts->input_dev,
14100cfa730SMark Brown 					 ABS_PRESSURE, 0);
14200cfa730SMark Brown 
14300cfa730SMark Brown 		input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 0);
144f5346668SMark Brown 
145f5346668SMark Brown 		schedule_work(&wm831x_ts->pd_data_work);
146bf283707SMark Brown 	} else {
147bf283707SMark Brown 		input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1);
14800cfa730SMark Brown 	}
14900cfa730SMark Brown 
15000cfa730SMark Brown 	input_sync(wm831x_ts->input_dev);
15100cfa730SMark Brown 
15200cfa730SMark Brown 	return IRQ_HANDLED;
15300cfa730SMark Brown }
15400cfa730SMark Brown 
wm831x_ts_pen_down_irq(int irq,void * irq_data)15500cfa730SMark Brown static irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data)
15600cfa730SMark Brown {
15700cfa730SMark Brown 	struct wm831x_ts *wm831x_ts = irq_data;
15800cfa730SMark Brown 	struct wm831x *wm831x = wm831x_ts->wm831x;
159723d9284SMark Brown 	int ena = 0;
16000cfa730SMark Brown 
161f5346668SMark Brown 	if (wm831x_ts->pen_down)
162f5346668SMark Brown 		return IRQ_HANDLED;
163f5346668SMark Brown 
164f5346668SMark Brown 	disable_irq_nosync(wm831x_ts->pd_irq);
165f5346668SMark Brown 
16600cfa730SMark Brown 	/* Start collecting data */
167723d9284SMark Brown 	if (wm831x_ts->pressure)
168723d9284SMark Brown 		ena |= WM831X_TCH_Z_ENA;
16900cfa730SMark Brown 
17000cfa730SMark Brown 	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
17100cfa730SMark Brown 			WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA,
17200cfa730SMark Brown 			WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | ena);
17300cfa730SMark Brown 
17400cfa730SMark Brown 	wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
17500cfa730SMark Brown 			WM831X_TCHPD_EINT, WM831X_TCHPD_EINT);
17600cfa730SMark Brown 
17700cfa730SMark Brown 	wm831x_ts->pen_down = true;
178f5346668SMark Brown 
179f5346668SMark Brown 	/* Switch from pen down to data */
180f5346668SMark Brown 	dev_dbg(wm831x->dev, "IRQ PD->DATA\n");
181f5346668SMark Brown 	schedule_work(&wm831x_ts->pd_data_work);
18200cfa730SMark Brown 
18300cfa730SMark Brown 	return IRQ_HANDLED;
18400cfa730SMark Brown }
18500cfa730SMark Brown 
wm831x_ts_input_open(struct input_dev * idev)18600cfa730SMark Brown static int wm831x_ts_input_open(struct input_dev *idev)
18700cfa730SMark Brown {
18800cfa730SMark Brown 	struct wm831x_ts *wm831x_ts = input_get_drvdata(idev);
18900cfa730SMark Brown 	struct wm831x *wm831x = wm831x_ts->wm831x;
19000cfa730SMark Brown 
19100cfa730SMark Brown 	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
192e7cbb90aSMark Brown 			WM831X_TCH_ENA | WM831X_TCH_CVT_ENA |
193e7cbb90aSMark Brown 			WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
194e7cbb90aSMark Brown 			WM831X_TCH_Z_ENA, WM831X_TCH_ENA);
19500cfa730SMark Brown 
19600cfa730SMark Brown 	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
19700cfa730SMark Brown 			WM831X_TCH_CVT_ENA, WM831X_TCH_CVT_ENA);
19800cfa730SMark Brown 
19900cfa730SMark Brown 	return 0;
20000cfa730SMark Brown }
20100cfa730SMark Brown 
wm831x_ts_input_close(struct input_dev * idev)20200cfa730SMark Brown static void wm831x_ts_input_close(struct input_dev *idev)
20300cfa730SMark Brown {
20400cfa730SMark Brown 	struct wm831x_ts *wm831x_ts = input_get_drvdata(idev);
20500cfa730SMark Brown 	struct wm831x *wm831x = wm831x_ts->wm831x;
20600cfa730SMark Brown 
207f5346668SMark Brown 	/* Shut the controller down, disabling all other functionality too */
20800cfa730SMark Brown 	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
209f5346668SMark Brown 			WM831X_TCH_ENA | WM831X_TCH_X_ENA |
210f5346668SMark Brown 			WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, 0);
21100cfa730SMark Brown 
212f5346668SMark Brown 	/* Make sure any pending IRQs are done, the above will prevent
213f5346668SMark Brown 	 * new ones firing.
214f5346668SMark Brown 	 */
215f5346668SMark Brown 	synchronize_irq(wm831x_ts->data_irq);
216f5346668SMark Brown 	synchronize_irq(wm831x_ts->pd_irq);
217f5346668SMark Brown 
218f5346668SMark Brown 	/* Make sure the IRQ completion work is quiesced */
21943829731STejun Heo 	flush_work(&wm831x_ts->pd_data_work);
220f5346668SMark Brown 
221f5346668SMark Brown 	/* If we ended up with the pen down then make sure we revert back
222f5346668SMark Brown 	 * to pen detection state for the next time we start up.
223f5346668SMark Brown 	 */
224f5346668SMark Brown 	if (wm831x_ts->pen_down) {
22500cfa730SMark Brown 		disable_irq(wm831x_ts->data_irq);
226f5346668SMark Brown 		enable_irq(wm831x_ts->pd_irq);
227f5346668SMark Brown 		wm831x_ts->pen_down = false;
228f5346668SMark Brown 	}
22900cfa730SMark Brown }
23000cfa730SMark Brown 
wm831x_ts_probe(struct platform_device * pdev)2315298cc4cSBill Pemberton static int wm831x_ts_probe(struct platform_device *pdev)
23200cfa730SMark Brown {
23300cfa730SMark Brown 	struct wm831x_ts *wm831x_ts;
23400cfa730SMark Brown 	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
23500cfa730SMark Brown 	struct wm831x_pdata *core_pdata = dev_get_platdata(pdev->dev.parent);
236723d9284SMark Brown 	struct wm831x_touch_pdata *pdata = NULL;
23700cfa730SMark Brown 	struct input_dev *input_dev;
238acad9853SMark Brown 	int error, irqf;
23900cfa730SMark Brown 
240723d9284SMark Brown 	if (core_pdata)
241723d9284SMark Brown 		pdata = core_pdata->touch;
242723d9284SMark Brown 
243ef8dee5cSMark Brown 	wm831x_ts = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ts),
244ef8dee5cSMark Brown 				 GFP_KERNEL);
245e7cd0aebSMark Brown 	input_dev = devm_input_allocate_device(&pdev->dev);
24600cfa730SMark Brown 	if (!wm831x_ts || !input_dev) {
24700cfa730SMark Brown 		error = -ENOMEM;
24800cfa730SMark Brown 		goto err_alloc;
24900cfa730SMark Brown 	}
25000cfa730SMark Brown 
25100cfa730SMark Brown 	wm831x_ts->wm831x = wm831x;
25200cfa730SMark Brown 	wm831x_ts->input_dev = input_dev;
253f5346668SMark Brown 	INIT_WORK(&wm831x_ts->pd_data_work, wm831x_pd_data_work);
25400cfa730SMark Brown 
25500cfa730SMark Brown 	/*
25600cfa730SMark Brown 	 * If we have a direct IRQ use it, otherwise use the interrupt
25700cfa730SMark Brown 	 * from the WM831x IRQ controller.
25800cfa730SMark Brown 	 */
259cd99758bSMark Brown 	wm831x_ts->data_irq = wm831x_irq(wm831x,
260cd99758bSMark Brown 					 platform_get_irq_byname(pdev,
261cd99758bSMark Brown 								 "TCHDATA"));
26200cfa730SMark Brown 	if (pdata && pdata->data_irq)
26300cfa730SMark Brown 		wm831x_ts->data_irq = pdata->data_irq;
26400cfa730SMark Brown 
265cd99758bSMark Brown 	wm831x_ts->pd_irq = wm831x_irq(wm831x,
266cd99758bSMark Brown 				       platform_get_irq_byname(pdev, "TCHPD"));
26700cfa730SMark Brown 	if (pdata && pdata->pd_irq)
26800cfa730SMark Brown 		wm831x_ts->pd_irq = pdata->pd_irq;
26900cfa730SMark Brown 
27023c483d2SMark Brown 	if (pdata)
27123c483d2SMark Brown 		wm831x_ts->pressure = pdata->pressure;
27223c483d2SMark Brown 	else
27323c483d2SMark Brown 		wm831x_ts->pressure = true;
27400cfa730SMark Brown 
27500cfa730SMark Brown 	/* Five wire touchscreens can't report pressure */
27600cfa730SMark Brown 	if (pdata && pdata->fivewire) {
27700cfa730SMark Brown 		wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
27800cfa730SMark Brown 				WM831X_TCH_5WIRE, WM831X_TCH_5WIRE);
27900cfa730SMark Brown 
28000cfa730SMark Brown 		/* Pressure measurements are not possible for five wire mode */
28100cfa730SMark Brown 		WARN_ON(pdata->pressure && pdata->fivewire);
28200cfa730SMark Brown 		wm831x_ts->pressure = false;
28300cfa730SMark Brown 	} else {
28400cfa730SMark Brown 		wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
28500cfa730SMark Brown 				WM831X_TCH_5WIRE, 0);
28600cfa730SMark Brown 	}
28700cfa730SMark Brown 
28800cfa730SMark Brown 	if (pdata) {
28900cfa730SMark Brown 		switch (pdata->isel) {
29000cfa730SMark Brown 		default:
29100cfa730SMark Brown 			dev_err(&pdev->dev, "Unsupported ISEL setting: %d\n",
29200cfa730SMark Brown 				pdata->isel);
293df561f66SGustavo A. R. Silva 			fallthrough;
29400cfa730SMark Brown 		case 200:
29500cfa730SMark Brown 		case 0:
29600cfa730SMark Brown 			wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
29700cfa730SMark Brown 					WM831X_TCH_ISEL, 0);
29800cfa730SMark Brown 			break;
29900cfa730SMark Brown 		case 400:
30000cfa730SMark Brown 			wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
30100cfa730SMark Brown 					WM831X_TCH_ISEL, WM831X_TCH_ISEL);
30200cfa730SMark Brown 			break;
30300cfa730SMark Brown 		}
30400cfa730SMark Brown 	}
30500cfa730SMark Brown 
30600cfa730SMark Brown 	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
30700cfa730SMark Brown 			WM831X_TCH_PDONLY, 0);
30800cfa730SMark Brown 
30900cfa730SMark Brown 	/* Default to 96 samples/sec */
31000cfa730SMark Brown 	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
31100cfa730SMark Brown 			WM831X_TCH_RATE_MASK, 6);
31200cfa730SMark Brown 
313acad9853SMark Brown 	if (pdata && pdata->data_irqf)
314acad9853SMark Brown 		irqf = pdata->data_irqf;
315acad9853SMark Brown 	else
316acad9853SMark Brown 		irqf = IRQF_TRIGGER_HIGH;
317acad9853SMark Brown 
31800cfa730SMark Brown 	error = request_threaded_irq(wm831x_ts->data_irq,
31900cfa730SMark Brown 				     NULL, wm831x_ts_data_irq,
320bcd9730aSBarry Song 				     irqf | IRQF_ONESHOT | IRQF_NO_AUTOEN,
32100cfa730SMark Brown 				     "Touchscreen data", wm831x_ts);
32200cfa730SMark Brown 	if (error) {
32300cfa730SMark Brown 		dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n",
32400cfa730SMark Brown 			wm831x_ts->data_irq, error);
32500cfa730SMark Brown 		goto err_alloc;
32600cfa730SMark Brown 	}
32700cfa730SMark Brown 
328acad9853SMark Brown 	if (pdata && pdata->pd_irqf)
329acad9853SMark Brown 		irqf = pdata->pd_irqf;
330acad9853SMark Brown 	else
331acad9853SMark Brown 		irqf = IRQF_TRIGGER_HIGH;
332acad9853SMark Brown 
33300cfa730SMark Brown 	error = request_threaded_irq(wm831x_ts->pd_irq,
33400cfa730SMark Brown 				     NULL, wm831x_ts_pen_down_irq,
335acad9853SMark Brown 				     irqf | IRQF_ONESHOT,
33600cfa730SMark Brown 				     "Touchscreen pen down", wm831x_ts);
33700cfa730SMark Brown 	if (error) {
33800cfa730SMark Brown 		dev_err(&pdev->dev, "Failed to request pen down IRQ %d: %d\n",
33900cfa730SMark Brown 			wm831x_ts->pd_irq, error);
34000cfa730SMark Brown 		goto err_data_irq;
34100cfa730SMark Brown 	}
34200cfa730SMark Brown 
34300cfa730SMark Brown 	/* set up touch configuration */
34400cfa730SMark Brown 	input_dev->name = "WM831x touchscreen";
34500cfa730SMark Brown 	input_dev->phys = "wm831x";
34600cfa730SMark Brown 	input_dev->open = wm831x_ts_input_open;
34700cfa730SMark Brown 	input_dev->close = wm831x_ts_input_close;
34800cfa730SMark Brown 
34900cfa730SMark Brown 	__set_bit(EV_ABS, input_dev->evbit);
35000cfa730SMark Brown 	__set_bit(EV_KEY, input_dev->evbit);
35100cfa730SMark Brown 	__set_bit(BTN_TOUCH, input_dev->keybit);
35200cfa730SMark Brown 
35300cfa730SMark Brown 	input_set_abs_params(input_dev, ABS_X, 0, 4095, 5, 0);
35400cfa730SMark Brown 	input_set_abs_params(input_dev, ABS_Y, 0, 4095, 5, 0);
35500cfa730SMark Brown 	if (wm831x_ts->pressure)
35600cfa730SMark Brown 		input_set_abs_params(input_dev, ABS_PRESSURE, 0, 4095, 5, 0);
35700cfa730SMark Brown 
35800cfa730SMark Brown 	input_set_drvdata(input_dev, wm831x_ts);
35900cfa730SMark Brown 	input_dev->dev.parent = &pdev->dev;
36000cfa730SMark Brown 
36100cfa730SMark Brown 	error = input_register_device(input_dev);
36200cfa730SMark Brown 	if (error)
36300cfa730SMark Brown 		goto err_pd_irq;
36400cfa730SMark Brown 
36500cfa730SMark Brown 	platform_set_drvdata(pdev, wm831x_ts);
36600cfa730SMark Brown 	return 0;
36700cfa730SMark Brown 
36800cfa730SMark Brown err_pd_irq:
36900cfa730SMark Brown 	free_irq(wm831x_ts->pd_irq, wm831x_ts);
37000cfa730SMark Brown err_data_irq:
37100cfa730SMark Brown 	free_irq(wm831x_ts->data_irq, wm831x_ts);
37200cfa730SMark Brown err_alloc:
37300cfa730SMark Brown 
37400cfa730SMark Brown 	return error;
37500cfa730SMark Brown }
37600cfa730SMark Brown 
wm831x_ts_remove(struct platform_device * pdev)377*33984b4fSUwe Kleine-König static void wm831x_ts_remove(struct platform_device *pdev)
37800cfa730SMark Brown {
37900cfa730SMark Brown 	struct wm831x_ts *wm831x_ts = platform_get_drvdata(pdev);
38000cfa730SMark Brown 
38100cfa730SMark Brown 	free_irq(wm831x_ts->pd_irq, wm831x_ts);
38200cfa730SMark Brown 	free_irq(wm831x_ts->data_irq, wm831x_ts);
38300cfa730SMark Brown }
38400cfa730SMark Brown 
38500cfa730SMark Brown static struct platform_driver wm831x_ts_driver = {
38600cfa730SMark Brown 	.driver = {
38700cfa730SMark Brown 		.name = "wm831x-touch",
38800cfa730SMark Brown 	},
38900cfa730SMark Brown 	.probe = wm831x_ts_probe,
390*33984b4fSUwe Kleine-König 	.remove_new = wm831x_ts_remove,
39100cfa730SMark Brown };
392cdcc96e2SJJ Ding module_platform_driver(wm831x_ts_driver);
39300cfa730SMark Brown 
39400cfa730SMark Brown /* Module information */
39500cfa730SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
39600cfa730SMark Brown MODULE_DESCRIPTION("WM831x PMIC touchscreen driver");
39700cfa730SMark Brown MODULE_LICENSE("GPL");
39800cfa730SMark Brown MODULE_ALIAS("platform:wm831x-touch");
399