[PATCH v3 20/23] rust: drm: kms: add synthesized CVT connector modes
Mike Lothian <[email protected]>
| Newsgroups | org.kernel.vger.rust-for-linux,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
Expose a lock-scoped helper for adding a driver-synthesized CVT timing to a connector probe result. This lets virtual and transport-backed displays offer valid continuous-frequency modes without reaching into DRM mode lists. Assisted-by: Claude:claude-opus-5 Signed-off-by: Mike Lothian <[email protected]> --- rust/kernel/drm/kms/connector.rs | 84 ++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs index dd126469788f..952c8e02777b 100644 --- a/rust/kernel/drm/kms/connector.rs +++ b/rust/kernel/drm/kms/connector.rs @@ -495,6 +495,60 @@ pub fn attach_encoder<E>(&self, encoder: &UnregisteredEncoder<E>) -> Result bindings::drm_connector_attach_encoder(self.as_raw(), encoder.as_raw()) }) } + + /// Attach the HDR output metadata property to this [`Connector`]. + /// + /// This property carries a blob supplied by userspace. Drivers must still validate and apply + /// the metadata in their atomic commit path before claiming that HDR output is supported. + pub fn attach_hdr_output_metadata_property(&self) { + // SAFETY: `self` is an initialized connector owned by this DRM device. The helper only + // attaches the mode-config-owned standard property to its mode object. + unsafe { + bindings::drm_connector_attach_hdr_output_metadata_property(self.as_raw()); + } + } + + /// Create and attach the standard DP colorspace property to this [`Connector`]. + /// + /// A zero mask asks DRM to expose every colorspace defined for DisplayPort. A driver must + /// still reject values its sink or transport cannot actually carry in its atomic check. + pub fn attach_colorspace_property(&self) -> Result { + to_result(unsafe { bindings::drm_mode_create_dp_colorspace_property(self.as_raw(), 0) })?; + // SAFETY: the successful create call above initialized `colorspace_property` for this + // connector; the C helper only attaches that property to this connector's mode object. + to_result(unsafe { bindings::drm_connector_attach_colorspace_property(self.as_raw()) }) + } + + /// Attach the standard `max bpc` range property to this [`Connector`]. + /// + /// `min_bpc` and `max_bpc` are validated before conversion so callers cannot wrap an invalid + /// range through the C `int` API. DRM requires the connector to have an atomic state before + /// this helper is called; newly-created Rust connectors acquire that state here. + pub fn attach_max_bpc_property(&self, min_bpc: u32, max_bpc: u32) -> Result { + if min_bpc == 0 || min_bpc > max_bpc || max_bpc > i32::MAX as u32 { + return Err(EINVAL); + } + + // `drm_connector_attach_max_bpc_property()` writes the initial bpc values into the + // connector state. `KmsDriver::create_objects()` runs before the mode-config-wide reset, + // so initialize our state through the driver's Rust reset callback when necessary. + let state = unsafe { (*self.as_raw()).state }; + if state.is_null() { + // SAFETY: `self` is a newly initialized `Connector<T>` and this unregistered typestate + // prevents concurrent access. The callback creates the matching `ConnectorState<T>`. + unsafe { connector_reset_callback::<T::State>(self.as_raw()) }; + } + + // SAFETY: `self` is initialized and now owns a connector state. The validated bounds fit + // the C API's signed integer parameters, and the helper only installs a DRM core property. + to_result(unsafe { + bindings::drm_connector_attach_max_bpc_property( + self.as_raw(), + min_bpc as i32, + max_bpc as i32, + ) + }) + } } /// Common methods available on any type which implements [`AsRawConnector`]. @@ -723,6 +777,36 @@ pub fn set_preferred_mode(&self, (h_pref, w_pref): (u32, u32)) { unsafe { bindings::drm_set_preferred_mode(self.as_raw(), h_pref, w_pref) } } + /// Add a driver-synthesised CVT mode to this connector's probed mode list. + /// + /// For a display whose EDID declares continuous frequencies, a driver may legitimately offer a + /// timing the EDID does not itself enumerate. `reduced` selects CVT reduced blanking (CVT-RB), + /// which matters when the *pixel clock* rather than the pixel rate is the constrained + /// resource -- RB cuts the clock for the same active pixels. + /// + /// Returns `EINVAL` if the core could not build the timing. + pub fn add_cvt_mode( + &self, + hdisplay: i32, + vdisplay: i32, + vrefresh: i32, + reduced: bool, + ) -> Result { + let dev = self.drm_dev().as_raw(); + // SAFETY: `dev` is this connector's live `drm_device`; `drm_cvt_mode` only computes a + // timing and allocates it, and we hold the mode-config lock via our type invariants. + let mode = unsafe { + bindings::drm_cvt_mode(dev, hdisplay, vdisplay, vrefresh, reduced, false, false) + }; + if mode.is_null() { + return Err(EINVAL); + } + // SAFETY: `mode` was just allocated by `drm_cvt_mode` and ownership passes to the + // connector here; we hold the locks required to modify its mode list. + unsafe { bindings::drm_mode_probed_add(self.as_raw(), mode) }; + Ok(()) + } + /// Parse an EDID, update the connector information, and add its advertised modes. /// /// Returns the number of modes added.