touchscreen-meddling.patch
[linux-2.6/openmoko-kernel/knife-kernel.git] / drivers / input / touchscreen / s3c2410_ts.c
blob83e7aff9ab7c3eb1b4fc7d8f744ba8eabdcc95c1
1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 * Copyright (c) 2004 Arnaud Patard <arnaud.patard@rtp-net.org>
17 * iPAQ H1940 touchscreen support
19 * ChangeLog
21 * 2004-09-05: Herbert Pƶtzl <herbert@13thfloor.at>
22 * - added clock (de-)allocation code
24 * 2005-03-06: Arnaud Patard <arnaud.patard@rtp-net.org>
25 * - h1940_ -> s3c2410 (this driver is now also used on the n30
26 * machines :P)
27 * - Debug messages are now enabled with the config option
28 * TOUCHSCREEN_S3C2410_DEBUG
29 * - Changed the way the value are read
30 * - Input subsystem should now work
31 * - Use ioremap and readl/writel
33 * 2005-03-23: Arnaud Patard <arnaud.patard@rtp-net.org>
34 * - Make use of some undocumented features of the touchscreen
35 * controller
37 * 2007-05-23: Harald Welte <laforge@openmoko.org>
38 * - Add proper support for S32440
40 * 2008-06-18: Andy Green <andy@openmoko.com>
41 * - Outlier removal
44 #include <linux/errno.h>
45 #include <linux/kernel.h>
46 #include <linux/module.h>
47 #include <linux/slab.h>
48 #include <linux/input.h>
49 #include <linux/init.h>
50 #include <linux/serio.h>
51 #include <linux/delay.h>
52 #include <linux/platform_device.h>
53 #include <linux/clk.h>
54 #include <asm/io.h>
55 #include <asm/irq.h>
57 #include <asm/arch/regs-gpio.h>
58 #include <asm/arch/ts.h>
60 #include <asm/plat-s3c/regs-adc.h>
62 /* For ts.dev.id.version */
63 #define S3C2410TSVERSION 0x0101
65 #define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0))
67 #define WAIT4INT(x) (((x)<<8) | \
68 S3C2410_ADCTSC_YM_SEN | \
69 S3C2410_ADCTSC_YP_SEN | \
70 S3C2410_ADCTSC_XP_SEN | \
71 S3C2410_ADCTSC_XY_PST(3))
73 #define AUTOPST (S3C2410_ADCTSC_YM_SEN | \
74 S3C2410_ADCTSC_YP_SEN | \
75 S3C2410_ADCTSC_XP_SEN | \
76 S3C2410_ADCTSC_AUTO_PST | \
77 S3C2410_ADCTSC_XY_PST(0))
79 #define DEBUG_LVL KERN_DEBUG
81 MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
82 MODULE_DESCRIPTION("s3c2410 touchscreen driver");
83 MODULE_LICENSE("GPL");
86 * Definitions & global arrays.
90 static char *s3c2410ts_name = "s3c2410 TouchScreen";
93 * Per-touchscreen data.
96 struct s3c2410ts_sample {
97 int x;
98 int y;
101 struct s3c2410ts {
102 struct input_dev *dev;
103 long xp;
104 long yp;
105 int count;
106 int shift;
107 int extent; /* 1 << shift */
109 /* the raw sample fifo is a lightweight way to track a running average
110 * of all taken samples. "running average" here means that it gives
111 * correct average for each sample, not only at the end of block of
112 * samples
114 int excursion_filter_len;
115 struct s3c2410ts_sample *raw_sample_fifo;
116 int head_raw_fifo;
117 int tail_raw_fifo;
118 struct s3c2410ts_sample raw_running_avg;
119 int reject_threshold_vs_avg;
120 int flag_previous_exceeded_threshold;
123 static struct s3c2410ts ts;
124 static void __iomem *base_addr;
126 static void clear_raw_fifo(void)
128 ts.head_raw_fifo = 0;
129 ts.tail_raw_fifo = 0;
130 ts.raw_running_avg.x = 0;
131 ts.raw_running_avg.y = 0;
132 ts.flag_previous_exceeded_threshold = 0;
136 static inline void s3c2410_ts_connect(void)
138 s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);
139 s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON);
140 s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON);
141 s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);
144 static void touch_timer_fire(unsigned long data)
146 unsigned long data0;
147 unsigned long data1;
148 int updown;
150 data0 = readl(base_addr + S3C2410_ADCDAT0);
151 data1 = readl(base_addr + S3C2410_ADCDAT1);
153 updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) &&
154 (!(data1 & S3C2410_ADCDAT0_UPDOWN));
156 if (updown) {
157 if (ts.count != 0) {
158 ts.xp >>= ts.shift;
159 ts.yp >>= ts.shift;
161 #ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
163 struct timeval tv;
165 do_gettimeofday(&tv);
166 printk(DEBUG_LVL "T:%06d, X:%03ld, Y:%03ld\n",
167 (int)tv.tv_usec, ts.xp, ts.yp);
169 #endif
171 input_report_abs(ts.dev, ABS_X, ts.xp);
172 input_report_abs(ts.dev, ABS_Y, ts.yp);
174 input_report_key(ts.dev, BTN_TOUCH, 1);
175 input_report_abs(ts.dev, ABS_PRESSURE, 1);
176 input_sync(ts.dev);
179 ts.xp = 0;
180 ts.yp = 0;
181 ts.count = 0;
183 writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
184 base_addr+S3C2410_ADCTSC);
185 writel(readl(base_addr+S3C2410_ADCCON) |
186 S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
187 } else {
188 ts.count = 0;
190 input_report_key(ts.dev, BTN_TOUCH, 0);
191 input_report_abs(ts.dev, ABS_PRESSURE, 0);
192 input_sync(ts.dev);
194 writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
198 static struct timer_list touch_timer =
199 TIMER_INITIALIZER(touch_timer_fire, 0, 0);
201 static irqreturn_t stylus_updown(int irq, void *dev_id)
203 unsigned long data0;
204 unsigned long data1;
205 int updown;
207 data0 = readl(base_addr+S3C2410_ADCDAT0);
208 data1 = readl(base_addr+S3C2410_ADCDAT1);
210 updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) &&
211 (!(data1 & S3C2410_ADCDAT0_UPDOWN));
213 /* TODO we should never get an interrupt with updown set while
214 * the timer is running, but maybe we ought to verify that the
215 * timer isn't running anyways. */
217 if (updown)
218 touch_timer_fire(0);
220 return IRQ_HANDLED;
224 static irqreturn_t stylus_action(int irq, void *dev_id)
226 unsigned long x;
227 unsigned long y;
228 int length = (ts.head_raw_fifo - ts.tail_raw_fifo) & (ts.extent - 1);
229 int scaled_avg_x = ts.raw_running_avg.x / length;
230 int scaled_avg_y = ts.raw_running_avg.y / length;
232 x = readl(base_addr + S3C2410_ADCDAT0) & S3C2410_ADCDAT0_XPDATA_MASK;
233 y = readl(base_addr + S3C2410_ADCDAT1) & S3C2410_ADCDAT1_YPDATA_MASK;
235 /* we appear to accept every sample into both the running average FIFO
236 * and the summing average. BUT, if the last sample crossed a
237 * machine-set threshold, each time we do a beauty contest
238 * on the new sample comparing if it is closer to the running
239 * average and the previous sample. If it is closer to the previous
240 * suspicious sample, we assume the change is real and accept both
241 * if the new sample has returned to being closer to the average than
242 * the previous sample, we take the previous sample as an excursion
243 * and overwrite it in both the running average and summing average.
246 if (ts.flag_previous_exceeded_threshold)
247 /* new one closer to "nonconformist" previous, or average?
248 * Pythagoras? Who? Don't need it because large excursion
249 * will be accounted for correctly this way
251 if ((abs(x - scaled_avg_x) + abs(y - scaled_avg_y)) <
252 (abs(x - ts.raw_sample_fifo[(ts.head_raw_fifo - 1) &
253 (ts.extent - 1)].x) +
254 abs(y - ts.raw_sample_fifo[(ts.head_raw_fifo - 1) &
255 (ts.extent - 1)].y))) {
256 /* it's closer to average, reject previous as a one-
257 * shot excursion, by overwriting it
259 ts.xp += x - ts.raw_sample_fifo[(ts.head_raw_fifo - 1) &
260 (ts.extent - 1)].x;
261 ts.yp += y - ts.raw_sample_fifo[(ts.head_raw_fifo - 1) &
262 (ts.extent - 1)].y;
263 ts.raw_sample_fifo[(ts.head_raw_fifo - 1) &
264 (ts.extent - 1)].x = x;
265 ts.raw_sample_fifo[(ts.head_raw_fifo - 1) &
266 (ts.extent - 1)].y = y;
267 /* no new sample: replaced previous, so we are done */
268 goto completed;
270 /* else it was closer to nonconformist previous: it's likely
271 * a genuine consistent move then.
272 * Keep previous and add new guy.
275 if ((x >= scaled_avg_x - ts.reject_threshold_vs_avg) &&
276 (x <= scaled_avg_x + ts.reject_threshold_vs_avg) &&
277 (y >= scaled_avg_y - ts.reject_threshold_vs_avg) &&
278 (y <= scaled_avg_y + ts.reject_threshold_vs_avg))
279 ts.flag_previous_exceeded_threshold = 0;
280 else
281 ts.flag_previous_exceeded_threshold = 1;
283 /* accepted */
284 ts.xp += x;
285 ts.yp += y;
286 ts.count++;
288 /* remove oldest sample from avg when we have full pipeline */
289 if (((ts.head_raw_fifo + 1) & (ts.extent - 1)) == ts.tail_raw_fifo) {
290 ts.raw_running_avg.x -= ts.raw_sample_fifo[ts.tail_raw_fifo].x;
291 ts.raw_running_avg.y -= ts.raw_sample_fifo[ts.tail_raw_fifo].y;
292 ts.tail_raw_fifo = (ts.tail_raw_fifo + 1) & (ts.extent - 1);
294 /* always add current sample to fifo and average */
295 ts.raw_sample_fifo[ts.head_raw_fifo].x = x;
296 ts.raw_sample_fifo[ts.head_raw_fifo].y = y;
297 ts.raw_running_avg.x += x;
298 ts.raw_running_avg.y += y;
299 ts.head_raw_fifo = (ts.head_raw_fifo + 1) & (ts.extent - 1);
301 completed:
302 if (ts.count >= (1 << ts.shift)) {
303 mod_timer(&touch_timer, jiffies + 1);
304 writel(WAIT4INT(1), base_addr+S3C2410_ADCTSC);
305 goto bail;
308 writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
309 base_addr+S3C2410_ADCTSC);
310 writel(readl(base_addr+S3C2410_ADCCON) |
311 S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
313 bail:
314 return IRQ_HANDLED;
317 static struct clk *adc_clock;
320 * The functions for inserting/removing us as a module.
323 static int __init s3c2410ts_probe(struct platform_device *pdev)
325 int rc;
326 struct s3c2410_ts_mach_info *info;
327 struct input_dev *input_dev;
329 info = (struct s3c2410_ts_mach_info *)pdev->dev.platform_data;
331 if (!info)
333 dev_err(&pdev->dev, "Hm... too bad: no platform data for ts\n");
334 return -EINVAL;
337 #ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
338 printk(DEBUG_LVL "Entering s3c2410ts_init\n");
339 #endif
341 adc_clock = clk_get(NULL, "adc");
342 if (!adc_clock) {
343 dev_err(&pdev->dev, "failed to get adc clock source\n");
344 return -ENOENT;
346 clk_enable(adc_clock);
348 #ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
349 printk(DEBUG_LVL "got and enabled clock\n");
350 #endif
352 base_addr = ioremap(S3C2410_PA_ADC,0x20);
353 if (base_addr == NULL) {
354 dev_err(&pdev->dev, "Failed to remap register block\n");
355 return -ENOMEM;
359 /* If we acutally are a S3C2410: Configure GPIOs */
360 if (!strcmp(pdev->name, "s3c2410-ts"))
361 s3c2410_ts_connect();
363 if ((info->presc & 0xff) > 0)
364 writel(S3C2410_ADCCON_PRSCEN |
365 S3C2410_ADCCON_PRSCVL(info->presc&0xFF),
366 base_addr + S3C2410_ADCCON);
367 else
368 writel(0, base_addr+S3C2410_ADCCON);
371 /* Initialise registers */
372 if ((info->delay & 0xffff) > 0)
373 writel(info->delay & 0xffff, base_addr + S3C2410_ADCDLY);
375 writel(WAIT4INT(0), base_addr + S3C2410_ADCTSC);
377 /* Initialise input stuff */
378 memset(&ts, 0, sizeof(struct s3c2410ts));
379 input_dev = input_allocate_device();
381 if (!input_dev) {
382 dev_err(&pdev->dev, "Unable to allocate the input device\n");
383 return -ENOMEM;
386 ts.dev = input_dev;
387 ts.dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) |
388 BIT_MASK(EV_ABS);
389 ts.dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
390 input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0);
391 input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0);
392 input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0);
394 ts.dev->private = &ts;
395 ts.dev->name = s3c2410ts_name;
396 ts.dev->id.bustype = BUS_RS232;
397 ts.dev->id.vendor = 0xDEAD;
398 ts.dev->id.product = 0xBEEF;
399 ts.dev->id.version = S3C2410TSVERSION;
401 ts.shift = info->oversampling_shift;
402 ts.extent = 1 << info->oversampling_shift;
403 ts.reject_threshold_vs_avg = info->reject_threshold_vs_avg;
404 ts.excursion_filter_len = 1 << info->excursion_filter_len_bits;
406 ts.raw_sample_fifo = kmalloc(sizeof(struct s3c2410ts_sample) *
407 ts.excursion_filter_len, GFP_KERNEL);
408 clear_raw_fifo();
410 /* Get irqs */
411 if (request_irq(IRQ_ADC, stylus_action, IRQF_SAMPLE_RANDOM,
412 "s3c2410_action", ts.dev)) {
413 dev_err(&pdev->dev, "Could not allocate ts IRQ_ADC !\n");
414 iounmap(base_addr);
415 return -EIO;
417 if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM,
418 "s3c2410_action", ts.dev)) {
419 dev_err(&pdev->dev, "Could not allocate ts IRQ_TC !\n");
420 free_irq(IRQ_ADC, ts.dev);
421 iounmap(base_addr);
422 return -EIO;
425 dev_info(&pdev->dev, "successfully loaded\n");
427 /* All went ok, so register to the input system */
428 rc = input_register_device(ts.dev);
429 if (rc) {
430 free_irq(IRQ_TC, ts.dev);
431 free_irq(IRQ_ADC, ts.dev);
432 clk_disable(adc_clock);
433 iounmap(base_addr);
434 return -EIO;
437 return 0;
440 static int s3c2410ts_remove(struct platform_device *pdev)
442 disable_irq(IRQ_ADC);
443 disable_irq(IRQ_TC);
444 free_irq(IRQ_TC,ts.dev);
445 free_irq(IRQ_ADC,ts.dev);
447 if (adc_clock) {
448 clk_disable(adc_clock);
449 clk_put(adc_clock);
450 adc_clock = NULL;
453 kfree(ts.raw_sample_fifo);
455 input_unregister_device(ts.dev);
456 iounmap(base_addr);
458 return 0;
461 #ifdef CONFIG_PM
462 static int s3c2410ts_suspend(struct platform_device *pdev, pm_message_t state)
464 writel(TSC_SLEEP, base_addr+S3C2410_ADCTSC);
465 writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_STDBM,
466 base_addr+S3C2410_ADCCON);
468 disable_irq(IRQ_ADC);
469 disable_irq(IRQ_TC);
471 clk_disable(adc_clock);
473 return 0;
476 static int s3c2410ts_resume(struct platform_device *pdev)
478 struct s3c2410_ts_mach_info *info =
479 ( struct s3c2410_ts_mach_info *)pdev->dev.platform_data;
481 clk_enable(adc_clock);
482 mdelay(1);
484 clear_raw_fifo();
486 enable_irq(IRQ_ADC);
487 enable_irq(IRQ_TC);
489 if ((info->presc&0xff) > 0)
490 writel(S3C2410_ADCCON_PRSCEN |
491 S3C2410_ADCCON_PRSCVL(info->presc&0xFF),
492 base_addr+S3C2410_ADCCON);
493 else
494 writel(0,base_addr+S3C2410_ADCCON);
496 /* Initialise registers */
497 if ((info->delay & 0xffff) > 0)
498 writel(info->delay & 0xffff, base_addr+S3C2410_ADCDLY);
500 writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
502 return 0;
505 #else
506 #define s3c2410ts_suspend NULL
507 #define s3c2410ts_resume NULL
508 #endif
510 static struct platform_driver s3c2410ts_driver = {
511 .driver = {
512 .name = "s3c2410-ts",
513 .owner = THIS_MODULE,
515 .probe = s3c2410ts_probe,
516 .remove = s3c2410ts_remove,
517 .suspend = s3c2410ts_suspend,
518 .resume = s3c2410ts_resume,
522 static struct platform_driver s3c2440ts_driver = {
523 .driver = {
524 .name = "s3c2440-ts",
525 .owner = THIS_MODULE,
527 .probe = s3c2410ts_probe,
528 .remove = s3c2410ts_remove,
529 .suspend = s3c2410ts_suspend,
530 .resume = s3c2410ts_resume,
534 static int __init s3c2410ts_init(void)
536 int rc;
538 rc = platform_driver_register(&s3c2410ts_driver);
539 if (rc < 0)
540 return rc;
542 rc = platform_driver_register(&s3c2440ts_driver);
543 if (rc < 0)
544 platform_driver_unregister(&s3c2410ts_driver);
546 return rc;
549 static void __exit s3c2410ts_exit(void)
551 platform_driver_unregister(&s3c2440ts_driver);
552 platform_driver_unregister(&s3c2410ts_driver);
555 module_init(s3c2410ts_init);
556 module_exit(s3c2410ts_exit);
559 Local variables:
560 compile-command: "make ARCH=arm CROSS_COMPILE=/usr/local/arm/3.3.2/bin/arm-linux- -k -C ../../.."
561 c-basic-offset: 8
562 End: