root/drivers/hid/hid-goodix-spi.c
// 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");