diff options
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/link')
31 files changed, 1046 insertions, 458 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c b/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c index ff8fe1a94965..96febabf464a 100644 --- a/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c +++ b/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c @@ -251,7 +251,7 @@ static void dp_test_send_phy_test_pattern(struct dc_link *link) link_training_settings.lttpr_mode = dp_decide_lttpr_mode(link, &link->cur_link_settings); - if ((link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && + if (((link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK) == AMD_EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && link_training_settings.lttpr_mode == LTTPR_MODE_TRANSPARENT) dp_fixed_vs_pe_read_lane_adjust( link, @@ -646,7 +646,7 @@ bool dp_set_test_pattern( if (IS_DP_PHY_PATTERN(test_pattern)) { /* Set DPCD Lane Settings before running test pattern */ if (p_link_settings != NULL) { - if ((link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && + if (((link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK) == AMD_EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && p_link_settings->lttpr_mode == LTTPR_MODE_TRANSPARENT) { dp_fixed_vs_pe_set_retimer_lane_settings( link, diff --git a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio.c b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio.c index 3e47a6735912..b68bcc9fca0a 100644 --- a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio.c +++ b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio.c @@ -48,9 +48,16 @@ void set_dio_throttled_vcp_size(struct pipe_ctx *pipe_ctx, void setup_dio_stream_encoder(struct pipe_ctx *pipe_ctx) { - struct link_encoder *link_enc = link_enc_cfg_get_link_enc(pipe_ctx->stream->link); + struct link_encoder *link_enc = pipe_ctx->link_res.dio_link_enc; struct stream_encoder *stream_enc = pipe_ctx->stream_res.stream_enc; + if (!pipe_ctx->stream->ctx->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(pipe_ctx->stream->link); + if (!link_enc) { + ASSERT(link_enc); + return; + } + link_enc->funcs->connect_dig_be_to_fe(link_enc, pipe_ctx->stream_res.stream_enc->id, true); if (dc_is_dp_signal(pipe_ctx->stream->signal)) @@ -71,9 +78,16 @@ void setup_dio_stream_encoder(struct pipe_ctx *pipe_ctx) void reset_dio_stream_encoder(struct pipe_ctx *pipe_ctx) { - struct link_encoder *link_enc = link_enc_cfg_get_link_enc(pipe_ctx->stream->link); + struct link_encoder *link_enc = pipe_ctx->link_res.dio_link_enc; struct stream_encoder *stream_enc = pipe_ctx->stream_res.stream_enc; + if (!pipe_ctx->stream->ctx->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(pipe_ctx->stream->link); + if (!link_enc) { + ASSERT(link_enc); + return; + } + if (!stream_enc) return; @@ -142,7 +156,14 @@ void enable_dio_dp_link_output(struct dc_link *link, enum clock_source_id clock_source, const struct dc_link_settings *link_settings) { - struct link_encoder *link_enc = link_enc_cfg_get_link_enc(link); + struct link_encoder *link_enc = link_res->dio_link_enc; + + if (!link->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(link); + if (!link_enc) { + ASSERT(link_enc); + return; + } if (dc_is_dp_sst_signal(signal)) link_enc->funcs->enable_dp_output( @@ -162,7 +183,14 @@ void disable_dio_link_output(struct dc_link *link, const struct link_resource *link_res, enum signal_type signal) { - struct link_encoder *link_enc = link_enc_cfg_get_link_enc(link); + struct link_encoder *link_enc = link_res->dio_link_enc; + + if (!link->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(link); + if (!link_enc) { + ASSERT(link_enc); + return; + } link_enc->funcs->disable_output(link_enc, signal); link->dc->link_srv->dp_trace_source_sequence(link, @@ -173,7 +201,14 @@ void set_dio_dp_link_test_pattern(struct dc_link *link, const struct link_resource *link_res, struct encoder_set_dp_phy_pattern_param *tp_params) { - struct link_encoder *link_enc = link_enc_cfg_get_link_enc(link); + struct link_encoder *link_enc = link_res->dio_link_enc; + + if (!link->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(link); + if (!link_enc) { + ASSERT(link_enc); + return; + } link_enc->funcs->dp_set_phy_pattern(link_enc, tp_params); link->dc->link_srv->dp_trace_source_sequence(link, DPCD_SOURCE_SEQ_AFTER_SET_SOURCE_PATTERN); @@ -184,7 +219,14 @@ void set_dio_dp_lane_settings(struct dc_link *link, const struct dc_link_settings *link_settings, const struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX]) { - struct link_encoder *link_enc = link_enc_cfg_get_link_enc(link); + struct link_encoder *link_enc = link_res->dio_link_enc; + + if (!link->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(link); + if (!link_enc) { + ASSERT(link_enc); + return; + } link_enc->funcs->dp_set_lane_settings(link_enc, link_settings, lane_settings); } @@ -193,9 +235,15 @@ void update_dio_stream_allocation_table(struct dc_link *link, const struct link_resource *link_res, const struct link_mst_stream_allocation_table *table) { - struct link_encoder *link_enc = link_enc_cfg_get_link_enc(link); + struct link_encoder *link_enc = link_res->dio_link_enc; + + if (!link->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(link); + if (!link_enc) { + ASSERT(link_enc); + return; + } - ASSERT(link_enc); link_enc->funcs->update_mst_stream_allocation_table(link_enc, table); } @@ -280,7 +328,10 @@ static const struct link_hwss dio_link_hwss = { bool can_use_dio_link_hwss(const struct dc_link *link, const struct link_resource *link_res) { - return link->link_enc != NULL; + if (!link->dc->config.unify_link_enc_assignment) + return link->link_enc != NULL; + else + return link_res->dio_link_enc != NULL; } /** diff --git a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio_fixed_vs_pe_retimer.c b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio_fixed_vs_pe_retimer.c index 348ea4cb832d..e1dff4e3f446 100644 --- a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio_fixed_vs_pe_retimer.c +++ b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio_fixed_vs_pe_retimer.c @@ -127,7 +127,10 @@ static void set_dio_fixed_vs_pe_retimer_dp_link_test_pattern(struct dc_link *lin const struct link_resource *link_res, struct encoder_set_dp_phy_pattern_param *tp_params) { - struct link_encoder *link_enc = link_enc_cfg_get_link_enc(link); + struct link_encoder *link_enc = link_res->dio_link_enc; + + if (!link->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(link); if (!set_dio_fixed_vs_pe_retimer_dp_link_test_pattern_override( link, link_res, tp_params, get_dio_link_hwss())) { @@ -187,7 +190,7 @@ static const struct link_hwss dio_fixed_vs_pe_retimer_link_hwss = { bool requires_fixed_vs_pe_retimer_dio_link_hwss(const struct dc_link *link) { - return (link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN); + return ((link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK) == AMD_EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN); } const struct link_hwss *get_dio_fixed_vs_pe_retimer_link_hwss(void) diff --git a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dpia.c b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dpia.c index 6499807af72a..81bf3c5e1fdf 100644 --- a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dpia.c +++ b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dpia.c @@ -35,12 +35,15 @@ static void update_dpia_stream_allocation_table(struct dc_link *link, const struct link_resource *link_res, const struct link_mst_stream_allocation_table *table) { - struct link_encoder *link_enc = link_enc_cfg_get_link_enc(link); + struct link_encoder *link_enc = link_res->dio_link_enc; static enum dc_status status; uint8_t mst_alloc_slots = 0, prev_mst_slots_in_use = 0xFF; int i; DC_LOGGER_INIT(link->ctx->logger); + if (!link->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(link); + for (i = 0; i < table->stream_count; i++) mst_alloc_slots += table->stream_allocations[i].slot_count; @@ -61,7 +64,10 @@ static void set_dio_dpia_link_test_pattern(struct dc_link *link, if (tp_params->dp_phy_pattern != DP_TEST_PATTERN_VIDEO_MODE) return; - struct link_encoder *link_enc = link_enc_cfg_get_link_enc(link); + struct link_encoder *link_enc = link_res->dio_link_enc; + + if (!link->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(link); if (!link_enc) return; @@ -77,17 +83,78 @@ static void set_dio_dpia_lane_settings(struct dc_link *link, { } +static void enable_dpia_link_output(struct dc_link *link, + const struct link_resource *link_res, + enum signal_type signal, + enum clock_source_id clock_source, + const struct dc_link_settings *link_settings) +{ + struct link_encoder *link_enc = link_res->dio_link_enc; + DC_LOGGER_INIT(link->ctx->logger); + + if (!link->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(link); + + if (link_enc != NULL) { + if (link->dc->config.enable_dpia_pre_training || link->dc->config.unify_link_enc_assignment) { + uint8_t fec_rdy = link->dc->link_srv->dp_should_enable_fec(link); + uint8_t digmode = dc_is_dp_sst_signal(signal) ? DIG_SST_MODE : DIG_MST_MODE; + + if (link_enc->funcs->enable_dpia_output) + link_enc->funcs->enable_dpia_output( + link_enc, + link_settings, + link->ddc_hw_inst, + digmode, + fec_rdy); + else + DC_LOG_ERROR("%s: link encoder does not support enable_dpia_output\n", __func__); + } else + enable_dio_dp_link_output(link, link_res, signal, clock_source, link_settings); + + } + + link->dc->link_srv->dp_trace_source_sequence(link, + DPCD_SOURCE_SEQ_AFTER_ENABLE_LINK_PHY); +} + +static void disable_dpia_link_output(struct dc_link *link, + const struct link_resource *link_res, + enum signal_type signal) +{ + struct link_encoder *link_enc = link_res->dio_link_enc; + DC_LOGGER_INIT(link->ctx->logger); + + if (!link->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(link); + + if (link_enc != NULL) { + if (link->dc->config.enable_dpia_pre_training || link->dc->config.unify_link_enc_assignment) { + uint8_t digmode = dc_is_dp_sst_signal(signal) ? DIG_SST_MODE : DIG_MST_MODE; + + if (link_enc->funcs->disable_dpia_output) + link_enc->funcs->disable_dpia_output(link_enc, link->ddc_hw_inst, digmode); + else + DC_LOG_ERROR("%s: link encoder does not support disable_dpia_output\n", __func__); + } else + link_enc->funcs->disable_output(link_enc, signal); + } + + link->dc->link_srv->dp_trace_source_sequence(link, + DPCD_SOURCE_SEQ_AFTER_DISABLE_LINK_PHY); +} + static const struct link_hwss dpia_link_hwss = { .setup_stream_encoder = setup_dio_stream_encoder, .reset_stream_encoder = reset_dio_stream_encoder, .setup_stream_attribute = setup_dio_stream_attribute, - .disable_link_output = disable_dio_link_output, + .disable_link_output = disable_dpia_link_output, .setup_audio_output = setup_dio_audio_output, .enable_audio_packet = enable_dio_audio_packet, .disable_audio_packet = disable_dio_audio_packet, .ext = { .set_throttled_vcp_size = set_dio_throttled_vcp_size, - .enable_dp_link_output = enable_dio_dp_link_output, + .enable_dp_link_output = enable_dpia_link_output, .set_dp_link_test_pattern = set_dio_dpia_link_test_pattern, .set_dp_lane_settings = set_dio_dpia_lane_settings, .update_stream_allocation_table = update_dpia_stream_allocation_table, @@ -97,8 +164,10 @@ static const struct link_hwss dpia_link_hwss = { bool can_use_dpia_link_hwss(const struct dc_link *link, const struct link_resource *link_res) { - return link->is_dig_mapping_flexible && - link->dc->res_pool->funcs->link_encs_assign; + if (!link->dc->config.unify_link_enc_assignment) + return link->is_dig_mapping_flexible && link->dc->res_pool->funcs->link_encs_assign; + else + return link->is_dig_mapping_flexible && link_res->dio_link_enc != NULL; } const struct link_hwss *get_dpia_link_hwss(void) diff --git a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dpia.h b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dpia.h index ad16ec5d9bb7..259e0f4775e1 100644 --- a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dpia.h +++ b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dpia.h @@ -27,6 +27,9 @@ #include "link_hwss.h" +#define DIG_SST_MODE 0 +#define DIG_MST_MODE 5 + const struct link_hwss *get_dpia_link_hwss(void); bool can_use_dpia_link_hwss(const struct dc_link *link, const struct link_resource *link_res); diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c index d21ee9d12d26..9655e6fa53a4 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c @@ -48,6 +48,9 @@ #include "dm_helpers.h" #include "clk_mgr.h" + // Offset DPCD 050Eh == 0x5A +#define MST_HUB_ID_0x5A 0x5A + #define DC_LOGGER \ link->ctx->logger #define DC_LOGGER_INIT(logger) @@ -608,6 +611,7 @@ static bool detect_dp(struct dc_link *link, link->dpcd_caps.dongle_type = sink_caps->dongle_type; link->dpcd_caps.is_dongle_type_one = sink_caps->is_dongle_type_one; link->dpcd_caps.dpcd_rev.raw = 0; + link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.raw = 0; } return true; @@ -692,6 +696,15 @@ static void apply_dpia_mst_dsc_always_on_wa(struct dc_link *link) link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT && !link->dc->debug.dpia_debug.bits.disable_mst_dsc_work_around) link->wa_flags.dpia_mst_dsc_always_on = true; + + if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && + link->type == dc_connection_mst_branch && + link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_90CC24 && + link->dpcd_caps.branch_vendor_specific_data[2] == MST_HUB_ID_0x5A && + link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT && + !link->dc->debug.dpia_debug.bits.disable_mst_dsc_work_around) { + link->wa_flags.dpia_mst_dsc_always_on = true; + } } static void revert_dpia_mst_dsc_always_on_wa(struct dc_link *link) @@ -804,7 +817,10 @@ static bool should_verify_link_capability_destructively(struct dc_link *link, { bool destrictive = false; struct dc_link_settings max_link_cap; - bool is_link_enc_unavailable = link->link_enc && + bool is_link_enc_unavailable = false; + + if (!link->dc->config.unify_link_enc_assignment) + is_link_enc_unavailable = link->link_enc && link->dc->res_pool->funcs->link_encs_assign && !link_enc_cfg_is_link_enc_avail( link->ctx->dc, @@ -817,7 +833,8 @@ static bool should_verify_link_capability_destructively(struct dc_link *link, if (link->dc->debug.skip_detection_link_training || dc_is_embedded_signal(link->local_sink->sink_signal) || - link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) { + (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && + !link->dc->config.enable_dpia_pre_training)) { destrictive = false; } else if (link_dp_get_encoding_format(&max_link_cap) == DP_8b_10b_ENCODING) { @@ -991,21 +1008,11 @@ static bool detect_link_and_local_sink(struct dc_link *link, link->reported_link_cap.link_rate > LINK_RATE_HIGH3) link->reported_link_cap.link_rate = LINK_RATE_HIGH3; - /* - * If this is DP over USB4 link then we need to: - * - Enable BW ALLOC support on DPtx if applicable - */ - if (dc->config.usb4_bw_alloc_support) { - if (link_dp_dpia_set_dptx_usb4_bw_alloc_support(link)) { - /* update with non reduced link cap if bw allocation mode is supported */ - if (link->dpia_bw_alloc_config.nrd_max_link_rate && - link->dpia_bw_alloc_config.nrd_max_lane_count) { - link->reported_link_cap.link_rate = - link->dpia_bw_alloc_config.nrd_max_link_rate; - link->reported_link_cap.lane_count = - link->dpia_bw_alloc_config.nrd_max_lane_count; - } - } + if (link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling + && link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dpia_bw_alloc + && link->dpcd_caps.usb4_dp_tun_info.driver_bw_cap.bits.driver_bw_alloc_support) { + if (link_dpia_enable_usb4_dp_bw_alloc_mode(link) == false) + link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dpia_bw_alloc = false; } break; } diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c index c4e03482ba9a..273a3be6d593 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c @@ -148,6 +148,7 @@ void link_blank_dp_stream(struct dc_link *link, bool hw_init) void link_set_all_streams_dpms_off_for_link(struct dc_link *link) { struct pipe_ctx *pipes[MAX_PIPES]; + struct dc_stream_state *streams[MAX_PIPES]; struct dc_state *state = link->dc->current_state; uint8_t count; int i; @@ -160,10 +161,18 @@ void link_set_all_streams_dpms_off_for_link(struct dc_link *link) link_get_master_pipes_with_dpms_on(link, state, &count, pipes); + /* The subsequent call to dc_commit_updates_for_stream for a full update + * will release the current state and swap to a new state. Releasing the + * current state results in the stream pointers in the pipe_ctx structs + * to be zero'd. Hence, cache all streams prior to dc_commit_updates_for_stream. + */ + for (i = 0; i < count; i++) + streams[i] = pipes[i]->stream; + for (i = 0; i < count; i++) { - stream_update.stream = pipes[i]->stream; + stream_update.stream = streams[i]; dc_commit_updates_for_stream(link->ctx->dc, NULL, 0, - pipes[i]->stream, &stream_update, + streams[i], &stream_update, state); } @@ -652,15 +661,15 @@ static void write_i2c_redriver_setting( static void update_psp_stream_config(struct pipe_ctx *pipe_ctx, bool dpms_off) { struct cp_psp *cp_psp = &pipe_ctx->stream->ctx->cp_psp; - struct link_encoder *link_enc = NULL; + struct link_encoder *link_enc = pipe_ctx->link_res.dio_link_enc; struct cp_psp_stream_config config = {0}; enum dp_panel_mode panel_mode = dp_get_panel_mode(pipe_ctx->stream->link); if (cp_psp == NULL || cp_psp->funcs.update_stream_config == NULL) return; - - link_enc = link_enc_cfg_get_link_enc(pipe_ctx->stream->link); + if (!pipe_ctx->stream->ctx->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(pipe_ctx->stream->link); ASSERT(link_enc); if (link_enc == NULL) return; @@ -772,6 +781,20 @@ static bool dp_set_dsc_on_rx(struct pipe_ctx *pipe_ctx, bool enable) return result; } +static bool dp_set_hblank_reduction_on_rx(struct pipe_ctx *pipe_ctx) +{ + struct dc *dc = pipe_ctx->stream->ctx->dc; + struct dc_stream_state *stream = pipe_ctx->stream; + bool result = false; + + if (dc_is_virtual_signal(stream->signal)) + result = true; + else + result = dm_helpers_dp_write_hblank_reduction(dc->ctx, stream); + return result; +} + + /* The stream with these settings can be sent (unblanked) only after DSC was enabled on RX first, * i.e. after dp_enable_dsc_on_rx() had been called */ @@ -808,7 +831,8 @@ void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable) enum optc_dsc_mode optc_dsc_mode; /* Enable DSC hw block */ - dsc_cfg.pic_width = (stream->timing.h_addressable + stream->timing.h_border_left + stream->timing.h_border_right) / opp_cnt; + dsc_cfg.pic_width = (stream->timing.h_addressable + pipe_ctx->hblank_borrow + + stream->timing.h_border_left + stream->timing.h_border_right) / opp_cnt; dsc_cfg.pic_height = stream->timing.v_addressable + stream->timing.v_border_top + stream->timing.v_border_bottom; dsc_cfg.pixel_encoding = stream->timing.pixel_encoding; dsc_cfg.color_depth = stream->timing.display_color_depth; @@ -1909,7 +1933,7 @@ static void disable_link_dp(struct dc_link *link, if (link_dp_get_encoding_format(&link_settings) == DP_8b_10b_ENCODING) { - dp_set_fec_enable(link, false); + dp_set_fec_enable(link, link_res, false); dp_set_fec_ready(link, link_res, false); } } @@ -1952,11 +1976,15 @@ static void enable_link_hdmi(struct pipe_ctx *pipe_ctx) stream->phy_pix_clk = stream->timing.pix_clk_100hz / 10; if (stream->phy_pix_clk > 340000) is_over_340mhz = true; + if (dc_is_tmds_signal(stream->signal) && stream->phy_pix_clk > 6000000UL) { + ASSERT(false); + return; + } if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) { unsigned short masked_chip_caps = pipe_ctx->stream->link->chip_caps & - EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK; - if (masked_chip_caps == EXT_DISPLAY_PATH_CAPS__HDMI20_TISN65DP159RSBT) { + AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK; + if (masked_chip_caps == AMD_EXT_DISPLAY_PATH_CAPS__HDMI20_TISN65DP159RSBT) { /* DP159, Retimer settings */ eng_id = pipe_ctx->stream_res.stream_enc->id; @@ -1967,7 +1995,7 @@ static void enable_link_hdmi(struct pipe_ctx *pipe_ctx) write_i2c_default_retimer_setting(pipe_ctx, is_vga_mode, is_over_340mhz); } - } else if (masked_chip_caps == EXT_DISPLAY_PATH_CAPS__HDMI20_PI3EQX1204) { + } else if (masked_chip_caps == AMD_EXT_DISPLAY_PATH_CAPS__HDMI20_PI3EQX1204) { /* PI3EQX1204, Redriver settings */ write_i2c_redriver_setting(pipe_ctx, is_over_340mhz); } @@ -2023,7 +2051,7 @@ static enum dc_status enable_link_dp(struct dc_state *state, int lt_attempts = LINK_TRAINING_ATTEMPTS; // Increase retry count if attempting DP1.x on FIXED_VS link - if ((link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && + if (((link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK) == AMD_EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && link_dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING) lt_attempts = 10; @@ -2038,7 +2066,8 @@ static enum dc_status enable_link_dp(struct dc_state *state, /* Train with fallback when enabling DPIA link. Conventional links are * trained with fallback during sink detection. */ - if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) + if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && + !link->dc->config.enable_dpia_pre_training) do_fallback = true; /* @@ -2082,6 +2111,9 @@ static enum dc_status enable_link_dp(struct dc_state *state, if (link_settings->link_rate == LINK_RATE_LOW) skip_video_pattern = false; + if (stream->sink_patches.oled_optimize_display_on) + set_default_brightness_aux(link); + if (perform_link_training_with_retries(link_settings, skip_video_pattern, lt_attempts, @@ -2099,16 +2131,20 @@ static enum dc_status enable_link_dp(struct dc_state *state, fec_enable = true; if (link_dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING) - dp_set_fec_enable(link, fec_enable); + dp_set_fec_enable(link, &pipe_ctx->link_res, fec_enable); // during mode set we do DP_SET_POWER off then on, aux writes are lost if (link->dpcd_sink_ext_caps.bits.oled == 1 || link->dpcd_sink_ext_caps.bits.sdr_aux_backlight_control == 1 || link->dpcd_sink_ext_caps.bits.hdr_aux_backlight_control == 1) { - set_default_brightness_aux(link); - if (link->dpcd_sink_ext_caps.bits.oled == 1) - msleep(bl_oled_enable_delay); - edp_backlight_enable_aux(link, true); + if (!stream->sink_patches.oled_optimize_display_on) { + set_default_brightness_aux(link); + if (link->dpcd_sink_ext_caps.bits.oled == 1) + msleep(bl_oled_enable_delay); + edp_backlight_enable_aux(link, true); + } else { + edp_backlight_enable_aux(link, true); + } } return status; @@ -2264,22 +2300,7 @@ static bool allocate_usb4_bandwidth_for_stream(struct dc_stream_state *stream, i link->dpia_bw_alloc_config.dp_overhead = link_dp_dpia_get_dp_overhead_in_dp_tunneling(link); req_bw += link->dpia_bw_alloc_config.dp_overhead; - if (link_dp_dpia_allocate_usb4_bandwidth_for_stream(link, req_bw)) { - if (req_bw <= link->dpia_bw_alloc_config.allocated_bw) { - DC_LOG_DEBUG("%s, Success in allocate bw for link(%d), allocated_bw(%d), dp_overhead(%d)\n", - __func__, link->link_index, link->dpia_bw_alloc_config.allocated_bw, - link->dpia_bw_alloc_config.dp_overhead); - } else { - // Cannot get the required bandwidth. - DC_LOG_ERROR("%s, Failed to allocate bw for link(%d), allocated_bw(%d), dp_overhead(%d)\n", - __func__, link->link_index, link->dpia_bw_alloc_config.allocated_bw, - link->dpia_bw_alloc_config.dp_overhead); - return false; - } - } else { - DC_LOG_DEBUG("%s, usb4 request bw timeout\n", __func__); - return false; - } + link_dp_dpia_allocate_usb4_bandwidth_for_stream(link, req_bw); if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { int i = 0; @@ -2353,7 +2374,7 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx) update_psp_stream_config(pipe_ctx, true); dc->hwss.blank_stream(pipe_ctx); - if (pipe_ctx->stream->link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) + if (pipe_ctx->link_config.dp_tunnel_settings.should_use_dp_bw_allocation) deallocate_usb4_bandwidth(pipe_ctx->stream); if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) @@ -2367,13 +2388,13 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx) enum engine_id eng_id = pipe_ctx->stream_res.stream_enc->id; unsigned short masked_chip_caps = link->chip_caps & - EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK; + AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK; //Need to inform that sink is going to use legacy HDMI mode. write_scdc_data( link->ddc, 165000,//vbios only handles 165Mhz. false); - if (masked_chip_caps == EXT_DISPLAY_PATH_CAPS__HDMI20_TISN65DP159RSBT) { + if (masked_chip_caps == AMD_EXT_DISPLAY_PATH_CAPS__HDMI20_TISN65DP159RSBT) { /* DP159, Retimer settings */ if (get_ext_hdmi_settings(pipe_ctx, eng_id, &settings)) write_i2c_retimer_setting(pipe_ctx, @@ -2381,7 +2402,7 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx) else write_i2c_default_retimer_setting(pipe_ctx, false, false); - } else if (masked_chip_caps == EXT_DISPLAY_PATH_CAPS__HDMI20_PI3EQX1204) { + } else if (masked_chip_caps == AMD_EXT_DISPLAY_PATH_CAPS__HDMI20_PI3EQX1204) { /* PI3EQX1204, Redriver settings */ write_i2c_redriver_setting(pipe_ctx, false); } @@ -2421,7 +2442,7 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx) if (link->connector_signal == SIGNAL_TYPE_EDP && dc->debug.psp_disabled_wa) { /* reset internal save state to default since eDP is off */ enum dp_panel_mode panel_mode = dp_get_panel_mode(pipe_ctx->stream->link); - /* since current psp not loaded, we need to reset it to default*/ + /* since current psp not loaded, we need to reset it to default */ link->panel_mode = panel_mode; } } @@ -2434,7 +2455,7 @@ void link_set_dpms_on( struct dc_stream_state *stream = pipe_ctx->stream; struct dc_link *link = stream->sink->link; enum dc_status status; - struct link_encoder *link_enc; + struct link_encoder *link_enc = pipe_ctx->link_res.dio_link_enc; enum otg_out_mux_dest otg_out_dest = OUT_MUX_DIO; struct vpg *vpg = pipe_ctx->stream_res.stream_enc->vpg; const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res); @@ -2459,7 +2480,8 @@ void link_set_dpms_on( } } - link_enc = link_enc_cfg_get_link_enc(link); + if (!dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(link); ASSERT(link_enc); if (!dc_is_virtual_signal(pipe_ctx->stream->signal) @@ -2521,6 +2543,15 @@ void link_set_dpms_on( if (pipe_ctx->stream->dpms_off) return; + /* For Dp tunneling link, a pending HPD means that we have a race condition between processing + * current link and processing the pending HPD. If we enable the link now, we may end up with a + * link that is not actually connected to a sink. So we skip enabling the link in this case. + */ + if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && link->is_hpd_pending) { + DC_LOG_DEBUG("%s, Link%d HPD is pending, not enable it.\n", __func__, link->link_index); + return; + } + /* Have to setup DSC before DIG FE and BE are connected (which happens before the * link training). This is to make sure the bandwidth sent to DIG BE won't be * bigger than what the link and/or DIG BE can handle. VBID[6]/CompressedStream_flag @@ -2586,7 +2617,10 @@ void link_set_dpms_on( } } - if (pipe_ctx->stream->link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + dp_set_hblank_reduction_on_rx(pipe_ctx); + + if (pipe_ctx->link_config.dp_tunnel_settings.should_use_dp_bw_allocation) allocate_usb4_bandwidth(pipe_ctx->stream); if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) diff --git a/drivers/gpu/drm/amd/display/dc/link/link_factory.c b/drivers/gpu/drm/amd/display/dc/link/link_factory.c index 5e1b5ab9fbc6..1a04f4b74585 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_factory.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_factory.c @@ -101,6 +101,7 @@ static void construct_link_service_validation(struct link_service *link_srv) link_srv->validate_mode_timing = link_validate_mode_timing; link_srv->dp_link_bandwidth_kbps = dp_link_bandwidth_kbps; link_srv->validate_dpia_bandwidth = link_validate_dpia_bandwidth; + link_srv->dp_required_hblank_size_bytes = dp_required_hblank_size_bytes; } /* link dpms owns the programming sequence of stream's dpms state associated @@ -155,6 +156,7 @@ static void construct_link_service_dp_capability(struct link_service *link_srv) link_srv->dp_get_encoding_format = link_dp_get_encoding_format; link_srv->dp_should_enable_fec = dp_should_enable_fec; link_srv->dp_decide_link_settings = link_decide_link_settings; + link_srv->dp_decide_tunnel_settings = link_decide_dp_tunnel_settings; link_srv->mst_decide_link_encoding_format = mst_decide_link_encoding_format; link_srv->edp_decide_link_settings = edp_decide_link_settings; @@ -174,7 +176,6 @@ static void construct_link_service_dp_phy_or_dpia(struct link_service *link_srv) { link_srv->dpia_handle_usb4_bandwidth_allocation_for_link = dpia_handle_usb4_bandwidth_allocation_for_link; - link_srv->dpia_handle_bw_alloc_response = dpia_handle_bw_alloc_response; link_srv->dp_set_drive_settings = dp_set_drive_settings; link_srv->dpcd_write_rx_power_ctrl = dpcd_write_rx_power_ctrl; } @@ -464,6 +465,7 @@ static bool construct_phy(struct dc_link *link, link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; link->irq_source_hpd_rx = DC_IRQ_SOURCE_INVALID; + link->irq_source_read_request = DC_IRQ_SOURCE_INVALID; link->link_status.dpcd_caps = &link->dpcd_caps; link->dc = init_params->dc; @@ -514,6 +516,9 @@ static bool construct_phy(struct dc_link *link, case CONNECTOR_ID_HDMI_TYPE_A: link->connector_signal = SIGNAL_TYPE_HDMI_TYPE_A; + if (link->hpd_gpio) + link->irq_source_read_request = + dal_irq_get_read_request(link->hpd_gpio); break; case CONNECTOR_ID_SINGLE_LINK_DVID: case CONNECTOR_ID_SINGLE_LINK_DVII: @@ -653,7 +658,7 @@ static bool construct_phy(struct dc_link *link, } /* Look for device tag that matches connector signal, - * CRT for rgb, LCD for other supported signal tyes + * CRT for rgb, LCD for other supported signal types */ if (!bp_funcs->is_device_id_supported(dc_ctx->dc_bios, link->device_tag.dev_id)) @@ -698,7 +703,7 @@ static bool construct_phy(struct dc_link *link, link->chip_caps); } - if (link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) { + if ((link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK) == AMD_EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) { link->bios_forced_drive_settings.VOLTAGE_SWING = (bios->integrated_info->ext_disp_conn_info.fixdpvoltageswing & 0x3); link->bios_forced_drive_settings.PRE_EMPHASIS = diff --git a/drivers/gpu/drm/amd/display/dc/link/link_validation.c b/drivers/gpu/drm/amd/display/dc/link/link_validation.c index 60f15a9ba7a5..29606fda029d 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_validation.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_validation.c @@ -409,3 +409,182 @@ bool link_validate_dpia_bandwidth(const struct dc_stream_state *stream, const un return dpia_validate_usb4_bw(dpia_link, bw_needed, num_dpias); } + +struct dp_audio_layout_config { + uint8_t layouts_per_sample_denom; + uint8_t symbols_per_layout; + uint8_t max_layouts_per_audio_sdp; +}; + +static void get_audio_layout_config( + uint32_t channel_count, + enum dp_link_encoding encoding, + struct dp_audio_layout_config *output) +{ + memset(output, 0, sizeof(struct dp_audio_layout_config)); + + /* Assuming L-PCM audio. Current implementation uses max 1 layout per SDP, + * with each layout being the same size (8ch layout). + */ + if (encoding == DP_8b_10b_ENCODING) { + if (channel_count == 2) { + output->layouts_per_sample_denom = 4; + output->symbols_per_layout = 40; + output->max_layouts_per_audio_sdp = 1; + } else if (channel_count == 8 || channel_count == 6) { + output->layouts_per_sample_denom = 1; + output->symbols_per_layout = 40; + output->max_layouts_per_audio_sdp = 1; + } + } else if (encoding == DP_128b_132b_ENCODING) { + if (channel_count == 2) { + output->layouts_per_sample_denom = 4; + output->symbols_per_layout = 10; + output->max_layouts_per_audio_sdp = 1; + } else if (channel_count == 8 || channel_count == 6) { + output->layouts_per_sample_denom = 1; + output->symbols_per_layout = 10; + output->max_layouts_per_audio_sdp = 1; + } + } +} + +static uint32_t get_av_stream_map_lane_count( + enum dp_link_encoding encoding, + enum dc_lane_count lane_count, + bool is_mst) +{ + uint32_t av_stream_map_lane_count = 0; + + if (encoding == DP_8b_10b_ENCODING) { + if (!is_mst) + av_stream_map_lane_count = lane_count; + else + av_stream_map_lane_count = 4; + } else if (encoding == DP_128b_132b_ENCODING) { + av_stream_map_lane_count = 4; + } + + ASSERT(av_stream_map_lane_count != 0); + + return av_stream_map_lane_count; +} + +static uint32_t get_audio_sdp_overhead( + enum dp_link_encoding encoding, + enum dc_lane_count lane_count, + bool is_mst) +{ + uint32_t audio_sdp_overhead = 0; + + if (encoding == DP_8b_10b_ENCODING) { + if (is_mst) + audio_sdp_overhead = 16; /* 4 * 2 + 8 */ + else + audio_sdp_overhead = lane_count * 2 + 8; + } else if (encoding == DP_128b_132b_ENCODING) { + audio_sdp_overhead = 10; /* 4 x 2.5 */ + } + + ASSERT(audio_sdp_overhead != 0); + + return audio_sdp_overhead; +} + +/* Current calculation only applicable for 8b/10b MST and 128b/132b SST/MST. + */ +static uint32_t calculate_overhead_hblank_bw_in_symbols( + uint32_t max_slice_h) +{ + uint32_t overhead_hblank_bw = 0; /* in stream symbols */ + + overhead_hblank_bw += max_slice_h * 4; /* EOC overhead */ + overhead_hblank_bw += 12; /* Main link overhead (VBID, BS/BE) */ + + return overhead_hblank_bw; +} + +uint32_t dp_required_hblank_size_bytes( + const struct dc_link *link, + struct dp_audio_bandwidth_params *audio_params) +{ + /* Main logic from dce_audio is duplicated here, with the main + * difference being: + * - Pre-determined lane count of 4 + * - Assumed 16 dsc slices for worst case + * - Assumed SDP split disabled for worst case + * TODO: Unify logic from dce_audio to prevent duplicated logic. + */ + + const struct dc_crtc_timing *timing = audio_params->crtc_timing; + const uint32_t channel_count = audio_params->channel_count; + const uint32_t sample_rate_hz = audio_params->sample_rate_hz; + const enum dp_link_encoding link_encoding = audio_params->link_encoding; + + // 8b/10b MST and 128b/132b are always 4 logical lanes. + const uint32_t lane_count = 4; + const bool is_mst = (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT); + // Maximum slice count is with ODM 4:1, 4 slices per DSC + const uint32_t max_slices_h = 16; + + const uint32_t av_stream_map_lane_count = get_av_stream_map_lane_count( + link_encoding, lane_count, is_mst); + const uint32_t audio_sdp_overhead = get_audio_sdp_overhead( + link_encoding, lane_count, is_mst); + struct dp_audio_layout_config layout_config; + + if (link_encoding == DP_8b_10b_ENCODING && link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT) + return 0; + + get_audio_layout_config( + channel_count, link_encoding, &layout_config); + + /* DP spec recommends between 1.05 to 1.1 safety margin to prevent sample under-run */ + struct fixed31_32 audio_sdp_margin = dc_fixpt_from_fraction(110, 100); + struct fixed31_32 horizontal_line_freq_khz = dc_fixpt_from_fraction( + timing->pix_clk_100hz, (long long)timing->h_total * 10); + struct fixed31_32 samples_per_line; + struct fixed31_32 layouts_per_line; + struct fixed31_32 symbols_per_sdp_max_layout; + struct fixed31_32 remainder; + uint32_t num_sdp_with_max_layouts; + uint32_t required_symbols_per_hblank; + uint32_t required_bytes_per_hblank = 0; + + samples_per_line = dc_fixpt_from_fraction(sample_rate_hz, 1000); + samples_per_line = dc_fixpt_div(samples_per_line, horizontal_line_freq_khz); + layouts_per_line = dc_fixpt_div_int(samples_per_line, layout_config.layouts_per_sample_denom); + // HBlank expansion usage assumes SDP split disabled to allow for worst case. + layouts_per_line = dc_fixpt_from_int(dc_fixpt_ceil(layouts_per_line)); + + num_sdp_with_max_layouts = dc_fixpt_floor( + dc_fixpt_div_int(layouts_per_line, layout_config.max_layouts_per_audio_sdp)); + symbols_per_sdp_max_layout = dc_fixpt_from_int( + layout_config.max_layouts_per_audio_sdp * layout_config.symbols_per_layout); + symbols_per_sdp_max_layout = dc_fixpt_add_int(symbols_per_sdp_max_layout, audio_sdp_overhead); + symbols_per_sdp_max_layout = dc_fixpt_mul(symbols_per_sdp_max_layout, audio_sdp_margin); + required_symbols_per_hblank = num_sdp_with_max_layouts; + required_symbols_per_hblank *= ((dc_fixpt_ceil(symbols_per_sdp_max_layout) + av_stream_map_lane_count) / + av_stream_map_lane_count) * av_stream_map_lane_count; + + if (num_sdp_with_max_layouts != dc_fixpt_ceil( + dc_fixpt_div_int(layouts_per_line, layout_config.max_layouts_per_audio_sdp))) { + remainder = dc_fixpt_sub_int(layouts_per_line, + num_sdp_with_max_layouts * layout_config.max_layouts_per_audio_sdp); + remainder = dc_fixpt_mul_int(remainder, layout_config.symbols_per_layout); + remainder = dc_fixpt_add_int(remainder, audio_sdp_overhead); + remainder = dc_fixpt_mul(remainder, audio_sdp_margin); + required_symbols_per_hblank += ((dc_fixpt_ceil(remainder) + av_stream_map_lane_count) / + av_stream_map_lane_count) * av_stream_map_lane_count; + } + + required_symbols_per_hblank += calculate_overhead_hblank_bw_in_symbols(max_slices_h); + + if (link_encoding == DP_8b_10b_ENCODING) + required_bytes_per_hblank = required_symbols_per_hblank; // 8 bits per 8b/10b symbol + else if (link_encoding == DP_128b_132b_ENCODING) + required_bytes_per_hblank = required_symbols_per_hblank * 4; // 32 bits per 128b/132b symbol + + return required_bytes_per_hblank; +} + diff --git a/drivers/gpu/drm/amd/display/dc/link/link_validation.h b/drivers/gpu/drm/amd/display/dc/link/link_validation.h index 595fb05946e9..bf398c49c3e8 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_validation.h +++ b/drivers/gpu/drm/amd/display/dc/link/link_validation.h @@ -37,4 +37,9 @@ uint32_t dp_link_bandwidth_kbps( const struct dc_link *link, const struct dc_link_settings *link_settings); + +uint32_t dp_required_hblank_size_bytes( + const struct dc_link *link, + struct dp_audio_bandwidth_params *audio_params); + #endif /* __LINK_VALIDATION_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c index d6d5bbf2108c..267180e7bc48 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c @@ -505,7 +505,7 @@ bool try_to_configure_aux_timeout(struct ddc_service *ddc, bool result = false; struct ddc *ddc_pin = ddc->ddc_pin; - if ((ddc->link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && + if (((ddc->link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK) == AMD_EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && !ddc->link->dc->debug.disable_fixed_vs_aux_timeout_wa && ddc->ctx->dce_version == DCN_VERSION_3_1) { /* Fixed VS workaround for AUX timeout */ diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c index d78c8ec4de79..0f965380a9b4 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c @@ -51,9 +51,10 @@ #include "dc_dmub_srv.h" #include "gpio_service_interface.h" +#define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */ + #define DC_LOGGER \ link->ctx->logger -#define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */ #ifndef MAX #define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) @@ -157,6 +158,14 @@ uint8_t dp_parse_lttpr_repeater_count(uint8_t lttpr_repeater_count) return 0; // invalid value } +uint32_t dp_get_closest_lttpr_offset(uint8_t lttpr_count) +{ + /* Calculate offset for LTTPR closest to DPTX which is highest in the chain + * Offset is 0 for single LTTPR cases as base LTTPR DPCD addresses target LTTPR 1 + */ + return DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE * (lttpr_count - 1); +} + uint32_t link_bw_kbps_from_raw_frl_link_rate_data(uint8_t bw) { switch (bw) { @@ -249,21 +258,23 @@ static uint32_t intersect_frl_link_bw_support( { uint32_t supported_bw_in_kbps = max_supported_frl_bw_in_kbps; - // HDMI_ENCODED_LINK_BW bits are only valid if HDMI Link Configuration bit is 1 (FRL mode) - if (hdmi_encoded_link_bw.bits.FRL_MODE) { - if (hdmi_encoded_link_bw.bits.BW_48Gbps) - supported_bw_in_kbps = 48000000; - else if (hdmi_encoded_link_bw.bits.BW_40Gbps) - supported_bw_in_kbps = 40000000; - else if (hdmi_encoded_link_bw.bits.BW_32Gbps) - supported_bw_in_kbps = 32000000; - else if (hdmi_encoded_link_bw.bits.BW_24Gbps) - supported_bw_in_kbps = 24000000; - else if (hdmi_encoded_link_bw.bits.BW_18Gbps) - supported_bw_in_kbps = 18000000; - else if (hdmi_encoded_link_bw.bits.BW_9Gbps) - supported_bw_in_kbps = 9000000; - } + /* Skip checking FRL_MODE bit, as certain PCON will clear + * it despite supporting the link BW indicated in the other bits. + */ + if (hdmi_encoded_link_bw.bits.BW_48Gbps) + supported_bw_in_kbps = 48000000; + else if (hdmi_encoded_link_bw.bits.BW_40Gbps) + supported_bw_in_kbps = 40000000; + else if (hdmi_encoded_link_bw.bits.BW_32Gbps) + supported_bw_in_kbps = 32000000; + else if (hdmi_encoded_link_bw.bits.BW_24Gbps) + supported_bw_in_kbps = 24000000; + else if (hdmi_encoded_link_bw.bits.BW_18Gbps) + supported_bw_in_kbps = 18000000; + else if (hdmi_encoded_link_bw.bits.BW_9Gbps) + supported_bw_in_kbps = 9000000; + else if (hdmi_encoded_link_bw.bits.FRL_LINK_TRAINING_FINISHED) + supported_bw_in_kbps = 0; /* This case should only get hit in regulated autonomous mode. */ return supported_bw_in_kbps; } @@ -329,9 +340,12 @@ bool dp_is_fec_supported(const struct dc_link *link) /* TODO - use asic cap instead of link_enc->features * we no longer know which link enc to use for this link before commit */ - struct link_encoder *link_enc = NULL; + struct resource_context *res_ctx = &link->dc->current_state->res_ctx; + struct resource_pool *res_pool = link->dc->res_pool; + struct link_encoder *link_enc = get_temp_dio_link_enc(res_ctx, res_pool, link); - link_enc = link_enc_cfg_get_link_enc(link); + if (!link->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(link); ASSERT(link_enc); return (dc_is_dp_signal(link->connector_signal) && link_enc && @@ -371,9 +385,15 @@ bool dp_is_128b_132b_signal(struct pipe_ctx *pipe_ctx) bool dp_is_lttpr_present(struct dc_link *link) { /* Some sink devices report invalid LTTPR revision, so don't validate against that cap */ - return (dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt) != 0 && + uint32_t lttpr_count = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt); + bool is_lttpr_present = (lttpr_count > 0 && link->dpcd_caps.lttpr_caps.max_lane_count > 0 && link->dpcd_caps.lttpr_caps.max_lane_count <= 4); + + if (lttpr_count > 0 && !is_lttpr_present) + DC_LOG_ERROR("LTTPR count is nonzero but invalid lane count reported. Assuming no LTTPR present.\n"); + + return is_lttpr_present; } /* in DP compliance test, DPR-120 may have @@ -944,6 +964,9 @@ bool link_decide_link_settings(struct dc_stream_state *stream, * TODO: add MST specific link training routine */ decide_mst_link_settings(link, link_setting); + } else if (stream->signal == SIGNAL_TYPE_VIRTUAL) { + link_setting->lane_count = LANE_COUNT_FOUR; + link_setting->link_rate = LINK_RATE_HIGH3; } else if (link->connector_signal == SIGNAL_TYPE_EDP) { /* enable edp link optimization for DSC eDP case */ if (stream->timing.flags.DSC) { @@ -966,9 +989,6 @@ bool link_decide_link_settings(struct dc_stream_state *stream, } else { edp_decide_link_settings(link, link_setting, req_bw); } - } else if (stream->signal == SIGNAL_TYPE_VIRTUAL) { - link_setting->lane_count = LANE_COUNT_FOUR; - link_setting->link_rate = LINK_RATE_HIGH3; } else { decide_dp_link_settings(link, link_setting, req_bw); } @@ -1071,6 +1091,48 @@ static enum dc_status wake_up_aux_channel(struct dc_link *link) return DC_OK; } +static void read_and_intersect_post_frl_lt_status( + struct dc_link *link) +{ + union autonomous_mode_and_frl_link_status autonomous_mode_caps = {0}; + union hdmi_tx_link_status hdmi_tx_link_status = {0}; + union hdmi_encoded_link_bw hdmi_encoded_link_bw = {0}; + + /* Check if dongle supports regulated autonomous mode. */ + core_link_read_dpcd(link, DP_REGULATED_AUTONOMOUS_MODE_SUPPORTED_AND_HDMI_LINK_TRAINING_STATUS, + &autonomous_mode_caps.raw, sizeof(autonomous_mode_caps)); + + link->dpcd_caps.dongle_caps.dp_hdmi_regulated_autonomous_mode_support = + autonomous_mode_caps.bits.REGULATED_AUTONOMOUS_MODE_SUPPORTED; + + if (link->dpcd_caps.dongle_caps.dp_hdmi_regulated_autonomous_mode_support) { + DC_LOG_DC("%s: PCON supports regulated autonomous mode.\n", __func__); + + core_link_read_dpcd(link, DP_PCON_HDMI_TX_LINK_STATUS, + &hdmi_tx_link_status.raw, sizeof(hdmi_tx_link_status)); + } + + // Intersect reported max link bw support with the supported link rate post FRL link training + if (core_link_read_dpcd(link, DP_PCON_HDMI_POST_FRL_STATUS, + &hdmi_encoded_link_bw.raw, sizeof(hdmi_encoded_link_bw)) == DC_OK) { + + if (link->dpcd_caps.dongle_caps.dp_hdmi_regulated_autonomous_mode_support && + (!hdmi_tx_link_status.bits.HDMI_TX_READY_STATUS || + !hdmi_encoded_link_bw.bits.FRL_LINK_TRAINING_FINISHED)) { + DC_LOG_WARNING("%s: PCON TX link training has not finished.\n", __func__); + + /* Link training not finished, ignore values from this DPCD reg. */ + return; + } + + link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps = intersect_frl_link_bw_support( + link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps, + hdmi_encoded_link_bw); + DC_LOG_DC("%s: pcon frl link bw = %u\n", __func__, + link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps); + } +} + static void get_active_converter_info( uint8_t data, struct dc_link *link) { @@ -1159,21 +1221,12 @@ static void get_active_converter_info( hdmi_color_caps.bits.MAX_BITS_PER_COLOR_COMPONENT); if (link->dc->caps.dp_hdmi21_pcon_support) { - union hdmi_encoded_link_bw hdmi_encoded_link_bw; link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps = link_bw_kbps_from_raw_frl_link_rate_data( hdmi_color_caps.bits.MAX_ENCODED_LINK_BW_SUPPORT); - // Intersect reported max link bw support with the supported link rate post FRL link training - if (core_link_read_dpcd(link, DP_PCON_HDMI_POST_FRL_STATUS, - &hdmi_encoded_link_bw.raw, sizeof(hdmi_encoded_link_bw)) == DC_OK) { - link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps = intersect_frl_link_bw_support( - link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps, - hdmi_encoded_link_bw); - DC_LOG_DC("%s: pcon frl link bw = %u\n", __func__, - link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps); - } + read_and_intersect_post_frl_lt_status(link); if (link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps > 0) link->dpcd_caps.dongle_caps.extendedCapValid = true; @@ -1207,6 +1260,13 @@ static void get_active_converter_info( dp_hw_fw_revision.ieee_fw_rev, sizeof(dp_hw_fw_revision.ieee_fw_rev)); } + + core_link_read_dpcd( + link, + DP_BRANCH_VENDOR_SPECIFIC_START, + (uint8_t *)link->dpcd_caps.branch_vendor_specific_data, + sizeof(link->dpcd_caps.branch_vendor_specific_data)); + if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_14 && link->dpcd_caps.dongle_type != DISPLAY_DONGLE_NONE) { union dp_dfp_cap_ext dfp_cap_ext; @@ -1494,9 +1554,11 @@ static bool dpcd_read_sink_ext_caps(struct dc_link *link) enum dc_status dp_retrieve_lttpr_cap(struct dc_link *link) { - uint8_t lttpr_dpcd_data[8] = {0}; + uint8_t lttpr_dpcd_data[10] = {0}; enum dc_status status; bool is_lttpr_present; + uint32_t lttpr_count; + uint32_t closest_lttpr_offset; /* Logic to determine LTTPR support*/ bool vbios_lttpr_interop = link->dc->caps.vbios_lttpr_aware; @@ -1544,26 +1606,54 @@ enum dc_status dp_retrieve_lttpr_cap(struct dc_link *link) lttpr_dpcd_data[DP_PHY_REPEATER_128B132B_RATES - DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV]; + link->dpcd_caps.lttpr_caps.alpm.raw = + lttpr_dpcd_data[DP_LTTPR_ALPM_CAPABILITIES - + DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV]; + + lttpr_count = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt); + /* If this chip cap is set, at least one retimer must exist in the chain * Override count to 1 if we receive a known bad count (0 or an invalid value) */ - if ((link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && - (dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt) == 0)) { + if (((link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK) == AMD_EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && + lttpr_count == 0) { /* If you see this message consistently, either the host platform has FIXED_VS flag * incorrectly configured or the sink device is returning an invalid count. */ DC_LOG_ERROR("lttpr_caps phy_repeater_cnt is 0x%x, forcing it to 0x80.", link->dpcd_caps.lttpr_caps.phy_repeater_cnt); link->dpcd_caps.lttpr_caps.phy_repeater_cnt = 0x80; + lttpr_count = 1; DC_LOG_DC("lttpr_caps forced phy_repeater_cnt = %d\n", link->dpcd_caps.lttpr_caps.phy_repeater_cnt); } - /* Attempt to train in LTTPR transparent mode if repeater count exceeds 8. */ is_lttpr_present = dp_is_lttpr_present(link); - if (is_lttpr_present) + DC_LOG_DC("is_lttpr_present = %d\n", is_lttpr_present); + + if (is_lttpr_present) { CONN_DATA_DETECT(link, lttpr_dpcd_data, sizeof(lttpr_dpcd_data), "LTTPR Caps: "); - DC_LOG_DC("is_lttpr_present = %d\n", is_lttpr_present); + // Identify closest LTTPR to determine if workarounds required for known embedded LTTPR + closest_lttpr_offset = dp_get_closest_lttpr_offset(lttpr_count); + + core_link_read_dpcd(link, (DP_LTTPR_IEEE_OUI + closest_lttpr_offset), + link->dpcd_caps.lttpr_caps.lttpr_ieee_oui, sizeof(link->dpcd_caps.lttpr_caps.lttpr_ieee_oui)); + core_link_read_dpcd(link, (DP_LTTPR_DEVICE_ID + closest_lttpr_offset), + link->dpcd_caps.lttpr_caps.lttpr_device_id, sizeof(link->dpcd_caps.lttpr_caps.lttpr_device_id)); + + if (lttpr_count > 1) { + CONN_DATA_DETECT(link, link->dpcd_caps.lttpr_caps.lttpr_ieee_oui, sizeof(link->dpcd_caps.lttpr_caps.lttpr_ieee_oui), + "Closest LTTPR To Host's IEEE OUI: "); + CONN_DATA_DETECT(link, link->dpcd_caps.lttpr_caps.lttpr_device_id, sizeof(link->dpcd_caps.lttpr_caps.lttpr_device_id), + "Closest LTTPR To Host's LTTPR Device ID: "); + } else { + CONN_DATA_DETECT(link, link->dpcd_caps.lttpr_caps.lttpr_ieee_oui, sizeof(link->dpcd_caps.lttpr_caps.lttpr_ieee_oui), + "LTTPR IEEE OUI: "); + CONN_DATA_DETECT(link, link->dpcd_caps.lttpr_caps.lttpr_device_id, sizeof(link->dpcd_caps.lttpr_caps.lttpr_device_id), + "LTTPR Device ID: "); + } + } + return status; } @@ -1624,9 +1714,6 @@ static bool retrieve_link_cap(struct dc_link *link) sizeof(link->dpcd_caps.lttpr_caps.phy_repeater_cnt)); } - /* Read DP tunneling information. */ - status = dpcd_get_tunneling_device_data(link); - dpcd_set_source_specific_data(link); /* Sink may need to configure internals based on vendor, so allow some * time before proceeding with possibly vendor specific transactions @@ -1699,7 +1786,7 @@ static bool retrieve_link_cap(struct dc_link *link) link->dpcd_caps.dprx_feature.raw = dpcd_dprx_data; if (status != DC_OK) - dm_error("%s: Read DPRX caps data failed.\n", __func__); + dm_error("%s: Read DPRX feature list failed.\n", __func__); /* AdaptiveSyncCapability */ dpcd_dprx_data = 0; @@ -1714,15 +1801,13 @@ static bool retrieve_link_cap(struct dc_link *link) link->dpcd_caps.adaptive_sync_caps.dp_adap_sync_caps.raw = dpcd_dprx_data; if (status != DC_OK) - dm_error("%s: Read DPRX caps data failed. Addr:%#x\n", + dm_error("%s: Read DPRX feature list_1 failed. Addr:%#x\n", __func__, DP_DPRX_FEATURE_ENUMERATION_LIST_CONT_1); } - else { link->dpcd_caps.dprx_feature.raw = 0; } - /* Error condition checking... * It is impossible for Sink to report Max Lane Count = 0. * It is possible for Sink to report Max Link Rate = 0, if it is @@ -1776,6 +1861,11 @@ static bool retrieve_link_cap(struct dc_link *link) link->test_pattern_enabled = false; link->compliance_test_state.raw = 0; + link->dpcd_caps.receive_port0_cap.raw[0] = + dpcd_data[DP_RECEIVE_PORT_0_CAP_0 - DP_DPCD_REV]; + link->dpcd_caps.receive_port0_cap.raw[1] = + dpcd_data[DP_RECEIVE_PORT_0_BUFFER_SIZE - DP_DPCD_REV]; + /* read sink count */ core_link_read_dpcd(link, DP_SINK_COUNT, @@ -1842,6 +1932,9 @@ static bool retrieve_link_cap(struct dc_link *link) DP_FEC_CAPABILITY, &link->dpcd_caps.fec_cap.raw, sizeof(link->dpcd_caps.fec_cap.raw)); + if (status != DC_OK) + DC_LOG_ERROR("%s:%d: core_link_read_dpcd (DP_FEC_CAPABILITY) failed\n", __func__, __LINE__); + status = core_link_read_dpcd( link, DP_DSC_SUPPORT, @@ -1864,6 +1957,9 @@ static bool retrieve_link_cap(struct dc_link *link) DP_DSC_BRANCH_OVERALL_THROUGHPUT_0, link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.raw, sizeof(link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.raw)); + if (status != DC_OK) + DC_LOG_ERROR("%s:%d: core_link_read_dpcd (DP_DSC_BRANCH_OVERALL_THROUGHPUT_0) failed\n", __func__, __LINE__); + DC_LOG_DSC("DSC branch decoder capability is read at link %d", link->link_index); DC_LOG_DSC("\tBRANCH_OVERALL_THROUGHPUT_0 = 0x%02x", link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.fields.BRANCH_OVERALL_THROUGHPUT_0); @@ -1900,6 +1996,7 @@ static bool retrieve_link_cap(struct dc_link *link) if (link->dpcd_caps.channel_coding_cap.bits.DP_128b_132b_SUPPORTED) { DC_LOG_DP2("128b/132b encoding is supported at link %d", link->link_index); + /* Read 128b/132b suppoerted link rates */ core_link_read_dpcd(link, DP_128B132B_SUPPORTED_LINK_RATES, &link->dpcd_caps.dp_128b_132b_supported_link_rates.raw, @@ -1947,6 +2044,11 @@ static bool retrieve_link_cap(struct dc_link *link) link->dpcd_caps.max_uncompressed_pixel_rate_cap.raw, sizeof(link->dpcd_caps.max_uncompressed_pixel_rate_cap.raw)); + /* Read DP tunneling information. */ + status = dpcd_get_tunneling_device_data(link); + if (status != DC_OK) + DC_LOG_DP2("%s: Read DP tunneling device data failed.\n", __func__); + retrieve_cable_id(link); dpcd_write_cable_id_to_dprx(link); @@ -2055,18 +2157,40 @@ void detect_edp_sink_caps(struct dc_link *link) core_link_read_dpcd(link, DP_SINK_PR_MAX_NUMBER_OF_DEVIATION_LINE, &link->dpcd_caps.pr_info.max_deviation_line, sizeof(link->dpcd_caps.pr_info.max_deviation_line)); + + /* + * OLED Emission Rate info + */ + if (link->dpcd_sink_ext_caps.bits.emission_output) + core_link_read_dpcd(link, DP_SINK_EMISSION_RATE, + (uint8_t *)&link->dpcd_caps.edp_oled_emission_rate, + sizeof(link->dpcd_caps.edp_oled_emission_rate)); + + /* + * Read Multi-SST (Single Stream Transport) capability + * for eDP version 1.4 or higher. + */ + if (link->dpcd_caps.dpcd_rev.raw >= DP_EDP_14) + core_link_read_dpcd( + link, + DP_EDP_MSO_LINK_CAPABILITIES, + (uint8_t *)&link->dpcd_caps.mso_cap_sst_links_supported, + sizeof(link->dpcd_caps.mso_cap_sst_links_supported)); } bool dp_get_max_link_enc_cap(const struct dc_link *link, struct dc_link_settings *max_link_enc_cap) { - struct link_encoder *link_enc = NULL; + struct resource_context *res_ctx = &link->dc->current_state->res_ctx; + struct resource_pool *res_pool = link->dc->res_pool; + struct link_encoder *link_enc = get_temp_dio_link_enc(res_ctx, res_pool, link); if (!max_link_enc_cap) { DC_LOG_ERROR("%s: Could not return max link encoder caps", __func__); return false; } - link_enc = link_enc_cfg_get_link_enc(link); + if (!link->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(link); ASSERT(link_enc); if (link_enc && link_enc->funcs->get_max_link_cap) { @@ -2094,10 +2218,13 @@ struct dc_link_settings dp_get_max_link_cap(struct dc_link *link) struct dc_link_settings max_link_cap = {0}; enum dc_link_rate lttpr_max_link_rate; enum dc_link_rate cable_max_link_rate; - struct link_encoder *link_enc = NULL; + struct resource_context *res_ctx = &link->dc->current_state->res_ctx; + struct resource_pool *res_pool = link->dc->res_pool; + struct link_encoder *link_enc = get_temp_dio_link_enc(res_ctx, res_pool, link); bool is_uhbr13_5_supported = true; - link_enc = link_enc_cfg_get_link_enc(link); + if (!link->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(link); ASSERT(link_enc); /* get max link encoder capability */ @@ -2282,6 +2409,14 @@ bool dp_verify_link_cap_with_retries( } else { link->verified_link_cap = last_verified_link_cap; } + + /* For Dp tunneling link, a pending HPD means that we have a race condition between processing + * current link and processing the pending HPD. Since the training is failed, we should just brak + * the loop so that we have chance to process the pending HPD. + */ + if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && link->is_hpd_pending) + break; + fsleep(10 * 1000); } diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.h index 8f0ce97f2362..940b147cc5d4 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.h +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.h @@ -48,6 +48,9 @@ enum dc_status dp_retrieve_lttpr_cap(struct dc_link *link); /* Convert PHY repeater count read from DPCD uint8_t. */ uint8_t dp_parse_lttpr_repeater_count(uint8_t lttpr_repeater_count); +/* Calculate embedded LTTPR address offset for vendor-specific behaviour */ +uint32_t dp_get_closest_lttpr_offset(uint8_t lttpr_count); + bool dp_is_sink_present(struct dc_link *link); bool dp_is_lttpr_present(struct dc_link *link); @@ -67,6 +70,7 @@ bool dp_is_128b_132b_signal(struct pipe_ctx *pipe_ctx); /* Initialize output parameter lt_settings. */ void dp_decide_training_settings( struct dc_link *link, + const struct link_resource *link_res, const struct dc_link_settings *link_setting, struct link_training_settings *lt_settings); diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.c index 6af42ba9885c..22bfdced64ab 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.c @@ -59,22 +59,52 @@ enum dc_status dpcd_get_tunneling_device_data(struct dc_link *link) dpcd_dp_tun_data, sizeof(dpcd_dp_tun_data)); - status = core_link_read_dpcd( - link, - DP_USB4_ROUTER_TOPOLOGY_ID, - dpcd_topology_data, - sizeof(dpcd_topology_data)); + if (status != DC_OK) + goto err; link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.raw = dpcd_dp_tun_data[DP_TUNNELING_CAPABILITIES_SUPPORT - DP_TUNNELING_CAPABILITIES_SUPPORT]; + + if (link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling == false) + goto err; + link->dpcd_caps.usb4_dp_tun_info.dpia_info.raw = dpcd_dp_tun_data[DP_IN_ADAPTER_INFO - DP_TUNNELING_CAPABILITIES_SUPPORT]; link->dpcd_caps.usb4_dp_tun_info.usb4_driver_id = dpcd_dp_tun_data[DP_USB4_DRIVER_ID - DP_TUNNELING_CAPABILITIES_SUPPORT]; + if (link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dpia_bw_alloc) { + status = core_link_read_dpcd(link, USB4_DRIVER_BW_CAPABILITY, + dpcd_dp_tun_data, 1); + + if (status != DC_OK) + goto err; + + link->dpcd_caps.usb4_dp_tun_info.driver_bw_cap.raw = dpcd_dp_tun_data[0]; + } + + DC_LOG_DEBUG("%s: Link[%d] DP tunneling support (RouterId=%d AdapterId=%d) " + "DPIA_BW_Alloc_support=%d " + "CM_BW_Alloc_support=%d ", + __func__, link->link_index, + link->dpcd_caps.usb4_dp_tun_info.usb4_driver_id, + link->dpcd_caps.usb4_dp_tun_info.dpia_info.bits.dpia_num, + link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dpia_bw_alloc, + link->dpcd_caps.usb4_dp_tun_info.driver_bw_cap.bits.driver_bw_alloc_support); + + status = core_link_read_dpcd( + link, + DP_USB4_ROUTER_TOPOLOGY_ID, + dpcd_topology_data, + sizeof(dpcd_topology_data)); + + if (status != DC_OK) + goto err; + for (i = 0; i < DPCD_USB4_TOPOLOGY_ID_LEN; i++) link->dpcd_caps.usb4_dp_tun_info.usb4_topology_id[i] = dpcd_topology_data[i]; +err: return status; } @@ -85,6 +115,7 @@ bool dpia_query_hpd_status(struct dc_link *link) /* prepare QUERY_HPD command */ cmd.query_hpd.header.type = DMUB_CMD__QUERY_HPD_STATE; + cmd.query_hpd.header.payload_bytes = sizeof(cmd.query_hpd.data); cmd.query_hpd.data.instance = link->link_id.enum_id - ENUM_ID_1; cmd.query_hpd.data.ch_type = AUX_CHANNEL_DPIA; @@ -112,3 +143,20 @@ bool dpia_query_hpd_status(struct dc_link *link) return link->hpd_status; } +void link_decide_dp_tunnel_settings(struct dc_stream_state *stream, + struct dc_tunnel_settings *dp_tunnel_setting) +{ + struct dc_link *link = stream->link; + + memset(dp_tunnel_setting, 0, sizeof(*dp_tunnel_setting)); + + if ((stream->signal == SIGNAL_TYPE_DISPLAY_PORT) || (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)) { + dp_tunnel_setting->should_enable_dp_tunneling = + link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling; + + if (link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dpia_bw_alloc + && link->dpcd_caps.usb4_dp_tun_info.driver_bw_cap.bits.driver_bw_alloc_support) + dp_tunnel_setting->should_use_dp_bw_allocation = true; + } +} + diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.h index 363f45a1a964..a61edfc9ca7a 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.h +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.h @@ -38,4 +38,10 @@ enum dc_status dpcd_get_tunneling_device_data(struct dc_link *link); * Returns true if HPD high. */ bool dpia_query_hpd_status(struct dc_link *link); + +/* Decide the DP tunneling settings based on the DPCD capabilities + */ +void link_decide_dp_tunnel_settings(struct dc_stream_state *stream, + struct dc_tunnel_settings *dp_tunnel_setting); + #endif /* __DC_LINK_DPIA_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c index 0f1c411523a2..3af7564a84f1 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c @@ -24,7 +24,7 @@ * */ /*********************************************************************/ -// USB4 DPIA BANDWIDTH ALLOCATION LOGIC +// USB4 DPIA BANDWIDTH ALLOCATION LOGIC /*********************************************************************/ #include "link_dp_dpia_bw.h" #include "link_dpcd.h" @@ -36,7 +36,7 @@ #define Kbps_TO_Gbps (1000 * 1000) // ------------------------------------------------------------------ -// PRIVATE FUNCTIONS +// PRIVATE FUNCTIONS // ------------------------------------------------------------------ /* * Always Check the following: @@ -44,11 +44,12 @@ * - Is HPD HIGH? * - Is BW Allocation Support Mode enabled on DP-Tx? */ -static bool get_bw_alloc_proceed_flag(struct dc_link *tmp) +static bool link_dp_is_bw_alloc_available(struct dc_link *link) { - return (tmp && DISPLAY_ENDPOINT_USB4_DPIA == tmp->ep_type - && tmp->hpd_status - && tmp->dpia_bw_alloc_config.bw_alloc_enabled); + return (link && link->hpd_status + && link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling + && link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dpia_bw_alloc + && link->dpcd_caps.usb4_dp_tun_info.driver_bw_cap.bits.driver_bw_alloc_support); } static void reset_bw_alloc_struct(struct dc_link *link) @@ -60,7 +61,6 @@ static void reset_bw_alloc_struct(struct dc_link *link) link->dpia_bw_alloc_config.estimated_bw = 0; link->dpia_bw_alloc_config.bw_granularity = 0; link->dpia_bw_alloc_config.dp_overhead = 0; - link->dpia_bw_alloc_config.response_ready = false; link->dpia_bw_alloc_config.nrd_max_lane_count = 0; link->dpia_bw_alloc_config.nrd_max_link_rate = 0; for (int i = 0; i < MAX_SINKS_PER_LINK; i++) @@ -142,7 +142,7 @@ static int get_non_reduced_max_lane_count(struct dc_link *link) * granuality, Driver_ID, CM_Group, & populate the BW allocation structs * for host router and dpia */ -static void init_usb4_bw_struct(struct dc_link *link) +static void retrieve_usb4_dp_bw_allocation_info(struct dc_link *link) { reset_bw_alloc_struct(link); @@ -243,20 +243,20 @@ static int get_host_router_total_dp_tunnel_bw(const struct dc *dc, uint8_t hr_in static void dpia_bw_alloc_unplug(struct dc_link *link) { if (link) { - DC_LOG_DEBUG("%s: resetting bw alloc config for link(%d)\n", + DC_LOG_DEBUG("%s: resetting BW alloc config for link(%d)\n", __func__, link->link_index); reset_bw_alloc_struct(link); } } -static void set_usb4_req_bw_req(struct dc_link *link, int req_bw) +static void link_dpia_send_bw_alloc_request(struct dc_link *link, int req_bw) { uint8_t requested_bw; uint32_t temp; /* Error check whether request bw greater than allocated */ if (req_bw > link->dpia_bw_alloc_config.estimated_bw) { - DC_LOG_ERROR("%s: Request bw greater than estimated bw for link(%d)\n", + DC_LOG_ERROR("%s: Request BW greater than estimated BW for link(%d)\n", __func__, link->link_index); req_bw = link->dpia_bw_alloc_config.estimated_bw; } @@ -271,76 +271,38 @@ static void set_usb4_req_bw_req(struct dc_link *link, int req_bw) /* Error check whether requested and allocated are equal */ req_bw = requested_bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); if (req_bw && (req_bw == link->dpia_bw_alloc_config.allocated_bw)) { - DC_LOG_ERROR("%s: Request bw equals to allocated bw for link(%d)\n", + DC_LOG_ERROR("%s: Request BW equals to allocated BW for link(%d)\n", __func__, link->link_index); } - link->dpia_bw_alloc_config.response_ready = false; // Reset flag - core_link_write_dpcd( - link, - REQUESTED_BW, + core_link_write_dpcd(link, REQUESTED_BW, &requested_bw, sizeof(uint8_t)); } -/* - * Return the response_ready flag from dc_link struct - * - * @link: pointer to the dc_link struct instance - * - * return: response_ready flag from dc_link struct - */ -static bool get_cm_response_ready_flag(struct dc_link *link) -{ - return link->dpia_bw_alloc_config.response_ready; -} - // ------------------------------------------------------------------ -// PUBLIC FUNCTIONS +// PUBLIC FUNCTIONS // ------------------------------------------------------------------ -bool link_dp_dpia_set_dptx_usb4_bw_alloc_support(struct dc_link *link) +bool link_dpia_enable_usb4_dp_bw_alloc_mode(struct dc_link *link) { bool ret = false; - uint8_t response = 0, - bw_support_dpia = 0, - bw_support_cm = 0; + uint8_t val; - if (!(link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && link->hpd_status)) - goto out; + if (link->hpd_status) { + val = DPTX_BW_ALLOC_MODE_ENABLE | DPTX_BW_ALLOC_UNMASK_IRQ; - if (core_link_read_dpcd( - link, - DP_TUNNELING_CAPABILITIES, - &response, - sizeof(uint8_t)) == DC_OK) - bw_support_dpia = (response >> 7) & 1; - - if (core_link_read_dpcd( - link, - USB4_DRIVER_BW_CAPABILITY, - &response, - sizeof(uint8_t)) == DC_OK) - bw_support_cm = (response >> 7) & 1; - - /* Send request acknowledgment to Turn ON DPTX support */ - if (bw_support_cm && bw_support_dpia) { - - response = 0x80; - if (core_link_write_dpcd( - link, - DPTX_BW_ALLOCATION_MODE_CONTROL, - &response, - sizeof(uint8_t)) != DC_OK) { - DC_LOG_DEBUG("%s: FAILURE Enabling DPtx BW Allocation Mode Support for link(%d)\n", - __func__, link->link_index); - } else { - // SUCCESS Enabled DPtx BW Allocation Mode Support - DC_LOG_DEBUG("%s: SUCCESS Enabling DPtx BW Allocation Mode Support for link(%d)\n", - __func__, link->link_index); + if (core_link_write_dpcd(link, DPTX_BW_ALLOCATION_MODE_CONTROL, &val, sizeof(uint8_t)) == DC_OK) { + DC_LOG_DEBUG("%s: link[%d] DPTX BW allocation mode enabled", __func__, link->link_index); + + retrieve_usb4_dp_bw_allocation_info(link); + + if (link->dpia_bw_alloc_config.nrd_max_link_rate && link->dpia_bw_alloc_config.nrd_max_lane_count) { + link->reported_link_cap.link_rate = link->dpia_bw_alloc_config.nrd_max_link_rate; + link->reported_link_cap.lane_count = link->dpia_bw_alloc_config.nrd_max_lane_count; + } - ret = true; - init_usb4_bw_struct(link); link->dpia_bw_alloc_config.bw_alloc_enabled = true; + ret = true; /* * During DP tunnel creation, CM preallocates BW and reduces estimated BW of other @@ -348,149 +310,78 @@ bool link_dp_dpia_set_dptx_usb4_bw_alloc_support(struct dc_link *link) * to make the CM to release preallocation and update estimated BW correctly for * all DPIAs per host router */ + // TODO: Zero allocation can be removed once the MSFT CM fix has been released link_dp_dpia_allocate_usb4_bandwidth_for_stream(link, 0); - } + } else + DC_LOG_DEBUG("%s: link[%d] failed to enable DPTX BW allocation mode", __func__, link->link_index); } -out: return ret; } -void dpia_handle_bw_alloc_response(struct dc_link *link, uint8_t bw, uint8_t result) +/* + * Handle DP BW allocation status register + * + * @link: pointer to the dc_link struct instance + * @status: content of DP tunneling status DPCD register + * + * return: none + */ +void link_dp_dpia_handle_bw_alloc_status(struct dc_link *link, uint8_t status) { - int bw_needed = 0; - int estimated = 0; - - if (!get_bw_alloc_proceed_flag((link))) - return; - - switch (result) { - - case DPIA_BW_REQ_FAILED: - - /* - * Ideally, we shouldn't run into this case as we always validate available - * bandwidth and request within that limit - */ - estimated = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); - - DC_LOG_ERROR("%s: BW REQ FAILURE for DP-TX Request for link(%d)\n", - __func__, link->link_index); - DC_LOG_ERROR("%s: current estimated_bw(%d), new estimated_bw(%d)\n", - __func__, link->dpia_bw_alloc_config.estimated_bw, estimated); - - /* Update the new Estimated BW value updated by CM */ - link->dpia_bw_alloc_config.estimated_bw = estimated; - - /* Allocate the previously requested bandwidth */ - set_usb4_req_bw_req(link, link->dpia_bw_alloc_config.estimated_bw); - - /* - * If FAIL then it is either: - * 1. Due to DP-Tx trying to allocate more than available i.e. it failed locally - * => get estimated and allocate that - * 2. Due to the fact that DP-Tx tried to allocated ESTIMATED BW and failed then - * CM will have to update 0xE0023 with new ESTIMATED BW value. - */ - break; - - case DPIA_BW_REQ_SUCCESS: - - bw_needed = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); - - DC_LOG_DEBUG("%s: BW REQ SUCCESS for DP-TX Request for link(%d)\n", - __func__, link->link_index); - DC_LOG_DEBUG("%s: current allocated_bw(%d), new allocated_bw(%d)\n", - __func__, link->dpia_bw_alloc_config.allocated_bw, bw_needed); - - link->dpia_bw_alloc_config.allocated_bw = bw_needed; - - link->dpia_bw_alloc_config.response_ready = true; - break; - - case DPIA_EST_BW_CHANGED: - - estimated = bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); - - DC_LOG_DEBUG("%s: ESTIMATED BW CHANGED for link(%d)\n", - __func__, link->link_index); - DC_LOG_DEBUG("%s: current estimated_bw(%d), new estimated_bw(%d)\n", - __func__, link->dpia_bw_alloc_config.estimated_bw, estimated); + if (status & DP_TUNNELING_BW_REQUEST_SUCCEEDED) { + DC_LOG_DEBUG("%s: BW Allocation request succeeded on link(%d)", + __func__, link->link_index); + } else if (status & DP_TUNNELING_BW_REQUEST_FAILED) { + link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link); - link->dpia_bw_alloc_config.estimated_bw = estimated; - break; + DC_LOG_DEBUG("%s: BW Allocation request failed on link(%d) allocated/estimated BW=%d", + __func__, link->link_index, link->dpia_bw_alloc_config.estimated_bw); - case DPIA_BW_ALLOC_CAPS_CHANGED: + link_dpia_send_bw_alloc_request(link, link->dpia_bw_alloc_config.estimated_bw); + } else if (status & DP_TUNNELING_ESTIMATED_BW_CHANGED) { + link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link); - DC_LOG_ERROR("%s: BW ALLOC CAPABILITY CHANGED to Disabled for link(%d)\n", - __func__, link->link_index); - link->dpia_bw_alloc_config.bw_alloc_enabled = false; - break; + DC_LOG_DEBUG("%s: Estimated BW changed on link(%d) new estimated BW=%d", + __func__, link->link_index, link->dpia_bw_alloc_config.estimated_bw); } -} -int dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int peak_bw) -{ - int ret = 0; - uint8_t timeout = 10; - - if (!(link && DISPLAY_ENDPOINT_USB4_DPIA == link->ep_type - && link->dpia_bw_alloc_config.bw_alloc_enabled)) - goto out; - - //1. Hot Plug - if (link->hpd_status && peak_bw > 0) { - - // If DP over USB4 then we need to check BW allocation - link->dpia_bw_alloc_config.link_max_bw = peak_bw; - set_usb4_req_bw_req(link, link->dpia_bw_alloc_config.link_max_bw); - do { - if (timeout > 0) - timeout--; - else - break; - msleep(10); - } while (!get_cm_response_ready_flag(link)); + core_link_write_dpcd( + link, DP_TUNNELING_STATUS, + &status, sizeof(status)); +} - if (!timeout) - ret = 0;// ERROR TIMEOUT waiting for response for allocating bw - else if (link->dpia_bw_alloc_config.allocated_bw > 0) - ret = link->dpia_bw_alloc_config.allocated_bw; +/* + * Handle the DP Bandwidth allocation for DPIA + * + */ +void dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int peak_bw) +{ + if (link && link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling + && link->dpia_bw_alloc_config.bw_alloc_enabled) { + //1. Hot Plug + if (link->hpd_status && peak_bw > 0) { + // If DP over USB4 then we need to check BW allocation + link->dpia_bw_alloc_config.link_max_bw = peak_bw; + + link_dpia_send_bw_alloc_request(link, peak_bw); + } + //2. Cold Unplug + else if (!link->hpd_status) + dpia_bw_alloc_unplug(link); } - //2. Cold Unplug - else if (!link->hpd_status) - dpia_bw_alloc_unplug(link); - -out: - return ret; } -bool link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int req_bw) -{ - bool ret = false; - uint8_t timeout = 10; +void link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int req_bw) +{ DC_LOG_DEBUG("%s: ENTER: link(%d), hpd_status(%d), current allocated_bw(%d), req_bw(%d)\n", __func__, link->link_index, link->hpd_status, link->dpia_bw_alloc_config.allocated_bw, req_bw); - if (!get_bw_alloc_proceed_flag(link)) - goto out; - - set_usb4_req_bw_req(link, req_bw); - do { - if (timeout > 0) - timeout--; - else - break; - msleep(10); - } while (!get_cm_response_ready_flag(link)); - - if (timeout) - ret = true; - -out: - DC_LOG_DEBUG("%s: EXIT: timeout(%d), ret(%d)\n", __func__, timeout, ret); - return ret; + if (link_dp_is_bw_alloc_available(link)) + link_dpia_send_bw_alloc_request(link, req_bw); + else + DC_LOG_DEBUG("%s: BW Allocation mode not available", __func__); } bool dpia_validate_usb4_bw(struct dc_link **link, int *bw_needed_per_dpia, const unsigned int num_dpias) @@ -541,7 +432,7 @@ int link_dp_dpia_get_dp_overhead_in_dp_tunneling(struct dc_link *link) { int dp_overhead = 0, link_mst_overhead = 0; - if (!get_bw_alloc_proceed_flag((link))) + if (!link_dp_is_bw_alloc_available(link)) return dp_overhead; /* if its mst link, add MTPH overhead */ diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.h index 3b6d8494f9d5..801965b5f9a4 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.h +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.h @@ -43,13 +43,13 @@ enum bw_type { }; /* - * Enable BW Allocation Mode Support from the DP-Tx side + * Enable USB4 DP BW allocation mode * * @link: pointer to the dc_link struct instance * * return: SUCCESS or FAILURE */ -bool link_dp_dpia_set_dptx_usb4_bw_alloc_support(struct dc_link *link); +bool link_dpia_enable_usb4_dp_bw_alloc_mode(struct dc_link *link); /* * Allocates only what the stream needs for bw, so if: @@ -59,9 +59,8 @@ bool link_dp_dpia_set_dptx_usb4_bw_alloc_support(struct dc_link *link); * @link: pointer to the dc_link struct instance * @req_bw: Bw requested by the stream * - * return: true if allocated successfully */ -bool link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int req_bw); +void link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int req_bw); /* * Handle the USB4 BW Allocation related functionality here: @@ -71,21 +70,8 @@ bool link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int r * @link: pointer to the dc_link struct instance * @peak_bw: Peak bw used by the link/sink * - * return: allocated bw else return 0 */ -int dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int peak_bw); - -/* - * Handle function for when the status of the Request above is complete. - * We will find out the result of allocating on CM and update structs. - * - * @link: pointer to the dc_link struct instance - * @bw: Allocated or Estimated BW depending on the result - * @result: Response type - * - * return: none - */ -void dpia_handle_bw_alloc_response(struct dc_link *link, uint8_t bw, uint8_t result); +void dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int peak_bw); /* * Handle the validation of total BW here and confirm that the bw used by each @@ -108,4 +94,14 @@ bool dpia_validate_usb4_bw(struct dc_link **link, int *bw_needed, const unsigned */ int link_dp_dpia_get_dp_overhead_in_dp_tunneling(struct dc_link *link); +/* + * Handle DP BW allocation status register + * + * @link: pointer to the dc_link struct instance + * @status: content of DP tunneling status register + * + * return: none + */ +void link_dp_dpia_handle_bw_alloc_status(struct dc_link *link, uint8_t status); + #endif /* DC_INC_LINK_DP_DPIA_BW_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c index 96bf135b6f05..693477413347 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c @@ -37,6 +37,7 @@ #include "link/accessories/link_dp_trace.h" #include "link/link_dpms.h" #include "dm_helpers.h" +#include "link_dp_dpia_bw.h" #define DC_LOGGER \ link->ctx->logger @@ -221,20 +222,16 @@ static void handle_hpd_irq_replay_sink(struct dc_link *link) &replay_error_status.raw, sizeof(replay_error_status.raw)); - link->replay_settings.config.replay_error_status.bits.LINK_CRC_ERROR = - replay_error_status.bits.LINK_CRC_ERROR; - link->replay_settings.config.replay_error_status.bits.DESYNC_ERROR = - replay_configuration.bits.DESYNC_ERROR_STATUS; - link->replay_settings.config.replay_error_status.bits.STATE_TRANSITION_ERROR = - replay_configuration.bits.STATE_TRANSITION_ERROR_STATUS; - - if (link->replay_settings.config.replay_error_status.bits.LINK_CRC_ERROR || - link->replay_settings.config.replay_error_status.bits.DESYNC_ERROR || - link->replay_settings.config.replay_error_status.bits.STATE_TRANSITION_ERROR) { + if (replay_error_status.bits.LINK_CRC_ERROR || + replay_configuration.bits.DESYNC_ERROR_STATUS || + replay_configuration.bits.STATE_TRANSITION_ERROR_STATUS) { bool allow_active; - if (link->replay_settings.config.replay_error_status.bits.DESYNC_ERROR) - link->replay_settings.config.received_desync_error_hpd = 1; + link->replay_settings.config.replay_error_status.raw |= replay_error_status.raw; + + /* Increment desync error counter if a desync error is detected */ + if (replay_configuration.bits.DESYNC_ERROR_STATUS) + link->replay_settings.replay_desync_error_fail_count++; if (link->replay_settings.config.force_disable_desync_error_check) return; @@ -291,6 +288,30 @@ void dp_handle_link_loss(struct dc_link *link) } } +static void dp_handle_tunneling_irq(struct dc_link *link) +{ + enum dc_status retval; + uint8_t tunneling_status = 0; + + retval = core_link_read_dpcd( + link, DP_TUNNELING_STATUS, + &tunneling_status, + sizeof(tunneling_status)); + + if (retval == DC_OK) { + DC_LOG_HW_HPD_IRQ("%s: Got DP tunneling status on link %d status=0x%x", + __func__, link->link_index, tunneling_status); + + if (tunneling_status & DP_TUNNELING_BW_ALLOC_BITS_MASK) + link_dp_dpia_handle_bw_alloc_status(link, tunneling_status); + } + + tunneling_status = DP_TUNNELING_IRQ; + core_link_write_dpcd( + link, DP_LINK_SERVICE_IRQ_VECTOR_ESI0, + &tunneling_status, 1); +} + static void read_dpcd204h_on_irq_hpd(struct dc_link *link, union hpd_irq_data *irq_data) { enum dc_status retval; @@ -324,13 +345,19 @@ enum dc_status dp_read_hpd_rx_irq_data( * * For DP 1.4 we need to read those from 2002h range. */ - if (link->dpcd_caps.dpcd_rev.raw < DPCD_REV_14) + if (link->dpcd_caps.dpcd_rev.raw < DPCD_REV_14) { retval = core_link_read_dpcd( link, DP_SINK_COUNT, irq_data->raw, - sizeof(union hpd_irq_data)); - else { + DP_SINK_STATUS - DP_SINK_COUNT + 1); + + if (link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling) { + retval = core_link_read_dpcd( + link, DP_LINK_SERVICE_IRQ_VECTOR_ESI0, + &irq_data->bytes.link_service_irq_esi0.raw, 1); + } + } else { /* Read 14 bytes in a single read and then copy only the required fields. * This is more efficient than doing it in two separate AUX reads. */ @@ -351,6 +378,7 @@ enum dc_status dp_read_hpd_rx_irq_data( irq_data->bytes.lane23_status.raw = tmp[DP_LANE2_3_STATUS_ESI - DP_SINK_COUNT_ESI]; irq_data->bytes.lane_status_updated.raw = tmp[DP_LANE_ALIGN_STATUS_UPDATED_ESI - DP_SINK_COUNT_ESI]; irq_data->bytes.sink_status.raw = tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI]; + irq_data->bytes.link_service_irq_esi0.raw = tmp[DP_LINK_SERVICE_IRQ_VECTOR_ESI0 - DP_SINK_COUNT_ESI]; /* * This display doesn't have correct values in DPCD200Eh. @@ -418,7 +446,8 @@ bool dp_handle_hpd_rx_irq(struct dc_link *link, if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.AUTOMATED_TEST) { // Workaround for DP 1.4a LL Compliance CTS as USB4 has to share encoders unlike DP and USBC - if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) + if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && + !link->dc->config.enable_dpia_pre_training) link->skip_fallback_on_link_loss = true; device_service_clear.bits.AUTOMATED_TEST = 1; @@ -492,6 +521,11 @@ bool dp_handle_hpd_rx_irq(struct dc_link *link, dp_trace_link_loss_increment(link); } + if (link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling) { + if (hpd_irq_dpcd_data.bytes.link_service_irq_esi0.bits.DP_LINK_TUNNELING_IRQ) + dp_handle_tunneling_irq(link); + } + if (link->type == dc_connection_sst_branch && hpd_irq_dpcd_data.bytes.sink_cnt.bits.SINK_COUNT != link->dpcd_sink_count) diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_phy.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_phy.c index bafa52a0165a..49521ac4b0e8 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_phy.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_phy.c @@ -75,7 +75,8 @@ void dp_disable_link_phy(struct dc_link *link, struct dc *dc = link->ctx->dc; if (!link->wa_flags.dp_keep_receiver_powered && - !link->skip_implict_edp_power_control) + !link->skip_implict_edp_power_control && + link->type != dc_connection_none) dpcd_write_rx_power_ctrl(link, false); dc->hwss.disable_link_output(link, link_res, signal); @@ -104,7 +105,7 @@ void dp_set_hw_lane_settings( // Don't return here if using FIXED_VS link HWSS and encoding is 128b/132b if ((link_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) && !is_immediate_downstream(link, offset) && - (!(link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) || + (!((link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK) == AMD_EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) || link_dp_get_encoding_format(&link_settings->link_settings) == DP_8b_10b_ENCODING)) return; @@ -141,11 +142,12 @@ enum dc_status dp_set_fec_ready(struct dc_link *link, const struct link_resource * if the sink supports it and leave it enabled on link. * If FEC is not supported, disable it. */ - struct link_encoder *link_enc = NULL; + struct link_encoder *link_enc = link_res->dio_link_enc; enum dc_status status = DC_OK; uint8_t fec_config = 0; - link_enc = link_enc_cfg_get_link_enc(link); + if (!link->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(link); ASSERT(link_enc); if (link_enc->funcs->fec_set_ready == NULL) return DC_NOT_SUPPORTED; @@ -163,8 +165,9 @@ enum dc_status dp_set_fec_ready(struct dc_link *link, const struct link_resource } else { if (link->fec_state == dc_link_fec_ready) { fec_config = 0; - core_link_write_dpcd(link, DP_FEC_CONFIGURATION, - &fec_config, sizeof(fec_config)); + if (link->type != dc_connection_none) + core_link_write_dpcd(link, DP_FEC_CONFIGURATION, + &fec_config, sizeof(fec_config)); link_enc->funcs->fec_set_ready(link_enc, false); link->fec_state = dc_link_fec_not_ready; @@ -174,13 +177,14 @@ enum dc_status dp_set_fec_ready(struct dc_link *link, const struct link_resource return status; } -void dp_set_fec_enable(struct dc_link *link, bool enable) +void dp_set_fec_enable(struct dc_link *link, const struct link_resource *link_res, bool enable) { - struct link_encoder *link_enc = NULL; + struct link_encoder *link_enc = link_res->dio_link_enc; - link_enc = link_enc_cfg_get_link_enc(link); - ASSERT(link_enc); - if (link_enc->funcs->fec_set_enable == NULL) + if (!link->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(link); + + if (link_enc == NULL || link_enc->funcs == NULL || link_enc->funcs->fec_set_enable == NULL) return; if (enable && dp_should_enable_fec(link)) { diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_phy.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_phy.h index 1eb0619d6710..ab1c1f8f1f8b 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_phy.h +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_phy.h @@ -52,7 +52,8 @@ void dp_set_drive_settings( enum dc_status dp_set_fec_ready(struct dc_link *link, const struct link_resource *link_res, bool ready); -void dp_set_fec_enable(struct dc_link *link, bool enable); +void dp_set_fec_enable(struct dc_link *link, + const struct link_resource *link_res, bool enable); void dpcd_write_rx_power_ctrl(struct dc_link *link, bool on); diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c index 27b881f947e8..2dc1a660e504 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c @@ -272,7 +272,7 @@ void dp_wait_for_training_aux_rd_interval( struct dc_link *link, uint32_t wait_in_micro_secs) { - fsleep(wait_in_micro_secs); + usleep_range_state(wait_in_micro_secs, wait_in_micro_secs, TASK_UNINTERRUPTIBLE); DC_LOG_HW_LINK_TRAINING("%s:\n wait = %d\n", __func__, @@ -736,10 +736,12 @@ void override_training_settings( lt_settings->pre_emphasis = overrides->pre_emphasis; if (overrides->post_cursor2 != NULL) lt_settings->post_cursor2 = overrides->post_cursor2; + if (link->wa_flags.force_dp_ffe_preset && !dp_is_lttpr_present(link)) + lt_settings->ffe_preset = &link->forced_dp_ffe_preset; if (overrides->ffe_preset != NULL) lt_settings->ffe_preset = overrides->ffe_preset; /* Override HW lane settings with BIOS forced values if present */ - if ((link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && + if ((link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && lt_settings->lttpr_mode == LTTPR_MODE_TRANSPARENT) { lt_settings->voltage_swing = &link->bios_forced_drive_settings.VOLTAGE_SWING; lt_settings->pre_emphasis = &link->bios_forced_drive_settings.PRE_EMPHASIS; @@ -783,7 +785,6 @@ void override_training_settings( lt_settings->lttpr_mode = LTTPR_MODE_NON_LTTPR; dp_get_lttpr_mode_override(link, <_settings->lttpr_mode); - } enum dc_dp_training_pattern decide_cr_training_pattern( @@ -799,19 +800,23 @@ enum dc_dp_training_pattern decide_cr_training_pattern( } enum dc_dp_training_pattern decide_eq_training_pattern(struct dc_link *link, + const struct link_resource *link_res, const struct dc_link_settings *link_settings) { - struct link_encoder *link_enc; + struct link_encoder *link_enc = link_res->dio_link_enc; struct encoder_feature_support *enc_caps; struct dpcd_caps *rx_caps = &link->dpcd_caps; enum dc_dp_training_pattern pattern = DP_TRAINING_PATTERN_SEQUENCE_2; - link_enc = link_enc_cfg_get_link_enc(link); - ASSERT(link_enc); - enc_caps = &link_enc->features; - switch (link_dp_get_encoding_format(link_settings)) { case DP_8b_10b_ENCODING: + if (!link->dc->config.unify_link_enc_assignment) + link_enc = link_enc_cfg_get_link_enc(link); + + if (!link_enc) + break; + + enc_caps = &link_enc->features; if (enc_caps->flags.bits.IS_TPS4_CAPABLE && rx_caps->max_down_spread.bits.TPS4_SUPPORTED) pattern = DP_TRAINING_PATTERN_SEQUENCE_4; @@ -884,13 +889,14 @@ void dp_decide_lane_settings( void dp_decide_training_settings( struct dc_link *link, + const struct link_resource *link_res, const struct dc_link_settings *link_settings, struct link_training_settings *lt_settings) { if (link_dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING) - decide_8b_10b_training_settings(link, link_settings, lt_settings); + decide_8b_10b_training_settings(link, link_res, link_settings, lt_settings); else if (link_dp_get_encoding_format(link_settings) == DP_128b_132b_ENCODING) - decide_128b_132b_training_settings(link, link_settings, lt_settings); + decide_128b_132b_training_settings(link, link_res, link_settings, lt_settings); } @@ -1107,9 +1113,13 @@ enum dc_status dpcd_set_link_settings( status = core_link_write_dpcd(link, DP_DOWNSPREAD_CTRL, &downspread.raw, sizeof(downspread)); + if (status != DC_OK) + DC_LOG_ERROR("%s:%d: core_link_write_dpcd (DP_DOWNSPREAD_CTRL) failed\n", __func__, __LINE__); status = core_link_write_dpcd(link, DP_LANE_COUNT_SET, &lane_count_set.raw, 1); + if (status != DC_OK) + DC_LOG_ERROR("%s:%d: core_link_write_dpcd (DP_LANE_COUNT_SET) failed\n", __func__, __LINE__); if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_13 && lt_settings->link_settings.use_link_rate_set == true) { @@ -1125,12 +1135,19 @@ enum dc_status dpcd_set_link_settings( supported_link_rates, sizeof(supported_link_rates)); } status = core_link_write_dpcd(link, DP_LINK_BW_SET, &rate, 1); + if (status != DC_OK) + DC_LOG_ERROR("%s:%d: core_link_write_dpcd (DP_LINK_BW_SET) failed\n", __func__, __LINE__); + status = core_link_write_dpcd(link, DP_LINK_RATE_SET, <_settings->link_settings.link_rate_set, 1); + if (status != DC_OK) + DC_LOG_ERROR("%s:%d: core_link_write_dpcd (DP_LINK_RATE_SET) failed\n", __func__, __LINE__); } else { rate = get_dpcd_link_rate(<_settings->link_settings); status = core_link_write_dpcd(link, DP_LINK_BW_SET, &rate, 1); + if (status != DC_OK) + DC_LOG_ERROR("%s:%d: core_link_write_dpcd (DP_LINK_BW_SET) failed\n", __func__, __LINE__); } if (rate) { @@ -1543,6 +1560,7 @@ enum link_training_result dp_perform_link_training( /* decide training settings */ dp_decide_training_settings( link, + link_res, link_settings, <_settings); @@ -1556,14 +1574,15 @@ enum link_training_result dp_perform_link_training( /* configure link prior to entering training mode */ dpcd_configure_lttpr_mode(link, <_settings); - dp_set_fec_ready(link, link_res, lt_settings.should_set_fec_ready); + if (link_dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING) + dp_set_fec_ready(link, link_res, lt_settings.should_set_fec_ready); dpcd_configure_channel_coding(link, <_settings); /* enter training mode: * Per DP specs starting from here, DPTX device shall not issue * Non-LT AUX transactions inside training mode. */ - if ((link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && encoding == DP_8b_10b_ENCODING) + if (((link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK) == AMD_EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && encoding == DP_8b_10b_ENCODING) status = dp_perform_fixed_vs_pe_training_sequence(link, link_res, <_settings); else if (encoding == DP_8b_10b_ENCODING) status = dp_perform_8b_10b_link_training(link, link_res, <_settings); @@ -1769,13 +1788,10 @@ bool perform_link_training_with_retries( is_link_bw_min = ((cur_link_settings.link_rate <= LINK_RATE_LOW) && (cur_link_settings.lane_count <= LANE_COUNT_ONE)); - if (is_link_bw_low) { + if (is_link_bw_low) DC_LOG_WARNING( "%s: Link(%d) bandwidth too low after fallback req_bw(%d) > link_bw(%d)\n", __func__, link->link_index, req_bw, link_bw); - - return false; - } } msleep(delay_between_attempts); diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.h index 0b18aa35c33c..574b083e0936 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.h +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.h @@ -104,6 +104,7 @@ void start_clock_recovery_pattern_early(struct dc_link *link, void dp_decide_training_settings( struct dc_link *link, + const struct link_resource *link_res, const struct dc_link_settings *link_settings, struct link_training_settings *lt_settings); @@ -117,6 +118,7 @@ enum dc_dp_training_pattern decide_cr_training_pattern( const struct dc_link_settings *link_settings); enum dc_dp_training_pattern decide_eq_training_pattern(struct dc_link *link, + const struct link_resource *link_res, const struct dc_link_settings *link_settings); enum lttpr_mode dp_decide_lttpr_mode(struct dc_link *link, diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_128b_132b.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_128b_132b.c index db87cfe37b5c..11565f187ac7 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_128b_132b.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_128b_132b.c @@ -204,6 +204,7 @@ enum link_training_result dp_perform_128b_132b_link_training( struct link_training_settings legacy_settings; decide_8b_10b_training_settings(link, + link_res, <_settings->link_settings, &legacy_settings); return dp_perform_8b_10b_link_training(link, link_res, &legacy_settings); @@ -227,6 +228,7 @@ enum link_training_result dp_perform_128b_132b_link_training( } void decide_128b_132b_training_settings(struct dc_link *link, + const struct link_resource *link_res, const struct dc_link_settings *link_settings, struct link_training_settings *lt_settings) { @@ -238,7 +240,7 @@ void decide_128b_132b_training_settings(struct dc_link *link, LINK_SPREAD_05_DOWNSPREAD_30KHZ; lt_settings->pattern_for_cr = decide_cr_training_pattern(link_settings); - lt_settings->pattern_for_eq = decide_eq_training_pattern(link, link_settings); + lt_settings->pattern_for_eq = decide_eq_training_pattern(link, link_res, link_settings); lt_settings->eq_pattern_time = 2500; lt_settings->eq_wait_time_limit = 400000; lt_settings->eq_loop_count_limit = 20; diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_128b_132b.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_128b_132b.h index 2147f24efc8b..901a42edafa1 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_128b_132b.h +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_128b_132b.h @@ -34,6 +34,7 @@ enum link_training_result dp_perform_128b_132b_link_training( struct link_training_settings *lt_settings); void decide_128b_132b_training_settings(struct dc_link *link, + const struct link_resource *link_res, const struct dc_link_settings *link_settings, struct link_training_settings *lt_settings); diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.c index 3bdce32a85e3..66d0fb1b9b9d 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.c @@ -35,22 +35,41 @@ #define DC_LOGGER \ link->ctx->logger +static void get_default_8b_10b_lttpr_aux_rd_interval( + union training_aux_rd_interval *training_rd_interval) +{ + /* LTTPR are required to program DPCD 0000Eh to 0x4 (16ms) upon AUX + * read reply to this register. Since old sinks with DPCD rev 1.1 + * and earlier may not support this register, assume the mandatory + * value is programmed by the LTTPR to avoid AUX timeout issues. + */ + training_rd_interval->raw = 0x4; +} + static int32_t get_cr_training_aux_rd_interval(struct dc_link *link, - const struct dc_link_settings *link_settings) + const struct dc_link_settings *link_settings, + enum lttpr_mode lttpr_mode) { union training_aux_rd_interval training_rd_interval; uint32_t wait_in_micro_secs = 100; memset(&training_rd_interval, 0, sizeof(training_rd_interval)); - if (link_dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING && - link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12) { - core_link_read_dpcd( - link, - DP_TRAINING_AUX_RD_INTERVAL, - (uint8_t *)&training_rd_interval, - sizeof(training_rd_interval)); - if (training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL) - wait_in_micro_secs = training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL * 4000; + if (link_dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING) { + if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12) + core_link_read_dpcd( + link, + DP_TRAINING_AUX_RD_INTERVAL, + (uint8_t *)&training_rd_interval, + sizeof(training_rd_interval)); + else if (dp_is_lttpr_present(link)) + get_default_8b_10b_lttpr_aux_rd_interval(&training_rd_interval); + + if (training_rd_interval.raw != 0) { + if (lttpr_mode != LTTPR_MODE_NON_TRANSPARENT) + wait_in_micro_secs = 400; + if (training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL) + wait_in_micro_secs = training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL * 4000; + } } return wait_in_micro_secs; } @@ -68,13 +87,15 @@ static uint32_t get_eq_training_aux_rd_interval( DP_128B132B_TRAINING_AUX_RD_INTERVAL, (uint8_t *)&training_rd_interval, sizeof(training_rd_interval)); - } else if (link_dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING && - link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12) { - core_link_read_dpcd( - link, - DP_TRAINING_AUX_RD_INTERVAL, - (uint8_t *)&training_rd_interval, - sizeof(training_rd_interval)); + } else if (link_dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING) { + if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12) + core_link_read_dpcd( + link, + DP_TRAINING_AUX_RD_INTERVAL, + (uint8_t *)&training_rd_interval, + sizeof(training_rd_interval)); + else if (dp_is_lttpr_present(link)) + get_default_8b_10b_lttpr_aux_rd_interval(&training_rd_interval); } switch (training_rd_interval.bits.TRAINIG_AUX_RD_INTERVAL) { @@ -90,7 +111,8 @@ static uint32_t get_eq_training_aux_rd_interval( } void decide_8b_10b_training_settings( - struct dc_link *link, + struct dc_link *link, + const struct link_resource *link_res, const struct dc_link_settings *link_setting, struct link_training_settings *lt_settings) { @@ -110,16 +132,24 @@ void decide_8b_10b_training_settings( */ lt_settings->link_settings.link_spread = link->dp_ss_off ? LINK_SPREAD_DISABLED : LINK_SPREAD_05_DOWNSPREAD_30KHZ; - lt_settings->cr_pattern_time = get_cr_training_aux_rd_interval(link, link_setting); lt_settings->eq_pattern_time = get_eq_training_aux_rd_interval(link, link_setting); lt_settings->pattern_for_cr = decide_cr_training_pattern(link_setting); - lt_settings->pattern_for_eq = decide_eq_training_pattern(link, link_setting); + lt_settings->pattern_for_eq = decide_eq_training_pattern(link, link_res, link_setting); lt_settings->enhanced_framing = 1; lt_settings->should_set_fec_ready = true; lt_settings->disallow_per_lane_settings = true; lt_settings->always_match_dpcd_with_hw_lane_settings = true; lt_settings->lttpr_mode = dp_decide_8b_10b_lttpr_mode(link); + lt_settings->cr_pattern_time = get_cr_training_aux_rd_interval(link, link_setting, lt_settings->lttpr_mode); dp_hw_to_dpcd_lane_settings(lt_settings, lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings); + + /* Some embedded LTTPRs rely on receiving TPS2 before LT to interop reliably with sensitive VGA dongles + * This allows these LTTPRs to minimize freq/phase and skew variation during lock and deskew sequences + */ + if ((link->chip_caps & AMD_EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK) == + AMD_EXT_DISPLAY_PATH_CAPS__DP_EARLY_8B10B_TPS2) { + lt_settings->lttpr_early_tps2 = true; + } } enum lttpr_mode dp_decide_8b_10b_lttpr_mode(struct dc_link *link) @@ -151,6 +181,42 @@ enum lttpr_mode dp_decide_8b_10b_lttpr_mode(struct dc_link *link) return LTTPR_MODE_NON_LTTPR; } +static void set_link_settings_and_perform_early_tps2_retimer_pre_lt_sequence(struct dc_link *link, + const struct link_resource *link_res, + struct link_training_settings *lt_settings, + uint32_t lttpr_count) +{ + /* Vendor-specific LTTPR early TPS2 sequence: + * 1. Output TPS2 + * 2. Wait 400us + * 3. Set link settings as usual + * 4. Write TPS1 to DP_TRAINING_PATTERN_SET_PHY_REPEATERx targeting LTTPR closest to host + * 5. Wait 1ms + * 6. Begin link training as usual + * */ + + uint32_t closest_lttpr_address_offset = dp_get_closest_lttpr_offset(lttpr_count); + + union dpcd_training_pattern dpcd_pattern = {0}; + + dpcd_pattern.v1_4.TRAINING_PATTERN_SET = 1; + dpcd_pattern.v1_4.SCRAMBLING_DISABLE = 1; + + DC_LOG_HW_LINK_TRAINING("%s\n GPU sends TPS2. Wait 400us.\n", __func__); + + dp_set_hw_training_pattern(link, link_res, DP_TRAINING_PATTERN_SEQUENCE_2, DPRX); + + dp_set_hw_lane_settings(link, link_res, lt_settings, DPRX); + + udelay(400); + + dpcd_set_link_settings(link, lt_settings); + + core_link_write_dpcd(link, DP_TRAINING_PATTERN_SET_PHY_REPEATER1 + closest_lttpr_address_offset, &dpcd_pattern.raw, 1); + + udelay(1000); + } + enum link_training_result perform_8b_10b_clock_recovery_sequence( struct dc_link *link, const struct link_resource *link_res, @@ -361,7 +427,7 @@ enum link_training_result dp_perform_8b_10b_link_training( { enum link_training_result status = LINK_TRAINING_SUCCESS; - uint8_t repeater_cnt; + uint8_t repeater_cnt = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt); uint8_t repeater_id; uint8_t lane = 0; @@ -369,14 +435,16 @@ enum link_training_result dp_perform_8b_10b_link_training( start_clock_recovery_pattern_early(link, link_res, lt_settings, DPRX); /* 1. set link rate, lane count and spread. */ - dpcd_set_link_settings(link, lt_settings); + if (lt_settings->lttpr_early_tps2) + set_link_settings_and_perform_early_tps2_retimer_pre_lt_sequence(link, link_res, lt_settings, repeater_cnt); + else + dpcd_set_link_settings(link, lt_settings); if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) { /* 2. perform link training (set link training done * to false is done as well) */ - repeater_cnt = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt); for (repeater_id = repeater_cnt; (repeater_id > 0 && status == LINK_TRAINING_SUCCESS); repeater_id--) { diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.h index d26de15ce954..ea0de701d83f 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.h +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.h @@ -54,7 +54,8 @@ enum link_training_result perform_8b_10b_channel_equalization_sequence( enum lttpr_mode dp_decide_8b_10b_lttpr_mode(struct dc_link *link); void decide_8b_10b_training_settings( - struct dc_link *link, + struct dc_link *link, + const struct link_resource *link_res, const struct dc_link_settings *link_setting, struct link_training_settings *lt_settings); diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_auxless.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_auxless.c index 4c6b886a9da8..f99d26290bc0 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_auxless.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_auxless.c @@ -39,6 +39,7 @@ bool dp_perform_link_training_skip_aux( dp_decide_training_settings( link, + link_res, link_setting, <_settings); override_training_settings( diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_dpia.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_dpia.c index 39e4b7dc9588..603537ffd128 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_dpia.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_dpia.c @@ -110,6 +110,7 @@ static enum link_training_result dpia_configure_link( dp_decide_training_settings( link, + link_res, link_setting, lt_settings); @@ -129,11 +130,14 @@ static enum link_training_result dpia_configure_link( if (status != DC_OK && link->is_hpd_pending) return LINK_TRAINING_ABORT; - if (link->preferred_training_settings.fec_enable != NULL) - fec_enable = *link->preferred_training_settings.fec_enable; - else - fec_enable = true; - status = dp_set_fec_ready(link, link_res, fec_enable); + if (link_dp_get_encoding_format(link_setting) == DP_8b_10b_ENCODING) { + if (link->preferred_training_settings.fec_enable != NULL) + fec_enable = *link->preferred_training_settings.fec_enable; + else + fec_enable = true; + status = dp_set_fec_ready(link, link_res, fec_enable); + } + if (status != DC_OK && link->is_hpd_pending) return LINK_TRAINING_ABORT; diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_fixed_vs_pe_retimer.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_fixed_vs_pe_retimer.c index b5cf75975fff..ce174ce5579c 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_fixed_vs_pe_retimer.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_fixed_vs_pe_retimer.c @@ -270,7 +270,8 @@ enum link_training_result dp_perform_fixed_vs_pe_training_sequence( rate = get_dpcd_link_rate(<_settings->link_settings); - if (!link->dpcd_caps.lttpr_caps.main_link_channel_coding.bits.DP_128b_132b_SUPPORTED) { + // Only perform toggle if FIXED_VS LTTPR reports no IEEE OUI + if (memcmp("\x0,\x0,\x0", &link->dpcd_caps.lttpr_caps.lttpr_ieee_oui[0], 3) == 0) { /* Vendor specific: Toggle link rate */ toggle_rate = (rate == 0x6) ? 0xA : 0x6; @@ -412,7 +413,6 @@ enum link_training_result dp_perform_fixed_vs_pe_training_sequence( /* 5. check CR done*/ if (dp_is_cr_done(lane_count, dpcd_lane_status)) { - status = LINK_TRAINING_SUCCESS; break; } diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c index 3aa05a2be6c0..da74c2b5854f 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c @@ -157,31 +157,13 @@ bool edp_set_backlight_level_nits(struct dc_link *link, uint32_t backlight_millinits, uint32_t transition_time_in_ms) { - struct dpcd_source_backlight_set dpcd_backlight_set; - uint8_t backlight_control = isHDR ? 1 : 0; - if (!link || (link->connector_signal != SIGNAL_TYPE_EDP && link->connector_signal != SIGNAL_TYPE_DISPLAY_PORT)) return false; - // OLEDs have no PWM, they can only use AUX - if (link->dpcd_sink_ext_caps.bits.oled == 1) - backlight_control = 1; - - *(uint32_t *)&dpcd_backlight_set.backlight_level_millinits = backlight_millinits; - *(uint16_t *)&dpcd_backlight_set.backlight_transition_time_ms = (uint16_t)transition_time_in_ms; - - - if (!link->dpcd_caps.panel_luminance_control) { - if (core_link_write_dpcd(link, DP_SOURCE_BACKLIGHT_LEVEL, - (uint8_t *)(&dpcd_backlight_set), - sizeof(dpcd_backlight_set)) != DC_OK) - return false; - - if (core_link_write_dpcd(link, DP_SOURCE_BACKLIGHT_CONTROL, - &backlight_control, 1) != DC_OK) - return false; - } else { + // use internal backlight control if dmub capabilities are not present + if (link->backlight_control_type == BACKLIGHT_CONTROL_VESA_AUX && + !link->dc->caps.dmub_caps.aux_backlight_support) { uint8_t backlight_enable = 0; struct target_luminance_value *target_luminance = NULL; @@ -205,6 +187,24 @@ bool edp_set_backlight_level_nits(struct dc_link *link, (uint8_t *)(target_luminance), sizeof(struct target_luminance_value)) != DC_OK) return false; + } else if (link->backlight_control_type == BACKLIGHT_CONTROL_AMD_AUX) { + struct dpcd_source_backlight_set dpcd_backlight_set; + *(uint32_t *)&dpcd_backlight_set.backlight_level_millinits = backlight_millinits; + *(uint16_t *)&dpcd_backlight_set.backlight_transition_time_ms = (uint16_t)transition_time_in_ms; + + uint8_t backlight_control = isHDR ? 1 : 0; + // OLEDs have no PWM, they can only use AUX + if (link->dpcd_sink_ext_caps.bits.oled == 1) + backlight_control = 1; + + if (core_link_write_dpcd(link, DP_SOURCE_BACKLIGHT_LEVEL, + (uint8_t *)(&dpcd_backlight_set), + sizeof(dpcd_backlight_set)) != DC_OK) + return false; + + if (core_link_write_dpcd(link, DP_SOURCE_BACKLIGHT_CONTROL, + &backlight_control, 1) != DC_OK) + return false; } return true; @@ -519,12 +519,12 @@ static struct pipe_ctx *get_pipe_from_link(const struct dc_link *link) } bool edp_set_backlight_level(const struct dc_link *link, - uint32_t backlight_pwm_u16_16, - uint32_t frame_ramp) + struct set_backlight_level_params *backlight_level_params) { struct dc *dc = link->ctx->dc; + uint32_t backlight_pwm_u16_16 = backlight_level_params->backlight_pwm_u16_16; + uint32_t frame_ramp = backlight_level_params->frame_ramp; - DC_LOGGER_INIT(link->ctx->logger); DC_LOG_BACKLIGHT("New Backlight level: %d (0x%X)\n", backlight_pwm_u16_16, backlight_pwm_u16_16); @@ -544,10 +544,11 @@ bool edp_set_backlight_level(const struct dc_link *link, return false; } + backlight_level_params->frame_ramp = frame_ramp; + dc->hwss.set_backlight_level( pipe_ctx, - backlight_pwm_u16_16, - frame_ramp); + backlight_level_params); } return true; } @@ -674,6 +675,18 @@ bool edp_setup_psr(struct dc_link *link, if (!link) return false; + //Clear PSR cfg + memset(&psr_configuration, 0, sizeof(psr_configuration)); + dm_helpers_dp_write_dpcd( + link->ctx, + link, + DP_PSR_EN_CFG, + &psr_configuration.raw, + sizeof(psr_configuration.raw)); + + if (link->psr_settings.psr_version == DC_PSR_VERSION_UNSUPPORTED) + return false; + dc = link->ctx->dc; dmcu = dc->res_pool->dmcu; psr = dc->res_pool->psr; @@ -684,9 +697,6 @@ bool edp_setup_psr(struct dc_link *link, if (!dc_get_edp_link_panel_inst(dc, link, &panel_inst)) return false; - - memset(&psr_configuration, 0, sizeof(psr_configuration)); - psr_configuration.bits.ENABLE = 1; psr_configuration.bits.CRC_VERIFICATION = 1; psr_configuration.bits.FRAME_CAPTURE_INDICATION = @@ -940,8 +950,7 @@ bool edp_setup_replay(struct dc_link *link, const struct dc_stream_state *stream struct replay_context replay_context = { 0 }; unsigned int lineTimeInNs = 0; - - union replay_enable_and_configuration replay_config; + union replay_enable_and_configuration replay_config = { 0 }; union dpcd_alpm_configuration alpm_config; @@ -950,6 +959,16 @@ bool edp_setup_replay(struct dc_link *link, const struct dc_stream_state *stream if (!link) return false; + //Clear Replay config + dm_helpers_dp_write_dpcd(link->ctx, link, + DP_SINK_PR_ENABLE_AND_CONFIGURATION, + (uint8_t *)&(replay_config.raw), sizeof(uint8_t)); + + if (!(link->replay_settings.config.replay_supported)) + return false; + + link->replay_settings.config.replay_error_status.raw = 0; + dc = link->ctx->dc; replay = dc->res_pool->replay; @@ -1003,6 +1022,9 @@ bool edp_setup_replay(struct dc_link *link, const struct dc_stream_state *stream &alpm_config.raw, sizeof(alpm_config.raw)); } + + link->replay_settings.config.replay_video_conferencing_optimization_enabled = false; + return true; } @@ -1111,11 +1133,11 @@ static struct abm *get_abm_from_stream_res(const struct dc_link *link) struct abm *abm = NULL; for (i = 0; i < MAX_PIPES; i++) { - struct pipe_ctx pipe_ctx = dc->current_state->res_ctx.pipe_ctx[i]; - struct dc_stream_state *stream = pipe_ctx.stream; + struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; + struct dc_stream_state *stream = pipe_ctx->stream; if (stream && stream->link == link) { - abm = pipe_ctx.stream_res.abm; + abm = pipe_ctx->stream_res.abm; break; } } @@ -1168,9 +1190,6 @@ static void edp_set_assr_enable(const struct dc *pDC, struct dc_link *link, link_enc_index = link->link_enc->transmitter - TRANSMITTER_UNIPHY_A; if (link_res->hpo_dp_link_enc) { - if (link->wa_flags.disable_assr_for_uhbr) - return; - link_enc_index = link_res->hpo_dp_link_enc->inst; use_hpo_dp_link_enc = true; } diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.h index 30dc8c24c008..bcfa6ac5d4e7 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.h +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.h @@ -36,8 +36,7 @@ bool edp_get_backlight_level_nits(struct dc_link *link, uint32_t *backlight_millinits_avg, uint32_t *backlight_millinits_peak); bool edp_set_backlight_level(const struct dc_link *link, - uint32_t backlight_pwm_u16_16, - uint32_t frame_ramp); + struct set_backlight_level_params *backlight_level_params); bool edp_set_backlight_level_nits(struct dc_link *link, bool isHDR, uint32_t backlight_millinits, |