]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
drm/qxl: Use vblank timer
authorThomas Zimmermann <tzimmermann@suse.de>
Wed, 8 Oct 2025 12:29:06 +0000 (14:29 +0200)
committerThomas Zimmermann <tzimmermann@suse.de>
Wed, 15 Oct 2025 08:29:59 +0000 (10:29 +0200)
Use a vblank timer to simulate the vblank interrupt. The DRM vblank
helpers provide an implementation on top of Linux' hrtimer. Qxl
enables and disables the timer as part of the CRTC. The atomic_flush
callback sets up the event. Like vblank interrupts, the vblank timer
fires at the rate of the display refresh.

Most userspace limits its page flip rate according to the DRM vblank
event. Qxl's virtual hardware does not provide vblank interrupts, so
DRM sends each event ASAP. With the fast access times of virtual display
memory, the event rate is much higher than the display mode's refresh
rate; creating the next page flip almost immediately. This leads to
excessive CPU overhead from even small display updates, such as moving
the mouse pointer.

This problem affects qxl and all other virtual displays. See [1] for
a discussion in the context of hypervdrm.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://lore.kernel.org/dri-devel/SN6PR02MB415702B00D6D52B0EE962C98D46CA@SN6PR02MB4157.namprd02.prod.outlook.com/
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Link: https://lore.kernel.org/r/20251008122911.231674-1-tzimmermann@suse.de
drivers/gpu/drm/qxl/qxl_display.c

index ae7e572b1b4ab54b1c4abca2362f26c2a347f260..b7d0e60c0de2486319ef6974fbc95087e7cd431a 100644 (file)
@@ -37,6 +37,8 @@
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_simple_kms_helper.h>
 #include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_vblank_helper.h>
 
 #include "qxl_drv.h"
 #include "qxl_object.h"
@@ -382,7 +384,25 @@ static void qxl_crtc_update_monitors_config(struct drm_crtc *crtc,
 static void qxl_crtc_atomic_flush(struct drm_crtc *crtc,
                                  struct drm_atomic_state *state)
 {
+       struct drm_device *dev = crtc->dev;
+       struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+       struct drm_pending_vblank_event *event;
+
        qxl_crtc_update_monitors_config(crtc, "flush");
+
+       spin_lock_irq(&dev->event_lock);
+
+       event = crtc_state->event;
+       crtc_state->event = NULL;
+
+       if (event) {
+               if (drm_crtc_vblank_get(crtc) == 0)
+                       drm_crtc_arm_vblank_event(crtc, event);
+               else
+                       drm_crtc_send_vblank_event(crtc, event);
+       }
+
+       spin_unlock_irq(&dev->event_lock);
 }
 
 static void qxl_crtc_destroy(struct drm_crtc *crtc)
@@ -401,6 +421,7 @@ static const struct drm_crtc_funcs qxl_crtc_funcs = {
        .reset = drm_atomic_helper_crtc_reset,
        .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
        .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+       DRM_CRTC_VBLANK_TIMER_FUNCS,
 };
 
 static int qxl_framebuffer_surface_dirty(struct drm_framebuffer *fb,
@@ -455,11 +476,15 @@ static void qxl_crtc_atomic_enable(struct drm_crtc *crtc,
                                   struct drm_atomic_state *state)
 {
        qxl_crtc_update_monitors_config(crtc, "enable");
+
+       drm_crtc_vblank_on(crtc);
 }
 
 static void qxl_crtc_atomic_disable(struct drm_crtc *crtc,
                                    struct drm_atomic_state *state)
 {
+       drm_crtc_vblank_off(crtc);
+
        qxl_crtc_update_monitors_config(crtc, "disable");
 }
 
@@ -1276,6 +1301,10 @@ int qxl_modeset_init(struct qxl_device *qdev)
 
        qxl_display_read_client_monitors_config(qdev);
 
+       ret = drm_vblank_init(&qdev->ddev, qxl_num_crtc);
+       if (ret)
+               return ret;
+
        drm_mode_config_reset(&qdev->ddev);
        return 0;
 }