Input: ads7846 - correct the value got from SPI
[deliverable/linux.git] / drivers / input / mouse / cyapa_gen6.c
CommitLineData
c2c06c41
DD
1/*
2 * Cypress APA trackpad with I2C interface
3 *
4 * Author: Dudley Du <dudl@cypress.com>
5 *
6 * Copyright (C) 2015 Cypress Semiconductor, Inc.
7 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License. See the file COPYING in the main directory of this archive for
10 * more details.
11 */
12
13#include <linux/delay.h>
14#include <linux/i2c.h>
15#include <linux/input.h>
16#include <linux/input/mt.h>
17#include <linux/mutex.h>
18#include <linux/completion.h>
19#include <linux/slab.h>
20#include <asm/unaligned.h>
21#include <linux/crc-itu-t.h>
22#include "cyapa.h"
23
24
25#define GEN6_ENABLE_CMD_IRQ 0x41
26#define GEN6_DISABLE_CMD_IRQ 0x42
27#define GEN6_ENABLE_DEV_IRQ 0x43
28#define GEN6_DISABLE_DEV_IRQ 0x44
29
30#define GEN6_POWER_MODE_ACTIVE 0x01
31#define GEN6_POWER_MODE_LP_MODE1 0x02
32#define GEN6_POWER_MODE_LP_MODE2 0x03
33#define GEN6_POWER_MODE_BTN_ONLY 0x04
34
35#define GEN6_SET_POWER_MODE_INTERVAL 0x47
36#define GEN6_GET_POWER_MODE_INTERVAL 0x48
37
38#define GEN6_MAX_RX_NUM 14
39#define GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC 0x00
40#define GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM 0x12
41
42
43struct pip_app_cmd_head {
44 __le16 addr;
45 __le16 length;
46 u8 report_id;
47 u8 resv; /* Reserved, must be 0 */
48 u8 cmd_code; /* bit7: resv, set to 0; bit6~0: command code.*/
49} __packed;
50
51struct pip_app_resp_head {
52 __le16 length;
53 u8 report_id;
54 u8 resv; /* Reserved, must be 0 */
55 u8 cmd_code; /* bit7: TGL; bit6~0: command code.*/
56 /*
57 * The value of data_status can be the first byte of data or
58 * the command status or the unsupported command code depending on the
59 * requested command code.
60 */
61 u8 data_status;
62} __packed;
63
64struct pip_fixed_info {
65 u8 silicon_id_high;
66 u8 silicon_id_low;
67 u8 family_id;
68};
69
70static u8 pip_get_bl_info[] = {
71 0x04, 0x00, 0x0B, 0x00, 0x40, 0x00, 0x01, 0x38,
72 0x00, 0x00, 0x70, 0x9E, 0x17
73};
74
75static bool cyapa_sort_pip_hid_descriptor_data(struct cyapa *cyapa,
76 u8 *buf, int len)
77{
78 if (len != PIP_HID_DESCRIPTOR_SIZE)
79 return false;
80
81 if (buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID ||
82 buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID)
83 return true;
84
85 return false;
86}
87
88static int cyapa_get_pip_fixed_info(struct cyapa *cyapa,
89 struct pip_fixed_info *pip_info, bool is_bootloader)
90{
91 u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH];
92 int resp_len;
93 u16 product_family;
94 int error;
95
96 if (is_bootloader) {
97 /* Read Bootloader Information to determine Gen5 or Gen6. */
98 resp_len = sizeof(resp_data);
99 error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
100 pip_get_bl_info, sizeof(pip_get_bl_info),
101 resp_data, &resp_len,
102 2000, cyapa_sort_tsg_pip_bl_resp_data,
103 false);
104 if (error || resp_len < PIP_BL_GET_INFO_RESP_LENGTH)
105 return error ? error : -EIO;
106
107 pip_info->family_id = resp_data[8];
108 pip_info->silicon_id_low = resp_data[10];
109 pip_info->silicon_id_high = resp_data[11];
110
111 return 0;
112 }
113
114 /* Get App System Information to determine Gen5 or Gen6. */
115 resp_len = sizeof(resp_data);
116 error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
117 pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH,
118 resp_data, &resp_len,
119 2000, cyapa_pip_sort_system_info_data, false);
120 if (error || resp_len < PIP_READ_SYS_INFO_RESP_LENGTH)
121 return error ? error : -EIO;
122
123 product_family = get_unaligned_le16(&resp_data[7]);
124 if ((product_family & PIP_PRODUCT_FAMILY_MASK) !=
125 PIP_PRODUCT_FAMILY_TRACKPAD)
126 return -EINVAL;
127
128 pip_info->family_id = resp_data[19];
129 pip_info->silicon_id_low = resp_data[21];
130 pip_info->silicon_id_high = resp_data[22];
131
132 return 0;
133
134}
135
136int cyapa_pip_state_parse(struct cyapa *cyapa, u8 *reg_data, int len)
137{
138 u8 cmd[] = { 0x01, 0x00};
139 struct pip_fixed_info pip_info;
140 u8 resp_data[PIP_HID_DESCRIPTOR_SIZE];
141 int resp_len;
142 bool is_bootloader;
143 int error;
144
145 cyapa->state = CYAPA_STATE_NO_DEVICE;
146
147 /* Try to wake from it deep sleep state if it is. */
148 cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON);
149
150 /* Empty the buffer queue to get fresh data with later commands. */
151 cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
152
153 /*
154 * Read description info from trackpad device to determine running in
155 * APP mode or Bootloader mode.
156 */
157 resp_len = PIP_HID_DESCRIPTOR_SIZE;
158 error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
159 cmd, sizeof(cmd),
160 resp_data, &resp_len,
161 300,
162 cyapa_sort_pip_hid_descriptor_data,
163 false);
164 if (error)
165 return error;
166
167 if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID)
168 is_bootloader = true;
169 else if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID)
170 is_bootloader = false;
171 else
172 return -EAGAIN;
173
174 /* Get PIP fixed information to determine Gen5 or Gen6. */
175 memset(&pip_info, 0, sizeof(struct pip_fixed_info));
176 error = cyapa_get_pip_fixed_info(cyapa, &pip_info, is_bootloader);
177 if (error)
178 return error;
179
180 if (pip_info.family_id == 0x9B && pip_info.silicon_id_high == 0x0B) {
181 cyapa->gen = CYAPA_GEN6;
182 cyapa->state = is_bootloader ? CYAPA_STATE_GEN6_BL
183 : CYAPA_STATE_GEN6_APP;
184 } else if (pip_info.family_id == 0x91 &&
185 pip_info.silicon_id_high == 0x02) {
186 cyapa->gen = CYAPA_GEN5;
187 cyapa->state = is_bootloader ? CYAPA_STATE_GEN5_BL
188 : CYAPA_STATE_GEN5_APP;
189 }
190
191 return 0;
192}
193
194static int cyapa_gen6_read_sys_info(struct cyapa *cyapa)
195{
196 u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH];
197 int resp_len;
198 u16 product_family;
199 u8 rotat_align;
200 int error;
201
202 /* Get App System Information to determine Gen5 or Gen6. */
203 resp_len = sizeof(resp_data);
204 error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
205 pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH,
206 resp_data, &resp_len,
207 2000, cyapa_pip_sort_system_info_data, false);
208 if (error || resp_len < sizeof(resp_data))
209 return error ? error : -EIO;
210
211 product_family = get_unaligned_le16(&resp_data[7]);
212 if ((product_family & PIP_PRODUCT_FAMILY_MASK) !=
213 PIP_PRODUCT_FAMILY_TRACKPAD)
214 return -EINVAL;
215
216 cyapa->platform_ver = (resp_data[67] >> PIP_BL_PLATFORM_VER_SHIFT) &
217 PIP_BL_PLATFORM_VER_MASK;
218 cyapa->fw_maj_ver = resp_data[9];
219 cyapa->fw_min_ver = resp_data[10];
220
221 cyapa->electrodes_x = resp_data[33];
222 cyapa->electrodes_y = resp_data[34];
223
224 cyapa->physical_size_x = get_unaligned_le16(&resp_data[35]) / 100;
225 cyapa->physical_size_y = get_unaligned_le16(&resp_data[37]) / 100;
226
227 cyapa->max_abs_x = get_unaligned_le16(&resp_data[39]);
228 cyapa->max_abs_y = get_unaligned_le16(&resp_data[41]);
229
230 cyapa->max_z = get_unaligned_le16(&resp_data[43]);
231
232 cyapa->x_origin = resp_data[45] & 0x01;
233 cyapa->y_origin = resp_data[46] & 0x01;
234
235 cyapa->btn_capability = (resp_data[70] << 3) & CAPABILITY_BTN_MASK;
236
237 memcpy(&cyapa->product_id[0], &resp_data[51], 5);
238 cyapa->product_id[5] = '-';
239 memcpy(&cyapa->product_id[6], &resp_data[56], 6);
240 cyapa->product_id[12] = '-';
241 memcpy(&cyapa->product_id[13], &resp_data[62], 2);
242 cyapa->product_id[15] = '\0';
243
244 rotat_align = resp_data[68];
245 if (rotat_align) {
246 cyapa->electrodes_rx = cyapa->electrodes_y;
247 cyapa->electrodes_rx = cyapa->electrodes_y;
248 } else {
249 cyapa->electrodes_rx = cyapa->electrodes_x;
250 cyapa->electrodes_rx = cyapa->electrodes_y;
251 }
252 cyapa->aligned_electrodes_rx = (cyapa->electrodes_rx + 3) & ~3u;
253
254 if (!cyapa->electrodes_x || !cyapa->electrodes_y ||
255 !cyapa->physical_size_x || !cyapa->physical_size_y ||
256 !cyapa->max_abs_x || !cyapa->max_abs_y || !cyapa->max_z)
257 return -EINVAL;
258
259 return 0;
260}
261
262static int cyapa_gen6_bl_read_app_info(struct cyapa *cyapa)
263{
264 u8 resp_data[PIP_BL_APP_INFO_RESP_LENGTH];
265 int resp_len;
266 int error;
267
268 resp_len = sizeof(resp_data);
269 error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
270 pip_bl_read_app_info, PIP_BL_READ_APP_INFO_CMD_LENGTH,
271 resp_data, &resp_len,
272 500, cyapa_sort_tsg_pip_bl_resp_data, false);
273 if (error || resp_len < PIP_BL_APP_INFO_RESP_LENGTH ||
274 !PIP_CMD_COMPLETE_SUCCESS(resp_data))
275 return error ? error : -EIO;
276
277 cyapa->fw_maj_ver = resp_data[8];
278 cyapa->fw_min_ver = resp_data[9];
279
280 cyapa->platform_ver = (resp_data[12] >> PIP_BL_PLATFORM_VER_SHIFT) &
281 PIP_BL_PLATFORM_VER_MASK;
282
283 memcpy(&cyapa->product_id[0], &resp_data[13], 5);
284 cyapa->product_id[5] = '-';
285 memcpy(&cyapa->product_id[6], &resp_data[18], 6);
286 cyapa->product_id[12] = '-';
287 memcpy(&cyapa->product_id[13], &resp_data[24], 2);
288 cyapa->product_id[15] = '\0';
289
290 return 0;
291
292}
293
294static int cyapa_gen6_config_dev_irq(struct cyapa *cyapa, u8 cmd_code)
295{
296 u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, cmd_code };
297 u8 resp_data[6];
298 int resp_len;
299 int error;
300
301 resp_len = sizeof(resp_data);
302 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
303 resp_data, &resp_len,
304 500, cyapa_sort_tsg_pip_app_resp_data, false);
305 if (error || !VALID_CMD_RESP_HEADER(resp_data, cmd_code) ||
306 !PIP_CMD_COMPLETE_SUCCESS(resp_data)
307 )
308 return error < 0 ? error : -EINVAL;
309
310 return 0;
311}
312
945525ee
DD
313static int cyapa_gen6_set_proximity(struct cyapa *cyapa, bool enable)
314{
315 int error;
316
317 cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
318 error = cyapa_pip_set_proximity(cyapa, enable);
319 cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
320
321 return error;
322}
323
c2c06c41
DD
324static int cyapa_gen6_change_power_state(struct cyapa *cyapa, u8 power_mode)
325{
326 u8 cmd[] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x46, power_mode };
327 u8 resp_data[6];
328 int resp_len;
329 int error;
330
331 resp_len = sizeof(resp_data);
332 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
333 resp_data, &resp_len,
334 500, cyapa_sort_tsg_pip_app_resp_data, false);
335 if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x46))
336 return error < 0 ? error : -EINVAL;
337
338 /* New power state applied in device not match the set power state. */
339 if (resp_data[5] != power_mode)
340 return -EAGAIN;
341
342 return 0;
343}
344
345static int cyapa_gen6_set_interval_setting(struct cyapa *cyapa,
346 struct gen6_interval_setting *interval_setting)
347{
348 struct gen6_set_interval_cmd {
349 __le16 addr;
350 __le16 length;
351 u8 report_id;
352 u8 rsvd; /* Reserved, must be 0 */
353 u8 cmd_code;
354 __le16 active_interval;
355 __le16 lp1_interval;
356 __le16 lp2_interval;
357 } __packed set_interval_cmd;
358 u8 resp_data[11];
359 int resp_len;
360 int error;
361
362 memset(&set_interval_cmd, 0, sizeof(set_interval_cmd));
363 put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &set_interval_cmd.addr);
364 put_unaligned_le16(sizeof(set_interval_cmd) - 2,
365 &set_interval_cmd.length);
366 set_interval_cmd.report_id = PIP_APP_CMD_REPORT_ID;
367 set_interval_cmd.cmd_code = GEN6_SET_POWER_MODE_INTERVAL;
368 put_unaligned_le16(interval_setting->active_interval,
369 &set_interval_cmd.active_interval);
370 put_unaligned_le16(interval_setting->lp1_interval,
371 &set_interval_cmd.lp1_interval);
372 put_unaligned_le16(interval_setting->lp2_interval,
373 &set_interval_cmd.lp2_interval);
374
375 resp_len = sizeof(resp_data);
376 error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
377 (u8 *)&set_interval_cmd, sizeof(set_interval_cmd),
378 resp_data, &resp_len,
379 500, cyapa_sort_tsg_pip_app_resp_data, false);
380 if (error ||
381 !VALID_CMD_RESP_HEADER(resp_data, GEN6_SET_POWER_MODE_INTERVAL))
382 return error < 0 ? error : -EINVAL;
383
384 /* Get the real set intervals from response. */
385 interval_setting->active_interval = get_unaligned_le16(&resp_data[5]);
386 interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]);
387 interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]);
388
389 return 0;
390}
391
392static int cyapa_gen6_get_interval_setting(struct cyapa *cyapa,
393 struct gen6_interval_setting *interval_setting)
394{
395 u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00,
396 GEN6_GET_POWER_MODE_INTERVAL };
397 u8 resp_data[11];
398 int resp_len;
399 int error;
400
401 resp_len = sizeof(resp_data);
402 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
403 resp_data, &resp_len,
404 500, cyapa_sort_tsg_pip_app_resp_data, false);
405 if (error ||
406 !VALID_CMD_RESP_HEADER(resp_data, GEN6_GET_POWER_MODE_INTERVAL))
407 return error < 0 ? error : -EINVAL;
408
409 interval_setting->active_interval = get_unaligned_le16(&resp_data[5]);
410 interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]);
411 interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]);
412
413 return 0;
414}
415
416static int cyapa_gen6_deep_sleep(struct cyapa *cyapa, u8 state)
417{
418 u8 ping[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x00 };
419
420 if (state == PIP_DEEP_SLEEP_STATE_ON)
421 /*
422 * Send ping command to notify device prepare for wake up
423 * when it's in deep sleep mode. At this time, device will
424 * response nothing except an I2C NAK.
425 */
426 cyapa_i2c_pip_write(cyapa, ping, sizeof(ping));
427
428 return cyapa_pip_deep_sleep(cyapa, state);
429}
430
431static int cyapa_gen6_set_power_mode(struct cyapa *cyapa,
757cae5a 432 u8 power_mode, u16 sleep_time, bool is_suspend)
c2c06c41
DD
433{
434 struct device *dev = &cyapa->client->dev;
435 struct gen6_interval_setting *interval_setting =
436 &cyapa->gen6_interval_setting;
437 u8 lp_mode;
438 int error;
439
440 if (cyapa->state != CYAPA_STATE_GEN6_APP)
441 return 0;
442
443 if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) {
444 /*
445 * Assume TP in deep sleep mode when driver is loaded,
446 * avoid driver unload and reload command IO issue caused by TP
447 * has been set into deep sleep mode when unloading.
448 */
449 PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
450 }
451
452 if (PIP_DEV_UNINIT_SLEEP_TIME(cyapa) &&
453 PIP_DEV_GET_PWR_STATE(cyapa) != PWR_MODE_OFF)
454 PIP_DEV_SET_SLEEP_TIME(cyapa, UNINIT_SLEEP_TIME);
455
456 if (PIP_DEV_GET_PWR_STATE(cyapa) == power_mode) {
457 if (power_mode == PWR_MODE_OFF ||
458 power_mode == PWR_MODE_FULL_ACTIVE ||
459 power_mode == PWR_MODE_BTN_ONLY ||
460 PIP_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) {
461 /* Has in correct power mode state, early return. */
462 return 0;
463 }
464 }
465
466 if (power_mode == PWR_MODE_OFF) {
467 cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
468
469 error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF);
470 if (error) {
471 dev_err(dev, "enter deep sleep fail: %d\n", error);
472 return error;
473 }
474
475 PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
476 return 0;
477 }
478
479 /*
480 * When trackpad in power off mode, it cannot change to other power
481 * state directly, must be wake up from sleep firstly, then
482 * continue to do next power sate change.
483 */
484 if (PIP_DEV_GET_PWR_STATE(cyapa) == PWR_MODE_OFF) {
485 error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON);
486 if (error) {
487 dev_err(dev, "deep sleep wake fail: %d\n", error);
488 return error;
489 }
490 }
491
492 /*
493 * Disable device assert interrupts for command response to avoid
494 * disturbing system suspending or hibernating process.
495 */
496 cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
497
498 if (power_mode == PWR_MODE_FULL_ACTIVE) {
499 error = cyapa_gen6_change_power_state(cyapa,
500 GEN6_POWER_MODE_ACTIVE);
501 if (error) {
502 dev_err(dev, "change to active fail: %d\n", error);
503 goto out;
504 }
505
506 PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE);
507
508 /* Sync the interval setting from device. */
509 cyapa_gen6_get_interval_setting(cyapa, interval_setting);
510
511 } else if (power_mode == PWR_MODE_BTN_ONLY) {
512 error = cyapa_gen6_change_power_state(cyapa,
513 GEN6_POWER_MODE_BTN_ONLY);
514 if (error) {
515 dev_err(dev, "fail to button only mode: %d\n", error);
516 goto out;
517 }
518
519 PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY);
520 } else {
521 /*
522 * Gen6 internally supports to 2 low power scan interval time,
523 * so can help to switch power mode quickly.
524 * such as runtime suspend and system suspend.
525 */
526 if (interval_setting->lp1_interval == sleep_time) {
527 lp_mode = GEN6_POWER_MODE_LP_MODE1;
528 } else if (interval_setting->lp2_interval == sleep_time) {
529 lp_mode = GEN6_POWER_MODE_LP_MODE2;
530 } else {
531 if (interval_setting->lp1_interval == 0) {
532 interval_setting->lp1_interval = sleep_time;
533 lp_mode = GEN6_POWER_MODE_LP_MODE1;
534 } else {
535 interval_setting->lp2_interval = sleep_time;
536 lp_mode = GEN6_POWER_MODE_LP_MODE2;
537 }
538 cyapa_gen6_set_interval_setting(cyapa,
539 interval_setting);
540 }
541
542 error = cyapa_gen6_change_power_state(cyapa, lp_mode);
543 if (error) {
544 dev_err(dev, "set power state to 0x%02x failed: %d\n",
545 lp_mode, error);
546 goto out;
547 }
548
549 PIP_DEV_SET_SLEEP_TIME(cyapa, sleep_time);
550 PIP_DEV_SET_PWR_STATE(cyapa,
551 cyapa_sleep_time_to_pwr_cmd(sleep_time));
552 }
553
554out:
555 cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
556 return error;
557}
558
559static int cyapa_gen6_initialize(struct cyapa *cyapa)
560{
561 return 0;
562}
563
564static int cyapa_pip_retrieve_data_structure(struct cyapa *cyapa,
565 u16 read_offset, u16 read_len, u8 data_id,
566 u8 *data, int *data_buf_lens)
567{
568 struct retrieve_data_struct_cmd {
569 struct pip_app_cmd_head head;
570 __le16 read_offset;
571 __le16 read_length;
572 u8 data_id;
573 } __packed cmd;
574 u8 resp_data[GEN6_MAX_RX_NUM + 10];
575 int resp_len;
576 int error;
577
578 memset(&cmd, 0, sizeof(cmd));
579 put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &cmd.head.addr);
580 put_unaligned_le16(sizeof(cmd), &cmd.head.length - 2);
581 cmd.head.report_id = PIP_APP_CMD_REPORT_ID;
582 cmd.head.cmd_code = PIP_RETRIEVE_DATA_STRUCTURE;
583 put_unaligned_le16(read_offset, &cmd.read_offset);
584 put_unaligned_le16(read_len, &cmd.read_length);
585 cmd.data_id = data_id;
586
587 resp_len = sizeof(resp_data);
588 error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
589 (u8 *)&cmd, sizeof(cmd),
590 resp_data, &resp_len,
591 500, cyapa_sort_tsg_pip_app_resp_data,
592 true);
593 if (error || !PIP_CMD_COMPLETE_SUCCESS(resp_data) ||
594 resp_data[6] != data_id ||
595 !VALID_CMD_RESP_HEADER(resp_data, PIP_RETRIEVE_DATA_STRUCTURE))
596 return (error < 0) ? error : -EAGAIN;
597
598 read_len = get_unaligned_le16(&resp_data[7]);
599 if (*data_buf_lens < read_len) {
600 *data_buf_lens = read_len;
601 return -ENOBUFS;
602 }
603
604 memcpy(data, &resp_data[10], read_len);
605 *data_buf_lens = read_len;
606 return 0;
607}
608
609static ssize_t cyapa_gen6_show_baseline(struct device *dev,
610 struct device_attribute *attr, char *buf)
611{
612 struct cyapa *cyapa = dev_get_drvdata(dev);
613 u8 data[GEN6_MAX_RX_NUM];
614 int data_len;
615 int size = 0;
616 int i;
617 int error;
618 int resume_error;
619
620 if (!cyapa_is_pip_app_mode(cyapa))
621 return -EBUSY;
622
623 /* 1. Suspend Scanning*/
624 error = cyapa_pip_suspend_scanning(cyapa);
625 if (error)
626 return error;
627
628 /* 2. IDAC and RX Attenuator Calibration Data (Center Frequency). */
629 data_len = sizeof(data);
630 error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len,
631 GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC,
632 data, &data_len);
633 if (error)
634 goto resume_scanning;
635
636 size = scnprintf(buf, PAGE_SIZE, "%d %d %d %d %d %d ",
637 data[0], /* RX Attenuator Mutual */
638 data[1], /* IDAC Mutual */
639 data[2], /* RX Attenuator Self RX */
640 data[3], /* IDAC Self RX */
641 data[4], /* RX Attenuator Self TX */
642 data[5] /* IDAC Self TX */
643 );
644
645 /* 3. Read Attenuator Trim. */
646 data_len = sizeof(data);
647 error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len,
648 GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM,
649 data, &data_len);
650 if (error)
651 goto resume_scanning;
652
653 /* set attenuator trim values. */
654 for (i = 0; i < data_len; i++)
655 size += scnprintf(buf + size, PAGE_SIZE - size, "%d ", data[i]);
656 size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
657
658resume_scanning:
659 /* 4. Resume Scanning*/
660 resume_error = cyapa_pip_resume_scanning(cyapa);
661 if (resume_error || error) {
662 memset(buf, 0, PAGE_SIZE);
663 return resume_error ? resume_error : error;
664 }
665
666 return size;
667}
668
669static int cyapa_gen6_operational_check(struct cyapa *cyapa)
670{
671 struct device *dev = &cyapa->client->dev;
672 int error;
673
674 if (cyapa->gen != CYAPA_GEN6)
675 return -ENODEV;
676
677 switch (cyapa->state) {
678 case CYAPA_STATE_GEN6_BL:
679 error = cyapa_pip_bl_exit(cyapa);
680 if (error) {
681 /* Try to update trackpad product information. */
682 cyapa_gen6_bl_read_app_info(cyapa);
683 goto out;
684 }
685
686 cyapa->state = CYAPA_STATE_GEN6_APP;
687
688 case CYAPA_STATE_GEN6_APP:
689 /*
690 * If trackpad device in deep sleep mode,
691 * the app command will fail.
692 * So always try to reset trackpad device to full active when
693 * the device state is required.
694 */
695 error = cyapa_gen6_set_power_mode(cyapa,
757cae5a 696 PWR_MODE_FULL_ACTIVE, 0, false);
c2c06c41
DD
697 if (error)
698 dev_warn(dev, "%s: failed to set power active mode.\n",
699 __func__);
700
945525ee
DD
701 /* By default, the trackpad proximity function is enabled. */
702 error = cyapa_pip_set_proximity(cyapa, true);
703 if (error)
704 dev_warn(dev, "%s: failed to enable proximity.\n",
705 __func__);
706
c2c06c41
DD
707 /* Get trackpad product information. */
708 error = cyapa_gen6_read_sys_info(cyapa);
709 if (error)
710 goto out;
711 /* Only support product ID starting with CYTRA */
712 if (memcmp(cyapa->product_id, product_id,
713 strlen(product_id)) != 0) {
714 dev_err(dev, "%s: unknown product ID (%s)\n",
715 __func__, cyapa->product_id);
716 error = -EINVAL;
717 }
718 break;
719 default:
720 error = -EINVAL;
721 }
722
723out:
724 return error;
725}
726
727const struct cyapa_dev_ops cyapa_gen6_ops = {
728 .check_fw = cyapa_pip_check_fw,
729 .bl_enter = cyapa_pip_bl_enter,
730 .bl_initiate = cyapa_pip_bl_initiate,
731 .update_fw = cyapa_pip_do_fw_update,
732 .bl_activate = cyapa_pip_bl_activate,
733 .bl_deactivate = cyapa_pip_bl_deactivate,
734
735 .show_baseline = cyapa_gen6_show_baseline,
736 .calibrate_store = cyapa_pip_do_calibrate,
737
738 .initialize = cyapa_gen6_initialize,
739
740 .state_parse = cyapa_pip_state_parse,
741 .operational_check = cyapa_gen6_operational_check,
742
743 .irq_handler = cyapa_pip_irq_handler,
744 .irq_cmd_handler = cyapa_pip_irq_cmd_handler,
745 .sort_empty_output_data = cyapa_empty_pip_output_data,
746 .set_power_mode = cyapa_gen6_set_power_mode,
945525ee
DD
747
748 .set_proximity = cyapa_gen6_set_proximity,
c2c06c41 749};
This page took 0.053157 seconds and 5 git commands to generate.