Explorar o código

Pull input changes from Henrik Rydberg, including large update to
atmel_mxt_ts driver by Daniel and MT protocol addition for win8 devices.

Conflicts:
drivers/input/touchscreen/atmel_mxt_ts.c

Dmitry Torokhov %!s(int64=13) %!d(string=hai) anos
pai
achega
f053ea8d84

+ 92 - 26
Documentation/input/multi-touch-protocol.txt

@@ -162,26 +162,48 @@ are divided into categories, to allow for partial implementation.  The
 minimum set consists of ABS_MT_POSITION_X and ABS_MT_POSITION_Y, which
 minimum set consists of ABS_MT_POSITION_X and ABS_MT_POSITION_Y, which
 allows for multiple contacts to be tracked.  If the device supports it, the
 allows for multiple contacts to be tracked.  If the device supports it, the
 ABS_MT_TOUCH_MAJOR and ABS_MT_WIDTH_MAJOR may be used to provide the size
 ABS_MT_TOUCH_MAJOR and ABS_MT_WIDTH_MAJOR may be used to provide the size
-of the contact area and approaching contact, respectively.
+of the contact area and approaching tool, respectively.
 
 
 The TOUCH and WIDTH parameters have a geometrical interpretation; imagine
 The TOUCH and WIDTH parameters have a geometrical interpretation; imagine
 looking through a window at someone gently holding a finger against the
 looking through a window at someone gently holding a finger against the
 glass.  You will see two regions, one inner region consisting of the part
 glass.  You will see two regions, one inner region consisting of the part
 of the finger actually touching the glass, and one outer region formed by
 of the finger actually touching the glass, and one outer region formed by
-the perimeter of the finger. The diameter of the inner region is the
-ABS_MT_TOUCH_MAJOR, the diameter of the outer region is
-ABS_MT_WIDTH_MAJOR. Now imagine the person pressing the finger harder
-against the glass. The inner region will increase, and in general, the
-ratio ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR, which is always smaller than
-unity, is related to the contact pressure. For pressure-based devices,
+the perimeter of the finger. The center of the touching region (a) is
+ABS_MT_POSITION_X/Y and the center of the approaching finger (b) is
+ABS_MT_TOOL_X/Y. The touch diameter is ABS_MT_TOUCH_MAJOR and the finger
+diameter is ABS_MT_WIDTH_MAJOR. Now imagine the person pressing the finger
+harder against the glass. The touch region will increase, and in general,
+the ratio ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR, which is always smaller
+than unity, is related to the contact pressure. For pressure-based devices,
 ABS_MT_PRESSURE may be used to provide the pressure on the contact area
 ABS_MT_PRESSURE may be used to provide the pressure on the contact area
 instead. Devices capable of contact hovering can use ABS_MT_DISTANCE to
 instead. Devices capable of contact hovering can use ABS_MT_DISTANCE to
 indicate the distance between the contact and the surface.
 indicate the distance between the contact and the surface.
 
 
-In addition to the MAJOR parameters, the oval shape of the contact can be
-described by adding the MINOR parameters, such that MAJOR and MINOR are the
-major and minor axis of an ellipse. Finally, the orientation of the oval
-shape can be describe with the ORIENTATION parameter.
+
+	  Linux MT                               Win8
+         __________                     _______________________
+        /          \                   |                       |
+       /            \                  |                       |
+      /     ____     \                 |                       |
+     /     /    \     \                |                       |
+     \     \  a  \     \               |       a               |
+      \     \____/      \              |                       |
+       \                 \             |                       |
+        \        b        \            |           b           |
+         \                 \           |                       |
+          \                 \          |                       |
+           \                 \         |                       |
+            \                /         |                       |
+             \              /          |                       |
+              \            /           |                       |
+               \__________/            |_______________________|
+
+
+In addition to the MAJOR parameters, the oval shape of the touch and finger
+regions can be described by adding the MINOR parameters, such that MAJOR
+and MINOR are the major and minor axis of an ellipse. The orientation of
+the touch ellipse can be described with the ORIENTATION parameter, and the
+direction of the finger ellipse is given by the vector (a - b).
 
 
 For type A devices, further specification of the touch shape is possible
 For type A devices, further specification of the touch shape is possible
 via ABS_MT_BLOB_ID.
 via ABS_MT_BLOB_ID.
@@ -224,7 +246,7 @@ tool. Omit if circular [4].
 The above four values can be used to derive additional information about
 The above four values can be used to derive additional information about
 the contact. The ratio ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR approximates
 the contact. The ratio ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR approximates
 the notion of pressure. The fingers of the hand and the palm all have
 the notion of pressure. The fingers of the hand and the palm all have
-different characteristic widths [1].
+different characteristic widths.
 
 
 ABS_MT_PRESSURE
 ABS_MT_PRESSURE
 
 
@@ -240,17 +262,24 @@ the contact is hovering above the surface.
 
 
 ABS_MT_ORIENTATION
 ABS_MT_ORIENTATION
 
 
