|  | // SPDX-License-Identifier: GPL-2.0-or-later | 
|  | /* | 
|  | * Goodix GT7986U SPI Driver Code for HID. | 
|  | * | 
|  | * Copyright (C) 2024 Godix, Inc. | 
|  | */ | 
|  | #include <linux/unaligned.h> | 
|  | #include <linux/delay.h> | 
|  | #include <linux/hid.h> | 
|  | #include <linux/interrupt.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/mutex.h> | 
|  | #include <linux/of.h> | 
|  | #include <linux/sizes.h> | 
|  | #include <linux/spi/spi.h> | 
|  |  | 
|  | #define GOODIX_DEV_CONFIRM_ADDR		0x10000 | 
|  | #define GOODIX_HID_DESC_ADDR		0x1058C | 
|  | #define GOODIX_HID_REPORT_DESC_ADDR	0x105AA | 
|  | #define GOODIX_HID_SIGN_ADDR		0x10D32 | 
|  | #define GOODIX_HID_CMD_ADDR		0x10364 | 
|  | #define GOODIX_HID_REPORT_ADDR		0x22C8C | 
|  |  | 
|  | #define GOODIX_HID_GET_REPORT_CMD	0x02 | 
|  | #define GOODIX_HID_SET_REPORT_CMD	0x03 | 
|  |  | 
|  | #define GOODIX_HID_MAX_INBUF_SIZE	128 | 
|  | #define GOODIX_HID_ACK_READY_FLAG	0x01 | 
|  | #define GOODIX_HID_REPORT_READY_FLAG	0x80 | 
|  |  | 
|  | #define GOODIX_DEV_CONFIRM_VAL		0xAA | 
|  |  | 
|  | #define GOODIX_SPI_WRITE_FLAG		0xF0 | 
|  | #define GOODIX_SPI_READ_FLAG		0xF1 | 
|  | #define GOODIX_SPI_TRANS_PREFIX_LEN	1 | 
|  | #define GOODIX_REGISTER_WIDTH		4 | 
|  | #define GOODIX_SPI_READ_DUMMY_LEN	3 | 
|  | #define GOODIX_SPI_READ_PREFIX_LEN	(GOODIX_SPI_TRANS_PREFIX_LEN + \ | 
|  | GOODIX_REGISTER_WIDTH + \ | 
|  | GOODIX_SPI_READ_DUMMY_LEN) | 
|  | #define GOODIX_SPI_WRITE_PREFIX_LEN	(GOODIX_SPI_TRANS_PREFIX_LEN + \ | 
|  | GOODIX_REGISTER_WIDTH) | 
|  |  | 
|  | #define GOODIX_CHECKSUM_SIZE		sizeof(u16) | 
|  | #define GOODIX_NORMAL_RESET_DELAY_MS	150 | 
|  |  | 
|  | struct goodix_hid_report_header { | 
|  | u8 flag; | 
|  | __le16 size; | 
|  | } __packed; | 
|  | #define GOODIX_HID_ACK_HEADER_SIZE	sizeof(struct goodix_hid_report_header) | 
|  |  | 
|  | struct goodix_hid_report_package { | 
|  | __le16 size; | 
|  | u8 data[]; | 
|  | }; | 
|  |  | 
|  | #define GOODIX_HID_PKG_LEN_SIZE		sizeof(u16) | 
|  | #define GOODIX_HID_COOR_DATA_LEN	82 | 
|  | #define GOODIX_HID_COOR_PKG_LEN		(GOODIX_HID_PKG_LEN_SIZE + \ | 
|  | GOODIX_HID_COOR_DATA_LEN) | 
|  |  | 
|  | /* power state */ | 
|  | #define GOODIX_SPI_POWER_ON		0x00 | 
|  | #define GOODIX_SPI_POWER_SLEEP		0x01 | 
|  |  | 
|  | /* flags used to record the current device operating state */ | 
|  | #define GOODIX_HID_STARTED		0 | 
|  |  | 
|  | struct goodix_hid_report_event { | 
|  | struct goodix_hid_report_header hdr; | 
|  | u8 data[GOODIX_HID_COOR_PKG_LEN]; | 
|  | } __packed; | 
|  |  | 
|  | struct goodix_hid_desc { | 
|  | __le16 desc_length; | 
|  | __le16 bcd_version; | 
|  | __le16 report_desc_length; | 
|  | __le16 report_desc_register; | 
|  | __le16 input_register; | 
|  | __le16 max_input_length; | 
|  | __le16 output_register; | 
|  | __le16 max_output_length; | 
|  | __le16 cmd_register; | 
|  | __le16 data_register; | 
|  | __le16 vendor_id; | 
|  | __le16 product_id; | 
|  | __le16 version_id; | 
|  | __le32 reserved; | 
|  | } __packed; | 
|  |  | 
|  | struct goodix_ts_data { | 
|  | struct device *dev; | 
|  | struct spi_device *spi; | 
|  | struct hid_device *hid; | 
|  | struct goodix_hid_desc hid_desc; | 
|  |  | 
|  | struct gpio_desc *reset_gpio; | 
|  | u32 hid_report_addr; | 
|  |  | 
|  | unsigned long flags; | 
|  | /* lock for hid raw request operation */ | 
|  | struct mutex hid_request_lock; | 
|  | /* buffer used to store hid report event */ | 
|  | u8 *event_buf; | 
|  | u32 hid_max_event_sz; | 
|  | /* buffer used to do spi data transfer */ | 
|  | u8 xfer_buf[SZ_2K] ____cacheline_aligned; | 
|  | }; | 
|  |  | 
|  | static void *goodix_get_event_report(struct goodix_ts_data *ts, u32 addr, | 
|  | u8 *data, size_t len) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(&ts->spi->dev); | 
|  | struct spi_transfer xfers; | 
|  | struct spi_message spi_msg; | 
|  | int error; | 
|  |  | 
|  | /* buffer format: 0xF1 + addr(4bytes) + dummy(3bytes) + data */ | 
|  | data[0] = GOODIX_SPI_READ_FLAG; | 
|  | put_unaligned_be32(addr, data + GOODIX_SPI_TRANS_PREFIX_LEN); | 
|  |  | 
|  | spi_message_init(&spi_msg); | 
|  | memset(&xfers, 0, sizeof(xfers)); | 
|  | xfers.tx_buf = data; | 
|  | xfers.rx_buf = data; | 
|  | xfers.len = GOODIX_SPI_READ_PREFIX_LEN + len; | 
|  | spi_message_add_tail(&xfers, &spi_msg); | 
|  |  | 
|  | error = spi_sync(spi, &spi_msg); | 
|  | if (error) { | 
|  | dev_err(ts->dev, "spi transfer error: %d", error); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | return data + GOODIX_SPI_READ_PREFIX_LEN; | 
|  | } | 
|  |  | 
|  | static int goodix_spi_read(struct goodix_ts_data *ts, u32 addr, | 
|  | void *data, size_t len) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(&ts->spi->dev); | 
|  | struct spi_transfer xfers; | 
|  | struct spi_message spi_msg; | 
|  | int error; | 
|  |  | 
|  | if (GOODIX_SPI_READ_PREFIX_LEN + len > sizeof(ts->xfer_buf)) { | 
|  | dev_err(ts->dev, "read data len exceed limit %zu", | 
|  | sizeof(ts->xfer_buf) - GOODIX_SPI_READ_PREFIX_LEN); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | /* buffer format: 0xF1 + addr(4bytes) + dummy(3bytes) + data */ | 
|  | ts->xfer_buf[0] = GOODIX_SPI_READ_FLAG; | 
|  | put_unaligned_be32(addr, ts->xfer_buf + GOODIX_SPI_TRANS_PREFIX_LEN); | 
|  |  | 
|  | spi_message_init(&spi_msg); | 
|  | memset(&xfers, 0, sizeof(xfers)); | 
|  | xfers.tx_buf = ts->xfer_buf; | 
|  | xfers.rx_buf = ts->xfer_buf; | 
|  | xfers.len = GOODIX_SPI_READ_PREFIX_LEN + len; | 
|  | spi_message_add_tail(&xfers, &spi_msg); | 
|  |  | 
|  | error = spi_sync(spi, &spi_msg); | 
|  | if (error) | 
|  | dev_err(ts->dev, "spi transfer error: %d", error); | 
|  | else | 
|  | memcpy(data, ts->xfer_buf + GOODIX_SPI_READ_PREFIX_LEN, len); | 
|  |  | 
|  | return error; | 
|  | } | 
|  |  | 
|  | static int goodix_spi_write(struct goodix_ts_data *ts, u32 addr, | 
|  | const void *data, size_t len) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(&ts->spi->dev); | 
|  | struct spi_transfer xfers; | 
|  | struct spi_message spi_msg; | 
|  | int error; | 
|  |  | 
|  | if (GOODIX_SPI_WRITE_PREFIX_LEN + len > sizeof(ts->xfer_buf)) { | 
|  | dev_err(ts->dev, "write data len exceed limit %zu", | 
|  | sizeof(ts->xfer_buf) - GOODIX_SPI_WRITE_PREFIX_LEN); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | /* buffer format: 0xF0 + addr(4bytes) + data */ | 
|  | ts->xfer_buf[0] = GOODIX_SPI_WRITE_FLAG; | 
|  | put_unaligned_be32(addr, ts->xfer_buf + GOODIX_SPI_TRANS_PREFIX_LEN); | 
|  | memcpy(ts->xfer_buf + GOODIX_SPI_WRITE_PREFIX_LEN, data, len); | 
|  |  | 
|  | spi_message_init(&spi_msg); | 
|  | memset(&xfers, 0, sizeof(xfers)); | 
|  | xfers.tx_buf = ts->xfer_buf; | 
|  | xfers.len = GOODIX_SPI_WRITE_PREFIX_LEN + len; | 
|  | spi_message_add_tail(&xfers, &spi_msg); | 
|  |  | 
|  | error = spi_sync(spi, &spi_msg); | 
|  | if (error) | 
|  | dev_err(ts->dev, "spi transfer error: %d", error); | 
|  |  | 
|  | return error; | 
|  | } | 
|  |  | 
|  | static int goodix_dev_confirm(struct goodix_ts_data *ts) | 
|  | { | 
|  | u8 tx_buf[8], rx_buf[8]; | 
|  | int retry = 3; | 
|  | int error; | 
|  |  | 
|  | gpiod_set_value_cansleep(ts->reset_gpio, 0); | 
|  | usleep_range(4000, 4100); | 
|  |  | 
|  | memset(tx_buf, GOODIX_DEV_CONFIRM_VAL, sizeof(tx_buf)); | 
|  | while (retry--) { | 
|  | error = goodix_spi_write(ts, GOODIX_DEV_CONFIRM_ADDR, | 
|  | tx_buf, sizeof(tx_buf)); | 
|  | if (error) | 
|  | return error; | 
|  |  | 
|  | error = goodix_spi_read(ts, GOODIX_DEV_CONFIRM_ADDR, | 
|  | rx_buf, sizeof(rx_buf)); | 
|  | if (error) | 
|  | return error; | 
|  |  | 
|  | if (!memcmp(tx_buf, rx_buf, sizeof(tx_buf))) | 
|  | return 0; | 
|  |  | 
|  | usleep_range(5000, 5100); | 
|  | } | 
|  |  | 
|  | dev_err(ts->dev, "device confirm failed, rx_buf: %*ph", 8, rx_buf); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * goodix_hid_parse() - hid-core .parse() callback | 
|  | * @hid: hid device instance | 
|  | * | 
|  | * This function gets called during call to hid_add_device | 
|  | * | 
|  | * Return: 0 on success and non zero on error | 
|  | */ | 
|  | static int goodix_hid_parse(struct hid_device *hid) | 
|  | { | 
|  | struct goodix_ts_data *ts = hid->driver_data; | 
|  | u16 rsize; | 
|  | int error; | 
|  |  | 
|  | rsize = le16_to_cpu(ts->hid_desc.report_desc_length); | 
|  | if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { | 
|  | dev_err(ts->dev, "invalid report desc size, %d", rsize); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | u8 *rdesc __free(kfree) = kzalloc(rsize, GFP_KERNEL); | 
|  | if (!rdesc) | 
|  | return -ENOMEM; | 
|  |  | 
|  | error = goodix_spi_read(ts, GOODIX_HID_REPORT_DESC_ADDR, rdesc, rsize); | 
|  | if (error) { | 
|  | dev_err(ts->dev, "failed get report desc, %d", error); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | error = hid_parse_report(hid, rdesc, rsize); | 
|  | if (error) { | 
|  | dev_err(ts->dev, "failed parse report, %d", error); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int goodix_hid_get_report_length(struct hid_report *report) | 
|  | { | 
|  | return ((report->size - 1) >> 3) + 1 + | 
|  | report->device->report_enum[report->type].numbered + 2; | 
|  | } | 
|  |  | 
|  | static void goodix_hid_find_max_report(struct hid_device *hid, unsigned int type, | 
|  | unsigned int *max) | 
|  | { | 
|  | struct hid_report *report; | 
|  | unsigned int size; | 
|  |  | 
|  | list_for_each_entry(report, &hid->report_enum[type].report_list, list) { | 
|  | size = goodix_hid_get_report_length(report); | 
|  | if (*max < size) | 
|  | *max = size; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int goodix_hid_start(struct hid_device *hid) | 
|  | { | 
|  | struct goodix_ts_data *ts = hid->driver_data; | 
|  | unsigned int bufsize = GOODIX_HID_COOR_PKG_LEN; | 
|  | u32 report_size; | 
|  |  | 
|  | goodix_hid_find_max_report(hid, HID_INPUT_REPORT, &bufsize); | 
|  | goodix_hid_find_max_report(hid, HID_OUTPUT_REPORT, &bufsize); | 
|  | goodix_hid_find_max_report(hid, HID_FEATURE_REPORT, &bufsize); | 
|  |  | 
|  | report_size = GOODIX_SPI_READ_PREFIX_LEN + | 
|  | GOODIX_HID_ACK_HEADER_SIZE + bufsize; | 
|  | if (report_size <= ts->hid_max_event_sz) | 
|  | return 0; | 
|  |  | 
|  | ts->event_buf = devm_krealloc(ts->dev, ts->event_buf, | 
|  | report_size, GFP_KERNEL); | 
|  | if (!ts->event_buf) | 
|  | return -ENOMEM; | 
|  |  | 
|  | ts->hid_max_event_sz = report_size; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void goodix_hid_stop(struct hid_device *hid) | 
|  | { | 
|  | hid->claimed = 0; | 
|  | } | 
|  |  | 
|  | static int goodix_hid_open(struct hid_device *hid) | 
|  | { | 
|  | struct goodix_ts_data *ts = hid->driver_data; | 
|  |  | 
|  | set_bit(GOODIX_HID_STARTED, &ts->flags); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void goodix_hid_close(struct hid_device *hid) | 
|  | { | 
|  | struct goodix_ts_data *ts = hid->driver_data; | 
|  |  | 
|  | clear_bit(GOODIX_HID_STARTED, &ts->flags); | 
|  | } | 
|  |  | 
|  | /* Return date length of response data */ | 
|  | static int goodix_hid_check_ack_status(struct goodix_ts_data *ts, u32 *resp_len) | 
|  | { | 
|  | struct goodix_hid_report_header hdr; | 
|  | int retry = 20; | 
|  | int error; | 
|  | int len; | 
|  |  | 
|  | while (retry--) { | 
|  | /* | 
|  | * 3 bytes of hid request response data | 
|  | * - byte 0:    Ack flag, value of 1 for data ready | 
|  | * - bytes 1-2: Response data length | 
|  | */ | 
|  | error = goodix_spi_read(ts, GOODIX_HID_CMD_ADDR, | 
|  | &hdr, sizeof(hdr)); | 
|  | if (!error && (hdr.flag & GOODIX_HID_ACK_READY_FLAG)) { | 
|  | len = le16_to_cpu(hdr.size); | 
|  | if (len < GOODIX_HID_PKG_LEN_SIZE) { | 
|  | dev_err(ts->dev, "hrd.size too short: %d", len); | 
|  | return -EINVAL; | 
|  | } | 
|  | *resp_len = len - GOODIX_HID_PKG_LEN_SIZE; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Wait 10ms for another try */ | 
|  | usleep_range(10000, 11000); | 
|  | } | 
|  |  | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * goodix_hid_get_raw_report() - Process hidraw GET REPORT operation | 
|  | * @hid: hid device instance | 
|  | * @reportnum: Report ID | 
|  | * @buf: Buffer for store the report date | 
|  | * @len: Length fo report data | 
|  | * @report_type: Report type | 
|  | * | 
|  | * The function for hid_ll_driver.get_raw_report to handle the HIDRAW ioctl | 
|  | * get report request. The transmitted data follows the standard i2c-hid | 
|  | * protocol with a specified header. | 
|  | * | 
|  | * Return: The length of the data in the buf on success, negative error code | 
|  | */ | 
|  | static int goodix_hid_get_raw_report(struct hid_device *hid, | 
|  | unsigned char reportnum, | 
|  | u8 *buf, size_t len, | 
|  | unsigned char report_type) | 
|  | { | 
|  | struct goodix_ts_data *ts = hid->driver_data; | 
|  | u16 data_register = le16_to_cpu(ts->hid_desc.data_register); | 
|  | u16 cmd_register = le16_to_cpu(ts->hid_desc.cmd_register); | 
|  | u8 tmp_buf[GOODIX_HID_MAX_INBUF_SIZE]; | 
|  | int tx_len = 0, args_len = 0; | 
|  | u32 response_data_len; | 
|  | u8 args[3]; | 
|  | int error; | 
|  |  | 
|  | if (report_type == HID_OUTPUT_REPORT) | 
|  | return -EINVAL; | 
|  |  | 
|  | if (reportnum == 3) { | 
|  | /* Get win8 signature data */ | 
|  | error = goodix_spi_read(ts, GOODIX_HID_SIGN_ADDR, buf, len); | 
|  | if (error) { | 
|  | dev_err(ts->dev, "failed get win8 sign: %d", error); | 
|  | return -EINVAL; | 
|  | } | 
|  | return len; | 
|  | } | 
|  |  | 
|  | if (reportnum >= 0x0F) | 
|  | args[args_len++] = reportnum; | 
|  |  | 
|  | put_unaligned_le16(data_register, args + args_len); | 
|  | args_len += sizeof(data_register); | 
|  |  | 
|  | /* Clean 3 bytes of hid ack header data */ | 
|  | memset(tmp_buf, 0, GOODIX_HID_ACK_HEADER_SIZE); | 
|  | tx_len += GOODIX_HID_ACK_HEADER_SIZE; | 
|  |  | 
|  | put_unaligned_le16(cmd_register, tmp_buf + tx_len); | 
|  | tx_len += sizeof(cmd_register); | 
|  |  | 
|  | tmp_buf[tx_len] = (report_type == HID_FEATURE_REPORT ? 0x03 : 0x01) << 4; | 
|  | tmp_buf[tx_len] |=  reportnum >= 0x0F ? 0x0F : reportnum; | 
|  | tx_len++; | 
|  |  | 
|  | tmp_buf[tx_len++] = GOODIX_HID_GET_REPORT_CMD; | 
|  |  | 
|  | memcpy(tmp_buf + tx_len, args, args_len); | 
|  | tx_len += args_len; | 
|  |  | 
|  | /* Step1: write report request info */ | 
|  | error = goodix_spi_write(ts, GOODIX_HID_CMD_ADDR, tmp_buf, tx_len); | 
|  | if (error) { | 
|  | dev_err(ts->dev, "failed send read feature cmd, %d", error); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | /* No need read response data */ | 
|  | if (!len) | 
|  | return 0; | 
|  |  | 
|  | /* Step2: check response data status */ | 
|  | error = goodix_hid_check_ack_status(ts, &response_data_len); | 
|  | if (error) | 
|  | return error; | 
|  |  | 
|  | /* Empty reprot response */ | 
|  | if (!response_data_len) | 
|  | return 0; | 
|  | len = min(len, response_data_len); | 
|  | /* Step3: read response data(skip 2bytes of hid pkg length) */ | 
|  | error = goodix_spi_read(ts, GOODIX_HID_CMD_ADDR + | 
|  | GOODIX_HID_ACK_HEADER_SIZE + | 
|  | GOODIX_HID_PKG_LEN_SIZE, buf, len); | 
|  | if (error) { | 
|  | dev_err(ts->dev, "failed read hid response data, %d", error); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | if (buf[0] != reportnum) { | 
|  | dev_err(ts->dev, "incorrect report (%d vs %d expected)", | 
|  | buf[0], reportnum); | 
|  | return -EINVAL; | 
|  | } | 
|  | return len; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * goodix_hid_set_raw_report() - process hidraw SET REPORT operation | 
|  | * @hid: HID device | 
|  | * @reportnum: Report ID | 
|  | * @buf: Buffer for communication | 
|  | * @len: Length of data in the buffer | 
|  | * @report_type: Report type | 
|  | * | 
|  | * The function for hid_ll_driver.get_raw_report to handle the HIDRAW ioctl | 
|  | * set report request. The transmitted data follows the standard i2c-hid | 
|  | * protocol with a specified header. | 
|  | * | 
|  | * Return: The length of the data sent, negative error code on failure | 
|  | */ | 
|  | static int goodix_hid_set_raw_report(struct hid_device *hid, | 
|  | unsigned char reportnum, | 
|  | __u8 *buf, size_t len, | 
|  | unsigned char report_type) | 
|  | { | 
|  | struct goodix_ts_data *ts = hid->driver_data; | 
|  | u16 data_register = le16_to_cpu(ts->hid_desc.data_register); | 
|  | u16 cmd_register = le16_to_cpu(ts->hid_desc.cmd_register); | 
|  | int tx_len = 0, args_len = 0; | 
|  | u8 tmp_buf[GOODIX_HID_MAX_INBUF_SIZE]; | 
|  | u8 args[5]; | 
|  | int error; | 
|  |  | 
|  | if (reportnum >= 0x0F) { | 
|  | args[args_len++] = reportnum; | 
|  | reportnum = 0x0F; | 
|  | } | 
|  |  | 
|  | put_unaligned_le16(data_register, args + args_len); | 
|  | args_len += sizeof(data_register); | 
|  |  | 
|  | put_unaligned_le16(GOODIX_HID_PKG_LEN_SIZE + len, args + args_len); | 
|  | args_len += GOODIX_HID_PKG_LEN_SIZE; | 
|  |  | 
|  | /* Clean 3 bytes of hid ack header data */ | 
|  | memset(tmp_buf, 0, GOODIX_HID_ACK_HEADER_SIZE); | 
|  | tx_len += GOODIX_HID_ACK_HEADER_SIZE; | 
|  |  | 
|  | put_unaligned_le16(cmd_register, tmp_buf + tx_len); | 
|  | tx_len += sizeof(cmd_register); | 
|  |  | 
|  | tmp_buf[tx_len++] = ((report_type == HID_FEATURE_REPORT ? 0x03 : 0x02) << 4) | reportnum; | 
|  | tmp_buf[tx_len++] = GOODIX_HID_SET_REPORT_CMD; | 
|  |  | 
|  | memcpy(tmp_buf + tx_len, args, args_len); | 
|  | tx_len += args_len; | 
|  |  | 
|  | memcpy(tmp_buf + tx_len, buf, len); | 
|  | tx_len += len; | 
|  |  | 
|  | error = goodix_spi_write(ts, GOODIX_HID_CMD_ADDR, tmp_buf, tx_len); | 
|  | if (error) { | 
|  | dev_err(ts->dev, "failed send report: %*ph", tx_len, tmp_buf); | 
|  | return error; | 
|  | } | 
|  | return len; | 
|  | } | 
|  |  | 
|  | static int goodix_hid_raw_request(struct hid_device *hid, | 
|  | unsigned char reportnum, | 
|  | __u8 *buf, size_t len, | 
|  | unsigned char rtype, int reqtype) | 
|  | { | 
|  | struct goodix_ts_data *ts = hid->driver_data; | 
|  | int error = -EINVAL; | 
|  |  | 
|  | guard(mutex)(&ts->hid_request_lock); | 
|  | switch (reqtype) { | 
|  | case HID_REQ_GET_REPORT: | 
|  | error = goodix_hid_get_raw_report(hid, reportnum, buf, | 
|  | len, rtype); | 
|  | break; | 
|  | case HID_REQ_SET_REPORT: | 
|  | if (buf[0] == reportnum) | 
|  | error = goodix_hid_set_raw_report(hid, reportnum, | 
|  | buf, len, rtype); | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | return error; | 
|  | } | 
|  |  | 
|  | static struct hid_ll_driver goodix_hid_ll_driver = { | 
|  | .parse = goodix_hid_parse, | 
|  | .start = goodix_hid_start, | 
|  | .stop = goodix_hid_stop, | 
|  | .open = goodix_hid_open, | 
|  | .close = goodix_hid_close, | 
|  | .raw_request = goodix_hid_raw_request | 
|  | }; | 
|  |  | 
|  | static irqreturn_t goodix_hid_irq(int irq, void *data) | 
|  | { | 
|  | struct goodix_ts_data *ts = data; | 
|  | struct goodix_hid_report_event *event; | 
|  | struct goodix_hid_report_package *pkg; | 
|  | u16 report_size; | 
|  |  | 
|  | if (!test_bit(GOODIX_HID_STARTED, &ts->flags)) | 
|  | return IRQ_HANDLED; | 
|  | /* | 
|  | * First, read buffer with space for header and coordinate package: | 
|  | * - event header = 3 bytes | 
|  | * - coordinate event = GOODIX_HID_COOR_PKG_LEN bytes | 
|  | * | 
|  | * If the data size info in the event header exceeds | 
|  | * GOODIX_HID_COOR_PKG_LEN, it means that there are other packages | 
|  | * besides the coordinate package. | 
|  | */ | 
|  | event = goodix_get_event_report(ts, ts->hid_report_addr, ts->event_buf, | 
|  | GOODIX_HID_ACK_HEADER_SIZE + | 
|  | GOODIX_HID_COOR_PKG_LEN); | 
|  | if (!event) { | 
|  | dev_err(ts->dev, "failed get coordinate data"); | 
|  | return IRQ_HANDLED; | 
|  | } | 
|  |  | 
|  | /* Check coordinate data valid falg */ | 
|  | if (event->hdr.flag != GOODIX_HID_REPORT_READY_FLAG) | 
|  | return IRQ_HANDLED; | 
|  |  | 
|  | pkg = (struct goodix_hid_report_package *)event->data; | 
|  | if (le16_to_cpu(pkg->size) < GOODIX_HID_PKG_LEN_SIZE) { | 
|  | dev_err(ts->dev, "invalid coordinate event package size, %d", | 
|  | le16_to_cpu(pkg->size)); | 
|  | return IRQ_HANDLED; | 
|  | } | 
|  | hid_input_report(ts->hid, HID_INPUT_REPORT, pkg->data, | 
|  | le16_to_cpu(pkg->size) - GOODIX_HID_PKG_LEN_SIZE, 1); | 
|  |  | 
|  | report_size = le16_to_cpu(event->hdr.size); | 
|  | /* Check if there are other packages */ | 
|  | if (report_size <= GOODIX_HID_COOR_PKG_LEN) | 
|  | return IRQ_HANDLED; | 
|  |  | 
|  | if (report_size >= ts->hid_max_event_sz) { | 
|  | dev_err(ts->dev, "package size exceed limit %d vs %d", | 
|  | report_size, ts->hid_max_event_sz); | 
|  | return IRQ_HANDLED; | 
|  | } | 
|  |  | 
|  | /* Read the package behind the coordinate data */ | 
|  | pkg = goodix_get_event_report(ts, ts->hid_report_addr + sizeof(*event), | 
|  | ts->event_buf, | 
|  | report_size - GOODIX_HID_COOR_PKG_LEN); | 
|  | if (!pkg) { | 
|  | dev_err(ts->dev, "failed read attachment data content"); | 
|  | return IRQ_HANDLED; | 
|  | } | 
|  |  | 
|  | hid_input_report(ts->hid, HID_INPUT_REPORT, pkg->data, | 
|  | le16_to_cpu(pkg->size) - GOODIX_HID_PKG_LEN_SIZE, 1); | 
|  |  | 
|  | return IRQ_HANDLED; | 
|  | } | 
|  |  | 
|  | static int goodix_hid_init(struct goodix_ts_data *ts) | 
|  | { | 
|  | struct hid_device *hid; | 
|  | int error; | 
|  |  | 
|  | /* Get hid descriptor */ | 
|  | error = goodix_spi_read(ts, GOODIX_HID_DESC_ADDR, &ts->hid_desc, | 
|  | sizeof(ts->hid_desc)); | 
|  | if (error) { | 
|  | dev_err(ts->dev, "failed get hid desc, %d", error); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | hid = hid_allocate_device(); | 
|  | if (IS_ERR(hid)) | 
|  | return PTR_ERR(hid); | 
|  |  | 
|  | hid->driver_data = ts; | 
|  | hid->ll_driver = &goodix_hid_ll_driver; | 
|  | hid->bus = BUS_SPI; | 
|  | hid->dev.parent = &ts->spi->dev; | 
|  |  | 
|  | hid->version = le16_to_cpu(ts->hid_desc.bcd_version); | 
|  | hid->vendor = le16_to_cpu(ts->hid_desc.vendor_id); | 
|  | hid->product = le16_to_cpu(ts->hid_desc.product_id); | 
|  | snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "hid-gdix", | 
|  | hid->vendor, hid->product); | 
|  |  | 
|  | error = hid_add_device(hid); | 
|  | if (error) { | 
|  | dev_err(ts->dev, "failed add hid device, %d", error); | 
|  | hid_destroy_device(hid); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | ts->hid = hid; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int goodix_spi_probe(struct spi_device *spi) | 
|  | { | 
|  | struct device *dev = &spi->dev; | 
|  | struct goodix_ts_data *ts; | 
|  | int error; | 
|  |  | 
|  | /* init spi_device */ | 
|  | spi->mode            = SPI_MODE_0; | 
|  | spi->bits_per_word   = 8; | 
|  | error = spi_setup(spi); | 
|  | if (error) | 
|  | return error; | 
|  |  | 
|  | ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); | 
|  | if (!ts) | 
|  | return -ENOMEM; | 
|  |  | 
|  | mutex_init(&ts->hid_request_lock); | 
|  | spi_set_drvdata(spi, ts); | 
|  | ts->spi = spi; | 
|  | ts->dev = dev; | 
|  | ts->hid_max_event_sz = GOODIX_SPI_READ_PREFIX_LEN + | 
|  | GOODIX_HID_ACK_HEADER_SIZE + GOODIX_HID_COOR_PKG_LEN; | 
|  | ts->event_buf = devm_kmalloc(dev, ts->hid_max_event_sz, GFP_KERNEL); | 
|  | if (!ts->event_buf) | 
|  | return -ENOMEM; | 
|  |  | 
|  | ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); | 
|  | if (IS_ERR(ts->reset_gpio)) | 
|  | return dev_err_probe(dev, PTR_ERR(ts->reset_gpio), | 
|  | "failed to request reset gpio\n"); | 
|  |  | 
|  | ts->hid_report_addr = GOODIX_HID_REPORT_ADDR; | 
|  | error = goodix_dev_confirm(ts); | 
|  | if (error) | 
|  | return error; | 
|  |  | 
|  | /* Waits 150ms for firmware to fully boot */ | 
|  | msleep(GOODIX_NORMAL_RESET_DELAY_MS); | 
|  |  | 
|  | error = goodix_hid_init(ts); | 
|  | if (error) { | 
|  | dev_err(dev, "failed init hid device"); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | error = devm_request_threaded_irq(&ts->spi->dev, ts->spi->irq, | 
|  | NULL, goodix_hid_irq, IRQF_ONESHOT, | 
|  | "goodix_spi_hid", ts); | 
|  | if (error) { | 
|  | dev_err(ts->dev, "could not register interrupt, irq = %d, %d", | 
|  | ts->spi->irq, error); | 
|  | goto err_destroy_hid; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | err_destroy_hid: | 
|  | hid_destroy_device(ts->hid); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | static void goodix_spi_remove(struct spi_device *spi) | 
|  | { | 
|  | struct goodix_ts_data *ts = spi_get_drvdata(spi); | 
|  |  | 
|  | disable_irq(spi->irq); | 
|  | hid_destroy_device(ts->hid); | 
|  | } | 
|  |  | 
|  | static int goodix_spi_set_power(struct goodix_ts_data *ts, int power_state) | 
|  | { | 
|  | u8 power_control_cmd[] = {0x00, 0x00, 0x00, 0x87, 0x02, 0x00, 0x08}; | 
|  | int error; | 
|  |  | 
|  | /* value 0 for power on, 1 for power sleep */ | 
|  | power_control_cmd[5] = power_state; | 
|  |  | 
|  | guard(mutex)(&ts->hid_request_lock); | 
|  | error = goodix_spi_write(ts, GOODIX_HID_CMD_ADDR, power_control_cmd, | 
|  | sizeof(power_control_cmd)); | 
|  | if (error) { | 
|  | dev_err(ts->dev, "failed set power mode: %s", | 
|  | power_state == GOODIX_SPI_POWER_ON ? "on" : "sleep"); | 
|  | return error; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int goodix_spi_suspend(struct device *dev) | 
|  | { | 
|  | struct goodix_ts_data *ts = dev_get_drvdata(dev); | 
|  |  | 
|  | disable_irq(ts->spi->irq); | 
|  | return goodix_spi_set_power(ts, GOODIX_SPI_POWER_SLEEP); | 
|  | } | 
|  |  | 
|  | static int goodix_spi_resume(struct device *dev) | 
|  | { | 
|  | struct goodix_ts_data *ts = dev_get_drvdata(dev); | 
|  |  | 
|  | enable_irq(ts->spi->irq); | 
|  | return goodix_spi_set_power(ts, GOODIX_SPI_POWER_ON); | 
|  | } | 
|  |  | 
|  | static DEFINE_SIMPLE_DEV_PM_OPS(goodix_spi_pm_ops, | 
|  | goodix_spi_suspend, goodix_spi_resume); | 
|  |  | 
|  | #ifdef CONFIG_ACPI | 
|  | static const struct acpi_device_id goodix_spi_acpi_match[] = { | 
|  | { "GXTS7986" }, | 
|  | { }, | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(acpi, goodix_spi_acpi_match); | 
|  | #endif | 
|  |  | 
|  | #ifdef CONFIG_OF | 
|  | static const struct of_device_id goodix_spi_of_match[] = { | 
|  | { .compatible = "goodix,gt7986u-spifw", }, | 
|  | { } | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(of, goodix_spi_of_match); | 
|  | #endif | 
|  |  | 
|  | static const struct spi_device_id goodix_spi_ids[] = { | 
|  | { "gt7986u" }, | 
|  | { }, | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(spi, goodix_spi_ids); | 
|  |  | 
|  | static struct spi_driver goodix_spi_driver = { | 
|  | .driver = { | 
|  | .name = "goodix-spi-hid", | 
|  | .acpi_match_table = ACPI_PTR(goodix_spi_acpi_match), | 
|  | .of_match_table = of_match_ptr(goodix_spi_of_match), | 
|  | .pm = pm_sleep_ptr(&goodix_spi_pm_ops), | 
|  | }, | 
|  | .probe =	goodix_spi_probe, | 
|  | .remove =	goodix_spi_remove, | 
|  | .id_table =	goodix_spi_ids, | 
|  | }; | 
|  | module_spi_driver(goodix_spi_driver); | 
|  |  | 
|  | MODULE_DESCRIPTION("Goodix SPI driver for HID touchscreen"); | 
|  | MODULE_AUTHOR("Goodix, Inc."); | 
|  | MODULE_LICENSE("GPL"); |