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