]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
drm/cirrus-qemu: Use vblank timer
authorThomas Zimmermann <tzimmermann@suse.de>
Wed, 8 Oct 2025 12:14:36 +0000 (14:14 +0200)
committerThomas Zimmermann <tzimmermann@suse.de>
Wed, 15 Oct 2025 08:29:49 +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. Cirrus-qemu
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. Cirrus-qemu' 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 cirrus-qemu 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/20251008121450.227997-1-tzimmermann@suse.de
drivers/gpu/drm/tiny/cirrus-qemu.c

index 97a93adc5669d7ae9f1ef37bd7bf3130d4e3695d..f728fa48ac88febff2a4f9e9033ec8ed79e2e51f 100644 (file)
@@ -45,6 +45,8 @@
 #include <drm/drm_modeset_helper_vtables.h>
 #include <drm/drm_module.h>
 #include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_vblank_helper.h>
 
 #define DRIVER_NAME "cirrus-qemu"
 #define DRIVER_DESC "qemu cirrus vga"
@@ -404,11 +406,15 @@ static void cirrus_crtc_helper_atomic_enable(struct drm_crtc *crtc,
 #endif
 
        drm_dev_exit(idx);
+
+       drm_crtc_vblank_on(crtc);
 }
 
 static const struct drm_crtc_helper_funcs cirrus_crtc_helper_funcs = {
        .atomic_check = cirrus_crtc_helper_atomic_check,
+       .atomic_flush = drm_crtc_vblank_atomic_flush,
        .atomic_enable = cirrus_crtc_helper_atomic_enable,
+       .atomic_disable = drm_crtc_vblank_atomic_disable,
 };
 
 static const struct drm_crtc_funcs cirrus_crtc_funcs = {
@@ -418,6 +424,7 @@ static const struct drm_crtc_funcs cirrus_crtc_funcs = {
        .page_flip = drm_atomic_helper_page_flip,
        .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
        .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+       DRM_CRTC_VBLANK_TIMER_FUNCS,
 };
 
 static const struct drm_encoder_funcs cirrus_encoder_funcs = {
@@ -493,6 +500,10 @@ static int cirrus_pipe_init(struct cirrus_device *cirrus)
        if (ret)
                return ret;
 
+       ret = drm_vblank_init(dev, 1);
+       if (ret)
+               return ret;
+
        return 0;
 }