diff mbox

[0/3] Input: synaptics - multitouch and multifinger support

Message ID s5hfwwg4p4t.wl%tiwai@suse.de
State New, archived
Headers show

Commit Message

Takashi Iwai Oct. 8, 2010, 4:37 p.m. UTC
None
diff mbox

Patch

diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 91d3517..6dd0d29 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -76,6 +76,17 @@  config MOUSE_PS2_SYNAPTICS_LED
 	  Say Y here if you have a Synaptics device with an embedded LED.
 	  This will enable LED class driver to control the LED device.
 
+config MOUSE_PS2_SYNAPTICS_MULTI_TOUCH
+	bool "Support multi-touch protocol of Synaptics devices"
+	depends on MOUSE_PS2_SYNAPTICS
+	help
+	  Say Y here for enabling the multi-touch protocol of recent
+	  Syanptics devices.  This may result in incompatible input
+	  events, thus make sure that you update X11 synaptics driver
+	  beforehand with the multi-protocol touch.
+
+	  If unsure, say N.
+
 config MOUSE_PS2_LIFEBOOK
 	bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EMBEDDED
 	default y
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 00799bc..79d9463 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -181,6 +181,12 @@  static int synaptics_capability(struct psmouse *psmouse)
 		}
 	}
 
+#ifdef CONFIG_MOUSE_PS2_SYNAPTICS_MULTI_TOUCH
+	/* FIXME: is this the right guess? */
+	if (priv->ext_cap_0c & (0x08 << 16))
+		priv->can_multi_touch = 1;
+#endif
+
 	return 0;
 }
 
@@ -458,6 +464,32 @@  static void synaptics_free_led(struct psmouse *psmouse)
 #define synaptics_sync_led(ps)	do {} while (0)
 #endif
 
+/* change to the multi-touch mode;
+ * this is done by sending SYN_QUE_MODEL cmd but setting a parameter
+ * by SETRATE instead of querying via GETINFO.
+ * 0xc8 seems to be the multi-touch mode.
+ */
+static int synaptics_init_multi_touch(struct psmouse *psmouse)
+{
+	unsigned char param[1];
+
+	if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
+		return -1;
+	param[0] = 0xc8;
+	if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE))
+		return -1;
+	return 0;
+}
+
+#ifdef CONFIG_MOUSE_PS2_SYNAPTICS_MULTI_TOUCH
+#define is_multi_touch(priv)	(priv)->can_multi_touch
+#else
+#define is_multi_touch(priv)	0
+#endif
+/* the multi-touch packet contains w=2 (like pen) */
+#define is_multi_touch_packet(priv, hw) \
+	(is_multi_touch(priv) && (hw)->w == 2)
+
 /*****************************************************************************
  *	Functions to interpret the absolute mode packets
  ****************************************************************************/
@@ -467,17 +499,27 @@  static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data
 	memset(hw, 0, sizeof(struct synaptics_hw_state));
 
 	if (SYN_MODEL_NEWABS(priv->model_id)) {
-		hw->x = (((buf[3] & 0x10) << 8) |
-			 ((buf[1] & 0x0f) << 8) |
-			 buf[4]);
-		hw->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));
+		if (is_multi_touch_packet(priv, hw)) {
+			/* a multi-touch packet is encoded differently;
+			 * it appears to have half-resolutions.  I might
+			 * have missed the lowest bits, but it's hard
+			 * to recognize.
+			 */
+			hw->x = ((buf[4] & 0x0f) << 8 | buf[1]) << 1;
+			hw->y = ((buf[4] & 0xf0) << 4 | buf[2]) << 1;
+			hw->z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
+		} else {
+			hw->x = (((buf[3] & 0x10) << 8) |
+				 ((buf[1] & 0x0f) << 8) |
+				 buf[4]);
+			hw->y = (((buf[3] & 0x20) << 7) |
+				 ((buf[1] & 0xf0) << 4) |
+				 buf[5]);
+			hw->z = buf[2];
+		}
 
 		hw->left  = (buf[0] & 0x01) ? 1 : 0;
 		hw->right = (buf[0] & 0x02) ? 1 : 0;
@@ -492,7 +534,7 @@  static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data
 
 		} else if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
 			hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
-			if (hw->w == 2)
+			if (!is_multi_touch(priv) && hw->w == 2)
 				hw->scroll = (signed char)(buf[1]);
 		}
 
@@ -550,6 +592,18 @@  static void synaptics_process_packet(struct psmouse *psmouse)
 
 	synaptics_parse_hw_state(psmouse->packet, priv, &hw);
 
