drm/i915: Disable DC states for all commits
authorjsg <jsg@openbsd.org>
Fri, 7 Apr 2023 04:07:59 +0000 (04:07 +0000)
committerjsg <jsg@openbsd.org>
Fri, 7 Apr 2023 04:07:59 +0000 (04:07 +0000)
From Ville Syrjala
0fc6fea41c7122aa5f2088117f50144b507e13d7 in linux-6.1.y/6.1.23
a2b6e99d8a623544f3bdccd28ee35b9c1b00daa5 in mainline linux

sys/dev/pci/drm/i915/display/intel_display.c

index 8901781..5bb5abf 100644 (file)
@@ -7124,6 +7124,8 @@ static void intel_update_crtc(struct intel_atomic_state *state,
 
        intel_fbc_update(state, crtc);
 
+       drm_WARN_ON(&i915->drm, !intel_display_power_is_enabled(i915, POWER_DOMAIN_DC_OFF));
+
        if (!modeset &&
            (new_crtc_state->uapi.color_mgmt_changed ||
             new_crtc_state->update_pipe))
@@ -7517,8 +7519,28 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
        drm_atomic_helper_wait_for_dependencies(&state->base);
        drm_dp_mst_atomic_wait_for_dependencies(&state->base);
 
-       if (state->modeset)
-               wakeref = intel_display_power_get(dev_priv, POWER_DOMAIN_MODESET);
+       /*
+        * During full modesets we write a lot of registers, wait
+        * for PLLs, etc. Doing that while DC states are enabled
+        * is not a good idea.
+        *
+        * During fastsets and other updates we also need to
+        * disable DC states due to the following scenario:
+        * 1. DC5 exit and PSR exit happen
+        * 2. Some or all _noarm() registers are written
+        * 3. Due to some long delay PSR is re-entered
+        * 4. DC5 entry -> DMC saves the already written new
+        *    _noarm() registers and the old not yet written
+        *    _arm() registers
+        * 5. DC5 exit -> DMC restores a mixture of old and
+        *    new register values and arms the update
+        * 6. PSR exit -> hardware latches a mixture of old and
+        *    new register values -> corrupted frame, or worse
+        * 7. New _arm() registers are finally written
+        * 8. Hardware finally latches a complete set of new
+        *    register values, and subsequent frames will be OK again
+        */
+       wakeref = intel_display_power_get(dev_priv, POWER_DOMAIN_DC_OFF);
 
        intel_atomic_prepare_plane_clear_colors(state);
 
@@ -7657,8 +7679,8 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
                 * the culprit.
                 */
                intel_uncore_arm_unclaimed_mmio_detection(&dev_priv->uncore);
-               intel_display_power_put(dev_priv, POWER_DOMAIN_MODESET, wakeref);
        }
+       intel_display_power_put(dev_priv, POWER_DOMAIN_DC_OFF, wakeref);
        intel_runtime_pm_put(&dev_priv->runtime_pm, state->wakeref);
 
        /*