linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] input: Add Atmel maXTouch touchscreen driver
@ 2010-11-12 14:14 Iiro Valkonen
  2010-11-12 20:10 ` Dmitry Torokhov
  0 siblings, 1 reply; 6+ messages in thread
From: Iiro Valkonen @ 2010-11-12 14:14 UTC (permalink / raw)
  To: dmitry.torokhov; +Cc: kyungmin.park, jy0922.shim, linux-input


Add Atmel maXTouch touchscreen driver (I2C interface).
The MT slots protocol will be implemented later.


Signed-off-by: Iiro Valkonen (iiro.valkonen@atmel.com)
---
 drivers/input/touchscreen/Kconfig          |   13 +
 drivers/input/touchscreen/Makefile         |    1 +
 drivers/input/touchscreen/atmel_maxtouch.c | 2104 ++++++++++++++++++++++++++++
 include/linux/atmel_maxtouch.h             |  310 ++++
 4 files changed, 2428 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/touchscreen/atmel_maxtouch.c
 create mode 100644 include/linux/atmel_maxtouch.h

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 06ea8da..a0cec0d 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -395,6 +395,19 @@ config TOUCHSCREEN_TOUCHWIN
 	  To compile this driver as a module, choose M here: the
 	  module will be called touchwin.
 
+config TOUCHSCREEN_ATMEL_MAXTOUCH
+ 	tristate "Atmel maXTouch based touchscreen: I2C Interface"
+ 	depends on I2C
+ 	help
+ 	  Say Y here if you have a touchscreen interface using an
+ 	  Amel maXTouch controller, and your board-specific initialization
+ 	  code includes that in its table of I2C devices.
+
+ 	  If unsure, say N (but it's safe to say "Y").
+
+ 	  To compile this driver as a module, choose M here: the
+ 	  module will be called maXTouch.
+
 config TOUCHSCREEN_ATMEL_TSADCC
 	tristate "Atmel Touchscreen Interface"
 	depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 7cc1b4f..861941d 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879)	+= ad7879.o
 obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C)	+= ad7879-i2c.o
 obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI)	+= ad7879-spi.o
 obj-$(CONFIG_TOUCHSCREEN_ADS7846)	+= ads7846.o
+obj-$(CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH)+= atmel_maxtouch.o
 obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC)	+= atmel_tsadcc.o
 obj-$(CONFIG_TOUCHSCREEN_BITSY)		+= h3600_ts_input.o
 obj-$(CONFIG_TOUCHSCREEN_BU21013)       += bu21013_ts.o
diff --git a/drivers/input/touchscreen/atmel_maxtouch.c b/drivers/input/touchscreen/atmel_maxtouch.c
new file mode 100644
index 0000000..cf22f65
--- /dev/null
+++ b/drivers/input/touchscreen/atmel_maxtouch.c
@@ -0,0 +1,2104 @@
+/*
+ *  Atmel maXTouch Touchscreen Controller Driver
+ *
+ *
+ *  Copyright (C) 2010 Atmel Corporation
+ *  Copyright (C) 2010 Ulf Samuelsson (ulf@atmel.com)
+ *  Copyright (C) 2009 Raphael Derosso Pereira <raphaelpereira@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ *
+ * Driver for Atmel maXTouch family of touch controllers.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/debugfs.h>
+#include <linux/cdev.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <linux/uaccess.h>
+
+#include <linux/atmel_maxtouch.h>
+
+
+#define DRIVER_VERSION "0.91a"
+
+static int debug = DEBUG_TRACE;
+/* Defaul comms mode = 0 */
+static int comms;
+module_param(debug, int, 0644);
+module_param(comms, int, 0644);
+
+MODULE_PARM_DESC(debug, "Activate debugging output");
+MODULE_PARM_DESC(comms, "Select communications mode");
+
+/* Device Info descriptor */
+/* Parsed from maXTouch "Id information" inside device */
+struct mxt_device_info {
+	u8   family_id;
+	u8   variant_id;
+	u8   major;
+	u8   minor;
+	u8   build;
+	u8   num_objs;
+	u8   x_size;
+	u8   y_size;
+	char family_name[16];	 /* Family name */
+	char variant_name[16];    /* Variant name */
+	u16  num_nodes;           /* Number of sensor nodes */
+};
+
+/* object descriptor table, parsed from maXTouch "object table" */
+struct mxt_object {
+	u16 chip_addr;
+	u8  type;
+	u8  size;
+	u8  instances;
+	u8  num_report_ids;
+};
+
+
+/* Mapping from report id to object type and instance */
+struct report_id_map {
+	u8  object;
+	u8  instance;
+/*
+ * This is the first report ID belonging to object. It enables us to
+ * find out easily the touch number: each touch has different report
+ * ID (which are assigned to touches in increasing order). By
+ * subtracting the first report ID from current, we get the touch
+ * number.
+ */
+	u8  first_rid;
+};
+
+
+/* Driver datastructure */
+struct mxt_data {
+	struct i2c_client    *client;
+	struct input_dev     *input;
+	char                 phys_name[32];
+	int                  irq;
+
+	u16                  last_read_addr;
+	bool                 new_msgs;
+	u8                   *last_message;
+
+	int                  valid_irq_counter;
+	int                  invalid_irq_counter;
+	int                  irq_counter;
+	int                  message_counter;
+	int                  read_fail_counter;
+
+
+	int                  bytes_to_read;
+
+	struct delayed_work  dwork;
+	u8                   xpos_format;
+	u8                   ypos_format;
+
+	u8                   numtouch;
+
+	struct mxt_device_info	device_info;
+
+	u32		     info_block_crc;
+	u32                  configuration_crc;
+	u16                  report_id_count;
+	struct report_id_map *rid_map;
+	struct mxt_object    *object_table;
+
+	u16                  msg_proc_addr;
+	u8                   message_size;
+
+	u16                  max_x_val;
+	u16                  max_y_val;
+
+	void                 (*init_hw)(void);
+	void                 (*exit_hw)(void);
+	u8                   (*valid_interrupt)(void);
+	u8                   (*read_chg)(void);
+
+	/* debugfs variables */
+	struct dentry        *debug_dir;
+	int                  current_debug_datap;
+
+	struct mutex         debug_mutex;
+	u16                  *debug_data;
+
+	/* Character device variables */
+	struct cdev          cdev;
+	struct cdev          cdev_messages;  /* 2nd Char dev for messages */
+	dev_t                dev_num;
+	struct class         *mxt_class;
+
+
+	u16                  address_pointer;
+	bool                 valid_ap;
+
+	/* Message buffer & pointers */
+	char                 *messages;
+	int                  msg_buffer_startp, msg_buffer_endp;
+
+	/* Put only non-touch messages to buffer if this is set */
+	char                 nontouch_msg_only;
+	struct mutex         msg_mutex;
+};
+
+#define I2C_RETRY_COUNT 5
+#define I2C_PAYLOAD_SIZE 254
+
+/* Returns the start address of object in mXT memory. */
+#define	MXT_BASE_ADDR(object_type, mxt)					\
+	get_object_address(object_type, 0, mxt->object_table,           \
+			   mxt->device_info.num_objs)
+
+/* Maps a report ID to an object type (object type number). */
+#define	REPORT_ID_TO_OBJECT(rid, mxt)			\
+	(((rid) == 0xff) ? 0 : mxt->rid_map[rid].object)
+
+/* Maps a report ID to an object type (string). */
+#define	REPORT_ID_TO_OBJECT_NAME(rid, mxt)			\
+	object_type_name[REPORT_ID_TO_OBJECT(rid, mxt)]
+
+/* Returns non-zero if given object is a touch object */
+#define IS_TOUCH_OBJECT(object) \
+	((object == MXT_TOUCH_MULTITOUCHSCREEN_T9) || \
+	 (object == MXT_TOUCH_KEYARRAY_T15) ||	\
+	 (object == MXT_TOUCH_PROXIMITY_T23) || \
+	 (object == MXT_TOUCH_SINGLETOUCHSCREEN_T10) || \
+	 (object == MXT_TOUCH_XSLIDER_T11) || \
+	 (object == MXT_TOUCH_YSLIDER_T12) || \
+	 (object == MXT_TOUCH_XWHEEL_T13) || \
+	 (object == MXT_TOUCH_YWHEEL_T14) || \
+	 (object == MXT_TOUCH_KEYSET_T31) || \
+	 (object == MXT_TOUCH_XSLIDERSET_T32) ? 1 : 0)
+
+#define mxt_debug(level, ...) \
+	do { \
+		if (debug >= (level)) \
+			pr_debug(__VA_ARGS__); \
+	} while (0)
+
+
+/*
+ * Check whether we have multi-touch enabled kernel; if not, report just the
+ * first touch (on mXT224, the maximum is 10 simultaneous touches).
+ * Because just the 1st one is reported, it might seem that the screen is not
+ * responding to touch if the first touch is removed while the screen is being
+ * touched by another finger, so beware.
+ *
+ */
+
+#ifdef ABS_MT_TRACKING_ID
+static inline void report_mt(int touch_number, int size, int x, int y,
+			     struct mxt_data *mxt) {
+	input_report_abs(mxt->input, ABS_MT_TRACKING_ID, touch_number);
+	input_report_abs(mxt->input, ABS_MT_TOUCH_MAJOR, size);
+	input_report_abs(mxt->input, ABS_MT_POSITION_X, x);
+	input_report_abs(mxt->input, ABS_MT_POSITION_Y, y);
+	input_mt_sync(mxt->input);
+}
+#else
+static inline void report_mt(int touch_number, int size, int x, int y,
+			     struct mxt_data *mxt) {
+	if (touch_number == 0) {
+		input_report_abs(mxt->input, ABS_TOOL_WIDTH, size);
+		input_report_abs(mxt->input, ABS_X, x);
+		input_report_abs(mxt->input, ABS_Y, y);
+	}
+}
+#endif
+
+
+static inline void report_gesture(int data, struct mxt_data *mxt)
+{
+	input_event(mxt->input, EV_MSC, MSC_GESTURE, data);
+}
+
+
+static const u8	*object_type_name[] = {
+	[0]  = "Reserved",
+	[5]  = "GEN_MESSAGEPROCESSOR_T5",
+	[6]  = "GEN_COMMANDPROCESSOR_T6",
+	[7]  = "GEN_POWERCONFIG_T7",
+	[8]  = "GEN_ACQUIRECONFIG_T8",
+	[9]  = "TOUCH_MULTITOUCHSCREEN_T9",
+	[15] = "TOUCH_KEYARRAY_T15",
+	[17] = "SPT_COMMSCONFIG_T18",
+	[19] = "SPT_GPIOPWM_T19",
+	[20] = "PROCI_GRIPFACESUPPRESSION_T20",
+	[22] = "PROCG_NOISESUPPRESSION_T22",
+	[23] = "TOUCH_PROXIMITY_T23",
+	[24] = "PROCI_ONETOUCHGESTUREPROCESSOR_T24",
+	[25] = "SPT_SELFTEST_T25",
+	[27] = "PROCI_TWOTOUCHGESTUREPROCESSOR_T27",
+	[28] = "SPT_CTECONFIG_T28",
+	[37] = "DEBUG_DIAGNOSTICS_T37",
+	[38] = "SPT_USER_DATA_T38",
+	[40] = "PROCI_GRIPSUPPRESSION_T40",
+	[41] = "PROCI_PALMSUPPRESSION_T41",
+	[42] = "PROCI_FACESUPPRESSION_T42",
+	[43] = "SPT_DIGITIZER_T43",
+	[44] = "SPT_MESSAGECOUNT_T44",
+};
+
+
+static u16 get_object_address(uint8_t object_type,
+			      uint8_t instance,
+			      struct mxt_object *object_table,
+			      int max_objs);
+
+static int mxt_write_ap(struct mxt_data *mxt, u16 ap);
+
+static int mxt_read_block_wo_addr(struct i2c_client *client,
+				  u16 length,
+				  u8 *value);
+
+ssize_t debug_data_read(struct mxt_data *mxt, char *buf, size_t count,
+			loff_t *ppos, u8 debug_command)
+{
+	int i;
+	u16 *data;
+	u16 diagnostics_reg;
+	int offset = 0;
+	int size;
+	int read_size;
+	int error;
+	char *buf_start;
+	u16 debug_data_addr;
+	u16 page_address;
+	u8 page;
+	u8 debug_command_reg;
+
+	data = mxt->debug_data;
+	if (data == NULL)
+		return -EIO;
+
+	/* If first read after open, read all data to buffer. */
+	if (mxt->current_debug_datap == 0) {
+		diagnostics_reg =
+			MXT_BASE_ADDR(MXT_GEN_COMMANDPROCESSOR_T6, mxt) +
+			MXT_ADR_T6_DIAGNOSTIC;
+		if (count > (mxt->device_info.num_nodes * 2))
+			count = mxt->device_info.num_nodes;
+		debug_data_addr =
+			MXT_BASE_ADDR(MXT_DEBUG_DIAGNOSTIC_T37, mxt) +
+			MXT_ADR_T37_DATA;
+		page_address = MXT_BASE_ADDR(MXT_DEBUG_DIAGNOSTIC_T37, mxt) +
+			       MXT_ADR_T37_PAGE;
+		error = mxt_read_block(mxt->client, page_address, 1, &page);
+		if (error < 0)
+			return error;
+		mxt_debug(DEBUG_TRACE, "debug data page = %d\n", page);
+		while (page != 0) {
+			error = mxt_write_byte(mxt->client,
+					diagnostics_reg,
+					MXT_CMD_T6_PAGE_DOWN);
+			if (error < 0)
+				return error;
+			/* Wait for command to be handled; when it has, the
+			   register will be cleared. */
+			debug_command_reg = 1;
+			while (debug_command_reg != 0) {
+				error = mxt_read_block(mxt->client,
+						diagnostics_reg, 1,
+						&debug_command_reg);
+				if (error < 0)
+					return error;
+				mxt_debug(DEBUG_TRACE,
+					"Waiting for debug diag command "
+					"to propagate...\n");
+
+			}
+			error = mxt_read_block(mxt->client, page_address, 1,
+					       &page);
+			if (error < 0)
+				return error;
+			mxt_debug(DEBUG_TRACE, "debug data page = %d\n", page);
+		}
+
+		/*
+		 * Lock mutex to prevent writing some unwanted data to debug
+		 * command register. User can still write through the char
+		 * device interface though. TODO: fix?
+		 */
+
+		mutex_lock(&mxt->debug_mutex);
+		/* Configure Debug Diagnostics object to show deltas/refs */
+		error = mxt_write_byte(mxt->client, diagnostics_reg,
+				debug_command);
+
+		/* Wait for command to be handled; when it has, the
+		 * register will be cleared. */
+		debug_command_reg = 1;
+		while (debug_command_reg != 0) {
+			error = mxt_read_block(mxt->client,
+					diagnostics_reg, 1,
+					&debug_command_reg);
+			if (error < 0)
+				return error;
+			mxt_debug(DEBUG_TRACE, "Waiting for debug diag command "
+				"to propagate...\n");
+		}
+
+		if (error < 0) {
+			printk(KERN_WARNING
+				"Error writing to maXTouch device!\n");
+			return error;
+		}
+
+		size = mxt->device_info.num_nodes * sizeof(u16);
+
+		while (size > 0) {
+			read_size = size > 128 ? 128 : size;
+			mxt_debug(DEBUG_TRACE,
+				"Debug data read loop, reading %d bytes...\n",
+				read_size);
+			error = mxt_read_block(mxt->client,
+					       debug_data_addr,
+					       read_size,
+					       (u8 *) &data[offset]);
+			if (error < 0) {
+				printk(KERN_WARNING
+					"Error reading debug data\n");
+				goto error;
+			}
+			offset += read_size/2;
+			size -= read_size;
+
+			/* Select next page */
+			error = mxt_write_byte(mxt->client, diagnostics_reg,
+					MXT_CMD_T6_PAGE_UP);
+			if (error < 0) {
+				printk(KERN_WARNING
+					"Error writing to maXTouch device!\n");
+				goto error;
+			}
+		}
+		mutex_unlock(&mxt->debug_mutex);
+	}
+
+	buf_start = buf;
+	i = mxt->current_debug_datap;
+
+	while (((buf - buf_start) < (count - 6)) &&
+		(i < mxt->device_info.num_nodes)) {
+
+		mxt->current_debug_datap++;
+		if (debug_command == MXT_CMD_T6_REFERENCES_MODE)
+			buf += sprintf(buf, "%d: %5d\n", i,
+				       (u16) le16_to_cpu(data[i]));
+		else if (debug_command == MXT_CMD_T6_DELTAS_MODE)
+			buf += sprintf(buf, "%d: %5d\n", i,
+				       (s16) le16_to_cpu(data[i]));
+		i++;
+	}
+
+	return buf - buf_start;
+error:
+	mutex_unlock(&mxt->debug_mutex);
+	return error;
+}
+
+ssize_t deltas_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+	return debug_data_read(file->private_data, buf, count, ppos,
+			       MXT_CMD_T6_DELTAS_MODE);
+}
+
+ssize_t refs_read(struct file *file, char *buf, size_t count,
+			loff_t *ppos)
+{
+	return debug_data_read(file->private_data, buf, count, ppos,
+			       MXT_CMD_T6_REFERENCES_MODE);
+}
+
+int debug_data_open(struct inode *inode, struct file *file)
+{
+	struct mxt_data *mxt;
+	int i;
+	mxt = inode->i_private;
+	if (mxt == NULL)
+		return -EIO;
+	mxt->current_debug_datap = 0;
+	mxt->debug_data = kmalloc(mxt->device_info.num_nodes * sizeof(u16),
+				  GFP_KERNEL);
+	if (mxt->debug_data == NULL)
+		return -ENOMEM;
+	for (i = 0; i < mxt->device_info.num_nodes; i++)
+		mxt->debug_data[i] = 7777;
+	file->private_data = mxt;
+	return 0;
+}
+
+int debug_data_release(struct inode *inode, struct file *file)
+{
+	struct mxt_data *mxt;
+	mxt = file->private_data;
+	kfree(mxt->debug_data);
+	return 0;
+}
+
+static const struct file_operations delta_fops = {
+	.owner = THIS_MODULE,
+	.open = debug_data_open,
+	.release = debug_data_release,
+	.read = deltas_read,
+};
+
+static const struct file_operations refs_fops = {
+	.owner = THIS_MODULE,
+	.open = debug_data_open,
+	.release = debug_data_release,
+	.read = refs_read,
+};
+
+
+int mxt_memory_open(struct inode *inode, struct file *file)
+{
+	struct mxt_data *mxt;
+	mxt = container_of(inode->i_cdev, struct mxt_data, cdev);
+	if (mxt == NULL)
+		return -EIO;
+	file->private_data = mxt;
+	return 0;
+}
+
+int mxt_message_open(struct inode *inode, struct file *file)
+{
+	struct mxt_data *mxt;
+	mxt = container_of(inode->i_cdev, struct mxt_data, cdev_messages);
+	if (mxt == NULL)
+		return -EIO;
+	file->private_data = mxt;
+	return 0;
+}
+
+ssize_t mxt_memory_read(struct file *file, char *buf, size_t count,
+			loff_t *ppos)
+{
+	int i;
+	struct mxt_data *mxt;
+
+	mxt = file->private_data;
+	if (mxt->valid_ap) {
+		mxt_debug(DEBUG_TRACE, "Reading %d bytes from current ap\n",
+			(int) count);
+		i = mxt_read_block_wo_addr(mxt->client, count, (u8 *) buf);
+	} else {
+		mxt_debug(DEBUG_TRACE, "Address pointer changed since set;"
+			"writing AP (%d) before reading %d bytes",
+			mxt->address_pointer, (int) count);
+		i = mxt_read_block(mxt->client, mxt->address_pointer, count,
+				buf);
+	}
+
+	return i;
+}
+
+ssize_t mxt_memory_write(struct file *file, const char *buf, size_t count,
+			 loff_t *ppos)
+{
+	int i;
+	int whole_blocks;
+	int last_block_size;
+	struct mxt_data *mxt;
+	u16 address;
+
+	mxt = file->private_data;
+	address = mxt->address_pointer;
+
+	mxt_debug(DEBUG_TRACE, "mxt_memory_write entered\n");
+	whole_blocks = count / I2C_PAYLOAD_SIZE;
+	last_block_size = count % I2C_PAYLOAD_SIZE;
+
+	for (i = 0; i < whole_blocks; i++) {
+		mxt_debug(DEBUG_TRACE, "About to write to %d...",
+			address);
+		mxt_write_block(mxt->client, address, I2C_PAYLOAD_SIZE,
+				(u8 *) buf);
+		address += I2C_PAYLOAD_SIZE;
+		buf += I2C_PAYLOAD_SIZE;
+	}
+
+	mxt_write_block(mxt->client, address, last_block_size, (u8 *) buf);
+
+	return count;
+}
+
+static int mxt_ioctl(struct inode *inode, struct file *file,
+		     unsigned int cmd, unsigned long arg)
+{
+	int retval;
+	struct mxt_data *mxt;
+
+	retval = 0;
+	mxt = file->private_data;
+
+	switch (cmd) {
+	case MXT_SET_ADDRESS_IOCTL:
+		retval = mxt_write_ap(mxt, (u16) arg);
+		if (retval >= 0) {
+			mxt->address_pointer = (u16) arg;
+			mxt->valid_ap = 1;
+		}
+		break;
+	case MXT_RESET_IOCTL:
+		retval = mxt_write_byte(mxt->client,
+			      MXT_BASE_ADDR(MXT_GEN_COMMANDPROCESSOR_T6, mxt) +
+			      MXT_ADR_T6_RESET,
+			      1);
+		break;
+	case MXT_CALIBRATE_IOCTL:
+		retval = mxt_write_byte(mxt->client,
+			      MXT_BASE_ADDR(MXT_GEN_COMMANDPROCESSOR_T6, mxt) +
+			      MXT_ADR_T6_CALIBRATE,
+			      1);
+
+		break;
+	case MXT_BACKUP_IOCTL:
+		retval = mxt_write_byte(mxt->client,
+			      MXT_BASE_ADDR(MXT_GEN_COMMANDPROCESSOR_T6, mxt) +
+			      MXT_ADR_T6_BACKUPNV,
+			      MXT_CMD_T6_BACKUP);
+		break;
+	case MXT_NONTOUCH_MSG_IOCTL:
+		mxt->nontouch_msg_only = 1;
+		break;
+	case MXT_ALL_MSG_IOCTL:
+		mxt->nontouch_msg_only = 0;
+		break;
+	default:
+		return -EIO;
+	}
+
+	return retval;
+}
+
+/*
+ * Copies messages from buffer to user space.
+ *
+ * NOTE: if less than (mxt->message_size * 5 + 1) bytes requested,
+ * this will return 0!
+ *
+ */
+ssize_t mxt_message_read(struct file *file, char *buf, size_t count,
+			 loff_t *ppos)
+{
+	int i;
+	struct mxt_data *mxt;
+	char *buf_start;
+
+	mxt = file->private_data;
+	if (mxt == NULL)
+		return -EIO;
+	buf_start = buf;
+
+	mutex_lock(&mxt->msg_mutex);
+	/* Copy messages until buffer empty, or 'count' bytes written */
+	while ((mxt->msg_buffer_startp != mxt->msg_buffer_endp) &&
+		((buf - buf_start) < (count - (5 * mxt->message_size) - 1))) {
+
+		for (i = 0; i < mxt->message_size; i++) {
+			buf += sprintf(buf, "[%2X] ",
+				*(mxt->messages + mxt->msg_buffer_endp *
+					mxt->message_size + i));
+		}
+		buf += sprintf(buf, "\n");
+		if (mxt->msg_buffer_endp < MXT_MESSAGE_BUFFER_SIZE)
+			mxt->msg_buffer_endp++;
+		else
+			mxt->msg_buffer_endp = 0;
+	}
+	mutex_unlock(&mxt->msg_mutex);
+	return buf - buf_start;
+}
+
+static const struct file_operations mxt_message_fops = {
+	.owner = THIS_MODULE,
+	.open = mxt_message_open,
+	.read = mxt_message_read,
+};
+
+static const struct file_operations mxt_memory_fops = {
+	.owner = THIS_MODULE,
+	.open = mxt_memory_open,
+	.read = mxt_memory_read,
+	.write = mxt_memory_write,
+	.unlocked_ioctl = mxt_ioctl,
+};
+
+
+/* Writes the address pointer (to set up following reads). */
+
+int mxt_write_ap(struct mxt_data *mxt, u16 ap)
+{
+	struct i2c_client *client;
+	__le16	le_ap = cpu_to_le16(ap);
+	client = mxt->client;
+	if (mxt != NULL)
+		mxt->last_read_addr = -1;
+	if (i2c_master_send(client, (u8 *) &le_ap, 2) == 2) {
+		mxt_debug(DEBUG_TRACE, "Address pointer set to %d\n", ap);
+		return 0;
+	} else {
+		mxt_debug(DEBUG_INFO, "Error writing address pointer!\n");
+		return -EIO;
+	}
+}
+
+
+
+/* Calculates the 24-bit CRC sum. */
+static u32 CRC_24(u32 crc, u8 byte1, u8 byte2)
+{
+	static const u32 crcpoly = 0x80001B;
+	u32 result;
+	u32 data_word;
+
+	data_word = ((((u16) byte2) << 8u) | byte1);
+	result = ((crc << 1u) ^ data_word);
+	if (result & 0x1000000)
+		result ^= crcpoly;
+	return result;
+}
+
+/* Returns object address in mXT chip, or zero if object is not found */
+static u16 get_object_address(uint8_t object_type,
+			      uint8_t instance,
+			      struct mxt_object *object_table,
+			      int max_objs)
+{
+	uint8_t object_table_index = 0;
+	uint8_t address_found = 0;
+	uint16_t address = 0;
+	struct mxt_object *obj;
+
+	while ((object_table_index < max_objs) && !address_found) {
+		obj = &object_table[object_table_index];
+		if (obj->type == object_type) {
+			address_found = 1;
+			/* Are there enough instances defined in the FW? */
+			if (obj->instances >= instance) {
+				address = obj->chip_addr +
+					  (obj->size + 1) * instance;
+			} else {
+				return 0;
+			}
+		}
+		object_table_index++;
+	}
+	return address;
+}
+
+
+/*
+ * Reads a block of bytes from given address from mXT chip. If we are
+ * reading from message window, and previous read was from message window,
+ * there's no need to write the address pointer: the mXT chip will
+ * automatically set the address pointer back to message window start.
+ */
+
+static int mxt_read_block(struct i2c_client *client,
+		   u16 addr,
+		   u16 length,
+		   u8 *value)
+{
+	struct i2c_adapter *adapter = client->adapter;
+	struct i2c_msg msg[2];
+	__le16	le_addr;
+	struct mxt_data *mxt;
+
+	mxt = i2c_get_clientdata(client);
+
+	if (mxt != NULL) {
+		if ((mxt->last_read_addr == addr) &&
+			(addr == mxt->msg_proc_addr)) {
+			if  (i2c_master_recv(client, value, length) == length)
+				return length;
+			else
+				return -EIO;
+		} else {
+			mxt->last_read_addr = addr;
+		}
+	}
+
+	mxt_debug(DEBUG_TRACE, "Writing address pointer & reading %d bytes "
+		"in on i2c transaction...\n", length);
+
+	le_addr = cpu_to_le16(addr);
+	msg[0].addr  = client->addr;
+	msg[0].flags = 0x00;
+	msg[0].len   = 2;
+	msg[0].buf   = (u8 *) &le_addr;
+
+	msg[1].addr  = client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len   = length;
+	msg[1].buf   = (u8 *) value;
+	if  (i2c_transfer(adapter, msg, 2) == 2)
+		return length;
+	else
+		return -EIO;
+
+}
+
+/* Reads a block of bytes from current address from mXT chip. */
+
+static int mxt_read_block_wo_addr(struct i2c_client *client,
+			   u16 length,
+			   u8 *value)
+{
+
+
+	if  (i2c_master_recv(client, value, length) == length) {
+		mxt_debug(DEBUG_TRACE, "I2C block read ok\n");
+		return length;
+	} else {
+		mxt_debug(DEBUG_INFO, "I2C block read failed\n");
+		return -EIO;
+	}
+
+}
+
+
+/* Writes one byte to given address in mXT chip. */
+
+static int mxt_write_byte(struct i2c_client *client, u16 addr, u8 value)
+{
+	struct {
+		__le16 le_addr;
+		u8 data;
+
+	} i2c_byte_transfer;
+
+	struct mxt_data *mxt;
+
+	mxt = i2c_get_clientdata(client);
+	if (mxt != NULL)
+		mxt->last_read_addr = -1;
+	i2c_byte_transfer.le_addr = cpu_to_le16(addr);
+	i2c_byte_transfer.data = value;
+	if  (i2c_master_send(client, (u8 *) &i2c_byte_transfer, 3) == 3)
+		return 0;
+	else
+		return -EIO;
+}
+
+
+/* Writes a block of bytes (max 256) to given address in mXT chip. */
+static int mxt_write_block(struct i2c_client *client,
+		    u16 addr,
+		    u16 length,
+		    u8 *value)
+{
+	int i;
+	struct {
+		__le16	le_addr;
+		u8	data[256];
+
+	} i2c_block_transfer;
+
+	struct mxt_data *mxt;
+
+	mxt_debug(DEBUG_TRACE, "Writing %d bytes to %d...", length, addr);
+	if (length > 256)
+		return -EINVAL;
+	mxt = i2c_get_clientdata(client);
+	if (mxt != NULL)
+		mxt->last_read_addr = -1;
+	for (i = 0; i < length; i++)
+		i2c_block_transfer.data[i] = *value++;
+	i2c_block_transfer.le_addr = cpu_to_le16(addr);
+	i = i2c_master_send(client, (u8 *) &i2c_block_transfer, length + 2);
+	if (i == (length + 2))
+		return length;
+	else
+		return -EIO;
+}
+
+/* Calculates the CRC value for mXT infoblock. */
+int calculate_infoblock_crc(u32 *crc_result, u8 *data, int crc_area_size)
+{
+	u32 crc = 0;
+	int i;
+
+	for (i = 0; i < (crc_area_size - 1); i = i + 2)
+		crc = CRC_24(crc, *(data + i), *(data + i + 1));
+	/* If uneven size, pad with zero */
+	if (crc_area_size & 0x0001)
+		crc = CRC_24(crc, *(data + i), 0);
+	/* Return only 24 bits of CRC. */
+	*crc_result = (crc & 0x00FFFFFF);
+
+	return 0;
+}
+
+/* Processes a touchscreen message. */
+void process_T9_message(u8 *message, struct mxt_data *mxt, int last_touch)
+{
+
+	struct	input_dev *input;
+	u8  status;
+	u16 xpos = 0xFFFF;
+	u16 ypos = 0xFFFF;
+	u8  touch_size = 255;
+	u8  touch_number;
+	u8  amplitude;
+	u8  report_id;
+
+	static int stored_size[MXT_MAX_NUM_TOUCHES];
+	static int stored_x[MXT_MAX_NUM_TOUCHES];
+	static int stored_y[MXT_MAX_NUM_TOUCHES];
+	int i;
+	int active_touches = 0;
+	/*
+	 * If the 'last_touch' flag is set, we have received all the touch
+	 * messages there are available in this cycle, so send the events for
+	 * touches that are active.
+	 */
+	if (last_touch) {
+		/*
+		 * For compatibility with single-touch systems, send also
+		 * ABS_X & ABS_Y & BTN_TOUCH events.
+		 */
+		if (stored_size[0]) {
+			input_report_abs(mxt->input, BTN_TOUCH, 1);
+			input_report_abs(mxt->input, ABS_X, stored_x[0]);
+			input_report_abs(mxt->input, ABS_Y, stored_y[0]);
+		} else {
+			input_report_abs(mxt->input, BTN_TOUCH, 0);
+		}
+
+		for (i = 0; i < mxt->numtouch; i++) {
+			if (stored_size[i]) {
+				active_touches++;
+				input_report_abs(mxt->input,
+						ABS_MT_TRACKING_ID,
+						i);
+				input_report_abs(mxt->input,
+						ABS_MT_TOUCH_MAJOR,
+						stored_size[i]);
+				input_report_abs(mxt->input,
+						ABS_MT_POSITION_X,
+						stored_x[i]);
+				input_report_abs(mxt->input,
+						ABS_MT_POSITION_Y,
+						stored_y[i]);
+				input_mt_sync(mxt->input);
+			}
+		}
+		if (active_touches == 0)
+			input_mt_sync(mxt->input);
+		input_sync(mxt->input);
+	} else {
+		input = mxt->input;
+		status = message[MXT_MSG_T9_STATUS];
+		report_id = message[0];
+
+		if (status & MXT_MSGB_T9_SUPPRESS) {
+			/* Touch has been suppressed by grip/face */
+			/* detection                              */
+			mxt_debug(DEBUG_TRACE, "SUPRESS");
+		} else {
+			/* Put together the 10-/12-bit coordinate values. */
+			xpos = message[MXT_MSG_T9_XPOSMSB] * 16 +
+				((message[MXT_MSG_T9_XYPOSLSB] >> 4) & 0xF);
+			ypos = message[MXT_MSG_T9_YPOSMSB] * 16 +
+				((message[MXT_MSG_T9_XYPOSLSB] >> 0) & 0xF);
+
+			if (mxt->max_x_val < 1024)
+				xpos >>= 2;
+			if (mxt->max_y_val < 1024)
+				ypos >>= 2;
+
+			touch_number = message[MXT_MSG_REPORTID] -
+				mxt->rid_map[report_id].first_rid;
+
+			stored_x[touch_number] = xpos;
+			stored_y[touch_number] = ypos;
+
+			if (status & MXT_MSGB_T9_DETECT) {
+				/*
+				 * mXT224 reports the number of touched nodes,
+				 * so the exact value for touch ellipse major
+				 * axis length in nodes would be
+				 * 2*sqrt(touch_size/pi)
+				 * (assuming round touch shape), which would
+				 * then need to be scaled using information
+				 * about how many sensor lines we do have. So
+				 * this is very much simplified,
+				 * but sufficient for most if not all apps?
+				 */
+				touch_size = message[MXT_MSG_T9_TCHAREA];
+				touch_size = touch_size >> 2;
+				if (!touch_size)
+					touch_size = 1;
+				stored_size[touch_number] = touch_size;
+				if (status & MXT_MSGB_T9_AMP)
+					/* Amplitude of touch has changed */
+					amplitude =
+					    message[MXT_MSG_T9_TCHAMPLITUDE];
+			}
+
+			if (status & MXT_MSGB_T9_RELEASE) {
+				/* The previously reported touch has been
+				 * removed. */
+				stored_size[touch_number] = 0;
+			}
+		}
+
+		if (status & MXT_MSGB_T9_SUPPRESS) {
+			mxt_debug(DEBUG_TRACE, "SUPRESS");
+		} else {
+			if (status & MXT_MSGB_T9_DETECT) {
+				mxt_debug(DEBUG_TRACE, "DETECT:%s%s%s%s",
+					((status & MXT_MSGB_T9_PRESS) ?
+						" PRESS" : ""),
+					((status & MXT_MSGB_T9_MOVE) ?
+						" MOVE" : ""),
+					((status & MXT_MSGB_T9_AMP) ?
+						" AMP" : ""),
+					((status & MXT_MSGB_T9_VECTOR) ?
+						" VECT" : ""));
+			} else if (status & MXT_MSGB_T9_RELEASE) {
+				mxt_debug(DEBUG_TRACE, "RELEASE");
+			}
+		}
+		mxt_debug(DEBUG_TRACE, "X=%d, Y=%d, TOUCHSIZE=%d",
+			xpos, ypos, touch_size);
+	}
+	return;
+}
+
+
+int process_message(u8 *message, u8 object, struct mxt_data *mxt)
+{
+	struct i2c_client *client;
+	u8  status;
+	u16 xpos = 0xFFFF;
+	u16 ypos = 0xFFFF;
+	u8  event;
+	u8  direction;
+	u16 distance;
+	u8  length;
+	u8  report_id;
+	static u8 error_cond;
+
+	client = mxt->client;
+	length = mxt->message_size;
+	report_id = message[0];
+
+	if ((mxt->nontouch_msg_only == 0) ||
+	    (!IS_TOUCH_OBJECT(object))) {
+		mutex_lock(&mxt->msg_mutex);
+		/* Copy the message to buffer */
+		if (mxt->msg_buffer_startp < MXT_MESSAGE_BUFFER_SIZE)
+			mxt->msg_buffer_startp++;
+		else
+			mxt->msg_buffer_startp = 0;
+		if (mxt->msg_buffer_startp == mxt->msg_buffer_endp) {
+			mxt_debug(DEBUG_TRACE,
+				  "Message buf full, discarding last entry.\n");
+			if (mxt->msg_buffer_endp < MXT_MESSAGE_BUFFER_SIZE)
+				mxt->msg_buffer_endp++;
+			else
+				mxt->msg_buffer_endp = 0;
+		}
+		memcpy((mxt->messages + mxt->msg_buffer_startp * length),
+		       message,
+		       length);
+		mutex_unlock(&mxt->msg_mutex);
+	}
+
+	switch (object) {
+	case MXT_GEN_COMMANDPROCESSOR_T6:
+		status = message[1];
+
+		if (status & MXT_MSGB_T6_COMSERR) {
+			if (!(error_cond & MXT_MSGB_T6_COMSERR)) {
+				dev_err(&client->dev,
+					"maXTouch checksum error\n");
+				error_cond |= MXT_MSGB_T6_COMSERR;
+			}
+		}
+		if (status & MXT_MSGB_T6_CFGERR) {
+			/*
+			 * Configuration error. A proper configuration
+			 * needs to be written to chip and backed up.
+			 */
+			if (!(error_cond & MXT_MSGB_T6_CFGERR)) {
+				dev_err(&client->dev,
+					"maXTouch configuration error\n");
+				error_cond |= MXT_MSGB_T6_CFGERR;
+			}
+		}
+		if (status & MXT_MSGB_T6_CAL) {
+			/* Calibration in action, no need to react */
+			dev_info(&client->dev,
+				"maXTouch calibration in progress\n");
+		}
+		if (status & MXT_MSGB_T6_SIGERR) {
+			/*
+			 * Signal acquisition error, something is seriously
+			 * wrong, not much we can in the driver to correct
+			 * this
+			 */
+			if (!(error_cond & MXT_MSGB_T6_SIGERR)) {
+				dev_err(&client->dev,
+					"maXTouch acquisition error\n");
+				error_cond |= MXT_MSGB_T6_SIGERR;
+			}
+		}
+		if (status & MXT_MSGB_T6_OFL) {
+			/*
+			 * Cycle overflow, the acquisition interval is too
+			 * short.
+			 */
+			dev_err(&client->dev,
+				"maXTouch cycle overflow\n");
+		}
+		if (status & MXT_MSGB_T6_RESET) {
+			/* Chip has reseted, no need to react. */
+			dev_info(&client->dev,
+				"maXTouch chip reset\n");
+		}
+		if (status == 0) {
+			/* Chip status back to normal. */
+			dev_info(&client->dev,
+				"maXTouch status normal\n");
+			error_cond = 0;
+		}
+		break;
+
+	case MXT_TOUCH_MULTITOUCHSCREEN_T9:
+		process_T9_message(message, mxt, 0);
+		break;
+
+	case MXT_SPT_GPIOPWM_T19:
+		if (debug >= DEBUG_TRACE)
+			dev_info(&client->dev,
+				"Receiving GPIO message\n");
+		break;
+
+	case MXT_PROCI_GRIPFACESUPPRESSION_T20:
+		if (debug >= DEBUG_TRACE)
+			dev_info(&client->dev,
+				"Receiving face suppression msg\n");
+		break;
+
+	case MXT_PROCG_NOISESUPPRESSION_T22:
+		if (debug >= DEBUG_TRACE)
+			dev_info(&client->dev,
+				"Receiving noise suppression msg\n");
+		status = message[MXT_MSG_T22_STATUS];
+		if (status & MXT_MSGB_T22_FHCHG) {
+			if (debug >= DEBUG_TRACE)
+				dev_info(&client->dev,
+					"maXTouch: Freq changed\n");
+		}
+		if (status & MXT_MSGB_T22_GCAFERR) {
+			if (debug >= DEBUG_TRACE)
+				dev_info(&client->dev,
+					"maXTouch: High noise "
+					"level\n");
+		}
+		if (status & MXT_MSGB_T22_FHERR) {
+			if (debug >= DEBUG_TRACE)
+				dev_info(&client->dev,
+					"maXTouch: Freq changed - "
+					"Noise level too high\n");
+		}
+		break;
+
+	case MXT_PROCI_ONETOUCHGESTUREPROCESSOR_T24:
+		if (debug >= DEBUG_TRACE)
+			dev_info(&client->dev,
+				"Receiving one-touch gesture msg\n");
+
+		event = message[MXT_MSG_T24_STATUS] & 0x0F;
+		xpos = message[MXT_MSG_T24_XPOSMSB] * 16 +
+			((message[MXT_MSG_T24_XYPOSLSB] >> 4) & 0x0F);
+		ypos = message[MXT_MSG_T24_YPOSMSB] * 16 +
+			((message[MXT_MSG_T24_XYPOSLSB] >> 0) & 0x0F);
+		if (mxt->max_x_val < 1024)
+			xpos >>= 2;
+		if (mxt->max_y_val < 1024)
+			ypos >>= 2;
+		direction = message[MXT_MSG_T24_DIR];
+		distance = message[MXT_MSG_T24_DIST] +
+			   (message[MXT_MSG_T24_DIST + 1] << 16);
+
+		report_gesture((event << 24) | (direction << 16) | distance,
+			mxt);
+		report_gesture((xpos << 16) | ypos, mxt);
+
+		break;
+
+	case MXT_SPT_SELFTEST_T25:
+		if (debug >= DEBUG_TRACE)
+			dev_info(&client->dev,
+				"Receiving Self-Test msg\n");
+
+		if (message[MXT_MSG_T25_STATUS] == MXT_MSGR_T25_OK) {
+			if (debug >= DEBUG_TRACE)
+				dev_info(&client->dev,
+					"maXTouch: Self-Test OK\n");
+
+		} else  {
+			dev_err(&client->dev,
+				"maXTouch: Self-Test Failed [%02x]:"
+				"{%02x,%02x,%02x,%02x,%02x}\n",
+				message[MXT_MSG_T25_STATUS],
+				message[MXT_MSG_T25_STATUS + 0],
+				message[MXT_MSG_T25_STATUS + 1],
+				message[MXT_MSG_T25_STATUS + 2],
+				message[MXT_MSG_T25_STATUS + 3],
+				message[MXT_MSG_T25_STATUS + 4]
+				);
+		}
+		break;
+
+	case MXT_PROCI_TWOTOUCHGESTUREPROCESSOR_T27:
+		if (debug >= DEBUG_TRACE)
+			dev_info(&client->dev,
+				"Receiving 2-touch gesture message\n");
+
+		event = message[MXT_MSG_T27_STATUS] & 0xF0;
+		xpos = message[MXT_MSG_T27_XPOSMSB] * 16 +
+			((message[MXT_MSG_T27_XYPOSLSB] >> 4) & 0x0F);
+		ypos = message[MXT_MSG_T27_YPOSMSB] * 16 +
+			((message[MXT_MSG_T27_XYPOSLSB] >> 0) & 0x0F);
+		if (mxt->max_x_val < 1024)
+			xpos >>= 2;
+		if (mxt->max_y_val < 1024)
+			ypos >>= 2;
+
+		direction = message[MXT_MSG_T27_ANGLE];
+		distance = message[MXT_MSG_T27_SEPARATION] +
+			   (message[MXT_MSG_T27_SEPARATION + 1] << 16);
+
+		report_gesture((event << 24) | (direction << 16) | distance,
+			mxt);
+		report_gesture((xpos << 16) | ypos, mxt);
+
+
+		break;
+
+	case MXT_SPT_CTECONFIG_T28:
+		if (debug >= DEBUG_TRACE)
+			dev_info(&client->dev,
+				"Receiving CTE message...\n");
+		status = message[MXT_MSG_T28_STATUS];
+		if (status & MXT_MSGB_T28_CHKERR)
+			dev_err(&client->dev,
+				"maXTouch: Power-Up CRC failure\n");
+
+		break;
+	default:
+		if (debug >= DEBUG_TRACE)
+			dev_info(&client->dev,
+				"maXTouch: Unknown message!\n");
+
+		break;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Processes messages when the interrupt line (CHG) is asserted. Keeps
+ * reading messages until a message with report ID 0xFF is received,
+ * which indicates that there is no more new messages.
+ *
+ */
+
+static void mxt_worker(struct work_struct *work)
+{
+	struct	mxt_data *mxt;
+	struct	i2c_client *client;
+
+	u8	*message;
+	u16	message_length;
+	u16	message_addr;
+	u8	report_id;
+	u8	object;
+	int	error;
+	int	i;
+	char    *message_string;
+	char    *message_start;
+
+	message = NULL;
+	mxt = container_of(work, struct mxt_data, dwork.work);
+	disable_irq(mxt->irq);
+	client = mxt->client;
+	message_addr = mxt->msg_proc_addr;
+	message_length = mxt->message_size;
+
+	if (message_length < 256) {
+		message = kmalloc(message_length, GFP_KERNEL);
+		if (message == NULL) {
+			dev_err(&client->dev, "Error allocating memory\n");
+			return;
+		}
+	} else {
+		dev_err(&client->dev,
+			"Message length larger than 256 bytes not supported\n");
+		return;
+	}
+
+	mxt_debug("maXTouch worker active:\n");
+
+	do {
+		/* Read next message, reread on failure. */
+		/* This driver currently does not request messages with CRC
+		 * byte, so we can read one byte less than message_length. */
+		mxt->message_counter++;
+		for (i = 1; i < I2C_RETRY_COUNT; i++) {
+			error = mxt_read_block(client,
+					       message_addr,
+					       message_length - 1,
+					       message);
+			if (error >= 0)
+				break;
+			mxt->read_fail_counter++;
+			dev_err(&client->dev,
+				"Failure reading maxTouch device\n");
+		}
+		if (error < 0) {
+			kfree(message);
+			return;
+		}
+
+		if (mxt->address_pointer != message_addr)
+			mxt->valid_ap = 0;
+		report_id = message[0];
+
+		if (debug >= DEBUG_RAW) {
+			mxt_debug(DEBUG_RAW, "%s message [msg count: %08x]:",
+				  REPORT_ID_TO_OBJECT_NAME(report_id, mxt),
+				  mxt->message_counter
+			);
+			/* 5 characters per one byte */
+			message_string = kmalloc(message_length * 5,
+						 GFP_KERNEL);
+			if (message_string == NULL) {
+				dev_err(&client->dev,
+					"Error allocating memory\n");
+				kfree(message);
+				return;
+			}
+			message_start = message_string;
+			for (i = 0; i < message_length; i++) {
+				message_string +=
+					sprintf(message_string,
+						"0x%02X ", message[i]);
+			}
+			mxt_debug(DEBUG_RAW, "%s", message_start);
+			kfree(message_start);
+		}
+
+		if ((report_id != MXT_END_OF_MESSAGES) && (report_id != 0)) {
+			memcpy(mxt->last_message, message, message_length);
+			mxt->new_msgs = 1;
+			smp_wmb();
+			/* Get type of object and process the message */
+			object = mxt->rid_map[report_id].object;
+			process_message(message, object, mxt);
+		}
+
+		mxt_debug(DEBUG_TRACE, "chgline: %d\n", mxt->read_chg());
+	} while (comms ? (mxt->read_chg() == 0) :
+		((report_id != MXT_END_OF_MESSAGES) && (report_id != 0)));
+
+	/* All messages processed, send the events) */
+	process_T9_message(NULL, mxt, 1);
+
+	kfree(message);
+	enable_irq(mxt->irq);
+	/* Make sure we just didn't miss a interrupt. */
+	if (mxt->read_chg() == 0)
+		schedule_delayed_work(&mxt->dwork, 0);
+}
+
+
+/*
+ * The maXTouch device will signal the host about a new message by asserting
+ * the CHG line. This ISR schedules a worker routine to read the message when
+ * that happens.
+ */
+
+static irqreturn_t mxt_irq_handler(int irq, void *_mxt)
+{
+	struct mxt_data *mxt = _mxt;
+
+	mxt->irq_counter++;
+	if (mxt->valid_interrupt()) {
+		/* Send the signal only if falling edge generated the irq. */
+		cancel_delayed_work(&mxt->dwork);
+		schedule_delayed_work(&mxt->dwork, 0);
+		mxt->valid_irq_counter++;
+	} else {
+		mxt->invalid_irq_counter++;
+		return IRQ_NONE;
+	}
+
+	return IRQ_HANDLED;
+}
+
+
+
+/******************************************************************************/
+/* Initialization of driver                                                   */
+/******************************************************************************/
+
+static int __devinit mxt_identify(struct i2c_client *client,
+				  struct mxt_data *mxt,
+				  u8 *id_block_data)
+{
+	u8 buf[MXT_ID_BLOCK_SIZE];
+	int error;
+	int identified;
+
+	identified = 0;
+
+	/* Read Device info to check if chip is valid */
+	error = mxt_read_block(client, MXT_ADDR_INFO_BLOCK, MXT_ID_BLOCK_SIZE,
+			       (u8 *) buf);
+
+	if (error < 0) {
+		mxt->read_fail_counter++;
+		dev_err(&client->dev, "Failure accessing maXTouch device\n");
+		return -EIO;
+	}
+
+	memcpy(id_block_data, buf, MXT_ID_BLOCK_SIZE);
+
+	mxt->device_info.family_id  = buf[0];
+	mxt->device_info.variant_id = buf[1];
+	mxt->device_info.major	    = ((buf[2] >> 4) & 0x0F);
+	mxt->device_info.minor      = (buf[2] & 0x0F);
+	mxt->device_info.build	    = buf[3];
+	mxt->device_info.x_size	    = buf[4];
+	mxt->device_info.y_size	    = buf[5];
+	mxt->device_info.num_objs   = buf[6];
+	mxt->device_info.num_nodes  = mxt->device_info.x_size *
+				      mxt->device_info.y_size;
+
+	/*
+	 * Check Family & Variant Info; warn if not recognized but
+	 * still continue.
+	 */
+
+	/* MXT224 */
+	if (mxt->device_info.family_id == MXT224_FAMILYID) {
+		strcpy(mxt->device_info.family_name, "mXT224");
+
+		if (mxt->device_info.variant_id == MXT224_CAL_VARIANTID) {
+			strcpy(mxt->device_info.variant_name, "Calibrated");
+		} else if (mxt->device_info.variant_id ==
+			MXT224_UNCAL_VARIANTID) {
+			strcpy(mxt->device_info.variant_name, "Uncalibrated");
+		} else {
+			dev_err(&client->dev,
+				"Warning: maXTouch Variant ID [%d] not "
+				"supported\n",
+				mxt->device_info.variant_id);
+			strcpy(mxt->device_info.variant_name, "UNKNOWN");
+			/* identified = -ENXIO; */
+		}
+
+	/* MXT1386 */
+	} else if (mxt->device_info.family_id == MXT1386_FAMILYID) {
+		strcpy(mxt->device_info.family_name, "mXT1386");
+
+		if (mxt->device_info.variant_id == MXT1386_CAL_VARIANTID) {
+			strcpy(mxt->device_info.variant_name, "Calibrated");
+		} else {
+			dev_err(&client->dev,
+				"Warning: maXTouch Variant ID [%d] not "
+				"supported\n",
+				mxt->device_info.variant_id);
+			strcpy(mxt->device_info.variant_name, "UNKNOWN");
+			/* identified = -ENXIO; */
+		}
+	/* Unknown family ID! */
+	} else {
+		dev_err(&client->dev,
+			"Warning: maXTouch Family ID [%d] not supported\n",
+			mxt->device_info.family_id);
+		strcpy(mxt->device_info.family_name, "UNKNOWN");
+		strcpy(mxt->device_info.variant_name, "UNKNOWN");
+		/* identified = -ENXIO; */
+	}
+
+	dev_info(
+		&client->dev,
+		"Atmel maXTouch (Family %s (%X), Variant %s (%X)) Firmware "
+		"version [%d.%d] Build %d\n",
+		mxt->device_info.family_name,
+		mxt->device_info.family_id,
+		mxt->device_info.variant_name,
+		mxt->device_info.variant_id,
+		mxt->device_info.major,
+		mxt->device_info.minor,
+		mxt->device_info.build
+	);
+	dev_info(
+		&client->dev,
+		"Atmel maXTouch Configuration "
+		"[X: %d] x [Y: %d]\n",
+		mxt->device_info.x_size,
+		mxt->device_info.y_size
+	);
+	return identified;
+}
+
+/*
+ * Reads the object table from maXTouch chip to get object data like
+ * address, size, report id. For Info Block CRC calculation, already read
+ * id data is passed to this function too (Info Block consists of the ID
+ * block and object table).
+ *
+ */
+static int __devinit mxt_read_object_table(struct i2c_client *client,
+					   struct mxt_data *mxt,
+					   u8 *raw_id_data)
+{
+	u16	report_id_count;
+	u8	buf[MXT_OBJECT_TABLE_ELEMENT_SIZE];
+	u8      *raw_ib_data;
+	u8	object_type;
+	u16	object_address;
+	u16	object_size;
+	u8	object_instances;
+	u8	object_report_ids;
+	u16	object_info_address;
+	u32	crc;
+	u32     calculated_crc;
+	int	i;
+	int	error;
+
+	u8	object_instance;
+	u8	object_report_id;
+	u8	report_id;
+	int     first_report_id;
+	int     ib_pointer;
+	struct mxt_object *object_table;
+
+	mxt_debug(DEBUG_TRACE, "maXTouch driver reading configuration\n");
+
+	object_table = kzalloc(sizeof(struct mxt_object) *
+			       mxt->device_info.num_objs,
+			       GFP_KERNEL);
+	if (object_table == NULL) {
+		printk(KERN_WARNING "maXTouch: Memory allocation failed!\n");
+		error = -ENOMEM;
+		goto err_object_table_alloc;
+	}
+
+	raw_ib_data = kmalloc(MXT_OBJECT_TABLE_ELEMENT_SIZE *
+			mxt->device_info.num_objs + MXT_ID_BLOCK_SIZE,
+			GFP_KERNEL);
+	if (raw_ib_data == NULL) {
+		printk(KERN_WARNING "maXTouch: Memory allocation failed!\n");
+		error = -ENOMEM;
+		goto err_ib_alloc;
+	}
+
+	/* Copy the ID data for CRC calculation. */
+	memcpy(raw_ib_data, raw_id_data, MXT_ID_BLOCK_SIZE);
+	ib_pointer = MXT_ID_BLOCK_SIZE;
+
+	mxt->object_table = object_table;
+
+	mxt_debug(DEBUG_TRACE, "maXTouch driver Memory allocated\n");
+
+	object_info_address = MXT_ADDR_OBJECT_TABLE;
+
+	report_id_count = 0;
+	for (i = 0; i < mxt->device_info.num_objs; i++) {
+		mxt_debug(DEBUG_TRACE, "Reading maXTouch at [0x%04x]: ",
+			  object_info_address);
+
+		error = mxt_read_block(client, object_info_address,
+				       MXT_OBJECT_TABLE_ELEMENT_SIZE, buf);
+
+		if (error < 0) {
+			mxt->read_fail_counter++;
+			dev_err(&client->dev,
+				"maXTouch Object %d could not be read\n", i);
+			error = -EIO;
+			goto err_object_read;
+		}
+
+		memcpy(raw_ib_data + ib_pointer, buf,
+		       MXT_OBJECT_TABLE_ELEMENT_SIZE);
+		ib_pointer += MXT_OBJECT_TABLE_ELEMENT_SIZE;
+
+		object_type       =  buf[0];
+		object_address    = (buf[2] << 8) + buf[1];
+		object_size       =  buf[3] + 1;
+		object_instances  =  buf[4] + 1;
+		object_report_ids =  buf[5];
+		mxt_debug(DEBUG_TRACE, "Type=%03d, Address=0x%04x, "
+			  "Size=0x%02x, %d instances, %d report id's\n",
+			  object_type,
+			  object_address,
+			  object_size,
+			  object_instances,
+			  object_report_ids
+		);
+
+		/* TODO: check whether object is known and supported? */
+		/* Save frequently needed info. */
+		if (object_type == MXT_GEN_MESSAGEPROCESSOR_T5) {
+			mxt->msg_proc_addr = object_address;
+			mxt->message_size = object_size;
+		}
+
+		object_table[i].type            = object_type;
+		object_table[i].chip_addr       = object_address;
+		object_table[i].size            = object_size;
+		object_table[i].instances       = object_instances;
+		object_table[i].num_report_ids  = object_report_ids;
+		report_id_count += object_instances * object_report_ids;
+
+		object_info_address += MXT_OBJECT_TABLE_ELEMENT_SIZE;
+	}
+
+	mxt->rid_map =
+		kzalloc(sizeof(struct report_id_map) * (report_id_count + 1),
+			/* allocate for report_id 0, even if not used */
+			GFP_KERNEL);
+	if (mxt->rid_map == NULL) {
+		printk(KERN_WARNING "maXTouch: Can't allocate memory!\n");
+		error = -ENOMEM;
+		goto err_rid_map_alloc;
+	}
+
+	mxt->messages = kzalloc(mxt->message_size * MXT_MESSAGE_BUFFER_SIZE,
+				GFP_KERNEL);
+	if (mxt->messages == NULL) {
+		printk(KERN_WARNING "maXTouch: Can't allocate memory!\n");
+		error = -ENOMEM;
+		goto err_msg_alloc;
+	}
+
+	mxt->last_message = kzalloc(mxt->message_size, GFP_KERNEL);
+	if (mxt->last_message == NULL) {
+		printk(KERN_WARNING "maXTouch: Can't allocate memory!\n");
+		error = -ENOMEM;
+		goto err_msg_alloc;
+	}
+
+	mxt->report_id_count = report_id_count;
+	if (report_id_count > 254) {	/* 0 & 255 are reserved */
+			dev_err(&client->dev,
+				"Too many maXTouch report id's [%d]\n",
+				report_id_count);
+			error = -ENXIO;
+			goto err_max_rid;
+	}
+
+	/* Create a mapping from report id to object type */
+	report_id = 1; /* Start from 1, 0 is reserved. */
+
+	/* Create table associating report id's with objects & instances */
+	for (i = 0; i < mxt->device_info.num_objs; i++) {
+		for (object_instance = 0;
+		     object_instance < object_table[i].instances;
+		     object_instance++){
+			first_report_id = report_id;
+			for (object_report_id = 0;
+			     object_report_id < object_table[i].num_report_ids;
+			     object_report_id++) {
+				mxt->rid_map[report_id].object =
+					object_table[i].type;
+				mxt->rid_map[report_id].instance =
+					object_instance;
+				mxt->rid_map[report_id].first_rid =
+					first_report_id;
+				report_id++;
+			}
+		}
+	}
+
+	/* Read 3 byte CRC */
+	error = mxt_read_block(client, object_info_address, 3, buf);
+	if (error < 0) {
+		mxt->read_fail_counter++;
+		dev_err(&client->dev, "Error reading CRC\n");
+	}
+
+	crc = (buf[2] << 16) | (buf[1] << 8) | buf[0];
+
+	if (calculate_infoblock_crc(&calculated_crc, raw_ib_data,
+				    ib_pointer)) {
+		printk(KERN_WARNING "Error while calculating CRC!\n");
+		calculated_crc = 0;
+	}
+	kfree(raw_ib_data);
+
+	mxt_debug(DEBUG_TRACE, "\nReported info block CRC = 0x%6X\n", crc);
+	mxt_debug(DEBUG_TRACE, "Calculated info block CRC = 0x%6X\n\n",
+		       calculated_crc);
+
+	if (crc == calculated_crc) {
+		mxt->info_block_crc = crc;
+	} else {
+		mxt->info_block_crc = 0;
+		printk(KERN_ALERT "maXTouch: Info block CRC invalid!\n");
+	}
+
+	if (debug >= DEBUG_VERBOSE) {
+
+		dev_info(&client->dev, "maXTouch: %d Objects\n",
+				mxt->device_info.num_objs);
+
+		for (i = 0; i < mxt->device_info.num_objs; i++) {
+			dev_info(&client->dev, "Type:\t\t\t[%d]: %s\n",
+				 object_table[i].type,
+				 object_type_name[object_table[i].type]);
+			dev_info(&client->dev, "\tAddress:\t0x%04X\n",
+				object_table[i].chip_addr);
+			dev_info(&client->dev, "\tSize:\t\t%d Bytes\n",
+				 object_table[i].size);
+			dev_info(&client->dev, "\tInstances:\t%d\n",
+				 object_table[i].instances);
+			dev_info(&client->dev, "\tReport Id's:\t%d\n",
+				 object_table[i].num_report_ids);
+		}
+	}
+
+	return 0;
+
+
+err_max_rid:
+	kfree(mxt->last_message);
+err_msg_alloc:
+	kfree(mxt->rid_map);
+err_rid_map_alloc:
+err_object_read:
+	kfree(raw_ib_data);
+err_ib_alloc:
+	kfree(object_table);
+err_object_table_alloc:
+	return error;
+}
+
+static int __devinit mxt_probe(struct i2c_client *client,
+			       const struct i2c_device_id *id)
+{
+	struct mxt_data          *mxt;
+	struct mxt_platform_data *pdata;
+	struct input_dev         *input;
+	u8 *id_data;
+	int error;
+
+	mxt_debug(DEBUG_INFO, "mXT224: mxt_probe\n");
+
+	if (client == NULL) {
+		pr_debug("maXTouch: client == NULL\n");
+		return	-EINVAL;
+	} else if (client->adapter == NULL) {
+		pr_debug("maXTouch: client->adapter == NULL\n");
+		return	-EINVAL;
+	} else if (&client->dev == NULL) {
+		pr_debug("maXTouch: client->dev == NULL\n");
+		return	-EINVAL;
+	} else if (&client->adapter->dev == NULL) {
+		pr_debug("maXTouch: client->adapter->dev == NULL\n");
+		return	-EINVAL;
+	} else if (id == NULL) {
+		pr_debug("maXTouch: id == NULL\n");
+		return	-EINVAL;
+	}
+
+	mxt_debug(DEBUG_INFO, "maXTouch driver v. %s\n", DRIVER_VERSION);
+	mxt_debug(DEBUG_INFO, "\t \"%s\"\n", client->name);
+	mxt_debug(DEBUG_INFO, "\taddr:\t0x%04x\n", client->addr);
+	mxt_debug(DEBUG_INFO, "\tirq:\t%d\n", client->irq);
+	mxt_debug(DEBUG_INFO, "\tflags:\t0x%04x\n", client->flags);
+	mxt_debug(DEBUG_INFO, "\tadapter:\"%s\"\n", client->adapter->name);
+	mxt_debug(DEBUG_INFO, "\tdevice:\t\"%s\"\n", client->dev.init_name);
+
+	mxt_debug(DEBUG_TRACE, "maXTouch driver functionality OK\n");
+
+	/* Allocate structure - we need it to identify device */
+	mxt = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
+	if (mxt == NULL) {
+		dev_err(&client->dev, "insufficient memory\n");
+		error = -ENOMEM;
+		goto err_mxt_alloc;
+	}
+
+	id_data = kmalloc(MXT_ID_BLOCK_SIZE, GFP_KERNEL);
+	if (id_data == NULL) {
+		dev_err(&client->dev, "insufficient memory\n");
+		error = -ENOMEM;
+		goto err_id_alloc;
+	}
+
+	input = input_allocate_device();
+	if (!input) {
+		dev_err(&client->dev, "error allocating input device\n");
+		error = -ENOMEM;
+		goto err_input_dev_alloc;
+	}
+
+	/* Initialize Platform data */
+
+	pdata = client->dev.platform_data;
+	if (pdata == NULL) {
+		dev_err(&client->dev, "platform data is required!\n");
+		error = -EINVAL;
+		goto err_pdata;
+	}
+	if (debug >= DEBUG_TRACE)
+		printk(KERN_INFO "Platform OK: pdata = 0x%08x\n",
+		       (unsigned int) pdata);
+
+	mxt->read_fail_counter = 0;
+	mxt->message_counter   = 0;
+	mxt->max_x_val         = pdata->max_x;
+	mxt->max_y_val         = pdata->max_y;
+	mxt->numtouch          = pdata->numtouch;
+
+	/* Get data that is defined in board specific code. */
+	mxt->init_hw = pdata->init_platform_hw;
+	mxt->exit_hw = pdata->exit_platform_hw;
+	mxt->read_chg = pdata->read_chg;
+
+	if (pdata->valid_interrupt != NULL)
+		mxt->valid_interrupt = pdata->valid_interrupt;
+	else
+		mxt->valid_interrupt = mxt_valid_interrupt_dummy;
+
+	if (mxt->init_hw != NULL)
+		mxt->init_hw();
+
+	if (debug >= DEBUG_TRACE)
+		printk(KERN_INFO "maXTouch driver identifying chip\n");
+
+	if (mxt_identify(client, mxt, id_data) < 0) {
+		dev_err(&client->dev, "Chip could not be identified\n");
+		error = -ENODEV;
+		goto err_identify;
+	}
+	/* Chip is valid and active. */
+	if (debug >= DEBUG_TRACE)
+		printk(KERN_INFO "maXTouch driver allocating input device\n");
+
+	mxt->client = client;
+	mxt->input  = input;
+
+	INIT_DELAYED_WORK(&mxt->dwork, mxt_worker);
+	mutex_init(&mxt->debug_mutex);
+	mutex_init(&mxt->msg_mutex);
+	mxt_debug(DEBUG_TRACE, "maXTouch driver creating device name\n");
+
+	snprintf(
+		mxt->phys_name,
+		sizeof(mxt->phys_name),
+		"%s/input0",
+		dev_name(&client->dev)
+	);
+	input->name = "Atmel maXTouch Touchscreen controller";
+	input->phys = mxt->phys_name;
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = &client->dev;
+
+	mxt_debug(DEBUG_INFO, "maXTouch name: \"%s\"\n", input->name);
+	mxt_debug(DEBUG_INFO, "maXTouch phys: \"%s\"\n", input->phys);
+	mxt_debug(DEBUG_INFO, "maXTouch driver setting abs parameters\n");
+
+	set_bit(BTN_TOUCH, input->keybit);
+
+	/* Single touch */
+	input_set_abs_params(input, ABS_X, 0, mxt->max_x_val, 0, 0);
+	input_set_abs_params(input, ABS_Y, 0, mxt->max_y_val, 0, 0);
+	input_set_abs_params(input, ABS_PRESSURE, 0, MXT_MAX_REPORTED_PRESSURE,
+			     0, 0);
+	input_set_abs_params(input, ABS_TOOL_WIDTH, 0, MXT_MAX_REPORTED_WIDTH,
+			     0, 0);
+
+	/* Multitouch */
+	input_set_abs_params(input, ABS_MT_POSITION_X, 0, mxt->max_x_val, 0, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, mxt->max_y_val, 0, 0);
+	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, MXT_MAX_TOUCH_SIZE,
+			     0, 0);
+	input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, MXT_MAX_NUM_TOUCHES,
+			     0, 0);
+
+	__set_bit(EV_ABS, input->evbit);
+	__set_bit(EV_SYN, input->evbit);
+	__set_bit(EV_KEY, input->evbit);
+	__set_bit(EV_MSC, input->evbit);
+	input->mscbit[0] = BIT_MASK(MSC_GESTURE);
+
+	mxt_debug(DEBUG_TRACE, "maXTouch driver setting client data\n");
+	i2c_set_clientdata(client, mxt);
+	mxt_debug(DEBUG_TRACE, "maXTouch driver setting drv data\n");
+	input_set_drvdata(input, mxt);
+	mxt_debug(DEBUG_TRACE, "maXTouch driver input register device\n");
+	error = input_register_device(mxt->input);
+	if (error < 0) {
+		dev_err(&client->dev,
+			"Failed to register input device\n");
+		goto err_register_device;
+	}
+
+	error = mxt_read_object_table(client, mxt, id_data);
+	if (error < 0)
+		goto err_read_ot;
+
+
+	/* Create debugfs entries. */
+	mxt->debug_dir = debugfs_create_dir("maXTouch", NULL);
+	if (mxt->debug_dir == -ENODEV) {
+		/* debugfs is not enabled. */
+		printk(KERN_WARNING "debugfs not enabled in kernel\n");
+	} else if (mxt->debug_dir == NULL) {
+		printk(KERN_WARNING "error creating debugfs dir\n");
+	} else {
+		mxt_debug(DEBUG_TRACE, "created \"maXTouch\" debugfs dir\n");
+		debugfs_create_file("deltas", S_IRUSR, mxt->debug_dir, mxt,
+				    &delta_fops);
+		debugfs_create_file("refs", S_IRUSR, mxt->debug_dir, mxt,
+				    &refs_fops);
+	}
+
+	/* Create character device nodes for reading & writing registers */
+	mxt->mxt_class = class_create(THIS_MODULE, "maXTouch_memory");
+	if (IS_ERR(mxt->mxt_class)) {
+		printk(KERN_WARNING "class create failed! exiting...");
+		goto err_read_ot;
+	}
+	/* 2 numbers; one for memory and one for messages */
+	error = alloc_chrdev_region(&mxt->dev_num, 0, 2,
+				    "maXTouch_memory");
+	mxt_debug(DEBUG_VERBOSE,
+		"device number %d allocated!\n", MAJOR(mxt->dev_num));
+	if (error)
+		printk(KERN_WARNING "Error registering device\n");
+
+	cdev_init(&mxt->cdev, &mxt_memory_fops);
+	cdev_init(&mxt->cdev_messages, &mxt_message_fops);
+
+	mxt_debug(DEBUG_VERBOSE, "cdev initialized\n");
+	mxt->cdev.owner = THIS_MODULE;
+	mxt->cdev_messages.owner = THIS_MODULE;
+
+	error = cdev_add(&mxt->cdev, mxt->dev_num, 1);
+	if (error)
+		printk(KERN_WARNING "Bad cdev\n");
+
+	error = cdev_add(&mxt->cdev_messages, mxt->dev_num + 1, 1);
+	if (error)
+		printk(KERN_WARNING "Bad cdev\n");
+
+	mxt_debug(DEBUG_VERBOSE, "cdev added\n");
+
+	device_create(mxt->mxt_class, NULL, MKDEV(MAJOR(mxt->dev_num), 0), NULL,
+		"maXTouch");
+
+	device_create(mxt->mxt_class, NULL, MKDEV(MAJOR(mxt->dev_num), 1), NULL,
+		"maXTouch_messages");
+
+	mxt->msg_buffer_startp = 0;
+	mxt->msg_buffer_endp = 0;
+
+	/* Allocate the interrupt */
+	mxt_debug(DEBUG_TRACE, "maXTouch driver allocating interrupt...\n");
+	mxt->irq = client->irq;
+	mxt->valid_irq_counter = 0;
+	mxt->invalid_irq_counter = 0;
+	mxt->irq_counter = 0;
+	if (mxt->irq) {
+		/* Try to request IRQ with falling edge first. This is
+		 * not always supported. If it fails, try with any edge. */
+		error = request_irq(mxt->irq,
+				    mxt_irq_handler,
+				    IRQF_TRIGGER_FALLING,
+				    client->dev.driver->name,
+				    mxt);
+		if (error < 0) {
+			/* TODO: why only 0 works on STK1000? */
+			error = request_irq(mxt->irq,
+					    mxt_irq_handler,
+					    0,
+					    client->dev.driver->name,
+					    mxt);
+		}
+
+		if (error < 0) {
+			dev_err(&client->dev,
+				"failed to allocate irq %d\n", mxt->irq);
+			goto err_irq;
+		}
+	}
+
+	if (debug > DEBUG_INFO)
+		dev_info(&client->dev, "touchscreen, irq %d\n", mxt->irq);
+
+	/* Schedule a worker routine to read any messages that might have
+	 * been sent before interrupts were enabled. */
+	cancel_delayed_work(&mxt->dwork);
+	schedule_delayed_work(&mxt->dwork, 0);
+	kfree(id_data);
+
+	return 0;
+
+
+err_irq:
+	kfree(mxt->rid_map);
+	kfree(mxt->object_table);
+	kfree(mxt->last_message);
+err_read_ot:
+err_register_device:
+err_identify:
+err_pdata:
+	input_free_device(input);
+err_input_dev_alloc:
+	kfree(id_data);
+err_id_alloc:
+	if (mxt->exit_hw != NULL)
+		mxt->exit_hw();
+	kfree(mxt);
+err_mxt_alloc:
+	return error;
+}
+
+static int __devexit mxt_remove(struct i2c_client *client)
+{
+	struct mxt_data *mxt;
+
+	mxt = i2c_get_clientdata(client);
+
+	/* Remove debug dir entries */
+	debugfs_remove_recursive(mxt->debug_dir);
+
+	if (mxt != NULL) {
+		if (mxt->exit_hw != NULL)
+			mxt->exit_hw();
+		if (mxt->irq)
+			free_irq(mxt->irq, mxt);
+		unregister_chrdev_region(mxt->dev_num, 2);
+		device_destroy(mxt->mxt_class, MKDEV(MAJOR(mxt->dev_num), 0));
+		device_destroy(mxt->mxt_class, MKDEV(MAJOR(mxt->dev_num), 1));
+		cdev_del(&mxt->cdev);
+		cdev_del(&mxt->cdev_messages);
+		cancel_delayed_work_sync(&mxt->dwork);
+		input_unregister_device(mxt->input);
+		class_destroy(mxt->mxt_class);
+		debugfs_remove(mxt->debug_dir);
+
+		kfree(mxt->rid_map);
+		kfree(mxt->object_table);
+		kfree(mxt->last_message);
+	}
+	kfree(mxt);
+
+	i2c_set_clientdata(client, NULL);
+	if (debug >= DEBUG_TRACE)
+		dev_info(&client->dev, "Touchscreen unregistered\n");
+
+	return 0;
+}
+
+#if defined(CONFIG_PM)
+/*
+ * Allow the touchscreen to resume the system from sleep.
+ * Alternatively we could put the maXTouch to deep sleep mode and use
+ * something else to resume.
+ *
+ * TODO: set ACQINT to some high value, and restore to original at resume?
+ */
+
+static int mxt_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+	struct mxt_data *mxt = i2c_get_clientdata(client);
+	if (device_may_wakeup(&client->dev))
+		enable_irq_wake(mxt->irq);
+	return 0;
+}
+
+static int mxt_resume(struct i2c_client *client)
+{
+	struct mxt_data *mxt = i2c_get_clientdata(client);
+	if (device_may_wakeup(&client->dev))
+		disable_irq_wake(mxt->irq);
+	return 0;
+}
+#else
+#define mxt_suspend NULL
+#define mxt_resume NULL
+#endif
+
+static const struct i2c_device_id mxt_idtable[] = {
+	{"maXTouch", 0,},
+	{ }
+};
+
+MODULE_DEVICE_TABLE(i2c, mxt_idtable);
+
+static struct i2c_driver mxt_driver = {
+	.driver = {
+		.name	= "maXTouch",
+		.owner  = THIS_MODULE,
+	},
+
+	.id_table	= mxt_idtable,
+	.probe		= mxt_probe,
+	.remove		= __devexit_p(mxt_remove),
+	.suspend	= mxt_suspend,
+	.resume		= mxt_resume,
+
+};
+
+static int __init mxt_init(void)
+{
+	int err;
+	err = i2c_add_driver(&mxt_driver);
+	if (err)
+		printk(KERN_WARNING "Adding maXTouch driver failed "
+		       "(errno = %d)\n", err);
+	else
+		mxt_debug(DEBUG_TRACE, "Successfully added driver %s\n",
+			  mxt_driver.driver.name);
+	return err;
+}
+
+static void __exit mxt_cleanup(void)
+{
+	i2c_del_driver(&mxt_driver);
+}
+
+
+module_init(mxt_init);
+module_exit(mxt_cleanup);
+
+MODULE_AUTHOR("Iiro Valkonen");
+MODULE_DESCRIPTION("Driver for Atmel maXTouch Touchscreen Controller");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/atmel_maxtouch.h b/include/linux/atmel_maxtouch.h
new file mode 100644
index 0000000..0f2904e
--- /dev/null
+++ b/include/linux/atmel_maxtouch.h
@@ -0,0 +1,310 @@
+/*
+ *  Atmel maXTouch header file
+ *
+ *  Copyright (c) 2010 Atmel Corporation
+ *  Copyright (C) 2010 Ulf Samuelsson (ulf@atmel.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 or 3 as
+ *  published by the Free Software Foundation.
+ *  See the file "COPYING" in the main directory of this archive
+ *  for more details.
+ *
+ */
+
+#define	MXT224_I2C_ADDR1				0x4A
+#define	MXT224_I2C_ADDR2				0x4B
+#define	MXT1386_I2C_ADDR1				0x4C
+#define	MXT1386_I2C_ADDR2				0x4D
+#define	MXT1386_I2C_ADDR3				0x5A
+#define	MXT1386_I2C_ADDR4				0x5B
+
+/*
+ * Select this address from above depending on what maXTouch
+ * chip you have and how it's address pins are configured;
+ * see datasheet.
+ */
+
+#define MXT_I2C_ADDRESS                                 MXT1386_I2C_ADDR1
+
+#define	MXT224_FAMILYID				        0x80
+#define MXT1386_FAMILYID                                0xA0
+
+#define	MXT224_CAL_VARIANTID				0x01
+#define MXT224_UNCAL_VARIANTID                          0x00
+#define MXT1386_CAL_VARIANTID                           0x00
+
+#define MXT_MAX_REPORTED_WIDTH                          255
+#define MXT_MAX_REPORTED_PRESSURE                       255
+#define MXT_MAX_TOUCH_SIZE                              255
+#define MXT_MAX_NUM_TOUCHES                             10
+
+/* Fixed addresses inside maXTouch device */
+#define	MXT_ADDR_INFO_BLOCK				0
+#define	MXT_ADDR_OBJECT_TABLE				7
+#define MXT_ID_BLOCK_SIZE                               7
+#define	MXT_OBJECT_TABLE_ELEMENT_SIZE			6
+
+/* Object types */
+#define	MXT_DEBUG_DELTAS_T2				2
+#define	MXT_DEBUG_REFERENCES_T3				3
+#define	MXT_GEN_MESSAGEPROCESSOR_T5			5
+#define	MXT_GEN_COMMANDPROCESSOR_T6			6
+#define	MXT_GEN_POWERCONFIG_T7				7
+#define	MXT_GEN_ACQUIRECONFIG_T8			8
+#define	MXT_TOUCH_MULTITOUCHSCREEN_T9			9
+#define MXT_TOUCH_SINGLETOUCHSCREEN_T10                 10
+#define MXT_TOUCH_XSLIDER_T11                           11
+#define MXT_TOUCH_YSLIDER_T12                           12
+#define MXT_TOUCH_XWHEEL_T13                            13
+#define MXT_TOUCH_YWHEEL_T14                            14
+#define	MXT_TOUCH_KEYARRAY_T15				15
+#define	MXT_SPT_GPIOPWM_T19				19
+#define	MXT_PROCI_GRIPFACESUPPRESSION_T20		20
+#define	MXT_PROCG_NOISESUPPRESSION_T22			22
+#define	MXT_TOUCH_PROXIMITY_T23				23
+#define	MXT_PROCI_ONETOUCHGESTUREPROCESSOR_T24		24
+#define	MXT_SPT_SELFTEST_T25				25
+#define MXT_DEBUG_CTERANGE_T26				26
+#define	MXT_PROCI_TWOTOUCHGESTUREPROCESSOR_T27		27
+#define	MXT_SPT_CTECONFIG_T28				28
+#define	MXT_TOUCH_KEYSET_T31				31
+#define	MXT_TOUCH_XSLIDERSET_T32			32
+#define	MXT_DEBUG_DIAGNOSTIC_T37			37
+#define	MXT_USER_INFO_T38				38
+
+
+/*
+ * If a message is read from mXT when there's no new messages available,
+ * the report ID of the message will be 0xFF.
+ */
+#define	MXT_END_OF_MESSAGES				0xFF
+
+
+/* GEN_COMMANDPROCESSOR_T6 Register offsets from T6 base address */
+#define	MXT_ADR_T6_RESET				0x00
+#define	MXT_ADR_T6_BACKUPNV				0x01
+#define	MXT_ADR_T6_CALIBRATE				0x02
+#define	MXT_ADR_T6_REPORTALL				0x03
+#define	MXT_ADR_T6_RESERVED				0x04
+#define	MXT_ADR_T6_DIAGNOSTIC				0x05
+
+/* T6 Debug Diagnostics Commands */
+#define	MXT_CMD_T6_PAGE_UP          0x01
+#define	MXT_CMD_T6_PAGE_DOWN        0x02
+#define	MXT_CMD_T6_DELTAS_MODE      0x10
+#define	MXT_CMD_T6_REFERENCES_MODE  0x11
+#define	MXT_CMD_T6_CTE_MODE         0x31
+
+/* T6 Backup Command */
+#define MXT_CMD_T6_BACKUP           0x55
+
+/* SPT_DEBUG_DIAGNOSTIC_T37 Register offsets from T37 base address */
+#define MXT_ADR_T37_PAGE                                0x01
+#define	MXT_ADR_T37_DATA				0x02
+
+
+
+/************************************************************************
+ * MESSAGE OBJECTS ADDRESS FIELDS
+ *
+ ************************************************************************/
+#define MXT_MSG_REPORTID                                0x00
+
+
+/* MXT_GEN_MESSAGEPROCESSOR_T5 Message address definitions		*/
+#define	MXT_MSG_T5_REPORTID				0x00
+#define	MXT_MSG_T5_MESSAGE				0x01
+#define	MXT_MSG_T5_CHECKSUM				0x08
+
+/* MXT_GEN_COMMANDPROCESSOR_T6 Message address definitions		*/
+#define	MXT_MSG_T6_STATUS				0x01
+#define		MXT_MSGB_T6_COMSERR		0x04
+#define		MXT_MSGB_T6_CFGERR		0x08
+#define		MXT_MSGB_T6_CAL			0x10
+#define		MXT_MSGB_T6_SIGERR		0x20
+#define		MXT_MSGB_T6_OFL			0x40
+#define		MXT_MSGB_T6_RESET		0x80
+/* Three bytes */
+#define	MXT_MSG_T6_CHECKSUM				0x02
+
+/* MXT_GEN_POWERCONFIG_T7 NO Message address definitions		*/
+/* MXT_GEN_ACQUIRECONFIG_T8 Message address definitions			*/
+/* MXT_TOUCH_MULTITOUCHSCREEN_T9 Message address definitions		*/
+
+#define	MXT_MSG_T9_STATUS				0x01
+/* Status bit field */
+#define		MXT_MSGB_T9_SUPPRESS		0x02
+#define		MXT_MSGB_T9_AMP			0x04
+#define		MXT_MSGB_T9_VECTOR		0x08
+#define		MXT_MSGB_T9_MOVE		0x10
+#define		MXT_MSGB_T9_RELEASE		0x20
+#define		MXT_MSGB_T9_PRESS		0x40
+#define		MXT_MSGB_T9_DETECT		0x80
+
+#define	MXT_MSG_T9_XPOSMSB				0x02
+#define	MXT_MSG_T9_YPOSMSB				0x03
+#define	MXT_MSG_T9_XYPOSLSB				0x04
+#define	MXT_MSG_T9_TCHAREA				0x05
+#define	MXT_MSG_T9_TCHAMPLITUDE				0x06
+#define	MXT_MSG_T9_TCHVECTOR				0x07
+
+
+/* MXT_SPT_GPIOPWM_T19 Message address definitions			*/
+#define	MXT_MSG_T19_STATUS				0x01
+
+/* MXT_PROCI_GRIPFACESUPPRESSION_T20 Message address definitions	*/
+#define	MXT_MSG_T20_STATUS				0x01
+#define		MXT_MSGB_T20_FACE_SUPPRESS	0x01
+/* MXT_PROCG_NOISESUPPRESSION_T22 Message address definitions		*/
+#define	MXT_MSG_T22_STATUS				0x01
+#define		MXT_MSGB_T22_FHCHG		0x01
+#define		MXT_MSGB_T22_GCAFERR		0x04
+#define		MXT_MSGB_T22_FHERR		0x08
+#define	MXT_MSG_T22_GCAFDEPTH				0x02
+
+/* MXT_TOUCH_PROXIMITY_T23 Message address definitions			*/
+#define	MXT_MSG_T23_STATUS				0x01
+#define		MXT_MSGB_T23_FALL		0x20
+#define		MXT_MSGB_T23_RISE		0x40
+#define		MXT_MSGB_T23_DETECT		0x80
+/* 16 bit */
+#define	MXT_MSG_T23_PROXDELTA				0x02
+
+/* MXT_PROCI_ONETOUCHGESTUREPROCESSOR_T24 Message address definitions	*/
+#define	MXT_MSG_T24_STATUS				0x01
+#define	MXT_MSG_T24_XPOSMSB				0x02
+#define	MXT_MSG_T24_YPOSMSB				0x03
+#define	MXT_MSG_T24_XYPOSLSB				0x04
+#define	MXT_MSG_T24_DIR					0x05
+/* 16 bit */
+#define	MXT_MSG_T24_DIST				0x06
+
+/* MXT_SPT_SELFTEST_T25 Message address definitions			*/
+#define	MXT_MSG_T25_STATUS				0x01
+/* 5 Bytes */
+#define		MXT_MSGR_T25_OK			0xFE
+#define		MXT_MSGR_T25_INVALID_TEST	0xFD
+#define		MXT_MSGR_T25_PIN_FAULT		0x11
+#define		MXT_MSGR_T25_SIGNAL_LIMIT_FAULT	0x17
+#define		MXT_MSGR_T25_GAIN_ERROR		0x20
+#define	MXT_MSG_T25_INFO				0x02
+
+/* MXT_PROCI_TWOTOUCHGESTUREPROCESSOR_T27 Message address definitions	*/
+#define	MXT_MSG_T27_STATUS			0x01
+#define		MXT_MSGB_T27_ROTATEDIR		0x10
+#define		MXT_MSGB_T27_PINCH		0x20
+#define		MXT_MSGB_T27_ROTATE		0x40
+#define		MXT_MSGB_T27_STRETCH		0x80
+#define	MXT_MSG_T27_XPOSMSB			0x02
+#define	MXT_MSG_T27_YPOSMSB			0x03
+#define	MXT_MSG_T27_XYPOSLSB			0x04
+#define	MXT_MSG_T27_ANGLE			0x05
+
+/* 16 bit */
+#define	MXT_MSG_T27_SEPARATION				0x06
+
+/* MXT_SPT_CTECONFIG_T28 Message address definitions			*/
+#define	MXT_MSG_T28_STATUS				0x01
+#define	MXT_MSGB_T28_CHKERR		0x01
+
+
+/* One Touch Events */
+#define	MXT_GESTURE_RESERVED		0x00
+#define	MXT_GESTURE_PRESS		0x01
+#define	MXT_GESTURE_RELEASE		0x02
+#define	MXT_GESTURE_TAP			0x03
+#define	MXT_GESTURE_DOUBLE_TAP		0x04
+#define	MXT_GESTURE_FLICK		0x05
+#define	MXT_GESTURE_DRAG		0x06
+#define	MXT_GESTURE_SHORT_PRESS		0x07
+#define	MXT_GESTURE_LONG_PRESS		0x08
+#define	MXT_GESTURE_REPEAT_PRESS	0x09
+#define	MXT_GESTURE_TAP_AND_PRESS	0x0a
+#define	MXT_GESTURE_THROW		0x0b
+
+/* Two-touch events */
+#define MXT_GESTURE_STRETCH             (1 << 7)
+#define MXT_GESTURE_ROTATE              (1 << 6)
+#define MXT_GESTURE_PINCH               (1 << 5)
+#define MXT_GESTURE_ROTATEDIR           (1 << 4)
+
+
+
+/* Bootloader states */
+#define WAITING_BOOTLOAD_COMMAND   0xC0
+#define WAITING_FRAME_DATA         0x80
+#define APP_CRC_FAIL               0x40
+#define FRAME_CRC_CHECK            0x02
+#define FRAME_CRC_PASS             0x04
+#define FRAME_CRC_FAIL             0x03
+
+#define MXT_MAX_FRAME_SIZE         276
+
+/* Debug levels */
+#define DEBUG_INFO     1
+#define DEBUG_VERBOSE  2
+#define DEBUG_MESSAGES 5
+#define DEBUG_RAW      8
+#define DEBUG_TRACE   10
+
+/* IOCTL commands */
+#define MXT_SET_ADDRESS_IOCTL _IOW('x', 0x01, int)  /* Sets the internal address pointer */
+#define MXT_RESET_IOCTL       _IO('x', 0x02)  /* Resets the device */
+#define MXT_CALIBRATE_IOCTL   _IO('x', 0x03)  /* Calibrates the device */
+/* Backups the current state of registers to NVM */
+#define MXT_BACKUP_IOCTL      _IO('x', 0x04)
+/*
+ * Only non-touch messages can be read from the message buffer
+ * (/dev/maXTouch_messages)
+ */
+#define MXT_NONTOUCH_MSG_IOCTL _IO('x', 0x05)
+/* All messages can be read from the message buffer */
+#define MXT_ALL_MSG_IOCTL _IO('x', 0x06)
+
+/* Message buffer size. This is a ring buffer, and when full, the oldest entry
+   will be overwritten. */
+#define MXT_MESSAGE_BUFFER_SIZE  128
+
+/* Routines for memory access within a 16 bit address space */
+
+static int mxt_read_block(struct i2c_client *client, u16 addr, u16 length,
+			  u8 *value);
+static int mxt_write_byte(struct i2c_client *client, u16 addr, u8 value);
+static int mxt_write_block(struct i2c_client *client, u16 addr, u16 length,
+			   u8 *value);
+
+
+/**
+ * struct mxt_platform_data - includes platform specific informatio
+ * related to Atmel maXTouch touchscreen controller.
+ *
+ * @numtouch:           Number of simultaneous touches supported
+ * @init_platform_hw(): Initialization function, which can for example
+ *                      trigger a hardware reset by toggling a GPIO pin
+ * @exit_platform_hw(): Function to run when the driver is unloaded.
+ * @valid_interrupt():  Function that checks the validity of the interrupt -
+ *                      function that check the validity of a interrupt (by
+ *                      reading the changeline interrupt pin and checking that
+ *                      it really is low for example).
+ * @max_x:              Reported X range
+ * @max_y:              Reported Y range
+ */
+
+struct mxt_platform_data {
+	u8    numtouch;	/* Number of touches to report	*/
+	void  (*init_platform_hw)(void);
+	void  (*exit_platform_hw)(void);
+	int   max_x;    /* The default reported X range   */
+	int   max_y;    /* The default reported Y range   */
+	u8    (*valid_interrupt) (void);
+	u8    (*read_chg) (void);
+};
+
+
+static u8 mxt_valid_interrupt_dummy(void)
+{
+	return 1;
+}
+
+void	mxt_hw_reset(void);
-- 
1.7.0.4



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] input: Add Atmel maXTouch touchscreen driver
  2010-11-12 14:14 [PATCH] input: Add Atmel maXTouch touchscreen driver Iiro Valkonen
@ 2010-11-12 20:10 ` Dmitry Torokhov
  2010-11-17 10:41   ` Iiro Valkonen
  0 siblings, 1 reply; 6+ messages in thread
From: Dmitry Torokhov @ 2010-11-12 20:10 UTC (permalink / raw)
  To: Iiro Valkonen; +Cc: kyungmin.park, jy0922.shim, linux-input

Hi Iiro,

On Fri, Nov 12, 2010 at 04:14:18PM +0200, Iiro Valkonen wrote:
> 
> Add Atmel maXTouch touchscreen driver (I2C interface).
> The MT slots protocol will be implemented later.
> 

I believe there is already a driver for this hardware in the tree
(qt602240.c). Could you tell me what additional features your driver
implements and why can't they be added to the existing driver.

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] input: Add Atmel maXTouch touchscreen driver
       [not found] <4CE2F692.8040403@atmel.com>
@ 2010-11-16 22:05 ` Dmitry Torokhov
  0 siblings, 0 replies; 6+ messages in thread
