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

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>

authored by

Noralf Trønnes and committed by
Dmitry Torokhov
92deea13 d6f0c3d3

+377
+35
Documentation/devicetree/bindings/input/touchscreen/focaltech-ft6236.txt
··· 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 + };
+1
Documentation/devicetree/bindings/vendor-prefixes.txt
··· 82 82 excito Excito 83 83 fcs Fairchild Semiconductor 84 84 firefly Firefly 85 + focaltech FocalTech Systems Co.,Ltd 85 86 fsl Freescale Semiconductor 86 87 GEFanuc GE Fanuc Intelligent Platforms Embedded Systems, Inc. 87 88 gef GE Fanuc Intelligent Platforms Embedded Systems, Inc.
+13
drivers/input/touchscreen/Kconfig
··· 295 295 To compile this driver as a module, choose M here: the 296 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 311 config TOUCHSCREEN_FUJITSU 299 312 tristate "Fujitsu serial touchscreen" 300 313 select SERIO
+1
drivers/input/touchscreen/Makefile
··· 35 35 obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o 36 36 obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o 37 37 obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o 38 + obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o 38 39 obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o 39 40 obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o 40 41 obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
+327
drivers/input/touchscreen/ft6236.c
··· 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");