FILE
link_dp_training_8b_10b.c
File preview with syntax highlighting.
linux-6.19.14/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.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 file implements dp 8b/10b link training software policies and 28 * sequences. 29 */ 30#include "link_dp_training_8b_10b.h" 31#include "link_dpcd.h" 32#include "link_dp_phy.h" 33#include "link_dp_capability.h" 34 35#define DC_LOGGER \ 36 link->ctx->logger 37 38static void get_default_8b_10b_lttpr_aux_rd_interval( 39 union training_aux_rd_interval *training_rd_interval) 40{ 41 /* LTTPR are required to program DPCD 0000Eh to 0x4 (16ms) upon AUX 42 * read reply to this register. Since old sinks with DPCD rev 1.1 43 * and earlier may not support this register, assume the mandatory 44 * value is programmed by the LTTPR to avoid AUX timeout issues. 45 */ 46 training_rd_interval->raw = 0x4; 47} 48 49static int32_t get_cr_training_aux_rd_interval(struct dc_link *link, 50 const struct dc_link_settings *link_settings, 51 enum lttpr_mode lttpr_mode) 52{ 53 union training_aux_rd_interval training_rd_interval; 54 uint32_t wait_in_micro_secs = 100; 55 56 memset(&training_rd_interval, 0, sizeof(training_rd_interval)); 57 if (link_dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING) { 58 if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12) 59 core_link_read_dpcd( 60 link, 61 DP_TRAINING_AUX_RD_INTERVAL, 62 (uint8_t *)&training_rd_interval, 63 sizeof(training_rd_interval)); 64 else if (dp_is_lttpr_present(link)) 65 get_default_8b_10b_lttpr_aux_rd_interval(&training_rd_interval); 66 67 if (training_rd_interval.raw != 0) { 68 if (lttpr_mode != LTTPR_MODE_NON_TRANSPARENT) 69 wait_in_micro_secs = 400; 70 if (training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL) 71 wait_in_micro_secs = training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL * 4000; 72 } 73 } 74 return wait_in_micro_secs; 75} 76 77static uint32_t get_eq_training_aux_rd_interval( 78 struct dc_link *link, 79 const struct dc_link_settings *link_settings) 80{ 81 union training_aux_rd_interval training_rd_interval; 82 83 memset(&training_rd_interval, 0, sizeof(training_rd_interval)); 84 if (link_dp_get_encoding_format(link_settings) == DP_128b_132b_ENCODING) { 85 core_link_read_dpcd( 86 link, 87 DP_128B132B_TRAINING_AUX_RD_INTERVAL, 88 (uint8_t *)&training_rd_interval, 89 sizeof(training_rd_interval)); 90 } else if (link_dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING) { 91 if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12) 92 core_link_read_dpcd( 93 link, 94 DP_TRAINING_AUX_RD_INTERVAL, 95 (uint8_t *)&training_rd_interval, 96 sizeof(training_rd_interval)); 97 else if (dp_is_lttpr_present(link)) 98 get_default_8b_10b_lttpr_aux_rd_interval(&training_rd_interval); 99 } 100 101 switch (training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL) { 102 case 0: return 400; 103 case 1: return 4000; 104 case 2: return 8000; 105 case 3: return 12000; 106 case 4: return 16000; 107 case 5: return 32000; 108 case 6: return 64000; 109 default: return 400; 110 } 111} 112 113void decide_8b_10b_training_settings( 114 struct dc_link *link, 115 const struct link_resource *link_res, 116 const struct dc_link_settings *link_setting, 117 struct link_training_settings *lt_settings) 118{ 119 memset(lt_settings, '\0', sizeof(struct link_training_settings)); 120 121 /* Initialize link settings */ 122 lt_settings->link_settings.use_link_rate_set = link_setting->use_link_rate_set; 123 lt_settings->link_settings.link_rate_set = link_setting->link_rate_set; 124 lt_settings->link_settings.link_rate = link_setting->link_rate; 125 lt_settings->link_settings.lane_count = link_setting->lane_count; 126 /* TODO hard coded to SS for now 127 * lt_settings.link_settings.link_spread = 128 * dal_display_path_is_ss_supported( 129 * path_mode->display_path) ? 130 * LINK_SPREAD_05_DOWNSPREAD_30KHZ : 131 * LINK_SPREAD_DISABLED; 132 */ 133 lt_settings->link_settings.link_spread = link->dp_ss_off ? 134 LINK_SPREAD_DISABLED : LINK_SPREAD_05_DOWNSPREAD_30KHZ; 135 lt_settings->eq_pattern_time = get_eq_training_aux_rd_interval(link, link_setting); 136 lt_settings->pattern_for_cr = decide_cr_training_pattern(link_setting); 137 lt_settings->pattern_for_eq = decide_eq_training_pattern(link, link_res, link_setting); 138 lt_settings->enhanced_framing = 1; 139 lt_settings->should_set_fec_ready = true; 140 lt_settings->disallow_per_lane_settings = true; 141 lt_settings->always_match_dpcd_with_hw_lane_settings = true; 142 lt_settings->lttpr_mode = dp_decide_8b_10b_lttpr_mode(link); 143 lt_settings->cr_pattern_time = get_cr_training_aux_rd_interval(link, link_setting, lt_settings->lttpr_mode); 144 dp_hw_to_dpcd_lane_settings(lt_settings, lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings); 145 146 /* Some embedded LTTPRs rely on receiving TPS2 before LT to interop reliably with sensitive VGA dongles 147 * This allows these LTTPRs to minimize freq/phase and skew variation during lock and deskew sequences 148 */ 149 if ((link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK) == 150 AMD_EXT_DISPLAY_PATH_CAPS__DP_EARLY_8B10B_TPS2) { 151 lt_settings->lttpr_early_tps2 = true; 152 } 153} 154 155enum lttpr_mode dp_decide_8b_10b_lttpr_mode(struct dc_link *link) 156{ 157 bool is_lttpr_present = dp_is_lttpr_present(link); 158 bool vbios_lttpr_force_non_transparent = link->dc->caps.vbios_lttpr_enable; 159 bool vbios_lttpr_aware = link->dc->caps.vbios_lttpr_aware; 160 161 if (!is_lttpr_present) 162 return LTTPR_MODE_NON_LTTPR; 163 164 if (vbios_lttpr_aware) { 165 if (vbios_lttpr_force_non_transparent) { 166 DC_LOG_DC("chose LTTPR_MODE_NON_TRANSPARENT due to VBIOS DCE_INFO_CAPS_LTTPR_SUPPORT_ENABLE set to 1.\n"); 167 return LTTPR_MODE_NON_TRANSPARENT; 168 } else { 169 DC_LOG_DC("chose LTTPR_MODE_NON_TRANSPARENT by default due to VBIOS not set DCE_INFO_CAPS_LTTPR_SUPPORT_ENABLE set to 1.\n"); 170 return LTTPR_MODE_TRANSPARENT; 171 } 172 } 173 174 if (link->dc->config.allow_lttpr_non_transparent_mode.bits.DP1_4A && 175 link->dc->caps.extended_aux_timeout_support) { 176 DC_LOG_DC("chose LTTPR_MODE_NON_TRANSPARENT by default and dc->config.allow_lttpr_non_transparent_mode.bits.DP1_4A set to 1.\n"); 177 return LTTPR_MODE_NON_TRANSPARENT; 178 } 179 180 DC_LOG_DC("chose LTTPR_MODE_NON_LTTPR.\n"); 181 return LTTPR_MODE_NON_LTTPR; 182} 183 184static void set_link_settings_and_perform_early_tps2_retimer_pre_lt_sequence(struct dc_link *link, 185 const struct link_resource *link_res, 186 struct link_training_settings *lt_settings, 187 uint32_t lttpr_count) 188{ 189 /* Vendor-specific LTTPR early TPS2 sequence: 190 * 1. Output TPS2 191 * 2. Wait 400us 192 * 3. Set link settings as usual 193 * 4. Write TPS1 to DP_TRAINING_PATTERN_SET_PHY_REPEATERx targeting LTTPR closest to host 194 * 5. Wait 1ms 195 * 6. Begin link training as usual 196 * */ 197 198 uint32_t closest_lttpr_address_offset = dp_get_closest_lttpr_offset(lttpr_count); 199 200 union dpcd_training_pattern dpcd_pattern = {0}; 201 202 dpcd_pattern.v1_4.TRAINING_PATTERN_SET = 1; 203 dpcd_pattern.v1_4.SCRAMBLING_DISABLE = 1; 204 205 DC_LOG_HW_LINK_TRAINING("%s\n GPU sends TPS2. Wait 400us.\n", __func__); 206 207 dp_set_hw_training_pattern(link, link_res, DP_TRAINING_PATTERN_SEQUENCE_2, DPRX); 208 209 dp_set_hw_lane_settings(link, link_res, lt_settings, DPRX); 210 211 udelay(400); 212 213 dpcd_set_link_settings(link, lt_settings); 214 215 core_link_write_dpcd(link, DP_TRAINING_PATTERN_SET_PHY_REPEATER1 + closest_lttpr_address_offset, &dpcd_pattern.raw, 1); 216 217 udelay(1000); 218 } 219 220enum link_training_result perform_8b_10b_clock_recovery_sequence( 221 struct dc_link *link, 222 const struct link_resource *link_res, 223 struct link_training_settings *lt_settings, 224 uint32_t offset) 225{ 226 enum dc_status status; 227 uint32_t retries_cr; 228 uint32_t retry_count; 229 uint32_t wait_time_microsec; 230 enum dc_lane_count lane_count = lt_settings->link_settings.lane_count; 231 union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX]; 232 union lane_align_status_updated dpcd_lane_status_updated; 233 union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0}; 234 235 retries_cr = 0; 236 retry_count = 0; 237 238 memset(&dpcd_lane_status, '\0', sizeof(dpcd_lane_status)); 239 memset(&dpcd_lane_status_updated, '\0', 240 sizeof(dpcd_lane_status_updated)); 241 242 if (!link->ctx->dc->work_arounds.lt_early_cr_pattern) 243 dp_set_hw_training_pattern(link, link_res, lt_settings->pattern_for_cr, offset); 244 245 /* najeeb - The synaptics MST hub can put the LT in 246 * infinite loop by switching the VS 247 */ 248 /* between level 0 and level 1 continuously, here 249 * we try for CR lock for LinkTrainingMaxCRRetry count*/ 250 while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) && 251 (retry_count < LINK_TRAINING_MAX_CR_RETRY)) { 252 253 254 /* 1. call HWSS to set lane settings*/ 255 dp_set_hw_lane_settings( 256 link, 257 link_res, 258 lt_settings, 259 offset); 260 261 /* 2. update DPCD of the receiver*/ 262 if (!retry_count) 263 /* EPR #361076 - write as a 5-byte burst, 264 * but only for the 1-st iteration.*/ 265 dpcd_set_lt_pattern_and_lane_settings( 266 link, 267 lt_settings, 268 lt_settings->pattern_for_cr, 269 offset); 270 else 271 dpcd_set_lane_settings( 272 link, 273 lt_settings, 274 offset); 275 276 /* 3. wait receiver to lock-on*/ 277 wait_time_microsec = lt_settings->cr_pattern_time; 278 279 dp_wait_for_training_aux_rd_interval( 280 link, 281 wait_time_microsec); 282 283 /* 4. Read lane status and requested drive 284 * settings as set by the sink 285 */ 286 status = dp_get_lane_status_and_lane_adjust( 287 link, 288 lt_settings, 289 dpcd_lane_status, 290 &dpcd_lane_status_updated, 291 dpcd_lane_adjust, 292 offset); 293 294 if (dp_check_dpcd_reqeust_status(link, status)) 295 return LINK_TRAINING_ABORT; 296 297 /* 5. check CR done*/ 298 if (dp_is_cr_done(lane_count, dpcd_lane_status)) { 299 DC_LOG_HW_LINK_TRAINING("%s: Clock recovery OK\n", __func__); 300 return LINK_TRAINING_SUCCESS; 301 } 302 303 /* 6. max VS reached*/ 304 if ((link_dp_get_encoding_format(<_settings->link_settings) == 305 DP_8b_10b_ENCODING) && 306 dp_is_max_vs_reached(lt_settings)) 307 break; 308 309 /* 7. same lane settings*/ 310 /* Note: settings are the same for all lanes, 311 * so comparing first lane is sufficient*/ 312 if ((link_dp_get_encoding_format(<_settings->link_settings) == DP_8b_10b_ENCODING) && 313 lt_settings->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET == 314 dpcd_lane_adjust[0].bits.VOLTAGE_SWING_LANE) 315 retries_cr++; 316 else if ((link_dp_get_encoding_format(<_settings->link_settings) == DP_128b_132b_ENCODING) && 317 lt_settings->dpcd_lane_settings[0].tx_ffe.PRESET_VALUE == 318 dpcd_lane_adjust[0].tx_ffe.PRESET_VALUE) 319 retries_cr++; 320 else 321 retries_cr = 0; 322 323 /* 8. update VS/PE/PC2 in lt_settings*/ 324 dp_decide_lane_settings(lt_settings, dpcd_lane_adjust, 325 lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings); 326 retry_count++; 327 } 328 329 if (retry_count >= LINK_TRAINING_MAX_CR_RETRY) { 330 ASSERT(0); 331 DC_LOG_ERROR("%s: Link Training Error, could not get CR after %d tries. Possibly voltage swing issue", 332 __func__, 333 LINK_TRAINING_MAX_CR_RETRY); 334 335 } 336 337 return dp_get_cr_failure(lane_count, dpcd_lane_status); 338} 339 340enum link_training_result perform_8b_10b_channel_equalization_sequence( 341 struct dc_link *link, 342 const struct link_resource *link_res, 343 struct link_training_settings *lt_settings, 344 uint32_t offset) 345{ 346 enum dc_status status; 347 enum dc_dp_training_pattern tr_pattern; 348 uint32_t retries_ch_eq; 349 uint32_t wait_time_microsec; 350 enum dc_lane_count lane_count = lt_settings->link_settings.lane_count; 351 union lane_align_status_updated dpcd_lane_status_updated = {0}; 352 union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0}; 353 union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0}; 354 355 /* Note: also check that TPS4 is a supported feature*/ 356 tr_pattern = lt_settings->pattern_for_eq; 357 358 if (is_repeater(lt_settings, offset) && link_dp_get_encoding_format(<_settings->link_settings) == DP_8b_10b_ENCODING) 359 tr_pattern = DP_TRAINING_PATTERN_SEQUENCE_4; 360 361 dp_set_hw_training_pattern(link, link_res, tr_pattern, offset); 362 363 for (retries_ch_eq = 0; retries_ch_eq <= LINK_TRAINING_MAX_RETRY_COUNT; 364 retries_ch_eq++) { 365 366 dp_set_hw_lane_settings(link, link_res, lt_settings, offset); 367 368 /* 2. update DPCD*/ 369 if (!retries_ch_eq) 370 /* EPR #361076 - write as a 5-byte burst, 371 * but only for the 1-st iteration 372 */ 373 374 dpcd_set_lt_pattern_and_lane_settings( 375 link, 376 lt_settings, 377 tr_pattern, offset); 378 else 379 dpcd_set_lane_settings(link, lt_settings, offset); 380 381 /* 3. wait for receiver to lock-on*/ 382 wait_time_microsec = dp_get_eq_aux_rd_interval(link, lt_settings, offset, retries_ch_eq); 383 384 dp_wait_for_training_aux_rd_interval( 385 link, 386 wait_time_microsec); 387 388 /* 4. Read lane status and requested 389 * drive settings as set by the sink*/ 390 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 offset); 398 399 if (dp_check_dpcd_reqeust_status(link, status)) 400 return LINK_TRAINING_ABORT; 401 402 /* 5. check CR done*/ 403 if (!dp_is_cr_done(lane_count, dpcd_lane_status)) 404 return dpcd_lane_status[0].bits.CR_DONE_0 ? 405 LINK_TRAINING_EQ_FAIL_CR_PARTIAL : 406 LINK_TRAINING_EQ_FAIL_CR; 407 408 /* 6. check CHEQ done*/ 409 if (dp_is_ch_eq_done(lane_count, dpcd_lane_status) && 410 dp_is_symbol_locked(lane_count, dpcd_lane_status) && 411 dp_check_interlane_aligned(dpcd_lane_status_updated, link, retries_ch_eq)) 412 return LINK_TRAINING_SUCCESS; 413 414 /* 7. update VS/PE/PC2 in lt_settings*/ 415 dp_decide_lane_settings(lt_settings, dpcd_lane_adjust, 416 lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings); 417 } 418 419 return LINK_TRAINING_EQ_FAIL_EQ; 420 421} 422 423enum link_training_result dp_perform_8b_10b_link_training( 424 struct dc_link *link, 425 const struct link_resource *link_res, 426 struct link_training_settings *lt_settings) 427{ 428 enum link_training_result status = LINK_TRAINING_SUCCESS; 429 430 uint8_t repeater_cnt = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt); 431 uint8_t repeater_id; 432 uint8_t lane = 0; 433 434 if (link->ctx->dc->work_arounds.lt_early_cr_pattern) 435 start_clock_recovery_pattern_early(link, link_res, lt_settings, DPRX); 436 437 /* 1. set link rate, lane count and spread. */ 438 if (lt_settings->lttpr_early_tps2) 439 set_link_settings_and_perform_early_tps2_retimer_pre_lt_sequence(link, link_res, lt_settings, repeater_cnt); 440 else 441 dpcd_set_link_settings(link, lt_settings); 442 443 if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) { 444 445 /* 2. perform link training (set link training done 446 * to false is done as well) 447 */ 448 449 for (repeater_id = repeater_cnt; (repeater_id > 0 && status == LINK_TRAINING_SUCCESS); 450 repeater_id--) { 451 status = perform_8b_10b_clock_recovery_sequence(link, link_res, lt_settings, repeater_id); 452 453 if (status != LINK_TRAINING_SUCCESS) { 454 repeater_training_done(link, repeater_id); 455 break; 456 } 457 458 status = perform_8b_10b_channel_equalization_sequence(link, 459 link_res, 460 lt_settings, 461 repeater_id); 462 if (status == LINK_TRAINING_SUCCESS) 463 DC_LOG_HW_LINK_TRAINING("%s: Channel EQ done.\n", __func__); 464 465 repeater_training_done(link, repeater_id); 466 467 if (status != LINK_TRAINING_SUCCESS) 468 break; 469 470 for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) { 471 lt_settings->dpcd_lane_settings[lane].raw = 0; 472 lt_settings->hw_lane_settings[lane].VOLTAGE_SWING = 0; 473 lt_settings->hw_lane_settings[lane].PRE_EMPHASIS = 0; 474 } 475 } 476 } 477 478 if (status == LINK_TRAINING_SUCCESS) { 479 status = perform_8b_10b_clock_recovery_sequence(link, link_res, lt_settings, DPRX); 480 if (status == LINK_TRAINING_SUCCESS) { 481 status = perform_8b_10b_channel_equalization_sequence(link, 482 link_res, 483 lt_settings, 484 DPRX); 485 if (status == LINK_TRAINING_SUCCESS) 486 DC_LOG_HW_LINK_TRAINING("%s: Channel EQ done.\n", __func__); 487 } 488 } 489 490 return status; 491}
~/
Read-only file preview.