]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
drm/fb-helper: Synchronize dirty worker with vblank
authorThomas Zimmermann <tzimmermann@suse.de>
Fri, 29 Aug 2025 09:13:45 +0000 (11:13 +0200)
committerThomas Zimmermann <tzimmermann@suse.de>
Tue, 16 Sep 2025 10:42:52 +0000 (12:42 +0200)
Before updating the display from the console's shadow buffer, the dirty
worker now waits for a vblank. This allows several screen updates to pile
up and acts as a rate limiter. If a DRM master is present, it could
interfere with the vblank. Don't wait in this case.

v4:
* share code with WAITFORVSYNC ioctl (Emil)
* use lock guard
v3:
* add back helper->lock
* acquire DRM master status while waiting for vblank
v2:
* don't hold helper->lock while waiting for vblank

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Acked-by: Sam Ravnborg <sam@ravnborg.org>
Link: https://lore.kernel.org/r/20250829091447.46719-1-tzimmermann@suse.de
drivers/gpu/drm/drm_client_modeset.c
drivers/gpu/drm/drm_fb_helper.c
include/drm/drm_client.h

index 9c2c3b0c8c470ee7727547daf83bd3bfda96764c..fc4caf7da5fcd3ffcff2af90d4bb6d79793a8f32 100644 (file)
@@ -1293,6 +1293,50 @@ int drm_client_modeset_dpms(struct drm_client_dev *client, int mode)
 }
 EXPORT_SYMBOL(drm_client_modeset_dpms);
 
+/**
+ * drm_client_modeset_wait_for_vblank() - Wait for the next VBLANK to occur
+ * @client: DRM client
+ * @crtc_index: The ndex of the CRTC to wait on
+ *
+ * Block the caller until the given CRTC has seen a VBLANK. Do nothing
+ * if the CRTC is disabled. If there's another DRM master present, fail
+ * with -EBUSY.
+ *
+ * Returns:
+ * 0 on success, or negative error code otherwise.
+ */
+int drm_client_modeset_wait_for_vblank(struct drm_client_dev *client, unsigned int crtc_index)
+{
+       struct drm_device *dev = client->dev;
+       struct drm_crtc *crtc;
+       int ret;
+
+       /*
+        * Rate-limit update frequency to vblank. If there's a DRM master
+        * present, it could interfere while we're waiting for the vblank
+        * event. Don't wait in this case.
+        */
+       if (!drm_master_internal_acquire(dev))
+               return -EBUSY;
+
+       crtc = client->modesets[crtc_index].crtc;
+
+       /*
+        * 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_get(crtc);
+       if (!ret) {
+               drm_crtc_wait_one_vblank(crtc);
+               drm_crtc_vblank_put(crtc);
+       }
+
+       drm_master_internal_release(dev);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_client_modeset_wait_for_vblank);
+
 #ifdef CONFIG_DRM_KUNIT_TEST
 #include "tests/drm_client_modeset_test.c"
 #endif
index 11a5b60cb9ce45deac54374bb32ecfd3619f1559..53e9dc0543de856452d96b376097461c8f26bf16 100644 (file)
@@ -368,6 +368,10 @@ static void drm_fb_helper_fb_dirty(struct drm_fb_helper *helper)
        unsigned long flags;
        int ret;
 
+       mutex_lock(&helper->lock);
+       drm_client_modeset_wait_for_vblank(&helper->client, 0);
+       mutex_unlock(&helper->lock);
+
        if (drm_WARN_ON_ONCE(dev, !helper->funcs->fb_dirty))
                return;
 
@@ -1068,15 +1072,9 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,
                        unsigned long arg)
 {
        struct drm_fb_helper *fb_helper = info->par;
-       struct drm_device *dev = fb_helper->dev;
-       struct drm_crtc *crtc;
        int ret = 0;
 
-       mutex_lock(&fb_helper->lock);
-       if (!drm_master_internal_acquire(dev)) {
-               ret = -EBUSY;
-               goto unlock;
-       }
+       guard(mutex)(&fb_helper->lock);
 
        switch (cmd) {
        case FBIO_WAITFORVSYNC:
@@ -1096,28 +1094,12 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,
                 * make. If we're not smart enough here, one should
                 * just consider switch the userspace to KMS.
                 */
-               crtc = fb_helper->client.modesets[0].crtc;
-
-               /*
-                * Only wait for a vblank event if the CRTC is
-                * enabled, otherwise just don't do anythintg,
-                * not even report an error.
-                */
-               ret = drm_crtc_vblank_get(crtc);
-               if (!ret) {
-                       drm_crtc_wait_one_vblank(crtc);
-                       drm_crtc_vblank_put(crtc);
-               }
-
-               ret = 0;
+               ret = drm_client_modeset_wait_for_vblank(&fb_helper->client, 0);
                break;
        default:
                ret = -ENOTTY;
        }
 
-       drm_master_internal_release(dev);
-unlock:
-       mutex_unlock(&fb_helper->lock);
        return ret;
 }
 EXPORT_SYMBOL(drm_fb_helper_ioctl);
index 146ca80e35db682ef6214d5b260b68dd63671c8c..bdd845e383ef35584726bc3da1d559a61eba0d47 100644 (file)
@@ -220,6 +220,7 @@ int drm_client_modeset_check(struct drm_client_dev *client);
 int drm_client_modeset_commit_locked(struct drm_client_dev *client);
 int drm_client_modeset_commit(struct drm_client_dev *client);
 int drm_client_modeset_dpms(struct drm_client_dev *client, int mode);
+int drm_client_modeset_wait_for_vblank(struct drm_client_dev *client, unsigned int crtc_index);
 
 /**
  * drm_client_for_each_modeset() - Iterate over client modesets