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> 197e577a17SAhmet Inan #include <linux/timer.h> 207e577a17SAhmet Inan #include <asm/unaligned.h> 217e577a17SAhmet Inan 227e577a17SAhmet Inan #define EXC3000_NUM_SLOTS 10 237e577a17SAhmet Inan #define EXC3000_SLOTS_PER_FRAME 5 247e577a17SAhmet Inan #define EXC3000_LEN_FRAME 66 257e577a17SAhmet Inan #define EXC3000_LEN_POINT 10 267e577a17SAhmet Inan #define EXC3000_MT_EVENT 6 277e577a17SAhmet Inan #define EXC3000_TIMEOUT_MS 100 287e577a17SAhmet Inan 297e577a17SAhmet Inan struct exc3000_data { 307e577a17SAhmet Inan struct i2c_client *client; 317e577a17SAhmet Inan struct input_dev *input; 327e577a17SAhmet Inan struct touchscreen_properties prop; 337e577a17SAhmet Inan struct timer_list timer; 347e577a17SAhmet Inan u8 buf[2 * EXC3000_LEN_FRAME]; 357e577a17SAhmet Inan }; 367e577a17SAhmet Inan 377e577a17SAhmet Inan static void exc3000_report_slots(struct input_dev *input, 387e577a17SAhmet Inan struct touchscreen_properties *prop, 397e577a17SAhmet Inan const u8 *buf, int num) 407e577a17SAhmet Inan { 417e577a17SAhmet Inan for (; num--; buf += EXC3000_LEN_POINT) { 427e577a17SAhmet Inan if (buf[0] & BIT(0)) { 437e577a17SAhmet Inan input_mt_slot(input, buf[1]); 447e577a17SAhmet Inan input_mt_report_slot_state(input, MT_TOOL_FINGER, true); 457e577a17SAhmet Inan touchscreen_report_pos(input, prop, 467e577a17SAhmet Inan get_unaligned_le16(buf + 2), 477e577a17SAhmet Inan get_unaligned_le16(buf + 4), 487e577a17SAhmet Inan true); 497e577a17SAhmet Inan } 507e577a17SAhmet Inan } 517e577a17SAhmet Inan } 527e577a17SAhmet Inan 537e577a17SAhmet Inan static void exc3000_timer(struct timer_list *t) 547e577a17SAhmet Inan { 557e577a17SAhmet Inan struct exc3000_data *data = from_timer(data, t, timer); 567e577a17SAhmet Inan 577e577a17SAhmet Inan input_mt_sync_frame(data->input); 587e577a17SAhmet Inan input_sync(data->input); 597e577a17SAhmet Inan } 607e577a17SAhmet Inan 617e577a17SAhmet Inan static int exc3000_read_frame(struct i2c_client *client, u8 *buf) 627e577a17SAhmet Inan { 637e577a17SAhmet Inan int ret; 647e577a17SAhmet Inan 657e577a17SAhmet Inan ret = i2c_master_send(client, "'", 2); 667e577a17SAhmet Inan if (ret < 0) 677e577a17SAhmet Inan return ret; 687e577a17SAhmet Inan 697e577a17SAhmet Inan if (ret != 2) 707e577a17SAhmet Inan return -EIO; 717e577a17SAhmet Inan 727e577a17SAhmet Inan ret = i2c_master_recv(client, buf, EXC3000_LEN_FRAME); 737e577a17SAhmet Inan if (ret < 0) 747e577a17SAhmet Inan return ret; 757e577a17SAhmet Inan 767e577a17SAhmet Inan if (ret != EXC3000_LEN_FRAME) 777e577a17SAhmet Inan return -EIO; 787e577a17SAhmet Inan 797e577a17SAhmet Inan if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME || 807e577a17SAhmet Inan buf[2] != EXC3000_MT_EVENT) 817e577a17SAhmet Inan return -EINVAL; 827e577a17SAhmet Inan 837e577a17SAhmet Inan return 0; 847e577a17SAhmet Inan } 857e577a17SAhmet Inan 867e577a17SAhmet Inan static int exc3000_read_data(struct i2c_client *client, 877e577a17SAhmet Inan u8 *buf, int *n_slots) 887e577a17SAhmet Inan { 897e577a17SAhmet Inan int error; 907e577a17SAhmet Inan 917e577a17SAhmet Inan error = exc3000_read_frame(client, buf); 927e577a17SAhmet Inan if (error) 937e577a17SAhmet Inan return error; 947e577a17SAhmet Inan 957e577a17SAhmet Inan *n_slots = buf[3]; 967e577a17SAhmet Inan if (!*n_slots || *n_slots > EXC3000_NUM_SLOTS) 977e577a17SAhmet Inan return -EINVAL; 987e577a17SAhmet Inan 997e577a17SAhmet Inan if (*n_slots > EXC3000_SLOTS_PER_FRAME) { 1007e577a17SAhmet Inan /* Read 2nd frame to get the rest of the contacts. */ 1017e577a17SAhmet Inan error = exc3000_read_frame(client, buf + EXC3000_LEN_FRAME); 1027e577a17SAhmet Inan if (error) 1037e577a17SAhmet Inan return error; 1047e577a17SAhmet Inan 1057e577a17SAhmet Inan /* 2nd chunk must have number of contacts set to 0. */ 1067e577a17SAhmet Inan if (buf[EXC3000_LEN_FRAME + 3] != 0) 1077e577a17SAhmet Inan return -EINVAL; 1087e577a17SAhmet Inan } 1097e577a17SAhmet Inan 1107e577a17SAhmet Inan return 0; 1117e577a17SAhmet Inan } 1127e577a17SAhmet Inan 1137e577a17SAhmet Inan static irqreturn_t exc3000_interrupt(int irq, void *dev_id) 1147e577a17SAhmet Inan { 1157e577a17SAhmet Inan struct exc3000_data *data = dev_id; 1167e577a17SAhmet Inan struct input_dev *input = data->input; 1177e577a17SAhmet Inan u8 *buf = data->buf; 1187e577a17SAhmet Inan int slots, total_slots; 1197e577a17SAhmet Inan int error; 1207e577a17SAhmet Inan 1217e577a17SAhmet Inan error = exc3000_read_data(data->client, buf, &total_slots); 1227e577a17SAhmet Inan if (error) { 1237e577a17SAhmet Inan /* Schedule a timer to release "stuck" contacts */ 1247e577a17SAhmet Inan mod_timer(&data->timer, 1257e577a17SAhmet Inan jiffies + msecs_to_jiffies(EXC3000_TIMEOUT_MS)); 1267e577a17SAhmet Inan goto out; 1277e577a17SAhmet Inan } 1287e577a17SAhmet Inan 1297e577a17SAhmet Inan /* 1307e577a17SAhmet Inan * We read full state successfully, no contacts will be "stuck". 1317e577a17SAhmet Inan */ 1327e577a17SAhmet Inan del_timer_sync(&data->timer); 1337e577a17SAhmet Inan 1347e577a17SAhmet Inan while (total_slots > 0) { 1357e577a17SAhmet Inan slots = min(total_slots, EXC3000_SLOTS_PER_FRAME); 1367e577a17SAhmet Inan exc3000_report_slots(input, &data->prop, buf + 4, slots); 1377e577a17SAhmet Inan total_slots -= slots; 1387e577a17SAhmet Inan buf += EXC3000_LEN_FRAME; 1397e577a17SAhmet Inan } 1407e577a17SAhmet Inan 1417e577a17SAhmet Inan input_mt_sync_frame(input); 1427e577a17SAhmet Inan input_sync(input); 1437e577a17SAhmet Inan 1447e577a17SAhmet Inan out: 1457e577a17SAhmet Inan return IRQ_HANDLED; 1467e577a17SAhmet Inan } 1477e577a17SAhmet Inan 148*deae5764SSebastian Reichel static int exc3000_probe(struct i2c_client *client) 1497e577a17SAhmet Inan { 1507e577a17SAhmet Inan struct exc3000_data *data; 1517e577a17SAhmet Inan struct input_dev *input; 1527e577a17SAhmet Inan int error; 1537e577a17SAhmet Inan 1547e577a17SAhmet Inan data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); 1557e577a17SAhmet Inan if (!data) 1567e577a17SAhmet Inan return -ENOMEM; 1577e577a17SAhmet Inan 1587e577a17SAhmet Inan data->client = client; 1597e577a17SAhmet Inan timer_setup(&data->timer, exc3000_timer, 0); 1607e577a17SAhmet Inan 1617e577a17SAhmet Inan input = devm_input_allocate_device(&client->dev); 1627e577a17SAhmet Inan if (!input) 1637e577a17SAhmet Inan return -ENOMEM; 1647e577a17SAhmet Inan 1657e577a17SAhmet Inan data->input = input; 1667e577a17SAhmet Inan 1677e577a17SAhmet Inan input->name = "EETI EXC3000 Touch Screen"; 1687e577a17SAhmet Inan input->id.bustype = BUS_I2C; 1697e577a17SAhmet Inan 1707e577a17SAhmet Inan input_set_abs_params(input, ABS_MT_POSITION_X, 0, 4095, 0, 0); 1717e577a17SAhmet Inan input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 4095, 0, 0); 1727e577a17SAhmet Inan touchscreen_parse_properties(input, true, &data->prop); 1737e577a17SAhmet Inan 1747e577a17SAhmet Inan error = input_mt_init_slots(input, EXC3000_NUM_SLOTS, 1757e577a17SAhmet Inan INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); 1767e577a17SAhmet Inan if (error) 1777e577a17SAhmet Inan return error; 1787e577a17SAhmet Inan 1797e577a17SAhmet Inan error = input_register_device(input); 1807e577a17SAhmet Inan if (error) 1817e577a17SAhmet Inan return error; 1827e577a17SAhmet Inan 1837e577a17SAhmet Inan error = devm_request_threaded_irq(&client->dev, client->irq, 1847e577a17SAhmet Inan NULL, exc3000_interrupt, IRQF_ONESHOT, 1857e577a17SAhmet Inan client->name, data); 1867e577a17SAhmet Inan if (error) 1877e577a17SAhmet Inan return error; 1887e577a17SAhmet Inan 1897e577a17SAhmet Inan return 0; 1907e577a17SAhmet Inan } 1917e577a17SAhmet Inan 1927e577a17SAhmet Inan static const struct i2c_device_id exc3000_id[] = { 1937e577a17SAhmet Inan { "exc3000", 0 }, 1947e577a17SAhmet Inan { } 1957e577a17SAhmet Inan }; 1967e577a17SAhmet Inan MODULE_DEVICE_TABLE(i2c, exc3000_id); 1977e577a17SAhmet Inan 1987e577a17SAhmet Inan #ifdef CONFIG_OF 1997e577a17SAhmet Inan static const struct of_device_id exc3000_of_match[] = { 2007e577a17SAhmet Inan { .compatible = "eeti,exc3000" }, 2017e577a17SAhmet Inan { } 2027e577a17SAhmet Inan }; 2037e577a17SAhmet Inan MODULE_DEVICE_TABLE(of, exc3000_of_match); 2047e577a17SAhmet Inan #endif 2057e577a17SAhmet Inan 2067e577a17SAhmet Inan static struct i2c_driver exc3000_driver = { 2077e577a17SAhmet Inan .driver = { 2087e577a17SAhmet Inan .name = "exc3000", 2097e577a17SAhmet Inan .of_match_table = of_match_ptr(exc3000_of_match), 2107e577a17SAhmet Inan }, 2117e577a17SAhmet Inan .id_table = exc3000_id, 212*deae5764SSebastian Reichel .probe_new = exc3000_probe, 2137e577a17SAhmet Inan }; 2147e577a17SAhmet Inan 2157e577a17SAhmet Inan module_i2c_driver(exc3000_driver); 2167e577a17SAhmet Inan 2177e577a17SAhmet Inan MODULE_AUTHOR("Ahmet Inan <inan@distec.de>"); 2187e577a17SAhmet Inan MODULE_DESCRIPTION("I2C connected EETI EXC3000 multiple touch controller driver"); 2197e577a17SAhmet Inan MODULE_LICENSE("GPL v2"); 220