18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci#include <linux/delay.h>
38c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
48c2ecf20Sopenharmony_ci#include <linux/i2c.h>
58c2ecf20Sopenharmony_ci#include <linux/input.h>
68c2ecf20Sopenharmony_ci#include <linux/input/mt.h>
78c2ecf20Sopenharmony_ci#include <linux/input/touchscreen.h>
88c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/of_device.h>
118c2ecf20Sopenharmony_ci#include <linux/sizes.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define ILI2XXX_POLL_PERIOD	20
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define ILI210X_DATA_SIZE	64
188c2ecf20Sopenharmony_ci#define ILI211X_DATA_SIZE	43
198c2ecf20Sopenharmony_ci#define ILI251X_DATA_SIZE1	31
208c2ecf20Sopenharmony_ci#define ILI251X_DATA_SIZE2	20
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* Touchscreen commands */
238c2ecf20Sopenharmony_ci#define REG_TOUCHDATA		0x10
248c2ecf20Sopenharmony_ci#define REG_PANEL_INFO		0x20
258c2ecf20Sopenharmony_ci#define REG_CALIBRATE		0xcc
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct ili2xxx_chip {
288c2ecf20Sopenharmony_ci	int (*read_reg)(struct i2c_client *client, u8 reg,
298c2ecf20Sopenharmony_ci			void *buf, size_t len);
308c2ecf20Sopenharmony_ci	int (*get_touch_data)(struct i2c_client *client, u8 *data);
318c2ecf20Sopenharmony_ci	bool (*parse_touch_data)(const u8 *data, unsigned int finger,
328c2ecf20Sopenharmony_ci				 unsigned int *x, unsigned int *y,
338c2ecf20Sopenharmony_ci				 unsigned int *z);
348c2ecf20Sopenharmony_ci	bool (*continue_polling)(const u8 *data, bool touch);
358c2ecf20Sopenharmony_ci	unsigned int max_touches;
368c2ecf20Sopenharmony_ci	unsigned int resolution;
378c2ecf20Sopenharmony_ci	bool has_calibrate_reg;
388c2ecf20Sopenharmony_ci	bool has_pressure_reg;
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistruct ili210x {
428c2ecf20Sopenharmony_ci	struct i2c_client *client;
438c2ecf20Sopenharmony_ci	struct input_dev *input;
448c2ecf20Sopenharmony_ci	struct gpio_desc *reset_gpio;
458c2ecf20Sopenharmony_ci	struct touchscreen_properties prop;
468c2ecf20Sopenharmony_ci	const struct ili2xxx_chip *chip;
478c2ecf20Sopenharmony_ci	bool stop;
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int ili210x_read_reg(struct i2c_client *client,
518c2ecf20Sopenharmony_ci			    u8 reg, void *buf, size_t len)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	struct i2c_msg msg[] = {
548c2ecf20Sopenharmony_ci		{
558c2ecf20Sopenharmony_ci			.addr	= client->addr,
568c2ecf20Sopenharmony_ci			.flags	= 0,
578c2ecf20Sopenharmony_ci			.len	= 1,
588c2ecf20Sopenharmony_ci			.buf	= &reg,
598c2ecf20Sopenharmony_ci		},
608c2ecf20Sopenharmony_ci		{
618c2ecf20Sopenharmony_ci			.addr	= client->addr,
628c2ecf20Sopenharmony_ci			.flags	= I2C_M_RD,
638c2ecf20Sopenharmony_ci			.len	= len,
648c2ecf20Sopenharmony_ci			.buf	= buf,
658c2ecf20Sopenharmony_ci		}
668c2ecf20Sopenharmony_ci	};
678c2ecf20Sopenharmony_ci	int error, ret;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
708c2ecf20Sopenharmony_ci	if (ret != ARRAY_SIZE(msg)) {
718c2ecf20Sopenharmony_ci		error = ret < 0 ? ret : -EIO;
728c2ecf20Sopenharmony_ci		dev_err(&client->dev, "%s failed: %d\n", __func__, error);
738c2ecf20Sopenharmony_ci		return error;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	return 0;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic int ili210x_read_touch_data(struct i2c_client *client, u8 *data)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	return ili210x_read_reg(client, REG_TOUCHDATA,
828c2ecf20Sopenharmony_ci				data, ILI210X_DATA_SIZE);
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic bool ili210x_touchdata_to_coords(const u8 *touchdata,
868c2ecf20Sopenharmony_ci					unsigned int finger,
878c2ecf20Sopenharmony_ci					unsigned int *x, unsigned int *y,
888c2ecf20Sopenharmony_ci					unsigned int *z)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	if (!(touchdata[0] & BIT(finger)))
918c2ecf20Sopenharmony_ci		return false;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	*x = get_unaligned_be16(touchdata + 1 + (finger * 4) + 0);
948c2ecf20Sopenharmony_ci	*y = get_unaligned_be16(touchdata + 1 + (finger * 4) + 2);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return true;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic bool ili210x_check_continue_polling(const u8 *data, bool touch)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	return data[0] & 0xf3;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic const struct ili2xxx_chip ili210x_chip = {
1058c2ecf20Sopenharmony_ci	.read_reg		= ili210x_read_reg,
1068c2ecf20Sopenharmony_ci	.get_touch_data		= ili210x_read_touch_data,
1078c2ecf20Sopenharmony_ci	.parse_touch_data	= ili210x_touchdata_to_coords,
1088c2ecf20Sopenharmony_ci	.continue_polling	= ili210x_check_continue_polling,
1098c2ecf20Sopenharmony_ci	.max_touches		= 2,
1108c2ecf20Sopenharmony_ci	.has_calibrate_reg	= true,
1118c2ecf20Sopenharmony_ci};
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic int ili211x_read_touch_data(struct i2c_client *client, u8 *data)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	s16 sum = 0;
1168c2ecf20Sopenharmony_ci	int error;
1178c2ecf20Sopenharmony_ci	int ret;
1188c2ecf20Sopenharmony_ci	int i;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	ret = i2c_master_recv(client, data, ILI211X_DATA_SIZE);
1218c2ecf20Sopenharmony_ci	if (ret != ILI211X_DATA_SIZE) {
1228c2ecf20Sopenharmony_ci		error = ret < 0 ? ret : -EIO;
1238c2ecf20Sopenharmony_ci		dev_err(&client->dev, "%s failed: %d\n", __func__, error);
1248c2ecf20Sopenharmony_ci		return error;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* This chip uses custom checksum at the end of data */
1288c2ecf20Sopenharmony_ci	for (i = 0; i < ILI211X_DATA_SIZE - 1; i++)
1298c2ecf20Sopenharmony_ci		sum = (sum + data[i]) & 0xff;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if ((-sum & 0xff) != data[ILI211X_DATA_SIZE - 1]) {
1328c2ecf20Sopenharmony_ci		dev_err(&client->dev,
1338c2ecf20Sopenharmony_ci			"CRC error (crc=0x%02x expected=0x%02x)\n",
1348c2ecf20Sopenharmony_ci			sum, data[ILI211X_DATA_SIZE - 1]);
1358c2ecf20Sopenharmony_ci		return -EIO;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	return 0;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic bool ili211x_touchdata_to_coords(const u8 *touchdata,
1428c2ecf20Sopenharmony_ci					unsigned int finger,
1438c2ecf20Sopenharmony_ci					unsigned int *x, unsigned int *y,
1448c2ecf20Sopenharmony_ci					unsigned int *z)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	u32 data;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	data = get_unaligned_be32(touchdata + 1 + (finger * 4) + 0);
1498c2ecf20Sopenharmony_ci	if (data == 0xffffffff)	/* Finger up */
1508c2ecf20Sopenharmony_ci		return false;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	*x = ((touchdata[1 + (finger * 4) + 0] & 0xf0) << 4) |
1538c2ecf20Sopenharmony_ci	     touchdata[1 + (finger * 4) + 1];
1548c2ecf20Sopenharmony_ci	*y = ((touchdata[1 + (finger * 4) + 0] & 0x0f) << 8) |
1558c2ecf20Sopenharmony_ci	     touchdata[1 + (finger * 4) + 2];
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return true;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic bool ili211x_decline_polling(const u8 *data, bool touch)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	return false;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic const struct ili2xxx_chip ili211x_chip = {
1668c2ecf20Sopenharmony_ci	.read_reg		= ili210x_read_reg,
1678c2ecf20Sopenharmony_ci	.get_touch_data		= ili211x_read_touch_data,
1688c2ecf20Sopenharmony_ci	.parse_touch_data	= ili211x_touchdata_to_coords,
1698c2ecf20Sopenharmony_ci	.continue_polling	= ili211x_decline_polling,
1708c2ecf20Sopenharmony_ci	.max_touches		= 10,
1718c2ecf20Sopenharmony_ci	.resolution		= 2048,
1728c2ecf20Sopenharmony_ci};
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic bool ili212x_touchdata_to_coords(const u8 *touchdata,
1758c2ecf20Sopenharmony_ci					unsigned int finger,
1768c2ecf20Sopenharmony_ci					unsigned int *x, unsigned int *y,
1778c2ecf20Sopenharmony_ci					unsigned int *z)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	u16 val;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	val = get_unaligned_be16(touchdata + 3 + (finger * 5) + 0);
1828c2ecf20Sopenharmony_ci	if (!(val & BIT(15)))	/* Touch indication */
1838c2ecf20Sopenharmony_ci		return false;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	*x = val & 0x3fff;
1868c2ecf20Sopenharmony_ci	*y = get_unaligned_be16(touchdata + 3 + (finger * 5) + 2);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	return true;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic bool ili212x_check_continue_polling(const u8 *data, bool touch)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	return touch;
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic const struct ili2xxx_chip ili212x_chip = {
1978c2ecf20Sopenharmony_ci	.read_reg		= ili210x_read_reg,
1988c2ecf20Sopenharmony_ci	.get_touch_data		= ili210x_read_touch_data,
1998c2ecf20Sopenharmony_ci	.parse_touch_data	= ili212x_touchdata_to_coords,
2008c2ecf20Sopenharmony_ci	.continue_polling	= ili212x_check_continue_polling,
2018c2ecf20Sopenharmony_ci	.max_touches		= 10,
2028c2ecf20Sopenharmony_ci	.has_calibrate_reg	= true,
2038c2ecf20Sopenharmony_ci};
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic int ili251x_read_reg(struct i2c_client *client,
2068c2ecf20Sopenharmony_ci			    u8 reg, void *buf, size_t len)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	int error;
2098c2ecf20Sopenharmony_ci	int ret;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	ret = i2c_master_send(client, &reg, 1);
2128c2ecf20Sopenharmony_ci	if (ret == 1) {
2138c2ecf20Sopenharmony_ci		usleep_range(5000, 5500);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci		ret = i2c_master_recv(client, buf, len);
2168c2ecf20Sopenharmony_ci		if (ret == len)
2178c2ecf20Sopenharmony_ci			return 0;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	error = ret < 0 ? ret : -EIO;
2218c2ecf20Sopenharmony_ci	dev_err(&client->dev, "%s failed: %d\n", __func__, error);
2228c2ecf20Sopenharmony_ci	return ret;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic int ili251x_read_touch_data(struct i2c_client *client, u8 *data)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	int error;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	error = ili251x_read_reg(client, REG_TOUCHDATA,
2308c2ecf20Sopenharmony_ci				 data, ILI251X_DATA_SIZE1);
2318c2ecf20Sopenharmony_ci	if (!error && data[0] == 2) {
2328c2ecf20Sopenharmony_ci		error = i2c_master_recv(client, data + ILI251X_DATA_SIZE1,
2338c2ecf20Sopenharmony_ci					ILI251X_DATA_SIZE2);
2348c2ecf20Sopenharmony_ci		if (error >= 0 && error != ILI251X_DATA_SIZE2)
2358c2ecf20Sopenharmony_ci			error = -EIO;
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	return error;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic bool ili251x_touchdata_to_coords(const u8 *touchdata,
2428c2ecf20Sopenharmony_ci					unsigned int finger,
2438c2ecf20Sopenharmony_ci					unsigned int *x, unsigned int *y,
2448c2ecf20Sopenharmony_ci					unsigned int *z)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	u16 val;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	val = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0);
2498c2ecf20Sopenharmony_ci	if (!(val & BIT(15)))	/* Touch indication */
2508c2ecf20Sopenharmony_ci		return false;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	*x = val & 0x3fff;
2538c2ecf20Sopenharmony_ci	*y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2);
2548c2ecf20Sopenharmony_ci	*z = touchdata[1 + (finger * 5) + 4];
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	return true;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic bool ili251x_check_continue_polling(const u8 *data, bool touch)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	return touch;
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic const struct ili2xxx_chip ili251x_chip = {
2658c2ecf20Sopenharmony_ci	.read_reg		= ili251x_read_reg,
2668c2ecf20Sopenharmony_ci	.get_touch_data		= ili251x_read_touch_data,
2678c2ecf20Sopenharmony_ci	.parse_touch_data	= ili251x_touchdata_to_coords,
2688c2ecf20Sopenharmony_ci	.continue_polling	= ili251x_check_continue_polling,
2698c2ecf20Sopenharmony_ci	.max_touches		= 10,
2708c2ecf20Sopenharmony_ci	.has_calibrate_reg	= true,
2718c2ecf20Sopenharmony_ci	.has_pressure_reg	= true,
2728c2ecf20Sopenharmony_ci};
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic bool ili210x_report_events(struct ili210x *priv, u8 *touchdata)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	struct input_dev *input = priv->input;
2778c2ecf20Sopenharmony_ci	int i;
2788c2ecf20Sopenharmony_ci	bool contact = false, touch;
2798c2ecf20Sopenharmony_ci	unsigned int x = 0, y = 0, z = 0;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	for (i = 0; i < priv->chip->max_touches; i++) {
2828c2ecf20Sopenharmony_ci		touch = priv->chip->parse_touch_data(touchdata, i, &x, &y, &z);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci		input_mt_slot(input, i);
2858c2ecf20Sopenharmony_ci		if (input_mt_report_slot_state(input, MT_TOOL_FINGER, touch)) {
2868c2ecf20Sopenharmony_ci			touchscreen_report_pos(input, &priv->prop, x, y, true);
2878c2ecf20Sopenharmony_ci			if (priv->chip->has_pressure_reg)
2888c2ecf20Sopenharmony_ci				input_report_abs(input, ABS_MT_PRESSURE, z);
2898c2ecf20Sopenharmony_ci			contact = true;
2908c2ecf20Sopenharmony_ci		}
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	input_mt_report_pointer_emulation(input, false);
2948c2ecf20Sopenharmony_ci	input_sync(input);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	return contact;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic irqreturn_t ili210x_irq(int irq, void *irq_data)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct ili210x *priv = irq_data;
3028c2ecf20Sopenharmony_ci	struct i2c_client *client = priv->client;
3038c2ecf20Sopenharmony_ci	const struct ili2xxx_chip *chip = priv->chip;
3048c2ecf20Sopenharmony_ci	u8 touchdata[ILI210X_DATA_SIZE] = { 0 };
3058c2ecf20Sopenharmony_ci	bool keep_polling;
3068c2ecf20Sopenharmony_ci	bool touch;
3078c2ecf20Sopenharmony_ci	int error;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	do {
3108c2ecf20Sopenharmony_ci		error = chip->get_touch_data(client, touchdata);
3118c2ecf20Sopenharmony_ci		if (error) {
3128c2ecf20Sopenharmony_ci			dev_err(&client->dev,
3138c2ecf20Sopenharmony_ci				"Unable to get touch data: %d\n", error);
3148c2ecf20Sopenharmony_ci			break;
3158c2ecf20Sopenharmony_ci		}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci		touch = ili210x_report_events(priv, touchdata);
3188c2ecf20Sopenharmony_ci		keep_polling = chip->continue_polling(touchdata, touch);
3198c2ecf20Sopenharmony_ci		if (keep_polling)
3208c2ecf20Sopenharmony_ci			msleep(ILI2XXX_POLL_PERIOD);
3218c2ecf20Sopenharmony_ci	} while (!priv->stop && keep_polling);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic ssize_t ili210x_calibrate(struct device *dev,
3278c2ecf20Sopenharmony_ci				 struct device_attribute *attr,
3288c2ecf20Sopenharmony_ci				 const char *buf, size_t count)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
3318c2ecf20Sopenharmony_ci	struct ili210x *priv = i2c_get_clientdata(client);
3328c2ecf20Sopenharmony_ci	unsigned long calibrate;
3338c2ecf20Sopenharmony_ci	int rc;
3348c2ecf20Sopenharmony_ci	u8 cmd = REG_CALIBRATE;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (kstrtoul(buf, 10, &calibrate))
3378c2ecf20Sopenharmony_ci		return -EINVAL;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (calibrate > 1)
3408c2ecf20Sopenharmony_ci		return -EINVAL;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	if (calibrate) {
3438c2ecf20Sopenharmony_ci		rc = i2c_master_send(priv->client, &cmd, sizeof(cmd));
3448c2ecf20Sopenharmony_ci		if (rc != sizeof(cmd))
3458c2ecf20Sopenharmony_ci			return -EIO;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	return count;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_cistatic DEVICE_ATTR(calibrate, S_IWUSR, NULL, ili210x_calibrate);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic struct attribute *ili210x_attributes[] = {
3538c2ecf20Sopenharmony_ci	&dev_attr_calibrate.attr,
3548c2ecf20Sopenharmony_ci	NULL,
3558c2ecf20Sopenharmony_ci};
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic umode_t ili210x_calibrate_visible(struct kobject *kobj,
3588c2ecf20Sopenharmony_ci					  struct attribute *attr, int index)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
3618c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
3628c2ecf20Sopenharmony_ci	struct ili210x *priv = i2c_get_clientdata(client);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	return priv->chip->has_calibrate_reg ? attr->mode : 0;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic const struct attribute_group ili210x_attr_group = {
3688c2ecf20Sopenharmony_ci	.attrs = ili210x_attributes,
3698c2ecf20Sopenharmony_ci	.is_visible = ili210x_calibrate_visible,
3708c2ecf20Sopenharmony_ci};
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic void ili210x_power_down(void *data)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	struct gpio_desc *reset_gpio = data;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(reset_gpio, 1);
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic void ili210x_stop(void *data)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	struct ili210x *priv = data;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/* Tell ISR to quit even if there is a contact. */
3848c2ecf20Sopenharmony_ci	priv->stop = true;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic int ili210x_i2c_probe(struct i2c_client *client,
3888c2ecf20Sopenharmony_ci			     const struct i2c_device_id *id)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	struct device *dev = &client->dev;
3918c2ecf20Sopenharmony_ci	const struct ili2xxx_chip *chip;
3928c2ecf20Sopenharmony_ci	struct ili210x *priv;
3938c2ecf20Sopenharmony_ci	struct gpio_desc *reset_gpio;
3948c2ecf20Sopenharmony_ci	struct input_dev *input;
3958c2ecf20Sopenharmony_ci	int error;
3968c2ecf20Sopenharmony_ci	unsigned int max_xy;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	chip = device_get_match_data(dev);
4018c2ecf20Sopenharmony_ci	if (!chip && id)
4028c2ecf20Sopenharmony_ci		chip = (const struct ili2xxx_chip *)id->driver_data;
4038c2ecf20Sopenharmony_ci	if (!chip) {
4048c2ecf20Sopenharmony_ci		dev_err(&client->dev, "unknown device model\n");
4058c2ecf20Sopenharmony_ci		return -ENODEV;
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	if (client->irq <= 0) {
4098c2ecf20Sopenharmony_ci		dev_err(dev, "No IRQ!\n");
4108c2ecf20Sopenharmony_ci		return -EINVAL;
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
4148c2ecf20Sopenharmony_ci	if (IS_ERR(reset_gpio))
4158c2ecf20Sopenharmony_ci		return PTR_ERR(reset_gpio);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	if (reset_gpio) {
4188c2ecf20Sopenharmony_ci		error = devm_add_action_or_reset(dev, ili210x_power_down,
4198c2ecf20Sopenharmony_ci						 reset_gpio);
4208c2ecf20Sopenharmony_ci		if (error)
4218c2ecf20Sopenharmony_ci			return error;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci		usleep_range(12000, 15000);
4248c2ecf20Sopenharmony_ci		gpiod_set_value_cansleep(reset_gpio, 0);
4258c2ecf20Sopenharmony_ci		msleep(160);
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
4298c2ecf20Sopenharmony_ci	if (!priv)
4308c2ecf20Sopenharmony_ci		return -ENOMEM;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	input = devm_input_allocate_device(dev);
4338c2ecf20Sopenharmony_ci	if (!input)
4348c2ecf20Sopenharmony_ci		return -ENOMEM;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	priv->client = client;
4378c2ecf20Sopenharmony_ci	priv->input = input;
4388c2ecf20Sopenharmony_ci	priv->reset_gpio = reset_gpio;
4398c2ecf20Sopenharmony_ci	priv->chip = chip;
4408c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, priv);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	/* Setup input device */
4438c2ecf20Sopenharmony_ci	input->name = "ILI210x Touchscreen";
4448c2ecf20Sopenharmony_ci	input->id.bustype = BUS_I2C;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	/* Multi touch */
4478c2ecf20Sopenharmony_ci	max_xy = (chip->resolution ?: SZ_64K) - 1;
4488c2ecf20Sopenharmony_ci	input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_xy, 0, 0);
4498c2ecf20Sopenharmony_ci	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_xy, 0, 0);
4508c2ecf20Sopenharmony_ci	if (priv->chip->has_pressure_reg)
4518c2ecf20Sopenharmony_ci		input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xa, 0, 0);
4528c2ecf20Sopenharmony_ci	touchscreen_parse_properties(input, true, &priv->prop);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	error = input_mt_init_slots(input, priv->chip->max_touches,
4558c2ecf20Sopenharmony_ci				    INPUT_MT_DIRECT);
4568c2ecf20Sopenharmony_ci	if (error) {
4578c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to set up slots, err: %d\n", error);
4588c2ecf20Sopenharmony_ci		return error;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq,
4628c2ecf20Sopenharmony_ci					  IRQF_ONESHOT, client->name, priv);
4638c2ecf20Sopenharmony_ci	if (error) {
4648c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n",
4658c2ecf20Sopenharmony_ci			error);
4668c2ecf20Sopenharmony_ci		return error;
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	error = devm_add_action_or_reset(dev, ili210x_stop, priv);
4708c2ecf20Sopenharmony_ci	if (error)
4718c2ecf20Sopenharmony_ci		return error;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	error = devm_device_add_group(dev, &ili210x_attr_group);
4748c2ecf20Sopenharmony_ci	if (error) {
4758c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to create sysfs attributes, err: %d\n",
4768c2ecf20Sopenharmony_ci			error);
4778c2ecf20Sopenharmony_ci		return error;
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	error = input_register_device(priv->input);
4818c2ecf20Sopenharmony_ci	if (error) {
4828c2ecf20Sopenharmony_ci		dev_err(dev, "Cannot register input device, err: %d\n", error);
4838c2ecf20Sopenharmony_ci		return error;
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	return 0;
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_cistatic const struct i2c_device_id ili210x_i2c_id[] = {
4908c2ecf20Sopenharmony_ci	{ "ili210x", (long)&ili210x_chip },
4918c2ecf20Sopenharmony_ci	{ "ili2117", (long)&ili211x_chip },
4928c2ecf20Sopenharmony_ci	{ "ili2120", (long)&ili212x_chip },
4938c2ecf20Sopenharmony_ci	{ "ili251x", (long)&ili251x_chip },
4948c2ecf20Sopenharmony_ci	{ }
4958c2ecf20Sopenharmony_ci};
4968c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ili210x_i2c_id);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic const struct of_device_id ili210x_dt_ids[] = {
4998c2ecf20Sopenharmony_ci	{ .compatible = "ilitek,ili210x", .data = &ili210x_chip },
5008c2ecf20Sopenharmony_ci	{ .compatible = "ilitek,ili2117", .data = &ili211x_chip },
5018c2ecf20Sopenharmony_ci	{ .compatible = "ilitek,ili2120", .data = &ili212x_chip },
5028c2ecf20Sopenharmony_ci	{ .compatible = "ilitek,ili251x", .data = &ili251x_chip },
5038c2ecf20Sopenharmony_ci	{ }
5048c2ecf20Sopenharmony_ci};
5058c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ili210x_dt_ids);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic struct i2c_driver ili210x_ts_driver = {
5088c2ecf20Sopenharmony_ci	.driver = {
5098c2ecf20Sopenharmony_ci		.name = "ili210x_i2c",
5108c2ecf20Sopenharmony_ci		.of_match_table = ili210x_dt_ids,
5118c2ecf20Sopenharmony_ci	},
5128c2ecf20Sopenharmony_ci	.id_table = ili210x_i2c_id,
5138c2ecf20Sopenharmony_ci	.probe = ili210x_i2c_probe,
5148c2ecf20Sopenharmony_ci};
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cimodule_i2c_driver(ili210x_ts_driver);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>");
5198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver");
5208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
521