xref: /netbsd-src/sys/arch/hpcsh/dev/j6x0tp.c (revision 7433666e375b3ac4cc764df5a6726be98bc1cdd5)
1*7433666eSthorpej /*	$NetBSD: j6x0tp.c,v 1.26 2023/12/20 14:50:02 thorpej Exp $ */
2e4bd58b0Suwe 
3e4bd58b0Suwe /*
4e4bd58b0Suwe  * Copyright (c) 2003 Valeriy E. Ushakov
5e4bd58b0Suwe  * All rights reserved.
6e4bd58b0Suwe  *
7e4bd58b0Suwe  * Redistribution and use in source and binary forms, with or without
8e4bd58b0Suwe  * modification, are permitted provided that the following conditions
9e4bd58b0Suwe  * are met:
10e4bd58b0Suwe  * 1. Redistributions of source code must retain the above copyright
11e4bd58b0Suwe  *    notice, this list of conditions and the following disclaimer.
12e4bd58b0Suwe  * 2. Redistributions in binary form must reproduce the above copyright
13e4bd58b0Suwe  *    notice, this list of conditions and the following disclaimer in the
14e4bd58b0Suwe  *    documentation and/or other materials provided with the distribution.
15e4bd58b0Suwe  * 3. The name of the author may not be used to endorse or promote products
16e4bd58b0Suwe  *    derived from this software without specific prior written permission
17e4bd58b0Suwe  *
18e4bd58b0Suwe  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19e4bd58b0Suwe  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20e4bd58b0Suwe  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21e4bd58b0Suwe  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22e4bd58b0Suwe  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23e4bd58b0Suwe  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24e4bd58b0Suwe  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25e4bd58b0Suwe  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26e4bd58b0Suwe  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27e4bd58b0Suwe  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28e4bd58b0Suwe  */
29e4bd58b0Suwe 
30e4bd58b0Suwe #include <sys/cdefs.h>
31*7433666eSthorpej __KERNEL_RCSID(0, "$NetBSD: j6x0tp.c,v 1.26 2023/12/20 14:50:02 thorpej Exp $");
32e4bd58b0Suwe 
33e4bd58b0Suwe #include <sys/param.h>
34e4bd58b0Suwe #include <sys/kernel.h>
35e4bd58b0Suwe #include <sys/device.h>
36e4bd58b0Suwe #include <sys/systm.h>
37e4bd58b0Suwe #include <sys/callout.h>
38e4bd58b0Suwe 
390af2a039Suwe #include "opt_j6x0tp.h"
400af2a039Suwe 
41e4bd58b0Suwe #include <dev/wscons/wsconsio.h>
42e4bd58b0Suwe #include <dev/wscons/wsmousevar.h>
430af2a039Suwe #include <dev/wscons/wskbdvar.h>
440af2a039Suwe #include <dev/wscons/wsksymvar.h>
450af2a039Suwe #include <dev/wscons/wsksymdef.h>
46f53a32e6Stsarna #include <dev/hpc/hpctpanelvar.h>
47e4bd58b0Suwe 
48e4bd58b0Suwe #include <machine/platid.h>
49e4bd58b0Suwe #include <machine/platid_mask.h>
50e4bd58b0Suwe 
51e4bd58b0Suwe #include <machine/intr.h>
52e4bd58b0Suwe 
53e4bd58b0Suwe #include <sh3/exception.h>
54e4bd58b0Suwe #include <sh3/intcreg.h>
55e4bd58b0Suwe #include <sh3/pfcreg.h>
56e4bd58b0Suwe #include <sh3/adcreg.h>
57e4bd58b0Suwe 
5849fd54d3Suwe #include <sh3/dev/adcvar.h>
5949fd54d3Suwe 
6049fd54d3Suwe 
61e4bd58b0Suwe #if 0 /* XXX: disabled in favor of local version that uses printf_nolog */
62e4bd58b0Suwe #define DPRINTF_ENABLE
63e4bd58b0Suwe #define DPRINTF_DEBUG	j6x0tp_debug
64e4bd58b0Suwe #define DPRINTF_LEVEL	0
65e4bd58b0Suwe #include <machine/debug.h>
66e4bd58b0Suwe #else
67e4bd58b0Suwe #ifdef J6X0TP_DEBUG
68e4bd58b0Suwe volatile int j6x0tp_debug = 0;
690af2a039Suwe #define DPRINTF_PRINTF		printf_nolog
700af2a039Suwe #define DPRINTF(arg)		if (j6x0tp_debug) DPRINTF_PRINTF arg
710af2a039Suwe #define DPRINTFN(n, arg)	if (j6x0tp_debug > (n)) DPRINTF_PRINTF arg
72e4bd58b0Suwe #else
73e4bd58b0Suwe #define DPRINTF(arg)		((void)0)
74e4bd58b0Suwe #define DPRINTFN(n, arg)	((void)0)
75e4bd58b0Suwe #endif
76e4bd58b0Suwe #endif
77e4bd58b0Suwe 
78e4bd58b0Suwe 
790af2a039Suwe /*
800af2a039Suwe  * PFC bits pertinent to Jornada 6x0 touchpanel
810af2a039Suwe  */
82e4bd58b0Suwe #define PHDR_TP_PEN_DOWN	0x08
83e4bd58b0Suwe 
84e4bd58b0Suwe #define SCPDR_TP_SCAN_ENABLE	0x20
85e4bd58b0Suwe #define SCPDR_TP_SCAN_Y		0x02
86e4bd58b0Suwe #define SCPDR_TP_SCAN_X		0x01
87e4bd58b0Suwe 
880af2a039Suwe /*
89758b6ed7Suwe  * A/D converter channels to get x/y from
900af2a039Suwe  */
91e4bd58b0Suwe #define ADC_CHANNEL_TP_Y	1
92e4bd58b0Suwe #define ADC_CHANNEL_TP_X	2
93e4bd58b0Suwe 
940af2a039Suwe /*
950af2a039Suwe  * Default (read: my device :) raw X/Y values for framebuffer edges.
960af2a039Suwe  * XXX: defopt these?
970af2a039Suwe  */
980af2a039Suwe #define J6X0TP_FB_LEFT		 38
990af2a039Suwe #define J6X0TP_FB_RIGHT		950
1000af2a039Suwe #define J6X0TP_FB_TOP		 80
1010af2a039Suwe #define J6X0TP_FB_BOTTOM	900
1020af2a039Suwe 
1030af2a039Suwe /*
1040af2a039Suwe  * Bottom of the n'th hard icon (n = 1..4)
1050af2a039Suwe  */
1060af2a039Suwe #define J6X0TP_HARD_ICON_MAX_Y(n) \
1070af2a039Suwe 	(J6X0TP_FB_TOP + ((J6X0TP_FB_BOTTOM - J6X0TP_FB_TOP) / 4) * (n))
1080af2a039Suwe 
109e4bd58b0Suwe 
110e4bd58b0Suwe struct j6x0tp_softc {
111b5ba6eebSuwe 	device_t sc_dev;
112e4bd58b0Suwe 
1130af2a039Suwe #define J6X0TP_WSMOUSE_ENABLED	0x01
1140af2a039Suwe #define J6X0TP_WSKBD_ENABLED	0x02
115e4bd58b0Suwe 	int sc_enabled;
116e4bd58b0Suwe 
1170af2a039Suwe 	int sc_hard_icon;
1180af2a039Suwe 
1190af2a039Suwe 	struct callout sc_touch_ch;
1200af2a039Suwe 
121b5ba6eebSuwe 	device_t sc_wsmousedev;
122b5ba6eebSuwe 	device_t sc_wskbddev;
1230af2a039Suwe 
1240af2a039Suwe 	struct tpcalib_softc sc_tpcalib; /* calibration info for wsmouse */
125e4bd58b0Suwe };
126e4bd58b0Suwe 
1270af2a039Suwe 
1280af2a039Suwe /* config machinery */
129b5ba6eebSuwe static int	j6x0tp_match(device_t, cfdata_t, void *);
130b5ba6eebSuwe static void	j6x0tp_attach(device_t, device_t, void *);
131e4bd58b0Suwe 
1320af2a039Suwe /* wsmouse accessops */
1330af2a039Suwe static int	j6x0tp_wsmouse_enable(void *);
134b5ba6eebSuwe static int	j6x0tp_wsmouse_ioctl(void *, u_long, void *, int, lwp_t *);
1350af2a039Suwe static void	j6x0tp_wsmouse_disable(void *);
136e4bd58b0Suwe 
1370af2a039Suwe /* wskbd accessops */
1380af2a039Suwe static int	j6x0tp_wskbd_enable(void *, int);
1390af2a039Suwe static void	j6x0tp_wskbd_set_leds(void *, int);
140b5ba6eebSuwe static int	j6x0tp_wskbd_ioctl(void *, u_long, void *, int, lwp_t *);
141e4bd58b0Suwe 
1420af2a039Suwe /* internal driver routines */
1430af2a039Suwe static void	j6x0tp_enable(struct j6x0tp_softc *);
1440af2a039Suwe static void	j6x0tp_disable(struct j6x0tp_softc *);
1454a747421Suwe static int	j6x0tp_enable_child(struct j6x0tp_softc *, int, int);
1460af2a039Suwe static int	j6x0tp_intr(void *);
1470af2a039Suwe static void	j6x0tp_start_polling(void *);
1480af2a039Suwe static void	j6x0tp_stop_polling(struct j6x0tp_softc *);
1490af2a039Suwe static void	j6x0tp_callout_wsmouse(void *);
1500af2a039Suwe static void	j6x0tp_callout_wskbd(void *);
1510af2a039Suwe static void	j6x0tp_wsmouse_input(struct j6x0tp_softc *, int, int);
1520af2a039Suwe static void	j6x0tp_get_raw_xy(int *, int *);
1530af2a039Suwe static int	j6x0tp_get_hard_icon(int, int);
1540af2a039Suwe 
155e4bd58b0Suwe 
156828f7077Suwe static const struct wsmouse_accessops j6x0tp_accessops = {
1570af2a039Suwe 	j6x0tp_wsmouse_enable,
1580af2a039Suwe 	j6x0tp_wsmouse_ioctl,
1590af2a039Suwe 	j6x0tp_wsmouse_disable
160e4bd58b0Suwe };
161e4bd58b0Suwe 
162e4bd58b0Suwe static const struct wsmouse_calibcoords j6x0tp_default_calib = {
163e4bd58b0Suwe 	0, 0, 639, 239,
164e4bd58b0Suwe 	4,
1650af2a039Suwe 	{{ J6X0TP_FB_LEFT,  J6X0TP_FB_TOP,      0,   0 },
1660af2a039Suwe 	 { J6X0TP_FB_RIGHT, J6X0TP_FB_TOP,    639,   0 },
1670af2a039Suwe 	 { J6X0TP_FB_LEFT,  J6X0TP_FB_BOTTOM,   0, 239 },
1680af2a039Suwe 	 { J6X0TP_FB_RIGHT, J6X0TP_FB_BOTTOM, 639, 239 }}
1690af2a039Suwe };
1700af2a039Suwe 
171828f7077Suwe static const struct wskbd_accessops j6x0tp_wskbd_accessops = {
1720af2a039Suwe 	j6x0tp_wskbd_enable,
1730af2a039Suwe 	j6x0tp_wskbd_set_leds,
1740af2a039Suwe 	j6x0tp_wskbd_ioctl
175e4bd58b0Suwe };
176e4bd58b0Suwe 
177e4bd58b0Suwe 
1780af2a039Suwe #ifndef J6X0TP_SETTINGS_ICON_KEYSYM
1790af2a039Suwe #define J6X0TP_SETTINGS_ICON_KEYSYM	KS_Home
1800af2a039Suwe #endif
1810af2a039Suwe #ifndef J6X0TP_PGUP_ICON_KEYSYM
1820af2a039Suwe #define J6X0TP_PGUP_ICON_KEYSYM		KS_Prior
1830af2a039Suwe #endif
1840af2a039Suwe #ifndef J6X0TP_PGDN_ICON_KEYSYM
1850af2a039Suwe #define J6X0TP_PGDN_ICON_KEYSYM		KS_Next
1860af2a039Suwe #endif
1870af2a039Suwe #ifndef J6X0TP_SWITCH_ICON_KEYSYM
1880af2a039Suwe #define J6X0TP_SWITCH_ICON_KEYSYM	KS_End
1890af2a039Suwe #endif
1900af2a039Suwe 
1910af2a039Suwe static const keysym_t j6x0tp_wskbd_keydesc[] = {
1920af2a039Suwe 	KS_KEYCODE(1), J6X0TP_SETTINGS_ICON_KEYSYM,
1930af2a039Suwe 	KS_KEYCODE(2), J6X0TP_PGUP_ICON_KEYSYM,
1940af2a039Suwe 	KS_KEYCODE(3), J6X0TP_PGDN_ICON_KEYSYM,
1950af2a039Suwe 	KS_KEYCODE(4), J6X0TP_SWITCH_ICON_KEYSYM
1960af2a039Suwe };
1970af2a039Suwe 
198828f7077Suwe static const struct wscons_keydesc j6x0tp_wskbd_keydesctab[] = {
1990af2a039Suwe 	{ KB_US, 0,
2000af2a039Suwe 	  sizeof(j6x0tp_wskbd_keydesc)/sizeof(keysym_t),
2010af2a039Suwe 	  j6x0tp_wskbd_keydesc
2020af2a039Suwe 	},
2030af2a039Suwe 	{0, 0, 0, 0}
2040af2a039Suwe };
2050af2a039Suwe 
206828f7077Suwe static const struct wskbd_mapdata j6x0tp_wskbd_keymapdata = {
2070af2a039Suwe         j6x0tp_wskbd_keydesctab, KB_US
2080af2a039Suwe };
2090af2a039Suwe 
2100af2a039Suwe 
211b5ba6eebSuwe CFATTACH_DECL_NEW(j6x0tp, sizeof(struct j6x0tp_softc),
2120af2a039Suwe     j6x0tp_match, j6x0tp_attach, NULL, NULL);
213e4bd58b0Suwe 
214e4bd58b0Suwe 
215e4bd58b0Suwe static int
j6x0tp_match(device_t parent,cfdata_t cf,void * aux)216b5ba6eebSuwe j6x0tp_match(device_t parent, cfdata_t cf, void *aux)
217e4bd58b0Suwe {
218e4bd58b0Suwe 
219e4bd58b0Suwe 	/*
220065e2422Suwe 	 * XXX: platid_mask_MACH_HP_LX also matches 360LX.  It's not
221065e2422Suwe 	 * confirmed whether touch panel in 360LX is connected this
222065e2422Suwe 	 * way.  We may need to regroup platid masks.
223e4bd58b0Suwe 	 */
224065e2422Suwe 	if (!platid_match(&platid, &platid_mask_MACH_HP_JORNADA_6XX)
225065e2422Suwe 	    && !platid_match(&platid, &platid_mask_MACH_HP_LX))
226e4bd58b0Suwe 		return (0);
227e4bd58b0Suwe 
2280af2a039Suwe 	if (strcmp(cf->cf_name, "j6x0tp") != 0)
229e4bd58b0Suwe 		return (0);
230e4bd58b0Suwe 
231e4bd58b0Suwe 	return (1);
232e4bd58b0Suwe }
233e4bd58b0Suwe 
234e4bd58b0Suwe 
235e4bd58b0Suwe static void
j6x0tp_attach(device_t parent,device_t self,void * aux)236b5ba6eebSuwe j6x0tp_attach(device_t parent, device_t self, void *aux)
237e4bd58b0Suwe {
238b5ba6eebSuwe 	struct j6x0tp_softc *sc;
239e4bd58b0Suwe 	struct wsmousedev_attach_args wsma;
2400af2a039Suwe 	struct wskbddev_attach_args wska;
241e4bd58b0Suwe 
242b5ba6eebSuwe 	aprint_naive("\n");
243b5ba6eebSuwe 	aprint_normal("\n");
244b5ba6eebSuwe 
245b5ba6eebSuwe 	sc = device_private(self);
246b5ba6eebSuwe 	sc->sc_dev = self;
247e4bd58b0Suwe 
248e4bd58b0Suwe 	sc->sc_enabled = 0;
2490af2a039Suwe 	sc->sc_hard_icon = 0;
250e4bd58b0Suwe 
2510af2a039Suwe 	/* touch-panel as a pointing device */
252e4bd58b0Suwe 	wsma.accessops = &j6x0tp_accessops;
253e4bd58b0Suwe 	wsma.accesscookie = sc;
254e4bd58b0Suwe 
2552685996bSthorpej 	sc->sc_wsmousedev = config_found(self, &wsma, wsmousedevprint,
256c7fb772bSthorpej 	    CFARGS(.iattr = "wsmousedev"));
257e4bd58b0Suwe 	if (sc->sc_wsmousedev == NULL)
258e4bd58b0Suwe 		return;
259e4bd58b0Suwe 
2600af2a039Suwe 	/* on-screen "hard icons" as a keyboard device */
2610af2a039Suwe 	wska.console = 0;
2620af2a039Suwe 	wska.keymap = &j6x0tp_wskbd_keymapdata;
2630af2a039Suwe 	wska.accessops = &j6x0tp_wskbd_accessops;
2640af2a039Suwe 	wska.accesscookie = sc;
2650af2a039Suwe 
2662685996bSthorpej 	sc->sc_wskbddev = config_found(self, &wska, wskbddevprint,
267c7fb772bSthorpej 	    CFARGS(.iattr = "wskbddev"));
2680af2a039Suwe 
269758b6ed7Suwe 	/* init calibration, set default parameters */
270e4bd58b0Suwe 	tpcalib_init(&sc->sc_tpcalib);
271e4bd58b0Suwe 	tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS,
27253524e44Schristos 		      (void *)__UNCONST(&j6x0tp_default_calib), 0, 0);
273e4bd58b0Suwe 
2740af2a039Suwe 	/* used when in polling mode */
2752af9604eSuwe 	callout_init(&sc->sc_touch_ch, 0);
276e4bd58b0Suwe 
2770af2a039Suwe 	/* establish interrupt handler, but disable until opened */
278e4bd58b0Suwe 	intc_intr_establish(SH7709_INTEVT2_IRQ3, IST_EDGE, IPL_TTY,
279e4bd58b0Suwe 			    j6x0tp_intr, sc);
2800af2a039Suwe 	intc_intr_disable(SH7709_INTEVT2_IRQ3);
281620a6f25Suwe 
282620a6f25Suwe 	if (!pmf_device_register(self, NULL, NULL))
283620a6f25Suwe 		aprint_error_dev(self, "unable to establish power handler\n");
284e4bd58b0Suwe }
285e4bd58b0Suwe 
286e4bd58b0Suwe 
2870af2a039Suwe /*
2880af2a039Suwe  * Enable touch panel:  we start in interrupt mode.
2890af2a039Suwe  * Must be called as spltty().
2900af2a039Suwe  */
2910af2a039Suwe static void
j6x0tp_enable(struct j6x0tp_softc * sc)2920af2a039Suwe j6x0tp_enable(struct j6x0tp_softc *sc)
2930af2a039Suwe {
2940af2a039Suwe 
295b5ba6eebSuwe 	DPRINTFN(2, ("%s: enable\n", device_xname(sc->sc_dev)));
2960af2a039Suwe 	intc_intr_enable(SH7709_INTEVT2_IRQ3);
2970af2a039Suwe }
2980af2a039Suwe 
2990af2a039Suwe 
3000af2a039Suwe /*
3010af2a039Suwe  * Disable touch panel: disable interrupt, cancel pending callout.
3020af2a039Suwe  * Must be called as spltty().
3030af2a039Suwe  */
3040af2a039Suwe static void
j6x0tp_disable(struct j6x0tp_softc * sc)3050af2a039Suwe j6x0tp_disable(struct j6x0tp_softc *sc)
3060af2a039Suwe {
3070af2a039Suwe 
308b5ba6eebSuwe 	DPRINTFN(2, ("%s: disable\n", device_xname(sc->sc_dev)));
3090af2a039Suwe 	intc_intr_disable(SH7709_INTEVT2_IRQ3);
3100af2a039Suwe 	callout_stop(&sc->sc_touch_ch);
3110af2a039Suwe }
3120af2a039Suwe 
3130af2a039Suwe 
3140af2a039Suwe static int
j6x0tp_enable_child(struct j6x0tp_softc * sc,int child,int on)3154a747421Suwe j6x0tp_enable_child(struct j6x0tp_softc *sc, int child, int on)
3160af2a039Suwe {
3170af2a039Suwe 	int s = spltty();
3180af2a039Suwe 
3190af2a039Suwe 	if (on) {
3200af2a039Suwe 		if (!sc->sc_enabled)
3210af2a039Suwe 			j6x0tp_enable(sc);
3220af2a039Suwe 		sc->sc_enabled |= child;
3230af2a039Suwe 	} else {
3240af2a039Suwe 		sc->sc_enabled &= ~child;
3250af2a039Suwe 		if (!sc->sc_enabled)
3260af2a039Suwe 			j6x0tp_disable(sc);
3270af2a039Suwe 	}
328e4bd58b0Suwe 
329e4bd58b0Suwe 	splx(s);
330e4bd58b0Suwe 	return (0);
331e4bd58b0Suwe }
332e4bd58b0Suwe 
333e4bd58b0Suwe 
3340af2a039Suwe static int
j6x0tp_wsmouse_enable(void * arg)335b5ba6eebSuwe j6x0tp_wsmouse_enable(void *arg)
336e4bd58b0Suwe {
337b5ba6eebSuwe 	struct j6x0tp_softc *sc = arg;
338e4bd58b0Suwe 
339b5ba6eebSuwe 	DPRINTFN(1, ("%s: wsmouse enable\n", device_xname(sc->sc_dev)));
3404a747421Suwe 	return (j6x0tp_enable_child(sc, J6X0TP_WSMOUSE_ENABLED, 1));
3410af2a039Suwe }
342e4bd58b0Suwe 
343e4bd58b0Suwe 
3440af2a039Suwe static void
j6x0tp_wsmouse_disable(void * arg)345b5ba6eebSuwe j6x0tp_wsmouse_disable(void *arg)
3460af2a039Suwe {
347b5ba6eebSuwe 	struct j6x0tp_softc *sc = arg;
348e4bd58b0Suwe 
349b5ba6eebSuwe 	DPRINTFN(1, ("%s: wsmouse disable\n", device_xname(sc->sc_dev)));
3504a747421Suwe 	j6x0tp_enable_child(sc, J6X0TP_WSMOUSE_ENABLED, 0);
3510af2a039Suwe }
3520af2a039Suwe 
3530af2a039Suwe 
3540af2a039Suwe static int
j6x0tp_wskbd_enable(void * arg,int on)355b5ba6eebSuwe j6x0tp_wskbd_enable(void *arg, int on)
3560af2a039Suwe {
357b5ba6eebSuwe 	struct j6x0tp_softc *sc = arg;
3580af2a039Suwe 
359b5ba6eebSuwe 	DPRINTFN(1, ("%s: wskbd %sable\n", device_xname(sc->sc_dev),
3600af2a039Suwe 		     on ? "en" : "dis"));
3614a747421Suwe 	return (j6x0tp_enable_child(sc, J6X0TP_WSKBD_ENABLED, on));
362e4bd58b0Suwe }
363e4bd58b0Suwe 
364e4bd58b0Suwe 
365e4bd58b0Suwe static int
j6x0tp_intr(void * arg)366b5ba6eebSuwe j6x0tp_intr(void *arg)
367e4bd58b0Suwe {
368b5ba6eebSuwe 	struct j6x0tp_softc *sc = arg;
369e4bd58b0Suwe 
370e4bd58b0Suwe 	uint8_t irr0;
371e4bd58b0Suwe 	uint8_t phdr, touched;
372e4bd58b0Suwe 	unsigned int steady, tremor_timeout;
373e4bd58b0Suwe 
374e4bd58b0Suwe 	irr0 = _reg_read_1(SH7709_IRR0);
375e4bd58b0Suwe 	if ((irr0 & IRR0_IRQ3) == 0) {
376e4bd58b0Suwe #ifdef DIAGNOSTIC
377b5ba6eebSuwe 		printf("%s: irr0 %02x?\n", device_xname(sc->sc_dev), irr0);
378e4bd58b0Suwe #endif
379e4bd58b0Suwe 		return (0);
380e4bd58b0Suwe 	}
381e4bd58b0Suwe 
3820af2a039Suwe 	if (!sc->sc_enabled) {
383aa1315c6Suwe 		DPRINTFN(1, ("%s: intr: !sc_enabled\n",
384b5ba6eebSuwe 			     device_xname(sc->sc_dev)));
3850af2a039Suwe 		intc_intr_disable(SH7709_INTEVT2_IRQ3);
3860af2a039Suwe 		goto served;
3870af2a039Suwe 	}
3880af2a039Suwe 
3890af2a039Suwe 
390e4bd58b0Suwe 	/*
391e4bd58b0Suwe 	 * Number of times the "touched" bit should be read
392e4bd58b0Suwe 	 * consecutively.
393e4bd58b0Suwe 	 */
394e4bd58b0Suwe #	define TREMOR_THRESHOLD 0x300
395e4bd58b0Suwe 
396e4bd58b0Suwe 	steady = 0;
397e4bd58b0Suwe 	tremor_timeout = TREMOR_THRESHOLD * 16;	/* XXX: arbitrary */
398e4bd58b0Suwe 	touched = PHDR_TP_PEN_DOWN;	/* we start with "touched" state */
399e4bd58b0Suwe 
400e4bd58b0Suwe 	do {
401e4bd58b0Suwe 		phdr = _reg_read_1(SH7709_PHDR);
402e4bd58b0Suwe 
403e4bd58b0Suwe 		if ((phdr & PHDR_TP_PEN_DOWN) == touched)
404e4bd58b0Suwe 			++steady;
405e4bd58b0Suwe 		else {
406e4bd58b0Suwe 			steady = 0;
407e4bd58b0Suwe 			touched = phdr & PHDR_TP_PEN_DOWN;
408e4bd58b0Suwe 		}
409e4bd58b0Suwe 
410e4bd58b0Suwe 		if (--tremor_timeout == 0) {
411e4bd58b0Suwe 			DPRINTF(("%s: tremor timeout!\n",
412b5ba6eebSuwe 				 device_xname(sc->sc_dev)));
413e4bd58b0Suwe 			goto served;
414e4bd58b0Suwe 		}
415e4bd58b0Suwe 	} while (steady < TREMOR_THRESHOLD);
416e4bd58b0Suwe 
4170af2a039Suwe 	if (touched) {
418e4bd58b0Suwe 		intc_intr_disable(SH7709_INTEVT2_IRQ3);
419e4bd58b0Suwe 
4200af2a039Suwe 		/*
4210af2a039Suwe 		 * ADC readings are not stable yet, so schedule
4220af2a039Suwe 		 * callout instead of accessing ADC from the interrupt
4230af2a039Suwe 		 * handler only to immediately delay().
4240af2a039Suwe 		 */
425e4bd58b0Suwe 		callout_reset(&sc->sc_touch_ch, hz/32,
4260af2a039Suwe 			      j6x0tp_start_polling, sc);
4270af2a039Suwe 	} else
428b5ba6eebSuwe 		DPRINTFN(1, ("%s: tremor\n", device_xname(sc->sc_dev)));
429e4bd58b0Suwe   served:
4300af2a039Suwe 	/* clear the interrupt (XXX: protect access?) */
431e4bd58b0Suwe 	_reg_write_1(SH7709_IRR0, irr0 & ~IRR0_IRQ3);
432e4bd58b0Suwe 
433e4bd58b0Suwe 	return (1);
434e4bd58b0Suwe }
435e4bd58b0Suwe 
436e4bd58b0Suwe 
4370af2a039Suwe /*
4380af2a039Suwe  * Called from the interrupt handler at spltty() upon first touch.
4390af2a039Suwe  * Decide if we are going to report this touch as a mouse click/drag
4400af2a039Suwe  * or as a key press.
4410af2a039Suwe  */
442e4bd58b0Suwe static void
j6x0tp_start_polling(void * arg)443b5ba6eebSuwe j6x0tp_start_polling(void *arg)
444e4bd58b0Suwe {
445b5ba6eebSuwe 	struct j6x0tp_softc *sc = arg;
4460af2a039Suwe 	uint8_t phdr;
4470af2a039Suwe 	int do_mouse, do_kbd;
4480af2a039Suwe 	int rawx, rawy;
4490af2a039Suwe 	int icon;
4500af2a039Suwe 
4510af2a039Suwe 	phdr = _reg_read_1(SH7709_PHDR);
4520af2a039Suwe 	if ((phdr & PHDR_TP_PEN_DOWN) == 0) {
4530af2a039Suwe 		DPRINTFN(2, ("%s: start: pen is not down\n",
454b5ba6eebSuwe 			     device_xname(sc->sc_dev)));
4550af2a039Suwe 		j6x0tp_stop_polling(sc);
4560af2a039Suwe 	}
4570af2a039Suwe 
4580af2a039Suwe 	j6x0tp_get_raw_xy(&rawx, &rawy);
4590af2a039Suwe 	DPRINTFN(2, ("%s: start: %4d %4d -> ",
460b5ba6eebSuwe 		     device_xname(sc->sc_dev), rawx, rawy));
4610af2a039Suwe 
4620af2a039Suwe 	do_mouse = sc->sc_enabled & J6X0TP_WSMOUSE_ENABLED;
4630af2a039Suwe #ifdef J6X0TP_WSMOUSE_EXCLUSIVE
4640af2a039Suwe 	if (do_mouse)
4650af2a039Suwe 		do_kbd = 0;
4660af2a039Suwe 	else
4670af2a039Suwe #endif
4680af2a039Suwe 		do_kbd = sc->sc_enabled & J6X0TP_WSKBD_ENABLED;
4690af2a039Suwe 
4700af2a039Suwe 	icon = 0;
4710af2a039Suwe 	if (do_kbd)
4720af2a039Suwe 		icon = j6x0tp_get_hard_icon(rawx, rawy);
4730af2a039Suwe 
4740af2a039Suwe 	if (icon != 0) {
4750af2a039Suwe 		DPRINTFN(2, ("icon %d\n", icon));
4760af2a039Suwe 		sc->sc_hard_icon = icon;
4770af2a039Suwe 		wskbd_input(sc->sc_wskbddev, WSCONS_EVENT_KEY_DOWN, icon);
4780af2a039Suwe 		callout_reset(&sc->sc_touch_ch, hz/32,
4790af2a039Suwe 			      j6x0tp_callout_wskbd, sc);
4800af2a039Suwe 	} else if (do_mouse) {
4810af2a039Suwe 		DPRINTFN(2, ("mouse\n"));
4820af2a039Suwe 		j6x0tp_wsmouse_input(sc, rawx, rawy);
4830af2a039Suwe 		callout_reset(&sc->sc_touch_ch, hz/32,
4840af2a039Suwe 			      j6x0tp_callout_wsmouse, sc);
4850af2a039Suwe 	} else {
4860af2a039Suwe 		DPRINTFN(2, ("ignore\n"));
4870af2a039Suwe 		j6x0tp_stop_polling(sc);
4880af2a039Suwe 	}
4890af2a039Suwe }
4900af2a039Suwe 
4910af2a039Suwe 
4920af2a039Suwe /*
4930af2a039Suwe  * Re-enable touch panel interrupt.
4940af2a039Suwe  * Called as spltty() when polling code detects pen-up.
4950af2a039Suwe  */
4960af2a039Suwe static void
j6x0tp_stop_polling(struct j6x0tp_softc * sc)4970af2a039Suwe j6x0tp_stop_polling(struct j6x0tp_softc *sc)
4980af2a039Suwe {
499e4bd58b0Suwe 	uint8_t irr0;
5000af2a039Suwe 
501b5ba6eebSuwe 	DPRINTFN(2, ("%s: stop\n", device_xname(sc->sc_dev)));
5020af2a039Suwe 
5030af2a039Suwe 	/* clear pending interrupt signal before re-enabling the interrupt */
5040af2a039Suwe 	irr0 = _reg_read_1(SH7709_IRR0);
5050af2a039Suwe 	if ((irr0 & IRR0_IRQ3) != 0)
5060af2a039Suwe 		_reg_write_1(SH7709_IRR0, irr0 & ~IRR0_IRQ3);
5070af2a039Suwe 
5080af2a039Suwe 	intc_intr_enable(SH7709_INTEVT2_IRQ3);
5090af2a039Suwe }
5100af2a039Suwe 
5110af2a039Suwe 
5120af2a039Suwe /*
5130af2a039Suwe  * We are reporting this touch as a keyboard event.
5140af2a039Suwe  * Poll touch screen waiting for pen-up.
5150af2a039Suwe  */
5160af2a039Suwe static void
j6x0tp_callout_wskbd(void * arg)517b5ba6eebSuwe j6x0tp_callout_wskbd(void *arg)
5180af2a039Suwe {
519b5ba6eebSuwe 	struct j6x0tp_softc *sc = arg;
5200af2a039Suwe 	uint8_t phdr;
521e4bd58b0Suwe 	int s;
522e4bd58b0Suwe 
523e4bd58b0Suwe 	s = spltty();
524e4bd58b0Suwe 
525e4bd58b0Suwe 	if (!sc->sc_enabled) {
5260af2a039Suwe 		DPRINTFN(1, ("%s: wskbd callout: !sc_enabled\n",
527b5ba6eebSuwe 			     device_xname(sc->sc_dev)));
528e4bd58b0Suwe 		splx(s);
529e4bd58b0Suwe 		return;
530e4bd58b0Suwe 	}
531e4bd58b0Suwe 
532e4bd58b0Suwe 	phdr = _reg_read_1(SH7709_PHDR);
5330af2a039Suwe 	if ((phdr & PHDR_TP_PEN_DOWN) != 0) {
5340af2a039Suwe 		/*
5350af2a039Suwe 		 * Pen is still down, continue polling.  Wskbd's
5360af2a039Suwe 		 * auto-repeat takes care of repeating the key.
5370af2a039Suwe 		 */
5380af2a039Suwe 		callout_schedule(&sc->sc_touch_ch, hz/32);
5390af2a039Suwe 	} else {
5400af2a039Suwe 		wskbd_input(sc->sc_wskbddev,
5410af2a039Suwe 			    WSCONS_EVENT_KEY_UP, sc->sc_hard_icon);
5420af2a039Suwe 		j6x0tp_stop_polling(sc);
5430af2a039Suwe 	}
5440af2a039Suwe 	splx(s);
5450af2a039Suwe }
546e4bd58b0Suwe 
547e4bd58b0Suwe 
5480af2a039Suwe /*
5490af2a039Suwe  * We are reporting this touch as a mouse click/drag.
5500af2a039Suwe  */
5510af2a039Suwe static void
j6x0tp_callout_wsmouse(void * arg)552b5ba6eebSuwe j6x0tp_callout_wsmouse(void *arg)
5530af2a039Suwe {
554b5ba6eebSuwe 	struct j6x0tp_softc *sc = arg;
5550af2a039Suwe 	uint8_t phdr;
5560af2a039Suwe 	int rawx, rawy;
5570af2a039Suwe 	int s;
558e4bd58b0Suwe 
5590af2a039Suwe 	s = spltty();
5600af2a039Suwe 
5610af2a039Suwe 	if (!sc->sc_enabled) {
5620af2a039Suwe 		DPRINTFN(1, ("%s: wsmouse callout: !sc_enabled\n",
563b5ba6eebSuwe 			     device_xname(sc->sc_dev)));
564e4bd58b0Suwe 		splx(s);
565e4bd58b0Suwe 		return;
566e4bd58b0Suwe 	}
567e4bd58b0Suwe 
5680af2a039Suwe 	phdr = _reg_read_1(SH7709_PHDR);
5690af2a039Suwe 	if ((phdr & PHDR_TP_PEN_DOWN) != 0) {
5700af2a039Suwe 		j6x0tp_get_raw_xy(&rawx, &rawy);
5710af2a039Suwe 		j6x0tp_wsmouse_input(sc, rawx, rawy); /* mouse dragged */
5720af2a039Suwe 		callout_schedule(&sc->sc_touch_ch, hz/32);
5730af2a039Suwe 	} else {
57457c0199dSplunky 		wsmouse_input(sc->sc_wsmousedev, 0, 0, 0, 0, 0, /* button up */
5750af2a039Suwe 			      WSMOUSE_INPUT_DELTA);
5760af2a039Suwe 		j6x0tp_stop_polling(sc);
5770af2a039Suwe 	}
5780af2a039Suwe 	splx(s);
5790af2a039Suwe }
5800af2a039Suwe 
5810af2a039Suwe 
5820af2a039Suwe /*
5830af2a039Suwe  * Report mouse click/drag.
5840af2a039Suwe  */
5850af2a039Suwe static void
j6x0tp_wsmouse_input(struct j6x0tp_softc * sc,int rawx,int rawy)5860af2a039Suwe j6x0tp_wsmouse_input(struct j6x0tp_softc *sc, int rawx, int rawy)
5870af2a039Suwe {
5880af2a039Suwe 	int x, y;
5890af2a039Suwe 
5900af2a039Suwe 	tpcalib_trans(&sc->sc_tpcalib, rawx, rawy, &x, &y);
5910af2a039Suwe 
5920af2a039Suwe 	DPRINTFN(3, ("%s: %4d %4d -> %3d %3d\n",
593b5ba6eebSuwe 		     device_xname(sc->sc_dev), rawx, rawy, x, y));
5940af2a039Suwe 
5950af2a039Suwe 	wsmouse_input(sc->sc_wsmousedev,
5960af2a039Suwe 			1,	/* button */
59757c0199dSplunky 			x, y, 0, 0,
5980af2a039Suwe 			WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
5990af2a039Suwe }
6000af2a039Suwe 
6010af2a039Suwe 
6020af2a039Suwe /*
6030af2a039Suwe  * Read raw X/Y coordinates from the ADC.
6040af2a039Suwe  * XXX: protect accesses to SCPDR?
6050af2a039Suwe  */
6060af2a039Suwe static void
j6x0tp_get_raw_xy(int * rawxp,int * rawyp)6070af2a039Suwe j6x0tp_get_raw_xy(int *rawxp, int *rawyp)
6080af2a039Suwe {
6090af2a039Suwe 	uint8_t scpdr;
610e4bd58b0Suwe 
611e4bd58b0Suwe 	/* Y axis */
612e4bd58b0Suwe 	scpdr = _reg_read_1(SH7709_SCPDR);
613e4bd58b0Suwe 	scpdr |=  SCPDR_TP_SCAN_ENABLE;
614e4bd58b0Suwe 	scpdr &= ~SCPDR_TP_SCAN_Y; /* pull low to scan */
615e4bd58b0Suwe 	_reg_write_1(SH7709_SCPDR, scpdr);
616e4bd58b0Suwe 	delay(10);
6170af2a039Suwe 
6180af2a039Suwe 	*rawyp = adc_sample_channel(ADC_CHANNEL_TP_Y);
619e4bd58b0Suwe 
620e4bd58b0Suwe 	/* X axis */
621e4bd58b0Suwe 	scpdr = _reg_read_1(SH7709_SCPDR);
622e4bd58b0Suwe 	scpdr |=  SCPDR_TP_SCAN_Y;
623e4bd58b0Suwe 	scpdr &= ~SCPDR_TP_SCAN_X; /* pull low to scan */
624e4bd58b0Suwe 	_reg_write_1(SH7709_SCPDR, scpdr);
625e4bd58b0Suwe 	delay(10);
6260af2a039Suwe 
6270af2a039Suwe 	*rawxp = adc_sample_channel(ADC_CHANNEL_TP_X);
628e4bd58b0Suwe 
629e4bd58b0Suwe 	/* restore SCPDR */
630e4bd58b0Suwe 	scpdr = _reg_read_1(SH7709_SCPDR);
631e4bd58b0Suwe 	scpdr |=  SCPDR_TP_SCAN_X;
632e4bd58b0Suwe 	scpdr &= ~SCPDR_TP_SCAN_ENABLE;
633e4bd58b0Suwe 	_reg_write_1(SH7709_SCPDR, scpdr);
6340af2a039Suwe }
635e4bd58b0Suwe 
636e4bd58b0Suwe 
6370af2a039Suwe /*
6380af2a039Suwe  * Check if the (rawx, rawy) is inside one of the 4 hard icons.
6390af2a039Suwe  * Return the icon number 1..4, or 0 if not inside an icon.
6400af2a039Suwe  */
6410af2a039Suwe static int
j6x0tp_get_hard_icon(int rawx,int rawy)6420af2a039Suwe j6x0tp_get_hard_icon(int rawx, int rawy)
6430af2a039Suwe {
6440af2a039Suwe 	if (rawx <= J6X0TP_FB_RIGHT)
6450af2a039Suwe 		return (0);
646e4bd58b0Suwe 
6470af2a039Suwe 	if (rawy < J6X0TP_HARD_ICON_MAX_Y(1))
6480af2a039Suwe 		return (1);
6490af2a039Suwe 	else if (rawy < J6X0TP_HARD_ICON_MAX_Y(2))
6500af2a039Suwe 		return (2);
6510af2a039Suwe 	else if (rawy < J6X0TP_HARD_ICON_MAX_Y(3))
6520af2a039Suwe 		return (3);
6530af2a039Suwe 	else
6540af2a039Suwe 		return (4);
655e4bd58b0Suwe }
656e4bd58b0Suwe 
657e4bd58b0Suwe 
658e4bd58b0Suwe static int
j6x0tp_wsmouse_ioctl(void * arg,u_long cmd,void * data,int flag,lwp_t * l)659b5ba6eebSuwe j6x0tp_wsmouse_ioctl(void *arg, u_long cmd, void *data, int flag, lwp_t *l)
660e4bd58b0Suwe {
661b5ba6eebSuwe 	struct j6x0tp_softc *sc = arg;
662e4bd58b0Suwe 
66395e1ffb1Schristos 	return hpc_tpanel_ioctl(&sc->sc_tpcalib, cmd, data, flag, l);
664e4bd58b0Suwe }
6650af2a039Suwe 
6660af2a039Suwe 
6670af2a039Suwe static int
j6x0tp_wskbd_ioctl(void * arg,u_long cmd,void * data,int flag,lwp_t * l)668b5ba6eebSuwe j6x0tp_wskbd_ioctl(void *arg, u_long cmd, void *data, int flag, lwp_t *l)
6690af2a039Suwe {
670b5ba6eebSuwe 	/* struct j6x0tp_softc *sc = arg; */
6710af2a039Suwe 
6720af2a039Suwe 	switch (cmd) {
6730af2a039Suwe 	case WSKBDIO_GTYPE:
6740af2a039Suwe 		*(int *)data = WSKBD_TYPE_HPC_BTN; /* may be use new type? */
6750af2a039Suwe 		return (0);
6760af2a039Suwe 
6770af2a039Suwe 	case WSKBDIO_GETLEDS:
6780af2a039Suwe 		*(int *)data = 0;
6790af2a039Suwe 		return (0);
6800af2a039Suwe 
6810af2a039Suwe 	default:
6820af2a039Suwe 		return (EPASSTHROUGH);
6830af2a039Suwe 	}
6840af2a039Suwe }
6850af2a039Suwe 
6860af2a039Suwe 
6870af2a039Suwe static void
j6x0tp_wskbd_set_leds(void * arg,int leds)688b5ba6eebSuwe j6x0tp_wskbd_set_leds(void *arg, int leds)
6890af2a039Suwe {
6900af2a039Suwe 
6910af2a039Suwe 	/* nothing to do*/
6920af2a039Suwe 	return;
6930af2a039Suwe }
694