1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 27e577a17SAhmet Inan /* 37e577a17SAhmet Inan * Driver for I2C connected EETI EXC3000 multiple touch controller 47e577a17SAhmet Inan * 57e577a17SAhmet Inan * Copyright (C) 2017 Ahmet Inan <inan@distec.de> 67e577a17SAhmet Inan * 77e577a17SAhmet Inan * minimal implementation based on egalax_ts.c and egalax_i2c.c 87e577a17SAhmet Inan */ 97e577a17SAhmet Inan 107e577a17SAhmet Inan #include <linux/bitops.h> 11*27aced19SSebastian Reichel #include <linux/delay.h> 127e577a17SAhmet Inan #include <linux/device.h> 13*27aced19SSebastian Reichel #include <linux/gpio/consumer.h> 147e577a17SAhmet Inan #include <linux/i2c.h> 157e577a17SAhmet Inan #include <linux/input.h> 167e577a17SAhmet Inan #include <linux/input/mt.h> 177e577a17SAhmet Inan #include <linux/input/touchscreen.h> 187e577a17SAhmet Inan #include <linux/interrupt.h> 197e577a17SAhmet Inan #include <linux/module.h> 207e577a17SAhmet Inan #include <linux/of.h> 213bdd21c6SSebastian Reichel #include <linux/sizes.h> 227e577a17SAhmet Inan #include <linux/timer.h> 237e577a17SAhmet Inan #include <asm/unaligned.h> 247e577a17SAhmet Inan 257e577a17SAhmet Inan #define EXC3000_NUM_SLOTS 10 267e577a17SAhmet Inan #define EXC3000_SLOTS_PER_FRAME 5 277e577a17SAhmet Inan #define EXC3000_LEN_FRAME 66 287e577a17SAhmet Inan #define EXC3000_LEN_POINT 10 293bdd21c6SSebastian Reichel 303bdd21c6SSebastian Reichel #define EXC3000_MT1_EVENT 0x06 313bdd21c6SSebastian Reichel #define EXC3000_MT2_EVENT 0x18 323bdd21c6SSebastian Reichel 337e577a17SAhmet Inan #define EXC3000_TIMEOUT_MS 100 347e577a17SAhmet Inan 35*27aced19SSebastian Reichel #define EXC3000_RESET_MS 10 36*27aced19SSebastian Reichel #define EXC3000_READY_MS 100 37*27aced19SSebastian Reichel 383bdd21c6SSebastian Reichel static const struct i2c_device_id exc3000_id[]; 393bdd21c6SSebastian Reichel 403bdd21c6SSebastian Reichel struct eeti_dev_info { 413bdd21c6SSebastian Reichel const char *name; 423bdd21c6SSebastian Reichel int max_xy; 433bdd21c6SSebastian Reichel }; 443bdd21c6SSebastian Reichel 453bdd21c6SSebastian Reichel enum eeti_dev_id { 463bdd21c6SSebastian Reichel EETI_EXC3000, 473bdd21c6SSebastian Reichel EETI_EXC80H60, 483bdd21c6SSebastian Reichel EETI_EXC80H84, 493bdd21c6SSebastian Reichel }; 503bdd21c6SSebastian Reichel 513bdd21c6SSebastian Reichel static struct eeti_dev_info exc3000_info[] = { 523bdd21c6SSebastian Reichel [EETI_EXC3000] = { 533bdd21c6SSebastian Reichel .name = "EETI EXC3000 Touch Screen", 543bdd21c6SSebastian Reichel .max_xy = SZ_4K - 1, 553bdd21c6SSebastian Reichel }, 563bdd21c6SSebastian Reichel [EETI_EXC80H60] = { 573bdd21c6SSebastian Reichel .name = "EETI EXC80H60 Touch Screen", 583bdd21c6SSebastian Reichel .max_xy = SZ_16K - 1, 593bdd21c6SSebastian Reichel }, 603bdd21c6SSebastian Reichel [EETI_EXC80H84] = { 613bdd21c6SSebastian Reichel .name = "EETI EXC80H84 Touch Screen", 623bdd21c6SSebastian Reichel .max_xy = SZ_16K - 1, 633bdd21c6SSebastian Reichel }, 643bdd21c6SSebastian Reichel }; 653bdd21c6SSebastian Reichel 667e577a17SAhmet Inan struct exc3000_data { 677e577a17SAhmet Inan struct i2c_client *client; 683bdd21c6SSebastian Reichel const struct eeti_dev_info *info; 697e577a17SAhmet Inan struct input_dev *input; 707e577a17SAhmet Inan struct touchscreen_properties prop; 71*27aced19SSebastian Reichel struct gpio_desc *reset; 727e577a17SAhmet Inan struct timer_list timer; 737e577a17SAhmet Inan u8 buf[2 * EXC3000_LEN_FRAME]; 747e577a17SAhmet Inan }; 757e577a17SAhmet Inan 767e577a17SAhmet Inan static void exc3000_report_slots(struct input_dev *input, 777e577a17SAhmet Inan struct touchscreen_properties *prop, 787e577a17SAhmet Inan const u8 *buf, int num) 797e577a17SAhmet Inan { 807e577a17SAhmet Inan for (; num--; buf += EXC3000_LEN_POINT) { 817e577a17SAhmet Inan if (buf[0] & BIT(0)) { 827e577a17SAhmet Inan input_mt_slot(input, buf[1]); 837e577a17SAhmet Inan input_mt_report_slot_state(input, MT_TOOL_FINGER, true); 847e577a17SAhmet Inan touchscreen_report_pos(input, prop, 857e577a17SAhmet Inan get_unaligned_le16(buf + 2), 867e577a17SAhmet Inan get_unaligned_le16(buf + 4), 877e577a17SAhmet Inan true); 887e577a17SAhmet Inan } 897e577a17SAhmet Inan } 907e577a17SAhmet Inan } 917e577a17SAhmet Inan 927e577a17SAhmet Inan static void exc3000_timer(struct timer_list *t) 937e577a17SAhmet Inan { 947e577a17SAhmet Inan struct exc3000_data *data = from_timer(data, t, timer); 957e577a17SAhmet Inan 967e577a17SAhmet Inan input_mt_sync_frame(data->input); 977e577a17SAhmet Inan input_sync(data->input); 987e577a17SAhmet Inan } 997e577a17SAhmet Inan 1003bdd21c6SSebastian Reichel static int exc3000_read_frame(struct exc3000_data *data, u8 *buf) 1017e577a17SAhmet Inan { 1023bdd21c6SSebastian Reichel struct i2c_client *client = data->client; 1033bdd21c6SSebastian Reichel u8 expected_event = EXC3000_MT1_EVENT; 1047e577a17SAhmet Inan int ret; 1057e577a17SAhmet Inan 1063bdd21c6SSebastian Reichel if (data->info->max_xy == SZ_16K - 1) 1073bdd21c6SSebastian Reichel expected_event = EXC3000_MT2_EVENT; 1083bdd21c6SSebastian Reichel 1097e577a17SAhmet Inan ret = i2c_master_send(client, "'", 2); 1107e577a17SAhmet Inan if (ret < 0) 1117e577a17SAhmet Inan return ret; 1127e577a17SAhmet Inan 1137e577a17SAhmet Inan if (ret != 2) 1147e577a17SAhmet Inan return -EIO; 1157e577a17SAhmet Inan 1167e577a17SAhmet Inan ret = i2c_master_recv(client, buf, EXC3000_LEN_FRAME); 1177e577a17SAhmet Inan if (ret < 0) 1187e577a17SAhmet Inan return ret; 1197e577a17SAhmet Inan 1207e577a17SAhmet Inan if (ret != EXC3000_LEN_FRAME) 1217e577a17SAhmet Inan return -EIO; 1227e577a17SAhmet Inan 1233bdd21c6SSebastian Reichel if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME) 1243bdd21c6SSebastian Reichel return -EINVAL; 1253bdd21c6SSebastian Reichel 1263bdd21c6SSebastian Reichel if (buf[2] != expected_event) 1277e577a17SAhmet Inan return -EINVAL; 1287e577a17SAhmet Inan 1297e577a17SAhmet Inan return 0; 1307e577a17SAhmet Inan } 1317e577a17SAhmet Inan 1323bdd21c6SSebastian Reichel static int exc3000_read_data(struct exc3000_data *data, 1337e577a17SAhmet Inan u8 *buf, int *n_slots) 1347e577a17SAhmet Inan { 1357e577a17SAhmet Inan int error; 1367e577a17SAhmet Inan 1373bdd21c6SSebastian Reichel error = exc3000_read_frame(data, buf); 1387e577a17SAhmet Inan if (error) 1397e577a17SAhmet Inan return error; 1407e577a17SAhmet Inan 1417e577a17SAhmet Inan *n_slots = buf[3]; 1427e577a17SAhmet Inan if (!*n_slots || *n_slots > EXC3000_NUM_SLOTS) 1437e577a17SAhmet Inan return -EINVAL; 1447e577a17SAhmet Inan 1457e577a17SAhmet Inan if (*n_slots > EXC3000_SLOTS_PER_FRAME) { 1467e577a17SAhmet Inan /* Read 2nd frame to get the rest of the contacts. */ 1473bdd21c6SSebastian Reichel error = exc3000_read_frame(data, buf + EXC3000_LEN_FRAME); 1487e577a17SAhmet Inan if (error) 1497e577a17SAhmet Inan return error; 1507e577a17SAhmet Inan 1517e577a17SAhmet Inan /* 2nd chunk must have number of contacts set to 0. */ 1527e577a17SAhmet Inan if (buf[EXC3000_LEN_FRAME + 3] != 0) 1537e577a17SAhmet Inan return -EINVAL; 1547e577a17SAhmet Inan } 1557e577a17SAhmet Inan 1567e577a17SAhmet Inan return 0; 1577e577a17SAhmet Inan } 1587e577a17SAhmet Inan 1597e577a17SAhmet Inan static irqreturn_t exc3000_interrupt(int irq, void *dev_id) 1607e577a17SAhmet Inan { 1617e577a17SAhmet Inan struct exc3000_data *data = dev_id; 1627e577a17SAhmet Inan struct input_dev *input = data->input; 1637e577a17SAhmet Inan u8 *buf = data->buf; 1647e577a17SAhmet Inan int slots, total_slots; 1657e577a17SAhmet Inan int error; 1667e577a17SAhmet Inan 1673bdd21c6SSebastian Reichel error = exc3000_read_data(data, buf, &total_slots); 1687e577a17SAhmet Inan if (error) { 1697e577a17SAhmet Inan /* Schedule a timer to release "stuck" contacts */ 1707e577a17SAhmet Inan mod_timer(&data->timer, 1717e577a17SAhmet Inan jiffies + msecs_to_jiffies(EXC3000_TIMEOUT_MS)); 1727e577a17SAhmet Inan goto out; 1737e577a17SAhmet Inan } 1747e577a17SAhmet Inan 1757e577a17SAhmet Inan /* 1767e577a17SAhmet Inan * We read full state successfully, no contacts will be "stuck". 1777e577a17SAhmet Inan */ 1787e577a17SAhmet Inan del_timer_sync(&data->timer); 1797e577a17SAhmet Inan 1807e577a17SAhmet Inan while (total_slots > 0) { 1817e577a17SAhmet Inan slots = min(total_slots, EXC3000_SLOTS_PER_FRAME); 1827e577a17SAhmet Inan exc3000_report_slots(input, &data->prop, buf + 4, slots); 1837e577a17SAhmet Inan total_slots -= slots; 1847e577a17SAhmet Inan buf += EXC3000_LEN_FRAME; 1857e577a17SAhmet Inan } 1867e577a17SAhmet Inan 1877e577a17SAhmet Inan input_mt_sync_frame(input); 1887e577a17SAhmet Inan input_sync(input); 1897e577a17SAhmet Inan 1907e577a17SAhmet Inan out: 1917e577a17SAhmet Inan return IRQ_HANDLED; 1927e577a17SAhmet Inan } 1937e577a17SAhmet Inan 194deae5764SSebastian Reichel static int exc3000_probe(struct i2c_client *client) 1957e577a17SAhmet Inan { 1967e577a17SAhmet Inan struct exc3000_data *data; 1977e577a17SAhmet Inan struct input_dev *input; 1983bdd21c6SSebastian Reichel int error, max_xy; 1997e577a17SAhmet Inan 2007e577a17SAhmet Inan data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); 2017e577a17SAhmet Inan if (!data) 2027e577a17SAhmet Inan return -ENOMEM; 2037e577a17SAhmet Inan 2047e577a17SAhmet Inan data->client = client; 2053bdd21c6SSebastian Reichel data->info = device_get_match_data(&client->dev); 2063bdd21c6SSebastian Reichel if (!data->info) { 2073bdd21c6SSebastian Reichel enum eeti_dev_id eeti_dev_id = 2083bdd21c6SSebastian Reichel i2c_match_id(exc3000_id, client)->driver_data; 2093bdd21c6SSebastian Reichel data->info = &exc3000_info[eeti_dev_id]; 2103bdd21c6SSebastian Reichel } 2117e577a17SAhmet Inan timer_setup(&data->timer, exc3000_timer, 0); 2127e577a17SAhmet Inan 213*27aced19SSebastian Reichel data->reset = devm_gpiod_get_optional(&client->dev, "reset", 214*27aced19SSebastian Reichel GPIOD_OUT_HIGH); 215*27aced19SSebastian Reichel if (IS_ERR(data->reset)) 216*27aced19SSebastian Reichel return PTR_ERR(data->reset); 217*27aced19SSebastian Reichel 218*27aced19SSebastian Reichel if (data->reset) { 219*27aced19SSebastian Reichel msleep(EXC3000_RESET_MS); 220*27aced19SSebastian Reichel gpiod_set_value_cansleep(data->reset, 0); 221*27aced19SSebastian Reichel msleep(EXC3000_READY_MS); 222*27aced19SSebastian Reichel } 223*27aced19SSebastian Reichel 2247e577a17SAhmet Inan input = devm_input_allocate_device(&client->dev); 2257e577a17SAhmet Inan if (!input) 2267e577a17SAhmet Inan return -ENOMEM; 2277e577a17SAhmet Inan 2287e577a17SAhmet Inan data->input = input; 2297e577a17SAhmet Inan 2303bdd21c6SSebastian Reichel input->name = data->info->name; 2317e577a17SAhmet Inan input->id.bustype = BUS_I2C; 2327e577a17SAhmet Inan 2333bdd21c6SSebastian Reichel max_xy = data->info->max_xy; 2343bdd21c6SSebastian Reichel input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_xy, 0, 0); 2353bdd21c6SSebastian Reichel input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_xy, 0, 0); 2363bdd21c6SSebastian Reichel 2377e577a17SAhmet Inan touchscreen_parse_properties(input, true, &data->prop); 2387e577a17SAhmet Inan 2397e577a17SAhmet Inan error = input_mt_init_slots(input, EXC3000_NUM_SLOTS, 2407e577a17SAhmet Inan INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); 2417e577a17SAhmet Inan if (error) 2427e577a17SAhmet Inan return error; 2437e577a17SAhmet Inan 2447e577a17SAhmet Inan error = input_register_device(input); 2457e577a17SAhmet Inan if (error) 2467e577a17SAhmet Inan return error; 2477e577a17SAhmet Inan 2487e577a17SAhmet Inan error = devm_request_threaded_irq(&client->dev, client->irq, 2497e577a17SAhmet Inan NULL, exc3000_interrupt, IRQF_ONESHOT, 2507e577a17SAhmet Inan client->name, data); 2517e577a17SAhmet Inan if (error) 2527e577a17SAhmet Inan return error; 2537e577a17SAhmet Inan 2547e577a17SAhmet Inan return 0; 2557e577a17SAhmet Inan } 2567e577a17SAhmet Inan 2577e577a17SAhmet Inan static const struct i2c_device_id exc3000_id[] = { 2583bdd21c6SSebastian Reichel { "exc3000", EETI_EXC3000 }, 2593bdd21c6SSebastian Reichel { "exc80h60", EETI_EXC80H60 }, 2603bdd21c6SSebastian Reichel { "exc80h84", EETI_EXC80H84 }, 2617e577a17SAhmet Inan { } 2627e577a17SAhmet Inan }; 2637e577a17SAhmet Inan MODULE_DEVICE_TABLE(i2c, exc3000_id); 2647e577a17SAhmet Inan 2657e577a17SAhmet Inan #ifdef CONFIG_OF 2667e577a17SAhmet Inan static const struct of_device_id exc3000_of_match[] = { 2673bdd21c6SSebastian Reichel { .compatible = "eeti,exc3000", .data = &exc3000_info[EETI_EXC3000] }, 2683bdd21c6SSebastian Reichel { .compatible = "eeti,exc80h60", .data = &exc3000_info[EETI_EXC80H60] }, 2693bdd21c6SSebastian Reichel { .compatible = "eeti,exc80h84", .data = &exc3000_info[EETI_EXC80H84] }, 2707e577a17SAhmet Inan { } 2717e577a17SAhmet Inan }; 2727e577a17SAhmet Inan MODULE_DEVICE_TABLE(of, exc3000_of_match); 2737e577a17SAhmet Inan #endif 2747e577a17SAhmet Inan 2757e577a17SAhmet Inan static struct i2c_driver exc3000_driver = { 2767e577a17SAhmet Inan .driver = { 2777e577a17SAhmet Inan .name = "exc3000", 2787e577a17SAhmet Inan .of_match_table = of_match_ptr(exc3000_of_match), 2797e577a17SAhmet Inan }, 2807e577a17SAhmet Inan .id_table = exc3000_id, 281deae5764SSebastian Reichel .probe_new = exc3000_probe, 2827e577a17SAhmet Inan }; 2837e577a17SAhmet Inan 2847e577a17SAhmet Inan module_i2c_driver(exc3000_driver); 2857e577a17SAhmet Inan 2867e577a17SAhmet Inan MODULE_AUTHOR("Ahmet Inan <inan@distec.de>"); 2877e577a17SAhmet Inan MODULE_DESCRIPTION("I2C connected EETI EXC3000 multiple touch controller driver"); 2887e577a17SAhmet Inan MODULE_LICENSE("GPL v2"); 289