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

Input: add support for Semtech SX8654 I2C touchscreen controller

Signed-off-by: Sébastien Szymanski <sebastien.szymanski@armadeus.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

authored by

Sébastien Szymanski and committed by
Dmitry Torokhov
902cb3af 771d8f1b

+314
+16
Documentation/devicetree/bindings/input/touchscreen/sx8654.txt
··· 1 + * Semtech SX8654 I2C Touchscreen Controller 2 + 3 + Required properties: 4 + - compatible: must be "semtech,sx8654" 5 + - reg: i2c slave address 6 + - interrupt-parent: the phandle for the interrupt controller 7 + - interrupts: touch controller interrupt 8 + 9 + Example: 10 + 11 + sx8654@48 { 12 + compatible = "semtech,sx8654"; 13 + reg = <0x48>; 14 + interrupt-parent = <&gpio6>; 15 + interrupts = <3 IRQ_TYPE_EDGE_FALLING>; 16 + };
+11
drivers/input/touchscreen/Kconfig
··· 962 962 To compile this driver as a module, choose M here: the 963 963 module will be called sur40. 964 964 965 + config TOUCHSCREEN_SX8654 966 + tristate "Semtech SX8654 touchscreen" 967 + depends on I2C 968 + help 969 + Say Y here if you have a Semtech SX8654 touchscreen controller. 970 + 971 + If unsure, say N 972 + 973 + To compile this driver as a module, choose M here: the 974 + module will be called sx8654. 975 + 965 976 config TOUCHSCREEN_TPS6507X 966 977 tristate "TPS6507x based touchscreens" 967 978 depends on I2C
+1
drivers/input/touchscreen/Makefile
··· 79 79 obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o 80 80 obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o 81 81 obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o 82 + obj-$(CONFIG_TOUCHSCREEN_SX8654) += sx8654.o 82 83 obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o 83 84 obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o
+286
drivers/input/touchscreen/sx8654.c
··· 1 + /* 2 + * Driver for Semtech SX8654 I2C touchscreen controller. 3 + * 4 + * Copyright (c) 2015 Armadeus Systems 5 + * Sébastien Szymanski <sebastien.szymanski@armadeus.com> 6 + * 7 + * Using code from: 8 + * - sx865x.c 9 + * Copyright (c) 2013 U-MoBo Srl 10 + * Pierluigi Passaro <p.passaro@u-mobo.com> 11 + * - sx8650.c 12 + * Copyright (c) 2009 Wayne Roberts 13 + * - tsc2007.c 14 + * Copyright (c) 2008 Kwangwoo Lee 15 + * - ads7846.c 16 + * Copyright (c) 2005 David Brownell 17 + * Copyright (c) 2006 Nokia Corporation 18 + * - corgi_ts.c 19 + * Copyright (C) 2004-2005 Richard Purdie 20 + * - omap_ts.[hc], ads7846.h, ts_osk.c 21 + * Copyright (C) 2002 MontaVista Software 22 + * Copyright (C) 2004 Texas Instruments 23 + * Copyright (C) 2005 Dirk Behme 24 + * 25 + * This program is free software; you can redistribute it and/or modify 26 + * it under the terms of the GNU General Public License version 2 as 27 + * published by the Free Software Foundation. 28 + */ 29 + 30 + #include <linux/input.h> 31 + #include <linux/module.h> 32 + #include <linux/of.h> 33 + #include <linux/i2c.h> 34 + #include <linux/interrupt.h> 35 + #include <linux/irq.h> 36 + 37 + /* register addresses */ 38 + #define I2C_REG_TOUCH0 0x00 39 + #define I2C_REG_TOUCH1 0x01 40 + #define I2C_REG_CHANMASK 0x04 41 + #define I2C_REG_IRQMASK 0x22 42 + #define I2C_REG_IRQSRC 0x23 43 + #define I2C_REG_SOFTRESET 0x3f 44 + 45 + /* commands */ 46 + #define CMD_READ_REGISTER 0x40 47 + #define CMD_MANUAL 0xc0 48 + #define CMD_PENTRG 0xe0 49 + 50 + /* value for I2C_REG_SOFTRESET */ 51 + #define SOFTRESET_VALUE 0xde 52 + 53 + /* bits for I2C_REG_IRQSRC */ 54 + #define IRQ_PENTOUCH_TOUCHCONVDONE 0x08 55 + #define IRQ_PENRELEASE 0x04 56 + 57 + /* bits for RegTouch1 */ 58 + #define CONDIRQ 0x20 59 + #define FILT_7SA 0x03 60 + 61 + /* bits for I2C_REG_CHANMASK */ 62 + #define CONV_X 0x80 63 + #define CONV_Y 0x40 64 + 65 + /* coordinates rate: higher nibble of CTRL0 register */ 66 + #define RATE_MANUAL 0x00 67 + #define RATE_5000CPS 0xf0 68 + 69 + /* power delay: lower nibble of CTRL0 register */ 70 + #define POWDLY_1_1MS 0x0b 71 + 72 + #define MAX_12BIT ((1 << 12) - 1) 73 + 74 + struct sx8654 { 75 + struct input_dev *input; 76 + struct i2c_client *client; 77 + }; 78 + 79 + static irqreturn_t sx8654_irq(int irq, void *handle) 80 + { 81 + struct sx8654 *sx8654 = handle; 82 + u8 irqsrc; 83 + u8 data[4]; 84 + unsigned int x, y; 85 + int retval; 86 + 87 + irqsrc = i2c_smbus_read_byte_data(sx8654->client, 88 + CMD_READ_REGISTER | I2C_REG_IRQSRC); 89 + dev_dbg(&sx8654->client->dev, "irqsrc = 0x%x", irqsrc); 90 + 91 + if (irqsrc < 0) 92 + goto out; 93 + 94 + if (irqsrc & IRQ_PENRELEASE) { 95 + dev_dbg(&sx8654->client->dev, "pen release interrupt"); 96 + 97 + input_report_key(sx8654->input, BTN_TOUCH, 0); 98 + input_sync(sx8654->input); 99 + } 100 + 101 + if (irqsrc & IRQ_PENTOUCH_TOUCHCONVDONE) { 102 + dev_dbg(&sx8654->client->dev, "pen touch interrupt"); 103 + 104 + retval = i2c_master_recv(sx8654->client, data, sizeof(data)); 105 + if (retval != sizeof(data)) 106 + goto out; 107 + 108 + /* invalid data */ 109 + if (unlikely(data[0] & 0x80 || data[2] & 0x80)) 110 + goto out; 111 + 112 + x = ((data[0] & 0xf) << 8) | (data[1]); 113 + y = ((data[2] & 0xf) << 8) | (data[3]); 114 + 115 + input_report_abs(sx8654->input, ABS_X, x); 116 + input_report_abs(sx8654->input, ABS_Y, y); 117 + input_report_key(sx8654->input, BTN_TOUCH, 1); 118 + input_sync(sx8654->input); 119 + 120 + dev_dbg(&sx8654->client->dev, "point(%4d,%4d)\n", x, y); 121 + } 122 + 123 + out: 124 + return IRQ_HANDLED; 125 + } 126 + 127 + static int sx8654_open(struct input_dev *dev) 128 + { 129 + struct sx8654 *sx8654 = input_get_drvdata(dev); 130 + struct i2c_client *client = sx8654->client; 131 + int error; 132 + 133 + /* enable pen trigger mode */ 134 + error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 135 + RATE_5000CPS | POWDLY_1_1MS); 136 + if (error) { 137 + dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed"); 138 + return error; 139 + } 140 + 141 + error = i2c_smbus_write_byte(client, CMD_PENTRG); 142 + if (error) { 143 + dev_err(&client->dev, "writing command CMD_PENTRG failed"); 144 + return error; 145 + } 146 + 147 + enable_irq(client->irq); 148 + 149 + return 0; 150 + } 151 + 152 + static void sx8654_close(struct input_dev *dev) 153 + { 154 + struct sx8654 *sx8654 = input_get_drvdata(dev); 155 + struct i2c_client *client = sx8654->client; 156 + int error; 157 + 158 + disable_irq(client->irq); 159 + 160 + /* enable manual mode mode */ 161 + error = i2c_smbus_write_byte(client, CMD_MANUAL); 162 + if (error) { 163 + dev_err(&client->dev, "writing command CMD_MANUAL failed"); 164 + return; 165 + } 166 + 167 + error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0); 168 + if (error) { 169 + dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed"); 170 + return; 171 + } 172 + } 173 + 174 + static int sx8654_probe(struct i2c_client *client, 175 + const struct i2c_device_id *id) 176 + { 177 + struct sx8654 *sx8654; 178 + struct input_dev *input; 179 + int error; 180 + 181 + if (!i2c_check_functionality(client->adapter, 182 + I2C_FUNC_SMBUS_READ_WORD_DATA)) 183 + return -ENXIO; 184 + 185 + sx8654 = devm_kzalloc(&client->dev, sizeof(*sx8654), GFP_KERNEL); 186 + if (!sx8654) 187 + return -ENOMEM; 188 + 189 + input = devm_input_allocate_device(&client->dev); 190 + if (!sx8654) 191 + return -ENOMEM; 192 + 193 + input->name = "SX8654 I2C Touchscreen"; 194 + input->id.bustype = BUS_I2C; 195 + input->dev.parent = &client->dev; 196 + input->open = sx8654_open; 197 + input->close = sx8654_close; 198 + 199 + __set_bit(INPUT_PROP_DIRECT, input->propbit); 200 + input_set_capability(input, EV_KEY, BTN_TOUCH); 201 + input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0); 202 + input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0); 203 + 204 + sx8654->client = client; 205 + sx8654->input = input; 206 + 207 + input_set_drvdata(sx8654->input, sx8654); 208 + 209 + error = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET, 210 + SOFTRESET_VALUE); 211 + if (error) { 212 + dev_err(&client->dev, "writing softreset value failed"); 213 + return error; 214 + } 215 + 216 + error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK, 217 + CONV_X | CONV_Y); 218 + if (error) { 219 + dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed"); 220 + return error; 221 + } 222 + 223 + error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK, 224 + IRQ_PENTOUCH_TOUCHCONVDONE | 225 + IRQ_PENRELEASE); 226 + if (error) { 227 + dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed"); 228 + return error; 229 + } 230 + 231 + error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1, 232 + CONDIRQ | FILT_7SA); 233 + if (error) { 234 + dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed"); 235 + return error; 236 + } 237 + 238 + error = devm_request_threaded_irq(&client->dev, client->irq, 239 + NULL, sx8654_irq, 240 + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 241 + client->name, sx8654); 242 + if (error) { 243 + dev_err(&client->dev, 244 + "Failed to enable IRQ %d, error: %d\n", 245 + client->irq, error); 246 + return error; 247 + } 248 + 249 + /* Disable the IRQ, we'll enable it in sx8654_open() */ 250 + disable_irq(client->irq); 251 + 252 + error = input_register_device(sx8654->input); 253 + if (error) 254 + return error; 255 + 256 + i2c_set_clientdata(client, sx8654); 257 + return 0; 258 + } 259 + 260 + #ifdef CONFIG_OF 261 + static const struct of_device_id sx8654_of_match[] = { 262 + { .compatible = "semtech,sx8654", }, 263 + { }, 264 + }; 265 + MODULE_DEVICE_TABLE(of, sx8654_of_match); 266 + #endif 267 + 268 + static const struct i2c_device_id sx8654_id_table[] = { 269 + { "semtech_sx8654", 0 }, 270 + { }, 271 + }; 272 + MODULE_DEVICE_TABLE(i2c, sx8654_id_table); 273 + 274 + static struct i2c_driver sx8654_driver = { 275 + .driver = { 276 + .name = "sx8654", 277 + .of_match_table = of_match_ptr(sx8654_of_match), 278 + }, 279 + .id_table = sx8654_id_table, 280 + .probe = sx8654_probe, 281 + }; 282 + module_i2c_driver(sx8654_driver); 283 + 284 + MODULE_AUTHOR("Sébastien Szymanski <sebastien.szymanski@armadeus.com>"); 285 + MODULE_DESCRIPTION("Semtech SX8654 I2C Touchscreen Driver"); 286 + MODULE_LICENSE("GPL");