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