diff --git a/Documentation/input/elantech.txt b/Documentation/input/elantech.txt
new file mode 100644
index 0000000..a9a81a7
--- /dev/null
+++ b/Documentation/input/elantech.txt
@@ -0,0 +1,203 @@
+Elantech Touchpad Driver
+========================
+
+        Copyright (C) 2007-2008 Arjan Opmeer <arjan@opmeer.net>
+
+        Extra information found and provided by Steve Havelka
+
+
+Introduction
+~~~~~~~~~~~~
+
+Like most touchpads the Elantech touchpad can be in two modes. One is called
+relative mode in which the touchpad uses its internal controller to
+translate touching the pad and moving your finger into mouse movement
+updates. In this mode the touchpad acts just like a normal wired mouse.
+
+The other is called absolute mode in which the touchpad merely acts as an XY
+grid and as long as there is a finger on the pad continuously sends the
+coordinates of the finger to the driver. Think of it like a tablet where
+your finger is the stylus.
+
+The Linux Elantech touchpad driver always puts the touchpad into absolute
+mode to be compatible with the Xorg Synaptics touchpad driver.
+
+The operation of the touchpad can be altered by adjusting the contents of
+some of its internal registers. These registers are represented by the
+driver as sysfs entries under /sys/bus/serio/drivers/psmouse/serio? that can
+be read from and written to.
+
+However some register settings have effect only when the touchpad is in
+relative mode and not in absolute mode. In absence of any Elantech
+documentation they are mentioned here for completeness sake.
+
+
+Registers
+~~~~~~~~~
+
+* reg_10
+
+   bit   7   6   5   4   3   2   1   0
+         B   C   T   D   L   A   S   E
+
+         E: 1 = enable smart edges unconditionally
+         S: 1 = enable smart edges only when dragging
+         A: 1 = absolute mode (needs 4 byte packets, see reg_11)
+         L: 1 = enable drag lock (see reg_22)
+         D: 1 = disable dynamic resolution
+         T: 1 = disable tapping
+         C: 1 = enable corner tap
+         B: 1 = swap left and right button
+
+* reg_11
+
+   bit   7   6   5   4   3   2   1   0
+         1   0   0   H   V   1   F   P
+
+         P: 1 = enable parity checking for relative mode
+         F: 1 = enable native 4 byte packet mode
+         V: 1 = enable vertical scroll area
+         H: 1 = enable horizontal scroll area
+
+* reg_20
+
+         single finger width?
+
+* reg_21
+
+         scroll area width (small: 0x40 ... wide: 0xff)
+
+* reg_22
+
+         drag lock time out (short: 0x14 ... long: 0xfe; 0xff =never)
+
+* reg_23
+
+         tap make timeout?
+
+* reg_24
+
+         tap release timeout?
+
+* reg_25
+
+         smart edge cursor speed (0x02 = slow, 0x03 = medium, 0x04 = fast)
+
+* reg_26
+
+         smart edge activation area width?
+
+
+
+Initially the Elantech touchpad is in emulation mode and reports 3 byte
+standard PS/2 packets and hence works with a standard mouse driver.
+However, it can be configured to talk its native 4 byte relative mode and 4
+byte absolute mode both for which a dedicated driver is needed.
+
+
+Native 4 byte relative mode packet format
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+byte 0:
+   bit   7   6   5   4   3   2   1   0
+         c   c  p2  p1   1   M   R   L
+
+         L, R, M = 1 when Left, Right, Middle mouse button pressed
+            some models have M as byte 3 odd parity bit
+         when parity checking is enabled (reg_11, P = 1):
+            p1..p2 = byte 1 and 2 odd parity bit
+         c = 1 when corner tap detected
+
+byte 1:
+   bit   7   6   5   4   3   2   1   0
+        dx7 dx6 dx5 dx4 dx3 dx2 dx1 dx0
+
+         dx7..dx0 = x movement;   positive = right, negative = left
+         byte 1 = 0xf0 when corner tap detected
+
+byte 2:
+   bit   7   6   5   4   3   2   1   0
+        dy7 dy6 dy5 dy4 dy3 dy2 dy1 dy0
+
+         dy7..dy0 = y movement;   positive = up,    negative = down
+
+byte 3:
+   parity checking enabled (reg_11, P = 1):
+
+      bit   7   6   5   4   3   2   1   0
+            w   h  n1  n0  ds3 ds2 ds1 ds0
+
+            normally:
+               ds3..ds0 = scroll wheel amount and direction
+                          positive = down or left
+                          negative = up or right
+            when corner tap detected:
+               ds0 = 1 when top right corner tapped
+               ds1 = 1 when bottom right corner tapped
+               ds2 = 1 when bottom left corner tapped
+               ds3 = 1 when top left corner tapped
+            n1..n0 = number of fingers on touchpad
+               only models with firmware 2.x report this, models with
+               firmware 1.x seem to map one, two and three finger taps
+               directly to L, M and R mouse buttons
+            h = 1 when horizontal scroll action
+            w = 1 when wide finger touch?
+
+   otherwise (reg_11, P = 0):
+
+      bit   7   6   5   4   3   2   1   0
+           ds7 ds6 ds5 ds4 ds3 ds2 ds1 ds0
+
+            ds7..ds0 = vertical scroll amount and direction
+                       negative = up
+                       positive = down
+
+
+Native 4 byte absolute mode packet format
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+byte 0:
+   firmware version 1.x:
+
+      bit   7   6   5   4   3   2   1   0
+            D   U  p1  p2   1  p3   R   L
+
+            L, R = 1 when Left, Right mouse button pressed
+            p1..p3 = byte 1..3 odd parity bit
+            D, U = 1 when rocker switch pressed Up, Down
+
+   firmware version 2.x:
+
+      bit   7   6   5   4   3   2   1   0
+           n1  n0  p2  p1   1  p3   R   L
+
+            L, R = 1 when Left, Right mouse button pressed
+            p1..p3 = byte 1..3 odd parity bit
+            n1..n0 = number of fingers on touchpad
+
+byte 1:
+   firmware version 1.x:
+
+      bit   7   6   5   4   3   2   1   0
+            f   0  th  tw  x9  x8  y9  y8
+
+            tw = 1 when two finger touch
+            th = 1 when three finger touch
+            f  = 1 when finger touch
+
+   firmware version 2.x:
+
+      bit   7   6   5   4   3   2   1   0
+            0   0   0   0  x9  x8  y9  y8
+
+byte 2:
+   bit   7   6   5   4   3   2   1   0
+        x7  x6  x5  x4  x3  x2  x1  x0
+
+         x9..x0 = absolute x value (horizontal)
+
+byte 3:
+   bit   7   6   5   4   3   2   1   0
+        y7  y6  y5  y4  y3  y2  y1  y0
+
+         y9..y0 = absolute y value (vertical)
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 7bbea09..32fe268 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -96,6 +96,21 @@ config MOUSE_PS2_TOUCHKIT
 
 	  If unsure, say N.
 
