[RFC PATCH 5/6] HID: spi-hid: support the Romulus13 QSPI touchpad

From: fQwQf

Date: Sat Oct 10 2026 - 13:03:12 EST


Provide the verified Romulus opcodes, register addresses and power/reset
delays. Reset loses the heatmap mode selected by iptsd, even if the
report descriptor is unchanged. Recreate the HID device after the
descriptor refresh so hidraw consumers reconnect and initialize that
mode again.

Wait for the device descriptor handshake after a power-cycle, including
on Romulus, so a missing response still triggers the bounded recovery
worker and returns -ETIMEDOUT. Defer READY and the old HID resume callback
until re-probe on this device. Apply the same policy when rolling back
a failed suspend, retaining the original suspend error. Keep the v5
CRC-based refresh and resume behavior for other hardware.

Signed-off-by: Jizhou Tong <fqwqf@xxxxxxxxx>
---
drivers/hid/spi-hid/spi-hid-core.c | 19 +++++++++++++------
drivers/hid/spi-hid/spi-hid-of.c | 20 ++++++++++++++++++++
drivers/hid/spi-hid/spi-hid.h | 2 ++
3 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/drivers/hid/spi-hid/spi-hid-core.c b/drivers/hid/spi-hid/spi-hid-core.c
index 2778d147b202ab663928a851198eca8fe628b16a..bd27bc3ea34c5fa65a903dd204800dbdafe3a7bc 100644
--- a/drivers/hid/spi-hid/spi-hid-core.c
+++ b/drivers/hid/spi-hid/spi-hid-core.c
@@ -287,8 +287,9 @@ static const char *spi_hid_power_mode_string(enum hidspi_power_state power_state

/*
* Wait for the device descriptor response after reset; the device then takes
- * requests. Set READY here: the report descriptor check that normally sets it
- * needs power_lock, which callers hold.
+ * requests. Set READY here for devices that reuse their HID instance: the
+ * report descriptor check needs power_lock, which callers hold. Devices that
+ * rebind after reset become ready when their new HID instance is parsed.
*/
static int spi_hid_wait_for_reset(struct spi_hid *shid)
{
@@ -304,7 +305,8 @@ static int spi_hid_wait_for_reset(struct spi_hid *shid)
return -ETIMEDOUT;
}

- set_bit(SPI_HID_READY, &shid->flags);
+ if (!shid->conf->rebind_after_reset)
+ set_bit(SPI_HID_READY, &shid->flags);
return 0;
}

@@ -366,7 +368,8 @@ static int spi_hid_suspend(struct spi_hid *shid)
shid->ops->deassert_reset(shid->ops);
enable_irq(shid->spi->irq);
if (shid->hid) {
- if (!spi_hid_wait_for_reset(shid))
+ if (!spi_hid_wait_for_reset(shid) &&
+ !shid->conf->rebind_after_reset)
hid_driver_reset_resume(shid->hid);
}
return error;
@@ -431,6 +434,9 @@ static int spi_hid_resume(struct spi_hid *shid)
error = spi_hid_wait_for_reset(shid);
if (error)
return error;
+ /* Refresh will recreate the HID device after the handshake. */
+ if (shid->conf->rebind_after_reset)
+ return 0;
}
error = hid_driver_reset_resume(shid->hid);
if (error) {
@@ -916,8 +922,9 @@ static void spi_hid_refresh_device(struct spi_hid *shid)
(size_t)error);
}

- /* Same report descriptor, so no need to create a new hid device. */
- if (new_crc32 == shid->report_descriptor_crc32) {
+ /* Some devices lose userspace-selected modes even with the same CRC. */
+ if (new_crc32 == shid->report_descriptor_crc32 &&
+ !shid->conf->rebind_after_reset) {
set_bit(SPI_HID_READY, &shid->flags);
return;
}
diff --git a/drivers/hid/spi-hid/spi-hid-of.c b/drivers/hid/spi-hid/spi-hid-of.c
index 51bcb1a1b2a85480044c6514890f790e2930f523..3548c2cc450e84098dd5b5cfa6ce85d3ffd43b42 100644
--- a/drivers/hid/spi-hid/spi-hid-of.c
+++ b/drivers/hid/spi-hid/spi-hid-of.c
@@ -56,12 +56,29 @@ static const struct spi_hid_timing_data timing_data = {
.minimal_reset_delay_ms = 100,
};

+static const struct spi_hid_timing_data romulus_timing = {
+ .post_power_on_delay_ms = 1000,
+ .minimal_reset_delay_ms = 300,
+};
+
static int spi_hid_of_populate_config(struct spi_hid_of_config *conf,
struct device *dev)
{
int error;
u32 val;

+ if (device_is_compatible(dev, "microsoft,romulus13-touchpad")) {
+ conf->property_conf = (struct spi_hid_conf) {
+ .input_report_header_address = 0x1000,
+ .input_report_body_address = 0x1004,
+ .output_report_address = 0x2000,
+ .read_opcode = 0xeb,
+ .write_opcode = 0xe2,
+ .rebind_after_reset = true,
+ };
+ goto power;
+ }
+
error = device_property_read_u32(dev, "input-report-header-address",
&val);
if (error) {
@@ -98,6 +115,7 @@ static int spi_hid_of_populate_config(struct spi_hid_of_config *conf,
return -ENODEV;
}

+power:
conf->supply = devm_regulator_get(dev, "vdd");
if (IS_ERR(conf->supply))
return dev_err_probe(dev, PTR_ERR(conf->supply),
@@ -206,12 +224,14 @@ static int spi_hid_of_probe(struct spi_mem *mem)
}

static const struct of_device_id spi_hid_of_match[] = {
+ { .compatible = "microsoft,romulus13-touchpad", .data = &romulus_timing },
{ .compatible = "hid-over-spi", .data = &timing_data },
{}
};
MODULE_DEVICE_TABLE(of, spi_hid_of_match);

static const struct spi_device_id spi_hid_of_id_table[] = {
+ { "romulus13-touchpad", 0 },
{ "hid", 0 },
{ "hid-over-spi", 0 },
{ }
diff --git a/drivers/hid/spi-hid/spi-hid.h b/drivers/hid/spi-hid/spi-hid.h
index dd3a851777a22eefa9005dc6f7a18bf62c5ac6b3..ccbb4b9d3aeefa759a7a824ea28f3f9a5450b3b3 100644
--- a/drivers/hid/spi-hid/spi-hid.h
+++ b/drivers/hid/spi-hid/spi-hid.h
@@ -17,6 +17,8 @@ struct spi_hid_conf {
u32 output_report_address;
u8 read_opcode;
u8 write_opcode;
+ /* Reconnect hidraw clients when reset loses device-specific state. */
+ bool rebind_after_reset;
};

/**