Hi

Am 06.06.25 um 10:43 schrieb Louis Chauvet:


Le 05/06/2025 à 17:24, Thomas Zimmermann a écrit :
The vblank timer simulates a vblank interrupt for hardware without
support. Rate-limits the display update frequency.

DRM drivers for hardware without vblank support apply display updates
ASAP. A vblank event informs DRM clients of the completed update.

Userspace compositors immediately schedule the next update, which
creates significant load on virtualization outputs. Display updates
are usually fast on virtualization outputs, as their framebuffers are
in regular system memory and there's no hardware vblank interrupt to
throttle the update rate.

The vblank timer is a HR timer that signals the vblank in software.
It limits the update frequency of a DRM driver similar to a hardware
vblank interrupt. The timer is not synchronized to the actual vblank
interval of the display.

The code has been adopted from vkms, which added the funtionality
in commit 3a0709928b17 ("drm/vkms: Add vblank events simulated by
hrtimers").

Signed-off-by: Thomas Zimmermann <tzimmerm...@suse.de>

Tested-by: Louis Chauvet <louis.chau...@bootlin.com>
Reviewed-by: Louis Chauvet <louis.chau...@bootlin.com>

Thanks for looking through the series. I'll add the tags when I prepare the next iteration, but the interfaces will change quite a bit. You may want to take another look at the series then.

Best regards
Thomas


---
  drivers/gpu/drm/Makefile           |   3 +-
  drivers/gpu/drm/drm_vblank_timer.c | 100 +++++++++++++++++++++++++++++
  include/drm/drm_vblank_timer.h     |  26 ++++++++
  3 files changed, 128 insertions(+), 1 deletion(-)
  create mode 100644 drivers/gpu/drm/drm_vblank_timer.c
  create mode 100644 include/drm/drm_vblank_timer.h

diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index b5d5561bbe5f..6722e2d1aa7e 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -146,7 +146,8 @@ drm_kms_helper-y := \
      drm_plane_helper.o \
      drm_probe_helper.o \
      drm_self_refresh_helper.o \
-    drm_simple_kms_helper.o
+    drm_simple_kms_helper.o \
+    drm_vblank_timer.o
  drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
  drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
  obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
diff --git a/drivers/gpu/drm/drm_vblank_timer.c b/drivers/gpu/drm/drm_vblank_timer.c
new file mode 100644
index 000000000000..be46d3135c8e
--- /dev/null
+++ b/drivers/gpu/drm/drm_vblank_timer.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/hrtimer.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_vblank_timer.h>
+
+static enum hrtimer_restart drm_vblank_timer_function(struct hrtimer *timer)
+{
+    struct drm_vblank_timer *vtimer = container_of(timer, struct drm_vblank_timer, timer);
+    struct drm_crtc *crtc = vtimer->crtc;
+    struct drm_device *dev = crtc->dev;
+    u64 ret_overrun;
+    bool succ;
+
+    ret_overrun = hrtimer_forward_now(&vtimer->timer, vtimer->period_ns);
+    if (ret_overrun != 1)
+        drm_warn(dev, "vblank timer overrun\n");
+
+    if (vtimer->crtc_handle_vblank)
+        succ = vtimer->crtc_handle_vblank(crtc);
+    else
+        succ = drm_crtc_handle_vblank(crtc);
+    if (!succ)
+        return HRTIMER_NORESTART;
+
+    return HRTIMER_RESTART;
+}
+
+static void drmm_vblank_timer_release(struct drm_device *dev, void *res)
+{
+    struct drm_vblank_timer *vtimer = res;
+
+    hrtimer_cancel(&vtimer->timer);
+}
+
+int drmm_vblank_timer_init(struct drm_vblank_timer *vtimer, struct drm_crtc *crtc,
+               bool (*crtc_handle_vblank)(struct drm_crtc *crtc))
+{
+    struct hrtimer *timer = &vtimer->timer;
+
+    vtimer->crtc = crtc;
+    vtimer->crtc_handle_vblank = crtc_handle_vblank;
+
+    hrtimer_setup(timer, drm_vblank_timer_function, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+
+    return drmm_add_action_or_reset(crtc->dev, drmm_vblank_timer_release, vtimer);
+}
+EXPORT_SYMBOL(drmm_vblank_timer_init);
+
+void drm_vblank_timer_start(struct drm_vblank_timer *vtimer)
+{
+    struct drm_crtc *crtc = vtimer->crtc;
+    struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
+
+    drm_calc_timestamping_constants(crtc, &crtc->mode);
+
+    vtimer->period_ns = ktime_set(0, vblank->framedur_ns);
+    hrtimer_start(&vtimer->timer, vtimer->period_ns, HRTIMER_MODE_REL);
+}
+EXPORT_SYMBOL(drm_vblank_timer_start);
+
+void drm_vblank_timer_cancel(struct drm_vblank_timer *vtimer)
+{
+    hrtimer_cancel(&vtimer->timer);
+}
+EXPORT_SYMBOL(drm_vblank_timer_cancel);
+
+bool drm_vblank_timer_get_vblank_timestamp(struct drm_vblank_timer *vtimer,
+                       int *max_error, ktime_t *vblank_time,
+                       bool in_vblank_irq)
+{
+    struct drm_crtc *crtc = vtimer->crtc;
+    struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
+
+    if (!READ_ONCE(vblank->enabled)) {
+        *vblank_time = ktime_get();
+        return true;
+    }
+
+    *vblank_time = READ_ONCE(vtimer->timer.node.expires);
+
+    if (WARN_ON(*vblank_time == vblank->time))
+        return true;
+
+    /*
+     * To prevent races we roll the hrtimer forward before we do any
+     * interrupt processing - this is how real hw works (the interrupt is +     * only generated after all the vblank registers are updated) and what
+     * the vblank core expects. Therefore we need to always correct the
+     * timestampe by one frame.
+     */
+    *vblank_time -= vtimer->period_ns;
+
+    return true;
+}
+EXPORT_SYMBOL(drm_vblank_timer_get_vblank_timestamp);
diff --git a/include/drm/drm_vblank_timer.h b/include/drm/drm_vblank_timer.h
new file mode 100644
index 000000000000..0b827ff1f59c
--- /dev/null
+++ b/include/drm/drm_vblank_timer.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _DRM_VBLANK_TIMER_H_
+#define _DRM_VBLANK_TIMER_H_
+
+#include <linux/hrtimer_types.h>
+#include <linux/types.h>
+
+struct drm_crtc;
+
+struct drm_vblank_timer {
+    struct drm_crtc *crtc;
+    bool (*crtc_handle_vblank)(struct drm_crtc *crtc);
+    ktime_t period_ns;
+    struct hrtimer timer;
+};
+
+int drmm_vblank_timer_init(struct drm_vblank_timer *vtimer, struct drm_crtc *crtc,
+               bool (*handle_vblank)(struct drm_crtc *crtc));
+void drm_vblank_timer_start(struct drm_vblank_timer *vtimer);
+void drm_vblank_timer_cancel(struct drm_vblank_timer *vtimer);
+bool drm_vblank_timer_get_vblank_timestamp(struct drm_vblank_timer *vtimer,
+                       int *max_error, ktime_t *vblank_time,
+                       bool in_vblank_irq);
+
+#endif


--
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstrasse 146, 90461 Nuernberg, Germany
GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman
HRB 36809 (AG Nuernberg)


Reply via email to