blob: e3a17119fa14594cce3de8504b9c354a2f5fe02c [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Himax hx83102j SPI Driver Code for HID.
*
* Copyright (C) 2024 Himax Corporation.
*/
#include "hid-himax.h"
static int himax_cable_detect_func(struct himax_ts_data *ts, bool force_renew);
static int himax_chip_init(struct himax_ts_data *ts);
static int himax_get_data(struct himax_ts_data *ts, u8 *data);
static int himax_heatmap_data_init(struct himax_ts_data *ts);
static int himax_platform_init(struct himax_ts_data *ts);
static int himax_switch_data_type(struct himax_ts_data *ts, u32 type);
static void himax_self_test(struct work_struct *work);
static void himax_ts_work(struct himax_ts_data *ts);
static const unsigned char g_windows_blob_validation_key[] = {
0xfc, 0x28, 0xfe, 0x84, 0x40, 0xcb, 0x9a, 0x87, 0x0d, 0xbe, 0x57, 0x3c, 0xb6, 0x70,
0x09, 0x88, 0x07, 0x97, 0x2d, 0x2b, 0xe3, 0x38, 0x34, 0xb6, 0x6c, 0xed, 0xb0, 0xf7,
0xe5, 0x9c, 0xf6, 0xc2, 0x2e, 0x84, 0x1b, 0xe8, 0xb4, 0x51, 0x78, 0x43, 0x1f, 0x28,
0x4b, 0x7c, 0x2d, 0x53, 0xaf, 0xfc, 0x47, 0x70, 0x1b, 0x59, 0x6f, 0x74, 0x43, 0xc4,
0xf3, 0x47, 0x18, 0x53, 0x1a, 0xa2, 0xa1, 0x71, 0xc7, 0x95, 0x0e, 0x31, 0x55, 0x21,
0xd3, 0xb5, 0x1e, 0xe9, 0x0c, 0xba, 0xec, 0xb8, 0x89, 0x19, 0x3e, 0xb3, 0xaf, 0x75,
0x81, 0x9d, 0x53, 0xb9, 0x41, 0x57, 0xf4, 0x6d, 0x39, 0x25, 0x29, 0x7c, 0x87, 0xd9,
0xb4, 0x98, 0x45, 0x7d, 0xa7, 0x26, 0x9c, 0x65, 0x3b, 0x85, 0x68, 0x89, 0xd7, 0x3b,
0xbd, 0xff, 0x14, 0x67, 0xf2, 0x2b, 0xf0, 0x2a, 0x41, 0x54, 0xf0, 0xfd, 0x2c, 0x66,
0x7c, 0xf8, 0xc0, 0x8f, 0x33, 0x13, 0x03, 0xf1, 0xd3, 0xc1, 0x0b, 0x89, 0xd9, 0x1b,
0x62, 0xcd, 0x51, 0xb7, 0x80, 0xb8, 0xaf, 0x3a, 0x10, 0xc1, 0x8a, 0x5b, 0xe8, 0x8a,
0x56, 0xf0, 0x8c, 0xaa, 0xfa, 0x35, 0xe9, 0x42, 0xc4, 0xd8, 0x55, 0xc3, 0x38, 0xcc,
0x2b, 0x53, 0x5c, 0x69, 0x52, 0xd5, 0xc8, 0x73, 0x02, 0x38, 0x7c, 0x73, 0xb6, 0x41,
0xe7, 0xff, 0x05, 0xd8, 0x2b, 0x79, 0x9a, 0xe2, 0x34, 0x60, 0x8f, 0xa3, 0x32, 0x1f,
0x09, 0x78, 0x62, 0xbc, 0x80, 0xe3, 0x0f, 0xbd, 0x65, 0x20, 0x08, 0x13, 0xc1, 0xe2,
0xee, 0x53, 0x2d, 0x86, 0x7e, 0xa7, 0x5a, 0xc5, 0xd3, 0x7d, 0x98, 0xbe, 0x31, 0x48,
0x1f, 0xfb, 0xda, 0xaf, 0xa2, 0xa8, 0x6a, 0x89, 0xd6, 0xbf, 0xf2, 0xd3, 0x32, 0x2a,
0x9a, 0xe4, 0xcf, 0x17, 0xb7, 0xb8, 0xf4, 0xe1, 0x33, 0x08, 0x24, 0x8b, 0xc4, 0x43,
0xa5, 0xe5, 0x24, 0xc2
};
/* Extension report descriptor for HIDRAW debug function */
static union himax_host_ext_rd g_host_ext_rd = {
.host_report_descriptor = {
0x06, 0x00, 0xff,/* Usage Page (Vendor-defined) */
0x09, 0x01,/* Usage (0x1) */
0xa1, 0x01,/* Collection (Application) */
0x75, 0x08,/* Report Size (8) */
0x15, 0x00,/* Logical Minimum (0) */
0x26, 0xff, 0x00,/* Logical Maximum (255) */
0x85, HIMAX_ID_CFG,/* Report ID (5) */
0x09, 0x02,/* Usage (0x2) */
0x96, 0xff, 0x00,/* Report Count (255) */
0xb1, 0x02,/* Feature (ID: 5, sz: 2040 bits(255 bytes)) */
0x85, HIMAX_ID_REG_RW,/* Report ID (6) */
0x09, 0x02,/* Usage (0x2) */
0x96, (HIMAX_HID_REG_SZ_MAX & 0xff), (HIMAX_HID_REG_SZ_MAX >> 8),
0xb1, 0x02,/* Feature (ID: 6, sz: 72 bits(9 bytes)) */
0x85, HIMAX_ID_TOUCH_MONITOR_SEL,/* Report ID (7) */
0x09, 0x02,/* Usage (0x2) */
0x96, 0x04, 0x00,/* Report Count (4) */
0xb1, 0x02,/* Feature (ID: 7, sz: 32 bits(4 bytes)) */
0x85, HIMAX_ID_TOUCH_MONITOR,/* Report ID (8) */
0x09, 0x02,/* Usage (0x2) */
0x96, 0x8d, 0x13,/* Report Count (5005) */
0xb1, 0x02,/* Feature (ID: 8, sz: 40040 bits(5005 bytes)) */
0x85, HIMAX_ID_FW_UPDATE,/* Report ID (10) */
0x09, 0x02,/* Usage (0x2) */
0x96, 0x00, 0x04,/* Report Count (1024) */
0x91, 0x02,/* Output (ID: 10, sz: 8192 bits(1024 bytes)) */
0x85, HIMAX_ID_FW_UPDATE_HANDSHAKING,/* Report ID (11) */
0x09, 0x02,/* Usage (0x2) */
0x96, 0x01, 0x00,/* Report Count (1) */
0xb1, 0x02,/* Feature (ID: 11, sz: 8 bits(1 bytes)) */
0x85, HIMAX_ID_SELF_TEST,/* Report ID (12) */
0x09, 0x02,/* Usage (0x2) */
0x96, 0x01, 0x00,/* Report Count (1) */
0xb1, 0x02,/* Feature (ID: 12, sz: 8 bits(1 bytes)) */
0x85, HIMAX_ID_INPUT_RD_DE,/* Report ID (49) */
0x09, 0x02,/* Usage (0x2) */
0x96, 0x01, 0x00,/* Report Count (1) */
0xb1, 0x02,/* Feature (ID: 49, sz: 8 bits(1 bytes)) */
0x85, HIMAX_ID_WINDOWS_BLOB_VALID,
0x09, 0xc5,/* Usage (0xc5) */
0x96, sizeof(g_windows_blob_validation_key) & 0xff,
(sizeof(g_windows_blob_validation_key) >> 8) & 0xff,/* Report Count (256) */
0xb1, 0x02,/* Feature (ID: 50, sz: 2048 bits(256 bytes)) */
0xc0,/* End Collection */
},
};
static const unsigned int g_host_ext_report_desc_sz =
sizeof(g_host_ext_rd.host_report_descriptor);
/* Dummy FW layout for user space tool update reference */
static const struct himax_hid_fw_unit g_dummy_main_code[9] = {
{
/* 0xa1 means part 0 ready, can send this part of FW */
.cmd = 0xa1,
.bin_start_offset = 0,
.unit_sz = 127,
},
{
/* 0xa2 means part 1 ready, can send this part of FW */
.cmd = 0xa2,
.bin_start_offset = 129,
.unit_sz = 111,
},
};
static const u16 g_himax_hid_raw_data_type[HIMAX_HID_RAW_DATA_TYPE_MAX] = {
HIMAX_HID_RAW_DATA_TYPE_DELTA,
HIMAX_HID_RAW_DATA_TYPE_RAW,
HIMAX_HID_RAW_DATA_TYPE_BASELINE,
HIMAX_HID_RAW_DATA_TYPE_NORMAL
};
/* Heatmap report descriptor for input disabled mode */
static union himax_heatmap_rd g_heatmap_rd = {
.host_report_descriptor = {
0x05, 0x0d,/* Usage Page (Digitizers) */
0x09, 0x0f,/* Usage (0xf) */
0xa1, 0x01,/* Collection (Application) */
0x85, 0x61,/* Report ID (97) */
0x05, 0x0d,/* Usage Page (Digitizers) */
0x15, 0x00,/* Logical Minimum (0) */
0x27, 0xff, 0xff, 0x00, 0x00,/* Logical Maximum (65535) */
0x75, 0x10,/* Report Size (16) */
0x95, 0x01,/* Report Count (1) */
0x09, 0x6a,/* Usage (0x6a) */
0x81, 0x02,/* Input (ID: 97, sz: 16 bits(2 bytes)) */
0x09, 0x6b,/* Usage (0x6b) */
0x81, 0x02,/* Input (ID: 97, sz: 16 bits(2 bytes)) */
0x27, 0xff, 0xff, 0xff, 0xff,/* Logical Maximum (-1) */
0x75, 0x20,/* Report Size (32) */
0x09, 0x56,/* Usage (0x56) */
0x81, 0x02,/* Input (ID: 97, sz: 32 bits(4 bytes)) */
0x05, 0x01,/* Usage Page (Generic Desktop) */
0x09, 0x3b,/* Usage (0x3b) */
0x81, 0x02,/* Input (ID: 97, sz: 32 bits(4 bytes)) */
0x05, 0x0d,/* Usage Page (Digitizers) */
0x26, 0xff, 0x00,/* Logical Maximum (255) */
0x09, 0x6c,/* Usage (0x6c) */
0x75, 0x08,/* Report Size (8) */
0x96, 0x00, 0x0c,/* Report Count (3072) */
0x81, 0x02,/* Input (ID: 97, sz: 24576 bits(3072 bytes)) */
0xc0,/* End Collection */
},
};
static const unsigned int g_host_heatmap_report_desc_sz =
sizeof(g_heatmap_rd.host_report_descriptor);
/* Need to map HID_INSPECTION_ENUM */
static char *g_himax_inspection_mode[] = {
"HIMAX_OPEN",
"HIMAX_MICRO_OPEN",
"HIMAX_SHORT",
"HIMAX_ABS_NOISE",
"HIMAX_RAWDATA",
"HIMAX_SORTING",
"HIMAX_BACK_NORMAL",
NULL
};
static const u16 g_hx_data_type[HIMAX_DATA_TYPE_MAX] = {
HIMAX_DATA_TYPE_SORTING,
HIMAX_DATA_TYPE_OPEN,
HIMAX_DATA_TYPE_MICRO_OPEN,
HIMAX_DATA_TYPE_SHORT,
HIMAX_DATA_TYPE_RAWDATA,
HIMAX_DATA_TYPE_NOISE,
HIMAX_DATA_TYPE_BACK_NORMAL,
};
/**
* himax_spi_read() - Read data from SPI
* @ts: Himax touch screen data
* @cmd_len: Length of command
* @buf: Buffer to store data
* @len: Length of data to read
*
* Himax spi_sync wrapper for read. Read protocol start with write command,
* and received the data after that.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_spi_read(struct himax_ts_data *ts, u8 cmd_len, u8 *buf, u32 len)
{
int ret;
int retry_cnt;
struct spi_message msg;
struct spi_transfer xfer = {
.len = cmd_len + len,
.tx_buf = ts->xfer_tx_data,
.rx_buf = ts->xfer_rx_data
};
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
for (retry_cnt = 0; retry_cnt < HIMAX_BUS_RETRY; retry_cnt++) {
ret = spi_sync(ts->spi, &msg);
if (!ret)
break;
}
if (retry_cnt == HIMAX_BUS_RETRY) {
dev_err(ts->dev, "%s: SPI read error retry over %d\n", __func__, HIMAX_BUS_RETRY);
return -EIO;
}
if (ret < 0)
return ret;
if (msg.status < 0)
return msg.status;
memcpy(buf, ts->xfer_rx_data + cmd_len, len);
return 0;
}
/**
* himax_spi_write() - Write data to SPI
* @ts: Himax touch screen data
* @tx_buf: Buffer to write
* @tx_len: Length of data to write
* @written: Length of data written
*
* Himax spi_sync wrapper for write.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_spi_write(struct himax_ts_data *ts, u8 *tx_buf, u32 tx_len, u32 *written)
{
int ret;
struct spi_message msg;
struct spi_transfer xfer = {
.tx_buf = tx_buf,
.len = tx_len,
};
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
*written = 0;
ret = spi_sync(ts->spi, &msg);
if (ret < 0)
return ret;
if (msg.status < 0)
return msg.status;
*written = msg.actual_length;
return 0;
}
/**
* himax_read() - Read data from Himax bus
* @ts: Himax touch screen data
* @cmd: Command to send
* @buf: Buffer to store data, caller should allocate the buffer
* @len: Length of data to read
*
* Basic read operation for Himax SPI bus. Which start with a 3 bytes command,
* 1st byte is the spi function select, 2nd byte is the command belong to the
* spi function and 3rd byte is the dummy byte for IC to process the command.
*
* The IC takes 1 basic operation at a time, so the read/write operation
* is proctected by rw_lock mutex_unlock. Also the buffer xfer_rx/tx_data is
* shared between read and write operation, protected by the same mutex lock.
* The xfer data limit by SPI constroller max xfer size + BUS_R/W_HLEN
*
* Return: 0 on success, negative error code on failure
*/
static int himax_read(struct himax_ts_data *ts, u8 cmd, u8 *buf, u32 len)
{
int ret;
if (len + HIMAX_BUS_R_HLEN > ts->spi_xfer_max_sz) {
dev_err(ts->dev, "%s, len[%u] is over %u\n", __func__,
len + HIMAX_BUS_R_HLEN, ts->spi_xfer_max_sz);
return -EINVAL;
}
mutex_lock(&ts->rw_lock);
memset(ts->xfer_rx_data, 0, HIMAX_BUS_R_HLEN + len);
ts->xfer_tx_data[0] = HIMAX_SPI_FUNCTION_READ;
ts->xfer_tx_data[1] = cmd;
ts->xfer_tx_data[2] = 0x00;
ret = himax_spi_read(ts, HIMAX_BUS_R_HLEN, buf, len);
mutex_unlock(&ts->rw_lock);
if (ret < 0)
dev_err(ts->dev, "%s: failed = %d\n", __func__, ret);
return ret;
}
/**
* himax_write() - Write data to Himax bus
* @ts: Himax touch screen data
* @cmd: Command to send
* @addr: Address to write
* @data: Data to write
* @len: Length of data to write
*
* Basic write operation for Himax IC. Which start with a 2 bytes command,
* 1st byte is the spi function select and 2nd byte is the command belong to the
* spi function. Else is the data to write.
*
* The IC takes 1 basic operation at a time, so the read/write operation
* is proctected by rw_lock mutex_unlock. Also the buffer xfer_tx_data is
* shared between read and write operation, protected by the same mutex lock.
* The xfer data limit by SPI constroller max xfer size + HIMAX_BUS_W_HLEN
*
* Return: 0 on success, negative error code on failure
*/
static int himax_write(struct himax_ts_data *ts, u8 cmd, u8 *addr, const u8 *data, u32 len)
{
int ret;
u8 offset;
u32 written;
u32 tmp_len;
if (len + HIMAX_BUS_W_HLEN > ts->spi_xfer_max_sz) {
dev_err(ts->dev, "%s: len[%u] is over %u\n", __func__,
len + HIMAX_BUS_W_HLEN, ts->spi_xfer_max_sz);
return -EFAULT;
}
mutex_lock(&ts->rw_lock);
memset(ts->xfer_tx_data, 0, len + HIMAX_BUS_W_HLEN);
ts->xfer_tx_data[0] = HIMAX_SPI_FUNCTION_WRITE;
ts->xfer_tx_data[1] = cmd;
offset = HIMAX_BUS_W_HLEN;
tmp_len = len;
if (addr) {
memcpy(ts->xfer_tx_data + offset, addr, 4);
offset += 4;
tmp_len -= 4;
}
if (data)
memcpy(ts->xfer_tx_data + offset, data, tmp_len);
ret = himax_spi_write(ts, ts->xfer_tx_data, len + HIMAX_BUS_W_HLEN, &written);
mutex_unlock(&ts->rw_lock);
if (ret < 0) {
dev_err(ts->dev, "%s: failed, ret = %d\n", __func__, ret);
return ret;
}
if (written != len + HIMAX_BUS_W_HLEN) {
dev_err(ts->dev, "%s: actual write length mismatched: %u != %u\n",
__func__, written, len + HIMAX_BUS_W_HLEN);
return -EIO;
}
return 0;
}
/**
* himax_mcu_set_burst_mode() - Set burst mode for MCU
* @ts: Himax touch screen data
* @auto_add_4_byte: Enable auto add 4 byte mode
*
* Set burst mode for MCU, which is used for read/write data from/to MCU.
* HIMAX_AHB_ADDR_CONTI config the IC to take data continuously,
* HIMAX_AHB_ADDR_INCR4 config the IC to auto increment the address by 4 byte when
* each 4 bytes read/write.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_mcu_set_burst_mode(struct himax_ts_data *ts, bool auto_add_4_byte)
{
int ret;
u8 tmp_data[HIMAX_REG_SZ];
tmp_data[0] = HIMAX_AHB_CMD_CONTI;
ret = himax_write(ts, HIMAX_AHB_ADDR_CONTI, NULL, tmp_data, 1);
if (ret < 0) {
dev_err(ts->dev, "%s: write ahb_addr_conti failed\n", __func__);
return ret;
}
tmp_data[0] = HIMAX_AHB_CMD_INCR4;
if (auto_add_4_byte)
tmp_data[0] |= HIMAX_AHB_CMD_INCR4_ADD_4_BYTE;
ret = himax_write(ts, HIMAX_AHB_ADDR_INCR4, NULL, tmp_data, 1);
if (ret < 0)
dev_err(ts->dev, "%s: write ahb_addr_incr4 failed\n", __func__);
return ret;
}
/**
* himax_burst_mode_enable() - Enable burst mode for MCU if possible
* @ts: Himax touch screen data
* @addr: Address to read/write
* @len: Length of data to read/write
*
* Enable burst mode for MCU, helper function to determine the burst mode
* operation for MCU. When the address is HIMAX_REG_ADDR_SPI200_DATA, the burst
* mode is disabled. When the length of data is over HIMAX_REG_SZ, the burst
* mode is enabled. Else the burst mode is disabled.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_burst_mode_enable(struct himax_ts_data *ts, u32 addr, u32 len)
{
int ret;
if (addr == HIMAX_REG_ADDR_SPI200_DATA)
ret = himax_mcu_set_burst_mode(ts, false);
else if (len > HIMAX_REG_SZ)
ret = himax_mcu_set_burst_mode(ts, true);
else
ret = himax_mcu_set_burst_mode(ts, false);
if (ret)
dev_err(ts->dev, "%s: burst enable fail!\n", __func__);
return ret;
}
/**
* himax_mcu_register_read() - Read data from IC register/sram
* @ts: Himax touch screen data
* @addr: Address to read
* @buf: Buffer to store data, caller should allocate the buffer
* @len: Length of data to read
*
* Himax TP IC has its internal register and SRAM, this function is used to
* read data from it. The reading protocol require a sequence of write and read,
* which include write address to IC and read data from IC. Thus the read/write
* operation is proctected by reg_lock mutex_unlock to protect the sequence.
* The first step is to set the burst mode for MCU, then write the address to
* AHB register to tell where to read. Then set the access direction to read,
* and read the data from AHB register. The max length of data to read is decided
* by AHB register max transfer size, but if it could't bigger then SPI controller
* max transfer size. When the length of data is over the max transfer size,
* the data will be read in multiple times.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_mcu_register_read(struct himax_ts_data *ts, u32 addr, u8 *buf, u32 len)
{
int i;
int ret;
u8 direction_switch = HIMAX_AHB_CMD_ACCESS_DIRECTION_READ;
u32 read_sz;
const u32 max_trans_sz =
min(HIMAX_HX83102J_REG_XFER_MAX, ts->spi_xfer_max_sz - HIMAX_BUS_R_HLEN);
union himax_dword_data target_addr;
mutex_lock(&ts->reg_lock);
ret = himax_burst_mode_enable(ts, addr, len);
if (ret)
goto read_end;
for (i = 0; i < len; i += read_sz) {
target_addr.dword = cpu_to_le32(addr + i);
ret = himax_write(ts, HIMAX_AHB_ADDR_BYTE_0, target_addr.byte, NULL, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: write ahb_addr_byte_0 failed\n", __func__);
goto read_end;
}
ret = himax_write(ts, HIMAX_AHB_ADDR_ACCESS_DIRECTION, NULL,
&direction_switch, 1);
if (ret < 0) {
dev_err(ts->dev, "%s: write ahb_addr_access_direction failed\n", __func__);
goto read_end;
}
read_sz = min((len - i), max_trans_sz);
ret = himax_read(ts, HIMAX_AHB_ADDR_RDATA_BYTE_0, buf + i, read_sz);
if (ret < 0) {
dev_err(ts->dev, "%s: read ahb_addr_rdata_byte_0 failed\n", __func__);
goto read_end;
}
}
read_end:
mutex_unlock(&ts->reg_lock);
if (ret < 0)
dev_err(ts->dev, "%s: addr = 0x%08X, len = %u, ret = %d\n", __func__,
addr, len, ret);
return ret;
}
/**
* himax_mcu_register_write() - Write data to IC register/sram
* @ts: Himax touch screen data
* @addr: Address to write
* @buf: Data to write
* @len: Length of data to write
*
* Himax TP IC has its internal register and SRAM, this function is used to
* write data to it. The writing protocol require a sequence of write, which
* include write address to IC and write data to IC. Thus the write operation
* is proctected by reg_lock mutex_unlock to protect the sequence. The first
* step is to set the burst mode for MCU, then write the address and data to
* AHB register. The max length of data to read is decided by AHB register max
* transfer size, but if it could't bigger then SPI controller max transfer
* size. When the length of data is over the max transfer size, the data will
* be written in multiple times.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_mcu_register_write(struct himax_ts_data *ts, u32 addr, const u8 *buf, u32 len)
{
int i;
int ret;
u32 write_sz;
const u32 max_trans_sz = min(HIMAX_HX83102J_REG_XFER_MAX,
ts->spi_xfer_max_sz - HIMAX_BUS_W_HLEN - HIMAX_REG_SZ);
union himax_dword_data target_addr;
mutex_lock(&ts->reg_lock);
ret = himax_burst_mode_enable(ts, addr, len);
if (ret)
goto write_end;
for (i = 0; i < len; i += max_trans_sz) {
write_sz = min((len - i), max_trans_sz);
target_addr.dword = cpu_to_le32(addr + i);
ret = himax_write(ts, HIMAX_AHB_ADDR_BYTE_0,
target_addr.byte, buf + i, write_sz + HIMAX_REG_SZ);
if (ret < 0) {
dev_err(ts->dev, "%s: write ahb_addr_byte_0 failed\n", __func__);
break;
}
}
write_end:
mutex_unlock(&ts->reg_lock);
if (ret < 0)
dev_err(ts->dev, "%s: addr = 0x%08X, len = %u, ret = %d\n", __func__,
addr, len, ret);
return ret;
}
/**
* himax_write_read_reg() - Write and read back data for handshake confirm
* @ts: Himax touch screen data
* @addr: Address to write
* @data: Data to write
* @hb: High byte of confirmation data
* @lb: Low byte of confirmation data
*
* Write data to IC register/sram and read back for handshake confirm. The
* process contain two stage, first stage is to write the data to IC,
* then read back the data from the same address to confirm the data is written
* successfully. The second stage is to read back data from the same address
* again to confirm handshake password is the same as confirmation data,
* which means the operation is done successfully. There may had a chance that
* data read back equals to confirmation data at first stage, in this case, return
* operation complete directly.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_write_read_reg(struct himax_ts_data *ts, u32 addr, u8 *data, u8 hb, u8 lb)
{
int ret;
u32 retry_cnt;
const u32 write_confirm_retry = 40;
const u32 read_confirm_retry = 200;
union himax_dword_data r_data, t_data;
memcpy(t_data.byte, data, HIMAX_REG_SZ);
for (retry_cnt = 0; retry_cnt < write_confirm_retry; retry_cnt++) {
ret = himax_mcu_register_read(ts, addr, r_data.byte, HIMAX_REG_SZ);
if (ret < 0) {
usleep_range(1000, 1100);
continue;
}
if (r_data.byte[1] == t_data.byte[1] && r_data.byte[0] == t_data.byte[0])
break;
else if (r_data.byte[1] == hb && r_data.byte[0] == lb)
return 0;
himax_mcu_register_write(ts, addr, t_data.byte, HIMAX_REG_SZ);
usleep_range(1000, 1100);
}
if (retry_cnt == write_confirm_retry)
goto err_retry_over;
for (retry_cnt = 0; retry_cnt < read_confirm_retry; retry_cnt++) {
ret = himax_mcu_register_read(ts, addr, r_data.byte, HIMAX_REG_SZ);
if (ret < 0) {
usleep_range(1000, 1100);
continue;
}
if (r_data.byte[1] == hb && r_data.byte[0] == lb)
return 0;
usleep_range(10000, 10100);
}
err_retry_over:
dev_err(ts->dev, "%s: failed to handshaking with DSRAM\n", __func__);
dev_err(ts->dev, "%s: addr = 0x%08X; data = 0x%02X%02X%02X%02X\n", __func__,
addr, data[3], data[2], data[1], data[0]);
dev_err(ts->dev, "%s: target = %02X%02X; r_data = %02X%02X\n", __func__,
hb, lb, r_data.byte[1], r_data.byte[0]);
return -EIO;
}
/**
* himax_mcu_interface_on() - Wakeup IC bus interface
* @ts: Himax touch screen data
*
* This function is used to wakeup IC bus interface. The IC may enter sleep mode
* and need to wakeup before any operation. The wakeup process is to read a dummy
* AHB register to wakeup the IC bus interface. Also, the function setup the burst
* mode as default for MCU and read back the burst mode setting to confirm the
* setting is written. The action is a double check to confirm the IC bus interface
* is ready for operation.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_mcu_interface_on(struct himax_ts_data *ts)
{
int ret;
u8 buf[2][HIMAX_REG_SZ];
u32 retry_cnt;
const u32 burst_retry_limit = 10;
mutex_lock(&ts->reg_lock);
/* Read a dummy register to wake up BUS. */
ret = himax_read(ts, HIMAX_AHB_ADDR_RDATA_BYTE_0, buf[0], 4);
mutex_unlock(&ts->reg_lock);
if (ret < 0) {
dev_err(ts->dev, "%s: read ahb_addr_rdata_byte_0 failed\n", __func__);
return ret;
}
for (retry_cnt = 0; retry_cnt < burst_retry_limit; retry_cnt++) {
/* AHB: read/write to SRAM in sequential order */
buf[0][0] = HIMAX_AHB_CMD_CONTI;
ret = himax_write(ts, HIMAX_AHB_ADDR_CONTI, NULL, buf[0], 1);
if (ret < 0) {
dev_err(ts->dev, "%s: write ahb_addr_conti failed\n", __func__);
return ret;
}
/* AHB: Auto increment SRAM addr+4 while each 4 bytes read/write */
buf[0][0] = HIMAX_AHB_CMD_INCR4;
ret = himax_write(ts, HIMAX_AHB_ADDR_INCR4, NULL, buf[0], 1);
if (ret < 0) {
dev_err(ts->dev, "%s: write ahb_addr_incr4 failed\n", __func__);
return ret;
}
/* Check cmd */
ret = himax_read(ts, HIMAX_AHB_ADDR_CONTI, buf[0], 1);
if (ret < 0) {
dev_err(ts->dev, "%s: read ahb_addr_conti failed\n", __func__);
return ret;
}
ret = himax_read(ts, HIMAX_AHB_ADDR_INCR4, buf[1], 1);
if (ret < 0) {
dev_err(ts->dev, "%s: read ahb_addr_incr4 failed\n", __func__);
return ret;
}
if (buf[0][0] == HIMAX_AHB_CMD_CONTI && buf[1][0] == HIMAX_AHB_CMD_INCR4)
return 0;
usleep_range(1000, 1100);
}
dev_err(ts->dev, "%s: failed!\n", __func__);
return -EIO;
}
/**
* hx83102j_pin_reset() - Reset the touch chip by hardware pin
* @ts: Himax touch screen data
*
* This function is used to hardware reset the touch chip. By pull down the
* reset pin to low over 20ms, ensure the reset circuit perform a complete reset
* to the touch chip.
*
* Return: None
*/
static void hx83102j_pin_reset(struct himax_ts_data *ts)
{
gpiod_set_value(ts->pdata.gpiod_rst, 1);
usleep_range(10000, 10100);
gpiod_set_value(ts->pdata.gpiod_rst, 0);
usleep_range(20000, 20100);
}
/**
* himax_int_enable() - Enable/Disable interrupt
* @ts: Himax touch screen data
* @enable: true for enable, false for disable
*
* This function is used to enable or disable the interrupt.
*
* Return: None
*/
static void himax_int_enable(struct himax_ts_data *ts, bool enable)
{
int irqnum = ts->himax_irq;
unsigned long flags;
spin_lock_irqsave(&ts->irq_lock, flags);
if (enable && atomic_read(&ts->irq_state) == 0) {
atomic_set(&ts->irq_state, 1);
enable_irq(irqnum);
} else if (!enable && atomic_read(&ts->irq_state) == 1) {
atomic_set(&ts->irq_state, 0);
disable_irq_nosync(irqnum);
}
spin_unlock_irqrestore(&ts->irq_lock, flags);
dev_info(ts->dev, "%s: Interrupt %s\n", __func__,
atomic_read(&ts->irq_state) ? "enabled" : "disabled");
}
/**
* himax_mcu_ic_reset() - Reset the touch chip and disable/enable interrupt
* @ts: Himax touch screen data
* @int_off: true for disable/enable interrupt, false for not
*
* This function is used to reset the touch chip with interrupt control. The
* TPIC will pull low the interrupt pin when IC is reset. When the ISR has been
* set and need to be take care of, the caller could set int_off to true to disable
* the interrupt before reset and enable the interrupt after reset.
*
* Return: None
*/
static void himax_mcu_ic_reset(struct himax_ts_data *ts, bool int_off)
{
if (int_off)
himax_int_enable(ts, false);
hx83102j_pin_reset(ts);
if (int_off)
himax_int_enable(ts, true);
}
/**
* hx83102j_reload_to_active() - Reload to active mode
* @ts: Himax touch screen data
*
* This function is used to write a flag to the IC register to make MCU restart without
* reload the firmware.
*
* Return: 0 on success, negative error code on failure
*/
static int hx83102j_reload_to_active(struct himax_ts_data *ts)
{
int ret;
u32 retry_cnt;
const u32 addr = HIMAX_REG_ADDR_RELOAD_TO_ACTIVE;
const u32 reload_to_active_cmd = 0xec;
const u32 reload_to_active_done = 0x01ec;
const u32 retry_limit = 5;
union himax_dword_data data;
for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
data.dword = cpu_to_le32(reload_to_active_cmd);
ret = himax_mcu_register_write(ts, addr, data.byte, 4);
if (ret < 0)
return ret;
usleep_range(1000, 1100);
ret = himax_mcu_register_read(ts, addr, data.byte, 4);
if (ret < 0)
return ret;
data.dword = le32_to_cpu(data.dword);
if (data.word[0] == reload_to_active_done)
break;
}
if (data.word[0] != reload_to_active_done) {
dev_err(ts->dev, "%s: Reload to active failed!\n", __func__);
return -EINVAL;
}
return 0;
}
/**
* hx83102j_en_hw_crc() - Enable/Disable HW CRC
* @ts: Himax touch screen data
* @en: true for enable, false for disable
*
* This function is used to enable or disable the HW CRC. The HW CRC
* is used to protect the SRAM data.
*
* Return: 0 on success, negative error code on failure
*/
static int hx83102j_en_hw_crc(struct himax_ts_data *ts, bool en)
{
int ret;
u32 retry_cnt;
const u32 addr = HIMAX_HX83102J_REG_ADDR_HW_CRC;
const u32 retry_limit = 5;
union himax_dword_data data, wrt_data;
if (en)
data.dword = cpu_to_le32(HIMAX_HX83102J_REG_DATA_HW_CRC);
else
data.dword = cpu_to_le32(HIMAX_HX83102J_REG_DATA_HW_CRC_DISABLE);
wrt_data.dword = data.dword;
for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
ret = himax_mcu_register_write(ts, addr, data.byte, 4);
if (ret < 0)
return ret;
usleep_range(1000, 1100);
ret = himax_mcu_register_read(ts, addr, data.byte, 4);
if (ret < 0)
return ret;
if (data.word[0] == wrt_data.word[0])
break;
}
if (data.word[0] != wrt_data.word[0]) {
dev_err(ts->dev, "%s: ECC fail!\n", __func__);
return -EINVAL;
}
return 0;
}
/**
* hx83102j_sense_off() - Stop MCU and enter safe mode
* @ts: Himax touch screen data
* @check_en: Check if need to ensure FW is stopped by its owne process
*
* Sense off is a process to make sure the MCU inside the touch chip is stopped.
* The process has two stage, first stage is to request FW to stop. Write
* HIMAX_REG_DATA_FW_GO_SAFEMODE to HIMAX_REG_ADDR_CTRL_FW tells the FW to stop by its own.
* Then read back the FW status to confirm the FW is stopped. When check_en is true,
* the function will resend the stop FW command until the retry limit reached.
* There maybe a chance that the FW is not stopped by its own, in this case, the
* safe mode in next stage still stop the MCU, but FW internal flag may not be
* configured correctly. The second stage is to enter safe mode and reset TCON.
* Safe mode is a mode that the IC circuit ensure the internal MCU is stopped.
* Since this IC is TDDI, the TCON need to be reset to make sure the IC is ready
* for next operation.
*
* Return: 0 on success, negative error code on failure
*/
static int hx83102j_sense_off(struct himax_ts_data *ts, bool check_en)
{
int ret;
u32 retry_cnt;
const u32 stop_fw_retry_limit = 35;
const u32 enter_safe_mode_retry_limit = 5;
const union himax_dword_data safe_mode = {
.dword = cpu_to_le32(HIMAX_REG_DATA_FW_GO_SAFEMODE)
};
union himax_dword_data data;
dev_info(ts->dev, "%s: check %s\n", __func__, check_en ? "True" : "False");
if (!check_en)
goto without_check;
for (retry_cnt = 0; retry_cnt < stop_fw_retry_limit; retry_cnt++) {
if (retry_cnt == 0 ||
(data.byte[0] != HIMAX_REG_DATA_FW_GO_SAFEMODE &&
data.byte[0] != HIMAX_REG_DATA_FW_RE_INIT &&
data.byte[0] != HIMAX_REG_DATA_FW_IN_SAFEMODE)) {
ret = himax_mcu_register_write(ts, HIMAX_REG_ADDR_CTRL_FW,
safe_mode.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: stop FW failed\n", __func__);
return ret;
}
}
usleep_range(10000, 11000);
ret = himax_mcu_register_read(ts, HIMAX_REG_ADDR_FW_STATUS, data.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read central state failed\n", __func__);
return ret;
}
if (data.byte[0] != HIMAX_REG_DATA_FW_STATE_RUNNING) {
dev_info(ts->dev, "%s: Do not need wait FW, Status = 0x%02X!\n", __func__,
data.byte[0]);
break;
}
ret = himax_mcu_register_read(ts, HIMAX_REG_ADDR_CTRL_FW, data.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read ctrl FW failed\n", __func__);
return ret;
}
if (data.byte[0] == HIMAX_REG_DATA_FW_IN_SAFEMODE)
break;
}
if (data.byte[0] != HIMAX_REG_DATA_FW_IN_SAFEMODE)
dev_warn(ts->dev, "%s: Failed to stop FW!\n", __func__);
without_check:
for (retry_cnt = 0; retry_cnt < enter_safe_mode_retry_limit; retry_cnt++) {
/* set Enter safe mode : 0x31 ==> 0x9527 */
data.word[0] = cpu_to_le16(HIMAX_HX83102J_SAFE_MODE_PASSWORD);
ret = himax_write(ts, HIMAX_AHB_ADDR_PSW_LB, NULL, data.byte, 2);
if (ret < 0) {
dev_err(ts->dev, "%s: enter safe mode failed\n", __func__);
return ret;
}
/* Check enter_save_mode */
ret = himax_mcu_register_read(ts, HIMAX_REG_ADDR_FW_STATUS, data.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read central state failed\n", __func__);
return ret;
}
if (data.byte[0] == HIMAX_REG_DATA_FW_STATE_SAFE_MODE) {
dev_info(ts->dev, "%s: Safe mode entered\n", __func__);
/* Reset TCON */
data.dword = cpu_to_le32(HIMAX_REG_DATA_TCON_RST);
ret = himax_mcu_register_write(ts, HIMAX_HX83102J_REG_ADDR_TCON_RST,
data.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: reset TCON failed\n", __func__);
return ret;
}
usleep_range(1000, 1100);
return 0;
}
usleep_range(5000, 5100);
hx83102j_pin_reset(ts);
}
dev_err(ts->dev, "%s: failed!\n", __func__);
return -EIO;
}
/**
* hx83102j_sense_on() - Sense on the touch chip
* @ts: Himax touch screen data
* @sw_reset: true for software reset, false for hardware reset
*
* This function is used to sense on the touch chip, which means to start running the
* FW. The process begin with wakeup the IC bus interface, then write a flag to the IC
* register to make MCU restart running the FW. When sw_reset is true, the function will
* send a command to the IC to leave safe mode. Otherwise, the function will call
* himax_mcu_ic_reset() to reset the touch chip by hardware pin.
* Then enable the HW CRC to protect sram data, and reload to active to make the MCU
* start running without reload the firmware.
*
* Return: 0 on success, negative error code on failure
*/
static int hx83102j_sense_on(struct himax_ts_data *ts, bool sw_reset)
{
int ret;
const union himax_dword_data re_init = {
.dword = cpu_to_le32(HIMAX_REG_DATA_FW_RE_INIT)
};
union himax_dword_data data;
dev_info(ts->dev, "%s: software reset %s\n", __func__, sw_reset ? "true" : "false");
ret = himax_mcu_interface_on(ts);
if (ret < 0)
return ret;
ret = himax_mcu_register_write(ts, HIMAX_REG_ADDR_CTRL_FW, re_init.byte, 4);
if (ret < 0)
return ret;
usleep_range(10000, 11000);
if (!sw_reset) {
himax_mcu_ic_reset(ts, false);
} else {
data.word[0] = cpu_to_le16(HIMAX_AHB_CMD_LEAVE_SAFE_MODE);
ret = himax_write(ts, HIMAX_AHB_ADDR_PSW_LB, NULL, data.byte, 2);
if (ret < 0)
return ret;
}
ret = hx83102j_en_hw_crc(ts, true);
if (ret < 0)
return ret;
ret = hx83102j_reload_to_active(ts);
if (ret < 0)
return ret;
return 0;
}
/**
* hx83102j_chip_detect() - Check if the touch chip is HX83102J
* @ts: Himax touch screen data
*
* This function is used to check if the touch chip is HX83102J. The process
* start with a hardware reset to the touch chip, then knock the IC bus interface
* to wakeup the IC bus interface. Then sense off the MCU to prevent bus conflict
* when reading the IC ID. The IC ID is read from the IC register, and compare
* with the expected ID. If the ID is matched, the chip is HX83102J. Due to display
* IC initial code may not ready before the IC ID is read, the function will retry
* to read the IC ID for several times to make sure the IC ID is read correctly.
* In any case, the SPI bus shouldn't have error when reading the IC ID, so the
* function will return error if the SPI bus has error. When the IC is not HX83102J,
* the function will return -ENODEV.
*
* Return: 0 on success, negative error code on failure
*/
static int hx83102j_chip_detect(struct himax_ts_data *ts)
{
int ret;
u32 retry_cnt;
const u32 read_icid_retry_limit = 5;
const u32 ic_id_mask = GENMASK(31, 8);
union himax_dword_data data;
hx83102j_pin_reset(ts);
ret = himax_mcu_interface_on(ts);
if (ret)
return ret;
ret = hx83102j_sense_off(ts, false);
if (ret)
return ret;
for (retry_cnt = 0; retry_cnt < read_icid_retry_limit; retry_cnt++) {
ret = himax_mcu_register_read(ts, HIMAX_REG_ADDR_ICID, data.byte, 4);
if (ret) {
dev_err(ts->dev, "%s: Read IC ID Fail\n", __func__);
return ret;
}
data.dword = le32_to_cpu(data.dword);
if ((data.dword & ic_id_mask) == HIMAX_REG_DATA_ICID) {
ts->ic_data.bl_size = HIMAX_HX83102J_FLASH_SIZE;
ts->ic_data.icid = data.dword;
dev_info(ts->dev, "%s: Detect IC HX83102J successfully\n", __func__);
return 0;
}
}
dev_err(ts->dev, "%s: Read driver ID register Fail! IC ID = %X,%X,%X\n", __func__,
data.byte[3], data.byte[2], data.byte[1]);
return -ENODEV;
}
/**
* himax_ts_thread() - Thread for interrupt handling
* @irq: Interrupt number
* @ptr: Pointer to the touch screen data
*
* This function is used to handle the interrupt. The function will call himax_ts_work()
* to handle the interrupt.
*
* Return: IRQ_HANDLED
*/
static irqreturn_t himax_ts_thread(int irq, void *ptr)
{
himax_ts_work((struct himax_ts_data *)ptr);
return IRQ_HANDLED;
}
/**
* __himax_ts_register_interrupt() - Register interrupt trigger
* @ts: Himax touch screen data
*
* This function is used to register the interrupt. The function will call
* devm_request_threaded_irq() to register the interrupt by the trigger type.
*
* Return: 0 on success, negative error code on failure
*/
static int __himax_ts_register_interrupt(struct himax_ts_data *ts)
{
if (ts->ic_data.interrupt_is_edge)
return devm_request_threaded_irq(ts->dev, ts->himax_irq, NULL,
himax_ts_thread,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
ts->dev->driver->name, ts);
return devm_request_threaded_irq(ts->dev, ts->himax_irq, NULL,
himax_ts_thread,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
ts->dev->driver->name, ts);
}
/**
* himax_ts_register_interrupt() - Register interrupt
* @ts: Himax touch screen data
*
* This function is a wrapper to call __himax_ts_register_interrupt() to register the
* interrupt and set irq_state.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_ts_register_interrupt(struct himax_ts_data *ts)
{
int ret;
if (!ts || !ts->himax_irq) {
dev_err(ts->dev, "%s: ts or ts->himax_irq invalid!\n", __func__);
return -EINVAL;
}
ret = __himax_ts_register_interrupt(ts);
if (!ret) {
atomic_set(&ts->irq_state, 1);
dev_info(ts->dev, "%s: irq enabled at: %d\n", __func__, ts->himax_irq);
return 0;
}
atomic_set(&ts->irq_state, 0);
dev_err(ts->dev, "%s: request_irq failed\n", __func__);
return ret;
}
/**
* himax_check_power_status() - Check power status
* @work: Work struct
*
* This function is used to check the power status. The function will call
* power_supply_is_system_supplied() to get the power status, and call
* himax_cable_detect_func() to update power status to FW.
*
* Return: None
*/
static void himax_check_power_status(struct work_struct *work)
{
struct himax_ts_data *ts = container_of(work, struct himax_ts_data,
work_pwr.work);
ts->latest_power_status = power_supply_is_system_supplied();
dev_info(ts->dev, "Update ts->latest_power_status = %X\n", ts->latest_power_status);
if (himax_cable_detect_func(ts, true))
dev_err(ts->dev, "%s: update cable status failed!\n", __func__);
}
/**
* pwr_notifier_callback() - Power notifier callback
* @self: Notifier block
* @event: Event from notifier
* @data: private data from notifier
*
* This function is used to handle the power notifier event. The function will
* schedule a delayed work to call himax_check_power_status() to check the power
* status. Due to power notifier often called multiple times, the function will
* cancel the previous delayed work and schedule a new one to work as a debounce.
*
* Return: 0
*/
static int pwr_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
{
struct himax_ts_data *ts = container_of(self, struct himax_ts_data,
power_notif);
cancel_delayed_work_sync(&ts->work_pwr);
queue_delayed_work(ts->himax_pwr_wq, &ts->work_pwr,
msecs_to_jiffies(HIMAX_DELAY_PWR_CHECK_MS));
return 0;
}
/**
* himax_pwr_register() - Register power notifier
* @work: Work struct
*
* This function is used to register the power notifier. The function will call
* power_supply_reg_notifier() to register the power notifier, and schedule a
* delayed work to call himax_check_power_status() to check the power status.
*
* Return: None
*/
static void himax_pwr_register(struct work_struct *work)
{
int ret;
struct himax_ts_data *ts = container_of(work, struct himax_ts_data,
work_pwr.work);
ts->power_notif.notifier_call = pwr_notifier_callback;
ret = power_supply_reg_notifier(&ts->power_notif);
if (ret) {
dev_err(ts->dev, "%s: Unable to register power_notif: %d\n", __func__, ret);
} else {
INIT_DELAYED_WORK(&ts->work_pwr, himax_check_power_status);
queue_delayed_work(ts->himax_pwr_wq, &ts->work_pwr,
msecs_to_jiffies(HIMAX_DELAY_PWR_INIT_CHECK_MS));
}
}
/**
* hx83102j_read_event_stack() - Read event stack from touch chip
* @ts: Himax touch screen data
* @buf: Buffer to store the data
* @length: Length of data to read
*
* This function is used to read the event stack from the touch chip. The event stack
* is an AHB output buffer, which store the touch report data.
*
* Return: 0 on success, negative error code on failure
*/
static int hx83102j_read_event_stack(struct himax_ts_data *ts, u8 *buf, u32 length)
{
u32 i;
int ret;
const u32 max_trunk_sz = ts->spi_xfer_max_sz - HIMAX_BUS_R_HLEN;
for (i = 0; i < length; i += max_trunk_sz) {
ret = himax_read(ts, HIMAX_AHB_ADDR_EVENT_STACK, buf + i,
min(length - i, max_trunk_sz));
if (ret) {
dev_err(ts->dev, "%s: read event stack error!\n", __func__);
return ret;
}
}
return 0;
}
/**
* hx83102j_chip_init_data() - Initialize the touch chip data
* @ts: Himax touch screen data
*
* This function is used to initialize hx83102j touch specific data in himax_ts_data.
* The chip_max_dsram_size is the maximum size of the DSRAM of hx83102j.
* The ic_data.enc16bits is the flag to indicate the heatmap data is transferred in
* 16 bits or 12 bits.
*
* Return: None
*/
static void hx83102j_chip_init_data(struct himax_ts_data *ts)
{
ts->chip_max_dsram_size = HIMAX_HX83102J_DSRAM_SZ;
ts->ic_data.enc16bits = false;
}
/**
* himax_touch_get() - Get touch data from touch chip
* @ts: Himax touch screen data
* @buf: Buffer to store the data
*
* This function is a wrapper to call hx83102j_read_event_stack() to read the touch
* data from the touch chip. The touch_data_sz is the size of the touch data to read,
* which is calculated by hid report descriptor provided by the firmware.
*
* Return: HIMAX_TS_SUCCESS on success, negative error code on failure. We categorize
* the error code into HIMAX_TS_GET_DATA_FAIL when the read fails, and HIMAX_TS_SUCCESS
* when the read is successful. The reason is that the may need special handling when
* the read fails.
*/
static int himax_touch_get(struct himax_ts_data *ts, u8 *buf)
{
if (hx83102j_read_event_stack(ts, buf, ts->touch_data_sz)) {
dev_err(ts->dev, "can't read data from chip!");
return HIMAX_TS_GET_DATA_FAIL;
}
return HIMAX_TS_SUCCESS;
}
/**
* himax_mcu_assign_sorting_mode() - Write sorting mode to dsram and verify
* @ts: Himax touch screen data
* @tmp_data_in: password to write
*
* This function is used to write the sorting mode password to dsram and verify the
* password is written correctly. The sorting mode password is used as a flag to
* FW to let it know which mode the touch chip is working on.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_mcu_assign_sorting_mode(struct himax_ts_data *ts, u8 *tmp_data_in)
{
int ret;
u8 rdata[4];
u32 retry_cnt;
const u32 retry_limit = 3;
for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
ret = himax_mcu_register_write(ts, HIMAX_DSRAM_ADDR_SORTING_MODE_EN,
tmp_data_in, HIMAX_REG_SZ);
if (ret < 0) {
dev_err(ts->dev, "%s: write sorting mode fail\n", __func__);
return ret;
}
usleep_range(1000, 1100);
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_SORTING_MODE_EN,
rdata, HIMAX_REG_SZ);
if (ret < 0) {
dev_err(ts->dev, "%s: read sorting mode fail\n", __func__);
return ret;
}
if (!memcmp(tmp_data_in, rdata, HIMAX_REG_SZ))
return 0;
}
dev_err(ts->dev, "%s: fail to write sorting mode\n", __func__);
return -EINVAL;
}
/**
* himax_mcu_read_FW_status() - Read FW status from touch chip
* @ts: Himax touch screen data
*
* This function is used to read the FW status from the touch chip. The FW status is
* values from dsram and register from TPIC. Which shows the FW vital working status
* for developer debug.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_mcu_read_FW_status(struct himax_ts_data *ts)
{
int i;
int ret;
size_t len;
u8 data[4];
const char * const reg_name[] = {
"DBG_MSG",
"FW_STATUS",
"DD_STATUS",
"RESET_FLAG"
};
const u32 dbg_reg_array[] = {
HIMAX_DSRAM_ADDR_DBG_MSG,
HIMAX_REG_ADDR_FW_STATUS,
HIMAX_REG_ADDR_DD_STATUS,
HIMAX_REG_ADDR_RESET_FLAG
};
len = ARRAY_SIZE(dbg_reg_array);
for (i = 0; i < len; i++) {
ret = himax_mcu_register_read(ts, dbg_reg_array[i], data, HIMAX_REG_SZ);
if (ret < 0) {
dev_err(ts->dev, "%s: read FW status fail\n", __func__);
return ret;
}
dev_info(ts->dev, "%s: %10s(0x%08X) = 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
__func__, reg_name[i], dbg_reg_array[i],
data[0], data[1], data[2], data[3]);
}
return 0;
}
/**
* himax_mcu_power_on_init() - Power on initialization
* @ts: Himax touch screen data
*
* This function is used to do the power on initialization after firmware has been
* loaded to sram. The process initialize varies IC register and dsram to make sure
* FW start running correctly. When all set, sense on the touch chip to make the FW
* start running and wait for the FW reload done password.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_mcu_power_on_init(struct himax_ts_data *ts)
{
int ret;
u32 retry_cnt;
const u32 hw_reset = 0x02;
const u32 retry_limit = 30;
union himax_dword_data data;
/* RawOut select initial */
data.dword = cpu_to_le32(HIMAX_DATA_CLEAR);
ret = himax_mcu_register_write(ts, HIMAX_HX83102J_DSRAM_ADDR_RAW_OUT_SEL, data.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: set RawOut select fail\n", __func__);
return ret;
}
/* Initial sorting mode password to normal mode */
ret = himax_mcu_assign_sorting_mode(ts, data.byte);
if (ret < 0) {
dev_err(ts->dev, "%s: assign sorting mode fail\n", __func__);
return ret;
}
/* N frame initial */
/* reset N frame back to default value 1 for normal mode */
data.dword = cpu_to_le32(1);
ret = himax_mcu_register_write(ts, HIMAX_DSRAM_ADDR_SET_NFRAME, data.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: set N frame fail\n", __func__);
return ret;
}
/* Initial FW reload status */
data.dword = cpu_to_le32(HIMAX_DATA_CLEAR);
ret = himax_mcu_register_write(ts, HIMAX_DSRAM_ADDR_2ND_FLASH_RELOAD, data.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: initial FW reload status fail\n", __func__);
return ret;
}
ret = hx83102j_sense_on(ts, false);
if (ret < 0) {
dev_err(ts->dev, "%s: sense on fail\n", __func__);
return ret;
}
dev_info(ts->dev, "%s: waiting for FW reload data\n", __func__);
for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
ret = himax_mcu_register_read(ts, HIMAX_REG_ADDR_RESET_FLAG, data.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read reset flag fail\n", __func__);
return ret;
}
/* when reset flag not expected, return EAGAIN for retry */
if (data.dword != hw_reset) {
dev_err(ts->dev, "%s: abnormal reset happened, need to reload FW\n",
__func__);
return -EAGAIN;
}
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_2ND_FLASH_RELOAD, data.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read FW reload status fail\n", __func__);
return ret;
}
/* use all 4 bytes to compare */
if (le32_to_cpu(data.dword) == HIMAX_DSRAM_DATA_FW_RELOAD_DONE) {
dev_info(ts->dev, "%s: FW reload done\n", __func__);
break;
}
dev_info(ts->dev, "%s: wait FW reload %u times\n", __func__, retry_cnt + 1);
ret = himax_mcu_read_FW_status(ts);
if (ret < 0)
dev_err(ts->dev, "%s: read FW status fail\n", __func__);
usleep_range(10000, 11000);
}
if (retry_cnt == retry_limit) {
dev_err(ts->dev, "%s: FW reload fail!\n", __func__);
return -EINVAL;
}
return 0;
}
/**
* himax_mcu_calculate_crc() - Calculate CRC-32 of given data
* @data: Data to calculate CRC
* @len: Length of data
*
* This function is used to calculate the CRC-32 of the given data. The function
* calculate the CRC-32 value by the polynomial 0x82f63b78.
*
* Return: CRC-32 value
*/
static u32 himax_mcu_calculate_crc(const u8 *data, int len)
{
int i, j, length;
u32 crc = GENMASK(31, 0);
u32 current_data;
u32 tmp;
const u32 mask = GENMASK(30, 0);
length = len / 4;
for (i = 0; i < length; i++) {
current_data = data[i * 4];
for (j = 1; j < 4; j++) {
tmp = data[i * 4 + j];
current_data += (tmp) << (8 * j);
}
crc = current_data ^ crc;
for (j = 0; j < 32; j++) {
if ((crc % 2) != 0)
crc = ((crc >> 1) & mask) ^ CRC32C_POLY_LE;
else
crc = (((crc >> 1) & mask));
}
}
return crc;
}
/**
* himax_mcu_check_crc() - Let TPIC check CRC itself
* @ts: Himax touch screen data
* @start_addr: Start address of the data in sram to check
* @reload_length: Length of the data to check
* @crc_result: CRC result for return
*
* This function is used to let TPIC check the CRC of the given data in sram. The
* function write the start address and length of the data to the TPIC, and wait for
* the TPIC to finish the CRC check. When the CRC check is done, the function read
* the CRC result from the TPIC.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_mcu_check_crc(struct himax_ts_data *ts, u32 start_addr,
int reload_length, u32 *crc_result)
{
int ret;
int length = reload_length / HIMAX_REG_SZ;
u32 retry_cnt;
const u32 retry_limit = 100;
union himax_dword_data data, addr;
addr.dword = cpu_to_le32(start_addr);
ret = himax_mcu_register_write(ts, HIMAX_REG_ADDR_RELOAD_ADDR_FROM, addr.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: write reload start address fail\n", __func__);
return ret;
}
data.word[1] = cpu_to_le16(HIMAX_REG_DATA_RELOAD_PASSWORD);
data.word[0] = cpu_to_le16(length);
ret = himax_mcu_register_write(ts, HIMAX_REG_ADDR_RELOAD_ADDR_CMD_BEAT, data.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: write reload length and password fail!\n", __func__);
return ret;
}
ret = himax_mcu_register_read(ts, HIMAX_REG_ADDR_RELOAD_ADDR_CMD_BEAT, data.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read reload length and password fail!\n", __func__);
return ret;
}
if (le16_to_cpu(data.word[0]) != length) {
dev_err(ts->dev, "%s: length verify failed!\n", __func__);
return -EINVAL;
}
for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
ret = himax_mcu_register_read(ts, HIMAX_REG_ADDR_RELOAD_STATUS, data.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read reload status fail!\n", __func__);
return ret;
}
data.dword = le32_to_cpu(data.dword);
if ((data.byte[0] & HIMAX_REG_DATA_RELOAD_DONE) != HIMAX_REG_DATA_RELOAD_DONE) {
ret = himax_mcu_register_read(ts, HIMAX_REG_ADDR_RELOAD_CRC32_RESULT,
data.byte, HIMAX_REG_SZ);
if (ret < 0) {
dev_err(ts->dev, "%s: read crc32 result fail!\n", __func__);
return ret;
}
*crc_result = le32_to_cpu(data.dword);
return 0;
}
dev_info(ts->dev, "%s: Waiting for HW ready!\n", __func__);
usleep_range(1000, 1100);
}
if (retry_cnt == retry_limit) {
ret = himax_mcu_read_FW_status(ts);
if (ret < 0)
dev_err(ts->dev, "%s: read FW status fail\n", __func__);
}
return -EINVAL;
}
/**
* himax_mcu_usb_detect_set() - Update power status to FW
* @ts: Himax touch screen data
* @plugged: Power status, 0 for not connected, 1 for connected
*
* This function is used to update the power status to the touch chip. The function
* write the power status to the TPIC, and read back to verify the write is successful.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_mcu_usb_detect_set(struct himax_ts_data *ts, bool plugged)
{
int ret;
u32 retry_cnt;
const u32 retry_limit = 5;
union himax_dword_data wdata, rdata;
for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
if (plugged)
wdata.dword = cpu_to_le32(HIMAX_DSRAM_DATA_USB_ATTACH);
else
wdata.dword = cpu_to_le32(HIMAX_DSRAM_DATA_USB_DETACH);
ret = himax_mcu_register_write(ts, HIMAX_DSRAM_ADDR_USB_DETECT, wdata.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: write USB detect status fail!\n", __func__);
return ret;
}
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_USB_DETECT, rdata.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read USB detect status fail!\n", __func__);
return ret;
}
if (rdata.dword == wdata.dword)
break;
}
if (retry_cnt == retry_limit) {
dev_err(ts->dev, "%s: Failed to set USB detect status\n", __func__);
return -EINVAL;
}
return 0;
}
/**
* himax_mcu_diag_register_set() - Set diag command for hidraw debug
* @ts: Himax touch screen data
* @diag_cmd: Diagnose command
*
* This function is used to set the diagnose parameter for hidraw ioctl. Which is the
* same as the raw out select register in TPIC. The ioctl may call in any time, so we
* call himax_mcu_interface_on() to make sure the TPIC is ready to receive the command.
* Then write data to TPIC and read back to verify the write is successful.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_mcu_diag_register_set(struct himax_ts_data *ts, u8 diag_cmd)
{
int ret;
u32 retry_cnt;
const u32 retry_limit = 50;
union himax_dword_data tmp_data, back_data;
tmp_data.dword = cpu_to_le32(diag_cmd);
ret = himax_mcu_interface_on(ts);
if (ret < 0)
return ret;
for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
ret = himax_mcu_register_write(ts, HIMAX_HX83102J_DSRAM_ADDR_RAW_OUT_SEL,
tmp_data.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: write raw out select fail!\n", __func__);
return ret;
}
ret = himax_mcu_register_read(ts, HIMAX_HX83102J_DSRAM_ADDR_RAW_OUT_SEL,
back_data.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read raw out select fail!\n", __func__);
return ret;
}
if (tmp_data.byte[0] == back_data.byte[0])
break;
}
if (tmp_data.byte[0] != back_data.byte[0]) {
dev_err(ts->dev, "%s: Failed to set diagnose register\n", __func__);
return -EINVAL;
}
return 0;
}
/**
* himax_mcu_diag_register_get() - Get diag command for hidraw debug
* @ts: Himax touch screen data
* @val: Diagnose value to return
*
* This function is used to get the diagnose parameter for hidraw ioctl.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_mcu_diag_register_get(struct himax_ts_data *ts, u32 *val)
{
int ret;
ret = himax_mcu_register_read(ts, HIMAX_HX83102J_DSRAM_ADDR_RAW_OUT_SEL, (u8 *)val, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read raw out select fail!\n", __func__);
return ret;
}
*val = le32_to_cpu(*val);
return 0;
}
/**
* himax_hid_update_info() - Update hid info
* @ts: Himax touch screen data
*
* This function is used to update the hid info from firmware image and IC data
* for hidraw ioctl to get the hid info. Which tell user space tool the touch
* information and how to update the firmware at runtime.The firmware update
* mapping tells user space tool how to update the firmware, it separates into
* bl part and main part. The bl part is used to update the bootloader, and runs
* only once. Which suits the need to update firmware through SPI. So we give
* bin_start_offset 0, and unit_sz as the size of firmware image in KB.
*
* Return: None
*/
static void himax_hid_update_info(struct himax_ts_data *ts)
{
memcpy(&ts->hid_info.fw_bin_desc, &ts->fw_bin_desc, sizeof(struct himax_bin_desc));
ts->hid_info.vid = cpu_to_be16(ts->hid_desc.vendor_id);
ts->hid_info.pid = cpu_to_be16(ts->hid_desc.product_id);
ts->hid_info.cfg_version = ts->ic_data.vendor_touch_cfg_ver;
ts->hid_info.disp_version = ts->ic_data.vendor_display_cfg_ver;
ts->hid_info.rx = ts->ic_data.rx_num;
ts->hid_info.tx = ts->ic_data.tx_num;
ts->hid_info.y_res = cpu_to_be16(ts->ic_data.y_res);
ts->hid_info.x_res = cpu_to_be16(ts->ic_data.x_res);
ts->hid_info.pt_num = ts->ic_data.max_point;
ts->hid_info.mkey_num = ts->ic_data.button_num;
/* firmware table parameters, use only bl part. */
ts->hid_info.bl_mapping.cmd = HIMAX_HID_FW_UPDATE_BL_CMD;
ts->hid_info.bl_mapping.bin_start_offset = 0;
ts->hid_info.bl_mapping.unit_sz = ts->ic_data.bl_size / 1024;
}
/**
* himax_mcu_read_FW_ver() - Read varies version from touch chip
* @ts: Himax touch screen data
*
* This function is used to read the firmware version, config version, touch config
* version, display config version, customer ID, customer info, and project info from
* the touch chip. The function will call himax_mcu_register_read() to read the data
* from the TPIC, and store the data to the IC data in himax_ts_data.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_mcu_read_FW_ver(struct himax_ts_data *ts)
{
int ret;
u8 data[HIMAX_TP_INFO_STR_LEN];
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_FW_VER, data, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read FW version fail\n", __func__);
return ret;
}
ts->ic_data.vendor_panel_ver = data[0];
ts->ic_data.vendor_fw_ver = data[1] << 8 | data[2];
dev_info(ts->dev, "%s: PANEL_VER: %X\n", __func__, ts->ic_data.vendor_panel_ver);
dev_info(ts->dev, "%s: FW_VER: %X\n", __func__, ts->ic_data.vendor_fw_ver);
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_CFG, data, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read CFG version fail\n", __func__);
return ret;
}
ts->ic_data.vendor_config_ver = data[2] << 8 | data[3];
ts->ic_data.vendor_touch_cfg_ver = data[2];
dev_info(ts->dev, "%s: TOUCH_VER: %X\n", __func__, ts->ic_data.vendor_touch_cfg_ver);
ts->ic_data.vendor_display_cfg_ver = data[3];
dev_info(ts->dev, "%s: DISPLAY_VER: %X\n", __func__, ts->ic_data.vendor_display_cfg_ver);
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_VENDOR, data, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read customer ID fail\n", __func__);
return ret;
}
ts->ic_data.vendor_cid_maj_ver = data[2];
ts->ic_data.vendor_cid_min_ver = data[3];
dev_info(ts->dev, "%s: CID_VER: %X\n", __func__, (ts->ic_data.vendor_cid_maj_ver << 8
| ts->ic_data.vendor_cid_min_ver));
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_CUS_INFO, data, HIMAX_TP_INFO_STR_LEN);
if (ret < 0) {
dev_err(ts->dev, "%s: read customer info fail\n", __func__);
return ret;
}
memcpy(ts->ic_data.vendor_cus_info, data, HIMAX_TP_INFO_STR_LEN);
dev_info(ts->dev, "%s: Cusomer ID : %s\n", __func__, ts->ic_data.vendor_cus_info);
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_PROJ_INFO, data, HIMAX_TP_INFO_STR_LEN);
if (ret < 0) {
dev_err(ts->dev, "%s: read project info fail\n", __func__);
return ret;
}
memcpy(ts->ic_data.vendor_proj_info, data, HIMAX_TP_INFO_STR_LEN);
dev_info(ts->dev, "%s: Project ID : %s\n", __func__, ts->ic_data.vendor_proj_info);
himax_hid_update_info(ts);
return 0;
}
/**
* himax_mcu_check_sorting_mode() - Read sorting mode from touch chip
* @ts: Himax touch screen data
* @tmp_data_in: Buffer to store the data
*
* This function is used to read the sorting mode from the touch chip
* for hidraw ioctl to get the sorting mode.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_mcu_check_sorting_mode(struct himax_ts_data *ts, u8 *tmp_data_in)
{
int ret;
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_SORTING_MODE_EN, tmp_data_in, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read sorting mode fail\n", __func__);
return ret;
}
return 0;
}
/**
* himax_bin_desc_data_get() - Parse descriptor data from firmware token
* @ts: Himax touch screen data
* @addr: Address of the data in firmware image
* @descript_buf: token for parsing
* @fw_all_data: Firmware image
*
* This function is used to parse the descriptor data from the firmware token. The
* descriptors are mappings of information in the firmware image. The function will
* check checksum of each token first, and then parse the token to get the related
* data. The data includes CID version, FW version, CFG version, touch config table,
* HID table, HID descriptor, and HID read descriptor.
*
* Return: true on success, false on failure
*/
static bool himax_bin_desc_data_get(struct himax_ts_data *ts,
u32 addr, u8 *descript_buf, const u8 *fw_all_data)
{
u16 chk_end;
u16 chk_sum;
u32 hid_table_addr;
u32 i, j;
u32 image_offset;
u32 map_code;
const u32 data_sz = 16;
const u32 report_desc_offset = 24;
union {
u8 *buf;
u32 *word;
} map_data;
/* looking for mapping in page, each mapping is 16 bytes */
for (i = 0; i < HIMAX_HX83102J_PAGE_SIZE; i = i + data_sz) {
chk_end = 0;
chk_sum = 0;
for (j = i; j < (i + data_sz); j++) {
chk_end |= descript_buf[j];
chk_sum += descript_buf[j];
}
if (!chk_end) { /* 1. Check all zero */
return false;
} else if (chk_sum % 0x100) { /* 2. Check sum */
dev_warn(ts->dev, "%s: chk sum failed in %X\n", __func__, i + addr);
} else { /* 3. get data */
map_data.buf = &descript_buf[i];
map_code = le32_to_cpup(map_data.word);
map_data.buf = &descript_buf[i + 4];
image_offset = le32_to_cpup(map_data.word);
/* 4. load info from FW image by specified mapping offset */
switch (map_code) {
/* Config ID */
case HIMAX_FW_CID:
ts->fw_info_table.addr_cid_ver_major = image_offset;
ts->fw_info_table.addr_cid_ver_minor = image_offset + 1;
memcpy(&ts->fw_bin_desc, &fw_all_data
[image_offset - sizeof(ts->hid_info.fw_bin_desc.passwd)],
sizeof(struct himax_bin_desc));
break;
/* FW version */
case HIMAX_FW_VER:
ts->fw_info_table.addr_fw_ver_major = image_offset;
ts->fw_info_table.addr_fw_ver_minor = image_offset + 1;
break;
/* Config version */
case HIMAX_CFG_VER:
ts->fw_info_table.addr_cfg_ver_major = image_offset;
ts->fw_info_table.addr_cfg_ver_minor = image_offset + 1;
break;
/* Touch config table */
case HIMAX_TP_CONFIG_TABLE:
ts->fw_info_table.addr_cfg_table = image_offset;
break;
/* HID table */
case HIMAX_HID_TABLE:
ts->fw_info_table.addr_hid_table = image_offset;
hid_table_addr = image_offset;
ts->fw_info_table.addr_hid_desc = hid_table_addr;
ts->fw_info_table.addr_hid_rd_desc =
hid_table_addr + report_desc_offset;
break;
}
}
}
return true;
}
/**
* himax_mcu_bin_desc_get() - Check and get the bin description from the data
* @fw: Firmware data
* @ts: Himax touch screen data
* @max_sz: Maximum size to check
*
* This function is used to check and get the bin description from the firmware data.
* It will check the given data to see if it match the bin description format, and
* call himax_bin_desc_data_get() to get the related data.
*
* Return: true on mapping_count > 0, false on otherwise
*/
static bool himax_mcu_bin_desc_get(unsigned char *fw, struct himax_ts_data *ts, u32 max_sz)
{
bool keep_on_flag;
u32 addr;
u32 mapping_count;
unsigned char *fw_buf;
const u8 header_id = 0x87;
const u8 header_id_loc = 0x0e;
const u8 header_sz = 8;
const u8 header[8] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/* Check bin is with description table or not */
if (!(memcmp(fw, header, header_sz) == 0 && fw[header_id_loc] == header_id)) {
dev_err(ts->dev, "%s: No description table\n", __func__);
return false;
}
for (addr = 0, mapping_count = 0; addr < max_sz; addr += HIMAX_HX83102J_PAGE_SIZE) {
fw_buf = &fw[addr];
/* Get related data */
keep_on_flag = himax_bin_desc_data_get(ts, addr, fw_buf, fw);
if (keep_on_flag)
mapping_count++;
else
break;
}
return mapping_count > 0;
}
/**
* himax_mcu_get_DSRAM_data() - Get DSRAM data from touch chip
* @ts: Himax touch screen data
* @info_data: Buffer to store the data
*
* This function is used to get the inspection data from DSRAM for hidraw ioctl.
* The inspection data contains capacitance data in two forms: mutual and self.
* The mutual data size is (rx_num * tx_num) * 2, and the self data size is
* (rx_num + tx_num) * 2. The first 4 bytes are the password, used as handshake
* to request FW put the data to DSRAM. And read back to confirm data is ready.
* After the data is read, the function will check the checksum of the data to
* make sure the data is correct. If the checksum is correct, the data will be
* stored to the info_data buffer. After we got the data, we will tell the FW
* that data is read, and stop outputing the data.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_mcu_get_DSRAM_data(struct himax_ts_data *ts, u8 *info_data)
{
int ret, stop_ret;
u8 *temp_info_data;
const u8 password_mask = GENMASK(7, 0);
u32 checksum;
u32 i;
u32 retry_cnt;
const u32 handshake_pwd_sz = 4;
const u32 retry_limit = 5;
const u32 x_num = ts->ic_data.rx_num;
const u32 y_num = ts->ic_data.tx_num;
/* 1. determine total size from rx tx amount */
u32 total_sz = (x_num * y_num + x_num + y_num) * 2 + handshake_pwd_sz;
union himax_dword_data data;
temp_info_data = kcalloc((total_sz + 8), sizeof(u8), GFP_KERNEL);
if (!temp_info_data)
return -ENOMEM;
/* 2. Start handshake and Wait Data Ready */
data.dword = cpu_to_le32(HIMAX_SRAM_PASSWRD_START);
ret = himax_write_read_reg(ts, HIMAX_DSRAM_ADDR_RAWDATA,
data.byte, HIMAX_SRAM_PASSWRD_END >> 8,
HIMAX_SRAM_PASSWRD_END & password_mask);
if (ret < 0) {
dev_err(ts->dev, "Data NOT ready => bypass");
kfree(temp_info_data);
return ret;
}
/* 3. Read RawData */
for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_RAWDATA,
temp_info_data, total_sz);
if (ret < 0) {
dev_err(ts->dev, "%s: read DSRAM data fail\n", __func__);
goto error_exit;
}
/*
* 4. Check Data Checksum
* start from location 2 means PASSWORD NOT included
*/
checksum = 0;
for (i = 2; i < total_sz; i += 2)
checksum += temp_info_data[i + 1] << 8 | temp_info_data[i];
if (checksum % 0x10000 != 0) {
dev_err(ts->dev, "%s: check_sum_cal fail=%08X\n", __func__, checksum);
} else {
memcpy(info_data, temp_info_data, total_sz * sizeof(u8));
break;
}
}
if (checksum % 0x10000 != 0) {
dev_err(ts->dev, "%s: retry_cnt = %u\n", __func__, retry_cnt);
ret = -EINVAL;
}
error_exit:
/* 4. FW stop outputing */
data.dword = 0;
data.byte[3] = temp_info_data[3];
data.byte[2] = temp_info_data[2];
stop_ret = himax_mcu_register_write(ts, HIMAX_DSRAM_ADDR_RAWDATA, data.byte, HIMAX_REG_SZ);
kfree(temp_info_data);
if (stop_ret < 0) {
dev_err(ts->dev, "%s: stop outputing fail\n", __func__);
return stop_ret;
}
return ret;
}
/**
* himax_mcu_tp_info_check() - Read touch information from touch chip
* @ts: Himax touch screen data
*
* This function is used to read the touch information from the touch chip. The
* information includes the touch resolution, touch point number, interrupt type,
* button number, stylus function, stylus version, and stylus ratio. These information
* is filled by FW after the FW initialized, so it must be called after FW finish
* loading.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_mcu_tp_info_check(struct himax_ts_data *ts)
{
int ret;
char data[HIMAX_REG_SZ];
u8 stylus_ratio;
u32 button_num;
u32 max_pt;
u32 rx_num;
u32 tx_num;
u32 x_res;
u32 y_res;
const u32 button_num_mask = 0x03;
const u32 interrupt_type_mask = 0x01;
const u32 interrupt_type_edge = 0x01;
bool int_is_edge;
bool stylus_func;
bool stylus_id_v2;
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_RXNUM_TXNUM, data, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read rx/tx num fail\n", __func__);
return ret;
}
rx_num = data[2];
tx_num = data[3];
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_MAXPT_XYRVS, data, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read max touch point fail\n", __func__);
return ret;
}
max_pt = data[0];
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_X_Y_RES, data, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read x/y resolution fail\n", __func__);
return ret;
}
y_res = be16_to_cpup((u16 *)&data[0]);
x_res = be16_to_cpup((u16 *)&data[2]);
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_INT_IS_EDGE, data, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read interrupt type fail\n", __func__);
return ret;
}
if ((data[1] & interrupt_type_mask) == interrupt_type_edge)
int_is_edge = true;
else
int_is_edge = false;
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_MKEY, data, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read button number fail\n", __func__);
return ret;
}
button_num = data[0] & button_num_mask;
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_STYLUS_FUNCTION, data, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read stylus function fail\n", __func__);
return ret;
}
stylus_func = data[3] ? true : false;
if (stylus_func) {
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_STYLUS_VERSION, data, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read stylus version fail\n", __func__);
return ret;
}
/* dsram_addr_stylus_version + 2 : 0=off 1=on */
stylus_id_v2 = data[2] ? true : false;
/* dsram_addr_stylus_version + 3 : 0=ratio_1 10=ratio_10 */
stylus_ratio = data[3];
}
ts->ic_data.button_num = button_num;
ts->ic_data.stylus_function = stylus_func;
ts->ic_data.rx_num = rx_num;
ts->ic_data.tx_num = tx_num;
ts->ic_data.x_res = x_res;
ts->ic_data.y_res = y_res;
ts->ic_data.max_point = max_pt;
ts->ic_data.interrupt_is_edge = int_is_edge;
if (stylus_func) {
ts->ic_data.stylus_v2 = stylus_id_v2;
ts->ic_data.stylus_ratio = stylus_ratio;
} else {
ts->ic_data.stylus_v2 = false;
ts->ic_data.stylus_ratio = 0;
}
dev_info(ts->dev, "%s: rx_num = %u, tx_num = %u\n", __func__,
ts->ic_data.rx_num, ts->ic_data.tx_num);
dev_info(ts->dev, "%s: max_point = %u\n", __func__, ts->ic_data.max_point);
dev_info(ts->dev, "%s: interrupt_is_edge = %s, stylus_function = %s\n", __func__,
ts->ic_data.interrupt_is_edge ? "true" : "false",
ts->ic_data.stylus_function ? "true" : "false");
dev_info(ts->dev, "%s: stylus_v2 = %s, stylus_ratio = %u\n", __func__,
ts->ic_data.stylus_v2 ? "true" : "false", ts->ic_data.stylus_ratio);
dev_info(ts->dev, "%s: TOUCH INFO updated\n", __func__);
return 0;
}
/**
* himax_mcu_resned_cmd_func() - Resend command collection
* @ts: Himax touch screen data
*
* This function is used to collect commands that need to be resent to TPIC after
* firmware restore. Usually we put system configuration status FW need to know
* after FW restore from system resume, firmware update or esd recovery. Currently,
* we put cable status here.
*
* return: 0 on success, negative error code on failure
*/
static int himax_mcu_resend_cmd_func(struct himax_ts_data *ts)
{
return himax_cable_detect_func(ts, true);
}
/**
* himax_disable_fw_reload() - Disable the FW reload data from flash
* @ts: Himax touch screen data
*
* This function is used to tell FW not to reload data from flash. It needs to be
* set before FW start running.
*
* return: 0 on success, negative error code on failure
*/
static int himax_disable_fw_reload(struct himax_ts_data *ts)
{
union himax_dword_data data = {
/*
* HIMAX_DSRAM_ADDR_FLASH_RELOAD: 0x10007f00
* 0x10007f00 <= 0x9aa9, let FW know there's no flash
* <= 0x5aa5, there has flash, but not reload
* <= 0x0000, there has flash, and reload
*/
.dword = cpu_to_le32(HIMAX_DSRAM_DATA_DISABLE_FLASH_RELOAD)
};
/* Disable Flash Reload */
return himax_mcu_register_write(ts, HIMAX_DSRAM_ADDR_FLASH_RELOAD, data.byte, 4);
}
/**
* himax_sram_write_crc_check() - Write the data to SRAM and check the CRC by hardware
* @ts: Himax touch screen data
* @addr: Address to write to
* @data: Data to write
* @len: Length of data
*
* This function is use to write FW code/data to SRAM and check the CRC by hardware to make
* sure the written data is correct. The FW code is designed to be CRC result 0, so if the
* CRC result is not 0, it means the written data is not correct.
*
* return: 0 on success, negative error code on failure
*/
static int himax_sram_write_crc_check(struct himax_ts_data *ts, u32 addr, const u8 *data, u32 len)
{
int ret;
u32 crc;
u32 retry_cnt;
const u32 retry_limit = 3;
for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
dev_info(ts->dev, "%s: Write FW to SRAM - total write size = %u\n", __func__, len);
ret = himax_mcu_register_write(ts, addr, data, len);
if (ret) {
dev_err(ts->dev, "%s: write FW to SRAM fail\n", __func__);
return ret;
}
ret = himax_mcu_check_crc(ts, addr, len, &crc);
if (ret) {
dev_err(ts->dev, "%s: check CRC fail\n", __func__);
return ret;
}
dev_info(ts->dev, "%s: HW CRC %s in %u time\n", __func__,
crc == 0 ? "OK" : "Fail", retry_cnt);
if (crc == 0)
break;
}
if (crc != 0) {
dev_err(ts->dev, "%s: HW CRC fail\n", __func__);
return -EINVAL;
}
return 0;
}
/**
* himax_zf_part_info() - Get and write the partition from the firmware to SRAM
* @fw: Firmware data
* @ts: Himax touch screen data
*
* This function is used to get the partition information from the firmware and write
* the partition to SRAM. The partition information includes the DSRAM address, the
* firmware offset, and the write size. The function will get the partition information
* into a table, and then write the partition to SRAM according to the table. After
* writing the partition to SRAM, the function will check the CRC by hardware to make
* sure the written data is correct.
*
* return: 0 on success, negative error code on failure
*/
static int himax_zf_part_info(const struct firmware *fw, struct himax_ts_data *ts)
{
int i;
int i_max = -1;
int i_min = -1;
int pnum;
int ret;
u8 buf[HIMAX_ZF_PARTITION_DESC_SZ];
u32 cfg_crc_sw;
u32 cfg_crc_hw;
u32 cfg_sz;
u32 dsram_base = 0xffffffff;
u32 dsram_max = 0;
u32 retry_cnt = 0;
u32 sram_min;
const u32 retry_limit = 3;
const u32 table_addr = ts->fw_info_table.addr_cfg_table;
struct himax_zf_info *info;
/* 1. initial check */
ret = hx83102j_en_hw_crc(ts, true);
if (ret < 0) {
dev_err(ts->dev, "%s: Failed to enable HW CRC\n", __func__);
return ret;
}
pnum = fw->data[table_addr + HIMAX_ZF_PARTITION_AMOUNT_OFFSET];
if (pnum < 2) {
dev_err(ts->dev, "%s: partition number is not correct\n", __func__);
return -EINVAL;
}
info = kcalloc(pnum, sizeof(struct himax_zf_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
memset(info, 0, pnum * sizeof(struct himax_zf_info));
/*
* 2. record partition information:
* partition 0: FW main code
*/
memcpy(buf, &fw->data[table_addr], HIMAX_ZF_PARTITION_DESC_SZ);
memcpy(info[0].sram_addr, buf, 4);
info[0].write_size = le32_to_cpup((u32 *)&buf[4]);
info[0].fw_addr = le32_to_cpup((u32 *)&buf[8]);
/* partition 1 ~ n: config data */
for (i = 1; i < pnum; i++) {
memcpy(buf, &fw->data[i * HIMAX_ZF_PARTITION_DESC_SZ + table_addr],
HIMAX_ZF_PARTITION_DESC_SZ);
memcpy(info[i].sram_addr, buf, 4);
info[i].write_size = le32_to_cpup((u32 *)&buf[4]);
info[i].fw_addr = le32_to_cpup((u32 *)&buf[8]);
info[i].cfg_addr = le32_to_cpup((u32 *)&info[i].sram_addr[0]);
/* Write address must be multiple of 4 */
if (info[i].cfg_addr % 4 != 0) {
info[i].cfg_addr -= (info[i].cfg_addr % 4);
info[i].fw_addr -= (info[i].cfg_addr % 4);
info[i].write_size += (info[i].cfg_addr % 4);
}
if (dsram_base > info[i].cfg_addr) {
dsram_base = info[i].cfg_addr;
i_min = i;
}
if (dsram_max < info[i].cfg_addr) {
dsram_max = info[i].cfg_addr;
i_max = i;
}
}
if (i_min < 0 || i_max < 0) {
dev_err(ts->dev, "%s: DSRAM address invalid!\n", __func__);
return -EINVAL;
}
/* 3. prepare data to update */
sram_min = info[i_min].cfg_addr;
cfg_sz = (dsram_max - dsram_base) + info[i_max].write_size;
/* Wrtie size must be multiple of 4 */
if (cfg_sz % 4 != 0)
cfg_sz = cfg_sz + 4 - (cfg_sz % 4);
dev_info(ts->dev, "%s: main code sz = %d, config sz = %d\n", __func__,
info[0].write_size, cfg_sz);
/* config size should be smaller than DSRAM size */
if (cfg_sz > ts->chip_max_dsram_size) {
dev_err(ts->dev, "%s: config size error[%d, %u]!!\n", __func__,
cfg_sz, ts->chip_max_dsram_size);
ret = -EINVAL;
goto alloc_cfg_buffer_failed;
}
memset(ts->zf_update_cfg_buffer, 0x00,
ts->chip_max_dsram_size * sizeof(u8));
/* Collect all partition in FW for DSRAM in a cfg buffer */
for (i = 1; i < pnum; i++)
memcpy(&ts->zf_update_cfg_buffer[info[i].cfg_addr - dsram_base],
&fw->data[info[i].fw_addr], info[i].write_size);
/*
* 4. write to sram
* First, write FW main code and check CRC by HW
*/
ret = himax_sram_write_crc_check(ts, le32_to_cpup((u32 *)info[0].sram_addr),
&fw->data[info[0].fw_addr], info[0].write_size);
if (ret < 0) {
dev_err(ts->dev, "%s: HW CRC fail\n", __func__);
goto write_main_code_failed;
}
/*
* Second, FW config data: Calculate CRC of CFG data which is going to write.
* CFG data don't have CRC pre-defined in FW and need to be calculated by driver.
*/
cfg_crc_sw = himax_mcu_calculate_crc(ts->zf_update_cfg_buffer, cfg_sz);
for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
/* Write hole cfg data to DSRAM */
dev_info(ts->dev, "%s: Write cfg to SRAM - total write size = %d\n",
__func__, cfg_sz);
ret = himax_mcu_register_write(ts, sram_min, ts->zf_update_cfg_buffer, cfg_sz);
if (ret < 0) {
dev_err(ts->dev, "%s: write cfg to SRAM fail\n", __func__);
goto write_cfg_failed;
}
/*
* Check CRC: Tell HW to calculate CRC from CFG start address in SRAM and check
* size is equal to size of CFG buffer written. Then we compare the two CRC data
* make sure data written is correct.
*/
ret = himax_mcu_check_crc(ts, sram_min, cfg_sz, &cfg_crc_hw);
if (ret) {
dev_err(ts->dev, "%s: check CRC failed!\n", __func__);
goto crc_failed;
}
if (cfg_crc_hw != cfg_crc_sw)
dev_err(ts->dev, "%s: Cfg CRC FAIL, HWCRC = %X, SWCRC = %X, retry = %u\n",
__func__, cfg_crc_hw, cfg_crc_sw, retry_cnt);
else
break;
}
if (retry_cnt == retry_limit && cfg_crc_hw != cfg_crc_sw) {
dev_err(ts->dev, "%s: Write cfg to SRAM fail\n", __func__);
ret = -EINVAL;
goto crc_not_match;
}
/* write back system config */
if (himax_mcu_resend_cmd_func(ts))
dev_warn(ts->dev, "%s: failed to resend config!\n", __func__);
crc_not_match:
crc_failed:
write_cfg_failed:
write_main_code_failed:
alloc_cfg_buffer_failed:
kfree(info);
return ret;
}
/**
* himax_mcu_firmware_update_zf() - Update the firmware to the touch chip
* @fw: Firmware data
* @ts: Himax touch screen data
*
* This function is used to update the firmware to the touch chip. The first step is
* to reset the touch chip, stop the MCU and then write the firmware to the touch chip.
*
* return: 0 on success, negative error code on failure
*/
static int himax_mcu_firmware_update_zf(const struct firmware *fw, struct himax_ts_data *ts)
{
int ret;
union himax_dword_data data_system_reset = {
.dword = cpu_to_le32(HIMAX_REG_DATA_SYSTEM_RESET)
};
dev_info(ts->dev, "%s: Updating FW - total FW size = %u\n", __func__, (u32)fw->size);
ret = himax_mcu_register_write(ts, HIMAX_REG_ADDR_SYSTEM_RESET, data_system_reset.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: system reset fail\n", __func__);
return ret;
}
ret = hx83102j_sense_off(ts, false);
if (ret)
return ret;
ret = himax_zf_part_info(fw, ts);
return ret;
}
/**
* himax_zf_reload_from_file() - Complete firmware update sequence
* @file_name: File name of the firmware
* @ts: Himax touch screen data
*
* This function process the full sequence of updating the firmware to the touch chip.
* It will first check if the other thread is updating now, if not, it will request the
* firmware from user space and then call himax_mcu_firmware_update_zf() to update the
* firmware, and then tell firmware not to reload data from flash and initial the touch
* chip by calling himax_mcu_power_on_init().
*
* return: 0 on success, negative error code on failure
*/
static int himax_zf_reload_from_file(char *file_name, struct himax_ts_data *ts)
{
int ret;
u32 fw_load_cnt;
const u32 fw_load_limit = 3;
const struct firmware *fw;
if (!mutex_trylock(&ts->zf_update_lock)) {
dev_warn(ts->dev, "%s: Other thread is updating now!\n", __func__);
return 0;
}
dev_info(ts->dev, "%s: Preparing to update %s!\n", __func__, file_name);
ret = request_firmware(&fw, file_name, ts->dev);
if (ret < 0) {
dev_err(ts->dev, "%s: request firmware fail, code[%d]!!\n", __func__, ret);
goto request_firmware_error;
}
for (fw_load_cnt = 0; fw_load_cnt < fw_load_limit; fw_load_cnt++) {
ret = himax_mcu_firmware_update_zf(fw, ts);
if (ret < 0)
goto load_firmware_error;
ret = himax_disable_fw_reload(ts);
if (ret < 0)
goto disable_fw_reload_error;
ret = himax_mcu_power_on_init(ts);
if (ret == -EAGAIN)
dev_err(ts->dev, "%s: initialize error, try reload FW.\n", __func__);
else
break;
}
disable_fw_reload_error:
load_firmware_error:
release_firmware(fw);
request_firmware_error:
mutex_unlock(&ts->zf_update_lock);
return ret;
}
/**
* himax_input_check() - Check the interrupt data
* @ts: Himax touch screen data
* @buf: Buffer of interrupt data
*
* This function is used to check the interrupt data. The function will check
* the interrupt data to see if it is normal or abnormal. If the interrupt data
* is all the same, it will return HIMAX_TS_ABNORMAL_PATTERN, otherwise, it will
* return HIMAX_TS_REPORT_DATA.
*
* Return: HIMAX_TS_ABNORMAL_PATTERN when all data is the same, HIMAX_TS_REPORT_DATA
* when data is normal.
*/
static int himax_input_check(struct himax_ts_data *ts, u8 *buf)
{
int i;
int length;
int same_cnt = 1;
/* Check all interrupt data */
length = ts->touch_data_sz;
if (!length)
return HIMAX_TS_REPORT_DATA;
for (i = 1; i < length; i++) {
if (buf[i] == buf[i - 1])
same_cnt++;
else
same_cnt = 1;
}
if (same_cnt == length) {
dev_warn(ts->dev, "%s: [HIMAX TP MSG] Detected abnormal input pattern\n", __func__);
return HIMAX_TS_ABNORMAL_PATTERN;
}
return HIMAX_TS_REPORT_DATA;
}
/**
* himax_hid_parse() - Parse the HID report descriptor
* @hid: HID device
*
* This function is used to parse the HID report descriptor.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_hid_parse(struct hid_device *hid)
{
int ret;
struct himax_ts_data *ts;
if (!hid)
return -ENODEV;
ts = hid->driver_data;
if (!ts)
return -EINVAL;
ret = hid_parse_report(hid, ts->hid_rd_data.rd_data, ts->hid_rd_data.rd_length);
if (ret) {
dev_err(ts->dev, "%s: failed parse report\n", __func__);
return ret;
}
return 0;
}
/**
* himax_hid_start - Start the HID device
* @hid: HID device
*
* The function for hid_ll_driver.start to start the HID device.
* This driver does not need to do anything here.
*
* Return: 0 for success
*/
static int himax_hid_start(struct hid_device *hid)
{
return 0;
}
/**
* himax_hid_stop - Stop the HID device
* @hid: HID device
*
* The function for hid_ll_driver.stop to stop the HID device.
* This driver does not need to do anything here.
*
* Return: None
*/
static void himax_hid_stop(struct hid_device *hid)
{
}
/**
* himax_hid_open - Open the HID device
* @hid: HID device
*
* The function for hid_ll_driver.open to open the HID device.
* This driver does not need to do anything here.
*
* Return: 0 for success
*/
static int himax_hid_open(struct hid_device *hid)
{
return 0;
}
/**
* himax_hid_close - Close the HID device
* @hid: HID device
*
* The function for hid_ll_driver.close to close the HID device.
* This driver does not need to do anything here.
*
* Return: None
*/
static void himax_hid_close(struct hid_device *hid)
{
}
/**
* free_firmware - Free the firmware data for hidraw run-time update
* @ts: Himax touch screen data
* @fw: Firmware data
*
* The function is used to free the firmware data for hidraw run-time update.
*
* Return: None
*/
static void free_firmware(struct himax_ts_data *ts, struct firmware *fw)
{
if (fw) {
devm_kfree(ts->dev, fw->data);
devm_kfree(ts->dev, fw->priv);
devm_kfree(ts->dev, fw);
}
}
/**
* himax_hid_load_user_firmware() - Load the firmware data for hidraw run-time update
* @ts: Himax touch screen data
* @fwdata: Firmware data, usually partial due to ioctl operation
* @sz: Size of the firmware data
*
* The function is used to load the firmware data for hidraw run-time update. The FW data
* is loaded to the hid_req_cfg.fw->data buffer. The function will check if the FW data
* is complete, if it is complete, it will return HIMAX_LOAD_FIRMWARE_DONE, otherwise, it
* will return HIMAX_LOAD_FIRMWARE_ONGOING. Due to IOCTL operation, the FW data is
* usually part of the FW image and need to be combined to a complete FW image.
*
* Return: HIMAX_LOAD_FIRMWARE_DONE when the FW data is complete, HIMAX_LOAD_FIRMWARE_ONGOING
* when the FW data is not complete
*/
static int himax_hid_load_user_firmware(struct himax_ts_data *ts, u8 *fwdata, size_t sz)
{
if (ts->hid_req_cfg.fw) {
/*
* if size is equal to complete FW image size, means new FW is be uploading
* then free the old FW. FW image size is equal to the size of the flash.
*/
if (ts->hid_req_cfg.fw->size == ts->ic_data.bl_size) {
dev_info(ts->dev, "%s: free old fw\n", __func__);
free_firmware(ts, ts->hid_req_cfg.fw);
ts->hid_req_cfg.fw = NULL;
}
}
/* if no FW data, allocate new firmware structure and FW image data space */
if (!ts->hid_req_cfg.fw) {
ts->hid_req_cfg.fw = devm_kzalloc(ts->dev, sizeof(*ts->hid_req_cfg.fw), GFP_KERNEL);
if (!ts->hid_req_cfg.fw)
return -ENOMEM;
ts->hid_req_cfg.fw->data = devm_kzalloc(ts->dev, ts->ic_data.bl_size, GFP_KERNEL);
if (!ts->hid_req_cfg.fw->data) {
devm_kfree(ts->dev, ts->hid_req_cfg.fw);
ts->hid_req_cfg.fw = NULL;
return -ENOMEM;
}
}
/* copy the FW data to the FW image buffer, exclude the ID byte */
memcpy((u8 *)ts->hid_req_cfg.fw->data + ts->hid_req_cfg.fw->size,
(u8 *)fwdata + 1, sz - 1);
ts->hid_req_cfg.fw->size += sz - 1;
/* When accumlate size is equal to FW image size, means transfer is completed */
if (ts->hid_req_cfg.fw->size == ts->ic_data.bl_size) {
dev_info(ts->dev, "%s: load firmware done\n", __func__);
return HIMAX_LOAD_FIRMWARE_DONE;
}
return HIMAX_LOAD_FIRMWARE_ONGOING;
}
/**
* himax_usi_write_cmd() - Write USI command to IC sram
* @ts: Himax touch screen data
* @cmd: USI command from user-space through HIDRAW
*
* This function write USI command from user-space to IC SRAM. It check the corresponding
* address data first, if they equals all zero in size of himax_usi_cmd. It means FW is ready
* to receive next USI command, FW will clean this address when it processed the command.
* Then we just write the command to this address for FW to process.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_usi_write_cmd(struct himax_ts_data *ts, struct himax_usi_cmd *cmd)
{
int ret;
u32 retry_cnt;
const u32 retry_limit = 3;
struct himax_usi_cmd tmp;
const struct himax_usi_cmd ready_to_write = { 0 };
for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_STYLUS_CMD, (u8 *)&tmp,
sizeof(struct himax_usi_cmd));
if (ret < 0) {
dev_err(ts->dev, "%s: read USI cmd fail\n", __func__);
return ret;
}
if (memcmp(&tmp, &ready_to_write, sizeof(struct himax_usi_cmd)) != 0)
usleep_range(1000, 2000);
else
break;
}
if (retry_cnt == retry_limit) {
dev_err(ts->dev, "%s: FW is not ready to receive USI command\n", __func__);
return -EBUSY;
}
ret = himax_mcu_register_write(ts, HIMAX_DSRAM_ADDR_STYLUS_CMD, (u8 *)cmd,
sizeof(struct himax_usi_cmd));
if (ret < 0) {
dev_err(ts->dev, "%s: write USI cmd fail\n", __func__);
return ret;
}
return 0;
}
/**
* himax_hid_get_raw_report - Process hidraw GET REPORT operation
* @hid: HID device
* @reportnum: Report ID
* @buf: Buffer for communication
* @len: Length of data in the buffer
* @report_type: Report type
*
* The function for hid_ll_driver.get_raw_report to handle the HIDRAW ioctl
* get report request. The report number to handle is based on the report
* descriptor of the HID device. The buf is used to communicate with user
* program, user pass the ID and parameters to the driver use this buf, and
* the driver will return the result to user also use this buf. The len is
* the length of data in the buf, passed by user program. The report_type is
* not used in this driver. We currently support the following report number:
* - HIMAX_ID_CONTACT_COUNT: Report the maximum number of touch points
* - HIMAX_ID_CFG: Report the configuration of the HID device
* - HIMAX_ID_FW_UPDATE_HANDSHAKING: Report the handshake status of the FW update
* - HIMAX_ID_SELF_TEST: Report the self test status
* - HIMAX_ID_TOUCH_MONITOR: Report the touch data
* - HIMAX_ID_TOUCH_MONITOR_SEL: Report current touch data type
* - HIMAX_ID_REG_RW: Report the register read data
* - HIMAX_ID_INPUT_RD_DE: Report current report descriptor disable state
* - HIMAX_ID_FW_UPDATE: Dummy report return ok only
* - HIMAX_ID_USI_COLOR: Report the stylus color data
* - HIMAX_ID_USI_WIDTH: Report the stylus width data
* - HIMAX_ID_USI_STYLE: Report the stylus style data
* - HIMAX_ID_USI_BUTTONS: Report the stylus buttons data {barrel, side, eraser}
* - HIMAX_ID_USI_FIRMWARE: Report the stylus firmware version
* - HIMAX_ID_USI_PROTOCOL: Report the stylus protocol version
* - HIMAX_ID_USI_TRANSDUCER: Report 0, write only attribute
* - HIMAX_ID_WINDOWS_BLOB_VALID: Report the windows blob data
* Case not listed here will return -EINVAL.
*
* Return: The length of the data in the buf on success, negative error code
*/
static int himax_hid_get_raw_report(const struct hid_device *hid,
unsigned char reportnum, __u8 *buf,
size_t len, unsigned char report_type)
{
int ret;
const u8 data_mask = GENMASK(7, 0);
u32 tmp_data;
struct himax_ts_data *ts;
struct himax_usi_info usi_info;
union himax_dword_data *tmp;
ts = hid->driver_data;
if (!ts) {
dev_err(ts->dev, "hid->driver_data is NULL");
return -EINVAL;
}
switch (reportnum) {
case HIMAX_ID_CONTACT_COUNT:
/* buf[0] is ID; buf[1] and later used as parameters for ID */
buf[0] = HIMAX_ID_CONTACT_COUNT;
buf[1] = ts->ic_data.max_point;
ret = len;
break;
case HIMAX_ID_CFG:
buf[0] = HIMAX_ID_CFG;
memcpy(buf + HIMAX_HID_ID_SZ, &ts->hid_info, sizeof(struct himax_hid_info));
ret = len;
break;
/*
* User check FW update status, the correct user update flow is:
* 1. set parameter to HIMAX_HID_FW_UPDATE_MAIN_CMD(0x55), send by
* HIMAX_ID_FW_UPDATE_HANDSHAKING
* 2. get HIMAX_ID_FW_UPDATE_HANDSHAKING see if status is ready to send FW
* 3. ready, send FW content by HIMAX_ID_FW_UPDATE
* 4. send complete, get HIMAX_ID_FW_UPDATE_HANDSHAKING see if status OK
* 5. by g_dummy_main_code, send 2 parts of main code which we don't use
* 6. tool start sending BL part when status is HIMAX_FWUP_BL_READY
* 7. FW image receive by himax_hid_load_user_firmware() and update
* 8. report hid_info.bl_mapping.cmd which is HIMAX_HID_FW_UPDATE_BL_CMD if tool check.
* Which means FW transfer complete.
*/
case HIMAX_ID_FW_UPDATE_HANDSHAKING:
/* Already in handshake mode */
if (ts->hid_req_cfg.processing_id == HIMAX_ID_FW_UPDATE_HANDSHAKING) {
/* Last set command is Bootloader update */
if (ts->hid_req_cfg.handshake_set == ts->hid_info.bl_mapping.cmd) {
ts->hid_req_cfg.handshake_get = ts->hid_info.bl_mapping.cmd;
/* Last set command is main code start */
} else if (ts->hid_req_cfg.handshake_set == HIMAX_HID_FW_UPDATE_MAIN_CMD) {
/*
* Just report part 0 is ready for update, rest data xferred
* size to 0
*/
ts->hid_req_cfg.handshake_get = g_dummy_main_code[0].cmd;
ts->hid_req_cfg.current_size = 0;
/* Last set command is main code part 0 start */
} else if (ts->hid_req_cfg.handshake_set == g_dummy_main_code[0].cmd) {
/* when part 0 data transfer complete */
if (ts->hid_req_cfg.current_size >= g_dummy_main_code[0].unit_sz) {
/*
* Just report part 1 is ready for update, reset xferred
* data size to 0
*/
ts->hid_req_cfg.handshake_get = g_dummy_main_code[1].cmd;
ts->hid_req_cfg.current_size = 0;
}
/* Last set command is main code part 1 start */
} else if (ts->hid_req_cfg.handshake_set == g_dummy_main_code[1].cmd) {
/* Part 1 xferred completed, request BL part */
if (ts->hid_req_cfg.current_size >= g_dummy_main_code[1].unit_sz) {
ts->hid_req_cfg.handshake_get = HIMAX_FWUP_BL_READY;
ts->hid_req_cfg.current_size = 0;
}
/* Else just report no error */
} else {
ts->hid_req_cfg.handshake_get = HIMAX_FWUP_NO_ERROR;
}
buf[0] = HIMAX_ID_FW_UPDATE_HANDSHAKING;
buf[1] = ts->hid_req_cfg.handshake_get;
/* Last command is FW sending, report last get update status */
} else if (ts->hid_req_cfg.processing_id == HIMAX_ID_FW_UPDATE) {
/* Need to lock due to progress check can have only one */
mutex_lock(&ts->hid_ioctl_lock);
buf[0] = HIMAX_ID_FW_UPDATE_HANDSHAKING;
buf[1] = ts->hid_req_cfg.handshake_get;
mutex_unlock(&ts->hid_ioctl_lock);
/* Shouldn't be here if using right tool, just return ok */
} else {
buf[0] = HIMAX_ID_FW_UPDATE_HANDSHAKING;
buf[1] = HIMAX_FWUP_NO_ERROR;
}
ret = len;
break;
/* Self test status check, return the result left in himax_self_test() */
case HIMAX_ID_SELF_TEST:
mutex_lock(&ts->hid_ioctl_lock);
buf[0] = HIMAX_ID_SELF_TEST;
buf[1] = ts->hid_req_cfg.handshake_get;
/* turn on interrupt, in case tool fail to set HIMAX_INSPECT_BACK_NORMAL */
himax_int_enable(ts, true);
mutex_unlock(&ts->hid_ioctl_lock);
ret = len;
break;
case HIMAX_ID_TOUCH_MONITOR:
ret = himax_get_data(ts, &buf[2]);
if (ret == HIMAX_INSPECT_OK) {
/* get data succeed */
buf[0] = HIMAX_ID_TOUCH_MONITOR;
buf[1] = 0;
ret = len;
} else {
ret = 0;
}
break;
case HIMAX_ID_TOUCH_MONITOR_SEL:
ret = himax_mcu_diag_register_get(ts, &tmp_data);
if (!ret) {
/* return read back value of diag register */
buf[0] = HIMAX_ID_TOUCH_MONITOR_SEL;
buf[1] = tmp_data & data_mask;
ret = len;
} else {
ret = 0;
}
break;
case HIMAX_ID_REG_RW:
/*
* standard REG RW, Address 4 bytes, Data 4 bytes all fixed
* standard REG RW len = 10 : ID(1) | R/W(1) | ADDR(4) | DATA(4)
*/
if (len == 10 &&
le32_to_cpup((u32 *)&buf[2]) != HIMAX_REG_TYPE_EXT_TYPE) {
/* standard REG RW */
ts->hid_req_cfg.reg_addr_sz = 4;
ts->hid_req_cfg.reg_data_sz = 4;
ts->hid_req_cfg.reg_addr.dword =
le32_to_cpup((u32 *)&buf[2]);
ret = himax_mcu_register_read(ts, ts->hid_req_cfg.reg_addr.dword,
ts->hid_req_cfg.reg_data,
ts->hid_req_cfg.reg_data_sz);
if (!ret) {
tmp = (union himax_dword_data *)ts->hid_req_cfg.reg_data;
tmp->dword = le32_to_cpu(tmp->dword);
buf[0] = HIMAX_ID_REG_RW;
/* Reg data at buf[6] */
memcpy(&buf[6], ts->hid_req_cfg.reg_data,
ts->hid_req_cfg.reg_data_sz);
ret = len;
} else {
ret = 0;
}
/*
* EXT type REG RW, Address 1 for AHB, 4 for SRAM/REG, Data 1~256
* EXT REG RW len >= 9 : ID(1) | R/W(1) | EXT_HDR(4) | EXT_TYPE(1) | REG_ADDR(1/4)
* | REG_DATA(1~256) => min 9, max 267
*/
} else if ((len >= 9) && (len <= (1 + HIMAX_HID_REG_SZ_MAX)) &&
(((union himax_dword_data *)&buf[2])->dword == HIMAX_REG_TYPE_EXT_TYPE)) {
switch (buf[6]) {
/* AHB type REG RW, Address 1, Data 1~256 */
case HIMAX_REG_TYPE_EXT_AHB:
ts->hid_req_cfg.reg_addr_sz = 1;
/*
* Data length = total length from user space tool - ID(1) - R/W(1)
* - EXT_HDR(4) - EXT_TYPE(1) - REG_ADDR(1)
*/
ts->hid_req_cfg.reg_data_sz = len - 1 - 1 - 4 - 1 - 1;
/* Reg addr at buf[7] */
ts->hid_req_cfg.reg_addr.dword = buf[7];
/* Call himax_read to read AHB register */
ret = himax_read(ts, ts->hid_req_cfg.reg_addr.dword,
ts->hid_req_cfg.reg_data,
ts->hid_req_cfg.reg_data_sz);
/* If read success, copy data to buf[8:] */
if (!ret) {
buf[0] = HIMAX_ID_REG_RW;
memcpy(&buf[8], ts->hid_req_cfg.reg_data,
ts->hid_req_cfg.reg_data_sz);
ret = len;
}
break;
/* SRAM/REG type REG RW, Address 4, Data 1~256 */
case HIMAX_REG_TYPE_EXT_SRAM:
ts->hid_req_cfg.reg_addr_sz = 4;
/*
* Data length = total length from user space tool - ID(1) - R/W(1)
* - EXT_HDR(4) - EXT_TYPE(1) - REG_ADDR(4)
*/
ts->hid_req_cfg.reg_data_sz = len - 1 - 1 - 4 - 1 - 4;
/* Reg addr at buf[7:10] */
ts->hid_req_cfg.reg_addr.dword =
((union himax_dword_data *)&buf[7])->dword;
ret = himax_mcu_register_read(ts, ts->hid_req_cfg.reg_addr.dword,
ts->hid_req_cfg.reg_data,
ts->hid_req_cfg.reg_data_sz);
/* If read success, copy data to buf[11:] */
if (!ret) {
buf[0] = HIMAX_ID_REG_RW;
memcpy(&buf[11], ts->hid_req_cfg.reg_data,
ts->hid_req_cfg.reg_data_sz);
ret = len;
} else {
ret = 0;
}
break;
default:
dev_err(ts->dev, "%s: Invalid ext type\n", __func__);
ret = -EINVAL;
}
} else {
dev_err(ts->dev, "%s: Invalid reg format!\n", __func__);
ret = -EINVAL;
}
break;
/* Report current report descriptor disable state */
case HIMAX_ID_INPUT_RD_DE:
buf[0] = HIMAX_ID_INPUT_RD_DE;
buf[1] = ts->hid_req_cfg.input_RD_de;
ret = len;
break;
/* HIMAX_ID_FW_UPDATE is for FW write only */
case HIMAX_ID_FW_UPDATE:
ret = 0;
break;
/* Report the stylus data */
case HIMAX_ID_USI_COLOR:
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_STYLUS_INFO, (u8 *)&usi_info,
sizeof(struct himax_usi_info));
if (ret)
break;
buf[0] = HIMAX_ID_USI_COLOR;
buf[1] = usi_info.pen_transducer;
buf[2] = usi_info.pen_color;
buf[3] = usi_info.pen_color_locked;
ret = 4;
break;
case HIMAX_ID_USI_WIDTH:
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_STYLUS_INFO, (u8 *)&usi_info,
sizeof(struct himax_usi_info));
if (ret)
break;
buf[0] = HIMAX_ID_USI_WIDTH;
buf[1] = usi_info.pen_transducer;
buf[2] = usi_info.pen_width;
buf[3] = usi_info.pen_width_locked;
ret = 4;
break;
case HIMAX_ID_USI_STYLE:
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_STYLUS_INFO, (u8 *)&usi_info,
sizeof(struct himax_usi_info));
if (ret)
break;
buf[0] = HIMAX_ID_USI_STYLE;
buf[1] = usi_info.pen_transducer;
buf[2] = usi_info.pen_style;
buf[3] = usi_info.pen_style_locked;
ret = 4;
break;
case HIMAX_ID_USI_BUTTONS:
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_STYLUS_INFO, (u8 *)&usi_info,
sizeof(struct himax_usi_info));
if (ret)
break;
buf[0] = HIMAX_ID_USI_BUTTONS;
buf[1] = usi_info.pen_transducer;
memcpy(&buf[2], usi_info.pen_buttons, 3);
ret = 5;
break;
case HIMAX_ID_USI_FIRMWARE:
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_STYLUS_INFO, (u8 *)&usi_info,
sizeof(struct himax_usi_info));
if (ret)
break;
buf[0] = HIMAX_ID_USI_FIRMWARE;
buf[1] = usi_info.pen_transducer;
memcpy(&buf[2], usi_info.pen_firmware_version, 12);
ret = 14;
break;
case HIMAX_ID_USI_PROTOCOL:
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_STYLUS_INFO, (u8 *)&usi_info,
sizeof(struct himax_usi_info));
if (ret)
break;
buf[0] = HIMAX_ID_USI_PROTOCOL;
buf[1] = usi_info.pen_transducer;
buf[2] = usi_info.pen_protocol_major;
buf[3] = usi_info.pen_protocol_minor;
ret = 4;
break;
case HIMAX_ID_USI_TRANSDUCER:
ret = 0;
break;
/* Report the windows blob data */
case HIMAX_ID_WINDOWS_BLOB_VALID:
buf[0] = HIMAX_ID_WINDOWS_BLOB_VALID;
memcpy(buf + 1, g_windows_blob_validation_key,
sizeof(g_windows_blob_validation_key));
ret = len;
break;
default:
dev_err(ts->dev, "%s: Invalid report number\n", __func__);
ret = -EINVAL;
break;
};
return ret;
}
/**
* himax_hid_set_raw_report() - process hidraw SET REPORT operation
* @hid: HID device
* @reportnum: Report ID
* @buf: Buffer for communication
* @len: Length of data in the buffer
* @report_type: Report type
*
* The function for hid_ll_driver.set_raw_report to handle the HIDRAW ioctl
* set report request. The report number to handle is based on the report
* descriptor of the HID device. The buf is used to communicate with user
* program, user pass the ID and parameters to the driver use this buf, and
* the driver will return the result to user also use this buf. The len is
* the length of data in the buf, passed by user program. The report_type is
* not used in this driver. We currently support the following report number:
* - HIMAX_ID_FW_UPDATE: Collect the firmware data and update the firmware
* - HIMAX_ID_FW_UPDATE_HANDSHAKING: Handshaking status of the FW update
* - HIMAX_ID_SELF_TEST: Start the specified self test
* - HIMAX_ID_TOUCH_MONITOR_SEL: Set the touch data type, part of the self test
* - HIMAX_ID_REG_RW: Write date to specified register/sram
* - HIMAX_ID_INPUT_RD_DE: Set the report descriptor disable state
* - HIMAX_ID_CONTACT_COUNT: Not support set report, return 0
* - HIMAX_ID_CFG: Not support set report, return 0
* - HIMAX_ID_TOUCH_MONITOR: Not support set report, return 0
* - HIMAX_ID_USI_COLOR: USI color report ID
* - HIMAX_ID_USI_WIDTH: USI width report ID
* - HIMAX_ID_USI_STYLE: USI style report ID
* - HIMAX_ID_USI_BUTTONS: USI buttons report ID
* - HIMAX_ID_USI_FIRMWARE: Not support set report, return 0
* - HIMAX_ID_USI_PROTOCOL: Not support set report, return 0
* - HIMAX_ID_USI_TRANSDUCER: USI transducer report ID for SET
* - HIMAX_ID_WINDOWS_BLOB_VALID: Not support set report, return 0
* Case not listed here will return -EINVAL.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_hid_set_raw_report(const struct hid_device *hid,
unsigned char reportnum, __u8 *buf, size_t len,
unsigned char report_type)
{
int ret;
int i;
struct himax_ts_data *ts;
struct himax_usi_cmd usi_cmd;
union himax_dword_data *tmp_data;
ts = hid->driver_data;
if (!ts) {
dev_err(ts->dev, "hid->driver_data is NULL");
return -EINVAL;
}
switch (reportnum) {
case HIMAX_ID_FW_UPDATE:
if (ts->hid_req_cfg.processing_id == HIMAX_ID_FW_UPDATE_HANDSHAKING) {
if (ts->hid_req_cfg.handshake_get == g_dummy_main_code[0].cmd) {
ts->hid_req_cfg.handshake_set = g_dummy_main_code[0].cmd;
ts->hid_req_cfg.current_size += len - 1;
ret = 0;
break;
} else if (ts->hid_req_cfg.handshake_get == g_dummy_main_code[1].cmd) {
ts->hid_req_cfg.handshake_set = g_dummy_main_code[1].cmd;
ts->hid_req_cfg.current_size += len - 1;
ret = 0;
break;
}
}
ret = himax_hid_load_user_firmware(ts, buf, len);
if (ret < 0) {
dev_err(ts->dev, "%s: load user firmware failed\n", __func__);
break;
} else if (ret == HIMAX_LOAD_FIRMWARE_ONGOING) {
ret = 0;
break;
}
dev_info(ts->dev, "%s: load user firmware succeeded\n", __func__);
ts->hid_req_cfg.processing_id = HIMAX_ID_FW_UPDATE;
ts->hid_req_cfg.handshake_get = HIMAX_FWUP_FLASH_PROG_ERROR;
himax_int_enable(ts, false);
ret = hx83102j_sense_off(ts, false);
if (ret)
break;
/* Lock it, free after re-init complete */
mutex_lock(&ts->hid_ioctl_lock);
/* Need to remove HID and probe again, queue re-init work and return immediately */
schedule_delayed_work(&ts->initial_work, msecs_to_jiffies(0));
ret = 0;
break;
case HIMAX_ID_FW_UPDATE_HANDSHAKING:
ts->hid_req_cfg.processing_id = HIMAX_ID_FW_UPDATE_HANDSHAKING;
ts->hid_req_cfg.handshake_set = buf[1];
ret = 0;
break;
case HIMAX_ID_SELF_TEST:
ts->hid_req_cfg.processing_id = HIMAX_ID_SELF_TEST;
ts->hid_req_cfg.handshake_set = buf[1];
dev_info(ts->dev, "%s: Initial self test\n", __func__);
switch (buf[1]) {
case HIMAX_HID_SELF_TEST_SHORT:
ts->hid_req_cfg.self_test_type = HIMAX_INSPECT_SHORT;
break;
case HIMAX_HID_SELF_TEST_OPEN:
ts->hid_req_cfg.self_test_type = HIMAX_INSPECT_OPEN;
break;
case HIMAX_HID_SELF_TEST_MICRO_OPEN:
ts->hid_req_cfg.self_test_type = HIMAX_INSPECT_MICRO_OPEN;
break;
case HIMAX_HID_SELF_TEST_RAWDATA:
ts->hid_req_cfg.self_test_type = HIMAX_INSPECT_RAWDATA;
break;
case HIMAX_HID_SELF_TEST_NOISE:
ts->hid_req_cfg.self_test_type = HIMAX_INSPECT_ABS_NOISE;
break;
case HIMAX_HID_SELF_TEST_RESET:
ts->hid_req_cfg.self_test_type = HIMAX_INSPECT_BACK_NORMAL;
break;
default:
dev_info(ts->dev, "Not support self test type, set to default(HIMAX_INSPECT_BACK_NORMAL)");
ts->hid_req_cfg.self_test_type = HIMAX_INSPECT_BACK_NORMAL;
}
/* Back to normal TP report mode */
if (ts->hid_req_cfg.self_test_type == HIMAX_INSPECT_BACK_NORMAL) {
ret = himax_switch_data_type(ts, HIMAX_INSPECT_BACK_NORMAL);
if (ret < 0) {
dev_err(ts->dev, "%s: switch data type to normal failed\n",
__func__);
break;
}
himax_int_enable(ts, false);
ret = himax_disable_fw_reload(ts);
if (ret < 0) {
dev_err(ts->dev, "%s: disable FW reload failed\n", __func__);
break;
}
ret = himax_mcu_power_on_init(ts);
if (ret < 0) {
dev_err(ts->dev, "%s: power on init failed\n", __func__);
break;
}
himax_int_enable(ts, true);
ret = 0;
break;
}
/* Lock it, free after self test finished */
mutex_lock(&ts->hid_ioctl_lock);
/* Disable interrupt, all work should be AP controlled */
himax_int_enable(ts, false);
/* Queue self test work, unblock hidraw interface */
queue_delayed_work(ts->himax_hidraw_wq, &ts->work_self_test,
msecs_to_jiffies(0));
ret = 0;
break;
case HIMAX_ID_TOUCH_MONITOR_SEL:
for (i = 0; i < HIMAX_HID_RAW_DATA_TYPE_MAX; i++) {
if (buf[1] == g_himax_hid_raw_data_type[i]) {
himax_mcu_diag_register_set(ts, buf[1]);
break;
}
}
if (i == HIMAX_HID_RAW_DATA_TYPE_MAX) {
dev_err(ts->dev, "%s: Not support data type\n", __func__);
ret = -EINVAL;
break;
}
ts->hid_req_cfg.processing_id = HIMAX_ID_TOUCH_MONITOR_SEL;
ts->hid_req_cfg.handshake_set = buf[1];
ret = 0;
break;
case HIMAX_ID_REG_RW:
/*
* standard REG RW, Address 4 bytes, Data 4 bytes all fixed
* standard REG RW len = 10 : ID(1) | R/W(1) | ADDR(4) | DATA(4)
*/
if (len == 10 &&
((union himax_dword_data *)&buf[2])->dword != HIMAX_REG_TYPE_EXT_TYPE) {
/* standard REG RW */
if (buf[1] == HIMAX_HID_REG_READ) {
ret = 0;
break;
}
ts->hid_req_cfg.reg_addr_sz = 4;
ts->hid_req_cfg.reg_data_sz = 4;
ts->hid_req_cfg.reg_addr.dword =
((union himax_dword_data *)&buf[2])->dword;
memcpy(ts->hid_req_cfg.reg_data, &buf[6], 4);
tmp_data = (union himax_dword_data *)(ts->hid_req_cfg.reg_data);
tmp_data->dword = cpu_to_le32(tmp_data->dword);
ret = himax_mcu_register_write(ts,
ts->hid_req_cfg.reg_addr.dword,
ts->hid_req_cfg.reg_data, 4);
/*
* EXT type REG RW, Address 1 for AHB, 4 for SRAM/REG, Data 1~256
* EXT REG RW len >= 9 : ID(1) | R/W(1) | EXT_HDR(4) | EXT_TYPE(1) | REG_ADDR(1/4)
* | REG_DATA(1~256) => min 9, max 267
*/
} else if ((len >= 9) && (len <= (1 + HIMAX_HID_REG_SZ_MAX)) &&
(((union himax_dword_data *)&buf[2])->dword == HIMAX_REG_TYPE_EXT_TYPE)) {
if (buf[1] == HIMAX_HID_REG_READ) {
ret = 0;
break;
}
switch (buf[6]) {
/* AHB type REG RW, Address 1, Data 1~256 */
case HIMAX_REG_TYPE_EXT_AHB:
ts->hid_req_cfg.reg_addr_sz = 1;
/*
* Data length = total length from user space tool - ID(1) - R/W(1)
* - EXT_HDR(4) - EXT_TYPE(1) - REG_ADDR(1)
*/
ts->hid_req_cfg.reg_data_sz = len - 1 - 1 - 4 - 1 - 1;
ts->hid_req_cfg.reg_addr.dword = buf[7];
memcpy(ts->hid_req_cfg.reg_data, &buf[8],
ts->hid_req_cfg.reg_data_sz);
ret = himax_write(ts, ts->hid_req_cfg.reg_addr.dword, NULL,
ts->hid_req_cfg.reg_data,
ts->hid_req_cfg.reg_data_sz);
break;
/* SRAM/REG type REG RW, Address 4, Data 1~256 */
case HIMAX_REG_TYPE_EXT_SRAM:
ts->hid_req_cfg.reg_addr_sz = 4;
/*
* Data length = total length from user space tool - ID(1) - R/W(1)
* - EXT_HDR(4) - EXT_TYPE(1) - REG_ADDR(4)
*/
ts->hid_req_cfg.reg_data_sz = len - 1 - 1 - 4 - 1 - 4;
ts->hid_req_cfg.reg_addr.dword =
((union himax_dword_data *)&buf[7])->dword;
memcpy(ts->hid_req_cfg.reg_data, &buf[11],
ts->hid_req_cfg.reg_data_sz);
ret = himax_mcu_register_write(ts,
ts->hid_req_cfg.reg_addr.dword,
ts->hid_req_cfg.reg_data,
ts->hid_req_cfg.reg_data_sz);
break;
default:
dev_err(ts->dev, "%s: Invalid ext type\n", __func__);
ret = -EINVAL;
break;
}
} else {
dev_err(ts->dev, "%s: Invalid reg format!\n", __func__);
ret = -EINVAL;
break;
}
ts->hid_req_cfg.processing_id = HIMAX_ID_REG_RW;
ts->hid_req_cfg.handshake_set = ts->hid_req_cfg.reg_addr.dword;
break;
case HIMAX_ID_INPUT_RD_DE:
ts->hid_req_cfg.processing_id = HIMAX_ID_INPUT_RD_DE;
ts->hid_req_cfg.handshake_set = !!buf[1];
if (ts->hid_req_cfg.input_RD_de != (!!buf[1])) {
ts->hid_req_cfg.input_RD_de = !!buf[1];
/* Re-register HID to update report descriptor */
queue_delayed_work(ts->himax_hidraw_wq, &ts->work_hid_update,
msecs_to_jiffies(0));
}
ret = 0;
break;
case HIMAX_ID_USI_COLOR:
case HIMAX_ID_USI_WIDTH:
case HIMAX_ID_USI_STYLE:
case HIMAX_ID_USI_BUTTONS:
case HIMAX_ID_USI_TRANSDUCER:
memset(&usi_cmd, 0, sizeof(struct himax_usi_cmd));
memcpy(&usi_cmd, buf, min(len, sizeof(struct himax_usi_cmd)));
ret = himax_usi_write_cmd(ts, &usi_cmd);
break;
case HIMAX_ID_USI_FIRMWARE:
case HIMAX_ID_USI_PROTOCOL:
case HIMAX_ID_CONTACT_COUNT:
case HIMAX_ID_CFG:
case HIMAX_ID_TOUCH_MONITOR:
case HIMAX_ID_WINDOWS_BLOB_VALID:
ret = 0;
break;
default:
ret = -EINVAL;
};
return ret;
}
/**
* himax_raw_request - Handle the HIDRAW ioctl request
* @hid: HID device
* @reportnum: Report ID
* @buf: Buffer for communication
* @len: Length of data in the buffer
* @rtype: Report type
* @reqtype: Request type
*
* The function for hid_ll_driver.raw_request to handle the HIDRAW ioctl
* request. We handle only the GET_REPORT and SET_REPORT request.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_raw_request(struct hid_device *hid, unsigned char reportnum, __u8 *buf,
size_t len, unsigned char rtype, int reqtype)
{
int ret;
switch (reqtype) {
case HID_REQ_GET_REPORT:
ret = himax_hid_get_raw_report(hid, reportnum, buf, len, rtype);
break;
case HID_REQ_SET_REPORT:
if (buf[0] != reportnum) {
ret = -EINVAL;
break;
}
ret = himax_hid_set_raw_report(hid, reportnum, buf, len, rtype);
break;
default:
ret = -EINVAL;
}
return ret;
}
static struct hid_ll_driver himax_hid_ll_driver = {
.parse = himax_hid_parse,
.start = himax_hid_start,
.stop = himax_hid_stop,
.open = himax_hid_open,
.close = himax_hid_close,
.raw_request = himax_raw_request
};
/**
* himax_hid_report() - Report input data to HID core
* @ts: Himax touch screen data
* @data: Data to report
* @len: Length of the data
*
* This function is a wrapper to report input data to HID core.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_hid_report(const struct himax_ts_data *ts, u8 *data, s32 len)
{
return hid_input_report(ts->hid, HID_INPUT_REPORT, data, len, 1);
}
/**
* himax_hid_probe() - Probe the HID device
* @ts: Himax touch screen data
*
* This function is used to probe the HID device.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_hid_probe(struct himax_ts_data *ts)
{
int ret;
struct hid_device *hid;
hid = ts->hid;
if (hid) {
hid_destroy_device(hid);
hid = NULL;
}
hid = hid_allocate_device();
if (IS_ERR(hid)) {
ret = PTR_ERR(hid);
return ret;
}
hid->driver_data = ts;
hid->ll_driver = &himax_hid_ll_driver;
hid->bus = BUS_SPI;
hid->dev.parent = &ts->spi->dev;
hid->version = ts->hid_desc.bcd_version;
hid->vendor = ts->hid_desc.vendor_id;
hid->product = ts->hid_desc.product_id;
snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "hid-hxtp",
hid->vendor, hid->product);
ret = hid_add_device(hid);
if (ret) {
dev_err(ts->dev, "%s: failed add hid device\n", __func__);
goto err_hid_data;
}
ts->hid = hid;
return 0;
err_hid_data:
hid_destroy_device(hid);
return ret;
}
/**
* himax_hid_remove() - Remove the HID device
* @ts: Himax touch screen data
*
* This function is used to remove the HID device.
* It will free the firmware space if it is not NULL.
*
* Return: None
*/
static void himax_hid_remove(struct himax_ts_data *ts)
{
mutex_lock(&ts->hid_ioctl_lock);
if (ts && ts->hid)
hid_destroy_device(ts->hid);
else
goto out;
ts->hid = NULL;
if (ts->hid_req_cfg.fw) {
dev_info(ts->dev, "%s: free fw\n", __func__);
free_firmware(ts, ts->hid_req_cfg.fw);
ts->hid_req_cfg.fw = NULL;
}
out:
mutex_unlock(&ts->hid_ioctl_lock);
}
/**
* himax_mcu_ic_excp_check() - Check the exception type
* @ts: Himax touch screen data
* @buf: input buffer
*
* This function is used to categorize the exception type and report the exception
* event to caller. The event type is categorized into exception event and all zero
* event. The function will check the first byte of interrupt data only because
* previous function has already confirm all data is the same. If the 1st byte data
* is not zero then return HIMAX_TS_EXCP_EVENT. Otherwise, it will increment the
* global all zero event count and check if it reached exception threshold. If it
* reached, it will return HIMAX_TS_EXCP_EVENT. If it is not reached, it will return
* HIMAX_TS_ZERO_EVENT_CNT.
*
* return:
* - HIMAX_TS_EXCP_EVENT : recovery is needed
* - HIMAX_TS_ZERO_EVENT_CNT : all zero event checked
*/
static int himax_mcu_ic_excp_check(struct himax_ts_data *ts, const u8 *buf)
{
bool excp_flag = false;
const u32 all_zero_excp_event_threshold = 5;
switch (buf[0]) {
case 0x00:
dev_info(ts->dev, "%s: [HIMAX TP MSG]: EXCEPTION event checked - ALL 0x00\n",
__func__);
excp_flag = false;
break;
default:
dev_info(ts->dev, "%s: [HIMAX TP MSG]: EXCEPTION event checked - All 0x%02X\n",
__func__, buf[0]);
excp_flag = true;
}
if (!excp_flag) {
ts->excp_zero_event_count++;
dev_info(ts->dev, "%s: ALL Zero event %d times\n", __func__,
ts->excp_zero_event_count);
if (ts->excp_zero_event_count == all_zero_excp_event_threshold) {
ts->excp_zero_event_count = 0;
return HIMAX_TS_EXCP_EVENT;
}
return HIMAX_TS_ZERO_EVENT_CNT;
}
ts->excp_zero_event_count = 0;
return HIMAX_TS_EXCP_EVENT;
}
/**
* himax_excp_hw_reset() - Do the ESD recovery
* @ts: Himax touch screen data
*
* This function is used to do the ESD recovery. It will remove the HID device,
* reload the firmware, and probe the HID device again. Because finger/stylus
* may stuck on the touch screen, it will remove the HID device first to avoid
* this situation.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_excp_hw_reset(struct himax_ts_data *ts)
{
int ret;
dev_info(ts->dev, "%s: START EXCEPTION Reset\n", __func__);
himax_hid_remove(ts);
ret = himax_zf_reload_from_file(ts->firmware_name, ts);
if (ret) {
dev_err(ts->dev, "%s: update FW fail, error: %d\n", __func__, ret);
return ret;
}
dev_info(ts->dev, "%s: update FW success\n", __func__);
ret = himax_hid_probe(ts);
if (ret) {
dev_err(ts->dev, "%s: hid probe fail, error: %d\n", __func__, ret);
return ret;
}
dev_info(ts->dev, "%s: END EXCEPTION Reset\n", __func__);
return 0;
}
/**
* himax_ts_event_check() - Check the abnormal interrupt data
* @ts: Himax touch screen data
* @buf: Interrupt data
*
* This function is used to check the abnormal interrupt data.
* If the data pattern matched the exception pattern, it will try to do
* the ESD recovery. For all zero data, it needs to be continuous reported
* for several times to trigger the ESD recovery(checked by himax_mcu_ic_excp_check())
*
* Return:
* - HIMAX_TS_EXCP_EVENT : exception recovery event
* - HIMAX_TS_ZERO_EVENT_CNT : zero event count
* - HIMAX_TS_EXCP_REC_OK : exception recovery done
* - HIMAX_TS_EXCP_REC_FAIL : exception recovery error
*/
static int himax_ts_event_check(struct himax_ts_data *ts, const u8 *buf)
{
int ret;
/* The first data read out right after chip reset is invalid. Drop it. */
if (ts->excp_reset_active) {
ts->excp_reset_active = false;
dev_info(ts->dev, "%s: Skip data after reset\n", __func__);
return HIMAX_TS_EXCP_REC_OK;
}
/* No data after reset, check exception pattern */
ret = himax_mcu_ic_excp_check(ts, buf);
switch (ret) {
/* Exception pattern matched, do recovery */
case HIMAX_TS_EXCP_EVENT:
/* Print debug message only, no check return */
himax_mcu_read_FW_status(ts);
ret = himax_excp_hw_reset(ts);
if (ret) {
dev_err(ts->dev, "%s: Recovery error!\n", __func__);
return HIMAX_TS_EXCP_REC_FAIL;
}
ts->excp_reset_active = true;
ret = HIMAX_TS_EXCP_EVENT;
break;
/* All zero event, but not reach reset threshold print debug message only */
case HIMAX_TS_ZERO_EVENT_CNT:
/* Print debug message only, no check return */
himax_mcu_read_FW_status(ts);
break;
}
return ret;
}
/**
* himax_err_ctrl() - ESD check and recovery
* @ts: Himax touch screen data
* @buf: Interrupt data
*
* This function is used to check the interrupt data. Use himax_input_check()
* to check the data. If the data is not valid, it will call himax_ts_event_check()
* to see if data match the exception pattern and do the ESD recovery when needed.
* If the data is valid, it will clear the exception counters and return
* HIMAX_TS_REPORT_DATA.
*
* Return:
* - HIMAX_TS_REPORT_DATA : valid data
* - HIMAX_TS_EXCP_EVENT : exception match
* - HIMAX_TS_ZERO_EVENT_CNT: zero frame event counted
* - HIMAX_TS_EXCP_REC_OK : exception recovery done
* - HIMAX_TS_EXCP_REC_FAIL : exception recovery error
*/
static int himax_err_ctrl(struct himax_ts_data *ts, u8 *buf)
{
int ret;
ret = himax_input_check(ts, buf);
if (ret == HIMAX_TS_ABNORMAL_PATTERN)
return himax_ts_event_check(ts, buf);
/* data check passed, clear excp counters */
ts->excp_zero_event_count = 0;
ts->excp_reset_active = false;
return ret;
}
/**
* heatmap_decompress_12bits() - Decompress 12bits heatmap data to 16bits
* @ts: Himax touch screen data
* @in_buf: Compressed heatmap data
* @target: Decompressed heatmap data
*
* This function is used to decompress the 12bits heatmap data to 16bits.
* The 12bits heatmap data is compressed in 3 bytes for 2 pixels, here we
* restore each 3 bytes to 4 bytes in the target buffer. the format of 12 bits:
* [value 1 low byte]|[value 2 low byte]|[value 1 high nibble][value 2 high nibble]
* 8 bits 8 bits 4 bits 4 bits
* restored 16 bits:
* [high nibble][low byte] : mask 0x0FFF, MSB 4 bits are ignored
*
* Return: None
*/
static void heatmap_decompress_12bits(struct himax_ts_data *ts, u8 *in_buf, u8 *target)
{
int i;
/* 1 byte for ID, else are HEATMAP headers */
const int in_offset = HIMAX_HEAT_MAP_INFO_SZ + 1;
const int heatmap_data_sz = ts->ic_data.rx_num * ts->ic_data.tx_num;
u8 *in_ptr;
const u8 h_nibble = GENMASK(7, 4);
const u8 l_nibble = GENMASK(3, 0);
u16 *target_ptr;
/* Copy headers to target buffer */
memcpy(target, in_buf, in_offset);
/* Restore 2 16bits raw data each time */
for (i = 0; i < heatmap_data_sz; i += 2) {
/* locate offset in in_buf, step 3 bytes each time */
in_ptr = &in_buf[in_offset + i * 3 / 2];
/* locate target offset, step 2 16bits each time */
target_ptr = (u16 *)&target[in_offset + i * 2];
/* 1st 16bits of 2 16bits raw data */
*target_ptr = (u16)in_ptr[0] | ((u16)(in_ptr[2] & h_nibble) << 4);
/* 2nd 16bits of 2 16bits raw data */
*(target_ptr + 1) = (u16)in_ptr[1] | (u16)(in_ptr[2] & l_nibble) << 8;
}
}
/**
* himax_ts_operation() - Process the touch interrupt data
* @ts: Himax touch screen data
*
* This function is used to process the touch interrupt data. It will
* call the himax_touch_get() to get the touch data.
* Check the data by calling the himax_err_ctrl() to see if the data is
* valid. If the data is not valid, it also process the ESD recovery.
* If the hid is probed, it will call the himax_hid_report() to report the
* touch data to the HID core. Due to the report data must match the HID
* report descriptor, the size of report data is fixed. To prevent next report
* data being contaminated by the previous data, all the data must be reported
* wheather previous data is valid or not.
* The heatmap data will be decompressed by calling the heatmap_decompress_12bits()
* if heatmap is not encoded in 16bits, otherwise it will be copied directly.
*
* Return: HIMAX_TS_SUCCESS on success, negative error code in
* himax_touch_report_status on failure
*/
static int himax_ts_operation(struct himax_ts_data *ts)
{
int ret;
u32 offset;
memset(ts->xfer_buf, 0x00, ts->xfer_buf_sz);
ret = himax_touch_get(ts, ts->xfer_buf);
if (ret == HIMAX_TS_GET_DATA_FAIL)
return ret;
ret = himax_err_ctrl(ts, ts->xfer_buf);
if (!(ret == HIMAX_TS_REPORT_DATA))
return ret;
if (ts->hid_probed) {
offset = 0;
if (!ts->hid_req_cfg.input_RD_de)
ret = himax_hid_report(ts,
ts->xfer_buf + offset + HIMAX_HID_REPORT_HDR_SZ,
ts->hid_desc.max_input_length -
HIMAX_HID_REPORT_HDR_SZ);
offset = ts->hid_desc.max_input_length;
if (ts->ic_data.stylus_function) {
if (!ts->hid_req_cfg.input_RD_de)
ret += himax_hid_report(ts,
ts->xfer_buf +
offset + HIMAX_HID_REPORT_HDR_SZ,
ts->hid_desc.max_input_length -
HIMAX_HID_REPORT_HDR_SZ);
offset += ts->hid_desc.max_input_length;
}
if (!ts->ic_data.enc16bits)
heatmap_decompress_12bits(ts,
ts->xfer_buf + offset + HIMAX_HID_REPORT_HDR_SZ,
ts->heatmap_buf);
else
memcpy(ts->heatmap_buf,
ts->xfer_buf + offset + HIMAX_HID_REPORT_HDR_SZ,
ts->heatmap_data_size + HIMAX_HEAT_MAP_INFO_SZ + 1);
ret += himax_hid_report(ts, ts->heatmap_buf,
(ts->ic_data.rx_num * ts->ic_data.tx_num * 2) +
HIMAX_HEAT_MAP_INFO_SZ + 1);
}
if (ret != 0)
return HIMAX_TS_GET_DATA_FAIL;
return HIMAX_TS_SUCCESS;
}
/**
* himax_cable_detect_func - cable status update handler
* @ts: himax touch screen data
* @force_renew: force renew cable status
*
* This function is used to maintain the cable status and call the
* corresponding function to update the cable status. If the update action
* is failed, it will print the error message and retry the action at the next
* time called.
*
* Return: Return: 0 on success, negative error code on failure
*/
static int himax_cable_detect_func(struct himax_ts_data *ts, bool force_renew)
{
int ret;
bool connect_status = ts->latest_power_status > 0 ? true : false;
if (connect_status != ts->usb_connected || force_renew) {
ret = himax_mcu_usb_detect_set(ts, connect_status);
if (ret < 0) {
dev_err(ts->dev, "%s: Cable status change to %s failed\n", __func__,
connect_status ? "connected" : "disconnected");
return ret;
}
ts->usb_connected = connect_status;
dev_info(ts->dev, "%s: Cable status change: %s\n", __func__,
ts->usb_connected ? "connected" : "disconnected");
}
return 0;
}
/**
* himax_ts_work() - Work function for the touch screen
* @ts: Himax touch screen data
*
* This function is used to handle interrupt bottom half work. It will
* call the himax_ts_operation() to get the touch data, dispatch the data
* to HID core. If the touch data is not valid, it will reset the TPIC.
* It will also call the hx83102j_reload_to_active() after the reset action.
* It will also call the himax_cable_detect_func() to check the cable status,
* and update the cable status to the FW if needed.
*
* Return: void
*/
static void himax_ts_work(struct himax_ts_data *ts)
{
himax_cable_detect_func(ts, false);
if (himax_ts_operation(ts) == HIMAX_TS_GET_DATA_FAIL) {
dev_info(ts->dev, "%s: Now reset the Touch chip\n", __func__);
himax_mcu_ic_reset(ts, true);
if (hx83102j_reload_to_active(ts))
dev_warn(ts->dev, "%s: Reload to active failed\n", __func__);
}
}
/**
* himax_update_fw() - update firmware using firmware structure
* @ts: Himax touch screen data
*
* This function use already initialize firmware structure in ts to update
* firmware.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_update_fw(struct himax_ts_data *ts)
{
int ret;
u32 retry_cnt;
const u32 retry_limit = 3;
for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
ret = himax_mcu_firmware_update_zf(ts->himax_fw, ts);
if (ret < 0) {
dev_err(ts->dev, "%s: TP upgrade error, upgrade_times = %d\n", __func__,
retry_cnt);
} else {
dev_info(ts->dev, "%s: TP FW upgrade OK\n", __func__);
return 0;
}
}
return -EIO;
}
/**
* himax_hid_rd_init() - Initialize the HID report descriptor
* @ts: Himax touch screen data
*
* The function is used to calculate the size of the HID report descriptor,
* allocate the memory and copy the report descriptor from firmware data to
* the allocated memory for later hid device registration.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_hid_rd_init(struct himax_ts_data *ts)
{
u32 rd_sz;
const u32 x_num = ts->ic_data.rx_num;
const u32 y_num = ts->ic_data.tx_num;
/* Data in 16bits and handshake data 4 bytes */
u32 raw_data_sz = (x_num * y_num + x_num + y_num) * 2 + 4;
/*
* If hidraw input debug function is enabled, the rd_sz is combined by
* heatmap_rd size and hidraw_debug_rd size. Otherwise the rd_sz is
* combined by FW RD size and hidraw_debug_rd size.
*/
if (!ts->hid_req_cfg.input_RD_de)
rd_sz = ts->hid_desc.report_desc_length + g_host_ext_report_desc_sz;
else
rd_sz = g_host_heatmap_report_desc_sz + g_host_ext_report_desc_sz;
/* fw_info_table should contain address of hid_rd_desc in FW image */
if (ts->fw_info_table.addr_hid_rd_desc != 0) {
/* if rd_sz has been change, need to release old one */
if (ts->hid_rd_data.rd_data &&
rd_sz != ts->hid_rd_data.rd_length) {
devm_kfree(ts->dev, ts->hid_rd_data.rd_data);
ts->hid_rd_data.rd_data = NULL;
}
if (!ts->hid_rd_data.rd_data) {
ts->hid_rd_data.rd_data = devm_kzalloc(ts->dev, rd_sz, GFP_KERNEL);
if (!ts->hid_rd_data.rd_data)
return -ENOMEM;
}
/* Copy the base RD from firmware table or heatmap RD only */
if (!ts->hid_req_cfg.input_RD_de) {
memcpy((void *)ts->hid_rd_data.rd_data,
&ts->himax_fw->data[ts->fw_info_table.addr_hid_rd_desc],
ts->hid_desc.report_desc_length);
ts->hid_rd_data.rd_length = ts->hid_desc.report_desc_length;
} else {
memcpy((void *)ts->hid_rd_data.rd_data,
g_heatmap_rd.host_report_descriptor,
g_host_heatmap_report_desc_sz);
ts->hid_rd_data.rd_length = g_host_heatmap_report_desc_sz;
}
/* Update monitor report_cnt by actual rawdata size */
g_host_ext_rd.rd_struct.monitor.report_cnt = cpu_to_le16(raw_data_sz);
/* Append hidraw_debug_rd to hid_rd_data */
memcpy((void *)(ts->hid_rd_data.rd_data + ts->hid_rd_data.rd_length),
&g_host_ext_rd.host_report_descriptor, g_host_ext_report_desc_sz);
ts->hid_rd_data.rd_length += g_host_ext_report_desc_sz;
}
return 0;
}
/**
* himax_hid_register() - Register the HID device
* @ts: Himax touch screen data
*
* The function is used to register the HID device. If the hid is probed,
* it will destroy the previous hid device and re-probe the hid device.
*
* Return: None
*/
static void himax_hid_register(struct himax_ts_data *ts)
{
if (ts->hid_probed) {
hid_destroy_device(ts->hid);
ts->hid = NULL;
ts->hid_probed = false;
}
if (himax_hid_probe(ts)) {
dev_err(ts->dev, "%s: hid probe fail\n", __func__);
ts->hid_probed = false;
} else {
ts->hid_probed = true;
}
}
/**
* himax_hid_report_data_init() - Calculate the size of the HID report data
* @ts: Himax touch screen data
*
* The function is used to calculate the final size of the HID report data.
* The base size is equal to the max_input_length of the hid descriptor.
* Plus the size of the heatmap data and its header. If the heatmap data is
* encoded in 12bits, the size of heatmap is 1.5 times of the size of the
* rx_num * tx_num. Otherwise, the size of heatmap is 2 times of the size of
* the rx_num * tx_num.
* If the size of the HID report data is not equal to the previous size, it
* will free the previous allocated memory and allocate the new memory which
* size is equal to the final size of touch_data_sz.
* It will also call the himax_heatmap_data_init() to initialize the heatmap
* data.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_hid_report_data_init(struct himax_ts_data *ts)
{
ts->touch_data_sz = ts->hid_desc.max_input_length;
if (ts->ic_data.stylus_function)
ts->touch_data_sz += ts->hid_desc.max_input_length;
ts->touch_data_sz += HIMAX_HEAT_MAP_HEADER_SZ;
ts->touch_data_sz += HIMAX_HEAT_MAP_INFO_SZ;
if (!ts->ic_data.enc16bits)
ts->heatmap_data_size = (ts->ic_data.rx_num * ts->ic_data.tx_num * 3) / 2;
else
ts->heatmap_data_size = (ts->ic_data.rx_num * ts->ic_data.tx_num * 2);
ts->touch_data_sz += ts->heatmap_data_size;
if (ts->touch_data_sz != ts->xfer_buf_sz) {
kfree(ts->xfer_buf);
ts->xfer_buf_sz = 0;
ts->xfer_buf = kzalloc(ts->touch_data_sz, GFP_KERNEL);
if (!ts->xfer_buf)
return -ENOMEM;
ts->xfer_buf_sz = ts->touch_data_sz;
}
if (himax_heatmap_data_init(ts)) {
dev_err(ts->dev, "%s: report data init fail\n", __func__);
return -ENOMEM;
}
return 0;
}
/**
* himax_power_set() - Set the power supply of touch screen
* @ts: Himax touch screen data
* @en: enable/disable regualtor
*
* This function is used to set the power supply of touch screen.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_power_set(struct himax_ts_data *ts, bool en)
{
int ret;
struct himax_platform_data *pdata = &ts->pdata;
if (pdata->vccd_supply) {
if (en)
ret = regulator_enable(pdata->vccd_supply);
else
ret = regulator_disable(pdata->vccd_supply);
if (ret) {
dev_err(ts->dev, "%s: unable to %s vccd supply\n", __func__,
en ? "enable" : "disable");
return ret;
}
}
if (pdata->vccd_supply)
usleep_range(2000, 2100);
return 0;
}
/**
* himax_power_deconfig() - De-configure the power supply of touchsreen
* @pdata: Himax platform data
*
* This function is used to de-configure the power supply of touch screen.
*
* Return: None
*/
static void himax_power_deconfig(struct himax_platform_data *pdata)
{
if (pdata->vccd_supply) {
regulator_disable(pdata->vccd_supply);
regulator_put(pdata->vccd_supply);
}
}
/**
* himax_initial_work() - Initial work for the touch screen
* @work: Work structure
*
* This function is used to do the initial work for the touch screen. It will
* call the request_firmware() to get the firmware from the file system, and parse the
* mapping table in 1k header. If the headers are parsed successfully, it will
* call the himax_update_fw() to update the firmware and power on the touch screen.
* If the power on action is successful, it will load the hid descriptor and
* check the touch panel information. If the touch panel information is correct,
* it will call the himax_hid_rd_init() to initialize the HID report descriptor,
* and call the himax_hid_register() to register the HID device. After all is done,
* it will release the firmware and enable the interrupt.
*
* Return: None
*/
static void himax_initial_work(struct work_struct *work)
{
struct himax_ts_data *ts = container_of(work, struct himax_ts_data,
initial_work.work);
int ret;
bool fw_load_status;
u32 fw_load_cnt;
const u32 fw_bin_header_sz = 1024;
const u32 fw_load_retry_limit = 3;
ts->ic_boot_done = false;
if (ts->hid_req_cfg.fw) {
ts->himax_fw = ts->hid_req_cfg.fw;
dev_info(ts->dev, "%s: get fw from hid_req_cfg\n", __func__);
} else {
dev_info(ts->dev, "%s: request file %s\n", __func__, ts->firmware_name);
ret = request_firmware(&ts->himax_fw, ts->firmware_name, ts->dev);
if (ret < 0) {
dev_err(ts->dev, "%s: request firmware failed, error code = %d\n",
__func__, ret);
return;
}
}
/* Parse the mapping table in 1k header */
fw_load_status = himax_mcu_bin_desc_get((unsigned char *)ts->himax_fw->data,
ts, fw_bin_header_sz);
if (!fw_load_status) {
dev_err(ts->dev, "%s: Failed to parse the mapping table!\n", __func__);
goto err_load_bin_descriptor;
}
for (fw_load_cnt = 0; fw_load_cnt < fw_load_retry_limit; fw_load_cnt++) {
if (himax_update_fw(ts)) {
dev_err(ts->dev, "%s: Update FW fail\n", __func__);
goto err_update_fw_failed;
}
dev_info(ts->dev, "%s: Update FW success\n", __func__);
/* write flag to sram to stop fw reload again. */
if (himax_disable_fw_reload(ts))
goto err_disable_fw_reload;
ret = himax_mcu_power_on_init(ts);
if (ret == -EAGAIN)
dev_err(ts->dev, "%s: initialize failed, reload FW again\n", __func__);
else if (ret < 0)
goto err_power_on_init;
else
break;
}
/* get hid descriptors */
if (!ts->fw_info_table.addr_hid_desc) {
dev_err(ts->dev, "%s: No HID descriptor! Wrong FW!\n", __func__);
goto err_wrong_firmware;
}
memcpy(&ts->hid_desc,
&ts->himax_fw->data[ts->fw_info_table.addr_hid_desc],
sizeof(struct himax_hid_desc));
ts->hid_desc.desc_length =
le16_to_cpu(ts->hid_desc.desc_length);
ts->hid_desc.bcd_version =
le16_to_cpu(ts->hid_desc.bcd_version);
ts->hid_desc.report_desc_length =
le16_to_cpu(ts->hid_desc.report_desc_length);
ts->hid_desc.max_input_length =
le16_to_cpu(ts->hid_desc.max_input_length);
ts->hid_desc.max_output_length =
le16_to_cpu(ts->hid_desc.max_output_length);
ts->hid_desc.max_fragment_length =
le16_to_cpu(ts->hid_desc.max_fragment_length);
ts->hid_desc.vendor_id =
le16_to_cpu(ts->hid_desc.vendor_id);
ts->hid_desc.product_id =
le16_to_cpu(ts->hid_desc.product_id);
ts->hid_desc.version_id =
le16_to_cpu(ts->hid_desc.version_id);
ts->hid_desc.flags =
le16_to_cpu(ts->hid_desc.flags);
if (himax_mcu_tp_info_check(ts))
goto err_tp_info_failed;
if (himax_mcu_read_FW_ver(ts))
goto err_read_fw_ver;
if (himax_hid_rd_init(ts)) {
dev_err(ts->dev, "%s: hid rd init fail\n", __func__);
goto err_hid_rd_init_failed;
}
if (ts->hid_req_cfg.fw) {
/* Set flag of HIDRAW FW update result */
ts->hid_req_cfg.handshake_get = HIMAX_FWUP_BL_READY;
/* Unlock HIDRAW ioctl for result checking */
mutex_unlock(&ts->hid_ioctl_lock);
}
usleep_range(1000000, 1000100);
himax_hid_register(ts);
if (!ts->hid_probed) {
goto err_hid_probe_failed;
} else {
if (himax_hid_report_data_init(ts)) {
dev_err(ts->dev, "%s: report data init fail\n", __func__);
goto err_report_data_init_failed;
}
}
/* Release FW if it is from request_firmware */
if (!ts->hid_req_cfg.fw)
release_firmware(ts->himax_fw);
ts->himax_fw = NULL;
ts->ic_boot_done = true;
himax_int_enable(ts, true);
return;
err_report_data_init_failed:
himax_hid_remove(ts);
ts->hid_probed = false;
err_hid_probe_failed:
err_hid_rd_init_failed:
err_read_fw_ver:
err_tp_info_failed:
err_wrong_firmware:
err_power_on_init:
err_disable_fw_reload:
err_update_fw_failed:
err_load_bin_descriptor:
if (!ts->hid_req_cfg.fw) {
release_firmware(ts->himax_fw);
} else {
ts->hid_req_cfg.handshake_get = HIMAX_FWUP_FLASH_PROG_ERROR;
mutex_unlock(&ts->hid_ioctl_lock);
}
ts->himax_fw = NULL;
}
/**
* himax_heatmap_data_init() - Initialize the heatmap data
* @ts: Himax touch screen data
*
* The function is used to initialize the heatmap data. It will allocate the
* memory for the heatmap data. The size of the heatmap data is equal to the
* rx_num * tx_num * 2 plus the size of the heatmap header and the size of
* the heatmap ID.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_heatmap_data_init(struct himax_ts_data *ts)
{
ts->heatmap_buf = devm_kzalloc(ts->dev, (ts->ic_data.rx_num * ts->ic_data.tx_num) * 2
+ HIMAX_HEAT_MAP_INFO_SZ + HIMAX_HID_ID_SZ, GFP_KERNEL);
if (!ts->heatmap_buf)
return -ENOMEM;
return 0;
}
/**
* himax_heatmap_data_deinit() - Deinitialize the heatmap data
* @ts: Himax touch screen data
*
* The function is used to deinitialize the heatmap data.
*
* Return: None
*/
static void himax_heatmap_data_deinit(struct himax_ts_data *ts)
{
devm_kfree(ts->dev, ts->heatmap_buf);
ts->heatmap_buf = NULL;
}
/**
* himax_hid_update() - Update the HID device
* @work: Work structure
*
* This function is used to update the HID device. When userspace tool switch
* on/off the input RD, the HID device should be re-registered to update the
* report descriptor. The heatmap size may change and need to release the old
* one first.
*
* Return: None
*/
static void himax_hid_update(struct work_struct *work)
{
struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work_hid_update.work);
himax_int_enable(ts, false);
himax_heatmap_data_deinit(ts);
if (!ts->hid_req_cfg.input_RD_de) {
himax_initial_work(&ts->initial_work.work);
} else {
if (!himax_hid_rd_init(ts)) {
dev_info(ts->dev, "%s: hid rd init success\n", __func__);
himax_hid_register(ts);
if (ts->hid_probed)
himax_hid_report_data_init(ts);
}
}
himax_int_enable(ts, true);
}
/**
* himax_ap_notify_fw_suspend() - Notify the FW of AP suspend status
* @ts: Himax touch screen data
* @suspend: Suspend status, true for suspend, false for resume
*
* This function is used to notify the FW of AP suspend status. It will write
* the suspend status to the DSRAM and read the status back to check if the
* status is written successfully. If IC is powered off when suspend, this
* function will only be used when resume.
*
* Return: None
*/
static void himax_ap_notify_fw_suspend(struct himax_ts_data *ts, bool suspend)
{
int ret;
u32 retry_cnt;
const u32 retry_limit = 10;
union himax_dword_data rdata, data;
if (suspend)
data.dword = cpu_to_le32(HIMAX_DSRAM_DATA_AP_NOTIFY_FW_SUSPEND);
else
data.dword = cpu_to_le32(HIMAX_DSRAM_DATA_AP_NOTIFY_FW_RESUME);
for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
ret = himax_mcu_register_write(ts, HIMAX_DSRAM_ADDR_AP_NOTIFY_FW_SUSPEND,
data.byte, 4);
if (ret) {
dev_err(ts->dev, "%s: write suspend status failed!\n", __func__);
return;
}
usleep_range(1000, 1100);
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_AP_NOTIFY_FW_SUSPEND,
rdata.byte, 4);
if (ret) {
dev_err(ts->dev, "%s: read suspend status failed!\n", __func__);
return;
}
if (rdata.dword == data.dword)
break;
}
}
/**
* himax_resume_proc() - Chip resume procedure of touch screen
* @ts: Himax touch screen data
*
* This function is used to resume the touch screen. It will call the
* himax_zf_reload_from_file() to reload the firmware. And call the
* himax_ap_notify_fw_suspend() to notify the FW of AP resume status.
*
* Return: None
*/
static void himax_resume_proc(struct himax_ts_data *ts)
{
int ret;
ret = himax_zf_reload_from_file(ts->firmware_name, ts);
if (ret) {
dev_err(ts->dev, "%s: update FW fail, code[%d]!!\n", __func__, ret);
return;
}
ts->resume_succeeded = true;
himax_ap_notify_fw_suspend(ts, false);
}
/**
* himax_chip_suspend() - Suspend the touch screen
* @ts: Himax touch screen data
*
* This function is used to suspend the touch screen. It will disable the
* interrupt and set the reset pin to activate state. Remove the HID at
* the end, to prevent stuck finger when resume.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_chip_suspend(struct himax_ts_data *ts)
{
himax_int_enable(ts, false);
gpiod_set_value(ts->pdata.gpiod_rst, 1);
himax_power_set(ts, false);
return 0;
}
/**
* himax_chip_resume() - Setup flags, I/O and resume
* @ts: Himax touch screen data
*
* This function is used to resume the touch screen. It will set the resume
* success flag to false, and disable reset pin. Then call the himax_resume_proc()
* to process detailed resume procedure.
* If the resume action is succeeded, it will call the himax_hid_probe() to restore
* the HID device and enable the interrupt.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_chip_resume(struct himax_ts_data *ts)
{
ts->resume_succeeded = false;
ts->excp_zero_event_count = 0;
ts->excp_reset_active = false;
if (himax_power_set(ts, true))
return -EIO;
gpiod_set_value(ts->pdata.gpiod_rst, 0);
himax_resume_proc(ts);
if (ts->resume_succeeded) {
himax_int_enable(ts, true);
} else {
dev_err(ts->dev, "%s: resume failed!\n", __func__);
return -ECANCELED;
}
return 0;
}
/**
* himax_suspend() - Suspend the touch screen
* @dev: Device structure
*
* Wrapper function for himax_chip_suspend() to be called by the PM or
* the DRM panel notifier.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_suspend(struct device *dev)
{
struct himax_ts_data *ts = dev_get_drvdata(dev);
if (!ts->initialized) {
dev_err(ts->dev, "%s: init not ready, skip!\n", __func__);
return -ECANCELED;
}
himax_chip_suspend(ts);
return 0;
}
/**
* himax_resume() - Resume the touch screen
* @dev: Device structure
*
* Wrapper function for himax_chip_resume() to be called by the PM or
* the DRM panel notifier.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_resume(struct device *dev)
{
int ret;
struct himax_ts_data *ts = dev_get_drvdata(dev);
if (!ts->initialized) {
if (himax_chip_init(ts))
return -ECANCELED;
}
ret = himax_chip_resume(ts);
if (ret < 0)
dev_err(ts->dev, "%s: resume failed!\n", __func__);
return ret;
}
/**
* himax_chip_init() - Initialize the Himax touch screen
* @ts: Himax touch screen data
*
* This function is used to initialize the Himax touch screen. It will
* initialize interrupt lock, register the interrupt, and disable the
* interrupt. If later part of initialization succeed, then interrupt will
* be enabled.
* And initialize varies flags, workqueue and delayed work for later use.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_chip_init(struct himax_ts_data *ts)
{
int ret;
hx83102j_chip_init_data(ts);
if (himax_ts_register_interrupt(ts)) {
dev_err(ts->dev, "%s: register interrupt failed\n", __func__);
return -EIO;
}
himax_int_enable(ts, false);
ts->zf_update_cfg_buffer = devm_kzalloc(ts->dev, ts->chip_max_dsram_size, GFP_KERNEL);
if (!ts->zf_update_cfg_buffer) {
ret = -ENOMEM;
goto err_update_cfg_buf_alloc_failed;
}
INIT_DELAYED_WORK(&ts->initial_work, himax_initial_work);
schedule_delayed_work(&ts->initial_work, msecs_to_jiffies(HIMAX_DELAY_BOOT_UPDATE_MS));
ts->himax_hidraw_wq =
create_singlethread_workqueue("himax_hidraw_wq");
if (!ts->himax_hidraw_wq) {
dev_err(ts->dev, "%s: allocate himax_hidraw_wq failed\n", __func__);
ret = -ENOMEM;
goto err_hidraw_wq_failed;
}
INIT_DELAYED_WORK(&ts->work_self_test, himax_self_test);
INIT_DELAYED_WORK(&ts->work_hid_update, himax_hid_update);
ts->usb_connected = false;
ts->initialized = true;
return 0;
err_hidraw_wq_failed:
cancel_delayed_work_sync(&ts->initial_work);
err_update_cfg_buf_alloc_failed:
return ret;
}
/**
* himax_chip_deinit() - Deinitialize the Himax touch screen
* @ts: Himax touch screen data
*
* This function is used to deinitialize the Himax touch screen.
*
* Return: None
*/
static void himax_chip_deinit(struct himax_ts_data *ts)
{
cancel_delayed_work_sync(&ts->work_self_test);
destroy_workqueue(ts->himax_hidraw_wq);
cancel_delayed_work_sync(&ts->initial_work);
}
#if defined(CONFIG_OF)
/**
* himax_parse_dt() - Parse the device tree
* @dt: Device node
* @pdata: Himax platform data
*
* This function is used to parse the device tree. If "himax,pid" is found,
* it will parse the PID value and set it to the platform data. The firmware
* name will set to himax_i2chid_$PID.bin if the PID is found, or
* himax_i2chid.bin if the PID is not found.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_parse_dt(struct device_node *dt, struct himax_platform_data *pdata)
{
int err;
size_t fw_name_len;
const char *fw_name;
struct himax_ts_data *ts;
if (!dt || !pdata)
return -EINVAL;
ts = container_of(pdata, struct himax_ts_data, pdata);
/* Set default firmware name, without PID */
strscpy(ts->firmware_name, HIMAX_BOOT_UPGRADE_FWNAME HIMAX_FW_EXT_NAME,
sizeof(HIMAX_BOOT_UPGRADE_FWNAME HIMAX_FW_EXT_NAME));
if (of_property_read_bool(dt, "vccd-supply")) {
pdata->vccd_supply = regulator_get(ts->dev, "vccd");
if (IS_ERR(pdata->vccd_supply)) {
dev_err(ts->dev, "%s: DT:failed to get vccd supply\n", __func__);
err = PTR_ERR(pdata->vccd_supply);
pdata->vccd_supply = NULL;
return err;
}
dev_info(ts->dev, "%s: DT:vccd-supply=%p\n", __func__, pdata->vccd_supply);
} else {
pdata->vccd_supply = NULL;
}
if (of_property_read_string(dt, "firmware-name", &fw_name)) {
dev_info(ts->dev, "%s: DT:firmware-name not found\n", __func__);
} else {
fw_name_len = strlen(fw_name) + 1;
strscpy(ts->firmware_name, fw_name, min(sizeof(ts->firmware_name), fw_name_len));
dev_info(ts->dev, "%s: firmware-name = %s\n", __func__, ts->firmware_name);
}
return 0;
}
#endif
/**
* __himax_initial_power_up() - Initial power up of the Himax touch screen
* @ts: Himax touch screen data
*
* This function is used to perform the initial power up sequence of the Himax
* touch screen for DRM panel notifier.
*
* Return: 0 on success, negative error code on failure
*/
static int __himax_initial_power_up(struct himax_ts_data *ts)
{
int ret;
ret = himax_platform_init(ts);
if (ret) {
dev_err(ts->dev, "%s: platform init failed\n", __func__);
return ret;
}
ret = hx83102j_chip_detect(ts);
if (ret) {
dev_err(ts->dev, "%s: IC detect failed\n", __func__);
return ret;
}
ret = himax_chip_init(ts);
if (ret) {
dev_err(ts->dev, "%s: chip init failed\n", __func__);
return ret;
}
ts->himax_pwr_wq = create_singlethread_workqueue("HMX_PWR_request");
if (!ts->himax_pwr_wq) {
dev_err(ts->dev, "%s: allocate himax_pwr_wq failed\n", __func__);
ret = -ENOMEM;
goto err_create_pwr_wq_failed;
}
INIT_DELAYED_WORK(&ts->work_pwr, himax_pwr_register);
himax_pwr_register(&ts->work_pwr.work);
ts->probe_finish = true;
return 0;
err_create_pwr_wq_failed:
himax_chip_deinit(ts);
return ret;
}
/**
* himax_panel_prepared() - Panel prepared callback
* @follower: DRM panel follower
*
* This function is called when the panel is prepared. It will call the
* __himax_initial_power_up() when the probe is not finished which means
* the first time driver start. Otherwise, it will call the himax_resume()
* to performed resume process.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_panel_prepared(struct drm_panel_follower *follower)
{
struct himax_platform_data *pdata =
container_of(follower, struct himax_platform_data, panel_follower);
struct himax_ts_data *ts = container_of(pdata, struct himax_ts_data, pdata);
if (!ts->probe_finish)
return __himax_initial_power_up(ts);
else
return himax_resume(ts->dev);
}
/**
* himax_panel_unpreparing() - Panel unpreparing callback
* @follower: DRM panel follower
*
* This function is called when the panel is unpreparing. It will call the
* himax_suspend() to perform the suspend process.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_panel_unpreparing(struct drm_panel_follower *follower)
{
struct himax_platform_data *pdata =
container_of(follower, struct himax_platform_data, panel_follower);
struct himax_ts_data *ts = container_of(pdata, struct himax_ts_data, pdata);
return himax_suspend(ts->dev);
}
/* Panel follower function table */
static const struct drm_panel_follower_funcs himax_panel_follower_funcs = {
.panel_prepared = himax_panel_prepared,
.panel_unpreparing = himax_panel_unpreparing,
};
/**
* himax_register_panel_follower() - Register the panel follower
* @ts: Himax touch screen data
*
* This function is used to register the panel follower. It will set the
* pdata.is_panel_follower to true and register the panel follower.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_register_panel_follower(struct himax_ts_data *ts)
{
struct device *dev = ts->dev;
ts->pdata.is_panel_follower = true;
ts->pdata.panel_follower.funcs = &himax_panel_follower_funcs;
if (device_can_wakeup(dev)) {
dev_warn(ts->dev, "Can't wakeup if following panel");
device_set_wakeup_capable(dev, false);
}
return drm_panel_add_follower(dev, &ts->pdata.panel_follower);
}
/**
* himax_initial_power_up() - Initial power up of the Himax touch screen
* @ts: Himax touch screen data
*
* This function checks if the device is a panel follower and calls
* himax_register_panel_follower() if it is. Otherwise, it calls
* __himax_initial_power_up().
*
* Return: 0 on success, negative error code on failure
*/
static int himax_initial_power_up(struct himax_ts_data *ts)
{
if (drm_is_panel_follower(ts->dev))
return himax_register_panel_follower(ts);
else
return __himax_initial_power_up(ts);
}
/**
* himax_switch_mode_inspection() - Switch the inspection mode
* @ts: Himax touch screen data
* @mode: Inspection mode
*
* This function is used to switch the inspection mode for self test.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_switch_mode_inspection(struct himax_ts_data *ts, int mode)
{
int ret;
union himax_dword_data data;
/* Stop previous Handshaking first */
data.dword = cpu_to_le32(HIMAX_DSRAM_DATA_HANDSHAKING_RELEASE);
ret = himax_mcu_register_write(ts, HIMAX_DSRAM_ADDR_RAWDATA, data.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: write handshaking release failed\n", __func__);
return ret;
}
/* Switch Mode */
switch (mode) {
case HIMAX_INSPECT_SORTING:
data.dword = cpu_to_le32(HIMAX_PWD_SORTING_START);
break;
case HIMAX_INSPECT_OPEN:
data.dword = cpu_to_le32(HIMAX_PWD_OPEN_START);
break;
case HIMAX_INSPECT_MICRO_OPEN:
data.dword = cpu_to_le32(HIMAX_PWD_OPEN_START);
break;
case HIMAX_INSPECT_SHORT:
data.dword = cpu_to_le32(HIMAX_PWD_SHORT_START);
break;
case HIMAX_INSPECT_RAWDATA:
data.dword = cpu_to_le32(HIMAX_PWD_RAWDATA_START);
break;
case HIMAX_INSPECT_ABS_NOISE:
data.dword = cpu_to_le32(HIMAX_PWD_NOISE_START);
break;
default:
dev_err(ts->dev, "%s: Wrong mode=%d\n", __func__, mode);
return -HIMAX_INSPECT_ESWITCHMODE;
}
/* Assign new sorting mode */
ret = himax_mcu_assign_sorting_mode(ts, data.byte);
if (ret < 0) {
dev_err(ts->dev, "%s: assign sorting mode failed\n", __func__);
return ret;
}
return 0;
}
/**
* himax_switch_data_type() - Switch the data type
* @ts: Himax touch screen data
* @type: Data type
*
* This function is used to switch the data type for self test.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_switch_data_type(struct himax_ts_data *ts, u32 type)
{
int ret;
u32 datatype;
dev_info(ts->dev, "%s: Expected type = %s\n", __func__, g_himax_inspection_mode[type]);
switch (type) {
case HIMAX_INSPECT_SORTING:
datatype = g_hx_data_type[HIMAX_DATA_SORTING];
break;
case HIMAX_INSPECT_OPEN:
datatype = g_hx_data_type[HIMAX_DATA_OPEN];
break;
case HIMAX_INSPECT_MICRO_OPEN:
datatype = g_hx_data_type[HIMAX_DATA_MICRO_OPEN];
break;
case HIMAX_INSPECT_SHORT:
datatype = g_hx_data_type[HIMAX_DATA_SHORT];
break;
case HIMAX_INSPECT_RAWDATA:
datatype = g_hx_data_type[HIMAX_DATA_RAWDATA];
break;
case HIMAX_INSPECT_ABS_NOISE:
datatype = g_hx_data_type[HIMAX_DATA_NOISE];
break;
case HIMAX_INSPECT_BACK_NORMAL:
datatype = g_hx_data_type[HIMAX_DATA_BACK_NORMAL];
break;
default:
dev_err(ts->dev, "%s: Wrong type=%d\n", __func__, type);
return -HIMAX_INSPECT_ESWITCHDATA;
}
ret = himax_mcu_diag_register_set(ts, datatype);
if (ret < 0)
dev_err(ts->dev, "%s: set data type failed\n", __func__);
return ret;
}
/**
* himax_bank_search_set() - Set the bank search
* @ts: Himax touch screen data
* @checktype: Inspection mode
*
* This function is used to set the bank search for self test. This register
* is combined with other function, bank search used LSB 8 bits. So need to
* read the register first, then set the bank search and write it back.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_bank_search_set(struct himax_ts_data *ts, u32 checktype)
{
int ret;
union himax_dword_data data;
ret = himax_mcu_register_read(ts, HIMAX_DSRAM_ADDR_BANK_SEARCH, data.byte, 4);
if (ret < 0) {
dev_err(ts->dev, "%s: read bank search failed\n", __func__);
return ret;
}
switch (checktype) {
case HIMAX_INSPECT_RAWDATA:
data.byte[0] = HIMAX_BANK_SEARCH_RAWDATA;
break;
case HIMAX_INSPECT_ABS_NOISE:
data.byte[0] = HIMAX_BANK_SEARCH_NOISE;
break;
default:
data.byte[0] = HIMAX_BANK_SEARCH_OPENSHORT;
}
ret = himax_mcu_register_write(ts, HIMAX_DSRAM_ADDR_BANK_SEARCH, data.byte, 4);
if (ret < 0)
dev_err(ts->dev, "%s: write bank search failed\n", __func__);
return ret;
}
/**
* himax_set_N_frame() - Set the N frame to skip for self test
* @ts: Himax touch screen data
* @n_frame: N frame value
* @checktype: Inspection mode
*
* This function is used to set the N frame to skip for self test. It will
* set the bank search first, then write the N frame to the register. The
* N frame is used to skip the frame for self test when collecting the data
* after switch the mode.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_set_N_frame(struct himax_ts_data *ts, u32 n_frame, u32 checktype)
{
int ret;
union himax_dword_data data;
ret = himax_bank_search_set(ts, checktype);
if (ret < 0) {
dev_err(ts->dev, "%s: set bank search failed\n", __func__);
return ret;
}
data.dword = cpu_to_le32(n_frame);
ret = himax_mcu_register_write(ts, HIMAX_DSRAM_ADDR_SET_NFRAME, data.byte, 4);
if (ret < 0)
dev_err(ts->dev, "%s: write N frame failed\n", __func__);
return ret;
}
/**
* himax_check_mode() - Check the inspection mode
* @ts: Himax touch screen data
* @checktype: Inspection mode
*
* This function is used to check the inspection mode for self test. It will
* read the sorting mode register and compare it with the expected value.
* If the mode is not matched, it will return
* HIMAX_INSPECT_CHANGE_MODE_REQUIRED, otherwise return 0 indicate the mode
* is matched.
*
* Return: 0 on mode matched, HIMAX_INSPECT_CHANGE_MODE_REQUIRED when mode
* change required, negative error code on failure
*/
static int himax_check_mode(struct himax_ts_data *ts, u32 checktype)
{
int ret;
const u16 passwd_mask = GENMASK(15, 0);
u16 wait_pwd;
union himax_dword_data data;
switch (checktype) {
case HIMAX_INSPECT_SORTING:
wait_pwd = cpu_to_le16(HIMAX_PWD_SORTING_END);
break;
case HIMAX_INSPECT_OPEN:
wait_pwd = cpu_to_le16(HIMAX_PWD_OPEN_END);
break;
case HIMAX_INSPECT_MICRO_OPEN:
wait_pwd = cpu_to_le16(HIMAX_PWD_OPEN_END);
break;
case HIMAX_INSPECT_SHORT:
wait_pwd = cpu_to_le16(HIMAX_PWD_SHORT_END);
break;
case HIMAX_INSPECT_RAWDATA:
wait_pwd = cpu_to_le16(HIMAX_PWD_RAWDATA_END);
break;
case HIMAX_INSPECT_ABS_NOISE:
wait_pwd = cpu_to_le16(HIMAX_PWD_NOISE_END);
break;
default:
dev_err(ts->dev, "%s: Wrong type = %u\n", __func__, checktype);
return -HIMAX_INSPECT_ESWITCHMODE;
}
ret = himax_mcu_check_sorting_mode(ts, data.byte);
if (ret)
return ret;
if ((le32_to_cpu(data.dword) & passwd_mask) == wait_pwd) {
dev_info(ts->dev, "%s: Current sorting mode: %s\n", __func__,
g_himax_inspection_mode[checktype]);
return 0;
} else {
return HIMAX_INSPECT_CHANGE_MODE_REQUIRED;
}
}
/**
* himax_wait_sorting_mode() - Wait the inspection mode
* @ts: Himax touch screen data
* @checktype: Inspection mode
*
* This function is used to wait the inspection mode for self test. It will
* read current sorting mode for most 10 times each interval 50ms to check
* if the target mode is reached.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_wait_sorting_mode(struct himax_ts_data *ts, u32 checktype)
{
const u16 passwd_mask = GENMASK(15, 0);
u16 wait_pwd;
u32 retry_cnt;
const u32 retry_limit = 10;
union himax_dword_data data;
switch (checktype) {
case HIMAX_INSPECT_SORTING:
wait_pwd = cpu_to_le16(HIMAX_PWD_SORTING_END);
break;
case HIMAX_INSPECT_OPEN:
wait_pwd = cpu_to_le16(HIMAX_PWD_OPEN_END);
break;
case HIMAX_INSPECT_MICRO_OPEN:
wait_pwd = cpu_to_le16(HIMAX_PWD_OPEN_END);
break;
case HIMAX_INSPECT_SHORT:
wait_pwd = cpu_to_le16(HIMAX_PWD_SHORT_END);
break;
case HIMAX_INSPECT_RAWDATA:
wait_pwd = cpu_to_le16(HIMAX_PWD_RAWDATA_END);
break;
case HIMAX_INSPECT_ABS_NOISE:
wait_pwd = cpu_to_le16(HIMAX_PWD_NOISE_END);
break;
default:
dev_warn(ts->dev, "%s: No target type = %u\n", __func__, checktype);
return -HIMAX_INSPECT_ESWITCHMODE;
}
dev_info(ts->dev, "%s: NowType: %s, Expected = 0x%04X\n", __func__,
g_himax_inspection_mode[checktype], wait_pwd);
for (retry_cnt = 0; retry_cnt < retry_limit; retry_cnt++) {
if (himax_mcu_check_sorting_mode(ts, data.byte))
return -HIMAX_INSPECT_ESWITCHMODE;
if ((le32_to_cpu(data.dword) & passwd_mask) == wait_pwd)
return 0;
usleep_range(50000, 50100);
}
return -HIMAX_INSPECT_ESWITCHMODE;
}
/**
* himax_self_test() - Self test work function
* @work: Work structure
*
* This function is used to perform the self test. It will check current
* mode, switch mode if required, wait the sorting mode, switch data type,
* set N frame, and get the data. The switch mode step may take more time
* by circustance, so it will retry 3 times if it failed. Wheather the
* self test is finished or failed, it will unlock the hid_ioctl_lock.
*
* Return: None
*/
static void himax_self_test(struct work_struct *work)
{
int ret, n_frame;
int switch_mode_cnt = 0;
const int switch_mode_retry_limit = 3;
struct himax_ts_data *ts =
container_of(work, struct himax_ts_data, work_self_test.work);
u32 checktype = ts->hid_req_cfg.self_test_type;
ret = himax_check_mode(ts, checktype);
if (ret < 0) {
ts->hid_req_cfg.handshake_get = HIMAX_HID_SELF_TEST_ERROR;
goto END;
}
if (ret == HIMAX_INSPECT_CHANGE_MODE_REQUIRED) {
dev_info(ts->dev, "%s: Need Change Mode, target = %s\n", __func__,
g_himax_inspection_mode[checktype]);
SWITCH_MODE:
ret = hx83102j_sense_off(ts, true);
if (ret) {
ts->hid_req_cfg.handshake_get = HIMAX_HID_SELF_TEST_ERROR;
goto END;
}
ret = himax_switch_mode_inspection(ts, checktype);
if (ret) {
dev_err(ts->dev, "%s: switch mode failed!\n", __func__);
ts->hid_req_cfg.handshake_get = HIMAX_HID_SELF_TEST_ERROR;
goto END;
}
if (checktype == HIMAX_INSPECT_ABS_NOISE)
n_frame = cpu_to_le32(HIMAX_NFRAME_NOISE);
else
n_frame = cpu_to_le32(HIMAX_NFRAME_OTHER);
ret = himax_set_N_frame(ts, n_frame, checktype);
if (ret) {
dev_err(ts->dev, "%s: set N frame failed!\n", __func__);
ts->hid_req_cfg.handshake_get = HIMAX_HID_SELF_TEST_ERROR;
goto END;
}
ret = hx83102j_sense_on(ts, true);
if (ret) {
dev_err(ts->dev, "%s: sense on failed!\n", __func__);
ts->hid_req_cfg.handshake_get = HIMAX_HID_SELF_TEST_ERROR;
goto END;
}
}
ret = himax_wait_sorting_mode(ts, checktype);
if (ret) {
if (ret == -HIMAX_INSPECT_ESWITCHMODE &&
switch_mode_cnt < switch_mode_retry_limit) {
switch_mode_cnt++;
himax_mcu_ic_reset(ts, false);
goto SWITCH_MODE;
}
dev_err(ts->dev, "%s: Wait sorting mode timeout\n", __func__);
ts->hid_req_cfg.handshake_get = HIMAX_HID_SELF_TEST_ERROR;
goto END;
}
ret = himax_switch_data_type(ts, checktype);
if (ret) {
dev_err(ts->dev, "%s: switch data type failed\n", __func__);
ts->hid_req_cfg.handshake_get = HIMAX_HID_SELF_TEST_ERROR;
goto END;
}
ts->hid_req_cfg.handshake_get = HIMAX_HID_SELF_TEST_FINISH;
END:
mutex_unlock(&ts->hid_ioctl_lock);
}
/**
* himax_get_data() - Get the self test result data
* @ts: Himax touch screen data
* @data: Data buffer
*
* This function is used to get the self test result data.
*
* Return: HIMAX_INSPECT_OK on success, -HIMAX_INSPECT_EGETRAW on failure
*/
static int himax_get_data(struct himax_ts_data *ts, u8 *data)
{
int get_raw_rlst;
get_raw_rlst = himax_mcu_get_DSRAM_data(ts, data);
if (!get_raw_rlst)
return HIMAX_INSPECT_OK;
else
return -HIMAX_INSPECT_EGETRAW;
}
/**
* himax_platform_deinit() - Deinitialize the platform related settings
* @ts: Pointer to the himax_ts_data structure
*
* This function deinitializes the platform related settings, frees the
* xfer_buf.
*
* Return: None
*/
static void himax_platform_deinit(struct himax_ts_data *ts)
{
struct himax_platform_data *pdata = &ts->pdata;
if (ts->xfer_buf_sz) {
kfree(ts->xfer_buf);
ts->xfer_buf = NULL;
ts->xfer_buf_sz = 0;
}
himax_power_deconfig(pdata);
}
/**
* himax_platform_init - Initialize the platform related settings
* @ts: Pointer to the himax_ts_data structure
*
* This function initializes the platform related settings.
* The xfer_buf is used for interrupt data receive. gpio reset pin is set to
* active and then deactivate to reset the IC.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_platform_init(struct himax_ts_data *ts)
{
int ret;
struct himax_platform_data *pdata = &ts->pdata;
ts->xfer_buf_sz = 0;
ts->xfer_buf = kzalloc(HIMAX_HX83102J_FULL_STACK_SZ, GFP_KERNEL);
if (!ts->xfer_buf)
return -ENOMEM;
ts->xfer_buf_sz = HIMAX_HX83102J_FULL_STACK_SZ;
gpiod_set_value(pdata->gpiod_rst, 1);
ret = himax_power_set(ts, true);
if (ret) {
dev_err(ts->dev, "%s: gpio power config failed\n", __func__);
return ret;
}
usleep_range(2000, 2100);
gpiod_set_value(pdata->gpiod_rst, 0);
return 0;
}
/**
* himax_spi_drv_probe - Probe function for the SPI driver
* @spi: Pointer to the spi_device structure
*
* This function is called when the SPI driver is probed. It initializes the
* himax_ts_data structure and assign the settings from spi device to
* himax_ts_data. The buffer for SPI transfer is allocate here. The SPI
* transfer settings also setup before any communication starts.
*
* Return: 0 on success, negative error code on failure
*/
static int himax_spi_drv_probe(struct spi_device *spi)
{
int ret;
struct himax_ts_data *ts;
static struct himax_platform_data *pdata;
dev_info(&spi->dev, "%s: Himax SPI driver probe\n", __func__);
ts = devm_kzalloc(&spi->dev, sizeof(struct himax_ts_data), GFP_KERNEL);
if (!ts)
return -ENOMEM;
if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) {
dev_err(ts->dev, "%s: Full duplex not supported by host\n", __func__);
return -EIO;
}
pdata = &ts->pdata;
ts->dev = &spi->dev;
if (!spi->irq) {
dev_err(ts->dev, "%s: no IRQ?\n", __func__);
return -EINVAL;
}
ts->himax_irq = spi->irq;
pdata->gpiod_rst = devm_gpiod_get(ts->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(pdata->gpiod_rst)) {
dev_err(ts->dev, "%s: gpio-rst value is not valid\n", __func__);
return -EIO;
}
#if defined(CONFIG_OF)
if (himax_parse_dt(spi->dev.of_node, pdata) < 0) {
dev_err(ts->dev, "%s: parse OF data failed!\n", __func__);
ts->dev = NULL;
return -ENODEV;
}
#endif
spi->bits_per_word = 8;
spi->mode = SPI_MODE_3;
spi->cs_setup.value = HIMAX_SPI_CS_SETUP_TIME;
ts->spi = spi;
/*
* The max_transfer_size is used to allocate the buffer for SPI transfer.
* The size should be given by the SPI master driver, but if not available
* then use the HIMAX_MAX_TP_EV_STACK_SZ as default. Which is the least size for
* each TP event data.
*/
if (spi->master->max_transfer_size)
ts->spi_xfer_max_sz = spi->master->max_transfer_size(spi);
else
ts->spi_xfer_max_sz = HIMAX_MAX_TP_EV_STACK_SZ;
ts->spi_xfer_max_sz = min(ts->spi_xfer_max_sz, HIMAX_BUS_RW_MAX_LEN);
/* SPI full-duplex rx_buf and tx_buf should be equal */
ts->xfer_rx_data = devm_kzalloc(ts->dev, ts->spi_xfer_max_sz, GFP_KERNEL);
if (!ts->xfer_rx_data)
return -ENOMEM;
ts->xfer_tx_data = devm_kzalloc(ts->dev, ts->spi_xfer_max_sz, GFP_KERNEL);
if (!ts->xfer_tx_data)
return -ENOMEM;
spin_lock_init(&ts->irq_lock);
mutex_init(&ts->rw_lock);
mutex_init(&ts->reg_lock);
mutex_init(&ts->hid_ioctl_lock);
mutex_init(&ts->zf_update_lock);
dev_set_drvdata(&spi->dev, ts);
spi_set_drvdata(spi, ts);
ts->probe_finish = false;
ts->initialized = false;
ts->ic_boot_done = false;
ret = himax_initial_power_up(ts);
if (ret) {
dev_err(ts->dev, "%s: initial power up failed\n", __func__);
return -ENODEV;
}
return ret;
}
/**
* himax_spi_drv_remove - Remove function for the SPI driver
* @spi: Pointer to the spi_device structure
*
* This function is called when the SPI driver is removed. It deinitializes the
* himax_ts_data structure and free the resources allocated for the SPI
* communication.
*/
static void himax_spi_drv_remove(struct spi_device *spi)
{
struct himax_ts_data *ts = spi_get_drvdata(spi);
if (ts->probe_finish) {
if (ts->ic_boot_done) {
himax_int_enable(ts, false);
if (ts->hid_probed)
himax_hid_remove(ts);
}
power_supply_unreg_notifier(&ts->power_notif);
cancel_delayed_work_sync(&ts->work_pwr);
destroy_workqueue(ts->himax_pwr_wq);
himax_chip_deinit(ts);
himax_platform_deinit(ts);
}
}
/**
* himax_shutdown - Shutdown the touch screen
* @spi: Himax touch screen spi device
*
* This function is used to shutdown the touch screen. It will disable the
* interrupt, set the reset pin to activate state. Then remove the hid device.
*
* Return: None
*/
static void himax_shutdown(struct spi_device *spi)
{
struct himax_ts_data *ts = spi_get_drvdata(spi);
if (!ts->initialized) {
dev_err(ts->dev, "%s: init not ready, skip!\n", __func__);
return;
}
himax_int_enable(ts, false);
himax_hid_remove(ts);
power_supply_unreg_notifier(&ts->power_notif);
cancel_delayed_work_sync(&ts->work_pwr);
destroy_workqueue(ts->himax_pwr_wq);
gpiod_set_value(ts->pdata.gpiod_rst, 1);
himax_chip_deinit(ts);
himax_platform_deinit(ts);
}
#if defined(CONFIG_OF)
static const struct of_device_id himax_table[] = {
{ .compatible = "himax,hx83102j" },
{},
};
MODULE_DEVICE_TABLE(of, himax_table);
#endif
static struct spi_driver himax_hid_over_spi_driver = {
.driver = {
.name = "hx83102j",
.owner = THIS_MODULE,
#if defined(CONFIG_OF)
.of_match_table = of_match_ptr(himax_table),
#endif
},
.probe = himax_spi_drv_probe,
.remove = himax_spi_drv_remove,
.shutdown = himax_shutdown,
};
static int __init himax_ic_init(void)
{
return spi_register_driver(&himax_hid_over_spi_driver);
}
static void __exit himax_ic_exit(void)
{
spi_unregister_driver(&himax_hid_over_spi_driver);
}
module_init(himax_ic_init);
module_exit(himax_ic_exit);
MODULE_VERSION("1.3.4");
MODULE_DESCRIPTION("Himax HX83102J SPI driver for HID");
MODULE_AUTHOR("Himax, Inc.");
MODULE_LICENSE("GPL");