ISOify.
[pandora-kernel.git] / arch / mips / au1000 / db1x00 / mirage_ts.c
1 /*
2  * linux/arch/mips/au1000/db1x00/mirage_ts.c
3  *
4  * BRIEF MODULE DESCRIPTION
5  *      Glue between Mirage board-specific touchscreen pieces
6  *      and generic Wolfson Codec touchscreen support.
7  *
8  *      Based on pb1100_ts.c used in Hydrogen II.
9  *
10  * Copyright (c) 2003 Embedded Edge, LLC
11  *              dan@embeddededge.com
12  *
13  *  This program is free software; you can redistribute  it and/or modify it
14  *  under  the terms of  the GNU General  Public License as published by the
15  *  Free Software Foundation;  either version 2 of the  License, or (at your
16  *  option) any later version.
17  *
18  *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
19  *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
20  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
21  *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
22  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
24  *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25  *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
26  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  *  You should have received a copy of the  GNU General Public License along
30  *  with this program; if not, write  to the Free Software Foundation, Inc.,
31  *  675 Mass Ave, Cambridge, MA 02139, USA.
32  */
33
34 #include <linux/config.h>
35 #include <linux/types.h>
36 #include <linux/module.h>
37 #include <linux/sched.h>
38 #include <linux/kernel.h>
39 #include <linux/init.h>
40 #include <linux/fs.h>
41 #include <linux/poll.h>
42 #include <linux/proc_fs.h>
43 #include <linux/smp.h>
44 #include <linux/smp_lock.h>
45 #include <linux/wait.h>
46
47 #include <asm/segment.h>
48 #include <asm/irq.h>
49 #include <asm/uaccess.h>
50 #include <asm/delay.h>
51 #include <asm/au1000.h>
52
53 /*
54  *  Imported interface to Wolfson Codec driver.
55  */
56 extern void *wm97xx_ts_get_handle(int which);
57 extern int wm97xx_ts_ready(void* ts_handle);
58 extern void wm97xx_ts_set_cal(void* ts_handle, int xscale, int xtrans, int yscale, int ytrans);
59 extern u16 wm97xx_ts_get_ac97(void* ts_handle, u8 reg);
60 extern void wm97xx_ts_set_ac97(void* ts_handle, u8 reg, u16 val);
61 extern int wm97xx_ts_read_data(void* ts_handle, long* x, long* y, long* pressure);
62 extern void wm97xx_ts_send_data(void* ts_handle, long x, long y, long z);
63
64 int wm97xx_comodule_present = 1;
65
66
67 #define TS_NAME "mirage_ts"
68
69 #define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
70 #define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
71 #define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
72 #define DPRINTK(format, arg...) printk("%s: " format "\n", __FUNCTION__ , ## arg)
73
74
75 #define PEN_DOWN_IRQ    AU1000_GPIO_7
76
77 static struct task_struct *ts_task = 0;
78 static DECLARE_COMPLETION(ts_complete);
79 static DECLARE_WAIT_QUEUE_HEAD(pendown_wait);
80
81 #ifdef CONFIG_WM97XX_FIVEWIRETS
82 static int release_pressure = 1;
83 #else
84 static int release_pressure = 50;
85 #endif
86
87 typedef struct {
88    long x;
89    long y;
90 } DOWN_EVENT;
91
92 #define SAMPLE_RATE     50      /* samples per second */
93 #define PEN_DEBOUNCE    5       /* samples for settling - fn of SAMPLE_RATE */
94 #define PEN_UP_TIMEOUT  10      /* in seconds */
95 #define PEN_UP_SETTLE   5       /* samples per second */
96
97 static struct {
98         int xscale;
99         int xtrans;
100         int yscale;
101         int ytrans;
102 } mirage_ts_cal =
103 {
104 #if 0
105         .xscale   = 84,
106         .xtrans = -157,
107         .yscale   = 66,
108         .ytrans = -150,
109 #else
110         .xscale   = 84,
111         .xtrans = -150,
112         .yscale   = 66,
113         .ytrans = -146,
114 #endif
115 };
116
117
118 static void pendown_irq(int irqnr, void *devid, struct pt_regs *regs)
119 {
120 //DPRINTK("got one 0x%x", au_readl(SYS_PINSTATERD));
121         wake_up(&pendown_wait);
122 }
123
124 static int ts_thread(void *id)
125 {
126         static int pen_was_down = 0;
127         static DOWN_EVENT pen_xy;
128         long x, y, z;
129         void *ts;       /* handle */
130         struct task_struct *tsk = current;
131         int timeout = HZ / SAMPLE_RATE;
132
133         ts_task = tsk;
134
135         daemonize();
136         tsk->tty = NULL;
137         tsk->policy = SCHED_FIFO;
138         tsk->rt_priority = 1;
139         strcpy(tsk->comm, "touchscreen");
140
141         /* only want to receive SIGKILL */
142         spin_lock_irq(&tsk->sigmask_lock);
143         siginitsetinv(&tsk->blocked, sigmask(SIGKILL));
144         recalc_sigpending(tsk);
145         spin_unlock_irq(&tsk->sigmask_lock);
146
147         /* get handle for codec */
148         ts = wm97xx_ts_get_handle(0);
149
150         /* proceed only after everybody is ready */
151         wait_event_timeout(pendown_wait, wm97xx_ts_ready(ts), HZ/4);
152
153         /* board-specific calibration */
154         wm97xx_ts_set_cal(ts,
155                         mirage_ts_cal.xscale,
156                         mirage_ts_cal.xtrans,
157                         mirage_ts_cal.yscale,
158                         mirage_ts_cal.ytrans);
159
160         /* route Wolfson pendown interrupts to our GPIO */
161         au_sync();
162         wm97xx_ts_set_ac97(ts, 0x4c, wm97xx_ts_get_ac97(ts, 0x4c) & ~0x0008);
163         au_sync();
164         wm97xx_ts_set_ac97(ts, 0x56, wm97xx_ts_get_ac97(ts, 0x56) & ~0x0008);
165         au_sync();
166         wm97xx_ts_set_ac97(ts, 0x52, wm97xx_ts_get_ac97(ts, 0x52) | 0x2008);
167         au_sync();
168
169         for (;;) {
170                 interruptible_sleep_on_timeout(&pendown_wait, timeout);
171                 disable_irq(PEN_DOWN_IRQ);
172                 if (signal_pending(tsk)) {
173                         break;
174                 }
175
176                 /* read codec */
177                 if (!wm97xx_ts_read_data(ts, &x, &y, &z))
178                         z = 0;  /* treat no-data and pen-up the same */
179
180                 if (signal_pending(tsk)) {
181                         break;
182                 }
183
184                 if (z >= release_pressure) {
185                         y = ~y; /* top to bottom */
186                         if (pen_was_down > 1 /*&& pen_was_down < PEN_DEBOUNCE*/) {//THXXX
187                                 /* bounce ? */
188                                 x = pen_xy.x;
189                                 y = pen_xy.y;
190                                 --pen_was_down;
191                         } else if (pen_was_down <= 1) {
192                                 pen_xy.x = x;
193                                 pen_xy.y = y;
194                                 if (pen_was_down)
195                                         wm97xx_ts_send_data(ts, x, y, z);
196                                 pen_was_down = PEN_DEBOUNCE;
197                         }
198                         //wm97xx_ts_send_data(ts, x, y, z);
199                         timeout = HZ / SAMPLE_RATE;
200                 } else {
201                         if (pen_was_down) {
202                                 if (--pen_was_down)
203                                         z = release_pressure;
204                                 else //THXXX
205                                 wm97xx_ts_send_data(ts, pen_xy.x, pen_xy.y, z);
206                         }
207                         /* The pendown signal takes some time to settle after
208                          * reading the pen pressure so wait a little
209                          * before enabling the pen.
210                          */
211                         if (! pen_was_down) {
212 //                              interruptible_sleep_on_timeout(&pendown_wait, HZ / PEN_UP_SETTLE);
213                                 timeout = HZ * PEN_UP_TIMEOUT;
214                         }
215                 }
216                 enable_irq(PEN_DOWN_IRQ);
217         }
218         enable_irq(PEN_DOWN_IRQ);
219         ts_task = NULL;
220         complete(&ts_complete);
221         return 0;
222 }
223
224 static int __init ts_mirage_init(void)
225 {
226         int ret;
227
228         /* pen down signal is connected to GPIO 7 */
229
230         ret = request_irq(PEN_DOWN_IRQ, pendown_irq, 0, "ts-pendown", NULL);
231         if (ret) {
232                 err("unable to get pendown irq%d: [%d]", PEN_DOWN_IRQ, ret);
233                 return ret;
234         }
235
236         lock_kernel();
237         ret = kernel_thread(ts_thread, NULL, CLONE_FS | CLONE_FILES);
238         if (ret < 0) {
239                 unlock_kernel();
240                 return ret;
241         }
242         unlock_kernel();
243
244         info("Mirage touchscreen IRQ initialized.");
245
246         return 0;
247 }
248
249 static void __exit ts_mirage_exit(void)
250 {
251         if (ts_task) {
252                 send_sig(SIGKILL, ts_task, 1);
253                 wait_for_completion(&ts_complete);
254         }
255
256         free_irq(PEN_DOWN_IRQ, NULL);
257 }
258
259 module_init(ts_mirage_init);
260 module_exit(ts_mirage_exit);
261