drm/amd/display: Fix Link Override Sequencing When Switching Between DIO/HPO

[WHY]
When performing certain link maintenance compliance tests or forcing link
settings, changing between 128b/132b and 8b/10b rates no longer works on
some ASICs. Some rate divider updates only occur when we set
timings or validate state, which is not performed currently when toggling
DPMS to change rates.

[HOW]
Re-calculate dividers and reprogram audio when switching between DIO
and HPO through DP compliance/escape code path.
Add OTG disable/re-enable so we don't touch the clock while OTG is active.
Acquire dcLock before forcing link settings to avoid thread synchronization
errors due to added programming in escape code path and potential HPD
interrupts.

Reviewed-by: George Shen <george.shen@amd.com>
Signed-off-by: Michael Strauss <michael.strauss@amd.com>
Signed-off-by: Mike Katsnelson <mike.katsnelson@amd.com>
Signed-off-by: Ray Wu <ray.wu@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
pull/1309/head
Michael Strauss 2025-02-12 13:52:42 -05:00 committed by Alex Deucher
parent 51496c7737
commit 9c6669c2e2
12 changed files with 126 additions and 14 deletions

View File

@ -1381,7 +1381,7 @@ static void populate_audio_dp_link_info(
}
}
static void build_audio_output(
void build_audio_output(
struct dc_state *state,
const struct pipe_ctx *pipe_ctx,
struct audio_output *audio_output)

View File

@ -110,5 +110,9 @@ void dce110_enable_dp_link_output(
enum signal_type signal,
enum clock_source_id clock_source,
const struct dc_link_settings *link_settings);
void build_audio_output(
struct dc_state *state,
const struct pipe_ctx *pipe_ctx,
struct audio_output *audio_output);
#endif /* __DC_HWSS_DCE110_H__ */

View File

@ -223,6 +223,11 @@ struct resource_funcs {
const struct dc_stream_state *stream);
bool (*program_mcache_pipe_config)(struct dc_state *context,
const struct dc_mcache_params *mcache_params);
enum dc_status (*update_dc_state_for_encoder_switch)(struct dc_link *link,
struct dc_link_settings *link_setting,
uint8_t pipe_count,
struct pipe_ctx *pipes,
struct audio_output *audio_output);
};
struct audio_support{

View File

@ -34,6 +34,7 @@
#include "dm_helpers.h"
#include "dc_dmub_srv.h"
#include "dce/dmub_hw_lock_mgr.h"
#include "clk_mgr.h"
#define DC_LOGGER \
link->ctx->logger
@ -67,10 +68,17 @@ static void dp_retrain_link_dp_test(struct dc_link *link,
{
struct pipe_ctx *pipes[MAX_PIPES];
struct dc_state *state = link->dc->current_state;
struct dc_stream_update stream_update = { 0 };
bool dpms_off = false;
bool needs_divider_update = false;
bool was_hpo_acquired = resource_is_hpo_acquired(link->dc->current_state);
bool is_hpo_acquired;
uint8_t count;
int i;
struct audio_output audio_output[MAX_PIPES];
needs_divider_update = (link->dc->link_srv->dp_get_encoding_format(link_setting) !=
link->dc->link_srv->dp_get_encoding_format((const struct dc_link_settings *) &link->cur_link_settings));
udelay(100);
@ -83,16 +91,59 @@ static void dp_retrain_link_dp_test(struct dc_link *link,
link->dc,
state,
pipes[i]);
// Disable OTG and re-enable after updating clocks
pipes[i]->stream_res.tg->funcs->disable_crtc(pipes[i]->stream_res.tg);
}
if (link->dc->hwss.setup_hpo_hw_control) {
is_hpo_acquired = resource_is_hpo_acquired(state);
if (was_hpo_acquired != is_hpo_acquired)
link->dc->hwss.setup_hpo_hw_control(link->dc->hwseq, is_hpo_acquired);
if (needs_divider_update && link->dc->res_pool->funcs->update_dc_state_for_encoder_switch) {
link->dc->res_pool->funcs->update_dc_state_for_encoder_switch(link,
link_setting, count,
*pipes, &audio_output[0]);
for (i = 0; i < count; i++) {
pipes[i]->clock_source->funcs->program_pix_clk(
pipes[i]->clock_source,
&pipes[i]->stream_res.pix_clk_params,
link->dc->link_srv->dp_get_encoding_format(&pipes[i]->link_config.dp_link_settings),
&pipes[i]->pll_settings);
if (pipes[i]->stream_res.audio != NULL) {
const struct link_hwss *link_hwss = get_link_hwss(
link, &pipes[i]->link_res);
link_hwss->setup_audio_output(pipes[i], &audio_output[i],
pipes[i]->stream_res.audio->inst);
pipes[i]->stream_res.audio->funcs->az_configure(
pipes[i]->stream_res.audio,
pipes[i]->stream->signal,
&audio_output[i].crtc_info,
&pipes[i]->stream->audio_info,
&audio_output[i].dp_link_info);
if (link->dc->config.disable_hbr_audio_dp2 &&
pipes[i]->stream_res.audio->funcs->az_disable_hbr_audio &&
link->dc->link_srv->dp_is_128b_132b_signal(pipes[i]))
pipes[i]->stream_res.audio->funcs->az_disable_hbr_audio(pipes[i]->stream_res.audio);
}
}
}
for (i = count-1; i >= 0; i--)
link_set_dpms_on(state, pipes[i]);
// Toggle on HPO I/O if necessary
is_hpo_acquired = resource_is_hpo_acquired(state);
if (was_hpo_acquired != is_hpo_acquired && link->dc->hwss.setup_hpo_hw_control)
link->dc->hwss.setup_hpo_hw_control(link->dc->hwseq, is_hpo_acquired);
for (i = 0; i < count; i++)
pipes[i]->stream_res.tg->funcs->enable_crtc(pipes[i]->stream_res.tg);
// Set DPMS on with stream update
for (i = 0; i < state->stream_count; i++)
if (state->streams[i] && state->streams[i]->link && state->streams[i]->link == link) {
stream_update.stream = state->streams[i];
stream_update.dpms_off = &dpms_off;
dc_update_planes_and_stream(state->clk_mgr->ctx->dc, NULL, 0, state->streams[i], &stream_update);
}
}
static void dp_test_send_link_training(struct dc_link *link)

View File

@ -1850,7 +1850,9 @@ static struct resource_funcs dcn31_res_pool_funcs = {
.patch_unknown_plane_state = dcn20_patch_unknown_plane_state,
.get_panel_config_defaults = dcn31_get_panel_config_defaults,
.get_det_buffer_size = dcn31_get_det_buffer_size,
.get_vstartup_for_pipe = dcn10_get_vstartup_for_pipe
.get_vstartup_for_pipe = dcn10_get_vstartup_for_pipe,
.update_dc_state_for_encoder_switch = dcn31_update_dc_state_for_encoder_switch,
.build_pipe_pix_clk_params = dcn20_build_pipe_pix_clk_params
};
static struct clock_source *dcn30_clock_source_create(
@ -2231,3 +2233,35 @@ struct resource_pool *dcn31_create_resource_pool(
kfree(pool);
return NULL;
}
enum dc_status dcn31_update_dc_state_for_encoder_switch(struct dc_link *link,
struct dc_link_settings *link_setting,
uint8_t pipe_count,
struct pipe_ctx *pipes,
struct audio_output *audio_output)
{
struct dc_state *state = link->dc->current_state;
int i;
#if defined(CONFIG_DRM_AMD_DC_FP)
for (i = 0; i < state->stream_count; i++)
if (state->streams[i] && state->streams[i]->link && state->streams[i]->link == link)
link->dc->hwss.calculate_pix_rate_divider((struct dc *)link->dc, state, state->streams[i]);
for (i = 0; i < pipe_count; i++) {
link->dc->res_pool->funcs->build_pipe_pix_clk_params(&pipes[i]);
// Setup audio
if (pipes[i].stream_res.audio != NULL)
build_audio_output(state, &pipes[i], &audio_output[i]);
}
#else
/* This DCN requires rate divider updates and audio reprogramming to allow DP1<-->DP2 link rate switching,
* but the above will not compile on architectures without an FPU.
*/
DC_LOG_WARNING("%s: DP1<-->DP2 link retraining will not work on this DCN on non-FPU platforms", __func__);
ASSERT(0);
#endif
return DC_OK;
}

View File

@ -66,6 +66,12 @@ struct resource_pool *dcn31_create_resource_pool(
unsigned int dcn31_get_det_buffer_size(
const struct dc_state *context);
enum dc_status dcn31_update_dc_state_for_encoder_switch(struct dc_link *link,
struct dc_link_settings *link_setting,
uint8_t pipe_count,
struct pipe_ctx *pipes,
struct audio_output *audio_output);
/*temp: B0 specific before switch to dcn313 headers*/
#ifndef regPHYPLLF_PIXCLK_RESYNC_CNTL
#define regPHYPLLF_PIXCLK_RESYNC_CNTL 0x007e

View File

@ -1779,7 +1779,9 @@ static struct resource_funcs dcn314_res_pool_funcs = {
.get_panel_config_defaults = dcn314_get_panel_config_defaults,
.get_preferred_eng_id_dpia = dcn314_get_preferred_eng_id_dpia,
.get_det_buffer_size = dcn31_get_det_buffer_size,
.get_vstartup_for_pipe = dcn10_get_vstartup_for_pipe
.get_vstartup_for_pipe = dcn10_get_vstartup_for_pipe,
.update_dc_state_for_encoder_switch = dcn31_update_dc_state_for_encoder_switch,
.build_pipe_pix_clk_params = dcn20_build_pipe_pix_clk_params
};
static struct clock_source *dcn30_clock_source_create(

View File

@ -1844,7 +1844,9 @@ static struct resource_funcs dcn315_res_pool_funcs = {
.get_panel_config_defaults = dcn315_get_panel_config_defaults,
.get_power_profile = dcn315_get_power_profile,
.get_det_buffer_size = dcn31_get_det_buffer_size,
.get_vstartup_for_pipe = dcn10_get_vstartup_for_pipe
.get_vstartup_for_pipe = dcn10_get_vstartup_for_pipe,
.update_dc_state_for_encoder_switch = dcn31_update_dc_state_for_encoder_switch,
.build_pipe_pix_clk_params = dcn20_build_pipe_pix_clk_params
};
static bool dcn315_resource_construct(

View File

@ -1720,7 +1720,9 @@ static struct resource_funcs dcn316_res_pool_funcs = {
.patch_unknown_plane_state = dcn20_patch_unknown_plane_state,
.get_panel_config_defaults = dcn316_get_panel_config_defaults,
.get_det_buffer_size = dcn31_get_det_buffer_size,
.get_vstartup_for_pipe = dcn10_get_vstartup_for_pipe
.get_vstartup_for_pipe = dcn10_get_vstartup_for_pipe,
.update_dc_state_for_encoder_switch = dcn31_update_dc_state_for_encoder_switch,
.build_pipe_pix_clk_params = dcn20_build_pipe_pix_clk_params
};
static bool dcn316_resource_construct(

View File

@ -1786,7 +1786,9 @@ static struct resource_funcs dcn35_res_pool_funcs = {
.get_panel_config_defaults = dcn35_get_panel_config_defaults,
.get_preferred_eng_id_dpia = dcn35_get_preferred_eng_id_dpia,
.get_det_buffer_size = dcn31_get_det_buffer_size,
.get_vstartup_for_pipe = dcn10_get_vstartup_for_pipe
.get_vstartup_for_pipe = dcn10_get_vstartup_for_pipe,
.update_dc_state_for_encoder_switch = dcn31_update_dc_state_for_encoder_switch,
.build_pipe_pix_clk_params = dcn20_build_pipe_pix_clk_params
};
static bool dcn35_resource_construct(

View File

@ -1758,7 +1758,9 @@ static struct resource_funcs dcn351_res_pool_funcs = {
.get_panel_config_defaults = dcn35_get_panel_config_defaults,
.get_preferred_eng_id_dpia = dcn351_get_preferred_eng_id_dpia,
.get_det_buffer_size = dcn31_get_det_buffer_size,
.get_vstartup_for_pipe = dcn10_get_vstartup_for_pipe
.get_vstartup_for_pipe = dcn10_get_vstartup_for_pipe,
.update_dc_state_for_encoder_switch = dcn31_update_dc_state_for_encoder_switch,
.build_pipe_pix_clk_params = dcn20_build_pipe_pix_clk_params
};
static bool dcn351_resource_construct(

View File

@ -1759,7 +1759,9 @@ static struct resource_funcs dcn36_res_pool_funcs = {
.patch_unknown_plane_state = dcn20_patch_unknown_plane_state,
.get_panel_config_defaults = dcn35_get_panel_config_defaults,
.get_preferred_eng_id_dpia = dcn36_get_preferred_eng_id_dpia,
.get_vstartup_for_pipe = dcn10_get_vstartup_for_pipe
.get_vstartup_for_pipe = dcn10_get_vstartup_for_pipe,
.update_dc_state_for_encoder_switch = dcn31_update_dc_state_for_encoder_switch,
.build_pipe_pix_clk_params = dcn20_build_pipe_pix_clk_params
};
static bool dcn36_resource_construct(