162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ILITEK Touch IC driver for 23XX, 25XX and Lego series 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011 ILI Technology Corporation. 662306a36Sopenharmony_ci * Copyright (C) 2020 Luca Hsu <luca_hsu@ilitek.com> 762306a36Sopenharmony_ci * Copyright (C) 2021 Joe Hung <joe_hung@ilitek.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/input.h> 1362306a36Sopenharmony_ci#include <linux/input/mt.h> 1462306a36Sopenharmony_ci#include <linux/i2c.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/gpio.h> 1962306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 2062306a36Sopenharmony_ci#include <linux/errno.h> 2162306a36Sopenharmony_ci#include <linux/acpi.h> 2262306a36Sopenharmony_ci#include <linux/input/touchscreen.h> 2362306a36Sopenharmony_ci#include <asm/unaligned.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define ILITEK_TS_NAME "ilitek_ts" 2762306a36Sopenharmony_ci#define BL_V1_8 0x108 2862306a36Sopenharmony_ci#define BL_V1_7 0x107 2962306a36Sopenharmony_ci#define BL_V1_6 0x106 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define ILITEK_TP_CMD_GET_TP_RES 0x20 3262306a36Sopenharmony_ci#define ILITEK_TP_CMD_GET_SCRN_RES 0x21 3362306a36Sopenharmony_ci#define ILITEK_TP_CMD_SET_IC_SLEEP 0x30 3462306a36Sopenharmony_ci#define ILITEK_TP_CMD_SET_IC_WAKE 0x31 3562306a36Sopenharmony_ci#define ILITEK_TP_CMD_GET_FW_VER 0x40 3662306a36Sopenharmony_ci#define ILITEK_TP_CMD_GET_PRL_VER 0x42 3762306a36Sopenharmony_ci#define ILITEK_TP_CMD_GET_MCU_VER 0x61 3862306a36Sopenharmony_ci#define ILITEK_TP_CMD_GET_IC_MODE 0xC0 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define REPORT_COUNT_ADDRESS 61 4162306a36Sopenharmony_ci#define ILITEK_SUPPORT_MAX_POINT 40 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct ilitek_protocol_info { 4462306a36Sopenharmony_ci u16 ver; 4562306a36Sopenharmony_ci u8 ver_major; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct ilitek_ts_data { 4962306a36Sopenharmony_ci struct i2c_client *client; 5062306a36Sopenharmony_ci struct gpio_desc *reset_gpio; 5162306a36Sopenharmony_ci struct input_dev *input_dev; 5262306a36Sopenharmony_ci struct touchscreen_properties prop; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci const struct ilitek_protocol_map *ptl_cb_func; 5562306a36Sopenharmony_ci struct ilitek_protocol_info ptl; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci char product_id[30]; 5862306a36Sopenharmony_ci u16 mcu_ver; 5962306a36Sopenharmony_ci u8 ic_mode; 6062306a36Sopenharmony_ci u8 firmware_ver[8]; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci s32 reset_time; 6362306a36Sopenharmony_ci s32 screen_max_x; 6462306a36Sopenharmony_ci s32 screen_max_y; 6562306a36Sopenharmony_ci s32 screen_min_x; 6662306a36Sopenharmony_ci s32 screen_min_y; 6762306a36Sopenharmony_ci s32 max_tp; 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct ilitek_protocol_map { 7162306a36Sopenharmony_ci u16 cmd; 7262306a36Sopenharmony_ci const char *name; 7362306a36Sopenharmony_ci int (*func)(struct ilitek_ts_data *ts, u16 cmd, u8 *inbuf, u8 *outbuf); 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cienum ilitek_cmds { 7762306a36Sopenharmony_ci /* common cmds */ 7862306a36Sopenharmony_ci GET_PTL_VER = 0, 7962306a36Sopenharmony_ci GET_FW_VER, 8062306a36Sopenharmony_ci GET_SCRN_RES, 8162306a36Sopenharmony_ci GET_TP_RES, 8262306a36Sopenharmony_ci GET_IC_MODE, 8362306a36Sopenharmony_ci GET_MCU_VER, 8462306a36Sopenharmony_ci SET_IC_SLEEP, 8562306a36Sopenharmony_ci SET_IC_WAKE, 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* ALWAYS keep at the end */ 8862306a36Sopenharmony_ci MAX_CMD_CNT 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* ILITEK I2C R/W APIs */ 9262306a36Sopenharmony_cistatic int ilitek_i2c_write_and_read(struct ilitek_ts_data *ts, 9362306a36Sopenharmony_ci u8 *cmd, int write_len, int delay, 9462306a36Sopenharmony_ci u8 *data, int read_len) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci int error; 9762306a36Sopenharmony_ci struct i2c_client *client = ts->client; 9862306a36Sopenharmony_ci struct i2c_msg msgs[] = { 9962306a36Sopenharmony_ci { 10062306a36Sopenharmony_ci .addr = client->addr, 10162306a36Sopenharmony_ci .flags = 0, 10262306a36Sopenharmony_ci .len = write_len, 10362306a36Sopenharmony_ci .buf = cmd, 10462306a36Sopenharmony_ci }, 10562306a36Sopenharmony_ci { 10662306a36Sopenharmony_ci .addr = client->addr, 10762306a36Sopenharmony_ci .flags = I2C_M_RD, 10862306a36Sopenharmony_ci .len = read_len, 10962306a36Sopenharmony_ci .buf = data, 11062306a36Sopenharmony_ci }, 11162306a36Sopenharmony_ci }; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (delay == 0 && write_len > 0 && read_len > 0) { 11462306a36Sopenharmony_ci error = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 11562306a36Sopenharmony_ci if (error < 0) 11662306a36Sopenharmony_ci return error; 11762306a36Sopenharmony_ci } else { 11862306a36Sopenharmony_ci if (write_len > 0) { 11962306a36Sopenharmony_ci error = i2c_transfer(client->adapter, msgs, 1); 12062306a36Sopenharmony_ci if (error < 0) 12162306a36Sopenharmony_ci return error; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci if (delay > 0) 12462306a36Sopenharmony_ci mdelay(delay); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (read_len > 0) { 12762306a36Sopenharmony_ci error = i2c_transfer(client->adapter, msgs + 1, 1); 12862306a36Sopenharmony_ci if (error < 0) 12962306a36Sopenharmony_ci return error; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci return 0; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* ILITEK ISR APIs */ 13762306a36Sopenharmony_cistatic void ilitek_touch_down(struct ilitek_ts_data *ts, unsigned int id, 13862306a36Sopenharmony_ci unsigned int x, unsigned int y) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct input_dev *input = ts->input_dev; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci input_mt_slot(input, id); 14362306a36Sopenharmony_ci input_mt_report_slot_state(input, MT_TOOL_FINGER, true); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci touchscreen_report_pos(input, &ts->prop, x, y, true); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic int ilitek_process_and_report_v6(struct ilitek_ts_data *ts) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci int error = 0; 15162306a36Sopenharmony_ci u8 buf[512]; 15262306a36Sopenharmony_ci int packet_len = 5; 15362306a36Sopenharmony_ci int packet_max_point = 10; 15462306a36Sopenharmony_ci int report_max_point; 15562306a36Sopenharmony_ci int i, count; 15662306a36Sopenharmony_ci struct input_dev *input = ts->input_dev; 15762306a36Sopenharmony_ci struct device *dev = &ts->client->dev; 15862306a36Sopenharmony_ci unsigned int x, y, status, id; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci error = ilitek_i2c_write_and_read(ts, NULL, 0, 0, buf, 64); 16162306a36Sopenharmony_ci if (error) { 16262306a36Sopenharmony_ci dev_err(dev, "get touch info failed, err:%d\n", error); 16362306a36Sopenharmony_ci goto err_sync_frame; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci report_max_point = buf[REPORT_COUNT_ADDRESS]; 16762306a36Sopenharmony_ci if (report_max_point > ts->max_tp) { 16862306a36Sopenharmony_ci dev_err(dev, "FW report max point:%d > panel info. max:%d\n", 16962306a36Sopenharmony_ci report_max_point, ts->max_tp); 17062306a36Sopenharmony_ci error = -EINVAL; 17162306a36Sopenharmony_ci goto err_sync_frame; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci count = DIV_ROUND_UP(report_max_point, packet_max_point); 17562306a36Sopenharmony_ci for (i = 1; i < count; i++) { 17662306a36Sopenharmony_ci error = ilitek_i2c_write_and_read(ts, NULL, 0, 0, 17762306a36Sopenharmony_ci buf + i * 64, 64); 17862306a36Sopenharmony_ci if (error) { 17962306a36Sopenharmony_ci dev_err(dev, "get touch info. failed, cnt:%d, err:%d\n", 18062306a36Sopenharmony_ci count, error); 18162306a36Sopenharmony_ci goto err_sync_frame; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci for (i = 0; i < report_max_point; i++) { 18662306a36Sopenharmony_ci status = buf[i * packet_len + 1] & 0x40; 18762306a36Sopenharmony_ci if (!status) 18862306a36Sopenharmony_ci continue; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci id = buf[i * packet_len + 1] & 0x3F; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci x = get_unaligned_le16(buf + i * packet_len + 2); 19362306a36Sopenharmony_ci y = get_unaligned_le16(buf + i * packet_len + 4); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (x > ts->screen_max_x || x < ts->screen_min_x || 19662306a36Sopenharmony_ci y > ts->screen_max_y || y < ts->screen_min_y) { 19762306a36Sopenharmony_ci dev_warn(dev, "invalid position, X[%d,%u,%d], Y[%d,%u,%d]\n", 19862306a36Sopenharmony_ci ts->screen_min_x, x, ts->screen_max_x, 19962306a36Sopenharmony_ci ts->screen_min_y, y, ts->screen_max_y); 20062306a36Sopenharmony_ci continue; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci ilitek_touch_down(ts, id, x, y); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cierr_sync_frame: 20762306a36Sopenharmony_ci input_mt_sync_frame(input); 20862306a36Sopenharmony_ci input_sync(input); 20962306a36Sopenharmony_ci return error; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci/* APIs of cmds for ILITEK Touch IC */ 21362306a36Sopenharmony_cistatic int api_protocol_set_cmd(struct ilitek_ts_data *ts, 21462306a36Sopenharmony_ci u16 idx, u8 *inbuf, u8 *outbuf) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci u16 cmd; 21762306a36Sopenharmony_ci int error; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (idx >= MAX_CMD_CNT) 22062306a36Sopenharmony_ci return -EINVAL; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci cmd = ts->ptl_cb_func[idx].cmd; 22362306a36Sopenharmony_ci error = ts->ptl_cb_func[idx].func(ts, cmd, inbuf, outbuf); 22462306a36Sopenharmony_ci if (error) 22562306a36Sopenharmony_ci return error; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return 0; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic int api_protocol_get_ptl_ver(struct ilitek_ts_data *ts, 23162306a36Sopenharmony_ci u16 cmd, u8 *inbuf, u8 *outbuf) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci int error; 23462306a36Sopenharmony_ci u8 buf[64]; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci buf[0] = cmd; 23762306a36Sopenharmony_ci error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 3); 23862306a36Sopenharmony_ci if (error) 23962306a36Sopenharmony_ci return error; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci ts->ptl.ver = get_unaligned_be16(outbuf); 24262306a36Sopenharmony_ci ts->ptl.ver_major = outbuf[0]; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return 0; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int api_protocol_get_mcu_ver(struct ilitek_ts_data *ts, 24862306a36Sopenharmony_ci u16 cmd, u8 *inbuf, u8 *outbuf) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci int error; 25162306a36Sopenharmony_ci u8 buf[64]; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci buf[0] = cmd; 25462306a36Sopenharmony_ci error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 32); 25562306a36Sopenharmony_ci if (error) 25662306a36Sopenharmony_ci return error; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci ts->mcu_ver = get_unaligned_le16(outbuf); 25962306a36Sopenharmony_ci memset(ts->product_id, 0, sizeof(ts->product_id)); 26062306a36Sopenharmony_ci memcpy(ts->product_id, outbuf + 6, 26); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic int api_protocol_get_fw_ver(struct ilitek_ts_data *ts, 26662306a36Sopenharmony_ci u16 cmd, u8 *inbuf, u8 *outbuf) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci int error; 26962306a36Sopenharmony_ci u8 buf[64]; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci buf[0] = cmd; 27262306a36Sopenharmony_ci error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 8); 27362306a36Sopenharmony_ci if (error) 27462306a36Sopenharmony_ci return error; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci memcpy(ts->firmware_ver, outbuf, 8); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int api_protocol_get_scrn_res(struct ilitek_ts_data *ts, 28262306a36Sopenharmony_ci u16 cmd, u8 *inbuf, u8 *outbuf) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci int error; 28562306a36Sopenharmony_ci u8 buf[64]; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci buf[0] = cmd; 28862306a36Sopenharmony_ci error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 8); 28962306a36Sopenharmony_ci if (error) 29062306a36Sopenharmony_ci return error; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci ts->screen_min_x = get_unaligned_le16(outbuf); 29362306a36Sopenharmony_ci ts->screen_min_y = get_unaligned_le16(outbuf + 2); 29462306a36Sopenharmony_ci ts->screen_max_x = get_unaligned_le16(outbuf + 4); 29562306a36Sopenharmony_ci ts->screen_max_y = get_unaligned_le16(outbuf + 6); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic int api_protocol_get_tp_res(struct ilitek_ts_data *ts, 30162306a36Sopenharmony_ci u16 cmd, u8 *inbuf, u8 *outbuf) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci int error; 30462306a36Sopenharmony_ci u8 buf[64]; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci buf[0] = cmd; 30762306a36Sopenharmony_ci error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 15); 30862306a36Sopenharmony_ci if (error) 30962306a36Sopenharmony_ci return error; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ts->max_tp = outbuf[8]; 31262306a36Sopenharmony_ci if (ts->max_tp > ILITEK_SUPPORT_MAX_POINT) { 31362306a36Sopenharmony_ci dev_err(&ts->client->dev, "Invalid MAX_TP:%d from FW\n", 31462306a36Sopenharmony_ci ts->max_tp); 31562306a36Sopenharmony_ci return -EINVAL; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return 0; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic int api_protocol_get_ic_mode(struct ilitek_ts_data *ts, 32262306a36Sopenharmony_ci u16 cmd, u8 *inbuf, u8 *outbuf) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci int error; 32562306a36Sopenharmony_ci u8 buf[64]; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci buf[0] = cmd; 32862306a36Sopenharmony_ci error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 2); 32962306a36Sopenharmony_ci if (error) 33062306a36Sopenharmony_ci return error; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci ts->ic_mode = outbuf[0]; 33362306a36Sopenharmony_ci return 0; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic int api_protocol_set_ic_sleep(struct ilitek_ts_data *ts, 33762306a36Sopenharmony_ci u16 cmd, u8 *inbuf, u8 *outbuf) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci u8 buf[64]; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci buf[0] = cmd; 34262306a36Sopenharmony_ci return ilitek_i2c_write_and_read(ts, buf, 1, 0, NULL, 0); 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic int api_protocol_set_ic_wake(struct ilitek_ts_data *ts, 34662306a36Sopenharmony_ci u16 cmd, u8 *inbuf, u8 *outbuf) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci u8 buf[64]; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci buf[0] = cmd; 35162306a36Sopenharmony_ci return ilitek_i2c_write_and_read(ts, buf, 1, 0, NULL, 0); 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic const struct ilitek_protocol_map ptl_func_map[] = { 35562306a36Sopenharmony_ci /* common cmds */ 35662306a36Sopenharmony_ci [GET_PTL_VER] = { 35762306a36Sopenharmony_ci ILITEK_TP_CMD_GET_PRL_VER, "GET_PTL_VER", 35862306a36Sopenharmony_ci api_protocol_get_ptl_ver 35962306a36Sopenharmony_ci }, 36062306a36Sopenharmony_ci [GET_FW_VER] = { 36162306a36Sopenharmony_ci ILITEK_TP_CMD_GET_FW_VER, "GET_FW_VER", 36262306a36Sopenharmony_ci api_protocol_get_fw_ver 36362306a36Sopenharmony_ci }, 36462306a36Sopenharmony_ci [GET_SCRN_RES] = { 36562306a36Sopenharmony_ci ILITEK_TP_CMD_GET_SCRN_RES, "GET_SCRN_RES", 36662306a36Sopenharmony_ci api_protocol_get_scrn_res 36762306a36Sopenharmony_ci }, 36862306a36Sopenharmony_ci [GET_TP_RES] = { 36962306a36Sopenharmony_ci ILITEK_TP_CMD_GET_TP_RES, "GET_TP_RES", 37062306a36Sopenharmony_ci api_protocol_get_tp_res 37162306a36Sopenharmony_ci }, 37262306a36Sopenharmony_ci [GET_IC_MODE] = { 37362306a36Sopenharmony_ci ILITEK_TP_CMD_GET_IC_MODE, "GET_IC_MODE", 37462306a36Sopenharmony_ci api_protocol_get_ic_mode 37562306a36Sopenharmony_ci }, 37662306a36Sopenharmony_ci [GET_MCU_VER] = { 37762306a36Sopenharmony_ci ILITEK_TP_CMD_GET_MCU_VER, "GET_MOD_VER", 37862306a36Sopenharmony_ci api_protocol_get_mcu_ver 37962306a36Sopenharmony_ci }, 38062306a36Sopenharmony_ci [SET_IC_SLEEP] = { 38162306a36Sopenharmony_ci ILITEK_TP_CMD_SET_IC_SLEEP, "SET_IC_SLEEP", 38262306a36Sopenharmony_ci api_protocol_set_ic_sleep 38362306a36Sopenharmony_ci }, 38462306a36Sopenharmony_ci [SET_IC_WAKE] = { 38562306a36Sopenharmony_ci ILITEK_TP_CMD_SET_IC_WAKE, "SET_IC_WAKE", 38662306a36Sopenharmony_ci api_protocol_set_ic_wake 38762306a36Sopenharmony_ci }, 38862306a36Sopenharmony_ci}; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci/* Probe APIs */ 39162306a36Sopenharmony_cistatic void ilitek_reset(struct ilitek_ts_data *ts, int delay) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci if (ts->reset_gpio) { 39462306a36Sopenharmony_ci gpiod_set_value(ts->reset_gpio, 1); 39562306a36Sopenharmony_ci mdelay(10); 39662306a36Sopenharmony_ci gpiod_set_value(ts->reset_gpio, 0); 39762306a36Sopenharmony_ci mdelay(delay); 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic int ilitek_protocol_init(struct ilitek_ts_data *ts) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci int error; 40462306a36Sopenharmony_ci u8 outbuf[64]; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci ts->ptl_cb_func = ptl_func_map; 40762306a36Sopenharmony_ci ts->reset_time = 600; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci error = api_protocol_set_cmd(ts, GET_PTL_VER, NULL, outbuf); 41062306a36Sopenharmony_ci if (error) 41162306a36Sopenharmony_ci return error; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* Protocol v3 is not support currently */ 41462306a36Sopenharmony_ci if (ts->ptl.ver_major == 0x3 || 41562306a36Sopenharmony_ci ts->ptl.ver == BL_V1_6 || 41662306a36Sopenharmony_ci ts->ptl.ver == BL_V1_7) 41762306a36Sopenharmony_ci return -EINVAL; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic int ilitek_read_tp_info(struct ilitek_ts_data *ts, bool boot) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci u8 outbuf[256]; 42562306a36Sopenharmony_ci int error; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci error = api_protocol_set_cmd(ts, GET_PTL_VER, NULL, outbuf); 42862306a36Sopenharmony_ci if (error) 42962306a36Sopenharmony_ci return error; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci error = api_protocol_set_cmd(ts, GET_MCU_VER, NULL, outbuf); 43262306a36Sopenharmony_ci if (error) 43362306a36Sopenharmony_ci return error; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci error = api_protocol_set_cmd(ts, GET_FW_VER, NULL, outbuf); 43662306a36Sopenharmony_ci if (error) 43762306a36Sopenharmony_ci return error; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (boot) { 44062306a36Sopenharmony_ci error = api_protocol_set_cmd(ts, GET_SCRN_RES, NULL, 44162306a36Sopenharmony_ci outbuf); 44262306a36Sopenharmony_ci if (error) 44362306a36Sopenharmony_ci return error; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci error = api_protocol_set_cmd(ts, GET_TP_RES, NULL, outbuf); 44762306a36Sopenharmony_ci if (error) 44862306a36Sopenharmony_ci return error; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci error = api_protocol_set_cmd(ts, GET_IC_MODE, NULL, outbuf); 45162306a36Sopenharmony_ci if (error) 45262306a36Sopenharmony_ci return error; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return 0; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic int ilitek_input_dev_init(struct device *dev, struct ilitek_ts_data *ts) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci int error; 46062306a36Sopenharmony_ci struct input_dev *input; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci input = devm_input_allocate_device(dev); 46362306a36Sopenharmony_ci if (!input) 46462306a36Sopenharmony_ci return -ENOMEM; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci ts->input_dev = input; 46762306a36Sopenharmony_ci input->name = ILITEK_TS_NAME; 46862306a36Sopenharmony_ci input->id.bustype = BUS_I2C; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci __set_bit(INPUT_PROP_DIRECT, input->propbit); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci input_set_abs_params(input, ABS_MT_POSITION_X, 47362306a36Sopenharmony_ci ts->screen_min_x, ts->screen_max_x, 0, 0); 47462306a36Sopenharmony_ci input_set_abs_params(input, ABS_MT_POSITION_Y, 47562306a36Sopenharmony_ci ts->screen_min_y, ts->screen_max_y, 0, 0); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci touchscreen_parse_properties(input, true, &ts->prop); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci error = input_mt_init_slots(input, ts->max_tp, 48062306a36Sopenharmony_ci INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); 48162306a36Sopenharmony_ci if (error) { 48262306a36Sopenharmony_ci dev_err(dev, "initialize MT slots failed, err:%d\n", error); 48362306a36Sopenharmony_ci return error; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci error = input_register_device(input); 48762306a36Sopenharmony_ci if (error) { 48862306a36Sopenharmony_ci dev_err(dev, "register input device failed, err:%d\n", error); 48962306a36Sopenharmony_ci return error; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci return 0; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic irqreturn_t ilitek_i2c_isr(int irq, void *dev_id) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct ilitek_ts_data *ts = dev_id; 49862306a36Sopenharmony_ci int error; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci error = ilitek_process_and_report_v6(ts); 50162306a36Sopenharmony_ci if (error < 0) { 50262306a36Sopenharmony_ci dev_err(&ts->client->dev, "[%s] err:%d\n", __func__, error); 50362306a36Sopenharmony_ci return IRQ_NONE; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci return IRQ_HANDLED; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic ssize_t firmware_version_show(struct device *dev, 51062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 51362306a36Sopenharmony_ci struct ilitek_ts_data *ts = i2c_get_clientdata(client); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, 51662306a36Sopenharmony_ci "fw version: [%02X%02X.%02X%02X.%02X%02X.%02X%02X]\n", 51762306a36Sopenharmony_ci ts->firmware_ver[0], ts->firmware_ver[1], 51862306a36Sopenharmony_ci ts->firmware_ver[2], ts->firmware_ver[3], 51962306a36Sopenharmony_ci ts->firmware_ver[4], ts->firmware_ver[5], 52062306a36Sopenharmony_ci ts->firmware_ver[6], ts->firmware_ver[7]); 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(firmware_version); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic ssize_t product_id_show(struct device *dev, 52562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 52862306a36Sopenharmony_ci struct ilitek_ts_data *ts = i2c_get_clientdata(client); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "product id: [%04X], module: [%s]\n", 53162306a36Sopenharmony_ci ts->mcu_ver, ts->product_id); 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(product_id); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic struct attribute *ilitek_sysfs_attrs[] = { 53662306a36Sopenharmony_ci &dev_attr_firmware_version.attr, 53762306a36Sopenharmony_ci &dev_attr_product_id.attr, 53862306a36Sopenharmony_ci NULL 53962306a36Sopenharmony_ci}; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic struct attribute_group ilitek_attrs_group = { 54262306a36Sopenharmony_ci .attrs = ilitek_sysfs_attrs, 54362306a36Sopenharmony_ci}; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic int ilitek_ts_i2c_probe(struct i2c_client *client) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci struct ilitek_ts_data *ts; 54862306a36Sopenharmony_ci struct device *dev = &client->dev; 54962306a36Sopenharmony_ci int error; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 55262306a36Sopenharmony_ci dev_err(dev, "i2c check functionality failed\n"); 55362306a36Sopenharmony_ci return -ENXIO; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); 55762306a36Sopenharmony_ci if (!ts) 55862306a36Sopenharmony_ci return -ENOMEM; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci ts->client = client; 56162306a36Sopenharmony_ci i2c_set_clientdata(client, ts); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 56462306a36Sopenharmony_ci if (IS_ERR(ts->reset_gpio)) { 56562306a36Sopenharmony_ci error = PTR_ERR(ts->reset_gpio); 56662306a36Sopenharmony_ci dev_err(dev, "request gpiod failed: %d", error); 56762306a36Sopenharmony_ci return error; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci ilitek_reset(ts, 1000); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci error = ilitek_protocol_init(ts); 57362306a36Sopenharmony_ci if (error) { 57462306a36Sopenharmony_ci dev_err(dev, "protocol init failed: %d", error); 57562306a36Sopenharmony_ci return error; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci error = ilitek_read_tp_info(ts, true); 57962306a36Sopenharmony_ci if (error) { 58062306a36Sopenharmony_ci dev_err(dev, "read tp info failed: %d", error); 58162306a36Sopenharmony_ci return error; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci error = ilitek_input_dev_init(dev, ts); 58562306a36Sopenharmony_ci if (error) { 58662306a36Sopenharmony_ci dev_err(dev, "input dev init failed: %d", error); 58762306a36Sopenharmony_ci return error; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci error = devm_request_threaded_irq(dev, ts->client->irq, 59162306a36Sopenharmony_ci NULL, ilitek_i2c_isr, IRQF_ONESHOT, 59262306a36Sopenharmony_ci "ilitek_touch_irq", ts); 59362306a36Sopenharmony_ci if (error) { 59462306a36Sopenharmony_ci dev_err(dev, "request threaded irq failed: %d\n", error); 59562306a36Sopenharmony_ci return error; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci error = devm_device_add_group(dev, &ilitek_attrs_group); 59962306a36Sopenharmony_ci if (error) { 60062306a36Sopenharmony_ci dev_err(dev, "sysfs create group failed: %d\n", error); 60162306a36Sopenharmony_ci return error; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return 0; 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int ilitek_suspend(struct device *dev) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 61062306a36Sopenharmony_ci struct ilitek_ts_data *ts = i2c_get_clientdata(client); 61162306a36Sopenharmony_ci int error; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci disable_irq(client->irq); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (!device_may_wakeup(dev)) { 61662306a36Sopenharmony_ci error = api_protocol_set_cmd(ts, SET_IC_SLEEP, NULL, NULL); 61762306a36Sopenharmony_ci if (error) 61862306a36Sopenharmony_ci return error; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci return 0; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic int ilitek_resume(struct device *dev) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 62762306a36Sopenharmony_ci struct ilitek_ts_data *ts = i2c_get_clientdata(client); 62862306a36Sopenharmony_ci int error; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (!device_may_wakeup(dev)) { 63162306a36Sopenharmony_ci error = api_protocol_set_cmd(ts, SET_IC_WAKE, NULL, NULL); 63262306a36Sopenharmony_ci if (error) 63362306a36Sopenharmony_ci return error; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci ilitek_reset(ts, ts->reset_time); 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci enable_irq(client->irq); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci return 0; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(ilitek_pm_ops, ilitek_suspend, ilitek_resume); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic const struct i2c_device_id ilitek_ts_i2c_id[] = { 64662306a36Sopenharmony_ci { ILITEK_TS_NAME, 0 }, 64762306a36Sopenharmony_ci { }, 64862306a36Sopenharmony_ci}; 64962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ilitek_ts_i2c_id); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci#ifdef CONFIG_ACPI 65262306a36Sopenharmony_cistatic const struct acpi_device_id ilitekts_acpi_id[] = { 65362306a36Sopenharmony_ci { "ILTK0001", 0 }, 65462306a36Sopenharmony_ci { }, 65562306a36Sopenharmony_ci}; 65662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, ilitekts_acpi_id); 65762306a36Sopenharmony_ci#endif 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci#ifdef CONFIG_OF 66062306a36Sopenharmony_cistatic const struct of_device_id ilitek_ts_i2c_match[] = { 66162306a36Sopenharmony_ci {.compatible = "ilitek,ili2130",}, 66262306a36Sopenharmony_ci {.compatible = "ilitek,ili2131",}, 66362306a36Sopenharmony_ci {.compatible = "ilitek,ili2132",}, 66462306a36Sopenharmony_ci {.compatible = "ilitek,ili2316",}, 66562306a36Sopenharmony_ci {.compatible = "ilitek,ili2322",}, 66662306a36Sopenharmony_ci {.compatible = "ilitek,ili2323",}, 66762306a36Sopenharmony_ci {.compatible = "ilitek,ili2326",}, 66862306a36Sopenharmony_ci {.compatible = "ilitek,ili2520",}, 66962306a36Sopenharmony_ci {.compatible = "ilitek,ili2521",}, 67062306a36Sopenharmony_ci { }, 67162306a36Sopenharmony_ci}; 67262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ilitek_ts_i2c_match); 67362306a36Sopenharmony_ci#endif 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic struct i2c_driver ilitek_ts_i2c_driver = { 67662306a36Sopenharmony_ci .driver = { 67762306a36Sopenharmony_ci .name = ILITEK_TS_NAME, 67862306a36Sopenharmony_ci .pm = pm_sleep_ptr(&ilitek_pm_ops), 67962306a36Sopenharmony_ci .of_match_table = of_match_ptr(ilitek_ts_i2c_match), 68062306a36Sopenharmony_ci .acpi_match_table = ACPI_PTR(ilitekts_acpi_id), 68162306a36Sopenharmony_ci }, 68262306a36Sopenharmony_ci .probe = ilitek_ts_i2c_probe, 68362306a36Sopenharmony_ci .id_table = ilitek_ts_i2c_id, 68462306a36Sopenharmony_ci}; 68562306a36Sopenharmony_cimodule_i2c_driver(ilitek_ts_i2c_driver); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ciMODULE_AUTHOR("ILITEK"); 68862306a36Sopenharmony_ciMODULE_DESCRIPTION("ILITEK I2C Touchscreen Driver"); 68962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 690