diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1780,6 +1780,7 @@ struct intel_psr { u32 dc3co_exitline; u32 dc3co_exit_delay; struct delayed_work dc3co_work; + struct delayed_work panel_replay_reenable_work; u8 entry_setup_frames; u8 io_wake_lines; diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -2481,6 +2481,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); + cancel_delayed_work_sync(&intel_dp->psr.panel_replay_reenable_work); } /** @@ -2512,6 +2513,7 @@ void intel_psr_pause(struct intel_dp *intel_dp) cancel_work_sync(&psr->work); cancel_delayed_work_sync(&psr->dc3co_work); + cancel_delayed_work_sync(&psr->panel_replay_reenable_work); } /** @@ -3545,6 +3547,17 @@ static void intel_psr_handle_irq(struct intel_dp *intel_dp) drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER, DP_SET_POWER_D0); } +#define PANEL_REPLAY_REENABLE_DELAY_MS 50 + +static bool panel_replay_exit_on_flush_enabled(struct intel_dp *intel_dp) +{ + return intel_dp_is_edp(intel_dp) && + intel_dp->psr.panel_replay_enabled && + intel_dp->psr.sel_update_enabled && + intel_has_dpcd_quirk(intel_dp, + QUIRK_PANEL_REPLAY_EXIT_ON_FLUSH); +} + static void intel_psr_work(struct work_struct *work) { struct intel_dp *intel_dp = @@ -3563,6 +3576,11 @@ static void intel_psr_work(struct work_struct *work) if (intel_dp->psr.pause_counter) goto unlock; + /* The dedicated delayed work owns re-entry while the quirk is armed. */ + if (panel_replay_exit_on_flush_enabled(intel_dp) && + delayed_work_pending(&intel_dp->psr.panel_replay_reenable_work)) + goto unlock; + /* * We have to make sure PSR is ready for re-enable * otherwise it keeps disabled until next full enable/disable cycle. @@ -3572,6 +3590,10 @@ static void intel_psr_work(struct work_struct *work) if (!__psr_wait_for_idle_locked(intel_dp)) goto unlock; + if (panel_replay_exit_on_flush_enabled(intel_dp) && + delayed_work_pending(&intel_dp->psr.panel_replay_reenable_work)) + goto unlock; + /* * The delayed work can race with an invalidate hence we need to * recheck. Since psr_flush first clears this and then reschedules we @@ -3585,6 +3607,33 @@ static void intel_psr_work(struct work_struct *work) mutex_unlock(&intel_dp->psr.lock); } +static void panel_replay_reenable_work(struct work_struct *work) +{ + struct intel_dp *intel_dp = + container_of(work, typeof(*intel_dp), + psr.panel_replay_reenable_work.work); + + mutex_lock(&intel_dp->psr.lock); + + if (!intel_dp->psr.enabled || + !panel_replay_exit_on_flush_enabled(intel_dp) || + intel_dp->psr.pause_counter || + READ_ONCE(intel_dp->psr.irq_aux_error)) + goto unlock; + + if (!__psr_wait_for_idle_locked(intel_dp)) + goto unlock; + + /* Recheck for activity or a newer deadline after the unlocked wait. */ + if (delayed_work_pending(&intel_dp->psr.panel_replay_reenable_work) || + intel_dp->psr.busy_frontbuffer_bits || intel_dp->psr.active) + goto unlock; + + intel_psr_activate(intel_dp); +unlock: + mutex_unlock(&intel_dp->psr.lock); +} + static void intel_psr_configure_full_frame_update(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); @@ -3656,8 +3705,11 @@ void intel_psr_invalidate(struct intel_display *display, INTEL_FRONTBUFFER_ALL_MASK(intel_dp->psr.pipe); intel_dp->psr.busy_frontbuffer_bits |= pipe_frontbuffer_bits; - if (pipe_frontbuffer_bits) + if (pipe_frontbuffer_bits) { + if (panel_replay_exit_on_flush_enabled(intel_dp)) + cancel_delayed_work(&intel_dp->psr.panel_replay_reenable_work); _psr_invalidate_handle(intel_dp); + } mutex_unlock(&intel_dp->psr.lock); } @@ -3773,6 +3825,15 @@ void intel_psr_flush(struct intel_display *display, if (intel_dp->psr.pause_counter) goto unlock; + if (pipe_frontbuffer_bits && + panel_replay_exit_on_flush_enabled(intel_dp)) { + intel_psr_exit(intel_dp); + mod_delayed_work(display->wq.unordered, + &intel_dp->psr.panel_replay_reenable_work, + msecs_to_jiffies(PANEL_REPLAY_REENABLE_DELAY_MS)); + goto unlock; + } + if (origin == ORIGIN_FLIP || (origin == ORIGIN_CURSOR_UPDATE && !intel_dp->psr.psr2_sel_fetch_enabled)) { @@ -3836,6 +3897,8 @@ void intel_psr_init(struct intel_dp *intel_dp) INIT_WORK(&intel_dp->psr.work, intel_psr_work); INIT_DELAYED_WORK(&intel_dp->psr.dc3co_work, tgl_dc3co_disable_work); + INIT_DELAYED_WORK(&intel_dp->psr.panel_replay_reenable_work, + panel_replay_reenable_work); mutex_init(&intel_dp->psr.lock); } diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c --- a/drivers/gpu/drm/i915/display/intel_quirks.c +++ b/drivers/gpu/drm/i915/display/intel_quirks.c @@ -94,6 +94,14 @@ static void quirk_disable_edp_panel_replay(struct intel_dp *intel_dp) drm_info(display->drm, "Applying disable Panel Replay quirk\n"); } +static void quirk_panel_replay_exit_on_flush(struct intel_dp *intel_dp) +{ + struct intel_display *display = to_intel_display(intel_dp); + + intel_set_dpcd_quirk(intel_dp, QUIRK_PANEL_REPLAY_EXIT_ON_FLUSH); + drm_info(display->drm, "Applying Panel Replay exit on flush quirk\n"); +} + static void quirk_disable_psr2(struct intel_display *display) { intel_set_quirk(display, QUIRK_DISABLE_PSR2); diff --git a/drivers/gpu/drm/i915/display/intel_quirks.h b/drivers/gpu/drm/i915/display/intel_quirks.h --- a/drivers/gpu/drm/i915/display/intel_quirks.h +++ b/drivers/gpu/drm/i915/display/intel_quirks.h @@ -23,6 +23,7 @@ enum intel_quirk_id { QUIRK_EDP_LIMIT_RATE_HBR2, QUIRK_DISABLE_EDP_PANEL_REPLAY, QUIRK_DISABLE_PSR2, + QUIRK_PANEL_REPLAY_EXIT_ON_FLUSH, }; void intel_init_quirks(struct intel_display *display);