aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/input/touchscreen/Kconfig17
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/da9052_tsi.c370
3 files changed, 388 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 229135c1dfe..f67657b2fd5 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -187,6 +187,23 @@ config TOUCHSCREEN_DA9034
187 Say Y here to enable the support for the touchscreen found 187 Say Y here to enable the support for the touchscreen found
188 on Dialog Semiconductor DA9034 PMIC. 188 on Dialog Semiconductor DA9034 PMIC.
189 189
190 If unsure, say N.
191
192 To compile this driver as a module, choose M here: the
193 module will be called da9034-ts.
194
195config TOUCHSCREEN_DA9052
196 tristate "Dialog DA9052/DA9053 TSI"
197 depends on PMIC_DA9052
198 help
199 Say Y here to support the touchscreen found on Dialog Semiconductor
200 DA9052-BC and DA9053-AA/Bx PMICs.
201
202 If unsure, say N.
203
204 To compile this driver as a module, choose M here: the
205 module will be called da9052_tsi.
206
190config TOUCHSCREEN_DYNAPRO 207config TOUCHSCREEN_DYNAPRO
191 tristate "Dynapro serial touchscreen" 208 tristate "Dynapro serial touchscreen"
192 select SERIO 209 select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 7cca7ddb464..eb8bfe1c1a4 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
22obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o 22obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o
23obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o 23obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o
24obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o 24obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o
25obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o
25obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o 26obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
26obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o 27obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o
27obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o 28obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
diff --git a/drivers/input/touchscreen/da9052_tsi.c b/drivers/input/touchscreen/da9052_tsi.c
new file mode 100644
index 00000000000..e8df341090c
--- /dev/null
+++ b/drivers/input/touchscreen/da9052_tsi.c
@@ -0,0 +1,370 @@
1/*
2 * TSI driver for Dialog DA9052
3 *
4 * Copyright(c) 2012 Dialog Semiconductor Ltd.
5 *
6 * Author: David Dajun Chen <dchen@diasemi.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 */
14#include <linux/module.h>
15#include <linux/input.h>
16#include <linux/delay.h>
17#include <linux/platform_device.h>
18#include <linux/interrupt.h>
19
20#include <linux/mfd/da9052/reg.h>
21#include <linux/mfd/da9052/da9052.h>
22
23#define TSI_PEN_DOWN_STATUS 0x40
24
25struct da9052_tsi {
26 struct da9052 *da9052;
27 struct input_dev *dev;
28 struct delayed_work ts_pen_work;
29 struct mutex mutex;
30 unsigned int irq_pendwn;
31 unsigned int irq_datardy;
32 bool stopped;
33 bool adc_on;
34};
35
36static void da9052_ts_adc_toggle(struct da9052_tsi *tsi, bool on)
37{
38 da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 0, on);
39 tsi->adc_on = on;
40}
41
42static irqreturn_t da9052_ts_pendwn_irq(int irq, void *data)
43{
44 struct da9052_tsi *tsi = data;
45
46 if (!tsi->stopped) {
47 /* Mask PEN_DOWN event and unmask TSI_READY event */
48 disable_irq_nosync(tsi->irq_pendwn);
49 enable_irq(tsi->irq_datardy);
50
51 da9052_ts_adc_toggle(tsi, true);
52
53 schedule_delayed_work(&tsi->ts_pen_work, HZ / 50);
54 }
55
56 return IRQ_HANDLED;
57}
58
59static void da9052_ts_read(struct da9052_tsi *tsi)
60{
61 struct input_dev *input = tsi->dev;
62 int ret;
63 u16 x, y, z;
64 u8 v;
65
66 ret = da9052_reg_read(tsi->da9052, DA9052_TSI_X_MSB_REG);
67 if (ret < 0)
68 return;
69
70 x = (u16) ret;
71
72 ret = da9052_reg_read(tsi->da9052, DA9052_TSI_Y_MSB_REG);
73 if (ret < 0)
74 return;
75
76 y = (u16) ret;
77
78 ret = da9052_reg_read(tsi->da9052, DA9052_TSI_Z_MSB_REG);
79 if (ret < 0)
80 return;
81
82 z = (u16) ret;
83
84 ret = da9052_reg_read(tsi->da9052, DA9052_TSI_LSB_REG);
85 if (ret < 0)
86 return;
87
88 v = (u8) ret;
89
90 x = ((x << 2) & 0x3fc) | (v & 0x3);
91 y = ((y << 2) & 0x3fc) | ((v & 0xc) >> 2);
92 z = ((z << 2) & 0x3fc) | ((v & 0x30) >> 4);
93
94 input_report_key(input, BTN_TOUCH, 1);
95 input_report_abs(input, ABS_X, x);
96 input_report_abs(input, ABS_Y, y);
97 input_report_abs(input, ABS_PRESSURE, z);
98 input_sync(input);
99}
100
101static irqreturn_t da9052_ts_datardy_irq(int irq, void *data)
102{
103 struct da9052_tsi *tsi = data;
104
105 da9052_ts_read(tsi);
106
107 return IRQ_HANDLED;
108}
109
110static void da9052_ts_pen_work(struct work_struct *work)
111{
112 struct da9052_tsi *tsi = container_of(work, struct da9052_tsi,
113 ts_pen_work.work);
114 if (!tsi->stopped) {
115 int ret = da9052_reg_read(tsi->da9052, DA9052_TSI_LSB_REG);
116 if (ret < 0 || (ret & TSI_PEN_DOWN_STATUS)) {
117 /* Pen is still DOWN (or read error) */
118 schedule_delayed_work(&tsi->ts_pen_work, HZ / 50);
119 } else {
120 struct input_dev *input = tsi->dev;
121
122 /* Pen UP */
123 da9052_ts_adc_toggle(tsi, false);
124
125 /* Report Pen UP */
126 input_report_key(input, BTN_TOUCH, 0);
127 input_report_abs(input, ABS_PRESSURE, 0);
128 input_sync(input);
129
130 /*
131 * FIXME: Fixes the unhandled irq issue when quick
132 * pen down and pen up events occurs
133 */
134 ret = da9052_reg_update(tsi->da9052,
135 DA9052_EVENT_B_REG, 0xC0, 0xC0);
136 if (ret < 0)
137 return;
138
139 /* Mask TSI_READY event and unmask PEN_DOWN event */
140 disable_irq(tsi->irq_datardy);
141 enable_irq(tsi->irq_pendwn);
142 }
143 }
144}
145
146static int __devinit da9052_ts_configure_gpio(struct da9052 *da9052)
147{
148 int error;
149
150 error = da9052_reg_update(da9052, DA9052_GPIO_2_3_REG, 0x30, 0);
151 if (error < 0)
152 return error;
153
154 error = da9052_reg_update(da9052, DA9052_GPIO_4_5_REG, 0x33, 0);
155 if (error < 0)
156 return error;
157
158 error = da9052_reg_update(da9052, DA9052_GPIO_6_7_REG, 0x33, 0);
159 if (error < 0)
160 return error;
161
162 return 0;
163}
164
165static int __devinit da9052_configure_tsi(struct da9052_tsi *tsi)
166{
167 int error;
168
169 error = da9052_ts_configure_gpio(tsi->da9052);
170 if (error)
171 return error;
172
173 /* Measure TSI sample every 1ms */
174 error = da9052_reg_update(tsi->da9052, DA9052_ADC_CONT_REG,
175 1 << 6, 1 << 6);
176 if (error < 0)
177 return error;
178
179 /* TSI_DELAY: 3 slots, TSI_SKIP: 0 slots, TSI_MODE: XYZP */
180 error = da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 0xFC, 0xC0);
181 if (error < 0)
182 return error;
183
184 /* Supply TSIRef through LD09 */
185 error = da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x59);
186 if (error < 0)
187 return error;
188
189 return 0;
190}
191
192static int da9052_ts_input_open(struct input_dev *input_dev)
193{
194 struct da9052_tsi *tsi = input_get_drvdata(input_dev);
195
196 tsi->stopped = false;
197 mb();
198
199 /* Unmask PEN_DOWN event */
200 enable_irq(tsi->irq_pendwn);
201
202 /* Enable Pen Detect Circuit */
203 return da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG,
204 1 << 1, 1 << 1);
205}
206
207static void da9052_ts_input_close(struct input_dev *input_dev)
208{
209 struct da9052_tsi *tsi = input_get_drvdata(input_dev);
210
211 tsi->stopped = true;
212 mb();
213 disable_irq(tsi->irq_pendwn);
214 cancel_delayed_work_sync(&tsi->ts_pen_work);
215
216 if (tsi->adc_on) {
217 disable_irq(tsi->irq_datardy);
218 da9052_ts_adc_toggle(tsi, false);
219
220 /*
221 * If ADC was on that means that pendwn IRQ was disabled
222 * twice and we need to enable it to keep enable/disable
223 * counter balanced. IRQ is still off though.
224 */
225 enable_irq(tsi->irq_pendwn);
226 }
227
228 /* Disable Pen Detect Circuit */
229 da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 1, 0);
230}
231
232static int __devinit da9052_ts_probe(struct platform_device *pdev)
233{
234 struct da9052 *da9052;
235 struct da9052_tsi *tsi;
236 struct input_dev *input_dev;
237 int irq_pendwn;
238 int irq_datardy;
239 int error;
240
241 da9052 = dev_get_drvdata(pdev->dev.parent);
242 if (!da9052)
243 return -EINVAL;
244
245 irq_pendwn = platform_get_irq_byname(pdev, "PENDWN");
246 irq_datardy = platform_get_irq_byname(pdev, "TSIRDY");
247 if (irq_pendwn < 0 || irq_datardy < 0) {
248 dev_err(da9052->dev, "Unable to determine device interrupts\n");
249 return -ENXIO;
250 }
251
252 tsi = kzalloc(sizeof(struct da9052_tsi), GFP_KERNEL);
253 input_dev = input_allocate_device();
254 if (!tsi || !input_dev) {
255 error = -ENOMEM;
256 goto err_free_mem;
257 }
258
259 tsi->da9052 = da9052;
260 tsi->dev = input_dev;
261 tsi->irq_pendwn = da9052->irq_base + irq_pendwn;
262 tsi->irq_datardy = da9052->irq_base + irq_datardy;
263 tsi->stopped = true;
264 INIT_DELAYED_WORK(&tsi->ts_pen_work, da9052_ts_pen_work);
265
266 input_dev->id.version = 0x0101;
267 input_dev->id.vendor = 0x15B6;
268 input_dev->id.product = 0x9052;
269 input_dev->name = "Dialog DA9052 TouchScreen Driver";
270 input_dev->dev.parent = &pdev->dev;
271 input_dev->open = da9052_ts_input_open;
272 input_dev->close = da9052_ts_input_close;
273
274 __set_bit(EV_ABS, input_dev->evbit);
275 __set_bit(EV_KEY, input_dev->evbit);
276 __set_bit(BTN_TOUCH, input_dev->keybit);
277
278 input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);
279 input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0);
280 input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1023, 0, 0);
281
282 input_set_drvdata(input_dev, tsi);
283
284 /* Disable Pen Detect Circuit */
285 da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 1, 0);
286
287 /* Disable ADC */
288 da9052_ts_adc_toggle(tsi, false);
289
290 error = request_threaded_irq(tsi->irq_pendwn,
291 NULL, da9052_ts_pendwn_irq,
292 IRQF_TRIGGER_LOW | IRQF_ONESHOT,
293 "PENDWN", tsi);
294 if (error) {
295 dev_err(tsi->da9052->dev,
296 "Failed to register PENDWN IRQ %d, error = %d\n",
297 tsi->irq_pendwn, error);
298 goto err_free_mem;
299 }
300
301 error = request_threaded_irq(tsi->irq_datardy,
302 NULL, da9052_ts_datardy_irq,
303 IRQF_TRIGGER_LOW | IRQF_ONESHOT,
304 "TSIRDY", tsi);
305 if (error) {
306 dev_err(tsi->da9052->dev,
307 "Failed to register TSIRDY IRQ %d, error = %d\n",
308 tsi->irq_datardy, error);
309 goto err_free_pendwn_irq;
310 }
311
312 /* Mask PEN_DOWN and TSI_READY events */
313 disable_irq(tsi->irq_pendwn);
314 disable_irq(tsi->irq_datardy);
315
316 error = da9052_configure_tsi(tsi);
317 if (error)
318 goto err_free_datardy_irq;
319
320 error = input_register_device(tsi->dev);
321 if (error)
322 goto err_free_datardy_irq;
323
324 platform_set_drvdata(pdev, tsi);
325
326 return 0;
327
328err_free_datardy_irq:
329 free_irq(tsi->irq_datardy, tsi);
330err_free_pendwn_irq:
331 free_irq(tsi->irq_pendwn, tsi);
332err_free_mem:
333 kfree(tsi);
334 input_free_device(input_dev);
335
336 return error;
337}
338
339static int __devexit da9052_ts_remove(struct platform_device *pdev)
340{
341 struct da9052_tsi *tsi = platform_get_drvdata(pdev);
342
343 da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x19);
344
345 free_irq(tsi->irq_pendwn, tsi);
346 free_irq(tsi->irq_datardy, tsi);
347
348 input_unregister_device(tsi->dev);
349 kfree(tsi);
350
351 platform_set_drvdata(pdev, NULL);
352
353 return 0;
354}
355
356static struct platform_driver da9052_tsi_driver = {
357 .probe = da9052_ts_probe,
358 .remove = __devexit_p(da9052_ts_remove),
359 .driver = {
360 .name = "da9052-tsi",
361 .owner = THIS_MODULE,
362 },
363};
364
365module_platform_driver(da9052_tsi_driver);
366
367MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9052");
368MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>");
369MODULE_LICENSE("GPL");
370MODULE_ALIAS("platform:da9052-tsi");