diff options
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c')
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c | 689 |
1 files changed, 560 insertions, 129 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c index 2c7aaed907b9..f5742719b5d9 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c @@ -49,7 +49,7 @@ static struct dc_link_settings get_common_supported_link_settings( struct dc_link_settings link_setting_a, struct dc_link_settings link_setting_b); -static void wait_for_training_aux_rd_interval( +static uint32_t get_training_aux_rd_interval( struct dc_link *link, uint32_t default_wait_in_micro_secs) { @@ -68,15 +68,21 @@ static void wait_for_training_aux_rd_interval( sizeof(training_rd_interval)); if (training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL) - default_wait_in_micro_secs = - training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL * 4000; + default_wait_in_micro_secs = training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL * 4000; } - udelay(default_wait_in_micro_secs); + return default_wait_in_micro_secs; +} + +static void wait_for_training_aux_rd_interval( + struct dc_link *link, + uint32_t wait_in_micro_secs) +{ + udelay(wait_in_micro_secs); DC_LOG_HW_LINK_TRAINING("%s:\n wait = %d\n", __func__, - default_wait_in_micro_secs); + wait_in_micro_secs); } static void dpcd_set_training_pattern( @@ -95,27 +101,27 @@ static void dpcd_set_training_pattern( dpcd_pattern.v1_4.TRAINING_PATTERN_SET); } -static enum hw_dp_training_pattern get_supported_tp(struct dc_link *link) +static enum dc_dp_training_pattern get_supported_tp(struct dc_link *link) { - enum hw_dp_training_pattern highest_tp = HW_DP_TRAINING_PATTERN_2; + enum dc_dp_training_pattern highest_tp = DP_TRAINING_PATTERN_SEQUENCE_2; struct encoder_feature_support *features = &link->link_enc->features; struct dpcd_caps *dpcd_caps = &link->dpcd_caps; if (features->flags.bits.IS_TPS3_CAPABLE) - highest_tp = HW_DP_TRAINING_PATTERN_3; + highest_tp = DP_TRAINING_PATTERN_SEQUENCE_3; if (features->flags.bits.IS_TPS4_CAPABLE) - highest_tp = HW_DP_TRAINING_PATTERN_4; + highest_tp = DP_TRAINING_PATTERN_SEQUENCE_4; if (dpcd_caps->max_down_spread.bits.TPS4_SUPPORTED && - highest_tp >= HW_DP_TRAINING_PATTERN_4) - return HW_DP_TRAINING_PATTERN_4; + highest_tp >= DP_TRAINING_PATTERN_SEQUENCE_4) + return DP_TRAINING_PATTERN_SEQUENCE_4; if (dpcd_caps->max_ln_count.bits.TPS3_SUPPORTED && - highest_tp >= HW_DP_TRAINING_PATTERN_3) - return HW_DP_TRAINING_PATTERN_3; + highest_tp >= DP_TRAINING_PATTERN_SEQUENCE_3) + return DP_TRAINING_PATTERN_SEQUENCE_3; - return HW_DP_TRAINING_PATTERN_2; + return DP_TRAINING_PATTERN_SEQUENCE_2; } static void dpcd_set_link_settings( @@ -126,7 +132,7 @@ static void dpcd_set_link_settings( union down_spread_ctrl downspread = { {0} }; union lane_count_set lane_count_set = { {0} }; - enum hw_dp_training_pattern hw_tr_pattern; + enum dc_dp_training_pattern dp_tr_pattern; downspread.raw = (uint8_t) (lt_settings->link_settings.link_spread); @@ -134,21 +140,21 @@ static void dpcd_set_link_settings( lane_count_set.bits.LANE_COUNT_SET = lt_settings->link_settings.lane_count; - lane_count_set.bits.ENHANCED_FRAMING = 1; - + lane_count_set.bits.ENHANCED_FRAMING = lt_settings->enhanced_framing; lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = 0; - hw_tr_pattern = get_supported_tp(link); - if (hw_tr_pattern != HW_DP_TRAINING_PATTERN_4) { + dp_tr_pattern = get_supported_tp(link); + + if (dp_tr_pattern != DP_TRAINING_PATTERN_SEQUENCE_4) { lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED; } core_link_write_dpcd(link, DP_DOWNSPREAD_CTRL, - &downspread.raw, sizeof(downspread)); + &downspread.raw, sizeof(downspread)); core_link_write_dpcd(link, DP_LANE_COUNT_SET, - &lane_count_set.raw, 1); + &lane_count_set.raw, 1); if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_14 && lt_settings->link_settings.use_link_rate_set == true) { @@ -162,46 +168,47 @@ static void dpcd_set_link_settings( } if (rate) { - DC_LOG_HW_LINK_TRAINING("%s\n %x rate = %x\n %x lane = %x\n %x spread = %x\n", + DC_LOG_HW_LINK_TRAINING("%s\n %x rate = %x\n %x lane = %x framing = %x\n %x spread = %x\n", __func__, DP_LINK_BW_SET, lt_settings->link_settings.link_rate, DP_LANE_COUNT_SET, lt_settings->link_settings.lane_count, + lt_settings->enhanced_framing, DP_DOWNSPREAD_CTRL, lt_settings->link_settings.link_spread); } else { - DC_LOG_HW_LINK_TRAINING("%s\n %x rate set = %x\n %x lane = %x\n %x spread = %x\n", + DC_LOG_HW_LINK_TRAINING("%s\n %x rate set = %x\n %x lane = %x framing = %x\n %x spread = %x\n", __func__, DP_LINK_RATE_SET, lt_settings->link_settings.link_rate_set, DP_LANE_COUNT_SET, lt_settings->link_settings.lane_count, + lt_settings->enhanced_framing, DP_DOWNSPREAD_CTRL, lt_settings->link_settings.link_spread); } - } static enum dpcd_training_patterns - hw_training_pattern_to_dpcd_training_pattern( + dc_dp_training_pattern_to_dpcd_training_pattern( struct dc_link *link, - enum hw_dp_training_pattern pattern) + enum dc_dp_training_pattern pattern) { enum dpcd_training_patterns dpcd_tr_pattern = DPCD_TRAINING_PATTERN_VIDEOIDLE; switch (pattern) { - case HW_DP_TRAINING_PATTERN_1: + case DP_TRAINING_PATTERN_SEQUENCE_1: dpcd_tr_pattern = DPCD_TRAINING_PATTERN_1; break; - case HW_DP_TRAINING_PATTERN_2: + case DP_TRAINING_PATTERN_SEQUENCE_2: dpcd_tr_pattern = DPCD_TRAINING_PATTERN_2; break; - case HW_DP_TRAINING_PATTERN_3: + case DP_TRAINING_PATTERN_SEQUENCE_3: dpcd_tr_pattern = DPCD_TRAINING_PATTERN_3; break; - case HW_DP_TRAINING_PATTERN_4: + case DP_TRAINING_PATTERN_SEQUENCE_4: dpcd_tr_pattern = DPCD_TRAINING_PATTERN_4; break; default: @@ -212,13 +219,12 @@ static enum dpcd_training_patterns } return dpcd_tr_pattern; - } static void dpcd_set_lt_pattern_and_lane_settings( struct dc_link *link, const struct link_training_settings *lt_settings, - enum hw_dp_training_pattern pattern) + enum dc_dp_training_pattern pattern) { union dpcd_training_lane dpcd_lane[LANE_COUNT_DP_MAX] = { { {0} } }; const uint32_t dpcd_base_lt_offset = @@ -233,7 +239,7 @@ static void dpcd_set_lt_pattern_and_lane_settings( * DpcdAddress_TrainingPatternSet *****************************************************************/ dpcd_pattern.v1_4.TRAINING_PATTERN_SET = - hw_training_pattern_to_dpcd_training_pattern(link, pattern); + dc_dp_training_pattern_to_dpcd_training_pattern(link, pattern); dpcd_lt_buffer[DP_TRAINING_PATTERN_SET - dpcd_base_lt_offset] = dpcd_pattern.raw; @@ -346,12 +352,20 @@ static void update_drive_settings( { uint32_t lane; for (lane = 0; lane < src.link_settings.lane_count; lane++) { - dest->lane_settings[lane].VOLTAGE_SWING = - src.lane_settings[lane].VOLTAGE_SWING; - dest->lane_settings[lane].PRE_EMPHASIS = - src.lane_settings[lane].PRE_EMPHASIS; - dest->lane_settings[lane].POST_CURSOR2 = - src.lane_settings[lane].POST_CURSOR2; + if (dest->voltage_swing == NULL) + dest->lane_settings[lane].VOLTAGE_SWING = src.lane_settings[lane].VOLTAGE_SWING; + else + dest->lane_settings[lane].VOLTAGE_SWING = *dest->voltage_swing; + + if (dest->pre_emphasis == NULL) + dest->lane_settings[lane].PRE_EMPHASIS = src.lane_settings[lane].PRE_EMPHASIS; + else + dest->lane_settings[lane].PRE_EMPHASIS = *dest->pre_emphasis; + + if (dest->post_cursor2 == NULL) + dest->lane_settings[lane].POST_CURSOR2 = src.lane_settings[lane].POST_CURSOR2; + else + dest->lane_settings[lane].POST_CURSOR2 = *dest->post_cursor2; } } @@ -754,15 +768,15 @@ static enum link_training_result perform_channel_equalization_sequence( struct link_training_settings *lt_settings) { struct link_training_settings req_settings; - enum hw_dp_training_pattern hw_tr_pattern; + enum dc_dp_training_pattern tr_pattern; uint32_t retries_ch_eq; enum dc_lane_count lane_count = lt_settings->link_settings.lane_count; union lane_align_status_updated dpcd_lane_status_updated = { {0} }; union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = { { {0} } }; - hw_tr_pattern = get_supported_tp(link); + tr_pattern = lt_settings->pattern_for_eq; - dp_set_hw_training_pattern(link, hw_tr_pattern); + dp_set_hw_training_pattern(link, tr_pattern); for (retries_ch_eq = 0; retries_ch_eq <= LINK_TRAINING_MAX_RETRY_COUNT; retries_ch_eq++) { @@ -776,12 +790,12 @@ static enum link_training_result perform_channel_equalization_sequence( dpcd_set_lt_pattern_and_lane_settings( link, lt_settings, - hw_tr_pattern); + tr_pattern); else dpcd_set_lane_settings(link, lt_settings); /* 3. wait for receiver to lock-on*/ - wait_for_training_aux_rd_interval(link, 400); + wait_for_training_aux_rd_interval(link, lt_settings->eq_pattern_time); /* 4. Read lane status and requested * drive settings as set by the sink*/ @@ -817,27 +831,16 @@ static enum link_training_result perform_clock_recovery_sequence( { uint32_t retries_cr; uint32_t retry_count; - uint32_t lane; struct link_training_settings req_settings; - enum dc_lane_count lane_count = - lt_settings->link_settings.lane_count; - enum hw_dp_training_pattern hw_tr_pattern = HW_DP_TRAINING_PATTERN_1; + enum dc_lane_count lane_count = lt_settings->link_settings.lane_count; + enum dc_dp_training_pattern tr_pattern = DP_TRAINING_PATTERN_SEQUENCE_1; union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX]; union lane_align_status_updated dpcd_lane_status_updated; retries_cr = 0; retry_count = 0; - /* initial drive setting (VS/PE/PC2)*/ - for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) { - lt_settings->lane_settings[lane].VOLTAGE_SWING = - VOLTAGE_SWING_LEVEL0; - lt_settings->lane_settings[lane].PRE_EMPHASIS = - PRE_EMPHASIS_DISABLED; - lt_settings->lane_settings[lane].POST_CURSOR2 = - POST_CURSOR2_DISABLED; - } - dp_set_hw_training_pattern(link, hw_tr_pattern); + dp_set_hw_training_pattern(link, tr_pattern); /* najeeb - The synaptics MST hub can put the LT in * infinite loop by switching the VS @@ -845,7 +848,7 @@ static enum link_training_result perform_clock_recovery_sequence( /* between level 0 and level 1 continuously, here * we try for CR lock for LinkTrainingMaxCRRetry count*/ while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) && - (retry_count < LINK_TRAINING_MAX_CR_RETRY)) { + (retry_count < LINK_TRAINING_MAX_CR_RETRY)) { memset(&dpcd_lane_status, '\0', sizeof(dpcd_lane_status)); memset(&dpcd_lane_status_updated, '\0', @@ -863,7 +866,7 @@ static enum link_training_result perform_clock_recovery_sequence( dpcd_set_lt_pattern_and_lane_settings( link, lt_settings, - hw_tr_pattern); + tr_pattern); else dpcd_set_lane_settings( link, @@ -872,7 +875,7 @@ static enum link_training_result perform_clock_recovery_sequence( /* 3. wait receiver to lock-on*/ wait_for_training_aux_rd_interval( link, - 100); + lt_settings->cr_pattern_time); /* 4. Read lane status and requested drive * settings as set by the sink @@ -939,7 +942,7 @@ static inline enum link_training_result perform_link_training_int( * TPS4 must be used instead of POST_LT_ADJ_REQ. */ if (link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED != 1 || - get_supported_tp(link) == HW_DP_TRAINING_PATTERN_4) + get_supported_tp(link) == DP_TRAINING_PATTERN_SEQUENCE_4) return status; if (status == LINK_TRAINING_SUCCESS && @@ -947,7 +950,7 @@ static inline enum link_training_result perform_link_training_int( status = LINK_TRAINING_LQA_FAIL; lane_count_set.bits.LANE_COUNT_SET = lt_settings->link_settings.lane_count; - lane_count_set.bits.ENHANCED_FRAMING = 1; + lane_count_set.bits.ENHANCED_FRAMING = lt_settings->enhanced_framing; lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED = 0; core_link_write_dpcd( @@ -959,24 +962,29 @@ static inline enum link_training_result perform_link_training_int( return status; } -enum link_training_result dc_link_dp_perform_link_training( - struct dc_link *link, +static void initialize_training_settings( + struct dc_link *link, const struct dc_link_settings *link_setting, - bool skip_video_pattern) + const struct dc_link_training_overrides *overrides, + struct link_training_settings *lt_settings) { - enum link_training_result status = LINK_TRAINING_SUCCESS; + uint32_t lane; - char *link_rate = "Unknown"; - char *lt_result = "Unknown"; + memset(lt_settings, '\0', sizeof(struct link_training_settings)); - struct link_training_settings lt_settings; + /* Initialize link settings */ + lt_settings->link_settings.use_link_rate_set = link_setting->use_link_rate_set; + lt_settings->link_settings.link_rate_set = link_setting->link_rate_set; - memset(<_settings, '\0', sizeof(lt_settings)); + if (link->preferred_link_setting.link_rate != LINK_RATE_UNKNOWN) + lt_settings->link_settings.link_rate = link->preferred_link_setting.link_rate; + else + lt_settings->link_settings.link_rate = link_setting->link_rate; - lt_settings.link_settings.link_rate = link_setting->link_rate; - lt_settings.link_settings.lane_count = link_setting->lane_count; - lt_settings.link_settings.use_link_rate_set = link_setting->use_link_rate_set; - lt_settings.link_settings.link_rate_set = link_setting->link_rate_set; + if (link->preferred_link_setting.lane_count != LANE_COUNT_UNKNOWN) + lt_settings->link_settings.lane_count = link->preferred_link_setting.lane_count; + else + lt_settings->link_settings.lane_count = link_setting->lane_count; /*@todo[vdevulap] move SS to LS, should not be handled by displaypath*/ @@ -987,31 +995,75 @@ enum link_training_result dc_link_dp_perform_link_training( * LINK_SPREAD_05_DOWNSPREAD_30KHZ : * LINK_SPREAD_DISABLED; */ + /* Initialize link spread */ if (link->dp_ss_off) - lt_settings.link_settings.link_spread = LINK_SPREAD_DISABLED; + lt_settings->link_settings.link_spread = LINK_SPREAD_DISABLED; + else if (overrides->downspread != NULL) + lt_settings->link_settings.link_spread + = *overrides->downspread + ? LINK_SPREAD_05_DOWNSPREAD_30KHZ + : LINK_SPREAD_DISABLED; else - lt_settings.link_settings.link_spread = LINK_SPREAD_05_DOWNSPREAD_30KHZ; + lt_settings->link_settings.link_spread = LINK_SPREAD_05_DOWNSPREAD_30KHZ; - /* 1. set link rate, lane count and spread*/ - dpcd_set_link_settings(link, <_settings); + /* Initialize lane settings overrides */ + if (overrides->voltage_swing != NULL) + lt_settings->voltage_swing = overrides->voltage_swing; - /* 2. perform link training (set link training done - * to false is done as well)*/ - status = perform_clock_recovery_sequence(link, <_settings); - if (status == LINK_TRAINING_SUCCESS) { - status = perform_channel_equalization_sequence(link, - <_settings); - } + if (overrides->pre_emphasis != NULL) + lt_settings->pre_emphasis = overrides->pre_emphasis; - if ((status == LINK_TRAINING_SUCCESS) || !skip_video_pattern) { - status = perform_link_training_int(link, - <_settings, - status); + if (overrides->post_cursor2 != NULL) + lt_settings->post_cursor2 = overrides->post_cursor2; + + /* Initialize lane settings (VS/PE/PC2) */ + for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) { + lt_settings->lane_settings[lane].VOLTAGE_SWING = + lt_settings->voltage_swing != NULL ? + *lt_settings->voltage_swing : + VOLTAGE_SWING_LEVEL0; + lt_settings->lane_settings[lane].PRE_EMPHASIS = + lt_settings->pre_emphasis != NULL ? + *lt_settings->pre_emphasis + : PRE_EMPHASIS_DISABLED; + lt_settings->lane_settings[lane].POST_CURSOR2 = + lt_settings->post_cursor2 != NULL ? + *lt_settings->post_cursor2 + : POST_CURSOR2_DISABLED; } - /* 6. print status message*/ - switch (lt_settings.link_settings.link_rate) { + /* Initialize training timings */ + if (overrides->cr_pattern_time != NULL) + lt_settings->cr_pattern_time = *overrides->cr_pattern_time; + else + lt_settings->cr_pattern_time = get_training_aux_rd_interval(link, 100); + + if (overrides->eq_pattern_time != NULL) + lt_settings->eq_pattern_time = *overrides->eq_pattern_time; + else + lt_settings->eq_pattern_time = get_training_aux_rd_interval(link, 400); + + if (overrides->pattern_for_eq != NULL) + lt_settings->pattern_for_eq = *overrides->pattern_for_eq; + else + lt_settings->pattern_for_eq = get_supported_tp(link); + + if (overrides->enhanced_framing != NULL) + lt_settings->enhanced_framing = *overrides->enhanced_framing; + else + lt_settings->enhanced_framing = 1; +} + +static void print_status_message( + struct dc_link *link, + const struct link_training_settings *lt_settings, + enum link_training_result status) +{ + char *link_rate = "Unknown"; + char *lt_result = "Unknown"; + char *lt_spread = "Disabled"; + switch (lt_settings->link_settings.link_rate) { case LINK_RATE_LOW: link_rate = "RBR"; break; @@ -1057,13 +1109,122 @@ enum link_training_result dc_link_dp_perform_link_training( break; } + switch (lt_settings->link_settings.link_spread) { + case LINK_SPREAD_DISABLED: + lt_spread = "Disabled"; + break; + case LINK_SPREAD_05_DOWNSPREAD_30KHZ: + lt_spread = "0.5% 30KHz"; + break; + case LINK_SPREAD_05_DOWNSPREAD_33KHZ: + lt_spread = "0.5% 33KHz"; + break; + default: + break; + } + /* Connectivity log: link training */ - CONN_MSG_LT(link, "%sx%d %s VS=%d, PE=%d", - link_rate, - lt_settings.link_settings.lane_count, - lt_result, - lt_settings.lane_settings[0].VOLTAGE_SWING, - lt_settings.lane_settings[0].PRE_EMPHASIS); + CONN_MSG_LT(link, "%sx%d %s VS=%d, PE=%d, DS=%s", + link_rate, + lt_settings->link_settings.lane_count, + lt_result, + lt_settings->lane_settings[0].VOLTAGE_SWING, + lt_settings->lane_settings[0].PRE_EMPHASIS, + lt_spread); +} + +bool dc_link_dp_perform_link_training_skip_aux( + struct dc_link *link, + const struct dc_link_settings *link_setting) +{ + struct link_training_settings lt_settings; + enum dc_dp_training_pattern pattern_for_cr = DP_TRAINING_PATTERN_SEQUENCE_1; + + initialize_training_settings( + link, + link_setting, + &link->preferred_training_settings, + <_settings); + + /* 1. Perform_clock_recovery_sequence. */ + + /* transmit training pattern for clock recovery */ + dp_set_hw_training_pattern(link, pattern_for_cr); + + /* call HWSS to set lane settings*/ + dp_set_hw_lane_settings(link, <_settings); + + /* wait receiver to lock-on*/ + wait_for_training_aux_rd_interval(link, lt_settings.cr_pattern_time); + + /* 2. Perform_channel_equalization_sequence. */ + + /* transmit training pattern for channel equalization. */ + dp_set_hw_training_pattern(link, lt_settings.pattern_for_eq); + + /* call HWSS to set lane settings*/ + dp_set_hw_lane_settings(link, <_settings); + + /* wait receiver to lock-on. */ + wait_for_training_aux_rd_interval(link, lt_settings.eq_pattern_time); + + /* 3. Perform_link_training_int. */ + + /* Mainlink output idle pattern. */ + dp_set_hw_test_pattern(link, DP_TEST_PATTERN_VIDEO_MODE, NULL, 0); + + print_status_message(link, <_settings, LINK_TRAINING_SUCCESS); + + return true; +} + +enum link_training_result dc_link_dp_perform_link_training( + struct dc_link *link, + const struct dc_link_settings *link_setting, + bool skip_video_pattern) +{ + enum link_training_result status = LINK_TRAINING_SUCCESS; + struct link_training_settings lt_settings; +#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT + bool fec_enable; +#endif + + initialize_training_settings( + link, + link_setting, + &link->preferred_training_settings, + <_settings); + + /* 1. set link rate, lane count and spread. */ + dpcd_set_link_settings(link, <_settings); + +#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT + if (link->preferred_training_settings.fec_enable != NULL) + fec_enable = *link->preferred_training_settings.fec_enable; + else + fec_enable = true; + + dp_set_fec_ready(link, fec_enable); +#endif + + + /* 2. perform link training (set link training done + * to false is done as well) + */ + status = perform_clock_recovery_sequence(link, <_settings); + if (status == LINK_TRAINING_SUCCESS) { + status = perform_channel_equalization_sequence(link, + <_settings); + } + + if ((status == LINK_TRAINING_SUCCESS) || !skip_video_pattern) { + status = perform_link_training_int(link, + <_settings, + status); + } + + /* 6. print status message*/ + print_status_message(link, <_settings, status); if (status != LINK_TRAINING_SUCCESS) link->ctx->dc->debug_data.ltFailCount++; @@ -1071,7 +1232,6 @@ enum link_training_result dc_link_dp_perform_link_training( return status; } - bool perform_link_training_with_retries( struct dc_link *link, const struct dc_link_settings *link_setting, @@ -1096,6 +1256,146 @@ bool perform_link_training_with_retries( return false; } +static enum clock_source_id get_clock_source_id(struct dc_link *link) +{ + enum clock_source_id dp_cs_id = CLOCK_SOURCE_ID_UNDEFINED; + struct clock_source *dp_cs = link->dc->res_pool->dp_clock_source; + + if (dp_cs != NULL) { + dp_cs_id = dp_cs->id; + } else { + /* + * dp clock source is not initialized for some reason. + * Should not happen, CLOCK_SOURCE_ID_EXTERNAL will be used + */ + ASSERT(dp_cs); + } + + return dp_cs_id; +} + +static void set_dp_mst_mode(struct dc_link *link, bool mst_enable) +{ + if (mst_enable == false && + link->type == dc_connection_mst_branch) { + /* Disable MST on link. Use only local sink. */ + dp_disable_link_phy_mst(link, link->connector_signal); + + link->type = dc_connection_single; + link->local_sink = link->remote_sinks[0]; + link->local_sink->sink_signal = SIGNAL_TYPE_DISPLAY_PORT; + } else if (mst_enable == true && + link->type == dc_connection_single && + link->remote_sinks[0] != NULL) { + /* Re-enable MST on link. */ + dp_disable_link_phy(link, link->connector_signal); + dp_enable_mst_on_sink(link, true); + + link->type = dc_connection_mst_branch; + link->local_sink->sink_signal = SIGNAL_TYPE_DISPLAY_PORT_MST; + } +} + +bool dc_link_dp_sync_lt_begin(struct dc_link *link) +{ + /* Begin Sync LT. During this time, + * DPCD:600h must not be powered down. + */ + link->sync_lt_in_progress = true; + + /*Clear any existing preferred settings.*/ + memset(&link->preferred_training_settings, 0, + sizeof(struct dc_link_training_overrides)); + memset(&link->preferred_link_setting, 0, + sizeof(struct dc_link_settings)); + + return true; +} + +enum link_training_result dc_link_dp_sync_lt_attempt( + struct dc_link *link, + struct dc_link_settings *link_settings, + struct dc_link_training_overrides *lt_overrides) +{ + struct link_training_settings lt_settings; + enum link_training_result lt_status = LINK_TRAINING_SUCCESS; + enum dp_panel_mode panel_mode = DP_PANEL_MODE_DEFAULT; + enum clock_source_id dp_cs_id = CLOCK_SOURCE_ID_EXTERNAL; +#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT + bool fec_enable = false; +#endif + + initialize_training_settings( + link, + link_settings, + lt_overrides, + <_settings); + + /* Setup MST Mode */ + if (lt_overrides->mst_enable) + set_dp_mst_mode(link, *lt_overrides->mst_enable); + + /* Disable link */ + dp_disable_link_phy(link, link->connector_signal); + + /* Enable link */ + dp_cs_id = get_clock_source_id(link); + dp_enable_link_phy(link, link->connector_signal, + dp_cs_id, link_settings); + +#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT + /* Set FEC enable */ + fec_enable = lt_overrides->fec_enable && *lt_overrides->fec_enable; + dp_set_fec_ready(link, fec_enable); +#endif + + if (lt_overrides->alternate_scrambler_reset) { + if (*lt_overrides->alternate_scrambler_reset) + panel_mode = DP_PANEL_MODE_EDP; + else + panel_mode = DP_PANEL_MODE_DEFAULT; + } else + panel_mode = dp_get_panel_mode(link); + + dp_set_panel_mode(link, panel_mode); + + /* Attempt to train with given link training settings */ + + /* Set link rate, lane count and spread. */ + dpcd_set_link_settings(link, <_settings); + + /* 2. perform link training (set link training done + * to false is done as well) + */ + lt_status = perform_clock_recovery_sequence(link, <_settings); + if (lt_status == LINK_TRAINING_SUCCESS) { + lt_status = perform_channel_equalization_sequence(link, + <_settings); + } + + /* 3. Sync LT must skip TRAINING_PATTERN_SET:0 (video pattern)*/ + /* 4. print status message*/ + print_status_message(link, <_settings, lt_status); + + return lt_status; +} + +bool dc_link_dp_sync_lt_end(struct dc_link *link, bool link_down) +{ + /* If input parameter is set, shut down phy. + * Still shouldn't turn off dp_receiver (DPCD:600h) + */ + if (link_down == true) { + dp_disable_link_phy(link, link->connector_signal); +#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT + dp_set_fec_ready(link, false); +#endif + } + + link->sync_lt_in_progress = false; + return true; +} + static struct dc_link_settings get_max_link_cap(struct dc_link *link) { /* Set Default link settings */ @@ -1250,7 +1550,6 @@ bool dp_verify_link_cap( bool success; bool skip_link_training; bool skip_video_pattern; - struct clock_source *dp_cs; enum clock_source_id dp_cs_id = CLOCK_SOURCE_ID_EXTERNAL; enum link_training_result status; union hpd_irq_data irq_data; @@ -1274,17 +1573,7 @@ bool dp_verify_link_cap( /* disable PHY done possible by BIOS, will be done by driver itself */ dp_disable_link_phy(link, link->connector_signal); - dp_cs = link->dc->res_pool->dp_clock_source; - - if (dp_cs) - dp_cs_id = dp_cs->id; - else { - /* - * dp clock source is not initialized for some reason. - * Should not happen, CLOCK_SOURCE_ID_EXTERNAL will be used - */ - ASSERT(dp_cs); - } + dp_cs_id = get_clock_source_id(link); /* link training starts with the maximum common settings * supported by both sink and ASIC. @@ -1354,6 +1643,33 @@ bool dp_verify_link_cap( return success; } +bool dp_verify_link_cap_with_retries( + struct dc_link *link, + struct dc_link_settings *known_limit_link_setting, + int attempts) +{ + uint8_t i = 0; + bool success = false; + + for (i = 0; i < attempts; i++) { + int fail_count = 0; + enum dc_connection_type type; + + memset(&link->verified_link_cap, 0, + sizeof(struct dc_link_settings)); + if (!dc_link_detect_sink(link, &type)) { + break; + } else if (dp_verify_link_cap(link, + &link->reported_link_cap, + &fail_count) && fail_count == 0) { + success = true; + break; + } + msleep(10); + } + return success; +} + static struct dc_link_settings get_common_supported_link_settings( struct dc_link_settings link_setting_a, struct dc_link_settings link_setting_b) @@ -2156,6 +2472,11 @@ bool is_mst_supported(struct dc_link *link) union dpcd_rev rev; union mstm_cap cap; + if (link->preferred_training_settings.mst_enable && + *link->preferred_training_settings.mst_enable == false) { + return false; + } + rev.raw = 0; cap.raw = 0; @@ -2363,13 +2684,13 @@ static void dp_wa_power_up_0010FA(struct dc_link *link, uint8_t *dpcd_data, if (link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_VGA_CONVERTER) { switch (link->dpcd_caps.branch_dev_id) { - /* Some active dongles (DP-VGA, DP-DLDVI converters) power down + /* 0010FA active dongles (DP-VGA, DP-DLDVI converters) power down * all internal circuits including AUX communication preventing * reading DPCD table and EDID (spec violation). * Encoder will skip DP RX power down on disable_output to * keep receiver powered all the time.*/ - case DP_BRANCH_DEVICE_ID_1: - case DP_BRANCH_DEVICE_ID_4: + case DP_BRANCH_DEVICE_ID_0010FA: + case DP_BRANCH_DEVICE_ID_0080E1: link->wa_flags.dp_keep_receiver_powered = true; break; @@ -2774,14 +3095,19 @@ static void set_crtc_test_pattern(struct dc_link *link, controller_test_pattern, color_depth); #if defined(CONFIG_DRM_AMD_DC_DCN2_0) else if (opp->funcs->opp_set_disp_pattern_generator) { - struct pipe_ctx *bot_odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); + struct pipe_ctx *odm_pipe; + int opp_cnt = 1; + + for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) + opp_cnt++; - if (bot_odm_pipe) { - struct output_pixel_processor *bot_opp = bot_odm_pipe->stream_res.opp; + width /= opp_cnt; - bot_opp->funcs->opp_program_bit_depth_reduction(bot_opp, ¶ms); - width /= 2; - bot_opp->funcs->opp_set_disp_pattern_generator(bot_opp, + for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) { + struct output_pixel_processor *odm_opp = odm_pipe->stream_res.opp; + + odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, ¶ms); + odm_opp->funcs->opp_set_disp_pattern_generator(odm_opp, controller_test_pattern, color_depth, NULL, @@ -2810,14 +3136,18 @@ static void set_crtc_test_pattern(struct dc_link *link, color_depth); #if defined(CONFIG_DRM_AMD_DC_DCN2_0) else if (opp->funcs->opp_set_disp_pattern_generator) { - struct pipe_ctx *bot_odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); + struct pipe_ctx *odm_pipe; + int opp_cnt = 1; + + for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) + opp_cnt++; - if (bot_odm_pipe) { - struct output_pixel_processor *bot_opp = bot_odm_pipe->stream_res.opp; + width /= opp_cnt; + for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) { + struct output_pixel_processor *odm_opp = odm_pipe->stream_res.opp; - bot_opp->funcs->opp_program_bit_depth_reduction(bot_opp, ¶ms); - width /= 2; - bot_opp->funcs->opp_set_disp_pattern_generator(bot_opp, + odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, ¶ms); + odm_opp->funcs->opp_set_disp_pattern_generator(odm_opp, CONTROLLER_DP_TEST_PATTERN_VIDEOMODE, color_depth, NULL, @@ -2858,7 +3188,7 @@ bool dc_link_dp_set_test_pattern( memset(&training_pattern, 0, sizeof(training_pattern)); for (i = 0; i < MAX_PIPES; i++) { - if (pipes[i].stream->link == link) { + if (pipes[i].stream->link == link && !pipes[i].top_pipe && !pipes[i].prev_odm_pipe) { pipe_ctx = &pipes[i]; break; } @@ -3007,6 +3337,105 @@ void dp_enable_mst_on_sink(struct dc_link *link, bool enable) core_link_write_dpcd(link, DP_MSTM_CTRL, &mstmCntl, 1); } +void dp_set_panel_mode(struct dc_link *link, enum dp_panel_mode panel_mode) +{ + union dpcd_edp_config edp_config_set; + bool panel_mode_edp = false; + + memset(&edp_config_set, '\0', sizeof(union dpcd_edp_config)); + + if (panel_mode != DP_PANEL_MODE_DEFAULT) { + + switch (panel_mode) { + case DP_PANEL_MODE_EDP: + case DP_PANEL_MODE_SPECIAL: + panel_mode_edp = true; + break; + + default: + break; + } + + /*set edp panel mode in receiver*/ + core_link_read_dpcd( + link, + DP_EDP_CONFIGURATION_SET, + &edp_config_set.raw, + sizeof(edp_config_set.raw)); + + if (edp_config_set.bits.PANEL_MODE_EDP + != panel_mode_edp) { + enum ddc_result result = DDC_RESULT_UNKNOWN; + + edp_config_set.bits.PANEL_MODE_EDP = + panel_mode_edp; + result = core_link_write_dpcd( + link, + DP_EDP_CONFIGURATION_SET, + &edp_config_set.raw, + sizeof(edp_config_set.raw)); + + ASSERT(result == DDC_RESULT_SUCESSFULL); + } + } + DC_LOG_DETECTION_DP_CAPS("Link: %d eDP panel mode supported: %d " + "eDP panel mode enabled: %d \n", + link->link_index, + link->dpcd_caps.panel_mode_edp, + panel_mode_edp); +} + +enum dp_panel_mode dp_get_panel_mode(struct dc_link *link) +{ + /* We need to explicitly check that connector + * is not DP. Some Travis_VGA get reported + * by video bios as DP. + */ + if (link->connector_signal != SIGNAL_TYPE_DISPLAY_PORT) { + + switch (link->dpcd_caps.branch_dev_id) { + case DP_BRANCH_DEVICE_ID_0022B9: + /* alternate scrambler reset is required for Travis + * for the case when external chip does not + * provide sink device id, alternate scrambler + * scheme will be overriden later by querying + * Encoder features + */ + if (strncmp( + link->dpcd_caps.branch_dev_name, + DP_VGA_LVDS_CONVERTER_ID_2, + sizeof( + link->dpcd_caps. + branch_dev_name)) == 0) { + return DP_PANEL_MODE_SPECIAL; + } + break; + case DP_BRANCH_DEVICE_ID_00001A: + /* alternate scrambler reset is required for Travis + * for the case when external chip does not provide + * sink device id, alternate scrambler scheme will + * be overriden later by querying Encoder feature + */ + if (strncmp(link->dpcd_caps.branch_dev_name, + DP_VGA_LVDS_CONVERTER_ID_3, + sizeof( + link->dpcd_caps. + branch_dev_name)) == 0) { + return DP_PANEL_MODE_SPECIAL; + } + break; + default: + break; + } + } + + if (link->dpcd_caps.panel_mode_edp) { + return DP_PANEL_MODE_EDP; + } + + return DP_PANEL_MODE_DEFAULT; +} + #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT void dp_set_fec_ready(struct dc_link *link, bool ready) { @@ -3024,7 +3453,7 @@ void dp_set_fec_ready(struct dc_link *link, bool ready) if (link_enc->funcs->fec_set_ready && link->dpcd_caps.fec_cap.bits.FEC_CAPABLE) { - if (link->fec_state == dc_link_fec_not_ready && ready) { + if (ready) { fec_config = 1; if (core_link_write_dpcd(link, DP_FEC_CONFIGURATION, @@ -3033,9 +3462,11 @@ void dp_set_fec_ready(struct dc_link *link, bool ready) link_enc->funcs->fec_set_ready(link_enc, true); link->fec_state = dc_link_fec_ready; } else { + link->link_enc->funcs->fec_set_ready(link->link_enc, false); + link->fec_state = dc_link_fec_not_ready; dm_error("dpcd write failed to set fec_ready"); } - } else if (link->fec_state == dc_link_fec_ready && !ready) { + } else if (link->fec_state == dc_link_fec_ready) { fec_config = 0; core_link_write_dpcd(link, DP_FEC_CONFIGURATION, |