diff --git a/src/backend/kms/drm_helpers.rs b/src/backend/kms/drm_helpers.rs index 6144e4f0..bc80b2b8 100644 --- a/src/backend/kms/drm_helpers.rs +++ b/src/backend/kms/drm_helpers.rs @@ -77,24 +77,6 @@ pub fn display_configuration( let mut req = AtomicModeReq::new(); let plane_handles = device.plane_handles()?; - for conn in connectors - .iter() - .flat_map(|conn| device.get_connector(*conn, false).ok()) - .filter(|conn| { - if let Some(enc) = conn.current_encoder() - && let Ok(enc) = device.get_encoder(enc) - && let Some(crtc) = enc.crtc() - { - return cleanup.contains(&crtc); - } - false - }) - .map(|info| info.handle()) - { - let crtc_id = get_prop(device, conn, "CRTC_ID")?; - req.add_property(conn, crtc_id, property::Value::CRTC(None)); - } - // We cannot just shortcut and use the legacy api for all cleanups because of this. // (Technically a device does not need to be atomic for planes to be used, but nobody does this otherwise.) for plane in plane_handles { @@ -108,7 +90,7 @@ pub fn display_configuration( _ => false, }, )?; - if cleanup.contains(&crtc) || !is_primary { + if !is_primary && !cleanup.contains(&crtc) { let crtc_id = get_prop(device, plane, "CRTC_ID")?; let fb_id = get_prop(device, plane, "FB_ID")?; req.add_property(plane, crtc_id, property::Value::CRTC(None)); @@ -116,27 +98,80 @@ pub fn display_configuration( } } } - - for crtc in cleanup { - let mode_id = get_prop(device, crtc, "MODE_ID")?; - let active = get_prop(device, crtc, "ACTIVE")?; - req.add_property(crtc, active, property::Value::Boolean(false)); - req.add_property(crtc, mode_id, property::Value::Unknown(0)); - } - device.atomic_commit(AtomicCommitFlags::ALLOW_MODESET, req)?; } else { for crtc in res_handles.crtcs() { #[allow(deprecated)] let _ = device.set_cursor(*crtc, Option::<&DumbBuffer>::None); } - for crtc in cleanup { + } + disable_crtcs(device, supports_atomic, &cleanup)?; + + Ok(map) +} + +/// Disables the given CRTCs and detaches their connectors/planes. +pub fn disable_crtcs( + device: &mut impl ControlDevice, + supports_atomic: bool, + crtcs: &[crtc::Handle], +) -> Result<()> { + if crtcs.is_empty() { + return Ok(()); + } + + let res_handles = device.resource_handles()?; + + if supports_atomic { + let mut req = AtomicModeReq::new(); + + for conn in res_handles + .connectors() + .iter() + .flat_map(|conn| device.get_connector(*conn, false).ok()) + .filter(|conn| { + if let Some(enc) = conn.current_encoder() + && let Ok(enc) = device.get_encoder(enc) + && let Some(crtc) = enc.crtc() + { + return crtcs.contains(&crtc); + } + false + }) + .map(|info| info.handle()) + { + let crtc_id = get_prop(device, conn, "CRTC_ID")?; + req.add_property(conn, crtc_id, property::Value::CRTC(None)); + } + + for plane in device.plane_handles()? { + let info = device.get_plane(plane)?; + if let Some(crtc) = info.crtc() + && crtcs.contains(&crtc) + { + let crtc_id = get_prop(device, plane, "CRTC_ID")?; + let fb_id = get_prop(device, plane, "FB_ID")?; + req.add_property(plane, crtc_id, property::Value::CRTC(None)); + req.add_property(plane, fb_id, property::Value::Framebuffer(None)); + } + } + + for crtc in crtcs { + let mode_id = get_prop(device, *crtc, "MODE_ID")?; + let active = get_prop(device, *crtc, "ACTIVE")?; + req.add_property(*crtc, active, property::Value::Boolean(false)); + req.add_property(*crtc, mode_id, property::Value::Unknown(0)); + } + + device.atomic_commit(AtomicCommitFlags::ALLOW_MODESET, req)?; + } else { + for crtc in crtcs { // null commit (necessary to trigger removal on the kernel side with the legacy api.) - let _ = device.set_crtc(crtc, None, (0, 0), &[], None); + let _ = device.set_crtc(*crtc, None, (0, 0), &[], None); } } - Ok(map) + Ok(()) } pub fn interface_name(device: &impl ControlDevice, connector: connector::Handle) -> Result { diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 694d6ce7..07612790 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -852,11 +852,26 @@ impl KmsGuard<'_> { // first drop old surfaces if !test_only { - for output in outputs.iter().filter(|o| !o.is_enabled()) { - device - .inner - .surfaces - .retain(|_, surface| surface.output != *output); + let mut disabled_crtcs = Vec::new(); + device.inner.surfaces.retain(|crtc, surface| { + if outputs + .iter() + .any(|o| !o.is_enabled() && surface.output == *o) + { + disabled_crtcs.push(*crtc); + false + } else { + true + } + }); + + let supports_atomic = device.drm.device().is_atomic(); + if let Err(err) = drm_helpers::disable_crtcs( + device.drm.device_mut(), + supports_atomic, + &disabled_crtcs, + ) { + warn!("Failed to disable crtcs for disabled outputs: {err}"); } }