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