diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 7047558..34df70f 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -41,6 +41,15 @@
 #define YMIN_NOMINAL 1408
 #define YMAX_NOMINAL 4448
 
+/*
+ * Left and right ClickPad button ranges; the gap between them is reserved
+ * for middle button.
+ */
+#define CLICKPAD_LEFT_BTN_X \
+	((XMAX_NOMINAL - XMIN_NOMINAL) * 2 / 5 + XMIN_NOMINAL)
+#define CLICKPAD_RIGHT_BTN_X \
+	((XMAX_NOMINAL - XMIN_NOMINAL) * 3 / 5 + XMIN_NOMINAL)
+
 
 /*****************************************************************************
  *	Stuff we need even when we do not want native Synaptics support
@@ -327,23 +336,56 @@ static void synaptics_pt_create(struct psmouse *psmouse)
  *	Functions to interpret the absolute mode packets
  ****************************************************************************/
 static void synaptics_parse_new_hw(unsigned char buf[],
-				   struct synaptics_data *priv,
-				   struct synaptics_hw_state *hw)
+				   struct synaptics_data *priv)
 {
-	hw->x = ((buf[3] & 0x10) << 8) | ((buf[1] & 0x0f) << 8) | buf[4];
-	hw->y = ((buf[3] & 0x20) << 7) | ((buf[1] & 0xf0) << 4) | buf[5];
+	struct synaptics_hw_state *hw = &priv->hw;
+	int x = ((buf[3] & 0x10) << 8) | ((buf[1] & 0x0f) << 8) | buf[4];
+	int y = ((buf[3] & 0x20) << 7) | ((buf[1] & 0xf0) << 4) | buf[5];
 
 	hw->z = buf[2];
 	hw->w = ((buf[0] & 0x30) >> 2) |
 		((buf[0] & 0x04) >> 1) |
 		((buf[3] & 0x04) >> 2);
 
-	hw->left  = buf[0] & 0x01;
-	hw->right = buf[0] & 0x02;
+	if (SYN_CAP_CLICKPAD(priv->ext_cap)) {
+		int click = (buf[0] ^ buf[3]) & 0x01;
 
-	if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
-		hw->middle = (buf[0] ^ buf[3]) & 0x01;
-		hw->scroll = hw->w == 2 ? (signed char)buf[1] : 0;
+		if (click && y < YMIN_NOMINAL) {
+			/*
+			 * User pressed in ClickZone; report new button
+			 * state but use :w
+			 * old coordinates and don't report
+			 * any pressure to prevent pointer movement.
+			 */
+			hw->left = x < CLICKPAD_LEFT_BTN_X;
+			hw->right = x > CLICKPAD_RIGHT_BTN_X;
+			hw->middle = x >= CLICKPAD_LEFT_BTN_X &&
+				     x <= CLICKPAD_RIGHT_BTN_X;
+			hw->z = 0;
+
+		} else {
+			/*
+			 * Finger is outside of the ClickZone - report
+			 * current coordinates.
+			 */
+			hw->x = x;
+			hw->y = y;
+
+			if (!click)
+				hw->left = hw->right = hw->middle = 0;
+		}
+
+	} else {
+		hw->x = x;
+		hw->y = y;
+
+		hw->left  = buf[0] & 0x01;
+		hw->right = buf[0] & 0x02;
+
+		if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
+			hw->middle = (buf[0] ^ buf[3]) & 0x01;
+			hw->scroll = hw->w == 2 ? (signed char)buf[1] : 0;
+		}
 	}
 
 	if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
