Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/vblank: Add vblank timer

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").

The new implementation is part of the existing vblank support,
which sets up the timer automatically. Drivers only have to start
and cancel the vblank timer as part of enabling and disabling the
CRTC. The new vblank helper library provides callbacks for struct
drm_crtc_funcs.

The standard way for handling vblank is to call drm_crtc_handle_vblank().
Drivers that require additional processing, such as vkms, can init
handle_vblank_timeout in struct drm_crtc_helper_funcs to refer to
their timeout handler.

There's a possible deadlock between drm_crtc_handle_vblank() and
hrtimer_cancel(). [1] The implementation avoids to call hrtimer_cancel()
directly and instead signals to the timer function to not restart
itself.

v4:
- fix possible race condition between timeout and atomic commit (Michael)
v3:
- avoid deadlock when cancelling timer (Ville, Lyude)
v2:
- implement vblank timer entirely in vblank helpers
- downgrade overrun warning to debug
- fix docs

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Tested-by: Louis Chauvet <louis.chauvet@bootlin.com>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Tested-by: Michael Kelley <mhklinux@outlook.com>
Link: https://lore.kernel.org/all/20250510094757.4174662-1-zengheng4@huawei.com/ # [1]
Link: https://lore.kernel.org/r/20250916083816.30275-2-tzimmermann@suse.de

