[PATCH] input - elantech: force query XY range after absolute mode

From: Benjamin Tissoires
Date: Tue Oct 13 2020 - 03:15:51 EST


For some v3 hw versions, if the ETP_FW_ID_QUERY command is
issued before the call to set_absolute_mode(), the returned
values are wrong.

Force an other ETP_FW_ID_QUERY after set_absolute_mode()
to get correct values.

Link: https://bugzilla.kernel.org/show_bug.cgi?id=209027
Cc: stable@xxxxxxxxxxxxxxx # 5.3+
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@xxxxxxxxxx>
---
drivers/input/mouse/elantech.c | 161 +++++++++++++++++++--------------
1 file changed, 91 insertions(+), 70 deletions(-)

diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 90f8765f9efc..ff8e5fb61dab 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1593,80 +1593,12 @@ static int elantech_set_properties(struct elantech_device_info *info)
return 0;
}

-static int elantech_query_info(struct psmouse *psmouse,
- struct elantech_device_info *info)
+static int elantech_get_range(struct psmouse *psmouse,
+ struct elantech_device_info *info)
{
unsigned char param[3];
unsigned char traces;

- memset(info, 0, sizeof(*info));
-
- /*
- * Do the version query again so we can store the result
- */
- if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
- psmouse_err(psmouse, "failed to query firmware version.\n");
- return -EINVAL;
- }
- info->fw_version = (param[0] << 16) | (param[1] << 8) | param[2];
-
- if (elantech_set_properties(info)) {
- psmouse_err(psmouse, "unknown hardware version, aborting...\n");
- return -EINVAL;
- }
- psmouse_info(psmouse,
- "assuming hardware version %d (with firmware version 0x%02x%02x%02x)\n",
- info->hw_version, param[0], param[1], param[2]);
-
- if (info->send_cmd(psmouse, ETP_CAPABILITIES_QUERY,
- info->capabilities)) {
- psmouse_err(psmouse, "failed to query capabilities.\n");
- return -EINVAL;
- }
- psmouse_info(psmouse,
- "Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n",
- info->capabilities[0], info->capabilities[1],
- info->capabilities[2]);
-
- if (info->hw_version != 1) {
- if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, info->samples)) {
- psmouse_err(psmouse, "failed to query sample data\n");
- return -EINVAL;
- }
- psmouse_info(psmouse,
- "Elan sample query result %02x, %02x, %02x\n",
- info->samples[0],
- info->samples[1],
- info->samples[2]);
- }
-
- if (info->samples[1] == 0x74 && info->hw_version == 0x03) {
- /*
- * This module has a bug which makes absolute mode
- * unusable, so let's abort so we'll be using standard
- * PS/2 protocol.
- */
- psmouse_info(psmouse,
- "absolute mode broken, forcing standard PS/2 protocol\n");
- return -ENODEV;
- }
-
- /* The MSB indicates the presence of the trackpoint */
- info->has_trackpoint = (info->capabilities[0] & 0x80) == 0x80;
-
- info->x_res = 31;
- info->y_res = 31;
- if (info->hw_version == 4) {
- if (elantech_get_resolution_v4(psmouse,
- &info->x_res,
- &info->y_res,
- &info->bus)) {
- psmouse_warn(psmouse,
- "failed to query resolution data.\n");
- }
- }
-
- /* query range information */
switch (info->hw_version) {
case 1:
info->x_min = ETP_XMIN_V1;
@@ -1745,6 +1677,87 @@ static int elantech_query_info(struct psmouse *psmouse,
break;
}

+ return 0;
+}
+
+static int elantech_query_info(struct psmouse *psmouse,
+ struct elantech_device_info *info)
+{
+ unsigned char param[3];
+ int error;
+
+ memset(info, 0, sizeof(*info));
+
+ /*
+ * Do the version query again so we can store the result
+ */
+ if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
+ psmouse_err(psmouse, "failed to query firmware version.\n");
+ return -EINVAL;
+ }
+ info->fw_version = (param[0] << 16) | (param[1] << 8) | param[2];
+
+ if (elantech_set_properties(info)) {
+ psmouse_err(psmouse, "unknown hardware version, aborting...\n");
+ return -EINVAL;
+ }
+ psmouse_info(psmouse,
+ "assuming hardware version %d (with firmware version 0x%02x%02x%02x)\n",
+ info->hw_version, param[0], param[1], param[2]);
+
+ if (info->send_cmd(psmouse, ETP_CAPABILITIES_QUERY,
+ info->capabilities)) {
+ psmouse_err(psmouse, "failed to query capabilities.\n");
+ return -EINVAL;
+ }
+ psmouse_info(psmouse,
+ "Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n",
+ info->capabilities[0], info->capabilities[1],
+ info->capabilities[2]);
+
+ if (info->hw_version != 1) {
+ if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, info->samples)) {
+ psmouse_err(psmouse, "failed to query sample data\n");
+ return -EINVAL;
+ }
+ psmouse_info(psmouse,
+ "Elan sample query result %02x, %02x, %02x\n",
+ info->samples[0],
+ info->samples[1],
+ info->samples[2]);
+ }
+
+ if (info->samples[1] == 0x74 && info->hw_version == 0x03) {
+ /*
+ * This module has a bug which makes absolute mode
+ * unusable, so let's abort so we'll be using standard
+ * PS/2 protocol.
+ */
+ psmouse_info(psmouse,
+ "absolute mode broken, forcing standard PS/2 protocol\n");
+ return -ENODEV;
+ }
+
+ /* The MSB indicates the presence of the trackpoint */
+ info->has_trackpoint = (info->capabilities[0] & 0x80) == 0x80;
+
+ info->x_res = 31;
+ info->y_res = 31;
+ if (info->hw_version == 4) {
+ if (elantech_get_resolution_v4(psmouse,
+ &info->x_res,
+ &info->y_res,
+ &info->bus)) {
+ psmouse_warn(psmouse,
+ "failed to query resolution data.\n");
+ }
+ }
+
+ /* query range information */
+ error = elantech_get_range(psmouse, info);
+ if (error)
+ return error;
+
/* check for the middle button: DMI matching or new v4 firmwares */
info->has_middle_button = dmi_check_system(elantech_dmi_has_middle_button) ||
(ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version) &&
@@ -1942,6 +1955,14 @@ static int elantech_setup_ps2(struct psmouse *psmouse,
goto init_fail;
}

+ /*
+ * some hardware v3 send wrong min max coordinates if the
+ * call to get those is made before elantech_set_absolute_mode().
+ */
+ error = elantech_get_range(psmouse, &etd->info);
+ if (error)
+ goto init_fail;
+
if (info->fw_version == 0x381f17) {
etd->original_set_rate = psmouse->set_rate;
psmouse->set_rate = elantech_set_rate_restore_reg_07;
--
2.26.2