linuxOS_D21X/source/linux-5.10/drivers/input/touchscreen/icn81xx/cts_tool.c
2024-11-29 16:13:46 +08:00

541 lines
17 KiB
C

#define LOG_TAG "Tool"
#include "cts_config.h"
#include "cts_platform.h"
#include "cts_core.h"
#include "cts_firmware.h"
#ifdef CONFIG_CTS_LEGACY_TOOL
#pragma pack(1)
/** Tool command structure */
struct cts_tool_cmd {
u8 cmd;
u8 flag;
u8 circle;
u8 times;
u8 retry;
u32 data_len;
u8 addr_len;
u8 addr[2];
u8 data[PAGE_SIZE];
};
#pragma pack()
#define CTS_TOOL_CMD_HEADER_LENGTH (12)
enum cts_tool_cmd_code {
CTS_TOOL_CMD_GET_PANEL_PARAM = 0,
CTS_TOOL_CMD_GET_DOWNLOAD_STATUS = 2,
CTS_TOOL_CMD_GET_RAW_DATA = 4,
CTS_TOOL_CMD_GET_DIFF_DATA = 6,
CTS_TOOL_CMD_READ_HOSTCOMM = 12,
CTS_TOOL_CMD_READ_ADC_STATUS = 14,
CTS_TOOL_CMD_READ_GESTURE_INFO = 16,
CTS_TOOL_CMD_READ_HOSTCOMM_MULTIBYTE = 18,
CTS_TOOL_CMD_READ_PROGRAM_MODE_MULTIBYTE = 20,
CTS_TOOL_CMD_READ_ICTYPE = 22,
CTS_TOOL_CMD_I2C_DIRECT_READ = 24,
CTS_TOOL_CMD_GET_DRIVER_INFO = 26,
CTS_TOOL_CMD_UPDATE_PANEL_PARAM_IN_SRAM = 1,
CTS_TOOL_CMD_DOWNLOAD_FIRMWARE_WITH_FILENAME = 3,
CTS_TOOL_CMD_DOWNLOAD_FIRMWARE = 5,
CTS_TOOL_CMD_WRITE_HOSTCOMM = 11,
CTS_TOOL_CMD_WRITE_HOSTCOMM_MULTIBYTE = 15,
CTS_TOOL_CMD_WRITE_PROGRAM_MODE_MULTIBYTE = 17,
CTS_TOOL_CMD_I2C_DIRECT_WRITE = 19,
};
static struct cts_tool_cmd cts_tool_cmd;
static char cts_tool_firmware_filepath[PATH_MAX];
/* If CFG_CTS_MAX_I2C_XFER_SIZE < 58(PC tool length), this is neccessary */
static u32 cts_tool_direct_access_addr = 0;
static int cts_tool_open(struct inode *inode, struct file *file)
{
file->private_data = PDE_DATA(inode);
return 0;
}
static ssize_t cts_tool_read(struct file *file,
char __user *buffer, size_t count, loff_t *ppos)
{
struct chipone_ts_data *cts_data;
struct cts_tool_cmd *cmd;
struct cts_device *cts_dev;
int ret = 0;
cts_data = (struct chipone_ts_data *)file->private_data;
if (cts_data == NULL) {
cts_err("Read with private_data = NULL");
return -EIO;
}
cmd = &cts_tool_cmd;
cts_dev = &cts_data->cts_dev;
cts_lock_device(cts_dev);
switch (cmd->cmd) {
case CTS_TOOL_CMD_GET_PANEL_PARAM:
cts_info("Get panel param len: %u", cmd->data_len);
ret = cts_get_panel_param(cts_dev, cmd->data, cmd->data_len);
if (ret) {
cts_err("Get panel param len: %u failed %d", cmd->data_len, ret);
}
break;
case CTS_TOOL_CMD_GET_DOWNLOAD_STATUS:
cmd->data[0] = 100;
cts_info("Get update status = %hhu", cmd->data[0]);
break;
case CTS_TOOL_CMD_GET_RAW_DATA:
case CTS_TOOL_CMD_GET_DIFF_DATA:
cts_dbg("Get %s data row: %u col: %u len: %u",
cmd->cmd == CTS_TOOL_CMD_GET_RAW_DATA ? "raw" : "diff",
cmd->addr[1], cmd->addr[0], cmd->data_len);
ret = cts_enable_get_rawdata(cts_dev);
if (ret) {
cts_err("Enable read raw/diff data failed %d", ret);
break;
}
mdelay(1);
if (cmd->cmd == CTS_TOOL_CMD_GET_RAW_DATA) {
ret = cts_get_rawdata(cts_dev, cmd->data);
} else if (cmd->cmd == CTS_TOOL_CMD_GET_DIFF_DATA) {
ret = cts_get_diffdata(cts_dev, cmd->data);
}
if(ret) {
cts_err("Get %s data failed %d",
cmd->cmd == CTS_TOOL_CMD_GET_RAW_DATA ? "raw" : "diff", ret);
break;
}
ret = cts_disable_get_rawdata(cts_dev);
if (ret) {
cts_err("Disable read raw/diff data failed %d", ret);
break;
}
break;
case CTS_TOOL_CMD_READ_HOSTCOMM:
ret = cts_fw_reg_readb(cts_dev,
get_unaligned_le16(cmd->addr), cmd->data);
if (ret) {
cts_err("Read firmware reg addr 0x%04x failed %d",
get_unaligned_le16(cmd->addr), ret);
} else {
cts_dbg("Read firmware reg addr 0x%04x, val=0x%02x",
get_unaligned_le16(cmd->addr), cmd->data[0]);
}
break;
#ifdef CONFIG_CTS_GESTURE
case CTS_TOOL_CMD_READ_GESTURE_INFO:
ret = cts_get_gesture_info(cts_dev, cmd->data, true);
if (ret) {
cts_err("Get gesture info failed %d", ret);
}
break;
#endif /* CONFIG_CTS_GESTURE */
case CTS_TOOL_CMD_READ_HOSTCOMM_MULTIBYTE:
cmd->data_len = min((size_t)cmd->data_len, sizeof(cmd->data));
ret = cts_fw_reg_readsb(cts_dev, get_unaligned_le16(cmd->addr),
cmd->data, cmd->data_len);
if (ret) {
cts_err("Read firmware reg addr 0x%04x len %u failed %d",
get_unaligned_le16(cmd->addr), cmd->data_len, ret);
} else {
cts_dbg("Read firmware reg addr 0x%04x len %u",
get_unaligned_le16(cmd->addr), cmd->data_len);
}
break;
case CTS_TOOL_CMD_READ_PROGRAM_MODE_MULTIBYTE:
cts_dbg("Read under program mode addr 0x%06x len %u",
(cmd->flag << 16) | get_unaligned_le16(cmd->addr),
cmd->data_len);
ret = cts_enter_program_mode(cts_dev);
if (ret) {
cts_err("Enter program mode failed %d", ret);
break;
}
ret = cts_sram_readsb(&cts_data->cts_dev,
get_unaligned_le16(cmd->addr), cmd->data, cmd->data_len);
if (ret) {
cts_err("Read under program mode I2C xfer failed %d", ret);
//break;
}
ret = cts_enter_normal_mode(cts_dev);
if (ret) {
cts_err("Enter normal mode failed %d", ret);
break;
}
break;
case CTS_TOOL_CMD_READ_ICTYPE:
cts_info("Get IC type");
if (cts_dev->hwdata) {
switch(cts_dev->hwdata->hwid) {
case CTS_HWID_ICNT89XX: cmd->data[0] = 0x89; break;
default: cmd->data[0] = 0x00; break;
}
} else {
cmd->data[0] = 0x10;
}
break;
case CTS_TOOL_CMD_I2C_DIRECT_READ:
{
u32 addr_width;
char *wr_buff = NULL;
u8 addr_buff[4];
size_t left_size, max_xfer_size;
u8 *data;
if (cmd->addr[0] != cts_dev->confdata.prog_i2c_addr) {
cmd->addr[0] = CTS_NORMAL_MODE_I2CADDR;
addr_width = 2;
} else {
addr_width = cts_dev->hwdata->program_addr_width;
}
cts_dbg("Direct read from i2c_addr 0x%02x addr 0x%06x size %u",
cmd->addr[0], cts_tool_direct_access_addr, cmd->data_len);
left_size = cmd->data_len;
max_xfer_size = cts_plat_get_max_i2c_xfer_size(cts_dev->pdata);
data = cmd->data;
while (left_size) {
size_t xfer_size = min(left_size, max_xfer_size);
ret = cts_plat_i2c_read(cts_data->pdata, cmd->addr[0],
wr_buff, addr_width, data, xfer_size, 1, 0);
if (ret) {
cts_err("Direct read i2c_addr 0x%02x addr 0x%06x len %zu failed %d",
cmd->addr[0], cts_tool_direct_access_addr, xfer_size, ret);
break;
}
left_size -= xfer_size;
if (left_size) {
data += xfer_size;
cts_tool_direct_access_addr += xfer_size;
if (addr_width == 2) {
put_unaligned_be16(cts_tool_direct_access_addr, addr_buff);
} else if (addr_width == 3) {
put_unaligned_be24(cts_tool_direct_access_addr, addr_buff);
}
wr_buff = addr_buff;
}
}
}
break;
case CTS_TOOL_CMD_GET_DRIVER_INFO:
break;
default:
cts_warn("Read unknown command %u", cmd->cmd);
ret = -EINVAL;
break;
}
cts_unlock_device(cts_dev);
if (ret == 0) {
if(cmd->cmd == CTS_TOOL_CMD_I2C_DIRECT_READ) {
ret = copy_to_user(buffer + CTS_TOOL_CMD_HEADER_LENGTH,
cmd->data, cmd->data_len);
} else {
ret = copy_to_user(buffer, cmd->data, cmd->data_len);
}
if (ret) {
cts_err("Copy data to user buffer failed %d", ret);
return 0;
}
return cmd->data_len;
}
return 0;
}
static ssize_t cts_tool_write(struct file *file,
const char __user * buffer, size_t count, loff_t * ppos)
{
struct chipone_ts_data *cts_data;
struct cts_device *cts_dev;
struct cts_tool_cmd *cmd;
int ret = 0;
if (count < CTS_TOOL_CMD_HEADER_LENGTH) {
cts_err("Write len %zu < command header", count);
return -EFAULT;
}
cts_data = (struct chipone_ts_data *)file->private_data;
if (cts_data == NULL) {
cts_err("Write with private_data = NULL");
return -EIO;
}
cmd = &cts_tool_cmd;
ret = copy_from_user(cmd, buffer, CTS_TOOL_CMD_HEADER_LENGTH);
if (ret) {
cts_err("Copy command header from user buffer failed %d", ret);
return -EIO;
} else {
ret = CTS_TOOL_CMD_HEADER_LENGTH;
}
if(cmd->cmd & BIT(0)) {
/* Write command */
if (cmd->data_len > sizeof(cmd->data)) {
cts_err("Write command(%d) with too long payload %d > %zu",
cmd->cmd, cmd->data_len, sizeof(cmd->data));
return -EINVAL;
}
if(cmd->data_len) {
ret = copy_from_user(cmd->data,
buffer + CTS_TOOL_CMD_HEADER_LENGTH, cmd->data_len);
if (ret) {
cts_err("Copy command payload from user buffer len %u failed %d",
cmd->data_len, ret);
return -EIO;
}
}
} else {
/* Prepare for read command */
cts_dbg("Write read command(%d) header, prepare read size: %d",
cmd->cmd, cmd->data_len);
return CTS_TOOL_CMD_HEADER_LENGTH + cmd->data_len;
}
cts_dev = &cts_data->cts_dev;
cts_lock_device(cts_dev);
switch (cmd->cmd) {
case CTS_TOOL_CMD_UPDATE_PANEL_PARAM_IN_SRAM:
cts_info("Write panel param len %u data\n", cmd->data_len);
ret = cts_fw_reg_writesb(cts_dev, CTS_DEVICE_FW_REG_PANEL_PARAM,
cmd->data, cmd->data_len);
if (ret) {
cts_err("Write panel param failed %d", ret);
break;
}
ret = cts_send_command(cts_dev, CTS_CMD_RESET);
if (ret) {
}
ret = cts_set_work_mode(cts_dev, 1);
if (ret) {
}
mdelay(100);
ret = cts_set_work_mode(cts_dev, 0);
if (ret) {
}
mdelay(100);
break;
case CTS_TOOL_CMD_DOWNLOAD_FIRMWARE_WITH_FILENAME:
cts_info("Write firmware path: '%.*s'",
cmd->data_len, cmd->data);
memcpy(cts_tool_firmware_filepath, cmd->data, cmd->data_len);
cts_tool_firmware_filepath[cmd->data_len] = '\0';
break;
case CTS_TOOL_CMD_DOWNLOAD_FIRMWARE:
cts_info("Start download firmware path: '%s'",
cts_tool_firmware_filepath);
ret = cts_stop_device(cts_dev);
if (ret) {
cts_err("Stop device failed %d", ret);
break;
}
// TODO: Use async mode such as thread, otherwise, host can not get update status.
#ifdef CFG_CTS_FIRMWARE_IN_FS
ret = cts_update_firmware_from_file(cts_dev, cts_tool_firmware_filepath, true);
if (ret) {
cts_err("Updata firmware failed %d", ret);
//break;
}
#endif
ret = cts_start_device(cts_dev);
if (ret) {
cts_err("Start device failed %d", ret);
break;
}
break;
case CTS_TOOL_CMD_WRITE_HOSTCOMM:
cts_dbg("Write firmware reg addr: 0x%04x val=0x%02x",
get_unaligned_le16(cmd->addr), cmd->data[0]);
ret = cts_fw_reg_writeb(cts_dev,
get_unaligned_le16(cmd->addr), cmd->data[0]);
if (ret) {
cts_err("Write firmware reg addr: 0x%04x val=0x%02x failed %d",
get_unaligned_le16(cmd->addr), cmd->data[0], ret);
}
break;
case CTS_TOOL_CMD_WRITE_HOSTCOMM_MULTIBYTE:
cts_dbg("Write firmare reg addr: 0x%04x len %u",
get_unaligned_le16(cmd->addr), cmd->data_len);
ret = cts_fw_reg_writesb(cts_dev, get_unaligned_le16(cmd->addr),
cmd->data, cmd->data_len);
if (ret) {
cts_err("Write firmare reg addr: 0x%04x len %u failed %d",
get_unaligned_le16(cmd->addr), cmd->data_len, ret);
}
break;
case CTS_TOOL_CMD_WRITE_PROGRAM_MODE_MULTIBYTE:
cts_dbg("Write to addr 0x%06x size %u under program mode",
(cmd->flag << 16) | (cmd->addr[1] << 8) | cmd->addr[0],
cmd->data_len);
ret = cts_enter_program_mode(cts_dev);
if (ret) {
cts_err("Enter program mode failed %d", ret);
break;
}
ret = cts_sram_writesb(cts_dev,
(cmd->flag << 16) | (cmd->addr[1] << 8) | cmd->addr[0],
cmd->data, cmd->data_len);
if (ret) {
cts_err("Write program mode multibyte failed %d", ret);
//break;
}
ret = cts_enter_normal_mode(cts_dev);
if (ret) {
cts_err("Enter normal mode failed %d", ret);
break;
}
break;
case CTS_TOOL_CMD_I2C_DIRECT_WRITE:
{
u32 addr_width;
size_t left_payload_size; /* Payload exclude address field */
size_t max_xfer_size;
char *payload;
if (cmd->addr[0] != cts_dev->confdata.prog_i2c_addr) {
cmd->addr[0] = CTS_NORMAL_MODE_I2CADDR;
addr_width = 2;
cts_tool_direct_access_addr = get_unaligned_be16(cmd->data);
} else {
addr_width = cts_dev->hwdata->program_addr_width;
cts_tool_direct_access_addr = get_unaligned_be24(cmd->data);
}
if (cmd->data_len < addr_width) {
cts_err("Direct write too short %d < address width %d",
cmd->data_len, addr_width);
ret = -EINVAL;
break;
}
cts_dbg("Direct write to i2c_addr 0x%02x addr 0x%06x size %u",
cmd->addr[0], cts_tool_direct_access_addr, cmd->data_len);
left_payload_size = cmd->data_len - addr_width;
max_xfer_size = cts_plat_get_max_i2c_xfer_size(cts_dev->pdata);
payload = cmd->data + addr_width;
do {
size_t xfer_payload_size = min(left_payload_size,
max_xfer_size - addr_width);
size_t xfer_len = xfer_payload_size + addr_width;
ret = cts_plat_i2c_write(cts_data->pdata, cmd->addr[0],
payload - addr_width, xfer_len, 1, 0);
if (ret) {
cts_err("Direct write i2c_addr 0x%02x addr 0x%06x len %zu failed %d",
cmd->addr[0], cts_tool_direct_access_addr, xfer_len, ret);
break;
}
left_payload_size -= xfer_payload_size;
if (left_payload_size) {
payload += xfer_payload_size;
cts_tool_direct_access_addr += xfer_payload_size;
if (addr_width == 2) {
put_unaligned_be16(cts_tool_direct_access_addr, payload - addr_width);
} else if (addr_width == 3) {
put_unaligned_be24(cts_tool_direct_access_addr, payload - addr_width);
}
}
} while (left_payload_size);
}
break;
default:
cts_warn("Write unknown command %u", cmd->cmd);
ret = -EINVAL;
break;
}
cts_unlock_device(cts_dev);
return ret ? 0 : cmd->data_len + CTS_TOOL_CMD_HEADER_LENGTH;
}
static struct file_operations cts_tool_fops = {
.owner = THIS_MODULE,
.open = cts_tool_open,
.read = cts_tool_read,
.write = cts_tool_write,
};
int cts_tool_init(struct chipone_ts_data *cts_data)
{
int ret = 0;
cts_info("Init");
cts_data->procfs_entry = proc_create_data(CFG_CTS_TOOL_PROC_FILENAME,
0666, NULL, &cts_tool_fops, cts_data);
if (IS_ERR(cts_data->procfs_entry)) {
cts_err("Create proc entry failed %ld",
PTR_ERR(cts_data->procfs_entry));
cts_data->procfs_entry = NULL;
}
return ret;
}
void cts_tool_deinit(struct chipone_ts_data *data)
{
cts_info("Deinit");
if (data->procfs_entry) {
remove_proc_entry(CFG_CTS_TOOL_PROC_FILENAME, NULL);
}
}
#endif /* CONFIG_CTS_LEGACY_TOOL */