OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [mfd/] [ucb1x00-ts.c] - Blame information for rev 78

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 *  Touchscreen driver for UCB1x00-based touchscreens
3
 *
4
 *  Copyright (C) 2001 Russell King, All Rights Reserved.
5
 *  Copyright (C) 2005 Pavel Machek
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License version 2 as
9
 * published by the Free Software Foundation.
10
 *
11
 * 21-Jan-2002 <jco@ict.es> :
12
 *
13
 * Added support for synchronous A/D mode. This mode is useful to
14
 * avoid noise induced in the touchpanel by the LCD, provided that
15
 * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin.
16
 * It is important to note that the signal connected to the ADCSYNC
17
 * pin should provide pulses even when the LCD is blanked, otherwise
18
 * a pen touch needed to unblank the LCD will never be read.
19
 */
20
#include <linux/module.h>
21
#include <linux/moduleparam.h>
22
#include <linux/init.h>
23
#include <linux/smp.h>
24
#include <linux/sched.h>
25
#include <linux/completion.h>
26
#include <linux/delay.h>
27
#include <linux/string.h>
28
#include <linux/input.h>
29
#include <linux/device.h>
30
#include <linux/freezer.h>
31
#include <linux/slab.h>
32
#include <linux/kthread.h>
33
 
34
#include <asm/dma.h>
35
#include <asm/semaphore.h>
36
#include <asm/arch/collie.h>
37
#include <asm/mach-types.h>
38
 
39
#include "ucb1x00.h"
40
 
41
 
42
struct ucb1x00_ts {
43
        struct input_dev        *idev;
44
        struct ucb1x00          *ucb;
45
 
46
        wait_queue_head_t       irq_wait;
47
        struct task_struct      *rtask;
48
        u16                     x_res;
49
        u16                     y_res;
50
 
51
        unsigned int            restart:1;
52
        unsigned int            adcsync:1;
53
};
54
 
55
static int adcsync;
56
 
57
static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y)
58
{
59
        struct input_dev *idev = ts->idev;
60
 
61
        input_report_abs(idev, ABS_X, x);
62
        input_report_abs(idev, ABS_Y, y);
63
        input_report_abs(idev, ABS_PRESSURE, pressure);
64
        input_sync(idev);
65
}
66
 
67
static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts)
68
{
69
        struct input_dev *idev = ts->idev;
70
 
71
        input_report_abs(idev, ABS_PRESSURE, 0);
72
        input_sync(idev);
73
}
74
 
75
/*
76
 * Switch to interrupt mode.
77
 */
78
static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts)
79
{
80
        ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
81
                        UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
82
                        UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
83
                        UCB_TS_CR_MODE_INT);
84
}
85
 
86
/*
87
 * Switch to pressure mode, and read pressure.  We don't need to wait
88
 * here, since both plates are being driven.
89
 */
90
static inline unsigned int ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts)
91
{
92
        if (machine_is_collie()) {
93
                ucb1x00_io_write(ts->ucb, COLLIE_TC35143_GPIO_TBL_CHK, 0);
94
                ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
95
                                  UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMX_POW |
96
                                  UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
97
 
98
                udelay(55);
99
 
100
                return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_AD2, ts->adcsync);
101
        } else {
102
                ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
103
                                  UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
104
                                  UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
105
                                  UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
106
 
107
                return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
108
        }
109
}
110
 
111
/*
112
 * Switch to X position mode and measure Y plate.  We switch the plate
113
 * configuration in pressure mode, then switch to position mode.  This
114
 * gives a faster response time.  Even so, we need to wait about 55us
115
 * for things to stabilise.
116
 */
117
static inline unsigned int ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts)
118
{
119
        if (machine_is_collie())
120
                ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK);
121
        else {
122
                ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
123
                                  UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
124
                                  UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
125
                ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
126
                                  UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
127
                                  UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
128
        }
129
        ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
130
                        UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
131
                        UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
132
 
133
        udelay(55);
134
 
135
        return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
136
}
137
 
138
/*
139
 * Switch to Y position mode and measure X plate.  We switch the plate
140
 * configuration in pressure mode, then switch to position mode.  This
141
 * gives a faster response time.  Even so, we need to wait about 55us
142
 * for things to stabilise.
143
 */
144
static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts)
145
{
146
        if (machine_is_collie())
147
                ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK);
148
        else {
149
                ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
150
                                  UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
151
                                  UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
152
                ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
153
                                  UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
154
                                  UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
155
        }
