[PATCH v9 1/5] Input: synaptics-rmi4 - handle duplicate/unknown PDT entries

From: David Heidelberg via B4 Relay

Date: Fri Jul 31 2026 - 16:43:26 EST


From: Casey Connolly <casey.connolly@xxxxxxxxxx>

Some third party rmi4-compatible ICs don't expose their PDT entries
very well. Add a few checks to skip duplicate entries as well as entries
for unsupported functions.

This is required to support some phones with third party displays.

Validated on a stock OnePlus 6T (original parts):
manufacturer: Synaptics, product: S3706B, fw id: 2852315

Co-developed-by: Kaustabh Chakraborty <kauschluss@xxxxxxxxxxx>
Signed-off-by: Kaustabh Chakraborty <kauschluss@xxxxxxxxxxx>
Signed-off-by: Casey Connolly <casey.connolly@xxxxxxxxxx>
Co-developed-by: David Heidelberg <david@xxxxxxx>
Signed-off-by: David Heidelberg <david@xxxxxxx>
---
drivers/input/rmi4/rmi_driver.c | 41 +++++++++++++++++++++++++++++++++++------
drivers/input/rmi4/rmi_driver.h | 7 +++++++
2 files changed, 42 insertions(+), 6 deletions(-)

diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index 5d49a9021c7d0..3edec5ae1f8c9 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -493,22 +493,48 @@ static void rmi_driver_copy_pdt_to_fd(const struct pdt_entry *pdt,
fd->command_base_addr = pdt->command_base_addr + pdt->page_start;
fd->control_base_addr = pdt->control_base_addr + pdt->page_start;
fd->data_base_addr = pdt->data_base_addr + pdt->page_start;
fd->function_number = pdt->function_number;
fd->interrupt_source_count = pdt->interrupt_source_count;
fd->function_version = pdt->function_version;
}

+static bool rmi_pdt_entry_is_valid(struct rmi_device *rmi_dev,
+ struct pdt_scan_state *state, u8 fn)
+{
+ switch (fn) {
+ case 0x01:
+ case 0x03:
+ case 0x11:
+ case 0x12:
+ case 0x30:
+ case 0x34:
+ case 0x3a:
+ case 0x54:
+ case 0x55:
+ if (state->pdts[fn] == true)
+ return false;
+ break;
+ default:
+ rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev,
+ "PDT has unknown function number %#02x\n", fn);
+ return false;
+ }
+
+ state->pdts[fn] = true;
+ return true;
+}
+
#define RMI_SCAN_CONTINUE 0
#define RMI_SCAN_DONE 1

static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
int page,
- int *empty_pages,
+ struct pdt_scan_state *state,
void *ctx,
int (*callback)(struct rmi_device *rmi_dev,
void *ctx,
const struct pdt_entry *entry))
{
struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
struct pdt_entry pdt_entry;
u16 page_start = RMI4_PAGE_SIZE * page;
@@ -521,44 +547,47 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
for (addr = pdt_start; addr >= pdt_end; addr -= RMI_PDT_ENTRY_SIZE) {
error = rmi_read_pdt_entry(rmi_dev, &pdt_entry, addr);
if (error)
return error;

if (RMI4_END_OF_PDT(pdt_entry.function_number))
break;

+ if (!rmi_pdt_entry_is_valid(rmi_dev, state, pdt_entry.function_number))
+ continue;
+
retval = callback(rmi_dev, ctx, &pdt_entry);
if (retval != RMI_SCAN_CONTINUE)
return retval;
}

/*
* Count number of empty PDT pages. If a gap of two pages
* or more is found, stop scanning.
*/
if (addr == pdt_start)
- ++*empty_pages;
+ ++state->empty_pages;
else
- *empty_pages = 0;
+ state->empty_pages = 0;

- return (data->bootloader_mode || *empty_pages >= 2) ?
+ return (data->bootloader_mode || state->empty_pages >= 2) ?
RMI_SCAN_DONE : RMI_SCAN_CONTINUE;
}

int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
int (*callback)(struct rmi_device *rmi_dev,
void *ctx, const struct pdt_entry *entry))
{
int page;
- int empty_pages = 0;
+ struct pdt_scan_state state = {0, 0, {0}};
int retval = RMI_SCAN_DONE;

for (page = 0; page <= RMI4_MAX_PAGE; page++) {
- retval = rmi_scan_pdt_page(rmi_dev, page, &empty_pages,
+ retval = rmi_scan_pdt_page(rmi_dev, page, &state,
ctx, callback);
if (retval != RMI_SCAN_CONTINUE)
break;
}

return retval < 0 ? retval : 0;
}

diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index abeafb77a4838..6de8faed08917 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -44,16 +44,23 @@ struct pdt_entry {
u8 interrupt_source_count;
u8 function_version;
u8 function_number;
};

#define RMI_REG_DESC_PRESENCE_BITS (32 * BITS_PER_BYTE)
#define RMI_REG_DESC_PRESENCE_REGS_MAX (3 + RMI_REG_DESC_PRESENCE_BITS / 8)
#define RMI_REG_DESC_SUBPACKET_BITS (37 * BITS_PER_BYTE)
+#define RMI_PDT_MAX 0x55
+
+struct pdt_scan_state {
+ u8 empty_pages;
+ bool pdts[RMI_PDT_MAX + 1];
+};
+

/* describes a single packet register */
struct rmi_register_desc_item {
u32 reg_size;
u16 reg;
u16 num_subpackets;
DECLARE_BITMAP(subpacket_map, RMI_REG_DESC_SUBPACKET_BITS);
};

--
2.53.0