Compute if dc3co is allowed in intel_atomic_commit_tail()
based on pipe/port constraints and runtime triggers and store
result in display->power.dc3co.

When DC3CO can be enabled, 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.

Changes in v5:
- Move DC3CO compute logic from intel_atomic_check()
  to intel_atomic_commit_tail as it is not advisable to
  change persistent state in atomic check (Jani Nikula)
- Add psr2 deep sleep check in dc3co compute.
- Move allowed computation logic inside dc3co update (Jani Nikula).
- Add dc3co support check in dc3co allowed function (Jani Nikula)
- Move all dc3co functions to intel_display_power.c and
  rename functions accordingly  (Jani Nikula)
- Clean up dc3co/dc6 power async delay in
  intel_atomic_commit_tail() (Jani Nikula)

BSpec: 75253
Signed-off-by: Dibin Moolakadan Subrahmanian 
<[email protected]>
---
 drivers/gpu/drm/i915/display/intel_display.c  |  14 +-
 .../gpu/drm/i915/display/intel_display_core.h |   2 +
 .../drm/i915/display/intel_display_power.c    | 135 ++++++++++++++++++
 .../drm/i915/display/intel_display_power.h    |  37 +++++
 4 files changed, 183 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_display.c 
b/drivers/gpu/drm/i915/display/intel_display.c
index 8e269b71f18e..083a77de752b 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -7428,6 +7428,7 @@ static void intel_atomic_commit_tail(struct 
intel_atomic_state *state)
        struct intel_crtc *crtc;
        struct intel_power_domain_mask put_domains[I915_MAX_PIPES] = {};
        struct ref_tracker *wakeref = NULL;
+       int power_async_delay;
 
        for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state)
                intel_atomic_dsb_prepare(state, crtc);
@@ -7536,6 +7537,8 @@ static void intel_atomic_commit_tail(struct 
intel_atomic_state *state)
        /* Now enable the clocks, plane, pipe, and connectors that we set up. */
        display->modeset.funcs->commit_modeset_enables(state);
 
+       intel_display_power_dc3co_compute(state);
+
        /* FIXME probably need to sequence this properly */
        intel_program_dpkgc_latency(state);
 
