Subject: merged touch-panel driver
To: None <uwe@ptc.spbu.ru>
From: KIYOHARA Takashi <kiyohara@kk.iij4u.or.jp>
List: port-hpcsh
Date: 07/04/2005 02:40:10
----Next_Part(Mon_Jul__4_02:40:10_2005_142)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Hi, uwe.


I merged drivers of the touch-panel of j6x0 and psh3. 


BTW,
PERSONA is using DADR0 by play, and using AD4 by record.
I have not written the driver of audio yet. ;-<  Do you write driver?
--
kiyohara


----Next_Part(Mon_Jul__4_02:40:10_2005_142)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="pfctp.c"

/*	$NetBSD$ */
/*
 * Copyright (c) 2005 KIYOHARA Takashi
 * All rights reserved.
 */
/*
 * Copyright (c) 2003 Valeriy E. Ushakov
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD$");

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/callout.h>
#ifdef GPROF
#include <sys/gmon.h>
#endif

#include "opt_pfctp.h"

#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsmousevar.h>
#include <dev/wscons/wskbdvar.h>
#include <dev/wscons/wsksymvar.h>
#include <dev/wscons/wsksymdef.h>
#include <dev/hpc/hpctpanelvar.h>

#include <machine/platid.h>
#include <machine/platid_mask.h>

#include <machine/intr.h>

#include <sh3/exception.h>
#include <sh3/intcreg.h>
#include <sh3/pfcreg.h>
#include <sh3/adcreg.h>

#include <sh3/dev/adcvar.h>


#define PFCTP_DEBUG
#if 0 /* XXX: disabled in favor of local version that uses printf_nolog */
#define DPRINTF_ENABLE
#define DPRINTF_DEBUG	pfctp_debug
#define DPRINTF_LEVEL	0
#include <machine/debug.h>
#else
#ifdef PFCTP_DEBUG
volatile int pfctp_debug = 0;
#define DPRINTF_PRINTF		printf_nolog
#define DPRINTF(arg)		if (pfctp_debug) DPRINTF_PRINTF arg
#define DPRINTFN(n, arg)	if (pfctp_debug > (n)) DPRINTF_PRINTF arg
#else
#define DPRINTF(arg)		((void)0)
#define DPRINTFN(n, arg)	((void)0)
#endif
#endif


/*
 * PFC bits pertinent to Jornada 6x0 and PERSONA SH3 machines touchpanel
 */
#define PFCTP_J6X0_PEN_DOWN	0x08

#define PFCTP_J6X0_SCAN_ENABLE	0x20
#define PFCTP_J6X0_SCAN_Y	0x02
#define PFCTP_J6X0_SCAN_X	0x01

#define PFCTP_PSH3_PEN_UP	0x40

#define PFCTP_PSH3_SCAN_ENABLE	0x20
#define PFCTP_PSH3_SCAN_DISABLE	0x01
#define PFCTP_PSH3_SCAN_X	0x06
#define PFCTP_PSH3_SCAN_Y	0x09

/*
 * A/D converter channels to get x/y from
 */
#define PFCTP_J6X0_ADC_CHANNEL_TP_Y	1
#define PFCTP_J6X0_ADC_CHANNEL_TP_X	2

#define PFCTP_PSH3_ADC_CHANNEL_TP_Y	0
#define PFCTP_PSH3_ADC_CHANNEL_TP_X	1

/*
 * Default (read: my device :) raw X/Y values for framebuffer edges.
 * XXX: defopt these?
 */
#define PFCTP_J6X0_FB_LEFT	 38
#define PFCTP_J6X0_FB_RIGHT	950
#define PFCTP_J6X0_FB_TOP	 80
#define PFCTP_J6X0_FB_BOTTOM	900

#define PFCTP_PSH3_FB_RIGHT	 56
#define PFCTP_PSH3_FB_LEFT	969
#define PFCTP_PSH3_FB_TOP	848
#define PFCTP_PSH3_FB_BOTTOM	121