+357 -3
+12
Documentation/gpu/drm-kms-helpers.rst
··· 92 92 .. kernel-doc:: drivers/gpu/drm/drm_gem_atomic_helper.c 93 93 :export: 94 94 95 + VBLANK Helper Reference 96 + ----------------------- 97 + 98 + .. kernel-doc:: drivers/gpu/drm/drm_vblank_helper.c 99 + :doc: overview 100 + 101 + .. kernel-doc:: include/drm/drm_vblank_helper.h 102 + :internal: 103 + 104 + .. kernel-doc:: drivers/gpu/drm/drm_vblank_helper.c 105 + :export: 106 + 95 107 Simple KMS Helper Reference 96 108 =========================== 97 109
+2 -1
drivers/gpu/drm/Makefile
··· 150 150 drm_plane_helper.o \ 151 151 drm_probe_helper.o \ 152 152 drm_self_refresh_helper.o \ 153 - drm_simple_kms_helper.o 153 + drm_simple_kms_helper.o \ 154 + drm_vblank_helper.o 154 155 drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o 155 156 drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o 156 157 obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
+170 -2
drivers/gpu/drm/drm_vblank.c
··· 136 136 * vblanks after a timer has expired, which can be configured through the 137 137 * ``vblankoffdelay`` module parameter. 138 138 * 139 - * Drivers for hardware without support for vertical-blanking interrupts 140 - * must not call drm_vblank_init(). For such drivers, atomic helpers will 139 + * Drivers for hardware without support for vertical-blanking interrupts can 140 + * use DRM vblank timers to send vblank events at the rate of the current 141 + * display mode's refresh. While not synchronized to the hardware's 142 + * vertical-blanking regions, the timer helps DRM clients and compositors to 143 + * adapt their update cycle to the display output. Drivers should set up 144 + * vblanking as usual, but call drm_crtc_vblank_start_timer() and 145 + * drm_crtc_vblank_cancel_timer() as part of their atomic mode setting. 146 + * See also DRM vblank helpers for more information. 147 + * 148 + * Drivers without support for vertical-blanking interrupts nor timers must 149 + * not call drm_vblank_init(). For these drivers, atomic helpers will 141 150 * automatically generate fake vblank events as part of the display update. 142 151 * This functionality also can be controlled by the driver by enabling and 143 152 * disabling struct drm_crtc_state.no_vblank. ··· 516 507 517 508 drm_WARN_ON(dev, READ_ONCE(vblank->enabled) && 518 509 drm_core_check_feature(dev, DRIVER_MODESET)); 510 + 511 + if (vblank->vblank_timer.crtc) 512 + hrtimer_cancel(&vblank->vblank_timer.timer); 519 513 520 514 drm_vblank_destroy_worker(vblank); 521 515 timer_delete_sync(&vblank->disable_timer); ··· 2174 2162 return ret; 2175 2163 } 2176 2164 2165 + /* 2166 + * VBLANK timer 2167 + */ 2168 + 2169 + static enum hrtimer_restart drm_vblank_timer_function(struct hrtimer *timer) 2170 + { 2171 + struct drm_vblank_crtc_timer *vtimer = 2172 + container_of(timer, struct drm_vblank_crtc_timer, timer); 2173 + struct drm_crtc *crtc = vtimer->crtc; 2174 + const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 2175 + struct drm_device *dev = crtc->dev; 2176 + unsigned long flags; 2177 + ktime_t interval; 2178 + u64 ret_overrun; 2179 + bool succ; 2180 + 2181 + spin_lock_irqsave(&vtimer->interval_lock, flags); 2182 + interval = vtimer->interval; 2183 + spin_unlock_irqrestore(&vtimer->interval_lock, flags); 2184 + 2185 + if (!interval) 2186 + return HRTIMER_NORESTART; 2187 + 2188 + ret_overrun = hrtimer_forward_now(&vtimer->timer, interval); 2189 + if (ret_overrun != 1) 2190 + drm_dbg_vbl(dev, "vblank timer overrun\n"); 2191 + 2192 + if (crtc_funcs->handle_vblank_timeout) 2193 + succ = crtc_funcs->handle_vblank_timeout(crtc); 2194 + else 2195 + succ = drm_crtc_handle_vblank(crtc); 2196 + if (!succ) 2197 + return HRTIMER_NORESTART; 2198 + 2199 + return HRTIMER_RESTART; 2200 + } 2201 + 2202 + /** 2203 + * drm_crtc_vblank_start_timer - Starts the vblank timer on the given CRTC 2204 + * @crtc: the CRTC 2205 + * 2206 + * Drivers should call this function from their CRTC's enable_vblank 2207 + * function to start a vblank timer. The timer will fire after the duration 2208 + * of a full frame. drm_crtc_vblank_cancel_timer() disables a running timer. 2209 + * 2210 + * Returns: 2211 + * 0 on success, or a negative errno code otherwise. 2212 + */ 2213 + int drm_crtc_vblank_start_timer(struct drm_crtc *crtc) 2214 + { 2215 + struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); 2216 + struct drm_vblank_crtc_timer *vtimer = &vblank->vblank_timer; 2217 + unsigned long flags; 2218 + 2219 + if (!vtimer->crtc) { 2220 + /* 2221 + * Set up the data structures on the first invocation. 2222 + */ 2223 + vtimer->crtc = crtc; 2224 + spin_lock_init(&vtimer->interval_lock); 2225 + hrtimer_setup(&vtimer->timer, drm_vblank_timer_function, 2226 + CLOCK_MONOTONIC, HRTIMER_MODE_REL); 2227 + } else { 2228 + /* 2229 + * Timer should not be active. If it is, wait for the 2230 + * previous cancel operations to finish. 2231 + */ 2232 + while (hrtimer_active(&vtimer->timer)) 2233 + hrtimer_try_to_cancel(&vtimer->timer); 2234 + } 2235 + 2236 + drm_calc_timestamping_constants(crtc, &crtc->mode); 2237 + 2238 + spin_lock_irqsave(&vtimer->interval_lock, flags); 2239 + vtimer->interval = ns_to_ktime(vblank->framedur_ns); 2240 + spin_unlock_irqrestore(&vtimer->interval_lock, flags); 2241 + 2242 + hrtimer_start(&vtimer->timer, vtimer->interval, HRTIMER_MODE_REL); 2243 + 2244 + return 0; 2245 + } 2246 + EXPORT_SYMBOL(drm_crtc_vblank_start_timer); 2247 + 2248 + /** 2249 + * drm_crtc_vblank_start_timer - Cancels the given CRTC's vblank timer 2250 + * @crtc: the CRTC 2251 + * 2252 + * Drivers should call this function from their CRTC's disable_vblank 2253 + * function to stop a vblank timer. 2254 + */ 2255 + void drm_crtc_vblank_cancel_timer(struct drm_crtc *crtc) 2256 + { 2257 + struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); 2258 + struct drm_vblank_crtc_timer *vtimer = &vblank->vblank_timer; 2259 + unsigned long flags; 2260 + 2261 + /* 2262 + * Calling hrtimer_cancel() can result in a deadlock with DRM's 2263 + * vblank_time_lime_lock and hrtimers' softirq_expiry_lock. So 2264 + * clear interval and indicate cancellation. The timer function 2265 + * will cancel itself on the next invocation. 2266 + */ 2267 + 2268 + spin_lock_irqsave(&vtimer->interval_lock, flags); 2269 + vtimer->interval = 0; 2270 + spin_unlock_irqrestore(&vtimer->interval_lock, flags); 2271 + 2272 + hrtimer_try_to_cancel(&vtimer->timer); 2273 + } 2274 + EXPORT_SYMBOL(drm_crtc_vblank_cancel_timer); 2275 + 2276 + /** 2277 + * drm_crtc_vblank_get_vblank_timeout - Returns the vblank timeout 2278 + * @crtc: The CRTC 2279 + * @vblank_time: Returns the next vblank timestamp 2280 + * 2281 + * The helper drm_crtc_vblank_get_vblank_timeout() returns the next vblank 2282 + * timestamp of the CRTC's vblank timer according to the timer's expiry 2283 + * time. 2284 + */ 2285 + void drm_crtc_vblank_get_vblank_timeout(struct drm_crtc *crtc, ktime_t *vblank_time) 2286 + { 2287 + struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); 2288 + struct drm_vblank_crtc_timer *vtimer = &vblank->vblank_timer; 2289 + u64 cur_count; 2290 + ktime_t cur_time; 2291 + 2292 + if (!READ_ONCE(vblank->enabled)) { 2293 + *vblank_time = ktime_get(); 2294 + return; 2295 + } 2296 + 2297 + /* 2298 + * A concurrent vblank timeout could update the expires field before 2299 + * we compare it with the vblank time. Hence we'd compare the old 2300 + * expiry time to the new vblank time; deducing the timer had already 2301 + * expired. Reread until we get consistent values from both fields. 2302 + */ 2303 + do { 2304 + cur_count = drm_crtc_vblank_count_and_time(crtc, &cur_time); 2305 + *vblank_time = READ_ONCE(vtimer->timer.node.expires); 2306 + } while (cur_count != drm_crtc_vblank_count_and_time(crtc, &cur_time)); 2307 + 2308 + if (drm_WARN_ON(crtc->dev, !ktime_compare(*vblank_time, cur_time))) 2309 + return; /* Already expired */ 2310 + 2311 + /* 2312 + * To prevent races we roll the hrtimer forward before we do any 2313 + * interrupt processing - this is how real hw works (the interrupt 2314 + * is only generated after all the vblank registers are updated) 2315 + * and what the vblank core expects. Therefore we need to always 2316 + * correct the timestamp by one frame. 2317 + */ 2318 + *vblank_time = ktime_sub(*vblank_time, vtimer->interval); 2319 + } 2320 + EXPORT_SYMBOL(drm_crtc_vblank_get_vblank_timeout);
+96
drivers/gpu/drm/drm_vblank_helper.c
··· 1 + // SPDX-License-Identifier: MIT 2 + 3 + #include <drm/drm_crtc.h> 4 + #include <drm/drm_managed.h> 5 + #include <drm/drm_modeset_helper_vtables.h> 6 + #include <drm/drm_print.h> 7 + #include <drm/drm_vblank.h> 8 + #include <drm/drm_vblank_helper.h> 9 + 10 + /** 11 + * DOC: overview 12 + * 13 + * The vblank helper library provides functions for supporting vertical 14 + * blanking in DRM drivers. 15 + * 16 + * For vblank timers, several callback implementations are available. 17 + * Drivers enable support for vblank timers by setting the vblank callbacks 18 + * in struct &drm_crtc_funcs to the helpers provided by this library. The 19 + * initializer macro DRM_CRTC_VBLANK_TIMER_FUNCS does this conveniently. 20 + * 21 + * Once the driver enables vblank support with drm_vblank_init(), each 22 + * CRTC's vblank timer fires according to the programmed display mode. By 23 + * default, the vblank timer invokes drm_crtc_handle_vblank(). Drivers with 24 + * more specific requirements can set their own handler function in 25 + * struct &drm_crtc_helper_funcs.handle_vblank_timeout. 26 + */ 27 + 28 + /* 29 + * VBLANK timer 30 + */ 31 + 32 + /** 33 + * drm_crtc_vblank_helper_enable_vblank_timer - Implements struct &drm_crtc_funcs.enable_vblank 34 + * @crtc: The CRTC 35 + * 36 + * The helper drm_crtc_vblank_helper_enable_vblank_timer() implements 37 + * enable_vblank of struct drm_crtc_helper_funcs for CRTCs that require 38 + * a VBLANK timer. It sets up the timer on the first invocation. The 39 + * started timer expires after the current frame duration. See struct 40 + * &drm_vblank_crtc.framedur_ns. 41 + * 42 + * See also struct &drm_crtc_helper_funcs.enable_vblank. 43 + * 44 + * Returns: 45 + * 0 on success, or a negative errno code otherwise. 46 + */ 47 + int drm_crtc_vblank_helper_enable_vblank_timer(struct drm_crtc *crtc) 48 + { 49 + return drm_crtc_vblank_start_timer(crtc); 50 + } 51 + EXPORT_SYMBOL(drm_crtc_vblank_helper_enable_vblank_timer); 52 + 53 + /** 54 + * drm_crtc_vblank_helper_disable_vblank_timer - Implements struct &drm_crtc_funcs.disable_vblank 55 + * @crtc: The CRTC 56 + * 57 + * The helper drm_crtc_vblank_helper_disable_vblank_timer() implements 58 + * disable_vblank of struct drm_crtc_funcs for CRTCs that require a 59 + * VBLANK timer. 60 + * 61 + * See also struct &drm_crtc_helper_funcs.disable_vblank. 62 + */ 63 + void drm_crtc_vblank_helper_disable_vblank_timer(struct drm_crtc *crtc) 64 + { 65 + drm_crtc_vblank_cancel_timer(crtc); 66 + } 67 + EXPORT_SYMBOL(drm_crtc_vblank_helper_disable_vblank_timer); 68 + 69 + /** 70 + * drm_crtc_vblank_helper_get_vblank_timestamp_from_timer - 71 + * Implements struct &drm_crtc_funcs.get_vblank_timestamp 72 + * @crtc: The CRTC 73 + * @max_error: Maximum acceptable error 74 + * @vblank_time: Returns the next vblank timestamp 75 + * @in_vblank_irq: True is called from drm_crtc_handle_vblank() 76 + * 77 + * The helper drm_crtc_helper_get_vblank_timestamp_from_timer() implements 78 + * get_vblank_timestamp of struct drm_crtc_funcs for CRTCs that require a 79 + * VBLANK timer. It returns the timestamp according to the timer's expiry 80 + * time. 81 + * 82 + * See also struct &drm_crtc_funcs.get_vblank_timestamp. 83 + * 84 + * Returns: 85 + * True on success, or false otherwise. 86 + */ 87 + bool drm_crtc_vblank_helper_get_vblank_timestamp_from_timer(struct drm_crtc *crtc, 88 + int *max_error, 89 + ktime_t *vblank_time, 90 + bool in_vblank_irq) 91 + { 92 + drm_crtc_vblank_get_vblank_timeout(crtc, vblank_time); 93 + 94 + return true; 95 + } 96 + EXPORT_SYMBOL(drm_crtc_vblank_helper_get_vblank_timestamp_from_timer);
+12
include/drm/drm_modeset_helper_vtables.h
··· 490 490 bool in_vblank_irq, int *vpos, int *hpos, 491 491 ktime_t *stime, ktime_t *etime, 492 492 const struct drm_display_mode *mode); 493 + 494 + /** 495 + * @handle_vblank_timeout: Handles timeouts of the vblank timer. 496 + * 497 + * Called by CRTC's the vblank timer on each timeout. Semantics is 498 + * equivalient to drm_crtc_handle_vblank(). Implementations should 499 + * invoke drm_crtc_handle_vblank() as part of processing the timeout. 500 + * 501 + * This callback is optional. If unset, the vblank timer invokes 502 + * drm_crtc_handle_vblank() directly. 503 + */ 504 + bool (*handle_vblank_timeout)(struct drm_crtc *crtc); 493 505 }; 494 506 495 507 /**
+32
include/drm/drm_vblank.h
··· 25 25 #define _DRM_VBLANK_H_ 26 26 27 27 #include <linux/seqlock.h> 28 + #include <linux/hrtimer.h> 28 29 #include <linux/idr.h> 29 30 #include <linux/poll.h> 30 31 #include <linux/kthread.h> ··· 102 101 * drm_crtc_vblank_on(). 103 102 */ 104 103 bool disable_immediate; 104 + }; 105 + 106 + /** 107 + * struct drm_vblank_crtc_timer - vblank timer for a CRTC 108 + */ 109 + struct drm_vblank_crtc_timer { 110 + /** 111 + * @timer: The vblank's high-resolution timer 112 + */ 113 + struct hrtimer timer; 114 + /** 115 + * @interval_lock: Protects @interval 116 + */ 117 + spinlock_t interval_lock; 118 + /** 119 + * @interval: Duration between two vblanks 120 + */ 121 + ktime_t interval; 122 + /** 123 + * @crtc: The timer's CRTC 124 + */ 125 + struct drm_crtc *crtc; 105 126 }; 106 127 107 128 /** ··· 277 254 * cancelled. 278 255 */ 279 256 wait_queue_head_t work_wait_queue; 257 + 258 + /** 259 + * @vblank_timer: Holds the state of the vblank timer 260 + */ 261 + struct drm_vblank_crtc_timer vblank_timer; 280 262 }; 281 263 282 264 struct drm_vblank_crtc *drm_crtc_vblank_crtc(struct drm_crtc *crtc); ··· 317 289 wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc); 318 290 void drm_crtc_set_max_vblank_count(struct drm_crtc *crtc, 319 291 u32 max_vblank_count); 292 + 293 + int drm_crtc_vblank_start_timer(struct drm_crtc *crtc); 294 + void drm_crtc_vblank_cancel_timer(struct drm_crtc *crtc); 295 + void drm_crtc_vblank_get_vblank_timeout(struct drm_crtc *crtc, ktime_t *vblank_time); 320 296 321 297 /* 322 298 * Helpers for struct drm_crtc_funcs
+33
include/drm/drm_vblank_helper.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ */ 2 + 3 + #ifndef _DRM_VBLANK_HELPER_H_ 4 + #define _DRM_VBLANK_HELPER_H_ 5 + 6 + #include <linux/hrtimer_types.h> 7 + #include <linux/types.h> 8 + 9 + struct drm_crtc; 10 + 11 + /* 12 + * VBLANK timer 13 + */ 14 + 15 + int drm_crtc_vblank_helper_enable_vblank_timer(struct drm_crtc *crtc); 16 + void drm_crtc_vblank_helper_disable_vblank_timer(struct drm_crtc *crtc); 17 + bool drm_crtc_vblank_helper_get_vblank_timestamp_from_timer(struct drm_crtc *crtc, 18 + int *max_error, 19 + ktime_t *vblank_time, 20 + bool in_vblank_irq); 21 + 22 + /** 23 + * DRM_CRTC_VBLANK_TIMER_FUNCS - Default implementation for VBLANK timers 24 + * 25 + * This macro initializes struct &drm_crtc_funcs to default helpers for 26 + * VBLANK timers. 27 + */ 28 + #define DRM_CRTC_VBLANK_TIMER_FUNCS \ 29 + .enable_vblank = drm_crtc_vblank_helper_enable_vblank_timer, \ 30 + .disable_vblank = drm_crtc_vblank_helper_disable_vblank_timer, \ 31 + .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp_from_timer 32 + 33 + #endif