xref: /linux/drivers/input/touchscreen/da9034-ts.c (revision ca55b2fef3a9373fcfc30f82fd26bc7fccbda732)
1 /*
2  * Touchscreen driver for Dialog Semiconductor DA9034
3  *
4  * Copyright (C) 2006-2008 Marvell International Ltd.
5  *	Fengwei Yin <fengwei.yin@marvell.com>
6  *	Bin Yang  <bin.yang@marvell.com>
7  *	Eric Miao <eric.miao@marvell.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  */
13 
14 #include <linux/module.h>
15 #include <linux/kernel.h>
16 #include <linux/delay.h>
17 #include <linux/platform_device.h>
18 #include <linux/input.h>
19 #include <linux/workqueue.h>
20 #include <linux/mfd/da903x.h>
21 #include <linux/slab.h>
22 
23 #define DA9034_MANUAL_CTRL	0x50
24 #define DA9034_LDO_ADC_EN	(1 << 4)
25 
26 #define DA9034_AUTO_CTRL1	0x51
27 
28 #define DA9034_AUTO_CTRL2	0x52
29 #define DA9034_AUTO_TSI_EN	(1 << 3)
30 #define DA9034_PEN_DETECT	(1 << 4)
31 
32 #define DA9034_TSI_CTRL1	0x53
33 #define DA9034_TSI_CTRL2	0x54
34 #define DA9034_TSI_X_MSB	0x6c
35 #define DA9034_TSI_Y_MSB	0x6d
36 #define DA9034_TSI_XY_LSB	0x6e
37 
38 enum {
39 	STATE_IDLE,	/* wait for pendown */
40 	STATE_BUSY,	/* TSI busy sampling */
41 	STATE_STOP,	/* sample available */
42 	STATE_WAIT,	/* Wait to start next sample */
43 };
44 
45 enum {
46 	EVENT_PEN_DOWN,
47 	EVENT_PEN_UP,
48 	EVENT_TSI_READY,
49 	EVENT_TIMEDOUT,
50 };
51 
52 struct da9034_touch {
53 	struct device		*da9034_dev;
54 	struct input_dev	*input_dev;
55 
56 	struct delayed_work	tsi_work;
57 	struct notifier_block	notifier;
58 
59 	int	state;
60 
61 	int	interval_ms;
62 	int	x_inverted;
63 	int	y_inverted;
64 
65 	int	last_x;
66 	int	last_y;
67 };
68 
69 static inline int is_pen_down(struct da9034_touch *touch)
70 {
71 	return da903x_query_status(touch->da9034_dev, DA9034_STATUS_PEN_DOWN);
72 }
73 
74 static inline int detect_pen_down(struct da9034_touch *touch, int on)
75 {
76 	if (on)
77 		return da903x_set_bits(touch->da9034_dev,
78 				DA9034_AUTO_CTRL2, DA9034_PEN_DETECT);
79 	else
80 		return da903x_clr_bits(touch->da9034_dev,
81 				DA9034_AUTO_CTRL2, DA9034_PEN_DETECT);
82 }
83 
84 static int read_tsi(struct da9034_touch *touch)
85 {
86 	uint8_t _x, _y, _v;
87 	int ret;
88 
89 	ret = da903x_read(touch->da9034_dev, DA9034_TSI_X_MSB, &_x);
90 	if (ret)
91 		return ret;
92 
93 	ret = da903x_read(touch->da9034_dev, DA9034_TSI_Y_MSB, &_y);
94 	if (ret)
95 		return ret;
96 
97 	ret = da903x_read(touch->da9034_dev, DA9034_TSI_XY_LSB, &_v);
98 	if (ret)
99 		return ret;
100 
101 	touch->last_x = ((_x << 2) & 0x3fc) | (_v & 0x3);
102 	touch->last_y = ((_y << 2) & 0x3fc) | ((_v & 0xc) >> 2);
103 
104 	return 0;
105 }
106 
107 static inline int start_tsi(struct da9034_touch *touch)
108 {
109 	return da903x_set_bits(touch->da9034_dev,
110 			DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN);
111 }
112 
113 static inline int stop_tsi(struct da9034_touch *touch)
114 {
115 	return da903x_clr_bits(touch->da9034_dev,
116 			DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN);
117 }
118 
119 static inline void report_pen_down(struct da9034_touch *touch)
120 {
121 	int x = touch->last_x;
122 	int y = touch->last_y;
123 
124 	x &= 0xfff;
125 	if (touch->x_inverted)
126 		x = 1024 - x;
127 	y &= 0xfff;
128 	if (touch->y_inverted)
129 		y = 1024 - y;
130 
131 	input_report_abs(touch->input_dev, ABS_X, x);
132 	input_report_abs(touch->input_dev, ABS_Y, y);
133 	input_report_key(touch->input_dev, BTN_TOUCH, 1);
134 
135 	input_sync(touch->input_dev);
136 }
137 
138 static inline void report_pen_up(struct da9034_touch *touch)
139 {
140 	input_report_key(touch->input_dev, BTN_TOUCH, 0);
141 	input_sync(touch->input_dev);
142 }
143 
144 static void da9034_event_handler(struct da9034_touch *touch, int event)
145 {
146 	int err;
147 
148 	switch (touch->state) {
149 	case STATE_IDLE:
150 		if (event != EVENT_PEN_DOWN)
151 			break;
152 
153 		/* Enable auto measurement of the TSI, this will
154 		 * automatically disable pen down detection
155 		 */
156 		err = start_tsi(touch);
157 		if (err)
158 			goto err_reset;
159 
160 		touch->state = STATE_BUSY;
161 		break;
162 
163 	case STATE_BUSY:
164 		if (event != EVENT_TSI_READY)
165 			break;
166 
167 		err = read_tsi(touch);
168 		if (err)
169 			goto err_reset;
170 
171 		/* Disable auto measurement of the TSI, so that
172 		 * pen down status will be available
173 		 */
174 		err = stop_tsi(touch);
175 		if (err)
176 			goto err_reset;
177 
178 		touch->state = STATE_STOP;
179 
180 		/* FIXME: PEN_{UP/DOWN} events are expected to be
181 		 * available by stopping TSI, but this is found not
182 		 * always true, delay and simulate such an event
183 		 * here is more reliable
184 		 */
185 		mdelay(1);
186 		da9034_event_handler(touch,
187 				     is_pen_down(touch) ? EVENT_PEN_DOWN :
188 							  EVENT_PEN_UP);
189 		break;
190 
191 	case STATE_STOP:
192 		if (event == EVENT_PEN_DOWN) {
193 			report_pen_down(touch);
194 			schedule_delayed_work(&touch->tsi_work,
195 				msecs_to_jiffies(touch->interval_ms));
196 			touch->state = STATE_WAIT;
197 		}
198 
199 		if (event == EVENT_PEN_UP) {
200 			report_pen_up(touch);
201 			touch->state = STATE_IDLE;
202 		}
203 		break;
204 
205 	case STATE_WAIT:
206 		if (event != EVENT_TIMEDOUT)
207 			break;
208 
209 		if (is_pen_down(touch)) {
210 			start_tsi(touch);
211 			touch->state = STATE_BUSY;
212 		} else {
213 			report_pen_up(touch);
214 			touch->state = STATE_IDLE;
215 		}
216 		break;
217 	}
218 	return;
219 
220 err_reset:
221 	touch->state = STATE_IDLE;
222 	stop_tsi(touch);
223 	detect_pen_down(touch, 1);
224 }
225 
226 static void da9034_tsi_work(struct work_struct *work)
227 {
228 	struct da9034_touch *touch =
229 		container_of(work, struct da9034_touch, tsi_work.work);
230 
231 	da9034_event_handler(touch, EVENT_TIMEDOUT);
232 }
233 
234 static int da9034_touch_notifier(struct notifier_block *nb,
235 				 unsigned long event, void *data)
236 {
237 	struct da9034_touch *touch =
238 		container_of(nb, struct da9034_touch, notifier);
239 
240 	if (event & DA9034_EVENT_TSI_READY)
241 		da9034_event_handler(touch, EVENT_TSI_READY);
242 
243 	if ((event & DA9034_EVENT_PEN_DOWN) && touch->state == STATE_IDLE)
244 		da9034_event_handler(touch, EVENT_PEN_DOWN);
245 
246 	return 0;
247 }
248 
249 static int da9034_touch_open(struct input_dev *dev)
250 {
251 	struct da9034_touch *touch = input_get_drvdata(dev);
252 	int ret;
253 
254 	ret = da903x_register_notifier(touch->da9034_dev, &touch->notifier,
255 			DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY);
256 	if (ret)
257 		return -EBUSY;
258 
259 	/* Enable ADC LDO */
260 	ret = da903x_set_bits(touch->da9034_dev,
261 			DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN);
262 	if (ret)
263 		return ret;
264 
265 	/* TSI_DELAY: 3 slots, TSI_SKIP: 3 slots */
266 	ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL1, 0x1b);
267 	if (ret)
268 		return ret;
269 
270 	ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL2, 0x00);
271 	if (ret)
272 		return ret;
273 
274 	touch->state = STATE_IDLE;
275 	detect_pen_down(touch, 1);
276 
277 	return 0;
278 }
279 
280 static void da9034_touch_close(struct input_dev *dev)
281 {
282 	struct da9034_touch *touch = input_get_drvdata(dev);
283 
284 	da903x_unregister_notifier(touch->da9034_dev, &touch->notifier,
285 			DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY);
286 
287 	cancel_delayed_work_sync(&touch->tsi_work);
288 
289 	touch->state = STATE_IDLE;
290 	stop_tsi(touch);
291 	detect_pen_down(touch, 0);
292 
293 	/* Disable ADC LDO */
294 	da903x_clr_bits(touch->da9034_dev,
295 			DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN);
296 }
297 
298 
299 static int da9034_touch_probe(struct platform_device *pdev)
300 {
301 	struct da9034_touch_pdata *pdata = dev_get_platdata(&pdev->dev);
302 	struct da9034_touch *touch;
303 	struct input_dev *input_dev;
304 	int error;
305 
306 	touch = devm_kzalloc(&pdev->dev, sizeof(struct da9034_touch),
307 			     GFP_KERNEL);
308 	if (!touch) {
309 		dev_err(&pdev->dev, "failed to allocate driver data\n");
310 		return -ENOMEM;
311 	}
312 
313 	touch->da9034_dev = pdev->dev.parent;
314 
315 	if (pdata) {
316 		touch->interval_ms	= pdata->interval_ms;
317 		touch->x_inverted	= pdata->x_inverted;
318 		touch->y_inverted	= pdata->y_inverted;
319 	} else {
320 		/* fallback into default */
321 		touch->interval_ms	= 10;
322 	}
323 
324 	INIT_DELAYED_WORK(&touch->tsi_work, da9034_tsi_work);
325 	touch->notifier.notifier_call = da9034_touch_notifier;
326 
327 	input_dev = devm_input_allocate_device(&pdev->dev);
328 	if (!input_dev) {
329 		dev_err(&pdev->dev, "failed to allocate input device\n");
330 		return -ENOMEM;
331 	}
332 
333 	input_dev->name		= pdev->name;
334 	input_dev->open		= da9034_touch_open;
335 	input_dev->close	= da9034_touch_close;
336 	input_dev->dev.parent	= &pdev->dev;
337 
338 	__set_bit(EV_ABS, input_dev->evbit);
339 	__set_bit(ABS_X, input_dev->absbit);
340 	__set_bit(ABS_Y, input_dev->absbit);
341 	input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);
342 	input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0);
343 
344 	__set_bit(EV_KEY, input_dev->evbit);
345 	__set_bit(BTN_TOUCH, input_dev->keybit);
346 
347 	touch->input_dev = input_dev;
348 	input_set_drvdata(input_dev, touch);
349 
350 	error = input_register_device(input_dev);
351 	if (error)
352 		return error;
353 
354 	return 0;
355 }
356 
357 static struct platform_driver da9034_touch_driver = {
358 	.driver	= {
359 		.name	= "da9034-touch",
360 	},
361 	.probe		= da9034_touch_probe,
362 };
363 module_platform_driver(da9034_touch_driver);
364 
365 MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9034");
366 MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>, Bin Yang <bin.yang@marvell.com>");
367 MODULE_LICENSE("GPL");
368 MODULE_ALIAS("platform:da9034-touch");
369