wzero3_tp.c revision 1.1 1 /* $NetBSD: wzero3_tp.c,v 1.1 2010/05/09 10:40:00 nonaka Exp $ */
2
3 /*
4 * Copyright (c) 2010 NONAKA Kimihiro <nonaka (at) netbsd.org>
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 *
16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: wzero3_tp.c,v 1.1 2010/05/09 10:40:00 nonaka Exp $");
31
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/device.h>
36 #include <sys/malloc.h>
37 #include <sys/kernel.h>
38 #include <sys/callout.h>
39
40 #include <machine/bootinfo.h>
41 #include <machine/platid.h>
42 #include <machine/platid_mask.h>
43
44 #include <dev/wscons/wsconsio.h>
45 #include <dev/wscons/wsmousevar.h>
46 #include <dev/wscons/wsdisplayvar.h>
47
48 #include <dev/hpc/hpcfbio.h>
49 #include <dev/hpc/hpctpanelvar.h>
50
51 #include <arm/xscale/pxa2x0cpu.h>
52 #include <arm/xscale/pxa2x0reg.h>
53 #include <arm/xscale/pxa2x0var.h>
54 #include <arm/xscale/xscalereg.h>
55 #include <arm/xscale/pxa2x0_lcd.h>
56 #include <arm/xscale/pxa2x0_gpio.h>
57
58 #include <hpcarm/dev/wzero3_reg.h>
59 #include <hpcarm/dev/wzero3_sspvar.h>
60
61 #ifdef WZERO3TP_DEBUG
62 #define DPRINTF(s) printf s
63 #else
64 #define DPRINTF(s)
65 #endif
66
67 /*
68 * ADS784x touch screen controller
69 */
70 #define ADSCTRL_PD0_SH 0 /* PD0 bit */
71 #define ADSCTRL_PD1_SH 1 /* PD1 bit */
72 #define ADSCTRL_DFR_SH 2 /* SER/DFR bit */
73 #define ADSCTRL_MOD_SH 3 /* Mode bit */
74 #define ADSCTRL_ADR_SH 4 /* Address setting */
75 #define ADSCTRL_STS_SH 7 /* Start bit */
76
77 #define POLL_TIMEOUT_RATE0 ((hz * 150)/1000)
78 #define POLL_TIMEOUT_RATE1 (hz / 100) /* XXX every tick */
79
80 /* Settable via sysctl. */
81 int wzero3tp_rawmode;
82
83 static const struct wsmouse_calibcoords ws007sh_default_calib = {
84 0, 0, 479, 639, /* minx, miny, maxx, maxy */
85 5, /* samplelen */
86 {
87 { 2050, 2024, 240, 320 }, /* rawx, rawy, x, y */
88 { 578, 3471, 48, 64 },
89 { 578, 582, 48, 576 },
90 { 3432, 582, 432, 576 },
91 { 3432, 3471, 432, 64 },
92 }
93 };
94
95 struct wzero3tp_pos {
96 int x;
97 int y;
98 int z; /* touch pressure */
99 };
100
101 struct wzero3tp_model;
102 struct wzero3tp_softc {
103 device_t sc_dev;
104 const struct wzero3tp_model *sc_model;
105 void *sc_gh;
106 struct callout sc_tp_poll;
107 int sc_enabled;
108 int sc_buttons; /* button emulation ? */
109 struct device *sc_wsmousedev;
110 struct wzero3tp_pos sc_oldpos;
111 int sc_resx;
112 int sc_resy;
113 struct tpcalib_softc sc_tpcalib;
114 };
115
116 static int wzero3tp_match(device_t, cfdata_t, void *);
117 static void wzero3tp_attach(device_t, device_t, void *);
118
119 CFATTACH_DECL_NEW(wzero3tp, sizeof(struct wzero3tp_softc),
120 wzero3tp_match, wzero3tp_attach, NULL, NULL);
121
122 static int wzero3tp_enable(void *);
123 static void wzero3tp_disable(void *);
124 static bool wzero3tp_suspend(device_t dv, const pmf_qual_t *);
125 static bool wzero3tp_resume(device_t dv, const pmf_qual_t *);
126 static void wzero3tp_poll(void *);
127 static int wzero3tp_irq(void *);
128 static int wzero3tp_ioctl(void *, u_long, void *, int, struct lwp *);
129
130 static const struct wsmouse_accessops wzero3tp_accessops = {
131 wzero3tp_enable,
132 wzero3tp_ioctl,
133 wzero3tp_disable
134 };
135
136 struct wzero3tp_model {
137 platid_mask_t *platid;
138 const char *name;
139 int resx, resy;
140 int intr_pin;
141 const struct wsmouse_calibcoords *calib;
142 } wzero3tp_table[] = {
143 #if 0
144 /* WS003SH */
145 {
146 &platid_mask_MACH_SHARP_WZERO3_WS003SH,
147 "WS003SH",
148 480, 640,
149 -1,
150 NULL,
151 },
152 /* WS004SH */
153 {
154 &platid_mask_MACH_SHARP_WZERO3_WS004SH,
155 "WS004SH",
156 480, 640,
157 -1,
158 NULL,
159 },
160 #endif
161 /* WS007SH */
162 {
163 &platid_mask_MACH_SHARP_WZERO3_WS007SH,
164 "WS007SH",
165 480, 640,
166 GPIO_WS007SH_TOUCH_PANEL,
167 &ws007sh_default_calib,
168 },
169 #if 0
170 /* WS011SH */
171 {
172 &platid_mask_MACH_SHARP_WZERO3_WS011SH,
173 "WS011SH",
174 480, 800,
175 GPIO_WS011SH_TOUCH_PANEL,
176 NULL,
177 },
178 /* WS0020H */
179 {
180 &platid_mask_MACH_SHARP_WZERO3_WS020SH,
181 "WS020SH",
182 480, 800,
183 -1,
184 NULL,
185 },
186 #endif
187 {
188 NULL, NULL, -1, -1, -1, NULL,
189 },
190 };
191
192
193 static const struct wzero3tp_model *
194 wzero3tp_lookup(void)
195 {
196 const struct wzero3tp_model *model;
197
198 for (model = wzero3tp_table; model->platid != NULL; model++) {
199 if (platid_match(&platid, model->platid)) {
200 return model;
201 }
202 }
203 return NULL;
204 }
205
206 static int
207 wzero3tp_match(device_t parent, cfdata_t cf, void *aux)
208 {
209
210 if (strcmp(cf->cf_name, "wzero3tp") != 0)
211 return 0;
212 if (wzero3tp_lookup() == NULL)
213 return 0;
214 return 1;
215 }
216
217 static void
218 wzero3tp_attach(device_t parent, device_t self, void *aux)
219 {
220 struct wzero3tp_softc *sc = device_private(self);
221 struct wsmousedev_attach_args a;
222
223 sc->sc_dev = self;
224
225 sc->sc_model = wzero3tp_lookup();
226 if (sc->sc_model == NULL)
227 return;
228
229 aprint_normal(": touch panel\n");
230 aprint_naive("\n");
231
232 callout_init(&sc->sc_tp_poll, 0);
233 callout_setfunc(&sc->sc_tp_poll, wzero3tp_poll, sc);
234
235 a.accessops = &wzero3tp_accessops;
236 a.accesscookie = sc;
237
238 sc->sc_resx = sc->sc_model->resx;
239 sc->sc_resy = sc->sc_model->resy;
240
241 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
242
243 /* Initialize calibration, set default parameters. */
244 tpcalib_init(&sc->sc_tpcalib);
245 tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS,
246 __UNCONST(sc->sc_model->calib), 0, 0);
247 }
248
249 static int
250 wzero3tp_enable(void *v)
251 {
252 struct wzero3tp_softc *sc = (struct wzero3tp_softc *)v;
253
254 DPRINTF(("%s: wzero3tp_enable()\n", device_xname(sc->sc_dev)));
255
256 if (sc->sc_enabled) {
257 DPRINTF(("%s: already enabled\n", device_xname(sc->sc_dev)));
258 return EBUSY;
259 }
260
261 callout_stop(&sc->sc_tp_poll);
262
263 if (!pmf_device_register(sc->sc_dev, wzero3tp_suspend, wzero3tp_resume))
264 aprint_error_dev(sc->sc_dev,
265 "couldn't establish power handler\n");
266
267 if (sc->sc_model->intr_pin >= 0) {
268 pxa2x0_gpio_set_function(sc->sc_model->intr_pin, GPIO_IN);
269
270 /* XXX */
271 if (sc->sc_gh == NULL) {
272 sc->sc_gh = pxa2x0_gpio_intr_establish(
273 sc->sc_model->intr_pin, IST_EDGE_FALLING, IPL_TTY,
274 wzero3tp_irq, sc);
275 } else {
276 pxa2x0_gpio_intr_unmask(sc->sc_gh);
277 }
278 }
279
280 /* enable interrupts */
281 sc->sc_enabled = 1;
282 sc->sc_buttons = 0;
283
284 return 0;
285 }
286
287 static void
288 wzero3tp_disable(void *v)
289 {
290 struct wzero3tp_softc *sc = (struct wzero3tp_softc *)v;
291
292 DPRINTF(("%s: wzero3tp_disable()\n", device_xname(sc->sc_dev)));
293
294 callout_stop(&sc->sc_tp_poll);
295
296 pmf_device_deregister(sc->sc_dev);
297
298 if (sc->sc_gh)
299 pxa2x0_gpio_intr_mask(sc->sc_gh);
300
301 /* disable interrupts */
302 sc->sc_enabled = 0;
303 }
304
305 static bool
306 wzero3tp_suspend(device_t dv, const pmf_qual_t *qual)
307 {
308 struct wzero3tp_softc *sc = device_private(dv);
309
310 DPRINTF(("%s: wzero3tp_suspend()\n", device_xname(sc->sc_dev)));
311
312 sc->sc_enabled = 0;
313 if (sc->sc_gh)
314 pxa2x0_gpio_intr_mask(sc->sc_gh);
315
316 callout_stop(&sc->sc_tp_poll);
317
318 /* Turn off reference voltage but leave ADC on. */
319 (void)wzero3ssp_ic_send(WZERO3_SSP_IC_ADS7846,
320 (1<<ADSCTRL_PD1_SH) | (1<<ADSCTRL_ADR_SH) | (1<<ADSCTRL_STS_SH));
321
322 if (sc->sc_model->intr_pin >= 0) {
323 pxa2x0_gpio_set_function(sc->sc_model->intr_pin,
324 GPIO_OUT|GPIO_SET);
325 }
326
327 return true;
328 }
329
330 static bool
331 wzero3tp_resume(device_t dv, const pmf_qual_t *qual)
332 {
333 struct wzero3tp_softc *sc = device_private(dv);
334
335 DPRINTF(("%s: wzero3tp_resume()\n", device_xname(sc->sc_dev)));
336
337 if (sc->sc_model->intr_pin >= 0)
338 pxa2x0_gpio_set_function(sc->sc_model->intr_pin, GPIO_IN);
339 if (sc->sc_gh)
340 pxa2x0_gpio_intr_mask(sc->sc_gh);
341
342 /* Enable automatic low power mode. */
343 (void)wzero3ssp_ic_send(WZERO3_SSP_IC_ADS7846,
344 (4<<ADSCTRL_ADR_SH) | (1<<ADSCTRL_STS_SH));
345
346 if (sc->sc_gh)
347 pxa2x0_gpio_intr_unmask(sc->sc_gh);
348 sc->sc_enabled = 1;
349
350 return true;
351 }
352
353 #define HSYNC() \
354 do { \
355 while (pxa2x0_gpio_get_bit(GPIO_WS007SH_HSYNC) == 0) \
356 continue; \
357 while (pxa2x0_gpio_get_bit(GPIO_WS007SH_HSYNC) != 0) \
358 continue; \
359 } while (/*CONSTCOND*/0)
360
361 static uint32_t wzero3tp_sync_ads784x(int, int, uint32_t);
362 static int wzero3tp_readpos(struct wzero3tp_pos *);
363
364 /*
365 * Communicate synchronously with the ADS784x touch screen controller.
366 */
367 static uint32_t
368 wzero3tp_sync_ads784x(int dorecv/* XXX */, int dosend/* XXX */, uint32_t cmd)
369 {
370 uint32_t rv = 0;
371
372 HSYNC();
373
374 if (dorecv)
375 rv = wzero3ssp_ic_stop(WZERO3_SSP_IC_ADS7846);
376
377 if (dosend) {
378 /* send dummy command; discard SSDR */
379 (void)wzero3ssp_ic_send(WZERO3_SSP_IC_ADS7846, cmd);
380
381 /* wait for refresh */
382 HSYNC();
383
384 /* send the actual command; keep ADS784x enabled */
385 wzero3ssp_ic_start(WZERO3_SSP_IC_ADS7846, cmd);
386 }
387
388 return rv;
389 }
390
391 static int
392 wzero3tp_readpos(struct wzero3tp_pos *pos)
393 {
394 int cmd, cmd0;
395 int z0, z1;
396 int down;
397
398 cmd0 = (1<<ADSCTRL_STS_SH) | (1<<ADSCTRL_PD0_SH) | (1<<ADSCTRL_PD1_SH);
399
400 /* check that pen is down */
401 cmd = cmd0 | (3<<ADSCTRL_ADR_SH);
402 z0 = wzero3ssp_ic_send(WZERO3_SSP_IC_ADS7846, cmd);
403 DPRINTF(("ztp_readpos(): first z0 = %d\n", z0));
404
405 down = (z0 >= 10);
406 if (!down)
407 goto out;
408
409 /* Y (discard) */
410 cmd = cmd0 | (1<<ADSCTRL_ADR_SH);
411 (void)wzero3tp_sync_ads784x(0, 1, cmd);
412
413 /* Y */
414 cmd = cmd0 | (1<<ADSCTRL_ADR_SH);
415 (void)wzero3tp_sync_ads784x(1, 1, cmd);
416
417 /* X */
418 cmd = cmd0 | (5<<ADSCTRL_ADR_SH);
419 pos->y = wzero3tp_sync_ads784x(1, 1, cmd);
420 DPRINTF(("wzero3tp_readpos(): y = %d\n", pos->y));
421
422 /* Z0 */
423 cmd = cmd0 | (3<<ADSCTRL_ADR_SH);
424 pos->x = wzero3tp_sync_ads784x(1, 1, cmd);
425 DPRINTF(("wzero3tp_readpos(): x = %d\n", pos->x));
426
427 /* Z1 */
428 cmd = cmd0 | (4<<ADSCTRL_ADR_SH);
429 z0 = wzero3tp_sync_ads784x(1, 1, cmd);
430 z1 = wzero3tp_sync_ads784x(1, 0, cmd);
431 DPRINTF(("wzero3tp_readpos(): z0 = %d, z1 = %d\n", z0, z1));
432
433 /* check that pen is still down */
434 if (z0 == 0 || (pos->x * (z1 - z0) / z0) >= 15000)
435 down = 0;
436 pos->z = down;
437
438 out:
439 /* Enable automatic low power mode. */
440 cmd = (1<<ADSCTRL_ADR_SH) | (1<<ADSCTRL_STS_SH);
441 (void)wzero3ssp_ic_send(WZERO3_SSP_IC_ADS7846, cmd);
442
443 return down;
444 }
445
446 static void
447 wzero3tp_poll(void *v)
448 {
449 int s;
450
451 s = spltty();
452 (void)wzero3tp_irq(v);
453 splx(s);
454 }
455
456 static int
457 wzero3tp_irq(void *v)
458 {
459 struct wzero3tp_softc *sc = (struct wzero3tp_softc *)v;
460 struct wzero3tp_pos tp = { 0, 0, 0 };
461 int pindown;
462 int down;
463 int x, y;
464 int s;
465
466 if (!sc->sc_enabled)
467 return 0;
468
469 s = splhigh();
470
471 if (sc->sc_model->intr_pin >= 0)
472 pindown = pxa2x0_gpio_get_bit(sc->sc_model->intr_pin) ? 0 : 1;
473 else
474 pindown = 0;/*XXX*/
475 DPRINTF(("%s: pindown = %d\n", device_xname(sc->sc_dev), pindown));
476 if (pindown) {
477 pxa2x0_gpio_intr_mask(sc->sc_gh);
478 callout_schedule(&sc->sc_tp_poll, POLL_TIMEOUT_RATE1);
479 }
480
481 down = wzero3tp_readpos(&tp);
482 DPRINTF(("%s: x = %d, y = %d, z = %d, down = %d\n",
483 device_xname(sc->sc_dev), tp.x, tp.y, tp.z, down));
484
485 if (!pindown) {
486 pxa2x0_gpio_intr_unmask(sc->sc_gh);
487 callout_schedule(&sc->sc_tp_poll, POLL_TIMEOUT_RATE0);
488 }
489 if (sc->sc_model->intr_pin >= 0)
490 pxa2x0_gpio_clear_intr(sc->sc_model->intr_pin);
491
492 splx(s);
493
494 if (down) {
495 if (!wzero3tp_rawmode) {
496 tpcalib_trans(&sc->sc_tpcalib, tp.x, tp.y, &x, &y);
497 DPRINTF(("%s: x = %d, y = %d\n",
498 device_xname(sc->sc_dev), x, y));
499 tp.x = x;
500 tp.y = y;
501 }
502 }
503
504 if (!down) {
505 /* x/y values are not reliable when pen is up */
506 tp = sc->sc_oldpos;
507 }
508
509 if (down || sc->sc_buttons != down) {
510 wsmouse_input(sc->sc_wsmousedev, down, tp.x, tp.y, 0, 0,
511 WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
512 sc->sc_buttons = down;
513 sc->sc_oldpos = tp;
514 }
515
516 return 1;
517 }
518
519 static int
520 wzero3tp_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
521 {
522 struct wzero3tp_softc *sc = (struct wzero3tp_softc *)v;
523 struct wsmouse_id *id;
524
525 switch (cmd) {
526 case WSMOUSEIO_GTYPE:
527 *(u_int *)data = WSMOUSE_TYPE_TPANEL;
528 return 0;
529
530 case WSMOUSEIO_GETID:
531 /*
532 * return unique ID string,
533 * "<vendor> <model> <serial number>"
534 */
535 id = (struct wsmouse_id *)data;
536 if (id->type != WSMOUSE_ID_TYPE_UIDSTR)
537 return EINVAL;
538 snprintf(id->data, WSMOUSE_ID_MAXLEN, "Sharp %s SN000000",
539 sc->sc_model->name);
540 id->length = strlen(id->data);
541 return 0;
542
543 case WSMOUSEIO_SCALIBCOORDS:
544 case WSMOUSEIO_GCALIBCOORDS:
545 return tpcalib_ioctl(&sc->sc_tpcalib, cmd, data, flag, l);
546 }
547
548 return EPASSTHROUGH;
549 }
550