diff options
author | Roger Liao <rogerliao@google.com> | 2020-06-11 11:03:55 +0800 |
---|---|---|
committer | Roger Liao <rogerliao@google.com> | 2020-06-11 11:04:00 +0800 |
commit | 97eeffb353e48ff7a7cd4d44e7dedb40a9290bed (patch) | |
tree | cb1b6a813186a64b166372964810177b69475775 | |
parent | b8c1024889a6cd711e6677c6262555138982fdd8 (diff) | |
parent | 7fb646d439aa9b2751b08ff2e44dab2c9e6a4ec8 (diff) | |
download | sec_touch-97eeffb353e48ff7a7cd4d44e7dedb40a9290bed.tar.gz |
Merge branch 'android-msm-pixel-4.19' into android-msm-barbet-4.19
From build 6575781
Bug: 158714637
Signed-off-by: Roger Liao <rogerliao@google.com>
Change-Id: I7a015909f4376153405934a4959196ae01a9afcd
-rw-r--r-- | Kbuild | 3 | ||||
-rw-r--r-- | Kconfig | 23 | ||||
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | sec_cmd.c | 554 | ||||
-rw-r--r-- | sec_cmd.h | 161 | ||||
-rw-r--r-- | sec_ts.c | 4828 | ||||
-rw-r--r-- | sec_ts.h | 1162 | ||||
-rw-r--r-- | sec_ts_fac_spec.h | 518 | ||||
-rw-r--r-- | sec_ts_fn.c | 8058 | ||||
-rw-r--r-- | sec_ts_fw.c | 1601 | ||||
-rw-r--r-- | sec_ts_only_vendor.c | 544 |
11 files changed, 17459 insertions, 0 deletions
@@ -0,0 +1,3 @@ +obj-$(CONFIG_TOUCHSCREEN_SEC_TS) += sec_touch.o +sec_touch-objs += sec_cmd.o sec_ts.o sec_ts_fw.o sec_ts_fn.o \ + sec_ts_only_vendor.o @@ -0,0 +1,23 @@ +# +# Samsung Electronics TOUCH driver configuration +# + +config TOUCHSCREEN_SEC_TS + tristate "Samsung Electronics Touchscreen" + depends on I2C + select TOUCHSCREEN_HEATMAP + 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/Makefile b/Makefile new file mode 100644 index 0000000..dbc92ee --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build +M ?= $(shell pwd) + +KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_SEC_TS=m + +modules modules_install clean: + $(MAKE) -C $(KERNEL_SRC) M=$(M) $(KBUILD_OPTIONS) $(@) diff --git a/sec_cmd.c b/sec_cmd.c new file mode 100644 index 0000000..fe452d4 --- /dev/null +++ b/sec_cmd.c @@ -0,0 +1,554 @@ +/* + * sec_cmd.c - samsung factory command driver + * + * Copyright (C) 2014 Samsung Electronics + * + * 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_cmd.h" + +#if defined USE_SEC_CMD_QUEUE +static void sec_cmd_store_function(struct sec_cmd_data *data); +#endif + +void sec_cmd_set_cmd_exit(struct sec_cmd_data *data) +{ + atomic_set(&data->cmd_is_running, 0); + +#ifdef USE_SEC_CMD_QUEUE + mutex_lock(&data->fifo_lock); + if (kfifo_len(&data->cmd_queue)) { + pr_info("%s %s: do next cmd, left cmd[%d]\n", SECLOG, __func__, + (int)(kfifo_len(&data->cmd_queue) / + sizeof(struct command))); + mutex_unlock(&data->fifo_lock); + + atomic_set(&data->cmd_is_running, 1); + + data->cmd_state = SEC_CMD_STATUS_RUNNING; + sec_cmd_store_function(data); + + } else { + mutex_unlock(&data->fifo_lock); + } +#endif +} + +void sec_cmd_set_default_result(struct sec_cmd_data *data) +{ + char delim = ':'; + + memset(data->cmd_result, 0x00, SEC_CMD_RESULT_STR_LEN); + memcpy(data->cmd_result, data->cmd, SEC_CMD_STR_LEN); + strncat(data->cmd_result, &delim, 1); +} + +void sec_cmd_set_cmd_result(struct sec_cmd_data *data, char *buff, int len) +{ + strlcat(data->cmd_result, buff, SEC_CMD_RESULT_STR_LEN); +} + +#ifndef USE_SEC_CMD_QUEUE +static ssize_t cmd_store(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count) +{ + struct sec_cmd_data *data = dev_get_drvdata(dev); + char *cur, *start, *end; + char buff[SEC_CMD_STR_LEN] = { 0 }; + size_t len; + struct sec_cmd *sec_cmd_ptr = NULL; + char delim = ','; + bool cmd_found = false; + unsigned int i, param_cnt = 0; + + if (!data) { + pr_err("%s %s: No platform data found\n", SECLOG, __func__); + return -EINVAL; + } + + if (count >= SEC_CMD_STR_LEN) { + pr_err("%s %s: cmd length is over (%s,%d)!!\n", + SECLOG, __func__, buf, (int)count); + return -EINVAL; + } + + if (atomic_cmpxchg(&data->cmd_is_running, 0, 1)) { + pr_err("%s %s: other cmd is running.\n", SECLOG, __func__); + return -EBUSY; + } + + data->cmd_state = SEC_CMD_STATUS_RUNNING; + for (i = 0; i < ARRAY_SIZE(data->cmd_param); i++) + data->cmd_param[i] = 0; + + len = count; + if (*(buf + len - 1) == '\n') + len--; + + memset(data->cmd, 0x00, ARRAY_SIZE(data->cmd)); + memcpy(data->cmd, buf, len); + + cur = strchr(buf, (int)delim); + if (cur) + memcpy(buff, buf, cur - buf); + else + memcpy(buff, buf, len); + + pr_debug("%s %s: COMMAND = %s\n", SECLOG, __func__, buff); + + /* find command */ + list_for_each_entry(sec_cmd_ptr, &data->cmd_list_head, list) { + if (!strncmp(buff, sec_cmd_ptr->cmd_name, SEC_CMD_STR_LEN)) { + cmd_found = true; + break; + } + } + + /* set not_support_cmd */ + if (!cmd_found) { + list_for_each_entry(sec_cmd_ptr, &data->cmd_list_head, list) { + if (!strncmp("not_support_cmd", sec_cmd_ptr->cmd_name, + SEC_CMD_STR_LEN)) + break; + } + } + + /* parsing parameters */ + if (cur && cmd_found) { + cur++; + start = cur; + memset(buff, 0x00, ARRAY_SIZE(buff)); + + do { + if (*cur == delim || cur - buf == len) { + end = cur; + memcpy(buff, start, end - start); + *(buff + + strnlen(buff, ARRAY_SIZE(buff))) = '\0'; + if (kstrtoint(buff, 10, + data->cmd_param + param_cnt) < 0) + goto err_out; + start = cur + 1; + memset(buff, 0x00, ARRAY_SIZE(buff)); + param_cnt++; + } + cur++; + } while ((cur - buf <= len) && (param_cnt < SEC_CMD_PARAM_NUM)); + } + + if (cmd_found) { + pr_info("%s %s: cmd = %s", + SECLOG, __func__, sec_cmd_ptr->cmd_name); + for (i = 0; i < param_cnt; i++) { + if (i == 0) + pr_cont(" param ="); + pr_cont(" %d", data->cmd_param[i]); + } + pr_cont("\n"); + } else { + pr_info("%s %s: cmd = %s(%s)\n", + SECLOG, __func__, buff, sec_cmd_ptr->cmd_name); + } + + sec_cmd_ptr->cmd_func(data); + sec_cmd_set_cmd_exit(data); + +err_out: + return count; +} + +#else /* defined USE_SEC_CMD_QUEUE */ +static void sec_cmd_store_function(struct sec_cmd_data *data) +{ + char *cur, *start, *end; + char buff[SEC_CMD_STR_LEN] = { 0 }; + int len, i; + struct sec_cmd *sec_cmd_ptr = NULL; + char delim = ','; + bool cmd_found = false; + int param_cnt = 0; + int ret; + const char *buf; + size_t count; + struct command cmd = { {0} }; + + if (!data) { + pr_err("%s %s: No platform data found\n", SECLOG, __func__); + return; + } + + mutex_lock(&data->fifo_lock); + if (kfifo_len(&data->cmd_queue)) { + ret = kfifo_out(&data->cmd_queue, &cmd, sizeof(struct command)); + if (!ret) { + pr_err("%s %s: kfifo_out failed, it seems empty, ret=%d\n", + SECLOG, __func__, ret); + mutex_unlock(&data->fifo_lock); + return; + } + } else { + pr_err("%s %s: left cmd is nothing\n", SECLOG, __func__); + mutex_unlock(&data->fifo_lock); + return; + } + mutex_unlock(&data->fifo_lock); + + buf = cmd.cmd; + count = strlen(buf); + + for (i = 0; i < (int)ARRAY_SIZE(data->cmd_param); i++) + data->cmd_param[i] = 0; + + len = (int)count; + if (*(buf + len - 1) == '\n') + len--; + + memset(data->cmd, 0x00, ARRAY_SIZE(data->cmd)); + memcpy(data->cmd, buf, len); + + cur = strchr(buf, (int)delim); + if (cur) + memcpy(buff, buf, cur - buf); + else + memcpy(buff, buf, len); + + pr_debug("%s %s: COMMAND : %s\n", SECLOG, __func__, buff); + + /* find command */ + list_for_each_entry(sec_cmd_ptr, &data->cmd_list_head, list) { + if (!strncmp(buff, sec_cmd_ptr->cmd_name, SEC_CMD_STR_LEN)) { + cmd_found = true; + break; + } + } + + /* set not_support_cmd */ + if (!cmd_found) { + list_for_each_entry(sec_cmd_ptr, &data->cmd_list_head, list) { + if (!strncmp("not_support_cmd", sec_cmd_ptr->cmd_name, + SEC_CMD_STR_LEN)) + break; + } + } + + /* parsing parameters */ + if (cur && cmd_found) { + cur++; + start = cur; + memset(buff, 0x00, ARRAY_SIZE(buff)); + + do { + if (*cur == delim || cur - buf == len) { + end = cur; + memcpy(buff, start, end - start); + *(buff + + strnlen(buff, ARRAY_SIZE(buff))) = '\0'; + if (kstrtoint(buff, 10, + data->cmd_param + param_cnt) < 0) + return; + start = cur + 1; + memset(buff, 0x00, ARRAY_SIZE(buff)); + param_cnt++; + } + cur++; + } while ((cur - buf <= len) && (param_cnt < SEC_CMD_PARAM_NUM)); + } + + if (cmd_found) { + pr_info("%s %s: cmd = %s", + SECLOG, __func__, sec_cmd_ptr->cmd_name); + for (i = 0; i < param_cnt; i++) { + if (i == 0) + pr_cont(" param ="); + pr_cont(" %d", data->cmd_param[i]); + } + pr_cont("\n"); + } else { + pr_info("%s %s: cmd = %s(%s)\n", + SECLOG, __func__, buff, sec_cmd_ptr->cmd_name); + } + + sec_cmd_ptr->cmd_func(data); + +} + +static ssize_t cmd_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sec_cmd_data *data = dev_get_drvdata(dev); + struct command cmd = { {0} }; + int queue_size; + + if (!data) { + pr_err("%s %s: No platform data found\n", SECLOG, __func__); + return -EINVAL; + } + + if (count >= SEC_CMD_STR_LEN) { + pr_err("%s %s: cmd length is over (%s,%d)!!\n", SECLOG, + __func__, buf, (int)count); + return -EINVAL; + } + + strlcpy(cmd.cmd, buf, sizeof(cmd.cmd)); + + mutex_lock(&data->fifo_lock); + queue_size = (kfifo_len(&data->cmd_queue) / sizeof(struct command)); + + if (kfifo_avail(&data->cmd_queue) && (queue_size < SEC_CMD_MAX_QUEUE)) { + kfifo_in(&data->cmd_queue, &cmd, sizeof(struct command)); + pr_info("%s %s: push cmd: %s\n", SECLOG, __func__, cmd.cmd); + } else { + pr_err("%s %s: cmd_queue is full!!\n", SECLOG, __func__); + + kfifo_reset(&data->cmd_queue); + pr_err("%s %s: cmd_queue is reset!!\n", SECLOG, __func__); + mutex_unlock(&data->fifo_lock); + + atomic_set(&data->cmd_is_running, 0); + + return -ENOSPC; + } + + if (atomic_cmpxchg(&data->cmd_is_running, 0, 1)) { + pr_err("%s %s: other cmd is running. Wait until previous cmd is done[%d]\n", + SECLOG, __func__, + (int)(kfifo_len(&data->cmd_queue) / + sizeof(struct command))); + mutex_unlock(&data->fifo_lock); + + return -EBUSY; + } + + mutex_unlock(&data->fifo_lock); + + data->cmd_state = SEC_CMD_STATUS_RUNNING; + sec_cmd_store_function(data); + + return count; +} +#endif + +static ssize_t cmd_status_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sec_cmd_data *data = dev_get_drvdata(dev); + char buff[16] = { 0 }; + + if (!data) { + pr_err("%s %s: No platform data found\n", SECLOG, __func__); + return -EINVAL; + } + + if (data->cmd_state == SEC_CMD_STATUS_WAITING) + snprintf(buff, sizeof(buff), "WAITING"); + + else if (data->cmd_state == SEC_CMD_STATUS_RUNNING) + snprintf(buff, sizeof(buff), "RUNNING"); + + else if (data->cmd_state == SEC_CMD_STATUS_OK) + snprintf(buff, sizeof(buff), "OK"); + + else if (data->cmd_state == SEC_CMD_STATUS_FAIL) + snprintf(buff, sizeof(buff), "FAIL"); + + else if (data->cmd_state == SEC_CMD_STATUS_NOT_APPLICABLE) + snprintf(buff, sizeof(buff), "NOT_APPLICABLE"); + + pr_debug("%s %s: %d, %s\n", SECLOG, __func__, data->cmd_state, buff); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); +} + +static ssize_t cmd_result_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sec_cmd_data *data = dev_get_drvdata(dev); + int size; + + if (!data) { + pr_err("%s %s: No platform data found\n", SECLOG, __func__); + return -EINVAL; + } + + data->cmd_state = SEC_CMD_STATUS_WAITING; + pr_info("%s %s: %s\n", SECLOG, __func__, data->cmd_result); + size = snprintf(buf, SEC_CMD_RESULT_STR_LEN, "%s\n", data->cmd_result); + + sec_cmd_set_cmd_exit(data); + + return size; +} + +static ssize_t cmd_list_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *data = dev_get_drvdata(dev); + struct sec_cmd *sec_cmd_ptr = NULL; + char *buffer = NULL; + char buffer_name[SEC_CMD_STR_LEN]; + int ret = 0; + + buffer = kzalloc(data->cmd_buffer_size + 30, GFP_KERNEL); + if (!buffer) { + pr_err("%s %s: No sec_cmd_list buffer\n", SECLOG, __func__); + return -EINVAL; + } + + snprintf(buffer, 30, "++factory command list++\n"); + + list_for_each_entry(sec_cmd_ptr, &data->cmd_list_head, list) { + if (strncmp(sec_cmd_ptr->cmd_name, "not_support_cmd", 15)) { + snprintf(buffer_name, SEC_CMD_STR_LEN, "%s\n", + sec_cmd_ptr->cmd_name); + strncat(buffer, buffer_name, SEC_CMD_STR_LEN); + } + } + + ret = snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buffer); + kfree(buffer); + + return ret; +} + +static DEVICE_ATTR_WO(cmd); +static DEVICE_ATTR_RO(cmd_status); +static DEVICE_ATTR_RO(cmd_result); +static DEVICE_ATTR_RO(cmd_list); + +static struct attribute *sec_fac_attrs[] = { + &dev_attr_cmd.attr, + &dev_attr_cmd_status.attr, + &dev_attr_cmd_result.attr, + &dev_attr_cmd_list.attr, + NULL, +}; + +static struct attribute_group sec_fac_attr_group = { + .attrs = sec_fac_attrs, +}; + + +int sec_cmd_init(struct sec_cmd_data *data, struct sec_cmd *cmds, + int len, int devt) +{ + const char *dev_name; + int ret, i; + + INIT_LIST_HEAD(&data->cmd_list_head); + + data->cmd_buffer_size = 0; + for (i = 0; i < len; i++) { + list_add_tail(&cmds[i].list, &data->cmd_list_head); + if (cmds[i].cmd_name) + data->cmd_buffer_size += strlen(cmds[i].cmd_name) + 1; + } + + atomic_set(&data->cmd_is_running, 0); + +#ifdef USE_SEC_CMD_QUEUE + if (kfifo_alloc(&data->cmd_queue, + SEC_CMD_MAX_QUEUE * sizeof(struct command), GFP_KERNEL)) { + pr_err("%s %s: failed to alloc queue for cmd\n", + SECLOG, __func__); + goto err_alloc_queue; + } + mutex_init(&data->fifo_lock); +#endif + + if (devt == SEC_CLASS_DEVT_TSP) { + dev_name = SEC_CLASS_DEV_NAME_TSP; + + } else if (devt == SEC_CLASS_DEVT_TKEY) { + dev_name = SEC_CLASS_DEV_NAME_TKEY; + + } else if (devt == SEC_CLASS_DEVT_WACOM) { + dev_name = SEC_CLASS_DEV_NAME_WACOM; + + } else { + pr_err("%s %s: not defined devt=%d\n", SECLOG, __func__, devt); + goto err_get_dev_name; + } + +#ifdef CONFIG_SEC_SYSFS + data->fac_dev = sec_device_create(data, dev_name); +#else + data->fac_dev = device_create(sec_class, NULL, devt, data, dev_name); +#endif + if (IS_ERR(data->fac_dev)) { + pr_err("%s %s: failed to create device for the sysfs\n", + SECLOG, __func__); + goto err_sysfs_device; + } + + dev_set_drvdata(data->fac_dev, data); + + ret = sysfs_create_group(&data->fac_dev->kobj, &sec_fac_attr_group); + if (ret < 0) { + pr_err("%s %s: failed to create sysfs group\n", + SECLOG, __func__); + goto err_sysfs_group; + } + + return 0; + + sysfs_remove_group(&data->fac_dev->kobj, &sec_fac_attr_group); +err_sysfs_group: +#ifdef CONFIG_SEC_SYSFS + sec_device_destroy(data->fac_dev->devt); +#else + device_destroy(sec_class, devt); +#endif +err_sysfs_device: +err_get_dev_name: +#ifdef USE_SEC_CMD_QUEUE + mutex_destroy(&data->fifo_lock); + kfifo_free(&data->cmd_queue); +err_alloc_queue: +#endif + list_del(&data->cmd_list_head); + return -ENODEV; +} + +void sec_cmd_exit(struct sec_cmd_data *data, int devt) +{ +#ifdef USE_SEC_CMD_QUEUE + struct command cmd = { {0} }; + int ret; +#endif + + pr_info("%s %s", SECLOG, __func__); + sysfs_remove_group(&data->fac_dev->kobj, &sec_fac_attr_group); + dev_set_drvdata(data->fac_dev, NULL); +#ifdef CONFIG_SEC_SYSFS + sec_device_destroy(data->fac_dev->devt); +#else + device_destroy(sec_class, devt); +#endif +#ifdef USE_SEC_CMD_QUEUE + mutex_lock(&data->fifo_lock); + while (kfifo_len(&data->cmd_queue)) { + ret = kfifo_out(&data->cmd_queue, &cmd, sizeof(struct command)); + if (!ret) { + pr_err("%s %s: kfifo_out failed, it seems empty, ret=%d\n", + SECLOG, __func__, ret); + } + pr_info("%s %s: remove pending commands: %s", + SECLOG, __func__, cmd.cmd); + } + mutex_unlock(&data->fifo_lock); + mutex_destroy(&data->fifo_lock); + kfifo_free(&data->cmd_queue); +#endif + list_del(&data->cmd_list_head); +} + +MODULE_DESCRIPTION("Samsung factory command"); +MODULE_LICENSE("GPL"); + + diff --git a/sec_cmd.h b/sec_cmd.h new file mode 100644 index 0000000..5299cf4 --- /dev/null +++ b/sec_cmd.h @@ -0,0 +1,161 @@ + +#ifndef _SEC_CMD_H_ +#define _SEC_CMD_H_ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/workqueue.h> +#include <linux/stat.h> +#include <linux/err.h> +#include <linux/input.h> +#ifdef CONFIG_SEC_SYSFS +#include <linux/sec_sysfs.h> +#endif + +#ifndef CONFIG_SEC_FACTORY +#include <linux/kfifo.h> +#endif + +#ifndef CONFIG_SEC_SYSFS +extern struct class *sec_class; +#endif + +#define SEC_CLASS_DEVT_TSP 10 +#define SEC_CLASS_DEVT_TKEY 11 +#define SEC_CLASS_DEVT_WACOM 12 + +#define SEC_CLASS_DEV_NAME_TSP "tsp" +#define SEC_CLASS_DEV_NAME_TKEY "sec_touchkey" +#define SEC_CLASS_DEV_NAME_WACOM "sec_epen" + +#define SEC_CMD(name, func) .cmd_name = name, .cmd_func = func + +#define SEC_CMD_BUF_SIZE (4096 - 1) +#define SEC_CMD_STR_LEN 256 +#define SEC_CMD_RESULT_STR_LEN (4096 - 1) +#define SEC_CMD_PARAM_NUM 8 + +struct sec_cmd { + struct list_head list; + const char *cmd_name; + void (*cmd_func)(void *device_data); +}; + +enum SEC_CMD_STATUS { + SEC_CMD_STATUS_WAITING = 0, + SEC_CMD_STATUS_RUNNING, // = 1 + SEC_CMD_STATUS_OK, // = 2 + SEC_CMD_STATUS_FAIL, // = 3 + SEC_CMD_STATUS_NOT_APPLICABLE, // = 4 +}; + +#ifdef USE_SEC_CMD_QUEUE +#define SEC_CMD_MAX_QUEUE 10 + +struct command { + char cmd[SEC_CMD_STR_LEN]; +}; +#endif + +struct sec_cmd_data { + struct device *fac_dev; + struct list_head cmd_list_head; + u8 cmd_state; + char cmd[SEC_CMD_STR_LEN]; + int cmd_param[SEC_CMD_PARAM_NUM]; + char cmd_result[SEC_CMD_RESULT_STR_LEN]; + int cmd_buffer_size; + atomic_t cmd_is_running; +#ifdef USE_SEC_CMD_QUEUE + struct kfifo cmd_queue; + struct mutex fifo_lock; +#endif +}; + +extern void sec_cmd_set_cmd_exit(struct sec_cmd_data *data); +extern void sec_cmd_set_default_result(struct sec_cmd_data *data); +extern void sec_cmd_set_cmd_result(struct sec_cmd_data *data, char *buff, + int len); +extern int sec_cmd_init(struct sec_cmd_data *data, + struct sec_cmd *cmds, int len, int devt); +extern void sec_cmd_exit(struct sec_cmd_data *data, int devt); + +/* + * sec Log + */ +#define SECLOG "[sec_input]" +#define INPUT_LOG_BUF_SIZE 512 + +#ifdef CONFIG_SEC_DEBUG_TSP_LOG +#include <linux/sec_debug.h> + +#define input_dbg(mode, dev, fmt, ...) \ +({ \ + static char input_log_buf[INPUT_LOG_BUF_SIZE]; \ + snprintf(input_log_buf, sizeof(input_log_buf), "%s %s", SECLOG, fmt); \ + dev_dbg(dev, input_log_buf, ## __VA_ARGS__); \ + if (mode) { \ + if (dev) \ + snprintf(input_log_buf, sizeof(input_log_buf), "%s %s",\ + dev_driver_string(dev), dev_name(dev));\ + else \ + snprintf(input_log_buf, sizeof(input_log_buf), "NULL");\ + sec_debug_tsp_log_msg(input_log_buf, fmt, ## __VA_ARGS__); \ + } \ +}) +#define input_info(mode, dev, fmt, ...) \ +({ \ + static char input_log_buf[INPUT_LOG_BUF_SIZE]; \ + snprintf(input_log_buf, sizeof(input_log_buf), "%s %s", SECLOG, fmt); \ + dev_info(dev, input_log_buf, ## __VA_ARGS__); \ + if (mode) { \ + if (dev) \ + snprintf(input_log_buf, sizeof(input_log_buf), "%s %s",\ + dev_driver_string(dev), dev_name(dev));\ + else \ + snprintf(input_log_buf, sizeof(input_log_buf), "NULL");\ + sec_debug_tsp_log_msg(input_log_buf, fmt, ## __VA_ARGS__); \ + } \ +}) +#define input_err(mode, dev, fmt, ...) \ +({ \ + static char input_log_buf[INPUT_LOG_BUF_SIZE]; \ + snprintf(input_log_buf, sizeof(input_log_buf), "%s %s", SECLOG, fmt); \ + dev_err(dev, input_log_buf, ## __VA_ARGS__); \ + if (mode) { \ + if (dev) \ + snprintf(input_log_buf, sizeof(input_log_buf), "%s %s",\ + dev_driver_string(dev), dev_name(dev));\ + else \ + snprintf(input_log_buf, sizeof(input_log_buf), "NULL");\ + sec_debug_tsp_log_msg(input_log_buf, fmt, ## __VA_ARGS__); \ + } \ +}) +#define input_log_fix() {} +#else +#define input_dbg(mode, dev, fmt, ...) \ +({ \ + static char input_log_buf[INPUT_LOG_BUF_SIZE]; \ + snprintf(input_log_buf, sizeof(input_log_buf), "%s %s", SECLOG, fmt); \ + dev_dbg(dev, input_log_buf, ## __VA_ARGS__); \ +}) +#define input_info(mode, dev, fmt, ...) \ +({ \ + static char input_log_buf[INPUT_LOG_BUF_SIZE]; \ + snprintf(input_log_buf, sizeof(input_log_buf), "%s %s", SECLOG, fmt); \ + dev_info(dev, input_log_buf, ## __VA_ARGS__); \ +}) +#define input_err(mode, dev, fmt, ...) \ +({ \ + static char input_log_buf[INPUT_LOG_BUF_SIZE]; \ + snprintf(input_log_buf, sizeof(input_log_buf), "%s %s", SECLOG, fmt); \ + dev_err(dev, input_log_buf, ## __VA_ARGS__); \ +}) +#define input_log_fix() {} +#endif + +#endif /* _SEC_CMD_H_ */ + + diff --git a/sec_ts.c b/sec_ts.c new file mode 100644 index 0000000..f3a0918 --- /dev/null +++ b/sec_ts.c @@ -0,0 +1,4828 @@ +/* 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" + +/* Switch GPIO values */ +#define SEC_SWITCH_GPIO_VALUE_SLPI_MASTER 1 +#define SEC_SWITCH_GPIO_VALUE_AP_MASTER 0 + +struct sec_ts_data *ts_dup; + +#ifndef CONFIG_SEC_SYSFS +/* Declare extern sec_class */ +struct class *sec_class; +#endif + +#ifdef USE_POWER_RESET_WORK +static void sec_ts_reset_work(struct work_struct *work); +#endif +static void sec_ts_fw_update_work(struct work_struct *work); +static void sec_ts_suspend_work(struct work_struct *work); +static void sec_ts_resume_work(struct work_struct *work); +static void sec_ts_charger_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); + +#ifndef I2C_INTERFACE +int sec_ts_spi_delay(u8 reg); +#endif + +int sec_ts_write(struct sec_ts_data *ts, u8 reg, u8 *data, int len) +{ + u8 *buf; + int ret; + unsigned char retry; +#ifdef I2C_INTERFACE + struct i2c_msg msg; +#else + struct spi_message msg; + struct spi_transfer transfer[1] = { { 0 } }; + unsigned int i; + unsigned int spi_len = 0; + unsigned char checksum = 0x0; +#endif + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, + "%s: POWER_STATUS : OFF\n", __func__); + goto err; + } + +#ifdef I2C_INTERFACE + if (len + 1 > sizeof(ts->io_write_buf)) { + input_err(true, &ts->client->dev, + "%s: len is larger than buffer size\n", __func__); + return -EINVAL; + } +#else + /* add 3 zero stuffing tx bytes at last */ + if (SEC_TS_SPI_HEADER_SIZE + 1 + len + SEC_TS_SPI_CHECKSUM_SIZE + 3 > + sizeof(ts->io_write_buf)) { + input_err(true, &ts->client->dev, + "%s: len is larger than buffer size\n", __func__); + return -EINVAL; + } +#endif + + mutex_lock(&ts->io_mutex); + + buf = ts->io_write_buf; +#ifdef I2C_INTERFACE + buf[0] = reg; + memcpy(buf + 1, data, len); + + msg.addr = ts->client->addr; + msg.flags = 0; + msg.len = len + 1; + msg.buf = buf; +#else + + buf[0] = SEC_TS_SPI_SYNC_CODE; + buf[1] = ((len + 1) >> 8) & 0xFF; + buf[2] = (len + 1) & 0xFF; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = reg; + memcpy(buf + SEC_TS_SPI_HEADER_SIZE + 1, data, len); + + spi_len = SEC_TS_SPI_HEADER_SIZE + 1 + len; + // spi_len = SPI header size(5)+register(1)+data size(len) + for (i = 0; i < spi_len ; i++) + checksum += buf[i]; + buf[spi_len++] = checksum; + // spi_len += checksum(1) + + spi_message_init(&msg); + + /* add 3 zero stuffing tx bytes at last */ + memset(ts->io_write_buf + spi_len, 0x00, 3); + /* spi transfer size should be multiple of 4 + **/ + spi_len = (spi_len + 3) & ~3; + transfer[0].len = spi_len; + transfer[0].tx_buf = buf; + transfer[0].rx_buf = NULL; + spi_message_add_tail(&transfer[0], &msg); + +#ifdef SEC_TS_DEBUG_IO + input_info(true, &ts->client->dev, "%s: ", __func__); +// for (i = 0; i < SEC_TS_SPI_HEADER_SIZE + 1 + len + 1; i++) + for (i = 0; i < 8; i++) + input_info(true, &ts->client->dev, "%X ", buf[i]); + input_info(true, &ts->client->dev, "\n"); +#endif + +#endif + + for (retry = 0; retry < SEC_TS_IO_RETRY_CNT; retry++) { +#ifdef I2C_INTERFACE + if ((ret = i2c_transfer(ts->client->adapter, &msg, 1)) == 1) + break; +#else + if ((ret = spi_sync(ts->client, &msg)) == 0) + break; +#endif + + 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->io_mutex); + goto err; + } + + usleep_range(1 * 1000, 1 * 1000); + + if (retry > 1) { + input_err(true, &ts->client->dev, + "%s: retry %d\n", __func__, retry + 1); + ts->comm_err_count++; + } + } + + mutex_unlock(&ts->io_mutex); + + if (retry == SEC_TS_IO_RETRY_CNT) { + input_err(true, &ts->client->dev, + "%s: 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 + } + +#ifdef I2C_INTERFACE + if (ret == 1) +#else + if (ret == 0) +#endif + return 0; +err: + return -EIO; +} + +static int sec_ts_read_internal(struct sec_ts_data *ts, u8 reg, + u8 *data, int len, bool dma_safe) +{ + u8 *buf; + int ret; + unsigned char retry; +#ifdef I2C_INTERFASCE + struct i2c_msg msg[2]; +#else + struct spi_message msg; + struct spi_transfer transfer[1] = { { 0 } }; + unsigned int i; + unsigned int spi_write_len = 0, spi_read_len = 0; + unsigned char write_checksum = 0x0, read_checksum = 0x0; + int copy_size = 0, copy_cur = 0; +#endif + int remain = len; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, + "%s: POWER_STATUS : OFF\n", __func__); + goto err; + } + +#ifndef I2C_INTERFACE + /* add 3 zero stuffing tx bytes at last */ + if (SEC_TS_SPI_HEADER_SIZE + 1 + SEC_TS_SPI_CHECKSUM_SIZE + 3 > + sizeof(ts->io_write_buf)) { + input_err(true, &ts->client->dev, + "%s: len is larger than buffer size\n", __func__); + return -EINVAL; + } +#endif + + if (len > sizeof(ts->io_read_buf) && dma_safe == false) { + input_err(true, &ts->client->dev, + "%s: len %d over pre-allocated size %d\n", + __func__, len, IO_PREALLOC_READ_BUF_SZ); + return -ENOSPC; + } + + mutex_lock(&ts->io_mutex); + + buf = ts->io_write_buf; +#ifdef I2C_INTERFACE + 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; + if (dma_safe == false) + msg[1].buf = ts->io_read_buf; + else + msg[1].buf = data; +#else + + buf[0] = SEC_TS_SPI_SYNC_CODE; + buf[1] = 0x00; + buf[2] = 0x01; + buf[3] = (len >> 8) & 0xFF; + buf[4] = len & 0xFF; + buf[5] = reg; + + spi_write_len = SEC_TS_SPI_HEADER_SIZE + 1; + for (i = 0; i < spi_write_len; i++) + write_checksum += buf[i]; + buf[spi_write_len] = write_checksum; + spi_write_len += SEC_TS_SPI_CHECKSUM_SIZE; + /* add 3 zero stuffing tx bytes at last */ + memset(ts->io_write_buf + spi_write_len, 0x00, 3); + spi_write_len = (spi_write_len + 3) & ~3; + + spi_read_len = len + + SEC_TS_SPI_READ_HEADER_SIZE + SEC_TS_SPI_CHECKSUM_SIZE; + spi_read_len = (spi_read_len + 3) & ~3; +#endif + if (len <= ts->io_burstmax) { +#ifdef I2C_INTERFACE + for (retry = 0; retry < SEC_TS_IO_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->io_mutex); + goto err; + } + + if (retry > 1) { + input_err(true, &ts->client->dev, + "%s: retry %d\n", __func__, retry + 1); + ts->comm_err_count++; + } + } + if (ret == 2 && dma_safe == false) + memcpy(data, + ts->io_read_buf[SEC_TS_SPI_READ_HEADER_SIZE], + len); +#else + for (retry = 0; retry < SEC_TS_IO_RETRY_CNT; retry++) { + spi_message_init(&msg); + // spi transfer size should be multiple of 4 + transfer[0].len = spi_write_len; + transfer[0].tx_buf = buf; + transfer[0].rx_buf = NULL; + spi_message_add_tail(&transfer[0], &msg); + + ret = spi_sync(ts->client, &msg); +#ifdef SEC_TS_DEBUG_IO + input_info(true, &ts->client->dev, + "%s: spi write buf %X %X %X %X %X %X %X\n", + __func__, buf[0], buf[1], buf[2], + buf[3], buf[4], buf[5], buf[6]); +#endif + // write fail + if (ret != 0) { + ret = -EIO; + + input_err(true, &ts->client->dev, + "%s: spi write retry %d\n", + __func__, retry + 1); + ts->comm_err_count++; + + 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->io_mutex); + goto err; + } + + if (retry == SEC_TS_IO_RETRY_CNT - 1) { + input_err(true, &ts->client->dev, + "%s: write reg retry over retry limit, skip read\n", + __func__); + goto skip_spi_read; + } + + continue; + } + + usleep_range(sec_ts_spi_delay(reg), + sec_ts_spi_delay(reg)); + + // read sequence start + spi_message_init(&msg); + transfer[0].len = spi_read_len; + transfer[0].tx_buf = NULL; + transfer[0].rx_buf = ts->io_read_buf; + spi_message_add_tail(&transfer[0], &msg); + ret = spi_sync(ts->client, &msg); + + for (i = 0, read_checksum = 0x0; + i < (SEC_TS_SPI_READ_HEADER_SIZE + len); + i++) + read_checksum += ts->io_read_buf[i]; + +#ifdef SEC_TS_DEBUG_IO + input_info(true, &ts->client->dev, "%s: ", __func__); +// for (i = 0; i < spi_read_len; i++) + for (i = 0; i < 8; i++) + input_info(true, &ts->client->dev, + "%X ", + ts->io_read_buf[i]); + input_info(true, &ts->client->dev, + "\n%s: checksum = %X", + __func__, read_checksum); +#endif + // read fail + if (ret != 0 || + ts->io_read_buf[0] != SEC_TS_SPI_SYNC_CODE || + reg != ts->io_read_buf[5] || + // ts->io_read_buf[6] != SEC_TS_SPI_CMD_OK || + read_checksum != + ts->io_read_buf[ + SEC_TS_SPI_READ_HEADER_SIZE + + len]) { + + ret = -EIO; + + input_err(true, &ts->client->dev, + "%s: retry %d\n", + __func__, retry + 1); + ts->comm_err_count++; + + 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->io_mutex); + goto err; + } + continue; + } else + break; + } + if (ret == 0) + memcpy(data, ts->io_read_buf + + SEC_TS_SPI_READ_HEADER_SIZE, len); +#endif //I2C_INTERFACE + } else { + /* + * read buffer is 256 byte. do not support long buffer over + * than 256. So, try to separate reading data about 256 bytes. + **/ +#ifdef I2C_INTERFACE + for (retry = 0; retry < SEC_TS_IO_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->io_mutex); + goto err; + } + + if (retry > 1) { + input_err(true, &ts->client->dev, + "%s: retry %d\n", + __func__, retry + 1); + ts->comm_err_count++; + } + } + + do { + if (remain > ts->io_burstmax) + msg[1].len = ts->io_burstmax; + else + msg[1].len = remain; + + remain -= ts->io_burstmax; + + for (retry = 0; retry < SEC_TS_IO_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->io_mutex); + goto err; + } + + if (retry > 1) { + input_err(true, &ts->client->dev, + "%s: retry %d\n", + __func__, retry + 1); + ts->comm_err_count++; + } + } + + msg[1].buf += msg[1].len; + + } while (remain > 0); + + if (ret == 1 && dma_safe == false) + memcpy(data, ts->io_read_buf, len); +#else + for (retry = 0; retry < SEC_TS_IO_RETRY_CNT; retry++) { + spi_message_init(&msg); + // spi transfer size should be multiple of 4 + transfer[0].len = spi_write_len; + transfer[0].tx_buf = buf; + transfer[0].rx_buf = NULL; + spi_message_add_tail(&transfer[0], &msg); + + ret = spi_sync(ts->client, &msg); + + // write fail + if (ret != 0) { + ret = -EIO; + + input_err(true, &ts->client->dev, + "%s: spi write retry %d\n", + __func__, retry + 1); + ts->comm_err_count++; + + 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->io_mutex); + goto err; + } + + if (retry == SEC_TS_IO_RETRY_CNT - 1) { + input_err(true, &ts->client->dev, + "%s: write reg retry over retry limit, skip read\n", + __func__); + goto skip_spi_read; + } + + continue; + } + + usleep_range(sec_ts_spi_delay(reg), + sec_ts_spi_delay(reg)); + + copy_size = 0; + remain = spi_read_len; + do { + if (remain > ts->io_burstmax) + copy_cur = ts->io_burstmax; + else + copy_cur = remain; + + spi_message_init(&msg); + + transfer[0].len = copy_cur; + transfer[0].tx_buf = NULL; + transfer[0].rx_buf = + &ts->io_read_buf[copy_size]; + // CS needs to stay low until read seq. is done + transfer[0].cs_change = + (remain > ts->io_burstmax) ? 1 : 0; + + spi_message_add_tail(&transfer[0], &msg); + + copy_size += copy_cur; + remain -= copy_cur; + + ret = spi_sync(ts->client, &msg); +#ifdef SEC_TS_DEBUG_IO + input_info(true, &ts->client->dev, + "%s: ", __func__); + for (i = 0; i < 8; i++) + input_info(true, + &ts->client->dev, "%X ", + ts->io_read_buf[i]); + input_info(true, &ts->client->dev, + "\n%s: checksum = %X", + __func__, read_checksum); +#endif + + if (ret != 0) { + ret = -EIO; + + input_err(true, &ts->client->dev, + "%s: retry %d\n", + __func__, retry + 1); + ts->comm_err_count++; + + 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->io_mutex); + goto err; + } + break; + } + } while (remain > 0); + if (ret != 0) { // read fail, retry + ret = -EIO; + continue; + } + + for (i = 0, read_checksum = 0x0; + i < SEC_TS_SPI_READ_HEADER_SIZE + len; i++) + read_checksum += ts->io_read_buf[i]; + //read success + if (ts->io_read_buf[0] == SEC_TS_SPI_SYNC_CODE && + // ts->io_read_buf[6] == SEC_TS_SPI_CMD_OK && + reg == ts->io_read_buf[5] && + read_checksum == + ts->io_read_buf[SEC_TS_SPI_READ_HEADER_SIZE + + len]) + break; + //read data fail + else if (ts->io_read_buf[6] + == SEC_TS_SPI_CMD_UNKNOWN || + ts->io_read_buf[6] + == SEC_TS_SPI_CMD_BAD_PARAM) { + input_info(true, &ts->client->dev, + "%s: CMD_NG cmd(M) = %X, cmd(S) = %X, cmd_result = %X\n", + __func__, reg, ts->io_read_buf[5], + ts->io_read_buf[6]); + ret = -EIO; + continue; + } else { + input_info(true, &ts->client->dev, + "%s: spi fail, ret %d, sync code %X, reg(M) %X, reg(S) %X, cmd_result %X, chksum(M) %X, chksum(S) %X\n", + __func__, ret, ts->io_read_buf[0], + reg, ts->io_read_buf[5], + ts->io_read_buf[6], read_checksum, + ts->io_read_buf[ + SEC_TS_SPI_READ_HEADER_SIZE + + len]); + ret = -EIO; + continue; + } + } + if (ret == 0) + memcpy(data, ts->io_read_buf + + SEC_TS_SPI_READ_HEADER_SIZE, len); +#endif + } +skip_spi_read: + mutex_unlock(&ts->io_mutex); + + if (retry == SEC_TS_IO_RETRY_CNT) { + input_err(true, &ts->client->dev, + "%s: read reg(%#x) over retry limit, comm_err_count %d, io_err_count %d\n", + __func__, reg, ts->comm_err_count, ts->io_err_count); + ret = -EIO; + ts->io_err_count++; +#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 + + } else + ts->io_err_count = 0; + + /* do hw reset if continuously failed over SEC_TS_IO_RESET_CNT times */ + if (ts->io_err_count >= SEC_TS_IO_RESET_CNT) { + ts->io_err_count = 0; + sec_ts_hw_reset(ts); + } + + return ret; + +err: + return -EIO; +} + +static int sec_ts_write_burst_internal(struct sec_ts_data *ts, + u8 *data, int len, bool dma_safe) +{ + int ret; + int retry; +#ifndef I2C_INTERFACE + struct spi_message msg; + struct spi_transfer transfer[1] = { { 0 } }; + unsigned int i; + unsigned int spi_len = 0; + unsigned char checksum = 0x0; +#endif + +#ifdef I2C_INTERFACE + if (len > sizeof(ts->io_write_buf) && dma_safe == false) { + input_err(true, &ts->client->dev, + "%s: len %d over pre-allocated size %d\n", + __func__, len, sizeof(ts->io_write_buf)); + return -ENOSPC; + } +#else + /* add 3 zero stuffing tx bytes at last */ + if (SEC_TS_SPI_HEADER_SIZE + len + SEC_TS_SPI_CHECKSUM_SIZE + 3 > + sizeof(ts->io_write_buf)) { + input_err(true, &ts->client->dev, + "%s: len is larger than buffer size\n", __func__); + return -EINVAL; + } +#endif + + mutex_lock(&ts->io_mutex); +#ifdef I2C_INTERFACE + if (dma_safe == false) { + memcpy(ts->io_write_buf, data, len); + data = ts->io_write_buf; + } +#else + + ts->io_write_buf[0] = SEC_TS_SPI_SYNC_CODE; + ts->io_write_buf[1] = (len >> 8) & 0xFF; + ts->io_write_buf[2] = len & 0xFF; + ts->io_write_buf[3] = 0x0; + ts->io_write_buf[4] = 0x0; + + memcpy(ts->io_write_buf + SEC_TS_SPI_HEADER_SIZE, data, len); + + + spi_len = SEC_TS_SPI_HEADER_SIZE + len; + for (i = 0; i < spi_len; i++) + checksum += ts->io_write_buf[i]; + + ts->io_write_buf[spi_len] = checksum; + spi_len += SEC_TS_SPI_CHECKSUM_SIZE; + + spi_message_init(&msg); + + /* add 3 zero stuffing tx bytes at last */ + memset(ts->io_write_buf + spi_len, 0x00, 3); + spi_len = (spi_len + 3) & ~3; + transfer[0].len = spi_len; + transfer[0].tx_buf = ts->io_write_buf; + transfer[0].rx_buf = NULL; + spi_message_add_tail(&transfer[0], &msg); + +#ifdef SEC_TS_DEBUG_IO + input_info(true, &ts->client->dev, "%s:\n", __func__); + for (i = 0; i < spi_len; i++) + input_info(true, &ts->client->dev, "%X ", ts->io_write_buf[i]); + input_info(true, &ts->client->dev, "\n"); +#endif + +#endif + + for (retry = 0; retry < SEC_TS_IO_RETRY_CNT; retry++) { +#ifdef I2C_INTERFACE + if ((ret = i2c_master_send(ts->client, data, len)) == len) + break; +#else + if ((ret = spi_sync(ts->client, &msg)) == 0) + break; +#endif + + usleep_range(1 * 1000, 1 * 1000); + + if (retry > 1) { + input_err(true, &ts->client->dev, + "%s: retry %d\n", __func__, retry + 1); + ts->comm_err_count++; + } + } + + mutex_unlock(&ts->io_mutex); + if (retry == SEC_TS_IO_RETRY_CNT) { + input_err(true, &ts->client->dev, + "%s: write over retry limit\n", __func__); + ret = -EIO; + } + + return ret; +} + +static int sec_ts_read_bulk_internal(struct sec_ts_data *ts, + u8 *data, int len, bool dma_safe) +{ + int ret; + unsigned char retry; + int remain = len; +#ifdef I2C_INTERFACE + struct i2c_msg msg; +#else + struct spi_message msg; + struct spi_transfer transfer[1] = { { 0 } }; + unsigned int i; + unsigned int spi_len = 0; + unsigned char checksum = 0x0; + int copy_size = 0, copy_cur = 0; + int retry_msg = 0; +#endif + + if (len > sizeof(ts->io_read_buf) && dma_safe == false) { + input_err(true, &ts->client->dev, + "%s: len %d over pre-allocated size %d\n", __func__, + len, sizeof(ts->io_read_buf)); + return -ENOSPC; + } + + mutex_lock(&ts->io_mutex); + +#ifdef I2C_INTERFACE + msg.addr = ts->client->addr; + msg.flags = I2C_M_RD; + msg.len = len; + if (dma_safe == false) + msg.buf = ts->io_read_buf; + else + msg.buf = data; + + do { + if (remain > ts->io_burstmax) + msg.len = ts->io_burstmax; + else + msg.len = remain; + + remain -= ts->io_burstmax; + + for (retry = 0; retry < SEC_TS_IO_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: retry %d\n", + __func__, retry + 1); + ts->comm_err_count++; + } + } + + if (retry == SEC_TS_IO_RETRY_CNT) { + input_err(true, &ts->client->dev, + "%s: read over retry limit\n", __func__); + ret = -EIO; + break; + } + + msg.buf += msg.len; + + } while (remain > 0); + + if (ret == 1 && dma_safe == false) + memcpy(data, ts->io_read_buf, len); +#else +retry_message: + remain = spi_len = (SEC_TS_SPI_READ_HEADER_SIZE + len + + SEC_TS_SPI_CHECKSUM_SIZE + 3) & ~3; + do { + if (remain > ts->io_burstmax) + copy_cur = ts->io_burstmax; + else + copy_cur = remain; + + spi_message_init(&msg); + transfer[0].len = copy_cur; + transfer[0].tx_buf = NULL; + transfer[0].rx_buf = &ts->io_read_buf[copy_size]; + /* CS needs to stay low until read seq. is done + */ + transfer[0].cs_change = (remain > ts->io_burstmax) ? 1 : 0; + + spi_message_add_tail(&transfer[0], &msg); + + copy_size += copy_cur; + remain -= copy_cur; + + for (retry = 0; retry < SEC_TS_IO_RETRY_CNT; retry++) { + ret = spi_sync(ts->client, &msg); + if (ret == 0) + 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->io_mutex); + goto err; + } + + if (retry > 1) { + input_err(true, &ts->client->dev, + "%s: retry %d\n", __func__, retry + 1); + ts->comm_err_count++; + } + } + } while (remain > 0); + + for (i = 0, checksum = 0; i < SEC_TS_SPI_READ_HEADER_SIZE + len; i++) + checksum += ts->io_read_buf[i]; + + if (ret == 0 && ts->io_read_buf[0] == SEC_TS_SPI_SYNC_CODE && + checksum == ts->io_read_buf[SEC_TS_SPI_READ_HEADER_SIZE + len]) + memcpy(data, ts->io_read_buf + SEC_TS_SPI_READ_HEADER_SIZE, + len); + else { + input_info(true, &ts->client->dev, + "%s: spi fail, ret %d, sync code %X, reg(S) %X, chksum(M) %X, chksum(S) %X\n", + __func__, ret, ts->io_read_buf[0], ts->io_read_buf[5], + checksum, + ts->io_read_buf[SEC_TS_SPI_READ_HEADER_SIZE + len]); + if (retry_msg++ < SEC_TS_IO_RETRY_CNT) + goto retry_message; + } +#endif + mutex_unlock(&ts->io_mutex); + +#ifdef I2C_INTERFACE + if (ret == 1) +#else + if (ret == 0) +#endif + return 0; +err: + return -EIO; +} + +/* Wrapper API for read and write */ +int sec_ts_read(struct sec_ts_data *ts, u8 reg, u8 *data, int len) +{ + return sec_ts_read_internal(ts, reg, data, len, false); +} + +int sec_ts_read_heap(struct sec_ts_data *ts, u8 reg, u8 *data, int len) +{ + return sec_ts_read_internal(ts, reg, data, len, true); +} + +int sec_ts_write_burst(struct sec_ts_data *ts, u8 *data, int len) +{ + return sec_ts_write_burst_internal(ts, data, len, false); +} + +int sec_ts_write_burst_heap(struct sec_ts_data *ts, u8 *data, int len) +{ + return sec_ts_write_burst_internal(ts, data, len, true); +} + +int sec_ts_read_bulk(struct sec_ts_data *ts, u8 *data, int len) +{ + return sec_ts_read_bulk_internal(ts, data, len, false); +} + +int sec_ts_read_bulk_heap(struct sec_ts_data *ts, u8 *data, int len) +{ + return sec_ts_read_bulk_internal(ts, data, len, true); +} + +#ifndef I2C_INTERFACE +int sec_ts_spi_delay(u8 reg) +{ + switch (reg) { + case SEC_TS_READ_TOUCH_RAWDATA: + return 400; + case SEC_TS_CMD_HEATMAP_READ: + return 500; + case SEC_TS_READ_ALL_EVENT: + return 500; + case SEC_TS_READ_CSRAM_RTDP_DATA: + return 500; + case SEC_TS_CAAT_READ_STORED_DATA: + return 500; + case SEC_TS_CMD_FLASH_READ_DATA: + return 1800; + case SEC_TS_READ_FIRMWARE_INTEGRITY: + return 20*1000; + case SEC_TS_READ_SELFTEST_RESULT: + return 3500; + default: return 100; + } +} +#endif + +static int sec_ts_read_from_customlib(struct sec_ts_data *ts, u8 *data, int len) +{ + int ret; + + ret = sec_ts_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_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 <linux/sec_debug.h> +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; + } + + ts->tsp_dump_lock = 1; + input_info(true, &ts->client->dev, "%s: start ##\n", __func__); + sec_ts_run_rawdata_all((void *)ts, false); + msleep(100); + + input_info(true, &ts->client->dev, "%s: done ##\n", __func__); + ts->tsp_dump_lock = 0; + +} + +static void dump_tsp_log(void) +{ + pr_info("%s: %s %s: start\n", SEC_TS_NAME, SECLOG, __func__); + +#ifdef CONFIG_BATTERY_SAMSUNG + if (lpcharge == 1) { + pr_err("%s: %s %s: ignored ## lpm charging Mode!!\n", + SEC_TS_NAME, SECLOG, __func__); + return; + } +#endif + + if (p_ghost_check == NULL) { + pr_err("%s: %s %s: ignored ## tsp probe fail!!\n", + SEC_TS_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) +{ + return sec_ts_wait_for_ready_with_count(ts, ack, + SEC_TS_WAIT_RETRY_CNT); +} + +int sec_ts_wait_for_ready_with_count(struct sec_ts_data *ts, unsigned int ack, + unsigned int count) +{ + int rc = -1; + int retry = 0; + u8 tBuff[SEC_TS_EVENT_BUFF_SIZE] = {0,}; + + while (retry < count) { + if (sec_ts_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; + } + } + } + sec_ts_delay(20); + retry++; + } + if (retry == count) + input_err(true, &ts->client->dev, "%s: Time Over\n", + __func__); + + 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; + + memset(ts->cali_report, 0, sizeof(ts->cali_report)); + ret = sec_ts_read(ts, SEC_TS_READ_CALIBRATION_REPORT, + ts->cali_report, sizeof(ts->cali_report)); + 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:%X, param version:%X %X %X %X\n", + __func__, ts->cali_report_try_cnt, ts->cali_report_pass_cnt, + ts->cali_report_fail_cnt, ts->cali_report_status, + ts->cali_report_param_ver[0], ts->cali_report_param_ver[1], + ts->cali_report_param_ver[2], ts->cali_report_param_ver[3]); + + return ts->cali_report_status; +} + +static 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, Cover=0x%x, Power mode=0x%x\n", + __func__, ts->charger_mode, ts->touch_functions, + ts->lowpower_status); + + /* charger mode */ + if (ts->charger_mode != SEC_TS_BIT_CHARGER_MODE_NO) { + w_data[0] = ts->charger_mode; + ret = ts->sec_ts_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_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_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->lowpower_status == TO_LOWPOWER_MODE) { + w_data[0] = (ts->lowpower_mode & + SEC_TS_MODE_LOWPOWER_FLAG) >> 1; + ret = sec_ts_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_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_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_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_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_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_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__); + } + + } +} + +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) +/* Update a state machine used to toggle control of the touch IC's motion + * filter. + */ +static void update_motion_filter(struct sec_ts_data *ts) +{ + /* Motion filter timeout, in milliseconds */ + const u32 mf_timeout_ms = 500; + u8 next_state; + /* Count the active touches */ + u8 touches = hweight32(ts->tid_touch_state); + + if (ts->use_default_mf) + return; + + /* Determine the next filter state. The motion filter is enabled by + * default and it is disabled while a single finger is touching the + * screen. If another finger is touched down or if a timeout expires, + * the motion filter is reenabled and remains enabled until all fingers + * are lifted. + */ + next_state = ts->mf_state; + switch (ts->mf_state) { + case SEC_TS_MF_FILTERED: + if (touches == 1) { + next_state = SEC_TS_MF_UNFILTERED; + ts->mf_downtime = ktime_get(); + } + break; + case SEC_TS_MF_UNFILTERED: + if (touches == 0) { + next_state = SEC_TS_MF_FILTERED; + } else if (touches > 1 || + ktime_after(ktime_get(), + ktime_add_ms(ts->mf_downtime, + mf_timeout_ms))) { + next_state = SEC_TS_MF_FILTERED_LOCKED; + } + break; + case SEC_TS_MF_FILTERED_LOCKED: + if (touches == 0) + next_state = SEC_TS_MF_FILTERED; + break; + } + + /* Send command to update filter state */ + if ((next_state == SEC_TS_MF_UNFILTERED) != + (ts->mf_state == SEC_TS_MF_UNFILTERED)) { + int ret; + u8 para; + + pr_debug("%s: setting motion filter = %s.\n", __func__, + (next_state == SEC_TS_MF_UNFILTERED) ? + "false" : "true"); + para = (next_state == SEC_TS_MF_UNFILTERED) ? 0x01 : 0x00; + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_CONT_REPORT, + ¶, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: write reg %#x para %#x failed, returned %i\n", + __func__, SEC_TS_CMD_SET_CONT_REPORT, para, ret); + } + } + ts->mf_state = next_state; +} + +static bool read_heatmap_raw(struct v4l2_heatmap *v4l2) +{ + struct sec_ts_data *ts = container_of(v4l2, struct sec_ts_data, v4l2); + const struct sec_ts_plat_data *pdata = ts->plat_data; + int result; + int max_x = v4l2->format.width; + int max_y = v4l2->format.height; + + if (ts->tsp_dump_lock == 1) { + input_info(true, &ts->client->dev, + "%s: drop this because raw data reading by others\n", + __func__); + return false; + } + + if (pdata->heatmap_mode == HEATMAP_PARTIAL) { + strength_t heatmap_value; + int heatmap_x, heatmap_y; + /* index for through the heatmap buffer read over the bus */ + unsigned int local_i; + /* final position of the heatmap value in the full frame */ + unsigned int frame_i; + unsigned int num_elements; + u8 enable; + struct heatmap_report report = {0}; + + result = sec_ts_read(ts, + SEC_TS_CMD_HEATMAP_ENABLE, &enable, 1); + if (result < 0) { + input_err(true, &ts->client->dev, + "%s: read reg %#x failed, returned %i\n", + __func__, SEC_TS_CMD_HEATMAP_ENABLE, result); + return false; + } + + if (!enable) { + enable = 1; + result = sec_ts_write(ts, + SEC_TS_CMD_HEATMAP_ENABLE, &enable, 1); + if (result < 0) + input_err(true, &ts->client->dev, + "%s: enable local heatmap failed, returned %i\n", + __func__, result); + /* + * After local heatmap enabled, it takes `1/SCAN_RATE` + * time to make data ready. But, we don't want to wait + * here to cause overhead. Just drop this and wait for + * next reading. + */ + return false; + } + + result = sec_ts_read(ts, SEC_TS_CMD_HEATMAP_READ, + (uint8_t *) &report, sizeof(report)); + if (result < 0) { + input_err(true, &ts->client->dev, + "%s: read failed, returned %i\n", + __func__, result); + return false; + } + + num_elements = report.size_x * report.size_y; + if (num_elements > LOCAL_HEATMAP_WIDTH * LOCAL_HEATMAP_HEIGHT) { + input_err(true, &ts->client->dev, + "Unexpected heatmap size: %i x %i", + report.size_x, report.size_y); + return false; + } + + /* + * Set all to zero, will only write to non-zero locations + * in the loop. + */ + memset(v4l2->frame, 0, v4l2->format.sizeimage); + /* populate the data buffer, rearranging into final locations */ + for (local_i = 0; local_i < num_elements; local_i++) { + /* big-endian order raw data into heatmap data type */ + be16_to_cpus(&report.data[local_i]); + heatmap_value = report.data[local_i]; + + if (heatmap_value == 0) { + /* + * Already initialized to zero. More + * importantly, samples around edges may go out + * of bounds. + * If their value is zero, this is ok. + */ + continue; + } + heatmap_x = report.offset_x + (local_i % report.size_x); + heatmap_y = report.offset_y + (local_i / report.size_x); + + if (heatmap_x < 0 || heatmap_x >= max_x || + heatmap_y < 0 || heatmap_y >= max_y) { + input_err(true, &ts->client->dev, + "Invalid x or y: (%i, %i), value=%i, ending loop\n", + heatmap_x, heatmap_y, + heatmap_value); + return false; + } + frame_i = heatmap_y * max_x + heatmap_x; + v4l2->frame[frame_i] = heatmap_value; + } + } else if (pdata->heatmap_mode == HEATMAP_FULL) { + int i, j, index = 0; + int ret = 0; + u8 type; + + if (!ts->heatmap_buff) { + ts->heatmap_buff = kmalloc( + sizeof(strength_t) * max_x * max_y, GFP_KERNEL); + if (!ts->heatmap_buff) { + input_err(true, &ts->client->dev, + "%s: alloc heatmap_buff failed\n", __func__); + return false; + } + } + + ret = sec_ts_read(ts, + SEC_TS_CMD_MUTU_RAW_TYPE, &ts->frame_type, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: read rawdata type failed\n", + __func__); + return false; + } + + /* Check raw type is TYPE_SIGNAL_DATA */ + if (ts->frame_type != TYPE_SIGNAL_DATA) { + input_info(true, &ts->client->dev, + "%s: frame_type change from %#x\n", + __func__, ts->frame_type); + + /* Check raw type is TYPE_INVALID_DATA */ + if (ts->frame_type != TYPE_INVALID_DATA) { + type = TYPE_INVALID_DATA; + ret = sec_ts_write(ts, + SEC_TS_CMD_MUTU_RAW_TYPE, &type, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: recover rawdata type failed\n", + __func__); + return false; + } + ts->frame_type = type; + } + + /* Set raw type to TYPE_SIGNAL_DATA */ + type = TYPE_SIGNAL_DATA; + ret = sec_ts_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__); + return false; + } + ts->frame_type = type; + + /* + * If raw type change, need to wait 50 ms to read data + * back. But, we don't wanto to wait here to cause + * overhead. Just drop this and wait for next reading. + */ + return false; + } + + ret = sec_ts_read_heap(ts, SEC_TS_READ_TOUCH_RAWDATA, + (u8 *)ts->heatmap_buff, + sizeof(strength_t) * max_x * max_y); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: Read delta frame failed\n", __func__); + return false; + } + + /* big-endian order raw data into heatmap data type */ + for (i = max_y - 1; i >= 0; i--) + for (j = max_x - 1; j >= 0 ; j--) + v4l2->frame[index++] = be16_to_cpup( + ts->heatmap_buff + (j * max_y) + i); + } else + return false; + + return true; +} +#endif + +#ifdef SEC_TS_SUPPORT_CUSTOMLIB +static void sec_ts_handle_lib_status_event(struct sec_ts_data *ts, + struct sec_ts_event_status *p_event_status) +{ + if ((p_event_status->stype == TYPE_STATUS_EVENT_CUSTOMLIB_INFO) && + (p_event_status->status_id == SEC_TS_EVENT_CUSTOMLIB_FORCE_KEY)) { + if (ts->power_status == SEC_TS_STATE_POWER_ON) { + if (p_event_status->status_data_1 & + SEC_TS_CUSTOMLIB_EVENT_PRESSURE_TOUCHED) { + ts->all_force_count++; + ts->scrub_id = + CUSTOMLIB_EVENT_TYPE_PRESSURE_TOUCHED; + } else { + if (ts->scrub_id == + CUSTOMLIB_EVENT_TYPE_AOD_HOMEKEY_PRESS) { + input_report_key(ts->input_dev, + KEY_HOMEPAGE, + 0); + ts->scrub_id = + CUSTOMLIB_EVENT_TYPE_AOD_HOMEKEY_RELEASE; + } else { + ts->scrub_id = + CUSTOMLIB_EVENT_TYPE_PRESSURE_RELEASED; + } + } + + input_report_key(ts->input_dev, + KEY_BLACK_UI_GESTURE, 1); + } else { + if (p_event_status->status_data_1 & + SEC_TS_CUSTOMLIB_EVENT_PRESSURE_RELEASED) { + input_report_key(ts->input_dev, + KEY_HOMEPAGE, 0); + input_report_key(ts->input_dev, + KEY_BLACK_UI_GESTURE, 1); + ts->scrub_id = + CUSTOMLIB_EVENT_TYPE_AOD_HOMEKEY_RLS_NO_HAPTIC; + input_sync(ts->input_dev); + haptic_homekey_release(); + } else { + input_report_key(ts->input_dev, + KEY_HOMEPAGE, 1); + input_sync(ts->input_dev); + ts->scrub_id = + CUSTOMLIB_EVENT_TYPE_AOD_HOMEKEY_PRESS; + haptic_homekey_press(); + ts->all_force_count++; + } + } + + ts->scrub_x = + ((p_event_status->status_data_4 >> 4) & 0xF) << 8 | + (p_event_status->status_data_3 & 0xFF); + ts->scrub_y = + ((p_event_status->status_data_4 >> 0) & 0xF) << 8 | + (p_event_status->status_data_2 & 0xFF); + + input_info(true, &ts->client->dev, "%s: PRESSURE[%d]\n", + __func__, ts->scrub_id); + + input_sync(ts->input_dev); + input_report_key(ts->input_dev, KEY_BLACK_UI_GESTURE, 0); + } +} +#endif + +static void sec_ts_handle_coord_event(struct sec_ts_data *ts, + struct sec_ts_event_coordinate *p_event_coord) +{ + u8 t_id; + + if (ts->input_closed) { + input_err(true, &ts->client->dev, "%s: device is closed\n", + __func__); + return; + } + + t_id = (p_event_coord->tid - 1); + + if (t_id < MAX_SUPPORT_TOUCH_COUNT + MAX_SUPPORT_HOVER_COUNT) { + 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 & + SEC_TS_PRESSURE_MAX; + 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].grip = + (ts->coord[t_id].ttype == SEC_TS_TOUCHTYPE_GRIP); + + 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_GRIP) || + (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 || + ts->tid_touch_state == 0) { + input_report_key(ts->input_dev, + BTN_TOUCH, 0); + input_report_key(ts->input_dev, + BTN_TOOL_FINGER, 0); + ts->check_multi = 0; + } + __clear_bit(t_id, &ts->tid_palm_state); + __clear_bit(t_id, &ts->tid_grip_state); + __clear_bit(t_id, &ts->tid_touch_state); + + } 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_t(unsigned int, + ts->coord[t_id].z, + ts->max_z_value); + ts->min_z_value = min_t(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); + __set_bit(t_id, &ts->tid_touch_state); + if (ts->coord[t_id].palm) { + input_mt_report_slot_state( + ts->input_dev, MT_TOOL_PALM, 1); + __set_bit(t_id, &ts->tid_palm_state); + __clear_bit(t_id, &ts->tid_grip_state); + } else if (ts->coord[t_id].grip) { + input_mt_report_slot_state( + ts->input_dev, MT_TOOL_PALM, 1); + __clear_bit(t_id, &ts->tid_palm_state); + __set_bit(t_id, &ts->tid_grip_state); + } else { + input_mt_report_slot_state( + ts->input_dev, + MT_TOOL_FINGER, 1); + __clear_bit(t_id, &ts->tid_palm_state); + __clear_bit(t_id, &ts->tid_grip_state); + } + 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); +#ifdef ABS_MT_CUSTOM + 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); +#endif + 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++; + } + + } else if (ts->coord[t_id].action == + SEC_TS_COORDINATE_ACTION_MOVE) { +#ifdef SW_GLOVE + 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); + } +#endif + input_mt_slot(ts->input_dev, t_id); + __set_bit(t_id, &ts->tid_touch_state); + if (ts->coord[t_id].palm) { + input_mt_report_slot_state( + ts->input_dev, MT_TOOL_PALM, 1); + __set_bit(t_id, &ts->tid_palm_state); + __clear_bit(t_id, &ts->tid_grip_state); + } else if (ts->coord[t_id].grip) { + input_mt_report_slot_state( + ts->input_dev, MT_TOOL_PALM, 1); + __clear_bit(t_id, &ts->tid_palm_state); + __set_bit(t_id, &ts->tid_grip_state); + } else { + input_mt_report_slot_state( + ts->input_dev, + MT_TOOL_FINGER, 1); + __clear_bit(t_id, &ts->tid_palm_state); + __clear_bit(t_id, &ts->tid_grip_state); + } + 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); +#ifdef ABS_MT_CUSTOM + 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); +#endif + 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); + } 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); +} + +#ifdef SEC_TS_SUPPORT_CUSTOMLIB +static void sec_ts_handle_gesture_event(struct sec_ts_data *ts, + struct sec_ts_gesture_status *p_gesture_status) +{ + if ((p_gesture_status->eid == 0x02) && + (p_gesture_status->stype == 0x00)) { + u8 customlib[3] = { 0 }; + + ret = sec_ts_read_from_customlib(ts, customlib, 3); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: fail to read custom library data\n", + __func__); + + input_info(true, &ts->client->dev, + "%s: Custom Library, %x, %x, %x\n", + __func__, customlib[0], customlib[1], customlib[2]); + + if (p_gesture_status->gesture_id == SEC_TS_GESTURE_CODE_SPAY || + p_gesture_status->gesture_id == + SEC_TS_GESTURE_CODE_DOUBLE_TAP) { + /* will be fixed to data structure */ + if (customlib[1] & SEC_TS_MODE_CUSTOMLIB_AOD) { + u8 data[5] = { 0x0A, 0x00, 0x00, 0x00, 0x00 }; + + ret = sec_ts_read_from_customlib(ts, data, 5); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: fail to read custom library data\n", + __func__); + + if (data[4] & SEC_TS_AOD_GESTURE_DOUBLETAB) + ts->scrub_id = + CUSTOMLIB_EVENT_TYPE_AOD_DOUBLETAB; + + ts->scrub_x = (data[1] & 0xFF) << 8 | + (data[0] & 0xFF); + ts->scrub_y = (data[3] & 0xFF) << 8 | + (data[2] & 0xFF); + input_info(true, &ts->client->dev, + "%s: aod: %d\n", + __func__, ts->scrub_id); + ts->all_aod_tap_count++; + } + if (customlib[1] & SEC_TS_MODE_CUSTOMLIB_SPAY) { + ts->scrub_id = CUSTOMLIB_EVENT_TYPE_SPAY; + input_info(true, &ts->client->dev, + "%s: SPAY: %d\n", + __func__, ts->scrub_id); + ts->all_spay_count++; + } + input_report_key(ts->input_dev, + KEY_BLACK_UI_GESTURE, 1); + input_sync(ts->input_dev); + input_report_key(ts->input_dev, + KEY_BLACK_UI_GESTURE, 0); + } + } +} +#endif + +#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_gesture_status *p_gesture_status; + struct sec_ts_event_status *p_event_status; + int curr_pos; + int remain_event_count = 0; + bool processed_pointer_event = false; + unsigned long last_tid_palm_state = ts->tid_palm_state; + unsigned long last_tid_grip_state = ts->tid_grip_state; + + if (ts->power_status == SEC_TS_STATE_LPM) { + + pm_wakeup_event(&ts->client->dev, 3 * MSEC_PER_SEC); + /* waiting for blsp block resuming, if not occurs error */ + ret = wait_for_completion_interruptible_timeout( + &ts->resume_done, + msecs_to_jiffies(3 * MSEC_PER_SEC)); + 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_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: read one event failed\n", __func__); + return; + } + + if (ts->temp == 0x01) + 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 || + left_event_count == 0xFF) { + input_err(true, &ts->client->dev, + "%s: event buffer overflow %d\n", + __func__, left_event_count); + + /* write clear event stack command + * when read_event_count > MAX_EVENT_COUNT + **/ + ret = sec_ts_write(ts, SEC_TS_CMD_CLEAR_EVENT_STACK, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: write clear event failed\n", __func__); + return; + } + + if (left_event_count > 0) { + ret = sec_ts_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: read one event failed\n", __func__); + return; + } + } + + do { + s16 max_force_p = 0; + + event_buff = read_event_buff[curr_pos]; + event_id = event_buff[0] & 0x3; + + if (ts->temp == 0x01) + 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) { + /* Demote 'vendor' messages */ + if (p_event_status->stype == + TYPE_STATUS_EVENT_VENDOR_INFO) { + u8 status_id = + p_event_status->status_id; + u8 status_data_1 = + p_event_status->status_data_1; + + input_dbg(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]); + + switch (status_id) { + case SEC_TS_EVENT_STATUS_ID_WLC: + input_info(true, + &ts->client->dev, + "STATUS: wlc mode change to %x\n", + status_data_1); + break; + + case SEC_TS_EVENT_STATUS_ID_NOISE: + input_info(true, + &ts->client->dev, + "STATUS: noise mode change to %x\n", + status_data_1); + break; + + case SEC_TS_EVENT_STATUS_ID_GRIP: + input_info(true, + &ts->client->dev, + "STATUS: detect grip %s!\n", + (status_data_1) ? + "enter" : "leave"); + break; + + case SEC_TS_EVENT_STATUS_ID_PALM: + input_info(true, + &ts->client->dev, + "STATUS: detect palm!\n"); + break; + + default: + break; + } + } else + 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]); + } + + if ((p_event_status->stype == + TYPE_STATUS_EVENT_INFO) && + (p_event_status->status_id == + SEC_TS_ACK_BOOT_COMPLETE)) { + u8 status_data_1 = + p_event_status->status_data_1; + + switch (status_data_1) { + case 0x20: + /* watchdog reset !? */ + sec_ts_unlocked_release_all_finger(ts); + ret = sec_ts_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); + break; + case 0x40: + input_info(true, &ts->client->dev, + "%s: sw_reset done\n", + __func__); + sec_ts_unlocked_release_all_finger(ts); + complete_all(&ts->boot_completed); + break; + case 0x10: + input_info(true, &ts->client->dev, + "%s: hw_reset done\n", + __func__); + sec_ts_unlocked_release_all_finger(ts); + complete_all(&ts->boot_completed); + break; + default: + break; + } + + } + + /* 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++; + + } + +#ifdef SEC_TS_SUPPORT_CUSTOMLIB + sec_ts_handle_lib_status_event(ts, p_event_status); +#endif + break; + + case SEC_TS_COORDINATE_EVENT: + processed_pointer_event = true; + sec_ts_handle_coord_event(ts, + (struct sec_ts_event_coordinate *)event_buff); + break; + + case SEC_TS_GESTURE_EVENT: + p_gesture_status = + (struct sec_ts_gesture_status *)event_buff; +#ifdef SEC_TS_SUPPORT_CUSTOMLIB + sec_ts_handle_gesture_event(ts, p_gesture_status); +#endif + 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; + } + + if (t_id < MAX_SUPPORT_TOUCH_COUNT + MAX_SUPPORT_HOVER_COUNT) { + if (ts->coord[t_id].action == + SEC_TS_COORDINATE_ACTION_PRESS) { + input_dbg(false, &ts->client->dev, + "%s[P] tID:%d x:%d y:%d z:%d major:%d minor:%d tc:%d type:%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); + + } else if (ts->coord[t_id].action == + SEC_TS_COORDINATE_ACTION_RELEASE) { + input_dbg(false, &ts->client->dev, + "%s[R] tID:%d mc:%d tc:%d lx:%d ly:%d f:%d v:%02X%02X cal:%02X(%02X) id(%d,%d) p:%d P%02XT%04X\n", + ts->dex_name, + t_id, ts->coord[t_id].mcount, + ts->touch_count, + ts->coord[t_id].x, ts->coord[t_id].y, + max_force_p, + ts->plat_data->img_version_of_ic[2], + ts->plat_data->img_version_of_ic[3], + ts->cal_status, ts->nv, ts->tspid_val, + ts->tspicid_val, + ts->coord[t_id].palm_count, + ts->cal_count, ts->tune_fix_ver); + + ts->coord[t_id].action = + SEC_TS_COORDINATE_ACTION_NONE; + ts->coord[t_id].mcount = 0; + ts->coord[t_id].palm_count = 0; + max_force_p = 0; + } + } + + curr_pos++; + remain_event_count--; + } while (remain_event_count >= 0); + + input_sync(ts->input_dev); +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) + if (processed_pointer_event) { + heatmap_read(&ts->v4l2, ktime_to_ns(ts->timestamp)); + + /* palm */ + if (last_tid_palm_state == 0 && + ts->tid_palm_state >= 1) { + input_info(true, &ts->client->dev, + "COORD: detect palm enter(tid 0x0 -> %#x)\n", + ts->tid_palm_state); + } + if (last_tid_palm_state >= 1 && + ts->tid_palm_state == 0) { + input_info(true, &ts->client->dev, + "COORD: detect palm leave(tid %#x -> 0x0), tid_touch %#x\n", + last_tid_palm_state, ts->tid_touch_state); + if (ts->touch_count || ts->tid_touch_state) { + ts->palms_leaved_once = true; + input_dbg(true, &ts->client->dev, + "COORD: wait all finger(s) release after palm entered\n"); + } + } + /* grip */ + if (last_tid_grip_state == 0 && + ts->tid_grip_state >= 1) { + input_info(true, &ts->client->dev, + "COORD: detect grip enter(tid 0x0 -> %#x)\n", + ts->tid_grip_state); + } + if (last_tid_grip_state >= 1 && + ts->tid_grip_state == 0) { + input_info(true, &ts->client->dev, + "COORD: detect grip leave(tid %#x -> 0x0), tid_touch %#x\n", + last_tid_grip_state, ts->tid_touch_state); + if (ts->touch_count || ts->tid_touch_state) { + ts->grips_leaved_once = true; + input_dbg(true, &ts->client->dev, + "COORD: wait all finger(s) release after grip entered\n"); + } + } + if ((ts->touch_count == 0 || ts->tid_touch_state == 0) && + (ts->palms_leaved_once || ts->grips_leaved_once)) { + ts->palms_leaved_once = false; + ts->grips_leaved_once = false; + input_info(true, &ts->client->dev, + "COORD: all fingers released with palm(s)/grip(s) leaved once\n"); + } + } +#endif +} + +static irqreturn_t sec_ts_isr(int irq, void *handle) +{ + struct sec_ts_data *ts = (struct sec_ts_data *)handle; + + ts->timestamp = ktime_get(); + input_set_timestamp(ts->input_dev, ts->timestamp); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t sec_ts_irq_thread(int irq, void *ptr) +{ + struct sec_ts_data *ts = (struct sec_ts_data *)ptr; + + if (sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_IRQ, true) < 0) { + /* Interrupt during bus suspend */ + input_info(true, &ts->client->dev, + "%s: Skipping stray interrupt since bus is suspended(power_status: %d)\n", + __func__, ts->power_status); + return IRQ_HANDLED; + } + + /* prevent CPU from entering deep sleep */ + pm_qos_update_request(&ts->pm_qos_req, 100); + pm_wakeup_event(&ts->client->dev, MSEC_PER_SEC); + + mutex_lock(&ts->eventlock); + + sec_ts_read_event(ts); + + mutex_unlock(&ts->eventlock); + +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) + /* Disable the firmware motion filter during single touch */ + update_motion_filter(ts); +#endif + + pm_qos_update_request(&ts->pm_qos_req, PM_QOS_DEFAULT_VALUE); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_IRQ, false); + + return IRQ_HANDLED; +} + +int get_tsp_status(void) +{ + return 0; +} +EXPORT_SYMBOL(get_tsp_status); + +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_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_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_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; + +} + +static 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 dvdd_enabled, avdd_enabled; + int ret = 0; + + if (pdata->regulator_dvdd) { + regulator_dvdd = regulator_get(&ts->client->dev, + 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); + } + + if (pdata->regulator_avdd) { + regulator_avdd = regulator_get(&ts->client->dev, + 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); + } + + if (regulator_dvdd && (dvdd_enabled != on)) { + ret = (on) ? regulator_enable(regulator_dvdd) : + regulator_disable(regulator_dvdd); + if (ret) + input_err(true, &ts->client->dev, + "%s: Failed to control dvdd: %d\n", + __func__, ret); + else { + sec_ts_delay(1); + dvdd_enabled = on; + } + } + + if (regulator_avdd && (avdd_enabled != on)) { + ret = (on) ? regulator_enable(regulator_avdd) : + regulator_disable(regulator_avdd); + if (ret) + input_err(true, &ts->client->dev, + "%s: Failed to control avdd: %d\n", + __func__, ret); + else + avdd_enabled = on; + } + + if (regulator_dvdd) { + input_info(true, &ts->client->dev, "%s: %s: dvdd:%s\n", + __func__, on ? "on" : "off", + regulator_is_enabled(regulator_dvdd) ? "on" : "off"); + regulator_put(regulator_dvdd); + } + + if (regulator_avdd) { + input_info(true, &ts->client->dev, "%s: %s: avdd:%s\n", + __func__, on ? "on" : "off", + regulator_is_enabled(regulator_avdd) ? "on" : "off"); + regulator_put(regulator_avdd); + } + + return ret; +} + +#ifdef I2C_INTERFACE +static int sec_ts_parse_dt(struct i2c_client *client) +#else +static int sec_ts_parse_dt(struct spi_device *client) +#endif +{ + 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; +#if defined(CONFIG_EXYNOS_DECON_FB) + int connected; +#endif + int index; + struct of_phandle_args panelmap; + struct drm_panel *panel = NULL; + + if (of_property_read_bool(np, "sec,panel_map")) { + for (index = 0 ;; index++) { + ret = of_parse_phandle_with_fixed_args(np, + "sec,panel_map", + 1, + index, + &panelmap); + if (ret) + return -EPROBE_DEFER; + panel = of_drm_find_panel(panelmap.np); + of_node_put(panelmap.np); + if (!IS_ERR_OR_NULL(panel)) { + pdata->panel = panel; + pdata->initial_panel_index = panelmap.args[0]; + break; + } + } + } + + 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; + } + + if (of_property_read_u32(np, "sec,i2c-burstmax", &pdata->io_burstmax)) { + input_dbg(false, &client->dev, + "%s: Failed to get io_burstmax property\n", __func__); + pdata->io_burstmax = 1024; //TODO: check this + } + if (pdata->io_burstmax > IO_PREALLOC_READ_BUF_SZ || + pdata->io_burstmax > IO_PREALLOC_WRITE_BUF_SZ) { + input_err(true, &client->dev, + "%s: io_burstmax is larger than io_read_buf and/or io_write_buf.\n", + __func__); +//TODO: check this +// return -EINVAL; + } + + 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; + +#ifdef PAT_CONTROL + if (of_property_read_u32(np, "sec,pat_function", + &pdata->pat_function) < 0) { + pdata->pat_function = 0; + input_err(true, dev, + "%s: Failed to get pat_function property\n", __func__); + } + + if (of_property_read_u32(np, "sec,afe_base", &pdata->afe_base) < 0) { + pdata->afe_base = 0; + input_err(true, dev, + "%s: Failed to get afe_base property\n", __func__); + } +#endif + + 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__); + + pdata->switch_gpio = of_get_named_gpio(np, + "sec,switch_gpio", 0); + if (gpio_is_valid(pdata->switch_gpio)) { + ret = gpio_request_one(pdata->switch_gpio, + GPIOF_OUT_INIT_LOW, + "sec,touch_i2c_switch"); + if (ret) { + input_err(true, dev, + "%s: Failed to request gpio %d\n", + __func__, pdata->switch_gpio); + return -EINVAL; + } + + ret = gpio_direction_output(pdata->switch_gpio, + SEC_SWITCH_GPIO_VALUE_AP_MASTER); + if (ret) { + input_err(true, dev, + "%s: Failed to set gpio %d direction\n", + __func__, pdata->switch_gpio); + return -EINVAL; + } + } else { + input_err(true, dev, "%s: Failed to get switch_gpio\n", + __func__); + } + + pdata->reset_gpio = of_get_named_gpio(np, "sec,reset_gpio", 0); + if (gpio_is_valid(pdata->reset_gpio)) { + ret = gpio_request_one(pdata->reset_gpio, + GPIOF_OUT_INIT_HIGH, + "sec,touch_reset_gpio"); + if (ret) { + input_err(true, dev, + "%s: Failed to request gpio %d, ret %d\n", + __func__, pdata->reset_gpio, ret); + pdata->reset_gpio = -1; + } + //TODO: check this + ret = gpio_direction_output(pdata->reset_gpio, 1); + mdelay(10); + ret = gpio_direction_output(pdata->reset_gpio, 0); + mdelay(10); + ret = gpio_direction_output(pdata->reset_gpio, 1); + + } else + input_err(true, dev, "%s: Failed to get reset_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 defined(CONFIG_FB_MSM_MDSS_SAMSUNG) + lcdtype = get_lcd_attached("GET"); + if (lcdtype < 0) { + input_err(true, &client->dev, + "%s: lcd is not attached\n", __func__); + return -ENODEV; + } +#endif + +#if defined(CONFIG_EXYNOS_DECON_FB) + connected = get_lcd_info("connected"); + if (connected < 0) { + input_err(true, dev, "%s: Failed to get lcd info\n", __func__); + return -EINVAL; + } + + if (!connected) { + input_err(true, &client->dev, + "%s: lcd is disconnected\n", __func__); + return -ENODEV; + } + + input_info(true, &client->dev, "%s: lcd is connected\n", __func__); + + lcdtype = get_lcd_info("id"); + if (lcdtype < 0) { + input_err(true, dev, "%s: Failed to get lcd info\n", __func__); + return -EINVAL; + } +#endif + + input_info(true, &client->dev, + "%s: lcdtype 0x%08X\n", __func__, lcdtype); + + if (pdata->model_name && strncmp(pdata->model_name, "G950", 4) == 0) + pdata->panel_revision = 0; + else + pdata->panel_revision = ((lcdtype >> 8) & 0xFF) >> 4; + + 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__); + + 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__); + + 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; + + if (of_property_read_u32(np, "sec,heatmap_mode", + &pdata->heatmap_mode) < 0) + pdata->heatmap_mode = 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"); + + pdata->support_mt_pressure = true; + +#ifdef PAT_CONTROL + input_err(true, &client->dev, + "%s: buffer limit: %d, lcd_id:%06X, bringup:%d, FW:%s(%d), id:%d,%d, pat_function:%d mis_cal:%d dex:%d, gesture:%d\n", + __func__, pdata->io_burstmax, lcdtype, pdata->bringup, + pdata->firmware_name, count, pdata->tsp_id, pdata->tsp_icid, + pdata->pat_function, pdata->mis_cal_check, pdata->support_dex, + pdata->support_sidegesture); +#else + input_err(true, &client->dev, + "%s: buffer limit: %d, lcd_id:%06X, bringup:%d, FW:%s(%d), id:%d,%d, dex:%d, gesture:%d\n", + __func__, pdata->io_burstmax, lcdtype, pdata->bringup, + pdata->firmware_name, count, pdata->tsp_id, pdata->tsp_icid, + pdata->support_dex, pdata->support_sidegesture); +#endif + return ret; +} + +int sec_ts_read_information(struct sec_ts_data *ts) +{ + unsigned char data[13] = { 0 }; + int ret; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_READ_INFO, true); + + memset(data, 0x0, 3); + ret = sec_ts_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); + goto out; + } + + 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_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); + goto out; + } + + 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_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); + goto out; + } + + input_info(true, &ts->client->dev, + "%s: STATUS : %X\n", + __func__, data[0]); + + memset(data, 0x0, 4); + ret = sec_ts_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); + goto out; + } + + 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_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); + goto out; + } + + input_info(true, &ts->client->dev, + "%s: Functions : %02X\n", + __func__, ts->touch_functions); + +out: + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_READ_INFO, false); + 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_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_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_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; +#ifdef I2C_INTERFACE + dev->id.bustype = BUS_I2C; +#else + dev->id.bustype = BUS_SPI; +#endif + 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); +#ifdef SEC_TS_SUPPORT_CUSTOMLIB + set_bit(KEY_BLACK_UI_GESTURE, dev->keybit); +#endif +#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 +#ifdef KEY_SIDE_GESTURE + 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); + } +#endif + set_bit(propbit, dev->propbit); + set_bit(KEY_HOMEPAGE, dev->keybit); + +#ifdef SW_GLOVE + input_set_capability(dev, EV_SW, SW_GLOVE); +#endif + 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_TOOL_TYPE, MT_TOOL_FINGER, + MT_TOOL_FINGER, 0, 0); +#ifdef ABS_MT_CUSTOM + input_set_abs_params(dev, ABS_MT_CUSTOM, 0, 0xFFFF, 0, 0); +#endif + if (ts->plat_data->support_mt_pressure) + input_set_abs_params(dev, ABS_MT_PRESSURE, 0, + SEC_TS_PRESSURE_MAX, 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_fw_init(struct sec_ts_data *ts) +{ + int ret = SEC_TS_ERR_NA; + bool force_update = false; + bool valid_firmware_integrity = false; + unsigned char data[5] = { 0 }; + unsigned char deviceID[5] = { 0 }; + unsigned char result = 0; + + ret = sec_ts_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_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 + input_err(true, &ts->client->dev, + "%s: invalid integrity result (0x%x)\n", + __func__, result); + } + + ret = sec_ts_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_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) || ret < 0) && + (valid_firmware_integrity == false)) + force_update = true; + + 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); + return SEC_TS_ERR_INIT; + } + + ts->touch_functions |= SEC_TS_DEFAULT_ENABLE_BIT_SETFUNC; + ret = sec_ts_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_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: fail to write Sense_on 0x%x\n", + __func__, ret); + return SEC_TS_ERR_INIT; + } + + ts->pFrame = kzalloc(ts->tx_count * ts->rx_count * 2, GFP_KERNEL); + if (!ts->pFrame) + return SEC_TS_ERR_ALLOC_FRAME; + + ts->gainTable = kzalloc(ts->tx_count * ts->rx_count, GFP_KERNEL); + if (!ts->gainTable) { + kfree(ts->pFrame); + ts->pFrame = NULL; + return SEC_TS_ERR_ALLOC_GAINTABLE; + } + + 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 0x%x\n", + __func__, ts->input_dev->name, ret); + return SEC_TS_ERR_REG_INPUT_DEV; + } + + 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 0x%x\n", + __func__, ts->input_dev_pad->name, ret); + return SEC_TS_ERR_REG_INPUT_PAD_DEV; + } + } + + return SEC_TS_ERR_NA; +} + +static void sec_ts_device_init(struct sec_ts_data *ts) +{ +#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 +} + +static struct notifier_block sec_ts_screen_nb; +static struct notifier_block sec_ts_psy_nb; + +#ifdef I2C_INTERFACE +static int sec_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +#else +static int sec_ts_probe(struct spi_device *client) +#endif +{ + struct sec_ts_data *ts; + struct sec_ts_plat_data *pdata; + int ret = 0; + + input_info(true, &client->dev, "%s\n", __func__); + +#ifdef I2C_INTERFACE + input_info(true, &client->dev, "%s: I2C interface\n", __func__); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + input_err(true, &client->dev, "%s: EIO err!\n", __func__); + return -EIO; + } +#else + input_info(true, &client->dev, "%s: SPI interface\n", __func__); +#endif + /* 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_read = sec_ts_read; + ts->sec_ts_read_heap = sec_ts_read_heap; + ts->sec_ts_write = sec_ts_write; + ts->sec_ts_write_burst = sec_ts_write_burst; + ts->sec_ts_write_burst_heap = sec_ts_write_burst_heap; + ts->sec_ts_read_bulk = sec_ts_read_bulk; + ts->sec_ts_read_bulk_heap = sec_ts_read_bulk_heap; + ts->io_burstmax = pdata->io_burstmax; +#ifdef USE_POWER_RESET_WORK + INIT_DELAYED_WORK(&ts->reset_work, sec_ts_reset_work); +#endif + INIT_WORK(&ts->suspend_work, sec_ts_suspend_work); + INIT_WORK(&ts->resume_work, sec_ts_resume_work); + INIT_WORK(&ts->charger_work, sec_ts_charger_work); + ts->event_wq = alloc_workqueue("sec_ts-event-queue", WQ_UNBOUND | + WQ_HIGHPRI | WQ_CPU_INTENSIVE, 1); + if (!ts->event_wq) { + input_err(true, &ts->client->dev, + "%s: Cannot create work thread\n", __func__); + ret = -ENOMEM; + goto error_alloc_workqueue; + } + + init_completion(&ts->bus_resumed); + complete_all(&ts->bus_resumed); + +#ifdef SEC_TS_FW_UPDATE_ON_PROBE + INIT_WORK(&ts->fw_update_work, sec_ts_fw_update_work); +#else + input_info(true, &ts->client->dev, "%s: fw update on probe disabled!\n", + __func__); + ts->fw_update_wq = alloc_workqueue("sec_ts-fw-update-queue", + WQ_UNBOUND | WQ_HIGHPRI | + WQ_CPU_INTENSIVE, 1); + if (!ts->fw_update_wq) { + input_err(true, &ts->client->dev, + "%s: Can't alloc fw update work thread\n", + __func__); + ret = -ENOMEM; + goto error_alloc_fw_update_wq; + } + INIT_DELAYED_WORK(&ts->fw_update_work, sec_ts_fw_update_work); +#endif + + ts->is_fw_corrupted = false; + + /* Assume screen is on throughout probe */ + ts->bus_refmask = SEC_TS_BUS_REF_SCREEN_ON; +#ifdef I2C_INTERFACE + i2c_set_clientdata(client, ts); +#else + spi_set_drvdata(client, ts); +#endif + +#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) + ts->tbn = tbn_init(&ts->client->dev); + if (!ts->tbn) { + input_err(true, &ts->client->dev, + "%s: TBN initialization error\n", __func__); + ret = -ENODEV; + goto err_init_tbn; + } +#endif + + 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->tid_palm_state = 0; + ts->tid_grip_state = 0; + ts->tid_touch_state = 0; + ts->palms_leaved_once = false; + ts->grips_leaved_once = false; + + ts->sec_ts_write = sec_ts_write; + ts->sec_ts_read = sec_ts_read; + ts->sec_ts_read_heap = sec_ts_read_heap; + 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->bus_mutex); + mutex_init(&ts->lock); + mutex_init(&ts->device_mutex); + mutex_init(&ts->io_mutex); + mutex_init(&ts->eventlock); + + init_completion(&ts->resume_done); + complete_all(&ts->resume_done); + + init_completion(&ts->boot_completed); + complete_all(&ts->boot_completed); + + if (pdata->always_lpmode) + ts->lowpower_mode |= SEC_TS_MODE_CUSTOMLIB_FORCE_KEY; + else + ts->lowpower_mode &= ~SEC_TS_MODE_CUSTOMLIB_FORCE_KEY; + + 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; + + ret = sec_ts_wait_for_ready(ts, SEC_TS_ACK_BOOT_COMPLETE); + if (ret < 0) { + u8 boot_status; + /* Read the boot status in case device is in bootloader mode */ + ret = ts->sec_ts_read(ts, SEC_TS_READ_BOOT_STATUS, + &boot_status, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: could not read boot status. Assuming no device connected.\n", + __func__); + goto err_init; + } + + input_info(true, &ts->client->dev, + "%s: Attempting to reflash the firmware. Boot status = 0x%02X\n", + __func__, boot_status); + if (boot_status != SEC_TS_STATUS_BOOT_MODE) + input_err(true, &ts->client->dev, + "%s: device is not in bootloader mode!\n", + __func__); + + ts->is_fw_corrupted = true; + } + + input_info(true, &client->dev, "%s: power enable\n", __func__); + + if (ts->is_fw_corrupted == false) { + switch (sec_ts_fw_init(ts)) { + case SEC_TS_ERR_INIT: + goto err_init; + case SEC_TS_ERR_ALLOC_FRAME: + goto err_allocate_frame; + case SEC_TS_ERR_ALLOC_GAINTABLE: + goto err_allocate_gaintable; + case SEC_TS_ERR_REG_INPUT_DEV: + goto err_input_register_device; + case SEC_TS_ERR_REG_INPUT_PAD_DEV: + goto err_input_pad_register_device; + } + } + + pm_qos_add_request(&ts->pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + ts->ignore_charger_nb = 0; + /* init motion filter mode */ + ts->use_default_mf = 0; + ts->mf_state = SEC_TS_MF_FILTERED; +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) + /* + * Heatmap_probe must be called before irq routine is registered, + * because heatmap_read is called from the irq context. + * If the ISR runs before heatmap_probe is finished, it will invoke + * heatmap_read and cause NPE, since read_frame would not yet be set. + */ + ts->v4l2.parent_dev = &ts->client->dev; + ts->v4l2.input_dev = ts->input_dev; + ts->v4l2.read_frame = read_heatmap_raw; + ts->v4l2.width = ts->tx_count; + ts->v4l2.height = ts->rx_count; + /* 120 Hz operation */ + ts->v4l2.timeperframe.numerator = 1; + ts->v4l2.timeperframe.denominator = 120; + ret = heatmap_probe(&ts->v4l2); + if (ret) { + input_err(true, &ts->client->dev, + "%s: Heatmap probe failed\n", __func__); + goto err_irq; + } +#endif + + input_info(true, &ts->client->dev, "%s: request_irq = %d\n", __func__, + client->irq); + + ret = request_threaded_irq(client->irq, sec_ts_isr, sec_ts_irq_thread, + ts->plat_data->irq_type, SEC_TS_NAME, ts); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: Unable to request threaded irq\n", __func__); + goto err_heatmap; + } + + ts->notifier = sec_ts_screen_nb; + ret = drm_panel_notifier_register(pdata->panel, &ts->notifier); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: drm_panel_notifier_register failed. ret = 0x%08X\n", + __func__, ret); + goto err_register_drm_client; + } + +#ifndef CONFIG_SEC_SYSFS + sec_class = class_create(THIS_MODULE, "sec"); +#endif + + device_init_wakeup(&client->dev, true); + + if (ts->is_fw_corrupted == false) + sec_ts_device_init(ts); + +#ifdef SEC_TS_FW_UPDATE_ON_PROBE + schedule_work(&ts->fw_update_work); + + /* Do not finish probe without checking and flashing the firmware */ + flush_work(&ts->fw_update_work); +#else + queue_delayed_work(ts->fw_update_wq, &ts->fw_update_work, + msecs_to_jiffies(SEC_TS_FW_UPDATE_DELAY_MS_AFTER_PROBE)); +#endif + +#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; + + ts->wlc_online = false; + ts->usb_present = false; + ts->charger_mode = SEC_TS_BIT_CHARGER_MODE_NO; + ts->wireless_psy = power_supply_get_by_name("wireless"); + ts->usb_psy = power_supply_get_by_name("usb"); + ts->psy_nb = sec_ts_psy_nb; + ret = power_supply_reg_notifier(&ts->psy_nb); + if (ret < 0) + input_err(true, &ts->client->dev, "psy notifier register failed\n"); + + 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 */ +/* + * sec_ts_fn_remove(ts); + * free_irq(client->irq, ts); + **/ +err_register_drm_client: + free_irq(client->irq, ts); +err_heatmap: +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) + heatmap_remove(&ts->v4l2); +err_irq: +#endif + pm_qos_remove_request(&ts->pm_qos_req); + 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->gainTable); +err_allocate_gaintable: + kfree(ts->pFrame); +err_allocate_frame: +err_init: + 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: +#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) + tbn_cleanup(ts->tbn); +err_init_tbn: +#endif + +#ifndef SEC_TS_FW_UPDATE_ON_PROBE + if (ts->fw_update_wq) + destroy_workqueue(ts->fw_update_wq); +error_alloc_fw_update_wq: +#endif + + if (ts->event_wq) + destroy_workqueue(ts->event_wq); +error_alloc_workqueue: + 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); + if (gpio_is_valid(pdata->switch_gpio)) + gpio_free(pdata->switch_gpio); + if (gpio_is_valid(pdata->reset_gpio)) + gpio_free(pdata->reset_gpio); + +error_allocate_pdata: + if (ret == -ECONNREFUSED) + sec_ts_delay(100); + if (ret != -EPROBE_DEFER) + 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(%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->nv, 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); +#ifdef SW_GLOVE + input_report_switch(ts->input_dev, SW_GLOVE, false); +#endif + ts->touchkey_glove_mode_status = false; + ts->touch_count = 0; + ts->check_multi = 0; + ts->tid_palm_state = 0; + ts->tid_grip_state = 0; + ts->tid_touch_state = 0; + ts->palms_leaved_once = false; + ts->grips_leaved_once = false; + +#ifdef KEY_SIDE_GESTURE + 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); + } +#endif + 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(%X|%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->nv, ts->cal_count, + 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); +#ifdef SW_GLOVE + input_report_switch(ts->input_dev, SW_GLOVE, false); +#endif + ts->touchkey_glove_mode_status = false; + ts->touch_count = 0; + ts->check_multi = 0; + ts->tid_palm_state = 0; + ts->tid_grip_state = 0; + ts->tid_touch_state = 0; + ts->palms_leaved_once = false; + ts->grips_leaved_once = false; + +#ifdef KEY_SIDE_GESTURE + 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); + } +#endif + 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); + + ts->reset_is_on_going = true; + input_info(true, &ts->client->dev, "%s\n", __func__); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_RESET, true); + + sec_ts_stop_device(ts); + + sec_ts_delay(30); + + sec_ts_start_device(ts); + + if (ts->input_dev_touch->disabled) { + input_err(true, &ts->client->dev, + "%s: call input_close\n", __func__); + + sec_ts_input_close(ts->input_dev); + + 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_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_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; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_RESET, false); +} +#endif + +void sec_ts_read_init_info(struct sec_ts_data *ts) +{ +#ifndef CONFIG_SEC_FACTORY + struct sec_ts_test_mode mode; + char para = TO_TOUCH_MODE; +#endif +#ifdef USE_PRESSURE_SENSOR + unsigned char data[18] = { 0 }; +#endif + int ret; + + ts->nv = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_FAC_RESULT); + ts->cal_count = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_CAL_COUNT); + ts->pressure_cal_base = get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_PRESSURE_BASE_CAL_COUNT); + ts->pressure_cal_delta = get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_PRESSURE_DELTA_CAL_COUNT); + + input_info(true, &ts->client->dev, + "%s: fac_nv:%02X, cal_count:%02X\n", + __func__, ts->nv, ts->cal_count); + +#ifdef PAT_CONTROL + ts->tune_fix_ver = (get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_TUNE_VERSION) << 8) | + get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_TUNE_VERSION + 1); + input_info(true, &ts->client->dev, + "%s: tune_fix_ver [%04X]\n", __func__, ts->tune_fix_ver); +#endif + +#ifdef USE_PRESSURE_SENSOR + ret = ts->sec_ts_read(ts, SEC_TS_CMD_SET_GET_PRESSURE, data, 18); + if (ret < 0) + return; + + ts->pressure_left = ((data[16] << 8) | data[17]); + ts->pressure_center = ((data[8] << 8) | data[9]); + ts->pressure_right = ((data[0] << 8) | data[1]); + input_info(true, &ts->client->dev, + "%s: left: %d, center: %d, right: %d\n", __func__, + ts->pressure_left, ts->pressure_center, ts->pressure_right); +#endif + +#ifndef CONFIG_SEC_FACTORY + /* run self-test */ + disable_irq(ts->client->irq); + execute_selftest(ts, + TEST_OPEN | TEST_NODE_VARIANCE | + TEST_SHORT | TEST_SELF_NODE | TEST_NOT_SAVE); + 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_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); + + /* run ambient read */ + 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); +#endif + + input_log_fix(); +} + +static void sec_ts_fw_update_work(struct work_struct *work) +{ +#ifdef SEC_TS_FW_UPDATE_ON_PROBE + struct sec_ts_data *ts = container_of(work, struct sec_ts_data, + fw_update_work); +#else + struct delayed_work *fw_update_work = container_of(work, + struct delayed_work, work); + struct sec_ts_data *ts = container_of(fw_update_work, + struct sec_ts_data, fw_update_work); +#endif + + int ret; + + input_info(true, &ts->client->dev, + "%s: Beginning firmware update after probe.\n", __func__); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_FW_UPDATE, true); + + ret = sec_ts_firmware_update_on_probe(ts, false); + if (ret < 0) + input_info(true, &ts->client->dev, + "%s: firmware update was unsuccessful.\n", + __func__); + + if (ts->is_fw_corrupted == true && ret == 0) { + ret = sec_ts_fw_init(ts); + if (ret == SEC_TS_ERR_NA) { + ts->is_fw_corrupted = false; + sec_ts_device_init(ts); + } else + input_info(true, &ts->client->dev, + "%s: fail to sec_ts_fw_init 0x%x\n", + __func__, ret); + } + + if (ts->is_fw_corrupted == false) + sec_ts_read_init_info(ts); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_FW_UPDATE, false); +} + +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) + sec_ts_set_custom_library(ts); + #endif + + data = (ts->lowpower_mode & SEC_TS_MODE_LOWPOWER_FLAG) >> 1; + ret = sec_ts_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__); + } + +retry_pmode: + ret = sec_ts_write(ts, SEC_TS_CMD_SET_POWER_MODE, &mode, 1); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: failed\n", __func__); + sec_ts_delay(50); + + /* read data */ + + ret = sec_ts_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__); + 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; + } + + ret = sec_ts_write(ts, SEC_TS_CMD_CLEAR_EVENT_STACK, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: write clear event failed\n", __func__); + + + 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); + } + + ts->lowpower_status = mode; + input_info(true, &ts->client->dev, "%s: end\n", __func__); + + 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; + + ts->input_closed = false; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_INPUT_DEV, true); + + if (ts->lowpower_status) { +#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 + ts->power_status = SEC_TS_STATE_POWER_ON; + } 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); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_INPUT_DEV, false); + + return 0; +} + +static void sec_ts_input_close(struct input_dev *dev) +{ + struct sec_ts_data *ts = input_get_drvdata(dev); + + ts->input_closed = true; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_INPUT_DEV, true); + + cancel_work_sync(&ts->suspend_work); + cancel_work_sync(&ts->resume_work); + +#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); + ts->power_status = SEC_TS_STATE_LPM; + } else { + sec_ts_stop_device(ts); + } + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_INPUT_DEV, false); +} +#endif + +#ifdef I2C_INTERFACE +static int sec_ts_remove(struct i2c_client *client) +#else +static int sec_ts_remove(struct spi_device *client) +#endif +{ +#ifdef I2C_INTERFACE + struct sec_ts_data *ts = i2c_get_clientdata(client); +#else + struct sec_ts_data *ts = spi_get_drvdata(client); +#endif + const struct sec_ts_plat_data *pdata = ts->plat_data; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + if (ts_dup == NULL || ts->probe_done == false) + return 0; + + /* Force the bus active throughout removal of the client */ + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_FORCE_ACTIVE, true); + + power_supply_unreg_notifier(&ts->psy_nb); + drm_panel_notifier_unregister(pdata->panel, &ts->notifier); + + cancel_work_sync(&ts->suspend_work); + cancel_work_sync(&ts->resume_work); + cancel_work_sync(&ts->charger_work); + destroy_workqueue(ts->event_wq); + +#ifdef SEC_TS_FW_UPDATE_ON_PROBE + cancel_work_sync(&ts->fw_update_work); +#else + cancel_delayed_work_sync(&ts->fw_update_work); + destroy_workqueue(ts->fw_update_wq); +#endif + + disable_irq_nosync(ts->client->irq); + free_irq(ts->client->irq, ts); + input_info(true, &ts->client->dev, "%s: irq disabled\n", __func__); + +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) + heatmap_remove(&ts->v4l2); +#endif + + pm_qos_remove_request(&ts->pm_qos_req); + +#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); + + 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; + + /* need to do software reset for next sec_ts_probe() without error */ + ts->sec_ts_write(ts, SEC_TS_CMD_SW_RESET, NULL, 0); + + ts->plat_data->power(ts, false); + +#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) + tbn_cleanup(ts->tbn); +#endif + + if (gpio_is_valid(ts->plat_data->irq_gpio)) + gpio_free(ts->plat_data->irq_gpio); + if (gpio_is_valid(ts->plat_data->switch_gpio)) + gpio_free(ts->plat_data->switch_gpio); + if (gpio_is_valid(ts->plat_data->reset_gpio)) + gpio_free(ts->plat_data->reset_gpio); + + sec_ts_raw_device_exit(ts); +#ifndef CONFIG_SEC_SYSFS + class_destroy(sec_class); +#endif + +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) + kfree(ts->heatmap_buff); +#endif + kfree(ts->gainTable); + kfree(ts->pFrame); + kfree(ts); + return 0; +} + +#ifdef I2C_INTERFACE +static void sec_ts_shutdown(struct i2c_client *client) +#else +static void sec_ts_shutdown(struct spi_device *client) +#endif +{ + pr_info("%s\n", __func__); + if (ts_dup) + 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; + } + + ts->power_status = SEC_TS_STATE_POWER_OFF; + + disable_irq(ts->client->irq); + 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; + + 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; + sec_ts_wait_for_ready(ts, SEC_TS_ACK_BOOT_COMPLETE); + + if (ts->plat_data->enable_sync) + ts->plat_data->enable_sync(true); + + if (ts->flip_enable) { + ret = sec_ts_write(ts, SEC_TS_CMD_SET_COVERTYPE, + &ts->cover_cmd, 1); + + 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", + __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", __func__); + } + + ts->touch_functions = ts->touch_functions | + SEC_TS_DEFAULT_ENABLE_BIT_SETFUNC; + ret = sec_ts_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", __func__); + + #ifdef SEC_TS_SUPPORT_CUSTOMLIB + if (ts->use_customlib) + sec_ts_set_custom_library(ts); + #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_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_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_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__); + } + + /* Sense_on */ + ret = sec_ts_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 0; +} + +#ifdef CONFIG_PM +static int sec_ts_pm_suspend(struct device *dev) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + if (ts->bus_refmask) + input_info(true, &ts->client->dev, + "%s: bus_refmask 0x%X\n", __func__, ts->bus_refmask); + + if (ts->power_status != SEC_TS_STATE_SUSPEND) { + input_err(true, &ts->client->dev, + "%s: can't suspend because touch bus is in use!\n", + __func__); + return -EBUSY; + } + + 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_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 + +/* + * Configure the switch GPIO to toggle bus master between AP and SLPI. + * gpio_value takes one of + * { SEC_SWITCH_GPIO_VALUE_SLPI_MASTER, SEC_SWITCH_GPIO_VALUE_AP_MASTER } + */ +static void sec_set_switch_gpio(struct sec_ts_data *ts, int gpio_value) +{ + int retval; + unsigned int gpio = ts->plat_data->switch_gpio; + + if (!gpio_is_valid(gpio)) + return; + + input_info(true, &ts->client->dev, "%s: toggling switch to %s\n", + __func__, gpio_value == SEC_SWITCH_GPIO_VALUE_AP_MASTER ? + "AP" : "SLPI"); + + retval = gpio_direction_output(gpio, gpio_value); + if (retval < 0) + input_err(true, &ts->client->dev, + "%s: Failed to toggle switch_gpio, err = %d\n", + __func__, retval); +} + +static void sec_ts_suspend_work(struct work_struct *work) +{ + struct sec_ts_data *ts = container_of(work, struct sec_ts_data, + suspend_work); + int ret = 0; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + mutex_lock(&ts->device_mutex); + + reinit_completion(&ts->bus_resumed); + + if (ts->power_status == SEC_TS_STATE_SUSPEND) { + input_err(true, &ts->client->dev, "%s: already suspended.\n", + __func__); + mutex_unlock(&ts->device_mutex); + return; + } + + pm_stay_awake(&ts->client->dev); + + /* Stop T-IC */ + sec_ts_fix_tmode(ts, TOUCH_SYSTEM_MODE_SLEEP, TOUCH_MODE_STATE_STOP); + ret = sec_ts_write(ts, SEC_TS_CMD_CLEAR_EVENT_STACK, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: write clear event failed\n", __func__); + + disable_irq_nosync(ts->client->irq); + sec_ts_locked_release_all_finger(ts); + + if (ts->plat_data->enable_sync) + ts->plat_data->enable_sync(false); + + ts->power_status = SEC_TS_STATE_SUSPEND; + + sec_ts_pinctrl_configure(ts, false); + + sec_set_switch_gpio(ts, SEC_SWITCH_GPIO_VALUE_SLPI_MASTER); + +#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) + if (ts->tbn) + tbn_release_bus(ts->tbn); +#endif + pm_relax(&ts->client->dev); + mutex_unlock(&ts->device_mutex); +} + +static void sec_ts_resume_work(struct work_struct *work) +{ + struct sec_ts_data *ts = container_of(work, struct sec_ts_data, + resume_work); + int ret = 0; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + mutex_lock(&ts->device_mutex); + +#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) + if (ts->tbn) + tbn_request_bus(ts->tbn); +#endif + + sec_set_switch_gpio(ts, SEC_SWITCH_GPIO_VALUE_AP_MASTER); + + sec_ts_pinctrl_configure(ts, true); + + if (ts->power_status == SEC_TS_STATE_POWER_ON) { + input_err(true, &ts->client->dev, "%s: already resumed.\n", + __func__); + mutex_unlock(&ts->device_mutex); + return; + } + + sec_ts_locked_release_all_finger(ts); + + ts->power_status = SEC_TS_STATE_POWER_ON; + + ret = sec_ts_system_reset(ts); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: reset failed! ret %d\n", __func__, ret); + + if (ts->plat_data->enable_sync) + ts->plat_data->enable_sync(true); + + ts->touch_functions = + ts->touch_functions | SEC_TS_DEFAULT_ENABLE_BIT_SETFUNC; + ret = sec_ts_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.", + __func__); + +#ifdef SEC_TS_SUPPORT_CUSTOMLIB + if (ts->use_customlib) + sec_ts_set_custom_library(ts); +#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_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_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_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__); + } + + /* set charger mode */ + ret = ts->sec_ts_write(ts, SET_TS_CMD_SET_CHARGER_MODE, + &ts->charger_mode, 1); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: write reg %#x %#x failed, returned %i\n", + __func__, SET_TS_CMD_SET_CHARGER_MODE, ts->charger_mode, + ret); + else + input_info(true, &ts->client->dev, "%s: set charger mode %#x\n", + __func__, ts->charger_mode); + queue_work(ts->event_wq, &ts->charger_work); + + /* Sense_on */ + ret = sec_ts_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: failed to write Sense_on.\n", __func__); + + enable_irq(ts->client->irq); + + complete_all(&ts->bus_resumed); + + mutex_unlock(&ts->device_mutex); +} + +static void sec_ts_charger_work(struct work_struct *work) +{ + int ret; + union power_supply_propval prop = {0,}; + struct sec_ts_data *ts = container_of(work, struct sec_ts_data, + charger_work); + u8 charger_mode = SEC_TS_BIT_CHARGER_MODE_NO; + bool usb_present = ts->usb_present; + bool wlc_online = ts->wlc_online; + + /* usb case */ + ret = power_supply_get_property(ts->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (ret == 0) { + usb_present = !!prop.intval; + if (usb_present) + charger_mode = SEC_TS_BIT_CHARGER_MODE_WIRE_CHARGER; + } + + /* wlc case */ + ret = power_supply_get_property(ts->wireless_psy, + POWER_SUPPLY_PROP_ONLINE, &prop); + if (ret == 0) { + wlc_online = !!prop.intval; + if (wlc_online) + charger_mode = SEC_TS_BIT_CHARGER_MODE_WIRELESS_CHARGER; + } + + /* rtx case */ + ret = power_supply_get_property(ts->wireless_psy, + POWER_SUPPLY_PROP_RTX, &prop); + if (ret == 0) + pr_debug("%s: RTX %s", __func__, + (!!prop.intval) ? "ON" : "OFF"); + + if (usb_present == ts->usb_present && + wlc_online == ts->wlc_online && + ts->keep_wlc_mode == false) + return; + + /* keep wlc mode if usb plug in w/ wlc off case */ + if (ts->keep_wlc_mode) { + input_info(true, &ts->client->dev, + "keep wlc mode after usb plug in during wlc online"); + charger_mode = SEC_TS_BIT_CHARGER_MODE_WIRELESS_CHARGER; + } + + input_info(true, &ts->client->dev, + "%s: keep_wlc_mode %d, USB(%d->%d), WLC(%d->%d), charger_mode(%#x->%#x)", + __func__, + ts->keep_wlc_mode, + ts->usb_present, usb_present, + ts->wlc_online, wlc_online, + ts->charger_mode, charger_mode); + + if (ts->charger_mode != charger_mode) { + if (ts->power_status == SEC_TS_STATE_POWER_ON) { + ret = ts->sec_ts_write(ts, SET_TS_CMD_SET_CHARGER_MODE, + &charger_mode, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: write reg %#x %#x failed, returned %i\n", + __func__, SET_TS_CMD_SET_CHARGER_MODE, + charger_mode, ret); + return; + } + + input_info(true, &ts->client->dev, + "%s: charger_mode change from %#x to %#x\n", + __func__, ts->charger_mode, charger_mode); + } else { + input_info(true, &ts->client->dev, + "%s: ONLY update charger_mode status from %#x to %#x, then will apply during resume\n", + __func__, ts->charger_mode, charger_mode); + } + ts->charger_mode = charger_mode; + } + + /* update final charger state */ + ts->wlc_online = wlc_online; + ts->usb_present = usb_present; + ts->keep_wlc_mode = false; +} + +static void sec_ts_aggregate_bus_state(struct sec_ts_data *ts) +{ + input_dbg(true, &ts->client->dev, "%s: bus_refmask = 0x%02X.\n", + __func__, ts->bus_refmask); + + /* Complete or cancel any outstanding transitions */ + cancel_work_sync(&ts->suspend_work); + cancel_work_sync(&ts->resume_work); + + if ((ts->bus_refmask == 0 && + ts->power_status == SEC_TS_STATE_SUSPEND) || + (ts->bus_refmask != 0 && + ts->power_status != SEC_TS_STATE_SUSPEND)) + return; + + if (ts->bus_refmask == 0) + queue_work(ts->event_wq, &ts->suspend_work); + else + queue_work(ts->event_wq, &ts->resume_work); +} + +int sec_ts_set_bus_ref(struct sec_ts_data *ts, u16 ref, bool enable) +{ + int result = 0; + + mutex_lock(&ts->bus_mutex); + + input_dbg(true, &ts->client->dev, "%s: bus_refmask = 0x%02X.\n", + __func__, ref); + + if ((enable && (ts->bus_refmask & ref)) || + (!enable && !(ts->bus_refmask & ref))) { + input_info(true, &ts->client->dev, + "%s: reference is unexpectedly set: mask=0x%04X, ref=0x%04X, enable=%d.\n", + __func__, ts->bus_refmask, ref, enable); + mutex_unlock(&ts->bus_mutex); + return -EINVAL; + } + + if (enable) { + /* IRQs can only keep the bus active. IRQs received while the + * bus is transferred to SLPI should be ignored. + */ + if (ref == SEC_TS_BUS_REF_IRQ && ts->bus_refmask == 0) + result = -EAGAIN; + else + ts->bus_refmask |= ref; + } else + ts->bus_refmask &= ~ref; + sec_ts_aggregate_bus_state(ts); + + mutex_unlock(&ts->bus_mutex); + + /* When triggering a wake, wait up to one second to resume. SCREEN_ON + * and IRQ references do not need to wait. + */ + if (enable && + ref != SEC_TS_BUS_REF_SCREEN_ON && ref != SEC_TS_BUS_REF_IRQ) { + wait_for_completion_timeout(&ts->bus_resumed, HZ); + if (ts->power_status != SEC_TS_STATE_POWER_ON) { + input_info(true, &ts->client->dev, + "%s: Failed to wake the touch bus.\n", + __func__); + result = -ETIMEDOUT; + } + } + + return result; +} + +static int sec_ts_screen_state_chg_callback(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct sec_ts_data *ts = container_of(nb, struct sec_ts_data, + notifier); + struct drm_panel_notifier *evdata = (struct drm_panel_notifier *)data; + unsigned int blank; + + input_dbg(true, &ts->client->dev, "%s: enter.\n", __func__); + + if (val != DRM_PANEL_EVENT_BLANK && val != DRM_PANEL_EARLY_EVENT_BLANK) + return NOTIFY_DONE; + + if (!ts || !evdata || !evdata->data) { + input_err(true, &ts->client->dev, + "%s: Bad screen state change notifier call.\n", + __func__); + return NOTIFY_DONE; + } + + blank = *((unsigned int *)evdata->data); + switch (blank) { + case DRM_PANEL_BLANK_POWERDOWN: + case DRM_PANEL_BLANK_LP: + if (val == DRM_PANEL_EARLY_EVENT_BLANK) { + input_dbg(true, &ts->client->dev, + "%s: DRM_PANEL_BLANK_POWERDOWN.\n", __func__); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SCREEN_ON, false); + } + break; + case DRM_PANEL_BLANK_UNBLANK: + if (val == DRM_PANEL_EVENT_BLANK) { + input_dbg(true, &ts->client->dev, + "%s: DRM_PANEL_BLANK_UNBLANK.\n", __func__); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SCREEN_ON, true); + } + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block sec_ts_screen_nb = { + .notifier_call = sec_ts_screen_state_chg_callback, +}; + +/* + * power supply callback + */ +static int sec_ts_psy_cb(struct notifier_block *nb, + unsigned long val, void *data) +{ + u64 debounce = 500; + struct sec_ts_data *ts = container_of(nb, struct sec_ts_data, psy_nb); + + pr_debug("%s: val %lu", __func__, val); + + if (val != PSY_EVENT_PROP_CHANGED || + ts->wireless_psy == NULL || + ts->usb_psy == NULL || + (ts->wireless_psy != data && ts->usb_psy != data) || + ts->ignore_charger_nb == 1) + return NOTIFY_OK; + + if (ts->usb_psy == data) { + ts->usb_changed_timestamp = ktime_get(); + if (ts->wlc_online) { + input_dbg(true, &ts->client->dev, + "%s: ignore this usb_psy changed during wlc_online!", + __func__); + return NOTIFY_OK; + } + } + + if (ts->wireless_psy == data) { + /* keep wlc mode after usb plug in during wlc online */ + if (ts->wlc_online == true && + ts->usb_present == false && + ktime_before(ktime_get(), + ktime_add_ms(ts->usb_changed_timestamp, debounce))) + ts->keep_wlc_mode = true; + } + + if (ts->power_status == SEC_TS_STATE_POWER_ON) + queue_work(ts->event_wq, &ts->charger_work); + + return NOTIFY_OK; +} + +static struct notifier_block sec_ts_psy_nb = { + .notifier_call = sec_ts_psy_cb, +}; + +#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 + +#ifdef I2C_INTERFACE +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_NAME, +#ifdef CONFIG_OF + .of_match_table = sec_ts_match_table, +#endif +#ifdef CONFIG_PM + .pm = &sec_ts_dev_pm_ops, +#endif + }, +}; +#else +static struct spi_driver sec_ts_driver = { + .probe = sec_ts_probe, + .remove = sec_ts_remove, + .shutdown = sec_ts_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = SEC_TS_NAME, +#ifdef CONFIG_OF + .of_match_table = sec_ts_match_table, +#endif +#ifdef CONFIG_PM + .pm = &sec_ts_dev_pm_ops, +#endif + }, +}; +#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__); + +#ifdef I2C_INTERFACE + return i2c_add_driver(&sec_ts_driver); +#else + return spi_register_driver(&sec_ts_driver); +#endif +} + +static void __exit sec_ts_exit(void) +{ + +#ifdef I2C_INTERFACE + i2c_del_driver(&sec_ts_driver); +#else + spi_unregister_driver(&sec_ts_driver); +#endif +} + +MODULE_AUTHOR("Hyobae, Ahn<hyobae.ahn@samsung.com>"); +MODULE_DESCRIPTION("Samsung Electronics TouchScreen driver"); +MODULE_LICENSE("GPL"); + +module_init(sec_ts_init); +module_exit(sec_ts_exit); diff --git a/sec_ts.h b/sec_ts.h new file mode 100644 index 0000000..4ebae8b --- /dev/null +++ b/sec_ts.h @@ -0,0 +1,1162 @@ +/* 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 <asm/unaligned.h> +#include <linux/completion.h> +#include <linux/ctype.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <linux/hrtimer.h> +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <linux/input.h> +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) +#include <linux/input/heatmap.h> +#endif +#include <linux/input/mt.h> +#include "sec_cmd.h" +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <drm/drm_panel.h> +#include <linux/of_gpio.h> +#include <linux/platform_device.h> +#include <linux/pm_qos.h> +#include <linux/power_supply.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/time.h> +#include <linux/uaccess.h> +#include <linux/vmalloc.h> +#include <linux/workqueue.h> +#ifdef CONFIG_SEC_SYSFS +#include <linux/sec_sysfs.h> +#endif + +#ifdef CONFIG_INPUT_BOOSTER +#include <linux/input/input_booster.h> +#endif + +#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) +#include <linux/input/touch_bus_negotiator.h> +#endif + +#define SEC_TS_NAME "sec_ts" +#define SEC_TS_DEVICE_NAME "SEC_TS" + +#undef SEC_TS_DEBUG_IO +#define USE_OPEN_CLOSE +#undef USE_RESET_DURING_POWER_ON +#undef USE_RESET_EXIT_LPM +#undef USE_POR_AFTER_I2C_RETRY +#undef USER_OPEN_DWORK +#undef USE_PRESSURE_SENSOR //TODO: check this +#undef PAT_CONTROL //TODO: check this + +#if defined(USE_RESET_DURING_POWER_ON) ||\ + defined(USE_POR_AFTER_I2C_RETRY) || defined(USE_RESET_EXIT_LPM) +#define USE_POWER_RESET_WORK +#endif + +#ifndef I2C_INTERFACE +#define SPI_CLOCK_FREQ 10000000 +#define SPI_DELAY_CS 10 +#define SEC_TS_SPI_SYNC_CODE 0xAA +#define SEC_TS_SPI_HEADER_SIZE 5 +#define SEC_TS_SPI_READ_HEADER_SIZE 7 +#define SEC_TS_SPI_CHECKSUM_SIZE 1 + +#define SEC_TS_SPI_CMD_OK 0x0 +#define SEC_TS_SPI_CMD_NG (1u<<7) +#define SEC_TS_SPI_CMD_UNKNOWN (SEC_TS_SPI_CMD_NG | (1)) +#define SEC_TS_SPI_CMD_FAIL (SEC_TS_SPI_CMD_NG | (2)) +#define SEC_TS_SPI_CMD_BAD_PARAM (SEC_TS_SPI_CMD_NG | (3)) +#define SEC_TS_SPI_CMD_CHKSUM_FAIL (SEC_TS_SPI_CMD_NG | (4)) +#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 SEC_TS_PRESSURE_MAX 0x3f + +#define IO_WRITE_BUFFER_SIZE (256 - 1)//10 + +#ifdef I2C_INTERFACE +/* max read size: from sec_ts_read_event() at sec_ts.c */ +#define IO_PREALLOC_READ_BUF_SZ (32 * SEC_TS_EVENT_BUFF_SIZE) +/* max write size: from sec_ts_flashpagewrite() at sec_ts_fw.c */ +#define IO_PREALLOC_WRITE_BUF_SZ (SEC_TS_SPI_HEADER_SIZE + 1 + 2 +\ + SEC_TS_FW_BLK_SIZE_MAX + 1) +#else +#define IO_PREALLOC_READ_BUF_SZ 2048 +#define IO_PREALLOC_WRITE_BUF_SZ 1024 +#endif + +#define SEC_TS_FW_HEADER_SIGN 0x53494654 +#define SEC_TS_FW_CHUNK_SIGN 0x53434654 + +#undef SEC_TS_FW_UPDATE_ON_PROBE +#define SEC_TS_FW_UPDATE_DELAY_MS_AFTER_PROBE 1000 + +#define AMBIENT_CAL 0 +#define OFFSET_CAL_SDC 1 +#define OFFSET_CAL_SEC 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_PRESSURE_INDEX 5 + +#define SEC_TS_NVM_OFFSET_PRESSURE_STRENGTH 6 +#define SEC_TS_NVM_OFFSET_PRESSURE_STRENGTH_1 6 +#define SEC_TS_NVM_OFFSET_PRESSURE_STRENGTH_2 12 +#define SEC_TS_NVM_OFFSET_PRESSURE_STRENGTH_3 18 +#define SEC_TS_NVM_OFFSET_PRESSURE_STRENGTH_4 24 + +#define SEC_TS_NVM_OFFSET_PRESSURE_RAWDATA 30 +#define SEC_TS_NVM_OFFSET_PRESSURE_RAWDATA_1 30 +#define SEC_TS_NVM_OFFSET_PRESSURE_RAWDATA_2 36 +#define SEC_TS_NVM_OFFSET_PRESSURE_RAWDATA_3 42 +#define SEC_TS_NVM_OFFSET_PRESSURE_RAWDATA_4 48 +#define SEC_TS_NVM_SIZE_PRESSURE_BLOCK 6 + +#define SEC_TS_NVM_OFFSET_PRESSURE_BASE_CAL_COUNT 54 +#define SEC_TS_NVM_OFFSET_PRESSURE_DELTA_CAL_COUNT 55 +#define SEC_TS_NVM_SIZE_PRESSURE_CAL_BLOCK 1 + +#define SEC_TS_NVM_LAST_BLOCK_OFFSET \ + SEC_TS_NVM_OFFSET_PRESSURE_DELTA_CAL_COUNT +#define SEC_TS_NVM_LAST_BLOCK_SIZE SEC_TS_NVM_SIZE_PRESSURE_CAL_BLOCK + +#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 touch 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_DISABLE_GAIN_LIMIT 0x2A + +#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_DISABLE_NORM_TABLE 0x40 +#define SEC_TS_CMD_READ_NORM_TABLE 0x41 +#define SEC_TS_CMD_DISABLE_BASELINE_ADAPT 0x43 +#define SEC_TS_CMD_DISABLE_DF 0x44 +#define SEC_TS_CMD_ERASE_FLASH 0x45 +#define SEC_TS_CMD_RESET_BASELINE 0x47 +#define SEC_TS_CMD_SET_CONT_REPORT 0x49 +#define SEC_TS_CMD_WRITE_NORM_TABLE 0x49 +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) +#define SEC_TS_CMD_HEATMAP_READ 0x4A +#define SEC_TS_CMD_HEATMAP_ENABLE 0x4B +#endif +#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_P2PTEST 0x82 +#define SEC_TS_CMD_SET_P2PTEST_MODE 0x83 +#define SEC_TS_CMD_NVM 0x85 +#define SEC_TS_CMD_SET_WET_MODE 0x8B +#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_CUSTOMLIB_OFFSET_PRESSURE_LEVEL 0x5E +#define SEC_TS_CMD_CUSTOMLIB_OFFSET_PRESSURE_THD_HIGH 0x84 +#define SEC_TS_CMD_CUSTOMLIB_OFFSET_PRESSURE_THD_LOW 0x86 +#define SEC_TS_CMD_CUSTOMLIB_LP_DUMP 0x01F0 + +#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 +#define SEC_TS_CAAT_READ_STORED_DATA 0xB7 +#define SEC_TS_CMD_SET_NOISE_MODE 0xBB +#define SEC_TS_CMD_SET_GRIP_DETEC 0xBC +#define SEC_TS_CMD_SET_PALM_DETEC 0xBE +#define SEC_TS_READ_CSRAM_RTDP_DATA 0xC3 + +/* 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_TOUCH_ENGINE_MODE 0xE1 +#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 +#define SEC_TS_TOUCHTYPE_GRIP 10 + +/* 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_P2P_TEST_DONE 0x42 + +/* 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 + +#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) + +#ifdef PAT_CONTROL +/* + * <<< apply to server >>> + * 0x00 : no action + * 0x01 : clear nv + * 0x02 : pat magic + * 0x03 : rfu + * + * <<< use for temp bin >>> + * 0x05 : forced clear nv & f/w update before pat magic, eventhough same f/w + * 0x06 : rfu + **/ +#define PAT_CONTROL_NONE 0x00 +#define PAT_CONTROL_CLEAR_NV 0x01 +#define PAT_CONTROL_PAT_MAGIC 0x02 +#define PAT_CONTROL_FORCE_UPDATE 0x05 + +#define PAT_COUNT_ZERO 0x00 +#define PAT_MAX_LCIA 0x80 +#define PAT_MAGIC_NUMBER 0x83 +#define PAT_MAX_MAGIC 0xC5 +#define PAT_EXT_FACT 0xE0 +#define PAT_MAX_EXT 0xF5 +#endif + +#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 spec_check_type { + SPEC_NO_CHECK = 0, + SPEC_CHECK = 1, + SPEC_PASS = 2, + SPEC_FAIL = 3, +}; + +enum region_type { + REGION_NORMAL = 0, + REGION_EDGE = 1, + REGION_CORNER = 2, + REGION_NOTCH = 3, + REGION_TYPE_COUNT = 4, + /* REGION type should be continuous number start from 0, + * since REGION_TYPE_COUNT is used for type count + */ +}; + +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, +}; + +enum TOUCH_POWER_MODE { + SEC_TS_STATE_POWER_OFF = 0, + SEC_TS_STATE_SUSPEND, + SEC_TS_STATE_LPM, + SEC_TS_STATE_POWER_ON +}; + +enum TOUCH_SYSTEM_MODE { + 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_SLEEP = 6 +}; + +enum TOUCH_MODE_STATE { + TOUCH_MODE_STATE_IDLE = 0, + TOUCH_MODE_STATE_HOVER = 1, + TOUCH_MODE_STATE_STOP = 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 +}; + +enum { + TEST_OPEN = (0x1 << 0), + TEST_NODE_VARIANCE = (0x1 << 1), + TEST_SHORT = (0x1 << 2), + TEST_SELF_NODE = (0x1 << 5), + TEST_NOT_SAVE = (0x1 << 7), + TEST_HIGH_FREQ = (0x1 << 8), +}; + +enum switch_system_mode { + TO_TOUCH_MODE = 0, + TO_LOWPOWER_MODE = 1, + TO_SELFTEST_MODE = 2, + TO_FLASH_MODE = 3, +}; + +enum noise_mode_param { + NOISE_MODE_DEFALUT = 0x00, + NOISE_MODE_OFF = 0x10, + NOISE_MODE_FORCE_ON = 0x11, +}; + +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_NORM2_DATA = 15, /* After fs norm. data + **/ + TYPE_OFFSET_DATA_SEC = 19, /* Cap Offset in SEC + * Manufacturing Line + **/ + TYPE_OFFSET_DATA_SDC = 29, /* Cap Offset in SDC + * Manufacturing Line + **/ + TYPE_NOI_P2P_MIN = 30, /* Peak-to-peak noise Min + **/ + TYPE_NOI_P2P_MAX = 31, /* Peak-to-peak noise Max + **/ + TYPE_OFFSET_DATA_SDC_CM2 = 129, + TYPE_OFFSET_DATA_SDC_NOT_SAVE = 229, + TYPE_INVALID_DATA = 0xFF, /* Invalid data type for + * release factory mode + **/ +}; + +enum CUSTOMLIB_EVENT_TYPE { + 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_RLS_NO_HAPTIC = 0x0E +}; + +enum { + SEC_TS_BUS_REF_SCREEN_ON = 0x01, + SEC_TS_BUS_REF_IRQ = 0x02, + SEC_TS_BUS_REF_RESET = 0x04, + SEC_TS_BUS_REF_FW_UPDATE = 0x08, + SEC_TS_BUS_REF_INPUT_DEV = 0x10, + SEC_TS_BUS_REF_READ_INFO = 0x20, + SEC_TS_BUS_REF_SYSFS = 0x40, + SEC_TS_BUS_REF_FORCE_ACTIVE = 0x80 +}; + +enum { + SEC_TS_ERR_NA = 0, + SEC_TS_ERR_INIT, + SEC_TS_ERR_ALLOC_FRAME, + SEC_TS_ERR_ALLOC_GAINTABLE, + SEC_TS_ERR_REG_INPUT_DEV, + SEC_TS_ERR_REG_INPUT_PAD_DEV +}; + +#define CMD_RESULT_WORD_LEN 10 + +#define SEC_TS_IO_RESET_CNT 3 +#define SEC_TS_IO_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_AOD_GESTURE_PRESS (1 << 7) +#define SEC_TS_AOD_GESTURE_LONGPRESS (1 << 6) +#define SEC_TS_AOD_GESTURE_DOUBLETAB (1 << 5) + +#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, +}; + +enum { + HEATMAP_OFF = 0, + HEATMAP_PARTIAL = 1, + HEATMAP_FULL = 2 +}; + +/* Motion filter finite state machine (FSM) states + * SEC_TS_MF_FILTERED - default coordinate filtering + * SEC_TS_MF_UNFILTERED - unfiltered single-touch coordinates + * SEC_TS_MF_FILTERED_LOCKED - filtered coordinates. Locked until touch is + * lifted. + */ +enum motion_filter_state_t { + SEC_TS_MF_FILTERED = 0, + SEC_TS_MF_UNFILTERED = 1, + SEC_TS_MF_FILTERED_LOCKED = 2 +}; + +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) +/* Local heatmap */ +#define LOCAL_HEATMAP_WIDTH 7 +#define LOCAL_HEATMAP_HEIGHT 7 + +struct heatmap_report { + int8_t offset_x; + uint8_t size_x; + int8_t offset_y; + uint8_t size_y; + /* data is in BE order; order should be enforced after data is read */ + strength_t data[LOCAL_HEATMAP_WIDTH * LOCAL_HEATMAP_HEIGHT]; +} __packed; +#endif + +#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[REGION_TYPE_COUNT]; + short max[REGION_TYPE_COUNT]; + bool allnode; + bool frame_channel; + enum spec_check_type spec_check; +}; + +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; + } __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; +} __packed; + + +/* status id for sec_ts event */ +#define SEC_TS_EVENT_STATUS_ID_NOISE 0x64 +#define SEC_TS_EVENT_STATUS_ID_WLC 0x66 +#define SEC_TS_EVENT_STATUS_ID_GRIP 0x69 +#define SEC_TS_EVENT_STATUS_ID_PALM 0x70 + +/* 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; +} __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; +} __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; + bool grip; +}; + +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; +#ifdef I2C_INTERFACE + struct i2c_client *client; +#else + struct spi_device *client; +#endif + 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]; + + ktime_t timestamp; /* time that the event was first received from + * the touch IC, acquired during hard interrupt, + * in CLOCK_MONOTONIC + **/ + + 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 bool input_closed; + + struct mutex bus_mutex; + u16 bus_refmask; + struct completion bus_resumed; + struct completion boot_completed; + + int touch_count; + int tx_count; + int rx_count; + int io_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; + u8 gesture_status[6]; + u8 cal_status; + struct mutex lock; + struct mutex device_mutex; + struct mutex io_mutex; + struct mutex eventlock; + + struct notifier_block notifier; + + struct pm_qos_request pm_qos_req; + + /* Stop changing charger mode by notifier */ + u8 ignore_charger_nb; + /* Stop changing motion filter and keep fw design */ + u8 use_default_mf; + /* Motion filter finite state machine (FSM) state */ + enum motion_filter_state_t mf_state; + /* Time of initial single-finger touch down. This timestamp is used to + * compute the duration a single finger is touched before it is lifted. + */ + ktime_t mf_downtime; + + u8 print_format; + u8 frame_type; +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) + struct v4l2_heatmap v4l2; + strength_t *heatmap_buff; +#endif + +#ifdef USE_POWER_RESET_WORK + struct delayed_work reset_work; + volatile bool reset_is_on_going; +#endif + +#ifdef SEC_TS_FW_UPDATE_ON_PROBE + struct work_struct fw_update_work; +#else + struct delayed_work fw_update_work; + struct workqueue_struct *fw_update_wq; +#endif + struct work_struct charger_work; /* charger work */ + struct work_struct suspend_work; + struct work_struct resume_work; + struct workqueue_struct *event_wq; /* Used for event handler, + * suspend, resume threads + **/ + struct completion resume_done; + struct sec_cmd_data sec; + short *pFrame; + u8 *gainTable; + + bool probe_done; + bool reinit_done; + bool flip_enable; + 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; +#endif + u8 tsp_dump_lock; + + 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 dive_count; /* dive mode count */ + unsigned int comm_err_count; /* comm error count */ + unsigned int io_err_count; /* io 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; + +#ifdef USE_PRESSURE_SENSOR + short pressure_left; + short pressure_center; + short pressure_right; + u8 pressure_user_level; +#endif + int temp; + + int fs_postcal_mean; + + bool is_fw_corrupted; + union { + u8 cali_report[8]; + struct { + u8 cali_report_try_cnt; + u8 cali_report_pass_cnt; + u8 cali_report_fail_cnt; + u8 cali_report_status; + u8 cali_report_param_ver[4]; + }; + }; + + /* slot id active state(bit mask) for grip/palm + **/ + unsigned long tid_palm_state; + unsigned long tid_grip_state; + /* slot id active state(bit mask) for all touch types + **/ + unsigned long tid_touch_state; + /* Record the state that grip/palm was leaved once ever after any + * touch pressed. This state will set to default after all active + * touch released. + **/ + bool palms_leaved_once; + bool grips_leaved_once; + +#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) + struct tbn_context *tbn; +#endif + + struct power_supply *wireless_psy; + struct power_supply *usb_psy; + struct notifier_block psy_nb; + bool wlc_online; + bool usb_present; + bool keep_wlc_mode; + ktime_t usb_changed_timestamp; + + int (*sec_ts_write)(struct sec_ts_data *ts, u8 reg, + u8 *data, int len); + + int (*sec_ts_read)(struct sec_ts_data *ts, u8 reg, + u8 *data, int len); + int (*sec_ts_read_heap)(struct sec_ts_data *ts, u8 reg, + u8 *data, int len); + + int (*sec_ts_write_burst)(struct sec_ts_data *ts, + u8 *data, int len); + int (*sec_ts_write_burst_heap)(struct sec_ts_data *ts, + u8 *data, int len); + + int (*sec_ts_read_bulk)(struct sec_ts_data *ts, + u8 *data, int len); + int (*sec_ts_read_bulk_heap)(struct sec_ts_data *ts, + u8 *data, int len); + + int (*sec_ts_read_customlib)(struct sec_ts_data *ts, + u8 *data, int len); + + /* alloc for io read buffer */ + u8 io_read_buf[IO_PREALLOC_READ_BUF_SZ]; + /* alloc for io write buffer */ + u8 io_write_buf[IO_PREALLOC_WRITE_BUF_SZ]; +}; + +struct sec_ts_plat_data { + int max_x; + int max_y; + unsigned int irq_gpio; + int irq_type; + int io_burstmax; + int always_lpmode; + int bringup; + int mis_cal_check; + int heatmap_mode; +#ifdef PAT_CONTROL + int pat_function; + int afe_base; +#endif + 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; + int switch_gpio; + int reset_gpio; + + bool regulator_boot_on; + bool support_mt_pressure; + bool support_dex; + bool support_sidegesture; + + struct drm_panel *panel; + u32 initial_panel_index; +}; + +int sec_ts_stop_device(struct sec_ts_data *ts); +int sec_ts_start_device(struct sec_ts_data *ts); +int sec_ts_hw_reset(struct sec_ts_data *ts); +int sec_ts_sw_reset(struct sec_ts_data *ts); +int sec_ts_system_reset(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_wait_for_ready_with_count(struct sec_ts_data *ts, unsigned int ack, + unsigned int count); +int sec_ts_try_wake(struct sec_ts_data *ts, bool wake_setting); +int sec_ts_set_bus_ref(struct sec_ts_data *ts, u16 ref, bool enable); + +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); +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); +#ifdef PAT_CONTROL +void set_pat_magic_number(struct sec_ts_data *ts); +#endif +int sec_ts_run_rawdata_type(struct sec_ts_data *ts, struct sec_cmd_data *sec); +void sec_ts_run_rawdata_all(struct sec_ts_data *ts, bool full_read); +int execute_selftest(struct sec_ts_data *ts, u32 option); +int execute_p2ptest(struct sec_ts_data *ts); +int sec_ts_read_raw_data(struct sec_ts_data *ts, + struct sec_cmd_data *sec, struct sec_ts_test_mode *mode); + +#if (1)//!defined(CONFIG_SAMSUNG_PRODUCT_SHIP) +int sec_ts_raw_device_init(struct sec_ts_data *ts); +#endif +void sec_ts_raw_device_exit(struct sec_ts_data *ts); + +extern struct class *sec_class; + +#if defined(CONFIG_FB_MSM_MDSS_SAMSUNG) +extern int get_lcd_attached(char *mode); +#endif + +#if defined(CONFIG_EXYNOS_DECON_FB) +extern int get_lcd_info(char *arg); +#endif + +#ifdef CONFIG_MOTOR_DRV_MAX77865 +extern int haptic_homekey_press(void); +extern int haptic_homekey_release(void); +#else +#define haptic_homekey_press() {} +#define haptic_homekey_release() {} +#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/sec_ts_fac_spec.h b/sec_ts_fac_spec.h new file mode 100644 index 0000000..c0ae9f7 --- /dev/null +++ b/sec_ts_fac_spec.h @@ -0,0 +1,518 @@ +/* GC1 rawcap gap spec */ + +/* Cm region definition table + * 0 : Normal + * 1 : Edge + * 2 : Corner + * 3 : Notch + */ +const int cm_region[34][16] = { + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, +}; + +/* Cm spec by region + * region = {NORMAL, EDGE, CORNER, NOTCH} + * CM_MAX = max, CM_MIN = min, CM_MM = max - min + */ +const short cm_max[4] = { 720, 720, 1024, 1024 }; +const short cm_min[4] = { 190, 190, 0, 0 }; +const short cm_mm[4] = { 700, 700, 1024, 1024 }; + +const int cm_gap[34][16] = { + {60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, + {60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60}, + {60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60}, +}; + +const int cm2_region[34][16] = { + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, +}; + +const short cm2_max[4] = { 500, 500, 1024, 1024 }; +const short cm2_min[4] = { 97, 97, 0, 0 }; +const short cm2_mm[4] = { 500, 500, 1024, 1024 }; + +const int cm2_gap[34][16] = { + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, + {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, +}; + +const int noi_min[34][16] = { + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, + {-30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30}, +}; + +const int noi_max[34][16] = { + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, + {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, +}; + +const int noi_mm = 60; + +const int cm_stdev_max = 15000; + +const int cs_tx_max = -7530; + +const int cs_tx_min = -20060; + +const int cs_tx_mm = 25000; + +const int cs_rx_max = -7530; + +const int cs_rx_min = -20060; + +const int cs_rx_mm = 25000; + +/* fs_precal high limit : +20% of fs target */ +const int fs_precal_h[34][16] = { + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, + {336, 336, 336, 336, 336, 336, 336, 336, + 336, 336, 336, 336, 336, 336, 336, 336}, +}; + +/* fs_mean high limit : +4% of fs_mean target */ +const int fs_mean_target_h = 269; + +/* fs_mean low limit : -4% of fs_mean target */ +const int fs_mean_target_l = 291; + +const int fs_postcal_uniform_spec = 4; + +/* fs_precal low limit : -20% of fs target */ +const int fs_precal_l[34][16] = { + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, + {224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224}, +}; + +const int fs_target[34][16] = { + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, + {280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280}, +}; + +const int cs_tx_gap = 3600; + +const int cs_rx_gap = 3600; diff --git a/sec_ts_fn.c b/sec_ts_fn.c new file mode 100644 index 0000000..4d0eecf --- /dev/null +++ b/sec_ts_fn.c @@ -0,0 +1,8058 @@ +/* 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" +#include "sec_ts_fac_spec.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); +#ifdef PAT_CONTROL +static void get_pat_information(void *device_data); +static void set_external_factory(void *device_data); +#endif +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_x_cross_routing(void *device_data); +static void get_y_cross_routing(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 run_rawcap_high_freq_read_all(void *device_data); +static void get_rawcap(void *device_data); +static void run_rawcap_gap_read_all(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_rawdata_stdev_read(void *device_data); +static void run_rawdata_p2p_read_all(void *device_data); +static void run_rawdata_read_type(void *device_data); +static void run_rawdata_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_rawcap_gap_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_force_calibration(void *device_data); +static void get_force_calibration(void *device_data); +#ifdef USE_PRESSURE_SENSOR +static void run_force_pressure_calibration(void *device_data); +static void set_pressure_test_mode(void *device_data); +static void run_pressure_filtered_strength_read_all(void *device_data); +static void run_pressure_strength_read_all(void *device_data); +static void run_pressure_rawdata_read_all(void *device_data); +static void run_pressure_offset_read_all(void *device_data); +static void set_pressure_strength(void *device_data); +static void set_pressure_rawdata(void *device_data); +static void set_pressure_data_index(void *device_data); +static void get_pressure_strength(void *device_data); +static void get_pressure_rawdata(void *device_data); +static void get_pressure_data_index(void *device_data); +static void set_pressure_strength_clear(void *device_data); +static void get_pressure_threshold(void *device_data); +static void set_pressure_user_level(void *device_data); +static void get_pressure_user_level(void *device_data); +#endif +static void run_fs_cal_pre_press(void *device_data); +static void run_fs_cal_get_data(void *device_data); +static void run_fs_cal_post_press(void *device_data); +static void enable_fs_cal_table(void *device_data); +static void enable_coordinate_report(void *device_data); +static void enable_gain_limit(void *device_data); +static void run_trx_short_test(void *device_data); +static void set_tsp_test_result(void *device_data); +static void get_tsp_test_result(void *device_data); +static void increase_disassemble_count(void *device_data); +static void get_disassemble_count(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 set_aod_rect(void *device_data); +static void get_aod_rect(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 force_touch_active(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 set_touch_mode(void *device_data); +static void not_support_cmd(void *device_data); +static void set_palm_detection_enable(void *device_data); +static void set_grip_detection_enable(void *device_data); +static void set_wet_mode_enable(void *device_data); +static void set_noise_mode_enable(void *device_data); +static void set_continuous_report_enable(void *device_data); +static void set_charger_nb_enable(void *device_data); +static void set_print_format(void *device_data); + +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),}, +#ifdef PAT_CONTROL + {SEC_CMD("get_pat_information", get_pat_information),}, + {SEC_CMD("set_external_factory", set_external_factory),}, +#endif + {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_x_cross_routing", get_x_cross_routing),}, + {SEC_CMD("get_y_cross_routing", get_y_cross_routing),}, + {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_rawcap_gap_read_all", run_rawcap_gap_read_all),}, + {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_rawdata_stdev_read", run_rawdata_stdev_read),}, + {SEC_CMD("run_rawdata_p2p_read_all", run_rawdata_p2p_read_all),}, + {SEC_CMD("run_rawdata_read_type", run_rawdata_read_type),}, + {SEC_CMD("run_rawdata_read_all", run_rawdata_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_rawcap_gap_read_all", + run_self_rawcap_gap_read_all),}, + {SEC_CMD("run_rawcap_high_freq_read_all", + run_rawcap_high_freq_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_force_calibration", run_force_calibration),}, + {SEC_CMD("get_force_calibration", get_force_calibration),}, +#ifdef USE_PRESSURE_SENSOR + {SEC_CMD("run_force_pressure_calibration", + run_force_pressure_calibration),}, + {SEC_CMD("set_pressure_test_mode", set_pressure_test_mode),}, + {SEC_CMD("run_pressure_filtered_strength_read_all", + run_pressure_filtered_strength_read_all),}, + {SEC_CMD("run_pressure_strength_read_all", + run_pressure_strength_read_all),}, + {SEC_CMD("run_pressure_rawdata_read_all", + run_pressure_rawdata_read_all),}, + {SEC_CMD("run_pressure_offset_read_all", + run_pressure_offset_read_all),}, + {SEC_CMD("set_pressure_strength", set_pressure_strength),}, + {SEC_CMD("set_pressure_rawdata", set_pressure_rawdata),}, + {SEC_CMD("set_pressure_data_index", set_pressure_data_index),}, + {SEC_CMD("get_pressure_strength", get_pressure_strength),}, + {SEC_CMD("get_pressure_rawdata", get_pressure_rawdata),}, + {SEC_CMD("get_pressure_data_index", get_pressure_data_index),}, + {SEC_CMD("set_pressure_strength_clear", set_pressure_strength_clear),}, + {SEC_CMD("get_pressure_threshold", get_pressure_threshold),}, + {SEC_CMD("set_pressure_user_level", set_pressure_user_level),}, + {SEC_CMD("get_pressure_user_level", get_pressure_user_level),}, +#endif + {SEC_CMD("run_fs_cal_pre_press", run_fs_cal_pre_press),}, + {SEC_CMD("run_fs_cal_get_data", run_fs_cal_get_data),}, + {SEC_CMD("run_fs_cal_post_press", run_fs_cal_post_press),}, + {SEC_CMD("enable_fs_cal_table", enable_fs_cal_table),}, + {SEC_CMD("enable_coordinate_report", enable_coordinate_report),}, + {SEC_CMD("enable_gain_limit", enable_gain_limit),}, + {SEC_CMD("run_trx_short_test", run_trx_short_test),}, + {SEC_CMD("set_tsp_test_result", set_tsp_test_result),}, + {SEC_CMD("get_tsp_test_result", get_tsp_test_result),}, + {SEC_CMD("increase_disassemble_count", increase_disassemble_count),}, + {SEC_CMD("get_disassemble_count", get_disassemble_count),}, + {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("set_aod_rect", set_aod_rect),}, + {SEC_CMD("get_aod_rect", get_aod_rect),}, + {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("force_touch_active", force_touch_active),}, + {SEC_CMD("set_touchable_area", set_touchable_area),}, + {SEC_CMD("set_log_level", set_log_level),}, + {SEC_CMD("debug", debug),}, + {SEC_CMD("set_touch_mode", set_touch_mode),}, + {SEC_CMD("set_palm_detection_enable", set_palm_detection_enable),}, + {SEC_CMD("set_grip_detection_enable", set_grip_detection_enable),}, + {SEC_CMD("set_wet_mode_enable", set_wet_mode_enable),}, + {SEC_CMD("set_noise_mode_enable", set_noise_mode_enable),}, + {SEC_CMD("set_continuous_report_enable", + set_continuous_report_enable),}, + {SEC_CMD("set_charger_nb_enable", set_charger_nb_enable),}, + {SEC_CMD("set_print_format", set_print_format),}, + {SEC_CMD("not_support_cmd", not_support_cmd),}, +}; + +static void set_palm_detection_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[4] = { 0 }; + u8 para = 0x0; + u8 ret = 0; + + input_info(true, &ts->client->dev, + "%s: %d\n", __func__, sec->cmd_param[0]); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] == 1) + para = 0x1; + else if (sec->cmd_param[0] == 0) + para = 0x0; + else { + input_info(true, &ts->client->dev, + "%s: param error! param = %d\n", + __func__, sec->cmd_param[0]); + goto err_out; + } + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_PALM_DETEC, ¶, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: write reg %#x para %#x failed, returned %i\n", + __func__, SEC_TS_CMD_SET_PALM_DETEC, para, ret); + goto err_out; + } + + scnprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +err_out: + scnprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void set_grip_detection_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[4] = { 0 }; + u8 para = 0x0; + u8 ret = 0; + + input_info(true, &ts->client->dev, + "%s: %d\n", __func__, sec->cmd_param[0]); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] == 1) + para = 0x1F; + else if (sec->cmd_param[0] == 0) + para = 0x0; + else { + input_info(true, &ts->client->dev, + "%s: param error! param = %d\n", + __func__, sec->cmd_param[0]); + goto err_out; + } + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_GRIP_DETEC, ¶, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: write reg %#x para %#x failed, returned %i\n", + __func__, SEC_TS_CMD_SET_GRIP_DETEC, para, ret); + goto err_out; + } + + scnprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +err_out: + scnprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void set_wet_mode_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[4] = { 0 }; + u8 para = 0x0; + u8 ret = 0; + + input_info(true, &ts->client->dev, + "%s: %d\n", __func__, sec->cmd_param[0]); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] == 1) + para = 0x0; + else if (sec->cmd_param[0] == 0) + para = 0x1; + else { + input_info(true, &ts->client->dev, + "%s: param error! param = %d\n", + __func__, sec->cmd_param[0]); + goto err_out; + } + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_WET_MODE, ¶, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: write reg %#x para %#x failed, returned %i\n", + __func__, SEC_TS_CMD_SET_WET_MODE, para, ret); + goto err_out; + } + + scnprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +err_out: + scnprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void set_noise_mode_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[4] = { 0 }; + u8 para = 0x0; + u8 ret = 0; + + input_info(true, &ts->client->dev, + "%s: %d\n", __func__, sec->cmd_param[0]); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] == 1) + para = NOISE_MODE_DEFALUT; + else if (sec->cmd_param[0] == 0) + para = NOISE_MODE_OFF; + else { + input_info(true, &ts->client->dev, + "%s: param error! param = %d\n", + __func__, sec->cmd_param[0]); + goto err_out; + } + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_NOISE_MODE, ¶, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: write reg %#x para %#x failed, returned %i\n", + __func__, SEC_TS_CMD_SET_NOISE_MODE, para, ret); + goto err_out; + } + + scnprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +err_out: + scnprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void set_continuous_report_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[4] = { 0 }; + u8 para = 0x0; + u8 ret = 0; + + input_info(true, &ts->client->dev, + "%s: %d\n", __func__, sec->cmd_param[0]); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] == 1) + para = 0x1; + else if (sec->cmd_param[0] == 0) + para = 0x0; + else { + input_info(true, &ts->client->dev, + "%s: param error! param = %d\n", + __func__, sec->cmd_param[0]); + goto err_out; + } + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_CONT_REPORT, ¶, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: write reg %#x para %#x failed, returned %i\n", + __func__, SEC_TS_CMD_SET_CONT_REPORT, para, ret); + goto err_out; + } + + ts->use_default_mf = para; + scnprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +err_out: + scnprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void set_charger_nb_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[4] = { 0 }; + + input_info(true, &ts->client->dev, + "%s: %d\n", __func__, sec->cmd_param[0]); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] == 1) + ts->ignore_charger_nb = 0; + else if (sec->cmd_param[0] == 0) + ts->ignore_charger_nb = 1; + else { + input_info(true, &ts->client->dev, + "%s: param error! param = %d\n", + __func__, sec->cmd_param[0]); + goto err_out; + } + + scnprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +err_out: + scnprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static ssize_t scrub_pos_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_RO(scrub_pos); + +static ssize_t 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 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 }; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + buffer = vzalloc(ts->rx_count * ts->tx_count * 6); + if (!buffer) { + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + 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); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return ret; +} + +static ssize_t 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 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 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 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 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 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 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 }; + + input_info(true, &ts->client->dev, "%s: %d\n", __func__, + ts->multi_count); + + snprintf(buff, sizeof(buff), "SE%02X%02X%02X%02X%02X%02X%02X", + ts->plat_data->panel_revision, + ts->plat_data->img_version_of_bin[2], + ts->plat_data->img_version_of_bin[3], ts->nv, ts->cal_count, + ts->pressure_cal_base, ts->pressure_cal_delta); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s", buff); +} + +static ssize_t 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 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 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 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 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 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 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 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 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 pressure_enable_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 }; + + if (ts->lowpower_mode & SEC_TS_MODE_CUSTOMLIB_FORCE_KEY) + snprintf(buff, sizeof(buff), "1"); + else + snprintf(buff, sizeof(buff), "0"); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); +} + +static ssize_t pressure_enable_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); + int ret; + unsigned long value = 0; + + if (count > 2) + return -EINVAL; + + ret = kstrtoul(buf, 10, &value); + if (ret != 0) + return ret; + + if (!ts->use_customlib) + return -EINVAL; + + if (value == 1) + ts->lowpower_mode |= SEC_TS_MODE_CUSTOMLIB_FORCE_KEY; + else + ts->lowpower_mode &= ~SEC_TS_MODE_CUSTOMLIB_FORCE_KEY; + + #ifdef SEC_TS_SUPPORT_CUSTOMLIB + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + sec_ts_set_custom_library(ts); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + #endif + + return count; +} + +static ssize_t get_lp_dump_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); + u8 string_data[8] = {0, }; + u16 current_index; + int i, ret; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Touch is stopped!\n", + __func__); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return snprintf(buf, SEC_CMD_BUF_SIZE, "TSP turned off"); + } + + string_data[0] = SEC_TS_CMD_CUSTOMLIB_LP_DUMP & 0xFF; + string_data[1] = (SEC_TS_CMD_CUSTOMLIB_LP_DUMP & 0xFF00) >> 8; + + disable_irq(ts->client->irq); + + ret = ts->sec_ts_read_customlib(ts, string_data, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Failed to read rect\n", + __func__); + snprintf(buf, SEC_CMD_BUF_SIZE, "NG, Failed to read rect"); + goto out; + } + + current_index = (string_data[1] & 0xFF) << 8 | (string_data[0] & 0xFF); + if (current_index > 1000 || current_index < 500) { + input_err(true, &ts->client->dev, + "Failed to Custom Library LP log %d\n", current_index); + snprintf(buf, SEC_CMD_BUF_SIZE, + "NG, Failed to Custom Library LP log, current_index=%d", + current_index); + goto out; + } + + input_info(true, &ts->client->dev, + "%s: DEBUG current_index = %d\n", __func__, current_index); + + /* Custom Library has 62 stacks for LP dump */ + for (i = 61; i >= 0; i--) { + u16 data0, data1, data2, data3; + char buff[30] = {0, }; + u16 string_addr; + + string_addr = current_index - (8 * i); + if (string_addr < 500) + string_addr += SEC_TS_CMD_CUSTOMLIB_LP_DUMP; + string_data[0] = string_addr & 0xFF; + string_data[1] = (string_addr & 0xFF00) >> 8; + + ret = ts->sec_ts_read_customlib(ts, string_data, 8); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: Failed to read rect\n", __func__); + snprintf(buf, SEC_CMD_BUF_SIZE, + "NG, Failed to read rect, addr=%d", + string_addr); + goto out; + } + + data0 = (string_data[1] & 0xFF) << 8 | (string_data[0] & 0xFF); + data1 = (string_data[3] & 0xFF) << 8 | (string_data[2] & 0xFF); + data2 = (string_data[5] & 0xFF) << 8 | (string_data[4] & 0xFF); + data3 = (string_data[7] & 0xFF) << 8 | (string_data[6] & 0xFF); + if (data0 || data1 || data2 || data3) { + snprintf(buff, sizeof(buff), + "%d: %04x%04x%04x%04x\n", + string_addr, data0, data1, data2, data3); + strncat(buf, buff, SEC_CMD_BUF_SIZE); + } + } + +out: + enable_irq(ts->client->irq); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return strlen(buf); +} + +static ssize_t force_recal_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); + u8 rbuf[4] = {0, }; + u32 recal_count; + int ret; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, + "%s: Touch is stopped!\n", __func__); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", -ENODEV); + } + + ret = ts->sec_ts_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__); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + 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); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", recal_count); +} + + +/* sysfs file node to store heatmap mode + * "echo cmd > heatmap_mode" to change + * Possible commands: + * 0 = HEATMAP_OFF + * 1 = HEATMAP_PARTIAL + * 2 = HEATMAP_FULL + */ +static ssize_t heatmap_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_plat_data *pdata = ts->plat_data; + int result; + int val; + u8 config; + + result = kstrtoint(buf, 10, &val); + if (result < 0 || val < HEATMAP_OFF || val > HEATMAP_FULL) { + input_err(true, &ts->client->dev, + "%s: Invalid input.\n", __func__); + return -EINVAL; + } + + pdata->heatmap_mode = val; + + /* reset all heatmap settings when any change */ + config = 0; + result = ts->sec_ts_write(ts, + SEC_TS_CMD_HEATMAP_ENABLE, &config, 1); + if (result < 0) + input_err(true, &ts->client->dev, + "%s: write reg %#x failed, returned %i\n", + __func__, SEC_TS_CMD_HEATMAP_ENABLE, result); + config = TYPE_INVALID_DATA; + result = ts->sec_ts_write(ts, + SEC_TS_CMD_MUTU_RAW_TYPE, &config, 1); + if (result < 0) + input_err(true, &ts->client->dev, + "%s: write reg %#x failed, returned %i\n", + __func__, SEC_TS_CMD_MUTU_RAW_TYPE, result); + + return count; +#else + return 0; +#endif +} + +static ssize_t heatmap_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + const struct sec_ts_plat_data *pdata = ts->plat_data; + + return scnprintf(buf, PAGE_SIZE, "%d\n", + pdata->heatmap_mode); +#else + return scnprintf(buf, PAGE_SIZE, "N/A\n"); +#endif +} + +static ssize_t fw_version_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 ret, written = 0; + u8 data[3]; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + /* If there is no FW file avaiable, + * sec_ts_save_version_of_ic() and sec_ts_save_version_of_bin() will + * no be called. Need to get through SEC_TS_READ_IMG_VERSION cmd. + */ + if (ts->plat_data->panel_revision == 0 && + ts->plat_data->img_version_of_bin[2] == 0 && + ts->plat_data->img_version_of_bin[3] == 0) { + u8 fw_ver[4]; + + ret = ts->sec_ts_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__); + goto out; + } + written += scnprintf(buf + written, PAGE_SIZE - written, + "SE-V%02X.%02X.%02X\n", + ts->plat_data->panel_revision, + fw_ver[2], + fw_ver[3]); + written += scnprintf(buf + written, PAGE_SIZE - written, + "FW file: N/A\n"); + } else { + written += scnprintf(buf + written, PAGE_SIZE - written, + "SE-V%02X.%02X.%02X\n", + ts->plat_data->panel_revision, + ts->plat_data->img_version_of_ic[2], + ts->plat_data->img_version_of_ic[3]); + written += scnprintf(buf + written, PAGE_SIZE - written, + "FW file: %s\n", + ts->plat_data->firmware_name); + } + + written += scnprintf(buf + written, PAGE_SIZE - written, + "Cal: %02X %02X %02X %02X %02X %02X %02X %02X\n", + ts->cali_report[0], ts->cali_report[1], ts->cali_report[2], + ts->cali_report[3], ts->cali_report[4], ts->cali_report[5], + ts->cali_report[6], ts->cali_report[7]); + + ret = ts->sec_ts_read(ts, SEC_TS_READ_ID, data, sizeof(data)); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read device id(%d)\n", + __func__, ret); + goto out; + } + written += scnprintf(buf + written, PAGE_SIZE - written, + "ID: %02X %02X %02X\n", + data[0], data[1], data[2]); +out: + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + + return written; +} + +static ssize_t 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); + int written = 0; + unsigned char data[4] = { 0 }; + int ret; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + data[0] = 0; + ret = ts->sec_ts_read(ts, SEC_TS_READ_BOOT_STATUS, data, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read boot status(%d)\n", + __func__, ret); + goto out; + } + written += scnprintf(buf + written, PAGE_SIZE - written, + "BOOT STATUS: 0x%02X\n", data[0]); + + memset(data, 0x0, 4); + ret = ts->sec_ts_read(ts, SEC_TS_READ_TS_STATUS, data, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to touch status(%d)\n", + __func__, ret); + goto out; + } + written += scnprintf(buf + written, PAGE_SIZE - written, + "TOUCH STATUS: 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", + data[0], data[1], data[2], data[3]); + + memset(data, 0x0, 2); + ret = ts->sec_ts_read(ts, SEC_TS_CMD_SET_TOUCHFUNCTION, data, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read touch functions(%d)\n", + __func__, ret); + goto out; + } + written += scnprintf(buf + written, PAGE_SIZE - written, + "Functions: 0x%02X, 0x%02X\n", data[0], data[1]); + +out: + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return written; +} + +static DEVICE_ATTR_RO(ito_check); +static DEVICE_ATTR_RO(raw_check); +static DEVICE_ATTR_RW(multi_count); +static DEVICE_ATTR_RW(wet_mode); +static DEVICE_ATTR_RW(comm_err_count); +static DEVICE_ATTR_RW(checksum); +static DEVICE_ATTR_RW(holding_time); +static DEVICE_ATTR_RW(all_touch_count); +static DEVICE_ATTR_RW(z_value); +static DEVICE_ATTR_RO(module_id); +static DEVICE_ATTR_RO(vendor); +static DEVICE_ATTR_RW(pressure_enable); +static DEVICE_ATTR_RO(get_lp_dump); +static DEVICE_ATTR_RO(force_recal_count); +static DEVICE_ATTR_RW(heatmap_mode); +static DEVICE_ATTR_RO(fw_version); +static DEVICE_ATTR_RO(status); + + +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_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_pressure_enable.attr, + &dev_attr_get_lp_dump.attr, + &dev_attr_force_recal_count.attr, + &dev_attr_heatmap_mode.attr, + &dev_attr_fw_version.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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + 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); + } + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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: mode %d state %d\n", + __func__, mode, state); + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_STATEMANAGE_ON, onoff, 1); + sec_ts_delay(20); + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_CHG_SYSMODE, tBuff, + sizeof(tBuff)); + 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_write(ts, SEC_TS_CMD_STATEMANAGE_ON, onoff, 1); + sec_ts_delay(20); + + return ret; +} + +/* sec_ts_cm_spec_over_check : apply gap calculation with ts->pFrame data + * gap = abs(N1 - N2) / MAX(N1, N2) * 100 (%) + */ +static int sec_ts_cm_spec_over_check(struct sec_ts_data *ts, short *gap, + bool gap_dir) +{ + int i = 0; + int j = 0; + int gapx, gapy, pos1, pos2; + short dpos1, dpos2; + int specover_count = 0; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + /* Get x-direction cm gap */ + if (!gap_dir) { + input_info(true, &ts->client->dev, "gapX TX\n"); + + for (i = 0; i < ts->rx_count; i++) { + for (j = 0; j < ts->tx_count - 1; j++) { + /* Exclude last line to get gap between two + * lines. + */ + pos1 = (i * ts->tx_count) + j; + pos2 = (i * ts->tx_count) + (j + 1); + + dpos1 = ts->pFrame[pos1]; + dpos2 = ts->pFrame[pos2]; + + if (dpos1 > dpos2) + gapx = 100 - (dpos2 * 100 / dpos1); + else + gapx = 100 - (dpos1 * 100 / dpos2); + + gap[pos1] = gapx; + + if (gapx > cm_gap[i][j]) + specover_count++; + } + } + } + + /* get y-direction cm gap */ + else { + input_info(true, &ts->client->dev, "gapY RX\n"); + + for (i = 0; i < ts->rx_count - 1; i++) { + for (j = 0; j < ts->tx_count; j++) { + pos1 = (i * ts->tx_count) + j; + pos2 = ((i + 1) * ts->tx_count) + j; + + dpos1 = ts->pFrame[pos1]; + dpos2 = ts->pFrame[pos2]; + + if (dpos1 > dpos2) + gapy = 100 - (dpos2 * 100 / dpos1); + else + gapy = 100 - (dpos1 * 100 / dpos2); + + gap[pos1] = gapy; + + if (gapy > cm_gap[i][j]) + specover_count++; + } + } + } + + input_info(true, &ts->client->dev, "%s: Gap NG for %d node(s)\n", + gap_dir == 0 ? "gapX" : "gapY", specover_count); + + return specover_count; +} + +static int sec_ts_cs_spec_over_check(struct sec_ts_data *ts, short *gap) +{ + int i; + int specover_count = 0; + short dTmp; + + for (i = 0; i < ts->tx_count - 1; i++) { + dTmp = ts->pFrame[i] - ts->pFrame[i + 1]; + if (dTmp < 0) + dTmp *= -1; + + gap[i] = dTmp; + + if (dTmp > cs_tx_gap) + specover_count++; + } + + for (i = ts->tx_count; i < ts->tx_count + ts->rx_count - 1; i++) { + dTmp = ts->pFrame[i] - ts->pFrame[i + 1]; + if (dTmp < 0) + dTmp *= -1; + + gap[i] = dTmp; + + if (dTmp > cs_rx_gap) + specover_count++; + } + + input_info(true, &ts->client->dev, "%s: Gap NG for %d node(s)\n", + __func__, specover_count); + + return specover_count; +} + +static int sec_ts_get_gain_table(struct sec_ts_data *ts) +{ + int i, j; + int temp; + int tmp_dv; + unsigned int str_size, str_len = 0; + unsigned char *pStr = NULL; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + for (i = 0; i < ts->rx_count; i++) { + for (j = 0; j < ts->tx_count; j++) { + tmp_dv = ts->pFrame[i * ts->tx_count + j]; + + /* skip notch area */ + if (cm_region[i][j] == REGION_NOTCH) { + ts->gainTable[j * ts->rx_count + i] = 0; + continue; + } + + if (tmp_dv <= 0) { + input_info(true, &ts->client->dev, + "%s: node[%d,%d] == 0\n", __func__, i, + j); + tmp_dv = 1; + } + + temp = (fs_target[i][j] * 1000) / (tmp_dv) * 64; + /* Add 500 to round the result */ + temp = (temp + 500) / 1000; + if (temp > 255) + temp = 255; + ts->gainTable[j * ts->rx_count + i] = (temp & 0xFF); + } + } + + str_size = 6 * (ts->tx_count + 1); + pStr = kzalloc(str_size, GFP_KERNEL); + if (pStr == NULL) + return -ENOMEM; + + input_info(true, &ts->client->dev, "%s: Gain Table\n", __func__); + + for (i = 0; i < ts->rx_count; i++) { + pStr[0] = 0; + str_len = 0; + for (j = 0; j < ts->tx_count; j++) { + str_len += scnprintf(pStr + str_len, str_size - str_len, + " %3d", + ts->gainTable[(j * ts->rx_count) + i]); + } + input_info(true, &ts->client->dev, "%s\n", pStr); + } + + kfree(pStr); + + return 0; +} + +static int sec_ts_write_gain_table(struct sec_ts_data *ts) +{ + int node_cnt = ts->tx_count * ts->rx_count; + u8 *gainTable = NULL; + u8 *tCmd = NULL; + int copy_max, copy_left, copy_size, copy_cur; + int ret = -1; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + /* Write norm table to ic + * divide data into 256 bytes: + * buffer size limit 256 bytes + */ + gainTable = ts->gainTable; + + copy_max = ts->io_burstmax - 3; + copy_left = node_cnt; + copy_size = 0; + copy_cur = (copy_left > copy_max) ? copy_max : copy_left; + + tCmd = kzalloc(copy_cur + 3, GFP_KERNEL); + if (!tCmd) + goto ErrorAlloc; + + while (copy_left > 0) { + tCmd[0] = SEC_TS_CMD_WRITE_NORM_TABLE; + tCmd[1] = (copy_size >> 8) & 0xFF; + tCmd[2] = (copy_size >> 0) & 0xFF; + + memcpy(&tCmd[3], &gainTable[copy_size], copy_cur); + + input_info(true, &ts->client->dev, + "%s: left = %d, cur = %d, size = %d\n", + __func__, copy_left, copy_cur, copy_size); + + ret = ts->sec_ts_write_burst_heap(ts, tCmd, 3 + copy_cur); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: table write failed\n", __func__); + + copy_size += copy_cur; + copy_left -= copy_cur; + copy_cur = (copy_left > copy_max) ? copy_max : copy_left; + } + +ErrorAlloc: + kfree(tCmd); + + return ret; +} + +/* sec_ts_get_postcal_mean : get mean value for all nodes */ +static int sec_ts_get_postcal_mean(struct sec_ts_data *ts) +{ + int i, j; + int sum = 0; + int nCnt = 0; + + for (i = 0; i < ts->rx_count; i++) { + for (j = 0; j < ts->tx_count; j++) { + if (cm_region[i][j] == REGION_NOTCH) { + /* Count notch nodes, where fs target is 0 */ + nCnt++; + continue; + } + sum += ts->pFrame[(i * ts->tx_count) + j]; + } + } + + /* exclude notch area from average */ + sum = sum / (ts->tx_count * ts->rx_count - nCnt); + + return sum; +} + +static int sec_ts_get_postcal_uniformity(struct sec_ts_data *ts, short *diff) +{ + int pos1, pos2; + short dpos1, dpos2, gap; + int i = 0; + int j = 0; + int specover_cnt = 0; + + for (i = 0; i < ts->rx_count; i++) { + for (j = 0; j < ts->tx_count - 1; j++) { + /* At the notch boundary, skip (leave gap as 0) + * if node[row][col] or node[row][col+1] is 0, + * it is notch boundary for column direction + */ + if ((cm_region[i][j] == REGION_NOTCH) || + (cm_region[i][j + 1] == REGION_NOTCH)) + continue; + + pos1 = (i * ts->tx_count) + j; + pos2 = (i * ts->tx_count) + (j + 1); + + dpos1 = ts->pFrame[pos1]; + dpos2 = ts->pFrame[pos2]; + + gap = (dpos1 > dpos2) ? (dpos1 - dpos2) : + (dpos2 - dpos1); + + diff[pos1] = gap; + } + } + + for (i = 0; i < ts->rx_count - 1; i++) { + for (j = 0; j < ts->tx_count; j++) { + /* At the notch boundary, skip (leave gap as 0) + * if node[row][col] or node[row+1][col] is 0, + * it is notch boundary for row direction + */ + if ((cm_region[i][j] == REGION_NOTCH) || + (cm_region[i + 1][j] == REGION_NOTCH)) + continue; + + pos1 = (i * ts->tx_count) + j; + pos2 = ((i + 1) * ts->tx_count) + j; + + dpos1 = ts->pFrame[pos1]; + dpos2 = ts->pFrame[pos2]; + + gap = (dpos1 > dpos2) ? (dpos1 - dpos2) : + (dpos2 - dpos1); + + /* find max gap between x and y direction */ + if (diff[pos1] < gap) + diff[pos1] = gap; + } + } + + for (i = 0; i < ts->rx_count * ts->tx_count; i++) { + /* since spec is in % unit, multiply 100 */ + diff[i] *= 100; + diff[i] /= (ts->fs_postcal_mean); + if (diff[i] > fs_postcal_uniform_spec) + specover_cnt++; + } + + return specover_cnt; +} + +static void sec_ts_print_frame(struct sec_ts_data *ts, short *min, short *max) +{ + int i = 0; + int j = 0; + const unsigned int buff_size = 6 * (ts->tx_count + 1); + unsigned int buff_len = 0; + unsigned char *pStr = NULL; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + pStr = kzalloc(buff_size, GFP_KERNEL); + if (pStr == NULL) + return; + + buff_len += scnprintf(pStr + buff_len, buff_size - buff_len, + " TX"); + + for (i = 0; i < ts->tx_count; i++) + buff_len += scnprintf(pStr + buff_len, buff_size - buff_len, + " %02d ", i); + + input_info(true, &ts->client->dev, "%s\n", pStr); + buff_len = 0; + memset(pStr, 0x0, buff_size); + buff_len += scnprintf(pStr + buff_len, buff_size - buff_len, " +"); + + for (i = 0; i < ts->tx_count; i++) + buff_len += scnprintf(pStr + buff_len, buff_size - buff_len, + "----"); + + input_info(true, &ts->client->dev, "%s\n", pStr); + + for (i = 0; i < ts->rx_count; i++) { + buff_len = 0; + memset(pStr, 0x0, buff_size); + buff_len += scnprintf(pStr + buff_len, buff_size - buff_len, + "Rx%02d | ", i); + + for (j = 0; j < ts->tx_count; j++) { + buff_len += scnprintf(pStr + buff_len, + buff_size - buff_len, + " %3d", ts->pFrame[(j * ts->rx_count) + i]); + + if (i > 0) { + 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]; + } + } + input_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, enum spec_check_type *spec_check) +{ + unsigned int readbytes = 0xFF; + unsigned char *pRead = NULL; + u8 mode = TYPE_INVALID_DATA; + int ret = 0; + int i = 0; + int j = 0; + short dTmp = 0; + short *temp = NULL; + u8 w_type; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + /* 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 */ + if (type == TYPE_OFFSET_DATA_SDC_CM2) + w_type = TYPE_OFFSET_DATA_SDC; + else if (type == TYPE_OFFSET_DATA_SDC_NOT_SAVE) + w_type = TYPE_OFFSET_DATA_SDC; + else + w_type = type; + + /* Set raw type to TYPE_INVALID_DATA if change before */ + ret = ts->sec_ts_read(ts, + SEC_TS_CMD_MUTU_RAW_TYPE, &ts->frame_type, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: read rawdata type failed\n", + __func__); + goto ErrorExit; + } + + if (ts->frame_type != TYPE_INVALID_DATA) { + ret = ts->sec_ts_write(ts, SEC_TS_CMD_MUTU_RAW_TYPE, &mode, 1); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: recover rawdata type failed\n", __func__); + } + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_MUTU_RAW_TYPE, &w_type, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: Set rawdata type failed\n", __func__); + goto ErrorExit; + } + ts->frame_type = w_type; + + sec_ts_delay(50); + + if (type == TYPE_OFFSET_DATA_SDC || type == TYPE_OFFSET_DATA_SDC_CM2 + || type == TYPE_OFFSET_DATA_SDC_NOT_SAVE) { + /* 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); + + if (type == TYPE_OFFSET_DATA_SDC) + execute_selftest(ts, + TEST_OPEN | TEST_NODE_VARIANCE); + else if (type == + TYPE_OFFSET_DATA_SDC_CM2) + execute_selftest(ts, + TEST_OPEN | TEST_NOT_SAVE | TEST_HIGH_FREQ); + else if (type == + TYPE_OFFSET_DATA_SDC_NOT_SAVE) + execute_selftest(ts, + TEST_OPEN | TEST_NODE_VARIANCE | TEST_NOT_SAVE); + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_POWER_MODE, ¶, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: Set powermode failed\n", __func__); + enable_irq(ts->client->irq); + goto ErrorRelease; + } + + /* read data and check ret later */ + ret = ts->sec_ts_read_heap(ts, SEC_TS_READ_TOUCH_RAWDATA, pRead, + readbytes); + enable_irq(ts->client->irq); + } else + /* read data and check ret later */ + ret = ts->sec_ts_read_heap(ts, SEC_TS_READ_TOUCH_RAWDATA, pRead, + readbytes); + + /* check read data */ + 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); + +#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]; + } + + /* spec check */ + if (*spec_check == SPEC_CHECK) { + int specover_count = 0; + + if (type == TYPE_OFFSET_DATA_SDC) { + unsigned int region = 0; + /* set initial value for min, max */ + for (i = 0; i < REGION_TYPE_COUNT; i++) { + min[i] = SHRT_MAX; + max[i] = SHRT_MIN; + } + + for (i = 0; i < ts->rx_count; i++) { + for (j = 0; j < ts->tx_count; j++) { + dTmp = ts->pFrame[i * ts->tx_count + j]; + region = cm_region[i][j]; + + if (region == REGION_NOTCH) + continue; + + min[region] = min(min[region], dTmp); + max[region] = max(max[region], dTmp); + + if (dTmp > cm_max[region]) + specover_count++; + if (dTmp < cm_min[region]) + specover_count++; + } + } + input_info(true, &ts->client->dev, + "%s: type = %d, specover = %d\n", + __func__, type, specover_count); + + if (specover_count == 0 && + (max[REGION_NORMAL] - min[REGION_NORMAL] < + cm_mm[REGION_NORMAL]) && + (max[REGION_EDGE] - min[REGION_EDGE] < + cm_mm[REGION_EDGE]) && + (max[REGION_CORNER] - min[REGION_CORNER] < + cm_mm[REGION_CORNER])) + *spec_check = SPEC_PASS; + else + *spec_check = SPEC_FAIL; + } else if (type == TYPE_NOI_P2P_MIN) { + for (i = 0; i < ts->rx_count; i++) { + for (j = 0; j < ts->tx_count; j++) { + dTmp = ts->pFrame[i * ts->tx_count + j]; + if (cm_region[i][j] != REGION_NOTCH && + dTmp < noi_min[i][j]) + specover_count++; + } + } + input_info(true, &ts->client->dev, + "%s: type = %d, specover = %d\n", + __func__, type, specover_count); + + if (specover_count == 0) + *spec_check = SPEC_PASS; + else + *spec_check = SPEC_FAIL; + } else if (type == TYPE_NOI_P2P_MAX) { + for (i = 0; i < ts->rx_count; i++) { + for (j = 0; j < ts->tx_count; j++) { + dTmp = ts->pFrame[i * ts->tx_count + j]; + if (cm_region[i][j] != REGION_NOTCH && + dTmp > noi_max[i][j]) + specover_count++; + } + } + input_info(true, &ts->client->dev, + "%s: type = %d, specover = %d\n", + __func__, type, specover_count); + + if (specover_count == 0) + *spec_check = SPEC_PASS; + else + *spec_check = SPEC_FAIL; + } + } + + kfree(temp); + +ErrorRelease: + /* release data monitory (unprepare AFE data memory) */ + ret = ts->sec_ts_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__); + else + ts->frame_type = mode; + +ErrorExit: + kfree(pRead); + + return ret; +} + +static void sec_ts_print_channel(struct sec_ts_data *ts) +{ + unsigned char *pStr = NULL; + unsigned int str_size, str_len = 0; + int i = 0, j = 0, k = 0; + + if (!ts->tx_count) + return; + + str_size = 7 * (ts->tx_count + 1); + pStr = vzalloc(str_size); + if (!pStr) + return; + + str_len = scnprintf(pStr, str_size, " TX"); + + for (k = 0; k < ts->tx_count; k++) { + str_len += scnprintf(pStr + str_len, str_size - str_len, + " %02d", k); + } + input_info(true, &ts->client->dev, "%s\n", pStr); + + str_len = scnprintf(pStr, str_size, " +"); + + for (k = 0; k < ts->tx_count; k++) { + str_len += scnprintf(pStr + str_len, str_size - str_len, + "------"); + } + input_info(true, &ts->client->dev, "%s\n", pStr); + + str_len = scnprintf(pStr, str_size, " | "); + + for (i = 0; i < (ts->tx_count + ts->rx_count) * 2; i += 2) { + if (j == ts->tx_count) { + input_info(true, &ts->client->dev, "%s\n", pStr); + input_info(true, &ts->client->dev, "\n"); + str_len = scnprintf(pStr, str_size, " RX"); + + for (k = 0; k < ts->tx_count; k++) { + str_len += scnprintf(pStr + str_len, + str_size - str_len, + " %02d", k); + } + + input_info(true, &ts->client->dev, "%s\n", pStr); + + str_len = scnprintf(pStr, str_size, " +"); + + for (k = 0; k < ts->tx_count; k++) { + str_len += scnprintf(pStr + str_len, + str_size - str_len, + "------"); + } + input_info(true, &ts->client->dev, "%s\n", pStr); + + str_len = scnprintf(pStr, str_size, " | "); + } else if (j && !(j % ts->tx_count)) { + input_info(true, &ts->client->dev, "%s\n", pStr); + str_len = scnprintf(pStr, str_size, " | "); + } + + str_len += scnprintf(pStr + str_len, str_size - str_len, " %5d", + ts->pFrame[j]); + + j++; + } + input_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, enum spec_check_type *spec_check) +{ + 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_info(true, &ts->client->dev, "%s\n", __func__); + + pRead = kzalloc(data_length, GFP_KERNEL); + if (!pRead) + return -ENOMEM; + + /* set OPCODE and data type */ + if (type == TYPE_OFFSET_DATA_SDC_NOT_SAVE) + w_data = TYPE_OFFSET_DATA_SDC; + else + w_data = type; + ret = ts->sec_ts_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 || + type == TYPE_OFFSET_DATA_SDC_NOT_SAVE) { + /* 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); + if (type == TYPE_OFFSET_DATA_SDC) + execute_selftest(ts, TEST_SELF_NODE); + else if (type == TYPE_OFFSET_DATA_SDC_NOT_SAVE) + execute_selftest(ts, TEST_SELF_NODE | TEST_NOT_SAVE); + ret = ts->sec_ts_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; + } + + /* read data and check ret later */ + ret = ts->sec_ts_read_heap(ts, SEC_TS_READ_TOUCH_SELF_RAWDATA, + pRead, data_length); + enable_irq(ts->client->irq); + /* end */ + } else + /* read data and check ret later */ + ret = ts->sec_ts_read_heap(ts, SEC_TS_READ_TOUCH_SELF_RAWDATA, + pRead, data_length); + + /* check read data */ + 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); + +/* d[00] ~ d[14] : TX channel + * d[15] ~ d[51] : none + * d[52] ~ d[77] : RX channel + * d[78] ~ d[103] : none + */ + for (ii = 0; ii < data_length; ii += 2) { + ts->pFrame[jj] = ((pRead[ii] << 8) | pRead[ii + 1]); + jj++; + } + + sec_ts_print_channel(ts); + + if (*spec_check == SPEC_CHECK) { + int specover_count = 0; + + if (type == TYPE_OFFSET_DATA_SDC) { + min[0] = min[1] = SHRT_MAX; + max[0] = max[1] = SHRT_MIN; + + for (ii = 0; ii < ts->tx_count; ii++) { + if (ts->pFrame[ii] > cs_tx_max) + specover_count++; + if (ts->pFrame[ii] < cs_tx_min) + specover_count++; + + min[0] = min(min[0], ts->pFrame[ii]); + max[0] = max(max[0], ts->pFrame[ii]); + } + for (ii = ts->tx_count; + ii < ts->tx_count + ts->rx_count; ii++) { + if (ts->pFrame[ii] > cs_rx_max) + specover_count++; + if (ts->pFrame[ii] < cs_rx_min) + specover_count++; + + min[1] = min(min[1], ts->pFrame[ii]); + max[1] = max(max[1], ts->pFrame[ii]); + } + } + + input_info(true, &ts->client->dev, + "%s: type : %d, specover = %d\n", + __func__, type, specover_count); + + if (specover_count == 0 && + (max[0] - min[0]) < cs_tx_mm && + (max[1] - min[1]) < cs_rx_mm) + *spec_check = SPEC_PASS; + else + *spec_check = SPEC_FAIL; + } + +err_read_data: + /* release data monitory (unprepare AFE data memory) */ + ret = ts->sec_ts_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 int sec_ts_read_gain_table(struct sec_ts_data *ts) +{ + int readbytes = ts->tx_count * ts->rx_count; + unsigned char *pRead = NULL; + short min = 0; + short max = 0; + int ret; + int i; + + /* readbytes : 1 byte for enable/disable info + 1 byte per node */ + pRead = kzalloc(1 + readbytes, GFP_KERNEL); + if (!pRead) + return -ENOMEM; + + ret = ts->sec_ts_read_heap(ts, SEC_TS_CMD_READ_NORM_TABLE, pRead, + 1 + readbytes); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read rawdata failed!\n", + __func__); + goto ErrorRead; + } + + input_info(true, &ts->client->dev, "%s: gain table is %s\n", + __func__, pRead[0] ? "On." : "Off."); + + for (i = 0; i < readbytes; i++) + ts->pFrame[i] = (short)pRead[i + 1]; + + sec_ts_print_frame(ts, &min, &max); + +ErrorRead: + kfree(pRead); + + return ret; +} + +int sec_ts_read_raw_data(struct sec_ts_data *ts, + struct sec_cmd_data *sec, struct sec_ts_test_mode *mode) +{ + int ii, jj; + int ret = 0; + const unsigned int buff_size = ts->tx_count * ts->rx_count * + CMD_RESULT_WORD_LEN; + unsigned int buff_len = 0; + char *buff; + + buff = kzalloc(buff_size, 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_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, &mode->spec_check); + else + ret = sec_ts_read_frame(ts, mode->type, mode->min, + mode->max, &mode->spec_check); + 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) { + if (mode->spec_check == SPEC_PASS) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "OK %d %d", + ts->rx_count, ts->tx_count); + } else if (mode->spec_check == SPEC_FAIL) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "NG %d %d", + ts->rx_count, ts->tx_count); + } + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, "\n "); + if (!ts->print_format) { + for (ii = 0; + ii < (ts->rx_count + ts->tx_count); + ii++) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "%3d,", ts->pFrame[ii]); + if (ii >= ts->tx_count - 1) + buff_len += scnprintf( + buff + buff_len, + buff_size - buff_len, + "\n"); + } + } else { + for (ii = ts->tx_count; + ii < (ts->rx_count + ts->tx_count); + ii++) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "%3d,", ts->pFrame[ii]); + } + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, "\n"); + for (ii = 0; ii < ts->tx_count; ii++) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "%3d,\n", ts->pFrame[ii]); + } + } + } else { + if (mode->spec_check == SPEC_NO_CHECK) + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, "\n"); + else if (mode->spec_check == SPEC_PASS) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "OK %d %d\n", + ts->rx_count, ts->tx_count); + } else { /* mode->spec_check == SPEC_FAIL) */ + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "NG %d %d\n", ts->rx_count, + ts->tx_count); + } + if (!ts->print_format) { + for (ii = 0; + ii < (ts->rx_count * ts->tx_count); ii++) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "%3d,", ts->pFrame[ii]); + if (ii % ts->tx_count == (ts->tx_count - 1)) + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "\n"); + } + } else { + for (ii = 0; ii < ts->tx_count; ii++) { + for (jj = 0; jj < ts->rx_count; jj++) { + buff_len += scnprintf( + buff + buff_len, + buff_size - buff_len, + "%3d,", + ts->pFrame[(jj * + ts->tx_count) + ii]); + } + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "\n"); + } + } + } + } else { + buff_len += scnprintf(buff + buff_len, buff_size - buff_len, + "%3d,%3d", mode->min[0], mode->max[0]); + } + + 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, buff_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; + + sec_cmd_set_cmd_result(sec, "FAIL", 4); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + + sec_ts_locked_release_all_finger(ts); + + return ret; +} + +int sec_ts_check_fs_precal(struct sec_ts_data *ts) +{ + int i, j; + int fail_count = 0; + short temp; + + for (i = 0; i < ts->rx_count; i++) { + for (j = 0; j < ts->tx_count; j++) { + temp = ts->pFrame[i * ts->tx_count + j]; + if (cm_region[i][j] == REGION_NOTCH) + continue; + /* check whether fs_precal data is within range */ + if ((temp > fs_precal_h[i][j]) || + (temp < fs_precal_l[i][j])) + fail_count++; + } + } + + return fail_count; +} + +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-V%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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + ret = ts->sec_ts_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; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + snprintf(buff, sizeof(buff), "SE-V%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); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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); +} + +#ifdef PAT_CONTROL +static void get_pat_information(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); + + /* fixed tune version will be saved at excute autotune */ + snprintf(buff, sizeof(buff), "P%02XT%04X", + ts->cal_count, ts->tune_fix_ver); + + 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_external_factory(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); + + ts->external_factory = true; + snprintf(buff, sizeof(buff), "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); +} +#endif + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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_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_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); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; +err: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + ret = sec_ts_start_device(ts); + +/* TODO: check this for SPI case + * 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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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 + 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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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_write(ts, SEC_TS_CMD_MIS_CAL_SPEC, + wreg, 3); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: nvm 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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + 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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + + +} + +/* + * ## Mis Cal result ## + * FF : initial value in Firmware. + * FD : Cal fail case + * F4 : fail case (5F) + * F3 : 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; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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_read(ts, SEC_TS_CMD_MIS_CAL_READ, + &mis_cal_data, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: 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_read(ts, SEC_TS_CMD_MIS_CAL_SPEC, wreg, 3); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: 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\n", __func__, + wreg[0], wreg[1], wreg[2]); + } + } + + snprintf(buff, sizeof(buff), "%d,%d,%d,%d", + mis_cal_data, wreg[0], wreg[1], wreg[2]); + + 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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +NG: + snprintf(buff, sizeof(buff), "%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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + ret = ts->sec_ts_read(ts, SEC_TS_CMD_WET_MODE, &wet_mode_info, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: 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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + 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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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 void get_x_cross_routing(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), "NG"); + 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_cross_routing(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; + + sec_cmd_set_default_result(sec); + + ret = strncmp(ts->plat_data->model_name, "G935", 4) + && strncmp(ts->plat_data->model_name, "N930", 4); + if (ret == 0) + snprintf(buff, sizeof(buff), "13,14"); + else + snprintf(buff, sizeof(buff), "NG"); + 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_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 }; + u8 cal_result; + u8 nv_result; + u8 temp; + u8 csum = 0; + int ret, i; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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; + } + + temp = DO_FW_CHECKSUM | DO_PARA_CHECKSUM; + ret = ts->sec_ts_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); + +#ifdef I2C_INTERFACE + ret = ts->sec_ts_read_bulk(ts, csum_result, 4); +#else + ret = ts->sec_ts_read(ts, SEC_TS_CMD_GET_CHECKSUM, csum_result, 4); +#endif + 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; + + 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; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +err: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SEC; + + sec_ts_read_raw_data(ts, sec, &mode); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SEC; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, sec, &mode); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + node = sec_ts_check_index(ts); + if (node < 0) { + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + 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); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SDC; + mode.spec_check = SPEC_CHECK; + + sec_ts_read_raw_data(ts, sec, &mode); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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; + mode.spec_check = SPEC_CHECK; + + sec_ts_read_raw_data(ts, sec, &mode); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + node = sec_ts_check_index(ts); + if (node < 0) { + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + 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); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void run_rawcap_gap_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; + int ret_x, ret_y; + int i; + short *gap_x, *gap_y; + short dTmp; + char *buff; + char temp[SEC_CMD_STR_LEN] = { 0 }; + const unsigned int buff_size = ts->tx_count * ts->rx_count * 2 + * CMD_RESULT_WORD_LEN + 4 * CMD_RESULT_WORD_LEN; + const unsigned int readbytes = ts->tx_count * ts->rx_count * 2; + const unsigned int X_DIR = 0; + const unsigned int Y_DIR = 1; + + if (!sec) + return; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + + gap_x = kzalloc(readbytes, GFP_KERNEL); + gap_y = kzalloc(readbytes, GFP_KERNEL); + buff = kzalloc(buff_size, GFP_KERNEL); + if (!gap_x || !gap_y || !buff) { + snprintf(temp, sizeof(temp), "FAIL"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, temp, sizeof(temp)); + goto ErrorAlloc; + } + + mode.type = TYPE_OFFSET_DATA_SDC; + mode.allnode = TEST_MODE_ALL_NODE; + mode.spec_check = SPEC_NO_CHECK; + + sec_ts_read_frame(ts, mode.type, mode.min, mode.max, + &mode.spec_check); + + ret_x = sec_ts_cm_spec_over_check(ts, gap_x, X_DIR); + + ret_y = sec_ts_cm_spec_over_check(ts, gap_y, Y_DIR); + + if (0 == (ret_x + ret_y)) { + strlcat(buff, "OK", buff_size); + sec->cmd_state = SEC_CMD_STATUS_OK; + } else { + strlcat(buff, "NG", buff_size); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } + strlcat(buff, "\n", buff_size); + for (i = 0; i < (ts->tx_count * ts->rx_count); i++) { + dTmp = (gap_x[i] > gap_y[i]) ? gap_x[i] : gap_y[i]; + snprintf(temp, sizeof(temp), "%3d,", dTmp); + strlcat(buff, temp, buff_size); + if (i % ts->tx_count == (ts->tx_count - 1)) + strlcat(buff, "\n", buff_size); + memset(temp, 0x00, sizeof(temp)); + } + strlcat(buff, "\n", buff_size); + + sec_cmd_set_cmd_result(sec, buff, buff_size); + +ErrorAlloc: + kfree(buff); + kfree(gap_y); + kfree(gap_x); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void run_rawcap_high_freq_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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SDC_CM2; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, sec, &mode); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_SIGNAL_DATA; + + sec_ts_read_raw_data(ts, sec, &mode); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_SIGNAL_DATA; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, sec, &mode); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + node = sec_ts_check_index(ts); + if (node < 0) { + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + 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); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static int sec_ts_read_frame_stdev(struct sec_ts_data *ts, + struct sec_cmd_data *sec, u8 type, short *min, short *max, + enum spec_check_type *spec_check, bool get_average_only) +{ + unsigned char *pRead = NULL; + short *pFrameAll = NULL; + int *pFrameAvg = NULL; + u64 *pFrameStd = NULL; + u8 inval_type = TYPE_INVALID_DATA; + int node_tot = 0; + int ret = 0; + int i = 0; + int j = 0; + int frame_len_byte = 0; + int frame_cnt = 0; + int frame_tot = 0; + int tmp = 0; + + const unsigned int buff_size = ts->tx_count * ts->rx_count * + CMD_RESULT_WORD_LEN; + unsigned int buff_len = 0; + char *pBuff; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + frame_tot = 100; + + /* set data length, allocation buffer memory */ + + ret = -ENOMEM; + pBuff = kzalloc(buff_size, GFP_KERNEL); + if (!pBuff) + goto ErrorAlloc; + + /* each node data 2bytes : 1frame bytes = node_tot * 2 */ + node_tot = ts->rx_count * ts->tx_count; + frame_len_byte = node_tot * 2; + + pRead = kzalloc(frame_len_byte, GFP_KERNEL); + if (!pRead) + goto ErrorAlloc; + + /* memory whole frame data : 1frame bytes * total frame */ + pFrameAll = kzalloc(frame_len_byte * frame_tot, GFP_KERNEL); + if (!pFrameAll) + goto ErrorAlloc; + + /* float type : type size is double */ + pFrameAvg = kzalloc(frame_len_byte * 2, GFP_KERNEL); + if (!pFrameAvg) + goto ErrorAlloc; + + /* 64-bit to prevent overflow */ + pFrameStd = kzalloc(frame_len_byte * 4, GFP_KERNEL); + if (!pFrameStd) + goto ErrorAlloc; + + /* fix touch 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 ErrorAlloc; + } + + /* set OPCODE and data type */ + ret = ts->sec_ts_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 ErrorDataType; + } + + sec_ts_delay(50); + + for (frame_cnt = 0; frame_cnt < frame_tot; frame_cnt++) { + /* read data */ + ret = ts->sec_ts_read_heap(ts, SEC_TS_READ_TOUCH_RAWDATA, + pRead, frame_len_byte); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: read rawdata failed!\n", __func__); + goto ErrorRelease; + } + + memset(ts->pFrame, 0x00, frame_len_byte); + + for (i = 0; i < frame_len_byte; i += 2) { + ts->pFrame[i / 2] = pRead[i + 1] + (pRead[i] << 8); + pFrameAvg[i / 2] += ts->pFrame[i / 2]; + } + + memcpy(pFrameAll + (frame_len_byte * frame_cnt) / sizeof(short), + ts->pFrame, frame_len_byte); + } + + /* get total frame average of each node */ + /* in the case of getting only average, *1000 not needed */ + for (j = 0; j < node_tot; j++) { + if (!get_average_only) + pFrameAvg[j] = pFrameAvg[j] * 1000; + pFrameAvg[j] = pFrameAvg[j] / frame_tot; + } + + input_info(true, &ts->client->dev, "%s: FrameAvg x 1000\n", __func__); + + /* print frame average x 1000 of each node */ + for (i = 0; i < ts->rx_count; i++) { + buff_len = scnprintf(pBuff, buff_size, "Rx%02d | ", i); + + for (j = 0; j < ts->tx_count; j++) { + buff_len += scnprintf(pBuff + buff_len, + buff_size - buff_len, + " %6d", + pFrameAvg[(j * ts->rx_count) + i]); + } + input_info(true, &ts->client->dev, "%s\n", pBuff); + } + + /* when only getting average, put average in + * ts->pFrame and goto set cmd_result + */ + if (get_average_only) { + for (i = 0; i < ts->tx_count; i++) { + for (j = 0; j < ts->rx_count; j++) { + ts->pFrame[(j * ts->tx_count) + i] = + (short)(pFrameAvg[(i * ts->rx_count) + + j]); + } + } + goto OnlyAverage; + } + + /* get standard deviation */ + for (i = 0; i < frame_tot; i++) { + for (j = 0; j < node_tot; j++) { + tmp = pFrameAll[node_tot * i + j] * 1000; + pFrameStd[j] = pFrameStd[j] + + (tmp - pFrameAvg[j]) * (tmp - pFrameAvg[j]); + } + } + + for (j = 0; j < node_tot; j++) + pFrameStd[j] = int_sqrt(pFrameStd[j] / frame_tot); + + /* print standard deviation x 1000 of each node */ + input_info(true, &ts->client->dev, "%s: FrameStd x 1000\n", __func__); + + *min = *max = pFrameStd[0]; + + for (i = 0; i < ts->rx_count; i++) { + buff_len = scnprintf(pBuff, buff_size, "Rx%02d | ", i); + + for (j = 0; j < ts->tx_count; j++) { + if (i > 0) { + if (pFrameStd[(j * ts->rx_count) + i] < *min) + *min = + pFrameStd[(j * ts->rx_count) + i]; + + if (pFrameStd[(j * ts->rx_count) + i] > *max) + *max = + pFrameStd[(j * ts->rx_count) + i]; + } + buff_len += scnprintf(pBuff + buff_len, + buff_size - buff_len, + " %6d", + (int)pFrameStd[(j * ts->rx_count) + i]); + } + input_info(true, &ts->client->dev, "%s\n", pBuff); + } + // SQRT(VAR) + + /* Rotate 90 degrees for readability */ + for (i = 0; i < ts->tx_count; i++) { + for (j = 0; j < ts->rx_count; j++) { + if (pFrameStd[(i * ts->rx_count) + j] > 32767) + /* Reduce to short data type and allow high + * values to saturate. + */ + ts->pFrame[(j * ts->tx_count) + i] = 32767; + else { + ts->pFrame[(j * ts->tx_count) + i] = + (short)(pFrameStd[(i * ts->rx_count) + + j]); + } + } + } + + if (*spec_check == SPEC_CHECK) { + int specover_count = 0; + + for (i = 0; i < ts->tx_count; i++) { + for (j = 0; j < ts->rx_count; j++) { + if (ts->pFrame[(j * ts->tx_count) + i] > + cm_stdev_max) + specover_count++; + } + } + + if (specover_count == 0) + *spec_check = SPEC_PASS; + else + *spec_check = SPEC_FAIL; + } + + if (*spec_check == SPEC_PASS) + buff_len = scnprintf(pBuff, buff_size, "OK %d %d\n", + ts->rx_count, ts->tx_count); + else if (*spec_check == SPEC_FAIL) + buff_len = scnprintf(pBuff, buff_size, "NG %d %d\n", + ts->rx_count, ts->tx_count); + else + buff_len = scnprintf(pBuff, buff_size, "\n"); + + for (i = 0; i < node_tot; i++) { + buff_len += scnprintf(pBuff + buff_len, buff_size - buff_len, + "%4d,", ts->pFrame[i]); + + if (i % ts->tx_count == ts->tx_count - 1) + buff_len += scnprintf(pBuff + buff_len, + buff_size - buff_len, "\n"); + } + + if (!sec) + goto ErrorRelease; + + sec_cmd_set_cmd_result(sec, pBuff, buff_len); + sec->cmd_state = SEC_CMD_STATUS_OK; + +ErrorRelease: +OnlyAverage: + /* release data monitory (unprepare AFE data memory) */ + ret = ts->sec_ts_write(ts, SEC_TS_CMD_MUTU_RAW_TYPE, &inval_type, + 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: Set rawdata type failed\n", __func__); + goto ErrorAlloc; + } + +ErrorDataType: + /* release mode fix */ + ret = sec_ts_release_tmode(ts); + if (ret < 0) { + input_err(true, + &ts->client->dev, "%s: failed to release tmode\n", + __func__); + } + +ErrorAlloc: + kfree(pFrameStd); + kfree(pFrameAvg); + kfree(pFrameAll); + kfree(pRead); + kfree(pBuff); + + return ret; +} + +static void run_rawdata_stdev_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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_REMV_AMB_DATA; + mode.spec_check = SPEC_CHECK; + + sec_ts_read_frame_stdev(ts, sec, mode.type, mode.min, mode.max, + &mode.spec_check, false); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static int sec_ts_read_frame_p2p(struct sec_ts_data *ts, + struct sec_cmd_data *sec, struct sec_ts_test_mode mode) +{ + const unsigned int frame_size = ts->rx_count * ts->tx_count * 2; + short *temp = NULL; + unsigned short readbytes; + int i; + int ret = -1; + char para = TO_TOUCH_MODE; + const unsigned int buff_size = ts->tx_count * ts->rx_count * + CMD_RESULT_WORD_LEN; + unsigned int buff_len = 0; + char *buff; + u8 result = 0x0; + + buff = kzalloc(buff_size, GFP_KERNEL); + if (!buff) + goto ErrorAllocbuff; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, + "%s: [ERROR] Touch is stopped\n", __func__); + goto ErrorPowerState; + } + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + + disable_irq(ts->client->irq); + + ret = execute_p2ptest(ts); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: P2P test failed\n", + __func__); + goto ErrorP2PTest; + } + + /* get min data */ + mode.type = TYPE_NOI_P2P_MIN; + mode.spec_check = SPEC_CHECK; + sec_ts_read_frame(ts, mode.type, mode.min, mode.max, + &mode.spec_check); + if (mode.spec_check == SPEC_FAIL) + result |= 0x1; + + readbytes = ts->rx_count * ts->tx_count; + + /* 2 bytes for each node data */ + temp = kzalloc(frame_size, GFP_KERNEL); + if (!temp) + goto ErrorAlloctemp; + + memcpy(temp, ts->pFrame, frame_size); + memset(ts->pFrame, 0x00, frame_size); + + /* get max data */ + mode.type = TYPE_NOI_P2P_MAX; + mode.spec_check = SPEC_CHECK; + sec_ts_read_frame(ts, mode.type, mode.min, mode.max, + &mode.spec_check); + if (mode.spec_check == SPEC_FAIL) + result |= 0x2; + + for (i = 0; i < readbytes; i++) { + /* get p2p by subtract min from max data */ + ts->pFrame[i] = ts->pFrame[i] - temp[i]; + if (ts->pFrame[i] > noi_mm) + result |= 0x4; + } + + if (result != 0x0) + buff_len += scnprintf(buff + buff_len, buff_size - buff_len, + "NG\n"); + else + buff_len += scnprintf(buff + buff_len, buff_size - buff_len, + "OK\n"); + + for (i = 0; i < readbytes; i++) { + buff_len += scnprintf(buff + buff_len, buff_size - buff_len, + "%3d,", ts->pFrame[i]); + if (i % ts->tx_count == (ts->tx_count - 1)) + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, "\n"); + } + + if (sec) { + sec_cmd_set_cmd_result(sec, buff, buff_len); + sec->cmd_state = SEC_CMD_STATUS_OK; + } + + kfree(temp); +ErrorAlloctemp: +ErrorP2PTest: + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_POWER_MODE, ¶, 1); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Set powermode failed\n", + __func__); + + enable_irq(ts->client->irq); +ErrorPowerState: + kfree(buff); +ErrorAllocbuff: + + if (sec && ret < 0) { + sec_cmd_set_cmd_result(sec, "NG", 3); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } + + return ret; +} + +static void run_rawdata_p2p_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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + + sec_ts_read_frame_p2p(ts, sec, mode); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +/* 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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SEC; + mode.frame_channel = TEST_MODE_READ_CHANNEL; + + sec_ts_read_raw_data(ts, sec, &mode); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SEC; + mode.frame_channel = TEST_MODE_READ_CHANNEL; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, sec, &mode); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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.spec_check = SPEC_CHECK; + + sec_ts_read_raw_data(ts, sec, &mode); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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; + mode.spec_check = SPEC_CHECK; + + sec_ts_read_raw_data(ts, sec, &mode); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void run_self_rawcap_gap_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; + int i; + int ret = 0; + char *buff = NULL; + short *gap = NULL; + const int gap_buff_size = (ts->tx_count - 1) + (ts->rx_count - 1); + const int buff_size = gap_buff_size * CMD_RESULT_WORD_LEN + 4; + unsigned int buff_len = 0; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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; + + gap = kzalloc(gap_buff_size, GFP_KERNEL); + buff = kzalloc(buff_size, GFP_KERNEL); + if (!gap || !buff) { + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, "FAIL", 4); + goto ErrorAlloc; + } + + sec_ts_read_channel(ts, mode.type, mode.min, mode.max, + &mode.spec_check); + + /* ret is number of spec over channel */ + ret = sec_ts_cs_spec_over_check(ts, gap); + + if (ret == 0) { + buff_len = scnprintf(buff, buff_size, "OK\n "); + sec->cmd_state = SEC_CMD_STATUS_OK; + } else { + buff_len = scnprintf(buff, buff_size, "NG\n "); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } + + for (i = 0; i < (ts->tx_count - 1); i++) { + buff_len += scnprintf(buff + buff_len, buff_size - buff_len, + "%6d,", gap[i]); + } + buff_len += scnprintf(buff + buff_len, buff_size - buff_len, "\n"); + + for (i = ts->tx_count; i < ts->tx_count + (ts->rx_count - 1); i++) { + buff_len += scnprintf(buff + buff_len, buff_size - buff_len, + "%6d,\n", gap[i]); + } + + sec_cmd_set_cmd_result(sec, buff, buff_len); + +ErrorAlloc: + kfree(buff); + kfree(gap); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_SIGNAL_DATA; + mode.frame_channel = TEST_MODE_READ_CHANNEL; + + sec_ts_read_raw_data(ts, sec, &mode); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_SIGNAL_DATA; + mode.frame_channel = TEST_MODE_READ_CHANNEL; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, sec, &mode); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +/* 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_info(true, &ts->client->dev, "%s\n", __func__); + + buff[0] = offset; + + ret = ts->sec_ts_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_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_write(ts, SEC_TS_CMD_CLEAR_EVENT_STACK, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: 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; + ret = ts->sec_ts_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 + */ +#ifdef I2C_INTERFACE + ret = ts->sec_ts_read_bulk(ts, buff, 1); +#else + ret = ts->sec_ts_read(ts, SEC_TS_CMD_NVM, buff, 1); +#endif + 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_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__); + + input_dbg(true, &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_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_write(ts, SEC_TS_CMD_CLEAR_EVENT_STACK, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: 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_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 + */ +#ifdef I2C_INTERFACE + ret = ts->sec_ts_read_bulk_heap(ts, buff, length); +#else + ret = ts->sec_ts_read(ts, SEC_TS_CMD_NVM, buff, length); +#endif + 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_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__); + + input_dbg(true, &ts->client->dev, "%s: SENSE ON\n", __func__); + + kfree(buff); + + return ret; +} + +#ifdef PAT_CONTROL +void set_pat_magic_number(struct sec_ts_data *ts) +{ + char buff[4] = { 0 }; + int ret; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + input_info(true, &ts->client->dev, "%s\n", __func__); + buff[0] = SEC_TS_NVM_OFFSET_CAL_COUNT; + buff[1] = 0; + buff[2] = PAT_MAGIC_NUMBER; + + input_info(true, &ts->client->dev, "%s: %02X\n", __func__, buff[2]); + + ret = ts->sec_ts_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); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} +#endif + + +/* FACTORY TEST RESULT SAVING FUNCTION + * bit 3 ~ 0 : OCTA Assy + * bit 7 ~ 4 : OCTA module + * param[0] : OCTA modue(1) / OCTA Assy(2) + * param[1] : TEST NONE(0) / TEST FAIL(1) / TEST PASS(2) : 2 bit + */ + +#define TEST_OCTA_MODULE 1 +#define TEST_OCTA_ASSAY 2 + +#define TEST_OCTA_NONE 0 +#define TEST_OCTA_FAIL 1 +#define TEST_OCTA_PASS 2 + +static void set_tsp_test_result(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_result *result; + char buff[SEC_CMD_STR_LEN] = { 0 }; + char r_data[1] = { 0 }; + int ret = 0; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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_truned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + r_data[0] = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_FAC_RESULT); + if (r_data[0] == 0xFF) + r_data[0] = 0; + + result = (struct sec_ts_test_result *)r_data; + + if (sec->cmd_param[0] == TEST_OCTA_ASSAY) { + result->assy_result = sec->cmd_param[1]; + if (result->assy_count < 3) + result->assy_count++; + } + + if (sec->cmd_param[0] == TEST_OCTA_MODULE) { + result->module_result = sec->cmd_param[1]; + if (result->module_count < 3) + result->module_count++; + } + + input_info(true, &ts->client->dev, + "%s: %d, %d, %d, %d, 0x%X\n", __func__, + result->module_result, result->module_count, + result->assy_result, result->assy_count, result->data[0]); + + /* 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) + * buff[2] : write data + **/ + memset(buff, 0x00, SEC_CMD_STR_LEN); + buff[2] = *result->data; + + input_info(true, &ts->client->dev, "%s: command (1)%X, (2)%X: %X\n", + __func__, sec->cmd_param[0], sec->cmd_param[1], buff[2]); + + ret = ts->sec_ts_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); + + ts->nv = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_FAC_RESULT); + + snprintf(buff, sizeof(buff), "OK"); + sec_cmd_set_cmd_result(sec, buff, strlen(buff)); + sec->cmd_state = SEC_CMD_STATUS_OK; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void get_tsp_test_result(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 }; + struct sec_ts_test_result *result; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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_truned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + memset(buff, 0x00, SEC_CMD_STR_LEN); + buff[0] = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_FAC_RESULT); + if (buff[0] == 0xFF) { + set_tsp_nvm_data_clear(ts, SEC_TS_NVM_OFFSET_FAC_RESULT); + buff[0] = 0; + } + + ts->nv = buff[0]; + + result = (struct sec_ts_test_result *)buff; + + input_info(true, &ts->client->dev, + "%s: [0x%X][0x%X] M:%d, M:%d, A:%d, A:%d\n", + __func__, *result->data, buff[0], + result->module_result, result->module_count, + result->assy_result, result->assy_count); + + snprintf(buff, sizeof(buff), "M:%s, M:%d, A:%s, A:%d", + result->module_result == 0 ? "NONE" : + result->module_result == 1 ? "FAIL" : "PASS", + result->module_count, + result->assy_result == 0 ? "NONE" : + result->assy_result == 1 ? "FAIL" : "PASS", + result->assy_count); + + sec_cmd_set_cmd_result(sec, buff, strlen(buff)); + sec->cmd_state = SEC_CMD_STATUS_OK; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void increase_disassemble_count(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; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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_truned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + buff[2] = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_DISASSEMBLE_COUNT); + + input_info(true, &ts->client->dev, + "%s: disassemble count is #1 %d\n", __func__, buff[2]); + + if (buff[2] == 0xFF) + buff[2] = 0; + + if (buff[2] < 0xFE) + buff[2]++; + + /* 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) + * buff[2] : write data + **/ + buff[0] = SEC_TS_NVM_OFFSET_DISASSEMBLE_COUNT; + buff[1] = 0; + + ret = ts->sec_ts_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); + + memset(buff, 0x00, 3); + buff[0] = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_DISASSEMBLE_COUNT); + input_info(true, &ts->client->dev, + "%s: check disassemble count: %d\n", __func__, buff[0]); + + snprintf(buff, sizeof(buff), "OK"); + sec_cmd_set_cmd_result(sec, buff, strlen(buff)); + sec->cmd_state = SEC_CMD_STATUS_OK; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void get_disassemble_count(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\n", __func__); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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_truned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + memset(buff, 0x00, SEC_CMD_STR_LEN); + buff[0] = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_DISASSEMBLE_COUNT); + if (buff[0] == 0xFF) { + set_tsp_nvm_data_clear(ts, SEC_TS_NVM_OFFSET_DISASSEMBLE_COUNT); + buff[0] = 0; + } + + input_info(true, &ts->client->dev, + "%s: read disassemble count: %d\n", __func__, buff[0]); + + snprintf(buff, sizeof(buff), "%d", buff[0]); + + sec_cmd_set_cmd_result(sec, buff, strlen(buff)); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +#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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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, strlen(buff)); + sec->cmd_state = SEC_CMD_STATUS_WAITING; + sec_cmd_set_cmd_exit(sec); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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...\n", __func__, buff); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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_WAITING; + sec_cmd_set_cmd_exit(sec); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +}; + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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_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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +}; + + +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 }; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +}; + +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; + + for (i = 0; i < length; i += 4) { + sec_ts_swap(&data[i], &data[i + 3]); + sec_ts_swap(&data[i + 1], &data[i + 2]); + } +} + +int execute_p2ptest(struct sec_ts_data *ts) +{ + int rc; + u8 tpara[2] = {0x0F, 0x11}; + + input_info(true, &ts->client->dev, "%s: P2P test start!\n", __func__); + rc = ts->sec_ts_write(ts, SEC_TS_CMD_SET_P2PTEST_MODE, tpara, 2); + if (rc < 0) { + input_err(true, &ts->client->dev, + "%s: Send P2Ptest Mode cmd failed!\n", __func__); + goto err_exit; + } + + sec_ts_delay(15); + + tpara[0] = 0x00; + tpara[1] = 0x64; + rc = ts->sec_ts_write(ts, SEC_TS_CMD_P2PTEST, tpara, 2); + if (rc < 0) { + input_err(true, &ts->client->dev, + "%s: Send P2Ptest cmd failed!\n", __func__); + goto err_exit; + } + + sec_ts_delay(1500); + + rc = sec_ts_wait_for_ready(ts, SEC_TS_VENDOR_ACK_P2P_TEST_DONE); + if (rc < 0) { + input_err(true, &ts->client->dev, + "%s: P2Ptest execution time out!\n", __func__); + goto err_exit; + } + + input_info(true, &ts->client->dev, "%s: P2P test done!\n", __func__); + +err_exit: + return rc; +} + +/* execute_selftest options + * bit[8] : Enable/disable the panel crack test + * bit[7] : Do NOT save + * bit[6] : Load self-test configuration only + * bit[5] : Get Self capacitance + * bit[4] : Reserved + * bit[3] : Reserved + * bit[2] : Enable/disable the short test + * bit[1] : Enable/disable the node variance test + * bit[0] : Enable/disable the open test + */ +int execute_selftest(struct sec_ts_data *ts, u32 option) +{ + int rc; + /* Selftest setting + * Get self capacitance + * Enable/disable the short test + * Enable/disable the node variance test + * Enable/disable the open test + */ + u8 tpara[2] = {(u8)(option & 0xff), (u8)((option & 0xff00) >> 8)}; + u8 *rBuff; + int i; + int result_size = SEC_TS_SELFTEST_REPORT_SIZE + + ts->tx_count * ts->rx_count * 2; + + 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_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(1500); + + 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_info(true, &ts->client->dev, "%s: Self test done!\n", __func__); + + rc = ts->sec_ts_read_heap(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, result_size); + + for (i = 0; i < 80; i += 4) { + if (i % 8 == 0) pr_cont("\n"); + if (i % 4 == 0) pr_cont("%s sec_ts : ", SECLOG); + + if (i / 4 == 0) pr_cont("SIG"); + else if (i / 4 == 1) pr_cont("VER"); + else if (i / 4 == 2) pr_cont("SIZ"); + else if (i / 4 == 3) pr_cont("CRC"); + else if (i / 4 == 4) pr_cont("RES"); + else if (i / 4 == 5) pr_cont("COU"); + else if (i / 4 == 6) pr_cont("PAS"); + else if (i / 4 == 7) pr_cont("FAI"); + else if (i / 4 == 8) pr_cont("CHA"); + else if (i / 4 == 9) pr_cont("AMB"); + else if (i / 4 == 10) pr_cont("RXS"); + else if (i / 4 == 11) pr_cont("TXS"); + else if (i / 4 == 12) pr_cont("RXO"); + else if (i / 4 == 13) pr_cont("TXO"); + else if (i / 4 == 14) pr_cont("RXG"); + else if (i / 4 == 15) pr_cont("TXG"); + else if (i / 4 == 16) pr_cont("RXR"); + else if (i / 4 == 17) pr_cont("TXT"); + else if (i / 4 == 18) pr_cont("RXT"); + else if (i / 4 == 19) pr_cont("TXR"); + + pr_cont(" %2X, %2X, %2X, %2X ", + rBuff[i], rBuff[i + 1], rBuff[i + 2], rBuff[i + 3]); + + + if (i / 4 == 4) { + /* RX, RX open check. */ + if ((rBuff[i + 3] & 0x30) != 0) + rc = 0; + /* TX, RX GND(VDD) short check. */ + else if ((rBuff[i + 3] & 0xC0) != 0) + rc = 0; + /* RX-RX, TX-TX short check. */ + else if ((rBuff[i + 2] & 0x03) != 0) + rc = 0; + /* TX-RX short check. */ + else if ((rBuff[i + 2] & 0x04) != 0) + 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]; + } + + } + +err_exit: + kfree(rBuff); + return rc; +} + +static void run_fs_cal_pre_press(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 = 0; + u8 off[1] = {STATE_MANAGE_OFF}; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + input_info(true, &ts->client->dev, "%s: initial sequence for fs cal\n", + __func__); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Touch is stopped!\n", + __func__); + goto ErrorPowerOff; + } + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_DISABLE_GAIN_LIMIT, off, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: fail to disable gain limit\n", __func__); + goto ErrorSendingCmd; + } + + ret = sec_ts_fix_tmode(ts, TOUCH_SYSTEM_MODE_TOUCH, + TOUCH_MODE_STATE_TOUCH); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail to fix tmode\n", + __func__); + goto ErrorSendingCmd; + } + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_DISABLE_BASELINE_ADAPT, off, + 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: fail to disable baselineAdapt\n", __func__); + goto ErrorSendingCmd; + } + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_DISABLE_DF, off, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail to disable df\n", + __func__); + goto ErrorSendingCmd; + } + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_TOUCH_ENGINE_MODE, + off, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: fail to disable touch engine\n", __func__); + goto ErrorSendingCmd; + } + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_RESET_BASELINE, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail to fix tmode\n", + __func__); + goto ErrorSendingCmd; + } + + sec_ts_delay(50); + + input_info(true, &ts->client->dev, "%s: ready to press\n", __func__); + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +ErrorSendingCmd: +ErrorPowerOff: + snprintf(buff, sizeof(buff), "%s", "NG"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void run_fs_cal_get_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); + struct sec_ts_test_mode mode; + short *diff_table = NULL; + const bool only_average = true; + char *buff; + int ret; + int i, j; + const unsigned int buff_size = ts->tx_count * ts->rx_count * + CMD_RESULT_WORD_LEN; + unsigned int buff_len = 0; + + input_info(true, &ts->client->dev, "%s: fs cal with stim pad\n", + __func__); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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__); + sec_cmd_set_cmd_result(sec, "NG", 2); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 3) { + input_err(true, &ts->client->dev, + "%s: Parameter Error\n", __func__); + sec_cmd_set_cmd_result(sec, "NG", 2); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + buff = kzalloc(buff_size, GFP_KERNEL); + if (!buff) { + sec_cmd_set_cmd_result(sec, "NG", 2); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + + if (sec->cmd_param[0] == 0) { + mode.type = TYPE_REMV_AMB_DATA; + mode.spec_check = SPEC_NO_CHECK; + ret = sec_ts_read_frame_stdev(ts, sec, mode.type, mode.min, + mode.max, &mode.spec_check, only_average); + if (ret < 0) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto SetCmdResult; + } + ret = sec_ts_check_fs_precal(ts); + + /* ret = NG node count */ + if (ret > 0) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, "NG\n"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, "OK\n"); + sec->cmd_state = SEC_CMD_STATUS_OK; + } + + for (i = 0; i < ts->tx_count * ts->rx_count; i++) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, "%4d,", + ts->pFrame[i]); + + if (i % ts->tx_count == ts->tx_count - 1) + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "\n"); + } + /* calculate gain table, and store it to ts->gainTable */ + sec_ts_get_gain_table(ts); + + } else if (sec->cmd_param[0] == 1) { + /* write gaintable(ts->gainTable) to ic */ + ret = sec_ts_write_gain_table(ts); + if (ret < 0) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "NG %d %d\n", + ts->rx_count, ts->tx_count); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "OK %d %d\n", + ts->rx_count, ts->tx_count); + sec->cmd_state = SEC_CMD_STATUS_OK; + } + + for (j = 0; j < ts->rx_count; j++) { + for (i = 0; i < ts->tx_count; i++) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "%4d,", + ts->gainTable[i*ts->rx_count + + j]); + } + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, "\n"); + } + } else if (sec->cmd_param[0] == 2) { + int mean; + /* for stim pad fixture 2 */ + mode.type = TYPE_NORM2_DATA; + mode.spec_check = SPEC_NO_CHECK; + + ret = sec_ts_read_frame_stdev(ts, sec, mode.type, mode.min, + mode.max, &mode.spec_check, only_average); + if (ret < 0) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto SetCmdResult; + } + + mean = sec_ts_get_postcal_mean(ts); + ts->fs_postcal_mean = mean; + + input_info(true, &ts->client->dev, + "%s : FS mean = %d\n", __func__, mean); + + if ((mean > fs_mean_target_h) || (mean < fs_mean_target_l)) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "NG %d %d\n%d\n", + ts->rx_count, ts->tx_count, + ts->fs_postcal_mean); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "OK %d %d\n%d\n", + ts->rx_count, ts->tx_count, + ts->fs_postcal_mean); + sec->cmd_state = SEC_CMD_STATUS_OK; + } + + for (i = 0; i < ts->tx_count * ts->rx_count; i++) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, "%4d,", + ts->pFrame[i]); + + if (i % ts->tx_count == ts->tx_count - 1) + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "\n"); + } + } else { //(sec->cmd_param[0] == 3) + /* get fs_uniformity */ + + diff_table = kzalloc(ts->tx_count * ts->rx_count * 2, + GFP_KERNEL); + + if ((diff_table == NULL) || (ts->fs_postcal_mean == 0)) { + input_err(true, &ts->client->dev, + "%s: fail to alloc diffTable, postcal mean = %d\n", + __func__, ts->fs_postcal_mean); + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, "NG %d %d", + ts->rx_count, ts->tx_count); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto SetCmdResult; + } + + ret = sec_ts_get_postcal_uniformity(ts, diff_table); + + if (ret == 0) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "OK %d %d\n", + ts->rx_count, ts->tx_count); + sec->cmd_state = SEC_CMD_STATUS_OK; + } else { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "NG %d %d\n", + ts->rx_count, ts->tx_count); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } + + for (i = 0; i < ts->tx_count * ts->rx_count; i++) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, "%4d,", + diff_table[i]); + + if (i % ts->tx_count == ts->tx_count - 1) + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "\n"); + } + } + +SetCmdResult: + + sec_cmd_set_cmd_result(sec, buff, buff_len); + + kfree(diff_table); + kfree(buff); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void run_fs_cal_post_press(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 = 0; + u8 on[1] = {STATE_MANAGE_ON}; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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__); + goto ErrorPowerOff; + } + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_TOUCH_ENGINE_MODE, on, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: fail to enable touch engine\n", __func__); + goto ErrorSendingCmd; + } + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_DISABLE_DF, on, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail to enable df\n", + __func__); + goto ErrorSendingCmd; + } + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_DISABLE_BASELINE_ADAPT, on, + 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: fail to enable baselineAdapt\n", __func__); + goto ErrorSendingCmd; + } + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_DISABLE_GAIN_LIMIT, on, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: fail to enable gain limit\n", __func__); + goto ErrorSendingCmd; + } + + ret = sec_ts_release_tmode(ts); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail to release tmode\n", + __func__); + goto ErrorSendingCmd; + } + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +ErrorSendingCmd: +ErrorPowerOff: + snprintf(buff, sizeof(buff), "%s", "NG"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +/* enable_fs_cal_table : enable or disable fs cal table + * cmd_param : 0 to disable, 1 to enable + * touch mode and state should be fixed before enable or disable + */ +static void enable_fs_cal_table(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); + u8 tPara; + int ret; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, "NG", 2); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + ret = sec_ts_fix_tmode(ts, TOUCH_SYSTEM_MODE_TOUCH, + TOUCH_MODE_STATE_TOUCH); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail to fix tmode\n", + __func__); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, "NG", 2); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + tPara = sec->cmd_param[0]; + + input_info(true, &ts->client->dev, "%s: fs cal table %s\n", + __func__, ((tPara == 0) ? "disable" : "enable")); + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_DISABLE_NORM_TABLE, + &tPara, 1); + + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: cmd write failed\n", __func__); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, "NG", 2); + } else { + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, "OK", 2); + } + + ret = sec_ts_release_tmode(ts); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to release tmode\n", + __func__); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void enable_coordinate_report(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); + int ret = 0; + u8 tPara; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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__); + sec_cmd_set_cmd_result(sec, "TSP turned off", 14); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, "NG", 2); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + tPara = sec->cmd_param[0]; + + input_info(true, &ts->client->dev, "%s: coordinate report %s\n", + __func__, ((tPara == 0) ? "disable" : "enable")); + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_TOUCH_ENGINE_MODE, + &tPara, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: cmd write failed\n", __func__); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, "NG", 2); + } else { + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, "OK", 2); + } + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void enable_gain_limit(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); + int ret = 0; + u8 tPara; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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__); + sec_cmd_set_cmd_result(sec, "TSP turned off", 14); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, "NG", 2); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + tPara = sec->cmd_param[0]; + + input_info(true, &ts->client->dev, "%s: gain limit %s\n", + __func__, ((tPara == 0) ? "disable" : "enable")); + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_DISABLE_GAIN_LIMIT, &tPara, + 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: cmd write failed\n", __func__); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, "NG", 2); + } else { + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, "OK", 2); + } + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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[SEC_CMD_STR_LEN] = {0}; + int rc; + char para = TO_TOUCH_MODE; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + disable_irq(ts->client->irq); + + rc = execute_selftest(ts, TEST_SHORT | TEST_OPEN | TEST_NODE_VARIANCE); + if (rc > 0) { + ts->sec_ts_write(ts, SEC_TS_CMD_SET_POWER_MODE, ¶, 1); + enable_irq(ts->client->irq); + 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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + ts->sec_ts_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; + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + + +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_SEC) + 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_write(ts, cmd, NULL, 0) < 0) { + input_err(true, &ts->client->dev, + "%s: Write Cal commend failed!\n", __func__); + return rc; + } + + sec_ts_delay(4000); + + 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; +#ifdef PAT_CONTROL + u8 img_ver[4]; +#endif + struct sec_ts_test_mode mode; + char mis_cal_data = 0xF0; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + sec_ts_read_calibration_report(ts); + + if (ts->touch_count > 0) { + snprintf(buff, sizeof(buff), "%s", "NG_FINGER_ON"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out_force_cal; + } + + disable_irq(ts->client->irq); + + rc = sec_ts_execute_force_calibration(ts, OFFSET_CAL_SEC); + if (rc < 0) { + snprintf(buff, sizeof(buff), "%s", "FAIL"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { +#ifdef USE_PRESSURE_SENSOR + rc = sec_ts_execute_force_calibration(ts, PRESSURE_CAL); + if (rc < 0) + input_err(true, &ts->client->dev, + "%s: fail to write PRESSURE CAL!\n", __func__); +#endif + + if (ts->plat_data->mis_cal_check) { + buff[0] = 0; + rc = ts->sec_ts_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_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_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_read(ts, SEC_TS_CMD_MIS_CAL_READ, + &mis_cal_data, 1); + if (rc < 0) { + input_err(true, &ts->client->dev, + "%s: 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_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; + + enable_irq(ts->client->irq); + + goto out_force_cal; + } + } + +#ifdef PAT_CONTROL + ts->cal_count = get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_CAL_COUNT); + + if (ts->external_factory == true) { + /* for external factory mode */ + if (ts->cal_count == PAT_MAX_EXT) + ts->cal_count = PAT_MAX_EXT; + else if (ts->cal_count >= PAT_EXT_FACT && + ts->cal_count < PAT_MAX_EXT) + ts->cal_count++; + else + ts->cal_count = PAT_EXT_FACT; + + /* not to enter external factory mode without setting + * everytime + **/ + ts->external_factory = false; + } else { + /* change from (virtual pat or vpat by external fatory) + * to real pat by forced calibarion by LCIA + **/ + if (ts->cal_count >= PAT_MAGIC_NUMBER) + ts->cal_count = 1; + else if (ts->cal_count == PAT_MAX_LCIA) + ts->cal_count = PAT_MAX_LCIA; + else + ts->cal_count++; + } + + /* 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) + * buff[2] : write data + **/ + buff[0] = SEC_TS_NVM_OFFSET_CAL_COUNT; + buff[1] = 0; + buff[2] = ts->cal_count; + input_info(true, &ts->client->dev, + "%s: write to nvm cal_count(%2X)\n", + __func__, buff[2]); + + rc = ts->sec_ts_write(ts, SEC_TS_CMD_NVM, buff, 3); + if (rc < 0) { + input_err(true, &ts->client->dev, + "%s: nvm write failed. ret: %d\n", + __func__, rc); + } + + sec_ts_delay(20); + + ts->cal_count = get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_CAL_COUNT); + + rc = ts->sec_ts_read(ts, SEC_TS_READ_IMG_VERSION, img_ver, 4); + if (rc < 0) { + input_err(true, &ts->client->dev, + "%s: Image version read error\n", __func__); + } else { + memset(buff, 0x00, SEC_CMD_STR_LEN); + buff[0] = get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_TUNE_VERSION); + if (buff[0] == 0xFF) { + set_tsp_nvm_data_clear(ts, + SEC_TS_NVM_OFFSET_TUNE_VERSION); + set_tsp_nvm_data_clear(ts, + SEC_TS_NVM_OFFSET_TUNE_VERSION + 1); + } + + ts->tune_fix_ver = (img_ver[2]<<8 | img_ver[3]); + buff[0] = SEC_TS_NVM_OFFSET_TUNE_VERSION; + buff[1] = 1;// 2bytes + buff[2] = img_ver[2]; + buff[3] = img_ver[3]; + input_info(true, &ts->client->dev, + "%s: write tune_ver to nvm (%2X %2X)\n", + __func__, buff[2], buff[3]); + + rc = ts->sec_ts_write(ts, SEC_TS_CMD_NVM, buff, 4); + if (rc < 0) { + input_err(true, &ts->client->dev, + "%s: nvm write failed. ret: %d\n", + __func__, rc); + } + sec_ts_delay(20); + + buff[0] = get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_TUNE_VERSION); + buff[1] = get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_TUNE_VERSION + 1); + ts->tune_fix_ver = buff[0]<<8 | buff[1]; + input_info(true, &ts->client->dev, + "%s: cal_count [%2X] tune_fix_ver [%04X]\n", + __func__, ts->cal_count, ts->tune_fix_ver); + } +#endif + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + } + + enable_irq(ts->client->irq); + +out_force_cal: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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 buff_len = sizeof(buff); + int written = 0; + int rc; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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__); + written += scnprintf(buff + written, buff_len - written, + "%s", "TSP turned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + rc = sec_ts_read_calibration_report(ts); + if (rc < 0) { + written += scnprintf(buff + written, buff_len - written, + "%s\n", "FAIL"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else if (rc == SEC_TS_STATUS_CALIBRATION_SDC) { + written += scnprintf(buff + written, buff_len - written, + "%s\n", "OK(MODULE)"); + sec->cmd_state = SEC_CMD_STATUS_OK; + } else if (rc == SEC_TS_STATUS_CALIBRATION_SEC) { + written += scnprintf(buff + written, buff_len - written, + "%s\n", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + } else { + written += scnprintf(buff + written, buff_len - written, + "%s\n", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } + + written += scnprintf(buff + written, buff_len - written, + "%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", + ts->cali_report[0], ts->cali_report[1], ts->cali_report[2], + ts->cali_report[3], ts->cali_report[4], ts->cali_report[5], + ts->cali_report[6], ts->cali_report[7]); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +#ifdef USE_PRESSURE_SENSOR +static void run_force_pressure_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; + char data[3] = { 0 }; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + if (ts->touch_count > 0) { + snprintf(buff, sizeof(buff), "%s", "NG_FINGER_ON"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out_force_pressure_cal; + } + + disable_irq(ts->client->irq); + + rc = sec_ts_execute_force_calibration(ts, PRESSURE_CAL); + if (rc < 0) { + snprintf(buff, sizeof(buff), "%s", "FAIL"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + } + + ts->pressure_cal_base = get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_PRESSURE_BASE_CAL_COUNT); + if (ts->pressure_cal_base == 0xFF) + ts->pressure_cal_base = 0; + if (ts->pressure_cal_base > 0xFD) + ts->pressure_cal_base = 0xFD; + + /* Use TSP NV area : in this model, use only one byte + * data[0] : offset from user NVM storage + * data[1] : length of stroed data - 1 (ex. using 1byte, + * value is 1 - 1 = 0) + * data[2] : write data + **/ + data[0] = SEC_TS_NVM_OFFSET_PRESSURE_BASE_CAL_COUNT; + data[1] = 0; + data[2] = ts->pressure_cal_base + 1; + + rc = ts->sec_ts_write(ts, SEC_TS_CMD_NVM, data, 3); + if (rc < 0) + input_err(true, &ts->client->dev, + "%s: nvm write failed. ret: %d\n", __func__, rc); + + ts->pressure_cal_base = get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_PRESSURE_BASE_CAL_COUNT); + + input_info(true, &ts->client->dev, "%s: count:%d\n", + __func__, ts->pressure_cal_base); + + enable_irq(ts->client->irq); + +out_force_pressure_cal: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void set_pressure_test_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; + unsigned char data = TYPE_INVALID_DATA; + unsigned char enable = 0; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + if (sec->cmd_param[0] == 1) { + enable = 0x1; + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_TEMPERATURE_COMP_MODE, + &enable, 1); + if (ret < 0) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out_test_mode; + } + + ret = sec_ts_fix_tmode(ts, TOUCH_SYSTEM_MODE_TOUCH, + TOUCH_MODE_STATE_TOUCH); + if (ret < 0) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out_test_mode; + } + + } else { + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SELECT_PRESSURE_TYPE, + &data, 1); + if (ret < 0) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out_test_mode; + } + + ret = sec_ts_release_tmode(ts); + if (ret < 0) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out_test_mode; + } + + enable = 0x0; + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_TEMPERATURE_COMP_MODE, + &enable, 1); + if (ret < 0) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out_test_mode; + } + } + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + +out_test_mode: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static int read_pressure_data(struct sec_ts_data *ts, u8 type, short *value) +{ + unsigned char data[6] = { 0 }; + short pressure[3] = { 0 }; + int ret; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) + return -ENODEV; + + ret = ts->sec_ts_read(ts, SEC_TS_CMD_SELECT_PRESSURE_TYPE, data, 1); + if (ret < 0) + return -EIO; + + if (data[0] != type) { + input_info(true, &ts->client->dev, "%s: type change to %02X\n", + __func__, type); + + data[1] = type; + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SELECT_PRESSURE_TYPE, + &data[1], 1); + if (ret < 0) + return -EIO; + + sec_ts_delay(30); + } + + memset(data, 0x00, 6); + + ret = ts->sec_ts_read(ts, SEC_TS_CMD_READ_PRESSURE_DATA, data, 6); + if (ret < 0) + return -EIO; + + pressure[0] = (data[0] << 8 | data[1]); + pressure[1] = (data[2] << 8 | data[3]); + pressure[2] = (data[4] << 8 | data[5]); + + input_info(true, &ts->client->dev, + "%s: Left: %d, Center: %d, Rignt: %d\n", + __func__, pressure[2], pressure[1], pressure[0]); + + memcpy(value, pressure, 3 * 2); + + return ret; +} + +static void run_pressure_filtered_strength_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[SEC_CMD_STR_LEN] = {0}; + short pressure[3] = { 0 }; + int ret; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + ret = read_pressure_data(ts, TYPE_SIGNAL_DATA, pressure); + if (ret < 0) { + snprintf(buff, sizeof(buff), "%s", "WRITE FAILED"); + goto error_read_str; + } + + snprintf(buff, sizeof(buff), "%d,%d,%d", + pressure[2], pressure[1], pressure[0]); + + sec->cmd_state = SEC_CMD_STATUS_OK; + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +error_read_str: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void run_pressure_strength_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[SEC_CMD_STR_LEN] = {0}; + short pressure[3] = { 0 }; + int ret; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + ret = read_pressure_data(ts, TYPE_REMV_AMB_DATA, pressure); + if (ret < 0) { + snprintf(buff, sizeof(buff), "%s", "WRITE FAILED"); + goto error_read_str; + } + + snprintf(buff, sizeof(buff), "%d,%d,%d", + pressure[2], pressure[1], pressure[0]); + + sec->cmd_state = SEC_CMD_STATUS_OK; + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +error_read_str: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void run_pressure_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[SEC_CMD_STR_LEN] = {0}; + short pressure[3] = { 0 }; + int ret; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + ret = read_pressure_data(ts, TYPE_RAW_DATA, pressure); + if (ret < 0) { + snprintf(buff, sizeof(buff), "%s", "WRITE FAILED"); + goto error_read_rawdata; + } + + snprintf(buff, sizeof(buff), "%d,%d,%d", + pressure[2], pressure[1], pressure[0]); + + sec->cmd_state = SEC_CMD_STATUS_OK; + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +error_read_rawdata: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void run_pressure_offset_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[SEC_CMD_STR_LEN] = {0}; + short pressure[3] = { 0 }; + int ret; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + ret = read_pressure_data(ts, TYPE_OFFSET_DATA_SEC, pressure); + if (ret < 0) { + snprintf(buff, sizeof(buff), "%s", "WRITE FAILED"); + goto error_read_str; + } + + snprintf(buff, sizeof(buff), "%d,%d,%d", + pressure[2], pressure[1], pressure[0]); + + sec->cmd_state = SEC_CMD_STATUS_OK; + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +error_read_str: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void set_pressure_strength(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 data[8] = { 0 }; + u8 cal_data[18] = { 0 }; + int index; + int ret; + short pressure[3] = { 0 }; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + if ((sec->cmd_param[0] < 1) || (sec->cmd_param[0] > 4)) + goto err_cmd_param_str; + + index = sec->cmd_param[0] - 1; + + /* RIGHT */ + cal_data[0] = (sec->cmd_param[3] >> 8); + cal_data[1] = (sec->cmd_param[3] & 0xFF); + /* CENTER */ + cal_data[8] = (sec->cmd_param[2] >> 8); + cal_data[9] = (sec->cmd_param[2] & 0xFF); + /* LEFT */ + cal_data[16] = (sec->cmd_param[1] >> 8); + cal_data[17] = (sec->cmd_param[1] & 0xFF); + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_GET_PRESSURE, cal_data, 18); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: cmd write failed. ret: %d\n", __func__, ret); + goto err_comm_str; + } + + sec_ts_delay(30); + + memset(cal_data, 0x00, 18); + ret = ts->sec_ts_read(ts, SEC_TS_CMD_SET_GET_PRESSURE, cal_data, 18); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: cmd write failed. ret: %d\n", __func__, ret); + goto err_comm_str; + } + + pressure[0] = ((cal_data[16] << 8) | cal_data[17]); + pressure[1] = ((cal_data[8] << 8) | cal_data[9]); + pressure[2] = ((cal_data[0] << 8) | cal_data[1]); + + input_info(true, &ts->client->dev, "%s: [%d] : %d, %d, %d\n", + __func__, pressure[0], pressure[1], pressure[2]); + + /* Use TSP NV area : in this model, use only one byte + * buff[0] : offset from user NVM storage + * buff[1] : [n] length of stroed data - 1 (ex. using 1byte, + * value is 1 - 1 = 0) + * buff[2] ... [n] : write data ... + **/ + data[0] = SEC_TS_NVM_OFFSET_PRESSURE_STRENGTH + + (index * SEC_TS_NVM_SIZE_PRESSURE_BLOCK); + data[1] = SEC_TS_NVM_SIZE_PRESSURE_BLOCK - 1; + /* RIGHT */ + data[2] = (sec->cmd_param[3] >> 8); + data[3] = (sec->cmd_param[3] & 0xFF); + /* CENTER */ + data[4] = (sec->cmd_param[2] >> 8); + data[5] = (sec->cmd_param[2] & 0xFF); + /*LEFT */ + data[6] = (sec->cmd_param[1] >> 8); + data[7] = (sec->cmd_param[1] & 0xFF); + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_NVM, data, 8); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: nvm write failed. ret: %d\n", __func__, ret); + goto err_comm_str; + } + + sec_ts_delay(20); + + input_info(true, &ts->client->dev, "%s: [%d] : %d, %d, %d\n", + __func__, index, + (data[6] << 8) + data[7], + (data[4] << 8) + data[5], + (data[0] << 8) + data[1]); + + memset(data, 0x00, 8); + + data[0] = SEC_TS_NVM_OFFSET_PRESSURE_INDEX; + data[1] = 0; + data[2] = (u8)(index & 0xFF); + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_NVM, data, 3); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: nvm write failed. ret: %d\n", __func__, ret); + goto err_comm_str; + } + + sec_ts_delay(20); + + snprintf(buff, sizeof(buff), "%s", "OK"); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + + ts->pressure_cal_delta = get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_PRESSURE_DELTA_CAL_COUNT); + if (ts->pressure_cal_delta == 0xFF) + ts->pressure_cal_delta = 0; + + if (ts->pressure_cal_delta > 0xFD) + ts->pressure_cal_delta = 0xFD; + + /* Use TSP NV area : in this model, use only one byte + * data[0] : offset from user NVM storage + * data[1] : length of stroed data - 1 (ex. using 1byte, + * value is 1 - 1 = 0) + * data[2] : write data + **/ + data[0] = SEC_TS_NVM_OFFSET_PRESSURE_DELTA_CAL_COUNT; + data[1] = 0; + data[2] = ts->pressure_cal_delta + 1; + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_NVM, data, 3); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: nvm write failed. ret: %d\n", __func__, ret); + + ts->pressure_cal_delta = get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_PRESSURE_DELTA_CAL_COUNT); + + input_info(true, &ts->client->dev, + "%s: count:%d\n", __func__, ts->pressure_cal_delta); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +err_cmd_param_str: + input_info(true, &ts->client->dev, "%s: parameter error: %u\n", + __func__, sec->cmd_param[0]); +err_comm_str: + snprintf(buff, sizeof(buff), "NG"); + sec_cmd_set_cmd_result(sec, buff, strlen(buff)); + + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void set_pressure_rawdata(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 data[8] = { 0 }; + int index; + int ret; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + if ((sec->cmd_param[0] < 1) || (sec->cmd_param[0] > 4)) + goto err_cmd_param_raw; + + index = sec->cmd_param[0] - 1; + + /* Use TSP NV area : in this model, use only one byte + * buff[0] : offset from user NVM storage + * buff[1] : [n] length of stroed data - 1 (ex. using 1byte, + * value is 1 - 1 = 0) + * buff[2] ... [n] : write data ... + **/ + data[0] = SEC_TS_NVM_OFFSET_PRESSURE_RAWDATA + + (index * SEC_TS_NVM_SIZE_PRESSURE_BLOCK); + data[1] = SEC_TS_NVM_SIZE_PRESSURE_BLOCK - 1; + data[2] = (sec->cmd_param[3] >> 8); + data[3] = (sec->cmd_param[3] & 0xFF); + data[4] = (sec->cmd_param[2] >> 8); + data[5] = (sec->cmd_param[2] & 0xFF); + data[6] = (sec->cmd_param[1] >> 8); + data[7] = (sec->cmd_param[1] & 0xFF); + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_NVM, data, 8); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: nvm write failed. ret: %d\n", __func__, ret); + goto err_comm_raw; + } + sec_ts_delay(20); + + memset(data, 0x00, 8); + + ret = get_tsp_nvm_data_by_size(ts, SEC_TS_NVM_OFFSET_PRESSURE_RAWDATA + + (index * SEC_TS_NVM_SIZE_PRESSURE_BLOCK), + SEC_TS_NVM_SIZE_PRESSURE_BLOCK, data); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: nvm read failed. ret: %d\n", __func__, ret); + goto err_comm_raw; + } + + 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: [%d] : %d, %d, %d\n", + __func__, index, + (data[4] << 8) + data[5], + (data[2] << 8) + data[3], + (data[1] << 8) + data[0]); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +err_cmd_param_raw: + input_info(true, &ts->client->dev, "%s: parameter error: %u\n", + __func__, sec->cmd_param[0]); +err_comm_raw: + + snprintf(buff, sizeof(buff), "NG"); + sec_cmd_set_cmd_result(sec, buff, strlen(buff)); + + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void set_pressure_data_index(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 data[8] = { 0 }; + u8 cal_data[18] = { 0 }; + int index; + int ret; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + if ((sec->cmd_param[0] < 0) || (sec->cmd_param[0] > 4)) + goto err_set_cmd_param_index; + + if (sec->cmd_param[0] == 0) { + input_info(true, &ts->client->dev, + "%s: clear calibration result\n", __func__); + /* clear pressure calibrated data */ + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_GET_PRESSURE, + cal_data, 18); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: cmd write failed. ret: %d\n", + __func__, ret); + goto err_set_comm_index; + } + + sec_ts_delay(30); + + goto clear_index; + } + + index = sec->cmd_param[0] - 1; + + ret = get_tsp_nvm_data_by_size(ts, SEC_TS_NVM_OFFSET_PRESSURE_STRENGTH, + 24, data); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: nvm read failed. ret: %d\n", __func__, ret); + goto err_set_comm_index; + } + + cal_data[16] = data[6 * index + 4]; + cal_data[17] = data[6 * index + 5]; /* LEFT */ + + cal_data[8] = data[6 * index + 2]; + cal_data[9] = data[6 * index + 3]; /* CENTER */ + + cal_data[0] = data[6 * index + 0]; + cal_data[1] = data[6 * index + 1]; /* RIGHT */ + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_GET_PRESSURE, cal_data, 18); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: cmd write failed. ret: %d\n", __func__, ret); + goto err_set_comm_index; + } + + sec_ts_delay(30); + + /* 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) + * buff[2] : write data + **/ + memset(data, 0x00, 8); + data[0] = SEC_TS_NVM_OFFSET_PRESSURE_INDEX; + data[1] = 0; + data[2] = (u8)(index & 0xFF); + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_NVM, data, 3); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: nvm write failed. ret: %d\n", __func__, ret); + goto err_set_comm_index; + } + + sec_ts_delay(20); + +clear_index: + snprintf(buff, sizeof(buff), "%s", "OK"); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +err_set_cmd_param_index: + input_info(true, &ts->client->dev, "%s: parameter error: %u\n", + __func__, sec->cmd_param[0]); +err_set_comm_index: + + snprintf(buff, sizeof(buff), "NG"); + sec_cmd_set_cmd_result(sec, buff, strlen(buff)); + + sec->cmd_state = SEC_CMD_STATUS_FAIL; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void get_pressure_strength(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 index; + u8 data[24] = { 0 }; + short pressure[3] = { 0 }; + int ret; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + if ((sec->cmd_param[0] < 1) || (sec->cmd_param[0] > 4)) + goto err_get_cmd_param_str; + + index = sec->cmd_param[0] - 1; + + ret = get_tsp_nvm_data_by_size(ts, SEC_TS_NVM_OFFSET_PRESSURE_STRENGTH, + 24, data); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: nvm read failed. ret: %d\n", __func__, ret); + goto err_get_comm_str; + } + + pressure[0] = ((data[6 * index + 4] << 8) + data[6 * index + 5]); + pressure[1] = ((data[6 * index + 2] << 8) + data[6 * index + 3]); + pressure[2] = ((data[6 * index + 0] << 8) + data[6 * index + 1]); + + snprintf(buff, sizeof(buff), "%d,%d,%d", + pressure[0], pressure[1], pressure[2]); + + 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: [%d] : %d, %d, %d\n", + __func__, index, pressure[0], pressure[1], pressure[2]); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +err_get_comm_str: + input_info(true, &ts->client->dev, "%s: parameter error: %u\n", + __func__, sec->cmd_param[0]); +err_get_cmd_param_str: + + snprintf(buff, sizeof(buff), "NG"); + sec_cmd_set_cmd_result(sec, buff, strlen(buff)); + + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void get_pressure_rawdata(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 index; + u8 data[24] = { 0 }; + short pressure[3] = { 0 }; + int ret; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + if ((sec->cmd_param[0] < 1) || (sec->cmd_param[0] > 4)) + goto err_get_cmd_param_raw; + + index = sec->cmd_param[0] - 1; + + ret = get_tsp_nvm_data_by_size(ts, SEC_TS_NVM_OFFSET_PRESSURE_RAWDATA, + 24, data); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: nvm read failed. ret: %d\n", __func__, ret); + goto err_get_comm_raw; + } + + pressure[0] = ((data[6 * index + 4] << 8) + data[6 * index + 5]); + pressure[1] = ((data[6 * index + 2] << 8) + data[6 * index + 3]); + pressure[2] = ((data[6 * index + 0] << 8) + data[6 * index + 1]); + + snprintf(buff, sizeof(buff), "%d,%d,%d", + pressure[0], pressure[1], pressure[2]); + + 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: [%d] : %d, %d, %d\n", + __func__, index, pressure[0], pressure[1], pressure[2]); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; +err_get_cmd_param_raw: + input_info(true, &ts->client->dev, "%s: parameter error: %u\n", + __func__, sec->cmd_param[0]); +err_get_comm_raw: + + snprintf(buff, sizeof(buff), "NG"); + sec_cmd_set_cmd_result(sec, buff, strlen(buff)); + + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void get_pressure_data_index(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 index = 0; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + index = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_PRESSURE_INDEX); + if (index < 0) { + goto err_get_index; + } else { + if (index == 0xFF) + snprintf(buff, sizeof(buff), "%d", 0); + else + snprintf(buff, sizeof(buff), "%d", index + 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: %d\n", + __func__, index); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +err_get_index: + + snprintf(buff, sizeof(buff), "NG"); + sec_cmd_set_cmd_result(sec, buff, strlen(buff)); + + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} +static void set_pressure_strength_clear(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 *data; + u8 cal_data[18] = { 0 }; + int ret; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + /* clear pressure calibrated data */ + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_GET_PRESSURE, cal_data, 18); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: cmd write failed. ret: %d\n", __func__, ret); + goto err_comm_str; + } + + sec_ts_delay(30); + + /* 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) + * buff[2] : write data + **/ + + /* strength 6 * 4, rawdata 6 * 4, buff[0], buff[1] */ + data = kzalloc(50, GFP_KERNEL); + if (!data) { + input_err(true, &ts->client->dev, + "%s failed to allocate memory. ret: %d\n", + __func__, ret); + goto err_comm_str; + } + + data[0] = SEC_TS_NVM_OFFSET_PRESSURE_INDEX; + data[1] = (SEC_TS_NVM_SIZE_PRESSURE_BLOCK * 8) - 1; + + /* remove calicated strength, rawdata in NVM */ + ret = ts->sec_ts_write(ts, SEC_TS_CMD_NVM, data, 50); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: nvm write failed. ret: %d\n", __func__, ret); + goto err_mem_str; + } + + sec_ts_delay(20); + + snprintf(buff, sizeof(buff), "%s", "OK"); + + kfree(data); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +err_mem_str: + kfree(data); +err_comm_str: + snprintf(buff, sizeof(buff), "NG"); + sec_cmd_set_cmd_result(sec, buff, strlen(buff)); + + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void get_pressure_threshold(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), "300"); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; +} + +/* low level is more sensitivity, except level-0(value 0) */ +static void set_pressure_user_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}; + int ret; + char addr[3] = { 0 }; + char data[2] = { 0 }; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + if ((sec->cmd_param[0] < 1) || (sec->cmd_param[0] > 5)) + goto out_set_user_level; + + /* + * byte[0]: m_customlib_ifpacket_addr[7:0] + * byte[1]: m_customlib_ifpacket_addr[15:8] + * byte[n] : user data (max 32 bytes) + */ + addr[0] = SEC_TS_CMD_CUSTOMLIB_OFFSET_PRESSURE_LEVEL; + addr[1] = 0x00; + addr[2] = sec->cmd_param[0]; + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_CUSTOMLIB_WRITE_PARAM, addr, 3); + if (ret < 0) + goto out_set_user_level; + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_CUSTOMLIB_NOTIFY_PACKET, NULL, 0); + if (ret < 0) + goto out_set_user_level; + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_CUSTOMLIB_READ_PARAM, addr, 2); + if (ret < 0) + goto out_set_user_level; + + ret = ts->sec_ts_read(ts, SEC_TS_CMD_CUSTOMLIB_READ_PARAM, data, 1); + if (ret < 0) + goto out_set_user_level; + + input_info(true, &ts->client->dev, "%s: set user level: %d\n", + __func__, data[0]); + + ts->pressure_user_level = data[0]; + + addr[0] = SEC_TS_CMD_CUSTOMLIB_OFFSET_PRESSURE_THD_HIGH; + ret = ts->sec_ts_write(ts, SEC_TS_CMD_CUSTOMLIB_READ_PARAM, addr, 2); + if (ret < 0) + goto out_set_user_level; + + ret = ts->sec_ts_read(ts, SEC_TS_CMD_CUSTOMLIB_READ_PARAM, data, 1); + if (ret < 0) + goto out_set_user_level; + + input_info(true, &ts->client->dev, "%s: HIGH THD: %d\n", + __func__, data[0]); + + addr[0] = SEC_TS_CMD_CUSTOMLIB_OFFSET_PRESSURE_THD_LOW; + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_CUSTOMLIB_READ_PARAM, addr, 2); + if (ret < 0) + goto out_set_user_level; + + ret = ts->sec_ts_read(ts, SEC_TS_CMD_CUSTOMLIB_READ_PARAM, data, 1); + if (ret < 0) + goto out_set_user_level; + + input_info(true, &ts->client->dev, "%s: LOW THD: %d\n", + __func__, 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; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +out_set_user_level: + snprintf(buff, sizeof(buff), "%s", "NG"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void get_pressure_user_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 addr[3] = { 0 }; + char data[2] = { 0 }; + int ret; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + snprintf(buff, sizeof(buff), "%d", ts->pressure_user_level); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + + addr[0] = SEC_TS_CMD_CUSTOMLIB_OFFSET_PRESSURE_LEVEL; + addr[1] = 0x00; + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_CUSTOMLIB_READ_PARAM, addr, 2); + if (ret < 0) + goto out_get_user_level; + + ret = ts->sec_ts_read(ts, SEC_TS_CMD_CUSTOMLIB_READ_PARAM, data, 1); + if (ret < 0) + goto out_get_user_level; + + input_err(true, &ts->client->dev, "%s: set user level: %d\n", + __func__, data[0]); + ts->pressure_user_level = 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; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + +out_get_user_level: + snprintf(buff, sizeof(buff), "%s", "NG"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} +#endif + +static void set_lowpower_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 }; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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; + } + +/* set lowpower mode by spay, edge_swipe function. + * ts->lowpower_mode = sec->cmd_param[0]; + **/ + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + + sec_cmd_set_cmd_exit(sec); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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_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); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + 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); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + 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); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void set_aod_rect(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 data[10] = {0x02, 0}; + int ret, i; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + input_info(true, &ts->client->dev, "%s: w:%d, h:%d, x:%d, y:%d\n", + __func__, sec->cmd_param[0], sec->cmd_param[1], + sec->cmd_param[2], sec->cmd_param[3]); + + for (i = 0; i < 4; i++) { + data[i * 2 + 2] = sec->cmd_param[i] & 0xFF; + data[i * 2 + 3] = (sec->cmd_param[i] >> 8) & 0xFF; + ts->rect_data[i] = sec->cmd_param[i]; + } + + if (ts->use_customlib) { + disable_irq(ts->client->irq); + ret = ts->sec_ts_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__); + goto NG; + } + + ret = ts->sec_ts_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__); + goto NG; + } + enable_irq(ts->client->irq); + } + + 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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; +NG: + enable_irq(ts->client->irq); + 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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + + +static void get_aod_rect(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 data[8] = {0x02, 0}; + u16 rect_data[4] = {0, }; + int ret, i; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + if (ts->use_customlib) { + disable_irq(ts->client->irq); + ret = ts->sec_ts_read_customlib(ts, data, 8); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: Failed to read rect\n", __func__); + goto NG; + } + enable_irq(ts->client->irq); + } + + for (i = 0; i < 4; i++) + rect_data[i] = (data[i * 2 + 1] & 0xFF) << 8 | + (data[i * 2] & 0xFF); + + input_info(true, &ts->client->dev, "%s: w:%d, h:%d, x:%d, y:%d\n", + __func__, + rect_data[0], rect_data[1], rect_data[2], rect_data[3]); + + 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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; +NG: + enable_irq(ts->client->irq); + 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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + 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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +/* + * 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_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_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_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_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_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; */ + u8 tPara[2] = { 0 }; + int ret; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + memset(buff, 0, sizeof(buff)); + + mutex_lock(&ts->device_mutex); + + tPara[0] = sec->cmd_param[0] & 0xFF; + tPara[1] = (sec->cmd_param[0] >> 8) & 0xFF; + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_DEADZONE_RANGE, tPara, 2); + if (ret < 0) + goto err_grip_data; +/* + * 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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + 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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +/* + * 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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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; + } + + 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_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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + 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); + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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_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); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +static void force_touch_active(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); + int active, ret; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { + sec_cmd_set_cmd_result(sec, "NG", 2); + sec_cmd_set_cmd_exit(sec); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + active = sec->cmd_param[0]; + input_info(true, &ts->client->dev, + "%s: %s\n", __func__, active ? "enable" : "disable"); + if (active) + pm_stay_awake(&ts->client->dev); + else + pm_relax(&ts->client->dev); + + ret = sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_FORCE_ACTIVE, active); + if (ret == 0) { + sec_cmd_set_cmd_result(sec, "OK", 2); + sec->cmd_state = SEC_CMD_STATUS_OK; + } else { + input_info(true, &ts->client->dev, + "%s: failed! ret %d\n", __func__, ret); + sec_cmd_set_cmd_result(sec, "NG", 2); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } + sec_cmd_set_cmd_exit(sec); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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_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); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + 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; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; + } + + ret = ts->sec_ts_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_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_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_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; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + return; +err: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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->temp = 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 set_print_format(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->print_format = !!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, strlen(buff)); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + sec_cmd_set_cmd_exit(sec); +} + +static void set_touch_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 = 0; + u8 para[4] = { 0 }; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + sec_cmd_set_default_result(sec); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: POWER off!\n", __func__); + goto err_out; + } + + switch (sec->cmd_param[0]) { + case 1: + input_info(true, &ts->client->dev, + "%s: param = %d, set Normal ACTIVE mode\n", + __func__, sec->cmd_param[0]); + sec_ts_fix_tmode(ts, + TOUCH_SYSTEM_MODE_TOUCH, TOUCH_MODE_STATE_TOUCH); + break; + case 2: + input_info(true, &ts->client->dev, + "%s: param = %d, set Normal IDLE mode\n", + __func__, sec->cmd_param[0]); + sec_ts_fix_tmode(ts, + TOUCH_SYSTEM_MODE_TOUCH, TOUCH_MODE_STATE_IDLE); + break; + case 3: + input_info(true, &ts->client->dev, + "%s: param = %d, set Lowpower ACTIVE mode\n", + __func__, sec->cmd_param[0]); + sec_ts_fix_tmode(ts, + TOUCH_SYSTEM_MODE_LOWPOWER, TOUCH_MODE_STATE_TOUCH); + break; + case 4: + input_info(true, &ts->client->dev, + "%s: param = %d, set Lowpower IDLE mode\n", + __func__, sec->cmd_param[0]); + sec_ts_fix_tmode(ts, + TOUCH_SYSTEM_MODE_LOWPOWER, TOUCH_MODE_STATE_IDLE); + break; + case 5: + input_info(true, &ts->client->dev, + "%s: param = %d, Sense On\n", + __func__, sec->cmd_param[0]); + ret = ts->sec_ts_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); + break; + case 6: + input_info(true, &ts->client->dev, + "%s: param = %d, Sense Off\n", + __func__, sec->cmd_param[0]); + sec_ts_fix_tmode(ts, TOUCH_SYSTEM_MODE_SLEEP, + TOUCH_MODE_STATE_STOP); + break; + case 7: + input_info(true, &ts->client->dev, + "%s: param = %d, do touch system reset\n", + __func__, sec->cmd_param[0]); + sec_ts_system_reset(ts); + break; + case 8: + input_info(true, &ts->client->dev, + "%s: Toggle Sense On/Off\n", + __func__, sec->cmd_param[0]); + ret = ts->sec_ts_read(ts, SEC_TS_READ_TS_STATUS, para, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read status(%d)\n", __func__, + ret); + goto err_out; + } + + if (para[1] == 6) {// have to sense on + input_info(true, &ts->client->dev, + "%s: param = %d, Sense On\n", + __func__, sec->cmd_param[0]); + ret = ts->sec_ts_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__); + } else {// have to sense off + input_info(true, &ts->client->dev, + "%s: param = %d, Sense Off\n", + __func__, sec->cmd_param[0]); + sec_ts_fix_tmode(ts, 0x6, 0x1); + } + + break; + default: + input_info(true, &ts->client->dev, + "%s: param error! param = %d\n", + __func__, sec->cmd_param[0]); + goto err_out; + } + + 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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); + + return; + +err_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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +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: FTS 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_remove_link(&ts->sec.fac_dev->kobj, "input"); + + sysfs_remove_group(&ts->sec.fac_dev->kobj, + &cmd_attr_group); + + sec_cmd_exit(&ts->sec, SEC_CLASS_DEVT_TSP); + +} + +int sec_ts_run_rawdata_type(struct sec_ts_data *ts, struct sec_cmd_data *sec) +{ + short min[REGION_TYPE_COUNT], max[REGION_TYPE_COUNT]; + enum spec_check_type spec_check = SPEC_NO_CHECK; + int i, ii, jj; + int ret = -1; + u8 data_type = 0; + u8 touch_type = sec->cmd_param[1]; + u8 read_type[9] = {TYPE_RAW_DATA, TYPE_AMBIENT_DATA, + TYPE_DECODED_DATA, TYPE_REMV_AMB_DATA, + TYPE_SIGNAL_DATA, TYPE_OFFSET_DATA_SEC, TYPE_OFFSET_DATA_SDC, + TYPE_NOI_P2P_MIN, TYPE_NOI_P2P_MAX}; + const unsigned int buff_size = ts->tx_count * ts->rx_count * + CMD_RESULT_WORD_LEN; + unsigned int buff_len = 0; + char *buff; + char para = TO_TOUCH_MODE; + +#ifdef USE_PRESSURE_SENSOR + short pressure[3] = { 0 }; + u8 cal_data[18] = { 0 }; +#endif + + buff = kzalloc(buff_size, GFP_KERNEL); + if (!buff) + goto error_alloc_mem; + + for (i = 0; i < 9; i++) { + if (read_type[i] == sec->cmd_param[0]) + break; + } + if (i == 9) { + input_err(true, &ts->client->dev, "%s: invalid data type\n", + __func__); + goto out; + } + + ts->tsp_dump_lock = 1; + input_info(true, &ts->client->dev, + "%s: start (wet:%d)##\n", + __func__, ts->wet_mode); + + + if (sec->cmd_param[0] == TYPE_OFFSET_DATA_SDC) + data_type = TYPE_OFFSET_DATA_SDC_NOT_SAVE; + else + data_type = (u8)sec->cmd_param[0]; + + if (data_type == TYPE_NOI_P2P_MIN + || data_type == TYPE_NOI_P2P_MAX) { + disable_irq(ts->client->irq); + + ret = execute_p2ptest(ts); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: P2P test failed\n", + __func__); + } + + ret = ts->sec_ts_write(ts, SEC_TS_CMD_SET_POWER_MODE, ¶, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: Set powermode failed\n", __func__); + enable_irq(ts->client->irq); + goto out; + } + + enable_irq(ts->client->irq); + } else { + 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 (touch_type == 0) { + ret = sec_ts_read_frame(ts, data_type, min, max, + &spec_check); + + if (ret < 0) + input_info(true, &ts->client->dev, + "%s: mutual %d : error ## ret:%d\n", + __func__, sec->cmd_param[0], ret); + else + input_info(true, &ts->client->dev, + "%s: mutual %d : Max/Min %d,%d ##\n", + __func__, sec->cmd_param[0], + max[0], min[0]); + + sec_ts_delay(20); + + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, "\n"); + if (!ts->print_format) { + for (ii = 0; ii < (ts->rx_count * ts->tx_count); ii++) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "%3d,", ts->pFrame[ii]); + if (ii % ts->tx_count == (ts->tx_count - 1)) + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "\n"); + } + } else { + for (ii = 0; ii < ts->tx_count; ii++) { + for (jj = 0; jj < ts->rx_count; jj++) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "%3d,", + ts->pFrame[(jj * ts->tx_count) + + ii]); + } + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "\n"); + } + } + } else if (touch_type > 0) { + ret = sec_ts_read_channel(ts, data_type, min, + max, &spec_check); + if (ret < 0) + input_info(true, &ts->client->dev, + "%s: self %d : error ## ret:%d\n", + __func__, sec->cmd_param[0], ret); + else + input_info(true, &ts->client->dev, + "%s: self %d : Max/Min %d,%d ##\n", + __func__, sec->cmd_param[0], max[0], + min[0]); + + sec_ts_delay(20); + + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, "\n "); + if (!ts->print_format) { + for (ii = 0; ii < (ts->rx_count + ts->tx_count); ii++) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "%3d,", ts->pFrame[ii]); + if (ii >= ts->tx_count - 1) + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "\n"); + } + } else { + for (ii = ts->tx_count; + ii < (ts->rx_count + ts->tx_count); ii++) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "%3d,", ts->pFrame[ii]); + } + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, "\n"); + for (ii = 0; ii < ts->tx_count; ii++) { + buff_len += scnprintf(buff + buff_len, + buff_size - buff_len, + "%3d,\n", ts->pFrame[ii]); + } + } + } + +#ifdef USE_PRESSURE_SENSOR + 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; + } + + /* run pressure offset data read */ + read_pressure_data(ts, TYPE_OFFSET_DATA_SEC, pressure); + sec_ts_delay(20); + + /* run pressure rawdata read */ + read_pressure_data(ts, TYPE_RAW_DATA, pressure); + sec_ts_delay(20); + + /* run pressure raw delta read */ + read_pressure_data(ts, TYPE_REMV_AMB_DATA, pressure); + sec_ts_delay(20); + + /* run pressure sigdata read */ + read_pressure_data(ts, TYPE_SIGNAL_DATA, pressure); + sec_ts_delay(20); + + ret = ts->sec_ts_read(ts, SEC_TS_CMD_SET_GET_PRESSURE, cal_data, + 18); + ts->pressure_left = ((cal_data[16] << 8) | cal_data[17]); + ts->pressure_center = ((cal_data[8] << 8) | cal_data[9]); + ts->pressure_right = ((cal_data[0] << 8) | cal_data[1]); + input_info(true, &ts->client->dev, "%s: pressure cal data - Left: %d, Center: %d, Right: %d\n", + __func__, ts->pressure_left, ts->pressure_center, + ts->pressure_right); +#endif + sec_ts_release_tmode(ts); + + 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; + } + + sec_ts_read_gain_table(ts); + + sec_ts_release_tmode(ts); +out: + input_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_info(true, &ts->client->dev, "%s: done (wet:%d)##\n", + __func__, ts->wet_mode); + ts->tsp_dump_lock = 0; + + sec_ts_locked_release_all_finger(ts); + + sec_cmd_set_cmd_result(sec, buff, buff_len); + kfree(buff); + + return ret; + +error_alloc_mem: + sec_cmd_set_cmd_result(sec, "FAIL", 4); + return ret; +} + +static void run_rawdata_read_type(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 = -1; + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + sec_cmd_set_default_result(sec); + + if (ts->tsp_dump_lock == 1) { + input_err(true, &ts->client->dev, "%s: already checking now\n", + __func__); + scnprintf(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__); + scnprintf(buff, sizeof(buff), "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out; + } + + ret = sec_ts_run_rawdata_type(ts, sec); + if (ret < 0) { + scnprintf(buff, sizeof(buff), "NA"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + scnprintf(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); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} + +/* + * 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[REGION_TYPE_COUNT], max[REGION_TYPE_COUNT]; + enum spec_check_type spec_check = SPEC_NO_CHECK; + int ret, i, read_num; + u8 test_type[5] = {TYPE_AMBIENT_DATA, TYPE_DECODED_DATA, + TYPE_SIGNAL_DATA, TYPE_OFFSET_DATA_SEC, TYPE_OFFSET_DATA_SDC}; +#ifdef USE_PRESSURE_SENSOR + short pressure[3] = { 0 }; + u8 cal_data[18] = { 0 }; +#endif + + ts->tsp_dump_lock = 1; + input_info(true, &ts->client->dev, + "%s: start (wet:%d)##\n", + __func__, 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, + &spec_check); + if (ret < 0) + input_info(true, &ts->client->dev, + "%s: mutual %d : error ## ret:%d\n", + __func__, test_type[i], ret); + else + input_info(true, &ts->client->dev, + "%s: mutual %d : Max/Min %d,%d ##\n", + __func__, test_type[i], max[0], min[0]); + sec_ts_delay(20); + + if (full_read) { + ret = sec_ts_read_channel(ts, test_type[i], min, + max, &spec_check); + if (ret < 0) + input_info(true, &ts->client->dev, + "%s: self %d : error ## ret:%d\n", + __func__, test_type[i], ret); + else + input_info(true, &ts->client->dev, + "%s: self %d : Max/Min %d,%d ##\n", + __func__, test_type[i], max[0], + min[0]); + sec_ts_delay(20); + } + } + +#ifdef USE_PRESSURE_SENSOR + 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; + } + + /* run pressure offset data read */ + read_pressure_data(ts, TYPE_OFFSET_DATA_SEC, pressure); + sec_ts_delay(20); + + /* run pressure rawdata read */ + read_pressure_data(ts, TYPE_RAW_DATA, pressure); + sec_ts_delay(20); + + /* run pressure raw delta read */ + read_pressure_data(ts, TYPE_REMV_AMB_DATA, pressure); + sec_ts_delay(20); + + /* run pressure sigdata read */ + read_pressure_data(ts, TYPE_SIGNAL_DATA, pressure); + sec_ts_delay(20); + + ret = ts->sec_ts_read(ts, SEC_TS_CMD_SET_GET_PRESSURE, cal_data, + 18); + ts->pressure_left = ((cal_data[16] << 8) | cal_data[17]); + ts->pressure_center = ((cal_data[8] << 8) | cal_data[9]); + ts->pressure_right = ((cal_data[0] << 8) | cal_data[1]); + input_info(true, &ts->client->dev, "%s: pressure cal data - Left: %d, Center: %d, Right: %d\n", + __func__, ts->pressure_left, ts->pressure_center, + ts->pressure_right); +#endif + sec_ts_release_tmode(ts); + + 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; + } + + sec_ts_read_gain_table(ts); + + sec_ts_release_tmode(ts); +out: + input_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_info(true, &ts->client->dev, "%s: done (wet:%d)##\n", + __func__, 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_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, true); + + 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); + + sec_ts_set_bus_ref(ts, SEC_TS_BUS_REF_SYSFS, false); +} diff --git a/sec_ts_fw.c b/sec_ts_fw.c new file mode 100644 index 0000000..fa1ae58 --- /dev/null +++ b/sec_ts_fw.c @@ -0,0 +1,1601 @@ +/* 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_ENABLE_FW_VERIFY 1 +#define SEC_TS_FW_BLK_SIZE 256 + +enum { + BUILT_IN = 0, + UMS, + BL, + FFU, +}; + +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; + +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_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_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_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; +} + +int sec_ts_hw_reset(struct sec_ts_data *ts) +{ + int reset_gpio = ts->plat_data->reset_gpio; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + if (!gpio_is_valid(reset_gpio)) { + input_err(true, &ts->client->dev, "%s: invalid gpio %d\n", + __func__, reset_gpio); + return -EINVAL; + } + + gpio_set_value(reset_gpio, 0); + sec_ts_delay(10); + gpio_set_value(reset_gpio, 1); + /* wait 70 ms at least from bootloader to applicateion mode */ + sec_ts_delay(70); + + return 0; +} + +int sec_ts_sw_reset(struct sec_ts_data *ts) +{ + int ret; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + ret = ts->sec_ts_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; + } + + /* wait 70 ms at least from bootloader to applicateion mode */ + sec_ts_delay(70); + + 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_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; +} + +int sec_ts_system_reset(struct sec_ts_data *ts) +{ + int ret = -1; + + reinit_completion(&ts->boot_completed); + ret = ts->sec_ts_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__); + else { + /* wait 70 ms at least from bootloader to applicateion mode */ + sec_ts_delay(70); + if (completion_done(&ts->bus_resumed) && + ts->probe_done == true) { + if (!completion_done(&ts->boot_completed) && + wait_for_completion_timeout(&ts->boot_completed, + msecs_to_jiffies(200) == 0)) + ret = -ETIME; + } else + /* Normally it should not happen with any retry. + * But, if happened, retry less time to wait ack + */ + ret = sec_ts_wait_for_ready_with_count(ts, + SEC_TS_ACK_BOOT_COMPLETE, 10); + + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: sw_reset time out!\n", __func__); + else + input_info(true, + &ts->client->dev, "%s: sw_reset done\n", + __func__); + } + + if (ret < 0) { + if (!gpio_is_valid(ts->plat_data->reset_gpio)) { + input_err(true, &ts->client->dev, + "%s: reset gpio is unavailable!\n", __func__); + goto err_system_reset; + } + + input_err(true, &ts->client->dev, + "%s: sw_reset failed or time out, try hw_reset to recover!\n", + __func__); + ret = sec_ts_hw_reset(ts); + if (ret) { + input_err(true, &ts->client->dev, + "%s: hw_reset failed\n", __func__); + goto err_system_reset; + } + + if (completion_done(&ts->bus_resumed) && + ts->probe_done == true) { + if (!completion_done(&ts->boot_completed) && + wait_for_completion_timeout(&ts->boot_completed, + msecs_to_jiffies(200) == 0)) + ret = -ETIME; + } else + ret = sec_ts_wait_for_ready_with_count(ts, + SEC_TS_ACK_BOOT_COMPLETE, 10); + + if (ret < 0) { + input_err(true, + &ts->client->dev, "%s: hw_reset time out\n", + __func__); + goto err_system_reset; + } + input_info(true, &ts->client->dev, "%s: hw_reset done\n", + __func__); + } + + /* Sense_on */ + ret = ts->sec_ts_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__); + goto err_system_reset; + } + + return 0; + +err_system_reset: + + complete_all(&ts->boot_completed); + 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_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_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_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; +} + +static 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_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]) { + input_err(true, &ts->client->dev, + "%s: do not matched version info\n", __func__); + return 0; + } + } + + for (i = 2; i < 4; i++) { + if (ts->plat_data->img_version_of_ic[i] != + ts->plat_data->img_version_of_bin[i]) + 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_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_write_burst(ts, tCmd, 1 + 2 + flash_page_size + 1); + return ret; +} + +static bool 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->io_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_write_burst_heap(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); + + /* end condition (page_idx >= 0) page_idx type unsigned int + **/ + if (page_idx == 0) + break; + } + + return mem_size; +err: + return -EIO; +} + +#if SEC_TS_ENABLE_FW_VERIFY +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_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_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_read_heap(ts, cmd[0], data, mem_size); + /* need to wait 500us for reading next flash area */ + udelay(500); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: memory read failed\n", + __func__); + return -EIO; + } +/* + * ret = ts->sec_ts_write(ts, cmd[0], NULL, 0); + * ret = ts->sec_ts_read_bulk_heap(ts, data, mem_size); + **/ + 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; +} +#endif + +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, write_size %d != fw_size %d\n", + __func__, write_size, fw_size); + 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_ENABLE_FW_VERIFY + 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]) { + input_info(true, &ts->client->dev, + "%s: data = %X, mem_rb = %X, ii = %d\n", + __func__, data[ii], mem_rb[ii], ii); + break; + } + } + + if (fw_size != ii) { + input_err(true, &ts->client->dev, + "%s: fw verify fail, fw_size %d != ii %d\n", + __func__, fw_size, ii); + ret = -1; + goto out; + } + } else { + ret = -1; + goto out; + } + + input_info(true, &ts->client->dev, "%s: verify done(%d)\n", + __func__, ret); + +out: +#endif + 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]; +#ifdef PAT_CONTROL + char buff[SEC_CMD_STR_LEN] = {0}; + u8 img_ver[4]; + bool magic_cal = false; +#endif + + /* 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); + +#ifdef PAT_CONTROL + if (restore_cal) { + if (ts->plat_data->pat_function == PAT_CONTROL_PAT_MAGIC) { + /* NOT to control cal count that was marked on external + * factory ( E0~E5 ) + **/ + if ((ts->cal_count >= PAT_MAGIC_NUMBER) && + (ts->cal_count < PAT_MAX_MAGIC)) + magic_cal = true; + } + } + input_info(true, &ts->client->dev, + "%s: cal_count(0x%02X) pat_function dt(%d) restore_cal(%d) magic_cal(%d)\n", + __func__, ts->cal_count, ts->plat_data->pat_function, + restore_cal, magic_cal); +#endif + + if (!bl_update) { +#ifdef PAT_CONTROL + if ((ts->cal_count == 0) || (ts->cal_count == 0xFF) || + (magic_cal == true)) { + input_err(true, &ts->client->dev, + "%s: RUN OFFSET CALIBRATION(0x%02X)\n", + __func__, ts->cal_count); + + ret = sec_ts_execute_force_calibration(ts, + OFFSET_CAL_SEC); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: fail to write OFFSET CAL SEC!\n", + __func__); + +#ifdef USE_PRESSURE_SENSOR + ret = sec_ts_execute_force_calibration(ts, + PRESSURE_CAL); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: fail to write PRESSURE CAL!\n", + __func__); +#endif + if (ret >= 0 && magic_cal) { + + ts->cal_count = get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_CAL_COUNT); + if (ts->cal_count == 0x00 || + ts->cal_count == 0xFF) + ts->cal_count = PAT_MAGIC_NUMBER; + else if (ts->cal_count >= PAT_MAGIC_NUMBER && + ts->cal_count < PAT_MAX_MAGIC) + ts->cal_count++; + + /* Use TSP NV area : in this model, use only + * one byte + * buff[0] : offset from user NVM storage + * buff[1] : length of stored data - 1 (ex. + * using 1byte, value is 1 - 1 = 0) + * buff[2] : write data + **/ + buff[0] = SEC_TS_NVM_OFFSET_CAL_COUNT; + buff[1] = 0; + buff[2] = ts->cal_count; + + ret = ts->sec_ts_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); + ts->cal_count = get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_CAL_COUNT); + input_info(true, &ts->client->dev, + "%s: cal_count = [%02X]\n", + __func__, ts->cal_count); + } + + ret = ts->sec_ts_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__); + } else { + memset(buff, 0x00, SEC_CMD_STR_LEN); + buff[0] = get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_TUNE_VERSION); + if (buff[0] == 0xFF) { + set_tsp_nvm_data_clear(ts, + SEC_TS_NVM_OFFSET_TUNE_VERSION); + set_tsp_nvm_data_clear(ts, + SEC_TS_NVM_OFFSET_TUNE_VERSION + 1); + } + + ts->tune_fix_ver = (img_ver[2] << 8 | + img_ver[3]); + buff[0] = SEC_TS_NVM_OFFSET_TUNE_VERSION; + buff[1] = 1;// 2bytes + buff[2] = img_ver[2]; + buff[3] = img_ver[3]; + ret = ts->sec_ts_write(ts, SEC_TS_CMD_NVM, + buff, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: nvm write failed. ret: %d\n", + __func__, ret); + } + sec_ts_delay(20); + + buff[0] = get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_TUNE_VERSION); + buff[1] = get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_TUNE_VERSION + 1); + ts->tune_fix_ver = buff[0]<<8 | buff[1]; + input_info(true, &ts->client->dev, + "%s: tune_fix_ver [%02X %02X]\n", + __func__, buff[0], buff[1]); + } + } else { + input_err(true, &ts->client->dev, + "%s: DO NOT CALIBRATION(0x%02X)\n", + __func__, ts->cal_count); + } +#else + /* auto-calibration if restore_cal = 0 */ + if (!restore_cal) { + input_err(true, &ts->client->dev, + "%s: RUN OFFSET CALIBRATION\n", + __func__); + + ret = sec_ts_execute_force_calibration(ts, + OFFSET_CAL_SEC); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: fail to write OFFSET CAL SEC!\n", + __func__); + +#ifdef USE_PRESSURE_SENSOR + ret = sec_ts_execute_force_calibration(ts, + PRESSURE_CAL); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: fail to write PRESSURE CAL!\n", + __func__); +#endif + + /* check mis-cal */ + if (ts->plat_data->mis_cal_check) { + u8 buff[2]; + u8 mis_cal_data; + + buff[0] = STATE_MANAGE_OFF; + ret = ts->sec_ts_write(ts, + SEC_TS_CMD_STATEMANAGE_ON, buff, 1); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: mis_cal_check error[1] ret: %d\n", + __func__, ret); + + buff[0] = TOUCH_SYSTEM_MODE_TOUCH; + buff[1] = TOUCH_MODE_STATE_TOUCH; + ret = ts->sec_ts_write(ts, + SEC_TS_CMD_CHG_SYSMODE, buff, 2); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: mis_cal_check error[2] ret: %d\n", + __func__, ret); + + input_info(true, &ts->client->dev, + "%s: mis_cal check\n", __func__); + ret = ts->sec_ts_write(ts, + SEC_TS_CMD_MIS_CAL_CHECK, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: mis_cal_check error[3] ret: %d\n", + __func__, ret); + sec_ts_delay(200); + + ret = ts->sec_ts_read(ts, + SEC_TS_CMD_MIS_CAL_READ, + &mis_cal_data, 1); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: fail!, %d\n", + __func__, ret); + else + input_info(true, &ts->client->dev, + "%s: mis_cal data : %d\n", + __func__, mis_cal_data); + + buff[0] = STATE_MANAGE_ON; + ret = ts->sec_ts_write(ts, + SEC_TS_CMD_STATEMANAGE_ON, buff, 1); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: mis_cal_check error[4] ret: %d\n", + __func__, ret); + } + + /* Update calibration report */ + sec_ts_read_calibration_report(ts); + } else + input_info(true, &ts->client->dev, + "%s: No calibration: restore_cal = %d\n", + __func__, restore_cal); +#endif + + /* Sense_on */ + ret = ts->sec_ts_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_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_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, 0, 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_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_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_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_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; + + if (ts->plat_data->bringup == 1 && ts->is_fw_corrupted == false) { + 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 && ts->is_fw_corrupted == false) { + input_err(true, &ts->client->dev, + "%s: bringup. do not update\n", __func__); + result = 0; + goto err_request_fw; + } + +#ifdef PAT_CONTROL + /* ic fw ver > bin fw ver && force is false + **/ + if ((result <= 0) && (!force_update)) { + /* clear nv, forced f/w update eventhough same f/w, + * then apply pat magic + **/ + if (ts->plat_data->pat_function == PAT_CONTROL_FORCE_UPDATE) { + input_info(true, &ts->client->dev, + "%s: run forced f/w update and excute autotune\n", + __func__); + } else { + input_info(true, &ts->client->dev, + "%s: skip - fw update & nv read\n", + __func__); + goto err_request_fw; + } + } + + ts->cal_count = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_CAL_COUNT); + input_info(true, &ts->client->dev, + "%s: cal_count [%02X]\n", __func__, ts->cal_count); + + /* initialize nv default value from 0xff to 0x00 */ + if (ts->cal_count == 0xFF) { + set_tsp_nvm_data_clear(ts, SEC_TS_NVM_OFFSET_CAL_COUNT); + set_tsp_nvm_data_clear(ts, SEC_TS_NVM_OFFSET_TUNE_VERSION); + set_tsp_nvm_data_clear(ts, SEC_TS_NVM_OFFSET_TUNE_VERSION+1); + input_info(true, &ts->client->dev, + "%s: initialize nv as default value & excute autotune\n", + __func__); + } + + ts->tune_fix_ver = (get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_TUNE_VERSION) << 8) | + get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_TUNE_VERSION + 1); + input_info(true, &ts->client->dev, + "%s: tune_fix_ver [%04X] afe_base [%04X]\n", + __func__, ts->tune_fix_ver, ts->plat_data->afe_base); + + /* check dt to clear pat */ + if (ts->plat_data->pat_function == PAT_CONTROL_CLEAR_NV || + ts->plat_data->pat_function == PAT_CONTROL_FORCE_UPDATE) + set_tsp_nvm_data_clear(ts, SEC_TS_NVM_OFFSET_CAL_COUNT); + ts->cal_count = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_CAL_COUNT); + + /* mismatch calibration - + * ic has too old calibration data after pat enabled + **/ + if (ts->plat_data->afe_base > ts->tune_fix_ver) { + restore_cal = 1; + set_tsp_nvm_data_clear(ts, SEC_TS_NVM_OFFSET_CAL_COUNT); + ts->cal_count = get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_CAL_COUNT); + } + + input_info(true, &ts->client->dev, + "%s: cal_count [%02X]\n", __func__, ts->cal_count); +#else + /* ic firmware version >= binary firmare version + * && forced is FALSE + * ic fw ver > bin fw ver && force is false + **/ + if ((result <= 0) && (!force_update)) { + input_info(true, &ts->client->dev, + "%s: skip fw update\n", __func__); + goto err_request_fw; + } +#endif + input_info(true, &ts->client->dev, "%s: IC config %x %x, Bin config %x %x\n", + __func__, ts->plat_data->config_version_of_ic[2], + ts->plat_data->config_version_of_ic[3], + ts->plat_data->config_version_of_bin[2], + ts->plat_data->config_version_of_bin[3]); + + /*judge auto-k by comparing config version*/ + if (ts->plat_data->config_version_of_ic[2] != + ts->plat_data->config_version_of_bin[2] || + ts->plat_data->config_version_of_ic[3] != + ts->plat_data->config_version_of_bin[3]) + restore_cal = 0; + else + restore_cal = 1; + + 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; +#ifdef PAT_CONTROL + /* change cal_count from 0 to magic number to make virtual + * pure auto tune + **/ + if ((ts->cal_count == 0 + && ts->plat_data->pat_function == PAT_CONTROL_PAT_MAGIC) || + (ts->plat_data->pat_function == PAT_CONTROL_FORCE_UPDATE)) { + set_pat_magic_number(ts); + ts->cal_count = get_tsp_nvm_data(ts, + SEC_TS_NVM_OFFSET_CAL_COUNT); + } +#endif + } + + 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; + + 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, 1, 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; + + 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 = kzalloc(fw_size, GFP_KERNEL); + 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; + /* + * sec_ts_check_firmware_version(ts, fw_data); + **/ + 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, 1, 0) < 0) + 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); + kfree(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, restore_cal = 0; + + 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); + + input_info(true, &ts->client->dev, "%s: IC config %x %x, Bin config %x %x\n", + __func__, ts->plat_data->config_version_of_ic[2], + ts->plat_data->config_version_of_ic[3], + ts->plat_data->config_version_of_bin[2], + ts->plat_data->config_version_of_bin[3]); + + if (ts->plat_data->config_version_of_ic[2] != + ts->plat_data->config_version_of_bin[2] || + ts->plat_data->config_version_of_ic[3] != + ts->plat_data->config_version_of_bin[3]) + restore_cal = 0; + else + restore_cal = 1; + + if (sec_ts_firmware_update(ts, fw_entry->data, + fw_entry->size, 0, restore_cal, 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); +#endif + + return ret; +} +EXPORT_SYMBOL(sec_ts_firmware_update_on_hidden_menu); + diff --git a/sec_ts_only_vendor.c b/sec_ts_only_vendor.c new file mode 100644 index 0000000..d4a740b --- /dev/null +++ b/sec_ts_only_vendor.c @@ -0,0 +1,544 @@ +/* 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <linux/regulator/consumer.h> +#include <linux/irq.h> +#include <linux/of_gpio.h> +#include <linux/time.h> +#include <linux/vmalloc.h> + +#include <linux/uaccess.h> +/*#include <asm/gpio.h>*/ + +#include "sec_ts.h" + +u8 lv1cmd; +u8 *read_lv1_buff; +static int lv1_readsize; +static int lv1_readremain; +static int lv1_readoffset; + +u8 lv1cmd_manual; +static int lv1_readsize_manual; +static int lv1_readremain_manual; +static int lv1_readoffset_manual; + +#define SEC_TS_CMD_BUF_SZ 64 +static u8 cmd_buf[SEC_TS_CMD_BUF_SZ]; +static int cmd_buf_num; + +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 ssize_t sec_ts_reg_manual_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_ts_regreadsize_manual_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_ts_regread_manual_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static DEVICE_ATTR_WO(sec_ts_reg); +static DEVICE_ATTR_WO(sec_ts_regreadsize); +static DEVICE_ATTR_WO(sec_ts_enter_recovery); +static DEVICE_ATTR_RO(sec_ts_regread); +static DEVICE_ATTR_RO(sec_ts_gesture_status); + +static DEVICE_ATTR_WO(sec_ts_reg_manual); +static DEVICE_ATTR_WO(sec_ts_regreadsize_manual); +static DEVICE_ATTR_RO(sec_ts_regread_manual); + +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, + &dev_attr_sec_ts_reg_manual.attr, + &dev_attr_sec_ts_regreadsize_manual.attr, + &dev_attr_sec_ts_regread_manual.attr, + NULL, +}; + +static struct attribute_group cmd_attr_group = { + .attrs = cmd_attributes, +}; + +/* for debugging-------------------------------------------------------------*/ +static void sec_ts_parsing_cmds(struct device *dev, + const char *buf, size_t size, bool write) +{ + u8 result, n = 0; + char *p, *temp_buf, *token; + size_t token_len = 0; + struct sec_ts_data *ts = dev_get_drvdata(dev); + + /* clear cmd_buf */ + memset(cmd_buf, 0, sizeof(cmd_buf)); + cmd_buf_num = 0; + + /* pre-check input */ + if (write) { + if (size < 2) { + input_info(true, &ts->client->dev, + "%s: invalid input size %d\n", + __func__); + return; + } + } else { + if (size < 3) { + input_info(true, &ts->client->dev, + "%s: invalid input size %d\n", + __func__); + return; + } else if (buf[0] != 'R' && buf[0] != 'r') { + input_info(true, &ts->client->dev, + "%s: invalid input %c, Have to start with R(r)\n", + __func__, buf[0]); + return; + } + } + + /* alloc temp_buf for parsing + * read case will skip 1st character + */ + temp_buf = kstrdup(buf, GFP_KERNEL); + if (!temp_buf) { + pr_err("%s: memory allocation failed!", + __func__); + return; + } + p = temp_buf; + + /* newline case at last char */ + if (p[size - 1] == '\n') + p[size - 1] = '\0'; + + /* skip 1st character for read case */ + if (!write) + p++; + + /* parsing */ + while (p && (n < SEC_TS_CMD_BUF_SZ)) { + + while (isspace(*p)) + p++; + + token = strsep(&p, " "); + if (!token || *token == '\0') + break; + + token_len = strlen(token); + if (token_len != 2) { + pr_err("%s: bad len %zu\n", __func__, token_len); + n = 0; + break; + } + + if (kstrtou8(token, 16, &result)) { + /* Conversion failed due to bad input. + * Discard the entire buffer. + */ + pr_err("%s: bad input\n", __func__); + n = 0; + break; + } + /* found a valid cmd/args */ + cmd_buf[n] = result; + n++; + } + kfree(temp_buf); + cmd_buf_num = n; +} + +/* sysfs file node to write reg + * + * echo _REG_ _VAL_ ... > sec_ts_reg_manual + * + * e.g. write reg 0xD7 with 0x02 0x04 + * echo D7 02 04 > sec_ts_reg_manual + */ +static ssize_t sec_ts_reg_manual_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; + } + + sec_ts_parsing_cmds(dev, buf, size, true); + + if (cmd_buf_num) { + ts->sec_ts_write_burst(ts, cmd_buf, cmd_buf_num); + + input_info(true, &ts->client->dev, + "%s: size %d, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", + __func__, cmd_buf_num, cmd_buf[0], cmd_buf[1], + cmd_buf[2], cmd_buf[3], cmd_buf[4]); + } + + return size; +} + +/* sysfs file node to read reg + * + * step 1: set reg and size that want to read + * echo R_REG_ _SIZE_ > sec_ts_regreadsize_manual + * + * e.g. read reg 0x52 for 3 bytes + * echo R52 03 > sec_ts_regreadsize_manual + * + * step 2: read reg + * cat sec_ts_regread_manual + */ +static ssize_t sec_ts_regreadsize_manual_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + sec_ts_parsing_cmds(dev, buf, size, false); + + if (cmd_buf_num == 2) { + lv1cmd_manual = cmd_buf[0]; + lv1_readsize_manual = cmd_buf[1]; + lv1_readoffset_manual = 0; + lv1_readremain_manual = 0; + input_info(true, &ts->client->dev, + "%s: read reg %X sz %d\n", + __func__, lv1cmd_manual, lv1_readsize_manual); + } else + input_info(true, &ts->client->dev, + "%s: invalid input to reg read! cmd_num %d, cmd %x %x\n", + __func__, cmd_buf_num, cmd_buf[0], cmd_buf[1]); + + return size; +} + + +/* sysfs file node to read reg + * check sec_ts_regreadsize_manual_store() above for details. + */ +static ssize_t sec_ts_regread_manual_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 *read_lv1_buff_manual; + struct sec_ts_data *ts = dev_get_drvdata(dev); + unsigned int str_len = 0; + int ret = 0, i; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, + "%s: Power off state\n", __func__); + return -EIO; + } + if (lv1_readsize_manual < 1) { + input_err(true, &ts->client->dev, + "%s: Nothing to read\n", __func__); + return -EIO; + } + + disable_irq(ts->client->irq); + + read_lv1_buff_manual = kzalloc(lv1_readsize_manual, GFP_KERNEL); + if (!read_lv1_buff_manual) + goto malloc_err; + + ret = ts->sec_ts_read_heap(ts, lv1cmd_manual, + read_lv1_buff_manual, lv1_readsize_manual); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: read reg %x failed!\n", + __func__, lv1cmd_manual); + else { + for (i = 0 ; i < lv1_readsize_manual ; i++) + str_len += scnprintf(buf + str_len, + PAGE_SIZE - str_len, + "%02X ", + (u8)read_lv1_buff_manual[i]); + str_len += scnprintf(buf + str_len, PAGE_SIZE - str_len, "\n"); + input_info(true, &ts->client->dev, "%s: reg %X sz %d -> %s\n", + __func__, lv1cmd_manual, lv1_readsize_manual, buf); + } + + kfree(read_lv1_buff_manual); + +malloc_err: + lv1_readremain_manual = 0; + enable_irq(ts->client->irq); + + return str_len; +} + +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_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; + + 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; + + ret = ts->sec_ts_read_heap(ts, lv1cmd, read_lv1_buff, lv1_readsize); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read %x command fail\n", + __func__, lv1cmd); + goto i2c_err; + } + + 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_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; + +#ifdef CONFIG_SEC_SYSFS + ts->dev = sec_device_create(ts, "sec_ts"); +#else + ts->dev = device_create(sec_class, NULL, 0, ts, "sec_ts"); +#endif + 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; +} + +void sec_ts_raw_device_exit(struct sec_ts_data *ts) +{ + sysfs_remove_group(&ts->dev->kobj, &cmd_attr_group); +#ifdef CONFIG_SEC_SYSFS + sec_device_destroy(ts->dev->devt); +#else + device_destroy(sec_class, 0); +#endif +} + |