From 11503c36967259f675fb4159d73bc8b49b7d4b1e Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Wed, 9 Oct 2024 12:07:22 -0700 Subject: [PATCH 1/3] Use `drm_ffi` for `DRM_MODE_DPMS_*` constants --- src/backend/drm/device/legacy.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/drm/device/legacy.rs b/src/backend/drm/device/legacy.rs index dc1a23fa5561..fa0564614ae4 100644 --- a/src/backend/drm/device/legacy.rs +++ b/src/backend/drm/device/legacy.rs @@ -202,9 +202,9 @@ where conn, *handle, if enabled { - 0 /*DRM_MODE_DPMS_ON*/ + drm_ffi::DRM_MODE_DPMS_ON.into() } else { - 3 /*DRM_MODE_DPMS_OFF*/ + drm_ffi::DRM_MODE_DPMS_OFF.into() }, ) .map_err(|source| { From c54e079238f85a99d6c299ff781be7b7178d0aac Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Wed, 9 Oct 2024 13:52:46 -0700 Subject: [PATCH 2/3] drm/surface/legacy: Remove unused `active` field of `State` This field is currently set, but never read. This logically doesn't need to be part of `state`/`pending` either, since a full commit is not needed to change the DPMS state. --- src/backend/drm/surface/legacy.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/backend/drm/surface/legacy.rs b/src/backend/drm/surface/legacy.rs index 027bdc8e11d9..ecaa78fae3d5 100644 --- a/src/backend/drm/surface/legacy.rs +++ b/src/backend/drm/surface/legacy.rs @@ -18,7 +18,6 @@ use tracing::{debug, info, info_span, instrument, trace}; #[derive(Debug, PartialEq, Eq, Clone)] pub struct State { - pub active: bool, pub mode: Mode, pub connectors: HashSet, } @@ -74,9 +73,6 @@ impl State { // we need to be sure, we require a mode to always be set without relying on the compiler. // So we cheat, because it works and is easier to handle later. Ok(State { - // On legacy there is not (reliable) way to read-back the dpms state. - // So we just always assume it is off. - active: false, mode: current_mode.unwrap_or_else(|| unsafe { std::mem::zeroed() }), connectors: current_connectors, }) @@ -107,7 +103,6 @@ impl LegacyDrmSurface { let state = State::current_state(&*fd, crtc)?; let pending = State { - active: true, mode, connectors: connectors.iter().copied().collect(), }; From 3a6d9cbf424d901878c95b5e964b758799343831 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Wed, 9 Oct 2024 20:25:52 -0700 Subject: [PATCH 3/3] Add a `clear` method to `DrmSurface` and `DrmCompositor` This sets DPMS state to off. Submitting a new frame will re-enable the output. --- src/backend/drm/compositor/mod.rs | 18 ++++++++++++++++++ src/backend/drm/surface/atomic.rs | 4 ++++ src/backend/drm/surface/legacy.rs | 28 +++++++++++++++++++++++++++- src/backend/drm/surface/mod.rs | 11 +++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/backend/drm/compositor/mod.rs b/src/backend/drm/compositor/mod.rs index 8b22b00ee817..2e7e99fb61e9 100644 --- a/src/backend/drm/compositor/mod.rs +++ b/src/backend/drm/compositor/mod.rs @@ -4216,6 +4216,24 @@ where Err(Some(RenderingReason::ScanoutFailed)) } } + + /// Clear the surface, setting DPMS state to off, disabling all planes, + /// and clearing the pending frame. + /// + /// Calling [`queue_frame`][Self::queue_frame] will re-enable. + pub fn clear(&mut self) -> Result<(), DrmError> { + self.surface.clear()?; + + self.current_frame + .planes + .iter_mut() + .for_each(|(_, state)| *state = Default::default()); + self.pending_frame = None; + self.queued_frame = None; + self.next_frame = None; + + Ok(()) + } } #[inline] diff --git a/src/backend/drm/surface/atomic.rs b/src/backend/drm/surface/atomic.rs index 5e7c1cc9fd51..21b6cea8787f 100644 --- a/src/backend/drm/surface/atomic.rs +++ b/src/backend/drm/surface/atomic.rs @@ -1081,6 +1081,10 @@ impl AtomicDrmSurface { pub(crate) fn device_fd(&self) -> &DrmDeviceFd { self.fd.device_fd() } + + pub fn clear(&self) -> Result<(), Error> { + self.clear_state() + } } struct TestBuffer { diff --git a/src/backend/drm/surface/legacy.rs b/src/backend/drm/surface/legacy.rs index ecaa78fae3d5..f4ba7f2a5fba 100644 --- a/src/backend/drm/surface/legacy.rs +++ b/src/backend/drm/surface/legacy.rs @@ -3,7 +3,7 @@ use drm::control::{connector, crtc, encoder, framebuffer, Device as ControlDevic use std::collections::HashSet; use std::sync::{ atomic::{AtomicBool, Ordering}, - Arc, RwLock, + Arc, Mutex, RwLock, }; use crate::backend::drm::error::AccessError; @@ -86,6 +86,7 @@ pub struct LegacyDrmSurface { crtc: crtc::Handle, state: RwLock, pending: RwLock, + dpms: Mutex, pub(super) span: tracing::Span, } @@ -114,6 +115,7 @@ impl LegacyDrmSurface { crtc, state: RwLock::new(state), pending: RwLock::new(pending), + dpms: Mutex::new(true), span, }; @@ -233,6 +235,13 @@ impl LegacyDrmSurface { let mut current = self.state.write().unwrap(); let pending = self.pending.read().unwrap(); + let mut dpms = self.dpms.lock().unwrap(); + + if !*dpms { + let connectors = current.connectors.intersection(&pending.connectors); + set_connector_state(&*self.fd, connectors.copied(), true)?; + *dpms = true; + } { let removed = current.connectors.difference(&pending.connectors); @@ -331,6 +340,13 @@ impl LegacyDrmSurface { return Err(Error::DeviceInactive); } + let current = self.state.read().unwrap(); + let mut dpms = self.dpms.lock().unwrap(); + if !*dpms { + set_connector_state(&*self.fd, current.connectors.iter().copied(), true)?; + *dpms = true; + } + ControlDevice::page_flip( &*self.fd, self.crtc, @@ -452,6 +468,16 @@ impl LegacyDrmSurface { pub(crate) fn device_fd(&self) -> &DrmDeviceFd { self.fd.device_fd() } + + pub fn clear(&self) -> Result<(), Error> { + let current = self.state.read().unwrap(); + let mut dpms = self.dpms.lock().unwrap(); + if *dpms { + set_connector_state(&*self.fd, current.connectors.iter().copied(), false)?; + *dpms = false; + } + Ok(()) + } } impl Drop for LegacyDrmSurface { diff --git a/src/backend/drm/surface/mod.rs b/src/backend/drm/surface/mod.rs index 04438058a377..57a3540a76d5 100644 --- a/src/backend/drm/surface/mod.rs +++ b/src/backend/drm/surface/mod.rs @@ -441,6 +441,17 @@ impl DrmSurface { DrmSurfaceInternal::Legacy(surf) => &surf.span, } } + + /// Clear the surface, setting DPMS state to off and disabling all planes. + /// + /// The surface will be re-enabled on the next [`page_flip`][Self::page_flip] or + /// [`commit`][Self::commit]. + pub fn clear(&self) -> Result<(), Error> { + match &*self.internal { + DrmSurfaceInternal::Atomic(surf) => surf.clear(), + DrmSurfaceInternal::Legacy(surf) => surf.clear(), + } + } } fn ensure_legacy_planes<'a>(