109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 271f8e38aSDmitry Torokhov #include <linux/delay.h> 371f8e38aSDmitry Torokhov #include <linux/gpio/consumer.h> 45c6a7a62SOlivier Sobrie #include <linux/i2c.h> 55c6a7a62SOlivier Sobrie #include <linux/input.h> 65c6a7a62SOlivier Sobrie #include <linux/input/mt.h> 7f67cc3e9SMarek Vasut #include <linux/input/touchscreen.h> 871f8e38aSDmitry Torokhov #include <linux/interrupt.h> 971f8e38aSDmitry Torokhov #include <linux/module.h> 1049588917SMarek Vasut #include <linux/of_device.h> 11*b32fbeaeSSven Van Asbroeck #include <linux/sizes.h> 1271f8e38aSDmitry Torokhov #include <linux/slab.h> 13e3559442SMarek Vasut #include <asm/unaligned.h> 145c6a7a62SOlivier Sobrie 1571f8e38aSDmitry Torokhov #define ILI2XXX_POLL_PERIOD 20 165c6a7a62SOlivier Sobrie 17ef536abdSDmitry Torokhov #define ILI210X_DATA_SIZE 64 18ef536abdSDmitry Torokhov #define ILI211X_DATA_SIZE 43 19ef536abdSDmitry Torokhov #define ILI251X_DATA_SIZE1 31 20ef536abdSDmitry Torokhov #define ILI251X_DATA_SIZE2 20 21ef536abdSDmitry Torokhov 225c6a7a62SOlivier Sobrie /* Touchscreen commands */ 235c6a7a62SOlivier Sobrie #define REG_TOUCHDATA 0x10 245c6a7a62SOlivier Sobrie #define REG_PANEL_INFO 0x20 255c6a7a62SOlivier Sobrie #define REG_CALIBRATE 0xcc 265c6a7a62SOlivier Sobrie 27ef536abdSDmitry Torokhov struct ili2xxx_chip { 28ef536abdSDmitry Torokhov int (*read_reg)(struct i2c_client *client, u8 reg, 29ef536abdSDmitry Torokhov void *buf, size_t len); 30ef536abdSDmitry Torokhov int (*get_touch_data)(struct i2c_client *client, u8 *data); 31ef536abdSDmitry Torokhov bool (*parse_touch_data)(const u8 *data, unsigned int finger, 32ef536abdSDmitry Torokhov unsigned int *x, unsigned int *y); 33ef536abdSDmitry Torokhov bool (*continue_polling)(const u8 *data, bool touch); 34ef536abdSDmitry Torokhov unsigned int max_touches; 35*b32fbeaeSSven Van Asbroeck unsigned int resolution; 3649588917SMarek Vasut }; 3749588917SMarek Vasut 385c6a7a62SOlivier Sobrie struct ili210x { 395c6a7a62SOlivier Sobrie struct i2c_client *client; 405c6a7a62SOlivier Sobrie struct input_dev *input; 41201f3c80SMarek Vasut struct gpio_desc *reset_gpio; 42f67cc3e9SMarek Vasut struct touchscreen_properties prop; 43ef536abdSDmitry Torokhov const struct ili2xxx_chip *chip; 4471f8e38aSDmitry Torokhov bool stop; 455c6a7a62SOlivier Sobrie }; 465c6a7a62SOlivier Sobrie 47ef536abdSDmitry Torokhov static int ili210x_read_reg(struct i2c_client *client, 48ef536abdSDmitry Torokhov u8 reg, void *buf, size_t len) 495c6a7a62SOlivier Sobrie { 50ef536abdSDmitry Torokhov struct i2c_msg msg[] = { 515c6a7a62SOlivier Sobrie { 525c6a7a62SOlivier Sobrie .addr = client->addr, 535c6a7a62SOlivier Sobrie .flags = 0, 545c6a7a62SOlivier Sobrie .len = 1, 555c6a7a62SOlivier Sobrie .buf = ®, 565c6a7a62SOlivier Sobrie }, 575c6a7a62SOlivier Sobrie { 585c6a7a62SOlivier Sobrie .addr = client->addr, 595c6a7a62SOlivier Sobrie .flags = I2C_M_RD, 605c6a7a62SOlivier Sobrie .len = len, 615c6a7a62SOlivier Sobrie .buf = buf, 625c6a7a62SOlivier Sobrie } 635c6a7a62SOlivier Sobrie }; 64ef536abdSDmitry Torokhov int error, ret; 655c6a7a62SOlivier Sobrie 66ef536abdSDmitry Torokhov ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); 67ef536abdSDmitry Torokhov if (ret != ARRAY_SIZE(msg)) { 68ef536abdSDmitry Torokhov error = ret < 0 ? ret : -EIO; 69ef536abdSDmitry Torokhov dev_err(&client->dev, "%s failed: %d\n", __func__, error); 70ef536abdSDmitry Torokhov return error; 7149588917SMarek Vasut } 7249588917SMarek Vasut 7349588917SMarek Vasut return 0; 7449588917SMarek Vasut } 7549588917SMarek Vasut 76ef536abdSDmitry Torokhov static int ili210x_read_touch_data(struct i2c_client *client, u8 *data) 7749588917SMarek Vasut { 78ef536abdSDmitry Torokhov return ili210x_read_reg(client, REG_TOUCHDATA, 79ef536abdSDmitry Torokhov data, ILI210X_DATA_SIZE); 8049588917SMarek Vasut } 815c6a7a62SOlivier Sobrie 82ef536abdSDmitry Torokhov static bool ili210x_touchdata_to_coords(const u8 *touchdata, 83e3559442SMarek Vasut unsigned int finger, 84e3559442SMarek Vasut unsigned int *x, unsigned int *y) 855c6a7a62SOlivier Sobrie { 86e3559442SMarek Vasut if (touchdata[0] & BIT(finger)) 87e3559442SMarek Vasut return false; 88e3559442SMarek Vasut 89e3559442SMarek Vasut *x = get_unaligned_be16(touchdata + 1 + (finger * 4) + 0); 90e3559442SMarek Vasut *y = get_unaligned_be16(touchdata + 1 + (finger * 4) + 2); 91e3559442SMarek Vasut 92e3559442SMarek Vasut return true; 93e3559442SMarek Vasut } 94e3559442SMarek Vasut 95ef536abdSDmitry Torokhov static bool ili210x_check_continue_polling(const u8 *data, bool touch) 96ef536abdSDmitry Torokhov { 97ef536abdSDmitry Torokhov return data[0] & 0xf3; 98ef536abdSDmitry Torokhov } 99ef536abdSDmitry Torokhov 100ef536abdSDmitry Torokhov static const struct ili2xxx_chip ili210x_chip = { 101ef536abdSDmitry Torokhov .read_reg = ili210x_read_reg, 102ef536abdSDmitry Torokhov .get_touch_data = ili210x_read_touch_data, 103ef536abdSDmitry Torokhov .parse_touch_data = ili210x_touchdata_to_coords, 104ef536abdSDmitry Torokhov .continue_polling = ili210x_check_continue_polling, 105ef536abdSDmitry Torokhov .max_touches = 2, 106ef536abdSDmitry Torokhov }; 107ef536abdSDmitry Torokhov 108ef536abdSDmitry Torokhov static int ili211x_read_touch_data(struct i2c_client *client, u8 *data) 109ef536abdSDmitry Torokhov { 110ef536abdSDmitry Torokhov s16 sum = 0; 111ef536abdSDmitry Torokhov int error; 112ef536abdSDmitry Torokhov int ret; 113ef536abdSDmitry Torokhov int i; 114ef536abdSDmitry Torokhov 115ef536abdSDmitry Torokhov ret = i2c_master_recv(client, data, ILI211X_DATA_SIZE); 116ef536abdSDmitry Torokhov if (ret != ILI211X_DATA_SIZE) { 117ef536abdSDmitry Torokhov error = ret < 0 ? ret : -EIO; 118ef536abdSDmitry Torokhov dev_err(&client->dev, "%s failed: %d\n", __func__, error); 119ef536abdSDmitry Torokhov return error; 120ef536abdSDmitry Torokhov } 121ef536abdSDmitry Torokhov 122ef536abdSDmitry Torokhov /* This chip uses custom checksum at the end of data */ 123ef536abdSDmitry Torokhov for (i = 0; i < ILI211X_DATA_SIZE - 1; i++) 124ef536abdSDmitry Torokhov sum = (sum + data[i]) & 0xff; 125ef536abdSDmitry Torokhov 126ef536abdSDmitry Torokhov if ((-sum & 0xff) != data[ILI211X_DATA_SIZE - 1]) { 127ef536abdSDmitry Torokhov dev_err(&client->dev, 128ef536abdSDmitry Torokhov "CRC error (crc=0x%02x expected=0x%02x)\n", 129ef536abdSDmitry Torokhov sum, data[ILI211X_DATA_SIZE - 1]); 130ef536abdSDmitry Torokhov return -EIO; 131ef536abdSDmitry Torokhov } 132ef536abdSDmitry Torokhov 133ef536abdSDmitry Torokhov return 0; 134ef536abdSDmitry Torokhov } 135ef536abdSDmitry Torokhov 136ef536abdSDmitry Torokhov static bool ili211x_touchdata_to_coords(const u8 *touchdata, 137eb91ecc9SMarek Vasut unsigned int finger, 138eb91ecc9SMarek Vasut unsigned int *x, unsigned int *y) 139eb91ecc9SMarek Vasut { 140eb91ecc9SMarek Vasut u32 data; 141eb91ecc9SMarek Vasut 142eb91ecc9SMarek Vasut data = get_unaligned_be32(touchdata + 1 + (finger * 4) + 0); 143eb91ecc9SMarek Vasut if (data == 0xffffffff) /* Finger up */ 144eb91ecc9SMarek Vasut return false; 145eb91ecc9SMarek Vasut 146eb91ecc9SMarek Vasut *x = ((touchdata[1 + (finger * 4) + 0] & 0xf0) << 4) | 147eb91ecc9SMarek Vasut touchdata[1 + (finger * 4) + 1]; 148eb91ecc9SMarek Vasut *y = ((touchdata[1 + (finger * 4) + 0] & 0x0f) << 8) | 149eb91ecc9SMarek Vasut touchdata[1 + (finger * 4) + 2]; 150eb91ecc9SMarek Vasut 151eb91ecc9SMarek Vasut return true; 152eb91ecc9SMarek Vasut } 153eb91ecc9SMarek Vasut 154ef536abdSDmitry Torokhov static bool ili211x_decline_polling(const u8 *data, bool touch) 155ef536abdSDmitry Torokhov { 156ef536abdSDmitry Torokhov return false; 157ef536abdSDmitry Torokhov } 158ef536abdSDmitry Torokhov 159ef536abdSDmitry Torokhov static const struct ili2xxx_chip ili211x_chip = { 160ef536abdSDmitry Torokhov .read_reg = ili210x_read_reg, 161ef536abdSDmitry Torokhov .get_touch_data = ili211x_read_touch_data, 162ef536abdSDmitry Torokhov .parse_touch_data = ili211x_touchdata_to_coords, 163ef536abdSDmitry Torokhov .continue_polling = ili211x_decline_polling, 164ef536abdSDmitry Torokhov .max_touches = 10, 165*b32fbeaeSSven Van Asbroeck .resolution = 2048, 166ef536abdSDmitry Torokhov }; 167ef536abdSDmitry Torokhov 168ef536abdSDmitry Torokhov static int ili251x_read_reg(struct i2c_client *client, 169ef536abdSDmitry Torokhov u8 reg, void *buf, size_t len) 170ef536abdSDmitry Torokhov { 171ef536abdSDmitry Torokhov int error; 172ef536abdSDmitry Torokhov int ret; 173ef536abdSDmitry Torokhov 174ef536abdSDmitry Torokhov ret = i2c_master_send(client, ®, 1); 175ef536abdSDmitry Torokhov if (ret == 1) { 176ef536abdSDmitry Torokhov usleep_range(5000, 5500); 177ef536abdSDmitry Torokhov 178ef536abdSDmitry Torokhov ret = i2c_master_recv(client, buf, len); 179ef536abdSDmitry Torokhov if (ret == len) 180ef536abdSDmitry Torokhov return 0; 181ef536abdSDmitry Torokhov } 182ef536abdSDmitry Torokhov 183ef536abdSDmitry Torokhov error = ret < 0 ? ret : -EIO; 184ef536abdSDmitry Torokhov dev_err(&client->dev, "%s failed: %d\n", __func__, error); 185ef536abdSDmitry Torokhov return ret; 186ef536abdSDmitry Torokhov } 187ef536abdSDmitry Torokhov 188ef536abdSDmitry Torokhov static int ili251x_read_touch_data(struct i2c_client *client, u8 *data) 189ef536abdSDmitry Torokhov { 190ef536abdSDmitry Torokhov int error; 191ef536abdSDmitry Torokhov 192ef536abdSDmitry Torokhov error = ili251x_read_reg(client, REG_TOUCHDATA, 193ef536abdSDmitry Torokhov data, ILI251X_DATA_SIZE1); 194ef536abdSDmitry Torokhov if (!error && data[0] == 2) { 195ef536abdSDmitry Torokhov error = i2c_master_recv(client, data + ILI251X_DATA_SIZE1, 196ef536abdSDmitry Torokhov ILI251X_DATA_SIZE2); 197ef536abdSDmitry Torokhov if (error >= 0 && error != ILI251X_DATA_SIZE2) 198ef536abdSDmitry Torokhov error = -EIO; 199ef536abdSDmitry Torokhov } 200ef536abdSDmitry Torokhov 201ef536abdSDmitry Torokhov return error; 202ef536abdSDmitry Torokhov } 203ef536abdSDmitry Torokhov 204ef536abdSDmitry Torokhov static bool ili251x_touchdata_to_coords(const u8 *touchdata, 20549588917SMarek Vasut unsigned int finger, 20649588917SMarek Vasut unsigned int *x, unsigned int *y) 20749588917SMarek Vasut { 208ef536abdSDmitry Torokhov u16 val; 209ef536abdSDmitry Torokhov 210ef536abdSDmitry Torokhov val = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0); 211ef536abdSDmitry Torokhov if (!(val & BIT(15))) /* Touch indication */ 21249588917SMarek Vasut return false; 21349588917SMarek Vasut 214ef536abdSDmitry Torokhov *x = val & 0x3fff; 21549588917SMarek Vasut *y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2); 21649588917SMarek Vasut 21749588917SMarek Vasut return true; 21849588917SMarek Vasut } 21949588917SMarek Vasut 220ef536abdSDmitry Torokhov static bool ili251x_check_continue_polling(const u8 *data, bool touch) 221ef536abdSDmitry Torokhov { 222ef536abdSDmitry Torokhov return touch; 223ef536abdSDmitry Torokhov } 224ef536abdSDmitry Torokhov 225ef536abdSDmitry Torokhov static const struct ili2xxx_chip ili251x_chip = { 226ef536abdSDmitry Torokhov .read_reg = ili251x_read_reg, 227ef536abdSDmitry Torokhov .get_touch_data = ili251x_read_touch_data, 228ef536abdSDmitry Torokhov .parse_touch_data = ili251x_touchdata_to_coords, 229ef536abdSDmitry Torokhov .continue_polling = ili251x_check_continue_polling, 230ef536abdSDmitry Torokhov .max_touches = 10, 231ef536abdSDmitry Torokhov }; 232ef536abdSDmitry Torokhov 233e3559442SMarek Vasut static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) 234e3559442SMarek Vasut { 235e3559442SMarek Vasut struct input_dev *input = priv->input; 2365c6a7a62SOlivier Sobrie int i; 237ef536abdSDmitry Torokhov bool contact = false, touch; 23849588917SMarek Vasut unsigned int x = 0, y = 0; 2395c6a7a62SOlivier Sobrie 240ef536abdSDmitry Torokhov for (i = 0; i < priv->chip->max_touches; i++) { 241ef536abdSDmitry Torokhov touch = priv->chip->parse_touch_data(touchdata, i, &x, &y); 24249588917SMarek Vasut 243f67cc3e9SMarek Vasut input_mt_slot(input, i); 244ef536abdSDmitry Torokhov if (input_mt_report_slot_state(input, MT_TOOL_FINGER, touch)) { 245ef536abdSDmitry Torokhov touchscreen_report_pos(input, &priv->prop, x, y, true); 246ef536abdSDmitry Torokhov contact = true; 247ef536abdSDmitry Torokhov } 2485c6a7a62SOlivier Sobrie } 2495c6a7a62SOlivier Sobrie 2505c6a7a62SOlivier Sobrie input_mt_report_pointer_emulation(input, false); 2515c6a7a62SOlivier Sobrie input_sync(input); 252e3559442SMarek Vasut 25349588917SMarek Vasut return contact; 2545c6a7a62SOlivier Sobrie } 2555c6a7a62SOlivier Sobrie 25671f8e38aSDmitry Torokhov static irqreturn_t ili210x_irq(int irq, void *irq_data) 2575c6a7a62SOlivier Sobrie { 25871f8e38aSDmitry Torokhov struct ili210x *priv = irq_data; 2595c6a7a62SOlivier Sobrie struct i2c_client *client = priv->client; 260ef536abdSDmitry Torokhov const struct ili2xxx_chip *chip = priv->chip; 261ef536abdSDmitry Torokhov u8 touchdata[ILI210X_DATA_SIZE] = { 0 }; 262ef536abdSDmitry Torokhov bool keep_polling; 263e3559442SMarek Vasut bool touch; 26471f8e38aSDmitry Torokhov int error; 2655c6a7a62SOlivier Sobrie 26671f8e38aSDmitry Torokhov do { 267ef536abdSDmitry Torokhov error = chip->get_touch_data(client, touchdata); 2685c6a7a62SOlivier Sobrie if (error) { 2695c6a7a62SOlivier Sobrie dev_err(&client->dev, 270ef536abdSDmitry Torokhov "Unable to get touch data: %d\n", error); 27171f8e38aSDmitry Torokhov break; 2725c6a7a62SOlivier Sobrie } 2735c6a7a62SOlivier Sobrie 274e3559442SMarek Vasut touch = ili210x_report_events(priv, touchdata); 275ef536abdSDmitry Torokhov keep_polling = chip->continue_polling(touchdata, touch); 276ef536abdSDmitry Torokhov if (keep_polling) 27771f8e38aSDmitry Torokhov msleep(ILI2XXX_POLL_PERIOD); 278ef536abdSDmitry Torokhov } while (!priv->stop && keep_polling); 2795c6a7a62SOlivier Sobrie 2805c6a7a62SOlivier Sobrie return IRQ_HANDLED; 2815c6a7a62SOlivier Sobrie } 2825c6a7a62SOlivier Sobrie 2835c6a7a62SOlivier Sobrie static ssize_t ili210x_calibrate(struct device *dev, 2845c6a7a62SOlivier Sobrie struct device_attribute *attr, 2855c6a7a62SOlivier Sobrie const char *buf, size_t count) 2865c6a7a62SOlivier Sobrie { 2875c6a7a62SOlivier Sobrie struct i2c_client *client = to_i2c_client(dev); 2885c6a7a62SOlivier Sobrie struct ili210x *priv = i2c_get_clientdata(client); 2895c6a7a62SOlivier Sobrie unsigned long calibrate; 2905c6a7a62SOlivier Sobrie int rc; 2915c6a7a62SOlivier Sobrie u8 cmd = REG_CALIBRATE; 2925c6a7a62SOlivier Sobrie 2935c6a7a62SOlivier Sobrie if (kstrtoul(buf, 10, &calibrate)) 2945c6a7a62SOlivier Sobrie return -EINVAL; 2955c6a7a62SOlivier Sobrie 2965c6a7a62SOlivier Sobrie if (calibrate > 1) 2975c6a7a62SOlivier Sobrie return -EINVAL; 2985c6a7a62SOlivier Sobrie 2995c6a7a62SOlivier Sobrie if (calibrate) { 3005c6a7a62SOlivier Sobrie rc = i2c_master_send(priv->client, &cmd, sizeof(cmd)); 3015c6a7a62SOlivier Sobrie if (rc != sizeof(cmd)) 3025c6a7a62SOlivier Sobrie return -EIO; 3035c6a7a62SOlivier Sobrie } 3045c6a7a62SOlivier Sobrie 3055c6a7a62SOlivier Sobrie return count; 3065c6a7a62SOlivier Sobrie } 307b27c0d0cSDmitry Torokhov static DEVICE_ATTR(calibrate, S_IWUSR, NULL, ili210x_calibrate); 3085c6a7a62SOlivier Sobrie 3095c6a7a62SOlivier Sobrie static struct attribute *ili210x_attributes[] = { 3105c6a7a62SOlivier Sobrie &dev_attr_calibrate.attr, 3115c6a7a62SOlivier Sobrie NULL, 3125c6a7a62SOlivier Sobrie }; 3135c6a7a62SOlivier Sobrie 3145c6a7a62SOlivier Sobrie static const struct attribute_group ili210x_attr_group = { 3155c6a7a62SOlivier Sobrie .attrs = ili210x_attributes, 3165c6a7a62SOlivier Sobrie }; 3175c6a7a62SOlivier Sobrie 318201f3c80SMarek Vasut static void ili210x_power_down(void *data) 319201f3c80SMarek Vasut { 320201f3c80SMarek Vasut struct gpio_desc *reset_gpio = data; 321201f3c80SMarek Vasut 322201f3c80SMarek Vasut gpiod_set_value_cansleep(reset_gpio, 1); 323201f3c80SMarek Vasut } 324201f3c80SMarek Vasut 32571f8e38aSDmitry Torokhov static void ili210x_stop(void *data) 3261bdec5d9SMarek Vasut { 3271bdec5d9SMarek Vasut struct ili210x *priv = data; 3281bdec5d9SMarek Vasut 32971f8e38aSDmitry Torokhov /* Tell ISR to quit even if there is a contact. */ 33071f8e38aSDmitry Torokhov priv->stop = true; 3311bdec5d9SMarek Vasut } 3321bdec5d9SMarek Vasut 3335298cc4cSBill Pemberton static int ili210x_i2c_probe(struct i2c_client *client, 3345c6a7a62SOlivier Sobrie const struct i2c_device_id *id) 3355c6a7a62SOlivier Sobrie { 3365c6a7a62SOlivier Sobrie struct device *dev = &client->dev; 337ef536abdSDmitry Torokhov const struct ili2xxx_chip *chip; 3385c6a7a62SOlivier Sobrie struct ili210x *priv; 339201f3c80SMarek Vasut struct gpio_desc *reset_gpio; 3405c6a7a62SOlivier Sobrie struct input_dev *input; 3415c6a7a62SOlivier Sobrie int error; 342*b32fbeaeSSven Van Asbroeck unsigned int max_xy; 3435c6a7a62SOlivier Sobrie 3445c6a7a62SOlivier Sobrie dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); 3455c6a7a62SOlivier Sobrie 346ef536abdSDmitry Torokhov chip = device_get_match_data(dev); 347ef536abdSDmitry Torokhov if (!chip && id) 348ef536abdSDmitry Torokhov chip = (const struct ili2xxx_chip *)id->driver_data; 349ef536abdSDmitry Torokhov if (!chip) { 350ef536abdSDmitry Torokhov dev_err(&client->dev, "unknown device model\n"); 351ef536abdSDmitry Torokhov return -ENODEV; 352ef536abdSDmitry Torokhov } 353ef536abdSDmitry Torokhov 3545c6a7a62SOlivier Sobrie if (client->irq <= 0) { 3555c6a7a62SOlivier Sobrie dev_err(dev, "No IRQ!\n"); 3565c6a7a62SOlivier Sobrie return -EINVAL; 3575c6a7a62SOlivier Sobrie } 3585c6a7a62SOlivier Sobrie 359201f3c80SMarek Vasut reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); 360201f3c80SMarek Vasut if (IS_ERR(reset_gpio)) 361201f3c80SMarek Vasut return PTR_ERR(reset_gpio); 362201f3c80SMarek Vasut 363201f3c80SMarek Vasut if (reset_gpio) { 364201f3c80SMarek Vasut error = devm_add_action_or_reset(dev, ili210x_power_down, 365201f3c80SMarek Vasut reset_gpio); 366201f3c80SMarek Vasut if (error) 367201f3c80SMarek Vasut return error; 368201f3c80SMarek Vasut 369201f3c80SMarek Vasut usleep_range(50, 100); 370201f3c80SMarek Vasut gpiod_set_value_cansleep(reset_gpio, 0); 371201f3c80SMarek Vasut msleep(100); 372201f3c80SMarek Vasut } 373201f3c80SMarek Vasut 37412294577SMarek Vasut priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 37512294577SMarek Vasut if (!priv) 37612294577SMarek Vasut return -ENOMEM; 37712294577SMarek Vasut 37812294577SMarek Vasut input = devm_input_allocate_device(dev); 37912294577SMarek Vasut if (!input) 38012294577SMarek Vasut return -ENOMEM; 38112294577SMarek Vasut 38212294577SMarek Vasut priv->client = client; 38312294577SMarek Vasut priv->input = input; 38412294577SMarek Vasut priv->reset_gpio = reset_gpio; 385ef536abdSDmitry Torokhov priv->chip = chip; 38612294577SMarek Vasut i2c_set_clientdata(client, priv); 38712294577SMarek Vasut 3885c6a7a62SOlivier Sobrie /* Setup input device */ 3895c6a7a62SOlivier Sobrie input->name = "ILI210x Touchscreen"; 3905c6a7a62SOlivier Sobrie input->id.bustype = BUS_I2C; 3915c6a7a62SOlivier Sobrie 3925c6a7a62SOlivier Sobrie /* Multi touch */ 393*b32fbeaeSSven Van Asbroeck max_xy = (chip->resolution ?: SZ_64K) - 1; 394*b32fbeaeSSven Van Asbroeck input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_xy, 0, 0); 395*b32fbeaeSSven Van Asbroeck input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_xy, 0, 0); 396f67cc3e9SMarek Vasut touchscreen_parse_properties(input, true, &priv->prop); 39743f06a4cSDmitry Torokhov 398ef536abdSDmitry Torokhov error = input_mt_init_slots(input, priv->chip->max_touches, 399ef536abdSDmitry Torokhov INPUT_MT_DIRECT); 40043f06a4cSDmitry Torokhov if (error) { 40143f06a4cSDmitry Torokhov dev_err(dev, "Unable to set up slots, err: %d\n", error); 40243f06a4cSDmitry Torokhov return error; 40343f06a4cSDmitry Torokhov } 4045c6a7a62SOlivier Sobrie 40571f8e38aSDmitry Torokhov error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq, 40671f8e38aSDmitry Torokhov IRQF_ONESHOT, client->name, priv); 4075c6a7a62SOlivier Sobrie if (error) { 4085c6a7a62SOlivier Sobrie dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", 4095c6a7a62SOlivier Sobrie error); 4101bdec5d9SMarek Vasut return error; 4115c6a7a62SOlivier Sobrie } 4125c6a7a62SOlivier Sobrie 41371f8e38aSDmitry Torokhov error = devm_add_action_or_reset(dev, ili210x_stop, priv); 41471f8e38aSDmitry Torokhov if (error) 41571f8e38aSDmitry Torokhov return error; 41671f8e38aSDmitry Torokhov 417576057bfSDmitry Torokhov error = devm_device_add_group(dev, &ili210x_attr_group); 4185c6a7a62SOlivier Sobrie if (error) { 4195c6a7a62SOlivier Sobrie dev_err(dev, "Unable to create sysfs attributes, err: %d\n", 4205c6a7a62SOlivier Sobrie error); 4211bdec5d9SMarek Vasut return error; 4225c6a7a62SOlivier Sobrie } 4235c6a7a62SOlivier Sobrie 4245c6a7a62SOlivier Sobrie error = input_register_device(priv->input); 4255c6a7a62SOlivier Sobrie if (error) { 426971bd8faSMasanari Iida dev_err(dev, "Cannot register input device, err: %d\n", error); 427576057bfSDmitry Torokhov return error; 4285c6a7a62SOlivier Sobrie } 4295c6a7a62SOlivier Sobrie 4305c6a7a62SOlivier Sobrie return 0; 4315c6a7a62SOlivier Sobrie } 4325c6a7a62SOlivier Sobrie 4335c6a7a62SOlivier Sobrie static const struct i2c_device_id ili210x_i2c_id[] = { 434ef536abdSDmitry Torokhov { "ili210x", (long)&ili210x_chip }, 435ef536abdSDmitry Torokhov { "ili2117", (long)&ili211x_chip }, 436ef536abdSDmitry Torokhov { "ili251x", (long)&ili251x_chip }, 4375c6a7a62SOlivier Sobrie { } 4385c6a7a62SOlivier Sobrie }; 4395c6a7a62SOlivier Sobrie MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); 4405c6a7a62SOlivier Sobrie 441c5d0e4b5SMarek Vasut static const struct of_device_id ili210x_dt_ids[] = { 442ef536abdSDmitry Torokhov { .compatible = "ilitek,ili210x", .data = &ili210x_chip }, 443ef536abdSDmitry Torokhov { .compatible = "ilitek,ili2117", .data = &ili211x_chip }, 444ef536abdSDmitry Torokhov { .compatible = "ilitek,ili251x", .data = &ili251x_chip }, 445ef536abdSDmitry Torokhov { } 446c5d0e4b5SMarek Vasut }; 447c5d0e4b5SMarek Vasut MODULE_DEVICE_TABLE(of, ili210x_dt_ids); 448c5d0e4b5SMarek Vasut 4495c6a7a62SOlivier Sobrie static struct i2c_driver ili210x_ts_driver = { 4505c6a7a62SOlivier Sobrie .driver = { 4515c6a7a62SOlivier Sobrie .name = "ili210x_i2c", 452c5d0e4b5SMarek Vasut .of_match_table = ili210x_dt_ids, 4535c6a7a62SOlivier Sobrie }, 4545c6a7a62SOlivier Sobrie .id_table = ili210x_i2c_id, 4555c6a7a62SOlivier Sobrie .probe = ili210x_i2c_probe, 4565c6a7a62SOlivier Sobrie }; 4575c6a7a62SOlivier Sobrie 4585c6a7a62SOlivier Sobrie module_i2c_driver(ili210x_ts_driver); 4595c6a7a62SOlivier Sobrie 4605c6a7a62SOlivier Sobrie MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>"); 4615c6a7a62SOlivier Sobrie MODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver"); 4625c6a7a62SOlivier Sobrie MODULE_LICENSE("GPL"); 463