-The orientation of the ellipse. The value should describe a signed quarter
-of a revolution clockwise around the touch center. The signed value range
-is arbitrary, but zero should be returned for a finger aligned along the Y
-axis of the surface, a negative value when finger is turned to the left, and
-a positive value when finger turned to the right. When completely aligned with
-the X axis, the range max should be returned.  Orientation can be omitted
-if the touching object is circular, or if the information is not available
-in the kernel driver. Partial orientation support is possible if the device
-can distinguish between the two axis, but not (uniquely) any values in
-between. In such cases, the range of ABS_MT_ORIENTATION should be [0, 1]
-[4].
+The orientation of the touching ellipse. The value should describe a signed
+quarter of a revolution clockwise around the touch center. The signed value
+range is arbitrary, but zero should be returned for an ellipse aligned with
+the Y axis of the surface, a negative value when the ellipse is turned to
+the left, and a positive value when the ellipse is turned to the
+right. When completely aligned with the X axis, the range max should be
+returned.
+
+Touch ellipsis are symmetrical by default. For devices capable of true 360
+degree orientation, the reported orientation must exceed the range max to
+indicate more than a quarter of a revolution. For an upside-down finger,
+range max * 2 should be returned.
+
+Orientation can be omitted if the touch area is circular, or if the
+information is not available in the kernel driver. Partial orientation
+support is possible if the device can distinguish between the two axis, but
+not (uniquely) any values in between. In such cases, the range of
+ABS_MT_ORIENTATION should be [0, 1] [4].
 
 
 ABS_MT_POSITION_X
 ABS_MT_POSITION_X
 
 
@@ -260,6 +289,23 @@ ABS_MT_POSITION_Y
 
 
 The surface Y coordinate of the center of the touching ellipse.
 The surface Y coordinate of the center of the touching ellipse.
 
 
+ABS_MT_TOOL_X
+
+The surface X coordinate of the center of the approaching tool. Omit if
+the device cannot distinguish between the intended touch point and the
+tool itself.
+
+ABS_MT_TOOL_Y
+
+The surface Y coordinate of the center of the approaching tool. Omit if the
+device cannot distinguish between the intended touch point and the tool
+itself.
+
+The four position values can be used to separate the position of the touch
+from the position of the tool. If both positions are present, the major
+tool axis points towards the touch point [1]. Otherwise, the tool axes are
+aligned with the touch axes.
+
 ABS_MT_TOOL_TYPE
 ABS_MT_TOOL_TYPE
 
 
 The type of approaching tool. A lot of kernel drivers cannot distinguish
 The type of approaching tool. A lot of kernel drivers cannot distinguish
@@ -305,6 +351,28 @@ The range of ABS_MT_ORIENTATION should be set to [0, 1], to indicate that
 the device can distinguish between a finger along the Y axis (0) and a
 the device can distinguish between a finger along the Y axis (0) and a
 finger along the X axis (1).
 finger along the X axis (1).
 
 
+For win8 devices with both T and C coordinates, the position mapping is
+
+   ABS_MT_POSITION_X := T_X
+   ABS_MT_POSITION_Y := T_Y
+   ABS_MT_TOOL_X := C_X
+   ABS_MT_TOOL_X := C_Y
+
+Unfortunately, there is not enough information to specify both the touching
+ellipse and the tool ellipse, so one has to resort to approximations.  One
+simple scheme, which is compatible with earlier usage, is:
+
+   ABS_MT_TOUCH_MAJOR := min(X, Y)
+   ABS_MT_TOUCH_MINOR := <not used>
+   ABS_MT_ORIENTATION := <not used>
+   ABS_MT_WIDTH_MAJOR := min(X, Y) + distance(T, C)
+   ABS_MT_WIDTH_MINOR := min(X, Y)
+
+Rationale: We have no information about the orientation of the touching
+ellipse, so approximate it with an inscribed circle instead. The tool
+ellipse should align with the the vector (T - C), so the diameter must
+increase with distance(T, C). Finally, assume that the touch diameter is
+equal to the tool thickness, and we arrive at the formulas above.
 
 
 Finger Tracking
 Finger Tracking
 ---------------
 ---------------
@@ -338,9 +406,7 @@ subsequent events of the same type refer to different fingers.
 For example usage of the type A protocol, see the bcm5974 driver. For
 For example usage of the type A protocol, see the bcm5974 driver. For
 example usage of the type B protocol, see the hid-egalax driver.
 example usage of the type B protocol, see the hid-egalax driver.
 
 
-[1] With the extension ABS_MT_APPROACH_X and ABS_MT_APPROACH_Y, the
-difference between the contact position and the approaching tool position
-could be used to derive tilt.
+[1] Also, the difference (TOOL_X - POSITION_X) can be used to model tilt.
 [2] The list can of course be extended.
 [2] The list can of course be extended.
 [3] The mtdev project: http://bitmath.org/code/mtdev/.
 [3] The mtdev project: http://bitmath.org/code/mtdev/.
 [4] See the section on event computation.
 [4] See the section on event computation.

+ 1 - 1
drivers/input/input-mt.c

