aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/touchscreen/mms_ts_gc.c
diff options
context:
space:
mode:
authorcodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
committercodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
commitc6da2cfeb05178a11c6d062a06f8078150ee492f (patch)
treef3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/input/touchscreen/mms_ts_gc.c
parentc6d7c4dbff353eac7919342ae6b3299a378160a6 (diff)
downloadkernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip
kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz
kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2
samsung update 1
Diffstat (limited to 'drivers/input/touchscreen/mms_ts_gc.c')
-rw-r--r--drivers/input/touchscreen/mms_ts_gc.c2950
1 files changed, 2950 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/mms_ts_gc.c b/drivers/input/touchscreen/mms_ts_gc.c
new file mode 100644
index 0000000..6fe7dbb
--- /dev/null
+++ b/drivers/input/touchscreen/mms_ts_gc.c
@@ -0,0 +1,2950 @@
+/*
+ * mms_ts.c - Touchscreen driver for Melfas MMS-series touch controllers
+ *
+ * Copyright (C) 2011 Google Inc.
+ * Author: Dima Zavin <dima@android.com>
+ * Simon Wilson <simonwilson@google.com>
+ *
+ * ISP reflashing code based on original code from Melfas.
+ * ISC reflashing code based on original code from Melfas.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#define SHOW_COORD 1
+#define ISC_DL_MODE 1
+#define TOUCH_BOOSTER 0
+#define SEC_TSP_FACTORY_TEST
+/* #define ESD_DEBUG */
+
+#include <linux/delay.h>
+#include <linux/earlysuspend.h>
+#include <linux/firmware.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <mach/gpio.h>
+#include <linux/uaccess.h>
+#include <linux/platform_data/mms_ts_gc.h>
+#include <asm/unaligned.h>
+#include <linux/fb.h>
+#if TOUCH_BOOSTER
+#include <mach/cpufreq.h>
+#include <mach/dev.h>
+#endif
+
+/* Model dependency values */
+#define MAX_FINGERS 10
+#define MAX_WIDTH 30
+#define MAX_PRESSURE 255
+#define FINGER_EVENT_SZ 6
+#define TSP_VENDOR "MELFAS"
+#define TSP_IC "MMS136"
+#define FW_VERSION 0x02 /* GC Bring-up */
+
+/* Registers */
+#define MMS_INPUT_EVENT_PKT_SZ 0x0F
+#define MMS_INPUT_EVENT0 0x10
+#define MMS_FW_VER_CORE 0xF3 /* GC Bring-up */
+#define MMS_FW_VER_PRIV 0xF4 /* GC Bring-up */
+#define MMS_FW_VER_PUBL 0xF5 /* GC Bring-up */
+#define MMS_FW_VERSION 0xF5 /* GC Bring-up */
+#define MMS_TA_NOISE_REG 0x30
+#define MMS_TA_NOISE_ON 0x01
+#define MMS_TA_NOISE_OFF 0x02
+
+enum {
+ TSP_STATE_RELEASE = 0,
+ TSP_STATE_PRESS,
+ TSP_STATE_MOVE,
+};
+
+#if TOUCH_BOOSTER
+#define TOUCH_BOOSTER_OFF_TIME 100
+#define TOUCH_BOOSTER_CHG_TIME 200
+#endif
+
+#if ISC_DL_MODE /* ISC_DL_MODE start */
+#define MAX_FW_PATH 255
+enum {
+ BUILT_IN = 0,
+ UMS,
+ REQ_FW,
+};
+
+enum {
+ ISC_NONE = -1,
+ ISC_SUCCESS = 0,
+ ISC_FILE_OPEN_ERROR,
+ ISC_FILE_CLOSE_ERROR,
+ ISC_FILE_FORMAT_ERROR,
+ ISC_WRITE_BUFFER_ERROR,
+ ISC_I2C_ERROR,
+ ISC_UPDATE_MODE_ENTER_ERROR,
+ ISC_CRC_ERROR,
+ ISC_VALIDATION_ERROR,
+ ISC_COMPATIVILITY_ERROR,
+ ISC_UPDATE_SECTION_ERROR,
+ ISC_SLAVE_ERASE_ERROR,
+ ISC_SLAVE_DOWNLOAD_ERROR,
+ ISC_DOWNLOAD_WHEN_SLAVE_IS_UPDATED_ERROR,
+ ISC_INITIAL_PACKET_ERROR,
+ ISC_NO_NEED_UPDATE_ERROR,
+ ISC_LIMIT
+};
+
+enum {
+ EC_NONE = -1,
+ EC_DEPRECATED = 0,
+ EC_BOOTLOADER_RUNNING = 1,
+ EC_BOOT_ON_SUCCEEDED = 2,
+ EC_ERASE_END_MARKER_ON_SLAVE_FINISHED = 3,
+ EC_SLAVE_DOWNLOAD_STARTS = 4,
+ EC_SLAVE_DOWNLOAD_FINISHED = 5,
+ EC_2CHIP_HANDSHAKE_FAILED = 0x0E,
+ EC_ESD_PATTERN_CHECKED = 0x0F,
+ EC_LIMIT
+};
+
+enum {
+ SEC_NONE = -1,
+ SEC_BOOTLOADER = 0,
+ SEC_CORE,
+ SEC_PRIVATE_CONFIG,
+ SEC_PUBLIC_CONFIG,
+ SEC_LIMIT
+};
+
+struct tISCFWInfo_t {
+ unsigned char version;
+ unsigned char compatible_version;
+ unsigned char start_addr;
+ unsigned char end_addr;
+};
+
+/*
+ * Default configuration of ISC mode
+ */
+#define DEFAULT_SLAVE_ADDR 0x48
+
+#define SECTION_NUM 4
+#define SECTION_NAME_LEN 5
+
+#define PAGE_HEADER 3
+#define PAGE_DATA 1024
+#define PAGE_TAIL 2
+#define PACKET_SIZE (PAGE_HEADER + PAGE_DATA + PAGE_TAIL)
+#define TS_WRITE_REGS_LEN 1030
+
+#define TIMEOUT_CNT 10
+#define STRING_BUF_LEN 100
+
+/* State Registers */
+#define MIP_ADDR_INPUT_INFORMATION 0x01
+#define ISC_ADDR_VERSION 0xE1
+#define ISC_ADDR_SECTION_PAGE_INFO 0xE5
+
+/* Config Update Commands */
+#define ISC_CMD_ENTER_ISC 0x5F
+#define ISC_CMD_ENTER_ISC_PARA1 0x01
+#define ISC_CMD_UPDATE_MODE 0xAE
+#define ISC_SUBCMD_ENTER_UPDATE 0x55
+#define ISC_SUBCMD_DATA_WRITE 0XF1
+#define ISC_SUBCMD_LEAVE_UPDATE_PARA1 0x0F
+#define ISC_SUBCMD_LEAVE_UPDATE_PARA2 0xF0
+#define ISC_CMD_CONFIRM_STATUS 0xAF
+
+#define ISC_STATUS_UPDATE_MODE 0x01
+#define ISC_STATUS_CRC_CHECK_SUCCESS 0x03
+
+#define ISC_CHAR_2_BCD(num) (((num/10)<<4) + (num%10))
+#define ISC_MAX(x, y) (((x) > (y)) ? (x) : (y))
+
+static const char section_name[SECTION_NUM][SECTION_NAME_LEN] = {
+ "BOOT", "CORE", "PRIV", "PUBL"
+};
+
+static const unsigned char crc0_buf[31] = {
+ 0x1D, 0x2C, 0x05, 0x34, 0x95, 0xA4, 0x8D, 0xBC,
+ 0x59, 0x68, 0x41, 0x70, 0xD1, 0xE0, 0xC9, 0xF8,
+ 0x3F, 0x0E, 0x27, 0x16, 0xB7, 0x86, 0xAF, 0x9E,
+ 0x7B, 0x4A, 0x63, 0x52, 0xF3, 0xC2, 0xEB
+};
+
+static const unsigned char crc1_buf[31] = {
+ 0x1E, 0x9C, 0xDF, 0x5D, 0x76, 0xF4, 0xB7, 0x35,
+ 0x2A, 0xA8, 0xEB, 0x69, 0x42, 0xC0, 0x83, 0x01,
+ 0x04, 0x86, 0xC5, 0x47, 0x6C, 0xEE, 0xAD, 0x2F,
+ 0x30, 0xB2, 0xF1, 0x73, 0x58, 0xDA, 0x99
+};
+
+static struct tISCFWInfo_t mbin_info[SECTION_NUM]; /* F/W ver from File */
+static struct tISCFWInfo_t ts_info[SECTION_NUM]; /* F/W ver from IC */
+static bool section_update_flag[SECTION_NUM];
+const struct firmware *fw_mbin[SECTION_NUM];
+static unsigned char g_wr_buf[1024 + 3 + 2];
+
+enum {
+ COMPARE_UPDATE = 0,
+ FORCED_UPDATE,
+};
+#endif /* ISC_DL_MODE end */
+
+enum {
+ ISP_MODE_FLASH_ERASE = 0x59F3,
+ ISP_MODE_FLASH_WRITE = 0x62CD,
+ ISP_MODE_FLASH_READ = 0x6AC9,
+};
+
+/* each address addresses 4-byte words */
+#define ISP_MAX_FW_SIZE (0x1F00 * 4)
+#define ISP_IC_INFO_ADDR 0x1F00
+
+#ifdef SEC_TSP_FACTORY_TEST
+/* Model dependency values */
+#define TX_NUM 19 /* GC Bring-up */
+#define RX_NUM 11 /* GC Bring-up */
+#define NODE_NUM (TX_NUM * RX_NUM)
+#define TSP_BUF_SIZE 1024
+
+/* VSC(Vender Specific Command) */
+#define MMS_VSC_CMD 0xB0 /* vendor specific command */
+#define MMS_VSC_MODE 0x1A /* mode of vendor */
+
+#define MMS_VSC_CMD_ENTER 0X01
+#define MMS_VSC_CMD_CM_DELTA 0X02
+#define MMS_VSC_CMD_CM_ABS 0X03
+#define MMS_VSC_CMD_EXIT 0X05
+#define MMS_VSC_CMD_INTENSITY 0X04
+#define MMS_VSC_CMD_RAW 0X06
+#define MMS_VSC_CMD_REFER 0X07
+
+#define TSP_CMD_STR_LEN 32
+#define TSP_CMD_RESULT_STR_LEN 512
+#define TSP_CMD_PARAM_NUM 8
+
+enum {
+ FAIL_PWR_CONTROL = -1,
+ SUCCESS_PWR_CONTROL = 0,
+};
+
+enum { /* this is using by cmd_state valiable. */
+ WAITING = 0,
+ RUNNING,
+ OK,
+ FAIL,
+ NOT_APPLICABLE,
+};
+#endif /* SEC_TSP_FACTORY_TEST */
+
+struct tsp_callbacks {
+ void (*inform_charger)(struct tsp_callbacks *tsp_cb, bool mode);
+};
+
+struct mms_ts_info {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ char phys[32];
+ int max_x;
+ int max_y;
+ bool invert_x;
+ bool invert_y;
+ int irq;
+ int (*power) (int on);
+ void (*input_event)(void *data);
+ bool enabled;
+ u8 fw_ic_ver;
+ const u8 *config_fw_version;
+ unsigned char finger_state[MAX_FINGERS];
+
+ struct melfas_tsi_platform_data *pdata;
+ struct early_suspend early_suspend;
+ struct mutex lock;
+
+ void (*register_cb)(void *);
+ struct tsp_callbacks callbacks;
+ bool ta_status;
+ bool noise_mode;
+
+#if TOUCH_BOOSTER
+ struct delayed_work work_dvfs_off;
+ struct delayed_work work_dvfs_chg;
+ bool dvfs_lock_status;
+ int cpufreq_level;
+ struct mutex dvfs_lock;
+ struct device *bus_dev;
+ struct device *sec_touchscreen;
+#endif /* TOUCH_BOOSTER */
+
+#if defined(SEC_TSP_FACTORY_TEST)
+ struct list_head cmd_list_head;
+ u8 cmd_state;
+ char cmd[TSP_CMD_STR_LEN];
+ int cmd_param[TSP_CMD_PARAM_NUM];
+ char cmd_result[TSP_CMD_RESULT_STR_LEN];
+ struct mutex cmd_lock;
+ bool cmd_is_running;
+ bool ft_flag;
+
+ unsigned int reference[NODE_NUM];
+ unsigned int raw[NODE_NUM]; /* CM_ABS */
+ unsigned int inspection[NODE_NUM];/* CM_DELTA */
+ unsigned int intensity[NODE_NUM];
+#endif /* SEC_TSP_FACTORY_TEST */
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void mms_ts_early_suspend(struct early_suspend *h);
+static void mms_ts_late_resume(struct early_suspend *h);
+#endif
+
+#if defined(SEC_TSP_FACTORY_TEST)
+#define TSP_CMD(name, func) .cmd_name = name, .cmd_func = func
+
+struct tsp_cmd {
+ struct list_head list;
+ const char *cmd_name;
+ void (*cmd_func)(void *device_data);
+};
+
+static void fw_update(void *device_data);
+static void get_fw_ver_bin(void *device_data);
+static void get_fw_ver_ic(void *device_data);
+static void get_config_ver(void *device_data);
+static void get_threshold(void *device_data);
+static void module_off_master(void *device_data);
+static void module_on_master(void *device_data);
+static void module_off_slave(void *device_data);
+static void module_on_slave(void *device_data);
+static void get_chip_vendor(void *device_data);
+static void get_chip_name(void *device_data);
+static void get_reference(void *device_data);
+static void get_cm_abs(void *device_data);
+static void get_cm_delta(void *device_data);
+static void get_intensity(void *device_data);
+static void get_x_num(void *device_data);
+static void get_y_num(void *device_data);
+static void run_reference_read(void *device_data);
+static void run_cm_abs_read(void *device_data);
+static void run_cm_delta_read(void *device_data);
+static void run_intensity_read(void *device_data);
+static void not_support_cmd(void *device_data);
+
+struct tsp_cmd tsp_cmds[] = {
+ {TSP_CMD("fw_update", fw_update),},
+ {TSP_CMD("get_fw_ver_bin", get_fw_ver_bin),},
+ {TSP_CMD("get_fw_ver_ic", get_fw_ver_ic),},
+ {TSP_CMD("get_config_ver", get_config_ver),},
+ {TSP_CMD("get_threshold", get_threshold),},
+ {TSP_CMD("module_off_master", module_off_master),},
+ {TSP_CMD("module_on_master", module_on_master),},
+ {TSP_CMD("module_off_slave", not_support_cmd),},
+ {TSP_CMD("module_on_slave", not_support_cmd),},
+ {TSP_CMD("get_chip_vendor", get_chip_vendor),},
+ {TSP_CMD("get_chip_name", get_chip_name),},
+ {TSP_CMD("get_x_num", get_x_num),},
+ {TSP_CMD("get_y_num", get_y_num),},
+ {TSP_CMD("get_reference", get_reference),},
+ {TSP_CMD("get_cm_abs", get_cm_abs),},
+ {TSP_CMD("get_cm_delta", get_cm_delta),},
+ {TSP_CMD("get_intensity", get_intensity),},
+ {TSP_CMD("run_reference_read", run_reference_read),},
+ {TSP_CMD("run_cm_abs_read", run_cm_abs_read),},
+ {TSP_CMD("run_cm_delta_read", run_cm_delta_read),},
+ {TSP_CMD("run_intensity_read", run_intensity_read),},
+ {TSP_CMD("not_support_cmd", not_support_cmd),},
+};
+#endif
+
+#if ISC_DL_MODE
+
+u8 *tsp_firmware_file[4] = {0, };
+
+static int mms100_i2c_read(struct i2c_client *client,
+ u16 addr, u16 length, u8 *value)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct i2c_msg msg;
+ int ret = -1;
+
+ msg.addr = client->addr;
+ msg.flags = 0x00;
+ msg.len = 1;
+ msg.buf = (u8 *) &addr;
+
+ ret = i2c_transfer(adapter, &msg, 1);
+
+ if (ret >= 0) {
+ msg.addr = client->addr;
+ msg.flags = I2C_M_RD;
+ msg.len = length;
+ msg.buf = (u8 *) value;
+
+ ret = i2c_transfer(adapter, &msg, 1);
+ }
+
+ if (ret < 0)
+ pr_err("[TSP] : read error : [%d]", ret);
+
+ return ret;
+}
+
+
+static int mms100_reset(struct i2c_client *client)
+{
+ struct mms_ts_info *info = i2c_get_clientdata(client);
+
+ info->pdata->power(false);
+ msleep(30);
+ info->pdata->power(true);
+ msleep(300);
+
+ return ISC_SUCCESS;
+}
+/*
+static int mms100_check_operating_mode(struct i2c_client *_client,
+ const int _error_code)
+{
+ int ret;
+ unsigned char rd_buf = 0x00;
+ unsigned char count = 0;
+
+ if(_client == NULL)
+ pr_err("[TSP ISC] _client is null");
+
+ ret = mms100_i2c_read(_client, ISC_ADDR_VERSION, 1, &rd_buf);
+
+ if (ret<0) {
+ pr_info("[TSP ISC] %s,%d: i2c read fail[%d]\n",
+ __func__, __LINE__, ret);
+ return _error_code;
+ }
+
+ return ISC_SUCCESS;
+}
+*/
+static int mms100_get_version_info(struct i2c_client *_client)
+{
+ int i, ret;
+ unsigned char rd_buf[8];
+
+ /* config version brust read (core, private, public) */
+ ret = mms100_i2c_read(_client, ISC_ADDR_VERSION, 4, rd_buf);
+
+ if (ret < 0) {
+ pr_info("[TSP ISC] %s,%d: i2c read fail[%d]\n",
+ __func__, __LINE__, ret);
+ return ISC_I2C_ERROR;
+ }
+
+ for (i = 0; i < SECTION_NUM; i++)
+ ts_info[i].version = rd_buf[i];
+
+ ts_info[SEC_CORE].compatible_version =
+ ts_info[SEC_BOOTLOADER].version;
+ ts_info[SEC_PRIVATE_CONFIG].compatible_version =
+ ts_info[SEC_PUBLIC_CONFIG].compatible_version =
+ ts_info[SEC_CORE].version;
+
+ ret = mms100_i2c_read(_client, ISC_ADDR_SECTION_PAGE_INFO, 8, rd_buf);
+
+ if (ret < 0) {
+ pr_info("[TSP ISC] %s,%d: i2c read fail[%d]\n",
+ __func__, __LINE__, ret);
+ return ISC_I2C_ERROR;
+ }
+
+ for (i = 0; i < SECTION_NUM; i++) {
+ ts_info[i].start_addr = rd_buf[i];
+ ts_info[i].end_addr = rd_buf[i + SECTION_NUM];
+ }
+
+ for (i = 0; i < SECTION_NUM; i++) {
+ pr_info("[TSP ISC] TS : Section(%d) version: 0x%02X",
+ i, ts_info[i].version);
+ pr_info("[TSP ISC] TS : Section(%d) Start Address: 0x%02X",
+ i, ts_info[i].start_addr);
+ pr_info("[TSP ISC] TS : Section(%d) End Address: 0x%02X",
+ i, ts_info[i].end_addr);
+ pr_info("[TSP ISC] TS : Section(%d) Compatibility: 0x%02X",
+ i, ts_info[i].compatible_version);
+ }
+
+ return ISC_SUCCESS;
+}
+
+static int mms100_seek_section_info(void)
+{
+ int i;
+ char str_buf[STRING_BUF_LEN];
+ char name_buf[SECTION_NAME_LEN];
+ int version;
+ int page_num;
+
+ const unsigned char *buf;
+ int next_ptr;
+
+ for (i = 0; i < SECTION_NUM; i++) {
+ if (tsp_firmware_file[i] == NULL) {
+ buf = NULL;
+ pr_info("[TSP ISC] tsp_firmware_file[%d] is NULL", i);
+ } else
+ buf = tsp_firmware_file[i];
+
+ if (buf == NULL) {
+ mbin_info[i].version = ts_info[i].version;
+ mbin_info[i].compatible_version =
+ ts_info[i].compatible_version;
+ mbin_info[i].start_addr = ts_info[i].start_addr;
+ mbin_info[i].end_addr = ts_info[i].end_addr;
+ } else {
+ next_ptr = 0;
+
+ do {
+ sscanf(buf + next_ptr, "%s", str_buf);
+ next_ptr += strlen(str_buf) + 1;
+ } while (!strstr(str_buf, "SECTION_NAME"));
+
+ sscanf(buf + next_ptr, "%s%s", str_buf, name_buf);
+
+ if (strncmp(section_name[i], name_buf,
+ SECTION_NAME_LEN))
+ return ISC_FILE_FORMAT_ERROR;
+
+ do {
+ sscanf(buf + next_ptr, "%s", str_buf);
+ next_ptr += strlen(str_buf) + 1;
+ } while (!strstr(str_buf, "SECTION_VERSION"));
+
+ sscanf(buf + next_ptr, "%s%d", str_buf, &version);
+ mbin_info[i].version = ISC_CHAR_2_BCD(version);
+
+ do {
+ sscanf(buf + next_ptr, "%s", str_buf);
+ next_ptr += strlen(str_buf) + 1;
+ } while (!strstr(str_buf, "START_PAGE_ADDR"));
+
+ sscanf(buf + next_ptr, "%s%d", str_buf, &page_num);
+ mbin_info[i].start_addr = page_num;
+
+ do {
+ sscanf(buf + next_ptr, "%s", str_buf);
+ next_ptr += strlen(str_buf) + 1;
+ } while (!strstr(str_buf, "END_PAGE_ADDR"));
+
+ sscanf(buf + next_ptr, "%s%d", str_buf, &page_num);
+ mbin_info[i].end_addr = page_num;
+
+ do {
+ sscanf(buf + next_ptr, "%s", str_buf);
+ next_ptr += strlen(str_buf) + 1;
+ } while (!strstr(str_buf, "COMPATIBLE_VERSION"));
+
+ sscanf(buf + next_ptr, "%s%d", str_buf, &version);
+ mbin_info[i].compatible_version =
+ ISC_CHAR_2_BCD(version);
+
+ do {
+ sscanf(buf + next_ptr, "%s", str_buf);
+ next_ptr += strlen(str_buf) + 1;
+ } while (!strstr(str_buf, "[Binary]"));
+
+ if (mbin_info[i].version == 0xFF)
+ return ISC_FILE_FORMAT_ERROR;
+ }
+ }
+
+ for (i = 0; i < SECTION_NUM; i++) {
+ pr_info("[TSP ISC] MBin : Section(%d) Version: 0x%02X",
+ i, mbin_info[i].version);
+ pr_info("[TSP ISC] MBin : Section(%d) Start Address: 0x%02X",
+ i, mbin_info[i].start_addr);
+ pr_info("[TSP ISC] MBin : Section(%d) End Address: 0x%02X",
+ i, mbin_info[i].end_addr);
+ pr_info("[TSP ISC] MBin : Section(%d) Compatibility: 0x%02X",
+ i, mbin_info[i].compatible_version);
+ }
+
+ return ISC_SUCCESS;
+}
+
+static int mms100_compare_version_info(struct i2c_client *_client,
+ bool forced_update)
+{
+ int i, ret;
+ unsigned char expected_compatibility[SECTION_NUM];
+
+ if (mms100_get_version_info(_client) != ISC_SUCCESS)
+ return ISC_I2C_ERROR;
+
+ ret = mms100_seek_section_info();
+ if (ret)
+ return ret;
+
+ section_update_flag[0] = false;
+ /* Check update areas , 0 : bootloader 1: core 2: private 3: public */
+ for (i = SEC_CORE; i < SECTION_NUM; i++) {
+ if ((mbin_info[i].version == 0) ||
+ (mbin_info[i].version != ts_info[i].version)) {
+ section_update_flag[i] = true;
+ pr_info("[TSP ISC] [%d] section will be updated!", i);
+ }
+ }
+ if (forced_update) {
+ section_update_flag[SEC_CORE] = true;
+ section_update_flag[SEC_PRIVATE_CONFIG] = true;
+ section_update_flag[SEC_PUBLIC_CONFIG] = true;
+ pr_info("[TSP ISC] forced_update enable!");
+ }
+ pr_info("[TSP ISC] section_update_flag : [%d][%d][%d]",
+ section_update_flag[1], section_update_flag[2],
+ section_update_flag[3]);
+
+ if (section_update_flag[SEC_BOOTLOADER]) {
+ expected_compatibility[SEC_CORE] =
+ mbin_info[SEC_BOOTLOADER].version;
+ } else {
+ expected_compatibility[SEC_CORE] =
+ ts_info[SEC_BOOTLOADER].version;
+ }
+
+ if (section_update_flag[SEC_CORE]) {
+ expected_compatibility[SEC_PUBLIC_CONFIG] =
+ expected_compatibility[SEC_PRIVATE_CONFIG] =
+ mbin_info[SEC_CORE].version;
+ } else {
+ expected_compatibility[SEC_PUBLIC_CONFIG] =
+ expected_compatibility[SEC_PRIVATE_CONFIG] =
+ ts_info[SEC_CORE].version;
+ }
+
+ for (i = SEC_CORE; i < SEC_PUBLIC_CONFIG; i++) {
+ if (section_update_flag[i]) {
+ pr_info("[TSP ISC] section_update_flag(%d), 0x%02x, 0x%02x\n",
+ i, expected_compatibility[i],
+ mbin_info[i].compatible_version);
+ if (!forced_update) {
+ if (expected_compatibility[i] !=
+ mbin_info[i].compatible_version)
+ return ISC_COMPATIVILITY_ERROR;
+ }
+ } else {
+ pr_info("[TSP ISC] !section_update_flag(%d), 0x%02x, 0x%02x\n",
+ i, expected_compatibility[i],
+ ts_info[i].compatible_version);
+ if (!forced_update) {
+ if (expected_compatibility[i] !=
+ ts_info[i].compatible_version)
+ return ISC_COMPATIVILITY_ERROR;
+ }
+ }
+ }
+ return ISC_SUCCESS;
+}
+
+static int mms100_enter_ISC_mode(struct i2c_client *_client)
+{
+ int ret;
+ unsigned char wr_buf[2];
+
+ pr_info("[TSP ISC] %s\n", __func__);
+
+ wr_buf[0] = ISC_CMD_ENTER_ISC;
+ wr_buf[1] = ISC_CMD_ENTER_ISC_PARA1;
+
+ ret = i2c_master_send(_client, wr_buf, 2);
+
+ if (ret < 0) {
+ pr_info("[TSP ISC] %s,%d: i2c write fail[%d]\n",
+ __func__, __LINE__, ret);
+ return ISC_I2C_ERROR;
+ }
+
+ msleep(50);
+
+ return ISC_SUCCESS;
+}
+
+static int mms100_enter_config_update(struct i2c_client *_client)
+{
+ int ret;
+ unsigned char wr_buf[10] = {0,};
+ unsigned char rd_buf;
+
+ wr_buf[0] = ISC_CMD_UPDATE_MODE;
+ wr_buf[1] = ISC_SUBCMD_ENTER_UPDATE;
+
+ ret = i2c_master_send(_client, wr_buf, 10);
+
+ if (ret < 0) {
+ pr_info("[TSP ISC] %s,%d: i2c write fail[%d]\n",
+ __func__, __LINE__, ret);
+ return ISC_I2C_ERROR;
+ }
+
+ ret = mms100_i2c_read(_client, ISC_CMD_CONFIRM_STATUS, 1, &rd_buf);
+ if (ret < 0) {
+ pr_info("[TSP ISC] %s,%d: i2c read fail[%d]\n",
+ __func__, __LINE__, ret);
+ return ISC_I2C_ERROR;
+ }
+
+ if (rd_buf != ISC_STATUS_UPDATE_MODE)
+ return ISC_UPDATE_MODE_ENTER_ERROR;
+
+ return ISC_SUCCESS;
+}
+
+static int mms100_ISC_clear_page(struct i2c_client *_client,
+ unsigned char _page_addr)
+{
+ int ret;
+ unsigned char rd_buf;
+
+ memset(&g_wr_buf[3], 0xFF, PAGE_DATA);
+
+ g_wr_buf[0] = ISC_CMD_UPDATE_MODE; /* command */
+ g_wr_buf[1] = ISC_SUBCMD_DATA_WRITE; /* sub_command */
+ g_wr_buf[2] = _page_addr;
+
+ g_wr_buf[PAGE_HEADER + PAGE_DATA] = crc0_buf[_page_addr];
+ g_wr_buf[PAGE_HEADER + PAGE_DATA + 1] = crc1_buf[_page_addr];
+
+ ret = i2c_master_send(_client, g_wr_buf, PACKET_SIZE);
+
+ if (ret < 0) {
+ pr_info("[TSP ISC] %s,%d: i2c write fail[%d]\n",
+ __func__, __LINE__, ret);
+ return ISC_I2C_ERROR;
+ }
+
+ ret = mms100_i2c_read(_client, ISC_CMD_CONFIRM_STATUS, 1, &rd_buf);
+
+ if (ret < 0) {
+ pr_info("[TSP ISC] %s,%d: i2c read fail[%d]\n",
+ __func__, __LINE__, ret);
+ return ISC_I2C_ERROR;
+ }
+
+ if (rd_buf != ISC_STATUS_CRC_CHECK_SUCCESS)
+ return ISC_UPDATE_MODE_ENTER_ERROR;
+
+ return ISC_SUCCESS;
+}
+
+static int mms100_ISC_clear_validate_markers(struct i2c_client *_client)
+{
+ int ret_msg;
+ int i, j;
+ bool is_matched_address;
+
+ for (i = SEC_CORE; i <= SEC_PUBLIC_CONFIG; i++) {
+ if (section_update_flag[i]) {
+ if (ts_info[i].end_addr <= 30 &&
+ ts_info[i].end_addr > 0) {
+ ret_msg = mms100_ISC_clear_page(_client,
+ ts_info[i].end_addr);
+
+ if (ret_msg != ISC_SUCCESS)
+ return ret_msg;
+ }
+ }
+ }
+
+ for (i = SEC_CORE; i <= SEC_PUBLIC_CONFIG; i++) {
+ if (section_update_flag[i]) {
+ is_matched_address = false;
+ for (j = SEC_CORE; j <= SEC_PUBLIC_CONFIG; j++) {
+ if (mbin_info[i].end_addr ==
+ ts_info[i].end_addr) {
+ is_matched_address = true;
+ break;
+ }
+ }
+
+ if (!is_matched_address) {
+ if (mbin_info[i].end_addr <= 30 &&
+ mbin_info[i].end_addr > 0) {
+ ret_msg = mms100_ISC_clear_page(_client,
+ mbin_info[i].end_addr);
+
+ if (ret_msg != ISC_SUCCESS)
+ return ret_msg;
+ }
+ }
+ }
+ }
+
+ return ISC_SUCCESS;
+}
+
+static int mms100_update_section_data(struct i2c_client *_client)
+{
+ int i, ret, next_ptr;
+ unsigned char rd_buf;
+ const unsigned char *ptr_fw;
+ char str_buf[STRING_BUF_LEN];
+ int page_addr;
+
+ for (i = 0; i < SECTION_NUM; i++) {
+ if (section_update_flag[i]) {
+ pr_info("[TSP ISC] section data i2c flash : [%d]", i);
+
+ next_ptr = 0;
+ ptr_fw = tsp_firmware_file[i];
+
+ do {
+ sscanf(ptr_fw + next_ptr, "%s", str_buf);
+ next_ptr += strlen(str_buf) + 1;
+ } while (!strstr(str_buf, "[Binary]"));
+ ptr_fw = ptr_fw + next_ptr + 2;
+
+ for (page_addr = mbin_info[i].start_addr;
+ page_addr <= mbin_info[i].end_addr;
+ page_addr++) {
+ if (page_addr - mbin_info[i].start_addr > 0)
+ ptr_fw += PACKET_SIZE;
+
+ if ((ptr_fw[0] != ISC_CMD_UPDATE_MODE) ||
+ (ptr_fw[1] != ISC_SUBCMD_DATA_WRITE) ||
+ (ptr_fw[2] != page_addr))
+ return ISC_WRITE_BUFFER_ERROR;
+
+ ret = i2c_master_send(_client,
+ ptr_fw, PACKET_SIZE);
+ if (ret < 0) {
+ pr_info("[TSP ISC] %s,%d: i2c write fail[%d]",
+ __func__, __LINE__, ret);
+ return ISC_I2C_ERROR;
+ }
+
+ ret = mms100_i2c_read(_client,
+ ISC_CMD_CONFIRM_STATUS, 1, &rd_buf);
+ if (ret < 0) {
+ pr_info("[TSP ISC] %s,%d: i2c read fail[%d]",
+ __func__, __LINE__, ret);
+ return ISC_I2C_ERROR;
+ }
+
+ if (rd_buf != ISC_STATUS_CRC_CHECK_SUCCESS)
+ return ISC_CRC_ERROR;
+
+ section_update_flag[i] = false;
+ }
+ }
+ }
+ return ISC_SUCCESS;
+}
+
+static int mms100_open_mbinary(struct i2c_client *_client, int mode)
+{
+ int i;
+ int ret = 0;
+ mm_segment_t old_fs = {0};
+ struct file *fp = NULL;
+ long fsize = 0, nread = 0;
+/* u8 *fw_name[] = {"BOOT", "CORE", "PRIV", "PUBL"}; */
+ char fw_path[MAX_FW_PATH+1];
+
+ if (mode == REQ_FW) {
+ for (i = SEC_CORE; i <= SEC_PUBLIC_CONFIG ; i++) {
+ snprintf(fw_path, MAX_FW_PATH, "tsp_melfas/GC_%s.fw",
+ section_name[i]);
+
+ ret = request_firmware(&(fw_mbin[i]), fw_path,
+ &_client->dev);
+ if (ret) {
+ pr_err("[TSP ISC] fail requestfirmware[%d]", i);
+ break;
+ }
+
+ tsp_firmware_file[i] = kzalloc((size_t)fw_mbin[i]->size,
+ GFP_KERNEL);
+
+ if (!tsp_firmware_file[i])
+ pr_err("[TSP ISC] fail to alloc buffer for fw");
+ else
+ memcpy((void *)tsp_firmware_file[i],
+ fw_mbin[i]->data, fw_mbin[i]->size);
+
+ if (fw_mbin[i] != NULL)
+ release_firmware(fw_mbin[i]);
+ }
+ pr_info("[TSP ISC] request_firmware[%d] is loaded!!", i);
+ } else if (mode == UMS) {
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+
+ for (i = SEC_CORE ; i <= SEC_PUBLIC_CONFIG ; i++) {
+ snprintf(fw_path, MAX_FW_PATH, "/sdcard/%s.mbin",
+ section_name[i]);
+ fp = filp_open(fw_path, O_RDONLY, 0);
+ if (IS_ERR(fp)) {
+ pr_err("[TSP ISC] file %s open error:%d",
+ fw_path, (s32)fp);
+ ret = 1;
+ }
+
+ fsize = fp->f_path.dentry->d_inode->i_size;
+
+ tsp_firmware_file[i] = kzalloc((size_t)fsize,
+ GFP_KERNEL);
+ if (!tsp_firmware_file[i]) {
+ pr_err("[TSP ISC] fail to alloc buffer for fw");
+ ret = 1;
+ }
+
+ nread = vfs_read(fp,
+ (char __user *)tsp_firmware_file[i],
+ fsize, &fp->f_pos);
+ if (nread != fsize) {
+ pr_err("[TSP ISC] nread != fsize error");
+ ret = 1;
+ }
+
+ filp_close(fp, current->files);
+ }
+
+ set_fs(old_fs);
+ pr_info("[TSP ISC] ums fw is loaded!");
+ } else {
+ pr_err("[TSP ISC] Not support mode[%d]", mode);
+ ret = 1;
+ }
+
+ if (!ret)
+ return ISC_SUCCESS;
+ else {
+ pr_err("[TSP ISC] mms100_open_mbinary fail");
+ return ret;
+ }
+}
+
+int mms100_ISC_download_mbinary(struct i2c_client *_client, int mode,
+ bool forced_update)
+{
+ int ret_msg = ISC_NONE;
+
+ pr_info("[TSP ISC] %s\n", __func__);
+/*
+ mms100_reset(_client);
+ ret_msg = mms100_check_operating_mode(_client, EC_BOOT_ON_SUCCEEDED);
+ if (ret_msg != ISC_SUCCESS)
+ goto ISC_ERROR_HANDLE;
+*/
+ ret_msg = mms100_open_mbinary(_client, mode);
+ if (ret_msg != ISC_SUCCESS)
+ goto ISC_ERROR_HANDLE;
+
+ /*Config version Check*/
+ ret_msg = mms100_compare_version_info(_client, forced_update);
+ if (ret_msg != ISC_SUCCESS)
+ goto ISC_ERROR_HANDLE;
+
+ ret_msg = mms100_enter_ISC_mode(_client);
+ if (ret_msg != ISC_SUCCESS)
+ goto ISC_ERROR_HANDLE;
+
+ ret_msg = mms100_enter_config_update(_client);
+ if (ret_msg != ISC_SUCCESS)
+ goto ISC_ERROR_HANDLE;
+
+ ret_msg = mms100_ISC_clear_validate_markers(_client);
+ if (ret_msg != ISC_SUCCESS)
+ goto ISC_ERROR_HANDLE;
+
+ ret_msg = mms100_update_section_data(_client);
+ if (ret_msg != ISC_SUCCESS)
+ goto ISC_ERROR_HANDLE;
+
+ ret_msg = ISC_SUCCESS;
+
+ISC_ERROR_HANDLE:
+ if (ret_msg != ISC_SUCCESS)
+ pr_err("[TSP ISC] ISC_ERROR_CODE: %d\n", ret_msg);
+
+ mms100_reset(_client);
+
+ kfree(tsp_firmware_file[1]);
+ kfree(tsp_firmware_file[2]);
+ kfree(tsp_firmware_file[3]);
+
+ pr_info("[TSP ISC]FIRMWARE_UPDATE_FINISHED!");
+
+ return ret_msg;
+}
+#endif /* ISC_DL_MODE start */
+
+
+#if TOUCH_BOOSTER
+static void change_dvfs_lock(struct work_struct *work)
+{
+ struct mms_ts_info *info = container_of(work,
+ struct mms_ts_info, work_dvfs_chg.work);
+ int ret;
+
+ mutex_lock(&info->dvfs_lock);
+
+ ret = dev_lock(info->bus_dev, info->sec_touchscreen, 267160);
+
+ if (ret < 0)
+ pr_err("melfas-ts : %s dev change bud lock failed(%d)\n",\
+ __func__, __LINE__);
+ else
+ pr_info("melfas-ts : change_dvfs_lock");
+ mutex_unlock(&info->dvfs_lock);
+}
+static void set_dvfs_off(struct work_struct *work)
+{
+
+ struct mms_ts_info *info = container_of(work,
+ struct mms_ts_info, work_dvfs_off.work);
+ int ret;
+
+ mutex_lock(&info->dvfs_lock);
+
+ ret = dev_unlock(info->bus_dev, info->sec_touchscreen);
+ if (ret < 0)
+ pr_err("melfas-ts : %s: dev unlock failed(%d)\n",
+ __func__, __LINE__);
+
+ exynos_cpufreq_lock_free(DVFS_LOCK_ID_TSP);
+ info->dvfs_lock_status = false;
+ pr_info("melfas-ts : DVFS Off!");
+ mutex_unlock(&info->dvfs_lock);
+ }
+
+static void set_dvfs_lock(struct mms_ts_info *info, uint32_t on)
+{
+ int ret;
+
+ mutex_lock(&info->dvfs_lock);
+ if (info->cpufreq_level <= 0) {
+ ret = exynos_cpufreq_get_level(800000, &info->cpufreq_level);
+ if (ret < 0)
+ pr_err("melfas-ts : exynos_cpufreq_get_level error");
+ goto out;
+ }
+ if (on == 0) {
+ if (info->dvfs_lock_status) {
+ cancel_delayed_work(&info->work_dvfs_chg);
+ schedule_delayed_work(&info->work_dvfs_off,
+ msecs_to_jiffies(TOUCH_BOOSTER_OFF_TIME));
+ }
+
+ } else if (on == 1) {
+ cancel_delayed_work(&info->work_dvfs_off);
+ if (!info->dvfs_lock_status) {
+ ret = dev_lock(info->bus_dev,
+ info->sec_touchscreen, 400200);
+ if (ret < 0) {
+ pr_err("melfas-ts : %s: dev lock failed(%d)",\
+ __func__, __LINE__);
+}
+
+ ret = exynos_cpufreq_lock(DVFS_LOCK_ID_TSP,
+ info->cpufreq_level);
+ if (ret < 0)
+ pr_err("melfas-ts : %s: cpu lock failed(%d)",\
+ __func__, __LINE__);
+
+ schedule_delayed_work(&info->work_dvfs_chg,
+ msecs_to_jiffies(TOUCH_BOOSTER_CHG_TIME));
+
+ info->dvfs_lock_status = true;
+ pr_info("melfas-ts :DVFS On[%d]", info->cpufreq_level);
+ }
+ } else if (on == 2) {
+ cancel_delayed_work(&info->work_dvfs_off);
+ cancel_delayed_work(&info->work_dvfs_chg);
+ schedule_work(&info->work_dvfs_off.work);
+ }
+out:
+ mutex_unlock(&info->dvfs_lock);
+}
+#endif
+
+static void release_all_fingers(struct mms_ts_info *info)
+{
+ int i;
+
+ printk(KERN_DEBUG "[TSP] %s\n", __func__);
+
+ for (i = 0; i < MAX_FINGERS; i++) {
+ input_mt_slot(info->input_dev, i);
+ input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER,
+ false);
+ info->finger_state[i] = TSP_STATE_RELEASE;
+ }
+ input_sync(info->input_dev);
+
+#if TOUCH_BOOSTER
+ set_dvfs_lock(info, 2);
+ dev_notice(&client->dev, "[TSP] dvfs_lock free.\n");
+#endif
+}
+
+static void reset_mms_ts(struct mms_ts_info *info)
+{
+ struct i2c_client *client = info->client;
+
+ dev_notice(&client->dev, "%s called, tsp state [%s]!\n",
+ __func__, info->enabled ? "enable" : "disable");
+ if (info->enabled == false)
+ return;
+
+ disable_irq_nosync(info->irq);
+ info->enabled = false;
+
+ info->pdata->power(0);
+ msleep(30);
+
+ release_all_fingers(info);
+
+ info->pdata->power(1);
+ msleep(120);
+
+ enable_irq(info->irq);
+ info->enabled = true;
+}
+
+static void mms_set_noise_mode(struct mms_ts_info *info)
+{
+ struct i2c_client *client = info->client;
+
+ if (!(info->noise_mode && info->enabled))
+ return;
+ dev_notice(&client->dev, "%s\n", __func__);
+
+ if (info->ta_status) {
+ dev_notice(&client->dev, "TA connect!!!\n");
+ i2c_smbus_write_byte_data(info->client, MMS_TA_NOISE_REG,
+ MMS_TA_NOISE_ON);
+ } else {
+ dev_notice(&client->dev, "TA disconnect!!!\n");
+ i2c_smbus_write_byte_data(info->client, MMS_TA_NOISE_REG,
+ MMS_TA_NOISE_OFF);
+ info->noise_mode = 0;
+ }
+}
+
+static void melfas_ta_cb(struct tsp_callbacks *cb, bool ta_status)
+{
+ struct mms_ts_info *info =
+ container_of(cb, struct mms_ts_info, callbacks);
+ struct i2c_client *client = info->client;
+
+ dev_notice(&client->dev, "%s [%s]\n", __func__,
+ ta_status ? "ON" : "OFF");
+
+ info->ta_status = ta_status;
+ /* GC Not support yet!
+ if (!ta_status)
+ mms_set_noise_mode(info);
+ */
+}
+
+static irqreturn_t mms_ts_interrupt(int irq, void *dev_id)
+{
+ struct mms_ts_info *info = dev_id;
+ struct i2c_client *client = info->client;
+ u8 buf[MAX_FINGERS * FINGER_EVENT_SZ] = { 0, };
+ int ret;
+ int i;
+ int sz;
+ u8 reg = MMS_INPUT_EVENT0;
+#if TOUCH_BOOSTER
+ int touch_is_pressed = 0;
+#endif
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .buf = &reg,
+ .len = 1,
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .buf = buf,
+ },
+ };
+
+ sz = i2c_smbus_read_byte_data(client, MMS_INPUT_EVENT_PKT_SZ);
+
+ if (sz < 0) {
+ dev_err(&client->dev, "%s bytes=%d\n", __func__, sz);
+ for (i = 0; i < 50; i++) {
+ sz = i2c_smbus_read_byte_data(client,
+ MMS_INPUT_EVENT_PKT_SZ);
+ if (sz > 0)
+ break;
+ }
+
+ if (i == 50) {
+ dev_dbg(&client->dev, "i2c failed... reset!!\n");
+ reset_mms_ts(info);
+ goto out;
+ }
+ }
+ if (sz == 0)
+ goto out;
+
+ if (sz > MAX_FINGERS*FINGER_EVENT_SZ) {
+ dev_err(&client->dev, "[TSP] abnormal data inputed.\n");
+ goto out;
+ }
+
+ msg[1].len = sz;
+ ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+ if (ret != ARRAY_SIZE(msg)) {
+ dev_err(&client->dev,
+ "failed to read %d bytes of touch data (%d)\n",
+ sz, ret);
+ goto out;
+ }
+
+ if (buf[0] == 0x0F) { /* ESD */
+ dev_dbg(&client->dev, "ESD DETECT.... reset!!\n");
+ reset_mms_ts(info);
+ goto out;
+ }
+
+ if (buf[0] == 0x0E) { /* NOISE MODE */
+ dev_dbg(&client->dev, "[TSP] noise mode enter!!\n");
+ info->noise_mode = 1 ;
+ /* GC Not support yet!
+ mms_set_noise_mode(info);
+ */
+ goto out;
+ }
+
+ for (i = 0; i < sz; i += FINGER_EVENT_SZ) {
+ u8 *tmp = &buf[i];
+ int id = (tmp[0] & 0xf) - 1;
+ int x = tmp[2] | ((tmp[1] & 0xf) << 8);
+ int y = tmp[3] | (((tmp[1] >> 4) & 0xf) << 8);
+ if (info->invert_x) {
+ x = info->max_x - x;
+ if (x < 0)
+ x = 0;
+ }
+ if (info->invert_y) {
+ y = info->max_y - y;
+ if (y < 0)
+ y = 0;
+ }
+ if (id >= MAX_FINGERS) {
+ dev_notice(&client->dev, \
+ "finger id error [%d]\n", id);
+ reset_mms_ts(info);
+ goto out;
+ }
+
+ if ((tmp[0] & 0x80) == TSP_STATE_RELEASE) {
+ input_mt_slot(info->input_dev, id);
+ input_mt_report_slot_state(info->input_dev,
+ MT_TOOL_FINGER, false);
+ input_sync(info->input_dev);
+#if SHOW_COORD
+ dev_notice(&client->dev,
+ "R [%2d],([%4d],[%3d]),S:%d W:%d [%d]",
+ id, x, y, tmp[4], tmp[5],
+ info->finger_state[id]);
+#else
+ dev_notice(&client->dev, "R [%2d][%d]", id,
+ info->finger_state[id]);
+#endif
+ info->finger_state[id] = TSP_STATE_RELEASE;
+ } else {
+ input_mt_slot(info->input_dev, id);
+ input_mt_report_slot_state(info->input_dev,
+ MT_TOOL_FINGER, true);
+ input_report_abs(info->input_dev,
+ ABS_MT_POSITION_X, x);
+ input_report_abs(info->input_dev,
+ ABS_MT_POSITION_Y, y);
+ input_report_abs(info->input_dev,
+ ABS_MT_TOUCH_MAJOR, tmp[4]);
+ input_report_abs(info->input_dev,
+ ABS_MT_WIDTH_MAJOR, tmp[5]);
+ input_sync(info->input_dev);
+
+ if (info->finger_state[id] == TSP_STATE_RELEASE) {
+ info->finger_state[id] = TSP_STATE_PRESS;
+#if SHOW_COORD
+ dev_notice(&client->dev,
+ "P [%2d],([%4d],[%3d]),S:%d W:%d",
+ id, x, y, tmp[4], tmp[5]);
+#else
+ dev_notice(&client->dev, "P [%2d]", id);
+#endif
+ } else
+ info->finger_state[id] = TSP_STATE_MOVE;
+ }
+ }
+
+#if TOUCH_BOOSTER
+ for (i = 0 ; MAX_FINGERS ; i++)
+ if (info->finger_state[i] == TSP_STATE_PRESS
+ || info->finger_state[i] == TSP_STATE_MOVE)
+ touch_is_pressed++;
+
+ set_dvfs_lock(info, !!touch_is_pressed);
+#endif
+
+out:
+ return IRQ_HANDLED;
+}
+
+static void hw_reboot(struct mms_ts_info *info, bool bootloader)
+{
+ info->pdata->power(0);
+ gpio_direction_output(info->pdata->gpio_sda, bootloader ? 0 : 1);
+ gpio_direction_output(info->pdata->gpio_scl, bootloader ? 0 : 1);
+ gpio_direction_output(info->pdata->gpio_int, 0);
+ msleep(30);
+ info->pdata->power(1);
+ msleep(30);
+
+ if (bootloader) {
+ gpio_set_value(info->pdata->gpio_scl, 0);
+ gpio_set_value(info->pdata->gpio_sda, 1);
+ } else {
+ gpio_set_value(info->pdata->gpio_int, 1);
+ gpio_direction_input(info->pdata->gpio_int);
+ gpio_direction_input(info->pdata->gpio_scl);
+ gpio_direction_input(info->pdata->gpio_sda);
+ }
+ msleep(40);
+}
+
+static inline void hw_reboot_bootloader(struct mms_ts_info *info)
+{
+ hw_reboot(info, true);
+}
+
+static inline void hw_reboot_normal(struct mms_ts_info *info)
+{
+ hw_reboot(info, false);
+}
+
+static void isp_toggle_clk(struct mms_ts_info *info, int start_lvl, int end_lvl,
+ int hold_us)
+{
+ gpio_set_value(info->pdata->gpio_scl, start_lvl);
+ udelay(hold_us);
+ gpio_set_value(info->pdata->gpio_scl, end_lvl);
+ udelay(hold_us);
+}
+
+/* 1 <= cnt <= 32 bits to write */
+static void isp_send_bits(struct mms_ts_info *info, u32 data, int cnt)
+{
+ gpio_direction_output(info->pdata->gpio_int, 0);
+ gpio_direction_output(info->pdata->gpio_scl, 0);
+ gpio_direction_output(info->pdata->gpio_sda, 0);
+
+ /* clock out the bits, msb first */
+ while (cnt--) {
+ gpio_set_value(info->pdata->gpio_sda, (data >> cnt) & 1);
+ udelay(3);
+ isp_toggle_clk(info, 1, 0, 3);
+ }
+}
+
+/* 1 <= cnt <= 32 bits to read */
+static u32 isp_recv_bits(struct mms_ts_info *info, int cnt)
+{
+ u32 data = 0;
+
+ gpio_direction_output(info->pdata->gpio_int, 0);
+ gpio_direction_output(info->pdata->gpio_scl, 0);
+ gpio_set_value(info->pdata->gpio_sda, 0);
+ gpio_direction_input(info->pdata->gpio_sda);
+
+ /* clock in the bits, msb first */
+ while (cnt--) {
+ isp_toggle_clk(info, 0, 1, 1);
+ data = (data << 1) | (!!gpio_get_value(info->pdata->gpio_sda));
+ }
+
+ gpio_direction_output(info->pdata->gpio_sda, 0);
+ return data;
+}
+
+static void isp_enter_mode(struct mms_ts_info *info, u32 mode)
+{
+ int cnt;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ gpio_direction_output(info->pdata->gpio_int, 0);
+ gpio_direction_output(info->pdata->gpio_scl, 0);
+ gpio_direction_output(info->pdata->gpio_sda, 1);
+
+ mode &= 0xffff;
+ for (cnt = 15; cnt >= 0; cnt--) {
+ gpio_set_value(info->pdata->gpio_int, (mode >> cnt) & 1);
+ udelay(3);
+ isp_toggle_clk(info, 1, 0, 3);
+ }
+
+ gpio_set_value(info->pdata->gpio_int, 0);
+ local_irq_restore(flags);
+}
+
+static void isp_exit_mode(struct mms_ts_info *info)
+{
+ int i;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ gpio_direction_output(info->pdata->gpio_int, 0);
+ udelay(3);
+
+ for (i = 0; i < 10; i++)
+ isp_toggle_clk(info, 1, 0, 3);
+ local_irq_restore(flags);
+}
+
+static void flash_set_address(struct mms_ts_info *info, u16 addr)
+{
+ /* Only 13 bits of addr are valid.
+ * The addr is in bits 13:1 of cmd */
+ isp_send_bits(info, (u32) (addr & 0x1fff) << 1, 18);
+}
+
+static void flash_erase(struct mms_ts_info *info)
+{
+ isp_enter_mode(info, ISP_MODE_FLASH_ERASE);
+
+ gpio_direction_output(info->pdata->gpio_int, 0);
+ gpio_direction_output(info->pdata->gpio_scl, 0);
+ gpio_direction_output(info->pdata->gpio_sda, 1);
+
+ /* 4 clock cycles with different timings for the erase to
+ * get processed, clk is already 0 from above */
+ udelay(7);
+ isp_toggle_clk(info, 1, 0, 3);
+ udelay(7);
+ isp_toggle_clk(info, 1, 0, 3);
+ usleep_range(25000, 35000);
+ isp_toggle_clk(info, 1, 0, 3);
+ usleep_range(150, 200);
+ isp_toggle_clk(info, 1, 0, 3);
+
+ gpio_set_value(info->pdata->gpio_sda, 0);
+
+ isp_exit_mode(info);
+}
+
+static u32 flash_readl(struct mms_ts_info *info, u16 addr)
+{
+ int i;
+ u32 val;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ isp_enter_mode(info, ISP_MODE_FLASH_READ);
+ flash_set_address(info, addr);
+
+ gpio_direction_output(info->pdata->gpio_scl, 0);
+ gpio_direction_output(info->pdata->gpio_sda, 0);
+ udelay(40);
+
+ /* data load cycle */
+ for (i = 0; i < 6; i++)
+ isp_toggle_clk(info, 1, 0, 10);
+
+ val = isp_recv_bits(info, 32);
+ isp_exit_mode(info);
+ local_irq_restore(flags);
+
+ return val;
+}
+
+static void flash_writel(struct mms_ts_info *info, u16 addr, u32 val)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ isp_enter_mode(info, ISP_MODE_FLASH_WRITE);
+ flash_set_address(info, addr);
+ isp_send_bits(info, val, 32);
+
+ gpio_direction_output(info->pdata->gpio_sda, 1);
+ /* 6 clock cycles with different timings for the data to get written
+ * into flash */
+ isp_toggle_clk(info, 0, 1, 3);
+ isp_toggle_clk(info, 0, 1, 3);
+ isp_toggle_clk(info, 0, 1, 6);
+ isp_toggle_clk(info, 0, 1, 12);
+ isp_toggle_clk(info, 0, 1, 3);
+ isp_toggle_clk(info, 0, 1, 3);
+
+ isp_toggle_clk(info, 1, 0, 1);
+
+ gpio_direction_output(info->pdata->gpio_sda, 0);
+ isp_exit_mode(info);
+ local_irq_restore(flags);
+ usleep_range(300, 400);
+}
+
+static bool flash_is_erased(struct mms_ts_info *info)
+{
+ struct i2c_client *client = info->client;
+ u32 val;
+ u16 addr;
+
+ for (addr = 0; addr < (ISP_MAX_FW_SIZE / 4); addr++) {
+ udelay(40);
+ val = flash_readl(info, addr);
+
+ if (val != 0xffffffff) {
+ dev_dbg(&client->dev,
+ "addr 0x%x not erased: 0x%08x != 0xffffffff\n",
+ addr, val);
+ return false;
+ }
+ }
+ return true;
+}
+
+static int fw_write_image(struct mms_ts_info *info, const u8 * data, size_t len)
+{
+ struct i2c_client *client = info->client;
+ u16 addr = 0;
+
+ for (addr = 0; addr < (len / 4); addr++, data += 4) {
+ u32 val = get_unaligned_le32(data);
+ u32 verify_val;
+ int retries = 3;
+
+ while (retries--) {
+ flash_writel(info, addr, val);
+ verify_val = flash_readl(info, addr);
+ if (val == verify_val)
+ break;
+ dev_err(&client->dev,
+ "mismatch @ addr 0x%x: 0x%x != 0x%x\n",
+ addr, verify_val, val);
+ continue;
+ }
+ if (retries < 0)
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int mms100_ISP_download(struct mms_ts_info *info, const u8 * data,
+ size_t len)
+{
+ struct i2c_client *client = info->client;
+ u32 val;
+ int ret = 0;
+
+ if (len % 4) {
+ dev_err(&client->dev,
+ "fw image size (%d) must be a multiple of 4 bytes\n",
+ len);
+ return -EINVAL;
+ } else if (len > ISP_MAX_FW_SIZE) {
+ dev_err(&client->dev,
+ "fw image is too big, %d > %d\n", len, ISP_MAX_FW_SIZE);
+ return -EINVAL;
+ }
+
+ dev_info(&client->dev, "fw download start\n");
+
+ info->pdata->power(0);
+ gpio_direction_output(info->pdata->gpio_sda, 0);
+ gpio_direction_output(info->pdata->gpio_scl, 0);
+ gpio_direction_output(info->pdata->gpio_int, 0);
+
+ hw_reboot_bootloader(info);
+
+ val = flash_readl(info, ISP_IC_INFO_ADDR);
+ dev_info(&client->dev, "IC info before erase : [%x]\n", val);
+
+ dev_info(&client->dev, "fw erase...\n");
+ flash_erase(info);
+ if (!flash_is_erased(info)) {
+ ret = -ENXIO;
+ goto err;
+ }
+
+ flash_writel(info, ISP_IC_INFO_ADDR, val);
+ val = flash_readl(info, ISP_IC_INFO_ADDR);
+ dev_info(&client->dev, "IC info after erase & write : [%x]\n", val);
+
+ dev_info(&client->dev, "fw write...\n");
+ usleep_range(1000, 1500);
+ ret = fw_write_image(info, data, len);
+ if (ret)
+ goto err;
+ usleep_range(1000, 1500);
+
+ hw_reboot_normal(info);
+ usleep_range(1000, 1500);
+ dev_info(&client->dev, "fw download done...\n");
+ return 0;
+
+err:
+ dev_err(&client->dev, "fw download failed...\n");
+ hw_reboot_normal(info);
+ return ret;
+}
+
+static int get_fw_version(struct mms_ts_info *info)
+{
+ struct i2c_client *client = info->client;
+ int ret;
+ int retries = 3;
+
+ /* this seems to fail sometimes after a reset.. retry a few times */
+ do {
+ ret = i2c_smbus_read_byte_data(info->client, MMS_FW_VERSION);
+ } while (ret < 0 && retries-- > 0);
+
+ dev_info(&client->dev, "MMS_FW_VER_CORE [0x%02x]",
+ i2c_smbus_read_byte_data(info->client, MMS_FW_VER_CORE));
+ dev_info(&client->dev, "MMS_FW_VER_PRIV [0x%02x]",
+ i2c_smbus_read_byte_data(info->client, MMS_FW_VER_PRIV));
+ dev_info(&client->dev, "MMS_FW_VER_PUBL [0x%02x]",
+ i2c_smbus_read_byte_data(info->client, MMS_FW_VER_PUBL));
+ return ret;
+}
+
+static int mms_ts_finish_config(struct mms_ts_info *info)
+{
+ struct i2c_client *client = info->client;
+ int ret;
+
+ ret = request_threaded_irq(client->irq, NULL, mms_ts_interrupt,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ MELFAS_TS_NAME, info);
+ if (ret < 0) {
+ ret = 1;
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_req_irq;
+ }
+
+ info->irq = client->irq;
+ barrier();
+
+ dev_info(&client->dev,
+ "Melfas MMS-series touch controller initialized\n");
+
+ return 0;
+
+err_req_irq:
+ return ret;
+}
+static int mms_ts_fw_info(struct mms_ts_info *info)
+{
+ struct i2c_client *client = info->client;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret = 0;
+ int ver;
+
+ /* firmware check */
+ ver = get_fw_version(info);
+ info->fw_ic_ver = ver;
+ dev_info(&client->dev, "FW ver [0x%02x]", ver);
+
+ if (!info->pdata || !info->pdata->mux_fw_flash) {
+ ret = 1;
+ dev_err(&client->dev,
+ "fw cannot be updated, missing platform data\n");
+ return ret;
+ }
+
+ ret = request_threaded_irq(client->irq, NULL, mms_ts_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ MELFAS_TS_NAME, info);
+ if (ret < 0) {
+ ret = 1;
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ return ret;
+ }
+
+ info->irq = client->irq;
+ barrier();
+
+ dev_info(&client->dev,
+ "Melfas MMS-series touch controller initialized\n");
+
+ return ret;
+}
+
+static int mms_ts_fw_load(struct mms_ts_info *info)
+{
+
+ struct i2c_client *client = info->client;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret = 0, ret_isp = 0;
+ int ver = 0x00;
+ int retries_isc = 3;
+ int retries_isp = 3;
+
+ /* firmware check */
+ ver = get_fw_version(info);
+ info->fw_ic_ver = ver;
+ dev_info(&client->dev, "FW ver. [0x%02x]", ver);
+ goto done;
+
+ if (!info->pdata || !info->pdata->mux_fw_flash) {
+ ret = 1;
+ dev_err(&client->dev,
+ "fw cannot be updated, missing platform data\n");
+ return ret;
+ }
+
+#if 0
+/* firmware update 3 try */
+/* ISC firmware update */
+
+ /* ISP firmware update */
+ while (retries_isc--) {
+ ret = mms100_ISC_download_mbinary(client, REQ_FW,
+ COMPARE_UPDATE);
+
+ if (ret > 0) {
+ dev_err(&client->dev, "ISP D/L mode fail\n");
+
+ i2c_lock_adapter(adapter);
+ info->pdata->mux_fw_flash(true);
+
+ while (retries_isp--) {
+ ret_isp = mms100_ISP_download(info,
+ BOOT_binary,
+ BOOT_binary_nLength);
+ if (ret_isp < 0)
+ dev_err(&client->dev,
+ "ISP D/L mode fail\n");
+ else {
+ dev_err(&client->dev,
+ "ISP D/L mode success\n");
+ break;
+ }
+ }
+
+ info->pdata->mux_fw_flash(false);
+ i2c_unlock_adapter(adapter);
+
+ ret = mms100_ISC_download_mbinary(client, REQ_FW,
+ FORCED_UPDATE);
+ }
+
+ if (ret == ISC_SUCCESS) {
+ ver = get_fw_version(info);
+ info->fw_ic_ver = ver;
+ dev_info(&client->dev, "fw update done: ver = 0x%02x\n",
+ ver);
+ goto done;
+ }
+ dev_err(&client->dev, "retrying flashing\n");
+ continue;
+ }
+
+ if (retries_isc == 0) {
+ dev_err(&client->dev, "tsp f/w update all fail!\n");
+ return ret;
+ }
+#endif
+
+done:
+ ret = request_threaded_irq(client->irq, NULL, mms_ts_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ MELFAS_TS_NAME, info);
+ if (ret < 0) {
+ ret = 1;
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ return ret;
+ }
+
+ info->irq = client->irq;
+ barrier();
+
+ dev_info(&client->dev,
+ "Melfas MMS-series touch controller initialized\n");
+ return ret;
+}
+
+
+#ifdef SEC_TSP_FACTORY_TEST
+#if 0
+static inline int msm_irq_to_gpio(unsigned irq)
+{
+ /* TODO : Need to verify chip->base=0 */
+ return irq - MSM_GPIO_TO_INT(0);
+}
+#endif
+
+static void set_cmd_result(struct mms_ts_info *info, char *buff, int len)
+{
+ strncat(info->cmd_result, buff, len);
+}
+
+static void get_raw_data_all(struct mms_ts_info *info, u8 cmd)
+{
+ u8 w_buf[6];
+ u8 read_buffer[2]; /* 52 */
+ int gpio;
+ int ret;
+ int i, j;
+ u32 max_value = 0, min_value = 0;
+ u32 raw_data;
+ char buff[TSP_CMD_STR_LEN] = {0};
+ gpio = info->pdata->gpio_int;
+
+/* gpio = msm_irq_to_gpio(info->irq); */
+ disable_irq(info->irq);
+
+ w_buf[0] = MMS_VSC_CMD; /* vendor specific command id */
+ w_buf[1] = MMS_VSC_MODE; /* mode of vendor */
+ w_buf[2] = 0; /* tx line */
+ w_buf[3] = 0; /* rx line */
+ w_buf[4] = 0; /* reserved */
+ w_buf[5] = 0; /* sub command */
+
+ if (cmd == MMS_VSC_CMD_EXIT) {
+ w_buf[5] = MMS_VSC_CMD_EXIT; /* exit test mode */
+
+ ret = i2c_smbus_write_i2c_block_data(info->client,
+ w_buf[0], 5, &w_buf[1]);
+ if (ret < 0)
+ goto err_i2c;
+ enable_irq(info->irq);
+ msleep(200);
+ return;
+ }
+
+ /* MMS_VSC_CMD_CM_DELTA or MMS_VSC_CMD_CM_ABS
+ * this two mode need to enter the test mode
+ * exit command must be followed by testing.
+ */
+ if (cmd == MMS_VSC_CMD_CM_DELTA || cmd == MMS_VSC_CMD_CM_ABS) {
+ /* enter the debug mode */
+ w_buf[2] = 0x0; /* tx */
+ w_buf[3] = 0x0; /* rx */
+ w_buf[5] = MMS_VSC_CMD_ENTER;
+
+ ret = i2c_smbus_write_i2c_block_data(info->client,
+ w_buf[0], 5, &w_buf[1]);
+ if (ret < 0)
+ goto err_i2c;
+
+ /* wating for the interrupt */
+ while (gpio_get_value(gpio))
+ udelay(100);
+ }
+
+ for (i = 0; i < RX_NUM; i++) {
+ for (j = 0; j < TX_NUM; j++) {
+
+ w_buf[2] = j; /* tx */
+ w_buf[3] = i; /* rx */
+ w_buf[5] = cmd;
+
+ ret = i2c_smbus_write_i2c_block_data(info->client,
+ w_buf[0], 5, &w_buf[1]);
+ if (ret < 0)
+ goto err_i2c;
+
+ usleep_range(1, 5);
+
+ ret = i2c_smbus_read_i2c_block_data(info->client, 0xBF,
+ 2, read_buffer);
+ if (ret < 0)
+ goto err_i2c;
+
+ raw_data = ((u16) read_buffer[1] << 8) | read_buffer[0];
+ if (i == 0 && j == 0) {
+ max_value = min_value = raw_data;
+ } else {
+ max_value = max(max_value, raw_data);
+ min_value = min(min_value, raw_data);
+ }
+
+ if (cmd == MMS_VSC_CMD_INTENSITY) {
+ info->intensity[j * RX_NUM + i] = raw_data;
+ dev_dbg(&info->client->dev, "[TSP] intensity[%d][%d] = %d\n",
+ i, j, info->intensity[j * RX_NUM + i]);
+ } else if (cmd == MMS_VSC_CMD_CM_DELTA) {
+ info->inspection[j * RX_NUM + i] = raw_data;
+ dev_dbg(&info->client->dev, "[TSP] delta[%d][%d] = %d\n",
+ i, j, info->inspection[j * RX_NUM + i]);
+ } else if (cmd == MMS_VSC_CMD_CM_ABS) {
+ info->raw[j * RX_NUM + i] = raw_data;
+ dev_dbg(&info->client->dev, "[TSP] raw[%d][%d] = %d\n",
+ i, j, info->raw[j * RX_NUM + i]);
+ } else if (cmd == MMS_VSC_CMD_REFER) {
+ info->reference[j * RX_NUM + i] =
+ raw_data >> 3;
+ dev_dbg(&info->client->dev, "[TSP] reference[%d][%d] = %d\n",
+ i, j, info->reference[j * RX_NUM + i]);
+ }
+ }
+
+ }
+
+ snprintf(buff, sizeof(buff), "%d,%d", min_value, max_value);
+ set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
+
+ enable_irq(info->irq);
+
+err_i2c:
+ dev_err(&info->client->dev, "%s: fail to i2c (cmd=%d)\n",
+ __func__, cmd);
+}
+
+static u32 get_raw_data_one(struct mms_ts_info *info, u16 rx_idx, u16 tx_idx,
+ u8 cmd)
+{
+ u8 w_buf[6];
+ u8 read_buffer[2];
+ int ret;
+ u32 raw_data;
+
+ w_buf[0] = MMS_VSC_CMD; /* vendor specific command id */
+ w_buf[1] = MMS_VSC_MODE; /* mode of vendor */
+ w_buf[2] = 0; /* tx line */
+ w_buf[3] = 0; /* rx line */
+ w_buf[4] = 0; /* reserved */
+ w_buf[5] = 0; /* sub command */
+
+ if (cmd != MMS_VSC_CMD_INTENSITY && cmd != MMS_VSC_CMD_RAW &&
+ cmd != MMS_VSC_CMD_REFER) {
+ dev_err(&info->client->dev, "%s: not profer command(cmd=%d)\n",
+ __func__, cmd);
+ return -1;
+ }
+
+ w_buf[2] = tx_idx; /* tx */
+ w_buf[3] = rx_idx; /* rx */
+ w_buf[5] = cmd; /* sub command */
+
+ ret = i2c_smbus_write_i2c_block_data(info->client, w_buf[0], 5,
+ &w_buf[1]);
+ if (ret < 0)
+ goto err_i2c;
+
+ ret = i2c_smbus_read_i2c_block_data(info->client, 0xBF, 2, read_buffer);
+ if (ret < 0)
+ goto err_i2c;
+
+ raw_data = ((u16) read_buffer[1] << 8) | read_buffer[0];
+ if (cmd == MMS_VSC_CMD_REFER)
+ raw_data = raw_data >> 4;
+
+ return raw_data;
+
+err_i2c:
+ dev_err(&info->client->dev, "%s: fail to i2c (cmd=%d)\n",
+ __func__, cmd);
+ return -1;
+}
+
+static ssize_t show_close_tsp_test(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mms_ts_info *info = dev_get_drvdata(dev);
+
+ get_raw_data_all(info, MMS_VSC_CMD_EXIT);
+ info->ft_flag = 0;
+
+ return snprintf(buf, TSP_BUF_SIZE, "%u\n", 0);
+}
+
+static void set_default_result(struct mms_ts_info *info)
+{
+ char delim = ':';
+
+ memset(info->cmd_result, 0x00, ARRAY_SIZE(info->cmd_result));
+ memcpy(info->cmd_result, info->cmd, strlen(info->cmd));
+ strncat(info->cmd_result, &delim, 1);
+}
+
+static int check_rx_tx_num(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ char buff[TSP_CMD_STR_LEN] = {0};
+ int node;
+
+ if (info->cmd_param[0] < 0 ||
+ info->cmd_param[0] >= TX_NUM ||
+ info->cmd_param[1] < 0 ||
+ info->cmd_param[1] >= RX_NUM) {
+ snprintf(buff, sizeof(buff) , "%s", "NG");
+ set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
+ info->cmd_state = 3;
+
+ dev_info(&info->client->dev, "%s: parameter error: %u,%u\n",
+ __func__, info->cmd_param[0],
+ info->cmd_param[1]);
+ node = -1;
+ return node;
+}
+ node = info->cmd_param[1] * RX_NUM + info->cmd_param[0];
+ dev_info(&info->client->dev, "%s: node = %d\n", __func__,
+ node);
+ return node;
+
+}
+
+static void not_support_cmd(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+ char buff[16] = {0};
+
+ set_default_result(info);
+ sprintf(buff, "%s", "NA");
+ set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
+ info->cmd_state = 4;
+ dev_info(&info->client->dev, "%s: \"%s(%d)\"\n", __func__,
+ buff, strnlen(buff, sizeof(buff)));
+ return;
+}
+
+static void fw_update(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+ struct i2c_client *client = info->client;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret = 0;
+ int ver = 0;
+ int retries = 5;
+
+ set_default_result(info);
+
+ dev_info(&client->dev,
+ "fw_ic_ver = 0x%02x, fw_bin_ver = 0x%02x\n",
+ info->fw_ic_ver, FW_VERSION);
+
+ goto not_support; /* GC Bring up- Not support yet */
+
+ switch (info->cmd_param[0]) {
+ case BUILT_IN:
+ info->cmd_param[0] = REQ_FW;
+ dev_info(&client->dev, "BUILT_IN=>REQ_FW update mode!\n");
+ break;
+
+ case UMS:
+ dev_info(&client->dev, "UMS update mode!\n");
+ break;
+
+ case REQ_FW:
+ dev_info(&client->dev, "REQ_FW update mode!\n");
+ break;
+
+ default:
+ dev_err(&client->dev, "invalid cmd_param[%d]\n",
+ info->cmd_param[0]);
+ goto not_support;
+ }
+
+ disable_irq(info->irq);
+ while (retries--) {
+
+#if ISC_DL_MODE
+ ret = mms100_ISC_download_mbinary(client, info->cmd_param[0],
+ FORCED_UPDATE);
+#endif
+ if (ret) {
+ dev_err(&client->dev, "retrying flashing\n");
+ continue;
+ }
+
+ ver = get_fw_version(info);
+ info->fw_ic_ver = ver;
+
+ dev_info(&client->dev,
+ "fw update done. ver = 0x%02x\n", ver);
+ info->cmd_state = OK;
+ enable_irq(info->irq);
+ return;
+ }
+
+not_support:
+ return;
+}
+
+static void get_fw_ver_bin(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ char buff[16] = {0};
+
+ set_default_result(info);
+ snprintf(buff, sizeof(buff), "%#02x", FW_VERSION);
+
+ set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
+ info->cmd_state = OK;
+ dev_info(&info->client->dev, "%s: %s(%d)\n", __func__,
+ buff, strnlen(buff, sizeof(buff)));
+}
+
+static void get_fw_ver_ic(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ char buff[16] = {0};
+ int ver;
+
+ set_default_result(info);
+
+ ver = info->fw_ic_ver;
+ snprintf(buff, sizeof(buff), "%#02x", ver);
+
+ set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
+ info->cmd_state = OK;
+ dev_info(&info->client->dev, "%s: %s(%d)\n", __func__,
+ buff, strnlen(buff, sizeof(buff)));
+}
+
+static void get_config_ver(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ char buff[20] = {0};
+
+ set_default_result(info);
+
+ snprintf(buff, sizeof(buff), "%s", info->config_fw_version);
+ set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
+ info->cmd_state = 2;
+ dev_info(&info->client->dev, "%s: %s(%d)\n", __func__,
+ buff, strnlen(buff, sizeof(buff)));
+}
+
+static void get_threshold(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ char buff[16] = {0};
+ int threshold;
+
+ set_default_result(info);
+
+ threshold = i2c_smbus_read_byte_data(info->client, 0x05);
+ if (threshold < 0) {
+ snprintf(buff, sizeof(buff), "%s", "NG");
+ set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
+ info->cmd_state = FAIL;
+ return;
+}
+ snprintf(buff, sizeof(buff), "%d", threshold);
+
+ set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
+ info->cmd_state = OK;
+ dev_info(&info->client->dev, "%s: %s(%d)\n", __func__,
+ buff, strnlen(buff, sizeof(buff)));
+}
+
+static void module_off_master(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ char buff[3] = {0};
+
+ mutex_lock(&info->lock);
+ if (info->enabled) {
+ disable_irq(info->irq);
+ info->enabled = false;
+ }
+ mutex_unlock(&info->lock);
+
+ if (info->pdata->power(0) == SUCCESS_PWR_CONTROL)
+ snprintf(buff, sizeof(buff), "%s", "OK");
+ else
+ snprintf(buff, sizeof(buff), "%s", "NG");
+
+ set_default_result(info);
+ set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
+
+ if (strncmp(buff, "OK", 2) == 0)
+ info->cmd_state = OK;
+ else
+ info->cmd_state = FAIL;
+
+ dev_info(&info->client->dev, "%s: %s\n", __func__, buff);
+}
+
+static void module_on_master(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ char buff[3] = {0};
+
+
+ mutex_lock(&info->lock);
+ if (!info->enabled) {
+ enable_irq(info->irq);
+ info->enabled = true;
+ }
+ mutex_unlock(&info->lock);
+
+ if (info->pdata->power(1) == SUCCESS_PWR_CONTROL)
+ snprintf(buff, sizeof(buff), "%s", "OK");
+ else
+ snprintf(buff, sizeof(buff), "%s", "NG");
+
+ set_default_result(info);
+ set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
+
+ if (strncmp(buff, "OK", 2) == 0)
+ info->cmd_state = OK;
+ else
+ info->cmd_state = FAIL;
+
+ dev_info(&info->client->dev, "%s: %s\n", __func__, buff);
+
+}
+
+static void module_off_slave(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ not_support_cmd(info);
+}
+
+static void module_on_slave(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ not_support_cmd(info);
+}
+
+static void get_chip_vendor(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ char buff[16] = {0};
+
+ set_default_result(info);
+
+ snprintf(buff, sizeof(buff), "%s", TSP_VENDOR);
+ set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
+ info->cmd_state = OK;
+ dev_info(&info->client->dev, "%s: %s(%d)\n", __func__,
+ buff, strnlen(buff, sizeof(buff)));
+}
+
+static void get_chip_name(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ char buff[16] = {0};
+
+ set_default_result(info);
+
+ snprintf(buff, sizeof(buff), "%s", TSP_IC);
+ set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
+ info->cmd_state = OK;
+ dev_info(&info->client->dev, "%s: %s(%d)\n", __func__,
+ buff, strnlen(buff, sizeof(buff)));
+}
+
+static void get_reference(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ char buff[16] = {0};
+ unsigned int val;
+ int node;
+
+ set_default_result(info);
+ node = check_rx_tx_num(info);
+
+ if (node < 0)
+ return;
+
+ val = info->reference[node];
+ snprintf(buff, sizeof(buff), "%u", val);
+ set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
+
+ info->cmd_state = OK;
+
+ dev_info(&info->client->dev, "%s: %s(%d)\n", __func__,
+ buff, strnlen(buff, sizeof(buff)));
+
+}
+
+static void get_cm_abs(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ char buff[16] = {0};
+ unsigned int val;
+ int node;
+
+ set_default_result(info);
+ node = check_rx_tx_num(info);
+
+ if (node < 0)
+ return;
+
+ val = info->raw[node];
+ snprintf(buff, sizeof(buff), "%u", val);
+ set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
+ info->cmd_state = OK;
+
+ dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, buff,
+ strnlen(buff, sizeof(buff)));
+}
+
+static void get_cm_delta(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ char buff[16] = {0};
+ unsigned int val;
+ int node;
+
+ set_default_result(info);
+ node = check_rx_tx_num(info);
+
+ if (node < 0)
+ return;
+
+ val = info->inspection[node];
+ snprintf(buff, sizeof(buff), "%u", val);
+ set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
+ info->cmd_state = OK;
+
+ dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, buff,
+ strnlen(buff, sizeof(buff)));
+}
+
+static void get_intensity(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ char buff[16] = {0};
+ unsigned int val;
+ int node;
+
+ set_default_result(info);
+ node = check_rx_tx_num(info);
+
+ if (node < 0)
+ return;
+
+ val = info->intensity[node];
+
+ snprintf(buff, sizeof(buff), "%u", val);
+ set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
+ info->cmd_state = OK;
+
+ dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, buff,
+ strnlen(buff, sizeof(buff)));
+}
+
+static void get_x_num(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ char buff[16] = {0};
+ int val;
+
+ set_default_result(info);
+
+ snprintf(buff, sizeof(buff), "%d", TX_NUM); /* TX_NUM(0xEF) */
+ set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
+ info->cmd_state = OK;
+
+ dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, buff,
+ strnlen(buff, sizeof(buff)));
+}
+
+static void get_y_num(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+ char buff[16] = {0};
+ int val;
+
+ set_default_result(info);
+
+ snprintf(buff, sizeof(buff), "%d", RX_NUM); /* RX_NUM(0xEE) */
+ set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
+ info->cmd_state = OK;
+
+ dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, buff,
+ strnlen(buff, sizeof(buff)));
+}
+
+static void run_reference_read(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ set_default_result(info);
+ get_raw_data_all(info, MMS_VSC_CMD_REFER);
+ info->cmd_state = OK;
+
+/* dev_info(&info->client->dev, "%s: %s(%d)\n", __func__); */
+}
+
+static void run_cm_abs_read(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ set_default_result(info);
+ get_raw_data_all(info, MMS_VSC_CMD_CM_ABS);
+ get_raw_data_all(info, MMS_VSC_CMD_EXIT);
+ info->cmd_state = OK;
+
+/* dev_info(&info->client->dev, "%s: %s(%d)\n", __func__); */
+}
+
+static void run_cm_delta_read(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ set_default_result(info);
+ get_raw_data_all(info, MMS_VSC_CMD_CM_DELTA);
+ get_raw_data_all(info, MMS_VSC_CMD_EXIT);
+ info->cmd_state = OK;
+
+/* dev_info(&info->client->dev, "%s: %s(%d)\n", __func__); */
+}
+
+static void run_intensity_read(void *device_data)
+{
+ struct mms_ts_info *info = (struct mms_ts_info *)device_data;
+
+ set_default_result(info);
+ get_raw_data_all(info, MMS_VSC_CMD_INTENSITY);
+ info->cmd_state = OK;
+
+/* dev_info(&info->client->dev, "%s: %s(%d)\n", __func__); */
+}
+
+static ssize_t store_cmd(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ struct mms_ts_info *info = dev_get_drvdata(dev);
+ struct i2c_client *client = info->client;
+
+ char *cur, *start, *end;
+ char buff[TSP_CMD_STR_LEN] = {0};
+ int len, i;
+ struct tsp_cmd *tsp_cmd_ptr = NULL;
+ char delim = ',';
+ bool cmd_found = false;
+ int param_cnt = 0;
+ int ret;
+
+ if (info->cmd_is_running == true) {
+ dev_err(&info->client->dev, "tsp_cmd: other cmd is running.\n");
+ goto err_out;
+ }
+
+
+ /* check lock */
+ mutex_lock(&info->cmd_lock);
+ info->cmd_is_running = true;
+ mutex_unlock(&info->cmd_lock);
+
+ info->cmd_state = RUNNING;
+
+ for (i = 0; i < ARRAY_SIZE(info->cmd_param); i++)
+ info->cmd_param[i] = 0;
+
+ len = (int)count;
+ if (*(buf + len - 1) == '\n')
+ len--;
+ memset(info->cmd, 0x00, ARRAY_SIZE(info->cmd));
+ memcpy(info->cmd, buf, len);
+
+ cur = strchr(buf, (int)delim);
+ if (cur)
+ memcpy(buff, buf, cur - buf);
+ else
+ memcpy(buff, buf, len);
+
+ /* find command */
+ list_for_each_entry(tsp_cmd_ptr, &info->cmd_list_head, list) {
+ if (!strcmp(buff, tsp_cmd_ptr->cmd_name)) {
+ cmd_found = true;
+ break;
+ }
+ }
+
+ /* set not_support_cmd */
+ if (!cmd_found) {
+ list_for_each_entry(tsp_cmd_ptr, &info->cmd_list_head, list) {
+ if (!strcmp("not_support_cmd", tsp_cmd_ptr->cmd_name))
+ break;
+ }
+ }
+
+ /* parsing parameters */
+ if (cur && cmd_found) {
+ cur++;
+ start = cur;
+ memset(buff, 0x00, ARRAY_SIZE(buff));
+ do {
+ if (*cur == delim || cur - buf == len) {
+ end = cur;
+ memcpy(buff, start, end - start);
+ *(buff + strlen(buff)) = '\0';
+ ret = kstrtoint(buff, 10,\
+ info->cmd_param + param_cnt);
+ start = cur + 1;
+ memset(buff, 0x00, ARRAY_SIZE(buff));
+ param_cnt++;
+ }
+ cur++;
+ } while (cur - buf <= len);
+ }
+
+ dev_info(&client->dev, "cmd = %s\n", tsp_cmd_ptr->cmd_name);
+ for (i = 0; i < param_cnt; i++)
+ dev_info(&client->dev, "cmd param %d= %d\n", i,
+ info->cmd_param[i]);
+
+ tsp_cmd_ptr->cmd_func(info);
+
+
+err_out:
+ return count;
+}
+
+static ssize_t show_cmd_status(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct mms_ts_info *info = dev_get_drvdata(dev);
+ char buff[16] = {0};
+
+ dev_info(&info->client->dev, "tsp cmd: status:%d\n",
+ info->cmd_state);
+
+ if (info->cmd_state == WAITING)
+ snprintf(buff, sizeof(buff), "WAITING");
+
+ else if (info->cmd_state == RUNNING)
+ snprintf(buff, sizeof(buff), "RUNNING");
+
+ else if (info->cmd_state == OK)
+ snprintf(buff, sizeof(buff), "OK");
+
+ else if (info->cmd_state == FAIL)
+ snprintf(buff, sizeof(buff), "FAIL");
+
+ else if (info->cmd_state == NOT_APPLICABLE)
+ snprintf(buff, sizeof(buff), "NOT_APPLICABLE");
+
+ return snprintf(buf, TSP_BUF_SIZE, "%s\n", buff);
+}
+
+static ssize_t show_cmd_result(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct mms_ts_info *info = dev_get_drvdata(dev);
+
+ dev_info(&info->client->dev, "tsp cmd: result: %s\n", info->cmd_result);
+
+ mutex_lock(&info->cmd_lock);
+ info->cmd_is_running = false;
+ mutex_unlock(&info->cmd_lock);
+
+ info->cmd_state = WAITING;
+
+ return snprintf(buf, TSP_BUF_SIZE, "%s\n", info->cmd_result);
+}
+
+#ifdef ESD_DEBUG
+
+static bool intensity_log_flag;
+
+static ssize_t show_intensity_logging_on(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct mms_ts_info *info = dev_get_drvdata(dev);
+ struct i2c_client *client = info->client;
+ struct file *fp;
+ char log_data[160] = { 0, };
+ char buff[16] = { 0, };
+ mm_segment_t old_fs;
+ long nwrite;
+ u32 val;
+ int i, y, c;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+#define MELFAS_DEBUG_LOG_PATH "/sdcard/melfas_log"
+
+ dev_info(&client->dev, "%s: start.\n", __func__);
+ fp = filp_open(MELFAS_DEBUG_LOG_PATH, O_RDWR | O_CREAT,
+ S_IRWXU | S_IRWXG | S_IRWXO);
+ if (IS_ERR(fp)) {
+ dev_err(&client->dev, "%s: fail to open log file\n", __func__);
+ set_fs(old_fs);
+ return -1;
+ }
+
+ intensity_log_flag = 1;
+ do {
+ for (y = 0; y < 3; y++) {
+ /* for tx chanel 0~2 */
+ memset(log_data, 0x00, 160);
+
+ snprintf(buff, 16, "%1u: ", y);
+ strncat(log_data, buff, strnlen(buff, 16));
+
+ for (i = 0; i < RX_NUM; i++) {
+ val = get_raw_data_one(info, i, y,
+ MMS_VSC_CMD_INTENSITY);
+ snprintf(buff, 16, "%5u, ", val);
+ strncat(log_data, buff, strnlen(buff, 16));
+ }
+ memset(buff, '\n', 2);
+ c = (y == 2) ? 2 : 1;
+ strncat(log_data, buff, c);
+ nwrite = vfs_write(fp, (const char __user *)log_data,
+ strnlen(log_data, 160), &fp->f_pos);
+ }
+ usleep_range(5000);
+ } while (intensity_log_flag);
+
+ filp_close(fp, current->files);
+ set_fs(old_fs);
+
+ return 0;
+}
+
+static ssize_t show_intensity_logging_off(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct mms_ts_info *info = dev_get_drvdata(dev);
+ intensity_log_flag = 0;
+ usleep_range(10000);
+ get_raw_data_all(info, MMS_VSC_CMD_EXIT);
+ return 0;
+}
+
+#endif
+
+static DEVICE_ATTR(close_tsp_test, S_IRUGO, show_close_tsp_test, NULL);
+static DEVICE_ATTR(cmd, S_IWUSR | S_IWGRP, NULL, store_cmd);
+static DEVICE_ATTR(cmd_status, S_IRUGO, show_cmd_status, NULL);
+static DEVICE_ATTR(cmd_result, S_IRUGO, show_cmd_result, NULL);
+#ifdef ESD_DEBUG
+static DEVICE_ATTR(intensity_logging_on, S_IRUGO, show_intensity_logging_on,
+ NULL);
+static DEVICE_ATTR(intensity_logging_off, S_IRUGO, show_intensity_logging_off,
+ NULL);
+#endif
+
+static struct attribute *sec_touch_facotry_attributes[] = {
+ &dev_attr_close_tsp_test.attr,
+ &dev_attr_cmd.attr,
+ &dev_attr_cmd_status.attr,
+ &dev_attr_cmd_result.attr,
+#ifdef ESD_DEBUG
+ &dev_attr_intensity_logging_on.attr,
+ &dev_attr_intensity_logging_off.attr,
+#endif
+ NULL,
+};
+
+static struct attribute_group sec_touch_factory_attr_group = {
+ .attrs = sec_touch_facotry_attributes,
+};
+#endif /* SEC_TSP_FACTORY_TEST */
+
+static int __devinit mms_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct mms_ts_info *info;
+ struct input_dev *input_dev;
+ int ret = 0;
+ char buf[4] = { 0, };
+ int i;
+
+#ifdef SEC_TSP_FACTORY_TEST
+ struct device *fac_dev_ts;
+#endif
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
+ return -EIO;
+
+ info = kzalloc(sizeof(struct mms_ts_info), GFP_KERNEL);
+ if (!info) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(&client->dev, "Failed to allocate memory for input device\n");
+ ret = -ENOMEM;
+ goto err_input_alloc;
+ }
+
+ info->client = client;
+ info->input_dev = input_dev;
+ info->pdata = client->dev.platform_data;
+ if (NULL == info->pdata) {
+ dev_err(&client->dev, "failed to get platform data\n");
+ goto err_config;
+ }
+ info->irq = -1;
+ mutex_init(&info->lock);
+
+ if (info->pdata) {
+ info->max_x = info->pdata->max_x;
+ info->max_y = info->pdata->max_y;
+ info->invert_x = info->pdata->invert_x;
+ info->invert_y = info->pdata->invert_y;
+ info->config_fw_version = info->pdata->config_fw_version;
+ info->input_event = info->pdata->input_event;
+ info->register_cb = info->pdata->register_cb;
+ } else {
+ info->max_x = 720;
+ info->max_y = 1280;
+ }
+ for (i = 0 ; i < MAX_FINGERS; i++)
+ info->finger_state[i] = TSP_STATE_RELEASE;
+
+ info->callbacks.inform_charger = melfas_ta_cb;
+ if (info->register_cb)
+ info->register_cb(&info->callbacks);
+
+ info->pdata->power(true);
+ msleep(100);
+
+ i2c_set_clientdata(client, info);
+ ret = i2c_master_recv(client, buf, 1);
+ if (ret < 0) { /* tsp connect check */
+ dev_err(&client->dev, "%s: tsp connect err [%d], Add[%d]\n",
+ __func__, ret, info->client->addr);
+ goto err_config;
+ }
+
+ ret = mms_ts_fw_info(info);
+/* ret = mms_ts_fw_load(info); */
+
+ if (ret) {
+ dev_err(&client->dev, "failed to initialize (%d)\n", ret);
+ goto err_config;
+ }
+
+ info->enabled = true;
+
+ snprintf(info->phys, sizeof(info->phys),
+ "%s/input0", dev_name(&client->dev));
+ input_dev->name = "sec_touchscreen"; /*= "Melfas MMSxxx Touchscreen";*/
+ input_dev->phys = info->phys;
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+ input_mt_init_slots(input_dev, MAX_FINGERS);
+ input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, MAX_WIDTH, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, (info->max_x)-1, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, (info->max_y)-1, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, MAX_PRESSURE, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
+ 0, MAX_PRESSURE, 0, 0);
+ input_set_drvdata(input_dev, info);
+
+ ret = input_register_device(input_dev);
+ if (ret) {
+ dev_err(&client->dev, "failed to register input dev (%d)\n",
+ ret);
+ goto err_reg_input_dev;
+ }
+
+#if TOUCH_BOOSTER
+ mutex_init(&info->dvfs_lock);
+ INIT_DELAYED_WORK(&info->work_dvfs_off, set_dvfs_off);
+ INIT_DELAYED_WORK(&info->work_dvfs_chg, change_dvfs_lock);
+ info->bus_dev = dev_get("exynos-busfreq");
+ info->sec_touchscreen = dev_get("sec_touchscreen");
+ info->cpufreq_level = -1;
+ info->dvfs_lock_status = false;
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ info->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ info->early_suspend.suspend = mms_ts_early_suspend;
+ info->early_suspend.resume = mms_ts_late_resume;
+ register_early_suspend(&info->early_suspend);
+#endif
+
+#ifdef SEC_TSP_FACTORY_TEST
+ INIT_LIST_HEAD(&info->cmd_list_head);
+ for (i = 0; i < ARRAY_SIZE(tsp_cmds); i++)
+ list_add_tail(&tsp_cmds[i].list, &info->cmd_list_head);
+
+ mutex_init(&info->cmd_lock);
+ info->cmd_is_running = false;
+
+ fac_dev_ts = device_create(sec_class,
+ NULL, 0, info, "tsp");
+ if (IS_ERR(fac_dev_ts))
+ dev_err(&client->dev, "Failed to create device for the sysfs\n");
+
+ ret = sysfs_create_group(&fac_dev_ts->kobj,
+ &sec_touch_factory_attr_group);
+ if (ret)
+ dev_err(&client->dev, "Failed to create sysfs group\n");
+#endif
+ return 0;
+
+err_reg_input_dev:
+ input_unregister_device(input_dev);
+ input_free_device(input_dev);
+err_config:
+/* input_unregister_device(input_dev);
+ input_dev = NULL;
+*/
+err_input_alloc:
+ kfree(info);
+err_alloc:
+ return ret;
+
+}
+
+static int __devexit mms_ts_remove(struct i2c_client *client)
+{
+ struct mms_ts_info *info = i2c_get_clientdata(client);
+
+ unregister_early_suspend(&info->early_suspend);
+ if (info->irq >= 0)
+ free_irq(info->irq, info);
+ input_unregister_device(info->input_dev);
+ kfree(info);
+
+ return 0;
+}
+
+#if defined(CONFIG_PM) || defined(CONFIG_HAS_EARLYSUSPEND)
+static int mms_ts_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mms_ts_info *info = i2c_get_clientdata(client);
+
+ if (!info->enabled)
+ return 0;
+
+ dev_notice(&info->client->dev, "%s\n", __func__);
+
+ disable_irq(info->irq);
+ info->enabled = false;
+ release_all_fingers(info);
+ info->pdata->power(0);
+ return 0;
+}
+
+static int mms_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mms_ts_info *info = i2c_get_clientdata(client);
+
+ if (info->enabled)
+ return 0;
+
+ dev_notice(&info->client->dev, "%s\n", __func__);
+ info->pdata->power(1);
+ msleep(120);
+ /* GC Not support yet!
+ mms_set_noise_mode(info);
+ */
+
+ /* Because irq_type by EXT_INTxCON register is changed to low_level
+ * after wakeup, irq_type set to falling edge interrupt again.
+ */
+ /* irq_set_irq_type(client->irq, IRQ_TYPE_EDGE_FALLING); */
+
+ enable_irq(info->irq);
+ info->enabled = true;
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void mms_ts_early_suspend(struct early_suspend *h)
+{
+ struct mms_ts_info *info;
+ info = container_of(h, struct mms_ts_info, early_suspend);
+ mms_ts_suspend(&info->client->dev);
+
+}
+
+static void mms_ts_late_resume(struct early_suspend *h)
+{
+ struct mms_ts_info *info;
+ info = container_of(h, struct mms_ts_info, early_suspend);
+ mms_ts_resume(&info->client->dev);
+}
+#endif
+
+#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND)
+static const struct dev_pm_ops mms_ts_pm_ops = {
+ .suspend = mms_ts_suspend,
+ .resume = mms_ts_resume,
+};
+#endif
+
+static const struct i2c_device_id mms_ts_id[] = {
+ {MELFAS_TS_NAME, 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, mms_ts_id);
+
+static struct i2c_driver mms_ts_driver = {
+ .probe = mms_ts_probe,
+ .remove = __devexit_p(mms_ts_remove),
+ .driver = {
+ .name = MELFAS_TS_NAME,
+#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND)
+ .pm = &mms_ts_pm_ops,
+#endif
+ },
+ .id_table = mms_ts_id,
+};
+
+static int __init mms_ts_init(void)
+{
+
+ return i2c_add_driver(&mms_ts_driver);
+
+}
+
+static void __exit mms_ts_exit(void)
+{
+ i2c_del_driver(&mms_ts_driver);
+}
+
+module_init(mms_ts_init);
+module_exit(mms_ts_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("Touchscreen driver for Melfas MMS-series controllers");
+MODULE_LICENSE("GPL");