Home | History | Annotate | Line # | Download | only in dev
j6x0tp.c revision 1.2
      1 /*	$NetBSD: j6x0tp.c,v 1.2 2003/10/22 23:52:46 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.2 2003/10/22 23:52:46 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 #include <sh3/dev/adcvar.h>
     58 
     59 
     60 #define J6X0TP_DEBUG
     61 #if 0 /* XXX: disabled in favor of local version that uses printf_nolog */
     62 #define DPRINTF_ENABLE
     63 #define DPRINTF_DEBUG	j6x0tp_debug
     64 #define DPRINTF_LEVEL	0
     65 #include <machine/debug.h>
     66 #else
     67 #ifdef J6X0TP_DEBUG
     68 volatile int j6x0tp_debug = 0;
     69 #define	DPRINTF(arg)		if (j6x0tp_debug) printf_nolog arg
     70 #define DPRINTFN(n, arg)	if (j6x0tp_debug > (n)) printf_nolog arg
     71 #else
     72 #define	DPRINTF(arg)		((void)0)
     73 #define DPRINTFN(n, arg)	((void)0)
     74 #endif
     75 #endif
     76 
     77 
     78 /* PFC bits pertinent to Jornada 6x0 touchpanel */
     79 #define PHDR_TP_PEN_DOWN	0x08
     80 
     81 #define SCPDR_TP_SCAN_ENABLE	0x20
     82 #define SCPDR_TP_SCAN_Y		0x02
     83 #define SCPDR_TP_SCAN_X		0x01
     84 
     85 /* A/D covnerter channels to get x/y from */
     86 #define ADC_CHANNEL_TP_Y	1
     87 #define ADC_CHANNEL_TP_X	2
     88 
     89 
     90 struct j6x0tp_softc {
     91 	struct device sc_dev;
     92 
     93 	struct callout sc_touch_ch;
     94 	int sc_intrlevel;
     95 	int sc_enabled;
     96 
     97 	struct device *sc_wsmousedev;
     98 	struct tpcalib_softc sc_tpcalib;
     99 };
    100 
    101 static int	j6x0tp_match(struct device *, struct cfdata *, void *);
    102 static void	j6x0tp_attach(struct device *, struct device *, void *);
    103 
    104 CFATTACH_DECL(j6x0tp, sizeof(struct j6x0tp_softc),
    105     j6x0tp_match, j6x0tp_attach, NULL, NULL);
    106 
    107 
    108 static int  j6x0tp_enable(void *);
    109 static int  j6x0tp_ioctl(void *, u_long, caddr_t, int, struct proc *);
    110 static void j6x0tp_disable(void *);
    111 
    112 const struct wsmouse_accessops j6x0tp_accessops = {
    113 	j6x0tp_enable,
    114 	j6x0tp_ioctl,
    115 	j6x0tp_disable,
    116 };
    117 
    118 static const struct wsmouse_calibcoords j6x0tp_default_calib = {
    119 	0, 0, 639, 239,
    120 	4,
    121 	{{  38,  80,   0,   0 }, /* upper left  */
    122 	 { 950,  80, 639,   0 }, /* upper right */
    123 	 {  38, 900,   0, 239 }, /* lower left  */
    124 	 { 950, 900, 639, 239 }} /* lower right */
    125 };
    126 
    127 
    128 static int	j6x0tp_intr(void *);
    129 static void	j6x0tp_poll_callout(void *self);
    130 
    131 
    132 static int
    133 j6x0tp_match(struct device *parent, struct cfdata *cfp, void *aux)
    134 {
    135 
    136 	/*
    137 	 * XXX: does platid_mask_MACH_HP_LX matches _JORNADA_6XX too?
    138 	 * Is 620 wired similarly?
    139 	 */
    140 	if (!platid_match(&platid, &platid_mask_MACH_HP_JORNADA_6XX))
    141 		return (0);
    142 
    143 	if (strcmp(cfp->cf_name, "j6x0tp") != 0)
    144 		return (0);
    145 
    146 	return (1);
    147 }
    148 
    149 
    150 static void
    151 j6x0tp_attach(struct device *parent, struct device *self, void *aux)
    152 {
    153 	struct j6x0tp_softc *sc = (struct j6x0tp_softc *)self;
    154 	struct wsmousedev_attach_args wsma;
    155 
    156 	printf("\n");
    157 
    158 	sc->sc_enabled = 0;
    159 
    160 	wsma.accessops = &j6x0tp_accessops;
    161 	wsma.accesscookie = sc;
    162 
    163 	sc->sc_wsmousedev = config_found(self, &wsma, wsmousedevprint);
    164 	if (sc->sc_wsmousedev == NULL)
    165 		return;
    166 
    167 	tpcalib_init(&sc->sc_tpcalib);
    168 	tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS,
    169 		      (caddr_t)&j6x0tp_default_calib, 0, 0);
    170 
    171 	callout_init(&sc->sc_touch_ch);
    172 
    173 	intc_intr_establish(SH7709_INTEVT2_IRQ3, IST_EDGE, IPL_TTY,
    174 			    j6x0tp_intr, sc);
    175 	sc->sc_intrlevel = intc_intr_disable(SH7709_INTEVT2_IRQ3);
    176 }
    177 
    178 
    179 static int
    180 j6x0tp_enable(void *self)
    181 {
    182 	struct j6x0tp_softc *sc = (struct j6x0tp_softc *)self;
    183 	int s;
    184 
    185 	DPRINTFN(1, ("%s: enable\n", sc->sc_dev.dv_xname));
    186 
    187 	s = spltty();
    188 
    189 	sc->sc_enabled = 1;
    190 	intc_intr_enable(SH7709_INTEVT2_IRQ3, sc->sc_intrlevel);
    191 
    192 	splx(s);
    193 	return (0);
    194 }
    195 
    196 
    197 static void
    198 j6x0tp_disable(void *self)
    199 {
    200 	struct j6x0tp_softc *sc = (struct j6x0tp_softc *)self;
    201 	int s;
    202 
    203 	s = spltty();
    204 
    205 	DPRINTFN(1, ("%s: disable\n", sc->sc_dev.dv_xname));
    206 
    207 	sc->sc_enabled = 0;
    208 	intc_intr_disable(SH7709_INTEVT2_IRQ3);
    209 	callout_stop(&sc->sc_touch_ch);
    210 
    211 	splx(s);
    212 }
    213 
    214 
    215 static int
    216 j6x0tp_intr(void *self)
    217 {
    218 	struct j6x0tp_softc *sc = (struct j6x0tp_softc *)self;
    219 
    220 	uint8_t irr0;
    221 	uint8_t phdr, touched;
    222 	unsigned int steady, tremor_timeout;
    223 
    224 	irr0 = _reg_read_1(SH7709_IRR0);
    225 	if ((irr0 & IRR0_IRQ3) == 0) {
    226 #ifdef DIAGNOSTIC
    227 		printf("%s: irr0 %02x?\n", sc->sc_dev.dv_xname, irr0);
    228 #endif
    229 		return (0);
    230 	}
    231 
    232 	/*
    233 	 * Number of times the "touched" bit should be read
    234 	 * consecutively.
    235 	 */
    236 #	define TREMOR_THRESHOLD 0x300
    237 
    238 	steady = 0;
    239 	tremor_timeout = TREMOR_THRESHOLD * 16;	/* XXX: arbitrary */
    240 	touched = PHDR_TP_PEN_DOWN;	/* we start with "touched" state */
    241 
    242 	do {
    243 		phdr = _reg_read_1(SH7709_PHDR);
    244 
    245 		if ((phdr & PHDR_TP_PEN_DOWN) == touched)
    246 			++steady;
    247 		else {
    248 			steady = 0;
    249 			touched = phdr & PHDR_TP_PEN_DOWN;
    250 		}
    251 
    252 		if (--tremor_timeout == 0) {
    253 			DPRINTF(("%s: tremor timeout!\n",
    254 				 sc->sc_dev.dv_xname));
    255 			goto served;
    256 		}
    257 	} while (steady < TREMOR_THRESHOLD);
    258 
    259 	if (touched == 0) {
    260 		DPRINTFN(1, ("%s: tremor\n", sc->sc_dev.dv_xname));
    261 		goto served;
    262 	}
    263 
    264 	intc_intr_disable(SH7709_INTEVT2_IRQ3);
    265 
    266 	if (sc->sc_enabled)
    267 		callout_reset(&sc->sc_touch_ch, hz/32,
    268 			      j6x0tp_poll_callout, sc);
    269 	else
    270 		DPRINTFN(1, ("%s: intr: !sc_enabled\n", sc->sc_dev.dv_xname));
    271 
    272   served:
    273 	/* clear the interrupt */
    274 	_reg_write_1(SH7709_IRR0, irr0 & ~IRR0_IRQ3);
    275 
    276 	return (1);
    277 }
    278 
    279 
    280 static void
    281 j6x0tp_poll_callout(void *self)
    282 {
    283 	struct j6x0tp_softc *sc = (struct j6x0tp_softc *)self;
    284 	uint8_t phdr, scpdr;
    285 	uint8_t irr0;
    286 	int rawx, rawy, x, y;
    287 	int s;
    288 
    289 	s = spltty();
    290 
    291 	if (!sc->sc_enabled) {
    292 		DPRINTFN(1, ("%s: callout: !sc_enabled\n",
    293 			     sc->sc_dev.dv_xname));
    294 		splx(s);
    295 		return;
    296 	}
    297 
    298 	phdr = _reg_read_1(SH7709_PHDR);
    299 	if ((phdr & PHDR_TP_PEN_DOWN) == 0) {
    300 		wsmouse_input(sc->sc_wsmousedev, 0, 0, 0, 0, 0); /* mouse up */
    301 
    302 		intc_intr_enable(SH7709_INTEVT2_IRQ3, sc->sc_intrlevel);
    303 
    304 		irr0 = _reg_read_1(SH7709_IRR0);
    305 		if ((irr0 & IRR0_IRQ3) != 0)
    306 			_reg_write_1(SH7709_IRR0, irr0 & ~IRR0_IRQ3);
    307 
    308 		splx(s);
    309 		return;
    310 	}
    311 
    312 	/* XXX: protect accesses to SCPDR? */
    313 
    314 	/* Y axis */
    315 	scpdr = _reg_read_1(SH7709_SCPDR);
    316 	scpdr |=  SCPDR_TP_SCAN_ENABLE;
    317 	scpdr &= ~SCPDR_TP_SCAN_Y; /* pull low to scan */
    318 	_reg_write_1(SH7709_SCPDR, scpdr);
    319 	delay(10);
    320 	rawy = adc_sample_channel(ADC_CHANNEL_TP_Y);
    321 
    322 	/* X axis */
    323 	scpdr = _reg_read_1(SH7709_SCPDR);
    324 	scpdr |=  SCPDR_TP_SCAN_Y;
    325 	scpdr &= ~SCPDR_TP_SCAN_X; /* pull low to scan */
    326 	_reg_write_1(SH7709_SCPDR, scpdr);
    327 	delay(10);
    328 	rawx = adc_sample_channel(ADC_CHANNEL_TP_X);
    329 
    330 	/* restore SCPDR */
    331 	scpdr = _reg_read_1(SH7709_SCPDR);
    332 	scpdr |=  SCPDR_TP_SCAN_X;
    333 	scpdr &= ~SCPDR_TP_SCAN_ENABLE;
    334 	_reg_write_1(SH7709_SCPDR, scpdr);
    335 
    336 	tpcalib_trans(&sc->sc_tpcalib, rawx, rawy, &x, &y);
    337 
    338 	DPRINTFN(2, ("%s: %4d %4d -> %3d %3d\n",
    339 		     sc->sc_dev.dv_xname, rawx, rawy, x, y));
    340 
    341 	wsmouse_input(sc->sc_wsmousedev, 1, x, y, 0,
    342 		      WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
    343 
    344 	callout_schedule(&sc->sc_touch_ch, hz/32);
    345 	splx(s);
    346 }
    347 
    348 
    349 static int
    350 j6x0tp_ioctl(void *self, u_long cmd, caddr_t data, int flag,
    351 		     struct proc *p)
    352 {
    353 	struct j6x0tp_softc *sc = (struct j6x0tp_softc *)self;
    354 
    355 	switch (cmd) {
    356 	case WSMOUSEIO_GTYPE:
    357 		*(u_int *)data = WSMOUSE_TYPE_TPANEL;
    358 		return (0);
    359 
    360 	case WSMOUSEIO_SCALIBCOORDS:
    361 	case WSMOUSEIO_GCALIBCOORDS:
    362 	case WSMOUSEIO_GETID:
    363 		return (tpcalib_ioctl(&sc->sc_tpcalib, cmd, data, flag, p));
    364 
    365 	default:
    366 		return (EPASSTHROUGH);
    367 	}
    368 }
    369