sunxi_ts.c revision 1.1
11.1Sjmcneill/* $NetBSD: sunxi_ts.c,v 1.1 2017/08/27 02:19:46 jmcneill Exp $ */ 21.1Sjmcneill 31.1Sjmcneill/*- 41.1Sjmcneill * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca> 51.1Sjmcneill * All rights reserved. 61.1Sjmcneill * 71.1Sjmcneill * Redistribution and use in source and binary forms, with or without 81.1Sjmcneill * modification, are permitted provided that the following conditions 91.1Sjmcneill * are met: 101.1Sjmcneill * 1. Redistributions of source code must retain the above copyright 111.1Sjmcneill * notice, this list of conditions and the following disclaimer. 121.1Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright 131.1Sjmcneill * notice, this list of conditions and the following disclaimer in the 141.1Sjmcneill * documentation and/or other materials provided with the distribution. 151.1Sjmcneill * 161.1Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 171.1Sjmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 181.1Sjmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 191.1Sjmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 201.1Sjmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 211.1Sjmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 221.1Sjmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 231.1Sjmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 241.1Sjmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 251.1Sjmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 261.1Sjmcneill * POSSIBILITY OF SUCH DAMAGE. 271.1Sjmcneill */ 281.1Sjmcneill 291.1Sjmcneill#include <sys/cdefs.h> 301.1Sjmcneill 311.1Sjmcneill__KERNEL_RCSID(0, "$NetBSD: sunxi_ts.c,v 1.1 2017/08/27 02:19:46 jmcneill Exp $"); 321.1Sjmcneill 331.1Sjmcneill#include <sys/param.h> 341.1Sjmcneill#include <sys/bus.h> 351.1Sjmcneill#include <sys/device.h> 361.1Sjmcneill#include <sys/intr.h> 371.1Sjmcneill#include <sys/systm.h> 381.1Sjmcneill#include <sys/time.h> 391.1Sjmcneill 401.1Sjmcneill#include <dev/fdt/fdtvar.h> 411.1Sjmcneill 421.1Sjmcneill#include <dev/wscons/wsconsio.h> 431.1Sjmcneill#include <dev/wscons/wsmousevar.h> 441.1Sjmcneill#include <dev/wscons/tpcalibvar.h> 451.1Sjmcneill 461.1Sjmcneill#define TS_TP_SENSITIVITY_ADJUST_DEFAULT 15 471.1Sjmcneill#define TS_FILTER_TYPE_DEFAULT 1 481.1Sjmcneill 491.1Sjmcneill#define TP_CTRL0 0x00 501.1Sjmcneill#define TP_CTRL0_ADC_FIRST_DLY __BITS(31,24) 511.1Sjmcneill#define TP_CTRL0_ADC_FIRST_DLY_MODE __BIT(23) 521.1Sjmcneill#define TP_CTRL0_ADC_CLK_SELECT __BIT(22) 531.1Sjmcneill#define TP_CTRL0_ADC_CLK_DIVIDER __BITS(21,20) 541.1Sjmcneill#define TP_CTRL0_FS_DIV __BITS(19,16) 551.1Sjmcneill#define TP_CTRL0_T_ACQ __BITS(15,0) 561.1Sjmcneill#define TP_CTRL1 0x04 571.1Sjmcneill#define TP_CTRL1_STYLUS_UP_DEBOUNCE __BITS(19,12) 581.1Sjmcneill#define TP_CTRL1_STYLUS_UP_DEBOUNCE_EN __BIT(9) 591.1Sjmcneill#define TP_CTRL1_TOUCH_PAN_CALI_EN __BIT(6) 601.1Sjmcneill#define TP_CTRL1_TP_DUAL_EN __BIT(5) 611.1Sjmcneill#define TP_CTRL1_TP_MODE_EN __BIT(4) 621.1Sjmcneill#define TP_CTRL1_TP_ADC_SELECT __BIT(3) 631.1Sjmcneill#define TP_CTRL1_ADC_CHAN_SELECT __BITS(2,0) 641.1Sjmcneill#define TP_CTRL2 0x08 651.1Sjmcneill#define TP_CTRL2_SENSITIVE_ADJUST __BITS(31,28) 661.1Sjmcneill#define TP_CTRL2_MODE_SELECT __BITS(27,26) 671.1Sjmcneill#define TP_CTRL2_PRE_MEA_EN __BIT(24) 681.1Sjmcneill#define TP_CTRL2_PRE_MEA_THRE_CNT __BITS(23,0) 691.1Sjmcneill#define TP_CTRL3 0x0c 701.1Sjmcneill#define TP_CTRL3_FILTER_EN __BIT(2) 711.1Sjmcneill#define TP_CTRL3_FILTER_TYPE __BITS(1,0) 721.1Sjmcneill#define TP_INT 0x10 731.1Sjmcneill#define TP_INT_OVERRUN_IRQ_EN __BIT(17) 741.1Sjmcneill#define TP_INT_DATA_IRQ_EN __BIT(16) 751.1Sjmcneill#define TP_INT_DATA_XY_CHANGE __BIT(13) 761.1Sjmcneill#define TP_INT_FIFO_TRIG_LEVEL __BITS(12,8) 771.1Sjmcneill#define TP_INT_DATA_DRQ_EN __BIT(7) 781.1Sjmcneill#define TP_INT_FIFO_FLUSH __BIT(4) 791.1Sjmcneill#define TP_INT_UP_IRQ_EN __BIT(1) 801.1Sjmcneill#define TP_INT_DOWN_IRQ_EN __BIT(0) 811.1Sjmcneill#define TP_FIFOCS 0x14 821.1Sjmcneill#define TP_FIFOCS_OVERRUN_PENDING __BIT(17) 831.1Sjmcneill#define TP_FIFOCS_DATA_PENDING __BIT(16) 841.1Sjmcneill#define TP_FIFOCS_RXA_CNT __BITS(12,8) 851.1Sjmcneill#define TP_FIFOCS_IDLE_FLG __BIT(2) 861.1Sjmcneill#define TP_FIFOCS_UP_PENDING __BIT(1) 871.1Sjmcneill#define TP_FIFOCS_DOWN_PENDING __BIT(0) 881.1Sjmcneill#define TP_CDAT 0x1c 891.1Sjmcneill#define TP_CDAT_MASK __BITS(11,0) 901.1Sjmcneill#define TP_DATA 0x24 911.1Sjmcneill#define TP_DATA_MASK __BITS(11,0) 921.1Sjmcneill#define TP_IO_CONFIG 0x28 931.1Sjmcneill#define TP_PORT_DATA 0x2c 941.1Sjmcneill#define TP_PORT_DATA_MASK __BITS(3,0) 951.1Sjmcneill 961.1Sjmcneillstatic int sunxi_ts_match(device_t, cfdata_t, void *); 971.1Sjmcneillstatic void sunxi_ts_attach(device_t, device_t, void *); 981.1Sjmcneill 991.1Sjmcneillstatic const char * const compatible[] = { 1001.1Sjmcneill "allwinner,sun5i-a13-ts", 1011.1Sjmcneill NULL 1021.1Sjmcneill}; 1031.1Sjmcneill 1041.1Sjmcneillstatic struct wsmouse_calibcoords sunxi_ts_default_calib = { 1051.1Sjmcneill .minx = 0, 1061.1Sjmcneill .miny = 0, 1071.1Sjmcneill .maxx = __SHIFTOUT_MASK(TP_DATA_MASK), 1081.1Sjmcneill .maxy = __SHIFTOUT_MASK(TP_DATA_MASK), 1091.1Sjmcneill .samplelen = WSMOUSE_CALIBCOORDS_RESET, 1101.1Sjmcneill}; 1111.1Sjmcneill 1121.1Sjmcneillstruct sunxi_ts_softc { 1131.1Sjmcneill device_t sc_dev; 1141.1Sjmcneill int sc_phandle; 1151.1Sjmcneill bus_space_tag_t sc_bst; 1161.1Sjmcneill bus_space_handle_t sc_bsh; 1171.1Sjmcneill 1181.1Sjmcneill bool sc_ts_attached; 1191.1Sjmcneill bool sc_ts_inverted_x; 1201.1Sjmcneill bool sc_ts_inverted_y; 1211.1Sjmcneill 1221.1Sjmcneill struct tpcalib_softc sc_tpcalib; 1231.1Sjmcneill device_t sc_wsmousedev; 1241.1Sjmcneill bool sc_ignoredata; 1251.1Sjmcneill 1261.1Sjmcneill u_int sc_tp_x; 1271.1Sjmcneill u_int sc_tp_y; 1281.1Sjmcneill u_int sc_tp_btns; 1291.1Sjmcneill}; 1301.1Sjmcneill 1311.1SjmcneillCFATTACH_DECL_NEW(sunxi_ts, sizeof(struct sunxi_ts_softc), 1321.1Sjmcneill sunxi_ts_match, sunxi_ts_attach, NULL, NULL); 1331.1Sjmcneill 1341.1Sjmcneill#define TS_READ(sc, reg) \ 1351.1Sjmcneill bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 1361.1Sjmcneill#define TS_WRITE(sc, reg, val) \ 1371.1Sjmcneill bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 1381.1Sjmcneill 1391.1Sjmcneillstatic int 1401.1Sjmcneillsunxi_ts_enable(void *v) 1411.1Sjmcneill{ 1421.1Sjmcneill struct sunxi_ts_softc * const sc = v; 1431.1Sjmcneill 1441.1Sjmcneill /* reset state */ 1451.1Sjmcneill sc->sc_ignoredata = true; 1461.1Sjmcneill sc->sc_tp_x = 0; 1471.1Sjmcneill sc->sc_tp_y = 0; 1481.1Sjmcneill sc->sc_tp_btns = 0; 1491.1Sjmcneill 1501.1Sjmcneill /* Enable touchpanel IRQs */ 1511.1Sjmcneill TS_WRITE(sc, TP_INT, 1521.1Sjmcneill TP_INT_DATA_IRQ_EN | TP_INT_FIFO_FLUSH | TP_INT_UP_IRQ_EN | 1531.1Sjmcneill __SHIFTIN(1, TP_INT_FIFO_TRIG_LEVEL)); 1541.1Sjmcneill 1551.1Sjmcneill return 0; 1561.1Sjmcneill} 1571.1Sjmcneill 1581.1Sjmcneillstatic void 1591.1Sjmcneillsunxi_ts_disable(void *v) 1601.1Sjmcneill{ 1611.1Sjmcneill struct sunxi_ts_softc * const sc = v; 1621.1Sjmcneill 1631.1Sjmcneill /* Disable touchpanel IRQs */ 1641.1Sjmcneill TS_WRITE(sc, TP_INT, 0); 1651.1Sjmcneill} 1661.1Sjmcneill 1671.1Sjmcneillstatic int 1681.1Sjmcneillsunxi_ts_ioctl(void *v, u_long cmd, void *data, int flag, lwp_t *l) 1691.1Sjmcneill{ 1701.1Sjmcneill struct sunxi_ts_softc * const sc = v; 1711.1Sjmcneill struct wsmouse_id *id; 1721.1Sjmcneill 1731.1Sjmcneill switch (cmd) { 1741.1Sjmcneill case WSMOUSEIO_GTYPE: 1751.1Sjmcneill *(int *)data = WSMOUSE_TYPE_TPANEL; 1761.1Sjmcneill return 0; 1771.1Sjmcneill 1781.1Sjmcneill case WSMOUSEIO_GETID: 1791.1Sjmcneill id = data; 1801.1Sjmcneill if (id->type != WSMOUSE_ID_TYPE_UIDSTR) 1811.1Sjmcneill return EINVAL; 1821.1Sjmcneill snprintf(id->data, WSMOUSE_ID_MAXLEN, 1831.1Sjmcneill "Allwinner TS SN000000"); 1841.1Sjmcneill id->length = strlen(id->data); 1851.1Sjmcneill return 0; 1861.1Sjmcneill 1871.1Sjmcneill case WSMOUSEIO_SCALIBCOORDS: 1881.1Sjmcneill case WSMOUSEIO_GCALIBCOORDS: 1891.1Sjmcneill return tpcalib_ioctl(&sc->sc_tpcalib, cmd, data, flag, l); 1901.1Sjmcneill } 1911.1Sjmcneill 1921.1Sjmcneill return EPASSTHROUGH; 1931.1Sjmcneill} 1941.1Sjmcneill 1951.1Sjmcneillstatic const struct wsmouse_accessops sunxi_ts_accessops = { 1961.1Sjmcneill .enable = sunxi_ts_enable, 1971.1Sjmcneill .disable = sunxi_ts_disable, 1981.1Sjmcneill .ioctl = sunxi_ts_ioctl, 1991.1Sjmcneill}; 2001.1Sjmcneill 2011.1Sjmcneillstatic int 2021.1Sjmcneillsunxi_ts_intr(void *priv) 2031.1Sjmcneill{ 2041.1Sjmcneill struct sunxi_ts_softc * const sc = priv; 2051.1Sjmcneill uint32_t fifocs, x, y; 2061.1Sjmcneill int s; 2071.1Sjmcneill 2081.1Sjmcneill fifocs = TS_READ(sc, TP_FIFOCS); 2091.1Sjmcneill 2101.1Sjmcneill if (fifocs & TP_FIFOCS_DATA_PENDING) { 2111.1Sjmcneill x = TS_READ(sc, TP_DATA); 2121.1Sjmcneill y = TS_READ(sc, TP_DATA); 2131.1Sjmcneill if (sc->sc_ignoredata) { 2141.1Sjmcneill /* Discard the first report */ 2151.1Sjmcneill sc->sc_ignoredata = false; 2161.1Sjmcneill } else { 2171.1Sjmcneill if (sc->sc_ts_inverted_x) 2181.1Sjmcneill x = __SHIFTOUT_MASK(TP_DATA_MASK) - x; 2191.1Sjmcneill if (sc->sc_ts_inverted_y) 2201.1Sjmcneill y = __SHIFTOUT_MASK(TP_DATA_MASK) - y; 2211.1Sjmcneill tpcalib_trans(&sc->sc_tpcalib, x, y, 2221.1Sjmcneill &sc->sc_tp_x, &sc->sc_tp_y); 2231.1Sjmcneill sc->sc_tp_btns |= 1; 2241.1Sjmcneill 2251.1Sjmcneill s = spltty(); 2261.1Sjmcneill wsmouse_input(sc->sc_wsmousedev, 2271.1Sjmcneill sc->sc_tp_btns, sc->sc_tp_x, sc->sc_tp_y, 0, 0, 2281.1Sjmcneill WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y); 2291.1Sjmcneill splx(s); 2301.1Sjmcneill } 2311.1Sjmcneill } 2321.1Sjmcneill 2331.1Sjmcneill if (fifocs & TP_FIFOCS_UP_PENDING) { 2341.1Sjmcneill sc->sc_ignoredata = true; 2351.1Sjmcneill sc->sc_tp_btns &= ~1; 2361.1Sjmcneill s = spltty(); 2371.1Sjmcneill wsmouse_input(sc->sc_wsmousedev, 2381.1Sjmcneill sc->sc_tp_btns, sc->sc_tp_x, sc->sc_tp_y, 0, 0, 2391.1Sjmcneill WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y); 2401.1Sjmcneill splx(s); 2411.1Sjmcneill } 2421.1Sjmcneill 2431.1Sjmcneill TS_WRITE(sc, TP_FIFOCS, fifocs); 2441.1Sjmcneill 2451.1Sjmcneill return 1; 2461.1Sjmcneill} 2471.1Sjmcneill 2481.1Sjmcneillstatic void 2491.1Sjmcneillsunxi_ts_init(struct sunxi_ts_softc *sc) 2501.1Sjmcneill{ 2511.1Sjmcneill u_int tp_sensitivity_adjust = TS_TP_SENSITIVITY_ADJUST_DEFAULT; 2521.1Sjmcneill u_int filter_type = TS_FILTER_TYPE_DEFAULT; 2531.1Sjmcneill 2541.1Sjmcneill of_getprop_uint32(sc->sc_phandle, "allwinner,tp-sensitive-adjust", 2551.1Sjmcneill &tp_sensitivity_adjust); 2561.1Sjmcneill of_getprop_uint32(sc->sc_phandle, "allwinner,filter-type", 2571.1Sjmcneill &filter_type); 2581.1Sjmcneill 2591.1Sjmcneill TS_WRITE(sc, TP_CTRL0, 2601.1Sjmcneill __SHIFTIN(0, TP_CTRL0_ADC_CLK_SELECT) | 2611.1Sjmcneill __SHIFTIN(2, TP_CTRL0_ADC_CLK_DIVIDER) | 2621.1Sjmcneill __SHIFTIN(7, TP_CTRL0_FS_DIV) | 2631.1Sjmcneill __SHIFTIN(63, TP_CTRL0_T_ACQ)); 2641.1Sjmcneill TS_WRITE(sc, TP_CTRL2, 2651.1Sjmcneill __SHIFTIN(0, TP_CTRL2_MODE_SELECT) | 2661.1Sjmcneill __SHIFTIN(tp_sensitivity_adjust, TP_CTRL2_SENSITIVE_ADJUST)); 2671.1Sjmcneill TS_WRITE(sc, TP_CTRL3, 2681.1Sjmcneill TP_CTRL3_FILTER_EN | 2691.1Sjmcneill __SHIFTIN(filter_type, TP_CTRL3_FILTER_TYPE)); 2701.1Sjmcneill TS_WRITE(sc, TP_CTRL1, 2711.1Sjmcneill TP_CTRL1_TP_MODE_EN | 2721.1Sjmcneill TP_CTRL1_STYLUS_UP_DEBOUNCE_EN | 2731.1Sjmcneill __SHIFTIN(5, TP_CTRL1_STYLUS_UP_DEBOUNCE)); 2741.1Sjmcneill} 2751.1Sjmcneill 2761.1Sjmcneillstatic int 2771.1Sjmcneillsunxi_ts_match(device_t parent, cfdata_t cf, void *aux) 2781.1Sjmcneill{ 2791.1Sjmcneill struct fdt_attach_args * const faa = aux; 2801.1Sjmcneill 2811.1Sjmcneill return of_match_compatible(faa->faa_phandle, compatible); 2821.1Sjmcneill} 2831.1Sjmcneill 2841.1Sjmcneillstatic void 2851.1Sjmcneillsunxi_ts_attach(device_t parent, device_t self, void *aux) 2861.1Sjmcneill{ 2871.1Sjmcneill struct sunxi_ts_softc * const sc = device_private(self); 2881.1Sjmcneill struct fdt_attach_args * const faa = aux; 2891.1Sjmcneill const int phandle = faa->faa_phandle; 2901.1Sjmcneill struct wsmousedev_attach_args a; 2911.1Sjmcneill char intrstr[128]; 2921.1Sjmcneill bus_addr_t addr; 2931.1Sjmcneill bus_size_t size; 2941.1Sjmcneill void *ih; 2951.1Sjmcneill 2961.1Sjmcneill if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 2971.1Sjmcneill aprint_error(": couldn't get registers\n"); 2981.1Sjmcneill return; 2991.1Sjmcneill } 3001.1Sjmcneill 3011.1Sjmcneill if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 3021.1Sjmcneill aprint_error(": failed to decode interrupt\n"); 3031.1Sjmcneill return; 3041.1Sjmcneill } 3051.1Sjmcneill 3061.1Sjmcneill sc->sc_dev = self; 3071.1Sjmcneill sc->sc_phandle = phandle; 3081.1Sjmcneill sc->sc_bst = faa->faa_bst; 3091.1Sjmcneill if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 3101.1Sjmcneill aprint_error(": couldn't map registers\n"); 3111.1Sjmcneill return; 3121.1Sjmcneill } 3131.1Sjmcneill sc->sc_ts_attached = of_getprop_bool(phandle, "allwinner,ts-attached"); 3141.1Sjmcneill sc->sc_ts_inverted_x = of_getprop_bool(phandle, 3151.1Sjmcneill "touchscreen-inverted-x"); 3161.1Sjmcneill sc->sc_ts_inverted_y = of_getprop_bool(phandle, 3171.1Sjmcneill "touchscreen-inverted-y"); 3181.1Sjmcneill 3191.1Sjmcneill aprint_naive("\n"); 3201.1Sjmcneill aprint_normal(": Touch Screen Controller\n"); 3211.1Sjmcneill 3221.1Sjmcneill sunxi_ts_init(sc); 3231.1Sjmcneill 3241.1Sjmcneill ih = fdtbus_intr_establish(phandle, 0, IPL_VM, 0, sunxi_ts_intr, sc); 3251.1Sjmcneill if (ih == NULL) { 3261.1Sjmcneill aprint_error_dev(self, "couldn't establish interrupt on %s\n", 3271.1Sjmcneill intrstr); 3281.1Sjmcneill return; 3291.1Sjmcneill } 3301.1Sjmcneill aprint_normal_dev(self, "interrupting on %s\n", intrstr); 3311.1Sjmcneill 3321.1Sjmcneill if (sc->sc_ts_attached) { 3331.1Sjmcneill tpcalib_init(&sc->sc_tpcalib); 3341.1Sjmcneill tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS, 3351.1Sjmcneill &sunxi_ts_default_calib, 0, 0); 3361.1Sjmcneill 3371.1Sjmcneill memset(&a, 0, sizeof(a)); 3381.1Sjmcneill a.accessops = &sunxi_ts_accessops; 3391.1Sjmcneill a.accesscookie = sc; 3401.1Sjmcneill sc->sc_wsmousedev = config_found_ia(self, "wsmousedev", 3411.1Sjmcneill &a, wsmousedevprint); 3421.1Sjmcneill } 3431.1Sjmcneill} 344