diff options
Diffstat (limited to 'drivers/input/touchscreen/lpc32xx_ts.c')
-rw-r--r-- | drivers/input/touchscreen/lpc32xx_ts.c | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/lpc32xx_ts.c b/drivers/input/touchscreen/lpc32xx_ts.c new file mode 100644 index 000000000000..dcf803f5a1f7 --- /dev/null +++ b/drivers/input/touchscreen/lpc32xx_ts.c | |||
@@ -0,0 +1,411 @@ | |||
1 | /* | ||
2 | * LPC32xx built-in touchscreen driver | ||
3 | * | ||
4 | * Copyright (C) 2010 NXP Semiconductors | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | */ | ||
16 | |||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/input.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/clk.h> | ||
23 | #include <linux/io.h> | ||
24 | #include <linux/slab.h> | ||
25 | |||
26 | /* | ||
27 | * Touchscreen controller register offsets | ||
28 | */ | ||
29 | #define LPC32XX_TSC_STAT 0x00 | ||
30 | #define LPC32XX_TSC_SEL 0x04 | ||
31 | #define LPC32XX_TSC_CON 0x08 | ||
32 | #define LPC32XX_TSC_FIFO 0x0C | ||
33 | #define LPC32XX_TSC_DTR 0x10 | ||
34 | #define LPC32XX_TSC_RTR 0x14 | ||
35 | #define LPC32XX_TSC_UTR 0x18 | ||
36 | #define LPC32XX_TSC_TTR 0x1C | ||
37 | #define LPC32XX_TSC_DXP 0x20 | ||
38 | #define LPC32XX_TSC_MIN_X 0x24 | ||
39 | #define LPC32XX_TSC_MAX_X 0x28 | ||
40 | #define LPC32XX_TSC_MIN_Y 0x2C | ||
41 | #define LPC32XX_TSC_MAX_Y 0x30 | ||
42 | #define LPC32XX_TSC_AUX_UTR 0x34 | ||
43 | #define LPC32XX_TSC_AUX_MIN 0x38 | ||
44 | #define LPC32XX_TSC_AUX_MAX 0x3C | ||
45 | |||
46 | #define LPC32XX_TSC_STAT_FIFO_OVRRN (1 << 8) | ||
47 | #define LPC32XX_TSC_STAT_FIFO_EMPTY (1 << 7) | ||
48 | |||
49 | #define LPC32XX_TSC_SEL_DEFVAL 0x0284 | ||
50 | |||
51 | #define LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 (0x1 << 11) | ||
52 | #define LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(s) ((10 - (s)) << 7) | ||
53 | #define LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(s) ((10 - (s)) << 4) | ||
54 | #define LPC32XX_TSC_ADCCON_POWER_UP (1 << 2) | ||
55 | #define LPC32XX_TSC_ADCCON_AUTO_EN (1 << 0) | ||
56 | |||
57 | #define LPC32XX_TSC_FIFO_TS_P_LEVEL (1 << 31) | ||
58 | #define LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(x) (((x) & 0x03FF0000) >> 16) | ||
59 | #define LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(y) ((y) & 0x000003FF) | ||
60 | |||
61 | #define LPC32XX_TSC_ADCDAT_VALUE_MASK 0x000003FF | ||
62 | |||
63 | #define LPC32XX_TSC_MIN_XY_VAL 0x0 | ||
64 | #define LPC32XX_TSC_MAX_XY_VAL 0x3FF | ||
65 | |||
66 | #define MOD_NAME "ts-lpc32xx" | ||
67 | |||
68 | #define tsc_readl(dev, reg) \ | ||
69 | __raw_readl((dev)->tsc_base + (reg)) | ||
70 | #define tsc_writel(dev, reg, val) \ | ||
71 | __raw_writel((val), (dev)->tsc_base + (reg)) | ||
72 | |||
73 | struct lpc32xx_tsc { | ||
74 | struct input_dev *dev; | ||
75 | void __iomem *tsc_base; | ||
76 | int irq; | ||
77 | struct clk *clk; | ||
78 | }; | ||
79 | |||
80 | static void lpc32xx_fifo_clear(struct lpc32xx_tsc *tsc) | ||
81 | { | ||
82 | while (!(tsc_readl(tsc, LPC32XX_TSC_STAT) & | ||
83 | LPC32XX_TSC_STAT_FIFO_EMPTY)) | ||
84 | tsc_readl(tsc, LPC32XX_TSC_FIFO); | ||
85 | } | ||
86 | |||
87 | static irqreturn_t lpc32xx_ts_interrupt(int irq, void *dev_id) | ||
88 | { | ||
89 | u32 tmp, rv[4], xs[4], ys[4]; | ||
90 | int idx; | ||
91 | struct lpc32xx_tsc *tsc = dev_id; | ||
92 | struct input_dev *input = tsc->dev; | ||
93 | |||
94 | tmp = tsc_readl(tsc, LPC32XX_TSC_STAT); | ||
95 | |||
96 | if (tmp & LPC32XX_TSC_STAT_FIFO_OVRRN) { | ||
97 | /* FIFO overflow - throw away samples */ | ||
98 | lpc32xx_fifo_clear(tsc); | ||
99 | return IRQ_HANDLED; | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * Gather and normalize 4 samples. Pen-up events may have less | ||
104 | * than 4 samples, but its ok to pop 4 and let the last sample | ||
105 | * pen status check drop the samples. | ||
106 | */ | ||
107 | idx = 0; | ||
108 | while (idx < 4 && | ||
109 | !(tsc_readl(tsc, LPC32XX_TSC_STAT) & | ||
110 | LPC32XX_TSC_STAT_FIFO_EMPTY)) { | ||
111 | tmp = tsc_readl(tsc, LPC32XX_TSC_FIFO); | ||
112 | xs[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK - | ||
113 | LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(tmp); | ||
114 | ys[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK - | ||
115 | LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(tmp); | ||
116 | rv[idx] = tmp; | ||
117 | idx++; | ||
118 | } | ||
119 | |||
120 | /* Data is only valid if pen is still down in last sample */ | ||
121 | if (!(rv[3] & LPC32XX_TSC_FIFO_TS_P_LEVEL) && idx == 4) { | ||
122 | /* Use average of 2nd and 3rd sample for position */ | ||
123 | input_report_abs(input, ABS_X, (xs[1] + xs[2]) / 2); | ||
124 | input_report_abs(input, ABS_Y, (ys[1] + ys[2]) / 2); | ||
125 | input_report_key(input, BTN_TOUCH, 1); | ||
126 | } else { | ||
127 | input_report_key(input, BTN_TOUCH, 0); | ||
128 | } | ||
129 | |||
130 | input_sync(input); | ||
131 | |||
132 | return IRQ_HANDLED; | ||
133 | } | ||
134 | |||
135 | static void lpc32xx_stop_tsc(struct lpc32xx_tsc *tsc) | ||
136 | { | ||
137 | /* Disable auto mode */ | ||
138 | tsc_writel(tsc, LPC32XX_TSC_CON, | ||
139 | tsc_readl(tsc, LPC32XX_TSC_CON) & | ||
140 | ~LPC32XX_TSC_ADCCON_AUTO_EN); | ||
141 | |||
142 | clk_disable(tsc->clk); | ||
143 | } | ||
144 | |||
145 | static void lpc32xx_setup_tsc(struct lpc32xx_tsc *tsc) | ||
146 | { | ||
147 | u32 tmp; | ||
148 | |||
149 | clk_enable(tsc->clk); | ||
150 | |||
151 | tmp = tsc_readl(tsc, LPC32XX_TSC_CON) & ~LPC32XX_TSC_ADCCON_POWER_UP; | ||
152 | |||
153 | /* Set the TSC FIFO depth to 4 samples @ 10-bits per sample (max) */ | ||
154 | tmp = LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 | | ||
155 | LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(10) | | ||
156 | LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(10); | ||
157 | tsc_writel(tsc, LPC32XX_TSC_CON, tmp); | ||
158 | |||
159 | /* These values are all preset */ | ||
160 | tsc_writel(tsc, LPC32XX_TSC_SEL, LPC32XX_TSC_SEL_DEFVAL); | ||
161 | tsc_writel(tsc, LPC32XX_TSC_MIN_X, LPC32XX_TSC_MIN_XY_VAL); | ||
162 | tsc_writel(tsc, LPC32XX_TSC_MAX_X, LPC32XX_TSC_MAX_XY_VAL); | ||
163 | tsc_writel(tsc, LPC32XX_TSC_MIN_Y, LPC32XX_TSC_MIN_XY_VAL); | ||
164 | tsc_writel(tsc, LPC32XX_TSC_MAX_Y, LPC32XX_TSC_MAX_XY_VAL); | ||
165 | |||
166 | /* Aux support is not used */ | ||
167 | tsc_writel(tsc, LPC32XX_TSC_AUX_UTR, 0); | ||
168 | tsc_writel(tsc, LPC32XX_TSC_AUX_MIN, 0); | ||
169 | tsc_writel(tsc, LPC32XX_TSC_AUX_MAX, 0); | ||
170 | |||
171 | /* | ||
172 | * Set sample rate to about 240Hz per X/Y pair. A single measurement | ||
173 | * consists of 4 pairs which gives about a 60Hz sample rate based on | ||
174 | * a stable 32768Hz clock source. Values are in clocks. | ||
175 | * Rate is (32768 / (RTR + XCONV + RTR + YCONV + DXP + TTR + UTR) / 4 | ||
176 | */ | ||
177 | tsc_writel(tsc, LPC32XX_TSC_RTR, 0x2); | ||
178 | tsc_writel(tsc, LPC32XX_TSC_DTR, 0x2); | ||
179 | tsc_writel(tsc, LPC32XX_TSC_TTR, 0x10); | ||
180 | tsc_writel(tsc, LPC32XX_TSC_DXP, 0x4); | ||
181 | tsc_writel(tsc, LPC32XX_TSC_UTR, 88); | ||
182 | |||
183 | lpc32xx_fifo_clear(tsc); | ||
184 | |||
185 | /* Enable automatic ts event capture */ | ||
186 | tsc_writel(tsc, LPC32XX_TSC_CON, tmp | LPC32XX_TSC_ADCCON_AUTO_EN); | ||
187 | } | ||
188 | |||
189 | static int lpc32xx_ts_open(struct input_dev *dev) | ||
190 | { | ||
191 | struct lpc32xx_tsc *tsc = input_get_drvdata(dev); | ||
192 | |||
193 | lpc32xx_setup_tsc(tsc); | ||
194 | |||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static void lpc32xx_ts_close(struct input_dev *dev) | ||
199 | { | ||
200 | struct lpc32xx_tsc *tsc = input_get_drvdata(dev); | ||
201 | |||
202 | lpc32xx_stop_tsc(tsc); | ||
203 | } | ||
204 | |||
205 | static int __devinit lpc32xx_ts_probe(struct platform_device *pdev) | ||
206 | { | ||
207 | struct lpc32xx_tsc *tsc; | ||
208 | struct input_dev *input; | ||
209 | struct resource *res; | ||
210 | resource_size_t size; | ||
211 | int irq; | ||
212 | int error; | ||
213 | |||
214 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
215 | if (!res) { | ||
216 | dev_err(&pdev->dev, "Can't get memory resource\n"); | ||
217 | return -ENOENT; | ||
218 | } | ||
219 | |||
220 | irq = platform_get_irq(pdev, 0); | ||
221 | if (irq < 0) { | ||
222 | dev_err(&pdev->dev, "Can't get interrupt resource\n"); | ||
223 | return irq; | ||
224 | } | ||
225 | |||
226 | tsc = kzalloc(sizeof(*tsc), GFP_KERNEL); | ||
227 | input = input_allocate_device(); | ||
228 | if (!tsc || !input) { | ||
229 | dev_err(&pdev->dev, "failed allocating memory\n"); | ||
230 | error = -ENOMEM; | ||
231 | goto err_free_mem; | ||
232 | } | ||
233 | |||
234 | tsc->dev = input; | ||
235 | tsc->irq = irq; | ||
236 | |||
237 | size = resource_size(res); | ||
238 | |||
239 | if (!request_mem_region(res->start, size, pdev->name)) { | ||
240 | dev_err(&pdev->dev, "TSC registers are not free\n"); | ||
241 | error = -EBUSY; | ||
242 | goto err_free_mem; | ||
243 | } | ||
244 | |||
245 | tsc->tsc_base = ioremap(res->start, size); | ||
246 | if (!tsc->tsc_base) { | ||
247 | dev_err(&pdev->dev, "Can't map memory\n"); | ||
248 | error = -ENOMEM; | ||
249 | goto err_release_mem; | ||
250 | } | ||
251 | |||
252 | tsc->clk = clk_get(&pdev->dev, NULL); | ||
253 | if (IS_ERR(tsc->clk)) { | ||
254 | dev_err(&pdev->dev, "failed getting clock\n"); | ||
255 | error = PTR_ERR(tsc->clk); | ||
256 | goto err_unmap; | ||
257 | } | ||
258 | |||
259 | input->name = MOD_NAME; | ||
260 | input->phys = "lpc32xx/input0"; | ||
261 | input->id.bustype = BUS_HOST; | ||
262 | input->id.vendor = 0x0001; | ||
263 | input->id.product = 0x0002; | ||
264 | input->id.version = 0x0100; | ||
265 | input->dev.parent = &pdev->dev; | ||
266 | input->open = lpc32xx_ts_open; | ||
267 | input->close = lpc32xx_ts_close; | ||
268 | |||
269 | input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); | ||
270 | input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); | ||
271 | input_set_abs_params(input, ABS_X, LPC32XX_TSC_MIN_XY_VAL, | ||
272 | LPC32XX_TSC_MAX_XY_VAL, 0, 0); | ||
273 | input_set_abs_params(input, ABS_Y, LPC32XX_TSC_MIN_XY_VAL, | ||
274 | LPC32XX_TSC_MAX_XY_VAL, 0, 0); | ||
275 | |||
276 | input_set_drvdata(input, tsc); | ||
277 | |||
278 | error = request_irq(tsc->irq, lpc32xx_ts_interrupt, | ||
279 | IRQF_DISABLED, pdev->name, tsc); | ||
280 | if (error) { | ||
281 | dev_err(&pdev->dev, "failed requesting interrupt\n"); | ||
282 | goto err_put_clock; | ||
283 | } | ||
284 | |||
285 | error = input_register_device(input); | ||
286 | if (error) { | ||
287 | dev_err(&pdev->dev, "failed registering input device\n"); | ||
288 | goto err_free_irq; | ||
289 | } | ||
290 | |||
291 | platform_set_drvdata(pdev, tsc); | ||
292 | device_init_wakeup(&pdev->dev, 1); | ||
293 | |||
294 | return 0; | ||
295 | |||
296 | err_free_irq: | ||
297 | free_irq(tsc->irq, tsc); | ||
298 | err_put_clock: | ||
299 | clk_put(tsc->clk); | ||
300 | err_unmap: | ||
301 | iounmap(tsc->tsc_base); | ||
302 | err_release_mem: | ||
303 | release_mem_region(res->start, size); | ||
304 | err_free_mem: | ||
305 | input_free_device(input); | ||
306 | kfree(tsc); | ||
307 | |||
308 | return error; | ||
309 | } | ||
310 | |||
311 | static int __devexit lpc32xx_ts_remove(struct platform_device *pdev) | ||
312 | { | ||
313 | struct lpc32xx_tsc *tsc = platform_get_drvdata(pdev); | ||
314 | struct resource *res; | ||
315 | |||
316 | device_init_wakeup(&pdev->dev, 0); | ||
317 | free_irq(tsc->irq, tsc); | ||
318 | |||
319 | input_unregister_device(tsc->dev); | ||
320 | |||
321 | clk_put(tsc->clk); | ||
322 | |||
323 | iounmap(tsc->tsc_base); | ||
324 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
325 | release_mem_region(res->start, resource_size(res)); | ||
326 | |||
327 | kfree(tsc); | ||
328 | |||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | #ifdef CONFIG_PM | ||
333 | static int lpc32xx_ts_suspend(struct device *dev) | ||
334 | { | ||
335 | struct lpc32xx_tsc *tsc = dev_get_drvdata(dev); | ||
336 | struct input_dev *input = tsc->dev; | ||
337 | |||
338 | /* | ||
339 | * Suspend and resume can be called when the device hasn't been | ||
340 | * enabled. If there are no users that have the device open, then | ||
341 | * avoid calling the TSC stop and start functions as the TSC | ||
342 | * isn't yet clocked. | ||
343 | */ | ||
344 | mutex_lock(&input->mutex); | ||
345 | |||
346 | if (input->users) { | ||
347 | if (device_may_wakeup(dev)) | ||
348 | enable_irq_wake(tsc->irq); | ||
349 | else | ||
350 | lpc32xx_stop_tsc(tsc); | ||
351 | } | ||
352 | |||
353 | mutex_unlock(&input->mutex); | ||
354 | |||
355 | return 0; | ||
356 | } | ||
357 | |||
358 | static int lpc32xx_ts_resume(struct device *dev) | ||
359 | { | ||
360 | struct lpc32xx_tsc *tsc = dev_get_drvdata(dev); | ||
361 | struct input_dev *input = tsc->dev; | ||
362 | |||
363 | mutex_lock(&input->mutex); | ||
364 | |||
365 | if (input->users) { | ||
366 | if (device_may_wakeup(dev)) | ||
367 | disable_irq_wake(tsc->irq); | ||
368 | else | ||
369 | lpc32xx_setup_tsc(tsc); | ||
370 | } | ||
371 | |||
372 | mutex_unlock(&input->mutex); | ||
373 | |||
374 | return 0; | ||
375 | } | ||
376 | |||
377 | static const struct dev_pm_ops lpc32xx_ts_pm_ops = { | ||
378 | .suspend = lpc32xx_ts_suspend, | ||
379 | .resume = lpc32xx_ts_resume, | ||
380 | }; | ||
381 | #define LPC32XX_TS_PM_OPS (&lpc32xx_ts_pm_ops) | ||
382 | #else | ||
383 | #define LPC32XX_TS_PM_OPS NULL | ||
384 | #endif | ||
385 | |||
386 | static struct platform_driver lpc32xx_ts_driver = { | ||
387 | .probe = lpc32xx_ts_probe, | ||
388 | .remove = __devexit_p(lpc32xx_ts_remove), | ||
389 | .driver = { | ||
390 | .name = MOD_NAME, | ||
391 | .owner = THIS_MODULE, | ||
392 | .pm = LPC32XX_TS_PM_OPS, | ||
393 | }, | ||
394 | }; | ||
395 | |||
396 | static int __init lpc32xx_ts_init(void) | ||
397 | { | ||
398 | return platform_driver_register(&lpc32xx_ts_driver); | ||
399 | } | ||
400 | module_init(lpc32xx_ts_init); | ||
401 | |||
402 | static void __exit lpc32xx_ts_exit(void) | ||
403 | { | ||
404 | platform_driver_unregister(&lpc32xx_ts_driver); | ||
405 | } | ||
406 | module_exit(lpc32xx_ts_exit); | ||
407 | |||
408 | MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com"); | ||
409 | MODULE_DESCRIPTION("LPC32XX TSC Driver"); | ||
410 | MODULE_LICENSE("GPL"); | ||
411 | MODULE_ALIAS("platform:lpc32xx_ts"); | ||