diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e93b8b..ea5be29 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ set(IMSENSORS_SRCS "src/imu/imu_lsm6dsl.c" "src/magnetic/magnetic_bmm150.c" "src/ppg/ppg_pah8001.c" + "src/touch/tp_it7259.c" ) set(IMSENSORS_INCS @@ -22,6 +23,7 @@ set(IMSENSORS_INCS "include/imsensors/imu" "include/imsensors/magnetic" "include/imsensors/ppg" + "include/imsensors/touch" ) set(IMSENSORS_INTF_INCS diff --git a/include/imsensors/touch/tp_it7259.h b/include/imsensors/touch/tp_it7259.h new file mode 100644 index 0000000..ff5b664 --- /dev/null +++ b/include/imsensors/touch/tp_it7259.h @@ -0,0 +1,46 @@ +#ifndef TP_IT7259_H +#define TP_IT7259_H + +#include "imsensors/common/sensors_common.h" + +typedef enum { + IT7259_PRES_NONE = 0U, + IT7259_PRES_HOVER = 1U, + IT7259_PRES_LIGHT = 2U, + IT7259_PRES_NORMAL = 4U, + IT7259_PRES_HIGH = 8U, + IT7259_PRES_HEAVY = 0x0FU, +} ims_it7259_pres_t; + +typedef struct { + ims_i2c_xfer_t i2c_xfer; + ims_delay_t delay; +} ims_it7259_cb_t; + +typedef struct { + uint32_t rom_version; + uint32_t fw_version; + uint8_t vendor_fw_version; +} ims_it7259_sensor_fw_info_t; + +typedef struct { + uint8_t id; + uint16_t pos_x; + uint16_t pos_y; + ims_it7259_pres_t pres; +} ims_it7259_point_t; + +typedef struct { + ims_it7259_cb_t cb; + void *pdev; + + ims_it7259_sensor_fw_info_t fw_info; + + uint16_t resolution_x; + uint16_t resolution_y; +} ims_it7259_t; + +ims_ret_t ims_it7259_init(ims_it7259_t *tp); +ims_ret_t ims_it7259_read_points(ims_it7259_t *tp, uint8_t *num_tp, ims_it7259_point_t *points); + +#endif // TP_IT7259_H diff --git a/src/touch/tp_it7259.c b/src/touch/tp_it7259.c new file mode 100644 index 0000000..208b586 --- /dev/null +++ b/src/touch/tp_it7259.c @@ -0,0 +1,300 @@ +#include "tp_it7259.h" + +#define ITE_BUF_LEN (18) /* This assumes the maximum payload length does not exceed this value */ + +#define ITE_QUERY_TIMEOUT (10) /* The device sometimes gives NAK for query command */ + +#define ITE_BUF_Pos (5U) +#define ITE_BUF_Msk (7U << ITE_BUF_Pos) + +#define ITE_QUERY_STAT_Pos (0U) +#define ITE_QUERY_STAT_Msk (3U << ITE_QUERY_STAT_Pos) + +#define ITE_CMD_DEVICE_NAME (0x00U) +#define ITE_CMD_DEVICE_NAME_LEN (0x0AU) +#define ITE_CMD_SENSOR_INFO (0x01U) +#define ITE_SUBCMD_FW_INFO (0x00U) +#define ITE_SUBCMD_FW_INFO_LEN (0x0AU) +#define ITE_SUBCMD_2D_RES (0x02U) +#define ITE_SUBCMD_2D_RES_LEN (0x0CU) + +#define ITE_POINT_INFO_LEN (0x0EU) + +typedef enum { + ITE_BUF_CMD = 1U, + ITE_BUF_QUERY = 4U, + ITE_BUF_CMD_RESP = 5U, + ITE_BUF_POINT = 7U, +} ite_buf_type_t; + +typedef enum { + ITE_STAT_DONE = 0U, + ITE_STAT_BUSY = 1U, + ITE_STAT_ERROR = 2U, + ITE_STAT_RESERVED = 3U, +} ite_query_stat_t; + +typedef struct __packed { + uint8_t cmd; + uint8_t sub; + uint8_t params[ITE_BUF_LEN - 2]; + uint8_t sub_len; + uint8_t params_len; +} ite_cmd_t; + +static const uint8_t ims_it7259_dev_name[] = {'I', 'T', 'E', '7', '2', '5', '9'}; + +static ims_ret_t ims_it7259_validate_device_name(ims_it7259_t *tp); +static ims_ret_t ims_it7259_sensor_fw_info(ims_it7259_t *tp, ims_it7259_sensor_fw_info_t *info); +static ims_ret_t ims_it7259_sensor_resoluion(ims_it7259_t *tp, uint16_t *res_x, uint16_t *res_y); +static ims_ret_t ims_it7259_buf_read(ims_it7259_t *tp, ite_buf_type_t type, uint8_t *data, uint8_t len); +static ims_ret_t ims_it7259_buf_write(ims_it7259_t *tp, ite_buf_type_t type, uint8_t *data, uint8_t len); +static ims_ret_t ims_it7259_query(ims_it7259_t *tp, uint8_t *query_result); +static ims_ret_t ims_it7259_command_send(ims_it7259_t *tp, ite_cmd_t *cmd); +static ims_ret_t ims_it7259_command_recv(ims_it7259_t *tp, uint8_t *data, uint8_t len); +static ims_ret_t ims_it7259_command_wait(ims_it7259_t *tp); + +ims_ret_t ims_it7259_init(ims_it7259_t *tp) { + ims_ret_t ret = ims_it7259_validate_device_name(tp); + if (ret != IMS_SUCCESS) { + return ret; + } + + ret = ims_it7259_sensor_fw_info(tp, &tp->fw_info); + if (ret != IMS_SUCCESS) { + return ret; + } + + ret = ims_it7259_sensor_resoluion(tp, &tp->resolution_x, &tp->resolution_y); + if (ret != IMS_SUCCESS) { + return ret; + } + + return IMS_SUCCESS; +} + +ims_ret_t ims_it7259_read_points(ims_it7259_t *tp, uint8_t *num_tp, ims_it7259_point_t *points) { + uint8_t rx_buf[ITE_POINT_INFO_LEN]; + + uint8_t query; + + ims_ret_t ret = ims_it7259_query(tp, &query); + if (ret != IMS_SUCCESS) { + return ret; + } + + if ((query >> 6U) == 0U) { + *num_tp = 0U; + + return IMS_SUCCESS; + } + + /* Two conditions on query [7:6]: 1xb: New touch, 01b: Still touched */ + + ret = ims_it7259_buf_read(tp, ITE_BUF_POINT, rx_buf, ITE_POINT_INFO_LEN); + if (ret != IMS_SUCCESS) { + return ret; + } + + /* Check whether this is a Point Data report */ + if ((rx_buf[0] & 0xF0) != 0x00) { + return IMS_FAIL; + } + + uint8_t array_num = *num_tp; + uint8_t current_id = 0; + uint8_t i; + for (i = 0; i < 3; i++) { + if (rx_buf[0] & (1U << i)) { + points[current_id].id = i; + points[current_id].pos_x = rx_buf[i + 2] | (rx_buf[i + 3] & 0x0FU) << 8U; + points[current_id].pos_y = rx_buf[i + 4] | (rx_buf[i + 3] & 0xF0U) << 4U; + points[current_id].pres = rx_buf[i + 5] & 0x0FU; + + current_id++; + /* Do not exceed buffer length */ + if (array_num == current_id) { + break; + } + } + } + + *num_tp = current_id; + + return IMS_SUCCESS; +} + +static ims_ret_t ims_it7259_validate_device_name(ims_it7259_t *tp) { + uint8_t name[ITE_CMD_DEVICE_NAME_LEN]; + + ite_cmd_t cmd = { + .cmd = ITE_CMD_DEVICE_NAME, + .sub_len = 0, + .params_len = 0, + }; + + ims_ret_t ret = ims_it7259_command_send(tp, &cmd); + if (ret != IMS_SUCCESS) { + return ret; + } + + ret = ims_it7259_command_wait(tp); + if (ret != IMS_SUCCESS) { + return ret; + } + + ret = ims_it7259_command_recv(tp, name, ITE_CMD_DEVICE_NAME_LEN); + if (ret != IMS_SUCCESS) { + return ret; + } + + /* Compare string revision. */ + for (uint8_t i = 0; i < sizeof(ims_it7259_dev_name); i++) { + /* The first byte is length */ + if (name[i + 1] != ims_it7259_dev_name[i]) { + return IMS_FAIL; + } + } + + return IMS_SUCCESS; +} + +static ims_ret_t ims_it7259_sensor_fw_info(ims_it7259_t *tp, ims_it7259_sensor_fw_info_t *info) { + uint8_t rx_buf[ITE_SUBCMD_FW_INFO_LEN]; + + ite_cmd_t cmd = { + .cmd = ITE_CMD_SENSOR_INFO, + .sub = ITE_SUBCMD_FW_INFO, + .sub_len = 1, + .params_len = 0, + }; + + ims_ret_t ret = ims_it7259_command_send(tp, &cmd); + if (ret != IMS_SUCCESS) { + return ret; + } + + ret = ims_it7259_command_wait(tp); + if (ret != IMS_SUCCESS) { + return ret; + } + + ret = ims_it7259_command_recv(tp, rx_buf, ITE_SUBCMD_FW_INFO_LEN); + if (ret != IMS_SUCCESS) { + return ret; + } + + info->rom_version = (rx_buf[1] << 24) | (rx_buf[2] << 16) | (rx_buf[3] << 8) | rx_buf[4]; + info->fw_version = (rx_buf[5] << 24) | (rx_buf[6] << 16) | (rx_buf[7] << 8) | rx_buf[8]; + info->vendor_fw_version = rx_buf[9]; + + return ret; +} + +static ims_ret_t ims_it7259_sensor_resoluion(ims_it7259_t *tp, uint16_t *res_x, uint16_t *res_y) { + uint8_t rx_buf[ITE_SUBCMD_2D_RES_LEN]; + + ite_cmd_t cmd = { + .cmd = ITE_CMD_SENSOR_INFO, + .sub = ITE_SUBCMD_2D_RES, + .sub_len = 1, + .params_len = 0, + }; + + ims_ret_t ret = ims_it7259_command_send(tp, &cmd); + if (ret != IMS_SUCCESS) { + return ret; + } + + ret = ims_it7259_command_wait(tp); + if (ret != IMS_SUCCESS) { + return ret; + } + + ret = ims_it7259_command_recv(tp, rx_buf, ITE_SUBCMD_2D_RES_LEN); + if (ret != IMS_SUCCESS) { + return ret; + } + + *res_x = ((rx_buf[3] << 16U) | rx_buf[2]) + 1; + *res_y = ((rx_buf[5] << 16U) | rx_buf[4]) + 1; + + return IMS_SUCCESS; +} + +static ims_ret_t ims_it7259_buf_read(ims_it7259_t *tp, ite_buf_type_t type, uint8_t *data, uint8_t len) { + uint8_t tx_buf[] = {(type << ITE_BUF_Pos) & ITE_BUF_Msk}; + + ims_i2c_xfer_desc_t xfer_desc = { + .tx_data = tx_buf, + .tx_size = 1U, + .rx_data = data, + .rx_size = len, + }; + + ims_ret_t ret = tp->cb.i2c_xfer(tp->pdev, &xfer_desc); + if (ret != IMS_SUCCESS) { + return ret; + } + + return IMS_SUCCESS; +} + +static ims_ret_t ims_it7259_buf_write(ims_it7259_t *tp, ite_buf_type_t type, uint8_t *data, uint8_t len) { + uint8_t tx_buf[ITE_BUF_LEN + 1] = {(type << ITE_BUF_Pos) & ITE_BUF_Msk}; + + memcpy(&tx_buf[1], data, len); /* ! WATCH BOUNDARY ! */ + + ims_i2c_xfer_desc_t xfer_desc = { + .tx_data = tx_buf, + .tx_size = len + 1, + .rx_data = NULL, + .rx_size = 0, + }; + + ims_ret_t ret = tp->cb.i2c_xfer(tp->pdev, &xfer_desc); + if (ret != IMS_SUCCESS) { + return ret; + } + + return IMS_SUCCESS; +} + +static ims_ret_t ims_it7259_query(ims_it7259_t *tp, uint8_t *query_result) { + return ims_it7259_buf_read(tp, ITE_BUF_QUERY, query_result, 0x01); +} + +static ims_ret_t ims_it7259_command_send(ims_it7259_t *tp, ite_cmd_t *cmd) { + return ims_it7259_buf_write(tp, ITE_BUF_CMD, (uint8_t *)cmd, (cmd->sub_len + cmd->params_len + 1)); +} + +static ims_ret_t ims_it7259_command_recv(ims_it7259_t *tp, uint8_t *data, uint8_t len) { + return ims_it7259_buf_read(tp, ITE_BUF_CMD_RESP, data, len); +} + +static ims_ret_t ims_it7259_command_wait(ims_it7259_t *tp) { + uint8_t query; + while (true) { + ims_ret_t ret = ims_it7259_query(tp, &query); + if (ret != IMS_SUCCESS) { + tp->cb.delay(tp->pdev, ITE_QUERY_TIMEOUT); + + ret = ims_it7259_query(tp, &query); + if (ret != IMS_SUCCESS) { + return ret; + } + } + + uint8_t stat = (query & ITE_QUERY_STAT_Msk) >> ITE_QUERY_STAT_Pos; + + if (stat == ITE_STAT_ERROR) { + return IMS_FAIL; + } + + if (stat == ITE_STAT_DONE) { + break; + } + } + + return IMS_SUCCESS; +} \ No newline at end of file