From patchwork Tue Mar 12 08:21:45 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Jander X-Patchwork-Id: 10848733 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A3DAF1575 for ; Tue, 12 Mar 2019 08:28:55 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8D1D229568 for ; Tue, 12 Mar 2019 08:28:55 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 7812E2957A; Tue, 12 Mar 2019 08:28:55 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 405C129575 for ; Tue, 12 Mar 2019 08:28:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727089AbfCLI2w (ORCPT ); Tue, 12 Mar 2019 04:28:52 -0400 Received: from protonic.xs4all.nl ([83.163.252.89]:46381 "EHLO protonic.nl" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726735AbfCLI2w (ORCPT ); Tue, 12 Mar 2019 04:28:52 -0400 Received: from erd988.prtnl (erd988.prtnl [192.168.224.30]) by sparta (Postfix) with ESMTP id 8B89144A00D9; Tue, 12 Mar 2019 09:23:38 +0100 (CET) From: David Jander To: Dmitry Torokhov , linux-input@vger.kernel.org Cc: Greg KH , Mark Brown , Imre Deak , David Jander Subject: [PATCH 2/2] input: touchscreen: ads7846.c: Re-write sampling and filtering functions Date: Tue, 12 Mar 2019 09:21:45 +0100 Message-Id: <20190312082145.8906-3-david@protonic.nl> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190312082145.8906-1-david@protonic.nl> References: <20190312082145.8906-1-david@protonic.nl> MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The ads7846.c touchscreen driver has several issues that stem from bad overall design of the sampling- and filtering routines. This patch can be seen as a nearly complete re-write of the driver, trying to fix the various issues. Most importantly, a near 100 times reduction of CPU usage caused by excessive context switching due to sending many different small SPI transfers, instead of one single (bigger) transfer to acquire all measurement data. The new filtering code is a bit better, less buggy and hopefully a lot easier to read and maintain. A potential integer- overflow that could cause fake touches has been fixed as well as an issue that caused the driver to report a pen-release when HWMON measurements are taken while the screen is being touched. Also, the unnecessary code duplication for different chip types has been reduced. This version of the driver makes use of the 16-clock-per-conversion mode as described in the data-sheets. It was tested with a tsc2046 but should work also with all other variants. Signed-off-by: David Jander --- .../bindings/input/touchscreen/ads7846.txt | 32 +- drivers/input/touchscreen/ads7846.c | 862 +++++++----------- include/linux/spi/ads7846.h | 27 +- 3 files changed, 370 insertions(+), 551 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/ads7846.txt b/Documentation/devicetree/bindings/input/touchscreen/ads7846.txt index 946f4fd0133e..ebc3614609d9 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/ads7846.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/ads7846.txt @@ -35,18 +35,6 @@ Optional properties: ti,swap-xy swap x and y axis ti,invert_x invert x axis ti,invert_y invert y axis - ti,settle-delay-usec Settling time of the analog signals; - a function of Vcc and the capacitance - on the X/Y drivers. If set to non-zero, - two samples are taken with settle_delay - us apart, and the second one is used. - ~150 uSec with 0.01uF caps (u16). - ti,penirq-recheck-delay-usecs If set to non-zero, after samples are - taken this delay is applied and penirq - is rechecked, to help avoid false - events. This value is affected by the - material used to build the touch layer - (u16). ti,x-plate-ohms Resistance of the X-plate, in Ohms (u16). ti,y-plate-ohms Resistance of the Y-plate, @@ -58,13 +46,23 @@ Optional properties: ti,pressure-min Minimum reported pressure value (threshold) - u16. ti,pressure-max Maximum reported pressure value (u16). - ti,debounce-max Max number of additional readings per - sample (u16). - ti,debounce-tol Tolerance used for filtering (u16). - ti,debounce-rep Additional consecutive good readings - required after the first two (u16). + ti,skip-samples Skip n samples after switching plates. + Maximum of 4. The delay this introduces + depends on the SPI clock frequency. + ti,touch-resistance-threshold Threshold of touchresistance (in Ohms) + for detecting a valid touch. + ti,filter-tolerance Maximum X/Y measurement deviation (raw) + of consecutive measurements. + ti,sample-period-msecs Touchscreen sample period in ms. + ti,report-period-msecs Touch report generation period in ms. ti,pendown-gpio-debounce Platform specific debounce time for the pendown-gpio (u32). + ti,pendown-glitch-max Maximum sample times of pendown signal + glitch. If sliding over a worn touch- + panel, touch-contact can be interrupted + briefly. Avoid detecting a pen release + signal too early by waiting this amount + of samples. pendown-gpio GPIO handle describing the pin the !PENIRQ line is connected to. wakeup-source use any event on touchscreen as wakeup event. diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index b853e889c6ec..27b09d5d7476 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1,6 +1,9 @@ /* * ADS7846 based touchscreen and sensor driver * + * Re-write sampling and filtering code: + * Copyright (c) 2019 David Jander + * * Copyright (c) 2005 David Brownell * Copyright (c) 2006 Nokia Corporation * Various changes: Imre Deak @@ -59,37 +62,21 @@ */ #define TS_POLL_DELAY 1 /* ms delay before the first sample */ -#define TS_POLL_PERIOD 5 /* ms delay between samples */ /* this driver doesn't aim at the peak continuous sample rate */ #define SAMPLE_BITS (8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */) -struct ts_event { - /* - * For portability, we can't read 12 bit values using SPI (which - * would make the controller deliver them as native byte order u16 - * with msbs zeroed). Instead, we read them as two 8-bit values, - * *** WHICH NEED BYTESWAPPING *** and range adjustment. - */ - u16 x; - u16 y; - u16 z1, z2; - bool ignore; - u8 x_buf[3]; - u8 y_buf[3]; +struct ads7846_vfilter_buf { + bool susp; + int x; + int y; + int rt; }; -/* - * We allocate this separately to avoid cache line sharing issues when - * driver is used with DMA-based SPI controllers (like atmel_spi) on - * systems where main memory is not DMA-coherent (most non-x86 boards). - */ -struct ads7846_packet { - u8 read_x, read_y, read_z1, read_z2, pwrdown; - u16 dummy; /* for the pwrdown read */ - struct ts_event tc; - /* for ads7845 with mpc5121 psc spi we use 3-byte buffers */ - u8 read_x_cmd[3], read_y_cmd[3], pwrdown_cmd[3]; +struct ads7846_vfilter { + int bidx; + int bcnt; + struct ads7846_vfilter_buf fbuf[4]; }; struct ads7846 { @@ -115,24 +102,34 @@ struct ads7846 { bool invert_y; bool use_internal; - struct ads7846_packet *packet; + struct ads7846_vfilter vfilter; - struct spi_transfer xfer[18]; - struct spi_message msg[5]; - int msg_count; - wait_queue_head_t wait; +#define MAX_SKIP_SAMPLES 4 +/* 4 measurements, 16 bits each, skipped samples + 2, 3 byte powerdown command */ +#define MAX_XFER_LEN ((4 * 2 * (MAX_SKIP_SAMPLES + 2)) + 3) - bool pendown; + unsigned int skip_samples; + unsigned int filter_tolerance; + unsigned int rt_threshold; + + unsigned int sample_period; + unsigned int report_period; + + struct ads7846_vfilter_buf touch; + bool touch_valid; + + /* DMA-safe buffers for SPI transfers */ + u8 rx_buf[MAX_XFER_LEN] ____cacheline_aligned; + u8 tx_buf[MAX_XFER_LEN] ____cacheline_aligned; - int read_cnt; - int read_rep; - int last_read; + struct spi_transfer xfer; + struct spi_message msg; - u16 debounce_max; - u16 debounce_tol; - u16 debounce_rep; + wait_queue_head_t wait; - u16 penirq_recheck_delay_usecs; + bool pendown; + int penup_count; + unsigned int pendown_glitch_max; struct mutex lock; bool stopped; /* P: lock */ @@ -148,12 +145,7 @@ struct ads7846 { void (*wait_for_sync)(void); }; -/* leave chip selected when we're done, for quicker re-select? */ -#if 0 #define CS_CHANGE(xfer) ((xfer).cs_change = 1) -#else -#define CS_CHANGE(xfer) ((xfer).cs_change = 0) -#endif /*--------------------------------------------------------------------------*/ @@ -287,28 +279,14 @@ static void ads7846_enable(struct ads7846 *ts) */ struct ser_req { - u8 ref_on; - u8 command; - u8 ref_off; - u16 scratch; - struct spi_message msg; - struct spi_transfer xfer[6]; - /* - * DMA (thus cache coherency maintenance) requires the - * transfer buffers to live in their own cache lines. - */ - __be16 sample ____cacheline_aligned; -}; - -struct ads7845_ser_req { - u8 command[3]; struct spi_message msg; struct spi_transfer xfer[2]; /* * DMA (thus cache coherency maintenance) requires the * transfer buffers to live in their own cache lines. */ - u8 sample[3] ____cacheline_aligned; + u8 tx_buf[8] ____cacheline_aligned; + u8 rx_buf[5] ____cacheline_aligned; }; static int ads7846_read12_ser(struct device *dev, unsigned command) @@ -317,26 +295,29 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) struct ads7846 *ts = dev_get_drvdata(dev); struct ser_req *req; int status; + int i = 0; + u8 *txb; req = kzalloc(sizeof *req, GFP_KERNEL); if (!req) return -ENOMEM; spi_message_init(&req->msg); + txb = &req->tx_buf[0]; - /* maybe turn on internal vREF, and let it settle */ + /* + * maybe turn on internal vREF, and let it settle + * note: use_internal can only be true on ads7846 + */ if (ts->use_internal) { - req->ref_on = REF_ON; - req->xfer[0].tx_buf = &req->ref_on; - req->xfer[0].len = 1; - spi_message_add_tail(&req->xfer[0], &req->msg); - - req->xfer[1].rx_buf = &req->scratch; - req->xfer[1].len = 2; + txb[i] = REF_ON; + req->xfer[0].tx_buf = &txb[i]; + req->xfer[0].len = 3; + i += 3; /* for 1uF, settle for 800 usec; no cap, 100 usec. */ - req->xfer[1].delay_usecs = ts->vref_delay_usecs; - spi_message_add_tail(&req->xfer[1], &req->msg); + req->xfer[0].delay_usecs = ts->vref_delay_usecs; + spi_message_add_tail(&req->xfer[0], &req->msg); /* Enable reference voltage */ command |= ADS_PD10_REF_ON; @@ -345,76 +326,23 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) /* Enable ADC in every case */ command |= ADS_PD10_ADC_ON; - /* take sample */ - req->command = (u8) command; - req->xfer[2].tx_buf = &req->command; - req->xfer[2].len = 1; - spi_message_add_tail(&req->xfer[2], &req->msg); - - req->xfer[3].rx_buf = &req->sample; - req->xfer[3].len = 2; - spi_message_add_tail(&req->xfer[3], &req->msg); - - /* REVISIT: take a few more samples, and compare ... */ - - /* converter in low power mode & enable PENIRQ */ - req->ref_off = PWRDOWN; - req->xfer[4].tx_buf = &req->ref_off; - req->xfer[4].len = 1; - spi_message_add_tail(&req->xfer[4], &req->msg); - - req->xfer[5].rx_buf = &req->scratch; - req->xfer[5].len = 2; - CS_CHANGE(req->xfer[5]); - spi_message_add_tail(&req->xfer[5], &req->msg); + req->xfer[1].tx_buf = &txb[i]; + txb[i] = command; + i += 2; + txb[i] = PWRDOWN; + i += 3; + req->xfer[1].rx_buf = &req->rx_buf[0]; + req->xfer[1].len = 5; + CS_CHANGE(req->xfer[1]); + spi_message_add_tail(&req->xfer[1], &req->msg); mutex_lock(&ts->lock); - ads7846_stop(ts); status = spi_sync(spi, &req->msg); - ads7846_restart(ts); mutex_unlock(&ts->lock); if (status == 0) { /* on-wire is a must-ignore bit, a BE12 value, then padding */ - status = be16_to_cpu(req->sample); - status = status >> 3; - status &= 0x0fff; - } - - kfree(req); - return status; -} - -static int ads7845_read12_ser(struct device *dev, unsigned command) -{ - struct spi_device *spi = to_spi_device(dev); - struct ads7846 *ts = dev_get_drvdata(dev); - struct ads7845_ser_req *req; - int status; - - req = kzalloc(sizeof *req, GFP_KERNEL); - if (!req) - return -ENOMEM; - - spi_message_init(&req->msg); - - req->command[0] = (u8) command; - req->xfer[0].tx_buf = req->command; - req->xfer[0].rx_buf = req->sample; - req->xfer[0].len = 3; - spi_message_add_tail(&req->xfer[0], &req->msg); - - mutex_lock(&ts->lock); - ads7846_stop(ts); - status = spi_sync(spi, &req->msg); - ads7846_restart(ts); - mutex_unlock(&ts->lock); - - if (status == 0) { - /* BE12 value, then padding */ - status = be16_to_cpu(*((u16 *)&req->sample[1])); - status = status >> 3; - status &= 0x0fff; + status = (be16_to_cpup((__be16 *)&req->rx_buf[1]) >> 3) & 0x0fff; } kfree(req); @@ -619,201 +547,242 @@ static void null_wait_for_sync(void) { } -static int ads7846_debounce_filter(void *ads, int data_idx, int *val) +static void ads7846_vfilter_reset(struct ads7846 *ts) { - struct ads7846 *ts = ads; + struct ads7846_vfilter *vf = &ts->vfilter; - if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) { - /* Start over collecting consistent readings. */ - ts->read_rep = 0; - /* - * Repeat it, if this was the first read or the read - * wasn't consistent enough. - */ - if (ts->read_cnt < ts->debounce_max) { - ts->last_read = *val; - ts->read_cnt++; - return ADS7846_FILTER_REPEAT; - } else { - /* - * Maximum number of debouncing reached and still - * not enough number of consistent readings. Abort - * the whole sample, repeat it in the next sampling - * period. - */ - ts->read_cnt = 0; - return ADS7846_FILTER_IGNORE; - } - } else { - if (++ts->read_rep > ts->debounce_rep) { - /* - * Got a good reading for this coordinate, - * go for the next one. - */ - ts->read_cnt = 0; - ts->read_rep = 0; - return ADS7846_FILTER_OK; - } else { - /* Read more values that are consistent. */ - ts->read_cnt++; - return ADS7846_FILTER_REPEAT; - } - } + vf->bidx = 0; + vf->bcnt = 0; } -static int ads7846_no_filter(void *ads, int data_idx, int *val) +static inline int ads7846_calc_rt(struct ads7846 *ts, int x, int z1, int z2, + bool pd) { - return ADS7846_FILTER_OK; -} - -static int ads7846_get_value(struct ads7846 *ts, struct spi_message *m) -{ - int value; - struct spi_transfer *t = - list_entry(m->transfers.prev, struct spi_transfer, transfer_list); + int Rt; + bool validz = ((ts->model == 7846) || (ts->model == 7873)); - if (ts->model == 7845) { - value = be16_to_cpup((__be16 *)&(((char *)t->rx_buf)[1])); - } else { + if (!validz) { + /* ads7843 and ads7845 cannot measure pressure, so we fake it */ + if (pd) + Rt = ts->rt_threshold / 2; + else + Rt = ts->pressure_max; + } else if (likely(x && z1) && pd) { /* - * adjust: on-wire is a must-ignore bit, a BE12 value, then - * padding; built from two 8 bit values written msb-first. + * Compute touch pressure resistance using equation #2 + * To avoid a potential integer overflow on spurious + * measurements causing a false high pressure touch, we do the + * final division by 4096 in two steps to scale-down + * intermediate results with minimal loss of precision. + * We multiply by "x_plate_resistance" first, which has a + * guaranteed lower bound higher than the first partial + * divisor (16), so no resolution of the (z2-z1) term is lost. */ - value = be16_to_cpup((__be16 *)t->rx_buf); + Rt = z2; + Rt -= z1; + Rt *= ts->x_plate_ohms; + Rt = (Rt + 8) >> 4; /* Pre-shift to avoid integer overflow */ + Rt *= x; + Rt /= z1; + Rt = (Rt + 128) >> 8; /* Rest of total 12 bit shift */ + } else { + Rt = ts->pressure_max; } - - /* enforce ADC output is 12 bits width */ - return (value >> 3) & 0xfff; -} - -static void ads7846_update_value(struct spi_message *m, int val) -{ - struct spi_transfer *t = - list_entry(m->transfers.prev, struct spi_transfer, transfer_list); - - *(u16 *)t->rx_buf = val; + return Rt; } -static void ads7846_read_state(struct ads7846 *ts) +/* + * Touch sample filter + * + * This filter does several things: + * 1. Each sample is comprised of two measurements for X, Y and RT. + * Those pairs of samples are first analyzed to discriminate good from + * bad measurments. If they are both good, the average is taken for further + * processing. Bad samples are entirely discarded and TS goes back to + * detection in released state. + * 2. If the current sample is far away from the previous one, it is marked as + * suspicious and not taken into account in this report. The next sample is + * Then compared to both the suspicious sample and the one preceding it. + * If the new sample is closer to the older sample, the suspicious sample is + * discarded. Distance comparisions are done in manhattan distance for + * reasons of simplicity. + * 3. Up to 4 successive samples are held in a buffer. + * All samples in the buffer, except suspicious ones, are averaged and + * reported. + * + * Note that due to interaction between 2 and 3, even when a sample is marked + * as suspicious, a new report is generated, excluding this sample, but also + * excluding the oldest sample from the previous report, due to buffer size, + * so this report is not identical to the previous one! In case of a slide + * movement, it will be a little further into the sliding direction, which is + * a good thing. + */ +static void ads7846_vfilter_process(struct ads7846 *ts, int x, int y, int rt) { - struct ads7846_packet *packet = ts->packet; - struct spi_message *m; - int msg_idx = 0; - int val; - int action; - int error; - - while (msg_idx < ts->msg_count) { - - ts->wait_for_sync(); - - m = &ts->msg[msg_idx]; - error = spi_sync(ts->spi, m); - if (error) { - dev_err(&ts->spi->dev, "spi_sync --> %d\n", error); - packet->tc.ignore = true; - return; + struct ads7846_vfilter *vf = &ts->vfilter; + struct ads7846_vfilter_buf *this, *prev; + int dx, dy, dx2, dy2; + int t, i; + int ax, ay, ar; + + prev = &vf->fbuf[(vf->bidx - 1) & 3]; + this = &vf->fbuf[vf->bidx]; + dx = abs(prev->x - x); + dy = abs(prev->y - y); + + this->susp = false; + if (!prev->susp && vf->bcnt) { + if ((dx > ts->filter_tolerance) || (dy > ts->filter_tolerance)) { + this->susp = true; + dev_vdbg(&ts->spi->dev, + "marked suspect: dx: %d, dy: %d\n", dx, dy); } + } else if (prev->susp && (vf->bcnt > 1)) { + t = (vf->bidx - 2) & 3; + dx2 = abs(x - vf->fbuf[t].x); + dy2 = abs(y - vf->fbuf[t].y); + if ((dx2 + dy2) < (dx + dy)) { + /* Overwrite previous sample */ + vf->bidx = (vf->bidx - 1) & 3; + this = &vf->fbuf[vf->bidx]; + this->susp = false; + vf->bcnt--; + dev_vdbg(&ts->spi->dev, + "skipped over suspect: dx: %d, dy: %d, dx2: %d, dy2: %d\n", + dx, dy, dx2, dy2); + if ((dx2 > ts->filter_tolerance) || + (dy2 > ts->filter_tolerance)) { + this->susp = true; + dev_vdbg(&ts->spi->dev, + "marked suspect again: dx: %d, dy: %d\n", + dx2, dy2); + } + } + } + this->x = x; + this->y = y; + this->rt = rt; - /* - * Last message is power down request, no need to convert - * or filter the value. - */ - if (msg_idx < ts->msg_count - 1) { - - val = ads7846_get_value(ts, m); - - action = ts->filter(ts->filter_data, msg_idx, &val); - switch (action) { - case ADS7846_FILTER_REPEAT: - continue; - - case ADS7846_FILTER_IGNORE: - packet->tc.ignore = true; - msg_idx = ts->msg_count - 1; - continue; + vf->bidx = (vf->bidx + 1) & 3; + if (vf->bcnt < 4) + vf->bcnt++; - case ADS7846_FILTER_OK: - ads7846_update_value(m, val); - packet->tc.ignore = false; - msg_idx++; - break; + if ((vf->bcnt - this->susp) < 2) { + dev_vdbg(&ts->spi->dev, "counting: %d\n", vf->bcnt); + return; + } - default: - BUG(); - } - } else { - msg_idx++; - } + /* Average up to 4 most recent samples (3 if this is suspect) */ + if (this->susp) { + ax = ay = ar = 0; + } else { + ax = x; + ay = y; + ar = rt; } + for (t = 0; t < (vf->bcnt - 1); t++) { + i = (vf->bidx - vf->bcnt + t) & 3; + ax += vf->fbuf[i].x; + ay += vf->fbuf[i].y; + ar += vf->fbuf[i].rt; + } + ax /= (vf->bcnt - this->susp); + ay /= (vf->bcnt - this->susp); + ar /= (vf->bcnt - this->susp); + + ts->touch.x = ax; + ts->touch.y = ay; + ts->touch.rt = ar; + ts->touch_valid = true; } -static void ads7846_report_state(struct ads7846 *ts) +static bool ads7846_read_state(struct ads7846 *ts) { - struct ads7846_packet *packet = ts->packet; - unsigned int Rt; - u16 x, y, z1, z2; + int error; + char *rxbuf = &ts->rx_buf[0]; + int x0, x1, y0, y1, z10, z11, z20, z21; + int rt0, rt1; + int dx, dy; + int skp = ts->skip_samples; + int stride = (skp + 2) * 2; /* Total samples = skip_samples + 2 */ + int offs; + bool pd; + + ts->wait_for_sync(); + + mutex_lock(&ts->lock); + pd = get_pendown_state(ts); + error = spi_sync(ts->spi, &ts->msg); + mutex_unlock(&ts->lock); + if (error) { + dev_err(&ts->spi->dev, "spi_sync --> %d\n", error); + ts->touch_valid = false; + return false; + } /* - * ads7846_get_value() does in-place conversion (including byte swap) - * from on-the-wire format as part of debouncing to get stable - * readings. + * Extract samples from RX buffer. + * RX data starts with a dummy byte (0) and then contains the sample + * data in BE16 ordering (every 2 bytes, one sample). + * First all Y-samples (2 + skip_samples total), then the same amount + * of X-samples, then same amount of Z1 and finally Z2. */ - if (ts->model == 7845) { - x = *(u16 *)packet->tc.x_buf; - y = *(u16 *)packet->tc.y_buf; - z1 = 0; - z2 = 0; + offs = skp * 2 + 1; + y0 = (be16_to_cpup((__be16 *)&rxbuf[offs]) >> 3) & 0x0fff; + x0 = (be16_to_cpup((__be16 *)&rxbuf[offs + stride]) >> 3) & 0x0fff; + z10 = (be16_to_cpup((__be16 *)&rxbuf[offs + stride * 2]) >> 3) & 0x0fff; + z20 = (be16_to_cpup((__be16 *)&rxbuf[offs + stride * 3]) >> 3) & 0x0fff; + + offs = skp * 2 + 3; + y1 = (be16_to_cpup((__be16 *)&rxbuf[offs]) >> 3) & 0x0fff; + x1 = (be16_to_cpup((__be16 *)&rxbuf[offs + stride]) >> 3) & 0x0fff; + z11 = (be16_to_cpup((__be16 *)&rxbuf[offs + stride * 2]) >> 3) & 0x0fff; + z21 = (be16_to_cpup((__be16 *)&rxbuf[offs + stride * 3]) >> 3) & 0x0fff; + + dx = abs(x0 - x1); + dy = abs(y0 - y1); + rt0 = ads7846_calc_rt(ts, x0, z10, z20, pd); + rt1 = ads7846_calc_rt(ts, x1, z11, z21, pd); + + dev_vdbg(&ts->spi->dev, + "x0: %d, x1: %d, y0: %d, y1: %d, z1: %d, z2: %d, rt0: %d, rt2: %d pd: %d\n", + x0, x1, y0, y1, z11, z21, rt0, rt1, pd); + if ((rt0 < ts->rt_threshold) && (rt1 < ts->rt_threshold) && + (dx < ts->filter_tolerance) && + (dy < ts->filter_tolerance)) { + ads7846_vfilter_process(ts, (x0 + x1) / 2, (y0 + y1) / 2, + (rt0 + rt1) / 2); } else { - x = packet->tc.x; - y = packet->tc.y; - z1 = packet->tc.z1; - z2 = packet->tc.z2; + dev_vdbg(&ts->spi->dev, "Sample not good: dx: %d, dy: %d\n", + dx, dy); } - /* range filtering */ - if (x == MAX_12BIT) - x = 0; - - if (ts->model == 7843) { - Rt = ts->pressure_max / 2; - } else if (ts->model == 7845) { - if (get_pendown_state(ts)) - Rt = ts->pressure_max / 2; - else - Rt = 0; - dev_vdbg(&ts->spi->dev, "x/y: %d/%d, PD %d\n", x, y, Rt); - } else if (likely(x && z1)) { - /* compute touch pressure resistance using equation #2 */ - Rt = z2; - Rt -= z1; - Rt *= x; - Rt *= ts->x_plate_ohms; - Rt /= z1; - Rt = (Rt + 2047) >> 12; + /* Pendown detection glitch filter */ + if (pd) { + ts->penup_count = 0; } else { - Rt = 0; + ads7846_vfilter_reset(ts); + if (ts->penup_count++ < ts->pendown_glitch_max) + pd = true; } + return pd; +} + +static int ads7846_report_state(struct ads7846 *ts) +{ + unsigned int Rt = ts->touch.rt; + unsigned int x = ts->touch.x; + unsigned int y = ts->touch.y; /* * Sample found inconsistent by debouncing or pressure is beyond * the maximum. Don't report it to user space, repeat at least * once more the measurement */ - if (packet->tc.ignore || Rt > ts->pressure_max) { - dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n", - packet->tc.ignore, Rt); - return; - } - - /* - * Maybe check the pendown state before reporting. This discards - * false readings when the pen is lifted. - */ - if (ts->penirq_recheck_delay_usecs) { - udelay(ts->penirq_recheck_delay_usecs); - if (!get_pendown_state(ts)) - Rt = 0; + if (!ts->touch_valid || Rt > ts->pressure_max) { + dev_vdbg(&ts->spi->dev, "valid: %d pressure: %d\n", + ts->touch_valid, Rt); + return 0; } /* @@ -849,7 +818,10 @@ static void ads7846_report_state(struct ads7846 *ts) input_sync(input); dev_vdbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt); + return 1; } + ts->touch_valid = false; /* Mark as processed */ + return 0; } static irqreturn_t ads7846_hard_irq(int irq, void *handle) @@ -863,21 +835,31 @@ static irqreturn_t ads7846_hard_irq(int irq, void *handle) static irqreturn_t ads7846_irq(int irq, void *handle) { struct ads7846 *ts = handle; + unsigned int rj = 0; + bool pd = true; + unsigned int rjperiod = msecs_to_jiffies(ts->report_period); + unsigned int sjperiod = msecs_to_jiffies(ts->sample_period); /* Start with a small delay before checking pendown state */ msleep(TS_POLL_DELAY); - while (!ts->stopped && get_pendown_state(ts)) { + ts->touch_valid = false; + while (!ts->stopped && pd) { /* pen is down, continue with the measurement */ - ads7846_read_state(ts); + pd = ads7846_read_state(ts); - if (!ts->stopped) - ads7846_report_state(ts); + if ((!ts->stopped) && time_after(jiffies, rj)) { + if (ads7846_report_state(ts)) + rj = jiffies + rjperiod; + } - wait_event_timeout(ts->wait, ts->stopped, - msecs_to_jiffies(TS_POLL_PERIOD)); + if (ts->pendown) + wait_event_timeout(ts->wait, ts->stopped, sjperiod); + else + wait_event_timeout(ts->wait, ts->stopped, 1); } + ads7846_vfilter_reset(ts); if (ts->pendown && !ts->stopped) { struct input_dev *input = ts->input; @@ -978,16 +960,20 @@ static int ads7846_setup_pendown(struct spi_device *spi, } /* - * Set up the transfers to read touchscreen state; this assumes we - * use formula #2 for pressure, not #3. + * Setup the command buffer to read out all touchscreen measurements. + * All commands will be transmitted in one single SPI transfer in order to + * reduce scheduling overhead, making use of the 16 clocks-per-conversion + * timing mode of the ADS784x. */ static void ads7846_setup_spi_msg(struct ads7846 *ts, const struct ads7846_platform_data *pdata) { - struct spi_message *m = &ts->msg[0]; - struct spi_transfer *x = ts->xfer; - struct ads7846_packet *packet = ts->packet; int vref = pdata->keep_vref_on; + char *txbuf = &ts->tx_buf[0]; + unsigned int skp = ts->skip_samples; + unsigned int stride = (skp + 2) * 2; /* total samples = skip + 2 */ + unsigned int i; + unsigned int offs; if (ts->model == 7873) { /* @@ -999,185 +985,48 @@ static void ads7846_setup_spi_msg(struct ads7846 *ts, vref = 0; } - ts->msg_count = 1; - spi_message_init(m); - m->context = ts; - - if (ts->model == 7845) { - packet->read_y_cmd[0] = READ_Y(vref); - packet->read_y_cmd[1] = 0; - packet->read_y_cmd[2] = 0; - x->tx_buf = &packet->read_y_cmd[0]; - x->rx_buf = &packet->tc.y_buf[0]; - x->len = 3; - spi_message_add_tail(x, m); - } else { - /* y- still on; turn on only y+ (and ADC) */ - packet->read_y = READ_Y(vref); - x->tx_buf = &packet->read_y; - x->len = 1; - spi_message_add_tail(x, m); - - x++; - x->rx_buf = &packet->tc.y; - x->len = 2; - spi_message_add_tail(x, m); - } + memset(txbuf, 0, sizeof(ts->tx_buf)); /* - * The first sample after switching drivers can be low quality; - * optionally discard it, using a second one after the signals - * have had enough time to stabilize. + * Fill the TX buffer with command data. The ADS874x can process 1 + * command every 2 bytes. Command bytes are filled starting at + * index 0 of the TX buffer. + * Every measurement command will be repeated (2 + skip_samples) times. + * First Y measurements, then X, Z1 and finally Z2. + * The skip_samples parameter is used to insert up to 2 dummy + * measurements before the 2 real measurements. The results of these + * dummy measurements are skipped. The idea is to give the signals time + * to settle after switching the plates. + * The last 2 consecutive measurements are always used for validation + * and filtering. */ - if (pdata->settle_delay_usecs) { - x->delay_usecs = pdata->settle_delay_usecs; - - x++; - x->tx_buf = &packet->read_y; - x->len = 1; - spi_message_add_tail(x, m); - - x++; - x->rx_buf = &packet->tc.y; - x->len = 2; - spi_message_add_tail(x, m); - } - - ts->msg_count++; - m++; - spi_message_init(m); - m->context = ts; - - if (ts->model == 7845) { - x++; - packet->read_x_cmd[0] = READ_X(vref); - packet->read_x_cmd[1] = 0; - packet->read_x_cmd[2] = 0; - x->tx_buf = &packet->read_x_cmd[0]; - x->rx_buf = &packet->tc.x_buf[0]; - x->len = 3; - spi_message_add_tail(x, m); - } else { - /* turn y- off, x+ on, then leave in lowpower */ - x++; - packet->read_x = READ_X(vref); - x->tx_buf = &packet->read_x; - x->len = 1; - spi_message_add_tail(x, m); - - x++; - x->rx_buf = &packet->tc.x; - x->len = 2; - spi_message_add_tail(x, m); - } - - /* ... maybe discard first sample ... */ - if (pdata->settle_delay_usecs) { - x->delay_usecs = pdata->settle_delay_usecs; - - x++; - x->tx_buf = &packet->read_x; - x->len = 1; - spi_message_add_tail(x, m); - - x++; - x->rx_buf = &packet->tc.x; - x->len = 2; - spi_message_add_tail(x, m); - } - - /* turn y+ off, x- on; we'll use formula #2 */ - if (ts->model == 7846) { - ts->msg_count++; - m++; - spi_message_init(m); - m->context = ts; - - x++; - packet->read_z1 = READ_Z1(vref); - x->tx_buf = &packet->read_z1; - x->len = 1; - spi_message_add_tail(x, m); - - x++; - x->rx_buf = &packet->tc.z1; - x->len = 2; - spi_message_add_tail(x, m); - - /* ... maybe discard first sample ... */ - if (pdata->settle_delay_usecs) { - x->delay_usecs = pdata->settle_delay_usecs; - - x++; - x->tx_buf = &packet->read_z1; - x->len = 1; - spi_message_add_tail(x, m); - - x++; - x->rx_buf = &packet->tc.z1; - x->len = 2; - spi_message_add_tail(x, m); - } - - ts->msg_count++; - m++; - spi_message_init(m); - m->context = ts; - - x++; - packet->read_z2 = READ_Z2(vref); - x->tx_buf = &packet->read_z2; - x->len = 1; - spi_message_add_tail(x, m); - - x++; - x->rx_buf = &packet->tc.z2; - x->len = 2; - spi_message_add_tail(x, m); - - /* ... maybe discard first sample ... */ - if (pdata->settle_delay_usecs) { - x->delay_usecs = pdata->settle_delay_usecs; - - x++; - x->tx_buf = &packet->read_z2; - x->len = 1; - spi_message_add_tail(x, m); - - x++; - x->rx_buf = &packet->tc.z2; - x->len = 2; - spi_message_add_tail(x, m); + for (i = 0; i < (skp + 2); i++) { + offs = i * 2; + txbuf[offs] = READ_Y(vref); + txbuf[offs + stride] = READ_X(vref); + if (ts->model == 7846) { + txbuf[offs + stride * 2] = READ_Z1(vref); + txbuf[offs + stride * 3] = READ_Z2(vref); } } + if (ts->model == 7846) + txbuf[stride * 4] = PWRDOWN; + else + txbuf[stride * 2] = PWRDOWN; - /* power down */ - ts->msg_count++; - m++; - spi_message_init(m); - m->context = ts; - - if (ts->model == 7845) { - x++; - packet->pwrdown_cmd[0] = PWRDOWN; - packet->pwrdown_cmd[1] = 0; - packet->pwrdown_cmd[2] = 0; - x->tx_buf = &packet->pwrdown_cmd[0]; - x->len = 3; - } else { - x++; - packet->pwrdown = PWRDOWN; - x->tx_buf = &packet->pwrdown; - x->len = 1; - spi_message_add_tail(x, m); - - x++; - x->rx_buf = &packet->dummy; - x->len = 2; - } + spi_message_init(&ts->msg); + ts->msg.context = ts; - CS_CHANGE(*x); - spi_message_add_tail(x, m); + ts->xfer.rx_buf = &ts->rx_buf[0]; + ts->xfer.tx_buf = txbuf; + if (ts->model == 7846) + ts->xfer.len = stride * 4 + 3; + else + ts->xfer.len = stride * 2 + 3; + ts->xfer.bits_per_word = 8; + + CS_CHANGE(ts->xfer); + spi_message_add_tail(&ts->xfer, &ts->msg); } #ifdef CONFIG_OF @@ -1223,11 +1072,6 @@ static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev) pdata->invert_x = of_property_read_bool(node, "ti,invert_x"); pdata->invert_y = of_property_read_bool(node, "ti,invert_y"); - of_property_read_u16(node, "ti,settle-delay-usec", - &pdata->settle_delay_usecs); - of_property_read_u16(node, "ti,penirq-recheck-delay-usecs", - &pdata->penirq_recheck_delay_usecs); - of_property_read_u16(node, "ti,x-plate-ohms", &pdata->x_plate_ohms); of_property_read_u16(node, "ti,y-plate-ohms", &pdata->y_plate_ohms); @@ -1239,13 +1083,21 @@ static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev) of_property_read_u16(node, "ti,pressure-min", &pdata->pressure_min); of_property_read_u16(node, "ti,pressure-max", &pdata->pressure_max); - of_property_read_u16(node, "ti,debounce-max", &pdata->debounce_max); - of_property_read_u16(node, "ti,debounce-tol", &pdata->debounce_tol); - of_property_read_u16(node, "ti,debounce-rep", &pdata->debounce_rep); - of_property_read_u32(node, "ti,pendown-gpio-debounce", &pdata->gpio_pendown_debounce); + of_property_read_u32(node, "ti,skip-samples", &pdata->skip_samples); + of_property_read_u32(node, "ti,sample-period-msecs", + &pdata->sample_period); + of_property_read_u32(node, "ti,report-period-msecs", + &pdata->report_period); + of_property_read_u32(node, "ti,filter-tolerance", + &pdata->filter_tolerance); + of_property_read_u32(node, "ti,touch-resistance-threshold", + &pdata->rt_threshold); + of_property_read_u32(node, "ti,pendown-glitch-max", + &pdata->pendown_glitch_max); + pdata->wakeup = of_property_read_bool(node, "wakeup-source") || of_property_read_bool(node, "linux,wakeup"); @@ -1265,7 +1117,6 @@ static int ads7846_probe(struct spi_device *spi) { const struct ads7846_platform_data *pdata; struct ads7846 *ts; - struct ads7846_packet *packet; struct input_dev *input_dev; unsigned long irq_flags; int err; @@ -1294,16 +1145,14 @@ static int ads7846_probe(struct spi_device *spi) return err; ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL); - packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL); input_dev = input_allocate_device(); - if (!ts || !packet || !input_dev) { + if (!ts || !input_dev) { err = -ENOMEM; goto err_free_mem; } spi_set_drvdata(spi, ts); - ts->packet = packet; ts->spi = spi; ts->input = input_dev; @@ -1323,40 +1172,23 @@ static int ads7846_probe(struct spi_device *spi) ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; ts->pressure_max = pdata->pressure_max ? : ~0; + ts->skip_samples = (pdata->skip_samples <= MAX_SKIP_SAMPLES) ? + pdata->skip_samples : 0; + ts->sample_period = pdata->sample_period ? : 10; + ts->report_period = pdata->report_period ? : 10; + ts->filter_tolerance = pdata->filter_tolerance ? : 80; + ts->rt_threshold = pdata->rt_threshold ? : 3500; + ts->pendown_glitch_max = pdata->pendown_glitch_max; ts->vref_mv = pdata->vref_mv; ts->swap_xy = pdata->swap_xy; ts->invert_x = pdata->invert_x; ts->invert_y = pdata->invert_y; - if (pdata->filter != NULL) { - if (pdata->filter_init != NULL) { - err = pdata->filter_init(pdata, &ts->filter_data); - if (err < 0) - goto err_free_mem; - } - ts->filter = pdata->filter; - ts->filter_cleanup = pdata->filter_cleanup; - } else if (pdata->debounce_max) { - ts->debounce_max = pdata->debounce_max; - if (ts->debounce_max < 2) - ts->debounce_max = 2; - ts->debounce_tol = pdata->debounce_tol; - ts->debounce_rep = pdata->debounce_rep; - ts->filter = ads7846_debounce_filter; - ts->filter_data = ts; - } else { - ts->filter = ads7846_no_filter; - } - err = ads7846_setup_pendown(spi, ts, pdata); if (err) goto err_cleanup_filter; - if (pdata->penirq_recheck_delay_usecs) - ts->penirq_recheck_delay_usecs = - pdata->penirq_recheck_delay_usecs; - ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync; snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev)); @@ -1424,7 +1256,7 @@ static int ads7846_probe(struct spi_device *spi) * the touchscreen, in case it's not connected. */ if (ts->model == 7845) - ads7845_read12_ser(&spi->dev, PWRDOWN); + ads7846_read12_ser(&spi->dev, PWRDOWN); else (void) ads7846_read12_ser(&spi->dev, READ_12BIT_SER(vaux)); @@ -1465,7 +1297,6 @@ static int ads7846_probe(struct spi_device *spi) ts->filter_cleanup(ts->filter_data); err_free_mem: input_free_device(input_dev); - kfree(packet); kfree(ts); return err; } @@ -1496,7 +1327,6 @@ static int ads7846_remove(struct spi_device *spi) if (ts->filter_cleanup) ts->filter_cleanup(ts->filter_data); - kfree(ts->packet); kfree(ts); dev_dbg(&spi->dev, "unregistered touchscreen\n"); diff --git a/include/linux/spi/ads7846.h b/include/linux/spi/ads7846.h index 6b8022c625a3..95d616797f52 100644 --- a/include/linux/spi/ads7846.h +++ b/include/linux/spi/ads7846.h @@ -23,19 +23,6 @@ struct ads7846_platform_data { bool invert_x; /* Invert x axis */ bool invert_y; /* Invert y axis */ - /* Settling time of the analog signals; a function of Vcc and the - * capacitance on the X/Y drivers. If set to non-zero, two samples - * are taken with settle_delay us apart, and the second one is used. - * ~150 uSec with 0.01uF caps. - */ - u16 settle_delay_usecs; - - /* If set to non-zero, after samples are taken this delay is applied - * and penirq is rechecked, to help avoid false events. This value - * is affected by the material used to build the touch layer. - */ - u16 penirq_recheck_delay_usecs; - u16 x_plate_ohms; u16 y_plate_ohms; @@ -43,15 +30,19 @@ struct ads7846_platform_data { u16 y_min, y_max; u16 pressure_min, pressure_max; - u16 debounce_max; /* max number of additional readings - * per sample */ - u16 debounce_tol; /* tolerance used for filtering */ - u16 debounce_rep; /* additional consecutive good readings - * required after the first two */ int gpio_pendown; /* the GPIO used to decide the pendown * state if get_pendown_state == NULL */ int gpio_pendown_debounce; /* platform specific debounce time for * the gpio_pendown */ + unsigned int skip_samples; /* Skip n (<=2) samples after switching + * plates. */ + unsigned int rt_threshold; /* Rt threshold for valid touch */ + unsigned int filter_tolerance; /* Filter X/Y tolerance (raw) */ + unsigned int sample_period; /* Touchscreen sample period in ms */ + unsigned int report_period; /* Touchscreen reporting period in ms. + * Must be a multiple of sample_period */ + unsigned int pendown_glitch_max; /* Maximum samples of pendown signal + * glitch */ int (*get_pendown_state)(void); int (*filter_init) (const struct ads7846_platform_data *pdata, void **filter_data);