diff options
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 32 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 3 | ||||
-rw-r--r-- | drivers/input/touchscreen/ads7846.c | 23 | ||||
-rw-r--r-- | drivers/input/touchscreen/atmel-wm97xx.c | 446 | ||||
-rw-r--r-- | drivers/input/touchscreen/eeti_ts.c | 286 | ||||
-rw-r--r-- | drivers/input/touchscreen/tsc2007.c | 2 | ||||
-rw-r--r-- | drivers/input/touchscreen/w90p910_ts.c | 350 |
7 files changed, 1137 insertions, 5 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index b01fd61dadcc..72e2712c7e2a 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig | |||
@@ -111,6 +111,15 @@ config TOUCHSCREEN_DA9034 | |||
111 | Say Y here to enable the support for the touchscreen found | 111 | Say Y here to enable the support for the touchscreen found |
112 | on Dialog Semiconductor DA9034 PMIC. | 112 | on Dialog Semiconductor DA9034 PMIC. |
113 | 113 | ||
114 | config TOUCHSCREEN_EETI | ||
115 | tristate "EETI touchscreen panel support" | ||
116 | depends on I2C | ||
117 | help | ||
118 | Say Y here to enable support for I2C connected EETI touch panels. | ||
119 | |||
120 | To compile this driver as a module, choose M here: the | ||
121 | module will be called eeti_ts. | ||
122 | |||
114 | config TOUCHSCREEN_FUJITSU | 123 | config TOUCHSCREEN_FUJITSU |
115 | tristate "Fujitsu serial touchscreen" | 124 | tristate "Fujitsu serial touchscreen" |
116 | select SERIO | 125 | select SERIO |
@@ -341,6 +350,21 @@ config TOUCHSCREEN_WM9713 | |||
341 | Say Y here to enable support for the Wolfson Microelectronics | 350 | Say Y here to enable support for the Wolfson Microelectronics |
342 | WM9713 touchscreen controller. | 351 | WM9713 touchscreen controller. |
343 | 352 | ||
353 | config TOUCHSCREEN_WM97XX_ATMEL | ||
354 | tristate "WM97xx Atmel accelerated touch" | ||
355 | depends on TOUCHSCREEN_WM97XX && (AVR32 || ARCH_AT91) | ||
356 | help | ||
357 | Say Y here for support for streaming mode with WM97xx touchscreens | ||
358 | on Atmel AT91 or AVR32 systems with an AC97C module. | ||
359 | |||
360 | Be aware that this will use channel B in the controller for | ||
361 | streaming data, this must not conflict with other AC97C drivers. | ||
362 | |||
363 | If unsure, say N. | ||
364 | |||
365 | To compile this driver as a module, choose M here: the module will | ||
366 | be called atmel-wm97xx. | ||
367 | |||
344 | config TOUCHSCREEN_WM97XX_MAINSTONE | 368 | config TOUCHSCREEN_WM97XX_MAINSTONE |
345 | tristate "WM97xx Mainstone accelerated touch" | 369 | tristate "WM97xx Mainstone accelerated touch" |
346 | depends on TOUCHSCREEN_WM97XX && ARCH_PXA | 370 | depends on TOUCHSCREEN_WM97XX && ARCH_PXA |
@@ -466,4 +490,12 @@ config TOUCHSCREEN_TSC2007 | |||
466 | To compile this driver as a module, choose M here: the | 490 | To compile this driver as a module, choose M here: the |
467 | module will be called tsc2007. | 491 | module will be called tsc2007. |
468 | 492 | ||
493 | config TOUCHSCREEN_W90X900 | ||
494 | tristate "W90P910 touchscreen driver" | ||
495 | help | ||
496 | Say Y here if you have a W90P910 based touchscreen. | ||
497 | |||
498 | To compile this driver as a module, choose M here: the | ||
499 | module will be called w90p910_ts. | ||
500 | |||
469 | endif | 501 | endif |
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 6700f7b9d165..3e1c5e0b952f 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile | |||
@@ -13,6 +13,7 @@ obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o | |||
13 | obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o | 13 | obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o |
14 | obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o | 14 | obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o |
15 | obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o | 15 | obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o |
16 | obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o | ||
16 | obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o | 17 | obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o |
17 | obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o | 18 | obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o |
18 | obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o | 19 | obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o |
@@ -35,5 +36,7 @@ obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o | |||
35 | wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o | 36 | wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o |
36 | wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o | 37 | wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o |
37 | wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o | 38 | wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o |
39 | obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o | ||
38 | obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o | 40 | obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o |
39 | obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o | 41 | obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o |
42 | obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o | ||
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 2b01e56568f8..ba9d38c3f412 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c | |||
@@ -83,6 +83,7 @@ struct ads7846_packet { | |||
83 | struct ads7846 { | 83 | struct ads7846 { |
84 | struct input_dev *input; | 84 | struct input_dev *input; |
85 | char phys[32]; | 85 | char phys[32]; |
86 | char name[32]; | ||
86 | 87 | ||
87 | struct spi_device *spi; | 88 | struct spi_device *spi; |
88 | 89 | ||
@@ -97,6 +98,8 @@ struct ads7846 { | |||
97 | u16 x_plate_ohms; | 98 | u16 x_plate_ohms; |
98 | u16 pressure_max; | 99 | u16 pressure_max; |
99 | 100 | ||
101 | bool swap_xy; | ||
102 | |||
100 | struct ads7846_packet *packet; | 103 | struct ads7846_packet *packet; |
101 | 104 | ||
102 | struct spi_transfer xfer[18]; | 105 | struct spi_transfer xfer[18]; |
@@ -599,6 +602,10 @@ static void ads7846_rx(void *ads) | |||
599 | dev_dbg(&ts->spi->dev, "DOWN\n"); | 602 | dev_dbg(&ts->spi->dev, "DOWN\n"); |
600 | #endif | 603 | #endif |
601 | } | 604 | } |
605 | |||
606 | if (ts->swap_xy) | ||
607 | swap(x, y); | ||
608 | |||
602 | input_report_abs(input, ABS_X, x); | 609 | input_report_abs(input, ABS_X, x); |
603 | input_report_abs(input, ABS_Y, y); | 610 | input_report_abs(input, ABS_Y, y); |
604 | input_report_abs(input, ABS_PRESSURE, Rt); | 611 | input_report_abs(input, ABS_PRESSURE, Rt); |
@@ -917,6 +924,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) | |||
917 | ts->spi = spi; | 924 | ts->spi = spi; |
918 | ts->input = input_dev; | 925 | ts->input = input_dev; |
919 | ts->vref_mv = pdata->vref_mv; | 926 | ts->vref_mv = pdata->vref_mv; |
927 | ts->swap_xy = pdata->swap_xy; | ||
920 | 928 | ||
921 | hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | 929 | hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
922 | ts->timer.function = ads7846_timer; | 930 | ts->timer.function = ads7846_timer; |
@@ -958,8 +966,9 @@ static int __devinit ads7846_probe(struct spi_device *spi) | |||
958 | ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync; | 966 | ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync; |
959 | 967 | ||
960 | snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev)); | 968 | snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev)); |
969 | snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model); | ||
961 | 970 | ||
962 | input_dev->name = "ADS784x Touchscreen"; | 971 | input_dev->name = ts->name; |
963 | input_dev->phys = ts->phys; | 972 | input_dev->phys = ts->phys; |
964 | input_dev->dev.parent = &spi->dev; | 973 | input_dev->dev.parent = &spi->dev; |
965 | 974 | ||
@@ -1141,9 +1150,15 @@ static int __devinit ads7846_probe(struct spi_device *spi) | |||
1141 | 1150 | ||
1142 | if (request_irq(spi->irq, ads7846_irq, IRQF_TRIGGER_FALLING, | 1151 | if (request_irq(spi->irq, ads7846_irq, IRQF_TRIGGER_FALLING, |
1143 | spi->dev.driver->name, ts)) { | 1152 | spi->dev.driver->name, ts)) { |
1144 | dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); | 1153 | dev_info(&spi->dev, |
1145 | err = -EBUSY; | 1154 | "trying pin change workaround on irq %d\n", spi->irq); |
1146 | goto err_free_gpio; | 1155 | err = request_irq(spi->irq, ads7846_irq, |
1156 | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, | ||
1157 | spi->dev.driver->name, ts); | ||
1158 | if (err) { | ||
1159 | dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); | ||
1160 | goto err_free_gpio; | ||
1161 | } | ||
1147 | } | 1162 | } |
1148 | 1163 | ||
1149 | err = ads784x_hwmon_register(spi, ts); | 1164 | err = ads784x_hwmon_register(spi, ts); |
diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c new file mode 100644 index 000000000000..35377f583e28 --- /dev/null +++ b/drivers/input/touchscreen/atmel-wm97xx.c | |||
@@ -0,0 +1,446 @@ | |||
1 | /* | ||
2 | * Atmel AT91 and AVR32 continuous touch screen driver for Wolfson WM97xx AC97 | ||
3 | * codecs. | ||
4 | * | ||
5 | * Copyright (C) 2008 - 2009 Atmel Corporation | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 as published by | ||
9 | * the Free Software Foundation. | ||
10 | */ | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/moduleparam.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/irq.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/wm97xx.h> | ||
19 | #include <linux/timer.h> | ||
20 | #include <linux/gpio.h> | ||
21 | #include <linux/io.h> | ||
22 | |||
23 | #define AC97C_ICA 0x10 | ||
24 | #define AC97C_CBRHR 0x30 | ||
25 | #define AC97C_CBSR 0x38 | ||
26 | #define AC97C_CBMR 0x3c | ||
27 | #define AC97C_IER 0x54 | ||
28 | #define AC97C_IDR 0x58 | ||
29 | |||
30 | #define AC97C_RXRDY (1 << 4) | ||
31 | #define AC97C_OVRUN (1 << 5) | ||
32 | |||
33 | #define AC97C_CMR_SIZE_20 (0 << 16) | ||
34 | #define AC97C_CMR_SIZE_18 (1 << 16) | ||
35 | #define AC97C_CMR_SIZE_16 (2 << 16) | ||
36 | #define AC97C_CMR_SIZE_10 (3 << 16) | ||
37 | #define AC97C_CMR_CEM_LITTLE (1 << 18) | ||
38 | #define AC97C_CMR_CEM_BIG (0 << 18) | ||
39 | #define AC97C_CMR_CENA (1 << 21) | ||
40 | |||
41 | #define AC97C_INT_CBEVT (1 << 4) | ||
42 | |||
43 | #define AC97C_SR_CAEVT (1 << 3) | ||
44 | |||
45 | #define AC97C_CH_MASK(slot) \ | ||
46 | (0x7 << (3 * (slot - 3))) | ||
47 | #define AC97C_CH_ASSIGN(slot, channel) \ | ||
48 | (AC97C_CHANNEL_##channel << (3 * (slot - 3))) | ||
49 | #define AC97C_CHANNEL_NONE 0x0 | ||
50 | #define AC97C_CHANNEL_B 0x2 | ||
51 | |||
52 | #define ac97c_writel(chip, reg, val) \ | ||
53 | __raw_writel((val), (chip)->regs + AC97C_##reg) | ||
54 | #define ac97c_readl(chip, reg) \ | ||
55 | __raw_readl((chip)->regs + AC97C_##reg) | ||
56 | |||
57 | #ifdef CONFIG_CPU_AT32AP700X | ||
58 | #define ATMEL_WM97XX_AC97C_IOMEM (0xfff02800) | ||
59 | #define ATMEL_WM97XX_AC97C_IRQ (29) | ||
60 | #define ATMEL_WM97XX_GPIO_DEFAULT (32+16) /* Pin 16 on port B. */ | ||
61 | #else | ||
62 | #error Unkown CPU, this driver only supports AT32AP700X CPUs. | ||
63 | #endif | ||
64 | |||
65 | struct continuous { | ||
66 | u16 id; /* codec id */ | ||
67 | u8 code; /* continuous code */ | ||
68 | u8 reads; /* number of coord reads per read cycle */ | ||
69 | u32 speed; /* number of coords per second */ | ||
70 | }; | ||
71 | |||
72 | #define WM_READS(sp) ((sp / HZ) + 1) | ||
73 | |||
74 | static const struct continuous cinfo[] = { | ||
75 | {WM9705_ID2, 0, WM_READS(94), 94}, | ||
76 | {WM9705_ID2, 1, WM_READS(188), 188}, | ||
77 | {WM9705_ID2, 2, WM_READS(375), 375}, | ||
78 | {WM9705_ID2, 3, WM_READS(750), 750}, | ||
79 | {WM9712_ID2, 0, WM_READS(94), 94}, | ||
80 | {WM9712_ID2, 1, WM_READS(188), 188}, | ||
81 | {WM9712_ID2, 2, WM_READS(375), 375}, | ||
82 | {WM9712_ID2, 3, WM_READS(750), 750}, | ||
83 | {WM9713_ID2, 0, WM_READS(94), 94}, | ||
84 | {WM9713_ID2, 1, WM_READS(120), 120}, | ||
85 | {WM9713_ID2, 2, WM_READS(154), 154}, | ||
86 | {WM9713_ID2, 3, WM_READS(188), 188}, | ||
87 | }; | ||
88 | |||
89 | /* Continuous speed index. */ | ||
90 | static int sp_idx; | ||
91 | |||
92 | /* | ||
93 | * Pen sampling frequency (Hz) in continuous mode. | ||
94 | */ | ||
95 | static int cont_rate = 188; | ||
96 | module_param(cont_rate, int, 0); | ||
97 | MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)"); | ||
98 | |||
99 | /* | ||
100 | * Pen down detection. | ||
101 | * | ||
102 | * This driver can either poll or use an interrupt to indicate a pen down | ||
103 | * event. If the irq request fails then it will fall back to polling mode. | ||
104 | */ | ||
105 | static int pen_int = 1; | ||
106 | module_param(pen_int, int, 0); | ||
107 | MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)"); | ||
108 | |||
109 | /* | ||
110 | * Pressure readback. | ||
111 | * | ||
112 | * Set to 1 to read back pen down pressure. | ||
113 | */ | ||
114 | static int pressure; | ||
115 | module_param(pressure, int, 0); | ||
116 | MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)"); | ||
117 | |||
118 | /* | ||
119 | * AC97 touch data slot. | ||
120 | * | ||
121 | * Touch screen readback data ac97 slot. | ||
122 | */ | ||
123 | static int ac97_touch_slot = 5; | ||
124 | module_param(ac97_touch_slot, int, 0); | ||
125 | MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number"); | ||
126 | |||
127 | /* | ||
128 | * GPIO line number. | ||
129 | * | ||
130 | * Set to GPIO number where the signal from the WM97xx device is hooked up. | ||
131 | */ | ||
132 | static int atmel_gpio_line = ATMEL_WM97XX_GPIO_DEFAULT; | ||
133 | module_param(atmel_gpio_line, int, 0); | ||
134 | MODULE_PARM_DESC(atmel_gpio_line, "GPIO line number connected to WM97xx"); | ||
135 | |||
136 | struct atmel_wm97xx { | ||
137 | struct wm97xx *wm; | ||
138 | struct timer_list pen_timer; | ||
139 | void __iomem *regs; | ||
140 | unsigned long ac97c_irq; | ||
141 | unsigned long gpio_pen; | ||
142 | unsigned long gpio_irq; | ||
143 | unsigned short x; | ||
144 | unsigned short y; | ||
145 | }; | ||
146 | |||
147 | static irqreturn_t atmel_wm97xx_channel_b_interrupt(int irq, void *dev_id) | ||
148 | { | ||
149 | struct atmel_wm97xx *atmel_wm97xx = dev_id; | ||
150 | struct wm97xx *wm = atmel_wm97xx->wm; | ||
151 | int status = ac97c_readl(atmel_wm97xx, CBSR); | ||
152 | irqreturn_t retval = IRQ_NONE; | ||
153 | |||
154 | if (status & AC97C_OVRUN) { | ||
155 | dev_dbg(&wm->touch_dev->dev, "AC97C overrun\n"); | ||
156 | ac97c_readl(atmel_wm97xx, CBRHR); | ||
157 | retval = IRQ_HANDLED; | ||
158 | } else if (status & AC97C_RXRDY) { | ||
159 | u16 data; | ||
160 | u16 value; | ||
161 | u16 source; | ||
162 | u16 pen_down; | ||
163 | |||
164 | data = ac97c_readl(atmel_wm97xx, CBRHR); | ||
165 | value = data & 0x0fff; | ||
166 | source = data & WM97XX_ADCSRC_MASK; | ||
167 | pen_down = (data & WM97XX_PEN_DOWN) >> 8; | ||
168 | |||
169 | if (source == WM97XX_ADCSEL_X) | ||
170 | atmel_wm97xx->x = value; | ||
171 | if (source == WM97XX_ADCSEL_Y) | ||
172 | atmel_wm97xx->y = value; | ||
173 | |||
174 | if (!pressure && source == WM97XX_ADCSEL_Y) { | ||
175 | input_report_abs(wm->input_dev, ABS_X, atmel_wm97xx->x); | ||
176 | input_report_abs(wm->input_dev, ABS_Y, atmel_wm97xx->y); | ||
177 | input_report_key(wm->input_dev, BTN_TOUCH, pen_down); | ||
178 | input_sync(wm->input_dev); | ||
179 | } else if (pressure && source == WM97XX_ADCSEL_PRES) { | ||
180 | input_report_abs(wm->input_dev, ABS_X, atmel_wm97xx->x); | ||
181 | input_report_abs(wm->input_dev, ABS_Y, atmel_wm97xx->y); | ||
182 | input_report_abs(wm->input_dev, ABS_PRESSURE, value); | ||
183 | input_report_key(wm->input_dev, BTN_TOUCH, value); | ||
184 | input_sync(wm->input_dev); | ||
185 | } | ||
186 | |||
187 | retval = IRQ_HANDLED; | ||
188 | } | ||
189 | |||
190 | return retval; | ||
191 | } | ||
192 | |||
193 | static void atmel_wm97xx_acc_pen_up(struct wm97xx *wm) | ||
194 | { | ||
195 | struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(wm->touch_dev); | ||
196 | struct input_dev *input_dev = wm->input_dev; | ||
197 | int pen_down = gpio_get_value(atmel_wm97xx->gpio_pen); | ||
198 | |||
199 | if (pen_down != 0) { | ||
200 | mod_timer(&atmel_wm97xx->pen_timer, | ||
201 | jiffies + msecs_to_jiffies(1)); | ||
202 | } else { | ||
203 | if (pressure) | ||
204 | input_report_abs(input_dev, ABS_PRESSURE, 0); | ||
205 | input_report_key(input_dev, BTN_TOUCH, 0); | ||
206 | input_sync(input_dev); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | static void atmel_wm97xx_pen_timer(unsigned long data) | ||
211 | { | ||
212 | atmel_wm97xx_acc_pen_up((struct wm97xx *)data); | ||
213 | } | ||
214 | |||
215 | static int atmel_wm97xx_acc_startup(struct wm97xx *wm) | ||
216 | { | ||
217 | struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(wm->touch_dev); | ||
218 | int idx = 0; | ||
219 | |||
220 | if (wm->ac97 == NULL) | ||
221 | return -ENODEV; | ||
222 | |||
223 | for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) { | ||
224 | if (wm->id != cinfo[idx].id) | ||
225 | continue; | ||
226 | |||
227 | sp_idx = idx; | ||
228 | |||
229 | if (cont_rate <= cinfo[idx].speed) | ||
230 | break; | ||
231 | } | ||
232 | |||
233 | wm->acc_rate = cinfo[sp_idx].code; | ||
234 | wm->acc_slot = ac97_touch_slot; | ||
235 | dev_info(&wm->touch_dev->dev, "atmel accelerated touchscreen driver, " | ||
236 | "%d samples/sec\n", cinfo[sp_idx].speed); | ||
237 | |||
238 | if (pen_int) { | ||
239 | unsigned long reg; | ||
240 | |||
241 | wm->pen_irq = atmel_wm97xx->gpio_irq; | ||
242 | |||
243 | switch (wm->id) { | ||
244 | case WM9712_ID2: /* Fall through. */ | ||
245 | case WM9713_ID2: | ||
246 | /* | ||
247 | * Use GPIO 13 (PEN_DOWN) to assert GPIO line 3 | ||
248 | * (PENDOWN). | ||
249 | */ | ||
250 | wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN, | ||
251 | WM97XX_GPIO_POL_HIGH, | ||
252 | WM97XX_GPIO_STICKY, | ||
253 | WM97XX_GPIO_WAKE); | ||
254 | wm97xx_config_gpio(wm, WM97XX_GPIO_3, WM97XX_GPIO_OUT, | ||
255 | WM97XX_GPIO_POL_HIGH, | ||
256 | WM97XX_GPIO_NOTSTICKY, | ||
257 | WM97XX_GPIO_NOWAKE); | ||
258 | case WM9705_ID2: /* Fall through. */ | ||
259 | /* | ||
260 | * Enable touch data slot in AC97 controller channel B. | ||
261 | */ | ||
262 | reg = ac97c_readl(atmel_wm97xx, ICA); | ||
263 | reg &= ~AC97C_CH_MASK(wm->acc_slot); | ||
264 | reg |= AC97C_CH_ASSIGN(wm->acc_slot, B); | ||
265 | ac97c_writel(atmel_wm97xx, ICA, reg); | ||
266 | |||
267 | /* | ||
268 | * Enable channel and interrupt for RXRDY and OVERRUN. | ||
269 | */ | ||
270 | ac97c_writel(atmel_wm97xx, CBMR, AC97C_CMR_CENA | ||
271 | | AC97C_CMR_CEM_BIG | ||
272 | | AC97C_CMR_SIZE_16 | ||
273 | | AC97C_OVRUN | ||
274 | | AC97C_RXRDY); | ||
275 | /* Dummy read to empty RXRHR. */ | ||
276 | ac97c_readl(atmel_wm97xx, CBRHR); | ||
277 | /* | ||
278 | * Enable interrupt for channel B in the AC97 | ||
279 | * controller. | ||
280 | */ | ||
281 | ac97c_writel(atmel_wm97xx, IER, AC97C_INT_CBEVT); | ||
282 | break; | ||
283 | default: | ||
284 | dev_err(&wm->touch_dev->dev, "pen down irq not " | ||
285 | "supported on this device\n"); | ||
286 | pen_int = 0; | ||
287 | break; | ||
288 | } | ||
289 | } | ||
290 | |||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | static void atmel_wm97xx_acc_shutdown(struct wm97xx *wm) | ||
295 | { | ||
296 | if (pen_int) { | ||
297 | struct atmel_wm97xx *atmel_wm97xx = | ||
298 | platform_get_drvdata(wm->touch_dev); | ||
299 | unsigned long ica; | ||
300 | |||
301 | switch (wm->id & 0xffff) { | ||
302 | case WM9705_ID2: /* Fall through. */ | ||
303 | case WM9712_ID2: /* Fall through. */ | ||
304 | case WM9713_ID2: | ||
305 | /* Disable slot and turn off channel B interrupts. */ | ||
306 | ica = ac97c_readl(atmel_wm97xx, ICA); | ||
307 | ica &= ~AC97C_CH_MASK(wm->acc_slot); | ||
308 | ac97c_writel(atmel_wm97xx, ICA, ica); | ||
309 | ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT); | ||
310 | ac97c_writel(atmel_wm97xx, CBMR, 0); | ||
311 | wm->pen_irq = 0; | ||
312 | break; | ||
313 | default: | ||
314 | dev_err(&wm->touch_dev->dev, "unknown codec\n"); | ||
315 | break; | ||
316 | } | ||
317 | } | ||
318 | } | ||
319 | |||
320 | static void atmel_wm97xx_irq_enable(struct wm97xx *wm, int enable) | ||
321 | { | ||
322 | /* Intentionally left empty. */ | ||
323 | } | ||
324 | |||
325 | static struct wm97xx_mach_ops atmel_mach_ops = { | ||
326 | .acc_enabled = 1, | ||
327 | .acc_pen_up = atmel_wm97xx_acc_pen_up, | ||
328 | .acc_startup = atmel_wm97xx_acc_startup, | ||
329 | .acc_shutdown = atmel_wm97xx_acc_shutdown, | ||
330 | .irq_enable = atmel_wm97xx_irq_enable, | ||
331 | .irq_gpio = WM97XX_GPIO_3, | ||
332 | }; | ||
333 | |||
334 | static int __init atmel_wm97xx_probe(struct platform_device *pdev) | ||
335 | { | ||
336 | struct wm97xx *wm = platform_get_drvdata(pdev); | ||
337 | struct atmel_wm97xx *atmel_wm97xx; | ||
338 | int ret; | ||
339 | |||
340 | atmel_wm97xx = kzalloc(sizeof(struct atmel_wm97xx), GFP_KERNEL); | ||
341 | if (!atmel_wm97xx) { | ||
342 | dev_dbg(&pdev->dev, "out of memory\n"); | ||
343 | return -ENOMEM; | ||
344 | } | ||
345 | |||
346 | atmel_wm97xx->wm = wm; | ||
347 | atmel_wm97xx->regs = (void *)ATMEL_WM97XX_AC97C_IOMEM; | ||
348 | atmel_wm97xx->ac97c_irq = ATMEL_WM97XX_AC97C_IRQ; | ||
349 | atmel_wm97xx->gpio_pen = atmel_gpio_line; | ||
350 | atmel_wm97xx->gpio_irq = gpio_to_irq(atmel_wm97xx->gpio_pen); | ||
351 | |||
352 | setup_timer(&atmel_wm97xx->pen_timer, atmel_wm97xx_pen_timer, | ||
353 | (unsigned long)wm); | ||
354 | |||
355 | ret = request_irq(atmel_wm97xx->ac97c_irq, | ||
356 | atmel_wm97xx_channel_b_interrupt, | ||
357 | IRQF_SHARED, "atmel-wm97xx-ch-b", atmel_wm97xx); | ||
358 | if (ret) { | ||
359 | dev_dbg(&pdev->dev, "could not request ac97c irq\n"); | ||
360 | goto err; | ||
361 | } | ||
362 | |||
363 | platform_set_drvdata(pdev, atmel_wm97xx); | ||
364 | |||
365 | ret = wm97xx_register_mach_ops(wm, &atmel_mach_ops); | ||
366 | if (ret) | ||
367 | goto err_irq; | ||
368 | |||
369 | return ret; | ||
370 | |||
371 | err_irq: | ||
372 | free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx); | ||
373 | err: | ||
374 | platform_set_drvdata(pdev, NULL); | ||
375 | kfree(atmel_wm97xx); | ||
376 | return ret; | ||
377 | } | ||
378 | |||
379 | static int __exit atmel_wm97xx_remove(struct platform_device *pdev) | ||
380 | { | ||
381 | struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev); | ||
382 | struct wm97xx *wm = atmel_wm97xx->wm; | ||
383 | |||
384 | ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT); | ||
385 | free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx); | ||
386 | del_timer_sync(&atmel_wm97xx->pen_timer); | ||
387 | wm97xx_unregister_mach_ops(wm); | ||
388 | platform_set_drvdata(pdev, NULL); | ||
389 | kfree(atmel_wm97xx); | ||
390 | |||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | #ifdef CONFIG_PM | ||
395 | static int atmel_wm97xx_suspend(struct platform_device *pdev, pm_message_t msg) | ||
396 | { | ||
397 | struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev); | ||
398 | |||
399 | ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT); | ||
400 | disable_irq(atmel_wm97xx->gpio_irq); | ||
401 | del_timer_sync(&atmel_wm97xx->pen_timer); | ||
402 | |||
403 | return 0; | ||
404 | } | ||
405 | |||
406 | static int atmel_wm97xx_resume(struct platform_device *pdev) | ||
407 | { | ||
408 | struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev); | ||
409 | struct wm97xx *wm = atmel_wm97xx->wm; | ||
410 | |||
411 | if (wm->input_dev->users) { | ||
412 | enable_irq(atmel_wm97xx->gpio_irq); | ||
413 | ac97c_writel(atmel_wm97xx, IER, AC97C_INT_CBEVT); | ||
414 | } | ||
415 | |||
416 | return 0; | ||
417 | } | ||
418 | #else | ||
419 | #define atmel_wm97xx_suspend NULL | ||
420 | #define atmel_wm97xx_resume NULL | ||
421 | #endif | ||
422 | |||
423 | static struct platform_driver atmel_wm97xx_driver = { | ||
424 | .remove = __exit_p(atmel_wm97xx_remove), | ||
425 | .driver = { | ||
426 | .name = "wm97xx-touch", | ||
427 | }, | ||
428 | .suspend = atmel_wm97xx_suspend, | ||
429 | .resume = atmel_wm97xx_resume, | ||
430 | }; | ||
431 | |||
432 | static int __init atmel_wm97xx_init(void) | ||
433 | { | ||
434 | return platform_driver_probe(&atmel_wm97xx_driver, atmel_wm97xx_probe); | ||
435 | } | ||
436 | module_init(atmel_wm97xx_init); | ||
437 | |||
438 | static void __exit atmel_wm97xx_exit(void) | ||
439 | { | ||
440 | platform_driver_unregister(&atmel_wm97xx_driver); | ||
441 | } | ||
442 | module_exit(atmel_wm97xx_exit); | ||
443 | |||
444 | MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>"); | ||
445 | MODULE_DESCRIPTION("wm97xx continuous touch driver for Atmel AT91 and AVR32"); | ||
446 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c new file mode 100644 index 000000000000..3ab92222a525 --- /dev/null +++ b/drivers/input/touchscreen/eeti_ts.c | |||
@@ -0,0 +1,286 @@ | |||
1 | /* | ||
2 | * Touch Screen driver for EETI's I2C connected touch screen panels | ||
3 | * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> | ||
4 | * | ||
5 | * See EETI's software guide for the protocol specification: | ||
6 | * http://home.eeti.com.tw/web20/eg/guide.htm | ||
7 | * | ||
8 | * Based on migor_ts.c | ||
9 | * Copyright (c) 2008 Magnus Damm | ||
10 | * Copyright (c) 2007 Ujjwal Pande <ujjwal@kenati.com> | ||
11 | * | ||
12 | * This file is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public | ||
14 | * License as published by the Free Software Foundation; either | ||
15 | * version 2 of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This file is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
20 | * General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public | ||
23 | * License along with this library; if not, write to the Free Software | ||
24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
25 | */ | ||
26 | |||
27 | #include <linux/module.h> | ||
28 | #include <linux/moduleparam.h> | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/input.h> | ||
31 | #include <linux/interrupt.h> | ||
32 | #include <linux/i2c.h> | ||
33 | #include <linux/timer.h> | ||
34 | #include <linux/gpio.h> | ||
35 | |||
36 | static int flip_x; | ||
37 | module_param(flip_x, bool, 0644); | ||
38 | MODULE_PARM_DESC(flip_x, "flip x coordinate"); | ||
39 | |||
40 | static int flip_y; | ||
41 | module_param(flip_y, bool, 0644); | ||
42 | MODULE_PARM_DESC(flip_y, "flip y coordinate"); | ||
43 | |||
44 | struct eeti_ts_priv { | ||
45 | struct i2c_client *client; | ||
46 | struct input_dev *input; | ||
47 | struct work_struct work; | ||
48 | struct mutex mutex; | ||
49 | int irq; | ||
50 | }; | ||
51 | |||
52 | #define EETI_TS_BITDEPTH (11) | ||
53 | #define EETI_MAXVAL ((1 << (EETI_TS_BITDEPTH + 1)) - 1) | ||
54 | |||
55 | #define REPORT_BIT_PRESSED (1 << 0) | ||
56 | #define REPORT_BIT_AD0 (1 << 1) | ||
57 | #define REPORT_BIT_AD1 (1 << 2) | ||
58 | #define REPORT_BIT_HAS_PRESSURE (1 << 6) | ||
59 | #define REPORT_RES_BITS(v) (((v) >> 1) + EETI_TS_BITDEPTH) | ||
60 | |||
61 | static void eeti_ts_read(struct work_struct *work) | ||
62 | { | ||
63 | char buf[6]; | ||
64 | unsigned int x, y, res, pressed, to = 100; | ||
65 | struct eeti_ts_priv *priv = | ||
66 | container_of(work, struct eeti_ts_priv, work); | ||
67 | |||
68 | mutex_lock(&priv->mutex); | ||
69 | |||
70 | while (!gpio_get_value(irq_to_gpio(priv->irq)) && --to) | ||
71 | i2c_master_recv(priv->client, buf, sizeof(buf)); | ||
72 | |||
73 | if (!to) { | ||
74 | dev_err(&priv->client->dev, | ||
75 | "unable to clear IRQ - line stuck?\n"); | ||
76 | goto out; | ||
77 | } | ||
78 | |||
79 | /* drop non-report packets */ | ||
80 | if (!(buf[0] & 0x80)) | ||
81 | goto out; | ||
82 | |||
83 | pressed = buf[0] & REPORT_BIT_PRESSED; | ||
84 | res = REPORT_RES_BITS(buf[0] & (REPORT_BIT_AD0 | REPORT_BIT_AD1)); | ||
85 | x = buf[2] | (buf[1] << 8); | ||
86 | y = buf[4] | (buf[3] << 8); | ||
87 | |||
88 | /* fix the range to 11 bits */ | ||
89 | x >>= res - EETI_TS_BITDEPTH; | ||
90 | y >>= res - EETI_TS_BITDEPTH; | ||
91 | |||
92 | if (flip_x) | ||
93 | x = EETI_MAXVAL - x; | ||
94 | |||
95 | if (flip_y) | ||
96 | y = EETI_MAXVAL - y; | ||
97 | |||
98 | if (buf[0] & REPORT_BIT_HAS_PRESSURE) | ||
99 | input_report_abs(priv->input, ABS_PRESSURE, buf[5]); | ||
100 | |||
101 | input_report_abs(priv->input, ABS_X, x); | ||
102 | input_report_abs(priv->input, ABS_Y, y); | ||
103 | input_report_key(priv->input, BTN_TOUCH, !!pressed); | ||
104 | input_sync(priv->input); | ||
105 | |||
106 | out: | ||
107 | mutex_unlock(&priv->mutex); | ||
108 | } | ||
109 | |||
110 | static irqreturn_t eeti_ts_isr(int irq, void *dev_id) | ||
111 | { | ||
112 | struct eeti_ts_priv *priv = dev_id; | ||
113 | |||
114 | /* postpone I2C transactions as we are atomic */ | ||
115 | schedule_work(&priv->work); | ||
116 | |||
117 | return IRQ_HANDLED; | ||
118 | } | ||
119 | |||
120 | static int eeti_ts_open(struct input_dev *dev) | ||
121 | { | ||
122 | struct eeti_ts_priv *priv = input_get_drvdata(dev); | ||
123 | |||
124 | enable_irq(priv->irq); | ||
125 | |||
126 | /* Read the events once to arm the IRQ */ | ||
127 | eeti_ts_read(&priv->work); | ||
128 | |||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | static void eeti_ts_close(struct input_dev *dev) | ||
133 | { | ||
134 | struct eeti_ts_priv *priv = input_get_drvdata(dev); | ||
135 | |||
136 | disable_irq(priv->irq); | ||
137 | cancel_work_sync(&priv->work); | ||
138 | } | ||
139 | |||
140 | static int __devinit eeti_ts_probe(struct i2c_client *client, | ||
141 | const struct i2c_device_id *idp) | ||
142 | { | ||
143 | struct eeti_ts_priv *priv; | ||
144 | struct input_dev *input; | ||
145 | int err = -ENOMEM; | ||
146 | |||
147 | /* In contrast to what's described in the datasheet, there seems | ||
148 | * to be no way of probing the presence of that device using I2C | ||
149 | * commands. So we need to blindly believe it is there, and wait | ||
150 | * for interrupts to occur. */ | ||
151 | |||
152 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
153 | if (!priv) { | ||
154 | dev_err(&client->dev, "failed to allocate driver data\n"); | ||
155 | goto err0; | ||
156 | } | ||
157 | |||
158 | mutex_init(&priv->mutex); | ||
159 | input = input_allocate_device(); | ||
160 | |||
161 | if (!input) { | ||
162 | dev_err(&client->dev, "Failed to allocate input device.\n"); | ||
163 | goto err1; | ||
164 | } | ||
165 | |||
166 | input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); | ||
167 | input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); | ||
168 | |||
169 | input_set_abs_params(input, ABS_X, 0, EETI_MAXVAL, 0, 0); | ||
170 | input_set_abs_params(input, ABS_Y, 0, EETI_MAXVAL, 0, 0); | ||
171 | input_set_abs_params(input, ABS_PRESSURE, 0, 0xff, 0, 0); | ||
172 | |||
173 | input->name = client->name; | ||
174 | input->id.bustype = BUS_I2C; | ||
175 | input->dev.parent = &client->dev; | ||
176 | input->open = eeti_ts_open; | ||
177 | input->close = eeti_ts_close; | ||
178 | |||
179 | priv->client = client; | ||
180 | priv->input = input; | ||
181 | priv->irq = client->irq; | ||
182 | |||
183 | INIT_WORK(&priv->work, eeti_ts_read); | ||
184 | i2c_set_clientdata(client, priv); | ||
185 | input_set_drvdata(input, priv); | ||
186 | |||
187 | err = input_register_device(input); | ||
188 | if (err) | ||
189 | goto err1; | ||
190 | |||
191 | err = request_irq(priv->irq, eeti_ts_isr, IRQF_TRIGGER_FALLING, | ||
192 | client->name, priv); | ||
193 | if (err) { | ||
194 | dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); | ||
195 | goto err2; | ||
196 | } | ||
197 | |||
198 | /* Disable the irq for now. It will be enabled once the input device | ||
199 | * is opened. */ | ||
200 | disable_irq(priv->irq); | ||
201 | |||
202 | device_init_wakeup(&client->dev, 0); | ||
203 | return 0; | ||
204 | |||
205 | err2: | ||
206 | input_unregister_device(input); | ||
207 | input = NULL; /* so we dont try to free it below */ | ||
208 | err1: | ||
209 | input_free_device(input); | ||
210 | i2c_set_clientdata(client, NULL); | ||
211 | kfree(priv); | ||
212 | err0: | ||
213 | return err; | ||
214 | } | ||
215 | |||
216 | static int __devexit eeti_ts_remove(struct i2c_client *client) | ||
217 | { | ||
218 | struct eeti_ts_priv *priv = i2c_get_clientdata(client); | ||
219 | |||
220 | free_irq(priv->irq, priv); | ||
221 | input_unregister_device(priv->input); | ||
222 | i2c_set_clientdata(client, NULL); | ||
223 | kfree(priv); | ||
224 | |||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | #ifdef CONFIG_PM | ||
229 | static int eeti_ts_suspend(struct i2c_client *client, pm_message_t mesg) | ||
230 | { | ||
231 | struct eeti_ts_priv *priv = i2c_get_clientdata(client); | ||
232 | |||
233 | if (device_may_wakeup(&client->dev)) | ||
234 | enable_irq_wake(priv->irq); | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | static int eeti_ts_resume(struct i2c_client *client) | ||
240 | { | ||
241 | struct eeti_ts_priv *priv = i2c_get_clientdata(client); | ||
242 | |||
243 | if (device_may_wakeup(&client->dev)) | ||
244 | disable_irq_wake(priv->irq); | ||
245 | |||
246 | return 0; | ||
247 | } | ||
248 | #else | ||
249 | #define eeti_ts_suspend NULL | ||
250 | #define eeti_ts_resume NULL | ||
251 | #endif | ||
252 | |||
253 | static const struct i2c_device_id eeti_ts_id[] = { | ||
254 | { "eeti_ts", 0 }, | ||
255 | { } | ||
256 | }; | ||
257 | MODULE_DEVICE_TABLE(i2c, eeti_ts_id); | ||
258 | |||
259 | static struct i2c_driver eeti_ts_driver = { | ||
260 | .driver = { | ||
261 | .name = "eeti_ts", | ||
262 | }, | ||
263 | .probe = eeti_ts_probe, | ||
264 | .remove = __devexit_p(eeti_ts_remove), | ||
265 | .suspend = eeti_ts_suspend, | ||
266 | .resume = eeti_ts_resume, | ||
267 | .id_table = eeti_ts_id, | ||
268 | }; | ||
269 | |||
270 | static int __init eeti_ts_init(void) | ||
271 | { | ||
272 | return i2c_add_driver(&eeti_ts_driver); | ||
273 | } | ||
274 | |||
275 | static void __exit eeti_ts_exit(void) | ||
276 | { | ||
277 | i2c_del_driver(&eeti_ts_driver); | ||
278 | } | ||
279 | |||
280 | MODULE_DESCRIPTION("EETI Touchscreen driver"); | ||
281 | MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); | ||
282 | MODULE_LICENSE("GPL"); | ||
283 | |||
284 | module_init(eeti_ts_init); | ||
285 | module_exit(eeti_ts_exit); | ||
286 | |||
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c index 948e167557f1..880f58c6a7c4 100644 --- a/drivers/input/touchscreen/tsc2007.c +++ b/drivers/input/touchscreen/tsc2007.c | |||
@@ -257,7 +257,7 @@ static int tsc2007_probe(struct i2c_client *client, | |||
257 | struct input_dev *input_dev; | 257 | struct input_dev *input_dev; |
258 | int err; | 258 | int err; |
259 | 259 | ||
260 | if (!pdata) { | 260 | if (!pdata || !pdata->get_pendown_state) { |
261 | dev_err(&client->dev, "platform data is required!\n"); | 261 | dev_err(&client->dev, "platform data is required!\n"); |
262 | return -EINVAL; | 262 | return -EINVAL; |
263 | } | 263 | } |
diff --git a/drivers/input/touchscreen/w90p910_ts.c b/drivers/input/touchscreen/w90p910_ts.c new file mode 100644 index 000000000000..6071f5882572 --- /dev/null +++ b/drivers/input/touchscreen/w90p910_ts.c | |||
@@ -0,0 +1,350 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Nuvoton technology corporation. | ||
3 | * | ||
4 | * Wan ZongShun <mcuos.com@gmail.com> | ||
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;version 2 of the License. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/delay.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/io.h> | ||
16 | #include <linux/input.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | |||
19 | /* ADC controller bit defines */ | ||
20 | #define ADC_DELAY 0xf00 | ||
21 | #define ADC_DOWN 0x01 | ||
22 | #define ADC_TSC_Y (0x01 << 8) | ||
23 | #define ADC_TSC_X (0x00 << 8) | ||
24 | #define TSC_FOURWIRE (~(0x03 << 1)) | ||
25 | #define ADC_CLK_EN (0x01 << 28) /* ADC clock enable */ | ||
26 | #define ADC_READ_CON (0x01 << 12) | ||
27 | #define ADC_CONV (0x01 << 13) | ||
28 | #define ADC_SEMIAUTO (0x01 << 14) | ||
29 | #define ADC_WAITTRIG (0x03 << 14) | ||
30 | #define ADC_RST1 (0x01 << 16) | ||
31 | #define ADC_RST0 (0x00 << 16) | ||
32 | #define ADC_EN (0x01 << 17) | ||
33 | #define ADC_INT (0x01 << 18) | ||
34 | #define WT_INT (0x01 << 20) | ||
35 | #define ADC_INT_EN (0x01 << 21) | ||
36 | #define LVD_INT_EN (0x01 << 22) | ||
37 | #define WT_INT_EN (0x01 << 23) | ||
38 | #define ADC_DIV (0x04 << 1) /* div = 6 */ | ||
39 | |||
40 | enum ts_state { | ||
41 | TS_WAIT_NEW_PACKET, /* We are waiting next touch report */ | ||
42 | TS_WAIT_X_COORD, /* We are waiting for ADC to report X coord */ | ||
43 | TS_WAIT_Y_COORD, /* We are waiting for ADC to report Y coord */ | ||
44 | TS_IDLE, /* Input device is closed, don't do anything */ | ||
45 | }; | ||
46 | |||
47 | struct w90p910_ts { | ||
48 | struct input_dev *input; | ||
49 | struct timer_list timer; | ||
50 | int irq_num; | ||
51 | void __iomem *clocken; | ||
52 | void __iomem *ts_reg; | ||
53 | spinlock_t lock; | ||
54 | enum ts_state state; | ||
55 | }; | ||
56 | |||
57 | static void w90p910_report_event(struct w90p910_ts *w90p910_ts, bool down) | ||
58 | { | ||
59 | struct input_dev *dev = w90p910_ts->input; | ||
60 | |||
61 | if (down) { | ||
62 | input_report_abs(dev, ABS_X, | ||
63 | __raw_readl(w90p910_ts->ts_reg + 0x0c)); | ||
64 | input_report_abs(dev, ABS_Y, | ||
65 | __raw_readl(w90p910_ts->ts_reg + 0x10)); | ||
66 | } | ||
67 | |||
68 | input_report_key(dev, BTN_TOUCH, down); | ||
69 | input_sync(dev); | ||
70 | } | ||
71 | |||
72 | static void w90p910_prepare_x_reading(struct w90p910_ts *w90p910_ts) | ||
73 | { | ||
74 | unsigned long ctlreg; | ||
75 | |||
76 | __raw_writel(ADC_TSC_X, w90p910_ts->ts_reg + 0x04); | ||
77 | ctlreg = __raw_readl(w90p910_ts->ts_reg); | ||
78 | ctlreg &= ~(ADC_WAITTRIG | WT_INT | WT_INT_EN); | ||
79 | ctlreg |= ADC_SEMIAUTO | ADC_INT_EN | ADC_CONV; | ||
80 | __raw_writel(ctlreg, w90p910_ts->ts_reg); | ||
81 | |||
82 | w90p910_ts->state = TS_WAIT_X_COORD; | ||
83 | } | ||
84 | |||
85 | static void w90p910_prepare_y_reading(struct w90p910_ts *w90p910_ts) | ||
86 | { | ||
87 | unsigned long ctlreg; | ||
88 | |||
89 | __raw_writel(ADC_TSC_Y, w90p910_ts->ts_reg + 0x04); | ||
90 | ctlreg = __raw_readl(w90p910_ts->ts_reg); | ||
91 | ctlreg &= ~(ADC_WAITTRIG | ADC_INT | WT_INT_EN); | ||
92 | ctlreg |= ADC_SEMIAUTO | ADC_INT_EN | ADC_CONV; | ||
93 | __raw_writel(ctlreg, w90p910_ts->ts_reg); | ||
94 | |||
95 | w90p910_ts->state = TS_WAIT_Y_COORD; | ||
96 | } | ||
97 | |||
98 | static void w90p910_prepare_next_packet(struct w90p910_ts *w90p910_ts) | ||
99 | { | ||
100 | unsigned long ctlreg; | ||
101 | |||
102 | ctlreg = __raw_readl(w90p910_ts->ts_reg); | ||
103 | ctlreg &= ~(ADC_INT | ADC_INT_EN | ADC_SEMIAUTO | ADC_CONV); | ||
104 | ctlreg |= ADC_WAITTRIG | WT_INT_EN; | ||
105 | __raw_writel(ctlreg, w90p910_ts->ts_reg); | ||
106 | |||
107 | w90p910_ts->state = TS_WAIT_NEW_PACKET; | ||
108 | } | ||
109 | |||
110 | static irqreturn_t w90p910_ts_interrupt(int irq, void *dev_id) | ||
111 | { | ||
112 | struct w90p910_ts *w90p910_ts = dev_id; | ||
113 | unsigned long flags; | ||
114 | |||
115 | spin_lock_irqsave(&w90p910_ts->lock, flags); | ||
116 | |||
117 | switch (w90p910_ts->state) { | ||
118 | case TS_WAIT_NEW_PACKET: | ||
119 | /* | ||
120 | * The controller only generates interrupts when pen | ||
121 | * is down. | ||
122 | */ | ||
123 | del_timer(&w90p910_ts->timer); | ||
124 | w90p910_prepare_x_reading(w90p910_ts); | ||
125 | break; | ||
126 | |||
127 | |||
128 | case TS_WAIT_X_COORD: | ||
129 | w90p910_prepare_y_reading(w90p910_ts); | ||
130 | break; | ||
131 | |||
132 | case TS_WAIT_Y_COORD: | ||
133 | w90p910_report_event(w90p910_ts, true); | ||
134 | w90p910_prepare_next_packet(w90p910_ts); | ||
135 | mod_timer(&w90p910_ts->timer, jiffies + msecs_to_jiffies(100)); | ||
136 | break; | ||
137 | |||
138 | case TS_IDLE: | ||
139 | break; | ||
140 | } | ||
141 | |||
142 | spin_unlock_irqrestore(&w90p910_ts->lock, flags); | ||
143 | |||
144 | return IRQ_HANDLED; | ||
145 | } | ||
146 | |||
147 | static void w90p910_check_pen_up(unsigned long data) | ||
148 | { | ||
149 | struct w90p910_ts *w90p910_ts = (struct w90p910_ts *) data; | ||
150 | unsigned long flags; | ||
151 | |||
152 | spin_lock_irqsave(&w90p910_ts->lock, flags); | ||
153 | |||
154 | if (w90p910_ts->state == TS_WAIT_NEW_PACKET && | ||
155 | !(__raw_readl(w90p910_ts->ts_reg + 0x04) & ADC_DOWN)) { | ||
156 | |||
157 | w90p910_report_event(w90p910_ts, false); | ||
158 | } | ||
159 | |||
160 | spin_unlock_irqrestore(&w90p910_ts->lock, flags); | ||
161 | } | ||
162 | |||
163 | static int w90p910_open(struct input_dev *dev) | ||
164 | { | ||
165 | struct w90p910_ts *w90p910_ts = input_get_drvdata(dev); | ||
166 | unsigned long val; | ||
167 | |||
168 | /* enable the ADC clock */ | ||
169 | val = __raw_readl(w90p910_ts->clocken); | ||
170 | __raw_writel(val | ADC_CLK_EN, w90p910_ts->clocken); | ||
171 | |||
172 | __raw_writel(ADC_RST1, w90p910_ts->ts_reg); | ||
173 | msleep(1); | ||
174 | __raw_writel(ADC_RST0, w90p910_ts->ts_reg); | ||
175 | msleep(1); | ||
176 | |||
177 | /* set delay and screen type */ | ||
178 | val = __raw_readl(w90p910_ts->ts_reg + 0x04); | ||
179 | __raw_writel(val & TSC_FOURWIRE, w90p910_ts->ts_reg + 0x04); | ||
180 | __raw_writel(ADC_DELAY, w90p910_ts->ts_reg + 0x08); | ||
181 | |||
182 | w90p910_ts->state = TS_WAIT_NEW_PACKET; | ||
183 | wmb(); | ||
184 | |||
185 | /* set trigger mode */ | ||
186 | val = __raw_readl(w90p910_ts->ts_reg); | ||
187 | val |= ADC_WAITTRIG | ADC_DIV | ADC_EN | WT_INT_EN; | ||
188 | __raw_writel(val, w90p910_ts->ts_reg); | ||
189 | |||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static void w90p910_close(struct input_dev *dev) | ||
194 | { | ||
195 | struct w90p910_ts *w90p910_ts = input_get_drvdata(dev); | ||
196 | unsigned long val; | ||
197 | |||
198 | /* disable trigger mode */ | ||
199 | |||
200 | spin_lock_irq(&w90p910_ts->lock); | ||
201 | |||
202 | w90p910_ts->state = TS_IDLE; | ||
203 | |||
204 | val = __raw_readl(w90p910_ts->ts_reg); | ||
205 | val &= ~(ADC_WAITTRIG | ADC_DIV | ADC_EN | WT_INT_EN | ADC_INT_EN); | ||
206 | __raw_writel(val, w90p910_ts->ts_reg); | ||
207 | |||
208 | spin_unlock_irq(&w90p910_ts->lock); | ||
209 | |||
210 | /* Now that interrupts are shut off we can safely delete timer */ | ||
211 | del_timer_sync(&w90p910_ts->timer); | ||
212 | |||
213 | /* stop the ADC clock */ | ||
214 | val = __raw_readl(w90p910_ts->clocken); | ||
215 | __raw_writel(val & ~ADC_CLK_EN, w90p910_ts->clocken); | ||
216 | } | ||
217 | |||
218 | static int __devinit w90x900ts_probe(struct platform_device *pdev) | ||
219 | { | ||
220 | struct w90p910_ts *w90p910_ts; | ||
221 | struct input_dev *input_dev; | ||
222 | struct resource *res; | ||
223 | int err; | ||
224 | |||
225 | w90p910_ts = kzalloc(sizeof(struct w90p910_ts), GFP_KERNEL); | ||
226 | input_dev = input_allocate_device(); | ||
227 | if (!w90p910_ts || !input_dev) { | ||
228 | err = -ENOMEM; | ||
229 | goto fail1; | ||
230 | } | ||
231 | |||
232 | w90p910_ts->input = input_dev; | ||
233 | w90p910_ts->state = TS_IDLE; | ||
234 | spin_lock_init(&w90p910_ts->lock); | ||
235 | setup_timer(&w90p910_ts->timer, w90p910_check_pen_up, | ||
236 | (unsigned long)&w90p910_ts); | ||
237 | |||
238 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
239 | if (!res) { | ||
240 | err = -ENXIO; | ||
241 | goto fail1; | ||
242 | } | ||
243 | |||
244 | if (!request_mem_region(res->start, res->end - res->start + 1, | ||
245 | pdev->name)) { | ||
246 | err = -EBUSY; | ||
247 | goto fail1; | ||
248 | } | ||
249 | |||
250 | w90p910_ts->ts_reg = ioremap(res->start, res->end - res->start + 1); | ||
251 | if (!w90p910_ts->ts_reg) { | ||
252 | err = -ENOMEM; | ||
253 | goto fail2; | ||
254 | } | ||
255 | |||
256 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
257 | if (!res) { | ||
258 | err = -ENXIO; | ||
259 | goto fail3; | ||
260 | } | ||
261 | |||
262 | w90p910_ts->clocken = (void __iomem *)res->start; | ||
263 | |||
264 | input_dev->name = "W90P910 TouchScreen"; | ||
265 | input_dev->phys = "w90p910ts/event0"; | ||
266 | input_dev->id.bustype = BUS_HOST; | ||
267 | input_dev->id.vendor = 0x0005; | ||
268 | input_dev->id.product = 0x0001; | ||
269 | input_dev->id.version = 0x0100; | ||
270 | input_dev->dev.parent = &pdev->dev; | ||
271 | input_dev->open = w90p910_open; | ||
272 | input_dev->close = w90p910_close; | ||
273 | |||
274 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); | ||
275 | input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); | ||
276 | |||
277 | input_set_abs_params(input_dev, ABS_X, 0, 0x400, 0, 0); | ||
278 | input_set_abs_params(input_dev, ABS_Y, 0, 0x400, 0, 0); | ||
279 | |||
280 | input_set_drvdata(input_dev, w90p910_ts); | ||
281 | |||
282 | w90p910_ts->irq_num = platform_get_irq(pdev, 0); | ||
283 | if (request_irq(w90p910_ts->irq_num, w90p910_ts_interrupt, | ||
284 | IRQF_DISABLED, "w90p910ts", w90p910_ts)) { | ||
285 | err = -EBUSY; | ||
286 | goto fail3; | ||
287 | } | ||
288 | |||
289 | err = input_register_device(w90p910_ts->input); | ||
290 | if (err) | ||
291 | goto fail4; | ||
292 | |||
293 | platform_set_drvdata(pdev, w90p910_ts); | ||
294 | |||
295 | return 0; | ||
296 | |||
297 | fail4: free_irq(w90p910_ts->irq_num, w90p910_ts); | ||
298 | fail3: iounmap(w90p910_ts->ts_reg); | ||
299 | fail2: release_mem_region(res->start, res->end - res->start + 1); | ||
300 | fail1: input_free_device(input_dev); | ||
301 | kfree(w90p910_ts); | ||
302 | return err; | ||
303 | } | ||
304 | |||
305 | static int __devexit w90x900ts_remove(struct platform_device *pdev) | ||
306 | { | ||
307 | struct w90p910_ts *w90p910_ts = platform_get_drvdata(pdev); | ||
308 | struct resource *res; | ||
309 | |||
310 | free_irq(w90p910_ts->irq_num, w90p910_ts); | ||
311 | del_timer_sync(&w90p910_ts->timer); | ||
312 | iounmap(w90p910_ts->ts_reg); | ||
313 | |||
314 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
315 | release_mem_region(res->start, res->end - res->start + 1); | ||
316 | |||
317 | input_unregister_device(w90p910_ts->input); | ||
318 | kfree(w90p910_ts); | ||
319 | |||
320 | platform_set_drvdata(pdev, NULL); | ||
321 | |||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | static struct platform_driver w90x900ts_driver = { | ||
326 | .probe = w90x900ts_probe, | ||
327 | .remove = __devexit_p(w90x900ts_remove), | ||
328 | .driver = { | ||
329 | .name = "w90x900-ts", | ||
330 | .owner = THIS_MODULE, | ||
331 | }, | ||
332 | }; | ||
333 | |||
334 | static int __init w90x900ts_init(void) | ||
335 | { | ||
336 | return platform_driver_register(&w90x900ts_driver); | ||
337 | } | ||
338 | |||
339 | static void __exit w90x900ts_exit(void) | ||
340 | { | ||
341 | platform_driver_unregister(&w90x900ts_driver); | ||
342 | } | ||
343 | |||
344 | module_init(w90x900ts_init); | ||
345 | module_exit(w90x900ts_exit); | ||
346 | |||
347 | MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); | ||
348 | MODULE_DESCRIPTION("w90p910 touch screen driver!"); | ||
349 | MODULE_LICENSE("GPL"); | ||
350 | MODULE_ALIAS("platform:w90p910-ts"); | ||