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