1 /* 2 * Cypress APA trackpad with I2C interface 3 * 4 * Author: Dudley Du <dudl@cypress.com> 5 * 6 * Copyright (C) 2015 Cypress Semiconductor, Inc. 7 * 8 * This file is subject to the terms and conditions of the GNU General Public 9 * License. See the file COPYING in the main directory of this archive for 10 * more details. 11 */ 12 13 #include <linux/delay.h> 14 #include <linux/i2c.h> 15 #include <linux/input.h> 16 #include <linux/input/mt.h> 17 #include <linux/mutex.h> 18 #include <linux/completion.h> 19 #include <linux/slab.h> 20 #include <asm/unaligned.h> 21 #include <linux/crc-itu-t.h> 22 #include "cyapa.h" 23 24 25 #define GEN6_ENABLE_CMD_IRQ 0x41 26 #define GEN6_DISABLE_CMD_IRQ 0x42 27 #define GEN6_ENABLE_DEV_IRQ 0x43 28 #define GEN6_DISABLE_DEV_IRQ 0x44 29 30 #define GEN6_POWER_MODE_ACTIVE 0x01 31 #define GEN6_POWER_MODE_LP_MODE1 0x02 32 #define GEN6_POWER_MODE_LP_MODE2 0x03 33 #define GEN6_POWER_MODE_BTN_ONLY 0x04 34 35 #define GEN6_SET_POWER_MODE_INTERVAL 0x47 36 #define GEN6_GET_POWER_MODE_INTERVAL 0x48 37 38 #define GEN6_MAX_RX_NUM 14 39 #define GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC 0x00 40 #define GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM 0x12 41 42 43 struct pip_app_cmd_head { 44 __le16 addr; 45 __le16 length; 46 u8 report_id; 47 u8 resv; /* Reserved, must be 0 */ 48 u8 cmd_code; /* bit7: resv, set to 0; bit6~0: command code.*/ 49 } __packed; 50 51 struct pip_app_resp_head { 52 __le16 length; 53 u8 report_id; 54 u8 resv; /* Reserved, must be 0 */ 55 u8 cmd_code; /* bit7: TGL; bit6~0: command code.*/ 56 /* 57 * The value of data_status can be the first byte of data or 58 * the command status or the unsupported command code depending on the 59 * requested command code. 60 */ 61 u8 data_status; 62 } __packed; 63 64 struct pip_fixed_info { 65 u8 silicon_id_high; 66 u8 silicon_id_low; 67 u8 family_id; 68 }; 69 70 static u8 pip_get_bl_info[] = { 71 0x04, 0x00, 0x0B, 0x00, 0x40, 0x00, 0x01, 0x38, 72 0x00, 0x00, 0x70, 0x9E, 0x17 73 }; 74 75 static bool cyapa_sort_pip_hid_descriptor_data(struct cyapa *cyapa, 76 u8 *buf, int len) 77 { 78 if (len != PIP_HID_DESCRIPTOR_SIZE) 79 return false; 80 81 if (buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID || 82 buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID) 83 return true; 84 85 return false; 86 } 87 88 static int cyapa_get_pip_fixed_info(struct cyapa *cyapa, 89 struct pip_fixed_info *pip_info, bool is_bootloader) 90 { 91 u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH]; 92 int resp_len; 93 u16 product_family; 94 int error; 95 96 if (is_bootloader) { 97 /* Read Bootloader Information to determine Gen5 or Gen6. */ 98 resp_len = sizeof(resp_data); 99 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, 100 pip_get_bl_info, sizeof(pip_get_bl_info), 101 resp_data, &resp_len, 102 2000, cyapa_sort_tsg_pip_bl_resp_data, 103 false); 104 if (error || resp_len < PIP_BL_GET_INFO_RESP_LENGTH) 105 return error ? error : -EIO; 106 107 pip_info->family_id = resp_data[8]; 108 pip_info->silicon_id_low = resp_data[10]; 109 pip_info->silicon_id_high = resp_data[11]; 110 111 return 0; 112 } 113 114 /* Get App System Information to determine Gen5 or Gen6. */ 115 resp_len = sizeof(resp_data); 116 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, 117 pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH, 118 resp_data, &resp_len, 119 2000, cyapa_pip_sort_system_info_data, false); 120 if (error || resp_len < PIP_READ_SYS_INFO_RESP_LENGTH) 121 return error ? error : -EIO; 122 123 product_family = get_unaligned_le16(&resp_data[7]); 124 if ((product_family & PIP_PRODUCT_FAMILY_MASK) != 125 PIP_PRODUCT_FAMILY_TRACKPAD) 126 return -EINVAL; 127 128 pip_info->family_id = resp_data[19]; 129 pip_info->silicon_id_low = resp_data[21]; 130 pip_info->silicon_id_high = resp_data[22]; 131 132 return 0; 133 134 } 135 136 int cyapa_pip_state_parse(struct cyapa *cyapa, u8 *reg_data, int len) 137 { 138 u8 cmd[] = { 0x01, 0x00}; 139 struct pip_fixed_info pip_info; 140 u8 resp_data[PIP_HID_DESCRIPTOR_SIZE]; 141 int resp_len; 142 bool is_bootloader; 143 int error; 144 145 cyapa->state = CYAPA_STATE_NO_DEVICE; 146 147 /* Try to wake from it deep sleep state if it is. */ 148 cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON); 149 150 /* Empty the buffer queue to get fresh data with later commands. */ 151 cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); 152 153 /* 154 * Read description info from trackpad device to determine running in 155 * APP mode or Bootloader mode. 156 */ 157 resp_len = PIP_HID_DESCRIPTOR_SIZE; 158 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, 159 cmd, sizeof(cmd), 160 resp_data, &resp_len, 161 300, 162 cyapa_sort_pip_hid_descriptor_data, 163 false); 164 if (error) 165 return error; 166 167 if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID) 168 is_bootloader = true; 169 else if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID) 170 is_bootloader = false; 171 else 172 return -EAGAIN; 173 174 /* Get PIP fixed information to determine Gen5 or Gen6. */ 175 memset(&pip_info, 0, sizeof(struct pip_fixed_info)); 176 error = cyapa_get_pip_fixed_info(cyapa, &pip_info, is_bootloader); 177 if (error) 178 return error; 179 180 if (pip_info.family_id == 0x9B && pip_info.silicon_id_high == 0x0B) { 181 cyapa->gen = CYAPA_GEN6; 182 cyapa->state = is_bootloader ? CYAPA_STATE_GEN6_BL 183 : CYAPA_STATE_GEN6_APP; 184 } else if (pip_info.family_id == 0x91 && 185 pip_info.silicon_id_high == 0x02) { 186 cyapa->gen = CYAPA_GEN5; 187 cyapa->state = is_bootloader ? CYAPA_STATE_GEN5_BL 188 : CYAPA_STATE_GEN5_APP; 189 } 190 191 return 0; 192 } 193 194 static int cyapa_gen6_read_sys_info(struct cyapa *cyapa) 195 { 196 u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH]; 197 int resp_len; 198 u16 product_family; 199 u8 rotat_align; 200 int error; 201 202 /* Get App System Information to determine Gen5 or Gen6. */ 203 resp_len = sizeof(resp_data); 204 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, 205 pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH, 206 resp_data, &resp_len, 207 2000, cyapa_pip_sort_system_info_data, false); 208 if (error || resp_len < sizeof(resp_data)) 209 return error ? error : -EIO; 210 211 product_family = get_unaligned_le16(&resp_data[7]); 212 if ((product_family & PIP_PRODUCT_FAMILY_MASK) != 213 PIP_PRODUCT_FAMILY_TRACKPAD) 214 return -EINVAL; 215 216 cyapa->platform_ver = (resp_data[67] >> PIP_BL_PLATFORM_VER_SHIFT) & 217 PIP_BL_PLATFORM_VER_MASK; 218 cyapa->fw_maj_ver = resp_data[9]; 219 cyapa->fw_min_ver = resp_data[10]; 220 221 cyapa->electrodes_x = resp_data[33]; 222 cyapa->electrodes_y = resp_data[34]; 223 224 cyapa->physical_size_x = get_unaligned_le16(&resp_data[35]) / 100; 225 cyapa->physical_size_y = get_unaligned_le16(&resp_data[37]) / 100; 226 227 cyapa->max_abs_x = get_unaligned_le16(&resp_data[39]); 228 cyapa->max_abs_y = get_unaligned_le16(&resp_data[41]); 229 230 cyapa->max_z = get_unaligned_le16(&resp_data[43]); 231 232 cyapa->x_origin = resp_data[45] & 0x01; 233 cyapa->y_origin = resp_data[46] & 0x01; 234 235 cyapa->btn_capability = (resp_data[70] << 3) & CAPABILITY_BTN_MASK; 236 237 memcpy(&cyapa->product_id[0], &resp_data[51], 5); 238 cyapa->product_id[5] = '-'; 239 memcpy(&cyapa->product_id[6], &resp_data[56], 6); 240 cyapa->product_id[12] = '-'; 241 memcpy(&cyapa->product_id[13], &resp_data[62], 2); 242 cyapa->product_id[15] = '\0'; 243 244 rotat_align = resp_data[68]; 245 if (rotat_align) { 246 cyapa->electrodes_rx = cyapa->electrodes_y; 247 cyapa->electrodes_rx = cyapa->electrodes_y; 248 } else { 249 cyapa->electrodes_rx = cyapa->electrodes_x; 250 cyapa->electrodes_rx = cyapa->electrodes_y; 251 } 252 cyapa->aligned_electrodes_rx = (cyapa->electrodes_rx + 3) & ~3u; 253 254 if (!cyapa->electrodes_x || !cyapa->electrodes_y || 255 !cyapa->physical_size_x || !cyapa->physical_size_y || 256 !cyapa->max_abs_x || !cyapa->max_abs_y || !cyapa->max_z) 257 return -EINVAL; 258 259 return 0; 260 } 261 262 static int cyapa_gen6_bl_read_app_info(struct cyapa *cyapa) 263 { 264 u8 resp_data[PIP_BL_APP_INFO_RESP_LENGTH]; 265 int resp_len; 266 int error; 267 268 resp_len = sizeof(resp_data); 269 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, 270 pip_bl_read_app_info, PIP_BL_READ_APP_INFO_CMD_LENGTH, 271 resp_data, &resp_len, 272 500, cyapa_sort_tsg_pip_bl_resp_data, false); 273 if (error || resp_len < PIP_BL_APP_INFO_RESP_LENGTH || 274 !PIP_CMD_COMPLETE_SUCCESS(resp_data)) 275 return error ? error : -EIO; 276 277 cyapa->fw_maj_ver = resp_data[8]; 278 cyapa->fw_min_ver = resp_data[9]; 279 280 cyapa->platform_ver = (resp_data[12] >> PIP_BL_PLATFORM_VER_SHIFT) & 281 PIP_BL_PLATFORM_VER_MASK; 282 283 memcpy(&cyapa->product_id[0], &resp_data[13], 5); 284 cyapa->product_id[5] = '-'; 285 memcpy(&cyapa->product_id[6], &resp_data[18], 6); 286 cyapa->product_id[12] = '-'; 287 memcpy(&cyapa->product_id[13], &resp_data[24], 2); 288 cyapa->product_id[15] = '\0'; 289 290 return 0; 291 292 } 293 294 static int cyapa_gen6_config_dev_irq(struct cyapa *cyapa, u8 cmd_code) 295 { 296 u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, cmd_code }; 297 u8 resp_data[6]; 298 int resp_len; 299 int error; 300 301 resp_len = sizeof(resp_data); 302 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), 303 resp_data, &resp_len, 304 500, cyapa_sort_tsg_pip_app_resp_data, false); 305 if (error || !VALID_CMD_RESP_HEADER(resp_data, cmd_code) || 306 !PIP_CMD_COMPLETE_SUCCESS(resp_data) 307 ) 308 return error < 0 ? error : -EINVAL; 309 310 return 0; 311 } 312 313 static int cyapa_gen6_set_proximity(struct cyapa *cyapa, bool enable) 314 { 315 int error; 316 317 cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ); 318 error = cyapa_pip_set_proximity(cyapa, enable); 319 cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ); 320 321 return error; 322 } 323 324 static int cyapa_gen6_change_power_state(struct cyapa *cyapa, u8 power_mode) 325 { 326 u8 cmd[] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x46, power_mode }; 327 u8 resp_data[6]; 328 int resp_len; 329 int error; 330 331 resp_len = sizeof(resp_data); 332 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), 333 resp_data, &resp_len, 334 500, cyapa_sort_tsg_pip_app_resp_data, false); 335 if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x46)) 336 return error < 0 ? error : -EINVAL; 337 338 /* New power state applied in device not match the set power state. */ 339 if (resp_data[5] != power_mode) 340 return -EAGAIN; 341 342 return 0; 343 } 344 345 static int cyapa_gen6_set_interval_setting(struct cyapa *cyapa, 346 struct gen6_interval_setting *interval_setting) 347 { 348 struct gen6_set_interval_cmd { 349 __le16 addr; 350 __le16 length; 351 u8 report_id; 352 u8 rsvd; /* Reserved, must be 0 */ 353 u8 cmd_code; 354 __le16 active_interval; 355 __le16 lp1_interval; 356 __le16 lp2_interval; 357 } __packed set_interval_cmd; 358 u8 resp_data[11]; 359 int resp_len; 360 int error; 361 362 memset(&set_interval_cmd, 0, sizeof(set_interval_cmd)); 363 put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &set_interval_cmd.addr); 364 put_unaligned_le16(sizeof(set_interval_cmd) - 2, 365 &set_interval_cmd.length); 366 set_interval_cmd.report_id = PIP_APP_CMD_REPORT_ID; 367 set_interval_cmd.cmd_code = GEN6_SET_POWER_MODE_INTERVAL; 368 put_unaligned_le16(interval_setting->active_interval, 369 &set_interval_cmd.active_interval); 370 put_unaligned_le16(interval_setting->lp1_interval, 371 &set_interval_cmd.lp1_interval); 372 put_unaligned_le16(interval_setting->lp2_interval, 373 &set_interval_cmd.lp2_interval); 374 375 resp_len = sizeof(resp_data); 376 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, 377 (u8 *)&set_interval_cmd, sizeof(set_interval_cmd), 378 resp_data, &resp_len, 379 500, cyapa_sort_tsg_pip_app_resp_data, false); 380 if (error || 381 !VALID_CMD_RESP_HEADER(resp_data, GEN6_SET_POWER_MODE_INTERVAL)) 382 return error < 0 ? error : -EINVAL; 383 384 /* Get the real set intervals from response. */ 385 interval_setting->active_interval = get_unaligned_le16(&resp_data[5]); 386 interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]); 387 interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]); 388 389 return 0; 390 } 391 392 static int cyapa_gen6_get_interval_setting(struct cyapa *cyapa, 393 struct gen6_interval_setting *interval_setting) 394 { 395 u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 396 GEN6_GET_POWER_MODE_INTERVAL }; 397 u8 resp_data[11]; 398 int resp_len; 399 int error; 400 401 resp_len = sizeof(resp_data); 402 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), 403 resp_data, &resp_len, 404 500, cyapa_sort_tsg_pip_app_resp_data, false); 405 if (error || 406 !VALID_CMD_RESP_HEADER(resp_data, GEN6_GET_POWER_MODE_INTERVAL)) 407 return error < 0 ? error : -EINVAL; 408 409 interval_setting->active_interval = get_unaligned_le16(&resp_data[5]); 410 interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]); 411 interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]); 412 413 return 0; 414 } 415 416 static int cyapa_gen6_deep_sleep(struct cyapa *cyapa, u8 state) 417 { 418 u8 ping[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x00 }; 419 420 if (state == PIP_DEEP_SLEEP_STATE_ON) 421 /* 422 * Send ping command to notify device prepare for wake up 423 * when it's in deep sleep mode. At this time, device will 424 * response nothing except an I2C NAK. 425 */ 426 cyapa_i2c_pip_write(cyapa, ping, sizeof(ping)); 427 428 return cyapa_pip_deep_sleep(cyapa, state); 429 } 430 431 static int cyapa_gen6_set_power_mode(struct cyapa *cyapa, 432 u8 power_mode, u16 sleep_time, bool is_suspend) 433 { 434 struct device *dev = &cyapa->client->dev; 435 struct gen6_interval_setting *interval_setting = 436 &cyapa->gen6_interval_setting; 437 u8 lp_mode; 438 int error; 439 440 if (cyapa->state != CYAPA_STATE_GEN6_APP) 441 return 0; 442 443 if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) { 444 /* 445 * Assume TP in deep sleep mode when driver is loaded, 446 * avoid driver unload and reload command IO issue caused by TP 447 * has been set into deep sleep mode when unloading. 448 */ 449 PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF); 450 } 451 452 if (PIP_DEV_UNINIT_SLEEP_TIME(cyapa) && 453 PIP_DEV_GET_PWR_STATE(cyapa) != PWR_MODE_OFF) 454 PIP_DEV_SET_SLEEP_TIME(cyapa, UNINIT_SLEEP_TIME); 455 456 if (PIP_DEV_GET_PWR_STATE(cyapa) == power_mode) { 457 if (power_mode == PWR_MODE_OFF || 458 power_mode == PWR_MODE_FULL_ACTIVE || 459 power_mode == PWR_MODE_BTN_ONLY || 460 PIP_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) { 461 /* Has in correct power mode state, early return. */ 462 return 0; 463 } 464 } 465 466 if (power_mode == PWR_MODE_OFF) { 467 cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ); 468 469 error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF); 470 if (error) { 471 dev_err(dev, "enter deep sleep fail: %d\n", error); 472 return error; 473 } 474 475 PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF); 476 return 0; 477 } 478 479 /* 480 * When trackpad in power off mode, it cannot change to other power 481 * state directly, must be wake up from sleep firstly, then 482 * continue to do next power sate change. 483 */ 484 if (PIP_DEV_GET_PWR_STATE(cyapa) == PWR_MODE_OFF) { 485 error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON); 486 if (error) { 487 dev_err(dev, "deep sleep wake fail: %d\n", error); 488 return error; 489 } 490 } 491 492 /* 493 * Disable device assert interrupts for command response to avoid 494 * disturbing system suspending or hibernating process. 495 */ 496 cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ); 497 498 if (power_mode == PWR_MODE_FULL_ACTIVE) { 499 error = cyapa_gen6_change_power_state(cyapa, 500 GEN6_POWER_MODE_ACTIVE); 501 if (error) { 502 dev_err(dev, "change to active fail: %d\n", error); 503 goto out; 504 } 505 506 PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE); 507 508 /* Sync the interval setting from device. */ 509 cyapa_gen6_get_interval_setting(cyapa, interval_setting); 510 511 } else if (power_mode == PWR_MODE_BTN_ONLY) { 512 error = cyapa_gen6_change_power_state(cyapa, 513 GEN6_POWER_MODE_BTN_ONLY); 514 if (error) { 515 dev_err(dev, "fail to button only mode: %d\n", error); 516 goto out; 517 } 518 519 PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY); 520 } else { 521 /* 522 * Gen6 internally supports to 2 low power scan interval time, 523 * so can help to switch power mode quickly. 524 * such as runtime suspend and system suspend. 525 */ 526 if (interval_setting->lp1_interval == sleep_time) { 527 lp_mode = GEN6_POWER_MODE_LP_MODE1; 528 } else if (interval_setting->lp2_interval == sleep_time) { 529 lp_mode = GEN6_POWER_MODE_LP_MODE2; 530 } else { 531 if (interval_setting->lp1_interval == 0) { 532 interval_setting->lp1_interval = sleep_time; 533 lp_mode = GEN6_POWER_MODE_LP_MODE1; 534 } else { 535 interval_setting->lp2_interval = sleep_time; 536 lp_mode = GEN6_POWER_MODE_LP_MODE2; 537 } 538 cyapa_gen6_set_interval_setting(cyapa, 539 interval_setting); 540 } 541 542 error = cyapa_gen6_change_power_state(cyapa, lp_mode); 543 if (error) { 544 dev_err(dev, "set power state to 0x%02x failed: %d\n", 545 lp_mode, error); 546 goto out; 547 } 548 549 PIP_DEV_SET_SLEEP_TIME(cyapa, sleep_time); 550 PIP_DEV_SET_PWR_STATE(cyapa, 551 cyapa_sleep_time_to_pwr_cmd(sleep_time)); 552 } 553 554 out: 555 cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ); 556 return error; 557 } 558 559 static int cyapa_gen6_initialize(struct cyapa *cyapa) 560 { 561 return 0; 562 } 563 564 static int cyapa_pip_retrieve_data_structure(struct cyapa *cyapa, 565 u16 read_offset, u16 read_len, u8 data_id, 566 u8 *data, int *data_buf_lens) 567 { 568 struct retrieve_data_struct_cmd { 569 struct pip_app_cmd_head head; 570 __le16 read_offset; 571 __le16 read_length; 572 u8 data_id; 573 } __packed cmd; 574 u8 resp_data[GEN6_MAX_RX_NUM + 10]; 575 int resp_len; 576 int error; 577 578 memset(&cmd, 0, sizeof(cmd)); 579 put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &cmd.head.addr); 580 put_unaligned_le16(sizeof(cmd), &cmd.head.length - 2); 581 cmd.head.report_id = PIP_APP_CMD_REPORT_ID; 582 cmd.head.cmd_code = PIP_RETRIEVE_DATA_STRUCTURE; 583 put_unaligned_le16(read_offset, &cmd.read_offset); 584 put_unaligned_le16(read_len, &cmd.read_length); 585 cmd.data_id = data_id; 586 587 resp_len = sizeof(resp_data); 588 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, 589 (u8 *)&cmd, sizeof(cmd), 590 resp_data, &resp_len, 591 500, cyapa_sort_tsg_pip_app_resp_data, 592 true); 593 if (error || !PIP_CMD_COMPLETE_SUCCESS(resp_data) || 594 resp_data[6] != data_id || 595 !VALID_CMD_RESP_HEADER(resp_data, PIP_RETRIEVE_DATA_STRUCTURE)) 596 return (error < 0) ? error : -EAGAIN; 597 598 read_len = get_unaligned_le16(&resp_data[7]); 599 if (*data_buf_lens < read_len) { 600 *data_buf_lens = read_len; 601 return -ENOBUFS; 602 } 603 604 memcpy(data, &resp_data[10], read_len); 605 *data_buf_lens = read_len; 606 return 0; 607 } 608 609 static ssize_t cyapa_gen6_show_baseline(struct device *dev, 610 struct device_attribute *attr, char *buf) 611 { 612 struct cyapa *cyapa = dev_get_drvdata(dev); 613 u8 data[GEN6_MAX_RX_NUM]; 614 int data_len; 615 int size = 0; 616 int i; 617 int error; 618 int resume_error; 619 620 if (!cyapa_is_pip_app_mode(cyapa)) 621 return -EBUSY; 622 623 /* 1. Suspend Scanning*/ 624 error = cyapa_pip_suspend_scanning(cyapa); 625 if (error) 626 return error; 627 628 /* 2. IDAC and RX Attenuator Calibration Data (Center Frequency). */ 629 data_len = sizeof(data); 630 error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len, 631 GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC, 632 data, &data_len); 633 if (error) 634 goto resume_scanning; 635 636 size = scnprintf(buf, PAGE_SIZE, "%d %d %d %d %d %d ", 637 data[0], /* RX Attenuator Mutual */ 638 data[1], /* IDAC Mutual */ 639 data[2], /* RX Attenuator Self RX */ 640 data[3], /* IDAC Self RX */ 641 data[4], /* RX Attenuator Self TX */ 642 data[5] /* IDAC Self TX */ 643 ); 644 645 /* 3. Read Attenuator Trim. */ 646 data_len = sizeof(data); 647 error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len, 648 GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM, 649 data, &data_len); 650 if (error) 651 goto resume_scanning; 652 653 /* set attenuator trim values. */ 654 for (i = 0; i < data_len; i++) 655 size += scnprintf(buf + size, PAGE_SIZE - size, "%d ", data[i]); 656 size += scnprintf(buf + size, PAGE_SIZE - size, "\n"); 657 658 resume_scanning: 659 /* 4. Resume Scanning*/ 660 resume_error = cyapa_pip_resume_scanning(cyapa); 661 if (resume_error || error) { 662 memset(buf, 0, PAGE_SIZE); 663 return resume_error ? resume_error : error; 664 } 665 666 return size; 667 } 668 669 static int cyapa_gen6_operational_check(struct cyapa *cyapa) 670 { 671 struct device *dev = &cyapa->client->dev; 672 int error; 673 674 if (cyapa->gen != CYAPA_GEN6) 675 return -ENODEV; 676 677 switch (cyapa->state) { 678 case CYAPA_STATE_GEN6_BL: 679 error = cyapa_pip_bl_exit(cyapa); 680 if (error) { 681 /* Try to update trackpad product information. */ 682 cyapa_gen6_bl_read_app_info(cyapa); 683 goto out; 684 } 685 686 cyapa->state = CYAPA_STATE_GEN6_APP; 687 688 case CYAPA_STATE_GEN6_APP: 689 /* 690 * If trackpad device in deep sleep mode, 691 * the app command will fail. 692 * So always try to reset trackpad device to full active when 693 * the device state is required. 694 */ 695 error = cyapa_gen6_set_power_mode(cyapa, 696 PWR_MODE_FULL_ACTIVE, 0, false); 697 if (error) 698 dev_warn(dev, "%s: failed to set power active mode.\n", 699 __func__); 700 701 /* By default, the trackpad proximity function is enabled. */ 702 error = cyapa_pip_set_proximity(cyapa, true); 703 if (error) 704 dev_warn(dev, "%s: failed to enable proximity.\n", 705 __func__); 706 707 /* Get trackpad product information. */ 708 error = cyapa_gen6_read_sys_info(cyapa); 709 if (error) 710 goto out; 711 /* Only support product ID starting with CYTRA */ 712 if (memcmp(cyapa->product_id, product_id, 713 strlen(product_id)) != 0) { 714 dev_err(dev, "%s: unknown product ID (%s)\n", 715 __func__, cyapa->product_id); 716 error = -EINVAL; 717 } 718 break; 719 default: 720 error = -EINVAL; 721 } 722 723 out: 724 return error; 725 } 726 727 const struct cyapa_dev_ops cyapa_gen6_ops = { 728 .check_fw = cyapa_pip_check_fw, 729 .bl_enter = cyapa_pip_bl_enter, 730 .bl_initiate = cyapa_pip_bl_initiate, 731 .update_fw = cyapa_pip_do_fw_update, 732 .bl_activate = cyapa_pip_bl_activate, 733 .bl_deactivate = cyapa_pip_bl_deactivate, 734 735 .show_baseline = cyapa_gen6_show_baseline, 736 .calibrate_store = cyapa_pip_do_calibrate, 737 738 .initialize = cyapa_gen6_initialize, 739 740 .state_parse = cyapa_pip_state_parse, 741 .operational_check = cyapa_gen6_operational_check, 742 743 .irq_handler = cyapa_pip_irq_handler, 744 .irq_cmd_handler = cyapa_pip_irq_cmd_handler, 745 .sort_empty_output_data = cyapa_empty_pip_output_data, 746 .set_power_mode = cyapa_gen6_set_power_mode, 747 748 .set_proximity = cyapa_gen6_set_proximity, 749 }; 750