@@ -379,9 +421,10 @@ static void synaptics_parse_new_hw(unsigned char buf[],
 }
 
 static void synaptics_parse_old_hw(unsigned char buf[],
-				   struct synaptics_data *priv,
-				   struct synaptics_hw_state *hw)
+				   struct synaptics_data *priv)
 {
+	struct synaptics_hw_state *hw = &priv->hw;
+
 	hw->x = ((buf[1] & 0x1f) << 8) | buf[2];
 	hw->y = ((buf[4] & 0x1f) << 8) | buf[5];
 
@@ -399,44 +442,44 @@ static void synaptics_process_packet(struct psmouse *psmouse)
 {
 	struct input_dev *dev = psmouse->dev;
 	struct synaptics_data *priv = psmouse->private;
-	struct synaptics_hw_state hw;
+	struct synaptics_hw_state *hw = &priv->hw;
 	int num_fingers;
 	int finger_width;
 	int i;
 
 	if (SYN_MODEL_NEWABS(priv->model_id))
-		synaptics_parse_new_hw(psmouse->packet, priv, &hw);
+		synaptics_parse_new_hw(psmouse->packet, priv);
 	else
-		synaptics_parse_old_hw(psmouse->packet, priv, &hw);
+		synaptics_parse_old_hw(psmouse->packet, priv);
 
-	if (hw.scroll) {
-		priv->scroll += hw.scroll;
+	if (hw->scroll) {
+		priv->scroll += hw->scroll;
 
 		while (priv->scroll >= 4) {
-			input_report_key(dev, BTN_BACK, !hw.down);
+			input_report_key(dev, BTN_BACK, !hw->down);
 			input_sync(dev);
-			input_report_key(dev, BTN_BACK, hw.down);
+			input_report_key(dev, BTN_BACK, hw->down);
 			input_sync(dev);
 			priv->scroll -= 4;
 		}
 		while (priv->scroll <= -4) {
-			input_report_key(dev, BTN_FORWARD, !hw.up);
+			input_report_key(dev, BTN_FORWARD, !hw->up);
 			input_sync(dev);
-			input_report_key(dev, BTN_FORWARD, hw.up);
+			input_report_key(dev, BTN_FORWARD, hw->up);
 			input_sync(dev);
 			priv->scroll += 4;
 		}
 		return;
 	}
 
-	if (hw.z > 0) {
+	if (hw->z > 0) {
 		num_fingers = 1;
 		finger_width = 5;
 		if (SYN_CAP_EXTENDED(priv->capabilities)) {
-			switch (hw.w) {
+			switch (hw->w) {
 			case 0 ... 1:
 				if (SYN_CAP_MULTIFINGER(priv->capabilities))
-					num_fingers = hw.w + 2;
+					num_fingers = hw->w + 2;
 				break;
 			case 2:
 				if (SYN_MODEL_PEN(priv->model_id))
@@ -444,7 +487,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
 				break;
 			case 4 ... 15:
 				if (SYN_CAP_PALMDETECT(priv->capabilities))
-					finger_width = hw.w;
+					finger_width = hw->w;
 				break;
 			}
 		}
@@ -457,35 +500,37 @@ static void synaptics_process_packet(struct psmouse *psmouse)
 	 * BTN_TOUCH has to be first as mousedev relies on it when doing
 	 * absolute -> relative conversion
 	 */
-	if (hw.z > 30) input_report_key(dev, BTN_TOUCH, 1);
-	if (hw.z < 25) input_report_key(dev, BTN_TOUCH, 0);
+	if (hw->z > 30) input_report_key(dev, BTN_TOUCH, 1);
+	if (hw->z < 25) input_report_key(dev, BTN_TOUCH, 0);
 
-	if (hw.z > 0) {
-		input_report_abs(dev, ABS_X, hw.x);
-		input_report_abs(dev, ABS_Y, YMAX_NOMINAL + YMIN_NOMINAL - hw.y);
+	if (hw->z > 0) {
+		input_report_abs(dev, ABS_X, hw->x);
+		input_report_abs(dev, ABS_Y, YMAX_NOMINAL + YMIN_NOMINAL - hw->y);
 	}
-	input_report_abs(dev, ABS_PRESSURE, hw.z);
+	input_report_abs(dev, ABS_PRESSURE, hw->z);
 
 	input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
 	input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
-	input_report_key(dev, BTN_LEFT, hw.left);
-	input_report_key(dev, BTN_RIGHT, hw.right);
+	input_report_key(dev, BTN_LEFT, hw->left);
+	input_report_key(dev, BTN_RIGHT, hw->right);
 
 	if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
 		input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
 		input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
 	}
 
-	if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
-		input_report_key(dev, BTN_MIDDLE, hw.middle);
+	if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities) ||
+	    SYN_CAP_CLICKPAD(priv->ext_cap)) {
+		input_report_key(dev, BTN_MIDDLE, hw->middle);
+	}
 
 	if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
-		input_report_key(dev, BTN_FORWARD, hw.up);
-		input_report_key(dev, BTN_BACK, hw.down);
+		input_report_key(dev, BTN_FORWARD, hw->up);
+		input_report_key(dev, BTN_BACK, hw->down);
 	}
 
 	for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
-		input_report_key(dev, BTN_0 + i, hw.ext_buttons & (1 << i));
+		input_report_key(dev, BTN_0 + i, hw->ext_buttons & (1 << i));
 
 	input_sync(dev);
 }
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index 838e7f2..e00c53d 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -48,6 +48,8 @@
 #define SYN_CAP_VALID(c)		((((c) & 0x00ff00) >> 8) == 0x47)
 #define SYN_EXT_CAP_REQUESTS(c)		(((c) & 0x700000) >> 20)
 #define SYN_CAP_MULTI_BUTTON_NO(ec)	(((ec) & 0x00f000) >> 12)
+#define SYN_CAP_PRODUCT_ID(ec)		(((ec) & 0xff0000) >> 16)
+#define SYN_CAP_CLICKPAD(ec)		(SYN_CAP_PRODUCT_ID(ec) == 0xe4)
 
 /* synaptics modes query bits */
 #define SYN_MODE_ABSOLUTE(m)		((m) & (1 << 7))
@@ -100,9 +102,10 @@ struct synaptics_data {
 	int x_res;				/* X resolution in units/mm */
 	int y_res;				/* Y resolution in units/mm */
 
+	struct synaptics_hw_state hw;
+	int scroll;
 	unsigned char pkt_type;			/* packet type - old, new, etc */
 	unsigned char mode;			/* current mode byte */
-	int scroll;
 };
 
 void synaptics_module_init(void);
