Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Cypress APA trackpad with I2C interface
0003  *
0004  * Author: Dudley Du <dudl@cypress.com>
0005  *
0006  * Copyright (C) 2015 Cypress Semiconductor, Inc.
0007  *
0008  * This file is subject to the terms and conditions of the GNU General Public
0009  * License.  See the file COPYING in the main directory of this archive for
0010  * more details.
0011  */
0012 
0013 #include <linux/delay.h>
0014 #include <linux/i2c.h>
0015 #include <linux/input.h>
0016 #include <linux/input/mt.h>
0017 #include <linux/mutex.h>
0018 #include <linux/completion.h>
0019 #include <linux/slab.h>
0020 #include <asm/unaligned.h>
0021 #include <linux/crc-itu-t.h>
0022 #include "cyapa.h"
0023 
0024 
0025 #define GEN6_ENABLE_CMD_IRQ 0x41
0026 #define GEN6_DISABLE_CMD_IRQ    0x42
0027 #define GEN6_ENABLE_DEV_IRQ 0x43
0028 #define GEN6_DISABLE_DEV_IRQ    0x44
0029 
0030 #define GEN6_POWER_MODE_ACTIVE      0x01
0031 #define GEN6_POWER_MODE_LP_MODE1    0x02
0032 #define GEN6_POWER_MODE_LP_MODE2    0x03
0033 #define GEN6_POWER_MODE_BTN_ONLY    0x04
0034 
0035 #define GEN6_SET_POWER_MODE_INTERVAL    0x47
0036 #define GEN6_GET_POWER_MODE_INTERVAL    0x48
0037 
0038 #define GEN6_MAX_RX_NUM 14
0039 #define GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC   0x00
0040 #define GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM      0x12
0041 
0042 
0043 struct pip_app_cmd_head {
0044     __le16 addr;
0045     __le16 length;
0046     u8 report_id;
0047     u8 resv;  /* Reserved, must be 0 */
0048     u8 cmd_code;  /* bit7: resv, set to 0; bit6~0: command code.*/
0049 } __packed;
0050 
0051 struct pip_app_resp_head {
0052     __le16 length;
0053     u8 report_id;
0054     u8 resv;  /* Reserved, must be 0 */
0055     u8 cmd_code;  /* bit7: TGL; bit6~0: command code.*/
0056     /*
0057      * The value of data_status can be the first byte of data or
0058      * the command status or the unsupported command code depending on the
0059      * requested command code.
0060      */
0061     u8 data_status;
0062 } __packed;
0063 
0064 struct pip_fixed_info {
0065     u8 silicon_id_high;
0066     u8 silicon_id_low;
0067     u8 family_id;
0068 };
0069 
0070 static u8 pip_get_bl_info[] = {
0071     0x04, 0x00, 0x0B, 0x00, 0x40, 0x00, 0x01, 0x38,
0072     0x00, 0x00, 0x70, 0x9E, 0x17
0073 };
0074 
0075 static bool cyapa_sort_pip_hid_descriptor_data(struct cyapa *cyapa,
0076         u8 *buf, int len)
0077 {
0078     if (len != PIP_HID_DESCRIPTOR_SIZE)
0079         return false;
0080 
0081     if (buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID ||
0082         buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID)
0083         return true;
0084 
0085     return false;
0086 }
0087 
0088 static int cyapa_get_pip_fixed_info(struct cyapa *cyapa,
0089         struct pip_fixed_info *pip_info, bool is_bootloader)
0090 {
0091     u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH];
0092     int resp_len;
0093     u16 product_family;
0094     int error;
0095 
0096     if (is_bootloader) {
0097         /* Read Bootloader Information to determine Gen5 or Gen6. */
0098         resp_len = sizeof(resp_data);
0099         error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
0100                 pip_get_bl_info, sizeof(pip_get_bl_info),
0101                 resp_data, &resp_len,
0102                 2000, cyapa_sort_tsg_pip_bl_resp_data,
0103                 false);
0104         if (error || resp_len < PIP_BL_GET_INFO_RESP_LENGTH)
0105             return error ? error : -EIO;
0106 
0107         pip_info->family_id = resp_data[8];
0108         pip_info->silicon_id_low = resp_data[10];
0109         pip_info->silicon_id_high = resp_data[11];
0110 
0111         return 0;
0112     }
0113 
0114     /* Get App System Information to determine Gen5 or Gen6. */
0115     resp_len = sizeof(resp_data);
0116     error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
0117             pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH,
0118             resp_data, &resp_len,
0119             2000, cyapa_pip_sort_system_info_data, false);
0120     if (error || resp_len < PIP_READ_SYS_INFO_RESP_LENGTH)
0121         return error ? error : -EIO;
0122 
0123     product_family = get_unaligned_le16(&resp_data[7]);
0124     if ((product_family & PIP_PRODUCT_FAMILY_MASK) !=
0125         PIP_PRODUCT_FAMILY_TRACKPAD)
0126         return -EINVAL;
0127 
0128     pip_info->family_id = resp_data[19];
0129     pip_info->silicon_id_low = resp_data[21];
0130     pip_info->silicon_id_high = resp_data[22];
0131 
0132     return 0;
0133 
0134 }
0135 
0136 int cyapa_pip_state_parse(struct cyapa *cyapa, u8 *reg_data, int len)
0137 {
0138     u8 cmd[] = { 0x01, 0x00};
0139     struct pip_fixed_info pip_info;
0140     u8 resp_data[PIP_HID_DESCRIPTOR_SIZE];
0141     int resp_len;
0142     bool is_bootloader;
0143     int error;
0144 
0145     cyapa->state = CYAPA_STATE_NO_DEVICE;
0146 
0147     /* Try to wake from it deep sleep state if it is. */
0148     cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON);
0149 
0150     /* Empty the buffer queue to get fresh data with later commands. */
0151     cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
0152 
0153     /*
0154      * Read description info from trackpad device to determine running in
0155      * APP mode or Bootloader mode.
0156      */
0157     resp_len = PIP_HID_DESCRIPTOR_SIZE;
0158     error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
0159             cmd, sizeof(cmd),
0160             resp_data, &resp_len,
0161             300,
0162             cyapa_sort_pip_hid_descriptor_data,
0163             false);
0164     if (error)
0165         return error;
0166 
0167     if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID)
0168         is_bootloader = true;
0169     else if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID)
0170         is_bootloader = false;
0171     else
0172         return -EAGAIN;
0173 
0174     /* Get PIP fixed information to determine Gen5 or Gen6. */
0175     memset(&pip_info, 0, sizeof(struct pip_fixed_info));
0176     error = cyapa_get_pip_fixed_info(cyapa, &pip_info, is_bootloader);
0177     if (error)
0178         return error;
0179 
0180     if (pip_info.family_id == 0x9B && pip_info.silicon_id_high == 0x0B) {
0181         cyapa->gen = CYAPA_GEN6;
0182         cyapa->state = is_bootloader ? CYAPA_STATE_GEN6_BL
0183                          : CYAPA_STATE_GEN6_APP;
0184     } else if (pip_info.family_id == 0x91 &&
0185            pip_info.silicon_id_high == 0x02) {
0186         cyapa->gen = CYAPA_GEN5;
0187         cyapa->state = is_bootloader ? CYAPA_STATE_GEN5_BL
0188                          : CYAPA_STATE_GEN5_APP;
0189     }
0190 
0191     return 0;
0192 }
0193 
0194 static int cyapa_gen6_read_sys_info(struct cyapa *cyapa)
0195 {
0196     u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH];
0197     int resp_len;
0198     u16 product_family;
0199     u8 rotat_align;
0200     int error;
0201 
0202     /* Get App System Information to determine Gen5 or Gen6. */
0203     resp_len = sizeof(resp_data);
0204     error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
0205             pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH,
0206             resp_data, &resp_len,
0207             2000, cyapa_pip_sort_system_info_data, false);
0208     if (error || resp_len < sizeof(resp_data))
0209         return error ? error : -EIO;
0210 
0211     product_family = get_unaligned_le16(&resp_data[7]);
0212     if ((product_family & PIP_PRODUCT_FAMILY_MASK) !=
0213         PIP_PRODUCT_FAMILY_TRACKPAD)
0214         return -EINVAL;
0215 
0216     cyapa->platform_ver = (resp_data[67] >> PIP_BL_PLATFORM_VER_SHIFT) &
0217                   PIP_BL_PLATFORM_VER_MASK;
0218     cyapa->fw_maj_ver = resp_data[9];
0219     cyapa->fw_min_ver = resp_data[10];
0220 
0221     cyapa->electrodes_x = resp_data[33];
0222     cyapa->electrodes_y = resp_data[34];
0223 
0224     cyapa->physical_size_x =  get_unaligned_le16(&resp_data[35]) / 100;
0225     cyapa->physical_size_y = get_unaligned_le16(&resp_data[37]) / 100;
0226 
0227     cyapa->max_abs_x = get_unaligned_le16(&resp_data[39]);
0228     cyapa->max_abs_y = get_unaligned_le16(&resp_data[41]);
0229 
0230     cyapa->max_z = get_unaligned_le16(&resp_data[43]);
0231 
0232     cyapa->x_origin = resp_data[45] & 0x01;
0233     cyapa->y_origin = resp_data[46] & 0x01;
0234 
0235     cyapa->btn_capability = (resp_data[70] << 3) & CAPABILITY_BTN_MASK;
0236 
0237     memcpy(&cyapa->product_id[0], &resp_data[51], 5);
0238     cyapa->product_id[5] = '-';
0239     memcpy(&cyapa->product_id[6], &resp_data[56], 6);
0240     cyapa->product_id[12] = '-';
0241     memcpy(&cyapa->product_id[13], &resp_data[62], 2);
0242     cyapa->product_id[15] = '\0';
0243 
0244     /* Get the number of Rx electrodes. */
0245     rotat_align = resp_data[68];
0246     cyapa->electrodes_rx =
0247         rotat_align ? cyapa->electrodes_y : cyapa->electrodes_x;
0248     cyapa->aligned_electrodes_rx = (cyapa->electrodes_rx + 3) & ~3u;
0249 
0250     if (!cyapa->electrodes_x || !cyapa->electrodes_y ||
0251         !cyapa->physical_size_x || !cyapa->physical_size_y ||
0252         !cyapa->max_abs_x || !cyapa->max_abs_y || !cyapa->max_z)
0253         return -EINVAL;
0254 
0255     return 0;
0256 }
0257 
0258 static int cyapa_gen6_bl_read_app_info(struct cyapa *cyapa)
0259 {
0260     u8 resp_data[PIP_BL_APP_INFO_RESP_LENGTH];
0261     int resp_len;
0262     int error;
0263 
0264     resp_len = sizeof(resp_data);
0265     error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
0266             pip_bl_read_app_info, PIP_BL_READ_APP_INFO_CMD_LENGTH,
0267             resp_data, &resp_len,
0268             500, cyapa_sort_tsg_pip_bl_resp_data, false);
0269     if (error || resp_len < PIP_BL_APP_INFO_RESP_LENGTH ||
0270         !PIP_CMD_COMPLETE_SUCCESS(resp_data))
0271         return error ? error : -EIO;
0272 
0273     cyapa->fw_maj_ver = resp_data[8];
0274     cyapa->fw_min_ver = resp_data[9];
0275 
0276     cyapa->platform_ver = (resp_data[12] >> PIP_BL_PLATFORM_VER_SHIFT) &
0277                   PIP_BL_PLATFORM_VER_MASK;
0278 
0279     memcpy(&cyapa->product_id[0], &resp_data[13], 5);
0280     cyapa->product_id[5] = '-';
0281     memcpy(&cyapa->product_id[6], &resp_data[18], 6);
0282     cyapa->product_id[12] = '-';
0283     memcpy(&cyapa->product_id[13], &resp_data[24], 2);
0284     cyapa->product_id[15] = '\0';
0285 
0286     return 0;
0287 
0288 }
0289 
0290 static int cyapa_gen6_config_dev_irq(struct cyapa *cyapa, u8 cmd_code)
0291 {
0292     u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, cmd_code };
0293     u8 resp_data[6];
0294     int resp_len;
0295     int error;
0296 
0297     resp_len = sizeof(resp_data);
0298     error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
0299             resp_data, &resp_len,
0300             500, cyapa_sort_tsg_pip_app_resp_data, false);
0301     if (error || !VALID_CMD_RESP_HEADER(resp_data, cmd_code) ||
0302             !PIP_CMD_COMPLETE_SUCCESS(resp_data)
0303             )
0304         return error < 0 ? error : -EINVAL;
0305 
0306     return 0;
0307 }
0308 
0309 static int cyapa_gen6_set_proximity(struct cyapa *cyapa, bool enable)
0310 {
0311     int error;
0312 
0313     cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
0314     error = cyapa_pip_set_proximity(cyapa, enable);
0315     cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
0316 
0317     return error;
0318 }
0319 
0320 static int cyapa_gen6_change_power_state(struct cyapa *cyapa, u8 power_mode)
0321 {
0322     u8 cmd[] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x46, power_mode };
0323     u8 resp_data[6];
0324     int resp_len;
0325     int error;
0326 
0327     resp_len = sizeof(resp_data);
0328     error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
0329             resp_data, &resp_len,
0330             500, cyapa_sort_tsg_pip_app_resp_data, false);
0331     if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x46))
0332         return error < 0 ? error : -EINVAL;
0333 
0334     /* New power state applied in device not match the set power state. */
0335     if (resp_data[5] != power_mode)
0336         return -EAGAIN;
0337 
0338     return 0;
0339 }
0340 
0341 static int cyapa_gen6_set_interval_setting(struct cyapa *cyapa,
0342         struct gen6_interval_setting *interval_setting)
0343 {
0344     struct gen6_set_interval_cmd {
0345         __le16 addr;
0346         __le16 length;
0347         u8 report_id;
0348         u8 rsvd;  /* Reserved, must be 0 */
0349         u8 cmd_code;
0350         __le16 active_interval;
0351         __le16 lp1_interval;
0352         __le16 lp2_interval;
0353     } __packed set_interval_cmd;
0354     u8 resp_data[11];
0355     int resp_len;
0356     int error;
0357 
0358     memset(&set_interval_cmd, 0, sizeof(set_interval_cmd));
0359     put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &set_interval_cmd.addr);
0360     put_unaligned_le16(sizeof(set_interval_cmd) - 2,
0361                &set_interval_cmd.length);
0362     set_interval_cmd.report_id = PIP_APP_CMD_REPORT_ID;
0363     set_interval_cmd.cmd_code = GEN6_SET_POWER_MODE_INTERVAL;
0364     put_unaligned_le16(interval_setting->active_interval,
0365                &set_interval_cmd.active_interval);
0366     put_unaligned_le16(interval_setting->lp1_interval,
0367                &set_interval_cmd.lp1_interval);
0368     put_unaligned_le16(interval_setting->lp2_interval,
0369                &set_interval_cmd.lp2_interval);
0370 
0371     resp_len = sizeof(resp_data);
0372     error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
0373             (u8 *)&set_interval_cmd, sizeof(set_interval_cmd),
0374             resp_data, &resp_len,
0375             500, cyapa_sort_tsg_pip_app_resp_data, false);
0376     if (error ||
0377         !VALID_CMD_RESP_HEADER(resp_data, GEN6_SET_POWER_MODE_INTERVAL))
0378         return error < 0 ? error : -EINVAL;
0379 
0380     /* Get the real set intervals from response. */
0381     interval_setting->active_interval = get_unaligned_le16(&resp_data[5]);
0382     interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]);
0383     interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]);
0384 
0385     return 0;
0386 }
0387 
0388 static int cyapa_gen6_get_interval_setting(struct cyapa *cyapa,
0389         struct gen6_interval_setting *interval_setting)
0390 {
0391     u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00,
0392              GEN6_GET_POWER_MODE_INTERVAL };
0393     u8 resp_data[11];
0394     int resp_len;
0395     int error;
0396 
0397     resp_len = sizeof(resp_data);
0398     error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
0399             resp_data, &resp_len,
0400             500, cyapa_sort_tsg_pip_app_resp_data, false);
0401     if (error ||
0402         !VALID_CMD_RESP_HEADER(resp_data, GEN6_GET_POWER_MODE_INTERVAL))
0403         return error < 0 ? error : -EINVAL;
0404 
0405     interval_setting->active_interval = get_unaligned_le16(&resp_data[5]);
0406     interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]);
0407     interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]);
0408 
0409     return 0;
0410 }
0411 
0412 static int cyapa_gen6_deep_sleep(struct cyapa *cyapa, u8 state)
0413 {
0414     u8 ping[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x00 };
0415 
0416     if (state == PIP_DEEP_SLEEP_STATE_ON)
0417         /*
0418          * Send ping command to notify device prepare for wake up
0419          * when it's in deep sleep mode. At this time, device will
0420          * response nothing except an I2C NAK.
0421          */
0422         cyapa_i2c_pip_write(cyapa, ping, sizeof(ping));
0423 
0424     return cyapa_pip_deep_sleep(cyapa, state);
0425 }
0426 
0427 static int cyapa_gen6_set_power_mode(struct cyapa *cyapa,
0428         u8 power_mode, u16 sleep_time, enum cyapa_pm_stage pm_stage)
0429 {
0430     struct device *dev = &cyapa->client->dev;
0431     struct gen6_interval_setting *interval_setting =
0432             &cyapa->gen6_interval_setting;
0433     u8 lp_mode;
0434     int error;
0435 
0436     if (cyapa->state != CYAPA_STATE_GEN6_APP)
0437         return 0;
0438 
0439     if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) {
0440         /*
0441          * Assume TP in deep sleep mode when driver is loaded,
0442          * avoid driver unload and reload command IO issue caused by TP
0443          * has been set into deep sleep mode when unloading.
0444          */
0445         PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
0446     }
0447 
0448     if (PIP_DEV_UNINIT_SLEEP_TIME(cyapa) &&
0449         PIP_DEV_GET_PWR_STATE(cyapa) != PWR_MODE_OFF)
0450         PIP_DEV_SET_SLEEP_TIME(cyapa, UNINIT_SLEEP_TIME);
0451 
0452     if (PIP_DEV_GET_PWR_STATE(cyapa) == power_mode) {
0453         if (power_mode == PWR_MODE_OFF ||
0454             power_mode == PWR_MODE_FULL_ACTIVE ||
0455             power_mode == PWR_MODE_BTN_ONLY ||
0456             PIP_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) {
0457             /* Has in correct power mode state, early return. */
0458             return 0;
0459         }
0460     }
0461 
0462     if (power_mode == PWR_MODE_OFF) {
0463         cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
0464 
0465         error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF);
0466         if (error) {
0467             dev_err(dev, "enter deep sleep fail: %d\n", error);
0468             return error;
0469         }
0470 
0471         PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
0472         return 0;
0473     }
0474 
0475     /*
0476      * When trackpad in power off mode, it cannot change to other power
0477      * state directly, must be wake up from sleep firstly, then
0478      * continue to do next power sate change.
0479      */
0480     if (PIP_DEV_GET_PWR_STATE(cyapa) == PWR_MODE_OFF) {
0481         error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON);
0482         if (error) {
0483             dev_err(dev, "deep sleep wake fail: %d\n", error);
0484             return error;
0485         }
0486     }
0487 
0488     /*
0489      * Disable device assert interrupts for command response to avoid
0490      * disturbing system suspending or hibernating process.
0491      */
0492     cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
0493 
0494     if (power_mode == PWR_MODE_FULL_ACTIVE) {
0495         error = cyapa_gen6_change_power_state(cyapa,
0496                 GEN6_POWER_MODE_ACTIVE);
0497         if (error) {
0498             dev_err(dev, "change to active fail: %d\n", error);
0499             goto out;
0500         }
0501 
0502         PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE);
0503 
0504         /* Sync the interval setting from device. */
0505         cyapa_gen6_get_interval_setting(cyapa, interval_setting);
0506 
0507     } else if (power_mode == PWR_MODE_BTN_ONLY) {
0508         error = cyapa_gen6_change_power_state(cyapa,
0509                 GEN6_POWER_MODE_BTN_ONLY);
0510         if (error) {
0511             dev_err(dev, "fail to button only mode: %d\n", error);
0512             goto out;
0513         }
0514 
0515         PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY);
0516     } else {
0517         /*
0518          * Gen6 internally supports to 2 low power scan interval time,
0519          * so can help to switch power mode quickly.
0520          * such as runtime suspend and system suspend.
0521          */
0522         if (interval_setting->lp1_interval == sleep_time) {
0523             lp_mode = GEN6_POWER_MODE_LP_MODE1;
0524         } else if (interval_setting->lp2_interval == sleep_time) {
0525             lp_mode = GEN6_POWER_MODE_LP_MODE2;
0526         } else {
0527             if (interval_setting->lp1_interval == 0) {
0528                 interval_setting->lp1_interval = sleep_time;
0529                 lp_mode = GEN6_POWER_MODE_LP_MODE1;
0530             } else {
0531                 interval_setting->lp2_interval = sleep_time;
0532                 lp_mode = GEN6_POWER_MODE_LP_MODE2;
0533             }
0534             cyapa_gen6_set_interval_setting(cyapa,
0535                             interval_setting);
0536         }
0537 
0538         error = cyapa_gen6_change_power_state(cyapa, lp_mode);
0539         if (error) {
0540             dev_err(dev, "set power state to 0x%02x failed: %d\n",
0541                 lp_mode, error);
0542             goto out;
0543         }
0544 
0545         PIP_DEV_SET_SLEEP_TIME(cyapa, sleep_time);
0546         PIP_DEV_SET_PWR_STATE(cyapa,
0547             cyapa_sleep_time_to_pwr_cmd(sleep_time));
0548     }
0549 
0550 out:
0551     cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
0552     return error;
0553 }
0554 
0555 static int cyapa_gen6_initialize(struct cyapa *cyapa)
0556 {
0557     return 0;
0558 }
0559 
0560 static int cyapa_pip_retrieve_data_structure(struct cyapa *cyapa,
0561         u16 read_offset, u16 read_len, u8 data_id,
0562         u8 *data, int *data_buf_lens)
0563 {
0564     struct retrieve_data_struct_cmd {
0565         struct pip_app_cmd_head head;
0566         __le16 read_offset;
0567         __le16 read_length;
0568         u8 data_id;
0569     } __packed cmd;
0570     u8 resp_data[GEN6_MAX_RX_NUM + 10];
0571     int resp_len;
0572     int error;
0573 
0574     memset(&cmd, 0, sizeof(cmd));
0575     put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &cmd.head.addr);
0576     put_unaligned_le16(sizeof(cmd) - 2, &cmd.head.length);
0577     cmd.head.report_id = PIP_APP_CMD_REPORT_ID;
0578     cmd.head.cmd_code = PIP_RETRIEVE_DATA_STRUCTURE;
0579     put_unaligned_le16(read_offset, &cmd.read_offset);
0580     put_unaligned_le16(read_len, &cmd.read_length);
0581     cmd.data_id = data_id;
0582 
0583     resp_len = sizeof(resp_data);
0584     error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
0585                 (u8 *)&cmd, sizeof(cmd),
0586                 resp_data, &resp_len,
0587                 500, cyapa_sort_tsg_pip_app_resp_data,
0588                 true);
0589     if (error || !PIP_CMD_COMPLETE_SUCCESS(resp_data) ||
0590         resp_data[6] != data_id ||
0591         !VALID_CMD_RESP_HEADER(resp_data, PIP_RETRIEVE_DATA_STRUCTURE))
0592         return (error < 0) ? error : -EAGAIN;
0593 
0594     read_len = get_unaligned_le16(&resp_data[7]);
0595     if (*data_buf_lens < read_len) {
0596         *data_buf_lens = read_len;
0597         return -ENOBUFS;
0598     }
0599 
0600     memcpy(data, &resp_data[10], read_len);
0601     *data_buf_lens = read_len;
0602     return 0;
0603 }
0604 
0605 static ssize_t cyapa_gen6_show_baseline(struct device *dev,
0606         struct device_attribute *attr, char *buf)
0607 {
0608     struct cyapa *cyapa = dev_get_drvdata(dev);
0609     u8 data[GEN6_MAX_RX_NUM];
0610     int data_len;
0611     int size = 0;
0612     int i;
0613     int error;
0614     int resume_error;
0615 
0616     if (!cyapa_is_pip_app_mode(cyapa))
0617         return -EBUSY;
0618 
0619     /* 1. Suspend Scanning*/
0620     error = cyapa_pip_suspend_scanning(cyapa);
0621     if (error)
0622         return error;
0623 
0624     /* 2. IDAC and RX Attenuator Calibration Data (Center Frequency). */
0625     data_len = sizeof(data);
0626     error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len,
0627             GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC,
0628             data, &data_len);
0629     if (error)
0630         goto resume_scanning;
0631 
0632     size = scnprintf(buf, PAGE_SIZE, "%d %d %d %d %d %d ",
0633             data[0],  /* RX Attenuator Mutual */
0634             data[1],  /* IDAC Mutual */
0635             data[2],  /* RX Attenuator Self RX */
0636             data[3],  /* IDAC Self RX */
0637             data[4],  /* RX Attenuator Self TX */
0638             data[5]   /* IDAC Self TX */
0639             );
0640 
0641     /* 3. Read Attenuator Trim. */
0642     data_len = sizeof(data);
0643     error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len,
0644             GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM,
0645             data, &data_len);
0646     if (error)
0647         goto resume_scanning;
0648 
0649     /* set attenuator trim values. */
0650     for (i = 0; i < data_len; i++)
0651         size += scnprintf(buf + size, PAGE_SIZE - size, "%d ", data[i]);
0652     size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
0653 
0654 resume_scanning:
0655     /* 4. Resume Scanning*/
0656     resume_error = cyapa_pip_resume_scanning(cyapa);
0657     if (resume_error || error) {
0658         memset(buf, 0, PAGE_SIZE);
0659         return resume_error ? resume_error : error;
0660     }
0661 
0662     return size;
0663 }
0664 
0665 static int cyapa_gen6_operational_check(struct cyapa *cyapa)
0666 {
0667     struct device *dev = &cyapa->client->dev;
0668     int error;
0669 
0670     if (cyapa->gen != CYAPA_GEN6)
0671         return -ENODEV;
0672 
0673     switch (cyapa->state) {
0674     case CYAPA_STATE_GEN6_BL:
0675         error = cyapa_pip_bl_exit(cyapa);
0676         if (error) {
0677             /* Try to update trackpad product information. */
0678             cyapa_gen6_bl_read_app_info(cyapa);
0679             goto out;
0680         }
0681 
0682         cyapa->state = CYAPA_STATE_GEN6_APP;
0683         fallthrough;
0684 
0685     case CYAPA_STATE_GEN6_APP:
0686         /*
0687          * If trackpad device in deep sleep mode,
0688          * the app command will fail.
0689          * So always try to reset trackpad device to full active when
0690          * the device state is required.
0691          */
0692         error = cyapa_gen6_set_power_mode(cyapa,
0693                 PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE);
0694         if (error)
0695             dev_warn(dev, "%s: failed to set power active mode.\n",
0696                 __func__);
0697 
0698         /* By default, the trackpad proximity function is enabled. */
0699         error = cyapa_pip_set_proximity(cyapa, true);
0700         if (error)
0701             dev_warn(dev, "%s: failed to enable proximity.\n",
0702                 __func__);
0703 
0704         /* Get trackpad product information. */
0705         error = cyapa_gen6_read_sys_info(cyapa);
0706         if (error)
0707             goto out;
0708         /* Only support product ID starting with CYTRA */
0709         if (memcmp(cyapa->product_id, product_id,
0710                 strlen(product_id)) != 0) {
0711             dev_err(dev, "%s: unknown product ID (%s)\n",
0712                 __func__, cyapa->product_id);
0713             error = -EINVAL;
0714         }
0715         break;
0716     default:
0717         error = -EINVAL;
0718     }
0719 
0720 out:
0721     return error;
0722 }
0723 
0724 const struct cyapa_dev_ops cyapa_gen6_ops = {
0725     .check_fw = cyapa_pip_check_fw,
0726     .bl_enter = cyapa_pip_bl_enter,
0727     .bl_initiate = cyapa_pip_bl_initiate,
0728     .update_fw = cyapa_pip_do_fw_update,
0729     .bl_activate = cyapa_pip_bl_activate,
0730     .bl_deactivate = cyapa_pip_bl_deactivate,
0731 
0732     .show_baseline = cyapa_gen6_show_baseline,
0733     .calibrate_store = cyapa_pip_do_calibrate,
0734 
0735     .initialize = cyapa_gen6_initialize,
0736 
0737     .state_parse = cyapa_pip_state_parse,
0738     .operational_check = cyapa_gen6_operational_check,
0739 
0740     .irq_handler = cyapa_pip_irq_handler,
0741     .irq_cmd_handler = cyapa_pip_irq_cmd_handler,
0742     .sort_empty_output_data = cyapa_empty_pip_output_data,
0743     .set_power_mode = cyapa_gen6_set_power_mode,
0744 
0745     .set_proximity = cyapa_gen6_set_proximity,
0746 };