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