xref: /linux/drivers/input/touchscreen/egalax_ts.c (revision 4116941b7a703f8c770998bb3a59966608cb5bb2)
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