]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/input/mouse/cyapa_gen6.c
Merge tag 'mac80211-for-davem-2015-09-22' of git://git.kernel.org/pub/scm/linux/kerne...
[karo-tx-linux.git] / drivers / input / mouse / cyapa_gen6.c
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
43 struct 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
51 struct 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
64 struct pip_fixed_info {
65         u8 silicon_id_high;
66         u8 silicon_id_low;
67         u8 family_id;
68 };
69
70 static u8 pip_get_bl_info[] = {
71         0x04, 0x00, 0x0B, 0x00, 0x40, 0x00, 0x01, 0x38,
72         0x00, 0x00, 0x70, 0x9E, 0x17
73 };
74
75 static 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
88 static 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
136 int 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
194 static 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
262 static 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
294 static 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
313 static 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
324 static 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
345 static 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
392 static 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
416 static 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
431 static int cyapa_gen6_set_power_mode(struct cyapa *cyapa,
432                 u8 power_mode, u16 sleep_time, bool is_suspend)
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
554 out:
555         cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
556         return error;
557 }
558
559 static int cyapa_gen6_initialize(struct cyapa *cyapa)
560 {
561         return 0;
562 }
563
564 static 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
609 static 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
658 resume_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
669 static 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,
696                                 PWR_MODE_FULL_ACTIVE, 0, false);
697                 if (error)
698                         dev_warn(dev, "%s: failed to set power active mode.\n",
699                                 __func__);
700
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
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
723 out:
724         return error;
725 }
726
727 const 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,
747
748         .set_proximity = cyapa_gen6_set_proximity,
749 };