[PATCH v3] drm/fb-helper: Only consider active CRTCs for vblank sync
From: Thomas Zimmermann
Date: Fri Jun 26 2026 - 03:31:07 EST
Only synchronize fbdev output to the vblank of an active CRTC. Go over
the list of CRTCs and pick the first that matches. Fixes warnings as
the one shown below
[ 77.201354] WARNING: drivers/gpu/drm/drm_vblank.c:1320 at drm_crtc_wait_one_vblank+0x194/0x1cc [drm], CPU#1: kworker/1:7/1867
[ 77.201354] omapdrm omapdrm.0: [drm] vblank wait timed out on crtc 0
This currently happens if the fbdev output is not on CRTC 0.
Atomic and non-atomic drivers require distinct code paths. As for other
fbdev operations, implement both and select the correct one at runtime.
Not finding an active CRTC is not a bug. Do not wait in this case, but
flush the display update as before.
v3:
- drop excessive state validation (Jani)
- acquire plane and CRTC mutices (Sashiko)
v2:
- move look-up code into separate helper
- support drivers with legacy modesetting
v1:
- see https://lore.kernel.org/dri-devel/1c9e0e24-9c4a-4259-8700-cf9e5fd60ca3@xxxxxxx/
Co-authored-by: H. Nikolaus Schaller <hns@xxxxxxxxxxxxx>
Signed-off-by: Thomas Zimmermann <tzimmermann@xxxxxxx>
Fixes: d8c4bddcd8bcb ("drm/fb-helper: Synchronize dirty worker with vblank")
Tested-by: Icenowy Zheng <zhengxingda@xxxxxxxxxxx>
Tested-by: H. Nikolaus Schaller <hns@xxxxxxxxxxxxx>
Closes: https://bugs.debian.org/1138033
---
drivers/gpu/drm/drm_fb_helper.c | 73 ++++++++++++++++++++++++++++++++-
1 file changed, 72 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 7b11a582f8ec..67b187f5c6bf 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -225,16 +225,87 @@ static void drm_fb_helper_resume_worker(struct work_struct *work)
console_unlock();
}
+static int find_crtc_index_atomic(struct drm_fb_helper *helper)
+{
+ struct drm_device *dev = helper->dev;
+ struct drm_plane *plane;
+ int crtc_index = -EINVAL;
+
+ drm_for_each_plane(plane, dev) {
+ const struct drm_plane_state *plane_state;
+
+ if (plane->type != DRM_PLANE_TYPE_PRIMARY)
+ continue;
+
+ drm_modeset_lock(&plane->mutex, NULL);
+ plane_state = plane->state;
+
+ if (plane_state->fb == helper->fb && plane_state->crtc) {
+ struct drm_crtc *crtc = plane_state->crtc;
+
+ drm_modeset_lock(&crtc->mutex, NULL);
+ if (crtc->state->active)
+ crtc_index = crtc->index;
+ drm_modeset_unlock(&crtc->mutex);
+ }
+ drm_modeset_unlock(&plane->mutex);
+
+ if (crtc_index >= 0)
+ break;
+ }
+
+ return crtc_index;
+}
+
+static int find_crtc_index_legacy(struct drm_fb_helper *helper)
+{
+ struct drm_device *dev = helper->dev;
+ struct drm_crtc *crtc;
+
+ drm_for_each_crtc(crtc, dev) {
+ struct drm_plane *plane = crtc->primary;
+
+ if (!crtc->enabled)
+ continue;
+ if (!plane || plane->fb != helper->fb)
+ continue; /* CRTC doesn't display fbdev emulation */
+
+ return crtc->index;
+ }
+
+ return -EINVAL;
+}
+
+static int drm_fb_helper_find_crtc_index(struct drm_fb_helper *helper)
+{
+ struct drm_device *dev = helper->dev;
+ int crtc_index;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ if (drm_drv_uses_atomic_modeset(dev))
+ crtc_index = find_crtc_index_atomic(helper);
+ else
+ crtc_index = find_crtc_index_legacy(helper);
+
+ mutex_unlock(&dev->mode_config.mutex);
+
+ return crtc_index;
+}
+
static void drm_fb_helper_fb_dirty(struct drm_fb_helper *helper)
{
struct drm_device *dev = helper->dev;
struct drm_clip_rect *clip = &helper->damage_clip;
struct drm_clip_rect clip_copy;
+ int crtc_index;
unsigned long flags;
int ret;
mutex_lock(&helper->lock);
- drm_client_modeset_wait_for_vblank(&helper->client, 0);
+ crtc_index = drm_fb_helper_find_crtc_index(helper);
+ if (crtc_index >= 0)
+ drm_client_modeset_wait_for_vblank(&helper->client, crtc_index);
mutex_unlock(&helper->lock);
if (drm_WARN_ON_ONCE(dev, !helper->funcs->fb_dirty))
--
2.54.0