diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2008-04-02 00:51:46 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2008-04-02 00:51:46 -0400 |
commit | 4db8a5f21e5149e09949516eef98b78b68880075 (patch) | |
tree | 4789c01b57125460881158b2e3179efa1e8fea4a | |
parent | dca98e91fb83a43fc430893f349fd8248fa0ba38 (diff) |
Input: WM97xx - add support for streaming mode on Mainstone
Signed-off-by: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
Signed-off-by: Graeme Gregory <gg@opensource.wolfsonmicro.com>
Signed-off-by: Mike Arthur <mike.arthur@wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 12 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/mainstone-wm97xx.c | 302 |
3 files changed, 315 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index f6e8dad9726b..565ec711c2ee 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig | |||
@@ -226,6 +226,18 @@ config TOUCHSCREEN_WM9713 | |||
226 | 226 | ||
227 | If unsure, say N. | 227 | If unsure, say N. |
228 | 228 | ||
229 | config TOUCHSCREEN_WM97XX_MAINSTONE | ||
230 | tristate "WM97xx Mainstone accelerated touch" | ||
231 | depends on TOUCHSCREEN_WM97XX && ARCH_PXA | ||
232 | help | ||
233 | Say Y here for support for streaming mode with WM97xx touchscreens | ||
234 | on Mainstone systems. | ||
235 | |||
236 | If unsure, say N. | ||
237 | |||
238 | To compile this driver as a module, choose M here: the | ||
239 | module will be called mainstone-wm97xx. | ||
240 | |||
229 | config TOUCHSCREEN_USB_COMPOSITE | 241 | config TOUCHSCREEN_USB_COMPOSITE |
230 | tristate "USB Touchscreen Driver" | 242 | tristate "USB Touchscreen Driver" |
231 | depends on USB_ARCH_HAS_HCD | 243 | depends on USB_ARCH_HAS_HCD |
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index f1bc82941cc6..3c096d75651d 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile | |||
@@ -25,3 +25,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o | |||
25 | wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o | 25 | wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o |
26 | wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o | 26 | wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o |
27 | wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o | 27 | wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o |
28 | obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o | ||
diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c new file mode 100644 index 000000000000..a79f029b91c0 --- /dev/null +++ b/drivers/input/touchscreen/mainstone-wm97xx.c | |||
@@ -0,0 +1,302 @@ | |||
1 | /* | ||
2 | * mainstone-wm97xx.c -- Mainstone Continuous Touch screen driver for | ||
3 | * Wolfson WM97xx AC97 Codecs. | ||
4 | * | ||
5 | * Copyright 2004, 2007 Wolfson Microelectronics PLC. | ||
6 | * Author: Liam Girdwood | ||
7 | * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com | ||
8 | * Parts Copyright : Ian Molton <spyro@f2s.com> | ||
9 | * Andrew Zabolotny <zap@homelink.ru> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify it | ||
12 | * under the terms of the GNU General Public License as published by the | ||
13 | * Free Software Foundation; either version 2 of the License, or (at your | ||
14 | * option) any later version. | ||
15 | * | ||
16 | * Notes: | ||
17 | * This is a wm97xx extended touch driver to capture touch | ||
18 | * data in a continuous manner on the Intel XScale archictecture | ||
19 | * | ||
20 | * Features: | ||
21 | * - codecs supported:- WM9705, WM9712, WM9713 | ||
22 | * - processors supported:- Intel XScale PXA25x, PXA26x, PXA27x | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #include <linux/module.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <linux/version.h> | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/delay.h> | ||
32 | #include <linux/irq.h> | ||
33 | #include <linux/interrupt.h> | ||
34 | #include <linux/wm97xx.h> | ||
35 | #include <linux/io.h> | ||
36 | #include <asm/arch/pxa-regs.h> | ||
37 | |||
38 | #define VERSION "0.13" | ||
39 | |||
40 | struct continuous { | ||
41 | u16 id; /* codec id */ | ||
42 | u8 code; /* continuous code */ | ||
43 | u8 reads; /* number of coord reads per read cycle */ | ||
44 | u32 speed; /* number of coords per second */ | ||
45 | }; | ||
46 | |||
47 | #define WM_READS(sp) ((sp / HZ) + 1) | ||
48 | |||
49 | static const struct continuous cinfo[] = { | ||
50 | {WM9705_ID2, 0, WM_READS(94), 94}, | ||
51 | {WM9705_ID2, 1, WM_READS(188), 188}, | ||
52 | {WM9705_ID2, 2, WM_READS(375), 375}, | ||
53 | {WM9705_ID2, 3, WM_READS(750), 750}, | ||
54 | {WM9712_ID2, 0, WM_READS(94), 94}, | ||
55 | {WM9712_ID2, 1, WM_READS(188), 188}, | ||
56 | {WM9712_ID2, 2, WM_READS(375), 375}, | ||
57 | {WM9712_ID2, 3, WM_READS(750), 750}, | ||
58 | {WM9713_ID2, 0, WM_READS(94), 94}, | ||
59 | {WM9713_ID2, 1, WM_READS(120), 120}, | ||
60 | {WM9713_ID2, 2, WM_READS(154), 154}, | ||
61 | {WM9713_ID2, 3, WM_READS(188), 188}, | ||
62 | }; | ||
63 | |||
64 | /* continuous speed index */ | ||
65 | static int sp_idx; | ||
66 | static u16 last, tries; | ||
67 | |||
68 | /* | ||
69 | * Pen sampling frequency (Hz) in continuous mode. | ||
70 | */ | ||
71 | static int cont_rate = 200; | ||
72 | module_param(cont_rate, int, 0); | ||
73 | MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)"); | ||
74 | |||
75 | /* | ||
76 | * Pen down detection. | ||
77 | * | ||
78 | * This driver can either poll or use an interrupt to indicate a pen down | ||
79 | * event. If the irq request fails then it will fall back to polling mode. | ||
80 | */ | ||
81 | static int pen_int; | ||
82 | module_param(pen_int, int, 0); | ||
83 | MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)"); | ||
84 | |||
85 | /* | ||
86 | * Pressure readback. | ||
87 | * | ||
88 | * Set to 1 to read back pen down pressure | ||
89 | */ | ||
90 | static int pressure; | ||
91 | module_param(pressure, int, 0); | ||
92 | MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)"); | ||
93 | |||
94 | /* | ||
95 | * AC97 touch data slot. | ||
96 | * | ||
97 | * Touch screen readback data ac97 slot | ||
98 | */ | ||
99 | static int ac97_touch_slot = 5; | ||
100 | module_param(ac97_touch_slot, int, 0); | ||
101 | MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number"); | ||
102 | |||
103 | |||
104 | /* flush AC97 slot 5 FIFO on pxa machines */ | ||
105 | #ifdef CONFIG_PXA27x | ||
106 | static void wm97xx_acc_pen_up(struct wm97xx *wm) | ||
107 | { | ||
108 | schedule_timeout_uninterruptible(1); | ||
109 | |||
110 | while (MISR & (1 << 2)) | ||
111 | MODR; | ||
112 | } | ||
113 | #else | ||
114 | static void wm97xx_acc_pen_up(struct wm97xx *wm) | ||
115 | { | ||
116 | int count = 16; | ||
117 | schedule_timeout_uninterruptible(1); | ||
118 | |||
119 | while (count < 16) { | ||
120 | MODR; | ||
121 | count--; | ||
122 | } | ||
123 | } | ||
124 | #endif | ||
125 | |||
126 | static int wm97xx_acc_pen_down(struct wm97xx *wm) | ||
127 | { | ||
128 | u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES; | ||
129 | int reads = 0; | ||
130 | |||
131 | /* When the AC97 queue has been drained we need to allow time | ||
132 | * to buffer up samples otherwise we end up spinning polling | ||
133 | * for samples. The controller can't have a suitably low | ||
134 | * threashold set to use the notifications it gives. | ||
135 | */ | ||
136 | schedule_timeout_uninterruptible(1); | ||
137 | |||
138 | if (tries > 5) { | ||
139 | tries = 0; | ||
140 | return RC_PENUP; | ||
141 | } | ||
142 | |||
143 | x = MODR; | ||
144 | if (x == last) { | ||
145 | tries++; | ||
146 | return RC_AGAIN; | ||
147 | } | ||
148 | last = x; | ||
149 | do { | ||
150 | if (reads) | ||
151 | x = MODR; | ||
152 | y = MODR; | ||
153 | if (pressure) | ||
154 | p = MODR; | ||
155 | |||
156 | /* are samples valid */ | ||
157 | if ((x & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_X || | ||
158 | (y & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_Y || | ||
159 | (p & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_PRES) | ||
160 | goto up; | ||
161 | |||
162 | /* coordinate is good */ | ||
163 | tries = 0; | ||
164 | input_report_abs(wm->input_dev, ABS_X, x & 0xfff); | ||
165 | input_report_abs(wm->input_dev, ABS_Y, y & 0xfff); | ||
166 | input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff); | ||
167 | input_sync(wm->input_dev); | ||
168 | reads++; | ||
169 | } while (reads < cinfo[sp_idx].reads); | ||
170 | up: | ||
171 | return RC_PENDOWN | RC_AGAIN; | ||
172 | } | ||
173 | |||
174 | static int wm97xx_acc_startup(struct wm97xx *wm) | ||
175 | { | ||
176 | int idx = 0; | ||
177 | |||
178 | /* check we have a codec */ | ||
179 | if (wm->ac97 == NULL) | ||
180 | return -ENODEV; | ||
181 | |||
182 | /* Go you big red fire engine */ | ||
183 | for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) { | ||
184 | if (wm->id != cinfo[idx].id) | ||
185 | continue; | ||
186 | sp_idx = idx; | ||
187 | if (cont_rate <= cinfo[idx].speed) | ||
188 | break; | ||
189 | } | ||
190 | wm->acc_rate = cinfo[sp_idx].code; | ||
191 | wm->acc_slot = ac97_touch_slot; | ||
192 | dev_info(wm->dev, | ||
193 | "mainstone accelerated touchscreen driver, %d samples/sec\n", | ||
194 | cinfo[sp_idx].speed); | ||
195 | |||
196 | /* codec specific irq config */ | ||
197 | if (pen_int) { | ||
198 | switch (wm->id) { | ||
199 | case WM9705_ID2: | ||
200 | wm->pen_irq = IRQ_GPIO(4); | ||
201 | set_irq_type(IRQ_GPIO(4), IRQT_BOTHEDGE); | ||
202 | break; | ||
203 | case WM9712_ID2: | ||
204 | case WM9713_ID2: | ||
205 | /* enable pen down interrupt */ | ||
206 | /* use PEN_DOWN GPIO 13 to assert IRQ on GPIO line 2 */ | ||
207 | wm->pen_irq = MAINSTONE_AC97_IRQ; | ||
208 | wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN, | ||
209 | WM97XX_GPIO_POL_HIGH, | ||
210 | WM97XX_GPIO_STICKY, | ||
211 | WM97XX_GPIO_WAKE); | ||
212 | wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT, | ||
213 | WM97XX_GPIO_POL_HIGH, | ||
214 | WM97XX_GPIO_NOTSTICKY, | ||
215 | WM97XX_GPIO_NOWAKE); | ||
216 | break; | ||
217 | default: | ||
218 | dev_err(wm->dev, | ||
219 | "pen down irq not supported on this device\n"); | ||
220 | pen_int = 0; | ||
221 | break; | ||
222 | } | ||
223 | } | ||
224 | |||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | static void wm97xx_acc_shutdown(struct wm97xx *wm) | ||
229 | { | ||
230 | /* codec specific deconfig */ | ||
231 | if (pen_int) { | ||
232 | switch (wm->id & 0xffff) { | ||
233 | case WM9705_ID2: | ||
234 | wm->pen_irq = 0; | ||
235 | break; | ||
236 | case WM9712_ID2: | ||
237 | case WM9713_ID2: | ||
238 | /* disable interrupt */ | ||
239 | wm->pen_irq = 0; | ||
240 | break; | ||
241 | } | ||
242 | } | ||
243 | } | ||
244 | |||
245 | static void wm97xx_irq_enable(struct wm97xx *wm, int enable) | ||
246 | { | ||
247 | if (enable) | ||
248 | enable_irq(wm->pen_irq); | ||
249 | else | ||
250 | disable_irq(wm->pen_irq); | ||
251 | } | ||
252 | |||
253 | static struct wm97xx_mach_ops mainstone_mach_ops = { | ||
254 | .acc_enabled = 1, | ||
255 | .acc_pen_up = wm97xx_acc_pen_up, | ||
256 | .acc_pen_down = wm97xx_acc_pen_down, | ||
257 | .acc_startup = wm97xx_acc_startup, | ||
258 | .acc_shutdown = wm97xx_acc_shutdown, | ||
259 | .irq_enable = wm97xx_irq_enable, | ||
260 | .irq_gpio = WM97XX_GPIO_2, | ||
261 | }; | ||
262 | |||
263 | static int mainstone_wm97xx_probe(struct platform_device *pdev) | ||
264 | { | ||
265 | struct wm97xx *wm = platform_get_drvdata(pdev); | ||
266 | |||
267 | return wm97xx_register_mach_ops(wm, &mainstone_mach_ops); | ||
268 | } | ||
269 | |||
270 | static int mainstone_wm97xx_remove(struct platform_device *pdev) | ||
271 | { | ||
272 | struct wm97xx *wm = platform_get_drvdata(pdev); | ||
273 | |||
274 | wm97xx_unregister_mach_ops(wm); | ||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | static struct platform_driver mainstone_wm97xx_driver = { | ||
279 | .probe = mainstone_wm97xx_probe, | ||
280 | .remove = mainstone_wm97xx_remove, | ||
281 | .driver = { | ||
282 | .name = "wm97xx-touch", | ||
283 | }, | ||
284 | }; | ||
285 | |||
286 | static int __init mainstone_wm97xx_init(void) | ||
287 | { | ||
288 | return platform_driver_register(&mainstone_wm97xx_driver); | ||
289 | } | ||
290 | |||
291 | static void __exit mainstone_wm97xx_exit(void) | ||
292 | { | ||
293 | platform_driver_unregister(&mainstone_wm97xx_driver); | ||
294 | } | ||
295 | |||
296 | module_init(mainstone_wm97xx_init); | ||
297 | module_exit(mainstone_wm97xx_exit); | ||
298 | |||
299 | /* Module information */ | ||
300 | MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>"); | ||
301 | MODULE_DESCRIPTION("wm97xx continuous touch driver for mainstone"); | ||
302 | MODULE_LICENSE("GPL"); | ||