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> 117e577a17SAhmet Inan #include <linux/device.h> 127e577a17SAhmet Inan #include <linux/i2c.h> 137e577a17SAhmet Inan #include <linux/input.h> 147e577a17SAhmet Inan #include <linux/input/mt.h> 157e577a17SAhmet Inan #include <linux/input/touchscreen.h> 167e577a17SAhmet Inan #include <linux/interrupt.h> 177e577a17SAhmet Inan #include <linux/module.h> 187e577a17SAhmet Inan #include <linux/of.h> 19*3bdd21c6SSebastian Reichel #include <linux/sizes.h> 207e577a17SAhmet Inan #include <linux/timer.h> 217e577a17SAhmet Inan #include <asm/unaligned.h> 227e577a17SAhmet Inan 237e577a17SAhmet Inan #define EXC3000_NUM_SLOTS 10 247e577a17SAhmet Inan #define EXC3000_SLOTS_PER_FRAME 5 257e577a17SAhmet Inan #define EXC3000_LEN_FRAME 66 267e577a17SAhmet Inan #define EXC3000_LEN_POINT 10 27*3bdd21c6SSebastian Reichel 28*3bdd21c6SSebastian Reichel #define EXC3000_MT1_EVENT 0x06 29*3bdd21c6SSebastian Reichel #define EXC3000_MT2_EVENT 0x18 30*3bdd21c6SSebastian Reichel 317e577a17SAhmet Inan #define EXC3000_TIMEOUT_MS 100 327e577a17SAhmet Inan 33*3bdd21c6SSebastian Reichel static const struct i2c_device_id exc3000_id[]; 34*3bdd21c6SSebastian Reichel 35*3bdd21c6SSebastian Reichel struct eeti_dev_info { 36*3bdd21c6SSebastian Reichel const char *name; 37*3bdd21c6SSebastian Reichel int max_xy; 38*3bdd21c6SSebastian Reichel }; 39*3bdd21c6SSebastian Reichel 40*3bdd21c6SSebastian Reichel enum eeti_dev_id { 41*3bdd21c6SSebastian Reichel EETI_EXC3000, 42*3bdd21c6SSebastian Reichel EETI_EXC80H60, 43*3bdd21c6SSebastian Reichel EETI_EXC80H84, 44*3bdd21c6SSebastian Reichel }; 45*3bdd21c6SSebastian Reichel 46*3bdd21c6SSebastian Reichel static struct eeti_dev_info exc3000_info[] = { 47*3bdd21c6SSebastian Reichel [EETI_EXC3000] = { 48*3bdd21c6SSebastian Reichel .name = "EETI EXC3000 Touch Screen", 49*3bdd21c6SSebastian Reichel .max_xy = SZ_4K - 1, 50*3bdd21c6SSebastian Reichel }, 51*3bdd21c6SSebastian Reichel [EETI_EXC80H60] = { 52*3bdd21c6SSebastian Reichel .name = "EETI EXC80H60 Touch Screen", 53*3bdd21c6SSebastian Reichel .max_xy = SZ_16K - 1, 54*3bdd21c6SSebastian Reichel }, 55*3bdd21c6SSebastian Reichel [EETI_EXC80H84] = { 56*3bdd21c6SSebastian Reichel .name = "EETI EXC80H84 Touch Screen", 57*3bdd21c6SSebastian Reichel .max_xy = SZ_16K - 1, 58*3bdd21c6SSebastian Reichel }, 59*3bdd21c6SSebastian Reichel }; 60*3bdd21c6SSebastian Reichel 617e577a17SAhmet Inan struct exc3000_data { 627e577a17SAhmet Inan struct i2c_client *client; 63*3bdd21c6SSebastian Reichel const struct eeti_dev_info *info; 647e577a17SAhmet Inan struct input_dev *input; 657e577a17SAhmet Inan struct touchscreen_properties prop; 667e577a17SAhmet Inan struct timer_list timer; 677e577a17SAhmet Inan u8 buf[2 * EXC3000_LEN_FRAME]; 687e577a17SAhmet Inan }; 697e577a17SAhmet Inan 707e577a17SAhmet Inan static void exc3000_report_slots(struct input_dev *input, 717e577a17SAhmet Inan struct touchscreen_properties *prop, 727e577a17SAhmet Inan const u8 *buf, int num) 737e577a17SAhmet Inan { 747e577a17SAhmet Inan for (; num--; buf += EXC3000_LEN_POINT) { 757e577a17SAhmet Inan if (buf[0] & BIT(0)) { 767e577a17SAhmet Inan input_mt_slot(input, buf[1]); 777e577a17SAhmet Inan input_mt_report_slot_state(input, MT_TOOL_FINGER, true); 787e577a17SAhmet Inan touchscreen_report_pos(input, prop, 797e577a17SAhmet Inan get_unaligned_le16(buf + 2), 807e577a17SAhmet Inan get_unaligned_le16(buf + 4), 817e577a17SAhmet Inan true); 827e577a17SAhmet Inan } 837e577a17SAhmet Inan } 847e577a17SAhmet Inan } 857e577a17SAhmet Inan 867e577a17SAhmet Inan static void exc3000_timer(struct timer_list *t) 877e577a17SAhmet Inan { 887e577a17SAhmet Inan struct exc3000_data *data = from_timer(data, t, timer); 897e577a17SAhmet Inan 907e577a17SAhmet Inan input_mt_sync_frame(data->input); 917e577a17SAhmet Inan input_sync(data->input); 927e577a17SAhmet Inan } 937e577a17SAhmet Inan 94*3bdd21c6SSebastian Reichel static int exc3000_read_frame(struct exc3000_data *data, u8 *buf) 957e577a17SAhmet Inan { 96*3bdd21c6SSebastian Reichel struct i2c_client *client = data->client; 97*3bdd21c6SSebastian Reichel u8 expected_event = EXC3000_MT1_EVENT; 987e577a17SAhmet Inan int ret; 997e577a17SAhmet Inan 100*3bdd21c6SSebastian Reichel if (data->info->max_xy == SZ_16K - 1) 101*3bdd21c6SSebastian Reichel expected_event = EXC3000_MT2_EVENT; 102*3bdd21c6SSebastian Reichel 1037e577a17SAhmet Inan ret = i2c_master_send(client, "'", 2); 1047e577a17SAhmet Inan if (ret < 0) 1057e577a17SAhmet Inan return ret; 1067e577a17SAhmet Inan 1077e577a17SAhmet Inan if (ret != 2) 1087e577a17SAhmet Inan return -EIO; 1097e577a17SAhmet Inan 1107e577a17SAhmet Inan ret = i2c_master_recv(client, buf, EXC3000_LEN_FRAME); 1117e577a17SAhmet Inan if (ret < 0) 1127e577a17SAhmet Inan return ret; 1137e577a17SAhmet Inan 1147e577a17SAhmet Inan if (ret != EXC3000_LEN_FRAME) 1157e577a17SAhmet Inan return -EIO; 1167e577a17SAhmet Inan 117*3bdd21c6SSebastian Reichel if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME) 118*3bdd21c6SSebastian Reichel return -EINVAL; 119*3bdd21c6SSebastian Reichel 120*3bdd21c6SSebastian Reichel if (buf[2] != expected_event) 1217e577a17SAhmet Inan return -EINVAL; 1227e577a17SAhmet Inan 1237e577a17SAhmet Inan return 0; 1247e577a17SAhmet Inan } 1257e577a17SAhmet Inan 126*3bdd21c6SSebastian Reichel static int exc3000_read_data(struct exc3000_data *data, 1277e577a17SAhmet Inan u8 *buf, int *n_slots) 1287e577a17SAhmet Inan { 1297e577a17SAhmet Inan int error; 1307e577a17SAhmet Inan 131*3bdd21c6SSebastian Reichel error = exc3000_read_frame(data, buf); 1327e577a17SAhmet Inan if (error) 1337e577a17SAhmet Inan return error; 1347e577a17SAhmet Inan 1357e577a17SAhmet Inan *n_slots = buf[3]; 1367e577a17SAhmet Inan if (!*n_slots || *n_slots > EXC3000_NUM_SLOTS) 1377e577a17SAhmet Inan return -EINVAL; 1387e577a17SAhmet Inan 1397e577a17SAhmet Inan if (*n_slots > EXC3000_SLOTS_PER_FRAME) { 1407e577a17SAhmet Inan /* Read 2nd frame to get the rest of the contacts. */ 141*3bdd21c6SSebastian Reichel error = exc3000_read_frame(data, buf + EXC3000_LEN_FRAME); 1427e577a17SAhmet Inan if (error) 1437e577a17SAhmet Inan return error; 1447e577a17SAhmet Inan 1457e577a17SAhmet Inan /* 2nd chunk must have number of contacts set to 0. */ 1467e577a17SAhmet Inan if (buf[EXC3000_LEN_FRAME + 3] != 0) 1477e577a17SAhmet Inan return -EINVAL; 1487e577a17SAhmet Inan } 1497e577a17SAhmet Inan 1507e577a17SAhmet Inan return 0; 1517e577a17SAhmet Inan } 1527e577a17SAhmet Inan 1537e577a17SAhmet Inan static irqreturn_t exc3000_interrupt(int irq, void *dev_id) 1547e577a17SAhmet Inan { 1557e577a17SAhmet Inan struct exc3000_data *data = dev_id; 1567e577a17SAhmet Inan struct input_dev *input = data->input; 1577e577a17SAhmet Inan u8 *buf = data->buf; 1587e577a17SAhmet Inan int slots, total_slots; 1597e577a17SAhmet Inan int error; 1607e577a17SAhmet Inan 161*3bdd21c6SSebastian Reichel error = exc3000_read_data(data, buf, &total_slots); 1627e577a17SAhmet Inan if (error) { 1637e577a17SAhmet Inan /* Schedule a timer to release "stuck" contacts */ 1647e577a17SAhmet Inan mod_timer(&data->timer, 1657e577a17SAhmet Inan jiffies + msecs_to_jiffies(EXC3000_TIMEOUT_MS)); 1667e577a17SAhmet Inan goto out; 1677e577a17SAhmet Inan } 1687e577a17SAhmet Inan 1697e577a17SAhmet Inan /* 1707e577a17SAhmet Inan * We read full state successfully, no contacts will be "stuck". 1717e577a17SAhmet Inan */ 1727e577a17SAhmet Inan del_timer_sync(&data->timer); 1737e577a17SAhmet Inan 1747e577a17SAhmet Inan while (total_slots > 0) { 1757e577a17SAhmet Inan slots = min(total_slots, EXC3000_SLOTS_PER_FRAME); 1767e577a17SAhmet Inan exc3000_report_slots(input, &data->prop, buf + 4, slots); 1777e577a17SAhmet Inan total_slots -= slots; 1787e577a17SAhmet Inan buf += EXC3000_LEN_FRAME; 1797e577a17SAhmet Inan } 1807e577a17SAhmet Inan 1817e577a17SAhmet Inan input_mt_sync_frame(input); 1827e577a17SAhmet Inan input_sync(input); 1837e577a17SAhmet Inan 1847e577a17SAhmet Inan out: 1857e577a17SAhmet Inan return IRQ_HANDLED; 1867e577a17SAhmet Inan } 1877e577a17SAhmet Inan 188deae5764SSebastian Reichel static int exc3000_probe(struct i2c_client *client) 1897e577a17SAhmet Inan { 1907e577a17SAhmet Inan struct exc3000_data *data; 1917e577a17SAhmet Inan struct input_dev *input; 192*3bdd21c6SSebastian Reichel int error, max_xy; 1937e577a17SAhmet Inan 1947e577a17SAhmet Inan data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); 1957e577a17SAhmet Inan if (!data) 1967e577a17SAhmet Inan return -ENOMEM; 1977e577a17SAhmet Inan 1987e577a17SAhmet Inan data->client = client; 199*3bdd21c6SSebastian Reichel data->info = device_get_match_data(&client->dev); 200*3bdd21c6SSebastian Reichel if (!data->info) { 201*3bdd21c6SSebastian Reichel enum eeti_dev_id eeti_dev_id = 202*3bdd21c6SSebastian Reichel i2c_match_id(exc3000_id, client)->driver_data; 203*3bdd21c6SSebastian Reichel data->info = &exc3000_info[eeti_dev_id]; 204*3bdd21c6SSebastian Reichel } 2057e577a17SAhmet Inan timer_setup(&data->timer, exc3000_timer, 0); 2067e577a17SAhmet Inan 2077e577a17SAhmet Inan input = devm_input_allocate_device(&client->dev); 2087e577a17SAhmet Inan if (!input) 2097e577a17SAhmet Inan return -ENOMEM; 2107e577a17SAhmet Inan 2117e577a17SAhmet Inan data->input = input; 2127e577a17SAhmet Inan 213*3bdd21c6SSebastian Reichel input->name = data->info->name; 2147e577a17SAhmet Inan input->id.bustype = BUS_I2C; 2157e577a17SAhmet Inan 216*3bdd21c6SSebastian Reichel max_xy = data->info->max_xy; 217*3bdd21c6SSebastian Reichel input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_xy, 0, 0); 218*3bdd21c6SSebastian Reichel input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_xy, 0, 0); 219*3bdd21c6SSebastian Reichel 2207e577a17SAhmet Inan touchscreen_parse_properties(input, true, &data->prop); 2217e577a17SAhmet Inan 2227e577a17SAhmet Inan error = input_mt_init_slots(input, EXC3000_NUM_SLOTS, 2237e577a17SAhmet Inan INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); 2247e577a17SAhmet Inan if (error) 2257e577a17SAhmet Inan return error; 2267e577a17SAhmet Inan 2277e577a17SAhmet Inan error = input_register_device(input); 2287e577a17SAhmet Inan if (error) 2297e577a17SAhmet Inan return error; 2307e577a17SAhmet Inan 2317e577a17SAhmet Inan error = devm_request_threaded_irq(&client->dev, client->irq, 2327e577a17SAhmet Inan NULL, exc3000_interrupt, IRQF_ONESHOT, 2337e577a17SAhmet Inan client->name, data); 2347e577a17SAhmet Inan if (error) 2357e577a17SAhmet Inan return error; 2367e577a17SAhmet Inan 2377e577a17SAhmet Inan return 0; 2387e577a17SAhmet Inan } 2397e577a17SAhmet Inan 2407e577a17SAhmet Inan static const struct i2c_device_id exc3000_id[] = { 241*3bdd21c6SSebastian Reichel { "exc3000", EETI_EXC3000 }, 242*3bdd21c6SSebastian Reichel { "exc80h60", EETI_EXC80H60 }, 243*3bdd21c6SSebastian Reichel { "exc80h84", EETI_EXC80H84 }, 2447e577a17SAhmet Inan { } 2457e577a17SAhmet Inan }; 2467e577a17SAhmet Inan MODULE_DEVICE_TABLE(i2c, exc3000_id); 2477e577a17SAhmet Inan 2487e577a17SAhmet Inan #ifdef CONFIG_OF 2497e577a17SAhmet Inan static const struct of_device_id exc3000_of_match[] = { 250*3bdd21c6SSebastian Reichel { .compatible = "eeti,exc3000", .data = &exc3000_info[EETI_EXC3000] }, 251*3bdd21c6SSebastian Reichel { .compatible = "eeti,exc80h60", .data = &exc3000_info[EETI_EXC80H60] }, 252*3bdd21c6SSebastian Reichel { .compatible = "eeti,exc80h84", .data = &exc3000_info[EETI_EXC80H84] }, 2537e577a17SAhmet Inan { } 2547e577a17SAhmet Inan }; 2557e577a17SAhmet Inan MODULE_DEVICE_TABLE(of, exc3000_of_match); 2567e577a17SAhmet Inan #endif 2577e577a17SAhmet Inan 2587e577a17SAhmet Inan static struct i2c_driver exc3000_driver = { 2597e577a17SAhmet Inan .driver = { 2607e577a17SAhmet Inan .name = "exc3000", 2617e577a17SAhmet Inan .of_match_table = of_match_ptr(exc3000_of_match), 2627e577a17SAhmet Inan }, 2637e577a17SAhmet Inan .id_table = exc3000_id, 264deae5764SSebastian Reichel .probe_new = exc3000_probe, 2657e577a17SAhmet Inan }; 2667e577a17SAhmet Inan 2677e577a17SAhmet Inan module_i2c_driver(exc3000_driver); 2687e577a17SAhmet Inan 2697e577a17SAhmet Inan MODULE_AUTHOR("Ahmet Inan <inan@distec.de>"); 2707e577a17SAhmet Inan MODULE_DESCRIPTION("I2C connected EETI EXC3000 multiple touch controller driver"); 2717e577a17SAhmet Inan MODULE_LICENSE("GPL v2"); 272