/*
 * Bottom of the n'th hard icon (n = 1..4)
 */
#define PFCTP_J6X0_HARD_ICON_MAX_Y(n) \
	(PFCTP_J6X0_FB_TOP + \
	((PFCTP_J6X0_FB_BOTTOM - PFCTP_J6X0_FB_TOP) / 4) * (n))


struct pfctp_softc {
	struct device sc_dev;

#define PFCTP_WSMOUSE_ENABLED	0x01
#define PFCTP_WSKBD_ENABLED	0x02
	int sc_enabled;

	int sc_hard_icon;

	struct callout sc_touch_ch;

	struct device *sc_wsmousedev;
	struct device *sc_wskbddev;

	struct tpcalib_softc sc_tpcalib; /* calibration info for wsmouse */

	int sc_irq;
	int sc_intevt;
	struct wsmouse_calibcoords *sc_default_calib;
	struct wskbd_mapdata *sc_wskbd_keymap;

	int (*sc_get_stat)(void);
	int (*sc_get_touched)(struct pfctp_softc *, int *);
	void (*sc_get_raw_xy)(int *, int *);
	int (*sc_get_hard_icon)(int, int);
};


/* config machinery */
static int	pfctp_match(struct device *, struct cfdata *, void *);
static void	pfctp_attach(struct device *, struct device *, void *);
static int	pfctp_wsmouse_submatch(struct device *, struct cfdata *,
				       void *);
static int	pfctp_wskbd_submatch(struct device *, struct cfdata *, void *);

/* wsmouse accessops */
static int	pfctp_wsmouse_enable(void *);
static int	pfctp_wsmouse_ioctl(void *, u_long, caddr_t, int,
				    struct proc *);
static void	pfctp_wsmouse_disable(void *);

/* wskbd accessops */
static int	pfctp_wskbd_enable(void *, int);
static void	pfctp_wskbd_set_leds(void *, int);
static int	pfctp_wskbd_ioctl(void *, u_long, caddr_t, int, struct proc *);

/* internal driver routines */
static void	pfctp_enable(struct pfctp_softc *);
static void	pfctp_disable(struct pfctp_softc *);
static int	pfctp_set_enable(struct pfctp_softc *, int, int);
static int	pfctp_intr(void *);
static void	pfctp_start_polling(void *);
static void	pfctp_stop_polling(struct pfctp_softc *);
static void	pfctp_callout_wsmouse(void *);
static void	pfctp_callout_wskbd(void *);
static void	pfctp_wsmouse_input(struct pfctp_softc *, int, int);

/* platid independent routines */
static int	pfctp_j6x0_get_stat(void);
static int	pfctp_j6x0_get_touched(struct pfctp_softc *, int *);
static void	pfctp_j6x0_get_raw_xy(int *, int *);
static int	pfctp_j6x0_get_hard_icon(int, int);

static int	pfctp_psh3_get_stat(void);
static int	pfctp_psh3_get_touched(struct pfctp_softc *, int *);
static void	pfctp_psh3_get_raw_xy(int *, int *);


static const struct wsmouse_accessops pfctp_accessops = {
	pfctp_wsmouse_enable,
	pfctp_wsmouse_ioctl,
	pfctp_wsmouse_disable
};

static const struct wskbd_accessops pfctp_wskbd_accessops = {
	pfctp_wskbd_enable,
	pfctp_wskbd_set_leds,
	pfctp_wskbd_ioctl
};


static const struct wsmouse_calibcoords pfctp_j6x0_default_calib = {
	0, 0, 639, 239,
	4,
	{{ PFCTP_J6X0_FB_LEFT,  PFCTP_J6X0_FB_TOP,      0,   0 },
	 { PFCTP_J6X0_FB_RIGHT, PFCTP_J6X0_FB_TOP,    639,   0 },
	 { PFCTP_J6X0_FB_LEFT,  PFCTP_J6X0_FB_BOTTOM,   0, 239 },
	 { PFCTP_J6X0_FB_RIGHT, PFCTP_J6X0_FB_BOTTOM, 639, 239 }}
};

