Date: | 2010-09-05 20:45:08 (13 years 6 months ago) |
---|---|
Author: | Lars C. |
Commit: | dc7f0a5b12cc8dd7e0a5e8045f1a6cc55e35198f |
Message: | input: Add touchscreen driver for the JZ4740 SoC This patch adds a touchscreen driver for the Ingenic JZ4740 SoC. The touchscreen controller is part of the ADC unit and thus this driver is a mfd cell from the jz4740-adc driver. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> |
Files: |
drivers/input/touchscreen/Kconfig (1 diff) drivers/input/touchscreen/Makefile (1 diff) drivers/input/touchscreen/jz4740-ts.c (1 diff) |
Change Details
drivers/input/touchscreen/Kconfig | ||
---|---|---|
681 | 681 | To compile this driver as a module, choose M here: the |
682 | 682 | module will be called stmpe-ts. |
683 | 683 | |
684 | config TOUCHSCREEN_JZ4740 | |
685 | tristate "JZ4740 touchscreen support" | |
686 | depends on MFD_JZ4740_ADC | |
687 | help | |
688 | Say Y here if you want support for the touchscreen controller found on | |
689 | Ingenic JZ4740 SoCs. | |
690 | ||
691 | If unsure, say N. | |
692 | ||
693 | To compile this driver as a module, choose M here: the | |
694 | module will be called jz4740-ts. | |
695 | ||
684 | 696 | endif |
drivers/input/touchscreen/Makefile | ||
---|---|---|
26 | 26 | obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o |
27 | 27 | obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o |
28 | 28 | obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o |
29 | obj-$(CONFIG_TOUCHSCREEN_JZ4740) += jz4740-ts.o | |
29 | 30 | obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o |
30 | 31 | obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o |
31 | 32 | obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o |
drivers/input/touchscreen/jz4740-ts.c | ||
---|---|---|
1 | /* | |
2 | * Touchscreen driver for Ingenic JZ SoCs. | |
3 | * | |
4 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | |
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 version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | */ | |
11 | ||
12 | #include <linux/interrupt.h> | |
13 | #include <linux/kernel.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/platform_device.h> | |
16 | #include <linux/slab.h> | |
17 | ||
18 | #include <linux/delay.h> | |
19 | #include <linux/mfd/core.h> | |
20 | #include <linux/input.h> | |
21 | #include <linux/bitops.h> | |
22 | #include <linux/jz4740-adc.h> | |
23 | ||
24 | struct jz4740_ts { | |
25 | struct platform_device *pdev; | |
26 | ||
27 | struct resource *mem; | |
28 | void __iomem *base; | |
29 | ||
30 | int irq_penup; | |
31 | int irq_pendown; | |
32 | int irq_data_ready; | |
33 | ||
34 | struct mfd_cell *cell; | |
35 | struct input_dev *input; | |
36 | ||
37 | bool is_open; | |
38 | }; | |
39 | ||
40 | static irqreturn_t jz4740_ts_data_ready_irq_handler(int irq, void *devid) | |
41 | { | |
42 | struct jz4740_ts *jz4740_ts = devid; | |
43 | uint32_t data; | |
44 | unsigned long x, y, z1, z2, pressure; | |
45 | ||
46 | data = readl(jz4740_ts->base + 0x08); | |
47 | x = data & 0xfff; | |
48 | y = (data >> 16) & 0xfff; | |
49 | ||
50 | data = readl(jz4740_ts->base + 0x08); | |
51 | z1 = data & 0xfff; | |
52 | z2 = (data >> 16) & 0xfff; | |
53 | if (z1 == 0) { | |
54 | pressure = 4095UL; | |
55 | } else if (z1 > z2) { | |
56 | pressure = 0; | |
57 | } else { | |
58 | if (data & 0x8000) | |
59 | pressure = (((480UL * x * z2) / z1) - 480UL * x) / 4096UL; | |
60 | else | |
61 | pressure = (((272UL * y * z2) / z1) - 272UL * y) / 4096UL; | |
62 | if (pressure >= 4096UL) | |
63 | pressure = 4095UL; | |
64 | pressure = 4095UL - pressure; | |
65 | } | |
66 | ||
67 | input_report_abs(jz4740_ts->input, ABS_X, y); | |
68 | input_report_abs(jz4740_ts->input, ABS_Y, 4095 - x); | |
69 | input_report_abs(jz4740_ts->input, ABS_PRESSURE, pressure); | |
70 | input_report_key(jz4740_ts->input, BTN_TOUCH, 1); | |
71 | input_sync(jz4740_ts->input); | |
72 | ||
73 | return IRQ_HANDLED; | |
74 | } | |
75 | ||
76 | static irqreturn_t jz4740_ts_pen_irq_handler(int irq, void *devid) | |
77 | { | |
78 | struct jz4740_ts *jz4740_ts = devid; | |
79 | int is_pressed; | |
80 | ||
81 | if (irq == jz4740_ts->irq_penup) { | |
82 | enable_irq(jz4740_ts->irq_pendown); | |
83 | is_pressed = 0; | |
84 | } else { | |
85 | enable_irq(jz4740_ts->irq_penup); | |
86 | is_pressed = 1; | |
87 | } | |
88 | disable_irq_nosync(irq); | |
89 | ||
90 | printk("pen irq: %d\n", irq); | |
91 | input_report_key(jz4740_ts->input, BTN_TOUCH, is_pressed); | |
92 | if (is_pressed == 0) | |
93 | input_report_abs(jz4740_ts->input, ABS_PRESSURE, 0); | |
94 | input_sync(jz4740_ts->input); | |
95 | ||
96 | return IRQ_HANDLED; | |
97 | } | |
98 | ||
99 | static int jz4740_ts_open(struct input_dev *input) | |
100 | { | |
101 | struct jz4740_ts *jz4740_ts = input_get_drvdata(input); | |
102 | ||
103 | jz4740_ts->is_open = true; | |
104 | jz4740_ts->cell->enable(jz4740_ts->pdev); | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
109 | static void jz4740_ts_close(struct input_dev *input) | |
110 | { | |
111 | struct jz4740_ts *jz4740_ts = input_get_drvdata(input); | |
112 | ||
113 | jz4740_ts->cell->disable(jz4740_ts->pdev); | |
114 | jz4740_ts->is_open = false; | |
115 | } | |
116 | ||
117 | static int __devinit jz4740_ts_probe(struct platform_device *pdev) | |
118 | { | |
119 | int ret = 0; | |
120 | struct jz4740_ts *jz4740_ts; | |
121 | struct input_dev *input; | |
122 | ||
123 | jz4740_ts = kzalloc(sizeof(*jz4740_ts), GFP_KERNEL); | |
124 | if (!jz4740_ts) { | |
125 | dev_err(&pdev->dev, "Failed to allocate driver structure\n"); | |
126 | return -ENOMEM; | |
127 | } | |
128 | ||
129 | jz4740_ts->pdev = pdev; | |
130 | jz4740_ts->cell = pdev->dev.platform_data; | |
131 | ||
132 | jz4740_ts->irq_data_ready = platform_get_irq(pdev, 0); | |
133 | if (jz4740_ts->irq_data_ready < 0) { | |
134 | ret = jz4740_ts->irq_data_ready; | |
135 | dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); | |
136 | goto err_free; | |
137 | } | |
138 | ||
139 | jz4740_ts->irq_penup = platform_get_irq(pdev, 1); | |
140 | if (jz4740_ts->irq_penup < 0) { | |
141 | ret = jz4740_ts->irq_penup; | |
142 | dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); | |
143 | goto err_free; | |
144 | } | |
145 | ||
146 | jz4740_ts->irq_pendown = platform_get_irq(pdev, 2); | |
147 | if (jz4740_ts->irq_pendown < 0) { | |
148 | ret = jz4740_ts->irq_pendown; | |
149 | dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); | |
150 | goto err_free; | |
151 | } | |
152 | ||
153 | jz4740_ts->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
154 | if (!jz4740_ts->mem) { | |
155 | ret = -ENOENT; | |
156 | dev_err(&pdev->dev, "Failed to get platform mmio resource\n"); | |
157 | goto err_free; | |
158 | } | |
159 | ||
160 | jz4740_ts->mem = request_mem_region(jz4740_ts->mem->start, | |
161 | resource_size(jz4740_ts->mem), pdev->name); | |
162 | if (!jz4740_ts->mem) { | |
163 | ret = -EBUSY; | |
164 | dev_err(&pdev->dev, "Failed to request mmio memory region\n"); | |
165 | goto err_free; | |
166 | } | |
167 | ||
168 | jz4740_ts->base = ioremap_nocache(jz4740_ts->mem->start, | |
169 | resource_size(jz4740_ts->mem)); | |
170 | if (!jz4740_ts->base) { | |
171 | ret = -EBUSY; | |
172 | dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); | |
173 | goto err_release_mem_region; | |
174 | } | |
175 | ||
176 | input = input_allocate_device(); | |
177 | if (!input) { | |
178 | dev_err(&pdev->dev, "Failed to allocate input device\n"); | |
179 | ret = -ENOMEM; | |
180 | goto err_iounmap; | |
181 | } | |
182 | ||
183 | input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); | |
184 | __set_bit(BTN_TOUCH, input->keybit); | |
185 | ||
186 | input_set_abs_params(input, ABS_X, 150, 3920, 0, 0); | |
187 | input_set_abs_params(input, ABS_Y, 270, 3700, 0, 0); | |
188 | input_set_abs_params(input, ABS_PRESSURE, 0, 4096, 0, 0); | |
189 | ||
190 | input->name = pdev->name; | |
191 | input->phys = "jz4740"; | |
192 | input->id.bustype = BUS_HOST; | |
193 | input->dev.parent = &pdev->dev; | |
194 | ||
195 | input->open = jz4740_ts_open; | |
196 | input->close = jz4740_ts_close; | |
197 | ||
198 | input_set_drvdata(input, jz4740_ts); | |
199 | ||
200 | ret = input_register_device(input); | |
201 | if (ret) { | |
202 | dev_err(&pdev->dev, "Failed to register input device: %d\n", ret); | |
203 | input_free_device(input); | |
204 | goto err_iounmap; | |
205 | } | |
206 | jz4740_ts->input = input; | |
207 | ||
208 | ret = request_irq(jz4740_ts->irq_data_ready, jz4740_ts_data_ready_irq_handler, 0, pdev->name, | |
209 | jz4740_ts); | |
210 | if (ret) { | |
211 | dev_err(&pdev->dev, "Failed to request irq %d\n", ret); | |
212 | goto err_input_unregister_device; | |
213 | } | |
214 | ret = request_irq(jz4740_ts->irq_penup, jz4740_ts_pen_irq_handler, 0, pdev->name, | |
215 | jz4740_ts); | |
216 | if (ret) { | |
217 | dev_err(&pdev->dev, "Failed to request irq %d\n", ret); | |
218 | goto err_free_irq_data_ready; | |
219 | } | |
220 | disable_irq(jz4740_ts->irq_penup); | |
221 | ret = request_irq(jz4740_ts->irq_pendown, jz4740_ts_pen_irq_handler, 0, pdev->name, | |
222 | jz4740_ts); | |
223 | if (ret) { | |
224 | dev_err(&pdev->dev, "Failed to request irq %d\n", ret); | |
225 | goto err_free_irq_penup; | |
226 | } | |
227 | platform_set_drvdata(pdev, jz4740_ts); | |
228 | ||
229 | jz4740_adc_set_config(pdev->dev.parent, | |
230 | JZ_ADC_CONFIG_EX_IN | JZ_ADC_CONFIG_XYZ_OFFSET(2) | JZ_ADC_CONFIG_DNUM(7), | |
231 | JZ_ADC_CONFIG_EX_IN | JZ_ADC_CONFIG_XYZ_MASK | JZ_ADC_CONFIG_DNUM_MASK); | |
232 | ||
233 | ||
234 | writel(0x15e, jz4740_ts->base); | |
235 | writel(0x32, jz4740_ts->base + 0x04); | |
236 | ||
237 | return 0; | |
238 | ||
239 | err_free_irq_penup: | |
240 | free_irq(jz4740_ts->irq_penup, jz4740_ts); | |
241 | err_free_irq_data_ready: | |
242 | free_irq(jz4740_ts->irq_data_ready, jz4740_ts); | |
243 | err_input_unregister_device: | |
244 | input_unregister_device(jz4740_ts->input); | |
245 | err_iounmap: | |
246 | platform_set_drvdata(pdev, NULL); | |
247 | iounmap(jz4740_ts->base); | |
248 | err_release_mem_region: | |
249 | release_mem_region(jz4740_ts->mem->start, resource_size(jz4740_ts->mem)); | |
250 | err_free: | |
251 | kfree(jz4740_ts); | |
252 | return ret; | |
253 | } | |
254 | ||
255 | static int __devexit jz4740_ts_remove(struct platform_device *pdev) | |
256 | { | |
257 | struct jz4740_ts *jz4740_ts = platform_get_drvdata(pdev); | |
258 | ||
259 | ||
260 | free_irq(jz4740_ts->irq_pendown, jz4740_ts); | |
261 | free_irq(jz4740_ts->irq_penup, jz4740_ts); | |
262 | free_irq(jz4740_ts->irq_data_ready, jz4740_ts); | |
263 | ||
264 | input_unregister_device(jz4740_ts->input); | |
265 | ||
266 | iounmap(jz4740_ts->base); | |
267 | release_mem_region(jz4740_ts->mem->start, resource_size(jz4740_ts->mem)); | |
268 | ||
269 | kfree(jz4740_ts); | |
270 | ||
271 | return 0; | |
272 | } | |
273 | ||
274 | #ifdef CONFIG_PM | |
275 | static int jz4740_ts_suspend(struct device *dev) | |
276 | { | |
277 | struct jz4740_ts *jz4740_ts = dev_get_drvdata(dev); | |
278 | ||
279 | if (jz4740_ts->is_open); | |
280 | jz4740_ts->cell->disable(jz4740_ts->pdev); | |
281 | ||
282 | return 0; | |
283 | } | |
284 | ||
285 | static int jz4740_ts_resume(struct device *dev) | |
286 | { | |
287 | struct jz4740_ts *jz4740_ts = dev_get_drvdata(dev); | |
288 | ||
289 | if (jz4740_ts->is_open); | |
290 | jz4740_ts->cell->enable(jz4740_ts->pdev); | |
291 | ||
292 | return 0; | |
293 | } | |
294 | ||
295 | static const struct dev_pm_ops jz4740_ts_pm_ops = { | |
296 | .suspend = jz4740_ts_suspend, | |
297 | .resume = jz4740_ts_resume, | |
298 | }; | |
299 | ||
300 | #define JZ4740_TS_PM_OPS (&jz4740_ts_pm_ops) | |
301 | #else | |
302 | #define JZ4740_TS_PM_OPS NULL | |
303 | #endif | |
304 | ||
305 | static struct platform_driver jz4740_ts_driver = { | |
306 | .probe = jz4740_ts_probe, | |
307 | .remove = __devexit_p(jz4740_ts_remove), | |
308 | .driver = { | |
309 | .name = "jz4740-ts", | |
310 | .owner = THIS_MODULE, | |
311 | .pm = JZ4740_TS_PM_OPS, | |
312 | }, | |
313 | }; | |
314 | ||
315 | static int __init jz4740_ts_init(void) | |
316 | { | |
317 | return platform_driver_register(&jz4740_ts_driver); | |
318 | } | |
319 | module_init(jz4740_ts_init); | |
320 | ||
321 | static void __exit jz4740_ts_exit(void) | |
322 | { | |
323 | platform_driver_unregister(&jz4740_ts_driver); | |
324 | } | |
325 | module_exit(jz4740_ts_exit); | |
326 | ||
327 | MODULE_ALIAS("platform:jz4740-ts"); | |
328 | MODULE_LICENSE("GPL"); | |
329 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | |
330 | MODULE_DESCRIPTION("JZ4740 SoC battery driver"); |
Branches:
ben-wpan
ben-wpan-stefan
5396a9238205f20f811ea57898980d3ca82df0b6
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9