Commit | Line | Data |
---|---|---|
1b8be32e RP |
1 | /* |
2 | * TI Touch Screen driver | |
3 | * | |
4 | * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License as | |
8 | * published by the Free Software Foundation version 2. | |
9 | * | |
10 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | |
11 | * kind, whether express or implied; without even the implied warranty | |
12 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | */ | |
15 | ||
16 | ||
17 | #include <linux/init.h> | |
18 | #include <linux/kernel.h> | |
19 | #include <linux/err.h> | |
20 | #include <linux/module.h> | |
21 | #include <linux/input.h> | |
22 | #include <linux/slab.h> | |
23 | #include <linux/interrupt.h> | |
24 | #include <linux/clk.h> | |
25 | #include <linux/platform_device.h> | |
26 | #include <linux/io.h> | |
55c04de5 | 27 | #include <linux/input/ti_am335x_tsc.h> |
1b8be32e RP |
28 | #include <linux/delay.h> |
29 | ||
2b99bafa | 30 | #include <linux/mfd/ti_am335x_tscadc.h> |
33f5cc60 PR |
31 | |
32 | #define ADCFSM_STEPID 0x10 | |
1b8be32e | 33 | #define SEQ_SETTLE 275 |
1b8be32e | 34 | #define MAX_12BIT ((1 << 12) - 1) |
1b8be32e | 35 | |
55c04de5 | 36 | struct titsc { |
1b8be32e | 37 | struct input_dev *input; |
2b99bafa | 38 | struct ti_tscadc_dev *mfd_tscadc; |
1b8be32e RP |
39 | unsigned int irq; |
40 | unsigned int wires; | |
41 | unsigned int x_plate_resistance; | |
42 | bool pen_down; | |
d1fb5743 | 43 | int steps_to_configure; |
1b8be32e RP |
44 | }; |
45 | ||
55c04de5 | 46 | static unsigned int titsc_readl(struct titsc *ts, unsigned int reg) |
1b8be32e | 47 | { |
2b99bafa | 48 | return readl(ts->mfd_tscadc->tscadc_base + reg); |
1b8be32e RP |
49 | } |
50 | ||
55c04de5 | 51 | static void titsc_writel(struct titsc *tsc, unsigned int reg, |
1b8be32e RP |
52 | unsigned int val) |
53 | { | |
2b99bafa | 54 | writel(val, tsc->mfd_tscadc->tscadc_base + reg); |
1b8be32e RP |
55 | } |
56 | ||
55c04de5 | 57 | static void titsc_step_config(struct titsc *ts_dev) |
1b8be32e RP |
58 | { |
59 | unsigned int config; | |
d1fb5743 | 60 | int i, total_steps; |
1b8be32e RP |
61 | |
62 | /* Configure the Step registers */ | |
d1fb5743 | 63 | total_steps = 2 * ts_dev->steps_to_configure; |
1b8be32e RP |
64 | |
65 | config = STEPCONFIG_MODE_HWSYNC | | |
33f5cc60 | 66 | STEPCONFIG_AVG_16 | STEPCONFIG_XPP; |
1b8be32e RP |
67 | switch (ts_dev->wires) { |
68 | case 4: | |
33f5cc60 | 69 | config |= STEPCONFIG_INP_AN2 | STEPCONFIG_XNN; |
1b8be32e RP |
70 | break; |
71 | case 5: | |
72 | config |= STEPCONFIG_YNN | | |
33f5cc60 | 73 | STEPCONFIG_INP_AN4 | STEPCONFIG_XNN | |
1b8be32e RP |
74 | STEPCONFIG_YPP; |
75 | break; | |
76 | case 8: | |
33f5cc60 | 77 | config |= STEPCONFIG_INP_AN2 | STEPCONFIG_XNN; |
1b8be32e RP |
78 | break; |
79 | } | |
80 | ||
d1fb5743 | 81 | for (i = 1; i <= ts_dev->steps_to_configure; i++) { |
55c04de5 PR |
82 | titsc_writel(ts_dev, REG_STEPCONFIG(i), config); |
83 | titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); | |
1b8be32e RP |
84 | } |
85 | ||
86 | config = 0; | |
87 | config = STEPCONFIG_MODE_HWSYNC | | |
33f5cc60 PR |
88 | STEPCONFIG_AVG_16 | STEPCONFIG_YNN | |
89 | STEPCONFIG_INM_ADCREFM | STEPCONFIG_FIFO1; | |
1b8be32e RP |
90 | switch (ts_dev->wires) { |
91 | case 4: | |
92 | config |= STEPCONFIG_YPP; | |
93 | break; | |
94 | case 5: | |
33f5cc60 | 95 | config |= STEPCONFIG_XPP | STEPCONFIG_INP_AN4 | |
1b8be32e RP |
96 | STEPCONFIG_XNP | STEPCONFIG_YPN; |
97 | break; | |
98 | case 8: | |
99 | config |= STEPCONFIG_YPP; | |
100 | break; | |
101 | } | |
102 | ||
d1fb5743 | 103 | for (i = (ts_dev->steps_to_configure + 1); i <= total_steps; i++) { |
55c04de5 PR |
104 | titsc_writel(ts_dev, REG_STEPCONFIG(i), config); |
105 | titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); | |
1b8be32e RP |
106 | } |
107 | ||
108 | config = 0; | |
109 | /* Charge step configuration */ | |
110 | config = STEPCONFIG_XPP | STEPCONFIG_YNN | | |
33f5cc60 PR |
111 | STEPCHARGE_RFP_XPUL | STEPCHARGE_RFM_XNUR | |
112 | STEPCHARGE_INM_AN1 | STEPCHARGE_INP_AN1; | |
1b8be32e | 113 | |
55c04de5 PR |
114 | titsc_writel(ts_dev, REG_CHARGECONFIG, config); |
115 | titsc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY); | |
1b8be32e RP |
116 | |
117 | config = 0; | |
118 | /* Configure to calculate pressure */ | |
119 | config = STEPCONFIG_MODE_HWSYNC | | |
33f5cc60 PR |
120 | STEPCONFIG_AVG_16 | STEPCONFIG_YPP | |
121 | STEPCONFIG_XNN | STEPCONFIG_INM_ADCREFM; | |
55c04de5 PR |
122 | titsc_writel(ts_dev, REG_STEPCONFIG(total_steps + 1), config); |
123 | titsc_writel(ts_dev, REG_STEPDELAY(total_steps + 1), | |
d1fb5743 | 124 | STEPCONFIG_OPENDLY); |
1b8be32e | 125 | |
33f5cc60 | 126 | config |= STEPCONFIG_INP_AN3 | STEPCONFIG_FIFO1; |
55c04de5 PR |
127 | titsc_writel(ts_dev, REG_STEPCONFIG(total_steps + 2), config); |
128 | titsc_writel(ts_dev, REG_STEPDELAY(total_steps + 2), | |
d1fb5743 | 129 | STEPCONFIG_OPENDLY); |
1b8be32e | 130 | |
2b99bafa | 131 | titsc_writel(ts_dev, REG_SE, STPENB_STEPENB_TC); |
1b8be32e RP |
132 | } |
133 | ||
55c04de5 | 134 | static void titsc_read_coordinates(struct titsc *ts_dev, |
1b8be32e RP |
135 | unsigned int *x, unsigned int *y) |
136 | { | |
55c04de5 | 137 | unsigned int fifocount = titsc_readl(ts_dev, REG_FIFO0CNT); |
1b8be32e RP |
138 | unsigned int prev_val_x = ~0, prev_val_y = ~0; |
139 | unsigned int prev_diff_x = ~0, prev_diff_y = ~0; | |
140 | unsigned int read, diff; | |
2b99bafa | 141 | unsigned int i, channel; |
1b8be32e RP |
142 | |
143 | /* | |
144 | * Delta filter is used to remove large variations in sampled | |
145 | * values from ADC. The filter tries to predict where the next | |
146 | * coordinate could be. This is done by taking a previous | |
147 | * coordinate and subtracting it form current one. Further the | |
148 | * algorithm compares the difference with that of a present value, | |
149 | * if true the value is reported to the sub system. | |
150 | */ | |
151 | for (i = 0; i < fifocount - 1; i++) { | |
2b99bafa PR |
152 | read = titsc_readl(ts_dev, REG_FIFO0); |
153 | channel = read & 0xf0000; | |
154 | channel = channel >> 0x10; | |
155 | if ((channel >= 0) && (channel < ts_dev->steps_to_configure)) { | |
156 | read &= 0xfff; | |
157 | diff = abs(read - prev_val_x); | |
158 | if (diff < prev_diff_x) { | |
159 | prev_diff_x = diff; | |
160 | *x = read; | |
161 | } | |
162 | prev_val_x = read; | |
1b8be32e | 163 | } |
1b8be32e | 164 | |
2b99bafa PR |
165 | read = titsc_readl(ts_dev, REG_FIFO1); |
166 | channel = read & 0xf0000; | |
167 | channel = channel >> 0x10; | |
168 | if ((channel >= ts_dev->steps_to_configure) && | |
169 | (channel < (2 * ts_dev->steps_to_configure - 1))) { | |
170 | read &= 0xfff; | |
171 | diff = abs(read - prev_val_y); | |
172 | if (diff < prev_diff_y) { | |
173 | prev_diff_y = diff; | |
174 | *y = read; | |
175 | } | |
176 | prev_val_y = read; | |
1b8be32e | 177 | } |
1b8be32e RP |
178 | } |
179 | } | |
180 | ||
55c04de5 | 181 | static irqreturn_t titsc_irq(int irq, void *dev) |
1b8be32e | 182 | { |
55c04de5 | 183 | struct titsc *ts_dev = dev; |
1b8be32e RP |
184 | struct input_dev *input_dev = ts_dev->input; |
185 | unsigned int status, irqclr = 0; | |
186 | unsigned int x = 0, y = 0; | |
187 | unsigned int z1, z2, z; | |
188 | unsigned int fsm; | |
2b99bafa PR |
189 | unsigned int fifo1count, fifo0count; |
190 | int i; | |
1b8be32e | 191 | |
55c04de5 | 192 | status = titsc_readl(ts_dev, REG_IRQSTATUS); |
30af55f9 | 193 | if (status & IRQENB_FIFO0THRES) { |
55c04de5 | 194 | titsc_read_coordinates(ts_dev, &x, &y); |
1b8be32e | 195 | |
55c04de5 PR |
196 | z1 = titsc_readl(ts_dev, REG_FIFO0) & 0xfff; |
197 | z2 = titsc_readl(ts_dev, REG_FIFO1) & 0xfff; | |
1b8be32e | 198 | |
2b99bafa PR |
199 | fifo1count = titsc_readl(ts_dev, REG_FIFO1CNT); |
200 | for (i = 0; i < fifo1count; i++) | |
201 | titsc_readl(ts_dev, REG_FIFO1); | |
202 | ||
203 | fifo0count = titsc_readl(ts_dev, REG_FIFO0CNT); | |
204 | for (i = 0; i < fifo0count; i++) | |
205 | titsc_readl(ts_dev, REG_FIFO0); | |
206 | ||
1b8be32e RP |
207 | if (ts_dev->pen_down && z1 != 0 && z2 != 0) { |
208 | /* | |
209 | * Calculate pressure using formula | |
210 | * Resistance(touch) = x plate resistance * | |
211 | * x postion/4096 * ((z2 / z1) - 1) | |
212 | */ | |
213 | z = z2 - z1; | |
214 | z *= x; | |
215 | z *= ts_dev->x_plate_resistance; | |
216 | z /= z1; | |
217 | z = (z + 2047) >> 12; | |
218 | ||
219 | if (z <= MAX_12BIT) { | |
220 | input_report_abs(input_dev, ABS_X, x); | |
221 | input_report_abs(input_dev, ABS_Y, y); | |
222 | input_report_abs(input_dev, ABS_PRESSURE, z); | |
223 | input_report_key(input_dev, BTN_TOUCH, 1); | |
224 | input_sync(input_dev); | |
225 | } | |
226 | } | |
30af55f9 | 227 | irqclr |= IRQENB_FIFO0THRES; |
1b8be32e RP |
228 | } |
229 | ||
230 | /* | |
231 | * Time for sequencer to settle, to read | |
232 | * correct state of the sequencer. | |
233 | */ | |
234 | udelay(SEQ_SETTLE); | |
235 | ||
55c04de5 | 236 | status = titsc_readl(ts_dev, REG_RAWIRQSTATUS); |
1b8be32e RP |
237 | if (status & IRQENB_PENUP) { |
238 | /* Pen up event */ | |
55c04de5 | 239 | fsm = titsc_readl(ts_dev, REG_ADCFSM); |
1b8be32e RP |
240 | if (fsm == ADCFSM_STEPID) { |
241 | ts_dev->pen_down = false; | |
242 | input_report_key(input_dev, BTN_TOUCH, 0); | |
243 | input_report_abs(input_dev, ABS_PRESSURE, 0); | |
244 | input_sync(input_dev); | |
245 | } else { | |
246 | ts_dev->pen_down = true; | |
247 | } | |
248 | irqclr |= IRQENB_PENUP; | |
249 | } | |
250 | ||
55c04de5 | 251 | titsc_writel(ts_dev, REG_IRQSTATUS, irqclr); |
1b8be32e | 252 | |
2b99bafa | 253 | titsc_writel(ts_dev, REG_SE, STPENB_STEPENB_TC); |
1b8be32e RP |
254 | return IRQ_HANDLED; |
255 | } | |
256 | ||
257 | /* | |
258 | * The functions for inserting/removing driver as a module. | |
259 | */ | |
260 | ||
55c04de5 | 261 | static int __devinit titsc_probe(struct platform_device *pdev) |
1b8be32e | 262 | { |
55c04de5 | 263 | struct titsc *ts_dev; |
1b8be32e | 264 | struct input_dev *input_dev; |
2b99bafa PR |
265 | struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data; |
266 | struct mfd_tscadc_board *pdata; | |
1b8be32e | 267 | int err; |
1b8be32e | 268 | |
2b99bafa | 269 | pdata = tscadc_dev->dev->platform_data; |
1b8be32e | 270 | |
2b99bafa PR |
271 | if (!pdata) { |
272 | dev_err(&pdev->dev, "Could not find platform data\n"); | |
1b8be32e RP |
273 | return -EINVAL; |
274 | } | |
275 | ||
276 | /* Allocate memory for device */ | |
55c04de5 | 277 | ts_dev = kzalloc(sizeof(struct titsc), GFP_KERNEL); |
1b8be32e RP |
278 | input_dev = input_allocate_device(); |
279 | if (!ts_dev || !input_dev) { | |
280 | dev_err(&pdev->dev, "failed to allocate memory.\n"); | |
281 | err = -ENOMEM; | |
282 | goto err_free_mem; | |
283 | } | |
284 | ||
2b99bafa PR |
285 | tscadc_dev->tsc = ts_dev; |
286 | ts_dev->mfd_tscadc = tscadc_dev; | |
1b8be32e | 287 | ts_dev->input = input_dev; |
2b99bafa PR |
288 | ts_dev->irq = tscadc_dev->irq; |
289 | ts_dev->wires = pdata->tsc_init->wires; | |
290 | ts_dev->x_plate_resistance = pdata->tsc_init->x_plate_resistance; | |
291 | ts_dev->steps_to_configure = pdata->tsc_init->steps_to_configure; | |
1b8be32e | 292 | |
55c04de5 | 293 | err = request_irq(ts_dev->irq, titsc_irq, |
1b8be32e RP |
294 | 0, pdev->dev.driver->name, ts_dev); |
295 | if (err) { | |
296 | dev_err(&pdev->dev, "failed to allocate irq.\n"); | |
2b99bafa | 297 | goto err_free_mem; |
1b8be32e | 298 | } |
1b8be32e | 299 | |
55c04de5 PR |
300 | titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES); |
301 | titsc_step_config(ts_dev); | |
302 | titsc_writel(ts_dev, REG_FIFO0THR, ts_dev->steps_to_configure); | |
1b8be32e | 303 | |
2b99bafa | 304 | input_dev->name = "ti-tsc"; |
1b8be32e RP |
305 | input_dev->dev.parent = &pdev->dev; |
306 | ||
307 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); | |
308 | input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); | |
309 | ||
310 | input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); | |
311 | input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); | |
312 | input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); | |
313 | ||
314 | /* register to the input system */ | |
315 | err = input_register_device(input_dev); | |
316 | if (err) | |
2b99bafa | 317 | goto err_free_irq; |
1b8be32e RP |
318 | |
319 | platform_set_drvdata(pdev, ts_dev); | |
320 | return 0; | |
321 | ||
1b8be32e RP |
322 | err_free_irq: |
323 | free_irq(ts_dev->irq, ts_dev); | |
1b8be32e RP |
324 | err_free_mem: |
325 | input_free_device(input_dev); | |
326 | kfree(ts_dev); | |
327 | return err; | |
328 | } | |
329 | ||
55c04de5 | 330 | static int __devexit titsc_remove(struct platform_device *pdev) |
1b8be32e | 331 | { |
2b99bafa PR |
332 | struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data; |
333 | struct titsc *ts_dev = tscadc_dev->tsc; | |
1b8be32e RP |
334 | |
335 | free_irq(ts_dev->irq, ts_dev); | |
336 | ||
337 | input_unregister_device(ts_dev->input); | |
338 | ||
2b99bafa PR |
339 | platform_set_drvdata(pdev, NULL); |
340 | kfree(ts_dev); | |
341 | return 0; | |
342 | } | |
1b8be32e | 343 | |
2b99bafa PR |
344 | #ifdef CONFIG_PM |
345 | static int titsc_suspend(struct device *dev) | |
346 | { | |
347 | struct ti_tscadc_dev *tscadc_dev = dev->platform_data; | |
348 | struct titsc *ts_dev = tscadc_dev->tsc; | |
349 | unsigned int idle; | |
350 | ||
351 | if (device_may_wakeup(tscadc_dev->dev)) { | |
352 | idle = titsc_readl(ts_dev, REG_IRQENABLE); | |
353 | titsc_writel(ts_dev, REG_IRQENABLE, | |
354 | (idle | IRQENB_HW_PEN)); | |
355 | titsc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB); | |
356 | } | |
357 | return 0; | |
358 | } | |
1b8be32e | 359 | |
2b99bafa PR |
360 | static int titsc_resume(struct device *dev) |
361 | { | |
362 | struct ti_tscadc_dev *tscadc_dev = dev->platform_data; | |
363 | struct titsc *ts_dev = tscadc_dev->tsc; | |
1b8be32e | 364 | |
2b99bafa PR |
365 | if (device_may_wakeup(tscadc_dev->dev)) { |
366 | titsc_writel(ts_dev, REG_IRQWAKEUP, | |
367 | 0x00); | |
368 | titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); | |
369 | } | |
370 | titsc_step_config(ts_dev); | |
371 | titsc_writel(ts_dev, REG_FIFO0THR, | |
372 | ts_dev->steps_to_configure); | |
1b8be32e RP |
373 | return 0; |
374 | } | |
375 | ||
2b99bafa PR |
376 | static const struct dev_pm_ops titsc_pm_ops = { |
377 | .suspend = titsc_suspend, | |
378 | .resume = titsc_resume, | |
379 | }; | |
380 | #define TITSC_PM_OPS (&titsc_pm_ops) | |
381 | #else | |
382 | #define TITSC_PM_OPS NULL | |
383 | #endif | |
384 | ||
1b8be32e | 385 | static struct platform_driver ti_tsc_driver = { |
55c04de5 PR |
386 | .probe = titsc_probe, |
387 | .remove = __devexit_p(titsc_remove), | |
1b8be32e RP |
388 | .driver = { |
389 | .name = "tsc", | |
390 | .owner = THIS_MODULE, | |
2b99bafa | 391 | .pm = TITSC_PM_OPS, |
1b8be32e RP |
392 | }, |
393 | }; | |
394 | module_platform_driver(ti_tsc_driver); | |
395 | ||
396 | MODULE_DESCRIPTION("TI touchscreen controller driver"); | |
397 | MODULE_AUTHOR("Rachna Patil <rachna@ti.com>"); | |
398 | MODULE_LICENSE("GPL"); |