[PATCH 08/19] drm/i915/display: Add DC3CO eligibility computation

Dibin Moolakadan Subrahmanian
2026-03-26T17:15:35Z

Compute DC3CO eligibility during atomic_check based on
pipe/port constraints and runtime triggers, and propagate
the result via intel_atomic_state.

When DC3CO is allowed, request DC_STATE_EN_UPTO_DC3CO and
reduce the DC entry delay. Otherwise, retain the existing
delay and set default DC_STATE_EN_UPTO_DC6 .

BSpec: 75253
Signed-off-by: Dibin Moolakadan Subrahmanian <dibin.moolakadan.subrahmanian@intel.com>
---
drivers/gpu/drm/i915/display/intel_display.c | 98 ++++++++++++++++++-
drivers/gpu/drm/i915/display/intel_display.h | 2 +-
.../drm/i915/display/intel_display_types.h | 7 ++
3 files changed, 101 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index f20d5ebe06ed..df0eaf6ae76b 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -5943,6 +5943,81 @@ static bool intel_pipes_need_modeset(struct intel_atomic_state *state,
return false;
}

+bool intel_dc3co_allowed(struct intel_atomic_state *state)
+{
+ return state && state->dc3co.allowed;
+}
+
+static bool intel_dc3co_port_pipe_compatible(struct intel_dp *intel_dp,
+ const struct intel_crtc_state *crtc_state)
+{
+ struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+ enum pipe pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe;
+ enum port port = dig_port->base.port;
+ int num_pipes = intel_crtc_num_joined_pipes(crtc_state);
+
+ return num_pipes == 1 && pipe <= PIPE_B && port <= PORT_B;
+}
+
+static void intel_dc3co_compute_state(struct intel_atomic_state *state)
+{
+ struct intel_display *display = to_intel_display(state);
+ struct intel_crtc *crtc;
+ struct intel_crtc_state *crtc_state;
+ struct intel_encoder *encoder;
+ struct intel_dp *intel_dp;
+ int active_pipes = 0;
+ u32 trigger = 0;
+
+ /* disable unless all conditions are met */
+ state->dc3co.trigger = DC3CO_TRIGGER_NONE;
+ state->dc3co.allowed = false;
+
+ if (!HAS_DC3CO(display))
+ return;
+
+ if (state->modeset)
+ return;
+
+ for_each_intel_crtc(display->drm, crtc) {
+ crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
+ if (!crtc_state)
+ crtc_state = intel_atomic_get_old_crtc_state(state, crtc);
+
+ if (!crtc_state || !crtc_state->hw.active)
+ continue;
+
+ active_pipes++;
+
+ if (active_pipes > 1)
+ return;
+
+ for_each_intel_encoder_mask(display->drm, encoder,
+ crtc_state->uapi.encoder_mask) {
+ if (encoder->type != INTEL_OUTPUT_EDP)
+ return;
+
+ intel_dp = enc_to_intel_dp(encoder);
+
+ if (!intel_dc3co_port_pipe_compatible(intel_dp, crtc_state))
+ return;
+ }
+
+ if (crtc_state->has_lobf)
+ trigger |= DC3CO_TRIGGER_LOBF;
+ if (crtc_state->has_panel_replay)
+ trigger |= DC3CO_TRIGGER_PANEL_REPLAY;
+ if (crtc_state->has_sel_update)
+ trigger |= DC3CO_TRIGGER_PSR2;
+ }
+ if (trigger) {
+ state->dc3co.trigger = trigger;
+ state->dc3co.allowed = true;
+ }
+ drm_dbg_kms(display->drm, "DC3CO allowed=%d trigger=0x%x\n",
+ state->dc3co.allowed, state->dc3co.trigger);
+}
+
static int intel_atomic_check_joiner(struct intel_atomic_state *state,
struct intel_crtc *primary_crtc)
{
@@ -6623,6 +6698,7 @@ int intel_atomic_check(struct drm_device *dev,
if (ret)
goto fail;

+ intel_dc3co_compute_state(state);
for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
new_crtc_state, i) {
intel_color_assert_luts(new_crtc_state);
@@ -7505,6 +7581,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
struct intel_power_domain_mask put_domains[I915_MAX_PIPES] = {};
struct ref_tracker *wakeref = NULL;
int i;
+ int power_async_delay;

for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i)
intel_atomic_dsb_prepare(state, crtc);
@@ -7711,11 +7788,22 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
*/
intel_uncore_arm_unclaimed_mmio_detection(uncore);
}
- /*
- * Delay re-enabling DC states by 17 ms to avoid the off->on->off
- * toggling overhead at and above 60 FPS.
- */
- intel_display_power_put_async_delay(display, POWER_DOMAIN_DC_OFF, wakeref, 17);
+
+ if (intel_dc3co_allowed(state) &&
+ intel_display_power_dc3co_supported(display)) {
+ intel_display_power_set_target_dc_state(display, DC_STATE_EN_UPTO_DC3CO);
+ power_async_delay = 1;
+ } else {
+ /*
+ * Delay re-enabling DC states by 17 ms to avoid the off->on->off
+ * toggling overhead at and above 60 FPS.
+ */
+ intel_display_power_set_target_dc_state(display, DC_STATE_EN_UPTO_DC6);
+ power_async_delay = 17;
+ }
+ intel_display_power_put_async_delay(display,
+ POWER_DOMAIN_DC_OFF, wakeref, power_async_delay);
+
intel_display_rpm_put(display, state->wakeref);

/*
diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h
index 552a59d19e0f..6eb84f9d8791 100644
--- a/drivers/gpu/drm/i915/display/intel_display.h
+++ b/drivers/gpu/drm/i915/display/intel_display.h
@@ -535,5 +535,5 @@ bool assert_port_valid(struct intel_display *display, enum port port);

bool intel_scanout_needs_vtd_wa(struct intel_display *display);
int intel_crtc_num_joined_pipes(const struct intel_crtc_state *crtc_state);
-
+bool intel_dc3co_allowed(struct intel_atomic_state *state);
#endif
diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
index 6830f911d94d..6c7f5bbbc821 100644
--- a/drivers/gpu/drm/i915/display/intel_display_types.h
+++ b/drivers/gpu/drm/i915/display/intel_display_types.h
@@ -632,6 +632,11 @@ struct dpll {
int p;
};

+struct intel_dc3co_state {
+ bool allowed; /* DC3CO eligibility result */
+ u32 trigger; /* Bitmask of active DC3CO triggers */
+};
+
struct intel_atomic_state {
struct drm_atomic_state base;

@@ -658,6 +663,8 @@ struct intel_atomic_state {
bool rps_interactive;

struct work_struct cleanup_work;
+
+ struct intel_dc3co_state dc3co;
};

struct intel_plane_state {
--
2.43.0