Skip to content

Commit c87a819

Browse files
kuzhyloldtor
authored andcommitted
Input: add driver for Hynitron CST816x series
Introduce support for the Hynitron CST816x series touchscreen controller used for 240×240 1.28-inch Round LCD Display Module manufactured by Waveshare Electronics. The driver is designed based on an Arduino implementation marked as under MIT License. This driver is written for a particular round display based on the CST816S controller, which is not compatiable with existing driver for Hynitron controllers. Signed-off-by: Oleh Kuzhylnyi <kuzhylol@gmail.com> Link: https://lore.kernel.org/r/20250921125939.249788-2-kuzhylol@gmail.com Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
1 parent 67c9b68 commit c87a819

3 files changed

Lines changed: 266 additions & 0 deletions

File tree

drivers/input/touchscreen/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,18 @@ config TOUCHSCREEN_HYNITRON_CSTXXX
475475
To compile this driver as a module, choose M here: the
476476
module will be called hynitron-cstxxx.
477477

478+
config TOUCHSCREEN_HYNITRON_CST816X
479+
tristate "Hynitron CST816x touchscreen"
480+
depends on I2C
481+
help
482+
Say Y here if you have a touchscreen using a Hynitron
483+
CST816x series touchscreen controller.
484+
485+
If unsure, say N.
486+
487+
To compile this driver as a module, choose M here: the
488+
module will be called hynitron-cst816x.
489+
478490
config TOUCHSCREEN_ILI210X
479491
tristate "Ilitek ILI210X based touchscreen"
480492
depends on I2C

