This series cleans up the driver a bit and implements changes needed to
support EKTF3624-based touchscreen used in Asus TF300T and similar
Tegra3-based tablets.
---
v2: extended with Dmitry's patches (replaced v1 patches 3 and 4)
v3: rebased for v5.7-rc1
Dmitry Osipenko (3):
input: elants: support 0x66 reply opcode for reporting touches
dt-bindings: input: elants-i2c: Document common touchscreen properties
dt-bindings: input: elants-i2c: Document eKTF3624
Michał Mirosław (6):
input: elants: document some registers and values
input: elants: support old touch report format
input: elants: remove unused axes
input: elants: override touchscreen info with DT properties
input: elants: refactor elants_i2c_execute_command()
input: elants: read touchscreen size for EKTF3624
.../devicetree/bindings/input/elants_i2c.txt | 6 +-
drivers/input/touchscreen/elants_i2c.c | 365 ++++++++++++------
2 files changed, 242 insertions(+), 129 deletions(-)
--
2.20.1
Apply some DRY-ing to elants_i2c_execute_command() callers. This pulls
polling and error printk()s into a single function.
Signed-off-by: Michał Mirosław <[email protected]>
---
drivers/input/touchscreen/elants_i2c.c | 189 +++++++++++++------------
1 file changed, 96 insertions(+), 93 deletions(-)
diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
index 87d686ce08f2..b0f083f7f2a9 100644
--- a/drivers/input/touchscreen/elants_i2c.c
+++ b/drivers/input/touchscreen/elants_i2c.c
@@ -205,7 +205,8 @@ static int elants_i2c_read(struct i2c_client *client, void *data, size_t size)
static int elants_i2c_execute_command(struct i2c_client *client,
const u8 *cmd, size_t cmd_size,
- u8 *resp, size_t resp_size)
+ u8 *resp, size_t resp_size,
+ int retries, const char *cmd_name)
{
struct i2c_msg msgs[2];
int ret;
@@ -225,30 +226,55 @@ static int elants_i2c_execute_command(struct i2c_client *client,
break;
default:
- dev_err(&client->dev, "%s: invalid command %*ph\n",
- __func__, (int)cmd_size, cmd);
+ dev_err(&client->dev, "(%s): invalid command: %*ph\n",
+ cmd_name, (int)cmd_size, cmd);
return -EINVAL;
}
- msgs[0].addr = client->addr;
- msgs[0].flags = client->flags & I2C_M_TEN;
- msgs[0].len = cmd_size;
- msgs[0].buf = (u8 *)cmd;
+ for (;;) {
+ msgs[0].addr = client->addr;
+ msgs[0].flags = client->flags & I2C_M_TEN;
+ msgs[0].len = cmd_size;
+ msgs[0].buf = (u8 *)cmd;
- msgs[1].addr = client->addr;
- msgs[1].flags = client->flags & I2C_M_TEN;
- msgs[1].flags |= I2C_M_RD;
- msgs[1].len = resp_size;
- msgs[1].buf = resp;
+ msgs[1].addr = client->addr;
+ msgs[1].flags = client->flags & I2C_M_TEN;
+ msgs[1].flags |= I2C_M_RD;
+ msgs[1].len = resp_size;
+ msgs[1].buf = resp;
- ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
- if (ret < 0)
- return ret;
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0) {
+ if (--retries > 0) {
+ dev_dbg(&client->dev,
+ "(%s) I2C transfer failed: %d (retrying)\n",
+ cmd_name, ret);
+ continue;
+ }
- if (ret != ARRAY_SIZE(msgs) || resp[FW_HDR_TYPE] != expected_response)
- return -EIO;
+ dev_err(&client->dev,
+ "(%s) I2C transfer failed: %d\n",
+ cmd_name, ret);
+ return ret;
+ }
- return 0;
+ if (ret != ARRAY_SIZE(msgs) ||
+ resp[FW_HDR_TYPE] != expected_response) {
+ if (--retries > 0) {
+ dev_dbg(&client->dev,
+ "(%s) unexpected response: %*ph (retrying)\n",
+ cmd_name, ret, resp);
+ continue;
+ }
+
+ dev_err(&client->dev,
+ "(%s) unexpected response: %*ph\n",
+ cmd_name, ret, resp);
+ return -EIO;
+ }
+
+ return --retries;
+ }
}
static int elants_i2c_calibrate(struct elants_data *ts)
@@ -321,27 +347,20 @@ static u16 elants_i2c_parse_version(u8 *buf)
static int elants_i2c_query_hw_version(struct elants_data *ts)
{
struct i2c_client *client = ts->client;
- int error, retry_cnt;
+ int retry_cnt = MAX_RETRIES;
const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_FW_ID, 0x00, 0x01 };
u8 resp[HEADER_SIZE];
- for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
- error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
- resp, sizeof(resp));
- if (!error) {
- ts->hw_version = elants_i2c_parse_version(resp);
- if (ts->hw_version != 0xffff)
- return 0;
- }
+ while (retry_cnt) {
+ retry_cnt = elants_i2c_execute_command(client, cmd, sizeof(cmd),
+ resp, sizeof(resp),
+ retry_cnt, "read fw id");
+ if (retry_cnt < 0)
+ return retry_cnt;
- dev_dbg(&client->dev, "read fw id error=%d, buf=%*phC\n",
- error, (int)sizeof(resp), resp);
- }
-
- if (error) {
- dev_err(&client->dev,
- "Failed to read fw id: %d\n", error);
- return error;
+ ts->hw_version = elants_i2c_parse_version(resp);
+ if (ts->hw_version != 0xffff)
+ return 0;
}
dev_err(&client->dev, "Invalid fw id: %#04x\n", ts->hw_version);
@@ -352,26 +371,28 @@ static int elants_i2c_query_hw_version(struct elants_data *ts)
static int elants_i2c_query_fw_version(struct elants_data *ts)
{
struct i2c_client *client = ts->client;
- int error, retry_cnt;
+ int retry_cnt = MAX_RETRIES;
const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_FW_VER, 0x00, 0x01 };
u8 resp[HEADER_SIZE];
- for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
- error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
- resp, sizeof(resp));
- if (!error) {
- ts->fw_version = elants_i2c_parse_version(resp);
- if (ts->fw_version != 0x0000 &&
- ts->fw_version != 0xffff)
- return 0;
- }
+ while (retry_cnt) {
+ retry_cnt = elants_i2c_execute_command(client, cmd,
+ sizeof(cmd),
+ resp, sizeof(resp),
+ retry_cnt,
+ "read fw version");
+ if (retry_cnt < 0)
+ return retry_cnt;
- dev_dbg(&client->dev, "read fw version error=%d, buf=%*phC\n",
- error, (int)sizeof(resp), resp);
+ ts->fw_version = elants_i2c_parse_version(resp);
+ if (ts->fw_version != 0x0000 && ts->fw_version != 0xffff)
+ return 0;
+
+ dev_dbg(&client->dev, "(read fw version) resp %*phC\n",
+ (int)sizeof(resp), resp);
}
- dev_err(&client->dev,
- "Failed to read fw version or fw version is invalid\n");
+ dev_err(&client->dev, "Invalid fw ver: %#04x\n", ts->fw_version);
return -EINVAL;
}
@@ -379,25 +400,20 @@ static int elants_i2c_query_fw_version(struct elants_data *ts)
static int elants_i2c_query_test_version(struct elants_data *ts)
{
struct i2c_client *client = ts->client;
- int error, retry_cnt;
+ int error;
u16 version;
const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_TEST_VER, 0x00, 0x01 };
u8 resp[HEADER_SIZE];
- for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
- error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
- resp, sizeof(resp));
- if (!error) {
- version = elants_i2c_parse_version(resp);
- ts->test_version = version >> 8;
- ts->solution_version = version & 0xff;
+ error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
+ resp, sizeof(resp), MAX_RETRIES,
+ "read test version");
+ if (error >= 0) {
+ version = elants_i2c_parse_version(resp);
+ ts->test_version = version >> 8;
+ ts->solution_version = version & 0xff;
- return 0;
- }
-
- dev_dbg(&client->dev,
- "read test version error rc=%d, buf=%*phC\n",
- error, (int)sizeof(resp), resp);
+ return 0;
}
dev_err(&client->dev, "Failed to read test version\n");
@@ -414,13 +430,10 @@ static int elants_i2c_query_bc_version(struct elants_data *ts)
int error;
error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
- resp, sizeof(resp));
- if (error) {
- dev_err(&client->dev,
- "read BC version error=%d, buf=%*phC\n",
- error, (int)sizeof(resp), resp);
+ resp, sizeof(resp), 1,
+ "read BC version");
+ if (error)
return error;
- }
version = elants_i2c_parse_version(resp);
ts->bc_version = version >> 8;
@@ -452,12 +465,10 @@ static int elants_i2c_query_ts_info(struct elants_data *ts)
error = elants_i2c_execute_command(client,
get_resolution_cmd,
sizeof(get_resolution_cmd),
- resp, sizeof(resp));
- if (error) {
- dev_err(&client->dev, "get resolution command failed: %d\n",
- error);
+ resp, sizeof(resp), 1,
+ "get resolution");
+ if (error)
return error;
- }
rows = resp[2] + resp[6] + resp[10];
cols = resp[3] + resp[7] + resp[11];
@@ -465,36 +476,29 @@ static int elants_i2c_query_ts_info(struct elants_data *ts)
/* Process mm_to_pixel information */
error = elants_i2c_execute_command(client,
get_osr_cmd, sizeof(get_osr_cmd),
- resp, sizeof(resp));
- if (error) {
- dev_err(&client->dev, "get osr command failed: %d\n",
- error);
+ resp, sizeof(resp), 1, "get osr");
+ if (error)
return error;
- }
osr = resp[3];
error = elants_i2c_execute_command(client,
get_physical_scan_cmd,
sizeof(get_physical_scan_cmd),
- resp, sizeof(resp));
- if (error) {
- dev_err(&client->dev, "get physical scan command failed: %d\n",
- error);
+ resp, sizeof(resp), 1,
+ "get physical scan");
+ if (error)
return error;
- }
phy_x = get_unaligned_be16(&resp[2]);
error = elants_i2c_execute_command(client,
get_physical_drive_cmd,
sizeof(get_physical_drive_cmd),
- resp, sizeof(resp));
- if (error) {
- dev_err(&client->dev, "get physical drive command failed: %d\n",
- error);
+ resp, sizeof(resp), 1,
+ "get physical drive");
+ if (error)
return error;
- }
phy_y = get_unaligned_be16(&resp[2]);
@@ -649,11 +653,10 @@ static int elants_i2c_validate_remark_id(struct elants_data *ts,
/* Compare TS Remark ID and FW Remark ID */
error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
- resp, sizeof(resp));
- if (error) {
- dev_err(&client->dev, "failed to query Remark ID: %d\n", error);
+ resp, sizeof(resp),
+ 1, "read Remark ID");
+ if (error < 0)
return error;
- }
ts_remark_id = get_unaligned_be16(&resp[3]);
--
2.20.1
From: Dmitry Osipenko <[email protected]>
The eKTF3624 hardware is similar to eKTH3500.
Signed-off-by: Dmitry Osipenko <[email protected]>
Reviewed-by: Michał Mirosław <[email protected]>
Acked-by: Rob Herring <[email protected]>
Signed-off-by: Michał Mirosław <[email protected]>
---
Documentation/devicetree/bindings/input/elants_i2c.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/input/elants_i2c.txt b/Documentation/devicetree/bindings/input/elants_i2c.txt
index 45fab32bbc19..1bc60303f0ea 100644
--- a/Documentation/devicetree/bindings/input/elants_i2c.txt
+++ b/Documentation/devicetree/bindings/input/elants_i2c.txt
@@ -1,7 +1,7 @@
Elantech I2C Touchscreen
Required properties:
-- compatible: must be "elan,ekth3500".
+- compatible: must be "elan,ekth3500" or "elan,ektf3624".
- reg: I2C address of the chip.
- interrupts: interrupt to which the chip is connected (see interrupt
binding[0]).
--
2.20.1
From: Dmitry Osipenko <[email protected]>
eKTF3624 touchscreen firmware uses two variants of the reply opcodes for
reporting touch events: one is 0x63 (used by older firmware) and other is
0x66 (used by newer firmware). The 0x66 variant is equal to 0x63 of
eKTH3500, while 0x63 needs small adjustment of the touch pressure value.
Nexus 7 tablet device has eKTF3624 touchscreen and it uses 0x66 opcode for
reporting touch events, let's support it now. Other devices, eg. ASUS TF300T,
use 0x63.
Note: CMD_HEADER_REK is used for replying to calibration requests, it has
the same 0x66 opcode number which eKTF3624 uses for reporting touches.
The calibration replies are handled separately from the the rest of the
commands in the driver by entering into ELAN_WAIT_RECALIBRATION state
and thus this change shouldn't change the old behavior.
Signed-off-by: Dmitry Osipenko <[email protected]>
Tested-by: Michał Mirosław <[email protected]>
Signed-off-by: Michał Mirosław <[email protected]>
---
drivers/input/touchscreen/elants_i2c.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
index 2b936e920874..9751139e8507 100644
--- a/drivers/input/touchscreen/elants_i2c.c
+++ b/drivers/input/touchscreen/elants_i2c.c
@@ -60,6 +60,15 @@
#define QUEUE_HEADER_NORMAL 0X63
#define QUEUE_HEADER_WAIT 0x64
+/*
+ * Depending on firmware version, eKTF3624 touchscreens may utilize one of
+ * these opcodes for the touch events: 0x63 and 0x66. The 0x63 is used by
+ * older firmware version and differs from 0x66 such that touch pressure
+ * value needs to be adjusted. The 0x66 opcode of newer firmware is equal
+ * to 0x63 of eKTH3500.
+ */
+#define QUEUE_HEADER_NORMAL2 0x66
+
/* Command header definition */
#define CMD_HEADER_WRITE 0x54
#define CMD_HEADER_READ 0x53
@@ -1050,7 +1059,6 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev)
switch (ts->buf[FW_HDR_TYPE]) {
case CMD_HEADER_HELLO:
case CMD_HEADER_RESP:
- case CMD_HEADER_REK:
break;
case QUEUE_HEADER_WAIT:
@@ -1070,6 +1078,7 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev)
break;
case QUEUE_HEADER_NORMAL:
+ case QUEUE_HEADER_NORMAL2:
report_count = ts->buf[FW_HDR_COUNT];
if (report_count == 0 || report_count > 3) {
dev_err(&client->dev,
--
2.20.1
On Mon, Apr 13, 2020 at 03:32:24PM +0200, Michał Mirosław wrote:
> Apply some DRY-ing to elants_i2c_execute_command() callers. This pulls
> polling and error printk()s into a single function.
>
> Signed-off-by: Michał Mirosław <[email protected]>
> ---
> drivers/input/touchscreen/elants_i2c.c | 189 +++++++++++++------------
> 1 file changed, 96 insertions(+), 93 deletions(-)
>
> diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
> index 87d686ce08f2..b0f083f7f2a9 100644
> --- a/drivers/input/touchscreen/elants_i2c.c
> +++ b/drivers/input/touchscreen/elants_i2c.c
> @@ -205,7 +205,8 @@ static int elants_i2c_read(struct i2c_client *client, void *data, size_t size)
>
> static int elants_i2c_execute_command(struct i2c_client *client,
> const u8 *cmd, size_t cmd_size,
> - u8 *resp, size_t resp_size)
> + u8 *resp, size_t resp_size,
> + int retries, const char *cmd_name)
> {
> struct i2c_msg msgs[2];
> int ret;
> @@ -225,30 +226,55 @@ static int elants_i2c_execute_command(struct i2c_client *client,
> break;
>
> default:
> - dev_err(&client->dev, "%s: invalid command %*ph\n",
> - __func__, (int)cmd_size, cmd);
> + dev_err(&client->dev, "(%s): invalid command: %*ph\n",
> + cmd_name, (int)cmd_size, cmd);
> return -EINVAL;
> }
>
> - msgs[0].addr = client->addr;
> - msgs[0].flags = client->flags & I2C_M_TEN;
> - msgs[0].len = cmd_size;
> - msgs[0].buf = (u8 *)cmd;
> + for (;;) {
> + msgs[0].addr = client->addr;
> + msgs[0].flags = client->flags & I2C_M_TEN;
> + msgs[0].len = cmd_size;
> + msgs[0].buf = (u8 *)cmd;
>
> - msgs[1].addr = client->addr;
> - msgs[1].flags = client->flags & I2C_M_TEN;
> - msgs[1].flags |= I2C_M_RD;
> - msgs[1].len = resp_size;
> - msgs[1].buf = resp;
> + msgs[1].addr = client->addr;
> + msgs[1].flags = client->flags & I2C_M_TEN;
> + msgs[1].flags |= I2C_M_RD;
> + msgs[1].len = resp_size;
> + msgs[1].buf = resp;
>
> - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
> - if (ret < 0)
> - return ret;
> + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
> + if (ret < 0) {
> + if (--retries > 0) {
> + dev_dbg(&client->dev,
> + "(%s) I2C transfer failed: %d (retrying)\n",
> + cmd_name, ret);
> + continue;
> + }
>
> - if (ret != ARRAY_SIZE(msgs) || resp[FW_HDR_TYPE] != expected_response)
> - return -EIO;
> + dev_err(&client->dev,
> + "(%s) I2C transfer failed: %d\n",
> + cmd_name, ret);
> + return ret;
> + }
>
> - return 0;
> + if (ret != ARRAY_SIZE(msgs) ||
> + resp[FW_HDR_TYPE] != expected_response) {
> + if (--retries > 0) {
> + dev_dbg(&client->dev,
> + "(%s) unexpected response: %*ph (retrying)\n",
> + cmd_name, ret, resp);
> + continue;
> + }
> +
> + dev_err(&client->dev,
> + "(%s) unexpected response: %*ph\n",
> + cmd_name, ret, resp);
> + return -EIO;
> + }
> +
> + return --retries;
I'd prefer if we returned 0 on success and I'd be OK when flashing
firmware to have separate retry counters for the command themselves and
for checking the response.
Thanks.
--
Dmitry
On Mon, Apr 13, 2020 at 03:32:26PM +0200, Michał Mirosław wrote:
> From: Dmitry Osipenko <[email protected]>
>
> eKTF3624 touchscreen firmware uses two variants of the reply opcodes for
> reporting touch events: one is 0x63 (used by older firmware) and other is
> 0x66 (used by newer firmware). The 0x66 variant is equal to 0x63 of
> eKTH3500, while 0x63 needs small adjustment of the touch pressure value.
>
> Nexus 7 tablet device has eKTF3624 touchscreen and it uses 0x66 opcode for
> reporting touch events, let's support it now. Other devices, eg. ASUS TF300T,
> use 0x63.
>
> Note: CMD_HEADER_REK is used for replying to calibration requests, it has
> the same 0x66 opcode number which eKTF3624 uses for reporting touches.
> The calibration replies are handled separately from the the rest of the
> commands in the driver by entering into ELAN_WAIT_RECALIBRATION state
> and thus this change shouldn't change the old behavior.
>
> Signed-off-by: Dmitry Osipenko <[email protected]>
> Tested-by: Michał Mirosław <[email protected]>
> Signed-off-by: Michał Mirosław <[email protected]>
> ---
> drivers/input/touchscreen/elants_i2c.c | 11 ++++++++++-
> 1 file changed, 10 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
> index 2b936e920874..9751139e8507 100644
> --- a/drivers/input/touchscreen/elants_i2c.c
> +++ b/drivers/input/touchscreen/elants_i2c.c
> @@ -60,6 +60,15 @@
> #define QUEUE_HEADER_NORMAL 0X63
> #define QUEUE_HEADER_WAIT 0x64
>
> +/*
> + * Depending on firmware version, eKTF3624 touchscreens may utilize one of
> + * these opcodes for the touch events: 0x63 and 0x66. The 0x63 is used by
> + * older firmware version and differs from 0x66 such that touch pressure
> + * value needs to be adjusted.
Who is responsible for the adjustment? Where does it happen?
Thanks.
--
Dmitry
26.04.2020 08:15, Dmitry Torokhov пишет:
> On Mon, Apr 13, 2020 at 03:32:26PM +0200, Michał Mirosław wrote:
>> From: Dmitry Osipenko <[email protected]>
>>
>> eKTF3624 touchscreen firmware uses two variants of the reply opcodes for
>> reporting touch events: one is 0x63 (used by older firmware) and other is
>> 0x66 (used by newer firmware). The 0x66 variant is equal to 0x63 of
>> eKTH3500, while 0x63 needs small adjustment of the touch pressure value.
>>
>> Nexus 7 tablet device has eKTF3624 touchscreen and it uses 0x66 opcode for
>> reporting touch events, let's support it now. Other devices, eg. ASUS TF300T,
>> use 0x63.
>>
>> Note: CMD_HEADER_REK is used for replying to calibration requests, it has
>> the same 0x66 opcode number which eKTF3624 uses for reporting touches.
>> The calibration replies are handled separately from the the rest of the
>> commands in the driver by entering into ELAN_WAIT_RECALIBRATION state
>> and thus this change shouldn't change the old behavior.
>>
>> Signed-off-by: Dmitry Osipenko <[email protected]>
>> Tested-by: Michał Mirosław <[email protected]>
>> Signed-off-by: Michał Mirosław <[email protected]>
>> ---
>> drivers/input/touchscreen/elants_i2c.c | 11 ++++++++++-
>> 1 file changed, 10 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
>> index 2b936e920874..9751139e8507 100644
>> --- a/drivers/input/touchscreen/elants_i2c.c
>> +++ b/drivers/input/touchscreen/elants_i2c.c
>> @@ -60,6 +60,15 @@
>> #define QUEUE_HEADER_NORMAL 0X63
>> #define QUEUE_HEADER_WAIT 0x64
>>
>> +/*
>> + * Depending on firmware version, eKTF3624 touchscreens may utilize one of
>> + * these opcodes for the touch events: 0x63 and 0x66. The 0x63 is used by
>> + * older firmware version and differs from 0x66 such that touch pressure
>> + * value needs to be adjusted.
>
> Who is responsible for the adjustment? Where does it happen?
Please see the patch #2 ("support old touch report format") which adds
support for parsing of the older packet format that requires the
pressure value adjustment.