From: Kavan Smith <kavansmith82@gmail.com>
To: dmitry.torokhov@gmail.com
Cc: michael.srba@seznam.cz, linux-input@vger.kernel.org,
linux-kernel@vger.kernel.org,
Kavan Smith <kavansmith82@gmail.com>
Subject: [PATCH] Input: zinitix - support the 6-byte/count touch report used by bt532
Date: Mon, 6 Jul 2026 11:08:25 -0700 [thread overview]
Message-ID: <20260706180826.408989-1-kavansmith82@gmail.com> (raw)
The driver assumes an 8-byte per-contact report stride with a per-finger
bitmask in report byte[2]. bt532 firmware (Samsung Galaxy Tab A 8.0,
msm8916-samsung-gt58) instead packs contacts at a 6-byte stride and reports
a finger COUNT in byte[2]. Under the 8-byte/bitmask assumption fingers 1+
are read misaligned, and a two-finger report (count 0x02) makes
for_each_set_bit() visit only slot 1, collapsing multitouch to a single
contact.
Add a per-compatible struct zinitix_chip_data that selects the report stride
and how byte[2] is interpreted, and attach it to the bt532 compatible (the
only in-tree user of it). All other compatibles keep the existing 8-byte,
bitmask behaviour unchanged. Contacts are parsed at the chip's stride, and
for count-style firmware every slot the chip marks with SUB_BIT_EXIST is
reported.
Signed-off-by: Kavan Smith <kavansmith82@gmail.com>
---
drivers/input/touchscreen/zinitix.c | 71 +++++++++++++++++++++++++++--
1 file changed, 58 insertions(+), 13 deletions(-)
diff --git a/drivers/input/touchscreen/zinitix.c b/drivers/input/touchscreen/zinitix.c
--- a/drivers/input/touchscreen/zinitix.c
+++ b/drivers/input/touchscreen/zinitix.c
@@ -135,26 +135,47 @@
#define CHIP_ON_DELAY 15 // ms
#define FIRMWARE_ON_DELAY 40 // ms
+/*
+ * Two Zinitix touch-report layouts exist in the wild. The default is an 8-byte
+ * per-contact stride with a per-finger bitmask in report byte[2]. Some firmware
+ * (e.g. bt532 on the Samsung Galaxy Tab A 8.0) packs contacts at a 6-byte stride
+ * and reports a finger COUNT in byte[2] instead of a bitmask. Selected per
+ * compatible via the of_device_id .data below.
+ */
+struct zinitix_chip_data {
+ unsigned int coord_stride; /* per-contact byte stride in the report */
+ bool finger_count; /* byte[2] is a finger count, not a bitmask */
+};
+
+static const struct zinitix_chip_data zinitix_bt532_data = {
+ .coord_stride = 6,
+ .finger_count = true,
+};
+
struct point_coord {
__le16 x;
__le16 y;
u8 width;
u8 sub_status;
- // currently unused, but needed as padding:
- u8 minor_width;
- u8 angle;
+ /*
+ * 8-byte-stride firmware appends two unused bytes here (minor_width,
+ * angle); the report is parsed at the chip's coord_stride, so on that
+ * firmware they are simply skipped rather than being part of this struct.
+ */
};
struct touch_event {
__le16 status;
u8 finger_mask;
u8 time_stamp;
- struct point_coord point_coord[MAX_SUPPORTED_FINGER_NUM];
+ /* raw per-contact bytes, parsed at the chip's coord_stride (max 8) */
+ u8 coords[MAX_SUPPORTED_FINGER_NUM * 8];
};
struct bt541_ts_data {
struct i2c_client *client;
struct input_dev *input_dev;
+ const struct zinitix_chip_data *chip;
struct touchscreen_properties prop;
struct regulator_bulk_data supplies[2];
u32 zinitix_mode;
@@ -444,6 +465,8 @@
{
struct bt541_ts_data *bt541 = bt541_handler;
struct i2c_client *client = bt541->client;
+ unsigned int stride = bt541->chip ? bt541->chip->coord_stride : 8;
+ bool finger_count = bt541->chip ? bt541->chip->finger_count : false;
struct touch_event touch_event;
unsigned long finger_mask;
__le16 icon_events;
@@ -452,8 +475,11 @@
memset(&touch_event, 0, sizeof(struct touch_event));
+ /* header (status, finger_mask, time_stamp) + one stride per contact */
error = zinitix_read_data(bt541->client, ZINITIX_POINT_STATUS_REG,
- &touch_event, sizeof(struct touch_event));
+ &touch_event,
+ offsetof(struct touch_event, coords) +
+ MAX_SUPPORTED_FINGER_NUM * stride);
if (error) {
dev_err(&client->dev, "Failed to read in touchpoint struct\n");
goto out;
@@ -470,13 +496,31 @@
zinitix_report_keys(bt541, le16_to_cpu(icon_events));
}
- finger_mask = touch_event.finger_mask;
- for_each_set_bit(i, &finger_mask, MAX_SUPPORTED_FINGER_NUM) {
- const struct point_coord *p = &touch_event.point_coord[i];
-
- /* Only process contacts that are actually reported */
- if (p->sub_status & SUB_BIT_EXIST)
- zinitix_report_finger(bt541, i, p);
+ if (finger_count) {
+ /*
+ * On this firmware byte[2] is a finger COUNT, not a bitmask: a
+ * 2-finger report carries 0x02, so for_each_set_bit() would look
+ * only at slot 1 and silently drop slot 0. The chip marks every
+ * populated contact with SUB_BIT_EXIST, so iterate all slots and
+ * report the present ones.
+ */
+ for (i = 0; i < MAX_SUPPORTED_FINGER_NUM; i++) {
+ const struct point_coord *p =
+ (const struct point_coord *)&touch_event.coords[i * stride];
+
+ if (p->sub_status & SUB_BIT_EXIST)
+ zinitix_report_finger(bt541, i, p);
+ }
+ } else {
+ /* default: byte[2] is a per-finger bitmask */
+ finger_mask = touch_event.finger_mask;
+ for_each_set_bit(i, &finger_mask, MAX_SUPPORTED_FINGER_NUM) {
+ const struct point_coord *p =
+ (const struct point_coord *)&touch_event.coords[i * stride];
+
+ if (p->sub_status & SUB_BIT_EXIST)
+ zinitix_report_finger(bt541, i, p);
+ }
}
input_mt_sync_frame(bt541->input_dev);
@@ -627,6 +671,7 @@
return -ENOMEM;
bt541->client = client;
+ bt541->chip = device_get_match_data(&client->dev);
i2c_set_clientdata(client, bt541);
error = zinitix_init_regulators(bt541);
@@ -741,7 +786,7 @@
{ .compatible = "zinitix,bt431" },
{ .compatible = "zinitix,bt432" },
{ .compatible = "zinitix,bt531" },
- { .compatible = "zinitix,bt532" },
+ { .compatible = "zinitix,bt532", .data = &zinitix_bt532_data },
{ .compatible = "zinitix,bt538" },
{ .compatible = "zinitix,bt541" },
{ .compatible = "zinitix,bt548" },
next reply other threads:[~2026-07-06 18:06 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-06 18:08 Kavan Smith [this message]
2026-07-06 18:16 ` [PATCH] Input: zinitix - support the 6-byte/count touch report used by bt532 sashiko-bot
2026-07-13 5:47 ` Nikita Travkin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260706180826.408989-1-kavansmith82@gmail.com \
--to=kavansmith82@gmail.com \
--cc=dmitry.torokhov@gmail.com \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=michael.srba@seznam.cz \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.