156
 
157
        ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
158
                        UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
159
                        UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
160
 
161
        udelay(55);
162
 
163
        return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync);
164
}
165
 
166
/*
167
 * Switch to X plate resistance mode.  Set MX to ground, PX to
168
 * supply.  Measure current.
169
 */
170
static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts)
171
{
172
        ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
173
                        UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
174
                        UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
175
        return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
176
}
177
 
178
/*
179
 * Switch to Y plate resistance mode.  Set MY to ground, PY to
180
 * supply.  Measure current.
181
 */
182
static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts)
183
{
184
        ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
185
                        UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
186
                        UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
187
        return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
188
}
189
 
190
static inline int ucb1x00_ts_pen_down(struct ucb1x00_ts *ts)
191
{
192
        unsigned int val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR);
193
 
194
        if (machine_is_collie())
195
                return (!(val & (UCB_TS_CR_TSPX_LOW)));
196
        else
197
                return (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW));
198
}
199
 
200
/*
201
 * This is a RT kernel thread that handles the ADC accesses
202
 * (mainly so we can use semaphores in the UCB1200 core code
203
 * to serialise accesses to the ADC).
204
 */
205
static int ucb1x00_thread(void *_ts)
206
{
207
        struct ucb1x00_ts *ts = _ts;
208
        struct task_struct *tsk = current;
209
        DECLARE_WAITQUEUE(wait, tsk);
210
        int valid = 0;
211
 
212
        set_freezable();
213
        add_wait_queue(&ts->irq_wait, &wait);
214
        while (!kthread_should_stop()) {
215
                unsigned int x, y, p;
216
                signed long timeout;
217
 
218
                ts->restart = 0;
219
 
220
                ucb1x00_adc_enable(ts->ucb);
221
 
222
                x = ucb1x00_ts_read_xpos(ts);
223
                y = ucb1x00_ts_read_ypos(ts);
224
                p = ucb1x00_ts_read_pressure(ts);
225
 
226
                /*
227
                 * Switch back to interrupt mode.
228
                 */
229
                ucb1x00_ts_mode_int(ts);
230
                ucb1x00_adc_disable(ts->ucb);
231
 
232
                msleep(10);
233
 
234
                ucb1x00_enable(ts->ucb);
235
 
236
 
237
                if (ucb1x00_ts_pen_down(ts)) {
238
                        set_task_state(tsk, TASK_INTERRUPTIBLE);
239
 
240
                        ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, machine_is_collie() ? UCB_RISING : UCB_FALLING);
241
                        ucb1x00_disable(ts->ucb);
242
 
243
                        /*
244
                         * If we spat out a valid sample set last time,
245
                         * spit out a "pen off" sample here.
246
                         */
247
                        if (valid) {
248
                                ucb1x00_ts_event_release(ts);
249
                                valid = 0;
250
                        }
251
 
252
                        timeout = MAX_SCHEDULE_TIMEOUT;
253
                } else {
254
                        ucb1x00_disable(ts->ucb);
255
 
256
                        /*
257
                         * Filtering is policy.  Policy belongs in user
258
                         * space.  We therefore leave it to user space
259
                         * to do any filtering they please.
260
                         */
261
                        if (!ts->restart) {
262
                                ucb1x00_ts_evt_add(ts, p, x, y);
263
                                valid = 1;
264
                        }
265
 
266
                        set_task_state(tsk, TASK_INTERRUPTIBLE);
267
                        timeout = HZ / 100;
268
                }
269
 
270
                try_to_freeze();
271
 
272
                schedule_timeout(timeout);
273
        }
274
 
275
        remove_wait_queue(&ts->irq_wait, &wait);
276
 
277
        ts->rtask = NULL;
278
        return 0;
279
}
280
 
281
/*
282
 * We only detect touch screen _touches_ with this interrupt
283
 * handler, and even then we just schedule our task.
284
 */
285
static void ucb1x00_ts_irq(int idx, void *id)
286
{
287
        struct ucb1x00_ts *ts = id;
288
 
289
        ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING);
290
        wake_up(&ts->irq_wait);
291
}
292
 
293
static int ucb1x00_ts_open(struct input_dev *idev)
294
{
295
        struct ucb1x00_ts *ts = input_get_drvdata(idev);
296
        int ret = 0;
297
 
298
        BUG_ON(ts->rtask);
299
 
300
        init_waitqueue_head(&ts->irq_wait);
301
        ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts);
