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 <tzimmerm...@suse.de> Acked-by: Sam Ravnborg <s...@ravnborg.org> --- The previous reviews of this patch can be found as part of the series at https://patchwork.freedesktop.org/series/66442/#rev3 . --- drivers/gpu/drm/drm_client_modeset.c | 44 ++++++++++++++++++++++++++++ drivers/gpu/drm/drm_fb_helper.c | 30 ++++--------------- include/drm/drm_client.h | 1 + 3 files changed, 51 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index 9c2c3b0c8c47..fc4caf7da5fc 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -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 diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 11a5b60cb9ce..53e9dc0543de 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -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); diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index 146ca80e35db..bdd845e383ef 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -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 -- 2.50.1