aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2015-03-21 23:40:45 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2015-03-24 14:27:52 -0400
commita93ad65d375f216025902a73ff25900d82a9de25 (patch)
tree5eabb55b0afc59c464fbbbe961f17c9f7c94a51e
parentd16a33bb94f88a40a99418993849c1eb93d60de3 (diff)
Input: add support for ChipOne icn8318 based touchscreens
The ChipOne icn8318 is an i2c capacitive touchscreen controller typically used in cheap android tablets, this commit adds a driver for it. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt46
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.txt1
-rw-r--r--MAINTAINERS7
-rw-r--r--drivers/input/touchscreen/Kconfig13
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/chipone_icn8318.c316
6 files changed, 384 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt b/Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt
new file mode 100644
index 000000000000..d11f8d615b5d
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt
@@ -0,0 +1,46 @@
1* ChipOne icn8318 I2C touchscreen controller
2
3Required properties:
4 - compatible : "chipone,icn8318"
5 - reg : I2C slave address of the chip (0x40)
6 - interrupt-parent : a phandle pointing to the interrupt controller
7 serving the interrupt for this chip
8 - interrupts : interrupt specification for the icn8318 interrupt
9 - wake-gpios : GPIO specification for the WAKE input
10 - touchscreen-size-x : horizontal resolution of touchscreen (in pixels)
11 - touchscreen-size-y : vertical resolution of touchscreen (in pixels)
12
13Optional properties:
14 - pinctrl-names : should be "default"
15 - pinctrl-0: : a phandle pointing to the pin settings for the
16 control gpios
17 - touchscreen-fuzz-x : horizontal noise value of the absolute input
18 device (in pixels)
19 - touchscreen-fuzz-y : vertical noise value of the absolute input
20 device (in pixels)
21 - touchscreen-inverted-x : X axis is inverted (boolean)
22 - touchscreen-inverted-y : Y axis is inverted (boolean)
23 - touchscreen-swapped-x-y : X and Y axis are swapped (boolean)
24 Swapping is done after inverting the axis
25
26Example:
27
28i2c@00000000 {
29 /* ... */
30
31 chipone_icn8318@40 {
32 compatible = "chipone,icn8318";
33 reg = <0x40>;
34 interrupt-parent = <&pio>;
35 interrupts = <9 IRQ_TYPE_EDGE_FALLING>; /* EINT9 (PG9) */
36 pinctrl-names = "default";
37 pinctrl-0 = <&ts_wake_pin_p66>;
38 wake-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */
39 touchscreen-size-x = <800>;
40 touchscreen-size-y = <480>;
41 touchscreen-inverted-x;
42 touchscreen-swapped-x-y;
43 };
44
45 /* ... */
46};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index c862cf94c9c8..728cd0e1b306 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -37,6 +37,7 @@ capella Capella Microsystems, Inc
37cavium Cavium, Inc. 37cavium Cavium, Inc.
38cdns Cadence Design Systems Inc. 38cdns Cadence Design Systems Inc.
39chipidea Chipidea, Inc 39chipidea Chipidea, Inc
40chipone ChipOne
40chipspark ChipSPARK 41chipspark ChipSPARK
41chrp Common Hardware Reference Platform 42chrp Common Hardware Reference Platform
42chunghwa Chunghwa Picture Tubes Ltd. 43chunghwa Chunghwa Picture Tubes Ltd.
diff --git a/MAINTAINERS b/MAINTAINERS
index 358eb0105e00..0a39a2843c11 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2523,6 +2523,13 @@ L: linux-usb@vger.kernel.org
2523S: Maintained 2523S: Maintained
2524F: drivers/usb/chipidea/ 2524F: drivers/usb/chipidea/
2525 2525
2526CHIPONE ICN8318 I2C TOUCHSCREEN DRIVER
2527M: Hans de Goede <hdegoede@redhat.com>
2528L: linux-input@vger.kernel.org
2529S: Maintained
2530F: Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt
2531F: drivers/input/touchscreen/chipone_icn8318.c
2532
2526CHROME HARDWARE PLATFORM SUPPORT 2533CHROME HARDWARE PLATFORM SUPPORT
2527M: Olof Johansson <olof@lixom.net> 2534M: Olof Johansson <olof@lixom.net>
2528S: Maintained 2535S: Maintained
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index a1cfe16e9a81..547f67d65372 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -140,6 +140,19 @@ config TOUCHSCREEN_BU21013
140 To compile this driver as a module, choose M here: the 140 To compile this driver as a module, choose M here: the
141 module will be called bu21013_ts. 141 module will be called bu21013_ts.
142 142
143config TOUCHSCREEN_CHIPONE_ICN8318
144 tristate "chipone icn8318 touchscreen controller"
145 depends on GPIOLIB
146 depends on I2C
147 depends on OF
148 help
149 Say Y here if you have a ChipOne icn8318 based I2C touchscreen.
150
151 If unsure, say N.
152
153 To compile this driver as a module, choose M here: the
154 module will be called chipone_icn8318.
155
143config TOUCHSCREEN_CY8CTMG110 156config TOUCHSCREEN_CY8CTMG110
144 tristate "cy8ctmg110 touchscreen" 157 tristate "cy8ctmg110 touchscreen"
145 depends on I2C 158 depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 09c33531ab8e..44deea743d02 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C) += ar1021_i2c.o
17obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o 17obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o
18obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o 18obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o
19obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o 19obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
20obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o
20obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o 21obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
21obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o 22obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
22obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o 23obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o
diff --git a/drivers/input/touchscreen/chipone_icn8318.c b/drivers/input/touchscreen/chipone_icn8318.c
new file mode 100644
index 000000000000..32e9db0e04bf
--- /dev/null
+++ b/drivers/input/touchscreen/chipone_icn8318.c
@@ -0,0 +1,316 @@
1/*
2 * Driver for ChipOne icn8318 i2c touchscreen controller
3 *
4 * Copyright (c) 2015 Red Hat Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Red Hat authors:
12 * Hans de Goede <hdegoede@redhat.com>
13 */
14
15#include <linux/gpio/consumer.h>
16#include <linux/interrupt.h>
17#include <linux/i2c.h>
18#include <linux/input.h>
19#include <linux/input/mt.h>
20#include <linux/module.h>
21#include <linux/of.h>
22
23#define ICN8318_REG_POWER 4
24#define ICN8318_REG_TOUCHDATA 16
25
26#define ICN8318_POWER_ACTIVE 0
27#define ICN8318_POWER_MONITOR 1
28#define ICN8318_POWER_HIBERNATE 2
29
30#define ICN8318_MAX_TOUCHES 5
31
32struct icn8318_touch {
33 __u8 slot;
34 __be16 x;
35 __be16 y;
36 __u8 pressure; /* Seems more like finger width then pressure really */
37 __u8 event;
38/* The difference between 2 and 3 is unclear */
39#define ICN8318_EVENT_NO_DATA 1 /* No finger seen yet since wakeup */
40#define ICN8318_EVENT_UPDATE1 2 /* New or updated coordinates */
41#define ICN8318_EVENT_UPDATE2 3 /* New or updated coordinates */
42#define ICN8318_EVENT_END 4 /* Finger lifted */
43} __packed;
44
45struct icn8318_touch_data {
46 __u8 softbutton;
47 __u8 touch_count;
48 struct icn8318_touch touches[ICN8318_MAX_TOUCHES];
49} __packed;
50
51struct icn8318_data {
52 struct i2c_client *client;
53 struct input_dev *input;
54 struct gpio_desc *wake_gpio;
55 u32 max_x;
56 u32 max_y;
57 bool invert_x;
58 bool invert_y;
59 bool swap_x_y;
60};
61
62static int icn8318_read_touch_data(struct i2c_client *client,
63 struct icn8318_touch_data *touch_data)
64{
65 u8 reg = ICN8318_REG_TOUCHDATA;
66 struct i2c_msg msg[2] = {
67 {
68 .addr = client->addr,
69 .len = 1,
70 .buf = &reg
71 },
72 {
73 .addr = client->addr,
74 .flags = I2C_M_RD,
75 .len = sizeof(struct icn8318_touch_data),
76 .buf = (u8 *)touch_data
77 }
78 };
79
80 return i2c_transfer(client->adapter, msg, 2);
81}
82
83static inline bool icn8318_touch_active(u8 event)
84{
85 return (event == ICN8318_EVENT_UPDATE1) ||
86 (event == ICN8318_EVENT_UPDATE2);
87}
88
89static irqreturn_t icn8318_irq(int irq, void *dev_id)
90{
91 struct icn8318_data *data = dev_id;
92 struct device *dev = &data->client->dev;
93 struct icn8318_touch_data touch_data;
94 int i, ret, x, y;
95
96 ret = icn8318_read_touch_data(data->client, &touch_data);
97 if (ret < 0) {
98 dev_err(dev, "Error reading touch data: %d\n", ret);
99 return IRQ_HANDLED;
100 }
101
102 if (touch_data.softbutton) {
103 /*
104 * Other data is invalid when a softbutton is pressed.
105 * This needs some extra devicetree bindings to map the icn8318
106 * softbutton codes to evdev codes. Currently no known devices
107 * use this.
108 */
109 return IRQ_HANDLED;
110 }
111
112 if (touch_data.touch_count > ICN8318_MAX_TOUCHES) {
113 dev_warn(dev, "Too much touches %d > %d\n",
114 touch_data.touch_count, ICN8318_MAX_TOUCHES);
115 touch_data.touch_count = ICN8318_MAX_TOUCHES;
116 }
117
118 for (i = 0; i < touch_data.touch_count; i++) {
119 struct icn8318_touch *touch = &touch_data.touches[i];
120 bool act = icn8318_touch_active(touch->event);
121
122 input_mt_slot(data->input, touch->slot);
123 input_mt_report_slot_state(data->input, MT_TOOL_FINGER, act);
124 if (!act)
125 continue;
126
127 x = be16_to_cpu(touch->x);
128 y = be16_to_cpu(touch->y);
129
130 if (data->invert_x)
131 x = data->max_x - x;
132
133 if (data->invert_y)
134 y = data->max_y - y;
135
136 if (!data->swap_x_y) {
137 input_event(data->input, EV_ABS, ABS_MT_POSITION_X, x);
138 input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, y);
139 } else {
140 input_event(data->input, EV_ABS, ABS_MT_POSITION_X, y);
141 input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, x);
142 }
143 }
144
145 input_mt_sync_frame(data->input);
146 input_sync(data->input);
147
148 return IRQ_HANDLED;
149}
150
151static int icn8318_start(struct input_dev *dev)
152{
153 struct icn8318_data *data = input_get_drvdata(dev);
154
155 enable_irq(data->client->irq);
156 gpiod_set_value_cansleep(data->wake_gpio, 1);
157
158 return 0;
159}
160
161static void icn8318_stop(struct input_dev *dev)
162{
163 struct icn8318_data *data = input_get_drvdata(dev);
164
165 disable_irq(data->client->irq);
166 i2c_smbus_write_byte_data(data->client, ICN8318_REG_POWER,
167 ICN8318_POWER_HIBERNATE);
168 gpiod_set_value_cansleep(data->wake_gpio, 0);
169}
170
171#ifdef CONFIG_PM_SLEEP
172static int icn8318_suspend(struct device *dev)
173{
174 struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev));
175
176 mutex_lock(&data->input->mutex);
177 if (data->input->users)
178 icn8318_stop(data->input);
179 mutex_unlock(&data->input->mutex);
180
181 return 0;
182}
183
184static int icn8318_resume(struct device *dev)
185{
186 struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev));
187
188 mutex_lock(&data->input->mutex);
189 if (data->input->users)
190 icn8318_start(data->input);
191 mutex_unlock(&data->input->mutex);
192
193 return 0;
194}
195#endif
196
197static SIMPLE_DEV_PM_OPS(icn8318_pm_ops, icn8318_suspend, icn8318_resume);
198
199static int icn8318_probe(struct i2c_client *client,
200 const struct i2c_device_id *id)
201{
202 struct device *dev = &client->dev;
203 struct device_node *np = dev->of_node;
204 struct icn8318_data *data;
205 struct input_dev *input;
206 u32 fuzz_x = 0, fuzz_y = 0;
207 int error;
208
209 if (!client->irq) {
210 dev_err(dev, "Error no irq specified\n");
211 return -EINVAL;
212 }
213
214 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
215 if (!data)
216 return -ENOMEM;
217
218 data->wake_gpio = devm_gpiod_get(dev, "wake", GPIOD_OUT_LOW);
219 if (IS_ERR(data->wake_gpio)) {
220 error = PTR_ERR(data->wake_gpio);
221 if (error != -EPROBE_DEFER)
222 dev_err(dev, "Error getting wake gpio: %d\n", error);
223 return error;
224 }
225
226 if (of_property_read_u32(np, "touchscreen-size-x", &data->max_x) ||
227 of_property_read_u32(np, "touchscreen-size-y", &data->max_y)) {
228 dev_err(dev, "Error touchscreen-size-x and/or -y missing\n");
229 return -EINVAL;
230 }
231
232 /* Optional */
233 of_property_read_u32(np, "touchscreen-fuzz-x", &fuzz_x);
234 of_property_read_u32(np, "touchscreen-fuzz-y", &fuzz_y);
235 data->invert_x = of_property_read_bool(np, "touchscreen-inverted-x");
236 data->invert_y = of_property_read_bool(np, "touchscreen-inverted-y");
237 data->swap_x_y = of_property_read_bool(np, "touchscreen-swapped-x-y");
238
239 input = devm_input_allocate_device(dev);
240 if (!input)
241 return -ENOMEM;
242
243 input->name = client->name;
244 input->id.bustype = BUS_I2C;
245 input->open = icn8318_start;
246 input->close = icn8318_stop;
247 input->dev.parent = dev;
248
249 if (!data->swap_x_y) {
250 input_set_abs_params(input, ABS_MT_POSITION_X, 0,
251 data->max_x, fuzz_x, 0);
252 input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
253 data->max_y, fuzz_y, 0);
254 } else {
255 input_set_abs_params(input, ABS_MT_POSITION_X, 0,
256 data->max_y, fuzz_y, 0);
257 input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
258 data->max_x, fuzz_x, 0);
259 }
260
261 error = input_mt_init_slots(input, ICN8318_MAX_TOUCHES,
262 INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
263 if (error)
264 return error;
265
266 data->client = client;
267 data->input = input;
268 input_set_drvdata(input, data);
269
270 error = devm_request_threaded_irq(dev, client->irq, NULL, icn8318_irq,
271 IRQF_ONESHOT, client->name, data);
272 if (error) {
273 dev_err(dev, "Error requesting irq: %d\n", error);
274 return error;
275 }
276
277 /* Stop device till opened */
278 icn8318_stop(data->input);
279
280 error = input_register_device(input);
281 if (error)
282 return error;
283
284 i2c_set_clientdata(client, data);
285
286 return 0;
287}
288
289static const struct of_device_id icn8318_of_match[] = {
290 { .compatible = "chipone,icn8318" },
291 { }
292};
293MODULE_DEVICE_TABLE(of, icn8318_of_match);
294
295/* This is useless for OF-enabled devices, but it is needed by I2C subsystem */
296static const struct i2c_device_id icn8318_i2c_id[] = {
297 { },
298};
299MODULE_DEVICE_TABLE(i2c, icn8318_i2c_id);
300
301static struct i2c_driver icn8318_driver = {
302 .driver = {
303 .owner = THIS_MODULE,
304 .name = "chipone_icn8318",
305 .pm = &icn8318_pm_ops,
306 .of_match_table = icn8318_of_match,
307 },
308 .probe = icn8318_probe,
309 .id_table = icn8318_i2c_id,
310};
311
312module_i2c_driver(icn8318_driver);
313
314MODULE_DESCRIPTION("ChipOne icn8318 I2C Touchscreen Driver");
315MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
316MODULE_LICENSE("GPL");