From: Dmitry Torokhov @ 2010-11-16 22:05 UTC (permalink / raw)
  To: Ulf Samuelsson; +Cc: linux-input, iiro.valkonen

Hi Ulf,

On Tue, Nov 16, 2010 at 10:24:34PM +0100, Ulf Samuelsson wrote:
> Hi Iiro,
> 
> On Fri, Nov 12, 2010 at 04:14:18PM +0200, Iiro Valkonen wrote:
> >/  /
> >/  Add Atmel maXTouch touchscreen driver (I2C interface)./
> >/  The MT slots protocol will be implemented later./
> >/  /
> 
> I believe there is already a driver for this hardware in the tree
> (qt602240.c). Could you tell me what additional features your driver
> implements and why can't they be added to the existing driver.
> 
> Thanks.
> 
> -- 
> Dmitry
> 
> 
> 
> =========
> When we reviewed the Samsung QT602240 driver internally,
> we found 20 significant problems including memory leaks
> and doing I2C transfers within interrupts.
> I have been told that since I2C transfers can sleep, this is not a
> good idea.

It does so in _threaded_ interrupt which is perfectly fine. The need to
access slower buses from the interrupt context was one of the reasons
this facility was added to the kernel.

> Configuration is done inside the driver, instead of using platform
> data. etc. etc.

