14f7ed234SMarkus Pargmann /* 24f7ed234SMarkus Pargmann * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de> 34f7ed234SMarkus Pargmann * 44f7ed234SMarkus Pargmann * This program is free software; you can redistribute it and/or modify it under 54f7ed234SMarkus Pargmann * the terms of the GNU General Public License version 2 as published by the 64f7ed234SMarkus Pargmann * Free Software Foundation. 74f7ed234SMarkus Pargmann * 84f7ed234SMarkus Pargmann * Based on driver from 2011: 94f7ed234SMarkus Pargmann * Juergen Beisert, Pengutronix <kernel@pengutronix.de> 104f7ed234SMarkus Pargmann * 114f7ed234SMarkus Pargmann * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue) 124f7ed234SMarkus Pargmann * connected to the imx25 ADC. 134f7ed234SMarkus Pargmann */ 144f7ed234SMarkus Pargmann 154f7ed234SMarkus Pargmann #include <linux/clk.h> 164f7ed234SMarkus Pargmann #include <linux/device.h> 174f7ed234SMarkus Pargmann #include <linux/input.h> 184f7ed234SMarkus Pargmann #include <linux/interrupt.h> 194f7ed234SMarkus Pargmann #include <linux/mfd/imx25-tsadc.h> 204f7ed234SMarkus Pargmann #include <linux/module.h> 214f7ed234SMarkus Pargmann #include <linux/of.h> 224f7ed234SMarkus Pargmann #include <linux/platform_device.h> 234f7ed234SMarkus Pargmann #include <linux/regmap.h> 244f7ed234SMarkus Pargmann 254f7ed234SMarkus Pargmann static const char mx25_tcq_name[] = "mx25-tcq"; 264f7ed234SMarkus Pargmann 274f7ed234SMarkus Pargmann enum mx25_tcq_mode { 284f7ed234SMarkus Pargmann MX25_TS_4WIRE, 294f7ed234SMarkus Pargmann }; 304f7ed234SMarkus Pargmann 314f7ed234SMarkus Pargmann struct mx25_tcq_priv { 324f7ed234SMarkus Pargmann struct regmap *regs; 334f7ed234SMarkus Pargmann struct regmap *core_regs; 344f7ed234SMarkus Pargmann struct input_dev *idev; 354f7ed234SMarkus Pargmann enum mx25_tcq_mode mode; 364f7ed234SMarkus Pargmann unsigned int pen_threshold; 374f7ed234SMarkus Pargmann unsigned int sample_count; 384f7ed234SMarkus Pargmann unsigned int expected_samples; 394f7ed234SMarkus Pargmann unsigned int pen_debounce; 404f7ed234SMarkus Pargmann unsigned int settling_time; 414f7ed234SMarkus Pargmann struct clk *clk; 424f7ed234SMarkus Pargmann int irq; 434f7ed234SMarkus Pargmann struct device *dev; 444f7ed234SMarkus Pargmann }; 454f7ed234SMarkus Pargmann 464f7ed234SMarkus Pargmann static struct regmap_config mx25_tcq_regconfig = { 474f7ed234SMarkus Pargmann .fast_io = true, 484f7ed234SMarkus Pargmann .max_register = 0x5c, 494f7ed234SMarkus Pargmann .reg_bits = 32, 504f7ed234SMarkus Pargmann .val_bits = 32, 514f7ed234SMarkus Pargmann .reg_stride = 4, 524f7ed234SMarkus Pargmann }; 534f7ed234SMarkus Pargmann 544f7ed234SMarkus Pargmann static const struct of_device_id mx25_tcq_ids[] = { 554f7ed234SMarkus Pargmann { .compatible = "fsl,imx25-tcq", }, 564f7ed234SMarkus Pargmann { /* Sentinel */ } 574f7ed234SMarkus Pargmann }; 58e1cb73f6SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, mx25_tcq_ids); 594f7ed234SMarkus Pargmann 604f7ed234SMarkus Pargmann #define TSC_4WIRE_PRE_INDEX 0 614f7ed234SMarkus Pargmann #define TSC_4WIRE_X_INDEX 1 624f7ed234SMarkus Pargmann #define TSC_4WIRE_Y_INDEX 2 634f7ed234SMarkus Pargmann #define TSC_4WIRE_POST_INDEX 3 644f7ed234SMarkus Pargmann #define TSC_4WIRE_LEAVE 4 654f7ed234SMarkus Pargmann 664f7ed234SMarkus Pargmann #define MX25_TSC_DEF_THRESHOLD 80 674f7ed234SMarkus Pargmann #define TSC_MAX_SAMPLES 16 684f7ed234SMarkus Pargmann 694f7ed234SMarkus Pargmann #define MX25_TSC_REPEAT_WAIT 14 704f7ed234SMarkus Pargmann 714f7ed234SMarkus Pargmann enum mx25_adc_configurations { 724f7ed234SMarkus Pargmann MX25_CFG_PRECHARGE = 0, 734f7ed234SMarkus Pargmann MX25_CFG_TOUCH_DETECT, 744f7ed234SMarkus Pargmann MX25_CFG_X_MEASUREMENT, 754f7ed234SMarkus Pargmann MX25_CFG_Y_MEASUREMENT, 764f7ed234SMarkus Pargmann }; 774f7ed234SMarkus Pargmann 784f7ed234SMarkus Pargmann #define MX25_PRECHARGE_VALUE (\ 794f7ed234SMarkus Pargmann MX25_ADCQ_CFG_YPLL_OFF | \ 804f7ed234SMarkus Pargmann MX25_ADCQ_CFG_XNUR_OFF | \ 814f7ed234SMarkus Pargmann MX25_ADCQ_CFG_XPUL_HIGH | \ 824f7ed234SMarkus Pargmann MX25_ADCQ_CFG_REFP_INT | \ 834f7ed234SMarkus Pargmann MX25_ADCQ_CFG_IN_XP | \ 844f7ed234SMarkus Pargmann MX25_ADCQ_CFG_REFN_NGND2 | \ 854f7ed234SMarkus Pargmann MX25_ADCQ_CFG_IGS) 864f7ed234SMarkus Pargmann 874f7ed234SMarkus Pargmann #define MX25_TOUCH_DETECT_VALUE (\ 884f7ed234SMarkus Pargmann MX25_ADCQ_CFG_YNLR | \ 894f7ed234SMarkus Pargmann MX25_ADCQ_CFG_YPLL_OFF | \ 904f7ed234SMarkus Pargmann MX25_ADCQ_CFG_XNUR_OFF | \ 914f7ed234SMarkus Pargmann MX25_ADCQ_CFG_XPUL_OFF | \ 924f7ed234SMarkus Pargmann MX25_ADCQ_CFG_REFP_INT | \ 934f7ed234SMarkus Pargmann MX25_ADCQ_CFG_IN_XP | \ 944f7ed234SMarkus Pargmann MX25_ADCQ_CFG_REFN_NGND2 | \ 954f7ed234SMarkus Pargmann MX25_ADCQ_CFG_PENIACK) 964f7ed234SMarkus Pargmann 974f7ed234SMarkus Pargmann static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv, 984f7ed234SMarkus Pargmann unsigned int settling_cnt) 994f7ed234SMarkus Pargmann { 1004f7ed234SMarkus Pargmann u32 precharge_cfg = 1014f7ed234SMarkus Pargmann MX25_PRECHARGE_VALUE | 1024f7ed234SMarkus Pargmann MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt); 1034f7ed234SMarkus Pargmann u32 touch_detect_cfg = 1044f7ed234SMarkus Pargmann MX25_TOUCH_DETECT_VALUE | 1054f7ed234SMarkus Pargmann MX25_ADCQ_CFG_NOS(1) | 1064f7ed234SMarkus Pargmann MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt); 1074f7ed234SMarkus Pargmann 1084f7ed234SMarkus Pargmann regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg); 1094f7ed234SMarkus Pargmann 1104f7ed234SMarkus Pargmann /* PRECHARGE */ 1114f7ed234SMarkus Pargmann regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE), 1124f7ed234SMarkus Pargmann precharge_cfg); 1134f7ed234SMarkus Pargmann 1144f7ed234SMarkus Pargmann /* TOUCH_DETECT */ 1154f7ed234SMarkus Pargmann regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT), 1164f7ed234SMarkus Pargmann touch_detect_cfg); 1174f7ed234SMarkus Pargmann 1184f7ed234SMarkus Pargmann /* X Measurement */ 1194f7ed234SMarkus Pargmann regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT), 1204f7ed234SMarkus Pargmann MX25_ADCQ_CFG_YPLL_OFF | 1214f7ed234SMarkus Pargmann MX25_ADCQ_CFG_XNUR_LOW | 1224f7ed234SMarkus Pargmann MX25_ADCQ_CFG_XPUL_HIGH | 1234f7ed234SMarkus Pargmann MX25_ADCQ_CFG_REFP_XP | 1244f7ed234SMarkus Pargmann MX25_ADCQ_CFG_IN_YP | 1254f7ed234SMarkus Pargmann MX25_ADCQ_CFG_REFN_XN | 1264f7ed234SMarkus Pargmann MX25_ADCQ_CFG_NOS(priv->sample_count) | 1274f7ed234SMarkus Pargmann MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt)); 1284f7ed234SMarkus Pargmann 1294f7ed234SMarkus Pargmann /* Y Measurement */ 1304f7ed234SMarkus Pargmann regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT), 1314f7ed234SMarkus Pargmann MX25_ADCQ_CFG_YNLR | 1324f7ed234SMarkus Pargmann MX25_ADCQ_CFG_YPLL_HIGH | 1334f7ed234SMarkus Pargmann MX25_ADCQ_CFG_XNUR_OFF | 1344f7ed234SMarkus Pargmann MX25_ADCQ_CFG_XPUL_OFF | 1354f7ed234SMarkus Pargmann MX25_ADCQ_CFG_REFP_YP | 1364f7ed234SMarkus Pargmann MX25_ADCQ_CFG_IN_XP | 1374f7ed234SMarkus Pargmann MX25_ADCQ_CFG_REFN_YN | 1384f7ed234SMarkus Pargmann MX25_ADCQ_CFG_NOS(priv->sample_count) | 1394f7ed234SMarkus Pargmann MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt)); 1404f7ed234SMarkus Pargmann 1414f7ed234SMarkus Pargmann /* Enable the touch detection right now */ 1424f7ed234SMarkus Pargmann regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg | 1434f7ed234SMarkus Pargmann MX25_ADCQ_CFG_IGS); 1444f7ed234SMarkus Pargmann } 1454f7ed234SMarkus Pargmann 1464f7ed234SMarkus Pargmann static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv, 1474f7ed234SMarkus Pargmann unsigned settling_cnt, int *items) 1484f7ed234SMarkus Pargmann { 1494f7ed234SMarkus Pargmann imx25_setup_queue_cfgs(priv, settling_cnt); 1504f7ed234SMarkus Pargmann 1514f7ed234SMarkus Pargmann /* Setup the conversion queue */ 1524f7ed234SMarkus Pargmann regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0, 1534f7ed234SMarkus Pargmann MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) | 1544f7ed234SMarkus Pargmann MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) | 1554f7ed234SMarkus Pargmann MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) | 1564f7ed234SMarkus Pargmann MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) | 1574f7ed234SMarkus Pargmann MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) | 1584f7ed234SMarkus Pargmann MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT)); 1594f7ed234SMarkus Pargmann 1604f7ed234SMarkus Pargmann /* 1614f7ed234SMarkus Pargmann * We measure X/Y with 'sample_count' number of samples and execute a 1624f7ed234SMarkus Pargmann * touch detection twice, with 1 sample each 1634f7ed234SMarkus Pargmann */ 1644f7ed234SMarkus Pargmann priv->expected_samples = priv->sample_count * 2 + 2; 1654f7ed234SMarkus Pargmann *items = 6; 1664f7ed234SMarkus Pargmann 1674f7ed234SMarkus Pargmann return 0; 1684f7ed234SMarkus Pargmann } 1694f7ed234SMarkus Pargmann 1704f7ed234SMarkus Pargmann static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv) 1714f7ed234SMarkus Pargmann { 1724f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 1734f7ed234SMarkus Pargmann MX25_ADCQ_CR_PDMSK); 1744f7ed234SMarkus Pargmann } 1754f7ed234SMarkus Pargmann 1764f7ed234SMarkus Pargmann static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv) 1774f7ed234SMarkus Pargmann { 1784f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0); 1794f7ed234SMarkus Pargmann } 1804f7ed234SMarkus Pargmann 1814f7ed234SMarkus Pargmann static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv) 1824f7ed234SMarkus Pargmann { 1834f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 1844f7ed234SMarkus Pargmann MX25_ADCQ_MR_FDRY_IRQ); 1854f7ed234SMarkus Pargmann } 1864f7ed234SMarkus Pargmann 1874f7ed234SMarkus Pargmann static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv) 1884f7ed234SMarkus Pargmann { 1894f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0); 1904f7ed234SMarkus Pargmann } 1914f7ed234SMarkus Pargmann 1924f7ed234SMarkus Pargmann static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv) 1934f7ed234SMarkus Pargmann { 1944f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, 1954f7ed234SMarkus Pargmann MX25_ADCQ_CR_FQS, 1964f7ed234SMarkus Pargmann MX25_ADCQ_CR_FQS); 1974f7ed234SMarkus Pargmann } 1984f7ed234SMarkus Pargmann 1994f7ed234SMarkus Pargmann static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv) 2004f7ed234SMarkus Pargmann { 2014f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, 2024f7ed234SMarkus Pargmann MX25_ADCQ_CR_FQS, 0); 2034f7ed234SMarkus Pargmann } 2044f7ed234SMarkus Pargmann 2054f7ed234SMarkus Pargmann static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv) 2064f7ed234SMarkus Pargmann { 2074f7ed234SMarkus Pargmann u32 tcqcr; 2084f7ed234SMarkus Pargmann 2094f7ed234SMarkus Pargmann regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr); 2104f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 2114f7ed234SMarkus Pargmann MX25_ADCQ_CR_FRST); 2124f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0); 2134f7ed234SMarkus Pargmann regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr); 2144f7ed234SMarkus Pargmann } 2154f7ed234SMarkus Pargmann 2164f7ed234SMarkus Pargmann static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv) 2174f7ed234SMarkus Pargmann { 2184f7ed234SMarkus Pargmann /* stop the queue from looping */ 2194f7ed234SMarkus Pargmann mx25_tcq_force_queue_stop(priv); 2204f7ed234SMarkus Pargmann 2214f7ed234SMarkus Pargmann /* for a clean touch detection, preload the X plane */ 2224f7ed234SMarkus Pargmann regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE); 2234f7ed234SMarkus Pargmann 2244f7ed234SMarkus Pargmann /* waste some time now to pre-load the X plate to high voltage */ 2254f7ed234SMarkus Pargmann mx25_tcq_fifo_reset(priv); 2264f7ed234SMarkus Pargmann 2274f7ed234SMarkus Pargmann /* re-enable the detection right now */ 2284f7ed234SMarkus Pargmann regmap_write(priv->core_regs, MX25_TSC_TICR, 2294f7ed234SMarkus Pargmann MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS); 2304f7ed234SMarkus Pargmann 2314f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD, 2324f7ed234SMarkus Pargmann MX25_ADCQ_SR_PD); 2334f7ed234SMarkus Pargmann 2344f7ed234SMarkus Pargmann /* enable the pen down event to be a source for the interrupt */ 2354f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0); 2364f7ed234SMarkus Pargmann 2374f7ed234SMarkus Pargmann /* lets fire the next IRQ if someone touches the touchscreen */ 2384f7ed234SMarkus Pargmann mx25_tcq_enable_touch_irq(priv); 2394f7ed234SMarkus Pargmann } 2404f7ed234SMarkus Pargmann 2414f7ed234SMarkus Pargmann static void mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv, 2424f7ed234SMarkus Pargmann u32 *sample_buf, 2434f7ed234SMarkus Pargmann unsigned int samples) 2444f7ed234SMarkus Pargmann { 2454f7ed234SMarkus Pargmann unsigned int x_pos = 0; 2464f7ed234SMarkus Pargmann unsigned int y_pos = 0; 2474f7ed234SMarkus Pargmann unsigned int touch_pre = 0; 2484f7ed234SMarkus Pargmann unsigned int touch_post = 0; 2494f7ed234SMarkus Pargmann unsigned int i; 2504f7ed234SMarkus Pargmann 2514f7ed234SMarkus Pargmann for (i = 0; i < samples; i++) { 2524f7ed234SMarkus Pargmann unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]); 2534f7ed234SMarkus Pargmann unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]); 2544f7ed234SMarkus Pargmann 2554f7ed234SMarkus Pargmann switch (index) { 2564f7ed234SMarkus Pargmann case 1: 2574f7ed234SMarkus Pargmann touch_pre = val; 2584f7ed234SMarkus Pargmann break; 2594f7ed234SMarkus Pargmann case 2: 2604f7ed234SMarkus Pargmann x_pos = val; 2614f7ed234SMarkus Pargmann break; 2624f7ed234SMarkus Pargmann case 3: 2634f7ed234SMarkus Pargmann y_pos = val; 2644f7ed234SMarkus Pargmann break; 2654f7ed234SMarkus Pargmann case 5: 2664f7ed234SMarkus Pargmann touch_post = val; 2674f7ed234SMarkus Pargmann break; 2684f7ed234SMarkus Pargmann default: 2694f7ed234SMarkus Pargmann dev_dbg(priv->dev, "Dropped samples because of invalid index %d\n", 2704f7ed234SMarkus Pargmann index); 2714f7ed234SMarkus Pargmann return; 2724f7ed234SMarkus Pargmann } 2734f7ed234SMarkus Pargmann } 2744f7ed234SMarkus Pargmann 2754f7ed234SMarkus Pargmann if (samples != 0) { 2764f7ed234SMarkus Pargmann /* 2774f7ed234SMarkus Pargmann * only if both touch measures are below a threshold, 2784f7ed234SMarkus Pargmann * the position is valid 2794f7ed234SMarkus Pargmann */ 2804f7ed234SMarkus Pargmann if (touch_pre < priv->pen_threshold && 2814f7ed234SMarkus Pargmann touch_post < priv->pen_threshold) { 2824f7ed234SMarkus Pargmann /* valid samples, generate a report */ 2834f7ed234SMarkus Pargmann x_pos /= priv->sample_count; 2844f7ed234SMarkus Pargmann y_pos /= priv->sample_count; 2854f7ed234SMarkus Pargmann input_report_abs(priv->idev, ABS_X, x_pos); 2864f7ed234SMarkus Pargmann input_report_abs(priv->idev, ABS_Y, y_pos); 2874f7ed234SMarkus Pargmann input_report_key(priv->idev, BTN_TOUCH, 1); 2884f7ed234SMarkus Pargmann input_sync(priv->idev); 2894f7ed234SMarkus Pargmann 2904f7ed234SMarkus Pargmann /* get next sample */ 2914f7ed234SMarkus Pargmann mx25_tcq_enable_fifo_irq(priv); 2924f7ed234SMarkus Pargmann } else if (touch_pre >= priv->pen_threshold && 2934f7ed234SMarkus Pargmann touch_post >= priv->pen_threshold) { 2944f7ed234SMarkus Pargmann /* 2954f7ed234SMarkus Pargmann * if both samples are invalid, 2964f7ed234SMarkus Pargmann * generate a release report 2974f7ed234SMarkus Pargmann */ 2984f7ed234SMarkus Pargmann input_report_key(priv->idev, BTN_TOUCH, 0); 2994f7ed234SMarkus Pargmann input_sync(priv->idev); 3004f7ed234SMarkus Pargmann mx25_tcq_re_enable_touch_detection(priv); 3014f7ed234SMarkus Pargmann } else { 3024f7ed234SMarkus Pargmann /* 3034f7ed234SMarkus Pargmann * if only one of both touch measurements are 3044f7ed234SMarkus Pargmann * below the threshold, still some bouncing 3054f7ed234SMarkus Pargmann * happens. Take additional samples in this 3064f7ed234SMarkus Pargmann * case to be sure 3074f7ed234SMarkus Pargmann */ 3084f7ed234SMarkus Pargmann mx25_tcq_enable_fifo_irq(priv); 3094f7ed234SMarkus Pargmann } 3104f7ed234SMarkus Pargmann } 3114f7ed234SMarkus Pargmann } 3124f7ed234SMarkus Pargmann 3134f7ed234SMarkus Pargmann static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id) 3144f7ed234SMarkus Pargmann { 3154f7ed234SMarkus Pargmann struct mx25_tcq_priv *priv = dev_id; 3164f7ed234SMarkus Pargmann u32 sample_buf[TSC_MAX_SAMPLES]; 3174f7ed234SMarkus Pargmann unsigned int samples; 3184f7ed234SMarkus Pargmann u32 stats; 3194f7ed234SMarkus Pargmann unsigned int i; 3204f7ed234SMarkus Pargmann 3214f7ed234SMarkus Pargmann /* 3224f7ed234SMarkus Pargmann * Check how many samples are available. We always have to read exactly 3234f7ed234SMarkus Pargmann * sample_count samples from the fifo, or a multiple of sample_count. 3244f7ed234SMarkus Pargmann * Otherwise we mixup samples into different touch events. 3254f7ed234SMarkus Pargmann */ 3264f7ed234SMarkus Pargmann regmap_read(priv->regs, MX25_ADCQ_SR, &stats); 3274f7ed234SMarkus Pargmann samples = MX25_ADCQ_SR_FDN(stats); 3284f7ed234SMarkus Pargmann samples -= samples % priv->sample_count; 3294f7ed234SMarkus Pargmann 3304f7ed234SMarkus Pargmann if (!samples) 3314f7ed234SMarkus Pargmann return IRQ_HANDLED; 3324f7ed234SMarkus Pargmann 3334f7ed234SMarkus Pargmann for (i = 0; i != samples; ++i) 3344f7ed234SMarkus Pargmann regmap_read(priv->regs, MX25_ADCQ_FIFO, &sample_buf[i]); 3354f7ed234SMarkus Pargmann 3364f7ed234SMarkus Pargmann mx25_tcq_create_event_for_4wire(priv, sample_buf, samples); 3374f7ed234SMarkus Pargmann 3384f7ed234SMarkus Pargmann return IRQ_HANDLED; 3394f7ed234SMarkus Pargmann } 3404f7ed234SMarkus Pargmann 3414f7ed234SMarkus Pargmann static irqreturn_t mx25_tcq_irq(int irq, void *dev_id) 3424f7ed234SMarkus Pargmann { 3434f7ed234SMarkus Pargmann struct mx25_tcq_priv *priv = dev_id; 3444f7ed234SMarkus Pargmann u32 stat; 3454f7ed234SMarkus Pargmann int ret = IRQ_HANDLED; 3464f7ed234SMarkus Pargmann 3474f7ed234SMarkus Pargmann regmap_read(priv->regs, MX25_ADCQ_SR, &stat); 3484f7ed234SMarkus Pargmann 3494f7ed234SMarkus Pargmann if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR)) 3504f7ed234SMarkus Pargmann mx25_tcq_re_enable_touch_detection(priv); 3514f7ed234SMarkus Pargmann 3524f7ed234SMarkus Pargmann if (stat & MX25_ADCQ_SR_PD) { 3534f7ed234SMarkus Pargmann mx25_tcq_disable_touch_irq(priv); 3544f7ed234SMarkus Pargmann mx25_tcq_force_queue_start(priv); 3554f7ed234SMarkus Pargmann mx25_tcq_enable_fifo_irq(priv); 3564f7ed234SMarkus Pargmann } 3574f7ed234SMarkus Pargmann 3584f7ed234SMarkus Pargmann if (stat & MX25_ADCQ_SR_FDRY) { 3594f7ed234SMarkus Pargmann mx25_tcq_disable_fifo_irq(priv); 3604f7ed234SMarkus Pargmann ret = IRQ_WAKE_THREAD; 3614f7ed234SMarkus Pargmann } 3624f7ed234SMarkus Pargmann 3634f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR | 3644f7ed234SMarkus Pargmann MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR | 3654f7ed234SMarkus Pargmann MX25_ADCQ_SR_PD, 3664f7ed234SMarkus Pargmann MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | 3674f7ed234SMarkus Pargmann MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD); 3684f7ed234SMarkus Pargmann 3694f7ed234SMarkus Pargmann return ret; 3704f7ed234SMarkus Pargmann } 3714f7ed234SMarkus Pargmann 3724f7ed234SMarkus Pargmann /* configure the state machine for a 4-wire touchscreen */ 3734f7ed234SMarkus Pargmann static int mx25_tcq_init(struct mx25_tcq_priv *priv) 3744f7ed234SMarkus Pargmann { 3754f7ed234SMarkus Pargmann u32 tgcr; 3764f7ed234SMarkus Pargmann unsigned int ipg_div; 3774f7ed234SMarkus Pargmann unsigned int adc_period; 3784f7ed234SMarkus Pargmann unsigned int debounce_cnt; 3794f7ed234SMarkus Pargmann unsigned int settling_cnt; 3804f7ed234SMarkus Pargmann int itemct; 3814f7ed234SMarkus Pargmann int error; 3824f7ed234SMarkus Pargmann 3834f7ed234SMarkus Pargmann regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr); 3844f7ed234SMarkus Pargmann ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr)); 3854f7ed234SMarkus Pargmann adc_period = USEC_PER_SEC * ipg_div * 2 + 2; 3864f7ed234SMarkus Pargmann adc_period /= clk_get_rate(priv->clk) / 1000 + 1; 3874f7ed234SMarkus Pargmann debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1; 3884f7ed234SMarkus Pargmann settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1; 3894f7ed234SMarkus Pargmann 3904f7ed234SMarkus Pargmann /* Reset */ 3914f7ed234SMarkus Pargmann regmap_write(priv->regs, MX25_ADCQ_CR, 3924f7ed234SMarkus Pargmann MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST); 3934f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, 3944f7ed234SMarkus Pargmann MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0); 3954f7ed234SMarkus Pargmann 3964f7ed234SMarkus Pargmann /* up to 128 * 8 ADC clocks are possible */ 3974f7ed234SMarkus Pargmann if (debounce_cnt > 127) 3984f7ed234SMarkus Pargmann debounce_cnt = 127; 3994f7ed234SMarkus Pargmann 4004f7ed234SMarkus Pargmann /* up to 255 * 8 ADC clocks are possible */ 4014f7ed234SMarkus Pargmann if (settling_cnt > 255) 4024f7ed234SMarkus Pargmann settling_cnt = 255; 4034f7ed234SMarkus Pargmann 4044f7ed234SMarkus Pargmann error = imx25_setup_queue_4wire(priv, settling_cnt, &itemct); 4054f7ed234SMarkus Pargmann if (error) 4064f7ed234SMarkus Pargmann return error; 4074f7ed234SMarkus Pargmann 4084f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, 4094f7ed234SMarkus Pargmann MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK, 4104f7ed234SMarkus Pargmann MX25_ADCQ_CR_LITEMID(itemct - 1) | 4114f7ed234SMarkus Pargmann MX25_ADCQ_CR_WMRK(priv->expected_samples - 1)); 4124f7ed234SMarkus Pargmann 4134f7ed234SMarkus Pargmann /* setup debounce count */ 4144f7ed234SMarkus Pargmann regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, 4154f7ed234SMarkus Pargmann MX25_TGCR_PDBTIME_MASK, 4164f7ed234SMarkus Pargmann MX25_TGCR_PDBTIME(debounce_cnt)); 4174f7ed234SMarkus Pargmann 4184f7ed234SMarkus Pargmann /* enable debounce */ 4194f7ed234SMarkus Pargmann regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN, 4204f7ed234SMarkus Pargmann MX25_TGCR_PDBEN); 4214f7ed234SMarkus Pargmann regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN, 4224f7ed234SMarkus Pargmann MX25_TGCR_PDEN); 4234f7ed234SMarkus Pargmann 4244f7ed234SMarkus Pargmann /* enable the engine on demand */ 4254f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK, 4264f7ed234SMarkus Pargmann MX25_ADCQ_CR_QSM_FQS); 4274f7ed234SMarkus Pargmann 4284f7ed234SMarkus Pargmann /* Enable repeat and repeat wait */ 4294f7ed234SMarkus Pargmann regmap_update_bits(priv->regs, MX25_ADCQ_CR, 4304f7ed234SMarkus Pargmann MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK, 4314f7ed234SMarkus Pargmann MX25_ADCQ_CR_RPT | 4324f7ed234SMarkus Pargmann MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT)); 4334f7ed234SMarkus Pargmann 4344f7ed234SMarkus Pargmann return 0; 4354f7ed234SMarkus Pargmann } 4364f7ed234SMarkus Pargmann 4374f7ed234SMarkus Pargmann static int mx25_tcq_parse_dt(struct platform_device *pdev, 4384f7ed234SMarkus Pargmann struct mx25_tcq_priv *priv) 4394f7ed234SMarkus Pargmann { 4404f7ed234SMarkus Pargmann struct device_node *np = pdev->dev.of_node; 4414f7ed234SMarkus Pargmann u32 wires; 4424f7ed234SMarkus Pargmann int error; 4434f7ed234SMarkus Pargmann 4444f7ed234SMarkus Pargmann /* Setup defaults */ 4454f7ed234SMarkus Pargmann priv->pen_threshold = 500; 4464f7ed234SMarkus Pargmann priv->sample_count = 3; 4474f7ed234SMarkus Pargmann priv->pen_debounce = 1000000; 4484f7ed234SMarkus Pargmann priv->settling_time = 250000; 4494f7ed234SMarkus Pargmann 4504f7ed234SMarkus Pargmann error = of_property_read_u32(np, "fsl,wires", &wires); 4514f7ed234SMarkus Pargmann if (error) { 4524f7ed234SMarkus Pargmann dev_err(&pdev->dev, "Failed to find fsl,wires properties\n"); 4534f7ed234SMarkus Pargmann return error; 4544f7ed234SMarkus Pargmann } 4554f7ed234SMarkus Pargmann 4564f7ed234SMarkus Pargmann if (wires == 4) { 4574f7ed234SMarkus Pargmann priv->mode = MX25_TS_4WIRE; 4584f7ed234SMarkus Pargmann } else { 4594f7ed234SMarkus Pargmann dev_err(&pdev->dev, "%u-wire mode not supported\n", wires); 4604f7ed234SMarkus Pargmann return -EINVAL; 4614f7ed234SMarkus Pargmann } 4624f7ed234SMarkus Pargmann 4634f7ed234SMarkus Pargmann /* These are optional, we don't care about the return values */ 4644f7ed234SMarkus Pargmann of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold); 4654f7ed234SMarkus Pargmann of_property_read_u32(np, "fsl,settling-time-ns", &priv->settling_time); 4664f7ed234SMarkus Pargmann of_property_read_u32(np, "fsl,pen-debounce-ns", &priv->pen_debounce); 4674f7ed234SMarkus Pargmann 4684f7ed234SMarkus Pargmann return 0; 4694f7ed234SMarkus Pargmann } 4704f7ed234SMarkus Pargmann 4714f7ed234SMarkus Pargmann static int mx25_tcq_open(struct input_dev *idev) 4724f7ed234SMarkus Pargmann { 4734f7ed234SMarkus Pargmann struct device *dev = &idev->dev; 4744f7ed234SMarkus Pargmann struct mx25_tcq_priv *priv = dev_get_drvdata(dev); 4754f7ed234SMarkus Pargmann int error; 4764f7ed234SMarkus Pargmann 4774f7ed234SMarkus Pargmann error = clk_prepare_enable(priv->clk); 4784f7ed234SMarkus Pargmann if (error) { 4794f7ed234SMarkus Pargmann dev_err(dev, "Failed to enable ipg clock\n"); 4804f7ed234SMarkus Pargmann return error; 4814f7ed234SMarkus Pargmann } 4824f7ed234SMarkus Pargmann 4834f7ed234SMarkus Pargmann error = mx25_tcq_init(priv); 4844f7ed234SMarkus Pargmann if (error) { 4854f7ed234SMarkus Pargmann dev_err(dev, "Failed to init tcq\n"); 4864f7ed234SMarkus Pargmann clk_disable_unprepare(priv->clk); 4874f7ed234SMarkus Pargmann return error; 4884f7ed234SMarkus Pargmann } 4894f7ed234SMarkus Pargmann 4904f7ed234SMarkus Pargmann mx25_tcq_re_enable_touch_detection(priv); 4914f7ed234SMarkus Pargmann 4924f7ed234SMarkus Pargmann return 0; 4934f7ed234SMarkus Pargmann } 4944f7ed234SMarkus Pargmann 4954f7ed234SMarkus Pargmann static void mx25_tcq_close(struct input_dev *idev) 4964f7ed234SMarkus Pargmann { 4974f7ed234SMarkus Pargmann struct mx25_tcq_priv *priv = input_get_drvdata(idev); 4984f7ed234SMarkus Pargmann 4994f7ed234SMarkus Pargmann mx25_tcq_force_queue_stop(priv); 5004f7ed234SMarkus Pargmann mx25_tcq_disable_touch_irq(priv); 5014f7ed234SMarkus Pargmann mx25_tcq_disable_fifo_irq(priv); 5024f7ed234SMarkus Pargmann clk_disable_unprepare(priv->clk); 5034f7ed234SMarkus Pargmann } 5044f7ed234SMarkus Pargmann 5054f7ed234SMarkus Pargmann static int mx25_tcq_probe(struct platform_device *pdev) 5064f7ed234SMarkus Pargmann { 5074f7ed234SMarkus Pargmann struct device *dev = &pdev->dev; 5084f7ed234SMarkus Pargmann struct input_dev *idev; 5094f7ed234SMarkus Pargmann struct mx25_tcq_priv *priv; 5104f7ed234SMarkus Pargmann struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent); 5114f7ed234SMarkus Pargmann struct resource *res; 5124f7ed234SMarkus Pargmann void __iomem *mem; 5134f7ed234SMarkus Pargmann int error; 5144f7ed234SMarkus Pargmann 5154f7ed234SMarkus Pargmann priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 5164f7ed234SMarkus Pargmann if (!priv) 5174f7ed234SMarkus Pargmann return -ENOMEM; 5184f7ed234SMarkus Pargmann priv->dev = dev; 5194f7ed234SMarkus Pargmann 5204f7ed234SMarkus Pargmann res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 5214f7ed234SMarkus Pargmann mem = devm_ioremap_resource(dev, res); 5224f7ed234SMarkus Pargmann if (IS_ERR(mem)) 5234f7ed234SMarkus Pargmann return PTR_ERR(mem); 5244f7ed234SMarkus Pargmann 5254f7ed234SMarkus Pargmann error = mx25_tcq_parse_dt(pdev, priv); 5264f7ed234SMarkus Pargmann if (error) 5274f7ed234SMarkus Pargmann return error; 5284f7ed234SMarkus Pargmann 5294f7ed234SMarkus Pargmann priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig); 5304f7ed234SMarkus Pargmann if (IS_ERR(priv->regs)) { 5314f7ed234SMarkus Pargmann dev_err(dev, "Failed to initialize regmap\n"); 5324f7ed234SMarkus Pargmann return PTR_ERR(priv->regs); 5334f7ed234SMarkus Pargmann } 5344f7ed234SMarkus Pargmann 5354f7ed234SMarkus Pargmann priv->irq = platform_get_irq(pdev, 0); 5364f7ed234SMarkus Pargmann if (priv->irq <= 0) { 5374f7ed234SMarkus Pargmann dev_err(dev, "Failed to get IRQ\n"); 5384f7ed234SMarkus Pargmann return priv->irq; 5394f7ed234SMarkus Pargmann } 5404f7ed234SMarkus Pargmann 5414f7ed234SMarkus Pargmann idev = devm_input_allocate_device(dev); 5424f7ed234SMarkus Pargmann if (!idev) { 5434f7ed234SMarkus Pargmann dev_err(dev, "Failed to allocate input device\n"); 5444f7ed234SMarkus Pargmann return -ENOMEM; 5454f7ed234SMarkus Pargmann } 5464f7ed234SMarkus Pargmann 5474f7ed234SMarkus Pargmann idev->name = mx25_tcq_name; 5484f7ed234SMarkus Pargmann input_set_capability(idev, EV_KEY, BTN_TOUCH); 5494f7ed234SMarkus Pargmann input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0); 5504f7ed234SMarkus Pargmann input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0); 5514f7ed234SMarkus Pargmann 5524f7ed234SMarkus Pargmann idev->id.bustype = BUS_HOST; 5534f7ed234SMarkus Pargmann idev->open = mx25_tcq_open; 5544f7ed234SMarkus Pargmann idev->close = mx25_tcq_close; 5554f7ed234SMarkus Pargmann 5564f7ed234SMarkus Pargmann priv->idev = idev; 5574f7ed234SMarkus Pargmann input_set_drvdata(idev, priv); 5584f7ed234SMarkus Pargmann 5594f7ed234SMarkus Pargmann priv->core_regs = tsadc->regs; 5604f7ed234SMarkus Pargmann if (!priv->core_regs) 5614f7ed234SMarkus Pargmann return -EINVAL; 5624f7ed234SMarkus Pargmann 5634f7ed234SMarkus Pargmann priv->clk = tsadc->clk; 5644f7ed234SMarkus Pargmann if (!priv->clk) 5654f7ed234SMarkus Pargmann return -EINVAL; 5664f7ed234SMarkus Pargmann 5674f7ed234SMarkus Pargmann platform_set_drvdata(pdev, priv); 5684f7ed234SMarkus Pargmann 5694f7ed234SMarkus Pargmann error = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq, 5704f7ed234SMarkus Pargmann mx25_tcq_irq_thread, 0, pdev->name, 5714f7ed234SMarkus Pargmann priv); 5724f7ed234SMarkus Pargmann if (error) { 5734f7ed234SMarkus Pargmann dev_err(dev, "Failed requesting IRQ\n"); 5744f7ed234SMarkus Pargmann return error; 5754f7ed234SMarkus Pargmann } 5764f7ed234SMarkus Pargmann 5774f7ed234SMarkus Pargmann error = input_register_device(idev); 5784f7ed234SMarkus Pargmann if (error) { 5794f7ed234SMarkus Pargmann dev_err(dev, "Failed to register input device\n"); 5804f7ed234SMarkus Pargmann return error; 5814f7ed234SMarkus Pargmann } 5824f7ed234SMarkus Pargmann 5834f7ed234SMarkus Pargmann return 0; 5844f7ed234SMarkus Pargmann } 5854f7ed234SMarkus Pargmann 5864f7ed234SMarkus Pargmann static struct platform_driver mx25_tcq_driver = { 5874f7ed234SMarkus Pargmann .driver = { 5884f7ed234SMarkus Pargmann .name = "mx25-tcq", 5894f7ed234SMarkus Pargmann .of_match_table = mx25_tcq_ids, 5904f7ed234SMarkus Pargmann }, 5914f7ed234SMarkus Pargmann .probe = mx25_tcq_probe, 5924f7ed234SMarkus Pargmann }; 5934f7ed234SMarkus Pargmann module_platform_driver(mx25_tcq_driver); 5944f7ed234SMarkus Pargmann 5954f7ed234SMarkus Pargmann MODULE_DESCRIPTION("TS input driver for Freescale mx25"); 5964f7ed234SMarkus Pargmann MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>"); 5974f7ed234SMarkus Pargmann MODULE_LICENSE("GPL v2"); 598