@@ -135,7 +135,7 @@ EXPORT_SYMBOL(input_mt_report_finger_count);
  */
  */
 void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
 void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
 {
 {
-	struct input_mt_slot *oldest = 0;
+	struct input_mt_slot *oldest = NULL;
 	int oldid = dev->trkid;
 	int oldid = dev->trkid;
 	int count = 0;
 	int count = 0;
 	int i;
 	int i;

+ 241 - 222
drivers/input/touchscreen/atmel_mxt_ts.c

@@ -36,6 +36,7 @@
 #define MXT_FW_NAME		"maxtouch.fw"
 #define MXT_FW_NAME		"maxtouch.fw"
 
 
 /* Registers */
 /* Registers */
+#define MXT_INFO		0x00
 #define MXT_FAMILY_ID		0x00
 #define MXT_FAMILY_ID		0x00
 #define MXT_VARIANT_ID		0x01
 #define MXT_VARIANT_ID		0x01
 #define MXT_VERSION		0x02
 #define MXT_VERSION		0x02
@@ -194,6 +195,7 @@
 #define MXT_BOOT_STATUS_MASK	0x3f
 #define MXT_BOOT_STATUS_MASK	0x3f
 
 
 /* Touch status */
 /* Touch status */
+#define MXT_UNGRIP		(1 << 0)
 #define MXT_SUPPRESS		(1 << 1)
 #define MXT_SUPPRESS		(1 << 1)
 #define MXT_AMP			(1 << 2)
 #define MXT_AMP			(1 << 2)
 #define MXT_VECTOR		(1 << 3)
 #define MXT_VECTOR		(1 << 3)
@@ -210,8 +212,6 @@
 /* Touchscreen absolute values */
 /* Touchscreen absolute values */
 #define MXT_MAX_AREA		0xff
 #define MXT_MAX_AREA		0xff
 
 
-#define MXT_MAX_FINGER		10
-
 struct mxt_info {
 struct mxt_info {
 	u8 family_id;
 	u8 family_id;
 	u8 variant_id;
 	u8 variant_id;
@@ -225,44 +225,37 @@ struct mxt_info {
 struct mxt_object {
 struct mxt_object {
 	u8 type;
 	u8 type;
 	u16 start_address;
 	u16 start_address;
-	u8 size;
-	u8 instances;
+	u8 size;		/* Size of each instance - 1 */
+	u8 instances;		/* Number of instances - 1 */
 	u8 num_report_ids;
 	u8 num_report_ids;
-
-	/* to map object and message */
-	u8 max_reportid;
-};
+} __packed;
 
 
 struct mxt_message {
 struct mxt_message {
 	u8 reportid;
 	u8 reportid;
 	u8 message[7];
 	u8 message[7];
 };
 };
 
 
-struct mxt_finger {
-	int status;
-	int x;
-	int y;
-	int area;
-	int pressure;
-};
-
 /* Each client has this additional data */
 /* Each client has this additional data */
 struct mxt_data {
 struct mxt_data {
 	struct i2c_client *client;
 	struct i2c_client *client;
 	struct input_dev *input_dev;
 	struct input_dev *input_dev;
+	char phys[64];		/* device physical location */
 	const struct mxt_platform_data *pdata;
 	const struct mxt_platform_data *pdata;
 	struct mxt_object *object_table;
 	struct mxt_object *object_table;
 	struct mxt_info info;
 	struct mxt_info info;
-	struct mxt_finger finger[MXT_MAX_FINGER];
 	unsigned int irq;
 	unsigned int irq;
 	unsigned int max_x;
 	unsigned int max_x;
 	unsigned int max_y;
 	unsigned int max_y;
+
+	/* Cached parameters from object table */
+	u8 T6_reportid;
+	u8 T9_reportid_min;
+	u8 T9_reportid_max;
 };
 };
 
 
 static bool mxt_object_readable(unsigned int type)
 static bool mxt_object_readable(unsigned int type)
 {
 {
 	switch (type) {
 	switch (type) {
-	case MXT_GEN_MESSAGE_T5:
 	case MXT_GEN_COMMAND_T6:
 	case MXT_GEN_COMMAND_T6:
 	case MXT_GEN_POWER_T7:
 	case MXT_GEN_POWER_T7:
 	case MXT_GEN_ACQUIRE_T8:
 	case MXT_GEN_ACQUIRE_T8:
@@ -396,6 +389,7 @@ static int __mxt_read_reg(struct i2c_client *client,
 {
 {
 	struct i2c_msg xfer[2];
 	struct i2c_msg xfer[2];
 	u8 buf[2];
 	u8 buf[2];
+	int ret;
 
 
 	buf[0] = reg & 0xff;
 	buf[0] = reg & 0xff;
 	buf[1] = (reg >> 8) & 0xff;
 	buf[1] = (reg >> 8) & 0xff;
@@ -412,12 +406,17 @@ static int __mxt_read_reg(struct i2c_client *client,
 	xfer[1].len = len;
 	xfer[1].len = len;
 	xfer[1].buf = val;
 	xfer[1].buf = val;
 
 
-	if (i2c_transfer(client->adapter, xfer, 2) != 2) {
-		dev_err(&client->dev, "%s: i2c transfer failed\n", __func__);
-		return -EIO;
+	ret = i2c_transfer(client->adapter, xfer, 2);
+	if (ret == 2) {
+		ret = 0;
+	} else {
+		if (ret >= 0)
+			ret = -EIO;
+		dev_err(&client->dev, "%s: i2c transfer failed (%d)\n",
+			__func__, ret);
 	}
 	}
 
 
-	return 0;
+	return ret;
 }
 }
 
 
 static int mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val)
 static int mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val)
@@ -425,27 +424,39 @@ static int mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val)
 	return __mxt_read_reg(client, reg, 1, val);
 	return __mxt_read_reg(client, reg, 1, val);
 }
 }
 
 
-static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val)
+static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len,
+			   const void *val)
 {
 {
-	u8 buf[3];
+	u8 *buf;
+	size_t count;
+	int ret;
+
+	count = len + 2;
+	buf = kmalloc(count, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
 
 
 	buf[0] = reg & 0xff;
 	buf[0] = reg & 0xff;
 	buf[1] = (reg >> 8) & 0xff;
 	buf[1] = (reg >> 8) & 0xff;
-	buf[2] = val;
+	memcpy(&buf[2], val, len);
 
 
-	if (i2c_master_send(client, buf, 3) != 3) {
-		dev_err(&client->dev, "%s: i2c send failed\n", __func__);
-		return -EIO;
+	ret = i2c_master_send(client, buf, count);
+	if (ret == count) {
+		ret = 0;
+	} else {
+		if (ret >= 0)
+			ret = -EIO;
+		dev_err(&client->dev, "%s: i2c send failed (%d)\n",
+			__func__, ret);
 	}
 	}
 
 
-	return 0;
+	kfree(buf);
+	return ret;
 }
 }
 
 
-static int mxt_read_object_table(struct i2c_client *client,
-				      u16 reg, u8 *object_buf)
+static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val)
 {
 {
-	return __mxt_read_reg(client, reg, MXT_OBJECT_SIZE,
-				   object_buf);
+	return __mxt_write_reg(client, reg, 1, &val);
 }
 }
 
 
 static struct mxt_object *
 static struct mxt_object *
@@ -479,20 +490,6 @@ static int mxt_read_message(struct mxt_data *data,
 			sizeof(struct mxt_message), message);
 			sizeof(struct mxt_message), message);
 }
 }
 
 
-static int mxt_read_object(struct mxt_data *data,
-				u8 type, u8 offset, u8 *val)
-{
-	struct mxt_object *object;
-	u16 reg;
-
-	object = mxt_get_object(data, type);
-	if (!object)
-		return -EINVAL;
-
-	reg = object->start_address;
-	return __mxt_read_reg(data->client, reg + offset, 1, val);
-}
-
 static int mxt_write_object(struct mxt_data *data,
 static int mxt_write_object(struct mxt_data *data,
 				 u8 type, u8 offset, u8 val)
 				 u8 type, u8 offset, u8 val)
 {
 {
@@ -507,75 +504,17 @@ static int mxt_write_object(struct mxt_data *data,
 	return mxt_write_reg(data->client, reg + offset, val);
 	return mxt_write_reg(data->client, reg + offset, val);
 }
 }
 
 
-static void mxt_input_report(struct mxt_data *data, int single_id)
-{
-	struct mxt_finger *finger = data->finger;
-	struct input_dev *input_dev = data->input_dev;
-	int status = finger[single_id].status;
-	int finger_num = 0;
-	int id;
-
-	for (id = 0; id < MXT_MAX_FINGER; id++) {
-		if (!finger[id].status)
-			continue;
-
-		input_mt_slot(input_dev, id);
-		input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,
-				finger[id].status != MXT_RELEASE);
-
-		if (finger[id].status != MXT_RELEASE) {
-			finger_num++;
-			input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
-					finger[id].area);
-			input_report_abs(input_dev, ABS_MT_POSITION_X,
-					finger[id].x);
-			input_report_abs(input_dev, ABS_MT_POSITION_Y,
-					finger[id].y);
-			input_report_abs(input_dev, ABS_MT_PRESSURE,
-					finger[id].pressure);
-		} else {
-			finger[id].status = 0;
-		}
-	}
-
-	input_report_key(input_dev, BTN_TOUCH, finger_num > 0);
-
-	if (status != MXT_RELEASE) {
-		input_report_abs(input_dev, ABS_X, finger[single_id].x);
-		input_report_abs(input_dev, ABS_Y, finger[single_id].y);
-		input_report_abs(input_dev,
-				 ABS_PRESSURE, finger[single_id].pressure);
-	}
-
-	input_sync(input_dev);
-}
-
 static void mxt_input_touchevent(struct mxt_data *data,
 static void mxt_input_touchevent(struct mxt_data *data,
 				      struct mxt_message *message, int id)
 				      struct mxt_message *message, int id)
 {
 {
-	struct mxt_finger *finger = data->finger;
 	struct device *dev = &data->client->dev;
 	struct device *dev = &data->client->dev;
 	u8 status = message->message[0];
 	u8 status = message->message[0];
+	struct input_dev *input_dev = data->input_dev;
 	int x;
 	int x;
 	int y;
 	int y;
 	int area;
 	int area;
 	int pressure;
 	int pressure;
 
 
-	/* Check the touch is present on the screen */
-	if (!(status & MXT_DETECT)) {
-		if (status & MXT_RELEASE) {
-			dev_dbg(dev, "[%d] released\n", id);
-
-			finger[id].status = MXT_RELEASE;
-			mxt_input_report(data, id);
-		}
-		return;
-	}
-
-	/* Check only AMP detection */
-	if (!(status & (MXT_PRESS | MXT_MOVE)))
-		return;
-
 	x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf);
 	x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf);
 	y = (message->message[2] << 4) | ((message->message[3] & 0xf));
 	y = (message->message[2] << 4) | ((message->message[3] & 0xf));
 	if (data->max_x < 1024)
 	if (data->max_x < 1024)
