[PATCH 03/12] drm/amd/display: Only raise FP2 outside of lock

Qingqing Zhuo qingqing.zhuo at amd.com
Fri Feb 3 15:24:43 UTC 2023


From: Wesley Chalmers <Wesley.Chalmers at amd.com>

[WHY]
If HUBP FP2 (VTG register) is higher than OTG FP2 (implicit), then
underflow can occur.

Since VTG is not double-buffered, there is risk of underflow when
raising FP2 if VBLANK is hit before programming and unlocking OTG is
completed.

It appears that if HUBP FP2 is lower than OTG FP2, no such problems
occur.

[HOW]
If FP2 is being lowered, update the VTG register in lock as normal.

If FP2 is being raised, wait until after OTG unlock, so that OTG FP2 is
raised first, then update VTG.

Reviewed-by: Jun Lei <Jun.Lei at amd.com>
Acked-by: Qingqing Zhuo <qingqing.zhuo at amd.com>
Signed-off-by: Wesley Chalmers <Wesley.Chalmers at amd.com>
---
 .../gpu/drm/amd/display/dc/dcn10/dcn10_optc.c |  9 +++++++
 .../gpu/drm/amd/display/dc/dcn10/dcn10_optc.h |  2 ++
 .../drm/amd/display/dc/dcn20/dcn20_hwseq.c    | 27 +++++++++++++++++--
 .../gpu/drm/amd/display/dc/dcn20/dcn20_optc.c |  1 +
 .../drm/amd/display/dc/dcn201/dcn201_optc.c   |  1 +
 .../gpu/drm/amd/display/dc/dcn30/dcn30_optc.c |  1 +
 .../gpu/drm/amd/display/dc/dcn31/dcn31_optc.c |  1 +
 .../drm/amd/display/dc/dcn314/dcn314_optc.c   |  1 +
 .../gpu/drm/amd/display/dc/dcn32/dcn32_optc.c |  1 +
 .../amd/display/dc/inc/hw/timing_generator.h  |  1 +
 10 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_optc.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_optc.c
index c9e53dc49c92..20466195b884 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_optc.c
@@ -369,6 +369,14 @@ void optc1_set_vtg_params(struct timing_generator *optc,
 		REG_UPDATE(CONTROL, VTG0_VCOUNT_INIT, v_init);
 }
 
