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