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>
23 #define DA9034_MANUAL_CTRL 0x50
24 #define DA9034_LDO_ADC_EN (1 << 4)
26 #define DA9034_AUTO_CTRL1 0x51
28 #define DA9034_AUTO_CTRL2 0x52
29 #define DA9034_AUTO_TSI_EN (1 << 3)
30 #define DA9034_PEN_DETECT (1 << 4)
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
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 */
53 struct device
*da9034_dev
;
54 struct input_dev
*input_dev
;
56 struct delayed_work tsi_work
;
57 struct notifier_block notifier
;
69 static inline int is_pen_down(struct da9034_touch
*touch
)
71 return da903x_query_status(touch
->da9034_dev
, DA9034_STATUS_PEN_DOWN
);
74 static inline int detect_pen_down(struct da9034_touch
*touch
, int on
)
77 return da903x_set_bits(touch
->da9034_dev
,
78 DA9034_AUTO_CTRL2
, DA9034_PEN_DETECT
);
80 return da903x_clr_bits(touch
->da9034_dev
,
81 DA9034_AUTO_CTRL2
, DA9034_PEN_DETECT
);
84 static int read_tsi(struct da9034_touch
*touch
)
89 ret
= da903x_read(touch
->da9034_dev
, DA9034_TSI_X_MSB
, &_x
);
93 ret
= da903x_read(touch
->da9034_dev
, DA9034_TSI_Y_MSB
, &_y
);
97 ret
= da903x_read(touch
->da9034_dev
, DA9034_TSI_XY_LSB
, &_v
);
101 touch
->last_x
= ((_x
<< 2) & 0x3fc) | (_v
& 0x3);
102 touch
->last_y
= ((_y
<< 2) & 0x3fc) | ((_v
& 0xc) >> 2);
107 static inline int start_tsi(struct da9034_touch
*touch
)
109 return da903x_set_bits(touch
->da9034_dev
,
110 DA9034_AUTO_CTRL2
, DA9034_AUTO_TSI_EN
);
113 static inline int stop_tsi(struct da9034_touch
*touch
)
115 return da903x_clr_bits(touch
->da9034_dev
,
116 DA9034_AUTO_CTRL2
, DA9034_AUTO_TSI_EN
);
119 static inline void report_pen_down(struct da9034_touch
*touch
)
121 int x
= touch
->last_x
;
122 int y
= touch
->last_y
;
125 if (touch
->x_inverted
)
128 if (touch
->y_inverted
)
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);
135 input_sync(touch
->input_dev
);
138 static inline void report_pen_up(struct da9034_touch
*touch
)
140 input_report_key(touch
->input_dev
, BTN_TOUCH
, 0);
141 input_sync(touch
->input_dev
);
144 static void da9034_event_handler(struct da9034_touch
*touch
, int event
)
148 switch (touch
->state
) {
150 if (event
!= EVENT_PEN_DOWN
)
153 /* Enable auto measurement of the TSI, this will
154 * automatically disable pen down detection
156 err
= start_tsi(touch
);
160 touch
->state
= STATE_BUSY
;
164 if (event
!= EVENT_TSI_READY
)
167 err
= read_tsi(touch
);
171 /* Disable auto measurement of the TSI, so that
172 * pen down status will be available
174 err
= stop_tsi(touch
);
178 touch
->state
= STATE_STOP
;
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
186 da9034_event_handler(touch
,
187 is_pen_down(touch
) ? EVENT_PEN_DOWN
:
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
;
199 if (event
== EVENT_PEN_UP
) {
200 report_pen_up(touch
);
201 touch
->state
= STATE_IDLE
;
206 if (event
!= EVENT_TIMEDOUT
)
209 if (is_pen_down(touch
)) {
211 touch
->state
= STATE_BUSY
;
213 report_pen_up(touch
);
214 touch
->state
= STATE_IDLE
;
221 touch
->state
= STATE_IDLE
;
223 detect_pen_down(touch
, 1);
226 static void da9034_tsi_work(struct work_struct
*work
)
228 struct da9034_touch
*touch
=
229 container_of(work
, struct da9034_touch
, tsi_work
.work
);
231 da9034_event_handler(touch
, EVENT_TIMEDOUT
);
234 static int da9034_touch_notifier(struct notifier_block
*nb
,
235 unsigned long event
, void *data
)
237 struct da9034_touch
*touch
=
238 container_of(nb
, struct da9034_touch
, notifier
);
240 if (event
& DA9034_EVENT_TSI_READY
)
241 da9034_event_handler(touch
, EVENT_TSI_READY
);
243 if ((event
& DA9034_EVENT_PEN_DOWN
) && touch
->state
== STATE_IDLE
)
244 da9034_event_handler(touch
, EVENT_PEN_DOWN
);
249 static int da9034_touch_open(struct input_dev
*dev
)
251 struct da9034_touch
*touch
= input_get_drvdata(dev
);
254 ret
= da903x_register_notifier(touch
->da9034_dev
, &touch
->notifier
,
255 DA9034_EVENT_PEN_DOWN
| DA9034_EVENT_TSI_READY
);
260 ret
= da903x_set_bits(touch
->da9034_dev
,
261 DA9034_MANUAL_CTRL
, DA9034_LDO_ADC_EN
);
265 /* TSI_DELAY: 3 slots, TSI_SKIP: 3 slots */
266 ret
= da903x_write(touch
->da9034_dev
, DA9034_TSI_CTRL1
, 0x1b);
270 ret
= da903x_write(touch
->da9034_dev
, DA9034_TSI_CTRL2
, 0x00);
274 touch
->state
= STATE_IDLE
;
275 detect_pen_down(touch
, 1);
280 static void da9034_touch_close(struct input_dev
*dev
)
282 struct da9034_touch
*touch
= input_get_drvdata(dev
);
284 da903x_unregister_notifier(touch
->da9034_dev
, &touch
->notifier
,
285 DA9034_EVENT_PEN_DOWN
| DA9034_EVENT_TSI_READY
);
287 cancel_delayed_work_sync(&touch
->tsi_work
);
289 touch
->state
= STATE_IDLE
;
291 detect_pen_down(touch
, 0);
293 /* Disable ADC LDO */
294 da903x_clr_bits(touch
->da9034_dev
,
295 DA9034_MANUAL_CTRL
, DA9034_LDO_ADC_EN
);
299 static int __devinit
da9034_touch_probe(struct platform_device
*pdev
)
301 struct da9034_touch_pdata
*pdata
= pdev
->dev
.platform_data
;
302 struct da9034_touch
*touch
;
303 struct input_dev
*input_dev
;
306 touch
= kzalloc(sizeof(struct da9034_touch
), GFP_KERNEL
);
308 dev_err(&pdev
->dev
, "failed to allocate driver data\n");
312 touch
->da9034_dev
= pdev
->dev
.parent
;
315 touch
->interval_ms
= pdata
->interval_ms
;
316 touch
->x_inverted
= pdata
->x_inverted
;
317 touch
->y_inverted
= pdata
->y_inverted
;
319 /* fallback into default */
320 touch
->interval_ms
= 10;
322 INIT_DELAYED_WORK(&touch
->tsi_work
, da9034_tsi_work
);
323 touch
->notifier
.notifier_call
= da9034_touch_notifier
;
325 input_dev
= input_allocate_device();
327 dev_err(&pdev
->dev
, "failed to allocate input device\n");
332 input_dev
->name
= pdev
->name
;
333 input_dev
->open
= da9034_touch_open
;
334 input_dev
->close
= da9034_touch_close
;
335 input_dev
->dev
.parent
= &pdev
->dev
;
337 __set_bit(EV_ABS
, input_dev
->evbit
);
338 __set_bit(ABS_X
, input_dev
->absbit
);
339 __set_bit(ABS_Y
, input_dev
->absbit
);
340 input_set_abs_params(input_dev
, ABS_X
, 0, 1023, 0, 0);
341 input_set_abs_params(input_dev
, ABS_Y
, 0, 1023, 0, 0);
343 __set_bit(EV_KEY
, input_dev
->evbit
);
344 __set_bit(BTN_TOUCH
, input_dev
->keybit
);
346 touch
->input_dev
= input_dev
;
347 input_set_drvdata(input_dev
, touch
);
349 ret
= input_register_device(input_dev
);
353 platform_set_drvdata(pdev
, touch
);
357 input_free_device(input_dev
);
363 static int __devexit
da9034_touch_remove(struct platform_device
*pdev
)
365 struct da9034_touch
*touch
= platform_get_drvdata(pdev
);
367 input_unregister_device(touch
->input_dev
);
373 static struct platform_driver da9034_touch_driver
= {
375 .name
= "da9034-touch",
376 .owner
= THIS_MODULE
,
378 .probe
= da9034_touch_probe
,
379 .remove
= __devexit_p(da9034_touch_remove
),
382 static int __init
da9034_touch_init(void)
384 return platform_driver_register(&da9034_touch_driver
);
386 module_init(da9034_touch_init
);
388 static void __exit
da9034_touch_exit(void)
390 platform_driver_unregister(&da9034_touch_driver
);
392 module_exit(da9034_touch_exit
);
394 MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9034");
395 MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>, Bin Yang <bin.yang@marvell.com>");
396 MODULE_LICENSE("GPL");
397 MODULE_ALIAS("platform:da9034-touch");