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

Input: add a new Novatek NVT-ts driver

Add a new driver for the Novatek i2c touchscreen controller as found
on the Acer Iconia One 7 B1-750 tablet. Unfortunately the touchscreen
controller model-number is unknown. Even with the tablet opened up it
is impossible to read the model-number.

Android calls this a "NVT-ts" touchscreen, but that may apply to other
Novatek controller models too.

This appears to be the same controller as the one supported by
https://github.com/advx9600/android/blob/master/touchscreen/NVTtouch_Android4.0/NVTtouch.c
but unfortunately that does not give us a model-number either.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Jeff LaBundy <jeff@labundy.com>
Link: https://lore.kernel.org/r/20230326212308.55730-1-hdegoede@redhat.com
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

authored by

Hans de Goede and committed by
Dmitry Torokhov
57d94d15 db7220c4

+318
+6
MAINTAINERS
··· 14781 14781 F: tools/include/nolibc/ 14782 14782 F: tools/testing/selftests/nolibc/ 14783 14783 14784 + NOVATEK NVT-TS I2C TOUCHSCREEN DRIVER 14785 + M: Hans de Goede <hdegoede@redhat.com> 14786 + L: linux-input@vger.kernel.org 14787 + S: Maintained 14788 + F: drivers/input/touchscreen/novatek-nvt-ts.c 14789 + 14784 14790 NSDEPS 14785 14791 M: Matthias Maennich <maennich@google.com> 14786 14792 S: Maintained
+10
drivers/input/touchscreen/Kconfig
··· 654 654 To compile this driver as a module, choose M here: the 655 655 module will be called mtouch. 656 656 657 + config TOUCHSCREEN_NOVATEK_NVT_TS 658 + tristate "Novatek NVT-ts touchscreen support" 659 + depends on I2C 660 + help 661 + Say Y here if you have a Novatek NVT-ts touchscreen. 662 + If unsure, say N. 663 + 664 + To compile this driver as a module, choose M here: the 665 + module will be called novatek-nvt-ts. 666 + 657 667 config TOUCHSCREEN_IMAGIS 658 668 tristate "Imagis touchscreen support" 659 669 depends on I2C
+1
drivers/input/touchscreen/Makefile
··· 67 67 obj-$(CONFIG_TOUCHSCREEN_MSG2638) += msg2638.o 68 68 obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o 69 69 obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o 70 + obj-$(CONFIG_TOUCHSCREEN_NOVATEK_NVT_TS) += novatek-nvt-ts.o 70 71 obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o 71 72 obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o 72 73 obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO) += ipaq-micro-ts.o
+301
drivers/input/touchscreen/novatek-nvt-ts.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Driver for Novatek i2c touchscreen controller as found on 4 + * the Acer Iconia One 7 B1-750 tablet. The Touchscreen controller 5 + * model-number is unknown. Android calls this a "NVT-ts" touchscreen, 6 + * but that may apply to other Novatek controller models too. 7 + * 8 + * Copyright (c) 2023 Hans de Goede <hdegoede@redhat.com> 9 + */ 10 + 11 + #include <linux/delay.h> 12 + #include <linux/gpio/consumer.h> 13 + #include <linux/interrupt.h> 14 + #include <linux/i2c.h> 15 + #include <linux/input.h> 16 + #include <linux/input/mt.h> 17 + #include <linux/input/touchscreen.h> 18 + #include <linux/module.h> 19 + 20 + #include <asm/unaligned.h> 21 + 22 + #define NVT_TS_TOUCH_START 0x00 23 + #define NVT_TS_TOUCH_SIZE 6 24 + 25 + #define NVT_TS_PARAMETERS_START 0x78 26 + /* These are offsets from NVT_TS_PARAMETERS_START */ 27 + #define NVT_TS_PARAMS_WIDTH 0x04 28 + #define NVT_TS_PARAMS_HEIGHT 0x06 29 + #define NVT_TS_PARAMS_MAX_TOUCH 0x09 30 + #define NVT_TS_PARAMS_MAX_BUTTONS 0x0a 31 + #define NVT_TS_PARAMS_IRQ_TYPE 0x0b 32 + #define NVT_TS_PARAMS_WAKE_TYPE 0x0c 33 + #define NVT_TS_PARAMS_CHIP_ID 0x0e 34 + #define NVT_TS_PARAMS_SIZE 0x0f 35 + 36 + #define NVT_TS_SUPPORTED_WAKE_TYPE 0x05 37 + #define NVT_TS_SUPPORTED_CHIP_ID 0x05 38 + 39 + #define NVT_TS_MAX_TOUCHES 10 40 + #define NVT_TS_MAX_SIZE 4096 41 + 42 + #define NVT_TS_TOUCH_INVALID 0xff 43 + #define NVT_TS_TOUCH_SLOT_SHIFT 3 44 + #define NVT_TS_TOUCH_TYPE_MASK GENMASK(2, 0) 45 + #define NVT_TS_TOUCH_NEW 1 46 + #define NVT_TS_TOUCH_UPDATE 2 47 + #define NVT_TS_TOUCH_RELEASE 3 48 + 49 + static const int nvt_ts_irq_type[4] = { 50 + IRQF_TRIGGER_RISING, 51 + IRQF_TRIGGER_FALLING, 52 + IRQF_TRIGGER_LOW, 53 + IRQF_TRIGGER_HIGH 54 + }; 55 + 56 + struct nvt_ts_data { 57 + struct i2c_client *client; 58 + struct input_dev *input; 59 + struct gpio_desc *reset_gpio; 60 + struct touchscreen_properties prop; 61 + int max_touches; 62 + u8 buf[NVT_TS_TOUCH_SIZE * NVT_TS_MAX_TOUCHES]; 63 + }; 64 + 65 + static int nvt_ts_read_data(struct i2c_client *client, u8 reg, u8 *data, int count) 66 + { 67 + struct i2c_msg msg[2] = { 68 + { 69 + .addr = client->addr, 70 + .len = 1, 71 + .buf = &reg, 72 + }, 73 + { 74 + .addr = client->addr, 75 + .flags = I2C_M_RD, 76 + .len = count, 77 + .buf = data, 78 + } 79 + }; 80 + int ret; 81 + 82 + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); 83 + if (ret != ARRAY_SIZE(msg)) { 84 + dev_err(&client->dev, "Error reading from 0x%02x: %d\n", reg, ret); 85 + return (ret < 0) ? ret : -EIO; 86 + } 87 + 88 + return 0; 89 + } 90 + 91 + static irqreturn_t nvt_ts_irq(int irq, void *dev_id) 92 + { 93 + struct nvt_ts_data *data = dev_id; 94 + struct device *dev = &data->client->dev; 95 + int i, error, slot, x, y; 96 + bool active; 97 + u8 *touch; 98 + 99 + error = nvt_ts_read_data(data->client, NVT_TS_TOUCH_START, data->buf, 100 + data->max_touches * NVT_TS_TOUCH_SIZE); 101 + if (error) 102 + return IRQ_HANDLED; 103 + 104 + for (i = 0; i < data->max_touches; i++) { 105 + touch = &data->buf[i * NVT_TS_TOUCH_SIZE]; 106 + 107 + if (touch[0] == NVT_TS_TOUCH_INVALID) 108 + continue; 109 + 110 + slot = touch[0] >> NVT_TS_TOUCH_SLOT_SHIFT; 111 + if (slot < 1 || slot > data->max_touches) { 112 + dev_warn(dev, "slot %d out of range, ignoring\n", slot); 113 + continue; 114 + } 115 + 116 + switch (touch[0] & NVT_TS_TOUCH_TYPE_MASK) { 117 + case NVT_TS_TOUCH_NEW: 118 + case NVT_TS_TOUCH_UPDATE: 119 + active = true; 120 + break; 121 + case NVT_TS_TOUCH_RELEASE: 122 + active = false; 123 + break; 124 + default: 125 + dev_warn(dev, "slot %d unknown state %d\n", slot, touch[0] & 7); 126 + continue; 127 + } 128 + 129 + slot--; 130 + x = (touch[1] << 4) | (touch[3] >> 4); 131 + y = (touch[2] << 4) | (touch[3] & 0x0f); 132 + 133 + input_mt_slot(data->input, slot); 134 + input_mt_report_slot_state(data->input, MT_TOOL_FINGER, active); 135 + touchscreen_report_pos(data->input, &data->prop, x, y, true); 136 + } 137 + 138 + input_mt_sync_frame(data->input); 139 + input_sync(data->input); 140 + 141 + return IRQ_HANDLED; 142 + } 143 + 144 + static int nvt_ts_start(struct input_dev *dev) 145 + { 146 + struct nvt_ts_data *data = input_get_drvdata(dev); 147 + 148 + enable_irq(data->client->irq); 149 + gpiod_set_value_cansleep(data->reset_gpio, 0); 150 + 151 + return 0; 152 + } 153 + 154 + static void nvt_ts_stop(struct input_dev *dev) 155 + { 156 + struct nvt_ts_data *data = input_get_drvdata(dev); 157 + 158 + disable_irq(data->client->irq); 159 + gpiod_set_value_cansleep(data->reset_gpio, 1); 160 + } 161 + 162 + static int nvt_ts_suspend(struct device *dev) 163 + { 164 + struct nvt_ts_data *data = i2c_get_clientdata(to_i2c_client(dev)); 165 + 166 + mutex_lock(&data->input->mutex); 167 + if (input_device_enabled(data->input)) 168 + nvt_ts_stop(data->input); 169 + mutex_unlock(&data->input->mutex); 170 + 171 + return 0; 172 + } 173 + 174 + static int nvt_ts_resume(struct device *dev) 175 + { 176 + struct nvt_ts_data *data = i2c_get_clientdata(to_i2c_client(dev)); 177 + 178 + mutex_lock(&data->input->mutex); 179 + if (input_device_enabled(data->input)) 180 + nvt_ts_start(data->input); 181 + mutex_unlock(&data->input->mutex); 182 + 183 + return 0; 184 + } 185 + 186 + static DEFINE_SIMPLE_DEV_PM_OPS(nvt_ts_pm_ops, nvt_ts_suspend, nvt_ts_resume); 187 + 188 + static int nvt_ts_probe(struct i2c_client *client) 189 + { 190 + struct device *dev = &client->dev; 191 + int error, width, height, irq_type; 192 + struct nvt_ts_data *data; 193 + struct input_dev *input; 194 + 195 + if (!client->irq) { 196 + dev_err(dev, "Error no irq specified\n"); 197 + return -EINVAL; 198 + } 199 + 200 + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 201 + if (!data) 202 + return -ENOMEM; 203 + 204 + data->client = client; 205 + i2c_set_clientdata(client, data); 206 + 207 + data->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); 208 + error = PTR_ERR_OR_ZERO(data->reset_gpio); 209 + if (error) { 210 + dev_err(dev, "failed to request reset GPIO: %d\n", error); 211 + return error; 212 + } 213 + 214 + /* Wait for controller to come out of reset before params read */ 215 + msleep(100); 216 + error = nvt_ts_read_data(data->client, NVT_TS_PARAMETERS_START, 217 + data->buf, NVT_TS_PARAMS_SIZE); 218 + gpiod_set_value_cansleep(data->reset_gpio, 1); /* Put back in reset */ 219 + if (error) 220 + return error; 221 + 222 + width = get_unaligned_be16(&data->buf[NVT_TS_PARAMS_WIDTH]); 223 + height = get_unaligned_be16(&data->buf[NVT_TS_PARAMS_HEIGHT]); 224 + data->max_touches = data->buf[NVT_TS_PARAMS_MAX_TOUCH]; 225 + irq_type = data->buf[NVT_TS_PARAMS_IRQ_TYPE]; 226 + 227 + if (width > NVT_TS_MAX_SIZE || height >= NVT_TS_MAX_SIZE || 228 + data->max_touches > NVT_TS_MAX_TOUCHES || 229 + irq_type >= ARRAY_SIZE(nvt_ts_irq_type) || 230 + data->buf[NVT_TS_PARAMS_WAKE_TYPE] != NVT_TS_SUPPORTED_WAKE_TYPE || 231 + data->buf[NVT_TS_PARAMS_CHIP_ID] != NVT_TS_SUPPORTED_CHIP_ID) { 232 + dev_err(dev, "Unsupported touchscreen parameters: %*ph\n", 233 + NVT_TS_PARAMS_SIZE, data->buf); 234 + return -EIO; 235 + } 236 + 237 + dev_dbg(dev, "Detected %dx%d touchscreen with %d max touches\n", 238 + width, height, data->max_touches); 239 + 240 + if (data->buf[NVT_TS_PARAMS_MAX_BUTTONS]) 241 + dev_warn(dev, "Touchscreen buttons are not supported\n"); 242 + 243 + input = devm_input_allocate_device(dev); 244 + if (!input) 245 + return -ENOMEM; 246 + 247 + input->name = client->name; 248 + input->id.bustype = BUS_I2C; 249 + input->open = nvt_ts_start; 250 + input->close = nvt_ts_stop; 251 + 252 + input_set_abs_params(input, ABS_MT_POSITION_X, 0, width - 1, 0, 0); 253 + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, height - 1, 0, 0); 254 + touchscreen_parse_properties(input, true, &data->prop); 255 + 256 + error = input_mt_init_slots(input, data->max_touches, 257 + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); 258 + if (error) 259 + return error; 260 + 261 + data->input = input; 262 + input_set_drvdata(input, data); 263 + 264 + error = devm_request_threaded_irq(dev, client->irq, NULL, nvt_ts_irq, 265 + IRQF_ONESHOT | IRQF_NO_AUTOEN | 266 + nvt_ts_irq_type[irq_type], 267 + client->name, data); 268 + if (error) { 269 + dev_err(dev, "failed to request irq: %d\n", error); 270 + return error; 271 + } 272 + 273 + error = input_register_device(input); 274 + if (error) { 275 + dev_err(dev, "failed to request irq: %d\n", error); 276 + return error; 277 + } 278 + 279 + return 0; 280 + } 281 + 282 + static const struct i2c_device_id nvt_ts_i2c_id[] = { 283 + { "NVT-ts" }, 284 + { } 285 + }; 286 + MODULE_DEVICE_TABLE(i2c, nvt_ts_i2c_id); 287 + 288 + static struct i2c_driver nvt_ts_driver = { 289 + .driver = { 290 + .name = "novatek-nvt-ts", 291 + .pm = pm_sleep_ptr(&nvt_ts_pm_ops), 292 + }, 293 + .probe_new = nvt_ts_probe, 294 + .id_table = nvt_ts_i2c_id, 295 + }; 296 + 297 + module_i2c_driver(nvt_ts_driver); 298 + 299 + MODULE_DESCRIPTION("Novatek NVT-ts touchscreen driver"); 300 + MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 301 + MODULE_LICENSE("GPL");