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