The platform data is defined in include/linux/i2c/qt602240_ts.h and can
be extended if needed.

The driver passed both mutitouch protocol review process and the general
review and I believe most (all?) memory leaks and other issues presented
in the original version have been fixed.

> 
> This is why I started writing a new driver and Iiro has added significant
> improvements/rewrites and is the current maintainer of the maXTouch
> driver within Atmel - the manufacturer of the device in question.
> 
> Have not checked if the qt602240.c still does memory leaks,
> but it still seems to make I2C transfers in the interrupt.
> 
> My suggestion is that we deprecate the qt602240.c and remove
> it in some future version.
> Instead we should use the official driver from Atmel, that Iiro sent
> to the list.

No. I regret to say but I find the in-tree version of qt602240.c in much
better shape than what was submitted by Iiro. Please identify the
improvements that are needed for the driver and submit separate patches
implementing these improvements.

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] input: Add Atmel maXTouch touchscreen driver
  2010-11-12 20:10 ` Dmitry Torokhov
@ 2010-11-17 10:41   ` Iiro Valkonen
  2010-11-17 14:04     ` Murphy, Dan
  0 siblings, 1 reply; 6+ messages in thread
From: Iiro Valkonen @ 2010-11-17 10:41 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: kyungmin.park, jy0922.shim, linux-input

On 11/12/2010 10:10 PM, Dmitry Torokhov wrote:
> Hi Iiro,
> 
> I believe there is already a driver for this hardware in the tree
> (qt602240.c). Could you tell me what additional features your driver
> implements and why can't they be added to the existing driver.
> 

Hi Dmitry,

the current driver is written for one maXTouch chip, mXT224. The driver that I submitted will support all current maXTouch chips, and it is built to be future-proof so it should work with yet to be released chips too, with minor or no changes. This shows in the naming too, part name "qt602240" is deprecated and it shouldn't be used. As the driver should work with all chips in the maXTouch family, a more generic naming convention in the driver name itself and all the code inside the driver would be more appropriate. 

Atmel would like to be the maintainer of this driver, and it would be easiest if we could use the code I submitted. Of course we would be happy to implement any improvements like the threaded IRQs.

Best Regards,
-- 
Iiro Valkonen
Application Engineer / Atmel Corporation
Tel: +358(9)45208250 / Mobile: +358(40)5820012
iiro.valkonen@atmel.com / www.atmel.com

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] input: Add Atmel maXTouch touchscreen driver
  2010-11-17 10:41   ` Iiro Valkonen
