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