aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorSeth Forshee <seth.forshee@canonical.com>2011-08-25 11:57:33 -0500
committerJohn Rigby <john.rigby@linaro.org>2011-11-16 14:27:04 -0700
commitbc0fe9c4f30e3e3833558edb1acf027212a3f5a8 (patch)
tree9ac52ae1f096d1e62af32061e5e8fdd8730f1b4c /drivers
parent1778510462c2ffb4a75a9e9c0ebe1bb2aaead3b5 (diff)
UBUNTU: SAUCE: (no-up) Input: elantech - Add v3 hardware support
BugLink: https://bugs.launchpad.net/bugs/681904 Adds basic v3 hardware support for newer devices not currently supported by the driver. Thanks to JJ Ding <jj_ding@emc.com.tw>, Tom Lin <tom_lin@emc.com.tw>, and Mark A. Stratman <stratman@gmail.com>, whose work on other patcesh revealed the workings of the v3 protocol. Signed-off-by: Seth Forshee <seth.forshee@canonical.com> Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/input/mouse/elantech.c181
-rw-r--r--drivers/input/mouse/elantech.h5
2 files changed, 157 insertions, 29 deletions
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 32503565faf..642cde057db 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -82,6 +82,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
{
struct elantech_data *etd = psmouse->private;
unsigned char param[3];
+ unsigned char command;
int rc = 0;
if (reg < 0x10 || reg > 0x26)
@@ -90,9 +91,11 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
if (reg > 0x11 && reg < 0x20)
return -1;
+ command = (etd->hw_version == 3) ? ETP_REGISTER_RW : ETP_REGISTER_READ;
+
switch (etd->hw_version) {
case 1:
- if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) ||
+ if (psmouse_sliced_command(psmouse, command) ||
psmouse_sliced_command(psmouse, reg) ||
ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
rc = -1;
@@ -100,8 +103,9 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
break;
case 2:
+ case 3:
if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
- elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READ) ||
+ elantech_ps2_command(psmouse, NULL, command) ||
elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
elantech_ps2_command(psmouse, NULL, reg) ||
elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) {
@@ -125,6 +129,7 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
unsigned char val)
{
struct elantech_data *etd = psmouse->private;
+ unsigned char command;
int rc = 0;
if (reg < 0x10 || reg > 0x26)
@@ -133,9 +138,11 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
if (reg > 0x11 && reg < 0x20)
return -1;
+ command = (etd->hw_version == 3) ? ETP_REGISTER_RW : ETP_REGISTER_WRITE;
+
switch (etd->hw_version) {
case 1:
- if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) ||
+ if (psmouse_sliced_command(psmouse, command) ||
psmouse_sliced_command(psmouse, reg) ||
psmouse_sliced_command(psmouse, val) ||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) {
@@ -144,8 +151,9 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
break;
case 2:
+ case 3:
if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
- elantech_ps2_command(psmouse, NULL, ETP_REGISTER_WRITE) ||
+ elantech_ps2_command(psmouse, NULL, command) ||
elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
elantech_ps2_command(psmouse, NULL, reg) ||
elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
@@ -223,7 +231,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
input_report_abs(dev, ABS_X,
((packet[1] & 0x0c) << 6) | packet[2]);
input_report_abs(dev, ABS_Y,
- ETP_YMAX_V1 - (((packet[1] & 0x03) << 8) | packet[3]));
+ etd->y_max - (((packet[1] & 0x03) << 8) | packet[3]));
}
input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
@@ -298,7 +306,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
* byte 4: . . . . . . y9 y8
* byte 5: y7 y6 y5 y4 y3 y2 y1 y0
*/
- y1 = ETP_YMAX_V2 - (((packet[4] & 0x03) << 8) | packet[5]);
+ y1 = etd->y_max - (((packet[4] & 0x03) << 8) | packet[5]);
input_report_abs(dev, ABS_X, x1);
input_report_abs(dev, ABS_Y, y1);
@@ -316,14 +324,14 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
*/
x1 = ((packet[0] & 0x10) << 4) | packet[1];
/* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */
- y1 = ETP_2FT_YMAX - (((packet[0] & 0x20) << 3) | packet[2]);
+ y1 = etd->y_max_2ft - (((packet[0] & 0x20) << 3) | packet[2]);
/*
* byte 3: . . by8 bx8 . . . .
* byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0
*/
x2 = ((packet[3] & 0x10) << 4) | packet[4];
/* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */
- y2 = ETP_2FT_YMAX - (((packet[3] & 0x20) << 3) | packet[5]);
+ y2 = etd->y_max_2ft - (((packet[3] & 0x20) << 3) | packet[5]);
/*
* For compatibility with the X Synaptics driver scale up
* one coordinate and report as ordinary mouse movent
@@ -352,6 +360,83 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
input_sync(dev);
}
+static int elantech_v3_is_noise(unsigned int x, unsigned int y)
+{
+ return x == 0xfff && y == 0xfff;
+}
+
+/*
+ * Interpret complete data packets and report absolute mode input events for
+ * hardware version 3. (6 byte packets)
+ */
+static void elantech_report_absolute_v3(struct psmouse *psmouse)
+{
+ struct elantech_data *etd = psmouse->private;
+ struct input_dev *dev = psmouse->dev;
+ unsigned char *packet1 = psmouse->packet;
+ unsigned char *packet2 = NULL;
+ unsigned int fingers, width = 0, pres = 0;
+ unsigned int x1, y1, x2 = 0, y2 = 0;
+
+ /* byte 0: n1 n0 . . . . R L */
+ fingers = (packet1[0] & 0xc0) >> 6;
+
+ /*
+ * Need to receive two packets for 2-touch events. If this
+ * is the first packet, store it away and wait for the second.
+ * We don't support parity in v3, so use that storage.
+ */
+ if (fingers == 2 && (packet1[0] & 0x0c) == 0x04 &&
+ (packet1[3] & 0xcf) == 0x2) {
+ memcpy(etd->parity, packet1, psmouse->pktsize);
+ return;
+ }
+
+ /*
+ * If this the second packet of a two-fingered touch event we
+ * need to process the previous packet stashed away in
+ * etd->parity
+ */
+ if (fingers == 2) {
+ packet1 = etd->parity;
+ packet2 = psmouse->packet;
+ }
+
+ x1 = ((packet1[1] & 0x0f) << 8) | packet1[2];
+ y1 = ((packet1[4] & 0x0f) << 8) | packet1[5];
+ if (elantech_v3_is_noise(x1, y1))
+ return;
+ y1 = etd->y_max - y1;
+
+ if (packet2) {
+ x2 = ((packet2[1] & 0x0f) << 8) | packet2[2];
+ y2 = etd->y_max - (((packet2[4] & 0x0f) << 8) | packet2[5]);
+ }
+
+ pres = (packet1[1] & 0xf0) | ((packet1[4] & 0xf0) >> 4);
+ width = ((packet1[0] & 0x30) >> 2) | ((packet1[3] & 0x30) >> 4);
+
+ elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
+
+ input_report_key(dev, BTN_TOUCH, fingers != 0);
+ input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
+ input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
+ input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
+ input_report_abs(dev, ABS_PRESSURE, pres);
+ input_report_abs(dev, ABS_TOOL_WIDTH, width);
+
+ if (fingers != 0) {
+ input_report_abs(dev, ABS_X, x1);
+ input_report_abs(dev, ABS_Y, y1);
+ }
+
+ input_report_key(dev, BTN_LEFT, packet1[0] & 0x01);
+ input_report_key(dev, BTN_RIGHT, packet1[0] & 0x02);
+
+ input_sync(dev);
+ return;
+}
+
static int elantech_check_parity_v1(struct psmouse *psmouse)
{
struct elantech_data *etd = psmouse->private;
@@ -401,6 +486,10 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
/* We don't know how to check parity in protocol v2 */
elantech_report_absolute_v2(psmouse);
break;
+ case 3:
+ /* We don't know how to check parity in protocol v3 */
+ elantech_report_absolute_v3(psmouse);
+ break;
}
return PSMOUSE_FULL_PACKET;
@@ -435,8 +524,13 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
elantech_write_reg(psmouse, 0x11, etd->reg_11) ||
elantech_write_reg(psmouse, 0x21, etd->reg_21)) {
rc = -1;
- break;
}
+ break;
+ case 3:
+ etd->reg_10 = 0x0b;
+ if (elantech_write_reg(psmouse, 0x10, etd->reg_10))
+ rc = -1;
+ break;
}
if (rc == 0) {
@@ -473,10 +567,11 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
/*
* Set the appropriate event bits for the input subsystem
*/
-static void elantech_set_input_params(struct psmouse *psmouse)
+static int elantech_set_input_params(struct psmouse *psmouse)
{
struct input_dev *dev = psmouse->dev;
struct elantech_data *etd = psmouse->private;
+ unsigned char param[3];
__set_bit(EV_KEY, dev->evbit);
__set_bit(EV_ABS, dev->evbit);
@@ -492,20 +587,40 @@ static void elantech_set_input_params(struct psmouse *psmouse)
switch (etd->hw_version) {
case 1:
+ etd->x_max = ETP_XMAX_V1;
+ etd->y_max = ETP_YMAX_V1;
+
/* Rocker button */
if (etd->fw_version < 0x020000 &&
(etd->capabilities & ETP_CAP_HAS_ROCKER)) {
__set_bit(BTN_FORWARD, dev->keybit);
__set_bit(BTN_BACK, dev->keybit);
}
- input_set_abs_params(dev, ABS_X, ETP_XMIN_V1, ETP_XMAX_V1, 0, 0);
- input_set_abs_params(dev, ABS_Y, ETP_YMIN_V1, ETP_YMAX_V1, 0, 0);
+ input_set_abs_params(dev, ABS_X, ETP_XMIN_V1, etd->x_max, 0, 0);
+ input_set_abs_params(dev, ABS_Y, ETP_YMIN_V1, etd->y_max, 0, 0);
break;
case 2:
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
- input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0);
- input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0);
+ /* fall through */
+ case 3:
+ if (etd->hw_version == 3) {
+ if (synaptics_send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+ return -1;
+
+ etd->x_max = (param[0] & 0x0f) << 8 | param[1];
+ etd->y_max = (param[0] & 0xf0) << 4 | param[2];
+ etd->y_max_2ft = etd->y_max;
+ elantech_debug("x_max = %d, y_max = %d\n",
+ etd->x_max, etd->y_max);
+ } else {
+ etd->x_max = ETP_XMAX_V2;
+ etd->y_max = ETP_YMAX_V2;
+ etd->y_max_2ft = ETP_2FT_YMAX;
+ }
+
+ input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, etd->x_max, 0, 0);
+ input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, etd->y_max, 0, 0);
if (etd->reports_pressure) {
input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2,
ETP_PMAX_V2, 0, 0);
@@ -514,10 +629,12 @@ static void elantech_set_input_params(struct psmouse *psmouse)
}
__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
input_mt_init_slots(dev, 2);
- input_set_abs_params(dev, ABS_MT_POSITION_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0);
- input_set_abs_params(dev, ABS_MT_POSITION_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0);
+ input_set_abs_params(dev, ABS_MT_POSITION_X, ETP_XMIN_V2, etd->x_max, 0, 0);
+ input_set_abs_params(dev, ABS_MT_POSITION_Y, ETP_YMIN_V2, etd->y_max, 0, 0);
break;
}
+
+ return 0;
}
struct elantech_attr_data {
@@ -659,7 +776,8 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties)
* Report this in case there are Elantech models that use a different
* set of magic numbers
*/
- if (param[0] != 0x3c || param[1] != 0x03 || param[2] != 0xc8) {
+ if (param[0] != 0x3c || param[1] != 0x03 ||
+ (param[2] != 0xc8 && param[2] != 0x00)) {
pr_debug("unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n",
param[0], param[1], param[2]);
return -1;
@@ -749,23 +867,24 @@ int elantech_init(struct psmouse *psmouse)
etd->fw_version = (param[0] << 16) | (param[1] << 8) | param[2];
- /*
- * Assume every version greater than this is new EeePC style
- * hardware with 6 byte packets
- */
- if (etd->fw_version >= 0x020030) {
+ if (etd->fw_version < 0x020030) {
+ etd->hw_version = 1;
+ etd->paritycheck = 1;
+ } else if (etd->fw_version <= 0x150600) {
etd->hw_version = 2;
/* For now show extra debug information */
etd->debug = 1;
- /* Don't know how to do parity checking for version 2 */
- etd->paritycheck = 0;
if (etd->fw_version >= 0x020800)
etd->reports_pressure = true;
-
+ } else if ((etd->fw_version & 0x0f0000) == 0x050000) {
+ etd->hw_version = 3;
+ etd->debug = 1;
+ etd->reports_pressure = true;
} else {
- etd->hw_version = 1;
- etd->paritycheck = 1;
+ pr_debug("unrecognized firmware version 0x%06x\n",
+ etd->fw_version);
+ goto init_fail;
}
pr_info("assuming hardware version %d, firmware version %d.%d.%d\n",
@@ -794,7 +913,11 @@ int elantech_init(struct psmouse *psmouse)
goto init_fail;
}
- elantech_set_input_params(psmouse);
+ error = elantech_set_input_params(psmouse);
+ if (error) {
+ pr_err("failed to set input parameters\n");
+ goto init_fail;
+ }
error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,
&elantech_attr_group);
@@ -806,7 +929,7 @@ int elantech_init(struct psmouse *psmouse)
psmouse->protocol_handler = elantech_process_byte;
psmouse->disconnect = elantech_disconnect;
psmouse->reconnect = elantech_reconnect;
- psmouse->pktsize = etd->hw_version == 2 ? 6 : 4;
+ psmouse->pktsize = etd->hw_version == 1 ? 4 : 6;
return 0;
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
index fabb2b99615..40787d60bbf 100644
--- a/drivers/input/mouse/elantech.h
+++ b/drivers/input/mouse/elantech.h
@@ -16,12 +16,14 @@
/*
* Command values for Synaptics style queries
*/
+#define ETP_FW_ID_QUERY 0x00
#define ETP_FW_VERSION_QUERY 0x01
#define ETP_CAPABILITIES_QUERY 0x02
/*
* Command values for register reading or writing
*/
+#define ETP_REGISTER_RW 0x00
#define ETP_REGISTER_READ 0x10
#define ETP_REGISTER_WRITE 0x11
@@ -110,6 +112,9 @@ struct elantech_data {
bool reports_pressure;
unsigned char hw_version;
unsigned int fw_version;
+ unsigned int x_max;
+ unsigned int y_max;
+ unsigned int y_max_2ft;
unsigned int single_finger_reports;
unsigned char parity[256];
};