static const struct wsmouse_calibcoords pfctp_psh3_default_calib = {
	0, 0, 639, 239,
	4,
	{{ PFCTP_PSH3_FB_LEFT,  PFCTP_PSH3_FB_TOP,      0,   0 },
	 { PFCTP_PSH3_FB_RIGHT, PFCTP_PSH3_FB_TOP,    639,   0 },
	 { PFCTP_PSH3_FB_LEFT,  PFCTP_PSH3_FB_BOTTOM,   0, 239 },
	 { PFCTP_PSH3_FB_RIGHT, PFCTP_PSH3_FB_BOTTOM, 639, 239 }}
};


#ifndef PFCTP_J6X0_SETTINGS_ICON_KEYSYM
#define PFCTP_J6X0_SETTINGS_ICON_KEYSYM	KS_Home
#endif
#ifndef PFCTP_J6X0_PGUP_ICON_KEYSYM
#define PFCTP_J6X0_PGUP_ICON_KEYSYM	KS_Prior
#endif
#ifndef PFCTP_J6X0_PGDN_ICON_KEYSYM
#define PFCTP_J6X0_PGDN_ICON_KEYSYM	KS_Next
#endif
#ifndef PFCTP_J6X0_SWITCH_ICON_KEYSYM
#define PFCTP_J6X0_SWITCH_ICON_KEYSYM	KS_End
#endif

static const keysym_t pfctp_j6x0_wskbd_keydesc[] = {
	KS_KEYCODE(1), PFCTP_J6X0_SETTINGS_ICON_KEYSYM,
	KS_KEYCODE(2), PFCTP_J6X0_PGUP_ICON_KEYSYM,
	KS_KEYCODE(3), PFCTP_J6X0_PGDN_ICON_KEYSYM,
	KS_KEYCODE(4), PFCTP_J6X0_SWITCH_ICON_KEYSYM
};

static const struct wscons_keydesc pfctp_j6x0_wskbd_keydesctab[] = {
	{ KB_US, 0,
	  sizeof(pfctp_j6x0_wskbd_keydesc)/sizeof(keysym_t),
	  pfctp_j6x0_wskbd_keydesc
	},
	{0, 0, 0, 0}
};

static const struct wskbd_mapdata pfctp_j6x0_wskbd_keymapdata = {
        pfctp_j6x0_wskbd_keydesctab, KB_US
};

/* pfctp any values table. this values is platfrom specific. */
static const struct {
	platid_mask_t *platform;
	int irq;
	int intevt;
	struct wsmouse_calibcoords *default_calib;
	struct wskbd_mapdata *wskbd_keymap;
	int (*get_stat)(void);
	int (*get_touched)(struct pfctp_softc *, int *);
	void (*get_raw_xy)(int *, int *);
	int (*get_hard_icon)(int, int);
} pfctp_values_table[] = {
	{ &platid_mask_MACH_HP_JORNADA_6XX,
	    IRR0_IRQ3,			SH7709_INTEVT2_IRQ3,
	    __UNCONST(&pfctp_j6x0_default_calib),
	    __UNCONST(&pfctp_j6x0_wskbd_keymapdata),
	    pfctp_j6x0_get_stat,	pfctp_j6x0_get_touched,
	    pfctp_j6x0_get_raw_xy,	pfctp_j6x0_get_hard_icon	},
	{ &platid_mask_MACH_HITACHI_PERSONA,
	    IRR0_IRQ2,			SH7709_INTEVT2_IRQ2,
	    __UNCONST(&pfctp_psh3_default_calib),
	    NULL,
	    pfctp_psh3_get_stat,	pfctp_psh3_get_touched, 
	    pfctp_psh3_get_raw_xy,	NULL				}
};


CFATTACH_DECL(pfctp, sizeof(struct pfctp_softc),
    pfctp_match, pfctp_attach, NULL, NULL);


