xref: /openbsd/sys/dev/i2c/iatp.c (revision 73910b96)
1*73910b96Sderaadt /* $OpenBSD: iatp.c,v 1.11 2024/08/19 14:24:24 deraadt Exp $ */
26406ee73Sjcs /*
36406ee73Sjcs  * Atmel maXTouch i2c touchscreen/touchpad driver
46406ee73Sjcs  * Copyright (c) 2016 joshua stein <jcs@openbsd.org>
56406ee73Sjcs  *
66406ee73Sjcs  * AT421085 datasheet:
76406ee73Sjcs  * http://www.atmel.com/images/Atmel-9626-AT42-QTouch-BSW-AT421085-Object-Protocol-Guide_Datasheet.pdf
86406ee73Sjcs  *
96406ee73Sjcs  * Uses code from libmaxtouch <https://github.com/atmel-maxtouch/mxt-app>
106406ee73Sjcs  * Copyright 2011 Atmel Corporation. All rights reserved.
116406ee73Sjcs  *
126406ee73Sjcs  * Redistribution and use in source and binary forms, with or without
136406ee73Sjcs  * modification, are permitted provided that the following conditions are met:
146406ee73Sjcs  *
156406ee73Sjcs  *    1. Redistributions of source code must retain the above copyright notice,
166406ee73Sjcs  *    this list of conditions and the following disclaimer.
176406ee73Sjcs  *
186406ee73Sjcs  *    2. Redistributions in binary form must reproduce the above copyright
196406ee73Sjcs  *    notice, this list of conditions and the following disclaimer in the
206406ee73Sjcs  *    documentation and/or other materials provided with the distribution.
216406ee73Sjcs  *
226406ee73Sjcs  * THIS SOFTWARE IS PROVIDED BY ATMEL ''AS IS'' AND ANY EXPRESS OR IMPLIED
236406ee73Sjcs  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
246406ee73Sjcs  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
256406ee73Sjcs  * EVENT SHALL ATMEL OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
266406ee73Sjcs  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
276406ee73Sjcs  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
286406ee73Sjcs  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
296406ee73Sjcs  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
306406ee73Sjcs  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
316406ee73Sjcs  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
326406ee73Sjcs  */
336406ee73Sjcs 
346406ee73Sjcs #include <sys/param.h>
356406ee73Sjcs #include <sys/systm.h>
366406ee73Sjcs #include <sys/kernel.h>
376406ee73Sjcs #include <sys/device.h>
386406ee73Sjcs #include <sys/malloc.h>
396406ee73Sjcs #include <sys/stdint.h>
406406ee73Sjcs 
416406ee73Sjcs #include <dev/i2c/i2cvar.h>
426406ee73Sjcs 
436406ee73Sjcs #include <dev/wscons/wsconsio.h>
446406ee73Sjcs #include <dev/wscons/wsmousevar.h>
456406ee73Sjcs #include <dev/hid/hid.h>
466406ee73Sjcs #include <dev/hid/hidmsvar.h>
476406ee73Sjcs 
486406ee73Sjcs /* #define IATP_DEBUG */
496406ee73Sjcs 
506406ee73Sjcs #ifdef IATP_DEBUG
516406ee73Sjcs #define DPRINTF(x) printf x
526406ee73Sjcs #else
536406ee73Sjcs #define DPRINTF(x)
546406ee73Sjcs #endif
556406ee73Sjcs 
566406ee73Sjcs struct mxt_object {
576406ee73Sjcs 	uint8_t type;
586406ee73Sjcs 	uint16_t start_pos;
596406ee73Sjcs 	uint8_t size_minus_one;
606406ee73Sjcs #define MXT_SIZE(o)		((uint16_t)((o)->size_minus_one) + 1)
616406ee73Sjcs 	uint8_t instances_minus_one;
626406ee73Sjcs #define MXT_INSTANCES(o)	((uint16_t)((o)->instances_minus_one) + 1)
636406ee73Sjcs 	uint8_t num_report_ids;
646406ee73Sjcs } __packed;
656406ee73Sjcs 
666406ee73Sjcs struct mxt_id_info {
676406ee73Sjcs 	uint8_t family;
686406ee73Sjcs 	uint8_t variant;
696406ee73Sjcs 	uint8_t version;
706406ee73Sjcs 	uint8_t build;
716406ee73Sjcs 	uint8_t matrix_x_size;
726406ee73Sjcs 	uint8_t matrix_y_size;
736406ee73Sjcs 	uint8_t num_objects;
746406ee73Sjcs } __packed;
756406ee73Sjcs 
766406ee73Sjcs struct mxt_info {
776406ee73Sjcs 	struct mxt_id_info id;
786406ee73Sjcs 	struct mxt_object *objects;
796406ee73Sjcs 	uint32_t crc;
806406ee73Sjcs 	uint8_t *raw_info;
816406ee73Sjcs 	uint8_t max_report_id;
826406ee73Sjcs };
836406ee73Sjcs 
846406ee73Sjcs /* object types we care about (of 117 total!) */
856406ee73Sjcs 
866406ee73Sjcs #define MXT_GEN_MESSAGEPROCESSOR_T5	5
876406ee73Sjcs 
886406ee73Sjcs #define MXT_GEN_COMMANDPROCESSOR_T6	6
896406ee73Sjcs # define MXT_T6_STATUS_RESET		(1 << 7)
906406ee73Sjcs # define MXT_T6_STATUS_OFL		(1 << 6)
916406ee73Sjcs # define MXT_T6_STATUS_SIGERR		(1 << 5)
926406ee73Sjcs # define MXT_T6_STATUS_CAL		(1 << 4)
936406ee73Sjcs # define MXT_T6_STATUS_CFGERR		(1 << 3)
946406ee73Sjcs # define MXT_T6_STATUS_COMSERR		(1 << 2)
956406ee73Sjcs # define MXT_T6_CMD_RESET		0
966406ee73Sjcs # define MXT_T6_CMD_BACKUPNV		1
976406ee73Sjcs # define MXT_T6_CMD_CALIBRATE		2
986406ee73Sjcs # define MXT_T6_CMD_REPORTALL		3
996406ee73Sjcs # define MXT_T6_CMD_DIAGNOSTIC		5
1006406ee73Sjcs 
1016406ee73Sjcs #define MXT_GEN_POWERCONFIG_T7		7
1026406ee73Sjcs # define MXT_T7_POWER_MODE_DEFAULT	1
1036406ee73Sjcs # define MXT_T7_POWER_MODE_DEEP_SLEEP	2
1046406ee73Sjcs struct mxt_t7_config {
1056406ee73Sjcs 	uint8_t idle;
1066406ee73Sjcs 	uint8_t active;
1076406ee73Sjcs 	uint8_t atoi_timeout;
1086406ee73Sjcs } __packed;
1096406ee73Sjcs 
1106406ee73Sjcs #define MXT_SPT_GPIOPWM_T19		19
1116406ee73Sjcs static const struct mxt_t19_button_map {
1126406ee73Sjcs 	const char *vendor;
1136406ee73Sjcs 	const char *product;
1146406ee73Sjcs 	const char *hid;
1156406ee73Sjcs 	int bit;
1166406ee73Sjcs } mxt_t19_button_map_devs[] = {
1176406ee73Sjcs 	/* Chromebook Pixel 2015 */
1186406ee73Sjcs 	{ "GOOGLE", "Samus", "ATML0000", 3 },
1196406ee73Sjcs 	/* Other Google Chromebooks */
1206406ee73Sjcs 	{ "GOOGLE", "", "ATML0000", 5 },
1216406ee73Sjcs 	{ NULL }
1226406ee73Sjcs };
1236406ee73Sjcs 
1246406ee73Sjcs #define MXT_SPT_MESSAGECOUNT_T44	44
1256406ee73Sjcs 
1266406ee73Sjcs #define MXT_TOUCH_MULTITOUCHSCREEN_T100	100
1276406ee73Sjcs # define MXT_T100_CTRL			0
1286406ee73Sjcs # define MXT_T100_CFG1			1
1296406ee73Sjcs # define MXT_T100_TCHAUX		3
1306406ee73Sjcs # define MXT_T100_XRANGE		13
1316406ee73Sjcs # define MXT_T100_YRANGE		24
1326406ee73Sjcs # define MXT_T100_CFG_SWITCHXY		(1 << 5)
1336406ee73Sjcs # define MXT_T100_TCHAUX_VECT		(1 << 0)
1346406ee73Sjcs # define MXT_T100_TCHAUX_AMPL		(1 << 1)
1356406ee73Sjcs # define MXT_T100_TCHAUX_AREA		(1 << 2)
1366406ee73Sjcs # define MXT_T100_DETECT		(1 << 7)
1376406ee73Sjcs # define MXT_T100_TYPE_MASK		0x70
1386406ee73Sjcs 
1396406ee73Sjcs enum t100_type {
1406406ee73Sjcs 	MXT_T100_TYPE_FINGER		= 1,
1416406ee73Sjcs 	MXT_T100_TYPE_PASSIVE_STYLUS	= 2,
1426406ee73Sjcs 	MXT_T100_TYPE_HOVERING_FINGER	= 4,
1436406ee73Sjcs 	MXT_T100_TYPE_GLOVE		= 5,
1446406ee73Sjcs 	MXT_T100_TYPE_LARGE_TOUCH	= 6,
1456406ee73Sjcs };
1466406ee73Sjcs 
1476406ee73Sjcs #define MXT_DISTANCE_ACTIVE_TOUCH	0
1486406ee73Sjcs #define MXT_DISTANCE_HOVERING		1
1496406ee73Sjcs 
1506406ee73Sjcs #define MXT_TOUCH_MAJOR_DEFAULT		1
1516406ee73Sjcs 
1526406ee73Sjcs struct iatp_softc {
1536406ee73Sjcs 	struct device		sc_dev;
1546406ee73Sjcs 	i2c_tag_t		sc_tag;
1556406ee73Sjcs 
1566406ee73Sjcs 	i2c_addr_t		sc_addr;
1576406ee73Sjcs 	void			*sc_ih;
1586406ee73Sjcs 
1596406ee73Sjcs 	struct device		*sc_wsmousedev;
1606406ee73Sjcs 	char			sc_hid[16];
1616406ee73Sjcs 	int			sc_busy;
1626406ee73Sjcs 	int			sc_enabled;
1636406ee73Sjcs 	int			sc_touchpad;
1646406ee73Sjcs 	struct tsscale		sc_tsscale;
1656406ee73Sjcs 
1666406ee73Sjcs 	uint8_t			*table;
1676406ee73Sjcs 	size_t			table_size;
1686406ee73Sjcs 
1696406ee73Sjcs 	struct mxt_info		info;
1706406ee73Sjcs 	uint8_t			*msg_buf;
1716406ee73Sjcs 	uint8_t			multitouch;
1726406ee73Sjcs 	uint8_t			num_touchids;
1736406ee73Sjcs 	uint32_t		max_x;
1746406ee73Sjcs 	uint32_t		max_y;
1756406ee73Sjcs 	uint8_t			button;
1766406ee73Sjcs 
1776406ee73Sjcs 	uint16_t		t5_address;
1786406ee73Sjcs 	uint8_t			t5_msg_size;
1796406ee73Sjcs 	uint16_t		t6_address;
1806406ee73Sjcs 	uint8_t			t6_reportid;
1816406ee73Sjcs 	uint16_t		t7_address;
1826406ee73Sjcs 	struct mxt_t7_config	t7_config;
1836406ee73Sjcs 	uint8_t			t19_reportid;
1846406ee73Sjcs 	int			t19_button_bit;
1856406ee73Sjcs 	uint16_t		t44_address;
1866406ee73Sjcs 	uint8_t			t100_reportid_min;
1876406ee73Sjcs 	uint8_t			t100_reportid_max;
1886406ee73Sjcs 	uint8_t			t100_aux_ampl;
1896406ee73Sjcs 	uint8_t			t100_aux_area;
1906406ee73Sjcs 	uint8_t			t100_aux_vect;
1916406ee73Sjcs };
1926406ee73Sjcs 
1936406ee73Sjcs int	iatp_match(struct device *, void *, void *);
1946406ee73Sjcs void	iatp_attach(struct device *, struct device *, void *);
1956406ee73Sjcs int	iatp_detach(struct device *, int);
1966406ee73Sjcs int	iatp_activate(struct device *, int);
1976406ee73Sjcs 
1986406ee73Sjcs int	iatp_ioctl(void *, u_long, caddr_t, int, struct proc *);
1996406ee73Sjcs int	iatp_enable(void *);
2006406ee73Sjcs void	iatp_disable(void *);
2016406ee73Sjcs 
2026406ee73Sjcs int	iatp_read_reg(struct iatp_softc *, uint16_t, size_t, void *);
2036406ee73Sjcs int	iatp_write_reg(struct iatp_softc *, uint16_t, size_t, void *);
2046406ee73Sjcs int	iatp_init(struct iatp_softc *);
2056406ee73Sjcs int	iatp_intr(void *);
2066406ee73Sjcs 
2076406ee73Sjcs int	iatp_proc_msg(struct iatp_softc *, uint8_t *);
2086406ee73Sjcs int	iatp_t5_read_msgs(struct iatp_softc *, int);
2096406ee73Sjcs void	iatp_t6_proc_msg(struct iatp_softc *, uint8_t *);
2106406ee73Sjcs int	iatp_t7_set_power_mode(struct iatp_softc *, int);
2116406ee73Sjcs void	iatp_t19_proc_msg(struct iatp_softc *, uint8_t *);
2126406ee73Sjcs int	iatp_t44_read_count(struct iatp_softc *);
2136406ee73Sjcs void	iatp_t100_proc_msg(struct iatp_softc *, uint8_t *);
2146406ee73Sjcs 
2156406ee73Sjcs const struct wsmouse_accessops iatp_accessops = {
2166406ee73Sjcs 	iatp_enable,
2176406ee73Sjcs 	iatp_ioctl,
2186406ee73Sjcs 	iatp_disable,
2196406ee73Sjcs };
2206406ee73Sjcs 
221471aeecfSnaddy const struct cfattach iatp_ca = {
2226406ee73Sjcs 	sizeof(struct iatp_softc),
2236406ee73Sjcs 	iatp_match,
2246406ee73Sjcs 	iatp_attach,
2256406ee73Sjcs 	iatp_detach,
2266406ee73Sjcs 	iatp_activate
2276406ee73Sjcs };
2286406ee73Sjcs 
2296406ee73Sjcs struct cfdriver iatp_cd = {
2306406ee73Sjcs 	NULL, "iatp", DV_DULL
2316406ee73Sjcs };
2326406ee73Sjcs 
2336406ee73Sjcs int
iatp_match(struct device * parent,void * match,void * aux)2346406ee73Sjcs iatp_match(struct device *parent, void *match, void *aux)
2356406ee73Sjcs {
2366406ee73Sjcs 	struct i2c_attach_args *ia = aux;
2376406ee73Sjcs 
2386406ee73Sjcs 	if (strcmp(ia->ia_name, "iatp") == 0)
2396406ee73Sjcs 		return 1;
2406406ee73Sjcs 
2416406ee73Sjcs 	return 0;
2426406ee73Sjcs }
2436406ee73Sjcs 
2446406ee73Sjcs void
iatp_attach(struct device * parent,struct device * self,void * aux)2456406ee73Sjcs iatp_attach(struct device *parent, struct device *self, void *aux)
2466406ee73Sjcs {
2476406ee73Sjcs 	struct iatp_softc *sc = (struct iatp_softc *)self;
2486406ee73Sjcs 	struct i2c_attach_args *ia = aux;
2496406ee73Sjcs 	struct wsmousedev_attach_args wsmaa;
2506406ee73Sjcs 
2516406ee73Sjcs 	sc->sc_tag = ia->ia_tag;
2526406ee73Sjcs 	sc->sc_addr = ia->ia_addr;
2536406ee73Sjcs 
2546406ee73Sjcs 	if (ia->ia_cookie != NULL)
2556406ee73Sjcs 		memcpy(&sc->sc_hid, ia->ia_cookie, sizeof(sc->sc_hid));
2566406ee73Sjcs 
2576406ee73Sjcs 	if (!iatp_init(sc))
2586406ee73Sjcs 		return;
2596406ee73Sjcs 
2606406ee73Sjcs 	if (ia->ia_intr) {
2616406ee73Sjcs 		printf(" %s", iic_intr_string(sc->sc_tag, ia->ia_intr));
2626406ee73Sjcs 
2636406ee73Sjcs 		sc->sc_ih = iic_intr_establish(sc->sc_tag, ia->ia_intr,
2646406ee73Sjcs 		    IPL_TTY, iatp_intr, sc, sc->sc_dev.dv_xname);
2656406ee73Sjcs 		if (sc->sc_ih == NULL) {
2666406ee73Sjcs 			printf(", can't establish interrupt\n");
2676406ee73Sjcs 			return;
2686406ee73Sjcs 		}
2696406ee73Sjcs 	}
2706406ee73Sjcs 
2716406ee73Sjcs 	printf(": Atmel maXTouch Touch%s (%dx%d)\n",
2726406ee73Sjcs 	    sc->sc_touchpad ? "pad" : "screen", sc->max_x, sc->max_y);
2736406ee73Sjcs 
2746406ee73Sjcs 	wsmaa.accessops = &iatp_accessops;
2756406ee73Sjcs 	wsmaa.accesscookie = sc;
2766406ee73Sjcs 	sc->sc_wsmousedev = config_found(self, &wsmaa, wsmousedevprint);
2776406ee73Sjcs }
2786406ee73Sjcs 
2796406ee73Sjcs int
iatp_detach(struct device * self,int flags)2806406ee73Sjcs iatp_detach(struct device *self, int flags)
2816406ee73Sjcs {
2826406ee73Sjcs 	struct iatp_softc *sc = (struct iatp_softc *)self;
2836406ee73Sjcs 
2846406ee73Sjcs 	if (sc->sc_ih != NULL) {
2856406ee73Sjcs 		intr_disestablish(sc->sc_ih);
2866406ee73Sjcs 		sc->sc_ih = NULL;
2876406ee73Sjcs 	}
2886406ee73Sjcs 
2896406ee73Sjcs 	sc->sc_enabled = 0;
2906406ee73Sjcs 
2916406ee73Sjcs 	return 0;
2926406ee73Sjcs }
2936406ee73Sjcs 
2946406ee73Sjcs int
iatp_activate(struct device * self,int act)2956406ee73Sjcs iatp_activate(struct device *self, int act)
2966406ee73Sjcs {
2976406ee73Sjcs 	struct iatp_softc *sc = (struct iatp_softc *)self;
29807e9317cSderaadt 	int rv;
2996406ee73Sjcs 
3006406ee73Sjcs 	switch (act) {
3016406ee73Sjcs 	case DVACT_QUIESCE:
30207e9317cSderaadt 		rv = config_activate_children(self, act);
3036406ee73Sjcs 		iatp_t7_set_power_mode(sc, MXT_T7_POWER_MODE_DEEP_SLEEP);
3046406ee73Sjcs 		break;
3056406ee73Sjcs 	case DVACT_WAKEUP:
3066406ee73Sjcs 		sc->sc_busy = 1;
3076406ee73Sjcs 		iatp_init(sc);
3086406ee73Sjcs 		sc->sc_busy = 0;
30907e9317cSderaadt 		rv = config_activate_children(self, act);
31007e9317cSderaadt 		break;
31107e9317cSderaadt 	default:
31207e9317cSderaadt 		rv = config_activate_children(self, act);
3136406ee73Sjcs 		break;
3146406ee73Sjcs 	}
31507e9317cSderaadt 	return rv;
3166406ee73Sjcs }
3176406ee73Sjcs 
3186406ee73Sjcs int
iatp_configure(struct iatp_softc * sc)31936e35055Sbru iatp_configure(struct iatp_softc *sc)
32036e35055Sbru {
32136e35055Sbru 	struct wsmousehw *hw;
32236e35055Sbru 
32336e35055Sbru 	hw = wsmouse_get_hw(sc->sc_wsmousedev);
32436e35055Sbru 	if (sc->sc_touchpad) {
32557f285d3Sjcs 		hw->type = WSMOUSE_TYPE_TOUCHPAD;
32636e35055Sbru 		hw->hw_type = WSMOUSEHW_CLICKPAD;
32736e35055Sbru 	} else {
32836e35055Sbru 		hw->type = WSMOUSE_TYPE_TPANEL;
32936e35055Sbru 		hw->hw_type = WSMOUSEHW_TPANEL;
33036e35055Sbru 	}
33136e35055Sbru 	hw->x_min = sc->sc_tsscale.minx;
33236e35055Sbru 	hw->x_max = sc->sc_tsscale.maxx;
33336e35055Sbru 	hw->y_min = sc->sc_tsscale.miny;
33436e35055Sbru 	hw->y_max = sc->sc_tsscale.maxy;
33536e35055Sbru 	hw->h_res = sc->sc_tsscale.resx;
33636e35055Sbru 	hw->v_res = sc->sc_tsscale.resy;
33736e35055Sbru 	hw->mt_slots = sc->num_touchids;
33836e35055Sbru 
33936e35055Sbru 	return (wsmouse_configure(sc->sc_wsmousedev, NULL, 0));
34036e35055Sbru }
34136e35055Sbru 
34236e35055Sbru int
iatp_enable(void * v)3436406ee73Sjcs iatp_enable(void *v)
3446406ee73Sjcs {
3456406ee73Sjcs 	struct iatp_softc *sc = v;
3466406ee73Sjcs 
3478544fed6Smpi 	if (sc->sc_busy &&
3488544fed6Smpi 	    tsleep_nsec(&sc->sc_busy, PRIBIO, "iatp", SEC_TO_NSEC(1)) != 0) {
3496406ee73Sjcs 		printf("%s: trying to enable but we're busy\n",
3506406ee73Sjcs 		    sc->sc_dev.dv_xname);
3516406ee73Sjcs 		return 1;
3526406ee73Sjcs 	}
3536406ee73Sjcs 
3546406ee73Sjcs 	sc->sc_busy = 1;
3556406ee73Sjcs 
3566406ee73Sjcs 	DPRINTF(("%s: enabling\n", sc->sc_dev.dv_xname));
3576406ee73Sjcs 
35836e35055Sbru 	if (iatp_configure(sc)) {
35936e35055Sbru 		printf("%s: failed wsmouse_configure\n", sc->sc_dev.dv_xname);
3606406ee73Sjcs 		return 1;
3616406ee73Sjcs 	}
3626406ee73Sjcs 
3636406ee73Sjcs 	/* force a read of any pending messages so we start getting new
3646406ee73Sjcs 	 * interrupts */
3656406ee73Sjcs 	iatp_t5_read_msgs(sc, sc->info.max_report_id);
3666406ee73Sjcs 
3676406ee73Sjcs 	sc->sc_enabled = 1;
3686406ee73Sjcs 	sc->sc_busy = 0;
3696406ee73Sjcs 
3706406ee73Sjcs 	return 0;
3716406ee73Sjcs }
3726406ee73Sjcs 
3736406ee73Sjcs void
iatp_disable(void * v)3746406ee73Sjcs iatp_disable(void *v)
3756406ee73Sjcs {
3766406ee73Sjcs 	struct iatp_softc *sc = v;
3776406ee73Sjcs 
3786406ee73Sjcs 	DPRINTF(("%s: disabling\n", sc->sc_dev.dv_xname));
3796406ee73Sjcs 
3806406ee73Sjcs 	if (sc->sc_touchpad)
3816406ee73Sjcs 		wsmouse_set_mode(sc->sc_wsmousedev, WSMOUSE_COMPAT);
3826406ee73Sjcs 
3836406ee73Sjcs 	sc->sc_enabled = 0;
3846406ee73Sjcs }
3856406ee73Sjcs 
3866406ee73Sjcs int
iatp_ioctl(void * v,u_long cmd,caddr_t data,int flag,struct proc * p)3876406ee73Sjcs iatp_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
3886406ee73Sjcs {
3896406ee73Sjcs 	struct iatp_softc *sc = v;
3906406ee73Sjcs 	struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
3916406ee73Sjcs 	int wsmode;
3926406ee73Sjcs 
3936406ee73Sjcs 	DPRINTF(("%s: %s: cmd %ld\n", sc->sc_dev.dv_xname, __func__, cmd));
3946406ee73Sjcs 
3956406ee73Sjcs 	switch (cmd) {
3966406ee73Sjcs 	case WSMOUSEIO_SCALIBCOORDS:
3976406ee73Sjcs 		sc->sc_tsscale.minx = wsmc->minx;
3986406ee73Sjcs 		sc->sc_tsscale.maxx = wsmc->maxx;
3996406ee73Sjcs 		sc->sc_tsscale.miny = wsmc->miny;
4006406ee73Sjcs 		sc->sc_tsscale.maxy = wsmc->maxy;
4016406ee73Sjcs 		sc->sc_tsscale.swapxy = wsmc->swapxy;
4026406ee73Sjcs 		sc->sc_tsscale.resx = wsmc->resx;
4036406ee73Sjcs 		sc->sc_tsscale.resy = wsmc->resy;
4046406ee73Sjcs 		break;
4056406ee73Sjcs 
4066406ee73Sjcs 	case WSMOUSEIO_GCALIBCOORDS:
4076406ee73Sjcs 		wsmc->minx = sc->sc_tsscale.minx;
4086406ee73Sjcs 		wsmc->maxx = sc->sc_tsscale.maxx;
4096406ee73Sjcs 		wsmc->miny = sc->sc_tsscale.miny;
4106406ee73Sjcs 		wsmc->maxy = sc->sc_tsscale.maxy;
4116406ee73Sjcs 		wsmc->swapxy = sc->sc_tsscale.swapxy;
4126406ee73Sjcs 		wsmc->resx = sc->sc_tsscale.resx;
4136406ee73Sjcs 		wsmc->resy = sc->sc_tsscale.resy;
4146406ee73Sjcs 		break;
4156406ee73Sjcs 
41657f285d3Sjcs 	case WSMOUSEIO_GTYPE: {
41757f285d3Sjcs 		struct wsmousehw *hw = wsmouse_get_hw(sc->sc_wsmousedev);
41857f285d3Sjcs 		*(u_int *)data = hw->type;
4196406ee73Sjcs 		break;
42057f285d3Sjcs 	}
4216406ee73Sjcs 
4226406ee73Sjcs 	case WSMOUSEIO_SETMODE:
4236406ee73Sjcs 		if (!sc->sc_touchpad)
4246406ee73Sjcs 			return -1;
4256406ee73Sjcs 
4266406ee73Sjcs 		wsmode = *(u_int *)data;
4276406ee73Sjcs 		if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE) {
4286406ee73Sjcs 			printf("%s: invalid mode %d\n", sc->sc_dev.dv_xname,
4296406ee73Sjcs 			    wsmode);
4306406ee73Sjcs 			return EINVAL;
4316406ee73Sjcs 		}
4326406ee73Sjcs 		wsmouse_set_mode(sc->sc_wsmousedev, wsmode);
4336406ee73Sjcs 		break;
4346406ee73Sjcs 
4356406ee73Sjcs 	default:
4366406ee73Sjcs 		return -1;
4376406ee73Sjcs 	}
4386406ee73Sjcs 
4396406ee73Sjcs 	return 0;
4406406ee73Sjcs }
4416406ee73Sjcs 
4426406ee73Sjcs int
iatp_init(struct iatp_softc * sc)4436406ee73Sjcs iatp_init(struct iatp_softc *sc)
4446406ee73Sjcs {
4456406ee73Sjcs 	uint8_t reportid;
4466406ee73Sjcs 	int i;
4476406ee73Sjcs 
4486406ee73Sjcs 	sc->sc_enabled = 0;
4496406ee73Sjcs 
4506406ee73Sjcs 	/* some sane defaults */
4516406ee73Sjcs 	sc->num_touchids = 10;
4526406ee73Sjcs 	sc->max_x = 1023;
4536406ee73Sjcs 	sc->max_y = 1023;
4546406ee73Sjcs 	sc->sc_touchpad = 0;
4556406ee73Sjcs 
4566406ee73Sjcs 	/*
4576406ee73Sjcs 	 * AT42QT1085 Information block:
4586406ee73Sjcs 	 *
4596406ee73Sjcs 	 * ID information (struct mxt_id_info)
4606406ee73Sjcs 	 *	0 Family ID
4616406ee73Sjcs 	 *	1 Variant ID
4626406ee73Sjcs 	 *	2 Version
4636406ee73Sjcs 	 *	3 Build
4646406ee73Sjcs 	 *	4 Number of Keys
4656406ee73Sjcs 	 *	5 1
4666406ee73Sjcs 	 *	6 Number of Object Table Elements
4676406ee73Sjcs 	 * Object Table Element 1 (struct mxt_object)
4686406ee73Sjcs 	 *	7 Object Type
4696406ee73Sjcs 	 *	8-9 Object Start Address
4706406ee73Sjcs 	 *	10 Size - 1
4716406ee73Sjcs 	 *	11 Instances - 1
4726406ee73Sjcs 	 *	12 Number of report IDs per instance
4736406ee73Sjcs 	 * Object Table Element 2 (struct mxt_object)
4746406ee73Sjcs 	 * 	...
4756406ee73Sjcs 	 * Information Block Checksum
4766406ee73Sjcs 	 * [ Object 1 ]
4776406ee73Sjcs 	 * ...
4786406ee73Sjcs 	 */
4796406ee73Sjcs 
4806406ee73Sjcs 	/* read table header */
4816406ee73Sjcs 	if (iatp_read_reg(sc, 0, sizeof(struct mxt_id_info), &sc->info.id) ||
4826406ee73Sjcs 	    !sc->info.id.num_objects) {
4836406ee73Sjcs 		printf("%s: failed reading main memory map\n",
4846406ee73Sjcs 		    sc->sc_dev.dv_xname);
4856406ee73Sjcs 		return 0;
4866406ee73Sjcs 	}
4876406ee73Sjcs 
4886406ee73Sjcs 	sc->table_size = sc->info.id.num_objects * sizeof(struct mxt_object);
4896406ee73Sjcs 	sc->table = malloc(sc->table_size, M_DEVBUF, M_NOWAIT | M_ZERO);
4906406ee73Sjcs 
4916406ee73Sjcs 	/* read all table objects */
4926406ee73Sjcs 	if (iatp_read_reg(sc, sizeof(struct mxt_id_info), sc->table_size,
4936406ee73Sjcs 	    sc->table)) {
4946406ee73Sjcs 		printf("%s: failed reading info table of size %zu\n",
4956406ee73Sjcs 		    sc->sc_dev.dv_xname, sc->table_size);
4966406ee73Sjcs 		return 0;
4976406ee73Sjcs 	}
4986406ee73Sjcs 
4996406ee73Sjcs 	reportid = 1;
5006406ee73Sjcs 	for (i = 0; i < sc->info.id.num_objects; i++) {
5016406ee73Sjcs 		struct mxt_object *object = (void *)(sc->table +
5026406ee73Sjcs 		    (sizeof(struct mxt_object) * i));
5036406ee73Sjcs 		int min_id = 0, max_id = 0;
5046406ee73Sjcs 
5056406ee73Sjcs 		if (object->num_report_ids) {
5066406ee73Sjcs 			min_id = reportid;
5076406ee73Sjcs 			reportid += (object->num_report_ids *
5086406ee73Sjcs 			    (uint8_t)MXT_INSTANCES(object));
5096406ee73Sjcs 			max_id = reportid - 1;
5106406ee73Sjcs 		}
5116406ee73Sjcs 
5126406ee73Sjcs 		DPRINTF(("%s: object[%d] T%d at 0x%x, %d report ids (%d-%d)\n",
5136406ee73Sjcs 		    sc->sc_dev.dv_xname, i, object->type,
5146406ee73Sjcs 		    le16toh(object->start_pos), object->num_report_ids, min_id,
5156406ee73Sjcs 		    max_id));
5166406ee73Sjcs 
5176406ee73Sjcs 		switch (object->type) {
5186406ee73Sjcs 		case MXT_GEN_MESSAGEPROCESSOR_T5:
5196406ee73Sjcs 			/*
5206406ee73Sjcs 			 * 4.2 - message processor is what interrupts and
5216406ee73Sjcs 			 * relays new messages to us
5226406ee73Sjcs 			 */
5236406ee73Sjcs 
5246406ee73Sjcs 			if (sc->info.id.family == 0x80 &&
5256406ee73Sjcs 			    sc->info.id.version < 0x20)
5266406ee73Sjcs 				/*
5276406ee73Sjcs 				 * from linux: "On mXT224 firmware versions
5286406ee73Sjcs 				 * prior to V2.0 read and discard unused CRC
5296406ee73Sjcs 				 * byte otherwise DMA reads are misaligned."
5306406ee73Sjcs 				 */
5316406ee73Sjcs 				sc->t5_msg_size = MXT_SIZE(object);
5326406ee73Sjcs 			else
5336406ee73Sjcs 				sc->t5_msg_size = MXT_SIZE(object) - 1;
5346406ee73Sjcs 
5356406ee73Sjcs 			sc->t5_address = le16toh(object->start_pos);
5366406ee73Sjcs 			break;
5376406ee73Sjcs 
5386406ee73Sjcs 		case MXT_GEN_COMMANDPROCESSOR_T6:
5396406ee73Sjcs 			/*
5406406ee73Sjcs 			 * 4.3 - command processor receives commands from us
5416406ee73Sjcs 			 * and reports command status messages
5426406ee73Sjcs 			 */
5436406ee73Sjcs 			sc->t6_address = le16toh(object->start_pos);
5446406ee73Sjcs 			sc->t6_reportid = min_id;
5456406ee73Sjcs 			break;
5466406ee73Sjcs 
5476406ee73Sjcs 		case MXT_GEN_POWERCONFIG_T7:
5486406ee73Sjcs 			/*
5496406ee73Sjcs 			 * 4.4 - power configuration, number of milliseconds
5506406ee73Sjcs 			 * between sampling in each mode
5516406ee73Sjcs 			 */
5526406ee73Sjcs 			sc->t7_address = le16toh(object->start_pos);
5536406ee73Sjcs 
5546406ee73Sjcs 			iatp_read_reg(sc, sc->t7_address,
5556406ee73Sjcs 			    sizeof(sc->t7_config), &sc->t7_config);
5566406ee73Sjcs 
5576406ee73Sjcs 			break;
5586406ee73Sjcs 
5596406ee73Sjcs 		case MXT_SPT_GPIOPWM_T19: {
5606406ee73Sjcs 			/*
5616406ee73Sjcs 			 * generic gpio pin, mapped to touchpad button(s)
5626406ee73Sjcs 			 */
5636406ee73Sjcs 			const struct mxt_t19_button_map *m;
5646406ee73Sjcs 
5656406ee73Sjcs 			sc->t19_reportid = min_id;
5666406ee73Sjcs 
5676406ee73Sjcs 			/* find this machine's button config */
5686406ee73Sjcs 			sc->t19_button_bit = -1;
5690217d669Skettenis 			if (hw_vendor == NULL || hw_prod == NULL)
5706406ee73Sjcs 				break;
5716406ee73Sjcs 
5726406ee73Sjcs 			for (m = mxt_t19_button_map_devs; m->vendor != NULL;
5736406ee73Sjcs 			    m++) {
5746406ee73Sjcs 				if (strncmp(hw_vendor, m->vendor,
5756406ee73Sjcs 				    strlen(m->vendor)) != 0 ||
5766406ee73Sjcs 				    strncmp(hw_prod, m->product,
5776406ee73Sjcs 				    strlen(m->product)) != 0 ||
5786406ee73Sjcs 				    strncmp(sc->sc_hid, m->hid,
5796406ee73Sjcs 				    strlen(m->hid)) != 0)
5806406ee73Sjcs 					continue;
5816406ee73Sjcs 
5826406ee73Sjcs 				DPRINTF(("%s: found matching t19 "
5836406ee73Sjcs 				    "button map device \"%s\"/\"%s\" on %s: "
5846406ee73Sjcs 				    "bit %d\n", sc->sc_dev.dv_xname,
5856406ee73Sjcs 				    m->vendor, m->product, m->hid, m->bit));
5866406ee73Sjcs 				sc->t19_button_bit = m->bit;
5876406ee73Sjcs 				break;
5886406ee73Sjcs 			}
5896406ee73Sjcs 
5906406ee73Sjcs 			if (sc->t19_button_bit > -1)
5916406ee73Sjcs 				sc->sc_touchpad = 1;
5926406ee73Sjcs 
5936406ee73Sjcs 			break;
5946406ee73Sjcs 		}
5956406ee73Sjcs 
5966406ee73Sjcs 		case MXT_SPT_MESSAGECOUNT_T44:
5976406ee73Sjcs 			sc->t44_address = le16toh(object->start_pos);
5986406ee73Sjcs 			break;
5996406ee73Sjcs 
6006406ee73Sjcs 		case MXT_TOUCH_MULTITOUCHSCREEN_T100: {
6016406ee73Sjcs 			uint16_t range_x, range_y;
6026406ee73Sjcs 			uint8_t orient, tchaux;
6036406ee73Sjcs 			int aux;
6046406ee73Sjcs 
6056406ee73Sjcs 			sc->t100_reportid_min = min_id;
6066406ee73Sjcs 			sc->t100_reportid_max = max_id;
6076406ee73Sjcs 			sc->num_touchids = object->num_report_ids - 2;
6086406ee73Sjcs 			sc->multitouch = MXT_TOUCH_MULTITOUCHSCREEN_T100;
6096406ee73Sjcs 
6106406ee73Sjcs 			if (iatp_read_reg(sc, object->start_pos +
6116406ee73Sjcs 			    MXT_T100_XRANGE, sizeof(range_x), &range_x) ||
6126406ee73Sjcs 			    iatp_read_reg(sc, object->start_pos +
6136406ee73Sjcs 			    MXT_T100_YRANGE, sizeof(range_y), &range_y) ||
6146406ee73Sjcs 			    iatp_read_reg(sc, object->start_pos +
6156406ee73Sjcs 			    MXT_T100_CFG1, 1, &orient) ||
6166406ee73Sjcs 			    iatp_read_reg(sc, object->start_pos +
6176406ee73Sjcs 			    MXT_T100_TCHAUX, 1, &tchaux)) {
6186406ee73Sjcs 				printf("%s: failed reading t100 settings\n",
6196406ee73Sjcs 				    sc->sc_dev.dv_xname);
6206406ee73Sjcs 				continue;
6216406ee73Sjcs 			}
6226406ee73Sjcs 
6236406ee73Sjcs 			/*
6246406ee73Sjcs 			 * orient just affects the size we read, not the x/y
6256406ee73Sjcs 			 * values we read per-packet later.
6266406ee73Sjcs 			 */
6276406ee73Sjcs 			if (orient & MXT_T100_CFG_SWITCHXY) {
6286406ee73Sjcs 				sc->max_x = le16toh(range_y);
6296406ee73Sjcs 				sc->max_y = le16toh(range_x);
6306406ee73Sjcs 			} else {
6316406ee73Sjcs 				sc->max_x = le16toh(range_x);
6326406ee73Sjcs 				sc->max_y = le16toh(range_y);
6336406ee73Sjcs 			}
6346406ee73Sjcs 
6356406ee73Sjcs 			aux = 6;
6366406ee73Sjcs 			if (tchaux & MXT_T100_TCHAUX_VECT)
6376406ee73Sjcs 				sc->t100_aux_vect = aux++;
6386406ee73Sjcs 			if (tchaux & MXT_T100_TCHAUX_AMPL)
6396406ee73Sjcs 				sc->t100_aux_ampl = aux++;
6406406ee73Sjcs 			if (tchaux & MXT_T100_TCHAUX_AREA)
6416406ee73Sjcs 				sc->t100_aux_area = aux++;
6426406ee73Sjcs 			break;
6436406ee73Sjcs 		}
6446406ee73Sjcs 		}
6456406ee73Sjcs 	}
6466406ee73Sjcs 
6476406ee73Sjcs 	sc->info.max_report_id = reportid;
6486406ee73Sjcs 
6496406ee73Sjcs 	sc->sc_tsscale.minx = 0;
6506406ee73Sjcs 	sc->sc_tsscale.maxx = sc->max_x;
6516406ee73Sjcs 	sc->sc_tsscale.miny = 0;
6526406ee73Sjcs 	sc->sc_tsscale.maxy = sc->max_y;
6536406ee73Sjcs 	sc->sc_tsscale.swapxy = 0;
6546406ee73Sjcs 	sc->sc_tsscale.resx = 0;
6556406ee73Sjcs 	sc->sc_tsscale.resy = 0;
6566406ee73Sjcs 
6576406ee73Sjcs 	/*
6586406ee73Sjcs 	 * iatp_t44_read_count expects t5 message processor to immediately
6596406ee73Sjcs 	 * follow t44 message count byte
6606406ee73Sjcs 	 */
6616406ee73Sjcs 	if (sc->t44_address && (sc->t5_address != sc->t44_address + 1)) {
6626406ee73Sjcs 		printf("%s: t5 address (0x%x) != t44 (0x%x + 1)\n",
6636406ee73Sjcs 		    sc->sc_dev.dv_xname, sc->t5_address, sc->t44_address);
6646406ee73Sjcs 		return 0;
6656406ee73Sjcs 	}
6666406ee73Sjcs 
6676406ee73Sjcs 	sc->msg_buf = mallocarray(sc->info.max_report_id, sc->t5_msg_size,
6686406ee73Sjcs 	    M_DEVBUF, M_NOWAIT | M_ZERO);
6696406ee73Sjcs 
6706406ee73Sjcs 	/* flush queue of any pending messages */
6716406ee73Sjcs 	iatp_t5_read_msgs(sc, sc->info.max_report_id);
6726406ee73Sjcs 
6736406ee73Sjcs 	return 1;
6746406ee73Sjcs }
6756406ee73Sjcs 
6766406ee73Sjcs int
iatp_read_reg(struct iatp_softc * sc,uint16_t reg,size_t len,void * val)6776406ee73Sjcs iatp_read_reg(struct iatp_softc *sc, uint16_t reg, size_t len, void *val)
6786406ee73Sjcs {
6796406ee73Sjcs 	uint8_t cmd[2] = { reg & 0xff, (reg >> 8) & 0xff };
6806406ee73Sjcs 	int ret;
6816406ee73Sjcs 
6824b036c7dSjcs 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
6836406ee73Sjcs 
6846406ee73Sjcs 	ret = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, &cmd,
6856406ee73Sjcs 	    sizeof(cmd), val, len, I2C_F_POLL);
6866406ee73Sjcs 
6874b036c7dSjcs 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
6886406ee73Sjcs 
6896406ee73Sjcs 	return ret;
6906406ee73Sjcs }
6916406ee73Sjcs 
6926406ee73Sjcs int
iatp_write_reg(struct iatp_softc * sc,uint16_t reg,size_t len,void * val)6936406ee73Sjcs iatp_write_reg(struct iatp_softc *sc, uint16_t reg, size_t len, void *val)
6946406ee73Sjcs {
6956406ee73Sjcs 	int ret;
6966406ee73Sjcs 	uint8_t *cmd;
6976406ee73Sjcs 
6986406ee73Sjcs 	cmd = malloc(len + 2, M_DEVBUF, M_NOWAIT | M_ZERO);
6996406ee73Sjcs 	cmd[0] = reg & 0xff;
7006406ee73Sjcs 	cmd[1] = (reg >> 8) & 0xff;
7016406ee73Sjcs 	memcpy(&cmd[2], val, len);
7026406ee73Sjcs 
7036406ee73Sjcs 	iic_acquire_bus(sc->sc_tag, 0);
7046406ee73Sjcs 
7056406ee73Sjcs 	ret = iic_exec(sc->sc_tag, I2C_OP_WRITE, sc->sc_addr, cmd, len + 2,
7066406ee73Sjcs 	    NULL, 0, I2C_F_POLL);
7076406ee73Sjcs 
7086406ee73Sjcs 	iic_release_bus(sc->sc_tag, 0);
7096406ee73Sjcs 
7104f61b8feSjsg 	free(cmd, M_DEVBUF, len + 2);
7116406ee73Sjcs 
7126406ee73Sjcs 	return ret;
7136406ee73Sjcs }
7146406ee73Sjcs 
7156406ee73Sjcs int
iatp_intr(void * arg)7166406ee73Sjcs iatp_intr(void *arg)
7176406ee73Sjcs {
7186406ee73Sjcs 	struct iatp_softc *sc = arg;
7196406ee73Sjcs 	int count;
7206406ee73Sjcs 
7216406ee73Sjcs 	DPRINTF(("%s: %s (busy:%d enabled:%d)\n", sc->sc_dev.dv_xname,
7226406ee73Sjcs 	    __func__, sc->sc_busy, sc->sc_enabled));
7236406ee73Sjcs 
7246406ee73Sjcs 	if (sc->sc_busy)
7256406ee73Sjcs 		return 1;
7266406ee73Sjcs 
7276406ee73Sjcs 	sc->sc_busy = 1;
7286406ee73Sjcs 
7296406ee73Sjcs 	if (sc->t44_address)
7306406ee73Sjcs 		count = iatp_t44_read_count(sc);
7316406ee73Sjcs 	else
7326406ee73Sjcs 		count = 1;
7336406ee73Sjcs 
7346406ee73Sjcs 	if (count)
7356406ee73Sjcs 		iatp_t5_read_msgs(sc, count);
7366406ee73Sjcs 
7376406ee73Sjcs 	sc->sc_busy = 0;
7386406ee73Sjcs 	wakeup(&sc->sc_busy);
7396406ee73Sjcs 
7406406ee73Sjcs 	return 1;
7416406ee73Sjcs }
7426406ee73Sjcs 
7436406ee73Sjcs int
iatp_proc_msg(struct iatp_softc * sc,uint8_t * msg)7446406ee73Sjcs iatp_proc_msg(struct iatp_softc *sc, uint8_t *msg)
7456406ee73Sjcs {
7466406ee73Sjcs 	uint8_t report_id = msg[0];
7476406ee73Sjcs 	int i;
7486406ee73Sjcs 
7496406ee73Sjcs 	/* process a single message that has already been read off the wire */
7506406ee73Sjcs 
7516406ee73Sjcs 	if (report_id == 0xff)
7526406ee73Sjcs 		/*
7536406ee73Sjcs 		 * this is usually when we've intentionally over-read just to
7546406ee73Sjcs 		 * clear any pending data to keep interrupts flowing
7556406ee73Sjcs 		 */
7566406ee73Sjcs 		return 0;
7576406ee73Sjcs 
7586406ee73Sjcs 	DPRINTF(("%s: %s: report id %d\n", sc->sc_dev.dv_xname, __func__,
7596406ee73Sjcs 	    report_id));
7606406ee73Sjcs 
7616406ee73Sjcs 	if (report_id == sc->t19_reportid)
7626406ee73Sjcs 		iatp_t19_proc_msg(sc, msg);
7636406ee73Sjcs 	else if (report_id >= sc->t100_reportid_min &&
7646406ee73Sjcs 	    report_id <= sc->t100_reportid_max)
7656406ee73Sjcs 		iatp_t100_proc_msg(sc, msg);
7666406ee73Sjcs 	else {
7676406ee73Sjcs 		DPRINTF(("%s: unknown message (report id %d)",
7686406ee73Sjcs 		    sc->sc_dev.dv_xname, report_id));
7696406ee73Sjcs 		for (i = 0; i < sc->t5_msg_size; i++)
7706406ee73Sjcs 			DPRINTF((" %02x", msg[i]));
7716406ee73Sjcs 		DPRINTF(("\n"));
7726406ee73Sjcs 	}
7736406ee73Sjcs 
7746406ee73Sjcs 	return 1;
7756406ee73Sjcs }
7766406ee73Sjcs 
7776406ee73Sjcs int
iatp_t5_read_msgs(struct iatp_softc * sc,int count)7786406ee73Sjcs iatp_t5_read_msgs(struct iatp_softc *sc, int count)
7796406ee73Sjcs {
7806406ee73Sjcs 	int i;
7816406ee73Sjcs 
7826406ee73Sjcs 	if (count > sc->info.max_report_id) {
7836406ee73Sjcs 		DPRINTF(("%s: clamping count %d to max_report_id %d\n",
7846406ee73Sjcs 		    sc->sc_dev.dv_xname, count, sc->info.max_report_id));
7856406ee73Sjcs 		count = sc->info.max_report_id;
7866406ee73Sjcs 	}
7876406ee73Sjcs 
7886406ee73Sjcs 	DPRINTF(("%s: %s: %d message(s) to read\n", sc->sc_dev.dv_xname,
7896406ee73Sjcs 	    __func__, count));
7906406ee73Sjcs 
7916406ee73Sjcs 	if (iatp_read_reg(sc, sc->t5_address, sc->t5_msg_size * count,
7926406ee73Sjcs 	    sc->msg_buf)) {
7936406ee73Sjcs 		printf("%s: failed reading %d\n", sc->sc_dev.dv_xname,
7946406ee73Sjcs 		    sc->t5_msg_size * count);
7956406ee73Sjcs 		return 0;
7966406ee73Sjcs 	}
7976406ee73Sjcs 
7986406ee73Sjcs 	for (i = 0;  i < count; i++)
7996406ee73Sjcs 		iatp_proc_msg(sc, sc->msg_buf + (sc->t5_msg_size * i));
8006406ee73Sjcs 
8016406ee73Sjcs 	return 1;
8026406ee73Sjcs }
8036406ee73Sjcs 
8046406ee73Sjcs void
iatp_t6_proc_msg(struct iatp_softc * sc,uint8_t * msg)8056406ee73Sjcs iatp_t6_proc_msg(struct iatp_softc *sc, uint8_t *msg)
8066406ee73Sjcs {
8076406ee73Sjcs 	uint8_t status = msg[1];
8086406ee73Sjcs 
8096406ee73Sjcs 	if (status & MXT_T6_STATUS_RESET)
8106406ee73Sjcs 		DPRINTF(("%s: completed reset\n", sc->sc_dev.dv_xname));
8116406ee73Sjcs 	else
8126406ee73Sjcs 		DPRINTF(("%s: other status report 0x%x\n", sc->sc_dev.dv_xname,
8136406ee73Sjcs 		    status));
8146406ee73Sjcs }
8156406ee73Sjcs 
8166406ee73Sjcs int
iatp_t7_set_power_mode(struct iatp_softc * sc,int mode)8176406ee73Sjcs iatp_t7_set_power_mode(struct iatp_softc *sc, int mode)
8186406ee73Sjcs {
8196406ee73Sjcs 	struct mxt_t7_config new_config;
8206406ee73Sjcs 
8216406ee73Sjcs 	if (mode == MXT_T7_POWER_MODE_DEEP_SLEEP) {
8226406ee73Sjcs 		new_config.idle = 0;
8236406ee73Sjcs 		new_config.active = 0;
8246406ee73Sjcs 		new_config.atoi_timeout = 0;
8256406ee73Sjcs 	} else
8266406ee73Sjcs 		new_config = sc->t7_config;
8276406ee73Sjcs 
8286406ee73Sjcs 	DPRINTF(("%s: setting power mode to %d\n", sc->sc_dev.dv_xname, mode));
8296406ee73Sjcs 
8306406ee73Sjcs 	if (iatp_write_reg(sc, sc->t7_address, sizeof(new_config),
8316406ee73Sjcs 	    &new_config)) {
8326406ee73Sjcs 		printf("%s: failed setting power mode to %d\n",
8336406ee73Sjcs 		    sc->sc_dev.dv_xname, mode);
8346406ee73Sjcs 		return 1;
8356406ee73Sjcs 	}
8366406ee73Sjcs 
8376406ee73Sjcs 	return 0;
8386406ee73Sjcs }
8396406ee73Sjcs 
8406406ee73Sjcs void
iatp_t19_proc_msg(struct iatp_softc * sc,uint8_t * msg)8416406ee73Sjcs iatp_t19_proc_msg(struct iatp_softc *sc, uint8_t *msg)
8426406ee73Sjcs {
8436406ee73Sjcs 	int s;
8446406ee73Sjcs 
8456406ee73Sjcs 	if (!sc->sc_enabled)
8466406ee73Sjcs 		return;
8476406ee73Sjcs 
8486406ee73Sjcs 	/* active-low switch */
8496406ee73Sjcs 	sc->button = !(msg[1] & (1 << sc->t19_button_bit));
8506406ee73Sjcs 
8516406ee73Sjcs 	DPRINTF(("%s: button is %d\n", sc->sc_dev.dv_xname, sc->button));
8526406ee73Sjcs 
8536406ee73Sjcs 	s = spltty();
8546406ee73Sjcs 	wsmouse_buttons(sc->sc_wsmousedev, sc->button);
8556406ee73Sjcs 	wsmouse_input_sync(sc->sc_wsmousedev);
8566406ee73Sjcs 	splx(s);
8576406ee73Sjcs }
8586406ee73Sjcs 
8596406ee73Sjcs int
iatp_t44_read_count(struct iatp_softc * sc)8606406ee73Sjcs iatp_t44_read_count(struct iatp_softc *sc)
8616406ee73Sjcs {
8626406ee73Sjcs 	int ret, count;
8636406ee73Sjcs 
8646406ee73Sjcs 	/* read t44 count byte and t5 message data in one shot */
8656406ee73Sjcs 	ret = iatp_read_reg(sc, sc->t44_address, 1 + sc->t5_msg_size,
8666406ee73Sjcs 	    sc->msg_buf);
8676406ee73Sjcs 	if (ret) {
8686406ee73Sjcs 		printf("%s: failed reading t44 and t5\n", sc->sc_dev.dv_xname);
8696406ee73Sjcs 		return 0;
8706406ee73Sjcs 	}
8716406ee73Sjcs 
8726406ee73Sjcs 	count = sc->msg_buf[0];
8736406ee73Sjcs 	if (count == 0) {
8746406ee73Sjcs 		DPRINTF(("%s: %s: no messages\n", sc->sc_dev.dv_xname,
8756406ee73Sjcs 		    __func__));
8766406ee73Sjcs 		/* flush so we keep getting interrupts */
8776406ee73Sjcs 		iatp_t5_read_msgs(sc, sc->info.max_report_id);
8786406ee73Sjcs 		return 0;
8796406ee73Sjcs 	}
8806406ee73Sjcs 
8816406ee73Sjcs 	count--;
8826406ee73Sjcs 	iatp_proc_msg(sc, sc->msg_buf + 1);
8836406ee73Sjcs 
8846406ee73Sjcs 	return count;
8856406ee73Sjcs }
8866406ee73Sjcs 
8876406ee73Sjcs void
iatp_t100_proc_msg(struct iatp_softc * sc,uint8_t * msg)8886406ee73Sjcs iatp_t100_proc_msg(struct iatp_softc *sc, uint8_t *msg)
8896406ee73Sjcs {
8906406ee73Sjcs 	int id = msg[0] - sc->t100_reportid_min - 2;
8916406ee73Sjcs 	int s;
8926406ee73Sjcs 	uint8_t status, type = 0, pressure = 0;
8936406ee73Sjcs 	uint16_t x, y;
8946406ee73Sjcs 
8956406ee73Sjcs 	if (id < 0 || !sc->sc_enabled)
8966406ee73Sjcs 		return;
8976406ee73Sjcs 
8986406ee73Sjcs 	status = msg[1];
8996406ee73Sjcs 	x = (msg[3] << 8) | msg[2];
9006406ee73Sjcs 	y = (msg[5] << 8) | msg[4];
9016406ee73Sjcs 
9026406ee73Sjcs 	if (status & MXT_T100_DETECT) {
9036406ee73Sjcs 		type = (status & MXT_T100_TYPE_MASK) >> 4;
9046406ee73Sjcs 
9056406ee73Sjcs 		if (sc->t100_aux_ampl)
9066406ee73Sjcs 			pressure = msg[sc->t100_aux_ampl];
9076406ee73Sjcs 
9086406ee73Sjcs 		if (!pressure && type != MXT_T100_TYPE_HOVERING_FINGER)
9096406ee73Sjcs 			pressure = 50; /* large enough for synaptics driver */
9106406ee73Sjcs 
9116406ee73Sjcs 		DPRINTF(("%s: type=%d x=%d y=%d finger=%d pressure=%d "
9126406ee73Sjcs 		    "button=%d\n", sc->sc_dev.dv_xname, type, x, y, id,
9136406ee73Sjcs 		    pressure, sc->button));
9146406ee73Sjcs 	} else {
9156406ee73Sjcs 		DPRINTF(("%s: closing slot for finger=%d\n",
9166406ee73Sjcs 		    sc->sc_dev.dv_xname, id));
9176406ee73Sjcs 
9186406ee73Sjcs 		if (sc->sc_touchpad)
9196406ee73Sjcs 			x = y = 0;
9206406ee73Sjcs 
9216406ee73Sjcs 		pressure = 0;
9226406ee73Sjcs 	}
9236406ee73Sjcs 
9246406ee73Sjcs 	if (sc->sc_touchpad)
9256406ee73Sjcs 		y = (sc->max_y - y);
9266406ee73Sjcs 
9276406ee73Sjcs 	/* TODO: adjust to sc_tsscale? */
9286406ee73Sjcs 
9296406ee73Sjcs 	s = spltty();
9306406ee73Sjcs 
9316406ee73Sjcs 	wsmouse_mtstate(sc->sc_wsmousedev, id, x, y, pressure);
9326406ee73Sjcs 
9336406ee73Sjcs 	/* on the touchscreen, assume any finger down is clicking */
9346406ee73Sjcs 	if (!sc->sc_touchpad)
9356406ee73Sjcs 		wsmouse_buttons(sc->sc_wsmousedev, pressure ? 1 : 0);
9366406ee73Sjcs 
9376406ee73Sjcs 	wsmouse_input_sync(sc->sc_wsmousedev);
9386406ee73Sjcs 
9396406ee73Sjcs 	splx(s);
9406406ee73Sjcs }
941