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 = ®, 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"); 254