@@ -586,30 +525,50 @@ static void mxt_input_touchevent(struct mxt_data *data,
 	area = message->message[4];
 	area = message->message[4];
 	pressure = message->message[5];
 	pressure = message->message[5];
 
 
-	dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id,
-		status & MXT_MOVE ? "moved" : "pressed",
-		x, y, area);
+	dev_dbg(dev,
+		"[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u\n",
+		id,
+		(status & MXT_DETECT) ? 'D' : '.',
+		(status & MXT_PRESS) ? 'P' : '.',
+		(status & MXT_RELEASE) ? 'R' : '.',
+		(status & MXT_MOVE) ? 'M' : '.',
+		(status & MXT_VECTOR) ? 'V' : '.',
+		(status & MXT_AMP) ? 'A' : '.',
+		(status & MXT_SUPPRESS) ? 'S' : '.',
+		(status & MXT_UNGRIP) ? 'U' : '.',
+		x, y, area, pressure);
+
+	input_mt_slot(input_dev, id);
+	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,
+				   status & MXT_DETECT);
+
+	if (status & MXT_DETECT) {
+		input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+		input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+		input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
+		input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area);
+	}
+}
 
 
-	finger[id].status = status & MXT_MOVE ?
-				MXT_MOVE : MXT_PRESS;
-	finger[id].x = x;
-	finger[id].y = y;
-	finger[id].area = area;
-	finger[id].pressure = pressure;
+static unsigned mxt_extract_T6_csum(const u8 *csum)
+{
+	return csum[0] | (csum[1] << 8) | (csum[2] << 16);
+}
 
 
-	mxt_input_report(data, id);
+static bool mxt_is_T9_message(struct mxt_data *data, struct mxt_message *msg)
+{
+	u8 id = msg->reportid;
+	return (id >= data->T9_reportid_min && id <= data->T9_reportid_max);
 }
 }
 
 
 static irqreturn_t mxt_interrupt(int irq, void *dev_id)
 static irqreturn_t mxt_interrupt(int irq, void *dev_id)
 {
 {
 	struct mxt_data *data = dev_id;
 	struct mxt_data *data = dev_id;
 	struct mxt_message message;
 	struct mxt_message message;
-	struct mxt_object *object;
+	const u8 *payload = &message.message[0];
 	struct device *dev = &data->client->dev;
 	struct device *dev = &data->client->dev;
-	int id;
 	u8 reportid;
 	u8 reportid;
-	u8 max_reportid;
-	u8 min_reportid;
+	bool update_input = false;
 
 
 	do {
 	do {
 		if (mxt_read_message(data, &message)) {
 		if (mxt_read_message(data, &message)) {
@@ -619,21 +578,25 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id)
 
 
 		reportid = message.reportid;
 		reportid = message.reportid;
 
 
-		/* whether reportid is thing of MXT_TOUCH_MULTI_T9 */
-		object = mxt_get_object(data, MXT_TOUCH_MULTI_T9);
-		if (!object)
-			goto end;
-
-		max_reportid = object->max_reportid;
-		min_reportid = max_reportid - object->num_report_ids + 1;
-		id = reportid - min_reportid;
-
-		if (reportid >= min_reportid && reportid <= max_reportid)
+		if (reportid == data->T6_reportid) {
+			u8 status = payload[0];
+			unsigned csum = mxt_extract_T6_csum(&payload[1]);
+			dev_dbg(dev, "Status: %02x Config Checksum: %06x\n",
+				status, csum);
+		} else if (mxt_is_T9_message(data, &message)) {
+			int id = reportid - data->T9_reportid_min;
 			mxt_input_touchevent(data, &message, id);
 			mxt_input_touchevent(data, &message, id);
-		else
+			update_input = true;
+		} else {
 			mxt_dump_message(dev, &message);
 			mxt_dump_message(dev, &message);
+		}
 	} while (reportid != 0xff);
 	} while (reportid != 0xff);
 
 
+	if (update_input) {
+		input_mt_report_pointer_emulation(data->input_dev, false);
+		input_sync(data->input_dev);
+	}
+
 end:
 end:
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }
@@ -644,7 +607,8 @@ static int mxt_check_reg_init(struct mxt_data *data)
 	struct mxt_object *object;
 	struct mxt_object *object;
 	struct device *dev = &data->client->dev;
 	struct device *dev = &data->client->dev;
 	int index = 0;
 	int index = 0;
-	int i, j, config_offset;
+	int i, size;
+	int ret;
 
 
 	if (!pdata->config) {
 	if (!pdata->config) {
 		dev_dbg(dev, "No cfg data defined, skipping reg init\n");
 		dev_dbg(dev, "No cfg data defined, skipping reg init\n");
@@ -657,18 +621,17 @@ static int mxt_check_reg_init(struct mxt_data *data)
 		if (!mxt_object_writable(object->type))
 		if (!mxt_object_writable(object->type))
 			continue;
 			continue;
 
 
-		for (j = 0;
-		     j < (object->size + 1) * (object->instances + 1);
-		     j++) {
-			config_offset = index + j;
-			if (config_offset > pdata->config_length) {
-				dev_err(dev, "Not enough config data!\n");
-				return -EINVAL;
-			}
-			mxt_write_object(data, object->type, j,
-					 pdata->config[config_offset]);
+		size = (object->size + 1) * (object->instances + 1);
+		if (index + size > pdata->config_length) {
+			dev_err(dev, "Not enough config data!\n");
+			return -EINVAL;
 		}
 		}
-		index += (object->size + 1) * (object->instances + 1);
+
+		ret = __mxt_write_reg(data->client, object->start_address,
+				size, &pdata->config[index]);
+		if (ret)
+			return ret;
+		index += size;
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -749,68 +712,76 @@ static int mxt_get_info(struct mxt_data *data)
 	struct i2c_client *client = data->client;
 	struct i2c_client *client = data->client;
 	struct mxt_info *info = &data->info;
 	struct mxt_info *info = &data->info;
 	int error;
 	int error;
-	u8 val;
 
 
-	error = mxt_read_reg(client, MXT_FAMILY_ID, &val);
+	/* Read 7-byte info block starting at address 0 */
+	error = __mxt_read_reg(client, MXT_INFO, sizeof(*info), info);
 	if (error)
 	if (error)
 		return error;
 		return error;
-	info->family_id = val;
-
-	error = mxt_read_reg(client, MXT_VARIANT_ID, &val);
-	if (error)
-		return error;
-	info->variant_id = val;
-
-	error = mxt_read_reg(client, MXT_VERSION, &val);
-	if (error)
-		return error;
-	info->version = val;
-
-	error = mxt_read_reg(client, MXT_BUILD, &val);
-	if (error)
-		return error;
-	info->build = val;
-
-	error = mxt_read_reg(client, MXT_OBJECT_NUM, &val);
-	if (error)
-		return error;
-	info->object_num = val;
 
 
 	return 0;
 	return 0;
 }
 }
 
 
 static int mxt_get_object_table(struct mxt_data *data)
 static int mxt_get_object_table(struct mxt_data *data)
 {
 {
+	struct i2c_client *client = data->client;
+	size_t table_size;
 	int error;
 	int error;
 	int i;
 	int i;
-	u16 reg;
-	u8 reportid = 0;
-	u8 buf[MXT_OBJECT_SIZE];
+	u8 reportid;
+
+	table_size = data->info.object_num * sizeof(struct mxt_object);
+	error = __mxt_read_reg(client, MXT_OBJECT_START, table_size,
+			data->object_table);
+	if (error)
+		return error;
 
 
+	/* Valid Report IDs start counting from 1 */
+	reportid = 1;
 	for (i = 0; i < data->info.object_num; i++) {
 	for (i = 0; i < data->info.object_num; i++) {
 		struct mxt_object *object = data->object_table + i;
 		struct mxt_object *object = data->object_table + i;
+		u8 min_id, max_id;
 
 
-		reg = MXT_OBJECT_START + MXT_OBJECT_SIZE * i;
-		error = mxt_read_object_table(data->client, reg, buf);
-		if (error)
-			return error;
-
-		object->type = buf[0];
-		object->start_address = (buf[2] << 8) | buf[1];
-		object->size = buf[3];
-		object->instances = buf[4];
-		object->num_report_ids = buf[5];
+		le16_to_cpus(&object->start_address);
 
 
 		if (object->num_report_ids) {
 		if (object->num_report_ids) {
+			min_id = reportid;
 			reportid += object->num_report_ids *
 			reportid += object->num_report_ids *
 					(object->instances + 1);
 					(object->instances + 1);
-			object->max_reportid = reportid;
+			max_id = reportid - 1;
+		} else {
+			min_id = 0;
+			max_id = 0;
+		}
+
+		dev_dbg(&data->client->dev,
+			"Type %2d Start %3d Size %3d Instances %2d ReportIDs %3u : %3u\n",
+			object->type, object->start_address, object->size + 1,
+			object->instances + 1, min_id, max_id);
+
+		switch (object->type) {
+		case MXT_GEN_COMMAND_T6:
+			data->T6_reportid = min_id;
+			break;
+		case MXT_TOUCH_MULTI_T9:
+			data->T9_reportid_min = min_id;
+			data->T9_reportid_max = max_id;
+			break;
 		}
 		}
 	}
 	}
 
 
 	return 0;
 	return 0;
 }
 }
 
 
+static void mxt_free_object_table(struct mxt_data *data)
+{
+	kfree(data->object_table);
+	data->object_table = NULL;
+	data->T6_reportid = 0;
+	data->T9_reportid_min = 0;
+	data->T9_reportid_max = 0;
+
+}
+
 static int mxt_initialize(struct mxt_data *data)
 static int mxt_initialize(struct mxt_data *data)
 {
 {
 	struct i2c_client *client = data->client;
 	struct i2c_client *client = data->client;
@@ -833,12 +804,12 @@ static int mxt_initialize(struct mxt_data *data)
 	/* Get object table information */
 	/* Get object table information */
 	error = mxt_get_object_table(data);
 	error = mxt_get_object_table(data);
 	if (error)
 	if (error)
-		return error;
+		goto err_free_object_table;
 
 
 	/* Check register init values */
 	/* Check register init values */
 	error = mxt_check_reg_init(data);
 	error = mxt_check_reg_init(data);
 	if (error)
 	if (error)
-		return error;
+		goto err_free_object_table;
 
 
 	mxt_handle_pdata(data);
 	mxt_handle_pdata(data);
 
 
@@ -856,25 +827,29 @@ static int mxt_initialize(struct mxt_data *data)
 	/* Update matrix size at info struct */
 	/* Update matrix size at info struct */
 	error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val);
 	error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val);
 	if (error)
 	if (error)
-		return error;
+		goto err_free_object_table;
 	info->matrix_xsize = val;
 	info->matrix_xsize = val;
 
 
 	error = mxt_read_reg(client, MXT_MATRIX_Y_SIZE, &val);
 	error = mxt_read_reg(client, MXT_MATRIX_Y_SIZE, &val);
 	if (error)
 	if (error)
-		return error;
+		goto err_free_object_table;
 	info->matrix_ysize = val;
 	info->matrix_ysize = val;
 
 
 	dev_info(&client->dev,
 	dev_info(&client->dev,
-			"Family ID: %d Variant ID: %d Version: %d Build: %d\n",
-			info->family_id, info->variant_id, info->version,
-			info->build);
+			"Family ID: %u Variant ID: %u Major.Minor.Build: %u.%u.%02X\n",
+			info->family_id, info->variant_id, info->version >> 4,
+			info->version & 0xf, info->build);
 
 
 	dev_info(&client->dev,
 	dev_info(&client->dev,
-			"Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n",
+			"Matrix X Size: %u Matrix Y Size: %u Object Num: %u\n",
 			info->matrix_xsize, info->matrix_ysize,
 			info->matrix_xsize, info->matrix_ysize,
 			info->object_num);
 			info->object_num);
 
 
 	return 0;
 	return 0;
+
+err_free_object_table:
+	mxt_free_object_table(data);
+	return error;
 }
 }
 
 
 static void mxt_calc_resolution(struct mxt_data *data)
 static void mxt_calc_resolution(struct mxt_data *data)
