]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
drm/i915/dp: Fix panel replay when DSC is enabled
authorImre Deak <imre.deak@intel.com>
Wed, 15 Oct 2025 16:19:34 +0000 (19:19 +0300)
committerImre Deak <imre.deak@intel.com>
Fri, 17 Oct 2025 18:48:57 +0000 (21:48 +0300)
Prevent enabling panel replay if the sink doesn't support this due to
DSC being enabled.

Panel replay has two modes, updating full frames or only selected
regions of the frame. If the sink doesn't support Panel Replay in full
frame update mode with DSC prevent Panel Replay completely if DSC is
enabled. If the sink doesn't support Panel Replay only in the selective
update mode while DSC is enabled, it will still support Panel Replay in
the full frame update mode, so only prevent selective updates in this
case.

v2:
- Use Panel Replay instead of PR in debug prints. (Jouni)
- Rebase on change tracking the link DSC state in the crtc state.

Cc: Jouni Högander <jouni.hogander@intel.com>
Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/14869
Reviewed-by: Jouni Högander <jouni.hogander@intel.com>
Signed-off-by: Imre Deak <imre.deak@intel.com>
Link: https://lore.kernel.org/r/20251015161934.262108-8-imre.deak@intel.com
drivers/gpu/drm/i915/display/intel_display_types.h
drivers/gpu/drm/i915/display/intel_dp.c
drivers/gpu/drm/i915/display/intel_psr.c

index 20747fa4d3daf5672ca481f69fe3dd4b698a9452..1e0cd81c9474ec7c9d9a166499209a301a861d27 100644 (file)
@@ -955,6 +955,12 @@ struct intel_csc_matrix {
        u16 postoff[3];
 };
 
+enum intel_panel_replay_dsc_support {
+       INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED,
+       INTEL_DP_PANEL_REPLAY_DSC_FULL_FRAME_ONLY,
+       INTEL_DP_PANEL_REPLAY_DSC_SELECTIVE_UPDATE,
+};
+
 struct intel_crtc_state {
        /*
         * uapi (drm) state. This is the software state shown to userspace.
@@ -1133,6 +1139,8 @@ struct intel_crtc_state {
        bool has_panel_replay;
        bool wm_level_disabled;
        bool pkg_c_latency_used;
+       /* Only used for state verification. */
+       enum intel_panel_replay_dsc_support panel_replay_dsc_support;
        u32 dc3co_exitline;
        u16 su_y_granularity;
        u8 active_non_psr_pipes;
@@ -1706,6 +1714,7 @@ struct intel_psr {
        bool source_panel_replay_support;
        bool sink_panel_replay_support;
        bool sink_panel_replay_su_support;
+       enum intel_panel_replay_dsc_support sink_panel_replay_dsc_support;
        bool panel_replay_enabled;
        u32 dc3co_exitline;
        u32 dc3co_exit_delay;
index 95884af242b3f9cd79e5b9a361d37c021d77f64e..215ad690ab07711dac8d85205c5add914b8e46e7 100644 (file)
@@ -6053,6 +6053,8 @@ intel_dp_detect(struct drm_connector *_connector,
                memset(connector->dp.dsc_dpcd, 0, sizeof(connector->dp.dsc_dpcd));
                intel_dp->psr.sink_panel_replay_support = false;
                intel_dp->psr.sink_panel_replay_su_support = false;
+               intel_dp->psr.sink_panel_replay_dsc_support =
+                       INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED;
 
                intel_dp_mst_disconnect(intel_dp);
 
index 703e5f6af04c7480680d8fd8be9161535f6ac652..3e99a65ec98863991171c904b8bebfe7f35767b9 100644 (file)
@@ -29,6 +29,7 @@
 #include <drm/drm_vblank.h>
 
 #include "i915_reg.h"
+#include "i915_utils.h"
 #include "intel_alpm.h"
 #include "intel_atomic.h"
 #include "intel_crtc.h"
@@ -50,6 +51,7 @@
 #include "intel_snps_phy.h"
 #include "intel_step.h"
 #include "intel_vblank.h"
+#include "intel_vdsc.h"
 #include "intel_vrr.h"
 #include "skl_universal_plane.h"
 
@@ -580,6 +582,44 @@ exit:
        intel_dp->psr.su_y_granularity = y;
 }
 
+static enum intel_panel_replay_dsc_support
+compute_pr_dsc_support(struct intel_dp *intel_dp)
+{
+       u8 pr_dsc_mode;
+       u8 val;
+
+       val = intel_dp->pr_dpcd[INTEL_PR_DPCD_INDEX(DP_PANEL_REPLAY_CAP_CAPABILITY)];
+       pr_dsc_mode = REG_FIELD_GET8(DP_PANEL_REPLAY_DSC_DECODE_CAPABILITY_IN_PR_MASK, val);
+
+       switch (pr_dsc_mode) {
+       case DP_DSC_DECODE_CAPABILITY_IN_PR_FULL_FRAME_ONLY:
+               return INTEL_DP_PANEL_REPLAY_DSC_FULL_FRAME_ONLY;
+       case DP_DSC_DECODE_CAPABILITY_IN_PR_SUPPORTED:
+               return INTEL_DP_PANEL_REPLAY_DSC_SELECTIVE_UPDATE;
+       default:
+               MISSING_CASE(pr_dsc_mode);
+               fallthrough;
+       case DP_DSC_DECODE_CAPABILITY_IN_PR_NOT_SUPPORTED:
+       case DP_DSC_DECODE_CAPABILITY_IN_PR_RESERVED:
+               return INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED;
+       }
+}
+
+static const char *panel_replay_dsc_support_str(enum intel_panel_replay_dsc_support dsc_support)
+{
+       switch (dsc_support) {
+       case INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED:
+               return "not supported";
+       case INTEL_DP_PANEL_REPLAY_DSC_FULL_FRAME_ONLY:
+               return "full frame only";
+       case INTEL_DP_PANEL_REPLAY_DSC_SELECTIVE_UPDATE:
+               return "selective update";
+       default:
+               MISSING_CASE(dsc_support);
+               return "n/a";
+       };
+}
+
 static void _panel_replay_init_dpcd(struct intel_dp *intel_dp)
 {
        struct intel_display *display = to_intel_display(intel_dp);
@@ -615,10 +655,13 @@ static void _panel_replay_init_dpcd(struct intel_dp *intel_dp)
            DP_PANEL_REPLAY_SU_SUPPORT)
                intel_dp->psr.sink_panel_replay_su_support = true;
 
+       intel_dp->psr.sink_panel_replay_dsc_support = compute_pr_dsc_support(intel_dp);
+
        drm_dbg_kms(display->drm,
-                   "Panel replay %sis supported by panel\n",
+                   "Panel replay %sis supported by panel (in DSC mode: %s)\n",
                    intel_dp->psr.sink_panel_replay_su_support ?
-                   "selective_update " : "");
+                   "selective_update " : "",
+                   panel_replay_dsc_support_str(intel_dp->psr.sink_panel_replay_dsc_support));
 }
 
 static void _psr_init_dpcd(struct intel_dp *intel_dp)