+
+uint32_t optc1_get_vstartup_start(struct timing_generator *optc)
+{
+	struct optc *optc1 = DCN10TG_FROM_TG(optc);
+
+	return optc1->vstartup_start;
+}
+
 void optc1_set_blank_data_double_buffer(struct timing_generator *optc, bool enable)
 {
 	struct optc *optc1 = DCN10TG_FROM_TG(optc);
@@ -1575,6 +1583,7 @@ static const struct timing_generator_funcs dcn10_tg_funcs = {
 		.get_crc = optc1_get_crc,
 		.configure_crc = optc1_configure_crc,
 		.set_vtg_params = optc1_set_vtg_params,
+		.get_vstartup_start = optc1_get_vstartup_start,
 		.program_manual_trigger = optc1_program_manual_trigger,
 		.setup_manual_trigger = optc1_setup_manual_trigger,
 		.get_hw_timing = optc1_get_hw_timing,
diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_optc.h b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_optc.h
index 0b37bb0e184b..88d6e423ccf9 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_optc.h
+++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_optc.h
@@ -721,4 +721,6 @@ bool optc1_is_two_pixels_per_containter(const struct dc_crtc_timing *timing);
 void optc1_set_vtg_params(struct timing_generator *optc,
 		const struct dc_crtc_timing *dc_crtc_timing, bool program_fp2);
 
+uint32_t optc1_get_vstartup_start(struct timing_generator *optc);
+
 #endif /* __DC_TIMING_GENERATOR_DCN10_H__ */
diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c
index cb8edb14603a..c657f34df0d3 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c
@@ -1695,8 +1695,9 @@ static void dcn20_program_pipe(
 		struct dc_state *context)
 {
 	struct dce_hwseq *hws = dc->hwseq;
-	/* Only need to unblank on top pipe */
+	uint32_t current_vstartup_start, old_vstartup_start;
 
+	/* Only need to unblank on top pipe */
 	if ((pipe_ctx->update_flags.bits.enable || pipe_ctx->stream->update_flags.bits.abm_level)
 			&& !pipe_ctx->top_pipe && !pipe_ctx->prev_odm_pipe)
 		hws->funcs.blank_pixel_data(dc, pipe_ctx, !pipe_ctx->plane_state->visible);
@@ -1704,6 +1705,8 @@ static void dcn20_program_pipe(
 	/* Only update TG on top pipe */
 	if (pipe_ctx->update_flags.bits.global_sync && !pipe_ctx->top_pipe
 			&& !pipe_ctx->prev_odm_pipe) {
+		old_vstartup_start = pipe_ctx->stream_res.tg->funcs->get_vstartup_start(pipe_ctx->stream_res.tg);
+		current_vstartup_start = pipe_ctx->pipe_dlg_param.vstartup_start;
 		pipe_ctx->stream_res.tg->funcs->program_global_sync(
 				pipe_ctx->stream_res.tg,
 				calculate_vready_offset_for_group(pipe_ctx),
@@ -1716,8 +1719,12 @@ static void dcn20_program_pipe(
 			pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
 		}
 
-		pipe_ctx->stream_res.tg->funcs->set_vtg_params(
+		if (current_vstartup_start < old_vstartup_start)
+			pipe_ctx->stream_res.tg->funcs->set_vtg_params(
 				pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing, true);
+		else
+			pipe_ctx->stream_res.tg->funcs->set_vtg_params(
+				pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing, false);
 
 		if (hws->funcs.setup_vupdate_interrupt)
 			hws->funcs.setup_vupdate_interrupt(dc, pipe_ctx);
@@ -1955,6 +1962,22 @@ void dcn20_post_unlock_program_front_end(
 		}
 	}
 
+	for (i = 0; i < dc->res_pool->pipe_count; i++) {
+		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
+
+		if (!pipe->stream)
+			continue;
+
+		if (!pipe->top_pipe && !pipe->prev_odm_pipe) {
+			if (pipe->stream->mall_stream_config.type != SUBVP_PHANTOM) {
+				pipe->stream_res.tg->funcs->wait_for_state(pipe->stream_res.tg, CRTC_STATE_VBLANK);
+				pipe->stream_res.tg->funcs->wait_for_state(pipe->stream_res.tg, CRTC_STATE_VACTIVE);
+			}
+			pipe->stream_res.tg->funcs->set_vtg_params(pipe->stream_res.tg, &pipe->stream->timing, true);
+		}
+	}
+
+
 	for (i = 0; i < dc->res_pool->pipe_count; i++) {
 		struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
 
diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_optc.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_optc.c
index a08c335b7383..af7c4f32143d 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_optc.c
@@ -550,6 +550,7 @@ static struct timing_generator_funcs dcn20_tg_funcs = {
 		.set_gsl = optc2_set_gsl,
 		.set_gsl_source_select = optc2_set_gsl_source_select,
 		.set_vtg_params = optc1_set_vtg_params,
+		.get_vstartup_start = optc1_get_vstartup_start,
 		.program_manual_trigger = optc2_program_manual_trigger,
 		.setup_manual_trigger = optc2_setup_manual_trigger,
 		.get_hw_timing = optc1_get_hw_timing,
diff --git a/drivers/gpu/drm/amd/display/dc/dcn201/dcn201_optc.c b/drivers/gpu/drm/amd/display/dc/dcn201/dcn201_optc.c
index 730875dfd8b4..203a2d7f06b6 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn201/dcn201_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/dcn201/dcn201_optc.c
@@ -183,6 +183,7 @@ static struct timing_generator_funcs dcn201_tg_funcs = {
 		.set_dwb_source = NULL,
 		.get_optc_source = optc201_get_optc_source,
 		.set_vtg_params = optc1_set_vtg_params,
+		.get_vstartup_start = optc1_get_vstartup_start,
 		.program_manual_trigger = optc2_program_manual_trigger,
 		.setup_manual_trigger = optc2_setup_manual_trigger,
 		.get_hw_timing = optc1_get_hw_timing,
diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_optc.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_optc.c
index 08b92715e2e6..dd25450c8625 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_optc.c
@@ -365,6 +365,7 @@ static struct timing_generator_funcs dcn30_tg_funcs = {
 		.set_gsl = optc2_set_gsl,
 		.set_gsl_source_select = optc2_set_gsl_source_select,
 		.set_vtg_params = optc1_set_vtg_params,
+		.get_vstartup_start = optc1_get_vstartup_start,
 		.program_manual_trigger = optc2_program_manual_trigger,
 		.setup_manual_trigger = optc2_setup_manual_trigger,
 		.get_hw_timing = optc1_get_hw_timing,
diff --git a/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_optc.c b/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_optc.c
index 63a677c8ee27..8e474f14d4aa 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_optc.c
@@ -288,6 +288,7 @@ static struct timing_generator_funcs dcn31_tg_funcs = {
 		.set_gsl = optc2_set_gsl,
 		.set_gsl_source_select = optc2_set_gsl_source_select,
 		.set_vtg_params = optc1_set_vtg_params,
+		.get_vstartup_start = optc1_get_vstartup_start,
 		.program_manual_trigger = optc2_program_manual_trigger,
 		.setup_manual_trigger = optc2_setup_manual_trigger,
 		.get_hw_timing = optc1_get_hw_timing,
diff --git a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_optc.c b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_optc.c
index 0086cafb0f7a..f978e77748d9 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_optc.c
@@ -248,6 +248,7 @@ static struct timing_generator_funcs dcn314_tg_funcs = {
 		.set_gsl = optc2_set_gsl,
 		.set_gsl_source_select = optc2_set_gsl_source_select,
 		.set_vtg_params = optc1_set_vtg_params,
+		.get_vstartup_start = optc1_get_vstartup_start,
 		.program_manual_trigger = optc2_program_manual_trigger,
 		.setup_manual_trigger = optc2_setup_manual_trigger,
 		.get_hw_timing = optc1_get_hw_timing,
diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_optc.c b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_optc.c
index 2ee798965bc2..596675dd6eda 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_optc.c
@@ -315,6 +315,7 @@ static struct timing_generator_funcs dcn32_tg_funcs = {
 		.set_gsl = optc2_set_gsl,
 		.set_gsl_source_select = optc2_set_gsl_source_select,
 		.set_vtg_params = optc1_set_vtg_params,
+		.get_vstartup_start = optc1_get_vstartup_start,
 		.program_manual_trigger = optc2_program_manual_trigger,
 		.setup_manual_trigger = optc2_setup_manual_trigger,
 		.get_hw_timing = optc1_get_hw_timing,
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h b/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h
index 1d9f9c53d2bd..5520b0a01879 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h
@@ -319,6 +319,7 @@ struct timing_generator_funcs {
 			uint32_t window_start, uint32_t window_end);
 	void (*set_vtotal_change_limit)(struct timing_generator *optc,
 			uint32_t limit);
+	uint32_t (*get_vstartup_start)(struct timing_generator *tg);
 	void (*align_vblanks)(struct timing_generator *master_optc,
 			struct timing_generator *slave_optc,
 			uint32_t master_pixel_clock_100Hz,
-- 
2.25.1



More information about the amd-gfx mailing list