Skip to content

Commit

Permalink
Input: elantech - fix protocol errors for some trackpoints in SMBus mode
Browse files Browse the repository at this point in the history
commit e4c9062 upstream.

There are some version of Elan trackpads that send incorrect data when
in SMbus mode, unless they are switched to use 0x5f reports instead of
standard 0x5e. This patch implements querying device to retrieve chips
identifying data, and switching it, when needed to the alternative
report.

Signed-off-by: Jingle Wu <jingle.wu@emc.com.tw>
Link: https://lore.kernel.org/r/20201211071531.32413-1-jingle.wu@emc.com.tw
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
jinglewu authored and gregkh committed Mar 7, 2021
1 parent d00a97d commit dde807b
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 2 deletions.
99 changes: 97 additions & 2 deletions drivers/input/mouse/elantech.c
Expand Up @@ -89,6 +89,47 @@ static int elantech_ps2_command(struct psmouse *psmouse,
return rc;
}

/*
* Send an Elantech style special command to read 3 bytes from a register
*/
static int elantech_read_reg_params(struct psmouse *psmouse, u8 reg, u8 *param)
{
if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) ||
elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
elantech_ps2_command(psmouse, NULL, reg) ||
elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) {
psmouse_err(psmouse,
"failed to read register %#02x\n", reg);
return -EIO;
}

return 0;
}

/*
* Send an Elantech style special command to write a register with a parameter
*/
static int elantech_write_reg_params(struct psmouse *psmouse, u8 reg, u8 *param)
{
if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) ||
elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
elantech_ps2_command(psmouse, NULL, reg) ||
elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
elantech_ps2_command(psmouse, NULL, param[0]) ||
elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
elantech_ps2_command(psmouse, NULL, param[1]) ||
elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) {
psmouse_err(psmouse,
"failed to write register %#02x with value %#02x%#02x\n",
reg, param[0], param[1]);
return -EIO;
}

return 0;
}

/*
* Send an Elantech style special command to read a value from a register
*/
Expand Down Expand Up @@ -1529,19 +1570,35 @@ static const struct dmi_system_id no_hw_res_dmi_table[] = {
{ }
};

/*
* Change Report id 0x5E to 0x5F.
*/
static int elantech_change_report_id(struct psmouse *psmouse)
{
unsigned char param[2] = { 0x10, 0x03 };

if (elantech_write_reg_params(psmouse, 0x7, param) ||
elantech_read_reg_params(psmouse, 0x7, param) ||
param[0] != 0x10 || param[1] != 0x03) {
psmouse_err(psmouse, "Unable to change report ID to 0x5f.\n");
return -EIO;
}

return 0;
}
/*
* determine hardware version and set some properties according to it.
*/
static int elantech_set_properties(struct elantech_device_info *info)
{
/* This represents the version of IC body. */
int ver = (info->fw_version & 0x0f0000) >> 16;
info->ic_version = (info->fw_version & 0x0f0000) >> 16;

/* Early version of Elan touchpads doesn't obey the rule. */
if (info->fw_version < 0x020030 || info->fw_version == 0x020600)
info->hw_version = 1;
else {
switch (ver) {
switch (info->ic_version) {
case 2:
case 4:
info->hw_version = 2;
Expand All @@ -1557,6 +1614,11 @@ static int elantech_set_properties(struct elantech_device_info *info)
}
}

/* Get information pattern for hw_version 4 */
info->pattern = 0x00;
if (info->ic_version == 0x0f && (info->fw_version & 0xff) <= 0x02)
info->pattern = info->fw_version & 0xff;

/* decide which send_cmd we're gonna use early */
info->send_cmd = info->hw_version >= 3 ? elantech_send_cmd :
synaptics_send_cmd;
Expand Down Expand Up @@ -1598,6 +1660,7 @@ static int elantech_query_info(struct psmouse *psmouse,
{
unsigned char param[3];
unsigned char traces;
unsigned char ic_body[3];

memset(info, 0, sizeof(*info));

Expand Down Expand Up @@ -1640,6 +1703,21 @@ static int elantech_query_info(struct psmouse *psmouse,
info->samples[2]);
}

if (info->pattern > 0x00 && info->ic_version == 0xf) {
if (info->send_cmd(psmouse, ETP_ICBODY_QUERY, ic_body)) {
psmouse_err(psmouse, "failed to query ic body\n");
return -EINVAL;
}
info->ic_version = be16_to_cpup((__be16 *)ic_body);
psmouse_info(psmouse,
"Elan ic body: %#04x, current fw version: %#02x\n",
info->ic_version, ic_body[2]);
}

info->product_id = be16_to_cpup((__be16 *)info->samples);
if (info->pattern == 0x00)
info->product_id &= 0xff;

if (info->samples[1] == 0x74 && info->hw_version == 0x03) {
/*
* This module has a bug which makes absolute mode
Expand All @@ -1654,6 +1732,23 @@ static int elantech_query_info(struct psmouse *psmouse,
/* The MSB indicates the presence of the trackpoint */
info->has_trackpoint = (info->capabilities[0] & 0x80) == 0x80;

if (info->has_trackpoint && info->ic_version == 0x0011 &&
(info->product_id == 0x08 || info->product_id == 0x09 ||
info->product_id == 0x0d || info->product_id == 0x0e)) {
/*
* This module has a bug which makes trackpoint in SMBus
* mode return invalid data unless trackpoint is switched
* from using 0x5e reports to 0x5f. If we are not able to
* make the switch, let's abort initialization so we'll be
* using standard PS/2 protocol.
*/
if (elantech_change_report_id(psmouse)) {
psmouse_info(psmouse,
"Trackpoint report is broken, forcing standard PS/2 protocol\n");
return -ENODEV;
}
}

info->x_res = 31;
info->y_res = 31;
if (info->hw_version == 4) {
Expand Down
4 changes: 4 additions & 0 deletions drivers/input/mouse/elantech.h
Expand Up @@ -18,6 +18,7 @@
#define ETP_CAPABILITIES_QUERY 0x02
#define ETP_SAMPLE_QUERY 0x03
#define ETP_RESOLUTION_QUERY 0x04
#define ETP_ICBODY_QUERY 0x05

/*
* Command values for register reading or writing
Expand Down Expand Up @@ -140,7 +141,10 @@ struct elantech_device_info {
unsigned char samples[3];
unsigned char debug;
unsigned char hw_version;
unsigned char pattern;
unsigned int fw_version;
unsigned int ic_version;
unsigned int product_id;
unsigned int x_min;
unsigned int y_min;
unsigned int x_max;
Expand Down

0 comments on commit dde807b

Please sign in to comment.