Root/
Source at commit dc637c9 created 12 years 4 months ago. By Xiangfu Liu, xburst: ben nanonote: first add WPAN(atben,atusb) driver take from qi-kernel.git thanks to Werner | |
---|---|
1 | From 71e0648ad022c6f05e1bed1fcca69abc8abde4d9 Mon Sep 17 00:00:00 2001 |
2 | From: Lars-Peter Clausen <lars@metafoo.de> |
3 | Date: Sun, 5 Sep 2010 20:45:08 +0200 |
4 | Subject: [PATCH 11/32] input: Add touchscreen driver for the JZ4740 SoC |
5 | |
6 | This patch adds a touchscreen driver for the Ingenic JZ4740 SoC. |
7 | The touchscreen controller is part of the ADC unit and thus this driver is a mfd |
8 | cell from the jz4740-adc driver. |
9 | |
10 | Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> |
11 | --- |
12 | drivers/input/touchscreen/Kconfig | 12 ++ |
13 | drivers/input/touchscreen/Makefile | 1 + |
14 | drivers/input/touchscreen/jz4740-ts.c | 330 +++++++++++++++++++++++++++++++++ |
15 | 3 files changed, 343 insertions(+), 0 deletions(-) |
16 | create mode 100644 drivers/input/touchscreen/jz4740-ts.c |
17 | |
18 | diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig |
19 | index cabd9e5..10836e5 100644 |
20 | --- a/drivers/input/touchscreen/Kconfig |
21 | +++ b/drivers/input/touchscreen/Kconfig |
22 | @@ -726,4 +726,16 @@ config TOUCHSCREEN_TPS6507X |
23 | To compile this driver as a module, choose M here: the |
24 | module will be called tps6507x_ts. |
25 | |
26 | +config TOUCHSCREEN_JZ4740 |
27 | + tristate "JZ4740 touchscreen support" |
28 | + depends on MFD_JZ4740_ADC |
29 | + help |
30 | + Say Y here if you want support for the touchscreen controller found on |
31 | + Ingenic JZ4740 SoCs. |
32 | + |
33 | + If unsure, say N. |
34 | + |
35 | + To compile this driver as a module, choose M here: the |
36 | + module will be called jz4740-ts. |
37 | + |
38 | endif |
39 | diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile |
40 | index 282d6f7..d8cc316 100644 |
41 | --- a/drivers/input/touchscreen/Makefile |
42 | +++ b/drivers/input/touchscreen/Makefile |
43 | @@ -27,6 +27,7 @@ obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o |
44 | obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o |
45 | obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o |
46 | obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o |
47 | +obj-$(CONFIG_TOUCHSCREEN_JZ4740) += jz4740-ts.o |
48 | obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o |
49 | obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o |
50 | obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o |
51 | diff --git a/drivers/input/touchscreen/jz4740-ts.c b/drivers/input/touchscreen/jz4740-ts.c |
52 | new file mode 100644 |
53 | index 0000000..dd5de34 |
54 | --- /dev/null |
55 | +++ b/drivers/input/touchscreen/jz4740-ts.c |
56 | @@ -0,0 +1,330 @@ |
57 | +/* |
58 | + * Touchscreen driver for Ingenic JZ SoCs. |
59 | + * |
60 | + * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> |
61 | + * |
62 | + * This program is free software; you can redistribute it and/or modify |
63 | + * it under the terms of the GNU General Public License version 2 as |
64 | + * published by the Free Software Foundation. |
65 | + * |
66 | + */ |
67 | + |
68 | +#include <linux/interrupt.h> |
69 | +#include <linux/kernel.h> |
70 | +#include <linux/module.h> |
71 | +#include <linux/platform_device.h> |
72 | +#include <linux/slab.h> |
73 | + |
74 | +#include <linux/delay.h> |
75 | +#include <linux/mfd/core.h> |
76 | +#include <linux/input.h> |
77 | +#include <linux/bitops.h> |
78 | +#include <linux/jz4740-adc.h> |
79 | + |
80 | +struct jz4740_ts { |
81 | + struct platform_device *pdev; |
82 | + |
83 | + struct resource *mem; |
84 | + void __iomem *base; |
85 | + |
86 | + int irq_penup; |
87 | + int irq_pendown; |
88 | + int irq_data_ready; |
89 | + |
90 | + struct mfd_cell *cell; |
91 | + struct input_dev *input; |
92 | + |
93 | + bool is_open; |
94 | +}; |
95 | + |
96 | +static irqreturn_t jz4740_ts_data_ready_irq_handler(int irq, void *devid) |
97 | +{ |
98 | + struct jz4740_ts *jz4740_ts = devid; |
99 | + uint32_t data; |
100 | + unsigned long x, y, z1, z2, pressure; |
101 | + |
102 | + data = readl(jz4740_ts->base + 0x08); |
103 | + x = data & 0xfff; |
104 | + y = (data >> 16) & 0xfff; |
105 | + |
106 | + data = readl(jz4740_ts->base + 0x08); |
107 | + z1 = data & 0xfff; |
108 | + z2 = (data >> 16) & 0xfff; |
109 | + if (z1 == 0) { |
110 | + pressure = 4095UL; |
111 | + } else if (z1 > z2) { |
112 | + pressure = 0; |
113 | + } else { |
114 | + if (data & 0x8000) |
115 | + pressure = (((480UL * x * z2) / z1) - 480UL * x) / 4096UL; |
116 | + else |
117 | + pressure = (((272UL * y * z2) / z1) - 272UL * y) / 4096UL; |
118 | + if (pressure >= 4096UL) |
119 | + pressure = 4095UL; |
120 | + pressure = 4095UL - pressure; |
121 | + } |
122 | + |
123 | + input_report_abs(jz4740_ts->input, ABS_X, y); |
124 | + input_report_abs(jz4740_ts->input, ABS_Y, 4095 - x); |
125 | + input_report_abs(jz4740_ts->input, ABS_PRESSURE, pressure); |
126 | + input_report_key(jz4740_ts->input, BTN_TOUCH, 1); |
127 | + input_sync(jz4740_ts->input); |
128 | + |
129 | + return IRQ_HANDLED; |
130 | +} |
131 | + |
132 | +static irqreturn_t jz4740_ts_pen_irq_handler(int irq, void *devid) |
133 | +{ |
134 | + struct jz4740_ts *jz4740_ts = devid; |
135 | + int is_pressed; |
136 | + |
137 | + if (irq == jz4740_ts->irq_penup) { |
138 | + enable_irq(jz4740_ts->irq_pendown); |
139 | + is_pressed = 0; |
140 | + } else { |
141 | + enable_irq(jz4740_ts->irq_penup); |
142 | + is_pressed = 1; |
143 | + } |
144 | + disable_irq_nosync(irq); |
145 | + |
146 | + printk("pen irq: %d\n", irq); |
147 | + input_report_key(jz4740_ts->input, BTN_TOUCH, is_pressed); |
148 | + if (is_pressed == 0) |
149 | + input_report_abs(jz4740_ts->input, ABS_PRESSURE, 0); |
150 | + input_sync(jz4740_ts->input); |
151 | + |
152 | + return IRQ_HANDLED; |
153 | +} |
154 | + |
155 | +static int jz4740_ts_open(struct input_dev *input) |
156 | +{ |
157 | + struct jz4740_ts *jz4740_ts = input_get_drvdata(input); |
158 | + |
159 | + jz4740_ts->is_open = true; |
160 | + jz4740_ts->cell->enable(jz4740_ts->pdev); |
161 | + |
162 | + return 0; |
163 | +} |
164 | + |
165 | +static void jz4740_ts_close(struct input_dev *input) |
166 | +{ |
167 | + struct jz4740_ts *jz4740_ts = input_get_drvdata(input); |
168 | + |
169 | + jz4740_ts->cell->disable(jz4740_ts->pdev); |
170 | + jz4740_ts->is_open = false; |
171 | +} |
172 | + |
173 | +static int __devinit jz4740_ts_probe(struct platform_device *pdev) |
174 | +{ |
175 | + int ret = 0; |
176 | + struct jz4740_ts *jz4740_ts; |
177 | + struct input_dev *input; |
178 | + |
179 | + jz4740_ts = kzalloc(sizeof(*jz4740_ts), GFP_KERNEL); |
180 | + if (!jz4740_ts) { |
181 | + dev_err(&pdev->dev, "Failed to allocate driver structure\n"); |
182 | + return -ENOMEM; |
183 | + } |
184 | + |
185 | + jz4740_ts->pdev = pdev; |
186 | + jz4740_ts->cell = pdev->dev.platform_data; |
187 | + |
188 | + jz4740_ts->irq_data_ready = platform_get_irq(pdev, 0); |
189 | + if (jz4740_ts->irq_data_ready < 0) { |
190 | + ret = jz4740_ts->irq_data_ready; |
191 | + dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); |
192 | + goto err_free; |
193 | + } |
194 | + |
195 | + jz4740_ts->irq_penup = platform_get_irq(pdev, 1); |
196 | + if (jz4740_ts->irq_penup < 0) { |
197 | + ret = jz4740_ts->irq_penup; |
198 | + dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); |
199 | + goto err_free; |
200 | + } |
201 | + |
202 | + jz4740_ts->irq_pendown = platform_get_irq(pdev, 2); |
203 | + if (jz4740_ts->irq_pendown < 0) { |
204 | + ret = jz4740_ts->irq_pendown; |
205 | + dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); |
206 | + goto err_free; |
207 | + } |
208 | + |
209 | + jz4740_ts->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
210 | + if (!jz4740_ts->mem) { |
211 | + ret = -ENOENT; |
212 | + dev_err(&pdev->dev, "Failed to get platform mmio resource\n"); |
213 | + goto err_free; |
214 | + } |
215 | + |
216 | + jz4740_ts->mem = request_mem_region(jz4740_ts->mem->start, |
217 | + resource_size(jz4740_ts->mem), pdev->name); |
218 | + if (!jz4740_ts->mem) { |
219 | + ret = -EBUSY; |
220 | + dev_err(&pdev->dev, "Failed to request mmio memory region\n"); |
221 | + goto err_free; |
222 | + } |
223 | + |
224 | + jz4740_ts->base = ioremap_nocache(jz4740_ts->mem->start, |
225 | + resource_size(jz4740_ts->mem)); |
226 | + if (!jz4740_ts->base) { |
227 | + ret = -EBUSY; |
228 | + dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); |
229 | + goto err_release_mem_region; |
230 | + } |
231 | + |
232 | + input = input_allocate_device(); |
233 | + if (!input) { |
234 | + dev_err(&pdev->dev, "Failed to allocate input device\n"); |
235 | + ret = -ENOMEM; |
236 | + goto err_iounmap; |
237 | + } |
238 | + |
239 | + input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); |
240 | + __set_bit(BTN_TOUCH, input->keybit); |
241 | + |
242 | + input_set_abs_params(input, ABS_X, 150, 3920, 0, 0); |
243 | + input_set_abs_params(input, ABS_Y, 270, 3700, 0, 0); |
244 | + input_set_abs_params(input, ABS_PRESSURE, 0, 4096, 0, 0); |
245 | + |
246 | + input->name = pdev->name; |
247 | + input->phys = "jz4740"; |
248 | + input->id.bustype = BUS_HOST; |
249 | + input->dev.parent = &pdev->dev; |
250 | + |
251 | + input->open = jz4740_ts_open; |
252 | + input->close = jz4740_ts_close; |
253 | + |
254 | + input_set_drvdata(input, jz4740_ts); |
255 | + |
256 | + ret = input_register_device(input); |
257 | + if (ret) { |
258 | + dev_err(&pdev->dev, "Failed to register input device: %d\n", ret); |
259 | + input_free_device(input); |
260 | + goto err_iounmap; |
261 | + } |
262 | + jz4740_ts->input = input; |
263 | + |
264 | + ret = request_irq(jz4740_ts->irq_data_ready, jz4740_ts_data_ready_irq_handler, 0, pdev->name, |
265 | + jz4740_ts); |
266 | + if (ret) { |
267 | + dev_err(&pdev->dev, "Failed to request irq %d\n", ret); |
268 | + goto err_input_unregister_device; |
269 | + } |
270 | + ret = request_irq(jz4740_ts->irq_penup, jz4740_ts_pen_irq_handler, 0, pdev->name, |
271 | + jz4740_ts); |
272 | + if (ret) { |
273 | + dev_err(&pdev->dev, "Failed to request irq %d\n", ret); |
274 | + goto err_free_irq_data_ready; |
275 | + } |
276 | + disable_irq(jz4740_ts->irq_penup); |
277 | + ret = request_irq(jz4740_ts->irq_pendown, jz4740_ts_pen_irq_handler, 0, pdev->name, |
278 | + jz4740_ts); |
279 | + if (ret) { |
280 | + dev_err(&pdev->dev, "Failed to request irq %d\n", ret); |
281 | + goto err_free_irq_penup; |
282 | + } |
283 | + platform_set_drvdata(pdev, jz4740_ts); |
284 | + |
285 | + jz4740_adc_set_config(pdev->dev.parent, |
286 | + JZ_ADC_CONFIG_EX_IN | JZ_ADC_CONFIG_XYZ_OFFSET(2) | JZ_ADC_CONFIG_DNUM(7), |
287 | + JZ_ADC_CONFIG_EX_IN | JZ_ADC_CONFIG_XYZ_MASK | JZ_ADC_CONFIG_DNUM_MASK); |
288 | + |
289 | + |
290 | + writel(0x15e, jz4740_ts->base); |
291 | + writel(0x32, jz4740_ts->base + 0x04); |
292 | + |
293 | + return 0; |
294 | + |
295 | +err_free_irq_penup: |
296 | + free_irq(jz4740_ts->irq_penup, jz4740_ts); |
297 | +err_free_irq_data_ready: |
298 | + free_irq(jz4740_ts->irq_data_ready, jz4740_ts); |
299 | +err_input_unregister_device: |
300 | + input_unregister_device(jz4740_ts->input); |
301 | +err_iounmap: |
302 | + platform_set_drvdata(pdev, NULL); |
303 | + iounmap(jz4740_ts->base); |
304 | +err_release_mem_region: |
305 | + release_mem_region(jz4740_ts->mem->start, resource_size(jz4740_ts->mem)); |
306 | +err_free: |
307 | + kfree(jz4740_ts); |
308 | + return ret; |
309 | +} |
310 | + |
311 | +static int __devexit jz4740_ts_remove(struct platform_device *pdev) |
312 | +{ |
313 | + struct jz4740_ts *jz4740_ts = platform_get_drvdata(pdev); |
314 | + |
315 | + |
316 | + free_irq(jz4740_ts->irq_pendown, jz4740_ts); |
317 | + free_irq(jz4740_ts->irq_penup, jz4740_ts); |
318 | + free_irq(jz4740_ts->irq_data_ready, jz4740_ts); |
319 | + |
320 | + input_unregister_device(jz4740_ts->input); |
321 | + |
322 | + iounmap(jz4740_ts->base); |
323 | + release_mem_region(jz4740_ts->mem->start, resource_size(jz4740_ts->mem)); |
324 | + |
325 | + kfree(jz4740_ts); |
326 | + |
327 | + return 0; |
328 | +} |
329 | + |
330 | +#ifdef CONFIG_PM |
331 | +static int jz4740_ts_suspend(struct device *dev) |
332 | +{ |
333 | + struct jz4740_ts *jz4740_ts = dev_get_drvdata(dev); |
334 | + |
335 | + if (jz4740_ts->is_open); |
336 | + jz4740_ts->cell->disable(jz4740_ts->pdev); |
337 | + |
338 | + return 0; |
339 | +} |
340 | + |
341 | +static int jz4740_ts_resume(struct device *dev) |
342 | +{ |
343 | + struct jz4740_ts *jz4740_ts = dev_get_drvdata(dev); |
344 | + |
345 | + if (jz4740_ts->is_open); |
346 | + jz4740_ts->cell->enable(jz4740_ts->pdev); |
347 | + |
348 | + return 0; |
349 | +} |
350 | + |
351 | +static const struct dev_pm_ops jz4740_ts_pm_ops = { |
352 | + .suspend = jz4740_ts_suspend, |
353 | + .resume = jz4740_ts_resume, |
354 | +}; |
355 | + |
356 | +#define JZ4740_TS_PM_OPS (&jz4740_ts_pm_ops) |
357 | +#else |
358 | +#define JZ4740_TS_PM_OPS NULL |
359 | +#endif |
360 | + |
361 | +static struct platform_driver jz4740_ts_driver = { |
362 | + .probe = jz4740_ts_probe, |
363 | + .remove = __devexit_p(jz4740_ts_remove), |
364 | + .driver = { |
365 | + .name = "jz4740-ts", |
366 | + .owner = THIS_MODULE, |
367 | + .pm = JZ4740_TS_PM_OPS, |
368 | + }, |
369 | +}; |
370 | + |
371 | +static int __init jz4740_ts_init(void) |
372 | +{ |
373 | + return platform_driver_register(&jz4740_ts_driver); |
374 | +} |
375 | +module_init(jz4740_ts_init); |
376 | + |
377 | +static void __exit jz4740_ts_exit(void) |
378 | +{ |
379 | + platform_driver_unregister(&jz4740_ts_driver); |
380 | +} |
381 | +module_exit(jz4740_ts_exit); |
382 | + |
383 | +MODULE_ALIAS("platform:jz4740-ts"); |
384 | +MODULE_LICENSE("GPL"); |
385 | +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); |
386 | +MODULE_DESCRIPTION("JZ4740 SoC battery driver"); |
387 | -- |
388 | 1.7.4.1 |
389 | |
390 |