* [PATCH v3 1/2] drm: Introduce drm_crtc_vblank_prepare()
@ 2026-01-09 19:20 sunpeng.li
2026-01-09 19:20 ` [PATCH v3 2/2] drm/amd/display: Implement prepare_vblank_enable callback sunpeng.li
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: sunpeng.li @ 2026-01-09 19:20 UTC (permalink / raw)
To: amd-gfx, dri-devel
Cc: Harry.Wentland, simona, airlied, jani.nikula, ville.syrjala,
Leo Li
From: Leo Li <sunpeng.li@amd.com>
Some drivers need to perform sleepable operations prior to enabling
vblank interrupts. A display hardware spin-up from a low-power state
that requires synchronization with the rest of the driver, for example.
To support this, introduce a DRM-internal drm_crtc_vblank_prepare()
helper that calls back into the driver -- if implemented -- for DRM to
do such preparation work before enabling vblank.
v3:
* Unexport drm_crtc_vblank_prepare() and make it DRM internal
* Drop warnings in drm core for vblank_prepare(), drivers can do so in
their implementations
* Drop unnecessary crtc null checks
* Check for drm_dev_has_vblank()
* Rebase on latest drm-misc-next
Signed-off-by: Leo Li <sunpeng.li@amd.com>
---
drivers/gpu/drm/drm_atomic_helper.c | 9 ++++++
drivers/gpu/drm/drm_client_modeset.c | 4 +++
drivers/gpu/drm/drm_internal.h | 1 +
drivers/gpu/drm/drm_plane.c | 5 +++
drivers/gpu/drm/drm_vblank.c | 48 ++++++++++++++++++++++++++++
drivers/gpu/drm/drm_vblank_helper.c | 5 ++-
drivers/gpu/drm/drm_vblank_work.c | 8 +++++
include/drm/drm_crtc.h | 21 ++++++++++++
include/drm/drm_vblank.h | 1 +
9 files changed, 101 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 5840e9cc6f666..2b9fa4aa48a1a 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -48,6 +48,7 @@
#include "drm_crtc_helper_internal.h"
#include "drm_crtc_internal.h"
+#include "drm_internal.h"
/**
* DOC: overview
@@ -1268,6 +1269,10 @@ crtc_disable(struct drm_device *dev, struct drm_atomic_state *state)
if (!drm_dev_has_vblank(dev))
continue;
+ ret = drm_crtc_vblank_prepare(crtc);
+ if (ret)
+ continue;
+
ret = drm_crtc_vblank_get(crtc);
/*
* Self-refresh is not a true "disable"; ensure vblank remains
@@ -1823,6 +1828,10 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
if (!new_crtc_state->active)
continue;
+ ret = drm_crtc_vblank_prepare(crtc);
+ if (ret != 0)
+ continue;
+
ret = drm_crtc_vblank_get(crtc);
if (ret != 0)
continue;
diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c
index fc4caf7da5fcd..6ccbde921dde4 100644
--- a/drivers/gpu/drm/drm_client_modeset.c
+++ b/drivers/gpu/drm/drm_client_modeset.c
@@ -1325,6 +1325,10 @@ int drm_client_modeset_wait_for_vblank(struct drm_client_dev *client, unsigned i
* Only wait for a vblank event if the CRTC is enabled, otherwise
* just don't do anything, not even report an error.
*/
+ ret = drm_crtc_vblank_prepare(crtc);
+ if (ret)
+ return ret;
+
ret = drm_crtc_vblank_get(crtc);
if (!ret) {
drm_crtc_wait_one_vblank(crtc);
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index f893b1e3a596e..8e3e21d734075 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -112,6 +112,7 @@ static inline bool drm_vblank_passed(u64 seq, u64 ref)
}
void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe);
+int drm_crtc_vblank_prepare(struct drm_crtc *crtc);
int drm_vblank_get(struct drm_device *dev, unsigned int pipe);
void drm_vblank_put(struct drm_device *dev, unsigned int pipe);
u64 drm_vblank_count(struct drm_device *dev, unsigned int pipe);
diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c
index bed2562bf911b..41681a3d96b15 100644
--- a/drivers/gpu/drm/drm_plane.c
+++ b/drivers/gpu/drm/drm_plane.c
@@ -35,6 +35,7 @@
#include <drm/drm_vblank.h>
#include "drm_crtc_internal.h"
+#include "drm_internal.h"
/**
* DOC: overview
@@ -1421,6 +1422,10 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
u32 current_vblank;
int r;
+ r = drm_crtc_vblank_prepare(crtc);
+ if (r)
+ return r;
+
r = drm_crtc_vblank_get(crtc);
if (r)
return r;
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index 42fe11cc139b9..b8a967a4ba7e5 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -1208,6 +1208,32 @@ static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe)
return ret;
}
+/**
+ * drm_crtc_vblank_prepare - prepare to enable vblank interrupts
+ *
+ * @crtc: which CRTC to prepare
+ *
+ * Some drivers may need to spin-up hardware from a low power state before
+ * enabling vblank interrupts. This function calls the prepare_enable_vblank
+ * callback, if available, to allow drivers to do that.
+ *
+ * This is a DRM-internal function, and is a thin wrapper around a driver
+ * callback. Drivers are expected to sequence their own prepare work internally.
+ *
+ * The spin-up may call sleeping functions, such as mutex_lock(). Therefore,
+ * this must be called from process context, where sleeping is allowed.
+ */
+int drm_crtc_vblank_prepare(struct drm_crtc *crtc)
+{
+ if (!drm_dev_has_vblank(crtc->dev))
+ return -EINVAL;
+
+ if (crtc->funcs->prepare_enable_vblank)
+ return crtc->funcs->prepare_enable_vblank(crtc);
+
+ return 0;
+}
+
int drm_vblank_get(struct drm_device *dev, unsigned int pipe)
{
struct drm_vblank_crtc *vblank = drm_vblank_crtc(dev, pipe);
@@ -1306,6 +1332,10 @@ int drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
int ret;
u64 last;
+ ret = drm_crtc_vblank_prepare(crtc);
+ if (ret)
+ return ret;
+
ret = drm_vblank_get(dev, pipe);
if (drm_WARN(dev, ret, "vblank not available on crtc %i, ret=%i\n",
pipe, ret))
@@ -1489,6 +1519,9 @@ void drm_crtc_vblank_on_config(struct drm_crtc *crtc,
if (drm_WARN_ON(dev, pipe >= dev->num_crtcs))
return;
+ if (drm_crtc_vblank_prepare(crtc))
+ return;
+
spin_lock_irq(&dev->vbl_lock);
drm_dbg_vbl(dev, "crtc %d, vblank enabled %d, inmodeset %d\n",
pipe, vblank->enabled, vblank->inmodeset);
@@ -1796,6 +1829,13 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
return 0;
}
+ crtc = drm_crtc_from_index(dev, vblank->pipe);
+ if (crtc) {
+ ret = drm_crtc_vblank_prepare(crtc);
+ if (ret)
+ return ret;
+ }
+
ret = drm_vblank_get(dev, pipe);
if (ret) {
drm_dbg_core(dev,
@@ -2031,6 +2071,10 @@ int drm_crtc_get_sequence_ioctl(struct drm_device *dev, void *data,
READ_ONCE(vblank->enabled);
if (!vblank_enabled) {
+ ret = drm_crtc_vblank_prepare(crtc);
+ if (ret)
+ return ret;
+
ret = drm_crtc_vblank_get(crtc);
if (ret) {
drm_dbg_core(dev,
@@ -2098,6 +2142,10 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data,
if (e == NULL)
return -ENOMEM;
+ ret = drm_crtc_vblank_prepare(crtc);
+ if (ret)
+ return ret;
+
ret = drm_crtc_vblank_get(crtc);
if (ret) {
drm_dbg_core(dev,
diff --git a/drivers/gpu/drm/drm_vblank_helper.c b/drivers/gpu/drm/drm_vblank_helper.c
index a04a6ba1b0ca0..fc5915acfa7f3 100644
--- a/drivers/gpu/drm/drm_vblank_helper.c
+++ b/drivers/gpu/drm/drm_vblank_helper.c
@@ -8,6 +8,8 @@
#include <drm/drm_vblank.h>
#include <drm/drm_vblank_helper.h>
+#include "drm_internal.h"
+
/**
* DOC: overview
*
@@ -61,7 +63,8 @@ void drm_crtc_vblank_atomic_flush(struct drm_crtc *crtc,
crtc_state->event = NULL;
if (event) {
- if (drm_crtc_vblank_get(crtc) == 0)
+ if (drm_crtc_vblank_prepare(crtc) == 0 &&
+ drm_crtc_vblank_get(crtc) == 0)
drm_crtc_arm_vblank_event(crtc, event);
else
drm_crtc_send_vblank_event(crtc, event);
diff --git a/drivers/gpu/drm/drm_vblank_work.c b/drivers/gpu/drm/drm_vblank_work.c
index 70f0199251ea0..252f60007781b 100644
--- a/drivers/gpu/drm/drm_vblank_work.c
+++ b/drivers/gpu/drm/drm_vblank_work.c
@@ -113,11 +113,19 @@ int drm_vblank_work_schedule(struct drm_vblank_work *work,
{
struct drm_vblank_crtc *vblank = work->vblank;
struct drm_device *dev = vblank->dev;
+ struct drm_crtc *crtc;
u64 cur_vbl;
unsigned long irqflags;
bool passed, inmodeset, rescheduling = false, wake = false;
int ret = 0;
+ crtc = drm_crtc_from_index(dev, vblank->pipe);
+ if (crtc) {
+ ret = drm_crtc_vblank_prepare(crtc);
+ if (ret)
+ return ret;
+ }
+
spin_lock_irqsave(&dev->event_lock, irqflags);
if (work->cancelling)
goto out;
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 66278ffeebd68..e5cf232d604c9 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -871,6 +871,27 @@ struct drm_crtc_funcs {
*/
u32 (*get_vblank_counter)(struct drm_crtc *crtc);
+ /**
+ * @prepare_enable_vblank:
+ *
+ * An optional callback to prepare driver for enabling of vblank
+ * interrupts. It allows drivers to perform any blocking operations for
+ * hardware setup that might be needed, and thus is called before any
+ * vblank spinlocks are acquired. It is called unconditionally,
+ * regardless of whether vblank interrupts are already enabled or not.
+ *
+ * Consequently, this callback is not synchronized with the rest of
+ * vblank management. Drivers should not access spinlock protected
+ * states here.
+ *
+ * This callback is optional. If not set, no preparation is performed.
+ *
+ * Returns:
+ *
+ * Zero on success, negative errno on failure.
+ */
+ int (*prepare_enable_vblank)(struct drm_crtc *crtc);
+
/**
* @enable_vblank:
*
diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h
index 2fcef9c0f5b1b..c91384ee2617b 100644
--- a/include/drm/drm_vblank.h
+++ b/include/drm/drm_vblank.h
@@ -301,6 +301,7 @@ void drm_vblank_set_event(struct drm_pending_vblank_event *e,
bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe);
bool drm_crtc_handle_vblank(struct drm_crtc *crtc);
int drm_crtc_vblank_get(struct drm_crtc *crtc);
+int drm_crtc_vblank_prepare_and_get(struct drm_crtc *crtc);
void drm_crtc_vblank_put(struct drm_crtc *crtc);
int drm_crtc_wait_one_vblank(struct drm_crtc *crtc);
void drm_crtc_vblank_off(struct drm_crtc *crtc);
--
2.52.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH v3 2/2] drm/amd/display: Implement prepare_vblank_enable callback
2026-01-09 19:20 [PATCH v3 1/2] drm: Introduce drm_crtc_vblank_prepare() sunpeng.li
@ 2026-01-09 19:20 ` sunpeng.li
2026-01-16 13:52 ` kernel test robot
2026-01-16 15:37 ` kernel test robot
2026-01-09 19:24 ` [PATCH v3 1/2] drm: Introduce drm_crtc_vblank_prepare() Leo Li
2026-01-16 14:03 ` kernel test robot
2 siblings, 2 replies; 6+ messages in thread
From: sunpeng.li @ 2026-01-09 19:20 UTC (permalink / raw)
To: amd-gfx, dri-devel
Cc: Harry.Wentland, simona, airlied, jani.nikula, ville.syrjala,
Leo Li
From: Leo Li <sunpeng.li@amd.com>
[Why]
APU DCN generations since DCN3.5 have the capability to power down
almost all of the DCN hw block during idle periods. This is referred to
as IPS -- idle power states. In combination with a panel remote-buffer
feature (like PSR or Panel Replay), IPS can save additional power.
Once DCN is in an IPS, no register access can occur. This includes
control registers for vblank interrupts; IPS must first be exited.
Transitioning in or out of IPS requires synchronization with the rest of
DC, as it powers up or down DCN, and may communicate with other MCUs on
the SOC to do so. This is done via the dc_lock mutex.
While calling enable_vblank, the DRM vblank core holds spinlocks that
prevent blocking operations. Yet acquiring the dc_lock mutex is
blocking. Thus, IPS can not be exited piror to programming vblank
interrupt registers from within enable_vblank. At least not in a
race-free way.
Prior to this change, amdgpu_dm was exiting IPS(*) without holding the
dc_lock, opening the door for races:
https://gitlab.freedesktop.org/drm/amd/-/issues/5233
(*) From touching the interrupt registers. All register reads today have
an implicit IPS exit, see dm_read_reg_func()
To solve this, the prepare_vblank_enable callback can be implemented to
exit IPS, as it is called from process context.
[How]
Implement the prepare_vblank_enable callback for amdgpu_dm. In it,
the dc_lock mutex is acquired, and IPS is exited.
Note that the only place that should unconditionally IPS allow is the
vblank disable path. All other paths shall check whether IPS was
previously allowed. If so, they can re-allow after all programming is
complete. They also need to hold the dc_lock for the duration of the IPS
disallow to re-allow. (This is not the for all of amdgpu_dm today,
cleanup will come in future patches.)
v2: Add missing semicolon, add docstring for prepare_vbl_disallow_idle
v3: Do prepare work (IPS exit) directly, instead of routing through DRM
Signed-off-by: Leo Li <sunpeng.li@amd.com>
---
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 37 ++++++++++++------
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 9 +++++
.../drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c | 6 ++-
.../amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 38 +++++++++++++++++--
4 files changed, 75 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 740711ac1037c..d0c412260be0c 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -9681,7 +9681,8 @@ static void update_stream_irq_parameters(
spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
}
-static void amdgpu_dm_handle_vrr_transition(struct dm_crtc_state *old_state,
+static void amdgpu_dm_handle_vrr_transition(struct amdgpu_display_manager *dm,
+ struct dm_crtc_state *old_state,
struct dm_crtc_state *new_state)
{
bool old_vrr_active = amdgpu_dm_crtc_vrr_active(old_state);
@@ -9696,8 +9697,11 @@ static void amdgpu_dm_handle_vrr_transition(struct dm_crtc_state *old_state,
* We also need vupdate irq for the actual core vblank handling
* at end of vblank.
*/
- WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0);
- WARN_ON(drm_crtc_vblank_get(new_state->base.crtc) != 0);
+ scoped_guard(mutex, &dm->dc_lock) {
+ dc_exit_ips_for_hw_access(dm->dc);
+ WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0);
+ WARN_ON(drm_crtc_vblank_get(new_state->base.crtc) != 0);
+ }
drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR off->on: Get vblank ref\n",
__func__, new_state->base.crtc->base.id);
} else if (old_vrr_active && !new_vrr_active) {
@@ -10122,7 +10126,11 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
*/
if (acrtc_attach->base.state->event &&
acrtc_state->active_planes > 0) {
- drm_crtc_vblank_get(pcrtc);
+
+ scoped_guard(mutex, &dm->dc_lock) {
+ dc_exit_ips_for_hw_access(dm->dc);
+ drm_crtc_vblank_get(pcrtc);
+ }
spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
@@ -10138,13 +10146,19 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
&acrtc_state->stream->vrr_infopacket;
}
} else if (cursor_update && acrtc_state->active_planes > 0) {
- spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
- if (acrtc_attach->base.state->event) {
- drm_crtc_vblank_get(pcrtc);
- acrtc_attach->event = acrtc_attach->base.state->event;
- acrtc_attach->base.state->event = NULL;
+
+ scoped_guard(mutex, &dm->dc_lock) {
+ dc_exit_ips_for_hw_access(dm->dc);
+
+ spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
+ if (acrtc_attach->base.state->event) {
+ drm_crtc_vblank_get(pcrtc);
+ acrtc_attach->event =
+ acrtc_attach->base.state->event;
+ acrtc_attach->base.state->event = NULL;
+ }
+ spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
}
- spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
}
/* Update the planes if changed or disable if we don't have any. */
@@ -10976,7 +10990,8 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
manage_dm_interrupts(adev, acrtc, dm_new_crtc_state);
}
/* Handle vrr on->off / off->on transitions */
- amdgpu_dm_handle_vrr_transition(dm_old_crtc_state, dm_new_crtc_state);
+ amdgpu_dm_handle_vrr_transition(dm, dm_old_crtc_state,
+ dm_new_crtc_state);
#ifdef CONFIG_DEBUG_FS
if (new_crtc_state->active &&
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
index bd0403005f370..b2fbdaa7c5c9c 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -585,6 +585,15 @@ struct amdgpu_display_manager {
*/
uint32_t active_vblank_irq_count;
+ /**
+ * @prepare_vbl_disallow_idle:
+ *
+ * Set to true when idle has been disallowed. Set to false when vblank
+ * interrupts have been enabled. i.e. idle re-allow on vblank disable is
+ * blocked if this is true.
+ */
+ bool prepare_vbl_disallow_idle;
+
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
/**
* @secure_display_ctx:
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c
index e20aa74380665..4477a0212893c 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c
@@ -656,7 +656,11 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name)
*/
enabled = amdgpu_dm_is_valid_crc_source(cur_crc_src);
if (!enabled && enable) {
- ret = drm_crtc_vblank_get(crtc);
+ scoped_guard(mutex, &dm->dc_lock) {
+ dc_exit_ips_for_hw_access(dm->dc);
+ ret = drm_crtc_vblank_get(crtc);
+ }
+
if (ret)
goto cleanup;
}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
index 697e232acebfb..5edc035ec152a 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
@@ -258,8 +258,8 @@ static void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work)
else if (dm->active_vblank_irq_count)
dm->active_vblank_irq_count--;
- if (dm->active_vblank_irq_count > 0)
- dc_allow_idle_optimizations(dm->dc, false);
+ /* prepare_vblank_enable must disallow idle first */
+ ASSERT(dm->dc->idle_optimizations_allowed == false);
/*
* Control PSR based on vblank requirements from OS
@@ -277,7 +277,13 @@ static void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work)
vblank_work->acrtc->dm_irq_params.allow_sr_entry);
}
- if (dm->active_vblank_irq_count == 0) {
+ /*
+ * If this worker runs disable between prepare_vblank and enable_vblank,
+ * we need to block idle re-allow. Leave it to the next vblank disable
+ * to re-allow idle.
+ */
+ if (dm->active_vblank_irq_count == 0 &&
+ !READ_ONCE(dm->prepare_vbl_disallow_idle)) {
dc_post_update_surfaces_to_stream(dm->dc);
r = amdgpu_dpm_pause_power_profile(adev, true);
@@ -308,6 +314,8 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
int irq_type;
int rc = 0;
+ ASSERT(dm->dc->idle_optimizations_allowed == false);
+
if (enable && !acrtc->base.enabled) {
drm_dbg_vbl(crtc->dev,
"Reject vblank enable on unconfigured CRTC %d (enabled=%d)\n",
@@ -399,6 +407,9 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
}
#endif
+ /* Ensure compiler emits the write before worker is queued */
+ WRITE_ONCE(dm->prepare_vbl_disallow_idle, false);
+
if (amdgpu_in_reset(adev))
return 0;
@@ -423,6 +434,26 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
return 0;
}
+static int amdgpu_prepare_enable_vblank(struct drm_crtc *crtc)
+{
+ struct amdgpu_device *adev = drm_to_adev(crtc->dev);
+ struct amdgpu_display_manager *dm = &adev->dm;
+
+ guard(mutex)(&adev->dm.dc_lock);
+
+ if (dm->dc->idle_optimizations_allowed) {
+ /*
+ * Prevent the disable worker from re-allowing idle until
+ * interrupts are enabled. Ensure compiler emits the write
+ * before disallowing idle.
+ */
+ WRITE_ONCE(dm->prepare_vbl_disallow_idle, true);
+ dc_exit_ips_for_hw_access(dm->dc);
+ }
+
+ return 0;
+}
+
int amdgpu_dm_crtc_enable_vblank(struct drm_crtc *crtc)
{
return amdgpu_dm_crtc_set_vblank(crtc, true);
@@ -590,6 +621,7 @@ static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = {
.verify_crc_source = amdgpu_dm_crtc_verify_crc_source,
.get_crc_sources = amdgpu_dm_crtc_get_crc_sources,
.get_vblank_counter = amdgpu_get_vblank_counter_kms,
+ .prepare_enable_vblank = amdgpu_prepare_enable_vblank,
.enable_vblank = amdgpu_dm_crtc_enable_vblank,
.disable_vblank = amdgpu_dm_crtc_disable_vblank,
.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
--
2.52.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH v3 2/2] drm/amd/display: Implement prepare_vblank_enable callback
2026-01-09 19:20 ` [PATCH v3 2/2] drm/amd/display: Implement prepare_vblank_enable callback sunpeng.li
@ 2026-01-16 13:52 ` kernel test robot
2026-01-16 15:37 ` kernel test robot
1 sibling, 0 replies; 6+ messages in thread
From: kernel test robot @ 2026-01-16 13:52 UTC (permalink / raw)
To: sunpeng.li, amd-gfx, dri-devel
Cc: oe-kbuild-all, Harry.Wentland, simona, airlied, jani.nikula,
ville.syrjala, Leo Li
Hi,
kernel test robot noticed the following build errors:
[auto build test ERROR on drm-misc/drm-misc-next]
[also build test ERROR on daeinki-drm-exynos/exynos-drm-next drm/drm-next drm-tip/drm-tip next-20260115]
[cannot apply to drm-i915/for-linux-next drm-i915/for-linux-next-fixes linus/master v6.19-rc5]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/sunpeng-li-amd-com/drm-amd-display-Implement-prepare_vblank_enable-callback/20260110-032355
base: https://gitlab.freedesktop.org/drm/misc/kernel.git drm-misc-next
patch link: https://lore.kernel.org/r/20260109192027.116325-2-sunpeng.li%40amd.com
patch subject: [PATCH v3 2/2] drm/amd/display: Implement prepare_vblank_enable callback
config: csky-allmodconfig (https://download.01.org/0day-ci/archive/20260116/202601162111.x5OKj7WN-lkp@intel.com/config)
compiler: csky-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260116/202601162111.x5OKj7WN-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601162111.x5OKj7WN-lkp@intel.com/
All errors (new ones prefixed by >>):
In file included from include/linux/preempt.h:11,
from include/linux/spinlock.h:56,
from include/drm/drm_crtc.h:28,
from drivers/gpu/drm/amd/amdgpu/../display/amdgpu_dm/amdgpu_dm_crc.c:27:
drivers/gpu/drm/amd/amdgpu/../display/amdgpu_dm/amdgpu_dm_crc.c: In function 'amdgpu_dm_crtc_set_crc_source':
>> drivers/gpu/drm/amd/amdgpu/../display/amdgpu_dm/amdgpu_dm_crc.c:659:38: error: 'dm' undeclared (first use in this function); did you mean 'dc'?
659 | scoped_guard(mutex, &dm->dc_lock) {
| ^~
include/linux/cleanup.h:433:34: note: in definition of macro '__scoped_guard'
433 | for (CLASS(_name, scope)(args); \
| ^~~~
drivers/gpu/drm/amd/amdgpu/../display/amdgpu_dm/amdgpu_dm_crc.c:659:17: note: in expansion of macro 'scoped_guard'
659 | scoped_guard(mutex, &dm->dc_lock) {
| ^~~~~~~~~~~~
drivers/gpu/drm/amd/amdgpu/../display/amdgpu_dm/amdgpu_dm_crc.c:659:38: note: each undeclared identifier is reported only once for each function it appears in
659 | scoped_guard(mutex, &dm->dc_lock) {
| ^~
include/linux/cleanup.h:433:34: note: in definition of macro '__scoped_guard'
433 | for (CLASS(_name, scope)(args); \
| ^~~~
drivers/gpu/drm/amd/amdgpu/../display/amdgpu_dm/amdgpu_dm_crc.c:659:17: note: in expansion of macro 'scoped_guard'
659 | scoped_guard(mutex, &dm->dc_lock) {
| ^~~~~~~~~~~~
vim +659 drivers/gpu/drm/amd/amdgpu/../display/amdgpu_dm/amdgpu_dm_crc.c
542
543 int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name)
544 {
545 enum amdgpu_dm_pipe_crc_source source = dm_parse_crc_source(src_name);
546 enum amdgpu_dm_pipe_crc_source cur_crc_src;
547 struct drm_crtc_commit *commit;
548 struct dm_crtc_state *crtc_state;
549 struct drm_device *drm_dev = crtc->dev;
550 #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
551 struct amdgpu_device *adev = drm_to_adev(drm_dev);
552 struct amdgpu_display_manager *dm = &adev->dm;
553 #endif
554 struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
555 struct drm_dp_aux *aux = NULL;
556 bool enable = false;
557 bool enabled = false;
558 int ret = 0;
559
560 if (source < 0) {
561 DRM_DEBUG_DRIVER("Unknown CRC source %s for CRTC%d\n",
562 src_name, crtc->index);
563 return -EINVAL;
564 }
565
566 ret = drm_modeset_lock(&crtc->mutex, NULL);
567 if (ret)
568 return ret;
569
570 spin_lock(&crtc->commit_lock);
571 commit = list_first_entry_or_null(&crtc->commit_list,
572 struct drm_crtc_commit, commit_entry);
573 if (commit)
574 drm_crtc_commit_get(commit);
575 spin_unlock(&crtc->commit_lock);
576
577 if (commit) {
578 /*
579 * Need to wait for all outstanding programming to complete
580 * in commit tail since it can modify CRC related fields and
581 * hardware state. Since we're holding the CRTC lock we're
582 * guaranteed that no other commit work can be queued off
583 * before we modify the state below.
584 */
585 ret = wait_for_completion_interruptible_timeout(
586 &commit->hw_done, 10 * HZ);
587 if (ret)
588 goto cleanup;
589 }
590
591 enable = amdgpu_dm_is_valid_crc_source(source);
592 crtc_state = to_dm_crtc_state(crtc->state);
593 spin_lock_irq(&drm_dev->event_lock);
594 cur_crc_src = acrtc->dm_irq_params.crc_src;
595 spin_unlock_irq(&drm_dev->event_lock);
596
597 /*
598 * USER REQ SRC | CURRENT SRC | BEHAVIOR
599 * -----------------------------
600 * None | None | Do nothing
601 * None | CRTC | Disable CRTC CRC, set default to dither
602 * None | DPRX | Disable DPRX CRC, need 'aux', set default to dither
603 * None | CRTC DITHER | Disable CRTC CRC
604 * None | DPRX DITHER | Disable DPRX CRC, need 'aux'
605 * CRTC | XXXX | Enable CRTC CRC, no dither
606 * DPRX | XXXX | Enable DPRX CRC, need 'aux', no dither
607 * CRTC DITHER | XXXX | Enable CRTC CRC, set dither
608 * DPRX DITHER | XXXX | Enable DPRX CRC, need 'aux', set dither
609 */
610 if (dm_is_crc_source_dprx(source) ||
611 (source == AMDGPU_DM_PIPE_CRC_SOURCE_NONE &&
612 dm_is_crc_source_dprx(cur_crc_src))) {
613 struct amdgpu_dm_connector *aconn = NULL;
614 struct drm_connector *connector;
615 struct drm_connector_list_iter conn_iter;
616
617 drm_connector_list_iter_begin(crtc->dev, &conn_iter);
618 drm_for_each_connector_iter(connector, &conn_iter) {
619 if (!connector->state || connector->state->crtc != crtc)
620 continue;
621
622 if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
623 continue;
624
625 aconn = to_amdgpu_dm_connector(connector);
626 break;
627 }
628 drm_connector_list_iter_end(&conn_iter);
629
630 if (!aconn) {
631 DRM_DEBUG_DRIVER("No amd connector matching CRTC-%d\n", crtc->index);
632 ret = -EINVAL;
633 goto cleanup;
634 }
635
636 aux = (aconn->mst_output_port) ? &aconn->mst_output_port->aux : &aconn->dm_dp_aux.aux;
637
638 if (!aux) {
639 DRM_DEBUG_DRIVER("No dp aux for amd connector\n");
640 ret = -EINVAL;
641 goto cleanup;
642 }
643
644 if ((aconn->base.connector_type != DRM_MODE_CONNECTOR_DisplayPort) &&
645 (aconn->base.connector_type != DRM_MODE_CONNECTOR_eDP)) {
646 DRM_DEBUG_DRIVER("No DP connector available for CRC source\n");
647 ret = -EINVAL;
648 goto cleanup;
649 }
650
651 }
652
653 /*
654 * Reading the CRC requires the vblank interrupt handler to be
655 * enabled. Keep a reference until CRC capture stops.
656 */
657 enabled = amdgpu_dm_is_valid_crc_source(cur_crc_src);
658 if (!enabled && enable) {
> 659 scoped_guard(mutex, &dm->dc_lock) {
660 dc_exit_ips_for_hw_access(dm->dc);
661 ret = drm_crtc_vblank_get(crtc);
662 }
663
664 if (ret)
665 goto cleanup;
666 }
667
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH v3 2/2] drm/amd/display: Implement prepare_vblank_enable callback
2026-01-09 19:20 ` [PATCH v3 2/2] drm/amd/display: Implement prepare_vblank_enable callback sunpeng.li
2026-01-16 13:52 ` kernel test robot
@ 2026-01-16 15:37 ` kernel test robot
1 sibling, 0 replies; 6+ messages in thread
From: kernel test robot @ 2026-01-16 15:37 UTC (permalink / raw)
To: sunpeng.li, amd-gfx, dri-devel
Cc: llvm, oe-kbuild-all, Harry.Wentland, simona, airlied, jani.nikula,
ville.syrjala, Leo Li
Hi,
kernel test robot noticed the following build errors:
[auto build test ERROR on drm-misc/drm-misc-next]
[also build test ERROR on daeinki-drm-exynos/exynos-drm-next drm/drm-next drm-tip/drm-tip next-20260115]
[cannot apply to drm-i915/for-linux-next drm-i915/for-linux-next-fixes linus/master v6.19-rc5]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/sunpeng-li-amd-com/drm-amd-display-Implement-prepare_vblank_enable-callback/20260110-032355
base: https://gitlab.freedesktop.org/drm/misc/kernel.git drm-misc-next
patch link: https://lore.kernel.org/r/20260109192027.116325-2-sunpeng.li%40amd.com
patch subject: [PATCH v3 2/2] drm/amd/display: Implement prepare_vblank_enable callback
config: x86_64-rhel-9.4-rust (https://download.01.org/0day-ci/archive/20260116/202601162358.Uv6IL5Jo-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
rustc: rustc 1.88.0 (6b00bc388 2025-06-23)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260116/202601162358.Uv6IL5Jo-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601162358.Uv6IL5Jo-lkp@intel.com/
All errors (new ones prefixed by >>):
>> drivers/gpu/drm/amd/amdgpu/../display/amdgpu_dm/amdgpu_dm_crc.c:659:24: error: use of undeclared identifier 'dm'
659 | scoped_guard(mutex, &dm->dc_lock) {
| ^
drivers/gpu/drm/amd/amdgpu/../display/amdgpu_dm/amdgpu_dm_crc.c:660:30: error: use of undeclared identifier 'dm'
660 | dc_exit_ips_for_hw_access(dm->dc);
| ^
2 errors generated.
vim +/dm +659 drivers/gpu/drm/amd/amdgpu/../display/amdgpu_dm/amdgpu_dm_crc.c
542
543 int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name)
544 {
545 enum amdgpu_dm_pipe_crc_source source = dm_parse_crc_source(src_name);
546 enum amdgpu_dm_pipe_crc_source cur_crc_src;
547 struct drm_crtc_commit *commit;
548 struct dm_crtc_state *crtc_state;
549 struct drm_device *drm_dev = crtc->dev;
550 #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
551 struct amdgpu_device *adev = drm_to_adev(drm_dev);
552 struct amdgpu_display_manager *dm = &adev->dm;
553 #endif
554 struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
555 struct drm_dp_aux *aux = NULL;
556 bool enable = false;
557 bool enabled = false;
558 int ret = 0;
559
560 if (source < 0) {
561 DRM_DEBUG_DRIVER("Unknown CRC source %s for CRTC%d\n",
562 src_name, crtc->index);
563 return -EINVAL;
564 }
565
566 ret = drm_modeset_lock(&crtc->mutex, NULL);
567 if (ret)
568 return ret;
569
570 spin_lock(&crtc->commit_lock);
571 commit = list_first_entry_or_null(&crtc->commit_list,
572 struct drm_crtc_commit, commit_entry);
573 if (commit)
574 drm_crtc_commit_get(commit);
575 spin_unlock(&crtc->commit_lock);
576
577 if (commit) {
578 /*
579 * Need to wait for all outstanding programming to complete
580 * in commit tail since it can modify CRC related fields and
581 * hardware state. Since we're holding the CRTC lock we're
582 * guaranteed that no other commit work can be queued off
583 * before we modify the state below.
584 */
585 ret = wait_for_completion_interruptible_timeout(
586 &commit->hw_done, 10 * HZ);
587 if (ret)
588 goto cleanup;
589 }
590
591 enable = amdgpu_dm_is_valid_crc_source(source);
592 crtc_state = to_dm_crtc_state(crtc->state);
593 spin_lock_irq(&drm_dev->event_lock);
594 cur_crc_src = acrtc->dm_irq_params.crc_src;
595 spin_unlock_irq(&drm_dev->event_lock);
596
597 /*
598 * USER REQ SRC | CURRENT SRC | BEHAVIOR
599 * -----------------------------
600 * None | None | Do nothing
601 * None | CRTC | Disable CRTC CRC, set default to dither
602 * None | DPRX | Disable DPRX CRC, need 'aux', set default to dither
603 * None | CRTC DITHER | Disable CRTC CRC
604 * None | DPRX DITHER | Disable DPRX CRC, need 'aux'
605 * CRTC | XXXX | Enable CRTC CRC, no dither
606 * DPRX | XXXX | Enable DPRX CRC, need 'aux', no dither
607 * CRTC DITHER | XXXX | Enable CRTC CRC, set dither
608 * DPRX DITHER | XXXX | Enable DPRX CRC, need 'aux', set dither
609 */
610 if (dm_is_crc_source_dprx(source) ||
611 (source == AMDGPU_DM_PIPE_CRC_SOURCE_NONE &&
612 dm_is_crc_source_dprx(cur_crc_src))) {
613 struct amdgpu_dm_connector *aconn = NULL;
614 struct drm_connector *connector;
615 struct drm_connector_list_iter conn_iter;
616
617 drm_connector_list_iter_begin(crtc->dev, &conn_iter);
618 drm_for_each_connector_iter(connector, &conn_iter) {
619 if (!connector->state || connector->state->crtc != crtc)
620 continue;
621
622 if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
623 continue;
624
625 aconn = to_amdgpu_dm_connector(connector);
626 break;
627 }
628 drm_connector_list_iter_end(&conn_iter);
629
630 if (!aconn) {
631 DRM_DEBUG_DRIVER("No amd connector matching CRTC-%d\n", crtc->index);
632 ret = -EINVAL;
633 goto cleanup;
634 }
635
636 aux = (aconn->mst_output_port) ? &aconn->mst_output_port->aux : &aconn->dm_dp_aux.aux;
637
638 if (!aux) {
639 DRM_DEBUG_DRIVER("No dp aux for amd connector\n");
640 ret = -EINVAL;
641 goto cleanup;
642 }
643
644 if ((aconn->base.connector_type != DRM_MODE_CONNECTOR_DisplayPort) &&
645 (aconn->base.connector_type != DRM_MODE_CONNECTOR_eDP)) {
646 DRM_DEBUG_DRIVER("No DP connector available for CRC source\n");
647 ret = -EINVAL;
648 goto cleanup;
649 }
650
651 }
652
653 /*
654 * Reading the CRC requires the vblank interrupt handler to be
655 * enabled. Keep a reference until CRC capture stops.
656 */
657 enabled = amdgpu_dm_is_valid_crc_source(cur_crc_src);
658 if (!enabled && enable) {
> 659 scoped_guard(mutex, &dm->dc_lock) {
660 dc_exit_ips_for_hw_access(dm->dc);
661 ret = drm_crtc_vblank_get(crtc);
662 }
663
664 if (ret)
665 goto cleanup;
666 }
667
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3 1/2] drm: Introduce drm_crtc_vblank_prepare()
2026-01-09 19:20 [PATCH v3 1/2] drm: Introduce drm_crtc_vblank_prepare() sunpeng.li
2026-01-09 19:20 ` [PATCH v3 2/2] drm/amd/display: Implement prepare_vblank_enable callback sunpeng.li
@ 2026-01-09 19:24 ` Leo Li
2026-01-16 14:03 ` kernel test robot
2 siblings, 0 replies; 6+ messages in thread
From: Leo Li @ 2026-01-09 19:24 UTC (permalink / raw)
To: amd-gfx, dri-devel
Cc: Harry.Wentland, simona, airlied, jani.nikula, ville.syrjala
On 2026-01-09 14:20, sunpeng.li@amd.com wrote:
> From: Leo Li <sunpeng.li@amd.com>
>
> Some drivers need to perform sleepable operations prior to enabling
> vblank interrupts. A display hardware spin-up from a low-power state
> that requires synchronization with the rest of the driver, for example.
>
> To support this, introduce a DRM-internal drm_crtc_vblank_prepare()
> helper that calls back into the driver -- if implemented -- for DRM to
> do such preparation work before enabling vblank.
>
> v3:
> * Unexport drm_crtc_vblank_prepare() and make it DRM internal
> * Drop warnings in drm core for vblank_prepare(), drivers can do so in
> their implementations
> * Drop unnecessary crtc null checks
> * Check for drm_dev_has_vblank()
> * Rebase on latest drm-misc-next
>
> Signed-off-by: Leo Li <sunpeng.li@amd.com>
> ---
> drivers/gpu/drm/drm_atomic_helper.c | 9 ++++++
> drivers/gpu/drm/drm_client_modeset.c | 4 +++
> drivers/gpu/drm/drm_internal.h | 1 +
> drivers/gpu/drm/drm_plane.c | 5 +++
> drivers/gpu/drm/drm_vblank.c | 48 ++++++++++++++++++++++++++++
> drivers/gpu/drm/drm_vblank_helper.c | 5 ++-
> drivers/gpu/drm/drm_vblank_work.c | 8 +++++
> include/drm/drm_crtc.h | 21 ++++++++++++
> include/drm/drm_vblank.h | 1 +
> 9 files changed, 101 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 5840e9cc6f666..2b9fa4aa48a1a 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -48,6 +48,7 @@
>
> #include "drm_crtc_helper_internal.h"
> #include "drm_crtc_internal.h"
> +#include "drm_internal.h"
>
> /**
> * DOC: overview
> @@ -1268,6 +1269,10 @@ crtc_disable(struct drm_device *dev, struct drm_atomic_state *state)
> if (!drm_dev_has_vblank(dev))
> continue;
>
> + ret = drm_crtc_vblank_prepare(crtc);
> + if (ret)
> + continue;
> +
> ret = drm_crtc_vblank_get(crtc);
> /*
> * Self-refresh is not a true "disable"; ensure vblank remains
> @@ -1823,6 +1828,10 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
> if (!new_crtc_state->active)
> continue;
>
> + ret = drm_crtc_vblank_prepare(crtc);
> + if (ret != 0)
> + continue;
> +
> ret = drm_crtc_vblank_get(crtc);
> if (ret != 0)
> continue;
> diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c
> index fc4caf7da5fcd..6ccbde921dde4 100644
> --- a/drivers/gpu/drm/drm_client_modeset.c
> +++ b/drivers/gpu/drm/drm_client_modeset.c
> @@ -1325,6 +1325,10 @@ int drm_client_modeset_wait_for_vblank(struct drm_client_dev *client, unsigned i
> * Only wait for a vblank event if the CRTC is enabled, otherwise
> * just don't do anything, not even report an error.
> */
> + ret = drm_crtc_vblank_prepare(crtc);
> + if (ret)
> + return ret;
> +
> ret = drm_crtc_vblank_get(crtc);
> if (!ret) {
> drm_crtc_wait_one_vblank(crtc);
> diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
> index f893b1e3a596e..8e3e21d734075 100644
> --- a/drivers/gpu/drm/drm_internal.h
> +++ b/drivers/gpu/drm/drm_internal.h
> @@ -112,6 +112,7 @@ static inline bool drm_vblank_passed(u64 seq, u64 ref)
> }
>
> void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe);
> +int drm_crtc_vblank_prepare(struct drm_crtc *crtc);
> int drm_vblank_get(struct drm_device *dev, unsigned int pipe);
> void drm_vblank_put(struct drm_device *dev, unsigned int pipe);
> u64 drm_vblank_count(struct drm_device *dev, unsigned int pipe);
> diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c
> index bed2562bf911b..41681a3d96b15 100644
> --- a/drivers/gpu/drm/drm_plane.c
> +++ b/drivers/gpu/drm/drm_plane.c
> @@ -35,6 +35,7 @@
> #include <drm/drm_vblank.h>
>
> #include "drm_crtc_internal.h"
> +#include "drm_internal.h"
>
> /**
> * DOC: overview
> @@ -1421,6 +1422,10 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
> u32 current_vblank;
> int r;
>
> + r = drm_crtc_vblank_prepare(crtc);
> + if (r)
> + return r;
> +
> r = drm_crtc_vblank_get(crtc);
> if (r)
> return r;
> diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
> index 42fe11cc139b9..b8a967a4ba7e5 100644
> --- a/drivers/gpu/drm/drm_vblank.c
> +++ b/drivers/gpu/drm/drm_vblank.c
> @@ -1208,6 +1208,32 @@ static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe)
> return ret;
> }
>
> +/**
> + * drm_crtc_vblank_prepare - prepare to enable vblank interrupts
> + *
> + * @crtc: which CRTC to prepare
> + *
> + * Some drivers may need to spin-up hardware from a low power state before
> + * enabling vblank interrupts. This function calls the prepare_enable_vblank
> + * callback, if available, to allow drivers to do that.
> + *
> + * This is a DRM-internal function, and is a thin wrapper around a driver
> + * callback. Drivers are expected to sequence their own prepare work internally.
> + *
> + * The spin-up may call sleeping functions, such as mutex_lock(). Therefore,
> + * this must be called from process context, where sleeping is allowed.
> + */
> +int drm_crtc_vblank_prepare(struct drm_crtc *crtc)
> +{
> + if (!drm_dev_has_vblank(crtc->dev))
> + return -EINVAL;
> +
> + if (crtc->funcs->prepare_enable_vblank)
> + return crtc->funcs->prepare_enable_vblank(crtc);
> +
> + return 0;
> +}
> +
> int drm_vblank_get(struct drm_device *dev, unsigned int pipe)
> {
> struct drm_vblank_crtc *vblank = drm_vblank_crtc(dev, pipe);
> @@ -1306,6 +1332,10 @@ int drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
> int ret;
> u64 last;
>
> + ret = drm_crtc_vblank_prepare(crtc);
> + if (ret)
> + return ret;
> +
> ret = drm_vblank_get(dev, pipe);
> if (drm_WARN(dev, ret, "vblank not available on crtc %i, ret=%i\n",
> pipe, ret))
> @@ -1489,6 +1519,9 @@ void drm_crtc_vblank_on_config(struct drm_crtc *crtc,
> if (drm_WARN_ON(dev, pipe >= dev->num_crtcs))
> return;
>
> + if (drm_crtc_vblank_prepare(crtc))
> + return;
> +
> spin_lock_irq(&dev->vbl_lock);
> drm_dbg_vbl(dev, "crtc %d, vblank enabled %d, inmodeset %d\n",
> pipe, vblank->enabled, vblank->inmodeset);
> @@ -1796,6 +1829,13 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
> return 0;
> }
>
> + crtc = drm_crtc_from_index(dev, vblank->pipe);
> + if (crtc) {
> + ret = drm_crtc_vblank_prepare(crtc);
> + if (ret)
> + return ret;
> + }
> +
> ret = drm_vblank_get(dev, pipe);
> if (ret) {
> drm_dbg_core(dev,
> @@ -2031,6 +2071,10 @@ int drm_crtc_get_sequence_ioctl(struct drm_device *dev, void *data,
> READ_ONCE(vblank->enabled);
>
> if (!vblank_enabled) {
> + ret = drm_crtc_vblank_prepare(crtc);
> + if (ret)
> + return ret;
> +
> ret = drm_crtc_vblank_get(crtc);
> if (ret) {
> drm_dbg_core(dev,
> @@ -2098,6 +2142,10 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data,
> if (e == NULL)
> return -ENOMEM;
>
> + ret = drm_crtc_vblank_prepare(crtc);
> + if (ret)
> + return ret;
> +
> ret = drm_crtc_vblank_get(crtc);
> if (ret) {
> drm_dbg_core(dev,
> diff --git a/drivers/gpu/drm/drm_vblank_helper.c b/drivers/gpu/drm/drm_vblank_helper.c
> index a04a6ba1b0ca0..fc5915acfa7f3 100644
> --- a/drivers/gpu/drm/drm_vblank_helper.c
> +++ b/drivers/gpu/drm/drm_vblank_helper.c
> @@ -8,6 +8,8 @@
> #include <drm/drm_vblank.h>
> #include <drm/drm_vblank_helper.h>
>
> +#include "drm_internal.h"
> +
> /**
> * DOC: overview
> *
> @@ -61,7 +63,8 @@ void drm_crtc_vblank_atomic_flush(struct drm_crtc *crtc,
> crtc_state->event = NULL;
>
> if (event) {
> - if (drm_crtc_vblank_get(crtc) == 0)
> + if (drm_crtc_vblank_prepare(crtc) == 0 &&
> + drm_crtc_vblank_get(crtc) == 0)
> drm_crtc_arm_vblank_event(crtc, event);
> else
> drm_crtc_send_vblank_event(crtc, event);
> diff --git a/drivers/gpu/drm/drm_vblank_work.c b/drivers/gpu/drm/drm_vblank_work.c
> index 70f0199251ea0..252f60007781b 100644
> --- a/drivers/gpu/drm/drm_vblank_work.c
> +++ b/drivers/gpu/drm/drm_vblank_work.c
> @@ -113,11 +113,19 @@ int drm_vblank_work_schedule(struct drm_vblank_work *work,
> {
> struct drm_vblank_crtc *vblank = work->vblank;
> struct drm_device *dev = vblank->dev;
> + struct drm_crtc *crtc;
> u64 cur_vbl;
> unsigned long irqflags;
> bool passed, inmodeset, rescheduling = false, wake = false;
> int ret = 0;
>
> + crtc = drm_crtc_from_index(dev, vblank->pipe);
> + if (crtc) {
> + ret = drm_crtc_vblank_prepare(crtc);
> + if (ret)
> + return ret;
> + }
> +
> spin_lock_irqsave(&dev->event_lock, irqflags);
> if (work->cancelling)
> goto out;
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 66278ffeebd68..e5cf232d604c9 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -871,6 +871,27 @@ struct drm_crtc_funcs {
> */
> u32 (*get_vblank_counter)(struct drm_crtc *crtc);
>
> + /**
> + * @prepare_enable_vblank:
> + *
> + * An optional callback to prepare driver for enabling of vblank
> + * interrupts. It allows drivers to perform any blocking operations for
> + * hardware setup that might be needed, and thus is called before any
> + * vblank spinlocks are acquired. It is called unconditionally,
> + * regardless of whether vblank interrupts are already enabled or not.
> + *
> + * Consequently, this callback is not synchronized with the rest of
> + * vblank management. Drivers should not access spinlock protected
> + * states here.
> + *
> + * This callback is optional. If not set, no preparation is performed.
> + *
> + * Returns:
> + *
> + * Zero on success, negative errno on failure.
> + */
> + int (*prepare_enable_vblank)(struct drm_crtc *crtc);
> +
> /**
> * @enable_vblank:
> *
> diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h
> index 2fcef9c0f5b1b..c91384ee2617b 100644
> --- a/include/drm/drm_vblank.h
> +++ b/include/drm/drm_vblank.h
> @@ -301,6 +301,7 @@ void drm_vblank_set_event(struct drm_pending_vblank_event *e,
> bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe);
> bool drm_crtc_handle_vblank(struct drm_crtc *crtc);
> int drm_crtc_vblank_get(struct drm_crtc *crtc);
> +int drm_crtc_vblank_prepare_and_get(struct drm_crtc *crtc);
Oops, please ignore this line -- a remenant of a previous idea that I missed cleaning up.
- Leo
> void drm_crtc_vblank_put(struct drm_crtc *crtc);
> int drm_crtc_wait_one_vblank(struct drm_crtc *crtc);
> void drm_crtc_vblank_off(struct drm_crtc *crtc);
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH v3 1/2] drm: Introduce drm_crtc_vblank_prepare()
2026-01-09 19:20 [PATCH v3 1/2] drm: Introduce drm_crtc_vblank_prepare() sunpeng.li
2026-01-09 19:20 ` [PATCH v3 2/2] drm/amd/display: Implement prepare_vblank_enable callback sunpeng.li
2026-01-09 19:24 ` [PATCH v3 1/2] drm: Introduce drm_crtc_vblank_prepare() Leo Li
@ 2026-01-16 14:03 ` kernel test robot
2 siblings, 0 replies; 6+ messages in thread
From: kernel test robot @ 2026-01-16 14:03 UTC (permalink / raw)
To: sunpeng.li, amd-gfx, dri-devel
Cc: oe-kbuild-all, Harry.Wentland, simona, airlied, jani.nikula,
ville.syrjala, Leo Li
Hi,
kernel test robot noticed the following build errors:
[auto build test ERROR on drm-misc/drm-misc-next]
[also build test ERROR on daeinki-drm-exynos/exynos-drm-next drm/drm-next drm-tip/drm-tip]
[cannot apply to drm-i915/for-linux-next drm-i915/for-linux-next-fixes linus/master v6.19-rc5]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/sunpeng-li-amd-com/drm-amd-display-Implement-prepare_vblank_enable-callback/20260110-032355
base: https://gitlab.freedesktop.org/drm/misc/kernel.git drm-misc-next
patch link: https://lore.kernel.org/r/20260109192027.116325-1-sunpeng.li%40amd.com
patch subject: [PATCH v3 1/2] drm: Introduce drm_crtc_vblank_prepare()
config: nios2-allmodconfig (https://download.01.org/0day-ci/archive/20260116/202601162107.idPiWG44-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 11.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260116/202601162107.idPiWG44-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601162107.idPiWG44-lkp@intel.com/
All errors (new ones prefixed by >>, old ones prefixed by <<):
>> ERROR: modpost: "drm_crtc_vblank_prepare" [drivers/gpu/drm/drm_kms_helper.ko] undefined!
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-01-16 15:38 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-09 19:20 [PATCH v3 1/2] drm: Introduce drm_crtc_vblank_prepare() sunpeng.li
2026-01-09 19:20 ` [PATCH v3 2/2] drm/amd/display: Implement prepare_vblank_enable callback sunpeng.li
2026-01-16 13:52 ` kernel test robot
2026-01-16 15:37 ` kernel test robot
2026-01-09 19:24 ` [PATCH v3 1/2] drm: Introduce drm_crtc_vblank_prepare() Leo Li
2026-01-16 14:03 ` kernel test robot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox