18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de>
48c2ecf20Sopenharmony_ci * Daniel Wagener <daniel.wagener@kernelconcepts.de> (M09 firmware support)
58c2ecf20Sopenharmony_ci * Lothar Waßmann <LW@KARO-electronics.de> (DT support)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci * This is a driver for the EDT "Polytouch" family of touch controllers
108c2ecf20Sopenharmony_ci * based on the FocalTech FT5x06 line of chips.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Development of this driver has been sponsored by Glyn:
138c2ecf20Sopenharmony_ci *    http://www.glyn.com/Products/Displays
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
178c2ecf20Sopenharmony_ci#include <linux/delay.h>
188c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
198c2ecf20Sopenharmony_ci#include <linux/i2c.h>
208c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
218c2ecf20Sopenharmony_ci#include <linux/input.h>
228c2ecf20Sopenharmony_ci#include <linux/input/mt.h>
238c2ecf20Sopenharmony_ci#include <linux/input/touchscreen.h>
248c2ecf20Sopenharmony_ci#include <linux/irq.h>
258c2ecf20Sopenharmony_ci#include <linux/kernel.h>
268c2ecf20Sopenharmony_ci#include <linux/module.h>
278c2ecf20Sopenharmony_ci#include <linux/ratelimit.h>
288c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
298c2ecf20Sopenharmony_ci#include <linux/slab.h>
308c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define WORK_REGISTER_THRESHOLD		0x00
358c2ecf20Sopenharmony_ci#define WORK_REGISTER_REPORT_RATE	0x08
368c2ecf20Sopenharmony_ci#define WORK_REGISTER_GAIN		0x30
378c2ecf20Sopenharmony_ci#define WORK_REGISTER_OFFSET		0x31
388c2ecf20Sopenharmony_ci#define WORK_REGISTER_NUM_X		0x33
398c2ecf20Sopenharmony_ci#define WORK_REGISTER_NUM_Y		0x34
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define PMOD_REGISTER_ACTIVE		0x00
428c2ecf20Sopenharmony_ci#define PMOD_REGISTER_HIBERNATE		0x03
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define M09_REGISTER_THRESHOLD		0x80
458c2ecf20Sopenharmony_ci#define M09_REGISTER_GAIN		0x92
468c2ecf20Sopenharmony_ci#define M09_REGISTER_OFFSET		0x93
478c2ecf20Sopenharmony_ci#define M09_REGISTER_NUM_X		0x94
488c2ecf20Sopenharmony_ci#define M09_REGISTER_NUM_Y		0x95
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define EV_REGISTER_THRESHOLD		0x40
518c2ecf20Sopenharmony_ci#define EV_REGISTER_GAIN		0x41
528c2ecf20Sopenharmony_ci#define EV_REGISTER_OFFSET_Y		0x45
538c2ecf20Sopenharmony_ci#define EV_REGISTER_OFFSET_X		0x46
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define NO_REGISTER			0xff
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define WORK_REGISTER_OPMODE		0x3c
588c2ecf20Sopenharmony_ci#define FACTORY_REGISTER_OPMODE		0x01
598c2ecf20Sopenharmony_ci#define PMOD_REGISTER_OPMODE		0xa5
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#define TOUCH_EVENT_DOWN		0x00
628c2ecf20Sopenharmony_ci#define TOUCH_EVENT_UP			0x01
638c2ecf20Sopenharmony_ci#define TOUCH_EVENT_ON			0x02
648c2ecf20Sopenharmony_ci#define TOUCH_EVENT_RESERVED		0x03
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci#define EDT_NAME_LEN			23
678c2ecf20Sopenharmony_ci#define EDT_SWITCH_MODE_RETRIES		10
688c2ecf20Sopenharmony_ci#define EDT_SWITCH_MODE_DELAY		5 /* msec */
698c2ecf20Sopenharmony_ci#define EDT_RAW_DATA_RETRIES		100
708c2ecf20Sopenharmony_ci#define EDT_RAW_DATA_DELAY		1000 /* usec */
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cienum edt_pmode {
738c2ecf20Sopenharmony_ci	EDT_PMODE_NOT_SUPPORTED,
748c2ecf20Sopenharmony_ci	EDT_PMODE_HIBERNATE,
758c2ecf20Sopenharmony_ci	EDT_PMODE_POWEROFF,
768c2ecf20Sopenharmony_ci};
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cienum edt_ver {
798c2ecf20Sopenharmony_ci	EDT_M06,
808c2ecf20Sopenharmony_ci	EDT_M09,
818c2ecf20Sopenharmony_ci	EDT_M12,
828c2ecf20Sopenharmony_ci	EV_FT,
838c2ecf20Sopenharmony_ci	GENERIC_FT,
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistruct edt_reg_addr {
878c2ecf20Sopenharmony_ci	int reg_threshold;
888c2ecf20Sopenharmony_ci	int reg_report_rate;
898c2ecf20Sopenharmony_ci	int reg_gain;
908c2ecf20Sopenharmony_ci	int reg_offset;
918c2ecf20Sopenharmony_ci	int reg_offset_x;
928c2ecf20Sopenharmony_ci	int reg_offset_y;
938c2ecf20Sopenharmony_ci	int reg_num_x;
948c2ecf20Sopenharmony_ci	int reg_num_y;
958c2ecf20Sopenharmony_ci};
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistruct edt_ft5x06_ts_data {
988c2ecf20Sopenharmony_ci	struct i2c_client *client;
998c2ecf20Sopenharmony_ci	struct input_dev *input;
1008c2ecf20Sopenharmony_ci	struct touchscreen_properties prop;
1018c2ecf20Sopenharmony_ci	u16 num_x;
1028c2ecf20Sopenharmony_ci	u16 num_y;
1038c2ecf20Sopenharmony_ci	struct regulator *vcc;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	struct gpio_desc *reset_gpio;
1068c2ecf20Sopenharmony_ci	struct gpio_desc *wake_gpio;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci#if defined(CONFIG_DEBUG_FS)
1098c2ecf20Sopenharmony_ci	struct dentry *debug_dir;
1108c2ecf20Sopenharmony_ci	u8 *raw_buffer;
1118c2ecf20Sopenharmony_ci	size_t raw_bufsize;
1128c2ecf20Sopenharmony_ci#endif
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	struct mutex mutex;
1158c2ecf20Sopenharmony_ci	bool factory_mode;
1168c2ecf20Sopenharmony_ci	enum edt_pmode suspend_mode;
1178c2ecf20Sopenharmony_ci	int threshold;
1188c2ecf20Sopenharmony_ci	int gain;
1198c2ecf20Sopenharmony_ci	int offset;
1208c2ecf20Sopenharmony_ci	int offset_x;
1218c2ecf20Sopenharmony_ci	int offset_y;
1228c2ecf20Sopenharmony_ci	int report_rate;
1238c2ecf20Sopenharmony_ci	int max_support_points;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	char name[EDT_NAME_LEN];
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	struct edt_reg_addr reg_addr;
1288c2ecf20Sopenharmony_ci	enum edt_ver version;
1298c2ecf20Sopenharmony_ci};
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistruct edt_i2c_chip_data {
1328c2ecf20Sopenharmony_ci	int  max_support_points;
1338c2ecf20Sopenharmony_ci};
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic int edt_ft5x06_ts_readwrite(struct i2c_client *client,
1368c2ecf20Sopenharmony_ci				   u16 wr_len, u8 *wr_buf,
1378c2ecf20Sopenharmony_ci				   u16 rd_len, u8 *rd_buf)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct i2c_msg wrmsg[2];
1408c2ecf20Sopenharmony_ci	int i = 0;
1418c2ecf20Sopenharmony_ci	int ret;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (wr_len) {
1448c2ecf20Sopenharmony_ci		wrmsg[i].addr  = client->addr;
1458c2ecf20Sopenharmony_ci		wrmsg[i].flags = 0;
1468c2ecf20Sopenharmony_ci		wrmsg[i].len = wr_len;
1478c2ecf20Sopenharmony_ci		wrmsg[i].buf = wr_buf;
1488c2ecf20Sopenharmony_ci		i++;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci	if (rd_len) {
1518c2ecf20Sopenharmony_ci		wrmsg[i].addr  = client->addr;
1528c2ecf20Sopenharmony_ci		wrmsg[i].flags = I2C_M_RD;
1538c2ecf20Sopenharmony_ci		wrmsg[i].len = rd_len;
1548c2ecf20Sopenharmony_ci		wrmsg[i].buf = rd_buf;
1558c2ecf20Sopenharmony_ci		i++;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	ret = i2c_transfer(client->adapter, wrmsg, i);
1598c2ecf20Sopenharmony_ci	if (ret < 0)
1608c2ecf20Sopenharmony_ci		return ret;
1618c2ecf20Sopenharmony_ci	if (ret != i)
1628c2ecf20Sopenharmony_ci		return -EIO;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return 0;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata,
1688c2ecf20Sopenharmony_ci				    u8 *buf, int buflen)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	int i;
1718c2ecf20Sopenharmony_ci	u8 crc = 0;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	for (i = 0; i < buflen - 1; i++)
1748c2ecf20Sopenharmony_ci		crc ^= buf[i];
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (crc != buf[buflen-1]) {
1778c2ecf20Sopenharmony_ci		dev_err_ratelimited(&tsdata->client->dev,
1788c2ecf20Sopenharmony_ci				    "crc error: 0x%02x expected, got 0x%02x\n",
1798c2ecf20Sopenharmony_ci				    crc, buf[buflen-1]);
1808c2ecf20Sopenharmony_ci		return false;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	return true;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct edt_ft5x06_ts_data *tsdata = dev_id;
1898c2ecf20Sopenharmony_ci	struct device *dev = &tsdata->client->dev;
1908c2ecf20Sopenharmony_ci	u8 cmd;
1918c2ecf20Sopenharmony_ci	u8 rdbuf[63];
1928c2ecf20Sopenharmony_ci	int i, type, x, y, id;
1938c2ecf20Sopenharmony_ci	int offset, tplen, datalen, crclen;
1948c2ecf20Sopenharmony_ci	int error;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	switch (tsdata->version) {
1978c2ecf20Sopenharmony_ci	case EDT_M06:
1988c2ecf20Sopenharmony_ci		cmd = 0xf9; /* tell the controller to send touch data */
1998c2ecf20Sopenharmony_ci		offset = 5; /* where the actual touch data starts */
2008c2ecf20Sopenharmony_ci		tplen = 4;  /* data comes in so called frames */
2018c2ecf20Sopenharmony_ci		crclen = 1; /* length of the crc data */
2028c2ecf20Sopenharmony_ci		break;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	case EDT_M09:
2058c2ecf20Sopenharmony_ci	case EDT_M12:
2068c2ecf20Sopenharmony_ci	case EV_FT:
2078c2ecf20Sopenharmony_ci	case GENERIC_FT:
2088c2ecf20Sopenharmony_ci		cmd = 0x0;
2098c2ecf20Sopenharmony_ci		offset = 3;
2108c2ecf20Sopenharmony_ci		tplen = 6;
2118c2ecf20Sopenharmony_ci		crclen = 0;
2128c2ecf20Sopenharmony_ci		break;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	default:
2158c2ecf20Sopenharmony_ci		goto out;
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	memset(rdbuf, 0, sizeof(rdbuf));
2198c2ecf20Sopenharmony_ci	datalen = tplen * tsdata->max_support_points + offset + crclen;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	error = edt_ft5x06_ts_readwrite(tsdata->client,
2228c2ecf20Sopenharmony_ci					sizeof(cmd), &cmd,
2238c2ecf20Sopenharmony_ci					datalen, rdbuf);
2248c2ecf20Sopenharmony_ci	if (error) {
2258c2ecf20Sopenharmony_ci		dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n",
2268c2ecf20Sopenharmony_ci				    error);
2278c2ecf20Sopenharmony_ci		goto out;
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	/* M09/M12 does not send header or CRC */
2318c2ecf20Sopenharmony_ci	if (tsdata->version == EDT_M06) {
2328c2ecf20Sopenharmony_ci		if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa ||
2338c2ecf20Sopenharmony_ci			rdbuf[2] != datalen) {
2348c2ecf20Sopenharmony_ci			dev_err_ratelimited(dev,
2358c2ecf20Sopenharmony_ci					"Unexpected header: %02x%02x%02x!\n",
2368c2ecf20Sopenharmony_ci					rdbuf[0], rdbuf[1], rdbuf[2]);
2378c2ecf20Sopenharmony_ci			goto out;
2388c2ecf20Sopenharmony_ci		}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci		if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, datalen))
2418c2ecf20Sopenharmony_ci			goto out;
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	for (i = 0; i < tsdata->max_support_points; i++) {
2458c2ecf20Sopenharmony_ci		u8 *buf = &rdbuf[i * tplen + offset];
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		type = buf[0] >> 6;
2488c2ecf20Sopenharmony_ci		/* ignore Reserved events */
2498c2ecf20Sopenharmony_ci		if (type == TOUCH_EVENT_RESERVED)
2508c2ecf20Sopenharmony_ci			continue;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci		/* M06 sometimes sends bogus coordinates in TOUCH_DOWN */
2538c2ecf20Sopenharmony_ci		if (tsdata->version == EDT_M06 && type == TOUCH_EVENT_DOWN)
2548c2ecf20Sopenharmony_ci			continue;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		x = get_unaligned_be16(buf) & 0x0fff;
2578c2ecf20Sopenharmony_ci		y = get_unaligned_be16(buf + 2) & 0x0fff;
2588c2ecf20Sopenharmony_ci		/* The FT5x26 send the y coordinate first */
2598c2ecf20Sopenharmony_ci		if (tsdata->version == EV_FT)
2608c2ecf20Sopenharmony_ci			swap(x, y);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		id = (buf[2] >> 4) & 0x0f;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci		input_mt_slot(tsdata->input, id);
2658c2ecf20Sopenharmony_ci		if (input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER,
2668c2ecf20Sopenharmony_ci					       type != TOUCH_EVENT_UP))
2678c2ecf20Sopenharmony_ci			touchscreen_report_pos(tsdata->input, &tsdata->prop,
2688c2ecf20Sopenharmony_ci					       x, y, true);
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	input_mt_report_pointer_emulation(tsdata->input, true);
2728c2ecf20Sopenharmony_ci	input_sync(tsdata->input);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ciout:
2758c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata,
2798c2ecf20Sopenharmony_ci				     u8 addr, u8 value)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	u8 wrbuf[4];
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	switch (tsdata->version) {
2848c2ecf20Sopenharmony_ci	case EDT_M06:
2858c2ecf20Sopenharmony_ci		wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
2868c2ecf20Sopenharmony_ci		wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
2878c2ecf20Sopenharmony_ci		wrbuf[2] = value;
2888c2ecf20Sopenharmony_ci		wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
2898c2ecf20Sopenharmony_ci		return edt_ft5x06_ts_readwrite(tsdata->client, 4,
2908c2ecf20Sopenharmony_ci					wrbuf, 0, NULL);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	case EDT_M09:
2938c2ecf20Sopenharmony_ci	case EDT_M12:
2948c2ecf20Sopenharmony_ci	case EV_FT:
2958c2ecf20Sopenharmony_ci	case GENERIC_FT:
2968c2ecf20Sopenharmony_ci		wrbuf[0] = addr;
2978c2ecf20Sopenharmony_ci		wrbuf[1] = value;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci		return edt_ft5x06_ts_readwrite(tsdata->client, 2,
3008c2ecf20Sopenharmony_ci					wrbuf, 0, NULL);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	default:
3038c2ecf20Sopenharmony_ci		return -EINVAL;
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata,
3088c2ecf20Sopenharmony_ci				    u8 addr)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	u8 wrbuf[2], rdbuf[2];
3118c2ecf20Sopenharmony_ci	int error;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	switch (tsdata->version) {
3148c2ecf20Sopenharmony_ci	case EDT_M06:
3158c2ecf20Sopenharmony_ci		wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
3168c2ecf20Sopenharmony_ci		wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
3178c2ecf20Sopenharmony_ci		wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci		error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2,
3208c2ecf20Sopenharmony_ci						rdbuf);
3218c2ecf20Sopenharmony_ci		if (error)
3228c2ecf20Sopenharmony_ci			return error;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci		if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) {
3258c2ecf20Sopenharmony_ci			dev_err(&tsdata->client->dev,
3268c2ecf20Sopenharmony_ci				"crc error: 0x%02x expected, got 0x%02x\n",
3278c2ecf20Sopenharmony_ci				wrbuf[0] ^ wrbuf[1] ^ rdbuf[0],
3288c2ecf20Sopenharmony_ci				rdbuf[1]);
3298c2ecf20Sopenharmony_ci			return -EIO;
3308c2ecf20Sopenharmony_ci		}
3318c2ecf20Sopenharmony_ci		break;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	case EDT_M09:
3348c2ecf20Sopenharmony_ci	case EDT_M12:
3358c2ecf20Sopenharmony_ci	case EV_FT:
3368c2ecf20Sopenharmony_ci	case GENERIC_FT:
3378c2ecf20Sopenharmony_ci		wrbuf[0] = addr;
3388c2ecf20Sopenharmony_ci		error = edt_ft5x06_ts_readwrite(tsdata->client, 1,
3398c2ecf20Sopenharmony_ci						wrbuf, 1, rdbuf);
3408c2ecf20Sopenharmony_ci		if (error)
3418c2ecf20Sopenharmony_ci			return error;
3428c2ecf20Sopenharmony_ci		break;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	default:
3458c2ecf20Sopenharmony_ci		return -EINVAL;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	return rdbuf[0];
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistruct edt_ft5x06_attribute {
3528c2ecf20Sopenharmony_ci	struct device_attribute dattr;
3538c2ecf20Sopenharmony_ci	size_t field_offset;
3548c2ecf20Sopenharmony_ci	u8 limit_low;
3558c2ecf20Sopenharmony_ci	u8 limit_high;
3568c2ecf20Sopenharmony_ci	u8 addr_m06;
3578c2ecf20Sopenharmony_ci	u8 addr_m09;
3588c2ecf20Sopenharmony_ci	u8 addr_ev;
3598c2ecf20Sopenharmony_ci};
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, _addr_ev,		\
3628c2ecf20Sopenharmony_ci		_limit_low, _limit_high)				\
3638c2ecf20Sopenharmony_ci	struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = {	\
3648c2ecf20Sopenharmony_ci		.dattr = __ATTR(_field, _mode,				\
3658c2ecf20Sopenharmony_ci				edt_ft5x06_setting_show,		\
3668c2ecf20Sopenharmony_ci				edt_ft5x06_setting_store),		\
3678c2ecf20Sopenharmony_ci		.field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \
3688c2ecf20Sopenharmony_ci		.addr_m06 = _addr_m06,					\
3698c2ecf20Sopenharmony_ci		.addr_m09 = _addr_m09,					\
3708c2ecf20Sopenharmony_ci		.addr_ev  = _addr_ev,					\
3718c2ecf20Sopenharmony_ci		.limit_low = _limit_low,				\
3728c2ecf20Sopenharmony_ci		.limit_high = _limit_high,				\
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic ssize_t edt_ft5x06_setting_show(struct device *dev,
3768c2ecf20Sopenharmony_ci				       struct device_attribute *dattr,
3778c2ecf20Sopenharmony_ci				       char *buf)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
3808c2ecf20Sopenharmony_ci	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
3818c2ecf20Sopenharmony_ci	struct edt_ft5x06_attribute *attr =
3828c2ecf20Sopenharmony_ci			container_of(dattr, struct edt_ft5x06_attribute, dattr);
3838c2ecf20Sopenharmony_ci	u8 *field = (u8 *)tsdata + attr->field_offset;
3848c2ecf20Sopenharmony_ci	int val;
3858c2ecf20Sopenharmony_ci	size_t count = 0;
3868c2ecf20Sopenharmony_ci	int error = 0;
3878c2ecf20Sopenharmony_ci	u8 addr;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	mutex_lock(&tsdata->mutex);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if (tsdata->factory_mode) {
3928c2ecf20Sopenharmony_ci		error = -EIO;
3938c2ecf20Sopenharmony_ci		goto out;
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	switch (tsdata->version) {
3978c2ecf20Sopenharmony_ci	case EDT_M06:
3988c2ecf20Sopenharmony_ci		addr = attr->addr_m06;
3998c2ecf20Sopenharmony_ci		break;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	case EDT_M09:
4028c2ecf20Sopenharmony_ci	case EDT_M12:
4038c2ecf20Sopenharmony_ci	case GENERIC_FT:
4048c2ecf20Sopenharmony_ci		addr = attr->addr_m09;
4058c2ecf20Sopenharmony_ci		break;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	case EV_FT:
4088c2ecf20Sopenharmony_ci		addr = attr->addr_ev;
4098c2ecf20Sopenharmony_ci		break;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	default:
4128c2ecf20Sopenharmony_ci		error = -ENODEV;
4138c2ecf20Sopenharmony_ci		goto out;
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	if (addr != NO_REGISTER) {
4178c2ecf20Sopenharmony_ci		val = edt_ft5x06_register_read(tsdata, addr);
4188c2ecf20Sopenharmony_ci		if (val < 0) {
4198c2ecf20Sopenharmony_ci			error = val;
4208c2ecf20Sopenharmony_ci			dev_err(&tsdata->client->dev,
4218c2ecf20Sopenharmony_ci				"Failed to fetch attribute %s, error %d\n",
4228c2ecf20Sopenharmony_ci				dattr->attr.name, error);
4238c2ecf20Sopenharmony_ci			goto out;
4248c2ecf20Sopenharmony_ci		}
4258c2ecf20Sopenharmony_ci	} else {
4268c2ecf20Sopenharmony_ci		val = *field;
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	if (val != *field) {
4308c2ecf20Sopenharmony_ci		dev_warn(&tsdata->client->dev,
4318c2ecf20Sopenharmony_ci			 "%s: read (%d) and stored value (%d) differ\n",
4328c2ecf20Sopenharmony_ci			 dattr->attr.name, val, *field);
4338c2ecf20Sopenharmony_ci		*field = val;
4348c2ecf20Sopenharmony_ci	}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	count = scnprintf(buf, PAGE_SIZE, "%d\n", val);
4378c2ecf20Sopenharmony_ciout:
4388c2ecf20Sopenharmony_ci	mutex_unlock(&tsdata->mutex);
4398c2ecf20Sopenharmony_ci	return error ?: count;
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic ssize_t edt_ft5x06_setting_store(struct device *dev,
4438c2ecf20Sopenharmony_ci					struct device_attribute *dattr,
4448c2ecf20Sopenharmony_ci					const char *buf, size_t count)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
4478c2ecf20Sopenharmony_ci	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
4488c2ecf20Sopenharmony_ci	struct edt_ft5x06_attribute *attr =
4498c2ecf20Sopenharmony_ci			container_of(dattr, struct edt_ft5x06_attribute, dattr);
4508c2ecf20Sopenharmony_ci	u8 *field = (u8 *)tsdata + attr->field_offset;
4518c2ecf20Sopenharmony_ci	unsigned int val;
4528c2ecf20Sopenharmony_ci	int error;
4538c2ecf20Sopenharmony_ci	u8 addr;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	mutex_lock(&tsdata->mutex);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (tsdata->factory_mode) {
4588c2ecf20Sopenharmony_ci		error = -EIO;
4598c2ecf20Sopenharmony_ci		goto out;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	error = kstrtouint(buf, 0, &val);
4638c2ecf20Sopenharmony_ci	if (error)
4648c2ecf20Sopenharmony_ci		goto out;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	if (val < attr->limit_low || val > attr->limit_high) {
4678c2ecf20Sopenharmony_ci		error = -ERANGE;
4688c2ecf20Sopenharmony_ci		goto out;
4698c2ecf20Sopenharmony_ci	}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	switch (tsdata->version) {
4728c2ecf20Sopenharmony_ci	case EDT_M06:
4738c2ecf20Sopenharmony_ci		addr = attr->addr_m06;
4748c2ecf20Sopenharmony_ci		break;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	case EDT_M09:
4778c2ecf20Sopenharmony_ci	case EDT_M12:
4788c2ecf20Sopenharmony_ci	case GENERIC_FT:
4798c2ecf20Sopenharmony_ci		addr = attr->addr_m09;
4808c2ecf20Sopenharmony_ci		break;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	case EV_FT:
4838c2ecf20Sopenharmony_ci		addr = attr->addr_ev;
4848c2ecf20Sopenharmony_ci		break;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	default:
4878c2ecf20Sopenharmony_ci		error = -ENODEV;
4888c2ecf20Sopenharmony_ci		goto out;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (addr != NO_REGISTER) {
4928c2ecf20Sopenharmony_ci		error = edt_ft5x06_register_write(tsdata, addr, val);
4938c2ecf20Sopenharmony_ci		if (error) {
4948c2ecf20Sopenharmony_ci			dev_err(&tsdata->client->dev,
4958c2ecf20Sopenharmony_ci				"Failed to update attribute %s, error: %d\n",
4968c2ecf20Sopenharmony_ci				dattr->attr.name, error);
4978c2ecf20Sopenharmony_ci			goto out;
4988c2ecf20Sopenharmony_ci		}
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci	*field = val;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ciout:
5038c2ecf20Sopenharmony_ci	mutex_unlock(&tsdata->mutex);
5048c2ecf20Sopenharmony_ci	return error ?: count;
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci/* m06, m09: range 0-31, m12: range 0-5 */
5088c2ecf20Sopenharmony_cistatic EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN,
5098c2ecf20Sopenharmony_ci		M09_REGISTER_GAIN, EV_REGISTER_GAIN, 0, 31);
5108c2ecf20Sopenharmony_ci/* m06, m09: range 0-31, m12: range 0-16 */
5118c2ecf20Sopenharmony_cistatic EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET,
5128c2ecf20Sopenharmony_ci		M09_REGISTER_OFFSET, NO_REGISTER, 0, 31);
5138c2ecf20Sopenharmony_ci/* m06, m09, m12: no supported, ev_ft: range 0-80 */
5148c2ecf20Sopenharmony_cistatic EDT_ATTR(offset_x, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER,
5158c2ecf20Sopenharmony_ci		EV_REGISTER_OFFSET_X, 0, 80);
5168c2ecf20Sopenharmony_ci/* m06, m09, m12: no supported, ev_ft: range 0-80 */
5178c2ecf20Sopenharmony_cistatic EDT_ATTR(offset_y, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER,
5188c2ecf20Sopenharmony_ci		EV_REGISTER_OFFSET_Y, 0, 80);
5198c2ecf20Sopenharmony_ci/* m06: range 20 to 80, m09: range 0 to 30, m12: range 1 to 255... */
5208c2ecf20Sopenharmony_cistatic EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD,
5218c2ecf20Sopenharmony_ci		M09_REGISTER_THRESHOLD, EV_REGISTER_THRESHOLD, 0, 255);
5228c2ecf20Sopenharmony_ci/* m06: range 3 to 14, m12: (0x64: 100Hz) */
5238c2ecf20Sopenharmony_cistatic EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE,
5248c2ecf20Sopenharmony_ci		NO_REGISTER, NO_REGISTER, 0, 255);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_cistatic struct attribute *edt_ft5x06_attrs[] = {
5278c2ecf20Sopenharmony_ci	&edt_ft5x06_attr_gain.dattr.attr,
5288c2ecf20Sopenharmony_ci	&edt_ft5x06_attr_offset.dattr.attr,
5298c2ecf20Sopenharmony_ci	&edt_ft5x06_attr_offset_x.dattr.attr,
5308c2ecf20Sopenharmony_ci	&edt_ft5x06_attr_offset_y.dattr.attr,
5318c2ecf20Sopenharmony_ci	&edt_ft5x06_attr_threshold.dattr.attr,
5328c2ecf20Sopenharmony_ci	&edt_ft5x06_attr_report_rate.dattr.attr,
5338c2ecf20Sopenharmony_ci	NULL
5348c2ecf20Sopenharmony_ci};
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic const struct attribute_group edt_ft5x06_attr_group = {
5378c2ecf20Sopenharmony_ci	.attrs = edt_ft5x06_attrs,
5388c2ecf20Sopenharmony_ci};
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_cistatic void edt_ft5x06_restore_reg_parameters(struct edt_ft5x06_ts_data *tsdata)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold,
5458c2ecf20Sopenharmony_ci				  tsdata->threshold);
5468c2ecf20Sopenharmony_ci	edt_ft5x06_register_write(tsdata, reg_addr->reg_gain,
5478c2ecf20Sopenharmony_ci				  tsdata->gain);
5488c2ecf20Sopenharmony_ci	if (reg_addr->reg_offset != NO_REGISTER)
5498c2ecf20Sopenharmony_ci		edt_ft5x06_register_write(tsdata, reg_addr->reg_offset,
5508c2ecf20Sopenharmony_ci					  tsdata->offset);
5518c2ecf20Sopenharmony_ci	if (reg_addr->reg_offset_x != NO_REGISTER)
5528c2ecf20Sopenharmony_ci		edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x,
5538c2ecf20Sopenharmony_ci					  tsdata->offset_x);
5548c2ecf20Sopenharmony_ci	if (reg_addr->reg_offset_y != NO_REGISTER)
5558c2ecf20Sopenharmony_ci		edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y,
5568c2ecf20Sopenharmony_ci					  tsdata->offset_y);
5578c2ecf20Sopenharmony_ci	if (reg_addr->reg_report_rate != NO_REGISTER)
5588c2ecf20Sopenharmony_ci		edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate,
5598c2ecf20Sopenharmony_ci				  tsdata->report_rate);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
5648c2ecf20Sopenharmony_cistatic int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
5658c2ecf20Sopenharmony_ci{
5668c2ecf20Sopenharmony_ci	struct i2c_client *client = tsdata->client;
5678c2ecf20Sopenharmony_ci	int retries = EDT_SWITCH_MODE_RETRIES;
5688c2ecf20Sopenharmony_ci	int ret;
5698c2ecf20Sopenharmony_ci	int error;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	if (tsdata->version != EDT_M06) {
5728c2ecf20Sopenharmony_ci		dev_err(&client->dev,
5738c2ecf20Sopenharmony_ci			"No factory mode support for non-M06 devices\n");
5748c2ecf20Sopenharmony_ci		return -EINVAL;
5758c2ecf20Sopenharmony_ci	}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	disable_irq(client->irq);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	if (!tsdata->raw_buffer) {
5808c2ecf20Sopenharmony_ci		tsdata->raw_bufsize = tsdata->num_x * tsdata->num_y *
5818c2ecf20Sopenharmony_ci				      sizeof(u16);
5828c2ecf20Sopenharmony_ci		tsdata->raw_buffer = kzalloc(tsdata->raw_bufsize, GFP_KERNEL);
5838c2ecf20Sopenharmony_ci		if (!tsdata->raw_buffer) {
5848c2ecf20Sopenharmony_ci			error = -ENOMEM;
5858c2ecf20Sopenharmony_ci			goto err_out;
5868c2ecf20Sopenharmony_ci		}
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	/* mode register is 0x3c when in the work mode */
5908c2ecf20Sopenharmony_ci	error = edt_ft5x06_register_write(tsdata, WORK_REGISTER_OPMODE, 0x03);
5918c2ecf20Sopenharmony_ci	if (error) {
5928c2ecf20Sopenharmony_ci		dev_err(&client->dev,
5938c2ecf20Sopenharmony_ci			"failed to switch to factory mode, error %d\n", error);
5948c2ecf20Sopenharmony_ci		goto err_out;
5958c2ecf20Sopenharmony_ci	}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	tsdata->factory_mode = true;
5988c2ecf20Sopenharmony_ci	do {
5998c2ecf20Sopenharmony_ci		mdelay(EDT_SWITCH_MODE_DELAY);
6008c2ecf20Sopenharmony_ci		/* mode register is 0x01 when in factory mode */
6018c2ecf20Sopenharmony_ci		ret = edt_ft5x06_register_read(tsdata, FACTORY_REGISTER_OPMODE);
6028c2ecf20Sopenharmony_ci		if (ret == 0x03)
6038c2ecf20Sopenharmony_ci			break;
6048c2ecf20Sopenharmony_ci	} while (--retries > 0);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	if (retries == 0) {
6078c2ecf20Sopenharmony_ci		dev_err(&client->dev, "not in factory mode after %dms.\n",
6088c2ecf20Sopenharmony_ci			EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY);
6098c2ecf20Sopenharmony_ci		error = -EIO;
6108c2ecf20Sopenharmony_ci		goto err_out;
6118c2ecf20Sopenharmony_ci	}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	return 0;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_cierr_out:
6168c2ecf20Sopenharmony_ci	kfree(tsdata->raw_buffer);
6178c2ecf20Sopenharmony_ci	tsdata->raw_buffer = NULL;
6188c2ecf20Sopenharmony_ci	tsdata->factory_mode = false;
6198c2ecf20Sopenharmony_ci	enable_irq(client->irq);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	return error;
6228c2ecf20Sopenharmony_ci}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_cistatic int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
6258c2ecf20Sopenharmony_ci{
6268c2ecf20Sopenharmony_ci	struct i2c_client *client = tsdata->client;
6278c2ecf20Sopenharmony_ci	int retries = EDT_SWITCH_MODE_RETRIES;
6288c2ecf20Sopenharmony_ci	int ret;
6298c2ecf20Sopenharmony_ci	int error;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	/* mode register is 0x01 when in the factory mode */
6328c2ecf20Sopenharmony_ci	error = edt_ft5x06_register_write(tsdata, FACTORY_REGISTER_OPMODE, 0x1);
6338c2ecf20Sopenharmony_ci	if (error) {
6348c2ecf20Sopenharmony_ci		dev_err(&client->dev,
6358c2ecf20Sopenharmony_ci			"failed to switch to work mode, error: %d\n", error);
6368c2ecf20Sopenharmony_ci		return error;
6378c2ecf20Sopenharmony_ci	}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	tsdata->factory_mode = false;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	do {
6428c2ecf20Sopenharmony_ci		mdelay(EDT_SWITCH_MODE_DELAY);
6438c2ecf20Sopenharmony_ci		/* mode register is 0x01 when in factory mode */
6448c2ecf20Sopenharmony_ci		ret = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OPMODE);
6458c2ecf20Sopenharmony_ci		if (ret == 0x01)
6468c2ecf20Sopenharmony_ci			break;
6478c2ecf20Sopenharmony_ci	} while (--retries > 0);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	if (retries == 0) {
6508c2ecf20Sopenharmony_ci		dev_err(&client->dev, "not in work mode after %dms.\n",
6518c2ecf20Sopenharmony_ci			EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY);
6528c2ecf20Sopenharmony_ci		tsdata->factory_mode = true;
6538c2ecf20Sopenharmony_ci		return -EIO;
6548c2ecf20Sopenharmony_ci	}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	kfree(tsdata->raw_buffer);
6578c2ecf20Sopenharmony_ci	tsdata->raw_buffer = NULL;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	edt_ft5x06_restore_reg_parameters(tsdata);
6608c2ecf20Sopenharmony_ci	enable_irq(client->irq);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	return 0;
6638c2ecf20Sopenharmony_ci}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_cistatic int edt_ft5x06_debugfs_mode_get(void *data, u64 *mode)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	struct edt_ft5x06_ts_data *tsdata = data;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	*mode = tsdata->factory_mode;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	return 0;
6728c2ecf20Sopenharmony_ci};
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_cistatic int edt_ft5x06_debugfs_mode_set(void *data, u64 mode)
6758c2ecf20Sopenharmony_ci{
6768c2ecf20Sopenharmony_ci	struct edt_ft5x06_ts_data *tsdata = data;
6778c2ecf20Sopenharmony_ci	int retval = 0;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	if (mode > 1)
6808c2ecf20Sopenharmony_ci		return -ERANGE;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	mutex_lock(&tsdata->mutex);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	if (mode != tsdata->factory_mode) {
6858c2ecf20Sopenharmony_ci		retval = mode ? edt_ft5x06_factory_mode(tsdata) :
6868c2ecf20Sopenharmony_ci				edt_ft5x06_work_mode(tsdata);
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	mutex_unlock(&tsdata->mutex);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	return retval;
6928c2ecf20Sopenharmony_ci};
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(debugfs_mode_fops, edt_ft5x06_debugfs_mode_get,
6958c2ecf20Sopenharmony_ci			edt_ft5x06_debugfs_mode_set, "%llu\n");
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_cistatic ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file,
6988c2ecf20Sopenharmony_ci				char __user *buf, size_t count, loff_t *off)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	struct edt_ft5x06_ts_data *tsdata = file->private_data;
7018c2ecf20Sopenharmony_ci	struct i2c_client *client = tsdata->client;
7028c2ecf20Sopenharmony_ci	int retries  = EDT_RAW_DATA_RETRIES;
7038c2ecf20Sopenharmony_ci	int val, i, error;
7048c2ecf20Sopenharmony_ci	size_t read = 0;
7058c2ecf20Sopenharmony_ci	int colbytes;
7068c2ecf20Sopenharmony_ci	char wrbuf[3];
7078c2ecf20Sopenharmony_ci	u8 *rdbuf;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	if (*off < 0 || *off >= tsdata->raw_bufsize)
7108c2ecf20Sopenharmony_ci		return 0;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	mutex_lock(&tsdata->mutex);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	if (!tsdata->factory_mode || !tsdata->raw_buffer) {
7158c2ecf20Sopenharmony_ci		error = -EIO;
7168c2ecf20Sopenharmony_ci		goto out;
7178c2ecf20Sopenharmony_ci	}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	error = edt_ft5x06_register_write(tsdata, 0x08, 0x01);
7208c2ecf20Sopenharmony_ci	if (error) {
7218c2ecf20Sopenharmony_ci		dev_dbg(&client->dev,
7228c2ecf20Sopenharmony_ci			"failed to write 0x08 register, error %d\n", error);
7238c2ecf20Sopenharmony_ci		goto out;
7248c2ecf20Sopenharmony_ci	}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	do {
7278c2ecf20Sopenharmony_ci		usleep_range(EDT_RAW_DATA_DELAY, EDT_RAW_DATA_DELAY + 100);
7288c2ecf20Sopenharmony_ci		val = edt_ft5x06_register_read(tsdata, 0x08);
7298c2ecf20Sopenharmony_ci		if (val < 1)
7308c2ecf20Sopenharmony_ci			break;
7318c2ecf20Sopenharmony_ci	} while (--retries > 0);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	if (val < 0) {
7348c2ecf20Sopenharmony_ci		error = val;
7358c2ecf20Sopenharmony_ci		dev_dbg(&client->dev,
7368c2ecf20Sopenharmony_ci			"failed to read 0x08 register, error %d\n", error);
7378c2ecf20Sopenharmony_ci		goto out;
7388c2ecf20Sopenharmony_ci	}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	if (retries == 0) {
7418c2ecf20Sopenharmony_ci		dev_dbg(&client->dev,
7428c2ecf20Sopenharmony_ci			"timed out waiting for register to settle\n");
7438c2ecf20Sopenharmony_ci		error = -ETIMEDOUT;
7448c2ecf20Sopenharmony_ci		goto out;
7458c2ecf20Sopenharmony_ci	}
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	rdbuf = tsdata->raw_buffer;
7488c2ecf20Sopenharmony_ci	colbytes = tsdata->num_y * sizeof(u16);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	wrbuf[0] = 0xf5;
7518c2ecf20Sopenharmony_ci	wrbuf[1] = 0x0e;
7528c2ecf20Sopenharmony_ci	for (i = 0; i < tsdata->num_x; i++) {
7538c2ecf20Sopenharmony_ci		wrbuf[2] = i;  /* column index */
7548c2ecf20Sopenharmony_ci		error = edt_ft5x06_ts_readwrite(tsdata->client,
7558c2ecf20Sopenharmony_ci						sizeof(wrbuf), wrbuf,
7568c2ecf20Sopenharmony_ci						colbytes, rdbuf);
7578c2ecf20Sopenharmony_ci		if (error)
7588c2ecf20Sopenharmony_ci			goto out;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci		rdbuf += colbytes;
7618c2ecf20Sopenharmony_ci	}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	read = min_t(size_t, count, tsdata->raw_bufsize - *off);
7648c2ecf20Sopenharmony_ci	if (copy_to_user(buf, tsdata->raw_buffer + *off, read)) {
7658c2ecf20Sopenharmony_ci		error = -EFAULT;
7668c2ecf20Sopenharmony_ci		goto out;
7678c2ecf20Sopenharmony_ci	}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	*off += read;
7708c2ecf20Sopenharmony_ciout:
7718c2ecf20Sopenharmony_ci	mutex_unlock(&tsdata->mutex);
7728c2ecf20Sopenharmony_ci	return error ?: read;
7738c2ecf20Sopenharmony_ci};
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_cistatic const struct file_operations debugfs_raw_data_fops = {
7768c2ecf20Sopenharmony_ci	.open = simple_open,
7778c2ecf20Sopenharmony_ci	.read = edt_ft5x06_debugfs_raw_data_read,
7788c2ecf20Sopenharmony_ci};
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_cistatic void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
7818c2ecf20Sopenharmony_ci					  const char *debugfs_name)
7828c2ecf20Sopenharmony_ci{
7838c2ecf20Sopenharmony_ci	tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	debugfs_create_u16("num_x", S_IRUSR, tsdata->debug_dir, &tsdata->num_x);
7868c2ecf20Sopenharmony_ci	debugfs_create_u16("num_y", S_IRUSR, tsdata->debug_dir, &tsdata->num_y);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	debugfs_create_file("mode", S_IRUSR | S_IWUSR,
7898c2ecf20Sopenharmony_ci			    tsdata->debug_dir, tsdata, &debugfs_mode_fops);
7908c2ecf20Sopenharmony_ci	debugfs_create_file("raw_data", S_IRUSR,
7918c2ecf20Sopenharmony_ci			    tsdata->debug_dir, tsdata, &debugfs_raw_data_fops);
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_cistatic void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
7958c2ecf20Sopenharmony_ci{
7968c2ecf20Sopenharmony_ci	debugfs_remove_recursive(tsdata->debug_dir);
7978c2ecf20Sopenharmony_ci	kfree(tsdata->raw_buffer);
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci#else
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_cistatic int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
8038c2ecf20Sopenharmony_ci{
8048c2ecf20Sopenharmony_ci	return -ENOSYS;
8058c2ecf20Sopenharmony_ci}
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_cistatic void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
8088c2ecf20Sopenharmony_ci					  const char *debugfs_name)
8098c2ecf20Sopenharmony_ci{
8108c2ecf20Sopenharmony_ci}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_cistatic void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
8138c2ecf20Sopenharmony_ci{
8148c2ecf20Sopenharmony_ci}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci#endif /* CONFIG_DEBUGFS */
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_cistatic int edt_ft5x06_ts_identify(struct i2c_client *client,
8198c2ecf20Sopenharmony_ci					struct edt_ft5x06_ts_data *tsdata,
8208c2ecf20Sopenharmony_ci					char *fw_version)
8218c2ecf20Sopenharmony_ci{
8228c2ecf20Sopenharmony_ci	u8 rdbuf[EDT_NAME_LEN];
8238c2ecf20Sopenharmony_ci	char *p;
8248c2ecf20Sopenharmony_ci	int error;
8258c2ecf20Sopenharmony_ci	char *model_name = tsdata->name;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	/* see what we find if we assume it is a M06 *
8288c2ecf20Sopenharmony_ci	 * if we get less than EDT_NAME_LEN, we don't want
8298c2ecf20Sopenharmony_ci	 * to have garbage in there
8308c2ecf20Sopenharmony_ci	 */
8318c2ecf20Sopenharmony_ci	memset(rdbuf, 0, sizeof(rdbuf));
8328c2ecf20Sopenharmony_ci	error = edt_ft5x06_ts_readwrite(client, 1, "\xBB",
8338c2ecf20Sopenharmony_ci					EDT_NAME_LEN - 1, rdbuf);
8348c2ecf20Sopenharmony_ci	if (error)
8358c2ecf20Sopenharmony_ci		return error;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	/* Probe content for something consistent.
8388c2ecf20Sopenharmony_ci	 * M06 starts with a response byte, M12 gives the data directly.
8398c2ecf20Sopenharmony_ci	 * M09/Generic does not provide model number information.
8408c2ecf20Sopenharmony_ci	 */
8418c2ecf20Sopenharmony_ci	if (!strncasecmp(rdbuf + 1, "EP0", 3)) {
8428c2ecf20Sopenharmony_ci		tsdata->version = EDT_M06;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci		/* remove last '$' end marker */
8458c2ecf20Sopenharmony_ci		rdbuf[EDT_NAME_LEN - 1] = '\0';
8468c2ecf20Sopenharmony_ci		if (rdbuf[EDT_NAME_LEN - 2] == '$')
8478c2ecf20Sopenharmony_ci			rdbuf[EDT_NAME_LEN - 2] = '\0';
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci		/* look for Model/Version separator */
8508c2ecf20Sopenharmony_ci		p = strchr(rdbuf, '*');
8518c2ecf20Sopenharmony_ci		if (p)
8528c2ecf20Sopenharmony_ci			*p++ = '\0';
8538c2ecf20Sopenharmony_ci		strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN);
8548c2ecf20Sopenharmony_ci		strlcpy(fw_version, p ? p : "", EDT_NAME_LEN);
8558c2ecf20Sopenharmony_ci	} else if (!strncasecmp(rdbuf, "EP0", 3)) {
8568c2ecf20Sopenharmony_ci		tsdata->version = EDT_M12;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci		/* remove last '$' end marker */
8598c2ecf20Sopenharmony_ci		rdbuf[EDT_NAME_LEN - 2] = '\0';
8608c2ecf20Sopenharmony_ci		if (rdbuf[EDT_NAME_LEN - 3] == '$')
8618c2ecf20Sopenharmony_ci			rdbuf[EDT_NAME_LEN - 3] = '\0';
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci		/* look for Model/Version separator */
8648c2ecf20Sopenharmony_ci		p = strchr(rdbuf, '*');
8658c2ecf20Sopenharmony_ci		if (p)
8668c2ecf20Sopenharmony_ci			*p++ = '\0';
8678c2ecf20Sopenharmony_ci		strlcpy(model_name, rdbuf, EDT_NAME_LEN);
8688c2ecf20Sopenharmony_ci		strlcpy(fw_version, p ? p : "", EDT_NAME_LEN);
8698c2ecf20Sopenharmony_ci	} else {
8708c2ecf20Sopenharmony_ci		/* If it is not an EDT M06/M12 touchscreen, then the model
8718c2ecf20Sopenharmony_ci		 * detection is a bit hairy. The different ft5x06
8728c2ecf20Sopenharmony_ci		 * firmares around don't reliably implement the
8738c2ecf20Sopenharmony_ci		 * identification registers. Well, we'll take a shot.
8748c2ecf20Sopenharmony_ci		 *
8758c2ecf20Sopenharmony_ci		 * The main difference between generic focaltec based
8768c2ecf20Sopenharmony_ci		 * touches and EDT M09 is that we know how to retrieve
8778c2ecf20Sopenharmony_ci		 * the max coordinates for the latter.
8788c2ecf20Sopenharmony_ci		 */
8798c2ecf20Sopenharmony_ci		tsdata->version = GENERIC_FT;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci		error = edt_ft5x06_ts_readwrite(client, 1, "\xA6",
8828c2ecf20Sopenharmony_ci						2, rdbuf);
8838c2ecf20Sopenharmony_ci		if (error)
8848c2ecf20Sopenharmony_ci			return error;
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci		strlcpy(fw_version, rdbuf, 2);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci		error = edt_ft5x06_ts_readwrite(client, 1, "\xA8",
8898c2ecf20Sopenharmony_ci						1, rdbuf);
8908c2ecf20Sopenharmony_ci		if (error)
8918c2ecf20Sopenharmony_ci			return error;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci		/* This "model identification" is not exact. Unfortunately
8948c2ecf20Sopenharmony_ci		 * not all firmwares for the ft5x06 put useful values in
8958c2ecf20Sopenharmony_ci		 * the identification registers.
8968c2ecf20Sopenharmony_ci		 */
8978c2ecf20Sopenharmony_ci		switch (rdbuf[0]) {
8988c2ecf20Sopenharmony_ci		case 0x35:   /* EDT EP0350M09 */
8998c2ecf20Sopenharmony_ci		case 0x43:   /* EDT EP0430M09 */
9008c2ecf20Sopenharmony_ci		case 0x50:   /* EDT EP0500M09 */
9018c2ecf20Sopenharmony_ci		case 0x57:   /* EDT EP0570M09 */
9028c2ecf20Sopenharmony_ci		case 0x70:   /* EDT EP0700M09 */
9038c2ecf20Sopenharmony_ci			tsdata->version = EDT_M09;
9048c2ecf20Sopenharmony_ci			snprintf(model_name, EDT_NAME_LEN, "EP0%i%i0M09",
9058c2ecf20Sopenharmony_ci				rdbuf[0] >> 4, rdbuf[0] & 0x0F);
9068c2ecf20Sopenharmony_ci			break;
9078c2ecf20Sopenharmony_ci		case 0xa1:   /* EDT EP1010ML00 */
9088c2ecf20Sopenharmony_ci			tsdata->version = EDT_M09;
9098c2ecf20Sopenharmony_ci			snprintf(model_name, EDT_NAME_LEN, "EP%i%i0ML00",
9108c2ecf20Sopenharmony_ci				rdbuf[0] >> 4, rdbuf[0] & 0x0F);
9118c2ecf20Sopenharmony_ci			break;
9128c2ecf20Sopenharmony_ci		case 0x5a:   /* Solomon Goldentek Display */
9138c2ecf20Sopenharmony_ci			snprintf(model_name, EDT_NAME_LEN, "GKTW50SCED1R0");
9148c2ecf20Sopenharmony_ci			break;
9158c2ecf20Sopenharmony_ci		case 0x59:  /* Evervision Display with FT5xx6 TS */
9168c2ecf20Sopenharmony_ci			tsdata->version = EV_FT;
9178c2ecf20Sopenharmony_ci			error = edt_ft5x06_ts_readwrite(client, 1, "\x53",
9188c2ecf20Sopenharmony_ci							1, rdbuf);
9198c2ecf20Sopenharmony_ci			if (error)
9208c2ecf20Sopenharmony_ci				return error;
9218c2ecf20Sopenharmony_ci			strlcpy(fw_version, rdbuf, 1);
9228c2ecf20Sopenharmony_ci			snprintf(model_name, EDT_NAME_LEN,
9238c2ecf20Sopenharmony_ci				 "EVERVISION-FT5726NEi");
9248c2ecf20Sopenharmony_ci			break;
9258c2ecf20Sopenharmony_ci		default:
9268c2ecf20Sopenharmony_ci			snprintf(model_name, EDT_NAME_LEN,
9278c2ecf20Sopenharmony_ci				 "generic ft5x06 (%02x)",
9288c2ecf20Sopenharmony_ci				 rdbuf[0]);
9298c2ecf20Sopenharmony_ci			break;
9308c2ecf20Sopenharmony_ci		}
9318c2ecf20Sopenharmony_ci	}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	return 0;
9348c2ecf20Sopenharmony_ci}
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_cistatic void edt_ft5x06_ts_get_defaults(struct device *dev,
9378c2ecf20Sopenharmony_ci				       struct edt_ft5x06_ts_data *tsdata)
9388c2ecf20Sopenharmony_ci{
9398c2ecf20Sopenharmony_ci	struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
9408c2ecf20Sopenharmony_ci	u32 val;
9418c2ecf20Sopenharmony_ci	int error;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	error = device_property_read_u32(dev, "threshold", &val);
9448c2ecf20Sopenharmony_ci	if (!error) {
9458c2ecf20Sopenharmony_ci		edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold, val);
9468c2ecf20Sopenharmony_ci		tsdata->threshold = val;
9478c2ecf20Sopenharmony_ci	}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	error = device_property_read_u32(dev, "gain", &val);
9508c2ecf20Sopenharmony_ci	if (!error) {
9518c2ecf20Sopenharmony_ci		edt_ft5x06_register_write(tsdata, reg_addr->reg_gain, val);
9528c2ecf20Sopenharmony_ci		tsdata->gain = val;
9538c2ecf20Sopenharmony_ci	}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	error = device_property_read_u32(dev, "offset", &val);
9568c2ecf20Sopenharmony_ci	if (!error) {
9578c2ecf20Sopenharmony_ci		if (reg_addr->reg_offset != NO_REGISTER)
9588c2ecf20Sopenharmony_ci			edt_ft5x06_register_write(tsdata,
9598c2ecf20Sopenharmony_ci						  reg_addr->reg_offset, val);
9608c2ecf20Sopenharmony_ci		tsdata->offset = val;
9618c2ecf20Sopenharmony_ci	}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	error = device_property_read_u32(dev, "offset-x", &val);
9648c2ecf20Sopenharmony_ci	if (!error) {
9658c2ecf20Sopenharmony_ci		if (reg_addr->reg_offset_x != NO_REGISTER)
9668c2ecf20Sopenharmony_ci			edt_ft5x06_register_write(tsdata,
9678c2ecf20Sopenharmony_ci						  reg_addr->reg_offset_x, val);
9688c2ecf20Sopenharmony_ci		tsdata->offset_x = val;
9698c2ecf20Sopenharmony_ci	}
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	error = device_property_read_u32(dev, "offset-y", &val);
9728c2ecf20Sopenharmony_ci	if (!error) {
9738c2ecf20Sopenharmony_ci		if (reg_addr->reg_offset_y != NO_REGISTER)
9748c2ecf20Sopenharmony_ci			edt_ft5x06_register_write(tsdata,
9758c2ecf20Sopenharmony_ci						  reg_addr->reg_offset_y, val);
9768c2ecf20Sopenharmony_ci		tsdata->offset_y = val;
9778c2ecf20Sopenharmony_ci	}
9788c2ecf20Sopenharmony_ci}
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_cistatic void
9818c2ecf20Sopenharmony_ciedt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata)
9828c2ecf20Sopenharmony_ci{
9838c2ecf20Sopenharmony_ci	struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	tsdata->threshold = edt_ft5x06_register_read(tsdata,
9868c2ecf20Sopenharmony_ci						     reg_addr->reg_threshold);
9878c2ecf20Sopenharmony_ci	tsdata->gain = edt_ft5x06_register_read(tsdata, reg_addr->reg_gain);
9888c2ecf20Sopenharmony_ci	if (reg_addr->reg_offset != NO_REGISTER)
9898c2ecf20Sopenharmony_ci		tsdata->offset =
9908c2ecf20Sopenharmony_ci			edt_ft5x06_register_read(tsdata, reg_addr->reg_offset);
9918c2ecf20Sopenharmony_ci	if (reg_addr->reg_offset_x != NO_REGISTER)
9928c2ecf20Sopenharmony_ci		tsdata->offset_x = edt_ft5x06_register_read(tsdata,
9938c2ecf20Sopenharmony_ci						reg_addr->reg_offset_x);
9948c2ecf20Sopenharmony_ci	if (reg_addr->reg_offset_y != NO_REGISTER)
9958c2ecf20Sopenharmony_ci		tsdata->offset_y = edt_ft5x06_register_read(tsdata,
9968c2ecf20Sopenharmony_ci						reg_addr->reg_offset_y);
9978c2ecf20Sopenharmony_ci	if (reg_addr->reg_report_rate != NO_REGISTER)
9988c2ecf20Sopenharmony_ci		tsdata->report_rate = edt_ft5x06_register_read(tsdata,
9998c2ecf20Sopenharmony_ci						reg_addr->reg_report_rate);
10008c2ecf20Sopenharmony_ci	if (tsdata->version == EDT_M06 ||
10018c2ecf20Sopenharmony_ci	    tsdata->version == EDT_M09 ||
10028c2ecf20Sopenharmony_ci	    tsdata->version == EDT_M12) {
10038c2ecf20Sopenharmony_ci		tsdata->num_x = edt_ft5x06_register_read(tsdata,
10048c2ecf20Sopenharmony_ci							 reg_addr->reg_num_x);
10058c2ecf20Sopenharmony_ci		tsdata->num_y = edt_ft5x06_register_read(tsdata,
10068c2ecf20Sopenharmony_ci							 reg_addr->reg_num_y);
10078c2ecf20Sopenharmony_ci	} else {
10088c2ecf20Sopenharmony_ci		tsdata->num_x = -1;
10098c2ecf20Sopenharmony_ci		tsdata->num_y = -1;
10108c2ecf20Sopenharmony_ci	}
10118c2ecf20Sopenharmony_ci}
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_cistatic void
10148c2ecf20Sopenharmony_ciedt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata)
10158c2ecf20Sopenharmony_ci{
10168c2ecf20Sopenharmony_ci	struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	switch (tsdata->version) {
10198c2ecf20Sopenharmony_ci	case EDT_M06:
10208c2ecf20Sopenharmony_ci		reg_addr->reg_threshold = WORK_REGISTER_THRESHOLD;
10218c2ecf20Sopenharmony_ci		reg_addr->reg_report_rate = WORK_REGISTER_REPORT_RATE;
10228c2ecf20Sopenharmony_ci		reg_addr->reg_gain = WORK_REGISTER_GAIN;
10238c2ecf20Sopenharmony_ci		reg_addr->reg_offset = WORK_REGISTER_OFFSET;
10248c2ecf20Sopenharmony_ci		reg_addr->reg_offset_x = NO_REGISTER;
10258c2ecf20Sopenharmony_ci		reg_addr->reg_offset_y = NO_REGISTER;
10268c2ecf20Sopenharmony_ci		reg_addr->reg_num_x = WORK_REGISTER_NUM_X;
10278c2ecf20Sopenharmony_ci		reg_addr->reg_num_y = WORK_REGISTER_NUM_Y;
10288c2ecf20Sopenharmony_ci		break;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	case EDT_M09:
10318c2ecf20Sopenharmony_ci	case EDT_M12:
10328c2ecf20Sopenharmony_ci		reg_addr->reg_threshold = M09_REGISTER_THRESHOLD;
10338c2ecf20Sopenharmony_ci		reg_addr->reg_report_rate = NO_REGISTER;
10348c2ecf20Sopenharmony_ci		reg_addr->reg_gain = M09_REGISTER_GAIN;
10358c2ecf20Sopenharmony_ci		reg_addr->reg_offset = M09_REGISTER_OFFSET;
10368c2ecf20Sopenharmony_ci		reg_addr->reg_offset_x = NO_REGISTER;
10378c2ecf20Sopenharmony_ci		reg_addr->reg_offset_y = NO_REGISTER;
10388c2ecf20Sopenharmony_ci		reg_addr->reg_num_x = M09_REGISTER_NUM_X;
10398c2ecf20Sopenharmony_ci		reg_addr->reg_num_y = M09_REGISTER_NUM_Y;
10408c2ecf20Sopenharmony_ci		break;
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	case EV_FT:
10438c2ecf20Sopenharmony_ci		reg_addr->reg_threshold = EV_REGISTER_THRESHOLD;
10448c2ecf20Sopenharmony_ci		reg_addr->reg_gain = EV_REGISTER_GAIN;
10458c2ecf20Sopenharmony_ci		reg_addr->reg_offset = NO_REGISTER;
10468c2ecf20Sopenharmony_ci		reg_addr->reg_offset_x = EV_REGISTER_OFFSET_X;
10478c2ecf20Sopenharmony_ci		reg_addr->reg_offset_y = EV_REGISTER_OFFSET_Y;
10488c2ecf20Sopenharmony_ci		reg_addr->reg_num_x = NO_REGISTER;
10498c2ecf20Sopenharmony_ci		reg_addr->reg_num_y = NO_REGISTER;
10508c2ecf20Sopenharmony_ci		reg_addr->reg_report_rate = NO_REGISTER;
10518c2ecf20Sopenharmony_ci		break;
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	case GENERIC_FT:
10548c2ecf20Sopenharmony_ci		/* this is a guesswork */
10558c2ecf20Sopenharmony_ci		reg_addr->reg_threshold = M09_REGISTER_THRESHOLD;
10568c2ecf20Sopenharmony_ci		reg_addr->reg_gain = M09_REGISTER_GAIN;
10578c2ecf20Sopenharmony_ci		reg_addr->reg_offset = M09_REGISTER_OFFSET;
10588c2ecf20Sopenharmony_ci		reg_addr->reg_offset_x = NO_REGISTER;
10598c2ecf20Sopenharmony_ci		reg_addr->reg_offset_y = NO_REGISTER;
10608c2ecf20Sopenharmony_ci		break;
10618c2ecf20Sopenharmony_ci	}
10628c2ecf20Sopenharmony_ci}
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_cistatic void edt_ft5x06_disable_regulator(void *arg)
10658c2ecf20Sopenharmony_ci{
10668c2ecf20Sopenharmony_ci	struct edt_ft5x06_ts_data *data = arg;
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	regulator_disable(data->vcc);
10698c2ecf20Sopenharmony_ci}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_cistatic int edt_ft5x06_ts_probe(struct i2c_client *client,
10728c2ecf20Sopenharmony_ci					 const struct i2c_device_id *id)
10738c2ecf20Sopenharmony_ci{
10748c2ecf20Sopenharmony_ci	const struct edt_i2c_chip_data *chip_data;
10758c2ecf20Sopenharmony_ci	struct edt_ft5x06_ts_data *tsdata;
10768c2ecf20Sopenharmony_ci	u8 buf[2] = { 0xfc, 0x00 };
10778c2ecf20Sopenharmony_ci	struct input_dev *input;
10788c2ecf20Sopenharmony_ci	unsigned long irq_flags;
10798c2ecf20Sopenharmony_ci	int error;
10808c2ecf20Sopenharmony_ci	char fw_version[EDT_NAME_LEN];
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n");
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);
10858c2ecf20Sopenharmony_ci	if (!tsdata) {
10868c2ecf20Sopenharmony_ci		dev_err(&client->dev, "failed to allocate driver data.\n");
10878c2ecf20Sopenharmony_ci		return -ENOMEM;
10888c2ecf20Sopenharmony_ci	}
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	chip_data = device_get_match_data(&client->dev);
10918c2ecf20Sopenharmony_ci	if (!chip_data)
10928c2ecf20Sopenharmony_ci		chip_data = (const struct edt_i2c_chip_data *)id->driver_data;
10938c2ecf20Sopenharmony_ci	if (!chip_data || !chip_data->max_support_points) {
10948c2ecf20Sopenharmony_ci		dev_err(&client->dev, "invalid or missing chip data\n");
10958c2ecf20Sopenharmony_ci		return -EINVAL;
10968c2ecf20Sopenharmony_ci	}
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	tsdata->max_support_points = chip_data->max_support_points;
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	tsdata->vcc = devm_regulator_get(&client->dev, "vcc");
11018c2ecf20Sopenharmony_ci	if (IS_ERR(tsdata->vcc)) {
11028c2ecf20Sopenharmony_ci		error = PTR_ERR(tsdata->vcc);
11038c2ecf20Sopenharmony_ci		if (error != -EPROBE_DEFER)
11048c2ecf20Sopenharmony_ci			dev_err(&client->dev,
11058c2ecf20Sopenharmony_ci				"failed to request regulator: %d\n", error);
11068c2ecf20Sopenharmony_ci		return error;
11078c2ecf20Sopenharmony_ci	}
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	error = regulator_enable(tsdata->vcc);
11108c2ecf20Sopenharmony_ci	if (error < 0) {
11118c2ecf20Sopenharmony_ci		dev_err(&client->dev, "failed to enable vcc: %d\n", error);
11128c2ecf20Sopenharmony_ci		return error;
11138c2ecf20Sopenharmony_ci	}
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	error = devm_add_action_or_reset(&client->dev,
11168c2ecf20Sopenharmony_ci					 edt_ft5x06_disable_regulator,
11178c2ecf20Sopenharmony_ci					 tsdata);
11188c2ecf20Sopenharmony_ci	if (error)
11198c2ecf20Sopenharmony_ci		return error;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev,
11228c2ecf20Sopenharmony_ci						     "reset", GPIOD_OUT_HIGH);
11238c2ecf20Sopenharmony_ci	if (IS_ERR(tsdata->reset_gpio)) {
11248c2ecf20Sopenharmony_ci		error = PTR_ERR(tsdata->reset_gpio);
11258c2ecf20Sopenharmony_ci		dev_err(&client->dev,
11268c2ecf20Sopenharmony_ci			"Failed to request GPIO reset pin, error %d\n", error);
11278c2ecf20Sopenharmony_ci		return error;
11288c2ecf20Sopenharmony_ci	}
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	tsdata->wake_gpio = devm_gpiod_get_optional(&client->dev,
11318c2ecf20Sopenharmony_ci						    "wake", GPIOD_OUT_LOW);
11328c2ecf20Sopenharmony_ci	if (IS_ERR(tsdata->wake_gpio)) {
11338c2ecf20Sopenharmony_ci		error = PTR_ERR(tsdata->wake_gpio);
11348c2ecf20Sopenharmony_ci		dev_err(&client->dev,
11358c2ecf20Sopenharmony_ci			"Failed to request GPIO wake pin, error %d\n", error);
11368c2ecf20Sopenharmony_ci		return error;
11378c2ecf20Sopenharmony_ci	}
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	/*
11408c2ecf20Sopenharmony_ci	 * Check which sleep modes we can support. Power-off requieres the
11418c2ecf20Sopenharmony_ci	 * reset-pin to ensure correct power-down/power-up behaviour. Start with
11428c2ecf20Sopenharmony_ci	 * the EDT_PMODE_POWEROFF test since this is the deepest possible sleep
11438c2ecf20Sopenharmony_ci	 * mode.
11448c2ecf20Sopenharmony_ci	 */
11458c2ecf20Sopenharmony_ci	if (tsdata->reset_gpio)
11468c2ecf20Sopenharmony_ci		tsdata->suspend_mode = EDT_PMODE_POWEROFF;
11478c2ecf20Sopenharmony_ci	else if (tsdata->wake_gpio)
11488c2ecf20Sopenharmony_ci		tsdata->suspend_mode = EDT_PMODE_HIBERNATE;
11498c2ecf20Sopenharmony_ci	else
11508c2ecf20Sopenharmony_ci		tsdata->suspend_mode = EDT_PMODE_NOT_SUPPORTED;
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	if (tsdata->wake_gpio) {
11538c2ecf20Sopenharmony_ci		usleep_range(5000, 6000);
11548c2ecf20Sopenharmony_ci		gpiod_set_value_cansleep(tsdata->wake_gpio, 1);
11558c2ecf20Sopenharmony_ci	}
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci	if (tsdata->reset_gpio) {
11588c2ecf20Sopenharmony_ci		usleep_range(5000, 6000);
11598c2ecf20Sopenharmony_ci		gpiod_set_value_cansleep(tsdata->reset_gpio, 0);
11608c2ecf20Sopenharmony_ci		msleep(300);
11618c2ecf20Sopenharmony_ci	}
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	input = devm_input_allocate_device(&client->dev);
11648c2ecf20Sopenharmony_ci	if (!input) {
11658c2ecf20Sopenharmony_ci		dev_err(&client->dev, "failed to allocate input device.\n");
11668c2ecf20Sopenharmony_ci		return -ENOMEM;
11678c2ecf20Sopenharmony_ci	}
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	mutex_init(&tsdata->mutex);
11708c2ecf20Sopenharmony_ci	tsdata->client = client;
11718c2ecf20Sopenharmony_ci	tsdata->input = input;
11728c2ecf20Sopenharmony_ci	tsdata->factory_mode = false;
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	error = edt_ft5x06_ts_identify(client, tsdata, fw_version);
11758c2ecf20Sopenharmony_ci	if (error) {
11768c2ecf20Sopenharmony_ci		dev_err(&client->dev, "touchscreen probe failed\n");
11778c2ecf20Sopenharmony_ci		return error;
11788c2ecf20Sopenharmony_ci	}
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	/*
11818c2ecf20Sopenharmony_ci	 * Dummy read access. EP0700MLP1 returns bogus data on the first
11828c2ecf20Sopenharmony_ci	 * register read access and ignores writes.
11838c2ecf20Sopenharmony_ci	 */
11848c2ecf20Sopenharmony_ci	edt_ft5x06_ts_readwrite(tsdata->client, 2, buf, 2, buf);
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	edt_ft5x06_ts_set_regs(tsdata);
11878c2ecf20Sopenharmony_ci	edt_ft5x06_ts_get_defaults(&client->dev, tsdata);
11888c2ecf20Sopenharmony_ci	edt_ft5x06_ts_get_parameters(tsdata);
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	dev_dbg(&client->dev,
11918c2ecf20Sopenharmony_ci		"Model \"%s\", Rev. \"%s\", %dx%d sensors\n",
11928c2ecf20Sopenharmony_ci		tsdata->name, fw_version, tsdata->num_x, tsdata->num_y);
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	input->name = tsdata->name;
11958c2ecf20Sopenharmony_ci	input->id.bustype = BUS_I2C;
11968c2ecf20Sopenharmony_ci	input->dev.parent = &client->dev;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	if (tsdata->version == EDT_M06 ||
11998c2ecf20Sopenharmony_ci	    tsdata->version == EDT_M09 ||
12008c2ecf20Sopenharmony_ci	    tsdata->version == EDT_M12) {
12018c2ecf20Sopenharmony_ci		input_set_abs_params(input, ABS_MT_POSITION_X,
12028c2ecf20Sopenharmony_ci				     0, tsdata->num_x * 64 - 1, 0, 0);
12038c2ecf20Sopenharmony_ci		input_set_abs_params(input, ABS_MT_POSITION_Y,
12048c2ecf20Sopenharmony_ci				     0, tsdata->num_y * 64 - 1, 0, 0);
12058c2ecf20Sopenharmony_ci	} else {
12068c2ecf20Sopenharmony_ci		/* Unknown maximum values. Specify via devicetree */
12078c2ecf20Sopenharmony_ci		input_set_abs_params(input, ABS_MT_POSITION_X,
12088c2ecf20Sopenharmony_ci				     0, 65535, 0, 0);
12098c2ecf20Sopenharmony_ci		input_set_abs_params(input, ABS_MT_POSITION_Y,
12108c2ecf20Sopenharmony_ci				     0, 65535, 0, 0);
12118c2ecf20Sopenharmony_ci	}
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	touchscreen_parse_properties(input, true, &tsdata->prop);
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	error = input_mt_init_slots(input, tsdata->max_support_points,
12168c2ecf20Sopenharmony_ci				INPUT_MT_DIRECT);
12178c2ecf20Sopenharmony_ci	if (error) {
12188c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Unable to init MT slots.\n");
12198c2ecf20Sopenharmony_ci		return error;
12208c2ecf20Sopenharmony_ci	}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, tsdata);
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	irq_flags = irq_get_trigger_type(client->irq);
12258c2ecf20Sopenharmony_ci	if (irq_flags == IRQF_TRIGGER_NONE)
12268c2ecf20Sopenharmony_ci		irq_flags = IRQF_TRIGGER_FALLING;
12278c2ecf20Sopenharmony_ci	irq_flags |= IRQF_ONESHOT;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	error = devm_request_threaded_irq(&client->dev, client->irq,
12308c2ecf20Sopenharmony_ci					NULL, edt_ft5x06_ts_isr, irq_flags,
12318c2ecf20Sopenharmony_ci					client->name, tsdata);
12328c2ecf20Sopenharmony_ci	if (error) {
12338c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
12348c2ecf20Sopenharmony_ci		return error;
12358c2ecf20Sopenharmony_ci	}
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	error = devm_device_add_group(&client->dev, &edt_ft5x06_attr_group);
12388c2ecf20Sopenharmony_ci	if (error)
12398c2ecf20Sopenharmony_ci		return error;
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	error = input_register_device(input);
12428c2ecf20Sopenharmony_ci	if (error)
12438c2ecf20Sopenharmony_ci		return error;
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev));
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	dev_dbg(&client->dev,
12488c2ecf20Sopenharmony_ci		"EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n",
12498c2ecf20Sopenharmony_ci		client->irq,
12508c2ecf20Sopenharmony_ci		tsdata->wake_gpio ? desc_to_gpio(tsdata->wake_gpio) : -1,
12518c2ecf20Sopenharmony_ci		tsdata->reset_gpio ? desc_to_gpio(tsdata->reset_gpio) : -1);
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	return 0;
12548c2ecf20Sopenharmony_ci}
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_cistatic int edt_ft5x06_ts_remove(struct i2c_client *client)
12578c2ecf20Sopenharmony_ci{
12588c2ecf20Sopenharmony_ci	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	edt_ft5x06_ts_teardown_debugfs(tsdata);
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	return 0;
12638c2ecf20Sopenharmony_ci}
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_cistatic int __maybe_unused edt_ft5x06_ts_suspend(struct device *dev)
12668c2ecf20Sopenharmony_ci{
12678c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
12688c2ecf20Sopenharmony_ci	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
12698c2ecf20Sopenharmony_ci	struct gpio_desc *reset_gpio = tsdata->reset_gpio;
12708c2ecf20Sopenharmony_ci	int ret;
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	if (device_may_wakeup(dev))
12738c2ecf20Sopenharmony_ci		return 0;
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	if (tsdata->suspend_mode == EDT_PMODE_NOT_SUPPORTED)
12768c2ecf20Sopenharmony_ci		return 0;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	/* Enter hibernate mode. */
12798c2ecf20Sopenharmony_ci	ret = edt_ft5x06_register_write(tsdata, PMOD_REGISTER_OPMODE,
12808c2ecf20Sopenharmony_ci					PMOD_REGISTER_HIBERNATE);
12818c2ecf20Sopenharmony_ci	if (ret)
12828c2ecf20Sopenharmony_ci		dev_warn(dev, "Failed to set hibernate mode\n");
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	if (tsdata->suspend_mode == EDT_PMODE_HIBERNATE)
12858c2ecf20Sopenharmony_ci		return 0;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	/*
12888c2ecf20Sopenharmony_ci	 * Power-off according the datasheet. Cut the power may leaf the irq
12898c2ecf20Sopenharmony_ci	 * line in an undefined state depending on the host pull resistor
12908c2ecf20Sopenharmony_ci	 * settings. Disable the irq to avoid adjusting each host till the
12918c2ecf20Sopenharmony_ci	 * device is back in a full functional state.
12928c2ecf20Sopenharmony_ci	 */
12938c2ecf20Sopenharmony_ci	disable_irq(tsdata->client->irq);
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(reset_gpio, 1);
12968c2ecf20Sopenharmony_ci	usleep_range(1000, 2000);
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	ret = regulator_disable(tsdata->vcc);
12998c2ecf20Sopenharmony_ci	if (ret)
13008c2ecf20Sopenharmony_ci		dev_warn(dev, "Failed to disable vcc\n");
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	return 0;
13038c2ecf20Sopenharmony_ci}
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_cistatic int __maybe_unused edt_ft5x06_ts_resume(struct device *dev)
13068c2ecf20Sopenharmony_ci{
13078c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
13088c2ecf20Sopenharmony_ci	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
13098c2ecf20Sopenharmony_ci	int ret = 0;
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	if (device_may_wakeup(dev))
13128c2ecf20Sopenharmony_ci		return 0;
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci	if (tsdata->suspend_mode == EDT_PMODE_NOT_SUPPORTED)
13158c2ecf20Sopenharmony_ci		return 0;
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	if (tsdata->suspend_mode == EDT_PMODE_POWEROFF) {
13188c2ecf20Sopenharmony_ci		struct gpio_desc *reset_gpio = tsdata->reset_gpio;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci		/*
13218c2ecf20Sopenharmony_ci		 * We can't check if the regulator is a dummy or a real
13228c2ecf20Sopenharmony_ci		 * regulator. So we need to specify the 5ms reset time (T_rst)
13238c2ecf20Sopenharmony_ci		 * here instead of the 100us T_rtp time. We also need to wait
13248c2ecf20Sopenharmony_ci		 * 300ms in case it was a real supply and the power was cutted
13258c2ecf20Sopenharmony_ci		 * of. Toggle the reset pin is also a way to exit the hibernate
13268c2ecf20Sopenharmony_ci		 * mode.
13278c2ecf20Sopenharmony_ci		 */
13288c2ecf20Sopenharmony_ci		gpiod_set_value_cansleep(reset_gpio, 1);
13298c2ecf20Sopenharmony_ci		usleep_range(5000, 6000);
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci		ret = regulator_enable(tsdata->vcc);
13328c2ecf20Sopenharmony_ci		if (ret) {
13338c2ecf20Sopenharmony_ci			dev_err(dev, "Failed to enable vcc\n");
13348c2ecf20Sopenharmony_ci			return ret;
13358c2ecf20Sopenharmony_ci		}
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci		usleep_range(1000, 2000);
13388c2ecf20Sopenharmony_ci		gpiod_set_value_cansleep(reset_gpio, 0);
13398c2ecf20Sopenharmony_ci		msleep(300);
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci		edt_ft5x06_restore_reg_parameters(tsdata);
13428c2ecf20Sopenharmony_ci		enable_irq(tsdata->client->irq);
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci		if (tsdata->factory_mode)
13458c2ecf20Sopenharmony_ci			ret = edt_ft5x06_factory_mode(tsdata);
13468c2ecf20Sopenharmony_ci	} else {
13478c2ecf20Sopenharmony_ci		struct gpio_desc *wake_gpio = tsdata->wake_gpio;
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci		gpiod_set_value_cansleep(wake_gpio, 0);
13508c2ecf20Sopenharmony_ci		usleep_range(5000, 6000);
13518c2ecf20Sopenharmony_ci		gpiod_set_value_cansleep(wake_gpio, 1);
13528c2ecf20Sopenharmony_ci	}
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	return ret;
13568c2ecf20Sopenharmony_ci}
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops,
13598c2ecf20Sopenharmony_ci			 edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume);
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_cistatic const struct edt_i2c_chip_data edt_ft5x06_data = {
13628c2ecf20Sopenharmony_ci	.max_support_points = 5,
13638c2ecf20Sopenharmony_ci};
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_cistatic const struct edt_i2c_chip_data edt_ft5506_data = {
13668c2ecf20Sopenharmony_ci	.max_support_points = 10,
13678c2ecf20Sopenharmony_ci};
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_cistatic const struct edt_i2c_chip_data edt_ft6236_data = {
13708c2ecf20Sopenharmony_ci	.max_support_points = 2,
13718c2ecf20Sopenharmony_ci};
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_cistatic const struct i2c_device_id edt_ft5x06_ts_id[] = {
13748c2ecf20Sopenharmony_ci	{ .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data },
13758c2ecf20Sopenharmony_ci	{ .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data },
13768c2ecf20Sopenharmony_ci	{ .name = "ev-ft5726", .driver_data = (long)&edt_ft5506_data },
13778c2ecf20Sopenharmony_ci	/* Note no edt- prefix for compatibility with the ft6236.c driver */
13788c2ecf20Sopenharmony_ci	{ .name = "ft6236", .driver_data = (long)&edt_ft6236_data },
13798c2ecf20Sopenharmony_ci	{ /* sentinel */ }
13808c2ecf20Sopenharmony_ci};
13818c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id);
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_cistatic const struct of_device_id edt_ft5x06_of_match[] = {
13848c2ecf20Sopenharmony_ci	{ .compatible = "edt,edt-ft5206", .data = &edt_ft5x06_data },
13858c2ecf20Sopenharmony_ci	{ .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data },
13868c2ecf20Sopenharmony_ci	{ .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data },
13878c2ecf20Sopenharmony_ci	{ .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data },
13888c2ecf20Sopenharmony_ci	{ .compatible = "evervision,ev-ft5726", .data = &edt_ft5506_data },
13898c2ecf20Sopenharmony_ci	/* Note focaltech vendor prefix for compatibility with ft6236.c */
13908c2ecf20Sopenharmony_ci	{ .compatible = "focaltech,ft6236", .data = &edt_ft6236_data },
13918c2ecf20Sopenharmony_ci	{ /* sentinel */ }
13928c2ecf20Sopenharmony_ci};
13938c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, edt_ft5x06_of_match);
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_cistatic struct i2c_driver edt_ft5x06_ts_driver = {
13968c2ecf20Sopenharmony_ci	.driver = {
13978c2ecf20Sopenharmony_ci		.name = "edt_ft5x06",
13988c2ecf20Sopenharmony_ci		.of_match_table = edt_ft5x06_of_match,
13998c2ecf20Sopenharmony_ci		.pm = &edt_ft5x06_ts_pm_ops,
14008c2ecf20Sopenharmony_ci		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
14018c2ecf20Sopenharmony_ci	},
14028c2ecf20Sopenharmony_ci	.id_table = edt_ft5x06_ts_id,
14038c2ecf20Sopenharmony_ci	.probe    = edt_ft5x06_ts_probe,
14048c2ecf20Sopenharmony_ci	.remove   = edt_ft5x06_ts_remove,
14058c2ecf20Sopenharmony_ci};
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_cimodule_i2c_driver(edt_ft5x06_ts_driver);
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ciMODULE_AUTHOR("Simon Budig <simon.budig@kernelconcepts.de>");
14108c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("EDT FT5x06 I2C Touchscreen Driver");
14118c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1412