DC3CO is useful power state, when DMC detects PSR2 idle frame
while an active video playback, playing 30fps video on 60hz panel
is the classic example of this use case.
DC5 and DC6 saves more power, but can't be entered during video
playback because there are not enough idle frames in a row to meet
most PSR2 panel deep sleep entry requirement typically 4 frames.
It will be worthy to enable DC3CO after completion of each flip
and switch back to DC5 when display is idle, as driver doesn't
differentiate between video playback and a normal flip.
It is safer to allow DC5 after 6 idle frame, as PSR2 requires
minimum 6 idle frame.
v2: calculated s/w state to switch over dc3co when there is an
update. [Imre]
used cancel_delayed_work_sync() in order to avoid any race
with already scheduled delayed work. [Imre]
v3: cancel_delayed_work_sync() may blocked the commit work.
Hence dropping it, dc5_idle_thread() checks the valid wakeref before
putting the reference count, which avoids any chances of dropping
a zero wakeref. [Imre (IRC)]
Cc: Jani Nikula
Cc: Imre Deak
Cc: Animesh Manna
Signed-off-by: Anshuman Gupta
---
drivers/gpu/drm/i915/display/intel_display.c | 4 +
.../drm/i915/display/intel_display_power.c| 77 +++
.../drm/i915/display/intel_display_power.h| 5 ++
drivers/gpu/drm/i915/i915_drv.h | 1 +
4 files changed, 87 insertions(+)
diff --git a/drivers/gpu/drm/i915/display/intel_display.c
b/drivers/gpu/drm/i915/display/intel_display.c
index 1ec204c14a10..906a8e6ec9e1 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -14082,6 +14082,8 @@ static void intel_atomic_commit_tail(struct
intel_atomic_state *state)
intel_uncore_arm_unclaimed_mmio_detection(&dev_priv->uncore);
intel_display_power_put(dev_priv, POWER_DOMAIN_MODESET,
wakeref);
}
+
+ tgl_switch_to_dc3co_after_flip(dev_priv);
intel_runtime_pm_put(&dev_priv->runtime_pm, state->wakeref);
/*
@@ -16157,6 +16159,7 @@ int intel_modeset_init(struct drm_device *dev)
init_llist_head(&dev_priv->atomic_helper.free_list);
INIT_WORK(&dev_priv->atomic_helper.free_work,
intel_atomic_helper_free_state_worker);
+ INIT_DELAYED_WORK(&dev_priv->csr.idle_work, intel_dc5_idle_thread);
intel_init_quirks(dev_priv);
@@ -17100,6 +17103,7 @@ void intel_modeset_driver_remove(struct drm_device *dev)
flush_workqueue(dev_priv->modeset_wq);
flush_work(&dev_priv->atomic_helper.free_work);
+ flush_delayed_work(&dev_priv->csr.idle_work);
WARN_ON(!llist_empty(&dev_priv->atomic_helper.free_list));
/*
diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c
b/drivers/gpu/drm/i915/display/intel_display_power.c
index 2667d205fa36..31d0f389ac17 100644
--- a/drivers/gpu/drm/i915/display/intel_display_power.c
+++ b/drivers/gpu/drm/i915/display/intel_display_power.c
@@ -19,6 +19,7 @@
#include "intel_sideband.h"
#include "intel_tc.h"
#include "intel_pm.h"
+#include "intel_psr.h"
bool intel_display_power_well_is_enabled(struct drm_i915_private *dev_priv,
enum i915_power_well_id power_well_id);
@@ -825,6 +826,46 @@ void tgl_enable_psr2_transcoder_exitline(struct
intel_crtc_state *cstate)
I915_WRITE(EXITLINE(cstate->cpu_transcoder), val);
}
+void tgl_switch_to_dc3co_after_flip(struct drm_i915_private *dev_priv)
+{
+ struct intel_crtc *crtc;
+ struct intel_crtc_state *cstate;
+ u32 delay;
+
+ if (!dev_priv->csr.prefer_dc3co)
+ return;
+
+ mutex_lock(&dev_priv->psr.lock);
+ if (!dev_priv->psr.psr2_enabled || !dev_priv->psr.active)
+ goto unlock;
+
+ /*
+* As every flip go through intel_atomic_commit, so tracking a
+* atomic commit will be a hint for idle frames.
+* Delayed work for 6 idle frames will be enough to allow dc6
+* over dc3co for deepest power savings.
+* At every atomic commit first cancel the delayed work ,
+* when delayed schedules that means display has been idle
+* for the 6 idle frame.
+*/
+ cancel_delayed_work(&dev_priv->csr.idle_work);
+ mutex_lock(&dev_priv->csr.dc5_mutex);
+ if (!dev_priv->csr.dc5_wakeref) {
+ dev_priv->csr.dc5_wakeref =
+ intel_display_power_get(dev_priv, POWER_DOMAIN_VIDEO);
+ tgl_psr2_deep_sleep_disable(dev_priv);
+ }
+ mutex_unlock(&dev_priv->csr.dc5_mutex);
+ crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_A);
+ cstate = to_intel_crtc_state(crtc->base.state);
+
+ delay = DC5_REQ_IDLE_FRAMES * intel_get_frame_time_us(cstate);
+ schedule_delayed_work(&dev_priv->csr.idle_work,
+ usecs_to_jiffies(delay));
+unlock:
+ mutex_unlock(&dev_priv->psr.lock);
+}
+