xref: /linux/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c (revision a788b2057029d831ee610c064bb607984a00367d)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /* Copyright (c) 2024 Intel Corporation */
3 
4 #include <linux/bitfield.h>
5 #include <linux/hid.h>
6 #include <linux/hid-over-i2c.h>
7 #include <linux/unaligned.h>
8 
9 #include "intel-thc-dev.h"
10 #include "intel-thc-dma.h"
11 
12 #include "quicki2c-dev.h"
13 #include "quicki2c-hid.h"
14 #include "quicki2c-protocol.h"
15 
16 static ssize_t quicki2c_init_write_buf(struct quicki2c_device *qcdev, u32 cmd, size_t cmd_len,
17 				       bool append_data_reg, u8 *data, size_t data_len,
18 				       u8 *write_buf, size_t write_buf_len)
19 {
20 	size_t buf_len, offset = 0;
21 
22 	buf_len = HIDI2C_REG_LEN + cmd_len;
23 
24 	if (append_data_reg)
25 		buf_len += HIDI2C_REG_LEN;
26 
27 	if (data && data_len)
28 		buf_len += data_len + HIDI2C_LENGTH_LEN;
29 
30 	if (buf_len > write_buf_len)
31 		return -EINVAL;
32 
33 	if (cmd_len) {
34 		memcpy(write_buf, &qcdev->dev_desc.cmd_reg, HIDI2C_REG_LEN);
35 		offset += HIDI2C_REG_LEN;
36 		memcpy(write_buf + offset, &cmd, cmd_len);
37 		offset += cmd_len;
38 
39 		if (append_data_reg) {
40 			memcpy(write_buf + offset, &qcdev->dev_desc.data_reg, HIDI2C_REG_LEN);
41 			offset += HIDI2C_REG_LEN;
42 		}
43 	} else {
44 		memcpy(write_buf, &qcdev->dev_desc.output_reg, HIDI2C_REG_LEN);
45 		offset += HIDI2C_REG_LEN;
46 	}
47 
48 	if (data && data_len) {
49 		put_unaligned_le16(data_len + HIDI2C_LENGTH_LEN, write_buf + offset);
50 		offset += HIDI2C_LENGTH_LEN;
51 		memcpy(write_buf + offset, data, data_len);
52 	}
53 
54 	return buf_len;
55 }
56 
57 static size_t quicki2c_encode_cmd(struct quicki2c_device *qcdev, u32 *cmd_buf,
58 				  u8 opcode, u8 report_type, u8 report_id)
59 {
60 	size_t cmd_len;
61 
62 	*cmd_buf = FIELD_PREP(HIDI2C_CMD_OPCODE, opcode) |
63 		   FIELD_PREP(HIDI2C_CMD_REPORT_TYPE, report_type);
64 
65 	if (report_id < HIDI2C_CMD_MAX_RI) {
66 		*cmd_buf |= FIELD_PREP(HIDI2C_CMD_REPORT_ID, report_id);
67 		cmd_len = HIDI2C_CMD_LEN;
68 	} else {
69 		*cmd_buf |= FIELD_PREP(HIDI2C_CMD_REPORT_ID, HIDI2C_CMD_MAX_RI) |
70 			    FIELD_PREP(HIDI2C_CMD_3RD_BYTE, report_id);
71 		cmd_len = HIDI2C_CMD_LEN_OPT;
72 	}
73 
74 	return cmd_len;
75 }
76 
77 static int write_cmd_to_txdma(struct quicki2c_device *qcdev, int opcode,
78 			      int report_type, int report_id, u8 *buf, size_t buf_len)
79 {
80 	size_t cmd_len;
81 	ssize_t len;
82 	u32 cmd;
83 
84 	cmd_len = quicki2c_encode_cmd(qcdev, &cmd, opcode, report_type, report_id);
85 
86 	len = quicki2c_init_write_buf(qcdev, cmd, cmd_len, buf ? true : false, buf,
87 				      buf_len, qcdev->report_buf, qcdev->report_len);
88 	if (len < 0)
89 		return len;
90 
91 	return thc_dma_write(qcdev->thc_hw, qcdev->report_buf, len);
92 }
93 
94 int quicki2c_set_power(struct quicki2c_device *qcdev, enum hidi2c_power_state power_state)
95 {
96 	return write_cmd_to_txdma(qcdev, HIDI2C_SET_POWER, HIDI2C_RESERVED, power_state, NULL, 0);
97 }
98 
99 int quicki2c_get_device_descriptor(struct quicki2c_device *qcdev)
100 {
101 	u32 read_len = 0;
102 	int ret;
103 
104 	ret = thc_tic_pio_write_and_read(qcdev->thc_hw, qcdev->hid_desc_addr,
105 					 HIDI2C_REG_LEN, NULL, HIDI2C_DEV_DESC_LEN,
106 					 &read_len, (u32 *)&qcdev->dev_desc);
107 	if (ret || HIDI2C_DEV_DESC_LEN != read_len) {
108 		dev_err_once(qcdev->dev, "Get device descriptor failed, ret %d, read len %u\n",
109 			     ret, read_len);
110 		return -EIO;
111 	}
112 
113 	if (le16_to_cpu(qcdev->dev_desc.bcd_ver) != HIDI2C_HID_DESC_BCDVERSION)
114 		return -EOPNOTSUPP;
115 
116 	return 0;
117 }
118 
119 int quicki2c_get_report_descriptor(struct quicki2c_device *qcdev)
120 {
121 	u16 desc_reg = le16_to_cpu(qcdev->dev_desc.report_desc_reg);
122 	size_t read_len = le16_to_cpu(qcdev->dev_desc.report_desc_len);
123 	u32 prd_len = read_len;
124 
125 	return thc_swdma_read(qcdev->thc_hw, (u8 *)&desc_reg, HIDI2C_REG_LEN,
126 			      &prd_len, qcdev->report_descriptor, &read_len);
127 }
128 
129 int quicki2c_get_report(struct quicki2c_device *qcdev, u8 report_type,
130 			unsigned int reportnum, void *buf, size_t buf_len)
131 {
132 	struct hidi2c_report_packet *rpt;
133 	size_t cmd_len, read_len = 0;
134 	int rep_type, ret;
135 	ssize_t len;
136 	u32 cmd;
137 
138 	if (report_type == HID_INPUT_REPORT) {
139 		rep_type = HIDI2C_INPUT;
140 	} else if (report_type == HID_FEATURE_REPORT) {
141 		rep_type = HIDI2C_FEATURE;
142 	} else {
143 		dev_err(qcdev->dev, "Unsupported report type for GET REPORT: %d\n", report_type);
144 		return -EINVAL;
145 	}
146 
147 	cmd_len = quicki2c_encode_cmd(qcdev, &cmd, HIDI2C_GET_REPORT, rep_type, reportnum);
148 
149 	len = quicki2c_init_write_buf(qcdev, cmd, cmd_len, true, NULL, 0,
150 				      qcdev->report_buf, qcdev->report_len);
151 	if (len < 0)
152 		return len;
153 
154 	rpt = (struct hidi2c_report_packet *)qcdev->input_buf;
155 
156 	ret = thc_swdma_read(qcdev->thc_hw, qcdev->report_buf, len, NULL, rpt, &read_len);
157 	if (ret) {
158 		dev_err_once(qcdev->dev, "Get report failed, ret %d, read len (%zu vs %zu)\n",
159 			     ret, read_len, buf_len);
160 		return ret;
161 	}
162 
163 	if (HIDI2C_DATA_LEN(le16_to_cpu(rpt->len)) != buf_len || rpt->data[0] != reportnum) {
164 		dev_err_once(qcdev->dev, "Invalid packet, len (%d vs %zu) report id (%d vs %d)\n",
165 			     le16_to_cpu(rpt->len), buf_len, rpt->data[0], reportnum);
166 		return -EINVAL;
167 	}
168 
169 	memcpy(buf, rpt->data, buf_len);
170 
171 	return buf_len;
172 }
173 
174 int quicki2c_set_report(struct quicki2c_device *qcdev, u8 report_type,
175 			unsigned int reportnum, void *buf, size_t buf_len)
176 {
177 	int rep_type;
178 	int ret;
179 
180 	if (report_type == HID_OUTPUT_REPORT) {
181 		rep_type = HIDI2C_OUTPUT;
182 	} else if (report_type == HID_FEATURE_REPORT) {
183 		rep_type = HIDI2C_FEATURE;
184 	} else {
185 		dev_err(qcdev->dev, "Unsupported report type for SET REPORT: %d\n", report_type);
186 		return -EINVAL;
187 	}
188 
189 	ret = write_cmd_to_txdma(qcdev, HIDI2C_SET_REPORT, rep_type, reportnum, buf, buf_len);
190 	if (ret) {
191 		dev_err_once(qcdev->dev, "Set Report failed, ret %d\n", ret);
192 		return ret;
193 	}
194 
195 	return buf_len;
196 }
197 
198 int quicki2c_output_report(struct quicki2c_device *qcdev, void *buf, size_t buf_len)
199 {
200 	ssize_t len;
201 	int ret;
202 
203 	len = quicki2c_init_write_buf(qcdev, 0, 0, false, buf, buf_len,
204 				      qcdev->report_buf, qcdev->report_len);
205 	if (len < 0)
206 		return -EINVAL;
207 
208 	ret = thc_dma_write(qcdev->thc_hw, qcdev->report_buf, len);
209 	if (ret) {
210 		dev_err(qcdev->dev, "Output Report failed, ret %d\n", ret);
211 		return ret;
212 	}
213 
214 	return buf_len;
215 }
216 
217 #define HIDI2C_RESET_TIMEOUT		5
218 
219 int quicki2c_reset(struct quicki2c_device *qcdev)
220 {
221 	u16 input_reg = le16_to_cpu(qcdev->dev_desc.input_reg);
222 	size_t read_len = HIDI2C_LENGTH_LEN;
223 	u32 prd_len = read_len;
224 	int ret;
225 
226 	qcdev->reset_ack = false;
227 	qcdev->state = QUICKI2C_RESETING;
228 
229 	ret = write_cmd_to_txdma(qcdev, HIDI2C_RESET, HIDI2C_RESERVED, 0, NULL, 0);
230 	if (ret) {
231 		dev_err_once(qcdev->dev, "Send reset command failed, ret %d\n", ret);
232 		return ret;
233 	}
234 
235 	ret = wait_event_interruptible_timeout(qcdev->reset_ack_wq, qcdev->reset_ack,
236 					       HIDI2C_RESET_TIMEOUT * HZ);
237 	if (qcdev->reset_ack)
238 		return 0;
239 
240 	/*
241 	 * Manually read reset response if it wasn't received, in case reset interrupt
242 	 * was missed by touch device or THC hardware.
243 	 */
244 	ret = thc_tic_pio_read(qcdev->thc_hw, input_reg, read_len, &prd_len,
245 			       (u32 *)qcdev->input_buf);
246 	if (ret) {
247 		dev_err_once(qcdev->dev, "Read Reset Response failed, ret %d\n", ret);
248 		return ret;
249 	}
250 
251 	/*
252 	 * Check response packet length, it's first 16 bits of packet.
253 	 * If response packet length is zero, it's reset response, otherwise not.
254 	 */
255 	if (get_unaligned_le16(qcdev->input_buf)) {
256 		dev_err_once(qcdev->dev,
257 			     "Wait reset response timed out ret:%d timeout:%ds\n",
258 			     ret, HIDI2C_RESET_TIMEOUT);
259 		return -ETIMEDOUT;
260 	}
261 
262 	qcdev->reset_ack = true;
263 
264 	return 0;
265 }
266