@@ -89,6 +89,57 @@ 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, unsigned char reg,
+ unsigned char *param)
+{
+ int rc = 0;
+
+ 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)) {
+ rc = -1;
+ }
+
+ if (rc)
+ psmouse_err(psmouse,
+ "failed to read register 0x%02x.\n", reg);
+ return rc;
+}
+
+/*
+ * Send an Elantech style special command to write a register with a parameter
+ */
+static int elantech_write_reg_params(struct psmouse *psmouse, unsigned char reg,
+ unsigned char *param)
+{
+
+ int rc = 0;
+
+ 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)) {
+ rc = -1;
+ }
+
+ if (rc)
+ psmouse_err(psmouse,
+ "failed to write register 0x%02x with value 0x%02x0x%02x.\n",
+ reg, param[0], param[1]);
+ return rc;
+
+}
+
/*
* Send an Elantech style special command to read a value from a register
*/
@@ -1529,6 +1580,27 @@ 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) == 0) {
+ if (elantech_read_reg_params(psmouse, 0x7, param) == 0) {
+ if ((param[0] == 0x10) && (param[1] == 0x03)) {
+ return 0;
+ }
+ psmouse_err(psmouse,
+ "Elantech change report id Fail. (0x%02x, 0x%02x)\n",
+ param[0], param[1]);
+ }
+ }
+ psmouse_err(psmouse,
+ "Elantech change report id Fail.\n");
+ return -1;
+}
/*
* determine hardware version and set some properties according to it.
*/
@@ -1556,6 +1628,18 @@ static int elantech_set_properties(struct elantech_device_info *info)
return -1;
}
}
+
+
+ /* Get information pattern for hw_version 4 */
+ if (ver == 15) {
+ if ((info->fw_version & 0x0000ff) == 0x01)
+ info->pattern = 0x01;
+ else if ((info->fw_version & 0x0000ff) == 0x02)
+ info->pattern = 0x02;
+ else
+ info->pattern = 0x00;
+ } else
+ info->pattern = 0x00;
/* decide which send_cmd we're gonna use early */
info->send_cmd = info->hw_version >= 3 ? elantech_send_cmd :
@@ -1598,7 +1682,8 @@ 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));
/*
@@ -1628,6 +1713,21 @@ static int elantech_query_info(struct psmouse *psmouse,
info->capabilities[0], info->capabilities[1],
info->capabilities[2]);
+
+ info->ic_version = (info->fw_version & 0x0f0000) >> 16;
+
+ 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 = (ic_body[0] << 8) | ic_body[1];
+ psmouse_info(psmouse,
+ "Elan ic body : 0x%04x, current fw version : 0x%02x\n",
+ info->ic_version,
+ ic_body[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");
@@ -1640,6 +1740,11 @@ static int elantech_query_info(struct psmouse *psmouse,
info->samples[2]);
}
+ if (info->pattern > 0x00)
+ info->product_id = (info->samples[0] << 8) | info->samples[1];
+ else
+ info->product_id = info->samples[1];
+
if (info->samples[1] == 0x74 && info->hw_version == 0x03) {
/*
* This module has a bug which makes absolute mode
@@ -1653,6 +1758,27 @@ 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) {
+ if ((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 default SMBUS trackpoint report
+ * 0x5E have a protocol error, So change the report id to 0x5F,
+ * if it is not changed to 0x5F report, so let's abort
+ * so we'll be using standard PS/2 protocol.
+ */
+ if (elantech_change_report_id(psmouse) != 0) {
+ psmouse_info(psmouse,
+ "Trackpoint report is broken, forcing standard PS/2 protocol\n");
+ return -ENODEV;
+ }
+
+ }
+
+ }
info->x_res = 31;
info->y_res = 31;
@@ -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
@@ -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;