For DC3CO, idle_frames is programmed to 0, so PSR does not
enter deep sleep. Add delayed work to schedule DC3CO exit
after an idle duration derived from frame time (minimum
equivalent of 6 frames).

The work is re-armed from the PSR flush path on relevant frontbuffer
activity. Once the display remains idle, DC3CO is disabled, idle frames
are reprogrammed to their normal value, and DC6 is enabled to allow
deeper power savings.

Changes in v2:
- Squash "PSR set idle frames while exit from DC3CO"
  into this patch (Uma Shankar)
- Add cancel_delayed_work() in intel_psr_disable_locked()
  before clearing dc3co_eligible (Uma Shankar)

Changes in v4:
- Re-arm cancelled DC3CO work in psr resume
- Schedule DC3CO work from intel_psr_post_plane_update(). This is to
  make sure DC3CO work scheduling will happen even without psr flush,
  which may be a valid scenario.

Signed-off-by: Dibin Moolakadan Subrahmanian 
<[email protected]>
---
 .../drm/i915/display/intel_display_types.h    |  2 +
 drivers/gpu/drm/i915/display/intel_psr.c      | 62 ++++++++++++++++++-
 2 files changed, 63 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h 
b/drivers/gpu/drm/i915/display/intel_display_types.h
index 5b1d0fa3e888..a040a1d37784 100644
--- a/drivers/gpu/drm/i915/display/intel_display_types.h
+++ b/drivers/gpu/drm/i915/display/intel_display_types.h
@@ -1773,6 +1773,8 @@ struct intel_psr {
        bool irq_aux_error;
        /* DC3CO allowed used to control PSR configuration */
        bool dc3co_allowed;
+       /* DC3CO disable work */
+       struct delayed_work dc3co_work;
        u16 su_w_granularity;
        u16 su_y_granularity;
        bool source_panel_replay_support;
diff --git a/drivers/gpu/drm/i915/display/intel_psr.c 
b/drivers/gpu/drm/i915/display/intel_psr.c
index 091da8341b0f..822bc1d6af53 100644
--- a/drivers/gpu/drm/i915/display/intel_psr.c
+++ b/drivers/gpu/drm/i915/display/intel_psr.c
@@ -1774,6 +1774,51 @@ static bool intel_psr_needs_wa_18037818876(struct 
intel_dp *intel_dp,
                !crtc_state->has_sel_update);
 }
 
+static void psr2_dc3co_disable_locked(struct intel_dp *intel_dp)
+{
+       struct intel_display *display = to_intel_display(intel_dp);
+
+       if (intel_dp->psr.dc3co_allowed) {
+               intel_dp->psr.dc3co_allowed = false;
+               intel_display_power_set_target_dc_state(display, 
DC_STATE_EN_UPTO_DC6);
+               psr2_program_idle_frames(intel_dp, 
psr_compute_idle_frames(intel_dp));
+       }
+}
+
+static void psr2_dc3co_disable_work(struct work_struct *work)
+{
+       struct intel_dp *intel_dp =
+               container_of(work, typeof(*intel_dp), psr.dc3co_work.work);
+
+       mutex_lock(&intel_dp->psr.lock);
+       psr2_dc3co_disable_locked(intel_dp);
+       mutex_unlock(&intel_dp->psr.lock);
+}
+
+static void
+psr2_dc3co_flush_locked(struct intel_dp *intel_dp, unsigned int 
frontbuffer_bits,
+                       enum fb_op_origin origin)
+{
+       struct intel_display *display = to_intel_display(intel_dp);
+
+       if (!intel_dp->psr.dc3co_allowed)
+               return;
+
+       if (!intel_dp->psr.sel_update_enabled ||
+           !intel_dp->psr.active)
+               return;
+       /*
+        * At every frontbuffer flush flip event modified delay of delayed work,
+        * when delayed work schedules that means display has been idle.
+        */
+       if (!(frontbuffer_bits &
+           INTEL_FRONTBUFFER_ALL_MASK(intel_dp->psr.pipe)))
+               return;
+
+       mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work,
+                        intel_dp->psr.dc3co_exit_delay);
+}
+
 static
 void intel_psr_set_non_psr_pipes(struct intel_dp *intel_dp,
                                 struct intel_crtc_state *crtc_state)
@@ -2331,6 +2376,7 @@ static void intel_psr_disable_locked(struct intel_dp 
*intel_dp)
        intel_dp->psr.psr2_sel_fetch_cff_enabled = false;
        intel_dp->psr.active_non_psr_pipes = 0;
        intel_dp->psr.pkg_c_latency_used = 0;
+       cancel_delayed_work(&intel_dp->psr.dc3co_work);
        intel_dp->psr.dc3co_allowed = false;
 }
 
@@ -2361,6 +2407,7 @@ void intel_psr_disable(struct intel_dp *intel_dp,
 
        mutex_unlock(&intel_dp->psr.lock);
        cancel_work_sync(&intel_dp->psr.work);
+       cancel_delayed_work_sync(&intel_dp->psr.dc3co_work);
 }
 
 /**
@@ -2391,6 +2438,7 @@ void intel_psr_pause(struct intel_dp *intel_dp)
        mutex_unlock(&psr->lock);
 
        cancel_work_sync(&psr->work);
+       cancel_delayed_work_sync(&psr->dc3co_work);
 }
 
 /**
@@ -2417,8 +2465,13 @@ void intel_psr_resume(struct intel_dp *intel_dp)
                goto out;
        }
 
-       if (--intel_dp->psr.pause_counter == 0)
+       if (--intel_dp->psr.pause_counter == 0) {
                intel_psr_activate(intel_dp);
+               /* re-arm cancelled dc3co work from pause */
+               if (intel_dp->psr.dc3co_allowed)
+                       mod_delayed_work(display->wq.unordered, 
&intel_dp->psr.dc3co_work,
+                                        intel_dp->psr.dc3co_exit_delay);
+       }
 
 out:
        mutex_unlock(&psr->lock);
@@ -3174,6 +3227,11 @@ void intel_psr_post_plane_update(struct 
intel_atomic_state *state,
                 */
                intel_dp->psr.busy_frontbuffer_bits = 0;
 
+               if (intel_dp->psr.dc3co_allowed) {
+                       mod_delayed_work(display->wq.unordered, 
&intel_dp->psr.dc3co_work,
+                                        intel_dp->psr.dc3co_exit_delay);
+               }
+
                mutex_unlock(&psr->lock);
        }
 }
@@ -3632,6 +3690,7 @@ void intel_psr_flush(struct intel_display *display,
                if (origin == ORIGIN_FLIP ||
                    (origin == ORIGIN_CURSOR_UPDATE &&
                     !intel_dp->psr.psr2_sel_fetch_enabled)) {
+                       psr2_dc3co_flush_locked(intel_dp, frontbuffer_bits, 
origin);
                        goto unlock;
                }
 
@@ -3690,6 +3749,7 @@ void intel_psr_init(struct intel_dp *intel_dp)
                intel_dp->psr.link_standby = connector->panel.vbt.psr.full_link;
 
        INIT_WORK(&intel_dp->psr.work, intel_psr_work);
+       INIT_DELAYED_WORK(&intel_dp->psr.dc3co_work, psr2_dc3co_disable_work);
        mutex_init(&intel_dp->psr.lock);
 }
 
-- 
2.43.0

Reply via email to