cyapa_gen6.c (21037B)
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) - 2, &cmd.head.length); 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 fallthrough; 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};