16d3a41abSAndy Shevchenko // SPDX-License-Identifier: GPL-2.0
243c4d13eSSimon Budig /*
343c4d13eSSimon Budig  * Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de>
4fd335ab0SLothar Waßmann  * Daniel Wagener <daniel.wagener@kernelconcepts.de> (M09 firmware support)
5dac90dc2SLothar Waßmann  * Lothar Waßmann <LW@KARO-electronics.de> (DT support)
643c4d13eSSimon Budig  */
743c4d13eSSimon Budig 
843c4d13eSSimon Budig /*
943c4d13eSSimon Budig  * This is a driver for the EDT "Polytouch" family of touch controllers
1043c4d13eSSimon Budig  * based on the FocalTech FT5x06 line of chips.
1143c4d13eSSimon Budig  *
1243c4d13eSSimon Budig  * Development of this driver has been sponsored by Glyn:
1343c4d13eSSimon Budig  *    http://www.glyn.com/Products/Displays
1443c4d13eSSimon Budig  */
1543c4d13eSSimon Budig 
168726e4c9SMarco Felsch #include <linux/debugfs.h>
178726e4c9SMarco Felsch #include <linux/delay.h>
188726e4c9SMarco Felsch #include <linux/gpio/consumer.h>
198726e4c9SMarco Felsch #include <linux/i2c.h>
2043c4d13eSSimon Budig #include <linux/interrupt.h>
2143c4d13eSSimon Budig #include <linux/input.h>
2243c4d13eSSimon Budig #include <linux/input/mt.h>
232c005598SMaxime Ripard #include <linux/input/touchscreen.h>
248726e4c9SMarco Felsch #include <linux/irq.h>
258726e4c9SMarco Felsch #include <linux/kernel.h>
268726e4c9SMarco Felsch #include <linux/module.h>
278726e4c9SMarco Felsch #include <linux/ratelimit.h>
287448bfecSMylène Josserand #include <linux/regulator/consumer.h>
298726e4c9SMarco Felsch #include <linux/slab.h>
308726e4c9SMarco Felsch #include <linux/uaccess.h>
318726e4c9SMarco Felsch 
328726e4c9SMarco Felsch #include <asm/unaligned.h>
3343c4d13eSSimon Budig 
3443c4d13eSSimon Budig #define WORK_REGISTER_THRESHOLD		0x00
3543c4d13eSSimon Budig #define WORK_REGISTER_REPORT_RATE	0x08
3643c4d13eSSimon Budig #define WORK_REGISTER_GAIN		0x30
3743c4d13eSSimon Budig #define WORK_REGISTER_OFFSET		0x31
3843c4d13eSSimon Budig #define WORK_REGISTER_NUM_X		0x33
3943c4d13eSSimon Budig #define WORK_REGISTER_NUM_Y		0x34
4043c4d13eSSimon Budig 
4121d1611aSMarco Felsch #define PMOD_REGISTER_ACTIVE		0x00
4221d1611aSMarco Felsch #define PMOD_REGISTER_HIBERNATE		0x03
4321d1611aSMarco Felsch 
44fd335ab0SLothar Waßmann #define M09_REGISTER_THRESHOLD		0x80
45fd335ab0SLothar Waßmann #define M09_REGISTER_GAIN		0x92
46fd335ab0SLothar Waßmann #define M09_REGISTER_OFFSET		0x93
47fd335ab0SLothar Waßmann #define M09_REGISTER_NUM_X		0x94
48fd335ab0SLothar Waßmann #define M09_REGISTER_NUM_Y		0x95
49fd335ab0SLothar Waßmann 
50a2f39dacSMarco Felsch #define EV_REGISTER_THRESHOLD		0x40
51a2f39dacSMarco Felsch #define EV_REGISTER_GAIN		0x41
52b6eba860SMarco Felsch #define EV_REGISTER_OFFSET_Y		0x45
53b6eba860SMarco Felsch #define EV_REGISTER_OFFSET_X		0x46
54a2f39dacSMarco Felsch 
55fd335ab0SLothar Waßmann #define NO_REGISTER			0xff
56fd335ab0SLothar Waßmann 
5743c4d13eSSimon Budig #define WORK_REGISTER_OPMODE		0x3c
5843c4d13eSSimon Budig #define FACTORY_REGISTER_OPMODE		0x01
5921d1611aSMarco Felsch #define PMOD_REGISTER_OPMODE		0xa5
6043c4d13eSSimon Budig 
6143c4d13eSSimon Budig #define TOUCH_EVENT_DOWN		0x00
6243c4d13eSSimon Budig #define TOUCH_EVENT_UP			0x01
6343c4d13eSSimon Budig #define TOUCH_EVENT_ON			0x02
6443c4d13eSSimon Budig #define TOUCH_EVENT_RESERVED		0x03
6543c4d13eSSimon Budig 
6643c4d13eSSimon Budig #define EDT_NAME_LEN			23
6743c4d13eSSimon Budig #define EDT_SWITCH_MODE_RETRIES		10
6843c4d13eSSimon Budig #define EDT_SWITCH_MODE_DELAY		5 /* msec */
6943c4d13eSSimon Budig #define EDT_RAW_DATA_RETRIES		100
700eeecf60SAniroop Mathur #define EDT_RAW_DATA_DELAY		1000 /* usec */
7143c4d13eSSimon Budig 
7221d1611aSMarco Felsch enum edt_pmode {
7321d1611aSMarco Felsch 	EDT_PMODE_NOT_SUPPORTED,
7421d1611aSMarco Felsch 	EDT_PMODE_HIBERNATE,
7521d1611aSMarco Felsch 	EDT_PMODE_POWEROFF,
7621d1611aSMarco Felsch };
7721d1611aSMarco Felsch 
78fd335ab0SLothar Waßmann enum edt_ver {
79169110c3SSimon Budig 	EDT_M06,
80169110c3SSimon Budig 	EDT_M09,
81aed5d0eeSSimon Budig 	EDT_M12,
82a2f39dacSMarco Felsch 	EV_FT,
83169110c3SSimon Budig 	GENERIC_FT,
84fd335ab0SLothar Waßmann };
85fd335ab0SLothar Waßmann 
86fd335ab0SLothar Waßmann struct edt_reg_addr {
87fd335ab0SLothar Waßmann 	int reg_threshold;
88fd335ab0SLothar Waßmann 	int reg_report_rate;
89fd335ab0SLothar Waßmann 	int reg_gain;
90fd335ab0SLothar Waßmann 	int reg_offset;
91b6eba860SMarco Felsch 	int reg_offset_x;
92b6eba860SMarco Felsch 	int reg_offset_y;
93fd335ab0SLothar Waßmann 	int reg_num_x;
94fd335ab0SLothar Waßmann 	int reg_num_y;
95fd335ab0SLothar Waßmann };
96fd335ab0SLothar Waßmann 
9743c4d13eSSimon Budig struct edt_ft5x06_ts_data {
9843c4d13eSSimon Budig 	struct i2c_client *client;
9943c4d13eSSimon Budig 	struct input_dev *input;
100ad368eb2SHans de Goede 	struct touchscreen_properties prop;
10143c4d13eSSimon Budig 	u16 num_x;
10243c4d13eSSimon Budig 	u16 num_y;
1037448bfecSMylène Josserand 	struct regulator *vcc;
10443c4d13eSSimon Budig 
10513c23cd1SFranklin S Cooper Jr 	struct gpio_desc *reset_gpio;
10613c23cd1SFranklin S Cooper Jr 	struct gpio_desc *wake_gpio;
107dac90dc2SLothar Waßmann 
10843c4d13eSSimon Budig #if defined(CONFIG_DEBUG_FS)
10943c4d13eSSimon Budig 	struct dentry *debug_dir;
11043c4d13eSSimon Budig 	u8 *raw_buffer;
11143c4d13eSSimon Budig 	size_t raw_bufsize;
11243c4d13eSSimon Budig #endif
11343c4d13eSSimon Budig 
11443c4d13eSSimon Budig 	struct mutex mutex;
11543c4d13eSSimon Budig 	bool factory_mode;
11621d1611aSMarco Felsch 	enum edt_pmode suspend_mode;
11743c4d13eSSimon Budig 	int threshold;
11843c4d13eSSimon Budig 	int gain;
11943c4d13eSSimon Budig 	int offset;
120b6eba860SMarco Felsch 	int offset_x;
121b6eba860SMarco Felsch 	int offset_y;
12243c4d13eSSimon Budig 	int report_rate;
123b1d2a3ecSFranklin S Cooper Jr 	int max_support_points;
12443c4d13eSSimon Budig 
12543c4d13eSSimon Budig 	char name[EDT_NAME_LEN];
126fd335ab0SLothar Waßmann 
127fd335ab0SLothar Waßmann 	struct edt_reg_addr reg_addr;
128fd335ab0SLothar Waßmann 	enum edt_ver version;
12943c4d13eSSimon Budig };
13043c4d13eSSimon Budig 
131b1d2a3ecSFranklin S Cooper Jr struct edt_i2c_chip_data {
132b1d2a3ecSFranklin S Cooper Jr 	int  max_support_points;
133b1d2a3ecSFranklin S Cooper Jr };
134b1d2a3ecSFranklin S Cooper Jr 
13543c4d13eSSimon Budig static int edt_ft5x06_ts_readwrite(struct i2c_client *client,
13643c4d13eSSimon Budig 				   u16 wr_len, u8 *wr_buf,
13743c4d13eSSimon Budig 				   u16 rd_len, u8 *rd_buf)
13843c4d13eSSimon Budig {
13943c4d13eSSimon Budig 	struct i2c_msg wrmsg[2];
14043c4d13eSSimon Budig 	int i = 0;
14143c4d13eSSimon Budig 	int ret;
14243c4d13eSSimon Budig 
14343c4d13eSSimon Budig 	if (wr_len) {
14443c4d13eSSimon Budig 		wrmsg[i].addr  = client->addr;
14543c4d13eSSimon Budig 		wrmsg[i].flags = 0;
14643c4d13eSSimon Budig 		wrmsg[i].len = wr_len;
14743c4d13eSSimon Budig 		wrmsg[i].buf = wr_buf;
14843c4d13eSSimon Budig 		i++;
14943c4d13eSSimon Budig 	}
15043c4d13eSSimon Budig 	if (rd_len) {
15143c4d13eSSimon Budig 		wrmsg[i].addr  = client->addr;
15243c4d13eSSimon Budig 		wrmsg[i].flags = I2C_M_RD;
15343c4d13eSSimon Budig 		wrmsg[i].len = rd_len;
15443c4d13eSSimon Budig 		wrmsg[i].buf = rd_buf;
15543c4d13eSSimon Budig 		i++;
15643c4d13eSSimon Budig 	}
15743c4d13eSSimon Budig 
15843c4d13eSSimon Budig 	ret = i2c_transfer(client->adapter, wrmsg, i);
15943c4d13eSSimon Budig 	if (ret < 0)
16043c4d13eSSimon Budig 		return ret;
16143c4d13eSSimon Budig 	if (ret != i)
16243c4d13eSSimon Budig 		return -EIO;
16343c4d13eSSimon Budig 
16443c4d13eSSimon Budig 	return 0;
16543c4d13eSSimon Budig }
16643c4d13eSSimon Budig 
16743c4d13eSSimon Budig static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata,
16843c4d13eSSimon Budig 				    u8 *buf, int buflen)
16943c4d13eSSimon Budig {
17043c4d13eSSimon Budig 	int i;
17143c4d13eSSimon Budig 	u8 crc = 0;
17243c4d13eSSimon Budig 
17343c4d13eSSimon Budig 	for (i = 0; i < buflen - 1; i++)
17443c4d13eSSimon Budig 		crc ^= buf[i];
17543c4d13eSSimon Budig 
17643c4d13eSSimon Budig 	if (crc != buf[buflen-1]) {
17743c4d13eSSimon Budig 		dev_err_ratelimited(&tsdata->client->dev,
17843c4d13eSSimon Budig 				    "crc error: 0x%02x expected, got 0x%02x\n",
17943c4d13eSSimon Budig 				    crc, buf[buflen-1]);
18043c4d13eSSimon Budig 		return false;
18143c4d13eSSimon Budig 	}
18243c4d13eSSimon Budig 
18343c4d13eSSimon Budig 	return true;
18443c4d13eSSimon Budig }
18543c4d13eSSimon Budig 
18643c4d13eSSimon Budig static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
18743c4d13eSSimon Budig {
18843c4d13eSSimon Budig 	struct edt_ft5x06_ts_data *tsdata = dev_id;
18943c4d13eSSimon Budig 	struct device *dev = &tsdata->client->dev;
190fd335ab0SLothar Waßmann 	u8 cmd;
1919378c025SFranklin S Cooper Jr 	u8 rdbuf[63];
19243c4d13eSSimon Budig 	int i, type, x, y, id;
193c789f1fbSFranklin S Cooper Jr 	int offset, tplen, datalen, crclen;
19443c4d13eSSimon Budig 	int error;
19543c4d13eSSimon Budig 
196fd335ab0SLothar Waßmann 	switch (tsdata->version) {
197169110c3SSimon Budig 	case EDT_M06:
198fd335ab0SLothar Waßmann 		cmd = 0xf9; /* tell the controller to send touch data */
199fd335ab0SLothar Waßmann 		offset = 5; /* where the actual touch data starts */
200fd335ab0SLothar Waßmann 		tplen = 4;  /* data comes in so called frames */
201c789f1fbSFranklin S Cooper Jr 		crclen = 1; /* length of the crc data */
202fd335ab0SLothar Waßmann 		break;
203fd335ab0SLothar Waßmann 
204169110c3SSimon Budig 	case EDT_M09:
205aed5d0eeSSimon Budig 	case EDT_M12:
206a2f39dacSMarco Felsch 	case EV_FT:
207169110c3SSimon Budig 	case GENERIC_FT:
2089378c025SFranklin S Cooper Jr 		cmd = 0x0;
2099378c025SFranklin S Cooper Jr 		offset = 3;
210fd335ab0SLothar Waßmann 		tplen = 6;
211c789f1fbSFranklin S Cooper Jr 		crclen = 0;
212fd335ab0SLothar Waßmann 		break;
213fd335ab0SLothar Waßmann 
214fd335ab0SLothar Waßmann 	default:
215fd335ab0SLothar Waßmann 		goto out;
216fd335ab0SLothar Waßmann 	}
217fd335ab0SLothar Waßmann 
21843c4d13eSSimon Budig 	memset(rdbuf, 0, sizeof(rdbuf));
219b1d2a3ecSFranklin S Cooper Jr 	datalen = tplen * tsdata->max_support_points + offset + crclen;
22043c4d13eSSimon Budig 
22143c4d13eSSimon Budig 	error = edt_ft5x06_ts_readwrite(tsdata->client,
22243c4d13eSSimon Budig 					sizeof(cmd), &cmd,
223fd335ab0SLothar Waßmann 					datalen, rdbuf);
22443c4d13eSSimon Budig 	if (error) {
22543c4d13eSSimon Budig 		dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n",
22643c4d13eSSimon Budig 				    error);
22743c4d13eSSimon Budig 		goto out;
22843c4d13eSSimon Budig 	}
22943c4d13eSSimon Budig 
230aed5d0eeSSimon Budig 	/* M09/M12 does not send header or CRC */
231169110c3SSimon Budig 	if (tsdata->version == EDT_M06) {
232fd335ab0SLothar Waßmann 		if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa ||
233fd335ab0SLothar Waßmann 			rdbuf[2] != datalen) {
234fd335ab0SLothar Waßmann 			dev_err_ratelimited(dev,
235fd335ab0SLothar Waßmann 					"Unexpected header: %02x%02x%02x!\n",
23643c4d13eSSimon Budig 					rdbuf[0], rdbuf[1], rdbuf[2]);
23743c4d13eSSimon Budig 			goto out;
23843c4d13eSSimon Budig 		}
23943c4d13eSSimon Budig 
240fd335ab0SLothar Waßmann 		if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, datalen))
24143c4d13eSSimon Budig 			goto out;
242fd335ab0SLothar Waßmann 	}
24343c4d13eSSimon Budig 
244b1d2a3ecSFranklin S Cooper Jr 	for (i = 0; i < tsdata->max_support_points; i++) {
245fd335ab0SLothar Waßmann 		u8 *buf = &rdbuf[i * tplen + offset];
24643c4d13eSSimon Budig 
24743c4d13eSSimon Budig 		type = buf[0] >> 6;
24843c4d13eSSimon Budig 		/* ignore Reserved events */
24943c4d13eSSimon Budig 		if (type == TOUCH_EVENT_RESERVED)
25043c4d13eSSimon Budig 			continue;
25143c4d13eSSimon Budig 
252fd335ab0SLothar Waßmann 		/* M06 sometimes sends bogus coordinates in TOUCH_DOWN */
253169110c3SSimon Budig 		if (tsdata->version == EDT_M06 && type == TOUCH_EVENT_DOWN)
254ee3e946eSLothar Waßmann 			continue;
255ee3e946eSLothar Waßmann 
2561b9c698cSDmitry Torokhov 		x = get_unaligned_be16(buf) & 0x0fff;
2571b9c698cSDmitry Torokhov 		y = get_unaligned_be16(buf + 2) & 0x0fff;
258a2f39dacSMarco Felsch 		/* The FT5x26 send the y coordinate first */
259a2f39dacSMarco Felsch 		if (tsdata->version == EV_FT)
260a2f39dacSMarco Felsch 			swap(x, y);
261a2f39dacSMarco Felsch 
26243c4d13eSSimon Budig 		id = (buf[2] >> 4) & 0x0f;
26343c4d13eSSimon Budig 
26443c4d13eSSimon Budig 		input_mt_slot(tsdata->input, id);
26517b92927SDmitry Torokhov 		if (input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER,
26617b92927SDmitry Torokhov 					       type != TOUCH_EVENT_UP))
26717b92927SDmitry Torokhov 			touchscreen_report_pos(tsdata->input, &tsdata->prop,
26817b92927SDmitry Torokhov 					       x, y, true);
26943c4d13eSSimon Budig 	}
27043c4d13eSSimon Budig 
27143c4d13eSSimon Budig 	input_mt_report_pointer_emulation(tsdata->input, true);
27243c4d13eSSimon Budig 	input_sync(tsdata->input);
27343c4d13eSSimon Budig 
27443c4d13eSSimon Budig out:
27543c4d13eSSimon Budig 	return IRQ_HANDLED;
27643c4d13eSSimon Budig }
27743c4d13eSSimon Budig 
27843c4d13eSSimon Budig static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata,
27943c4d13eSSimon Budig 				     u8 addr, u8 value)
28043c4d13eSSimon Budig {
28143c4d13eSSimon Budig 	u8 wrbuf[4];
28243c4d13eSSimon Budig 
283fd335ab0SLothar Waßmann 	switch (tsdata->version) {
284169110c3SSimon Budig 	case EDT_M06:
28543c4d13eSSimon Budig 		wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
28643c4d13eSSimon Budig 		wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
28743c4d13eSSimon Budig 		wrbuf[2] = value;
28843c4d13eSSimon Budig 		wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
289fd335ab0SLothar Waßmann 		return edt_ft5x06_ts_readwrite(tsdata->client, 4,
290fd335ab0SLothar Waßmann 					wrbuf, 0, NULL);
291a2f39dacSMarco Felsch 	/* fallthrough */
292169110c3SSimon Budig 	case EDT_M09:
293aed5d0eeSSimon Budig 	case EDT_M12:
294a2f39dacSMarco Felsch 	case EV_FT:
295169110c3SSimon Budig 	case GENERIC_FT:
296fd335ab0SLothar Waßmann 		wrbuf[0] = addr;
297fd335ab0SLothar Waßmann 		wrbuf[1] = value;
29843c4d13eSSimon Budig 
299cc071acaSRobert Woerle 		return edt_ft5x06_ts_readwrite(tsdata->client, 2,
300fd335ab0SLothar Waßmann 					wrbuf, 0, NULL);
301fd335ab0SLothar Waßmann 
302fd335ab0SLothar Waßmann 	default:
303fd335ab0SLothar Waßmann 		return -EINVAL;
304fd335ab0SLothar Waßmann 	}
30543c4d13eSSimon Budig }
30643c4d13eSSimon Budig 
30743c4d13eSSimon Budig static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata,
30843c4d13eSSimon Budig 				    u8 addr)
30943c4d13eSSimon Budig {
31043c4d13eSSimon Budig 	u8 wrbuf[2], rdbuf[2];
31143c4d13eSSimon Budig 	int error;
31243c4d13eSSimon Budig 
313fd335ab0SLothar Waßmann 	switch (tsdata->version) {
314169110c3SSimon Budig 	case EDT_M06:
31543c4d13eSSimon Budig 		wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
31643c4d13eSSimon Budig 		wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
31743c4d13eSSimon Budig 		wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40;
31843c4d13eSSimon Budig 
319e2c3ecf0SDan Carpenter 		error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2,
320e2c3ecf0SDan Carpenter 						rdbuf);
321e2c3ecf0SDan Carpenter 		if (error)
32243c4d13eSSimon Budig 			return error;
32343c4d13eSSimon Budig 
32443c4d13eSSimon Budig 		if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) {
32543c4d13eSSimon Budig 			dev_err(&tsdata->client->dev,
32643c4d13eSSimon Budig 				"crc error: 0x%02x expected, got 0x%02x\n",
327fd335ab0SLothar Waßmann 				wrbuf[0] ^ wrbuf[1] ^ rdbuf[0],
328fd335ab0SLothar Waßmann 				rdbuf[1]);
32943c4d13eSSimon Budig 			return -EIO;
33043c4d13eSSimon Budig 		}
331fd335ab0SLothar Waßmann 		break;
332fd335ab0SLothar Waßmann 
333a2f39dacSMarco Felsch 	/* fallthrough */
334169110c3SSimon Budig 	case EDT_M09:
335aed5d0eeSSimon Budig 	case EDT_M12:
336a2f39dacSMarco Felsch 	case EV_FT:
337169110c3SSimon Budig 	case GENERIC_FT:
338fd335ab0SLothar Waßmann 		wrbuf[0] = addr;
339fd335ab0SLothar Waßmann 		error = edt_ft5x06_ts_readwrite(tsdata->client, 1,
340fd335ab0SLothar Waßmann 						wrbuf, 1, rdbuf);
341fd335ab0SLothar Waßmann 		if (error)
342fd335ab0SLothar Waßmann 			return error;
343fd335ab0SLothar Waßmann 		break;
344fd335ab0SLothar Waßmann 
345fd335ab0SLothar Waßmann 	default:
346fd335ab0SLothar Waßmann 		return -EINVAL;
347fd335ab0SLothar Waßmann 	}
34843c4d13eSSimon Budig 
34943c4d13eSSimon Budig 	return rdbuf[0];
35043c4d13eSSimon Budig }
35143c4d13eSSimon Budig 
35243c4d13eSSimon Budig struct edt_ft5x06_attribute {
35343c4d13eSSimon Budig 	struct device_attribute dattr;
35443c4d13eSSimon Budig 	size_t field_offset;
35543c4d13eSSimon Budig 	u8 limit_low;
35643c4d13eSSimon Budig 	u8 limit_high;
357fd335ab0SLothar Waßmann 	u8 addr_m06;
358fd335ab0SLothar Waßmann 	u8 addr_m09;
3592ebc1919SMarco Felsch 	u8 addr_ev;
36043c4d13eSSimon Budig };
36143c4d13eSSimon Budig 
3622ebc1919SMarco Felsch #define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, _addr_ev,		\
363fd335ab0SLothar Waßmann 		_limit_low, _limit_high)				\
36443c4d13eSSimon Budig 	struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = {	\
36543c4d13eSSimon Budig 		.dattr = __ATTR(_field, _mode,				\
36643c4d13eSSimon Budig 				edt_ft5x06_setting_show,		\
36743c4d13eSSimon Budig 				edt_ft5x06_setting_store),		\
368fd335ab0SLothar Waßmann 		.field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \
369fd335ab0SLothar Waßmann 		.addr_m06 = _addr_m06,					\
370fd335ab0SLothar Waßmann 		.addr_m09 = _addr_m09,					\
3712ebc1919SMarco Felsch 		.addr_ev  = _addr_ev,					\
37243c4d13eSSimon Budig 		.limit_low = _limit_low,				\
37343c4d13eSSimon Budig 		.limit_high = _limit_high,				\
37443c4d13eSSimon Budig 	}
37543c4d13eSSimon Budig 
37643c4d13eSSimon Budig static ssize_t edt_ft5x06_setting_show(struct device *dev,
37743c4d13eSSimon Budig 				       struct device_attribute *dattr,
37843c4d13eSSimon Budig 				       char *buf)
37943c4d13eSSimon Budig {
38043c4d13eSSimon Budig 	struct i2c_client *client = to_i2c_client(dev);
38143c4d13eSSimon Budig 	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
38243c4d13eSSimon Budig 	struct edt_ft5x06_attribute *attr =
38343c4d13eSSimon Budig 			container_of(dattr, struct edt_ft5x06_attribute, dattr);
3841730d814SLothar Waßmann 	u8 *field = (u8 *)tsdata + attr->field_offset;
38543c4d13eSSimon Budig 	int val;
38643c4d13eSSimon Budig 	size_t count = 0;
38743c4d13eSSimon Budig 	int error = 0;
388fd335ab0SLothar Waßmann 	u8 addr;
38943c4d13eSSimon Budig 
39043c4d13eSSimon Budig 	mutex_lock(&tsdata->mutex);
39143c4d13eSSimon Budig 
39243c4d13eSSimon Budig 	if (tsdata->factory_mode) {
39343c4d13eSSimon Budig 		error = -EIO;
39443c4d13eSSimon Budig 		goto out;
39543c4d13eSSimon Budig 	}
39643c4d13eSSimon Budig 
397fd335ab0SLothar Waßmann 	switch (tsdata->version) {
398169110c3SSimon Budig 	case EDT_M06:
399fd335ab0SLothar Waßmann 		addr = attr->addr_m06;
400fd335ab0SLothar Waßmann 		break;
401fd335ab0SLothar Waßmann 
402169110c3SSimon Budig 	case EDT_M09:
403aed5d0eeSSimon Budig 	case EDT_M12:
404169110c3SSimon Budig 	case GENERIC_FT:
405fd335ab0SLothar Waßmann 		addr = attr->addr_m09;
406fd335ab0SLothar Waßmann 		break;
407fd335ab0SLothar Waßmann 
4082ebc1919SMarco Felsch 	case EV_FT:
4092ebc1919SMarco Felsch 		addr = attr->addr_ev;
4102ebc1919SMarco Felsch 		break;
4112ebc1919SMarco Felsch 
412fd335ab0SLothar Waßmann 	default:
413fd335ab0SLothar Waßmann 		error = -ENODEV;
414fd335ab0SLothar Waßmann 		goto out;
415fd335ab0SLothar Waßmann 	}
416fd335ab0SLothar Waßmann 
417fd335ab0SLothar Waßmann 	if (addr != NO_REGISTER) {
418fd335ab0SLothar Waßmann 		val = edt_ft5x06_register_read(tsdata, addr);
41943c4d13eSSimon Budig 		if (val < 0) {
42043c4d13eSSimon Budig 			error = val;
42143c4d13eSSimon Budig 			dev_err(&tsdata->client->dev,
42243c4d13eSSimon Budig 				"Failed to fetch attribute %s, error %d\n",
42343c4d13eSSimon Budig 				dattr->attr.name, error);
42443c4d13eSSimon Budig 			goto out;
42543c4d13eSSimon Budig 		}
426fd335ab0SLothar Waßmann 	} else {
427fd335ab0SLothar Waßmann 		val = *field;
428fd335ab0SLothar Waßmann 	}
42943c4d13eSSimon Budig 
43043c4d13eSSimon Budig 	if (val != *field) {
43143c4d13eSSimon Budig 		dev_warn(&tsdata->client->dev,
43243c4d13eSSimon Budig 			 "%s: read (%d) and stored value (%d) differ\n",
43343c4d13eSSimon Budig 			 dattr->attr.name, val, *field);
43443c4d13eSSimon Budig 		*field = val;
43543c4d13eSSimon Budig 	}
43643c4d13eSSimon Budig 
43743c4d13eSSimon Budig 	count = scnprintf(buf, PAGE_SIZE, "%d\n", val);
43843c4d13eSSimon Budig out:
43943c4d13eSSimon Budig 	mutex_unlock(&tsdata->mutex);
44043c4d13eSSimon Budig 	return error ?: count;
44143c4d13eSSimon Budig }
44243c4d13eSSimon Budig 
44343c4d13eSSimon Budig static ssize_t edt_ft5x06_setting_store(struct device *dev,
44443c4d13eSSimon Budig 					struct device_attribute *dattr,
44543c4d13eSSimon Budig 					const char *buf, size_t count)
44643c4d13eSSimon Budig {
44743c4d13eSSimon Budig 	struct i2c_client *client = to_i2c_client(dev);
44843c4d13eSSimon Budig 	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
44943c4d13eSSimon Budig 	struct edt_ft5x06_attribute *attr =
45043c4d13eSSimon Budig 			container_of(dattr, struct edt_ft5x06_attribute, dattr);
4511730d814SLothar Waßmann 	u8 *field = (u8 *)tsdata + attr->field_offset;
45243c4d13eSSimon Budig 	unsigned int val;
45343c4d13eSSimon Budig 	int error;
454fd335ab0SLothar Waßmann 	u8 addr;
45543c4d13eSSimon Budig 
45643c4d13eSSimon Budig 	mutex_lock(&tsdata->mutex);
45743c4d13eSSimon Budig 
45843c4d13eSSimon Budig 	if (tsdata->factory_mode) {
45943c4d13eSSimon Budig 		error = -EIO;
46043c4d13eSSimon Budig 		goto out;
46143c4d13eSSimon Budig 	}
46243c4d13eSSimon Budig 
46343c4d13eSSimon Budig 	error = kstrtouint(buf, 0, &val);
46443c4d13eSSimon Budig 	if (error)
46543c4d13eSSimon Budig 		goto out;
46643c4d13eSSimon Budig 
46743c4d13eSSimon Budig 	if (val < attr->limit_low || val > attr->limit_high) {
46843c4d13eSSimon Budig 		error = -ERANGE;
46943c4d13eSSimon Budig 		goto out;
47043c4d13eSSimon Budig 	}
47143c4d13eSSimon Budig 
472fd335ab0SLothar Waßmann 	switch (tsdata->version) {
473169110c3SSimon Budig 	case EDT_M06:
474fd335ab0SLothar Waßmann 		addr = attr->addr_m06;
475fd335ab0SLothar Waßmann 		break;
476fd335ab0SLothar Waßmann 
477169110c3SSimon Budig 	case EDT_M09:
478aed5d0eeSSimon Budig 	case EDT_M12:
479169110c3SSimon Budig 	case GENERIC_FT:
480fd335ab0SLothar Waßmann 		addr = attr->addr_m09;
481fd335ab0SLothar Waßmann 		break;
482fd335ab0SLothar Waßmann 
4832ebc1919SMarco Felsch 	case EV_FT:
4842ebc1919SMarco Felsch 		addr = attr->addr_ev;
4852ebc1919SMarco Felsch 		break;
4862ebc1919SMarco Felsch 
487fd335ab0SLothar Waßmann 	default:
488fd335ab0SLothar Waßmann 		error = -ENODEV;
489fd335ab0SLothar Waßmann 		goto out;
490fd335ab0SLothar Waßmann 	}
491fd335ab0SLothar Waßmann 
492fd335ab0SLothar Waßmann 	if (addr != NO_REGISTER) {
493fd335ab0SLothar Waßmann 		error = edt_ft5x06_register_write(tsdata, addr, val);
49443c4d13eSSimon Budig 		if (error) {
49543c4d13eSSimon Budig 			dev_err(&tsdata->client->dev,
49643c4d13eSSimon Budig 				"Failed to update attribute %s, error: %d\n",
49743c4d13eSSimon Budig 				dattr->attr.name, error);
49843c4d13eSSimon Budig 			goto out;
49943c4d13eSSimon Budig 		}
500fd335ab0SLothar Waßmann 	}
50143c4d13eSSimon Budig 	*field = val;
50243c4d13eSSimon Budig 
50343c4d13eSSimon Budig out:
50443c4d13eSSimon Budig 	mutex_unlock(&tsdata->mutex);
50543c4d13eSSimon Budig 	return error ?: count;
50643c4d13eSSimon Budig }
50743c4d13eSSimon Budig 
508aed5d0eeSSimon Budig /* m06, m09: range 0-31, m12: range 0-5 */
509fd335ab0SLothar Waßmann static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN,
5102ebc1919SMarco Felsch 		M09_REGISTER_GAIN, EV_REGISTER_GAIN, 0, 31);
511aed5d0eeSSimon Budig /* m06, m09: range 0-31, m12: range 0-16 */
512fd335ab0SLothar Waßmann static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET,
5132ebc1919SMarco Felsch 		M09_REGISTER_OFFSET, NO_REGISTER, 0, 31);
514b6eba860SMarco Felsch /* m06, m09, m12: no supported, ev_ft: range 0-80 */
515b6eba860SMarco Felsch static EDT_ATTR(offset_x, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER,
516b6eba860SMarco Felsch 		EV_REGISTER_OFFSET_X, 0, 80);
517b6eba860SMarco Felsch /* m06, m09, m12: no supported, ev_ft: range 0-80 */
518b6eba860SMarco Felsch static EDT_ATTR(offset_y, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER,
519b6eba860SMarco Felsch 		EV_REGISTER_OFFSET_Y, 0, 80);
520aed5d0eeSSimon Budig /* m06: range 20 to 80, m09: range 0 to 30, m12: range 1 to 255... */
521fd335ab0SLothar Waßmann static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD,
5222ebc1919SMarco Felsch 		M09_REGISTER_THRESHOLD, EV_REGISTER_THRESHOLD, 0, 255);
523aed5d0eeSSimon Budig /* m06: range 3 to 14, m12: (0x64: 100Hz) */
524fd335ab0SLothar Waßmann static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE,
5252ebc1919SMarco Felsch 		NO_REGISTER, NO_REGISTER, 0, 255);
52643c4d13eSSimon Budig 
52743c4d13eSSimon Budig static struct attribute *edt_ft5x06_attrs[] = {
52843c4d13eSSimon Budig 	&edt_ft5x06_attr_gain.dattr.attr,
52943c4d13eSSimon Budig 	&edt_ft5x06_attr_offset.dattr.attr,
530b6eba860SMarco Felsch 	&edt_ft5x06_attr_offset_x.dattr.attr,
531b6eba860SMarco Felsch 	&edt_ft5x06_attr_offset_y.dattr.attr,
53243c4d13eSSimon Budig 	&edt_ft5x06_attr_threshold.dattr.attr,
53343c4d13eSSimon Budig 	&edt_ft5x06_attr_report_rate.dattr.attr,
53443c4d13eSSimon Budig 	NULL
53543c4d13eSSimon Budig };
53643c4d13eSSimon Budig 
53743c4d13eSSimon Budig static const struct attribute_group edt_ft5x06_attr_group = {
53843c4d13eSSimon Budig 	.attrs = edt_ft5x06_attrs,
53943c4d13eSSimon Budig };
54043c4d13eSSimon Budig 
541f4ee52f3SMarco Felsch static void edt_ft5x06_restore_reg_parameters(struct edt_ft5x06_ts_data *tsdata)
542f4ee52f3SMarco Felsch {
543f4ee52f3SMarco Felsch 	struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
544f4ee52f3SMarco Felsch 
545f4ee52f3SMarco Felsch 	edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold,
546f4ee52f3SMarco Felsch 				  tsdata->threshold);
547f4ee52f3SMarco Felsch 	edt_ft5x06_register_write(tsdata, reg_addr->reg_gain,
548f4ee52f3SMarco Felsch 				  tsdata->gain);
549f4ee52f3SMarco Felsch 	if (reg_addr->reg_offset != NO_REGISTER)
550f4ee52f3SMarco Felsch 		edt_ft5x06_register_write(tsdata, reg_addr->reg_offset,
551f4ee52f3SMarco Felsch 					  tsdata->offset);
552f4ee52f3SMarco Felsch 	if (reg_addr->reg_offset_x != NO_REGISTER)
553f4ee52f3SMarco Felsch 		edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x,
554f4ee52f3SMarco Felsch 					  tsdata->offset_x);
555f4ee52f3SMarco Felsch 	if (reg_addr->reg_offset_y != NO_REGISTER)
556f4ee52f3SMarco Felsch 		edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y,
557f4ee52f3SMarco Felsch 					  tsdata->offset_y);
558f4ee52f3SMarco Felsch 	if (reg_addr->reg_report_rate != NO_REGISTER)
559f4ee52f3SMarco Felsch 		edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate,
560f4ee52f3SMarco Felsch 				  tsdata->report_rate);
561f4ee52f3SMarco Felsch 
562f4ee52f3SMarco Felsch }
563f4ee52f3SMarco Felsch 
56443c4d13eSSimon Budig #ifdef CONFIG_DEBUG_FS
56543c4d13eSSimon Budig static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
56643c4d13eSSimon Budig {
56743c4d13eSSimon Budig 	struct i2c_client *client = tsdata->client;
56843c4d13eSSimon Budig 	int retries = EDT_SWITCH_MODE_RETRIES;
56943c4d13eSSimon Budig 	int ret;
57043c4d13eSSimon Budig 	int error;
57143c4d13eSSimon Budig 
5724b3e910dSDmitry Torokhov 	if (tsdata->version != EDT_M06) {
5734b3e910dSDmitry Torokhov 		dev_err(&client->dev,
5744b3e910dSDmitry Torokhov 			"No factory mode support for non-M06 devices\n");
5754b3e910dSDmitry Torokhov 		return -EINVAL;
5764b3e910dSDmitry Torokhov 	}
5774b3e910dSDmitry Torokhov 
57843c4d13eSSimon Budig 	disable_irq(client->irq);
57943c4d13eSSimon Budig 
58043c4d13eSSimon Budig 	if (!tsdata->raw_buffer) {
58143c4d13eSSimon Budig 		tsdata->raw_bufsize = tsdata->num_x * tsdata->num_y *
58243c4d13eSSimon Budig 				      sizeof(u16);
58343c4d13eSSimon Budig 		tsdata->raw_buffer = kzalloc(tsdata->raw_bufsize, GFP_KERNEL);
58443c4d13eSSimon Budig 		if (!tsdata->raw_buffer) {
58543c4d13eSSimon Budig 			error = -ENOMEM;
58643c4d13eSSimon Budig 			goto err_out;
58743c4d13eSSimon Budig 		}
58843c4d13eSSimon Budig 	}
58943c4d13eSSimon Budig 
59043c4d13eSSimon Budig 	/* mode register is 0x3c when in the work mode */
59143c4d13eSSimon Budig 	error = edt_ft5x06_register_write(tsdata, WORK_REGISTER_OPMODE, 0x03);
59243c4d13eSSimon Budig 	if (error) {
59343c4d13eSSimon Budig 		dev_err(&client->dev,
59443c4d13eSSimon Budig 			"failed to switch to factory mode, error %d\n", error);
59543c4d13eSSimon Budig 		goto err_out;
59643c4d13eSSimon Budig 	}
59743c4d13eSSimon Budig 
59843c4d13eSSimon Budig 	tsdata->factory_mode = true;
59943c4d13eSSimon Budig 	do {
60043c4d13eSSimon Budig 		mdelay(EDT_SWITCH_MODE_DELAY);
60143c4d13eSSimon Budig 		/* mode register is 0x01 when in factory mode */
60243c4d13eSSimon Budig 		ret = edt_ft5x06_register_read(tsdata, FACTORY_REGISTER_OPMODE);
60343c4d13eSSimon Budig 		if (ret == 0x03)
60443c4d13eSSimon Budig 			break;
60543c4d13eSSimon Budig 	} while (--retries > 0);
60643c4d13eSSimon Budig 
60743c4d13eSSimon Budig 	if (retries == 0) {
60843c4d13eSSimon Budig 		dev_err(&client->dev, "not in factory mode after %dms.\n",
60943c4d13eSSimon Budig 			EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY);
61043c4d13eSSimon Budig 		error = -EIO;
61143c4d13eSSimon Budig 		goto err_out;
61243c4d13eSSimon Budig 	}
61343c4d13eSSimon Budig 
61443c4d13eSSimon Budig 	return 0;
61543c4d13eSSimon Budig 
61643c4d13eSSimon Budig err_out:
61743c4d13eSSimon Budig 	kfree(tsdata->raw_buffer);
61843c4d13eSSimon Budig 	tsdata->raw_buffer = NULL;
61943c4d13eSSimon Budig 	tsdata->factory_mode = false;
62043c4d13eSSimon Budig 	enable_irq(client->irq);
62143c4d13eSSimon Budig 
62243c4d13eSSimon Budig 	return error;
62343c4d13eSSimon Budig }
62443c4d13eSSimon Budig 
62543c4d13eSSimon Budig static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
62643c4d13eSSimon Budig {
62743c4d13eSSimon Budig 	struct i2c_client *client = tsdata->client;
62843c4d13eSSimon Budig 	int retries = EDT_SWITCH_MODE_RETRIES;
62943c4d13eSSimon Budig 	int ret;
63043c4d13eSSimon Budig 	int error;
63143c4d13eSSimon Budig 
63243c4d13eSSimon Budig 	/* mode register is 0x01 when in the factory mode */
63343c4d13eSSimon Budig 	error = edt_ft5x06_register_write(tsdata, FACTORY_REGISTER_OPMODE, 0x1);
63443c4d13eSSimon Budig 	if (error) {
63543c4d13eSSimon Budig 		dev_err(&client->dev,
63643c4d13eSSimon Budig 			"failed to switch to work mode, error: %d\n", error);
63743c4d13eSSimon Budig 		return error;
63843c4d13eSSimon Budig 	}
63943c4d13eSSimon Budig 
64043c4d13eSSimon Budig 	tsdata->factory_mode = false;
64143c4d13eSSimon Budig 
64243c4d13eSSimon Budig 	do {
64343c4d13eSSimon Budig 		mdelay(EDT_SWITCH_MODE_DELAY);
64443c4d13eSSimon Budig 		/* mode register is 0x01 when in factory mode */
64543c4d13eSSimon Budig 		ret = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OPMODE);
64643c4d13eSSimon Budig 		if (ret == 0x01)
64743c4d13eSSimon Budig 			break;
64843c4d13eSSimon Budig 	} while (--retries > 0);
64943c4d13eSSimon Budig 
65043c4d13eSSimon Budig 	if (retries == 0) {
65143c4d13eSSimon Budig 		dev_err(&client->dev, "not in work mode after %dms.\n",
65243c4d13eSSimon Budig 			EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY);
65343c4d13eSSimon Budig 		tsdata->factory_mode = true;
65443c4d13eSSimon Budig 		return -EIO;
65543c4d13eSSimon Budig 	}
65643c4d13eSSimon Budig 
65743c4d13eSSimon Budig 	kfree(tsdata->raw_buffer);
65843c4d13eSSimon Budig 	tsdata->raw_buffer = NULL;
65943c4d13eSSimon Budig 
660f4ee52f3SMarco Felsch 	edt_ft5x06_restore_reg_parameters(tsdata);
66143c4d13eSSimon Budig 	enable_irq(client->irq);
66243c4d13eSSimon Budig 
66343c4d13eSSimon Budig 	return 0;
66443c4d13eSSimon Budig }
66543c4d13eSSimon Budig 
66643c4d13eSSimon Budig static int edt_ft5x06_debugfs_mode_get(void *data, u64 *mode)
66743c4d13eSSimon Budig {
66843c4d13eSSimon Budig 	struct edt_ft5x06_ts_data *tsdata = data;
66943c4d13eSSimon Budig 
67043c4d13eSSimon Budig 	*mode = tsdata->factory_mode;
67143c4d13eSSimon Budig 
67243c4d13eSSimon Budig 	return 0;
67343c4d13eSSimon Budig };
67443c4d13eSSimon Budig 
67543c4d13eSSimon Budig static int edt_ft5x06_debugfs_mode_set(void *data, u64 mode)
67643c4d13eSSimon Budig {
67743c4d13eSSimon Budig 	struct edt_ft5x06_ts_data *tsdata = data;
67843c4d13eSSimon Budig 	int retval = 0;
67943c4d13eSSimon Budig 
68043c4d13eSSimon Budig 	if (mode > 1)
68143c4d13eSSimon Budig 		return -ERANGE;
68243c4d13eSSimon Budig 
68343c4d13eSSimon Budig 	mutex_lock(&tsdata->mutex);
68443c4d13eSSimon Budig 
68543c4d13eSSimon Budig 	if (mode != tsdata->factory_mode) {
68643c4d13eSSimon Budig 		retval = mode ? edt_ft5x06_factory_mode(tsdata) :
68743c4d13eSSimon Budig 				edt_ft5x06_work_mode(tsdata);
68843c4d13eSSimon Budig 	}
68943c4d13eSSimon Budig 
69043c4d13eSSimon Budig 	mutex_unlock(&tsdata->mutex);
69143c4d13eSSimon Budig 
69243c4d13eSSimon Budig 	return retval;
69343c4d13eSSimon Budig };
69443c4d13eSSimon Budig 
69543c4d13eSSimon Budig DEFINE_SIMPLE_ATTRIBUTE(debugfs_mode_fops, edt_ft5x06_debugfs_mode_get,
69643c4d13eSSimon Budig 			edt_ft5x06_debugfs_mode_set, "%llu\n");
69743c4d13eSSimon Budig 
69843c4d13eSSimon Budig static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file,
69943c4d13eSSimon Budig 				char __user *buf, size_t count, loff_t *off)
70043c4d13eSSimon Budig {
70143c4d13eSSimon Budig 	struct edt_ft5x06_ts_data *tsdata = file->private_data;
70243c4d13eSSimon Budig 	struct i2c_client *client = tsdata->client;
70343c4d13eSSimon Budig 	int retries  = EDT_RAW_DATA_RETRIES;
70443c4d13eSSimon Budig 	int val, i, error;
70543c4d13eSSimon Budig 	size_t read = 0;
70643c4d13eSSimon Budig 	int colbytes;
70743c4d13eSSimon Budig 	char wrbuf[3];
70843c4d13eSSimon Budig 	u8 *rdbuf;
70943c4d13eSSimon Budig 
71043c4d13eSSimon Budig 	if (*off < 0 || *off >= tsdata->raw_bufsize)
71143c4d13eSSimon Budig 		return 0;
71243c4d13eSSimon Budig 
71343c4d13eSSimon Budig 	mutex_lock(&tsdata->mutex);
71443c4d13eSSimon Budig 
71543c4d13eSSimon Budig 	if (!tsdata->factory_mode || !tsdata->raw_buffer) {
71643c4d13eSSimon Budig 		error = -EIO;
71743c4d13eSSimon Budig 		goto out;
71843c4d13eSSimon Budig 	}
71943c4d13eSSimon Budig 
72043c4d13eSSimon Budig 	error = edt_ft5x06_register_write(tsdata, 0x08, 0x01);
72143c4d13eSSimon Budig 	if (error) {
72243c4d13eSSimon Budig 		dev_dbg(&client->dev,
72343c4d13eSSimon Budig 			"failed to write 0x08 register, error %d\n", error);
72443c4d13eSSimon Budig 		goto out;
72543c4d13eSSimon Budig 	}
72643c4d13eSSimon Budig 
72743c4d13eSSimon Budig 	do {
7280eeecf60SAniroop Mathur 		usleep_range(EDT_RAW_DATA_DELAY, EDT_RAW_DATA_DELAY + 100);
72943c4d13eSSimon Budig 		val = edt_ft5x06_register_read(tsdata, 0x08);
73043c4d13eSSimon Budig 		if (val < 1)
73143c4d13eSSimon Budig 			break;
73243c4d13eSSimon Budig 	} while (--retries > 0);
73343c4d13eSSimon Budig 
73443c4d13eSSimon Budig 	if (val < 0) {
73543c4d13eSSimon Budig 		error = val;
73643c4d13eSSimon Budig 		dev_dbg(&client->dev,
73743c4d13eSSimon Budig 			"failed to read 0x08 register, error %d\n", error);
73843c4d13eSSimon Budig 		goto out;
73943c4d13eSSimon Budig 	}
74043c4d13eSSimon Budig 
74143c4d13eSSimon Budig 	if (retries == 0) {
74243c4d13eSSimon Budig 		dev_dbg(&client->dev,
74343c4d13eSSimon Budig 			"timed out waiting for register to settle\n");
74443c4d13eSSimon Budig 		error = -ETIMEDOUT;
74543c4d13eSSimon Budig 		goto out;
74643c4d13eSSimon Budig 	}
74743c4d13eSSimon Budig 
74843c4d13eSSimon Budig 	rdbuf = tsdata->raw_buffer;
74943c4d13eSSimon Budig 	colbytes = tsdata->num_y * sizeof(u16);
75043c4d13eSSimon Budig 
75143c4d13eSSimon Budig 	wrbuf[0] = 0xf5;
75243c4d13eSSimon Budig 	wrbuf[1] = 0x0e;
75343c4d13eSSimon Budig 	for (i = 0; i < tsdata->num_x; i++) {
75443c4d13eSSimon Budig 		wrbuf[2] = i;  /* column index */
75543c4d13eSSimon Budig 		error = edt_ft5x06_ts_readwrite(tsdata->client,
75643c4d13eSSimon Budig 						sizeof(wrbuf), wrbuf,
75743c4d13eSSimon Budig 						colbytes, rdbuf);
75843c4d13eSSimon Budig 		if (error)
75943c4d13eSSimon Budig 			goto out;
76043c4d13eSSimon Budig 
76143c4d13eSSimon Budig 		rdbuf += colbytes;
76243c4d13eSSimon Budig 	}
76343c4d13eSSimon Budig 
76443c4d13eSSimon Budig 	read = min_t(size_t, count, tsdata->raw_bufsize - *off);
76535b1da4eSAxel Lin 	if (copy_to_user(buf, tsdata->raw_buffer + *off, read)) {
76635b1da4eSAxel Lin 		error = -EFAULT;
76735b1da4eSAxel Lin 		goto out;
76835b1da4eSAxel Lin 	}
76935b1da4eSAxel Lin 
77043c4d13eSSimon Budig 	*off += read;
77143c4d13eSSimon Budig out:
77243c4d13eSSimon Budig 	mutex_unlock(&tsdata->mutex);
77343c4d13eSSimon Budig 	return error ?: read;
77443c4d13eSSimon Budig };
77543c4d13eSSimon Budig 
77643c4d13eSSimon Budig static const struct file_operations debugfs_raw_data_fops = {
777f6c0df6aSWei Yongjun 	.open = simple_open,
77843c4d13eSSimon Budig 	.read = edt_ft5x06_debugfs_raw_data_read,
77943c4d13eSSimon Budig };
78043c4d13eSSimon Budig 
78121d1611aSMarco Felsch static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
78243c4d13eSSimon Budig 					  const char *debugfs_name)
78343c4d13eSSimon Budig {
78443c4d13eSSimon Budig 	tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL);
78543c4d13eSSimon Budig 
78643c4d13eSSimon Budig 	debugfs_create_u16("num_x", S_IRUSR, tsdata->debug_dir, &tsdata->num_x);
78743c4d13eSSimon Budig 	debugfs_create_u16("num_y", S_IRUSR, tsdata->debug_dir, &tsdata->num_y);
78843c4d13eSSimon Budig 
78943c4d13eSSimon Budig 	debugfs_create_file("mode", S_IRUSR | S_IWUSR,
79043c4d13eSSimon Budig 			    tsdata->debug_dir, tsdata, &debugfs_mode_fops);
79143c4d13eSSimon Budig 	debugfs_create_file("raw_data", S_IRUSR,
79243c4d13eSSimon Budig 			    tsdata->debug_dir, tsdata, &debugfs_raw_data_fops);
79343c4d13eSSimon Budig }
79443c4d13eSSimon Budig 
79521d1611aSMarco Felsch static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
79643c4d13eSSimon Budig {
79743c4d13eSSimon Budig 	debugfs_remove_recursive(tsdata->debug_dir);
798a1d0fa77SGuenter Roeck 	kfree(tsdata->raw_buffer);
79943c4d13eSSimon Budig }
80043c4d13eSSimon Budig 
80143c4d13eSSimon Budig #else
80243c4d13eSSimon Budig 
80321d1611aSMarco Felsch static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
80421d1611aSMarco Felsch {
80521d1611aSMarco Felsch 	return -ENOSYS;
80621d1611aSMarco Felsch }
80721d1611aSMarco Felsch 
80821d1611aSMarco Felsch static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
80943c4d13eSSimon Budig 					  const char *debugfs_name)
81043c4d13eSSimon Budig {
81143c4d13eSSimon Budig }
81243c4d13eSSimon Budig 
81321d1611aSMarco Felsch static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
81443c4d13eSSimon Budig {
81543c4d13eSSimon Budig }
81643c4d13eSSimon Budig 
81743c4d13eSSimon Budig #endif /* CONFIG_DEBUGFS */
81843c4d13eSSimon Budig 
8195298cc4cSBill Pemberton static int edt_ft5x06_ts_identify(struct i2c_client *client,
820fd335ab0SLothar Waßmann 					struct edt_ft5x06_ts_data *tsdata,
82143c4d13eSSimon Budig 					char *fw_version)
82243c4d13eSSimon Budig {
82343c4d13eSSimon Budig 	u8 rdbuf[EDT_NAME_LEN];
82443c4d13eSSimon Budig 	char *p;
82543c4d13eSSimon Budig 	int error;
826fd335ab0SLothar Waßmann 	char *model_name = tsdata->name;
82743c4d13eSSimon Budig 
828fd335ab0SLothar Waßmann 	/* see what we find if we assume it is a M06 *
829fd335ab0SLothar Waßmann 	 * if we get less than EDT_NAME_LEN, we don't want
830fd335ab0SLothar Waßmann 	 * to have garbage in there
831fd335ab0SLothar Waßmann 	 */
832fd335ab0SLothar Waßmann 	memset(rdbuf, 0, sizeof(rdbuf));
833aed5d0eeSSimon Budig 	error = edt_ft5x06_ts_readwrite(client, 1, "\xBB",
83443c4d13eSSimon Budig 					EDT_NAME_LEN - 1, rdbuf);
83543c4d13eSSimon Budig 	if (error)
83643c4d13eSSimon Budig 		return error;
83743c4d13eSSimon Budig 
838aed5d0eeSSimon Budig 	/* Probe content for something consistent.
839aed5d0eeSSimon Budig 	 * M06 starts with a response byte, M12 gives the data directly.
840aed5d0eeSSimon Budig 	 * M09/Generic does not provide model number information.
841fd335ab0SLothar Waßmann 	 */
842aed5d0eeSSimon Budig 	if (!strncasecmp(rdbuf + 1, "EP0", 3)) {
843169110c3SSimon Budig 		tsdata->version = EDT_M06;
844fd335ab0SLothar Waßmann 
84543c4d13eSSimon Budig 		/* remove last '$' end marker */
84643c4d13eSSimon Budig 		rdbuf[EDT_NAME_LEN - 1] = '\0';
84743c4d13eSSimon Budig 		if (rdbuf[EDT_NAME_LEN - 2] == '$')
84843c4d13eSSimon Budig 			rdbuf[EDT_NAME_LEN - 2] = '\0';
84943c4d13eSSimon Budig 
85043c4d13eSSimon Budig 		/* look for Model/Version separator */
85143c4d13eSSimon Budig 		p = strchr(rdbuf, '*');
85243c4d13eSSimon Budig 		if (p)
85343c4d13eSSimon Budig 			*p++ = '\0';
85443c4d13eSSimon Budig 		strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN);
85543c4d13eSSimon Budig 		strlcpy(fw_version, p ? p : "", EDT_NAME_LEN);
856aed5d0eeSSimon Budig 	} else if (!strncasecmp(rdbuf, "EP0", 3)) {
857aed5d0eeSSimon Budig 		tsdata->version = EDT_M12;
858aed5d0eeSSimon Budig 
859aed5d0eeSSimon Budig 		/* remove last '$' end marker */
860aed5d0eeSSimon Budig 		rdbuf[EDT_NAME_LEN - 2] = '\0';
861aed5d0eeSSimon Budig 		if (rdbuf[EDT_NAME_LEN - 3] == '$')
862aed5d0eeSSimon Budig 			rdbuf[EDT_NAME_LEN - 3] = '\0';
863aed5d0eeSSimon Budig 
864aed5d0eeSSimon Budig 		/* look for Model/Version separator */
865aed5d0eeSSimon Budig 		p = strchr(rdbuf, '*');
866aed5d0eeSSimon Budig 		if (p)
867aed5d0eeSSimon Budig 			*p++ = '\0';
868aed5d0eeSSimon Budig 		strlcpy(model_name, rdbuf, EDT_NAME_LEN);
869aed5d0eeSSimon Budig 		strlcpy(fw_version, p ? p : "", EDT_NAME_LEN);
870fd335ab0SLothar Waßmann 	} else {
871aed5d0eeSSimon Budig 		/* If it is not an EDT M06/M12 touchscreen, then the model
872169110c3SSimon Budig 		 * detection is a bit hairy. The different ft5x06
873169110c3SSimon Budig 		 * firmares around don't reliably implement the
874169110c3SSimon Budig 		 * identification registers. Well, we'll take a shot.
875169110c3SSimon Budig 		 *
876169110c3SSimon Budig 		 * The main difference between generic focaltec based
877169110c3SSimon Budig 		 * touches and EDT M09 is that we know how to retrieve
878169110c3SSimon Budig 		 * the max coordinates for the latter.
879169110c3SSimon Budig 		 */
880169110c3SSimon Budig 		tsdata->version = GENERIC_FT;
881fd335ab0SLothar Waßmann 
882fd335ab0SLothar Waßmann 		error = edt_ft5x06_ts_readwrite(client, 1, "\xA6",
883fd335ab0SLothar Waßmann 						2, rdbuf);
884fd335ab0SLothar Waßmann 		if (error)
885fd335ab0SLothar Waßmann 			return error;
886fd335ab0SLothar Waßmann 
887fd335ab0SLothar Waßmann 		strlcpy(fw_version, rdbuf, 2);
888fd335ab0SLothar Waßmann 
889fd335ab0SLothar Waßmann 		error = edt_ft5x06_ts_readwrite(client, 1, "\xA8",
890fd335ab0SLothar Waßmann 						1, rdbuf);
891fd335ab0SLothar Waßmann 		if (error)
892fd335ab0SLothar Waßmann 			return error;
893fd335ab0SLothar Waßmann 
894169110c3SSimon Budig 		/* This "model identification" is not exact. Unfortunately
895169110c3SSimon Budig 		 * not all firmwares for the ft5x06 put useful values in
896169110c3SSimon Budig 		 * the identification registers.
897169110c3SSimon Budig 		 */
898169110c3SSimon Budig 		switch (rdbuf[0]) {
899169110c3SSimon Budig 		case 0x35:   /* EDT EP0350M09 */
900169110c3SSimon Budig 		case 0x43:   /* EDT EP0430M09 */
901169110c3SSimon Budig 		case 0x50:   /* EDT EP0500M09 */
902169110c3SSimon Budig 		case 0x57:   /* EDT EP0570M09 */
903169110c3SSimon Budig 		case 0x70:   /* EDT EP0700M09 */
904169110c3SSimon Budig 			tsdata->version = EDT_M09;
905fd335ab0SLothar Waßmann 			snprintf(model_name, EDT_NAME_LEN, "EP0%i%i0M09",
906fd335ab0SLothar Waßmann 				rdbuf[0] >> 4, rdbuf[0] & 0x0F);
907169110c3SSimon Budig 			break;
908169110c3SSimon Budig 		case 0xa1:   /* EDT EP1010ML00 */
909169110c3SSimon Budig 			tsdata->version = EDT_M09;
910169110c3SSimon Budig 			snprintf(model_name, EDT_NAME_LEN, "EP%i%i0ML00",
911169110c3SSimon Budig 				rdbuf[0] >> 4, rdbuf[0] & 0x0F);
912169110c3SSimon Budig 			break;
913169110c3SSimon Budig 		case 0x5a:   /* Solomon Goldentek Display */
914169110c3SSimon Budig 			snprintf(model_name, EDT_NAME_LEN, "GKTW50SCED1R0");
915169110c3SSimon Budig 			break;
916a2f39dacSMarco Felsch 		case 0x59:  /* Evervision Display with FT5xx6 TS */
917a2f39dacSMarco Felsch 			tsdata->version = EV_FT;
918a2f39dacSMarco Felsch 			error = edt_ft5x06_ts_readwrite(client, 1, "\x53",
919a2f39dacSMarco Felsch 							1, rdbuf);
920a2f39dacSMarco Felsch 			if (error)
921a2f39dacSMarco Felsch 				return error;
922a2f39dacSMarco Felsch 			strlcpy(fw_version, rdbuf, 1);
923a2f39dacSMarco Felsch 			snprintf(model_name, EDT_NAME_LEN,
924a2f39dacSMarco Felsch 				 "EVERVISION-FT5726NEi");
925a2f39dacSMarco Felsch 			break;
926169110c3SSimon Budig 		default:
927169110c3SSimon Budig 			snprintf(model_name, EDT_NAME_LEN,
928169110c3SSimon Budig 				 "generic ft5x06 (%02x)",
929169110c3SSimon Budig 				 rdbuf[0]);
930169110c3SSimon Budig 			break;
931169110c3SSimon Budig 		}
932fd335ab0SLothar Waßmann 	}
93343c4d13eSSimon Budig 
93443c4d13eSSimon Budig 	return 0;
93543c4d13eSSimon Budig }
93643c4d13eSSimon Budig 
9372e23b7a9SDmitry Torokhov static void edt_ft5x06_ts_get_defaults(struct device *dev,
938dac90dc2SLothar Waßmann 				       struct edt_ft5x06_ts_data *tsdata)
939dac90dc2SLothar Waßmann {
940fd335ab0SLothar Waßmann 	struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
9412e23b7a9SDmitry Torokhov 	u32 val;
9422e23b7a9SDmitry Torokhov 	int error;
943fd335ab0SLothar Waßmann 
9442e23b7a9SDmitry Torokhov 	error = device_property_read_u32(dev, "threshold", &val);
945dc262dfaSPhilipp Zabel 	if (!error) {
946dc262dfaSPhilipp Zabel 		edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold, val);
947dc262dfaSPhilipp Zabel 		tsdata->threshold = val;
948dc262dfaSPhilipp Zabel 	}
9492e23b7a9SDmitry Torokhov 
9502e23b7a9SDmitry Torokhov 	error = device_property_read_u32(dev, "gain", &val);
951dc262dfaSPhilipp Zabel 	if (!error) {
952dc262dfaSPhilipp Zabel 		edt_ft5x06_register_write(tsdata, reg_addr->reg_gain, val);
953dc262dfaSPhilipp Zabel 		tsdata->gain = val;
954dc262dfaSPhilipp Zabel 	}
9552e23b7a9SDmitry Torokhov 
9562e23b7a9SDmitry Torokhov 	error = device_property_read_u32(dev, "offset", &val);
957dc262dfaSPhilipp Zabel 	if (!error) {
958255cdaf7SMarco Felsch 		if (reg_addr->reg_offset != NO_REGISTER)
959255cdaf7SMarco Felsch 			edt_ft5x06_register_write(tsdata,
960255cdaf7SMarco Felsch 						  reg_addr->reg_offset, val);
961dc262dfaSPhilipp Zabel 		tsdata->offset = val;
962dc262dfaSPhilipp Zabel 	}
963b6eba860SMarco Felsch 
964b6eba860SMarco Felsch 	error = device_property_read_u32(dev, "offset-x", &val);
965b6eba860SMarco Felsch 	if (!error) {
966255cdaf7SMarco Felsch 		if (reg_addr->reg_offset_x != NO_REGISTER)
967255cdaf7SMarco Felsch 			edt_ft5x06_register_write(tsdata,
968255cdaf7SMarco Felsch 						  reg_addr->reg_offset_x, val);
969b6eba860SMarco Felsch 		tsdata->offset_x = val;
970b6eba860SMarco Felsch 	}
971b6eba860SMarco Felsch 
972b6eba860SMarco Felsch 	error = device_property_read_u32(dev, "offset-y", &val);
973b6eba860SMarco Felsch 	if (!error) {
974255cdaf7SMarco Felsch 		if (reg_addr->reg_offset_y != NO_REGISTER)
975255cdaf7SMarco Felsch 			edt_ft5x06_register_write(tsdata,
976255cdaf7SMarco Felsch 						  reg_addr->reg_offset_y, val);
977b6eba860SMarco Felsch 		tsdata->offset_y = val;
978b6eba860SMarco Felsch 	}
979dac90dc2SLothar Waßmann }
980dac90dc2SLothar Waßmann 
9815298cc4cSBill Pemberton static void
98243c4d13eSSimon Budig edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata)
98343c4d13eSSimon Budig {
984fd335ab0SLothar Waßmann 	struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
985fd335ab0SLothar Waßmann 
98643c4d13eSSimon Budig 	tsdata->threshold = edt_ft5x06_register_read(tsdata,
987fd335ab0SLothar Waßmann 						     reg_addr->reg_threshold);
988fd335ab0SLothar Waßmann 	tsdata->gain = edt_ft5x06_register_read(tsdata, reg_addr->reg_gain);
989a2f39dacSMarco Felsch 	if (reg_addr->reg_offset != NO_REGISTER)
990a2f39dacSMarco Felsch 		tsdata->offset =
991a2f39dacSMarco Felsch 			edt_ft5x06_register_read(tsdata, reg_addr->reg_offset);
992b6eba860SMarco Felsch 	if (reg_addr->reg_offset_x != NO_REGISTER)
993b6eba860SMarco Felsch 		tsdata->offset_x = edt_ft5x06_register_read(tsdata,
994b6eba860SMarco Felsch 						reg_addr->reg_offset_x);
995b6eba860SMarco Felsch 	if (reg_addr->reg_offset_y != NO_REGISTER)
996b6eba860SMarco Felsch 		tsdata->offset_y = edt_ft5x06_register_read(tsdata,
997b6eba860SMarco Felsch 						reg_addr->reg_offset_y);
998fd335ab0SLothar Waßmann 	if (reg_addr->reg_report_rate != NO_REGISTER)
99943c4d13eSSimon Budig 		tsdata->report_rate = edt_ft5x06_register_read(tsdata,
1000fd335ab0SLothar Waßmann 						reg_addr->reg_report_rate);
1001169110c3SSimon Budig 	if (tsdata->version == EDT_M06 ||
1002aed5d0eeSSimon Budig 	    tsdata->version == EDT_M09 ||
1003aed5d0eeSSimon Budig 	    tsdata->version == EDT_M12) {
1004169110c3SSimon Budig 		tsdata->num_x = edt_ft5x06_register_read(tsdata,
1005169110c3SSimon Budig 							 reg_addr->reg_num_x);
1006169110c3SSimon Budig 		tsdata->num_y = edt_ft5x06_register_read(tsdata,
1007169110c3SSimon Budig 							 reg_addr->reg_num_y);
1008169110c3SSimon Budig 	} else {
1009169110c3SSimon Budig 		tsdata->num_x = -1;
1010169110c3SSimon Budig 		tsdata->num_y = -1;
1011169110c3SSimon Budig 	}
1012fd335ab0SLothar Waßmann }
1013fd335ab0SLothar Waßmann 
1014fd335ab0SLothar Waßmann static void
1015fd335ab0SLothar Waßmann edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata)
1016fd335ab0SLothar Waßmann {
1017fd335ab0SLothar Waßmann 	struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
1018fd335ab0SLothar Waßmann 
1019fd335ab0SLothar Waßmann 	switch (tsdata->version) {
1020169110c3SSimon Budig 	case EDT_M06:
1021fd335ab0SLothar Waßmann 		reg_addr->reg_threshold = WORK_REGISTER_THRESHOLD;
1022fd335ab0SLothar Waßmann 		reg_addr->reg_report_rate = WORK_REGISTER_REPORT_RATE;
1023fd335ab0SLothar Waßmann 		reg_addr->reg_gain = WORK_REGISTER_GAIN;
1024fd335ab0SLothar Waßmann 		reg_addr->reg_offset = WORK_REGISTER_OFFSET;
1025b6eba860SMarco Felsch 		reg_addr->reg_offset_x = NO_REGISTER;
1026b6eba860SMarco Felsch 		reg_addr->reg_offset_y = NO_REGISTER;
1027fd335ab0SLothar Waßmann 		reg_addr->reg_num_x = WORK_REGISTER_NUM_X;
1028fd335ab0SLothar Waßmann 		reg_addr->reg_num_y = WORK_REGISTER_NUM_Y;
1029fd335ab0SLothar Waßmann 		break;
1030fd335ab0SLothar Waßmann 
1031169110c3SSimon Budig 	case EDT_M09:
1032aed5d0eeSSimon Budig 	case EDT_M12:
1033fd335ab0SLothar Waßmann 		reg_addr->reg_threshold = M09_REGISTER_THRESHOLD;
103447014752SLuca Ceresoli 		reg_addr->reg_report_rate = NO_REGISTER;
1035fd335ab0SLothar Waßmann 		reg_addr->reg_gain = M09_REGISTER_GAIN;
1036fd335ab0SLothar Waßmann 		reg_addr->reg_offset = M09_REGISTER_OFFSET;
1037b6eba860SMarco Felsch 		reg_addr->reg_offset_x = NO_REGISTER;
1038b6eba860SMarco Felsch 		reg_addr->reg_offset_y = NO_REGISTER;
1039fd335ab0SLothar Waßmann 		reg_addr->reg_num_x = M09_REGISTER_NUM_X;
1040fd335ab0SLothar Waßmann 		reg_addr->reg_num_y = M09_REGISTER_NUM_Y;
1041fd335ab0SLothar Waßmann 		break;
1042169110c3SSimon Budig 
1043a2f39dacSMarco Felsch 	case EV_FT:
1044a2f39dacSMarco Felsch 		reg_addr->reg_threshold = EV_REGISTER_THRESHOLD;
1045a2f39dacSMarco Felsch 		reg_addr->reg_gain = EV_REGISTER_GAIN;
1046a2f39dacSMarco Felsch 		reg_addr->reg_offset = NO_REGISTER;
1047b6eba860SMarco Felsch 		reg_addr->reg_offset_x = EV_REGISTER_OFFSET_X;
1048b6eba860SMarco Felsch 		reg_addr->reg_offset_y = EV_REGISTER_OFFSET_Y;
1049a2f39dacSMarco Felsch 		reg_addr->reg_num_x = NO_REGISTER;
1050a2f39dacSMarco Felsch 		reg_addr->reg_num_y = NO_REGISTER;
1051a2f39dacSMarco Felsch 		reg_addr->reg_report_rate = NO_REGISTER;
1052a2f39dacSMarco Felsch 		break;
1053a2f39dacSMarco Felsch 
1054169110c3SSimon Budig 	case GENERIC_FT:
1055169110c3SSimon Budig 		/* this is a guesswork */
1056169110c3SSimon Budig 		reg_addr->reg_threshold = M09_REGISTER_THRESHOLD;
1057169110c3SSimon Budig 		reg_addr->reg_gain = M09_REGISTER_GAIN;
1058169110c3SSimon Budig 		reg_addr->reg_offset = M09_REGISTER_OFFSET;
1059b6eba860SMarco Felsch 		reg_addr->reg_offset_x = NO_REGISTER;
1060b6eba860SMarco Felsch 		reg_addr->reg_offset_y = NO_REGISTER;
1061169110c3SSimon Budig 		break;
1062fd335ab0SLothar Waßmann 	}
106343c4d13eSSimon Budig }
106443c4d13eSSimon Budig 
10657448bfecSMylène Josserand static void edt_ft5x06_disable_regulator(void *arg)
10667448bfecSMylène Josserand {
10677448bfecSMylène Josserand 	struct edt_ft5x06_ts_data *data = arg;
10687448bfecSMylène Josserand 
10697448bfecSMylène Josserand 	regulator_disable(data->vcc);
10707448bfecSMylène Josserand }
10717448bfecSMylène Josserand 
10725298cc4cSBill Pemberton static int edt_ft5x06_ts_probe(struct i2c_client *client,
107343c4d13eSSimon Budig 					 const struct i2c_device_id *id)
107443c4d13eSSimon Budig {
1075b1d2a3ecSFranklin S Cooper Jr 	const struct edt_i2c_chip_data *chip_data;
107643c4d13eSSimon Budig 	struct edt_ft5x06_ts_data *tsdata;
1077e112324cSPhilipp Zabel 	u8 buf[2] = { 0xfc, 0x00 };
107843c4d13eSSimon Budig 	struct input_dev *input;
1079f0bef75cSDmitry Torokhov 	unsigned long irq_flags;
108043c4d13eSSimon Budig 	int error;
108143c4d13eSSimon Budig 	char fw_version[EDT_NAME_LEN];
108243c4d13eSSimon Budig 
108343c4d13eSSimon Budig 	dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n");
108443c4d13eSSimon Budig 
108502300bd6SLothar Waßmann 	tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);
108602300bd6SLothar Waßmann 	if (!tsdata) {
108743c4d13eSSimon Budig 		dev_err(&client->dev, "failed to allocate driver data.\n");
108802300bd6SLothar Waßmann 		return -ENOMEM;
108902300bd6SLothar Waßmann 	}
109002300bd6SLothar Waßmann 
1091fc226eb2SAndy Shevchenko 	chip_data = device_get_match_data(&client->dev);
1092b1d2a3ecSFranklin S Cooper Jr 	if (!chip_data)
1093b1d2a3ecSFranklin S Cooper Jr 		chip_data = (const struct edt_i2c_chip_data *)id->driver_data;
1094b1d2a3ecSFranklin S Cooper Jr 	if (!chip_data || !chip_data->max_support_points) {
1095b1d2a3ecSFranklin S Cooper Jr 		dev_err(&client->dev, "invalid or missing chip data\n");
1096b1d2a3ecSFranklin S Cooper Jr 		return -EINVAL;
1097b1d2a3ecSFranklin S Cooper Jr 	}
1098b1d2a3ecSFranklin S Cooper Jr 
1099b1d2a3ecSFranklin S Cooper Jr 	tsdata->max_support_points = chip_data->max_support_points;
1100b1d2a3ecSFranklin S Cooper Jr 
11017448bfecSMylène Josserand 	tsdata->vcc = devm_regulator_get(&client->dev, "vcc");
11027448bfecSMylène Josserand 	if (IS_ERR(tsdata->vcc)) {
11037448bfecSMylène Josserand 		error = PTR_ERR(tsdata->vcc);
11047448bfecSMylène Josserand 		if (error != -EPROBE_DEFER)
11057448bfecSMylène Josserand 			dev_err(&client->dev,
11067448bfecSMylène Josserand 				"failed to request regulator: %d\n", error);
11077448bfecSMylène Josserand 		return error;
11087448bfecSMylène Josserand 	}
11097448bfecSMylène Josserand 
11107448bfecSMylène Josserand 	error = regulator_enable(tsdata->vcc);
11117448bfecSMylène Josserand 	if (error < 0) {
11127448bfecSMylène Josserand 		dev_err(&client->dev, "failed to enable vcc: %d\n", error);
11137448bfecSMylène Josserand 		return error;
11147448bfecSMylène Josserand 	}
11157448bfecSMylène Josserand 
11167448bfecSMylène Josserand 	error = devm_add_action_or_reset(&client->dev,
11177448bfecSMylène Josserand 					 edt_ft5x06_disable_regulator,
11187448bfecSMylène Josserand 					 tsdata);
11197448bfecSMylène Josserand 	if (error)
11207448bfecSMylène Josserand 		return error;
11217448bfecSMylène Josserand 
112213c23cd1SFranklin S Cooper Jr 	tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev,
112313c23cd1SFranklin S Cooper Jr 						     "reset", GPIOD_OUT_HIGH);
112413c23cd1SFranklin S Cooper Jr 	if (IS_ERR(tsdata->reset_gpio)) {
112513c23cd1SFranklin S Cooper Jr 		error = PTR_ERR(tsdata->reset_gpio);
1126dac90dc2SLothar Waßmann 		dev_err(&client->dev,
112713c23cd1SFranklin S Cooper Jr 			"Failed to request GPIO reset pin, error %d\n", error);
1128dac90dc2SLothar Waßmann 		return error;
1129dac90dc2SLothar Waßmann 	}
1130dac90dc2SLothar Waßmann 
113113c23cd1SFranklin S Cooper Jr 	tsdata->wake_gpio = devm_gpiod_get_optional(&client->dev,
113213c23cd1SFranklin S Cooper Jr 						    "wake", GPIOD_OUT_LOW);
113313c23cd1SFranklin S Cooper Jr 	if (IS_ERR(tsdata->wake_gpio)) {
113413c23cd1SFranklin S Cooper Jr 		error = PTR_ERR(tsdata->wake_gpio);
1135dac90dc2SLothar Waßmann 		dev_err(&client->dev,
113613c23cd1SFranklin S Cooper Jr 			"Failed to request GPIO wake pin, error %d\n", error);
1137dac90dc2SLothar Waßmann 		return error;
1138dac90dc2SLothar Waßmann 	}
113913c23cd1SFranklin S Cooper Jr 
114021d1611aSMarco Felsch 	/*
114121d1611aSMarco Felsch 	 * Check which sleep modes we can support. Power-off requieres the
114221d1611aSMarco Felsch 	 * reset-pin to ensure correct power-down/power-up behaviour. Start with
114321d1611aSMarco Felsch 	 * the EDT_PMODE_POWEROFF test since this is the deepest possible sleep
114421d1611aSMarco Felsch 	 * mode.
114521d1611aSMarco Felsch 	 */
114621d1611aSMarco Felsch 	if (tsdata->reset_gpio)
114721d1611aSMarco Felsch 		tsdata->suspend_mode = EDT_PMODE_POWEROFF;
114821d1611aSMarco Felsch 	else if (tsdata->wake_gpio)
114921d1611aSMarco Felsch 		tsdata->suspend_mode = EDT_PMODE_HIBERNATE;
115021d1611aSMarco Felsch 	else
115121d1611aSMarco Felsch 		tsdata->suspend_mode = EDT_PMODE_NOT_SUPPORTED;
115221d1611aSMarco Felsch 
115313c23cd1SFranklin S Cooper Jr 	if (tsdata->wake_gpio) {
115413c23cd1SFranklin S Cooper Jr 		usleep_range(5000, 6000);
115513c23cd1SFranklin S Cooper Jr 		gpiod_set_value_cansleep(tsdata->wake_gpio, 1);
115613c23cd1SFranklin S Cooper Jr 	}
115713c23cd1SFranklin S Cooper Jr 
115813c23cd1SFranklin S Cooper Jr 	if (tsdata->reset_gpio) {
115913c23cd1SFranklin S Cooper Jr 		usleep_range(5000, 6000);
116013c23cd1SFranklin S Cooper Jr 		gpiod_set_value_cansleep(tsdata->reset_gpio, 0);
116113c23cd1SFranklin S Cooper Jr 		msleep(300);
1162dac90dc2SLothar Waßmann 	}
1163dac90dc2SLothar Waßmann 
116402300bd6SLothar Waßmann 	input = devm_input_allocate_device(&client->dev);
116502300bd6SLothar Waßmann 	if (!input) {
116602300bd6SLothar Waßmann 		dev_err(&client->dev, "failed to allocate input device.\n");
116702300bd6SLothar Waßmann 		return -ENOMEM;
116843c4d13eSSimon Budig 	}
116943c4d13eSSimon Budig 
117043c4d13eSSimon Budig 	mutex_init(&tsdata->mutex);
117143c4d13eSSimon Budig 	tsdata->client = client;
117243c4d13eSSimon Budig 	tsdata->input = input;
117343c4d13eSSimon Budig 	tsdata->factory_mode = false;
117443c4d13eSSimon Budig 
1175fd335ab0SLothar Waßmann 	error = edt_ft5x06_ts_identify(client, tsdata, fw_version);
117643c4d13eSSimon Budig 	if (error) {
117743c4d13eSSimon Budig 		dev_err(&client->dev, "touchscreen probe failed\n");
117802300bd6SLothar Waßmann 		return error;
117943c4d13eSSimon Budig 	}
118043c4d13eSSimon Budig 
1181e112324cSPhilipp Zabel 	/*
1182e112324cSPhilipp Zabel 	 * Dummy read access. EP0700MLP1 returns bogus data on the first
1183e112324cSPhilipp Zabel 	 * register read access and ignores writes.
1184e112324cSPhilipp Zabel 	 */
1185e112324cSPhilipp Zabel 	edt_ft5x06_ts_readwrite(tsdata->client, 2, buf, 2, buf);
1186e112324cSPhilipp Zabel 
1187fd335ab0SLothar Waßmann 	edt_ft5x06_ts_set_regs(tsdata);
11882e23b7a9SDmitry Torokhov 	edt_ft5x06_ts_get_defaults(&client->dev, tsdata);
118943c4d13eSSimon Budig 	edt_ft5x06_ts_get_parameters(tsdata);
119043c4d13eSSimon Budig 
119143c4d13eSSimon Budig 	dev_dbg(&client->dev,
119243c4d13eSSimon Budig 		"Model \"%s\", Rev. \"%s\", %dx%d sensors\n",
119343c4d13eSSimon Budig 		tsdata->name, fw_version, tsdata->num_x, tsdata->num_y);
119443c4d13eSSimon Budig 
119543c4d13eSSimon Budig 	input->name = tsdata->name;
119643c4d13eSSimon Budig 	input->id.bustype = BUS_I2C;
119743c4d13eSSimon Budig 	input->dev.parent = &client->dev;
119843c4d13eSSimon Budig 
1199169110c3SSimon Budig 	if (tsdata->version == EDT_M06 ||
1200aed5d0eeSSimon Budig 	    tsdata->version == EDT_M09 ||
1201aed5d0eeSSimon Budig 	    tsdata->version == EDT_M12) {
120243c4d13eSSimon Budig 		input_set_abs_params(input, ABS_MT_POSITION_X,
120343c4d13eSSimon Budig 				     0, tsdata->num_x * 64 - 1, 0, 0);
120443c4d13eSSimon Budig 		input_set_abs_params(input, ABS_MT_POSITION_Y,
120543c4d13eSSimon Budig 				     0, tsdata->num_y * 64 - 1, 0, 0);
1206169110c3SSimon Budig 	} else {
1207169110c3SSimon Budig 		/* Unknown maximum values. Specify via devicetree */
1208169110c3SSimon Budig 		input_set_abs_params(input, ABS_MT_POSITION_X,
1209169110c3SSimon Budig 				     0, 65535, 0, 0);
1210169110c3SSimon Budig 		input_set_abs_params(input, ABS_MT_POSITION_Y,
1211169110c3SSimon Budig 				     0, 65535, 0, 0);
1212169110c3SSimon Budig 	}
12132c005598SMaxime Ripard 
1214ad368eb2SHans de Goede 	touchscreen_parse_properties(input, true, &tsdata->prop);
12152c005598SMaxime Ripard 
1216b1d2a3ecSFranklin S Cooper Jr 	error = input_mt_init_slots(input, tsdata->max_support_points,
1217b1d2a3ecSFranklin S Cooper Jr 				INPUT_MT_DIRECT);
121843c4d13eSSimon Budig 	if (error) {
121943c4d13eSSimon Budig 		dev_err(&client->dev, "Unable to init MT slots.\n");
122002300bd6SLothar Waßmann 		return error;
122143c4d13eSSimon Budig 	}
122243c4d13eSSimon Budig 
122343c4d13eSSimon Budig 	i2c_set_clientdata(client, tsdata);
122443c4d13eSSimon Budig 
1225f0bef75cSDmitry Torokhov 	irq_flags = irq_get_trigger_type(client->irq);
1226f0bef75cSDmitry Torokhov 	if (irq_flags == IRQF_TRIGGER_NONE)
1227f0bef75cSDmitry Torokhov 		irq_flags = IRQF_TRIGGER_FALLING;
1228f0bef75cSDmitry Torokhov 	irq_flags |= IRQF_ONESHOT;
1229f0bef75cSDmitry Torokhov 
1230f0bef75cSDmitry Torokhov 	error = devm_request_threaded_irq(&client->dev, client->irq,
1231f0bef75cSDmitry Torokhov 					NULL, edt_ft5x06_ts_isr, irq_flags,
123243c4d13eSSimon Budig 					client->name, tsdata);
123343c4d13eSSimon Budig 	if (error) {
123443c4d13eSSimon Budig 		dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
123502300bd6SLothar Waßmann 		return error;
123643c4d13eSSimon Budig 	}
123743c4d13eSSimon Budig 
1238e3adf559SAndi Shyti 	error = devm_device_add_group(&client->dev, &edt_ft5x06_attr_group);
123943c4d13eSSimon Budig 	if (error)
124002300bd6SLothar Waßmann 		return error;
124143c4d13eSSimon Budig 
124243c4d13eSSimon Budig 	error = input_register_device(input);
1243dac90dc2SLothar Waßmann 	if (error)
1244e3adf559SAndi Shyti 		return error;
124543c4d13eSSimon Budig 
124643c4d13eSSimon Budig 	edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev));
124743c4d13eSSimon Budig 
124843c4d13eSSimon Budig 	dev_dbg(&client->dev,
1249dac90dc2SLothar Waßmann 		"EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n",
12508b58cc36SFranklin S Cooper Jr 		client->irq,
12518b58cc36SFranklin S Cooper Jr 		tsdata->wake_gpio ? desc_to_gpio(tsdata->wake_gpio) : -1,
12528b58cc36SFranklin S Cooper Jr 		tsdata->reset_gpio ? desc_to_gpio(tsdata->reset_gpio) : -1);
125343c4d13eSSimon Budig 
125443c4d13eSSimon Budig 	return 0;
125543c4d13eSSimon Budig }
125643c4d13eSSimon Budig 
1257e2619cf7SBill Pemberton static int edt_ft5x06_ts_remove(struct i2c_client *client)
125843c4d13eSSimon Budig {
125943c4d13eSSimon Budig 	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
126043c4d13eSSimon Budig 
126143c4d13eSSimon Budig 	edt_ft5x06_ts_teardown_debugfs(tsdata);
126243c4d13eSSimon Budig 
126343c4d13eSSimon Budig 	return 0;
126443c4d13eSSimon Budig }
126543c4d13eSSimon Budig 
126621d1611aSMarco Felsch static int __maybe_unused edt_ft5x06_ts_suspend(struct device *dev)
126721d1611aSMarco Felsch {
126821d1611aSMarco Felsch 	struct i2c_client *client = to_i2c_client(dev);
126921d1611aSMarco Felsch 	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
127021d1611aSMarco Felsch 	struct gpio_desc *reset_gpio = tsdata->reset_gpio;
127121d1611aSMarco Felsch 	int ret;
127221d1611aSMarco Felsch 
127321d1611aSMarco Felsch 	if (device_may_wakeup(dev))
127421d1611aSMarco Felsch 		return 0;
127521d1611aSMarco Felsch 
127621d1611aSMarco Felsch 	if (tsdata->suspend_mode == EDT_PMODE_NOT_SUPPORTED)
127721d1611aSMarco Felsch 		return 0;
127821d1611aSMarco Felsch 
127921d1611aSMarco Felsch 	/* Enter hibernate mode. */
128021d1611aSMarco Felsch 	ret = edt_ft5x06_register_write(tsdata, PMOD_REGISTER_OPMODE,
128121d1611aSMarco Felsch 					PMOD_REGISTER_HIBERNATE);
128221d1611aSMarco Felsch 	if (ret)
128321d1611aSMarco Felsch 		dev_warn(dev, "Failed to set hibernate mode\n");
128421d1611aSMarco Felsch 
128521d1611aSMarco Felsch 	if (tsdata->suspend_mode == EDT_PMODE_HIBERNATE)
128621d1611aSMarco Felsch 		return 0;
128721d1611aSMarco Felsch 
128821d1611aSMarco Felsch 	/*
128921d1611aSMarco Felsch 	 * Power-off according the datasheet. Cut the power may leaf the irq
129021d1611aSMarco Felsch 	 * line in an undefined state depending on the host pull resistor
129121d1611aSMarco Felsch 	 * settings. Disable the irq to avoid adjusting each host till the
129221d1611aSMarco Felsch 	 * device is back in a full functional state.
129321d1611aSMarco Felsch 	 */
129421d1611aSMarco Felsch 	disable_irq(tsdata->client->irq);
129521d1611aSMarco Felsch 
129621d1611aSMarco Felsch 	gpiod_set_value_cansleep(reset_gpio, 1);
129721d1611aSMarco Felsch 	usleep_range(1000, 2000);
129821d1611aSMarco Felsch 
129921d1611aSMarco Felsch 	ret = regulator_disable(tsdata->vcc);
130021d1611aSMarco Felsch 	if (ret)
130121d1611aSMarco Felsch 		dev_warn(dev, "Failed to disable vcc\n");
130221d1611aSMarco Felsch 
130321d1611aSMarco Felsch 	return 0;
130421d1611aSMarco Felsch }
130521d1611aSMarco Felsch 
130621d1611aSMarco Felsch static int __maybe_unused edt_ft5x06_ts_resume(struct device *dev)
130721d1611aSMarco Felsch {
130821d1611aSMarco Felsch 	struct i2c_client *client = to_i2c_client(dev);
130921d1611aSMarco Felsch 	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
131021d1611aSMarco Felsch 	int ret = 0;
131121d1611aSMarco Felsch 
131221d1611aSMarco Felsch 	if (device_may_wakeup(dev))
131321d1611aSMarco Felsch 		return 0;
131421d1611aSMarco Felsch 
131521d1611aSMarco Felsch 	if (tsdata->suspend_mode == EDT_PMODE_NOT_SUPPORTED)
131621d1611aSMarco Felsch 		return 0;
131721d1611aSMarco Felsch 
131821d1611aSMarco Felsch 	if (tsdata->suspend_mode == EDT_PMODE_POWEROFF) {
131921d1611aSMarco Felsch 		struct gpio_desc *reset_gpio = tsdata->reset_gpio;
132021d1611aSMarco Felsch 
132121d1611aSMarco Felsch 		/*
132221d1611aSMarco Felsch 		 * We can't check if the regulator is a dummy or a real
132321d1611aSMarco Felsch 		 * regulator. So we need to specify the 5ms reset time (T_rst)
132421d1611aSMarco Felsch 		 * here instead of the 100us T_rtp time. We also need to wait
132521d1611aSMarco Felsch 		 * 300ms in case it was a real supply and the power was cutted
132621d1611aSMarco Felsch 		 * of. Toggle the reset pin is also a way to exit the hibernate
132721d1611aSMarco Felsch 		 * mode.
132821d1611aSMarco Felsch 		 */
132921d1611aSMarco Felsch 		gpiod_set_value_cansleep(reset_gpio, 1);
133021d1611aSMarco Felsch 		usleep_range(5000, 6000);
133121d1611aSMarco Felsch 
133221d1611aSMarco Felsch 		ret = regulator_enable(tsdata->vcc);
133321d1611aSMarco Felsch 		if (ret) {
133421d1611aSMarco Felsch 			dev_err(dev, "Failed to enable vcc\n");
133521d1611aSMarco Felsch 			return ret;
133621d1611aSMarco Felsch 		}
133721d1611aSMarco Felsch 
133821d1611aSMarco Felsch 		usleep_range(1000, 2000);
133921d1611aSMarco Felsch 		gpiod_set_value_cansleep(reset_gpio, 0);
134021d1611aSMarco Felsch 		msleep(300);
134121d1611aSMarco Felsch 
134221d1611aSMarco Felsch 		edt_ft5x06_restore_reg_parameters(tsdata);
134321d1611aSMarco Felsch 		enable_irq(tsdata->client->irq);
134421d1611aSMarco Felsch 
134521d1611aSMarco Felsch 		if (tsdata->factory_mode)
134621d1611aSMarco Felsch 			ret = edt_ft5x06_factory_mode(tsdata);
134721d1611aSMarco Felsch 	} else {
134821d1611aSMarco Felsch 		struct gpio_desc *wake_gpio = tsdata->wake_gpio;
134921d1611aSMarco Felsch 
135021d1611aSMarco Felsch 		gpiod_set_value_cansleep(wake_gpio, 0);
135121d1611aSMarco Felsch 		usleep_range(5000, 6000);
135221d1611aSMarco Felsch 		gpiod_set_value_cansleep(wake_gpio, 1);
135321d1611aSMarco Felsch 	}
135421d1611aSMarco Felsch 
135521d1611aSMarco Felsch 
135621d1611aSMarco Felsch 	return ret;
135721d1611aSMarco Felsch }
135821d1611aSMarco Felsch 
135921d1611aSMarco Felsch static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops,
136021d1611aSMarco Felsch 			 edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume);
136121d1611aSMarco Felsch 
1362b1d2a3ecSFranklin S Cooper Jr static const struct edt_i2c_chip_data edt_ft5x06_data = {
1363b1d2a3ecSFranklin S Cooper Jr 	.max_support_points = 5,
1364b1d2a3ecSFranklin S Cooper Jr };
1365b1d2a3ecSFranklin S Cooper Jr 
1366af33e0adSFranklin S Cooper Jr static const struct edt_i2c_chip_data edt_ft5506_data = {
1367af33e0adSFranklin S Cooper Jr 	.max_support_points = 10,
1368af33e0adSFranklin S Cooper Jr };
1369af33e0adSFranklin S Cooper Jr 
1370d1871654SHans de Goede static const struct edt_i2c_chip_data edt_ft6236_data = {
1371d1871654SHans de Goede 	.max_support_points = 2,
1372d1871654SHans de Goede };
1373d1871654SHans de Goede 
137443c4d13eSSimon Budig static const struct i2c_device_id edt_ft5x06_ts_id[] = {
1375b1d2a3ecSFranklin S Cooper Jr 	{ .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data },
1376af33e0adSFranklin S Cooper Jr 	{ .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data },
1377a2f39dacSMarco Felsch 	{ .name = "ev-ft5726", .driver_data = (long)&edt_ft5506_data },
1378d1871654SHans de Goede 	/* Note no edt- prefix for compatibility with the ft6236.c driver */
1379d1871654SHans de Goede 	{ .name = "ft6236", .driver_data = (long)&edt_ft6236_data },
13801730d814SLothar Waßmann 	{ /* sentinel */ }
138143c4d13eSSimon Budig };
138243c4d13eSSimon Budig MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id);
138343c4d13eSSimon Budig 
1384dac90dc2SLothar Waßmann static const struct of_device_id edt_ft5x06_of_match[] = {
1385b1d2a3ecSFranklin S Cooper Jr 	{ .compatible = "edt,edt-ft5206", .data = &edt_ft5x06_data },
1386b1d2a3ecSFranklin S Cooper Jr 	{ .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data },
1387b1d2a3ecSFranklin S Cooper Jr 	{ .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data },
1388af33e0adSFranklin S Cooper Jr 	{ .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data },
1389a2f39dacSMarco Felsch 	{ .compatible = "evervision,ev-ft5726", .data = &edt_ft5506_data },
1390d1871654SHans de Goede 	/* Note focaltech vendor prefix for compatibility with ft6236.c */
1391d1871654SHans de Goede 	{ .compatible = "focaltech,ft6236", .data = &edt_ft6236_data },
1392dac90dc2SLothar Waßmann 	{ /* sentinel */ }
1393dac90dc2SLothar Waßmann };
1394dac90dc2SLothar Waßmann MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match);
1395dac90dc2SLothar Waßmann 
139643c4d13eSSimon Budig static struct i2c_driver edt_ft5x06_ts_driver = {
139743c4d13eSSimon Budig 	.driver = {
139843c4d13eSSimon Budig 		.name = "edt_ft5x06",
1399fc226eb2SAndy Shevchenko 		.of_match_table = edt_ft5x06_of_match,
140021d1611aSMarco Felsch 		.pm = &edt_ft5x06_ts_pm_ops,
14010f58daaaSAhmad Fatoum 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
140243c4d13eSSimon Budig 	},
140343c4d13eSSimon Budig 	.id_table = edt_ft5x06_ts_id,
140443c4d13eSSimon Budig 	.probe    = edt_ft5x06_ts_probe,
14051cb0aa88SBill Pemberton 	.remove   = edt_ft5x06_ts_remove,
140643c4d13eSSimon Budig };
140743c4d13eSSimon Budig 
140843c4d13eSSimon Budig module_i2c_driver(edt_ft5x06_ts_driver);
140943c4d13eSSimon Budig 
141043c4d13eSSimon Budig MODULE_AUTHOR("Simon Budig <simon.budig@kernelconcepts.de>");
141143c4d13eSSimon Budig MODULE_DESCRIPTION("EDT FT5x06 I2C Touchscreen Driver");
14126d3a41abSAndy Shevchenko MODULE_LICENSE("GPL v2");
1413