lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <1388146515-19481-1-git-send-email-scott.liu@emc.com.tw>
Date:	Fri, 27 Dec 2013 20:15:15 +0800
From:	scott <scott.liu@....com.tw>
To:	Scott Liu <scott.liu@....com.tw>,
	Dmitry Torokhov <dmitry.torokhov@...il.com>,
	linux-kernel@...r.kernel.org, linux-input@...r.kernel.org
Cc:	Vincent Wang <vincent.wang@....com.tw>,
	Jeff Chuang <jeff.chuang@....com.tw>,
	Agnes Cheng <agnescheng@...gle.com>
Subject: [PATCH v1] Input: elants_i2c: Add Elan touchscreen support

From: Scott Liu <scott.liu@....com.tw>

This patch is for Elan eKTH Touchscreen product, I2C adpater module.

Signed-off-by: Scott Liu <scott.liu@....com.tw>
---
 drivers/input/touchscreen/Kconfig      |   12 +
 drivers/input/touchscreen/Makefile     |    1 +
 drivers/input/touchscreen/elants_i2c.c | 1789 ++++++++++++++++++++++++++++++++
 3 files changed, 1802 insertions(+)
 create mode 100644 drivers/input/touchscreen/elants_i2c.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 961d58d..b400faa 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -317,6 +317,18 @@ config TOUCHSCREEN_GUNZE
 	  To compile this driver as a module, choose M here: the
 	  module will be called gunze.
 
+config TOUCHSCREEN_ELAN
+	tristate "Elan touchscreens"
+	depends on I2C
+	help
+	  Say Y here if you have an Elan touchscreen connected to
+	  your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called elan.
+
 config TOUCHSCREEN_ELO
 	tristate "Elo serial touchscreens"
 	select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 62801f2..687d5a7 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06)	+= edt-ft5x06.o
 obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE)	+= hampshire.o
 obj-$(CONFIG_TOUCHSCREEN_GUNZE)		+= gunze.o
 obj-$(CONFIG_TOUCHSCREEN_EETI)		+= eeti_ts.o
+obj-$(CONFIG_TOUCHSCREEN_ELAN)		+= elants_i2c.o
 obj-$(CONFIG_TOUCHSCREEN_ELO)		+= elo.o
 obj-$(CONFIG_TOUCHSCREEN_EGALAX)	+= egalax_ts.o
 obj-$(CONFIG_TOUCHSCREEN_FUJITSU)	+= fujitsu_ts.o
diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
new file mode 100644
index 0000000..e46675a
--- /dev/null
+++ b/drivers/input/touchscreen/elants_i2c.c
@@ -0,0 +1,1789 @@
+/*
+ * Elan Microelectronics touchpanels with I2C interface
+ *
+ * Copyright (C) 2012 Elan Microelectronics Corporation.
+ * Scott Liu <scott.liu@....com.tw>
+ *
+ * This code is partly based on hid-multitouch.c:
+ *
+ *  Copyright (c) 2010-2012 Stephane Chatty <chatty@...c.fr>
+ *  Copyright (c) 2010-2012 Benjamin Tissoires <benjamin.tissoires@...il.com>
+ *  Copyright (c) 2010-2012 Ecole Nationale de l'Aviation Civile, France
+ *
+ */
+
+/*
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/async.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/buffer_head.h>
+#include <linux/version.h>
+#include <linux/kfifo.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/firmware.h>
+#include <linux/version.h>
+#include <linux/input/mt.h>
+
+/* debug option */
+static bool debug = false;
+module_param(debug, bool, 0444);
+MODULE_PARM_DESC(debug, "print a lot of debug information");
+
+#define elan_dbg(client, fmt, arg...)   \
+	do {	\
+		if (debug)      \
+			dev_printk(KERN_DEBUG, \
+				&client->dev, fmt, ##arg);	\
+	} while (0)
+
+/*
+ * Device, Driver information
+ */
+#define DEVICE_NAME	"elants_i2c"
+
+#define DRV_MA_VER 1
+#define DRV_MI_VER 0
+#define DRV_SUB_MI_VER 4
+
+#define _str(s) (#s)
+#define str(s)  _str(s)
+#define DRIVER_VERSION  str(DRV_MA_VER.DRV_MI_VER.DRV_SUB_MI_VER)
+
+/* For CMT (must match XRANGE/YRANGE as defined in board config */
+#define X_PIXELS_PER_MM	13
+#define Y_PIXELS_PER_MM	14
+
+/*
+ * Finger report description
+ */
+
+#define MAX_CONTACT_NUM	10
+
+/*
+ * kfifo buffer size, used for Read command handshake
+ */
+
+#define FIFO_SIZE       (64)
+
+/*
+ *Convert from rows or columns into resolution
+ */
+
+#define ELAN_TS_RESOLUTION(n)   ((n - 1) * 64)
+
+static const char hello_packet[4] = { 0x55, 0x55, 0x55, 0x55 };
+static const char iniap_packet[4] = { 0x55, 0xaa, 0x33, 0xcc };
+static const char recov_packet[4] = { 0x55, 0x55, 0x80, 0x80 };
+
+/*
+ * Elan I2C Address definition
+ */
+
+#define	DEV_MASTER	0x10
+#define	DEV_SLAVE1	0x20
+#define	DEV_SLAVE2	0x21
+#define	MAX_DEVICE	3
+
+/*
+ * Finger report header byte definition
+ */
+
+#define	REPORT_HEADER_10_FINGER	0x62
+
+/*
+ * Buffer mode Queue Header information
+ */
+
+#define	QUEUE_HEADER_SINGLE	0x62
+#define	QUEUE_HEADER_NORMAL	0X63
+#define QUEUE_HEADER_WAIT	0x64
+#define	QUEUE_HEADER_SIZE	4
+
+#define PACKET_SIZE	55
+#define	MAX_PACKET_LEN	169
+
+/*
+ * Command header definition
+ */
+
+#define	CMD_HEADER_WRITE	0x54
+#define	CMD_HEADER_READ	0x53
+#define CMD_HEADER_6B_READ	0x5B
+#define	CMD_HEADER_RESP	0x52
+#define	CMD_HEADER_6B_RESP	0x9B
+#define	CMD_HEADER_HELLO	0x55
+#define	CMD_HEADER_REK	0x66
+#define	CMD_RESP_LEN	4
+
+/*
+ * FW information position
+ */
+
+#define	FW_POS_HEADER	0
+#define	FW_POS_STATE	1
+#define	FW_POS_TOTAL	2
+#define	FW_POS_CHECKSUM	34
+#define	FW_POS_WIDTH	35
+#define FW_POS_PRESSURE 45
+
+/*
+ * test_bit definition
+ */
+
+#define	LOCK_FILE_OPERATE	0
+#define	LOCK_CMD_HANDSHAKE	1
+#define	LOCK_FINGER_REPORT	2
+#define	LOCK_FW_UPDATE	3
+
+/*
+ * kfifo return value definition
+ */
+
+#define RET_OK	0
+#define	RET_CMDRSP	1
+#define	RET_FAIL	-1
+
+/*
+ * define boot condition definition
+ */
+
+#define	E_BOOT_NORM	0
+#define	E_BOOT_IAP	1
+
+/* FW read command, 0x53 0x?? 0x0, 0x01 */
+#define	E_ELAN_INFO_FW_VER	0x00
+#define	E_ELAN_INFO_BC_VER	0x10
+#define E_ELAN_INFO_TEST_VER	0xe0
+#define	E_ELAN_INFO_FW_ID	0xf0
+
+/**
+ * struct multi_queue_header - used by buffer queue header
+ *
+ * @packet_id: packet_id represented status of buffer.
+ * @report_count: number of finger report in buffer.
+ * @report_length: total length exclusive queue length.
+ */
+struct multi_queue_header {
+	u8 packet_id;
+	u8 report_count;
+	u8 report_length;
+	u8 reserved;
+};
+
+struct mt_slot {
+	__s32 x, y, p, w, h;
+	__s32 contactid;	/* the device ContactID assigned to this slot */
+	bool touch_state;	/* is the touch valid? */
+	bool seen_in_this_frame;	/* has this slot been updated */
+};
+
+struct mt_device {
+	struct mt_slot curdata;	/* placeholder of incoming data */
+	__u8 num_received;	/* how many contacts we received */
+	__u8 num_expected;	/* expected last contact index */
+	__u8 maxcontacts;
+	bool curvalid;		/* is the current contact valid? */
+	unsigned mt_flags;	/* flags to pass to input-mt */
+	struct mt_slot *slots;
+};
+
+/**
+ * struct elants_data - represents a global define of elants device
+ */
+struct elants_data {
+
+	bool irq_wake;
+
+	/* [0] : Solution version
+	   [1] : minor version */
+	union {
+		u8 _fw_version[2];
+		u16 fw_version;
+	};
+
+	u8 bc_version;
+	u8 iap_version;
+
+	union {
+		u8 _test_version[2];
+		u16 test_version;
+	};
+
+	bool fw_enabled;
+	int rows;
+	int cols;
+	int x_max;
+	int y_max;
+#define IAP_MODE_ENABLE	1
+	unsigned int iap_mode;
+	unsigned int rx_size;
+
+	u8 packet_size;
+
+	struct multi_queue_header mq_header;
+
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct task_struct *thread;
+
+	struct mutex mutex;	/* Protects I2C accesses to device */
+	struct mutex tr_mutex;	/* Protects I2C tx/rx */
+
+	unsigned long flags;	/* device flags */
+
+	unsigned short i2caddr;
+
+	struct kfifo fifo;
+	struct mutex fifo_mutex;
+	wait_queue_head_t wait;
+	spinlock_t rx_kfifo_lock;
+
+	struct mt_device td;	/* mt device */
+
+	/* fields required for debug fs */
+	struct mutex dbfs_mutex;
+	struct dentry *dbfs_root;
+
+	/* Add for TS driver debug */
+	long int irq_received;
+	long int packet_received;
+	long int touched_sync;
+	long int no_touched_sync;
+	long int checksum_correct;
+	long int wdt_reset;
+	u16 checksum_fail;
+	u16 header_fail;
+	u16 mq_header_fail;
+};
+
+/*
+ *  Function implement
+ */
+static inline void elan_msleep(u32 t)
+{
+	/*
+	 * If the sleeping time is 10us - 20ms, usleep_range() is recommended.
+	 * Read Documentation/timers/timers-howto.txt
+	 */
+	usleep_range(t * 1000, t * 1000 + 500);
+}
+
+static inline bool elan_chip_number(u8 addr)
+{
+	return addr == DEV_MASTER ? 1 : 0;
+}
+
+/**
+ *	elan_set_data - set command to TP.
+ *
+ *	@client: our i2c device
+ *	@data: command or data which will send to TP.
+ *	@len: command length usually.
+ *
+ *	set command to our TP.
+ */
+static int elan_set_data(struct i2c_client *client, const u8 *data, size_t len)
+{
+	struct elants_data *ts = i2c_get_clientdata(client);
+	struct i2c_adapter *adap = client->adapter;
+	struct i2c_msg msg;
+	int rc = 0;
+
+	dev_dbg(&client->dev, "Enter: %s\n", __func__);
+	elan_dbg(client,
+		 "%s cmd: %*phC, addr=%x\n",
+		 __func__, (int)len, data, ts->i2caddr);
+
+	mutex_lock(&ts->tr_mutex);
+
+	msg.addr = ts->i2caddr;
+	msg.flags = ts->client->flags & I2C_M_TEN;
+	msg.len = len;
+	msg.buf = (char *)data;
+
+	rc = i2c_transfer(adap, &msg, 1);
+	if (rc != 1)
+		dev_err(&ts->client->dev,
+			"i2c_transfer write fail, rc=%d\n", rc);
+
+	mutex_unlock(&ts->tr_mutex);
+
+	/* If everything went ok (i.e. 1 msg transmitted), return #0
+	   transmitted, else error code. */
+	return (rc < 0) ? -EIO : 0;
+
+}
+
+/**
+ *	elan_get_data - get command to TP.
+ *
+ *	@client: our i2c device
+ *	@data: data to be received from TP.
+ *	@len: command length usually.
+ *
+ *	get data from our TP.
+ */
+static int elan_get_data(struct i2c_client *client, const u8 *buf, size_t len)
+{
+	struct elants_data *ts = i2c_get_clientdata(client);
+	struct i2c_adapter *adap = client->adapter;
+	struct i2c_msg msg;
+	int rc = 0;
+
+	dev_dbg(&client->dev, "Enter: %s id:0x%x\n", __func__, ts->i2caddr);
+
+	mutex_lock(&ts->tr_mutex);
+
+	msg.addr = ts->i2caddr;
+	msg.flags = client->flags & I2C_M_TEN;
+	msg.flags |= I2C_M_RD;
+	msg.len = len;
+	msg.buf = (char *)buf;
+
+	rc = i2c_transfer(adap, &msg, 1);
+	if (rc != 1)
+		dev_err(&client->dev, "i2c_transfer read fail, rc=%d\n", rc);
+
+	elan_dbg(client, "%s len=%zu data:%*phC\n",
+		 __func__, len, (int)len, buf);
+
+	mutex_unlock(&ts->tr_mutex);
+
+	/* If everything went ok (i.e. 1 msg transmitted), return #0
+	   transmitted, else error code. */
+	return rc != 1 ? -EIO : 0;
+}
+
+/**
+ *	elan_i2c_read_block
+ *
+ *	@client: our i2c device
+ *	@cmd: command
+ *	@val: data to be received from TP.
+ *	@len: command length usually.
+ *
+ *	get data from our TP by continueous read.
+ */
+static int elan_i2c_read_block(struct i2c_client *client,
+			       u8 *cmd, u8 *val, u16 len)
+{
+	struct i2c_msg msgs[2];
+	int ret;
+
+	dev_dbg(&client->dev, "Enter: %s\n", __func__);
+
+	msgs[0].addr = client->addr;
+	msgs[0].flags = client->flags & I2C_M_TEN;
+	msgs[0].len = 4;
+	msgs[0].buf = cmd;
+
+	msgs[1].addr = client->addr;
+	msgs[1].flags = client->flags & I2C_M_TEN;
+	msgs[1].flags |= I2C_M_RD;
+	msgs[1].len = len;
+	msgs[1].buf = val;
+
+	ret = i2c_transfer(client->adapter, msgs, 2);
+	return (ret == 2) ? len : ret;
+}
+
+static int elan_dbfs_open(struct inode *inode, struct file *file)
+{
+	int retval = 0;
+	struct elants_data *ts = inode->i_private;
+
+	dev_dbg(&ts->client->dev, "Enter %s\n", __func__);
+
+	if (!ts)
+		return -ENODEV;
+
+	retval = mutex_lock_interruptible(&ts->dbfs_mutex);
+	if (retval)
+		return retval;
+
+	if (!kobject_get(&ts->client->dev.kobj)) {
+		retval = -ENODEV;
+		goto dbfs_out;
+	}
+
+	file->private_data = ts;
+dbfs_out:
+	mutex_unlock(&ts->dbfs_mutex);
+	return retval;
+}
+
+static ssize_t elan_dbfs_read(struct file *file,
+			      char __user *buffer, size_t count, loff_t *ppos)
+{
+	u8 rxbuf[256];
+	int ret;
+	struct elants_data *ts = file->private_data;
+	struct i2c_adapter *adap = ts->client->adapter;
+	struct i2c_msg msg;
+
+	if (count > 256)
+		return -EMSGSIZE;
+
+	msg.addr = ts->i2caddr;
+	msg.flags = ts->client->flags & I2C_M_TEN;
+	msg.flags |= I2C_M_RD;
+	msg.len = count;
+	msg.buf = rxbuf;
+
+	ret = i2c_transfer(adap, &msg, 1);
+	if (ret == 1)
+		if (copy_to_user(buffer, rxbuf, count))
+			return -EFAULT;
+
+	/* If everything went ok (i.e. 1 msg transmitted), return #bytes
+	   transmitted, else error code. */
+	return (ret == 1) ? count : ret;
+}
+
+static ssize_t elan_dbfs_write(struct file *file,
+			       const char __user *buffer, size_t count,
+			       loff_t *ppos)
+{
+	int ret;
+	u8 txbuf[256];
+	struct elants_data *ts = file->private_data;
+	struct i2c_adapter *adap = ts->client->adapter;
+	struct i2c_msg msg;
+
+	if (count > 256)
+		return -EMSGSIZE;
+
+	if (copy_from_user(txbuf, buffer, count))
+		return -EFAULT;
+
+	msg.addr = ts->i2caddr;
+	msg.flags = ts->client->flags & I2C_M_TEN;
+	msg.len = count;
+	msg.buf = (char *)txbuf;
+
+	ret = i2c_transfer(adap, &msg, 1);
+	if (ret != 1)
+		dev_err(&ts->client->dev,
+			"i2c_master_send fail, ret=%d\n", ret);
+
+	/* If everything went ok (i.e. 1 msg transmitted), return #bytes
+	   transmitted, else error code. */
+	return (ret == 1) ? count : ret;
+}
+
+static int elan_dbfs_release(struct inode *inode, struct file *file)
+{
+	struct elants_data *ts = file->private_data;
+
+	if (!ts)
+		return -ENODEV;
+
+	if (ts->dbfs_root) {
+		debugfs_remove_recursive(ts->dbfs_root);
+		mutex_destroy(&ts->dbfs_mutex);
+	}
+
+	return 0;
+}
+
+static const struct file_operations elan_debug_fops = {
+	.owner = THIS_MODULE,
+	.open = elan_dbfs_open,
+	.release = elan_dbfs_release,
+	.read = elan_dbfs_read,
+	.write = elan_dbfs_write,
+};
+
+static int elan_dbfs_init(struct elants_data *ts)
+{
+	/* Create a global debugfs root for all elan ts devices */
+	ts->dbfs_root = debugfs_create_dir(DEVICE_NAME, NULL);
+	if (ts->dbfs_root == ERR_PTR(-ENODEV))
+		ts->dbfs_root = NULL;
+
+	mutex_init(&ts->dbfs_mutex);
+
+	debugfs_create_file("elan-iap", 0777,
+			    ts->dbfs_root, ts, &elan_debug_fops);
+
+	return 0;
+}
+
+/**
+ * elan_sw_reset - software reset.
+ *
+ * @client: our i2c device
+ *	retval >0 means reset success,
+ *	otherwise is fail.
+ *
+ */
+static int elan_sw_reset(struct i2c_client *client)
+{
+	int ret;
+	const u8 srst[4] = { 0x77, 0x77, 0x77, 0x77 };
+
+	dev_dbg(&client->dev, "Enter: %s\n", __func__);
+
+	ret = i2c_master_send(client, srst, sizeof(srst));
+
+	if (ret != sizeof(srst)) {
+		dev_err(&client->dev, "%s: i2c_master_send failed\n", __func__);
+		return -ENODEV;
+	}
+
+	/* Wait to send fastboot command */
+	elan_msleep(10);
+
+	return ret;
+}
+
+static int elan_boot(struct i2c_client *client, u16 addr, u8 type)
+{
+	int rc;
+	struct elants_data *ts = i2c_get_clientdata(client);
+	uint8_t command[2][4] = {
+		{0x4D, 0x61, 0x69, 0x6E},
+		{0x45, 0x49, 0x41, 0x50},
+	};
+
+	ts->i2caddr = addr;
+	rc = elan_set_data(client, command[(int32_t) type], 4);
+	if (rc < 0) {
+		if (type == E_BOOT_IAP)
+			dev_err(&client->dev, "Boot IAP fail, error=%d\n", rc);
+		else
+			dev_dbg(&client->dev,
+				"Boot normal fail, error=%d\n", rc);
+		ts->i2caddr = DEV_MASTER;
+		return -EINVAL;
+	}
+	dev_info(&client->dev, "Boot success -- 0x%x\n", addr);
+	ts->i2caddr = DEV_MASTER;
+	return 0;
+}
+
+/**
+ *	__hello_packet_handler	- hadle hello packet.
+ *
+ *	@client: our i2c device
+ *	@return:
+ *		>0 means success,
+ *		otherwise fail.
+ *
+ *	Normal hello packet is {0x55, 0x55, 0x55, 0x55}
+ *	recovery mode hello packet is {0x55, 0x55, 0x80, 0x80}
+ */
+static int __hello_packet_handler(struct i2c_client *client)
+{
+	int rc = 0;
+	uint8_t buf_recv[4] = { 0 };
+	struct elants_data *ts = i2c_get_clientdata(client);
+
+	dev_dbg(&client->dev, "Enter: %s\n", __func__);
+
+	rc = elan_get_data(client, buf_recv, 4);
+
+	/* Print buf_recv anyway */
+	dev_info(&client->dev, "rc=%d HelloPacket:%*phC\n",
+			rc, 4, buf_recv);
+
+	if (rc < 0) {
+		dev_err(&client->dev,
+			"%s: Try recovery because of no hello\n", __func__);
+	}
+
+	if (memcmp(buf_recv, hello_packet, 4)) {
+		if (!memcmp(buf_recv, recov_packet, 4)) {
+			dev_info(&client->dev,
+				 "got mainflow recovery message\n");
+
+			ts->iap_mode = IAP_MODE_ENABLE;
+			set_bit(LOCK_FW_UPDATE, &ts->flags);
+		}
+
+		return -ENODEV;
+	}
+
+	ts->i2caddr = DEV_MASTER;
+
+	return rc;
+}
+
+/**
+ * Firmware version is for something big movement.
+ * ex: CodeBase update.
+ */
+static int __fw_packet_handler(struct i2c_client *client)
+{
+	struct elants_data *ts = i2c_get_clientdata(client);
+	int rc, tries = 3;
+	const uint8_t cmd[] = { CMD_HEADER_READ,
+		E_ELAN_INFO_FW_VER, 0x00, 0x01
+	};
+	uint8_t buf_recv[4] = { 0x0 };
+
+	dev_dbg(&client->dev, "Enter: %s\n", __func__);
+
+	/* Command not support in IAP recovery mode */
+	if (test_bit(LOCK_FW_UPDATE, &ts->flags))
+		return 0;
+retry:
+	rc = elan_i2c_read_block(client, (u8 *) cmd, buf_recv, 4);
+	if (rc < 0)
+		elan_dbg(client,
+			 "read fw version rc=%d, buf=%*phC\n", rc, 4, buf_recv);
+
+	if (buf_recv[0] == CMD_HEADER_RESP) {
+		ts->_fw_version[0] = ((buf_recv[1] & 0x0f) << 4) |
+		    ((buf_recv[2] & 0xf0) >> 4);
+		ts->_fw_version[1] = ((buf_recv[2] & 0x0f) << 4) |
+		    ((buf_recv[3] & 0xf0) >> 4);
+
+		if (ts->fw_version == 0x00 || ts->fw_version == 0xFF) {
+			dev_err(&client->dev,
+				"\n\nFW version is empty, "
+				"suggest IAP ELAN chip\n\n");
+			return -EINVAL;
+		}
+	} else {
+		elan_dbg(client, "read fw retry tries=%d\n", tries);
+		if (tries > 0) {
+			tries--;
+			goto retry;
+		}
+
+		ts->fw_version = 0xffff;
+		dev_err(&client->dev,
+			"\n\nFW version is empty, "
+			"suggest IAP ELAN chip\n\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * Test version is for something minor change.
+ * ex: parameters, coordination
+ */
+static int __test_packet_handler(struct i2c_client *client)
+{
+	struct elants_data *ts = i2c_get_clientdata(client);
+	int rc, tries = 3;
+	const uint8_t cmd[] = { CMD_HEADER_READ,
+		E_ELAN_INFO_TEST_VER, 0x00, 0x01
+	};
+	uint8_t buf_recv[4] = { 0x0 };
+
+	dev_dbg(&client->dev, "Enter: %s\n", __func__);
+
+	/* Command not support in IAP recovery mode */
+	if (test_bit(LOCK_FW_UPDATE, &ts->flags))
+		return 0;
+retry:
+	rc = elan_i2c_read_block(client, (u8 *) cmd, buf_recv, 4);
+	if (rc < 0) {
+		elan_dbg(client, "read test version error rc=%d, buf=%*phC\n",
+			 rc, 4, buf_recv);
+		return rc;
+	}
+
+	if (buf_recv[0] == CMD_HEADER_RESP) {
+		ts->_test_version[0] = ((buf_recv[1] & 0x0f) << 4) |
+		    ((buf_recv[2] & 0xf0) >> 4);
+		ts->_test_version[1] = ((buf_recv[2] & 0x0f) << 4) |
+		    ((buf_recv[3] & 0xf0) >> 4);
+	} else {
+		elan_dbg(client, "read fw retry tries=%d\n", tries);
+		if (tries > 0) {
+			tries--;
+			goto retry;
+		}
+		ts->test_version = 0xffff;
+		dev_err(&client->dev, "Get test version fail\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int __tp_info_handler(struct i2c_client *client)
+{
+	struct i2c_msg msgs[2];
+	struct elants_data *ts = i2c_get_clientdata(client);
+	int rc;
+	uint8_t buf_recv[17] = { 0x0 };
+	const uint8_t get_resolution_cmd[] = {
+		CMD_HEADER_6B_READ, 0x00, 0x00, 0x00, 0x00, 0x00
+	};
+
+	dev_dbg(&client->dev, "Enter: %s\n", __func__);
+
+	/* Command not support in IAP recovery mode */
+	if (test_bit(LOCK_FW_UPDATE, &ts->flags))
+		return 0;
+
+	msgs[0].addr = client->addr;
+	msgs[0].flags = client->flags & I2C_M_TEN;
+	msgs[0].len = 6;
+	msgs[0].buf = (u8 *) get_resolution_cmd;
+
+	msgs[1].addr = client->addr;
+	msgs[1].flags = client->flags & I2C_M_TEN;
+	msgs[1].flags |= I2C_M_RD;
+	msgs[1].len = sizeof(buf_recv);
+	msgs[1].buf = buf_recv;
+
+	rc = i2c_transfer(client->adapter, msgs, 2);
+	if (rc < 0) {
+		elan_dbg(client, "tp_info error rc=%d\n", rc);
+		return rc;
+	}
+
+	if (buf_recv[0] != CMD_HEADER_6B_RESP)
+		return -EINVAL;
+
+	ts->rows = (buf_recv[2] + buf_recv[6] + buf_recv[10]);
+	ts->cols = (buf_recv[3] + buf_recv[7] + buf_recv[11]);
+
+	if (ts->rows < 2 || ts->cols < 2) {
+		dev_warn(&client->dev,
+			 "Invalid resolution (%d, %d)\n", ts->rows, ts->cols);
+
+		ts->rows = -1;
+		ts->cols = -1;
+
+		return 0;
+	}
+
+	return 0;
+}
+
+/**
+*	elan_touch_get_bc_ver	- obtain bootcode data from device
+*
+*	@client: the interface we are querying
+*
+*	Send a bootcode version command and fill the results into the
+*	elan device structures. Caller must hold the mutex
+*
+*	Returns 0 or an error code
+*/
+static int __bc_packet_handler(struct i2c_client *client)
+{
+	struct elants_data *ts = i2c_get_clientdata(client);
+	const u8 get_bc_ver_cmd[] = { CMD_HEADER_READ,
+		E_ELAN_INFO_BC_VER, 0x00, 0x01
+	};
+	u8 buf_recv[4];
+	int rc;
+
+	dev_dbg(&client->dev, "Enter: %s\n", __func__);
+
+	/* Command not support in IAP recovery mode */
+	if (test_bit(LOCK_FW_UPDATE, &ts->flags))
+		return 0;
+
+	rc = elan_i2c_read_block(client, (u8 *) get_bc_ver_cmd,
+				 buf_recv, sizeof(buf_recv));
+	if (rc < 0) {
+		dev_err(&client->dev,
+			"Read FW version error rc=%d, buf=%*phC\n", rc, 4,
+			buf_recv);
+		return rc;
+	}
+
+	ts->bc_version = (((buf_recv[1] & 0x0f) << 4) |
+			  ((buf_recv[2] & 0xf0) >> 4));
+	ts->iap_version = (((buf_recv[2] & 0x0f) << 4) |
+			   ((buf_recv[3] & 0xf0) >> 4));
+	return 0;
+}
+
+static int __elan_fastboot(struct i2c_client *client)
+{
+	int rc = 0;
+
+	dev_dbg(&client->dev, "Enter: %s\n", __func__);
+
+	rc = elan_boot(client, DEV_MASTER, E_BOOT_NORM);
+	if (rc < 0)
+		return -1;
+
+	/* Wait for Hello packets */
+	msleep(50);
+
+	return rc;
+}
+
+static int elan_open(struct input_dev *input)
+{
+	struct elants_data *ts = input_get_drvdata(input);
+
+	dev_dbg(&ts->client->dev, "Enter: %s\n", __func__);
+
+	return 0;
+}
+
+static void elan_close(struct input_dev *input)
+{
+	struct elants_data *ts = input_get_drvdata(input);
+
+	dev_dbg(&ts->client->dev, "Enter %s\n", __func__);
+
+	return;
+}
+
+/**
+*	elan_touch_pull_frame - pull a frame from the fifo
+*
+*	@ts: our elan touch device
+*	@buf: data buffer
+*
+*	Pulls a frame from the FIFO into the provided ehr and data buffer.
+*	The data buffer must be at least CMD_RESP_LEN bytes long.
+*/
+static int elan_touch_pull_frame(struct elants_data *ts, u8 *buf)
+{
+	dev_dbg(&ts->client->dev, "Enter: %s\n", __func__);
+
+	WARN_ON(kfifo_out_locked(&ts->fifo, buf, CMD_RESP_LEN,
+				 &ts->rx_kfifo_lock)
+		!= CMD_RESP_LEN);
+	return CMD_RESP_LEN;
+}
+
+/**
+ *	elan_touch_fifo_clean_old - Make room for new frames
+ *
+ *	@ed: our elan touch device
+ *	@room: space needed
+ *
+ *	Empty old frames out of the FIFO until we can fit the new one into
+ *	the other end.
+ */
+static void elan_touch_fifo_clean_old(struct elants_data *ts, int room)
+{
+	u8 buffer[CMD_RESP_LEN];
+
+	dev_dbg(&ts->client->dev, "Enter: %s\n", __func__);
+
+	while (kfifo_len(&ts->fifo) + room >= FIFO_SIZE)
+		elan_touch_pull_frame(ts, buffer);
+}
+
+/**
+ *	elan_getrepoinfo - parse Multi-queue report header
+ *
+ *	@client: our i2c device
+ *	@buf: buffer data
+ *
+ *	parsing report header and get data length.
+ *
+ */
+static int elan_getrepoinfo(struct i2c_client *client, uint8_t *buf)
+{
+	struct elants_data *ts = i2c_get_clientdata(client);
+	struct multi_queue_header *buff = (struct multi_queue_header *)buf;
+	struct multi_queue_header *mq = &ts->mq_header;
+	const u8 wait_packet[4] = { 0x64, 0x64, 0x64, 0x64 };
+	int times = 10, rc = 0;
+
+	dev_dbg(&client->dev, "Enter: %s\n", __func__);
+
+	switch (buf[FW_POS_HEADER]) {
+	case CMD_HEADER_HELLO:
+		if (!memcmp(buf, hello_packet, 4))
+			ts->wdt_reset++;
+		return RET_CMDRSP;
+	case CMD_HEADER_RESP:
+	case CMD_HEADER_REK:
+		/* Queue the data, using the fifo lock to serialize
+		 * the multiple accesses to the FIFO
+		 */
+		elan_dbg(client, "recv CMD_HEADER_RESP\n");
+
+		mutex_lock(&ts->fifo_mutex);
+		if (kfifo_len(&ts->fifo) + CMD_RESP_LEN >= FIFO_SIZE)
+			elan_touch_fifo_clean_old(ts, CMD_RESP_LEN);
+		/* Push the data */
+		kfifo_in_locked(&ts->fifo, buf,
+				CMD_RESP_LEN, &ts->rx_kfifo_lock);
+		mutex_unlock(&ts->fifo_mutex);
+
+		elan_dbg(client, "wake_up [%*phC]\n", 4, buf);
+		wake_up_interruptible(&ts->wait);
+		return RET_CMDRSP;
+		/* Buffer mode header */
+	case QUEUE_HEADER_NORMAL:
+		elan_dbg(client,
+			 "report_count=%d report_len=%d\n",
+			 buff->report_count, buff->report_length);
+
+		if (buff->report_count <= 3) {
+			mq->report_count = buff->report_count;
+			mq->report_length = buff->report_length;
+			ts->rx_size = mq->report_length;
+			ts->packet_size = mq->report_length / mq->report_count;
+		} else
+			return RET_FAIL;
+
+		break;
+	case QUEUE_HEADER_WAIT:
+		dev_info(&client->dev,
+			 "QUEUE_HEADER_WAIT %x:%x:%x:%x\n",
+			 buf[0], buf[1], buf[2], buf[3]);
+		/* BUGFIX: buff[0] might be wrong (0x63),
+		 * check buff[1 ~ 3] is enough
+		 */
+		if (!memcmp(buf + 1, &wait_packet[1], 3)) {
+			do {
+				udelay(30);
+				elan_get_data(client, (u8 *) buff, ts->rx_size);
+			} while ((buff->packet_id != QUEUE_HEADER_NORMAL) &&
+				 (--times > 0));
+			if (times > 0)
+				rc = elan_getrepoinfo(client, (uint8_t *) buff);
+			else
+				return RET_FAIL;
+			elan_dbg(client,
+				 "Detect Wait_Header:rx_size=%d, "
+				 "report_count=%d report_len=%d\n",
+				 ts->rx_size,
+				 mq->report_count, mq->report_length);
+		} else
+			dev_err(&client->dev,
+				"ERROR!! wait header:%x:%x:%x:%x\n",
+				buf[0], buf[1], buf[2], buf[3]);
+		break;
+		/* Not buffer mode, it's single word mode */
+	case QUEUE_HEADER_SINGLE:
+		mq->report_count = 1;
+		mq->report_length = PACKET_SIZE;
+		ts->rx_size = mq->report_length;
+		return ts->rx_size;
+	default:
+		dev_err(&client->dev,
+			"unknown multi-queue command!! --%x %x:%x:%x--\n",
+			buf[0], buf[1], buf[2], buf[3]);
+		ts->mq_header_fail++;
+
+		/* If glitch causes frame error, drop all finger report */
+		ts->rx_size = MAX_PACKET_LEN;
+		elan_get_data(client, (u8 *) buff, ts->rx_size);
+		return RET_FAIL;
+	}
+
+	return ts->rx_size;
+}
+
+/**
+ *	elan_touch_checksum -  Add for checksum mechanism
+ *
+ *	@client : our i2c device
+ *	@buf : buffer data
+ *
+ *	caculating checksum for make sure all data validity.
+ */
+static int elan_touch_checksum(struct i2c_client *client, u8 *buf)
+{
+	u8 i = 0, checksum = 0;
+	struct elants_data *ts = i2c_get_clientdata(client);
+
+	dev_dbg(&client->dev, "Enter: %s\n", __func__);
+
+	for (i = 0; i < FW_POS_CHECKSUM; i++)
+		checksum = checksum + buf[i];
+
+	ts->checksum_correct = checksum;
+
+	if (checksum != buf[FW_POS_CHECKSUM]) {
+		ts->checksum_fail++;
+		dev_err(&client->dev,
+			"elan touch checksum fail: %02x:%02x\n",
+			checksum, buf[FW_POS_CHECKSUM]);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+/**
+ *	elan_parse_fid - parse the 10 fid bits
+ *
+ *	@data: the input bit stream
+ *	@fid: an array of fid values
+ *
+ *	Unpack the 10 bits into an array.
+ *	FIXME: Review whether we can't just use << operators after making
+ *	sure the bits are native endian ?
+ */
+static inline void elan_parse_fid(u8 *data, u8 *fid)
+{
+	fid[0] = (data[0] & 0x01);
+	fid[1] = (data[0] & 0x02);
+	fid[2] = (data[0] & 0x04);
+	fid[3] = (data[0] & 0x08);
+	fid[4] = (data[0] & 0x10);
+	fid[5] = (data[0] & 0x20);
+	fid[6] = (data[0] & 0x40);
+	fid[7] = (data[0] & 0x80);
+	fid[8] = (data[1] & 0x10);
+	fid[9] = (data[1] & 0x20);
+}
+
+/**
+ *      elan_parse_wid - parse width data with length of 5-bit
+ *
+ *      @data: the input bit stream
+ *      @wid: an array of width level
+ *
+ *      Unpack the 10 bits into an array.
+ */
+static inline void elan_parse_wid(u8 *data, u8 *wid)
+{
+	wid[0] = (data[0] & 0x1f);
+	wid[1] = (data[1] & 0x1f);
+	wid[2] = (data[2] & 0x1f);
+	wid[3] = (data[3] & 0x1f);
+	wid[4] = (data[4] & 0x1f);
+	wid[5] = (data[5] & 0x1f);
+	wid[6] = (data[6] & 0x1f);
+	wid[7] = (data[7] & 0x1f);
+	wid[8] = (data[8] & 0x1f);
+	wid[9] = (data[9] & 0x1f);
+
+	return;
+}
+
+/**
+ *      elan_parse_pid - parse pressure data with length of 8-bit
+ *
+ *      @data: the input bit stream
+ *      @wid: an array of width level
+ *
+ *      Unpack the 10 bits into an array.
+ */
+static inline void elan_parse_pid(u8 *data, u8 *pressure)
+{
+	pressure[0] = data[0];
+	pressure[1] = data[1];
+	pressure[2] = data[2];
+	pressure[3] = data[3];
+	pressure[4] = data[4];
+	pressure[5] = data[5];
+	pressure[6] = data[6];
+	pressure[7] = data[7];
+	pressure[8] = data[8];
+	pressure[9] = data[9];
+
+	return;
+}
+
+static inline int elan_parse_xy(uint8_t *data, uint16_t *x, uint16_t *y)
+{
+	*x = *y = 0;
+
+	*x = (data[0] & 0xf0);
+	*x <<= 4;
+	*x |= data[1];
+
+	*y = (data[0] & 0x0f);
+	*y <<= 8;
+	*y |= data[2];
+
+	return 0;
+}
+
+static inline int elan_lookup_wid(u8 data, u16 *w, u16 *h)
+{
+	static u16 pre_w = 0, pre_h = 0, cur_w, cur_h;
+	const u8 width_lookup_table[15][2] = {
+		{3, 2}, {3, 2}, {6, 3},
+		{6, 3}, {10, 3}, {15, 4},
+		{15, 5}, {15, 5}, {15, 5},
+		{15, 5}, {15, 5}, {15, 5},
+		{15, 5}, {15, 5}, {15, 5},
+	};
+
+	cur_w = width_lookup_table[data][0] * 17;
+	cur_h = width_lookup_table[data][1] * 17;
+
+	*w = (cur_w + pre_w) >> 1;
+	*h = (cur_h + pre_h) >> 1;
+
+	pre_w = cur_w;
+	pre_h = cur_h;
+
+	return 0;
+}
+
+static int elan_mt_compute_slot(struct mt_device *td)
+{
+	int i;
+	for (i = 0; i < td->maxcontacts; ++i) {
+		if (td->slots[i].contactid == td->curdata.contactid &&
+		    td->slots[i].touch_state)
+			return i;
+	}
+	for (i = 0; i < td->maxcontacts; ++i) {
+		if (!td->slots[i].seen_in_this_frame &&
+		    !td->slots[i].touch_state)
+			return i;
+	}
+	/* should not occurs. If this happens that means
+	 * that the device sent more touches that it says
+	 * in the report descriptor. It is ignored then. */
+	return -1;
+}
+
+/*
+* this function is called when a whole contact has been processed,
+* so that it can assign it to a slot and store the data there
+*/
+static void elan_mt_complete_slot(struct mt_device *td)
+{
+	td->curdata.seen_in_this_frame = true;
+	if (td->curvalid) {
+		int slotnum = elan_mt_compute_slot(td);
+
+		if (slotnum >= 0 && slotnum < td->maxcontacts)
+			td->slots[slotnum] = td->curdata;
+	}
+	td->num_received++;
+}
+
+/*
+* this function is called when a whole packet has been received and processed,
+* so that it can decide what to send to the input layer.
+*/
+static void elan_mt_emit_event(struct mt_device *td, struct input_dev *input)
+{
+	struct elants_data *ts = container_of(td, struct elants_data, td);
+	int i;
+
+	for (i = 0; i < td->maxcontacts; ++i) {
+		struct mt_slot *s = &(td->slots[i]);
+		if (!s->seen_in_this_frame)
+			s->touch_state = false;
+
+		input_mt_slot(input, i);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER,
+					   s->touch_state);
+		if (s->touch_state) {
+			/* this finger is on the screen */
+			int major = max(s->w, s->h), minor = min(s->w, s->h);
+
+			elan_dbg(ts->client, "i=%d x=%d y=%d p=%d w=%d h=%d.\n",
+				 i, s->x, s->y, s->p, major, minor);
+
+			input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x);
+			input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y);
+			input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p);
+			input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
+		}
+		s->seen_in_this_frame = false;
+	}
+
+	input_mt_report_pointer_emulation(input, true);
+	input_sync(input);
+	td->num_received = 0;
+}
+
+/**
+ *	elan_mt_event - process finger reports
+ *	@ts: our touchscreen
+ *	@finger_stat: number of fingers in packet
+ *	@buf: received buffer
+ *
+ *	Walk the received report and process the finger data, extracting
+ *	and reporting co-ordinates. No locking is needed here as the workqueue
+ *	does our threading for us.
+ */
+static int elan_mt_event(struct elants_data *ts, int finger_stat, u8 *buf)
+{
+	int i;
+	u8 fid[MAX_CONTACT_NUM] = { 0 };
+	u8 wid[MAX_CONTACT_NUM] = { 0 };
+	u8 pid[MAX_CONTACT_NUM] = { 0 };
+	u16 x = 0, y = 0, w = 0, h = 0;
+	struct i2c_client *client = ts->client;
+	struct mt_device *td = &ts->td;
+
+	dev_dbg(&client->dev, "Enter: %s\n", __func__);
+
+	/* Parsing Finger, Width, Pressure field */
+	elan_parse_fid(&buf[FW_POS_STATE], &fid[0]);
+	elan_parse_wid(&buf[FW_POS_WIDTH], &wid[0]);
+	elan_parse_pid(&buf[FW_POS_PRESSURE], &pid[0]);
+
+	/* number of fingers expects in this frame */
+	td->num_expected = finger_stat;
+	for (i = 0; i < td->maxcontacts; i++) {
+		if (finger_stat == 0)
+			break;
+
+		/* tracking id */
+		td->curdata.contactid = (fid[i] > 0) ? i + 1 : 0;
+
+		if (td->curdata.contactid == 0)
+			continue;
+
+		td->curdata.touch_state = true;
+
+		elan_parse_xy(&buf[3 + i * 3], &x, &y);
+		td->curdata.x = x;
+		td->curdata.y = y;
+		td->curdata.p = pid[i];
+
+		h = w = wid[i];
+		td->curdata.w = w;
+		td->curdata.h = h;
+
+		finger_stat--;
+
+		elan_mt_complete_slot(td);
+	}
+
+	if (td->num_received >= td->num_expected)
+		elan_mt_emit_event(td, ts->input);
+
+	return 1;
+}
+
+/**
+ *	elan_report_data - report finger report to user space.
+ *
+ *	@client : our i2c device
+ *	@buf : raw data from TP device.
+ *
+ *	- reporting finger data to user space.
+ *
+ */
+static void elan_report_data(struct i2c_client *client, uint8_t *buf)
+{
+	struct elants_data *ts = i2c_get_clientdata(client);
+
+	dev_dbg(&client->dev, "Enter: %s\n", __func__);
+
+	switch (buf[FW_POS_HEADER]) {
+	case REPORT_HEADER_10_FINGER:{
+			u8 finger_stat = buf[FW_POS_TOTAL] & 0x0f;
+			elan_dbg(client, "finger_stat == %d\n", finger_stat);
+			elan_dbg(client, "finger:%*phC\n", 10, buf);
+
+			/* Enter right process, reset int_status */
+			ts->packet_received++;
+
+			if (likely(finger_stat != 0)) {
+				ts->td.curvalid = true;
+				ts->touched_sync++;
+			} else {
+				ts->no_touched_sync++;
+			}
+			elan_mt_event(ts, finger_stat, buf);
+		}
+		break;
+	default:
+		ts->header_fail++;
+		dev_warn(&client->dev,
+			 "%s: unknown packet type: %*phC\n", __func__, 10, buf);
+		break;
+	}
+
+	return;
+}
+
+static irqreturn_t elan_work_func(int irq, void *work)
+{
+	struct elants_data *ts = work;
+	struct i2c_client *client = ts->client;
+
+	uint8_t buf[MAX_PACKET_LEN] = { 0x0 };
+	u8 pos = 0, rc = 0;
+
+	dev_dbg(&client->dev, "Enter: %s\n", __func__);
+
+	ts->irq_received++;
+
+	if (test_bit(LOCK_FW_UPDATE, &ts->flags))
+		return IRQ_HANDLED;
+
+	mutex_lock(&ts->mutex);
+
+	set_bit(LOCK_FINGER_REPORT, &ts->flags);
+
+	/* - Read multi_queue header */
+	rc = elan_get_data(client, (u8 *) buf, ts->rx_size);
+	if (rc < 0)
+		goto fail;
+
+	/*  - Get multi_queue header info */
+	rc = elan_getrepoinfo(ts->client, buf);
+	if (rc < 0 || rc == RET_CMDRSP)
+		goto fail;
+
+	/*  - check if packet size is valid */
+	if ((ts->packet_size != PACKET_SIZE)) {
+		dev_err(&ts->client->dev, "%s: uncorrect packet size = %d\n",
+			__func__, ts->packet_size);
+		goto fail;
+	}
+
+	/* - Get finger report data */
+	rc = elan_get_data(client, (u8 *) buf, ts->rx_size);
+	if (rc < 0)
+		goto fail;
+
+	clear_bit(LOCK_FINGER_REPORT, &ts->flags);
+
+	mutex_unlock(&ts->mutex);
+
+	/* - parsing report and send out */
+	while (ts->mq_header.report_count--) {
+		if (elan_touch_checksum(ts->client, buf + pos) == 0)
+			elan_report_data(ts->client, buf + pos);
+		pos = pos + ts->packet_size;
+		udelay(10);
+	}
+
+	ts->rx_size = QUEUE_HEADER_SIZE;
+
+	return IRQ_HANDLED;
+
+fail:
+	clear_bit(LOCK_FINGER_REPORT, &ts->flags);
+	mutex_unlock(&ts->mutex);
+	ts->rx_size = QUEUE_HEADER_SIZE;
+
+	return IRQ_HANDLED;
+}
+
+static int remove_elants(struct i2c_client *client)
+{
+	int ret = 0;
+	struct elants_data *ts = i2c_get_clientdata(client);
+
+	if ((client->irq))
+		free_irq(client->irq, ts);
+
+	if (ts->input)
+		input_unregister_device(ts->input);
+
+	if (&ts->mutex)
+		mutex_destroy(&ts->mutex);
+	if (&ts->tr_mutex)
+		mutex_destroy(&ts->tr_mutex);
+	if (&ts->fifo_mutex)
+		mutex_destroy(&ts->fifo_mutex);
+
+	kfree(ts->td.slots);
+	kfifo_free(&ts->fifo);
+	kfree(ts);
+
+	return ret;
+}
+
+static int elan_input_dev_create(struct elants_data *ts)
+{
+	int err = 0;
+	struct i2c_client *client = ts->client;
+
+	if (ts->rows > 0 && ts->cols > 0) {
+		/* translate trace number to TSP resolution */
+		ts->x_max = ELAN_TS_RESOLUTION(ts->rows);
+		ts->y_max = ELAN_TS_RESOLUTION(ts->cols);
+	} else
+		dev_warn(&client->dev, "trace number error, %d,%d\n",
+			 ts->rows, ts->cols);
+
+	/* Clear the existing one if it exists */
+	if (ts->input) {
+		input_unregister_device(ts->input);
+		ts->input = NULL;
+	}
+
+	ts->input = input_allocate_device();
+	if (ts->input == NULL) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	ts->input->name = "Elan-Touchscreen";
+	ts->input->id.bustype = BUS_I2C;
+	ts->input->dev.parent = &ts->client->dev;
+	ts->input->open = elan_open;
+	ts->input->close = elan_close;
+
+	__set_bit(BTN_TOUCH, ts->input->keybit);
+	__set_bit(EV_ABS, ts->input->evbit);
+	__set_bit(EV_KEY, ts->input->evbit);
+
+	/*! - Single touch input params setup */
+	input_set_abs_params(ts->input, ABS_X, 0, ts->x_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_Y, 0, ts->y_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_PRESSURE, 0, 255, 0, 0);
+	input_abs_set_res(ts->input, ABS_X, X_PIXELS_PER_MM);
+	input_abs_set_res(ts->input, ABS_Y, Y_PIXELS_PER_MM);
+
+	ts->td.maxcontacts = MAX_CONTACT_NUM;
+	ts->td.mt_flags |= INPUT_MT_DIRECT;
+
+	/* Multitouch input params setup */
+	err =
+	    input_mt_init_slots(ts->input, ts->td.maxcontacts, ts->td.mt_flags);
+	if (err) {
+		dev_err(&client->dev,
+			"allocate memory for MT slots failed, %d\n", err);
+		goto err_free_device;
+	}
+
+	input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, ts->x_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->y_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_X, X_PIXELS_PER_MM);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_Y, Y_PIXELS_PER_MM);
+
+	input_set_drvdata(ts->input, ts);
+
+	ts->td.slots = kzalloc(MAX_CONTACT_NUM * sizeof(struct mt_slot),
+			       GFP_KERNEL);
+	if (!ts->td.slots) {
+		dev_err(&client->dev, "cannot allocate multitouch slots\n");
+		goto err_free_device;
+	}
+
+	err = input_register_device(ts->input);
+	if (err) {
+		dev_err(&client->dev, "unable to register input device\n");
+		goto err_free_slot;
+	}
+
+	return 0;
+
+err_free_slot:
+	kfree(ts->td.slots);
+err_free_device:
+	input_free_device(ts->input);
+	ts->input = NULL;
+	return err;
+}
+
+/**
+ *	elan_initialize - initialization process.
+ *
+ *	@client: our i2c client
+ *
+ *	set our TP up
+ *	-# reset
+ *	-# hello packet
+ *	-# fw version
+ *	-# test version
+ *	-# TP info (resolution)
+ *
+ */
+static int elan_initialize(struct i2c_client *client)
+{
+	struct elants_data *ts = i2c_get_clientdata(client);
+	u8 buf[4] = { 0 };
+	int rc;
+
+	dev_dbg(&client->dev, "Enter: %s\n", __func__);
+
+	/* handle recovery packets */
+	rc = elan_get_data(client, buf, 3);
+	if (rc == 0) {
+		if (!memcmp(buf, recov_packet + 2, 2)) {
+			ts->iap_mode = IAP_MODE_ENABLE;
+			set_bit(LOCK_FW_UPDATE, &ts->flags);
+			dev_err(&client->dev, "Get recovery packet == %*phC\n",
+				3, buf);
+
+			return -ENODEV;
+		}
+	}
+
+	rc = elan_sw_reset(client);
+	if (rc < 0) {
+		dev_err(&client->dev, "Software reset failed\n");
+		/* continue */
+	}
+
+	ts->rx_size = QUEUE_HEADER_SIZE;
+
+	rc = __elan_fastboot(client);
+	if (rc < 0)
+		dev_err(&client->dev, "fastboot failed, rc=%d\n", rc);
+
+	/*! - elan hello packet init */
+	rc = __hello_packet_handler(client);
+	if (rc < 0) {
+		dev_err(&client->dev, "hello packet error.\n");
+
+		return -ENODEV;
+	}
+
+	/* elan fw version */
+	rc = __fw_packet_handler(client);
+	if (rc < 0) {
+		dev_err(&client->dev, "firmware checking error rc=%d\n", rc);
+
+		if (rc == -EINVAL) {
+			set_bit(LOCK_FW_UPDATE, &ts->flags);
+			ts->iap_mode = IAP_MODE_ENABLE;
+		}
+	}
+
+	/* elan TP information */
+	rc = __tp_info_handler(client);
+	if (rc < 0) {
+		dev_err(&client->dev, "TP information checking error.\n");
+		/* Go through down */
+	}
+
+	/* elan test version */
+	rc = __test_packet_handler(client);
+	if (rc < 0) {
+		dev_err(&client->dev, "test version error\n");
+		/* Go through down */
+	}
+
+	/* Get TS BootCode version */
+	rc = __bc_packet_handler(client);
+	if (rc < 0) {
+		dev_err(&client->dev, "TP get BC version error.\n");
+		/* Go through down */
+	}
+
+	return 0;
+}
+
+/**
+ *	elan_initialize_async	- init touch device.
+ *
+ *	@work: /INT work queue
+ *
+ *	Perform real probe for our I2C device and if successful configure
+ *	it up as an input device. If not then clean up and return an error
+ *	code.
+ */
+
+static void elan_initialize_async(void *data, async_cookie_t cookie)
+{
+	struct elants_data *ts = data;
+	struct i2c_client *client = ts->client;
+	int err = 0;
+
+	mutex_lock(&ts->mutex);
+
+	err = elan_initialize(client);
+	if (err < 0)
+		dev_err(&client->dev, "probe failed! unbind device.\n");
+
+	err = elan_input_dev_create(ts);
+	if (err) {
+		dev_err(&client->dev, "%s crated failed, %d\n", __func__, err);
+		goto fail_un;
+	}
+
+	dev_info(&client->dev, "Elan Touchscreen Information:\n\r");
+	dev_info(&client->dev,
+		 "    Firmware Version:  0x%04x\n\r", ts->fw_version);
+	dev_info(&client->dev,
+		 "    Test Version:  0x%04x\n\r", ts->test_version);
+	dev_info(&client->dev, "    BC Version:  0x%04x\n\r", ts->bc_version);
+	dev_info(&client->dev, "    IAP Version:  0x%04x\n\r", ts->iap_version);
+	dev_info(&client->dev, "    Trace Num:   %d, %d\n", ts->rows, ts->cols);
+	dev_info(&client->dev, "    Resolution X,Y:  %d,%d\n",
+			ts->x_max, ts->y_max);
+
+	err = request_threaded_irq(client->irq, NULL,
+				   elan_work_func,
+				   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				   client->name, ts);
+	if (err) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		goto fail_un;
+	}
+
+	mutex_unlock(&ts->mutex);
+
+	return;
+
+fail_un:
+	mutex_unlock(&ts->mutex);
+	remove_elants(client);
+	return;
+}
+
+/**
+ *	elan_probe - probe for touchpad
+ *
+ *	Perform setup and probe for our I2C device and if successful configure
+ *	it up as an input device. If not then clean up and return an error
+ *	code.
+ */
+static int elan_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	long err = -1;
+	struct elan_i2c_platform_data *pdata = NULL;
+	struct elants_data *ts =
+	    kzalloc(sizeof(struct elants_data), GFP_KERNEL);
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev,
+			"%s: i2c check functionality error\n", DEVICE_NAME);
+		return -ENODEV;
+	}
+
+	ts = kzalloc(sizeof(struct elants_data), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	mutex_init(&ts->mutex);
+	mutex_init(&ts->tr_mutex);
+	mutex_init(&ts->fifo_mutex);
+	init_waitqueue_head(&ts->wait);
+	spin_lock_init(&ts->rx_kfifo_lock);
+
+	err = elan_dbfs_init(ts);
+	if (err < 0) {
+		dev_err(&client->dev, "error create elan debugfs.\n");
+		goto fail_un;
+	} else
+		ts->fw_enabled = 1;
+
+	pdata = client->dev.platform_data;
+	if (!pdata) {
+		dev_err(&client->dev,
+			"%s No platform data provided\n", DEVICE_NAME);
+	}
+
+	/* set initial i2c address */
+	client->addr = DEV_MASTER;
+	ts->i2caddr = client->addr;
+
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+
+	/* initial kfifo */
+	err = kfifo_alloc(&ts->fifo, FIFO_SIZE, GFP_KERNEL);
+	if (!kfifo_initialized(&ts->fifo)) {
+		dev_err(&client->dev, "%s error kfifo_alloc\n", __func__);
+		goto fail_un;
+	}
+
+	/* Says HELLO to touch device */
+	async_schedule(elan_initialize_async, ts);
+
+	device_init_wakeup(&client->dev, true);
+
+	return 0;
+
+fail_un:
+	remove_elants(client);
+	return err;
+}
+
+static int elan_remove(struct i2c_client *client)
+{
+	return remove_elants(client);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int elan_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elants_data *ts = i2c_get_clientdata(client);
+	const u8 set_sleep_cmd[] = { 0x54, 0x50, 0x00, 0x01 };
+	int rc = 0;
+
+	dev_dbg(&client->dev, "Enter: %s\n", __func__);
+
+	/* Command not support in IAP recovery mode */
+	if (test_bit(LOCK_FW_UPDATE, &ts->flags))
+		return 0;
+
+	mutex_lock(&ts->mutex);
+	rc = elan_set_data(client, set_sleep_cmd, sizeof(set_sleep_cmd));
+	if (rc < 0)
+		dev_err(&client->dev, "suspend command failed!\n");
+
+	if (device_may_wakeup(dev))
+		ts->irq_wake = (enable_irq_wake(client->irq) == 0);
+
+	disable_irq(client->irq);
+
+	mutex_unlock(&ts->mutex);
+
+	return 0;
+}
+
+static int elan_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elants_data *ts = i2c_get_clientdata(client);
+	const u8 set_active_cmd[] = { 0x54, 0x58, 0x00, 0x01 };
+	int rc = 0;
+
+	dev_dbg(&client->dev, "Enter: %s\n", __func__);
+
+	/* Command not support in IAP recovery mode */
+	if (test_bit(LOCK_FW_UPDATE, &ts->flags))
+		return 0;
+
+	if (device_may_wakeup(dev) && ts->irq_wake)
+		disable_irq_wake(client->irq);
+
+	mutex_lock(&ts->mutex);
+
+	rc = elan_set_data(client, set_active_cmd, sizeof(set_active_cmd));
+	if (rc < 0)
+		dev_err(&client->dev, "resume command failed!\n");
+
+	enable_irq(client->irq);
+
+	mutex_unlock(&ts->mutex);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume);
+
+static const struct i2c_device_id elan_ts_id[] = {
+	{DEVICE_NAME, 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, elan_ts_id);
+
+static struct i2c_driver elan_ts_driver = {
+	.probe = elan_probe,
+	.remove = elan_remove,
+	.id_table = elan_ts_id,
+	.driver = {
+		   .name = DEVICE_NAME,
+		   .owner = THIS_MODULE,
+		   .pm = &elan_pm_ops,
+		   },
+};
+
+module_i2c_driver(elan_ts_driver);
+
+MODULE_VERSION(DEVICE_NAME);
+MODULE_DESCRIPTION("Elan I2c Touchscreen driver");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