From af2bdde38f91b4edefbfba62f377dd3d602a066e Mon Sep 17 00:00:00 2001 From: Konstantin Makariev Date: Thu, 9 May 2019 08:15:48 -0500 Subject: [PATCH] sec_mmi: initial kernel driver Kernel driver code for Samsung LSI. Includes MMI specific interfaces Change-Id: I0b3ae080637f8f103d8d2928240841909ec6c258 Signed-off-by: Konstantin Makariev Reviewed-on: https://gerrit.mot.com/1351484 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Konstantin Makariev Submit-Approved: Jira Key --- drivers/input/touchscreen/sec_mmi/Android.mk | 8 + drivers/input/touchscreen/sec_mmi/Kbuild | 7 + drivers/input/touchscreen/sec_mmi/Kconfig | 22 + drivers/input/touchscreen/sec_mmi/Makefile | 10 + drivers/input/touchscreen/sec_mmi/sec_mmi.h | 25 + drivers/input/touchscreen/sec_mmi/sec_ts.c | 2441 +++++++++++ drivers/input/touchscreen/sec_mmi/sec_ts.h | 822 ++++ drivers/input/touchscreen/sec_mmi/sec_ts_fn.c | 3829 +++++++++++++++++ drivers/input/touchscreen/sec_mmi/sec_ts_fw.c | 1028 +++++ .../input/touchscreen/sec_mmi/sec_ts_mmi.c | 419 ++ .../touchscreen/sec_mmi/sec_ts_only_vendor.c | 295 ++ 11 files changed, 8906 insertions(+) create mode 100644 drivers/input/touchscreen/sec_mmi/Android.mk create mode 100644 drivers/input/touchscreen/sec_mmi/Kbuild create mode 100644 drivers/input/touchscreen/sec_mmi/Kconfig create mode 100644 drivers/input/touchscreen/sec_mmi/Makefile create mode 100644 drivers/input/touchscreen/sec_mmi/sec_mmi.h create mode 100644 drivers/input/touchscreen/sec_mmi/sec_ts.c create mode 100644 drivers/input/touchscreen/sec_mmi/sec_ts.h create mode 100644 drivers/input/touchscreen/sec_mmi/sec_ts_fn.c create mode 100644 drivers/input/touchscreen/sec_mmi/sec_ts_fw.c create mode 100644 drivers/input/touchscreen/sec_mmi/sec_ts_mmi.c create mode 100644 drivers/input/touchscreen/sec_mmi/sec_ts_only_vendor.c diff --git a/drivers/input/touchscreen/sec_mmi/Android.mk b/drivers/input/touchscreen/sec_mmi/Android.mk new file mode 100644 index 000000000000..8cf8b3414610 --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := sec_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk \ No newline at end of file diff --git a/drivers/input/touchscreen/sec_mmi/Kbuild b/drivers/input/touchscreen/sec_mmi/Kbuild new file mode 100644 index 000000000000..b74db7df16af --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/Kbuild @@ -0,0 +1,7 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/sec_mmi + +obj-m := sec_mmi.o +sec_mmi-objs = sec_ts.o sec_ts_fn.o sec_ts_fw.o sec_ts_only_vendor.o sec_ts_mmi.o \ No newline at end of file diff --git a/drivers/input/touchscreen/sec_mmi/Kconfig b/drivers/input/touchscreen/sec_mmi/Kconfig new file mode 100644 index 000000000000..e2598b2e10c9 --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/Kconfig @@ -0,0 +1,22 @@ +# +# Samsung Electronics TOUCH driver configuration +# + +config TOUCHSCREEN_SEC_TS + tristate "Samsung Electronics Touchscreen" + depends on I2C + help + Say Y here if you want support for SEC touchscreen controllers. + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sec_ts. + +config TOUCHSCREEN_SEC_TS_GLOVEMODE + tristate "Samsung Electronics Touchscreen" + depends on I2C + help + Say Y here if you have a Samsung Electronics Touchscreen and want to enable + support for the built-in touchscreen. + + If unsure, say N. diff --git a/drivers/input/touchscreen/sec_mmi/Makefile b/drivers/input/touchscreen/sec_mmi/Makefile new file mode 100644 index 000000000000..34b09dd815a7 --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean \ No newline at end of file diff --git a/drivers/input/touchscreen/sec_mmi/sec_mmi.h b/drivers/input/touchscreen/sec_mmi/sec_mmi.h new file mode 100644 index 000000000000..cc94c6110851 --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/sec_mmi.h @@ -0,0 +1,25 @@ +#ifndef _SEC_MMI_H_ +#define _SEC_MMI_H_ + +#include +#include +#include +#include + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +struct sec_mmi_data { + struct i2c_client *i2c_client; + struct device *ts_class_dev; + struct notifier_block panel_nb; + struct work_struct resume_work; + + int ctrl_dsi; + + char *image_name; + const char *class_entry_name; + const char *bound_display; +}; + +#endif diff --git a/drivers/input/touchscreen/sec_mmi/sec_ts.c b/drivers/input/touchscreen/sec_mmi/sec_ts.c new file mode 100644 index 000000000000..bd538b7f025b --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/sec_ts.c @@ -0,0 +1,2441 @@ +/* drivers/input/touchscreen/sec_ts.c + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * http://www.samsungsemi.com/ + * + * Core file for Samsung TSC driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +struct sec_ts_data *tsp_info; + +#include "sec_ts.h" + +struct sec_ts_data *ts_dup; + +#ifdef USE_POWER_RESET_WORK +static void sec_ts_reset_work(struct work_struct *work); +#endif +static void sec_ts_read_info_work(struct work_struct *work); + +#ifdef USE_OPEN_CLOSE +static int sec_ts_input_open(struct input_dev *dev); +static void sec_ts_input_close(struct input_dev *dev); +#endif + +int sec_ts_read_information(struct sec_ts_data *ts); + + +int sec_ts_i2c_write(struct sec_ts_data *ts, u8 reg, u8 *data, int len) +{ + u8 buf[I2C_WRITE_BUFFER_SIZE + 1]; + int ret; + unsigned char retry; + struct i2c_msg msg; + int i; + + if (len > I2C_WRITE_BUFFER_SIZE) { + input_err(true, &ts->client->dev, "%s: len is larger than buffer size\n", __func__); + return -EINVAL; + } + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: POWER_STATUS : OFF\n", __func__); + goto err; + } + + buf[0] = reg; + memcpy(buf + 1, data, len); + + msg.addr = ts->client->addr; + msg.flags = 0; + msg.len = len + 1; + msg.buf = buf; + + mutex_lock(&ts->i2c_mutex); + for (retry = 0; retry < SEC_TS_I2C_RETRY_CNT; retry++) { + ret = i2c_transfer(ts->client->adapter, &msg, 1); + if (ret == 1) + break; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: POWER_STATUS : OFF, retry:%d\n", __func__, retry); + mutex_unlock(&ts->i2c_mutex); + goto err; + } + + usleep_range(1 * 1000, 1 * 1000); + + if (retry > 1) { + input_err(true, &ts->client->dev, "%s: I2C retry %d, ret:%d\n", __func__, retry + 1, ret); + ts->comm_err_count++; + } + } + + mutex_unlock(&ts->i2c_mutex); + + if (retry == SEC_TS_I2C_RETRY_CNT) { + input_err(true, &ts->client->dev, "%s: I2C write over retry limit\n", __func__); + ret = -EIO; +#ifdef USE_POR_AFTER_I2C_RETRY + if (ts->probe_done && !ts->reset_is_on_going) + schedule_delayed_work(&ts->reset_work, msecs_to_jiffies(TOUCH_RESET_DWORK_TIME)); +#endif + } + + if (ts->debug_flag & SEC_TS_DEBUG_PRINT_I2C_WRITE_CMD) { + pr_info("sec_input:i2c_cmd: W: %02X | ", reg); + for (i = 0; i < len; i++) + pr_cont("%02X ", data[i]); + pr_cont("\n"); + } + + if (ret == 1) + return 0; +err: + return -EIO; +} + +int sec_ts_i2c_read(struct sec_ts_data *ts, u8 reg, u8 *data, int len) +{ + u8 buf[4]; + int ret; + unsigned char retry; + struct i2c_msg msg[2]; + int remain = len; + int i; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: POWER_STATUS : OFF\n", __func__); + goto err; + } + + buf[0] = reg; + + msg[0].addr = ts->client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = buf; + + msg[1].addr = ts->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = len; + msg[1].buf = data; + + mutex_lock(&ts->i2c_mutex); + + if (len <= ts->i2c_burstmax) { + + for (retry = 0; retry < SEC_TS_I2C_RETRY_CNT; retry++) { + ret = i2c_transfer(ts->client->adapter, msg, 2); + if (ret == 2) + break; + usleep_range(1 * 1000, 1 * 1000); + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: POWER_STATUS : OFF, retry:%d\n", __func__, retry); + mutex_unlock(&ts->i2c_mutex); + goto err; + } + + if (retry > 1) { + input_err(true, &ts->client->dev, "%s: I2C retry %d, ret:%d\n", + __func__, retry + 1, ret); + ts->comm_err_count++; + } + } + + } else { + /* + * I2C read buffer is 256 byte. do not support long buffer over than 256. + * So, try to seperate reading data about 256 bytes. + */ + + for (retry = 0; retry < SEC_TS_I2C_RETRY_CNT; retry++) { + ret = i2c_transfer(ts->client->adapter, msg, 1); + if (ret == 1) + break; + usleep_range(1 * 1000, 1 * 1000); + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: POWER_STATUS : OFF, retry:%d\n", __func__, retry); + mutex_unlock(&ts->i2c_mutex); + goto err; + } + + if (retry > 1) { + input_err(true, &ts->client->dev, "%s: I2C retry %d, ret:%d\n", + __func__, retry + 1, ret); + ts->comm_err_count++; + } + } + + do { + if (remain > ts->i2c_burstmax) + msg[1].len = ts->i2c_burstmax; + else + msg[1].len = remain; + + remain -= ts->i2c_burstmax; + + for (retry = 0; retry < SEC_TS_I2C_RETRY_CNT; retry++) { + ret = i2c_transfer(ts->client->adapter, &msg[1], 1); + if (ret == 1) + break; + usleep_range(1 * 1000, 1 * 1000); + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: POWER_STATUS : OFF, retry:%d\n", __func__, retry); + mutex_unlock(&ts->i2c_mutex); + goto err; + } + + if (retry > 1) { + input_err(true, &ts->client->dev, "%s: I2C retry %d, ret:%d\n", + __func__, retry + 1, ret); + ts->comm_err_count++; + } + } + + msg[1].buf += msg[1].len; + + } while (remain > 0); + + } + + mutex_unlock(&ts->i2c_mutex); + + if (retry == SEC_TS_I2C_RETRY_CNT) { + input_err(true, &ts->client->dev, "%s: I2C read over retry limit\n", __func__); + ret = -EIO; +#ifdef USE_POR_AFTER_I2C_RETRY + if (ts->probe_done && !ts->reset_is_on_going) + schedule_delayed_work(&ts->reset_work, msecs_to_jiffies(TOUCH_RESET_DWORK_TIME)); +#endif + + } + + if (ts->debug_flag & SEC_TS_DEBUG_PRINT_I2C_READ_CMD) { + pr_info("sec_input:i2c_cmd: R: %02X | ", reg); + for (i = 0; i < len; i++) + pr_cont("%02X ", data[i]); + pr_cont("\n"); + } + + return ret; + +err: + return -EIO; +} + +static int sec_ts_i2c_write_burst(struct sec_ts_data *ts, u8 *data, int len) +{ + int ret; + int retry; + + mutex_lock(&ts->i2c_mutex); + for (retry = 0; retry < SEC_TS_I2C_RETRY_CNT; retry++) { + ret = i2c_master_send(ts->client, data, len); + if (ret == len) + break; + + usleep_range(1 * 1000, 1 * 1000); + + if (retry > 1) { + input_err(true, &ts->client->dev, "%s: I2C retry %d, ret:%d\n", __func__, retry + 1, ret); + ts->comm_err_count++; + } + } + + mutex_unlock(&ts->i2c_mutex); + if (retry == SEC_TS_I2C_RETRY_CNT) { + input_err(true, &ts->client->dev, "%s: I2C write over retry limit\n", __func__); + ret = -EIO; + } + + return ret; +} + +static int sec_ts_i2c_read_bulk(struct sec_ts_data *ts, u8 *data, int len) +{ + int ret; + unsigned char retry; + int remain = len; + struct i2c_msg msg; + + msg.addr = ts->client->addr; + msg.flags = I2C_M_RD; + msg.len = len; + msg.buf = data; + + mutex_lock(&ts->i2c_mutex); + + do { + if (remain > ts->i2c_burstmax) + msg.len = ts->i2c_burstmax; + else + msg.len = remain; + + remain -= ts->i2c_burstmax; + + for (retry = 0; retry < SEC_TS_I2C_RETRY_CNT; retry++) { + ret = i2c_transfer(ts->client->adapter, &msg, 1); + if (ret == 1) + break; + usleep_range(1 * 1000, 1 * 1000); + + if (retry > 1) { + input_err(true, &ts->client->dev, "%s: I2C retry %d, ret:%d\n", + __func__, retry + 1, ret); + ts->comm_err_count++; + } + } + + if (retry == SEC_TS_I2C_RETRY_CNT) { + input_err(true, &ts->client->dev, "%s: I2C read over retry limit\n", __func__); + ret = -EIO; + + break; + } + msg.buf += msg.len; + + } while (remain > 0); + + mutex_unlock(&ts->i2c_mutex); + + if (ret == 1) + return 0; + + return -EIO; +} +static int sec_ts_read_from_customlib(struct sec_ts_data *ts, u8 *data, int len) +{ + int ret; + + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_CUSTOMLIB_READ_PARAM, data, 2); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to read custom library command\n", __func__); + + ret = sec_ts_i2c_read(ts, SEC_TS_CMD_CUSTOMLIB_READ_PARAM, (u8 *)data, len); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to read custom library command\n", __func__); + + return ret; +} + +#if defined(CONFIG_TOUCHSCREEN_DUMP_MODE) +#include +extern struct tsp_dump_callbacks dump_callbacks; +static struct delayed_work *p_ghost_check; + +static void sec_ts_check_rawdata(struct work_struct *work) +{ + struct sec_ts_data *ts = container_of(work, struct sec_ts_data, ghost_check.work); + + if (ts->tsp_dump_lock == 1) { + input_err(true, &ts->client->dev, "%s: ignored ## already checking..\n", __func__); + return; + } + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: ignored ## IC is power off\n", __func__); + return; + } + + input_info(true, &ts->client->dev, "%s: start ##\n", __func__); + sec_ts_run_rawdata_all(ts, true); + msleep(100); + + input_info(true, &ts->client->dev, "%s: done ##\n", __func__); +} + +static void dump_tsp_log(void) +{ + pr_info("%s: %s %s: start\n", SEC_TS_I2C_NAME, SECLOG, __func__); + +#ifdef CONFIG_BATTERY_SAMSUNG + if (lpcharge == 1) { + pr_err("%s: %s %s: ignored ## lpm charging Mode!!\n", SEC_TS_I2C_NAME, SECLOG, __func__); + return; + } +#endif + + if (p_ghost_check == NULL) { + pr_err("%s: %s %s: ignored ## tsp probe fail!!\n", SEC_TS_I2C_NAME, SECLOG, __func__); + return; + } + schedule_delayed_work(p_ghost_check, msecs_to_jiffies(100)); +} +#endif + + +void sec_ts_delay(unsigned int ms) +{ + if (ms < 20) + usleep_range(ms * 1000, ms * 1000); + else + msleep(ms); +} + +int sec_ts_wait_for_ready(struct sec_ts_data *ts, unsigned int ack) +{ + int rc = -1; + int retry = 0; + u8 tBuff[SEC_TS_EVENT_BUFF_SIZE] = {0,}; + + while (sec_ts_i2c_read(ts, SEC_TS_READ_ONE_EVENT, tBuff, SEC_TS_EVENT_BUFF_SIZE) > 0) { + if (((tBuff[0] >> 2) & 0xF) == TYPE_STATUS_EVENT_INFO) { + if (tBuff[1] == ack) { + rc = 0; + break; + } + } else if (((tBuff[0] >> 2) & 0xF) == TYPE_STATUS_EVENT_VENDOR_INFO) { + if (tBuff[1] == ack) { + rc = 0; + break; + } + } + + if (retry++ > SEC_TS_WAIT_RETRY_CNT) { + input_err(true, &ts->client->dev, "%s: Time Over\n", __func__); + break; + } + sec_ts_delay(20); + } + + input_info(true, &ts->client->dev, + "%s: %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X [%d]\n", + __func__, tBuff[0], tBuff[1], tBuff[2], tBuff[3], + tBuff[4], tBuff[5], tBuff[6], tBuff[7], retry); + + return rc; +} + +int sec_ts_read_calibration_report(struct sec_ts_data *ts) +{ + int ret; + u8 buf[5] = { 0 }; + + buf[0] = SEC_TS_READ_CALIBRATION_REPORT; + + ret = sec_ts_i2c_read(ts, buf[0], &buf[1], 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to read, %d\n", __func__, ret); + return ret; + } + + input_info(true, &ts->client->dev, "%s: count:%d, pass count:%d, fail count:%d, status:0x%X\n", + __func__, buf[1], buf[2], buf[3], buf[4]); + + return buf[4]; +} + +void sec_ts_reinit(struct sec_ts_data *ts) +{ + u8 w_data[2] = {0x00, 0x00}; + int ret = 0; + + input_info(true, &ts->client->dev, + "%s : charger=0x%x, touch_functions=0x%x, Power mode=0x%x, noise_mode=%d\n", + __func__, ts->charger_mode, ts->touch_functions, + ts->power_status, ts->touch_noise_status); + + ts->touch_noise_status = 0; + + /* charger mode */ + if (ts->charger_mode != SEC_TS_BIT_CHARGER_MODE_NO) { + w_data[0] = ts->charger_mode; + ret = ts->sec_ts_i2c_write(ts, SET_TS_CMD_SET_CHARGER_MODE, (u8 *)&w_data[0], 1); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to send command(0x%x)", + __func__, SET_TS_CMD_SET_CHARGER_MODE); + } + + /* Cover mode */ + if (ts->touch_functions & SEC_TS_BIT_SETFUNC_COVER) { + w_data[0] = ts->cover_cmd; + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_COVERTYPE, (u8 *)&w_data[0], 1); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to send command(0x%x)", + __func__, SEC_TS_CMD_SET_COVERTYPE); + } + + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_TOUCHFUNCTION, (u8 *)&(ts->touch_functions), 2); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to send command(0x%x)", + __func__, SEC_TS_CMD_SET_TOUCHFUNCTION); +#ifdef SEC_TS_SUPPORT_CUSTOMLIB + if (ts->use_customlib) + sec_ts_set_custom_library(ts); +#endif + /* Power mode */ + if (ts->power_status == SEC_TS_STATE_LPM) { + w_data[0] = (ts->lowpower_mode & SEC_TS_MODE_LOWPOWER_FLAG) >> 1; + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_WAKEUP_GESTURE_MODE, (u8 *)&w_data[0], 1); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to send command(0x%x)", + __func__, SEC_TS_CMD_WAKEUP_GESTURE_MODE); + + w_data[0] = TO_LOWPOWER_MODE; + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_POWER_MODE, (u8 *)&w_data[0], 1); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to send command(0x%x)", + __func__, SEC_TS_CMD_SET_POWER_MODE); + + sec_ts_delay(50); + + if (ts->lowpower_mode & SEC_TS_MODE_CUSTOMLIB_AOD) { + int i, ret; + u8 data[10] = {0x02, 0}; + + for (i = 0; i < 4; i++) { + data[i * 2 + 2] = ts->rect_data[i] & 0xFF; + data[i * 2 + 3] = (ts->rect_data[i] >> 8) & 0xFF; + } + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CUSTOMLIB_WRITE_PARAM, &data[0], 10); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to write offset\n", __func__); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CUSTOMLIB_NOTIFY_PACKET, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to send notify\n", __func__); + + } + + } else { + + sec_ts_set_grip_type(ts, ONLY_EDGE_HANDLER); + + if (ts->dex_mode) { + input_info(true, &ts->client->dev, "%s: set dex mode\n", __func__); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_DEX_MODE, &ts->dex_mode, 1); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: failed to set dex mode %x\n", __func__, ts->dex_mode); + } + + if (ts->brush_mode) { + input_info(true, &ts->client->dev, "%s: set brush mode\n", __func__); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_BRUSH_MODE, &ts->brush_mode, 1); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: failed to set brush mode\n", __func__); + } + + if (ts->touchable_area) { + input_info(true, &ts->client->dev, "%s: set 16:9 mode\n", __func__); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_TOUCHABLE_AREA, &ts->touchable_area, 1); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: failed to set 16:9 mode\n", __func__); + } + + } + return; +} + +#define MAX_EVENT_COUNT 32 +static void sec_ts_read_event(struct sec_ts_data *ts) +{ + int ret; + u8 t_id; + u8 event_id; + u8 left_event_count; + u8 read_event_buff[MAX_EVENT_COUNT][SEC_TS_EVENT_BUFF_SIZE] = { { 0 } }; + u8 *event_buff; + struct sec_ts_event_coordinate *p_event_coord; + struct sec_ts_gesture_status *p_gesture_status; + struct sec_ts_event_status *p_event_status; + int curr_pos; + int remain_event_count = 0; + int pre_ttype = 0; + + if (ts->power_status == SEC_TS_STATE_LPM) { + wake_lock_timeout(&ts->wakelock, msecs_to_jiffies(500)); + + /* waiting for blsp block resuming, if not occurs i2c error */ + ret = wait_for_completion_interruptible_timeout(&ts->resume_done, msecs_to_jiffies(500)); + if (ret == 0) { + input_err(true, &ts->client->dev, "%s: LPM: pm resume is not handled\n", __func__); + return; + } + + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: LPM: -ERESTARTSYS if interrupted, %d\n", __func__, ret); + return; + } + + input_info(true, &ts->client->dev, "%s: run LPM interrupt handler, %d\n", __func__, ret); + /* run lpm interrupt handler */ + } + + ret = t_id = event_id = curr_pos = remain_event_count = 0; + /* repeat READ_ONE_EVENT until buffer is empty(No event) */ + ret = sec_ts_i2c_read(ts, SEC_TS_READ_ONE_EVENT, (u8 *)read_event_buff[0], SEC_TS_EVENT_BUFF_SIZE); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: i2c read one event failed\n", __func__); + return; + } + + if (ts->debug_flag & SEC_TS_DEBUG_PRINT_ONEEVENT) + input_info(true, &ts->client->dev, "ONE: %02X %02X %02X %02X %02X %02X %02X %02X\n", + read_event_buff[0][0], read_event_buff[0][1], + read_event_buff[0][2], read_event_buff[0][3], + read_event_buff[0][4], read_event_buff[0][5], + read_event_buff[0][6], read_event_buff[0][7]); + + if (read_event_buff[0][0] == 0) { + input_info(true, &ts->client->dev, "%s: event buffer is empty\n", __func__); + return; + } + + left_event_count = read_event_buff[0][7] & 0x3F; + remain_event_count = left_event_count; + + if (left_event_count > MAX_EVENT_COUNT - 1) { + input_err(true, &ts->client->dev, "%s: event buffer overflow\n", __func__); + + /* write clear event stack command when read_event_count > MAX_EVENT_COUNT */ + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_CLEAR_EVENT_STACK, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: i2c write clear event failed\n", __func__); + + sec_ts_unlocked_release_all_finger(ts); + + return; + } + + if (left_event_count > 0) { + ret = sec_ts_i2c_read(ts, SEC_TS_READ_ALL_EVENT, (u8 *)read_event_buff[1], + sizeof(u8) * (SEC_TS_EVENT_BUFF_SIZE) * (left_event_count)); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: i2c read one event failed\n", __func__); + return; + } + } + + do { + event_buff = read_event_buff[curr_pos]; + event_id = event_buff[0] & 0x3; + + if (ts->debug_flag & SEC_TS_DEBUG_PRINT_ALLEVENT) + input_info(true, &ts->client->dev, "ALL: %02X %02X %02X %02X %02X %02X %02X %02X\n", + event_buff[0], event_buff[1], event_buff[2], event_buff[3], + event_buff[4], event_buff[5], event_buff[6], event_buff[7]); + + switch (event_id) { + case SEC_TS_STATUS_EVENT: + p_event_status = (struct sec_ts_event_status *)event_buff; + + /* tchsta == 0 && ttype == 0 && eid == 0 : buffer empty */ + if (p_event_status->stype > 0) + input_info(true, &ts->client->dev, "%s: STATUS %x %x %x %x %x %x %x %x\n", __func__, + event_buff[0], event_buff[1], event_buff[2], + event_buff[3], event_buff[4], event_buff[5], + event_buff[6], event_buff[7]); + + /* watchdog reset -> send SENSEON command */ + if ((p_event_status->stype == TYPE_STATUS_EVENT_INFO) && + (p_event_status->status_id == SEC_TS_ACK_BOOT_COMPLETE) && + (p_event_status->status_data_1 == 0x20)) { + + sec_ts_unlocked_release_all_finger(ts); + + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to write Sense_on\n", __func__); + + sec_ts_reinit(ts); + } + + /* event queue full-> all finger release */ + if ((p_event_status->stype == TYPE_STATUS_EVENT_ERR) && + (p_event_status->status_id == SEC_TS_ERR_EVENT_QUEUE_FULL)) { + input_err(true, &ts->client->dev, "%s: IC Event Queue is full\n", __func__); + sec_ts_unlocked_release_all_finger(ts); + } + + if ((p_event_status->stype == TYPE_STATUS_EVENT_ERR) && + (p_event_status->status_id == SEC_TS_ERR_EVENT_ESD)) { + input_err(true, &ts->client->dev, "%s: ESD detected. run reset\n", __func__); +#ifdef USE_RESET_DURING_POWER_ON + schedule_work(&ts->reset_work.work); +#endif + } + + if ((p_event_status->stype == TYPE_STATUS_EVENT_INFO) && + (p_event_status->status_id == SEC_TS_ACK_WET_MODE)) { + ts->wet_mode = p_event_status->status_data_1; + input_info(true, &ts->client->dev, "%s: water wet mode %d\n", + __func__, ts->wet_mode); + if (ts->wet_mode) + ts->wet_count++; + } + + if ((p_event_status->stype == TYPE_STATUS_EVENT_VENDOR_INFO) && + (p_event_status->status_id == SEC_TS_VENDOR_ACK_NOISE_STATUS_NOTI)) { + + ts->touch_noise_status = !!p_event_status->status_data_1; + input_info(true, &ts->client->dev, "%s: TSP NOISE MODE %s[%d]\n", + __func__, ts->touch_noise_status == 0 ? "OFF" : "ON", + p_event_status->status_data_1); + + if (ts->touch_noise_status) + ts->noise_count++; + } + + break; + + case SEC_TS_COORDINATE_EVENT: + if (ts->power_status != SEC_TS_STATE_POWER_ON) { + input_err(true, &ts->client->dev, "%s: device is closed\n", __func__); + break; + } + p_event_coord = (struct sec_ts_event_coordinate *)event_buff; + + t_id = (p_event_coord->tid - 1); + + if (t_id < MAX_SUPPORT_TOUCH_COUNT + MAX_SUPPORT_HOVER_COUNT) { + pre_ttype = ts->coord[t_id].ttype; + ts->coord[t_id].id = t_id; + ts->coord[t_id].action = p_event_coord->tchsta; + ts->coord[t_id].x = (p_event_coord->x_11_4 << 4) | (p_event_coord->x_3_0); + ts->coord[t_id].y = (p_event_coord->y_11_4 << 4) | (p_event_coord->y_3_0); + ts->coord[t_id].z = p_event_coord->z & 0x3F; + ts->coord[t_id].ttype = p_event_coord->ttype_3_2 << 2 | p_event_coord->ttype_1_0 << 0; + ts->coord[t_id].major = p_event_coord->major; + ts->coord[t_id].minor = p_event_coord->minor; + + if (!ts->coord[t_id].palm && (ts->coord[t_id].ttype == SEC_TS_TOUCHTYPE_PALM)) + ts->coord[t_id].palm_count++; + + ts->coord[t_id].palm = (ts->coord[t_id].ttype == SEC_TS_TOUCHTYPE_PALM); + ts->coord[t_id].left_event = p_event_coord->left_event; + + if (ts->coord[t_id].z <= 0) + ts->coord[t_id].z = 1; + + if ((ts->coord[t_id].ttype == SEC_TS_TOUCHTYPE_NORMAL) + || (ts->coord[t_id].ttype == SEC_TS_TOUCHTYPE_PALM) + || (ts->coord[t_id].ttype == SEC_TS_TOUCHTYPE_WET) + || (ts->coord[t_id].ttype == SEC_TS_TOUCHTYPE_GLOVE)) { + + if (ts->coord[t_id].action == SEC_TS_COORDINATE_ACTION_RELEASE) { + + do_gettimeofday(&ts->time_released[t_id]); + + if (ts->time_longest < (ts->time_released[t_id].tv_sec - ts->time_pressed[t_id].tv_sec)) + ts->time_longest = (ts->time_released[t_id].tv_sec - ts->time_pressed[t_id].tv_sec); + + input_mt_slot(ts->input_dev, t_id); + if (ts->plat_data->support_mt_pressure) + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); + + if (ts->touch_count > 0) + ts->touch_count--; + if (ts->touch_count == 0) { + input_report_key(ts->input_dev, BTN_TOUCH, 0); + input_report_key(ts->input_dev, BTN_TOOL_FINGER, 0); + ts->check_multi = 0; + } + +#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP) + input_info(true, &ts->client->dev, + "%s[R] tID:%d mc:%d tc:%d lx:%d ly:%d v:%02X%02X cal:%02X id(%d,%d) p:%d noise:%x lp:(%x)\n", + ts->dex_name, + t_id, ts->coord[t_id].mcount, ts->touch_count, + ts->coord[t_id].x, ts->coord[t_id].y, + ts->plat_data->img_version_of_ic[2], + ts->plat_data->img_version_of_ic[3], + ts->cal_status, ts->tspid_val, + ts->tspicid_val, ts->coord[t_id].palm_count, + ts->touch_noise_status, ts->lowpower_mode); +#else + input_info(true, &ts->client->dev, + "%s[R] tID:%d mc:%d tc:%d v:%02X%02X cal:%02X id(%d,%d) p:%d noise:%x lp:(%x)\n", + ts->dex_name, + t_id, ts->coord[t_id].mcount, ts->touch_count, + ts->plat_data->img_version_of_ic[2], + ts->plat_data->img_version_of_ic[3], + ts->cal_status, ts->tspid_val, + ts->tspicid_val, ts->coord[t_id].palm_count, + ts->touch_noise_status, ts->lowpower_mode); +#endif + ts->coord[t_id].action = SEC_TS_COORDINATE_ACTION_NONE; + ts->coord[t_id].mcount = 0; + ts->coord[t_id].palm_count = 0; + + + } else if (ts->coord[t_id].action == SEC_TS_COORDINATE_ACTION_PRESS) { + do_gettimeofday(&ts->time_pressed[t_id]); + + ts->touch_count++; + ts->all_finger_count++; + + ts->max_z_value = max((unsigned int)ts->coord[t_id].z, ts->max_z_value); + ts->min_z_value = min((unsigned int)ts->coord[t_id].z, ts->min_z_value); + ts->sum_z_value += (unsigned int)ts->coord[t_id].z; + + input_mt_slot(ts->input_dev, t_id); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + input_report_key(ts->input_dev, BTN_TOUCH, 1); + input_report_key(ts->input_dev, BTN_TOOL_FINGER, 1); + + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, ts->coord[t_id].x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, ts->coord[t_id].y); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, ts->coord[t_id].major); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MINOR, ts->coord[t_id].minor); + if (ts->brush_mode) + input_report_abs(ts->input_dev, ABS_MT_CUSTOM, (ts->coord[t_id].z << 1) | ts->coord[t_id].palm); + else + input_report_abs(ts->input_dev, ABS_MT_CUSTOM, (BRUSH_Z_DATA << 1) | ts->coord[t_id].palm); + if (ts->plat_data->support_mt_pressure) + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, ts->coord[t_id].z); + + if ((ts->touch_count > 4) && (ts->check_multi == 0)) { + ts->check_multi = 1; + ts->multi_count++; + } + +#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP) + input_info(true, &ts->client->dev, + "%s[P] tID:%d x:%d y:%d z:%d major:%d minor:%d tc:%d type:%X noise:%x\n", + ts->dex_name, t_id, ts->coord[t_id].x, + ts->coord[t_id].y, ts->coord[t_id].z, + ts->coord[t_id].major, ts->coord[t_id].minor, + ts->touch_count, + ts->coord[t_id].ttype, ts->touch_noise_status); +#else + input_info(true, &ts->client->dev, + "%s[P] tID:%d z:%d major:%d minor:%d tc:%d type:%X noise:%x\n", + ts->dex_name, + t_id, ts->coord[t_id].z, ts->coord[t_id].major, + ts->coord[t_id].minor, ts->touch_count, + ts->coord[t_id].ttype, ts->touch_noise_status); +#endif + } else if (ts->coord[t_id].action == SEC_TS_COORDINATE_ACTION_MOVE) { + if ((ts->coord[t_id].ttype == SEC_TS_TOUCHTYPE_GLOVE) && !ts->touchkey_glove_mode_status) { + ts->touchkey_glove_mode_status = true; + input_report_switch(ts->input_dev, SW_GLOVE, 1); + } else if ((ts->coord[t_id].ttype != SEC_TS_TOUCHTYPE_GLOVE) && ts->touchkey_glove_mode_status) { + ts->touchkey_glove_mode_status = false; + input_report_switch(ts->input_dev, SW_GLOVE, 0); + } + + input_mt_slot(ts->input_dev, t_id); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + input_report_key(ts->input_dev, BTN_TOUCH, 1); + input_report_key(ts->input_dev, BTN_TOOL_FINGER, 1); + + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, ts->coord[t_id].x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, ts->coord[t_id].y); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, ts->coord[t_id].major); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MINOR, ts->coord[t_id].minor); + if (ts->brush_mode) + input_report_abs(ts->input_dev, ABS_MT_CUSTOM, (ts->coord[t_id].z << 1) | ts->coord[t_id].palm); + else + input_report_abs(ts->input_dev, ABS_MT_CUSTOM, (BRUSH_Z_DATA << 1) | ts->coord[t_id].palm); + + if (ts->plat_data->support_mt_pressure) + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, ts->coord[t_id].z); + ts->coord[t_id].mcount++; + } else { + input_dbg(true, &ts->client->dev, + "%s: do not support coordinate action(%d)\n", __func__, ts->coord[t_id].action); + } + + if ((ts->coord[t_id].action == SEC_TS_COORDINATE_ACTION_PRESS) + || (ts->coord[t_id].action == SEC_TS_COORDINATE_ACTION_MOVE)) { + + if (ts->coord[t_id].ttype != pre_ttype) { + input_info(true, &ts->client->dev, "%s : tID:%d ttype(%x->%x)\n", + __func__, ts->coord[t_id].id, + pre_ttype, ts->coord[t_id].ttype); + } + } + + } else { + input_dbg(true, &ts->client->dev, + "%s: do not support coordinate type(%d)\n", __func__, ts->coord[t_id].ttype); + } + } else { + input_err(true, &ts->client->dev, "%s: tid(%d) is out of range\n", __func__, t_id); + } + break; + + case SEC_TS_GESTURE_EVENT: + p_gesture_status = (struct sec_ts_gesture_status *)event_buff; + + switch (p_gesture_status->stype) { + case SEC_TS_GESTURE_CODE_SPAY: + /* */ + /* Gesture event handling */ + /* */ + break; + case SEC_TS_GESTURE_CODE_DOUBLE_TAP: + /* */ + /* Gesture event handling */ + /* */ + break; + } + + input_sync(ts->input_dev); + input_report_key(ts->input_dev, KEY_BLACK_UI_GESTURE, 0); + break; + default: + input_err(true, &ts->client->dev, "%s: unknown event %x %x %x %x %x %x\n", __func__, + event_buff[0], event_buff[1], event_buff[2], + event_buff[3], event_buff[4], event_buff[5]); + break; + } + + curr_pos++; + remain_event_count--; + } while (remain_event_count >= 0); + + input_sync(ts->input_dev); +} + +static irqreturn_t sec_ts_irq_thread(int irq, void *ptr) +{ + struct sec_ts_data *ts = (struct sec_ts_data *)ptr; + + mutex_lock(&ts->eventlock); + + sec_ts_read_event(ts); + + mutex_unlock(&ts->eventlock); + + return IRQ_HANDLED; +} + +int get_tsp_status(void) +{ + return 0; +} +EXPORT_SYMBOL(get_tsp_status); + +void sec_ts_set_charger(bool enable) +{ + return; +#if 0 + int ret; + u8 noise_mode_on[] = {0x01}; + u8 noise_mode_off[] = {0x00}; + + if (enable) { + input_info(true, &ts->client->dev, "sec_ts_set_charger : charger CONNECTED!!\n"); + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_NOISE_MODE, noise_mode_on, sizeof(noise_mode_on)); + if (ret < 0) + input_err(true, &ts->client->dev, "sec_ts_set_charger: fail to write NOISE_ON\n"); + } else { + input_info(true, &ts->client->dev, "sec_ts_set_charger : charger DISCONNECTED!!\n"); + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_NOISE_MODE, noise_mode_off, sizeof(noise_mode_off)); + if (ret < 0) + input_err(true, &ts->client->dev, "sec_ts_set_charger: fail to write NOISE_OFF\n"); + } +#endif +} +EXPORT_SYMBOL(sec_ts_set_charger); + +int sec_ts_glove_mode_enables(struct sec_ts_data *ts, int mode) +{ + int ret; + + if (mode) + ts->touch_functions = (ts->touch_functions | SEC_TS_BIT_SETFUNC_GLOVE | SEC_TS_DEFAULT_ENABLE_BIT_SETFUNC); + else + ts->touch_functions = ((ts->touch_functions & (~SEC_TS_BIT_SETFUNC_GLOVE)) | SEC_TS_DEFAULT_ENABLE_BIT_SETFUNC); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: pwr off, glove:%d, status:%x\n", __func__, + mode, ts->touch_functions); + goto glove_enable_err; + } + + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_TOUCHFUNCTION, (u8 *)&ts->touch_functions, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Failed to send command", __func__); + goto glove_enable_err; + } + + input_info(true, &ts->client->dev, "%s: glove:%d, status:%x\n", __func__, + mode, ts->touch_functions); + + return 0; + +glove_enable_err: + return -EIO; +} +EXPORT_SYMBOL(sec_ts_glove_mode_enables); + +int sec_ts_set_cover_type(struct sec_ts_data *ts, bool enable) +{ + int ret; + + input_info(true, &ts->client->dev, "%s: %d\n", __func__, ts->cover_type); + + + switch (ts->cover_type) { + case SEC_TS_VIEW_WIRELESS: + case SEC_TS_VIEW_COVER: + case SEC_TS_VIEW_WALLET: + case SEC_TS_FLIP_WALLET: + case SEC_TS_LED_COVER: + case SEC_TS_MONTBLANC_COVER: + case SEC_TS_CLEAR_FLIP_COVER: + case SEC_TS_QWERTY_KEYBOARD_EUR: + case SEC_TS_QWERTY_KEYBOARD_KOR: + ts->cover_cmd = (u8)ts->cover_type; + break; + case SEC_TS_CHARGER_COVER: + case SEC_TS_COVER_NOTHING1: + case SEC_TS_COVER_NOTHING2: + default: + ts->cover_cmd = 0; + input_err(true, &ts->client->dev, "%s: not chage touch state, %d\n", + __func__, ts->cover_type); + break; + } + + if (enable) + ts->touch_functions = (ts->touch_functions | SEC_TS_BIT_SETFUNC_COVER | SEC_TS_DEFAULT_ENABLE_BIT_SETFUNC); + else + ts->touch_functions = ((ts->touch_functions & (~SEC_TS_BIT_SETFUNC_COVER)) | SEC_TS_DEFAULT_ENABLE_BIT_SETFUNC); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: pwr off, close:%d, status:%x\n", __func__, + enable, ts->touch_functions); + goto cover_enable_err; + } + + if (enable) { + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_COVERTYPE, &ts->cover_cmd, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Failed to send covertype command: %d", __func__, ts->cover_cmd); + goto cover_enable_err; + } + } + + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_TOUCHFUNCTION, (u8 *)&(ts->touch_functions), 2); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Failed to send command", __func__); + goto cover_enable_err; + } + + input_info(true, &ts->client->dev, "%s: close:%d, status:%x\n", __func__, + enable, ts->touch_functions); + + return 0; + +cover_enable_err: + return -EIO; + + +} +EXPORT_SYMBOL(sec_ts_set_cover_type); + +void sec_ts_set_grip_type(struct sec_ts_data *ts, u8 set_type) +{ + u8 mode = G_NONE; + + input_info(true, &ts->client->dev, "%s: re-init grip(%d), edh:%d, edg:%d, lan:%d\n", __func__, + set_type, ts->grip_edgehandler_direction, ts->grip_edge_range, ts->grip_landscape_mode); + + /* edge handler */ + if (ts->grip_edgehandler_direction != 0) + mode |= G_SET_EDGE_HANDLER; + + if (set_type == GRIP_ALL_DATA) { + /* edge */ + if (ts->grip_edge_range != 60) + mode |= G_SET_EDGE_ZONE; + + /* dead zone */ + if (ts->grip_landscape_mode == 1) /* default 0 mode, 32 */ + mode |= G_SET_LANDSCAPE_MODE; + else + mode |= G_SET_NORMAL_MODE; + } + + if (mode) + set_grip_data_to_ic(ts, mode); + +} + +/* for debugging--------------------------------------------------------------------------------------*/ + +static int sec_ts_pinctrl_configure(struct sec_ts_data *ts, bool enable) +{ + struct pinctrl_state *state; + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, enable ? "ACTIVE" : "SUSPEND"); + + if (enable) { + state = pinctrl_lookup_state(ts->plat_data->pinctrl, "on_state"); + if (IS_ERR(ts->plat_data->pinctrl)) + input_err(true, &ts->client->dev, "%s: could not get active pinstate\n", __func__); + } else { + state = pinctrl_lookup_state(ts->plat_data->pinctrl, "off_state"); + if (IS_ERR(ts->plat_data->pinctrl)) + input_err(true, &ts->client->dev, "%s: could not get suspend pinstate\n", __func__); + } + + if (!IS_ERR_OR_NULL(state)) + return pinctrl_select_state(ts->plat_data->pinctrl, state); + + return 0; + +} + +int sec_ts_power(void *data, bool on) +{ + struct sec_ts_data *ts = (struct sec_ts_data *)data; + const struct sec_ts_plat_data *pdata = ts->plat_data; + struct regulator *regulator_dvdd = NULL; + struct regulator *regulator_avdd = NULL; + static bool enabled; + int ret = 0; + + if (enabled == on) + return ret; + + regulator_dvdd = regulator_get(NULL, pdata->regulator_dvdd); + if (IS_ERR_OR_NULL(regulator_dvdd)) { + input_err(true, &ts->client->dev, "%s: Failed to get %s regulator.\n", + __func__, pdata->regulator_dvdd); + ret = PTR_ERR(regulator_dvdd); + goto error; + } + + regulator_avdd = regulator_get(NULL, pdata->regulator_avdd); + if (IS_ERR_OR_NULL(regulator_avdd)) { + input_err(true, &ts->client->dev, "%s: Failed to get %s regulator.\n", + __func__, pdata->regulator_avdd); + ret = PTR_ERR(regulator_avdd); + goto error; + } + + if (on) { + ret = regulator_enable(regulator_dvdd); + if (ret) { + input_err(true, &ts->client->dev, "%s: Failed to enable avdd: %d\n", __func__, ret); + goto out; + } + + sec_ts_delay(1); + + ret = regulator_enable(regulator_avdd); + if (ret) { + input_err(true, &ts->client->dev, "%s: Failed to enable vdd: %d\n", __func__, ret); + goto out; + } + } else { + regulator_disable(regulator_avdd); + sec_ts_delay(4); + regulator_disable(regulator_dvdd); + } + + enabled = on; + +out: + input_err(true, &ts->client->dev, "%s: %s: avdd:%s, dvdd:%s\n", __func__, on ? "on" : "off", + regulator_is_enabled(regulator_avdd) ? "on" : "off", + regulator_is_enabled(regulator_dvdd) ? "on" : "off"); + +error: + regulator_put(regulator_dvdd); + regulator_put(regulator_avdd); + + return ret; +} + +static int sec_ts_parse_dt(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct sec_ts_plat_data *pdata = dev->platform_data; + struct device_node *np = dev->of_node; + u32 coords[2]; + int ret = 0; + int count = 0; + u32 ic_match_value; + int lcdtype = 0; + + pdata->tsp_icid = of_get_named_gpio(np, "sec,tsp-icid_gpio", 0); + if (gpio_is_valid(pdata->tsp_icid)) { + input_info(true, dev, "%s: TSP_ICID : %d\n", __func__, gpio_get_value(pdata->tsp_icid)); + if (of_property_read_u32(np, "sec,icid_match_value", &ic_match_value)) { + input_err(true, dev, "%s: Failed to get icid match value\n", __func__); + return -EINVAL; + } + + if (gpio_get_value(pdata->tsp_icid) != ic_match_value) { + input_err(true, dev, "%s: Do not match TSP_ICID\n", __func__); + return -EINVAL; + } + } else { + input_err(true, dev, "%s: Failed to get tsp-icid gpio\n", __func__); + } + + pdata->tsp_vsync = of_get_named_gpio(np, "sec,tsp_vsync_gpio", 0); + if (gpio_is_valid(pdata->tsp_vsync)) + input_info(true, &client->dev, "%s: vsync %s\n", __func__, + gpio_get_value(pdata->tsp_vsync) ? "disable" : "enable"); + + pdata->irq_gpio = of_get_named_gpio(np, "sec,irq_gpio", 0); + if (gpio_is_valid(pdata->irq_gpio)) { + ret = gpio_request_one(pdata->irq_gpio, GPIOF_DIR_IN, "sec,tsp_int"); + if (ret) { + input_err(true, &client->dev, "%s: Unable to request tsp_int [%d]\n", __func__, pdata->irq_gpio); + return -EINVAL; + } + } else { + input_err(true, &client->dev, "%s: Failed to get irq gpio\n", __func__); + return -EINVAL; + } + + client->irq = gpio_to_irq(pdata->irq_gpio); + + if (of_property_read_u32(np, "sec,irq_type", &pdata->irq_type)) { + input_err(true, dev, "%s: Failed to get irq_type property\n", __func__); + pdata->irq_type = IRQF_TRIGGER_LOW | IRQF_ONESHOT; + } else { + input_info(true, dev, "%s: irq_type property:%X, %d\n", __func__, + pdata->irq_type, pdata->irq_type); + } + + if (of_property_read_u32(np, "sec,i2c-burstmax", &pdata->i2c_burstmax)) { + input_dbg(false, &client->dev, "%s: Failed to get i2c_burstmax property\n", __func__); + pdata->i2c_burstmax = 256; + } + + if (of_property_read_u32_array(np, "sec,max_coords", coords, 2)) { + input_err(true, &client->dev, "%s: Failed to get max_coords property\n", __func__); + return -EINVAL; + } + pdata->max_x = coords[0] - 1; + pdata->max_y = coords[1] - 1; + + pdata->tsp_id = of_get_named_gpio(np, "sec,tsp-id_gpio", 0); + if (gpio_is_valid(pdata->tsp_id)) + input_info(true, dev, "%s: TSP_ID : %d\n", __func__, gpio_get_value(pdata->tsp_id)); + else + input_err(true, dev, "%s: Failed to get tsp-id gpio\n", __func__); + + count = of_property_count_strings(np, "sec,firmware_name"); + if (count <= 0) { + pdata->firmware_name = NULL; + } else { + if (gpio_is_valid(pdata->tsp_id)) + of_property_read_string_index(np, "sec,firmware_name", gpio_get_value(pdata->tsp_id), &pdata->firmware_name); + else + of_property_read_string_index(np, "sec,firmware_name", 0, &pdata->firmware_name); + } + + if (of_property_read_string_index(np, "sec,project_name", 0, &pdata->project_name)) + input_err(true, &client->dev, "%s: skipped to get project_name property\n", __func__); + if (of_property_read_string_index(np, "sec,project_name", 1, &pdata->model_name)) + input_err(true, &client->dev, "%s: skipped to get model_name property\n", __func__); + + if (of_property_read_string(np, "sec,regulator_dvdd", &pdata->regulator_dvdd)) { + input_err(true, dev, "%s: Failed to get regulator_dvdd name property\n", __func__); + return -EINVAL; + } + + if (of_property_read_string(np, "sec,regulator_avdd", &pdata->regulator_avdd)) { + input_err(true, dev, "%s: Failed to get regulator_avdd name property\n", __func__); + return -EINVAL; + } + + pdata->power = sec_ts_power; + + if (of_property_read_u32(np, "sec,always_lpmode", &pdata->always_lpmode) < 0) + pdata->always_lpmode = 0; + + if (of_property_read_u32(np, "sec,bringup", &pdata->bringup) < 0) + pdata->bringup = 0; + + if (of_property_read_u32(np, "sec,mis_cal_check", &pdata->mis_cal_check) < 0) + pdata->mis_cal_check = 0; + + pdata->regulator_boot_on = of_property_read_bool(np, "sec,regulator_boot_on"); + pdata->support_sidegesture = of_property_read_bool(np, "sec,support_sidegesture"); + pdata->support_dex = of_property_read_bool(np, "support_dex_mode"); + +#ifdef CONFIG_SEC_FACTORY + pdata->support_mt_pressure = true; +#endif + + input_err(true, &client->dev, "%s: i2c buffer limit: %d, lcd_id:%06X, bringup:%d, FW:%s(%d), id:%d,%d, mis_cal:%d dex:%d, gesture:%d\n", + __func__, pdata->i2c_burstmax, lcdtype, pdata->bringup, pdata->firmware_name, + count, pdata->tsp_id, pdata->tsp_icid, pdata->mis_cal_check, + pdata->support_dex, pdata->support_sidegesture); + return ret; +} + +int sec_ts_read_information(struct sec_ts_data *ts) +{ + unsigned char data[13] = { 0 }; + int ret; + + memset(data, 0x0, 3); + ret = sec_ts_i2c_read(ts, SEC_TS_READ_ID, data, 3); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read device id(%d)\n", + __func__, ret); + return ret; + } + + input_info(true, &ts->client->dev, + "%s: %X, %X, %X\n", + __func__, data[0], data[1], data[2]); + memset(data, 0x0, 11); + ret = sec_ts_i2c_read(ts, SEC_TS_READ_PANEL_INFO, data, 11); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read sub id(%d)\n", + __func__, ret); + return ret; + } + + input_info(true, &ts->client->dev, + "%s: nTX:%X, nRX:%X, rY:%d, rX:%d\n", + __func__, data[8], data[9], + (data[2] << 8) | data[3], (data[0] << 8) | data[1]); + + /* Set X,Y Resolution from IC information. */ + if (((data[0] << 8) | data[1]) > 0) + ts->plat_data->max_x = ((data[0] << 8) | data[1]) - 1; + + if (((data[2] << 8) | data[3]) > 0) + ts->plat_data->max_y = ((data[2] << 8) | data[3]) - 1; + + ts->tx_count = data[8]; + ts->rx_count = data[9]; + + data[0] = 0; + ret = sec_ts_i2c_read(ts, SEC_TS_READ_BOOT_STATUS, data, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read sub id(%d)\n", + __func__, ret); + return ret; + } + + input_info(true, &ts->client->dev, + "%s: STATUS : %X\n", + __func__, data[0]); + + memset(data, 0x0, 4); + ret = sec_ts_i2c_read(ts, SEC_TS_READ_TS_STATUS, data, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read sub id(%d)\n", + __func__, ret); + return ret; + } + + input_info(true, &ts->client->dev, + "%s: TOUCH STATUS : %02X, %02X, %02X, %02X\n", + __func__, data[0], data[1], data[2], data[3]); + ret = sec_ts_i2c_read(ts, SEC_TS_CMD_SET_TOUCHFUNCTION, (u8 *)&(ts->touch_functions), 2); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read touch functions(%d)\n", + __func__, ret); + return ret; + } + + input_info(true, &ts->client->dev, + "%s: Functions : %02X\n", + __func__, ts->touch_functions); + + return ret; +} + +#ifdef SEC_TS_SUPPORT_CUSTOMLIB +int sec_ts_set_custom_library(struct sec_ts_data *ts) +{ + u8 data[3] = { 0 }; + int ret; + + input_err(true, &ts->client->dev, "%s: Custom Library (0x%02x)\n", + __func__, ts->lowpower_mode); + + data[2] = ts->lowpower_mode; + + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_CUSTOMLIB_WRITE_PARAM, &data[0], 3); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to Custom Library\n", __func__); + + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_CUSTOMLIB_NOTIFY_PACKET, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to send NOTIFY Custom Library\n", __func__); + + return ret; +} + +int sec_ts_check_custom_library(struct sec_ts_data *ts) +{ + u8 data[10] = { 0 }; + int ret = -1; + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_CUSTOMLIB_GET_INFO, &data[0], 10); + + input_info(true, &ts->client->dev, + "%s: (%d) %c%c%c%c, || %02X, %02X, %02X, %02X, || %02X, %02X\n", + __func__, ret, data[0], data[1], data[2], data[3], data[4], + data[5], data[6], data[7], data[8], data[9]); + + /* compare model name with device tree */ + if (ts->plat_data->model_name) + ret = strncmp(data, ts->plat_data->model_name, 4); + + if (ret == 0) + ts->use_customlib = true; + else + ts->use_customlib = false; + + input_err(true, &ts->client->dev, "%s: use %s\n", + __func__, ts->use_customlib? "CUSTOMLIB" : "VENDOR"); + + return ret; +} +#endif + +static void sec_ts_set_input_prop(struct sec_ts_data *ts, struct input_dev *dev, u8 propbit) +{ + static char sec_ts_phys[64] = { 0 }; + + snprintf(sec_ts_phys, sizeof(sec_ts_phys), "%s/input1", + dev->name); + dev->phys = sec_ts_phys; + dev->id.bustype = BUS_I2C; + dev->dev.parent = &ts->client->dev; + + set_bit(EV_SYN, dev->evbit); + set_bit(EV_KEY, dev->evbit); + set_bit(EV_ABS, dev->evbit); + set_bit(EV_SW, dev->evbit); + set_bit(BTN_TOUCH, dev->keybit); + set_bit(BTN_TOOL_FINGER, dev->keybit); + set_bit(KEY_BLACK_UI_GESTURE, dev->keybit); +#ifdef SEC_TS_SUPPORT_TOUCH_KEY + if (ts->plat_data->support_mskey) { + int i; + + for (i = 0 ; i < ts->plat_data->num_touchkey ; i++) + set_bit(ts->plat_data->touchkey[i].keycode, dev->keybit); + + set_bit(EV_LED, dev->evbit); + set_bit(LED_MISC, dev->ledbit); + } +#endif + if (ts->plat_data->support_sidegesture) { + set_bit(KEY_SIDE_GESTURE, dev->keybit); + set_bit(KEY_SIDE_GESTURE_RIGHT, dev->keybit); + set_bit(KEY_SIDE_GESTURE_LEFT, dev->keybit); + } + set_bit(propbit, dev->propbit); + set_bit(KEY_HOMEPAGE, dev->keybit); + + input_set_capability(dev, EV_SW, SW_GLOVE); + + input_set_abs_params(dev, ABS_MT_POSITION_X, 0, ts->plat_data->max_x, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, ts->plat_data->max_y, 0, 0); + input_set_abs_params(dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(dev, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0); + input_set_abs_params(dev, ABS_MT_CUSTOM, 0, 0xFFFFFFFF, 0, 0); + if (ts->plat_data->support_mt_pressure) + input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); + + if (propbit == INPUT_PROP_POINTER) + input_mt_init_slots(dev, MAX_SUPPORT_TOUCH_COUNT, INPUT_MT_POINTER); + else + input_mt_init_slots(dev, MAX_SUPPORT_TOUCH_COUNT, INPUT_MT_DIRECT); + + input_set_drvdata(dev, ts); +} + +static int sec_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct sec_ts_data *ts; + struct sec_ts_plat_data *pdata; + int ret = 0; + bool force_update = false; + bool valid_firmware_integrity = false; + unsigned char data[5] = { 0 }; + unsigned char deviceID[5] = { 0 }; + unsigned char result = 0; + + input_info(true, &client->dev, "%s\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + input_err(true, &client->dev, "%s: EIO err!\n", __func__); + return -EIO; + } + + /* parse dt */ + if (client->dev.of_node) { + pdata = devm_kzalloc(&client->dev, + sizeof(struct sec_ts_plat_data), GFP_KERNEL); + + if (!pdata) { + input_err(true, &client->dev, "%s: Failed to allocate platform data\n", __func__); + goto error_allocate_pdata; + } + + client->dev.platform_data = pdata; + + ret = sec_ts_parse_dt(client); + if (ret) { + input_err(true, &client->dev, "%s: Failed to parse dt\n", __func__); + goto error_allocate_mem; + } + } else { + pdata = client->dev.platform_data; + if (!pdata) { + input_err(true, &client->dev, "%s: No platform data found\n", __func__); + goto error_allocate_pdata; + } + } + + if (!pdata->power) { + input_err(true, &client->dev, "%s: No power contorl found\n", __func__); + goto error_allocate_mem; + } + + pdata->pinctrl = devm_pinctrl_get(&client->dev); + if (IS_ERR(pdata->pinctrl)) + input_err(true, &client->dev, "%s: could not get pinctrl\n", __func__); + + ts = kzalloc(sizeof(struct sec_ts_data), GFP_KERNEL); + if (!ts) + goto error_allocate_mem; + + ts->client = client; + ts->plat_data = pdata; + ts->crc_addr = 0x0001FE00; + ts->fw_addr = 0x00002000; + ts->para_addr = 0x18000; + ts->flash_page_size = SEC_TS_FW_BLK_SIZE_DEFAULT; + ts->sec_ts_i2c_read = sec_ts_i2c_read; + ts->sec_ts_i2c_write = sec_ts_i2c_write; + ts->sec_ts_i2c_write_burst = sec_ts_i2c_write_burst; + ts->sec_ts_i2c_read_bulk = sec_ts_i2c_read_bulk; + ts->i2c_burstmax = pdata->i2c_burstmax; +#ifdef USE_POWER_RESET_WORK + INIT_DELAYED_WORK(&ts->reset_work, sec_ts_reset_work); +#endif + INIT_DELAYED_WORK(&ts->work_read_info, sec_ts_read_info_work); + + i2c_set_clientdata(client, ts); + + if (gpio_is_valid(ts->plat_data->tsp_id)) + ts->tspid_val = gpio_get_value(ts->plat_data->tsp_id); + + if (gpio_is_valid(ts->plat_data->tsp_icid)) + ts->tspicid_val = gpio_get_value(ts->plat_data->tsp_icid); + + ts->input_dev = input_allocate_device(); + if (!ts->input_dev) { + input_err(true, &ts->client->dev, "%s: allocate device err!\n", __func__); + ret = -ENOMEM; + goto err_allocate_input_dev; + } + + if (ts->plat_data->support_dex) { + ts->input_dev_pad = input_allocate_device(); + if (!ts->input_dev_pad) { + input_err(true, &ts->client->dev, "%s: allocate device err!\n", __func__); + ret = -ENOMEM; + goto err_allocate_input_dev_pad; + } + } + + ts->touch_count = 0; + ts->sec_ts_i2c_write = sec_ts_i2c_write; + ts->sec_ts_i2c_read = sec_ts_i2c_read; + ts->sec_ts_read_customlib = sec_ts_read_from_customlib; + + ts->max_z_value = 0; + ts->min_z_value = 0xFFFFFFFF; + ts->sum_z_value = 0; + + mutex_init(&ts->lock); + mutex_init(&ts->device_mutex); + mutex_init(&ts->i2c_mutex); + mutex_init(&ts->eventlock); + mutex_init(&ts->modechange); + + wake_lock_init(&ts->wakelock, WAKE_LOCK_SUSPEND, "tsp_wakelock"); + init_completion(&ts->resume_done); + complete_all(&ts->resume_done); + + if (pdata->always_lpmode) + ts->lowpower_mode |= SEC_TS_MODE_CUSTOMLIB_FORCE_KEY; + else + ts->lowpower_mode &= ~SEC_TS_MODE_CUSTOMLIB_FORCE_KEY; + + input_info(true, &client->dev, "%s: init resource\n", __func__); + + sec_ts_pinctrl_configure(ts, true); + + /* power enable */ + sec_ts_power(ts, true); + if (!pdata->regulator_boot_on) + sec_ts_delay(70); + ts->power_status = SEC_TS_STATE_POWER_ON; + ts->external_factory = false; + + sec_ts_wait_for_ready(ts, SEC_TS_ACK_BOOT_COMPLETE); + + input_info(true, &client->dev, "%s: power enable\n", __func__); + + ret = sec_ts_i2c_read(ts, SEC_TS_READ_DEVICE_ID, deviceID, 5); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: failed to read device ID(%d)\n", __func__, ret); + else + input_info(true, &ts->client->dev, + "%s: TOUCH DEVICE ID : %02X, %02X, %02X, %02X, %02X\n", __func__, + deviceID[0], deviceID[1], deviceID[2], deviceID[3], deviceID[4]); + + ret = sec_ts_i2c_read(ts, SEC_TS_READ_FIRMWARE_INTEGRITY, &result, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to integrity check (%d)\n", __func__, ret); + } else { + if (result & 0x80) { + valid_firmware_integrity = true; + } else if (result & 0x40) { + valid_firmware_integrity = false; + input_err(true, &ts->client->dev, "%s: invalid firmware (0x%x)\n", __func__, result); + } else { + valid_firmware_integrity = false; + input_err(true, &ts->client->dev, "%s: invalid integrity result (0x%x)\n", __func__, result); + } + } + + ret = sec_ts_i2c_read(ts, SEC_TS_READ_BOOT_STATUS, &data[0], 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read sub id(%d)\n", + __func__, ret); + } else { + ret = sec_ts_i2c_read(ts, SEC_TS_READ_TS_STATUS, &data[1], 4); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to touch status(%d)\n", + __func__, ret); + } + } + input_info(true, &ts->client->dev, + "%s: TOUCH STATUS : %02X || %02X, %02X, %02X, %02X\n", + __func__, data[0], data[1], data[2], data[3], data[4]); + + if (data[0] == SEC_TS_STATUS_BOOT_MODE) + ts->checksum_result = 1; + + if ((data[0] == SEC_TS_STATUS_APP_MODE && data[2] == TOUCH_SYSTEM_MODE_FLASH) || + (valid_firmware_integrity == false)) + force_update = true; + else + force_update = false; + +#ifdef SEC_TS_FW_UPDATE_ON_PROBE + ret = sec_ts_firmware_update_on_probe(ts, force_update); + if (ret < 0) + goto err_init; +#else + input_info(true, &ts->client->dev, "%s: fw update on probe disabled!\n", __func__); +#endif + + ret = sec_ts_read_information(ts); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail to read information 0x%x\n", __func__, ret); + goto err_init; + } + + ts->touch_functions |= SEC_TS_DEFAULT_ENABLE_BIT_SETFUNC; + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_TOUCHFUNCTION, (u8 *)&ts->touch_functions, 2); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to send touch func_mode command", __func__); + + /* Sense_on */ + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail to write Sense_on\n", __func__); + goto err_init; + } + + ts->pFrame = kzalloc(ts->tx_count * ts->rx_count * 2, GFP_KERNEL); + if (!ts->pFrame) { + ret = -ENOMEM; + goto err_allocate_frame; + } + + if (ts->plat_data->support_dex) { + ts->input_dev_pad->name = "sec_touchpad"; + sec_ts_set_input_prop(ts, ts->input_dev_pad, INPUT_PROP_POINTER); + } + ts->dex_name = ""; + + ts->input_dev->name = "sec_touchscreen"; + sec_ts_set_input_prop(ts, ts->input_dev, INPUT_PROP_DIRECT); +#ifdef USE_OPEN_CLOSE + ts->input_dev->open = sec_ts_input_open; + ts->input_dev->close = sec_ts_input_close; +#endif + ts->input_dev_touch = ts->input_dev; + + ret = input_register_device(ts->input_dev); + if (ret) { + input_err(true, &ts->client->dev, "%s: Unable to register %s input device\n", __func__, ts->input_dev->name); + goto err_input_register_device; + } + if (ts->plat_data->support_dex) { + ret = input_register_device(ts->input_dev_pad); + if (ret) { + input_err(true, &ts->client->dev, "%s: Unable to register %s input device\n", __func__, ts->input_dev_pad->name); + goto err_input_pad_register_device; + } + } + + input_info(true, &ts->client->dev, "%s: request_irq = %d\n", __func__, client->irq); + + ret = request_threaded_irq(client->irq, NULL, sec_ts_irq_thread, + ts->plat_data->irq_type, SEC_TS_I2C_NAME, ts); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Unable to request threaded irq\n", __func__); + goto err_irq; + } + + /* need remove below resource @ remove driver */ +#if (1) //!defined(CONFIG_SAMSUNG_PRODUCT_SHIP) + sec_ts_raw_device_init(ts); +#endif + sec_ts_fn_init(ts); + + +#ifdef SEC_TS_SUPPORT_CUSTOMLIB + sec_ts_check_custom_library(ts); + if (ts->use_customlib) + sec_ts_set_custom_library(ts); + +#endif + + device_init_wakeup(&client->dev, true); + + schedule_delayed_work(&ts->work_read_info, msecs_to_jiffies(50)); + +#if defined(CONFIG_TOUCHSCREEN_DUMP_MODE) + dump_callbacks.inform_dump = dump_tsp_log; + INIT_DELAYED_WORK(&ts->ghost_check, sec_ts_check_rawdata); + p_ghost_check = &ts->ghost_check; +#endif + + ts_dup = ts; + ts->probe_done = true; + + input_err(true, &ts->client->dev, "%s: done\n", __func__); + input_log_fix(); + + return 0; + + /* need to be enabled when new goto statement is added */ +#if 0 + sec_ts_fn_remove(ts); + free_irq(client->irq, ts); +#endif +err_irq: + if (ts->plat_data->support_dex) { + input_unregister_device(ts->input_dev_pad); + ts->input_dev_pad = NULL; + } +err_input_pad_register_device: + input_unregister_device(ts->input_dev); + ts->input_dev = NULL; + ts->input_dev_touch = NULL; +err_input_register_device: + kfree(ts->pFrame); +err_allocate_frame: +err_init: + wake_lock_destroy(&ts->wakelock); + sec_ts_power(ts, false); + if (ts->plat_data->support_dex) { + if (ts->input_dev_pad) + input_free_device(ts->input_dev_pad); + } +err_allocate_input_dev_pad: + if (ts->input_dev) + input_free_device(ts->input_dev); +err_allocate_input_dev: + kfree(ts); + +error_allocate_mem: + if (gpio_is_valid(pdata->irq_gpio)) + gpio_free(pdata->irq_gpio); + if (gpio_is_valid(pdata->tsp_id)) + gpio_free(pdata->tsp_id); + if (gpio_is_valid(pdata->tsp_icid)) + gpio_free(pdata->tsp_icid); + +error_allocate_pdata: + if (ret == -ECONNREFUSED) + sec_ts_delay(100); + ret = -ENODEV; +#ifdef CONFIG_TOUCHSCREEN_DUMP_MODE + p_ghost_check = NULL; +#endif + ts_dup = NULL; + input_err(true, &client->dev, "%s: failed(%d)\n", __func__, ret); + input_log_fix(); + return ret; +} + +void sec_ts_unlocked_release_all_finger(struct sec_ts_data *ts) +{ + int i; + + for (i = 0; i < MAX_SUPPORT_TOUCH_COUNT; i++) { + input_mt_slot(ts->input_dev, i); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); + + if ((ts->coord[i].action == SEC_TS_COORDINATE_ACTION_PRESS) || + (ts->coord[i].action == SEC_TS_COORDINATE_ACTION_MOVE)) { + + ts->coord[i].action = SEC_TS_COORDINATE_ACTION_RELEASE; + input_info(true, &ts->client->dev, + "%s: [RA] tID:%d mc:%d tc:%d v:%02X%02X cal:%02X id(%d,%d) p:%d\n", + __func__, i, ts->coord[i].mcount, ts->touch_count, + ts->plat_data->img_version_of_ic[2], + ts->plat_data->img_version_of_ic[3], + ts->cal_status, ts->tspid_val, + ts->tspicid_val, ts->coord[i].palm_count); + + do_gettimeofday(&ts->time_released[i]); + + if (ts->time_longest < (ts->time_released[i].tv_sec - ts->time_pressed[i].tv_sec)) + ts->time_longest = (ts->time_released[i].tv_sec - ts->time_pressed[i].tv_sec); + } + + ts->coord[i].mcount = 0; + ts->coord[i].palm_count = 0; + + } + + input_mt_slot(ts->input_dev, 0); + + input_report_key(ts->input_dev, BTN_TOUCH, false); + input_report_key(ts->input_dev, BTN_TOOL_FINGER, false); + input_report_switch(ts->input_dev, SW_GLOVE, false); + ts->touchkey_glove_mode_status = false; + ts->touch_count = 0; + ts->check_multi = 0; + + if (ts->plat_data->support_sidegesture) { + input_report_key(ts->input_dev, KEY_SIDE_GESTURE, 0); + input_report_key(ts->input_dev, KEY_SIDE_GESTURE_LEFT, 0); + input_report_key(ts->input_dev, KEY_SIDE_GESTURE_RIGHT, 0); + } + + input_report_key(ts->input_dev, KEY_HOMEPAGE, 0); + input_sync(ts->input_dev); + +} + +void sec_ts_locked_release_all_finger(struct sec_ts_data *ts) +{ + int i; + + mutex_lock(&ts->eventlock); + + for (i = 0; i < MAX_SUPPORT_TOUCH_COUNT; i++) { + input_mt_slot(ts->input_dev, i); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); + + if ((ts->coord[i].action == SEC_TS_COORDINATE_ACTION_PRESS) || + (ts->coord[i].action == SEC_TS_COORDINATE_ACTION_MOVE)) { + + ts->coord[i].action = SEC_TS_COORDINATE_ACTION_RELEASE; + input_info(true, &ts->client->dev, + "%s: [RA] tID:%d mc: %d tc:%d, v:%02X%02X, cal:%X id(%d,%d), p:%d\n", + __func__, i, ts->coord[i].mcount, ts->touch_count, + ts->plat_data->img_version_of_ic[2], + ts->plat_data->img_version_of_ic[3], + ts->cal_status, ts->tspid_val, ts->tspicid_val, ts->coord[i].palm_count); + + do_gettimeofday(&ts->time_released[i]); + + if (ts->time_longest < (ts->time_released[i].tv_sec - ts->time_pressed[i].tv_sec)) + ts->time_longest = (ts->time_released[i].tv_sec - ts->time_pressed[i].tv_sec); + } + + ts->coord[i].mcount = 0; + ts->coord[i].palm_count = 0; + + } + + input_mt_slot(ts->input_dev, 0); + + input_report_key(ts->input_dev, BTN_TOUCH, false); + input_report_key(ts->input_dev, BTN_TOOL_FINGER, false); + input_report_switch(ts->input_dev, SW_GLOVE, false); + ts->touchkey_glove_mode_status = false; + ts->touch_count = 0; + ts->check_multi = 0; + + if (ts->plat_data->support_sidegesture) { + input_report_key(ts->input_dev, KEY_SIDE_GESTURE, 0); + input_report_key(ts->input_dev, KEY_SIDE_GESTURE_LEFT, 0); + input_report_key(ts->input_dev, KEY_SIDE_GESTURE_RIGHT, 0); + } + + input_report_key(ts->input_dev, KEY_HOMEPAGE, 0); + input_sync(ts->input_dev); + + mutex_unlock(&ts->eventlock); + +} + +#ifdef USE_POWER_RESET_WORK +static void sec_ts_reset_work(struct work_struct *work) +{ + struct sec_ts_data *ts = container_of(work, struct sec_ts_data, + reset_work.work); + int ret; + + if (ts->reset_is_on_going) { + input_err(true, &ts->client->dev, "%s: reset is ongoing\n", __func__); + return; + } + + mutex_lock(&ts->modechange); + wake_lock(&ts->wakelock); + + ts->reset_is_on_going = true; + input_info(true, &ts->client->dev, "%s\n", __func__); + + sec_ts_stop_device(ts); + + sec_ts_delay(30); + + ret = sec_ts_start_device(ts); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to reset, ret:%d\n", __func__, ret); + ts->reset_is_on_going = false; + cancel_delayed_work(&ts->reset_work); + schedule_delayed_work(&ts->reset_work, msecs_to_jiffies(TOUCH_RESET_DWORK_TIME)); + mutex_unlock(&ts->modechange); + + wake_unlock(&ts->wakelock); + + return; + } + + if (ts->input_dev_touch->disabled) { + input_err(true, &ts->client->dev , "%s: call input_close\n", __func__); + + if (ts->lowpower_mode) { + ret = sec_ts_set_lowpowermode(ts, TO_LOWPOWER_MODE); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to reset, ret:%d\n", __func__, ret); + ts->reset_is_on_going = false; + cancel_delayed_work(&ts->reset_work); + schedule_delayed_work(&ts->reset_work, msecs_to_jiffies(TOUCH_RESET_DWORK_TIME)); + mutex_unlock(&ts->modechange); + wake_unlock(&ts->wakelock); + return; + } + } else { + sec_ts_stop_device(ts); + } + + if ((ts->lowpower_mode & SEC_TS_MODE_CUSTOMLIB_AOD) && ts->use_customlib) { + int i, ret; + u8 data[10] = {0x02, 0}; + + for (i = 0; i < 4; i++) { + data[i * 2 + 2] = ts->rect_data[i] & 0xFF; + data[i * 2 + 3] = (ts->rect_data[i] >> 8) & 0xFF; + } + + disable_irq(ts->client->irq); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CUSTOMLIB_WRITE_PARAM, &data[0], 10); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to write offset\n", __func__); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CUSTOMLIB_NOTIFY_PACKET, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to send notify\n", __func__); + enable_irq(ts->client->irq); + } + } + ts->reset_is_on_going = false; + mutex_unlock(&ts->modechange); + + wake_unlock(&ts->wakelock); +} +#endif + +static void sec_ts_read_info_work(struct work_struct *work) +{ + struct sec_ts_data *ts = container_of(work, struct sec_ts_data, + work_read_info.work); + char para = TO_TOUCH_MODE; + int ret; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + /* run self-test */ + disable_irq(ts->client->irq); + execute_selftest(ts, false); + enable_irq(ts->client->irq); + + input_info(true, &ts->client->dev, "%s: %02X %02X %02X %02X\n", + __func__, ts->ito_test[0], ts->ito_test[1] + , ts->ito_test[2], ts->ito_test[3]); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_POWER_MODE, ¶, 1); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to set\n", __func__); + + sec_ts_delay(350); + + input_log_fix(); + + sec_ts_run_rawdata_all(ts, false); + ts->info_work_done = true; +} + + +int sec_ts_set_lowpowermode(struct sec_ts_data *ts, u8 mode) +{ + int ret; + int retrycnt = 0; + u8 data; + char para = 0; + + input_err(true, &ts->client->dev, "%s: %s(%X)\n", __func__, + mode == TO_LOWPOWER_MODE ? "ENTER" : "EXIT", ts->lowpower_mode); + + if (mode) { + #ifdef SEC_TS_SUPPORT_CUSTOMLIB + if (ts->use_customlib) { + ret = sec_ts_set_custom_library(ts); + if (ret < 0) + goto i2c_error; + } + #endif + + data = (ts->lowpower_mode & SEC_TS_MODE_LOWPOWER_FLAG) >> 1; + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_WAKEUP_GESTURE_MODE, &data, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Failed to set\n", __func__); + goto i2c_error; + } + } + +retry_pmode: + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_POWER_MODE, &mode, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed\n", __func__); + goto i2c_error; + } + + sec_ts_delay(50); + + ret = sec_ts_i2c_read(ts, SEC_TS_CMD_SET_POWER_MODE, ¶, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read power mode failed!\n", __func__); + goto i2c_error; + } else { + input_info(true, &ts->client->dev, "%s: power mode - write(%d) read(%d)\n", __func__, mode, para); + } + + if (mode != para) { + retrycnt++; + if (retrycnt < 5) + goto retry_pmode; + } + + if (mode) { + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_CLEAR_EVENT_STACK, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: i2c write clear event failed\n", __func__); + goto i2c_error; + } + } + + sec_ts_locked_release_all_finger(ts); + + if (device_may_wakeup(&ts->client->dev)) { + if (mode) + enable_irq_wake(ts->client->irq); + else + disable_irq_wake(ts->client->irq); + } + + if (mode == TO_LOWPOWER_MODE) + ts->power_status = SEC_TS_STATE_LPM; + else + ts->power_status = SEC_TS_STATE_POWER_ON; + +i2c_error: + input_info(true, &ts->client->dev, "%s: end %d\n", __func__, ret); + + return ret; +} + +#ifdef USE_OPEN_CLOSE +static int sec_ts_input_open(struct input_dev *dev) +{ + struct sec_ts_data *ts = input_get_drvdata(dev); + int ret; + + if (!ts->info_work_done) { + input_err(true, &ts->client->dev, "%s not finished info work\n", __func__); + return 0; + } + + mutex_lock(&ts->modechange); + + ts->input_closed = false; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + if (ts->power_status == SEC_TS_STATE_LPM) { +#ifdef USE_RESET_EXIT_LPM + schedule_delayed_work(&ts->reset_work, msecs_to_jiffies(TOUCH_RESET_DWORK_TIME)); +#else + sec_ts_set_lowpowermode(ts, TO_TOUCH_MODE); +#endif + } else { + ret = sec_ts_start_device(ts); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to start device\n", __func__); + } + + /* because edge and dead zone will recover soon */ + sec_ts_set_grip_type(ts, ONLY_EDGE_HANDLER); + + mutex_unlock(&ts->modechange); + + return 0; +} + +static void sec_ts_input_close(struct input_dev *dev) +{ + struct sec_ts_data *ts = input_get_drvdata(dev); + + if (!ts->info_work_done) { + input_err(true, &ts->client->dev, "%s not finished info work\n", __func__); + return; + } + + mutex_lock(&ts->modechange); + + ts->input_closed = true; + + input_info(true, &ts->client->dev, "%s\n", __func__); + +#ifdef USE_POWER_RESET_WORK + cancel_delayed_work(&ts->reset_work); +#endif + +#ifndef CONFIG_SEC_FACTORY + ts->lowpower_mode |= SEC_TS_MODE_CUSTOMLIB_FORCE_KEY; +#endif + if (ts->lowpower_mode) { + sec_ts_set_lowpowermode(ts, TO_LOWPOWER_MODE); + } else { + sec_ts_stop_device(ts); + } + + mutex_unlock(&ts->modechange); +} +#endif + +static int sec_ts_remove(struct i2c_client *client) +{ + struct sec_ts_data *ts = i2c_get_clientdata(client); + + input_info(true, &ts->client->dev, "%s\n", __func__); + + cancel_delayed_work_sync(&ts->work_read_info); + flush_delayed_work(&ts->work_read_info); + + disable_irq_nosync(ts->client->irq); + free_irq(ts->client->irq, ts); + input_info(true, &ts->client->dev, "%s: irq disabled\n", __func__); + +#ifdef USE_POWER_RESET_WORK + cancel_delayed_work_sync(&ts->reset_work); + flush_delayed_work(&ts->reset_work); + + input_info(true, &ts->client->dev, "%s: flush queue\n", __func__); + +#endif + + sec_ts_fn_remove(ts); + +#ifdef CONFIG_TOUCHSCREEN_DUMP_MODE + p_ghost_check = NULL; +#endif + device_init_wakeup(&client->dev, false); + wake_lock_destroy(&ts->wakelock); + + ts->lowpower_mode = false; + ts->probe_done = false; + + if (ts->plat_data->support_dex) { + input_mt_destroy_slots(ts->input_dev_pad); + input_unregister_device(ts->input_dev_pad); + } + + ts->input_dev = ts->input_dev_touch; + input_mt_destroy_slots(ts->input_dev); + input_unregister_device(ts->input_dev); + + ts->input_dev_pad = NULL; + ts->input_dev = NULL; + ts->input_dev_touch = NULL; + ts_dup = NULL; + ts->plat_data->power(ts, false); + + kfree(ts); + return 0; +} + +static void sec_ts_shutdown(struct i2c_client *client) +{ + struct sec_ts_data *ts = i2c_get_clientdata(client); + + input_info(true, &ts->client->dev, "%s\n", __func__); + + sec_ts_remove(client); +} + +int sec_ts_stop_device(struct sec_ts_data *ts) +{ + input_info(true, &ts->client->dev, "%s\n", __func__); + + mutex_lock(&ts->device_mutex); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: already power off\n", __func__); + goto out; + } + + disable_irq(ts->client->irq); + + ts->power_status = SEC_TS_STATE_POWER_OFF; + + sec_ts_locked_release_all_finger(ts); + + ts->plat_data->power(ts, false); + + if (ts->plat_data->enable_sync) + ts->plat_data->enable_sync(false); + + sec_ts_pinctrl_configure(ts, false); + +out: + mutex_unlock(&ts->device_mutex); + return 0; +} + +int sec_ts_start_device(struct sec_ts_data *ts) +{ + int ret = -1; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + sec_ts_pinctrl_configure(ts, true); + + mutex_lock(&ts->device_mutex); + + if (ts->power_status == SEC_TS_STATE_POWER_ON) { + input_err(true, &ts->client->dev, "%s: already power on\n", __func__); + goto out; + } + + sec_ts_locked_release_all_finger(ts); + + ts->plat_data->power(ts, true); + sec_ts_delay(70); + ts->power_status = SEC_TS_STATE_POWER_ON; + ts->touch_noise_status = 0; + + ret = sec_ts_wait_for_ready(ts, SEC_TS_ACK_BOOT_COMPLETE); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: Failed to wait_for_ready\n", __func__); + goto err; + } + + if (ts->plat_data->enable_sync) + ts->plat_data->enable_sync(true); + + if (ts->flip_enable) { + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_COVERTYPE, &ts->cover_cmd, 1); + if (ret < 0) + goto err; + + ts->touch_functions = ts->touch_functions | SEC_TS_BIT_SETFUNC_COVER; + input_info(true, &ts->client->dev, + "%s: cover cmd write type:%d, mode:%x, ret:%d\n", + __func__, ts->touch_functions, ts->cover_cmd, ret); + } else { + ts->touch_functions = (ts->touch_functions & (~SEC_TS_BIT_SETFUNC_COVER)); + input_info(true, &ts->client->dev, + "%s: cover open, not send cmd\n", __func__); + } + + ts->touch_functions = ts->touch_functions | SEC_TS_DEFAULT_ENABLE_BIT_SETFUNC; + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_TOUCHFUNCTION, (u8 *)&ts->touch_functions, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: Failed to send touch function command\n", __func__); + goto err; + } + + #ifdef SEC_TS_SUPPORT_CUSTOMLIB + if (ts->use_customlib) { + ret = sec_ts_set_custom_library(ts); + if (ret < 0) + goto err; + } + #endif + + sec_ts_set_grip_type(ts, ONLY_EDGE_HANDLER); + + if (ts->dex_mode) { + input_info(true, &ts->client->dev, "%s: set dex mode\n", __func__); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_DEX_MODE, &ts->dex_mode, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to set dex mode %x\n", __func__, ts->dex_mode); + goto err; + } + } + + if (ts->brush_mode) { + input_info(true, &ts->client->dev, "%s: set brush mode\n", __func__); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_BRUSH_MODE, &ts->brush_mode, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to set brush mode\n", __func__); + goto err; + } + } + + if (ts->touchable_area) { + input_info(true, &ts->client->dev, "%s: set 16:9 mode\n", __func__); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_TOUCHABLE_AREA, &ts->touchable_area, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to set 16:9 mode\n", __func__); + goto err; + } + } + +err: + /* Sense_on */ + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to write Sense_on\n", __func__); + + enable_irq(ts->client->irq); + +out: + mutex_unlock(&ts->device_mutex); + return ret; +} + +#ifdef CONFIG_PM +static int sec_ts_pm_suspend(struct device *dev) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + if (ts->lowpower_mode) + reinit_completion(&ts->resume_done); + + return 0; +} + +static int sec_ts_pm_resume(struct device *dev) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + if (ts->lowpower_mode) + complete_all(&ts->resume_done); + + return 0; +} +#endif + +static const struct i2c_device_id sec_ts_id[] = { + { SEC_TS_I2C_NAME, 0 }, + { }, +}; + +#ifdef CONFIG_PM +static const struct dev_pm_ops sec_ts_dev_pm_ops = { + .suspend = sec_ts_pm_suspend, + .resume = sec_ts_pm_resume, +}; +#endif + +#ifdef CONFIG_OF +static const struct of_device_id sec_ts_match_table[] = { + { .compatible = "sec,sec_ts",}, + { }, +}; +#else +#define sec_ts_match_table NULL +#endif + +static struct i2c_driver sec_ts_driver = { + .probe = sec_ts_probe, + .remove = sec_ts_remove, + .shutdown = sec_ts_shutdown, + .id_table = sec_ts_id, + .driver = { + .owner = THIS_MODULE, + .name = SEC_TS_I2C_NAME, +#ifdef CONFIG_OF + .of_match_table = sec_ts_match_table, +#endif +#ifdef CONFIG_PM + .pm = &sec_ts_dev_pm_ops, +#endif + }, +}; + +static int __init sec_ts_init(void) +{ +#ifdef CONFIG_BATTERY_SAMSUNG + if (lpcharge == 1) { + pr_err("%s %s: Do not load driver due to : lpm %d\n", + SECLOG, __func__, lpcharge); + return -ENODEV; + } +#endif + pr_err("%s %s\n", SECLOG, __func__); + + return i2c_add_driver(&sec_ts_driver); +} + +static void __exit sec_ts_exit(void) +{ + i2c_del_driver(&sec_ts_driver); +} + +MODULE_AUTHOR("Hyobae, Ahn"); +MODULE_DESCRIPTION("Samsung Electronics TouchScreen driver"); +MODULE_LICENSE("GPL"); + +module_init(sec_ts_init); +module_exit(sec_ts_exit); diff --git a/drivers/input/touchscreen/sec_mmi/sec_ts.h b/drivers/input/touchscreen/sec_mmi/sec_ts.h new file mode 100644 index 000000000000..84ca197e8829 --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/sec_ts.h @@ -0,0 +1,822 @@ +/* drivers/input/touchscreen/sec_ts.h + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * http://www.samsungsemi.com/ + * + * Core file for Samsung TSC driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __SEC_TS_H__ +#define __SEC_TS_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_INPUT_BOOSTER +#include +#endif + +#ifndef input_info +#define input_info(f, d, fmt, args...) dev_info(d, fmt ##args) +#endif + +#ifndef input_err +#define input_info(f, d, fmt, args...) dev_info(d, fmt ##args) +#endif + +#define SEC_TS_I2C_NAME "sec_ts" +#define SEC_TS_DEVICE_NAME "SEC_TS" + +#define USE_OPEN_CLOSE +#undef USE_RESET_DURING_POWER_ON +#undef USE_RESET_EXIT_LPM +#define USE_POR_AFTER_I2C_RETRY +#undef USER_OPEN_DWORK +#define USE_PRESSURE_SENSOR +#define PAT_CONTROL + +#if defined(USE_RESET_DURING_POWER_ON) || defined(USE_POR_AFTER_I2C_RETRY) || defined(USE_RESET_EXIT_LPM) +#define USE_POWER_RESET_WORK +#endif + +#define TOUCH_RESET_DWORK_TIME 10 +#define BRUSH_Z_DATA 63 /* for ArtCanvas */ + +#define MASK_1_BITS 0x0001 +#define MASK_2_BITS 0x0003 +#define MASK_3_BITS 0x0007 +#define MASK_4_BITS 0x000F +#define MASK_5_BITS 0x001F +#define MASK_6_BITS 0x003F +#define MASK_7_BITS 0x007F +#define MASK_8_BITS 0x00FF + +/* support feature */ +//#define SEC_TS_SUPPORT_CUSTOMLIB /* support user defined library */ + +#define TYPE_STATUS_EVENT_CMD_DRIVEN 0 +#define TYPE_STATUS_EVENT_ERR 1 +#define TYPE_STATUS_EVENT_INFO 2 +#define TYPE_STATUS_EVENT_USER_INPUT 3 +#define TYPE_STATUS_EVENT_CUSTOMLIB_INFO 6 +#define TYPE_STATUS_EVENT_VENDOR_INFO 7 +#define TYPE_STATUS_CODE_SAR 0x28 + +#define BIT_STATUS_EVENT_CMD_DRIVEN(a) (a << TYPE_STATUS_EVENT_CMD_DRIVEN) +#define BIT_STATUS_EVENT_ERR(a) (a << TYPE_STATUS_EVENT_ERR) +#define BIT_STATUS_EVENT_INFO(a) (a << TYPE_STATUS_EVENT_INFO) +#define BIT_STATUS_EVENT_USER_INPUT(a) (a << TYPE_STATUS_EVENT_USER_INPUT) +#define BIT_STATUS_EVENT_VENDOR_INFO(a) (a << TYPE_STATUS_EVENT_VENDOR_INFO) + +#define DO_FW_CHECKSUM (1 << 0) +#define DO_PARA_CHECKSUM (1 << 1) +#define MAX_SUPPORT_TOUCH_COUNT 10 +#define MAX_SUPPORT_HOVER_COUNT 1 + +#define SEC_TS_EVENTID_HOVER 10 + +#define SEC_TS_DEFAULT_FW_NAME "tsp_sec/sec_hero.fw" +#define SEC_TS_DEFAULT_BL_NAME "tsp_sec/s6smc41_blupdate_img_REL.bin" +#define SEC_TS_DEFAULT_PARA_NAME "tsp_sec/s6smc41_para_REL_DGA0_V0106_150114_193317.bin" +#define SEC_TS_DEFAULT_UMS_FW "/sdcard/Firmware/TSP/lsi.bin" +#define SEC_TS_DEFAULT_FFU_FW "ffu_tsp.bin" +#define SEC_TS_MAX_FW_PATH 64 +#define SEC_TS_FW_BLK_SIZE_MAX (512) +#define SEC_TS_FW_BLK_SIZE_DEFAULT (512) +#define SEC_TS_SELFTEST_REPORT_SIZE 80 + +#define I2C_WRITE_BUFFER_SIZE (256 - 1)//10 + +#define SEC_TS_FW_HEADER_SIGN 0x53494654 +#define SEC_TS_FW_CHUNK_SIGN 0x53434654 + +#define SEC_TS_FW_UPDATE_ON_PROBE + +#define AMBIENT_CAL 0 +#define OFFSET_CAL_SDC 1 +#define OFFSET_CAL_SET 2 +#define PRESSURE_CAL 3 + +#define SEC_TS_SKIPTSP_DUTY 100 + +#define SEC_TS_NVM_OFFSET_FAC_RESULT 0 +#define SEC_TS_NVM_OFFSET_CAL_COUNT 1 +#define SEC_TS_NVM_OFFSET_DISASSEMBLE_COUNT 2 +#define SEC_TS_NVM_OFFSET_TUNE_VERSION 3 +#define SEC_TS_NVM_OFFSET_TUNE_VERSION_LENGTH 2 + +#define SEC_TS_NVM_OFFSET_LENGTH (SEC_TS_NVM_LAST_BLOCK_OFFSET + SEC_TS_NVM_LAST_BLOCK_SIZE + 1) + +/* SEC_TS READ REGISTER ADDRESS */ +#define SEC_TS_CMD_SENSE_ON 0x10 +#define SEC_TS_CMD_SENSE_OFF 0x11 +#define SEC_TS_CMD_SW_RESET 0x12 +#define SEC_TS_CMD_CALIBRATION_SEC 0x13 // send it to touch ic, but toucu ic works nothing. +#define SEC_TS_CMD_FACTORY_PANELCALIBRATION 0x14 + +#define SEC_TS_READ_GPIO_STATUS 0x20 // not support +#define SEC_TS_READ_FIRMWARE_INTEGRITY 0x21 +#define SEC_TS_READ_DEVICE_ID 0x22 +#define SEC_TS_READ_PANEL_INFO 0x23 +#define SEC_TS_READ_CORE_CONFIG_VERSION 0x24 + +#define SEC_TS_CMD_SET_TOUCHFUNCTION 0x30 +#define SEC_TS_CMD_SET_TSC_MODE 0x31 +#define SET_TS_CMD_SET_CHARGER_MODE 0x32 +#define SET_TS_CMD_SET_NOISE_MODE 0x33 +#define SET_TS_CMD_SET_REPORT_RATE 0x34 +#define SEC_TS_CMD_TOUCH_MODE_FOR_THRESHOLD 0x35 +#define SEC_TS_CMD_TOUCH_THRESHOLD 0x36 +#define SET_TS_CMD_KEY_THRESHOLD 0x37 +#define SEC_TS_CMD_SET_COVERTYPE 0x38 +#define SEC_TS_CMD_WAKEUP_GESTURE_MODE 0x39 +#define SEC_TS_WRITE_POSITION_FILTER 0x3A +#define SEC_TS_CMD_WET_MODE 0x3B +#define SEC_TS_CMD_ERASE_FLASH 0x45 +#define SEC_TS_READ_ID 0x52 +#define SEC_TS_READ_BOOT_STATUS 0x55 +#define SEC_TS_CMD_ENTER_FW_MODE 0x57 +#define SEC_TS_READ_ONE_EVENT 0x60 +#define SEC_TS_READ_ALL_EVENT 0x61 +#define SEC_TS_CMD_CLEAR_EVENT_STACK 0x62 +#define SEC_TS_CMD_MUTU_RAW_TYPE 0x70 +#define SEC_TS_CMD_SELF_RAW_TYPE 0x71 +#define SEC_TS_READ_TOUCH_RAWDATA 0x72 +#define SEC_TS_READ_TOUCH_SELF_RAWDATA 0x73 +#define SEC_TS_READ_SELFTEST_RESULT 0x80 +#define SEC_TS_CMD_CALIBRATION_AMBIENT 0x81 +#define SEC_TS_CMD_P2P_TEST 0x82 +#define SEC_TS_CMD_P2P_MODE 0x83 +#define SEC_TS_CMD_NVM 0x85 +#define SEC_TS_CMD_STATEMANAGE_ON 0x8E +#define SEC_TS_CMD_CALIBRATION_OFFSET_SDC 0x8F + +/* SEC_TS CUSTOMLIB OPCODE COMMAND */ +#define SEC_TS_CMD_CUSTOMLIB_GET_INFO 0x90 +#define SEC_TS_CMD_CUSTOMLIB_WRITE_PARAM 0x91 +#define SEC_TS_CMD_CUSTOMLIB_READ_PARAM 0x92 +#define SEC_TS_CMD_CUSTOMLIB_NOTIFY_PACKET 0x93 + +#define SEC_TS_CMD_STATUS_EVENT_TYPE 0xA0 +#define SEC_TS_READ_FW_INFO 0xA2 +#define SEC_TS_READ_FW_VERSION 0xA3 +#define SEC_TS_READ_PARA_VERSION 0xA4 +#define SEC_TS_READ_IMG_VERSION 0xA5 +#define SEC_TS_CMD_GET_CHECKSUM 0xA6 +#define SEC_TS_CMD_MIS_CAL_CHECK 0xA7 +#define SEC_TS_CMD_MIS_CAL_READ 0xA8 +#define SEC_TS_CMD_MIS_CAL_SPEC 0xA9 +#define SEC_TS_CMD_DEADZONE_RANGE 0xAA +#define SEC_TS_CMD_LONGPRESSZONE_RANGE 0xAB +#define SEC_TS_CMD_LONGPRESS_DROP_AREA 0xAC +#define SEC_TS_CMD_LONGPRESS_DROP_DIFF 0xAD +#define SEC_TS_READ_TS_STATUS 0xAF +#define SEC_TS_CMD_SELFTEST 0xAE +#define SEC_TS_READ_FORCE_RECAL_COUNT 0xB0 +#define SEC_TS_READ_FORCE_SIG_MAX_VAL 0xB1 + +/* SEC_TS FLASH COMMAND */ +#define SEC_TS_CMD_FLASH_READ_ADDR 0xD0 +#define SEC_TS_CMD_FLASH_READ_SIZE 0xD1 +#define SEC_TS_CMD_FLASH_READ_DATA 0xD2 +#define SEC_TS_CMD_CHG_SYSMODE 0xD7 +#define SEC_TS_CMD_FLASH_ERASE 0xD8 +#define SEC_TS_CMD_FLASH_WRITE 0xD9 +#define SEC_TS_CMD_FLASH_PADDING 0xDA + +#define SEC_TS_READ_BL_UPDATE_STATUS 0xDB +#define SEC_TS_CMD_SET_POWER_MODE 0xE4 +#define SEC_TS_CMD_EDGE_DEADZONE 0xE5 +#define SEC_TS_CMD_SET_DEX_MODE 0xE7 +#define SEC_TS_CMD_CALIBRATION_PRESSURE 0xE9 +/* Have to need delay 30msec after writing 0xEA command */ +/* Do not write Zero with 0xEA command */ +#define SEC_TS_CMD_SET_GET_PRESSURE 0xEA +#define SEC_TS_CMD_SET_USER_PRESSURE 0xEB +#define SEC_TS_CMD_SET_TEMPERATURE_COMP_MODE 0xEC +#define SEC_TS_CMD_SET_TOUCHABLE_AREA 0xED +#define SEC_TS_CMD_SET_BRUSH_MODE 0xEF + +#define SEC_TS_READ_CALIBRATION_REPORT 0xF1 +#define SEC_TS_CMD_SET_VENDOR_EVENT_LEVEL 0xF2 +#define SEC_TS_CMD_SET_SPENMODE 0xF3 +#define SEC_TS_CMD_SELECT_PRESSURE_TYPE 0xF5 +#define SEC_TS_CMD_READ_PRESSURE_DATA 0xF6 + +#define SEC_TS_FLASH_SIZE_64 64 +#define SEC_TS_FLASH_SIZE_128 128 +#define SEC_TS_FLASH_SIZE_256 256 + +#define SEC_TS_FLASH_SIZE_CMD 1 +#define SEC_TS_FLASH_SIZE_ADDR 2 +#define SEC_TS_FLASH_SIZE_CHECKSUM 1 + +#define SEC_TS_STATUS_BOOT_MODE 0x10 +#define SEC_TS_STATUS_APP_MODE 0x20 + +#define SEC_TS_FIRMWARE_PAGE_SIZE_256 256 +#define SEC_TS_FIRMWARE_PAGE_SIZE_128 128 + +/* SEC status event id */ +#define SEC_TS_COORDINATE_EVENT 0 +#define SEC_TS_STATUS_EVENT 1 +#define SEC_TS_GESTURE_EVENT 2 +#define SEC_TS_EMPTY_EVENT 3 + +#define SEC_TS_EVENT_BUFF_SIZE 8 +#define SEC_TS_SID_GESTURE 0x14 +#define SEC_TS_GESTURE_CODE_SPAY 0x00 +#define SEC_TS_GESTURE_CODE_DOUBLE_TAP 0x01 + +#define SEC_TS_COORDINATE_ACTION_NONE 0 +#define SEC_TS_COORDINATE_ACTION_PRESS 1 +#define SEC_TS_COORDINATE_ACTION_MOVE 2 +#define SEC_TS_COORDINATE_ACTION_RELEASE 3 + +#define SEC_TS_TOUCHTYPE_NORMAL 0 +#define SEC_TS_TOUCHTYPE_HOVER 1 +#define SEC_TS_TOUCHTYPE_FLIPCOVER 2 +#define SEC_TS_TOUCHTYPE_GLOVE 3 +#define SEC_TS_TOUCHTYPE_STYLUS 4 +#define SEC_TS_TOUCHTYPE_PALM 5 +#define SEC_TS_TOUCHTYPE_WET 6 +#define SEC_TS_TOUCHTYPE_PROXIMITY 7 +#define SEC_TS_TOUCHTYPE_JIG 8 + +/* SEC_TS_INFO : Info acknowledge event */ +#define SEC_TS_ACK_BOOT_COMPLETE 0x00 +#define SEC_TS_ACK_WET_MODE 0x1 + +/* SEC_TS_VENDOR_INFO : Vendor acknowledge event */ +#define SEC_TS_VENDOR_ACK_OFFSET_CAL_DONE 0x40 +#define SEC_TS_VENDOR_ACK_SELF_TEST_DONE 0x41 +#define SEC_TS_VENDOR_ACK_CMR_TEST_DONE 0x42 /* mutual */ + +#define SEC_TS_VENDOR_ACK_NOISE_STATUS_NOTI 0x64 +/* SEC_TS_STATUS_EVENT_USER_INPUT */ +#define SEC_TS_EVENT_FORCE_KEY 0x1 + +/* SEC_TS_STATUS_EVENT_CUSTOMLIB_INFO */ +#define SEC_TS_EVENT_CUSTOMLIB_FORCE_KEY 0x00 + +/* SEC_TS_ERROR : Error event */ +#define SEC_TS_ERR_EVNET_CORE_ERR 0x0 +#define SEC_TS_ERR_EVENT_QUEUE_FULL 0x01 +#define SEC_TS_ERR_EVENT_ESD 0x2 + +/* SEC_TS_DEBUG : Print event contents */ +#define SEC_TS_DEBUG_PRINT_ALLEVENT 0x01 +#define SEC_TS_DEBUG_PRINT_ONEEVENT 0x02 +#define SEC_TS_DEBUG_PRINT_I2C_READ_CMD 0x04 +#define SEC_TS_DEBUG_PRINT_I2C_WRITE_CMD 0x08 +#define SEC_TS_DEBUG_SEND_UEVENT 0x80 + +#define SEC_TS_BIT_SETFUNC_TOUCH (1 << 0) +#define SEC_TS_BIT_SETFUNC_MUTUAL (1 << 0) +#define SEC_TS_BIT_SETFUNC_HOVER (1 << 1) +#define SEC_TS_BIT_SETFUNC_COVER (1 << 2) +#define SEC_TS_BIT_SETFUNC_GLOVE (1 << 3) +#define SEC_TS_BIT_SETFUNC_STYLUS (1 << 4) +#define SEC_TS_BIT_SETFUNC_PALM (1 << 5) +#define SEC_TS_BIT_SETFUNC_WET (1 << 6) +#define SEC_TS_BIT_SETFUNC_PROXIMITY (1 << 7) + +#define SEC_TS_DEFAULT_ENABLE_BIT_SETFUNC (SEC_TS_BIT_SETFUNC_TOUCH | SEC_TS_BIT_SETFUNC_PALM | SEC_TS_BIT_SETFUNC_WET) + +#define SEC_TS_BIT_CHARGER_MODE_NO (0x1 << 0) +#define SEC_TS_BIT_CHARGER_MODE_WIRE_CHARGER (0x1 << 1) +#define SEC_TS_BIT_CHARGER_MODE_WIRELESS_CHARGER (0x1 << 2) +#define SEC_TS_BIT_CHARGER_MODE_WIRELESS_BATTERY_PACK (0x1 << 3) + +#define STATE_MANAGE_ON 1 +#define STATE_MANAGE_OFF 0 + +#define SEC_TS_STATUS_NOT_CALIBRATION 0x50 +#define SEC_TS_STATUS_CALIBRATION_SDC 0xA1 +#define SEC_TS_STATUS_CALIBRATION_SEC 0xA2 + +#define SEC_TS_CMD_EDGE_HANDLER 0xAA +#define SEC_TS_CMD_EDGE_AREA 0xAB +#define SEC_TS_CMD_DEAD_ZONE 0xAC +#define SEC_TS_CMD_LANDSCAPE_MODE 0xAD + +enum grip_write_mode { + G_NONE = 0, + G_SET_EDGE_HANDLER = 1, + G_SET_EDGE_ZONE = 2, + G_SET_NORMAL_MODE = 4, + G_SET_LANDSCAPE_MODE = 8, + G_CLR_LANDSCAPE_MODE = 16, +}; +enum grip_set_data { + ONLY_EDGE_HANDLER = 0, + GRIP_ALL_DATA = 1, +}; + +typedef enum { + SEC_TS_STATE_POWER_OFF = 0, + SEC_TS_STATE_LPM, + SEC_TS_STATE_POWER_ON +} TOUCH_POWER_MODE; + +typedef enum { + TOUCH_SYSTEM_MODE_BOOT = 0, + TOUCH_SYSTEM_MODE_CALIBRATION = 1, + TOUCH_SYSTEM_MODE_TOUCH = 2, + TOUCH_SYSTEM_MODE_SELFTEST = 3, + TOUCH_SYSTEM_MODE_FLASH = 4, + TOUCH_SYSTEM_MODE_LOWPOWER = 5, + TOUCH_SYSTEM_MODE_LISTEN +} TOUCH_SYSTEM_MODE; + +typedef enum { + TOUCH_MODE_STATE_IDLE = 0, + TOUCH_MODE_STATE_HOVER = 1, + TOUCH_MODE_STATE_TOUCH = 2, + TOUCH_MODE_STATE_NOISY = 3, + TOUCH_MODE_STATE_CAL = 4, + TOUCH_MODE_STATE_CAL2 = 5, + TOUCH_MODE_STATE_WAKEUP = 10 +} TOUCH_MODE_STATE; + +enum switch_system_mode { + TO_TOUCH_MODE = 0, + TO_LOWPOWER_MODE = 1, + TO_SELFTEST_MODE = 2, + TO_FLASH_MODE = 3, +}; + +enum { + TYPE_RAW_DATA = 0, /* Total - Offset : delta data */ + TYPE_SIGNAL_DATA = 1, /* Signal - Filtering & Normalization */ + TYPE_AMBIENT_BASELINE = 2, /* Cap Baseline */ + TYPE_AMBIENT_DATA = 3, /* Cap Ambient */ + TYPE_REMV_BASELINE_DATA = 4, + TYPE_DECODED_DATA = 5, /* Raw */ + TYPE_REMV_AMB_DATA = 6, /* TYPE_RAW_DATA - TYPE_AMBIENT_DATA */ + TYPE_OFFSET_DATA_SET = 19, /* Cap Offset in SET Manufacturing Line */ + TYPE_OFFSET_DATA_SDC = 29, /* Cap Offset in SDC Manufacturing Line */ + TYPE_RAW_DATA_P2P_MIN = 30, /* Raw min data for 100 frame */ + TYPE_RAW_DATA_P2P_MAX = 31, /* Raw max data for 100 frame */ + TYPE_RAWDATA_MAX, + TYPE_INVALID_DATA = 0xFF, /* Invalid data type for release factory mode */ +}; + +typedef enum { + CUSTOMLIB_EVENT_TYPE_SPAY = 0x04, + CUSTOMLIB_EVENT_TYPE_PRESSURE_TOUCHED = 0x05, + CUSTOMLIB_EVENT_TYPE_PRESSURE_RELEASED = 0x06, + CUSTOMLIB_EVENT_TYPE_AOD = 0x08, + CUSTOMLIB_EVENT_TYPE_AOD_PRESS = 0x09, + CUSTOMLIB_EVENT_TYPE_AOD_LONGPRESS = 0x0A, + CUSTOMLIB_EVENT_TYPE_AOD_DOUBLETAB = 0x0B, + CUSTOMLIB_EVENT_TYPE_AOD_HOMEKEY_PRESS = 0x0C, + CUSTOMLIB_EVENT_TYPE_AOD_HOMEKEY_RELEASE = 0x0D, + CUSTOMLIB_EVENT_TYPE_AOD_HOMEKEY_RELEASE_NO_HAPTIC = 0x0E +} CUSTOMLIB_EVENT_TYPE; + +#define CMD_RESULT_WORD_LEN 10 + +#define SEC_TS_I2C_RETRY_CNT 3 +#define SEC_TS_WAIT_RETRY_CNT 100 + +#define SEC_TS_MODE_CUSTOMLIB_SPAY (1 << 1) +#define SEC_TS_MODE_CUSTOMLIB_AOD (1 << 2) +#define SEC_TS_MODE_CUSTOMLIB_FORCE_KEY (1 << 6) + +#define SEC_TS_MODE_LOWPOWER_FLAG (SEC_TS_MODE_CUSTOMLIB_SPAY | SEC_TS_MODE_CUSTOMLIB_AOD \ + | SEC_TS_MODE_CUSTOMLIB_FORCE_KEY) + +#define SEC_TS_CUSTOMLIB_EVENT_PRESSURE_TOUCHED (1 << 6) +#define SEC_TS_CUSTOMLIB_EVENT_PRESSURE_RELEASED (1 << 7) + +enum sec_ts_cover_id { + SEC_TS_FLIP_WALLET = 0, + SEC_TS_VIEW_COVER, + SEC_TS_COVER_NOTHING1, + SEC_TS_VIEW_WIRELESS, + SEC_TS_COVER_NOTHING2, + SEC_TS_CHARGER_COVER, + SEC_TS_VIEW_WALLET, + SEC_TS_LED_COVER, + SEC_TS_CLEAR_FLIP_COVER, + SEC_TS_QWERTY_KEYBOARD_EUR, + SEC_TS_QWERTY_KEYBOARD_KOR, + SEC_TS_MONTBLANC_COVER = 100, +}; + +enum sec_fw_update_status { + SEC_NOT_UPDATE = 0, + SEC_NEED_FW_UPDATE, + SEC_NEED_CALIBRATION_ONLY, + SEC_NEED_FW_UPDATE_N_CALIBRATION, +}; + +enum tsp_hw_parameter { + TSP_ITO_CHECK = 1, + TSP_RAW_CHECK = 2, + TSP_MULTI_COUNT = 3, + TSP_WET_MODE = 4, + TSP_COMM_ERR_COUNT = 5, + TSP_MODULE_ID = 6, +}; + +#define TEST_MODE_MIN_MAX false +#define TEST_MODE_ALL_NODE true +#define TEST_MODE_READ_FRAME false +#define TEST_MODE_READ_CHANNEL true + +/* factory test mode */ +struct sec_ts_test_mode { + u8 type; + short min; + short max; + bool allnode; + bool frame_channel; +}; + +struct sec_ts_fw_file { + u8 *data; + u32 pos; + size_t size; +}; + +/* + * write 0xE4 [ 11 | 10 | 01 | 00 ] + * MSB <-------------------> LSB + * read 0xE4 + * mapping sequnce : LSB -> MSB + * struct sec_ts_test_result { + * * assy : front + OCTA assay + * * module : only OCTA + * union { + * struct { + * u8 assy_count:2; -> 00 + * u8 assy_result:2; -> 01 + * u8 module_count:2; -> 10 + * u8 module_result:2; -> 11 + * } __attribute__ ((packed)); + * unsigned char data[1]; + * }; + *}; + */ +struct sec_ts_test_result { + union { + struct { + u8 assy_count:2; + u8 assy_result:2; + u8 module_count:2; + u8 module_result:2; + } __attribute__ ((packed)); + unsigned char data[1]; + }; +}; + +/* 8 byte */ +struct sec_ts_gesture_status { + u8 eid:2; + u8 stype:4; + u8 sf:2; + u8 gesture_id; + u8 gesture_data_1; + u8 gesture_data_2; + u8 gesture_data_3; + u8 gesture_data_4; + u8 reserved_1; + u8 left_event_5_0:6; + u8 reserved_2:2; +} __attribute__ ((packed)); + +/* 8 byte */ +struct sec_ts_event_status { + u8 eid:2; + u8 stype:4; + u8 sf:2; + u8 status_id; + u8 status_data_1; + u8 status_data_2; + u8 status_data_3; + u8 status_data_4; + u8 status_data_5; + u8 left_event_5_0:6; + u8 reserved_2:2; +} __attribute__ ((packed)); + +/* 8 byte */ +struct sec_ts_event_coordinate { + u8 eid:2; + u8 tid:4; + u8 tchsta:2; + u8 x_11_4; + u8 y_11_4; + u8 y_3_0:4; + u8 x_3_0:4; + u8 major; + u8 minor; + u8 z:6; + u8 ttype_3_2:2; + u8 left_event:6; + u8 ttype_1_0:2; +} __attribute__ ((packed)); + +/* not fixed */ +struct sec_ts_coordinate { + u8 id; + u8 ttype; + u8 action; + u16 x; + u16 y; + u8 z; + u8 hover_flag; + u8 glove_flag; + u8 touch_height; + u16 mcount; + u8 major; + u8 minor; + bool palm; + int palm_count; + u8 left_event; +}; + + +struct sec_ts_data { + u32 isr_pin; + + u32 crc_addr; + u32 fw_addr; + u32 para_addr; + u32 flash_page_size; + u8 boot_ver[3]; + + struct device *dev; + struct i2c_client *client; + struct input_dev *input_dev; + struct input_dev *input_dev_pad; + struct input_dev *input_dev_touch; + struct sec_ts_plat_data *plat_data; + struct sec_ts_coordinate coord[MAX_SUPPORT_TOUCH_COUNT + MAX_SUPPORT_HOVER_COUNT]; + + struct timeval time_pressed[MAX_SUPPORT_TOUCH_COUNT + MAX_SUPPORT_HOVER_COUNT]; + struct timeval time_released[MAX_SUPPORT_TOUCH_COUNT + MAX_SUPPORT_HOVER_COUNT]; + long time_longest; + + u8 lowpower_mode; + u8 lowpower_status; + u8 dex_mode; + char *dex_name; + u8 brush_mode; + u8 touchable_area; + volatile u8 touch_noise_status; + volatile bool input_closed; + + int touch_count; + int tx_count; + int rx_count; + int i2c_burstmax; + int ta_status; + volatile int power_status; + int raw_status; + int touchkey_glove_mode_status; + u16 touch_functions; + u8 charger_mode; + struct sec_ts_event_coordinate touchtype; + bool touched[11]; + u8 gesture_status[6]; + u8 cal_status; + struct mutex lock; + struct mutex device_mutex; + struct mutex i2c_mutex; + struct mutex eventlock; + struct mutex modechange; + + struct delayed_work work_read_info; +#ifdef USE_POWER_RESET_WORK + struct delayed_work reset_work; + volatile bool reset_is_on_going; +#endif + struct completion resume_done; + struct wake_lock wakelock; + struct sec_cmd_data sec; + short *pFrame; + + bool probe_done; + bool reinit_done; + bool flip_enable; + bool info_work_done; + int cover_type; + u8 cover_cmd; + u16 rect_data[4]; + + int tspid_val; + int tspicid_val; + + bool use_customlib; + unsigned int scrub_id; + unsigned int scrub_x; + unsigned int scrub_y; + + u8 grip_edgehandler_direction; + int grip_edgehandler_start_y; + int grip_edgehandler_end_y; + u16 grip_edge_range; + u8 grip_deadzone_up_x; + u8 grip_deadzone_dn_x; + int grip_deadzone_y; + u8 grip_landscape_mode; + int grip_landscape_edge; + u16 grip_landscape_deadzone; + +#ifdef CONFIG_TOUCHSCREEN_DUMP_MODE + struct delayed_work ghost_check; + u8 tsp_dump_lock; +#endif + + int nv; + int cal_count; + int tune_fix_ver; + bool external_factory; + + int wet_mode; + + unsigned char ito_test[4]; /* ito panel tx/rx chanel */ + unsigned char check_multi; + unsigned int multi_count; /* multi touch count */ + unsigned int wet_count; /* wet mode count */ + unsigned int noise_count; /* noise mode count */ + unsigned int dive_count; /* dive mode count */ + unsigned int comm_err_count; /* i2c comm error count */ + unsigned int checksum_result; /* checksum result */ + unsigned char module_id[4]; + unsigned int all_finger_count; + unsigned int all_force_count; + unsigned int all_aod_tap_count; + unsigned int all_spay_count; + unsigned int max_z_value; + unsigned int min_z_value; + unsigned int sum_z_value; + unsigned char pressure_cal_base; + unsigned char pressure_cal_delta; + + + /* for factory - factory_cmd_result_all() */ + short cm_raw_set_p2p_min; //CM_RAW_SET_P2P + short cm_raw_set_p2p_max; //CM_RAW_SET_P2P + short cm_raw_set_p2p_diff; //CM_RAW_SET_P2P_DIFF + +#ifdef USE_PRESSURE_SENSOR + short pressure_left; + short pressure_center; + short pressure_right; + u8 pressure_user_level; +#endif + int debug_flag; + + int (*sec_ts_i2c_write)(struct sec_ts_data *ts, u8 reg, u8 *data, int len); + int (*sec_ts_i2c_read)(struct sec_ts_data *ts, u8 reg, u8 *data, int len); + int (*sec_ts_i2c_write_burst)(struct sec_ts_data *ts, u8 *data, int len); + int (*sec_ts_i2c_read_bulk)(struct sec_ts_data *ts, u8 *data, int len); + int (*sec_ts_read_customlib)(struct sec_ts_data *ts, u8 *data, int len); +}; + +struct sec_ts_plat_data { + int max_x; + int max_y; + unsigned irq_gpio; + int irq_type; + int i2c_burstmax; + int always_lpmode; + int bringup; + int mis_cal_check; + + const char *firmware_name; + const char *model_name; + const char *project_name; + const char *regulator_dvdd; + const char *regulator_avdd; + + u32 panel_revision; + u8 core_version_of_ic[4]; + u8 core_version_of_bin[4]; + u8 config_version_of_ic[4]; + u8 config_version_of_bin[4]; + u8 img_version_of_ic[4]; + u8 img_version_of_bin[4]; + + struct pinctrl *pinctrl; + + int (*power)(void *data, bool on); + void (*enable_sync)(bool on); + int tsp_icid; + int tsp_id; + int tsp_vsync; + + bool regulator_boot_on; + bool support_mt_pressure; + bool support_dex; + bool support_sidegesture; +}; + +typedef struct { + u32 signature; /* signature */ + u32 version; /* version */ + u32 totalsize; /* total size */ + u32 checksum; /* checksum */ + u32 img_ver; /* image file version */ + u32 img_date; /* image file date */ + u32 img_description; /* image file description */ + u32 fw_ver; /* firmware version */ + u32 fw_date; /* firmware date */ + u32 fw_description; /* firmware description */ + u32 para_ver; /* parameter version */ + u32 para_date; /* parameter date */ + u32 para_description; /* parameter description */ + u32 num_chunk; /* number of chunk */ + u32 reserved1; + u32 reserved2; +} fw_header; + +typedef struct { + u32 signature; + u32 addr; + u32 size; + u32 reserved; +} fw_chunk; + +int sec_ts_power(void *data, bool on); +int sec_ts_stop_device(struct sec_ts_data *ts); +int sec_ts_start_device(struct sec_ts_data *ts); +int sec_ts_set_lowpowermode(struct sec_ts_data *ts, u8 mode); +int sec_ts_firmware_update_on_probe(struct sec_ts_data *ts, bool force_update); +int sec_ts_firmware_update_on_hidden_menu(struct sec_ts_data *ts, int update_type); +int sec_ts_glove_mode_enables(struct sec_ts_data *ts, int mode); +int sec_ts_set_cover_type(struct sec_ts_data *ts, bool enable); +int sec_ts_wait_for_ready(struct sec_ts_data *ts, unsigned int ack); +int sec_ts_function(int (*func_init)(void *device_data), void (*func_remove)(void)); +int sec_ts_fn_init(struct sec_ts_data *ts); +int sec_ts_read_calibration_report(struct sec_ts_data *ts); +int sec_ts_execute_force_calibration(struct sec_ts_data *ts, int cal_mode); +int sec_ts_fix_tmode(struct sec_ts_data *ts, u8 mode, u8 state); +int sec_ts_release_tmode(struct sec_ts_data *ts); +int get_tsp_nvm_data(struct sec_ts_data *ts, u8 offset); +int get_tsp_nvm_data_by_size(struct sec_ts_data *ts, u8 offset, int length, u8 *data); +void set_tsp_nvm_data_clear(struct sec_ts_data *ts, u8 offset); +#ifdef SEC_TS_SUPPORT_CUSTOMLIB +int sec_ts_set_custom_library(struct sec_ts_data *ts); +int sec_ts_check_custom_library(struct sec_ts_data *ts); +#endif +void sec_ts_unlocked_release_all_finger(struct sec_ts_data *ts); +void sec_ts_locked_release_all_finger(struct sec_ts_data *ts); +void sec_ts_fn_remove(struct sec_ts_data *ts); +void sec_ts_delay(unsigned int ms); +int sec_ts_read_information(struct sec_ts_data *ts); +int execute_selftest(struct sec_ts_data *ts, bool save_result); + +void sec_ts_run_rawdata_all(struct sec_ts_data *ts, bool full_read); + +void sec_ts_reinit(struct sec_ts_data *ts); + +#if (1)//!defined(CONFIG_SAMSUNG_PRODUCT_SHIP) + +int sec_ts_raw_device_init(struct sec_ts_data *ts); +#endif + +extern bool tsp_init_done; + +extern struct sec_ts_data *ts_dup; + +#ifdef CONFIG_BATTERY_SAMSUNG +extern unsigned int lpcharge; +#endif + +extern void set_grip_data_to_ic(struct sec_ts_data *ts, u8 flag); +extern void sec_ts_set_grip_type(struct sec_ts_data *ts, u8 set_type); + + +#endif diff --git a/drivers/input/touchscreen/sec_mmi/sec_ts_fn.c b/drivers/input/touchscreen/sec_mmi/sec_ts_fn.c new file mode 100644 index 000000000000..99889fa06842 --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/sec_ts_fn.c @@ -0,0 +1,3829 @@ +/* drivers/input/touchscreen/sec_ts_fn.c + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * http://www.samsungsemi.com/ + * + * Core file for Samsung TSC driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "sec_ts.h" + +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 get_chip_vendor(void *device_data); +static void get_chip_name(void *device_data); +static void set_mis_cal_spec(void *device_data); +static void get_mis_cal_info(void *device_data); +static void get_wet_mode(void *device_data); +static void get_x_num(void *device_data); +static void get_y_num(void *device_data); +static void get_checksum_data(void *device_data); +static void run_reference_read(void *device_data); +static void run_reference_read_all(void *device_data); +static void get_reference(void *device_data); +static void run_rawcap_read(void *device_data); +static void run_rawcap_read_all(void *device_data); +static void get_rawcap(void *device_data); +static void run_delta_read(void *device_data); +static void run_delta_read_all(void *device_data); +static void get_delta(void *device_data); +static void run_raw_p2p_min_read_all(void *device_data); +static void run_raw_p2p_max_read_all(void *device_data); +static void run_self_reference_read(void *device_data); +static void run_self_reference_read_all(void *device_data); +static void run_self_rawcap_read(void *device_data); +static void run_self_rawcap_read_all(void *device_data); +static void run_self_delta_read(void *device_data); +static void run_self_delta_read_all(void *device_data); +static void run_rawdata_read_all(void *device_data); +static void run_force_calibration(void *device_data); +static void get_force_calibration(void *device_data); +static void get_gap_data_x_all(void *device_data); +static void get_gap_data_y_all(void *device_data); +static void run_trx_short_test(void *device_data); +static void glove_mode(void *device_data); +static void clear_cover_mode(void *device_data); +static void dead_zone_enable(void *device_data); +static void drawing_test_enable(void *device_data); +static void set_lowpower_mode(void *device_data); +static void set_wirelesscharger_mode(void *device_data); +static void spay_enable(void *device_data); +static void aod_enable(void *device_data); +static void set_grip_data(void *device_data); +static void dex_enable(void *device_data); +static void brush_enable(void *device_data); +static void set_touchable_area(void *device_data); +static void set_log_level(void *device_data); +static void debug(void *device_data); +static void not_support_cmd(void *device_data); + +static void sec_ts_print_frame(struct sec_ts_data *ts, short *min, short *max); + +static struct sec_cmd sec_cmds[] = { + {SEC_CMD("fw_update", fw_update),}, + {SEC_CMD("get_fw_ver_bin", get_fw_ver_bin),}, + {SEC_CMD("get_fw_ver_ic", get_fw_ver_ic),}, + {SEC_CMD("get_config_ver", get_config_ver),}, + + {SEC_CMD("get_threshold", get_threshold),}, + {SEC_CMD("module_off_master", module_off_master),}, + {SEC_CMD("module_on_master", module_on_master),}, + {SEC_CMD("get_chip_vendor", get_chip_vendor),}, + {SEC_CMD("get_chip_name", get_chip_name),}, + {SEC_CMD("set_mis_cal_spec", set_mis_cal_spec),}, + {SEC_CMD("get_mis_cal_info", get_mis_cal_info),}, + {SEC_CMD("get_wet_mode", get_wet_mode),}, + {SEC_CMD("get_x_num", get_x_num),}, + {SEC_CMD("get_y_num", get_y_num),}, + {SEC_CMD("get_checksum_data", get_checksum_data),}, + {SEC_CMD("run_reference_read", run_reference_read),}, + {SEC_CMD("run_reference_read_all", run_reference_read_all),}, + {SEC_CMD("get_reference", get_reference),}, + {SEC_CMD("run_rawcap_read", run_rawcap_read),}, + {SEC_CMD("run_rawcap_read_all", run_rawcap_read_all),}, + {SEC_CMD("get_rawcap", get_rawcap),}, + {SEC_CMD("run_delta_read", run_delta_read),}, + {SEC_CMD("run_delta_read_all", run_delta_read_all),}, + {SEC_CMD("get_delta", get_delta),}, + {SEC_CMD("run_raw_p2p_min_read_all", run_raw_p2p_min_read_all),}, + {SEC_CMD("run_raw_p2p_max_read_all", run_raw_p2p_max_read_all),}, + {SEC_CMD("run_self_reference_read", run_self_reference_read),}, + {SEC_CMD("run_self_reference_read_all", run_self_reference_read_all),}, + {SEC_CMD("run_self_rawcap_read", run_self_rawcap_read),}, + {SEC_CMD("run_self_rawcap_read_all", run_self_rawcap_read_all),}, + {SEC_CMD("run_self_delta_read", run_self_delta_read),}, + {SEC_CMD("run_self_delta_read_all", run_self_delta_read_all),}, + {SEC_CMD("run_rawdata_read_all", run_rawdata_read_all),}, + {SEC_CMD("run_force_calibration", run_force_calibration),}, + {SEC_CMD("get_force_calibration", get_force_calibration),}, + {SEC_CMD("get_gap_data_x_all", get_gap_data_x_all),}, + {SEC_CMD("get_gap_data_y_all", get_gap_data_y_all),}, + {SEC_CMD("run_trx_short_test", run_trx_short_test),}, + {SEC_CMD("glove_mode", glove_mode),}, + {SEC_CMD("clear_cover_mode", clear_cover_mode),}, + {SEC_CMD("dead_zone_enable", dead_zone_enable),}, + {SEC_CMD("drawing_test_enable", drawing_test_enable),}, + {SEC_CMD("set_lowpower_mode", set_lowpower_mode),}, + {SEC_CMD("set_wirelesscharger_mode", set_wirelesscharger_mode),}, + {SEC_CMD("spay_enable", spay_enable),}, + {SEC_CMD("aod_enable", aod_enable),}, + {SEC_CMD("set_grip_data", set_grip_data),}, + {SEC_CMD("dex_enable", dex_enable),}, + {SEC_CMD("brush_enable", brush_enable),}, + {SEC_CMD("set_touchable_area", set_touchable_area),}, + {SEC_CMD("set_log_level", set_log_level),}, + {SEC_CMD("debug", debug),}, + {SEC_CMD("not_support_cmd", not_support_cmd),}, +}; + +static ssize_t scrub_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[256] = { 0 }; + +#ifdef CONFIG_SAMSUNG_PRODUCT_SHIP + input_info(true, &ts->client->dev, + "%s: scrub_id: %d\n", __func__, ts->scrub_id); +#else + input_info(true, &ts->client->dev, + "%s: scrub_id: %d, X:%d, Y:%d\n", __func__, + ts->scrub_id, ts->scrub_x, ts->scrub_y); +#endif + snprintf(buff, sizeof(buff), "%d %d %d", ts->scrub_id, ts->scrub_x, ts->scrub_y); + + ts->scrub_x = 0; + ts->scrub_y = 0; + + return snprintf(buf, PAGE_SIZE, "%s", buff); +} + +static DEVICE_ATTR(scrub_pos, 0444, scrub_position_show, NULL); + +static ssize_t read_ito_check_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[256] = { 0 }; + + input_info(true, &ts->client->dev, "%s: %02X%02X%02X%02X\n", __func__, + ts->ito_test[0], ts->ito_test[1], + ts->ito_test[2], ts->ito_test[3]); + + snprintf(buff, sizeof(buff), "%02X%02X%02X%02X", + ts->ito_test[0], ts->ito_test[1], + ts->ito_test[2], ts->ito_test[3]); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s", buff); +} + +static ssize_t read_raw_check_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + int ii, ret = 0; + char *buffer = NULL; + char temp[CMD_RESULT_WORD_LEN] = { 0 }; + + buffer = vzalloc(ts->rx_count * ts->tx_count * 6); + if (!buffer) + return -ENOMEM; + + memset(buffer, 0x00, ts->rx_count * ts->tx_count * 6); + + for (ii = 0; ii < (ts->rx_count * ts->tx_count - 1); ii++) { + snprintf(temp, CMD_RESULT_WORD_LEN, "%d ", ts->pFrame[ii]); + strncat(buffer, temp, CMD_RESULT_WORD_LEN); + + memset(temp, 0x00, CMD_RESULT_WORD_LEN); + } + + snprintf(temp, CMD_RESULT_WORD_LEN, "%d", ts->pFrame[ii]); + strncat(buffer, temp, CMD_RESULT_WORD_LEN); + + ret = snprintf(buf, ts->rx_count * ts->tx_count * 6, buffer); + vfree(buffer); + + return ret; +} + +static ssize_t read_multi_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + input_info(true, &ts->client->dev, "%s: %d\n", __func__, + ts->multi_count); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", ts->multi_count); +} + +static ssize_t clear_multi_count_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + ts->multi_count = 0; + + input_info(true, &ts->client->dev, "%s: clear\n", __func__); + + return count; +} + +static ssize_t read_wet_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + input_info(true, &ts->client->dev, "%s: %d, %d\n", __func__, + ts->wet_count, ts->dive_count); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", ts->wet_count); +} + +static ssize_t clear_wet_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + ts->wet_count = 0; + ts->dive_count= 0; + + input_info(true, &ts->client->dev, "%s: clear\n", __func__); + + return count; +} + +static ssize_t read_noise_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + input_info(true, &ts->client->dev, "%s: %d\n", __func__, ts->noise_count); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", ts->noise_count); +} + +static ssize_t clear_noise_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + ts->noise_count = 0; + + input_info(true, &ts->client->dev, "%s: clear\n", __func__); + + return count; +} + +static ssize_t read_comm_err_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + input_info(true, &ts->client->dev, "%s: %d\n", __func__, + ts->multi_count); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", ts->comm_err_count); +} + +static ssize_t clear_comm_err_count_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + ts->comm_err_count = 0; + + input_info(true, &ts->client->dev, "%s: clear\n", __func__); + + return count; +} + +static ssize_t read_module_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[256] = { 0 }; + + snprintf(buff, sizeof(buff), "SE%02X%02X%02X", + ts->plat_data->panel_revision, ts->plat_data->img_version_of_bin[2], + ts->plat_data->img_version_of_bin[3]); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s", buff); +} + +static ssize_t read_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + unsigned char buffer[10] = { 0 }; + + snprintf(buffer, 5, ts->plat_data->firmware_name + 8); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "LSI_%s", buffer); +} + +static ssize_t clear_checksum_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + ts->checksum_result = 0; + + input_info(true, &ts->client->dev, "%s: clear\n", __func__); + + return count; +} + +static ssize_t read_checksum_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + input_info(true, &ts->client->dev, "%s: %d\n", __func__, + ts->checksum_result); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", ts->checksum_result); +} + +static ssize_t clear_holding_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + ts->time_longest = 0; + + input_info(true, &ts->client->dev, "%s: clear\n", __func__); + + return count; +} + +static ssize_t read_holding_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + input_info(true, &ts->client->dev, "%s: %ld\n", __func__, + ts->time_longest); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%ld", ts->time_longest); +} + +static ssize_t read_all_touch_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + input_info(true, &ts->client->dev, "%s: touch:%d, force:%d, aod:%d, spay:%d\n", __func__, + ts->all_finger_count, ts->all_force_count, + ts->all_aod_tap_count, ts->all_spay_count); + + return snprintf(buf, SEC_CMD_BUF_SIZE, + "\"TTCN\":\"%d\",\"TFCN\":\"%d\",\"TACN\":\"%d\",\"TSCN\":\"%d\"", + ts->all_finger_count, ts->all_force_count, + ts->all_aod_tap_count, ts->all_spay_count); +} + +static ssize_t clear_all_touch_count_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + ts->all_force_count = 0; + ts->all_aod_tap_count = 0; + ts->all_spay_count = 0; + + input_info(true, &ts->client->dev, "%s: clear\n", __func__); + + return count; +} + +static ssize_t read_z_value_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + input_info(true, &ts->client->dev, "%s: max:%d, min:%d, avg:%d\n", __func__, + ts->max_z_value, ts->min_z_value, + ts->sum_z_value); + + if (ts->all_finger_count) + return snprintf(buf, SEC_CMD_BUF_SIZE, + "\"TMXZ\":\"%d\",\"TMNZ\":\"%d\",\"TAVZ\":\"%d\"", + ts->max_z_value, ts->min_z_value, + ts->sum_z_value / ts->all_finger_count); + else + return snprintf(buf, SEC_CMD_BUF_SIZE, + "\"TMXZ\":\"%d\",\"TMNZ\":\"%d\"", + ts->max_z_value, ts->min_z_value); + +} + +static ssize_t clear_z_value_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + ts->max_z_value= 0; + ts->min_z_value= 0xFFFFFFFF; + ts->sum_z_value= 0; + ts->all_finger_count = 0; + + input_info(true, &ts->client->dev, "%s: clear\n", __func__); + + return count; +} + +static ssize_t get_force_recal_count(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + u8 rbuf[4] = {0, }; + u32 recal_count; + int ret; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Touch is stopped!\n", __func__); + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", -ENODEV); + } + + if (ts->reset_is_on_going) { + input_err(true, &ts->client->dev, "%s: Reset is ongoing!\n", __func__); + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", -EBUSY); + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_FORCE_RECAL_COUNT, rbuf, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: Failed to read\n", __func__); + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", -EIO); + } + + recal_count = (rbuf[0] & 0xFF) << 24 | (rbuf[1] & 0xFF) << 16 | + (rbuf[2] & 0xFF) << 8 | (rbuf[3] & 0xFF); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", recal_count); +} + +static ssize_t ic_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[512] = { 0 }; + char temp[128] = { 0 }; + u8 data[2] = { 0 }; + int ret; + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_SET_TOUCHFUNCTION, data, 2); + if (ret < 0) + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); + + snprintf(temp, sizeof(temp), "mutual,%d,", data[0] & 0x01 ? 1 : 0); + strncat(buff, temp, sizeof(temp)); + snprintf(temp, sizeof(temp), "hover,%d,", data[0] & 0x02 ? 1 : 0); + strncat(buff, temp, sizeof(temp)); + snprintf(temp, sizeof(temp), "cover,%d,", data[0] & 0x04 ? 1 : 0); + strncat(buff, temp, sizeof(temp)); + snprintf(temp, sizeof(temp), "glove,%d,", data[0] & 0x08 ? 1 : 0); + strncat(buff, temp, sizeof(temp)); + snprintf(temp, sizeof(temp), "stylus,%d,", data[0] & 0x10 ? 1 : 0); + strncat(buff, temp, sizeof(temp)); + snprintf(temp, sizeof(temp), "palm,%d,", data[0] & 0x20 ? 1 : 0); + strncat(buff, temp, sizeof(temp)); + snprintf(temp, sizeof(temp), "wet,%d,", data[0] & 0x40 ? 1 : 0); + strncat(buff, temp, sizeof(temp)); + snprintf(temp, sizeof(temp), "prox,%d,", data[0] & 0x80 ? 1 : 0); + strncat(buff, temp, sizeof(temp)); + + memset(data, 0x00, 2); + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_SET_POWER_MODE, data, 1); + if (ret < 0) + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); + + snprintf(temp, sizeof(temp), "npm,%d,", data[0] == 0 ? 1 : 0); + strncat(buff, temp, sizeof(temp)); + snprintf(temp, sizeof(temp), "lpm,%d,", data[0] == 1 ? 1 : 0); + strncat(buff, temp, sizeof(temp)); + snprintf(temp, sizeof(temp), "test,%d,", data[0] == 2 ? 1 : 0); + strncat(buff, temp, sizeof(temp)); + snprintf(temp, sizeof(temp), "flash,%d,", data[0] == 3 ? 1 : 0); + strncat(buff, temp, sizeof(temp)); + + memset(data, 0x00, 2); + ret = ts->sec_ts_i2c_read(ts, SET_TS_CMD_SET_CHARGER_MODE, data, 1); + if (ret < 0) + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); + + snprintf(temp, sizeof(temp), "no_charge,%d,", data[0] == 0 ? 1 : 0); + strncat(buff, temp, sizeof(temp)); + snprintf(temp, sizeof(temp), "wire_charge,%d,", data[0] == 1 ? 1 : 0); + strncat(buff, temp, sizeof(temp)); + snprintf(temp, sizeof(temp), "wireless_charge,%d,", data[0] == 2 ? 1 : 0); + strncat(buff, temp, sizeof(temp)); + + memset(data, 0x00, 2); + ret = ts->sec_ts_i2c_read(ts, SET_TS_CMD_SET_NOISE_MODE, data, 1); + if (ret < 0) + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); + + snprintf(temp, sizeof(temp), "noise,%d,", data[0]); + strncat(buff, temp, sizeof(temp)); + + memset(data, 0x00, 2); + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_SET_COVERTYPE, data, 1); + if (ret < 0) + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); + + snprintf(temp, sizeof(temp), "cover_type,%d,", data[0]); + strncat(buff, temp, sizeof(temp)); + + data[0] = 0; + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_SET_DEX_MODE, data, 1); + if (ret < 0) + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); + + snprintf(temp, sizeof(temp), "dex,%d,", data[0]); + strncat(buff, temp, sizeof(temp)); + + data[0] = 0; + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_SET_BRUSH_MODE, data, 1); + if (ret < 0) + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); + + snprintf(temp, sizeof(temp), "artcanvas,%d,", data[0]); + strncat(buff, temp, sizeof(temp)); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); +} + +static DEVICE_ATTR(ito_check, 0444, read_ito_check_show, NULL); +static DEVICE_ATTR(raw_check, 0444, read_raw_check_show, NULL); +static DEVICE_ATTR(multi_count, 0664, read_multi_count_show, clear_multi_count_store); +static DEVICE_ATTR(wet_mode, 0664, read_wet_mode_show, clear_wet_mode_store); +static DEVICE_ATTR(noise_mode, 0664, read_noise_mode_show, clear_noise_mode_store); +static DEVICE_ATTR(comm_err_count, 0664, read_comm_err_count_show, clear_comm_err_count_store); +static DEVICE_ATTR(checksum, 0664, read_checksum_show, clear_checksum_store); +static DEVICE_ATTR(holding_time, 0664, read_holding_time_show, clear_holding_time_store); +static DEVICE_ATTR(all_touch_count, 0664, read_all_touch_count_show, clear_all_touch_count_store); +static DEVICE_ATTR(z_value, 0664, read_z_value_show, clear_z_value_store); +static DEVICE_ATTR(module_id, 0444, read_module_id_show, NULL); +static DEVICE_ATTR(vendor, 0444, read_vendor_show, NULL); +static DEVICE_ATTR(force_recal_count, 0444, get_force_recal_count, NULL); +static DEVICE_ATTR(status, 0444, ic_status_show, NULL); + +static struct attribute *cmd_attributes[] = { + &dev_attr_scrub_pos.attr, + &dev_attr_ito_check.attr, + &dev_attr_raw_check.attr, + &dev_attr_multi_count.attr, + &dev_attr_wet_mode.attr, + &dev_attr_noise_mode.attr, + &dev_attr_comm_err_count.attr, + &dev_attr_checksum.attr, + &dev_attr_holding_time.attr, + &dev_attr_all_touch_count.attr, + &dev_attr_z_value.attr, + &dev_attr_module_id.attr, + &dev_attr_vendor.attr, + &dev_attr_force_recal_count.attr, + &dev_attr_status.attr, + NULL, +}; + +static struct attribute_group cmd_attr_group = { + .attrs = cmd_attributes, +}; + +static int sec_ts_check_index(struct sec_ts_data *ts) +{ + struct sec_cmd_data *sec = &ts->sec; + char buff[SEC_CMD_STR_LEN] = { 0 }; + int node; + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > ts->tx_count + || sec->cmd_param[1] < 0 || sec->cmd_param[1] > ts->rx_count) { + + snprintf(buff, sizeof(buff), "%s", "NG"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + input_info(true, &ts->client->dev, "%s: parameter error: %u, %u\n", + __func__, sec->cmd_param[0], sec->cmd_param[0]); + node = -1; + return node; + } + node = sec->cmd_param[1] * ts->tx_count + sec->cmd_param[0]; + input_info(true, &ts->client->dev, "%s: node = %d\n", __func__, node); + return node; +} +static void fw_update(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[64] = { 0 }; + int retval = 0; + + sec_cmd_set_default_result(sec); + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", + __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + return; + } + + retval = sec_ts_firmware_update_on_hidden_menu(ts, sec->cmd_param[0]); + if (retval < 0) { + snprintf(buff, sizeof(buff), "%s", "NA"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + input_err(true, &ts->client->dev, "%s: failed [%d]\n", __func__, retval); + } else { + snprintf(buff, sizeof(buff), "%s", "OK"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: success [%d]\n", __func__, retval); + } +} + +int sec_ts_fix_tmode(struct sec_ts_data *ts, u8 mode, u8 state) +{ + int ret; + u8 onoff[1] = {STATE_MANAGE_OFF}; + u8 tBuff[2] = { mode, state }; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_STATEMANAGE_ON, onoff, 1); + sec_ts_delay(20); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CHG_SYSMODE, tBuff, sizeof(tBuff)); + sec_ts_delay(20); + + return ret; +} + +int sec_ts_p2p_tmode(struct sec_ts_data *ts) +{ + int ret; + u8 mode[1] = {0x0C}; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CLEAR_EVENT_STACK, NULL, 0); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_P2P_MODE, mode, sizeof(mode)); + sec_ts_delay(30); + + return ret; +} + +static int execute_p2ptest(struct sec_ts_data *ts) +{ + int ret; + u8 test[2] = {0x00, 0x64}; + u8 tBuff[10] = {0}; + int retry = 0; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_P2P_TEST, test, sizeof(test)); + sec_ts_delay(2000); + + ret = -1; + + while (ts->sec_ts_i2c_read(ts, SEC_TS_READ_ONE_EVENT, tBuff, 8)) { + if (((tBuff[0] >> 2) & 0xF) == TYPE_STATUS_EVENT_VENDOR_INFO) { + if (tBuff[1] == SEC_TS_VENDOR_ACK_CMR_TEST_DONE) { + ts->cm_raw_set_p2p_max = (tBuff[2] & 0xFF) << 8 | (tBuff[3] & 0xFF); + ts->cm_raw_set_p2p_min = (tBuff[4] & 0xFF) << 8 | (tBuff[5] & 0xFF); + ts->cm_raw_set_p2p_diff = (tBuff[7] & 0xC0) << 2 | (tBuff[6] & 0xFF); + } + } + + if ((tBuff[7] & 0x3F) == 0x00) { + input_info(true, &ts->client->dev, "%s: left event is 0\n", __func__); + ret = 0; + break; + } + + if (retry++ > SEC_TS_WAIT_RETRY_CNT) { + input_err(true, &ts->client->dev, "%s: Time Over\n", __func__); + break; + } + sec_ts_delay(20); + } + return ret; +} + +int sec_ts_release_tmode(struct sec_ts_data *ts) +{ + int ret; + u8 onoff[1] = {STATE_MANAGE_ON}; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_STATEMANAGE_ON, onoff, 1); + sec_ts_delay(20); + + return ret; +} + +static void sec_ts_print_frame(struct sec_ts_data *ts, short *min, short *max) +{ + int i = 0; + int j = 0; + unsigned char *pStr = NULL; + unsigned char pTmp[16] = { 0 }; + int lsize = 7 * (ts->tx_count + 1); + + input_raw_info(true, &ts->client->dev, "%s\n", __func__); + + pStr = kzalloc(lsize, GFP_KERNEL); + if (pStr == NULL) + return; + + memset(pStr, 0x0, lsize); + snprintf(pTmp, sizeof(pTmp), " TX"); + strncat(pStr, pTmp, lsize); + + for (i = 0; i < ts->tx_count; i++) { + snprintf(pTmp, sizeof(pTmp), " %02d ", i); + strncat(pStr, pTmp, lsize); + } + + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + memset(pStr, 0x0, lsize); + snprintf(pTmp, sizeof(pTmp), " +"); + strncat(pStr, pTmp, lsize); + + for (i = 0; i < ts->tx_count; i++) { + snprintf(pTmp, sizeof(pTmp), "----"); + strncat(pStr, pTmp, lsize); + } + + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + + for (i = 0; i < ts->rx_count; i++) { + memset(pStr, 0x0, lsize); + snprintf(pTmp, sizeof(pTmp), "Rx%02d | ", i); + strncat(pStr, pTmp, lsize); + + for (j = 0; j < ts->tx_count; j++) { + snprintf(pTmp, sizeof(pTmp), " %3d", ts->pFrame[(j * ts->rx_count) + i]); + + if (ts->pFrame[(j * ts->rx_count) + i] < *min) + *min = ts->pFrame[(j * ts->rx_count) + i]; + + if (ts->pFrame[(j * ts->rx_count) + i] > *max) + *max = ts->pFrame[(j * ts->rx_count) + i]; + + strncat(pStr, pTmp, lsize); + } + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + } + kfree(pStr); +} + +static int sec_ts_read_frame(struct sec_ts_data *ts, u8 type, short *min, + short *max, bool save_result) +{ + unsigned int readbytes = 0xFF; + unsigned char *pRead = NULL; + u8 mode = TYPE_INVALID_DATA; + int ret = 0; + int i = 0; + int j = 0; + short *temp = NULL; + + input_raw_info(true, &ts->client->dev, "%s: type %d\n", __func__, type); + + /* set data length, allocation buffer memory */ + readbytes = ts->rx_count * ts->tx_count * 2; + + pRead = kzalloc(readbytes, GFP_KERNEL); + if (!pRead) + return -ENOMEM; + + /* set OPCODE and data type */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_MUTU_RAW_TYPE, &type, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Set rawdata type failed\n", __func__); + goto ErrorExit; + } + + sec_ts_delay(50); + + if (type == TYPE_OFFSET_DATA_SDC) { + /* excute selftest for real cap offset data, because real cap data is not memory data in normal touch. */ + char para = TO_TOUCH_MODE; + + disable_irq(ts->client->irq); + + ret = execute_selftest(ts, save_result); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: execute_selftest failed\n", __func__); + enable_irq(ts->client->irq); + goto ErrorRelease; + } + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_POWER_MODE, ¶, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Set rawdata type failed\n", __func__); + enable_irq(ts->client->irq); + goto ErrorRelease; + } + + enable_irq(ts->client->irq); + } + + /* read data */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_TOUCH_RAWDATA, pRead, readbytes); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read rawdata failed!\n", __func__); + goto ErrorRelease; + } + + memset(ts->pFrame, 0x00, readbytes); + + for (i = 0; i < readbytes; i += 2) + ts->pFrame[i / 2] = pRead[i + 1] + (pRead[i] << 8); + + *min = *max = ts->pFrame[0]; + +#ifdef DEBUG_MSG + input_info(true, &ts->client->dev, "%s: 02X%02X%02X readbytes=%d\n", __func__, + pRead[0], pRead[1], pRead[2], readbytes); +#endif + sec_ts_print_frame(ts, min, max); + + temp = kzalloc(readbytes, GFP_KERNEL); + if (!temp) + goto ErrorRelease; + + memcpy(temp, ts->pFrame, ts->tx_count * ts->rx_count * 2); + memset(ts->pFrame, 0x00, ts->tx_count * ts->rx_count * 2); + + for (i = 0; i < ts->tx_count; i++) { + for (j = 0; j < ts->rx_count; j++) + ts->pFrame[(j * ts->tx_count) + i] = temp[(i * ts->rx_count) + j]; + } + + kfree(temp); + +ErrorRelease: + /* release data monitory (unprepare AFE data memory) */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_MUTU_RAW_TYPE, &mode, 1); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Set rawdata type failed\n", __func__); + +ErrorExit: + kfree(pRead); + + return ret; +} + +static void sec_ts_print_channel(struct sec_ts_data *ts) +{ + unsigned char *pStr = NULL; + unsigned char pTmp[16] = { 0 }; + int i = 0, j = 0, k = 0; + + if (!ts->tx_count) + return; + + pStr = vzalloc(7 * (ts->tx_count + 1)); + if (!pStr) + return; + + memset(pStr, 0x0, 7 * (ts->tx_count + 1)); + snprintf(pTmp, sizeof(pTmp), " TX"); + strncat(pStr, pTmp, 7 * ts->tx_count); + + for (k = 0; k < ts->tx_count; k++) { + snprintf(pTmp, sizeof(pTmp), " %02d", k); + strncat(pStr, pTmp, 7 * ts->tx_count); + } + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + + memset(pStr, 0x0, 7 * (ts->tx_count + 1)); + snprintf(pTmp, sizeof(pTmp), " +"); + strncat(pStr, pTmp, 7 * ts->tx_count); + + for (k = 0; k < ts->tx_count; k++) { + snprintf(pTmp, sizeof(pTmp), "------"); + strncat(pStr, pTmp, 7 * ts->tx_count); + } + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + + memset(pStr, 0x0, 7 * (ts->tx_count + 1)); + snprintf(pTmp, sizeof(pTmp), " | "); + strncat(pStr, pTmp, 7 * ts->tx_count); + + for (i = 0; i < (ts->tx_count + ts->rx_count) * 2; i += 2) { + if (j == ts->tx_count) { + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + input_raw_info(true, &ts->client->dev, "\n"); + memset(pStr, 0x0, 7 * (ts->tx_count + 1)); + snprintf(pTmp, sizeof(pTmp), " RX"); + strncat(pStr, pTmp, 7 * ts->tx_count); + + for (k = 0; k < ts->tx_count; k++) { + snprintf(pTmp, sizeof(pTmp), " %02d", k); + strncat(pStr, pTmp, 7 * ts->tx_count); + } + + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + + memset(pStr, 0x0, 7 * (ts->tx_count + 1)); + snprintf(pTmp, sizeof(pTmp), " +"); + strncat(pStr, pTmp, 7 * ts->tx_count); + + for (k = 0; k < ts->tx_count; k++) { + snprintf(pTmp, sizeof(pTmp), "------"); + strncat(pStr, pTmp, 7 * ts->tx_count); + } + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + + memset(pStr, 0x0, 7 * (ts->tx_count + 1)); + snprintf(pTmp, sizeof(pTmp), " | "); + strncat(pStr, pTmp, 7 * ts->tx_count); + } else if (j && !(j % ts->tx_count)) { + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + memset(pStr, 0x0, 7 * (ts->tx_count + 1)); + snprintf(pTmp, sizeof(pTmp), " | "); + strncat(pStr, pTmp, 7 * ts->tx_count); + } + + snprintf(pTmp, sizeof(pTmp), " %5d", ts->pFrame[j]); + strncat(pStr, pTmp, 7 * ts->tx_count); + + j++; + } + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + vfree(pStr); +} + +static int sec_ts_read_channel(struct sec_ts_data *ts, u8 type, + short *min, short *max, bool save_result) +{ + unsigned char *pRead = NULL; + u8 mode = TYPE_INVALID_DATA; + int ret = 0; + int ii = 0; + int jj = 0; + unsigned int data_length = (ts->tx_count + ts->rx_count) * 2; + u8 w_data; + + input_raw_info(true, &ts->client->dev, "%s: type %d\n", __func__, type); + + pRead = kzalloc(data_length, GFP_KERNEL); + if (!pRead) + return -ENOMEM; + + /* set OPCODE and data type */ + w_data = type; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SELF_RAW_TYPE, &w_data, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Set rawdata type failed\n", __func__); + goto out_read_channel; + } + + sec_ts_delay(50); + + if (type == TYPE_OFFSET_DATA_SDC) { + /* execute selftest for real cap offset data, + * because real cap data is not memory data in normal touch. + */ + char para = TO_TOUCH_MODE; + disable_irq(ts->client->irq); + ret = execute_selftest(ts, save_result); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: execute_selftest failed!\n", __func__); + enable_irq(ts->client->irq); + goto err_read_data; + } + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_POWER_MODE, ¶, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: set rawdata type failed!\n", __func__); + enable_irq(ts->client->irq); + goto err_read_data; + } + enable_irq(ts->client->irq); + /* end */ + } + /* read data */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_TOUCH_SELF_RAWDATA, pRead, data_length); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read rawdata failed!\n", __func__); + goto err_read_data; + } + + /* clear all pFrame data */ + memset(ts->pFrame, 0x00, data_length); + + for (ii = 0; ii < data_length; ii += 2) { + ts->pFrame[jj] = ((pRead[ii] << 8) | pRead[ii + 1]); + + if (ii == 0) + *min = *max = ts->pFrame[jj]; + + *min = min(*min, ts->pFrame[jj]); + *max = max(*max, ts->pFrame[jj]); + + jj++; + } + + sec_ts_print_channel(ts); + +err_read_data: + /* release data monitory (unprepare AFE data memory) */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SELF_RAW_TYPE, &mode, 1); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Set rawdata type failed\n", __func__); + +out_read_channel: + kfree(pRead); + + return ret; +} + +static void get_gap_data_x_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char *buff = NULL; + int ii; + int node_gap = 0; + char temp[SEC_CMD_STR_LEN] = { 0 }; + + sec_cmd_set_default_result(sec); + + buff = kzalloc(ts->tx_count * ts->rx_count * CMD_RESULT_WORD_LEN, GFP_KERNEL); + if (!buff) + return; + + for (ii = 0; ii < (ts->rx_count * ts->tx_count); ii++) { + if ((ii + 1) % (ts->tx_count) != 0) { + if (ts->pFrame[ii] > ts->pFrame[ii + 1]) + node_gap = 100 - (ts->pFrame[ii + 1] * 100 / ts->pFrame[ii]); + else + node_gap = 100 - (ts->pFrame[ii] * 100 / ts->pFrame[ii + 1]); + + snprintf(temp, CMD_RESULT_WORD_LEN, "%d,", node_gap); + strncat(buff, temp, CMD_RESULT_WORD_LEN); + memset(temp, 0x00, SEC_CMD_STR_LEN); + } + } + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, ts->tx_count * ts->rx_count * CMD_RESULT_WORD_LEN)); + sec->cmd_state = SEC_CMD_STATUS_OK; + kfree(buff); +} + +static void get_gap_data_y_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char *buff = NULL; + int ii; + int node_gap = 0; + char temp[SEC_CMD_STR_LEN] = { 0 }; + + sec_cmd_set_default_result(sec); + + buff = kzalloc(ts->tx_count * ts->rx_count * CMD_RESULT_WORD_LEN, GFP_KERNEL); + if (!buff) + return; + + for (ii = 0; ii < (ts->rx_count * ts->tx_count); ii++) { + if (ii < (ts->rx_count - 1) * ts->tx_count) { + if (ts->pFrame[ii] > ts->pFrame[ii + ts->tx_count]) + node_gap = 100 - (ts->pFrame[ii + ts->tx_count] * 100 / ts->pFrame[ii]); + else + node_gap = 100 - (ts->pFrame[ii] * 100 / ts->pFrame[ii + ts->tx_count]); + + snprintf(temp, CMD_RESULT_WORD_LEN, "%d,", node_gap); + strncat(buff, temp, CMD_RESULT_WORD_LEN); + memset(temp, 0x00, SEC_CMD_STR_LEN); + } + } + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, ts->tx_count * ts->rx_count * CMD_RESULT_WORD_LEN)); + sec->cmd_state = SEC_CMD_STATUS_OK; + kfree(buff); +} + +static int sec_ts_read_raw_data(struct sec_ts_data *ts, + struct sec_cmd_data *sec, struct sec_ts_test_mode *mode) +{ + int ii; + int ret = 0; + char temp[SEC_CMD_STR_LEN] = { 0 }; + char *buff; + + buff = kzalloc(ts->tx_count * ts->rx_count * CMD_RESULT_WORD_LEN, GFP_KERNEL); + if (!buff) + goto error_alloc_mem; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", + __func__); + goto error_power_state; + } + + input_raw_info(true, &ts->client->dev, "%s: %d, %s\n", + __func__, mode->type, mode->allnode ? "ALL" : ""); + + ret = sec_ts_fix_tmode(ts, TOUCH_SYSTEM_MODE_TOUCH, TOUCH_MODE_STATE_TOUCH); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to fix tmode\n", + __func__); + goto error_test_fail; + } + + if (mode->frame_channel) + ret = sec_ts_read_channel(ts, mode->type, &mode->min, &mode->max, true); + else + ret = sec_ts_read_frame(ts, mode->type, &mode->min, &mode->max, true); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to read frame\n", + __func__); + goto error_test_fail; + } + + if (mode->allnode) { + if (mode->frame_channel) { + for (ii = 0; ii < (ts->rx_count + ts->tx_count); ii++) { + snprintf(temp, CMD_RESULT_WORD_LEN, "%d,", ts->pFrame[ii]); + strncat(buff, temp, CMD_RESULT_WORD_LEN); + + memset(temp, 0x00, SEC_CMD_STR_LEN); + } + } else { + for (ii = 0; ii < (ts->rx_count * ts->tx_count); ii++) { + snprintf(temp, CMD_RESULT_WORD_LEN, "%d,", ts->pFrame[ii]); + strncat(buff, temp, CMD_RESULT_WORD_LEN); + + memset(temp, 0x00, SEC_CMD_STR_LEN); + } + } + } else { + snprintf(buff, SEC_CMD_STR_LEN, "%d,%d", mode->min, mode->max); + } + + ret = sec_ts_release_tmode(ts); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to release tmode\n", + __func__); + goto error_test_fail; + } + + if (!sec) + goto out_rawdata; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, ts->tx_count * ts->rx_count * CMD_RESULT_WORD_LEN)); + sec->cmd_state = SEC_CMD_STATUS_OK; + +out_rawdata: + kfree(buff); + + sec_ts_locked_release_all_finger(ts); + + return ret; + +error_test_fail: +error_power_state: + kfree(buff); +error_alloc_mem: + if (!sec) + return ret; + + snprintf(temp, SEC_CMD_STR_LEN, "FAIL"); + sec_cmd_set_cmd_result(sec, temp, SEC_CMD_STR_LEN); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + + sec_ts_locked_release_all_finger(ts); + + return ret; +} + +static int sec_ts_read_rawp2p_data(struct sec_ts_data *ts, + struct sec_cmd_data *sec, struct sec_ts_test_mode *mode) +{ + int ii; + int ret = 0; + char temp[SEC_CMD_STR_LEN] = { 0 }; + char *buff; + char para = TO_TOUCH_MODE; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", + __func__); + goto error_power_state; + } + + buff = kzalloc(ts->tx_count * ts->rx_count * CMD_RESULT_WORD_LEN, GFP_KERNEL); + if (!buff) + goto error_alloc_mem; + + input_info(true, &ts->client->dev, "%s: %d, %s\n", + __func__, mode->type, mode->allnode ? "ALL" : ""); + + disable_irq(ts->client->irq); + + ret = sec_ts_p2p_tmode(ts); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to fix p2p tmode\n", + __func__); + goto error_tmode_fail; + } + + ret = execute_p2ptest(ts); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to fix p2p test\n", + __func__); + goto error_test_fail; + } + + if (mode->frame_channel) + ret = sec_ts_read_channel(ts, mode->type, &mode->min, &mode->max, true); + else + ret = sec_ts_read_frame(ts, mode->type, &mode->min, &mode->max, true); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to read frame\n", + __func__); + goto error_test_fail; + } + + if (mode->allnode) { + if (mode->frame_channel) { + for (ii = 0; ii < (ts->rx_count + ts->tx_count); ii++) { + snprintf(temp, CMD_RESULT_WORD_LEN, "%d,", ts->pFrame[ii]); + strncat(buff, temp, CMD_RESULT_WORD_LEN); + + memset(temp, 0x00, SEC_CMD_STR_LEN); + } + } else { + for (ii = 0; ii < (ts->rx_count * ts->tx_count); ii++) { + snprintf(temp, CMD_RESULT_WORD_LEN, "%d,", ts->pFrame[ii]); + strncat(buff, temp, CMD_RESULT_WORD_LEN); + + memset(temp, 0x00, SEC_CMD_STR_LEN); + } + } + } else { + snprintf(buff, SEC_CMD_STR_LEN, "%d,%d", mode->min, mode->max); + } + + if (!sec) + goto out_rawdata; + + sec_ts_locked_release_all_finger(ts); + sec_ts_delay(30); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_POWER_MODE, ¶, 1); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: set rawdata type failed!\n", __func__); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, ts->tx_count * ts->rx_count * CMD_RESULT_WORD_LEN)); + sec->cmd_state = SEC_CMD_STATUS_OK; + +out_rawdata: + enable_irq(ts->client->irq); + kfree(buff); + + return ret; +error_test_fail: +error_tmode_fail: + kfree(buff); + enable_irq(ts->client->irq); +error_alloc_mem: +error_power_state: + if (!sec) + return ret; + + snprintf(temp, SEC_CMD_STR_LEN, "FAIL"); + sec_cmd_set_cmd_result(sec, temp, SEC_CMD_STR_LEN); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + + return ret; +} + + +static void get_fw_ver_bin(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + + sec_cmd_set_default_result(sec); + + snprintf(buff, sizeof(buff), "SE%02X%02X%02X", + ts->plat_data->panel_revision, ts->plat_data->img_version_of_bin[2], + ts->plat_data->img_version_of_bin[3]); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void get_fw_ver_ic(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + int ret; + u8 fw_ver[4]; + + sec_cmd_set_default_result(sec); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", + __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + return; + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_IMG_VERSION, fw_ver, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: firmware version read error\n", __func__); + snprintf(buff, sizeof(buff), "%s", "NG"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + return; + } + + snprintf(buff, sizeof(buff), "SE%02X%02X%02X", + ts->plat_data->panel_revision, fw_ver[2], fw_ver[3]); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void get_config_ver(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[22] = { 0 }; + + sec_cmd_set_default_result(sec); + + snprintf(buff, sizeof(buff), "%s_SE_%02X%02X", + ts->plat_data->model_name, + ts->plat_data->config_version_of_ic[2], ts->plat_data->config_version_of_ic[3]); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void get_threshold(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[20] = { 0 }; + char threshold[2] = { 0 }; + int ret; + + sec_cmd_set_default_result(sec); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + goto err; + } + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_TOUCH_MODE_FOR_THRESHOLD, threshold, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: threshold write type failed. ret: %d\n", __func__, ret); + snprintf(buff, sizeof(buff), "%s", "NG"); + goto err; + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_TOUCH_THRESHOLD, threshold, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read threshold fail!\n", __func__); + snprintf(buff, sizeof(buff), "%s", "NG"); + goto err; + } + + input_info(true, &ts->client->dev, "0x%02X, 0x%02X\n", + threshold[0], threshold[1]); + + snprintf(buff, sizeof(buff), "%d", (threshold[0] << 8) | threshold[1]); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + + return; +err: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + return; +} + +static void module_off_master(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[3] = { 0 }; + int ret = 0; + + ret = sec_ts_stop_device(ts); + + if (ret == 0) + snprintf(buff, sizeof(buff), "%s", "OK"); + else + snprintf(buff, sizeof(buff), "%s", "NG"); + + sec_cmd_set_default_result(sec); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + if (strncmp(buff, "OK", 2) == 0) + sec->cmd_state = SEC_CMD_STATUS_OK; + else + sec->cmd_state = SEC_CMD_STATUS_FAIL; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void module_on_master(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[3] = { 0 }; + int ret = 0; + + ret = sec_ts_start_device(ts); + + if (ts->input_dev->disabled) { + sec_ts_set_lowpowermode(ts, TO_LOWPOWER_MODE); + ts->power_status = SEC_TS_STATE_LPM; + } + + if (ret == 0) + snprintf(buff, sizeof(buff), "%s", "OK"); + else + snprintf(buff, sizeof(buff), "%s", "NG"); + + sec_cmd_set_default_result(sec); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + if (strncmp(buff, "OK", 2) == 0) + sec->cmd_state = SEC_CMD_STATUS_OK; + else + sec->cmd_state = SEC_CMD_STATUS_FAIL; + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void get_chip_vendor(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + + strncpy(buff, "SEC", sizeof(buff)); + sec_cmd_set_default_result(sec); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void get_chip_name(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + + if (ts->plat_data->img_version_of_ic[0] == 0x02) + strncpy(buff, "MC44", sizeof(buff)); + else if (ts->plat_data->img_version_of_ic[0] == 0x05) + strncpy(buff, "A552", sizeof(buff)); + else if (ts->plat_data->img_version_of_ic[0] == 0x09) + strncpy(buff, "Y661", sizeof(buff)); + else if (ts->plat_data->img_version_of_ic[0] == 0x10) + strncpy(buff, "Y761", sizeof(buff)); + else if (ts->plat_data->img_version_of_ic[0] == 0x17) + strncpy(buff, "Y771", sizeof(buff)); + else + strncpy(buff, "N/A", sizeof(buff)); + + sec_cmd_set_default_result(sec); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void set_mis_cal_spec(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + char wreg[5] = { 0 }; + int ret; + + sec_cmd_set_default_result(sec); + + if (ts->plat_data->mis_cal_check == 0) { + input_err(true, &ts->client->dev, "%s: [ERROR] not support, %d\n", __func__); + goto NG; + } else if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", __func__); + goto NG; + } else { + if ((sec->cmd_param[0] < 0 || sec->cmd_param[0] > 255) || + (sec->cmd_param[1] < 0 || sec->cmd_param[1] > 255) || + (sec->cmd_param[2] < 0 || sec->cmd_param[2] > 255)) { + snprintf(buff, sizeof(buff), "%s", "NG"); + goto NG; + } else { + wreg[0] = sec->cmd_param[0]; + wreg[1] = sec->cmd_param[1]; + wreg[2] = sec->cmd_param[2]; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_MIS_CAL_SPEC, wreg, 3); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Misscal spec write failed. ret: %d\n", __func__, ret); + goto NG; + } else { + input_info(true, &ts->client->dev, "%s: tx gap=%d, rx gap=%d, peak=%d\n", + __func__, wreg[0], wreg[1], wreg[2]); + sec_ts_delay(20); + } + } + } + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + return; + +NG: + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + return; +} + +/* + * ## Mis Cal result ## + * FF : initial value in Firmware. + * FD : Cal fail case + * F4 : i2c fail case (5F) + * F3 : i2c fail case (5E) + * F2 : power off state + * F1 : not support mis cal concept + * F0 : initial value in fucntion + * 08 : Ambient Ambient condition check(PEAK) result 0 (PASS), 1(FAIL) + * 04 : Ambient Ambient condition check(DIFF MAX TX) result 0 (PASS), 1(FAIL) + * 02 : Ambient Ambient condition check(DIFF MAX RX) result 0 (PASS), 1(FAIL) + * 01 : Wet Wet mode result 0 (PASS), 1(FAIL) + * 00 : Pass + */ +static void get_mis_cal_info(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + char mis_cal_data = 0xF0; + char wreg[5] = { 0 }; + int ret; + char buff_all[16] = { 0 }; + + sec_cmd_set_default_result(sec); + + if (ts->plat_data->mis_cal_check == 0) { + input_err(true, &ts->client->dev, "%s: [ERROR] not support, %d\n", __func__); + mis_cal_data = 0xF1; + goto NG; + } else if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", __func__); + mis_cal_data = 0xF2; + goto NG; + } else { + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_MIS_CAL_READ, &mis_cal_data, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: i2c fail!, %d\n", __func__, ret); + mis_cal_data = 0xF3; + goto NG; + } else { + input_info(true, &ts->client->dev, "%s: miss cal data : %d\n", __func__, mis_cal_data); + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_MIS_CAL_SPEC, wreg, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: i2c fail!, %d\n", __func__, ret); + mis_cal_data = 0xF4; + goto NG; + } else { + input_info(true, &ts->client->dev, "%s: miss cal spec : %d,%d,%d,%d\n", + __func__, wreg[0], wreg[1], wreg[2], wreg[3]); + } + } + + snprintf(buff, sizeof(buff), "%d", mis_cal_data); + snprintf(buff_all, sizeof(buff_all), "%d,%d,%d,%d,%d", mis_cal_data, wreg[0], wreg[1], wreg[2], wreg[3]); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff_all); + return; + +NG: + snprintf(buff, sizeof(buff), "%d", mis_cal_data); + snprintf(buff_all, sizeof(buff_all), "%d,%d,%d,%d", mis_cal_data, 0, 0, 0); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff_all); +} + +static void get_wet_mode(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + char wet_mode_info = 0; + int ret; + + sec_cmd_set_default_result(sec); + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_WET_MODE, &wet_mode_info, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: i2c fail!, %d\n", __func__, ret); + goto NG; + } + + snprintf(buff, sizeof(buff), "%d", wet_mode_info); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + return; + +NG: + snprintf(buff, sizeof(buff), "NG"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + +} + +static void get_x_num(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + + sec_cmd_set_default_result(sec); + snprintf(buff, sizeof(buff), "%d", ts->tx_count); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void get_y_num(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + + sec_cmd_set_default_result(sec); + snprintf(buff, sizeof(buff), "%d", ts->rx_count); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static u32 checksum_sum(struct sec_ts_data *ts, u8 *chunk_data, u32 size) +{ + u32 data_32 = 0; + u32 checksum = 0; + u8 *fd = chunk_data; + int i; + + for (i = 0; i < size/4; i++) { + data_32 = (((fd[(i*4)+0]&0xFF)<<0) | ((fd[(i*4)+1]&0xFF)<<8) | + ((fd[(i*4)+2]&0xFF)<<16) | ((fd[(i*4)+3]&0xFF)<<24)); + checksum += data_32; + } + + checksum &= 0xFFFFFFFF; + + input_info(true, &ts->client->dev, "%s: checksum = [%x]\n", __func__, checksum); + + return checksum; +} + +static u32 get_bin_checksum(struct sec_ts_data *ts, const u8 *data, size_t size) +{ + + int i; + fw_header *fw_hd; + fw_chunk *fw_ch; + u8 *fd = (u8 *)data; + u32 data_32; + u32 checksum = 0; + u32 chunk_checksum[3]; + u32 img_checksum; + + fw_hd = (fw_header *)fd; + fd += sizeof(fw_header); + + if (fw_hd->signature != SEC_TS_FW_HEADER_SIGN) { + input_err(true, &ts->client->dev, "%s: firmware header error = %08X\n", __func__, fw_hd->signature); + return 0; + } + + for (i = 0; i < (fw_hd->totalsize/4); i++) { + data_32 = (((data[(i*4)+0]&0xFF)<<0) | ((data[(i*4)+1]&0xFF)<<8) | + ((data[(i*4)+2]&0xFF)<<16) | ((data[(i*4)+3]&0xFF)<<24)); + checksum += data_32; + } + + input_info(true, &ts->client->dev, "%s: fw_hd->totalsize = [%d] checksum = [%x]\n", + __func__, fw_hd->totalsize, checksum); + + checksum &= 0xFFFFFFFF; + if (checksum != 0) { + input_err(true, &ts->client->dev, "%s: Checksum fail! = %08X\n", __func__, checksum); + return 0; + } + + for (i = 0; i < fw_hd->num_chunk; i++) { + fw_ch = (fw_chunk *)fd; + + input_info(true, &ts->client->dev, "%s: [%d] 0x%08X, 0x%08X, 0x%08X, 0x%08X\n", __func__, i, + fw_ch->signature, fw_ch->addr, fw_ch->size, fw_ch->reserved); + + if (fw_ch->signature != SEC_TS_FW_CHUNK_SIGN) { + input_err(true, &ts->client->dev, "%s: firmware chunk error = %08X\n", + __func__, fw_ch->signature); + return 0; + } + fd += sizeof(fw_chunk); + + checksum = checksum_sum(ts, fd, fw_ch->size); + chunk_checksum[i] = checksum; + fd += fw_ch->size; + } + + img_checksum = chunk_checksum[0] + chunk_checksum[1]; + + return img_checksum; +} + +static void get_checksum_data(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + char csum_result[4] = { 0 }; + char data[5] = { 0 }; + u8 cal_result; + u8 nv_result; + u8 temp; + u8 csum = 0; + int ret, i; + + u32 ic_checksum; + u32 img_checksum; + const struct firmware *fw_entry; + char fw_path[SEC_TS_MAX_FW_PATH]; + u8 fw_ver[4]; + + sec_cmd_set_default_result(sec); + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + goto err; + } + + disable_irq(ts->client->irq); + + ts->plat_data->power(ts, false); + ts->power_status = SEC_TS_STATE_POWER_OFF; + sec_ts_delay(50); + + ts->plat_data->power(ts, true); + ts->power_status = SEC_TS_STATE_POWER_ON; + sec_ts_delay(70); + + ret = sec_ts_wait_for_ready(ts, SEC_TS_ACK_BOOT_COMPLETE); + if (ret < 0) { + enable_irq(ts->client->irq); + input_err(true, &ts->client->dev, "%s: boot complete failed\n", __func__); + snprintf(buff, sizeof(buff), "%s", "NG"); + goto err; + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_FIRMWARE_INTEGRITY, &data[0], 1); + if (ret < 0 || (data[0] != 0x80)) { + enable_irq(ts->client->irq); + input_err(true, &ts->client->dev, "%s: firmware integrity failed, ret:%d, data:%X\n", + __func__, ret, data[0]); + snprintf(buff, sizeof(buff), "%s", "NG"); + goto err; + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_BOOT_STATUS, &data[1], 1); + if (ret < 0 || (data[1] != SEC_TS_STATUS_APP_MODE)) { + enable_irq(ts->client->irq); + input_err(true, &ts->client->dev, "%s: boot status failed, ret:%d, data:%X\n", __func__, ret, data[0]); + snprintf(buff, sizeof(buff), "%s", "NG"); + goto err; + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_TS_STATUS, &data[2], 4); + if (ret < 0 || (data[3] == TOUCH_SYSTEM_MODE_FLASH)) { + enable_irq(ts->client->irq); + input_err(true, &ts->client->dev, "%s: touch status failed, ret:%d, data:%X\n", __func__, ret, data[3]); + snprintf(buff, sizeof(buff), "%s", "NG"); + goto err; + } + + sec_ts_reinit(ts); + + enable_irq(ts->client->irq); + + input_err(true, &ts->client->dev, "%s: data[0]:%X, data[1]:%X, data[3]:%X\n", __func__, data[0], data[1], data[3]); + + temp = DO_FW_CHECKSUM | DO_PARA_CHECKSUM; + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_GET_CHECKSUM, &temp, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: send get_checksum_cmd fail!\n", __func__); + snprintf(buff, sizeof(buff), "%s", "SendCMDfail"); + goto err; + } + + sec_ts_delay(20); + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_GET_CHECKSUM, csum_result, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read get_checksum result fail!\n", __func__); + snprintf(buff, sizeof(buff), "%s", "ReadCSUMfail"); + goto err; + } + + nv_result = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_FAC_RESULT); + nv_result += get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_CAL_COUNT); + + cal_result = sec_ts_read_calibration_report(ts); + + for (i = 0; i < 4; i++) + csum += csum_result[i]; + + csum += nv_result; + csum += cal_result; + + csum = ~csum; + + if (ts->plat_data->firmware_name != NULL) { + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_IMG_VERSION, fw_ver, 4); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: firmware version read error\n", __func__); + + for (i = 0; i < 4; i++) { + if (ts->plat_data->img_version_of_bin[i] != fw_ver[i]) { + input_err(true, &ts->client->dev, "%s: do not matched version info [%d]:[%x]!=[%x] and skip!\n", + __func__, i, ts->plat_data->img_version_of_bin[i], fw_ver[i]); + goto out; + } + } + + ic_checksum = (((csum_result[0]&0xFF)<<24) | ((csum_result[1]&0xFF)<<16) | + ((csum_result[2]&0xFF)<<8) | ((csum_result[3]&0xFF)<<0)); + + snprintf(fw_path, SEC_TS_MAX_FW_PATH, "%s", ts->plat_data->firmware_name); + + if (request_firmware(&fw_entry, fw_path, &ts->client->dev) != 0) { + input_err(true, &ts->client->dev, "%s: firmware is not available\n", __func__); + snprintf(buff, sizeof(buff), "%s", "NG"); + goto err; + } + + img_checksum = get_bin_checksum(ts, fw_entry->data, fw_entry->size); + + release_firmware(fw_entry); + + input_info(true, &ts->client->dev, "%s: img_checksum=[0x%X], ic_checksum=[0x%X]\n", + __func__, img_checksum, ic_checksum); + + if (img_checksum != ic_checksum) { + input_err(true, &ts->client->dev, "%s: img_checksum=[0x%X] != ic_checksum=[0x%X]!!!\n", + __func__, img_checksum, ic_checksum); + snprintf(buff, sizeof(buff), "%s", "NG"); + goto err; + } + } +out: + + input_info(true, &ts->client->dev, "%s: checksum = %02X\n", __func__, csum); + snprintf(buff, sizeof(buff), "%02X", csum); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + return; + +err: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; +} + +static void run_reference_read(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SET; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void run_reference_read_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SET; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void get_reference(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + short val = 0; + int node = 0; + + sec_cmd_set_default_result(sec); + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", + __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + return; + } + + node = sec_ts_check_index(ts); + if (node < 0) + return; + + val = ts->pFrame[node]; + snprintf(buff, sizeof(buff), "%d", val); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void run_rawcap_read(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SDC; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void run_rawcap_read_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SDC; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void get_rawcap(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + short val = 0; + int node = 0; + + sec_cmd_set_default_result(sec); + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", + __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + return; + } + + node = sec_ts_check_index(ts); + if (node < 0) + return; + + val = ts->pFrame[node]; + snprintf(buff, sizeof(buff), "%d", val); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void run_delta_read(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_RAW_DATA; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void run_delta_read_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_RAW_DATA; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void get_delta(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + short val = 0; + int node = 0; + + sec_cmd_set_default_result(sec); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", + __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + return; + } + + node = sec_ts_check_index(ts); + if (node < 0) + return; + + val = ts->pFrame[node]; + snprintf(buff, sizeof(buff), "%d", val); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void run_raw_p2p_min_read_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_RAW_DATA_P2P_MIN; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_rawp2p_data(ts, sec, &mode); +} + +static void run_raw_p2p_max_read_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_RAW_DATA_P2P_MAX; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_rawp2p_data(ts, sec, &mode); +} + +/* self reference : send TX power in TX channel, receive in TX channel */ +static void run_self_reference_read(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SET; + mode.frame_channel = TEST_MODE_READ_CHANNEL; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void run_self_reference_read_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SET; + mode.frame_channel = TEST_MODE_READ_CHANNEL; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void run_self_rawcap_read(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SDC; + mode.frame_channel = TEST_MODE_READ_CHANNEL; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void run_self_rawcap_read_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SDC; + mode.frame_channel = TEST_MODE_READ_CHANNEL; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void run_self_delta_read(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_RAW_DATA; + mode.frame_channel = TEST_MODE_READ_CHANNEL; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void run_self_delta_read_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_RAW_DATA; + mode.frame_channel = TEST_MODE_READ_CHANNEL; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +/* + * sec_ts_run_rawdata_all : read all raw data + * + * when you want to read full raw data (full_read : true) + * "mutual/self 3, 5, 29, 1, 19" data will be saved in log + * + * otherwise, (full_read : false, especially on boot time) + * only "mutual 3, 5, 29" data will be saved in log + */ +void sec_ts_run_rawdata_all(struct sec_ts_data *ts, bool full_read) +{ + short min, max; + int ret, i, read_num; + u8 test_type[5] = {TYPE_AMBIENT_DATA, TYPE_DECODED_DATA, + TYPE_SIGNAL_DATA, TYPE_OFFSET_DATA_SET, TYPE_OFFSET_DATA_SDC}; + + ts->tsp_dump_lock = 1; + input_raw_data_clear(); + input_raw_info(true, &ts->client->dev, + "%s: start (noise:%d, wet:%d)##\n", + __func__, ts->touch_noise_status, ts->wet_mode); + + ret = sec_ts_fix_tmode(ts, TOUCH_SYSTEM_MODE_TOUCH, TOUCH_MODE_STATE_TOUCH); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to fix tmode\n", + __func__); + goto out; + } + + if (full_read) { + read_num = 5; + } else { + read_num = 3; + test_type[read_num - 1] = TYPE_OFFSET_DATA_SDC; + } + + for (i = 0; i < read_num; i++) { + ret = sec_ts_read_frame(ts, test_type[i], &min, &max, false); + if (ret < 0) { + input_raw_info(true, &ts->client->dev, + "%s: mutual %d : error ## ret:%d\n", + __func__, test_type[i], ret); + goto out; + } else { + input_raw_info(true, &ts->client->dev, + "%s: mutual %d : Max/Min %d,%d ##\n", + __func__, test_type[i], max, min); + } + sec_ts_delay(20); + + if (full_read) { + ret = sec_ts_read_channel(ts, test_type[i], &min, &max, false); + if (ret < 0) { + input_raw_info(true, &ts->client->dev, + "%s: self %d : error ## ret:%d\n", + __func__, test_type[i], ret); + goto out; + } else { + input_raw_info(true, &ts->client->dev, + "%s: self %d : Max/Min %d,%d ##\n", + __func__, test_type[i], max, min); + } + sec_ts_delay(20); + } + } + + sec_ts_release_tmode(ts); + +out: + input_raw_info(true, &ts->client->dev, "%s: ito : %02X %02X %02X %02X\n", + __func__, ts->ito_test[0], ts->ito_test[1] + , ts->ito_test[2], ts->ito_test[3]); + + input_raw_info(true, &ts->client->dev, "%s: done (noise:%d, wet:%d)##\n", + __func__, ts->touch_noise_status, ts->wet_mode); + ts->tsp_dump_lock = 0; + + sec_ts_locked_release_all_finger(ts); +} + +static void run_rawdata_read_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + + sec_cmd_set_default_result(sec); + + if (ts->tsp_dump_lock == 1) { + input_err(true, &ts->client->dev, "%s: already checking now\n", __func__); + snprintf(buff, sizeof(buff), "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out; + } + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: IC is power off\n", __func__); + snprintf(buff, sizeof(buff), "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out; + } + + sec_ts_run_rawdata_all(ts, true); + + snprintf(buff, sizeof(buff), "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; +out: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +/* Use TSP NV area + * buff[0] : offset from user NVM storage + * buff[1] : length of stroed data - 1 (ex. using 1byte, value is 1 - 1 = 0) + * buff[2] : write data + * buff[..] : cont. + */ +void set_tsp_nvm_data_clear(struct sec_ts_data *ts, u8 offset) +{ + char buff[4] = { 0 }; + int ret; + + input_dbg(true, &ts->client->dev, "%s\n", __func__); + + buff[0] = offset; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_NVM, buff, 3); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: nvm write failed. ret: %d\n", __func__, ret); + + sec_ts_delay(20); +} + +int get_tsp_nvm_data(struct sec_ts_data *ts, u8 offset) +{ + char buff[2] = { 0 }; + int ret; + + /* SENSE OFF -> CELAR EVENT STACK -> READ NV -> SENSE ON */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_OFF, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail to write Sense_off\n", __func__); + goto out_nvm; + } + + input_dbg(false, &ts->client->dev, "%s: SENSE OFF\n", __func__); + + sec_ts_delay(100); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CLEAR_EVENT_STACK, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: i2c write clear event failed\n", __func__); + goto out_nvm; + } + + input_dbg(false, &ts->client->dev, "%s: CLEAR EVENT STACK\n", __func__); + + sec_ts_delay(100); + + sec_ts_locked_release_all_finger(ts); + + /* send NV data using command + * Use TSP NV area : in this model, use only one byte + * buff[0] : offset from user NVM storage + * buff[1] : length of stroed data - 1 (ex. using 1byte, value is 1 - 1 = 0) + */ + memset(buff, 0x00, 2); + buff[0] = offset; + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_NVM, buff, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: nvm send command failed. ret: %d\n", __func__, ret); + goto out_nvm; + } + + sec_ts_delay(20); + + /* read NV data + * Use TSP NV area : in this model, use only one byte + */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_NVM, buff, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: nvm send command failed. ret: %d\n", __func__, ret); + goto out_nvm; + } + + input_info(true, &ts->client->dev, "%s: offset:%u data:%02X\n", __func__, offset, buff[0]); + +out_nvm: + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to write Sense_on\n", __func__); + + sec_ts_delay(300); + + input_dbg(false, &ts->client->dev, "%s: SENSE ON\n", __func__); + + return buff[0]; +} + +int get_tsp_nvm_data_by_size(struct sec_ts_data *ts, u8 offset, int length, u8 *data) +{ + char *buff = NULL; + int ret; + + buff = kzalloc(length, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + input_info(true, &ts->client->dev, "%s: offset:%u, length:%d, size:%d\n", __func__, offset, length, sizeof(data)); + + /* SENSE OFF -> CELAR EVENT STACK -> READ NV -> SENSE ON */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_OFF, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail to write Sense_off\n", __func__); + goto out_nvm; + } + + input_dbg(true, &ts->client->dev, "%s: SENSE OFF\n", __func__); + + sec_ts_delay(100); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CLEAR_EVENT_STACK, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: i2c write clear event failed\n", __func__); + goto out_nvm; + } + + input_dbg(true, &ts->client->dev, "%s: CLEAR EVENT STACK\n", __func__); + + sec_ts_delay(100); + + sec_ts_locked_release_all_finger(ts); + + /* send NV data using command + * Use TSP NV area : in this model, use only one byte + * buff[0] : offset from user NVM storage + * buff[1] : length of stroed data - 1 (ex. using 1byte, value is 1 - 1 = 0) + */ + memset(buff, 0x00, 2); + buff[0] = offset; + buff[1] = length - 1; + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_NVM, buff, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: nvm send command failed. ret: %d\n", __func__, ret); + goto out_nvm; + } + + sec_ts_delay(20); + + /* read NV data + * Use TSP NV area : in this model, use only one byte + */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_NVM, buff, length); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: nvm send command failed. ret: %d\n", __func__, ret); + goto out_nvm; + } + + memcpy(data, buff, length); + +out_nvm: + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to write Sense_on\n", __func__); + + sec_ts_delay(300); + + input_dbg(true, &ts->client->dev, "%s: SENSE ON\n", __func__); + + kfree(buff); + + return ret; +} + +#define GLOVE_MODE_EN (1 << 0) +#define CLEAR_COVER_EN (1 << 1) +#define FAST_GLOVE_MODE_EN (1 << 2) + +static void glove_mode(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + int glove_mode_enables = 0; + + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { + snprintf(buff, sizeof(buff), "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + int retval; + + if (sec->cmd_param[0]) + glove_mode_enables |= GLOVE_MODE_EN; + else + glove_mode_enables &= ~(GLOVE_MODE_EN); + + retval = sec_ts_glove_mode_enables(ts, glove_mode_enables); + + if (retval < 0) { + input_err(true, &ts->client->dev, "%s: failed, retval = %d\n", __func__, retval); + snprintf(buff, sizeof(buff), "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + snprintf(buff, sizeof(buff), "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + } + } + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_exit(sec); +} + +static void clear_cover_mode(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + + input_info(true, &ts->client->dev, "%s: start clear_cover_mode %s\n", __func__, buff); + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 3) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + if (sec->cmd_param[0] > 1) { + ts->flip_enable = true; + ts->cover_type = sec->cmd_param[1]; + ts->cover_cmd = (u8)ts->cover_type; + } else { + ts->flip_enable = false; + } + + if (!ts->power_status == SEC_TS_STATE_POWER_OFF && ts->reinit_done) { + if (ts->flip_enable) + sec_ts_set_cover_type(ts, true); + else + sec_ts_set_cover_type(ts, false); + } + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + } + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_exit(sec); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +}; + +static void dead_zone_enable(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + int ret; + char data = 0; + + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + data = sec->cmd_param[0]; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_EDGE_DEADZONE, &data, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to set deadzone\n", __func__); + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto err_set_dead_zone; + } + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + } + +err_set_dead_zone: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +}; + +static void drawing_test_enable(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; +#ifdef SEC_TS_SUPPORT_CUSTOMLIB + int ret; +#endif + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + if (ts->use_customlib) { + if (sec->cmd_param[0]) + ts->lowpower_mode &= ~SEC_TS_MODE_CUSTOMLIB_FORCE_KEY; + else + ts->lowpower_mode |= SEC_TS_MODE_CUSTOMLIB_FORCE_KEY; + + #ifdef SEC_TS_SUPPORT_CUSTOMLIB + ret = sec_ts_set_custom_library(ts); + if (ret < 0) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + } + #endif + + } else { + snprintf(buff, sizeof(buff), "%s", "NA"); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + } + } + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +}; + +static void sec_ts_swap(u8 *a, u8 *b) +{ + u8 temp = *a; + + *a = *b; + *b = temp; +} + +static void rearrange_sft_result(u8 *data, int length) +{ + int i, nlength; + + nlength = length - (length % 4); + + for (i = 0; i < nlength; i += 4) { + sec_ts_swap(&data[i], &data[i + 3]); + sec_ts_swap(&data[i + 1], &data[i + 2]); + } +} + +int execute_selftest(struct sec_ts_data *ts, bool save_result) +{ + u8 pStr[50] = {0}; + u8 pTmp[20]; + int rc = 0; + u8 tpara[2] = {0x23, 0x40}; + u8 *rBuff; + int i; + int result_size = SEC_TS_SELFTEST_REPORT_SIZE + ts->tx_count * ts->rx_count * 2; + + /* save selftest result in flash */ + if (save_result) + tpara[0] = 0x23; + else + tpara[0] = 0xA3; + + rBuff = kzalloc(result_size, GFP_KERNEL); + if (!rBuff) + return -ENOMEM; + + input_info(true, &ts->client->dev, "%s: Self test start!\n", __func__); + rc = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SELFTEST, tpara, 2); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: Send selftest cmd failed!\n", __func__); + goto err_exit; + } + + sec_ts_delay(350); + + rc = sec_ts_wait_for_ready(ts, SEC_TS_VENDOR_ACK_SELF_TEST_DONE); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: Selftest execution time out!\n", __func__); + goto err_exit; + } + + input_raw_info(true, &ts->client->dev, "%s: Self test done!\n", __func__); + + rc = ts->sec_ts_i2c_read(ts, SEC_TS_READ_SELFTEST_RESULT, rBuff, result_size); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: Selftest execution time out!\n", __func__); + goto err_exit; + } + + rearrange_sft_result(rBuff, SEC_TS_SELFTEST_REPORT_SIZE); + + for (i = 0; i < 80; i += 4) { + if (i / 4 == 0) + strncat(pStr, "SIG ", 5); + else if (i / 4 == 1) + strncat(pStr, "VER ", 5); + else if (i / 4 == 2) + strncat(pStr, "SIZ ", 5); + else if (i / 4 == 3) + strncat(pStr, "CRC ", 5); + else if (i / 4 == 4) + strncat(pStr, "RES ", 5); + else if (i / 4 == 5) + strncat(pStr, "COU ", 5); + else if (i / 4 == 6) + strncat(pStr, "PAS ", 5); + else if (i / 4 == 7) + strncat(pStr, "FAI ", 5); + else if (i / 4 == 8) + strncat(pStr, "CHA ", 5); + else if (i / 4 == 9) + strncat(pStr, "AMB ", 5); + else if (i / 4 == 10) + strncat(pStr, "RXS ", 5); + else if (i / 4 == 11) + strncat(pStr, "TXS ", 5); + else if (i / 4 == 12) + strncat(pStr, "RXO ", 5); + else if (i / 4 == 13) + strncat(pStr, "TXO ", 5); + else if (i / 4 == 14) + strncat(pStr, "RXG ", 5); + else if (i / 4 == 15) + strncat(pStr, "TXG ", 5); + else if (i / 4 == 16) + strncat(pStr, "RXR ", 5); + else if (i / 4 == 17) + strncat(pStr, "TXT ", 5); + else if (i / 4 == 18) + strncat(pStr, "RXT ", 5); + else if (i / 4 == 19) + strncat(pStr, "TXR ", 5); + + snprintf(pTmp, sizeof(pTmp), "%2X, %2X, %2X, %2X", + rBuff[i], rBuff[i + 1], rBuff[i + 2], rBuff[i + 3]); + strncat(pStr, pTmp, strnlen(pTmp, sizeof(pTmp))); + + if (i / 4 == 4) { + if ((rBuff[i + 3] & 0x30) != 0)// RX, RX open check. + rc = 0; + else + rc = 1; + + ts->ito_test[0] = rBuff[i]; + ts->ito_test[1] = rBuff[i + 1]; + ts->ito_test[2] = rBuff[i + 2]; + ts->ito_test[3] = rBuff[i + 3]; + } + if (i % 8 == 4) { + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + memset(pStr, 0x00, sizeof(pStr)); + } else { + strncat(pStr, " ", 3); + } + } + +err_exit: + kfree(rBuff); + return rc; +} + +static void run_trx_short_test(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[1024 + 256] = {0}; + int rc; + int size = SEC_TS_SELFTEST_REPORT_SIZE + ts->tx_count * ts->rx_count * 2; + char para = TO_TOUCH_MODE; + u8 *rBuff = NULL; + int ii; + u8 data[32] = { 0 }; + int sum = 0; + u8 pStr[50] = {0}; + u8 pTmp[20]; + + sec_cmd_set_default_result(sec); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Touch is stopped!\n", __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + return; + } + + rBuff = kzalloc(size, GFP_KERNEL); + if (!rBuff) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + return; + } + + memset(rBuff, 0x00, size); + memset(data, 0x00, 32); + + disable_irq(ts->client->irq); + + input_info(true, &ts->client->dev, "%s: set power mode to test mode\n", __func__); + data[0] = 0x02; + rc = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_POWER_MODE, data, 1); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: set test mode failed\n", __func__); + goto err_trx_short; + } + + input_info(true, &ts->client->dev, "%s: clear event stack\n", __func__); + rc = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CLEAR_EVENT_STACK, NULL, 0); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: clear event stack failed\n", __func__); + goto err_trx_short; + } + + input_info(true, &ts->client->dev, "%s: self test start\n", __func__); + + data[0] = 0x04; + + rc = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SELFTEST, data, 1); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: Send selftest cmd failed!\n", __func__); + goto err_trx_short; + } + + sec_ts_delay(700); + + rc = sec_ts_wait_for_ready(ts, SEC_TS_VENDOR_ACK_SELF_TEST_DONE); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: Selftest execution time out!\n", __func__); + goto err_trx_short; + } + + input_info(true, &ts->client->dev, "%s: self test done\n", __func__); + + rc = ts->sec_ts_i2c_read(ts, SEC_TS_READ_SELFTEST_RESULT, rBuff, size); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: Selftest execution time out!\n", __func__); + goto err_trx_short; + } + + rc = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_POWER_MODE, ¶, 1); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: mode changed failed!\n", __func__); + goto err_trx_short; + } + + sec_ts_reinit(ts); + + input_info(true, &ts->client->dev, "%s: Test Result %02X, %02X, %02X, %02X\n", + __func__, rBuff[16], rBuff[17], rBuff[18], rBuff[19]); + + enable_irq(ts->client->irq); + + memcpy(data, &rBuff[48], 32); + + for (ii = 0; ii < 32; ii++) + sum += data[ii]; + + if (!sum) + goto test_ok; + + for (ii = 0; ii < 8; ii += 4) { + if (ii / 4 == 0) + strncat(pStr, "RXO", 5); + if (ii / 4 == 1) + strncat(pStr, "TXO", 5); + if (ii / 4 == 2) + strncat(pStr, "RXG", 5); + if (ii / 4 == 3) + strncat(pStr, "TXG", 5); + if (ii / 4 == 4) + strncat(pStr, "RXR", 5); + if (ii / 4 == 5) + strncat(pStr, "TXT", 5); + if (ii / 4 == 6) + strncat(pStr, "RXT", 5); + if (ii / 4 == 7) + strncat(pStr, "TXR", 5); + + snprintf(pTmp, sizeof(pTmp), "%2X, %2X, %2X, %2X", + data[ii], data[ii + 1], data[ii + 2], data[ii + 3]); + strncat(pStr, pTmp, strnlen(pTmp, sizeof(pTmp))); + if (ii % 8 == 4) { + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + strncat(buff, pStr, sizeof(pStr)); + + memset(pStr, 0x00, sizeof(pStr)); + } else { + strncat(pStr, " ", 3); + } + } + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + + kfree(rBuff); + return; + +test_ok: + snprintf(buff, sizeof(buff), "%s", "OK"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + + kfree(rBuff); + return; + +err_trx_short: + + ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_POWER_MODE, ¶, 1); + enable_irq(ts->client->irq); + + snprintf(buff, sizeof(buff), "%s", "NG"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + + kfree(rBuff); + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + return; +} + +int sec_ts_execute_force_calibration(struct sec_ts_data *ts, int cal_mode) +{ + int rc = -1; + u8 cmd; + + input_info(true, &ts->client->dev, "%s: %d\n", __func__, cal_mode); + + if (cal_mode == OFFSET_CAL_SET) + cmd = SEC_TS_CMD_FACTORY_PANELCALIBRATION; + else if (cal_mode == AMBIENT_CAL) + cmd = SEC_TS_CMD_CALIBRATION_AMBIENT; +#ifdef USE_PRESSURE_SENSOR + else if (cal_mode == PRESSURE_CAL) + cmd = SEC_TS_CMD_CALIBRATION_PRESSURE; +#endif + else + return rc; + + if (ts->sec_ts_i2c_write(ts, cmd, NULL, 0) < 0) { + input_err(true, &ts->client->dev, "%s: Write Cal commend failed!\n", __func__); + return rc; + } + + sec_ts_delay(1000); + + rc = sec_ts_wait_for_ready(ts, SEC_TS_VENDOR_ACK_OFFSET_CAL_DONE); + + return rc; +} + +static void run_force_calibration(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = {0}; + int rc; + struct sec_ts_test_mode mode; + char mis_cal_data = 0xF0; + + sec_cmd_set_default_result(sec); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Touch is stopped!\n", __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + goto out_force_cal_before_irq_ctrl; + } + + if (ts->touch_count > 0) { + snprintf(buff, sizeof(buff), "%s", "NG_FINGER_ON"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out_force_cal_before_irq_ctrl; + } + + disable_irq(ts->client->irq); + + rc = sec_ts_execute_force_calibration(ts, OFFSET_CAL_SET); + if (rc < 0) { + snprintf(buff, sizeof(buff), "%s", "FAIL"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out_force_cal; + } + + if (ts->plat_data->mis_cal_check) { + sec_ts_delay(50); + + buff[0] = 0; + rc = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_STATEMANAGE_ON, buff, 1); + if (rc < 0) { + input_err(true, &ts->client->dev, + "%s: mis_cal_check error[1] ret: %d\n", __func__, rc); + } + + buff[0] = 0x2; + buff[1] = 0x2; + rc = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CHG_SYSMODE, buff, 2); + if (rc < 0) { + input_err(true, &ts->client->dev, + "%s: mis_cal_check error[2] ret: %d\n", __func__, rc); + } + + input_err(true, &ts->client->dev, "%s: try mis Cal. check\n", __func__); + rc = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_MIS_CAL_CHECK, NULL, 0); + if (rc < 0) { + input_err(true, &ts->client->dev, + "%s: mis_cal_check error[3] ret: %d\n", __func__, rc); + } + sec_ts_delay(200); + + rc = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_MIS_CAL_READ, &mis_cal_data, 1); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: i2c fail!, %d\n", __func__, rc); + mis_cal_data = 0xF3; + } else { + input_info(true, &ts->client->dev, "%s: miss cal data : %d\n", __func__, mis_cal_data); + } + + buff[0] = 1; + rc = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_STATEMANAGE_ON, buff, 1); + if (rc < 0) { + input_err(true, &ts->client->dev, + "%s: mis_cal_check error[4] ret: %d\n", __func__, rc); + } + + if (mis_cal_data) { + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_AMBIENT_DATA; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, NULL, &mode); + snprintf(buff, sizeof(buff), "%s", "MIS CAL"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out_force_cal; + } + } + + ts->cal_status = sec_ts_read_calibration_report(ts); + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + +out_force_cal: + enable_irq(ts->client->irq); + +out_force_cal_before_irq_ctrl: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + +} + +static void get_force_calibration(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = {0}; + int rc; + + sec_cmd_set_default_result(sec); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Touch is stopped!\n", __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + return; + } + + rc = sec_ts_read_calibration_report(ts); + if (rc < 0) { + snprintf(buff, sizeof(buff), "%s", "FAIL"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else if (rc == SEC_TS_STATUS_CALIBRATION_SEC) { + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + } else { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + +} + +static void set_lowpower_mode(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + char buff[SEC_CMD_STR_LEN] = { 0 }; + + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + } + +#if 0 + /* set lowpower mode by spay, edge_swipe function. */ + ts->lowpower_mode = sec->cmd_param[0]; +#endif + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + + sec_cmd_set_cmd_exit(sec); + + return; + +} + +static void set_wirelesscharger_mode(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + int ret; + bool mode; + u8 w_data[1] = {0x00}; + + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 3) + goto OUT; + + if (sec->cmd_param[0] == 0) { + ts->charger_mode |= SEC_TS_BIT_CHARGER_MODE_NO; + mode = false; + } else { + ts->charger_mode &= (~SEC_TS_BIT_CHARGER_MODE_NO); + mode = true; + } + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: fail to enable w-charger status, POWER_STATUS=OFF\n", __func__); + goto NG; + } + + if (sec->cmd_param[0] == 1) + ts->charger_mode = ts->charger_mode | SEC_TS_BIT_CHARGER_MODE_WIRELESS_CHARGER; + else if (sec->cmd_param[0] == 3) + ts->charger_mode = ts->charger_mode | SEC_TS_BIT_CHARGER_MODE_WIRELESS_BATTERY_PACK; + else if (mode == false) + ts->charger_mode = ts->charger_mode & (~SEC_TS_BIT_CHARGER_MODE_WIRELESS_CHARGER) & (~SEC_TS_BIT_CHARGER_MODE_WIRELESS_BATTERY_PACK); + + w_data[0] = ts->charger_mode; + ret = ts->sec_ts_i2c_write(ts, SET_TS_CMD_SET_CHARGER_MODE, w_data, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Failed to send command 74\n", __func__); + goto NG; + } + + input_err(true, &ts->client->dev, "%s: %s, status =%x\n", + __func__, (mode) ? "wireless enable" : "wireless disable", ts->charger_mode); + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + return; + +NG: + input_err(true, &ts->client->dev, "%s: %s, status =%x\n", + __func__, (mode) ? "wireless enable" : "wireless disable", ts->charger_mode); + +OUT: + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); +} + +static void spay_enable(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) + goto NG; + + if (sec->cmd_param[0]) { + if (ts->use_customlib) + ts->lowpower_mode |= SEC_TS_MODE_CUSTOMLIB_SPAY; + } else { + if (ts->use_customlib) + ts->lowpower_mode &= ~SEC_TS_MODE_CUSTOMLIB_SPAY; + } + + input_info(true, &ts->client->dev, "%s: %02X\n", __func__, ts->lowpower_mode); + + #ifdef SEC_TS_SUPPORT_CUSTOMLIB + if (ts->use_customlib) + sec_ts_set_custom_library(ts); + #endif + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + return; + +NG: + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); +} + +static void aod_enable(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) + goto NG; + + if (sec->cmd_param[0]) { + if (ts->use_customlib) + ts->lowpower_mode |= SEC_TS_MODE_CUSTOMLIB_AOD; + } else { + if (ts->use_customlib) + ts->lowpower_mode &= ~SEC_TS_MODE_CUSTOMLIB_AOD; + } + + input_info(true, &ts->client->dev, "%s: %02X\n", __func__, ts->lowpower_mode); + + #ifdef SEC_TS_SUPPORT_CUSTOMLIB + if (ts->use_customlib) + sec_ts_set_custom_library(ts); + #endif + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + return; + +NG: + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); +} + +/* + * flag 1 : set edge handler + * 2 : set (portrait, normal) edge zone data + * 4 : set (portrait, normal) dead zone data + * 8 : set landscape mode data + * 16 : mode clear + * data + * 0x30, FFF (y start), FFF (y end), FF(direction) + * 0x31, FFFF (edge zone) + * 0x32, FF (up x), FF (down x), FFFF (y) + * 0x33, FF (mode), FFF (edge), FFF (dead zone) + * case + * edge handler set : 0x30.... + * booting time : 0x30... + 0x31... + * normal mode : 0x32... (+0x31...) + * landscape mode : 0x33... + * landscape -> normal (if same with old data) : 0x33, 0 + * landscape -> normal (etc) : 0x32.... + 0x33, 0 + */ + +void set_grip_data_to_ic(struct sec_ts_data *ts, u8 flag) +{ + u8 data[8] = { 0 }; + + input_info(true, &ts->client->dev, "%s: flag: %02X (clr,lan,nor,edg,han)\n", __func__, flag); + + if (flag & G_SET_EDGE_HANDLER) { + if (ts->grip_edgehandler_direction == 0) { + data[0] = 0x0; + data[1] = 0x0; + data[2] = 0x0; + data[3] = 0x0; + } else { + data[0] = (ts->grip_edgehandler_start_y >> 4) & 0xFF; + data[1] = (ts->grip_edgehandler_start_y << 4 & 0xF0) | ((ts->grip_edgehandler_end_y >> 8) & 0xF); + data[2] = ts->grip_edgehandler_end_y & 0xFF; + data[3] = ts->grip_edgehandler_direction & 0x3; + } + ts->sec_ts_i2c_write(ts, SEC_TS_CMD_EDGE_HANDLER, data, 4); + input_info(true, &ts->client->dev, "%s: 0x%02X %02X,%02X,%02X,%02X\n", + __func__, SEC_TS_CMD_EDGE_HANDLER, data[0], data[1], data[2], data[3]); + } + + if (flag & G_SET_EDGE_ZONE) { + data[0] = (ts->grip_edge_range >> 8) & 0xFF; + data[1] = ts->grip_edge_range & 0xFF; + ts->sec_ts_i2c_write(ts, SEC_TS_CMD_EDGE_AREA, data, 2); + input_info(true, &ts->client->dev, "%s: 0x%02X %02X,%02X\n", + __func__, SEC_TS_CMD_EDGE_AREA, data[0], data[1]); + } + + if (flag & G_SET_NORMAL_MODE) { + data[0] = ts->grip_deadzone_up_x & 0xFF; + data[1] = ts->grip_deadzone_dn_x & 0xFF; + data[2] = (ts->grip_deadzone_y >> 8) & 0xFF; + data[3] = ts->grip_deadzone_y & 0xFF; + ts->sec_ts_i2c_write(ts, SEC_TS_CMD_DEAD_ZONE, data, 4); + input_info(true, &ts->client->dev, "%s: 0x%02X %02X,%02X,%02X,%02X\n", + __func__, SEC_TS_CMD_DEAD_ZONE, data[0], data[1], data[2], data[3]); + } + + if (flag & G_SET_LANDSCAPE_MODE) { + data[0] = ts->grip_landscape_mode & 0x1; + data[1] = (ts->grip_landscape_edge >> 4) & 0xFF; + data[2] = (ts->grip_landscape_edge << 4 & 0xF0) | ((ts->grip_landscape_deadzone >> 8) & 0xF); + data[3] = ts->grip_landscape_deadzone & 0xFF; + ts->sec_ts_i2c_write(ts, SEC_TS_CMD_LANDSCAPE_MODE, data, 4); + input_info(true, &ts->client->dev, "%s: 0x%02X %02X,%02X,%02X,%02X\n", + __func__, SEC_TS_CMD_LANDSCAPE_MODE, data[0], data[1], data[2], data[3]); + } + + if (flag & G_CLR_LANDSCAPE_MODE) { + data[0] = ts->grip_landscape_mode; + ts->sec_ts_i2c_write(ts, SEC_TS_CMD_LANDSCAPE_MODE, data, 1); + input_info(true, &ts->client->dev, "%s: 0x%02X %02X\n", + __func__, SEC_TS_CMD_LANDSCAPE_MODE, data[0]); + } +} + +/* + * index 0 : set edge handler + * 1 : portrait (normal) mode + * 2 : landscape mode + * + * data + * 0, X (direction), X (y start), X (y end) + * direction : 0 (off), 1 (left), 2 (right) + * ex) echo set_grip_data,0,2,600,900 > cmd + * + * 1, X (edge zone), X (dead zone up x), X (dead zone down x), X (dead zone y) + * ex) echo set_grip_data,1,200,10,50,1500 > cmd + * + * 2, 1 (landscape mode), X (edge zone), X (dead zone) + * ex) echo set_grip_data,2,1,200,100 > cmd + * + * 2, 0 (portrait mode) + * ex) echo set_grip_data,2,0 > cmd + */ + +static void set_grip_data(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + u8 mode = G_NONE; + + sec_cmd_set_default_result(sec); + + memset(buff, 0, sizeof(buff)); + + mutex_lock(&ts->device_mutex); + + if (sec->cmd_param[0] == 0) { // edge handler + if (sec->cmd_param[1] == 0) { // clear + ts->grip_edgehandler_direction = 0; + } else if (sec->cmd_param[1] < 3) { + ts->grip_edgehandler_direction = sec->cmd_param[1]; + ts->grip_edgehandler_start_y = sec->cmd_param[2]; + ts->grip_edgehandler_end_y = sec->cmd_param[3]; + } else { + input_err(true, &ts->client->dev, "%s: cmd1 is abnormal, %d (%d)\n", + __func__, sec->cmd_param[1], __LINE__); + goto err_grip_data; + } + + mode = mode | G_SET_EDGE_HANDLER; + set_grip_data_to_ic(ts, mode); + + } else if (sec->cmd_param[0] == 1) { // normal mode + if (ts->grip_edge_range != sec->cmd_param[1]) + mode = mode | G_SET_EDGE_ZONE; + + ts->grip_edge_range = sec->cmd_param[1]; + ts->grip_deadzone_up_x = sec->cmd_param[2]; + ts->grip_deadzone_dn_x = sec->cmd_param[3]; + ts->grip_deadzone_y = sec->cmd_param[4]; + mode = mode | G_SET_NORMAL_MODE; + + if (ts->grip_landscape_mode == 1) { + ts->grip_landscape_mode = 0; + mode = mode | G_CLR_LANDSCAPE_MODE; + } + set_grip_data_to_ic(ts, mode); + } else if (sec->cmd_param[0] == 2) { // landscape mode + if (sec->cmd_param[1] == 0) { // normal mode + ts->grip_landscape_mode = 0; + mode = mode | G_CLR_LANDSCAPE_MODE; + } else if (sec->cmd_param[1] == 1) { + ts->grip_landscape_mode = 1; + ts->grip_landscape_edge = sec->cmd_param[2]; + ts->grip_landscape_deadzone = sec->cmd_param[3]; + mode = mode | G_SET_LANDSCAPE_MODE; + } else { + input_err(true, &ts->client->dev, "%s: cmd1 is abnormal, %d (%d)\n", + __func__, sec->cmd_param[1], __LINE__); + goto err_grip_data; + } + set_grip_data_to_ic(ts, mode); + } else { + input_err(true, &ts->client->dev, "%s: cmd0 is abnormal, %d", __func__, sec->cmd_param[0]); + goto err_grip_data; + } + + mutex_unlock(&ts->device_mutex); + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + return; + +err_grip_data: + mutex_unlock(&ts->device_mutex); + + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); +} + +/* + * Set/Get Dex Mode 0xE7 + * 0: Disable dex mode + * 1: Full screen mode + * 2: Iris mode + */ +static void dex_enable(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + int ret; + + sec_cmd_set_default_result(sec); + + if (!ts->plat_data->support_dex) { + input_err(true, &ts->client->dev, "%s: not support DeX mode\n", __func__); + goto NG; + } + + if ((sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) && + (sec->cmd_param[1] < 0 || sec->cmd_param[1] > 1)) { + input_err(true, &ts->client->dev, "%s: not support param\n", __func__); + goto NG; + } + + sec_ts_unlocked_release_all_finger(ts); + + ts->dex_mode = sec->cmd_param[0]; + if (ts->dex_mode) { + input_err(true, &ts->client->dev, "%s: set DeX touch_pad mode%s\n", + __func__, sec->cmd_param[1] ? " & Iris mode" : ""); + ts->input_dev = ts->input_dev_pad; + if (sec->cmd_param[1]) { + /* Iris mode */ + ts->dex_mode = 0x02; + ts->dex_name = "[DeXI]"; + } else { + ts->dex_name = "[DeX]"; + } + } else { + input_err(true, &ts->client->dev, "%s: set touch mode\n", __func__); + ts->input_dev = ts->input_dev_touch; + ts->dex_name = ""; + } + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Touch is stopped!\n", __func__); + goto NG; + } + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_DEX_MODE, &ts->dex_mode, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to set dex %smode\n", __func__, + sec->cmd_param[1] ? "iris " : ""); + goto NG; + } + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + return; + +NG: + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); +} + +static void brush_enable(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + int ret; + + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out; + } + + ts->brush_mode = sec->cmd_param[0]; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Touch is stopped!\n", __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + goto out; + } + + input_info(true, &ts->client->dev, + "%s: set brush mode %s\n", __func__, ts->brush_mode ? "enable" : "disable"); + + /* - 0: Disable Artcanvas min phi mode + * - 1: Enable Artcanvas min phi mode + */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_BRUSH_MODE, &ts->brush_mode, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to set brush mode\n", __func__); + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out; + } + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; +out: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void set_touchable_area(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + int ret; + + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out; + } + + ts->touchable_area = sec->cmd_param[0]; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Touch is stopped!\n", __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + goto out; + } + + input_info(true, &ts->client->dev, + "%s: set 16:9 mode %s\n", __func__, ts->touchable_area ? "enable" : "disable"); + + /* - 0: Disable 16:9 mode + * - 1: Enable 16:9 mode + */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_TOUCHABLE_AREA, &ts->touchable_area, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to set 16:9 mode\n", __func__); + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out; + } + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; +out: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void set_log_level(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + char tBuff[2] = { 0 }; + u8 w_data[1] = {0x00}; + int ret; + + sec_cmd_set_default_result(sec); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Touch is stopped!\n", __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + return; + } + + if ((sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) || + (sec->cmd_param[1] < 0 || sec->cmd_param[1] > 1) || + (sec->cmd_param[2] < 0 || sec->cmd_param[2] > 1) || + (sec->cmd_param[3] < 0 || sec->cmd_param[3] > 1) || + (sec->cmd_param[4] < 0 || sec->cmd_param[4] > 1) || + (sec->cmd_param[5] < 0 || sec->cmd_param[5] > 1) || + (sec->cmd_param[6] < 0 || sec->cmd_param[6] > 1) || + (sec->cmd_param[7] < 0 || sec->cmd_param[7] > 1)) { + input_err(true, &ts->client->dev, "%s: para out of range\n", __func__); + snprintf(buff, sizeof(buff), "%s", "Para out of range"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + return; + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_STATUS_EVENT_TYPE, tBuff, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Read Event type enable status fail\n", __func__); + snprintf(buff, sizeof(buff), "%s", "Read Stat Fail"); + goto err; + } + + input_info(true, &ts->client->dev, "%s: STATUS_EVENT enable = 0x%02X, 0x%02X\n", + __func__, tBuff[0], tBuff[1]); + + tBuff[0] = BIT_STATUS_EVENT_VENDOR_INFO(sec->cmd_param[6]); + tBuff[1] = BIT_STATUS_EVENT_ERR(sec->cmd_param[0]) | + BIT_STATUS_EVENT_INFO(sec->cmd_param[1]) | + BIT_STATUS_EVENT_USER_INPUT(sec->cmd_param[2]); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_STATUS_EVENT_TYPE, tBuff, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Write Event type enable status fail\n", __func__); + snprintf(buff, sizeof(buff), "%s", "Write Stat Fail"); + goto err; + } + + if (sec->cmd_param[0] == 1 && sec->cmd_param[1] == 1 && + sec->cmd_param[2] == 1 && sec->cmd_param[3] == 1 && + sec->cmd_param[4] == 1 && sec->cmd_param[5] == 1 && + sec->cmd_param[6] == 1 && sec->cmd_param[7] == 1) { + w_data[0] = 0x1; + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_VENDOR_EVENT_LEVEL, w_data, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Write Vendor Event Level fail\n", __func__); + snprintf(buff, sizeof(buff), "%s", "Write Stat Fail"); + goto err; + } + } else { + w_data[0] = 0x0; + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_VENDOR_EVENT_LEVEL, w_data, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Write Vendor Event Level fail\n", __func__); + snprintf(buff, sizeof(buff), "%s", "Write Stat Fail"); + goto err; + } + } + + input_info(true, &ts->client->dev, "%s: ERROR : %d, INFO : %d, USER_INPUT : %d, INFO_CUSTOMLIB : %d, VENDOR_INFO : %d, VENDOR_EVENT_LEVEL : %d\n", + __func__, sec->cmd_param[0], sec->cmd_param[1], sec->cmd_param[2], sec->cmd_param[5], sec->cmd_param[6], w_data[0]); + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + return; +err: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; +} + +static void debug(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + + sec_cmd_set_default_result(sec); + + ts->debug_flag = sec->cmd_param[0]; + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); +} + +static void not_support_cmd(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + char buff[SEC_CMD_STR_LEN] = { 0 }; + + sec_cmd_set_default_result(sec); + snprintf(buff, sizeof(buff), "%s", "NA"); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + sec_cmd_set_cmd_exit(sec); +} + +int sec_ts_fn_init(struct sec_ts_data *ts) +{ + int retval; + + retval = sec_cmd_init(&ts->sec, sec_cmds, + ARRAY_SIZE(sec_cmds), SEC_CLASS_DEVT_TSP); + if (retval < 0) { + input_err(true, &ts->client->dev, + "%s: Failed to sec_cmd_init\n", __func__); + goto exit; + } + + retval = sysfs_create_group(&ts->sec.fac_dev->kobj, + &cmd_attr_group); + if (retval < 0) { + input_err(true, &ts->client->dev, + "%s: Failed to create sysfs attributes\n", __func__); + goto exit; + } + + retval = sysfs_create_link(&ts->sec.fac_dev->kobj, + &ts->input_dev->dev.kobj, "input"); + if (retval < 0) { + input_err(true, &ts->client->dev, + "%s: Failed to create input symbolic link\n", + __func__); + goto exit; + } + + ts->reinit_done = true; + + return 0; + +exit: + return retval; +} + +void sec_ts_fn_remove(struct sec_ts_data *ts) +{ + input_err(true, &ts->client->dev, "%s\n", __func__); + + sysfs_delete_link(&ts->sec.fac_dev->kobj, &ts->input_dev->dev.kobj, "input"); + + sysfs_remove_group(&ts->sec.fac_dev->kobj, + &cmd_attr_group); + + sec_cmd_exit(&ts->sec, SEC_CLASS_DEVT_TSP); + +} diff --git a/drivers/input/touchscreen/sec_mmi/sec_ts_fw.c b/drivers/input/touchscreen/sec_mmi/sec_ts_fw.c new file mode 100644 index 000000000000..708bbe915e1c --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/sec_ts_fw.c @@ -0,0 +1,1028 @@ +/* drivers/input/touchscreen/sec_ts_fw.c + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * http://www.samsungsemi.com/ + * + * Core file for Samsung TSC driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "sec_ts.h" + +#define SEC_TS_FW_BLK_SIZE 256 + +enum { + BUILT_IN = 0, + UMS, + BL, + FFU, +}; + +static int sec_ts_enter_fw_mode(struct sec_ts_data *ts) +{ + int ret; + u8 fw_update_mode_passwd[] = {0x55, 0xAC}; + u8 fw_status; + u8 id[3]; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_ENTER_FW_MODE, fw_update_mode_passwd, sizeof(fw_update_mode_passwd)); + sec_ts_delay(20); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: write fail, enter_fw_mode\n", __func__); + return 0; + } + + input_info(true, &ts->client->dev, "%s: write ok, enter_fw_mode - 0x%x 0x%x 0x%x\n", + __func__, SEC_TS_CMD_ENTER_FW_MODE, fw_update_mode_passwd[0], fw_update_mode_passwd[1]); + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_BOOT_STATUS, &fw_status, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read fail, read_boot_status\n", __func__); + return 0; + } + if (fw_status != SEC_TS_STATUS_BOOT_MODE) { + input_err(true, &ts->client->dev, "%s: enter fail! read_boot_status = 0x%x\n", __func__, fw_status); + return 0; + } + + input_info(true, &ts->client->dev, "%s: Success! read_boot_status = 0x%x\n", __func__, fw_status); + + sec_ts_delay(10); + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_ID, id, 3); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read id fail\n", __func__); + return 0; + } + + ts->boot_ver[0] = id[0]; + ts->boot_ver[1] = id[1]; + ts->boot_ver[2] = id[2]; + + ts->flash_page_size = SEC_TS_FW_BLK_SIZE_DEFAULT; + + input_info(true, &ts->client->dev, "%s: read_boot_id = %02X%02X%02X\n", __func__, id[0], id[1], id[2]); + + return 1; +} + +static int sec_ts_sw_reset(struct sec_ts_data *ts) +{ + int ret; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SW_RESET, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: write fail, sw_reset\n", __func__); + return 0; + } + + sec_ts_delay(100); + + ret = sec_ts_wait_for_ready(ts, SEC_TS_ACK_BOOT_COMPLETE); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: time out\n", __func__); + return 0; + } + + input_info(true, &ts->client->dev, "%s: sw_reset\n", __func__); + + /* Sense_on */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: write fail, Sense_on\n", __func__); + return 0; + } + + return ret; +} + +static void sec_ts_save_version_of_bin(struct sec_ts_data *ts, const fw_header *fw_hd) +{ + ts->plat_data->img_version_of_bin[3] = ((fw_hd->img_ver >> 24) & 0xff); + ts->plat_data->img_version_of_bin[2] = ((fw_hd->img_ver >> 16) & 0xff); + ts->plat_data->img_version_of_bin[1] = ((fw_hd->img_ver >> 8) & 0xff); + ts->plat_data->img_version_of_bin[0] = ((fw_hd->img_ver >> 0) & 0xff); + + ts->plat_data->core_version_of_bin[3] = ((fw_hd->fw_ver >> 24) & 0xff); + ts->plat_data->core_version_of_bin[2] = ((fw_hd->fw_ver >> 16) & 0xff); + ts->plat_data->core_version_of_bin[1] = ((fw_hd->fw_ver >> 8) & 0xff); + ts->plat_data->core_version_of_bin[0] = ((fw_hd->fw_ver >> 0) & 0xff); + + ts->plat_data->config_version_of_bin[3] = ((fw_hd->para_ver >> 24) & 0xff); + ts->plat_data->config_version_of_bin[2] = ((fw_hd->para_ver >> 16) & 0xff); + ts->plat_data->config_version_of_bin[1] = ((fw_hd->para_ver >> 8) & 0xff); + ts->plat_data->config_version_of_bin[0] = ((fw_hd->para_ver >> 0) & 0xff); + + input_info(true, &ts->client->dev, "%s: img_ver of bin = %x.%x.%x.%x\n", __func__, + ts->plat_data->img_version_of_bin[0], + ts->plat_data->img_version_of_bin[1], + ts->plat_data->img_version_of_bin[2], + ts->plat_data->img_version_of_bin[3]); + + input_info(true, &ts->client->dev, "%s: core_ver of bin = %x.%x.%x.%x\n", __func__, + ts->plat_data->core_version_of_bin[0], + ts->plat_data->core_version_of_bin[1], + ts->plat_data->core_version_of_bin[2], + ts->plat_data->core_version_of_bin[3]); + + input_info(true, &ts->client->dev, "%s: config_ver of bin = %x.%x.%x.%x\n", __func__, + ts->plat_data->config_version_of_bin[0], + ts->plat_data->config_version_of_bin[1], + ts->plat_data->config_version_of_bin[2], + ts->plat_data->config_version_of_bin[3]); +} + +static int sec_ts_save_version_of_ic(struct sec_ts_data *ts) +{ + u8 img_ver[4] = {0,}; + u8 core_ver[4] = {0,}; + u8 config_ver[4] = {0,}; + int ret; + + /* Image ver */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_IMG_VERSION, img_ver, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Image version read error\n", __func__); + return -EIO; + } + input_info(true, &ts->client->dev, "%s: IC Image version info : %x.%x.%x.%x\n", + __func__, img_ver[0], img_ver[1], img_ver[2], img_ver[3]); + + ts->plat_data->img_version_of_ic[0] = img_ver[0]; + ts->plat_data->img_version_of_ic[1] = img_ver[1]; + ts->plat_data->img_version_of_ic[2] = img_ver[2]; + ts->plat_data->img_version_of_ic[3] = img_ver[3]; + + /* Core ver */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_FW_VERSION, core_ver, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: core version read error\n", __func__); + return -EIO; + } + input_info(true, &ts->client->dev, "%s: IC Core version info : %x.%x.%x.%x,\n", + __func__, core_ver[0], core_ver[1], core_ver[2], core_ver[3]); + + ts->plat_data->core_version_of_ic[0] = core_ver[0]; + ts->plat_data->core_version_of_ic[1] = core_ver[1]; + ts->plat_data->core_version_of_ic[2] = core_ver[2]; + ts->plat_data->core_version_of_ic[3] = core_ver[3]; + + /* Config ver */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_PARA_VERSION, config_ver, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: config version read error\n", __func__); + return -EIO; + } + input_info(true, &ts->client->dev, "%s: IC config version info : %x.%x.%x.%x\n", + __func__, config_ver[0], config_ver[1], config_ver[2], config_ver[3]); + + ts->plat_data->config_version_of_ic[0] = config_ver[0]; + ts->plat_data->config_version_of_ic[1] = config_ver[1]; + ts->plat_data->config_version_of_ic[2] = config_ver[2]; + ts->plat_data->config_version_of_ic[3] = config_ver[3]; + + return 1; +} + +int sec_ts_check_firmware_version(struct sec_ts_data *ts, const u8 *fw_info) +{ + fw_header *fw_hd; + u8 buff[1]; + int i; + int ret; + /* + * sec_ts_check_firmware_version + * return value = 1 : firmware download needed, + * return value = 0 : skip firmware download + */ + + fw_hd = (fw_header *)fw_info; + + sec_ts_save_version_of_bin(ts, fw_hd); + + /* firmware download if READ_BOOT_STATUS = 0x10 */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_BOOT_STATUS, buff, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: fail to read BootStatus\n", __func__); + return -EIO; + } + + if (buff[0] == SEC_TS_STATUS_BOOT_MODE) { + input_err(true, &ts->client->dev, + "%s: ReadBootStatus = 0x%x, Firmware download Start!\n", + __func__, buff[0]); + return 1; + } + + ret = sec_ts_save_version_of_ic(ts); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail to read ic version\n", __func__); + return -EIO; + } + + /* check f/w version + * ver[0] : IC version + * ver[1] : Project version + */ + for (i = 0; i < 2; i++) { + if (ts->plat_data->img_version_of_ic[i] != ts->plat_data->img_version_of_bin[i]) { + if (ts->plat_data->bringup == 3) { + input_err(true, &ts->client->dev, "%s: bringup. force update\n", __func__); + return 1; + } + input_err(true, &ts->client->dev, "%s: do not matched version info\n", __func__); + return 0; + } + } + + if (ts->plat_data->img_version_of_ic[2] != ts->plat_data->img_version_of_bin[2]) + return 1; + + if (ts->plat_data->img_version_of_ic[3] < ts->plat_data->img_version_of_bin[3]) + return 1; + + return 0; +} + +static u8 sec_ts_checksum(u8 *data, int offset, int size) +{ + int i; + u8 checksum = 0; + + for (i = 0; i < size; i++) + checksum += data[i + offset]; + + return checksum; +} + +static int sec_ts_flashpageerase(struct sec_ts_data *ts, u32 page_idx, u32 page_num) +{ + int ret; + u8 tCmd[6]; + + tCmd[0] = SEC_TS_CMD_FLASH_ERASE; + tCmd[1] = (u8)((page_idx >> 8) & 0xFF); + tCmd[2] = (u8)((page_idx >> 0) & 0xFF); + tCmd[3] = (u8)((page_num >> 8) & 0xFF); + tCmd[4] = (u8)((page_num >> 0) & 0xFF); + tCmd[5] = sec_ts_checksum(tCmd, 1, 4); + + ret = ts->sec_ts_i2c_write_burst(ts, tCmd, 6); + + return ret; +} + +static int sec_ts_flashpagewrite(struct sec_ts_data *ts, u32 page_idx, u8 *page_data) +{ + int ret; + u8 tCmd[1 + 2 + SEC_TS_FW_BLK_SIZE_MAX + 1]; + int flash_page_size = (int)ts->flash_page_size; + + tCmd[0] = 0xD9; + tCmd[1] = (u8)((page_idx >> 8) & 0xFF); + tCmd[2] = (u8)((page_idx >> 0) & 0xFF); + + memcpy(&tCmd[3], page_data, flash_page_size); + tCmd[1 + 2 + flash_page_size] = sec_ts_checksum(tCmd, 1, 2 + flash_page_size); + + ret = ts->sec_ts_i2c_write_burst(ts, tCmd, 1 + 2 + flash_page_size + 1); + return ret; +} + +static int sec_ts_limited_flashpagewrite(struct sec_ts_data *ts, u32 page_idx, u8 *page_data) +{ + int ret = 0; + u8 *tCmd; + u8 copy_data[3 + SEC_TS_FW_BLK_SIZE_MAX]; + int copy_left = (int)ts->flash_page_size + 3; + int copy_size = 0; + int copy_max = ts->i2c_burstmax - 1; + int flash_page_size = (int)ts->flash_page_size; + + copy_data[0] = (u8)((page_idx >> 8) & 0xFF); /* addH */ + copy_data[1] = (u8)((page_idx >> 0) & 0xFF); /* addL */ + + memcpy(©_data[2], page_data, flash_page_size); /* DATA */ + copy_data[2 + flash_page_size] = sec_ts_checksum(copy_data, 0, 2 + flash_page_size); /* CS */ + + while (copy_left > 0) { + int copy_cur = (copy_left > copy_max) ? copy_max : copy_left; + + tCmd = kzalloc(copy_cur + 1, GFP_KERNEL); + if (!tCmd) + goto err_write; + + if (copy_size == 0) + tCmd[0] = SEC_TS_CMD_FLASH_WRITE; + else + tCmd[0] = SEC_TS_CMD_FLASH_PADDING; + + memcpy(&tCmd[1], ©_data[copy_size], copy_cur); + + ret = ts->sec_ts_i2c_write_burst(ts, tCmd, 1 + copy_cur); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: failed, ret:%d\n", __func__, ret); + + copy_size += copy_cur; + copy_left -= copy_cur; + kfree(tCmd); + } + return ret; + +err_write: + input_err(true, &ts->client->dev, + "%s: failed to alloc.\n", __func__); + return -ENOMEM; + +} + +static int sec_ts_flashwrite(struct sec_ts_data *ts, u32 mem_addr, u8 *mem_data, u32 mem_size, int retry) +{ + int ret; + u32 page_idx; + u32 size_copy; + u32 flash_page_size; + u32 page_idx_start; + u32 page_idx_end; + u32 page_num; + u8 page_buf[SEC_TS_FW_BLK_SIZE_MAX]; + + if (mem_size == 0) + return 0; + + flash_page_size = ts->flash_page_size; + page_idx_start = mem_addr / flash_page_size; + page_idx_end = (mem_addr + mem_size - 1) / flash_page_size; + page_num = page_idx_end - page_idx_start + 1; + + ret = sec_ts_flashpageerase(ts, page_idx_start, page_num); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: fw erase failed, mem_addr= %08X, pagenum = %d\n", + __func__, mem_addr, page_num); + return -EIO; + } + + sec_ts_delay(page_num + 10); + + size_copy = mem_size % flash_page_size; + if (size_copy == 0) + size_copy = flash_page_size; + + memset(page_buf, 0, flash_page_size); + + for (page_idx = page_num - 1;; page_idx--) { + memcpy(page_buf, mem_data + (page_idx * flash_page_size), size_copy); + if (ts->boot_ver[0] == 0xB2) { + ret = sec_ts_flashpagewrite(ts, (page_idx + page_idx_start), page_buf); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fw write failed, page_idx = %u\n", __func__, page_idx); + goto err; + } + + if (retry) { + sec_ts_delay(50); + ret = sec_ts_flashpagewrite(ts, (page_idx + page_idx_start), page_buf); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fw write failed, page_idx = %u\n", __func__, page_idx); + goto err; + } + } + } else { + ret = sec_ts_limited_flashpagewrite(ts, (page_idx + page_idx_start), page_buf); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fw write failed, page_idx = %u\n", __func__, page_idx); + goto err; + } + + if (retry) { + sec_ts_delay(50); + ret = sec_ts_limited_flashpagewrite(ts, (page_idx + page_idx_start), page_buf); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fw write failed, page_idx = %u\n", __func__, page_idx); + goto err; + } + } + + } + + size_copy = flash_page_size; + sec_ts_delay(5); + + if (page_idx == 0) /* end condition (page_idx >= 0) page_idx type unsinged int */ + break; + } + + return mem_size; +err: + return -EIO; +} + +static int sec_ts_memoryblockread(struct sec_ts_data *ts, u32 mem_addr, int mem_size, u8 *buf) +{ + int ret; + u8 cmd[5]; + u8 *data; + + if (mem_size >= 64 * 1024) { + input_err(true, &ts->client->dev, + "%s: mem size over 64K\n", __func__); + return -EIO; + } + + cmd[0] = (u8)SEC_TS_CMD_FLASH_READ_ADDR; + cmd[1] = (u8)((mem_addr >> 24) & 0xff); + cmd[2] = (u8)((mem_addr >> 16) & 0xff); + cmd[3] = (u8)((mem_addr >> 8) & 0xff); + cmd[4] = (u8)((mem_addr >> 0) & 0xff); + + ret = ts->sec_ts_i2c_write_burst(ts, cmd, 5); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: send command failed, %02X\n", __func__, cmd[0]); + return -EIO; + } + + udelay(10); + cmd[0] = (u8)SEC_TS_CMD_FLASH_READ_SIZE; + cmd[1] = (u8)((mem_size >> 8) & 0xff); + cmd[2] = (u8)((mem_size >> 0) & 0xff); + + ret = ts->sec_ts_i2c_write_burst(ts, cmd, 3); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: send command failed, %02X\n", __func__, cmd[0]); + return -EIO; + } + + udelay(10); + cmd[0] = (u8)SEC_TS_CMD_FLASH_READ_DATA; + + data = buf; + + + ret = ts->sec_ts_i2c_read(ts, cmd[0], data, mem_size); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: memory read failed\n", __func__); + return -EIO; + } +#if 0 + ret = ts->sec_ts_i2c_write(ts, cmd[0], NULL, 0); + ret = ts->sec_ts_i2c_read_bulk(ts, data, mem_size); +#endif + return 0; +} + +static int sec_ts_memoryread(struct sec_ts_data *ts, u32 mem_addr, u8 *mem_data, u32 mem_size) +{ + int ret; + int retry = 3; + int read_size = 0; + int unit_size; + int max_size = 1024; + int read_left = (int)mem_size; + u8 *tmp_data; + + tmp_data = kmalloc(max_size, GFP_KERNEL); + if (!tmp_data) { + input_err(true, &ts->client->dev, + "%s: failed to kmalloc\n", __func__); + return -ENOMEM; + } + + while (read_left > 0) { + unit_size = (read_left > max_size) ? max_size : read_left; + retry = 3; + do { + ret = sec_ts_memoryblockread(ts, mem_addr, unit_size, tmp_data); + if (retry-- == 0) { + input_err(true, &ts->client->dev, + "%s: fw read fail mem_addr=%08X,unit_size=%d\n", + __func__, mem_addr, unit_size); + kfree(tmp_data); + return -1; + } + + memcpy(mem_data + read_size, tmp_data, unit_size); + } while (ret < 0); + + mem_addr += unit_size; + read_size += unit_size; + read_left -= unit_size; + } + + kfree(tmp_data); + + return read_size; +} + +static int sec_ts_chunk_update(struct sec_ts_data *ts, u32 addr, u32 size, u8 *data, int retry) +{ + u32 fw_size; + u32 write_size; + u8 *mem_rb; + int ret = 0; + + fw_size = size; + + write_size = sec_ts_flashwrite(ts, addr, data, fw_size, retry); + if (write_size != fw_size) { + input_err(true, &ts->client->dev, "%s: fw write failed\n", __func__); + ret = -1; + goto err_write_fail; + } + + mem_rb = vzalloc(fw_size); + if (!mem_rb) { + input_err(true, &ts->client->dev, "%s: vzalloc failed\n", __func__); + ret = -1; + goto err_write_fail; + } + + if (sec_ts_memoryread(ts, addr, mem_rb, fw_size) >= 0) { + u32 ii; + + for (ii = 0; ii < fw_size; ii++) { + if (data[ii] != mem_rb[ii]) + break; + } + + if (fw_size != ii) { + input_err(true, &ts->client->dev, "%s: fw verify fail\n", __func__); + ret = -1; + goto out; + } + } else { + ret = -1; + goto out; + } + + input_info(true, &ts->client->dev, "%s: verify done(%d)\n", __func__, ret); + +out: + vfree(mem_rb); +err_write_fail: + sec_ts_delay(10); + + return ret; +} + +static int sec_ts_firmware_update(struct sec_ts_data *ts, const u8 *data, + size_t size, int bl_update, int restore_cal, int retry) +{ + int i; + int ret; + fw_header *fw_hd; + fw_chunk *fw_ch; + u8 fw_status = 0; + u8 *fd = (u8 *)data; + u8 tBuff[3]; + + /* Check whether CRC is appended or not. + * Enter Firmware Update Mode + */ + if (!sec_ts_enter_fw_mode(ts)) { + input_err(true, &ts->client->dev, "%s: firmware mode failed\n", __func__); + return -1; + } + + if (bl_update && (ts->boot_ver[0] == 0xB4)) { + input_info(true, &ts->client->dev, "%s: bootloader is up to date\n", __func__); + return 0; + } + + input_info(true, &ts->client->dev, "%s: firmware update retry :%d\n", __func__, retry); + + fw_hd = (fw_header *)fd; + fd += sizeof(fw_header); + + if (fw_hd->signature != SEC_TS_FW_HEADER_SIGN) { + input_err(true, &ts->client->dev, "%s: firmware header error = %08X\n", __func__, fw_hd->signature); + return -1; + } + + input_err(true, &ts->client->dev, "%s: num_chunk : %d\n", __func__, fw_hd->num_chunk); + + for (i = 0; i < fw_hd->num_chunk; i++) { + fw_ch = (fw_chunk *)fd; + + input_err(true, &ts->client->dev, "%s: [%d] 0x%08X, 0x%08X, 0x%08X, 0x%08X\n", __func__, i, + fw_ch->signature, fw_ch->addr, fw_ch->size, fw_ch->reserved); + + if (fw_ch->signature != SEC_TS_FW_CHUNK_SIGN) { + input_err(true, &ts->client->dev, "%s: firmware chunk error = %08X\n", __func__, fw_ch->signature); + return -1; + } + fd += sizeof(fw_chunk); + ret = sec_ts_chunk_update(ts, fw_ch->addr, fw_ch->size, fd, retry); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: firmware chunk write failed, addr=%08X, size = %d\n", __func__, fw_ch->addr, fw_ch->size); + return -1; + } + fd += fw_ch->size; + } + + sec_ts_sw_reset(ts); + + if (!bl_update) { + if (restore_cal) { + input_err(true, &ts->client->dev, "%s: RUN OFFSET CALIBRATION\n", __func__); + + ret = sec_ts_execute_force_calibration(ts, OFFSET_CAL_SET); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to write OFFSET CAL SET!\n", __func__); + } + + /* Sense_on */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: write fail, Sense_on\n", __func__); + return -EIO; + } + + if (ts->sec_ts_i2c_read(ts, SEC_TS_READ_BOOT_STATUS, &fw_status, 1) < 0) { + input_err(true, &ts->client->dev, "%s: read fail, read_boot_status = 0x%x\n", __func__, fw_status); + return -EIO; + } + + if (fw_status != SEC_TS_STATUS_APP_MODE) { + input_err(true, &ts->client->dev, "%s: fw update sequence done, BUT read_boot_status = 0x%x\n", __func__, fw_status); + return -EIO; + } + + input_info(true, &ts->client->dev, "%s: fw update Success! read_boot_status = 0x%x\n", __func__, fw_status); + + return 1; + } else { + + if (ts->sec_ts_i2c_read(ts, SEC_TS_READ_ID, tBuff, 3) < 0) { + input_err(true, &ts->client->dev, "%s: read device id fail after bl fw download\n", __func__); + return -EIO; + } + + if (tBuff[0] == 0xA0) { + input_info(true, &ts->client->dev, "%s: bl fw download success - device id = %02X\n", __func__, tBuff[0]); + return -EIO; + } else { + input_err(true, &ts->client->dev, "%s: bl fw id does not match - device id = %02X\n", __func__, tBuff[0]); + return -EIO; + } + } + +} + +int sec_ts_firmware_update_bl(struct sec_ts_data *ts) +{ + const struct firmware *fw_entry; + char fw_path[SEC_TS_MAX_FW_PATH]; + int result = -1; + + disable_irq(ts->client->irq); + + snprintf(fw_path, SEC_TS_MAX_FW_PATH, "%s", SEC_TS_DEFAULT_BL_NAME); + + input_info(true, &ts->client->dev, "%s: initial bl update %s\n", __func__, fw_path); + + /* Loading Firmware------------------------------------------ */ + if (request_firmware(&fw_entry, fw_path, &ts->client->dev) != 0) { + input_err(true, &ts->client->dev, "%s: bt is not available\n", __func__); + goto err_request_fw; + } + input_info(true, &ts->client->dev, "%s: request bt done! size = %d\n", __func__, (int)fw_entry->size); + + result = sec_ts_firmware_update(ts, fw_entry->data, fw_entry->size, 1, false, 0); + +err_request_fw: + release_firmware(fw_entry); + enable_irq(ts->client->irq); + + return result; +} + +int sec_ts_bl_update(struct sec_ts_data *ts) +{ + int ret; + u8 tCmd[5] = { 0xDE, 0xAD, 0xBE, 0xEF }; + u8 tBuff[3]; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_READ_BL_UPDATE_STATUS, tCmd, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: bl update command send fail!\n", __func__); + goto err; + } + sec_ts_delay(10); + + do { + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_BL_UPDATE_STATUS, tBuff, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read bl update status fail!\n", __func__); + goto err; + } + sec_ts_delay(2); + + } while (tBuff[0] == 0x1); + + tCmd[0] = 0x55; + tCmd[1] = 0xAC; + ret = ts->sec_ts_i2c_write(ts, 0x57, tCmd, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: write passwd fail!\n", __func__); + goto err; + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_ID, tBuff, 3); + + if (tBuff[0] == 0xB4) { + input_info(true, &ts->client->dev, "%s: bl update completed!\n", __func__); + ret = 1; + } else { + input_info(true, &ts->client->dev, "%s: bl updated but bl version not matching, ver=%02X\n", __func__, tBuff[0]); + goto err; + } + + return ret; +err: + return -EIO; +} + +int sec_ts_firmware_update_on_probe(struct sec_ts_data *ts, bool force_update) +{ + const struct firmware *fw_entry; + char fw_path[SEC_TS_MAX_FW_PATH]; + int result = -1, restore_cal = 0; + int ii = 0; + int ret = 0; + int skip_firmup = 0; + + if (ts->plat_data->bringup == 1) { + input_err(true, &ts->client->dev, "%s: bringup. do not update\n", __func__); + return 0; + } + + if (ts->plat_data->firmware_name) + snprintf(fw_path, SEC_TS_MAX_FW_PATH, "%s", ts->plat_data->firmware_name); + else + return 0; + + disable_irq(ts->client->irq); + + /* read cal status */ + ts->cal_status = sec_ts_read_calibration_report(ts); + + input_info(true, &ts->client->dev, "%s: initial firmware update %s, cal:%X\n", + __func__, fw_path, ts->cal_status); + + /* Loading Firmware */ + if (request_firmware(&fw_entry, fw_path, &ts->client->dev) != 0) { + input_err(true, &ts->client->dev, "%s: firmware is not available\n", __func__); + goto err_request_fw; + } + input_info(true, &ts->client->dev, "%s: request firmware done! size = %d\n", __func__, (int)fw_entry->size); + + result = sec_ts_check_firmware_version(ts, fw_entry->data); + if (ts->plat_data->bringup == 2) { + input_err(true, &ts->client->dev, "%s: bringup. do not update\n", __func__); + result = 0; + goto err_request_fw; + } + + /* don't firmup case */ + if ((result <= 0) && (!force_update)) { + input_info(true, &ts->client->dev, "%s: skip - fw update\n", __func__); + /* goto err_request_fw; */ + skip_firmup = 1; + } + + if ((skip_firmup == 1) && (restore_cal == 0)) + goto err_request_fw; + + for (ii = 0; ii < 3; ii++) { + ret = sec_ts_firmware_update(ts, fw_entry->data, fw_entry->size, 0, restore_cal, ii); + if (ret >= 0) + break; + } + + if (ret < 0) + result = -1; + else + result = 0; + + sec_ts_save_version_of_ic(ts); + +err_request_fw: + release_firmware(fw_entry); + enable_irq(ts->client->irq); + return result; +} + +static int sec_ts_load_fw_from_bin(struct sec_ts_data *ts) +{ + const struct firmware *fw_entry; + char fw_path[SEC_TS_MAX_FW_PATH]; + int error = 0; + int restore_cal = 0; + + if (ts->client->irq) + disable_irq(ts->client->irq); + + if (!ts->plat_data->firmware_name) + snprintf(fw_path, SEC_TS_MAX_FW_PATH, "%s", SEC_TS_DEFAULT_FW_NAME); + else + snprintf(fw_path, SEC_TS_MAX_FW_PATH, "%s", ts->plat_data->firmware_name); + + input_info(true, &ts->client->dev, "%s: initial firmware update %s\n", __func__, fw_path); + + /* Loading Firmware */ + if (request_firmware(&fw_entry, fw_path, &ts->client->dev) != 0) { + input_err(true, &ts->client->dev, "%s: firmware is not available\n", __func__); + error = -1; + goto err_request_fw; + } + input_info(true, &ts->client->dev, "%s: request firmware done! size = %d\n", __func__, (int)fw_entry->size); + + /* use virtual pat_control - magic cal 1 */ + if (sec_ts_firmware_update(ts, fw_entry->data, fw_entry->size, 0, restore_cal, 0) < 0) + error = -1; + else + error = 0; + + sec_ts_save_version_of_ic(ts); + +err_request_fw: + release_firmware(fw_entry); + if (ts->client->irq) + enable_irq(ts->client->irq); + + return error; +} + +static int sec_ts_load_fw_from_ums(struct sec_ts_data *ts) +{ + fw_header *fw_hd; + struct file *fp; + mm_segment_t old_fs; + long fw_size, nread; + int error = 0; + int restore_cal = 0; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + fp = filp_open(SEC_TS_DEFAULT_UMS_FW, O_RDONLY, S_IRUSR); + if (IS_ERR(fp)) { + input_err(true, ts->dev, "%s: failed to open %s.\n", __func__, + SEC_TS_DEFAULT_UMS_FW); + error = -ENOENT; + goto open_err; + } + + fw_size = fp->f_path.dentry->d_inode->i_size; + + if (fw_size > 0) { + unsigned char *fw_data; + + fw_data = vzalloc(fw_size); + if (!fw_data) { + input_err(true, ts->dev, "%s: failed to alloc mem\n", __func__); + error = -ENOMEM; + filp_close(fp, NULL); + goto open_err; + } + nread = vfs_read(fp, (char __user *)fw_data, + fw_size, &fp->f_pos); + + input_info(true, ts->dev, + "%s: start, file path %s, size %ld Bytes\n", + __func__, SEC_TS_DEFAULT_UMS_FW, fw_size); + + if (nread != fw_size) { + input_err(true, ts->dev, + "%s: failed to read firmware file, nread %ld Bytes\n", + __func__, nread); + error = -EIO; + } else { + fw_hd = (fw_header *)fw_data; +#if 0 + sec_ts_check_firmware_version(ts, fw_data); +#endif + input_info(true, &ts->client->dev, "%s: firmware version %08X\n", __func__, fw_hd->fw_ver); + input_info(true, &ts->client->dev, "%s: parameter version %08X\n", __func__, fw_hd->para_ver); + + if (ts->client->irq) + disable_irq(ts->client->irq); + /* use virtual pat_control - magic cal 1 */ + if (sec_ts_firmware_update(ts, fw_data, fw_size, 0, restore_cal, 0) < 0) { + error = -1; /* firmware failed */ + goto done; + } + + sec_ts_save_version_of_ic(ts); + } + + if (error < 0) + input_err(true, ts->dev, "%s: failed update firmware\n", + __func__); + +done: + if (ts->client->irq) + enable_irq(ts->client->irq); + vfree(fw_data); + } + + filp_close(fp, NULL); + +open_err: + set_fs(old_fs); + return error; +} + +static int sec_ts_load_fw_from_ffu(struct sec_ts_data *ts) +{ + const struct firmware *fw_entry; + const char *fw_path = SEC_TS_DEFAULT_FFU_FW; + int result = -1; + + disable_irq(ts->client->irq); + + input_info(true, ts->dev, "%s: Load firmware : %s\n", __func__, fw_path); + + /* Loading Firmware */ + if (request_firmware(&fw_entry, fw_path, &ts->client->dev) != 0) { + input_err(true, &ts->client->dev, "%s: firmware is not available\n", __func__); + goto err_request_fw; + } + input_info(true, &ts->client->dev, "%s: request firmware done! size = %d\n", __func__, (int)fw_entry->size); + + sec_ts_check_firmware_version(ts, fw_entry->data); + + if (sec_ts_firmware_update(ts, fw_entry->data, fw_entry->size, 0, false, 0) < 0) + result = -1; + else + result = 0; + + sec_ts_save_version_of_ic(ts); + +err_request_fw: + release_firmware(fw_entry); + enable_irq(ts->client->irq); + return result; +} + +int sec_ts_firmware_update_on_hidden_menu(struct sec_ts_data *ts, int update_type) +{ + int ret = 0; + + /* Factory cmd for firmware update + * argument represent what is source of firmware like below. + * + * 0 : [BUILT_IN] Getting firmware which is for user. + * 1 : [UMS] Getting firmware from sd card. + * 2 : none + * 3 : [FFU] Getting firmware from air. + */ + + switch (update_type) { + case BUILT_IN: + ret = sec_ts_load_fw_from_bin(ts); + break; + case UMS: + ret = sec_ts_load_fw_from_ums(ts); + break; + case FFU: + ret = sec_ts_load_fw_from_ffu(ts); + break; + case BL: + ret = sec_ts_firmware_update_bl(ts); + if (ret < 0) { + break; + } else if (!ret) { + ret = sec_ts_firmware_update_on_probe(ts, false); + break; + } else { + ret = sec_ts_bl_update(ts); + if (ret < 0) + break; + ret = sec_ts_firmware_update_on_probe(ts, false); + if (ret < 0) + break; + } + break; + default: + input_err(true, ts->dev, "%s: Not support command[%d]\n", + __func__, update_type); + break; + } + +#ifdef SEC_TS_SUPPORT_CUSTOMLIB + sec_ts_check_custom_library(ts); + if (ts->use_customlib) + sec_ts_set_custom_library(ts); +#endif + + return ret; +} +EXPORT_SYMBOL(sec_ts_firmware_update_on_hidden_menu); diff --git a/drivers/input/touchscreen/sec_mmi/sec_ts_mmi.c b/drivers/input/touchscreen/sec_mmi/sec_ts_mmi.c new file mode 100644 index 000000000000..2afaf45c00ec --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/sec_ts_mmi.c @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2018 Motorola Mobility LLC + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sec_mmi.h" + +/* MMI specific sysfs entries and API */ +#define DEV_MMI (&data->i2c_client->dev) +#define DEV_TS (&ts->client->dev) + +static inline ssize_t sec_mmi_store_error(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +static ssize_t sec_mmi_forcereflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_doreflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_drv_irq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_drv_irq_show(struct device *dev, + struct device_attribute *attr, char *buf); +static inline ssize_t sec_mmi_show_error(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t sec_mmi_ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t sec_mmi_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static DEVICE_ATTR(forcereflash, (S_IWUSR | S_IWGRP), NULL, sec_mmi_forcereflash_store); +static DEVICE_ATTR(doreflash, (S_IWUSR | S_IWGRP), NULL, sec_mmi_doreflash_store); +static DEVICE_ATTR(drv_irq, (S_IWUSR | S_IWGRP | S_IRUGO), sec_mmi_drv_irq_show, sec_mmi_drv_irq_store); +static DEVICE_ATTR(hw_irqstat, S_IRUGO, sec_mmi_hw_irqstat_show, NULL); +static DEVICE_ATTR(ic_ver, S_IRUGO, sec_mmi_ic_ver_show, NULL); +static DEVICE_ATTR(poweron, S_IRUGO, sec_mmi_poweron_show, NULL); + +static struct attribute *mmi_attributes[] = { + &dev_attr_sec_mmi_forcereflash.attr, + &dev_attr_sec_mmi_fdoreflash.attr, + &dev_attr_sec_mmi_drv_irq.attr, + &dev_attr_sec_mmi_hw_irqstat.attr, + &dev_attr_sec_mmi_ic_ver.attr, + &dev_attr_sec_mmi_poweron.attr, + NULL, +}; + +static struct attribute_group mmi_attr_group = { + .attrs = mmi_attributes, +}; + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_mmi_data *data = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!data) { + dev_err(DEV_MMI "data pointer is NULL\n"); + return (ssize_t)0; + } + path = kobject_get_path(&data->i2c_client->dev.kobj, GFP_KERNEL); + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "samsung"); +} + +static struct device_attribute class_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_NULL +}; + +static int sec_mmi_touchscreen_class( + struct sec_mmi_data *data, bool create) +{ + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + const char *class_fname = "primary"; + static struct class *touchscreen_class; + static int minor; + + if (create) { + minor = input_get_new_minor(data->i2c_client->addr, + 1, false); + if (minor < 0) + minor = input_get_new_minor(TSDEV_MINOR_BASE, + TSDEV_MINOR_MAX, true); + dev_info(DEV_MMI, "assigned minor %d\n", minor); + + if (!touchscreen_class) { + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + } + + if (data->class_entry_name) + class_fname = data->class_entry_name; + + data->ts_class_dev = device_create(touchscreen_class, + NULL, MKDEV(INPUT_MAJOR, minor), data, class_fname); + if (IS_ERR(data->ts_class_dev)) { + error = PTR_ERR(data->ts_class_dev); + data->ts_class_dev = NULL; + return error; + } + + dev_set_drvdata(data->ts_class_dev, data); + + for (i = 0; class_attrs[i].attr.name != NULL; ++i) { + error = device_create_file(data->ts_class_dev, &class_attrs[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + } else { + if (!touchscreen_class || !data->ts_class_dev) + return -ENODEV; + + dev_set_drvdata(rmi4_data->ts_class_dev, NULL); + + for (i = 0; class_attrs[i].attr.name != NULL; ++i) + device_remove_file(data->ts_class_dev, &class_attrs[i]); + + device_unregister(data->ts_class_dev); + data->ts_class_dev = NULL; + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(data->ts_class_dev, &class_attrs[i]); + device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor)); + data->ts_class_dev = NULL; + class_unregister(touchscreen_class); + dev_err(DEV_MMI, "%s: error creating touchscreen class attrs\n", __func__); + + return -ENODEV; +} + +static int sec_mmi_panel_cb(struct notifier_block *nb, + unsigned long event, void *evd) +{ + struct msm_drm_notifier *evdata = evd; + struct sec_mmi_data *data = + container_of(nb, struct sec_mmi_data, panel_nb); + + if (!evdata || (evdata->id != data->ctrl_dsi)) { + dev_dbg(DEV_MMI, "%s: drm notification: id(%d) != ctrl_dsi(%d)\n", + __func__, evdata->id, data->ctrl_dsi); + return 0; + } + + if ((event == MSM_DRM_EARLY_EVENT_BLANK || event == MSM_DRM_EVENT_BLANK) && + evdata && evdata->data && data) { + int *blank = evdata->data; + + dev_dbg(DEV_MMI, "%s: drm notification: event = %lu blank = %d\n", + __func__, event, *blank); + /* entering suspend upon early blank event */ + /* to ensure shared power supply is still on */ + /* for in-cell design touch solutions */ + if (event == MSM_DRM_EARLY_EVENT_BLANK) { + if (*blank != MSM_DRM_BLANK_POWERDOWN) + return 0; + synaptics_dsx_display_off(&data->i2c_client->dev); + } else if (*blank == MSM_DRM_BLANK_UNBLANK) { + synaptics_dsx_display_on(&data->i2c_client->dev); + } + } + + return 0; +} + +static int sec_mmi_register_notifier( + struct sec_mmi_data *data, bool enable) +{ + int rc; + + if (enable) { + data->panel_nb.notifier_call = sec_mmi_panel_cb; + rc = msm_drm_register_client(&data->panel_nb); + } else + rc = msm_drm_unregister_client(&data->panel_nb); + + if (!rc) + dev_dbg(DEV_MMI, "%s: %sregistered drm notifier\n", + __func__, enable ? "" : "un"); + else + dev_err(DEV_MMI, "%s: unable to %sregister drm notifier\n", + __func__, enable ? "" : "un"); + + return rc; +} + +static ssize_t sec_mmi_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%d\n", + ts->power_status != SEC_TS_STATE_POWER_OFF); +} + +static ssize_t sec_mmi_ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%s%s\n%s%x\n%s%x\n", + "Product ID: ", rmi->product_id_string, + "Build ID: ", build_id, + "Config ID: ", config_id); +} + +static ssize_t sec_mmi_doreflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + const struct firmware *fw_entry; + char fw_path[SEC_TS_MAX_FW_PATH]; + int result = -1; + + if (size > SEC_TS_MAX_FW_PATH) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: FW filename is too long\n", + __func__); + return -EINVAL; + } + + disable_irq(ts->client->irq); + scnprintf(fw_path, SEC_TS_MAX_FW_PATH, "%s", buf); + input_info(true, DEV_TS, "%s: initial bl update %s\n", __func__, fw_path); + + /* Loading Firmware------------------------------------------ */ + if (request_firmware(&fw_entry, fw_path, &ts->client->dev) != 0) { + input_err(true, DEV_TS, "%s: bt is not available\n", __func__); + goto err_request_fw; + } + input_info(true, DEV_TS, "%s: request bt done! size = %d\n", __func__, (int)fw_entry->size); + + result = sec_ts_firmware_update(ts, fw_entry->data, fw_entry->size, 1, false, 0); + +err_request_fw: + release_firmware(fw_entry); + enable_irq(ts->client->irq); + + return result; +} + +static ssize_t sec_ts_regreadsize_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + mutex_lock(&ts->device_mutex); + + lv1cmd = buf[0]; + lv1_readsize = ((unsigned int)buf[4] << 24) | + ((unsigned int)buf[3] << 16) | ((unsigned int) buf[2] << 8) | ((unsigned int)buf[1] << 0); + lv1_readoffset = 0; + lv1_readremain = 0; + + mutex_unlock(&ts->device_mutex); + + return size; +} + +static ssize_t sec_ts_enter_recovery_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + struct sec_ts_plat_data *pdata = ts->plat_data; + int ret; + unsigned long on; + + ret = kstrtoul(buf, 10, &on); + if (ret != 0) { + input_err(true, &ts->client->dev, "%s: failed to read:%d\n", + __func__, ret); + return -EINVAL; + } + + if (on == 1) { + disable_irq(ts->client->irq); + gpio_free(pdata->irq_gpio); + + input_info(true, &ts->client->dev, "%s: gpio free\n", __func__); + if (gpio_is_valid(pdata->irq_gpio)) { + ret = gpio_request_one(pdata->irq_gpio, GPIOF_OUT_INIT_LOW, "sec,tsp_int"); + input_info(true, &ts->client->dev, "%s: gpio request one\n", __func__); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Unable to request tsp_int [%d]: %d\n", __func__, pdata->irq_gpio, ret); + } else { + input_err(true, &ts->client->dev, "%s: Failed to get irq gpio\n", __func__); + return -EINVAL; + } + + pdata->power(ts, false); + sec_ts_delay(100); + pdata->power(ts, true); + } else { + gpio_free(pdata->irq_gpio); + + if (gpio_is_valid(pdata->irq_gpio)) { + ret = gpio_request_one(pdata->irq_gpio, GPIOF_DIR_IN, "sec,tsp_int"); + if (ret) { + input_err(true, &ts->client->dev, "%s: Unable to request tsp_int [%d]\n", __func__, pdata->irq_gpio); + return -EINVAL; + } + } else { + input_err(true, &ts->client->dev, "%s: Failed to get irq gpio\n", __func__); + return -EINVAL; + } + + pdata->power(ts, false); + sec_ts_delay(500); + pdata->power(ts, true); + sec_ts_delay(500); + + /* AFE Calibration */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CALIBRATION_AMBIENT, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to write AFE_CAL\n", __func__); + + sec_ts_delay(1000); + enable_irq(ts->client->irq); + } + + sec_ts_read_information(ts); + + return size; +} + +static inline ssize_t sec_ts_show_error(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + input_err(true, &ts->client->dev, "%s: read only function, %s\n", __func__, attr->attr.name); + return -EPERM; +} + +static inline ssize_t sec_ts_store_error(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + input_err(true, &ts->client->dev, "%s: write only function, %s\n", __func__, attr->attr.name); + return -EPERM; +} + +int sec_ts_raw_device_init(struct sec_ts_data *ts) +{ + int ret; + + struct class *sec_class = NULL; + + sec_class = class_create(THIS_MODULE, "sec"); + ret = IS_ERR_OR_NULL(sec_class); + if (ret) { + input_err(true, &ts->client->dev, "%s: fail - class_create\n", __func__); + return; + } + + ts->dev = device_create(sec_class, NULL, 0, ts, "sec_ts"); + + ret = IS_ERR(ts->dev); + if (ret) { + input_err(true, &ts->client->dev, "%s: fail - device_create\n", __func__); + return ret; + } + + ret = sysfs_create_group(&ts->dev->kobj, &cmd_attr_group); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail - sysfs_create_group\n", __func__); + goto err_sysfs; + } + + return ret; +err_sysfs: + input_err(true, &ts->client->dev, "%s: fail\n", __func__); + return ret; +} diff --git a/drivers/input/touchscreen/sec_mmi/sec_ts_only_vendor.c b/drivers/input/touchscreen/sec_mmi/sec_ts_only_vendor.c new file mode 100644 index 000000000000..27e98eee2b1e --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/sec_ts_only_vendor.c @@ -0,0 +1,295 @@ +/* drivers/input/touchscreen/sec_ts_fw.c + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * http://www.samsungsemi.com/ + * + * Core file for Samsung TSC driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +/*#include */ + +#include "sec_ts.h" + +u8 lv1cmd; +u8 *read_lv1_buff; +static int lv1_readsize; +static int lv1_readremain; +static int lv1_readoffset; + +static ssize_t sec_ts_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_ts_regreadsize_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static inline ssize_t sec_ts_store_error(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +static ssize_t sec_ts_enter_recovery_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_ts_regread_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t sec_ts_gesture_status_show(struct device *dev, + struct device_attribute *attr, char *buf); +static inline ssize_t sec_ts_show_error(struct device *dev, + struct device_attribute *attr, char *buf); + +static DEVICE_ATTR(sec_ts_reg, (S_IWUSR | S_IWGRP), NULL, sec_ts_reg_store); +static DEVICE_ATTR(sec_ts_regreadsize, (S_IWUSR | S_IWGRP), NULL, sec_ts_regreadsize_store); +static DEVICE_ATTR(sec_ts_enter_recovery, (S_IWUSR | S_IWGRP), NULL, sec_ts_enter_recovery_store); +static DEVICE_ATTR(sec_ts_regread, S_IRUGO, sec_ts_regread_show, NULL); +static DEVICE_ATTR(sec_ts_gesture_status, S_IRUGO, sec_ts_gesture_status_show, NULL); + +static struct attribute *cmd_attributes[] = { + &dev_attr_sec_ts_reg.attr, + &dev_attr_sec_ts_regreadsize.attr, + &dev_attr_sec_ts_enter_recovery.attr, + &dev_attr_sec_ts_regread.attr, + &dev_attr_sec_ts_gesture_status.attr, + NULL, +}; + +static struct attribute_group cmd_attr_group = { + .attrs = cmd_attributes, +}; + +/* for debugging--------------------------------------------------------------------------------------*/ +static ssize_t sec_ts_reg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_info(true, &ts->client->dev, "%s: Power off state\n", __func__); + return -EIO; + } + + if (size > 0) + ts->sec_ts_i2c_write_burst(ts, (u8 *)buf, size); + + input_info(true, &ts->client->dev, "%s: 0x%x, 0x%x, size %d\n", __func__, buf[0], buf[1], (int)size); + return size; +} + +static ssize_t sec_ts_regread_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + int ret; + int length; + int remain; + int offset; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Power off state\n", __func__); + return -EIO; + } + + disable_irq(ts->client->irq); + + mutex_lock(&ts->device_mutex); + + read_lv1_buff = kzalloc(lv1_readsize, GFP_KERNEL); + if (!read_lv1_buff) + goto malloc_err; + + remain = lv1_readsize; + offset = 0; + do { + if (remain >= ts->i2c_burstmax) + length = ts->i2c_burstmax; + else + length = remain; + + if (offset == 0) + ret = ts->sec_ts_i2c_read(ts, lv1cmd, &read_lv1_buff[offset], length); + else + ret = ts->sec_ts_i2c_read_bulk (ts, &read_lv1_buff[offset], length); + + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: i2c read %x command, remain =%d\n", __func__, lv1cmd, remain); + goto i2c_err; + } + + remain -= length; + offset += length; + } while (remain > 0); + + input_info(true, &ts->client->dev, "%s: lv1_readsize = %d\n", __func__, lv1_readsize); + memcpy(buf, read_lv1_buff + lv1_readoffset, lv1_readsize); + +i2c_err: + kfree(read_lv1_buff); +malloc_err: + mutex_unlock(&ts->device_mutex); + lv1_readremain = 0; + enable_irq(ts->client->irq); + + return lv1_readsize; +} + +static ssize_t sec_ts_gesture_status_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + mutex_lock(&ts->device_mutex); + memcpy(buf, ts->gesture_status, sizeof(ts->gesture_status)); + input_info(true, &ts->client->dev, + "%s: GESTURE STATUS %x %x %x %x %x %x\n", __func__, + ts->gesture_status[0], ts->gesture_status[1], ts->gesture_status[2], + ts->gesture_status[3], ts->gesture_status[4], ts->gesture_status[5]); + mutex_unlock(&ts->device_mutex); + + return sizeof(ts->gesture_status); +} + +static ssize_t sec_ts_regreadsize_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + mutex_lock(&ts->device_mutex); + + lv1cmd = buf[0]; + lv1_readsize = ((unsigned int)buf[4] << 24) | + ((unsigned int)buf[3] << 16) | ((unsigned int) buf[2] << 8) | ((unsigned int)buf[1] << 0); + lv1_readoffset = 0; + lv1_readremain = 0; + + mutex_unlock(&ts->device_mutex); + + return size; +} + +static ssize_t sec_ts_enter_recovery_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + struct sec_ts_plat_data *pdata = ts->plat_data; + int ret; + unsigned long on; + + ret = kstrtoul(buf, 10, &on); + if (ret != 0) { + input_err(true, &ts->client->dev, "%s: failed to read:%d\n", + __func__, ret); + return -EINVAL; + } + + if (on == 1) { + disable_irq(ts->client->irq); + gpio_free(pdata->irq_gpio); + + input_info(true, &ts->client->dev, "%s: gpio free\n", __func__); + if (gpio_is_valid(pdata->irq_gpio)) { + ret = gpio_request_one(pdata->irq_gpio, GPIOF_OUT_INIT_LOW, "sec,tsp_int"); + input_info(true, &ts->client->dev, "%s: gpio request one\n", __func__); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Unable to request tsp_int [%d]: %d\n", __func__, pdata->irq_gpio, ret); + } else { + input_err(true, &ts->client->dev, "%s: Failed to get irq gpio\n", __func__); + return -EINVAL; + } + + pdata->power(ts, false); + sec_ts_delay(100); + pdata->power(ts, true); + } else { + gpio_free(pdata->irq_gpio); + + if (gpio_is_valid(pdata->irq_gpio)) { + ret = gpio_request_one(pdata->irq_gpio, GPIOF_DIR_IN, "sec,tsp_int"); + if (ret) { + input_err(true, &ts->client->dev, "%s: Unable to request tsp_int [%d]\n", __func__, pdata->irq_gpio); + return -EINVAL; + } + } else { + input_err(true, &ts->client->dev, "%s: Failed to get irq gpio\n", __func__); + return -EINVAL; + } + + pdata->power(ts, false); + sec_ts_delay(500); + pdata->power(ts, true); + sec_ts_delay(500); + + /* AFE Calibration */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CALIBRATION_AMBIENT, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to write AFE_CAL\n", __func__); + + sec_ts_delay(1000); + enable_irq(ts->client->irq); + } + + sec_ts_read_information(ts); + + return size; +} + +static inline ssize_t sec_ts_show_error(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + input_err(true, &ts->client->dev, "%s: read only function, %s\n", __func__, attr->attr.name); + return -EPERM; +} + +static inline ssize_t sec_ts_store_error(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + input_err(true, &ts->client->dev, "%s: write only function, %s\n", __func__, attr->attr.name); + return -EPERM; +} + +int sec_ts_raw_device_init(struct sec_ts_data *ts) +{ + int ret; + + struct class *sec_class = NULL; + + sec_class = class_create(THIS_MODULE, "sec"); + ret = IS_ERR_OR_NULL(sec_class); + if (ret) { + input_err(true, &ts->client->dev, "%s: fail - class_create\n", __func__); + return; + } + + ts->dev = device_create(sec_class, NULL, 0, ts, "sec_ts"); + + ret = IS_ERR(ts->dev); + if (ret) { + input_err(true, &ts->client->dev, "%s: fail - device_create\n", __func__); + return ret; + } + + ret = sysfs_create_group(&ts->dev->kobj, &cmd_attr_group); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail - sysfs_create_group\n", __func__); + goto err_sysfs; + } + + return ret; +err_sysfs: + input_err(true, &ts->client->dev, "%s: fail\n", __func__); + return ret; +}