2013-12-27

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

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

Signed-off-by: Scott Liu <scott.liu@emc.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@emc.com.tw>

+ *

+ * This code is partly based on hid-multitouch.c:

+ *

+ * Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr>

+ * Copyright (c) 2010-2012 Benjamin Tissoires <benjamin.tissoires@gmail.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;

+

+ 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.contactid == td->curdata.contactid &&

+ td->slots.touch_state)

+ return i;

+ }

+ for (i = 0; i < td->maxcontacts; ++i) {

+ if (!td->slots.seen_in_this_frame &&

+ !td->slots.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);

+ 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 > 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;

+

+ h = w = wid;

+ 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@vger.kernel.org

More majordomo info at http://vger.kernel.org/majordomo-info.html

Please read the FAQ at http://www.tux.org/lkml/

Show more