static int
pfctp_match(struct device *parent, struct cfdata *cf, void *aux)
{
        int i, n = sizeof(pfctp_values_table) / sizeof(pfctp_values_table[0]);

	/*
	 * XXX: does platid_mask_MACH_HP_LX matches _JORNADA_6XX too?
	 * Is 620 wired similarly?
	 */
	for (i = 0; i < n; i++)
		if (platid_match(&platid, pfctp_values_table[i].platform))
			break;
	if (i == n)
		return (0);

	if (strcmp(cf->cf_name, "pfctp") != 0)
		return (0);

	return (1);
}


/*
 * Attach the touch panel driver and its ws* children.
 *
 * Note that we have to use submatch to distinguish between children
 * because ws{kbd,mouse}_match match unconditionally.
 */
static void
pfctp_attach(struct device *parent, struct device *self, void *aux)
{
	struct pfctp_softc *sc = (struct pfctp_softc *)self;
	struct wsmousedev_attach_args wsma;
	struct wskbddev_attach_args wska;
        int i, n = sizeof(pfctp_values_table) / sizeof(pfctp_values_table[0]);

	for (i = 0; i < n; i++)
		if (platid_match(&platid, pfctp_values_table[i].platform))
			break;

	printf("\n");

	sc->sc_enabled = 0;
	sc->sc_hard_icon = 0;
	sc->sc_irq = pfctp_values_table[i].irq;
	sc->sc_intevt = pfctp_values_table[i].intevt;
	sc->sc_default_calib = pfctp_values_table[i].default_calib;
	sc->sc_wskbd_keymap = pfctp_values_table[i].wskbd_keymap;
	sc->sc_get_stat = pfctp_values_table[i].get_stat;
	sc->sc_get_touched = pfctp_values_table[i].get_touched;
	sc->sc_get_raw_xy = pfctp_values_table[i].get_raw_xy;
	sc->sc_get_hard_icon = pfctp_values_table[i].get_hard_icon;

	/* touch-panel as a pointing device */
	wsma.accessops = &pfctp_accessops;
	wsma.accesscookie = sc;

	sc->sc_wsmousedev = config_found_sm(
	    self, &wsma, wsmousedevprint, pfctp_wsmouse_submatch);
	if (sc->sc_wsmousedev == NULL)
		return;

	if (sc->sc_wskbd_keymap != NULL) {
		/* on-screen "hard icons" as a keyboard device */
		wska.console = 0;
		wska.keymap = sc->sc_wskbd_keymap;
		wska.accessops = &pfctp_wskbd_accessops;
		wska.accesscookie = sc;

		sc->sc_wskbddev = config_found_sm(
		    self, &wska, wskbddevprint, pfctp_wskbd_submatch);
	}

	/* init calibration, set default parameters */
	tpcalib_init(&sc->sc_tpcalib);
	tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS,
	    (caddr_t)__UNCONST(sc->sc_default_calib), 0, 0);

	/* used when in polling mode */
	callout_init(&sc->sc_touch_ch);

	/* establish interrupt handler, but disable until opened */
	intc_intr_establish(
	    sc->sc_intevt, IST_EDGE, IPL_TTY, pfctp_intr, sc);
	intc_intr_disable(sc->sc_intevt);
}


static int
pfctp_wsmouse_submatch(struct device *parent, struct cfdata *cf, void *aux)
{

	return (!strcmp(cf->cf_name, "wsmouse"));
}


static int
pfctp_wskbd_submatch(struct device *parent, struct cfdata *cf, void *aux)
{

	return (!strcmp(cf->cf_name, "wskbd"));
}


/*
 * Enable touch panel:  we start in interrupt mode.
 * Must be called at spltty().
 */
static void
pfctp_enable(struct pfctp_softc *sc)
{

	DPRINTFN(2, ("%s: enable\n", sc->sc_dev.dv_xname));
	intc_intr_enable(sc->sc_intevt);
}


/*
 * Disable touch panel: disable interrupt, cancel pending callout.
 * Must be called at spltty().
 */
