Input: remove use of __devinit
[deliverable/linux.git] / drivers / input / touchscreen / da9052_tsi.c
CommitLineData
eead75a2
AJ
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
5298cc4c 146static int da9052_ts_configure_gpio(struct da9052 *da9052)
eead75a2
AJ
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
5298cc4c 165static int da9052_configure_tsi(struct da9052_tsi *tsi)
eead75a2
AJ
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
5298cc4c 232static int da9052_ts_probe(struct platform_device *pdev)
eead75a2
AJ
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,
1cb0aa88 358 .remove = da9052_ts_remove,
eead75a2
AJ
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");
This page took 0.070914 seconds and 5 git commands to generate.