@ 2010-11-17 14:04     ` Murphy, Dan
  2010-11-17 18:23       ` Dmitry Torokhov
  0 siblings, 1 reply; 6+ messages in thread
From: Murphy, Dan @ 2010-11-17 14:04 UTC (permalink / raw)
  To: Iiro Valkonen; +Cc: Dmitry Torokhov, kyungmin.park, jy0922.shim, linux-input

Hi Dmitry

On Wed, Nov 17, 2010 at 4:41 AM, Iiro Valkonen <iiro.valkonen@atmel.com> wrote:
> On 11/12/2010 10:10 PM, Dmitry Torokhov wrote:
> Hi Dmitry,
>
> the current driver is written for one maXTouch chip, mXT224. The driver that I submitted will support all current >maXTouch chips, and it is built to be future-proof so it should work with yet to be released chips too, with minor or >no changes. This shows in the naming too, part name "qt602240" is deprecated and it shouldn't be used. As the >driver should work with all chips in the maXTouch family, a more generic naming convention in the driver name itself >and all the code inside the driver would be more appropriate.

Having written this same driver for this IC I agree with Iiro that we
should have one driver that would support multiple IC firmware
revisions.  Once we had the base driver completed adding functionality
to the driver was a snap.

Can these two drivers co-exist and let the developer decide which driver to use?
Of course the Atmel driver needs to be scrubbed for problems.

