[kernel/f15] Add patches queued for 3.2 for elantech driver (rhbz 728607)

Josh Boyer jwboyer at fedoraproject.org
Thu Nov 3 13:42:06 UTC 2011


commit c7fa63c78c25bda3ac1af342dd45c96f6e148980
Author: Josh Boyer <jwboyer at redhat.com>
Date:   Thu Nov 3 09:35:03 2011 -0400

    Add patches queued for 3.2 for elantech driver (rhbz 728607)

 elantech.patch | 2116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 kernel.spec    |   11 +-
 2 files changed, 2126 insertions(+), 1 deletions(-)
---
diff --git a/elantech.patch b/elantech.patch
new file mode 100644
index 0000000..255efd2
--- /dev/null
+++ b/elantech.patch
@@ -0,0 +1,2116 @@
+From 13ac768d9a8731c8b3bab7d6c86520f290272fe7 Mon Sep 17 00:00:00 2001
+From: JJ Ding <jj_ding at emc.com.tw>
+Date: Fri, 9 Sep 2011 10:22:19 -0700
+Subject: Input: elantech - correct x, y value range for v2 hardware
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+x, y values are actually 12-bit long. Also update protocol document to
+reflect the change.
+
+Signed-off-by: JJ Ding <jj_ding at emc.com.tw>
+Acked-by: Daniel Kurtz <djkurtz at chromium.org>
+Acked-by: Éric Piel <eric.piel at tremplin-utc.net>
+Signed-off-by: Dmitry Torokhov <dtor at mail.ru>
+---
+ Documentation/input/elantech.txt |    8 ++++----
+ drivers/input/mouse/elantech.c   |    8 ++++----
+ 2 files changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/Documentation/input/elantech.txt b/Documentation/input/elantech.txt
+index db798af..bce9941 100644
+--- a/Documentation/input/elantech.txt
++++ b/Documentation/input/elantech.txt
+@@ -389,14 +389,14 @@ byte 0:
+ byte 1:
+ 
+    bit   7   6   5   4   3   2   1   0
+-	 p7  p6  p5  p4  .  x10 x9  x8
++	 p7  p6  p5  p4 x11 x10 x9  x8
+ 
+ byte 2:
+ 
+    bit   7   6   5   4   3   2   1   0
+ 	 x7  x6  x5  x4  x3  x2  x1  x0
+ 
+-         x10..x0 = absolute x value (horizontal)
++         x11..x0 = absolute x value (horizontal)
+ 
+ byte 3:
+ 
+@@ -420,7 +420,7 @@ byte 3:
+ byte 4:
+ 
+    bit   7   6   5   4   3   2   1   0
+-        p3  p1  p2  p0   .   .  y9  y8
++        p3  p1  p2  p0  y11 y10 y9  y8
+ 
+ 	 p7..p0 = pressure (not EF113)
+ 
+@@ -429,7 +429,7 @@ byte 5:
+    bit   7   6   5   4   3   2   1   0
+         y7  y6  y5  y4  y3  y2  y1  y0
+ 
+-         y9..y0 = absolute y value (vertical)
++         y11..y0 = absolute y value (vertical)
+ 
+ 
+ 4.2.2 Two finger touch
+diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
+index 3250356..da161da 100644
+--- a/drivers/input/mouse/elantech.c
++++ b/drivers/input/mouse/elantech.c
+@@ -290,15 +290,15 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
+ 		/* pass through... */
+ 	case 1:
+ 		/*
+-		 * byte 1:  .   .   .   .   .  x10 x9  x8
++		 * byte 1:  .   .   .   .  x11 x10 x9  x8
+ 		 * byte 2: x7  x6  x5  x4  x4  x2  x1  x0
+ 		 */
+-		x1 = ((packet[1] & 0x07) << 8) | packet[2];
++		x1 = ((packet[1] & 0x0f) << 8) | packet[2];
+ 		/*
+-		 * byte 4:  .   .   .   .   .   .  y9  y8
++		 * byte 4:  .   .   .   .  y11 y10 y9  y8
+ 		 * byte 5: y7  y6  y5  y4  y3  y2  y1  y0
+ 		 */
+-		y1 = ETP_YMAX_V2 - (((packet[4] & 0x03) << 8) | packet[5]);
++		y1 = ETP_YMAX_V2 - (((packet[4] & 0x0f) << 8) | packet[5]);
+ 
+ 		input_report_abs(dev, ABS_X, x1);
+ 		input_report_abs(dev, ABS_Y, y1);
+-- 
+1.7.6.4
+
+
+From c47c9334b4ebb6ecb565d9bf834df170fcd09484 Mon Sep 17 00:00:00 2001
+From: JJ Ding <jj_ding at emc.com.tw>
+Date: Fri, 9 Sep 2011 10:22:58 -0700
+Subject: Input: elantech - get rid of ETP_2FT_* in elantech.h
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+For two finger touches the coordinate of each finger gets reported
+separately but with reduced resolution.
+
+With this change, we now have the same range for ST and MT data and
+scale MT data because it has lower resolution to match ST.
+
+Suggested-by: Dmitry Torokhov <dmitry.torokhov at gmail.com>
+Signed-off-by: JJ Ding <jj_ding at emc.com.tw>
+Acked-by: Daniel Kurtz <djkurtz at chromium.org>
+Acked-by: Éric Piel <eric.piel at tremplin-utc.net>
+Signed-off-by: Dmitry Torokhov <dtor at mail.ru>
+---
+ drivers/input/mouse/elantech.c |   28 +++++++++++++---------------
+ drivers/input/mouse/elantech.h |   11 -----------
+ 2 files changed, 13 insertions(+), 26 deletions(-)
+
+diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
+index da161da..cd8e2e5 100644
+--- a/drivers/input/mouse/elantech.c
++++ b/drivers/input/mouse/elantech.c
+@@ -273,11 +273,11 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
+ 	struct elantech_data *etd = psmouse->private;
+ 	struct input_dev *dev = psmouse->dev;
+ 	unsigned char *packet = psmouse->packet;
+-	unsigned int fingers, x1 = 0, y1 = 0, x2 = 0, y2 = 0, width = 0, pres = 0;
++	unsigned int fingers, x1 = 0, y1 = 0, x2 = 0, y2 = 0;
++	unsigned int width = 0, pres = 0;
+ 
+ 	/* byte 0: n1  n0   .   .   .   .   R   L */
+ 	fingers = (packet[0] & 0xc0) >> 6;
+-	input_report_key(dev, BTN_TOUCH, fingers != 0);
+ 
+ 	switch (fingers) {
+ 	case 3:
+@@ -300,9 +300,6 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
+ 		 */
+ 		y1 = ETP_YMAX_V2 - (((packet[4] & 0x0f) << 8) | packet[5]);
+ 
+-		input_report_abs(dev, ABS_X, x1);
+-		input_report_abs(dev, ABS_Y, y1);
+-
+ 		pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);
+ 		width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4);
+ 		break;
+@@ -314,22 +311,18 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
+ 		 * byte 0:  .   .  ay8 ax8  .   .   .   .
+ 		 * byte 1: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0
+ 		 */
+-		x1 = ((packet[0] & 0x10) << 4) | packet[1];
++		x1 = (((packet[0] & 0x10) << 4) | packet[1]) << 2;
+ 		/* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */
+-		y1 = ETP_2FT_YMAX - (((packet[0] & 0x20) << 3) | packet[2]);
++		y1 = ETP_YMAX_V2 -
++			((((packet[0] & 0x20) << 3) | packet[2]) << 2);
+ 		/*
+ 		 * byte 3:  .   .  by8 bx8  .   .   .   .
+ 		 * byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0
+ 		 */
+-		x2 = ((packet[3] & 0x10) << 4) | packet[4];
++		x2 = (((packet[3] & 0x10) << 4) | packet[4]) << 2;
+ 		/* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */
+-		y2 = ETP_2FT_YMAX - (((packet[3] & 0x20) << 3) | packet[5]);
+-		/*
+-		 * For compatibility with the X Synaptics driver scale up
+-		 * one coordinate and report as ordinary mouse movent
+-		 */
+-		input_report_abs(dev, ABS_X, x1 << 2);
+-		input_report_abs(dev, ABS_Y, y1 << 2);
++		y2 = ETP_YMAX_V2 -
++			((((packet[3] & 0x20) << 3) | packet[5]) << 2);
+ 
+ 		/* Unknown so just report sensible values */
+ 		pres = 127;
+@@ -337,6 +330,11 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
+ 		break;
+ 	}
+ 
++	input_report_key(dev, BTN_TOUCH, fingers != 0);
++	if (fingers != 0) {
++		input_report_abs(dev, ABS_X, x1);
++		input_report_abs(dev, ABS_Y, y1);
++	}
+ 	elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
+ 	input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
+ 	input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
+diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
+index fabb2b9..1c5894e 100644
+--- a/drivers/input/mouse/elantech.h
++++ b/drivers/input/mouse/elantech.h
+@@ -82,17 +82,6 @@
+ #define ETP_WMIN_V2			0
+ #define ETP_WMAX_V2			15
+ 
+-/*
+- * For two finger touches the coordinate of each finger gets reported
+- * separately but with reduced resolution.
+- */
+-#define ETP_2FT_FUZZ			4
+-
+-#define ETP_2FT_XMIN			(  0 + ETP_2FT_FUZZ)
+-#define ETP_2FT_XMAX			(288 - ETP_2FT_FUZZ)
+-#define ETP_2FT_YMIN			(  0 + ETP_2FT_FUZZ)
+-#define ETP_2FT_YMAX			(192 - ETP_2FT_FUZZ)
+-
+ struct elantech_data {
+ 	unsigned char reg_10;
+ 	unsigned char reg_11;
+-- 
+1.7.6.4
+
+
+From aa719e391c1769c93ec42a30daffa4ffa2a8503c Mon Sep 17 00:00:00 2001
+From: JJ Ding <jj_ding at emc.com.tw>
+Date: Fri, 9 Sep 2011 10:26:16 -0700
+Subject: Input: elantech - use firmware provided x, y ranges
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+With newer hardware, the touchpad provides range info.
+Let's use it.
+
+Signed-off-by: JJ Ding <jj_ding at emc.com.tw>
+Acked-by: Daniel Kurtz <djkurtz at chromium.org>
+Acked-by: Éric Piel <eric.piel at tremplin-utc.net>
+Signed-off-by: Dmitry Torokhov <dtor at mail.ru>
+---
+ drivers/input/mouse/elantech.c |   71 +++++++++++++++++++++++++++++++--------
+ drivers/input/mouse/elantech.h |    3 +-
+ 2 files changed, 58 insertions(+), 16 deletions(-)
+
+diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
+index cd8e2e5..296b6a6 100644
+--- a/drivers/input/mouse/elantech.c
++++ b/drivers/input/mouse/elantech.c
+@@ -223,7 +223,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);
+@@ -233,7 +233,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
+ 	input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+ 
+ 	if (etd->fw_version < 0x020000 &&
+-	    (etd->capabilities & ETP_CAP_HAS_ROCKER)) {
++	    (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
+ 		/* rocker up */
+ 		input_report_key(dev, BTN_FORWARD, packet[0] & 0x40);
+ 		/* rocker down */
+@@ -298,7 +298,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
+ 		 * byte 4:  .   .   .   .  y11 y10 y9  y8
+ 		 * byte 5: y7  y6  y5  y4  y3  y2  y1  y0
+ 		 */
+-		y1 = ETP_YMAX_V2 - (((packet[4] & 0x0f) << 8) | packet[5]);
++		y1 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);
+ 
+ 		pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);
+ 		width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4);
+@@ -313,7 +313,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
+ 		 */
+ 		x1 = (((packet[0] & 0x10) << 4) | packet[1]) << 2;
+ 		/* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */
+-		y1 = ETP_YMAX_V2 -
++		y1 = etd->y_max -
+ 			((((packet[0] & 0x20) << 3) | packet[2]) << 2);
+ 		/*
+ 		 * byte 3:  .   .  by8 bx8  .   .   .   .
+@@ -321,7 +321,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
+ 		 */
+ 		x2 = (((packet[3] & 0x10) << 4) | packet[4]) << 2;
+ 		/* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */
+-		y2 = ETP_YMAX_V2 -
++		y2 = etd->y_max -
+ 			((((packet[3] & 0x20) << 3) | packet[5]) << 2);
+ 
+ 		/* Unknown so just report sensible values */
+@@ -468,6 +468,41 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
+ 	return rc;
+ }
+ 
++static void elantech_set_range(struct psmouse *psmouse,
++			       unsigned int *x_min, unsigned int *y_min,
++			       unsigned int *x_max, unsigned int *y_max)
++{
++	struct elantech_data *etd = psmouse->private;
++	int i;
++
++	switch (etd->hw_version) {
++	case 1:
++		*x_min = ETP_XMIN_V1;
++		*y_min = ETP_YMIN_V1;
++		*x_max = ETP_XMAX_V1;
++		*y_max = ETP_YMAX_V1;
++		break;
++
++	case 2:
++		if (etd->fw_version == 0x020800 ||
++		    etd->fw_version == 0x020b00 ||
++		    etd->fw_version == 0x020030) {
++			*x_min = ETP_XMIN_V2;
++			*y_min = ETP_YMIN_V2;
++			*x_max = ETP_XMAX_V2;
++			*y_max = ETP_YMAX_V2;
++		} else {
++			i = (etd->fw_version > 0x020800 &&
++			     etd->fw_version < 0x020900) ? 1 : 2;
++			*x_min = 0;
++			*y_min = 0;
++			*x_max = (etd->capabilities[1] - i) * 64;
++			*y_max = (etd->capabilities[2] - i) * 64;
++		}
++		break;
++	}
++}
++
+ /*
+  * Set the appropriate event bits for the input subsystem
+  */
+@@ -475,6 +510,9 @@ static void elantech_set_input_params(struct psmouse *psmouse)
+ {
+ 	struct input_dev *dev = psmouse->dev;
+ 	struct elantech_data *etd = psmouse->private;
++	unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0;
++
++	elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max);
+ 
+ 	__set_bit(EV_KEY, dev->evbit);
+ 	__set_bit(EV_ABS, dev->evbit);
+@@ -492,18 +530,18 @@ static void elantech_set_input_params(struct psmouse *psmouse)
+ 	case 1:
+ 		/* Rocker button */
+ 		if (etd->fw_version < 0x020000 &&
+-		    (etd->capabilities & ETP_CAP_HAS_ROCKER)) {
++		    (etd->capabilities[0] & 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, x_min, x_max, 0, 0);
++		input_set_abs_params(dev, ABS_Y, y_min, 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);
++		input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
++		input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);
+ 		if (etd->reports_pressure) {
+ 			input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2,
+ 					     ETP_PMAX_V2, 0, 0);
+@@ -512,10 +550,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, x_min, x_max, 0, 0);
++		input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
+ 		break;
+ 	}
++
++	etd->y_max = y_max;
+ }
+ 
+ struct elantech_attr_data {
+@@ -769,13 +809,14 @@ int elantech_init(struct psmouse *psmouse)
+ 	pr_info("assuming hardware version %d, firmware version %d.%d.%d\n",
+ 		etd->hw_version, param[0], param[1], param[2]);
+ 
+-	if (synaptics_send_cmd(psmouse, ETP_CAPABILITIES_QUERY, param)) {
++	if (synaptics_send_cmd(psmouse, ETP_CAPABILITIES_QUERY,
++	    etd->capabilities)) {
+ 		pr_err("failed to query capabilities.\n");
+ 		goto init_fail;
+ 	}
+ 	pr_info("Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n",
+-		param[0], param[1], param[2]);
+-	etd->capabilities = param[0];
++		etd->capabilities[0], etd->capabilities[1],
++		etd->capabilities[2]);
+ 
+ 	/*
+ 	 * This firmware suffers from misreporting coordinates when
+diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
+index 1c5894e..b54ea27 100644
+--- a/drivers/input/mouse/elantech.h
++++ b/drivers/input/mouse/elantech.h
+@@ -93,13 +93,14 @@ struct elantech_data {
+ 	unsigned char reg_25;
+ 	unsigned char reg_26;
+ 	unsigned char debug;
+-	unsigned char capabilities;
++	unsigned char capabilities[3];
+ 	bool paritycheck;
+ 	bool jumpy_cursor;
+ 	bool reports_pressure;
+ 	unsigned char hw_version;
+ 	unsigned int fw_version;
+ 	unsigned int single_finger_reports;
++	unsigned int y_max;
+ 	unsigned char parity[256];
+ };
+ 
+-- 
+1.7.6.4
+
+
+From 91f395e84dc83707b01c69e297894dbf992a566e Mon Sep 17 00:00:00 2001
+From: JJ Ding <jj_ding at emc.com.tw>
+Date: Fri, 9 Sep 2011 10:27:42 -0700
+Subject: Input: elantech - remove ETP_EDGE_FUZZ_V2
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Don't try to be too clever and remove ETP_EDGE_FUZZ_V2. X, Y ranges
+should be just the raw resolution of the device. Otherwise, they can
+cause underflow on the Y axis.
+
+Suggested-by: Éric Piel <eric.piel at tremplin-utc.net>
+Signed-off-by: JJ Ding <jj_ding at emc.com.tw>
+Acked-by: Daniel Kurtz <djkurtz at chromium.org>
+Acked-by: Éric Piel <eric.piel at tremplin-utc.net>
+Signed-off-by: Dmitry Torokhov <dtor at mail.ru>
+---
+ drivers/input/mouse/elantech.h |   15 ++++++---------
+ 1 files changed, 6 insertions(+), 9 deletions(-)
+
+diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
+index b54ea27..d9e6144 100644
+--- a/drivers/input/mouse/elantech.h
++++ b/drivers/input/mouse/elantech.h
+@@ -66,16 +66,13 @@
+ #define ETP_YMAX_V1			(384 - ETP_EDGE_FUZZ_V1)
+ 
+ /*
+- * It seems the resolution for hardware version 2 doubled.
+- * Hence the X and Y ranges are doubled too.
+- * The bezel around the pad also appears to be smaller
++ * The resolution for older v2 hardware doubled.
++ * (newer v2's firmware provides command so we can query)
+  */
+-#define ETP_EDGE_FUZZ_V2		8
+-
+-#define ETP_XMIN_V2			(   0 + ETP_EDGE_FUZZ_V2)
+-#define ETP_XMAX_V2			(1152 - ETP_EDGE_FUZZ_V2)
+-#define ETP_YMIN_V2			(   0 + ETP_EDGE_FUZZ_V2)
+-#define ETP_YMAX_V2			( 768 - ETP_EDGE_FUZZ_V2)
++#define ETP_XMIN_V2			0
++#define ETP_XMAX_V2			1152
++#define ETP_YMIN_V2			0
++#define ETP_YMAX_V2			768
+ 
+ #define ETP_PMIN_V2			0
+ #define ETP_PMAX_V2			255
+-- 
+1.7.6.4
+
+
+From cc66bde05d267cce5a6f64d877e63036505cc31e Mon Sep 17 00:00:00 2001
+From: JJ Ding <jj_ding at emc.com.tw>
+Date: Fri, 9 Sep 2011 10:28:04 -0700
+Subject: Input: elantech - packet checking for v2 hardware
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+For v2 hardware, there is no real parity check, but we can still check
+some constant bits for data integrity.
+
+Also rename elantech_check_parity_v1 to elantech_packet_check_v1 to make
+these packet checking function names consistent.
+
+Signed-off-by: JJ Ding <jj_ding at emc.com.tw>
+Acked-by: Daniel Kurtz <djkurtz at chromium.org>
+Acked-by: Éric Piel <eric.piel at tremplin-utc.net>
+Signed-off-by: Dmitry Torokhov <dtor at mail.ru>
+---
+ drivers/input/mouse/elantech.c |   39 ++++++++++++++++++++++++++++++++++-----
+ 1 files changed, 34 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
+index 296b6a6..f2e3a2b 100644
+--- a/drivers/input/mouse/elantech.c
++++ b/drivers/input/mouse/elantech.c
+@@ -350,7 +350,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
+ 	input_sync(dev);
+ }
+ 
+-static int elantech_check_parity_v1(struct psmouse *psmouse)
++static int elantech_packet_check_v1(struct psmouse *psmouse)
+ {
+ 	struct elantech_data *etd = psmouse->private;
+ 	unsigned char *packet = psmouse->packet;
+@@ -374,6 +374,34 @@ static int elantech_check_parity_v1(struct psmouse *psmouse)
+ 	       etd->parity[packet[3]] == p3;
+ }
+ 
++static int elantech_packet_check_v2(struct psmouse *psmouse)
++{
++	struct elantech_data *etd = psmouse->private;
++	unsigned char *packet = psmouse->packet;
++
++	/*
++	 * V2 hardware has two flavors. Older ones that do not report pressure,
++	 * and newer ones that reports pressure and width. With newer ones, all
++	 * packets (1, 2, 3 finger touch) have the same constant bits. With
++	 * older ones, 1/3 finger touch packets and 2 finger touch packets
++	 * have different constant bits.
++	 * With all three cases, if the constant bits are not exactly what I
++	 * expected, I consider them invalid.
++	 */
++	if (etd->reports_pressure)
++		return (packet[0] & 0x0c) == 0x04 &&
++		       (packet[3] & 0x0f) == 0x02;
++
++	if ((packet[0] & 0xc0) == 0x80)
++		return (packet[0] & 0x0c) == 0x0c &&
++		       (packet[3] & 0x0e) == 0x08;
++
++	return (packet[0] & 0x3c) == 0x3c &&
++	       (packet[1] & 0xf0) == 0x00 &&
++	       (packet[3] & 0x3e) == 0x38 &&
++	       (packet[4] & 0xf0) == 0x00;
++}
++
+ /*
+  * Process byte stream from mouse and handle complete packets
+  */
+@@ -389,14 +417,16 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
+ 
+ 	switch (etd->hw_version) {
+ 	case 1:
+-		if (etd->paritycheck && !elantech_check_parity_v1(psmouse))
++		if (etd->paritycheck && !elantech_packet_check_v1(psmouse))
+ 			return PSMOUSE_BAD_DATA;
+ 
+ 		elantech_report_absolute_v1(psmouse);
+ 		break;
+ 
+ 	case 2:
+-		/* We don't know how to check parity in protocol v2 */
++		if (etd->paritycheck && !elantech_packet_check_v2(psmouse))
++			return PSMOUSE_BAD_DATA;
++
+ 		elantech_report_absolute_v2(psmouse);
+ 		break;
+ 	}
+@@ -795,8 +825,7 @@ int elantech_init(struct psmouse *psmouse)
+ 		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;
++		etd->paritycheck = 1;
+ 
+ 		if (etd->fw_version >= 0x020800)
+ 			etd->reports_pressure = true;
+-- 
+1.7.6.4
+
+
+From 7e1abba14d70426b251ee58451ab1671af425409 Mon Sep 17 00:00:00 2001
+From: JJ Ding <jj_ding at emc.com.tw>
+Date: Fri, 9 Sep 2011 10:28:19 -0700
+Subject: Input: elantech - clean up elantech_init
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Group property setting code into elantech_set_properties.
+
+Signed-off-by: JJ Ding <jj_ding at emc.com.tw>
+Acked-by: Daniel Kurtz <djkurtz at chromium.org>
+Acked-by: Éric Piel <eric.piel at tremplin-utc.net>
+Signed-off-by: Dmitry Torokhov <dtor at mail.ru>
+---
+ drivers/input/mouse/elantech.c |   69 ++++++++++++++++++++++-----------------
+ 1 files changed, 39 insertions(+), 30 deletions(-)
+
+diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
+index f2e3a2b..1ab1c14 100644
+--- a/drivers/input/mouse/elantech.c
++++ b/drivers/input/mouse/elantech.c
+@@ -791,6 +791,42 @@ static int elantech_reconnect(struct psmouse *psmouse)
+ }
+ 
+ /*
++ * determine hardware version and set some properties according to it.
++ */
++static void elantech_set_properties(struct elantech_data *etd)
++{
++	/*
++	 * Assume every version greater than 0x020030 is new EeePC style
++	 * hardware with 6 byte packets, except 0x020600
++	 */
++	if (etd->fw_version < 0x020030 || etd->fw_version == 0x020600)
++		etd->hw_version = 1;
++	else
++		etd->hw_version = 2;
++
++	/*
++	 * Turn on packet checking by default.
++	 */
++	etd->paritycheck = 1;
++
++	/*
++	 * This firmware suffers from misreporting coordinates when
++	 * a touch action starts causing the mouse cursor or scrolled page
++	 * to jump. Enable a workaround.
++	 */
++	etd->jumpy_cursor =
++		(etd->fw_version == 0x020022 || etd->fw_version == 0x020600);
++
++	if (etd->hw_version == 2) {
++		/* For now show extra debug information */
++		etd->debug = 1;
++
++		if (etd->fw_version >= 0x020800)
++			etd->reports_pressure = true;
++	}
++}
++
++/*
+  * Initialize the touchpad and create sysfs entries
+  */
+ int elantech_init(struct psmouse *psmouse)
+@@ -816,26 +852,9 @@ 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) {
+-		etd->hw_version = 2;
+-		/* For now show extra debug information */
+-		etd->debug = 1;
+-		etd->paritycheck = 1;
+-
+-		if (etd->fw_version >= 0x020800)
+-			etd->reports_pressure = true;
+-
+-	} else {
+-		etd->hw_version = 1;
+-		etd->paritycheck = 1;
+-	}
+-
+-	pr_info("assuming hardware version %d, firmware version %d.%d.%d\n",
++	elantech_set_properties(etd);
++	pr_info("assuming hardware version %d "
++		"(with firmware version 0x%02x%02x%02x)\n",
+ 		etd->hw_version, param[0], param[1], param[2]);
+ 
+ 	if (synaptics_send_cmd(psmouse, ETP_CAPABILITIES_QUERY,
+@@ -847,16 +866,6 @@ int elantech_init(struct psmouse *psmouse)
+ 		etd->capabilities[0], etd->capabilities[1],
+ 		etd->capabilities[2]);
+ 
+-	/*
+-	 * This firmware suffers from misreporting coordinates when
+-	 * a touch action starts causing the mouse cursor or scrolled page
+-	 * to jump. Enable a workaround.
+-	 */
+-	if (etd->fw_version == 0x020022 || etd->fw_version == 0x020600) {
+-		pr_info("firmware version 2.0.34/2.6.0 detected, enabling jumpy cursor workaround\n");
+-		etd->jumpy_cursor = true;
+-	}
+-
+ 	if (elantech_set_absolute_mode(psmouse)) {
+ 		pr_err("failed to put touchpad into absolute mode.\n");
+ 		goto init_fail;
+-- 
+1.7.6.4
+
+
+From 709d9ebd2853032df0599c30d5ac61c8397679f3 Mon Sep 17 00:00:00 2001
+From: JJ Ding <jj_ding at emc.com.tw>
+Date: Fri, 9 Sep 2011 10:30:31 -0700
+Subject: Input: elantech - add v3 hardware support
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+v3 hardware's packet format is almost identical to v2 (one/three finger touch),
+except when sensing two finger touch, the hardware sends 12 bytes of data.
+
+Signed-off-by: JJ Ding <jj_ding at emc.com.tw>
+Acked-by: Daniel Kurtz <djkurtz at chromium.org>
+Acked-by: Éric Piel <eric.piel at tremplin-utc.net>
+Signed-off-by: Dmitry Torokhov <dtor at mail.ru>
+---
+ Documentation/input/elantech.txt |  117 +++++++++++++++++++--
+ drivers/input/mouse/elantech.c   |  208 ++++++++++++++++++++++++++++++++++----
+ drivers/input/mouse/elantech.h   |   12 ++
+ 3 files changed, 306 insertions(+), 31 deletions(-)
+
+diff --git a/Documentation/input/elantech.txt b/Documentation/input/elantech.txt
+index bce9941..cee08ee 100644
+--- a/Documentation/input/elantech.txt
++++ b/Documentation/input/elantech.txt
+@@ -16,15 +16,22 @@ Contents
+ 
+  1. Introduction
+  2. Extra knobs
+- 3. Hardware version 1
+-    3.1 Registers
+-    3.2 Native relative mode 4 byte packet format
+-    3.3 Native absolute mode 4 byte packet format
+- 4. Hardware version 2
++ 3. Differentiating hardware versions
++ 4. Hardware version 1
+     4.1 Registers
+-    4.2 Native absolute mode 6 byte packet format
+-        4.2.1 One finger touch
+-        4.2.2 Two finger touch
++    4.2 Native relative mode 4 byte packet format
++    4.3 Native absolute mode 4 byte packet format
++ 5. Hardware version 2
++    5.1 Registers
++    5.2 Native absolute mode 6 byte packet format
++        5.2.1 Parity checking and packet re-synchronization
++        5.2.2 One/Three finger touch
++        5.2.3 Two finger touch
++ 6. Hardware version 3
++    6.1 Registers
++    6.2 Native absolute mode 6 byte packet format
++        6.2.1 One/Three finger touch
++        6.2.2 Two finger touch
+ 
+ 
+ 
+@@ -375,7 +382,7 @@ For all the other ones, there are just a few constant bits:
+ 
+ In case an error is detected, all the packets are shifted by one (and packet[0] is discarded).
+ 
+-5.2.1 One/Three finger touch
++5.2.2 One/Three finger touch
+       ~~~~~~~~~~~~~~~~
+ 
+ byte 0:
+@@ -384,7 +391,7 @@ byte 0:
+ 	 n1  n0  w3  w2   .   .   R   L
+ 
+          L, R = 1 when Left, Right mouse button pressed
+-         n1..n0 = numbers of fingers on touchpad
++         n1..n0 = number of fingers on touchpad
+ 
+ byte 1:
+ 
+@@ -432,7 +439,7 @@ byte 5:
+          y11..y0 = absolute y value (vertical)
+ 
+ 
+-4.2.2 Two finger touch
++5.2.3 Two finger touch
+       ~~~~~~~~~~~~~~~~
+ 
+ Note that the two pairs of coordinates are not exactly the coordinates of the
+@@ -446,7 +453,7 @@ byte 0:
+         n1  n0  ay8 ax8  .   .   R   L
+ 
+          L, R = 1 when Left, Right mouse button pressed
+-         n1..n0 = numbers of fingers on touchpad
++         n1..n0 = number of fingers on touchpad
+ 
+ byte 1:
+ 
+@@ -480,3 +487,89 @@ byte 5:
+         by7 by8 by5 by4 by3 by2 by1 by0
+ 
+          by8..by0 = upper-right finger absolute y value
++
++/////////////////////////////////////////////////////////////////////////////
++
++6. Hardware version 3
++   ==================
++
++6.1 Registers
++    ~~~~~~~~~
++* reg_10
++
++   bit   7   6   5   4   3   2   1   0
++         0   0   0   0   0   0   0   A
++
++         A: 1 = enable absolute tracking
++
++6.2 Native absolute mode 6 byte packet format
++    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++1 and 3 finger touch shares the same 6-byte packet format, except that
++3 finger touch only reports the position of the center of all three fingers.
++
++Firmware would send 12 bytes of data for 2 finger touch.
++
++Note on debounce:
++In case the box has unstable power supply or other electricity issues, or
++when number of finger changes, F/W would send "debounce packet" to inform
++driver that the hardware is in debounce status.
++The debouce packet has the following signature:
++    byte 0: 0xc4
++    byte 1: 0xff
++    byte 2: 0xff
++    byte 3: 0x02
++    byte 4: 0xff
++    byte 5: 0xff
++When we encounter this kind of packet, we just ignore it.
++
++6.2.1 One/Three finger touch
++      ~~~~~~~~~~~~~~~~~~~~~~
++
++byte 0:
++
++   bit   7   6   5   4   3   2   1   0
++        n1  n0  w3  w2   0   1   R   L
++
++        L, R = 1 when Left, Right mouse button pressed
++        n1..n0 = number of fingers on touchpad
++
++byte 1:
++
++   bit   7   6   5   4   3   2   1   0
++        p7  p6  p5  p4 x11 x10  x9  x8
++
++byte 2:
++
++   bit   7   6   5   4   3   2   1   0
++        x7  x6  x5  x4  x3  x2  x1  x0
++
++        x11..x0 = absolute x value (horizontal)
++
++byte 3:
++
++   bit   7   6   5   4   3   2   1   0
++         0   0  w1  w0   0   0   1   0
++
++         w3..w0 = width of the finger touch
++
++byte 4:
++
++   bit   7   6   5   4   3   2   1   0
++        p3  p1  p2  p0  y11 y10 y9  y8
++
++        p7..p0 = pressure
++
++byte 5:
++
++   bit   7   6   5   4   3   2   1   0
++        y7  y6  y5  y4  y3  y2  y1  y0
++
++        y11..y0 = absolute y value (vertical)
++
++6.2.2 Two finger touch
++      ~~~~~~~~~~~~~~~~
++
++The packet format is exactly the same for two finger touch, except the hardware
++sends two 6 byte packets. The first packet contains data for the first finger,
++the second packet has data for the second finger. So for two finger touch a
++total of 12 bytes are sent.
+diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
+index 1ab1c14..9cfc70a 100644
+--- a/drivers/input/mouse/elantech.c
++++ b/drivers/input/mouse/elantech.c
+@@ -108,6 +108,16 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
+ 			rc = -1;
+ 		}
+ 		break;
++
++	case 3:
++		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;
++		}
++		break;
+ 	}
+ 
+ 	if (rc)
+@@ -154,6 +164,18 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
+ 			rc = -1;
+ 		}
+ 		break;
++
++	case 3:
++		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, val) ||
++		    elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) {
++			rc = -1;
++		}
++		break;
+ 	}
+ 
+ 	if (rc)
+@@ -350,6 +372,84 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
+ 	input_sync(dev);
+ }
+ 
++/*
++ * Interpret complete data packets and report absolute mode input events for
++ * hardware version 3. (12 byte packets for two fingers)
++ */
++static void elantech_report_absolute_v3(struct psmouse *psmouse,
++					int packet_type)
++{
++	struct input_dev *dev = psmouse->dev;
++	struct elantech_data *etd = psmouse->private;
++	unsigned char *packet = psmouse->packet;
++	unsigned int fingers = 0, x1 = 0, y1 = 0, x2 = 0, y2 = 0;
++	unsigned int width = 0, pres = 0;
++
++	/* byte 0: n1  n0   .   .   .   .   R   L */
++	fingers = (packet[0] & 0xc0) >> 6;
++
++	switch (fingers) {
++	case 3:
++	case 1:
++		/*
++		 * byte 1:  .   .   .   .  x11 x10 x9  x8
++		 * byte 2: x7  x6  x5  x4  x4  x2  x1  x0
++		 */
++		x1 = ((packet[1] & 0x0f) << 8) | packet[2];
++		/*
++		 * byte 4:  .   .   .   .  y11 y10 y9  y8
++		 * byte 5: y7  y6  y5  y4  y3  y2  y1  y0
++		 */
++		y1 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);
++		break;
++
++	case 2:
++		if (packet_type == PACKET_V3_HEAD) {
++			/*
++			 * byte 1:   .    .    .    .  ax11 ax10 ax9  ax8
++			 * byte 2: ax7  ax6  ax5  ax4  ax3  ax2  ax1  ax0
++			 */
++			etd->prev_x = ((packet[1] & 0x0f) << 8) | packet[2];
++			/*
++			 * byte 4:   .    .    .    .  ay11 ay10 ay9  ay8
++			 * byte 5: ay7  ay6  ay5  ay4  ay3  ay2  ay1  ay0
++			 */
++			etd->prev_y = etd->y_max -
++				(((packet[4] & 0x0f) << 8) | packet[5]);
++			/*
++			 * wait for next packet
++			 */
++			return;
++		}
++
++		/* packet_type == PACKET_V3_TAIL */
++		x1 = etd->prev_x;
++		y1 = etd->prev_y;
++		x2 = ((packet[1] & 0x0f) << 8) | packet[2];
++		y2 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);
++		break;
++	}
++
++	pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);
++	width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4);
++
++	input_report_key(dev, BTN_TOUCH, fingers != 0);
++	if (fingers != 0) {
++		input_report_abs(dev, ABS_X, x1);
++		input_report_abs(dev, ABS_Y, y1);
++	}
++	elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
++	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_key(dev, BTN_LEFT, packet[0] & 0x01);
++	input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
++	input_report_abs(dev, ABS_PRESSURE, pres);
++	input_report_abs(dev, ABS_TOOL_WIDTH, width);
++
++	input_sync(dev);
++}
++
+ static int elantech_packet_check_v1(struct psmouse *psmouse)
+ {
+ 	struct elantech_data *etd = psmouse->private;
+@@ -403,11 +503,37 @@ static int elantech_packet_check_v2(struct psmouse *psmouse)
+ }
+ 
+ /*
++ * We check the constant bits to determine what packet type we get,
++ * so packet checking is mandatory for v3 hardware.
++ */
++static int elantech_packet_check_v3(struct psmouse *psmouse)
++{
++	const u8 debounce_packet[] = { 0xc4, 0xff, 0xff, 0x02, 0xff, 0xff };
++	unsigned char *packet = psmouse->packet;
++
++	/*
++	 * check debounce first, it has the same signature in byte 0
++	 * and byte 3 as PACKET_V3_HEAD.
++	 */
++	if (!memcmp(packet, debounce_packet, sizeof(debounce_packet)))
++		return PACKET_DEBOUNCE;
++
++	if ((packet[0] & 0x0c) == 0x04 && (packet[3] & 0xcf) == 0x02)
++		return PACKET_V3_HEAD;
++
++	if ((packet[0] & 0x0c) == 0x0c && (packet[3] & 0xce) == 0x0c)
++		return PACKET_V3_TAIL;
++
++	return PACKET_UNKNOWN;
++}
++
++/*
+  * Process byte stream from mouse and handle complete packets
+  */
+ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
+ {
+ 	struct elantech_data *etd = psmouse->private;
++	int packet_type;
+ 
+ 	if (psmouse->pktcnt < psmouse->pktsize)
+ 		return PSMOUSE_GOOD_DATA;
+@@ -429,6 +555,18 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
+ 
+ 		elantech_report_absolute_v2(psmouse);
+ 		break;
++
++	case 3:
++		packet_type = elantech_packet_check_v3(psmouse);
++		/* ignore debounce */
++		if (packet_type == PACKET_DEBOUNCE)
++			return PSMOUSE_FULL_PACKET;
++
++		if (packet_type == PACKET_UNKNOWN)
++			return PSMOUSE_BAD_DATA;
++
++		elantech_report_absolute_v3(psmouse, packet_type);
++		break;
+ 	}
+ 
+ 	return PSMOUSE_FULL_PACKET;
+@@ -463,8 +601,15 @@ 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) {
+@@ -498,11 +643,12 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
+ 	return rc;
+ }
+ 
+-static void elantech_set_range(struct psmouse *psmouse,
+-			       unsigned int *x_min, unsigned int *y_min,
+-			       unsigned int *x_max, unsigned int *y_max)
++static int elantech_set_range(struct psmouse *psmouse,
++			      unsigned int *x_min, unsigned int *y_min,
++			      unsigned int *x_max, unsigned int *y_max)
+ {
+ 	struct elantech_data *etd = psmouse->private;
++	unsigned char param[3];
+ 	int i;
+ 
+ 	switch (etd->hw_version) {
+@@ -530,19 +676,30 @@ static void elantech_set_range(struct psmouse *psmouse,
+ 			*y_max = (etd->capabilities[2] - i) * 64;
+ 		}
+ 		break;
++
++	case 3:
++		if (synaptics_send_cmd(psmouse, ETP_FW_ID_QUERY, param))
++			return -1;
++
++		*x_max = (0x0f & param[0]) << 8 | param[1];
++		*y_max = (0xf0 & param[0]) << 4 | param[2];
++		break;
+ 	}
++
++	return 0;
+ }
+ 
+ /*
+  * 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 int x_min = 0, y_min = 0, x_max = 0, y_max = 0;
+ 
+-	elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max);
++	if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max))
++		return -1;
+ 
+ 	__set_bit(EV_KEY, dev->evbit);
+ 	__set_bit(EV_ABS, dev->evbit);
+@@ -570,6 +727,9 @@ static void elantech_set_input_params(struct psmouse *psmouse)
+ 
+ 	case 2:
+ 		__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
++		__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
++		/* fall through */
++	case 3:
+ 		input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
+ 		input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);
+ 		if (etd->reports_pressure) {
+@@ -578,7 +738,6 @@ static void elantech_set_input_params(struct psmouse *psmouse)
+ 			input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
+ 					     ETP_WMAX_V2, 0, 0);
+ 		}
+-		__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
+ 		input_mt_init_slots(dev, 2);
+ 		input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
+ 		input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
+@@ -586,6 +745,8 @@ static void elantech_set_input_params(struct psmouse *psmouse)
+ 	}
+ 
+ 	etd->y_max = y_max;
++
++	return 0;
+ }
+ 
+ struct elantech_attr_data {
+@@ -727,7 +888,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;
+@@ -793,16 +955,16 @@ static int elantech_reconnect(struct psmouse *psmouse)
+ /*
+  * determine hardware version and set some properties according to it.
+  */
+-static void elantech_set_properties(struct elantech_data *etd)
++static int elantech_set_properties(struct elantech_data *etd)
+ {
+-	/*
+-	 * Assume every version greater than 0x020030 is new EeePC style
+-	 * hardware with 6 byte packets, except 0x020600
+-	 */
+ 	if (etd->fw_version < 0x020030 || etd->fw_version == 0x020600)
+ 		etd->hw_version = 1;
+-	else
++	else if (etd->fw_version < 0x150600)
+ 		etd->hw_version = 2;
++	else if ((etd->fw_version & 0x0f0000) >> 16 == 5)
++		etd->hw_version = 3;
++	else
++		return -1;
+ 
+ 	/*
+ 	 * Turn on packet checking by default.
+@@ -817,13 +979,15 @@ static void elantech_set_properties(struct elantech_data *etd)
+ 	etd->jumpy_cursor =
+ 		(etd->fw_version == 0x020022 || etd->fw_version == 0x020600);
+ 
+-	if (etd->hw_version == 2) {
++	if (etd->hw_version > 1) {
+ 		/* For now show extra debug information */
+ 		etd->debug = 1;
+ 
+ 		if (etd->fw_version >= 0x020800)
+ 			etd->reports_pressure = true;
+ 	}
++
++	return 0;
+ }
+ 
+ /*
+@@ -850,9 +1014,12 @@ int elantech_init(struct psmouse *psmouse)
+ 		pr_err("failed to query firmware version.\n");
+ 		goto init_fail;
+ 	}
+-
+ 	etd->fw_version = (param[0] << 16) | (param[1] << 8) | param[2];
+-	elantech_set_properties(etd);
++
++	if (elantech_set_properties(etd)) {
++		pr_err("unknown hardware version, aborting...\n");
++		goto init_fail;
++	}
+ 	pr_info("assuming hardware version %d "
+ 		"(with firmware version 0x%02x%02x%02x)\n",
+ 		etd->hw_version, param[0], param[1], param[2]);
+@@ -871,7 +1038,10 @@ int elantech_init(struct psmouse *psmouse)
+ 		goto init_fail;
+ 	}
+ 
+-	elantech_set_input_params(psmouse);
++	if (elantech_set_input_params(psmouse)) {
++		pr_err("failed to query touchpad range.\n");
++		goto init_fail;
++	}
+ 
+ 	error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,
+ 				   &elantech_attr_group);
+@@ -883,7 +1053,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 ? 6 : 4;
+ 
+ 	return 0;
+ 
+diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
+index d9e6144..236c33c 100644
+--- a/drivers/input/mouse/elantech.h
++++ b/drivers/input/mouse/elantech.h
+@@ -16,6 +16,7 @@
+ /*
+  * Command values for Synaptics style queries
+  */
++#define ETP_FW_ID_QUERY			0x00
+ #define ETP_FW_VERSION_QUERY		0x01
+ #define ETP_CAPABILITIES_QUERY		0x02
+ 
+@@ -24,6 +25,7 @@
+  */
+ #define ETP_REGISTER_READ		0x10
+ #define ETP_REGISTER_WRITE		0x11
++#define ETP_REGISTER_READWRITE		0x00
+ 
+ /*
+  * Hardware version 2 custom PS/2 command value
+@@ -79,6 +81,14 @@
+ #define ETP_WMIN_V2			0
+ #define ETP_WMAX_V2			15
+ 
++/*
++ * v3 hardware has 2 kinds of packet types.
++ */
++#define PACKET_UNKNOWN			0x01
++#define PACKET_DEBOUNCE			0x02
++#define PACKET_V3_HEAD			0x03
++#define PACKET_V3_TAIL			0x04
++
+ struct elantech_data {
+ 	unsigned char reg_10;
+ 	unsigned char reg_11;
+@@ -98,6 +108,8 @@ struct elantech_data {
+ 	unsigned int fw_version;
+ 	unsigned int single_finger_reports;
+ 	unsigned int y_max;
++	unsigned int prev_x;
++	unsigned int prev_y;
+ 	unsigned char parity[256];
+ };
+ 
+-- 
+1.7.6.4
+
+
+From 5936f37c68ab27b24d6f2faf23268a9aefd3092e Mon Sep 17 00:00:00 2001
+From: JJ Ding <jj_ding at emc.com.tw>
+Date: Fri, 9 Sep 2011 10:31:58 -0700
+Subject: Input: elantech - add v4 hardware support
+
+v4 hardware is a true multitouch capable touchpad (up to 5 fingers).
+The packet format is quite complex, please see protocol document for
+reference.
+
+Signed-off-by: JJ Ding <jj_ding at emc.com.tw>
+Signed-off-by: Dmitry Torokhov <dtor at mail.ru>
+---
+ Documentation/input/elantech.txt |  170 ++++++++++++++++++++++++++
+ drivers/input/mouse/elantech.c   |  249 ++++++++++++++++++++++++++++++++++++--
+ drivers/input/mouse/elantech.h   |   29 ++++-
+ 3 files changed, 432 insertions(+), 16 deletions(-)
+
+diff --git a/Documentation/input/elantech.txt b/Documentation/input/elantech.txt
+index cee08ee..5602eb7 100644
+--- a/Documentation/input/elantech.txt
++++ b/Documentation/input/elantech.txt
+@@ -32,6 +32,12 @@ Contents
+     6.2 Native absolute mode 6 byte packet format
+         6.2.1 One/Three finger touch
+         6.2.2 Two finger touch
++ 7. Hardware version 4
++    7.1 Registers
++    7.2 Native absolute mode 6 byte packet format
++        7.2.1 Status packet
++        7.2.2 Head packet
++        7.2.3 Motion packet
+ 
+ 
+ 
+@@ -573,3 +579,167 @@ The packet format is exactly the same for two finger touch, except the hardware
+ sends two 6 byte packets. The first packet contains data for the first finger,
+ the second packet has data for the second finger. So for two finger touch a
+ total of 12 bytes are sent.
++
++/////////////////////////////////////////////////////////////////////////////
++
++7. Hardware version 4
++   ==================
++
++7.1 Registers
++    ~~~~~~~~~
++* reg_07
++
++   bit   7   6   5   4   3   2   1   0
++         0   0   0   0   0   0   0   A
++
++         A: 1 = enable absolute tracking
++
++7.2 Native absolute mode 6 byte packet format
++    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++v4 hardware is a true multitouch touchpad, capable of tracking up to 5 fingers.
++Unfortunately, due to PS/2's limited bandwidth, its packet format is rather
++complex.
++
++Whenever the numbers or identities of the fingers changes, the hardware sends a
++status packet to indicate how many and which fingers is on touchpad, followed by
++head packets or motion packets. A head packet contains data of finger id, finger
++position (absolute x, y values), width, and pressure. A motion packet contains
++two fingers' position delta.
++
++For example, when status packet tells there are 2 fingers on touchpad, then we
++can expect two following head packets. If the finger status doesn't change,
++the following packets would be motion packets, only sending delta of finger
++position, until we receive a status packet.
++
++One exception is one finger touch. when a status packet tells us there is only
++one finger, the hardware would just send head packets afterwards.
++
++7.2.1 Status packet
++      ~~~~~~~~~~~~~
++
++byte 0:
++
++   bit   7   6   5   4   3   2   1   0
++         .   .   .   .   0   1   R   L
++
++         L, R = 1 when Left, Right mouse button pressed
++
++byte 1:
++
++   bit   7   6   5   4   3   2   1   0
++         .   .   . ft4 ft3 ft2 ft1 ft0
++
++         ft4 ft3 ft2 ft1 ft0 ftn = 1 when finger n is on touchpad
++
++byte 2: not used
++
++byte 3:
++
++   bit   7   6   5   4   3   2   1   0
++         .   .   .   1   0   0   0   0
++
++         constant bits
++
++byte 4:
++
++   bit   7   6   5   4   3   2   1   0
++         p   .   .   .   .   .   .   .
++
++         p = 1 for palm
++
++byte 5: not used
++
++7.2.2 Head packet
++      ~~~~~~~~~~~
++
++byte 0:
++
++   bit   7   6   5   4   3   2   1   0
++        w3  w2  w1  w0   0   1   R   L
++
++        L, R = 1 when Left, Right mouse button pressed
++        w3..w0 = finger width (spans how many trace lines)
++
++byte 1:
++
++   bit   7   6   5   4   3   2   1   0
++        p7  p6  p5  p4 x11 x10  x9  x8
++
++byte 2:
++
++   bit   7   6   5   4   3   2   1   0
++        x7  x6  x5  x4  x3  x2  x1  x0
++
++        x11..x0 = absolute x value (horizontal)
++
++byte 3:
++
++   bit   7   6   5   4   3   2   1   0
++       id2 id1 id0   1   0   0   0   1
++
++       id2..id0 = finger id
++
++byte 4:
++
++   bit   7   6   5   4   3   2   1   0
++        p3  p1  p2  p0  y11 y10 y9  y8
++
++        p7..p0 = pressure
++
++byte 5:
++
++   bit   7   6   5   4   3   2   1   0
++        y7  y6  y5  y4  y3  y2  y1  y0
++
++        y11..y0 = absolute y value (vertical)
++
++7.2.3 Motion packet
++      ~~~~~~~~~~~~~
++
++byte 0:
++
++   bit   7   6   5   4   3   2   1   0
++       id2 id1 id0   w   0   1   R   L
++
++       L, R = 1 when Left, Right mouse button pressed
++       id2..id0 = finger id
++       w = 1 when delta overflows (> 127 or < -128), in this case
++       firmware sends us (delta x / 5) and (delta y  / 5)
++
++byte 1:
++
++   bit   7   6   5   4   3   2   1   0
++        x7  x6  x5  x4  x3  x2  x1  x0
++
++        x7..x0 = delta x (two's complement)
++
++byte 2:
++
++   bit   7   6   5   4   3   2   1   0
++        y7  y6  y5  y4  y3  y2  y1  y0
++
++        y7..y0 = delta y (two's complement)
++
++byte 3:
++
++   bit   7   6   5   4   3   2   1   0
++       id2 id1 id0   1   0   0   1   0
++
++       id2..id0 = finger id
++
++byte 4:
++
++   bit   7   6   5   4   3   2   1   0
++        x7  x6  x5  x4  x3  x2  x1  x0
++
++        x7..x0 = delta x (two's complement)
++
++byte 5:
++
++   bit   7   6   5   4   3   2   1   0
++        y7  y6  y5  y4  y3  y2  y1  y0
++
++        y7..y0 = delta y (two's complement)
++
++        byte 0 ~ 2 for one finger
++        byte 3 ~ 5 for another
+diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
+index 9cfc70a..b8733b3 100644
+--- a/drivers/input/mouse/elantech.c
++++ b/drivers/input/mouse/elantech.c
+@@ -84,7 +84,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
+ 	unsigned char param[3];
+ 	int rc = 0;
+ 
+-	if (reg < 0x10 || reg > 0x26)
++	if (reg < 0x07 || reg > 0x26)
+ 		return -1;
+ 
+ 	if (reg > 0x11 && reg < 0x20)
+@@ -109,7 +109,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
+ 		}
+ 		break;
+ 
+-	case 3:
++	case 3 ... 4:
+ 		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) ||
+@@ -122,8 +122,10 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
+ 
+ 	if (rc)
+ 		pr_err("failed to read register 0x%02x.\n", reg);
+-	else
++	else if (etd->hw_version != 4)
+ 		*val = param[0];
++	else
++		*val = param[1];
+ 
+ 	return rc;
+ }
+@@ -137,7 +139,7 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
+ 	struct elantech_data *etd = psmouse->private;
+ 	int rc = 0;
+ 
+-	if (reg < 0x10 || reg > 0x26)
++	if (reg < 0x07 || reg > 0x26)
+ 		return -1;
+ 
+ 	if (reg > 0x11 && reg < 0x20)
+@@ -176,6 +178,20 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
+ 			rc = -1;
+ 		}
+ 		break;
++
++	case 4:
++		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, ETP_REGISTER_READWRITE) ||
++		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
++		    elantech_ps2_command(psmouse, NULL, val) ||
++		    elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) {
++			rc = -1;
++		}
++		break;
+ 	}
+ 
+ 	if (rc)
+@@ -409,12 +425,12 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse,
+ 			 * byte 1:   .    .    .    .  ax11 ax10 ax9  ax8
+ 			 * byte 2: ax7  ax6  ax5  ax4  ax3  ax2  ax1  ax0
+ 			 */
+-			etd->prev_x = ((packet[1] & 0x0f) << 8) | packet[2];
++			etd->mt[0].x = ((packet[1] & 0x0f) << 8) | packet[2];
+ 			/*
+ 			 * byte 4:   .    .    .    .  ay11 ay10 ay9  ay8
+ 			 * byte 5: ay7  ay6  ay5  ay4  ay3  ay2  ay1  ay0
+ 			 */
+-			etd->prev_y = etd->y_max -
++			etd->mt[0].y = etd->y_max -
+ 				(((packet[4] & 0x0f) << 8) | packet[5]);
+ 			/*
+ 			 * wait for next packet
+@@ -423,8 +439,8 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse,
+ 		}
+ 
+ 		/* packet_type == PACKET_V3_TAIL */
+-		x1 = etd->prev_x;
+-		y1 = etd->prev_y;
++		x1 = etd->mt[0].x;
++		y1 = etd->mt[0].y;
+ 		x2 = ((packet[1] & 0x0f) << 8) | packet[2];
+ 		y2 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);
+ 		break;
+@@ -450,6 +466,129 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse,
+ 	input_sync(dev);
+ }
+ 
++static void elantech_input_sync_v4(struct psmouse *psmouse)
++{
++	struct input_dev *dev = psmouse->dev;
++	unsigned char *packet = psmouse->packet;
++
++	input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
++	input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
++	input_mt_report_pointer_emulation(dev, true);
++	input_sync(dev);
++}
++
++static void process_packet_status_v4(struct psmouse *psmouse)
++{
++	struct input_dev *dev = psmouse->dev;
++	unsigned char *packet = psmouse->packet;
++	unsigned fingers;
++	int i;
++
++	/* notify finger state change */
++	fingers = packet[1] & 0x1f;
++	for (i = 0; i < ETP_MAX_FINGERS; i++) {
++		if ((fingers & (1 << i)) == 0) {
++			input_mt_slot(dev, i);
++			input_mt_report_slot_state(dev, MT_TOOL_FINGER, false);
++		}
++	}
++
++	elantech_input_sync_v4(psmouse);
++}
++
++static void process_packet_head_v4(struct psmouse *psmouse)
++{
++	struct input_dev *dev = psmouse->dev;
++	struct elantech_data *etd = psmouse->private;
++	unsigned char *packet = psmouse->packet;
++	int id = ((packet[3] & 0xe0) >> 5) - 1;
++	int pres, traces;
++
++	if (id < 0)
++		return;
++
++	etd->mt[id].x = ((packet[1] & 0x0f) << 8) | packet[2];
++	etd->mt[id].y = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);
++	pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);
++	traces = (packet[0] & 0xf0) >> 4;
++
++	input_mt_slot(dev, id);
++	input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
++
++	input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[id].x);
++	input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[id].y);
++	input_report_abs(dev, ABS_MT_PRESSURE, pres);
++	input_report_abs(dev, ABS_MT_TOUCH_MAJOR, traces * etd->width);
++	/* report this for backwards compatibility */
++	input_report_abs(dev, ABS_TOOL_WIDTH, traces);
++
++	elantech_input_sync_v4(psmouse);
++}
++
++static void process_packet_motion_v4(struct psmouse *psmouse)
++{
++	struct input_dev *dev = psmouse->dev;
++	struct elantech_data *etd = psmouse->private;
++	unsigned char *packet = psmouse->packet;
++	int weight, delta_x1 = 0, delta_y1 = 0, delta_x2 = 0, delta_y2 = 0;
++	int id, sid;
++
++	id = ((packet[0] & 0xe0) >> 5) - 1;
++	if (id < 0)
++		return;
++
++	sid = ((packet[3] & 0xe0) >> 5) - 1;
++	weight = (packet[0] & 0x10) ? ETP_WEIGHT_VALUE : 1;
++	/*
++	 * Motion packets give us the delta of x, y values of specific fingers,
++	 * but in two's complement. Let the compiler do the conversion for us.
++	 * Also _enlarge_ the numbers to int, in case of overflow.
++	 */
++	delta_x1 = (signed char)packet[1];
++	delta_y1 = (signed char)packet[2];
++	delta_x2 = (signed char)packet[4];
++	delta_y2 = (signed char)packet[5];
++
++	etd->mt[id].x += delta_x1 * weight;
++	etd->mt[id].y -= delta_y1 * weight;
++	input_mt_slot(dev, id);
++	input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[id].x);
++	input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[id].y);
++
++	if (sid >= 0) {
++		etd->mt[sid].x += delta_x2 * weight;
++		etd->mt[sid].y -= delta_y2 * weight;
++		input_mt_slot(dev, sid);
++		input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[sid].x);
++		input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[sid].y);
++	}
++
++	elantech_input_sync_v4(psmouse);
++}
++
++static void elantech_report_absolute_v4(struct psmouse *psmouse,
++					int packet_type)
++{
++	switch (packet_type) {
++	case PACKET_V4_STATUS:
++		process_packet_status_v4(psmouse);
++		break;
++
++	case PACKET_V4_HEAD:
++		process_packet_head_v4(psmouse);
++		break;
++
++	case PACKET_V4_MOTION:
++		process_packet_motion_v4(psmouse);
++		break;
++
++	case PACKET_UNKNOWN:
++	default:
++		/* impossible to get here */
++		break;
++	}
++}
++
+ static int elantech_packet_check_v1(struct psmouse *psmouse)
+ {
+ 	struct elantech_data *etd = psmouse->private;
+@@ -504,7 +643,7 @@ static int elantech_packet_check_v2(struct psmouse *psmouse)
+ 
+ /*
+  * We check the constant bits to determine what packet type we get,
+- * so packet checking is mandatory for v3 hardware.
++ * so packet checking is mandatory for v3 and later hardware.
+  */
+ static int elantech_packet_check_v3(struct psmouse *psmouse)
+ {
+@@ -527,6 +666,25 @@ static int elantech_packet_check_v3(struct psmouse *psmouse)
+ 	return PACKET_UNKNOWN;
+ }
+ 
++static int elantech_packet_check_v4(struct psmouse *psmouse)
++{
++	unsigned char *packet = psmouse->packet;
++
++	if ((packet[0] & 0x0c) == 0x04 &&
++	    (packet[3] & 0x1f) == 0x11)
++		return PACKET_V4_HEAD;
++
++	if ((packet[0] & 0x0c) == 0x04 &&
++	    (packet[3] & 0x1f) == 0x12)
++		return PACKET_V4_MOTION;
++
++	if ((packet[0] & 0x0c) == 0x04 &&
++	    (packet[3] & 0x1f) == 0x10)
++		return PACKET_V4_STATUS;
++
++	return PACKET_UNKNOWN;
++}
++
+ /*
+  * Process byte stream from mouse and handle complete packets
+  */
+@@ -567,6 +725,14 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
+ 
+ 		elantech_report_absolute_v3(psmouse, packet_type);
+ 		break;
++
++	case 4:
++		packet_type = elantech_packet_check_v4(psmouse);
++		if (packet_type == PACKET_UNKNOWN)
++			return PSMOUSE_BAD_DATA;
++
++		elantech_report_absolute_v4(psmouse, packet_type);
++		break;
+ 	}
+ 
+ 	return PSMOUSE_FULL_PACKET;
+@@ -610,6 +776,13 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
+ 			rc = -1;
+ 
+ 		break;
++
++	case 4:
++		etd->reg_07 = 0x01;
++		if (elantech_write_reg(psmouse, 0x07, etd->reg_07))
++			rc = -1;
++
++		goto skip_readback_reg_10; /* v4 has no reg 0x10 to read */
+ 	}
+ 
+ 	if (rc == 0) {
+@@ -637,6 +810,7 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
+ 		}
+ 	}
+ 
++ skip_readback_reg_10:
+ 	if (rc)
+ 		pr_err("failed to initialise registers.\n");
+ 
+@@ -645,10 +819,12 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
+ 
+ static int elantech_set_range(struct psmouse *psmouse,
+ 			      unsigned int *x_min, unsigned int *y_min,
+-			      unsigned int *x_max, unsigned int *y_max)
++			      unsigned int *x_max, unsigned int *y_max,
++			      unsigned int *width)
+ {
+ 	struct elantech_data *etd = psmouse->private;
+ 	unsigned char param[3];
++	unsigned char traces;
+ 	int i;
+ 
+ 	switch (etd->hw_version) {
+@@ -684,6 +860,19 @@ static int elantech_set_range(struct psmouse *psmouse,
+ 		*x_max = (0x0f & param[0]) << 8 | param[1];
+ 		*y_max = (0xf0 & param[0]) << 4 | param[2];
+ 		break;
++
++	case 4:
++		if (synaptics_send_cmd(psmouse, ETP_FW_ID_QUERY, param))
++			return -1;
++
++		*x_max = (0x0f & param[0]) << 8 | param[1];
++		*y_max = (0xf0 & param[0]) << 4 | param[2];
++		traces = etd->capabilities[1];
++		if ((traces < 2) || (traces > *x_max))
++			return -1;
++
++		*width = *x_max / (traces - 1);
++		break;
+ 	}
+ 
+ 	return 0;
+@@ -696,9 +885,9 @@ static int elantech_set_input_params(struct psmouse *psmouse)
+ {
+ 	struct input_dev *dev = psmouse->dev;
+ 	struct elantech_data *etd = psmouse->private;
+-	unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0;
++	unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0;
+ 
+-	if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max))
++	if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width))
+ 		return -1;
+ 
+ 	__set_bit(EV_KEY, dev->evbit);
+@@ -742,9 +931,37 @@ static int elantech_set_input_params(struct psmouse *psmouse)
+ 		input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
+ 		input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
+ 		break;
++
++	case 4:
++		__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
++		/* For X to recognize me as touchpad. */
++		input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
++		input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);
++		/*
++		 * range of pressure and width is the same as v2,
++		 * report ABS_PRESSURE, ABS_TOOL_WIDTH for compatibility.
++		 */
++		input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2,
++				     ETP_PMAX_V2, 0, 0);
++		input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
++				     ETP_WMAX_V2, 0, 0);
++		/* Multitouch capable pad, up to 5 fingers. */
++		input_mt_init_slots(dev, ETP_MAX_FINGERS);
++		input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
++		input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
++		input_set_abs_params(dev, ABS_MT_PRESSURE, ETP_PMIN_V2,
++				     ETP_PMAX_V2, 0, 0);
++		/*
++		 * The firmware reports how many trace lines the finger spans,
++		 * convert to surface unit as Protocol-B requires.
++		 */
++		input_set_abs_params(dev, ABS_MT_TOUCH_MAJOR, 0,
++				     ETP_WMAX_V2 * width, 0, 0);
++		break;
+ 	}
+ 
+ 	etd->y_max = y_max;
++	etd->width = width;
+ 
+ 	return 0;
+ }
+@@ -816,6 +1033,7 @@ static ssize_t elantech_set_int_attr(struct psmouse *psmouse,
+ 			    elantech_show_int_attr,			\
+ 			    elantech_set_int_attr)
+ 
++ELANTECH_INT_ATTR(reg_07, 0x07);
+ ELANTECH_INT_ATTR(reg_10, 0x10);
+ ELANTECH_INT_ATTR(reg_11, 0x11);
+ ELANTECH_INT_ATTR(reg_20, 0x20);
+@@ -829,6 +1047,7 @@ ELANTECH_INT_ATTR(debug, 0);
+ ELANTECH_INT_ATTR(paritycheck, 0);
+ 
+ static struct attribute *elantech_attrs[] = {
++	&psmouse_attr_reg_07.dattr.attr,
+ 	&psmouse_attr_reg_10.dattr.attr,
+ 	&psmouse_attr_reg_11.dattr.attr,
+ 	&psmouse_attr_reg_20.dattr.attr,
+@@ -957,12 +1176,16 @@ static int elantech_reconnect(struct psmouse *psmouse)
+  */
+ static int elantech_set_properties(struct elantech_data *etd)
+ {
++	int ver = (etd->fw_version & 0x0f0000) >> 16;
++
+ 	if (etd->fw_version < 0x020030 || etd->fw_version == 0x020600)
+ 		etd->hw_version = 1;
+ 	else if (etd->fw_version < 0x150600)
+ 		etd->hw_version = 2;
+-	else if ((etd->fw_version & 0x0f0000) >> 16 == 5)
++	else if (ver == 5)
+ 		etd->hw_version = 3;
++	else if (ver == 6)
++		etd->hw_version = 4;
+ 	else
+ 		return -1;
+ 
+diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
+index 236c33c..7ecaef0 100644
+--- a/drivers/input/mouse/elantech.h
++++ b/drivers/input/mouse/elantech.h
+@@ -82,14 +82,37 @@
+ #define ETP_WMAX_V2			15
+ 
+ /*
+- * v3 hardware has 2 kinds of packet types.
++ * v3 hardware has 2 kinds of packet types,
++ * v4 hardware has 3.
+  */
+ #define PACKET_UNKNOWN			0x01
+ #define PACKET_DEBOUNCE			0x02
+ #define PACKET_V3_HEAD			0x03
+ #define PACKET_V3_TAIL			0x04
++#define PACKET_V4_HEAD			0x05
++#define PACKET_V4_MOTION		0x06
++#define PACKET_V4_STATUS		0x07
++
++/*
++ * track up to 5 fingers for v4 hardware
++ */
++#define ETP_MAX_FINGERS			5
++
++/*
++ * weight value for v4 hardware
++ */
++#define ETP_WEIGHT_VALUE		5
++
++/*
++ * The base position for one finger, v4 hardware
++ */
++struct finger_pos {
++	unsigned int x;
++	unsigned int y;
++};
+ 
+ struct elantech_data {
++	unsigned char reg_07;
+ 	unsigned char reg_10;
+ 	unsigned char reg_11;
+ 	unsigned char reg_20;
+@@ -108,8 +131,8 @@ struct elantech_data {
+ 	unsigned int fw_version;
+ 	unsigned int single_finger_reports;
+ 	unsigned int y_max;
+-	unsigned int prev_x;
+-	unsigned int prev_y;
++	unsigned int width;
++	struct finger_pos mt[ETP_MAX_FINGERS];
+ 	unsigned char parity[256];
+ };
+ 
+-- 
+1.7.6.4
+
+
+From 3c09085f371a68f09147abb59b35db928fe3950f Mon Sep 17 00:00:00 2001
+From: JJ Ding <jj_ding at emc.com.tw>
+Date: Tue, 20 Sep 2011 22:42:51 -0700
+Subject: Input: elantech - better support all those v2 variants
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+V2 hardware has many variants. This patch adddresses two issues:
+
+ - some model also has debounce packets, but with a different signature
+   than v3. Now we just check debounce for all v2 hardware.
+
+ - due to different scanning methods the hardware uses, x and y ranges have
+   to be calculated differently. And for some specific versions, we can just
+   see them as custom-made, so set {x, y} the same values as Windows driver
+   does.
+
+Signed-off-by: JJ Ding <jj_ding at emc.com.tw>
+Tested-by: Richard Schütz <r.schtz at t-online.de>
+Reviewed-by: Éric Piel <eric.piel at tremplin-utc.net>
+Signed-off-by: Dmitry Torokhov <dtor at mail.ru>
+---
+ drivers/input/mouse/elantech.c |   46 +++++++++++++++++++++++++++++++++++----
+ drivers/input/mouse/elantech.h |    1 +
+ 2 files changed, 42 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
+index b8733b3..c2d91eb 100644
+--- a/drivers/input/mouse/elantech.c
++++ b/drivers/input/mouse/elantech.c
+@@ -613,6 +613,18 @@ static int elantech_packet_check_v1(struct psmouse *psmouse)
+ 	       etd->parity[packet[3]] == p3;
+ }
+ 
++static int elantech_debounce_check_v2(struct psmouse *psmouse)
++{
++        /*
++         * When we encounter packet that matches this exactly, it means the
++         * hardware is in debounce status. Just ignore the whole packet.
++         */
++        const u8 debounce_packet[] = { 0x84, 0xff, 0xff, 0x02, 0xff, 0xff };
++        unsigned char *packet = psmouse->packet;
++
++        return !memcmp(packet, debounce_packet, sizeof(debounce_packet));
++}
++
+ static int elantech_packet_check_v2(struct psmouse *psmouse)
+ {
+ 	struct elantech_data *etd = psmouse->private;
+@@ -708,6 +720,10 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
+ 		break;
+ 
+ 	case 2:
++		/* ignore debounce */
++		if (elantech_debounce_check_v2(psmouse))
++			return PSMOUSE_FULL_PACKET;
++
+ 		if (etd->paritycheck && !elantech_packet_check_v2(psmouse))
+ 			return PSMOUSE_BAD_DATA;
+ 
+@@ -825,7 +841,6 @@ static int elantech_set_range(struct psmouse *psmouse,
+ 	struct elantech_data *etd = psmouse->private;
+ 	unsigned char param[3];
+ 	unsigned char traces;
+-	int i;
+ 
+ 	switch (etd->hw_version) {
+ 	case 1:
+@@ -844,12 +859,33 @@ static int elantech_set_range(struct psmouse *psmouse,
+ 			*x_max = ETP_XMAX_V2;
+ 			*y_max = ETP_YMAX_V2;
+ 		} else {
++			int i;
++			int fixed_dpi;
++
+ 			i = (etd->fw_version > 0x020800 &&
+ 			     etd->fw_version < 0x020900) ? 1 : 2;
+-			*x_min = 0;
+-			*y_min = 0;
+-			*x_max = (etd->capabilities[1] - i) * 64;
+-			*y_max = (etd->capabilities[2] - i) * 64;
++
++			if (synaptics_send_cmd(psmouse, ETP_FW_ID_QUERY, param))
++				return -1;
++
++			fixed_dpi = param[1] & 0x10;
++
++			if (((etd->fw_version >> 16) == 0x14) && fixed_dpi) {
++				if (synaptics_send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
++					return -1;
++
++				*x_max = (etd->capabilities[1] - i) * param[1] / 2;
++				*y_max = (etd->capabilities[2] - i) * param[2] / 2;
++			} else if (etd->fw_version == 0x040216) {
++				*x_max = 819;
++				*y_max = 405;
++			} else if (etd->fw_version == 0x040219 || etd->fw_version == 0x040215) {
++				*x_max = 900;
++				*y_max = 500;
++			} else {
++				*x_max = (etd->capabilities[1] - i) * 64;
++				*y_max = (etd->capabilities[2] - i) * 64;
++			}
+ 		}
+ 		break;
+ 
+diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
+index 7ecaef0..9e5f1aa 100644
+--- a/drivers/input/mouse/elantech.h
++++ b/drivers/input/mouse/elantech.h
+@@ -19,6 +19,7 @@
+ #define ETP_FW_ID_QUERY			0x00
+ #define ETP_FW_VERSION_QUERY		0x01
+ #define ETP_CAPABILITIES_QUERY		0x02
++#define ETP_SAMPLE_QUERY		0x03
+ 
+ /*
+  * Command values for register reading or writing
+-- 
+1.7.6.4
+
+
+From 6c0ec284648f0346e96b0079999cb7af055f58ab Mon Sep 17 00:00:00 2001
+From: JJ Ding <jj_ding at emc.com.tw>
+Date: Tue, 20 Sep 2011 22:42:51 -0700
+Subject: Input: elantech - remove module parameter force_elantech
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This essentially reverts commit f81bc788ff91d4efd4baf88b2c29713838caa8e5.
+
+With recent work on elantech driver, I believe we now have complete support
+for all elantech touchpads. So remove this hack.
+
+Signed-off-by: JJ Ding <jj_ding at emc.com.tw>
+Reviewed-by: Éric Piel <eric.piel at tremplin-utc.net>
+Signed-off-by: Dmitry Torokhov <dtor at mail.ru>
+---
+ drivers/input/mouse/elantech.c |   12 ++----------
+ 1 files changed, 2 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
+index c2d91eb..25290b3 100644
+--- a/drivers/input/mouse/elantech.c
++++ b/drivers/input/mouse/elantech.c
+@@ -28,10 +28,6 @@
+ 			printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__);	\
+ 	} while (0)
+ 
+-static bool force_elantech;
+-module_param_named(force_elantech, force_elantech, bool, 0644);
+-MODULE_PARM_DESC(force_elantech, "Force the Elantech PS/2 protocol extension to be used, 1 = enabled, 0 = disabled (default).");
+-
+ /*
+  * Send a Synaptics style sliced query command
+  */
+@@ -1164,12 +1160,8 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties)
+ 		 param[0], param[1], param[2]);
+ 
+ 	if (!elantech_is_signature_valid(param)) {
+-		if (!force_elantech) {
+-			pr_debug("Probably not a real Elantech touchpad. Aborting.\n");
+-			return -1;
+-		}
+-
+-		pr_debug("Probably not a real Elantech touchpad. Enabling anyway due to force_elantech.\n");
++		pr_debug("Probably not a real Elantech touchpad. Aborting.\n");
++		return -1;
+ 	}
+ 
+ 	if (set_properties) {
+-- 
+1.7.6.4
+
diff --git a/kernel.spec b/kernel.spec
index 87fc89a..3b1cd23 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -42,7 +42,7 @@ Summary: The Linux kernel
 # When changing real_sublevel below, reset this by hand to 1
 # (or to 0 and then use rpmdev-bumpspec).
 #
-%global baserelease 5
+%global baserelease 6
 %global fedora_build %{baserelease}
 
 # real_sublevel is the 3.x kernel version we're starting with
@@ -722,6 +722,9 @@ Patch21070: oom-fix-integer-overflow-of-points.patch
 #rhbz 706574
 Patch21071: WMI-properly-cleanup-devices-to-avoid-crashes.patch
 
+#rhbz 728607
+Patch21060: elantech.patch
+
 %endif
 
 BuildRoot: %{_tmppath}/kernel-%{KVERREL}-root
@@ -1310,6 +1313,9 @@ ApplyPatch oom-fix-integer-overflow-of-points.patch
 #rhbz 706574
 ApplyPatch WMI-properly-cleanup-devices-to-avoid-crashes.patch
 
+#rhbz 728607
+ApplyPatch elantech.patch
+
 # END OF PATCH APPLICATIONS
 
 %endif
@@ -1930,6 +1936,9 @@ fi
 # and build.
 
 %changelog
+* Thu Nov 03 2011 Josh Boyer <jwboyer at redhat.com>
+- Add patches queued for 3.2 for elantech driver (rhbz 728607)
+
 * Wed Nov 02 2011 Josh Boyer <jwboyer at redhat.com>
 - Add patch to fix oops when removing wmi module (rhbz 706574)
 


More information about the scm-commits mailing list