summaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen/ist30xxa/ist30xx_cmcs.c
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/input/touchscreen/ist30xxa/ist30xx_cmcs.c
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/input/touchscreen/ist30xxa/ist30xx_cmcs.c')
-rw-r--r--drivers/input/touchscreen/ist30xxa/ist30xx_cmcs.c1345
1 files changed, 1345 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/ist30xxa/ist30xx_cmcs.c b/drivers/input/touchscreen/ist30xxa/ist30xx_cmcs.c
new file mode 100644
index 00000000..0b39b20f
--- /dev/null
+++ b/drivers/input/touchscreen/ist30xxa/ist30xx_cmcs.c
@@ -0,0 +1,1345 @@
+/*
+ * Copyright (C) 2010,Imagis Technology Co. Ltd. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/stat.h>
+#include <linux/uaccess.h>
+#include <linux/fcntl.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+
+#include "ist30xx.h"
+#include "ist30xx_update.h"
+#include "ist30xx_misc.h"
+#include "ist30xx_cmcs.h"
+
+#if IST30XX_INTERNAL_CMCS_BIN
+#include "ist30xx_cmcs_bin.h"
+#endif
+
+#define CMCS_PARSING_DEBUG (0)
+
+#define CMCS_READY (0)
+#define CMCS_NOT_READY (-1)
+
+#define TSP_CH_UNUSED (0)
+#define TSP_CH_SCREEN (1)
+#define TSP_CH_KEY (2)
+#define TSP_CH_UNKNOWN (-1)
+
+extern struct ist30xx_data *ts_data;
+
+int cmcs_ready = CMCS_READY;
+u8 *ts_cmcs_bin = NULL;
+u32 ts_cmcs_bin_size = 0;
+CMCS_BIN_INFO ist30xx_cmcs_bin;
+CMCS_BIN_INFO *ts_cmcs = (CMCS_BIN_INFO *)&ist30xx_cmcs_bin;
+CMCS_BUF ist30xx_cmcs_buf;
+CMCS_BUF *ts_cmcs_buf = (CMCS_BUF *)&ist30xx_cmcs_buf;
+
+
+int ist30xx_parse_cmcs_bin(const u8 *buf, const u32 size)
+{
+ int ret = -EPERM;
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ memcpy(ts_cmcs->magic1, buf, sizeof(ts_cmcs->magic1));
+ memcpy(ts_cmcs->magic2, &buf[size - sizeof(ts_cmcs->magic2)],
+ sizeof(ts_cmcs->magic2));
+ memcpy(cmcs, &buf[sizeof(ts_cmcs->magic1)], sizeof(ts_cmcs->cmcs));
+
+ if (!strncmp(ts_cmcs->magic1, IST30XX_CMCS_MAGIC, sizeof(ts_cmcs->magic1))
+ && !strncmp(ts_cmcs->magic2, IST30XX_CMCS_MAGIC,
+ sizeof(ts_cmcs->magic2))) {
+ int idx;
+
+ idx = sizeof(ts_cmcs->magic1) + sizeof(ts_cmcs->cmcs);
+ ts_cmcs->buf_cmcs = (u8 *)&buf[idx];
+
+ idx += cmcs->cmd.cmcs_size;
+ ts_cmcs->buf_sensor = (u32 *)&buf[idx];
+
+ idx += (cmcs->sensor1_size + cmcs->sensor2_size + cmcs->sensor3_size);
+ ts_cmcs->buf_node = (u16 *)&buf[idx];
+
+ ret = 0;
+ }
+
+ tsp_verb("Magic1: %s, Magic2: %s\n", ts_cmcs->magic1, ts_cmcs->magic2);
+ tsp_verb(" mode: 0x%x, base(screen: %d, key: %d)\n",
+ cmcs->cmd.mode, cmcs->cmd.base_screen, cmcs->cmd.base_key);
+ tsp_verb(" start_cp (cm: %d, cs: %d), vcmp (cm: %d, cs: %d)\n",
+ cmcs->cmd.start_cp_cm, cmcs->cmd.start_cp_cs,
+ cmcs->cmd.vcmp_cm, cmcs->cmd.vcmp_cs);
+ tsp_verb(" timeout: %d\n", cmcs->timeout);
+ tsp_verb(" baseline scrn: 0x%08x, key: 0x%08x\n",
+ cmcs->addr.base_screen, cmcs->addr.base_key);
+ tsp_verb(" start_cp: 0x%08x, vcmp: 0x%08x\n",
+ cmcs->addr.start_cp, cmcs->addr.vcmp);
+ tsp_verb(" sensor 1: 0x%08x, 2: 0x%08x, 3: 0x%08x\n",
+ cmcs->addr.sensor1, cmcs->addr.sensor2, cmcs->addr.sensor3);
+ tsp_verb(" tx: %d, rx:%d, key rx: %d, num(%d, %d, %d, %d, %d)\n",
+ cmcs->ch.tx_num, cmcs->ch.rx_num, cmcs->ch.key_rx, cmcs->ch.key1,
+ cmcs->ch.key2, cmcs->ch.key3, cmcs->ch.key4, cmcs->ch.key5);
+ tsp_verb(" cr: screen(%4d, %4d), key(%4d, %4d)\n",
+ cmcs->spec_cr.screen_min, cmcs->spec_cr.screen_max,
+ cmcs->spec_cr.key_min, cmcs->spec_cr.key_max);
+ tsp_verb(" cm: screen(%4d, %4d), key(%4d, %4d)\n",
+ cmcs->spec_cm.screen_min, cmcs->spec_cm.screen_max,
+ cmcs->spec_cm.key_min, cmcs->spec_cm.key_max);
+ tsp_verb(" cs0: screen(%4d, %4d), key(%4d, %4d)\n",
+ cmcs->spec_cs0.screen_min, cmcs->spec_cs0.screen_max,
+ cmcs->spec_cs0.key_min, cmcs->spec_cs0.key_max);
+ tsp_verb(" cs1: screen(%4d, %4d), key(%4d, %4d)\n",
+ cmcs->spec_cs1.screen_min, cmcs->spec_cs1.screen_max,
+ cmcs->spec_cs1.key_min, cmcs->spec_cs1.key_max);
+ tsp_verb(" slope - x(%d, %d), y(%d, %d), key(%d, %d)\n",
+ cmcs->slope.x_min, cmcs->slope.x_max,
+ cmcs->slope.y_min, cmcs->slope.y_max,
+ cmcs->slope.key_min, cmcs->slope.key_max);
+ tsp_verb(" size - cmcs(%d), sensor(%d, %d, %d)\n",
+ cmcs->cmd.cmcs_size, cmcs->sensor1_size,
+ cmcs->sensor2_size, cmcs->sensor3_size);
+ tsp_verb(" checksum - cmcs: 0x%08x, sensor: 0x%08x\n",
+ cmcs->cmcs_chksum, cmcs->sensor_chksum);
+ tsp_verb(" cmcs: %x, %x, %x, %x\n", ts_cmcs->buf_cmcs[0],
+ ts_cmcs->buf_cmcs[1], ts_cmcs->buf_cmcs[2], ts_cmcs->buf_cmcs[3]);
+ tsp_verb(" sensor: %x, %x, %x, %x\n",
+ ts_cmcs->buf_sensor[0], ts_cmcs->buf_sensor[1],
+ ts_cmcs->buf_sensor[2], ts_cmcs->buf_sensor[3]);
+ tsp_verb(" node: %x, %x, %x, %x\n",
+ ts_cmcs->buf_node[0], ts_cmcs->buf_node[1],
+ ts_cmcs->buf_node[2], ts_cmcs->buf_node[3]);
+
+ return ret;
+}
+
+int ist30xx_get_cmcs_info(const u8 *buf, const u32 size)
+{
+ int ret;
+
+ cmcs_ready = CMCS_NOT_READY;
+
+ ret = ist30xx_parse_cmcs_bin(buf, size);
+ if (unlikely(ret != TAGS_PARSE_OK))
+ tsp_warn("Cannot find tags of CMCS, make a bin by 'cmcs2bin.exe'\n");
+
+ return ret;
+}
+
+int ist30xx_set_cmcs_sensor(struct i2c_client *client, CMCS_INFO *cmcs,
+ u32 *buf32)
+{
+ int i, ret;
+ int len;
+ u32 waddr;
+ u32 *tmp32;
+
+ tsp_verb("%08x %08x %08x %08x\n", buf32[0], buf32[1], buf32[2], buf32[3]);
+
+ waddr = cmcs->addr.sensor1;
+ len = cmcs->sensor1_size / IST30XX_DATA_LEN;
+
+ for (i = 0; i < len; i++) {
+ ret = ist30xx_write_cmd(client, waddr, *buf32++);
+ if (ret)
+ return ret;
+
+ waddr += IST30XX_DATA_LEN;
+ }
+
+ tmp32 = buf32;
+ tsp_verb("%08x %08x %08x %08x\n", tmp32[0], tmp32[1], tmp32[2], tmp32[3]);
+
+ waddr = cmcs->addr.sensor2;
+ len = (cmcs->sensor2_size - 0x10) / IST30XX_DATA_LEN;
+
+ for (i = 0; i < len; i++) {
+ ret = ist30xx_write_cmd(client, waddr, *buf32++);
+ if (ret)
+ return ret;
+
+ waddr += IST30XX_DATA_LEN;
+ }
+ buf32 += (0x10 / IST30XX_DATA_LEN);
+
+ tmp32 = buf32;
+ tsp_verb("%08x %08x %08x %08x\n", tmp32[0], tmp32[1], tmp32[2], tmp32[3]);
+
+ waddr = cmcs->addr.sensor3;
+ len = cmcs->sensor3_size / IST30XX_DATA_LEN;
+
+ for (i = 0; i < len; i++) {
+ ret = ist30xx_write_cmd(client, waddr, *buf32++);
+ if (ret)
+ return ret;
+
+ waddr += IST30XX_DATA_LEN;
+ }
+
+ return 0;
+}
+
+int ist30xx_set_cmcs_cmd(struct i2c_client *client, CMCS_INFO *cmcs)
+{
+ int ret;
+ u32 val;
+
+ val = (u32)(cmcs->cmd.base_screen | (cmcs->cmd.mode << 16));
+ ret = ist30xx_write_cmd(client, cmcs->addr.base_screen, val);
+ if (ret)
+ return ret;
+ tsp_verb("Baseline screen(0x%08x): 0x%08x\n", cmcs->addr.base_screen, val);
+
+ val = (u32)cmcs->cmd.base_key;
+ ret = ist30xx_write_cmd(client, cmcs->addr.base_key, val);
+ if (ret)
+ return ret;
+ tsp_verb("Baseline key(0x%08x): 0x%08x\n", cmcs->addr.base_key, val);
+
+ val = cmcs->cmd.start_cp_cm | (cmcs->cmd.start_cp_cs << 16);
+ ret = ist30xx_write_cmd(client, cmcs->addr.start_cp, val);
+ if (ret)
+ return ret;
+ tsp_verb("StartCP(0x%08x): 0x%08x\n", cmcs->addr.start_cp, val);
+
+ val = cmcs->cmd.vcmp_cm | (cmcs->cmd.vcmp_cs << 16);
+ ret = ist30xx_write_cmd(client, cmcs->addr.vcmp, val);
+ if (ret)
+ return ret;
+ tsp_verb("VCMP(0x%08x): 0x%08x\n", cmcs->addr.vcmp, val);
+
+ return 0;
+}
+
+int ist30xx_parse_cmcs_buf(CMCS_INFO *cmcs, s16 *buf)
+{
+ int i, j;
+
+ tsp_info(" %d * %d\n", cmcs->ch.tx_num, cmcs->ch.rx_num);
+ for (i = 0; i < cmcs->ch.tx_num; i++) {
+ tsp_info(" ");
+ for (j = 0; j < cmcs->ch.rx_num; j++)
+ printk("%5d ", buf[i * cmcs->ch.rx_num + j]);
+ printk("\n");
+ }
+
+ return 0;
+}
+
+int ist30xx_apply_cmcs_slope(CMCS_INFO *cmcs, CMCS_BUF *cmcs_buf)
+{
+ int i, j;
+ int idx1, idx2;
+ int ch_num = cmcs->ch.tx_num * cmcs->ch.rx_num;
+ int width = cmcs->ch.rx_num;
+ int height = cmcs->ch.tx_num;
+ s16 *pcm = (s16 *)cmcs_buf->cm;
+ s16 *pspec = (s16 *)cmcs_buf->spec;
+ s16 *pslope0 = (s16 *)cmcs_buf->slope0;
+ s16 *pslope1 = (s16 *)cmcs_buf->slope1;
+
+ if (cmcs->ch.key_rx)
+ width -= 1;
+ else
+ height -= 1;
+
+ memset(cmcs_buf->slope0, 0, sizeof(cmcs_buf->slope0));
+ memset(cmcs_buf->slope1, 0, sizeof(cmcs_buf->slope1));
+
+ memcpy(cmcs_buf->spec, ts_cmcs->buf_node, (ch_num * sizeof(u16)));
+
+ idx1 = 0;
+#if CMCS_PARSING_DEBUG
+ tsp_info("# Node specific\n");
+ for (i = 0; i < cmcs->ch.tx_num; i++) {
+ tsp_info(" ");
+ for (j = 0; j < cmcs->ch.rx_num; j++)
+ printk("%5d ", cmcs_buf->spec[idx1++]);
+ printk("\n");
+ }
+#endif
+
+ tsp_verb("# Apply slope0_x\n");
+ for (i = 0; i < height; i++) {
+ for (j = 0; j < width - 1; j++) {
+ idx1 = (i * cmcs->ch.rx_num) + j;
+ idx2 = idx1 + 1;
+
+ pslope0[idx1] = (pcm[idx2] - pcm[idx1]);
+ pslope0[idx1] += (pspec[idx1] - pspec[idx2]);
+ }
+ }
+
+ tsp_verb("# Apply slope1_y\n");
+ for (i = 0; i < height - 1; i++) {
+ for (j = 0; j < width; j++) {
+ idx1 = (i * cmcs->ch.rx_num) + j;
+ idx2 = idx1 + cmcs->ch.rx_num;
+
+ pslope1[idx1] = (pcm[idx2] - pcm[idx1]);
+ pslope1[idx1] += (pspec[idx1] - pspec[idx2]);
+ }
+ }
+
+#if CMCS_PARSING_DEBUG
+ tsp_info("# slope0_x\n");
+ for (i = 0; i < height; i++) {
+ tsp_info(" ");
+ for (j = 0; j < width; j++) {
+ idx1 = (i * cmcs->ch.rx_num) + j;
+ printk("%5d ", pslope0[idx1]);
+ }
+ printk("\n");
+ }
+
+ tsp_info("# slope1_y\n");
+ for (i = 0; i < height; i++) {
+ tsp_info(" ");
+ for (j = 0; j < width; j++) {
+ idx1 = (i * cmcs->ch.rx_num) + j;
+ printk("%5d ", pslope1[idx1]);
+ }
+ printk("\n");
+ }
+#endif
+
+ return 0;
+}
+
+
+int ist30xx_get_cmcs_buf(struct i2c_client *client, CMCS_INFO *cmcs, s16 *buf)
+{
+ int ret;
+ u16 len = (IST30XX_CMCS_BUF_SIZE * sizeof(buf[0])) / IST30XX_DATA_LEN;
+
+ ret = ist30xx_read_buf(client, CMD_DEFAULT, (u32 *)buf, len);
+ if (unlikely(ret))
+ return ret;
+
+#if CMCS_PARSING_DEBUG
+ ret = ist30xx_parse_cmcs_buf(cmcs, buf);
+#endif
+
+ return ret;
+}
+
+
+extern int ist30xx_calib_wait(void);
+#define cmcs_next_step(ret) { if (unlikely(ret)) goto end; msleep(10); }
+int ist30xx_cmcs_test(const u8 *buf, int size)
+{
+ int ret;
+ int len;
+ u32 chksum = 0;
+ u32 *buf32;
+ struct i2c_client *client = (struct i2c_client *)ts_data->client;
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ tsp_info("*** CM/CS test ***\n");
+ tsp_info(" mode: 0x%x, baseline(screen: %d, key: %d)\n",
+ cmcs->cmd.mode, cmcs->cmd.base_screen, cmcs->cmd.base_key);
+ tsp_info(" start_cp (cm: %d, cs: %d), vcmp (cm: %d, cs: %d)\n",
+ cmcs->cmd.start_cp_cm, cmcs->cmd.start_cp_cs,
+ cmcs->cmd.vcmp_cm, cmcs->cmd.vcmp_cs);
+
+ ist30xx_disable_irq(ts_data);
+
+ ist30xx_reset(false);
+
+ ret = ist30xx_cmd_reg(client, CMD_ENTER_REG_ACCESS);
+ cmcs_next_step(ret);
+
+ /* Set sensor register */
+ buf32 = ts_cmcs->buf_sensor;
+ ret = ist30xx_set_cmcs_sensor(client, cmcs, buf32);
+ cmcs_next_step(ret);
+
+ /* Set command */
+ ret = ist30xx_set_cmcs_cmd(client, cmcs);
+ cmcs_next_step(ret);
+
+ ret = ist30xx_cmd_reg(client, CMD_EXIT_REG_ACCESS);
+ cmcs_next_step(ret);
+
+ /* Load cmcs test code */
+ ret = ist30xx_write_cmd(client, CMD_EXEC_MEM_CODE, 0);
+ cmcs_next_step(ret);
+
+ buf32 = (u32 *)ts_cmcs->buf_cmcs;
+ len = cmcs->cmd.cmcs_size / IST30XX_DATA_LEN;
+ tsp_verb("%08x %08x %08x %08x\n", buf32[0], buf32[1], buf32[2], buf32[3]);
+ ret = ist30xx_write_buf(client, len, buf32, len);
+ cmcs_next_step(ret);
+
+ /* Check checksum */
+ ret = ist30xx_read_cmd(client, CMD_DEFAULT, &chksum);
+ cmcs_next_step(ret);
+ if (chksum != IST30XX_CMCS_LOAD_END)
+ goto end;
+ tsp_info("CM/CS code ready!!\n");
+
+ /* Check checksum */
+ ret = ist30xx_read_cmd(client, CMD_DEFAULT, &chksum);
+ cmcs_next_step(ret);
+ tsp_info("CM/CS code chksum: %08x, %08x\n", chksum, cmcs->cmcs_chksum);
+
+ ist30xx_enable_irq(ts_data);
+ /* Wait CMCS test result */
+ if (ist30xx_calib_wait() == 1)
+ tsp_info("CM/CS test OK.\n");
+ else
+ tsp_info("CM/CS test fail.\n");
+ ist30xx_disable_irq(ts_data);
+
+ /* Read CM/CS data*/
+ if (ENABLE_CM_MODE(cmcs->cmd.mode)) {
+ /* Read CM data */
+ memset(ts_cmcs_buf->cm, 0, sizeof(ts_cmcs_buf->cm));
+
+ ret = ist30xx_get_cmcs_buf(client, cmcs, ts_cmcs_buf->cm);
+ cmcs_next_step(ret);
+
+ ret = ist30xx_apply_cmcs_slope(cmcs, ts_cmcs_buf);
+ }
+
+ if (ENABLE_CS_MODE(cmcs->cmd.mode)) {
+ /* Read CS0 data */
+ memset(ts_cmcs_buf->cs0, 0, sizeof(ts_cmcs_buf->cs0));
+ memset(ts_cmcs_buf->cs1, 0, sizeof(ts_cmcs_buf->cs1));
+
+ ret = ist30xx_get_cmcs_buf(client, cmcs, ts_cmcs_buf->cs0);
+ cmcs_next_step(ret);
+
+ /* Read CS1 data */
+ ret = ist30xx_get_cmcs_buf(client, cmcs, ts_cmcs_buf->cs1);
+ cmcs_next_step(ret);
+ }
+
+ ist30xx_reset(false);
+
+ ist30xx_start(ts_data);
+
+ cmcs_ready = CMCS_READY;
+
+end:
+ if (unlikely(ret)) {
+ tsp_warn("CM/CS test Fail!, ret=%d\n", ret);
+ } else if (unlikely(chksum != cmcs->cmcs_chksum)) {
+ tsp_warn("Error CheckSum: %x(%x)\n", chksum, cmcs->cmcs_chksum);
+ ret = -ENOEXEC;
+ }
+
+ ist30xx_enable_irq(ts_data);
+
+ return ret;
+}
+
+
+int check_tsp_type(int tx, int rx)
+{
+ struct CMCS_CH_INFO *ch = (struct CMCS_CH_INFO *)&ts_cmcs->cmcs.ch;
+
+ int last_rx_ch = (int)ch->rx_num - 1;
+ int last_tx_ch = (int)ch->tx_num - 1;
+
+ if ((rx > last_rx_ch) || (rx < 0) || (tx > last_tx_ch) || (tx < 0)) {
+ tsp_warn("TSP channel is not correct!! (%d * %d)\n", tx, rx);
+ return TSP_CH_UNKNOWN;
+ }
+
+ if (ch->key_rx) { // Key on RX channel
+ if (rx == last_rx_ch) {
+ if ((tx == ch->key1) || (tx == ch->key2) || (tx == ch->key3) ||
+ (tx == ch->key4) || (tx == ch->key5))
+ return TSP_CH_KEY;
+ else
+ return TSP_CH_UNUSED;
+ }
+ } // Key on TX channel
+ else {
+ if (tx == last_tx_ch) {
+ if ((rx == ch->key1) || (rx == ch->key2) || (rx == ch->key3) ||
+ (rx == ch->key4) || (rx == ch->key5))
+ return TSP_CH_KEY;
+ else
+ return TSP_CH_UNUSED;
+ }
+ }
+
+ return TSP_CH_SCREEN;
+}
+
+int print_cmcs(s16 *buf16, char *buf)
+{
+ int i, j;
+ int idx;
+ int count = 0;
+ char msg[128];
+
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ int tx_num = cmcs->ch.tx_num;
+ int rx_num = cmcs->ch.rx_num;
+
+ for (i = 0; i < tx_num; i++) {
+ for (j = 0; j < rx_num; j++) {
+ idx = (i * cmcs->ch.rx_num) + j;
+ count += sprintf(msg, "%5d ", buf16[idx]);
+ strcat(buf, msg);
+ }
+
+ count += sprintf(msg, "\n");
+ strcat(buf, msg);
+ }
+
+ return count;
+}
+
+int print_line_cmcs(int mode, s16 *buf16, char *buf)
+{
+ int i, j;
+ int idx;
+ int type;
+ int count = 0;
+ int key_index[5] = { 0, };
+ int key_cnt = 0;
+ char msg[128];
+
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ int tx_num = cmcs->ch.tx_num;
+ int rx_num = cmcs->ch.rx_num;
+
+ if ((mode == CMCS_FLAG_CM_SLOPE0) || (mode == CMCS_FLAG_CM_SLOPE1)) {
+ if (cmcs->ch.key_rx)
+ rx_num--;
+ else
+ tx_num--;
+ }
+
+ for (i = 0; i < tx_num; i++) {
+ for (j = 0; j < rx_num; j++) {
+ type = check_tsp_type(i, j);
+ if ((type == TSP_CH_UNKNOWN) || (type == TSP_CH_UNUSED))
+ continue; // Ignore
+
+ if ((mode == CMCS_FLAG_CM_SLOPE0) && (j == (rx_num - 1)))
+ continue;
+ else if ((mode == CMCS_FLAG_CM_SLOPE1) && (i == (tx_num - 1)))
+ continue;
+
+ idx = (i * cmcs->ch.rx_num) + j;
+
+ if (type == TSP_CH_KEY) {
+ key_index[key_cnt++] = idx;
+ continue;
+ }
+
+ count += sprintf(msg, "%5d ", buf16[idx]);
+ strcat(buf, msg);
+ }
+ }
+
+ tsp_info("key cnt: %d\n", key_cnt);
+ if ((mode != CMCS_FLAG_CM_SLOPE0) && (mode != CMCS_FLAG_CM_SLOPE1)) {
+ tsp_info("key cnt: %d\n", key_cnt);
+ for (i = 0; i < key_cnt; i++) {
+ count += sprintf(msg, "%5d ", buf16[key_index[i]]);
+ strcat(buf, msg);
+ }
+ }
+
+ count += sprintf(msg, "\n");
+ strcat(buf, msg);
+
+ return count;
+}
+
+/* sysfs: /sys/class/touch/cmcs/info */
+ssize_t ist30xx_cmcs_info_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int count;
+ char msg[128];
+
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "CMCS test is not work!!\n");
+
+ if (cmcs == NULL)
+ return sprintf(buf, "Unknown cmcs bin\n");
+
+ /* Mode */
+ count = sprintf(msg, "%d ", cmcs->cmd.mode);
+ strcat(buf, msg);
+
+ /* Channel */
+ count += sprintf(msg, "%d %d %d %d %d %d %d %d ",
+ cmcs->ch.tx_num, cmcs->ch.rx_num, cmcs->ch.key_rx, cmcs->ch.key1,
+ cmcs->ch.key2, cmcs->ch.key3, cmcs->ch.key4, cmcs->ch.key5);
+ strcat(buf, msg);
+
+ /* Slope */
+ count += sprintf(msg, "%d %d %d %d %d %d",
+ cmcs->slope.x_min, cmcs->slope.x_max,
+ cmcs->slope.y_min, cmcs->slope.y_max,
+ cmcs->slope.key_min, cmcs->slope.key_max);
+ strcat(buf, msg);
+
+ /* CM */
+ count += sprintf(msg, "%d %d %d %d ",
+ cmcs->spec_cm.screen_min, cmcs->spec_cm.screen_max,
+ cmcs->spec_cm.key_min, cmcs->spec_cm.key_max);
+ strcat(buf, msg);
+
+ /* CS0 */
+ count += sprintf(msg, "%d %d %d %d ",
+ cmcs->spec_cs0.screen_min, cmcs->spec_cs0.screen_max,
+ cmcs->spec_cs0.key_min, cmcs->spec_cs0.key_max);
+ strcat(buf, msg);
+
+ /* CS1 */
+ count += sprintf(msg, "%d %d %d %d ",
+ cmcs->spec_cs1.screen_min, cmcs->spec_cs1.screen_max,
+ cmcs->spec_cs1.key_min, cmcs->spec_cs1.key_max);
+ strcat(buf, msg);
+
+ /* CR */
+ count += sprintf(msg, "%d %d %d %d ",
+ cmcs->spec_cr.screen_min, cmcs->spec_cr.screen_max,
+ cmcs->spec_cr.key_min, cmcs->spec_cr.key_max);
+ strcat(buf, msg);
+
+ tsp_verb("%s\n", buf);
+
+ return count;
+}
+
+/* sysfs: /sys/class/touch/cmcs/cmcs_binary */
+ssize_t ist30xx_cmcs_binary_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+
+ if ((ts_cmcs_bin == NULL) || (ts_cmcs_bin_size == 0))
+ return sprintf(buf, "Binary is not correct(%d)\n", ts_cmcs_bin_size);
+
+ ist30xx_get_cmcs_info(ts_cmcs_bin, ts_cmcs_bin_size);
+
+ mutex_lock(&ist30xx_mutex);
+ ret = ist30xx_cmcs_test(ts_cmcs_bin, ts_cmcs_bin_size);
+ mutex_unlock(&ist30xx_mutex);
+
+ return sprintf(buf, (ret == 0 ? "OK\n" : "Fail\n"));
+}
+
+/* sysfs: /sys/class/touch/cmcs/cmcs_custom */
+ssize_t ist30xx_cmcs_custom_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ int bin_size = 0;
+ u8 *bin = NULL;
+ const struct firmware *req_bin = NULL;
+
+ ret = request_firmware(&req_bin, IST30XXB_CMCS_NAME, &ts_data->client->dev);
+ if (ret)
+ return sprintf(buf, "File not found, %s\n", IST30XXB_CMCS_NAME);
+
+ bin = (u8 *)req_bin->data;
+ bin_size = (u32)req_bin->size;
+
+ ist30xx_get_cmcs_info(bin, bin_size);
+
+ mutex_lock(&ist30xx_mutex);
+ ret = ist30xx_cmcs_test(bin, bin_size);
+ mutex_unlock(&ist30xx_mutex);
+
+ release_firmware(req_bin);
+
+ tsp_info("size: %d\n", sprintf(buf, (ret == 0 ? "OK\n" : "Fail\n")));
+
+ return sprintf(buf, (ret == 0 ? "OK\n" : "Fail\n"));
+}
+
+#define MAX_FILE_PATH 255
+/* sysfs: /sys/class/touch/cmcs/cmcs_sdcard */
+ssize_t ist30xx_cmcs_sdcard_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret = 0;
+ int bin_size = 0;
+ u8 *bin = NULL;
+ const u8 *buff = 0;
+ mm_segment_t old_fs = { 0 };
+ struct file *fp = NULL;
+ long fsize = 0, nread = 0;
+ char fw_path[MAX_FILE_PATH];
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+
+ snprintf(fw_path, MAX_FILE_PATH, "/sdcard/touch/firmware/%s",
+ IST30XXB_CMCS_NAME);
+ fp = filp_open(fw_path, O_RDONLY, 0);
+ if (IS_ERR(fp)) {
+ tsp_info("file %s open error:%d\n", fw_path, (s32)fp);
+ ret = -ENOENT;
+ goto err_file_open;
+ }
+
+ fsize = fp->f_path.dentry->d_inode->i_size;
+
+ buff = kzalloc((size_t)fsize, GFP_KERNEL);
+ if (!buff) {
+ tsp_info("fail to alloc buffer\n");
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ nread = vfs_read(fp, (char __user *)buff, fsize, &fp->f_pos);
+ if (nread != fsize) {
+ tsp_info("mismatch fw size\n");
+
+ goto err_fw_size;
+ }
+
+ bin = (u8 *)buff;
+ bin_size = (u32)fsize;
+
+ filp_close(fp, current->files);
+ set_fs(old_fs);
+ tsp_info("firmware is loaded!!\n");
+
+ ist30xx_get_cmcs_info(bin, bin_size);
+
+ mutex_lock(&ist30xx_mutex);
+ ret = ist30xx_cmcs_test(bin, bin_size);
+ mutex_unlock(&ist30xx_mutex);
+
+ kfree(buff);
+
+ tsp_info("size: %d\n", sprintf(buf, (ret == 0 ? "OK\n" : "Fail\n")));
+
+ return sprintf(buf, (ret == 0 ? "OK\n" : "Fail\n"));
+
+err_fw_size:
+ if (buff)
+ kfree(buff);
+err_alloc:
+ if (fp)
+ filp_close(fp, NULL);
+err_file_open:
+ set_fs(old_fs);
+
+ return 0;
+}
+
+/* sysfs: /sys/class/touch/cmcs/cm */
+ssize_t ist30xx_cm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "CMCS test is not work!!\n");
+
+ if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CM))
+ return 0;
+
+ tsp_verb("CM (%d * %d)\n", cmcs->ch.tx_num, cmcs->ch.rx_num);
+
+ return print_cmcs(ts_cmcs_buf->cm, buf);
+}
+
+/* sysfs: /sys/class/touch/cmcs/cm_spec */
+ssize_t ist30xx_cm_spec_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "CMCS test is not work!!\n");
+
+ if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CM))
+ return 0;
+
+ tsp_verb("CM Spec (%d * %d)\n", cmcs->ch.tx_num, cmcs->ch.rx_num);
+
+ return print_cmcs(ts_cmcs_buf->spec, buf);
+}
+
+/* sysfs: /sys/class/touch/cmcs/cm_slope0 */
+ssize_t ist30xx_cm_slope0_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "CMCS test is not work!!\n");
+
+ if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CM))
+ return 0;
+
+ tsp_verb("CM Slope0_X (%d * %d)\n", cmcs->ch.tx_num, cmcs->ch.rx_num);
+
+ return print_cmcs(ts_cmcs_buf->slope0, buf);
+}
+
+/* sysfs: /sys/class/touch/cmcs/cm_slope1 */
+ssize_t ist30xx_cm_slope1_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "CMCS test is not work!!\n");
+
+ if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CM))
+ return 0;
+
+ tsp_verb("CM Slope1_Y (%d * %d)\n",
+ ts_cmcs->cmcs.ch.tx_num, ts_cmcs->cmcs.ch.rx_num);
+
+ return print_cmcs(ts_cmcs_buf->slope1, buf);
+}
+
+/* sysfs: /sys/class/touch/cmcs/cs0 */
+ssize_t ist30xx_cs0_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "CMCS test is not work!!\n");
+
+ if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CS))
+ return 0;
+
+ tsp_verb("CS0 (%d * %d)\n", cmcs->ch.tx_num, cmcs->ch.rx_num);
+
+ return print_cmcs(ts_cmcs_buf->cs0, buf);
+}
+
+/* sysfs: /sys/class/touch/cmcs/cs1 */
+ssize_t ist30xx_cs1_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "CMCS test is not work!!\n");
+
+ if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CS))
+ return 0;
+
+ tsp_verb("CS1 (%d * %d)\n", cmcs->ch.tx_num, cmcs->ch.rx_num);
+
+ return print_cmcs(ts_cmcs_buf->cs1, buf);
+}
+
+
+int print_cm_slope_result(u8 flag, s16 *buf16, char *buf)
+{
+ int i, j;
+ int type, idx;
+ int count = 0, err_cnt = 0;
+ int min_spec, max_spec;
+
+ char msg[128];
+
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+ struct CMCS_SLOPE_INFO *spec = (struct CMCS_SLOPE_INFO *)&cmcs->slope;
+
+ if (flag == CMCS_FLAG_CM_SLOPE0) {
+ min_spec = spec->x_min;
+ max_spec = spec->x_max;
+ } else if (flag == CMCS_FLAG_CM_SLOPE1) {
+ min_spec = spec->y_min;
+ max_spec = spec->y_max;
+ } else {
+ count = sprintf(msg, "Unknown flag: %d\n", flag);
+ strcat(buf, msg);
+ return count;
+ }
+
+ min_spec *= -1;
+
+ count = sprintf(msg, "Spec: %d ~ %d\n", min_spec, max_spec);
+ strcat(buf, msg);
+
+ for (i = 0; i < cmcs->ch.tx_num; i++) {
+ for (j = 0; j < cmcs->ch.rx_num; j++) {
+ idx = (i * cmcs->ch.rx_num) + j;
+
+ type = check_tsp_type(i, j);
+ if ((type == TSP_CH_UNKNOWN) || (type == TSP_CH_UNUSED))
+ continue; // Ignore
+
+ if ((buf16[idx] > min_spec) && (buf16[idx] < max_spec))
+ continue; // OK
+
+ count += sprintf(msg, "%2d,%2d:%4d\n", i, j, buf16[idx]);
+ strcat(buf, msg);
+
+ err_cnt++;
+ }
+ }
+
+ /* Check error count */
+ if (err_cnt == 0)
+ count += sprintf(msg, "OK\n");
+ else
+ count += sprintf(msg, "Fail, node count: %d\n", err_cnt);
+ strcat(buf, msg);
+
+ return count;
+}
+
+int print_cm_key_slope_result(s16 *buf16, char *buf, bool print)
+{
+ int i, j;
+ int type, idx;
+ int count = 0;
+ int min_spec, max_spec;
+ int key_num = 0;
+ s16 key_cm[5] = { 0, };
+ s16 slope_result;
+
+ char msg[128];
+
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+ struct CMCS_SLOPE_INFO *spec = (struct CMCS_SLOPE_INFO *)&cmcs->slope;
+
+ min_spec = spec->key_min;
+ max_spec = spec->key_max;
+
+ min_spec *= -1;
+ if (print) {
+ count = sprintf(msg, "Spec: %d ~ %d\n", min_spec, max_spec);
+ strcat(buf, msg);
+ }
+
+ for (i = 0; i < cmcs->ch.tx_num; i++) {
+ for (j = 0; j < cmcs->ch.rx_num; j++) {
+ idx = (i * cmcs->ch.rx_num) + j;
+
+ type = check_tsp_type(i, j);
+ if (type == TSP_CH_KEY)
+ key_cm[key_num++] = buf16[idx];
+ }
+ }
+
+ /* Check key slope */
+ slope_result = key_cm[0] - key_cm[1];
+
+ if (print) {
+ if ((slope_result > min_spec) && (slope_result < max_spec))
+ count += sprintf(msg, "OK (%d)\n", slope_result);
+ else
+ count += sprintf(msg, "Fail (%d)\n", slope_result);
+ } else {
+ count += sprintf(msg, "%d\n",
+ (slope_result > 0 ? slope_result : -slope_result));
+ }
+
+ strcat(buf, msg);
+
+ return count;
+}
+
+int print_cs_result(s16 *buf16, char *buf, int cs_num)
+{
+ int i, j;
+ int type, idx;
+ int count = 0, err_cnt = 0;
+ int min_spec, max_spec;
+
+ char msg[128];
+
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+ struct CMCS_SPEC_INFO *spec;
+
+ if (cs_num == 0)
+ spec = (struct CMCS_SPEC_INFO *)&cmcs->spec_cs0;
+ else
+ spec = (struct CMCS_SPEC_INFO *)&cmcs->spec_cs1;
+
+ count = sprintf(msg, "Spec: screen(%d~%d), key(%d~%d)\n",
+ spec->screen_min, spec->screen_max, spec->key_min, spec->key_max);
+ strcat(buf, msg);
+
+ for (i = 0; i < cmcs->ch.tx_num; i++) {
+ for (j = 0; j < cmcs->ch.rx_num; j++) {
+ idx = (i * cmcs->ch.rx_num) + j;
+
+ type = check_tsp_type(i, j);
+ if ((type == TSP_CH_UNKNOWN) || (type == TSP_CH_UNUSED))
+ continue; // Ignore
+
+ if (type == TSP_CH_SCREEN) {
+ min_spec = spec->screen_min;
+ max_spec = spec->screen_max;
+ } else { // TSP_CH_KEY
+ min_spec = spec->key_min;
+ max_spec = spec->key_max;
+ }
+
+ if ((buf16[idx] > min_spec) && (buf16[idx] < max_spec))
+ continue; // OK
+
+ count += sprintf(msg, "%2d,%2d:%4d\n", i, j, buf16[idx]);
+ strcat(buf, msg);
+
+ err_cnt++;
+ }
+ }
+
+ /* Check error count */
+ if (err_cnt == 0)
+ count += sprintf(msg, "OK\n");
+ else
+ count += sprintf(msg, "Fail, node count: %d\n", err_cnt);
+ strcat(buf, msg);
+
+ return count;
+}
+
+/* sysfs: /sys/class/touch/cmcs/cm_result */
+ssize_t ist30xx_cm_result_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i, j;
+ int type, idx, err_cnt = 0;
+ int min_spec, max_spec;
+ int count = 0;
+ short cm;
+ char msg[128];
+
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "CMCS test is not work!!\n");
+
+ if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CM))
+ return 0;
+
+ for (i = 0; i < cmcs->ch.tx_num; i++) {
+ for (j = 0; j < cmcs->ch.rx_num; j++) {
+ idx = (i * cmcs->ch.rx_num) + j;
+
+ type = check_tsp_type(i, j);
+ if ((type == TSP_CH_UNKNOWN) || (type == TSP_CH_UNUSED))
+ continue;
+
+ min_spec = max_spec = ts_cmcs_buf->spec[idx];
+ if (type == TSP_CH_SCREEN) {
+ min_spec -= (min_spec * cmcs->spec_cm.screen_min / 100);
+ max_spec += (min_spec * cmcs->spec_cm.screen_max / 100);
+ } else { // TSP_CH_KEY
+ min_spec -= (min_spec * cmcs->spec_cm.key_min / 100);
+ max_spec += (min_spec * cmcs->spec_cm.key_max / 100);
+ }
+
+ cm = ts_cmcs_buf->cm[idx];
+ if ((cm > min_spec) && (cm < max_spec))
+ continue; // OK
+
+ count += sprintf(msg, "%2d,%2d:%4d (%4d~%4d)\n",
+ i, j, cm, min_spec, max_spec);
+ strcat(buf, msg);
+
+ err_cnt++;
+ }
+ }
+
+ /* Check error count */
+ if (err_cnt == 0)
+ count += sprintf(msg, "OK\n");
+ else
+ count += sprintf(msg, "Fail, node count: %d\n", err_cnt);
+ strcat(buf, msg);
+
+ return count;
+}
+
+/* sysfs: /sys/class/touch/cmcs/cm_slope0_result */
+ssize_t ist30xx_cm_slope0_result_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "CMCS test is not work!!\n");
+
+ if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CM))
+ return 0;
+
+ return print_cm_slope_result(CMCS_FLAG_CM_SLOPE0,
+ ts_cmcs_buf->slope0, buf);
+}
+
+/* sysfs: /sys/class/touch/cmcs/cm_slope1_result */
+ssize_t ist30xx_cm_slope1_result_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "CMCS test is not work!!\n");
+
+ if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CM))
+ return 0;
+
+ return print_cm_slope_result(CMCS_FLAG_CM_SLOPE1,
+ ts_cmcs_buf->slope1, buf);
+}
+/* sysfs: /sys/class/touch/cmcs/cm_key_slope_result */
+ssize_t ist30xx_cm_key_slope_result_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "CMCS test is not work!!\n");
+
+ if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CM))
+ return 0;
+
+ return print_cm_key_slope_result(ts_cmcs_buf->cm, buf, true);
+}
+
+/* sysfs: /sys/class/touch/cmcs/cs0_result */
+ssize_t ist30xx_cs0_result_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "CMCS test is not work!!\n");
+
+ if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CS))
+ return 0;
+
+ return print_cs_result(ts_cmcs_buf->cs0, buf, 0);
+}
+
+/* sysfs: /sys/class/touch/cmcs/cs1_result */
+ssize_t ist30xx_cs1_result_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "CMCS test is not work!!\n");
+
+ if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CS))
+ return 0;
+
+ return print_cs_result(ts_cmcs_buf->cs1, buf, 1);
+}
+
+/* sysfs: /sys/class/touch/cmcs/line_cm */
+ssize_t ist30xx_line_cm_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "CMCS test is not work!!\n");
+
+ if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CM))
+ return 0;
+
+ return print_line_cmcs(CMCS_FLAG_CM, ts_cmcs_buf->cm, buf);
+}
+
+/* sysfs: /sys/class/touch/cmcs/line_cm_slope0 */
+ssize_t ist30xx_line_cm_slope0_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "CMCS test is not work!!\n");
+
+ if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CM))
+ return 0;
+
+ return print_line_cmcs(CMCS_FLAG_CM_SLOPE0, ts_cmcs_buf->slope0, buf);
+}
+
+/* sysfs: /sys/class/touch/cmcs/line_cm_slope1 */
+ssize_t ist30xx_line_cm_slope1_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "CMCS test is not work!!\n");
+
+ if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CM))
+ return 0;
+
+ return print_line_cmcs(CMCS_FLAG_CM_SLOPE1, ts_cmcs_buf->slope1, buf);
+}
+
+/* sysfs: /sys/class/touch/cmcs/line_cs0 */
+ssize_t ist30xx_line_cs0_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "CMCS test is not work!!\n");
+
+ if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CS))
+ return 0;
+
+ return print_line_cmcs(CMCS_FLAG_CS0, ts_cmcs_buf->cs0, buf);
+}
+
+/* sysfs: /sys/class/touch/cmcs/line_cs1 */
+ssize_t ist30xx_line_cs1_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ CMCS_INFO *cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "CMCS test is not work!!\n");
+
+ if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CS))
+ return 0;
+
+ return print_line_cmcs(CMCS_FLAG_CS1, ts_cmcs_buf->cs1, buf);
+}
+
+/* sysfs: /sys/class/touch/cmcs/cm_key_slope_value */
+ssize_t ist30xx_cm_key_slope_value_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ CMCS_INFO *cmcs;
+
+ if ((ts_cmcs_bin == NULL) || (ts_cmcs_bin_size == 0))
+ return sprintf(buf, "%s", "FFFF");
+
+ ist30xx_get_cmcs_info(ts_cmcs_bin, ts_cmcs_bin_size);
+
+ mutex_lock(&ist30xx_mutex);
+ ret = ist30xx_cmcs_test(ts_cmcs_bin, ts_cmcs_bin_size);
+ mutex_unlock(&ist30xx_mutex);
+
+ if (ret)
+ return sprintf(buf, "%s", "FFFF");
+
+ cmcs = (CMCS_INFO *)&ts_cmcs->cmcs;
+
+ if (cmcs_ready == CMCS_NOT_READY)
+ return sprintf(buf, "%s", "FFFF");
+
+ if ((cmcs->cmd.mode) && !(cmcs->cmd.mode & FLAG_ENABLE_CM))
+ return sprintf(buf, "%s", "FFFF");
+
+ return print_cm_key_slope_result(ts_cmcs_buf->cm, buf, false);
+}
+
+/* sysfs : cmcs */
+static DEVICE_ATTR(info, S_IRUGO, ist30xx_cmcs_info_show, NULL);
+static DEVICE_ATTR(cmcs_binary, S_IRUGO, ist30xx_cmcs_binary_show, NULL);
+static DEVICE_ATTR(cmcs_custom, S_IRUGO, ist30xx_cmcs_custom_show, NULL);
+static DEVICE_ATTR(cmcs_sdcard, S_IRUGO, ist30xx_cmcs_sdcard_show, NULL);
+static DEVICE_ATTR(cm, S_IRUGO, ist30xx_cm_show, NULL);
+static DEVICE_ATTR(cm_spec, S_IRUGO, ist30xx_cm_spec_show, NULL);
+static DEVICE_ATTR(cm_slope0, S_IRUGO, ist30xx_cm_slope0_show, NULL);
+static DEVICE_ATTR(cm_slope1, S_IRUGO, ist30xx_cm_slope1_show, NULL);
+static DEVICE_ATTR(cs0, S_IRUGO, ist30xx_cs0_show, NULL);
+static DEVICE_ATTR(cs1, S_IRUGO, ist30xx_cs1_show, NULL);
+static DEVICE_ATTR(cm_result, S_IRUGO, ist30xx_cm_result_show, NULL);
+static DEVICE_ATTR(cm_slope0_result, S_IRUGO,
+ ist30xx_cm_slope0_result_show, NULL);
+static DEVICE_ATTR(cm_slope1_result, S_IRUGO,
+ ist30xx_cm_slope1_result_show, NULL);
+static DEVICE_ATTR(cm_key_slope_result, S_IRUGO,
+ ist30xx_cm_key_slope_result_show, NULL);
+static DEVICE_ATTR(cs0_result, S_IRUGO, ist30xx_cs0_result_show, NULL);
+static DEVICE_ATTR(cs1_result, S_IRUGO, ist30xx_cs1_result_show, NULL);
+static DEVICE_ATTR(line_cm, S_IRUGO, ist30xx_line_cm_show, NULL);
+static DEVICE_ATTR(line_cm_slope0, S_IRUGO, ist30xx_line_cm_slope0_show, NULL);
+static DEVICE_ATTR(line_cm_slope1, S_IRUGO, ist30xx_line_cm_slope1_show, NULL);
+static DEVICE_ATTR(line_cs0, S_IRUGO, ist30xx_line_cs0_show, NULL);
+static DEVICE_ATTR(line_cs1, S_IRUGO, ist30xx_line_cs1_show, NULL);
+static DEVICE_ATTR(cm_key_slope_value, S_IRUGO,
+ ist30xx_cm_key_slope_value_show, NULL);
+
+static struct attribute *cmcs_attributes[] = {
+ &dev_attr_info.attr,
+ &dev_attr_cmcs_binary.attr,
+ &dev_attr_cmcs_custom.attr,
+ &dev_attr_cmcs_sdcard.attr,
+ &dev_attr_cm.attr,
+ &dev_attr_cm_spec.attr,
+ &dev_attr_cm_slope0.attr,
+ &dev_attr_cm_slope1.attr,
+ &dev_attr_cs0.attr,
+ &dev_attr_cs1.attr,
+ &dev_attr_cm_result.attr,
+ &dev_attr_cm_slope0_result.attr,
+ &dev_attr_cm_slope1_result.attr,
+ &dev_attr_cm_key_slope_result.attr,
+ &dev_attr_cs0_result.attr,
+ &dev_attr_cs1_result.attr,
+ &dev_attr_line_cm.attr,
+ &dev_attr_line_cm_slope0.attr,
+ &dev_attr_line_cm_slope1.attr,
+ &dev_attr_line_cs0.attr,
+ &dev_attr_line_cs1.attr,
+ &dev_attr_cm_key_slope_value.attr,
+ NULL,
+};
+
+static struct attribute_group cmcs_attr_group = {
+ .attrs = cmcs_attributes,
+};
+
+extern struct class *ist30xx_class;
+struct device *ist30xx_cmcs_dev;
+
+
+int ist30xx_init_cmcs_sysfs(void)
+{
+ /* /sys/class/touch/cmcs */
+ ist30xx_cmcs_dev = device_create(ist30xx_class, NULL, 0, NULL, "cmcs");
+
+ /* /sys/class/touch/cmcs/... */
+ if (unlikely(sysfs_create_group(&ist30xx_cmcs_dev->kobj,
+ &cmcs_attr_group)))
+ tsp_err("Failed to create sysfs group(%s)!\n", "cmcs");
+
+#if IST30XX_INTERNAL_CMCS_BIN
+ ts_cmcs_bin = (u8 *)ist30xxb_cmcs;
+ ts_cmcs_bin_size = sizeof(ist30xxb_cmcs);
+
+ ist30xx_get_cmcs_info(ts_cmcs_bin, ts_cmcs_bin_size);
+#endif
+
+ return 0;
+}