fix: internal laptop screen stuck on vendor logo with external monitor

This commit is contained in:
Alexander Daichendt 2026-06-28 16:30:53 +02:00 committed by Victoria Brekenfeld
parent dacdc3db39
commit a9f2427c00
2 changed files with 85 additions and 35 deletions

View file

@ -77,24 +77,6 @@ pub fn display_configuration(
let mut req = AtomicModeReq::new(); let mut req = AtomicModeReq::new();
let plane_handles = device.plane_handles()?; 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. // 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.) // (Technically a device does not need to be atomic for planes to be used, but nobody does this otherwise.)
for plane in plane_handles { for plane in plane_handles {
@ -108,7 +90,7 @@ pub fn display_configuration(
_ => false, _ => false,
}, },
)?; )?;
if cleanup.contains(&crtc) || !is_primary { if !is_primary && !cleanup.contains(&crtc) {
let crtc_id = get_prop(device, plane, "CRTC_ID")?; let crtc_id = get_prop(device, plane, "CRTC_ID")?;
let fb_id = get_prop(device, plane, "FB_ID")?; let fb_id = get_prop(device, plane, "FB_ID")?;
req.add_property(plane, crtc_id, property::Value::CRTC(None)); 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)?; device.atomic_commit(AtomicCommitFlags::ALLOW_MODESET, req)?;
} else { } else {
for crtc in res_handles.crtcs() { for crtc in res_handles.crtcs() {
#[allow(deprecated)] #[allow(deprecated)]
let _ = device.set_cursor(*crtc, Option::<&DumbBuffer>::None); 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.) // 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<String> { pub fn interface_name(device: &impl ControlDevice, connector: connector::Handle) -> Result<String> {

View file

@ -852,11 +852,26 @@ impl KmsGuard<'_> {
// first drop old surfaces // first drop old surfaces
if !test_only { if !test_only {
for output in outputs.iter().filter(|o| !o.is_enabled()) { let mut disabled_crtcs = Vec::new();
device device.inner.surfaces.retain(|crtc, surface| {
.inner if outputs
.surfaces .iter()
.retain(|_, surface| surface.output != *output); .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}");
} }
} }