Now.  I also strongly agree with Dmitry that the driver you are
submitting is IMHO really rough and needs work.  Without even
reviewing the complete functionality I have many concerns about the
code.

For instance the header file spacing is really crazy.  I am not sure
if the spacing was intended but it is really hard to follow.  In the
source it seems like there is more debug code then functional working
code.  The driver should be updated to use the request_threaded_irq
call and not the request_irq then there is no need for scheduling work
queues.  I can go on and on.... But I won't do a complete review on
this driver unless Dmitry indicates that he will merge it once
complete.

>
> Atmel would like to be the maintainer of this driver, and it would be easiest if we could use the code I submitted. Of course we would be happy to implement any improvements like the threaded IRQs.

I would prefer Atmel be the maintainer as they can expedite future
changes into the driver prior to releasing the support firmware.

Dan

------------------------------------------------------------------------------------
Dan Murphy
Texas Instruments

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] input: Add Atmel maXTouch touchscreen driver
  2010-11-17 14:04     ` Murphy, Dan
@ 2010-11-17 18:23       ` Dmitry Torokhov
  0 siblings, 0 replies; 6+ messages in thread
From: Dmitry Torokhov @ 2010-11-17 18:23 UTC (permalink / raw)
  To: Murphy, Dan; +Cc: Iiro Valkonen, kyungmin.park, jy0922.shim, linux-input

