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 
46 #include <asm/segment.h>
47 #include <asm/irq.h>
48 #include <asm/uaccess.h>
49 #include <asm/delay.h>
50 #include <asm/au1000.h>
51 
52 /*
53  *  Imported interface to Wolfson Codec driver.
54  */
55 extern void *wm97xx_ts_get_handle(int which);
56 extern int wm97xx_ts_ready(void* ts_handle);
57 extern void wm97xx_ts_set_cal(void* ts_handle, int xscale, int xtrans, int yscale, int ytrans);
58 extern u16 wm97xx_ts_get_ac97(void* ts_handle, u8 reg);
59 extern void wm97xx_ts_set_ac97(void* ts_handle, u8 reg, u16 val);
60 extern int wm97xx_ts_read_data(void* ts_handle, long* x, long* y, long* pressure);
61 extern void wm97xx_ts_send_data(void* ts_handle, long x, long y, long z);
62 
63 int wm97xx_comodule_present = 1;
64 
65 
66 #define TS_NAME "mirage_ts"
67 
68 #define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
69 #define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
70 #define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
71 #define DPRINTK(format, arg...) printk(__FUNCTION__ ": " format "\n" , ## arg)
72 
73 
74 #define PEN_DOWN_IRQ	AU1000_GPIO_7
75 
76 static struct task_struct *ts_task = 0;
77 static DECLARE_COMPLETION(ts_complete);
78 static DECLARE_WAIT_QUEUE_HEAD(pendown_wait);
79 
80 #ifdef CONFIG_WM97XX_FIVEWIRETS
81 static int release_pressure = 1;
82 #else
83 static int release_pressure = 50;
84 #endif
85 
86 typedef struct {
87    long x;
88    long y;
89 } DOWN_EVENT;
90 
91 #define SAMPLE_RATE	50	/* samples per second */
92 #define PEN_DEBOUNCE	5	/* samples for settling - fn of SAMPLE_RATE */
93 #define PEN_UP_TIMEOUT	10	/* in seconds */
94 #define PEN_UP_SETTLE	5	/* samples per second */
95 
96 static struct {
97 	int xscale;
98 	int xtrans;
99 	int yscale;
100 	int ytrans;
101 } mirage_ts_cal =
102 {
103 #if 0
104 	xscale:   84,
105 	xtrans: -157,
106 	yscale:   66,
107 	ytrans: -150,
108 #else
109 	xscale:   84,
110 	xtrans: -150,
111 	yscale:   66,
112 	ytrans: -146,
113 #endif
114 };
115 
116 
pendown_irq(int irqnr,void * devid,struct pt_regs * regs)117 static void pendown_irq(int irqnr, void *devid, struct pt_regs *regs)
118 {
119 //DPRINTK("got one 0x%x", au_readl(SYS_PINSTATERD));
120 	wake_up(&pendown_wait);
121 }
122 
ts_thread(void * id)123 static int ts_thread(void *id)
124 {
125 	static int pen_was_down = 0;
126 	static DOWN_EVENT pen_xy;
127 	long x, y, z;
128 	void *ts;	/* handle */
129 	struct task_struct *tsk = current;
130 	int timeout = HZ / SAMPLE_RATE;
131 
132 	ts_task = tsk;
133 
134 	daemonize();
135 	tsk->tty = NULL;
136 	tsk->policy = SCHED_FIFO;
137 	tsk->rt_priority = 1;
138 	strcpy(tsk->comm, "touchscreen");
139 
140 	/* only want to receive SIGKILL */
141 	spin_lock_irq(&tsk->sigmask_lock);
142 	siginitsetinv(&tsk->blocked, sigmask(SIGKILL));
143 	recalc_sigpending(tsk);
144 	spin_unlock_irq(&tsk->sigmask_lock);
145 
146 	/* get handle for codec */
147 	ts = wm97xx_ts_get_handle(0);
148 
149 	/* proceed only after everybody is ready */
150 	while ( ! wm97xx_ts_ready(ts) ) {
151 		/* give a little time for initializations to complete */
152 		interruptible_sleep_on_timeout(&pendown_wait, HZ / 4);
153 	}
154 
155 	/* board-specific calibration */
156 	wm97xx_ts_set_cal(ts,
157 			mirage_ts_cal.xscale,
158 			mirage_ts_cal.xtrans,
159 			mirage_ts_cal.yscale,
160 			mirage_ts_cal.ytrans);
161 
162 	/* route Wolfson pendown interrupts to our GPIO */
163 	au_sync();
164 	wm97xx_ts_set_ac97(ts, 0x4c, wm97xx_ts_get_ac97(ts, 0x4c) & ~0x0008);
165 	au_sync();
166 	wm97xx_ts_set_ac97(ts, 0x56, wm97xx_ts_get_ac97(ts, 0x56) & ~0x0008);
167 	au_sync();
168 	wm97xx_ts_set_ac97(ts, 0x52, wm97xx_ts_get_ac97(ts, 0x52) | 0x2008);
169 	au_sync();
170 
171 	for (;;) {
172 		interruptible_sleep_on_timeout(&pendown_wait, timeout);
173 		disable_irq(PEN_DOWN_IRQ);
174 		if (signal_pending(tsk)) {
175 			break;
176 		}
177 
178 		/* read codec */
179 		if (!wm97xx_ts_read_data(ts, &x, &y, &z))
180 			z = 0;	/* treat no-data and pen-up the same */
181 
182 		if (signal_pending(tsk)) {
183 			break;
184 		}
185 
186 		if (z >= release_pressure) {
187 			y = ~y;	/* top to bottom */
188 			if (pen_was_down > 1 /*&& pen_was_down < PEN_DEBOUNCE*/) {//THXXX
189 				/* bounce ? */
190 				x = pen_xy.x;
191 				y = pen_xy.y;
192 				--pen_was_down;
193 			} else if (pen_was_down <= 1) {
194 				pen_xy.x = x;
195 				pen_xy.y = y;
196 				if (pen_was_down)
197 					wm97xx_ts_send_data(ts, x, y, z);
198 				pen_was_down = PEN_DEBOUNCE;
199 			}
200 			//wm97xx_ts_send_data(ts, x, y, z);
201 			timeout = HZ / SAMPLE_RATE;
202 		} else {
203 			if (pen_was_down) {
204 				if (--pen_was_down)
205 					z = release_pressure;
206 				else //THXXX
207 				wm97xx_ts_send_data(ts, pen_xy.x, pen_xy.y, z);
208 			}
209 			/* The pendown signal takes some time to settle after
210 			 * reading the pen pressure so wait a little
211 			 * before enabling the pen.
212 			 */
213 			if (! pen_was_down) {
214 //				interruptible_sleep_on_timeout(&pendown_wait, HZ / PEN_UP_SETTLE);
215 				timeout = HZ * PEN_UP_TIMEOUT;
216 			}
217 		}
218 		enable_irq(PEN_DOWN_IRQ);
219 	}
220 	enable_irq(PEN_DOWN_IRQ);
221 	ts_task = NULL;
222 	complete(&ts_complete);
223 	return 0;
224 }
225 
ts_mirage_init(void)226 static int __init ts_mirage_init(void)
227 {
228 	int ret;
229 
230 	/* pen down signal is connected to GPIO 7 */
231 
232 	ret = request_irq(PEN_DOWN_IRQ, pendown_irq, 0, "ts-pendown", NULL);
233 	if (ret) {
234 		err("unable to get pendown irq%d: [%d]", PEN_DOWN_IRQ, ret);
235 		return ret;
236 	}
237 
238 	lock_kernel();
239 	ret = kernel_thread(ts_thread, NULL, CLONE_FS | CLONE_FILES);
240 	if (ret < 0) {
241 		unlock_kernel();
242 		return ret;
243 	}
244 	unlock_kernel();
245 
246 	info("Mirage touchscreen IRQ initialized.");
247 
248 	return 0;
249 }
250 
ts_mirage_exit(void)251 static void __exit ts_mirage_exit(void)
252 {
253 	if (ts_task) {
254 		send_sig(SIGKILL, ts_task, 1);
255 		wait_for_completion(&ts_complete);
256 	}
257 
258 	free_irq(PEN_DOWN_IRQ, NULL);
259 }
260 
261 module_init(ts_mirage_init);
262 module_exit(ts_mirage_exit);
263 
264