1 /* 2 * Driver for I2C connected EETI EXC3000 multiple touch controller 3 * 4 * Copyright (C) 2017 Ahmet Inan <inan@distec.de> 5 * 6 * 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 */ 12 13 #include <linux/bitops.h> 14 #include <linux/device.h> 15 #include <linux/i2c.h> 16 #include <linux/input.h> 17 #include <linux/input/mt.h> 18 #include <linux/input/touchscreen.h> 19 #include <linux/interrupt.h> 20 #include <linux/module.h> 21 #include <linux/of.h> 22 #include <linux/timer.h> 23 #include <asm/unaligned.h> 24 25 #define EXC3000_NUM_SLOTS 10 26 #define EXC3000_SLOTS_PER_FRAME 5 27 #define EXC3000_LEN_FRAME 66 28 #define EXC3000_LEN_POINT 10 29 #define EXC3000_MT_EVENT 6 30 #define EXC3000_TIMEOUT_MS 100 31 32 struct exc3000_data { 33 struct i2c_client *client; 34 struct input_dev *input; 35 struct touchscreen_properties prop; 36 struct timer_list timer; 37 u8 buf[2 * EXC3000_LEN_FRAME]; 38 }; 39 40 static void exc3000_report_slots(struct input_dev *input, 41 struct touchscreen_properties *prop, 42 const u8 *buf, int num) 43 { 44 for (; num--; buf += EXC3000_LEN_POINT) { 45 if (buf[0] & BIT(0)) { 46 input_mt_slot(input, buf[1]); 47 input_mt_report_slot_state(input, MT_TOOL_FINGER, true); 48 touchscreen_report_pos(input, prop, 49 get_unaligned_le16(buf + 2), 50 get_unaligned_le16(buf + 4), 51 true); 52 } 53 } 54 } 55 56 static void exc3000_timer(struct timer_list *t) 57 { 58 struct exc3000_data *data = from_timer(data, t, timer); 59 60 input_mt_sync_frame(data->input); 61 input_sync(data->input); 62 } 63 64 static int exc3000_read_frame(struct i2c_client *client, u8 *buf) 65 { 66 int ret; 67 68 ret = i2c_master_send(client, "'", 2); 69 if (ret < 0) 70 return ret; 71 72 if (ret != 2) 73 return -EIO; 74 75 ret = i2c_master_recv(client, buf, EXC3000_LEN_FRAME); 76 if (ret < 0) 77 return ret; 78 79 if (ret != EXC3000_LEN_FRAME) 80 return -EIO; 81 82 if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME || 83 buf[2] != EXC3000_MT_EVENT) 84 return -EINVAL; 85 86 return 0; 87 } 88 89 static int exc3000_read_data(struct i2c_client *client, 90 u8 *buf, int *n_slots) 91 { 92 int error; 93 94 error = exc3000_read_frame(client, buf); 95 if (error) 96 return error; 97 98 *n_slots = buf[3]; 99 if (!*n_slots || *n_slots > EXC3000_NUM_SLOTS) 100 return -EINVAL; 101 102 if (*n_slots > EXC3000_SLOTS_PER_FRAME) { 103 /* Read 2nd frame to get the rest of the contacts. */ 104 error = exc3000_read_frame(client, buf + EXC3000_LEN_FRAME); 105 if (error) 106 return error; 107 108 /* 2nd chunk must have number of contacts set to 0. */ 109 if (buf[EXC3000_LEN_FRAME + 3] != 0) 110 return -EINVAL; 111 } 112 113 return 0; 114 } 115 116 static irqreturn_t exc3000_interrupt(int irq, void *dev_id) 117 { 118 struct exc3000_data *data = dev_id; 119 struct input_dev *input = data->input; 120 u8 *buf = data->buf; 121 int slots, total_slots; 122 int error; 123 124 error = exc3000_read_data(data->client, buf, &total_slots); 125 if (error) { 126 /* Schedule a timer to release "stuck" contacts */ 127 mod_timer(&data->timer, 128 jiffies + msecs_to_jiffies(EXC3000_TIMEOUT_MS)); 129 goto out; 130 } 131 132 /* 133 * We read full state successfully, no contacts will be "stuck". 134 */ 135 del_timer_sync(&data->timer); 136 137 while (total_slots > 0) { 138 slots = min(total_slots, EXC3000_SLOTS_PER_FRAME); 139 exc3000_report_slots(input, &data->prop, buf + 4, slots); 140 total_slots -= slots; 141 buf += EXC3000_LEN_FRAME; 142 } 143 144 input_mt_sync_frame(input); 145 input_sync(input); 146 147 out: 148 return IRQ_HANDLED; 149 } 150 151 static int exc3000_probe(struct i2c_client *client, 152 const struct i2c_device_id *id) 153 { 154 struct exc3000_data *data; 155 struct input_dev *input; 156 int error; 157 158 data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); 159 if (!data) 160 return -ENOMEM; 161 162 data->client = client; 163 timer_setup(&data->timer, exc3000_timer, 0); 164 165 input = devm_input_allocate_device(&client->dev); 166 if (!input) 167 return -ENOMEM; 168 169 data->input = input; 170 171 input->name = "EETI EXC3000 Touch Screen"; 172 input->id.bustype = BUS_I2C; 173 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); 176 touchscreen_parse_properties(input, true, &data->prop); 177 178 error = input_mt_init_slots(input, EXC3000_NUM_SLOTS, 179 INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); 180 if (error) 181 return error; 182 183 error = input_register_device(input); 184 if (error) 185 return error; 186 187 error = devm_request_threaded_irq(&client->dev, client->irq, 188 NULL, exc3000_interrupt, IRQF_ONESHOT, 189 client->name, data); 190 if (error) 191 return error; 192 193 return 0; 194 } 195 196 static const struct i2c_device_id exc3000_id[] = { 197 { "exc3000", 0 }, 198 { } 199 }; 200 MODULE_DEVICE_TABLE(i2c, exc3000_id); 201 202 #ifdef CONFIG_OF 203 static const struct of_device_id exc3000_of_match[] = { 204 { .compatible = "eeti,exc3000" }, 205 { } 206 }; 207 MODULE_DEVICE_TABLE(of, exc3000_of_match); 208 #endif 209 210 static struct i2c_driver exc3000_driver = { 211 .driver = { 212 .name = "exc3000", 213 .of_match_table = of_match_ptr(exc3000_of_match), 214 }, 215 .id_table = exc3000_id, 216 .probe = exc3000_probe, 217 }; 218 219 module_i2c_driver(exc3000_driver); 220 221 MODULE_AUTHOR("Ahmet Inan <inan@distec.de>"); 222 MODULE_DESCRIPTION("I2C connected EETI EXC3000 multiple touch controller driver"); 223 MODULE_LICENSE("GPL v2"); 224