Commit | Line | Data |
---|---|---|
43c4d13e SB |
1 | /* |
2 | * Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de> | |
dac90dc2 | 3 | * Lothar Waßmann <LW@KARO-electronics.de> (DT support) |
43c4d13e SB |
4 | * |
5 | * This software is licensed under the terms of the GNU General Public | |
6 | * License version 2, as published by the Free Software Foundation, and | |
7 | * may be copied, distributed, and modified under those terms. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public | |
15 | * License along with this library; if not, write to the Free Software | |
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
17 | */ | |
18 | ||
19 | /* | |
20 | * This is a driver for the EDT "Polytouch" family of touch controllers | |
21 | * based on the FocalTech FT5x06 line of chips. | |
22 | * | |
23 | * Development of this driver has been sponsored by Glyn: | |
24 | * http://www.glyn.com/Products/Displays | |
25 | */ | |
26 | ||
27 | #include <linux/module.h> | |
28 | #include <linux/ratelimit.h> | |
29 | #include <linux/interrupt.h> | |
30 | #include <linux/input.h> | |
31 | #include <linux/i2c.h> | |
32 | #include <linux/uaccess.h> | |
33 | #include <linux/delay.h> | |
34 | #include <linux/debugfs.h> | |
35 | #include <linux/slab.h> | |
36 | #include <linux/gpio.h> | |
dac90dc2 | 37 | #include <linux/of_gpio.h> |
43c4d13e SB |
38 | #include <linux/input/mt.h> |
39 | #include <linux/input/edt-ft5x06.h> | |
40 | ||
41 | #define MAX_SUPPORT_POINTS 5 | |
42 | ||
43 | #define WORK_REGISTER_THRESHOLD 0x00 | |
44 | #define WORK_REGISTER_REPORT_RATE 0x08 | |
45 | #define WORK_REGISTER_GAIN 0x30 | |
46 | #define WORK_REGISTER_OFFSET 0x31 | |
47 | #define WORK_REGISTER_NUM_X 0x33 | |
48 | #define WORK_REGISTER_NUM_Y 0x34 | |
49 | ||
50 | #define WORK_REGISTER_OPMODE 0x3c | |
51 | #define FACTORY_REGISTER_OPMODE 0x01 | |
52 | ||
53 | #define TOUCH_EVENT_DOWN 0x00 | |
54 | #define TOUCH_EVENT_UP 0x01 | |
55 | #define TOUCH_EVENT_ON 0x02 | |
56 | #define TOUCH_EVENT_RESERVED 0x03 | |
57 | ||
58 | #define EDT_NAME_LEN 23 | |
59 | #define EDT_SWITCH_MODE_RETRIES 10 | |
60 | #define EDT_SWITCH_MODE_DELAY 5 /* msec */ | |
61 | #define EDT_RAW_DATA_RETRIES 100 | |
62 | #define EDT_RAW_DATA_DELAY 1 /* msec */ | |
63 | ||
64 | struct edt_ft5x06_ts_data { | |
65 | struct i2c_client *client; | |
66 | struct input_dev *input; | |
67 | u16 num_x; | |
68 | u16 num_y; | |
69 | ||
dac90dc2 LW |
70 | int reset_pin; |
71 | int irq_pin; | |
72 | int wake_pin; | |
73 | ||
43c4d13e SB |
74 | #if defined(CONFIG_DEBUG_FS) |
75 | struct dentry *debug_dir; | |
76 | u8 *raw_buffer; | |
77 | size_t raw_bufsize; | |
78 | #endif | |
79 | ||
80 | struct mutex mutex; | |
81 | bool factory_mode; | |
82 | int threshold; | |
83 | int gain; | |
84 | int offset; | |
85 | int report_rate; | |
86 | ||
87 | char name[EDT_NAME_LEN]; | |
88 | }; | |
89 | ||
90 | static int edt_ft5x06_ts_readwrite(struct i2c_client *client, | |
91 | u16 wr_len, u8 *wr_buf, | |
92 | u16 rd_len, u8 *rd_buf) | |
93 | { | |
94 | struct i2c_msg wrmsg[2]; | |
95 | int i = 0; | |
96 | int ret; | |
97 | ||
98 | if (wr_len) { | |
99 | wrmsg[i].addr = client->addr; | |
100 | wrmsg[i].flags = 0; | |
101 | wrmsg[i].len = wr_len; | |
102 | wrmsg[i].buf = wr_buf; | |
103 | i++; | |
104 | } | |
105 | if (rd_len) { | |
106 | wrmsg[i].addr = client->addr; | |
107 | wrmsg[i].flags = I2C_M_RD; | |
108 | wrmsg[i].len = rd_len; | |
109 | wrmsg[i].buf = rd_buf; | |
110 | i++; | |
111 | } | |
112 | ||
113 | ret = i2c_transfer(client->adapter, wrmsg, i); | |
114 | if (ret < 0) | |
115 | return ret; | |
116 | if (ret != i) | |
117 | return -EIO; | |
118 | ||
119 | return 0; | |
120 | } | |
121 | ||
122 | static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata, | |
123 | u8 *buf, int buflen) | |
124 | { | |
125 | int i; | |
126 | u8 crc = 0; | |
127 | ||
128 | for (i = 0; i < buflen - 1; i++) | |
129 | crc ^= buf[i]; | |
130 | ||
131 | if (crc != buf[buflen-1]) { | |
132 | dev_err_ratelimited(&tsdata->client->dev, | |
133 | "crc error: 0x%02x expected, got 0x%02x\n", | |
134 | crc, buf[buflen-1]); | |
135 | return false; | |
136 | } | |
137 | ||
138 | return true; | |
139 | } | |
140 | ||
141 | static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) | |
142 | { | |
143 | struct edt_ft5x06_ts_data *tsdata = dev_id; | |
144 | struct device *dev = &tsdata->client->dev; | |
145 | u8 cmd = 0xf9; | |
146 | u8 rdbuf[26]; | |
147 | int i, type, x, y, id; | |
148 | int error; | |
149 | ||
150 | memset(rdbuf, 0, sizeof(rdbuf)); | |
151 | ||
152 | error = edt_ft5x06_ts_readwrite(tsdata->client, | |
153 | sizeof(cmd), &cmd, | |
154 | sizeof(rdbuf), rdbuf); | |
155 | if (error) { | |
156 | dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n", | |
157 | error); | |
158 | goto out; | |
159 | } | |
160 | ||
161 | if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || rdbuf[2] != 26) { | |
162 | dev_err_ratelimited(dev, "Unexpected header: %02x%02x%02x!\n", | |
163 | rdbuf[0], rdbuf[1], rdbuf[2]); | |
164 | goto out; | |
165 | } | |
166 | ||
167 | if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, 26)) | |
168 | goto out; | |
169 | ||
170 | for (i = 0; i < MAX_SUPPORT_POINTS; i++) { | |
171 | u8 *buf = &rdbuf[i * 4 + 5]; | |
172 | bool down; | |
173 | ||
174 | type = buf[0] >> 6; | |
175 | /* ignore Reserved events */ | |
176 | if (type == TOUCH_EVENT_RESERVED) | |
177 | continue; | |
178 | ||
ee3e946e LW |
179 | /* ignore TOUCH_DOWN events, might have bogus coordinates */ |
180 | if (type == TOUCH_EVENT_DOWN) | |
181 | continue; | |
182 | ||
43c4d13e SB |
183 | x = ((buf[0] << 8) | buf[1]) & 0x0fff; |
184 | y = ((buf[2] << 8) | buf[3]) & 0x0fff; | |
185 | id = (buf[2] >> 4) & 0x0f; | |
1730d814 | 186 | down = type != TOUCH_EVENT_UP; |
43c4d13e SB |
187 | |
188 | input_mt_slot(tsdata->input, id); | |
189 | input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down); | |
190 | ||
191 | if (!down) | |
192 | continue; | |
193 | ||
194 | input_report_abs(tsdata->input, ABS_MT_POSITION_X, x); | |
195 | input_report_abs(tsdata->input, ABS_MT_POSITION_Y, y); | |
196 | } | |
197 | ||
198 | input_mt_report_pointer_emulation(tsdata->input, true); | |
199 | input_sync(tsdata->input); | |
200 | ||
201 | out: | |
202 | return IRQ_HANDLED; | |
203 | } | |
204 | ||
205 | static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, | |
206 | u8 addr, u8 value) | |
207 | { | |
208 | u8 wrbuf[4]; | |
209 | ||
210 | wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; | |
211 | wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; | |
212 | wrbuf[2] = value; | |
213 | wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; | |
214 | ||
215 | return edt_ft5x06_ts_readwrite(tsdata->client, 4, wrbuf, 0, NULL); | |
216 | } | |
217 | ||
218 | static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, | |
219 | u8 addr) | |
220 | { | |
221 | u8 wrbuf[2], rdbuf[2]; | |
222 | int error; | |
223 | ||
224 | wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; | |
225 | wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; | |
226 | wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40; | |
227 | ||
228 | error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, rdbuf); | |
229 | if (error) | |
230 | return error; | |
231 | ||
232 | if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) { | |
233 | dev_err(&tsdata->client->dev, | |
234 | "crc error: 0x%02x expected, got 0x%02x\n", | |
235 | wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], rdbuf[1]); | |
236 | return -EIO; | |
237 | } | |
238 | ||
239 | return rdbuf[0]; | |
240 | } | |
241 | ||
242 | struct edt_ft5x06_attribute { | |
243 | struct device_attribute dattr; | |
244 | size_t field_offset; | |
245 | u8 limit_low; | |
246 | u8 limit_high; | |
247 | u8 addr; | |
248 | }; | |
249 | ||
250 | #define EDT_ATTR(_field, _mode, _addr, _limit_low, _limit_high) \ | |
251 | struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \ | |
252 | .dattr = __ATTR(_field, _mode, \ | |
253 | edt_ft5x06_setting_show, \ | |
254 | edt_ft5x06_setting_store), \ | |
255 | .field_offset = \ | |
256 | offsetof(struct edt_ft5x06_ts_data, _field), \ | |
257 | .limit_low = _limit_low, \ | |
258 | .limit_high = _limit_high, \ | |
259 | .addr = _addr, \ | |
260 | } | |
261 | ||
262 | static ssize_t edt_ft5x06_setting_show(struct device *dev, | |
263 | struct device_attribute *dattr, | |
264 | char *buf) | |
265 | { | |
266 | struct i2c_client *client = to_i2c_client(dev); | |
267 | struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); | |
268 | struct edt_ft5x06_attribute *attr = | |
269 | container_of(dattr, struct edt_ft5x06_attribute, dattr); | |
1730d814 | 270 | u8 *field = (u8 *)tsdata + attr->field_offset; |
43c4d13e SB |
271 | int val; |
272 | size_t count = 0; | |
273 | int error = 0; | |
274 | ||
275 | mutex_lock(&tsdata->mutex); | |
276 | ||
277 | if (tsdata->factory_mode) { | |
278 | error = -EIO; | |
279 | goto out; | |
280 | } | |
281 | ||
282 | val = edt_ft5x06_register_read(tsdata, attr->addr); | |
283 | if (val < 0) { | |
284 | error = val; | |
285 | dev_err(&tsdata->client->dev, | |
286 | "Failed to fetch attribute %s, error %d\n", | |
287 | dattr->attr.name, error); | |
288 | goto out; | |
289 | } | |
290 | ||
291 | if (val != *field) { | |
292 | dev_warn(&tsdata->client->dev, | |
293 | "%s: read (%d) and stored value (%d) differ\n", | |
294 | dattr->attr.name, val, *field); | |
295 | *field = val; | |
296 | } | |
297 | ||
298 | count = scnprintf(buf, PAGE_SIZE, "%d\n", val); | |
299 | out: | |
300 | mutex_unlock(&tsdata->mutex); | |
301 | return error ?: count; | |
302 | } | |
303 | ||
304 | static ssize_t edt_ft5x06_setting_store(struct device *dev, | |
305 | struct device_attribute *dattr, | |
306 | const char *buf, size_t count) | |
307 | { | |
308 | struct i2c_client *client = to_i2c_client(dev); | |
309 | struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); | |
310 | struct edt_ft5x06_attribute *attr = | |
311 | container_of(dattr, struct edt_ft5x06_attribute, dattr); | |
1730d814 | 312 | u8 *field = (u8 *)tsdata + attr->field_offset; |
43c4d13e SB |
313 | unsigned int val; |
314 | int error; | |
315 | ||
316 | mutex_lock(&tsdata->mutex); | |
317 | ||
318 | if (tsdata->factory_mode) { | |
319 | error = -EIO; | |
320 | goto out; | |
321 | } | |
322 | ||
323 | error = kstrtouint(buf, 0, &val); | |
324 | if (error) | |
325 | goto out; | |
326 | ||
327 | if (val < attr->limit_low || val > attr->limit_high) { | |
328 | error = -ERANGE; | |
329 | goto out; | |
330 | } | |
331 | ||
332 | error = edt_ft5x06_register_write(tsdata, attr->addr, val); | |
333 | if (error) { | |
334 | dev_err(&tsdata->client->dev, | |
335 | "Failed to update attribute %s, error: %d\n", | |
336 | dattr->attr.name, error); | |
337 | goto out; | |
338 | } | |
339 | ||
340 | *field = val; | |
341 | ||
342 | out: | |
343 | mutex_unlock(&tsdata->mutex); | |
344 | return error ?: count; | |
345 | } | |
346 | ||
347 | static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, 0, 31); | |
348 | static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, 0, 31); | |
349 | static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, | |
350 | WORK_REGISTER_THRESHOLD, 20, 80); | |
351 | static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, | |
352 | WORK_REGISTER_REPORT_RATE, 3, 14); | |
353 | ||
354 | static struct attribute *edt_ft5x06_attrs[] = { | |
355 | &edt_ft5x06_attr_gain.dattr.attr, | |
356 | &edt_ft5x06_attr_offset.dattr.attr, | |
357 | &edt_ft5x06_attr_threshold.dattr.attr, | |
358 | &edt_ft5x06_attr_report_rate.dattr.attr, | |
359 | NULL | |
360 | }; | |
361 | ||
362 | static const struct attribute_group edt_ft5x06_attr_group = { | |
363 | .attrs = edt_ft5x06_attrs, | |
364 | }; | |
365 | ||
366 | #ifdef CONFIG_DEBUG_FS | |
367 | static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata) | |
368 | { | |
369 | struct i2c_client *client = tsdata->client; | |
370 | int retries = EDT_SWITCH_MODE_RETRIES; | |
371 | int ret; | |
372 | int error; | |
373 | ||
374 | disable_irq(client->irq); | |
375 | ||
376 | if (!tsdata->raw_buffer) { | |
377 | tsdata->raw_bufsize = tsdata->num_x * tsdata->num_y * | |
378 | sizeof(u16); | |
379 | tsdata->raw_buffer = kzalloc(tsdata->raw_bufsize, GFP_KERNEL); | |
380 | if (!tsdata->raw_buffer) { | |
381 | error = -ENOMEM; | |
382 | goto err_out; | |
383 | } | |
384 | } | |
385 | ||
386 | /* mode register is 0x3c when in the work mode */ | |
387 | error = edt_ft5x06_register_write(tsdata, WORK_REGISTER_OPMODE, 0x03); | |
388 | if (error) { | |
389 | dev_err(&client->dev, | |
390 | "failed to switch to factory mode, error %d\n", error); | |
391 | goto err_out; | |
392 | } | |
393 | ||
394 | tsdata->factory_mode = true; | |
395 | do { | |
396 | mdelay(EDT_SWITCH_MODE_DELAY); | |
397 | /* mode register is 0x01 when in factory mode */ | |
398 | ret = edt_ft5x06_register_read(tsdata, FACTORY_REGISTER_OPMODE); | |
399 | if (ret == 0x03) | |
400 | break; | |
401 | } while (--retries > 0); | |
402 | ||
403 | if (retries == 0) { | |
404 | dev_err(&client->dev, "not in factory mode after %dms.\n", | |
405 | EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY); | |
406 | error = -EIO; | |
407 | goto err_out; | |
408 | } | |
409 | ||
410 | return 0; | |
411 | ||
412 | err_out: | |
413 | kfree(tsdata->raw_buffer); | |
414 | tsdata->raw_buffer = NULL; | |
415 | tsdata->factory_mode = false; | |
416 | enable_irq(client->irq); | |
417 | ||
418 | return error; | |
419 | } | |
420 | ||
421 | static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) | |
422 | { | |
423 | struct i2c_client *client = tsdata->client; | |
424 | int retries = EDT_SWITCH_MODE_RETRIES; | |
425 | int ret; | |
426 | int error; | |
427 | ||
428 | /* mode register is 0x01 when in the factory mode */ | |
429 | error = edt_ft5x06_register_write(tsdata, FACTORY_REGISTER_OPMODE, 0x1); | |
430 | if (error) { | |
431 | dev_err(&client->dev, | |
432 | "failed to switch to work mode, error: %d\n", error); | |
433 | return error; | |
434 | } | |
435 | ||
436 | tsdata->factory_mode = false; | |
437 | ||
438 | do { | |
439 | mdelay(EDT_SWITCH_MODE_DELAY); | |
440 | /* mode register is 0x01 when in factory mode */ | |
441 | ret = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OPMODE); | |
442 | if (ret == 0x01) | |
443 | break; | |
444 | } while (--retries > 0); | |
445 | ||
446 | if (retries == 0) { | |
447 | dev_err(&client->dev, "not in work mode after %dms.\n", | |
448 | EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY); | |
449 | tsdata->factory_mode = true; | |
450 | return -EIO; | |
451 | } | |
452 | ||
8efcc503 | 453 | kfree(tsdata->raw_buffer); |
43c4d13e SB |
454 | tsdata->raw_buffer = NULL; |
455 | ||
456 | /* restore parameters */ | |
457 | edt_ft5x06_register_write(tsdata, WORK_REGISTER_THRESHOLD, | |
458 | tsdata->threshold); | |
459 | edt_ft5x06_register_write(tsdata, WORK_REGISTER_GAIN, | |
460 | tsdata->gain); | |
461 | edt_ft5x06_register_write(tsdata, WORK_REGISTER_OFFSET, | |
462 | tsdata->offset); | |
463 | edt_ft5x06_register_write(tsdata, WORK_REGISTER_REPORT_RATE, | |
464 | tsdata->report_rate); | |
465 | ||
466 | enable_irq(client->irq); | |
467 | ||
468 | return 0; | |
469 | } | |
470 | ||
471 | static int edt_ft5x06_debugfs_mode_get(void *data, u64 *mode) | |
472 | { | |
473 | struct edt_ft5x06_ts_data *tsdata = data; | |
474 | ||
475 | *mode = tsdata->factory_mode; | |
476 | ||
477 | return 0; | |
478 | }; | |
479 | ||
480 | static int edt_ft5x06_debugfs_mode_set(void *data, u64 mode) | |
481 | { | |
482 | struct edt_ft5x06_ts_data *tsdata = data; | |
483 | int retval = 0; | |
484 | ||
485 | if (mode > 1) | |
486 | return -ERANGE; | |
487 | ||
488 | mutex_lock(&tsdata->mutex); | |
489 | ||
490 | if (mode != tsdata->factory_mode) { | |
491 | retval = mode ? edt_ft5x06_factory_mode(tsdata) : | |
1730d814 | 492 | edt_ft5x06_work_mode(tsdata); |
43c4d13e SB |
493 | } |
494 | ||
495 | mutex_unlock(&tsdata->mutex); | |
496 | ||
497 | return retval; | |
498 | }; | |
499 | ||
500 | DEFINE_SIMPLE_ATTRIBUTE(debugfs_mode_fops, edt_ft5x06_debugfs_mode_get, | |
501 | edt_ft5x06_debugfs_mode_set, "%llu\n"); | |
502 | ||
43c4d13e SB |
503 | static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file, |
504 | char __user *buf, size_t count, loff_t *off) | |
505 | { | |
506 | struct edt_ft5x06_ts_data *tsdata = file->private_data; | |
507 | struct i2c_client *client = tsdata->client; | |
508 | int retries = EDT_RAW_DATA_RETRIES; | |
509 | int val, i, error; | |
510 | size_t read = 0; | |
511 | int colbytes; | |
512 | char wrbuf[3]; | |
513 | u8 *rdbuf; | |
514 | ||
515 | if (*off < 0 || *off >= tsdata->raw_bufsize) | |
516 | return 0; | |
517 | ||
518 | mutex_lock(&tsdata->mutex); | |
519 | ||
520 | if (!tsdata->factory_mode || !tsdata->raw_buffer) { | |
521 | error = -EIO; | |
522 | goto out; | |
523 | } | |
524 | ||
525 | error = edt_ft5x06_register_write(tsdata, 0x08, 0x01); | |
526 | if (error) { | |
527 | dev_dbg(&client->dev, | |
528 | "failed to write 0x08 register, error %d\n", error); | |
529 | goto out; | |
530 | } | |
531 | ||
532 | do { | |
533 | msleep(EDT_RAW_DATA_DELAY); | |
534 | val = edt_ft5x06_register_read(tsdata, 0x08); | |
535 | if (val < 1) | |
536 | break; | |
537 | } while (--retries > 0); | |
538 | ||
539 | if (val < 0) { | |
540 | error = val; | |
541 | dev_dbg(&client->dev, | |
542 | "failed to read 0x08 register, error %d\n", error); | |
543 | goto out; | |
544 | } | |
545 | ||
546 | if (retries == 0) { | |
547 | dev_dbg(&client->dev, | |
548 | "timed out waiting for register to settle\n"); | |
549 | error = -ETIMEDOUT; | |
550 | goto out; | |
551 | } | |
552 | ||
553 | rdbuf = tsdata->raw_buffer; | |
554 | colbytes = tsdata->num_y * sizeof(u16); | |
555 | ||
556 | wrbuf[0] = 0xf5; | |
557 | wrbuf[1] = 0x0e; | |
558 | for (i = 0; i < tsdata->num_x; i++) { | |
559 | wrbuf[2] = i; /* column index */ | |
560 | error = edt_ft5x06_ts_readwrite(tsdata->client, | |
561 | sizeof(wrbuf), wrbuf, | |
562 | colbytes, rdbuf); | |
563 | if (error) | |
564 | goto out; | |
565 | ||
566 | rdbuf += colbytes; | |
567 | } | |
568 | ||
569 | read = min_t(size_t, count, tsdata->raw_bufsize - *off); | |
35b1da4e AL |
570 | if (copy_to_user(buf, tsdata->raw_buffer + *off, read)) { |
571 | error = -EFAULT; | |
572 | goto out; | |
573 | } | |
574 | ||
575 | *off += read; | |
43c4d13e SB |
576 | out: |
577 | mutex_unlock(&tsdata->mutex); | |
578 | return error ?: read; | |
579 | }; | |
580 | ||
43c4d13e | 581 | static const struct file_operations debugfs_raw_data_fops = { |
f6c0df6a | 582 | .open = simple_open, |
43c4d13e SB |
583 | .read = edt_ft5x06_debugfs_raw_data_read, |
584 | }; | |
585 | ||
5298cc4c | 586 | static void |
43c4d13e SB |
587 | edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, |
588 | const char *debugfs_name) | |
589 | { | |
590 | tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL); | |
591 | if (!tsdata->debug_dir) | |
592 | return; | |
593 | ||
594 | debugfs_create_u16("num_x", S_IRUSR, tsdata->debug_dir, &tsdata->num_x); | |
595 | debugfs_create_u16("num_y", S_IRUSR, tsdata->debug_dir, &tsdata->num_y); | |
596 | ||
597 | debugfs_create_file("mode", S_IRUSR | S_IWUSR, | |
598 | tsdata->debug_dir, tsdata, &debugfs_mode_fops); | |
599 | debugfs_create_file("raw_data", S_IRUSR, | |
600 | tsdata->debug_dir, tsdata, &debugfs_raw_data_fops); | |
601 | } | |
602 | ||
e2619cf7 | 603 | static void |
43c4d13e SB |
604 | edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) |
605 | { | |
606 | if (tsdata->debug_dir) | |
607 | debugfs_remove_recursive(tsdata->debug_dir); | |
a1d0fa77 | 608 | kfree(tsdata->raw_buffer); |
43c4d13e SB |
609 | } |
610 | ||
611 | #else | |
612 | ||
613 | static inline void | |
614 | edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, | |
615 | const char *debugfs_name) | |
616 | { | |
617 | } | |
618 | ||
619 | static inline void | |
620 | edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) | |
621 | { | |
622 | } | |
623 | ||
624 | #endif /* CONFIG_DEBUGFS */ | |
625 | ||
5298cc4c | 626 | static int edt_ft5x06_ts_reset(struct i2c_client *client, |
dac90dc2 | 627 | struct edt_ft5x06_ts_data *tsdata) |
43c4d13e SB |
628 | { |
629 | int error; | |
630 | ||
dac90dc2 LW |
631 | if (gpio_is_valid(tsdata->wake_pin)) { |
632 | error = devm_gpio_request_one(&client->dev, | |
633 | tsdata->wake_pin, GPIOF_OUT_INIT_LOW, | |
634 | "edt-ft5x06 wake"); | |
635 | if (error) { | |
636 | dev_err(&client->dev, | |
637 | "Failed to request GPIO %d as wake pin, error %d\n", | |
638 | tsdata->wake_pin, error); | |
639 | return error; | |
640 | } | |
641 | ||
c0808467 | 642 | msleep(5); |
dac90dc2 LW |
643 | gpio_set_value(tsdata->wake_pin, 1); |
644 | } | |
645 | if (gpio_is_valid(tsdata->reset_pin)) { | |
43c4d13e | 646 | /* this pulls reset down, enabling the low active reset */ |
dac90dc2 LW |
647 | error = devm_gpio_request_one(&client->dev, |
648 | tsdata->reset_pin, GPIOF_OUT_INIT_LOW, | |
649 | "edt-ft5x06 reset"); | |
43c4d13e SB |
650 | if (error) { |
651 | dev_err(&client->dev, | |
652 | "Failed to request GPIO %d as reset pin, error %d\n", | |
dac90dc2 | 653 | tsdata->reset_pin, error); |
43c4d13e SB |
654 | return error; |
655 | } | |
656 | ||
c0808467 | 657 | msleep(5); |
dac90dc2 | 658 | gpio_set_value(tsdata->reset_pin, 1); |
c0808467 | 659 | msleep(300); |
43c4d13e SB |
660 | } |
661 | ||
662 | return 0; | |
663 | } | |
664 | ||
5298cc4c | 665 | static int edt_ft5x06_ts_identify(struct i2c_client *client, |
43c4d13e SB |
666 | char *model_name, |
667 | char *fw_version) | |
668 | { | |
669 | u8 rdbuf[EDT_NAME_LEN]; | |
670 | char *p; | |
671 | int error; | |
672 | ||
673 | error = edt_ft5x06_ts_readwrite(client, 1, "\xbb", | |
674 | EDT_NAME_LEN - 1, rdbuf); | |
675 | if (error) | |
676 | return error; | |
677 | ||
678 | /* remove last '$' end marker */ | |
679 | rdbuf[EDT_NAME_LEN - 1] = '\0'; | |
680 | if (rdbuf[EDT_NAME_LEN - 2] == '$') | |
681 | rdbuf[EDT_NAME_LEN - 2] = '\0'; | |
682 | ||
683 | /* look for Model/Version separator */ | |
684 | p = strchr(rdbuf, '*'); | |
685 | if (p) | |
686 | *p++ = '\0'; | |
687 | ||
688 | strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN); | |
689 | strlcpy(fw_version, p ? p : "", EDT_NAME_LEN); | |
690 | ||
691 | return 0; | |
692 | } | |
693 | ||
694 | #define EDT_ATTR_CHECKSET(name, reg) \ | |
695 | if (pdata->name >= edt_ft5x06_attr_##name.limit_low && \ | |
696 | pdata->name <= edt_ft5x06_attr_##name.limit_high) \ | |
697 | edt_ft5x06_register_write(tsdata, reg, pdata->name) | |
698 | ||
dac90dc2 LW |
699 | #define EDT_GET_PROP(name, reg) { \ |
700 | u32 val; \ | |
701 | if (of_property_read_u32(np, #name, &val) == 0) \ | |
702 | edt_ft5x06_register_write(tsdata, reg, val); \ | |
703 | } | |
704 | ||
705 | static void edt_ft5x06_ts_get_dt_defaults(struct device_node *np, | |
706 | struct edt_ft5x06_ts_data *tsdata) | |
707 | { | |
708 | EDT_GET_PROP(threshold, WORK_REGISTER_THRESHOLD); | |
709 | EDT_GET_PROP(gain, WORK_REGISTER_GAIN); | |
710 | EDT_GET_PROP(offset, WORK_REGISTER_OFFSET); | |
711 | } | |
712 | ||
5298cc4c | 713 | static void |
43c4d13e SB |
714 | edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata, |
715 | const struct edt_ft5x06_platform_data *pdata) | |
716 | { | |
717 | if (!pdata->use_parameters) | |
718 | return; | |
719 | ||
720 | /* pick up defaults from the platform data */ | |
721 | EDT_ATTR_CHECKSET(threshold, WORK_REGISTER_THRESHOLD); | |
722 | EDT_ATTR_CHECKSET(gain, WORK_REGISTER_GAIN); | |
723 | EDT_ATTR_CHECKSET(offset, WORK_REGISTER_OFFSET); | |
724 | EDT_ATTR_CHECKSET(report_rate, WORK_REGISTER_REPORT_RATE); | |
725 | } | |
726 | ||
5298cc4c | 727 | static void |
43c4d13e SB |
728 | edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata) |
729 | { | |
730 | tsdata->threshold = edt_ft5x06_register_read(tsdata, | |
731 | WORK_REGISTER_THRESHOLD); | |
732 | tsdata->gain = edt_ft5x06_register_read(tsdata, WORK_REGISTER_GAIN); | |
733 | tsdata->offset = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OFFSET); | |
734 | tsdata->report_rate = edt_ft5x06_register_read(tsdata, | |
735 | WORK_REGISTER_REPORT_RATE); | |
736 | tsdata->num_x = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_X); | |
737 | tsdata->num_y = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_Y); | |
738 | } | |
739 | ||
dac90dc2 LW |
740 | #ifdef CONFIG_OF |
741 | static int edt_ft5x06_i2c_ts_probe_dt(struct device *dev, | |
742 | struct edt_ft5x06_ts_data *tsdata) | |
743 | { | |
744 | struct device_node *np = dev->of_node; | |
745 | ||
746 | /* | |
747 | * irq_pin is not needed for DT setup. | |
748 | * irq is associated via 'interrupts' property in DT | |
749 | */ | |
750 | tsdata->irq_pin = -EINVAL; | |
751 | tsdata->reset_pin = of_get_named_gpio(np, "reset-gpios", 0); | |
752 | tsdata->wake_pin = of_get_named_gpio(np, "wake-gpios", 0); | |
753 | ||
754 | return 0; | |
755 | } | |
756 | #else | |
757 | static inline int edt_ft5x06_i2c_ts_probe_dt(struct device *dev, | |
758 | struct edt_ft5x06_ts_data *tsdata) | |
759 | { | |
760 | return -ENODEV; | |
761 | } | |
762 | #endif | |
763 | ||
5298cc4c | 764 | static int edt_ft5x06_ts_probe(struct i2c_client *client, |
43c4d13e SB |
765 | const struct i2c_device_id *id) |
766 | { | |
767 | const struct edt_ft5x06_platform_data *pdata = | |
c838cb3d | 768 | dev_get_platdata(&client->dev); |
43c4d13e SB |
769 | struct edt_ft5x06_ts_data *tsdata; |
770 | struct input_dev *input; | |
771 | int error; | |
772 | char fw_version[EDT_NAME_LEN]; | |
773 | ||
774 | dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n"); | |
775 | ||
dac90dc2 LW |
776 | tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL); |
777 | if (!tsdata) { | |
778 | dev_err(&client->dev, "failed to allocate driver data.\n"); | |
779 | return -ENOMEM; | |
780 | } | |
781 | ||
43c4d13e | 782 | if (!pdata) { |
dac90dc2 LW |
783 | error = edt_ft5x06_i2c_ts_probe_dt(&client->dev, tsdata); |
784 | if (error) { | |
785 | dev_err(&client->dev, | |
786 | "DT probe failed and no platform data present\n"); | |
787 | return error; | |
788 | } | |
789 | } else { | |
790 | tsdata->reset_pin = pdata->reset_pin; | |
791 | tsdata->irq_pin = pdata->irq_pin; | |
792 | tsdata->wake_pin = -EINVAL; | |
43c4d13e SB |
793 | } |
794 | ||
dac90dc2 | 795 | error = edt_ft5x06_ts_reset(client, tsdata); |
43c4d13e SB |
796 | if (error) |
797 | return error; | |
798 | ||
dac90dc2 LW |
799 | if (gpio_is_valid(tsdata->irq_pin)) { |
800 | error = devm_gpio_request_one(&client->dev, tsdata->irq_pin, | |
801 | GPIOF_IN, "edt-ft5x06 irq"); | |
43c4d13e SB |
802 | if (error) { |
803 | dev_err(&client->dev, | |
804 | "Failed to request GPIO %d, error %d\n", | |
dac90dc2 | 805 | tsdata->irq_pin, error); |
43c4d13e SB |
806 | return error; |
807 | } | |
808 | } | |
809 | ||
02300bd6 LW |
810 | input = devm_input_allocate_device(&client->dev); |
811 | if (!input) { | |
812 | dev_err(&client->dev, "failed to allocate input device.\n"); | |
813 | return -ENOMEM; | |
43c4d13e SB |
814 | } |
815 | ||
816 | mutex_init(&tsdata->mutex); | |
817 | tsdata->client = client; | |
818 | tsdata->input = input; | |
819 | tsdata->factory_mode = false; | |
820 | ||
821 | error = edt_ft5x06_ts_identify(client, tsdata->name, fw_version); | |
822 | if (error) { | |
823 | dev_err(&client->dev, "touchscreen probe failed\n"); | |
02300bd6 | 824 | return error; |
43c4d13e SB |
825 | } |
826 | ||
dac90dc2 LW |
827 | if (!pdata) |
828 | edt_ft5x06_ts_get_dt_defaults(client->dev.of_node, tsdata); | |
829 | else | |
830 | edt_ft5x06_ts_get_defaults(tsdata, pdata); | |
831 | ||
43c4d13e SB |
832 | edt_ft5x06_ts_get_parameters(tsdata); |
833 | ||
834 | dev_dbg(&client->dev, | |
835 | "Model \"%s\", Rev. \"%s\", %dx%d sensors\n", | |
836 | tsdata->name, fw_version, tsdata->num_x, tsdata->num_y); | |
837 | ||
838 | input->name = tsdata->name; | |
839 | input->id.bustype = BUS_I2C; | |
840 | input->dev.parent = &client->dev; | |
841 | ||
842 | __set_bit(EV_SYN, input->evbit); | |
843 | __set_bit(EV_KEY, input->evbit); | |
844 | __set_bit(EV_ABS, input->evbit); | |
845 | __set_bit(BTN_TOUCH, input->keybit); | |
846 | input_set_abs_params(input, ABS_X, 0, tsdata->num_x * 64 - 1, 0, 0); | |
847 | input_set_abs_params(input, ABS_Y, 0, tsdata->num_y * 64 - 1, 0, 0); | |
848 | input_set_abs_params(input, ABS_MT_POSITION_X, | |
849 | 0, tsdata->num_x * 64 - 1, 0, 0); | |
850 | input_set_abs_params(input, ABS_MT_POSITION_Y, | |
851 | 0, tsdata->num_y * 64 - 1, 0, 0); | |
b4adbbef | 852 | error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0); |
43c4d13e SB |
853 | if (error) { |
854 | dev_err(&client->dev, "Unable to init MT slots.\n"); | |
02300bd6 | 855 | return error; |
43c4d13e SB |
856 | } |
857 | ||
858 | input_set_drvdata(input, tsdata); | |
859 | i2c_set_clientdata(client, tsdata); | |
860 | ||
dac90dc2 LW |
861 | error = devm_request_threaded_irq(&client->dev, client->irq, NULL, |
862 | edt_ft5x06_ts_isr, | |
863 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | |
864 | client->name, tsdata); | |
43c4d13e SB |
865 | if (error) { |
866 | dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); | |
02300bd6 | 867 | return error; |
43c4d13e SB |
868 | } |
869 | ||
870 | error = sysfs_create_group(&client->dev.kobj, &edt_ft5x06_attr_group); | |
871 | if (error) | |
02300bd6 | 872 | return error; |
43c4d13e SB |
873 | |
874 | error = input_register_device(input); | |
dac90dc2 LW |
875 | if (error) |
876 | goto err_remove_attrs; | |
43c4d13e SB |
877 | |
878 | edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev)); | |
879 | device_init_wakeup(&client->dev, 1); | |
880 | ||
881 | dev_dbg(&client->dev, | |
dac90dc2 LW |
882 | "EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n", |
883 | client->irq, tsdata->wake_pin, tsdata->reset_pin); | |
43c4d13e SB |
884 | |
885 | return 0; | |
dac90dc2 LW |
886 | |
887 | err_remove_attrs: | |
888 | sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group); | |
889 | return error; | |
43c4d13e SB |
890 | } |
891 | ||
e2619cf7 | 892 | static int edt_ft5x06_ts_remove(struct i2c_client *client) |
43c4d13e | 893 | { |
43c4d13e SB |
894 | struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); |
895 | ||
896 | edt_ft5x06_ts_teardown_debugfs(tsdata); | |
897 | sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group); | |
898 | ||
43c4d13e SB |
899 | return 0; |
900 | } | |
901 | ||
902 | #ifdef CONFIG_PM_SLEEP | |
903 | static int edt_ft5x06_ts_suspend(struct device *dev) | |
904 | { | |
905 | struct i2c_client *client = to_i2c_client(dev); | |
906 | ||
907 | if (device_may_wakeup(dev)) | |
908 | enable_irq_wake(client->irq); | |
909 | ||
910 | return 0; | |
911 | } | |
912 | ||
913 | static int edt_ft5x06_ts_resume(struct device *dev) | |
914 | { | |
915 | struct i2c_client *client = to_i2c_client(dev); | |
916 | ||
917 | if (device_may_wakeup(dev)) | |
918 | disable_irq_wake(client->irq); | |
919 | ||
920 | return 0; | |
921 | } | |
922 | #endif | |
923 | ||
924 | static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops, | |
925 | edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume); | |
926 | ||
927 | static const struct i2c_device_id edt_ft5x06_ts_id[] = { | |
1730d814 LW |
928 | { "edt-ft5x06", 0, }, |
929 | { /* sentinel */ } | |
43c4d13e SB |
930 | }; |
931 | MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id); | |
932 | ||
dac90dc2 LW |
933 | #ifdef CONFIG_OF |
934 | static const struct of_device_id edt_ft5x06_of_match[] = { | |
935 | { .compatible = "edt,edt-ft5206", }, | |
936 | { .compatible = "edt,edt-ft5306", }, | |
937 | { .compatible = "edt,edt-ft5406", }, | |
938 | { /* sentinel */ } | |
939 | }; | |
940 | MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match); | |
941 | #endif | |
942 | ||
43c4d13e SB |
943 | static struct i2c_driver edt_ft5x06_ts_driver = { |
944 | .driver = { | |
945 | .owner = THIS_MODULE, | |
946 | .name = "edt_ft5x06", | |
dac90dc2 | 947 | .of_match_table = of_match_ptr(edt_ft5x06_of_match), |
43c4d13e SB |
948 | .pm = &edt_ft5x06_ts_pm_ops, |
949 | }, | |
950 | .id_table = edt_ft5x06_ts_id, | |
951 | .probe = edt_ft5x06_ts_probe, | |
1cb0aa88 | 952 | .remove = edt_ft5x06_ts_remove, |
43c4d13e SB |
953 | }; |
954 | ||
955 | module_i2c_driver(edt_ft5x06_ts_driver); | |
956 | ||
957 | MODULE_AUTHOR("Simon Budig <simon.budig@kernelconcepts.de>"); | |
958 | MODULE_DESCRIPTION("EDT FT5x06 I2C Touchscreen Driver"); | |
959 | MODULE_LICENSE("GPL"); |