mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
drm/amd/display: Exit idle optimizations before programming
[Why]
We need to exit PSR/IPS before programming. Before calling DC for
programming in amdgpu_dm_commit_planes(), there's a
vblank_control_workqueue flush. This waits for IPS and PSR exit. (See
drm_vblank_on/off() > amdgpu_dm_crtc_set_vblank() --queue_work()->
amdgpu_dm_crtc_vblank_control_worker())
Prior to the tagged "Fixes:" change, drm_vblank_get() was called before
the workqueue flush. This ordering ensures that PSR exit occurred before
programming. After the "Fixes:" change, drm_vblank_get() is called after
the workqueue flush, leading to programming while idle optimizations are
still active. This can lead to incorrect flip_pending detection used by
vblank event delivery.
[How]
Split the vblank_get() component of `dm_arm_vblank_event()` into
`dm_arm_vblank_event_pre_programming()`, which is called before
programming. Call it before the vblank_control_workqueue flush.
Includes a drive-by cleanup of prepare_flip_isr(): the only caller is
dm_arm_vblank_event() and it's simple enough to roll-in.
v2: Fix checkpatch formatting warning on
drm_arm_vblank_event_pre_programming() arg alignment.
Fixes: 48ab86360a ("drm/amd/display: check GRPH_FLIP status before sending event")
Cc: stable@vger.kernel.org
Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/4141#note_3583205
Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/5527
Assisted-by: Codex:gpt-5.6-sol
Assisted-by: Claude:opus-5
Suggested-by: David Weber <weber.aulendorf@gmail.com>
Signed-off-by: Leo Li <sunpeng.li@amd.com>
Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
(cherry picked from commit 05984e2952)
This commit is contained in:
@@ -9903,25 +9903,6 @@ static void remove_stream(struct amdgpu_device *adev,
|
||||
acrtc->enabled = false;
|
||||
}
|
||||
|
||||
static void prepare_flip_isr(struct amdgpu_crtc *acrtc)
|
||||
{
|
||||
|
||||
assert_spin_locked(&acrtc->base.dev->event_lock);
|
||||
WARN_ON(acrtc->event);
|
||||
|
||||
acrtc->event = acrtc->base.state->event;
|
||||
|
||||
/* Set the flip status */
|
||||
acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED;
|
||||
|
||||
/* Mark this event as consumed */
|
||||
acrtc->base.state->event = NULL;
|
||||
|
||||
drm_dbg_state(acrtc->base.dev,
|
||||
"crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n",
|
||||
acrtc->crtc_id);
|
||||
}
|
||||
|
||||
static void update_freesync_state_on_stream(
|
||||
struct amdgpu_display_manager *dm,
|
||||
struct dm_crtc_state *new_crtc_state,
|
||||
@@ -10274,17 +10255,47 @@ static void dm_arm_vblank_event(struct amdgpu_crtc *acrtc,
|
||||
return;
|
||||
|
||||
if (pflip_update) {
|
||||
drm_crtc_vblank_get(&acrtc->base);
|
||||
WARN_ON(acrtc->pflip_status != AMDGPU_FLIP_NONE);
|
||||
/* Arm flip completion handling and event delivery after programming. */
|
||||
prepare_flip_isr(acrtc);
|
||||
WARN_ON(acrtc->event);
|
||||
|
||||
acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED;
|
||||
acrtc->event = acrtc->base.state->event;
|
||||
acrtc->base.state->event = NULL;
|
||||
|
||||
drm_dbg_state(acrtc->base.dev,
|
||||
"crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n",
|
||||
acrtc->crtc_id);
|
||||
} else if (cursor_update) {
|
||||
drm_crtc_vblank_get(&acrtc->base);
|
||||
acrtc->event = acrtc->base.state->event;
|
||||
acrtc->base.state->event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_arm_vblank_event_pre_programming - Prepare for programming
|
||||
* @acrtc: The amdgpu CRTC to prepare
|
||||
* @acrtc_state: The new CRTC state
|
||||
* @pflip_update: Whether a page flip is being programmed
|
||||
* @cursor_update: Whether a cursor update is being programmed
|
||||
*
|
||||
* Grab a reference on the vblank counter if a page flip or cursor update is to
|
||||
* be programmed. Do this before programming so the HW is not in any
|
||||
* idle-optimized state (such as PSR).
|
||||
*/
|
||||
static void dm_arm_vblank_event_pre_programming(struct amdgpu_crtc *acrtc,
|
||||
struct dm_crtc_state *acrtc_state,
|
||||
bool pflip_update,
|
||||
bool cursor_update)
|
||||
{
|
||||
assert_spin_locked(&acrtc->base.dev->event_lock);
|
||||
|
||||
if (!acrtc->base.state->event || acrtc_state->active_planes == 0)
|
||||
return;
|
||||
|
||||
if (pflip_update || cursor_update)
|
||||
drm_crtc_vblank_get(&acrtc->base);
|
||||
}
|
||||
|
||||
static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
|
||||
struct drm_device *dev,
|
||||
struct amdgpu_display_manager *dm,
|
||||
@@ -10550,16 +10561,19 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* DCE depends on a combination of GRPH_FLIP, VLINE0, and VUPDATE for
|
||||
* event delivery. Only GRPH_FLIP handler can send pflip events, and it
|
||||
* only fires if HW latched to the flip. Maintain legacy behavior by
|
||||
* arming event before programming.
|
||||
*/
|
||||
if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) {
|
||||
scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) {
|
||||
scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) {
|
||||
dm_arm_vblank_event_pre_programming(acrtc_attach, acrtc_state,
|
||||
pflip_present,
|
||||
cursor_update);
|
||||
/*
|
||||
* DCE depends on a combination of GRPH_FLIP, VLINE0, and
|
||||
* VUPDATE for event delivery. Only GRPH_FLIP handler can send
|
||||
* pflip events, and it only fires if HW latched to the flip.
|
||||
* Maintain legacy behavior by arming event before programming.
|
||||
*/
|
||||
if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) {
|
||||
dm_arm_vblank_event(acrtc_attach, acrtc_state,
|
||||
pflip_present, cursor_update);
|
||||
pflip_present, cursor_update);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user