@@ -891,6 +866,44 @@ static void mxt_calc_resolution(struct mxt_data *data)
 	}
 	}
 }
 }
 
 
+/* Firmware Version is returned as Major.Minor.Build */
+static ssize_t mxt_fw_version_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct mxt_data *data = dev_get_drvdata(dev);
+	struct mxt_info *info = &data->info;
+	return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n",
+			 info->version >> 4, info->version & 0xf, info->build);
+}
+
+/* Hardware Version is returned as FamilyID.VariantID */
+static ssize_t mxt_hw_version_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct mxt_data *data = dev_get_drvdata(dev);
+	struct mxt_info *info = &data->info;
+	return scnprintf(buf, PAGE_SIZE, "%u.%u\n",
+			 info->family_id, info->variant_id);
+}
+
+static ssize_t mxt_show_instance(char *buf, int count,
+				 struct mxt_object *object, int instance,
+				 const u8 *val)
+{
+	int i;
+
+	if (object->instances > 0)
+		count += scnprintf(buf + count, PAGE_SIZE - count,
+				   "Instance %u\n", instance);
+
+	for (i = 0; i < object->size + 1; i++)
+		count += scnprintf(buf + count, PAGE_SIZE - count,
+				"\t[%2u]: %02x (%d)\n", i, val[i], val[i]);
+	count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
+
+	return count;
+}
+
 static ssize_t mxt_object_show(struct device *dev,
 static ssize_t mxt_object_show(struct device *dev,
 				    struct device_attribute *attr, char *buf)
 				    struct device_attribute *attr, char *buf)
 {
 {
@@ -899,43 +912,38 @@ static ssize_t mxt_object_show(struct device *dev,
 	int count = 0;
 	int count = 0;
 	int i, j;
 	int i, j;
 	int error;
 	int error;
-	u8 val;
+	u8 *obuf;
 
 
+	/* Pre-allocate buffer large enough to hold max sized object. */
+	obuf = kmalloc(256, GFP_KERNEL);
+	if (!obuf)
+		return -ENOMEM;
+
+	error = 0;
 	for (i = 0; i < data->info.object_num; i++) {
 	for (i = 0; i < data->info.object_num; i++) {
 		object = data->object_table + i;
 		object = data->object_table + i;
 
 
-		count += snprintf(buf + count, PAGE_SIZE - count,
-				"Object[%d] (Type %d)\n",
-				i + 1, object->type);
-		if (count >= PAGE_SIZE)
-			return PAGE_SIZE - 1;
-
-		if (!mxt_object_readable(object->type)) {
-			count += snprintf(buf + count, PAGE_SIZE - count,
-					"\n");
-			if (count >= PAGE_SIZE)
-				return PAGE_SIZE - 1;
+		if (!mxt_object_readable(object->type))
 			continue;
 			continue;
-		}
 
 
-		for (j = 0; j < object->size + 1; j++) {
-			error = mxt_read_object(data,
-						object->type, j, &val);
+		count += scnprintf(buf + count, PAGE_SIZE - count,
+				"T%u:\n", object->type);
+
+		for (j = 0; j < object->instances + 1; j++) {
+			u16 size = object->size + 1;
+			u16 addr = object->start_address + j * size;
+
+			error = __mxt_read_reg(data->client, addr, size, obuf);
 			if (error)
 			if (error)
-				return error;
+				goto done;
 
 
-			count += snprintf(buf + count, PAGE_SIZE - count,
-					"\t[%2d]: %02x (%d)\n", j, val, val);
-			if (count >= PAGE_SIZE)
-				return PAGE_SIZE - 1;
+			count = mxt_show_instance(buf, count, object, j, obuf);
 		}
 		}
-
-		count += snprintf(buf + count, PAGE_SIZE - count, "\n");
-		if (count >= PAGE_SIZE)
-			return PAGE_SIZE - 1;
 	}
 	}
 
 
-	return count;
+done:
+	kfree(obuf);
+	return error ?: count;
 }
 }
 
 
 static int mxt_load_fw(struct device *dev, const char *fn)
 static int mxt_load_fw(struct device *dev, const char *fn)
@@ -1028,8 +1036,7 @@ static ssize_t mxt_update_fw_store(struct device *dev,
 		/* Wait for reset */
 		/* Wait for reset */
 		msleep(MXT_FWRESET_TIME);
 		msleep(MXT_FWRESET_TIME);
 
 
-		kfree(data->object_table);
-		data->object_table = NULL;
+		mxt_free_object_table(data);
 
 
 		mxt_initialize(data);
 		mxt_initialize(data);
 	}
 	}
