linux/drivers/input/mouse/cyapa_gen6.c
<<
>>
Prefs
   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        /* 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
 258static 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
 290static 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
 309static 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
 320static 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
 341static 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
 388static 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
 412static 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
 427static int cyapa_gen6_set_power_mode(struct cyapa *cyapa,
 428                u8 power_mode, u16 sleep_time, enum cyapa_pm_stage pm_stage)
 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
 550out:
 551        cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
 552        return error;
 553}
 554
 555static int cyapa_gen6_initialize(struct cyapa *cyapa)
 556{
 557        return 0;
 558}
 559
 560static 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
 605static 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
 654resume_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
 665static 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                /* fall through */
 684
 685        case CYAPA_STATE_GEN6_APP:
 686                /*
 687                 * If trackpad device in deep sleep mode,
 688                 * the app command will fail.
 689                 * So always try to reset trackpad device to full active when
 690                 * the device state is required.
 691                 */
 692                error = cyapa_gen6_set_power_mode(cyapa,
 693                                PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE);
 694                if (error)
 695                        dev_warn(dev, "%s: failed to set power active mode.\n",
 696                                __func__);
 697
 698                /* By default, the trackpad proximity function is enabled. */
 699                error = cyapa_pip_set_proximity(cyapa, true);
 700                if (error)
 701                        dev_warn(dev, "%s: failed to enable proximity.\n",
 702                                __func__);
 703
 704                /* Get trackpad product information. */
 705                error = cyapa_gen6_read_sys_info(cyapa);
 706                if (error)
 707                        goto out;
 708                /* Only support product ID starting with CYTRA */
 709                if (memcmp(cyapa->product_id, product_id,
 710                                strlen(product_id)) != 0) {
 711                        dev_err(dev, "%s: unknown product ID (%s)\n",
 712                                __func__, cyapa->product_id);
 713                        error = -EINVAL;
 714                }
 715                break;
 716        default:
 717                error = -EINVAL;
 718        }
 719
 720out:
 721        return error;
 722}
 723
 724const struct cyapa_dev_ops cyapa_gen6_ops = {
 725        .check_fw = cyapa_pip_check_fw,
 726        .bl_enter = cyapa_pip_bl_enter,
 727        .bl_initiate = cyapa_pip_bl_initiate,
 728        .update_fw = cyapa_pip_do_fw_update,
 729        .bl_activate = cyapa_pip_bl_activate,
 730        .bl_deactivate = cyapa_pip_bl_deactivate,
 731
 732        .show_baseline = cyapa_gen6_show_baseline,
 733        .calibrate_store = cyapa_pip_do_calibrate,
 734
 735        .initialize = cyapa_gen6_initialize,
 736
 737        .state_parse = cyapa_pip_state_parse,
 738        .operational_check = cyapa_gen6_operational_check,
 739
 740        .irq_handler = cyapa_pip_irq_handler,
 741        .irq_cmd_handler = cyapa_pip_irq_cmd_handler,
 742        .sort_empty_output_data = cyapa_empty_pip_output_data,
 743        .set_power_mode = cyapa_gen6_set_power_mode,
 744
 745        .set_proximity = cyapa_gen6_set_proximity,
 746};
 747