2 * Touchscreen driver for Dialog Semiconductor DA9034
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>
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.
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>
24 #define DA9034_MANUAL_CTRL 0x50
25 #define DA9034_LDO_ADC_EN (1 << 4)
27 #define DA9034_AUTO_CTRL1 0x51
29 #define DA9034_AUTO_CTRL2 0x52
30 #define DA9034_AUTO_TSI_EN (1 << 3)
31 #define DA9034_PEN_DETECT (1 << 4)
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
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 */
54 struct device
*da9034_dev
;
55 struct input_dev
*input_dev
;
57 struct delayed_work tsi_work
;
58 struct notifier_block notifier
;
70 static inline int is_pen_down(struct da9034_touch
*touch
)
72 return da903x_query_status(touch
->da9034_dev
, DA9034_STATUS_PEN_DOWN
);
75 static inline int detect_pen_down(struct da9034_touch
*touch
, int on
)
78 return da903x_set_bits(touch
->da9034_dev
,
79 DA9034_AUTO_CTRL2
, DA9034_PEN_DETECT
);
81 return da903x_clr_bits(touch
->da9034_dev
,
82 DA9034_AUTO_CTRL2
, DA9034_PEN_DETECT
);
85 static int read_tsi(struct da9034_touch
*touch
)
90 ret
= da903x_read(touch
->da9034_dev
, DA9034_TSI_X_MSB
, &_x
);
94 ret
= da903x_read(touch
->da9034_dev
, DA9034_TSI_Y_MSB
, &_y
);
98 ret
= da903x_read(touch
->da9034_dev
, DA9034_TSI_XY_LSB
, &_v
);
102 touch
->last_x
= ((_x
<< 2) & 0x3fc) | (_v
& 0x3);
103 touch
->last_y
= ((_y
<< 2) & 0x3fc) | ((_v
& 0xc) >> 2);
108 static inline int start_tsi(struct da9034_touch
*touch
)
110 return da903x_set_bits(touch
->da9034_dev
,
111 DA9034_AUTO_CTRL2
, DA9034_AUTO_TSI_EN
);
114 static inline int stop_tsi(struct da9034_touch
*touch
)
116 return da903x_clr_bits(touch
->da9034_dev
,
117 DA9034_AUTO_CTRL2
, DA9034_AUTO_TSI_EN
);
120 static inline void report_pen_down(struct da9034_touch
*touch
)
122 int x
= touch
->last_x
;
123 int y
= touch
->last_y
;
126 if (touch
->x_inverted
)
129 if (touch
->y_inverted
)
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);
136 input_sync(touch
->input_dev
);
139 static inline void report_pen_up(struct da9034_touch
*touch
)
141 input_report_key(touch
->input_dev
, BTN_TOUCH
, 0);
142 input_sync(touch
->input_dev
);
145 static void da9034_event_handler(struct da9034_touch
*touch
, int event
)
149 switch (touch
->state
) {
151 if (event
!= EVENT_PEN_DOWN
)
154 /* Enable auto measurement of the TSI, this will
155 * automatically disable pen down detection
157 err
= start_tsi(touch
);
161 touch
->state
= STATE_BUSY
;
165 if (event
!= EVENT_TSI_READY
)
168 err
= read_tsi(touch
);
172 /* Disable auto measurement of the TSI, so that
173 * pen down status will be available
175 err
= stop_tsi(touch
);
179 touch
->state
= STATE_STOP
;
181 /* FIXME: PEN_{UP/DOWN} events are expected to be
182 * available by stopping TSI, but this is found not
183 * always true, delay and simulate such an event
184 * here is more reliable
187 da9034_event_handler(touch
,
188 is_pen_down(touch
) ? EVENT_PEN_DOWN
:
193 if (event
== EVENT_PEN_DOWN
) {
194 report_pen_down(touch
);
195 schedule_delayed_work(&touch
->tsi_work
,
196 msecs_to_jiffies(touch
->interval_ms
));
197 touch
->state
= STATE_WAIT
;
200 if (event
== EVENT_PEN_UP
) {
201 report_pen_up(touch
);
202 touch
->state
= STATE_IDLE
;
207 if (event
!= EVENT_TIMEDOUT
)
210 if (is_pen_down(touch
)) {
212 touch
->state
= STATE_BUSY
;
214 report_pen_up(touch
);
215 touch
->state
= STATE_IDLE
;
222 touch
->state
= STATE_IDLE
;
224 detect_pen_down(touch
, 1);
227 static void da9034_tsi_work(struct work_struct
*work
)
229 struct da9034_touch
*touch
=
230 container_of(work
, struct da9034_touch
, tsi_work
.work
);
232 da9034_event_handler(touch
, EVENT_TIMEDOUT
);
235 static int da9034_touch_notifier(struct notifier_block
*nb
,
236 unsigned long event
, void *data
)
238 struct da9034_touch
*touch
=
239 container_of(nb
, struct da9034_touch
, notifier
);
241 if (event
& DA9034_EVENT_TSI_READY
)
242 da9034_event_handler(touch
, EVENT_TSI_READY
);
244 if ((event
& DA9034_EVENT_PEN_DOWN
) && touch
->state
== STATE_IDLE
)
245 da9034_event_handler(touch
, EVENT_PEN_DOWN
);
250 static int da9034_touch_open(struct input_dev
*dev
)
252 struct da9034_touch
*touch
= input_get_drvdata(dev
);
255 ret
= da903x_register_notifier(touch
->da9034_dev
, &touch
->notifier
,
256 DA9034_EVENT_PEN_DOWN
| DA9034_EVENT_TSI_READY
);
261 ret
= da903x_set_bits(touch
->da9034_dev
,
262 DA9034_MANUAL_CTRL
, DA9034_LDO_ADC_EN
);
266 /* TSI_DELAY: 3 slots, TSI_SKIP: 3 slots */
267 ret
= da903x_write(touch
->da9034_dev
, DA9034_TSI_CTRL1
, 0x1b);
271 ret
= da903x_write(touch
->da9034_dev
, DA9034_TSI_CTRL2
, 0x00);
275 touch
->state
= STATE_IDLE
;
276 detect_pen_down(touch
, 1);
281 static void da9034_touch_close(struct input_dev
*dev
)
283 struct da9034_touch
*touch
= input_get_drvdata(dev
);
285 da903x_unregister_notifier(touch
->da9034_dev
, &touch
->notifier
,
286 DA9034_EVENT_PEN_DOWN
| DA9034_EVENT_TSI_READY
);
288 cancel_delayed_work_sync(&touch
->tsi_work
);
290 touch
->state
= STATE_IDLE
;
292 detect_pen_down(touch
, 0);
294 /* Disable ADC LDO */
295 da903x_clr_bits(touch
->da9034_dev
,
296 DA9034_MANUAL_CTRL
, DA9034_LDO_ADC_EN
);
300 static int __devinit
da9034_touch_probe(struct platform_device
*pdev
)
302 struct da9034_touch_pdata
*pdata
= pdev
->dev
.platform_data
;
303 struct da9034_touch
*touch
;
304 struct input_dev
*input_dev
;
307 touch
= kzalloc(sizeof(struct da9034_touch
), GFP_KERNEL
);
309 dev_err(&pdev
->dev
, "failed to allocate driver data\n");
313 touch
->da9034_dev
= pdev
->dev
.parent
;
316 touch
->interval_ms
= pdata
->interval_ms
;
317 touch
->x_inverted
= pdata
->x_inverted
;
318 touch
->y_inverted
= pdata
->y_inverted
;
320 /* fallback into default */
321 touch
->interval_ms
= 10;
323 INIT_DELAYED_WORK(&touch
->tsi_work
, da9034_tsi_work
);
324 touch
->notifier
.notifier_call
= da9034_touch_notifier
;
326 input_dev
= input_allocate_device();
328 dev_err(&pdev
->dev
, "failed to allocate input device\n");
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
;
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);
344 __set_bit(EV_KEY
, input_dev
->evbit
);
345 __set_bit(BTN_TOUCH
, input_dev
->keybit
);
347 touch
->input_dev
= input_dev
;
348 input_set_drvdata(input_dev
, touch
);
350 ret
= input_register_device(input_dev
);
354 platform_set_drvdata(pdev
, touch
);
358 input_free_device(input_dev
);
364 static int __devexit
da9034_touch_remove(struct platform_device
*pdev
)
366 struct da9034_touch
*touch
= platform_get_drvdata(pdev
);
368 input_unregister_device(touch
->input_dev
);
374 static struct platform_driver da9034_touch_driver
= {
376 .name
= "da9034-touch",
377 .owner
= THIS_MODULE
,
379 .probe
= da9034_touch_probe
,
380 .remove
= __devexit_p(da9034_touch_remove
),
383 static int __init
da9034_touch_init(void)
385 return platform_driver_register(&da9034_touch_driver
);
387 module_init(da9034_touch_init
);
389 static void __exit
da9034_touch_exit(void)
391 platform_driver_unregister(&da9034_touch_driver
);
393 module_exit(da9034_touch_exit
);
395 MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9034");
396 MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>, Bin Yang <bin.yang@marvell.com>");
397 MODULE_LICENSE("GPL");
398 MODULE_ALIAS("platform:da9034-touch");