1/* $NetBSD: zrc.c,v 1.7 2011/06/19 16:20:09 nonaka Exp $ */ 2/* $OpenBSD: zaurus_remote.c,v 1.1 2005/11/17 05:26:31 uwe Exp $ */ 3 4/* 5 * Copyright (c) 2005 Uwe Stuehler <uwe@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20#include <sys/cdefs.h> 21__KERNEL_RCSID(0, "$NetBSD: zrc.c,v 1.7 2011/06/19 16:20:09 nonaka Exp $"); 22 23#include <sys/param.h> 24#include <sys/device.h> 25#include <sys/kernel.h> 26#include <sys/callout.h> 27#include <sys/systm.h> 28 29#include <dev/wscons/wsconsio.h> 30#include <dev/wscons/wskbdvar.h> 31#include <dev/wscons/wsksymdef.h> 32#include <dev/wscons/wsksymvar.h> 33 34#include <arm/xscale/pxa2x0reg.h> 35#include <arm/xscale/pxa2x0_gpio.h> 36 37#include <machine/intr.h> 38 39#include <zaurus/zaurus/zaurus_reg.h> 40#include <zaurus/zaurus/zaurus_var.h> 41#include <zaurus/dev/zsspvar.h> 42#include <zaurus/dev/scoopvar.h> 43#include <zaurus/dev/ioexpvar.h> 44 45#define RESCAN_INTERVAL (hz/100) 46 47#define KEY_RELEASE 0 /* button release */ 48#define KEY_VOL_DOWN 1 49#define KEY_MUTE 2 50#define KEY_REWIND 3 51#define KEY_VOL_UP 4 52#define KEY_FORWARD 5 53#define KEY_PLAY 6 54#define KEY_STOP 7 55#define KEY_EARPHONE 8 56 57#ifdef DEBUG 58static const char *zrc_keyname[] = { 59 "(release)", "volume down", "mute", "rewind", "volume up", 60 "forward", "play", "stop", "(earphone)" 61}; 62#endif 63 64struct zrc_akey { 65 int min; /* minimum ADC value or INT_MIN */ 66 int key; /* remote control key number */ 67}; 68 69/* Values match the resistors in the CE-RH2 remote control. */ 70static const struct zrc_akey zrc_akeytab_c3000[] = { 71 { 238, KEY_RELEASE }, 72 { 202, KEY_VOL_DOWN }, 73 { 168, KEY_MUTE }, 74 { 135, KEY_REWIND }, 75 { 105, KEY_VOL_UP }, 76 { 74, KEY_FORWARD }, 77 { 42, KEY_PLAY }, 78 { 12, KEY_STOP }, 79 { INT_MIN, KEY_EARPHONE } 80}; 81 82static const struct zrc_akey *zrc_akeytab = zrc_akeytab_c3000; 83 84struct zrc_softc { 85 device_t sc_dev; 86 struct callout sc_to; 87 void *sc_ih; 88 int sc_key; /* being scanned */ 89 int sc_scans; /* rescan counter */ 90 int sc_noise; /* discard if too noisy? */ 91 int sc_keydown; /* currently pressed key */ 92 struct device *sc_wskbddev; 93#ifdef WSDISPLAY_COMPAT_RAWKBD 94 int sc_rawkbd; 95#endif 96}; 97 98static int zrc_match(struct device *, struct cfdata *, void *); 99static void zrc_attach(struct device *, struct device *, void *); 100 101CFATTACH_DECL_NEW(zrc, sizeof(struct zrc_softc), 102 zrc_match, zrc_attach, NULL, NULL); 103 104static int zrc_finalize(device_t); 105static int zrc_intr(void *); 106static void zrc_timeout(void *); 107static int zrc_scan(void); 108static void zrc_input(struct zrc_softc *, int, int); 109 110static int zrc_enable(void *, int); 111static void zrc_set_leds(void *, int); 112static int zrc_ioctl(void *, u_long, void *, int, struct lwp *); 113 114struct wskbd_accessops zrc_accessops = { 115 zrc_enable, 116 zrc_set_leds, 117 zrc_ioctl, 118}; 119 120#define KC(n) KS_KEYCODE(n) 121 122/* XXX what keys should be generated in translated mode? */ 123static const keysym_t zrc_keydesc[] = { 124 KC(KEY_VOL_DOWN), KS_Cmd_VolumeUp, 125 KC(KEY_MUTE), KS_Cmd_VolumeToggle, 126 KC(KEY_REWIND), KS_b, 127 KC(KEY_VOL_UP), KS_Cmd_VolumeDown, 128 KC(KEY_FORWARD), KS_f, 129 KC(KEY_PLAY), KS_p, 130 KC(KEY_STOP), KS_s, 131}; 132 133#ifdef WSDISPLAY_COMPAT_RAWKBD 134#define RAWKEY_AudioRewind 0xa0 135#define RAWKEY_AudioForward 0xa1 136#define RAWKEY_AudioPlay 0xa2 137#define RAWKEY_AudioStop 0xa3 138static const keysym_t zrc_xt_keymap[] = { 139 /* KC(KEY_RELEASE), */ RAWKEY_Null, 140 /* KC(KEY_VOL_DOWN), */ RAWKEY_AudioLower, 141 /* KC(KEY_MUTE), */ RAWKEY_AudioMute, 142 /* KC(KEY_REWIND), */ RAWKEY_AudioRewind, 143 /* KC(KEY_VOL_UP), */ RAWKEY_AudioRaise, 144 /* KC(KEY_FORWARD), */ RAWKEY_AudioForward, 145 /* KC(KEY_PLAY), */ RAWKEY_AudioPlay, 146 /* KC(KEY_STOP), */ RAWKEY_AudioStop, 147}; 148#endif 149 150static const struct wscons_keydesc zrc_keydesctab[] = { 151 {KB_US, 0, sizeof(zrc_keydesc)/sizeof(keysym_t), zrc_keydesc}, 152 {0, 0, 0, 0} 153}; 154 155struct wskbd_mapdata zrc_keymapdata = { 156 zrc_keydesctab, KB_US 157}; 158 159#undef KC 160 161static int 162zrc_match(device_t parent, cfdata_t cf, void *aux) 163{ 164 165 if (ZAURUS_ISC1000 || ZAURUS_ISC3000) 166 return 1; 167 return 0; 168} 169 170static void 171zrc_attach(device_t parent, device_t self, void *aux) 172{ 173 struct zrc_softc *sc = device_private(self); 174 struct wskbddev_attach_args a; 175 176 sc->sc_dev = self; 177 178 aprint_normal(": CE-RH2 remote control\n"); 179 aprint_naive("\n"); 180 181 /* Configure remote control interrupt handling. */ 182 callout_init(&sc->sc_to, 0); 183 callout_setfunc(&sc->sc_to, zrc_timeout, sc); 184 185 /* Establish interrput */ 186 pxa2x0_gpio_set_function(C3000_RC_IRQ_PIN, GPIO_IN); 187 sc->sc_ih = pxa2x0_gpio_intr_establish(C3000_RC_IRQ_PIN, 188 IST_EDGE_BOTH, IPL_BIO, zrc_intr, sc); 189 if (sc->sc_ih == NULL) { 190 aprint_error_dev(sc->sc_dev, "couldn't establish interrupt.\n"); 191 return; 192 } 193 194 /* defer enabling pullup until ioexp or scoop is attached */ 195 config_finalize_register(self, zrc_finalize); 196 197 sc->sc_keydown = KEY_RELEASE; 198 199 a.console = 0; 200 a.keymap = &zrc_keymapdata; 201 a.accessops = &zrc_accessops; 202 a.accesscookie = sc; 203 204 sc->sc_wskbddev = config_found(self, &a, wskbddevprint); 205} 206 207static int 208zrc_finalize(device_t dv) 209{ 210 211 /* Enable the pullup while waiting for an interrupt. */ 212 if (ZAURUS_ISC1000) 213 ioexp_akin_pullup(1); 214 else 215 scoop_akin_pullup(1); 216 217 return 0; 218} 219 220static int 221zrc_intr(void *v) 222{ 223 struct zrc_softc *sc = v; 224 225 /* just return if remote control isn't present */ 226 227 pxa2x0_gpio_intr_mask(sc->sc_ih); 228 if (ZAURUS_ISC1000) 229 ioexp_akin_pullup(0); 230 else 231 scoop_akin_pullup(0); 232 sc->sc_key = zrc_scan(); 233 sc->sc_scans = 0; 234 sc->sc_noise = 0; 235 callout_schedule(&sc->sc_to, RESCAN_INTERVAL); 236 237 return 1; 238} 239 240static void 241zrc_timeout(void *v) 242{ 243 struct zrc_softc *sc = v; 244 int key; 245 246 key = zrc_scan(); 247 switch (sc->sc_scans) { 248 case 0: 249 case 1: 250 case 2: 251 /* wait for a stable read */ 252 if (sc->sc_key == key) 253 sc->sc_scans++; 254 else { 255 sc->sc_key = key; 256 sc->sc_scans = 0; 257 sc->sc_noise++; 258 } 259 callout_schedule(&sc->sc_to, RESCAN_INTERVAL); 260 break; 261 case 3: 262 /* generate key press event */ 263 if (sc->sc_key != key) { 264 key = sc->sc_key; 265 sc->sc_noise++; 266 } 267 sc->sc_scans++; 268 switch (key) { 269 case KEY_EARPHONE: 270 case KEY_RELEASE: 271 sc->sc_scans = 6; 272 break; 273 default: 274#ifdef DEBUG 275 printf("%s: %s pressed (%d noise)\n", 276 device_xname(sc->sc_dev), 277 zrc_keyname[key], sc->sc_noise); 278#endif 279 sc->sc_keydown = key; 280 sc->sc_noise = 0; 281 zrc_input(sc, key, 1); 282 break; 283 } 284 callout_schedule(&sc->sc_to, RESCAN_INTERVAL); 285 break; 286 case 4: 287 case 5: 288 /* wait for key release, permit noise */ 289 if (sc->sc_key == key) { 290 if (sc->sc_scans == 5) 291 sc->sc_noise++; 292 sc->sc_scans = 4; 293 } else 294 sc->sc_scans++; 295 callout_schedule(&sc->sc_to, RESCAN_INTERVAL); 296 break; 297 case 6: 298 /* generate key release event */ 299 if (sc->sc_keydown != KEY_RELEASE) { 300 zrc_input(sc, sc->sc_keydown, 0); 301#ifdef DEBUG 302 printf("%s: %s released (%d noise)\n", 303 device_xname(sc->sc_dev), 304 zrc_keyname[sc->sc_keydown], sc->sc_noise); 305#endif 306 sc->sc_keydown = KEY_RELEASE; 307 } 308 /* FALLTHROUGH */ 309 default: 310 /* unmask interrupt again */ 311 callout_stop(&sc->sc_to); 312 sc->sc_scans = 7; 313 if (ZAURUS_ISC1000) 314 ioexp_akin_pullup(1); 315 else 316 scoop_akin_pullup(1); 317 pxa2x0_gpio_intr_unmask(sc->sc_ih); 318 } 319} 320 321static int 322zrc_scan(void) 323{ 324 int val; 325 int i; 326 327/* XXX MAX1111 command word - also appears in zaurus_apm.c */ 328#define MAXCTRL_PD0 (1<<0) 329#define MAXCTRL_PD1 (1<<1) 330#define MAXCTRL_SGL (1<<2) 331#define MAXCTRL_UNI (1<<3) 332#define MAXCTRL_SEL_SHIFT 4 333#define MAXCTRL_STR (1<<7) 334 335#define C3000_ADCCH_ZRC 0 336 val = zssp_read_max1111(MAXCTRL_PD0 | MAXCTRL_PD1 | MAXCTRL_SGL | 337 MAXCTRL_UNI | (C3000_ADCCH_ZRC << MAXCTRL_SEL_SHIFT) | 338 MAXCTRL_STR); 339 for (i = 0; zrc_akeytab[i].min != INT_MIN; i++) 340 if (val >= zrc_akeytab[i].min) 341 break; 342 return zrc_akeytab[i].key; 343} 344 345static void 346zrc_input(struct zrc_softc *sc, int key, int down) 347{ 348 u_int type = down ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP; 349 int s; 350 351 s = spltty(); 352 353#ifdef WSDISPLAY_COMPAT_RAWKBD 354 if (sc->sc_rawkbd) { 355 int c; 356 u_char cbuf[2]; 357 int ncbuf = 0; 358 359 c = zrc_xt_keymap[key]; 360 if (c & 0x80) 361 cbuf[ncbuf++] = 0xe0; 362 cbuf[ncbuf] = c & 0x7f; 363 364 if (!down) 365 cbuf[ncbuf] |= 0x80; 366 ncbuf++; 367 368 wskbd_rawinput(sc->sc_wskbddev, cbuf, ncbuf); 369 } else 370#endif 371 wskbd_input(sc->sc_wskbddev, type, key); 372 373 splx(s); 374} 375 376static int 377zrc_enable(void *v, int on) 378{ 379 380 return 0; 381} 382 383static void 384zrc_set_leds(void *v, int on) 385{ 386 387 /* Nothing to do */ 388} 389 390static int 391zrc_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) 392{ 393#ifdef WSDISPLAY_COMPAT_RAWKBD 394 struct zrc_softc *sc = v; 395#endif 396 397 switch (cmd) { 398 case WSKBDIO_GTYPE: 399 *(int *)data = WSKBD_TYPE_ZAURUS; 400 return 0; 401 case WSKBDIO_SETLEDS: 402 return 0; 403 case WSKBDIO_GETLEDS: 404 *(int *)data = 0; 405 return 0; 406#ifdef WSDISPLAY_COMPAT_RAWKBD 407 case WSKBDIO_SETMODE: 408 sc->sc_rawkbd = (*(int *)data == WSKBD_RAW); 409 return 0; 410#endif 411 } 412 return EPASSTHROUGH; 413} 414