linux/drivers/input/touchscreen/cy8ctmg110_ts.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Driver for cypress touch screen controller
   4 *
   5 * Copyright (c) 2009 Aava Mobile
   6 *
   7 * Some cleanups by Alan Cox <alan@linux.intel.com>
   8 */
   9
  10#include <linux/i2c.h>
  11#include <linux/input.h>
  12#include <linux/interrupt.h>
  13#include <linux/gpio/consumer.h>
  14#include <linux/kernel.h>
  15#include <linux/module.h>
  16#include <linux/slab.h>
  17#include <asm/byteorder.h>
  18
  19#define CY8CTMG110_DRIVER_NAME      "cy8ctmg110"
  20
  21/* Touch coordinates */
  22#define CY8CTMG110_X_MIN                0
  23#define CY8CTMG110_Y_MIN                0
  24#define CY8CTMG110_X_MAX                759
  25#define CY8CTMG110_Y_MAX                465
  26
  27
  28/* cy8ctmg110 register definitions */
  29#define CY8CTMG110_TOUCH_WAKEUP_TIME    0
  30#define CY8CTMG110_TOUCH_SLEEP_TIME     2
  31#define CY8CTMG110_TOUCH_X1             3
  32#define CY8CTMG110_TOUCH_Y1             5
  33#define CY8CTMG110_TOUCH_X2             7
  34#define CY8CTMG110_TOUCH_Y2             9
  35#define CY8CTMG110_FINGERS              11
  36#define CY8CTMG110_GESTURE              12
  37#define CY8CTMG110_REG_MAX              13
  38
  39
  40/*
  41 * The touch driver structure.
  42 */
  43struct cy8ctmg110 {
  44        struct input_dev *input;
  45        char phys[32];
  46        struct i2c_client *client;
  47        struct gpio_desc *reset_gpio;
  48};
  49
  50/*
  51 * cy8ctmg110_power is the routine that is called when touch hardware
  52 * is being powered off or on. When powering on this routine de-asserts
  53 * the RESET line, when powering off reset line is asserted.
  54 */
  55static void cy8ctmg110_power(struct cy8ctmg110 *ts, bool poweron)
  56{
  57        if (ts->reset_gpio)
  58                gpiod_set_value_cansleep(ts->reset_gpio, !poweron);
  59}
  60
  61static int cy8ctmg110_write_regs(struct cy8ctmg110 *tsc, unsigned char reg,
  62                unsigned char len, unsigned char *value)
  63{
  64        struct i2c_client *client = tsc->client;
  65        int ret;
  66        unsigned char i2c_data[6];
  67
  68        BUG_ON(len > 5);
  69
  70        i2c_data[0] = reg;
  71        memcpy(i2c_data + 1, value, len);
  72
  73        ret = i2c_master_send(client, i2c_data, len + 1);
  74        if (ret != len + 1) {
  75                dev_err(&client->dev, "i2c write data cmd failed\n");
  76                return ret < 0 ? ret : -EIO;
  77        }
  78
  79        return 0;
  80}
  81
  82static int cy8ctmg110_read_regs(struct cy8ctmg110 *tsc,
  83                unsigned char *data, unsigned char len, unsigned char cmd)
  84{
  85        struct i2c_client *client = tsc->client;
  86        int ret;
  87        struct i2c_msg msg[2] = {
  88                /* first write slave position to i2c devices */
  89                {
  90                        .addr = client->addr,
  91                        .len = 1,
  92                        .buf = &cmd
  93                },
  94                /* Second read data from position */
  95                {
  96                        .addr = client->addr,
  97                        .flags = I2C_M_RD,
  98                        .len = len,
  99                        .buf = data
 100                }
 101        };
 102
 103        ret = i2c_transfer(client->adapter, msg, 2);
 104        if (ret < 0)
 105                return ret;
 106
 107        return 0;
 108}
 109
 110static int cy8ctmg110_touch_pos(struct cy8ctmg110 *tsc)
 111{
 112        struct input_dev *input = tsc->input;
 113        unsigned char reg_p[CY8CTMG110_REG_MAX];
 114
 115        memset(reg_p, 0, CY8CTMG110_REG_MAX);
 116
 117        /* Reading coordinates */
 118        if (cy8ctmg110_read_regs(tsc, reg_p, 9, CY8CTMG110_TOUCH_X1) != 0)
 119                return -EIO;
 120
 121        /* Number of touch */
 122        if (reg_p[8] == 0) {
 123                input_report_key(input, BTN_TOUCH, 0);
 124        } else  {
 125                input_report_key(input, BTN_TOUCH, 1);
 126                input_report_abs(input, ABS_X,
 127                                 be16_to_cpup((__be16 *)(reg_p + 0)));
 128                input_report_abs(input, ABS_Y,
 129                                 be16_to_cpup((__be16 *)(reg_p + 2)));
 130        }
 131
 132        input_sync(input);
 133
 134        return 0;
 135}
 136
 137static int cy8ctmg110_set_sleepmode(struct cy8ctmg110 *ts, bool sleep)
 138{
 139        unsigned char reg_p[3];
 140
 141        if (sleep) {
 142                reg_p[0] = 0x00;
 143                reg_p[1] = 0xff;
 144                reg_p[2] = 5;
 145        } else {
 146                reg_p[0] = 0x10;
 147                reg_p[1] = 0xff;
 148                reg_p[2] = 0;
 149        }
 150
 151        return cy8ctmg110_write_regs(ts, CY8CTMG110_TOUCH_WAKEUP_TIME, 3, reg_p);
 152}
 153
 154static irqreturn_t cy8ctmg110_irq_thread(int irq, void *dev_id)
 155{
 156        struct cy8ctmg110 *tsc = dev_id;
 157
 158        cy8ctmg110_touch_pos(tsc);
 159
 160        return IRQ_HANDLED;
 161}
 162
 163static void cy8ctmg110_shut_off(void *_ts)
 164{
 165        struct cy8ctmg110 *ts = _ts;
 166
 167        cy8ctmg110_set_sleepmode(ts, true);
 168        cy8ctmg110_power(ts, false);
 169}
 170
 171static int cy8ctmg110_probe(struct i2c_client *client,
 172                                        const struct i2c_device_id *id)
 173{
 174        struct cy8ctmg110 *ts;
 175        struct input_dev *input_dev;
 176        int err;
 177
 178        if (!i2c_check_functionality(client->adapter,
 179                                        I2C_FUNC_SMBUS_READ_WORD_DATA))
 180                return -EIO;
 181
 182        ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
 183        if (!ts)
 184                return -ENOMEM;
 185
 186        input_dev = devm_input_allocate_device(&client->dev);
 187        if (!input_dev)
 188                return -ENOMEM;
 189
 190        ts->client = client;
 191        ts->input = input_dev;
 192
 193        snprintf(ts->phys, sizeof(ts->phys),
 194                 "%s/input0", dev_name(&client->dev));
 195
 196        input_dev->name = CY8CTMG110_DRIVER_NAME " Touchscreen";
 197        input_dev->phys = ts->phys;
 198        input_dev->id.bustype = BUS_I2C;
 199
 200        input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
 201        input_set_abs_params(input_dev, ABS_X,
 202                        CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 4, 0);
 203        input_set_abs_params(input_dev, ABS_Y,
 204                        CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 4, 0);
 205
 206        /* Request and assert reset line */
 207        ts->reset_gpio = devm_gpiod_get_optional(&client->dev, NULL,
 208                                                 GPIOD_OUT_HIGH);
 209        if (IS_ERR(ts->reset_gpio)) {
 210                err = PTR_ERR(ts->reset_gpio);
 211                dev_err(&client->dev,
 212                        "Unable to request reset GPIO: %d\n", err);
 213                return err;
 214        }
 215
 216        cy8ctmg110_power(ts, true);
 217        cy8ctmg110_set_sleepmode(ts, false);
 218
 219        err = devm_add_action_or_reset(&client->dev, cy8ctmg110_shut_off, ts);
 220        if (err)
 221                return err;
 222
 223        err = devm_request_threaded_irq(&client->dev, client->irq,
 224                                        NULL, cy8ctmg110_irq_thread,
 225                                        IRQF_ONESHOT, "touch_reset_key", ts);
 226        if (err) {
 227                dev_err(&client->dev,
 228                        "irq %d busy? error %d\n", client->irq, err);
 229                return err;
 230        }
 231
 232        err = input_register_device(input_dev);
 233        if (err)
 234                return err;
 235
 236        i2c_set_clientdata(client, ts);
 237
 238        return 0;
 239}
 240
 241static int __maybe_unused cy8ctmg110_suspend(struct device *dev)
 242{
 243        struct i2c_client *client = to_i2c_client(dev);
 244        struct cy8ctmg110 *ts = i2c_get_clientdata(client);
 245
 246        if (!device_may_wakeup(&client->dev)) {
 247                cy8ctmg110_set_sleepmode(ts, true);
 248                cy8ctmg110_power(ts, false);
 249        }
 250
 251        return 0;
 252}
 253
 254static int __maybe_unused cy8ctmg110_resume(struct device *dev)
 255{
 256        struct i2c_client *client = to_i2c_client(dev);
 257        struct cy8ctmg110 *ts = i2c_get_clientdata(client);
 258
 259        if (!device_may_wakeup(&client->dev)) {
 260                cy8ctmg110_power(ts, true);
 261                cy8ctmg110_set_sleepmode(ts, false);
 262        }
 263
 264        return 0;
 265}
 266
 267static SIMPLE_DEV_PM_OPS(cy8ctmg110_pm, cy8ctmg110_suspend, cy8ctmg110_resume);
 268
 269static const struct i2c_device_id cy8ctmg110_idtable[] = {
 270        { CY8CTMG110_DRIVER_NAME, 1 },
 271        { }
 272};
 273
 274MODULE_DEVICE_TABLE(i2c, cy8ctmg110_idtable);
 275
 276static struct i2c_driver cy8ctmg110_driver = {
 277        .driver         = {
 278                .name   = CY8CTMG110_DRIVER_NAME,
 279                .pm     = &cy8ctmg110_pm,
 280        },
 281        .id_table       = cy8ctmg110_idtable,
 282        .probe          = cy8ctmg110_probe,
 283};
 284
 285module_i2c_driver(cy8ctmg110_driver);
 286
 287MODULE_AUTHOR("Samuli Konttila <samuli.konttila@aavamobile.com>");
 288MODULE_DESCRIPTION("cy8ctmg110 TouchScreen Driver");
 289MODULE_LICENSE("GPL v2");
 290