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
;
182 da9034_event_handler(touch
,
183 is_pen_down(touch
) ? EVENT_PEN_DOWN
:
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
;
195 if (event
== EVENT_PEN_UP
) {
196 report_pen_up(touch
);
197 touch
->state
= STATE_IDLE
;
202 if (event
!= EVENT_TIMEDOUT
)
205 if (is_pen_down(touch
)) {
207 touch
->state
= STATE_BUSY
;
209 report_pen_up(touch
);
210 touch
->state
= STATE_IDLE
;
217 touch
->state
= STATE_IDLE
;
219 detect_pen_down(touch
, 1);
222 static void da9034_tsi_work(struct work_struct
*work
)
224 struct da9034_touch
*touch
=
225 container_of(work
, struct da9034_touch
, tsi_work
.work
);
227 da9034_event_handler(touch
, EVENT_TIMEDOUT
);
230 static int da9034_touch_notifier(struct notifier_block
*nb
,
231 unsigned long event
, void *data
)
233 struct da9034_touch
*touch
=
234 container_of(nb
, struct da9034_touch
, notifier
);
236 if (event
& DA9034_EVENT_TSI_READY
)
237 da9034_event_handler(touch
, EVENT_TSI_READY
);
239 if ((event
& DA9034_EVENT_PEN_DOWN
) && touch
->state
== STATE_IDLE
)
240 da9034_event_handler(touch
, EVENT_PEN_DOWN
);
245 static int da9034_touch_open(struct input_dev
*dev
)
247 struct da9034_touch
*touch
= input_get_drvdata(dev
);
250 ret
= da903x_register_notifier(touch
->da9034_dev
, &touch
->notifier
,
251 DA9034_EVENT_PEN_DOWN
| DA9034_EVENT_TSI_READY
);
256 ret
= da903x_set_bits(touch
->da9034_dev
,
257 DA9034_MANUAL_CTRL
, DA9034_LDO_ADC_EN
);
261 /* TSI_DELAY: 3 slots, TSI_SKIP: 3 slots */
262 ret
= da903x_write(touch
->da9034_dev
, DA9034_TSI_CTRL1
, 0x1b);
266 ret
= da903x_write(touch
->da9034_dev
, DA9034_TSI_CTRL2
, 0x00);
270 touch
->state
= STATE_IDLE
;
271 detect_pen_down(touch
, 1);
276 static void da9034_touch_close(struct input_dev
*dev
)
278 struct da9034_touch
*touch
= input_get_drvdata(dev
);
280 da903x_unregister_notifier(touch
->da9034_dev
, &touch
->notifier
,
281 DA9034_EVENT_PEN_DOWN
| DA9034_EVENT_TSI_READY
);
283 cancel_delayed_work_sync(&touch
->tsi_work
);
285 touch
->state
= STATE_IDLE
;
287 detect_pen_down(touch
, 0);
289 /* Disable ADC LDO */
290 da903x_clr_bits(touch
->da9034_dev
,
291 DA9034_MANUAL_CTRL
, DA9034_LDO_ADC_EN
);
295 static int __devinit
da9034_touch_probe(struct platform_device
*pdev
)
297 struct da9034_touch_pdata
*pdata
= pdev
->dev
.platform_data
;
298 struct da9034_touch
*touch
;
299 struct input_dev
*input_dev
;
302 touch
= kzalloc(sizeof(struct da9034_touch
), GFP_KERNEL
);
304 dev_err(&pdev
->dev
, "failed to allocate driver data\n");
308 touch
->da9034_dev
= pdev
->dev
.parent
;
311 touch
->interval_ms
= pdata
->interval_ms
;
312 touch
->x_inverted
= pdata
->x_inverted
;
313 touch
->y_inverted
= pdata
->y_inverted
;
315 /* fallback into default */
316 touch
->interval_ms
= 10;
318 INIT_DELAYED_WORK(&touch
->tsi_work
, da9034_tsi_work
);
319 touch
->notifier
.notifier_call
= da9034_touch_notifier
;
321 input_dev
= input_allocate_device();
323 dev_err(&pdev
->dev
, "failed to allocate input device\n");
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
;
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);
339 __set_bit(EV_KEY
, input_dev
->evbit
);
340 __set_bit(BTN_TOUCH
, input_dev
->keybit
);
342 touch
->input_dev
= input_dev
;
343 input_set_drvdata(input_dev
, touch
);
345 ret
= input_register_device(input_dev
);
349 platform_set_drvdata(pdev
, touch
);
353 input_free_device(input_dev
);
359 static int __devexit
da9034_touch_remove(struct platform_device
*pdev
)
361 struct da9034_touch
*touch
= platform_get_drvdata(pdev
);
363 input_unregister_device(touch
->input_dev
);
369 static struct platform_driver da9034_touch_driver
= {
371 .name
= "da9034-touch",
372 .owner
= THIS_MODULE
,
374 .probe
= da9034_touch_probe
,
375 .remove
= __devexit_p(da9034_touch_remove
),
378 static int __init
da9034_touch_init(void)
380 return platform_driver_register(&da9034_touch_driver
);
382 module_init(da9034_touch_init
);
384 static void __exit
da9034_touch_exit(void)
386 platform_driver_unregister(&da9034_touch_driver
);
388 module_exit(da9034_touch_exit
);
390 MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9034");
391 MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>, Bin Yang <bin.yang@marvell.com>");
392 MODULE_LICENSE("GPL");
393 MODULE_ALIAS("platform:da9034-touch");