From: Aric Cyr <[email protected]> [Why] dc_stream_adjust_vmin_vmax() short circuits when the requested v_totals match stream->adjust and nothing is pending. A display that is not driving DRR requests a constant v_total, so that short circuit is permanent once hardware stops matching - and init_hw() resets the OTG DRR registers and the static screen trigger mask. The commit that follows need not restore them either: apply_ctx_to_hw() skips enable_stream_timing() when pipe_need_reprogram() considers the pipe unchanged, and dc_commit_streams() can skip the commit entirely on !streams_changed(). Panel replay never sees a static screen trigger again.
timing_adjust_pending should cover this, but it lives inside struct dc_crtc_timing_adjust, which is copied and compared by value. hwss.set_drr() takes it by value, and copy_stream_update_to_stream() wrote the flag back into the caller's struct. [How] Move timing_adjust_pending into struct dc_stream_state so a dc_crtc_timing_adjust is pure request data, safe to copy and compare. Add program_pending_drr_adjust() to settle any stream still carrying a pending adjustment at the end of a commit, so a commit always leaves stream->adjust describing the OTG. It is a no-op when nothing is pending. Reviewed-by: Josip Pavic <[email protected]> Signed-off-by: Aric Cyr <[email protected]> Signed-off-by: Ray Wu <[email protected]> --- .../display/amdgpu_dm/amdgpu_dm_freesync.c | 2 +- .../amdgpu_dm/tests/amdgpu_dm_freesync_test.c | 4 +- drivers/gpu/drm/amd/display/dc/core/dc.c | 51 +++++++++++++++---- .../drm/amd/display/dc/core/dc_hw_sequencer.c | 2 +- drivers/gpu/drm/amd/display/dc/dc_hw_types.h | 1 - drivers/gpu/drm/amd/display/dc/dc_stream.h | 3 ++ 6 files changed, 47 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_freesync.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_freesync.c index b65af2cc1fb7..49c6048d0cc9 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_freesync.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_freesync.c @@ -42,7 +42,7 @@ bool amdgpu_dm_is_dc_timing_adjust_needed(struct dm_crtc_state *old_state, struct dm_crtc_state *new_state) { - if (new_state->stream->adjust.timing_adjust_pending) + if (new_state->stream->timing_adjust_pending) return true; if (new_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED) return true; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_freesync_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_freesync_test.c index 547a543b9f5d..85670e1cbb05 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_freesync_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_freesync_test.c @@ -173,7 +173,7 @@ static void dm_test_dc_timing_adjust_pending(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); new_state->stream = stream; - stream->adjust.timing_adjust_pending = 1; + stream->timing_adjust_pending = 1; KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_dc_timing_adjust_needed(old_state, new_state)); } @@ -592,7 +592,7 @@ static void dm_test_update_freesync_state_pre_ai_active(struct kunit *test) amdgpu_dm_update_freesync_state_on_stream(&adev->dm, &crtc_state, stream, surface, 0); - KUNIT_EXPECT_FALSE(test, stream->adjust.timing_adjust_pending); + KUNIT_EXPECT_FALSE(test, stream->timing_adjust_pending); } static void dm_test_update_freesync_state_pcon_version(struct kunit *test, u8 version) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 8cf6edbd39c9..f84f659c101b 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -458,7 +458,34 @@ static void invalidate_drr_adjust(struct dc *dc) for (i = 0; i < dc->current_state->stream_count; i++) if (dc->current_state->streams[i]) - dc->current_state->streams[i]->adjust.timing_adjust_pending = true; + dc->current_state->streams[i]->timing_adjust_pending = true; +} + +/* apply_ctx_to_hw() skips controller programming for a pipe it considers unchanged, so a + * commit on its own does not guarantee the OTG matches stream->adjust. Settle anything + * still outstanding here - dc_stream_adjust_vmin_vmax() cannot, because it short circuits + * when the requested v_totals are unchanged, which is permanent for a stream that is not + * driving DRR. + */ +static void program_pending_drr_adjust(struct dc *dc, struct dc_state *context) +{ + int i; + + if (!dc->hwss.set_drr) + return; + + for (i = 0; i < MAX_PIPES; i++) { + struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; + + if (!resource_is_pipe_type(pipe, OTG_MASTER) || !pipe->stream_res.tg) + continue; + + if (!pipe->stream->timing_adjust_pending) + continue; + + dc_exit_ips_for_hw_access(dc); + dc->hwss.set_drr(&pipe, 1, pipe->stream->adjust); + } } /** @@ -1370,7 +1397,7 @@ bool dc_stream_adjust_vmin_vmax(struct dc *dc, if (dc->optimized_required && (stream->adjust.v_total_max != adjust->v_total_max || stream->adjust.v_total_min != adjust->v_total_min)) { - stream->adjust.timing_adjust_pending = true; + stream->timing_adjust_pending = true; return false; } } @@ -1378,7 +1405,7 @@ bool dc_stream_adjust_vmin_vmax(struct dc *dc, /* timing_adjust_pending tracks whether stream->adjust still describes the * OTG, so a cleared flag plus matching values means hw is already current. */ - drr_unchanged = !stream->adjust.timing_adjust_pending && + drr_unchanged = !stream->timing_adjust_pending && stream->adjust.v_total_min == adjust->v_total_min && stream->adjust.v_total_max == adjust->v_total_max && stream->adjust.v_total_mid == adjust->v_total_mid && @@ -1402,13 +1429,13 @@ bool dc_stream_adjust_vmin_vmax(struct dc *dc, stream->adjust.v_total_min = adjust->v_total_min; stream->adjust.allow_otg_v_count_halt = adjust->allow_otg_v_count_halt; /* Only cleared once the new values have actually reached an OTG. */ - stream->adjust.timing_adjust_pending = true; + stream->timing_adjust_pending = true; if (exceeds_max_v_total) { if (!set_long_vtotal(dc, stream, adjust)) return false; - stream->adjust.timing_adjust_pending = false; + stream->timing_adjust_pending = false; return true; } @@ -1419,7 +1446,7 @@ bool dc_stream_adjust_vmin_vmax(struct dc *dc, dc->hwss.set_drr(&pipe, 1, *adjust); - stream->adjust.timing_adjust_pending = false; + stream->timing_adjust_pending = false; if (dc->debug.visual_confirm == VISUAL_CONFIRM_BOOSTED_REFRESH_RATE) { if (pipe->stream && pipe->plane_state) { @@ -2422,6 +2449,8 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c return result; } + program_pending_drr_adjust(dc, context); + dc_trigger_sync(dc, context); /* Full update should unconditionally be triggered when dc_commit_state_no_check is called */ @@ -2578,8 +2607,10 @@ enum dc_status dc_commit_streams(struct dc *dc, struct dc_commit_streams_params return res; if (!streams_changed(dc, params->streams, params->stream_count) && - dc->current_state->power_source == params->power_source) + dc->current_state->power_source == params->power_source) { + program_pending_drr_adjust(dc, dc->current_state); return res; + } dc_exit_ips_for_hw_access(dc); @@ -3604,11 +3635,9 @@ static void copy_stream_update_to_stream(struct dc *dc, if (update->crtc_timing_adjust) { if (stream->adjust.v_total_min != update->crtc_timing_adjust->v_total_min || - stream->adjust.v_total_max != update->crtc_timing_adjust->v_total_max || - stream->adjust.timing_adjust_pending) - update->crtc_timing_adjust->timing_adjust_pending = true; + stream->adjust.v_total_max != update->crtc_timing_adjust->v_total_max) + stream->timing_adjust_pending = true; stream->adjust = *update->crtc_timing_adjust; - update->crtc_timing_adjust->timing_adjust_pending = false; } if (update->dpms_off) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c index 86957b4d182a..415ad6ddb740 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c @@ -756,7 +756,7 @@ void set_drr_and_clear_adjust_pending( if (stream) /* A null params resets DRR in hw, leaving stream->adjust stale. */ - stream->adjust.timing_adjust_pending = (params == NULL); + stream->timing_adjust_pending = (params == NULL); } void get_fams2_visual_confirm_color( diff --git a/drivers/gpu/drm/amd/display/dc/dc_hw_types.h b/drivers/gpu/drm/amd/display/dc/dc_hw_types.h index baf6091bb52d..eb9effb22658 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_hw_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_hw_types.h @@ -1083,7 +1083,6 @@ struct dc_crtc_timing_adjust { uint32_t v_total_mid; uint32_t v_total_mid_frame_num; uint32_t allow_otg_v_count_halt; - uint8_t timing_adjust_pending; }; struct dtbclk_dto_params { diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h index fec5e7a84bb9..7999a7d84a77 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_stream.h +++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h @@ -336,6 +336,9 @@ struct dc_stream_state { enum dc_blending_linearity blending_linearity; struct dc_update_scratch_space *update_scratch; bool firmware_controlled_hdr_info_packet; + + /* timing adjust has not been propagated to hardware yet. */ + bool timing_adjust_pending; }; #define ABM_LEVEL_IMMEDIATE_DISABLE 255 -- 2.43.0
