File Index
Files and directories
Sign in
FILE

hynitron-cst816x.c

File preview with syntax highlighting.

linux-6.19.14/drivers/input/touchscreen/hynitron-cst816x.c C
  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
 21struct cst816x_touch {
 22	u8 gest;
 23	u8 active;
 24	u16 abs_x;
 25	u16 abs_y;
 26} __packed;
 27
 28struct 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
 36static 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
 65static 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
 95static 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
115static 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
130static 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
160static 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
168static 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
191static 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
228static const struct i2c_device_id cst816x_id[] = {
229	{ .name = "cst816s", 0 },
230	{ }
231};
232MODULE_DEVICE_TABLE(i2c, cst816x_id);
233
234static const struct of_device_id cst816x_of_match[] = {
235	{ .compatible = "hynitron,cst816s", },
236	{ }
237};
238MODULE_DEVICE_TABLE(of, cst816x_of_match);
239
240static 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
249module_i2c_driver(cst816x_driver);
250
251MODULE_AUTHOR("Oleh Kuzhylnyi <kuzhylol@gmail.com>");
252MODULE_DESCRIPTION("Hynitron CST816x Series Touchscreen Driver");
253MODULE_LICENSE("GPL");
~/ Read-only file preview.