Add quirks to support mode switch among Ilitek normal, debug and test mode and allow delay before send output reports. Add a shared variable to configure response timeout value for Ilitek touch controllers.
Signed-off-by: Jingyuan Liang <[email protected]> --- drivers/hid/spi-hid/spi-hid-core.c | 84 +++++++++++++++++++++++++++++++++++++- drivers/hid/spi-hid/spi-hid-core.h | 4 ++ drivers/hid/spi-hid/spi-hid.h | 6 +++ 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/drivers/hid/spi-hid/spi-hid-core.c b/drivers/hid/spi-hid/spi-hid-core.c index 893a0d4642d2..736e51f10cfc 100644 --- a/drivers/hid/spi-hid/spi-hid-core.c +++ b/drivers/hid/spi-hid/spi-hid-core.c @@ -22,6 +22,7 @@ #include <linux/completion.h> #include <linux/crc32.h> +#include <linux/delay.h> #include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/err.h> @@ -45,9 +46,14 @@ #include <linux/wait.h> #include <linux/workqueue.h> +#include "../hid-ids.h" #include "spi-hid.h" #include "spi-hid-core.h" +/* quirks to control the device */ +#define SPI_HID_QUIRK_MODE_SWITCH BIT(0) +#define SPI_HID_QUIRK_READ_DELAY BIT(1) + /* Protocol constants */ #define SPI_HID_READ_APPROVAL_CONSTANT 0xff #define SPI_HID_INPUT_HEADER_SYNC_BYTE 0x5a @@ -86,6 +92,16 @@ #define SPI_HID_CREATE_DEVICE 4 #define SPI_HID_ERROR 5 +static const struct spi_hid_quirks { + __u16 idVendor; + __u16 idProduct; + __u32 quirks; +} spi_hid_quirks[] = { + { USB_VENDOR_ID_ILITEK, HID_ANY_ID, + SPI_HID_QUIRK_MODE_SWITCH | SPI_HID_QUIRK_READ_DELAY }, + { 0, 0 } +}; + /* Processed data from input report header */ struct spi_hid_input_header { u8 version; @@ -112,6 +128,27 @@ struct spi_hid_output_report { static struct hid_ll_driver spi_hid_ll_driver; +/** + * spi_hid_lookup_quirk: return any quirks associated with a SPI HID device + * @idVendor: the 16-bit vendor ID + * @idProduct: the 16-bit product ID + * + * Returns: a u32 quirks value. + */ +static u32 spi_hid_lookup_quirk(const u16 idVendor, const u16 idProduct) +{ + u32 quirks = 0; + int n; + + for (n = 0; spi_hid_quirks[n].idVendor; n++) + if (spi_hid_quirks[n].idVendor == idVendor && + (spi_hid_quirks[n].idProduct == (__u16)HID_ANY_ID || + spi_hid_quirks[n].idProduct == idProduct)) + quirks = spi_hid_quirks[n].quirks; + + return quirks; +} + static void spi_hid_populate_read_approvals(const struct spi_hid_conf *conf, u8 *header_buf, u8 *body_buf) { @@ -382,6 +419,9 @@ static int spi_hid_send_output_report(struct spi_hid *shid, u8 padding; int error; + if (shid->quirks & SPI_HID_QUIRK_READ_DELAY) + usleep_range(2000, 2100); + guard(mutex)(&shid->output_lock); if (report->content_length > shid->desc.max_output_length) { dev_err(dev, "Output report too big, content_length 0x%x.", @@ -406,18 +446,38 @@ static int spi_hid_send_output_report(struct spi_hid *shid, return error; } +static const u32 spi_hid_get_timeout(struct spi_hid *shid) +{ + struct device *dev = &shid->spi->dev; + u32 timeout; + + timeout = READ_ONCE(shid->ops->response_timeout_ms); + + if (timeout < SPI_HID_RESP_TIMEOUT || timeout > 10000) { + dev_dbg(dev, "Response timeout is out of range, using default %d", + SPI_HID_RESP_TIMEOUT); + timeout = SPI_HID_RESP_TIMEOUT; + } + + return timeout; +} + static int spi_hid_sync_request(struct spi_hid *shid, struct spi_hid_output_report *report) { struct device *dev = &shid->spi->dev; + u32 timeout = SPI_HID_RESP_TIMEOUT; int error; error = spi_hid_send_output_report(shid, report); if (error) return error; + if (shid->quirks & SPI_HID_QUIRK_MODE_SWITCH) + timeout = spi_hid_get_timeout(shid); + error = wait_for_completion_interruptible_timeout(&shid->output_done, - msecs_to_jiffies(SPI_HID_RESP_TIMEOUT)); + msecs_to_jiffies(timeout)); if (error == 0) { dev_err(dev, "Response timed out."); return -ETIMEDOUT; @@ -561,6 +621,8 @@ static int spi_hid_create_device(struct spi_hid *shid) hid->vendor = shid->desc.vendor_id; hid->product = shid->desc.product_id; + shid->quirks = spi_hid_lookup_quirk(hid->vendor, hid->product); + snprintf(hid->name, sizeof(hid->name), "spi %04X:%04X", hid->vendor, hid->product); strscpy(hid->phys, dev_name(&shid->spi->dev), sizeof(hid->phys)); @@ -836,6 +898,24 @@ static irqreturn_t spi_hid_dev_irq(int irq, void *_shid) goto out; } + if (shid->quirks & SPI_HID_QUIRK_MODE_SWITCH) { + /* + * Update reset_pending on mode transitions inferred from + * response timeout (entering/exiting a mode). + */ + u32 timeout = spi_hid_get_timeout(shid); + bool mode_enabled = timeout > SPI_HID_RESP_TIMEOUT; + + if (mode_enabled != shid->prev_mode_enabled) { + if (mode_enabled) + set_bit(SPI_HID_RESET_PENDING, &shid->flags); + else + clear_bit(SPI_HID_RESET_PENDING, &shid->flags); + } + + shid->prev_mode_enabled = mode_enabled; + } + if (shid->input_message.status < 0) { dev_warn(dev, "Error reading header: %d.", shid->input_message.status); @@ -1190,6 +1270,8 @@ static int spi_hid_dev_init(struct spi_hid *shid) struct device *dev = &spi->dev; int error; + shid->ops->custom_init(shid->ops); + shid->ops->assert_reset(shid->ops); shid->ops->sleep_minimal_reset_delay(shid->ops); diff --git a/drivers/hid/spi-hid/spi-hid-core.h b/drivers/hid/spi-hid/spi-hid-core.h index 88e9020d37aa..8441dbad95d4 100644 --- a/drivers/hid/spi-hid/spi-hid-core.h +++ b/drivers/hid/spi-hid/spi-hid-core.h @@ -62,6 +62,10 @@ struct spi_hid { u16 response_length; u16 bufsize; + bool prev_mode_enabled; /* Previous device mode tracked for SPI_HID_QUIRK_MODE_SWITCH. */ + + unsigned long quirks; /* Various quirks. */ + enum hidspi_power_state power_state; u8 reset_attempts; /* The number of reset attempts. */ diff --git a/drivers/hid/spi-hid/spi-hid.h b/drivers/hid/spi-hid/spi-hid.h index 5651c7fb706a..3c0369bdb4ab 100644 --- a/drivers/hid/spi-hid/spi-hid.h +++ b/drivers/hid/spi-hid/spi-hid.h @@ -25,6 +25,9 @@ struct spi_hid_conf { * @power_down: do sequencing to power down the device * @assert_reset: do sequencing to assert the reset line * @deassert_reset: do sequencing to deassert the reset line + * @sleep_minimal_reset_delay: minimal sleep delay during reset + * @custom_init: customized device init + * @response_timeout_ms: output report response timeout in ms */ struct spihid_ops { int (*power_up)(struct spihid_ops *ops); @@ -32,6 +35,9 @@ struct spihid_ops { int (*assert_reset)(struct spihid_ops *ops); int (*deassert_reset)(struct spihid_ops *ops); void (*sleep_minimal_reset_delay)(struct spihid_ops *ops); + int (*custom_init)(struct spihid_ops *ops); + + u32 response_timeout_ms; }; int spi_hid_core_probe(struct spi_device *spi, struct spihid_ops *ops, -- 2.53.0.473.g4a7958ca14-goog
