drm/i915/display: PSR Add delayed work to exit DC3CO

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 v3:
- 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.

Changes in v4:
- Remove unused function parameter enum fb_op_origin origin
that is never used in the function body. (Animesh)

Signed-off-by: Dibin Moolakadan Subrahmanian <dibin.moolakadan.subrahmanian@intel.com>
Reviewed-by: Uma Shankar <uma.shankar@intel.com>
Reviewed-by: Animesh Manna <animesh.manna@intel.com>
Signed-off-by: Animesh Manna <animesh.manna@intel.com>
Link: https://patch.msgid.link/20260616162154.2630995-13-dibin.moolakadan.subrahmanian@intel.com
This commit is contained in:
Dibin Moolakadan Subrahmanian 2026-06-16 21:51:50 +05:30 committed by Animesh Manna
parent 645a651450
commit d49e3c5c81
2 changed files with 62 additions and 1 deletions

View File

@ -1778,6 +1778,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;

View File

@ -1774,6 +1774,50 @@ 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)
{
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 +2375,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 +2406,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 +2437,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 +2464,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 +3226,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 +3689,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);
goto unlock;
}
@ -3690,6 +3748,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);
}