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> 1171f8e38aSDmitry Torokhov #include <linux/slab.h> 12e3559442SMarek Vasut #include <asm/unaligned.h> 135c6a7a62SOlivier Sobrie 1449588917SMarek Vasut #define ILI210X_TOUCHES 2 15eb91ecc9SMarek Vasut #define ILI211X_TOUCHES 10 1649588917SMarek Vasut #define ILI251X_TOUCHES 10 1771f8e38aSDmitry Torokhov 1871f8e38aSDmitry Torokhov #define ILI2XXX_POLL_PERIOD 20 195c6a7a62SOlivier Sobrie 205c6a7a62SOlivier Sobrie /* Touchscreen commands */ 215c6a7a62SOlivier Sobrie #define REG_TOUCHDATA 0x10 225c6a7a62SOlivier Sobrie #define REG_PANEL_INFO 0x20 235c6a7a62SOlivier Sobrie #define REG_FIRMWARE_VERSION 0x40 245c6a7a62SOlivier Sobrie #define REG_CALIBRATE 0xcc 255c6a7a62SOlivier Sobrie 265c6a7a62SOlivier Sobrie struct firmware_version { 275c6a7a62SOlivier Sobrie u8 id; 285c6a7a62SOlivier Sobrie u8 major; 295c6a7a62SOlivier Sobrie u8 minor; 305c6a7a62SOlivier Sobrie } __packed; 315c6a7a62SOlivier Sobrie 3249588917SMarek Vasut enum ili2xxx_model { 3349588917SMarek Vasut MODEL_ILI210X, 34eb91ecc9SMarek Vasut MODEL_ILI211X, 3549588917SMarek Vasut MODEL_ILI251X, 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; 4349588917SMarek Vasut enum ili2xxx_model model; 4449588917SMarek Vasut unsigned int max_touches; 4571f8e38aSDmitry Torokhov bool stop; 465c6a7a62SOlivier Sobrie }; 475c6a7a62SOlivier Sobrie 485c6a7a62SOlivier Sobrie static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, 495c6a7a62SOlivier Sobrie size_t len) 505c6a7a62SOlivier Sobrie { 5149588917SMarek Vasut struct ili210x *priv = i2c_get_clientdata(client); 525c6a7a62SOlivier Sobrie struct i2c_msg msg[2] = { 535c6a7a62SOlivier Sobrie { 545c6a7a62SOlivier Sobrie .addr = client->addr, 555c6a7a62SOlivier Sobrie .flags = 0, 565c6a7a62SOlivier Sobrie .len = 1, 575c6a7a62SOlivier Sobrie .buf = ®, 585c6a7a62SOlivier Sobrie }, 595c6a7a62SOlivier Sobrie { 605c6a7a62SOlivier Sobrie .addr = client->addr, 615c6a7a62SOlivier Sobrie .flags = I2C_M_RD, 625c6a7a62SOlivier Sobrie .len = len, 635c6a7a62SOlivier Sobrie .buf = buf, 645c6a7a62SOlivier Sobrie } 655c6a7a62SOlivier Sobrie }; 665c6a7a62SOlivier Sobrie 6749588917SMarek Vasut if (priv->model == MODEL_ILI251X) { 6849588917SMarek Vasut if (i2c_transfer(client->adapter, msg, 1) != 1) { 6949588917SMarek Vasut dev_err(&client->dev, "i2c transfer failed\n"); 7049588917SMarek Vasut return -EIO; 7149588917SMarek Vasut } 7249588917SMarek Vasut 7349588917SMarek Vasut usleep_range(5000, 5500); 7449588917SMarek Vasut 7549588917SMarek Vasut if (i2c_transfer(client->adapter, msg + 1, 1) != 1) { 7649588917SMarek Vasut dev_err(&client->dev, "i2c transfer failed\n"); 7749588917SMarek Vasut return -EIO; 7849588917SMarek Vasut } 7949588917SMarek Vasut } else { 805c6a7a62SOlivier Sobrie if (i2c_transfer(client->adapter, msg, 2) != 2) { 815c6a7a62SOlivier Sobrie dev_err(&client->dev, "i2c transfer failed\n"); 825c6a7a62SOlivier Sobrie return -EIO; 835c6a7a62SOlivier Sobrie } 8449588917SMarek Vasut } 8549588917SMarek Vasut 8649588917SMarek Vasut return 0; 8749588917SMarek Vasut } 8849588917SMarek Vasut 8949588917SMarek Vasut static int ili210x_read(struct i2c_client *client, void *buf, size_t len) 9049588917SMarek Vasut { 9149588917SMarek Vasut struct i2c_msg msg = { 9249588917SMarek Vasut .addr = client->addr, 9349588917SMarek Vasut .flags = I2C_M_RD, 9449588917SMarek Vasut .len = len, 9549588917SMarek Vasut .buf = buf, 9649588917SMarek Vasut }; 9749588917SMarek Vasut 9849588917SMarek Vasut if (i2c_transfer(client->adapter, &msg, 1) != 1) { 9949588917SMarek Vasut dev_err(&client->dev, "i2c transfer failed\n"); 10049588917SMarek Vasut return -EIO; 10149588917SMarek Vasut } 1025c6a7a62SOlivier Sobrie 1035c6a7a62SOlivier Sobrie return 0; 1045c6a7a62SOlivier Sobrie } 1055c6a7a62SOlivier Sobrie 106e3559442SMarek Vasut static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, 107e3559442SMarek Vasut unsigned int finger, 108e3559442SMarek Vasut unsigned int *x, unsigned int *y) 1095c6a7a62SOlivier Sobrie { 11049588917SMarek Vasut if (finger >= ILI210X_TOUCHES) 111e3559442SMarek Vasut return false; 112e3559442SMarek Vasut 113e3559442SMarek Vasut if (touchdata[0] & BIT(finger)) 114e3559442SMarek Vasut return false; 115e3559442SMarek Vasut 116e3559442SMarek Vasut *x = get_unaligned_be16(touchdata + 1 + (finger * 4) + 0); 117e3559442SMarek Vasut *y = get_unaligned_be16(touchdata + 1 + (finger * 4) + 2); 118e3559442SMarek Vasut 119e3559442SMarek Vasut return true; 120e3559442SMarek Vasut } 121e3559442SMarek Vasut 122eb91ecc9SMarek Vasut static bool ili211x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, 123eb91ecc9SMarek Vasut unsigned int finger, 124eb91ecc9SMarek Vasut unsigned int *x, unsigned int *y) 125eb91ecc9SMarek Vasut { 126eb91ecc9SMarek Vasut u32 data; 127eb91ecc9SMarek Vasut 128eb91ecc9SMarek Vasut if (finger >= ILI211X_TOUCHES) 129eb91ecc9SMarek Vasut return false; 130eb91ecc9SMarek Vasut 131eb91ecc9SMarek Vasut data = get_unaligned_be32(touchdata + 1 + (finger * 4) + 0); 132eb91ecc9SMarek Vasut if (data == 0xffffffff) /* Finger up */ 133eb91ecc9SMarek Vasut return false; 134eb91ecc9SMarek Vasut 135eb91ecc9SMarek Vasut *x = ((touchdata[1 + (finger * 4) + 0] & 0xf0) << 4) | 136eb91ecc9SMarek Vasut touchdata[1 + (finger * 4) + 1]; 137eb91ecc9SMarek Vasut *y = ((touchdata[1 + (finger * 4) + 0] & 0x0f) << 8) | 138eb91ecc9SMarek Vasut touchdata[1 + (finger * 4) + 2]; 139eb91ecc9SMarek Vasut 140eb91ecc9SMarek Vasut return true; 141eb91ecc9SMarek Vasut } 142eb91ecc9SMarek Vasut 14349588917SMarek Vasut static bool ili251x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, 14449588917SMarek Vasut unsigned int finger, 14549588917SMarek Vasut unsigned int *x, unsigned int *y) 14649588917SMarek Vasut { 14749588917SMarek Vasut if (finger >= ILI251X_TOUCHES) 14849588917SMarek Vasut return false; 14949588917SMarek Vasut 15049588917SMarek Vasut *x = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0); 15149588917SMarek Vasut if (!(*x & BIT(15))) /* Touch indication */ 15249588917SMarek Vasut return false; 15349588917SMarek Vasut 15449588917SMarek Vasut *x &= 0x3fff; 15549588917SMarek Vasut *y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2); 15649588917SMarek Vasut 15749588917SMarek Vasut return true; 15849588917SMarek Vasut } 15949588917SMarek Vasut 160e3559442SMarek Vasut static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) 161e3559442SMarek Vasut { 162e3559442SMarek Vasut struct input_dev *input = priv->input; 1635c6a7a62SOlivier Sobrie int i; 16449588917SMarek Vasut bool contact = false, touch = false; 16549588917SMarek Vasut unsigned int x = 0, y = 0; 1665c6a7a62SOlivier Sobrie 16749588917SMarek Vasut for (i = 0; i < priv->max_touches; i++) { 16849588917SMarek Vasut if (priv->model == MODEL_ILI210X) { 16949588917SMarek Vasut touch = ili210x_touchdata_to_coords(priv, touchdata, 17049588917SMarek Vasut i, &x, &y); 171eb91ecc9SMarek Vasut } else if (priv->model == MODEL_ILI211X) { 172eb91ecc9SMarek Vasut touch = ili211x_touchdata_to_coords(priv, touchdata, 173eb91ecc9SMarek Vasut i, &x, &y); 17449588917SMarek Vasut } else if (priv->model == MODEL_ILI251X) { 17549588917SMarek Vasut touch = ili251x_touchdata_to_coords(priv, touchdata, 17649588917SMarek Vasut i, &x, &y); 17749588917SMarek Vasut if (touch) 17849588917SMarek Vasut contact = true; 17949588917SMarek Vasut } 18049588917SMarek Vasut 181f67cc3e9SMarek Vasut input_mt_slot(input, i); 1825c6a7a62SOlivier Sobrie input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); 183f67cc3e9SMarek Vasut if (!touch) 184f67cc3e9SMarek Vasut continue; 185f67cc3e9SMarek Vasut touchscreen_report_pos(input, &priv->prop, x, y, 186f67cc3e9SMarek Vasut true); 1875c6a7a62SOlivier Sobrie } 1885c6a7a62SOlivier Sobrie 1895c6a7a62SOlivier Sobrie input_mt_report_pointer_emulation(input, false); 1905c6a7a62SOlivier Sobrie input_sync(input); 191e3559442SMarek Vasut 19249588917SMarek Vasut if (priv->model == MODEL_ILI210X) 19349588917SMarek Vasut contact = touchdata[0] & 0xf3; 19449588917SMarek Vasut 19549588917SMarek Vasut return contact; 1965c6a7a62SOlivier Sobrie } 1975c6a7a62SOlivier Sobrie 19871f8e38aSDmitry Torokhov static irqreturn_t ili210x_irq(int irq, void *irq_data) 1995c6a7a62SOlivier Sobrie { 20071f8e38aSDmitry Torokhov struct ili210x *priv = irq_data; 2015c6a7a62SOlivier Sobrie struct i2c_client *client = priv->client; 20249588917SMarek Vasut u8 touchdata[64] = { 0 }; 203eb91ecc9SMarek Vasut s16 sum = 0; 204e3559442SMarek Vasut bool touch; 20571f8e38aSDmitry Torokhov int i; 20671f8e38aSDmitry Torokhov int error; 2075c6a7a62SOlivier Sobrie 20871f8e38aSDmitry Torokhov do { 20949588917SMarek Vasut if (priv->model == MODEL_ILI210X) { 2105c6a7a62SOlivier Sobrie error = ili210x_read_reg(client, REG_TOUCHDATA, 211e3559442SMarek Vasut touchdata, sizeof(touchdata)); 212eb91ecc9SMarek Vasut } else if (priv->model == MODEL_ILI211X) { 213eb91ecc9SMarek Vasut error = ili210x_read(client, touchdata, 43); 214eb91ecc9SMarek Vasut if (!error) { 21571f8e38aSDmitry Torokhov /* 21671f8e38aSDmitry Torokhov * This chip uses custom checksum at the end 21771f8e38aSDmitry Torokhov * of data. 21871f8e38aSDmitry Torokhov */ 219eb91ecc9SMarek Vasut for (i = 0; i <= 41; i++) 220eb91ecc9SMarek Vasut sum = (sum + touchdata[i]) & 0xff; 221eb91ecc9SMarek Vasut if ((-sum & 0xff) != touchdata[42]) { 222eb91ecc9SMarek Vasut dev_err(&client->dev, 223eb91ecc9SMarek Vasut "CRC error (crc=0x%02x expected=0x%02x)\n", 224eb91ecc9SMarek Vasut sum, touchdata[42]); 22571f8e38aSDmitry Torokhov break; 226eb91ecc9SMarek Vasut } 227eb91ecc9SMarek Vasut } 22849588917SMarek Vasut } else if (priv->model == MODEL_ILI251X) { 22949588917SMarek Vasut error = ili210x_read_reg(client, REG_TOUCHDATA, 23049588917SMarek Vasut touchdata, 31); 23149588917SMarek Vasut if (!error && touchdata[0] == 2) 23271f8e38aSDmitry Torokhov error = ili210x_read(client, 23371f8e38aSDmitry Torokhov &touchdata[31], 20); 23449588917SMarek Vasut } 23549588917SMarek Vasut 2365c6a7a62SOlivier Sobrie if (error) { 2375c6a7a62SOlivier Sobrie dev_err(&client->dev, 2385c6a7a62SOlivier Sobrie "Unable to get touchdata, err = %d\n", error); 23971f8e38aSDmitry Torokhov break; 2405c6a7a62SOlivier Sobrie } 2415c6a7a62SOlivier Sobrie 242e3559442SMarek Vasut touch = ili210x_report_events(priv, touchdata); 243e3559442SMarek Vasut if (touch) 24471f8e38aSDmitry Torokhov msleep(ILI2XXX_POLL_PERIOD); 24571f8e38aSDmitry Torokhov } while (!priv->stop && touch); 2465c6a7a62SOlivier Sobrie 2475c6a7a62SOlivier Sobrie return IRQ_HANDLED; 2485c6a7a62SOlivier Sobrie } 2495c6a7a62SOlivier Sobrie 2505c6a7a62SOlivier Sobrie static ssize_t ili210x_calibrate(struct device *dev, 2515c6a7a62SOlivier Sobrie struct device_attribute *attr, 2525c6a7a62SOlivier Sobrie const char *buf, size_t count) 2535c6a7a62SOlivier Sobrie { 2545c6a7a62SOlivier Sobrie struct i2c_client *client = to_i2c_client(dev); 2555c6a7a62SOlivier Sobrie struct ili210x *priv = i2c_get_clientdata(client); 2565c6a7a62SOlivier Sobrie unsigned long calibrate; 2575c6a7a62SOlivier Sobrie int rc; 2585c6a7a62SOlivier Sobrie u8 cmd = REG_CALIBRATE; 2595c6a7a62SOlivier Sobrie 2605c6a7a62SOlivier Sobrie if (kstrtoul(buf, 10, &calibrate)) 2615c6a7a62SOlivier Sobrie return -EINVAL; 2625c6a7a62SOlivier Sobrie 2635c6a7a62SOlivier Sobrie if (calibrate > 1) 2645c6a7a62SOlivier Sobrie return -EINVAL; 2655c6a7a62SOlivier Sobrie 2665c6a7a62SOlivier Sobrie if (calibrate) { 2675c6a7a62SOlivier Sobrie rc = i2c_master_send(priv->client, &cmd, sizeof(cmd)); 2685c6a7a62SOlivier Sobrie if (rc != sizeof(cmd)) 2695c6a7a62SOlivier Sobrie return -EIO; 2705c6a7a62SOlivier Sobrie } 2715c6a7a62SOlivier Sobrie 2725c6a7a62SOlivier Sobrie return count; 2735c6a7a62SOlivier Sobrie } 274b27c0d0cSDmitry Torokhov static DEVICE_ATTR(calibrate, S_IWUSR, NULL, ili210x_calibrate); 2755c6a7a62SOlivier Sobrie 2765c6a7a62SOlivier Sobrie static struct attribute *ili210x_attributes[] = { 2775c6a7a62SOlivier Sobrie &dev_attr_calibrate.attr, 2785c6a7a62SOlivier Sobrie NULL, 2795c6a7a62SOlivier Sobrie }; 2805c6a7a62SOlivier Sobrie 2815c6a7a62SOlivier Sobrie static const struct attribute_group ili210x_attr_group = { 2825c6a7a62SOlivier Sobrie .attrs = ili210x_attributes, 2835c6a7a62SOlivier Sobrie }; 2845c6a7a62SOlivier Sobrie 285201f3c80SMarek Vasut static void ili210x_power_down(void *data) 286201f3c80SMarek Vasut { 287201f3c80SMarek Vasut struct gpio_desc *reset_gpio = data; 288201f3c80SMarek Vasut 289201f3c80SMarek Vasut gpiod_set_value_cansleep(reset_gpio, 1); 290201f3c80SMarek Vasut } 291201f3c80SMarek Vasut 29271f8e38aSDmitry Torokhov static void ili210x_stop(void *data) 2931bdec5d9SMarek Vasut { 2941bdec5d9SMarek Vasut struct ili210x *priv = data; 2951bdec5d9SMarek Vasut 29671f8e38aSDmitry Torokhov /* Tell ISR to quit even if there is a contact. */ 29771f8e38aSDmitry Torokhov priv->stop = true; 2981bdec5d9SMarek Vasut } 2991bdec5d9SMarek Vasut 3005298cc4cSBill Pemberton static int ili210x_i2c_probe(struct i2c_client *client, 3015c6a7a62SOlivier Sobrie const struct i2c_device_id *id) 3025c6a7a62SOlivier Sobrie { 3035c6a7a62SOlivier Sobrie struct device *dev = &client->dev; 3045c6a7a62SOlivier Sobrie struct ili210x *priv; 305201f3c80SMarek Vasut struct gpio_desc *reset_gpio; 3065c6a7a62SOlivier Sobrie struct input_dev *input; 3075c6a7a62SOlivier Sobrie struct firmware_version firmware; 30849588917SMarek Vasut enum ili2xxx_model model; 3095c6a7a62SOlivier Sobrie int error; 3105c6a7a62SOlivier Sobrie 31149588917SMarek Vasut model = (enum ili2xxx_model)id->driver_data; 31249588917SMarek Vasut 3135c6a7a62SOlivier Sobrie dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); 3145c6a7a62SOlivier Sobrie 3155c6a7a62SOlivier Sobrie if (client->irq <= 0) { 3165c6a7a62SOlivier Sobrie dev_err(dev, "No IRQ!\n"); 3175c6a7a62SOlivier Sobrie return -EINVAL; 3185c6a7a62SOlivier Sobrie } 3195c6a7a62SOlivier Sobrie 320201f3c80SMarek Vasut reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); 321201f3c80SMarek Vasut if (IS_ERR(reset_gpio)) 322201f3c80SMarek Vasut return PTR_ERR(reset_gpio); 323201f3c80SMarek Vasut 324201f3c80SMarek Vasut if (reset_gpio) { 325201f3c80SMarek Vasut error = devm_add_action_or_reset(dev, ili210x_power_down, 326201f3c80SMarek Vasut reset_gpio); 327201f3c80SMarek Vasut if (error) 328201f3c80SMarek Vasut return error; 329201f3c80SMarek Vasut 330201f3c80SMarek Vasut usleep_range(50, 100); 331201f3c80SMarek Vasut gpiod_set_value_cansleep(reset_gpio, 0); 332201f3c80SMarek Vasut msleep(100); 333201f3c80SMarek Vasut } 334201f3c80SMarek Vasut 33512294577SMarek Vasut priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 33612294577SMarek Vasut if (!priv) 33712294577SMarek Vasut return -ENOMEM; 33812294577SMarek Vasut 33912294577SMarek Vasut input = devm_input_allocate_device(dev); 34012294577SMarek Vasut if (!input) 34112294577SMarek Vasut return -ENOMEM; 34212294577SMarek Vasut 34312294577SMarek Vasut priv->client = client; 34412294577SMarek Vasut priv->input = input; 34512294577SMarek Vasut priv->reset_gpio = reset_gpio; 34649588917SMarek Vasut priv->model = model; 34749588917SMarek Vasut if (model == MODEL_ILI210X) 34849588917SMarek Vasut priv->max_touches = ILI210X_TOUCHES; 349eb91ecc9SMarek Vasut if (model == MODEL_ILI211X) 350eb91ecc9SMarek Vasut priv->max_touches = ILI211X_TOUCHES; 35149588917SMarek Vasut if (model == MODEL_ILI251X) 35249588917SMarek Vasut priv->max_touches = ILI251X_TOUCHES; 35312294577SMarek Vasut 35412294577SMarek Vasut i2c_set_clientdata(client, priv); 35512294577SMarek Vasut 3565c6a7a62SOlivier Sobrie /* Get firmware version */ 3575c6a7a62SOlivier Sobrie error = ili210x_read_reg(client, REG_FIRMWARE_VERSION, 3585c6a7a62SOlivier Sobrie &firmware, sizeof(firmware)); 3595c6a7a62SOlivier Sobrie if (error) { 3605c6a7a62SOlivier Sobrie dev_err(dev, "Failed to get firmware version, err: %d\n", 3615c6a7a62SOlivier Sobrie error); 3625c6a7a62SOlivier Sobrie return error; 3635c6a7a62SOlivier Sobrie } 3645c6a7a62SOlivier Sobrie 3655c6a7a62SOlivier Sobrie /* Setup input device */ 3665c6a7a62SOlivier Sobrie input->name = "ILI210x Touchscreen"; 3675c6a7a62SOlivier Sobrie input->id.bustype = BUS_I2C; 3685c6a7a62SOlivier Sobrie input->dev.parent = dev; 3695c6a7a62SOlivier Sobrie 3705c6a7a62SOlivier Sobrie /* Multi touch */ 371f67cc3e9SMarek Vasut input_set_abs_params(input, ABS_MT_POSITION_X, 0, 0xffff, 0, 0); 372f67cc3e9SMarek Vasut input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0); 373f67cc3e9SMarek Vasut touchscreen_parse_properties(input, true, &priv->prop); 374*43f06a4cSDmitry Torokhov 375*43f06a4cSDmitry Torokhov error = input_mt_init_slots(input, priv->max_touches, INPUT_MT_DIRECT); 376*43f06a4cSDmitry Torokhov if (error) { 377*43f06a4cSDmitry Torokhov dev_err(dev, "Unable to set up slots, err: %d\n", error); 378*43f06a4cSDmitry Torokhov return error; 379*43f06a4cSDmitry Torokhov } 3805c6a7a62SOlivier Sobrie 38171f8e38aSDmitry Torokhov error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq, 38271f8e38aSDmitry Torokhov IRQF_ONESHOT, client->name, priv); 3835c6a7a62SOlivier Sobrie if (error) { 3845c6a7a62SOlivier Sobrie dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", 3855c6a7a62SOlivier Sobrie error); 3861bdec5d9SMarek Vasut return error; 3875c6a7a62SOlivier Sobrie } 3885c6a7a62SOlivier Sobrie 38971f8e38aSDmitry Torokhov error = devm_add_action_or_reset(dev, ili210x_stop, priv); 39071f8e38aSDmitry Torokhov if (error) 39171f8e38aSDmitry Torokhov return error; 39271f8e38aSDmitry Torokhov 393576057bfSDmitry Torokhov error = devm_device_add_group(dev, &ili210x_attr_group); 3945c6a7a62SOlivier Sobrie if (error) { 3955c6a7a62SOlivier Sobrie dev_err(dev, "Unable to create sysfs attributes, err: %d\n", 3965c6a7a62SOlivier Sobrie error); 3971bdec5d9SMarek Vasut return error; 3985c6a7a62SOlivier Sobrie } 3995c6a7a62SOlivier Sobrie 4005c6a7a62SOlivier Sobrie error = input_register_device(priv->input); 4015c6a7a62SOlivier Sobrie if (error) { 402971bd8faSMasanari Iida dev_err(dev, "Cannot register input device, err: %d\n", error); 403576057bfSDmitry Torokhov return error; 4045c6a7a62SOlivier Sobrie } 4055c6a7a62SOlivier Sobrie 406d7ddf154SGuenter Roeck device_init_wakeup(dev, 1); 4075c6a7a62SOlivier Sobrie 4085c6a7a62SOlivier Sobrie dev_dbg(dev, 4095c6a7a62SOlivier Sobrie "ILI210x initialized (IRQ: %d), firmware version %d.%d.%d", 4105c6a7a62SOlivier Sobrie client->irq, firmware.id, firmware.major, firmware.minor); 4115c6a7a62SOlivier Sobrie 4125c6a7a62SOlivier Sobrie return 0; 4135c6a7a62SOlivier Sobrie } 4145c6a7a62SOlivier Sobrie 41502b6a58bSJingoo Han static int __maybe_unused ili210x_i2c_suspend(struct device *dev) 4165c6a7a62SOlivier Sobrie { 4175c6a7a62SOlivier Sobrie struct i2c_client *client = to_i2c_client(dev); 4185c6a7a62SOlivier Sobrie 4195c6a7a62SOlivier Sobrie if (device_may_wakeup(&client->dev)) 4205c6a7a62SOlivier Sobrie enable_irq_wake(client->irq); 4215c6a7a62SOlivier Sobrie 4225c6a7a62SOlivier Sobrie return 0; 4235c6a7a62SOlivier Sobrie } 4245c6a7a62SOlivier Sobrie 42502b6a58bSJingoo Han static int __maybe_unused ili210x_i2c_resume(struct device *dev) 4265c6a7a62SOlivier Sobrie { 4275c6a7a62SOlivier Sobrie struct i2c_client *client = to_i2c_client(dev); 4285c6a7a62SOlivier Sobrie 4295c6a7a62SOlivier Sobrie if (device_may_wakeup(&client->dev)) 4305c6a7a62SOlivier Sobrie disable_irq_wake(client->irq); 4315c6a7a62SOlivier Sobrie 4325c6a7a62SOlivier Sobrie return 0; 4335c6a7a62SOlivier Sobrie } 4345c6a7a62SOlivier Sobrie 4355c6a7a62SOlivier Sobrie static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, 4365c6a7a62SOlivier Sobrie ili210x_i2c_suspend, ili210x_i2c_resume); 4375c6a7a62SOlivier Sobrie 4385c6a7a62SOlivier Sobrie static const struct i2c_device_id ili210x_i2c_id[] = { 43949588917SMarek Vasut { "ili210x", MODEL_ILI210X }, 440eb91ecc9SMarek Vasut { "ili2117", MODEL_ILI211X }, 44149588917SMarek Vasut { "ili251x", MODEL_ILI251X }, 4425c6a7a62SOlivier Sobrie { } 4435c6a7a62SOlivier Sobrie }; 4445c6a7a62SOlivier Sobrie MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); 4455c6a7a62SOlivier Sobrie 446c5d0e4b5SMarek Vasut static const struct of_device_id ili210x_dt_ids[] = { 44749588917SMarek Vasut { .compatible = "ilitek,ili210x", .data = (void *)MODEL_ILI210X }, 448eb91ecc9SMarek Vasut { .compatible = "ilitek,ili2117", .data = (void *)MODEL_ILI211X }, 44949588917SMarek Vasut { .compatible = "ilitek,ili251x", .data = (void *)MODEL_ILI251X }, 450c5d0e4b5SMarek Vasut { }, 451c5d0e4b5SMarek Vasut }; 452c5d0e4b5SMarek Vasut MODULE_DEVICE_TABLE(of, ili210x_dt_ids); 453c5d0e4b5SMarek Vasut 4545c6a7a62SOlivier Sobrie static struct i2c_driver ili210x_ts_driver = { 4555c6a7a62SOlivier Sobrie .driver = { 4565c6a7a62SOlivier Sobrie .name = "ili210x_i2c", 4575c6a7a62SOlivier Sobrie .pm = &ili210x_i2c_pm, 458c5d0e4b5SMarek Vasut .of_match_table = ili210x_dt_ids, 4595c6a7a62SOlivier Sobrie }, 4605c6a7a62SOlivier Sobrie .id_table = ili210x_i2c_id, 4615c6a7a62SOlivier Sobrie .probe = ili210x_i2c_probe, 4625c6a7a62SOlivier Sobrie }; 4635c6a7a62SOlivier Sobrie 4645c6a7a62SOlivier Sobrie module_i2c_driver(ili210x_ts_driver); 4655c6a7a62SOlivier Sobrie 4665c6a7a62SOlivier Sobrie MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>"); 4675c6a7a62SOlivier Sobrie MODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver"); 4685c6a7a62SOlivier Sobrie MODULE_LICENSE("GPL"); 469