drivers/input/touchscreen/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_SPI) += goodix_berlin_spi.o
5151
obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep.o
5252
obj-$(CONFIG_TOUCHSCREEN_HIMAX_HX852X) += himax_hx852x.o
5353
obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX) += hynitron_cstxxx.o
54+
obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CST816X) += hynitron-cst816x.o
5455
obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
5556
obj-$(CONFIG_TOUCHSCREEN_ILITEK) += ilitek_ts_i2c.o
5657
obj-$(CONFIG_TOUCHSCREEN_IMAGIS) += imagis.o
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Driver for I2C connected Hynitron CST816x Series Touchscreen
4+
*
5+
* Copyright (C) 2025 Oleh Kuzhylnyi <kuzhylol@gmail.com>
6+
*/
7+
8+
#include <linux/delay.h>
9+
#include <linux/device.h>
10+
#include <linux/err.h>
11+
#include <linux/gpio/consumer.h>
12+
#include <linux/i2c.h>
13+
#include <linux/input.h>
14+
#include <linux/unaligned.h>
15+
#include <linux/interrupt.h>
16+
#include <linux/module.h>
17+
18+
#define CST816X_RD_REG 0x01
19+
#define CST816X_NUM_KEYS 5
20+
21+
struct cst816x_touch {
22+
u8 gest;
23+
u8 active;
24+
u16 abs_x;
25+
u16 abs_y;
26+
} __packed;
27+
28+
struct cst816x_priv {
29+
struct i2c_client *client;
30+
struct gpio_desc *reset;
31+
struct input_dev *input;
32+
unsigned int keycode[CST816X_NUM_KEYS];
33+
unsigned int keycodemax;
34+
};
35+
36+
static int cst816x_parse_keycodes(struct device *dev, struct cst816x_priv *priv)
37+
{
38+
int count;
39+
int error;
40+
41+
if (device_property_present(dev, "linux,keycodes")) {
42+
count = device_property_count_u32(dev, "linux,keycodes");
43+
if (count < 0) {
44+
error = count;
45+
dev_err(dev, "failed to count keys: %d\n", error);
46+
return error;
47+
} else if (count > ARRAY_SIZE(priv->keycode)) {
48+
dev_err(dev, "too many keys defined: %d\n", count);
49+
return -EINVAL;
50+
}
51+
priv->keycodemax = count;
52+
53+
error = device_property_read_u32_array(dev, "linux,keycodes",
54+
priv->keycode,
55+
priv->keycodemax);
56+
if (error) {
57+
dev_err(dev, "failed to read keycodes: %d\n", error);
58+
return error;
59+
}
60+
}
61+
62+
return 0;
63+
}
64+
65+
static int cst816x_i2c_read_register(struct cst816x_priv *priv, u8 reg,
66+
void *buf, size_t len)
67+
{
68+
struct i2c_msg xfer[] = {
69+
{
70+
.addr = priv->client->addr,
71+
.flags = 0,
72+
.buf = &reg,
73+
.len = sizeof(reg),
74+
},
75+
{
76+
.addr = priv->client->addr,
77+
.flags = I2C_M_RD,
78+
.buf = buf,
79+
.len = len,
80+
},
81+
};
82+
int error;
83+
int ret;
84+
85+
ret = i2c_transfer(priv->client->adapter, xfer, ARRAY_SIZE(xfer));
86+
if (ret != ARRAY_SIZE(xfer)) {
87+
error = ret < 0 ? ret : -EIO;
88+
dev_err(&priv->client->dev, "i2c rx err: %d\n", error);
89+
return error;
90+
}
91+
92+
return 0;
93+
}
94+
95+
static u8 cst816x_gest_idx(u8 gest)
96+
{
97+
u8 index;
98+
99+
switch (gest) {
100+
case 0x01: /* Slide up gesture */
101+
case 0x02: /* Slide down gesture */
102+
case 0x03: /* Slide left gesture */
103+
case 0x04: /* Slide right gesture */
104+
index = gest;
105+
break;
106+
case 0x0c: /* Long press gesture */
107+
default:
108+
index = CST816X_NUM_KEYS;
109+
break;
110+
}
111+
112+
return index - 1;
113+
}
114+
115+
static bool cst816x_process_touch(struct cst816x_priv *priv,
116+
struct cst816x_touch *tch)
117+
{
118+
if (cst816x_i2c_read_register(priv, CST816X_RD_REG, tch, sizeof(*tch)))
119+
return false;
120+
121+
tch->abs_x = get_unaligned_be16(&tch->abs_x) & GENMASK(11, 0);
122+
tch->abs_y = get_unaligned_be16(&tch->abs_y) & GENMASK(11, 0);
123+
124+
dev_dbg(&priv->client->dev, "x: %u, y: %u, t: %u, g: 0x%x\n",
125+
tch->abs_x, tch->abs_y, tch->active, tch->gest);
126+
127+
return true;
128+
}
129+
130+
static int cst816x_register_input(struct cst816x_priv *priv)
131+
{
132+
priv->input = devm_input_allocate_device(&priv->client->dev);
133+
if (!priv->input)
134+
return -ENOMEM;
135+
136+
priv->input->name = "Hynitron CST816x Series Touchscreen";
137+
priv->input->phys = "input/ts";
138+
priv->input->id.bustype = BUS_I2C;
139+
140+
input_set_drvdata(priv->input, priv);
141+
142+
input_set_abs_params(priv->input, ABS_X, 0, 240, 0, 0);
143+
input_set_abs_params(priv->input, ABS_Y, 0, 240, 0, 0);
144+
input_set_capability(priv->input, EV_KEY, BTN_TOUCH);
145+
146+
priv->input->keycode = priv->keycode;
147+
priv->input->keycodesize = sizeof(priv->keycode[0]);
148+
priv->input->keycodemax = priv->keycodemax;
149+
150+
for (int i = 0; i < priv->keycodemax; i++) {
151+
if (priv->keycode[i] == KEY_RESERVED)
152+
continue;
153+
154+
input_set_capability(priv->input, EV_KEY, priv->keycode[i]);
155+
}
156+
157+
return input_register_device(priv->input);
158+
}
159+
160+
static void cst816x_reset(struct cst816x_priv *priv)
161+
{
162+
gpiod_set_value_cansleep(priv->reset, 1);
163+
msleep(50);
164+
gpiod_set_value_cansleep(priv->reset, 0);
165+
msleep(100);
166+
}
167+
168+
static irqreturn_t cst816x_irq_cb(int irq, void *cookie)
169+
{
170+
struct cst816x_priv *priv = cookie;
171+
struct cst816x_touch tch;
172+
173+
if (!cst816x_process_touch(priv, &tch))
174+
return IRQ_HANDLED;
175+
176+
input_report_abs(priv->input, ABS_X, tch.abs_x);
177+
input_report_abs(priv->input, ABS_Y, tch.abs_y);
178+
179+
if (tch.gest)
180+
input_report_key(priv->input,
181+
priv->keycode[cst816x_gest_idx(tch.gest)],
182+
tch.active);
183+
184+
input_report_key(priv->input, BTN_TOUCH, tch.active);
185+
186+
input_sync(priv->input);
187+
188+
return IRQ_HANDLED;
189+
}
190+
191+
static int cst816x_probe(struct i2c_client *client)
192+
{
193+
struct device *dev = &client->dev;
194+
struct cst816x_priv *priv;
195+
int error;
196+
197+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
198+
if (!priv)
199+
return -ENOMEM;
200+
201+
priv->client = client;
202+
203+
priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
204+
if (IS_ERR(priv->reset))
205+
return dev_err_probe(dev, PTR_ERR(priv->reset),
206+
"gpio reset request failed\n");
207+
208+
if (priv->reset)
209+
cst816x_reset(priv);
210+
211+
error = cst816x_parse_keycodes(dev, priv);
212+
if (error)
213+
dev_warn(dev, "no gestures found in dt\n");
214+
215+
error = cst816x_register_input(priv);
216+
if (error)
217+
return dev_err_probe(dev, error, "input register failed\n");
218+
219+
error = devm_request_threaded_irq(dev, client->irq,
220+
NULL, cst816x_irq_cb, IRQF_ONESHOT,
221+
dev_driver_string(dev), priv);
222+
if (error)
223+
return dev_err_probe(dev, error, "irq request failed\n");
224+
225+
return 0;
226+
}
227+
228+
static const struct i2c_device_id cst816x_id[] = {
229+
{ .name = "cst816s", 0 },
230+
{ }
231+
};
232+
MODULE_DEVICE_TABLE(i2c, cst816x_id);
233+
234+
static const struct of_device_id cst816x_of_match[] = {
235+
{ .compatible = "hynitron,cst816s", },
236+
{ }
237+
};
238+
MODULE_DEVICE_TABLE(of, cst816x_of_match);
239+
240+
static struct i2c_driver cst816x_driver = {
241+
.driver = {
242+
.name = "cst816x",
243+
.of_match_table = cst816x_of_match,
244+
},
245+
.id_table = cst816x_id,
246+
.probe = cst816x_probe,
247+
};
248+
249+
module_i2c_driver(cst816x_driver);
250+
251+
MODULE_AUTHOR("Oleh Kuzhylnyi <kuzhylol@gmail.com>");
252+
MODULE_DESCRIPTION("Hynitron CST816x Series Touchscreen Driver");
253+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)