@@ -1043,10 +1050,14 @@ static ssize_t mxt_update_fw_store(struct device *dev,
 	return count;
 	return count;
 }
 }
 
 
+static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL);
 static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL);
 static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL);
 static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store);
 static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store);
 
 
 static struct attribute *mxt_attrs[] = {
 static struct attribute *mxt_attrs[] = {
+	&dev_attr_fw_version.attr,
+	&dev_attr_hw_version.attr,
 	&dev_attr_object.attr,
 	&dev_attr_object.attr,
 	&dev_attr_update_fw.attr,
 	&dev_attr_update_fw.attr,
 	NULL
 	NULL
@@ -1093,6 +1104,7 @@ static int __devinit mxt_probe(struct i2c_client *client,
 	struct mxt_data *data;
 	struct mxt_data *data;
 	struct input_dev *input_dev;
 	struct input_dev *input_dev;
 	int error;
 	int error;
+	unsigned int num_mt_slots;
 
 
 	if (!pdata)
 	if (!pdata)
 		return -EINVAL;
 		return -EINVAL;
@@ -1106,6 +1118,10 @@ static int __devinit mxt_probe(struct i2c_client *client,
 	}
 	}
 
 
 	input_dev->name = "Atmel maXTouch Touchscreen";
 	input_dev->name = "Atmel maXTouch Touchscreen";
+	snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0",
+		 client->adapter->nr, client->addr);
+	input_dev->phys = data->phys;
+
 	input_dev->id.bustype = BUS_I2C;
 	input_dev->id.bustype = BUS_I2C;
 	input_dev->dev.parent = &client->dev;
 	input_dev->dev.parent = &client->dev;
 	input_dev->open = mxt_input_open;
 	input_dev->open = mxt_input_open;
@@ -1118,6 +1134,10 @@ static int __devinit mxt_probe(struct i2c_client *client,
 
 
 	mxt_calc_resolution(data);
 	mxt_calc_resolution(data);
 
 
+	error = mxt_initialize(data);
+	if (error)
+		goto err_free_mem;
+
 	__set_bit(EV_ABS, input_dev->evbit);
 	__set_bit(EV_ABS, input_dev->evbit);
 	__set_bit(EV_KEY, input_dev->evbit);
 	__set_bit(EV_KEY, input_dev->evbit);
 	__set_bit(BTN_TOUCH, input_dev->keybit);
 	__set_bit(BTN_TOUCH, input_dev->keybit);
@@ -1131,7 +1151,10 @@ static int __devinit mxt_probe(struct i2c_client *client,
 			     0, 255, 0, 0);
 			     0, 255, 0, 0);
 
 
 	/* For multi touch */
 	/* For multi touch */
-	input_mt_init_slots(input_dev, MXT_MAX_FINGER);
+	num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
+	error = input_mt_init_slots(input_dev, num_mt_slots);
+	if (error)
+		goto err_free_object;
 	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
 	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
 			     0, MXT_MAX_AREA, 0, 0);
 			     0, MXT_MAX_AREA, 0, 0);
 	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
 	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
