diff options
| -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"); | ||
