This patch is for Elan eKTF Touchscreen product, I2C adpater module.
Signed-off-by: Scott Liu <[email protected]>
---
Hi,
v2 revision I have fixed some bug as your advise.
1. To target the mainline
2. No Android dependency
3. reuse those duplication code from Henrik's patchset.
(input_mt_sync_frame() / input_mt_get_slot_by_key())
drivers/input/touchscreen/Kconfig | 9 +
drivers/input/touchscreen/Makefile | 1 +
drivers/input/touchscreen/elants_i2c.c | 2549 ++++++++++++++++++++++++++++++++
include/linux/i2c/elants.h | 61 +
4 files changed, 2620 insertions(+)
create mode 100644 drivers/input/touchscreen/elants_i2c.c
create mode 100644 include/linux/i2c/elants.h
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 1ba232c..50e6f05 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -237,6 +237,15 @@ config TOUCHSCREEN_EETI
To compile this driver as a module, choose M here: the
module will be called eeti_ts.
+config TOUCHSCREEN_ELAN
+ tristate "Elan touchscreen panel support"
+ depends on I2C
+ help
+ Say Y here to enable support for I2C connected Elan touch panels.
+
+ To compile this driver as a module, choose M here: the
+ module will be called elants_i2c.
+
config TOUCHSCREEN_EGALAX
tristate "EETI eGalax multi-touch panel support"
depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 178eb12..428a631 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -28,6 +28,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..30e135e
--- /dev/null
+++ b/drivers/input/touchscreen/elants_i2c.c
@@ -0,0 +1,2549 @@
+/*
+ * Elan Microelectronics touchpanels with I2C interface
+ *
+ * Copyright (C) 2012 Elan Microelectronics Corporation.
+ * Scott Liu <[email protected]>
+ *
+ * This code is partly based on hid-multitouch.c:
+ *
+ * Copyright (c) 2010-2012 Stephane Chatty <[email protected]>
+ * Copyright (c) 2010-2012 Benjamin Tissoires <[email protected]>
+ * 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/i2c.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <linux/hrtimer.h>
+#include <linux/timer.h>
+#include <linux/kthread.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/buffer_head.h>
+#include <linux/version.h>
+#include <linux/kfifo.h>
+#include <linux/slab.h>
+#include <linux/input/mt.h>
+#include <linux/i2c/elants.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...) \
+ if (debug) \
+ dev_printk(KERN_DEBUG, &client->dev, fmt, ##arg)
+
+/*=================================================
+ * Marco
+ *================================================= */
+#define DRV_NAME "elants_i2c"
+
+#define DRV_MA_VER 2
+#define DRV_MI_VER 0
+#define DRV_SUB_MI_VER 0
+
+#define _str(s) #s
+#define str(s) _str(s)
+#define DRIVER_VERSION str(DRV_MA_VER.DRV_MI_VER.DRV_SUB_MI_VER)
+
+#define IDX_PACKET_SIZE_WIDTH 40
+
+#define MAX_CONTACT_NUM 10
+
+/* FW Power Saving Mode */
+#define PWR_STATE_DEEP_SLEEP 0
+#define PWR_STATE_NORMAL 1
+#define PWR_STATE_MASK BIT(3)
+
+/* 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 };
+
+
+/*! driver status flag, should move to public header file */
+#define STA_NONINIT 0x00000001
+#define STA_INIT 0x00000002
+#define STA_INIT2 0x00000004
+#define STA_INIT3 0x00000100
+#define STA_INIT4 0x00000200
+#define STA_PROBED 0x00000008
+#define STA_ERR_HELLO_PKT 0x00000010
+#define STA_USE_IRQ 0x00000020
+#define STA_SLEEP_MODE 0x00000040
+
+/*=======================================
+ * Structure Definition
+ *=======================================*/
+
+/*! @enum elan i2c address definition */
+enum elan_i2c_addr {
+ elan_i2c_master = 0x10,
+ elan_i2c_slave1 = 0x20,
+ elan_i2c_slave2 = 0x21,
+
+ elan_i2c_maxnum = 3,
+};
+
+/*! @enum finger_report_header Finger report header byte definition */
+enum finger_report_header {
+ idx_coordinate_packet_4_finger = 0x5c,
+ idx_coordinate_packet_10_finger = 0x62,
+};
+
+/*! @enum fw_queue_report_hdr FW Queue report header definition */
+enum fw_queue_report_hdr {
+ queue_header_byte_Single = 0x62,
+ queue_header_byte_normal = 0x63,
+ queue_header_byte_wait = 0x64,
+ queue_header_size = 4,
+ queue_packet_max_len = queue_header_size + (IDX_PACKET_SIZE_WIDTH * 3),
+};
+
+/*! @enum fw_normal_cmd_hdr FW Normal command header definition */
+enum fw_normal_cmd_hdr {
+ cmd_header_byte_write = 0x54,
+ cmd_header_byte_read = 0x53,
+ cmd_header_byte_response = 0x52,
+ cmd_header_byte_hello = 0x55,
+ cmd_response_len = 4
+};
+
+/*! @enum fw_info_pos FW information position */
+enum fw_info_pos {
+ idx_finger_header = 0,
+ idx_finger_state = 1,
+ idx_finger_total = 2,
+ idx_finger_checksum = 34,
+ idx_finger_width = 35,
+ idx_4finger_checksum = 17,
+ idx_finger_width_checksum = 34,
+};
+
+/*! @enum lock_bit Lock bit definition */
+enum lock_bit {
+ idx_file_operate = 0,
+ idx_cmd_handshake = 1,
+ idx_finger_report = 2,
+ idx_update_fw = 3,
+};
+
+enum kfifo_ret {
+ ret_ok = 0,
+ ret_cmdrsp = 1,
+ ret_fail = -1,
+};
+
+typedef enum elan_boot_e {
+ E_BOOT_NORM = 0,
+ E_BOOT_IAP = 1
+} elan_boot_t;
+
+
+/*! @struct <multi_queue_header> */
+struct multi_queue_header {
+ u8 packet_id;
+ u8 report_count;
+ u8 report_length;
+ u8 reserved;
+};
+
+/*! @brief elan_polling */
+struct elan_polling {
+ struct workqueue_struct *elan_wq; /* polling work queue */
+ struct timer_list timer; /* Polling intr_gpio timer */
+ u8 int_status; /* polling intr gpio status */
+};
+
+/* finger handler, refer to hid-multitouch.c */
+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;
+};
+
+
+/**
+*
+* @brief elants_data
+*
+* all variable should include in this struct.
+*
+*/
+struct elants_data {
+ int intr_gpio; /* interupter pin*/
+ int rst_gpio; /* reset pin*/
+ int use_irq;
+ u8 major_fw_version;
+ u8 minor_fw_version;
+ u8 major_bc_version;
+ u8 minor_bc_version;
+ u8 major_hw_id;
+ u8 minor_hw_id;
+ bool fw_enabled; /* True if firmware device enabled*/
+ int rows; /* Panel geometry for input layer*/
+ int cols;
+ int x_max;
+ int y_max;
+ /* Power state 0:sleep 1:active*/
+ u8 power_state;
+ /* TS is in IAP mode already*/
+#define IAP_MODE_ENABLE 1
+ /* 1 : Firmware update mode
+ 0 : normal*/
+ unsigned int iap_mode;
+ unsigned int rx_size; /* Read size in use*/
+
+ /* Multi-queue info */
+ struct multi_queue_header mq_header;
+
+ /* our i2c client*/
+ struct i2c_client *client;
+ /* input device*/
+ struct input_dev *input;
+ /* normal function work queue*/
+ struct work_struct work;
+ /* start probe start*/
+ struct task_struct *thread;
+ /* char device for ioctl and IAP*/
+ struct miscdevice firmware;
+ /* regular polling work thread*/
+ struct work_struct pollingwork;
+ /* regular polling work queue*/
+ struct elan_polling polling;
+
+ /* Protects I2C accesses to device*/
+ struct mutex mutex;
+ /* Protects I2C tx/rx*/
+ struct mutex tr_mutex;
+
+ /* device flags */
+ unsigned long flags;
+
+ /* elan-iap i2c address*/
+ unsigned short i2caddr;
+
+ /* fifo and processing */
+ struct kfifo fifo;
+
+ /* Serialize operations around FIFO */
+ struct mutex fifo_mutex;
+ wait_queue_head_t wait;
+ spinlock_t rx_kfifo_lock;
+
+ /* boot log */
+ u8 boot_log[256];
+
+ /*! Add for TS driver debug */
+ unsigned int status;
+ long int irq_received;
+ long int packet_received;
+ long int packet_fail;
+ 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;
+ u16 drop_frame;
+
+ /* mt device */
+ struct mt_device td;
+};
+
+
+
+/*/============================================
+ * Function prototype
+ *=============================================*/
+
+static void
+elants_drop_packet(struct elants_data *ts);
+static int
+elan_hw_reset(struct i2c_client *client);
+static int
+elan_touch_pull_frame(struct elants_data *ts,
+ u8 *buf);
+
+/*=================================================
+ * Global variable
+ *=================================================*/
+
+/*! for elan-iap char driver */
+static struct miscdevice *private_ts;
+
+#define elants_acqurie_data(a, cmd, buf, c) \
+ elants_async_recv_data(a, cmd, buf, c, c)
+
+/*=================================================
+ * 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);
+}
+
+/**
+ * @brief elan_ts_poll - polling intr pin status.
+ * @param client : our i2c device
+ *
+ * @retval 0 means intr pin is low, \n
+ * otherwise is high.
+ *
+ * polling intr pin untill low and the maximus wait time is \b 200ms.
+ */
+static int elan_ts_poll(struct i2c_client *client)
+{
+ struct elants_data *ts = i2c_get_clientdata(client);
+ int status = 0, retry = 20;
+
+ do {
+ status = gpio_get_value(ts->intr_gpio);
+ elan_dbg(client,
+ "%s: status = %d\n", __func__, status);
+ retry--;
+ mdelay(10);
+ } while (status != 0 && retry > 0);
+
+ elan_dbg(client,
+ "%s: poll interrupt status %s\n",
+ __func__, status == 1 ? "high" : "low");
+ return (status == 0 ? 0 : -ETIMEDOUT);
+}
+
+
+/**
+ * @brief \b elants_async_recv_data - get TP status
+ * @param client : our i2c device
+ * @param cmd : asking command
+ * @param buf : result
+ * @param size : command length usually.
+ *
+ * set command type and TP will return its status in buf.
+ */
+static int elants_async_recv_data(
+ struct i2c_client *client,
+ const uint8_t *cmd,
+ uint8_t *buf, size_t tx_size,
+ size_t rx_size)
+{
+ struct elants_data *ts = i2c_get_clientdata(client);
+ dev_dbg(&client->dev,
+ "[ELAN] Enter: %s\n", __func__);
+
+ if (buf == NULL)
+ return -EINVAL;
+
+ mutex_lock(&ts->tr_mutex);
+ if ((i2c_master_send(client, cmd, tx_size)) != tx_size) {
+ dev_err(&client->dev,
+ "%s: i2c_master_send failed\n", __func__);
+ goto fail;
+ }
+
+ if (unlikely(elan_ts_poll(client) < 0))
+ goto fail;
+ else {
+ if (i2c_master_recv(client, buf, rx_size) != rx_size)
+ goto fail;
+ mutex_unlock(&ts->tr_mutex);
+ }
+
+ return 0;
+
+fail:
+ mutex_unlock(&ts->tr_mutex);
+ return -EINVAL;
+}
+
+
+/**
+ * @brief \b elan_ts_set_data - set command to TP.
+ * @param client : our i2c device
+ * @param data : command or data which will send to TP.
+ * @param len : command length usually.
+ *
+ * set command to our TP.
+ */
+static int elants_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, "[ELAN] Enter: %s\n", __func__);
+ elan_dbg(client,
+ "dump cmd: %02x, %02x, %02x, %02x, addr=%x\n",
+ data[0], data[1], data[2], data[3], 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,
+ "[ELAN] i2c_transfer write fail, rc=%d\n", rc);
+
+ mutex_unlock(&ts->tr_mutex);
+
+ /* If everything went ok (i.e. 1 msg transmitted), return #bytes
+ transmitted, else error code. */
+ return (rc == 1) ? len : rc;
+
+}
+
+/**
+ * @brief \b elan_ts_get_data - set command to TP.
+ * @param client : our i2c device
+ * @param data : data to be received from TP.
+ * @param len : command length usually.
+ *
+ * get data from our TP.
+ */
+static int elants_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,
+ "[ELAN] 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,
+ "[ELAN] i2c_transfer read fail, rc=%d\n", rc);
+
+ mutex_unlock(&ts->tr_mutex);
+
+ /* If everything went ok (i.e. 1 msg transmitted), return #bytes
+ transmitted, else error code. */
+ return (rc == 1) ? len : rc;
+}
+
+
+/**
+ * @brief __issue_bootcode_cmd - hadle bootcode status.
+ * @param client : our i2c device
+ *
+ * @return { >0 means success,
+ * otherwise fail. }
+ *
+ * Enter Normal mode packet is {0x55, 0x55, 0x55, 0x55}
+ * 2012/3/1 won't issue bootcmd in normal driver initial flow.
+ */
+static int elan_bootcode_cmd(struct i2c_client *client,
+ u8 passcode)
+{
+ int rc = 0;
+ struct elants_data *ts = i2c_get_clientdata(client);
+ u8 command[2][4] = {
+ {0x45, 0x49, 0x41, 0xFF}, /* normal_command */
+ {0x45, 0x49, 0x41, 0x50}, /* iap_command */
+ };
+
+ /* Detect old / new FW */
+ elan_msleep(90);
+
+ if (gpio_get_value(ts->intr_gpio) == 0) {
+ /* Old FW */
+ pr_warn("detect intr!!!!! OLD BC detect\n");
+ return -2;
+ }
+
+ if (passcode > E_BOOT_IAP)
+ return -1;
+
+ elan_dbg(client, "passcode = %x:%x:%x:%x\n",
+ command[passcode][0],
+ command[passcode][1],
+ command[passcode][2],
+ command[passcode][3]);
+
+ mutex_lock(&ts->mutex);
+
+ ts->i2caddr = elan_i2c_slave2;
+ rc = elants_set_data(client, command[passcode], 4);
+ if (rc < 0)
+ dev_err(&ts->client->dev,
+ "[ELAN] set passcode[%d] fail\n", passcode);
+
+ ts->i2caddr = elan_i2c_slave1;
+ rc = elants_set_data(client, command[passcode], 4);
+ if (rc < 0)
+ dev_err(&ts->client->dev,
+ "[ELAN] set passcode[%d] fail\n", passcode);
+
+ ts->i2caddr = elan_i2c_master;
+ rc = elants_set_data(client, command[passcode], 4);
+ if (rc < 0)
+ dev_err(&ts->client->dev,
+ "[ELAN] set passcode[%d] fail\n", passcode);
+
+ /* normal communitcat interactives with Master. */
+ ts->i2caddr = elan_i2c_master;
+ mutex_unlock(&ts->mutex);
+
+ return rc;
+}
+
+/**
+ * @brief interfaces
+ * provide the hardware and firmware information
+ */
+static ssize_t show_fw_version_value(
+ struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct elants_data *ts = i2c_get_clientdata(client);
+
+ if (test_bit(idx_update_fw, &ts->flags))
+ return -1;
+
+ return sprintf(buf, "%.2x %.2x\n",
+ ts->major_fw_version,
+ ts->minor_fw_version);
+}
+
+
+static ssize_t show_bc_version_value(
+ struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct elants_data *ts = i2c_get_clientdata(client);
+
+ return sprintf(buf, "%.2x %.2x\n",
+ ts->major_bc_version,
+ ts->minor_bc_version);
+}
+
+static ssize_t show_drvver_value(
+ struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%s\n", DRIVER_VERSION);
+}
+
+
+static ssize_t show_intr_gpio(
+ struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct elants_data *ts = i2c_get_clientdata(client);
+
+ if (test_bit(idx_update_fw, &ts->flags))
+ return -1;
+
+ ret = gpio_get_value(ts->intr_gpio);
+
+ return sprintf(buf, "%d\n", ret);
+}
+
+static ssize_t show_adapter_pkt_rvd(
+ struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct elants_data *ts = i2c_get_clientdata(client);
+
+ if (test_bit(idx_update_fw, &ts->flags))
+ return -1;
+
+ return sprintf(buf, "irq_received=%ld packet_received=%ld " \
+ "packet_fail=%ld mq_hdr_fail=%d, " \
+ "checksum_fail=%x header_fail=%d, " \
+ "poll_timer=%d, wdt_reset=%ld\n",
+ ts->irq_received, ts->packet_received,
+ ts->packet_fail, ts->mq_header_fail,
+ ts->checksum_fail, ts->header_fail,
+ ts->drop_frame, ts->wdt_reset);
+}
+
+static ssize_t show_queue_count(
+ struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct elants_data *ts = i2c_get_clientdata(client);
+
+ if (test_bit(idx_update_fw, &ts->flags))
+ return -1;
+
+ return sprintf(buf, "queue_report_count=%d, report_length=%d\n",
+ ts->mq_header.report_count,
+ ts->mq_header.report_length);
+}
+
+
+static ssize_t store_power_mode(
+ struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = 0;
+ unsigned long val;
+ const char AcMode[] = {0x54, 0x30, 0x00, 0x01};
+ const char BtMode[] = {0x54, 0x38, 0x00, 0x01};
+ char cmd[4];
+ struct i2c_client *client = to_i2c_client(dev);
+ struct elants_data *ts = i2c_get_clientdata(client);
+
+ if (test_bit(idx_update_fw, &ts->flags))
+ return -1;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ switch (val) {
+ case 0:
+ memcpy(cmd, AcMode, sizeof(AcMode));
+ break;
+ case 1:
+ memcpy(cmd, BtMode, sizeof(BtMode));
+ break;
+ default:
+ return -1;
+ }
+
+ mutex_lock(&ts->mutex);
+ elants_set_data(ts->client, cmd, sizeof(cmd));
+ mutex_unlock(&ts->mutex);
+
+ return count;
+}
+
+static ssize_t show_power_mode(
+ struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ const char RespCmd[] = {0x53, 0x30, 0x00, 0x01};
+ char tbuf[4];
+ struct i2c_client *client = to_i2c_client(dev);
+ struct elants_data *ts = i2c_get_clientdata(client);
+
+ if (test_bit(idx_update_fw, &ts->flags))
+ return -1;
+
+
+ mutex_lock(&ts->mutex);
+ set_bit(idx_cmd_handshake, &ts->flags);
+ elants_acqurie_data(ts->client, RespCmd, tbuf, sizeof(RespCmd));
+ if (tbuf[0] != cmd_header_byte_response) {
+ dev_err(&client->dev,
+ "exception!! %x:%x:%x:%x\n",
+ tbuf[0], tbuf[1], tbuf[2], tbuf[3]);
+ elants_drop_packet(ts);
+ memset(tbuf, 0x0, sizeof(tbuf));
+ }
+ clear_bit(idx_cmd_handshake, &ts->flags);
+ mutex_unlock(&ts->mutex);
+
+ return sprintf(buf, "%x:%x:%x:%x\n",
+ tbuf[0], tbuf[1], tbuf[2], tbuf[3]);
+}
+
+static ssize_t show_report_rate(
+ struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct elants_data *ts = i2c_get_clientdata(client);
+ long int old_frame, new_frame;
+
+ old_frame = ts->packet_received;
+ ssleep(1);
+ new_frame = ts->packet_received;
+
+ return sprintf(buf, "%ld\n", new_frame - old_frame);
+}
+
+static ssize_t show_iap_mode(
+ struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct elants_data *ts = i2c_get_clientdata(client);
+
+ return sprintf(buf, "%s\n",
+ (ts->iap_mode == 0) ? "Normal" : "Recovery");
+}
+
+static ssize_t show_recal(
+ struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct elants_data *ts = i2c_get_clientdata(client);
+ u8 sbuf[4], retstr[32] = "re-K finish";
+
+ u8 w_flashkey[4] = {0x54, 0xC0, 0xE1, 0x5A};
+ u8 rek[4] = {0x54, 0x29, 0x00, 0x01};
+
+ ts->i2caddr = elan_i2c_master;
+ elants_set_data(client, w_flashkey, 4);
+ elan_msleep(1);
+ elants_set_data(client, rek, 4);
+
+ while (gpio_get_value(ts->intr_gpio) == 0) {
+ if (elants_get_data(client, sbuf, 4) != 4)
+ sprintf(retstr, "re-K Fail");
+ pr_info("[ELAN] buf=%x:%x:%x:%x\n", buf[0],
+ sbuf[1], sbuf[2], sbuf[3]);
+ }
+
+ return sprintf(buf, "%s\n", retstr);
+}
+
+
+static ssize_t show_boot_log(
+ struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct elants_data *ts = i2c_get_clientdata(client);
+
+ /* show boot status, allow user cat this log during iap mode enable!! */
+
+ return scnprintf(buf, PAGE_SIZE, ts->boot_log);
+}
+
+static ssize_t store_set_mode(
+ struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret, i;
+ u8 cmds[32], recv_buf[32];
+ struct i2c_client *client = to_i2c_client(dev);
+ struct elants_data *ts = i2c_get_clientdata(client);
+
+ if (test_bit(idx_update_fw, &ts->flags))
+ return -1;
+
+ /* get input */
+ sscanf(buf, "%x %x %x %x", &cmds[0], &cmds[1], &cmds[2], &cmds[3]);
+
+ if (cmds[0] != cmd_header_byte_read &&
+ cmds[0] != cmd_header_byte_write)
+ return -EFAULT;
+
+ pr_info("[ELAN] Command %.2x:%.2x:%.2x:%.2x\n",
+ cmds[0], cmds[1], cmds[2], cmds[3]);
+
+ ret = elants_cmd_transfer_once(client,
+ cmds, 4, cmds, 4);
+
+ return (ret > 0) ? count : -1;
+}
+
+
+
+static DEVICE_ATTR(cmd_mode, S_IWUGO, NULL, store_set_mode);
+static DEVICE_ATTR(power_mode, S_IRUGO|S_IWUGO,
+ show_power_mode, store_power_mode);
+static DEVICE_ATTR(queue_count, S_IRUGO, show_queue_count, NULL);
+static DEVICE_ATTR(drv_version, S_IRUGO, show_drvver_value, NULL);
+static DEVICE_ATTR(fw_version, S_IRUGO, show_fw_version_value, NULL);
+static DEVICE_ATTR(bc_version, S_IRUGO, show_bc_version_value, NULL);
+static DEVICE_ATTR(ts_packet, S_IRUGO, show_adapter_pkt_rvd, NULL);
+static DEVICE_ATTR(gpio, S_IRUGO, show_intr_gpio, NULL);
+static DEVICE_ATTR(report_rate, S_IRUGO, show_report_rate, NULL);
+static DEVICE_ATTR(boot_log, S_IRUGO, show_boot_log, NULL);
+static DEVICE_ATTR(iap_mode, S_IRUGO, show_iap_mode, NULL);
+static DEVICE_ATTR(rek, S_IRUGO, show_recal, NULL);
+
+
+
+static struct attribute *elan_attributes[] = {
+ &dev_attr_cmd_mode.attr,
+ &dev_attr_power_mode.attr,
+ &dev_attr_queue_count.attr,
+ &dev_attr_fw_version.attr,
+ &dev_attr_drv_version.attr,
+ &dev_attr_bc_version.attr,
+ &dev_attr_gpio.attr,
+ &dev_attr_ts_packet.attr,
+ &dev_attr_report_rate.attr,
+ &dev_attr_boot_log.attr,
+ &dev_attr_iap_mode.attr,
+ &dev_attr_rek.attr,
+
+ NULL
+};
+
+
+static struct attribute_group elan_attribute_group = {
+ .name = "elants",
+ .attrs = elan_attributes,
+};
+
+
+/**
+ * @brief elan_hw_reset - h/w reset.
+ * @param client : our i2c device
+ *
+ * @retval >0 means reset success, \n
+ * otherwise is fail.
+ *
+ */
+static int elan_hw_reset(struct i2c_client *client)
+{
+ struct elants_data *ts = i2c_get_clientdata(client);
+ int ret;
+
+ if (ts->rst_gpio < 0)
+ return -1;
+
+ ret = gpio_direction_output(ts->rst_gpio, 0);
+ if (ret < 0) {
+ pr_err("gpio_direction fail!\n");
+ return ret;
+ }
+
+ elan_msleep(2);
+
+ ret = gpio_direction_output(ts->rst_gpio, 1);
+ if (ret < 0)
+ return ret;
+
+ /* wait > 10ms to ensure that fw is in IAP mode */
+ msleep(20);
+
+ return ret;
+}
+
+
+/**
+ * @brief \b elants_is_iap - is it in iap mode ?
+ * @param client : our i2c device
+ * @param addr : i2c address
+ *
+ * check whether fw currently is in iap.
+ * @return :
+ * < 0 :normal mode
+ * > 0 :iap mode or abnormal.
+ * -EINVAL : addr overflow, no such device
+ */
+static int elants_is_iap(
+ struct i2c_client *client,
+ u16 addr)
+{
+ int rc;
+ uint8_t buf_recv[4] = {0};
+ struct elants_data *ts = i2c_get_clientdata(client);
+
+ ts->i2caddr = addr;
+ rc = elants_get_data(client, buf_recv, 4);
+ if (unlikely(rc < 0)) {
+ dev_err(&client->dev,
+ "[ELAN] %s: ID 0x%.2x retval=%d!\n",
+ __func__, addr, rc);
+ rc = -ENODEV;
+ } else {
+ if (memcmp(buf_recv, iniap_packet, 4)) {
+ dev_err(&client->dev,
+ "[ELAN] %s: ID 0x%x retval=%d !\n",
+ __func__, addr, rc);
+ rc = -ENODEV;
+ }
+ }
+
+ ts->i2caddr = elan_i2c_master;
+
+ return rc;
+}
+
+static int elants_boot(
+ struct i2c_client *client,
+ u16 addr,
+ elan_boot_t type)
+{
+ int rc;
+ struct elants_data *ts = i2c_get_clientdata(client);
+ uint8_t command[2][4] = {
+ {0x4D, 0x61, 0x69, 0x6E}, /* normal_command */
+ {0x45, 0x49, 0x41, 0x50}, /* iap_command */
+ };
+
+ ts->i2caddr = addr;
+ rc = elants_set_data(client, command[(int32_t)type], 4);
+ if (rc != 4) {
+ if (type == E_BOOT_IAP)
+ dev_err(&client->dev,
+ "[elan] Boot IAP fail, error=%d\n",
+ rc);
+ else
+ dev_dbg(&client->dev,
+ "[elan] Boot normal fail, error=%d\n",
+ rc);
+ ts->i2caddr = elan_i2c_master;
+ return -EINVAL;
+ }
+ pr_info("[elan] Boot success -- 0x%x\n", addr);
+ ts->i2caddr = elan_i2c_master;
+ return 0;
+}
+
+static int elants_enter_iap(struct i2c_client *client)
+{
+ int rc = 0;
+ struct elants_data *ts = i2c_get_clientdata(client);
+
+ elan_hw_reset(client);
+
+ /* Boot devices to IAP mode */
+ rc = elants_boot(client, elan_i2c_master, E_BOOT_IAP);
+ if (rc < 0)
+ return rc;
+
+ elan_msleep(10);
+
+ /* Check if devices can enter IAP mode */
+ rc = elants_is_iap(client, elan_i2c_master);
+ if (rc < 0)
+ return rc;
+
+ ts->iap_mode = IAP_MODE_ENABLE;
+ set_bit(idx_update_fw, &ts->flags);
+ ts->i2caddr = elan_i2c_master;
+
+ return rc;
+}
+
+
+
+/**
+ * @brief __hello_packet_handler - hadle hello packet.
+ * @param 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, tries = 5;
+ uint8_t buf_recv[4] = {0};
+ struct elants_data *ts = i2c_get_clientdata(client);
+
+
+retry: /* wait INT for 1sec */
+ rc = elan_ts_poll(client);
+ if (rc < 0) {
+ dev_err(&client->dev,
+ "%s: poll failed!\n", ELAN_DEV_NAME);
+ if (tries-- > 0)
+ goto retry;
+ }
+
+ rc = elants_get_data(client, buf_recv, 4);
+
+ /* Print buf_recv anyway */
+ pr_info("[ELAN] rc = %d Hello Packet: " \
+ "[0x%.2x 0x%.2x 0x%.2x 0x%.2x]\n",
+ rc, buf_recv[0], buf_recv[1], buf_recv[2], buf_recv[3]);
+
+ if (rc != 4) {
+ dev_err(&client->dev,
+ "[ELAN] %s: Try recovery because of no hello\n",
+ __func__);
+
+ /* force TP FW into IAP mode. */
+ elants_enter_iap(client);
+ return -EINVAL;
+ }
+
+ /* v0.85 IAP recovery mechanism 2012-1022*/
+ if (memcmp(buf_recv, hello_packet, 4)) {
+ /* Check if got 55558080 and
+ * should receive next 4Byte for BC version
+ */
+ if (!memcmp(buf_recv, recov_packet, 4)) {
+ pr_warn("[ELAN] got IAP recovery packet!(55558080)\n");
+ rc = elants_get_data(client, buf_recv, 4);
+ if (unlikely(rc != 4)) {
+ dev_err(&client->dev,
+ "Got IAP recovery packet fail!\n");
+ return -ENODEV;
+ }
+
+ /* buf_recv[0,1] is dummy data */
+ ts->major_bc_version = buf_recv[3];
+ ts->minor_bc_version = buf_recv[2];
+
+ pr_info("[ELAN] BC version = %.2x:%.2x",
+ ts->major_bc_version,
+ ts->minor_bc_version);
+ }
+
+ pr_info("[ELAN] got mainflow recovery message\n");
+ rc = elants_enter_iap(client);
+ if (rc < 0)
+ dev_err(&client->dev, "Enter IAP mode fail!\n");
+ return -ENODEV;
+ }
+
+ ts->i2caddr = elan_i2c_master;
+
+ return rc;
+}
+
+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_byte_read, 0x00, 0x00, 0x01};
+ uint8_t buf_recv[4] = {0x0};
+
+ /* Command not support in IAP recovery mode */
+ if (test_bit(idx_update_fw, &ts->flags))
+ return 0;
+retry:
+ rc = elants_acqurie_data(client, cmd, buf_recv, 4);
+ if (rc < 0)
+ return rc;
+
+ if (buf_recv[0] == cmd_header_byte_response) {
+ ts->major_fw_version = ((buf_recv[1] & 0x0f) << 4) |
+ ((buf_recv[2] & 0xf0) >> 4);
+ ts->minor_fw_version = ((buf_recv[2] & 0x0f) << 4) |
+ ((buf_recv[3] & 0xf0) >> 4);
+
+ if ((ts->major_fw_version == 0x00 &&
+ ts->minor_fw_version == 0x00) ||
+ (ts->major_fw_version == 0xFF &&
+ ts->minor_fw_version == 0xFF)) {
+ dev_err(&client->dev,
+ "\n\n[ELAN] FW version is empty, "
+ "suggest IAP ELAN chip\n\n");
+ return -EINVAL;
+ }
+
+ elan_dbg(client,
+ "[ELAN] TOUCH MAJOR FW VERSION 0x%02x\n",
+ ts->major_fw_version);
+ elan_dbg(client,
+ "[ELAN] TOUCH MINOR FW VERSION 0x%02x\n",
+ ts->minor_fw_version);
+ } else {
+ if (tries > 0) {
+ tries--;
+ goto retry;
+ }
+ ts->major_fw_version = 0xff;
+ ts->minor_fw_version = 0xff;
+ dev_err(&client->dev,
+ "\n\n[ELAN] FW version is empty, "
+ "suggest IAP ELAN chip\n\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __touch_get_resolution(struct i2c_client *client)
+{
+ struct elants_data *ts = i2c_get_clientdata(client);
+ int rc;
+ uint8_t buf_recv[17] = {0x0};
+ const uint8_t get_resolution_cmd[] = {
+ 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ /* Command not support in IAP recovery mode */
+ if (test_bit(idx_update_fw, &ts->flags))
+ return 0;
+
+ rc = elants_async_recv_data(client, get_resolution_cmd, buf_recv,
+ sizeof(get_resolution_cmd), sizeof(buf_recv));
+ if (rc < 0)
+ return -rc;
+
+ elan_dbg(client,
+ "[ELAN] %x:%x:%x, :%x:%x:%x, %d\n",
+ buf_recv[2], buf_recv[6], buf_recv[10],
+ buf_recv[3], buf_recv[7], buf_recv[0], rc);
+
+ if (buf_recv[0] != 0x9B)
+ return -EINVAL;
+
+ elan_dbg(client, "[ELAN] %x:%x:%x, :%x:%x:%x, %d\n",
+ buf_recv[2], buf_recv[6], buf_recv[10],
+ buf_recv[3], buf_recv[7], buf_recv[11], rc);
+
+
+ ts->rows = (buf_recv[2] + buf_recv[6] + buf_recv[10]);
+ ts->cols = (buf_recv[3] + buf_recv[7] + buf_recv[11]);
+
+ elan_dbg(client, "[ELAN] %x:%x:%x, :%x:%x:%x\n", buf_recv[2],
+ buf_recv[6], buf_recv[10],
+ buf_recv[3], buf_recv[7], buf_recv[11]);
+
+ if (ts->rows < 2 || ts->cols < 2) {
+ dev_err(&client->dev,
+ "[ELAN] Invalid resolution (%d, %d)\n",
+ ts->rows, ts->cols);
+
+ /* set default resolution if TP information is wrong */
+ ts->rows = ELAN_X_MAX;
+ ts->cols = ELAN_Y_MAX;
+
+ rc = ret_fail;
+ }
+
+ /* translate trace number to TSP resolution */
+ ts->cols = ELAN_TS_RESOLUTION(ts->cols);
+ ts->rows = ELAN_TS_RESOLUTION(ts->rows);
+
+ elan_dbg(client,
+ "[ELAN] resolution rows=0x%.2x, "
+ "cols=0x%.2x\n",
+ ts->rows, ts->cols);
+
+ 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 __elan_touch_get_bc_ver(struct i2c_client *client)
+{
+ struct elants_data *ts = i2c_get_clientdata(client);
+ const u8 get_bc_ver_cmd[] = {0x53, 0x10, 0x00, 0x01};
+ u8 buf_recv[4];
+ int rc;
+
+ /* Command not support in IAP recovery mode */
+ if (test_bit(idx_update_fw, &ts->flags))
+ return 0;
+
+ rc = elants_acqurie_data(client, get_bc_ver_cmd,
+ buf_recv, sizeof(buf_recv));
+ if (rc < 0) {
+ dev_err(&client->dev,
+ "elan_ts_acqurie_data failed: get_bc_ver\n");
+ return rc;
+ }
+
+ ts->major_bc_version = (((buf_recv[1] & 0x0f) << 4) |
+ ((buf_recv[2]&0xf0) >> 4));
+ ts->minor_bc_version = (((buf_recv[2] & 0x0f) << 4) |
+ ((buf_recv[3]&0xf0) >> 4));
+
+ elan_dbg(client,
+ "ELAN TOUCH MAJOR BC VERSION 0x%02x\n",
+ ts->major_bc_version);
+ elan_dbg(client,
+ "ELAN TOUCH MINOR BC VERSION 0x%02x\n",
+ ts->minor_bc_version);
+
+ return 0;
+}
+
+static int __elan_fastboot(struct i2c_client *client, int *count)
+{
+ int rc = 0;
+
+
+ rc = elants_boot(client, elan_i2c_master, E_BOOT_NORM);
+ if (rc < 0) {
+ if (*count > 0)
+ return -EAGAIN;
+ return -1;
+ }
+
+ msleep(100);
+
+ return rc;
+}
+
+/**
+ * @brief elan_open - open elan device
+ * @param input : input device
+ *
+ */
+static int elan_open(struct input_dev *input)
+{
+ struct elants_data *ts = input_get_drvdata(input);
+
+ dev_err(&ts->client->dev,
+ "[ELAN] Enter %s\n", __func__);
+
+ /* wait probe work_func done */
+ while ((ts->status & STA_INIT4) == 0)
+ elan_msleep(1);
+
+ return 0;
+}
+
+/**
+ * @brief elan_close - close input device
+ * @param input : input device
+ *
+ */
+static void elan_close(struct input_dev *input)
+{
+ struct elants_data *ts = input_get_drvdata(input);
+
+ dev_err(&ts->client->dev,
+ "[ELAN] Enter %s\n", __func__);
+
+ return;
+}
+
+
+/**
+ * @brief \b elan_ts_setup - initialization process.
+ * @param client : our i2c client
+ *
+ * set our TP up
+ * -# reset
+ * -# hello packet
+ * -# fw version
+ * -# TP info (resolution)
+ *
+ */
+static int __devinit elants_setup(struct i2c_client *client)
+{
+ struct elants_data *ts = i2c_get_clientdata(client);
+ int rc, tries = 3, count = 3;
+ int len = 0;
+ bool fastboot = true;
+
+retry:
+ /*! Reset */
+ elan_hw_reset(client);
+
+ ts->rx_size = queue_header_size;
+
+ /* New BootCode */
+ if (fastboot == true) {
+ rc = __elan_fastboot(client, &count);
+ if (rc < 0) {
+ if (rc == -EAGAIN && --count > 0)
+ goto retry;
+ else {
+ len += sprintf(ts->boot_log + len,
+ "serious bug, BC may be broken!!\n");
+ return -1;
+ }
+ }
+ fastboot = 0;
+ } else {
+ /* Wait bootcode timeout 1 second +
+ Main-flow initial ~100ms */
+ ssleep(2);
+ }
+
+ /*! - elan hello packet init */
+ rc = __hello_packet_handler(client);
+ if (rc < 0) {
+ /* Wrong hello_packet or polling fail, retry */
+ if ((rc == -EINVAL || rc == -ETIMEDOUT) && --tries > 0) {
+ dev_err(&client->dev,
+ "[ELAN] retries=%d, rc=%d\n",
+ tries, rc);
+ goto retry;
+ }
+
+ len += sprintf(ts->boot_log + len,
+ "retry=%d polling time-out\n",
+ 3-tries);
+
+ dev_err(&client->dev,
+ "hello packet error.\n");
+ /* Go through down*/
+
+ /* if iap mode enable, return and wait for IAP */
+ if (ts->iap_mode == IAP_MODE_ENABLE) {
+ len += sprintf(ts->boot_log + len, "IAP mode enable\n");
+ return rc;
+ }
+ }
+
+ elan_dbg(client,
+ "__hello_packet_handler ...\n");
+
+ /*! - elan fw version */
+ rc = __fw_packet_handler(client);
+ if (rc < 0) {
+ dev_err(&client->dev, "firmware checking error.\n");
+ len += sprintf(ts->boot_log+len, "fw_packet_error=%x:%x\n",
+ ts->major_fw_version, ts->minor_fw_version);
+
+ if (rc == -EINVAL) {
+ len += sprintf(ts->boot_log+len,
+ "FW version is empty, IAP enable!!\n");
+ set_bit(idx_update_fw, &ts->flags);
+ ts->iap_mode = IAP_MODE_ENABLE;
+ }
+
+ /* Go through down*/
+ }
+
+ elan_dbg(client, "__fw_packet_handler...\n");
+
+ /*! - elan TP information */
+ rc = __touch_get_resolution(client);
+ if (rc < 0) {
+ dev_err(&client->dev,
+ "TP information checking error.\n");
+ len += sprintf(ts->boot_log+len,
+ "touch_get_resolution=%x:%x\n",
+ ts->rows, ts->cols);
+ /* Go through down*/
+ }
+
+ elan_dbg(client,
+ "__touch_get_resolution...\n");
+
+
+ /* Get TS BootCode version */
+ rc = __elan_touch_get_bc_ver(client);
+ if (rc < 0) {
+ dev_err(&client->dev,
+ "TP get BC version error.\n");
+ len += sprintf(ts->boot_log+len,
+ "touch_get_BC_ver=%x:%x\n",
+ ts->major_bc_version, ts->major_bc_version);
+ /* Go through down*/
+ }
+
+ if (len == 0)
+ len += sprintf(ts->boot_log, "boot success!!\n");
+
+ return rc;
+
+}
+
+
+static int elants_get_power_state(struct i2c_client *client)
+{
+ int rc = 0;
+ const uint8_t cmd[] = {cmd_header_byte_read, 0x50, 0x00, 0x01};
+ uint8_t buf[4], power_state;
+
+ rc = elants_acqurie_data(client, cmd, buf, 4);
+ if (rc)
+ return rc;
+
+ power_state = buf[1];
+ elan_dbg(client,
+ "dump repsponse: %0x\n", power_state);
+ power_state = (power_state & PWR_STATE_MASK) >> 3;
+ elan_dbg(client,
+ "power state = %s\n",
+ power_state == PWR_STATE_DEEP_SLEEP ?
+ "Deep Sleep" : "Normal/Idle");
+
+ return power_state;
+}
+
+
+/**
+ * @brief \b elan_ts_recv_data - received TP data
+ * @param client: our i2c device
+ * @param buf : buffer for put received data
+ *
+ * received data from TP device.
+ */
+static int elants_recv_data(struct i2c_client *client, uint8_t *buf)
+{
+ struct elants_data *ts = i2c_get_clientdata(client);
+ int rc = 0, bytes_to_recv;
+
+ dev_dbg(&client->dev,
+ "[ELAN] Enter %s\n", __func__);
+
+ if (buf == NULL)
+ return -EINVAL;
+
+ mutex_lock(&ts->tr_mutex);
+
+ bytes_to_recv = ts->rx_size;
+ rc = i2c_master_recv(client, buf, bytes_to_recv);
+
+ dev_dbg(&client->dev,
+ "[ELAN] %d:%d:%x:%x, addr=%x\n", bytes_to_recv, rc,
+ buf[0], buf[34], client->addr);
+ dev_dbg(&client->dev,
+ "[ELAN] %x:%x:%x:%x:%x:%x\n", buf[0], buf[1], buf[2],
+ buf[3], buf[4], buf[5]);
+
+ if (rc != bytes_to_recv) {
+ dev_err(&client->dev,
+ "%s: i2c_master_recv error?!\n",
+ __func__);
+ rc = -EINVAL;
+ }
+ mutex_unlock(&ts->tr_mutex);
+
+ return rc;
+}
+
+/**
+ * elan_touch_pull_frame - pull a frame from the fifo
+ * @ed: our elan touch device
+ * @ehr: return buffer for the header
+ * @buf: data buffer
+ *
+ * Pulls a frame from the FIFO into the provided ehr and data buffer.
+ * The data buffer must be at least cmd_response_len bytes long.
+ */
+static int elan_touch_pull_frame(struct elants_data *ts, u8 *buf)
+{
+ dev_dbg(&ts->client->dev,
+ "[ELAN] Enter %s\n", __func__);
+
+ WARN_ON(kfifo_out_locked(&ts->fifo, buf, cmd_response_len,
+ &ts->rx_kfifo_lock)
+ != cmd_response_len);
+ return cmd_response_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_response_len];
+ dev_dbg(&ts->client->dev,
+ "[ELAN] Enter %s\n", __func__);
+
+ while (kfifo_len(&ts->fifo) + room >= FIFO_SIZE)
+ elan_touch_pull_frame(ts, buffer);
+}
+
+
+/** @brief \b elan_ts_GetRepoInfo - parse Multi-queue report header
+ * @param client : our i2c device
+ * @param buf : buffer data
+ *
+ * parsing report header and get data length.
+ *
+ */
+static int elants_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;
+ const u8 wait_packet[4] = {0x64, 0x64, 0x64, 0x64};
+ int times = 10, rc = 0;
+
+ switch (buf[idx_finger_header]) {
+ case cmd_header_byte_hello:
+ if (!memcmp(buf, hello_packet, 4))
+ ts->wdt_reset++;
+ return ret_cmdrsp;
+ case cmd_header_byte_response:
+ /* Queue the data, using the fifo lock to serialize the multiple
+ accesses to the FIFO */
+ elan_dbg(client,
+ "[ELAN] recv cmd_header_byte_response\n");
+
+ mutex_lock(&ts->fifo_mutex);
+ if (kfifo_len(&ts->fifo) + cmd_response_len >= FIFO_SIZE)
+ /* Make room, make room */
+ elan_touch_fifo_clean_old(ts, cmd_response_len);
+ /* Push the data */
+ kfifo_in_locked(&ts->fifo, buf,
+ cmd_response_len, &ts->rx_kfifo_lock);
+ mutex_unlock(&ts->fifo_mutex);
+
+ elan_dbg(client,
+ "[ELAN] wake_up [%02x:%02x:%02x:%02x]\n",
+ buf[0], buf[1], buf[2], buf[3]);
+ wake_up(&ts->wait);
+ return ret_cmdrsp;
+ case cmd_header_byte_rek:
+ /* re-K comand response, ingore it! */
+ return ret_cmdrsp;
+ /* Buffer mode header */
+ case queue_header_byte_normal:
+ elan_dbg(client,
+ "[ELAN] report_count=%d report_len=%d\n",
+ buff->report_count, buff->report_length);
+ if (likely(buff->report_count <= 3)) {
+ ts->mq_header.report_count = buff->report_count;
+ ts->mq_header.report_length = buff->report_length;
+ ts->rx_size = ts->mq_header.report_length;
+ } else
+ return ret_fail;
+
+ break;
+ case queue_header_byte_wait:
+ dev_err(&client->dev,
+ "========queue_header_byte_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);
+ elants_recv_data(client, (uint8_t *)buff);
+ } while (buff->packet_id != queue_header_byte_normal &&
+ --times > 0);
+ if (times > 0)
+ rc = elants_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,
+ ts->mq_header.report_count,
+ ts->mq_header.report_length);
+ } else
+ dev_err(&client->dev,
+ "[ELAN] 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_byte_Single:
+ ts->mq_header.report_count = 1;
+ ts->mq_header.report_length = IDX_PACKET_SIZE_WIDTH;
+ ts->rx_size = ts->mq_header.report_length;
+ return ts->rx_size;
+ break;
+ 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 = queue_packet_max_len;
+ elants_recv_data(client, (uint8_t *)buff);
+ return ret_fail;
+ }
+
+ return ts->rx_size;
+}
+
+
+/** @brief \b elan_touch_checksum - Add for checksum mechanism
+ * @param client : our i2c device
+ * @param 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);
+
+ /*! FIXME: checksum wasn't including width byte */
+ for (i = 0; i < IDX_PACKET_SIZE_WIDTH - 6; i++)
+ checksum = checksum + buf[i];
+
+ ts->checksum_correct = checksum;
+
+ if (checksum != buf[idx_finger_width_checksum]) {
+ ts->checksum_fail++;
+ dev_err(&client->dev,
+ "elan touch checksum fail: %02x:%02x\n",
+ checksum, buf[idx_finger_width_checksum]);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief \b elan_touch_parse_fid - parse the 10 fid bits
+ * @param data : the input bit stream
+ * @param 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_touch_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);
+}
+
+/**
+ * @brief \b elan_touch_parse_wid - parse the 10 wid bits
+ * @param data : the input bit stream
+ * @param wid : an array of width level
+ *
+ * Unpack the 10 bits into an array.
+ *
+ */
+static inline void elan_touch_parse_wid(u8 *data, u8 *wid)
+{
+ wid[0] = (data[0] >> 4);
+ wid[1] = (data[0] & 0x0f);
+ wid[2] = (data[1] >> 4);
+ wid[3] = (data[1] & 0x0f);
+ wid[4] = (data[2] >> 4);
+ wid[5] = (data[2] & 0x0f);
+ wid[6] = (data[3] >> 4);
+ wid[7] = (data[3] & 0x0f);
+ wid[8] = (data[4] >> 4);
+ wid[9] = (data[4] & 0x0f);
+}
+
+
+static inline int elants_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;
+}
+
+/* transition stage for lookup by table, maybe update finger report
+ using by Win8 50Byte format that having actual value for X, Y width */
+static inline int elan_lookup_wid(
+ u8 data,
+ u16 *w,
+ u16 *h)
+{
+ static u16 pre_w, pre_h, 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;
+
+ /* IIR to balance w, h vaule, don't jitter too much */
+ *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,
+ struct input_dev *input)
+{
+ return input_mt_get_slot_by_key(input, td->curdata.contactid);
+}
+
+/*
+ * 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,
+ struct input_dev *input)
+{
+ struct elants_data *ts =
+ container_of(td, struct elants_data, td);
+
+ if (td->curvalid) {
+ int slotnum = elan_mt_compute_slot(td, input);
+ struct mt_slot *s = &td->curdata;
+
+ if (slotnum < 0 || slotnum >= td->maxcontacts)
+ return;
+
+ input_mt_slot(input, slotnum);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER,
+ s->touch_state);
+ if (s->touch_state) {
+ /* this finger is on the screen */
+ int wide = (s->w > s->h);
+ /* divided by two to match visual scale of touch */
+ int major = max(s->w, s->h) >> 1;
+ int minor = min(s->w, s->h) >> 1;
+
+ elan_dbg(ts->client,
+ "[ELAN] slot=%d x=%x y=%x w=%x h=%x\n",
+ slotnum, s->x, s->y, 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_ORIENTATION, wide);
+ input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p);
+ input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
+ input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
+ }
+ }
+
+ 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 mt_sync_frame(struct mt_device *td, struct input_dev *input)
+{
+ input_mt_sync_frame(input);
+ input_sync(input);
+ td->num_received = 0;
+}
+
+
+/** @brief \b elan_mt_event - process finger reports
+ * @param ts: our touchscreen
+ * @param finger_stat : number of fingers in packet
+ * @param 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], wid[MAX_CONTACT_NUM];
+ u16 x, y, w, h;
+ struct i2c_client *client = ts->client;
+ struct mt_device *td = &ts->td;
+
+ dev_dbg(&client->dev,
+ "[ELAN] Enter elan_mt_event Func\n");
+
+ /* Parsing Finger, Width field */
+ elan_touch_parse_fid(&buf[idx_finger_state], &fid[0]);
+ elan_touch_parse_wid(&buf[idx_finger_width], &wid[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;
+
+ elants_parse_xy(&buf[3+i*3], &x, &y);
+ td->curdata.x = x;
+ td->curdata.y = y;
+
+ elan_lookup_wid(wid[i], &w, &h);
+ td->curdata.w = w;
+ td->curdata.h = h;
+
+ finger_stat--;
+
+ elan_mt_complete_slot(td, ts->input);
+ }
+
+ if (td->num_received >= td->num_expected)
+ mt_sync_frame(td, ts->input);
+
+ return 1;
+}
+
+
+
+/** @brief \b elants_report_data - report finger report to user space.
+ * @param client : our i2c device
+ * @param buf : raw data from TP device.
+ *
+ * - reporting finger data to user space.
+ * - packet_fail count
+ *
+ */
+static void elants_report_data(struct i2c_client *client, uint8_t *buf)
+{
+ struct elants_data *ts = i2c_get_clientdata(client);
+
+ switch (buf[idx_finger_header]) {
+ case idx_coordinate_packet_4_finger:
+ case idx_coordinate_packet_10_finger: {
+ u8 finger_stat = buf[idx_finger_total] & 0x0f;
+ elan_dbg(client,
+ "[ELAN] finger_stat == %d\n", finger_stat);
+ elan_dbg(client,
+ "[ELAN] finger:%x:%x:%x:%x:%x\n",
+ buf[0], buf[1], buf[2], buf[3], buf[4]);
+
+ /* Enter right process, reset int_status*/
+ ts->polling.int_status = 0;
+ ts->packet_received++;
+
+ if (likely(finger_stat != 0)) {
+ ts->td.curvalid = true;
+ ts->touched_sync++;
+ } else
+ ts->no_touched_sync++;
+
+ /* Handle mt event */
+ elan_mt_event(ts, finger_stat, buf);
+ }
+ break;
+ default:
+ ts->header_fail++;
+ dev_err(&client->dev,
+ "[ELAN] %s: unknown packet type: %x:%x:%x:%x\n",
+ __func__, buf[0], buf[1], buf[2], buf[3]);
+ break;
+ }
+
+ return;
+}
+
+/** @brief \b elants_drop_packet - err handler that drop all packet.
+ *
+ * mutex protection will at outside this function.
+ *
+ */
+static void elants_drop_packet(struct elants_data *ts)
+{
+ uint8_t buf[queue_packet_max_len] = {0x0};
+
+ dev_dbg(&ts->client->dev,
+ "[ELAN] Enter %s\n", __func__);
+
+ if (test_bit(idx_update_fw, &ts->flags))
+ return;
+
+ if (gpio_get_value(ts->intr_gpio) != 0)
+ goto end;
+
+ /* -# Read multi_queue header */
+ ts->rx_size = queue_header_size;
+ if (elants_recv_data(ts->client, buf) < 0)
+ goto end;
+
+ /* -# Get multi_queue header info*/
+ if (elants_GetRepoInfo(ts->client, buf) > 0) {
+ /* 3. Get finger report data */
+ elants_recv_data(ts->client, buf);
+ udelay(10);
+ if (gpio_get_value(ts->intr_gpio) != 0)
+ goto end;
+ }
+
+ /* -#received all packet till ts->intr pull high */
+ ts->rx_size = 1;
+ while (gpio_get_value(ts->intr_gpio) == 0) {
+ elants_recv_data(ts->client, buf);
+ udelay(10);
+ }
+
+end:
+ ts->rx_size = queue_header_size;
+ dev_dbg(&ts->client->dev,
+ "[ELAN] Leave %s\n", __func__);
+
+ return;
+}
+
+
+
+/** @brief \b elan_ts_pollWork_func - regular processing
+ * @param work : pass from kernel
+ *
+ * poll_timer polled processing to occur
+ * elan_ts_pollWork_func() to check if our ts
+ * is abnormal.
+ *
+ */
+static void elants_pollWork_func(struct work_struct *work)
+
+{
+ struct elants_data *ts =
+ container_of(work, struct elants_data, pollingwork);
+
+ dev_dbg(&ts->client->dev,
+ "[ELAN] Enter %s\n", __func__);
+
+ if (test_bit(idx_update_fw, &ts->flags))
+ goto fail;
+
+ if (gpio_get_value(ts->intr_gpio) != 0)
+ goto fail;
+ else {
+ ts->polling.int_status++;
+ if (ts->polling.int_status < 3)
+ goto fail;
+ }
+
+ /* - we use mutex_trylock() here since polling is not very important */
+ if (!mutex_trylock(&ts->mutex)) {
+ elan_dbg(ts->client,
+ "[ELAN] trylock fail! return...\n");
+ goto fail;
+ }
+
+ if (test_bit(idx_finger_report, &ts->flags)) {
+ elan_dbg(ts->client,
+ "[ELAN] 1.finger_Report processing ... ignore!!\n");
+ goto unlock;
+ }
+
+ if (test_bit(idx_cmd_handshake, &ts->flags)) {
+ elan_dbg(ts->client,
+ "[ELAN] 2.command processing ... ignore!!\n");
+ goto unlock;
+ }
+
+ dev_err(&ts->client->dev,
+ "[ELAN] Force to release intr_gpio!!\n");
+
+ ts->drop_frame++;
+ elants_drop_packet(ts);
+
+unlock:
+ mutex_unlock(&ts->mutex);
+
+fail:
+ return;
+}
+
+static irqreturn_t elants_work_func(int irq, void *work)
+{
+ struct elants_data *ts = work;
+ uint8_t buf[queue_packet_max_len] = {0x0};
+ u8 pos = 0, rc;
+
+ dev_dbg(&ts->client->dev,
+ "[ELAN] Enter %s\n", __func__);
+
+ if (test_bit(idx_update_fw, &ts->flags))
+ return IRQ_HANDLED;
+
+ /* - this means that we have already serviced it or glich happen!! */
+ if (gpio_get_value(ts->intr_gpio) != 0) {
+ dev_dbg(&ts->client->dev,
+ "[ELAN] Leave %s(gpio)\n", __func__);
+ return IRQ_HANDLED;
+ }
+
+ mutex_lock(&ts->mutex);
+
+ set_bit(idx_finger_report, &ts->flags);
+
+ /* - Read multi_queue header */
+ if (elants_recv_data(ts->client, buf) < 0)
+ goto fail;
+
+ /* - Get multi_queue header info */
+ rc = elants_GetRepoInfo(ts->client, buf);
+ if (rc < 0 || rc == ret_cmdrsp)
+ goto fail;
+
+ /* - Get finger report data */
+ if (elants_recv_data(ts->client, buf) < 0)
+ goto fail;
+
+ clear_bit(idx_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)
+ elants_report_data(ts->client, buf + pos);
+ pos = pos + IDX_PACKET_SIZE_WIDTH;
+ udelay(10);
+ }
+
+ ts->rx_size = queue_header_size;
+ dev_dbg(&ts->client->dev,
+ "[ELAN] Leave %s\n", __func__);
+ return IRQ_HANDLED;
+
+fail:
+ clear_bit(idx_finger_report, &ts->flags);
+ mutex_unlock(&ts->mutex);
+ ts->rx_size = queue_header_size;
+
+ dev_dbg(&ts->client->dev,
+ "[ELAN] Leave %s(Fail)\n", __func__);
+ return IRQ_HANDLED;
+}
+
+/**
+ * @brief \b elants_poll_timer_func - err handler when intr_gpio is low but no isr serviced it.
+ * @param data : our ts
+ *
+ * intr_gpio polling checking, it'll force to get data if intr_gpio is low
+ * and not in isr routine.
+ *
+ */
+static void elants_poll_timer_func(unsigned long data)
+{
+ struct elants_data *ts = (struct elants_data *)data;
+ struct elan_polling *timer = &ts->polling;
+
+ dev_dbg(&ts->client->dev,
+ "[ELAN] Enter %s\n", __func__);
+
+ /* - ignore it if normal work is processing */
+ if (work_pending(&ts->work)) {
+ dev_dbg(&ts->client->dev,
+ "[ELAN] 1.work_pending ... ignore!!\n");
+ goto reset;
+ }
+
+ /* - ignore it if poll_timer work is processing */
+ if (work_pending(&ts->pollingwork)) {
+ dev_dbg(&ts->client->dev,
+ "[ELAN] 2.work_pending ... ignore!!\n");
+ goto reset;
+ }
+
+ if (!queue_work(timer->elan_wq, &ts->pollingwork))
+ dev_err(&ts->client->dev,
+ "[ELAN] pollWork active failed!!\n");
+
+reset:
+ timer->timer.expires = jiffies + 10 * HZ;
+ add_timer(&ts->polling.timer);
+
+ return;
+}
+
+static irqreturn_t elants_irq_handler(int irq, void *dev_id)
+{
+ struct elants_data *ts = dev_id;
+ struct i2c_client *client = ts->client;
+
+ dev_dbg(&client->dev, "[ELAN] %s\n", __func__);
+
+ ts->irq_received++;
+
+ return IRQ_WAKE_THREAD;
+}
+
+
+static int elants_register_interrupt(struct i2c_client *client)
+{
+ struct elants_data *ts = i2c_get_clientdata(client);
+ int err = -1;
+
+ if (client->irq) {
+
+ ts->use_irq = 1;
+
+ err = request_threaded_irq(client->irq, elants_irq_handler,
+ elants_work_func,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ client->name, ts);
+ if (err) {
+ dev_err(&client->dev,
+ "%s: request_irq %d failed\n",
+ __func__, client->irq);
+ goto err;
+ }
+
+ ts->status |= STA_USE_IRQ;
+
+ elan_dbg(client,
+ "[ELAN] %s in interrupt mode\n", ts->input->name);
+ }
+err:
+ return err;
+}
+
+static int remove_elants(struct i2c_client *client)
+{
+ int ret = 0;
+ struct elants_data *ts = i2c_get_clientdata(client);
+
+ if (&client->dev.kobj)
+ sysfs_remove_group(&client->dev.kobj, &elan_attribute_group);
+
+ if (ts->use_irq) {
+ if (client->irq)
+ free_irq(client->irq, ts);
+ }
+
+ input_mt_destroy_slots(ts->input);
+
+ if (ts->input)
+ input_unregister_device(ts->input);
+
+ if (ts->polling.elan_wq)
+ destroy_workqueue(ts->polling.elan_wq);
+
+ if (&ts->polling.timer) {
+ ret = del_timer(&ts->polling.timer);
+ if (ret != 0)
+ dev_err(&client->dev,
+ "[ELAN] del_timer fail!!\n");
+ }
+
+ 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);
+
+ if (ts->fw_enabled) {
+ ret = misc_deregister(&ts->firmware);
+ if (ret < 0)
+ dev_err(&client->dev,
+ "[ELAN] misc_deregister fail!!\n");
+ }
+
+ kfree(ts->td.slots);
+
+ /* free kfifo */
+ kfifo_free(&ts->fifo);
+ kfree(ts);
+
+ return ret;
+}
+
+
+/**
+ * probe_thread_func - 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 int probe_thread_func(void *data)
+{
+ struct elants_data *ts = data;
+ struct i2c_client *client = ts->client;
+ int err = 0;
+
+ mutex_lock(&ts->mutex);
+
+ err = elants_setup(client);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "[ELAN] %s probe failed\n",
+ ELAN_DEV_NAME);
+ /* User can IAP anyway, continue */
+ }
+
+ set_bit(BTN_TOUCH, ts->input->keybit);
+
+ /*! - Single touch input params setup */
+ input_set_abs_params(ts->input, ABS_X, 0, ts->cols, 0, 0);
+ input_set_abs_params(ts->input, ABS_Y, 0, ts->rows, 0, 0);
+ input_set_abs_params(ts->input, ABS_PRESSURE, 0, 255, 0, 0);
+
+ /*! @warning FIXME: ABS_MT_PRESSURE can be supported by FW.
+ currently we have done it by ABS_MT_WIDTH_MAJOR */
+ /*! - Multitouch input params setup */
+ input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, ts->cols, 0, 0);
+ input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->rows, 0, 0);
+ input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(ts->input, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0);
+
+ /* not test, keep mark for less Android-ICS */
+ input_set_abs_params(ts->input, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+
+ __set_bit(EV_ABS, ts->input->evbit);
+ __set_bit(EV_SYN, ts->input->evbit);
+
+ input_set_drvdata(ts->input, ts);
+
+ err = input_register_device(ts->input);
+ if (err) {
+ dev_err(&client->dev,
+ "[ELAN] %s unable to register input device\n",
+ ELAN_DEV_NAME);
+ goto fail_un;
+ }
+
+ 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 fail_un;
+ }
+ ts->td.maxcontacts = MAX_CONTACT_NUM;
+ ts->td.mt_flags |= INPUT_PROP_DIRECT;
+
+ input_mt_init_slots(ts->input, ts->td.maxcontacts, ts->td.mt_flags);
+ /* set frame rate size as 100fps */
+ input_set_events_per_packet(ts->input, 100);
+
+ /*! @warning If the firmware device fails
+ * we carry on as it doesn't stop normal
+ usage */
+ private_ts = &ts->firmware;
+ if (elants_register_interrupt(ts->client) < 0) {
+ dev_err(&client->dev,
+ "[ELAN] %s register_interrupt failed!\n",
+ ELAN_DEV_NAME);
+ goto fail_un;
+ }
+
+ /*! - Register SW watchdog timer */
+ init_timer(&ts->polling.timer);
+ /*! - check intr_gpio every 10secs */
+ ts->polling.timer.expires = jiffies + 10 * HZ;
+ ts->polling.timer.function = &elants_poll_timer_func;
+ ts->polling.timer.data = (unsigned long)ts;
+ add_timer(&ts->polling.timer);
+
+ mutex_unlock(&ts->mutex);
+
+ ts->status |= STA_INIT4;
+
+ return 0;
+
+fail_un:
+ mutex_unlock(&ts->mutex);
+ remove_elants(client);
+ return err;
+}
+
+
+
+/**
+ * @brief elants_probe - probe for touchpad
+ * @param client : our I2C client
+ *
+ * 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 __devinit elants_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 (ts == NULL)
+ return -ENOMEM;
+
+ ts->status |= STA_PROBED;
+
+ 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);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev,
+ "[ELAN] %s: i2c check functionality error\n",
+ ELAN_DEV_NAME);
+ goto fail_un;
+ }
+
+ /*! - Regular polling Process */
+ ts->polling.elan_wq = create_singlethread_workqueue("elan_poll_wq");
+ if (!ts->polling.elan_wq) {
+ dev_err(&client->dev,
+ "[ELAN] %s: create workqueue failed\n",
+ ELAN_DEV_NAME);
+ goto fail_un;
+ }
+
+ /* INIT polling machinams */
+ INIT_WORK(&ts->pollingwork, elants_pollWork_func);
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->dev,
+ "[ELAN] %s no platform data\n",
+ ELAN_DEV_NAME);
+ goto fail_un;
+ }
+
+ ts->status &= STA_INIT;
+
+ ts->intr_gpio = pdata->intr_gpio;
+ ts->rst_gpio = pdata->rst_gpio;
+ /* set initial i2c address */
+ client->addr = elan_i2c_master;
+ ts->i2caddr = client->addr;
+
+ elan_dbg(client,
+ "reset=%d, intr=%d\n", ts->rst_gpio, ts->intr_gpio);
+
+ ts->client = client;
+ i2c_set_clientdata(client, ts);
+
+ /* INIT 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;
+ }
+
+ ts->status &= STA_INIT2;
+
+ ts->input = input_allocate_device();
+ if (ts->input == NULL) {
+ dev_err(&client->dev,
+ "[ELAN] %s Failed to allocate input device\n",
+ ELAN_DEV_NAME);
+ goto fail_un;
+ }
+
+ ts->input->name = ELAN_DEV_NAME;
+ ts->input->open = elan_open;
+ ts->input->close = elan_close;
+
+ /* Says HELLO to touch device */
+ ts->thread = kthread_run(probe_thread_func, ts, client->name);
+ if (IS_ERR(ts->thread)) {
+ err = PTR_ERR(ts->thread);
+ goto fail_un;
+ }
+
+ ts->status &= STA_INIT3;
+
+ /* - register sysfs @} */
+ elan_dbg(client,
+ "[ELAN] create sysfs!!\n");
+ if (sysfs_create_group(&client->dev.kobj, &elan_attribute_group))
+ dev_err(&client->dev,
+ "[ELAN] sysfs create group error\n");
+
+
+ return 0;
+
+fail_un:
+ remove_elants(client);
+ return err;
+}
+
+static int __devexit elants_remove(struct i2c_client *client)
+{
+ return remove_elants(client);
+}
+
+#ifdef CONFIG_PM_SLEEP
+/** @brief \b elan_ts_suspend - enter sleep mode
+ *
+ * when Suspend mode, disable_irq and cancel work queue.
+ *
+ */
+static int elants_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_err(&client->dev,
+ "[ELAN] %s: enter\n", __func__);
+
+ /* Command not support in IAP recovery mode */
+ if (test_bit(idx_update_fw, &ts->flags))
+ return 0;
+
+ disable_irq(client->irq);
+ cancel_work_sync(&ts->work);
+
+ mutex_lock(&ts->mutex);
+ rc = elants_set_data(client,
+ set_sleep_cmd, sizeof(set_sleep_cmd));
+
+ if (rc < 0)
+ goto end;
+
+ ts->power_state = 0;
+ /* de-active timer */
+ del_timer_sync(&ts->polling.timer);
+
+
+end:
+ mutex_unlock(&ts->mutex);
+ return rc;
+}
+
+
+/** @brief \b elan_ts_resume - enter wake-up mode
+ *
+ * when Wake-up mode, enable_irq.
+ *
+ */
+static int elants_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, retry = 5;
+
+ dev_err(&client->dev,
+ "[ELAN] %s: enter\n", __func__);
+
+ /* Command not support in IAP recovery mode */
+ if (test_bit(idx_update_fw, &ts->flags))
+ return 0;
+
+ mutex_lock(&ts->mutex);
+
+ do {
+ rc = elants_set_data(client,
+ set_active_cmd, sizeof(set_active_cmd));
+ elan_msleep(2);
+ rc = elants_get_power_state(client);
+ if (unlikely(rc != PWR_STATE_NORMAL))
+ dev_err(&client->dev,
+ "[ELAN] %s: wake up tp failed! err = %d\n",
+ __func__, rc);
+ else
+ break;
+ } while (--retry);
+
+ ts->power_state = 1;
+ mutex_unlock(&ts->mutex);
+
+ /* re-active poll timer */
+ mod_timer(&ts->polling.timer, jiffies + 10 * HZ);
+
+ enable_irq(client->irq);
+
+ return rc;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(elants_pm, elants_suspend, elants_resume);
+
+/*! brief system registeration */
+static const struct i2c_device_id elan_ts_id[] = {
+ { ELAN_DEV_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, elan_ts_id);
+
+/*! brief system registeration */
+static struct i2c_driver elan_ts_driver = {
+ .probe = elants_probe,
+ .remove = __devexit_p(elants_remove),
+ .id_table = elan_ts_id,
+ .driver = {
+ .name = ELAN_DEV_NAME,
+ .owner = THIS_MODULE,
+ .bus = &i2c_bus_type,
+ .pm = &elants_pm,
+ },
+};
+
+module_i2c_driver(elan_ts_driver);
+
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_DESCRIPTION("Elan Microelectronics Touchpanels");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/i2c/elants.h b/include/linux/i2c/elants.h
new file mode 100644
index 0000000..5b45a0c
--- /dev/null
+++ b/include/linux/i2c/elants.h
@@ -0,0 +1,61 @@
+#ifndef _LINUX_I2C_ELANTS_H
+#define _LINUX_I2C_ELANTS_H
+
+#define ELAN_DEV_NAME "elants_i2c"
+
+/* set TSP resolution by actual TPM */
+#define ELAN_X_MAX 2944
+#define ELAN_Y_MAX 1856
+
+
+/*! @brief \b platform data
+ * @param intr_gpio : gpio pin
+ * @param rst_gpio : interuption pin
+ *
+ * Platform data, all platform dependent variable should put here.
+ *
+ */
+struct elan_i2c_platform_data {
+ unsigned short version;
+ unsigned int abs_x_min;
+ unsigned int abs_x_max;
+ unsigned int abs_y_min;
+ unsigned int abs_y_max;
+ int intr_gpio;
+ int rst_gpio;
+};
+
+struct elan_ioctl_data {
+ int len;
+ char buf[32];
+};
+
+/*! @brief \b ioctl command definition.
+ * @param IOCTL_I2C_SLAVE : set i2c address that would be controled right now.
+ * @param IOCTL_MAJOR_FW_VER : fw major number
+ * @param IOCTL_MINOR_FW_VER : fw minor number
+ * @param IOCTL_RESET : Hardware Reset
+ * @param IOCTL_SET_COMMAND : control command set to TP device.
+ * @param IOCTL_GET_COMMAND : get response from our TP device.
+ * @param IOCTL_IAP_ENABLE : Enter IAP mode
+ * @param IOCTL_IAP_DISABLE : Leave IAP mode
+ * @param IOCTL_I2C_INT : gpio status
+ *
+ *
+ * ioctl command, easy user to control our TP from application.
+ *
+ */
+#define ELAN_IOCTLID 0xD0
+#define IOCTL_I2C_SLAVE _IOW(ELAN_IOCTLID, 1, int)
+#define IOCTL_MAJOR_FW_VER _IOR(ELAN_IOCTLID, 2, int)
+#define IOCTL_MINOR_FW_VER _IOR(ELAN_IOCTLID, 3, int)
+#define IOCTL_RESET _IOW(ELAN_IOCTLID, 4, int)
+#define IOCTL_I2C_INT _IOR(ELAN_IOCTLID, 8, int)
+#define IOCTL_SET_COMMAND _IOW(ELAN_IOCTLID, 9, unsigned long)
+#define IOCTL_GET_COMMAND _IOR(ELAN_IOCTLID, 10, unsigned long)
+#define IOCTL_IAP_ENABLE _IOW(ELAN_IOCTLID, 11, int)
+#define IOCTL_IAP_DISABLE _IOW(ELAN_IOCTLID, 12, int)
+#define IOCTL_CHECK_RECOVERY_MODE _IOR(ELAN_IOCTLID, 13, int)
+#define IOCTL_BOOTCODE_CMD _IOW(ELAN_IOCTLID, 14, unsigned long)
+
+#endif /* _LINUX_I2C_ELANTS_H */
--
1.7.9.5
Hi Scott,
On Wed, Oct 24, 2012 at 09:41:43AM +0800, Scott Liu wrote:
> This patch is for Elan eKTF Touchscreen product, I2C adpater module.
>
> Signed-off-by: Scott Liu <[email protected]>
> ---
>
> Hi,
> v2 revision I have fixed some bug as your advise.
> 1. To target the mainline
> 2. No Android dependency
> 3. reuse those duplication code from Henrik's patchset.
> (input_mt_sync_frame() / input_mt_get_slot_by_key())
Just a quick run through the code, so:
- please remove polling support, it is not useful in production;
- why do you need a separate probe work instead of doing what you
need in elants_probe()
- it is not a good idea to register input device first and then
allocating memory for MT handling.
- I do not understand why kfifo is needed
- please remove the rest of the custom threads
- you do not need to call input_mt_destroy_slots() explicitly
- use request_firmware() instead of special character device to upload
firmware.
- please use standard kernel-doc markup.
- consider what attributes are there only for debugging and move them to
debugfs.
- I find the use of enums in this driver quite unconventional, just
standard #defines would probably be more straightforward.
Thanks.
--
Dmitry
Hi Dmitry,
Thanks for review.
> -----Original Message-----
> From: Dmitry Torokhov [mailto:[email protected]]
> Sent: Thursday, October 25, 2012 2:13 AM
> To: Scott Liu
> Cc: [email protected]; [email protected];
[email protected];
> Benjamin Tissoires; Jesse; Vincent Wang; Paul
> Subject: Re: [PATCH v2] Support Elan Touchscreen eKTF product.
>
> Hi Scott,
>
> On Wed, Oct 24, 2012 at 09:41:43AM +0800, Scott Liu wrote:
> > This patch is for Elan eKTF Touchscreen product, I2C adpater module.
> >
> > Signed-off-by: Scott Liu <[email protected]>
> > ---
> >
> > Hi,
> > v2 revision I have fixed some bug as your advise.
> > 1. To target the mainline
> > 2. No Android dependency
> > 3. reuse those duplication code from Henrik's patchset.
> > (input_mt_sync_frame() / input_mt_get_slot_by_key())
>
> Just a quick run through the code, so:
>
> - please remove polling support, it is not useful in production;
OK.
> - why do you need a separate probe work instead of doing what you
> need in elants_probe()
will fix.
> - it is not a good idea to register input device first and then
> allocating memory for MT handling.
Ooop...will fix.
> - I do not understand why kfifo is needed
The firmware and the host would conflict by read command and finger report
simultaneously. So I'm simply using kfifo in IRQ thread function.
* read command: writing 4 bytes commands and the device asserts GPIO
interrupt and then response 4 bytes data.
There was an error if we do not use kfifo:
With heavy loading by finger report / read command, the driver may
get finger report as response data.
So, do you understand my meaning?
> - please remove the rest of the custom threads
OK
> - you do not need to call input_mt_destroy_slots() explicitly
OK
> - use request_firmware() instead of special character device to upload
> firmware.
OK, but I'll remove firmware update function at this patch first.
> - please use standard kernel-doc markup.
> - consider what attributes are there only for debugging and move them to
> debugfs.
OK.
> - I find the use of enums in this driver quite unconventional, just
> standard #defines would probably be more straightforward.
OK.
Thanks,
Scott
>
> Thanks.
>
> --
> Dmitry
On Thu, Oct 25, 2012 at 12:32:39PM +0800, 劉嘉駿 wrote:
> Hi Dmitry,
> Thanks for review.
>
> > -----Original Message-----
> > From: Dmitry Torokhov [mailto:[email protected]]
> > Sent: Thursday, October 25, 2012 2:13 AM
> > To: Scott Liu
> > Cc: [email protected]; [email protected];
> [email protected];
> > Benjamin Tissoires; Jesse; Vincent Wang; Paul
> > Subject: Re: [PATCH v2] Support Elan Touchscreen eKTF product.
> >
> > Hi Scott,
> >
> > On Wed, Oct 24, 2012 at 09:41:43AM +0800, Scott Liu wrote:
> > > This patch is for Elan eKTF Touchscreen product, I2C adpater module.
> > >
> > > Signed-off-by: Scott Liu <[email protected]>
> > > ---
> > >
> > > Hi,
> > > v2 revision I have fixed some bug as your advise.
> > > 1. To target the mainline
> > > 2. No Android dependency
> > > 3. reuse those duplication code from Henrik's patchset.
> > > (input_mt_sync_frame() / input_mt_get_slot_by_key())
> >
> > Just a quick run through the code, so:
> >
> > - please remove polling support, it is not useful in production;
>
> OK.
>
> > - why do you need a separate probe work instead of doing what you
> > need in elants_probe()
>
> will fix.
>
> > - it is not a good idea to register input device first and then
> > allocating memory for MT handling.
>
> Ooop...will fix.
>
> > - I do not understand why kfifo is needed
>
> The firmware and the host would conflict by read command and finger report
> simultaneously. So I'm simply using kfifo in IRQ thread function.
>
> * read command: writing 4 bytes commands and the device asserts GPIO
> interrupt and then response 4 bytes data.
>
> There was an error if we do not use kfifo:
> With heavy loading by finger report / read command, the driver may
> get finger report as response data.
>
> So, do you understand my meaning?
No I don't. Most of my confusion stems from the fact that you only put
data into kfifo but not actually use it anywhere. You do fetch the data
in your "drop old" function, but that data is just dropped. So I really
do not see the point.
Thanks.
--
Dmitry