@@ -1144,13 +1167,9 @@ static int __devinit mxt_probe(struct i2c_client *client,
 	input_set_drvdata(input_dev, data);
 	input_set_drvdata(input_dev, data);
 	i2c_set_clientdata(client, data);
 	i2c_set_clientdata(client, data);
 
 
-	error = mxt_initialize(data);
-	if (error)
-		goto err_free_object;
-
 	error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
 	error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
 				     pdata->irqflags | IRQF_ONESHOT,
 				     pdata->irqflags | IRQF_ONESHOT,
-				     client->dev.driver->name, data);
+				     client->name, data);
 	if (error) {
 	if (error) {
 		dev_err(&client->dev, "Failed to register interrupt\n");
 		dev_err(&client->dev, "Failed to register interrupt\n");
 		goto err_free_object;
 		goto err_free_object;

+ 5 - 3
include/linux/input.h

@@ -807,18 +807,20 @@ struct input_keymap_entry {
 #define ABS_MT_WIDTH_MAJOR	0x32	/* Major axis of approaching ellipse */
 #define ABS_MT_WIDTH_MAJOR	0x32	/* Major axis of approaching ellipse */
 #define ABS_MT_WIDTH_MINOR	0x33	/* Minor axis (omit if circular) */
 #define ABS_MT_WIDTH_MINOR	0x33	/* Minor axis (omit if circular) */
 #define ABS_MT_ORIENTATION	0x34	/* Ellipse orientation */
 #define ABS_MT_ORIENTATION	0x34	/* Ellipse orientation */
-#define ABS_MT_POSITION_X	0x35	/* Center X ellipse position */
-#define ABS_MT_POSITION_Y	0x36	/* Center Y ellipse position */
+#define ABS_MT_POSITION_X	0x35	/* Center X touch position */
+#define ABS_MT_POSITION_Y	0x36	/* Center Y touch position */
 #define ABS_MT_TOOL_TYPE	0x37	/* Type of touching device */
 #define ABS_MT_TOOL_TYPE	0x37	/* Type of touching device */
 #define ABS_MT_BLOB_ID		0x38	/* Group a set of packets as a blob */
 #define ABS_MT_BLOB_ID		0x38	/* Group a set of packets as a blob */
 #define ABS_MT_TRACKING_ID	0x39	/* Unique ID of initiated contact */
 #define ABS_MT_TRACKING_ID	0x39	/* Unique ID of initiated contact */
 #define ABS_MT_PRESSURE		0x3a	/* Pressure on contact area */
 #define ABS_MT_PRESSURE		0x3a	/* Pressure on contact area */
 #define ABS_MT_DISTANCE		0x3b	/* Contact hover distance */
 #define ABS_MT_DISTANCE		0x3b	/* Contact hover distance */
+#define ABS_MT_TOOL_X		0x3c	/* Center X tool position */
+#define ABS_MT_TOOL_Y		0x3d	/* Center Y tool position */
 
 
 #ifdef __KERNEL__
 #ifdef __KERNEL__
 /* Implementation details, userspace should not care about these */
 /* Implementation details, userspace should not care about these */
 #define ABS_MT_FIRST		ABS_MT_TOUCH_MAJOR
 #define ABS_MT_FIRST		ABS_MT_TOUCH_MAJOR
-#define ABS_MT_LAST		ABS_MT_DISTANCE
+#define ABS_MT_LAST		ABS_MT_TOOL_Y
 #endif
 #endif
 
 
 #define ABS_MAX			0x3f
 #define ABS_MAX			0x3f