linux/drivers/input/touchscreen/egalax_ts.c
<<
>>
Prefs
   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#include <linux/of_gpio.h>
  32
  33/*
  34 * Mouse Mode: some panel may configure the controller to mouse mode,
  35 * which can only report one point at a given time.
  36 * This driver will ignore events in this mode.
  37 */
  38#define REPORT_MODE_MOUSE               0x1
  39/*
  40 * Vendor Mode: this mode is used to transfer some vendor specific
  41 * messages.
  42 * This driver will ignore events in this mode.
  43 */
  44#define REPORT_MODE_VENDOR              0x3
  45/* Multiple Touch Mode */
  46#define REPORT_MODE_MTTOUCH             0x4
  47
  48#define MAX_SUPPORT_POINTS              5
  49
  50#define EVENT_VALID_OFFSET      7
  51#define EVENT_VALID_MASK        (0x1 << EVENT_VALID_OFFSET)
  52#define EVENT_ID_OFFSET         2
  53#define EVENT_ID_MASK           (0xf << EVENT_ID_OFFSET)
  54#define EVENT_IN_RANGE          (0x1 << 1)
  55#define EVENT_DOWN_UP           (0X1 << 0)
  56
  57#define MAX_I2C_DATA_LEN        10
  58
  59#define EGALAX_MAX_X    32760
  60#define EGALAX_MAX_Y    32760
  61#define EGALAX_MAX_TRIES 100
  62
  63struct egalax_ts {
  64        struct i2c_client               *client;
  65        struct input_dev                *input_dev;
  66};
  67
  68static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id)
  69{
  70        struct egalax_ts *ts = dev_id;
  71        struct input_dev *input_dev = ts->input_dev;
  72        struct i2c_client *client = ts->client;
  73        u8 buf[MAX_I2C_DATA_LEN];
  74        int id, ret, x, y, z;
  75        int tries = 0;
  76        bool down, valid;
  77        u8 state;
  78
  79        do {
  80                ret = i2c_master_recv(client, buf, MAX_I2C_DATA_LEN);
  81        } while (ret == -EAGAIN && tries++ < EGALAX_MAX_TRIES);
  82
  83        if (ret < 0)
  84                return IRQ_HANDLED;
  85
  86        if (buf[0] != REPORT_MODE_MTTOUCH) {
  87                /* ignore mouse events and vendor events */
  88                return IRQ_HANDLED;
  89        }
  90
  91        state = buf[1];
  92        x = (buf[3] << 8) | buf[2];
  93        y = (buf[5] << 8) | buf[4];
  94        z = (buf[7] << 8) | buf[6];
  95
  96        valid = state & EVENT_VALID_MASK;
  97        id = (state & EVENT_ID_MASK) >> EVENT_ID_OFFSET;
  98        down = state & EVENT_DOWN_UP;
  99
 100        if (!valid || id > MAX_SUPPORT_POINTS) {
 101                dev_dbg(&client->dev, "point invalid\n");
 102                return IRQ_HANDLED;
 103        }
 104
 105        input_mt_slot(input_dev, id);
 106        input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, down);
 107
 108        dev_dbg(&client->dev, "%s id:%d x:%d y:%d z:%d",
 109                down ? "down" : "up", id, x, y, z);
 110
 111        if (down) {
 112                input_report_abs(input_dev, ABS_MT_POSITION_X, x);
 113                input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
 114                input_report_abs(input_dev, ABS_MT_PRESSURE, z);
 115        }
 116
 117        input_mt_report_pointer_emulation(input_dev, true);
 118        input_sync(input_dev);
 119
 120        return IRQ_HANDLED;
 121}
 122
 123/* wake up controller by an falling edge of interrupt gpio.  */
 124static int egalax_wake_up_device(struct i2c_client *client)
 125{
 126        struct device_node *np = client->dev.of_node;
 127        int gpio;
 128        int ret;
 129
 130        if (!np)
 131                return -ENODEV;
 132
 133        gpio = of_get_named_gpio(np, "wakeup-gpios", 0);
 134        if (!gpio_is_valid(gpio))
 135                return -ENODEV;
 136
 137        ret = gpio_request(gpio, "egalax_irq");
 138        if (ret < 0) {
 139                dev_err(&client->dev,
 140                        "request gpio failed, cannot wake up controller: %d\n",
 141                        ret);
 142                return ret;
 143        }
 144
 145        /* wake up controller via an falling edge on IRQ gpio. */
 146        gpio_direction_output(gpio, 0);
 147        gpio_set_value(gpio, 1);
 148
 149        /* controller should be waken up, return irq.  */
 150        gpio_direction_input(gpio);
 151        gpio_free(gpio);
 152
 153        return 0;
 154}
 155
 156static int egalax_firmware_version(struct i2c_client *client)
 157{
 158        static const u8 cmd[MAX_I2C_DATA_LEN] = { 0x03, 0x03, 0xa, 0x01, 0x41 };
 159        int ret;
 160
 161        ret = i2c_master_send(client, cmd, MAX_I2C_DATA_LEN);
 162        if (ret < 0)
 163                return ret;
 164
 165        return 0;
 166}
 167
 168static int egalax_ts_probe(struct i2c_client *client,
 169                                       const struct i2c_device_id *id)
 170{
 171        struct egalax_ts *ts;
 172        struct input_dev *input_dev;
 173        int ret;
 174        int error;
 175
 176        ts = kzalloc(sizeof(struct egalax_ts), GFP_KERNEL);
 177        if (!ts) {
 178                dev_err(&client->dev, "Failed to allocate memory\n");
 179                return -ENOMEM;
 180        }
 181
 182        input_dev = input_allocate_device();
 183        if (!input_dev) {
 184                dev_err(&client->dev, "Failed to allocate memory\n");
 185                error = -ENOMEM;
 186                goto err_free_ts;
 187        }
 188
 189        ts->client = client;
 190        ts->input_dev = input_dev;
 191
 192        /* controller may be in sleep, wake it up. */
 193        error = egalax_wake_up_device(client);
 194        if (error) {
 195                dev_err(&client->dev, "Failed to wake up the controller\n");
 196                goto err_free_dev;
 197        }
 198
 199        ret = egalax_firmware_version(client);
 200        if (ret < 0) {
 201                dev_err(&client->dev, "Failed to read firmware version\n");
 202                error = -EIO;
 203                goto err_free_dev;
 204        }
 205
 206        input_dev->name = "EETI eGalax Touch Screen";
 207        input_dev->id.bustype = BUS_I2C;
 208        input_dev->dev.parent = &client->dev;
 209
 210        __set_bit(EV_ABS, input_dev->evbit);
 211        __set_bit(EV_KEY, input_dev->evbit);
 212        __set_bit(BTN_TOUCH, input_dev->keybit);
 213
 214        input_set_abs_params(input_dev, ABS_X, 0, EGALAX_MAX_X, 0, 0);
 215        input_set_abs_params(input_dev, ABS_Y, 0, EGALAX_MAX_Y, 0, 0);
 216        input_set_abs_params(input_dev,
 217                             ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0);
 218        input_set_abs_params(input_dev,
 219                             ABS_MT_POSITION_X, 0, EGALAX_MAX_Y, 0, 0);
 220        input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS, 0);
 221
 222        input_set_drvdata(input_dev, ts);
 223
 224        error = request_threaded_irq(client->irq, NULL, egalax_ts_interrupt,
 225                                     IRQF_TRIGGER_LOW | IRQF_ONESHOT,
 226                                     "egalax_ts", ts);
 227        if (error < 0) {
 228                dev_err(&client->dev, "Failed to register interrupt\n");
 229                goto err_free_dev;
 230        }
 231
 232        error = input_register_device(ts->input_dev);
 233        if (error)
 234                goto err_free_irq;
 235
 236        i2c_set_clientdata(client, ts);
 237        return 0;
 238
 239err_free_irq:
 240        free_irq(client->irq, ts);
 241err_free_dev:
 242        input_free_device(input_dev);
 243err_free_ts:
 244        kfree(ts);
 245
 246        return error;
 247}
 248
 249static int egalax_ts_remove(struct i2c_client *client)
 250{
 251        struct egalax_ts *ts = i2c_get_clientdata(client);
 252
 253        free_irq(client->irq, ts);
 254
 255        input_unregister_device(ts->input_dev);
 256        kfree(ts);
 257
 258        return 0;
 259}
 260
 261static const struct i2c_device_id egalax_ts_id[] = {
 262        { "egalax_ts", 0 },
 263        { }
 264};
 265MODULE_DEVICE_TABLE(i2c, egalax_ts_id);
 266
 267#ifdef CONFIG_PM_SLEEP
 268static int egalax_ts_suspend(struct device *dev)
 269{
 270        static const u8 suspend_cmd[MAX_I2C_DATA_LEN] = {
 271                0x3, 0x6, 0xa, 0x3, 0x36, 0x3f, 0x2, 0, 0, 0
 272        };
 273        struct i2c_client *client = to_i2c_client(dev);
 274        int ret;
 275
 276        ret = i2c_master_send(client, suspend_cmd, MAX_I2C_DATA_LEN);
 277        return ret > 0 ? 0 : ret;
 278}
 279
 280static int egalax_ts_resume(struct device *dev)
 281{
 282        struct i2c_client *client = to_i2c_client(dev);
 283
 284        return egalax_wake_up_device(client);
 285}
 286#endif
 287
 288static SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume);
 289
 290static struct of_device_id egalax_ts_dt_ids[] = {
 291        { .compatible = "eeti,egalax_ts" },
 292        { /* sentinel */ }
 293};
 294
 295static struct i2c_driver egalax_ts_driver = {
 296        .driver = {
 297                .name   = "egalax_ts",
 298                .owner  = THIS_MODULE,
 299                .pm     = &egalax_ts_pm_ops,
 300                .of_match_table = of_match_ptr(egalax_ts_dt_ids),
 301        },
 302        .id_table       = egalax_ts_id,
 303        .probe          = egalax_ts_probe,
 304        .remove         = egalax_ts_remove,
 305};
 306
 307module_i2c_driver(egalax_ts_driver);
 308
 309MODULE_AUTHOR("Freescale Semiconductor, Inc.");
 310MODULE_DESCRIPTION("Touchscreen driver for EETI eGalax touch controller");
 311MODULE_LICENSE("GPL");
 312
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.