[<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