On Wed, Nov 17, 2010 at 08:04:32AM -0600, Murphy, Dan wrote:
> Hi Dmitry
> 
> On Wed, Nov 17, 2010 at 4:41 AM, Iiro Valkonen <iiro.valkonen@atmel.com> wrote:
> > On 11/12/2010 10:10 PM, Dmitry Torokhov wrote:
> > Hi Dmitry,
> >
> > the current driver is written for one maXTouch chip, mXT224. The
> > driver that I submitted will support all current >maXTouch chips,
> > and it is built to be future-proof so it should work with yet to be
> > released chips too, with minor or no changes. This shows in the
> > naming too, part name "qt602240" is deprecated and it shouldn't be
> > used. As the driver should work with all chips in the maXTouch
> > family, a more generic naming convention in the driver name itself
> > and all the code inside the driver would be more appropriate.

The name of the driver is not important and can easily be changed, just
send me a patch.

> 
> Having written this same driver for this IC I agree with Iiro that we
> should have one driver that would support multiple IC firmware
> revisions.  Once we had the base driver completed adding functionality
> to the driver was a snap.
> 
> Can these two drivers co-exist and let the developer decide which driver to use?
> Of course the Atmel driver needs to be scrubbed for problems.

No, that would cause duplicate effort. Just witness patches form Intel
submitted for the mainline driver a few days ago.