@@ -7632,11 +7635,12 @@ 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);
+
+       power_async_delay = intel_display_power_select_target_dc_state(state);
+
+       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_core.h 
b/drivers/gpu/drm/i915/display/intel_display_core.h
index 09ce25a6d4b1..6068d0f5089e 100644
--- a/drivers/gpu/drm/i915/display/intel_display_core.h
+++ b/drivers/gpu/drm/i915/display/intel_display_core.h
@@ -538,6 +538,8 @@ struct intel_display {
 
        struct {
                struct i915_power_domains domains;
+               /* DC3CO state */
+               struct intel_dc3co_state dc3co;
 
                /* Shadow for DISPLAY_PHY_CONTROL which can't be safely read */
                u32 chv_phy_control;
diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c 
b/drivers/gpu/drm/i915/display/intel_display_power.c
index eadae8eb5709..193129e171cd 100644
--- a/drivers/gpu/drm/i915/display/intel_display_power.c
+++ b/drivers/gpu/drm/i915/display/intel_display_power.c
@@ -14,7 +14,9 @@
 #include "intel_cdclk.h"
 #include "intel_clock_gating.h"
 #include "intel_combo_phy.h"
+#include "intel_crtc.h"
 #include "intel_de.h"
+#include "intel_display.h"
 #include "intel_display_power.h"
 #include "intel_display_power_map.h"
 #include "intel_display_power_well.h"
@@ -30,6 +32,8 @@
 #include "intel_pch_refclk.h"
 #include "intel_pmdemand.h"
 #include "intel_pps_regs.h"
+#include "intel_psr.h"
+#include "intel_psr_regs.h"
 #include "intel_snps_phy.h"
 #include "skl_watermark.h"
 #include "skl_watermark_regs.h"
@@ -368,6 +372,136 @@ bool intel_display_power_dc3co_supported(struct 
intel_display *display)
        return (power_domains->allowed_dc_mask & DC_STATE_EN_UPTO_DC3CO) == 
DC_STATE_EN_UPTO_DC3CO;
 }
 
+bool intel_display_power_dc3co_allowed(struct intel_display *display)
+{
+       struct intel_dc3co_state *dc3co = &display->power.dc3co;
+       bool allowed;
+
+       if (!intel_display_power_dc3co_supported(display))
+               return false;
+
+       mutex_lock(&dc3co->lock);
+       allowed = dc3co->allowed;
+       mutex_unlock(&dc3co->lock);
+
+       return allowed;
+}
+
+void intel_display_power_dc3co_update(struct intel_display *display, u32 
trigger)
+{
+       struct intel_dc3co_state *dc3co = &display->power.dc3co;
+
+       if (!intel_display_power_dc3co_supported(display))
+               return;
+
+       mutex_lock(&dc3co->lock);
+       dc3co->trigger = trigger;
+       dc3co->allowed = !!trigger;
+       mutex_unlock(&dc3co->lock);
+       drm_dbg_kms(display->drm, "DC3CO allowed=%d trigger=0x%x\n",
+                   dc3co->allowed, dc3co->trigger);
+}
+
+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);
+
+       /* Need to follow 1:1 mapping because of CMTG restriction */
+       if (DISPLAY_VER(to_intel_display(crtc_state)) == 35)
+               return num_pipes == 1 &&
+                      ((pipe == PIPE_A && port == PORT_A) ||
+                       (pipe == PIPE_B && port == PORT_B));
+       else
+               return num_pipes == 1 && pipe <= PIPE_B && port <= PORT_B;
+}
+
+void intel_display_power_dc3co_compute(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;
+       u8 active_pipes = 0;
+       enum pipe pipe;
+       u32 trigger = DC3CO_TRIGGER_NONE;
+
+       if (!intel_display_power_dc3co_supported(display))
+               return;
+
+       for_each_intel_crtc(display, crtc)
+               active_pipes |= crtc->active ? BIT(crtc->pipe) : 0;
+
+       active_pipes = intel_calc_active_pipes(state, active_pipes);
+
+       if (hweight8(active_pipes) != 1)
+               goto done;
+
+       pipe = ffs(active_pipes) - 1;
+       crtc = intel_crtc_for_pipe(display, pipe);
+
+       crtc_state = to_intel_crtc_state(crtc->base.state);
+
+       for_each_intel_encoder_mask(display->drm, encoder,
+                                   crtc_state->uapi.encoder_mask) {
+               if (encoder->type != INTEL_OUTPUT_EDP)
+                       goto done;
+
+               intel_dp = enc_to_intel_dp(encoder);
+
+               if (!intel_dc3co_port_pipe_compatible(intel_dp, crtc_state))
+                       goto done;
+
+               if (intel_psr2_in_deep_sleep(intel_dp))
+                       goto done;
+       }
+
+       if (crtc_state->has_lobf)
+               trigger |= DC3CO_TRIGGER_LOBF;
+       if (crtc_state->has_panel_replay && intel_dp->as_sdp_supported)
+               trigger |= DC3CO_TRIGGER_PANEL_REPLAY;
+       if (crtc_state->has_sel_update)
+               trigger |= DC3CO_TRIGGER_PSR2;
+
+done:
+       intel_display_power_dc3co_update(display, trigger);
+}
+
+/*
+ * Select the target DC state for this commit and return the async-put delay
+ * to use when releasing the DC_OFF reference.
+ *
+ * Picks DC_STATE_EN_UPTO_DC3CO when DC3CO can be enabled
+ * otherwise falls back to default DC state of DC_STATE_EN_UPTO_DC6.
+ * The chosen target is programmed via 
intel_display_power_set_target_dc_state().
+ *
+ * Returns the async-put delay (in ms) to use when releasing the DC_OFF
+ * reference: DC3CO_PUT_ASYNC_DELAY_MS when DC3CO was selected, otherwise
+ * DC6_PUT_ASYNC_DELAY_MS.
+ */
+int intel_display_power_select_target_dc_state(struct intel_atomic_state 
*state)
+{
+       struct intel_display *display = to_intel_display(state);
+       u32 target_dc_state;
+
+       if (!intel_display_power_dc3co_supported(display))
+               return DC6_PUT_ASYNC_DELAY_MS;
+
+       if (intel_display_power_dc3co_allowed(display))
+               target_dc_state = DC_STATE_EN_UPTO_DC3CO;
+       else
+               target_dc_state = DC_STATE_EN_UPTO_DC6;
+
+       intel_display_power_set_target_dc_state(display, target_dc_state);
+
+       return target_dc_state == DC_STATE_EN_UPTO_DC3CO ?
+               DC3CO_PUT_ASYNC_DELAY_MS : DC6_PUT_ASYNC_DELAY_MS;
+}
+
 static void __async_put_domains_mask(struct i915_power_domains *power_domains,
                                     struct intel_power_domain_mask *mask)
 {
@@ -1047,6 +1181,7 @@ int intel_display_power_init(struct intel_display 
*display)
                sanitize_target_dc_state(display, DC_STATE_EN_UPTO_DC6);
 
        mutex_init(&power_domains->lock);
+       mutex_init(&display->power.dc3co.lock);
 
        INIT_DELAYED_WORK(&power_domains->async_put_work,
                          intel_display_power_put_async_work);
diff --git a/drivers/gpu/drm/i915/display/intel_display_power.h 
b/drivers/gpu/drm/i915/display/intel_display_power.h
index 06b3e49e5f8b..7470b541677b 100644
--- a/drivers/gpu/drm/i915/display/intel_display_power.h
+++ b/drivers/gpu/drm/i915/display/intel_display_power.h
@@ -9,9 +9,12 @@
 #include <linux/mutex.h>
 #include <linux/workqueue.h>
 
+#include "intel_display_limits.h"
+
 enum aux_ch;
 enum port;
 struct i915_power_well;
+struct intel_atomic_state;
 struct intel_display;
 struct intel_encoder;
 struct ref_tracker;
@@ -131,6 +134,36 @@ struct intel_power_domain_mask {
        DECLARE_BITMAP(bits, POWER_DOMAIN_NUM);
 };
 
+/*
+ * DC3CO enabling triggers (bitmask).
+ * DC3CO may be enabled when at least one of these triggers is active.
+ * Additional constraints may still apply.
+ */
+#define DC3CO_TRIGGER_NONE             (0)
+#define DC3CO_TRIGGER_PSR2             BIT(0)
+#define DC3CO_TRIGGER_LOBF             BIT(1)
+#define DC3CO_TRIGGER_PANEL_REPLAY     BIT(2)
+#define DC3CO_TRIGGER_ALL              (DC3CO_TRIGGER_PSR2 | \
+                                        DC3CO_TRIGGER_LOBF | \
+                                        DC3CO_TRIGGER_PANEL_REPLAY)
+
+/*
+ * Delay to re-enable DC5/DC6 states by 17 ms to avoid the off->on->off
+ * toggling overhead at and above 60 FPS.
+ */
+#define DC6_PUT_ASYNC_DELAY_MS         17
+/*
+ * Use minimal re-enable delay to allow DC3CO entry on
+ * the next idle frame.
+ */
+#define DC3CO_PUT_ASYNC_DELAY_MS       1
+
+struct intel_dc3co_state {
+       struct mutex lock; /* protects allowed and trigger fields */
+       bool allowed; /* DC3CO compute result */
+       u32 trigger; /* Bitmask of active DC3CO triggers */
+};
+
 struct i915_power_domains {
        /*
         * Power wells needed for initialization at driver init and suspend
@@ -183,6 +216,10 @@ void intel_display_power_set_target_dc_state(struct 
intel_display *display,
                                             u32 state);
 u32 intel_display_power_get_current_dc_state(struct intel_display *display);
 bool intel_display_power_dc3co_supported(struct intel_display *display);
+void intel_display_power_dc3co_update(struct intel_display *display, u32 
trigger);
+bool intel_display_power_dc3co_allowed(struct intel_display *display);
+void intel_display_power_dc3co_compute(struct intel_atomic_state *state);
+int intel_display_power_select_target_dc_state(struct intel_atomic_state 
*state);
 
 void intel_display_power_runtime_suspend(struct intel_display *display);
 void intel_display_power_runtime_resume(struct intel_display *display);
-- 
2.43.0

Reply via email to