18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for EETI eGalax Multiple Touch Controller
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Freescale Semiconductor, Inc.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * based on max11801_ts.c
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci/* EETI eGalax serial touch screen controller is a I2C based multiple
118c2ecf20Sopenharmony_ci * touch screen controller, it supports 5 point multiple touch. */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/* TODO:
148c2ecf20Sopenharmony_ci  - auto idle mode support
158c2ecf20Sopenharmony_ci*/
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/i2c.h>
198c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
208c2ecf20Sopenharmony_ci#include <linux/input.h>
218c2ecf20Sopenharmony_ci#include <linux/irq.h>
228c2ecf20Sopenharmony_ci#include <linux/gpio.h>
238c2ecf20Sopenharmony_ci#include <linux/delay.h>
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci#include <linux/bitops.h>
268c2ecf20Sopenharmony_ci#include <linux/input/mt.h>
278c2ecf20Sopenharmony_ci#include <linux/of_gpio.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/*
308c2ecf20Sopenharmony_ci * Mouse Mode: some panel may configure the controller to mouse mode,
318c2ecf20Sopenharmony_ci * which can only report one point at a given time.
328c2ecf20Sopenharmony_ci * This driver will ignore events in this mode.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci#define REPORT_MODE_MOUSE		0x1
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci * Vendor Mode: this mode is used to transfer some vendor specific
378c2ecf20Sopenharmony_ci * messages.
388c2ecf20Sopenharmony_ci * This driver will ignore events in this mode.
398c2ecf20Sopenharmony_ci */
408c2ecf20Sopenharmony_ci#define REPORT_MODE_VENDOR		0x3
418c2ecf20Sopenharmony_ci/* Multiple Touch Mode */
428c2ecf20Sopenharmony_ci#define REPORT_MODE_MTTOUCH		0x4
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define MAX_SUPPORT_POINTS		5
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define EVENT_VALID_OFFSET	7
478c2ecf20Sopenharmony_ci#define EVENT_VALID_MASK	(0x1 << EVENT_VALID_OFFSET)
488c2ecf20Sopenharmony_ci#define EVENT_ID_OFFSET		2
498c2ecf20Sopenharmony_ci#define EVENT_ID_MASK		(0xf << EVENT_ID_OFFSET)
508c2ecf20Sopenharmony_ci#define EVENT_IN_RANGE		(0x1 << 1)
518c2ecf20Sopenharmony_ci#define EVENT_DOWN_UP		(0X1 << 0)
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define MAX_I2C_DATA_LEN	10
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define EGALAX_MAX_X	32760
568c2ecf20Sopenharmony_ci#define EGALAX_MAX_Y	32760
578c2ecf20Sopenharmony_ci#define EGALAX_MAX_TRIES 100
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistruct egalax_ts {
608c2ecf20Sopenharmony_ci	struct i2c_client		*client;
618c2ecf20Sopenharmony_ci	struct input_dev		*input_dev;
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic irqreturn_t egalax_ts_interrupt(int irq, void *dev_id)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	struct egalax_ts *ts = dev_id;
678c2ecf20Sopenharmony_ci	struct input_dev *input_dev = ts->input_dev;
688c2ecf20Sopenharmony_ci	struct i2c_client *client = ts->client;
698c2ecf20Sopenharmony_ci	u8 buf[MAX_I2C_DATA_LEN];
708c2ecf20Sopenharmony_ci	int id, ret, x, y, z;
718c2ecf20Sopenharmony_ci	int tries = 0;
728c2ecf20Sopenharmony_ci	bool down, valid;
738c2ecf20Sopenharmony_ci	u8 state;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	do {
768c2ecf20Sopenharmony_ci		ret = i2c_master_recv(client, buf, MAX_I2C_DATA_LEN);
778c2ecf20Sopenharmony_ci	} while (ret == -EAGAIN && tries++ < EGALAX_MAX_TRIES);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (ret < 0)
808c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	if (buf[0] != REPORT_MODE_MTTOUCH) {
838c2ecf20Sopenharmony_ci		/* ignore mouse events and vendor events */
848c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	state = buf[1];
888c2ecf20Sopenharmony_ci	x = (buf[3] << 8) | buf[2];
898c2ecf20Sopenharmony_ci	y = (buf[5] << 8) | buf[4];
908c2ecf20Sopenharmony_ci	z = (buf[7] << 8) | buf[6];
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	valid = state & EVENT_VALID_MASK;
938c2ecf20Sopenharmony_ci	id = (state & EVENT_ID_MASK) >> EVENT_ID_OFFSET;
948c2ecf20Sopenharmony_ci	down = state & EVENT_DOWN_UP;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (!valid || id > MAX_SUPPORT_POINTS) {
978c2ecf20Sopenharmony_ci		dev_dbg(&client->dev, "point invalid\n");
988c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	input_mt_slot(input_dev, id);
1028c2ecf20Sopenharmony_ci	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, down);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	dev_dbg(&client->dev, "%s id:%d x:%d y:%d z:%d",
1058c2ecf20Sopenharmony_ci		down ? "down" : "up", id, x, y, z);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (down) {
1088c2ecf20Sopenharmony_ci		input_report_abs(input_dev, ABS_MT_POSITION_X, x);
1098c2ecf20Sopenharmony_ci		input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
1108c2ecf20Sopenharmony_ci		input_report_abs(input_dev, ABS_MT_PRESSURE, z);
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	input_mt_report_pointer_emulation(input_dev, true);
1148c2ecf20Sopenharmony_ci	input_sync(input_dev);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/* wake up controller by an falling edge of interrupt gpio.  */
1208c2ecf20Sopenharmony_cistatic int egalax_wake_up_device(struct i2c_client *client)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	struct device_node *np = client->dev.of_node;
1238c2ecf20Sopenharmony_ci	int gpio;
1248c2ecf20Sopenharmony_ci	int ret;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (!np)
1278c2ecf20Sopenharmony_ci		return -ENODEV;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	gpio = of_get_named_gpio(np, "wakeup-gpios", 0);
1308c2ecf20Sopenharmony_ci	if (!gpio_is_valid(gpio))
1318c2ecf20Sopenharmony_ci		return -ENODEV;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	ret = gpio_request(gpio, "egalax_irq");
1348c2ecf20Sopenharmony_ci	if (ret < 0) {
1358c2ecf20Sopenharmony_ci		dev_err(&client->dev,
1368c2ecf20Sopenharmony_ci			"request gpio failed, cannot wake up controller: %d\n",
1378c2ecf20Sopenharmony_ci			ret);
1388c2ecf20Sopenharmony_ci		return ret;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* wake up controller via an falling edge on IRQ gpio. */
1428c2ecf20Sopenharmony_ci	gpio_direction_output(gpio, 0);
1438c2ecf20Sopenharmony_ci	gpio_set_value(gpio, 1);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	/* controller should be waken up, return irq.  */
1468c2ecf20Sopenharmony_ci	gpio_direction_input(gpio);
1478c2ecf20Sopenharmony_ci	gpio_free(gpio);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	return 0;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic int egalax_firmware_version(struct i2c_client *client)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	static const u8 cmd[MAX_I2C_DATA_LEN] = { 0x03, 0x03, 0xa, 0x01, 0x41 };
1558c2ecf20Sopenharmony_ci	int ret;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	ret = i2c_master_send(client, cmd, MAX_I2C_DATA_LEN);
1588c2ecf20Sopenharmony_ci	if (ret < 0)
1598c2ecf20Sopenharmony_ci		return ret;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	return 0;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic int egalax_ts_probe(struct i2c_client *client,
1658c2ecf20Sopenharmony_ci			   const struct i2c_device_id *id)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct egalax_ts *ts;
1688c2ecf20Sopenharmony_ci	struct input_dev *input_dev;
1698c2ecf20Sopenharmony_ci	int error;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	ts = devm_kzalloc(&client->dev, sizeof(struct egalax_ts), GFP_KERNEL);
1728c2ecf20Sopenharmony_ci	if (!ts) {
1738c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Failed to allocate memory\n");
1748c2ecf20Sopenharmony_ci		return -ENOMEM;
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	input_dev = devm_input_allocate_device(&client->dev);
1788c2ecf20Sopenharmony_ci	if (!input_dev) {
1798c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Failed to allocate memory\n");
1808c2ecf20Sopenharmony_ci		return -ENOMEM;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	ts->client = client;
1848c2ecf20Sopenharmony_ci	ts->input_dev = input_dev;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* controller may be in sleep, wake it up. */
1878c2ecf20Sopenharmony_ci	error = egalax_wake_up_device(client);
1888c2ecf20Sopenharmony_ci	if (error) {
1898c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Failed to wake up the controller\n");
1908c2ecf20Sopenharmony_ci		return error;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	error = egalax_firmware_version(client);
1948c2ecf20Sopenharmony_ci	if (error < 0) {
1958c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Failed to read firmware version\n");
1968c2ecf20Sopenharmony_ci		return error;
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	input_dev->name = "EETI eGalax Touch Screen";
2008c2ecf20Sopenharmony_ci	input_dev->id.bustype = BUS_I2C;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	__set_bit(EV_ABS, input_dev->evbit);
2038c2ecf20Sopenharmony_ci	__set_bit(EV_KEY, input_dev->evbit);
2048c2ecf20Sopenharmony_ci	__set_bit(BTN_TOUCH, input_dev->keybit);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev, ABS_X, 0, EGALAX_MAX_X, 0, 0);
2078c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev, ABS_Y, 0, EGALAX_MAX_Y, 0, 0);
2088c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev,
2098c2ecf20Sopenharmony_ci			     ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0);
2108c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev,
2118c2ecf20Sopenharmony_ci			     ABS_MT_POSITION_Y, 0, EGALAX_MAX_Y, 0, 0);
2128c2ecf20Sopenharmony_ci	input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS, 0);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
2158c2ecf20Sopenharmony_ci					  egalax_ts_interrupt,
2168c2ecf20Sopenharmony_ci					  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
2178c2ecf20Sopenharmony_ci					  "egalax_ts", ts);
2188c2ecf20Sopenharmony_ci	if (error < 0) {
2198c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Failed to register interrupt\n");
2208c2ecf20Sopenharmony_ci		return error;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	error = input_register_device(ts->input_dev);
2248c2ecf20Sopenharmony_ci	if (error)
2258c2ecf20Sopenharmony_ci		return error;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	return 0;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic const struct i2c_device_id egalax_ts_id[] = {
2318c2ecf20Sopenharmony_ci	{ "egalax_ts", 0 },
2328c2ecf20Sopenharmony_ci	{ }
2338c2ecf20Sopenharmony_ci};
2348c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, egalax_ts_id);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic int __maybe_unused egalax_ts_suspend(struct device *dev)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	static const u8 suspend_cmd[MAX_I2C_DATA_LEN] = {
2398c2ecf20Sopenharmony_ci		0x3, 0x6, 0xa, 0x3, 0x36, 0x3f, 0x2, 0, 0, 0
2408c2ecf20Sopenharmony_ci	};
2418c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
2428c2ecf20Sopenharmony_ci	int ret;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (device_may_wakeup(dev))
2458c2ecf20Sopenharmony_ci		return enable_irq_wake(client->irq);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	ret = i2c_master_send(client, suspend_cmd, MAX_I2C_DATA_LEN);
2488c2ecf20Sopenharmony_ci	return ret > 0 ? 0 : ret;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic int __maybe_unused egalax_ts_resume(struct device *dev)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (device_may_wakeup(dev))
2568c2ecf20Sopenharmony_ci		return disable_irq_wake(client->irq);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	return egalax_wake_up_device(client);
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic const struct of_device_id egalax_ts_dt_ids[] = {
2648c2ecf20Sopenharmony_ci	{ .compatible = "eeti,egalax_ts" },
2658c2ecf20Sopenharmony_ci	{ /* sentinel */ }
2668c2ecf20Sopenharmony_ci};
2678c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, egalax_ts_dt_ids);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic struct i2c_driver egalax_ts_driver = {
2708c2ecf20Sopenharmony_ci	.driver = {
2718c2ecf20Sopenharmony_ci		.name	= "egalax_ts",
2728c2ecf20Sopenharmony_ci		.pm	= &egalax_ts_pm_ops,
2738c2ecf20Sopenharmony_ci		.of_match_table	= egalax_ts_dt_ids,
2748c2ecf20Sopenharmony_ci	},
2758c2ecf20Sopenharmony_ci	.id_table	= egalax_ts_id,
2768c2ecf20Sopenharmony_ci	.probe		= egalax_ts_probe,
2778c2ecf20Sopenharmony_ci};
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cimodule_i2c_driver(egalax_ts_driver);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor, Inc.");
2828c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Touchscreen driver for EETI eGalax touch controller");
2838c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
284