aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMika Penttilä <mika.penttila@nextfour.com>2016-08-03 02:52:32 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2016-08-03 03:49:00 -0400
commita485cb037fe64367ec14813f018edb87799c5eb1 (patch)
tree5fa170560a3084e3dd2ac451e6ef3576a3cd41fa
parentb27c0d0c3bf3073e8ae19875eb1d3755c5e8c072 (diff)
Input: add driver for SiS 9200 family I2C touchscreen controllers
This is a driver for SiS 9200 family touchscreen controllers using I2C bus. Signed-off-by: Mika Penttilä <mika.penttila@nextfour.com> Acked-by: Tammy Tseng <tammy_tseng@sis.com> Acked-by: Yuger Yu <yuger_yu@sis.com> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/sis_i2c.txt33
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.txt1
-rw-r--r--drivers/input/touchscreen/Kconfig12
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/sis_i2c.c413
5 files changed, 460 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/input/touchscreen/sis_i2c.txt b/Documentation/devicetree/bindings/input/touchscreen/sis_i2c.txt
new file mode 100644
index 000000000000..d87ad14f1efe
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/sis_i2c.txt
@@ -0,0 +1,33 @@
1* SiS I2C Multiple Touch Controller
2
3Required properties:
4- compatible: must be "sis,9200-ts"
5- reg: i2c slave address
6- interrupt-parent: the phandle for the interrupt controller
7 (see interrupt binding [0])
8- interrupts: touch controller interrupt (see interrupt
9 binding [0])
10
11Optional properties:
12- pinctrl-names: should be "default" (see pinctrl binding [1]).
13- pinctrl-0: a phandle pointing to the pin settings for the
14 device (see pinctrl binding [1]).
15- attn-gpios: the gpio pin used as attention line
16- reset-gpios: the gpio pin used to reset the controller
17- wakeup-source: touchscreen can be used as a wakeup source
18
19[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
20[1]: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
21
22Example:
23
24 sis9255@5c {
25 compatible = "sis,9200-ts";
26 reg = <0x5c>;
27 pinctrl-names = "default";
28 pinctrl-0 = <&pinctrl_sis>;
29 interrupt-parent = <&gpio3>;
30 interrupts = <19 IRQ_TYPE_EDGE_FALLING>;
31 irq-gpios = <&gpio3 19 GPIO_ACTIVE_LOW>;
32 reset-gpios = <&gpio2 30 GPIO_ACTIVE_LOW>;
33 };
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 80fdbe2ce088..99029cfed459 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -221,6 +221,7 @@ simtek
221sii Seiko Instruments, Inc. 221sii Seiko Instruments, Inc.
222silergy Silergy Corp. 222silergy Silergy Corp.
223sirf SiRF Technology, Inc. 223sirf SiRF Technology, Inc.
224sis Silicon Integrated Systems Corp.
224sitronix Sitronix Technology Corporation 225sitronix Sitronix Technology Corporation
225skyworks Skyworks Solutions, Inc. 226skyworks Skyworks Solutions, Inc.
226smsc Standard Microsystems Corporation 227smsc Standard Microsystems Corporation
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 6abcd1766092..8d893cefb0e3 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1071,6 +1071,18 @@ config TOUCHSCREEN_SILEAD
1071 To compile this driver as a module, choose M here: the 1071 To compile this driver as a module, choose M here: the
1072 module will be called silead. 1072 module will be called silead.
1073 1073
1074config TOUCHSCREEN_SIS_I2C
1075 tristate "SiS 9200 family I2C touchscreen"
1076 depends on I2C
1077 depends on GPIOLIB || COMPILE_TEST
1078 help
1079 This enables support for SiS 9200 family over I2C based touchscreens.
1080
1081 If unsure, say N.
1082
1083 To compile this driver as a module, choose M here: the
1084 module will be called sis_i2c.
1085
1074config TOUCHSCREEN_ST1232 1086config TOUCHSCREEN_ST1232
1075 tristate "Sitronix ST1232 touchscreen controllers" 1087 tristate "Sitronix ST1232 touchscreen controllers"
1076 depends on I2C 1088 depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 3b598c901677..b4373d6be402 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o
65obj-$(CONFIG_TOUCHSCREEN_RM_TS) += raydium_i2c_ts.o 65obj-$(CONFIG_TOUCHSCREEN_RM_TS) += raydium_i2c_ts.o
66obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o 66obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
67obj-$(CONFIG_TOUCHSCREEN_SILEAD) += silead.o 67obj-$(CONFIG_TOUCHSCREEN_SILEAD) += silead.o
68obj-$(CONFIG_TOUCHSCREEN_SIS_I2C) += sis_i2c.o
68obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o 69obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o
69obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o 70obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
70obj-$(CONFIG_TOUCHSCREEN_SUN4I) += sun4i-ts.o 71obj-$(CONFIG_TOUCHSCREEN_SUN4I) += sun4i-ts.o
diff --git a/drivers/input/touchscreen/sis_i2c.c b/drivers/input/touchscreen/sis_i2c.c
new file mode 100644
index 000000000000..8d93f8c9a403
--- /dev/null
+++ b/drivers/input/touchscreen/sis_i2c.c
@@ -0,0 +1,413 @@
1/*
2 * Touch Screen driver for SiS 9200 family I2C Touch panels
3 *
4 * Copyright (C) 2015 SiS, Inc.
5 * Copyright (C) 2016 Nextfour Group
6 *
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#include <linux/crc-itu-t.h>
18#include <linux/delay.h>
19#include <linux/i2c.h>
20#include <linux/input.h>
21#include <linux/input/mt.h>
22#include <linux/interrupt.h>
23#include <linux/gpio/consumer.h>
24#include <linux/module.h>
25#include <linux/slab.h>
26#include <asm/unaligned.h>
27
28#define SIS_I2C_NAME "sis_i2c_ts"
29
30/*
31 * The I2C packet format:
32 * le16 byte count
33 * u8 Report ID
34 * <contact data - variable length>
35 * u8 Number of contacts
36 * le16 Scan Time (optional)
37 * le16 CRC
38 *
39 * One touch point information consists of 6+ bytes, the order is:
40 * u8 contact state
41 * u8 finger id
42 * le16 x axis
43 * le16 y axis
44 * u8 contact width (optional)
45 * u8 contact height (optional)
46 * u8 pressure (optional)
47 *
48 * Maximum amount of data transmitted in one shot is 64 bytes, if controller
49 * needs to report more contacts than fit in one packet it will send true
50 * number of contacts in first packet and 0 as number of contacts in second
51 * packet.
52 */
53
54#define SIS_MAX_PACKET_SIZE 64
55
56#define SIS_PKT_LEN_OFFSET 0
57#define SIS_PKT_REPORT_OFFSET 2 /* Report ID/type */
58#define SIS_PKT_CONTACT_OFFSET 3 /* First contact */
59
60#define SIS_SCAN_TIME_LEN 2
61
62/* Supported report types */
63#define SIS_ALL_IN_ONE_PACKAGE 0x10
64#define SIS_PKT_IS_TOUCH(x) (((x) & 0x0f) == 0x01)
65#define SIS_PKT_IS_HIDI2C(x) (((x) & 0x0f) == 0x06)
66
67/* Contact properties within report */
68#define SIS_PKT_HAS_AREA(x) ((x) & BIT(4))
69#define SIS_PKT_HAS_PRESSURE(x) ((x) & BIT(5))
70#define SIS_PKT_HAS_SCANTIME(x) ((x) & BIT(6))
71
72/* Contact size */
73#define SIS_BASE_LEN_PER_CONTACT 6
74#define SIS_AREA_LEN_PER_CONTACT 2
75#define SIS_PRESSURE_LEN_PER_CONTACT 1
76
77/* Offsets within contact data */
78#define SIS_CONTACT_STATUS_OFFSET 0
79#define SIS_CONTACT_ID_OFFSET 1 /* Contact ID */
80#define SIS_CONTACT_X_OFFSET 2
81#define SIS_CONTACT_Y_OFFSET 4
82#define SIS_CONTACT_WIDTH_OFFSET 6
83#define SIS_CONTACT_HEIGHT_OFFSET 7
84#define SIS_CONTACT_PRESSURE_OFFSET(id) (SIS_PKT_HAS_AREA(id) ? 8 : 6)
85
86/* Individual contact state */
87#define SIS_STATUS_UP 0x0
88#define SIS_STATUS_DOWN 0x3
89
90/* Touchscreen parameters */
91#define SIS_MAX_FINGERS 10
92#define SIS_MAX_X 4095
93#define SIS_MAX_Y 4095
94#define SIS_MAX_PRESSURE 255
95
96/* Resolution diagonal */
97#define SIS_AREA_LENGTH_LONGER 5792
98/*((SIS_MAX_X^2) + (SIS_MAX_Y^2))^0.5*/
99#define SIS_AREA_LENGTH_SHORT 5792
100#define SIS_AREA_UNIT (5792 / 32)
101
102struct sis_ts_data {
103 struct i2c_client *client;
104 struct input_dev *input;
105
106 struct gpio_desc *attn_gpio;
107 struct gpio_desc *reset_gpio;
108
109 u8 packet[SIS_MAX_PACKET_SIZE];
110};
111
112static int sis_read_packet(struct i2c_client *client, u8 *buf,
113 unsigned int *num_contacts,
114 unsigned int *contact_size)
115{
116 int count_idx;
117 int ret;
118 u16 len;
119 u16 crc, pkg_crc;
120 u8 report_id;
121
122 ret = i2c_master_recv(client, buf, SIS_MAX_PACKET_SIZE);
123 if (ret <= 0)
124 return -EIO;
125
126 len = get_unaligned_le16(&buf[SIS_PKT_LEN_OFFSET]);
127 if (len > SIS_MAX_PACKET_SIZE) {
128 dev_err(&client->dev,
129 "%s: invalid packet length (%d vs %d)\n",
130 __func__, len, SIS_MAX_PACKET_SIZE);
131 return -E2BIG;
132 }
133
134 if (len < 10)
135 return -EINVAL;
136
137 report_id = buf[SIS_PKT_REPORT_OFFSET];
138 count_idx = len - 1;
139 *contact_size = SIS_BASE_LEN_PER_CONTACT;
140
141 if (report_id != SIS_ALL_IN_ONE_PACKAGE) {
142 if (SIS_PKT_IS_TOUCH(report_id)) {
143 /*
144 * Calculate CRC ignoring packet length
145 * in the beginning and CRC transmitted
146 * at the end of the packet.
147 */
148 crc = crc_itu_t(0, buf + 2, len - 2 - 2);
149 pkg_crc = get_unaligned_le16(&buf[len - 2]);
150
151 if (crc != pkg_crc) {
152 dev_err(&client->dev,
153 "%s: CRC Error (%d vs %d)\n",
154 __func__, crc, pkg_crc);
155 return -EINVAL;
156 }
157
158 count_idx -= 2;
159
160 } else if (!SIS_PKT_IS_HIDI2C(report_id)) {
161 dev_err(&client->dev,
162 "%s: invalid packet ID %#02x\n",
163 __func__, report_id);
164 return -EINVAL;
165 }
166
167 if (SIS_PKT_HAS_SCANTIME(report_id))
168 count_idx -= SIS_SCAN_TIME_LEN;
169
170 if (SIS_PKT_HAS_AREA(report_id))
171 *contact_size += SIS_AREA_LEN_PER_CONTACT;
172 if (SIS_PKT_HAS_PRESSURE(report_id))
173 *contact_size += SIS_PRESSURE_LEN_PER_CONTACT;
174 }
175
176 *num_contacts = buf[count_idx];
177 return 0;
178}
179
180static int sis_ts_report_contact(struct sis_ts_data *ts, const u8 *data, u8 id)
181{
182 struct input_dev *input = ts->input;
183 int slot;
184 u8 status = data[SIS_CONTACT_STATUS_OFFSET];
185 u8 pressure;
186 u8 height, width;
187 u16 x, y;
188
189 if (status != SIS_STATUS_DOWN && status != SIS_STATUS_UP) {
190 dev_err(&ts->client->dev, "Unexpected touch status: %#02x\n",
191 data[SIS_CONTACT_STATUS_OFFSET]);
192 return -EINVAL;
193 }
194
195 slot = input_mt_get_slot_by_key(input, data[SIS_CONTACT_ID_OFFSET]);
196 if (slot < 0)
197 return -ENOENT;
198
199 input_mt_slot(input, slot);
200 input_mt_report_slot_state(input, MT_TOOL_FINGER,
201 status == SIS_STATUS_DOWN);
202
203 if (status == SIS_STATUS_DOWN) {
204 pressure = height = width = 1;
205 if (id != SIS_ALL_IN_ONE_PACKAGE) {
206 if (SIS_PKT_HAS_AREA(id)) {
207 width = data[SIS_CONTACT_WIDTH_OFFSET];
208 height = data[SIS_CONTACT_HEIGHT_OFFSET];
209 }
210
211 if (SIS_PKT_HAS_PRESSURE(id))
212 pressure =
213 data[SIS_CONTACT_PRESSURE_OFFSET(id)];
214 }
215
216 x = get_unaligned_le16(&data[SIS_CONTACT_X_OFFSET]);
217 y = get_unaligned_le16(&data[SIS_CONTACT_Y_OFFSET]);
218
219 input_report_abs(input, ABS_MT_TOUCH_MAJOR,
220 width * SIS_AREA_UNIT);
221 input_report_abs(input, ABS_MT_TOUCH_MINOR,
222 height * SIS_AREA_UNIT);
223 input_report_abs(input, ABS_MT_PRESSURE, pressure);
224 input_report_abs(input, ABS_MT_POSITION_X, x);
225 input_report_abs(input, ABS_MT_POSITION_Y, y);
226 }
227
228 return 0;
229}
230
231static void sis_ts_handle_packet(struct sis_ts_data *ts)
232{
233 const u8 *contact;
234 unsigned int num_to_report = 0;
235 unsigned int num_contacts;
236 unsigned int num_reported;
237 unsigned int contact_size;
238 int error;
239 u8 report_id;
240
241 do {
242 error = sis_read_packet(ts->client, ts->packet,
243 &num_contacts, &contact_size);
244 if (error)
245 break;
246
247 if (num_to_report == 0) {
248 num_to_report = num_contacts;
249 } else if (num_contacts != 0) {
250 dev_err(&ts->client->dev,
251 "%s: nonzero (%d) point count in tail packet\n",
252 __func__, num_contacts);
253 break;
254 }
255
256 report_id = ts->packet[SIS_PKT_REPORT_OFFSET];
257 contact = &ts->packet[SIS_PKT_CONTACT_OFFSET];
258 num_reported = 0;
259
260 while (num_to_report > 0) {
261 error = sis_ts_report_contact(ts, contact, report_id);
262 if (error)
263 break;
264
265 contact += contact_size;
266 num_to_report--;
267 num_reported++;
268
269 if (report_id != SIS_ALL_IN_ONE_PACKAGE &&
270 num_reported >= 5) {
271 /*
272 * The remainder of contacts is sent
273 * in the 2nd packet.
274 */
275 break;
276 }
277 }
278 } while (num_to_report > 0);
279
280 input_mt_sync_frame(ts->input);
281 input_sync(ts->input);
282}
283
284static irqreturn_t sis_ts_irq_handler(int irq, void *dev_id)
285{
286 struct sis_ts_data *ts = dev_id;
287
288 do {
289 sis_ts_handle_packet(ts);
290 } while (ts->attn_gpio && gpiod_get_value_cansleep(ts->attn_gpio));
291
292 return IRQ_HANDLED;
293}
294
295static void sis_ts_reset(struct sis_ts_data *ts)
296{
297 if (ts->reset_gpio) {
298 /* Get out of reset */
299 usleep_range(1000, 2000);
300 gpiod_set_value(ts->reset_gpio, 1);
301 usleep_range(1000, 2000);
302 gpiod_set_value(ts->reset_gpio, 0);
303 msleep(100);
304 }
305}
306
307static int sis_ts_probe(struct i2c_client *client,
308 const struct i2c_device_id *id)
309{
310 struct sis_ts_data *ts;
311 struct input_dev *input;
312 int error;
313
314 ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
315 if (!ts)
316 return -ENOMEM;
317
318 ts->client = client;
319 i2c_set_clientdata(client, ts);
320
321 ts->attn_gpio = devm_gpiod_get_optional(&client->dev,
322 "attn", GPIOD_IN);
323 if (IS_ERR(ts->attn_gpio)) {
324 error = PTR_ERR(ts->attn_gpio);
325 if (error != -EPROBE_DEFER)
326 dev_err(&client->dev,
327 "Failed to get attention GPIO: %d\n", error);
328 return error;
329 }
330
331 ts->reset_gpio = devm_gpiod_get_optional(&client->dev,
332 "reset", GPIOD_OUT_LOW);
333 if (IS_ERR(ts->reset_gpio)) {
334 error = PTR_ERR(ts->reset_gpio);
335 if (error != -EPROBE_DEFER)
336 dev_err(&client->dev,
337 "Failed to get reset GPIO: %d\n", error);
338 return error;
339 }
340
341 sis_ts_reset(ts);
342
343 ts->input = input = devm_input_allocate_device(&client->dev);
344 if (!input) {
345 dev_err(&client->dev, "Failed to allocate input device\n");
346 return -ENOMEM;
347 }
348
349 input->name = "SiS Touchscreen";
350 input->id.bustype = BUS_I2C;
351
352 input_set_abs_params(input, ABS_MT_POSITION_X, 0, SIS_MAX_X, 0, 0);
353 input_set_abs_params(input, ABS_MT_POSITION_Y, 0, SIS_MAX_Y, 0, 0);
354 input_set_abs_params(input, ABS_MT_PRESSURE, 0, SIS_MAX_PRESSURE, 0, 0);
355 input_set_abs_params(input, ABS_MT_TOUCH_MAJOR,
356 0, SIS_AREA_LENGTH_LONGER, 0, 0);
357 input_set_abs_params(input, ABS_MT_TOUCH_MINOR,
358 0, SIS_AREA_LENGTH_SHORT, 0, 0);
359
360 error = input_mt_init_slots(input, SIS_MAX_FINGERS, INPUT_MT_DIRECT);
361 if (error) {
362 dev_err(&client->dev,
363 "Failed to initialize MT slots: %d\n", error);
364 return error;
365 }
366
367 error = devm_request_threaded_irq(&client->dev, client->irq,
368 NULL, sis_ts_irq_handler,
369 IRQF_ONESHOT,
370 client->name, ts);
371 if (error) {
372 dev_err(&client->dev, "Failed to request IRQ: %d\n", error);
373 return error;
374 }
375
376 error = input_register_device(ts->input);
377 if (error) {
378 dev_err(&client->dev,
379 "Failed to register input device: %d\n", error);
380 return error;
381 }
382
383 return 0;
384}
385
386#ifdef CONFIG_OF
387static const struct of_device_id sis_ts_dt_ids[] = {
388 { .compatible = "sis,9200-ts" },
389 { /* sentinel */ }
390};
391MODULE_DEVICE_TABLE(of, sis_ts_dt_ids);
392#endif
393
394static const struct i2c_device_id sis_ts_id[] = {
395 { SIS_I2C_NAME, 0 },
396 { "9200-ts", 0 },
397 { /* sentinel */ }
398};
399MODULE_DEVICE_TABLE(i2c, sis_ts_id);
400
401static struct i2c_driver sis_ts_driver = {
402 .driver = {
403 .name = SIS_I2C_NAME,
404 .of_match_table = of_match_ptr(sis_ts_dt_ids),
405 },
406 .probe = sis_ts_probe,
407 .id_table = sis_ts_id,
408};
409module_i2c_driver(sis_ts_driver);
410
411MODULE_DESCRIPTION("SiS 9200 Family Touchscreen Driver");
412MODULE_LICENSE("GPL v2");
413MODULE_AUTHOR("Mika Penttilä <mika.penttila@nextfour.com>");