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