From patchwork Thu Aug 25 16:57:33 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Seth Forshee X-Patchwork-Id: 111627 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from chlorine.canonical.com (chlorine.canonical.com [91.189.94.204]) by ozlabs.org (Postfix) with ESMTP id 3E8C5B6F9F for ; Fri, 26 Aug 2011 02:58:13 +1000 (EST) Received: from localhost ([127.0.0.1] helo=chlorine.canonical.com) by chlorine.canonical.com with esmtp (Exim 4.71) (envelope-from ) id 1QwdFZ-00014x-0L; Thu, 25 Aug 2011 16:57:53 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by chlorine.canonical.com with esmtp (Exim 4.71) (envelope-from ) id 1QwdFQ-00014Q-EB for kernel-team@lists.ubuntu.com; Thu, 25 Aug 2011 16:57:46 +0000 Received: from c-69-254-227-224.hsd1.ks.comcast.net ([69.254.227.224] helo=canonical.com) by youngberry.canonical.com with esmtpsa (TLS1.0:DHE_RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1QwdFP-0004WG-RJ for kernel-team@lists.ubuntu.com; Thu, 25 Aug 2011 16:57:44 +0000 From: Seth Forshee To: kernel-team@lists.ubuntu.com Subject: [PATCH 1/2][Oneiric] UBUNTU: SAUCE: (no-up) Input: elantech - Add v3 hardware support Date: Thu, 25 Aug 2011 11:57:33 -0500 Message-Id: <1314291454-11157-2-git-send-email-seth.forshee@canonical.com> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1314291454-11157-1-git-send-email-seth.forshee@canonical.com> References: <1314291454-11157-1-git-send-email-seth.forshee@canonical.com> X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.13 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: kernel-team-bounces@lists.ubuntu.com Errors-To: kernel-team-bounces@lists.ubuntu.com 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 , Tom Lin , and Mark A. Stratman , whose work on other patcesh revealed the workings of the v3 protocol. Signed-off-by: Seth Forshee --- drivers/input/mouse/elantech.c | 181 +++++++++++++++++++++++++++++++++------- drivers/input/mouse/elantech.h | 5 + 2 files changed, 157 insertions(+), 29 deletions(-) diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 3250356..642cde0 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 fabb2b9..40787d6 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]; };