Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
16static 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
57static 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
77static 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
94int 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
99int 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
119int 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
129int 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
174int 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
198int 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
219int 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}