1a150c1feSFabio Estevam // SPDX-License-Identifier: GPL-2.0 259bae1dbSZhang Jiejing /* 359bae1dbSZhang Jiejing * Driver for EETI eGalax Multiple Touch Controller 459bae1dbSZhang Jiejing * 559bae1dbSZhang Jiejing * Copyright (C) 2011 Freescale Semiconductor, Inc. 659bae1dbSZhang Jiejing * 759bae1dbSZhang Jiejing * based on max11801_ts.c 859bae1dbSZhang Jiejing */ 959bae1dbSZhang Jiejing 1059bae1dbSZhang Jiejing /* EETI eGalax serial touch screen controller is a I2C based multiple 1159bae1dbSZhang Jiejing * touch screen controller, it supports 5 point multiple touch. */ 1259bae1dbSZhang Jiejing 1359bae1dbSZhang Jiejing /* TODO: 1459bae1dbSZhang Jiejing - auto idle mode support 1559bae1dbSZhang Jiejing */ 1659bae1dbSZhang Jiejing 1759bae1dbSZhang Jiejing #include <linux/module.h> 1859bae1dbSZhang Jiejing #include <linux/i2c.h> 1959bae1dbSZhang Jiejing #include <linux/interrupt.h> 2059bae1dbSZhang Jiejing #include <linux/input.h> 2159bae1dbSZhang Jiejing #include <linux/irq.h> 2259bae1dbSZhang Jiejing #include <linux/gpio.h> 2359bae1dbSZhang Jiejing #include <linux/delay.h> 2459bae1dbSZhang Jiejing #include <linux/slab.h> 2559bae1dbSZhang Jiejing #include <linux/bitops.h> 2659bae1dbSZhang Jiejing #include <linux/input/mt.h> 27ae495e84SHui Wang #include <linux/of_gpio.h> 2859bae1dbSZhang Jiejing 2959bae1dbSZhang Jiejing /* 3059bae1dbSZhang Jiejing * Mouse Mode: some panel may configure the controller to mouse mode, 3159bae1dbSZhang Jiejing * which can only report one point at a given time. 3259bae1dbSZhang Jiejing * This driver will ignore events in this mode. 3359bae1dbSZhang Jiejing */ 3459bae1dbSZhang Jiejing #define REPORT_MODE_MOUSE 0x1 3559bae1dbSZhang Jiejing /* 3659bae1dbSZhang Jiejing * Vendor Mode: this mode is used to transfer some vendor specific 3759bae1dbSZhang Jiejing * messages. 3859bae1dbSZhang Jiejing * This driver will ignore events in this mode. 3959bae1dbSZhang Jiejing */ 4059bae1dbSZhang Jiejing #define REPORT_MODE_VENDOR 0x3 4159bae1dbSZhang Jiejing /* Multiple Touch Mode */ 4259bae1dbSZhang Jiejing #define REPORT_MODE_MTTOUCH 0x4 4359bae1dbSZhang Jiejing 4459bae1dbSZhang Jiejing #define MAX_SUPPORT_POINTS 5 4559bae1dbSZhang Jiejing 4659bae1dbSZhang Jiejing #define EVENT_VALID_OFFSET 7 4759bae1dbSZhang Jiejing #define EVENT_VALID_MASK (0x1 << EVENT_VALID_OFFSET) 4859bae1dbSZhang Jiejing #define EVENT_ID_OFFSET 2 4959bae1dbSZhang Jiejing #define EVENT_ID_MASK (0xf << EVENT_ID_OFFSET) 5059bae1dbSZhang Jiejing #define EVENT_IN_RANGE (0x1 << 1) 5159bae1dbSZhang Jiejing #define EVENT_DOWN_UP (0X1 << 0) 5259bae1dbSZhang Jiejing 5359bae1dbSZhang Jiejing #define MAX_I2C_DATA_LEN 10 5459bae1dbSZhang Jiejing 5559bae1dbSZhang Jiejing #define EGALAX_MAX_X 32760 5659bae1dbSZhang Jiejing #define EGALAX_MAX_Y 32760 5759bae1dbSZhang Jiejing #define EGALAX_MAX_TRIES 100 5859bae1dbSZhang Jiejing 5959bae1dbSZhang Jiejing struct egalax_ts { 6059bae1dbSZhang Jiejing struct i2c_client *client; 6159bae1dbSZhang Jiejing struct input_dev *input_dev; 6259bae1dbSZhang Jiejing }; 6359bae1dbSZhang Jiejing 6459bae1dbSZhang Jiejing static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id) 6559bae1dbSZhang Jiejing { 6659bae1dbSZhang Jiejing struct egalax_ts *ts = dev_id; 6759bae1dbSZhang Jiejing struct input_dev *input_dev = ts->input_dev; 6859bae1dbSZhang Jiejing struct i2c_client *client = ts->client; 6959bae1dbSZhang Jiejing u8 buf[MAX_I2C_DATA_LEN]; 7059bae1dbSZhang Jiejing int id, ret, x, y, z; 7159bae1dbSZhang Jiejing int tries = 0; 7259bae1dbSZhang Jiejing bool down, valid; 7359bae1dbSZhang Jiejing u8 state; 7459bae1dbSZhang Jiejing 7559bae1dbSZhang Jiejing do { 7659bae1dbSZhang Jiejing ret = i2c_master_recv(client, buf, MAX_I2C_DATA_LEN); 7759bae1dbSZhang Jiejing } while (ret == -EAGAIN && tries++ < EGALAX_MAX_TRIES); 7859bae1dbSZhang Jiejing 7959bae1dbSZhang Jiejing if (ret < 0) 8059bae1dbSZhang Jiejing return IRQ_HANDLED; 8159bae1dbSZhang Jiejing 8259bae1dbSZhang Jiejing if (buf[0] != REPORT_MODE_MTTOUCH) { 8359bae1dbSZhang Jiejing /* ignore mouse events and vendor events */ 8459bae1dbSZhang Jiejing return IRQ_HANDLED; 8559bae1dbSZhang Jiejing } 8659bae1dbSZhang Jiejing 8759bae1dbSZhang Jiejing state = buf[1]; 8859bae1dbSZhang Jiejing x = (buf[3] << 8) | buf[2]; 8959bae1dbSZhang Jiejing y = (buf[5] << 8) | buf[4]; 9059bae1dbSZhang Jiejing z = (buf[7] << 8) | buf[6]; 9159bae1dbSZhang Jiejing 9259bae1dbSZhang Jiejing valid = state & EVENT_VALID_MASK; 9359bae1dbSZhang Jiejing id = (state & EVENT_ID_MASK) >> EVENT_ID_OFFSET; 9459bae1dbSZhang Jiejing down = state & EVENT_DOWN_UP; 9559bae1dbSZhang Jiejing 9659bae1dbSZhang Jiejing if (!valid || id > MAX_SUPPORT_POINTS) { 9759bae1dbSZhang Jiejing dev_dbg(&client->dev, "point invalid\n"); 9859bae1dbSZhang Jiejing return IRQ_HANDLED; 9959bae1dbSZhang Jiejing } 10059bae1dbSZhang Jiejing 10159bae1dbSZhang Jiejing input_mt_slot(input_dev, id); 10259bae1dbSZhang Jiejing input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, down); 10359bae1dbSZhang Jiejing 10459bae1dbSZhang Jiejing dev_dbg(&client->dev, "%s id:%d x:%d y:%d z:%d", 10559bae1dbSZhang Jiejing down ? "down" : "up", id, x, y, z); 10659bae1dbSZhang Jiejing 10759bae1dbSZhang Jiejing if (down) { 10859bae1dbSZhang Jiejing input_report_abs(input_dev, ABS_MT_POSITION_X, x); 10959bae1dbSZhang Jiejing input_report_abs(input_dev, ABS_MT_POSITION_Y, y); 11059bae1dbSZhang Jiejing input_report_abs(input_dev, ABS_MT_PRESSURE, z); 11159bae1dbSZhang Jiejing } 11259bae1dbSZhang Jiejing 11359bae1dbSZhang Jiejing input_mt_report_pointer_emulation(input_dev, true); 11459bae1dbSZhang Jiejing input_sync(input_dev); 11559bae1dbSZhang Jiejing 11659bae1dbSZhang Jiejing return IRQ_HANDLED; 11759bae1dbSZhang Jiejing } 11859bae1dbSZhang Jiejing 11959bae1dbSZhang Jiejing /* wake up controller by an falling edge of interrupt gpio. */ 12059bae1dbSZhang Jiejing static int egalax_wake_up_device(struct i2c_client *client) 12159bae1dbSZhang Jiejing { 122ae495e84SHui Wang struct device_node *np = client->dev.of_node; 123ae495e84SHui Wang int gpio; 12459bae1dbSZhang Jiejing int ret; 12559bae1dbSZhang Jiejing 126ae495e84SHui Wang if (!np) 127ae495e84SHui Wang return -ENODEV; 128ae495e84SHui Wang 129ae495e84SHui Wang gpio = of_get_named_gpio(np, "wakeup-gpios", 0); 130ae495e84SHui Wang if (!gpio_is_valid(gpio)) 131ae495e84SHui Wang return -ENODEV; 132ae495e84SHui Wang 13359bae1dbSZhang Jiejing ret = gpio_request(gpio, "egalax_irq"); 13459bae1dbSZhang Jiejing if (ret < 0) { 13559bae1dbSZhang Jiejing dev_err(&client->dev, 13659bae1dbSZhang Jiejing "request gpio failed, cannot wake up controller: %d\n", 13759bae1dbSZhang Jiejing ret); 13859bae1dbSZhang Jiejing return ret; 13959bae1dbSZhang Jiejing } 14059bae1dbSZhang Jiejing 14159bae1dbSZhang Jiejing /* wake up controller via an falling edge on IRQ gpio. */ 14259bae1dbSZhang Jiejing gpio_direction_output(gpio, 0); 14359bae1dbSZhang Jiejing gpio_set_value(gpio, 1); 14459bae1dbSZhang Jiejing 14559bae1dbSZhang Jiejing /* controller should be waken up, return irq. */ 14659bae1dbSZhang Jiejing gpio_direction_input(gpio); 14759bae1dbSZhang Jiejing gpio_free(gpio); 14859bae1dbSZhang Jiejing 14959bae1dbSZhang Jiejing return 0; 15059bae1dbSZhang Jiejing } 15159bae1dbSZhang Jiejing 1525298cc4cSBill Pemberton static int egalax_firmware_version(struct i2c_client *client) 15359bae1dbSZhang Jiejing { 15459bae1dbSZhang Jiejing static const u8 cmd[MAX_I2C_DATA_LEN] = { 0x03, 0x03, 0xa, 0x01, 0x41 }; 15559bae1dbSZhang Jiejing int ret; 15659bae1dbSZhang Jiejing 15759bae1dbSZhang Jiejing ret = i2c_master_send(client, cmd, MAX_I2C_DATA_LEN); 15859bae1dbSZhang Jiejing if (ret < 0) 15959bae1dbSZhang Jiejing return ret; 16059bae1dbSZhang Jiejing 16159bae1dbSZhang Jiejing return 0; 16259bae1dbSZhang Jiejing } 16359bae1dbSZhang Jiejing 1645298cc4cSBill Pemberton static int egalax_ts_probe(struct i2c_client *client, 16559bae1dbSZhang Jiejing const struct i2c_device_id *id) 16659bae1dbSZhang Jiejing { 16759bae1dbSZhang Jiejing struct egalax_ts *ts; 16859bae1dbSZhang Jiejing struct input_dev *input_dev; 16959bae1dbSZhang Jiejing int error; 17059bae1dbSZhang Jiejing 1711ffb064eSAndy Shevchenko ts = devm_kzalloc(&client->dev, sizeof(struct egalax_ts), GFP_KERNEL); 17259bae1dbSZhang Jiejing if (!ts) { 17359bae1dbSZhang Jiejing dev_err(&client->dev, "Failed to allocate memory\n"); 17459bae1dbSZhang Jiejing return -ENOMEM; 17559bae1dbSZhang Jiejing } 17659bae1dbSZhang Jiejing 1771ffb064eSAndy Shevchenko input_dev = devm_input_allocate_device(&client->dev); 17859bae1dbSZhang Jiejing if (!input_dev) { 17959bae1dbSZhang Jiejing dev_err(&client->dev, "Failed to allocate memory\n"); 1801ffb064eSAndy Shevchenko return -ENOMEM; 18159bae1dbSZhang Jiejing } 18259bae1dbSZhang Jiejing 18359bae1dbSZhang Jiejing ts->client = client; 18459bae1dbSZhang Jiejing ts->input_dev = input_dev; 18559bae1dbSZhang Jiejing 18659bae1dbSZhang Jiejing /* controller may be in sleep, wake it up. */ 187ae495e84SHui Wang error = egalax_wake_up_device(client); 188ae495e84SHui Wang if (error) { 189ae495e84SHui Wang dev_err(&client->dev, "Failed to wake up the controller\n"); 1901ffb064eSAndy Shevchenko return error; 191ae495e84SHui Wang } 19259bae1dbSZhang Jiejing 1931ffb064eSAndy Shevchenko error = egalax_firmware_version(client); 1941ffb064eSAndy Shevchenko if (error < 0) { 19559bae1dbSZhang Jiejing dev_err(&client->dev, "Failed to read firmware version\n"); 1961ffb064eSAndy Shevchenko return error; 19759bae1dbSZhang Jiejing } 19859bae1dbSZhang Jiejing 19959bae1dbSZhang Jiejing input_dev->name = "EETI eGalax Touch Screen"; 20059bae1dbSZhang Jiejing input_dev->id.bustype = BUS_I2C; 20159bae1dbSZhang Jiejing 20259bae1dbSZhang Jiejing __set_bit(EV_ABS, input_dev->evbit); 20359bae1dbSZhang Jiejing __set_bit(EV_KEY, input_dev->evbit); 20459bae1dbSZhang Jiejing __set_bit(BTN_TOUCH, input_dev->keybit); 20559bae1dbSZhang Jiejing 20659bae1dbSZhang Jiejing input_set_abs_params(input_dev, ABS_X, 0, EGALAX_MAX_X, 0, 0); 20759bae1dbSZhang Jiejing input_set_abs_params(input_dev, ABS_Y, 0, EGALAX_MAX_Y, 0, 0); 20859bae1dbSZhang Jiejing input_set_abs_params(input_dev, 20959bae1dbSZhang Jiejing ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0); 21059bae1dbSZhang Jiejing input_set_abs_params(input_dev, 2113c9cfa78SHeiko Abraham ABS_MT_POSITION_Y, 0, EGALAX_MAX_Y, 0, 0); 212b4adbbefSHenrik Rydberg input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS, 0); 21359bae1dbSZhang Jiejing 2141ffb064eSAndy Shevchenko error = devm_request_threaded_irq(&client->dev, client->irq, NULL, 2151ffb064eSAndy Shevchenko egalax_ts_interrupt, 21659bae1dbSZhang Jiejing IRQF_TRIGGER_LOW | IRQF_ONESHOT, 21759bae1dbSZhang Jiejing "egalax_ts", ts); 21859bae1dbSZhang Jiejing if (error < 0) { 21959bae1dbSZhang Jiejing dev_err(&client->dev, "Failed to register interrupt\n"); 2201ffb064eSAndy Shevchenko return error; 22159bae1dbSZhang Jiejing } 22259bae1dbSZhang Jiejing 22359bae1dbSZhang Jiejing error = input_register_device(ts->input_dev); 22459bae1dbSZhang Jiejing if (error) 2251ffb064eSAndy Shevchenko return error; 22659bae1dbSZhang Jiejing 22759bae1dbSZhang Jiejing return 0; 22859bae1dbSZhang Jiejing } 22959bae1dbSZhang Jiejing 23059bae1dbSZhang Jiejing static const struct i2c_device_id egalax_ts_id[] = { 23159bae1dbSZhang Jiejing { "egalax_ts", 0 }, 23259bae1dbSZhang Jiejing { } 23359bae1dbSZhang Jiejing }; 23459bae1dbSZhang Jiejing MODULE_DEVICE_TABLE(i2c, egalax_ts_id); 23559bae1dbSZhang Jiejing 23602b6a58bSJingoo Han static int __maybe_unused egalax_ts_suspend(struct device *dev) 23759bae1dbSZhang Jiejing { 23859bae1dbSZhang Jiejing static const u8 suspend_cmd[MAX_I2C_DATA_LEN] = { 23959bae1dbSZhang Jiejing 0x3, 0x6, 0xa, 0x3, 0x36, 0x3f, 0x2, 0, 0, 0 24059bae1dbSZhang Jiejing }; 24159bae1dbSZhang Jiejing struct i2c_client *client = to_i2c_client(dev); 24259bae1dbSZhang Jiejing int ret; 24359bae1dbSZhang Jiejing 244*49f62249SAnson Huang if (device_may_wakeup(dev)) 245*49f62249SAnson Huang return enable_irq_wake(client->irq); 246*49f62249SAnson Huang 24759bae1dbSZhang Jiejing ret = i2c_master_send(client, suspend_cmd, MAX_I2C_DATA_LEN); 24859bae1dbSZhang Jiejing return ret > 0 ? 0 : ret; 24959bae1dbSZhang Jiejing } 25059bae1dbSZhang Jiejing 25102b6a58bSJingoo Han static int __maybe_unused egalax_ts_resume(struct device *dev) 25259bae1dbSZhang Jiejing { 25359bae1dbSZhang Jiejing struct i2c_client *client = to_i2c_client(dev); 25459bae1dbSZhang Jiejing 255*49f62249SAnson Huang if (device_may_wakeup(dev)) 256*49f62249SAnson Huang return disable_irq_wake(client->irq); 257*49f62249SAnson Huang 25859bae1dbSZhang Jiejing return egalax_wake_up_device(client); 25959bae1dbSZhang Jiejing } 26059bae1dbSZhang Jiejing 26159bae1dbSZhang Jiejing static SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume); 26259bae1dbSZhang Jiejing 263a5fd844cSJingoo Han static const struct of_device_id egalax_ts_dt_ids[] = { 264ae495e84SHui Wang { .compatible = "eeti,egalax_ts" }, 265ae495e84SHui Wang { /* sentinel */ } 266ae495e84SHui Wang }; 26702d9bd05SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, egalax_ts_dt_ids); 268ae495e84SHui Wang 26959bae1dbSZhang Jiejing static struct i2c_driver egalax_ts_driver = { 27059bae1dbSZhang Jiejing .driver = { 27159bae1dbSZhang Jiejing .name = "egalax_ts", 27259bae1dbSZhang Jiejing .pm = &egalax_ts_pm_ops, 273085f17c6SSachin Kamat .of_match_table = egalax_ts_dt_ids, 27459bae1dbSZhang Jiejing }, 27559bae1dbSZhang Jiejing .id_table = egalax_ts_id, 27659bae1dbSZhang Jiejing .probe = egalax_ts_probe, 27759bae1dbSZhang Jiejing }; 27859bae1dbSZhang Jiejing 2791b92c1cfSAxel Lin module_i2c_driver(egalax_ts_driver); 28059bae1dbSZhang Jiejing 28159bae1dbSZhang Jiejing MODULE_AUTHOR("Freescale Semiconductor, Inc."); 28259bae1dbSZhang Jiejing MODULE_DESCRIPTION("Touchscreen driver for EETI eGalax touch controller"); 28359bae1dbSZhang Jiejing MODULE_LICENSE("GPL"); 284