Development process in Linux kernel is rarely revolutionary but rather
evolutionary. That is why I asked Iiro to enumerate the changes needed
for qt602240 to support entire line of maXTouch chips.

> 
> Now.  I also strongly agree with Dmitry that the driver you are
> submitting is IMHO really rough and needs work.  Without even
> reviewing the complete functionality I have many concerns about the
> code.
> 
> For instance the header file spacing is really crazy.  I am not sure
> if the spacing was intended but it is really hard to follow.  In the
> source it seems like there is more debug code then functional working
> code.  The driver should be updated to use the request_threaded_irq
> call and not the request_irq then there is no need for scheduling work
> queues.  I can go on and on.... But I won't do a complete review on
> this driver unless Dmitry indicates that he will merge it once
> complete.

Like I said, I will not take the driver wholesale but I will take it
piecemeal, feature by feature transforming qt602240.

BTW, what is up with needing 2 custom character devices?

> 
> >
> > Atmel would like to be the maintainer of this driver, and it would
> > be easiest if we could use the code I submitted. Of course we would
> > be happy to implement any improvements like the threaded IRQs.
>
> I would prefer Atmel be the maintainer as they can expedite future
> changes into the driver prior to releasing the support firmware.
>

I will be delighted if Atmel takes over the driver, as long as they
follow the standard kernel practices.

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2010-11-17 18:23 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-11-12 14:14 [PATCH] input: Add Atmel maXTouch touchscreen driver Iiro Valkonen
2010-11-12 20:10 ` Dmitry Torokhov
2010-11-17 10:41   ` Iiro Valkonen
2010-11-17 14:04     ` Murphy, Dan
2010-11-17 18:23       ` Dmitry Torokhov
     [not found] <4CE2F692.8040403@atmel.com>
2010-11-16 22:05 ` Dmitry Torokhov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).