Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Mika Penttilä and committed by
Dmitry Torokhov
a485cb03 b27c0d0c

+460
+33
Documentation/devicetree/bindings/input/touchscreen/sis_i2c.txt
··· 1 + * SiS I2C Multiple Touch Controller 2 + 3 + Required 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 + 11 + Optional 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 + 22 + Example: 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 + };
+1
Documentation/devicetree/bindings/vendor-prefixes.txt
··· 221 221 sii Seiko Instruments, Inc. 222 222 silergy Silergy Corp. 223 223 sirf SiRF Technology, Inc. 224 + sis Silicon Integrated Systems Corp. 224 225 sitronix Sitronix Technology Corporation 225 226 skyworks Skyworks Solutions, Inc. 226 227 smsc Standard Microsystems Corporation
+12
drivers/input/touchscreen/Kconfig
··· 1071 1071 To compile this driver as a module, choose M here: the 1072 1072 module will be called silead. 1073 1073 1074 + config 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 + 1074 1086 config TOUCHSCREEN_ST1232 1075 1087 tristate "Sitronix ST1232 touchscreen controllers" 1076 1088 depends on I2C
+1
drivers/input/touchscreen/Makefile
··· 65 65 obj-$(CONFIG_TOUCHSCREEN_RM_TS) += raydium_i2c_ts.o 66 66 obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o 67 67 obj-$(CONFIG_TOUCHSCREEN_SILEAD) += silead.o 68 + obj-$(CONFIG_TOUCHSCREEN_SIS_I2C) += sis_i2c.o 68 69 obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o 69 70 obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o 70 71 obj-$(CONFIG_TOUCHSCREEN_SUN4I) += sun4i-ts.o
+413
drivers/input/touchscreen/sis_i2c.c
··· 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 + 102 + struct 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 + 112 + static 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 + 180 + static 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 + 231 + static 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 + 284 + static 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 + 295 + static 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 + 307 + static 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 387 + static const struct of_device_id sis_ts_dt_ids[] = { 388 + { .compatible = "sis,9200-ts" }, 389 + { /* sentinel */ } 390 + }; 391 + MODULE_DEVICE_TABLE(of, sis_ts_dt_ids); 392 + #endif 393 + 394 + static const struct i2c_device_id sis_ts_id[] = { 395 + { SIS_I2C_NAME, 0 }, 396 + { "9200-ts", 0 }, 397 + { /* sentinel */ } 398 + }; 399 + MODULE_DEVICE_TABLE(i2c, sis_ts_id); 400 + 401 + static 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 + }; 409 + module_i2c_driver(sis_ts_driver); 410 + 411 + MODULE_DESCRIPTION("SiS 9200 Family Touchscreen Driver"); 412 + MODULE_LICENSE("GPL v2"); 413 + MODULE_AUTHOR("Mika Penttilä <mika.penttila@nextfour.com>");