]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/input/mouse/cyapa_gen6.c
Merge branch 'sched-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[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         /* Get the number of Rx electrodes. */
245         rotat_align = resp_data[68];
246         cyapa->electrodes_rx =
247                 rotat_align ? cyapa->electrodes_y : cyapa->electrodes_x;
248         cyapa->aligned_electrodes_rx = (cyapa->electrodes_rx + 3) & ~3u;
249
250         if (!cyapa->electrodes_x || !cyapa->electrodes_y ||
251                 !cyapa->physical_size_x || !cyapa->physical_size_y ||
252                 !cyapa->max_abs_x || !cyapa->max_abs_y || !cyapa->max_z)
253                 return -EINVAL;
254
255         return 0;
256 }
257
258 static int cyapa_gen6_bl_read_app_info(struct cyapa *cyapa)
259 {
260         u8 resp_data[PIP_BL_APP_INFO_RESP_LENGTH];
261         int resp_len;
262         int error;
263
264         resp_len = sizeof(resp_data);
265         error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
266                         pip_bl_read_app_info, PIP_BL_READ_APP_INFO_CMD_LENGTH,
267                         resp_data, &resp_len,
268                         500, cyapa_sort_tsg_pip_bl_resp_data, false);
269         if (error || resp_len < PIP_BL_APP_INFO_RESP_LENGTH ||
270                 !PIP_CMD_COMPLETE_SUCCESS(resp_data))
271                 return error ? error : -EIO;
272
273         cyapa->fw_maj_ver = resp_data[8];
274         cyapa->fw_min_ver = resp_data[9];
275
276         cyapa->platform_ver = (resp_data[12] >> PIP_BL_PLATFORM_VER_SHIFT) &
277                               PIP_BL_PLATFORM_VER_MASK;
278
279         memcpy(&cyapa->product_id[0], &resp_data[13], 5);
280         cyapa->product_id[5] = '-';
281         memcpy(&cyapa->product_id[6], &resp_data[18], 6);
282         cyapa->product_id[12] = '-';
283         memcpy(&cyapa->product_id[13], &resp_data[24], 2);
284         cyapa->product_id[15] = '\0';
285
286         return 0;
287
288 }
289
290 static int cyapa_gen6_config_dev_irq(struct cyapa *cyapa, u8 cmd_code)
291 {
292         u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, cmd_code };
293         u8 resp_data[6];
294         int resp_len;
295         int error;
296
297         resp_len = sizeof(resp_data);
298         error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
299                         resp_data, &resp_len,
300                         500, cyapa_sort_tsg_pip_app_resp_data, false);
301         if (error || !VALID_CMD_RESP_HEADER(resp_data, cmd_code) ||
302                         !PIP_CMD_COMPLETE_SUCCESS(resp_data)
303                         )
304                 return error < 0 ? error : -EINVAL;
305
306         return 0;
307 }
308
309 static int cyapa_gen6_set_proximity(struct cyapa *cyapa, bool enable)
310 {
311         int error;
312
313         cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
314         error = cyapa_pip_set_proximity(cyapa, enable);
315         cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
316
317         return error;
318 }
319
320 static int cyapa_gen6_change_power_state(struct cyapa *cyapa, u8 power_mode)
321 {
322         u8 cmd[] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x46, power_mode };
323         u8 resp_data[6];
324         int resp_len;
325         int error;
326
327         resp_len = sizeof(resp_data);
328         error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
329                         resp_data, &resp_len,
330                         500, cyapa_sort_tsg_pip_app_resp_data, false);
331         if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x46))
332                 return error < 0 ? error : -EINVAL;
333
334         /* New power state applied in device not match the set power state. */
335         if (resp_data[5] != power_mode)
336                 return -EAGAIN;
337
338         return 0;
339 }
340
341 static int cyapa_gen6_set_interval_setting(struct cyapa *cyapa,
342                 struct gen6_interval_setting *interval_setting)
343 {
344         struct gen6_set_interval_cmd {
345                 __le16 addr;
346                 __le16 length;
347                 u8 report_id;
348                 u8 rsvd;  /* Reserved, must be 0 */
349                 u8 cmd_code;
350                 __le16 active_interval;
351                 __le16 lp1_interval;
352                 __le16 lp2_interval;
353         } __packed set_interval_cmd;
354         u8 resp_data[11];
355         int resp_len;
356         int error;
357
358         memset(&set_interval_cmd, 0, sizeof(set_interval_cmd));
359         put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &set_interval_cmd.addr);
360         put_unaligned_le16(sizeof(set_interval_cmd) - 2,
361                            &set_interval_cmd.length);
362         set_interval_cmd.report_id = PIP_APP_CMD_REPORT_ID;
363         set_interval_cmd.cmd_code = GEN6_SET_POWER_MODE_INTERVAL;
364         put_unaligned_le16(interval_setting->active_interval,
365                            &set_interval_cmd.active_interval);
366         put_unaligned_le16(interval_setting->lp1_interval,
367                            &set_interval_cmd.lp1_interval);
368         put_unaligned_le16(interval_setting->lp2_interval,
369                            &set_interval_cmd.lp2_interval);
370
371         resp_len = sizeof(resp_data);
372         error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
373                         (u8 *)&set_interval_cmd, sizeof(set_interval_cmd),
374                         resp_data, &resp_len,
375                         500, cyapa_sort_tsg_pip_app_resp_data, false);
376         if (error ||
377                 !VALID_CMD_RESP_HEADER(resp_data, GEN6_SET_POWER_MODE_INTERVAL))
378                 return error < 0 ? error : -EINVAL;
379
380         /* Get the real set intervals from response. */
381         interval_setting->active_interval = get_unaligned_le16(&resp_data[5]);
382         interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]);
383         interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]);
384
385         return 0;
386 }
387
388 static int cyapa_gen6_get_interval_setting(struct cyapa *cyapa,
389                 struct gen6_interval_setting *interval_setting)
390 {
391         u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00,
392                      GEN6_GET_POWER_MODE_INTERVAL };
393         u8 resp_data[11];
394         int resp_len;
395         int error;
396
397         resp_len = sizeof(resp_data);
398         error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
399                         resp_data, &resp_len,
400                         500, cyapa_sort_tsg_pip_app_resp_data, false);
401         if (error ||
402                 !VALID_CMD_RESP_HEADER(resp_data, GEN6_GET_POWER_MODE_INTERVAL))
403                 return error < 0 ? error : -EINVAL;
404
405         interval_setting->active_interval = get_unaligned_le16(&resp_data[5]);
406         interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]);
407         interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]);
408
409         return 0;
410 }
411
412 static int cyapa_gen6_deep_sleep(struct cyapa *cyapa, u8 state)
413 {
414         u8 ping[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x00 };
415
416         if (state == PIP_DEEP_SLEEP_STATE_ON)
417                 /*
418                  * Send ping command to notify device prepare for wake up
419                  * when it's in deep sleep mode. At this time, device will
420                  * response nothing except an I2C NAK.
421                  */
422                 cyapa_i2c_pip_write(cyapa, ping, sizeof(ping));
423
424         return cyapa_pip_deep_sleep(cyapa, state);
425 }
426
427 static int cyapa_gen6_set_power_mode(struct cyapa *cyapa,
428                 u8 power_mode, u16 sleep_time, bool is_suspend)
429 {
430         struct device *dev = &cyapa->client->dev;
431         struct gen6_interval_setting *interval_setting =
432                         &cyapa->gen6_interval_setting;
433         u8 lp_mode;
434         int error;
435
436         if (cyapa->state != CYAPA_STATE_GEN6_APP)
437                 return 0;
438
439         if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) {
440                 /*
441                  * Assume TP in deep sleep mode when driver is loaded,
442                  * avoid driver unload and reload command IO issue caused by TP
443                  * has been set into deep sleep mode when unloading.
444                  */
445                 PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
446         }
447
448         if (PIP_DEV_UNINIT_SLEEP_TIME(cyapa) &&
449                 PIP_DEV_GET_PWR_STATE(cyapa) != PWR_MODE_OFF)
450                 PIP_DEV_SET_SLEEP_TIME(cyapa, UNINIT_SLEEP_TIME);
451
452         if (PIP_DEV_GET_PWR_STATE(cyapa) == power_mode) {
453                 if (power_mode == PWR_MODE_OFF ||
454                         power_mode == PWR_MODE_FULL_ACTIVE ||
455                         power_mode == PWR_MODE_BTN_ONLY ||
456                         PIP_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) {
457                         /* Has in correct power mode state, early return. */
458                         return 0;
459                 }
460         }
461
462         if (power_mode == PWR_MODE_OFF) {
463                 cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
464
465                 error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF);
466                 if (error) {
467                         dev_err(dev, "enter deep sleep fail: %d\n", error);
468                         return error;
469                 }
470
471                 PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
472                 return 0;
473         }
474
475         /*
476          * When trackpad in power off mode, it cannot change to other power
477          * state directly, must be wake up from sleep firstly, then
478          * continue to do next power sate change.
479          */
480         if (PIP_DEV_GET_PWR_STATE(cyapa) == PWR_MODE_OFF) {
481                 error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON);
482                 if (error) {
483                         dev_err(dev, "deep sleep wake fail: %d\n", error);
484                         return error;
485                 }
486         }
487
488         /*
489          * Disable device assert interrupts for command response to avoid
490          * disturbing system suspending or hibernating process.
491          */
492         cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
493
494         if (power_mode == PWR_MODE_FULL_ACTIVE) {
495                 error = cyapa_gen6_change_power_state(cyapa,
496                                 GEN6_POWER_MODE_ACTIVE);
497                 if (error) {
498                         dev_err(dev, "change to active fail: %d\n", error);
499                         goto out;
500                 }
501
502                 PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE);
503
504                 /* Sync the interval setting from device. */
505                 cyapa_gen6_get_interval_setting(cyapa, interval_setting);
506
507         } else if (power_mode == PWR_MODE_BTN_ONLY) {
508                 error = cyapa_gen6_change_power_state(cyapa,
509                                 GEN6_POWER_MODE_BTN_ONLY);
510                 if (error) {
511                         dev_err(dev, "fail to button only mode: %d\n", error);
512                         goto out;
513                 }
514
515                 PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY);
516         } else {
517                 /*
518                  * Gen6 internally supports to 2 low power scan interval time,
519                  * so can help to switch power mode quickly.
520                  * such as runtime suspend and system suspend.
521                  */
522                 if (interval_setting->lp1_interval == sleep_time) {
523                         lp_mode = GEN6_POWER_MODE_LP_MODE1;
524                 } else if (interval_setting->lp2_interval == sleep_time) {
525                         lp_mode = GEN6_POWER_MODE_LP_MODE2;
526                 } else {
527                         if (interval_setting->lp1_interval == 0) {
528                                 interval_setting->lp1_interval = sleep_time;
529                                 lp_mode = GEN6_POWER_MODE_LP_MODE1;
530                         } else {
531                                 interval_setting->lp2_interval = sleep_time;
532                                 lp_mode = GEN6_POWER_MODE_LP_MODE2;
533                         }
534                         cyapa_gen6_set_interval_setting(cyapa,
535                                                         interval_setting);
536                 }
537
538                 error = cyapa_gen6_change_power_state(cyapa, lp_mode);
539                 if (error) {
540                         dev_err(dev, "set power state to 0x%02x failed: %d\n",
541                                 lp_mode, error);
542                         goto out;
543                 }
544
545                 PIP_DEV_SET_SLEEP_TIME(cyapa, sleep_time);
546                 PIP_DEV_SET_PWR_STATE(cyapa,
547                         cyapa_sleep_time_to_pwr_cmd(sleep_time));
548         }
549
550 out:
551         cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
552         return error;
553 }
554
555 static int cyapa_gen6_initialize(struct cyapa *cyapa)
556 {
557         return 0;
558 }
559
560 static int cyapa_pip_retrieve_data_structure(struct cyapa *cyapa,
561                 u16 read_offset, u16 read_len, u8 data_id,
562                 u8 *data, int *data_buf_lens)
563 {
564         struct retrieve_data_struct_cmd {
565                 struct pip_app_cmd_head head;
566                 __le16 read_offset;
567                 __le16 read_length;
568                 u8 data_id;
569         } __packed cmd;
570         u8 resp_data[GEN6_MAX_RX_NUM + 10];
571         int resp_len;
572         int error;
573
574         memset(&cmd, 0, sizeof(cmd));
575         put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &cmd.head.addr);
576         put_unaligned_le16(sizeof(cmd), &cmd.head.length - 2);
577         cmd.head.report_id = PIP_APP_CMD_REPORT_ID;
578         cmd.head.cmd_code = PIP_RETRIEVE_DATA_STRUCTURE;
579         put_unaligned_le16(read_offset, &cmd.read_offset);
580         put_unaligned_le16(read_len, &cmd.read_length);
581         cmd.data_id = data_id;
582
583         resp_len = sizeof(resp_data);
584         error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
585                                 (u8 *)&cmd, sizeof(cmd),
586                                 resp_data, &resp_len,
587                                 500, cyapa_sort_tsg_pip_app_resp_data,
588                                 true);
589         if (error || !PIP_CMD_COMPLETE_SUCCESS(resp_data) ||
590                 resp_data[6] != data_id ||
591                 !VALID_CMD_RESP_HEADER(resp_data, PIP_RETRIEVE_DATA_STRUCTURE))
592                 return (error < 0) ? error : -EAGAIN;
593
594         read_len = get_unaligned_le16(&resp_data[7]);
595         if (*data_buf_lens < read_len) {
596                 *data_buf_lens = read_len;
597                 return -ENOBUFS;
598         }
599
600         memcpy(data, &resp_data[10], read_len);
601         *data_buf_lens = read_len;
602         return 0;
603 }
604
605 static ssize_t cyapa_gen6_show_baseline(struct device *dev,
606                 struct device_attribute *attr, char *buf)
607 {
608         struct cyapa *cyapa = dev_get_drvdata(dev);
609         u8 data[GEN6_MAX_RX_NUM];
610         int data_len;
611         int size = 0;
612         int i;
613         int error;
614         int resume_error;
615
616         if (!cyapa_is_pip_app_mode(cyapa))
617                 return -EBUSY;
618
619         /* 1. Suspend Scanning*/
620         error = cyapa_pip_suspend_scanning(cyapa);
621         if (error)
622                 return error;
623
624         /* 2. IDAC and RX Attenuator Calibration Data (Center Frequency). */
625         data_len = sizeof(data);
626         error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len,
627                         GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC,
628                         data, &data_len);
629         if (error)
630                 goto resume_scanning;
631
632         size = scnprintf(buf, PAGE_SIZE, "%d %d %d %d %d %d ",
633                         data[0],  /* RX Attenuator Mutual */
634                         data[1],  /* IDAC Mutual */
635                         data[2],  /* RX Attenuator Self RX */
636                         data[3],  /* IDAC Self RX */
637                         data[4],  /* RX Attenuator Self TX */
638                         data[5]   /* IDAC Self TX */
639                         );
640
641         /* 3. Read Attenuator Trim. */
642         data_len = sizeof(data);
643         error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len,
644                         GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM,
645                         data, &data_len);
646         if (error)
647                 goto resume_scanning;
648
649         /* set attenuator trim values. */
650         for (i = 0; i < data_len; i++)
651                 size += scnprintf(buf + size, PAGE_SIZE - size, "%d ", data[i]);
652         size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
653
654 resume_scanning:
655         /* 4. Resume Scanning*/
656         resume_error = cyapa_pip_resume_scanning(cyapa);
657         if (resume_error || error) {
658                 memset(buf, 0, PAGE_SIZE);
659                 return resume_error ? resume_error : error;
660         }
661
662         return size;
663 }
664
665 static int cyapa_gen6_operational_check(struct cyapa *cyapa)
666 {
667         struct device *dev = &cyapa->client->dev;
668         int error;
669
670         if (cyapa->gen != CYAPA_GEN6)
671                 return -ENODEV;
672
673         switch (cyapa->state) {
674         case CYAPA_STATE_GEN6_BL:
675                 error = cyapa_pip_bl_exit(cyapa);
676                 if (error) {
677                         /* Try to update trackpad product information. */
678                         cyapa_gen6_bl_read_app_info(cyapa);
679                         goto out;
680                 }
681
682                 cyapa->state = CYAPA_STATE_GEN6_APP;
683
684         case CYAPA_STATE_GEN6_APP:
685                 /*
686                  * If trackpad device in deep sleep mode,
687                  * the app command will fail.
688                  * So always try to reset trackpad device to full active when
689                  * the device state is required.
690                  */
691                 error = cyapa_gen6_set_power_mode(cyapa,
692                                 PWR_MODE_FULL_ACTIVE, 0, false);
693                 if (error)
694                         dev_warn(dev, "%s: failed to set power active mode.\n",
695                                 __func__);
696
697                 /* By default, the trackpad proximity function is enabled. */
698                 error = cyapa_pip_set_proximity(cyapa, true);
699                 if (error)
700                         dev_warn(dev, "%s: failed to enable proximity.\n",
701                                 __func__);
702
703                 /* Get trackpad product information. */
704                 error = cyapa_gen6_read_sys_info(cyapa);
705                 if (error)
706                         goto out;
707                 /* Only support product ID starting with CYTRA */
708                 if (memcmp(cyapa->product_id, product_id,
709                                 strlen(product_id)) != 0) {
710                         dev_err(dev, "%s: unknown product ID (%s)\n",
711                                 __func__, cyapa->product_id);
712                         error = -EINVAL;
713                 }
714                 break;
715         default:
716                 error = -EINVAL;
717         }
718
719 out:
720         return error;
721 }
722
723 const struct cyapa_dev_ops cyapa_gen6_ops = {
724         .check_fw = cyapa_pip_check_fw,
725         .bl_enter = cyapa_pip_bl_enter,
726         .bl_initiate = cyapa_pip_bl_initiate,
727         .update_fw = cyapa_pip_do_fw_update,
728         .bl_activate = cyapa_pip_bl_activate,
729         .bl_deactivate = cyapa_pip_bl_deactivate,
730
731         .show_baseline = cyapa_gen6_show_baseline,
732         .calibrate_store = cyapa_pip_do_calibrate,
733
734         .initialize = cyapa_gen6_initialize,
735
736         .state_parse = cyapa_pip_state_parse,
737         .operational_check = cyapa_gen6_operational_check,
738
739         .irq_handler = cyapa_pip_irq_handler,
740         .irq_cmd_handler = cyapa_pip_irq_cmd_handler,
741         .sort_empty_output_data = cyapa_empty_pip_output_data,
742         .set_power_mode = cyapa_gen6_set_power_mode,
743
744         .set_proximity = cyapa_gen6_set_proximity,
745 };