Merge branch 'org.openembedded.dev' of git@git.openembedded.net:openembedded into...
[vuplus_openembedded] / packages / linux / linux-2.6.28 / collie / 0013-add-collie-touchscreen-driver.patch
1 From 40787f3e48d1cc1e63dc5dd6aeda720f688fc05e Mon Sep 17 00:00:00 2001
2 From: Thomas Kunze <thommycheck@gmx.de>
3 Date: Mon, 20 Oct 2008 17:44:23 +0200
4 Subject: [PATCH 13/23] add collie touchscreen driver
5
6 ---
7  drivers/input/touchscreen/Kconfig     |    6 +
8  drivers/input/touchscreen/Makefile    |    1 +
9  drivers/input/touchscreen/collie-ts.c |  449 +++++++++++++++++++++++++++++++++
10  drivers/mfd/Makefile                  |    1 -
11  include/linux/mfd/ucb1x00.h           |    3 +
12  5 files changed, 459 insertions(+), 1 deletions(-)
13  create mode 100644 drivers/input/touchscreen/collie-ts.c
14
15 diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
16 index 3ac8cd6..a9f89ed 100644
17 --- a/drivers/input/touchscreen/Kconfig
18 +++ b/drivers/input/touchscreen/Kconfig
19 @@ -228,6 +228,12 @@ config TOUCHSCREEN_UCB1200_TS
20           This enabled support for the Pilips UCB1200 touchscreen interface
21           and compatible.
22  
23 +config TOUCHSCREEN_COLLIE_TS
24 +       tristate "Touchscreen collie support"
25 +       depends on MCP_UCB1200 && INPUT && !MCP_UCB1200_TS
26 +       help
27 +         Driver for touchscreen on collie - sharp sl-5500.
28 +
29  config TOUCHSCREEN_UCB1400
30         tristate "Philips UCB1400 touchscreen"
31         depends on AC97_BUS
32 diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
33 index 77ba930..77715cd 100644
34 --- a/drivers/input/touchscreen/Makefile
35 +++ b/drivers/input/touchscreen/Makefile
36 @@ -26,6 +26,7 @@ obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)  += touchit213.o
37  obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)   += touchright.o
38  obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)     += touchwin.o
39  obj-$(CONFIG_TOUCHSCREEN_UCB1200_TS)   += ucb1x00-ts.o
40 +obj-$(CONFIG_TOUCHSCREEN_COLLIE_TS)     += collie-ts.o
41  obj-$(CONFIG_TOUCHSCREEN_UCB1400)      += ucb1400_ts.o
42  obj-$(CONFIG_TOUCHSCREEN_WM97XX)       += wm97xx-ts.o
43  wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
44 diff --git a/drivers/input/touchscreen/collie-ts.c b/drivers/input/touchscreen/collie-ts.c
45 new file mode 100644
46 index 0000000..c7c0272
47 --- /dev/null
48 +++ b/drivers/input/touchscreen/collie-ts.c
49 @@ -0,0 +1,449 @@
50 +/*
51 + *  Touchscreen driver for UCB1x00-based touchscreens
52 + *
53 + *  Copyright (C) 2001 Russell King, All Rights Reserved.
54 + *  Copyright (C) 2005 Pavel Machek
55 + *
56 + * This program is free software; you can redistribute it and/or modify
57 + * it under the terms of the GNU General Public License version 2 as
58 + * published by the Free Software Foundation.
59 + *
60 + * 21-Jan-2002 <jco@ict.es> :
61 + *
62 + * Added support for synchronous A/D mode. This mode is useful to
63 + * avoid noise induced in the touchpanel by the LCD, provided that
64 + * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin.
65 + * It is important to note that the signal connected to the ADCSYNC
66 + * pin should provide pulses even when the LCD is blanked, otherwise
67 + * a pen touch needed to unblank the LCD will never be read.
68 + */
69 +#include <linux/module.h>
70 +#include <linux/moduleparam.h>
71 +#include <linux/init.h>
72 +#include <linux/smp.h>
73 +#include <linux/smp_lock.h>
74 +#include <linux/sched.h>
75 +#include <linux/completion.h>
76 +#include <linux/delay.h>
77 +#include <linux/string.h>
78 +#include <linux/input.h>
79 +#include <linux/device.h>
80 +#include <linux/freezer.h>
81 +#include <linux/slab.h>
82 +#include <linux/kthread.h>
83 +#include <linux/semaphore.h>
84 +
85 +#include <mach/dma.h>
86 +#include <mach/collie.h>
87 +#include <asm/mach-types.h>
88 +
89 +#include <linux/mfd/ucb1x00.h>
90 +
91 +struct ucb1x00_ts {
92 +       struct input_dev        *idev;
93 +       struct ucb1x00          *ucb;
94 +
95 +       wait_queue_head_t       irq_wait;
96 +       struct task_struct      *rtask;
97 +       u16                     x_res;
98 +       u16                     y_res;
99 +
100 +       unsigned int            adcsync:1;
101 +};
102 +
103 +static int adcsync;
104 +
105 +/**********************************
106 +
107 +     ................
108 +     .              . = 340
109 +     .              .
110 +     .             ^.
111 +     .             ^.
112 +     .             ^.
113 +     .             ^.
114 +     .              .
115 +     .             X. = 10
116 +     .  <<<<<<<<  Y .
117 +     ................
118 +     .   Sharp    =200
119 +     .              .
120 +     .  -   O    -  .
121 +     .              .
122 +     ................
123 +
124 +**********************************/
125 +
126 +
127 +static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y)
128 +{
129 +       struct input_dev *idev = ts->idev;
130 +
131 +       input_report_abs(idev, ABS_X, x);
132 +       input_report_abs(idev, ABS_Y, y);
133 +       input_report_abs(idev, ABS_PRESSURE, pressure);
134 +        input_report_key(idev, BTN_TOUCH, 1);
135 +       input_sync(idev);
136 +}
137 +
138 +static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts)
139 +{
140 +       struct input_dev *idev = ts->idev;
141 +
142 +       input_report_abs(idev, ABS_PRESSURE, 0);
143 +        input_report_key(idev, BTN_TOUCH, 0);
144 +       input_sync(idev);
145 +}
146 +
147 +/*
148 + * Switch to interrupt mode. This set touchscreen to interrupt 
149 + * mode, so that chip is able to send interrupt.
150 + */
151 +static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts)
152 +{
153 +       ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
154 +                       UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
155 +                       UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
156 +                       UCB_TS_CR_MODE_INT);
157 +}
158 +
159 +/*
160 + * Switch to pressure mode, and read pressure.  We don't need to wait
161 + * here, since both plates are being driven.
162 + *
163 + * set_read_pressure() in sharp code
164 + */
165 +static inline void ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts)
166 +{
167 +       ucb1x00_io_write(ts->ucb, COLLIE_TC35143_GPIO_TBL_CHK, 0);
168 +       ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
169 +                         UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMX_POW |
170 +                         UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
171 +
172 +       ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr | 
173 +                         UCB_ADC_INP_AD2 | 
174 +                         UCB_ADC_SYNC_ENA);
175 +       udelay(100);
176 +       ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr | 
177 +                         UCB_ADC_INP_AD2 | 
178 +                         UCB_ADC_SYNC_ENA | UCB_ADC_START);
179 +}
180 +
181 +/*
182 + * Switch to X position mode and measure Y plate.  We switch the plate
183 + * configuration in pressure mode, then switch to position mode.  This
184 + * gives a faster response time.  Even so, we need to wait about 55us
185 + * for things to stabilise.
186 + */
187 +static inline void ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts)
188 +{
189 +       ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK);
190 +       ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
191 +                       UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
192 +                       UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
193 +
194 +
195 +       ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr | 
196 +                         UCB_ADC_INP_TSPY | UCB_ADC_SYNC_ENA);
197 +       udelay(100);
198 +       ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr | 
199 +                         UCB_ADC_INP_TSPY | UCB_ADC_SYNC_ENA | 
200 +                         UCB_ADC_START);
201 +}
202 +
203 +/*
204 + * Switch to Y position mode and measure X plate.  We switch the plate
205 + * configuration in pressure mode, then switch to position mode.  This
206 + * gives a faster response time.  Even so, we need to wait about 55us
207 + * for things to stabilise.
208 + */
209 +static inline void ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts)
210 +{
211 +       ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK);
212 +
213 +       ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
214 +                       UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
215 +                       UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
216 +
217 +
218 +       ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr | 
219 +                         UCB_ADC_INP_TSPX | UCB_ADC_SYNC_ENA);
220 +       udelay(100);
221 +       ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr |
222 +                         UCB_ADC_INP_TSPX | UCB_ADC_SYNC_ENA | 
223 +                         UCB_ADC_START);
224 +}
225 +
226 +/*
227 + * Switch to X plate resistance mode.  Set MX to ground, PX to
228 + * supply.  Measure current.
229 + */
230 +static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts)
231 +{
232 +       ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
233 +                       UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
234 +                       UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
235 +       return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
236 +}
237 +
238 +/*
239 + * Switch to Y plate resistance mode.  Set MY to ground, PY to
240 + * supply.  Measure current.
241 + */
242 +static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts)
243 +{
244 +       ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
245 +                       UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
246 +                       UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
247 +       return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
248 +}
249 +
250 +/*
251 + * This is a RT kernel thread that handles the ADC accesses
252 + * (mainly so we can use semaphores in the UCB1200 core code
253 + * to serialise accesses to the ADC).
254 + */
255 +static int ucb1x00_thread(void *_ts)
256 +{
257 +       struct ucb1x00_ts *ts = _ts;
258 +       struct task_struct *tsk = current;
259 +       DECLARE_WAITQUEUE(wait, tsk);
260 +       int state;
261 +
262 +       /*
263 +        * We could run as a real-time thread.  However, thus far
264 +        * this doesn't seem to be necessary.
265 +        */
266 +
267 +       add_wait_queue(&ts->irq_wait, &wait);   
268 +
269 +       while (!kthread_should_stop()) {
270 +               unsigned int data[3];
271 +                       
272 +               for (state=0; state<3; state++) {
273 +                       
274 +                       ucb1x00_adc_enable(ts->ucb);
275 +                       ucb1x00_enable_irq(ts->ucb, UCB_IRQ_ADC, UCB_FALLING);
276 +                       switch (state) {
277 +                       /* Order matters here; last measurement seems to be more noisy then the
278 +                          rest, and we care about pressure least */
279 +                               case 2: ucb1x00_ts_read_pressure(ts);
280 +                                       break;
281 +                               case 0: ucb1x00_ts_read_ypos(ts);
282 +                                       break;
283 +                               case 1: ucb1x00_ts_read_xpos(ts);
284 +                                       break;
285 +                       }
286 +                       /* wait for adc */
287 +                       try_to_freeze();
288 +                       schedule_timeout(1000 * HZ);
289 +                       ucb1x00_disable_irq(ts->ucb, UCB_IRQ_ADC, UCB_FALLING);
290 +                       data[state] = UCB_ADC_DAT(ucb1x00_reg_read(ts->ucb, UCB_ADC_DATA));
291 +                       ucb1x00_adc_disable(ts->ucb);
292 +               }       
293 +               
294 +               /* If not pressed any more, try to sleep! */
295 +               if (data[2] < 300) {
296 +                       set_task_state(tsk, TASK_INTERRUPTIBLE);
297 +                       ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_RISING);
298 +                       ucb1x00_ts_mode_int(ts);
299 +                       ucb1x00_disable(ts->ucb);
300 +                       ucb1x00_ts_event_release(ts);
301 +                       try_to_freeze();
302 +                       schedule_timeout(1000 * HZ);
303 +                       ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_RISING);
304 +                       ucb1x00_enable(ts->ucb);
305 +               } else {        
306 +                       ucb1x00_ts_evt_add(ts, data[2], data[1], data[0]);
307 +               }
308 +               ucb1x00_disable(ts->ucb);
309 +               msleep(20);
310 +               ucb1x00_enable(ts->ucb);
311 +       }
312 +
313 +       remove_wait_queue(&ts->irq_wait, &wait);
314 +
315 +       ts->rtask = NULL;
316 +       return 0;
317 +}
318 +
319 +/*
320 + * We only detect touch screen _touches_ with this interrupt
321 + * handler, and even then we just schedule our task.
322 + */
323 +static void ucb1x00_ts_irq(int idx, void *id)
324 +{
325 +       struct ucb1x00_ts *ts = id;
326 +       wake_up(&ts->irq_wait);
327 +}
328 +
329 +static void ucb1x00_adc_irq(int idx, void *id)
330 +{
331 +       struct ucb1x00_ts *ts = id;
332 +       wake_up(&ts->irq_wait);
333 +}
334 +
335 +static int ucb1x00_ts_open(struct input_dev *idev)
336 +{
337 +       struct ucb1x00_ts *ts = input_get_drvdata(idev);
338 +       int ret = 0;
339 +
340 +       BUG_ON(ts->rtask);
341 +
342 +       init_waitqueue_head(&ts->irq_wait);
343 +       
344 +       ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts);
345 +       if (ret < 0)
346 +               return ret;
347 +       
348 +       ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_ADC, ucb1x00_adc_irq, ts);
349 +       if (ret < 0) {
350 +               ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
351 +               return ret;
352 +       }
353 +
354 +       ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_RISING);
355 +       
356 +       /*
357 +        * If we do this at all, we should allow the user to
358 +        * measure and read the X and Y resistance at any time.
359 +        */
360 +       ucb1x00_adc_enable(ts->ucb);
361 +       ts->x_res = ucb1x00_ts_read_xres(ts);
362 +       ts->y_res = ucb1x00_ts_read_yres(ts);
363 +       ucb1x00_adc_disable(ts->ucb);
364 +
365 +       if (machine_is_collie()) {
366 +               ucb1x00_io_set_dir(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK);
367 +       }
368 +
369 +       ts->rtask = kthread_run(ucb1x00_thread, ts, "ktsd");
370 +       if (!IS_ERR(ts->rtask)) {
371 +               ret = 0;
372 +       } else {
373 +               ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
374 +               ts->rtask = NULL;
375 +               ret = -EFAULT;
376 +       }
377 +       
378 +       return ret;
379 +}
380 +
381 +/*
382 + * Release touchscreen resources.  Disable IRQs.
383 + */
384 +static void ucb1x00_ts_close(struct input_dev *idev)
385 +{
386 +       struct ucb1x00_ts *ts = input_get_drvdata(idev);
387 +
388 +       if (ts->rtask)
389 +               kthread_stop(ts->rtask);
390 +
391 +       ucb1x00_enable(ts->ucb);
392 +       ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
393 +       ucb1x00_free_irq(ts->ucb, UCB_IRQ_ADC, ts);
394 +       ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0);
395 +       ucb1x00_disable(ts->ucb);
396 +}
397 +
398 +#ifdef CONFIG_PM
399 +static int ucb1x00_ts_resume(struct ucb1x00_dev *dev)
400 +{
401 +       struct ucb1x00_ts *ts = dev->priv;
402 +
403 +       if (ts->rtask != NULL) {
404 +               /*
405 +                * Restart the TS thread to ensure the
406 +                * TS interrupt mode is set up again
407 +                * after sleep.
408 +                */
409 +               wake_up(&ts->irq_wait);
410 +       }
411 +       return 0;
412 +}
413 +#else
414 +#define ucb1x00_ts_resume NULL
415 +#endif
416 +
417 +
418 +/*
419 + * Initialisation.
420 + */
421 +static int ucb1x00_ts_add(struct ucb1x00_dev *dev)
422 +{
423 +       struct ucb1x00_ts *ts;
424 +       struct input_dev *idev;
425 +       int err;
426 +
427 +       ts = kzalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL);
428 +       idev = input_allocate_device();
429 +       if (!ts || !idev) {
430 +               err = -ENOMEM;
431 +               goto fail;
432 +       }
433 +
434 +       ts->ucb = dev->ucb;
435 +       ts->idev = idev;
436 +       ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC;
437 +
438 +       input_set_drvdata(idev, ts);
439 +       idev->name       = "Touchscreen panel";
440 +       idev->id.product = ts->ucb->id;
441 +       idev->open       = ucb1x00_ts_open;
442 +       idev->close      = ucb1x00_ts_close;
443 +
444 +       __set_bit(EV_ABS, idev->evbit);
445 +       __set_bit(ABS_X, idev->absbit);
446 +       __set_bit(ABS_Y, idev->absbit);
447 +       __set_bit(ABS_PRESSURE, idev->absbit);
448 +
449 +        input_set_abs_params(ts->idev, ABS_X, 0, 450, 0, 0);
450 +        input_set_abs_params(ts->idev, ABS_Y, 200, 800, 0, 0);
451 +        input_set_abs_params(ts->idev, ABS_PRESSURE, 400, 800, 0, 0);
452 +
453 +
454 +       err = input_register_device(idev);
455 +       if (err)
456 +               goto fail;
457 +
458 +       dev->priv = ts;
459 +
460 +       return 0;
461 +
462 + fail:
463 +       input_free_device(idev);
464 +       kfree(ts);
465 +       return err;
466 +}
467 +
468 +static void ucb1x00_ts_remove(struct ucb1x00_dev *dev)
469 +{
470 +       struct ucb1x00_ts *ts = dev->priv;
471 +
472 +       input_unregister_device(ts->idev);
473 +       kfree(ts);
474 +}
475 +
476 +static struct ucb1x00_driver ucb1x00_ts_driver = {
477 +       .add            = ucb1x00_ts_add,
478 +       .remove         = ucb1x00_ts_remove,
479 +       .resume         = ucb1x00_ts_resume,
480 +};
481 +
482 +static int __init ucb1x00_ts_init(void)
483 +{
484 +       return ucb1x00_register_driver(&ucb1x00_ts_driver);
485 +}
486 +
487 +static void __exit ucb1x00_ts_exit(void)
488 +{
489 +       ucb1x00_unregister_driver(&ucb1x00_ts_driver);
490 +}
491 +
492 +module_param(adcsync, int, 0444);
493 +module_init(ucb1x00_ts_init);
494 +module_exit(ucb1x00_ts_exit);
495 +
496 +MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
497 +MODULE_DESCRIPTION("UCB1x00 touchscreen driver");
498 +MODULE_LICENSE("GPL");
499 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
500 index 4981aff..7bbba6e 100644
501 --- a/drivers/mfd/Makefile
502 +++ b/drivers/mfd/Makefile
503 @@ -24,7 +24,6 @@ obj-$(CONFIG_MFD_CORE)                += mfd-core.o
504  obj-$(CONFIG_MCP)              += mcp-core.o
505  obj-$(CONFIG_MCP_SA11X0)       += mcp-sa11x0.o
506  obj-$(CONFIG_MCP_UCB1200)      += ucb1x00-core.o
507 -
508  ifeq ($(CONFIG_SA1100_ASSABET),y)
509  obj-$(CONFIG_MCP_UCB1200)      += ucb1x00-assabet.o
510  endif
511 diff --git a/include/linux/mfd/ucb1x00.h b/include/linux/mfd/ucb1x00.h
512 index eac3463..70eb763 100644
513 --- a/include/linux/mfd/ucb1x00.h
514 +++ b/include/linux/mfd/ucb1x00.h
515 @@ -35,7 +35,10 @@
516  #define UCB_IE_TCLIP           (1 << 14)
517  #define UCB_IE_ACLIP           (1 << 15)
518  
519 +/* UCB1200 irqs */
520 +#define UCB_IRQ_ADC            11
521  #define UCB_IRQ_TSPX           12
522 +#define UCB_IRQ_TSMX           13
523  
524  #define UCB_TC_A       0x05
525  #define UCB_TC_A_LOOP          (1 << 7)        /* UCB1200 */
526 -- 
527 1.5.6.5
528