FILE
link_dp_training_dpia.c
File preview with syntax highlighting.
linux-6.19.14/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_dpia.c
1/* 2 * Copyright 2022 Advanced Micro Devices, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: AMD 23 * 24 */ 25 26/* FILE POLICY AND INTENDED USAGE: 27 * This module implements functionality for training DPIA links. 28 */ 29#include "link_dp_training_dpia.h" 30#include "dc.h" 31#include "inc/core_status.h" 32#include "dpcd_defs.h" 33 34#include "link_dp_dpia.h" 35#include "link_hwss.h" 36#include "dm_helpers.h" 37#include "dmub/inc/dmub_cmd.h" 38#include "link_dpcd.h" 39#include "link_dp_phy.h" 40#include "link_dp_training_8b_10b.h" 41#include "link_dp_capability.h" 42#include "dc_dmub_srv.h" 43#define DC_LOGGER \ 44 link->ctx->logger 45 46/* Extend interval between training status checks for manual testing. */ 47#define DPIA_DEBUG_EXTENDED_AUX_RD_INTERVAL_US 60000000 48 49#define TRAINING_AUX_RD_INTERVAL 100 //us 50 51/* SET_CONFIG message types sent by driver. */ 52enum dpia_set_config_type { 53 DPIA_SET_CFG_SET_LINK = 0x01, 54 DPIA_SET_CFG_SET_PHY_TEST_MODE = 0x05, 55 DPIA_SET_CFG_SET_TRAINING = 0x18, 56 DPIA_SET_CFG_SET_VSPE = 0x19 57}; 58 59/* Training stages (TS) in SET_CONFIG(SET_TRAINING) message. */ 60enum dpia_set_config_ts { 61 DPIA_TS_DPRX_DONE = 0x00, /* Done training DPRX. */ 62 DPIA_TS_TPS1 = 0x01, 63 DPIA_TS_TPS2 = 0x02, 64 DPIA_TS_TPS3 = 0x03, 65 DPIA_TS_TPS4 = 0x07, 66 DPIA_TS_UFP_DONE = 0xff /* Done training DPTX-to-DPIA hop. */ 67}; 68 69/* SET_CONFIG message data associated with messages sent by driver. */ 70union dpia_set_config_data { 71 struct { 72 uint8_t mode : 1; 73 uint8_t reserved : 7; 74 } set_link; 75 struct { 76 uint8_t stage; 77 } set_training; 78 struct { 79 uint8_t swing : 2; 80 uint8_t max_swing_reached : 1; 81 uint8_t pre_emph : 2; 82 uint8_t max_pre_emph_reached : 1; 83 uint8_t reserved : 2; 84 } set_vspe; 85 uint8_t raw; 86}; 87 88 89/* Configure link as prescribed in link_setting; set LTTPR mode; and 90 * Initialize link training settings. 91 * Abort link training if sink unplug detected. 92 * 93 * @param link DPIA link being trained. 94 * @param[in] link_setting Lane count, link rate and downspread control. 95 * @param[out] lt_settings Link settings and drive settings (voltage swing and pre-emphasis). 96 */ 97static enum link_training_result dpia_configure_link( 98 struct dc_link *link, 99 const struct link_resource *link_res, 100 const struct dc_link_settings *link_setting, 101 struct link_training_settings *lt_settings) 102{ 103 enum dc_status status; 104 bool fec_enable; 105 106 DC_LOG_HW_LINK_TRAINING("%s\n DPIA(%d) configuring\n - LTTPR mode(%d)\n", 107 __func__, 108 link->link_id.enum_id - ENUM_ID_1, 109 lt_settings->lttpr_mode); 110 111 dp_decide_training_settings( 112 link, 113 link_res, 114 link_setting, 115 lt_settings); 116 117 dp_get_lttpr_mode_override(link, <_settings->lttpr_mode); 118 119 status = dpcd_configure_channel_coding(link, lt_settings); 120 if (status != DC_OK && link->is_hpd_pending) 121 return LINK_TRAINING_ABORT; 122 123 /* Configure lttpr mode */ 124 status = dpcd_configure_lttpr_mode(link, lt_settings); 125 if (status != DC_OK && link->is_hpd_pending) 126 return LINK_TRAINING_ABORT; 127 128 /* Set link rate, lane count and spread. */ 129 status = dpcd_set_link_settings(link, lt_settings); 130 if (status != DC_OK && link->is_hpd_pending) 131 return LINK_TRAINING_ABORT; 132 133 if (link_dp_get_encoding_format(link_setting) == DP_8b_10b_ENCODING) { 134 if (link->preferred_training_settings.fec_enable != NULL) 135 fec_enable = *link->preferred_training_settings.fec_enable; 136 else 137 fec_enable = true; 138 status = dp_set_fec_ready(link, link_res, fec_enable); 139 } 140 141 if (status != DC_OK && link->is_hpd_pending) 142 return LINK_TRAINING_ABORT; 143 144 return LINK_TRAINING_SUCCESS; 145} 146 147static enum dc_status core_link_send_set_config( 148 struct dc_link *link, 149 uint8_t msg_type, 150 uint8_t msg_data) 151{ 152 struct set_config_cmd_payload payload; 153 enum set_config_status set_config_result = SET_CONFIG_PENDING; 154 155 /* prepare set_config payload */ 156 payload.msg_type = msg_type; 157 payload.msg_data = msg_data; 158 159 if (!link->ddc->ddc_pin && !link->aux_access_disabled && 160 (dm_helpers_dmub_set_config_sync(link->ctx, 161 link, &payload, &set_config_result) == -1)) { 162 return DC_ERROR_UNEXPECTED; 163 } 164 165 /* set_config should return ACK if successful */ 166 return (set_config_result == SET_CONFIG_ACK_RECEIVED) ? DC_OK : DC_ERROR_UNEXPECTED; 167} 168 169/* Build SET_CONFIG message data payload for specified message type. */ 170static uint8_t dpia_build_set_config_data( 171 enum dpia_set_config_type type, 172 struct dc_link *link, 173 struct link_training_settings *lt_settings) 174{ 175 union dpia_set_config_data data; 176 177 data.raw = 0; 178 179 switch (type) { 180 case DPIA_SET_CFG_SET_LINK: 181 data.set_link.mode = lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT ? 1 : 0; 182 break; 183 case DPIA_SET_CFG_SET_PHY_TEST_MODE: 184 break; 185 case DPIA_SET_CFG_SET_VSPE: 186 /* Assume all lanes have same drive settings. */ 187 data.set_vspe.swing = lt_settings->hw_lane_settings[0].VOLTAGE_SWING; 188 data.set_vspe.pre_emph = lt_settings->hw_lane_settings[0].PRE_EMPHASIS; 189 data.set_vspe.max_swing_reached = 190 lt_settings->hw_lane_settings[0].VOLTAGE_SWING == VOLTAGE_SWING_MAX_LEVEL ? 1 : 0; 191 data.set_vspe.max_pre_emph_reached = 192 lt_settings->hw_lane_settings[0].PRE_EMPHASIS == PRE_EMPHASIS_MAX_LEVEL ? 1 : 0; 193 break; 194 default: 195 ASSERT(false); /* Message type not supported by helper function. */ 196 break; 197 } 198 199 return data.raw; 200} 201 202/* Convert DC training pattern to DPIA training stage. */ 203static enum dc_status convert_trng_ptn_to_trng_stg(enum dc_dp_training_pattern tps, enum dpia_set_config_ts *ts) 204{ 205 enum dc_status status = DC_OK; 206 207 switch (tps) { 208 case DP_TRAINING_PATTERN_SEQUENCE_1: 209 *ts = DPIA_TS_TPS1; 210 break; 211 case DP_TRAINING_PATTERN_SEQUENCE_2: 212 *ts = DPIA_TS_TPS2; 213 break; 214 case DP_TRAINING_PATTERN_SEQUENCE_3: 215 *ts = DPIA_TS_TPS3; 216 break; 217 case DP_TRAINING_PATTERN_SEQUENCE_4: 218 *ts = DPIA_TS_TPS4; 219 break; 220 case DP_TRAINING_PATTERN_VIDEOIDLE: 221 *ts = DPIA_TS_DPRX_DONE; 222 break; 223 default: /* TPS not supported by helper function. */ 224 ASSERT(false); 225 *ts = DPIA_TS_DPRX_DONE; 226 status = DC_UNSUPPORTED_VALUE; 227 break; 228 } 229 230 return status; 231} 232 233/* Write training pattern to DPCD. */ 234static enum dc_status dpcd_set_lt_pattern( 235 struct dc_link *link, 236 enum dc_dp_training_pattern pattern, 237 uint32_t hop) 238{ 239 union dpcd_training_pattern dpcd_pattern = {0}; 240 uint32_t dpcd_tps_offset = DP_TRAINING_PATTERN_SET; 241 enum dc_status status; 242 243 if (hop != DPRX) 244 dpcd_tps_offset = DP_TRAINING_PATTERN_SET_PHY_REPEATER1 + 245 ((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (hop - 1)); 246 247 /* DpcdAddress_TrainingPatternSet */ 248 dpcd_pattern.v1_4.TRAINING_PATTERN_SET = 249 dp_training_pattern_to_dpcd_training_pattern(link, pattern); 250 251 dpcd_pattern.v1_4.SCRAMBLING_DISABLE = 252 dp_initialize_scrambling_data_symbols(link, pattern); 253 254 if (hop != DPRX) { 255 DC_LOG_HW_LINK_TRAINING("%s\n LTTPR Repeater ID: %d\n 0x%X pattern = %x\n", 256 __func__, 257 hop, 258 dpcd_tps_offset, 259 dpcd_pattern.v1_4.TRAINING_PATTERN_SET); 260 } else { 261 DC_LOG_HW_LINK_TRAINING("%s\n 0x%X pattern = %x\n", 262 __func__, 263 dpcd_tps_offset, 264 dpcd_pattern.v1_4.TRAINING_PATTERN_SET); 265 } 266 267 status = core_link_write_dpcd( 268 link, 269 dpcd_tps_offset, 270 &dpcd_pattern.raw, 271 sizeof(dpcd_pattern.raw)); 272 273 return status; 274} 275 276/* Execute clock recovery phase of link training for specified hop in display 277 * path.in non-transparent mode: 278 * - Driver issues both DPCD and SET_CONFIG transactions. 279 * - TPS1 is transmitted for any hops downstream of DPOA. 280 * - Drive (VS/PE) only transmitted for the hop immediately downstream of DPOA. 281 * - CR for the first hop (DPTX-to-DPIA) is assumed to be successful. 282 * 283 * @param link DPIA link being trained. 284 * @param lt_settings link_setting and drive settings (voltage swing and pre-emphasis). 285 * @param hop Hop in display path. DPRX = 0. 286 */ 287static enum link_training_result dpia_training_cr_non_transparent( 288 struct dc_link *link, 289 const struct link_resource *link_res, 290 struct link_training_settings *lt_settings, 291 uint32_t hop) 292{ 293 enum link_training_result result = LINK_TRAINING_CR_FAIL_LANE0; 294 uint8_t repeater_cnt = 0; /* Number of hops/repeaters in display path. */ 295 enum dc_status status = DC_ERROR_UNEXPECTED; 296 uint32_t retries_cr = 0; /* Number of consecutive attempts with same VS or PE. */ 297 uint32_t retry_count = 0; 298 uint32_t wait_time_microsec = TRAINING_AUX_RD_INTERVAL; /* From DP spec, CR read interval is always 100us. */ 299 enum dc_lane_count lane_count = lt_settings->link_settings.lane_count; 300 union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0}; 301 union lane_align_status_updated dpcd_lane_status_updated = {0}; 302 union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0}; 303 uint8_t set_cfg_data; 304 enum dpia_set_config_ts ts; 305 306 repeater_cnt = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt); 307 308 /* Cap of LINK_TRAINING_MAX_CR_RETRY attempts at clock recovery. 309 * Fix inherited from perform_clock_recovery_sequence() - 310 * the DP equivalent of this function: 311 * Required for Synaptics MST hub which can put the LT in 312 * infinite loop by switching the VS between level 0 and level 1 313 * continuously. 314 */ 315 while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) && 316 (retry_count < LINK_TRAINING_MAX_CR_RETRY)) { 317 318 /* DPTX-to-DPIA */ 319 if (hop == repeater_cnt) { 320 /* Send SET_CONFIG(SET_LINK:LC,LR,LTTPR) to notify DPOA that 321 * non-transparent link training has started. 322 * This also enables the transmission of clk_sync packets. 323 */ 324 set_cfg_data = dpia_build_set_config_data( 325 DPIA_SET_CFG_SET_LINK, 326 link, 327 lt_settings); 328 status = core_link_send_set_config( 329 link, 330 DPIA_SET_CFG_SET_LINK, 331 set_cfg_data); 332 /* CR for this hop is considered successful as long as 333 * SET_CONFIG message is acknowledged by DPOA. 334 */ 335 if (status == DC_OK) 336 result = LINK_TRAINING_SUCCESS; 337 else 338 result = LINK_TRAINING_ABORT; 339 break; 340 } 341 342 /* DPOA-to-x */ 343 /* Instruct DPOA to transmit TPS1 then update DPCD. */ 344 if (retry_count == 0) { 345 status = convert_trng_ptn_to_trng_stg(lt_settings->pattern_for_cr, &ts); 346 if (status != DC_OK) { 347 result = LINK_TRAINING_ABORT; 348 break; 349 } 350 status = core_link_send_set_config( 351 link, 352 DPIA_SET_CFG_SET_TRAINING, 353 ts); 354 if (status != DC_OK) { 355 result = LINK_TRAINING_ABORT; 356 break; 357 } 358 status = dpcd_set_lt_pattern(link, lt_settings->pattern_for_cr, hop); 359 if (status != DC_OK) { 360 result = LINK_TRAINING_ABORT; 361 break; 362 } 363 } 364 365 /* Update DPOA drive settings then DPCD. DPOA does only adjusts 366 * drive settings for hops immediately downstream. 367 */ 368 if (hop == repeater_cnt - 1) { 369 set_cfg_data = dpia_build_set_config_data( 370 DPIA_SET_CFG_SET_VSPE, 371 link, 372 lt_settings); 373 status = core_link_send_set_config( 374 link, 375 DPIA_SET_CFG_SET_VSPE, 376 set_cfg_data); 377 if (status != DC_OK) { 378 result = LINK_TRAINING_ABORT; 379 break; 380 } 381 } 382 status = dpcd_set_lane_settings(link, lt_settings, hop); 383 if (status != DC_OK) { 384 result = LINK_TRAINING_ABORT; 385 break; 386 } 387 388 dp_wait_for_training_aux_rd_interval(link, wait_time_microsec); 389 390 /* Read status and adjustment requests from DPCD. */ 391 status = dp_get_lane_status_and_lane_adjust( 392 link, 393 lt_settings, 394 dpcd_lane_status, 395 &dpcd_lane_status_updated, 396 dpcd_lane_adjust, 397 hop); 398 if (status != DC_OK) { 399 result = LINK_TRAINING_ABORT; 400 break; 401 } 402 403 /* Check if clock recovery successful. */ 404 if (dp_is_cr_done(lane_count, dpcd_lane_status)) { 405 DC_LOG_HW_LINK_TRAINING("%s: Clock recovery OK\n", __func__); 406 result = LINK_TRAINING_SUCCESS; 407 break; 408 } 409 410 result = dp_get_cr_failure(lane_count, dpcd_lane_status); 411 412 if (dp_is_max_vs_reached(lt_settings)) 413 break; 414 415 /* Count number of attempts with same drive settings. 416 * Note: settings are the same for all lanes, 417 * so comparing first lane is sufficient. 418 */ 419 if ((lt_settings->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET == 420 dpcd_lane_adjust[0].bits.VOLTAGE_SWING_LANE) 421 && (lt_settings->dpcd_lane_settings[0].bits.PRE_EMPHASIS_SET == 422 dpcd_lane_adjust[0].bits.PRE_EMPHASIS_LANE)) 423 retries_cr++; 424 else 425 retries_cr = 0; 426 427 /* Update VS/PE. */ 428 dp_decide_lane_settings(lt_settings, dpcd_lane_adjust, 429 lt_settings->hw_lane_settings, 430 lt_settings->dpcd_lane_settings); 431 retry_count++; 432 } 433 434 DC_LOG_HW_LINK_TRAINING( 435 "%s\n DPIA(%d) clock recovery\n -hop(%d)\n - result(%d)\n - retries(%d)\n - status(%d)\n", 436 __func__, 437 link->link_id.enum_id - ENUM_ID_1, 438 hop, 439 result, 440 retry_count, 441 status); 442 443 return result; 444} 445 446/* Execute clock recovery phase of link training in transparent LTTPR mode: 447 * - Driver only issues DPCD transactions and leaves USB4 tunneling (SET_CONFIG) messages to DPIA. 448 * - Driver writes TPS1 to DPCD to kick off training. 449 * - Clock recovery (CR) for link is handled by DPOA, which reports result to DPIA on completion. 450 * - DPIA communicates result to driver by updating CR status when driver reads DPCD. 451 * 452 * @param link DPIA link being trained. 453 * @param lt_settings link_setting and drive settings (voltage swing and pre-emphasis). 454 */ 455static enum link_training_result dpia_training_cr_transparent( 456 struct dc_link *link, 457 const struct link_resource *link_res, 458 struct link_training_settings *lt_settings) 459{ 460 enum link_training_result result = LINK_TRAINING_CR_FAIL_LANE0; 461 enum dc_status status; 462 uint32_t retries_cr = 0; /* Number of consecutive attempts with same VS or PE. */ 463 uint32_t retry_count = 0; 464 uint32_t wait_time_microsec = lt_settings->cr_pattern_time; 465 enum dc_lane_count lane_count = lt_settings->link_settings.lane_count; 466 union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0}; 467 union lane_align_status_updated dpcd_lane_status_updated = {0}; 468 union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0}; 469 470 /* Cap of LINK_TRAINING_MAX_CR_RETRY attempts at clock recovery. 471 * Fix inherited from perform_clock_recovery_sequence() - 472 * the DP equivalent of this function: 473 * Required for Synaptics MST hub which can put the LT in 474 * infinite loop by switching the VS between level 0 and level 1 475 * continuously. 476 */ 477 while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) && 478 (retry_count < LINK_TRAINING_MAX_CR_RETRY)) { 479 480 /* Write TPS1 (not VS or PE) to DPCD to start CR phase. 481 * DPIA sends SET_CONFIG(SET_LINK) to notify DPOA to 482 * start link training. 483 */ 484 if (retry_count == 0) { 485 status = dpcd_set_lt_pattern(link, lt_settings->pattern_for_cr, DPRX); 486 if (status != DC_OK) { 487 result = LINK_TRAINING_ABORT; 488 break; 489 } 490 } 491 492 dp_wait_for_training_aux_rd_interval(link, wait_time_microsec); 493 494 /* Read status and adjustment requests from DPCD. */ 495 status = dp_get_lane_status_and_lane_adjust( 496 link, 497 lt_settings, 498 dpcd_lane_status, 499 &dpcd_lane_status_updated, 500 dpcd_lane_adjust, 501 DPRX); 502 if (status != DC_OK) { 503 result = LINK_TRAINING_ABORT; 504 break; 505 } 506 507 /* Check if clock recovery successful. */ 508 if (dp_is_cr_done(lane_count, dpcd_lane_status)) { 509 DC_LOG_HW_LINK_TRAINING("%s: Clock recovery OK\n", __func__); 510 result = LINK_TRAINING_SUCCESS; 511 break; 512 } 513 514 result = dp_get_cr_failure(lane_count, dpcd_lane_status); 515 516 if (dp_is_max_vs_reached(lt_settings)) 517 break; 518 519 /* Count number of attempts with same drive settings. 520 * Note: settings are the same for all lanes, 521 * so comparing first lane is sufficient. 522 */ 523 if ((lt_settings->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET == 524 dpcd_lane_adjust[0].bits.VOLTAGE_SWING_LANE) 525 && (lt_settings->dpcd_lane_settings[0].bits.PRE_EMPHASIS_SET == 526 dpcd_lane_adjust[0].bits.PRE_EMPHASIS_LANE)) 527 retries_cr++; 528 else 529 retries_cr = 0; 530 531 /* Update VS/PE. */ 532 dp_decide_lane_settings(lt_settings, dpcd_lane_adjust, 533 lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings); 534 retry_count++; 535 } 536 537 DC_LOG_HW_LINK_TRAINING("%s\n DPIA(%d) clock recovery\n -hop(%d)\n - result(%d)\n - retries(%d)\n", 538 __func__, 539 link->link_id.enum_id - ENUM_ID_1, 540 DPRX, 541 result, 542 retry_count); 543 544 return result; 545} 546 547/* Execute clock recovery phase of link training for specified hop in display 548 * path. 549 * 550 * @param link DPIA link being trained. 551 * @param lt_settings link_setting and drive settings (voltage swing and pre-emphasis). 552 * @param hop Hop in display path. DPRX = 0. 553 */ 554static enum link_training_result dpia_training_cr_phase( 555 struct dc_link *link, 556 const struct link_resource *link_res, 557 struct link_training_settings *lt_settings, 558 uint32_t hop) 559{ 560 enum link_training_result result = LINK_TRAINING_CR_FAIL_LANE0; 561 562 if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) 563 result = dpia_training_cr_non_transparent(link, link_res, lt_settings, hop); 564 else 565 result = dpia_training_cr_transparent(link, link_res, lt_settings); 566 567 return result; 568} 569 570/* Execute equalization phase of link training for specified hop in display 571 * path in non-transparent mode: 572 * - driver issues both DPCD and SET_CONFIG transactions. 573 * - TPSx is transmitted for any hops downstream of DPOA. 574 * - Drive (VS/PE) only transmitted for the hop immediately downstream of DPOA. 575 * - EQ for the first hop (DPTX-to-DPIA) is assumed to be successful. 576 * - DPRX EQ only reported successful when both DPRX and DPIA requirements (clk sync packets sent) fulfilled. 577 * 578 * @param link DPIA link being trained. 579 * @param lt_settings link_setting and drive settings (voltage swing and pre-emphasis). 580 * @param hop Hop in display path. DPRX = 0. 581 */ 582static enum link_training_result dpia_training_eq_non_transparent( 583 struct dc_link *link, 584 const struct link_resource *link_res, 585 struct link_training_settings *lt_settings, 586 uint32_t hop) 587{ 588 enum link_training_result result = LINK_TRAINING_EQ_FAIL_EQ; 589 uint8_t repeater_cnt = 0; /* Number of hops/repeaters in display path. */ 590 uint32_t retries_eq = 0; 591 enum dc_status status = DC_ERROR_UNEXPECTED; 592 enum dc_dp_training_pattern tr_pattern; 593 uint32_t wait_time_microsec = 0; 594 enum dc_lane_count lane_count = lt_settings->link_settings.lane_count; 595 union lane_align_status_updated dpcd_lane_status_updated = {0}; 596 union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0}; 597 union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0}; 598 uint8_t set_cfg_data; 599 enum dpia_set_config_ts ts; 600 601 /* Training pattern is TPS4 for repeater; 602 * TPS2/3/4 for DPRX depending on what it supports. 603 */ 604 if (hop == DPRX) 605 tr_pattern = lt_settings->pattern_for_eq; 606 else 607 tr_pattern = DP_TRAINING_PATTERN_SEQUENCE_4; 608 609 repeater_cnt = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt); 610 611 for (retries_eq = 0; retries_eq < LINK_TRAINING_MAX_RETRY_COUNT; retries_eq++) { 612 613 /* DPTX-to-DPIA equalization always successful. */ 614 if (hop == repeater_cnt) { 615 result = LINK_TRAINING_SUCCESS; 616 break; 617 } 618 619 /* Instruct DPOA to transmit TPSn then update DPCD. */ 620 if (retries_eq == 0) { 621 status = convert_trng_ptn_to_trng_stg(tr_pattern, &ts); 622 if (status != DC_OK) { 623 result = LINK_TRAINING_ABORT; 624 break; 625 } 626 status = core_link_send_set_config( 627 link, 628 DPIA_SET_CFG_SET_TRAINING, 629 ts); 630 if (status != DC_OK) { 631 result = LINK_TRAINING_ABORT; 632 break; 633 } 634 status = dpcd_set_lt_pattern(link, tr_pattern, hop); 635 if (status != DC_OK) { 636 result = LINK_TRAINING_ABORT; 637 break; 638 } 639 } 640 641 /* Update DPOA drive settings then DPCD. DPOA only adjusts 642 * drive settings for hop immediately downstream. 643 */ 644 if (hop == repeater_cnt - 1) { 645 set_cfg_data = dpia_build_set_config_data( 646 DPIA_SET_CFG_SET_VSPE, 647 link, 648 lt_settings); 649 status = core_link_send_set_config( 650 link, 651 DPIA_SET_CFG_SET_VSPE, 652 set_cfg_data); 653 if (status != DC_OK) { 654 result = LINK_TRAINING_ABORT; 655 break; 656 } 657 } 658 status = dpcd_set_lane_settings(link, lt_settings, hop); 659 if (status != DC_OK) { 660 result = LINK_TRAINING_ABORT; 661 break; 662 } 663 664 /* Extend wait time on second equalisation attempt on final hop to 665 * ensure clock sync packets have been sent. 666 */ 667 if (hop == DPRX && retries_eq == 1) 668 wait_time_microsec = max(wait_time_microsec, (uint32_t) DPIA_CLK_SYNC_DELAY); 669 else 670 wait_time_microsec = dpia_get_eq_aux_rd_interval(link, lt_settings, hop); 671 672 dp_wait_for_training_aux_rd_interval(link, wait_time_microsec); 673 674 /* Read status and adjustment requests from DPCD. */ 675 status = dp_get_lane_status_and_lane_adjust( 676 link, 677 lt_settings, 678 dpcd_lane_status, 679 &dpcd_lane_status_updated, 680 dpcd_lane_adjust, 681 hop); 682 if (status != DC_OK) { 683 result = LINK_TRAINING_ABORT; 684 break; 685 } 686 687 /* CR can still fail during EQ phase. Fail training if CR fails. */ 688 if (!dp_is_cr_done(lane_count, dpcd_lane_status)) { 689 result = LINK_TRAINING_EQ_FAIL_CR; 690 break; 691 } 692 693 if (dp_is_ch_eq_done(lane_count, dpcd_lane_status) && 694 dp_is_symbol_locked(link->cur_link_settings.lane_count, dpcd_lane_status) && 695 dp_is_interlane_aligned(dpcd_lane_status_updated)) { 696 result = LINK_TRAINING_SUCCESS; 697 break; 698 } 699 700 /* Update VS/PE. */ 701 dp_decide_lane_settings(lt_settings, dpcd_lane_adjust, 702 lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings); 703 } 704 705 DC_LOG_HW_LINK_TRAINING( 706 "%s\n DPIA(%d) equalization\n - hop(%d)\n - result(%d)\n - retries(%d)\n - status(%d)\n", 707 __func__, 708 link->link_id.enum_id - ENUM_ID_1, 709 hop, 710 result, 711 retries_eq, 712 status); 713 714 return result; 715} 716 717/* Execute equalization phase of link training for specified hop in display 718 * path in transparent LTTPR mode: 719 * - driver only issues DPCD transactions leaves USB4 tunneling (SET_CONFIG) messages to DPIA. 720 * - driver writes TPSx to DPCD to notify DPIA that is in equalization phase. 721 * - equalization (EQ) for link is handled by DPOA, which reports result to DPIA on completion. 722 * - DPIA communicates result to driver by updating EQ status when driver reads DPCD. 723 * 724 * @param link DPIA link being trained. 725 * @param lt_settings link_setting and drive settings (voltage swing and pre-emphasis). 726 * @param hop Hop in display path. DPRX = 0. 727 */ 728static enum link_training_result dpia_training_eq_transparent( 729 struct dc_link *link, 730 const struct link_resource *link_res, 731 struct link_training_settings *lt_settings) 732{ 733 enum link_training_result result = LINK_TRAINING_EQ_FAIL_EQ; 734 uint32_t retries_eq = 0; 735 enum dc_status status; 736 enum dc_dp_training_pattern tr_pattern = lt_settings->pattern_for_eq; 737 uint32_t wait_time_microsec; 738 enum dc_lane_count lane_count = lt_settings->link_settings.lane_count; 739 union lane_align_status_updated dpcd_lane_status_updated = {0}; 740 union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0}; 741 union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0}; 742 743 wait_time_microsec = dpia_get_eq_aux_rd_interval(link, lt_settings, DPRX); 744 745 for (retries_eq = 0; retries_eq < LINK_TRAINING_MAX_RETRY_COUNT; retries_eq++) { 746 747 if (retries_eq == 0) { 748 status = dpcd_set_lt_pattern(link, tr_pattern, DPRX); 749 if (status != DC_OK) { 750 result = LINK_TRAINING_ABORT; 751 break; 752 } 753 } 754 755 dp_wait_for_training_aux_rd_interval(link, wait_time_microsec); 756 757 /* Read status and adjustment requests from DPCD. */ 758 status = dp_get_lane_status_and_lane_adjust( 759 link, 760 lt_settings, 761 dpcd_lane_status, 762 &dpcd_lane_status_updated, 763 dpcd_lane_adjust, 764 DPRX); 765 if (status != DC_OK) { 766 result = LINK_TRAINING_ABORT; 767 break; 768 } 769 770 /* CR can still fail during EQ phase. Fail training if CR fails. */ 771 if (!dp_is_cr_done(lane_count, dpcd_lane_status)) { 772 result = LINK_TRAINING_EQ_FAIL_CR; 773 break; 774 } 775 776 if (dp_is_ch_eq_done(lane_count, dpcd_lane_status) && 777 dp_is_symbol_locked(link->cur_link_settings.lane_count, dpcd_lane_status)) { 778 /* Take into consideration corner case for DP 1.4a LL Compliance CTS as USB4 779 * has to share encoders unlike DP and USBC 780 */ 781 if (dp_is_interlane_aligned(dpcd_lane_status_updated) || (link->skip_fallback_on_link_loss && retries_eq)) { 782 result = LINK_TRAINING_SUCCESS; 783 break; 784 } 785 } 786 787 /* Update VS/PE. */ 788 dp_decide_lane_settings(lt_settings, dpcd_lane_adjust, 789 lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings); 790 } 791 792 DC_LOG_HW_LINK_TRAINING("%s\n DPIA(%d) equalization\n - hop(%d)\n - result(%d)\n - retries(%d)\n", 793 __func__, 794 link->link_id.enum_id - ENUM_ID_1, 795 DPRX, 796 result, 797 retries_eq); 798 799 return result; 800} 801 802/* Execute equalization phase of link training for specified hop in display 803 * path. 804 * 805 * @param link DPIA link being trained. 806 * @param lt_settings link_setting and drive settings (voltage swing and pre-emphasis). 807 * @param hop Hop in display path. DPRX = 0. 808 */ 809static enum link_training_result dpia_training_eq_phase( 810 struct dc_link *link, 811 const struct link_resource *link_res, 812 struct link_training_settings *lt_settings, 813 uint32_t hop) 814{ 815 enum link_training_result result; 816 817 if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) 818 result = dpia_training_eq_non_transparent(link, link_res, lt_settings, hop); 819 else 820 result = dpia_training_eq_transparent(link, link_res, lt_settings); 821 822 return result; 823} 824 825/* End training of specified hop in display path. */ 826static enum dc_status dpcd_clear_lt_pattern( 827 struct dc_link *link, 828 uint32_t hop) 829{ 830 union dpcd_training_pattern dpcd_pattern = {0}; 831 uint32_t dpcd_tps_offset = DP_TRAINING_PATTERN_SET; 832 enum dc_status status; 833 834 if (hop != DPRX) 835 dpcd_tps_offset = DP_TRAINING_PATTERN_SET_PHY_REPEATER1 + 836 ((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (hop - 1)); 837 838 status = core_link_write_dpcd( 839 link, 840 dpcd_tps_offset, 841 &dpcd_pattern.raw, 842 sizeof(dpcd_pattern.raw)); 843 844 return status; 845} 846 847/* End training of specified hop in display path. 848 * 849 * In transparent LTTPR mode: 850 * - driver clears training pattern for the specified hop in DPCD. 851 * In non-transparent LTTPR mode: 852 * - in addition to clearing training pattern, driver issues USB4 tunneling 853 * (SET_CONFIG) messages to notify DPOA when training is done for first hop 854 * (DPTX-to-DPIA) and last hop (DPRX). 855 * 856 * @param link DPIA link being trained. 857 * @param hop Hop in display path. DPRX = 0. 858 */ 859static enum link_training_result dpia_training_end( 860 struct dc_link *link, 861 struct link_training_settings *lt_settings, 862 uint32_t hop) 863{ 864 enum link_training_result result = LINK_TRAINING_SUCCESS; 865 uint8_t repeater_cnt = 0; /* Number of hops/repeaters in display path. */ 866 enum dc_status status; 867 868 if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) { 869 870 repeater_cnt = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt); 871 872 if (hop == repeater_cnt) { /* DPTX-to-DPIA */ 873 /* Send SET_CONFIG(SET_TRAINING:0xff) to notify DPOA that 874 * DPTX-to-DPIA hop trained. No DPCD write needed for first hop. 875 */ 876 status = core_link_send_set_config( 877 link, 878 DPIA_SET_CFG_SET_TRAINING, 879 DPIA_TS_UFP_DONE); 880 if (status != DC_OK) 881 result = LINK_TRAINING_ABORT; 882 } else { /* DPOA-to-x */ 883 /* Write 0x0 to TRAINING_PATTERN_SET */ 884 status = dpcd_clear_lt_pattern(link, hop); 885 if (status != DC_OK) 886 result = LINK_TRAINING_ABORT; 887 } 888 889 /* Notify DPOA that non-transparent link training of DPRX done. */ 890 if (hop == DPRX && result != LINK_TRAINING_ABORT) { 891 status = core_link_send_set_config( 892 link, 893 DPIA_SET_CFG_SET_TRAINING, 894 DPIA_TS_DPRX_DONE); 895 if (status != DC_OK) 896 result = LINK_TRAINING_ABORT; 897 } 898 899 } else { /* non-LTTPR or transparent LTTPR. */ 900 901 /* Write 0x0 to TRAINING_PATTERN_SET */ 902 status = dpcd_clear_lt_pattern(link, hop); 903 if (status != DC_OK) 904 result = LINK_TRAINING_ABORT; 905 906 } 907 908 DC_LOG_HW_LINK_TRAINING("%s\n DPIA(%d) end\n - hop(%d)\n - result(%d)\n - LTTPR mode(%d)\n", 909 __func__, 910 link->link_id.enum_id - ENUM_ID_1, 911 hop, 912 result, 913 lt_settings->lttpr_mode); 914 915 return result; 916} 917 918/* Return status read interval during equalization phase. */ 919uint32_t dpia_get_eq_aux_rd_interval( 920 const struct dc_link *link, 921 const struct link_training_settings *lt_settings, 922 uint32_t hop) 923{ 924 /* Check debug option for extending aux read interval. */ 925 if (link->dc->debug.dpia_debug.bits.extend_aux_rd_interval) 926 return DPIA_DEBUG_EXTENDED_AUX_RD_INTERVAL_US; 927 else if (hop == DPRX) 928 return lt_settings->eq_pattern_time; 929 else 930 return dp_translate_training_aux_read_interval( 931 link->dpcd_caps.lttpr_caps.aux_rd_interval[hop - 1]); 932} 933 934/* When aborting training of specified hop in display path, clean up by: 935 * - Attempting to clear DPCD TRAINING_PATTERN_SET, LINK_BW_SET and LANE_COUNT_SET. 936 * - Sending SET_CONFIG(SET_LINK) with lane count and link rate set to 0. 937 * 938 * @param link DPIA link being trained. 939 * @param hop Hop in display path. DPRX = 0. 940 */ 941void dpia_training_abort( 942 struct dc_link *link, 943 struct link_training_settings *lt_settings, 944 uint32_t hop) 945{ 946 uint8_t data = 0; 947 uint32_t dpcd_tps_offset = DP_TRAINING_PATTERN_SET; 948 949 DC_LOG_HW_LINK_TRAINING("%s\n DPIA(%d) aborting\n - LTTPR mode(%d)\n - HPD(%d)\n", 950 __func__, 951 link->link_id.enum_id - ENUM_ID_1, 952 lt_settings->lttpr_mode, 953 link->is_hpd_pending); 954 955 /* Abandon clean-up if sink unplugged. */ 956 if (link->is_hpd_pending) 957 return; 958 959 if (hop != DPRX) 960 dpcd_tps_offset = DP_TRAINING_PATTERN_SET_PHY_REPEATER1 + 961 ((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (hop - 1)); 962 963 core_link_write_dpcd(link, dpcd_tps_offset, &data, 1); 964 core_link_write_dpcd(link, DP_LINK_BW_SET, &data, 1); 965 core_link_write_dpcd(link, DP_LANE_COUNT_SET, &data, 1); 966 967 if (!link->dc->config.consolidated_dpia_dp_lt) 968 core_link_send_set_config(link, DPIA_SET_CFG_SET_LINK, data); 969} 970 971void dpia_set_tps_notification( 972 struct dc_link *link, 973 const struct link_training_settings *lt_settings, 974 uint8_t pattern, 975 uint32_t hop) 976{ 977 uint8_t repeater_cnt = 0; /* Number of hops/repeaters in display path. */ 978 979 if (lt_settings->lttpr_mode != LTTPR_MODE_NON_TRANSPARENT || pattern == DPCD_TRAINING_PATTERN_VIDEOIDLE) 980 return; 981 982 repeater_cnt = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt); 983 984 if (hop != repeater_cnt) 985 dc_process_dmub_dpia_set_tps_notification(link->ctx->dc, link->link_index, pattern); 986} 987 988enum link_training_result dpia_perform_link_training( 989 struct dc_link *link, 990 const struct link_resource *link_res, 991 const struct dc_link_settings *link_setting, 992 bool skip_video_pattern) 993{ 994 enum link_training_result result; 995 struct link_training_settings lt_settings = {0}; 996 uint8_t repeater_cnt = 0; /* Number of hops/repeaters in display path. */ 997 int8_t repeater_id; /* Current hop. */ 998 999 struct dc_link_settings link_settings = *link_setting; // non-const copy to pass in 1000 1001 lt_settings.lttpr_mode = dp_decide_lttpr_mode(link, &link_settings); 1002 1003 /* Configure link as prescribed in link_setting and set LTTPR mode. */ 1004 result = dpia_configure_link(link, link_res, link_setting, <_settings); 1005 if (result != LINK_TRAINING_SUCCESS) 1006 return result; 1007 1008 if (lt_settings.lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) 1009 repeater_cnt = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt); 1010 1011 /* Train each hop in turn starting with the one closest to DPTX. 1012 * In transparent or non-LTTPR mode, train only the final hop (DPRX). 1013 */ 1014 for (repeater_id = repeater_cnt; repeater_id >= 0; repeater_id--) { 1015 /* Clock recovery. */ 1016 result = dpia_training_cr_phase(link, link_res, <_settings, repeater_id); 1017 if (result != LINK_TRAINING_SUCCESS) 1018 break; 1019 1020 /* Equalization. */ 1021 result = dpia_training_eq_phase(link, link_res, <_settings, repeater_id); 1022 if (result != LINK_TRAINING_SUCCESS) 1023 break; 1024 1025 /* Stop training hop. */ 1026 result = dpia_training_end(link, <_settings, repeater_id); 1027 if (result != LINK_TRAINING_SUCCESS) 1028 break; 1029 } 1030 1031 /* Double-check link status if training successful; gracefully abort 1032 * training of current hop if training failed due to message tunneling 1033 * failure; end training of hop if training ended conventionally and 1034 * falling back to lower bandwidth settings possible. 1035 */ 1036 if (result == LINK_TRAINING_SUCCESS) { 1037 fsleep(5000); 1038 if (!link->skip_fallback_on_link_loss) 1039 result = dp_check_link_loss_status(link, <_settings); 1040 } else if (result == LINK_TRAINING_ABORT) 1041 dpia_training_abort(link, <_settings, repeater_id); 1042 else 1043 dpia_training_end(link, <_settings, repeater_id); 1044 1045 return result; 1046}
~/
Read-only file preview.