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