xref: /OK3568_Linux_fs/kernel/drivers/input/touchscreen/cyttsp5/cyttsp5_proximity.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * cyttsp5_proximity.c
3*4882a593Smuzhiyun  * Parade TrueTouch(TM) Standard Product V5 Proximity Module.
4*4882a593Smuzhiyun  * For use with Parade touchscreen controllers.
5*4882a593Smuzhiyun  * Supported parts include:
6*4882a593Smuzhiyun  * CYTMA5XX
7*4882a593Smuzhiyun  * CYTMA448
8*4882a593Smuzhiyun  * CYTMA445A
9*4882a593Smuzhiyun  * CYTT21XXX
10*4882a593Smuzhiyun  * CYTT31XXX
11*4882a593Smuzhiyun  *
12*4882a593Smuzhiyun  * Copyright (C) 2015 Parade Technologies
13*4882a593Smuzhiyun  * Copyright (C) 2013-2015 Cypress Semiconductor
14*4882a593Smuzhiyun  *
15*4882a593Smuzhiyun  * This program is free software; you can redistribute it and/or
16*4882a593Smuzhiyun  * modify it under the terms of the GNU General Public License
17*4882a593Smuzhiyun  * version 2, and only version 2, as published by the
18*4882a593Smuzhiyun  * Free Software Foundation.
19*4882a593Smuzhiyun  *
20*4882a593Smuzhiyun  * This program is distributed in the hope that it will be useful,
21*4882a593Smuzhiyun  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22*4882a593Smuzhiyun  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23*4882a593Smuzhiyun  * GNU General Public License for more details.
24*4882a593Smuzhiyun  *
25*4882a593Smuzhiyun  * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
26*4882a593Smuzhiyun  *
27*4882a593Smuzhiyun  */
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun #include "cyttsp5_regs.h"
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun #define CYTTSP5_PROXIMITY_NAME "cyttsp5_proximity"
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun /* Timeout value in ms. */
34*4882a593Smuzhiyun #define CYTTSP5_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT		1000
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun #define CYTTSP5_PROXIMITY_ON 0
37*4882a593Smuzhiyun #define CYTTSP5_PROXIMITY_OFF 1
38*4882a593Smuzhiyun 
get_prox_data(struct device * dev)39*4882a593Smuzhiyun static inline struct cyttsp5_proximity_data *get_prox_data(struct device *dev)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun 	struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun 	return &cd->pd;
44*4882a593Smuzhiyun }
45*4882a593Smuzhiyun 
cyttsp5_report_proximity(struct cyttsp5_proximity_data * pd,bool on)46*4882a593Smuzhiyun static void cyttsp5_report_proximity(struct cyttsp5_proximity_data *pd,
47*4882a593Smuzhiyun 	bool on)
48*4882a593Smuzhiyun {
49*4882a593Smuzhiyun 	int val = on ? CYTTSP5_PROXIMITY_ON : CYTTSP5_PROXIMITY_OFF;
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	input_report_abs(pd->input, ABS_DISTANCE, val);
52*4882a593Smuzhiyun 	input_sync(pd->input);
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun 
cyttsp5_get_touch_axis(struct cyttsp5_proximity_data * pd,int * axis,int size,int max,u8 * xy_data,int bofs)55*4882a593Smuzhiyun static void cyttsp5_get_touch_axis(struct cyttsp5_proximity_data *pd,
56*4882a593Smuzhiyun 	int *axis, int size, int max, u8 *xy_data, int bofs)
57*4882a593Smuzhiyun {
58*4882a593Smuzhiyun 	int nbyte;
59*4882a593Smuzhiyun 	int next;
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) {
62*4882a593Smuzhiyun 		parade_debug(pd->dev, DEBUG_LEVEL_2,
63*4882a593Smuzhiyun 			"%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d) bofs=%d\n",
64*4882a593Smuzhiyun 			__func__, *axis, *axis, size, max, xy_data, next,
65*4882a593Smuzhiyun 			xy_data[next], xy_data[next], bofs);
66*4882a593Smuzhiyun 		*axis = *axis + ((xy_data[next] >> bofs) << (nbyte * 8));
67*4882a593Smuzhiyun 		next++;
68*4882a593Smuzhiyun 	}
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	*axis &= max - 1;
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 	parade_debug(pd->dev, DEBUG_LEVEL_2,
73*4882a593Smuzhiyun 		"%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d)\n",
74*4882a593Smuzhiyun 		__func__, *axis, *axis, size, max, xy_data, next,
75*4882a593Smuzhiyun 		xy_data[next], xy_data[next]);
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun 
cyttsp5_get_touch_hdr(struct cyttsp5_proximity_data * pd,struct cyttsp5_touch * touch,u8 * xy_mode)78*4882a593Smuzhiyun static void cyttsp5_get_touch_hdr(struct cyttsp5_proximity_data *pd,
79*4882a593Smuzhiyun 	struct cyttsp5_touch *touch, u8 *xy_mode)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun 	struct device *dev = pd->dev;
82*4882a593Smuzhiyun 	struct cyttsp5_sysinfo *si = pd->si;
83*4882a593Smuzhiyun 	enum cyttsp5_tch_hdr hdr;
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	for (hdr = CY_TCH_TIME; hdr < CY_TCH_NUM_HDR; hdr++) {
86*4882a593Smuzhiyun 		if (!si->tch_hdr[hdr].report)
87*4882a593Smuzhiyun 			continue;
88*4882a593Smuzhiyun 		cyttsp5_get_touch_axis(pd, &touch->hdr[hdr],
89*4882a593Smuzhiyun 			si->tch_hdr[hdr].size,
90*4882a593Smuzhiyun 			si->tch_hdr[hdr].max,
91*4882a593Smuzhiyun 			xy_mode + si->tch_hdr[hdr].ofs,
92*4882a593Smuzhiyun 			si->tch_hdr[hdr].bofs);
93*4882a593Smuzhiyun 		parade_debug(dev, DEBUG_LEVEL_2, "%s: get %s=%04X(%d)\n",
94*4882a593Smuzhiyun 			__func__, cyttsp5_tch_hdr_string[hdr],
95*4882a593Smuzhiyun 			touch->hdr[hdr], touch->hdr[hdr]);
96*4882a593Smuzhiyun 	}
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun 
cyttsp5_get_touch(struct cyttsp5_proximity_data * pd,struct cyttsp5_touch * touch,u8 * xy_data)99*4882a593Smuzhiyun static void cyttsp5_get_touch(struct cyttsp5_proximity_data *pd,
100*4882a593Smuzhiyun 	struct cyttsp5_touch *touch, u8 *xy_data)
101*4882a593Smuzhiyun {
102*4882a593Smuzhiyun 	struct device *dev = pd->dev;
103*4882a593Smuzhiyun 	struct cyttsp5_sysinfo *si = pd->si;
104*4882a593Smuzhiyun 	enum cyttsp5_tch_abs abs;
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) {
107*4882a593Smuzhiyun 		if (!si->tch_abs[abs].report)
108*4882a593Smuzhiyun 			continue;
109*4882a593Smuzhiyun 		cyttsp5_get_touch_axis(pd, &touch->abs[abs],
110*4882a593Smuzhiyun 			si->tch_abs[abs].size,
111*4882a593Smuzhiyun 			si->tch_abs[abs].max,
112*4882a593Smuzhiyun 			xy_data + si->tch_abs[abs].ofs,
113*4882a593Smuzhiyun 			si->tch_abs[abs].bofs);
114*4882a593Smuzhiyun 		parade_debug(dev, DEBUG_LEVEL_2, "%s: get %s=%04X(%d)\n",
115*4882a593Smuzhiyun 			__func__, cyttsp5_tch_abs_string[abs],
116*4882a593Smuzhiyun 			touch->abs[abs], touch->abs[abs]);
117*4882a593Smuzhiyun 	}
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	parade_debug(dev, DEBUG_LEVEL_2, "%s: x=%04X(%d) y=%04X(%d)\n",
120*4882a593Smuzhiyun 		__func__, touch->abs[CY_TCH_X], touch->abs[CY_TCH_X],
121*4882a593Smuzhiyun 		touch->abs[CY_TCH_Y], touch->abs[CY_TCH_Y]);
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun 
cyttsp5_get_proximity_touch(struct cyttsp5_proximity_data * pd,struct cyttsp5_touch * tch,int num_cur_tch)124*4882a593Smuzhiyun static void cyttsp5_get_proximity_touch(struct cyttsp5_proximity_data *pd,
125*4882a593Smuzhiyun 		struct cyttsp5_touch *tch, int num_cur_tch)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun 	struct cyttsp5_sysinfo *si = pd->si;
128*4882a593Smuzhiyun 	int i;
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	for (i = 0; i < num_cur_tch; i++) {
131*4882a593Smuzhiyun 		cyttsp5_get_touch(pd, tch, si->xy_data +
132*4882a593Smuzhiyun 			(i * si->desc.tch_record_size));
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 		/* Check for proximity event */
135*4882a593Smuzhiyun 		if (tch->abs[CY_TCH_O] == CY_OBJ_PROXIMITY) {
136*4882a593Smuzhiyun 			if (tch->abs[CY_TCH_E] == CY_EV_TOUCHDOWN)
137*4882a593Smuzhiyun 				cyttsp5_report_proximity(pd, true);
138*4882a593Smuzhiyun 			else if (tch->abs[CY_TCH_E] == CY_EV_LIFTOFF)
139*4882a593Smuzhiyun 				cyttsp5_report_proximity(pd, false);
140*4882a593Smuzhiyun 			break;
141*4882a593Smuzhiyun 		}
142*4882a593Smuzhiyun 	}
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun /* read xy_data for all current touches */
cyttsp5_xy_worker(struct cyttsp5_proximity_data * pd)146*4882a593Smuzhiyun static int cyttsp5_xy_worker(struct cyttsp5_proximity_data *pd)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun 	struct device *dev = pd->dev;
149*4882a593Smuzhiyun 	struct cyttsp5_sysinfo *si = pd->si;
150*4882a593Smuzhiyun 	struct cyttsp5_touch tch;
151*4882a593Smuzhiyun 	u8 num_cur_tch;
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	cyttsp5_get_touch_hdr(pd, &tch, si->xy_mode + 3);
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	num_cur_tch = tch.hdr[CY_TCH_NUM];
156*4882a593Smuzhiyun 	if (num_cur_tch > si->sensing_conf_data.max_tch) {
157*4882a593Smuzhiyun 		dev_err(dev, "%s: Num touch err detected (n=%d)\n",
158*4882a593Smuzhiyun 			__func__, num_cur_tch);
159*4882a593Smuzhiyun 		num_cur_tch = si->sensing_conf_data.max_tch;
160*4882a593Smuzhiyun 	}
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	if (tch.hdr[CY_TCH_LO])
163*4882a593Smuzhiyun 		parade_debug(dev, DEBUG_LEVEL_1, "%s: Large area detected\n",
164*4882a593Smuzhiyun 		__func__);
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	/* extract xy_data for all currently reported touches */
167*4882a593Smuzhiyun 	parade_debug(dev, DEBUG_LEVEL_2, "%s: extract data num_cur_rec=%d\n",
168*4882a593Smuzhiyun 		__func__, num_cur_tch);
169*4882a593Smuzhiyun 	if (num_cur_tch)
170*4882a593Smuzhiyun 		cyttsp5_get_proximity_touch(pd, &tch, num_cur_tch);
171*4882a593Smuzhiyun 	else
172*4882a593Smuzhiyun 		cyttsp5_report_proximity(pd, false);
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun 	return 0;
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun 
cyttsp5_proximity_attention(struct device * dev)177*4882a593Smuzhiyun static int cyttsp5_proximity_attention(struct device *dev)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun 	struct cyttsp5_proximity_data *pd = get_prox_data(dev);
180*4882a593Smuzhiyun 	int rc = 0;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	if (pd->si->xy_mode[2] != pd->si->desc.tch_report_id)
183*4882a593Smuzhiyun 		return 0;
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	mutex_lock(&pd->prox_lock);
186*4882a593Smuzhiyun 	rc = cyttsp5_xy_worker(pd);
187*4882a593Smuzhiyun 	mutex_unlock(&pd->prox_lock);
188*4882a593Smuzhiyun 	if (rc < 0)
189*4882a593Smuzhiyun 		dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc);
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	return rc;
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun 
cyttsp5_startup_attention(struct device * dev)194*4882a593Smuzhiyun static int cyttsp5_startup_attention(struct device *dev)
195*4882a593Smuzhiyun {
196*4882a593Smuzhiyun 	struct cyttsp5_proximity_data *pd = get_prox_data(dev);
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	mutex_lock(&pd->prox_lock);
199*4882a593Smuzhiyun 	cyttsp5_report_proximity(pd, false);
200*4882a593Smuzhiyun 	mutex_unlock(&pd->prox_lock);
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	return 0;
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun 
_cyttsp5_set_proximity_via_touchmode_enabled(struct cyttsp5_proximity_data * pd,bool enable)205*4882a593Smuzhiyun static int _cyttsp5_set_proximity_via_touchmode_enabled(
206*4882a593Smuzhiyun 		struct cyttsp5_proximity_data *pd, bool enable)
207*4882a593Smuzhiyun {
208*4882a593Smuzhiyun 	struct device *dev = pd->dev;
209*4882a593Smuzhiyun 	u32 touchmode_enabled;
210*4882a593Smuzhiyun 	int rc;
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	rc = cyttsp5_request_nonhid_get_param(dev, 0,
213*4882a593Smuzhiyun 			CY_RAM_ID_TOUCHMODE_ENABLED, &touchmode_enabled);
214*4882a593Smuzhiyun 	if (rc)
215*4882a593Smuzhiyun 		return rc;
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	if (enable)
218*4882a593Smuzhiyun 		touchmode_enabled |= 0x80;
219*4882a593Smuzhiyun 	else
220*4882a593Smuzhiyun 		touchmode_enabled &= 0x7F;
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	rc = cyttsp5_request_nonhid_set_param(dev, 0,
223*4882a593Smuzhiyun 			CY_RAM_ID_TOUCHMODE_ENABLED, touchmode_enabled,
224*4882a593Smuzhiyun 			CY_RAM_ID_TOUCHMODE_ENABLED_SIZE);
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	return rc;
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun 
_cyttsp5_set_proximity_via_proximity_enable(struct cyttsp5_proximity_data * pd,bool enable)229*4882a593Smuzhiyun static int _cyttsp5_set_proximity_via_proximity_enable(
230*4882a593Smuzhiyun 		struct cyttsp5_proximity_data *pd, bool enable)
231*4882a593Smuzhiyun {
232*4882a593Smuzhiyun 	struct device *dev = pd->dev;
233*4882a593Smuzhiyun 	u32 proximity_enable;
234*4882a593Smuzhiyun 	int rc;
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	rc = cyttsp5_request_nonhid_get_param(dev, 0,
237*4882a593Smuzhiyun 			CY_RAM_ID_PROXIMITY_ENABLE, &proximity_enable);
238*4882a593Smuzhiyun 	if (rc)
239*4882a593Smuzhiyun 		return rc;
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	if (enable)
242*4882a593Smuzhiyun 		proximity_enable |= 0x01;
243*4882a593Smuzhiyun 	else
244*4882a593Smuzhiyun 		proximity_enable &= 0xFE;
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	rc = cyttsp5_request_nonhid_set_param(dev, 0,
247*4882a593Smuzhiyun 			CY_RAM_ID_PROXIMITY_ENABLE, proximity_enable,
248*4882a593Smuzhiyun 			CY_RAM_ID_PROXIMITY_ENABLE_SIZE);
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	return rc;
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun 
_cyttsp5_set_proximity(struct cyttsp5_proximity_data * pd,bool enable)253*4882a593Smuzhiyun static int _cyttsp5_set_proximity(struct cyttsp5_proximity_data *pd,
254*4882a593Smuzhiyun 		bool enable)
255*4882a593Smuzhiyun {
256*4882a593Smuzhiyun 	if (!IS_PIP_VER_GE(pd->si, 1, 4))
257*4882a593Smuzhiyun 		return _cyttsp5_set_proximity_via_touchmode_enabled(pd,
258*4882a593Smuzhiyun 				enable);
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 	return _cyttsp5_set_proximity_via_proximity_enable(pd, enable);
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun 
_cyttsp5_proximity_enable(struct cyttsp5_proximity_data * pd)263*4882a593Smuzhiyun static int _cyttsp5_proximity_enable(struct cyttsp5_proximity_data *pd)
264*4882a593Smuzhiyun {
265*4882a593Smuzhiyun 	struct device *dev = pd->dev;
266*4882a593Smuzhiyun 	int rc = 0;
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 	pm_runtime_get_sync(dev);
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 	rc = cyttsp5_request_exclusive(dev,
271*4882a593Smuzhiyun 			CYTTSP5_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT);
272*4882a593Smuzhiyun 	if (rc < 0) {
273*4882a593Smuzhiyun 		dev_err(dev, "%s: Error on request exclusive r=%d\n",
274*4882a593Smuzhiyun 				__func__, rc);
275*4882a593Smuzhiyun 		goto exit;
276*4882a593Smuzhiyun 	}
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	rc = _cyttsp5_set_proximity(pd, true);
279*4882a593Smuzhiyun 	if (rc < 0) {
280*4882a593Smuzhiyun 		dev_err(dev, "%s: Error on request enable proximity scantype r=%d\n",
281*4882a593Smuzhiyun 				__func__, rc);
282*4882a593Smuzhiyun 		goto exit_release;
283*4882a593Smuzhiyun 	}
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	parade_debug(dev, DEBUG_LEVEL_2, "%s: setup subscriptions\n", __func__);
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 	/* set up touch call back */
288*4882a593Smuzhiyun 	_cyttsp5_subscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_PROXIMITY_NAME,
289*4882a593Smuzhiyun 		cyttsp5_proximity_attention, CY_MODE_OPERATIONAL);
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	/* set up startup call back */
292*4882a593Smuzhiyun 	_cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP,
293*4882a593Smuzhiyun 		CYTTSP5_PROXIMITY_NAME, cyttsp5_startup_attention, 0);
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun exit_release:
296*4882a593Smuzhiyun 	cyttsp5_release_exclusive(dev);
297*4882a593Smuzhiyun exit:
298*4882a593Smuzhiyun 	return rc;
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun 
_cyttsp5_proximity_disable(struct cyttsp5_proximity_data * pd,bool force)301*4882a593Smuzhiyun static int _cyttsp5_proximity_disable(struct cyttsp5_proximity_data *pd,
302*4882a593Smuzhiyun 		bool force)
303*4882a593Smuzhiyun {
304*4882a593Smuzhiyun 	struct device *dev = pd->dev;
305*4882a593Smuzhiyun 	int rc = 0;
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	rc = cyttsp5_request_exclusive(dev,
308*4882a593Smuzhiyun 			CYTTSP5_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT);
309*4882a593Smuzhiyun 	if (rc < 0) {
310*4882a593Smuzhiyun 		dev_err(dev, "%s: Error on request exclusive r=%d\n",
311*4882a593Smuzhiyun 				__func__, rc);
312*4882a593Smuzhiyun 		goto exit;
313*4882a593Smuzhiyun 	}
314*4882a593Smuzhiyun 
315*4882a593Smuzhiyun 	rc = _cyttsp5_set_proximity(pd, false);
316*4882a593Smuzhiyun 	if (rc < 0) {
317*4882a593Smuzhiyun 		dev_err(dev, "%s: Error on request disable proximity scan r=%d\n",
318*4882a593Smuzhiyun 				__func__, rc);
319*4882a593Smuzhiyun 		goto exit_release;
320*4882a593Smuzhiyun 	}
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun exit_release:
323*4882a593Smuzhiyun 	cyttsp5_release_exclusive(dev);
324*4882a593Smuzhiyun 
325*4882a593Smuzhiyun exit:
326*4882a593Smuzhiyun 	if (!rc || force) {
327*4882a593Smuzhiyun 		_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_IRQ,
328*4882a593Smuzhiyun 			CYTTSP5_PROXIMITY_NAME, cyttsp5_proximity_attention,
329*4882a593Smuzhiyun 			CY_MODE_OPERATIONAL);
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 		_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP,
332*4882a593Smuzhiyun 			CYTTSP5_PROXIMITY_NAME, cyttsp5_startup_attention, 0);
333*4882a593Smuzhiyun 	}
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	pm_runtime_put(dev);
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	return rc;
338*4882a593Smuzhiyun }
339*4882a593Smuzhiyun 
cyttsp5_proximity_enable_show(struct device * dev,struct device_attribute * attr,char * buf)340*4882a593Smuzhiyun static ssize_t cyttsp5_proximity_enable_show(struct device *dev,
341*4882a593Smuzhiyun 		struct device_attribute *attr, char *buf)
342*4882a593Smuzhiyun {
343*4882a593Smuzhiyun 	struct cyttsp5_proximity_data *pd = get_prox_data(dev);
344*4882a593Smuzhiyun 	int val = 0;
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 	mutex_lock(&pd->sysfs_lock);
347*4882a593Smuzhiyun 	val = pd->enable_count;
348*4882a593Smuzhiyun 	mutex_unlock(&pd->sysfs_lock);
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun 	return scnprintf(buf, CY_MAX_PRBUF_SIZE, "%d\n", val);
351*4882a593Smuzhiyun }
352*4882a593Smuzhiyun 
cyttsp5_proximity_enable_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)353*4882a593Smuzhiyun static ssize_t cyttsp5_proximity_enable_store(struct device *dev,
354*4882a593Smuzhiyun 		struct device_attribute *attr, const char *buf, size_t size)
355*4882a593Smuzhiyun {
356*4882a593Smuzhiyun 	struct cyttsp5_proximity_data *pd = get_prox_data(dev);
357*4882a593Smuzhiyun 	unsigned long value;
358*4882a593Smuzhiyun 	int rc;
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	rc = kstrtoul(buf, 10, &value);
361*4882a593Smuzhiyun 	if (rc < 0 || (value != 0 && value != 1)) {
362*4882a593Smuzhiyun 		dev_err(dev, "%s: Invalid value\n", __func__);
363*4882a593Smuzhiyun 		return -EINVAL;
364*4882a593Smuzhiyun 	}
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 	mutex_lock(&pd->sysfs_lock);
367*4882a593Smuzhiyun 	if (value) {
368*4882a593Smuzhiyun 		if (pd->enable_count++) {
369*4882a593Smuzhiyun 			parade_debug(dev, DEBUG_LEVEL_2, "%s: '%s' already enabled\n",
370*4882a593Smuzhiyun 				__func__, pd->input->name);
371*4882a593Smuzhiyun 		} else {
372*4882a593Smuzhiyun 			rc = _cyttsp5_proximity_enable(pd);
373*4882a593Smuzhiyun 			if (rc)
374*4882a593Smuzhiyun 				pd->enable_count--;
375*4882a593Smuzhiyun 		}
376*4882a593Smuzhiyun 	} else {
377*4882a593Smuzhiyun 		if (--pd->enable_count) {
378*4882a593Smuzhiyun 			if (pd->enable_count < 0) {
379*4882a593Smuzhiyun 				dev_err(dev, "%s: '%s' unbalanced disable\n",
380*4882a593Smuzhiyun 					__func__, pd->input->name);
381*4882a593Smuzhiyun 				pd->enable_count = 0;
382*4882a593Smuzhiyun 			}
383*4882a593Smuzhiyun 		} else {
384*4882a593Smuzhiyun 			rc = _cyttsp5_proximity_disable(pd, false);
385*4882a593Smuzhiyun 			if (rc)
386*4882a593Smuzhiyun 				pd->enable_count++;
387*4882a593Smuzhiyun 		}
388*4882a593Smuzhiyun 	}
389*4882a593Smuzhiyun 	mutex_unlock(&pd->sysfs_lock);
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	if (rc)
392*4882a593Smuzhiyun 		return rc;
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun 	return size;
395*4882a593Smuzhiyun }
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun static DEVICE_ATTR(prox_enable, S_IRUSR | S_IWUSR,
398*4882a593Smuzhiyun 		cyttsp5_proximity_enable_show,
399*4882a593Smuzhiyun 		cyttsp5_proximity_enable_store);
400*4882a593Smuzhiyun 
cyttsp5_setup_input_device_and_sysfs(struct device * dev)401*4882a593Smuzhiyun static int cyttsp5_setup_input_device_and_sysfs(struct device *dev)
402*4882a593Smuzhiyun {
403*4882a593Smuzhiyun 	struct cyttsp5_proximity_data *pd = get_prox_data(dev);
404*4882a593Smuzhiyun 	int signal = CY_IGNORE_VALUE;
405*4882a593Smuzhiyun 	int i;
406*4882a593Smuzhiyun 	int rc;
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 	rc = device_create_file(dev, &dev_attr_prox_enable);
409*4882a593Smuzhiyun 	if (rc) {
410*4882a593Smuzhiyun 		dev_err(dev, "%s: Error, could not create enable\n",
411*4882a593Smuzhiyun 				__func__);
412*4882a593Smuzhiyun 		goto exit;
413*4882a593Smuzhiyun 	}
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	parade_debug(dev, DEBUG_LEVEL_2, "%s: Initialize event signals\n",
416*4882a593Smuzhiyun 				__func__);
417*4882a593Smuzhiyun 
418*4882a593Smuzhiyun 	__set_bit(EV_ABS, pd->input->evbit);
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	/* set event signal capabilities */
421*4882a593Smuzhiyun 	for (i = 0; i < NUM_SIGNALS(pd->pdata->frmwrk); i++) {
422*4882a593Smuzhiyun 		signal = PARAM_SIGNAL(pd->pdata->frmwrk, i);
423*4882a593Smuzhiyun 		if (signal != CY_IGNORE_VALUE) {
424*4882a593Smuzhiyun 			input_set_abs_params(pd->input, signal,
425*4882a593Smuzhiyun 				PARAM_MIN(pd->pdata->frmwrk, i),
426*4882a593Smuzhiyun 				PARAM_MAX(pd->pdata->frmwrk, i),
427*4882a593Smuzhiyun 				PARAM_FUZZ(pd->pdata->frmwrk, i),
428*4882a593Smuzhiyun 				PARAM_FLAT(pd->pdata->frmwrk, i));
429*4882a593Smuzhiyun 		}
430*4882a593Smuzhiyun 	}
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	rc = input_register_device(pd->input);
433*4882a593Smuzhiyun 	if (rc) {
434*4882a593Smuzhiyun 		dev_err(dev, "%s: Error, failed register input device r=%d\n",
435*4882a593Smuzhiyun 			__func__, rc);
436*4882a593Smuzhiyun 		goto unregister_enable;
437*4882a593Smuzhiyun 	}
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 	pd->input_device_registered = true;
440*4882a593Smuzhiyun 	return rc;
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun unregister_enable:
443*4882a593Smuzhiyun 	device_remove_file(dev, &dev_attr_prox_enable);
444*4882a593Smuzhiyun exit:
445*4882a593Smuzhiyun 	return rc;
446*4882a593Smuzhiyun }
447*4882a593Smuzhiyun 
cyttsp5_setup_input_attention(struct device * dev)448*4882a593Smuzhiyun static int cyttsp5_setup_input_attention(struct device *dev)
449*4882a593Smuzhiyun {
450*4882a593Smuzhiyun 	struct cyttsp5_proximity_data *pd = get_prox_data(dev);
451*4882a593Smuzhiyun 	int rc;
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 	pd->si = _cyttsp5_request_sysinfo(dev);
454*4882a593Smuzhiyun 	if (!pd->si)
455*4882a593Smuzhiyun 		return -EINVAL;
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 	rc = cyttsp5_setup_input_device_and_sysfs(dev);
458*4882a593Smuzhiyun 	if (!rc)
459*4882a593Smuzhiyun 		rc = _cyttsp5_set_proximity(pd, false);
460*4882a593Smuzhiyun 
461*4882a593Smuzhiyun 	_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP,
462*4882a593Smuzhiyun 		CYTTSP5_PROXIMITY_NAME, cyttsp5_setup_input_attention, 0);
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun 	return rc;
465*4882a593Smuzhiyun }
466*4882a593Smuzhiyun 
cyttsp5_proximity_probe(struct device * dev)467*4882a593Smuzhiyun int cyttsp5_proximity_probe(struct device *dev)
468*4882a593Smuzhiyun {
469*4882a593Smuzhiyun 	struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
470*4882a593Smuzhiyun 	struct cyttsp5_proximity_data *pd = &cd->pd;
471*4882a593Smuzhiyun 	struct cyttsp5_platform_data *pdata = dev_get_platdata(dev);
472*4882a593Smuzhiyun 	struct cyttsp5_proximity_platform_data *prox_pdata;
473*4882a593Smuzhiyun 	int rc = 0;
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 	if (!pdata ||  !pdata->prox_pdata) {
476*4882a593Smuzhiyun 		dev_err(dev, "%s: Missing platform data\n", __func__);
477*4882a593Smuzhiyun 		rc = -ENODEV;
478*4882a593Smuzhiyun 		goto error_no_pdata;
479*4882a593Smuzhiyun 	}
480*4882a593Smuzhiyun 	prox_pdata = pdata->prox_pdata;
481*4882a593Smuzhiyun 
482*4882a593Smuzhiyun 	mutex_init(&pd->prox_lock);
483*4882a593Smuzhiyun 	mutex_init(&pd->sysfs_lock);
484*4882a593Smuzhiyun 	pd->dev = dev;
485*4882a593Smuzhiyun 	pd->pdata = prox_pdata;
486*4882a593Smuzhiyun 
487*4882a593Smuzhiyun 	/* Create the input device and register it. */
488*4882a593Smuzhiyun 	parade_debug(dev, DEBUG_LEVEL_2,
489*4882a593Smuzhiyun 		"%s: Create the input device and register it\n", __func__);
490*4882a593Smuzhiyun 	pd->input = input_allocate_device();
491*4882a593Smuzhiyun 	if (!pd->input) {
492*4882a593Smuzhiyun 		dev_err(dev, "%s: Error, failed to allocate input device\n",
493*4882a593Smuzhiyun 			__func__);
494*4882a593Smuzhiyun 		rc = -ENODEV;
495*4882a593Smuzhiyun 		goto error_alloc_failed;
496*4882a593Smuzhiyun 	}
497*4882a593Smuzhiyun 
498*4882a593Smuzhiyun 	if (pd->pdata->inp_dev_name)
499*4882a593Smuzhiyun 		pd->input->name = pd->pdata->inp_dev_name;
500*4882a593Smuzhiyun 	else
501*4882a593Smuzhiyun 		pd->input->name = CYTTSP5_PROXIMITY_NAME;
502*4882a593Smuzhiyun 	scnprintf(pd->phys, sizeof(pd->phys), "%s/input%d", dev_name(dev),
503*4882a593Smuzhiyun 			cd->phys_num++);
504*4882a593Smuzhiyun 	pd->input->phys = pd->phys;
505*4882a593Smuzhiyun 	pd->input->dev.parent = pd->dev;
506*4882a593Smuzhiyun 	input_set_drvdata(pd->input, pd);
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun 	/* get sysinfo */
509*4882a593Smuzhiyun 	pd->si = _cyttsp5_request_sysinfo(dev);
510*4882a593Smuzhiyun 
511*4882a593Smuzhiyun 	if (pd->si) {
512*4882a593Smuzhiyun 		rc = cyttsp5_setup_input_device_and_sysfs(dev);
513*4882a593Smuzhiyun 		if (rc)
514*4882a593Smuzhiyun 			goto error_init_input;
515*4882a593Smuzhiyun 
516*4882a593Smuzhiyun 		rc = _cyttsp5_set_proximity(pd, false);
517*4882a593Smuzhiyun 	} else {
518*4882a593Smuzhiyun 		dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n",
519*4882a593Smuzhiyun 			__func__, pd->si);
520*4882a593Smuzhiyun 		_cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP,
521*4882a593Smuzhiyun 			CYTTSP5_PROXIMITY_NAME, cyttsp5_setup_input_attention,
522*4882a593Smuzhiyun 			0);
523*4882a593Smuzhiyun 	}
524*4882a593Smuzhiyun 
525*4882a593Smuzhiyun 	return 0;
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun error_init_input:
528*4882a593Smuzhiyun 	input_free_device(pd->input);
529*4882a593Smuzhiyun error_alloc_failed:
530*4882a593Smuzhiyun error_no_pdata:
531*4882a593Smuzhiyun 	dev_err(dev, "%s failed.\n", __func__);
532*4882a593Smuzhiyun 	return rc;
533*4882a593Smuzhiyun }
534*4882a593Smuzhiyun 
cyttsp5_proximity_release(struct device * dev)535*4882a593Smuzhiyun int cyttsp5_proximity_release(struct device *dev)
536*4882a593Smuzhiyun {
537*4882a593Smuzhiyun 	struct cyttsp5_proximity_data *pd = get_prox_data(dev);
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun 	if (pd->input_device_registered) {
540*4882a593Smuzhiyun 		/* Disable proximity sensing */
541*4882a593Smuzhiyun 		mutex_lock(&pd->sysfs_lock);
542*4882a593Smuzhiyun 		if (pd->enable_count)
543*4882a593Smuzhiyun 			_cyttsp5_proximity_disable(pd, true);
544*4882a593Smuzhiyun 		mutex_unlock(&pd->sysfs_lock);
545*4882a593Smuzhiyun 		device_remove_file(dev, &dev_attr_prox_enable);
546*4882a593Smuzhiyun 		input_unregister_device(pd->input);
547*4882a593Smuzhiyun 	} else {
548*4882a593Smuzhiyun 		input_free_device(pd->input);
549*4882a593Smuzhiyun 		_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP,
550*4882a593Smuzhiyun 			CYTTSP5_PROXIMITY_NAME, cyttsp5_setup_input_attention,
551*4882a593Smuzhiyun 			0);
552*4882a593Smuzhiyun 	}
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun 	return 0;
555*4882a593Smuzhiyun }
556