302
        if (ret < 0)
303
                goto out;
304
 
305
        /*
306
         * If we do this at all, we should allow the user to
307
         * measure and read the X and Y resistance at any time.
308
         */
309
        ucb1x00_adc_enable(ts->ucb);
310
        ts->x_res = ucb1x00_ts_read_xres(ts);
311
        ts->y_res = ucb1x00_ts_read_yres(ts);
312
        ucb1x00_adc_disable(ts->ucb);
313
 
314
        ts->rtask = kthread_run(ucb1x00_thread, ts, "ktsd");
315
        if (!IS_ERR(ts->rtask)) {
316
                ret = 0;
317
        } else {
318
                ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
319
                ts->rtask = NULL;
320
                ret = -EFAULT;
321
        }
322
 
323
 out:
324
        return ret;
325
}
326
 
327
/*
328
 * Release touchscreen resources.  Disable IRQs.
329
 */
330
static void ucb1x00_ts_close(struct input_dev *idev)
331
{
332
        struct ucb1x00_ts *ts = input_get_drvdata(idev);
333
 
334
        if (ts->rtask)
335
                kthread_stop(ts->rtask);
336
 
337
        ucb1x00_enable(ts->ucb);
338
        ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
339
        ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0);
340
        ucb1x00_disable(ts->ucb);
341
}
342
 
343
#ifdef CONFIG_PM
344
static int ucb1x00_ts_resume(struct ucb1x00_dev *dev)
345
{
346
        struct ucb1x00_ts *ts = dev->priv;
347
 
348
        if (ts->rtask != NULL) {
349
                /*
350
                 * Restart the TS thread to ensure the
351
                 * TS interrupt mode is set up again
352
                 * after sleep.
353
                 */
354
                ts->restart = 1;
355
                wake_up(&ts->irq_wait);
356
        }
357
        return 0;
358
}
359
#else
360
#define ucb1x00_ts_resume NULL
361
#endif
362
 
363
 
364
/*
365
 * Initialisation.
366
 */
367
static int ucb1x00_ts_add(struct ucb1x00_dev *dev)
368
{
369
        struct ucb1x00_ts *ts;
370
        struct input_dev *idev;
371
        int err;
372
 
373
        ts = kzalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL);
374
        idev = input_allocate_device();
375
        if (!ts || !idev) {
376
                err = -ENOMEM;
377
                goto fail;
378
        }
379
 
380
        ts->ucb = dev->ucb;
381
        ts->idev = idev;
382
        ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC;
383
 
384
        idev->name       = "Touchscreen panel";
385
        idev->id.product = ts->ucb->id;
386
        idev->open       = ucb1x00_ts_open;
387
        idev->close      = ucb1x00_ts_close;
388
 
389
        __set_bit(EV_ABS, idev->evbit);
390
        __set_bit(ABS_X, idev->absbit);
391
        __set_bit(ABS_Y, idev->absbit);
392
        __set_bit(ABS_PRESSURE, idev->absbit);
393
 
394
        input_set_drvdata(idev, ts);
395
 
396
        err = input_register_device(idev);
397
        if (err)
398
                goto fail;
399
 
400
        dev->priv = ts;
401
 
402
        return 0;
403
 
404
 fail:
405
        input_free_device(idev);
406
        kfree(ts);
407
        return err;
408
}
409
 
410
static void ucb1x00_ts_remove(struct ucb1x00_dev *dev)
411
{
412
        struct ucb1x00_ts *ts = dev->priv;
413
 
414
        input_unregister_device(ts->idev);
415
        kfree(ts);
416
}
417
 
418
static struct ucb1x00_driver ucb1x00_ts_driver = {
419
        .add            = ucb1x00_ts_add,
420
        .remove         = ucb1x00_ts_remove,
421
        .resume         = ucb1x00_ts_resume,
422
};
423
 
424
static int __init ucb1x00_ts_init(void)
425
{
426
        return ucb1x00_register_driver(&ucb1x00_ts_driver);
427
}
428
 
429
static void __exit ucb1x00_ts_exit(void)
430
{
431
        ucb1x00_unregister_driver(&ucb1x00_ts_driver);
432
}
433
 
434
module_param(adcsync, int, 0444);
435
module_init(ucb1x00_ts_init);
436
module_exit(ucb1x00_ts_exit);
437
 
438
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
439
MODULE_DESCRIPTION("UCB1x00 touchscreen driver");
440
MODULE_LICENSE("GPL");

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.