diff options
author | Noralf Trønnes <noralf@tronnes.org> | 2015-10-02 14:30:11 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2015-10-02 14:44:20 -0400 |
commit | 92deea1350f9fa97a841d45cd1f8228d5e8aa667 (patch) | |
tree | 6cabe72c00be0e5f0a7f426373f7651c4d28652a | |
parent | d6f0c3d3a815aca40f1ea9daf6ed0d1f9e243185 (diff) |
Input: add support for FocalTech FT6236 touchscreen controller
This adds support for the FT6x06 and the FT6x36 family of capacitive touch
panel controllers, in particular the FT6236.
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
-rw-r--r-- | Documentation/devicetree/bindings/input/touchscreen/focaltech-ft6236.txt | 35 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/vendor-prefixes.txt | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 13 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/ft6236.c | 327 |
5 files changed, 377 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/input/touchscreen/focaltech-ft6236.txt b/Documentation/devicetree/bindings/input/touchscreen/focaltech-ft6236.txt new file mode 100644 index 000000000000..777521da3da5 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/focaltech-ft6236.txt | |||
@@ -0,0 +1,35 @@ | |||
1 | * FocalTech FT6236 I2C touchscreen controller | ||
2 | |||
3 | Required properties: | ||
4 | - compatible : "focaltech,ft6236" | ||
5 | - reg : I2C slave address of the chip (0x38) | ||
6 | - interrupt-parent : a phandle pointing to the interrupt controller | ||
7 | serving the interrupt for this chip | ||
8 | - interrupts : interrupt specification for the touch controller | ||
9 | interrupt | ||
10 | - reset-gpios : GPIO specification for the RSTN input | ||
11 | - touchscreen-size-x : horizontal resolution of touchscreen (in pixels) | ||
12 | - touchscreen-size-y : vertical resolution of touchscreen (in pixels) | ||
13 | |||
14 | Optional properties: | ||
15 | - touchscreen-fuzz-x : horizontal noise value of the absolute input | ||
16 | device (in pixels) | ||
17 | - touchscreen-fuzz-y : vertical noise value of the absolute input | ||
18 | device (in pixels) | ||
19 | - touchscreen-inverted-x : X axis is inverted (boolean) | ||
20 | - touchscreen-inverted-y : Y axis is inverted (boolean) | ||
21 | - touchscreen-swapped-x-y: X and Y axis are swapped (boolean) | ||
22 | Swapping is done after inverting the axis | ||
23 | |||
24 | Example: | ||
25 | |||
26 | ft6x06@38 { | ||
27 | compatible = "focaltech,ft6236"; | ||
28 | reg = <0x38>; | ||
29 | interrupt-parent = <&gpio>; | ||
30 | interrupts = <23 2>; | ||
31 | touchscreen-size-x = <320>; | ||
32 | touchscreen-size-y = <480>; | ||
33 | touchscreen-inverted-x; | ||
34 | touchscreen-swapped-x-y; | ||
35 | }; | ||
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 82d2ac97af74..3222b2ff475e 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt | |||
@@ -82,6 +82,7 @@ everspin Everspin Technologies, Inc. | |||
82 | excito Excito | 82 | excito Excito |
83 | fcs Fairchild Semiconductor | 83 | fcs Fairchild Semiconductor |
84 | firefly Firefly | 84 | firefly Firefly |
85 | focaltech FocalTech Systems Co.,Ltd | ||
85 | fsl Freescale Semiconductor | 86 | fsl Freescale Semiconductor |
86 | GEFanuc GE Fanuc Intelligent Platforms Embedded Systems, Inc. | 87 | GEFanuc GE Fanuc Intelligent Platforms Embedded Systems, Inc. |
87 | gef GE Fanuc Intelligent Platforms Embedded Systems, Inc. | 88 | gef GE Fanuc Intelligent Platforms Embedded Systems, Inc. |
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 600dcceff542..eda89b6b7e11 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig | |||
@@ -295,6 +295,19 @@ config TOUCHSCREEN_EGALAX | |||
295 | To compile this driver as a module, choose M here: the | 295 | To compile this driver as a module, choose M here: the |
296 | module will be called egalax_ts. | 296 | module will be called egalax_ts. |
297 | 297 | ||
298 | config TOUCHSCREEN_FT6236 | ||
299 | tristate "FT6236 I2C touchscreen" | ||
300 | depends on I2C | ||
301 | depends on GPIOLIB || COMPILE_TEST | ||
302 | help | ||
303 | Say Y here to enable support for the I2C connected FT6x06 and | ||
304 | FT6x36 family of capacitive touchscreen drivers. | ||
305 | |||
306 | If unsure, say N. | ||
307 | |||
308 | To compile this driver as a module, choose M here: the | ||
309 | module will be called ft6236. | ||
310 | |||
298 | config TOUCHSCREEN_FUJITSU | 311 | config TOUCHSCREEN_FUJITSU |
299 | tristate "Fujitsu serial touchscreen" | 312 | tristate "Fujitsu serial touchscreen" |
300 | select SERIO | 313 | select SERIO |
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 1b79cc09744a..baba189e3e16 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile | |||
@@ -35,6 +35,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o | |||
35 | obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o | 35 | obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o |
36 | obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o | 36 | obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o |
37 | obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o | 37 | obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o |
38 | obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o | ||
38 | obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o | 39 | obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o |
39 | obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o | 40 | obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o |
40 | obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o | 41 | obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o |
diff --git a/drivers/input/touchscreen/ft6236.c b/drivers/input/touchscreen/ft6236.c new file mode 100644 index 000000000000..cccae193668e --- /dev/null +++ b/drivers/input/touchscreen/ft6236.c | |||
@@ -0,0 +1,327 @@ | |||
1 | /* | ||
2 | * FocalTech FT6236 TouchScreen driver. | ||
3 | * | ||
4 | * Copyright (c) 2010 Focal tech Ltd. | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | */ | ||
15 | |||
16 | #include <linux/delay.h> | ||
17 | #include <linux/gpio/consumer.h> | ||
18 | #include <linux/i2c.h> | ||
19 | #include <linux/input.h> | ||
20 | #include <linux/input/mt.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/property.h> | ||
24 | |||
25 | #define FT6236_MAX_TOUCH_POINTS 2 | ||
26 | |||
27 | #define FT6236_REG_TH_GROUP 0x80 | ||
28 | #define FT6236_REG_PERIODACTIVE 0x88 | ||
29 | #define FT6236_REG_LIB_VER_H 0xa1 | ||
30 | #define FT6236_REG_LIB_VER_L 0xa2 | ||
31 | #define FT6236_REG_CIPHER 0xa3 | ||
32 | #define FT6236_REG_FIRMID 0xa6 | ||
33 | #define FT6236_REG_FOCALTECH_ID 0xa8 | ||
34 | #define FT6236_REG_RELEASE_CODE_ID 0xaf | ||
35 | |||
36 | #define FT6236_EVENT_PRESS_DOWN 0 | ||
37 | #define FT6236_EVENT_LIFT_UP 1 | ||
38 | #define FT6236_EVENT_CONTACT 2 | ||
39 | #define FT6236_EVENT_NO_EVENT 3 | ||
40 | |||
41 | struct ft6236_data { | ||
42 | struct i2c_client *client; | ||
43 | struct input_dev *input; | ||
44 | struct gpio_desc *reset_gpio; | ||
45 | u32 max_x; | ||
46 | u32 max_y; | ||
47 | bool invert_x; | ||
48 | bool invert_y; | ||
49 | bool swap_xy; | ||
50 | }; | ||
51 | |||
52 | /* | ||
53 | * This struct is a touchpoint as stored in hardware. Note that the id, | ||
54 | * as well as the event, are stored in the upper nybble of the hi byte. | ||
55 | */ | ||
56 | struct ft6236_touchpoint { | ||
57 | union { | ||
58 | u8 xhi; | ||
59 | u8 event; | ||
60 | }; | ||
61 | u8 xlo; | ||
62 | union { | ||
63 | u8 yhi; | ||
64 | u8 id; | ||
65 | }; | ||
66 | u8 ylo; | ||
67 | u8 weight; | ||
68 | u8 misc; | ||
69 | } __packed; | ||
70 | |||
71 | /* This packet represents the register map as read from offset 0 */ | ||
72 | struct ft6236_packet { | ||
73 | u8 dev_mode; | ||
74 | u8 gest_id; | ||
75 | u8 touches; | ||
76 | struct ft6236_touchpoint points[FT6236_MAX_TOUCH_POINTS]; | ||
77 | } __packed; | ||
78 | |||
79 | static int ft6236_read(struct i2c_client *client, u8 reg, u8 len, void *data) | ||
80 | { | ||
81 | int error; | ||
82 | |||
83 | error = i2c_smbus_read_i2c_block_data(client, reg, len, data); | ||
84 | if (error < 0) | ||
85 | return error; | ||
86 | |||
87 | if (error != len) | ||
88 | return -EIO; | ||
89 | |||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static irqreturn_t ft6236_interrupt(int irq, void *dev_id) | ||
94 | { | ||
95 | struct ft6236_data *ft6236 = dev_id; | ||
96 | struct device *dev = &ft6236->client->dev; | ||
97 | struct input_dev *input = ft6236->input; | ||
98 | struct ft6236_packet buf; | ||
99 | u8 touches; | ||
100 | int i, error; | ||
101 | |||
102 | error = ft6236_read(ft6236->client, 0, sizeof(buf), &buf); | ||
103 | if (error) { | ||
104 | dev_err(dev, "read touchdata failed %d\n", error); | ||
105 | return IRQ_HANDLED; | ||
106 | } | ||
107 | |||
108 | touches = buf.touches & 0xf; | ||
109 | if (touches > FT6236_MAX_TOUCH_POINTS) { | ||
110 | dev_dbg(dev, | ||
111 | "%d touch points reported, only %d are supported\n", | ||
112 | touches, FT6236_MAX_TOUCH_POINTS); | ||
113 | touches = FT6236_MAX_TOUCH_POINTS; | ||
114 | } | ||
115 | |||
116 | for (i = 0; i < touches; i++) { | ||
117 | struct ft6236_touchpoint *point = &buf.points[i]; | ||
118 | u16 x = ((point->xhi & 0xf) << 8) | buf.points[i].xlo; | ||
119 | u16 y = ((point->yhi & 0xf) << 8) | buf.points[i].ylo; | ||
120 | u8 event = point->event >> 6; | ||
121 | u8 id = point->id >> 4; | ||
122 | bool act = (event == FT6236_EVENT_PRESS_DOWN || | ||
123 | event == FT6236_EVENT_CONTACT); | ||
124 | |||
125 | input_mt_slot(input, id); | ||
126 | input_mt_report_slot_state(input, MT_TOOL_FINGER, act); | ||
127 | if (!act) | ||
128 | continue; | ||
129 | |||
130 | if (ft6236->invert_x) | ||
131 | x = ft6236->max_x - x; | ||
132 | |||
133 | if (ft6236->invert_y) | ||
134 | y = ft6236->max_y - y; | ||
135 | |||
136 | if (ft6236->swap_xy) { | ||
137 | input_report_abs(input, ABS_MT_POSITION_X, y); | ||
138 | input_report_abs(input, ABS_MT_POSITION_Y, x); | ||
139 | } else { | ||
140 | input_report_abs(input, ABS_MT_POSITION_X, x); | ||
141 | input_report_abs(input, ABS_MT_POSITION_Y, y); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | input_mt_sync_frame(input); | ||
146 | input_sync(input); | ||
147 | |||
148 | return IRQ_HANDLED; | ||
149 | } | ||
150 | |||
151 | static u8 ft6236_debug_read_byte(struct ft6236_data *ft6236, u8 reg) | ||
152 | { | ||
153 | struct i2c_client *client = ft6236->client; | ||
154 | u8 val = 0; | ||
155 | int error; | ||
156 | |||
157 | error = ft6236_read(client, reg, 1, &val); | ||
158 | if (error) | ||
159 | dev_dbg(&client->dev, | ||
160 | "error reading register 0x%02x: %d\n", reg, error); | ||
161 | |||
162 | return val; | ||
163 | } | ||
164 | |||
165 | static void ft6236_debug_info(struct ft6236_data *ft6236) | ||
166 | { | ||
167 | struct device *dev = &ft6236->client->dev; | ||
168 | |||
169 | dev_dbg(dev, "Touch threshold is %d\n", | ||
170 | ft6236_debug_read_byte(ft6236, FT6236_REG_TH_GROUP) * 4); | ||
171 | dev_dbg(dev, "Report rate is %dHz\n", | ||
172 | ft6236_debug_read_byte(ft6236, FT6236_REG_PERIODACTIVE) * 10); | ||
173 | dev_dbg(dev, "Firmware library version 0x%02x%02x\n", | ||
174 | ft6236_debug_read_byte(ft6236, FT6236_REG_LIB_VER_H), | ||
175 | ft6236_debug_read_byte(ft6236, FT6236_REG_LIB_VER_L)); | ||
176 | dev_dbg(dev, "Firmware version 0x%02x\n", | ||
177 | ft6236_debug_read_byte(ft6236, FT6236_REG_FIRMID)); | ||
178 | dev_dbg(dev, "Chip vendor ID 0x%02x\n", | ||
179 | ft6236_debug_read_byte(ft6236, FT6236_REG_CIPHER)); | ||
180 | dev_dbg(dev, "CTPM vendor ID 0x%02x\n", | ||
181 | ft6236_debug_read_byte(ft6236, FT6236_REG_FOCALTECH_ID)); | ||
182 | dev_dbg(dev, "Release code version 0x%02x\n", | ||
183 | ft6236_debug_read_byte(ft6236, FT6236_REG_RELEASE_CODE_ID)); | ||
184 | } | ||
185 | |||
186 | static void ft6236_reset(struct ft6236_data *ft6236) | ||
187 | { | ||
188 | if (!ft6236->reset_gpio) | ||
189 | return; | ||
190 | |||
191 | gpiod_set_value_cansleep(ft6236->reset_gpio, 1); | ||
192 | usleep_range(5000, 20000); | ||
193 | gpiod_set_value_cansleep(ft6236->reset_gpio, 0); | ||
194 | msleep(300); | ||
195 | } | ||
196 | |||
197 | static int ft6236_probe(struct i2c_client *client, | ||
198 | const struct i2c_device_id *id) | ||
199 | { | ||
200 | struct device *dev = &client->dev; | ||
201 | struct ft6236_data *ft6236; | ||
202 | struct input_dev *input; | ||
203 | u32 fuzz_x = 0, fuzz_y = 0; | ||
204 | u8 val; | ||
205 | int error; | ||
206 | |||
207 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) | ||
208 | return -ENXIO; | ||
209 | |||
210 | if (!client->irq) { | ||
211 | dev_err(dev, "irq is missing\n"); | ||
212 | return -EINVAL; | ||
213 | } | ||
214 | |||
215 | ft6236 = devm_kzalloc(dev, sizeof(*ft6236), GFP_KERNEL); | ||
216 | if (!ft6236) | ||
217 | return -ENOMEM; | ||
218 | |||
219 | ft6236->client = client; | ||
220 | ft6236->reset_gpio = devm_gpiod_get_optional(dev, "reset", | ||
221 | GPIOD_OUT_LOW); | ||
222 | if (IS_ERR(ft6236->reset_gpio)) { | ||
223 | error = PTR_ERR(ft6236->reset_gpio); | ||
224 | if (error != -EPROBE_DEFER) | ||
225 | dev_err(dev, "error getting reset gpio: %d\n", error); | ||
226 | return error; | ||
227 | } | ||
228 | |||
229 | ft6236_reset(ft6236); | ||
230 | |||
231 | /* verify that the controller is present */ | ||
232 | error = ft6236_read(client, 0x00, 1, &val); | ||
233 | if (error) { | ||
234 | dev_err(dev, "failed to read from controller: %d\n", error); | ||
235 | return error; | ||
236 | } | ||
237 | |||
238 | ft6236_debug_info(ft6236); | ||
239 | |||
240 | input = devm_input_allocate_device(dev); | ||
241 | if (!input) | ||
242 | return -ENOMEM; | ||
243 | |||
244 | ft6236->input = input; | ||
245 | input->name = client->name; | ||
246 | input->id.bustype = BUS_I2C; | ||
247 | |||
248 | if (device_property_read_u32(dev, "touchscreen-size-x", | ||
249 | &ft6236->max_x) || | ||
250 | device_property_read_u32(dev, "touchscreen-size-y", | ||
251 | &ft6236->max_y)) { | ||
252 | dev_err(dev, "touchscreen-size-x and/or -y missing\n"); | ||
253 | return -EINVAL; | ||
254 | } | ||
255 | |||
256 | device_property_read_u32(dev, "touchscreen-fuzz-x", &fuzz_x); | ||
257 | device_property_read_u32(dev, "touchscreen-fuzz-y", &fuzz_y); | ||
258 | ft6236->invert_x = device_property_read_bool(dev, | ||
259 | "touchscreen-inverted-x"); | ||
260 | ft6236->invert_y = device_property_read_bool(dev, | ||
261 | "touchscreen-inverted-y"); | ||
262 | ft6236->swap_xy = device_property_read_bool(dev, | ||
263 | "touchscreen-swapped-x-y"); | ||
264 | |||
265 | if (ft6236->swap_xy) { | ||
266 | input_set_abs_params(input, ABS_MT_POSITION_X, 0, | ||
267 | ft6236->max_y, fuzz_y, 0); | ||
268 | input_set_abs_params(input, ABS_MT_POSITION_Y, 0, | ||
269 | ft6236->max_x, fuzz_x, 0); | ||
270 | } else { | ||
271 | input_set_abs_params(input, ABS_MT_POSITION_X, 0, | ||
272 | ft6236->max_x, fuzz_x, 0); | ||
273 | input_set_abs_params(input, ABS_MT_POSITION_Y, 0, | ||
274 | ft6236->max_y, fuzz_y, 0); | ||
275 | } | ||
276 | |||
277 | error = input_mt_init_slots(input, FT6236_MAX_TOUCH_POINTS, | ||
278 | INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); | ||
279 | if (error) | ||
280 | return error; | ||
281 | |||
282 | error = devm_request_threaded_irq(dev, client->irq, NULL, | ||
283 | ft6236_interrupt, IRQF_ONESHOT, | ||
284 | client->name, ft6236); | ||
285 | if (error) { | ||
286 | dev_err(dev, "request irq %d failed: %d\n", client->irq, error); | ||
287 | return error; | ||
288 | } | ||
289 | |||
290 | error = input_register_device(input); | ||
291 | if (error) { | ||
292 | dev_err(dev, "failed to register input device: %d\n", error); | ||
293 | return error; | ||
294 | } | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | #ifdef CONFIG_OF | ||
300 | static const struct of_device_id ft6236_of_match[] = { | ||
301 | { .compatible = "focaltech,ft6236", }, | ||
302 | { } | ||
303 | }; | ||
304 | MODULE_DEVICE_TABLE(of, ft6236_of_match); | ||
305 | #endif | ||
306 | |||
307 | static const struct i2c_device_id ft6236_id[] = { | ||
308 | { "ft6236", }, | ||
309 | { } | ||
310 | }; | ||
311 | MODULE_DEVICE_TABLE(i2c, ft6236_id); | ||
312 | |||
313 | static struct i2c_driver ft6236_driver = { | ||
314 | .driver = { | ||
315 | .name = "ft6236", | ||
316 | .owner = THIS_MODULE, | ||
317 | .of_match_table = of_match_ptr(ft6236_of_match), | ||
318 | }, | ||
319 | .probe = ft6236_probe, | ||
320 | .id_table = ft6236_id, | ||
321 | }; | ||
322 | module_i2c_driver(ft6236_driver); | ||
323 | |||
324 | MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>"); | ||
325 | MODULE_AUTHOR("Noralf Trønnes <noralf@tronnes.org>"); | ||
326 | MODULE_DESCRIPTION("FocalTech FT6236 TouchScreen driver"); | ||
327 | MODULE_LICENSE("GPL"); | ||