kernel: modules: package driver for Hynitron CST816x touch sensor

Package kernel driver for the Hynitron CST816x touch sensor, found on
ST7789V-based 240x240 px MIPI-DBI display of the Adtran SmartRG SDG-9000.
Import pending patches improving the driver.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
This commit is contained in:
Daniel Golle committed 2026-10-07 00:03:51 +01:00
1 parent dc7acc1888
commit 2bba9c1ba5
6 files changed
+472

No files matched your search

+19
View File
@@ -262,6 +262,25 @@ endef
$(eval $(call KernelPackage,input-touchscreen-edt-ft5x06))
define KernelPackage/input-touchscreen-hynitron-cst816x
SUBMENU:=$(INPUT_MODULES_MENU)
TITLE:=Hynitron CST816X touchscreen
DEPENDS:=@!LINUX_6_12 +kmod-i2c-core +kmod-input-core
KCONFIG:= \
CONFIG_INPUT_TOUCHSCREEN=y \
CONFIG_TOUCHSCREEN_HYNITRON_CST816X
FILES:=$(LINUX_DIR)/drivers/input/touchscreen/hynitron-cst816x.ko
AUTOLOAD:=$(call AutoProbe,hynitron-cst816x)
endef
define KernelPackage/input-touchscreen-hynitron-cst816x/description
Kernel module for Hynitron CST816X touchscreen controller
used for 240×240 1.28-inch Round LCD Display Module.
endef
$(eval $(call KernelPackage,input-touchscreen-hynitron-cst816x))
define KernelPackage/keyboard-imx
SUBMENU:=$(INPUT_MODULES_MENU)
TITLE:=IMX keypad support
@@ -0,0 +1,49 @@
From 614a8ac3f9e568d6ccf4787f48b894ba8ce4b584 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Thu, 1 Oct 2026 01:57:03 +0100
Subject: [PATCH v4 1/5] dt-bindings: input: touchscreen: hynitron,cst816x: axes
and panel
The chip hands out coordinates in its own frame and knows nothing of
the panel it is glued to, so how it is oriented against that panel,
which coordinate range the assembly reports and how noisy it is are
properties of the board. Reference touchscreen.yaml and let its
properties through, among them the 'panel' phandle naming the panel a
controller is mounted on, which asks for the two to be power sequenced
together.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v3:
- describe the whole of touchscreen.yaml with unevaluatedProperties
(Krzysztof Kozlowski)
v2:
- allow the min, size and fuzz properties as well (Sashiko AI review)
- allow the 'panel' phandle
- justify the change in the commit message (Conor Dooley)
---
.../bindings/input/touchscreen/hynitron,cst816x.yaml | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
--- a/Documentation/devicetree/bindings/input/touchscreen/hynitron,cst816x.yaml
+++ b/Documentation/devicetree/bindings/input/touchscreen/hynitron,cst816x.yaml
@@ -41,7 +41,10 @@ required:
- reg
- interrupts
-additionalProperties: false
+unevaluatedProperties: false
+
+allOf:
+ - $ref: touchscreen.yaml#
examples:
- |
@@ -59,6 +62,7 @@ examples:
reset-gpios = <&gpio 17 GPIO_ACTIVE_LOW>;
linux,keycodes = <KEY_UP>, <KEY_DOWN>, <KEY_LEFT>, <KEY_RIGHT>,
<BTN_TOOL_TRIPLETAP>;
+ touchscreen-swapped-x-y;
};
};
@@ -0,0 +1,58 @@
From 9e3cca6e6f43c886c58d9258fca4ad19cff066b4 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Thu, 1 Oct 2026 01:01:51 +0100
Subject: [PATCH v4 2/5] Input: hynitron-cst816x: respect touchscreen DT
properties
Parse common touchscreen properties and use touchscreen_report_pos()
instead of reporting raw absolute position.
This makes the driver respect the touchscreen-min-x/y,
touchscreen-size-x/y, touchscreen-fuzz-x/y, touchscreen-inverted-x/y
and touchscreen-swapped-x-y properties.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v2:
- name all the properties the parser honours, no functional change
drivers/input/touchscreen/hynitron-cst816x.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
--- a/drivers/input/touchscreen/hynitron-cst816x.c
+++ b/drivers/input/touchscreen/hynitron-cst816x.c
@@ -11,6 +11,7 @@
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/input.h>
+#include <linux/input/touchscreen.h>
#include <linux/unaligned.h>
#include <linux/interrupt.h>
#include <linux/module.h>
@@ -31,6 +32,7 @@ struct cst816x_priv {
struct input_dev *input;
unsigned int keycode[CST816X_NUM_KEYS];
unsigned int keycodemax;
+ struct touchscreen_properties prop;
};
static int cst816x_parse_keycodes(struct device *dev, struct cst816x_priv *priv)
@@ -142,6 +144,7 @@ static int cst816x_register_input(struct
input_set_abs_params(priv->input, ABS_X, 0, 240, 0, 0);
input_set_abs_params(priv->input, ABS_Y, 0, 240, 0, 0);
input_set_capability(priv->input, EV_KEY, BTN_TOUCH);
+ touchscreen_parse_properties(priv->input, false, &priv->prop);
priv->input->keycode = priv->keycode;
priv->input->keycodesize = sizeof(priv->keycode[0]);
@@ -173,8 +176,8 @@ static irqreturn_t cst816x_irq_cb(int ir
if (!cst816x_process_touch(priv, &tch))
return IRQ_HANDLED;
- input_report_abs(priv->input, ABS_X, tch.abs_x);
- input_report_abs(priv->input, ABS_Y, tch.abs_y);
+ touchscreen_report_pos(priv->input, &priv->prop,
+ tch.abs_x, tch.abs_y, false);
if (tch.gest)
input_report_key(priv->input,
@@ -0,0 +1,163 @@
From 5312d67200e3251b2be08aef0a726d50d94fadde Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Mon, 5 Oct 2026 20:22:23 +0100
Subject: [PATCH v4 3/5] Input: hynitron-cst816x: release gesture keys
The gesture key is reported with the value of the touch flag, so it
stays pressed once the controller stops reporting the gesture code
while the finger is still down. Report the press on its own and
release the keys the input core still holds down on the first report
that carries no gesture code. Report a key only for the gesture codes
the binding names: the controller also sends codes such as single
click, and the index fallback aliased those onto the long-press
keycode. Keycodes above KEY_MAX are rejected, as that release looks
the key state up in the input core's key bitmap.
Fixes: c87a819bec86 ("Input: add driver for Hynitron CST816x series")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v4:
- map only the documented gesture codes to keys, as single click
aliased onto the long-press keycode (Oleh Kuzhylnyi)
- read each keycode once in the release loop, so the key it tests is
the key it releases (Sashiko AI review)
v2:
- reject keycodes above KEY_MAX and publish keycodemax only once the
whole array validates (Sashiko AI review)
drivers/input/touchscreen/hynitron-cst816x.c | 87 +++++++++++++-------
1 file changed, 55 insertions(+), 32 deletions(-)
--- a/drivers/input/touchscreen/hynitron-cst816x.c
+++ b/drivers/input/touchscreen/hynitron-cst816x.c
@@ -39,28 +39,40 @@ static int cst816x_parse_keycodes(struct
{
int count;
int error;
+ int i;
- if (device_property_present(dev, "linux,keycodes")) {
- count = device_property_count_u32(dev, "linux,keycodes");
- if (count < 0) {
- error = count;
- dev_err(dev, "failed to count keys: %d\n", error);
- return error;
- } else if (count > ARRAY_SIZE(priv->keycode)) {
- dev_err(dev, "too many keys defined: %d\n", count);
- return -EINVAL;
- }
- priv->keycodemax = count;
-
- error = device_property_read_u32_array(dev, "linux,keycodes",
- priv->keycode,
- priv->keycodemax);
- if (error) {
- dev_err(dev, "failed to read keycodes: %d\n", error);
- return error;
- }
+ if (!device_property_present(dev, "linux,keycodes"))
+ return 0;
+
+ count = device_property_count_u32(dev, "linux,keycodes");
+ if (count < 0) {
+ error = count;
+ dev_err(dev, "failed to count keys: %d\n", error);
+ return error;
+ }
+
+ if (count > ARRAY_SIZE(priv->keycode)) {
+ dev_err(dev, "too many keys defined: %d\n", count);
+ return -EINVAL;
+ }
+
+ error = device_property_read_u32_array(dev, "linux,keycodes",
+ priv->keycode, count);
+ if (error) {
+ dev_err(dev, "failed to read keycodes: %d\n", error);
+ return error;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (priv->keycode[i] <= KEY_MAX)
+ continue;
+
+ dev_err(dev, "keycode %u out of range\n", priv->keycode[i]);
+ return -EINVAL;
}
+ priv->keycodemax = count;
+
return 0;
}
@@ -94,24 +106,19 @@ static int cst816x_i2c_read_register(str
return 0;
}
-static u8 cst816x_gest_idx(u8 gest)
+static unsigned int cst816x_gest_keycode(struct cst816x_priv *priv, u8 gest)
{
- u8 index;
-
switch (gest) {
case 0x01: /* Slide up gesture */
case 0x02: /* Slide down gesture */
case 0x03: /* Slide left gesture */
case 0x04: /* Slide right gesture */
- index = gest;
- break;
+ return priv->keycode[gest - 1];
case 0x0c: /* Long press gesture */
+ return priv->keycode[CST816X_NUM_KEYS - 1];
default:
- index = CST816X_NUM_KEYS;
- break;
+ return KEY_RESERVED;
}
-
- return index - 1;
}
static bool cst816x_process_touch(struct cst816x_priv *priv,
@@ -168,10 +175,25 @@ static void cst816x_reset(struct cst816x
msleep(100);
}
+static void cst816x_release_keys(struct cst816x_priv *priv)
+{
+ unsigned int key;
+ unsigned int i;
+
+ for (i = 0; i < priv->keycodemax; i++) {
+ key = priv->keycode[i];
+ if (!test_bit(key, priv->input->key))
+ continue;
+
+ input_report_key(priv->input, key, 0);
+ }
+}
+
static irqreturn_t cst816x_irq_cb(int irq, void *cookie)
{
struct cst816x_priv *priv = cookie;
struct cst816x_touch tch;
+ unsigned int key;
if (!cst816x_process_touch(priv, &tch))
return IRQ_HANDLED;
@@ -179,10 +201,11 @@ static irqreturn_t cst816x_irq_cb(int ir
touchscreen_report_pos(priv->input, &priv->prop,
tch.abs_x, tch.abs_y, false);
- if (tch.gest)
- input_report_key(priv->input,
- priv->keycode[cst816x_gest_idx(tch.gest)],
- tch.active);
+ key = cst816x_gest_keycode(priv, tch.gest);
+ if (key != KEY_RESERVED)
+ input_report_key(priv->input, key, 1);
+ else
+ cst816x_release_keys(priv);
input_report_key(priv->input, BTN_TOUCH, tch.active);
@@ -0,0 +1,154 @@
From d73d26d8c9d75fa230e3dd64058acb65f45da64d Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Mon, 5 Oct 2026 20:22:23 +0100
Subject: [PATCH v4 4/5] Input: hynitron-cst816x: time out gesture key release
The controller reports a gesture code once and can then stay quiet
until the next touch, which leaves the gesture key pressed for as long
as the panel is left alone. Release the gesture keys 40 ms after the
last report that carried a gesture code, so that the release comes
from whichever happens first, the timeout or a report without a
gesture code. The 40 ms matches TSC200X_PENUP_TIME_MS, chosen there
for the same want of a lift event from the controller, and has to stay
above the interval between consecutive reports of a held gesture.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v4:
- follow the keycode lookup and the single read of patch 3
v3:
- use timer_shutdown_sync() in the devres teardown action (Sashiko AI
review)
v2:
- skip the release when the interrupt handler re-armed the timer while
the timer callback waited for the lock (Sashiko AI review)
drivers/input/touchscreen/hynitron-cst816x.c | 52 ++++++++++++++++++--
1 file changed, 49 insertions(+), 3 deletions(-)
--- a/drivers/input/touchscreen/hynitron-cst816x.c
+++ b/drivers/input/touchscreen/hynitron-cst816x.c
@@ -15,9 +15,12 @@
#include <linux/unaligned.h>
#include <linux/interrupt.h>
#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
#define CST816X_RD_REG 0x01
#define CST816X_NUM_KEYS 5
+#define CST816X_GEST_HOLD_MS 40
struct cst816x_touch {
u8 gest;
@@ -32,6 +35,9 @@ struct cst816x_priv {
struct input_dev *input;
unsigned int keycode[CST816X_NUM_KEYS];
unsigned int keycodemax;
+ struct timer_list release_timer;
+ /* lock keeps the timeout out of the middle of a report */
+ spinlock_t lock;
struct touchscreen_properties prop;
};
@@ -175,8 +181,9 @@ static void cst816x_reset(struct cst816x
msleep(100);
}
-static void cst816x_release_keys(struct cst816x_priv *priv)
+static bool cst816x_release_keys(struct cst816x_priv *priv)
{
+ bool released = false;
unsigned int key;
unsigned int i;
@@ -186,34 +193,67 @@ static void cst816x_release_keys(struct
continue;
input_report_key(priv->input, key, 0);
+ released = true;
}
+
+ return released;
+}
+
+static void cst816x_release_timeout(struct timer_list *t)
+{
+ struct cst816x_priv *priv = timer_container_of(priv, t, release_timer);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ /* a re-arm while this waited for the lock supersedes the timeout */
+ if (!timer_pending(&priv->release_timer) && cst816x_release_keys(priv))
+ input_sync(priv->input);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
}
static irqreturn_t cst816x_irq_cb(int irq, void *cookie)
{
struct cst816x_priv *priv = cookie;
struct cst816x_touch tch;
+ unsigned long flags;
unsigned int key;
if (!cst816x_process_touch(priv, &tch))
return IRQ_HANDLED;
+ spin_lock_irqsave(&priv->lock, flags);
+
touchscreen_report_pos(priv->input, &priv->prop,
tch.abs_x, tch.abs_y, false);
key = cst816x_gest_keycode(priv, tch.gest);
- if (key != KEY_RESERVED)
+ if (key != KEY_RESERVED) {
input_report_key(priv->input, key, 1);
- else
+ mod_timer(&priv->release_timer,
+ jiffies + msecs_to_jiffies(CST816X_GEST_HOLD_MS));
+ } else {
+ timer_delete(&priv->release_timer);
cst816x_release_keys(priv);
+ }
input_report_key(priv->input, BTN_TOUCH, tch.active);
input_sync(priv->input);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
return IRQ_HANDLED;
}
+static void cst816x_timer_stop(void *data)
+{
+ struct cst816x_priv *priv = data;
+
+ timer_shutdown_sync(&priv->release_timer);
+}
+
static int cst816x_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
@@ -225,6 +265,8 @@ static int cst816x_probe(struct i2c_clie
return -ENOMEM;
priv->client = client;
+ spin_lock_init(&priv->lock);
+ timer_setup(&priv->release_timer, cst816x_release_timeout, 0);
priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(priv->reset))
@@ -242,6 +284,10 @@ static int cst816x_probe(struct i2c_clie
if (error)
return dev_err_probe(dev, error, "input register failed\n");
+ error = devm_add_action_or_reset(dev, cst816x_timer_stop, priv);
+ if (error)
+ return error;
+
error = devm_request_threaded_irq(dev, client->irq,
NULL, cst816x_irq_cb, IRQF_ONESHOT,
dev_driver_string(dev), priv);
@@ -0,0 +1,29 @@
From ff91fc6f203d4442d555597cdb455b476af9da50 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Mon, 5 Oct 2026 21:45:37 +0100
Subject: [PATCH v4 5/5] Input: hynitron-cst816x: set INPUT_PROP_DIRECT
The sensor sits on the panel, so the coordinates it reports map
straight onto the display. Without either direct or pointer set,
event-codes.rst leaves userspace to deduce the device type from the
emitted event types, where ABS_X, ABS_Y and BTN_TOUCH read as a
touchpad and a touch becomes relative pointer motion.
Fixes: c87a819bec86 ("Input: add driver for Hynitron CST816x series")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v4: new patch
drivers/input/touchscreen/hynitron-cst816x.c | 1 +
1 file changed, 1 insertion(+)
--- a/drivers/input/touchscreen/hynitron-cst816x.c
+++ b/drivers/input/touchscreen/hynitron-cst816x.c
@@ -157,6 +157,7 @@ static int cst816x_register_input(struct
input_set_abs_params(priv->input, ABS_X, 0, 240, 0, 0);
input_set_abs_params(priv->input, ABS_Y, 0, 240, 0, 0);
input_set_capability(priv->input, EV_KEY, BTN_TOUCH);
+ __set_bit(INPUT_PROP_DIRECT, priv->input->propbit);
touchscreen_parse_properties(priv->input, false, &priv->prop);
priv->input->keycode = priv->keycode;