108dbd0f8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
24065d1e7SJavier Martinez Canillas /*
34065d1e7SJavier Martinez Canillas  * Core Source for:
44065d1e7SJavier Martinez Canillas  * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers.
54065d1e7SJavier Martinez Canillas  * For use with Cypress Txx3xx parts.
64065d1e7SJavier Martinez Canillas  * Supported parts include:
74065d1e7SJavier Martinez Canillas  * CY8CTST341
84065d1e7SJavier Martinez Canillas  * CY8CTMA340
94065d1e7SJavier Martinez Canillas  *
104065d1e7SJavier Martinez Canillas  * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
114065d1e7SJavier Martinez Canillas  * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
124065d1e7SJavier Martinez Canillas  *
134065d1e7SJavier Martinez Canillas  * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
144065d1e7SJavier Martinez Canillas  */
154065d1e7SJavier Martinez Canillas 
164065d1e7SJavier Martinez Canillas #include <linux/delay.h>
174065d1e7SJavier Martinez Canillas #include <linux/input.h>
184065d1e7SJavier Martinez Canillas #include <linux/input/mt.h>
19707b61bbSOreste Salerno #include <linux/input/touchscreen.h>
204065d1e7SJavier Martinez Canillas #include <linux/gpio.h>
214065d1e7SJavier Martinez Canillas #include <linux/interrupt.h>
224065d1e7SJavier Martinez Canillas #include <linux/slab.h>
23707b61bbSOreste Salerno #include <linux/property.h>
24707b61bbSOreste Salerno #include <linux/gpio/consumer.h>
256cf3b3abSLinus Walleij #include <linux/regulator/consumer.h>
264065d1e7SJavier Martinez Canillas 
274065d1e7SJavier Martinez Canillas #include "cyttsp_core.h"
284065d1e7SJavier Martinez Canillas 
294065d1e7SJavier Martinez Canillas /* Bootloader number of command keys */
304065d1e7SJavier Martinez Canillas #define CY_NUM_BL_KEYS		8
314065d1e7SJavier Martinez Canillas 
324065d1e7SJavier Martinez Canillas /* helpers */
334065d1e7SJavier Martinez Canillas #define GET_NUM_TOUCHES(x)		((x) & 0x0F)
344065d1e7SJavier Martinez Canillas #define IS_LARGE_AREA(x)		(((x) & 0x10) >> 4)
354065d1e7SJavier Martinez Canillas #define IS_BAD_PKT(x)			((x) & 0x20)
364065d1e7SJavier Martinez Canillas #define IS_VALID_APP(x)			((x) & 0x01)
374065d1e7SJavier Martinez Canillas #define IS_OPERATIONAL_ERR(x)		((x) & 0x3F)
384065d1e7SJavier Martinez Canillas #define GET_HSTMODE(reg)		(((reg) & 0x70) >> 4)
394065d1e7SJavier Martinez Canillas #define GET_BOOTLOADERMODE(reg)		(((reg) & 0x10) >> 4)
404065d1e7SJavier Martinez Canillas 
414065d1e7SJavier Martinez Canillas #define CY_REG_BASE			0x00
424065d1e7SJavier Martinez Canillas #define CY_REG_ACT_DIST			0x1E
434065d1e7SJavier Martinez Canillas #define CY_REG_ACT_INTRVL		0x1D
444065d1e7SJavier Martinez Canillas #define CY_REG_TCH_TMOUT		(CY_REG_ACT_INTRVL + 1)
454065d1e7SJavier Martinez Canillas #define CY_REG_LP_INTRVL		(CY_REG_TCH_TMOUT + 1)
464065d1e7SJavier Martinez Canillas #define CY_MAXZ				255
474065d1e7SJavier Martinez Canillas #define CY_DELAY_DFLT			20 /* ms */
484065d1e7SJavier Martinez Canillas #define CY_DELAY_MAX			500
49d27ac0fbSDmitry Torokhov /* Active distance in pixels for a gesture to be reported */
50d27ac0fbSDmitry Torokhov #define CY_ACT_DIST_DFLT		0xF8 /* pixels */
51707b61bbSOreste Salerno #define CY_ACT_DIST_MASK		0x0F
52d27ac0fbSDmitry Torokhov /* Active Power state scanning/processing refresh interval */
53d27ac0fbSDmitry Torokhov #define CY_ACT_INTRVL_DFLT		0x00 /* ms */
54d27ac0fbSDmitry Torokhov /* Low Power state scanning/processing refresh interval */
55d27ac0fbSDmitry Torokhov #define CY_LP_INTRVL_DFLT		0x0A /* ms */
56d27ac0fbSDmitry Torokhov /* touch timeout for the Active power */
57d27ac0fbSDmitry Torokhov #define CY_TCH_TMOUT_DFLT		0xFF /* ms */
584065d1e7SJavier Martinez Canillas #define CY_HNDSHK_BIT			0x80
594065d1e7SJavier Martinez Canillas /* device mode bits */
604065d1e7SJavier Martinez Canillas #define CY_OPERATE_MODE			0x00
614065d1e7SJavier Martinez Canillas #define CY_SYSINFO_MODE			0x10
624065d1e7SJavier Martinez Canillas /* power mode select bits */
634065d1e7SJavier Martinez Canillas #define CY_SOFT_RESET_MODE		0x01 /* return to Bootloader mode */
644065d1e7SJavier Martinez Canillas #define CY_DEEP_SLEEP_MODE		0x02
654065d1e7SJavier Martinez Canillas #define CY_LOW_POWER_MODE		0x04
664065d1e7SJavier Martinez Canillas 
674065d1e7SJavier Martinez Canillas /* Slots management */
684065d1e7SJavier Martinez Canillas #define CY_MAX_FINGER			4
694065d1e7SJavier Martinez Canillas #define CY_MAX_ID			16
704065d1e7SJavier Martinez Canillas 
714065d1e7SJavier Martinez Canillas static const u8 bl_command[] = {
724065d1e7SJavier Martinez Canillas 	0x00,			/* file offset */
734065d1e7SJavier Martinez Canillas 	0xFF,			/* command */
744065d1e7SJavier Martinez Canillas 	0xA5,			/* exit bootloader command */
754065d1e7SJavier Martinez Canillas 	0, 1, 2, 3, 4, 5, 6, 7	/* default keys */
764065d1e7SJavier Martinez Canillas };
774065d1e7SJavier Martinez Canillas 
ttsp_read_block_data(struct cyttsp * ts,u8 command,u8 length,void * buf)784065d1e7SJavier Martinez Canillas static int ttsp_read_block_data(struct cyttsp *ts, u8 command,
794065d1e7SJavier Martinez Canillas 				u8 length, void *buf)
804065d1e7SJavier Martinez Canillas {
814065d1e7SJavier Martinez Canillas 	int error;
824065d1e7SJavier Martinez Canillas 	int tries;
834065d1e7SJavier Martinez Canillas 
844065d1e7SJavier Martinez Canillas 	for (tries = 0; tries < CY_NUM_RETRY; tries++) {
859664877eSFerruh Yigit 		error = ts->bus_ops->read(ts->dev, ts->xfer_buf, command,
869664877eSFerruh Yigit 				length, buf);
874065d1e7SJavier Martinez Canillas 		if (!error)
884065d1e7SJavier Martinez Canillas 			return 0;
894065d1e7SJavier Martinez Canillas 
904065d1e7SJavier Martinez Canillas 		msleep(CY_DELAY_DFLT);
914065d1e7SJavier Martinez Canillas 	}
924065d1e7SJavier Martinez Canillas 
934065d1e7SJavier Martinez Canillas 	return -EIO;
944065d1e7SJavier Martinez Canillas }
954065d1e7SJavier Martinez Canillas 
ttsp_write_block_data(struct cyttsp * ts,u8 command,u8 length,void * buf)964065d1e7SJavier Martinez Canillas static int ttsp_write_block_data(struct cyttsp *ts, u8 command,
974065d1e7SJavier Martinez Canillas 				 u8 length, void *buf)
984065d1e7SJavier Martinez Canillas {
994065d1e7SJavier Martinez Canillas 	int error;
1004065d1e7SJavier Martinez Canillas 	int tries;
1014065d1e7SJavier Martinez Canillas 
1024065d1e7SJavier Martinez Canillas 	for (tries = 0; tries < CY_NUM_RETRY; tries++) {
1039664877eSFerruh Yigit 		error = ts->bus_ops->write(ts->dev, ts->xfer_buf, command,
1049664877eSFerruh Yigit 				length, buf);
1054065d1e7SJavier Martinez Canillas 		if (!error)
1064065d1e7SJavier Martinez Canillas 			return 0;
1074065d1e7SJavier Martinez Canillas 
1084065d1e7SJavier Martinez Canillas 		msleep(CY_DELAY_DFLT);
1094065d1e7SJavier Martinez Canillas 	}
1104065d1e7SJavier Martinez Canillas 
1114065d1e7SJavier Martinez Canillas 	return -EIO;
1124065d1e7SJavier Martinez Canillas }
1134065d1e7SJavier Martinez Canillas 
ttsp_send_command(struct cyttsp * ts,u8 cmd)1144065d1e7SJavier Martinez Canillas static int ttsp_send_command(struct cyttsp *ts, u8 cmd)
1154065d1e7SJavier Martinez Canillas {
1164065d1e7SJavier Martinez Canillas 	return ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd);
1174065d1e7SJavier Martinez Canillas }
1184065d1e7SJavier Martinez Canillas 
cyttsp_handshake(struct cyttsp * ts)119fbd5e77eSFerruh Yigit static int cyttsp_handshake(struct cyttsp *ts)
120fbd5e77eSFerruh Yigit {
121707b61bbSOreste Salerno 	if (ts->use_hndshk)
122fbd5e77eSFerruh Yigit 		return ttsp_send_command(ts,
123fbd5e77eSFerruh Yigit 				ts->xy_data.hst_mode ^ CY_HNDSHK_BIT);
124fbd5e77eSFerruh Yigit 
125fbd5e77eSFerruh Yigit 	return 0;
126fbd5e77eSFerruh Yigit }
127fbd5e77eSFerruh Yigit 
cyttsp_load_bl_regs(struct cyttsp * ts)1284065d1e7SJavier Martinez Canillas static int cyttsp_load_bl_regs(struct cyttsp *ts)
1294065d1e7SJavier Martinez Canillas {
1304065d1e7SJavier Martinez Canillas 	memset(&ts->bl_data, 0, sizeof(ts->bl_data));
1314065d1e7SJavier Martinez Canillas 	ts->bl_data.bl_status = 0x10;
1324065d1e7SJavier Martinez Canillas 
1334065d1e7SJavier Martinez Canillas 	return ttsp_read_block_data(ts, CY_REG_BASE,
1344065d1e7SJavier Martinez Canillas 				    sizeof(ts->bl_data), &ts->bl_data);
1354065d1e7SJavier Martinez Canillas }
1364065d1e7SJavier Martinez Canillas 
cyttsp_exit_bl_mode(struct cyttsp * ts)1374065d1e7SJavier Martinez Canillas static int cyttsp_exit_bl_mode(struct cyttsp *ts)
1384065d1e7SJavier Martinez Canillas {
1394065d1e7SJavier Martinez Canillas 	int error;
1404065d1e7SJavier Martinez Canillas 	u8 bl_cmd[sizeof(bl_command)];
1414065d1e7SJavier Martinez Canillas 
1424065d1e7SJavier Martinez Canillas 	memcpy(bl_cmd, bl_command, sizeof(bl_command));
143707b61bbSOreste Salerno 	if (ts->bl_keys)
1444065d1e7SJavier Martinez Canillas 		memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS],
145707b61bbSOreste Salerno 			ts->bl_keys, CY_NUM_BL_KEYS);
1464065d1e7SJavier Martinez Canillas 
1474065d1e7SJavier Martinez Canillas 	error = ttsp_write_block_data(ts, CY_REG_BASE,
1484065d1e7SJavier Martinez Canillas 				      sizeof(bl_cmd), bl_cmd);
1494065d1e7SJavier Martinez Canillas 	if (error)
1504065d1e7SJavier Martinez Canillas 		return error;
1514065d1e7SJavier Martinez Canillas 
1524065d1e7SJavier Martinez Canillas 	/* wait for TTSP Device to complete the operation */
1534065d1e7SJavier Martinez Canillas 	msleep(CY_DELAY_DFLT);
1544065d1e7SJavier Martinez Canillas 
1554065d1e7SJavier Martinez Canillas 	error = cyttsp_load_bl_regs(ts);
1564065d1e7SJavier Martinez Canillas 	if (error)
1574065d1e7SJavier Martinez Canillas 		return error;
1584065d1e7SJavier Martinez Canillas 
1594065d1e7SJavier Martinez Canillas 	if (GET_BOOTLOADERMODE(ts->bl_data.bl_status))
1604065d1e7SJavier Martinez Canillas 		return -EIO;
1614065d1e7SJavier Martinez Canillas 
1624065d1e7SJavier Martinez Canillas 	return 0;
1634065d1e7SJavier Martinez Canillas }
1644065d1e7SJavier Martinez Canillas 
cyttsp_set_operational_mode(struct cyttsp * ts)1654065d1e7SJavier Martinez Canillas static int cyttsp_set_operational_mode(struct cyttsp *ts)
1664065d1e7SJavier Martinez Canillas {
1674065d1e7SJavier Martinez Canillas 	int error;
1684065d1e7SJavier Martinez Canillas 
1694065d1e7SJavier Martinez Canillas 	error = ttsp_send_command(ts, CY_OPERATE_MODE);
1704065d1e7SJavier Martinez Canillas 	if (error)
1714065d1e7SJavier Martinez Canillas 		return error;
1724065d1e7SJavier Martinez Canillas 
1734065d1e7SJavier Martinez Canillas 	/* wait for TTSP Device to complete switch to Operational mode */
1744065d1e7SJavier Martinez Canillas 	error = ttsp_read_block_data(ts, CY_REG_BASE,
1754065d1e7SJavier Martinez Canillas 				     sizeof(ts->xy_data), &ts->xy_data);
1764065d1e7SJavier Martinez Canillas 	if (error)
1774065d1e7SJavier Martinez Canillas 		return error;
1784065d1e7SJavier Martinez Canillas 
179fbd5e77eSFerruh Yigit 	error = cyttsp_handshake(ts);
180fbd5e77eSFerruh Yigit 	if (error)
181fbd5e77eSFerruh Yigit 		return error;
182fbd5e77eSFerruh Yigit 
1834065d1e7SJavier Martinez Canillas 	return ts->xy_data.act_dist == CY_ACT_DIST_DFLT ? -EIO : 0;
1844065d1e7SJavier Martinez Canillas }
1854065d1e7SJavier Martinez Canillas 
cyttsp_set_sysinfo_mode(struct cyttsp * ts)1864065d1e7SJavier Martinez Canillas static int cyttsp_set_sysinfo_mode(struct cyttsp *ts)
1874065d1e7SJavier Martinez Canillas {
1884065d1e7SJavier Martinez Canillas 	int error;
1894065d1e7SJavier Martinez Canillas 
1904065d1e7SJavier Martinez Canillas 	memset(&ts->sysinfo_data, 0, sizeof(ts->sysinfo_data));
1914065d1e7SJavier Martinez Canillas 
1924065d1e7SJavier Martinez Canillas 	/* switch to sysinfo mode */
1934065d1e7SJavier Martinez Canillas 	error = ttsp_send_command(ts, CY_SYSINFO_MODE);
1944065d1e7SJavier Martinez Canillas 	if (error)
1954065d1e7SJavier Martinez Canillas 		return error;
1964065d1e7SJavier Martinez Canillas 
1974065d1e7SJavier Martinez Canillas 	/* read sysinfo registers */
1984065d1e7SJavier Martinez Canillas 	msleep(CY_DELAY_DFLT);
1994065d1e7SJavier Martinez Canillas 	error = ttsp_read_block_data(ts, CY_REG_BASE, sizeof(ts->sysinfo_data),
2004065d1e7SJavier Martinez Canillas 				      &ts->sysinfo_data);
2014065d1e7SJavier Martinez Canillas 	if (error)
2024065d1e7SJavier Martinez Canillas 		return error;
2034065d1e7SJavier Martinez Canillas 
204fbd5e77eSFerruh Yigit 	error = cyttsp_handshake(ts);
205fbd5e77eSFerruh Yigit 	if (error)
206fbd5e77eSFerruh Yigit 		return error;
207fbd5e77eSFerruh Yigit 
2084065d1e7SJavier Martinez Canillas 	if (!ts->sysinfo_data.tts_verh && !ts->sysinfo_data.tts_verl)
2094065d1e7SJavier Martinez Canillas 		return -EIO;
2104065d1e7SJavier Martinez Canillas 
2114065d1e7SJavier Martinez Canillas 	return 0;
2124065d1e7SJavier Martinez Canillas }
2134065d1e7SJavier Martinez Canillas 
cyttsp_set_sysinfo_regs(struct cyttsp * ts)2144065d1e7SJavier Martinez Canillas static int cyttsp_set_sysinfo_regs(struct cyttsp *ts)
2154065d1e7SJavier Martinez Canillas {
2164065d1e7SJavier Martinez Canillas 	int retval = 0;
2174065d1e7SJavier Martinez Canillas 
218707b61bbSOreste Salerno 	if (ts->act_intrvl != CY_ACT_INTRVL_DFLT ||
219707b61bbSOreste Salerno 	    ts->tch_tmout != CY_TCH_TMOUT_DFLT ||
220707b61bbSOreste Salerno 	    ts->lp_intrvl != CY_LP_INTRVL_DFLT) {
2214065d1e7SJavier Martinez Canillas 
2224065d1e7SJavier Martinez Canillas 		u8 intrvl_ray[] = {
223707b61bbSOreste Salerno 			ts->act_intrvl,
224707b61bbSOreste Salerno 			ts->tch_tmout,
225707b61bbSOreste Salerno 			ts->lp_intrvl
2264065d1e7SJavier Martinez Canillas 		};
2274065d1e7SJavier Martinez Canillas 
2284065d1e7SJavier Martinez Canillas 		/* set intrvl registers */
2294065d1e7SJavier Martinez Canillas 		retval = ttsp_write_block_data(ts, CY_REG_ACT_INTRVL,
2304065d1e7SJavier Martinez Canillas 					sizeof(intrvl_ray), intrvl_ray);
2314065d1e7SJavier Martinez Canillas 		msleep(CY_DELAY_DFLT);
2324065d1e7SJavier Martinez Canillas 	}
2334065d1e7SJavier Martinez Canillas 
2344065d1e7SJavier Martinez Canillas 	return retval;
2354065d1e7SJavier Martinez Canillas }
2364065d1e7SJavier Martinez Canillas 
cyttsp_hard_reset(struct cyttsp * ts)2378fb81d20SOreste Salerno static void cyttsp_hard_reset(struct cyttsp *ts)
2388fb81d20SOreste Salerno {
2398fb81d20SOreste Salerno 	if (ts->reset_gpio) {
240c61ac36fSLinus Walleij 		/*
241c61ac36fSLinus Walleij 		 * According to the CY8CTMA340 datasheet page 21, the external
242c61ac36fSLinus Walleij 		 * reset pulse width should be >= 1 ms. The datasheet does not
243c61ac36fSLinus Walleij 		 * specify how long we have to wait after reset but a vendor
244c61ac36fSLinus Walleij 		 * tree specifies 5 ms here.
245c61ac36fSLinus Walleij 		 */
2468fb81d20SOreste Salerno 		gpiod_set_value_cansleep(ts->reset_gpio, 1);
247c61ac36fSLinus Walleij 		usleep_range(1000, 2000);
2488fb81d20SOreste Salerno 		gpiod_set_value_cansleep(ts->reset_gpio, 0);
249c61ac36fSLinus Walleij 		usleep_range(5000, 6000);
2508fb81d20SOreste Salerno 	}
2518fb81d20SOreste Salerno }
2528fb81d20SOreste Salerno 
cyttsp_soft_reset(struct cyttsp * ts)2534065d1e7SJavier Martinez Canillas static int cyttsp_soft_reset(struct cyttsp *ts)
2544065d1e7SJavier Martinez Canillas {
2554065d1e7SJavier Martinez Canillas 	int retval;
2564065d1e7SJavier Martinez Canillas 
2574065d1e7SJavier Martinez Canillas 	/* wait for interrupt to set ready completion */
25816735d02SWolfram Sang 	reinit_completion(&ts->bl_ready);
2594065d1e7SJavier Martinez Canillas 	ts->state = CY_BL_STATE;
2604065d1e7SJavier Martinez Canillas 
2614065d1e7SJavier Martinez Canillas 	enable_irq(ts->irq);
2624065d1e7SJavier Martinez Canillas 
2634065d1e7SJavier Martinez Canillas 	retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE);
26484c36ab7SLinus Walleij 	if (retval) {
26584c36ab7SLinus Walleij 		dev_err(ts->dev, "failed to send soft reset\n");
2664065d1e7SJavier Martinez Canillas 		goto out;
26784c36ab7SLinus Walleij 	}
2684065d1e7SJavier Martinez Canillas 
26984c36ab7SLinus Walleij 	if (!wait_for_completion_timeout(&ts->bl_ready,
27084c36ab7SLinus Walleij 			msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX))) {
27184c36ab7SLinus Walleij 		dev_err(ts->dev, "timeout waiting for soft reset\n");
27284c36ab7SLinus Walleij 		retval = -EIO;
27384c36ab7SLinus Walleij 	}
2744065d1e7SJavier Martinez Canillas 
2754065d1e7SJavier Martinez Canillas out:
2764065d1e7SJavier Martinez Canillas 	ts->state = CY_IDLE_STATE;
2774065d1e7SJavier Martinez Canillas 	disable_irq(ts->irq);
2784065d1e7SJavier Martinez Canillas 	return retval;
2794065d1e7SJavier Martinez Canillas }
2804065d1e7SJavier Martinez Canillas 
cyttsp_act_dist_setup(struct cyttsp * ts)2814065d1e7SJavier Martinez Canillas static int cyttsp_act_dist_setup(struct cyttsp *ts)
2824065d1e7SJavier Martinez Canillas {
283707b61bbSOreste Salerno 	u8 act_dist_setup = ts->act_dist;
2844065d1e7SJavier Martinez Canillas 
2854065d1e7SJavier Martinez Canillas 	/* Init gesture; active distance setup */
2864065d1e7SJavier Martinez Canillas 	return ttsp_write_block_data(ts, CY_REG_ACT_DIST,
2874065d1e7SJavier Martinez Canillas 				sizeof(act_dist_setup), &act_dist_setup);
2884065d1e7SJavier Martinez Canillas }
2894065d1e7SJavier Martinez Canillas 
cyttsp_extract_track_ids(struct cyttsp_xydata * xy_data,int * ids)2904065d1e7SJavier Martinez Canillas static void cyttsp_extract_track_ids(struct cyttsp_xydata *xy_data, int *ids)
2914065d1e7SJavier Martinez Canillas {
2924065d1e7SJavier Martinez Canillas 	ids[0] = xy_data->touch12_id >> 4;
2934065d1e7SJavier Martinez Canillas 	ids[1] = xy_data->touch12_id & 0xF;
2944065d1e7SJavier Martinez Canillas 	ids[2] = xy_data->touch34_id >> 4;
2954065d1e7SJavier Martinez Canillas 	ids[3] = xy_data->touch34_id & 0xF;
2964065d1e7SJavier Martinez Canillas }
2974065d1e7SJavier Martinez Canillas 
cyttsp_get_tch(struct cyttsp_xydata * xy_data,int idx)2984065d1e7SJavier Martinez Canillas static const struct cyttsp_tch *cyttsp_get_tch(struct cyttsp_xydata *xy_data,
2994065d1e7SJavier Martinez Canillas 					       int idx)
3004065d1e7SJavier Martinez Canillas {
3014065d1e7SJavier Martinez Canillas 	switch (idx) {
3024065d1e7SJavier Martinez Canillas 	case 0:
3034065d1e7SJavier Martinez Canillas 		return &xy_data->tch1;
3044065d1e7SJavier Martinez Canillas 	case 1:
3054065d1e7SJavier Martinez Canillas 		return &xy_data->tch2;
3064065d1e7SJavier Martinez Canillas 	case 2:
3074065d1e7SJavier Martinez Canillas 		return &xy_data->tch3;
3084065d1e7SJavier Martinez Canillas 	case 3:
3094065d1e7SJavier Martinez Canillas 		return &xy_data->tch4;
3104065d1e7SJavier Martinez Canillas 	default:
3114065d1e7SJavier Martinez Canillas 		return NULL;
3124065d1e7SJavier Martinez Canillas 	}
3134065d1e7SJavier Martinez Canillas }
3144065d1e7SJavier Martinez Canillas 
cyttsp_report_tchdata(struct cyttsp * ts)3154065d1e7SJavier Martinez Canillas static void cyttsp_report_tchdata(struct cyttsp *ts)
3164065d1e7SJavier Martinez Canillas {
3174065d1e7SJavier Martinez Canillas 	struct cyttsp_xydata *xy_data = &ts->xy_data;
3184065d1e7SJavier Martinez Canillas 	struct input_dev *input = ts->input;
3194065d1e7SJavier Martinez Canillas 	int num_tch = GET_NUM_TOUCHES(xy_data->tt_stat);
3204065d1e7SJavier Martinez Canillas 	const struct cyttsp_tch *tch;
3214065d1e7SJavier Martinez Canillas 	int ids[CY_MAX_ID];
3224065d1e7SJavier Martinez Canillas 	int i;
3234065d1e7SJavier Martinez Canillas 	DECLARE_BITMAP(used, CY_MAX_ID);
3244065d1e7SJavier Martinez Canillas 
3254065d1e7SJavier Martinez Canillas 	if (IS_LARGE_AREA(xy_data->tt_stat) == 1) {
3264065d1e7SJavier Martinez Canillas 		/* terminate all active tracks */
3274065d1e7SJavier Martinez Canillas 		num_tch = 0;
3284065d1e7SJavier Martinez Canillas 		dev_dbg(ts->dev, "%s: Large area detected\n", __func__);
3294065d1e7SJavier Martinez Canillas 	} else if (num_tch > CY_MAX_FINGER) {
3304065d1e7SJavier Martinez Canillas 		/* terminate all active tracks */
3314065d1e7SJavier Martinez Canillas 		num_tch = 0;
3324065d1e7SJavier Martinez Canillas 		dev_dbg(ts->dev, "%s: Num touch error detected\n", __func__);
3334065d1e7SJavier Martinez Canillas 	} else if (IS_BAD_PKT(xy_data->tt_mode)) {
3344065d1e7SJavier Martinez Canillas 		/* terminate all active tracks */
3354065d1e7SJavier Martinez Canillas 		num_tch = 0;
3364065d1e7SJavier Martinez Canillas 		dev_dbg(ts->dev, "%s: Invalid buffer detected\n", __func__);
3374065d1e7SJavier Martinez Canillas 	}
3384065d1e7SJavier Martinez Canillas 
3394065d1e7SJavier Martinez Canillas 	cyttsp_extract_track_ids(xy_data, ids);
3404065d1e7SJavier Martinez Canillas 
3414065d1e7SJavier Martinez Canillas 	bitmap_zero(used, CY_MAX_ID);
3424065d1e7SJavier Martinez Canillas 
3434065d1e7SJavier Martinez Canillas 	for (i = 0; i < num_tch; i++) {
3444065d1e7SJavier Martinez Canillas 		tch = cyttsp_get_tch(xy_data, i);
3454065d1e7SJavier Martinez Canillas 
3464065d1e7SJavier Martinez Canillas 		input_mt_slot(input, ids[i]);
3474065d1e7SJavier Martinez Canillas 		input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
3484065d1e7SJavier Martinez Canillas 		input_report_abs(input, ABS_MT_POSITION_X, be16_to_cpu(tch->x));
3494065d1e7SJavier Martinez Canillas 		input_report_abs(input, ABS_MT_POSITION_Y, be16_to_cpu(tch->y));
3504065d1e7SJavier Martinez Canillas 		input_report_abs(input, ABS_MT_TOUCH_MAJOR, tch->z);
3514065d1e7SJavier Martinez Canillas 
3524065d1e7SJavier Martinez Canillas 		__set_bit(ids[i], used);
3534065d1e7SJavier Martinez Canillas 	}
3544065d1e7SJavier Martinez Canillas 
3554065d1e7SJavier Martinez Canillas 	for (i = 0; i < CY_MAX_ID; i++) {
3564065d1e7SJavier Martinez Canillas 		if (test_bit(i, used))
3574065d1e7SJavier Martinez Canillas 			continue;
3584065d1e7SJavier Martinez Canillas 
3594065d1e7SJavier Martinez Canillas 		input_mt_slot(input, i);
3605fc70e35SJiada Wang 		input_mt_report_slot_inactive(input);
3614065d1e7SJavier Martinez Canillas 	}
3624065d1e7SJavier Martinez Canillas 
3634065d1e7SJavier Martinez Canillas 	input_sync(input);
3644065d1e7SJavier Martinez Canillas }
3654065d1e7SJavier Martinez Canillas 
cyttsp_irq(int irq,void * handle)3664065d1e7SJavier Martinez Canillas static irqreturn_t cyttsp_irq(int irq, void *handle)
3674065d1e7SJavier Martinez Canillas {
3684065d1e7SJavier Martinez Canillas 	struct cyttsp *ts = handle;
3694065d1e7SJavier Martinez Canillas 	int error;
3704065d1e7SJavier Martinez Canillas 
3714065d1e7SJavier Martinez Canillas 	if (unlikely(ts->state == CY_BL_STATE)) {
3724065d1e7SJavier Martinez Canillas 		complete(&ts->bl_ready);
3734065d1e7SJavier Martinez Canillas 		goto out;
3744065d1e7SJavier Martinez Canillas 	}
3754065d1e7SJavier Martinez Canillas 
3764065d1e7SJavier Martinez Canillas 	/* Get touch data from CYTTSP device */
3774065d1e7SJavier Martinez Canillas 	error = ttsp_read_block_data(ts, CY_REG_BASE,
3784065d1e7SJavier Martinez Canillas 				 sizeof(struct cyttsp_xydata), &ts->xy_data);
3794065d1e7SJavier Martinez Canillas 	if (error)
3804065d1e7SJavier Martinez Canillas 		goto out;
3814065d1e7SJavier Martinez Canillas 
3824065d1e7SJavier Martinez Canillas 	/* provide flow control handshake */
383fbd5e77eSFerruh Yigit 	error = cyttsp_handshake(ts);
3844065d1e7SJavier Martinez Canillas 	if (error)
3854065d1e7SJavier Martinez Canillas 		goto out;
3864065d1e7SJavier Martinez Canillas 
3874065d1e7SJavier Martinez Canillas 	if (unlikely(ts->state == CY_IDLE_STATE))
3884065d1e7SJavier Martinez Canillas 		goto out;
3894065d1e7SJavier Martinez Canillas 
3904065d1e7SJavier Martinez Canillas 	if (GET_BOOTLOADERMODE(ts->xy_data.tt_mode)) {
3914065d1e7SJavier Martinez Canillas 		/*
3924065d1e7SJavier Martinez Canillas 		 * TTSP device has reset back to bootloader mode.
3934065d1e7SJavier Martinez Canillas 		 * Restore to operational mode.
3944065d1e7SJavier Martinez Canillas 		 */
3954065d1e7SJavier Martinez Canillas 		error = cyttsp_exit_bl_mode(ts);
3964065d1e7SJavier Martinez Canillas 		if (error) {
3974065d1e7SJavier Martinez Canillas 			dev_err(ts->dev,
3984065d1e7SJavier Martinez Canillas 				"Could not return to operational mode, err: %d\n",
3994065d1e7SJavier Martinez Canillas 				error);
4004065d1e7SJavier Martinez Canillas 			ts->state = CY_IDLE_STATE;
4014065d1e7SJavier Martinez Canillas 		}
4024065d1e7SJavier Martinez Canillas 	} else {
4034065d1e7SJavier Martinez Canillas 		cyttsp_report_tchdata(ts);
4044065d1e7SJavier Martinez Canillas 	}
4054065d1e7SJavier Martinez Canillas 
4064065d1e7SJavier Martinez Canillas out:
4074065d1e7SJavier Martinez Canillas 	return IRQ_HANDLED;
4084065d1e7SJavier Martinez Canillas }
4094065d1e7SJavier Martinez Canillas 
cyttsp_power_on(struct cyttsp * ts)4104065d1e7SJavier Martinez Canillas static int cyttsp_power_on(struct cyttsp *ts)
4114065d1e7SJavier Martinez Canillas {
4124065d1e7SJavier Martinez Canillas 	int error;
4134065d1e7SJavier Martinez Canillas 
4144065d1e7SJavier Martinez Canillas 	error = cyttsp_soft_reset(ts);
4154065d1e7SJavier Martinez Canillas 	if (error)
4164065d1e7SJavier Martinez Canillas 		return error;
4174065d1e7SJavier Martinez Canillas 
4184065d1e7SJavier Martinez Canillas 	error = cyttsp_load_bl_regs(ts);
4194065d1e7SJavier Martinez Canillas 	if (error)
4204065d1e7SJavier Martinez Canillas 		return error;
4214065d1e7SJavier Martinez Canillas 
4224065d1e7SJavier Martinez Canillas 	if (GET_BOOTLOADERMODE(ts->bl_data.bl_status) &&
4234065d1e7SJavier Martinez Canillas 	    IS_VALID_APP(ts->bl_data.bl_status)) {
4244065d1e7SJavier Martinez Canillas 		error = cyttsp_exit_bl_mode(ts);
42539841136SLinus Walleij 		if (error) {
42639841136SLinus Walleij 			dev_err(ts->dev, "failed to exit bootloader mode\n");
4274065d1e7SJavier Martinez Canillas 			return error;
4284065d1e7SJavier Martinez Canillas 		}
42939841136SLinus Walleij 	}
4304065d1e7SJavier Martinez Canillas 
4314065d1e7SJavier Martinez Canillas 	if (GET_HSTMODE(ts->bl_data.bl_file) != CY_OPERATE_MODE ||
4324065d1e7SJavier Martinez Canillas 	    IS_OPERATIONAL_ERR(ts->bl_data.bl_status)) {
4334065d1e7SJavier Martinez Canillas 		return -ENODEV;
4344065d1e7SJavier Martinez Canillas 	}
4354065d1e7SJavier Martinez Canillas 
4364065d1e7SJavier Martinez Canillas 	error = cyttsp_set_sysinfo_mode(ts);
4374065d1e7SJavier Martinez Canillas 	if (error)
4384065d1e7SJavier Martinez Canillas 		return error;
4394065d1e7SJavier Martinez Canillas 
4404065d1e7SJavier Martinez Canillas 	error = cyttsp_set_sysinfo_regs(ts);
4414065d1e7SJavier Martinez Canillas 	if (error)
4424065d1e7SJavier Martinez Canillas 		return error;
4434065d1e7SJavier Martinez Canillas 
4444065d1e7SJavier Martinez Canillas 	error = cyttsp_set_operational_mode(ts);
4454065d1e7SJavier Martinez Canillas 	if (error)
4464065d1e7SJavier Martinez Canillas 		return error;
4474065d1e7SJavier Martinez Canillas 
4484065d1e7SJavier Martinez Canillas 	/* init active distance */
4494065d1e7SJavier Martinez Canillas 	error = cyttsp_act_dist_setup(ts);
4504065d1e7SJavier Martinez Canillas 	if (error)
4514065d1e7SJavier Martinez Canillas 		return error;
4524065d1e7SJavier Martinez Canillas 
4534065d1e7SJavier Martinez Canillas 	ts->state = CY_ACTIVE_STATE;
4544065d1e7SJavier Martinez Canillas 
4554065d1e7SJavier Martinez Canillas 	return 0;
4564065d1e7SJavier Martinez Canillas }
4574065d1e7SJavier Martinez Canillas 
cyttsp_enable(struct cyttsp * ts)4584065d1e7SJavier Martinez Canillas static int cyttsp_enable(struct cyttsp *ts)
4594065d1e7SJavier Martinez Canillas {
4604065d1e7SJavier Martinez Canillas 	int error;
4614065d1e7SJavier Martinez Canillas 
4624065d1e7SJavier Martinez Canillas 	/*
4634065d1e7SJavier Martinez Canillas 	 * The device firmware can wake on an I2C or SPI memory slave
4644065d1e7SJavier Martinez Canillas 	 * address match. So just reading a register is sufficient to
4654065d1e7SJavier Martinez Canillas 	 * wake up the device. The first read attempt will fail but it
4664065d1e7SJavier Martinez Canillas 	 * will wake it up making the second read attempt successful.
4674065d1e7SJavier Martinez Canillas 	 */
4684065d1e7SJavier Martinez Canillas 	error = ttsp_read_block_data(ts, CY_REG_BASE,
4694065d1e7SJavier Martinez Canillas 				     sizeof(ts->xy_data), &ts->xy_data);
4704065d1e7SJavier Martinez Canillas 	if (error)
4714065d1e7SJavier Martinez Canillas 		return error;
4724065d1e7SJavier Martinez Canillas 
4734065d1e7SJavier Martinez Canillas 	if (GET_HSTMODE(ts->xy_data.hst_mode))
4744065d1e7SJavier Martinez Canillas 		return -EIO;
4754065d1e7SJavier Martinez Canillas 
4764065d1e7SJavier Martinez Canillas 	enable_irq(ts->irq);
4774065d1e7SJavier Martinez Canillas 
4784065d1e7SJavier Martinez Canillas 	return 0;
4794065d1e7SJavier Martinez Canillas }
4804065d1e7SJavier Martinez Canillas 
cyttsp_disable(struct cyttsp * ts)4814065d1e7SJavier Martinez Canillas static int cyttsp_disable(struct cyttsp *ts)
4824065d1e7SJavier Martinez Canillas {
4834065d1e7SJavier Martinez Canillas 	int error;
4844065d1e7SJavier Martinez Canillas 
4854065d1e7SJavier Martinez Canillas 	error = ttsp_send_command(ts, CY_LOW_POWER_MODE);
4864065d1e7SJavier Martinez Canillas 	if (error)
4874065d1e7SJavier Martinez Canillas 		return error;
4884065d1e7SJavier Martinez Canillas 
4894065d1e7SJavier Martinez Canillas 	disable_irq(ts->irq);
4904065d1e7SJavier Martinez Canillas 
4914065d1e7SJavier Martinez Canillas 	return 0;
4924065d1e7SJavier Martinez Canillas }
4934065d1e7SJavier Martinez Canillas 
cyttsp_suspend(struct device * dev)494*c3c2f2bcSJonathan Cameron static int cyttsp_suspend(struct device *dev)
4954065d1e7SJavier Martinez Canillas {
4964065d1e7SJavier Martinez Canillas 	struct cyttsp *ts = dev_get_drvdata(dev);
4974065d1e7SJavier Martinez Canillas 	int retval = 0;
4984065d1e7SJavier Martinez Canillas 
4994065d1e7SJavier Martinez Canillas 	mutex_lock(&ts->input->mutex);
5004065d1e7SJavier Martinez Canillas 
501d69f0a43SAndrzej Pietrasiewicz 	if (input_device_enabled(ts->input)) {
5024065d1e7SJavier Martinez Canillas 		retval = cyttsp_disable(ts);
5034065d1e7SJavier Martinez Canillas 		if (retval == 0)
5044065d1e7SJavier Martinez Canillas 			ts->suspended = true;
5054065d1e7SJavier Martinez Canillas 	}
5064065d1e7SJavier Martinez Canillas 
5074065d1e7SJavier Martinez Canillas 	mutex_unlock(&ts->input->mutex);
5084065d1e7SJavier Martinez Canillas 
5094065d1e7SJavier Martinez Canillas 	return retval;
5104065d1e7SJavier Martinez Canillas }
5114065d1e7SJavier Martinez Canillas 
cyttsp_resume(struct device * dev)512*c3c2f2bcSJonathan Cameron static int cyttsp_resume(struct device *dev)
5134065d1e7SJavier Martinez Canillas {
5144065d1e7SJavier Martinez Canillas 	struct cyttsp *ts = dev_get_drvdata(dev);
5154065d1e7SJavier Martinez Canillas 
5164065d1e7SJavier Martinez Canillas 	mutex_lock(&ts->input->mutex);
5174065d1e7SJavier Martinez Canillas 
518d69f0a43SAndrzej Pietrasiewicz 	if (input_device_enabled(ts->input))
5194065d1e7SJavier Martinez Canillas 		cyttsp_enable(ts);
5204065d1e7SJavier Martinez Canillas 
5214065d1e7SJavier Martinez Canillas 	ts->suspended = false;
5224065d1e7SJavier Martinez Canillas 
5234065d1e7SJavier Martinez Canillas 	mutex_unlock(&ts->input->mutex);
5244065d1e7SJavier Martinez Canillas 
5254065d1e7SJavier Martinez Canillas 	return 0;
5264065d1e7SJavier Martinez Canillas }
5274065d1e7SJavier Martinez Canillas 
528*c3c2f2bcSJonathan Cameron EXPORT_GPL_SIMPLE_DEV_PM_OPS(cyttsp_pm_ops, cyttsp_suspend, cyttsp_resume);
5294065d1e7SJavier Martinez Canillas 
cyttsp_open(struct input_dev * dev)5304065d1e7SJavier Martinez Canillas static int cyttsp_open(struct input_dev *dev)
5314065d1e7SJavier Martinez Canillas {
5324065d1e7SJavier Martinez Canillas 	struct cyttsp *ts = input_get_drvdata(dev);
5334065d1e7SJavier Martinez Canillas 	int retval = 0;
5344065d1e7SJavier Martinez Canillas 
5354065d1e7SJavier Martinez Canillas 	if (!ts->suspended)
5364065d1e7SJavier Martinez Canillas 		retval = cyttsp_enable(ts);
5374065d1e7SJavier Martinez Canillas 
5384065d1e7SJavier Martinez Canillas 	return retval;
5394065d1e7SJavier Martinez Canillas }
5404065d1e7SJavier Martinez Canillas 
cyttsp_close(struct input_dev * dev)5414065d1e7SJavier Martinez Canillas static void cyttsp_close(struct input_dev *dev)
5424065d1e7SJavier Martinez Canillas {
5434065d1e7SJavier Martinez Canillas 	struct cyttsp *ts = input_get_drvdata(dev);
5444065d1e7SJavier Martinez Canillas 
5454065d1e7SJavier Martinez Canillas 	if (!ts->suspended)
5464065d1e7SJavier Martinez Canillas 		cyttsp_disable(ts);
5474065d1e7SJavier Martinez Canillas }
5484065d1e7SJavier Martinez Canillas 
cyttsp_parse_properties(struct cyttsp * ts)549707b61bbSOreste Salerno static int cyttsp_parse_properties(struct cyttsp *ts)
550e4cf92baSOreste Salerno {
551707b61bbSOreste Salerno 	struct device *dev = ts->dev;
552707b61bbSOreste Salerno 	u32 dt_value;
553707b61bbSOreste Salerno 	int ret;
554e4cf92baSOreste Salerno 
555707b61bbSOreste Salerno 	ts->bl_keys = devm_kzalloc(dev, CY_NUM_BL_KEYS, GFP_KERNEL);
556707b61bbSOreste Salerno 	if (!ts->bl_keys)
557707b61bbSOreste Salerno 		return -ENOMEM;
558707b61bbSOreste Salerno 
559707b61bbSOreste Salerno 	/* Set some default values */
560707b61bbSOreste Salerno 	ts->use_hndshk = false;
561707b61bbSOreste Salerno 	ts->act_dist = CY_ACT_DIST_DFLT;
562707b61bbSOreste Salerno 	ts->act_intrvl = CY_ACT_INTRVL_DFLT;
563707b61bbSOreste Salerno 	ts->tch_tmout = CY_TCH_TMOUT_DFLT;
564707b61bbSOreste Salerno 	ts->lp_intrvl = CY_LP_INTRVL_DFLT;
565707b61bbSOreste Salerno 
566707b61bbSOreste Salerno 	ret = device_property_read_u8_array(dev, "bootloader-key",
567707b61bbSOreste Salerno 					    ts->bl_keys, CY_NUM_BL_KEYS);
568707b61bbSOreste Salerno 	if (ret) {
569707b61bbSOreste Salerno 		dev_err(dev,
570707b61bbSOreste Salerno 			"bootloader-key property could not be retrieved\n");
571707b61bbSOreste Salerno 		return ret;
572707b61bbSOreste Salerno 	}
573707b61bbSOreste Salerno 
574707b61bbSOreste Salerno 	ts->use_hndshk = device_property_present(dev, "use-handshake");
575707b61bbSOreste Salerno 
576707b61bbSOreste Salerno 	if (!device_property_read_u32(dev, "active-distance", &dt_value)) {
577707b61bbSOreste Salerno 		if (dt_value > 15) {
578707b61bbSOreste Salerno 			dev_err(dev, "active-distance (%u) must be [0-15]\n",
579707b61bbSOreste Salerno 				dt_value);
580707b61bbSOreste Salerno 			return -EINVAL;
581707b61bbSOreste Salerno 		}
582707b61bbSOreste Salerno 		ts->act_dist &= ~CY_ACT_DIST_MASK;
583707b61bbSOreste Salerno 		ts->act_dist |= dt_value;
584707b61bbSOreste Salerno 	}
585707b61bbSOreste Salerno 
586707b61bbSOreste Salerno 	if (!device_property_read_u32(dev, "active-interval-ms", &dt_value)) {
587707b61bbSOreste Salerno 		if (dt_value > 255) {
588707b61bbSOreste Salerno 			dev_err(dev, "active-interval-ms (%u) must be [0-255]\n",
589707b61bbSOreste Salerno 				dt_value);
590707b61bbSOreste Salerno 			return -EINVAL;
591707b61bbSOreste Salerno 		}
592707b61bbSOreste Salerno 		ts->act_intrvl = dt_value;
593707b61bbSOreste Salerno 	}
594707b61bbSOreste Salerno 
595707b61bbSOreste Salerno 	if (!device_property_read_u32(dev, "lowpower-interval-ms", &dt_value)) {
596707b61bbSOreste Salerno 		if (dt_value > 2550) {
597707b61bbSOreste Salerno 			dev_err(dev, "lowpower-interval-ms (%u) must be [0-2550]\n",
598707b61bbSOreste Salerno 				dt_value);
599707b61bbSOreste Salerno 			return -EINVAL;
600707b61bbSOreste Salerno 		}
601707b61bbSOreste Salerno 		/* Register value is expressed in 0.01s / bit */
602707b61bbSOreste Salerno 		ts->lp_intrvl = dt_value / 10;
603707b61bbSOreste Salerno 	}
604707b61bbSOreste Salerno 
605707b61bbSOreste Salerno 	if (!device_property_read_u32(dev, "touch-timeout-ms", &dt_value)) {
606707b61bbSOreste Salerno 		if (dt_value > 2550) {
607707b61bbSOreste Salerno 			dev_err(dev, "touch-timeout-ms (%u) must be [0-2550]\n",
608707b61bbSOreste Salerno 				dt_value);
609707b61bbSOreste Salerno 			return -EINVAL;
610707b61bbSOreste Salerno 		}
611707b61bbSOreste Salerno 		/* Register value is expressed in 0.01s / bit */
612707b61bbSOreste Salerno 		ts->tch_tmout = dt_value / 10;
613707b61bbSOreste Salerno 	}
614707b61bbSOreste Salerno 
615707b61bbSOreste Salerno 	return 0;
616e4cf92baSOreste Salerno }
617e4cf92baSOreste Salerno 
cyttsp_disable_regulators(void * _ts)6186cf3b3abSLinus Walleij static void cyttsp_disable_regulators(void *_ts)
6196cf3b3abSLinus Walleij {
6206cf3b3abSLinus Walleij 	struct cyttsp *ts = _ts;
6216cf3b3abSLinus Walleij 
6226cf3b3abSLinus Walleij 	regulator_bulk_disable(ARRAY_SIZE(ts->regulators),
6236cf3b3abSLinus Walleij 			       ts->regulators);
6246cf3b3abSLinus Walleij }
6256cf3b3abSLinus Walleij 
cyttsp_probe(const struct cyttsp_bus_ops * bus_ops,struct device * dev,int irq,size_t xfer_buf_size)6264065d1e7SJavier Martinez Canillas struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
6274065d1e7SJavier Martinez Canillas 			    struct device *dev, int irq, size_t xfer_buf_size)
6284065d1e7SJavier Martinez Canillas {
6294065d1e7SJavier Martinez Canillas 	struct cyttsp *ts;
6304065d1e7SJavier Martinez Canillas 	struct input_dev *input_dev;
6314065d1e7SJavier Martinez Canillas 	int error;
6324065d1e7SJavier Martinez Canillas 
633e4cf92baSOreste Salerno 	ts = devm_kzalloc(dev, sizeof(*ts) + xfer_buf_size, GFP_KERNEL);
634e4cf92baSOreste Salerno 	if (!ts)
635e4cf92baSOreste Salerno 		return ERR_PTR(-ENOMEM);
636e4cf92baSOreste Salerno 
637e4cf92baSOreste Salerno 	input_dev = devm_input_allocate_device(dev);
638e4cf92baSOreste Salerno 	if (!input_dev)
639e4cf92baSOreste Salerno 		return ERR_PTR(-ENOMEM);
6404065d1e7SJavier Martinez Canillas 
6414065d1e7SJavier Martinez Canillas 	ts->dev = dev;
6424065d1e7SJavier Martinez Canillas 	ts->input = input_dev;
6434065d1e7SJavier Martinez Canillas 	ts->bus_ops = bus_ops;
6444065d1e7SJavier Martinez Canillas 	ts->irq = irq;
6454065d1e7SJavier Martinez Canillas 
6466cf3b3abSLinus Walleij 	/*
6476cf3b3abSLinus Walleij 	 * VCPIN is the analog voltage supply
6486cf3b3abSLinus Walleij 	 * VDD is the digital voltage supply
6496cf3b3abSLinus Walleij 	 */
6506cf3b3abSLinus Walleij 	ts->regulators[0].supply = "vcpin";
6516cf3b3abSLinus Walleij 	ts->regulators[1].supply = "vdd";
6526cf3b3abSLinus Walleij 	error = devm_regulator_bulk_get(dev, ARRAY_SIZE(ts->regulators),
6536cf3b3abSLinus Walleij 					ts->regulators);
6546cf3b3abSLinus Walleij 	if (error) {
6556cf3b3abSLinus Walleij 		dev_err(dev, "Failed to get regulators: %d\n", error);
6566cf3b3abSLinus Walleij 		return ERR_PTR(error);
6576cf3b3abSLinus Walleij 	}
6586cf3b3abSLinus Walleij 
6596cf3b3abSLinus Walleij 	error = regulator_bulk_enable(ARRAY_SIZE(ts->regulators),
6606cf3b3abSLinus Walleij 				      ts->regulators);
6616cf3b3abSLinus Walleij 	if (error) {
6626cf3b3abSLinus Walleij 		dev_err(dev, "Cannot enable regulators: %d\n", error);
6636cf3b3abSLinus Walleij 		return ERR_PTR(error);
6646cf3b3abSLinus Walleij 	}
6656cf3b3abSLinus Walleij 
6666cf3b3abSLinus Walleij 	error = devm_add_action_or_reset(dev, cyttsp_disable_regulators, ts);
6676cf3b3abSLinus Walleij 	if (error) {
6686cf3b3abSLinus Walleij 		dev_err(dev, "failed to install chip disable handler\n");
6696cf3b3abSLinus Walleij 		return ERR_PTR(error);
6706cf3b3abSLinus Walleij 	}
6716cf3b3abSLinus Walleij 
672707b61bbSOreste Salerno 	ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
673707b61bbSOreste Salerno 	if (IS_ERR(ts->reset_gpio)) {
674707b61bbSOreste Salerno 		error = PTR_ERR(ts->reset_gpio);
675707b61bbSOreste Salerno 		dev_err(dev, "Failed to request reset gpio, error %d\n", error);
676707b61bbSOreste Salerno 		return ERR_PTR(error);
677707b61bbSOreste Salerno 	}
678707b61bbSOreste Salerno 
679707b61bbSOreste Salerno 	error = cyttsp_parse_properties(ts);
680707b61bbSOreste Salerno 	if (error)
681707b61bbSOreste Salerno 		return ERR_PTR(error);
682707b61bbSOreste Salerno 
6834065d1e7SJavier Martinez Canillas 	init_completion(&ts->bl_ready);
6844065d1e7SJavier Martinez Canillas 
685707b61bbSOreste Salerno 	input_dev->name = "Cypress TTSP TouchScreen";
6864065d1e7SJavier Martinez Canillas 	input_dev->id.bustype = bus_ops->bustype;
6874065d1e7SJavier Martinez Canillas 	input_dev->dev.parent = ts->dev;
6884065d1e7SJavier Martinez Canillas 
6894065d1e7SJavier Martinez Canillas 	input_dev->open = cyttsp_open;
6904065d1e7SJavier Martinez Canillas 	input_dev->close = cyttsp_close;
6914065d1e7SJavier Martinez Canillas 
6924065d1e7SJavier Martinez Canillas 	input_set_drvdata(input_dev, ts);
6934065d1e7SJavier Martinez Canillas 
694707b61bbSOreste Salerno 	input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X);
695707b61bbSOreste Salerno 	input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y);
696ddfe7e1cSLinus Walleij 	/* One byte for width 0..255 so this is the limit */
697ddfe7e1cSLinus Walleij 	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
698ddfe7e1cSLinus Walleij 
699ed7c9870SHans de Goede 	touchscreen_parse_properties(input_dev, true, NULL);
7004065d1e7SJavier Martinez Canillas 
701dadf1fd8SLinus Walleij 	error = input_mt_init_slots(input_dev, CY_MAX_ID, INPUT_MT_DIRECT);
70269a12402SOreste Salerno 	if (error) {
70369a12402SOreste Salerno 		dev_err(dev, "Unable to init MT slots.\n");
70469a12402SOreste Salerno 		return ERR_PTR(error);
70569a12402SOreste Salerno 	}
7064065d1e7SJavier Martinez Canillas 
707e4cf92baSOreste Salerno 	error = devm_request_threaded_irq(dev, ts->irq, NULL, cyttsp_irq,
708c9d2939dSDmitry Torokhov 					  IRQF_ONESHOT | IRQF_NO_AUTOEN,
709707b61bbSOreste Salerno 					  "cyttsp", ts);
7104065d1e7SJavier Martinez Canillas 	if (error) {
7114065d1e7SJavier Martinez Canillas 		dev_err(ts->dev, "failed to request IRQ %d, err: %d\n",
7124065d1e7SJavier Martinez Canillas 			ts->irq, error);
713e4cf92baSOreste Salerno 		return ERR_PTR(error);
7144065d1e7SJavier Martinez Canillas 	}
7154065d1e7SJavier Martinez Canillas 
7168fb81d20SOreste Salerno 	cyttsp_hard_reset(ts);
7178fb81d20SOreste Salerno 
7184065d1e7SJavier Martinez Canillas 	error = cyttsp_power_on(ts);
7194065d1e7SJavier Martinez Canillas 	if (error)
720e4cf92baSOreste Salerno 		return ERR_PTR(error);
7214065d1e7SJavier Martinez Canillas 
7224065d1e7SJavier Martinez Canillas 	error = input_register_device(input_dev);
7234065d1e7SJavier Martinez Canillas 	if (error) {
7244065d1e7SJavier Martinez Canillas 		dev_err(ts->dev, "failed to register input device: %d\n",
7254065d1e7SJavier Martinez Canillas 			error);
726e4cf92baSOreste Salerno 		return ERR_PTR(error);
7274065d1e7SJavier Martinez Canillas 	}
7284065d1e7SJavier Martinez Canillas 
7294065d1e7SJavier Martinez Canillas 	return ts;
7304065d1e7SJavier Martinez Canillas }
7314065d1e7SJavier Martinez Canillas EXPORT_SYMBOL_GPL(cyttsp_probe);
7324065d1e7SJavier Martinez Canillas 
7334065d1e7SJavier Martinez Canillas MODULE_LICENSE("GPL");
7344065d1e7SJavier Martinez Canillas MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core");
7354065d1e7SJavier Martinez Canillas MODULE_AUTHOR("Cypress");
736