Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.19-rc8 253 lines 5.9 kB view raw
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");