static void
pfctp_disable(struct pfctp_softc *sc)
{

	DPRINTFN(2, ("%s: disable\n", sc->sc_dev.dv_xname));
	intc_intr_disable(sc->sc_intevt);
	callout_stop(&sc->sc_touch_ch);
}


static int
pfctp_set_enable(struct pfctp_softc *sc, int on, int child)
{
	int s = spltty();

	if (on) {
		if (!sc->sc_enabled)
			pfctp_enable(sc);
		sc->sc_enabled |= child;
	} else {
		sc->sc_enabled &= ~child;
		if (!sc->sc_enabled)
			pfctp_disable(sc);
	}

	splx(s);
	return (0);
}


static int
pfctp_wsmouse_enable(void *self)
{
	struct pfctp_softc *sc = (struct pfctp_softc *)self;

	DPRINTFN(1, ("%s: wsmouse enable\n", sc->sc_dev.dv_xname));
	return (pfctp_set_enable(sc, 1, PFCTP_WSMOUSE_ENABLED));
}


static void
pfctp_wsmouse_disable(void *self)
{
	struct pfctp_softc *sc = (struct pfctp_softc *)self;

	DPRINTFN(1, ("%s: wsmouse disable\n", sc->sc_dev.dv_xname));
	pfctp_set_enable(sc, 0, PFCTP_WSMOUSE_ENABLED);
}


static int
pfctp_wskbd_enable(void *self, int on)
{
	struct pfctp_softc *sc = (struct pfctp_softc *)self;

	DPRINTFN(1, ("%s: wskbd %sable\n",
	    sc->sc_dev.dv_xname, on ? "en" : "dis"));
	return (pfctp_set_enable(sc, on, PFCTP_WSKBD_ENABLED));
}


static int
pfctp_intr(void *self)
{
	struct pfctp_softc *sc = (struct pfctp_softc *)self;
	int touched, sflag = 0;
	uint8_t irr0;

	irr0 = _reg_read_1(SH7709_IRR0);
	if ((irr0 & sc->sc_irq) == 0) {
#ifdef DIAGNOSTIC
		printf("%s: irr0 %02x?\n", sc->sc_dev.dv_xname, irr0);
#endif
		return (0);
	}

	if (!sc->sc_enabled) {
		DPRINTFN(1, ("%s: intr: !sc_enabled\n", sc->sc_dev.dv_xname));
		intc_intr_disable(sc->sc_intevt);
		goto served;
	}

	touched = sc->sc_get_touched(sc, &sflag);
	if (sflag)
		goto served;

	if (touched) {
		intc_intr_disable(sc->sc_intevt);

		/*
		 * ADC readings are not stable yet, so schedule
		 * callout instead of accessing ADC from the interrupt
		 * handler only to immediately delay().
		 */
		callout_reset(&sc->sc_touch_ch, hz/32, pfctp_start_polling, sc);
	} else
		DPRINTFN(1, ("%s: tremor\n", sc->sc_dev.dv_xname));
served:
	/* clear the interrupt (XXX: protect access?) */
	_reg_write_1(SH7709_IRR0, irr0 & ~sc->sc_irq);

	return (1);
}


/*
 * Called from the interrupt handler at spltty() upon first touch.
 * Decide if we are going to report this touch as a mouse click/drag
 * or as a key press.
 */
