2255 lines
63 KiB
C
2255 lines
63 KiB
C
/* Himax Android Driver Sample Code for common functions
|
|
*
|
|
* Copyright (C) 2021 Himax Corporation.
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
/*#include "himax_common.h"*/
|
|
/*#include "himax_ic_core.h"*/
|
|
#include "himax.h"
|
|
#include "himax_inspection.h"
|
|
|
|
#define SUPPORT_FINGER_DATA_CHECKSUM 0x0F
|
|
#define TS_WAKE_LOCK_TIMEOUT (5000)
|
|
#define FRAME_COUNT 5
|
|
|
|
#if defined(__EMBEDDED_FW__)
|
|
struct firmware g_embedded_fw = {
|
|
.data = _binary___Himax_firmware_bin_start,
|
|
};
|
|
#endif
|
|
|
|
#define HIMAX_PROC_TOUCH_FOLDER "android_touch"
|
|
#define HIMAX_PROC_SELF_TEST_FILE "self_test"
|
|
#define HIMAX_PROC_WP_BP_LOCK_FILE "WPBPlock_node"
|
|
#define HIMAX_PROC_FAIL_DET_FILE "Faildet"
|
|
|
|
#if defined(HX_TP_INSPECT_MODE)
|
|
#define HIMAX_PROC_INSPECT_MODE_FILE "Inspect_mode"
|
|
#endif
|
|
|
|
static int himax_chip_num;
|
|
|
|
static ssize_t himax_self_test(struct seq_file *s, void *v)
|
|
{
|
|
struct himax_ts_data *ts = PDE_DATA(file_inode(s->file));
|
|
size_t ret = 0;
|
|
|
|
I("%s: enter, %d\n", __func__, __LINE__);
|
|
|
|
if (ts->suspended == 1) {
|
|
E("%s: please do self test in normal active mode\n", __func__);
|
|
return HX_INIT_FAIL;
|
|
}
|
|
himax_int_enable(ts, 0); /* disable irq */
|
|
|
|
ts->in_self_test = 1;
|
|
ts->core_fp.fp_chip_self_test(ts, s, v);
|
|
ts->in_self_test = 0;
|
|
|
|
himax_int_enable(ts, 1);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void *himax_self_test_seq_start(struct seq_file *s, loff_t *pos)
|
|
{
|
|
if (*pos >= 1)
|
|
return NULL;
|
|
|
|
return (void *)((unsigned long)*pos + 1);
|
|
}
|
|
|
|
static void *himax_self_test_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static void himax_self_test_seq_stop(struct seq_file *s, void *v)
|
|
{
|
|
}
|
|
|
|
static int himax_self_test_seq_read(struct seq_file *s, void *v)
|
|
{
|
|
struct himax_ts_data *ts = PDE_DATA(file_inode(s->file));
|
|
size_t ret = 0;
|
|
|
|
if (ts->chip_test_r_flag) {
|
|
#if defined(CONFIG_TOUCHSCREEN_HIMAX_INSPECT)
|
|
if (ts->rslt_data)
|
|
seq_printf(s, "%s", ts->rslt_data);
|
|
else
|
|
#endif
|
|
seq_puts(s, "No chip test data.\n");
|
|
} else {
|
|
himax_self_test(s, v);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct seq_operations himax_self_test_seq_ops = {
|
|
.start = himax_self_test_seq_start,
|
|
.next = himax_self_test_seq_next,
|
|
.stop = himax_self_test_seq_stop,
|
|
.show = himax_self_test_seq_read,
|
|
};
|
|
|
|
static int himax_self_test_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return seq_open(file, &himax_self_test_seq_ops);
|
|
};
|
|
|
|
static ssize_t himax_self_test_write(struct file *filp, const char __user *buff, size_t len,
|
|
loff_t *data)
|
|
{
|
|
struct himax_ts_data *ts = PDE_DATA(file_inode(filp));
|
|
char buf[80] = { 0 };
|
|
|
|
if (len >= 80) {
|
|
I("%s: no command exceeds 80 chars.\n", __func__);
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (copy_from_user(buf, buff, len))
|
|
return -EFAULT;
|
|
|
|
if (buf[0] == 'r') {
|
|
ts->chip_test_r_flag = true;
|
|
I("%s: Start to read chip test data.\n", __func__);
|
|
} else {
|
|
ts->chip_test_r_flag = false;
|
|
I("%s: Back to do self test.\n", __func__);
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
static void *himax_WPBPlock_node_seq_start(struct seq_file *s, loff_t *pos)
|
|
{
|
|
if (*pos >= 1)
|
|
return NULL;
|
|
|
|
return (void *)((unsigned long)*pos + 1);
|
|
}
|
|
|
|
static void *himax_WPBPlock_node_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static void himax_WPBPlock_node_seq_stop(struct seq_file *s, void *v)
|
|
{
|
|
}
|
|
|
|
static int himax_WPBPlock_node_seq_read(struct seq_file *s, void *v)
|
|
{
|
|
struct himax_ts_data *ts = PDE_DATA(file_inode(s->file));
|
|
size_t ret = 0;
|
|
uint8_t status;
|
|
|
|
status = himax_mcu_WP_BP_status(ts);
|
|
|
|
if (status == 0x9C) {
|
|
seq_printf(s, "WP BP lock status is lock with value %x.\n", status);
|
|
} else {
|
|
seq_printf(s, "WP BP lock status is unlock with value %x.\n", status);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct seq_operations himax_WPBPlock_node_seq_ops = {
|
|
.start = himax_WPBPlock_node_seq_start,
|
|
.next = himax_WPBPlock_node_seq_next,
|
|
.stop = himax_WPBPlock_node_seq_stop,
|
|
.show = himax_WPBPlock_node_seq_read,
|
|
};
|
|
|
|
static int himax_WPBPlock_node_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return seq_open(file, &himax_WPBPlock_node_seq_ops);
|
|
};
|
|
|
|
static ssize_t himax_WPBPlock_node_write(struct file *filp, const char __user *buff, size_t len,
|
|
loff_t *data)
|
|
{
|
|
struct himax_ts_data *ts = PDE_DATA(file_inode(filp));
|
|
char buf[80] = { 0 };
|
|
int result = 0;
|
|
|
|
if (len >= 80) {
|
|
I("%s: no command exceeds 80 chars.\n", __func__);
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (copy_from_user(buf, buff, len))
|
|
return -EFAULT;
|
|
|
|
if ((buf[0] == 'd') || (buf[0] == 'D')) {
|
|
I("%s: Start to disable BP lock.\n", __func__);
|
|
result = himax_mcu_WP_BP_disable(ts);
|
|
I("%s: himax_mcu_WP_BP_disable return :%d.\n", __func__, result);
|
|
|
|
} else if ((buf[0] == 'e') || (buf[0] == 'E')) {
|
|
I("%s: Start to enable BP lock.\n", __func__);
|
|
result = himax_mcu_WP_BP_enable(ts);
|
|
I("%s: himax_mcu_WP_BP_enable return :%d.\n", __func__, result);
|
|
} else {
|
|
I("%s: Input cmd is incorrect!\n", __func__);
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
static void *himax_fail_det_seq_start(struct seq_file *s, loff_t *pos)
|
|
{
|
|
if (*pos >= 1)
|
|
return NULL;
|
|
|
|
return (void *)((unsigned long)*pos + 1);
|
|
}
|
|
|
|
static void *himax_fail_det_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static void himax_fail_det_seq_stop(struct seq_file *s, void *v)
|
|
{
|
|
}
|
|
|
|
static int himax_fail_det_seq_read(struct seq_file *s, void *v)
|
|
{
|
|
struct himax_ts_data *ts = PDE_DATA(file_inode(s->file));
|
|
size_t ret = 0;
|
|
uint8_t data[24] = { 0 };
|
|
|
|
ts->core_fp.fp_dd_clk_set(ts, true);
|
|
ts->core_fp.fp_dd_reg_en(ts, true);
|
|
|
|
ts->core_fp.fp_dd_reg_read(ts, 0xE5, 0, 8, data, 0);
|
|
I("%s E5_Bank0: para[0]=0x%2.2X,para[1]=0x%2.2X,para[2]=0x%2.2X, para[3]=0x%2.2X\n",
|
|
__func__, data[0], data[1], data[2], data[3]);
|
|
I("%s E5_Bank0: para[4]=0x%2.2X,para[5]=0x%2.2X,para[6]=0x%2.2X, para[7]=0x%2.2X\n",
|
|
__func__, data[4], data[5], data[6], data[7]);
|
|
seq_printf(s,
|
|
"E5_Bank0: para[0]=0x%2.2X,para[1]=0x%2.2X,para[2]=0x%2.2X, para[3]=0x%2.2X\n",
|
|
data[0], data[1], data[2], data[3]);
|
|
seq_printf(s,
|
|
"E5_Bank0: para[0]=0x%2.2X,para[1]=0x%2.2X,para[2]=0x%2.2X, para[3]=0x%2.2X\n",
|
|
data[4], data[5], data[6], data[7]);
|
|
|
|
ts->core_fp.fp_dd_reg_read(ts, 0xE5, 0, 8, data, 1);
|
|
I("%s E5_Bank1: para[0]=0x%2.2X,para[1]=0x%2.2X,para[2]=0x%2.2X, para[3]=0x%2.2X\n",
|
|
__func__, data[0], data[1], data[2], data[3]);
|
|
I("%s E5_Bank1: para[4]=0x%2.2X,para[5]=0x%2.2X,para[6]=0x%2.2X, para[7]=0x%2.2X\n",
|
|
__func__, data[4], data[5], data[6], data[7]);
|
|
|
|
seq_printf(s,
|
|
"E5_Bank1: para[0]=0x%2.2X,para[1]=0x%2.2X,para[2]=0x%2.2X, para[3]=0x%2.2X\n",
|
|
data[0], data[1], data[2], data[3]);
|
|
seq_printf(s,
|
|
"E5_Bank1: para[0]=0x%2.2X,para[1]=0x%2.2X,para[2]=0x%2.2X, para[3]=0x%2.2X\n",
|
|
data[4], data[5], data[6], data[7]);
|
|
|
|
ts->core_fp.fp_dd_clk_set(ts, false);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct seq_operations himax_fail_det_seq_ops = {
|
|
.start = himax_fail_det_seq_start,
|
|
.next = himax_fail_det_seq_next,
|
|
.stop = himax_fail_det_seq_stop,
|
|
.show = himax_fail_det_seq_read,
|
|
};
|
|
|
|
static int himax_fail_det_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return seq_open(file, &himax_fail_det_seq_ops);
|
|
};
|
|
|
|
static ssize_t himax_fail_det_write(struct file *filp, const char __user *buff, size_t len,
|
|
loff_t *data)
|
|
{
|
|
/*not implement write function*/
|
|
return len;
|
|
}
|
|
|
|
static const struct proc_ops himax_proc_self_test_ops = {
|
|
.proc_open = himax_self_test_proc_open,
|
|
.proc_read = seq_read,
|
|
.proc_write = himax_self_test_write,
|
|
.proc_release = seq_release,
|
|
};
|
|
|
|
static const struct proc_ops himax_proc_WPBPlock_node_ops = {
|
|
.proc_open = himax_WPBPlock_node_proc_open,
|
|
.proc_read = seq_read,
|
|
.proc_write = himax_WPBPlock_node_write,
|
|
.proc_release = seq_release,
|
|
};
|
|
|
|
static const struct proc_ops himax_proc_fail_det_ops = {
|
|
.proc_open = himax_fail_det_proc_open,
|
|
.proc_read = seq_read,
|
|
.proc_write = himax_fail_det_write,
|
|
.proc_release = seq_release,
|
|
};
|
|
|
|
#if defined(HX_TP_INSPECT_MODE)
|
|
static void himax_bank_search_set(struct himax_ts_data *ts, uint16_t Nframe, uint8_t checktype)
|
|
{
|
|
uint8_t tmp_addr[4];
|
|
uint8_t tmp_data[4];
|
|
|
|
/*skip frame 0x100070F4*/
|
|
himax_parse_assign_cmd(addr_skip_frame, tmp_addr, sizeof(tmp_addr));
|
|
himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, false);
|
|
|
|
switch (checktype) {
|
|
case HX_ACT_IDLE_RAWDATA:
|
|
case HX_ACT_IDLE_BPN_RAWDATA:
|
|
case HX_ACT_IDLE_NOISE:
|
|
tmp_data[0] = BS_ACT_IDLE;
|
|
break;
|
|
case HX_LP_RAWDATA:
|
|
case HX_LP_BPN_RAWDATA:
|
|
case HX_LP_ABS_NOISE:
|
|
case HX_LP_WT_NOISE:
|
|
tmp_data[0] = BS_LPWUG;
|
|
break;
|
|
case HX_LP_IDLE_RAWDATA:
|
|
case HX_LP_IDLE_BPN_RAWDATA:
|
|
case HX_LP_IDLE_NOISE:
|
|
tmp_data[0] = BS_LP_dile;
|
|
break;
|
|
case HX_RAWDATA:
|
|
case HX_BPN_RAWDATA:
|
|
case HX_SC:
|
|
tmp_data[0] = BS_RAWDATA;
|
|
break;
|
|
case HX_WT_NOISE:
|
|
case HX_ABS_NOISE:
|
|
tmp_data[0] = BS_NOISE;
|
|
break;
|
|
default:
|
|
tmp_data[0] = BS_OPENSHORT;
|
|
break;
|
|
}
|
|
tmp_data[0] = 1;
|
|
himax_mcu_register_write(ts, tmp_addr, 4, tmp_data, 0);
|
|
}
|
|
static int himax_chip_inspect_mode(struct seq_file *s, void *v)
|
|
{
|
|
struct himax_ts_data *ts = PDE_DATA(file_inode(s->file));
|
|
uint8_t tmp_addr[4];
|
|
uint8_t tmp_data[4];
|
|
char buf[64] = { 0 };
|
|
uint32_t cnt = 0;
|
|
bool is_done = false;
|
|
int16_t Low_bound;
|
|
int16_t low_threshold;
|
|
|
|
/*
|
|
* A low threshold value 0xff00 is assign defaultly, for int16_t
|
|
* type, 0xff00 equal to -256(2^16- 0xff00). So set the low threshold
|
|
* as -256 directly.
|
|
*/
|
|
low_threshold = -256;
|
|
|
|
tmp_addr[3] = 0x10;
|
|
tmp_addr[2] = 0x00;
|
|
tmp_addr[1] = 0x74;
|
|
tmp_addr[0] = 0x54;
|
|
|
|
switch (ts->inspect_mode_flag) {
|
|
case 0x01: /*Start to do Short_test*/
|
|
tmp_data[3] = 0x5A;
|
|
tmp_data[2] = 0x00;
|
|
tmp_data[1] = 0x01;
|
|
tmp_data[0] = 0xA5;
|
|
seq_printf(s, "Item : Short_test.\n");
|
|
break;
|
|
case 0x02: /*Start to do Open_test*/
|
|
tmp_data[3] = 0x5A;
|
|
tmp_data[2] = 0x00;
|
|
tmp_data[1] = 0x02;
|
|
tmp_data[0] = 0xA4;
|
|
seq_printf(s, "Item : Open_test.\n");
|
|
break;
|
|
case 0x08: /*Start to do Noise_test*/
|
|
tmp_data[3] = 0x5A;
|
|
tmp_data[2] = 0x00;
|
|
tmp_data[1] = 0x08;
|
|
tmp_data[0] = 0x9E;
|
|
seq_printf(s, "Item : Noise_test. \n");
|
|
break;
|
|
case 0x0B: /*Start to do All_test*/
|
|
tmp_data[3] = 0x5A;
|
|
tmp_data[2] = 0x00;
|
|
tmp_data[1] = 0x0B;
|
|
tmp_data[0] = 0x9B;
|
|
seq_printf(s, "Item : All_test.\n");
|
|
break;
|
|
default:
|
|
seq_printf(s, "Input cmd is incorrect.\n");
|
|
return false;
|
|
}
|
|
|
|
ts->core_fp.fp_sense_off(ts, true);
|
|
if (ts->core_fp.fp_reload_disable != NULL)
|
|
ts->core_fp.fp_reload_disable(ts, 1);
|
|
himax_bank_search_set(ts, 1, 1);
|
|
ts->core_fp.fp_sense_on(ts, 0x01);
|
|
msleep(20);
|
|
|
|
himax_mcu_register_write(ts, tmp_addr, DATA_LEN_4, tmp_data, 0);
|
|
himax_mcu_register_read(ts, tmp_addr, DATA_LEN_4, tmp_data, 0);
|
|
I("Now register =0x%02X, 0x%02X, 0x%02X, 0x%02X\n", tmp_data[3], tmp_data[2], tmp_data[1],
|
|
tmp_data[0]);
|
|
|
|
while (cnt++ < 8000) {
|
|
ts->core_fp.fp_read_event_stack(ts, buf, 64);
|
|
I("%s : %s = 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", __func__, "Waiting for test",
|
|
buf[0], buf[1], buf[2], buf[3]);
|
|
if ((buf[0] == 0x20) && (buf[1] == 0x19)) {
|
|
is_done = true;
|
|
break;
|
|
}
|
|
msleep(1);
|
|
}
|
|
|
|
if (is_done) {
|
|
if (buf[3] == 0x01) {
|
|
switch (ts->inspect_mode_flag) {
|
|
case 0x01: /*Start to do Short_test*/
|
|
seq_printf(s, "Short Test : %s \n",
|
|
((buf[7] & 0x01) == 0x01) ? "Fail" : "Pass");
|
|
Low_bound = ((buf[14] << 8) + buf[15]);
|
|
Low_bound = (Low_bound > low_threshold) ?
|
|
(low_threshold - Low_bound) : Low_bound;
|
|
seq_printf(s, "%s = %5d / %5d\n", "Short Max. / Min.",
|
|
((buf[12] << 8) + buf[13]), Low_bound);
|
|
break;
|
|
case 0x02: /*Start to do Open_test*/
|
|
seq_printf(s, "Open Test : %s \n",
|
|
((buf[7] & 0x02) == 0x02) ? "Fail" : "Pass");
|
|
Low_bound = ((buf[18] << 8) + buf[19]);
|
|
Low_bound = (Low_bound > low_threshold) ?
|
|
(low_threshold - Low_bound) : Low_bound;
|
|
seq_printf(s, "%s = %5d / %5d\n", "Open Max. / Min.",
|
|
((buf[16] << 8) + buf[17]), Low_bound);
|
|
break;
|
|
case 0x08: /*Start to do Noise_test*/
|
|
seq_printf(s, "Noise Test : %s \n",
|
|
((buf[7] & 0x08) == 0x08) ? "Fail" : "Pass");
|
|
Low_bound = ((buf[26] << 8) + buf[27]);
|
|
Low_bound = (Low_bound > low_threshold) ?
|
|
(low_threshold - Low_bound) : Low_bound;
|
|
seq_printf(s, "%s = %5d / %5d\n", "Noise Max. / Min.",
|
|
((buf[24] << 8) + buf[25]), Low_bound);
|
|
// seq_printf(s, "%s = %5d / %5d\n", "Noise Max. / Min.", ((buf[24] << 8) + buf[25]), ((buf[26] << 8) + buf[27]));
|
|
break;
|
|
case 0x0B: /*Start to do All_test*/
|
|
seq_printf(s, "Short Test : %s \n",
|
|
((buf[7] & 0x01) == 0x01) ? "Fail" : "Pass");
|
|
seq_printf(s, "Open Test : %s \n",
|
|
((buf[7] & 0x02) == 0x02) ? "Fail" : "Pass");
|
|
seq_printf(s, "Noise Test : %s \n",
|
|
((buf[7] & 0x08) == 0x08) ? "Fail" : "Pass");
|
|
Low_bound = ((buf[14] << 8) + buf[15]);
|
|
Low_bound = (Low_bound > low_threshold) ?
|
|
(low_threshold - Low_bound) : Low_bound;
|
|
seq_printf(s, "%s = %5d / %5d\n", "Short Max. / Min.",
|
|
((buf[12] << 8) + buf[13]), Low_bound);
|
|
Low_bound = ((buf[18] << 8) + buf[19]);
|
|
Low_bound = (Low_bound > low_threshold) ?
|
|
(low_threshold - Low_bound) : Low_bound;
|
|
seq_printf(s, "%s = %5d / %5d\n", "Open Max. / Min.",
|
|
((buf[16] << 8) + buf[17]), Low_bound);
|
|
Low_bound = ((buf[26] << 8) + buf[27]);
|
|
Low_bound = (Low_bound > low_threshold) ?
|
|
(low_threshold - Low_bound) : Low_bound;
|
|
seq_printf(s, "%s = %5d / %5d\n", "Noise Max. / Min.",
|
|
((buf[24] << 8) + buf[25]), Low_bound);
|
|
break;
|
|
default:
|
|
seq_printf(s, "Input cmd is incorrect.\n");
|
|
}
|
|
} else if (buf[3] == 0x10)
|
|
seq_printf(s, "%s \n", "Self Test command error.");
|
|
else if (buf[3] == 0x20)
|
|
seq_printf(s, "%s \n", "Self Test command CRC error.");
|
|
|
|
I("%s = 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", "Test Result", buf[4], buf[5], buf[6],
|
|
buf[7]);
|
|
I("%s = 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", "Result Information", buf[8], buf[9],
|
|
buf[10], buf[11]);
|
|
|
|
} else
|
|
seq_printf(
|
|
s,
|
|
"[ERROR] Inspect Mode has not Completed! Please check if FW support it or not! \n");
|
|
|
|
return true;
|
|
}
|
|
|
|
static ssize_t himax_inspect_mode(struct seq_file *s, void *v)
|
|
{
|
|
struct himax_ts_data *ts = PDE_DATA(file_inode(s->file));
|
|
|
|
I("%s: enter, %d\n", __func__, __LINE__);
|
|
|
|
if (ts->suspended == 1) {
|
|
E("%s: please do self test in normal active mode\n", __func__);
|
|
return HX_INIT_FAIL;
|
|
}
|
|
if (ts->inspect_mode_flag == 0x0F) { /*User Call HELP!*/
|
|
seq_printf(s, "\n");
|
|
seq_printf(s, "@ Short Test : %s \n",
|
|
"#echo Short > Inspect_mode ; #cat Inspect_mode");
|
|
seq_printf(s, "@ Open Test : %s \n",
|
|
"#echo Open > Inspect_mode ; #cat Inspect_mode");
|
|
seq_printf(s, "@ Noise Test : %s \n",
|
|
"#echo Noise > Inspect_mode ; #cat Inspect_mode");
|
|
seq_printf(s, "@ All Test : %s \n",
|
|
"#echo All > Inspect_mode ; #cat Inspect_mode");
|
|
seq_printf(s, "\n");
|
|
seq_printf(s, "@ Set Inspect_mode Threshold by writing register : \n");
|
|
seq_printf(s,
|
|
" Address | Threshold Item | [31:16] | [15:0] \n");
|
|
seq_printf(s,
|
|
" 0x100074A0 | Short Test | High Boundary | Low Boundary \n");
|
|
seq_printf(s,
|
|
" 0x100074A4 | Open Test | High Boundary | Low Boundary \n");
|
|
seq_printf(s,
|
|
" 0x100074AC | Noise Test | High Boundary | Low Boundary \n");
|
|
seq_printf(s,
|
|
" 0x100074B0 | Raw data | High Boundary | Low Boundary \n");
|
|
seq_printf(s, "\n");
|
|
seq_printf(s, "Example : To set High Boundary = 0xFF ; Low Boundary = 0x11\n");
|
|
seq_printf(s, "#echo register,w:x100074A4:x00FF0011 > debug \n");
|
|
seq_printf(s, "\n");
|
|
return true;
|
|
}
|
|
|
|
himax_int_enable(ts, 0); /* disable irq */
|
|
// ts->in_self_test = 1;
|
|
himax_chip_inspect_mode(s, v);
|
|
// ts->in_self_test = 0;
|
|
himax_int_enable(ts, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void *himax_inspect_mode_seq_start(struct seq_file *s, loff_t *pos)
|
|
{
|
|
if (*pos >= 1)
|
|
return NULL;
|
|
|
|
return (void *)((unsigned long)*pos + 1);
|
|
}
|
|
|
|
static void *himax_inspect_mode_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static void himax_inspect_mode_seq_stop(struct seq_file *s, void *v)
|
|
{
|
|
}
|
|
|
|
static int himax_inspect_mode_seq_read(struct seq_file *s, void *v)
|
|
{
|
|
struct himax_ts_data *ts = PDE_DATA(file_inode(s->file));
|
|
size_t ret = 0;
|
|
|
|
if (ts->chip_test_r_flag) {
|
|
#if defined(CONFIG_TOUCHSCREEN_HIMAX_INSPECT)
|
|
if (ts->rslt_data)
|
|
seq_printf(s, "%s", ts->rslt_data);
|
|
else
|
|
#endif
|
|
seq_puts(s, "No chip test data.\n");
|
|
} else {
|
|
himax_inspect_mode(s, v);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct seq_operations himax_inspect_mode_seq_ops = {
|
|
.start = himax_inspect_mode_seq_start,
|
|
.next = himax_inspect_mode_seq_next,
|
|
.stop = himax_inspect_mode_seq_stop,
|
|
.show = himax_inspect_mode_seq_read,
|
|
};
|
|
|
|
static int himax_inspect_mode_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return seq_open(file, &himax_inspect_mode_seq_ops);
|
|
};
|
|
|
|
static ssize_t himax_inspect_mode_write(struct file *filp, const char __user *buff, size_t len,
|
|
loff_t *data)
|
|
{
|
|
struct himax_ts_data *ts = PDE_DATA(file_inode(filp));
|
|
char buf[80] = { 0 };
|
|
|
|
if (len >= 80) {
|
|
I("%s: no command exceeds 80 chars.\n", __func__);
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (copy_from_user(buf, buff, len))
|
|
return -EFAULT;
|
|
|
|
if ((buf[0] == 's') || (buf[0] == 'S')) {
|
|
ts->inspect_mode_flag = 0x01;
|
|
I("%s: Start to do Short_test.\n", __func__);
|
|
} else if ((buf[0] == 'o') || (buf[0] == 'O')) {
|
|
ts->inspect_mode_flag = 0x02;
|
|
I("%s: Start to do Open_test.\n", __func__);
|
|
} else if ((buf[0] == 'n') || (buf[0] == 'N')) {
|
|
ts->inspect_mode_flag = 0x08;
|
|
I("%s: Start to do Noise_test.\n", __func__);
|
|
} else if ((buf[0] == 'a') || (buf[0] == 'A')) {
|
|
ts->inspect_mode_flag = 0x0B;
|
|
I("%s: Start to do All_test.\n", __func__);
|
|
} else if (buf[0] == 'h') {
|
|
ts->inspect_mode_flag = 0x0F;
|
|
I("%s: User Call HELP! Lest's assist user to operate.\n", __func__);
|
|
} else {
|
|
I("%s: Input cmd is incorrect!\n", __func__);
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
static const struct proc_ops himax_proc_inspect_mode_ops = {
|
|
//.owner = THIS_MODULE,
|
|
.proc_open = himax_inspect_mode_proc_open,
|
|
.proc_read = seq_read,
|
|
.proc_write = himax_inspect_mode_write,
|
|
.proc_release = seq_release,
|
|
};
|
|
#endif
|
|
|
|
static int himax_common_proc_init(struct himax_ts_data *ts)
|
|
{
|
|
const char *dir_name = ts->location ? ts->location : HIMAX_PROC_TOUCH_FOLDER;
|
|
|
|
ts->debug.procfs.proc_dir = proc_mkdir(dir_name, NULL);
|
|
|
|
if (ts->debug.procfs.proc_dir == NULL) {
|
|
E(" %s: himax_touch_proc_dir file create failed!\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
#if defined(CONFIG_TOUCHSCREEN_HIMAX_INSPECT)
|
|
himax_inspection_init(ts);
|
|
#endif
|
|
ts->debug.procfs.self_test =
|
|
proc_create_data(HIMAX_PROC_SELF_TEST_FILE, 0444, ts->debug.procfs.proc_dir,
|
|
&himax_proc_self_test_ops, ts);
|
|
if (ts->debug.procfs.self_test == NULL) {
|
|
E(" %s: proc self_test file create failed!\n", __func__);
|
|
goto fail_1;
|
|
}
|
|
|
|
ts->debug.procfs.wpbplock_node =
|
|
proc_create_data(HIMAX_PROC_WP_BP_LOCK_FILE, 0444, ts->debug.procfs.proc_dir,
|
|
&himax_proc_WPBPlock_node_ops, ts);
|
|
if (ts->debug.procfs.wpbplock_node == NULL) {
|
|
E(" %s: proc BPlock file create failed!\n", __func__);
|
|
goto fail_1;
|
|
}
|
|
|
|
ts->debug.procfs.fail_det =
|
|
proc_create_data(HIMAX_PROC_FAIL_DET_FILE, 0444, ts->debug.procfs.proc_dir,
|
|
&himax_proc_fail_det_ops, ts);
|
|
if (ts->debug.procfs.fail_det == NULL) {
|
|
E(" %s: proc fail det file create failed!\n", __func__);
|
|
goto fail_1;
|
|
}
|
|
|
|
#if defined(HX_TP_INSPECT_MODE)
|
|
ts->debug.procfs.inspect_mode =
|
|
proc_create_data(HIMAX_PROC_INSPECT_MODE_FILE, 0644, ts->debug.procfs.proc_dir,
|
|
&himax_proc_inspect_mode_ops, ts);
|
|
|
|
if (ts->debug.procfs.inspect_mode == NULL) {
|
|
E(" %s: proc INSPECT_MODE file create failed!\n", __func__);
|
|
goto fail_6;
|
|
}
|
|
#endif
|
|
return 0;
|
|
#if defined(HX_TP_INSPECT_MODE)
|
|
fail_6:
|
|
remove_proc_entry(HIMAX_PROC_INSPECT_MODE_FILE, ts->debug.procfs.proc_dir);
|
|
#endif
|
|
remove_proc_entry(HIMAX_PROC_SELF_TEST_FILE, ts->debug.procfs.proc_dir);
|
|
fail_1:
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static void himax_common_proc_deinit(struct himax_ts_data *ts)
|
|
{
|
|
remove_proc_entry(HIMAX_PROC_SELF_TEST_FILE, ts->debug.procfs.proc_dir);
|
|
remove_proc_entry(HIMAX_PROC_TOUCH_FOLDER, NULL);
|
|
}
|
|
|
|
void himax_parse_assign_cmd(uint32_t addr, uint8_t *cmd, int len)
|
|
{
|
|
/*I("%s: Entering!\n", __func__);*/
|
|
switch (len) {
|
|
case 1:
|
|
cmd[0] = addr;
|
|
/*I("%s: cmd[0] = 0x%02X\n", __func__, cmd[0]);*/
|
|
break;
|
|
case 2:
|
|
cmd[0] = addr % 0x100;
|
|
cmd[1] = (addr >> 8) % 0x100;
|
|
/*I("%s: cmd[0] = 0x%02X,cmd[1] = 0x%02X\n",*/
|
|
/* __func__, cmd[0], cmd[1]);*/
|
|
break;
|
|
case 4:
|
|
cmd[0] = addr % 0x100;
|
|
cmd[1] = (addr >> 8) % 0x100;
|
|
cmd[2] = (addr >> 16) % 0x100;
|
|
cmd[3] = addr / 0x1000000;
|
|
/* I("%s: cmd[0] = 0x%02X,cmd[1] = 0x%02X,*/
|
|
/*cmd[2] = 0x%02X,cmd[3] = 0x%02X\n", */
|
|
/* __func__, cmd[0], cmd[1], cmd[2], cmd[3]);*/
|
|
break;
|
|
default:
|
|
E("%s: input length fault,len = %d!\n", __func__, len);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(himax_parse_assign_cmd);
|
|
|
|
int himax_input_register(struct himax_ts_data *ts)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = himax_dev_set(ts);
|
|
|
|
if (ret < 0) {
|
|
I("%s, input device register fail!\n", __func__);
|
|
ret = INPUT_REGISTER_FAIL;
|
|
goto input_device_fail;
|
|
}
|
|
|
|
set_bit(EV_SYN, ts->input_dev->evbit);
|
|
set_bit(EV_ABS, ts->input_dev->evbit);
|
|
set_bit(EV_KEY, ts->input_dev->evbit);
|
|
set_bit(KEY_BACK, ts->input_dev->keybit);
|
|
set_bit(KEY_HOME, ts->input_dev->keybit);
|
|
set_bit(KEY_MENU, ts->input_dev->keybit);
|
|
set_bit(KEY_SEARCH, ts->input_dev->keybit);
|
|
|
|
#if defined(CONFIG_TOUCHSCREEN_HIMAX_INSPECT)
|
|
set_bit(KEY_POWER, ts->input_dev->keybit);
|
|
#endif
|
|
set_bit(BTN_TOUCH, ts->input_dev->keybit);
|
|
set_bit(KEY_APPSELECT, ts->input_dev->keybit);
|
|
set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
|
|
#if defined(HX_PROTOCOL_A)
|
|
/*ts->input_dev->mtsize = ts->nFinger_support;*/
|
|
input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, 3, 0, 0);
|
|
#else
|
|
set_bit(MT_TOOL_FINGER, ts->input_dev->keybit);
|
|
#if defined(HX_PROTOCOL_B_3PA)
|
|
input_mt_init_slots(ts->input_dev, ts->nFinger_support, INPUT_MT_DIRECT);
|
|
#else
|
|
input_mt_init_slots(ts->input_dev, ts->nFinger_support);
|
|
#endif
|
|
#endif
|
|
I("input_set_abs_params: mix_x %d, max_x %d, min_y %d, max_y %d\n", ts->pdata->abs_x_min,
|
|
ts->pdata->abs_x_max, ts->pdata->abs_y_min, ts->pdata->abs_y_max);
|
|
input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, ts->pdata->abs_x_min,
|
|
ts->pdata->abs_x_max, ts->pdata->abs_x_fuzz, 0);
|
|
input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, ts->pdata->abs_y_min,
|
|
ts->pdata->abs_y_max, ts->pdata->abs_y_fuzz, 0);
|
|
input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, ts->pdata->abs_pressure_min,
|
|
ts->pdata->abs_pressure_max, ts->pdata->abs_pressure_fuzz, 0);
|
|
#if !defined(HX_PROTOCOL_A)
|
|
input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, ts->pdata->abs_pressure_min,
|
|
ts->pdata->abs_pressure_max, ts->pdata->abs_pressure_fuzz, 0);
|
|
input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, ts->pdata->abs_width_min,
|
|
ts->pdata->abs_width_max, ts->pdata->abs_pressure_fuzz, 0);
|
|
#endif
|
|
/* input_set_abs_params(ts->input_dev, ABS_MT_AMPLITUDE, 0,*/
|
|
/* ((ts->pdata->abs_pressure_max << 16)*/
|
|
/* | ts->pdata->abs_width_max),*/
|
|
/* 0, 0);*/
|
|
/* input_set_abs_params(ts->input_dev, ABS_MT_POSITION,*/
|
|
/* 0, (BIT(31)*/
|
|
/* | (ts->pdata->abs_x_max << 16)*/
|
|
/* | ts->pdata->abs_y_max),*/
|
|
/* 0, 0);*/
|
|
|
|
if (himax_input_register_device(ts->input_dev) == 0) {
|
|
ret = NO_ERR;
|
|
} else {
|
|
E("%s: input register fail\n", __func__);
|
|
ret = INPUT_REGISTER_FAIL;
|
|
goto input_device_fail;
|
|
}
|
|
|
|
I("%s, input device registered.\n", __func__);
|
|
|
|
input_device_fail:
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(himax_input_register);
|
|
|
|
static void calcDataSize(struct himax_ts_data *ts_data)
|
|
{
|
|
ts_data->x_channel = ts_data->ic_data->HX_RX_NUM;
|
|
ts_data->y_channel = ts_data->ic_data->HX_TX_NUM;
|
|
ts_data->nFinger_support = ts_data->ic_data->HX_MAX_PT;
|
|
|
|
ts_data->coord_data_size = 4 * ts_data->nFinger_support;
|
|
ts_data->area_data_size =
|
|
((ts_data->nFinger_support / 4) + (ts_data->nFinger_support % 4 ? 1 : 0)) * 4;
|
|
ts_data->coordInfoSize = ts_data->coord_data_size + ts_data->area_data_size + 4;
|
|
ts_data->raw_data_frame_size =
|
|
128 - ts_data->coord_data_size - ts_data->area_data_size - 4 - 4 - 1;
|
|
|
|
if (ts_data->raw_data_frame_size == 0) {
|
|
E("%s: could NOT calculate!\n", __func__);
|
|
return;
|
|
}
|
|
|
|
I("%s: coord_dsz:%d,area_dsz:%d,raw_data_fsz:%d", __func__, ts_data->coord_data_size,
|
|
ts_data->area_data_size, ts_data->raw_data_frame_size);
|
|
}
|
|
|
|
static void calculate_point_number(struct himax_ts_data *ts)
|
|
{
|
|
ts->hx_touch_info_point_cnt = ts->ic_data->HX_MAX_PT * 4;
|
|
|
|
if ((ts->ic_data->HX_MAX_PT % 4) == 0)
|
|
ts->hx_touch_info_point_cnt += (ts->ic_data->HX_MAX_PT / 4) * 4;
|
|
else
|
|
ts->hx_touch_info_point_cnt += ((ts->ic_data->HX_MAX_PT / 4) + 1) * 4;
|
|
}
|
|
|
|
#if defined(HX_BOOT_UPGRADE)
|
|
/*-------------------------------------------------------------------------
|
|
*
|
|
* Create: Unknown
|
|
*
|
|
* Using: Read FW_VER and CFG_VER value from FW file and compare with
|
|
* FW/CFG version from MCU.
|
|
*
|
|
* param: None
|
|
*
|
|
* Dependency function: himax_mcu_fw_ver_bin
|
|
*
|
|
*/
|
|
static int himax_auto_update_check(struct himax_ts_data *ts)
|
|
{
|
|
int32_t ret;
|
|
bool is_testFW = false;
|
|
|
|
if ((0x80 & ((uint8_t)(ts->ic_data->vendor_config_ver >> 8))) == 0x80)
|
|
is_testFW = true;
|
|
|
|
I("%s: Entering!\n", __func__);
|
|
if (ts->core_fp.fp_fw_ver_bin(ts) == 0) {
|
|
I("%s:ic_data->vendor_arch_ver:%d,g_i_FW_VER:%d,ic_data->vendor_config_ver:%d,g_i_CFG_VER:%d\n",
|
|
__func__, ts->ic_data->vendor_arch_ver, ts->fw_ver,
|
|
ts->ic_data->vendor_config_ver, ts->cfg_ver);
|
|
if (((ts->ic_data->vendor_arch_ver < ts->fw_ver) ||
|
|
(ts->ic_data->vendor_config_ver < ts->cfg_ver)) &&
|
|
!is_testFW) {
|
|
I("%s: Need update\n", __func__);
|
|
ret = 0;
|
|
} else {
|
|
I("%s: Need not update!\n", __func__);
|
|
ret = 1;
|
|
}
|
|
} else {
|
|
E("%s: FW bin fail!\n", __func__);
|
|
ret = 1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int i_get_FW(struct himax_ts_data *ts)
|
|
{
|
|
int ret = -1;
|
|
int result = NO_ERR;
|
|
|
|
I("%s: file name = %s\n", __func__, ts->pdata->fw_name);
|
|
ret = request_firmware(&ts->hxfw, ts->pdata->fw_name, ts->dev);
|
|
if (ret < 0) {
|
|
#if defined(__EMBEDDED_FW__)
|
|
ts->hxfw = &g_embedded_fw;
|
|
I("%s: Not find FW in userspace, use embedded FW(size:%zu)", __func__,
|
|
g_embedded_fw.size);
|
|
#else
|
|
E("%s,%d: error code = %d\n", __func__, __LINE__, ret);
|
|
return OPEN_FILE_FAIL;
|
|
#endif
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int i_update_FW(struct himax_ts_data *ts)
|
|
{
|
|
int upgrade_times = 0;
|
|
int8_t ret = 0;
|
|
int8_t result = 0;
|
|
himax_int_enable(ts, 0);
|
|
|
|
update_retry:
|
|
|
|
if (ts->hxfw->size == FW_SIZE_64k)
|
|
ret = ts->core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_64k(
|
|
ts, (unsigned char *)ts->hxfw->data, ts->hxfw->size, false);
|
|
else if (ts->hxfw->size == FW_SIZE_128k)
|
|
ret = ts->core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_128k(
|
|
ts, (unsigned char *)ts->hxfw->data, ts->hxfw->size, false);
|
|
|
|
if (ret == 0) {
|
|
upgrade_times++;
|
|
E("%s: TP upgrade error, upgrade_times = %d\n", __func__, upgrade_times);
|
|
|
|
if (upgrade_times < 3)
|
|
goto update_retry;
|
|
} else if (ret == -1) {
|
|
E("%s: WP BP disable error \n", __func__);
|
|
} else {
|
|
ts->core_fp.fp_reload_disable(ts, 0);
|
|
ts->core_fp.fp_power_on_init(ts);
|
|
ts->core_fp.fp_read_FW_ver(ts);
|
|
ts->core_fp.fp_touch_information(ts);
|
|
/*upgrade success*/
|
|
I("%s: TP upgrade OK\n", __func__);
|
|
}
|
|
|
|
/* need to be review with FW update flow */
|
|
result = himax_mcu_WP_BP_enable(ts);
|
|
|
|
if (result < 0) {
|
|
I("%s: WP BP enable fail\n", __func__);
|
|
}
|
|
|
|
himax_int_enable(ts, 1);
|
|
return result;
|
|
}
|
|
#endif
|
|
/*
|
|
*static int himax_loadSensorConfig(struct himax_i2c_platform_data *pdata)
|
|
*{
|
|
* I("%s: initialization complete\n", __func__);
|
|
* return NO_ERR;
|
|
*}
|
|
*/
|
|
|
|
int himax_report_data_init(struct himax_ts_data *ts)
|
|
{
|
|
if (ts->hx_touch_data->hx_coord_buf != NULL) {
|
|
kfree(ts->hx_touch_data->hx_coord_buf);
|
|
ts->hx_touch_data->hx_coord_buf = NULL;
|
|
}
|
|
if (ts->hx_touch_data->hx_rawdata_buf != NULL) {
|
|
kfree(ts->hx_touch_data->hx_rawdata_buf);
|
|
ts->hx_touch_data->hx_rawdata_buf = NULL;
|
|
}
|
|
ts->hx_touch_data->touch_all_size = ts->core_fp.fp_get_touch_data_size(ts);
|
|
ts->hx_touch_data->raw_cnt_max = ts->ic_data->HX_MAX_PT / 4;
|
|
ts->hx_touch_data->raw_cnt_rmd = ts->ic_data->HX_MAX_PT % 4;
|
|
/* more than 4 fingers */
|
|
if (ts->hx_touch_data->raw_cnt_rmd != 0x00) {
|
|
ts->hx_touch_data->rawdata_size =
|
|
ts->core_fp.fp_cal_data_len(ts, ts->hx_touch_data->raw_cnt_rmd,
|
|
ts->ic_data->HX_MAX_PT,
|
|
ts->hx_touch_data->raw_cnt_max);
|
|
|
|
ts->hx_touch_data->touch_info_size =
|
|
(ts->ic_data->HX_MAX_PT + ts->hx_touch_data->raw_cnt_max + 2) * 4;
|
|
} else { /* less than 4 fingers */
|
|
ts->hx_touch_data->rawdata_size =
|
|
ts->core_fp.fp_cal_data_len(ts, ts->hx_touch_data->raw_cnt_rmd,
|
|
ts->ic_data->HX_MAX_PT,
|
|
ts->hx_touch_data->raw_cnt_max);
|
|
|
|
ts->hx_touch_data->touch_info_size =
|
|
(ts->ic_data->HX_MAX_PT + ts->hx_touch_data->raw_cnt_max + 1) * 4;
|
|
}
|
|
if ((ts->ic_data->HX_TX_NUM * ts->ic_data->HX_RX_NUM + ts->ic_data->HX_TX_NUM +
|
|
ts->ic_data->HX_RX_NUM) %
|
|
ts->hx_touch_data->rawdata_size ==
|
|
0)
|
|
ts->hx_touch_data->rawdata_frame_size =
|
|
(ts->ic_data->HX_TX_NUM * ts->ic_data->HX_RX_NUM + ts->ic_data->HX_TX_NUM +
|
|
ts->ic_data->HX_RX_NUM) /
|
|
ts->hx_touch_data->rawdata_size;
|
|
else
|
|
ts->hx_touch_data->rawdata_frame_size =
|
|
(ts->ic_data->HX_TX_NUM * ts->ic_data->HX_RX_NUM + ts->ic_data->HX_TX_NUM +
|
|
ts->ic_data->HX_RX_NUM) /
|
|
ts->hx_touch_data->rawdata_size +
|
|
1;
|
|
|
|
I("%s:rawdata_fsz = %d,HX_MAX_PT:%d,hx_raw_cnt_max:%d\n", __func__,
|
|
ts->hx_touch_data->rawdata_frame_size, ts->ic_data->HX_MAX_PT,
|
|
ts->hx_touch_data->raw_cnt_max);
|
|
I("%s:hx_raw_cnt_rmd:%d,g_hx_rawdata_size:%d,touch_info_size:%d\n", __func__,
|
|
ts->hx_touch_data->raw_cnt_rmd, ts->hx_touch_data->rawdata_size,
|
|
ts->hx_touch_data->touch_info_size);
|
|
|
|
ts->hx_touch_data->hx_coord_buf =
|
|
kzalloc(sizeof(uint8_t) * (ts->hx_touch_data->touch_info_size), GFP_KERNEL);
|
|
|
|
if (ts->hx_touch_data->hx_coord_buf == NULL)
|
|
goto mem_alloc_fail_coord_buf;
|
|
|
|
ts->hx_touch_data->hx_rawdata_buf =
|
|
kzalloc(sizeof(uint8_t) * (ts->hx_touch_data->touch_all_size -
|
|
ts->hx_touch_data->touch_info_size),
|
|
GFP_KERNEL);
|
|
if (ts->hx_touch_data->hx_rawdata_buf == NULL)
|
|
goto mem_alloc_fail_rawdata_buf;
|
|
|
|
if (ts->target_report_data == NULL) {
|
|
ts->target_report_data =
|
|
kzalloc(sizeof(struct himax_target_report_data), GFP_KERNEL);
|
|
if (ts->target_report_data == NULL)
|
|
goto mem_alloc_fail_report_data;
|
|
|
|
ts->target_report_data->x =
|
|
kzalloc(sizeof(int) * (ts->ic_data->HX_MAX_PT), GFP_KERNEL);
|
|
if (ts->target_report_data->x == NULL)
|
|
goto mem_alloc_fail_report_data_x;
|
|
|
|
ts->target_report_data->y =
|
|
kzalloc(sizeof(int) * (ts->ic_data->HX_MAX_PT), GFP_KERNEL);
|
|
if (ts->target_report_data->y == NULL)
|
|
goto mem_alloc_fail_report_data_y;
|
|
|
|
ts->target_report_data->w =
|
|
kzalloc(sizeof(int) * (ts->ic_data->HX_MAX_PT), GFP_KERNEL);
|
|
if (ts->target_report_data->w == NULL)
|
|
goto mem_alloc_fail_report_data_w;
|
|
|
|
ts->target_report_data->finger_id =
|
|
kzalloc(sizeof(int) * (ts->ic_data->HX_MAX_PT), GFP_KERNEL);
|
|
if (ts->target_report_data->finger_id == NULL)
|
|
goto mem_alloc_fail_report_data_fid;
|
|
}
|
|
|
|
return NO_ERR;
|
|
|
|
mem_alloc_fail_report_data_fid:
|
|
kfree(ts->target_report_data->w);
|
|
ts->target_report_data->w = NULL;
|
|
mem_alloc_fail_report_data_w:
|
|
kfree(ts->target_report_data->y);
|
|
ts->target_report_data->y = NULL;
|
|
mem_alloc_fail_report_data_y:
|
|
kfree(ts->target_report_data->x);
|
|
ts->target_report_data->x = NULL;
|
|
mem_alloc_fail_report_data_x:
|
|
kfree(ts->target_report_data);
|
|
ts->target_report_data = NULL;
|
|
mem_alloc_fail_report_data:
|
|
kfree(ts->hx_touch_data->hx_rawdata_buf);
|
|
ts->hx_touch_data->hx_rawdata_buf = NULL;
|
|
mem_alloc_fail_rawdata_buf:
|
|
kfree(ts->hx_touch_data->hx_coord_buf);
|
|
ts->hx_touch_data->hx_coord_buf = NULL;
|
|
mem_alloc_fail_coord_buf:
|
|
|
|
E("%s: Failed to allocate memory\n", __func__);
|
|
return MEM_ALLOC_FAIL;
|
|
}
|
|
EXPORT_SYMBOL(himax_report_data_init);
|
|
|
|
static void himax_report_data_deinit(struct himax_ts_data *ts)
|
|
{
|
|
kfree(ts->target_report_data->finger_id);
|
|
ts->target_report_data->finger_id = NULL;
|
|
kfree(ts->target_report_data->w);
|
|
ts->target_report_data->w = NULL;
|
|
kfree(ts->target_report_data->y);
|
|
ts->target_report_data->y = NULL;
|
|
kfree(ts->target_report_data->x);
|
|
ts->target_report_data->x = NULL;
|
|
kfree(ts->target_report_data);
|
|
ts->target_report_data = NULL;
|
|
kfree(ts->hx_touch_data->hx_rawdata_buf);
|
|
ts->hx_touch_data->hx_rawdata_buf = NULL;
|
|
kfree(ts->hx_touch_data->hx_coord_buf);
|
|
ts->hx_touch_data->hx_coord_buf = NULL;
|
|
}
|
|
|
|
static int himax_ts_work_status(struct himax_ts_data *ts)
|
|
{
|
|
/* 1: normal */
|
|
int result = HX_REPORT_COORD;
|
|
|
|
ts->hx_touch_data->diag_cmd = ts->diag_cmd;
|
|
if (ts->hx_touch_data->diag_cmd)
|
|
result = HX_REPORT_COORD_RAWDATA;
|
|
|
|
/* I("Now Status is %d\n", result); */
|
|
return result;
|
|
}
|
|
|
|
static int himax_touch_get(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status)
|
|
{
|
|
if (ts->ts_dbg != 0)
|
|
I("%s: Entering, ts_status=%d!\n", __func__, ts_status);
|
|
|
|
switch (ts_path) {
|
|
/*normal*/
|
|
case HX_REPORT_COORD:
|
|
if (ts->hx_hw_reset_activate) {
|
|
if (!ts->core_fp.fp_read_event_stack(ts, buf, 128)) {
|
|
E("%s: can't read data from chip!\n", __func__);
|
|
ts_status = HX_TS_GET_DATA_FAIL;
|
|
}
|
|
} else {
|
|
if (!ts->core_fp.fp_read_event_stack(ts, buf,
|
|
ts->hx_touch_data->touch_info_size)) {
|
|
E("%s: can't read data from chip!\n", __func__);
|
|
ts_status = HX_TS_GET_DATA_FAIL;
|
|
}
|
|
}
|
|
break;
|
|
case HX_REPORT_COORD_RAWDATA:
|
|
if (!ts->core_fp.fp_read_event_stack(ts, buf, 128)) {
|
|
E("%s: can't read data from chip!\n", __func__);
|
|
ts_status = HX_TS_GET_DATA_FAIL;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ts_status;
|
|
}
|
|
|
|
/* start error_control*/
|
|
static int himax_checksum_cal(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status)
|
|
{
|
|
uint16_t check_sum_cal = 0;
|
|
int32_t i = 0;
|
|
int length = 0;
|
|
int zero_cnt = 0;
|
|
int raw_data_sel = 0;
|
|
int ret_val = ts_status;
|
|
|
|
if (ts->ts_dbg != 0)
|
|
I("%s: Entering, ts_status=%d!\n", __func__, ts_status);
|
|
|
|
/* Normal */
|
|
switch (ts_path) {
|
|
case HX_REPORT_COORD:
|
|
length = ts->hx_touch_data->touch_info_size;
|
|
break;
|
|
case HX_REPORT_COORD_RAWDATA:
|
|
length = ts->hx_touch_data->touch_info_size;
|
|
break;
|
|
default:
|
|
I("%s, Normal error!\n", __func__);
|
|
ret_val = HX_PATH_FAIL;
|
|
goto END_FUNCTION;
|
|
}
|
|
|
|
for (i = 0; i < length; i++) {
|
|
check_sum_cal += buf[i];
|
|
if (buf[i] == 0x00)
|
|
zero_cnt++;
|
|
}
|
|
|
|
if (check_sum_cal % 0x100 != 0) {
|
|
I("point data_checksum not match check_sum_cal: 0x%02X", check_sum_cal);
|
|
ret_val = HX_CHKSUM_FAIL;
|
|
} else if (zero_cnt == length) {
|
|
if (ts->use_irq)
|
|
I("[HIMAX TP MSG] All Zero event\n");
|
|
|
|
ret_val = HX_CHKSUM_FAIL;
|
|
} else {
|
|
raw_data_sel = buf[ts->hx_touch_info_point_cnt] >> 4 & 0x0F;
|
|
/*I("%s:raw_out_sel=%x , hx_touch_data->diag_cmd=%x.\n",*/
|
|
/* __func__, raw_data_sel,*/
|
|
/* ts->hx_touch_data->diag_cmd);*/
|
|
/*raw data out not match skip it*/
|
|
if ((raw_data_sel != 0x0F) && (raw_data_sel != ts->hx_touch_data->diag_cmd)) {
|
|
/*I("%s:raw data out not match.\n", __func__);*/
|
|
if (!ts->hx_touch_data->diag_cmd) {
|
|
/*Need to clear event stack here*/
|
|
ts->core_fp.fp_read_event_stack(
|
|
ts, buf, (128 - ts->hx_touch_data->touch_info_size));
|
|
/*I("%s: size =%d, buf[0]=%x ,buf[1]=%x,*/
|
|
/* buf[2]=%x, buf[3]=%x.\n",*/
|
|
/* __func__,*/
|
|
/* (128-ts->hx_touch_data->touch_info_size),*/
|
|
/* buf[0], buf[1], buf[2], buf[3]);*/
|
|
/*I("%s:also clear event stack.\n", __func__);*/
|
|
}
|
|
ret_val = HX_READY_SERVE;
|
|
}
|
|
}
|
|
|
|
END_FUNCTION:
|
|
if (ts->ts_dbg != 0)
|
|
I("%s: END, ret_val=%d!\n", __func__, ret_val);
|
|
return ret_val;
|
|
}
|
|
|
|
static int himax_err_ctrl(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status)
|
|
{
|
|
#if defined(HX_RST_PIN_FUNC)
|
|
if (ts->hx_hw_reset_activate) {
|
|
/* drop 1st interrupts after chip reset */
|
|
ts->hx_hw_reset_activate = 0;
|
|
I("[HX_HW_RESET_ACTIVATE]%s:Back from reset,ready to serve.\n", __func__);
|
|
ts_status = HX_RST_OK;
|
|
goto END_FUNCTION;
|
|
}
|
|
#endif
|
|
|
|
ts_status = himax_checksum_cal(ts, buf, ts_path, ts_status);
|
|
|
|
END_FUNCTION:
|
|
if (ts->ts_dbg != 0)
|
|
I("%s: END, ts_status=%d!\n", __func__, ts_status);
|
|
return ts_status;
|
|
}
|
|
/* end error_control*/
|
|
|
|
/* start distribute_data*/
|
|
static int himax_distribute_touch_data(struct himax_ts_data *ts, uint8_t *buf, int ts_path,
|
|
int ts_status)
|
|
{
|
|
uint8_t hx_state_info_pos = ts->hx_touch_data->touch_info_size - 3;
|
|
|
|
if (ts->ts_dbg != 0)
|
|
I("%s: Entering, ts_status=%d!\n", __func__, ts_status);
|
|
|
|
if (ts_path == HX_REPORT_COORD) {
|
|
memcpy(ts->hx_touch_data->hx_coord_buf, &buf[0],
|
|
ts->hx_touch_data->touch_info_size);
|
|
|
|
if (buf[hx_state_info_pos] != 0xFF && buf[hx_state_info_pos + 1] != 0xFF)
|
|
memcpy(ts->hx_touch_data->hx_state_info, &buf[hx_state_info_pos], 2);
|
|
else
|
|
memset(ts->hx_touch_data->hx_state_info, 0x00,
|
|
sizeof(ts->hx_touch_data->hx_state_info));
|
|
|
|
if ((ts->hx_hw_reset_activate)
|
|
|
|
) {
|
|
memcpy(ts->hx_touch_data->hx_rawdata_buf,
|
|
&buf[ts->hx_touch_data->touch_info_size],
|
|
ts->hx_touch_data->touch_all_size -
|
|
ts->hx_touch_data->touch_info_size);
|
|
}
|
|
} else if (ts_path == HX_REPORT_COORD_RAWDATA) {
|
|
memcpy(ts->hx_touch_data->hx_coord_buf, &buf[0],
|
|
ts->hx_touch_data->touch_info_size);
|
|
|
|
if (buf[hx_state_info_pos] != 0xFF && buf[hx_state_info_pos + 1] != 0xFF)
|
|
memcpy(ts->hx_touch_data->hx_state_info, &buf[hx_state_info_pos], 2);
|
|
else
|
|
memset(ts->hx_touch_data->hx_state_info, 0x00,
|
|
sizeof(ts->hx_touch_data->hx_state_info));
|
|
|
|
memcpy(ts->hx_touch_data->hx_rawdata_buf, &buf[ts->hx_touch_data->touch_info_size],
|
|
ts->hx_touch_data->touch_all_size - ts->hx_touch_data->touch_info_size);
|
|
|
|
} else {
|
|
E("%s, Fail Path!\n", __func__);
|
|
ts_status = HX_PATH_FAIL;
|
|
}
|
|
|
|
if (ts->ts_dbg != 0)
|
|
I("%s: End, ts_status=%d!\n", __func__, ts_status);
|
|
return ts_status;
|
|
}
|
|
/* end assign_data*/
|
|
|
|
/* start parse_report_data*/
|
|
static int himax_parse_report_points(struct himax_ts_data *ts, int ts_path, int ts_status)
|
|
{
|
|
int x = 0, y = 0, w = 0;
|
|
int base = 0;
|
|
int event_id = 0;
|
|
int32_t loop_i = 0;
|
|
|
|
if (ts->ts_dbg != 0)
|
|
I("%s: start!\n", __func__);
|
|
|
|
ts->old_finger = ts->pre_finger_mask;
|
|
if (ts->hx_point_num == 0) {
|
|
if (ts->ts_dbg != 0)
|
|
I("%s: hx_point_num = 0!\n", __func__);
|
|
return ts_status;
|
|
}
|
|
ts->pre_finger_mask = 0;
|
|
ts->hx_touch_data->finger_num =
|
|
ts->hx_touch_data->hx_coord_buf[ts->coordInfoSize - 4] & 0x0F;
|
|
ts->hx_touch_data->finger_on = 1;
|
|
ts->aa_press = 1;
|
|
|
|
ts->target_report_data->finger_num = ts->hx_touch_data->finger_num;
|
|
ts->target_report_data->finger_on = ts->hx_touch_data->finger_on;
|
|
ts->target_report_data->ig_count = ts->hx_touch_data->hx_coord_buf[ts->coordInfoSize - 5];
|
|
|
|
if (ts->ts_dbg != 0)
|
|
I("%s:finger_num = 0x%2X, finger_on = %d\n", __func__,
|
|
ts->target_report_data->finger_num, ts->target_report_data->finger_on);
|
|
|
|
for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) {
|
|
base = loop_i * 4;
|
|
x = ts->hx_touch_data->hx_coord_buf[base] << 8 |
|
|
ts->hx_touch_data->hx_coord_buf[base + 1];
|
|
y = (ts->hx_touch_data->hx_coord_buf[base + 2] << 8 |
|
|
ts->hx_touch_data->hx_coord_buf[base + 3]);
|
|
|
|
w = ts->hx_touch_data->hx_coord_buf[(ts->nFinger_support * 4) + loop_i];
|
|
|
|
if (ts->ts_dbg != 0)
|
|
D("%s: now parsing[%d]:x=%d, y=%d, w=%d\n", __func__, loop_i, x, y, w);
|
|
|
|
if (ts->ic_data->HX_IS_ID_EN) {
|
|
x = (ts->hx_touch_data->hx_coord_buf[base] & 0x3F) << 8 |
|
|
ts->hx_touch_data->hx_coord_buf[base + 1];
|
|
event_id = ts->hx_touch_data->hx_coord_buf[base] >> 0x06;
|
|
/*if ((event_id == 0) || (event_id == 3)) {*/ /*No touch event or Leave event*/
|
|
if (event_id == 3) { /*No touch event or Leave event*/
|
|
x = 0xFFFF;
|
|
y = 0xFFFF;
|
|
}
|
|
I("%s: now parsing[%d]:x=%d, y=%d, event_id=%d\n", __func__, loop_i, x, y,
|
|
event_id);
|
|
}
|
|
|
|
if (x >= 0 && x <= ts->pdata->abs_x_max && y >= 0 && y <= ts->pdata->abs_y_max) {
|
|
ts->hx_touch_data->finger_num--;
|
|
|
|
ts->target_report_data->x[loop_i] = x;
|
|
ts->target_report_data->y[loop_i] = y;
|
|
ts->target_report_data->w[loop_i] = w;
|
|
ts->target_report_data->finger_id[loop_i] = 1;
|
|
|
|
/*I("%s: g_target_report_data->x[loop_i]=%d,*/
|
|
/*g_target_report_data->y[loop_i]=%d,*/
|
|
/*g_target_report_data->w[loop_i]=%d",*/
|
|
/*__func__, ts->target_report_data->x[loop_i],*/
|
|
/*ts->target_report_data->y[loop_i],*/
|
|
/*ts->target_report_data->w[loop_i]); */
|
|
|
|
if (!ts->first_pressed) {
|
|
ts->first_pressed = 1;
|
|
I("S1@%d, %d\n", x, y);
|
|
}
|
|
|
|
ts->pre_finger_data[loop_i][0] = x;
|
|
ts->pre_finger_data[loop_i][1] = y;
|
|
|
|
ts->pre_finger_mask = ts->pre_finger_mask + (1 << loop_i);
|
|
} else { /* report coordinates */
|
|
ts->target_report_data->x[loop_i] = x;
|
|
ts->target_report_data->y[loop_i] = y;
|
|
ts->target_report_data->w[loop_i] = w;
|
|
ts->target_report_data->finger_id[loop_i] = 0;
|
|
|
|
if (loop_i == 0 && ts->first_pressed == 1) {
|
|
ts->first_pressed = 2;
|
|
I("E1@%d, %d\n", ts->pre_finger_data[0][0],
|
|
ts->pre_finger_data[0][1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ts->ts_dbg != 0) {
|
|
for (loop_i = 0; loop_i < 10; loop_i++)
|
|
D("DBG X=%d Y=%d ID=%d\n", ts->target_report_data->x[loop_i],
|
|
ts->target_report_data->y[loop_i],
|
|
ts->target_report_data->finger_id[loop_i]);
|
|
|
|
D("DBG finger number %d\n", ts->target_report_data->finger_num);
|
|
}
|
|
|
|
if (ts->ts_dbg != 0)
|
|
I("%s: end!\n", __func__);
|
|
return ts_status;
|
|
}
|
|
|
|
static int himax_parse_report_data(struct himax_ts_data *ts, int ts_path, int ts_status)
|
|
{
|
|
if (ts->ts_dbg != 0)
|
|
I("%s: start now_status=%d!\n", __func__, ts_status);
|
|
|
|
ts->en_noisefilter =
|
|
(ts->hx_touch_data->hx_coord_buf[ts->hx_touch_info_point_cnt + 2] >> 3);
|
|
/* I("EN_NoiseFilter=%d\n", ts->en_noisefilter); */
|
|
ts->en_noisefilter = ts->en_noisefilter & 0x01;
|
|
/* I("EN_NoiseFilter2=%d\n", ts->en_noisefilter); */
|
|
ts->p_point_num = ts->hx_point_num;
|
|
|
|
if (ts->hx_touch_data->hx_coord_buf[ts->hx_touch_info_point_cnt] == 0xff)
|
|
ts->hx_point_num = 0;
|
|
else
|
|
ts->hx_point_num =
|
|
ts->hx_touch_data->hx_coord_buf[ts->hx_touch_info_point_cnt] & 0x0f;
|
|
|
|
switch (ts_path) {
|
|
case HX_REPORT_COORD:
|
|
ts_status = himax_parse_report_points(ts, ts_path, ts_status);
|
|
break;
|
|
case HX_REPORT_COORD_RAWDATA:
|
|
/* touch monitor rawdata */
|
|
if (ts->debug.ops.fp_set_diag_cmd) {
|
|
ts->debug.ops.fp_set_diag_cmd(ts, ts->ic_data, ts->hx_touch_data);
|
|
I("%s:raw data_checksum not match\n", __func__);
|
|
} else {
|
|
E("%s,There is no init set_diag_cmd\n", __func__);
|
|
}
|
|
|
|
ts_status = himax_parse_report_points(ts, ts_path, ts_status);
|
|
break;
|
|
|
|
default:
|
|
E("%s:Fail Path!\n", __func__);
|
|
ts_status = HX_PATH_FAIL;
|
|
break;
|
|
}
|
|
if (ts->ts_dbg != 0)
|
|
I("%s: end now_status=%d!\n", __func__, ts_status);
|
|
return ts_status;
|
|
}
|
|
|
|
/* end parse_report_data*/
|
|
|
|
static void himax_report_all_leave_event(struct himax_ts_data *ts)
|
|
{
|
|
int loop_i = 0;
|
|
|
|
for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) {
|
|
#if !defined(HX_PROTOCOL_A)
|
|
input_mt_slot(ts->input_dev, loop_i);
|
|
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
|
|
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0);
|
|
input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0);
|
|
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0);
|
|
#endif
|
|
}
|
|
input_report_key(ts->input_dev, BTN_TOUCH, 0);
|
|
input_sync(ts->input_dev);
|
|
}
|
|
|
|
/* start report_point*/
|
|
static void himax_finger_report(struct himax_ts_data *ts)
|
|
{
|
|
int i = 0;
|
|
bool valid = false;
|
|
|
|
if (ts->ts_dbg != 0) {
|
|
I("%s:start hx_touch_data->finger_num=%d\n", __func__,
|
|
ts->hx_touch_data->finger_num);
|
|
}
|
|
for (i = 0; i < ts->nFinger_support; i++) {
|
|
if (ts->target_report_data->x[i] >= 0 &&
|
|
ts->target_report_data->x[i] <= ts->pdata->abs_x_max &&
|
|
ts->target_report_data->y[i] >= 0 &&
|
|
ts->target_report_data->y[i] <= ts->pdata->abs_y_max)
|
|
valid = true;
|
|
else
|
|
valid = false;
|
|
if (ts->ts_dbg != 0)
|
|
I("valid=%d\n", valid);
|
|
if (valid) {
|
|
if (ts->ts_dbg != 0) {
|
|
I("report_data->x[i]=%d,y[i]=%d,w[i]=%d",
|
|
ts->target_report_data->x[i], ts->target_report_data->y[i],
|
|
ts->target_report_data->w[i]);
|
|
}
|
|
#if !defined(HX_PROTOCOL_A)
|
|
input_mt_slot(ts->input_dev, i);
|
|
#else
|
|
input_report_key(ts->input_dev, BTN_TOUCH, 1);
|
|
#endif
|
|
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
|
|
ts->target_report_data->w[i]);
|
|
#if !defined(HX_PROTOCOL_A)
|
|
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR,
|
|
ts->target_report_data->w[i]);
|
|
input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
|
|
ts->target_report_data->w[i]);
|
|
#else
|
|
input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, i);
|
|
#endif
|
|
input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
|
|
ts->target_report_data->x[i]);
|
|
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
|
|
ts->target_report_data->y[i]);
|
|
#if !defined(HX_PROTOCOL_A)
|
|
ts->last_slot = i;
|
|
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1);
|
|
pr_debug("[HXTP]Finger down,report_data->x[i]=%d,y[i]=%d,w[i]=%d",
|
|
ts->target_report_data->x[i], ts->target_report_data->y[i],
|
|
ts->target_report_data->w[i]);
|
|
if (ts->touch_num >= 50) {
|
|
I("Finger down,report_data->x[i]=%d,y[i]=%d,w[i]=%d",
|
|
ts->target_report_data->x[i], ts->target_report_data->y[i],
|
|
ts->target_report_data->w[i]);
|
|
ts->touch_num = 0;
|
|
}
|
|
#else
|
|
input_mt_sync(ts->input_dev);
|
|
#endif
|
|
} else {
|
|
#if !defined(HX_PROTOCOL_A)
|
|
input_mt_slot(ts->input_dev, i);
|
|
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
|
|
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0);
|
|
input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0);
|
|
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0);
|
|
#endif
|
|
}
|
|
}
|
|
#if !defined(HX_PROTOCOL_A)
|
|
input_report_key(ts->input_dev, BTN_TOUCH, 1);
|
|
#endif
|
|
input_sync(ts->input_dev);
|
|
|
|
if (ts->ts_dbg != 0)
|
|
I("%s:end\n", __func__);
|
|
}
|
|
|
|
static void himax_finger_leave(struct himax_ts_data *ts)
|
|
{
|
|
#if !defined(HX_PROTOCOL_A)
|
|
int32_t loop_i = 0;
|
|
#endif
|
|
|
|
if (ts->ts_dbg != 0)
|
|
I("%s: start!\n", __func__);
|
|
|
|
ts->hx_touch_data->finger_on = 0;
|
|
ts->target_report_data->finger_on = 0;
|
|
ts->target_report_data->finger_num = 0;
|
|
ts->aa_press = 0;
|
|
|
|
#if defined(HX_PROTOCOL_A)
|
|
input_mt_sync(ts->input_dev);
|
|
#endif
|
|
#if !defined(HX_PROTOCOL_A)
|
|
for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) {
|
|
input_mt_slot(ts->input_dev, loop_i);
|
|
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
|
|
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0);
|
|
input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0);
|
|
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0);
|
|
}
|
|
#endif
|
|
if (ts->pre_finger_mask > 0)
|
|
ts->pre_finger_mask = 0;
|
|
|
|
if (ts->first_pressed == 1) {
|
|
ts->first_pressed = 2;
|
|
I("E1@%d, %d\n", ts->pre_finger_data[0][0], ts->pre_finger_data[0][1]);
|
|
}
|
|
|
|
/*if (ts->debug_log_level & BIT(1))*/
|
|
/* himax_log_touch_event(x, y, w, loop_i, ts->en_noisefilter,*/
|
|
/* HX_FINGER_LEAVE); */
|
|
|
|
input_report_key(ts->input_dev, BTN_TOUCH, 0);
|
|
input_sync(ts->input_dev);
|
|
ts->touch_num++;
|
|
|
|
if (ts->ts_dbg != 0)
|
|
I("%s: end!\n", __func__);
|
|
}
|
|
|
|
static void himax_report_points(struct himax_ts_data *ts)
|
|
{
|
|
if (ts->ts_dbg != 0)
|
|
I("%s: start!\n", __func__);
|
|
|
|
if (ts->hx_point_num != 0)
|
|
himax_finger_report(ts);
|
|
else
|
|
himax_finger_leave(ts);
|
|
|
|
ts->last_en_noisefilter = ts->en_noisefilter;
|
|
|
|
if (ts->ts_dbg != 0)
|
|
I("%s: end!\n", __func__);
|
|
}
|
|
/* end report_points*/
|
|
|
|
int himax_report_data(struct himax_ts_data *ts, int ts_path, int ts_status)
|
|
{
|
|
if (ts->ts_dbg != 0)
|
|
I("%s: Entering, ts_status=%d!\n", __func__, ts_status);
|
|
|
|
if (ts_path == HX_REPORT_COORD || ts_path == HX_REPORT_COORD_RAWDATA) {
|
|
/* Touch Point information */
|
|
himax_report_points(ts);
|
|
|
|
} else {
|
|
E("%s:Fail Path!\n", __func__);
|
|
ts_status = HX_PATH_FAIL;
|
|
}
|
|
|
|
if (ts->ts_dbg != 0)
|
|
I("%s: END, ts_status=%d!\n", __func__, ts_status);
|
|
return ts_status;
|
|
}
|
|
/* end report_data */
|
|
|
|
static int himax_ts_operation(struct himax_ts_data *ts, int ts_path, int ts_status)
|
|
{
|
|
uint8_t hw_reset_check[2];
|
|
#if defined(FCA_PROTOCOL_EN)
|
|
uint8_t loop_i;
|
|
uint8_t Finger_i = 0;
|
|
uint16_t x_coord[5];
|
|
uint16_t y_coord[5];
|
|
memset(x_coord, 0xFFFF, 5 * sizeof(uint16_t));
|
|
memset(y_coord, 0xFFFF, 5 * sizeof(uint16_t));
|
|
#endif
|
|
memset(ts->xfer_buff, 0x00, 128 * sizeof(uint8_t));
|
|
memset(hw_reset_check, 0x00, sizeof(hw_reset_check));
|
|
|
|
ts_status = himax_touch_get(ts, ts->xfer_buff, ts_path, ts_status);
|
|
if (ts_status == HX_TS_GET_DATA_FAIL)
|
|
goto END_FUNCTION;
|
|
#if !defined(FCA_PROTOCOL_EN)
|
|
ts_status = himax_distribute_touch_data(ts, ts->xfer_buff, ts_path, ts_status);
|
|
ts_status = himax_err_ctrl(ts, ts->xfer_buff, ts_path, ts_status);
|
|
|
|
if (ts_status == HX_REPORT_DATA || ts_status == HX_TS_NORMAL_END)
|
|
ts_status = himax_parse_report_data(ts, ts_path, ts_status);
|
|
else
|
|
goto END_FUNCTION;
|
|
|
|
ts_status = himax_report_data(ts, ts_path, ts_status);
|
|
|
|
#else
|
|
for (loop_i = 0; loop_i < 25; loop_i++) {
|
|
if (loop_i == 0)
|
|
I("%s: Msg_cnt = 0x%2X\n", __func__, ts->xfer_buff[0]);
|
|
else if ((loop_i % 4) == 0 && loop_i < 24) {
|
|
if (ts->xfer_buff[loop_i] >> 7) {
|
|
x_coord[Finger_i] =
|
|
(((ts->xfer_buff[loop_i] & 0x07) << 8) |
|
|
ts->xfer_buff[loop_i - 1]); /* (HI & 0x07) >> 0x08 | LO*/
|
|
y_coord[Finger_i++] = (((ts->xfer_buff[loop_i + 2] & 0x07) << 8) |
|
|
ts->xfer_buff[loop_i + 1]);
|
|
}
|
|
}
|
|
}
|
|
if (!Finger_i) {
|
|
himax_finger_leave(ts);
|
|
} else {
|
|
for (loop_i = 0; loop_i < Finger_i; loop_i++) {
|
|
if (x_coord[loop_i] != 0xFFFF) {
|
|
I("x_coord[%d] = %2d\n", loop_i, x_coord[loop_i]);
|
|
I("y_coord[%d] = %2d\n", loop_i, y_coord[loop_i]);
|
|
}
|
|
input_mt_slot(ts->input_dev,
|
|
loop_i); //input_mt_slot(input, 0);
|
|
input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 1);
|
|
input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_coord[loop_i]);
|
|
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_coord[loop_i]);
|
|
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1);
|
|
input_report_key(ts->input_dev, BTN_TOUCH, 1);
|
|
input_sync(ts->input_dev);
|
|
}
|
|
}
|
|
|
|
goto END_FUNCTION;
|
|
#endif
|
|
|
|
END_FUNCTION:
|
|
return ts_status;
|
|
}
|
|
|
|
void himax_fail_det_work(struct himax_ts_data *ts)
|
|
{
|
|
uint8_t data[8] = { 0 };
|
|
/* uint8_t addr[4] = {0xD4, 0x74, 0x00, 0x10};
|
|
Clear Simulation Register
|
|
himax_mcu_register_write(ts, addr, DATA_LEN_4, data, 0);
|
|
I("%s Clear: addr[0]=0x%2.2X,addr[1]=0x%2.2X,addr[2]=0x%2.2X, addr[3]=0x%2.2X\n",
|
|
__func__,addr[0],addr[1],addr[2],addr[3]);
|
|
*/
|
|
ts->core_fp.fp_dd_clk_set(ts, true);
|
|
ts->core_fp.fp_dd_reg_en(ts, true);
|
|
|
|
ts->core_fp.fp_dd_reg_read(ts, 0xE5, 0, 8, data, 0);
|
|
I("%s E5_Bank0: para[0]=0x%2.2X,para[1]=0x%2.2X,para[2]=0x%2.2X, para[3]=0x%2.2X\n",
|
|
__func__, data[0], data[1], data[2], data[3]);
|
|
I("%s E5_Bank0: para[4]=0x%2.2X,para[5]=0x%2.2X,para[6]=0x%2.2X, para[7]=0x%2.2X\n",
|
|
__func__, data[4], data[5], data[6], data[7]);
|
|
|
|
ts->core_fp.fp_dd_reg_read(ts, 0xE5, 0, 8, data, 1);
|
|
I("%s E5_Bank1: para[0]=0x%2.2X,para[1]=0x%2.2X,para[2]=0x%2.2X, para[3]=0x%2.2X\n",
|
|
__func__, data[0], data[1], data[2], data[3]);
|
|
I("%s E5_Bank1: para[4]=0x%2.2X,para[5]=0x%2.2X,para[6]=0x%2.2X, para[7]=0x%2.2X\n",
|
|
__func__, data[4], data[5], data[6], data[7]);
|
|
|
|
ts->core_fp.fp_dd_clk_set(ts, false);
|
|
|
|
/* It depends on customer: */
|
|
|
|
goto AP_recovery;
|
|
|
|
AP_recovery:
|
|
|
|
I("%s: Now FAIL_DET tie high means IC need external recovery\n", __func__);
|
|
|
|
himax_mcu_tp_lcm_pin_reset(ts);
|
|
}
|
|
|
|
void himax_ts_work(struct himax_ts_data *ts)
|
|
{
|
|
int ts_status = HX_TS_NORMAL_END;
|
|
int ts_path = 0;
|
|
|
|
if (ts->debug.ops.fp_ts_dbg_func)
|
|
ts->debug.ops.fp_ts_dbg_func(ts, HX_FINGER_ON);
|
|
|
|
ts_path = himax_ts_work_status(ts);
|
|
switch (ts_path) {
|
|
case HX_REPORT_COORD:
|
|
ts_status = himax_ts_operation(ts, ts_path, ts_status);
|
|
break;
|
|
case HX_REPORT_COORD_RAWDATA:
|
|
ts_status = himax_ts_operation(ts, ts_path, ts_status);
|
|
break;
|
|
default:
|
|
E("%s:Path Fault! value=%d\n", __func__, ts_path);
|
|
goto END_FUNCTION;
|
|
}
|
|
|
|
if (ts_status == HX_TS_GET_DATA_FAIL)
|
|
goto GET_TOUCH_FAIL;
|
|
else
|
|
goto END_FUNCTION;
|
|
|
|
GET_TOUCH_FAIL:
|
|
I("%s: Now reset the Touch chip.\n", __func__);
|
|
#if defined(HX_RST_PIN_FUNC)
|
|
if (HX_SYSTEM_RESET == 0)
|
|
ts->core_fp.fp_ic_reset(ts, false, true);
|
|
else
|
|
ts->core_fp.fp_system_reset(ts);
|
|
#endif
|
|
END_FUNCTION:
|
|
if (ts->debug.ops.fp_ts_dbg_func)
|
|
ts->debug.ops.fp_ts_dbg_func(ts, HX_FINGER_LEAVE);
|
|
}
|
|
/*end ts_work*/
|
|
enum hrtimer_restart himax_ts_timer_func(struct hrtimer *timer)
|
|
{
|
|
struct himax_ts_data *ts;
|
|
|
|
ts = container_of(timer, struct himax_ts_data, timer);
|
|
queue_work(ts->himax_wq, &ts->work);
|
|
hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL);
|
|
return HRTIMER_NORESTART;
|
|
}
|
|
|
|
#if defined(HX_BOOT_UPGRADE)
|
|
static void himax_boot_upgrade(struct work_struct *work)
|
|
{
|
|
struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work_boot_upgrade.work);
|
|
if (i_get_FW(ts) != 0)
|
|
return;
|
|
|
|
ts->core_fp.fp_bin_desc_get(ts, (unsigned char *)ts->hxfw->data, HX1K);
|
|
|
|
if (ts->boot_upgrade_flag == true) {
|
|
I("%s: Forced upgrade\n", __func__);
|
|
goto UPDATE_FW;
|
|
}
|
|
|
|
if (himax_auto_update_check(ts) != 0)
|
|
goto SKIP_UPDATE_FW;
|
|
|
|
UPDATE_FW:
|
|
if (i_update_FW(ts) <= 0)
|
|
E("%s: Update FW fail\n", __func__);
|
|
else
|
|
I("%s: Update FW success\n", __func__);
|
|
|
|
SKIP_UPDATE_FW:
|
|
release_firmware(ts->hxfw);
|
|
ts->hxfw = NULL;
|
|
}
|
|
#endif
|
|
|
|
static int hx_chk_flash_sts(struct himax_ts_data *ts)
|
|
{
|
|
int rslt = 0;
|
|
|
|
I("%s: Entering\n", __func__);
|
|
|
|
rslt = (!ts->core_fp.fp_calculateChecksum(ts, false, FW_SIZE_128k));
|
|
/*avoid the FW is full of zero*/
|
|
rslt |= ts->core_fp.fp_flash_lastdata_check(ts, FW_SIZE_128k);
|
|
|
|
return rslt;
|
|
}
|
|
|
|
#if defined(HX_CONFIG_FB) || defined(HX_CONFIG_DRM)
|
|
static void himax_fb_register(struct work_struct *work)
|
|
{
|
|
int ret = 0;
|
|
|
|
struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work_att.work);
|
|
|
|
I("%s in\n", __func__);
|
|
#if defined(HX_CONFIG_FB)
|
|
ts->fb_notif.notifier_call = himax_fb_notifier_callback;
|
|
ret = fb_register_client(&ts->fb_notif);
|
|
#elif defined(HX_CONFIG_DRM)
|
|
ts->fb_notif.notifier_call = himax_drm_notifier_callback;
|
|
ret = msm_drm_register_client(&ts->fb_notif);
|
|
#endif
|
|
if (ret)
|
|
E("Unable to register fb_notifier: %d\n", ret);
|
|
}
|
|
#endif
|
|
|
|
#if defined(HX_CONTAINER_SPEED_UP)
|
|
static void himax_resume_work_func(struct work_struct *work)
|
|
{
|
|
struct himax_ts_data *ts = container_of(work, struct himax_ts_data, ts_int_work.work);
|
|
|
|
himax_chip_common_resume(ts);
|
|
}
|
|
#endif
|
|
|
|
int himax_chip_common_init(struct himax_ts_data *ts)
|
|
{
|
|
int ret = 0;
|
|
int err = PROBE_FAIL;
|
|
struct himax_i2c_platform_data *pdata;
|
|
struct himax_chip_entry *entry;
|
|
|
|
ts->p_point_num = 0xFFFF;
|
|
|
|
#if defined(__EMBEDDED_FW__)
|
|
g_embedded_fw.size = (size_t)_binary___Himax_firmware_bin_end -
|
|
(size_t)_binary___Himax_firmware_bin_start;
|
|
#endif
|
|
ts->xfer_buff = devm_kzalloc(ts->dev, 128 * sizeof(uint8_t), GFP_KERNEL);
|
|
if (ts->xfer_buff == NULL) {
|
|
err = -ENOMEM;
|
|
goto err_xfer_buff_fail;
|
|
}
|
|
|
|
I("PDATA START\n");
|
|
pdata = kzalloc(sizeof(struct himax_i2c_platform_data), GFP_KERNEL);
|
|
if (pdata == NULL) { /*Allocate Platform data space*/
|
|
err = -ENOMEM;
|
|
goto err_dt_platform_data_fail;
|
|
}
|
|
|
|
I("ic_data START\n");
|
|
ts->ic_data = kzalloc(sizeof(struct himax_ic_data), GFP_KERNEL);
|
|
if (ts->ic_data == NULL) { /*Allocate IC data space*/
|
|
err = -ENOMEM;
|
|
goto err_dt_ic_data_fail;
|
|
}
|
|
|
|
/* allocate report data */
|
|
ts->hx_touch_data = kzalloc(sizeof(struct himax_report_data), GFP_KERNEL);
|
|
if (ts->hx_touch_data == NULL) {
|
|
err = -ENOMEM;
|
|
goto err_alloc_touch_data_failed;
|
|
}
|
|
|
|
ts->pdata = pdata;
|
|
|
|
if (himax_parse_dt(ts, pdata) < 0) {
|
|
I(" pdata is NULL for DT\n");
|
|
goto err_alloc_dt_pdata_failed;
|
|
}
|
|
|
|
ts->lcm_gpio = pdata->lcm_rst;
|
|
|
|
#if defined(HX_RST_PIN_FUNC)
|
|
ts->rst_gpio = pdata->gpio_reset;
|
|
#endif
|
|
himax_gpio_power_config(ts, pdata);
|
|
|
|
#if !defined(CONFIG_OF)
|
|
if (pdata->power) {
|
|
ret = pdata->power(1);
|
|
if (ret < 0) {
|
|
E("%s: power on failed\n", __func__);
|
|
goto err_power_failed;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ts->hx_chip_inited = 0;
|
|
if (list_empty(&ts->chips)) {
|
|
I("%s: No available chip exist!\n", __func__);
|
|
goto error_ic_detect_failed;
|
|
}
|
|
|
|
list_for_each_entry (entry, &ts->chips, list) {
|
|
++himax_chip_num;
|
|
if (!entry->ops.detect)
|
|
continue;
|
|
|
|
if (entry->ops.detect(ts)) {
|
|
I("%s: chip found! list_num=%d\n", __func__, himax_chip_num);
|
|
goto found_hx_chip;
|
|
}
|
|
|
|
I("%s:num=%d,chip NOT found! go Next\n", __func__, himax_chip_num);
|
|
}
|
|
|
|
if (entry && list_entry_is_head(entry, &ts->chips, list)) {
|
|
I("%s: No available chip exist!\n", __func__);
|
|
goto error_ic_detect_failed;
|
|
}
|
|
|
|
found_hx_chip:
|
|
if (ts->core_fp.fp_chip_init != NULL) {
|
|
I("%s:lsy---fp_chip_init\n", __func__);
|
|
ts->core_fp.fp_chip_init(ts);
|
|
} else {
|
|
E("%s: function point of chip_init is NULL!\n", __func__);
|
|
goto error_ic_detect_failed;
|
|
}
|
|
|
|
if (pdata->virtual_key)
|
|
ts->button = pdata->virtual_key;
|
|
|
|
if (hx_chk_flash_sts(ts) == 1) {
|
|
E("%s: check flash fail, please upgrade FW\n", __func__);
|
|
#if defined(HX_BOOT_UPGRADE)
|
|
ts->boot_upgrade_flag = 1;
|
|
#endif
|
|
} else {
|
|
ts->core_fp.fp_reload_disable(ts, 0);
|
|
ts->core_fp.fp_power_on_init(ts);
|
|
ts->core_fp.fp_read_FW_ver(ts);
|
|
}
|
|
|
|
#if defined(HX_BOOT_UPGRADE)
|
|
ts->himax_boot_upgrade_wq = create_singlethread_workqueue("HX_boot_upgrade");
|
|
if (!ts->himax_boot_upgrade_wq) {
|
|
E("allocate himax_boot_upgrade_wq failed\n");
|
|
err = -ENOMEM;
|
|
goto err_boot_upgrade_wq_failed;
|
|
}
|
|
INIT_DELAYED_WORK(&ts->work_boot_upgrade, himax_boot_upgrade);
|
|
queue_delayed_work(ts->himax_boot_upgrade_wq, &ts->work_boot_upgrade,
|
|
msecs_to_jiffies(2000));
|
|
#endif
|
|
|
|
#if defined(HX_CONTAINER_SPEED_UP)
|
|
ts->ts_int_workqueue = create_singlethread_workqueue("himax_ts_resume_wq");
|
|
if (!ts->ts_int_workqueue) {
|
|
E("%s: create ts_resume workqueue failed\n", __func__);
|
|
goto err_create_ts_resume_wq_failed;
|
|
}
|
|
INIT_DELAYED_WORK(&ts->ts_int_work, himax_resume_work_func);
|
|
#endif
|
|
|
|
/*Himax Power On and Load Config*/
|
|
/* if (himax_loadSensorConfig(pdata)) {
|
|
* E("%s: Load Sesnsor configuration failed, unload driver.\n",
|
|
* __func__);
|
|
* goto err_detect_failed;
|
|
* }
|
|
*/
|
|
ts->core_fp.fp_power_on_init(ts);
|
|
calculate_point_number(ts);
|
|
|
|
#if defined(CONFIG_OF)
|
|
ts->power = pdata->power;
|
|
#endif
|
|
|
|
/*calculate the i2c data size*/
|
|
calcDataSize(ts);
|
|
I("%s: calcDataSize complete\n", __func__);
|
|
|
|
#if defined(CONFIG_OF)
|
|
ts->pdata->abs_pressure_min = 0;
|
|
ts->pdata->abs_pressure_max = 200;
|
|
ts->pdata->abs_width_min = 0;
|
|
ts->pdata->abs_width_max = 200;
|
|
pdata->cable_config[0] = 0xF0;
|
|
pdata->cable_config[1] = 0x00;
|
|
#endif
|
|
|
|
ts->suspended = false;
|
|
ts->touch_num = 50;
|
|
|
|
#if defined(HX_PROTOCOL_A)
|
|
ts->protocol_type = PROTOCOL_TYPE_A;
|
|
#else
|
|
ts->protocol_type = PROTOCOL_TYPE_B;
|
|
#endif
|
|
I("%s: Use Protocol Type %c\n", __func__, ts->protocol_type == PROTOCOL_TYPE_A ? 'A' : 'B');
|
|
|
|
ret = himax_input_register(ts);
|
|
if (ret) {
|
|
E("%s: Unable to register %s input device\n", __func__, ts->input_dev->name);
|
|
goto err_input_register_device_failed;
|
|
}
|
|
|
|
spin_lock_init(&ts->irq_lock);
|
|
ts->initialized = true;
|
|
|
|
#if defined(HX_CONFIG_FB) || defined(HX_CONFIG_DRM)
|
|
ts->himax_att_wq = create_singlethread_workqueue("HMX_ATT_request");
|
|
|
|
if (!ts->himax_att_wq) {
|
|
E(" allocate himax_att_wq failed\n");
|
|
err = -ENOMEM;
|
|
goto err_get_intr_bit_failed;
|
|
}
|
|
|
|
INIT_DELAYED_WORK(&ts->work_att, himax_fb_register);
|
|
queue_delayed_work(ts->himax_att_wq, &ts->work_att, msecs_to_jiffies(15000));
|
|
#endif
|
|
|
|
/*touch data init*/
|
|
err = himax_report_data_init(ts);
|
|
#if defined(HIMAX_I2C_PLATFORM)
|
|
himax_sysfs_init(ts);
|
|
#endif
|
|
|
|
if (err)
|
|
goto err_report_data_init_failed;
|
|
|
|
if (himax_common_proc_init(ts)) {
|
|
E(" %s: himax_common proc_init failed!\n", __func__);
|
|
err = -ENOMEM;
|
|
goto err_creat_proc_file_failed;
|
|
}
|
|
|
|
himax_ts_register_interrupt(ts);
|
|
|
|
himax_fail_det_register_interrupt(ts);
|
|
|
|
#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG)
|
|
if (himax_debug_init(ts))
|
|
E(" %s: debug initial failed!\n", __func__);
|
|
#endif
|
|
|
|
#if defined(HX_BOOT_UPGRADE)
|
|
if (ts->boot_upgrade_flag)
|
|
himax_int_enable(ts, 0);
|
|
#endif
|
|
ts->hx_chip_inited = true;
|
|
return 0;
|
|
|
|
err_creat_proc_file_failed:
|
|
himax_report_data_deinit(ts);
|
|
err_report_data_init_failed:
|
|
|
|
#if defined(HX_CONFIG_FB) || defined(HX_CONFIG_DRM)
|
|
cancel_delayed_work_sync(&ts->work_att);
|
|
destroy_workqueue(ts->himax_att_wq);
|
|
err_get_intr_bit_failed:
|
|
#endif
|
|
err_input_register_device_failed:
|
|
input_free_device(ts->input_dev);
|
|
/*err_detect_failed:*/
|
|
|
|
#if defined(HX_CONTAINER_SPEED_UP)
|
|
cancel_delayed_work_sync(&ts->ts_int_work);
|
|
destroy_workqueue(ts->ts_int_workqueue);
|
|
err_create_ts_resume_wq_failed:
|
|
#endif
|
|
|
|
#if defined(HX_BOOT_UPGRADE)
|
|
cancel_delayed_work_sync(&ts->work_boot_upgrade);
|
|
destroy_workqueue(ts->himax_boot_upgrade_wq);
|
|
err_boot_upgrade_wq_failed:
|
|
#endif
|
|
|
|
error_ic_detect_failed:
|
|
himax_gpio_power_deconfig(pdata);
|
|
#if !defined(CONFIG_OF)
|
|
err_power_failed:
|
|
#endif
|
|
err_alloc_dt_pdata_failed:
|
|
kfree(ts->hx_touch_data);
|
|
ts->hx_touch_data = NULL;
|
|
err_alloc_touch_data_failed:
|
|
kfree(ts->ic_data);
|
|
ts->ic_data = NULL;
|
|
err_dt_ic_data_fail:
|
|
kfree(pdata);
|
|
pdata = NULL;
|
|
err_dt_platform_data_fail:
|
|
devm_kfree(ts->dev, ts->xfer_buff);
|
|
ts->xfer_buff = NULL;
|
|
err_xfer_buff_fail:
|
|
return err;
|
|
}
|
|
|
|
void himax_chip_common_deinit(struct himax_ts_data *ts)
|
|
{
|
|
himax_ts_unregister_interrupt(ts);
|
|
|
|
#if defined(CONFIG_TOUCHSCREEN_HIMAX_INSPECT)
|
|
himax_inspect_data_clear(ts);
|
|
#endif
|
|
|
|
#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG)
|
|
himax_debug_remove(ts);
|
|
#endif
|
|
|
|
himax_common_proc_deinit(ts);
|
|
himax_report_data_deinit(ts);
|
|
|
|
#if defined(HX_CONFIG_FB)
|
|
if (fb_unregister_client(&ts->fb_notif))
|
|
E("Error occurred while unregistering fb_notifier.\n");
|
|
cancel_delayed_work_sync(&ts->work_att);
|
|
destroy_workqueue(ts->himax_att_wq);
|
|
#elif defined(HX_CONFIG_DRM)
|
|
|
|
if (msm_drm_unregister_client(&ts->fb_notif))
|
|
E("Error occurred while unregistering drm_notifier.\n");
|
|
|
|
cancel_delayed_work_sync(&ts->work_att);
|
|
destroy_workqueue(ts->himax_att_wq);
|
|
#endif
|
|
input_free_device(ts->input_dev);
|
|
#if defined(HX_CONTAINER_SPEED_UP)
|
|
cancel_delayed_work_sync(&ts->ts_int_work);
|
|
destroy_workqueue(ts->ts_int_workqueue);
|
|
#endif
|
|
|
|
#if defined(HX_BOOT_UPGRADE)
|
|
cancel_delayed_work_sync(&ts->work_boot_upgrade);
|
|
destroy_workqueue(ts->himax_boot_upgrade_wq);
|
|
#endif
|
|
himax_gpio_power_deconfig(ts->pdata);
|
|
himax_mcu_in_cmd_struct_free(ts);
|
|
|
|
kfree(ts->hx_touch_data);
|
|
ts->hx_touch_data = NULL;
|
|
kfree(ts->ic_data);
|
|
ts->ic_data = NULL;
|
|
kfree(ts->pdata->virtual_key);
|
|
ts->pdata->virtual_key = NULL;
|
|
devm_kfree(ts->dev, ts->xfer_buff);
|
|
ts->xfer_buff = NULL;
|
|
kfree(ts->pdata);
|
|
ts->pdata = NULL;
|
|
kfree(ts);
|
|
ts = NULL;
|
|
|
|
I("%s: Common section deinited!\n", __func__);
|
|
}
|
|
|
|
int himax_chip_common_suspend(struct himax_ts_data *ts)
|
|
{
|
|
if (ts->suspended) {
|
|
I("%s: Already suspended. Skipped.\n", __func__);
|
|
goto END;
|
|
} else {
|
|
ts->suspended = true;
|
|
I("%s: enter\n", __func__);
|
|
}
|
|
|
|
if (ts->debug.flash_dump_going == true) {
|
|
I("[himax] %s: Flash dump is going, reject suspend\n", __func__);
|
|
goto END;
|
|
}
|
|
|
|
himax_int_enable(ts, 0);
|
|
if (ts->core_fp.fp_suspend_ic_action != NULL)
|
|
ts->core_fp.fp_suspend_ic_action(ts);
|
|
|
|
if (!ts->use_irq) {
|
|
int32_t cancel_state;
|
|
|
|
cancel_state = cancel_work_sync(&ts->work);
|
|
if (cancel_state)
|
|
himax_int_enable(ts, 1);
|
|
}
|
|
|
|
/*ts->first_pressed = 0;*/
|
|
atomic_set(&ts->suspend_mode, 1);
|
|
ts->pre_finger_mask = 0;
|
|
|
|
if (ts->pdata)
|
|
if (ts->pdata->powerOff3V3 && ts->pdata->power)
|
|
ts->pdata->power(0);
|
|
|
|
END:
|
|
if (ts->in_self_test == 1)
|
|
ts->suspend_resume_done = 1;
|
|
|
|
I("%s: END\n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int himax_chip_common_resume(struct himax_ts_data *ts)
|
|
{
|
|
I("%s: enter\n", __func__);
|
|
|
|
if (ts->suspended == false) {
|
|
I("%s: It had entered resume, skip this step\n", __func__);
|
|
goto END;
|
|
} else {
|
|
ts->suspended = false;
|
|
}
|
|
|
|
atomic_set(&ts->suspend_mode, 0);
|
|
ts->diag_cmd = 0;
|
|
|
|
if (ts->pdata)
|
|
if (ts->pdata->powerOff3V3 && ts->pdata->power)
|
|
ts->pdata->power(1);
|
|
|
|
#if defined(HX_RST_PIN_FUNC)
|
|
if (ts->core_fp.fp_ic_reset != NULL)
|
|
ts->core_fp.fp_ic_reset(ts, false, false);
|
|
#endif
|
|
|
|
himax_report_all_leave_event(ts);
|
|
|
|
if (ts->core_fp.fp_sense_on != NULL)
|
|
ts->core_fp.fp_resume_ic_action(ts);
|
|
|
|
himax_int_enable(ts, 1);
|
|
|
|
END:
|
|
if (ts->in_self_test == 1)
|
|
ts->suspend_resume_done = 1;
|
|
|
|
I("%s: END\n", __func__);
|
|
return 0;
|
|
}
|