linux/drivers/input/touchscreen/ili210x.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2#include <linux/module.h>
   3#include <linux/i2c.h>
   4#include <linux/interrupt.h>
   5#include <linux/slab.h>
   6#include <linux/input.h>
   7#include <linux/input/mt.h>
   8#include <linux/input/touchscreen.h>
   9#include <linux/delay.h>
  10#include <linux/workqueue.h>
  11#include <linux/gpio/consumer.h>
  12#include <linux/of_device.h>
  13#include <asm/unaligned.h>
  14
  15#define ILI210X_TOUCHES         2
  16#define ILI251X_TOUCHES         10
  17#define DEFAULT_POLL_PERIOD     20
  18
  19/* Touchscreen commands */
  20#define REG_TOUCHDATA           0x10
  21#define REG_PANEL_INFO          0x20
  22#define REG_FIRMWARE_VERSION    0x40
  23#define REG_CALIBRATE           0xcc
  24
  25struct firmware_version {
  26        u8 id;
  27        u8 major;
  28        u8 minor;
  29} __packed;
  30
  31enum ili2xxx_model {
  32        MODEL_ILI210X,
  33        MODEL_ILI251X,
  34};
  35
  36struct ili210x {
  37        struct i2c_client *client;
  38        struct input_dev *input;
  39        unsigned int poll_period;
  40        struct delayed_work dwork;
  41        struct gpio_desc *reset_gpio;
  42        struct touchscreen_properties prop;
  43        enum ili2xxx_model model;
  44        unsigned int max_touches;
  45};
  46
  47static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
  48                            size_t len)
  49{
  50        struct ili210x *priv = i2c_get_clientdata(client);
  51        struct i2c_msg msg[2] = {
  52                {
  53                        .addr   = client->addr,
  54                        .flags  = 0,
  55                        .len    = 1,
  56                        .buf    = &reg,
  57                },
  58                {
  59                        .addr   = client->addr,
  60                        .flags  = I2C_M_RD,
  61                        .len    = len,
  62                        .buf    = buf,
  63                }
  64        };
  65
  66        if (priv->model == MODEL_ILI251X) {
  67                if (i2c_transfer(client->adapter, msg, 1) != 1) {
  68                        dev_err(&client->dev, "i2c transfer failed\n");
  69                        return -EIO;
  70                }
  71
  72                usleep_range(5000, 5500);
  73
  74                if (i2c_transfer(client->adapter, msg + 1, 1) != 1) {
  75                        dev_err(&client->dev, "i2c transfer failed\n");
  76                        return -EIO;
  77                }
  78        } else {
  79                if (i2c_transfer(client->adapter, msg, 2) != 2) {
  80                        dev_err(&client->dev, "i2c transfer failed\n");
  81                        return -EIO;
  82                }
  83        }
  84
  85        return 0;
  86}
  87
  88static int ili210x_read(struct i2c_client *client, void *buf, size_t len)
  89{
  90        struct i2c_msg msg = {
  91                .addr   = client->addr,
  92                .flags  = I2C_M_RD,
  93                .len    = len,
  94                .buf    = buf,
  95        };
  96
  97        if (i2c_transfer(client->adapter, &msg, 1) != 1) {
  98                dev_err(&client->dev, "i2c transfer failed\n");
  99                return -EIO;
 100        }
 101
 102        return 0;
 103}
 104
 105static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata,
 106                                        unsigned int finger,
 107                                        unsigned int *x, unsigned int *y)
 108{
 109        if (finger >= ILI210X_TOUCHES)
 110                return false;
 111
 112        if (touchdata[0] & BIT(finger))
 113                return false;
 114
 115        *x = get_unaligned_be16(touchdata + 1 + (finger * 4) + 0);
 116        *y = get_unaligned_be16(touchdata + 1 + (finger * 4) + 2);
 117
 118        return true;
 119}
 120
 121static bool ili251x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata,
 122                                        unsigned int finger,
 123                                        unsigned int *x, unsigned int *y)
 124{
 125        if (finger >= ILI251X_TOUCHES)
 126                return false;
 127
 128        *x = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0);
 129        if (!(*x & BIT(15)))    /* Touch indication */
 130                return false;
 131
 132        *x &= 0x3fff;
 133        *y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2);
 134
 135        return true;
 136}
 137
 138static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata)
 139{
 140        struct input_dev *input = priv->input;
 141        int i;
 142        bool contact = false, touch = false;
 143        unsigned int x = 0, y = 0;
 144
 145        for (i = 0; i < priv->max_touches; i++) {
 146                if (priv->model == MODEL_ILI210X) {
 147                        touch = ili210x_touchdata_to_coords(priv, touchdata,
 148                                                            i, &x, &y);
 149                } else if (priv->model == MODEL_ILI251X) {
 150                        touch = ili251x_touchdata_to_coords(priv, touchdata,
 151                                                            i, &x, &y);
 152                        if (touch)
 153                                contact = true;
 154                }
 155
 156                input_mt_slot(input, i);
 157                input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
 158                if (!touch)
 159                        continue;
 160                touchscreen_report_pos(input, &priv->prop, x, y,
 161                                       true);
 162        }
 163
 164        input_mt_report_pointer_emulation(input, false);
 165        input_sync(input);
 166
 167        if (priv->model == MODEL_ILI210X)
 168                contact = touchdata[0] & 0xf3;
 169
 170        return contact;
 171}
 172
 173static void ili210x_work(struct work_struct *work)
 174{
 175        struct ili210x *priv = container_of(work, struct ili210x,
 176                                            dwork.work);
 177        struct i2c_client *client = priv->client;
 178        u8 touchdata[64] = { 0 };
 179        bool touch;
 180        int error = -EINVAL;
 181
 182        if (priv->model == MODEL_ILI210X) {
 183                error = ili210x_read_reg(client, REG_TOUCHDATA,
 184                                         touchdata, sizeof(touchdata));
 185        } else if (priv->model == MODEL_ILI251X) {
 186                error = ili210x_read_reg(client, REG_TOUCHDATA,
 187                                         touchdata, 31);
 188                if (!error && touchdata[0] == 2)
 189                        error = ili210x_read(client, &touchdata[31], 20);
 190        }
 191
 192        if (error) {
 193                dev_err(&client->dev,
 194                        "Unable to get touchdata, err = %d\n", error);
 195                return;
 196        }
 197
 198        touch = ili210x_report_events(priv, touchdata);
 199
 200        if (touch)
 201                schedule_delayed_work(&priv->dwork,
 202                                      msecs_to_jiffies(priv->poll_period));
 203}
 204
 205static irqreturn_t ili210x_irq(int irq, void *irq_data)
 206{
 207        struct ili210x *priv = irq_data;
 208
 209        schedule_delayed_work(&priv->dwork, 0);
 210
 211        return IRQ_HANDLED;
 212}
 213
 214static ssize_t ili210x_calibrate(struct device *dev,
 215                                 struct device_attribute *attr,
 216                                 const char *buf, size_t count)
 217{
 218        struct i2c_client *client = to_i2c_client(dev);
 219        struct ili210x *priv = i2c_get_clientdata(client);
 220        unsigned long calibrate;
 221        int rc;
 222        u8 cmd = REG_CALIBRATE;
 223
 224        if (kstrtoul(buf, 10, &calibrate))
 225                return -EINVAL;
 226
 227        if (calibrate > 1)
 228                return -EINVAL;
 229
 230        if (calibrate) {
 231                rc = i2c_master_send(priv->client, &cmd, sizeof(cmd));
 232                if (rc != sizeof(cmd))
 233                        return -EIO;
 234        }
 235
 236        return count;
 237}
 238static DEVICE_ATTR(calibrate, S_IWUSR, NULL, ili210x_calibrate);
 239
 240static struct attribute *ili210x_attributes[] = {
 241        &dev_attr_calibrate.attr,
 242        NULL,
 243};
 244
 245static const struct attribute_group ili210x_attr_group = {
 246        .attrs = ili210x_attributes,
 247};
 248
 249static void ili210x_power_down(void *data)
 250{
 251        struct gpio_desc *reset_gpio = data;
 252
 253        gpiod_set_value_cansleep(reset_gpio, 1);
 254}
 255
 256static void ili210x_cancel_work(void *data)
 257{
 258        struct ili210x *priv = data;
 259
 260        cancel_delayed_work_sync(&priv->dwork);
 261}
 262
 263static int ili210x_i2c_probe(struct i2c_client *client,
 264                                       const struct i2c_device_id *id)
 265{
 266        struct device *dev = &client->dev;
 267        struct ili210x *priv;
 268        struct gpio_desc *reset_gpio;
 269        struct input_dev *input;
 270        struct firmware_version firmware;
 271        enum ili2xxx_model model;
 272        int error;
 273
 274        model = (enum ili2xxx_model)id->driver_data;
 275
 276        dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
 277
 278        if (client->irq <= 0) {
 279                dev_err(dev, "No IRQ!\n");
 280                return -EINVAL;
 281        }
 282
 283        reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
 284        if (IS_ERR(reset_gpio))
 285                return PTR_ERR(reset_gpio);
 286
 287        if (reset_gpio) {
 288                error = devm_add_action_or_reset(dev, ili210x_power_down,
 289                                                 reset_gpio);
 290                if (error)
 291                        return error;
 292
 293                usleep_range(50, 100);
 294                gpiod_set_value_cansleep(reset_gpio, 0);
 295                msleep(100);
 296        }
 297
 298        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 299        if (!priv)
 300                return -ENOMEM;
 301
 302        input = devm_input_allocate_device(dev);
 303        if (!input)
 304                return -ENOMEM;
 305
 306        priv->client = client;
 307        priv->input = input;
 308        priv->poll_period = DEFAULT_POLL_PERIOD;
 309        INIT_DELAYED_WORK(&priv->dwork, ili210x_work);
 310        priv->reset_gpio = reset_gpio;
 311        priv->model = model;
 312        if (model == MODEL_ILI210X)
 313                priv->max_touches = ILI210X_TOUCHES;
 314        if (model == MODEL_ILI251X)
 315                priv->max_touches = ILI251X_TOUCHES;
 316
 317        i2c_set_clientdata(client, priv);
 318
 319        /* Get firmware version */
 320        error = ili210x_read_reg(client, REG_FIRMWARE_VERSION,
 321                                 &firmware, sizeof(firmware));
 322        if (error) {
 323                dev_err(dev, "Failed to get firmware version, err: %d\n",
 324                        error);
 325                return error;
 326        }
 327
 328        /* Setup input device */
 329        input->name = "ILI210x Touchscreen";
 330        input->id.bustype = BUS_I2C;
 331        input->dev.parent = dev;
 332
 333        /* Multi touch */
 334        input_set_abs_params(input, ABS_MT_POSITION_X, 0, 0xffff, 0, 0);
 335        input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0);
 336        touchscreen_parse_properties(input, true, &priv->prop);
 337        input_mt_init_slots(input, priv->max_touches, INPUT_MT_DIRECT);
 338
 339        error = devm_add_action(dev, ili210x_cancel_work, priv);
 340        if (error)
 341                return error;
 342
 343        error = devm_request_irq(dev, client->irq, ili210x_irq, 0,
 344                                 client->name, priv);
 345        if (error) {
 346                dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n",
 347                        error);
 348                return error;
 349        }
 350
 351        error = devm_device_add_group(dev, &ili210x_attr_group);
 352        if (error) {
 353                dev_err(dev, "Unable to create sysfs attributes, err: %d\n",
 354                        error);
 355                return error;
 356        }
 357
 358        error = input_register_device(priv->input);
 359        if (error) {
 360                dev_err(dev, "Cannot register input device, err: %d\n", error);
 361                return error;
 362        }
 363
 364        device_init_wakeup(dev, 1);
 365
 366        dev_dbg(dev,
 367                "ILI210x initialized (IRQ: %d), firmware version %d.%d.%d",
 368                client->irq, firmware.id, firmware.major, firmware.minor);
 369
 370        return 0;
 371}
 372
 373static int __maybe_unused ili210x_i2c_suspend(struct device *dev)
 374{
 375        struct i2c_client *client = to_i2c_client(dev);
 376
 377        if (device_may_wakeup(&client->dev))
 378                enable_irq_wake(client->irq);
 379
 380        return 0;
 381}
 382
 383static int __maybe_unused ili210x_i2c_resume(struct device *dev)
 384{
 385        struct i2c_client *client = to_i2c_client(dev);
 386
 387        if (device_may_wakeup(&client->dev))
 388                disable_irq_wake(client->irq);
 389
 390        return 0;
 391}
 392
 393static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm,
 394                         ili210x_i2c_suspend, ili210x_i2c_resume);
 395
 396static const struct i2c_device_id ili210x_i2c_id[] = {
 397        { "ili210x", MODEL_ILI210X },
 398        { "ili251x", MODEL_ILI251X },
 399        { }
 400};
 401MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id);
 402
 403static const struct of_device_id ili210x_dt_ids[] = {
 404        { .compatible = "ilitek,ili210x", .data = (void *)MODEL_ILI210X },
 405        { .compatible = "ilitek,ili251x", .data = (void *)MODEL_ILI251X },
 406        { },
 407};
 408MODULE_DEVICE_TABLE(of, ili210x_dt_ids);
 409
 410static struct i2c_driver ili210x_ts_driver = {
 411        .driver = {
 412                .name = "ili210x_i2c",
 413                .pm = &ili210x_i2c_pm,
 414                .of_match_table = ili210x_dt_ids,
 415        },
 416        .id_table = ili210x_i2c_id,
 417        .probe = ili210x_i2c_probe,
 418};
 419
 420module_i2c_driver(ili210x_ts_driver);
 421
 422MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>");
 423MODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver");
 424MODULE_LICENSE("GPL");
 425