Home | History | Annotate | Line # | Download | only in dev
psh3tp.c revision 1.12
      1 /*	$NetBSD: psh3tp.c,v 1.12 2008/03/31 15:49:29 kiyohara Exp $	*/
      2 /*
      3  * Copyright (c) 2005 KIYOHARA Takashi
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     18  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
     19  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     23  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
     24  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     25  * POSSIBILITY OF SUCH DAMAGE.
     26  *
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 
     31 #include <sys/types.h>
     32 #include <sys/param.h>
     33 #include <sys/device.h>
     34 #include <sys/errno.h>
     35 #include <sys/kernel.h>
     36 #include <sys/malloc.h>
     37 #include <sys/systm.h>
     38 #include <sys/callout.h>
     39 
     40 #include "opt_psh3tp.h"
     41 
     42 #include <dev/wscons/wsconsio.h>
     43 #include <dev/wscons/wsmousevar.h>
     44 #include <dev/hpc/hpctpanelvar.h>
     45 
     46 #include <machine/platid.h>
     47 #include <machine/platid_mask.h>
     48 
     49 #include <machine/intr.h>
     50 
     51 #include <sh3/exception.h>
     52 #include <sh3/intcreg.h>
     53 #include <sh3/pfcreg.h>
     54 #include <sh3/adcreg.h>
     55 
     56 #include <sh3/dev/adcvar.h>
     57 
     58 
     59 #ifdef PSH3TP_DEBUG
     60 volatile int psh3tp_debug = 4;
     61 #define DPRINTF_PRINTF		printf_nolog
     62 #define DPRINTF(arg)		if (psh3tp_debug) DPRINTF_PRINTF arg
     63 #define DPRINTFN(n, arg)	if (psh3tp_debug > (n)) DPRINTF_PRINTF arg
     64 #else
     65 #define DPRINTF(arg)		((void)0)
     66 #define DPRINTFN(n, arg)	((void)0)
     67 #endif
     68 
     69 
     70 /*
     71  * PFC bits pertinent to PERSONA HPW-50PA touch-panel
     72  */
     73 #define PHDR_TP_PEN_UP		0x40
     74 #define SCPDR_TP_SCAN_ENABLE	0x20
     75 #define SCPDR_TP_SCAN_DISABLE	0x01
     76 #define SCPDR_TP_SCAN_X		0x06
     77 #define SCPDR_TP_SCAN_Y		0x09
     78 
     79 /*
     80  * A/D converter channels to get x/y from
     81  */
     82 #define ADC_CHANNEL_TP_X	1
     83 #define ADC_CHANNEL_TP_Y	0
     84 
     85 /*
     86  * Default (read: my device) raw X/Y values for framebuffer edges.
     87  */
     88 #define PSH3TP_FB_RIGHT		 56
     89 #define PSH3TP_FB_LEFT		969
     90 #define PSH3TP_FB_TOP		848
     91 #define PSH3TP_FB_BOTTOM	121
     92 
     93 
     94 struct psh3tp_softc {
     95 	device_t sc_dev;
     96 
     97 #define PSH3TP_WSMOUSE_ENABLED	0x01
     98 	int sc_enabled;
     99 	struct callout sc_touch_ch;
    100 	device_t sc_wsmousedev;
    101 	struct tpcalib_softc sc_tpcalib; /* calibration info for wsmouse */
    102 };
    103 
    104 
    105 /* config machinery */
    106 static int psh3tp_match(device_t, struct cfdata *, void *);
    107 static void psh3tp_attach(device_t, device_t, void *);
    108 
    109 /* wsmouse accessops */
    110 static int psh3tp_wsmouse_enable(void *);
    111 static int psh3tp_wsmouse_ioctl(void *, u_long, void *, int, struct lwp *);
    112 static void psh3tp_wsmouse_disable(void *);
    113 
    114 /* internal driver routines */
    115 static void psh3tp_enable(struct psh3tp_softc *);
    116 static void psh3tp_disable(struct psh3tp_softc *);
    117 static int psh3tp_set_enable(struct psh3tp_softc *, int, int);
    118 static int psh3tp_intr(void *);
    119 static void psh3tp_start_polling(void *);
    120 static void psh3tp_stop_polling(struct psh3tp_softc *);
    121 static void psh3tp_callout_wsmouse(void *);
    122 static void psh3tp_wsmouse_input(struct psh3tp_softc *, int, int);
    123 static void psh3tp_get_raw_xy(int *, int *);
    124 
    125 
    126 const struct wsmouse_accessops psh3tp_accessops = {
    127 	psh3tp_wsmouse_enable,
    128 	psh3tp_wsmouse_ioctl,
    129 	psh3tp_wsmouse_disable
    130 };
    131 
    132 static const struct wsmouse_calibcoords psh3tp_default_calib = {
    133 	0, 0, 639, 239,
    134 	4,
    135 	{{ PSH3TP_FB_LEFT,  PSH3TP_FB_TOP,      0,   0 },
    136 	 { PSH3TP_FB_RIGHT, PSH3TP_FB_TOP,    639,   0 },
    137 	 { PSH3TP_FB_LEFT,  PSH3TP_FB_BOTTOM,   0, 239 },
    138 	 { PSH3TP_FB_RIGHT, PSH3TP_FB_BOTTOM, 639, 239 }}
    139 };
    140 
    141 
    142 CFATTACH_DECL_NEW(psh3tp, sizeof(struct psh3tp_softc),
    143     psh3tp_match, psh3tp_attach, NULL, NULL);
    144 
    145 
    146 /* ARGSUSED */
    147 static int
    148 psh3tp_match(device_t parent __unused, struct cfdata *cf, void *aux __unused)
    149 {
    150 
    151 	if (!platid_match(&platid, &platid_mask_MACH_HITACHI_PERSONA))
    152 		return 0;
    153 
    154 	if (strcmp(cf->cf_name, "psh3tp") != 0)
    155 		return 0;
    156 
    157 	return 1;
    158 }
    159 
    160 
    161 /*
    162  * Attach the touch panel driver and its wsmouse child.
    163  *
    164  * Note that we have to use submatch to distinguish between child because
    165  * wsmouse_match matches unconditionally.
    166  */
    167 /* ARGSUSED */
    168 static void
    169 psh3tp_attach(device_t parent __unused, device_t self, void *aux __unused)
    170 {
    171 	struct psh3tp_softc *sc = device_private(self);
    172 	struct wsmousedev_attach_args wsma;
    173 
    174 	aprint_naive("\n");
    175 	aprint_normal("\n");
    176 
    177 	sc->sc_dev = self;
    178 	sc->sc_enabled = 0;
    179 
    180 	/* touch-panel as a pointing device */
    181 	wsma.accessops = &psh3tp_accessops;
    182 	wsma.accesscookie = sc;
    183 
    184 	sc->sc_wsmousedev = config_found_ia(
    185 	    self, "wsmousedev", &wsma, wsmousedevprint);
    186 	if (sc->sc_wsmousedev == NULL)
    187 		return;
    188 
    189 	/* init calibration, set default parameters */
    190 	tpcalib_init(&sc->sc_tpcalib);
    191 	tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS,
    192 	    (void *)__UNCONST(&psh3tp_default_calib), 0, 0);
    193 
    194 	/* used when in polling mode */
    195 	callout_init(&sc->sc_touch_ch, 0);
    196 
    197 	/* establish interrupt handler, but disable until opened */
    198 	intc_intr_establish(SH7709_INTEVT2_IRQ2,
    199 	    IST_EDGE, IPL_TTY, psh3tp_intr, sc);
    200 	intc_intr_disable(SH7709_INTEVT2_IRQ2);
    201 }
    202 
    203 
    204 /*
    205  * Enable touch panel:  we start in interrupt mode.
    206  * Must be called at spltty().
    207  */
    208 /* ARGSUSED */
    209 static void
    210 psh3tp_enable(struct psh3tp_softc *sc __unused)
    211 {
    212 
    213 	DPRINTFN(2, ("%s: enable\n", sc->sc_dev.dv_xname));
    214 	intc_intr_enable(SH7709_INTEVT2_IRQ2);
    215 }
    216 
    217 
    218 /*
    219  * Disable touch panel: disable interrupt, cancel pending callout.
    220  * Must be called at spltty().
    221  */
    222 static void
    223 psh3tp_disable(struct psh3tp_softc *sc)
    224 {
    225 
    226 	DPRINTFN(2, ("%s: disable\n", sc->sc_dev.dv_xname));
    227 	intc_intr_disable(SH7709_INTEVT2_IRQ2);
    228 	callout_stop(&sc->sc_touch_ch);
    229 }
    230 
    231 
    232 static int
    233 psh3tp_set_enable(struct psh3tp_softc *sc, int on, int child)
    234 {
    235 	int s = spltty();
    236 
    237 	if (on) {
    238 		if (!sc->sc_enabled)
    239 			psh3tp_enable(sc);
    240 		sc->sc_enabled |= child;
    241 	} else {
    242 		sc->sc_enabled &= ~child;
    243 		if (!sc->sc_enabled)
    244 			psh3tp_disable(sc);
    245 	}
    246 
    247 	splx(s);
    248 	return 0;
    249 }
    250 
    251 
    252 static int
    253 psh3tp_wsmouse_enable(void *self)
    254 {
    255 	struct psh3tp_softc *sc = (struct psh3tp_softc *)self;
    256 
    257 	DPRINTFN(1, ("%s: wsmouse enable\n", sc->sc_dev.dv_xname));
    258 	return psh3tp_set_enable(sc, 1, PSH3TP_WSMOUSE_ENABLED);
    259 }
    260 
    261 
    262 static void
    263 psh3tp_wsmouse_disable(void *self)
    264 {
    265 	struct psh3tp_softc *sc = (struct psh3tp_softc *)self;
    266 
    267 	DPRINTFN(1, ("%s: wsmouse disable\n", sc->sc_dev.dv_xname));
    268 	psh3tp_set_enable(sc, 0, PSH3TP_WSMOUSE_ENABLED);
    269 }
    270 
    271 
    272 static int
    273 psh3tp_intr(void *self)
    274 {
    275 	struct psh3tp_softc *sc = (struct psh3tp_softc *)self;
    276 
    277 	uint8_t irr0;
    278 	uint8_t phdr, touched;
    279 	unsigned int steady, tremor_timeout;
    280 
    281 	irr0 = _reg_read_1(SH7709_IRR0);
    282 	if ((irr0 & IRR0_IRQ2) == 0) {
    283 #ifdef DIAGNOSTIC
    284 		printf("%s: irr0 %02x?\n", sc->sc_dev.dv_xname, irr0);
    285 #endif
    286 		return 0;
    287 	}
    288 
    289 	if (!sc->sc_enabled) {
    290 		DPRINTFN(1, ("%s: intr: !sc_enabled\n", sc->sc_dev.dv_xname));
    291 		intc_intr_disable(SH7709_INTEVT2_IRQ2);
    292 		goto served;
    293 	}
    294 
    295 	/*
    296 	 * Number of times the "touched" bit should be read
    297 	 * consecutively.
    298 	 */
    299 #define TREMOR_THRESHOLD 0x300
    300 	steady = 0;
    301 	tremor_timeout = TREMOR_THRESHOLD * 16;	/* XXX: arbitrary */
    302 	touched = true;		/* we start with "touched" state */
    303 
    304 	do {
    305 		uint8_t state;
    306 
    307 		phdr = _reg_read_1(SH7709_PHDR);
    308 		state = ((phdr & PHDR_TP_PEN_UP) != PHDR_TP_PEN_UP);
    309 
    310 		if (state == touched)
    311 			++steady;
    312 		else {
    313 			steady = 0;
    314 			touched = state;
    315 		}
    316 
    317 		if (--tremor_timeout == 0) {
    318 			DPRINTF(("%s: tremor timeout!\n", sc->sc_dev.dv_xname));
    319 			goto served;
    320 		}
    321 	} while (steady < TREMOR_THRESHOLD);
    322 
    323 	if (touched) {
    324 		intc_intr_disable(SH7709_INTEVT2_IRQ2);
    325 
    326 		/*
    327 		 * ADC readings are not stable yet, so schedule
    328 		 * callout instead of accessing ADC from the interrupt
    329 		 * handler only to immediately delay().
    330 		 */
    331 		callout_reset(&sc->sc_touch_ch,
    332 		    hz/32, psh3tp_start_polling, sc);
    333 	} else
    334 		DPRINTFN(1, ("%s: tremor\n", sc->sc_dev.dv_xname));
    335 served:
    336 	/* clear the interrupt */
    337 	_reg_write_1(SH7709_IRR0, irr0 & ~IRR0_IRQ2);
    338 
    339 	return 1;
    340 }
    341 
    342 
    343 /*
    344  * Called from the interrupt handler at spltty() upon first touch.
    345  * Decide if we are going to report this touch as a mouse click/drag.
    346  */
    347 static void
    348 psh3tp_start_polling(void *self)
    349 {
    350 	struct psh3tp_softc *sc = (struct psh3tp_softc *)self;
    351 	uint8_t phdr;
    352 	int rawx, rawy;
    353 
    354 	phdr = _reg_read_1(SH7709_PHDR);
    355 	if ((phdr & PHDR_TP_PEN_UP) == PHDR_TP_PEN_UP) {
    356 		DPRINTFN(2, ("%s: start: pen is not down\n",
    357 		    sc->sc_dev.dv_xname));
    358 		psh3tp_stop_polling(sc);
    359 		return;
    360 	}
    361 
    362 	psh3tp_get_raw_xy(&rawx, &rawy);
    363 	DPRINTFN(2, ("%s: start: %4d %4d -> ",
    364 	    sc->sc_dev.dv_xname, rawx, rawy));
    365 
    366 	if (sc->sc_enabled & PSH3TP_WSMOUSE_ENABLED) {
    367 		DPRINTFN(2, ("mouse\n"));
    368 		psh3tp_wsmouse_input(sc, rawx, rawy);
    369 		callout_reset(&sc->sc_touch_ch,
    370 		    hz/32, psh3tp_callout_wsmouse, sc);
    371 	} else {
    372 		DPRINTFN(2, ("ignore\n"));
    373 		psh3tp_stop_polling(sc);
    374 	}
    375 }
    376 
    377 
    378 /*
    379  * Re-enable touch panel interrupt.
    380  * Called at spltty() when polling code detects pen-up.
    381  */
    382 /* ARGSUSED */
    383 static void
    384 psh3tp_stop_polling(struct psh3tp_softc *sc __unused)
    385 {
    386 	uint8_t irr0;
    387 
    388 	DPRINTFN(2, ("%s: stop\n", sc->sc_dev.dv_xname));
    389 
    390 	/* clear pending interrupt signal before re-enabling the interrupt */
    391 	irr0 = _reg_read_1(SH7709_IRR0);
    392 	if ((irr0 & IRR0_IRQ2) != 0)
    393 		_reg_write_1(SH7709_IRR0, irr0 & ~IRR0_IRQ2);
    394 
    395 	intc_intr_enable(SH7709_INTEVT2_IRQ2);
    396 }
    397 
    398 
    399 /*
    400  * We are reporting this touch as a mouse click/drag.
    401  */
    402 static void
    403 psh3tp_callout_wsmouse(void *self)
    404 {
    405 	struct psh3tp_softc *sc = (struct psh3tp_softc *)self;
    406 	uint8_t phdr;
    407 	int rawx, rawy;
    408 	int s;
    409 
    410 	s = spltty();
    411 
    412 	if (!sc->sc_enabled) {
    413 		DPRINTFN(1, ("%s: wsmouse callout: !sc_enabled\n",
    414 		    sc->sc_dev.dv_xname));
    415 		splx(s);
    416 		return;
    417 	}
    418 
    419 	phdr = _reg_read_1(SH7709_PHDR);
    420 	if ((phdr & PHDR_TP_PEN_UP) != PHDR_TP_PEN_UP) {
    421 		psh3tp_get_raw_xy(&rawx, &rawy);
    422 		psh3tp_wsmouse_input(sc, rawx, rawy); /* mouse dragged */
    423 		callout_schedule(&sc->sc_touch_ch, hz/32);
    424 	} else {
    425 		wsmouse_input( /* button up */
    426 		    sc->sc_wsmousedev, 0, 0, 0, 0, 0, WSMOUSE_INPUT_DELTA);
    427 		psh3tp_stop_polling(sc);
    428 	}
    429 	splx(s);
    430 }
    431 
    432 
    433 /*
    434  * Report mouse click/drag.
    435  */
    436 static void
    437 psh3tp_wsmouse_input(struct psh3tp_softc *sc, int rawx, int rawy)
    438 {
    439 	int x, y;
    440 
    441 	tpcalib_trans(&sc->sc_tpcalib, rawx, rawy, &x, &y);
    442 
    443 	DPRINTFN(3, ("%s: %4d %4d -> %3d %3d\n",
    444 	     sc->sc_dev.dv_xname, rawx, rawy, x, y));
    445 
    446 	wsmouse_input(sc->sc_wsmousedev,
    447 	    1,	/* button */
    448 	    x, y, 0, 0,
    449 	    WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
    450 }
    451 
    452 
    453 /*
    454  * Read raw X/Y coordinates from the ADC.
    455  */
    456 static void
    457 psh3tp_get_raw_xy(int *rawxp, int *rawyp)
    458 {
    459 	uint8_t scpdr;
    460 
    461 	/* X axis */
    462 	scpdr = _reg_read_1(SH7709_SCPDR);
    463 	scpdr &= ~SCPDR_TP_SCAN_DISABLE;
    464 	scpdr |= (SCPDR_TP_SCAN_ENABLE | SCPDR_TP_SCAN_X);
    465 	_reg_write_1(SH7709_SCPDR, scpdr);
    466 	delay(40);
    467 
    468 	*rawxp = adc_sample_channel(ADC_CHANNEL_TP_X);
    469 
    470 	/* Y axis */
    471 	scpdr = _reg_read_1(SH7709_SCPDR);
    472 	scpdr &=  ~SCPDR_TP_SCAN_X;
    473 	scpdr |= (SCPDR_TP_SCAN_ENABLE | SCPDR_TP_SCAN_Y);
    474 	_reg_write_1(SH7709_SCPDR, scpdr);
    475 	delay(40);
    476 
    477 	*rawyp = adc_sample_channel(ADC_CHANNEL_TP_Y);
    478 
    479 	/* restore SCPDR */
    480 	scpdr = _reg_read_1(SH7709_SCPDR);
    481 	scpdr &= ~(SCPDR_TP_SCAN_ENABLE | SCPDR_TP_SCAN_Y);
    482 	scpdr |= SCPDR_TP_SCAN_DISABLE;
    483 	_reg_write_1(SH7709_SCPDR, scpdr);
    484 }
    485 
    486 
    487 static int
    488 psh3tp_wsmouse_ioctl(void *self, u_long cmd, void *data, int flag,
    489 		     struct lwp *l)
    490 {
    491 	struct psh3tp_softc *sc = (struct psh3tp_softc *)self;
    492 
    493 	return hpc_tpanel_ioctl(&sc->sc_tpcalib, cmd, data, flag, l);
    494 }
    495