srdusr
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2024-08-29 21:31:00 +0200
committersrdusr <[email protected]>2024-08-29 21:31:00 +0200
commit3509cf6127185d9fed5a63ceca18a003213a20d7 (patch)
tree661e39acfebd37cbb6abaddc686df724d869b988
parent4fb537aa4cfe52e364dbcd9a97078f0206ada54b (diff)
downloadsrdwm-3509cf6127185d9fed5a63ceca18a003213a20d7.tar.gz
srdwm-3509cf6127185d9fed5a63ceca18a003213a20d7.zip
Fix VT-switch resume getting stuck on a stale pending page flip
A flip issued right before a VT switch away could still be undelivered when the session resumed - the kernel refuses a new page flip on a CRTC with one already outstanding, which showed up live as a black screen that never recovered across two switch attempts, with a rapid repeating "Device or resource busy" loop in the log. Drain and apply any pending DRM events before reasserting CRTCs and rendering again on resume, so a stale flip from before the switch can't collide with the fresh one.
-rw-r--r--crates/wayland/src/udev/session.rs25
1 files changed, 24 insertions, 1 deletions
diff --git a/crates/wayland/src/udev/session.rs b/crates/wayland/src/udev/session.rs
index 2853f33..fa40b0e 100644
--- a/crates/wayland/src/udev/session.rs
+++ b/crates/wayland/src/udev/session.rs
@@ -63,9 +63,32 @@ pub(crate) fn register_session_notifier(handle: &LoopHandle<'static, CompState>,
SessionEvent::ActivateSession => {
log::info!("udev: session resumed (VT switch back)");
udev.active = true;
+ let card = udev.card.clone();
+
+ // A flip issued right before the VT switch away may
+ // never have completed while inactive (nothing was
+ // scanning out), and its completion event can still be
+ // sitting undelivered on the DRM fd. The kernel refuses
+ // a new page flip on a CRTC with one already
+ // unacknowledged (EBUSY) - drain and apply any such
+ // events now, before reasserting crtcs, so a stale flip
+ // from before the switch can't collide with the fresh
+ // one `render_udev_frame` is about to issue below.
+ match card.receive_events() {
+ Ok(events) => {
+ for event in events {
+ let DrmEvent::PageFlip(flip) = event else { continue };
+ if let Some(head) = udev.heads.iter_mut().find(|h| h.crtc == flip.crtc) {
+ head.front = 1 - head.front;
+ head.flip_pending = false;
+ }
+ }
+ }
+ Err(e) => log::debug!("udev: no pending flip events to drain on resume: {e}"),
+ }
+
// Some drivers reset mode-setting state across a VT
// switch; reassert every head before rendering again.
- let card = udev.card.clone();
for head in &mut udev.heads {
let fb = head.buffers[head.front].fb;
if let Err(e) = card.set_crtc(head.crtc, Some(fb), (0, 0), &[], None) {