1*88c7a0e2Sandvar /* $NetBSD: zrc.c,v 1.13 2023/03/28 20:01:58 andvar Exp $ */
2953d3b5bSober /* $OpenBSD: zaurus_remote.c,v 1.1 2005/11/17 05:26:31 uwe Exp $ */
3953d3b5bSober
4953d3b5bSober /*
5953d3b5bSober * Copyright (c) 2005 Uwe Stuehler <uwe@openbsd.org>
6953d3b5bSober *
7953d3b5bSober * Permission to use, copy, modify, and distribute this software for any
8953d3b5bSober * purpose with or without fee is hereby granted, provided that the above
9953d3b5bSober * copyright notice and this permission notice appear in all copies.
10953d3b5bSober *
11953d3b5bSober * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12953d3b5bSober * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13953d3b5bSober * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14953d3b5bSober * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15953d3b5bSober * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16953d3b5bSober * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17953d3b5bSober * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18953d3b5bSober */
19953d3b5bSober
20aa1e0567Stsutsui #include "opt_wsdisplay_compat.h"
21aa1e0567Stsutsui
22953d3b5bSober #include <sys/cdefs.h>
23*88c7a0e2Sandvar __KERNEL_RCSID(0, "$NetBSD: zrc.c,v 1.13 2023/03/28 20:01:58 andvar Exp $");
24953d3b5bSober
25953d3b5bSober #include <sys/param.h>
26953d3b5bSober #include <sys/device.h>
27953d3b5bSober #include <sys/kernel.h>
28953d3b5bSober #include <sys/callout.h>
29953d3b5bSober #include <sys/systm.h>
30953d3b5bSober
31953d3b5bSober #include <dev/wscons/wsconsio.h>
32953d3b5bSober #include <dev/wscons/wskbdvar.h>
33953d3b5bSober #include <dev/wscons/wsksymdef.h>
34953d3b5bSober #include <dev/wscons/wsksymvar.h>
35953d3b5bSober
36953d3b5bSober #include <arm/xscale/pxa2x0reg.h>
37953d3b5bSober #include <arm/xscale/pxa2x0_gpio.h>
38953d3b5bSober
39953d3b5bSober #include <machine/intr.h>
40953d3b5bSober
41953d3b5bSober #include <zaurus/zaurus/zaurus_reg.h>
42953d3b5bSober #include <zaurus/zaurus/zaurus_var.h>
43003bdf4cSnonaka #include <zaurus/dev/zsspvar.h>
44003bdf4cSnonaka #include <zaurus/dev/scoopvar.h>
45003bdf4cSnonaka #include <zaurus/dev/ioexpvar.h>
46953d3b5bSober
47953d3b5bSober #define RESCAN_INTERVAL (hz/100)
48953d3b5bSober
49953d3b5bSober #define KEY_RELEASE 0 /* button release */
50953d3b5bSober #define KEY_VOL_DOWN 1
51953d3b5bSober #define KEY_MUTE 2
52953d3b5bSober #define KEY_REWIND 3
53953d3b5bSober #define KEY_VOL_UP 4
54953d3b5bSober #define KEY_FORWARD 5
55953d3b5bSober #define KEY_PLAY 6
56953d3b5bSober #define KEY_STOP 7
57953d3b5bSober #define KEY_EARPHONE 8
58953d3b5bSober
59953d3b5bSober #ifdef DEBUG
60953d3b5bSober static const char *zrc_keyname[] = {
61953d3b5bSober "(release)", "volume down", "mute", "rewind", "volume up",
62953d3b5bSober "forward", "play", "stop", "(earphone)"
63953d3b5bSober };
64953d3b5bSober #endif
65953d3b5bSober
66953d3b5bSober struct zrc_akey {
67953d3b5bSober int min; /* minimum ADC value or INT_MIN */
68953d3b5bSober int key; /* remote control key number */
69953d3b5bSober };
70953d3b5bSober
71953d3b5bSober /* Values match the resistors in the CE-RH2 remote control. */
72953d3b5bSober static const struct zrc_akey zrc_akeytab_c3000[] = {
73953d3b5bSober { 238, KEY_RELEASE },
74953d3b5bSober { 202, KEY_VOL_DOWN },
75953d3b5bSober { 168, KEY_MUTE },
76953d3b5bSober { 135, KEY_REWIND },
77953d3b5bSober { 105, KEY_VOL_UP },
78953d3b5bSober { 74, KEY_FORWARD },
79953d3b5bSober { 42, KEY_PLAY },
80953d3b5bSober { 12, KEY_STOP },
81953d3b5bSober { INT_MIN, KEY_EARPHONE }
82953d3b5bSober };
83953d3b5bSober
84953d3b5bSober static const struct zrc_akey *zrc_akeytab = zrc_akeytab_c3000;
85953d3b5bSober
86953d3b5bSober struct zrc_softc {
871a686006Snonaka device_t sc_dev;
88953d3b5bSober struct callout sc_to;
89953d3b5bSober void *sc_ih;
90953d3b5bSober int sc_key; /* being scanned */
91953d3b5bSober int sc_scans; /* rescan counter */
92953d3b5bSober int sc_noise; /* discard if too noisy? */
93953d3b5bSober int sc_keydown; /* currently pressed key */
94cbab9cadSchs device_t sc_wskbddev;
95953d3b5bSober #ifdef WSDISPLAY_COMPAT_RAWKBD
96953d3b5bSober int sc_rawkbd;
97953d3b5bSober #endif
98953d3b5bSober };
99953d3b5bSober
100cbab9cadSchs static int zrc_match(device_t, cfdata_t, void *);
101cbab9cadSchs static void zrc_attach(device_t, device_t, void *);
102953d3b5bSober
1031a686006Snonaka CFATTACH_DECL_NEW(zrc, sizeof(struct zrc_softc),
104953d3b5bSober zrc_match, zrc_attach, NULL, NULL);
105953d3b5bSober
106fd148500Stsutsui static int zrc_finalize(device_t);
107953d3b5bSober static int zrc_intr(void *);
108953d3b5bSober static void zrc_timeout(void *);
109953d3b5bSober static int zrc_scan(void);
110953d3b5bSober static void zrc_input(struct zrc_softc *, int, int);
111953d3b5bSober
112953d3b5bSober static int zrc_enable(void *, int);
113953d3b5bSober static void zrc_set_leds(void *, int);
11453524e44Schristos static int zrc_ioctl(void *, u_long, void *, int, struct lwp *);
115953d3b5bSober
116953d3b5bSober struct wskbd_accessops zrc_accessops = {
117953d3b5bSober zrc_enable,
118953d3b5bSober zrc_set_leds,
119953d3b5bSober zrc_ioctl,
120953d3b5bSober };
121953d3b5bSober
122953d3b5bSober #define KC(n) KS_KEYCODE(n)
123953d3b5bSober
124953d3b5bSober /* XXX what keys should be generated in translated mode? */
125953d3b5bSober static const keysym_t zrc_keydesc[] = {
126003bdf4cSnonaka KC(KEY_VOL_DOWN), KS_Cmd_VolumeUp,
127003bdf4cSnonaka KC(KEY_MUTE), KS_Cmd_VolumeToggle,
128953d3b5bSober KC(KEY_REWIND), KS_b,
129003bdf4cSnonaka KC(KEY_VOL_UP), KS_Cmd_VolumeDown,
130953d3b5bSober KC(KEY_FORWARD), KS_f,
131953d3b5bSober KC(KEY_PLAY), KS_p,
132953d3b5bSober KC(KEY_STOP), KS_s,
133953d3b5bSober };
134953d3b5bSober
135953d3b5bSober #ifdef WSDISPLAY_COMPAT_RAWKBD
136aa1e0567Stsutsui /* XXX see OpenBSD's <sys/dev/wscons/wskbdraw.h> */
137aa1e0567Stsutsui #define RAWKEY_Null 0x00
138aa1e0567Stsutsui #define RAWKEY_AudioMute 0x85
139aa1e0567Stsutsui #define RAWKEY_AudioLower 0x86
140aa1e0567Stsutsui #define RAWKEY_AudioRaise 0x87
141953d3b5bSober #define RAWKEY_AudioRewind 0xa0
142953d3b5bSober #define RAWKEY_AudioForward 0xa1
143953d3b5bSober #define RAWKEY_AudioPlay 0xa2
144953d3b5bSober #define RAWKEY_AudioStop 0xa3
145953d3b5bSober static const keysym_t zrc_xt_keymap[] = {
146953d3b5bSober /* KC(KEY_RELEASE), */ RAWKEY_Null,
147953d3b5bSober /* KC(KEY_VOL_DOWN), */ RAWKEY_AudioLower,
148953d3b5bSober /* KC(KEY_MUTE), */ RAWKEY_AudioMute,
149953d3b5bSober /* KC(KEY_REWIND), */ RAWKEY_AudioRewind,
150953d3b5bSober /* KC(KEY_VOL_UP), */ RAWKEY_AudioRaise,
151953d3b5bSober /* KC(KEY_FORWARD), */ RAWKEY_AudioForward,
152953d3b5bSober /* KC(KEY_PLAY), */ RAWKEY_AudioPlay,
153953d3b5bSober /* KC(KEY_STOP), */ RAWKEY_AudioStop,
154953d3b5bSober };
155953d3b5bSober #endif
156953d3b5bSober
157953d3b5bSober static const struct wscons_keydesc zrc_keydesctab[] = {
158953d3b5bSober {KB_US, 0, sizeof(zrc_keydesc)/sizeof(keysym_t), zrc_keydesc},
159953d3b5bSober {0, 0, 0, 0}
160953d3b5bSober };
161953d3b5bSober
162953d3b5bSober struct wskbd_mapdata zrc_keymapdata = {
163953d3b5bSober zrc_keydesctab, KB_US
164953d3b5bSober };
165953d3b5bSober
166f19ed1a8Speter #undef KC
167f19ed1a8Speter
168f19ed1a8Speter static int
zrc_match(device_t parent,cfdata_t cf,void * aux)1691a686006Snonaka zrc_match(device_t parent, cfdata_t cf, void *aux)
170953d3b5bSober {
171953d3b5bSober
172003bdf4cSnonaka if (ZAURUS_ISC1000 || ZAURUS_ISC3000)
173953d3b5bSober return 1;
174953d3b5bSober return 0;
175953d3b5bSober }
176953d3b5bSober
177f19ed1a8Speter static void
zrc_attach(device_t parent,device_t self,void * aux)1781a686006Snonaka zrc_attach(device_t parent, device_t self, void *aux)
179953d3b5bSober {
1801a686006Snonaka struct zrc_softc *sc = device_private(self);
181953d3b5bSober struct wskbddev_attach_args a;
182953d3b5bSober
1831a686006Snonaka sc->sc_dev = self;
1841a686006Snonaka
1851a686006Snonaka aprint_normal(": CE-RH2 remote control\n");
1861a686006Snonaka aprint_naive("\n");
1871a686006Snonaka
188953d3b5bSober /* Configure remote control interrupt handling. */
18988ab7da9Sad callout_init(&sc->sc_to, 0);
190953d3b5bSober callout_setfunc(&sc->sc_to, zrc_timeout, sc);
1911a686006Snonaka
192*88c7a0e2Sandvar /* Establish interrupt */
193953d3b5bSober pxa2x0_gpio_set_function(C3000_RC_IRQ_PIN, GPIO_IN);
194953d3b5bSober sc->sc_ih = pxa2x0_gpio_intr_establish(C3000_RC_IRQ_PIN,
195953d3b5bSober IST_EDGE_BOTH, IPL_BIO, zrc_intr, sc);
1961a686006Snonaka if (sc->sc_ih == NULL) {
1971a686006Snonaka aprint_error_dev(sc->sc_dev, "couldn't establish interrupt.\n");
1981a686006Snonaka return;
1991a686006Snonaka }
200953d3b5bSober
201fd148500Stsutsui /* defer enabling pullup until ioexp or scoop is attached */
202fd148500Stsutsui config_finalize_register(self, zrc_finalize);
203953d3b5bSober
204953d3b5bSober sc->sc_keydown = KEY_RELEASE;
205953d3b5bSober
206953d3b5bSober a.console = 0;
207953d3b5bSober a.keymap = &zrc_keymapdata;
208953d3b5bSober a.accessops = &zrc_accessops;
209953d3b5bSober a.accesscookie = sc;
210953d3b5bSober
211c7fb772bSthorpej sc->sc_wskbddev = config_found(self, &a, wskbddevprint, CFARGS_NONE);
212953d3b5bSober }
213953d3b5bSober
214f19ed1a8Speter static int
zrc_finalize(device_t dv)215fd148500Stsutsui zrc_finalize(device_t dv)
216fd148500Stsutsui {
217fd148500Stsutsui
218fd148500Stsutsui /* Enable the pullup while waiting for an interrupt. */
219fd148500Stsutsui if (ZAURUS_ISC1000)
220fd148500Stsutsui ioexp_akin_pullup(1);
221fd148500Stsutsui else
222fd148500Stsutsui scoop_akin_pullup(1);
223fd148500Stsutsui
224fd148500Stsutsui return 0;
225fd148500Stsutsui }
226fd148500Stsutsui
227fd148500Stsutsui static int
zrc_intr(void * v)228953d3b5bSober zrc_intr(void *v)
229953d3b5bSober {
230953d3b5bSober struct zrc_softc *sc = v;
231953d3b5bSober
232953d3b5bSober /* just return if remote control isn't present */
233953d3b5bSober
234953d3b5bSober pxa2x0_gpio_intr_mask(sc->sc_ih);
235003bdf4cSnonaka if (ZAURUS_ISC1000)
236003bdf4cSnonaka ioexp_akin_pullup(0);
237003bdf4cSnonaka else
238953d3b5bSober scoop_akin_pullup(0);
239953d3b5bSober sc->sc_key = zrc_scan();
240953d3b5bSober sc->sc_scans = 0;
241953d3b5bSober sc->sc_noise = 0;
242953d3b5bSober callout_schedule(&sc->sc_to, RESCAN_INTERVAL);
243f19ed1a8Speter
244953d3b5bSober return 1;
245953d3b5bSober }
246953d3b5bSober
247f19ed1a8Speter static void
zrc_timeout(void * v)248953d3b5bSober zrc_timeout(void *v)
249953d3b5bSober {
250953d3b5bSober struct zrc_softc *sc = v;
251953d3b5bSober int key;
252953d3b5bSober
253953d3b5bSober key = zrc_scan();
254953d3b5bSober switch (sc->sc_scans) {
255953d3b5bSober case 0:
256953d3b5bSober case 1:
257953d3b5bSober case 2:
258953d3b5bSober /* wait for a stable read */
259953d3b5bSober if (sc->sc_key == key)
260953d3b5bSober sc->sc_scans++;
261953d3b5bSober else {
262953d3b5bSober sc->sc_key = key;
263953d3b5bSober sc->sc_scans = 0;
264953d3b5bSober sc->sc_noise++;
265953d3b5bSober }
266953d3b5bSober callout_schedule(&sc->sc_to, RESCAN_INTERVAL);
267953d3b5bSober break;
268953d3b5bSober case 3:
269953d3b5bSober /* generate key press event */
270953d3b5bSober if (sc->sc_key != key) {
271953d3b5bSober key = sc->sc_key;
272953d3b5bSober sc->sc_noise++;
273953d3b5bSober }
274953d3b5bSober sc->sc_scans++;
275953d3b5bSober switch (key) {
276953d3b5bSober case KEY_EARPHONE:
277953d3b5bSober case KEY_RELEASE:
278953d3b5bSober sc->sc_scans = 6;
279953d3b5bSober break;
280953d3b5bSober default:
281953d3b5bSober #ifdef DEBUG
2821a686006Snonaka printf("%s: %s pressed (%d noise)\n",
2831a686006Snonaka device_xname(sc->sc_dev),
2841a686006Snonaka zrc_keyname[key], sc->sc_noise);
285953d3b5bSober #endif
286953d3b5bSober sc->sc_keydown = key;
287953d3b5bSober sc->sc_noise = 0;
288953d3b5bSober zrc_input(sc, key, 1);
289953d3b5bSober break;
290953d3b5bSober }
291953d3b5bSober callout_schedule(&sc->sc_to, RESCAN_INTERVAL);
292953d3b5bSober break;
293953d3b5bSober case 4:
294953d3b5bSober case 5:
295953d3b5bSober /* wait for key release, permit noise */
296953d3b5bSober if (sc->sc_key == key) {
297953d3b5bSober if (sc->sc_scans == 5)
298953d3b5bSober sc->sc_noise++;
299953d3b5bSober sc->sc_scans = 4;
300953d3b5bSober } else
301953d3b5bSober sc->sc_scans++;
302953d3b5bSober callout_schedule(&sc->sc_to, RESCAN_INTERVAL);
303953d3b5bSober break;
304953d3b5bSober case 6:
305953d3b5bSober /* generate key release event */
306953d3b5bSober if (sc->sc_keydown != KEY_RELEASE) {
307953d3b5bSober zrc_input(sc, sc->sc_keydown, 0);
308953d3b5bSober #ifdef DEBUG
3091a686006Snonaka printf("%s: %s released (%d noise)\n",
3101a686006Snonaka device_xname(sc->sc_dev),
311953d3b5bSober zrc_keyname[sc->sc_keydown], sc->sc_noise);
312953d3b5bSober #endif
313953d3b5bSober sc->sc_keydown = KEY_RELEASE;
314953d3b5bSober }
315953d3b5bSober /* FALLTHROUGH */
316953d3b5bSober default:
317953d3b5bSober /* unmask interrupt again */
318953d3b5bSober callout_stop(&sc->sc_to);
319953d3b5bSober sc->sc_scans = 7;
320003bdf4cSnonaka if (ZAURUS_ISC1000)
321003bdf4cSnonaka ioexp_akin_pullup(1);
322003bdf4cSnonaka else
323953d3b5bSober scoop_akin_pullup(1);
324953d3b5bSober pxa2x0_gpio_intr_unmask(sc->sc_ih);
325953d3b5bSober }
326953d3b5bSober }
327953d3b5bSober
328f19ed1a8Speter static int
zrc_scan(void)329953d3b5bSober zrc_scan(void)
330953d3b5bSober {
331953d3b5bSober int val;
332953d3b5bSober int i;
333953d3b5bSober
334953d3b5bSober /* XXX MAX1111 command word - also appears in zaurus_apm.c */
335953d3b5bSober #define MAXCTRL_PD0 (1<<0)
336953d3b5bSober #define MAXCTRL_PD1 (1<<1)
337953d3b5bSober #define MAXCTRL_SGL (1<<2)
338953d3b5bSober #define MAXCTRL_UNI (1<<3)
339953d3b5bSober #define MAXCTRL_SEL_SHIFT 4
340953d3b5bSober #define MAXCTRL_STR (1<<7)
341953d3b5bSober
342953d3b5bSober #define C3000_ADCCH_ZRC 0
343953d3b5bSober val = zssp_read_max1111(MAXCTRL_PD0 | MAXCTRL_PD1 | MAXCTRL_SGL |
344953d3b5bSober MAXCTRL_UNI | (C3000_ADCCH_ZRC << MAXCTRL_SEL_SHIFT) |
345953d3b5bSober MAXCTRL_STR);
346953d3b5bSober for (i = 0; zrc_akeytab[i].min != INT_MIN; i++)
347953d3b5bSober if (val >= zrc_akeytab[i].min)
348953d3b5bSober break;
349953d3b5bSober return zrc_akeytab[i].key;
350953d3b5bSober }
351953d3b5bSober
352f19ed1a8Speter static void
zrc_input(struct zrc_softc * sc,int key,int down)353953d3b5bSober zrc_input(struct zrc_softc *sc, int key, int down)
354953d3b5bSober {
355953d3b5bSober u_int type = down ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP;
356953d3b5bSober int s;
357953d3b5bSober
358953d3b5bSober s = spltty();
359953d3b5bSober
360953d3b5bSober #ifdef WSDISPLAY_COMPAT_RAWKBD
361953d3b5bSober if (sc->sc_rawkbd) {
362953d3b5bSober int c;
363953d3b5bSober u_char cbuf[2];
364953d3b5bSober int ncbuf = 0;
365953d3b5bSober
366953d3b5bSober c = zrc_xt_keymap[key];
367953d3b5bSober if (c & 0x80)
368953d3b5bSober cbuf[ncbuf++] = 0xe0;
369953d3b5bSober cbuf[ncbuf] = c & 0x7f;
370953d3b5bSober
371953d3b5bSober if (!down)
372953d3b5bSober cbuf[ncbuf] |= 0x80;
373953d3b5bSober ncbuf++;
374953d3b5bSober
375953d3b5bSober wskbd_rawinput(sc->sc_wskbddev, cbuf, ncbuf);
376953d3b5bSober } else
377953d3b5bSober #endif
378953d3b5bSober wskbd_input(sc->sc_wskbddev, type, key);
379953d3b5bSober
380953d3b5bSober splx(s);
381953d3b5bSober }
382953d3b5bSober
383f19ed1a8Speter static int
zrc_enable(void * v,int on)384953d3b5bSober zrc_enable(void *v, int on)
385953d3b5bSober {
386953d3b5bSober
387953d3b5bSober return 0;
388953d3b5bSober }
389953d3b5bSober
390f19ed1a8Speter static void
zrc_set_leds(void * v,int on)391953d3b5bSober zrc_set_leds(void *v, int on)
392953d3b5bSober {
393953d3b5bSober
394953d3b5bSober /* Nothing to do */
395953d3b5bSober }
396953d3b5bSober
397f19ed1a8Speter static int
zrc_ioctl(void * v,u_long cmd,void * data,int flag,struct lwp * l)39853524e44Schristos zrc_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
399953d3b5bSober {
400953d3b5bSober #ifdef WSDISPLAY_COMPAT_RAWKBD
401953d3b5bSober struct zrc_softc *sc = v;
402953d3b5bSober #endif
403953d3b5bSober
404953d3b5bSober switch (cmd) {
405953d3b5bSober case WSKBDIO_GTYPE:
406953d3b5bSober *(int *)data = WSKBD_TYPE_ZAURUS;
407953d3b5bSober return 0;
408953d3b5bSober case WSKBDIO_SETLEDS:
409953d3b5bSober return 0;
410953d3b5bSober case WSKBDIO_GETLEDS:
411953d3b5bSober *(int *)data = 0;
412953d3b5bSober return 0;
413953d3b5bSober #ifdef WSDISPLAY_COMPAT_RAWKBD
414953d3b5bSober case WSKBDIO_SETMODE:
415953d3b5bSober sc->sc_rawkbd = (*(int *)data == WSKBD_RAW);
416953d3b5bSober return 0;
417953d3b5bSober #endif
418953d3b5bSober }
419f19ed1a8Speter return EPASSTHROUGH;
420953d3b5bSober }
421