+config MOUSE_PS2_ELANTECH
+	bool "Elantech PS/2 protocol extension"
+	depends on MOUSE_PS2
+	help
+	  Say Y here if you have an Elantech PS/2 touchpad connected
+	  to your system.
+
+	  If unsure, say N.
+
+	  This driver exposes some configuration registers via sysfs
+	  entries.
+
+	  For further information, see
+	  <file:Documentation/input/elantech.txt>.
+
 config MOUSE_SERIAL
 	tristate "Serial mouse"
 	select SERIO
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index 9e6e363..951d77c 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -24,3 +24,4 @@ psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP)	+= logips2pp.o
 psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK)	+= lifebook.o
 psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT)	+= trackpoint.o
 psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT)	+= touchkit_ps2.o
+psmouse-$(CONFIG_MOUSE_PS2_ELANTECH)	+= elantech.o
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
new file mode 100644
index 0000000..5e45f24
--- /dev/null
+++ b/drivers/input/mouse/elantech.c
@@ -0,0 +1,410 @@
+/*
+ * Elantech Touchpad driver (v4)
+ *
+ * Copyright (C) 2007-2008 Arjan Opmeer <arjan@opmeer.net>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include "psmouse.h"
+#include "elantech.h"
+
+/*
+ * Send a Synaptics style special command
+ */
+static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c,
+				unsigned char *param)
+{
+	if (psmouse_sliced_command(psmouse, c))
+		return -1;
+	if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
+		return -1;
+
+	return 0;
+}
+
+/*
+ * Send an Elantech style special command to write a register with a value
+ */
+static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
+				unsigned char val)
+{
+	struct ps2dev	*ps2dev = &psmouse->ps2dev;
+
+	if ((reg < 0x10) || (reg > 0x26))
+		return -1;
+	if ((reg > 0x11) && (reg < 0x20))
+		return -1;
+
+	if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) ||
+	    psmouse_sliced_command(psmouse, reg) ||
+	    psmouse_sliced_command(psmouse, val) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) {
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Dump a complete mouse movement packet to the syslog
+ */
+static void elantech_packet_dump(unsigned char *packet, int size)
+{
+	int	i;
+
+	printk(KERN_DEBUG "elantech.c: PS/2 packet [");
+	for (i = 0; i < size; i++)
+		printk("%s0x%02x ", (i) ? ", " : " ", packet[i]);
+	printk("]\n");
+}
+
+/*
+ * Interpret complete data packets and report absolute mode input events
+ */
+static void elantech_report_absolute(struct psmouse *psmouse)
+{
+	struct elantech_data	*etd = psmouse->private;
+	struct input_dev 	*dev = psmouse->dev;
+	unsigned char 		*packet = psmouse->packet;
+	int			fingers;
+
+	if (etd->fw_version_maj == 0x01) {
+		/* byte 0:  D   U  p1  p2   1  p3   R   L
+		   byte 1:  f   0  th  tw  x9  x8  y9  y8 */
+		fingers = ((packet[1] & 0x80) >> 7) +
+				((packet[1] & 0x30) >> 4);
+	} else {
+		/* byte 0: n1  n0  p2  p1   1  p3   R   L
+		   byte 1:  0   0   0   0  x9  x8  y9  y8 */
+		fingers = (packet[0] & 0xc0) >> 6;
+	}
+
+	input_report_key(dev, BTN_TOUCH, fingers != 0);
+	/* byte 2: x7  x6  x5  x4  x3  x2  x1  x0
+	   byte 3: y7  y6  y5  y4  y3  y2  y1  y0 */
+	if (fingers) {
+		input_report_abs(dev, ABS_X,
+			((packet[1] & 0x0c) << 6) | packet[2]);
+		input_report_abs(dev, ABS_Y, ETP_YMAX -
+			(((packet[1] & 0x03) << 8) | packet[3]));
+	}
+	input_report_abs(dev, ABS_PRESSURE, (fingers) ? ETP_DEF_PRESSURE : 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_key(dev, BTN_LEFT, packet[0] & 0x01);
+	input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+	if ((etd->fw_version_maj == 0x01) &&
+	    (etd->capabilities & ETP_CAP_HAS_ROCKER)) {
+		/* rocker up */
+		input_report_key(dev, BTN_FORWARD, packet[0] & 0x40);
+		/* rocker down */
+		input_report_key(dev, BTN_BACK, packet[0] & 0x80);
+	}
+}
+
+/*
+ * 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;
+	struct input_dev 	*dev = psmouse->dev;
+	unsigned char 		*packet = psmouse->packet;
+	unsigned char		p1, p2, p3;
+
+	if (psmouse->pktcnt < psmouse->pktsize)
+		return PSMOUSE_GOOD_DATA;
+
+	if (etd->debug)
+		elantech_packet_dump(packet, psmouse->pktsize);
+
+	/* Parity bits are placed differently */
+	if (etd->fw_version_maj == 0x01) {
+		/* byte 0:  D   U  p1  p2   1  p3   R   L */
+		p1 = (packet[0] & 0x20) >> 5;
+		p2 = (packet[0] & 0x10) >> 4;
+	} else {
+		/* byte 0: n1  n0  p2  p1   1  p3   R   L */
+		p1 = (packet[0] & 0x10) >> 4;
+		p2 = (packet[0] & 0x20) >> 5;
+	}
+	p3 = (packet[0] & 0x04) >> 2;
+	if ((etd->parity[packet[1]] != p1) || (etd->parity[packet[2]] != p2) ||
+	    (etd->parity[packet[3]] != p3))
+		return PSMOUSE_BAD_DATA;
+
+	elantech_report_absolute(psmouse);
+
+	input_sync(dev);
+
+	return PSMOUSE_FULL_PACKET;
+}
+
+/*
+ * Put the touchpad into absolute mode
+ */
+static int elantech_set_absolute_mode(struct psmouse *psmouse)
+{
+	struct elantech_data	*etd = psmouse->private;
+
+	etd->reg_10 = 0x16;
+	etd->reg_11 = 0x8f;
+	if (elantech_write_reg(psmouse, 0x10, etd->reg_10) ||
+	    elantech_write_reg(psmouse, 0x11, etd->reg_11)) {
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Set the appropriate event bits for the input subsystem
+ */
+static void elantech_set_input_params(struct psmouse *psmouse)
+{
+	struct elantech_data	*etd = psmouse->private;
+	struct input_dev 	*dev = psmouse->dev;
+
+	set_bit(EV_KEY, dev->evbit);
+	set_bit(EV_ABS, dev->evbit);
+
+	set_bit(BTN_LEFT, dev->keybit);
+	set_bit(BTN_RIGHT, dev->keybit);
+
+	set_bit(BTN_TOUCH, dev->keybit);
+	set_bit(BTN_TOOL_FINGER, dev->keybit);
+	set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+	set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
+
+	/* Rocker button */
+	if ((etd->fw_version_maj == 0x01) &&
+	    (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, ETP_XMAX, 0, 0);
+	input_set_abs_params(dev, ABS_Y, ETP_YMIN, ETP_YMAX, 0, 0);
+	input_set_abs_params(dev, ABS_PRESSURE, 0, ETP_MAX_PRESSURE, 0, 0);
+}
+
+struct elantech_attr_data {
+	size_t		field_offset;
+	unsigned char	reg;
+};
+
+/*
+ * Display a register value by reading a sysfs entry
+ */
+static ssize_t elantech_show_int_attr(struct psmouse *psmouse, void *data,
+					char *buf)
+{
+	struct elantech_data		*etd = psmouse->private;
+	struct elantech_attr_data	*attr = data;
+	unsigned char			*reg = (unsigned char *)
+						etd + attr->field_offset;
+
+	return sprintf(buf, "0x%02x\n", *reg);
+}
+
+/*
+ * Write a register value by writing a sysfs entry
+ */
+static ssize_t elantech_set_int_attr(struct psmouse *psmouse, void *data,
+						const char *buf, size_t count)
+{
+	struct elantech_data		*etd = psmouse->private;
+	struct elantech_attr_data	*attr = data;
+	unsigned char 			*reg = (unsigned char *)
+						etd + attr->field_offset;
+	unsigned long			value;
+	int				err;
+
+	err = strict_strtoul(buf, 16, &value);
+	if (err)
+		return err;
+	if (value > 0xff)
+		return -EINVAL;
+
+	if (attr->reg == 0x10)
+		/* Force absolute mode always on */
+		value |= ETP_R10_ABSOLUTE_MODE;
+	else if (attr->reg == 0x11)
+		/* Force 4 byte mode always on */
+		value |= ETP_R11_4_BYTE_MODE;
+
+	*reg = value;
+	if (attr->reg)
+		elantech_write_reg(psmouse, attr->reg, value);
+
+	return count;
+}
+
+#define ELANTECH_INT_ATTR(_name, _register)				\
+	static struct elantech_attr_data elantech_attr_##_name = {	\
+		.field_offset = offsetof(struct elantech_data, _name),	\
+		.reg = _register,					\
+	};								\
+	PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO,			\
+			    &elantech_attr_##_name,			\
+			    elantech_show_int_attr,			\
+			    elantech_set_int_attr)
+
+ELANTECH_INT_ATTR(reg_10, 0x10);
+ELANTECH_INT_ATTR(reg_11, 0x11);
+ELANTECH_INT_ATTR(reg_20, 0x20);
+ELANTECH_INT_ATTR(reg_21, 0x21);
+ELANTECH_INT_ATTR(reg_22, 0x22);
+ELANTECH_INT_ATTR(reg_23, 0x23);
+ELANTECH_INT_ATTR(reg_24, 0x24);
+ELANTECH_INT_ATTR(reg_25, 0x25);
+ELANTECH_INT_ATTR(reg_26, 0x26);
+ELANTECH_INT_ATTR(debug, 0);
+
+static struct attribute *elantech_attrs[] = {
+	&psmouse_attr_reg_10.dattr.attr,
+	&psmouse_attr_reg_11.dattr.attr,
+	&psmouse_attr_reg_20.dattr.attr,
+	&psmouse_attr_reg_21.dattr.attr,
+	&psmouse_attr_reg_22.dattr.attr,
+	&psmouse_attr_reg_23.dattr.attr,
+	&psmouse_attr_reg_24.dattr.attr,
+	&psmouse_attr_reg_25.dattr.attr,
+	&psmouse_attr_reg_26.dattr.attr,
+	&psmouse_attr_debug.dattr.attr,
+	NULL
+};
+
+static struct attribute_group elantech_attr_group = {
+	.attrs = elantech_attrs,
+};
+
+/*
+ * Use magic knock to detect Elantech touchpad
+ */
+int elantech_detect(struct psmouse *psmouse, int set_properties)
+{
+	struct ps2dev	*ps2dev = &psmouse->ps2dev;
+	unsigned char	param[3];
+
+	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_DISABLE);
+	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
+	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
+	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
+
+	/*
+	 * 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)) {
+		pr_info("elantech.c: unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n",
+			param[0], param[1], param[2]);
+		return -1;
+	}
+
+	if (set_properties) {
+		psmouse->vendor = "Elantech";
+		psmouse->name = "Touchpad";
+	}
+
+	return 0;
+}
+
+/*
+ * Clean up sysfs entries when disconnecting
+ */
+static void elantech_disconnect(struct psmouse *psmouse)
+{
+	sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
+				&elantech_attr_group);
+	kfree(psmouse->private);
+	psmouse->private = NULL;
+}
+
+/*
+ * Put the touchpad back into absolute mode when reconnecting
+ */
+static int elantech_reconnect(struct psmouse *psmouse)
+{
+	if (elantech_detect(psmouse, 0))
+		return -1;
+
+	if (elantech_set_absolute_mode(psmouse)) {
+		pr_err("elantech.c: failed to put touchpad back into absolute mode.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Initialize the touchpad and create sysfs entries
+ */
+int elantech_init(struct psmouse *psmouse)
+{
+	struct elantech_data	*etd;
+	int			i, error;
+	unsigned char		param[3];
+
+	etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL);
+	psmouse->private = etd;
+	if (!etd)
+		return -1;
+
+	etd->parity[0] = 1;
+	for (i = 1; i < 256; i++)
+		etd->parity[i] = (etd->parity[i & (i - 1)] ^ 1);
+
+	/*
+	 * Find out what version hardware this is
+	 */
+	if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param))
+		goto init_fail;
+	pr_info("elantech.c: Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n",
+		param[0], param[1], param[2]);
+	etd->fw_version_maj = param[0];
+	etd->fw_version_min = param[2];
+
+	if (synaptics_send_cmd(psmouse, ETP_CAPABILITIES_QUERY, param))
+		goto init_fail;
+	pr_info("elantech.c: Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n",
+		param[0], param[1], param[2]);
+	etd->capabilities = param[0];
+
+	if (elantech_set_absolute_mode(psmouse)) {
+		pr_err("elantech.c: failed to put touchpad into absolute mode.\n");
+		goto init_fail;
+	}
+	elantech_set_input_params(psmouse);
+
+	error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,
+					&elantech_attr_group);
+	if (error) {
+		pr_err("elantech.c: failed to create sysfs attributes, error: %d.\n",
+			error);
+		goto init_fail;
+	}
+
+	psmouse->protocol_handler = elantech_process_byte;
+	psmouse->disconnect = elantech_disconnect;
+	psmouse->reconnect = elantech_reconnect;
+	psmouse->pktsize = 4;
+
+	return 0;
+
+init_fail:
+	kfree(etd);
+	return -1;
+}
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
new file mode 100644
index 0000000..1c0452d
--- /dev/null
+++ b/drivers/input/mouse/elantech.h
@@ -0,0 +1,88 @@
+/*
+ * Elantech Touchpad driver (v4)
+ *
+ * Copyright (C) 2007-2008 Arjan Opmeer <arjan@opmeer.net>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#ifndef _ELANTECH_H
+#define _ELANTECH_H
+
+/*
+ * Command values for Synaptics style queries
+ */
+#define ETP_FW_VERSION_QUERY		0x01
+#define ETP_CAPABILITIES_QUERY		0x02
+
+/*
+ * Commands start with this value
+ */
+#define ETP_REGISTER_WRITE		0x11
+
+/*
+ * Register bitmasks
+ */
+#define ETP_R10_ABSOLUTE_MODE		0x04
+#define ETP_R11_4_BYTE_MODE 		0x02
+
+/*
+ * Capability bitmasks
+ */
+#define ETP_CAP_HAS_ROCKER		0x04
+
+/*
+ * One hard to find application note states that X axis range is 0 to 576
+ * and Y axis range is 0 to 384.
+ * Edge fuzz might be necessary because of bezel around the touchpad
+ */
+#define ETP_EDGE_FUZZ			32
+
+#define ETP_XMIN 			(  0 + ETP_EDGE_FUZZ)
+#define ETP_XMAX 			(576 - ETP_EDGE_FUZZ)
+#define ETP_YMIN 			(  0 + ETP_EDGE_FUZZ)
+#define ETP_YMAX 			(384 - ETP_EDGE_FUZZ)
+
+/*
+ * It seems the touchpad does not report pressure.
+ * Just choose some values for compatibility with X Synaptics driver
+ */
+#define ETP_MAX_PRESSURE		255
+#define ETP_DEF_PRESSURE		128
+
+struct elantech_data {
+	unsigned char reg_10;
+	unsigned char reg_11;
+	unsigned char reg_20;
+	unsigned char reg_21;
+	unsigned char reg_22;
+	unsigned char reg_23;
+	unsigned char reg_24;
+	unsigned char reg_25;
+	unsigned char reg_26;
+	unsigned char debug;
+	unsigned char capabilities;
+	unsigned char fw_version_maj;
+	unsigned char fw_version_min;
+	unsigned char parity[256];
+};
+
+#ifdef CONFIG_MOUSE_PS2_ELANTECH
+int elantech_detect(struct psmouse *psmouse, int set_properties);
+int elantech_init(struct psmouse *psmouse);
+#else
+static inline int elantech_detect(struct psmouse *psmouse, int set_properties)
+{
+	return -ENOSYS;
+}
+static inline int elantech_init(struct psmouse *psmouse)
+{
+	return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_ELANTECH */
+
+#endif
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index f5a6be1..d144fec 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -28,6 +28,7 @@
 #include "lifebook.h"
 #include "trackpoint.h"
 #include "touchkit_ps2.h"
+#include "elantech.h"
 
 #define DRIVER_DESC	"PS/2 mouse driver"
 
@@ -653,6 +654,13 @@ static int psmouse_extensions(struct psmouse *psmouse,
 	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
 	psmouse_reset(psmouse);
 
+	if (elantech_detect(psmouse, set_properties) == 0) {
+		if (max_proto > PSMOUSE_IMEX) {
+			if (!set_properties || elantech_init(psmouse) == 0)
+				return PSMOUSE_ELANTECH;
+		}
+	}
+
 	if (max_proto >= PSMOUSE_IMEX && im_explorer_detect(psmouse, set_properties) == 0)
 		return PSMOUSE_IMEX;
 
@@ -768,6 +776,15 @@ static const struct psmouse_protocol psmouse_protocols[] = {
 		.alias		= "cortps",
 		.detect		= cortron_detect,
 	},
+#ifdef CONFIG_MOUSE_PS2_ELANTECH
+	{
+		.type		= PSMOUSE_ELANTECH,
+		.name		= "ETPS/2",
+		.alias		= "elantech",
+		.detect		= elantech_detect,
+		.init		= elantech_init,
+	},
+#endif
 	{
 		.type		= PSMOUSE_AUTO,
 		.name		= "auto",
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index 1317bdd..8bdccac 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -89,6 +89,7 @@ enum psmouse_type {
 	PSMOUSE_TRACKPOINT,
 	PSMOUSE_TOUCHKIT_PS2,
 	PSMOUSE_CORTRON,
+	PSMOUSE_ELANTECH,
 	PSMOUSE_AUTO		/* This one should always be last */
 };
 
