Skip to content

Commit e4c9062

Browse files
jinglewudtor
authored andcommitted
Input: elantech - fix protocol errors for some trackpoints in SMBus mode
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>
1 parent 056115d commit e4c9062

File tree

2 files changed

+101
-2
lines changed

2 files changed

+101
-2
lines changed

drivers/input/mouse/elantech.c

+97-2
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,47 @@ static int elantech_ps2_command(struct psmouse *psmouse,
8989
return rc;
9090
}
9191

92+
/*
93+
* Send an Elantech style special command to read 3 bytes from a register
94+
*/
95+
static int elantech_read_reg_params(struct psmouse *psmouse, u8 reg, u8 *param)
96+
{
97+
if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
98+
elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) ||
99+
elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
100+
elantech_ps2_command(psmouse, NULL, reg) ||
101+
elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) {
102+
psmouse_err(psmouse,
103+
"failed to read register %#02x\n", reg);
104+
return -EIO;
105+
}
106+
107+
return 0;
108+
}
109+
110+
/*
111+
* Send an Elantech style special command to write a register with a parameter
112+
*/
113+
static int elantech_write_reg_params(struct psmouse *psmouse, u8 reg, u8 *param)
114+
{
115+
if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
116+
elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) ||
117+
elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
118+
elantech_ps2_command(psmouse, NULL, reg) ||
119+
elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
120+
elantech_ps2_command(psmouse, NULL, param[0]) ||
121+
elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
122+
elantech_ps2_command(psmouse, NULL, param[1]) ||
123+
elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) {
124+
psmouse_err(psmouse,
125+
"failed to write register %#02x with value %#02x%#02x\n",
126+
reg, param[0], param[1]);
127+
return -EIO;
128+
}
129+
130+
return 0;
131+
}
132+
92133
/*
93134
* Send an Elantech style special command to read a value from a register
94135
*/
@@ -1529,19 +1570,35 @@ static const struct dmi_system_id no_hw_res_dmi_table[] = {
15291570
{ }
15301571
};
15311572

1573+
/*
1574+
* Change Report id 0x5E to 0x5F.
1575+
*/
1576+
static int elantech_change_report_id(struct psmouse *psmouse)
1577+
{
1578+
unsigned char param[2] = { 0x10, 0x03 };
1579+
1580+
if (elantech_write_reg_params(psmouse, 0x7, param) ||
1581+
elantech_read_reg_params(psmouse, 0x7, param) ||
1582+
param[0] != 0x10 || param[1] != 0x03) {
1583+
psmouse_err(psmouse, "Unable to change report ID to 0x5f.\n");
1584+
return -EIO;
1585+
}
1586+
1587+
return 0;
1588+
}
15321589
/*
15331590
* determine hardware version and set some properties according to it.
15341591
*/
15351592
static int elantech_set_properties(struct elantech_device_info *info)
15361593
{
15371594
/* This represents the version of IC body. */
1538-
int ver = (info->fw_version & 0x0f0000) >> 16;
1595+
info->ic_version = (info->fw_version & 0x0f0000) >> 16;
15391596

15401597
/* Early version of Elan touchpads doesn't obey the rule. */
15411598
if (info->fw_version < 0x020030 || info->fw_version == 0x020600)
15421599
info->hw_version = 1;
15431600
else {
1544-
switch (ver) {
1601+
switch (info->ic_version) {
15451602
case 2:
15461603
case 4:
15471604
info->hw_version = 2;
@@ -1557,6 +1614,11 @@ static int elantech_set_properties(struct elantech_device_info *info)
15571614
}
15581615
}
15591616

1617+
/* Get information pattern for hw_version 4 */
1618+
info->pattern = 0x00;
1619+
if (info->ic_version == 0x0f && (info->fw_version & 0xff) <= 0x02)
1620+
info->pattern = info->fw_version & 0xff;
1621+
15601622
/* decide which send_cmd we're gonna use early */
15611623
info->send_cmd = info->hw_version >= 3 ? elantech_send_cmd :
15621624
synaptics_send_cmd;
@@ -1598,6 +1660,7 @@ static int elantech_query_info(struct psmouse *psmouse,
15981660
{
15991661
unsigned char param[3];
16001662
unsigned char traces;
1663+
unsigned char ic_body[3];
16011664

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

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

1706+
if (info->pattern > 0x00 && info->ic_version == 0xf) {
1707+
if (info->send_cmd(psmouse, ETP_ICBODY_QUERY, ic_body)) {
1708+
psmouse_err(psmouse, "failed to query ic body\n");
1709+
return -EINVAL;
1710+
}
1711+
info->ic_version = be16_to_cpup((__be16 *)ic_body);
1712+
psmouse_info(psmouse,
1713+
"Elan ic body: %#04x, current fw version: %#02x\n",
1714+
info->ic_version, ic_body[2]);
1715+
}
1716+
1717+
info->product_id = be16_to_cpup((__be16 *)info->samples);
1718+
if (info->pattern == 0x00)
1719+
info->product_id &= 0xff;
1720+
16431721
if (info->samples[1] == 0x74 && info->hw_version == 0x03) {
16441722
/*
16451723
* This module has a bug which makes absolute mode
@@ -1654,6 +1732,23 @@ static int elantech_query_info(struct psmouse *psmouse,
16541732
/* The MSB indicates the presence of the trackpoint */
16551733
info->has_trackpoint = (info->capabilities[0] & 0x80) == 0x80;
16561734

1735+
if (info->has_trackpoint && info->ic_version == 0x0011 &&
1736+
(info->product_id == 0x08 || info->product_id == 0x09 ||
1737+
info->product_id == 0x0d || info->product_id == 0x0e)) {
1738+
/*
1739+
* This module has a bug which makes trackpoint in SMBus
1740+
* mode return invalid data unless trackpoint is switched
1741+
* from using 0x5e reports to 0x5f. If we are not able to
1742+
* make the switch, let's abort initialization so we'll be
1743+
* using standard PS/2 protocol.
1744+
*/
1745+
if (elantech_change_report_id(psmouse)) {
1746+
psmouse_info(psmouse,
1747+
"Trackpoint report is broken, forcing standard PS/2 protocol\n");
1748+
return -ENODEV;
1749+
}
1750+
}
1751+
16571752
info->x_res = 31;
16581753
info->y_res = 31;
16591754
if (info->hw_version == 4) {

drivers/input/mouse/elantech.h

+4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#define ETP_CAPABILITIES_QUERY 0x02
1919
#define ETP_SAMPLE_QUERY 0x03
2020
#define ETP_RESOLUTION_QUERY 0x04
21+
#define ETP_ICBODY_QUERY 0x05
2122

2223
/*
2324
* Command values for register reading or writing
@@ -140,7 +141,10 @@ struct elantech_device_info {
140141
unsigned char samples[3];
141142
unsigned char debug;
142143
unsigned char hw_version;
144+
unsigned char pattern;
143145
unsigned int fw_version;
146+
unsigned int ic_version;
147+
unsigned int product_id;
144148
unsigned int x_min;
145149
unsigned int y_min;
146150
unsigned int x_max;

0 commit comments

Comments
 (0)