.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Driver for I2C connected EETI EXC3000 multiple touch controller |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2017 Ahmet Inan <inan@distec.de> |
---|
5 | 6 | * |
---|
6 | 7 | * minimal implementation based on egalax_ts.c and egalax_i2c.c |
---|
7 | | - * |
---|
8 | | - * This program is free software; you can redistribute it and/or modify |
---|
9 | | - * it under the terms of the GNU General Public License version 2 as |
---|
10 | | - * published by the Free Software Foundation. |
---|
11 | 8 | */ |
---|
12 | 9 | |
---|
13 | 10 | #include <linux/bitops.h> |
---|
| 11 | +#include <linux/delay.h> |
---|
14 | 12 | #include <linux/device.h> |
---|
| 13 | +#include <linux/gpio/consumer.h> |
---|
15 | 14 | #include <linux/i2c.h> |
---|
16 | 15 | #include <linux/input.h> |
---|
17 | 16 | #include <linux/input/mt.h> |
---|
.. | .. |
---|
19 | 18 | #include <linux/interrupt.h> |
---|
20 | 19 | #include <linux/module.h> |
---|
21 | 20 | #include <linux/of.h> |
---|
| 21 | +#include <linux/sizes.h> |
---|
22 | 22 | #include <linux/timer.h> |
---|
23 | 23 | #include <asm/unaligned.h> |
---|
24 | 24 | |
---|
.. | .. |
---|
26 | 26 | #define EXC3000_SLOTS_PER_FRAME 5 |
---|
27 | 27 | #define EXC3000_LEN_FRAME 66 |
---|
28 | 28 | #define EXC3000_LEN_POINT 10 |
---|
29 | | -#define EXC3000_MT_EVENT 6 |
---|
| 29 | + |
---|
| 30 | +#define EXC3000_LEN_MODEL_NAME 16 |
---|
| 31 | +#define EXC3000_LEN_FW_VERSION 16 |
---|
| 32 | + |
---|
| 33 | +#define EXC3000_MT1_EVENT 0x06 |
---|
| 34 | +#define EXC3000_MT2_EVENT 0x18 |
---|
| 35 | + |
---|
30 | 36 | #define EXC3000_TIMEOUT_MS 100 |
---|
| 37 | + |
---|
| 38 | +#define EXC3000_RESET_MS 10 |
---|
| 39 | +#define EXC3000_READY_MS 100 |
---|
| 40 | + |
---|
| 41 | +static const struct i2c_device_id exc3000_id[]; |
---|
| 42 | + |
---|
| 43 | +struct eeti_dev_info { |
---|
| 44 | + const char *name; |
---|
| 45 | + int max_xy; |
---|
| 46 | +}; |
---|
| 47 | + |
---|
| 48 | +enum eeti_dev_id { |
---|
| 49 | + EETI_EXC3000, |
---|
| 50 | + EETI_EXC80H60, |
---|
| 51 | + EETI_EXC80H84, |
---|
| 52 | +}; |
---|
| 53 | + |
---|
| 54 | +static struct eeti_dev_info exc3000_info[] = { |
---|
| 55 | + [EETI_EXC3000] = { |
---|
| 56 | + .name = "EETI EXC3000 Touch Screen", |
---|
| 57 | + .max_xy = SZ_4K - 1, |
---|
| 58 | + }, |
---|
| 59 | + [EETI_EXC80H60] = { |
---|
| 60 | + .name = "EETI EXC80H60 Touch Screen", |
---|
| 61 | + .max_xy = SZ_16K - 1, |
---|
| 62 | + }, |
---|
| 63 | + [EETI_EXC80H84] = { |
---|
| 64 | + .name = "EETI EXC80H84 Touch Screen", |
---|
| 65 | + .max_xy = SZ_16K - 1, |
---|
| 66 | + }, |
---|
| 67 | +}; |
---|
31 | 68 | |
---|
32 | 69 | struct exc3000_data { |
---|
33 | 70 | struct i2c_client *client; |
---|
| 71 | + const struct eeti_dev_info *info; |
---|
34 | 72 | struct input_dev *input; |
---|
35 | 73 | struct touchscreen_properties prop; |
---|
| 74 | + struct gpio_desc *reset; |
---|
36 | 75 | struct timer_list timer; |
---|
37 | 76 | u8 buf[2 * EXC3000_LEN_FRAME]; |
---|
| 77 | + struct completion wait_event; |
---|
| 78 | + struct mutex query_lock; |
---|
| 79 | + int query_result; |
---|
| 80 | + char model[EXC3000_LEN_MODEL_NAME]; |
---|
| 81 | + char fw_version[EXC3000_LEN_FW_VERSION]; |
---|
38 | 82 | }; |
---|
39 | 83 | |
---|
40 | 84 | static void exc3000_report_slots(struct input_dev *input, |
---|
.. | .. |
---|
61 | 105 | input_sync(data->input); |
---|
62 | 106 | } |
---|
63 | 107 | |
---|
64 | | -static int exc3000_read_frame(struct i2c_client *client, u8 *buf) |
---|
| 108 | +static int exc3000_read_frame(struct exc3000_data *data, u8 *buf) |
---|
65 | 109 | { |
---|
| 110 | + struct i2c_client *client = data->client; |
---|
| 111 | + u8 expected_event = EXC3000_MT1_EVENT; |
---|
66 | 112 | int ret; |
---|
| 113 | + |
---|
| 114 | + if (data->info->max_xy == SZ_16K - 1) |
---|
| 115 | + expected_event = EXC3000_MT2_EVENT; |
---|
67 | 116 | |
---|
68 | 117 | ret = i2c_master_send(client, "'", 2); |
---|
69 | 118 | if (ret < 0) |
---|
.. | .. |
---|
79 | 128 | if (ret != EXC3000_LEN_FRAME) |
---|
80 | 129 | return -EIO; |
---|
81 | 130 | |
---|
82 | | - if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME || |
---|
83 | | - buf[2] != EXC3000_MT_EVENT) |
---|
| 131 | + if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME) |
---|
| 132 | + return -EINVAL; |
---|
| 133 | + |
---|
| 134 | + if (buf[2] != expected_event) |
---|
84 | 135 | return -EINVAL; |
---|
85 | 136 | |
---|
86 | 137 | return 0; |
---|
87 | 138 | } |
---|
88 | 139 | |
---|
89 | | -static int exc3000_read_data(struct i2c_client *client, |
---|
| 140 | +static int exc3000_read_data(struct exc3000_data *data, |
---|
90 | 141 | u8 *buf, int *n_slots) |
---|
91 | 142 | { |
---|
92 | 143 | int error; |
---|
93 | 144 | |
---|
94 | | - error = exc3000_read_frame(client, buf); |
---|
| 145 | + error = exc3000_read_frame(data, buf); |
---|
95 | 146 | if (error) |
---|
96 | 147 | return error; |
---|
97 | 148 | |
---|
.. | .. |
---|
101 | 152 | |
---|
102 | 153 | if (*n_slots > EXC3000_SLOTS_PER_FRAME) { |
---|
103 | 154 | /* Read 2nd frame to get the rest of the contacts. */ |
---|
104 | | - error = exc3000_read_frame(client, buf + EXC3000_LEN_FRAME); |
---|
| 155 | + error = exc3000_read_frame(data, buf + EXC3000_LEN_FRAME); |
---|
105 | 156 | if (error) |
---|
106 | 157 | return error; |
---|
107 | 158 | |
---|
.. | .. |
---|
109 | 160 | if (buf[EXC3000_LEN_FRAME + 3] != 0) |
---|
110 | 161 | return -EINVAL; |
---|
111 | 162 | } |
---|
| 163 | + |
---|
| 164 | + return 0; |
---|
| 165 | +} |
---|
| 166 | + |
---|
| 167 | +static int exc3000_query_interrupt(struct exc3000_data *data) |
---|
| 168 | +{ |
---|
| 169 | + u8 *buf = data->buf; |
---|
| 170 | + int error; |
---|
| 171 | + |
---|
| 172 | + error = i2c_master_recv(data->client, buf, EXC3000_LEN_FRAME); |
---|
| 173 | + if (error < 0) |
---|
| 174 | + return error; |
---|
| 175 | + |
---|
| 176 | + if (buf[0] != 'B') |
---|
| 177 | + return -EPROTO; |
---|
| 178 | + |
---|
| 179 | + if (buf[4] == 'E') |
---|
| 180 | + strlcpy(data->model, buf + 5, sizeof(data->model)); |
---|
| 181 | + else if (buf[4] == 'D') |
---|
| 182 | + strlcpy(data->fw_version, buf + 5, sizeof(data->fw_version)); |
---|
| 183 | + else |
---|
| 184 | + return -EPROTO; |
---|
112 | 185 | |
---|
113 | 186 | return 0; |
---|
114 | 187 | } |
---|
.. | .. |
---|
121 | 194 | int slots, total_slots; |
---|
122 | 195 | int error; |
---|
123 | 196 | |
---|
124 | | - error = exc3000_read_data(data->client, buf, &total_slots); |
---|
| 197 | + if (mutex_is_locked(&data->query_lock)) { |
---|
| 198 | + data->query_result = exc3000_query_interrupt(data); |
---|
| 199 | + complete(&data->wait_event); |
---|
| 200 | + goto out; |
---|
| 201 | + } |
---|
| 202 | + |
---|
| 203 | + error = exc3000_read_data(data, buf, &total_slots); |
---|
125 | 204 | if (error) { |
---|
126 | 205 | /* Schedule a timer to release "stuck" contacts */ |
---|
127 | 206 | mod_timer(&data->timer, |
---|
.. | .. |
---|
148 | 227 | return IRQ_HANDLED; |
---|
149 | 228 | } |
---|
150 | 229 | |
---|
151 | | -static int exc3000_probe(struct i2c_client *client, |
---|
152 | | - const struct i2c_device_id *id) |
---|
| 230 | +static ssize_t fw_version_show(struct device *dev, |
---|
| 231 | + struct device_attribute *attr, char *buf) |
---|
| 232 | +{ |
---|
| 233 | + struct i2c_client *client = to_i2c_client(dev); |
---|
| 234 | + struct exc3000_data *data = i2c_get_clientdata(client); |
---|
| 235 | + static const u8 request[68] = { |
---|
| 236 | + 0x67, 0x00, 0x42, 0x00, 0x03, 0x01, 'D', 0x00 |
---|
| 237 | + }; |
---|
| 238 | + int error; |
---|
| 239 | + |
---|
| 240 | + mutex_lock(&data->query_lock); |
---|
| 241 | + |
---|
| 242 | + data->query_result = -ETIMEDOUT; |
---|
| 243 | + reinit_completion(&data->wait_event); |
---|
| 244 | + |
---|
| 245 | + error = i2c_master_send(client, request, sizeof(request)); |
---|
| 246 | + if (error < 0) { |
---|
| 247 | + mutex_unlock(&data->query_lock); |
---|
| 248 | + return error; |
---|
| 249 | + } |
---|
| 250 | + |
---|
| 251 | + wait_for_completion_interruptible_timeout(&data->wait_event, 1 * HZ); |
---|
| 252 | + mutex_unlock(&data->query_lock); |
---|
| 253 | + |
---|
| 254 | + if (data->query_result < 0) |
---|
| 255 | + return data->query_result; |
---|
| 256 | + |
---|
| 257 | + return sprintf(buf, "%s\n", data->fw_version); |
---|
| 258 | +} |
---|
| 259 | +static DEVICE_ATTR_RO(fw_version); |
---|
| 260 | + |
---|
| 261 | +static ssize_t exc3000_get_model(struct exc3000_data *data) |
---|
| 262 | +{ |
---|
| 263 | + static const u8 request[68] = { |
---|
| 264 | + 0x67, 0x00, 0x42, 0x00, 0x03, 0x01, 'E', 0x00 |
---|
| 265 | + }; |
---|
| 266 | + struct i2c_client *client = data->client; |
---|
| 267 | + int error; |
---|
| 268 | + |
---|
| 269 | + mutex_lock(&data->query_lock); |
---|
| 270 | + data->query_result = -ETIMEDOUT; |
---|
| 271 | + reinit_completion(&data->wait_event); |
---|
| 272 | + |
---|
| 273 | + error = i2c_master_send(client, request, sizeof(request)); |
---|
| 274 | + if (error < 0) { |
---|
| 275 | + mutex_unlock(&data->query_lock); |
---|
| 276 | + return error; |
---|
| 277 | + } |
---|
| 278 | + |
---|
| 279 | + wait_for_completion_interruptible_timeout(&data->wait_event, 1 * HZ); |
---|
| 280 | + mutex_unlock(&data->query_lock); |
---|
| 281 | + |
---|
| 282 | + return data->query_result; |
---|
| 283 | +} |
---|
| 284 | + |
---|
| 285 | +static ssize_t model_show(struct device *dev, |
---|
| 286 | + struct device_attribute *attr, char *buf) |
---|
| 287 | +{ |
---|
| 288 | + struct i2c_client *client = to_i2c_client(dev); |
---|
| 289 | + struct exc3000_data *data = i2c_get_clientdata(client); |
---|
| 290 | + int error; |
---|
| 291 | + |
---|
| 292 | + error = exc3000_get_model(data); |
---|
| 293 | + if (error < 0) |
---|
| 294 | + return error; |
---|
| 295 | + |
---|
| 296 | + return sprintf(buf, "%s\n", data->model); |
---|
| 297 | +} |
---|
| 298 | +static DEVICE_ATTR_RO(model); |
---|
| 299 | + |
---|
| 300 | +static struct attribute *sysfs_attrs[] = { |
---|
| 301 | + &dev_attr_fw_version.attr, |
---|
| 302 | + &dev_attr_model.attr, |
---|
| 303 | + NULL |
---|
| 304 | +}; |
---|
| 305 | + |
---|
| 306 | +static struct attribute_group exc3000_attribute_group = { |
---|
| 307 | + .attrs = sysfs_attrs |
---|
| 308 | +}; |
---|
| 309 | + |
---|
| 310 | +static int exc3000_probe(struct i2c_client *client) |
---|
153 | 311 | { |
---|
154 | 312 | struct exc3000_data *data; |
---|
155 | 313 | struct input_dev *input; |
---|
156 | | - int error; |
---|
| 314 | + int error, max_xy, retry; |
---|
157 | 315 | |
---|
158 | 316 | data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); |
---|
159 | 317 | if (!data) |
---|
160 | 318 | return -ENOMEM; |
---|
161 | 319 | |
---|
162 | 320 | data->client = client; |
---|
| 321 | + data->info = device_get_match_data(&client->dev); |
---|
| 322 | + if (!data->info) { |
---|
| 323 | + enum eeti_dev_id eeti_dev_id = |
---|
| 324 | + i2c_match_id(exc3000_id, client)->driver_data; |
---|
| 325 | + data->info = &exc3000_info[eeti_dev_id]; |
---|
| 326 | + } |
---|
163 | 327 | timer_setup(&data->timer, exc3000_timer, 0); |
---|
| 328 | + init_completion(&data->wait_event); |
---|
| 329 | + mutex_init(&data->query_lock); |
---|
| 330 | + |
---|
| 331 | + data->reset = devm_gpiod_get_optional(&client->dev, "reset", |
---|
| 332 | + GPIOD_OUT_HIGH); |
---|
| 333 | + if (IS_ERR(data->reset)) |
---|
| 334 | + return PTR_ERR(data->reset); |
---|
| 335 | + |
---|
| 336 | + if (data->reset) { |
---|
| 337 | + msleep(EXC3000_RESET_MS); |
---|
| 338 | + gpiod_set_value_cansleep(data->reset, 0); |
---|
| 339 | + msleep(EXC3000_READY_MS); |
---|
| 340 | + } |
---|
164 | 341 | |
---|
165 | 342 | input = devm_input_allocate_device(&client->dev); |
---|
166 | 343 | if (!input) |
---|
167 | 344 | return -ENOMEM; |
---|
168 | 345 | |
---|
169 | 346 | data->input = input; |
---|
| 347 | + input_set_drvdata(input, data); |
---|
170 | 348 | |
---|
171 | | - input->name = "EETI EXC3000 Touch Screen"; |
---|
| 349 | + input->name = data->info->name; |
---|
172 | 350 | input->id.bustype = BUS_I2C; |
---|
173 | 351 | |
---|
174 | | - input_set_abs_params(input, ABS_MT_POSITION_X, 0, 4095, 0, 0); |
---|
175 | | - input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 4095, 0, 0); |
---|
| 352 | + max_xy = data->info->max_xy; |
---|
| 353 | + input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_xy, 0, 0); |
---|
| 354 | + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_xy, 0, 0); |
---|
| 355 | + |
---|
176 | 356 | touchscreen_parse_properties(input, true, &data->prop); |
---|
177 | 357 | |
---|
178 | 358 | error = input_mt_init_slots(input, EXC3000_NUM_SLOTS, |
---|
.. | .. |
---|
190 | 370 | if (error) |
---|
191 | 371 | return error; |
---|
192 | 372 | |
---|
| 373 | + /* |
---|
| 374 | + * I²C does not have built-in recovery, so retry on failure. This |
---|
| 375 | + * ensures, that the device probe will not fail for temporary issues |
---|
| 376 | + * on the bus. This is not needed for the sysfs calls (userspace |
---|
| 377 | + * will receive the error code and can start another query) and |
---|
| 378 | + * cannot be done for touch events (but that only means loosing one |
---|
| 379 | + * or two touch events anyways). |
---|
| 380 | + */ |
---|
| 381 | + for (retry = 0; retry < 3; retry++) { |
---|
| 382 | + error = exc3000_get_model(data); |
---|
| 383 | + if (!error) |
---|
| 384 | + break; |
---|
| 385 | + dev_warn(&client->dev, "Retry %d get EETI EXC3000 model: %d\n", |
---|
| 386 | + retry + 1, error); |
---|
| 387 | + } |
---|
| 388 | + |
---|
| 389 | + if (error) |
---|
| 390 | + return error; |
---|
| 391 | + |
---|
| 392 | + dev_dbg(&client->dev, "TS Model: %s", data->model); |
---|
| 393 | + |
---|
| 394 | + i2c_set_clientdata(client, data); |
---|
| 395 | + |
---|
| 396 | + error = devm_device_add_group(&client->dev, &exc3000_attribute_group); |
---|
| 397 | + if (error) |
---|
| 398 | + return error; |
---|
| 399 | + |
---|
193 | 400 | return 0; |
---|
194 | 401 | } |
---|
195 | 402 | |
---|
196 | 403 | static const struct i2c_device_id exc3000_id[] = { |
---|
197 | | - { "exc3000", 0 }, |
---|
| 404 | + { "exc3000", EETI_EXC3000 }, |
---|
| 405 | + { "exc80h60", EETI_EXC80H60 }, |
---|
| 406 | + { "exc80h84", EETI_EXC80H84 }, |
---|
198 | 407 | { } |
---|
199 | 408 | }; |
---|
200 | 409 | MODULE_DEVICE_TABLE(i2c, exc3000_id); |
---|
201 | 410 | |
---|
202 | 411 | #ifdef CONFIG_OF |
---|
203 | 412 | static const struct of_device_id exc3000_of_match[] = { |
---|
204 | | - { .compatible = "eeti,exc3000" }, |
---|
| 413 | + { .compatible = "eeti,exc3000", .data = &exc3000_info[EETI_EXC3000] }, |
---|
| 414 | + { .compatible = "eeti,exc80h60", .data = &exc3000_info[EETI_EXC80H60] }, |
---|
| 415 | + { .compatible = "eeti,exc80h84", .data = &exc3000_info[EETI_EXC80H84] }, |
---|
205 | 416 | { } |
---|
206 | 417 | }; |
---|
207 | 418 | MODULE_DEVICE_TABLE(of, exc3000_of_match); |
---|
.. | .. |
---|
213 | 424 | .of_match_table = of_match_ptr(exc3000_of_match), |
---|
214 | 425 | }, |
---|
215 | 426 | .id_table = exc3000_id, |
---|
216 | | - .probe = exc3000_probe, |
---|
| 427 | + .probe_new = exc3000_probe, |
---|
217 | 428 | }; |
---|
218 | 429 | |
---|
219 | 430 | module_i2c_driver(exc3000_driver); |
---|