142370681SJoe Hung // SPDX-License-Identifier: GPL-2.0
242370681SJoe Hung /*
342370681SJoe Hung  * ILITEK Touch IC driver for 23XX, 25XX and Lego series
442370681SJoe Hung  *
542370681SJoe Hung  * Copyright (C) 2011 ILI Technology Corporation.
642370681SJoe Hung  * Copyright (C) 2020 Luca Hsu <luca_hsu@ilitek.com>
742370681SJoe Hung  * Copyright (C) 2021 Joe Hung <joe_hung@ilitek.com>
842370681SJoe Hung  */
942370681SJoe Hung 
1042370681SJoe Hung #include <linux/kernel.h>
1142370681SJoe Hung #include <linux/module.h>
1242370681SJoe Hung #include <linux/input.h>
1342370681SJoe Hung #include <linux/input/mt.h>
1442370681SJoe Hung #include <linux/i2c.h>
1542370681SJoe Hung #include <linux/slab.h>
1642370681SJoe Hung #include <linux/delay.h>
1742370681SJoe Hung #include <linux/interrupt.h>
1842370681SJoe Hung #include <linux/gpio.h>
1942370681SJoe Hung #include <linux/gpio/consumer.h>
2042370681SJoe Hung #include <linux/errno.h>
2142370681SJoe Hung #include <linux/acpi.h>
2242370681SJoe Hung #include <linux/input/touchscreen.h>
2342370681SJoe Hung #include <asm/unaligned.h>
2442370681SJoe Hung 
2542370681SJoe Hung 
2642370681SJoe Hung #define ILITEK_TS_NAME					"ilitek_ts"
2742370681SJoe Hung #define BL_V1_8						0x108
2842370681SJoe Hung #define BL_V1_7						0x107
2942370681SJoe Hung #define BL_V1_6						0x106
3042370681SJoe Hung 
3142370681SJoe Hung #define ILITEK_TP_CMD_GET_TP_RES			0x20
3242370681SJoe Hung #define ILITEK_TP_CMD_GET_SCRN_RES			0x21
3342370681SJoe Hung #define ILITEK_TP_CMD_SET_IC_SLEEP			0x30
3442370681SJoe Hung #define ILITEK_TP_CMD_SET_IC_WAKE			0x31
3542370681SJoe Hung #define ILITEK_TP_CMD_GET_FW_VER			0x40
3642370681SJoe Hung #define ILITEK_TP_CMD_GET_PRL_VER			0x42
3742370681SJoe Hung #define ILITEK_TP_CMD_GET_MCU_VER			0x61
3842370681SJoe Hung #define ILITEK_TP_CMD_GET_IC_MODE			0xC0
3942370681SJoe Hung 
4042370681SJoe Hung #define REPORT_COUNT_ADDRESS				61
4142370681SJoe Hung #define ILITEK_SUPPORT_MAX_POINT			40
4242370681SJoe Hung 
4342370681SJoe Hung struct ilitek_protocol_info {
4442370681SJoe Hung 	u16 ver;
4542370681SJoe Hung 	u8 ver_major;
4642370681SJoe Hung };
4742370681SJoe Hung 
4842370681SJoe Hung struct ilitek_ts_data {
4942370681SJoe Hung 	struct i2c_client		*client;
5042370681SJoe Hung 	struct gpio_desc		*reset_gpio;
5142370681SJoe Hung 	struct input_dev		*input_dev;
5242370681SJoe Hung 	struct touchscreen_properties	prop;
5342370681SJoe Hung 
5442370681SJoe Hung 	const struct ilitek_protocol_map *ptl_cb_func;
5542370681SJoe Hung 	struct ilitek_protocol_info	ptl;
5642370681SJoe Hung 
5742370681SJoe Hung 	char				product_id[30];
5842370681SJoe Hung 	u16				mcu_ver;
5942370681SJoe Hung 	u8				ic_mode;
6042370681SJoe Hung 	u8				firmware_ver[8];
6142370681SJoe Hung 
6242370681SJoe Hung 	s32				reset_time;
6342370681SJoe Hung 	s32				screen_max_x;
6442370681SJoe Hung 	s32				screen_max_y;
6542370681SJoe Hung 	s32				screen_min_x;
6642370681SJoe Hung 	s32				screen_min_y;
6742370681SJoe Hung 	s32				max_tp;
6842370681SJoe Hung };
6942370681SJoe Hung 
7042370681SJoe Hung struct ilitek_protocol_map {
7142370681SJoe Hung 	u16 cmd;
7242370681SJoe Hung 	const char *name;
7342370681SJoe Hung 	int (*func)(struct ilitek_ts_data *ts, u16 cmd, u8 *inbuf, u8 *outbuf);
7442370681SJoe Hung };
7542370681SJoe Hung 
7642370681SJoe Hung enum ilitek_cmds {
7742370681SJoe Hung 	/* common cmds */
7842370681SJoe Hung 	GET_PTL_VER = 0,
7942370681SJoe Hung 	GET_FW_VER,
8042370681SJoe Hung 	GET_SCRN_RES,
8142370681SJoe Hung 	GET_TP_RES,
8242370681SJoe Hung 	GET_IC_MODE,
8342370681SJoe Hung 	GET_MCU_VER,
8442370681SJoe Hung 	SET_IC_SLEEP,
8542370681SJoe Hung 	SET_IC_WAKE,
8642370681SJoe Hung 
8742370681SJoe Hung 	/* ALWAYS keep at the end */
8842370681SJoe Hung 	MAX_CMD_CNT
8942370681SJoe Hung };
9042370681SJoe Hung 
9142370681SJoe Hung /* ILITEK I2C R/W APIs */
ilitek_i2c_write_and_read(struct ilitek_ts_data * ts,u8 * cmd,int write_len,int delay,u8 * data,int read_len)9242370681SJoe Hung static int ilitek_i2c_write_and_read(struct ilitek_ts_data *ts,
9342370681SJoe Hung 				     u8 *cmd, int write_len, int delay,
9442370681SJoe Hung 				     u8 *data, int read_len)
9542370681SJoe Hung {
9642370681SJoe Hung 	int error;
9742370681SJoe Hung 	struct i2c_client *client = ts->client;
9842370681SJoe Hung 	struct i2c_msg msgs[] = {
9942370681SJoe Hung 		{
10042370681SJoe Hung 			.addr = client->addr,
10142370681SJoe Hung 			.flags = 0,
10242370681SJoe Hung 			.len = write_len,
10342370681SJoe Hung 			.buf = cmd,
10442370681SJoe Hung 		},
10542370681SJoe Hung 		{
10642370681SJoe Hung 			.addr = client->addr,
10742370681SJoe Hung 			.flags = I2C_M_RD,
10842370681SJoe Hung 			.len = read_len,
10942370681SJoe Hung 			.buf = data,
11042370681SJoe Hung 		},
11142370681SJoe Hung 	};
11242370681SJoe Hung 
11342370681SJoe Hung 	if (delay == 0 && write_len > 0 && read_len > 0) {
11442370681SJoe Hung 		error = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
11542370681SJoe Hung 		if (error < 0)
11642370681SJoe Hung 			return error;
11742370681SJoe Hung 	} else {
11842370681SJoe Hung 		if (write_len > 0) {
11942370681SJoe Hung 			error = i2c_transfer(client->adapter, msgs, 1);
12042370681SJoe Hung 			if (error < 0)
12142370681SJoe Hung 				return error;
12242370681SJoe Hung 		}
12342370681SJoe Hung 		if (delay > 0)
12442370681SJoe Hung 			mdelay(delay);
12542370681SJoe Hung 
12642370681SJoe Hung 		if (read_len > 0) {
12742370681SJoe Hung 			error = i2c_transfer(client->adapter, msgs + 1, 1);
12842370681SJoe Hung 			if (error < 0)
12942370681SJoe Hung 				return error;
13042370681SJoe Hung 		}
13142370681SJoe Hung 	}
13242370681SJoe Hung 
13342370681SJoe Hung 	return 0;
13442370681SJoe Hung }
13542370681SJoe Hung 
13642370681SJoe Hung /* ILITEK ISR APIs */
ilitek_touch_down(struct ilitek_ts_data * ts,unsigned int id,unsigned int x,unsigned int y)13742370681SJoe Hung static void ilitek_touch_down(struct ilitek_ts_data *ts, unsigned int id,
13842370681SJoe Hung 			      unsigned int x, unsigned int y)
13942370681SJoe Hung {
14042370681SJoe Hung 	struct input_dev *input = ts->input_dev;
14142370681SJoe Hung 
14242370681SJoe Hung 	input_mt_slot(input, id);
14342370681SJoe Hung 	input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
14442370681SJoe Hung 
14542370681SJoe Hung 	touchscreen_report_pos(input, &ts->prop, x, y, true);
14642370681SJoe Hung }
14742370681SJoe Hung 
ilitek_process_and_report_v6(struct ilitek_ts_data * ts)14842370681SJoe Hung static int ilitek_process_and_report_v6(struct ilitek_ts_data *ts)
14942370681SJoe Hung {
15042370681SJoe Hung 	int error = 0;
15142370681SJoe Hung 	u8 buf[512];
15242370681SJoe Hung 	int packet_len = 5;
15342370681SJoe Hung 	int packet_max_point = 10;
15442370681SJoe Hung 	int report_max_point;
15542370681SJoe Hung 	int i, count;
15642370681SJoe Hung 	struct input_dev *input = ts->input_dev;
15742370681SJoe Hung 	struct device *dev = &ts->client->dev;
15842370681SJoe Hung 	unsigned int x, y, status, id;
15942370681SJoe Hung 
16042370681SJoe Hung 	error = ilitek_i2c_write_and_read(ts, NULL, 0, 0, buf, 64);
16142370681SJoe Hung 	if (error) {
16242370681SJoe Hung 		dev_err(dev, "get touch info failed, err:%d\n", error);
16342370681SJoe Hung 		goto err_sync_frame;
16442370681SJoe Hung 	}
16542370681SJoe Hung 
16642370681SJoe Hung 	report_max_point = buf[REPORT_COUNT_ADDRESS];
16742370681SJoe Hung 	if (report_max_point > ts->max_tp) {
16842370681SJoe Hung 		dev_err(dev, "FW report max point:%d > panel info. max:%d\n",
16942370681SJoe Hung 			report_max_point, ts->max_tp);
17042370681SJoe Hung 		error = -EINVAL;
17142370681SJoe Hung 		goto err_sync_frame;
17242370681SJoe Hung 	}
17342370681SJoe Hung 
17442370681SJoe Hung 	count = DIV_ROUND_UP(report_max_point, packet_max_point);
17542370681SJoe Hung 	for (i = 1; i < count; i++) {
17642370681SJoe Hung 		error = ilitek_i2c_write_and_read(ts, NULL, 0, 0,
17742370681SJoe Hung 						  buf + i * 64, 64);
17842370681SJoe Hung 		if (error) {
17942370681SJoe Hung 			dev_err(dev, "get touch info. failed, cnt:%d, err:%d\n",
18042370681SJoe Hung 				count, error);
18142370681SJoe Hung 			goto err_sync_frame;
18242370681SJoe Hung 		}
18342370681SJoe Hung 	}
18442370681SJoe Hung 
18542370681SJoe Hung 	for (i = 0; i < report_max_point; i++) {
18642370681SJoe Hung 		status = buf[i * packet_len + 1] & 0x40;
18742370681SJoe Hung 		if (!status)
18842370681SJoe Hung 			continue;
18942370681SJoe Hung 
19042370681SJoe Hung 		id = buf[i * packet_len + 1] & 0x3F;
19142370681SJoe Hung 
19242370681SJoe Hung 		x = get_unaligned_le16(buf + i * packet_len + 2);
19342370681SJoe Hung 		y = get_unaligned_le16(buf + i * packet_len + 4);
19442370681SJoe Hung 
19542370681SJoe Hung 		if (x > ts->screen_max_x || x < ts->screen_min_x ||
19642370681SJoe Hung 		    y > ts->screen_max_y || y < ts->screen_min_y) {
19742370681SJoe Hung 			dev_warn(dev, "invalid position, X[%d,%u,%d], Y[%d,%u,%d]\n",
19842370681SJoe Hung 				 ts->screen_min_x, x, ts->screen_max_x,
19942370681SJoe Hung 				 ts->screen_min_y, y, ts->screen_max_y);
20042370681SJoe Hung 			continue;
20142370681SJoe Hung 		}
20242370681SJoe Hung 
20342370681SJoe Hung 		ilitek_touch_down(ts, id, x, y);
20442370681SJoe Hung 	}
20542370681SJoe Hung 
20642370681SJoe Hung err_sync_frame:
20742370681SJoe Hung 	input_mt_sync_frame(input);
20842370681SJoe Hung 	input_sync(input);
20942370681SJoe Hung 	return error;
21042370681SJoe Hung }
21142370681SJoe Hung 
21242370681SJoe Hung /* APIs of cmds for ILITEK Touch IC */
api_protocol_set_cmd(struct ilitek_ts_data * ts,u16 idx,u8 * inbuf,u8 * outbuf)21342370681SJoe Hung static int api_protocol_set_cmd(struct ilitek_ts_data *ts,
21442370681SJoe Hung 				u16 idx, u8 *inbuf, u8 *outbuf)
21542370681SJoe Hung {
21642370681SJoe Hung 	u16 cmd;
21742370681SJoe Hung 	int error;
21842370681SJoe Hung 
21942370681SJoe Hung 	if (idx >= MAX_CMD_CNT)
22042370681SJoe Hung 		return -EINVAL;
22142370681SJoe Hung 
22242370681SJoe Hung 	cmd = ts->ptl_cb_func[idx].cmd;
22342370681SJoe Hung 	error = ts->ptl_cb_func[idx].func(ts, cmd, inbuf, outbuf);
22442370681SJoe Hung 	if (error)
22542370681SJoe Hung 		return error;
22642370681SJoe Hung 
22742370681SJoe Hung 	return 0;
22842370681SJoe Hung }
22942370681SJoe Hung 
api_protocol_get_ptl_ver(struct ilitek_ts_data * ts,u16 cmd,u8 * inbuf,u8 * outbuf)23042370681SJoe Hung static int api_protocol_get_ptl_ver(struct ilitek_ts_data *ts,
23142370681SJoe Hung 				    u16 cmd, u8 *inbuf, u8 *outbuf)
23242370681SJoe Hung {
23342370681SJoe Hung 	int error;
23442370681SJoe Hung 	u8 buf[64];
23542370681SJoe Hung 
23642370681SJoe Hung 	buf[0] = cmd;
23742370681SJoe Hung 	error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 3);
23842370681SJoe Hung 	if (error)
23942370681SJoe Hung 		return error;
24042370681SJoe Hung 
24142370681SJoe Hung 	ts->ptl.ver = get_unaligned_be16(outbuf);
24242370681SJoe Hung 	ts->ptl.ver_major = outbuf[0];
24342370681SJoe Hung 
24442370681SJoe Hung 	return 0;
24542370681SJoe Hung }
24642370681SJoe Hung 
api_protocol_get_mcu_ver(struct ilitek_ts_data * ts,u16 cmd,u8 * inbuf,u8 * outbuf)24742370681SJoe Hung static int api_protocol_get_mcu_ver(struct ilitek_ts_data *ts,
24842370681SJoe Hung 				    u16 cmd, u8 *inbuf, u8 *outbuf)
24942370681SJoe Hung {
25042370681SJoe Hung 	int error;
25142370681SJoe Hung 	u8 buf[64];
25242370681SJoe Hung 
25342370681SJoe Hung 	buf[0] = cmd;
25442370681SJoe Hung 	error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 32);
25542370681SJoe Hung 	if (error)
25642370681SJoe Hung 		return error;
25742370681SJoe Hung 
25842370681SJoe Hung 	ts->mcu_ver = get_unaligned_le16(outbuf);
25942370681SJoe Hung 	memset(ts->product_id, 0, sizeof(ts->product_id));
26042370681SJoe Hung 	memcpy(ts->product_id, outbuf + 6, 26);
26142370681SJoe Hung 
26242370681SJoe Hung 	return 0;
26342370681SJoe Hung }
26442370681SJoe Hung 
api_protocol_get_fw_ver(struct ilitek_ts_data * ts,u16 cmd,u8 * inbuf,u8 * outbuf)26542370681SJoe Hung static int api_protocol_get_fw_ver(struct ilitek_ts_data *ts,
26642370681SJoe Hung 				   u16 cmd, u8 *inbuf, u8 *outbuf)
26742370681SJoe Hung {
26842370681SJoe Hung 	int error;
26942370681SJoe Hung 	u8 buf[64];
27042370681SJoe Hung 
27142370681SJoe Hung 	buf[0] = cmd;
27242370681SJoe Hung 	error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 8);
27342370681SJoe Hung 	if (error)
27442370681SJoe Hung 		return error;
27542370681SJoe Hung 
27642370681SJoe Hung 	memcpy(ts->firmware_ver, outbuf, 8);
27742370681SJoe Hung 
27842370681SJoe Hung 	return 0;
27942370681SJoe Hung }
28042370681SJoe Hung 
api_protocol_get_scrn_res(struct ilitek_ts_data * ts,u16 cmd,u8 * inbuf,u8 * outbuf)28142370681SJoe Hung static int api_protocol_get_scrn_res(struct ilitek_ts_data *ts,
28242370681SJoe Hung 				     u16 cmd, u8 *inbuf, u8 *outbuf)
28342370681SJoe Hung {
28442370681SJoe Hung 	int error;
28542370681SJoe Hung 	u8 buf[64];
28642370681SJoe Hung 
28742370681SJoe Hung 	buf[0] = cmd;
28842370681SJoe Hung 	error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 8);
28942370681SJoe Hung 	if (error)
29042370681SJoe Hung 		return error;
29142370681SJoe Hung 
29242370681SJoe Hung 	ts->screen_min_x = get_unaligned_le16(outbuf);
29342370681SJoe Hung 	ts->screen_min_y = get_unaligned_le16(outbuf + 2);
29442370681SJoe Hung 	ts->screen_max_x = get_unaligned_le16(outbuf + 4);
29542370681SJoe Hung 	ts->screen_max_y = get_unaligned_le16(outbuf + 6);
29642370681SJoe Hung 
29742370681SJoe Hung 	return 0;
29842370681SJoe Hung }
29942370681SJoe Hung 
api_protocol_get_tp_res(struct ilitek_ts_data * ts,u16 cmd,u8 * inbuf,u8 * outbuf)30042370681SJoe Hung static int api_protocol_get_tp_res(struct ilitek_ts_data *ts,
30142370681SJoe Hung 				   u16 cmd, u8 *inbuf, u8 *outbuf)
30242370681SJoe Hung {
30342370681SJoe Hung 	int error;
30442370681SJoe Hung 	u8 buf[64];
30542370681SJoe Hung 
30642370681SJoe Hung 	buf[0] = cmd;
30742370681SJoe Hung 	error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 15);
30842370681SJoe Hung 	if (error)
30942370681SJoe Hung 		return error;
31042370681SJoe Hung 
31142370681SJoe Hung 	ts->max_tp = outbuf[8];
31242370681SJoe Hung 	if (ts->max_tp > ILITEK_SUPPORT_MAX_POINT) {
31342370681SJoe Hung 		dev_err(&ts->client->dev, "Invalid MAX_TP:%d from FW\n",
31442370681SJoe Hung 			ts->max_tp);
31542370681SJoe Hung 		return -EINVAL;
31642370681SJoe Hung 	}
31742370681SJoe Hung 
31842370681SJoe Hung 	return 0;
31942370681SJoe Hung }
32042370681SJoe Hung 
api_protocol_get_ic_mode(struct ilitek_ts_data * ts,u16 cmd,u8 * inbuf,u8 * outbuf)32142370681SJoe Hung static int api_protocol_get_ic_mode(struct ilitek_ts_data *ts,
32242370681SJoe Hung 				    u16 cmd, u8 *inbuf, u8 *outbuf)
32342370681SJoe Hung {
32442370681SJoe Hung 	int error;
32542370681SJoe Hung 	u8 buf[64];
32642370681SJoe Hung 
32742370681SJoe Hung 	buf[0] = cmd;
32842370681SJoe Hung 	error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 2);
32942370681SJoe Hung 	if (error)
33042370681SJoe Hung 		return error;
33142370681SJoe Hung 
33242370681SJoe Hung 	ts->ic_mode = outbuf[0];
33342370681SJoe Hung 	return 0;
33442370681SJoe Hung }
33542370681SJoe Hung 
api_protocol_set_ic_sleep(struct ilitek_ts_data * ts,u16 cmd,u8 * inbuf,u8 * outbuf)33642370681SJoe Hung static int api_protocol_set_ic_sleep(struct ilitek_ts_data *ts,
33742370681SJoe Hung 				     u16 cmd, u8 *inbuf, u8 *outbuf)
33842370681SJoe Hung {
33942370681SJoe Hung 	u8 buf[64];
34042370681SJoe Hung 
34142370681SJoe Hung 	buf[0] = cmd;
34242370681SJoe Hung 	return ilitek_i2c_write_and_read(ts, buf, 1, 0, NULL, 0);
34342370681SJoe Hung }
34442370681SJoe Hung 
api_protocol_set_ic_wake(struct ilitek_ts_data * ts,u16 cmd,u8 * inbuf,u8 * outbuf)34542370681SJoe Hung static int api_protocol_set_ic_wake(struct ilitek_ts_data *ts,
34642370681SJoe Hung 				    u16 cmd, u8 *inbuf, u8 *outbuf)
34742370681SJoe Hung {
34842370681SJoe Hung 	u8 buf[64];
34942370681SJoe Hung 
35042370681SJoe Hung 	buf[0] = cmd;
35142370681SJoe Hung 	return ilitek_i2c_write_and_read(ts, buf, 1, 0, NULL, 0);
35242370681SJoe Hung }
35342370681SJoe Hung 
35442370681SJoe Hung static const struct ilitek_protocol_map ptl_func_map[] = {
35542370681SJoe Hung 	/* common cmds */
35642370681SJoe Hung 	[GET_PTL_VER] = {
35742370681SJoe Hung 		ILITEK_TP_CMD_GET_PRL_VER, "GET_PTL_VER",
35842370681SJoe Hung 		api_protocol_get_ptl_ver
35942370681SJoe Hung 	},
36042370681SJoe Hung 	[GET_FW_VER] = {
36142370681SJoe Hung 		ILITEK_TP_CMD_GET_FW_VER, "GET_FW_VER",
36242370681SJoe Hung 		api_protocol_get_fw_ver
36342370681SJoe Hung 	},
36442370681SJoe Hung 	[GET_SCRN_RES] = {
36542370681SJoe Hung 		ILITEK_TP_CMD_GET_SCRN_RES, "GET_SCRN_RES",
36642370681SJoe Hung 		api_protocol_get_scrn_res
36742370681SJoe Hung 	},
36842370681SJoe Hung 	[GET_TP_RES] = {
36942370681SJoe Hung 		ILITEK_TP_CMD_GET_TP_RES, "GET_TP_RES",
37042370681SJoe Hung 		api_protocol_get_tp_res
37142370681SJoe Hung 	},
37242370681SJoe Hung 	[GET_IC_MODE] = {
37342370681SJoe Hung 		ILITEK_TP_CMD_GET_IC_MODE, "GET_IC_MODE",
37442370681SJoe Hung 			   api_protocol_get_ic_mode
37542370681SJoe Hung 	},
37642370681SJoe Hung 	[GET_MCU_VER] = {
37742370681SJoe Hung 		ILITEK_TP_CMD_GET_MCU_VER, "GET_MOD_VER",
37842370681SJoe Hung 			   api_protocol_get_mcu_ver
37942370681SJoe Hung 	},
38042370681SJoe Hung 	[SET_IC_SLEEP] = {
38142370681SJoe Hung 		ILITEK_TP_CMD_SET_IC_SLEEP, "SET_IC_SLEEP",
38242370681SJoe Hung 		api_protocol_set_ic_sleep
38342370681SJoe Hung 	},
38442370681SJoe Hung 	[SET_IC_WAKE] = {
38542370681SJoe Hung 		ILITEK_TP_CMD_SET_IC_WAKE, "SET_IC_WAKE",
38642370681SJoe Hung 		api_protocol_set_ic_wake
38742370681SJoe Hung 	},
38842370681SJoe Hung };
38942370681SJoe Hung 
39042370681SJoe Hung /* Probe APIs */
ilitek_reset(struct ilitek_ts_data * ts,int delay)39142370681SJoe Hung static void ilitek_reset(struct ilitek_ts_data *ts, int delay)
39242370681SJoe Hung {
39342370681SJoe Hung 	if (ts->reset_gpio) {
39442370681SJoe Hung 		gpiod_set_value(ts->reset_gpio, 1);
39542370681SJoe Hung 		mdelay(10);
39642370681SJoe Hung 		gpiod_set_value(ts->reset_gpio, 0);
39742370681SJoe Hung 		mdelay(delay);
39842370681SJoe Hung 	}
39942370681SJoe Hung }
40042370681SJoe Hung 
ilitek_protocol_init(struct ilitek_ts_data * ts)40142370681SJoe Hung static int ilitek_protocol_init(struct ilitek_ts_data *ts)
40242370681SJoe Hung {
40342370681SJoe Hung 	int error;
40442370681SJoe Hung 	u8 outbuf[64];
40542370681SJoe Hung 
40642370681SJoe Hung 	ts->ptl_cb_func = ptl_func_map;
40742370681SJoe Hung 	ts->reset_time = 600;
40842370681SJoe Hung 
40942370681SJoe Hung 	error = api_protocol_set_cmd(ts, GET_PTL_VER, NULL, outbuf);
41042370681SJoe Hung 	if (error)
41142370681SJoe Hung 		return error;
41242370681SJoe Hung 
41342370681SJoe Hung 	/* Protocol v3 is not support currently */
41442370681SJoe Hung 	if (ts->ptl.ver_major == 0x3 ||
41542370681SJoe Hung 	    ts->ptl.ver == BL_V1_6 ||
41642370681SJoe Hung 	    ts->ptl.ver == BL_V1_7)
41742370681SJoe Hung 		return -EINVAL;
41842370681SJoe Hung 
41942370681SJoe Hung 	return 0;
42042370681SJoe Hung }
42142370681SJoe Hung 
ilitek_read_tp_info(struct ilitek_ts_data * ts,bool boot)42242370681SJoe Hung static int ilitek_read_tp_info(struct ilitek_ts_data *ts, bool boot)
42342370681SJoe Hung {
42442370681SJoe Hung 	u8 outbuf[256];
42542370681SJoe Hung 	int error;
42642370681SJoe Hung 
42742370681SJoe Hung 	error = api_protocol_set_cmd(ts, GET_PTL_VER, NULL, outbuf);
42842370681SJoe Hung 	if (error)
42942370681SJoe Hung 		return error;
43042370681SJoe Hung 
43142370681SJoe Hung 	error = api_protocol_set_cmd(ts, GET_MCU_VER, NULL, outbuf);
43242370681SJoe Hung 	if (error)
43342370681SJoe Hung 		return error;
43442370681SJoe Hung 
43542370681SJoe Hung 	error = api_protocol_set_cmd(ts, GET_FW_VER, NULL, outbuf);
43642370681SJoe Hung 	if (error)
43742370681SJoe Hung 		return error;
43842370681SJoe Hung 
43942370681SJoe Hung 	if (boot) {
44042370681SJoe Hung 		error = api_protocol_set_cmd(ts, GET_SCRN_RES, NULL,
44142370681SJoe Hung 					     outbuf);
44242370681SJoe Hung 		if (error)
44342370681SJoe Hung 			return error;
44442370681SJoe Hung 	}
44542370681SJoe Hung 
44642370681SJoe Hung 	error = api_protocol_set_cmd(ts, GET_TP_RES, NULL, outbuf);
44742370681SJoe Hung 	if (error)
44842370681SJoe Hung 		return error;
44942370681SJoe Hung 
45042370681SJoe Hung 	error = api_protocol_set_cmd(ts, GET_IC_MODE, NULL, outbuf);
45142370681SJoe Hung 	if (error)
45242370681SJoe Hung 		return error;
45342370681SJoe Hung 
45442370681SJoe Hung 	return 0;
45542370681SJoe Hung }
45642370681SJoe Hung 
ilitek_input_dev_init(struct device * dev,struct ilitek_ts_data * ts)45742370681SJoe Hung static int ilitek_input_dev_init(struct device *dev, struct ilitek_ts_data *ts)
45842370681SJoe Hung {
45942370681SJoe Hung 	int error;
46042370681SJoe Hung 	struct input_dev *input;
46142370681SJoe Hung 
46242370681SJoe Hung 	input = devm_input_allocate_device(dev);
46342370681SJoe Hung 	if (!input)
46442370681SJoe Hung 		return -ENOMEM;
46542370681SJoe Hung 
46642370681SJoe Hung 	ts->input_dev = input;
46742370681SJoe Hung 	input->name = ILITEK_TS_NAME;
46842370681SJoe Hung 	input->id.bustype = BUS_I2C;
46942370681SJoe Hung 
47042370681SJoe Hung 	__set_bit(INPUT_PROP_DIRECT, input->propbit);
47142370681SJoe Hung 
47242370681SJoe Hung 	input_set_abs_params(input, ABS_MT_POSITION_X,
47342370681SJoe Hung 			     ts->screen_min_x, ts->screen_max_x, 0, 0);
47442370681SJoe Hung 	input_set_abs_params(input, ABS_MT_POSITION_Y,
47542370681SJoe Hung 			     ts->screen_min_y, ts->screen_max_y, 0, 0);
47642370681SJoe Hung 
47742370681SJoe Hung 	touchscreen_parse_properties(input, true, &ts->prop);
47842370681SJoe Hung 
47942370681SJoe Hung 	error = input_mt_init_slots(input, ts->max_tp,
48042370681SJoe Hung 				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
48142370681SJoe Hung 	if (error) {
48242370681SJoe Hung 		dev_err(dev, "initialize MT slots failed, err:%d\n", error);
48342370681SJoe Hung 		return error;
48442370681SJoe Hung 	}
48542370681SJoe Hung 
48642370681SJoe Hung 	error = input_register_device(input);
48742370681SJoe Hung 	if (error) {
48842370681SJoe Hung 		dev_err(dev, "register input device failed, err:%d\n", error);
48942370681SJoe Hung 		return error;
49042370681SJoe Hung 	}
49142370681SJoe Hung 
49242370681SJoe Hung 	return 0;
49342370681SJoe Hung }
49442370681SJoe Hung 
ilitek_i2c_isr(int irq,void * dev_id)49542370681SJoe Hung static irqreturn_t ilitek_i2c_isr(int irq, void *dev_id)
49642370681SJoe Hung {
49742370681SJoe Hung 	struct ilitek_ts_data *ts = dev_id;
49842370681SJoe Hung 	int error;
49942370681SJoe Hung 
50042370681SJoe Hung 	error = ilitek_process_and_report_v6(ts);
50142370681SJoe Hung 	if (error < 0) {
50242370681SJoe Hung 		dev_err(&ts->client->dev, "[%s] err:%d\n", __func__, error);
50342370681SJoe Hung 		return IRQ_NONE;
50442370681SJoe Hung 	}
50542370681SJoe Hung 
50642370681SJoe Hung 	return IRQ_HANDLED;
50742370681SJoe Hung }
50842370681SJoe Hung 
firmware_version_show(struct device * dev,struct device_attribute * attr,char * buf)50942370681SJoe Hung static ssize_t firmware_version_show(struct device *dev,
51042370681SJoe Hung 				     struct device_attribute *attr, char *buf)
51142370681SJoe Hung {
51242370681SJoe Hung 	struct i2c_client *client = to_i2c_client(dev);
51342370681SJoe Hung 	struct ilitek_ts_data *ts = i2c_get_clientdata(client);
51442370681SJoe Hung 
51542370681SJoe Hung 	return scnprintf(buf, PAGE_SIZE,
51642370681SJoe Hung 			 "fw version: [%02X%02X.%02X%02X.%02X%02X.%02X%02X]\n",
51742370681SJoe Hung 			 ts->firmware_ver[0], ts->firmware_ver[1],
51842370681SJoe Hung 			 ts->firmware_ver[2], ts->firmware_ver[3],
51942370681SJoe Hung 			 ts->firmware_ver[4], ts->firmware_ver[5],
52042370681SJoe Hung 			 ts->firmware_ver[6], ts->firmware_ver[7]);
52142370681SJoe Hung }
52242370681SJoe Hung static DEVICE_ATTR_RO(firmware_version);
52342370681SJoe Hung 
product_id_show(struct device * dev,struct device_attribute * attr,char * buf)52442370681SJoe Hung static ssize_t product_id_show(struct device *dev,
52542370681SJoe Hung 			       struct device_attribute *attr, char *buf)
52642370681SJoe Hung {
52742370681SJoe Hung 	struct i2c_client *client = to_i2c_client(dev);
52842370681SJoe Hung 	struct ilitek_ts_data *ts = i2c_get_clientdata(client);
52942370681SJoe Hung 
53042370681SJoe Hung 	return scnprintf(buf, PAGE_SIZE, "product id: [%04X], module: [%s]\n",
53142370681SJoe Hung 			 ts->mcu_ver, ts->product_id);
53242370681SJoe Hung }
53342370681SJoe Hung static DEVICE_ATTR_RO(product_id);
53442370681SJoe Hung 
53542370681SJoe Hung static struct attribute *ilitek_sysfs_attrs[] = {
53642370681SJoe Hung 	&dev_attr_firmware_version.attr,
53742370681SJoe Hung 	&dev_attr_product_id.attr,
53842370681SJoe Hung 	NULL
53942370681SJoe Hung };
54042370681SJoe Hung 
54142370681SJoe Hung static struct attribute_group ilitek_attrs_group = {
54242370681SJoe Hung 	.attrs = ilitek_sysfs_attrs,
54342370681SJoe Hung };
54442370681SJoe Hung 
ilitek_ts_i2c_probe(struct i2c_client * client)54519a28e79SUwe Kleine-König static int ilitek_ts_i2c_probe(struct i2c_client *client)
54642370681SJoe Hung {
54742370681SJoe Hung 	struct ilitek_ts_data *ts;
54842370681SJoe Hung 	struct device *dev = &client->dev;
54942370681SJoe Hung 	int error;
55042370681SJoe Hung 
55142370681SJoe Hung 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
55242370681SJoe Hung 		dev_err(dev, "i2c check functionality failed\n");
55342370681SJoe Hung 		return -ENXIO;
55442370681SJoe Hung 	}
55542370681SJoe Hung 
55642370681SJoe Hung 	ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
55742370681SJoe Hung 	if (!ts)
55842370681SJoe Hung 		return -ENOMEM;
55942370681SJoe Hung 
56042370681SJoe Hung 	ts->client = client;
56142370681SJoe Hung 	i2c_set_clientdata(client, ts);
56242370681SJoe Hung 
56342370681SJoe Hung 	ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
56442370681SJoe Hung 	if (IS_ERR(ts->reset_gpio)) {
56542370681SJoe Hung 		error = PTR_ERR(ts->reset_gpio);
56642370681SJoe Hung 		dev_err(dev, "request gpiod failed: %d", error);
56742370681SJoe Hung 		return error;
56842370681SJoe Hung 	}
56942370681SJoe Hung 
57042370681SJoe Hung 	ilitek_reset(ts, 1000);
57142370681SJoe Hung 
57242370681SJoe Hung 	error = ilitek_protocol_init(ts);
57342370681SJoe Hung 	if (error) {
57442370681SJoe Hung 		dev_err(dev, "protocol init failed: %d", error);
57542370681SJoe Hung 		return error;
57642370681SJoe Hung 	}
57742370681SJoe Hung 
57842370681SJoe Hung 	error = ilitek_read_tp_info(ts, true);
57942370681SJoe Hung 	if (error) {
58042370681SJoe Hung 		dev_err(dev, "read tp info failed: %d", error);
58142370681SJoe Hung 		return error;
58242370681SJoe Hung 	}
58342370681SJoe Hung 
58442370681SJoe Hung 	error = ilitek_input_dev_init(dev, ts);
58542370681SJoe Hung 	if (error) {
58642370681SJoe Hung 		dev_err(dev, "input dev init failed: %d", error);
58742370681SJoe Hung 		return error;
58842370681SJoe Hung 	}
58942370681SJoe Hung 
59042370681SJoe Hung 	error = devm_request_threaded_irq(dev, ts->client->irq,
59142370681SJoe Hung 					  NULL, ilitek_i2c_isr, IRQF_ONESHOT,
59242370681SJoe Hung 					  "ilitek_touch_irq", ts);
59342370681SJoe Hung 	if (error) {
59442370681SJoe Hung 		dev_err(dev, "request threaded irq failed: %d\n", error);
59542370681SJoe Hung 		return error;
59642370681SJoe Hung 	}
59742370681SJoe Hung 
59842370681SJoe Hung 	error = devm_device_add_group(dev, &ilitek_attrs_group);
59942370681SJoe Hung 	if (error) {
60042370681SJoe Hung 		dev_err(dev, "sysfs create group failed: %d\n", error);
60142370681SJoe Hung 		return error;
60242370681SJoe Hung 	}
60342370681SJoe Hung 
60442370681SJoe Hung 	return 0;
60542370681SJoe Hung }
60642370681SJoe Hung 
ilitek_suspend(struct device * dev)6074024f848SJonathan Cameron static int ilitek_suspend(struct device *dev)
60842370681SJoe Hung {
60942370681SJoe Hung 	struct i2c_client *client = to_i2c_client(dev);
61042370681SJoe Hung 	struct ilitek_ts_data *ts = i2c_get_clientdata(client);
61142370681SJoe Hung 	int error;
61242370681SJoe Hung 
61342370681SJoe Hung 	disable_irq(client->irq);
61442370681SJoe Hung 
61542370681SJoe Hung 	if (!device_may_wakeup(dev)) {
61642370681SJoe Hung 		error = api_protocol_set_cmd(ts, SET_IC_SLEEP, NULL, NULL);
61742370681SJoe Hung 		if (error)
61842370681SJoe Hung 			return error;
61942370681SJoe Hung 	}
62042370681SJoe Hung 
62142370681SJoe Hung 	return 0;
62242370681SJoe Hung }
62342370681SJoe Hung 
ilitek_resume(struct device * dev)6244024f848SJonathan Cameron static int ilitek_resume(struct device *dev)
62542370681SJoe Hung {
62642370681SJoe Hung 	struct i2c_client *client = to_i2c_client(dev);
62742370681SJoe Hung 	struct ilitek_ts_data *ts = i2c_get_clientdata(client);
62842370681SJoe Hung 	int error;
62942370681SJoe Hung 
63042370681SJoe Hung 	if (!device_may_wakeup(dev)) {
63142370681SJoe Hung 		error = api_protocol_set_cmd(ts, SET_IC_WAKE, NULL, NULL);
63242370681SJoe Hung 		if (error)
63342370681SJoe Hung 			return error;
63442370681SJoe Hung 
63542370681SJoe Hung 		ilitek_reset(ts, ts->reset_time);
63642370681SJoe Hung 	}
63742370681SJoe Hung 
63842370681SJoe Hung 	enable_irq(client->irq);
63942370681SJoe Hung 
64042370681SJoe Hung 	return 0;
64142370681SJoe Hung }
64242370681SJoe Hung 
6434024f848SJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(ilitek_pm_ops, ilitek_suspend, ilitek_resume);
64442370681SJoe Hung 
64542370681SJoe Hung static const struct i2c_device_id ilitek_ts_i2c_id[] = {
64642370681SJoe Hung 	{ ILITEK_TS_NAME, 0 },
64742370681SJoe Hung 	{ },
64842370681SJoe Hung };
64942370681SJoe Hung MODULE_DEVICE_TABLE(i2c, ilitek_ts_i2c_id);
65042370681SJoe Hung 
65142370681SJoe Hung #ifdef CONFIG_ACPI
65242370681SJoe Hung static const struct acpi_device_id ilitekts_acpi_id[] = {
65342370681SJoe Hung 	{ "ILTK0001", 0 },
65442370681SJoe Hung 	{ },
65542370681SJoe Hung };
65642370681SJoe Hung MODULE_DEVICE_TABLE(acpi, ilitekts_acpi_id);
65742370681SJoe Hung #endif
65842370681SJoe Hung 
65942370681SJoe Hung #ifdef CONFIG_OF
66042370681SJoe Hung static const struct of_device_id ilitek_ts_i2c_match[] = {
66142370681SJoe Hung 	{.compatible = "ilitek,ili2130",},
66242370681SJoe Hung 	{.compatible = "ilitek,ili2131",},
66342370681SJoe Hung 	{.compatible = "ilitek,ili2132",},
66442370681SJoe Hung 	{.compatible = "ilitek,ili2316",},
66542370681SJoe Hung 	{.compatible = "ilitek,ili2322",},
66642370681SJoe Hung 	{.compatible = "ilitek,ili2323",},
66742370681SJoe Hung 	{.compatible = "ilitek,ili2326",},
66842370681SJoe Hung 	{.compatible = "ilitek,ili2520",},
66942370681SJoe Hung 	{.compatible = "ilitek,ili2521",},
67042370681SJoe Hung 	{ },
67142370681SJoe Hung };
67242370681SJoe Hung MODULE_DEVICE_TABLE(of, ilitek_ts_i2c_match);
67342370681SJoe Hung #endif
67442370681SJoe Hung 
67542370681SJoe Hung static struct i2c_driver ilitek_ts_i2c_driver = {
67642370681SJoe Hung 	.driver = {
67742370681SJoe Hung 		.name = ILITEK_TS_NAME,
6784024f848SJonathan Cameron 		.pm = pm_sleep_ptr(&ilitek_pm_ops),
67942370681SJoe Hung 		.of_match_table = of_match_ptr(ilitek_ts_i2c_match),
68042370681SJoe Hung 		.acpi_match_table = ACPI_PTR(ilitekts_acpi_id),
68142370681SJoe Hung 	},
682*d8bde56dSUwe Kleine-König 	.probe = ilitek_ts_i2c_probe,
68342370681SJoe Hung 	.id_table = ilitek_ts_i2c_id,
68442370681SJoe Hung };
68542370681SJoe Hung module_i2c_driver(ilitek_ts_i2c_driver);
68642370681SJoe Hung 
68742370681SJoe Hung MODULE_AUTHOR("ILITEK");
68842370681SJoe Hung MODULE_DESCRIPTION("ILITEK I2C Touchscreen Driver");
68942370681SJoe Hung MODULE_LICENSE("GPL");
690