[PATCH 3/4] Input: edt-ft5x06 - allow reading the touch frame one register at a time

Alexandre Hamamdjian <[email protected]>
Newsgroups org.kernel.feeds.b4-sent,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-input,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
From: Teguh Sobirin <[email protected]>

Every touch interrupt reads the whole touch frame in a single i2c block
read via regmap_bulk_read(). On some boards the i2c controller the panel
is wired to cannot sustain that multi-byte transfer: on the AYANEO Pocket
DS the FocalTech FT5426 sits on a marginal Qualcomm GENI bus that
intermittently aborts a long read with -EAGAIN or -ETIMEDOUT, and the
GENI controller has no bus recovery, so the block read fails on nearly
every interrupt and the panel is unusable.

Honour the "no-regmap-bulk-read" property. When set, the driver reads the
frame one register at a time with a short retry on the transient bus
errors, keeping each transfer small enough to complete. Boards on a
healthy bus keep using the single bulk transfer and are unaffected.

Signed-off-by: Teguh Sobirin <[email protected]>
Co-developed-by: Alexandre Hamamdjian <[email protected]>
Signed-off-by: Alexandre Hamamdjian <[email protected]>
---
 drivers/input/touchscreen/edt-ft5x06.c | 37 ++++++++++++++++++++++++++++++++--
 1 file changed, 35 insertions(+), 2 deletions(-)

diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index d6c3d033b83d..ac61ac44fd64 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -146,6 +146,7 @@ struct edt_ft5x06_ts_data {
 	enum edt_ver version;
 	unsigned int crc_errors;
 	unsigned int header_errors;
+	bool no_regmap_bulk_read;
 };
 
 struct edt_i2c_chip_data {
@@ -295,6 +296,31 @@ static const struct regmap_config edt_M06_i2c_regmap_config = {
 	.write = edt_M06_i2c_write,
 };
 
+static int edt_ft5x06_bulk_read(struct regmap *map, unsigned int start,
+				void *val, size_t len)
+{
+	u8 *dst = val;
+	size_t off;
+
+	for (off = 0; off < len; off++) {
+		unsigned int v;
+		int ret, tries;
+
+		for (tries = 0; tries < 3; tries++) {
+			ret = regmap_read(map, start + off, &v);
+			if (!ret)
+				break;
+			if (ret == -ETIMEDOUT || ret == -EAGAIN)
+				usleep_range(2000, 4000);
+		}
+		if (ret)
+			return ret;
+		dst[off] = v;
+	}
+
+	return 0;
+}
+
 static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
 {
 	struct edt_ft5x06_ts_data *tsdata = dev_id;
@@ -304,8 +330,12 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
 	int error;
 
 	memset(rdbuf, 0, sizeof(rdbuf));
-	error = regmap_bulk_read(tsdata->regmap, tsdata->tdata_cmd, rdbuf,
-				 tsdata->tdata_len);
+	if (tsdata->no_regmap_bulk_read)
+		error = edt_ft5x06_bulk_read(tsdata->regmap, tsdata->tdata_cmd,
+					     rdbuf, tsdata->tdata_len);
+	else
+		error = regmap_bulk_read(tsdata->regmap, tsdata->tdata_cmd,
+					 rdbuf, tsdata->tdata_len);
 	if (error) {
 		dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n",
 				    error);
@@ -1212,6 +1242,9 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client)
 		return error;
 	}
 
+	tsdata->no_regmap_bulk_read =
+		device_property_read_bool(&client->dev, "no-regmap-bulk-read");
+
 	/*
 	 * Check which sleep modes we can support. Power-off requires the
 	 * reset-pin to ensure correct power-down/power-up behaviour. Start with

-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.