xref: /linux/drivers/input/touchscreen/zforce_ts.c (revision 7ec462100ef9142344ddbf86f2c3008b97acddbe)
19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2c6d81bd7SHeiko Stübner /*
3c6d81bd7SHeiko Stübner  * Copyright (C) 2012-2013 MundoReader S.L.
4c6d81bd7SHeiko Stübner  * Author: Heiko Stuebner <heiko@sntech.de>
5c6d81bd7SHeiko Stübner  *
6c6d81bd7SHeiko Stübner  * based in parts on Nook zforce driver
7c6d81bd7SHeiko Stübner  *
8c6d81bd7SHeiko Stübner  * Copyright (C) 2010 Barnes & Noble, Inc.
9c6d81bd7SHeiko Stübner  * Author: Pieter Truter<ptruter@intrinsyc.com>
10c6d81bd7SHeiko Stübner  */
11c6d81bd7SHeiko Stübner 
12c6d81bd7SHeiko Stübner #include <linux/delay.h>
13c6d81bd7SHeiko Stübner #include <linux/device.h>
14f820b437SDmitry Torokhov #include <linux/gpio/consumer.h>
15f820b437SDmitry Torokhov #include <linux/i2c.h>
16f820b437SDmitry Torokhov #include <linux/input.h>
17c6d81bd7SHeiko Stübner #include <linux/input/mt.h>
18435e84ecSAndreas Kemnade #include <linux/input/touchscreen.h>
19f820b437SDmitry Torokhov #include <linux/interrupt.h>
20f820b437SDmitry Torokhov #include <linux/module.h>
216f2e6c9bSHeiko Stübner #include <linux/of.h>
22f820b437SDmitry Torokhov #include <linux/property.h>
23f820b437SDmitry Torokhov #include <linux/regulator/consumer.h>
24f820b437SDmitry Torokhov #include <linux/slab.h>
25*5f60d5f6SAl Viro #include <linux/unaligned.h>
26c6d81bd7SHeiko Stübner 
27c6d81bd7SHeiko Stübner #define WAIT_TIMEOUT		msecs_to_jiffies(1000)
28c6d81bd7SHeiko Stübner 
29c6d81bd7SHeiko Stübner #define FRAME_START		0xee
30d333b606SLuis Ortega #define FRAME_MAXSIZE		257
31c6d81bd7SHeiko Stübner 
32c6d81bd7SHeiko Stübner /* Offsets of the different parts of the payload the controller sends */
33c6d81bd7SHeiko Stübner #define PAYLOAD_HEADER		0
34c6d81bd7SHeiko Stübner #define PAYLOAD_LENGTH		1
35c6d81bd7SHeiko Stübner #define PAYLOAD_BODY		2
36c6d81bd7SHeiko Stübner 
37c6d81bd7SHeiko Stübner /* Response offsets */
38c6d81bd7SHeiko Stübner #define RESPONSE_ID		0
39c6d81bd7SHeiko Stübner #define RESPONSE_DATA		1
40c6d81bd7SHeiko Stübner 
41c6d81bd7SHeiko Stübner /* Commands */
42c6d81bd7SHeiko Stübner #define COMMAND_DEACTIVATE	0x00
43c6d81bd7SHeiko Stübner #define COMMAND_INITIALIZE	0x01
44c6d81bd7SHeiko Stübner #define COMMAND_RESOLUTION	0x02
45c6d81bd7SHeiko Stübner #define COMMAND_SETCONFIG	0x03
46c6d81bd7SHeiko Stübner #define COMMAND_DATAREQUEST	0x04
47c6d81bd7SHeiko Stübner #define COMMAND_SCANFREQ	0x08
48c6d81bd7SHeiko Stübner #define COMMAND_STATUS		0X1e
49c6d81bd7SHeiko Stübner 
50c6d81bd7SHeiko Stübner /*
51c6d81bd7SHeiko Stübner  * Responses the controller sends as a result of
52c6d81bd7SHeiko Stübner  * command requests
53c6d81bd7SHeiko Stübner  */
54c6d81bd7SHeiko Stübner #define RESPONSE_DEACTIVATE	0x00
55c6d81bd7SHeiko Stübner #define RESPONSE_INITIALIZE	0x01
56c6d81bd7SHeiko Stübner #define RESPONSE_RESOLUTION	0x02
57c6d81bd7SHeiko Stübner #define RESPONSE_SETCONFIG	0x03
58c6d81bd7SHeiko Stübner #define RESPONSE_SCANFREQ	0x08
59c6d81bd7SHeiko Stübner #define RESPONSE_STATUS		0X1e
60c6d81bd7SHeiko Stübner 
61c6d81bd7SHeiko Stübner /*
62deb49819SLuis Ortega  * Notifications are sent by the touch controller without
63c6d81bd7SHeiko Stübner  * being requested by the driver and include for example
64c6d81bd7SHeiko Stübner  * touch indications
65c6d81bd7SHeiko Stübner  */
66c6d81bd7SHeiko Stübner #define NOTIFICATION_TOUCH		0x04
67c6d81bd7SHeiko Stübner #define NOTIFICATION_BOOTCOMPLETE	0x07
68c6d81bd7SHeiko Stübner #define NOTIFICATION_OVERRUN		0x25
69c6d81bd7SHeiko Stübner #define NOTIFICATION_PROXIMITY		0x26
70c6d81bd7SHeiko Stübner #define NOTIFICATION_INVALID_COMMAND	0xfe
71c6d81bd7SHeiko Stübner 
72c6d81bd7SHeiko Stübner #define ZFORCE_REPORT_POINTS		2
73c6d81bd7SHeiko Stübner #define ZFORCE_MAX_AREA			0xff
74c6d81bd7SHeiko Stübner 
75c6d81bd7SHeiko Stübner #define STATE_DOWN			0
76c6d81bd7SHeiko Stübner #define STATE_MOVE			1
77c6d81bd7SHeiko Stübner #define STATE_UP			2
78c6d81bd7SHeiko Stübner 
79c6d81bd7SHeiko Stübner #define SETCONFIG_DUALTOUCH		(1 << 0)
80c6d81bd7SHeiko Stübner 
81c6d81bd7SHeiko Stübner struct zforce_point {
82c6d81bd7SHeiko Stübner 	int coord_x;
83c6d81bd7SHeiko Stübner 	int coord_y;
84c6d81bd7SHeiko Stübner 	int state;
85c6d81bd7SHeiko Stübner 	int id;
86c6d81bd7SHeiko Stübner 	int area_major;
87c6d81bd7SHeiko Stübner 	int area_minor;
88c6d81bd7SHeiko Stübner 	int orientation;
89c6d81bd7SHeiko Stübner 	int pressure;
90c6d81bd7SHeiko Stübner 	int prblty;
91c6d81bd7SHeiko Stübner };
92c6d81bd7SHeiko Stübner 
93c6d81bd7SHeiko Stübner /*
94c6d81bd7SHeiko Stübner  * @client		the i2c_client
95c6d81bd7SHeiko Stübner  * @input		the input device
96c6d81bd7SHeiko Stübner  * @suspending		in the process of going to suspend (don't emit wakeup
97c6d81bd7SHeiko Stübner  *			events for commands executed to suspend the device)
98c6d81bd7SHeiko Stübner  * @suspended		device suspended
99c6d81bd7SHeiko Stübner  * @command_done	completion to wait for the command result
100deb49819SLuis Ortega  * @command_waiting	the id of the command that is currently waiting
101c6d81bd7SHeiko Stübner  *			for a result
102c6d81bd7SHeiko Stübner  * @command_result	returned result of the command
103c6d81bd7SHeiko Stübner  */
104c6d81bd7SHeiko Stübner struct zforce_ts {
105c6d81bd7SHeiko Stübner 	struct i2c_client	*client;
106c6d81bd7SHeiko Stübner 	struct input_dev	*input;
107435e84ecSAndreas Kemnade 	struct touchscreen_properties prop;
108c6d81bd7SHeiko Stübner 	char			phys[32];
109c6d81bd7SHeiko Stübner 
1102d538095SDirk Behme 	struct gpio_desc	*gpio_int;
1112d538095SDirk Behme 	struct gpio_desc	*gpio_rst;
1122d538095SDirk Behme 
113c6d81bd7SHeiko Stübner 	bool			suspending;
114c6d81bd7SHeiko Stübner 	bool			suspended;
115c6d81bd7SHeiko Stübner 	bool			boot_complete;
116c6d81bd7SHeiko Stübner 
117c6d81bd7SHeiko Stübner 	/* Firmware version information */
118c6d81bd7SHeiko Stübner 	u16			version_major;
119c6d81bd7SHeiko Stübner 	u16			version_minor;
120c6d81bd7SHeiko Stübner 	u16			version_build;
121c6d81bd7SHeiko Stübner 	u16			version_rev;
122c6d81bd7SHeiko Stübner 
123c6d81bd7SHeiko Stübner 	struct completion	command_done;
124c6d81bd7SHeiko Stübner 	int			command_waiting;
125c6d81bd7SHeiko Stübner 	int			command_result;
126c6d81bd7SHeiko Stübner };
127c6d81bd7SHeiko Stübner 
zforce_command(struct zforce_ts * ts,u8 cmd)128c6d81bd7SHeiko Stübner static int zforce_command(struct zforce_ts *ts, u8 cmd)
129c6d81bd7SHeiko Stübner {
130c6d81bd7SHeiko Stübner 	struct i2c_client *client = ts->client;
131c6d81bd7SHeiko Stübner 	char buf[3];
132c6d81bd7SHeiko Stübner 	int ret;
133c6d81bd7SHeiko Stübner 
134c6d81bd7SHeiko Stübner 	dev_dbg(&client->dev, "%s: 0x%x\n", __func__, cmd);
135c6d81bd7SHeiko Stübner 
136c6d81bd7SHeiko Stübner 	buf[0] = FRAME_START;
137c6d81bd7SHeiko Stübner 	buf[1] = 1; /* data size, command only */
138c6d81bd7SHeiko Stübner 	buf[2] = cmd;
139c6d81bd7SHeiko Stübner 
140c6d81bd7SHeiko Stübner 	ret = i2c_master_send(client, &buf[0], ARRAY_SIZE(buf));
141c6d81bd7SHeiko Stübner 	if (ret < 0) {
142c6d81bd7SHeiko Stübner 		dev_err(&client->dev, "i2c send data request error: %d\n", ret);
143c6d81bd7SHeiko Stübner 		return ret;
144c6d81bd7SHeiko Stübner 	}
145c6d81bd7SHeiko Stübner 
146c6d81bd7SHeiko Stübner 	return 0;
147c6d81bd7SHeiko Stübner }
148c6d81bd7SHeiko Stübner 
zforce_send_wait(struct zforce_ts * ts,const char * buf,int len)149c6d81bd7SHeiko Stübner static int zforce_send_wait(struct zforce_ts *ts, const char *buf, int len)
150c6d81bd7SHeiko Stübner {
151c6d81bd7SHeiko Stübner 	struct i2c_client *client = ts->client;
152c6d81bd7SHeiko Stübner 	int ret;
153c6d81bd7SHeiko Stübner 
154c6d81bd7SHeiko Stübner 	dev_dbg(&client->dev, "sending %d bytes for command 0x%x\n",
155c6d81bd7SHeiko Stübner 		buf[1], buf[2]);
156c6d81bd7SHeiko Stübner 
157c6d81bd7SHeiko Stübner 	ts->command_waiting = buf[2];
158c6d81bd7SHeiko Stübner 
159c6d81bd7SHeiko Stübner 	ret = i2c_master_send(client, buf, len);
160c6d81bd7SHeiko Stübner 	if (ret < 0) {
161c6d81bd7SHeiko Stübner 		dev_err(&client->dev, "i2c send data request error: %d\n", ret);
1627e168b81SDmitry Torokhov 		return ret;
163c6d81bd7SHeiko Stübner 	}
164c6d81bd7SHeiko Stübner 
165c6d81bd7SHeiko Stübner 	dev_dbg(&client->dev, "waiting for result for command 0x%x\n", buf[2]);
166c6d81bd7SHeiko Stübner 
167f388412fSDmitry Torokhov 	if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0)
168f388412fSDmitry Torokhov 		return -ETIME;
169c6d81bd7SHeiko Stübner 
170c6d81bd7SHeiko Stübner 	ret = ts->command_result;
171f388412fSDmitry Torokhov 	return 0;
172c6d81bd7SHeiko Stübner }
173c6d81bd7SHeiko Stübner 
zforce_command_wait(struct zforce_ts * ts,u8 cmd)174c6d81bd7SHeiko Stübner static int zforce_command_wait(struct zforce_ts *ts, u8 cmd)
175c6d81bd7SHeiko Stübner {
176c6d81bd7SHeiko Stübner 	struct i2c_client *client = ts->client;
177c6d81bd7SHeiko Stübner 	char buf[3];
1787e168b81SDmitry Torokhov 	int error;
179c6d81bd7SHeiko Stübner 
180c6d81bd7SHeiko Stübner 	dev_dbg(&client->dev, "%s: 0x%x\n", __func__, cmd);
181c6d81bd7SHeiko Stübner 
182c6d81bd7SHeiko Stübner 	buf[0] = FRAME_START;
183c6d81bd7SHeiko Stübner 	buf[1] = 1; /* data size, command only */
184c6d81bd7SHeiko Stübner 	buf[2] = cmd;
185c6d81bd7SHeiko Stübner 
1867e168b81SDmitry Torokhov 	error = zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
1877e168b81SDmitry Torokhov 	if (error) {
1887e168b81SDmitry Torokhov 		dev_err(&client->dev, "i2c send data request error: %d\n",
1897e168b81SDmitry Torokhov 			error);
1907e168b81SDmitry Torokhov 		return error;
191c6d81bd7SHeiko Stübner 	}
192c6d81bd7SHeiko Stübner 
193c6d81bd7SHeiko Stübner 	return 0;
194c6d81bd7SHeiko Stübner }
195c6d81bd7SHeiko Stübner 
zforce_resolution(struct zforce_ts * ts,u16 x,u16 y)196c6d81bd7SHeiko Stübner static int zforce_resolution(struct zforce_ts *ts, u16 x, u16 y)
197c6d81bd7SHeiko Stübner {
198c6d81bd7SHeiko Stübner 	struct i2c_client *client = ts->client;
199c6d81bd7SHeiko Stübner 	char buf[7] = { FRAME_START, 5, COMMAND_RESOLUTION,
200c6d81bd7SHeiko Stübner 			(x & 0xff), ((x >> 8) & 0xff),
201c6d81bd7SHeiko Stübner 			(y & 0xff), ((y >> 8) & 0xff) };
202c6d81bd7SHeiko Stübner 
203c6d81bd7SHeiko Stübner 	dev_dbg(&client->dev, "set resolution to (%d,%d)\n", x, y);
204c6d81bd7SHeiko Stübner 
205c6d81bd7SHeiko Stübner 	return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
206c6d81bd7SHeiko Stübner }
207c6d81bd7SHeiko Stübner 
zforce_scan_frequency(struct zforce_ts * ts,u16 idle,u16 finger,u16 stylus)208c6d81bd7SHeiko Stübner static int zforce_scan_frequency(struct zforce_ts *ts, u16 idle, u16 finger,
209c6d81bd7SHeiko Stübner 				 u16 stylus)
210c6d81bd7SHeiko Stübner {
211c6d81bd7SHeiko Stübner 	struct i2c_client *client = ts->client;
212c6d81bd7SHeiko Stübner 	char buf[9] = { FRAME_START, 7, COMMAND_SCANFREQ,
213c6d81bd7SHeiko Stübner 			(idle & 0xff), ((idle >> 8) & 0xff),
214c6d81bd7SHeiko Stübner 			(finger & 0xff), ((finger >> 8) & 0xff),
215c6d81bd7SHeiko Stübner 			(stylus & 0xff), ((stylus >> 8) & 0xff) };
216c6d81bd7SHeiko Stübner 
217ad697b96SLuis Ortega 	dev_dbg(&client->dev,
218ad697b96SLuis Ortega 		"set scan frequency to (idle: %d, finger: %d, stylus: %d)\n",
219c6d81bd7SHeiko Stübner 		idle, finger, stylus);
220c6d81bd7SHeiko Stübner 
221c6d81bd7SHeiko Stübner 	return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
222c6d81bd7SHeiko Stübner }
223c6d81bd7SHeiko Stübner 
zforce_setconfig(struct zforce_ts * ts,char b1)224c6d81bd7SHeiko Stübner static int zforce_setconfig(struct zforce_ts *ts, char b1)
225c6d81bd7SHeiko Stübner {
226c6d81bd7SHeiko Stübner 	struct i2c_client *client = ts->client;
227c6d81bd7SHeiko Stübner 	char buf[7] = { FRAME_START, 5, COMMAND_SETCONFIG,
228c6d81bd7SHeiko Stübner 			b1, 0, 0, 0 };
229c6d81bd7SHeiko Stübner 
230c6d81bd7SHeiko Stübner 	dev_dbg(&client->dev, "set config to (%d)\n", b1);
231c6d81bd7SHeiko Stübner 
232c6d81bd7SHeiko Stübner 	return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
233c6d81bd7SHeiko Stübner }
234c6d81bd7SHeiko Stübner 
zforce_start(struct zforce_ts * ts)235c6d81bd7SHeiko Stübner static int zforce_start(struct zforce_ts *ts)
236c6d81bd7SHeiko Stübner {
237c6d81bd7SHeiko Stübner 	struct i2c_client *client = ts->client;
2387e168b81SDmitry Torokhov 	int error;
239c6d81bd7SHeiko Stübner 
240c6d81bd7SHeiko Stübner 	dev_dbg(&client->dev, "starting device\n");
241c6d81bd7SHeiko Stübner 
2427e168b81SDmitry Torokhov 	error = zforce_command_wait(ts, COMMAND_INITIALIZE);
2437e168b81SDmitry Torokhov 	if (error) {
2447e168b81SDmitry Torokhov 		dev_err(&client->dev, "Unable to initialize, %d\n", error);
2457e168b81SDmitry Torokhov 		return error;
246c6d81bd7SHeiko Stübner 	}
247c6d81bd7SHeiko Stübner 
2487e168b81SDmitry Torokhov 	error = zforce_resolution(ts, ts->prop.max_x, ts->prop.max_y);
2497e168b81SDmitry Torokhov 	if (error) {
2507e168b81SDmitry Torokhov 		dev_err(&client->dev, "Unable to set resolution, %d\n", error);
2517e168b81SDmitry Torokhov 		goto err_deactivate;
252c6d81bd7SHeiko Stübner 	}
253c6d81bd7SHeiko Stübner 
2547e168b81SDmitry Torokhov 	error = zforce_scan_frequency(ts, 10, 50, 50);
2557e168b81SDmitry Torokhov 	if (error) {
256c6d81bd7SHeiko Stübner 		dev_err(&client->dev, "Unable to set scan frequency, %d\n",
2577e168b81SDmitry Torokhov 			error);
2587e168b81SDmitry Torokhov 		goto err_deactivate;
259c6d81bd7SHeiko Stübner 	}
260c6d81bd7SHeiko Stübner 
2617e168b81SDmitry Torokhov 	error = zforce_setconfig(ts, SETCONFIG_DUALTOUCH);
2627e168b81SDmitry Torokhov 	if (error) {
263c6d81bd7SHeiko Stübner 		dev_err(&client->dev, "Unable to set config\n");
2647e168b81SDmitry Torokhov 		goto err_deactivate;
265c6d81bd7SHeiko Stübner 	}
266c6d81bd7SHeiko Stübner 
267c6d81bd7SHeiko Stübner 	/* start sending touch events */
2687e168b81SDmitry Torokhov 	error = zforce_command(ts, COMMAND_DATAREQUEST);
2697e168b81SDmitry Torokhov 	if (error) {
270c6d81bd7SHeiko Stübner 		dev_err(&client->dev, "Unable to request data\n");
2717e168b81SDmitry Torokhov 		goto err_deactivate;
272c6d81bd7SHeiko Stübner 	}
273c6d81bd7SHeiko Stübner 
274c6d81bd7SHeiko Stübner 	/*
275c6d81bd7SHeiko Stübner 	 * Per NN, initial cal. take max. of 200msec.
276c6d81bd7SHeiko Stübner 	 * Allow time to complete this calibration
277c6d81bd7SHeiko Stübner 	 */
278c6d81bd7SHeiko Stübner 	msleep(200);
279c6d81bd7SHeiko Stübner 
280c6d81bd7SHeiko Stübner 	return 0;
281c6d81bd7SHeiko Stübner 
2827e168b81SDmitry Torokhov err_deactivate:
283c6d81bd7SHeiko Stübner 	zforce_command_wait(ts, COMMAND_DEACTIVATE);
2847e168b81SDmitry Torokhov 	return error;
285c6d81bd7SHeiko Stübner }
286c6d81bd7SHeiko Stübner 
zforce_stop(struct zforce_ts * ts)287c6d81bd7SHeiko Stübner static int zforce_stop(struct zforce_ts *ts)
288c6d81bd7SHeiko Stübner {
289c6d81bd7SHeiko Stübner 	struct i2c_client *client = ts->client;
2907e168b81SDmitry Torokhov 	int error;
291c6d81bd7SHeiko Stübner 
292c6d81bd7SHeiko Stübner 	dev_dbg(&client->dev, "stopping device\n");
293c6d81bd7SHeiko Stübner 
294c6d81bd7SHeiko Stübner 	/* Deactivates touch sensing and puts the device into sleep. */
2957e168b81SDmitry Torokhov 	error = zforce_command_wait(ts, COMMAND_DEACTIVATE);
2967e168b81SDmitry Torokhov 	if (error) {
297c6d81bd7SHeiko Stübner 		dev_err(&client->dev, "could not deactivate device, %d\n",
2987e168b81SDmitry Torokhov 			error);
2997e168b81SDmitry Torokhov 		return error;
300c6d81bd7SHeiko Stübner 	}
301c6d81bd7SHeiko Stübner 
302c6d81bd7SHeiko Stübner 	return 0;
303c6d81bd7SHeiko Stübner }
304c6d81bd7SHeiko Stübner 
zforce_touch_event(struct zforce_ts * ts,u8 * payload)305c6d81bd7SHeiko Stübner static int zforce_touch_event(struct zforce_ts *ts, u8 *payload)
306c6d81bd7SHeiko Stübner {
307c6d81bd7SHeiko Stübner 	struct i2c_client *client = ts->client;
308c6d81bd7SHeiko Stübner 	struct zforce_point point;
309c6d81bd7SHeiko Stübner 	int count, i, num = 0;
31043a48ec5SDmitry Torokhov 	u8 *p;
311c6d81bd7SHeiko Stübner 
312c6d81bd7SHeiko Stübner 	count = payload[0];
313c6d81bd7SHeiko Stübner 	if (count > ZFORCE_REPORT_POINTS) {
314ad697b96SLuis Ortega 		dev_warn(&client->dev,
315ad697b96SLuis Ortega 			 "too many coordinates %d, expected max %d\n",
316c6d81bd7SHeiko Stübner 			 count, ZFORCE_REPORT_POINTS);
317c6d81bd7SHeiko Stübner 		count = ZFORCE_REPORT_POINTS;
318c6d81bd7SHeiko Stübner 	}
319c6d81bd7SHeiko Stübner 
320c6d81bd7SHeiko Stübner 	for (i = 0; i < count; i++) {
32143a48ec5SDmitry Torokhov 		p = &payload[i * 9 + 1];
32243a48ec5SDmitry Torokhov 
32343a48ec5SDmitry Torokhov 		point.coord_x = get_unaligned_le16(&p[0]);
32443a48ec5SDmitry Torokhov 		point.coord_y = get_unaligned_le16(&p[2]);
325c6d81bd7SHeiko Stübner 
326435e84ecSAndreas Kemnade 		if (point.coord_x > ts->prop.max_x ||
327435e84ecSAndreas Kemnade 		    point.coord_y > ts->prop.max_y) {
328c6d81bd7SHeiko Stübner 			dev_warn(&client->dev, "coordinates (%d,%d) invalid\n",
329c6d81bd7SHeiko Stübner 				point.coord_x, point.coord_y);
330c6d81bd7SHeiko Stübner 			point.coord_x = point.coord_y = 0;
331c6d81bd7SHeiko Stübner 		}
332c6d81bd7SHeiko Stübner 
33343a48ec5SDmitry Torokhov 		point.state = p[4] & 0x0f;
33443a48ec5SDmitry Torokhov 		point.id = (p[4] & 0xf0) >> 4;
335c6d81bd7SHeiko Stübner 
336c6d81bd7SHeiko Stübner 		/* determine touch major, minor and orientation */
33743a48ec5SDmitry Torokhov 		point.area_major = max(p[5], p[6]);
33843a48ec5SDmitry Torokhov 		point.area_minor = min(p[5], p[6]);
33943a48ec5SDmitry Torokhov 		point.orientation = p[5] > p[6];
340c6d81bd7SHeiko Stübner 
34143a48ec5SDmitry Torokhov 		point.pressure = p[7];
34243a48ec5SDmitry Torokhov 		point.prblty = p[8];
343c6d81bd7SHeiko Stübner 
344c6d81bd7SHeiko Stübner 		dev_dbg(&client->dev,
345c6d81bd7SHeiko Stübner 			"point %d/%d: state %d, id %d, pressure %d, prblty %d, x %d, y %d, amajor %d, aminor %d, ori %d\n",
346c6d81bd7SHeiko Stübner 			i, count, point.state, point.id,
347c6d81bd7SHeiko Stübner 			point.pressure, point.prblty,
348c6d81bd7SHeiko Stübner 			point.coord_x, point.coord_y,
349c6d81bd7SHeiko Stübner 			point.area_major, point.area_minor,
350c6d81bd7SHeiko Stübner 			point.orientation);
351c6d81bd7SHeiko Stübner 
352c6d81bd7SHeiko Stübner 		/* the zforce id starts with "1", so needs to be decreased */
353c6d81bd7SHeiko Stübner 		input_mt_slot(ts->input, point.id - 1);
354c6d81bd7SHeiko Stübner 
3555e091a49SDmitry Torokhov 		if (input_mt_report_slot_state(ts->input, MT_TOOL_FINGER,
3565e091a49SDmitry Torokhov 					       point.state != STATE_UP)) {
357435e84ecSAndreas Kemnade 			touchscreen_report_pos(ts->input, &ts->prop,
358435e84ecSAndreas Kemnade 					       point.coord_x, point.coord_y,
359435e84ecSAndreas Kemnade 					       true);
360c6d81bd7SHeiko Stübner 			input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
361c6d81bd7SHeiko Stübner 					 point.area_major);
362c6d81bd7SHeiko Stübner 			input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
363c6d81bd7SHeiko Stübner 					 point.area_minor);
364c6d81bd7SHeiko Stübner 			input_report_abs(ts->input, ABS_MT_ORIENTATION,
365c6d81bd7SHeiko Stübner 					 point.orientation);
366c6d81bd7SHeiko Stübner 			num++;
367c6d81bd7SHeiko Stübner 		}
368c6d81bd7SHeiko Stübner 	}
369c6d81bd7SHeiko Stübner 
370c6d81bd7SHeiko Stübner 	input_mt_sync_frame(ts->input);
371c6d81bd7SHeiko Stübner 
372c6d81bd7SHeiko Stübner 	input_mt_report_finger_count(ts->input, num);
373c6d81bd7SHeiko Stübner 
374c6d81bd7SHeiko Stübner 	input_sync(ts->input);
375c6d81bd7SHeiko Stübner 
376c6d81bd7SHeiko Stübner 	return 0;
377c6d81bd7SHeiko Stübner }
378c6d81bd7SHeiko Stübner 
zforce_read_packet(struct zforce_ts * ts,u8 * buf)379c6d81bd7SHeiko Stübner static int zforce_read_packet(struct zforce_ts *ts, u8 *buf)
380c6d81bd7SHeiko Stübner {
381c6d81bd7SHeiko Stübner 	struct i2c_client *client = ts->client;
382c6d81bd7SHeiko Stübner 	int ret;
383c6d81bd7SHeiko Stübner 
384c6d81bd7SHeiko Stübner 	/* read 2 byte message header */
385c6d81bd7SHeiko Stübner 	ret = i2c_master_recv(client, buf, 2);
386c6d81bd7SHeiko Stübner 	if (ret < 0) {
387c6d81bd7SHeiko Stübner 		dev_err(&client->dev, "error reading header: %d\n", ret);
388f388412fSDmitry Torokhov 		return ret;
389c6d81bd7SHeiko Stübner 	}
390c6d81bd7SHeiko Stübner 
391c6d81bd7SHeiko Stübner 	if (buf[PAYLOAD_HEADER] != FRAME_START) {
392c6d81bd7SHeiko Stübner 		dev_err(&client->dev, "invalid frame start: %d\n", buf[0]);
393f388412fSDmitry Torokhov 		return -EIO;
394c6d81bd7SHeiko Stübner 	}
395c6d81bd7SHeiko Stübner 
3965aee41a6SLuis Ortega 	if (buf[PAYLOAD_LENGTH] == 0) {
397c6d81bd7SHeiko Stübner 		dev_err(&client->dev, "invalid payload length: %d\n",
398c6d81bd7SHeiko Stübner 			buf[PAYLOAD_LENGTH]);
399f388412fSDmitry Torokhov 		return -EIO;
400c6d81bd7SHeiko Stübner 	}
401c6d81bd7SHeiko Stübner 
402c6d81bd7SHeiko Stübner 	/* read the message */
403c6d81bd7SHeiko Stübner 	ret = i2c_master_recv(client, &buf[PAYLOAD_BODY], buf[PAYLOAD_LENGTH]);
404c6d81bd7SHeiko Stübner 	if (ret < 0) {
405c6d81bd7SHeiko Stübner 		dev_err(&client->dev, "error reading payload: %d\n", ret);
406f388412fSDmitry Torokhov 		return ret;
407c6d81bd7SHeiko Stübner 	}
408c6d81bd7SHeiko Stübner 
409c6d81bd7SHeiko Stübner 	dev_dbg(&client->dev, "read %d bytes for response command 0x%x\n",
410c6d81bd7SHeiko Stübner 		buf[PAYLOAD_LENGTH], buf[PAYLOAD_BODY]);
411c6d81bd7SHeiko Stübner 
412f388412fSDmitry Torokhov 	return 0;
413c6d81bd7SHeiko Stübner }
414c6d81bd7SHeiko Stübner 
zforce_complete(struct zforce_ts * ts,int cmd,int result)415c6d81bd7SHeiko Stübner static void zforce_complete(struct zforce_ts *ts, int cmd, int result)
416c6d81bd7SHeiko Stübner {
417c6d81bd7SHeiko Stübner 	struct i2c_client *client = ts->client;
418c6d81bd7SHeiko Stübner 
419c6d81bd7SHeiko Stübner 	if (ts->command_waiting == cmd) {
420c6d81bd7SHeiko Stübner 		dev_dbg(&client->dev, "completing command 0x%x\n", cmd);
421c6d81bd7SHeiko Stübner 		ts->command_result = result;
422c6d81bd7SHeiko Stübner 		complete(&ts->command_done);
423c6d81bd7SHeiko Stübner 	} else {
424c6d81bd7SHeiko Stübner 		dev_dbg(&client->dev, "command %d not for us\n", cmd);
425c6d81bd7SHeiko Stübner 	}
426c6d81bd7SHeiko Stübner }
427c6d81bd7SHeiko Stübner 
zforce_irq(int irq,void * dev_id)428499e6127SHeiko Stübner static irqreturn_t zforce_irq(int irq, void *dev_id)
429499e6127SHeiko Stübner {
430499e6127SHeiko Stübner 	struct zforce_ts *ts = dev_id;
431499e6127SHeiko Stübner 	struct i2c_client *client = ts->client;
432499e6127SHeiko Stübner 
433499e6127SHeiko Stübner 	if (ts->suspended && device_may_wakeup(&client->dev))
434499e6127SHeiko Stübner 		pm_wakeup_event(&client->dev, 500);
435499e6127SHeiko Stübner 
436499e6127SHeiko Stübner 	return IRQ_WAKE_THREAD;
437499e6127SHeiko Stübner }
438499e6127SHeiko Stübner 
zforce_irq_thread(int irq,void * dev_id)439499e6127SHeiko Stübner static irqreturn_t zforce_irq_thread(int irq, void *dev_id)
440c6d81bd7SHeiko Stübner {
441c6d81bd7SHeiko Stübner 	struct zforce_ts *ts = dev_id;
442c6d81bd7SHeiko Stübner 	struct i2c_client *client = ts->client;
4437e168b81SDmitry Torokhov 	int error;
444d333b606SLuis Ortega 	u8 payload_buffer[FRAME_MAXSIZE];
445c6d81bd7SHeiko Stübner 	u8 *payload;
4469ba33f61SDmitry Torokhov 	bool suspending;
447c6d81bd7SHeiko Stübner 
448c6d81bd7SHeiko Stübner 	/*
449499e6127SHeiko Stübner 	 * When still suspended, return.
450c6d81bd7SHeiko Stübner 	 * Due to the level-interrupt we will get re-triggered later.
451c6d81bd7SHeiko Stübner 	 */
452c6d81bd7SHeiko Stübner 	if (ts->suspended) {
453c6d81bd7SHeiko Stübner 		msleep(20);
454c6d81bd7SHeiko Stübner 		return IRQ_HANDLED;
455c6d81bd7SHeiko Stübner 	}
456c6d81bd7SHeiko Stübner 
457c6d81bd7SHeiko Stübner 	dev_dbg(&client->dev, "handling interrupt\n");
458c6d81bd7SHeiko Stübner 
459c6d81bd7SHeiko Stübner 	/* Don't emit wakeup events from commands run by zforce_suspend */
4609ba33f61SDmitry Torokhov 	suspending = READ_ONCE(ts->suspending);
4619ba33f61SDmitry Torokhov 	if (!suspending && device_may_wakeup(&client->dev))
462c6d81bd7SHeiko Stübner 		pm_stay_awake(&client->dev);
463c6d81bd7SHeiko Stübner 
46462f46669SDirk Behme 	/*
46562f46669SDirk Behme 	 * Run at least once and exit the loop if
46662f46669SDirk Behme 	 * - the optional interrupt GPIO isn't specified
46762f46669SDirk Behme 	 *   (there is only one packet read per ISR invocation, then)
46862f46669SDirk Behme 	 * or
46962f46669SDirk Behme 	 * - the GPIO isn't active any more
47062f46669SDirk Behme 	 *   (packet read until the level GPIO indicates that there is
47162f46669SDirk Behme 	 *    no IRQ any more)
47262f46669SDirk Behme 	 */
47362f46669SDirk Behme 	do {
4747e168b81SDmitry Torokhov 		error = zforce_read_packet(ts, payload_buffer);
4757e168b81SDmitry Torokhov 		if (error) {
476ad697b96SLuis Ortega 			dev_err(&client->dev,
4777e168b81SDmitry Torokhov 				"could not read packet, ret: %d\n", error);
478c6d81bd7SHeiko Stübner 			break;
479c6d81bd7SHeiko Stübner 		}
480c6d81bd7SHeiko Stübner 
481c6d81bd7SHeiko Stübner 		payload =  &payload_buffer[PAYLOAD_BODY];
482c6d81bd7SHeiko Stübner 
483c6d81bd7SHeiko Stübner 		switch (payload[RESPONSE_ID]) {
484c6d81bd7SHeiko Stübner 		case NOTIFICATION_TOUCH:
485c6d81bd7SHeiko Stübner 			/*
486c6d81bd7SHeiko Stübner 			 * Always report touch-events received while
487c6d81bd7SHeiko Stübner 			 * suspending, when being a wakeup source
488c6d81bd7SHeiko Stübner 			 */
4899ba33f61SDmitry Torokhov 			if (suspending && device_may_wakeup(&client->dev))
490c6d81bd7SHeiko Stübner 				pm_wakeup_event(&client->dev, 500);
491c6d81bd7SHeiko Stübner 			zforce_touch_event(ts, &payload[RESPONSE_DATA]);
492c6d81bd7SHeiko Stübner 			break;
493c6d81bd7SHeiko Stübner 
494c6d81bd7SHeiko Stübner 		case NOTIFICATION_BOOTCOMPLETE:
495c6d81bd7SHeiko Stübner 			ts->boot_complete = payload[RESPONSE_DATA];
496c6d81bd7SHeiko Stübner 			zforce_complete(ts, payload[RESPONSE_ID], 0);
497c6d81bd7SHeiko Stübner 			break;
498c6d81bd7SHeiko Stübner 
499c6d81bd7SHeiko Stübner 		case RESPONSE_INITIALIZE:
500c6d81bd7SHeiko Stübner 		case RESPONSE_DEACTIVATE:
501c6d81bd7SHeiko Stübner 		case RESPONSE_SETCONFIG:
502c6d81bd7SHeiko Stübner 		case RESPONSE_RESOLUTION:
503c6d81bd7SHeiko Stübner 		case RESPONSE_SCANFREQ:
504c6d81bd7SHeiko Stübner 			zforce_complete(ts, payload[RESPONSE_ID],
505c6d81bd7SHeiko Stübner 					payload[RESPONSE_DATA]);
506c6d81bd7SHeiko Stübner 			break;
507c6d81bd7SHeiko Stübner 
508c6d81bd7SHeiko Stübner 		case RESPONSE_STATUS:
509c6d81bd7SHeiko Stübner 			/*
510c6d81bd7SHeiko Stübner 			 * Version Payload Results
511c6d81bd7SHeiko Stübner 			 * [2:major] [2:minor] [2:build] [2:rev]
512c6d81bd7SHeiko Stübner 			 */
51383b6549eSDmitry Torokhov 			ts->version_major =
51483b6549eSDmitry Torokhov 				get_unaligned_le16(&payload[RESPONSE_DATA]);
51583b6549eSDmitry Torokhov 			ts->version_minor =
51683b6549eSDmitry Torokhov 				get_unaligned_le16(&payload[RESPONSE_DATA + 2]);
51783b6549eSDmitry Torokhov 			ts->version_build =
51883b6549eSDmitry Torokhov 				get_unaligned_le16(&payload[RESPONSE_DATA + 4]);
51983b6549eSDmitry Torokhov 			ts->version_rev =
52083b6549eSDmitry Torokhov 				get_unaligned_le16(&payload[RESPONSE_DATA + 6]);
52183b6549eSDmitry Torokhov 
522ad697b96SLuis Ortega 			dev_dbg(&ts->client->dev,
523ad697b96SLuis Ortega 				"Firmware Version %04x:%04x %04x:%04x\n",
524c6d81bd7SHeiko Stübner 				ts->version_major, ts->version_minor,
525c6d81bd7SHeiko Stübner 				ts->version_build, ts->version_rev);
526c6d81bd7SHeiko Stübner 
527c6d81bd7SHeiko Stübner 			zforce_complete(ts, payload[RESPONSE_ID], 0);
528c6d81bd7SHeiko Stübner 			break;
529c6d81bd7SHeiko Stübner 
530c6d81bd7SHeiko Stübner 		case NOTIFICATION_INVALID_COMMAND:
531c6d81bd7SHeiko Stübner 			dev_err(&ts->client->dev, "invalid command: 0x%x\n",
532c6d81bd7SHeiko Stübner 				payload[RESPONSE_DATA]);
533c6d81bd7SHeiko Stübner 			break;
534c6d81bd7SHeiko Stübner 
535c6d81bd7SHeiko Stübner 		default:
536ad697b96SLuis Ortega 			dev_err(&ts->client->dev,
537ad697b96SLuis Ortega 				"unrecognized response id: 0x%x\n",
538c6d81bd7SHeiko Stübner 				payload[RESPONSE_ID]);
539c6d81bd7SHeiko Stübner 			break;
540c6d81bd7SHeiko Stübner 		}
54162f46669SDirk Behme 	} while (gpiod_get_value_cansleep(ts->gpio_int));
542c6d81bd7SHeiko Stübner 
5439ba33f61SDmitry Torokhov 	if (!suspending && device_may_wakeup(&client->dev))
544c6d81bd7SHeiko Stübner 		pm_relax(&client->dev);
545c6d81bd7SHeiko Stübner 
546c6d81bd7SHeiko Stübner 	dev_dbg(&client->dev, "finished interrupt\n");
547c6d81bd7SHeiko Stübner 
548c6d81bd7SHeiko Stübner 	return IRQ_HANDLED;
549c6d81bd7SHeiko Stübner }
550c6d81bd7SHeiko Stübner 
zforce_input_open(struct input_dev * dev)551c6d81bd7SHeiko Stübner static int zforce_input_open(struct input_dev *dev)
552c6d81bd7SHeiko Stübner {
553c6d81bd7SHeiko Stübner 	struct zforce_ts *ts = input_get_drvdata(dev);
554c6d81bd7SHeiko Stübner 
555a47fcbc2SJavier Martinez Canillas 	return zforce_start(ts);
556c6d81bd7SHeiko Stübner }
557c6d81bd7SHeiko Stübner 
zforce_input_close(struct input_dev * dev)558c6d81bd7SHeiko Stübner static void zforce_input_close(struct input_dev *dev)
559c6d81bd7SHeiko Stübner {
560c6d81bd7SHeiko Stübner 	struct zforce_ts *ts = input_get_drvdata(dev);
561c6d81bd7SHeiko Stübner 	struct i2c_client *client = ts->client;
5627e168b81SDmitry Torokhov 	int error;
563c6d81bd7SHeiko Stübner 
5647e168b81SDmitry Torokhov 	error = zforce_stop(ts);
5657e168b81SDmitry Torokhov 	if (error)
566c6d81bd7SHeiko Stübner 		dev_warn(&client->dev, "stopping zforce failed\n");
567c6d81bd7SHeiko Stübner }
568c6d81bd7SHeiko Stübner 
__zforce_suspend(struct zforce_ts * ts)5695c200267SDmitry Torokhov static int __zforce_suspend(struct zforce_ts *ts)
570c6d81bd7SHeiko Stübner {
5715c200267SDmitry Torokhov 	struct i2c_client *client = ts->client;
572c6d81bd7SHeiko Stübner 	struct input_dev *input = ts->input;
5737e168b81SDmitry Torokhov 	int error;
574c6d81bd7SHeiko Stübner 
5755c200267SDmitry Torokhov 	guard(mutex)(&input->mutex);
576c6d81bd7SHeiko Stübner 
577c6d81bd7SHeiko Stübner 	/*
578c6d81bd7SHeiko Stübner 	 * When configured as a wakeup source device should always wake
579c6d81bd7SHeiko Stübner 	 * the system, therefore start device if necessary.
580c6d81bd7SHeiko Stübner 	 */
581c6d81bd7SHeiko Stübner 	if (device_may_wakeup(&client->dev)) {
582c6d81bd7SHeiko Stübner 		dev_dbg(&client->dev, "suspend while being a wakeup source\n");
583c6d81bd7SHeiko Stübner 
584c6d81bd7SHeiko Stübner 		/* Need to start device, if not open, to be a wakeup source. */
585d69f0a43SAndrzej Pietrasiewicz 		if (!input_device_enabled(input)) {
5867e168b81SDmitry Torokhov 			error = zforce_start(ts);
5877e168b81SDmitry Torokhov 			if (error)
5887e168b81SDmitry Torokhov 				return error;
589c6d81bd7SHeiko Stübner 		}
590c6d81bd7SHeiko Stübner 
591c6d81bd7SHeiko Stübner 		enable_irq_wake(client->irq);
592d69f0a43SAndrzej Pietrasiewicz 	} else if (input_device_enabled(input)) {
593ad697b96SLuis Ortega 		dev_dbg(&client->dev,
594ad697b96SLuis Ortega 			"suspend without being a wakeup source\n");
595c6d81bd7SHeiko Stübner 
5967e168b81SDmitry Torokhov 		error = zforce_stop(ts);
5977e168b81SDmitry Torokhov 		if (error)
5987e168b81SDmitry Torokhov 			return error;
599c6d81bd7SHeiko Stübner 
600c6d81bd7SHeiko Stübner 		disable_irq(client->irq);
601c6d81bd7SHeiko Stübner 	}
602c6d81bd7SHeiko Stübner 
603c6d81bd7SHeiko Stübner 	ts->suspended = true;
6045c200267SDmitry Torokhov 	return 0;
6055c200267SDmitry Torokhov }
606c6d81bd7SHeiko Stübner 
zforce_suspend(struct device * dev)6075c200267SDmitry Torokhov static int zforce_suspend(struct device *dev)
6085c200267SDmitry Torokhov {
6095c200267SDmitry Torokhov 	struct i2c_client *client = to_i2c_client(dev);
6105c200267SDmitry Torokhov 	struct zforce_ts *ts = i2c_get_clientdata(client);
6115c200267SDmitry Torokhov 	int ret;
6125c200267SDmitry Torokhov 
6135c200267SDmitry Torokhov 	WRITE_ONCE(ts->suspending, true);
6145c200267SDmitry Torokhov 	smp_mb();
6155c200267SDmitry Torokhov 
6165c200267SDmitry Torokhov 	ret = __zforce_suspend(ts);
6175c200267SDmitry Torokhov 
6189ba33f61SDmitry Torokhov 	smp_mb();
6199ba33f61SDmitry Torokhov 	WRITE_ONCE(ts->suspending, false);
6209ba33f61SDmitry Torokhov 
621c6d81bd7SHeiko Stübner 	return ret;
622c6d81bd7SHeiko Stübner }
623c6d81bd7SHeiko Stübner 
zforce_resume(struct device * dev)624dc69e98aSJonathan Cameron static int zforce_resume(struct device *dev)
625c6d81bd7SHeiko Stübner {
626c6d81bd7SHeiko Stübner 	struct i2c_client *client = to_i2c_client(dev);
627c6d81bd7SHeiko Stübner 	struct zforce_ts *ts = i2c_get_clientdata(client);
628c6d81bd7SHeiko Stübner 	struct input_dev *input = ts->input;
6297e168b81SDmitry Torokhov 	int error;
630c6d81bd7SHeiko Stübner 
6315c200267SDmitry Torokhov 	guard(mutex)(&input->mutex);
632c6d81bd7SHeiko Stübner 
633c6d81bd7SHeiko Stübner 	ts->suspended = false;
634c6d81bd7SHeiko Stübner 
635c6d81bd7SHeiko Stübner 	if (device_may_wakeup(&client->dev)) {
636c6d81bd7SHeiko Stübner 		dev_dbg(&client->dev, "resume from being a wakeup source\n");
637c6d81bd7SHeiko Stübner 
638c6d81bd7SHeiko Stübner 		disable_irq_wake(client->irq);
639c6d81bd7SHeiko Stübner 
640c6d81bd7SHeiko Stübner 		/* need to stop device if it was not open on suspend */
641d69f0a43SAndrzej Pietrasiewicz 		if (!input_device_enabled(input)) {
6427e168b81SDmitry Torokhov 			error = zforce_stop(ts);
6437e168b81SDmitry Torokhov 			if (error)
6447e168b81SDmitry Torokhov 				return error;
645c6d81bd7SHeiko Stübner 		}
646d69f0a43SAndrzej Pietrasiewicz 	} else if (input_device_enabled(input)) {
647c6d81bd7SHeiko Stübner 		dev_dbg(&client->dev, "resume without being a wakeup source\n");
648c6d81bd7SHeiko Stübner 
649c6d81bd7SHeiko Stübner 		enable_irq(client->irq);
650c6d81bd7SHeiko Stübner 
6517e168b81SDmitry Torokhov 		error = zforce_start(ts);
6527e168b81SDmitry Torokhov 		if (error)
6537e168b81SDmitry Torokhov 			return error;
654c6d81bd7SHeiko Stübner 	}
655c6d81bd7SHeiko Stübner 
6565c200267SDmitry Torokhov 	return 0;
657c6d81bd7SHeiko Stübner }
658c6d81bd7SHeiko Stübner 
659dc69e98aSJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(zforce_pm_ops, zforce_suspend, zforce_resume);
660c6d81bd7SHeiko Stübner 
zforce_reset(void * data)661c6d81bd7SHeiko Stübner static void zforce_reset(void *data)
662c6d81bd7SHeiko Stübner {
663c6d81bd7SHeiko Stübner 	struct zforce_ts *ts = data;
664c6d81bd7SHeiko Stübner 
6656a5180c6SDmitry Torokhov 	gpiod_set_value_cansleep(ts->gpio_rst, 1);
66639740370SHeiko Stuebner 	udelay(10);
667c6d81bd7SHeiko Stübner }
668c6d81bd7SHeiko Stübner 
zforce_ts_parse_legacy_properties(struct zforce_ts * ts)669f820b437SDmitry Torokhov static void zforce_ts_parse_legacy_properties(struct zforce_ts *ts)
6706f2e6c9bSHeiko Stübner {
671f820b437SDmitry Torokhov 	u32 x_max = 0;
672f820b437SDmitry Torokhov 	u32 y_max = 0;
6736f2e6c9bSHeiko Stübner 
674f820b437SDmitry Torokhov 	device_property_read_u32(&ts->client->dev, "x-size", &x_max);
675f820b437SDmitry Torokhov 	input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, x_max, 0, 0);
6766f2e6c9bSHeiko Stübner 
677f820b437SDmitry Torokhov 	device_property_read_u32(&ts->client->dev, "y-size", &y_max);
678f820b437SDmitry Torokhov 	input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, y_max, 0, 0);
6796f2e6c9bSHeiko Stübner }
6806f2e6c9bSHeiko Stübner 
zforce_probe(struct i2c_client * client)68156232e93SUwe Kleine-König static int zforce_probe(struct i2c_client *client)
682c6d81bd7SHeiko Stübner {
683c6d81bd7SHeiko Stübner 	struct zforce_ts *ts;
684c6d81bd7SHeiko Stübner 	struct input_dev *input_dev;
6857e168b81SDmitry Torokhov 	int error;
686c6d81bd7SHeiko Stübner 
687c6d81bd7SHeiko Stübner 	ts = devm_kzalloc(&client->dev, sizeof(struct zforce_ts), GFP_KERNEL);
688c6d81bd7SHeiko Stübner 	if (!ts)
689c6d81bd7SHeiko Stübner 		return -ENOMEM;
690c6d81bd7SHeiko Stübner 
69162f46669SDirk Behme 	ts->gpio_rst = devm_gpiod_get_optional(&client->dev, "reset",
69262f46669SDirk Behme 					       GPIOD_OUT_HIGH);
6937e168b81SDmitry Torokhov 	error = PTR_ERR_OR_ZERO(ts->gpio_rst);
6947e168b81SDmitry Torokhov 	if (error)
6957e168b81SDmitry Torokhov 		return dev_err_probe(&client->dev, error,
6967e168b81SDmitry Torokhov 				     "failed to request reset GPIO\n");
69762f46669SDirk Behme 
69862f46669SDirk Behme 	if (ts->gpio_rst) {
69962f46669SDirk Behme 		ts->gpio_int = devm_gpiod_get_optional(&client->dev, "irq",
70062f46669SDirk Behme 						       GPIOD_IN);
7017e168b81SDmitry Torokhov 		error = PTR_ERR_OR_ZERO(ts->gpio_int);
7027e168b81SDmitry Torokhov 		if (error)
7037e168b81SDmitry Torokhov 			return dev_err_probe(&client->dev, error,
7047e168b81SDmitry Torokhov 					     "failed to request interrupt GPIO\n");
70562f46669SDirk Behme 	} else {
70662f46669SDirk Behme 		/*
70762f46669SDirk Behme 		 * Deprecated GPIO handling for compatibility
70862f46669SDirk Behme 		 * with legacy binding.
70962f46669SDirk Behme 		 */
71062f46669SDirk Behme 
7112d538095SDirk Behme 		/* INT GPIO */
71262f46669SDirk Behme 		ts->gpio_int = devm_gpiod_get_index(&client->dev, NULL, 0,
71362f46669SDirk Behme 						    GPIOD_IN);
7147e168b81SDmitry Torokhov 
7157e168b81SDmitry Torokhov 		error = PTR_ERR_OR_ZERO(ts->gpio_int);
7167e168b81SDmitry Torokhov 		if (error)
7177e168b81SDmitry Torokhov 			return dev_err_probe(&client->dev, error,
7187e168b81SDmitry Torokhov 					     "failed to request interrupt GPIO\n");
719c6d81bd7SHeiko Stübner 
7202d538095SDirk Behme 		/* RST GPIO */
7212d538095SDirk Behme 		ts->gpio_rst = devm_gpiod_get_index(&client->dev, NULL, 1,
7222d538095SDirk Behme 					    GPIOD_OUT_HIGH);
7237e168b81SDmitry Torokhov 		error = PTR_ERR_OR_ZERO(ts->gpio_rst);
7247e168b81SDmitry Torokhov 		if (error)
7257e168b81SDmitry Torokhov 			return dev_err_probe(&client->dev, error,
7267e168b81SDmitry Torokhov 					     "failed to request reset GPIO\n");
72762f46669SDirk Behme 	}
728c6d81bd7SHeiko Stübner 
7298f2ef261SDmitry Torokhov 	error = devm_regulator_get_enable(&client->dev, "vdd");
73016ff0224SDmitry Torokhov 	if (error)
7317e168b81SDmitry Torokhov 		return dev_err_probe(&client->dev, error,
7327e168b81SDmitry Torokhov 				     "failed to request vdd supply\n");
73316ff0224SDmitry Torokhov 
73439740370SHeiko Stuebner 	/*
73516ff0224SDmitry Torokhov 	 * According to datasheet add 100us grace time after regular
73639740370SHeiko Stuebner 	 * regulator enable delay.
73739740370SHeiko Stuebner 	 */
73816ff0224SDmitry Torokhov 	usleep_range(100, 200);
73939740370SHeiko Stuebner 
7407e168b81SDmitry Torokhov 	error = devm_add_action_or_reset(&client->dev, zforce_reset, ts);
7417e168b81SDmitry Torokhov 	if (error)
7427e168b81SDmitry Torokhov 		return dev_err_probe(&client->dev, error,
7437e168b81SDmitry Torokhov 				     "failed to register reset action\n");
744c6d81bd7SHeiko Stübner 
745c6d81bd7SHeiko Stübner 	snprintf(ts->phys, sizeof(ts->phys),
746c6d81bd7SHeiko Stübner 		 "%s/input0", dev_name(&client->dev));
747c6d81bd7SHeiko Stübner 
748c6d81bd7SHeiko Stübner 	input_dev = devm_input_allocate_device(&client->dev);
7497e168b81SDmitry Torokhov 	if (!input_dev)
7507e168b81SDmitry Torokhov 		return dev_err_probe(&client->dev, -ENOMEM,
7517e168b81SDmitry Torokhov 				     "could not allocate input device\n");
752c6d81bd7SHeiko Stübner 
753c6d81bd7SHeiko Stübner 	ts->client = client;
754c6d81bd7SHeiko Stübner 	ts->input = input_dev;
755c6d81bd7SHeiko Stübner 
756c6d81bd7SHeiko Stübner 	input_dev->name = "Neonode zForce touchscreen";
757c6d81bd7SHeiko Stübner 	input_dev->phys = ts->phys;
758c6d81bd7SHeiko Stübner 	input_dev->id.bustype = BUS_I2C;
759c6d81bd7SHeiko Stübner 
760c6d81bd7SHeiko Stübner 	input_dev->open = zforce_input_open;
761c6d81bd7SHeiko Stübner 	input_dev->close = zforce_input_close;
762c6d81bd7SHeiko Stübner 
763f820b437SDmitry Torokhov 	zforce_ts_parse_legacy_properties(ts);
764435e84ecSAndreas Kemnade 	touchscreen_parse_properties(input_dev, true, &ts->prop);
7657e168b81SDmitry Torokhov 	if (ts->prop.max_x == 0 || ts->prop.max_y == 0)
7667e168b81SDmitry Torokhov 		return dev_err_probe(&client->dev, -EINVAL, "no size specified");
767435e84ecSAndreas Kemnade 
768c6d81bd7SHeiko Stübner 	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
769c6d81bd7SHeiko Stübner 			     ZFORCE_MAX_AREA, 0, 0);
770c6d81bd7SHeiko Stübner 	input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0,
771c6d81bd7SHeiko Stübner 			     ZFORCE_MAX_AREA, 0, 0);
772c6d81bd7SHeiko Stübner 	input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
77362445594SDmitry Torokhov 
7747e168b81SDmitry Torokhov 	error = input_mt_init_slots(input_dev, ZFORCE_REPORT_POINTS,
77562445594SDmitry Torokhov 				  INPUT_MT_DIRECT);
7767e168b81SDmitry Torokhov 	if (error)
7777e168b81SDmitry Torokhov 		return error;
778c6d81bd7SHeiko Stübner 
779c6d81bd7SHeiko Stübner 	input_set_drvdata(ts->input, ts);
780c6d81bd7SHeiko Stübner 
781c6d81bd7SHeiko Stübner 	init_completion(&ts->command_done);
782c6d81bd7SHeiko Stübner 
783c6d81bd7SHeiko Stübner 	/*
784c6d81bd7SHeiko Stübner 	 * The zforce pulls the interrupt low when it has data ready.
785c6d81bd7SHeiko Stübner 	 * After it is triggered the isr thread runs until all the available
786c6d81bd7SHeiko Stübner 	 * packets have been read and the interrupt is high again.
787c6d81bd7SHeiko Stübner 	 * Therefore we can trigger the interrupt anytime it is low and do
788c6d81bd7SHeiko Stübner 	 * not need to limit it to the interrupt edge.
789c6d81bd7SHeiko Stübner 	 */
7907e168b81SDmitry Torokhov 	error = devm_request_threaded_irq(&client->dev, client->irq,
791499e6127SHeiko Stübner 					  zforce_irq, zforce_irq_thread,
7927846bd8bSDmitry Torokhov 					  IRQF_ONESHOT, input_dev->name, ts);
7937e168b81SDmitry Torokhov 	if (error)
7947e168b81SDmitry Torokhov 		return dev_err_probe(&client->dev, error,
7957e168b81SDmitry Torokhov 				     "irq %d request failed\n", client->irq);
796c6d81bd7SHeiko Stübner 
797c6d81bd7SHeiko Stübner 	i2c_set_clientdata(client, ts);
798c6d81bd7SHeiko Stübner 
799c6d81bd7SHeiko Stübner 	/* let the controller boot */
8006a5180c6SDmitry Torokhov 	gpiod_set_value_cansleep(ts->gpio_rst, 0);
801c6d81bd7SHeiko Stübner 
802c6d81bd7SHeiko Stübner 	ts->command_waiting = NOTIFICATION_BOOTCOMPLETE;
803c6d81bd7SHeiko Stübner 	if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0)
804c6d81bd7SHeiko Stübner 		dev_warn(&client->dev, "bootcomplete timed out\n");
805c6d81bd7SHeiko Stübner 
806c6d81bd7SHeiko Stübner 	/* need to start device to get version information */
8077e168b81SDmitry Torokhov 	error = zforce_command_wait(ts, COMMAND_INITIALIZE);
8087e168b81SDmitry Torokhov 	if (error)
8097e168b81SDmitry Torokhov 		return dev_err_probe(&client->dev, error, "unable to initialize\n");
810c6d81bd7SHeiko Stübner 
811deb49819SLuis Ortega 	/* this gets the firmware version among other information */
8127e168b81SDmitry Torokhov 	error = zforce_command_wait(ts, COMMAND_STATUS);
8137e168b81SDmitry Torokhov 	if (error) {
8147e168b81SDmitry Torokhov 		dev_err_probe(&client->dev, error, "couldn't get status\n");
815c6d81bd7SHeiko Stübner 		zforce_stop(ts);
8167e168b81SDmitry Torokhov 		return error;
817c6d81bd7SHeiko Stübner 	}
818c6d81bd7SHeiko Stübner 
819c6d81bd7SHeiko Stübner 	/* stop device and put it into sleep until it is opened */
8207e168b81SDmitry Torokhov 	error = zforce_stop(ts);
8217e168b81SDmitry Torokhov 	if (error)
8227e168b81SDmitry Torokhov 		return error;
823c6d81bd7SHeiko Stübner 
824c6d81bd7SHeiko Stübner 	device_set_wakeup_capable(&client->dev, true);
825c6d81bd7SHeiko Stübner 
8267e168b81SDmitry Torokhov 	error = input_register_device(input_dev);
8277e168b81SDmitry Torokhov 	if (error)
8287e168b81SDmitry Torokhov 		return dev_err_probe(&client->dev, error,
8297e168b81SDmitry Torokhov 				     "could not register input device\n");
830c6d81bd7SHeiko Stübner 
831c6d81bd7SHeiko Stübner 	return 0;
832c6d81bd7SHeiko Stübner }
833c6d81bd7SHeiko Stübner 
834b5ed81ccSDmitry Torokhov static const struct i2c_device_id zforce_idtable[] = {
8355852f2afSUwe Kleine-König 	{ "zforce-ts" },
836c6d81bd7SHeiko Stübner 	{ }
837c6d81bd7SHeiko Stübner };
838c6d81bd7SHeiko Stübner MODULE_DEVICE_TABLE(i2c, zforce_idtable);
839c6d81bd7SHeiko Stübner 
8406f2e6c9bSHeiko Stübner #ifdef CONFIG_OF
841f3f63193SJingoo Han static const struct of_device_id zforce_dt_idtable[] = {
8426f2e6c9bSHeiko Stübner 	{ .compatible = "neonode,zforce" },
8436f2e6c9bSHeiko Stübner 	{},
8446f2e6c9bSHeiko Stübner };
8456f2e6c9bSHeiko Stübner MODULE_DEVICE_TABLE(of, zforce_dt_idtable);
8466f2e6c9bSHeiko Stübner #endif
8476f2e6c9bSHeiko Stübner 
848c6d81bd7SHeiko Stübner static struct i2c_driver zforce_driver = {
849c6d81bd7SHeiko Stübner 	.driver = {
850c6d81bd7SHeiko Stübner 		.name	= "zforce-ts",
851dc69e98aSJonathan Cameron 		.pm	= pm_sleep_ptr(&zforce_pm_ops),
8526f2e6c9bSHeiko Stübner 		.of_match_table	= of_match_ptr(zforce_dt_idtable),
8534172a64eSDmitry Torokhov 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
854c6d81bd7SHeiko Stübner 	},
855d8bde56dSUwe Kleine-König 	.probe		= zforce_probe,
856c6d81bd7SHeiko Stübner 	.id_table	= zforce_idtable,
857c6d81bd7SHeiko Stübner };
858c6d81bd7SHeiko Stübner 
859c6d81bd7SHeiko Stübner module_i2c_driver(zforce_driver);
860c6d81bd7SHeiko Stübner 
861c6d81bd7SHeiko Stübner MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
862c6d81bd7SHeiko Stübner MODULE_DESCRIPTION("zForce TouchScreen Driver");
863c6d81bd7SHeiko Stübner MODULE_LICENSE("GPL");
864