From: Aric Cyr <[email protected]>

[Why]
dc_stream_adjust_vmin_vmax() skips hardware programming when
stream->adjust already matches the request and timing_adjust_pending is
clear. That treats stream->adjust as a faithful shadow of the OTG, but
dc_set_power_state(D0) calls init_hw(),
which returns OTG_V_TOTAL_MIN/MAX and the static screen triggers to
their reset defaults without touching any stream. After an S3/S4 resume
or a GPU reset the shadow claims hardware is current when it is not.

A resume that reuses the same stream object also short-circuits
dc_commit_streams() on !streams_changed(), so enable_stream_timing()
never runs either and nothing reprograms DRR. Replay then fails to arm:
the static screen trigger is at its reset value and the coasting vtotal
no longer matches the OTG.

The max_v_total overflow path has a second problem. When OTG v_count
halt is not allowed it stores the new values and marks the adjustment
pending even though nothing was programmed, and no later call with the
same values can clear that flag.

[How]
Add invalidate_drr_adjust() to mark every stream in current_state as
having a pending timing adjustment, and call it once hardware has been
re-initialized in dc_set_power_state(),
so the next dc_stream_adjust_vmin_vmax() reprograms the OTG.

Hoist the max_v_total check above the point where stream->adjust is
updated and return early when v_count halt is not allowed, leaving the
shadow describing the OTG instead of latching a flag nothing can clear.
On the paths that do update stream->adjust, set the flag up front and
clear it only after set_long_vtotal() or set_drr() has actually run.

Reviewed-by: Josip Pavic <[email protected]>
Signed-off-by: Aric Cyr <[email protected]>
Signed-off-by: Ivan Lipski <[email protected]>
---
 drivers/gpu/drm/amd/display/dc/core/dc.c | 43 +++++++++++++++++++-----
 1 file changed, 34 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c 
b/drivers/gpu/drm/amd/display/dc/core/dc.c
index ef876a24afc2..05e8fcc710e3 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -446,6 +446,21 @@ static bool set_long_vtotal(struct dc *dc, struct 
dc_stream_state *stream, struc
        return false;
 }
 
+/* A hardware re-init resets the OTG DRR registers while stream->adjust keeps 
the
+ * pre-reset values, so those values represent has to be marked stale.
+ */
+static void invalidate_drr_adjust(struct dc *dc)
+{
+       int i;
+
+       if (!dc->current_state)
+               return;
+
+       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_stream_get_last_used_drr_vtotal - Looks up the pipe context of
  * dc_stream_state and gets the last VTOTAL used by DRR (Dynamic Refresh Rate)
@@ -1343,6 +1358,9 @@ bool dc_stream_adjust_vmin_vmax(struct dc *dc,
 {
        int i;
        bool drr_unchanged;
+       bool exceeds_max_v_total = dc->caps.max_v_total != 0 &&
+                       (adjust->v_total_max > dc->caps.max_v_total ||
+                        adjust->v_total_min > dc->caps.max_v_total);
 
        /*
         * Don't adjust DRR while there's bandwidth optimizations pending to
@@ -1370,6 +1388,12 @@ bool dc_stream_adjust_vmin_vmax(struct dc *dc,
        if (drr_unchanged)
                return true;
 
+       /* leave stream->adjust describing the OTG rather than latching
+        * a pending adjustment that no later call can ever clear.
+        */
+       if (exceeds_max_v_total && !adjust->allow_otg_v_count_halt)
+               return false;
+
        dc_exit_ips_for_hw_access(dc);
 
        stream->adjust.v_total_max = adjust->v_total_max;
@@ -1377,16 +1401,15 @@ bool dc_stream_adjust_vmin_vmax(struct dc *dc,
        stream->adjust.v_total_mid_frame_num = adjust->v_total_mid_frame_num;
        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;
 
-       if (dc->caps.max_v_total != 0 &&
-               (adjust->v_total_max > dc->caps.max_v_total || 
adjust->v_total_min > dc->caps.max_v_total)) {
-               if (adjust->allow_otg_v_count_halt) {
-                       stream->adjust.timing_adjust_pending = false;
-                       return set_long_vtotal(dc, stream, adjust);
-               }
-               /* Nothing reached hw, so stream->adjust no longer describes 
the OTG. */
-               stream->adjust.timing_adjust_pending = true;
-               return false;
+       if (exceeds_max_v_total) {
+               if (!set_long_vtotal(dc, stream, adjust))
+                       return false;
+
+               stream->adjust.timing_adjust_pending = false;
+               return true;
        }
 
        for (i = 0; i < MAX_PIPES; i++) {
@@ -6310,6 +6333,8 @@ void dc_set_power_state(struct dc *dc, enum 
dc_acpi_cm_power_state power_state)
 
                dc->hwss.init_hw(dc);
 
+               invalidate_drr_adjust(dc);
+
                if (dc->hwss.init_sys_ctx != NULL &&
                        dc->vm_pa_config.valid) {
                        dc->hwss.init_sys_ctx(dc->hwseq, dc, &dc->vm_pa_config);
-- 
2.43.0

Reply via email to