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