static void
pfctp_start_polling(void *self)
{
	struct pfctp_softc *sc = (struct pfctp_softc *)self;
	int do_mouse, do_kbd;
	int rawx, rawy;
	int icon = 0;

	if (!sc->sc_get_stat()) {
		DPRINTFN(2, ("%s: start: pen is not down\n",
		    sc->sc_dev.dv_xname));
		pfctp_stop_polling(sc);
	}

	sc->sc_get_raw_xy(&rawx, &rawy);
	DPRINTFN(2, ("%s: start: %4d %4d -> ",
	    sc->sc_dev.dv_xname, rawx, rawy));

	do_mouse = sc->sc_enabled & PFCTP_WSMOUSE_ENABLED;
	if (sc->sc_wskbd_keymap == NULL) {
#ifdef PFCTP_WSMOUSE_EXCLUSIVE
		if (do_mouse)
			do_kbd = 0;
		else
#endif
			do_kbd = sc->sc_enabled & PFCTP_WSKBD_ENABLED;

		if (do_kbd)
			icon = sc->sc_get_hard_icon(rawx, rawy);
	}

	if (icon != 0) {
		DPRINTFN(2, ("icon %d\n", icon));
		sc->sc_hard_icon = icon;
		wskbd_input(sc->sc_wskbddev, WSCONS_EVENT_KEY_DOWN, icon);
		callout_reset(&sc->sc_touch_ch, hz/32, pfctp_callout_wskbd, sc);
	} else if (do_mouse) {
		DPRINTFN(2, ("mouse\n"));
		pfctp_wsmouse_input(sc, rawx, rawy);
		callout_reset(&sc->sc_touch_ch,
		    hz/32, pfctp_callout_wsmouse, sc);
	} else {
		DPRINTFN(2, ("ignore\n"));
		pfctp_stop_polling(sc);
	}
}


/*
 * Re-enable touch panel interrupt.
 * Called at spltty() when polling code detects pen-up.
 */
static void
pfctp_stop_polling(struct pfctp_softc *sc)
{
	uint8_t irr0;

	DPRINTFN(2, ("%s: stop\n", sc->sc_dev.dv_xname));

	/* clear pending interrupt signal before re-enabling the interrupt */
	irr0 = _reg_read_1(SH7709_IRR0);
	if ((irr0 & sc->sc_irq) != 0)
		_reg_write_1(SH7709_IRR0, irr0 & ~sc->sc_irq);

	intc_intr_enable(sc->sc_intevt);
}


/*
 * We are reporting this touch as a keyboard event.
 * Poll touch screen waiting for pen-up.
 */
static void
pfctp_callout_wskbd(void *self)
{
	struct pfctp_softc *sc = (struct pfctp_softc *)self;
	int s;

	s = spltty();

	if (!sc->sc_enabled) {
		DPRINTFN(1, ("%s: wskbd callout: !sc_enabled\n",
		    sc->sc_dev.dv_xname));
		splx(s);
		return;
	}

	if (sc->sc_get_stat()) {
		/*
		 * Pen is still down, continue polling.  Wskbd's
		 * auto-repeat takes care of repeating the key.
		 */
		callout_schedule(&sc->sc_touch_ch, hz/32);
	} else {
		wskbd_input(sc->sc_wskbddev,
		    WSCONS_EVENT_KEY_UP, sc->sc_hard_icon);
		pfctp_stop_polling(sc);
	}
	splx(s);
}


/*
 * We are reporting this touch as a mouse click/drag.
 */
static void
pfctp_callout_wsmouse(void *self)
{
	struct pfctp_softc *sc = (struct pfctp_softc *)self;
	int rawx, rawy;
	int s;

	s = spltty();

	if (!sc->sc_enabled) {
		DPRINTFN(1, ("%s: wsmouse callout: !sc_enabled\n",
		    sc->sc_dev.dv_xname));
		splx(s);
		return;
	}

	if (sc->sc_get_stat()) {
		sc->sc_get_raw_xy(&rawx, &rawy);
		pfctp_wsmouse_input(sc, rawx, rawy); /* mouse dragged */
		callout_schedule(&sc->sc_touch_ch, hz/32);
	} else {
		wsmouse_input(/* button up */
		    sc->sc_wsmousedev, 0, 0, 0, 0, WSMOUSE_INPUT_DELTA);
		pfctp_stop_polling(sc);
	}
	splx(s);
}


/*
 * Report mouse click/drag.
 */