@@ -1606,9 +1649,21 @@ static bool intel_sel_update_config_valid(struct intel_dp *intel_dp,
                goto unsupported;
        }
 
-       if (crtc_state->has_panel_replay && (DISPLAY_VER(display) < 14 ||
-                                            !intel_dp->psr.sink_panel_replay_su_support))
-               goto unsupported;
+       if (crtc_state->has_panel_replay) {
+               if (DISPLAY_VER(display) < 14)
+                       goto unsupported;
+
+               if (!intel_dp->psr.sink_panel_replay_su_support)
+                       goto unsupported;
+
+               if (intel_dsc_enabled_on_link(crtc_state) &&
+                   intel_dp->psr.sink_panel_replay_dsc_support !=
+                   INTEL_DP_PANEL_REPLAY_DSC_SELECTIVE_UPDATE) {
+                       drm_dbg_kms(display->drm,
+                                   "Selective update with Panel Replay not enabled because it's not supported with DSC\n");
+                       goto unsupported;
+               }
+       }
 
        if (crtc_state->crc_enabled) {
                drm_dbg_kms(display->drm,
@@ -1685,6 +1740,14 @@ _panel_replay_compute_config(struct intel_dp *intel_dp,
                return false;
        }
 
+       if (intel_dsc_enabled_on_link(crtc_state) &&
+           intel_dp->psr.sink_panel_replay_dsc_support ==
+           INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED) {
+               drm_dbg_kms(display->drm,
+                           "Panel Replay not enabled because it's not supported with DSC\n");
+               return false;
+       }
+
        if (!intel_dp_is_edp(intel_dp))
                return true;
 
@@ -1790,6 +1853,8 @@ void intel_psr_compute_config(struct intel_dp *intel_dp,
                return;
        }
 
+       /* Only used for state verification. */
+       crtc_state->panel_replay_dsc_support = intel_dp->psr.sink_panel_replay_dsc_support;
        crtc_state->has_panel_replay = _panel_replay_compute_config(intel_dp,
                                                                    crtc_state,
                                                                    conn_state);
@@ -2991,6 +3056,20 @@ void intel_psr_pre_plane_update(struct intel_atomic_state *state,
        }
 }
 
+static void
+verify_panel_replay_dsc_state(const struct intel_crtc_state *crtc_state)
+{
+       struct intel_display *display = to_intel_display(crtc_state);
+
+       if (!crtc_state->has_panel_replay)
+               return;
+
+       drm_WARN_ON(display->drm,
+                   intel_dsc_enabled_on_link(crtc_state) &&
+                   crtc_state->panel_replay_dsc_support ==
+                   INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED);
+}
+
 void intel_psr_post_plane_update(struct intel_atomic_state *state,
                                 struct intel_crtc *crtc)
 {
@@ -3002,6 +3081,8 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state,
        if (!crtc_state->has_psr)
                return;
 
+       verify_panel_replay_dsc_state(crtc_state);
+
        for_each_intel_encoder_mask_with_psr(state->base.dev, encoder,
                                             crtc_state->uapi.encoder_mask) {
                struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
@@ -4031,6 +4112,8 @@ static void intel_psr_sink_capability(struct intel_dp *intel_dp,
        seq_printf(m, ", Panel Replay = %s", str_yes_no(psr->sink_panel_replay_support));
        seq_printf(m, ", Panel Replay Selective Update = %s",
                   str_yes_no(psr->sink_panel_replay_su_support));
+       seq_printf(m, ", Panel Replay DSC support = %s",
+                  panel_replay_dsc_support_str(psr->sink_panel_replay_dsc_support));
        if (intel_dp->pr_dpcd[INTEL_PR_DPCD_INDEX(DP_PANEL_REPLAY_CAP_SUPPORT)] &
            DP_PANEL_REPLAY_EARLY_TRANSPORT_SUPPORT)
                seq_printf(m, " (Early Transport)");