File Index
Files and directories
Sign in
FILE

quicki2c-protocol.c

File preview with syntax highlighting.

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