+	if (is_multi_touch_packet(priv, &hw)) {
+		/* multi-touching */
+		if (hw.z > 0) {
+			input_report_abs(dev, ABS_MT_POSITION_X, hw.x);
+			input_report_abs(dev, ABS_MT_POSITION_Y,
+					 YMAX_NOMINAL + YMIN_NOMINAL - hw.y);
+		}
+		input_report_abs(dev, ABS_MT_PRESSURE, hw.z);
+		input_mt_sync(dev);
+		return;
+	}
+
 	if (hw.scroll) {
 		priv->scroll += hw.scroll;
 
@@ -576,7 +630,8 @@  static void synaptics_process_packet(struct psmouse *psmouse)
 		if (SYN_CAP_EXTENDED(priv->capabilities)) {
 			switch (hw.w) {
 			case 0 ... 1:
-				if (SYN_CAP_MULTIFINGER(priv->capabilities))
+				if (SYN_CAP_MULTIFINGER(priv->capabilities) ||
+				    is_multi_touch(priv))
 					num_fingers = hw.w + 2;
 				break;
 			case 2:
@@ -602,10 +657,15 @@  static void synaptics_process_packet(struct psmouse *psmouse)
 	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);
+		int key;
+		key = is_multi_touch(priv) ? ABS_MT_POSITION_X : ABS_X;
+		input_report_abs(dev, key, hw.x);
+		key = is_multi_touch(priv) ? ABS_MT_POSITION_Y : ABS_Y;
+		input_report_abs(dev, key, YMAX_NOMINAL + YMIN_NOMINAL - hw.y);
 	}
-	input_report_abs(dev, ABS_PRESSURE, hw.z);
+	input_report_abs(dev,
+			 is_multi_touch(priv) ? ABS_MT_PRESSURE : ABS_PRESSURE,
+			 hw.z);
 
 	if (SYN_CAP_PALMDETECT(priv->capabilities))
 		input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
@@ -614,7 +674,7 @@  static void synaptics_process_packet(struct psmouse *psmouse)
 	input_report_key(dev, BTN_LEFT, hw.left);
 	input_report_key(dev, BTN_RIGHT, hw.right);
 
-	if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
+	if (SYN_CAP_MULTIFINGER(priv->capabilities) || is_multi_touch(priv)) {
 		input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
 		input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
 	}
@@ -630,6 +690,8 @@  static void synaptics_process_packet(struct psmouse *psmouse)
 	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));
 
+	if (is_multi_touch(priv))
+		input_mt_sync(dev);
 	input_sync(dev);
 }
 
@@ -719,7 +781,7 @@  static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
 	__set_bit(BTN_LEFT, dev->keybit);
 	__set_bit(BTN_RIGHT, dev->keybit);
 
-	if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
+	if (SYN_CAP_MULTIFINGER(priv->capabilities) || is_multi_touch(priv)) {
 		__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
 		__set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
 	}
@@ -748,6 +810,16 @@  static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
 		__clear_bit(BTN_RIGHT, dev->keybit);
 		__clear_bit(BTN_MIDDLE, dev->keybit);
 	}
+
+	if (is_multi_touch(priv)) {
+		input_set_abs_params(dev, ABS_MT_POSITION_X,
+				     XMIN_NOMINAL, XMAX_NOMINAL, 0, 0);
+		input_set_abs_params(dev, ABS_MT_POSITION_Y,
+				     YMIN_NOMINAL, YMAX_NOMINAL, 0, 0);
+		input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
+		input_abs_set_res(dev, ABS_MT_POSITION_X, priv->x_res);
+		input_abs_set_res(dev, ABS_MT_POSITION_Y, priv->y_res);
+	}
 }
 
 static void synaptics_disconnect(struct psmouse *psmouse)
@@ -785,6 +857,8 @@  static int synaptics_reconnect(struct psmouse *psmouse)
 	}
 
 	synaptics_sync_led(psmouse);
+	if (is_multi_touch(priv))
+		synaptics_init_multi_touch(psmouse);
 
 	return 0;
 }
@@ -863,6 +937,15 @@  int synaptics_init(struct psmouse *psmouse)
 	if (synaptics_init_led(psmouse) < 0)
 		goto init_fail;
 
+	if (priv->can_multi_touch) {
+		if (synaptics_init_multi_touch(psmouse)) {
+			printk(KERN_WARNING "Synaptics: "
+			       "unable to initialize multi-touch\n");
+			priv->can_multi_touch = 0;
+		} else
+			printk(KERN_INFO "Synaptics: multi-touch enabled\n");
+	}
+
 	set_input_params(psmouse->dev, priv);
 
 	/*
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index e1a9033..b586087 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -111,6 +111,7 @@  struct synaptics_data {
 
 	unsigned char pkt_type;			/* packet type - old, new, etc */
 	unsigned char mode;			/* current mode byte */
+	unsigned char can_multi_touch;		/* multi-touch support */
 	int scroll;
 	struct synaptics_led *led;
 };