static void
pfctp_wsmouse_input(struct pfctp_softc *sc, int rawx, int rawy)
{
	int x, y;

	tpcalib_trans(&sc->sc_tpcalib, rawx, rawy, &x, &y);
		
	DPRINTFN(3, ("%s: %4d %4d -> %3d %3d\n",
	    sc->sc_dev.dv_xname, rawx, rawy, x, y));

	wsmouse_input(sc->sc_wsmousedev,
		      1,	/* button */
		      x, y,
		      0,	/* flags */
		      WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
}


static int
pfctp_wsmouse_ioctl(void *self, u_long cmd, caddr_t data, int flag,
		     struct proc *p)
{
	struct pfctp_softc *sc = (struct pfctp_softc *)self;

	return hpc_tpanel_ioctl(&sc->sc_tpcalib, cmd, data, flag, p);
}


static int
pfctp_wskbd_ioctl(void *self, u_long cmd, caddr_t data, int flag,
		     struct proc *p)
{
	/* struct pfcp_softc *sc = (struct pfctp_softc *)self; */

	switch (cmd) {
	case WSKBDIO_GTYPE:
		*(int *)data = WSKBD_TYPE_HPC_BTN; /* may be use new type? */
		return (0);

	case WSKBDIO_GETLEDS:
		*(int *)data = 0;
		return (0);

	default:
		return (EPASSTHROUGH);
	}
}


static void
pfctp_wskbd_set_leds(void *self, int leds)
{

	/* nothing to do*/
	return;
}


/*
 * platid independent routines.
 */
/*
 * Jornada 6x0 routines.
 */
static int
pfctp_j6x0_get_stat()
{
	uint8_t phdr;

	phdr = _reg_read_1(SH7709_PHDR);
	return(phdr & PFCTP_J6X0_PEN_DOWN);
}

static int
pfctp_j6x0_get_touched(struct pfctp_softc *sc, int *served)
{
	uint8_t phdr, touched;
	unsigned int steady, tremor_timeout;

	/*
	 * Number of times the "touched" bit should be read
	 * consecutively.
	 */
#define J6X0_TREMOR_THRESHOLD 0x300

	steady = 0;
	tremor_timeout = J6X0_TREMOR_THRESHOLD * 16;	/* XXX: arbitrary */
	touched = PFCTP_J6X0_PEN_DOWN;/* we start with "touched" state */

	do {
		phdr = _reg_read_1(SH7709_PHDR);

		if ((phdr & PFCTP_J6X0_PEN_DOWN) == touched)
			++steady;
		else {
			steady = 0;
			touched = phdr & PFCTP_J6X0_PEN_DOWN;
		}

		if (--tremor_timeout == 0) {
			DPRINTF(("%s: tremor timeout!\n", sc->sc_dev.dv_xname));
			*served = 1;
			return (0);
		}
	} while (steady < J6X0_TREMOR_THRESHOLD);

	return (touched);
}

/*
 * Read raw X/Y coordinates from the ADC.
 * XXX: protect accesses to SCPDR?
 */
static void
pfctp_j6x0_get_raw_xy(int *rawxp, int *rawyp)
{
	uint8_t scpdr;

	/* Y axis */
	scpdr = _reg_read_1(SH7709_SCPDR);
	scpdr |=  PFCTP_J6X0_SCAN_ENABLE;
	scpdr &= ~PFCTP_J6X0_SCAN_Y; /* pull low to scan */
	_reg_write_1(SH7709_SCPDR, scpdr);
	delay(10);

	*rawyp = adc_sample_channel(PFCTP_J6X0_ADC_CHANNEL_TP_Y);

	/* X axis */
	scpdr = _reg_read_1(SH7709_SCPDR);
	scpdr |=  PFCTP_J6X0_SCAN_Y;
	scpdr &= ~PFCTP_J6X0_SCAN_X; /* pull low to scan */
	_reg_write_1(SH7709_SCPDR, scpdr);
	delay(10);

	*rawxp = adc_sample_channel(PFCTP_J6X0_ADC_CHANNEL_TP_X);

	/* restore SCPDR */
	scpdr = _reg_read_1(SH7709_SCPDR);
	scpdr |=  PFCTP_J6X0_SCAN_X;
	scpdr &= ~PFCTP_J6X0_SCAN_ENABLE;
	_reg_write_1(SH7709_SCPDR, scpdr);
}


/*
 * Check if the (rawx, rawy) is inside one of the 4 hard icons.
 * Return the icon number 1..4, or 0 if not inside an icon.
 */
static int
pfctp_j6x0_get_hard_icon(int rawx, int rawy)
{
	if (rawx <= PFCTP_J6X0_FB_RIGHT)
		return (0);

	if (rawy < PFCTP_J6X0_HARD_ICON_MAX_Y(1))
		return (1);
	else if (rawy < PFCTP_J6X0_HARD_ICON_MAX_Y(2))
		return (2);
	else if (rawy < PFCTP_J6X0_HARD_ICON_MAX_Y(3))
		return (3);
	else
		return (4);
}


/*
 * PERSONA SH3 machine routines.
 */

static int
pfctp_psh3_get_stat()
{
	uint8_t phdr;

	phdr = _reg_read_1(SH7709_PHDR);
	return ((phdr & PFCTP_PSH3_PEN_UP) != PFCTP_PSH3_PEN_UP);

}

static int
pfctp_psh3_get_touched(struct pfctp_softc *sc, int *served)
{
	uint8_t phdr, touched;
	unsigned int steady, tremor_timeout;

        /*
	 * Number of times the "touched" bit should be read
	 * consecutively.
	 */
#define PSH3_TREMOR_THRESHOLD 0x300
	steady = 0;
	tremor_timeout = PSH3_TREMOR_THRESHOLD * 16;	/* XXX: arbitrary */
	touched = TRUE;			/* we start with "touched" state */

	do {
		uint8_t state;

		phdr = _reg_read_1(SH7709_PHDR);
		state = ((phdr & PFCTP_PSH3_PEN_UP) != PFCTP_PSH3_PEN_UP);

		if (state == touched)
			++ steady;
		else {
			steady = 0;
			touched = state;
		}

		if (-- tremor_timeout == 0) {
			DPRINTF(("%s: tremor timeout!\n", sc->sc_dev.dv_xname));
			*served = 1;
			return (0);
		}
	} while (steady < PSH3_TREMOR_THRESHOLD);

	return (touched);
}

/*
 * Read raw X/Y coordinates from the ADC.
 */
static void
pfctp_psh3_get_raw_xy(int *rawxp, int *rawyp)
{
	uint8_t scpdr;

	/* X axis */
	scpdr = _reg_read_1(SH7709_SCPDR);
	scpdr &= ~PFCTP_PSH3_SCAN_DISABLE;
	scpdr |= (PFCTP_PSH3_SCAN_ENABLE | PFCTP_PSH3_SCAN_X);
	_reg_write_1(SH7709_SCPDR, scpdr);
	delay(40);

	*rawxp = adc_sample_channel(PFCTP_PSH3_ADC_CHANNEL_TP_X);

	/* Y axis */
	scpdr = _reg_read_1(SH7709_SCPDR);
	scpdr &=  ~PFCTP_PSH3_SCAN_X;
	scpdr |= (PFCTP_PSH3_SCAN_ENABLE | PFCTP_PSH3_SCAN_Y);
	_reg_write_1(SH7709_SCPDR, scpdr);
	delay(40);

	*rawyp = adc_sample_channel(PFCTP_PSH3_ADC_CHANNEL_TP_Y);

	/* restore SCPDR */
	scpdr = _reg_read_1(SH7709_SCPDR);
	scpdr &= ~(PFCTP_PSH3_SCAN_ENABLE | PFCTP_PSH3_SCAN_Y);
	scpdr |= PFCTP_PSH3_SCAN_DISABLE;
	_reg_write_1(SH7709_SCPDR, scpdr);
}

----Next_Part(Mon_Jul__4_02:40:10_2005_142)----