diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2012-07-30 01:34:47 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2012-07-30 01:34:47 -0400 |
commit | cf45b5a2525d9e7473db955750a8db9d4160b6ab (patch) | |
tree | fa0d5db50adfbf4906313567c9f8f72984fadb0e /drivers/input | |
parent | 314820c9e892d8f41ba4db300ec96770d9c8294b (diff) | |
parent | c0394506e69b37c47d391c2a7bbea3ea236d8ec8 (diff) |
Merge branch 'next' into for-linus
Prepare second set of changes for 3.6 merge window.
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/mouse/synaptics.c | 22 | ||||
-rw-r--r-- | drivers/input/tablet/wacom_wac.c | 21 | ||||
-rw-r--r-- | drivers/input/tablet/wacom_wac.h | 3 | ||||
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 13 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/edt-ft5x06.c | 898 |
6 files changed, 955 insertions, 3 deletions
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index d5b390f75c9a..14eaecea2b70 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c | |||
@@ -40,11 +40,27 @@ | |||
40 | * Note that newer firmware allows querying device for maximum useable | 40 | * Note that newer firmware allows querying device for maximum useable |
41 | * coordinates. | 41 | * coordinates. |
42 | */ | 42 | */ |
43 | #define XMIN 0 | ||
44 | #define XMAX 6143 | ||
45 | #define YMIN 0 | ||
46 | #define YMAX 6143 | ||
43 | #define XMIN_NOMINAL 1472 | 47 | #define XMIN_NOMINAL 1472 |
44 | #define XMAX_NOMINAL 5472 | 48 | #define XMAX_NOMINAL 5472 |
45 | #define YMIN_NOMINAL 1408 | 49 | #define YMIN_NOMINAL 1408 |
46 | #define YMAX_NOMINAL 4448 | 50 | #define YMAX_NOMINAL 4448 |
47 | 51 | ||
52 | /* Size in bits of absolute position values reported by the hardware */ | ||
53 | #define ABS_POS_BITS 13 | ||
54 | |||
55 | /* | ||
56 | * Any position values from the hardware above the following limits are | ||
57 | * treated as "wrapped around negative" values that have been truncated to | ||
58 | * the 13-bit reporting range of the hardware. These are just reasonable | ||
59 | * guesses and can be adjusted if hardware is found that operates outside | ||
60 | * of these parameters. | ||
61 | */ | ||
62 | #define X_MAX_POSITIVE (((1 << ABS_POS_BITS) + XMAX) / 2) | ||
63 | #define Y_MAX_POSITIVE (((1 << ABS_POS_BITS) + YMAX) / 2) | ||
48 | 64 | ||
49 | /***************************************************************************** | 65 | /***************************************************************************** |
50 | * Stuff we need even when we do not want native Synaptics support | 66 | * Stuff we need even when we do not want native Synaptics support |
@@ -588,6 +604,12 @@ static int synaptics_parse_hw_state(const unsigned char buf[], | |||
588 | hw->right = (buf[0] & 0x02) ? 1 : 0; | 604 | hw->right = (buf[0] & 0x02) ? 1 : 0; |
589 | } | 605 | } |
590 | 606 | ||
607 | /* Convert wrap-around values to negative */ | ||
608 | if (hw->x > X_MAX_POSITIVE) | ||
609 | hw->x -= 1 << ABS_POS_BITS; | ||
610 | if (hw->y > Y_MAX_POSITIVE) | ||
611 | hw->y -= 1 << ABS_POS_BITS; | ||
612 | |||
591 | return 0; | 613 | return 0; |
592 | } | 614 | } |
593 | 615 | ||
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 6533f44be5bd..002041975de9 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c | |||
@@ -464,7 +464,7 @@ static void wacom_intuos_general(struct wacom_wac *wacom) | |||
464 | t = (data[6] << 2) | ((data[7] >> 6) & 3); | 464 | t = (data[6] << 2) | ((data[7] >> 6) & 3); |
465 | if ((features->type >= INTUOS4S && features->type <= INTUOS4L) || | 465 | if ((features->type >= INTUOS4S && features->type <= INTUOS4L) || |
466 | (features->type >= INTUOS5S && features->type <= INTUOS5L) || | 466 | (features->type >= INTUOS5S && features->type <= INTUOS5L) || |
467 | features->type == WACOM_21UX2 || features->type == WACOM_24HD) { | 467 | (features->type >= WACOM_21UX2 && features->type <= WACOM_24HD)) { |
468 | t = (t << 1) | (data[1] & 1); | 468 | t = (t << 1) | (data[1] & 1); |
469 | } | 469 | } |
470 | input_report_abs(input, ABS_PRESSURE, t); | 470 | input_report_abs(input, ABS_PRESSURE, t); |
@@ -614,7 +614,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) | |||
614 | input_report_abs(input, ABS_MISC, 0); | 614 | input_report_abs(input, ABS_MISC, 0); |
615 | } | 615 | } |
616 | } else { | 616 | } else { |
617 | if (features->type == WACOM_21UX2) { | 617 | if (features->type == WACOM_21UX2 || features->type == WACOM_22HD) { |
618 | input_report_key(input, BTN_0, (data[5] & 0x01)); | 618 | input_report_key(input, BTN_0, (data[5] & 0x01)); |
619 | input_report_key(input, BTN_1, (data[6] & 0x01)); | 619 | input_report_key(input, BTN_1, (data[6] & 0x01)); |
620 | input_report_key(input, BTN_2, (data[6] & 0x02)); | 620 | input_report_key(input, BTN_2, (data[6] & 0x02)); |
@@ -633,6 +633,12 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) | |||
633 | input_report_key(input, BTN_Z, (data[8] & 0x20)); | 633 | input_report_key(input, BTN_Z, (data[8] & 0x20)); |
634 | input_report_key(input, BTN_BASE, (data[8] & 0x40)); | 634 | input_report_key(input, BTN_BASE, (data[8] & 0x40)); |
635 | input_report_key(input, BTN_BASE2, (data[8] & 0x80)); | 635 | input_report_key(input, BTN_BASE2, (data[8] & 0x80)); |
636 | |||
637 | if (features->type == WACOM_22HD) { | ||
638 | input_report_key(input, KEY_PROG1, data[9] & 0x01); | ||
639 | input_report_key(input, KEY_PROG2, data[9] & 0x02); | ||
640 | input_report_key(input, KEY_PROG3, data[9] & 0x04); | ||
641 | } | ||
636 | } else { | 642 | } else { |
637 | input_report_key(input, BTN_0, (data[5] & 0x01)); | 643 | input_report_key(input, BTN_0, (data[5] & 0x01)); |
638 | input_report_key(input, BTN_1, (data[5] & 0x02)); | 644 | input_report_key(input, BTN_1, (data[5] & 0x02)); |
@@ -1231,6 +1237,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) | |||
1231 | case CINTIQ: | 1237 | case CINTIQ: |
1232 | case WACOM_BEE: | 1238 | case WACOM_BEE: |
1233 | case WACOM_21UX2: | 1239 | case WACOM_21UX2: |
1240 | case WACOM_22HD: | ||
1234 | case WACOM_24HD: | 1241 | case WACOM_24HD: |
1235 | sync = wacom_intuos_irq(wacom_wac); | 1242 | sync = wacom_intuos_irq(wacom_wac); |
1236 | break; | 1243 | break; |
@@ -1432,6 +1439,12 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, | |||
1432 | wacom_setup_cintiq(wacom_wac); | 1439 | wacom_setup_cintiq(wacom_wac); |
1433 | break; | 1440 | break; |
1434 | 1441 | ||
1442 | case WACOM_22HD: | ||
1443 | __set_bit(KEY_PROG1, input_dev->keybit); | ||
1444 | __set_bit(KEY_PROG2, input_dev->keybit); | ||
1445 | __set_bit(KEY_PROG3, input_dev->keybit); | ||
1446 | /* fall through */ | ||
1447 | |||
1435 | case WACOM_21UX2: | 1448 | case WACOM_21UX2: |
1436 | __set_bit(BTN_A, input_dev->keybit); | 1449 | __set_bit(BTN_A, input_dev->keybit); |
1437 | __set_bit(BTN_B, input_dev->keybit); | 1450 | __set_bit(BTN_B, input_dev->keybit); |
@@ -1858,6 +1871,9 @@ static const struct wacom_features wacom_features_0xF0 = | |||
1858 | static const struct wacom_features wacom_features_0xCC = | 1871 | static const struct wacom_features wacom_features_0xCC = |
1859 | { "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87200, 65600, 2047, | 1872 | { "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87200, 65600, 2047, |
1860 | 63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; | 1873 | 63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; |
1874 | static const struct wacom_features wacom_features_0xFA = | ||
1875 | { "Wacom Cintiq 22HD", WACOM_PKGLEN_INTUOS, 95840, 54260, 2047, | ||
1876 | 63, WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; | ||
1861 | static const struct wacom_features wacom_features_0x90 = | 1877 | static const struct wacom_features wacom_features_0x90 = |
1862 | { "Wacom ISDv4 90", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, | 1878 | { "Wacom ISDv4 90", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, |
1863 | 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; | 1879 | 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; |
@@ -2075,6 +2091,7 @@ const struct usb_device_id wacom_ids[] = { | |||
2075 | { USB_DEVICE_WACOM(0xEF) }, | 2091 | { USB_DEVICE_WACOM(0xEF) }, |
2076 | { USB_DEVICE_WACOM(0x47) }, | 2092 | { USB_DEVICE_WACOM(0x47) }, |
2077 | { USB_DEVICE_WACOM(0xF4) }, | 2093 | { USB_DEVICE_WACOM(0xF4) }, |
2094 | { USB_DEVICE_WACOM(0xFA) }, | ||
2078 | { USB_DEVICE_LENOVO(0x6004) }, | 2095 | { USB_DEVICE_LENOVO(0x6004) }, |
2079 | { } | 2096 | { } |
2080 | }; | 2097 | }; |
diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index bd5d37b28714..96c185cc301e 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h | |||
@@ -73,8 +73,9 @@ enum { | |||
73 | INTUOS5S, | 73 | INTUOS5S, |
74 | INTUOS5, | 74 | INTUOS5, |
75 | INTUOS5L, | 75 | INTUOS5L, |
76 | WACOM_24HD, | ||
77 | WACOM_21UX2, | 76 | WACOM_21UX2, |
77 | WACOM_22HD, | ||
78 | WACOM_24HD, | ||
78 | CINTIQ, | 79 | CINTIQ, |
79 | WACOM_BEE, | 80 | WACOM_BEE, |
80 | WACOM_MO, | 81 | WACOM_MO, |
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 73bd2f6b82ec..1ba232cbc09d 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig | |||
@@ -472,6 +472,19 @@ config TOUCHSCREEN_PENMOUNT | |||
472 | To compile this driver as a module, choose M here: the | 472 | To compile this driver as a module, choose M here: the |
473 | module will be called penmount. | 473 | module will be called penmount. |
474 | 474 | ||
475 | config TOUCHSCREEN_EDT_FT5X06 | ||
476 | tristate "EDT FocalTech FT5x06 I2C Touchscreen support" | ||
477 | depends on I2C | ||
478 | help | ||
479 | Say Y here if you have an EDT "Polytouch" touchscreen based | ||
480 | on the FocalTech FT5x06 family of controllers connected to | ||
481 | your system. | ||
482 | |||
483 | If unsure, say N. | ||
484 | |||
485 | To compile this driver as a module, choose M here: the | ||
486 | module will be called edt-ft5x06. | ||
487 | |||
475 | config TOUCHSCREEN_MIGOR | 488 | config TOUCHSCREEN_MIGOR |
476 | tristate "Renesas MIGO-R touchscreen" | 489 | tristate "Renesas MIGO-R touchscreen" |
477 | depends on SH_MIGOR && I2C | 490 | depends on SH_MIGOR && I2C |
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 5920c60f999d..178eb128d90f 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile | |||
@@ -24,6 +24,7 @@ obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o | |||
24 | obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o | 24 | obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o |
25 | obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o | 25 | obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o |
26 | obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o | 26 | obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o |
27 | obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06) += edt-ft5x06.o | ||
27 | obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o | 28 | obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o |
28 | obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o | 29 | obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o |
29 | obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o | 30 | obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o |
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c new file mode 100644 index 000000000000..9afc777a40a7 --- /dev/null +++ b/drivers/input/touchscreen/edt-ft5x06.c | |||
@@ -0,0 +1,898 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de> | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public | ||
14 | * License along with this library; if not, write to the Free Software | ||
15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
16 | */ | ||
17 | |||
18 | /* | ||
19 | * This is a driver for the EDT "Polytouch" family of touch controllers | ||
20 | * based on the FocalTech FT5x06 line of chips. | ||
21 | * | ||
22 | * Development of this driver has been sponsored by Glyn: | ||
23 | * http://www.glyn.com/Products/Displays | ||
24 | */ | ||
25 | |||
26 | #include <linux/module.h> | ||
27 | #include <linux/ratelimit.h> | ||
28 | #include <linux/interrupt.h> | ||
29 | #include <linux/input.h> | ||
30 | #include <linux/i2c.h> | ||
31 | #include <linux/uaccess.h> | ||
32 | #include <linux/delay.h> | ||
33 | #include <linux/debugfs.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/gpio.h> | ||
36 | #include <linux/input/mt.h> | ||
37 | #include <linux/input/edt-ft5x06.h> | ||
38 | |||
39 | #define MAX_SUPPORT_POINTS 5 | ||
40 | |||
41 | #define WORK_REGISTER_THRESHOLD 0x00 | ||
42 | #define WORK_REGISTER_REPORT_RATE 0x08 | ||
43 | #define WORK_REGISTER_GAIN 0x30 | ||
44 | #define WORK_REGISTER_OFFSET 0x31 | ||
45 | #define WORK_REGISTER_NUM_X 0x33 | ||
46 | #define WORK_REGISTER_NUM_Y 0x34 | ||
47 | |||
48 | #define WORK_REGISTER_OPMODE 0x3c | ||
49 | #define FACTORY_REGISTER_OPMODE 0x01 | ||
50 | |||
51 | #define TOUCH_EVENT_DOWN 0x00 | ||
52 | #define TOUCH_EVENT_UP 0x01 | ||
53 | #define TOUCH_EVENT_ON 0x02 | ||
54 | #define TOUCH_EVENT_RESERVED 0x03 | ||
55 | |||
56 | #define EDT_NAME_LEN 23 | ||
57 | #define EDT_SWITCH_MODE_RETRIES 10 | ||
58 | #define EDT_SWITCH_MODE_DELAY 5 /* msec */ | ||
59 | #define EDT_RAW_DATA_RETRIES 100 | ||
60 | #define EDT_RAW_DATA_DELAY 1 /* msec */ | ||
61 | |||
62 | struct edt_ft5x06_ts_data { | ||
63 | struct i2c_client *client; | ||
64 | struct input_dev *input; | ||
65 | u16 num_x; | ||
66 | u16 num_y; | ||
67 | |||
68 | #if defined(CONFIG_DEBUG_FS) | ||
69 | struct dentry *debug_dir; | ||
70 | u8 *raw_buffer; | ||
71 | size_t raw_bufsize; | ||
72 | #endif | ||
73 | |||
74 | struct mutex mutex; | ||
75 | bool factory_mode; | ||
76 | int threshold; | ||
77 | int gain; | ||
78 | int offset; | ||
79 | int report_rate; | ||
80 | |||
81 | char name[EDT_NAME_LEN]; | ||
82 | }; | ||
83 | |||
84 | static int edt_ft5x06_ts_readwrite(struct i2c_client *client, | ||
85 | u16 wr_len, u8 *wr_buf, | ||
86 | u16 rd_len, u8 *rd_buf) | ||
87 | { | ||
88 | struct i2c_msg wrmsg[2]; | ||
89 | int i = 0; | ||
90 | int ret; | ||
91 | |||
92 | if (wr_len) { | ||
93 | wrmsg[i].addr = client->addr; | ||
94 | wrmsg[i].flags = 0; | ||
95 | wrmsg[i].len = wr_len; | ||
96 | wrmsg[i].buf = wr_buf; | ||
97 | i++; | ||
98 | } | ||
99 | if (rd_len) { | ||
100 | wrmsg[i].addr = client->addr; | ||
101 | wrmsg[i].flags = I2C_M_RD; | ||
102 | wrmsg[i].len = rd_len; | ||
103 | wrmsg[i].buf = rd_buf; | ||
104 | i++; | ||
105 | } | ||
106 | |||
107 | ret = i2c_transfer(client->adapter, wrmsg, i); | ||
108 | if (ret < 0) | ||
109 | return ret; | ||
110 | if (ret != i) | ||
111 | return -EIO; | ||
112 | |||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata, | ||
117 | u8 *buf, int buflen) | ||
118 | { | ||
119 | int i; | ||
120 | u8 crc = 0; | ||
121 | |||
122 | for (i = 0; i < buflen - 1; i++) | ||
123 | crc ^= buf[i]; | ||
124 | |||
125 | if (crc != buf[buflen-1]) { | ||
126 | dev_err_ratelimited(&tsdata->client->dev, | ||
127 | "crc error: 0x%02x expected, got 0x%02x\n", | ||
128 | crc, buf[buflen-1]); | ||
129 | return false; | ||
130 | } | ||
131 | |||
132 | return true; | ||
133 | } | ||
134 | |||
135 | static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) | ||
136 | { | ||
137 | struct edt_ft5x06_ts_data *tsdata = dev_id; | ||
138 | struct device *dev = &tsdata->client->dev; | ||
139 | u8 cmd = 0xf9; | ||
140 | u8 rdbuf[26]; | ||
141 | int i, type, x, y, id; | ||
142 | int error; | ||
143 | |||
144 | memset(rdbuf, 0, sizeof(rdbuf)); | ||
145 | |||
146 | error = edt_ft5x06_ts_readwrite(tsdata->client, | ||
147 | sizeof(cmd), &cmd, | ||
148 | sizeof(rdbuf), rdbuf); | ||
149 | if (error) { | ||
150 | dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n", | ||
151 | error); | ||
152 | goto out; | ||
153 | } | ||
154 | |||
155 | if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || rdbuf[2] != 26) { | ||
156 | dev_err_ratelimited(dev, "Unexpected header: %02x%02x%02x!\n", | ||
157 | rdbuf[0], rdbuf[1], rdbuf[2]); | ||
158 | goto out; | ||
159 | } | ||
160 | |||
161 | if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, 26)) | ||
162 | goto out; | ||
163 | |||
164 | for (i = 0; i < MAX_SUPPORT_POINTS; i++) { | ||
165 | u8 *buf = &rdbuf[i * 4 + 5]; | ||
166 | bool down; | ||
167 | |||
168 | type = buf[0] >> 6; | ||
169 | /* ignore Reserved events */ | ||
170 | if (type == TOUCH_EVENT_RESERVED) | ||
171 | continue; | ||
172 | |||
173 | x = ((buf[0] << 8) | buf[1]) & 0x0fff; | ||
174 | y = ((buf[2] << 8) | buf[3]) & 0x0fff; | ||
175 | id = (buf[2] >> 4) & 0x0f; | ||
176 | down = (type != TOUCH_EVENT_UP); | ||
177 | |||
178 | input_mt_slot(tsdata->input, id); | ||
179 | input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down); | ||
180 | |||
181 | if (!down) | ||
182 | continue; | ||
183 | |||
184 | input_report_abs(tsdata->input, ABS_MT_POSITION_X, x); | ||
185 | input_report_abs(tsdata->input, ABS_MT_POSITION_Y, y); | ||
186 | } | ||
187 | |||
188 | input_mt_report_pointer_emulation(tsdata->input, true); | ||
189 | input_sync(tsdata->input); | ||
190 | |||
191 | out: | ||
192 | return IRQ_HANDLED; | ||
193 | } | ||
194 | |||
195 | static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, | ||
196 | u8 addr, u8 value) | ||
197 | { | ||
198 | u8 wrbuf[4]; | ||
199 | |||
200 | wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; | ||
201 | wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; | ||
202 | wrbuf[2] = value; | ||
203 | wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; | ||
204 | |||
205 | return edt_ft5x06_ts_readwrite(tsdata->client, 4, wrbuf, 0, NULL); | ||
206 | } | ||
207 | |||
208 | static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, | ||
209 | u8 addr) | ||
210 | { | ||
211 | u8 wrbuf[2], rdbuf[2]; | ||
212 | int error; | ||
213 | |||
214 | wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; | ||
215 | wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; | ||
216 | wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40; | ||
217 | |||
218 | error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, rdbuf); | ||
219 | if (error) | ||
220 | return error; | ||
221 | |||
222 | if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) { | ||
223 | dev_err(&tsdata->client->dev, | ||
224 | "crc error: 0x%02x expected, got 0x%02x\n", | ||
225 | wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], rdbuf[1]); | ||
226 | return -EIO; | ||
227 | } | ||
228 | |||
229 | return rdbuf[0]; | ||
230 | } | ||
231 | |||
232 | struct edt_ft5x06_attribute { | ||
233 | struct device_attribute dattr; | ||
234 | size_t field_offset; | ||
235 | u8 limit_low; | ||
236 | u8 limit_high; | ||
237 | u8 addr; | ||
238 | }; | ||
239 | |||
240 | #define EDT_ATTR(_field, _mode, _addr, _limit_low, _limit_high) \ | ||
241 | struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \ | ||
242 | .dattr = __ATTR(_field, _mode, \ | ||
243 | edt_ft5x06_setting_show, \ | ||
244 | edt_ft5x06_setting_store), \ | ||
245 | .field_offset = \ | ||
246 | offsetof(struct edt_ft5x06_ts_data, _field), \ | ||
247 | .limit_low = _limit_low, \ | ||
248 | .limit_high = _limit_high, \ | ||
249 | .addr = _addr, \ | ||
250 | } | ||
251 | |||
252 | static ssize_t edt_ft5x06_setting_show(struct device *dev, | ||
253 | struct device_attribute *dattr, | ||
254 | char *buf) | ||
255 | { | ||
256 | struct i2c_client *client = to_i2c_client(dev); | ||
257 | struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); | ||
258 | struct edt_ft5x06_attribute *attr = | ||
259 | container_of(dattr, struct edt_ft5x06_attribute, dattr); | ||
260 | u8 *field = (u8 *)((char *)tsdata + attr->field_offset); | ||
261 | int val; | ||
262 | size_t count = 0; | ||
263 | int error = 0; | ||
264 | |||
265 | mutex_lock(&tsdata->mutex); | ||
266 | |||
267 | if (tsdata->factory_mode) { | ||
268 | error = -EIO; | ||
269 | goto out; | ||
270 | } | ||
271 | |||
272 | val = edt_ft5x06_register_read(tsdata, attr->addr); | ||
273 | if (val < 0) { | ||
274 | error = val; | ||
275 | dev_err(&tsdata->client->dev, | ||
276 | "Failed to fetch attribute %s, error %d\n", | ||
277 | dattr->attr.name, error); | ||
278 | goto out; | ||
279 | } | ||
280 | |||
281 | if (val != *field) { | ||
282 | dev_warn(&tsdata->client->dev, | ||
283 | "%s: read (%d) and stored value (%d) differ\n", | ||
284 | dattr->attr.name, val, *field); | ||
285 | *field = val; | ||
286 | } | ||
287 | |||
288 | count = scnprintf(buf, PAGE_SIZE, "%d\n", val); | ||
289 | out: | ||
290 | mutex_unlock(&tsdata->mutex); | ||
291 | return error ?: count; | ||
292 | } | ||
293 | |||
294 | static ssize_t edt_ft5x06_setting_store(struct device *dev, | ||
295 | struct device_attribute *dattr, | ||
296 | const char *buf, size_t count) | ||
297 | { | ||
298 | struct i2c_client *client = to_i2c_client(dev); | ||
299 | struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); | ||
300 | struct edt_ft5x06_attribute *attr = | ||
301 | container_of(dattr, struct edt_ft5x06_attribute, dattr); | ||
302 | u8 *field = (u8 *)((char *)tsdata + attr->field_offset); | ||
303 | unsigned int val; | ||
304 | int error; | ||
305 | |||
306 | mutex_lock(&tsdata->mutex); | ||
307 | |||
308 | if (tsdata->factory_mode) { | ||
309 | error = -EIO; | ||
310 | goto out; | ||
311 | } | ||
312 | |||
313 | error = kstrtouint(buf, 0, &val); | ||
314 | if (error) | ||
315 | goto out; | ||
316 | |||
317 | if (val < attr->limit_low || val > attr->limit_high) { | ||
318 | error = -ERANGE; | ||
319 | goto out; | ||
320 | } | ||
321 | |||
322 | error = edt_ft5x06_register_write(tsdata, attr->addr, val); | ||
323 | if (error) { | ||
324 | dev_err(&tsdata->client->dev, | ||
325 | "Failed to update attribute %s, error: %d\n", | ||
326 | dattr->attr.name, error); | ||
327 | goto out; | ||
328 | } | ||
329 | |||
330 | *field = val; | ||
331 | |||
332 | out: | ||
333 | mutex_unlock(&tsdata->mutex); | ||
334 | return error ?: count; | ||
335 | } | ||
336 | |||
337 | static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, 0, 31); | ||
338 | static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, 0, 31); | ||
339 | static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, | ||
340 | WORK_REGISTER_THRESHOLD, 20, 80); | ||
341 | static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, | ||
342 | WORK_REGISTER_REPORT_RATE, 3, 14); | ||
343 | |||
344 | static struct attribute *edt_ft5x06_attrs[] = { | ||
345 | &edt_ft5x06_attr_gain.dattr.attr, | ||
346 | &edt_ft5x06_attr_offset.dattr.attr, | ||
347 | &edt_ft5x06_attr_threshold.dattr.attr, | ||
348 | &edt_ft5x06_attr_report_rate.dattr.attr, | ||
349 | NULL | ||
350 | }; | ||
351 | |||
352 | static const struct attribute_group edt_ft5x06_attr_group = { | ||
353 | .attrs = edt_ft5x06_attrs, | ||
354 | }; | ||
355 | |||
356 | #ifdef CONFIG_DEBUG_FS | ||
357 | static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata) | ||
358 | { | ||
359 | struct i2c_client *client = tsdata->client; | ||
360 | int retries = EDT_SWITCH_MODE_RETRIES; | ||
361 | int ret; | ||
362 | int error; | ||
363 | |||
364 | disable_irq(client->irq); | ||
365 | |||
366 | if (!tsdata->raw_buffer) { | ||
367 | tsdata->raw_bufsize = tsdata->num_x * tsdata->num_y * | ||
368 | sizeof(u16); | ||
369 | tsdata->raw_buffer = kzalloc(tsdata->raw_bufsize, GFP_KERNEL); | ||
370 | if (!tsdata->raw_buffer) { | ||
371 | error = -ENOMEM; | ||
372 | goto err_out; | ||
373 | } | ||
374 | } | ||
375 | |||
376 | /* mode register is 0x3c when in the work mode */ | ||
377 | error = edt_ft5x06_register_write(tsdata, WORK_REGISTER_OPMODE, 0x03); | ||
378 | if (error) { | ||
379 | dev_err(&client->dev, | ||
380 | "failed to switch to factory mode, error %d\n", error); | ||
381 | goto err_out; | ||
382 | } | ||
383 | |||
384 | tsdata->factory_mode = true; | ||
385 | do { | ||
386 | mdelay(EDT_SWITCH_MODE_DELAY); | ||
387 | /* mode register is 0x01 when in factory mode */ | ||
388 | ret = edt_ft5x06_register_read(tsdata, FACTORY_REGISTER_OPMODE); | ||
389 | if (ret == 0x03) | ||
390 | break; | ||
391 | } while (--retries > 0); | ||
392 | |||
393 | if (retries == 0) { | ||
394 | dev_err(&client->dev, "not in factory mode after %dms.\n", | ||
395 | EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY); | ||
396 | error = -EIO; | ||
397 | goto err_out; | ||
398 | } | ||
399 | |||
400 | return 0; | ||
401 | |||
402 | err_out: | ||
403 | kfree(tsdata->raw_buffer); | ||
404 | tsdata->raw_buffer = NULL; | ||
405 | tsdata->factory_mode = false; | ||
406 | enable_irq(client->irq); | ||
407 | |||
408 | return error; | ||
409 | } | ||
410 | |||
411 | static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) | ||
412 | { | ||
413 | struct i2c_client *client = tsdata->client; | ||
414 | int retries = EDT_SWITCH_MODE_RETRIES; | ||
415 | int ret; | ||
416 | int error; | ||
417 | |||
418 | /* mode register is 0x01 when in the factory mode */ | ||
419 | error = edt_ft5x06_register_write(tsdata, FACTORY_REGISTER_OPMODE, 0x1); | ||
420 | if (error) { | ||
421 | dev_err(&client->dev, | ||
422 | "failed to switch to work mode, error: %d\n", error); | ||
423 | return error; | ||
424 | } | ||
425 | |||
426 | tsdata->factory_mode = false; | ||
427 | |||
428 | do { | ||
429 | mdelay(EDT_SWITCH_MODE_DELAY); | ||
430 | /* mode register is 0x01 when in factory mode */ | ||
431 | ret = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OPMODE); | ||
432 | if (ret == 0x01) | ||
433 | break; | ||
434 | } while (--retries > 0); | ||
435 | |||
436 | if (retries == 0) { | ||
437 | dev_err(&client->dev, "not in work mode after %dms.\n", | ||
438 | EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY); | ||
439 | tsdata->factory_mode = true; | ||
440 | return -EIO; | ||
441 | } | ||
442 | |||
443 | if (tsdata->raw_buffer) | ||
444 | kfree(tsdata->raw_buffer); | ||
445 | tsdata->raw_buffer = NULL; | ||
446 | |||
447 | /* restore parameters */ | ||
448 | edt_ft5x06_register_write(tsdata, WORK_REGISTER_THRESHOLD, | ||
449 | tsdata->threshold); | ||
450 | edt_ft5x06_register_write(tsdata, WORK_REGISTER_GAIN, | ||
451 | tsdata->gain); | ||
452 | edt_ft5x06_register_write(tsdata, WORK_REGISTER_OFFSET, | ||
453 | tsdata->offset); | ||
454 | edt_ft5x06_register_write(tsdata, WORK_REGISTER_REPORT_RATE, | ||
455 | tsdata->report_rate); | ||
456 | |||
457 | enable_irq(client->irq); | ||
458 | |||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | static int edt_ft5x06_debugfs_mode_get(void *data, u64 *mode) | ||
463 | { | ||
464 | struct edt_ft5x06_ts_data *tsdata = data; | ||
465 | |||
466 | *mode = tsdata->factory_mode; | ||
467 | |||
468 | return 0; | ||
469 | }; | ||
470 | |||
471 | static int edt_ft5x06_debugfs_mode_set(void *data, u64 mode) | ||
472 | { | ||
473 | struct edt_ft5x06_ts_data *tsdata = data; | ||
474 | int retval = 0; | ||
475 | |||
476 | if (mode > 1) | ||
477 | return -ERANGE; | ||
478 | |||
479 | mutex_lock(&tsdata->mutex); | ||
480 | |||
481 | if (mode != tsdata->factory_mode) { | ||
482 | retval = mode ? edt_ft5x06_factory_mode(tsdata) : | ||
483 | edt_ft5x06_work_mode(tsdata); | ||
484 | } | ||
485 | |||
486 | mutex_unlock(&tsdata->mutex); | ||
487 | |||
488 | return retval; | ||
489 | }; | ||
490 | |||
491 | DEFINE_SIMPLE_ATTRIBUTE(debugfs_mode_fops, edt_ft5x06_debugfs_mode_get, | ||
492 | edt_ft5x06_debugfs_mode_set, "%llu\n"); | ||
493 | |||
494 | static int edt_ft5x06_debugfs_raw_data_open(struct inode *inode, | ||
495 | struct file *file) | ||
496 | { | ||
497 | file->private_data = inode->i_private; | ||
498 | |||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file, | ||
503 | char __user *buf, size_t count, loff_t *off) | ||
504 | { | ||
505 | struct edt_ft5x06_ts_data *tsdata = file->private_data; | ||
506 | struct i2c_client *client = tsdata->client; | ||
507 | int retries = EDT_RAW_DATA_RETRIES; | ||
508 | int val, i, error; | ||
509 | size_t read = 0; | ||
510 | int colbytes; | ||
511 | char wrbuf[3]; | ||
512 | u8 *rdbuf; | ||
513 | |||
514 | if (*off < 0 || *off >= tsdata->raw_bufsize) | ||
515 | return 0; | ||
516 | |||
517 | mutex_lock(&tsdata->mutex); | ||
518 | |||
519 | if (!tsdata->factory_mode || !tsdata->raw_buffer) { | ||
520 | error = -EIO; | ||
521 | goto out; | ||
522 | } | ||
523 | |||
524 | error = edt_ft5x06_register_write(tsdata, 0x08, 0x01); | ||
525 | if (error) { | ||
526 | dev_dbg(&client->dev, | ||
527 | "failed to write 0x08 register, error %d\n", error); | ||
528 | goto out; | ||
529 | } | ||
530 | |||
531 | do { | ||
532 | msleep(EDT_RAW_DATA_DELAY); | ||
533 | val = edt_ft5x06_register_read(tsdata, 0x08); | ||
534 | if (val < 1) | ||
535 | break; | ||
536 | } while (--retries > 0); | ||
537 | |||
538 | if (val < 0) { | ||
539 | error = val; | ||
540 | dev_dbg(&client->dev, | ||
541 | "failed to read 0x08 register, error %d\n", error); | ||
542 | goto out; | ||
543 | } | ||
544 | |||
545 | if (retries == 0) { | ||
546 | dev_dbg(&client->dev, | ||
547 | "timed out waiting for register to settle\n"); | ||
548 | error = -ETIMEDOUT; | ||
549 | goto out; | ||
550 | } | ||
551 | |||
552 | rdbuf = tsdata->raw_buffer; | ||
553 | colbytes = tsdata->num_y * sizeof(u16); | ||
554 | |||
555 | wrbuf[0] = 0xf5; | ||
556 | wrbuf[1] = 0x0e; | ||
557 | for (i = 0; i < tsdata->num_x; i++) { | ||
558 | wrbuf[2] = i; /* column index */ | ||
559 | error = edt_ft5x06_ts_readwrite(tsdata->client, | ||
560 | sizeof(wrbuf), wrbuf, | ||
561 | colbytes, rdbuf); | ||
562 | if (error) | ||
563 | goto out; | ||
564 | |||
565 | rdbuf += colbytes; | ||
566 | } | ||
567 | |||
568 | read = min_t(size_t, count, tsdata->raw_bufsize - *off); | ||
569 | error = copy_to_user(buf, tsdata->raw_buffer + *off, read); | ||
570 | if (!error) | ||
571 | *off += read; | ||
572 | out: | ||
573 | mutex_unlock(&tsdata->mutex); | ||
574 | return error ?: read; | ||
575 | }; | ||
576 | |||
577 | |||
578 | static const struct file_operations debugfs_raw_data_fops = { | ||
579 | .open = edt_ft5x06_debugfs_raw_data_open, | ||
580 | .read = edt_ft5x06_debugfs_raw_data_read, | ||
581 | }; | ||
582 | |||
583 | static void __devinit | ||
584 | edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, | ||
585 | const char *debugfs_name) | ||
586 | { | ||
587 | tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL); | ||
588 | if (!tsdata->debug_dir) | ||
589 | return; | ||
590 | |||
591 | debugfs_create_u16("num_x", S_IRUSR, tsdata->debug_dir, &tsdata->num_x); | ||
592 | debugfs_create_u16("num_y", S_IRUSR, tsdata->debug_dir, &tsdata->num_y); | ||
593 | |||
594 | debugfs_create_file("mode", S_IRUSR | S_IWUSR, | ||
595 | tsdata->debug_dir, tsdata, &debugfs_mode_fops); | ||
596 | debugfs_create_file("raw_data", S_IRUSR, | ||
597 | tsdata->debug_dir, tsdata, &debugfs_raw_data_fops); | ||
598 | } | ||
599 | |||
600 | static void __devexit | ||
601 | edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) | ||
602 | { | ||
603 | if (tsdata->debug_dir) | ||
604 | debugfs_remove_recursive(tsdata->debug_dir); | ||
605 | } | ||
606 | |||
607 | #else | ||
608 | |||
609 | static inline void | ||
610 | edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, | ||
611 | const char *debugfs_name) | ||
612 | { | ||
613 | } | ||
614 | |||
615 | static inline void | ||
616 | edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) | ||
617 | { | ||
618 | } | ||
619 | |||
620 | #endif /* CONFIG_DEBUGFS */ | ||
621 | |||
622 | |||
623 | |||
624 | static int __devinit edt_ft5x06_ts_reset(struct i2c_client *client, | ||
625 | int reset_pin) | ||
626 | { | ||
627 | int error; | ||
628 | |||
629 | if (gpio_is_valid(reset_pin)) { | ||
630 | /* this pulls reset down, enabling the low active reset */ | ||
631 | error = gpio_request_one(reset_pin, GPIOF_OUT_INIT_LOW, | ||
632 | "edt-ft5x06 reset"); | ||
633 | if (error) { | ||
634 | dev_err(&client->dev, | ||
635 | "Failed to request GPIO %d as reset pin, error %d\n", | ||
636 | reset_pin, error); | ||
637 | return error; | ||
638 | } | ||
639 | |||
640 | mdelay(50); | ||
641 | gpio_set_value(reset_pin, 1); | ||
642 | mdelay(100); | ||
643 | } | ||
644 | |||
645 | return 0; | ||
646 | } | ||
647 | |||
648 | static int __devinit edt_ft5x06_ts_identify(struct i2c_client *client, | ||
649 | char *model_name, | ||
650 | char *fw_version) | ||
651 | { | ||
652 | u8 rdbuf[EDT_NAME_LEN]; | ||
653 | char *p; | ||
654 | int error; | ||
655 | |||
656 | error = edt_ft5x06_ts_readwrite(client, 1, "\xbb", | ||
657 | EDT_NAME_LEN - 1, rdbuf); | ||
658 | if (error) | ||
659 | return error; | ||
660 | |||
661 | /* remove last '$' end marker */ | ||
662 | rdbuf[EDT_NAME_LEN - 1] = '\0'; | ||
663 | if (rdbuf[EDT_NAME_LEN - 2] == '$') | ||
664 | rdbuf[EDT_NAME_LEN - 2] = '\0'; | ||
665 | |||
666 | /* look for Model/Version separator */ | ||
667 | p = strchr(rdbuf, '*'); | ||
668 | if (p) | ||
669 | *p++ = '\0'; | ||
670 | |||
671 | strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN); | ||
672 | strlcpy(fw_version, p ? p : "", EDT_NAME_LEN); | ||
673 | |||
674 | return 0; | ||
675 | } | ||
676 | |||
677 | #define EDT_ATTR_CHECKSET(name, reg) \ | ||
678 | if (pdata->name >= edt_ft5x06_attr_##name.limit_low && \ | ||
679 | pdata->name <= edt_ft5x06_attr_##name.limit_high) \ | ||
680 | edt_ft5x06_register_write(tsdata, reg, pdata->name) | ||
681 | |||
682 | static void __devinit | ||
683 | edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata, | ||
684 | const struct edt_ft5x06_platform_data *pdata) | ||
685 | { | ||
686 | if (!pdata->use_parameters) | ||
687 | return; | ||
688 | |||
689 | /* pick up defaults from the platform data */ | ||
690 | EDT_ATTR_CHECKSET(threshold, WORK_REGISTER_THRESHOLD); | ||
691 | EDT_ATTR_CHECKSET(gain, WORK_REGISTER_GAIN); | ||
692 | EDT_ATTR_CHECKSET(offset, WORK_REGISTER_OFFSET); | ||
693 | EDT_ATTR_CHECKSET(report_rate, WORK_REGISTER_REPORT_RATE); | ||
694 | } | ||
695 | |||
696 | static void __devinit | ||
697 | edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata) | ||
698 | { | ||
699 | tsdata->threshold = edt_ft5x06_register_read(tsdata, | ||
700 | WORK_REGISTER_THRESHOLD); | ||
701 | tsdata->gain = edt_ft5x06_register_read(tsdata, WORK_REGISTER_GAIN); | ||
702 | tsdata->offset = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OFFSET); | ||
703 | tsdata->report_rate = edt_ft5x06_register_read(tsdata, | ||
704 | WORK_REGISTER_REPORT_RATE); | ||
705 | tsdata->num_x = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_X); | ||
706 | tsdata->num_y = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_Y); | ||
707 | } | ||
708 | |||
709 | static int __devinit edt_ft5x06_ts_probe(struct i2c_client *client, | ||
710 | const struct i2c_device_id *id) | ||
711 | { | ||
712 | const struct edt_ft5x06_platform_data *pdata = | ||
713 | client->dev.platform_data; | ||
714 | struct edt_ft5x06_ts_data *tsdata; | ||
715 | struct input_dev *input; | ||
716 | int error; | ||
717 | char fw_version[EDT_NAME_LEN]; | ||
718 | |||
719 | dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n"); | ||
720 | |||
721 | if (!pdata) { | ||
722 | dev_err(&client->dev, "no platform data?\n"); | ||
723 | return -EINVAL; | ||
724 | } | ||
725 | |||
726 | error = edt_ft5x06_ts_reset(client, pdata->reset_pin); | ||
727 | if (error) | ||
728 | return error; | ||
729 | |||
730 | if (gpio_is_valid(pdata->irq_pin)) { | ||
731 | error = gpio_request_one(pdata->irq_pin, | ||
732 | GPIOF_IN, "edt-ft5x06 irq"); | ||
733 | if (error) { | ||
734 | dev_err(&client->dev, | ||
735 | "Failed to request GPIO %d, error %d\n", | ||
736 | pdata->irq_pin, error); | ||
737 | return error; | ||
738 | } | ||
739 | } | ||
740 | |||
741 | tsdata = kzalloc(sizeof(*tsdata), GFP_KERNEL); | ||
742 | input = input_allocate_device(); | ||
743 | if (!tsdata || !input) { | ||
744 | dev_err(&client->dev, "failed to allocate driver data.\n"); | ||
745 | error = -ENOMEM; | ||
746 | goto err_free_mem; | ||
747 | } | ||
748 | |||
749 | mutex_init(&tsdata->mutex); | ||
750 | tsdata->client = client; | ||
751 | tsdata->input = input; | ||
752 | tsdata->factory_mode = false; | ||
753 | |||
754 | error = edt_ft5x06_ts_identify(client, tsdata->name, fw_version); | ||
755 | if (error) { | ||
756 | dev_err(&client->dev, "touchscreen probe failed\n"); | ||
757 | goto err_free_mem; | ||
758 | } | ||
759 | |||
760 | edt_ft5x06_ts_get_defaults(tsdata, pdata); | ||
761 | edt_ft5x06_ts_get_parameters(tsdata); | ||
762 | |||
763 | dev_dbg(&client->dev, | ||
764 | "Model \"%s\", Rev. \"%s\", %dx%d sensors\n", | ||
765 | tsdata->name, fw_version, tsdata->num_x, tsdata->num_y); | ||
766 | |||
767 | input->name = tsdata->name; | ||
768 | input->id.bustype = BUS_I2C; | ||
769 | input->dev.parent = &client->dev; | ||
770 | |||
771 | __set_bit(EV_SYN, input->evbit); | ||
772 | __set_bit(EV_KEY, input->evbit); | ||
773 | __set_bit(EV_ABS, input->evbit); | ||
774 | __set_bit(BTN_TOUCH, input->keybit); | ||
775 | input_set_abs_params(input, ABS_X, 0, tsdata->num_x * 64 - 1, 0, 0); | ||
776 | input_set_abs_params(input, ABS_Y, 0, tsdata->num_y * 64 - 1, 0, 0); | ||
777 | input_set_abs_params(input, ABS_MT_POSITION_X, | ||
778 | 0, tsdata->num_x * 64 - 1, 0, 0); | ||
779 | input_set_abs_params(input, ABS_MT_POSITION_Y, | ||
780 | 0, tsdata->num_y * 64 - 1, 0, 0); | ||
781 | error = input_mt_init_slots(input, MAX_SUPPORT_POINTS); | ||
782 | if (error) { | ||
783 | dev_err(&client->dev, "Unable to init MT slots.\n"); | ||
784 | goto err_free_mem; | ||
785 | } | ||
786 | |||
787 | input_set_drvdata(input, tsdata); | ||
788 | i2c_set_clientdata(client, tsdata); | ||
789 | |||
790 | error = request_threaded_irq(client->irq, NULL, edt_ft5x06_ts_isr, | ||
791 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | ||
792 | client->name, tsdata); | ||
793 | if (error) { | ||
794 | dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); | ||
795 | goto err_free_mem; | ||
796 | } | ||
797 | |||
798 | error = sysfs_create_group(&client->dev.kobj, &edt_ft5x06_attr_group); | ||
799 | if (error) | ||
800 | goto err_free_irq; | ||
801 | |||
802 | error = input_register_device(input); | ||
803 | if (error) | ||
804 | goto err_remove_attrs; | ||
805 | |||
806 | edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev)); | ||
807 | device_init_wakeup(&client->dev, 1); | ||
808 | |||
809 | dev_dbg(&client->dev, | ||
810 | "EDT FT5x06 initialized: IRQ pin %d, Reset pin %d.\n", | ||
811 | pdata->irq_pin, pdata->reset_pin); | ||
812 | |||
813 | return 0; | ||
814 | |||
815 | err_remove_attrs: | ||
816 | sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group); | ||
817 | err_free_irq: | ||
818 | free_irq(client->irq, tsdata); | ||
819 | err_free_mem: | ||
820 | input_free_device(input); | ||
821 | kfree(tsdata); | ||
822 | |||
823 | if (gpio_is_valid(pdata->irq_pin)) | ||
824 | gpio_free(pdata->irq_pin); | ||
825 | |||
826 | return error; | ||
827 | } | ||
828 | |||
829 | static int __devexit edt_ft5x06_ts_remove(struct i2c_client *client) | ||
830 | { | ||
831 | const struct edt_ft5x06_platform_data *pdata = | ||
832 | dev_get_platdata(&client->dev); | ||
833 | struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); | ||
834 | |||
835 | edt_ft5x06_ts_teardown_debugfs(tsdata); | ||
836 | sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group); | ||
837 | |||
838 | free_irq(client->irq, tsdata); | ||
839 | input_unregister_device(tsdata->input); | ||
840 | |||
841 | if (gpio_is_valid(pdata->irq_pin)) | ||
842 | gpio_free(pdata->irq_pin); | ||
843 | if (gpio_is_valid(pdata->reset_pin)) | ||
844 | gpio_free(pdata->reset_pin); | ||
845 | |||
846 | kfree(tsdata->raw_buffer); | ||
847 | kfree(tsdata); | ||
848 | |||
849 | return 0; | ||
850 | } | ||
851 | |||
852 | #ifdef CONFIG_PM_SLEEP | ||
853 | static int edt_ft5x06_ts_suspend(struct device *dev) | ||
854 | { | ||
855 | struct i2c_client *client = to_i2c_client(dev); | ||
856 | |||
857 | if (device_may_wakeup(dev)) | ||
858 | enable_irq_wake(client->irq); | ||
859 | |||
860 | return 0; | ||
861 | } | ||
862 | |||
863 | static int edt_ft5x06_ts_resume(struct device *dev) | ||
864 | { | ||
865 | struct i2c_client *client = to_i2c_client(dev); | ||
866 | |||
867 | if (device_may_wakeup(dev)) | ||
868 | disable_irq_wake(client->irq); | ||
869 | |||
870 | return 0; | ||
871 | } | ||
872 | #endif | ||
873 | |||
874 | static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops, | ||
875 | edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume); | ||
876 | |||
877 | static const struct i2c_device_id edt_ft5x06_ts_id[] = { | ||
878 | { "edt-ft5x06", 0 }, | ||
879 | { } | ||
880 | }; | ||
881 | MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id); | ||
882 | |||
883 | static struct i2c_driver edt_ft5x06_ts_driver = { | ||
884 | .driver = { | ||
885 | .owner = THIS_MODULE, | ||
886 | .name = "edt_ft5x06", | ||
887 | .pm = &edt_ft5x06_ts_pm_ops, | ||
888 | }, | ||
889 | .id_table = edt_ft5x06_ts_id, | ||
890 | .probe = edt_ft5x06_ts_probe, | ||
891 | .remove = __devexit_p(edt_ft5x06_ts_remove), | ||
892 | }; | ||
893 | |||
894 | module_i2c_driver(edt_ft5x06_ts_driver); | ||
895 | |||
896 | MODULE_AUTHOR("Simon Budig <simon.budig@kernelconcepts.de>"); | ||
897 | MODULE_DESCRIPTION("EDT FT5x06 I2C Touchscreen Driver"); | ||
898 | MODULE_LICENSE("GPL"); | ||