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

Input: add EETI eGalax I2C capacitive multi touch driver

This patch adds the EETI eGalax serial multi touch controller driver.

EETI eGalax serial touch screen controller is a I2C based multiple
capacitive touch screen controller, it can support 5 touch events maximum.

Signed-off-by: Zhang Jiejing <jiejing.zhang@freescale.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

authored by

Zhang Jiejing and committed by
Dmitry Torokhov
59bae1db 83551c01

+314
+10
drivers/input/touchscreen/Kconfig
··· 177 177 To compile this driver as a module, choose M here: the 178 178 module will be called eeti_ts. 179 179 180 + config TOUCHSCREEN_EGALAX 181 + tristate "EETI eGalax multi-touch panel support" 182 + depends on I2C 183 + help 184 + Say Y here to enable support for I2C connected EETI 185 + eGalax multi-touch panels. 186 + 187 + To compile this driver as a module, choose M here: the 188 + module will be called egalax_ts. 189 + 180 190 config TOUCHSCREEN_FUJITSU 181 191 tristate "Fujitsu serial touchscreen" 182 192 select SERIO
+1
drivers/input/touchscreen/Makefile
··· 23 23 obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o 24 24 obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o 25 25 obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o 26 + obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o 26 27 obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o 27 28 obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o 28 29 obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o
+303
drivers/input/touchscreen/egalax_ts.c
··· 1 + /* 2 + * Driver for EETI eGalax Multiple Touch Controller 3 + * 4 + * Copyright (C) 2011 Freescale Semiconductor, Inc. 5 + * 6 + * based on max11801_ts.c 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License version 2 as 10 + * published by the Free Software Foundation. 11 + */ 12 + 13 + /* EETI eGalax serial touch screen controller is a I2C based multiple 14 + * touch screen controller, it supports 5 point multiple touch. */ 15 + 16 + /* TODO: 17 + - auto idle mode support 18 + */ 19 + 20 + #include <linux/module.h> 21 + #include <linux/init.h> 22 + #include <linux/i2c.h> 23 + #include <linux/interrupt.h> 24 + #include <linux/input.h> 25 + #include <linux/irq.h> 26 + #include <linux/gpio.h> 27 + #include <linux/delay.h> 28 + #include <linux/slab.h> 29 + #include <linux/bitops.h> 30 + #include <linux/input/mt.h> 31 + 32 + /* 33 + * Mouse Mode: some panel may configure the controller to mouse mode, 34 + * which can only report one point at a given time. 35 + * This driver will ignore events in this mode. 36 + */ 37 + #define REPORT_MODE_MOUSE 0x1 38 + /* 39 + * Vendor Mode: this mode is used to transfer some vendor specific 40 + * messages. 41 + * This driver will ignore events in this mode. 42 + */ 43 + #define REPORT_MODE_VENDOR 0x3 44 + /* Multiple Touch Mode */ 45 + #define REPORT_MODE_MTTOUCH 0x4 46 + 47 + #define MAX_SUPPORT_POINTS 5 48 + 49 + #define EVENT_VALID_OFFSET 7 50 + #define EVENT_VALID_MASK (0x1 << EVENT_VALID_OFFSET) 51 + #define EVENT_ID_OFFSET 2 52 + #define EVENT_ID_MASK (0xf << EVENT_ID_OFFSET) 53 + #define EVENT_IN_RANGE (0x1 << 1) 54 + #define EVENT_DOWN_UP (0X1 << 0) 55 + 56 + #define MAX_I2C_DATA_LEN 10 57 + 58 + #define EGALAX_MAX_X 32760 59 + #define EGALAX_MAX_Y 32760 60 + #define EGALAX_MAX_TRIES 100 61 + 62 + struct egalax_ts { 63 + struct i2c_client *client; 64 + struct input_dev *input_dev; 65 + }; 66 + 67 + static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id) 68 + { 69 + struct egalax_ts *ts = dev_id; 70 + struct input_dev *input_dev = ts->input_dev; 71 + struct i2c_client *client = ts->client; 72 + u8 buf[MAX_I2C_DATA_LEN]; 73 + int id, ret, x, y, z; 74 + int tries = 0; 75 + bool down, valid; 76 + u8 state; 77 + 78 + do { 79 + ret = i2c_master_recv(client, buf, MAX_I2C_DATA_LEN); 80 + } while (ret == -EAGAIN && tries++ < EGALAX_MAX_TRIES); 81 + 82 + if (ret < 0) 83 + return IRQ_HANDLED; 84 + 85 + if (buf[0] != REPORT_MODE_MTTOUCH) { 86 + /* ignore mouse events and vendor events */ 87 + return IRQ_HANDLED; 88 + } 89 + 90 + state = buf[1]; 91 + x = (buf[3] << 8) | buf[2]; 92 + y = (buf[5] << 8) | buf[4]; 93 + z = (buf[7] << 8) | buf[6]; 94 + 95 + valid = state & EVENT_VALID_MASK; 96 + id = (state & EVENT_ID_MASK) >> EVENT_ID_OFFSET; 97 + down = state & EVENT_DOWN_UP; 98 + 99 + if (!valid || id > MAX_SUPPORT_POINTS) { 100 + dev_dbg(&client->dev, "point invalid\n"); 101 + return IRQ_HANDLED; 102 + } 103 + 104 + input_mt_slot(input_dev, id); 105 + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, down); 106 + 107 + dev_dbg(&client->dev, "%s id:%d x:%d y:%d z:%d", 108 + down ? "down" : "up", id, x, y, z); 109 + 110 + if (down) { 111 + input_report_abs(input_dev, ABS_MT_POSITION_X, x); 112 + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); 113 + input_report_abs(input_dev, ABS_MT_PRESSURE, z); 114 + } 115 + 116 + input_mt_report_pointer_emulation(input_dev, true); 117 + input_sync(input_dev); 118 + 119 + return IRQ_HANDLED; 120 + } 121 + 122 + /* wake up controller by an falling edge of interrupt gpio. */ 123 + static int egalax_wake_up_device(struct i2c_client *client) 124 + { 125 + int gpio = irq_to_gpio(client->irq); 126 + int ret; 127 + 128 + ret = gpio_request(gpio, "egalax_irq"); 129 + if (ret < 0) { 130 + dev_err(&client->dev, 131 + "request gpio failed, cannot wake up controller: %d\n", 132 + ret); 133 + return ret; 134 + } 135 + 136 + /* wake up controller via an falling edge on IRQ gpio. */ 137 + gpio_direction_output(gpio, 0); 138 + gpio_set_value(gpio, 1); 139 + 140 + /* controller should be waken up, return irq. */ 141 + gpio_direction_input(gpio); 142 + gpio_free(gpio); 143 + 144 + return 0; 145 + } 146 + 147 + static int __devinit egalax_firmware_version(struct i2c_client *client) 148 + { 149 + static const u8 cmd[MAX_I2C_DATA_LEN] = { 0x03, 0x03, 0xa, 0x01, 0x41 }; 150 + int ret; 151 + 152 + ret = i2c_master_send(client, cmd, MAX_I2C_DATA_LEN); 153 + if (ret < 0) 154 + return ret; 155 + 156 + return 0; 157 + } 158 + 159 + static int __devinit egalax_ts_probe(struct i2c_client *client, 160 + const struct i2c_device_id *id) 161 + { 162 + struct egalax_ts *ts; 163 + struct input_dev *input_dev; 164 + int ret; 165 + int error; 166 + 167 + ts = kzalloc(sizeof(struct egalax_ts), GFP_KERNEL); 168 + if (!ts) { 169 + dev_err(&client->dev, "Failed to allocate memory\n"); 170 + return -ENOMEM; 171 + } 172 + 173 + input_dev = input_allocate_device(); 174 + if (!input_dev) { 175 + dev_err(&client->dev, "Failed to allocate memory\n"); 176 + error = -ENOMEM; 177 + goto err_free_ts; 178 + } 179 + 180 + ts->client = client; 181 + ts->input_dev = input_dev; 182 + 183 + /* controller may be in sleep, wake it up. */ 184 + egalax_wake_up_device(client); 185 + 186 + ret = egalax_firmware_version(client); 187 + if (ret < 0) { 188 + dev_err(&client->dev, "Failed to read firmware version\n"); 189 + error = -EIO; 190 + goto err_free_dev; 191 + } 192 + 193 + input_dev->name = "EETI eGalax Touch Screen"; 194 + input_dev->id.bustype = BUS_I2C; 195 + input_dev->dev.parent = &client->dev; 196 + 197 + __set_bit(EV_ABS, input_dev->evbit); 198 + __set_bit(EV_KEY, input_dev->evbit); 199 + __set_bit(BTN_TOUCH, input_dev->keybit); 200 + 201 + input_set_abs_params(input_dev, ABS_X, 0, EGALAX_MAX_X, 0, 0); 202 + input_set_abs_params(input_dev, ABS_Y, 0, EGALAX_MAX_Y, 0, 0); 203 + input_set_abs_params(input_dev, 204 + ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0); 205 + input_set_abs_params(input_dev, 206 + ABS_MT_POSITION_X, 0, EGALAX_MAX_Y, 0, 0); 207 + input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS); 208 + 209 + input_set_drvdata(input_dev, ts); 210 + 211 + error = request_threaded_irq(client->irq, NULL, egalax_ts_interrupt, 212 + IRQF_TRIGGER_LOW | IRQF_ONESHOT, 213 + "egalax_ts", ts); 214 + if (error < 0) { 215 + dev_err(&client->dev, "Failed to register interrupt\n"); 216 + goto err_free_dev; 217 + } 218 + 219 + error = input_register_device(ts->input_dev); 220 + if (error) 221 + goto err_free_irq; 222 + 223 + i2c_set_clientdata(client, ts); 224 + return 0; 225 + 226 + err_free_irq: 227 + free_irq(client->irq, ts); 228 + err_free_dev: 229 + input_free_device(input_dev); 230 + err_free_ts: 231 + kfree(ts); 232 + 233 + return error; 234 + } 235 + 236 + static __devexit int egalax_ts_remove(struct i2c_client *client) 237 + { 238 + struct egalax_ts *ts = i2c_get_clientdata(client); 239 + 240 + free_irq(client->irq, ts); 241 + 242 + input_unregister_device(ts->input_dev); 243 + kfree(ts); 244 + 245 + return 0; 246 + } 247 + 248 + static const struct i2c_device_id egalax_ts_id[] = { 249 + { "egalax_ts", 0 }, 250 + { } 251 + }; 252 + MODULE_DEVICE_TABLE(i2c, egalax_ts_id); 253 + 254 + #ifdef CONFIG_PM_SLEEP 255 + static int egalax_ts_suspend(struct device *dev) 256 + { 257 + static const u8 suspend_cmd[MAX_I2C_DATA_LEN] = { 258 + 0x3, 0x6, 0xa, 0x3, 0x36, 0x3f, 0x2, 0, 0, 0 259 + }; 260 + struct i2c_client *client = to_i2c_client(dev); 261 + int ret; 262 + 263 + ret = i2c_master_send(client, suspend_cmd, MAX_I2C_DATA_LEN); 264 + return ret > 0 ? 0 : ret; 265 + } 266 + 267 + static int egalax_ts_resume(struct device *dev) 268 + { 269 + struct i2c_client *client = to_i2c_client(dev); 270 + 271 + return egalax_wake_up_device(client); 272 + } 273 + #endif 274 + 275 + static SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume); 276 + 277 + static struct i2c_driver egalax_ts_driver = { 278 + .driver = { 279 + .name = "egalax_ts", 280 + .owner = THIS_MODULE, 281 + .pm = &egalax_ts_pm_ops, 282 + }, 283 + .id_table = egalax_ts_id, 284 + .probe = egalax_ts_probe, 285 + .remove = __devexit_p(egalax_ts_remove), 286 + }; 287 + 288 + static int __init egalax_ts_init(void) 289 + { 290 + return i2c_add_driver(&egalax_ts_driver); 291 + } 292 + 293 + static void __exit egalax_ts_exit(void) 294 + { 295 + i2c_del_driver(&egalax_ts_driver); 296 + } 297 + 298 + module_init(egalax_ts_init); 299 + module_exit(egalax_ts_exit); 300 + 301 + MODULE_AUTHOR("Freescale Semiconductor, Inc."); 302 + MODULE_DESCRIPTION("Touchscreen driver for EETI eGalax touch controller"); 303 + MODULE_LICENSE("GPL");