j6x0tp.c revision 1.1 1 /* $NetBSD: j6x0tp.c,v 1.1 2003/10/19 02:20:25 uwe Exp $ */
2
3 /*
4 * Copyright (c) 2003 Valeriy E. Ushakov
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN 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 USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * 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
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: j6x0tp.c,v 1.1 2003/10/19 02:20:25 uwe Exp $");
32
33 #include <sys/param.h>
34 #include <sys/kernel.h>
35 #include <sys/device.h>
36 #include <sys/malloc.h>
37 #include <sys/systm.h>
38 #include <sys/callout.h>
39 #ifdef GPROF
40 #include <sys/gmon.h>
41 #endif
42
43 #include <dev/wscons/wsconsio.h>
44 #include <dev/wscons/wsmousevar.h>
45 #include <dev/hpc/tpcalibvar.h>
46
47 #include <machine/platid.h>
48 #include <machine/platid_mask.h>
49
50 #include <machine/intr.h>
51
52 #include <sh3/exception.h>
53 #include <sh3/intcreg.h>
54 #include <sh3/pfcreg.h>
55 #include <sh3/adcreg.h>
56
57 #define J6X0TP_DEBUG
58 #if 0 /* XXX: disabled in favor of local version that uses printf_nolog */
59 #define DPRINTF_ENABLE
60 #define DPRINTF_DEBUG j6x0tp_debug
61 #define DPRINTF_LEVEL 0
62 #include <machine/debug.h>
63 #else
64 #ifdef J6X0TP_DEBUG
65 volatile int j6x0tp_debug = 0;
66 #define DPRINTF(arg) if (j6x0tp_debug) printf_nolog arg
67 #define DPRINTFN(n, arg) if (j6x0tp_debug > (n)) printf_nolog arg
68 #else
69 #define DPRINTF(arg) ((void)0)
70 #define DPRINTFN(n, arg) ((void)0)
71 #endif
72 #endif
73
74
75 /* PFC bits pertinent to Jornada 6x0 touchpanel */
76 #define PHDR_TP_PEN_DOWN 0x08
77
78 #define SCPDR_TP_SCAN_ENABLE 0x20
79 #define SCPDR_TP_SCAN_Y 0x02
80 #define SCPDR_TP_SCAN_X 0x01
81
82 /* A/D covnerter channels to get x/y from */
83 #define ADC_CHANNEL_TP_Y 1
84 #define ADC_CHANNEL_TP_X 2
85
86 extern int adc_sample_channel(int); /* XXX: adcvar.h */
87
88
89 struct j6x0tp_softc {
90 struct device sc_dev;
91
92 struct callout sc_touch_ch;
93 int sc_intrlevel;
94 int sc_enabled;
95
96 struct device *sc_wsmousedev;
97 struct tpcalib_softc sc_tpcalib;
98 };
99
100 static int j6x0tp_match(struct device *, struct cfdata *, void *);
101 static void j6x0tp_attach(struct device *, struct device *, void *);
102
103 CFATTACH_DECL(j6x0tp, sizeof(struct j6x0tp_softc),
104 j6x0tp_match, j6x0tp_attach, NULL, NULL);
105
106
107 static int j6x0tp_enable(void *);
108 static int j6x0tp_ioctl(void *, u_long, caddr_t, int, struct proc *);
109 static void j6x0tp_disable(void *);
110
111 const struct wsmouse_accessops j6x0tp_accessops = {
112 j6x0tp_enable,
113 j6x0tp_ioctl,
114 j6x0tp_disable,
115 };
116
117 static const struct wsmouse_calibcoords j6x0tp_default_calib = {
118 0, 0, 639, 239,
119 4,
120 {{ 38, 80, 0, 0 }, /* upper left */
121 { 950, 80, 639, 0 }, /* upper right */
122 { 38, 900, 0, 239 }, /* lower left */
123 { 950, 900, 639, 239 }} /* lower right */
124 };
125
126
127 static int j6x0tp_intr(void *);
128 static void j6x0tp_poll_callout(void *self);
129
130
131 static int
132 j6x0tp_match(struct device *parent, struct cfdata *cfp, void *aux)
133 {
134
135 /*
136 * XXX: does platid_mask_MACH_HP_LX matches _JORNADA_6XX too?
137 * Is 620 wired similarly?
138 */
139 if (!platid_match(&platid, &platid_mask_MACH_HP_JORNADA_6XX))
140 return (0);
141
142 if (strcmp(cfp->cf_name, "j6x0tp") != 0)
143 return (0);
144
145 return (1);
146 }
147
148
149 static void
150 j6x0tp_attach(struct device *parent, struct device *self, void *aux)
151 {
152 struct j6x0tp_softc *sc = (struct j6x0tp_softc *)self;
153 struct wsmousedev_attach_args wsma;
154
155 printf("\n");
156
157 sc->sc_enabled = 0;
158
159 wsma.accessops = &j6x0tp_accessops;
160 wsma.accesscookie = sc;
161
162 sc->sc_wsmousedev = config_found(self, &wsma, wsmousedevprint);
163 if (sc->sc_wsmousedev == NULL)
164 return;
165
166 tpcalib_init(&sc->sc_tpcalib);
167 tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS,
168 (caddr_t)&j6x0tp_default_calib, 0, 0);
169
170 callout_init(&sc->sc_touch_ch);
171
172 intc_intr_establish(SH7709_INTEVT2_IRQ3, IST_EDGE, IPL_TTY,
173 j6x0tp_intr, sc);
174 sc->sc_intrlevel = intc_intr_disable(SH7709_INTEVT2_IRQ3);
175 }
176
177
178 static int
179 j6x0tp_enable(void *self)
180 {
181 struct j6x0tp_softc *sc = (struct j6x0tp_softc *)self;
182 int s;
183
184 DPRINTFN(1, ("%s: enable\n", sc->sc_dev.dv_xname));
185
186 s = spltty();
187
188 sc->sc_enabled = 1;
189 intc_intr_enable(SH7709_INTEVT2_IRQ3, sc->sc_intrlevel);
190
191 splx(s);
192 return (0);
193 }
194
195
196 static void
197 j6x0tp_disable(void *self)
198 {
199 struct j6x0tp_softc *sc = (struct j6x0tp_softc *)self;
200 int s;
201
202 s = spltty();
203
204 DPRINTFN(1, ("%s: disable\n", sc->sc_dev.dv_xname));
205
206 sc->sc_enabled = 0;
207 intc_intr_disable(SH7709_INTEVT2_IRQ3);
208 callout_stop(&sc->sc_touch_ch);
209
210 splx(s);
211 }
212
213
214 static int
215 j6x0tp_intr(void *self)
216 {
217 struct j6x0tp_softc *sc = (struct j6x0tp_softc *)self;
218
219 uint8_t irr0;
220 uint8_t phdr, touched;
221 unsigned int steady, tremor_timeout;
222
223 irr0 = _reg_read_1(SH7709_IRR0);
224 if ((irr0 & IRR0_IRQ3) == 0) {
225 #ifdef DIAGNOSTIC
226 printf("%s: irr0 %02x?\n", sc->sc_dev.dv_xname, irr0);
227 #endif
228 return (0);
229 }
230
231 /*
232 * Number of times the "touched" bit should be read
233 * consecutively.
234 */
235 # define TREMOR_THRESHOLD 0x300
236
237 steady = 0;
238 tremor_timeout = TREMOR_THRESHOLD * 16; /* XXX: arbitrary */
239 touched = PHDR_TP_PEN_DOWN; /* we start with "touched" state */
240
241 do {
242 phdr = _reg_read_1(SH7709_PHDR);
243
244 if ((phdr & PHDR_TP_PEN_DOWN) == touched)
245 ++steady;
246 else {
247 steady = 0;
248 touched = phdr & PHDR_TP_PEN_DOWN;
249 }
250
251 if (--tremor_timeout == 0) {
252 DPRINTF(("%s: tremor timeout!\n",
253 sc->sc_dev.dv_xname));
254 goto served;
255 }
256 } while (steady < TREMOR_THRESHOLD);
257
258 if (touched == 0) {
259 DPRINTFN(1, ("%s: tremor\n", sc->sc_dev.dv_xname));
260 goto served;
261 }
262
263 intc_intr_disable(SH7709_INTEVT2_IRQ3);
264
265 if (sc->sc_enabled)
266 callout_reset(&sc->sc_touch_ch, hz/32,
267 j6x0tp_poll_callout, sc);
268 else
269 DPRINTFN(1, ("%s: intr: !sc_enabled\n", sc->sc_dev.dv_xname));
270
271 served:
272 /* clear the interrupt */
273 _reg_write_1(SH7709_IRR0, irr0 & ~IRR0_IRQ3);
274
275 return (1);
276 }
277
278
279 static void
280 j6x0tp_poll_callout(void *self)
281 {
282 struct j6x0tp_softc *sc = (struct j6x0tp_softc *)self;
283 uint8_t phdr, scpdr;
284 uint8_t irr0;
285 int rawx, rawy, x, y;
286 int s;
287
288 s = spltty();
289
290 if (!sc->sc_enabled) {
291 DPRINTFN(1, ("%s: callout: !sc_enabled\n",
292 sc->sc_dev.dv_xname));
293 splx(s);
294 return;
295 }
296
297 phdr = _reg_read_1(SH7709_PHDR);
298 if ((phdr & PHDR_TP_PEN_DOWN) == 0) {
299 wsmouse_input(sc->sc_wsmousedev, 0, 0, 0, 0, 0); /* mouse up */
300
301 intc_intr_enable(SH7709_INTEVT2_IRQ3, sc->sc_intrlevel);
302
303 irr0 = _reg_read_1(SH7709_IRR0);
304 if ((irr0 & IRR0_IRQ3) != 0)
305 _reg_write_1(SH7709_IRR0, irr0 & ~IRR0_IRQ3);
306
307 splx(s);
308 return;
309 }
310
311 /* XXX: protect accesses to SCPDR? */
312
313 /* Y axis */
314 scpdr = _reg_read_1(SH7709_SCPDR);
315 scpdr |= SCPDR_TP_SCAN_ENABLE;
316 scpdr &= ~SCPDR_TP_SCAN_Y; /* pull low to scan */
317 _reg_write_1(SH7709_SCPDR, scpdr);
318 delay(10);
319 rawy = adc_sample_channel(ADC_CHANNEL_TP_Y);
320
321 /* X axis */
322 scpdr = _reg_read_1(SH7709_SCPDR);
323 scpdr |= SCPDR_TP_SCAN_Y;
324 scpdr &= ~SCPDR_TP_SCAN_X; /* pull low to scan */
325 _reg_write_1(SH7709_SCPDR, scpdr);
326 delay(10);
327 rawx = adc_sample_channel(ADC_CHANNEL_TP_X);
328
329 /* restore SCPDR */
330 scpdr = _reg_read_1(SH7709_SCPDR);
331 scpdr |= SCPDR_TP_SCAN_X;
332 scpdr &= ~SCPDR_TP_SCAN_ENABLE;
333 _reg_write_1(SH7709_SCPDR, scpdr);
334
335 tpcalib_trans(&sc->sc_tpcalib, rawx, rawy, &x, &y);
336
337 DPRINTFN(2, ("%s: %4d %4d -> %3d %3d\n",
338 sc->sc_dev.dv_xname, rawx, rawy, x, y));
339
340 wsmouse_input(sc->sc_wsmousedev, 1, x, y, 0,
341 WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
342
343 callout_schedule(&sc->sc_touch_ch, hz/32);
344 splx(s);
345 }
346
347
348 static int
349 j6x0tp_ioctl(void *self, u_long cmd, caddr_t data, int flag,
350 struct proc *p)
351 {
352 struct j6x0tp_softc *sc = (struct j6x0tp_softc *)self;
353
354 switch (cmd) {
355 case WSMOUSEIO_GTYPE:
356 *(u_int *)data = WSMOUSE_TYPE_TPANEL;
357 return (0);
358
359 case WSMOUSEIO_SCALIBCOORDS:
360 case WSMOUSEIO_GCALIBCOORDS:
361 case WSMOUSEIO_GETID:
362 return (tpcalib_ioctl(&sc->sc_tpcalib, cmd, data, flag, p));
363
364 default:
365 return (EPASSTHROUGH);
366 }
367 }
368