Re: [PATCH v4 18/20] drm/tyr: add CSF firmware interface support
From: Onur Özkan
Date: Mon Apr 27 2026 - 05:12:59 EST
On Fri, 24 Apr 2026 16:39:12 -0700
Deborah Brouwer <deborah.brouwer@xxxxxxxxxxxxx> wrote:
> Add initial support for the Command Stream Frontend (CSF) firmware
> interfaces, enabling communication between the driver and the MCU through
> shared memory.
>
> Implement the global (GLB), command stream group (CSG), and command stream
> (CS) interfaces. These provide access to the firmware control, input, and
> output blocks and allow discovery of the available CSGs and CSs at
> runtime.
>
> Store the global interface in the firmware state and initialize it after
> firmware boot during probe.
>
> Co-developed-by: Daniel Almeida <daniel.almeida@xxxxxxxxxxxxx>
> Signed-off-by: Daniel Almeida <daniel.almeida@xxxxxxxxxxxxx>
> Co-developed-by: Boris Brezillon <boris.brezillon@xxxxxxxxxxxxx>
> Signed-off-by: Boris Brezillon <boris.brezillon@xxxxxxxxxxxxx>
> Signed-off-by: Deborah Brouwer <deborah.brouwer@xxxxxxxxxxxxx>
> ---
> drivers/gpu/drm/tyr/driver.rs | 2 +-
> drivers/gpu/drm/tyr/fw.rs | 62 +-
> drivers/gpu/drm/tyr/fw/interfaces.rs | 2005 ++++++++++++++++++++++++++++++++++
> drivers/gpu/drm/tyr/gem.rs | 5 +
> 4 files changed, 2061 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
> index 3225385cd511..20ae114a4180 100644
> --- a/drivers/gpu/drm/tyr/driver.rs
> +++ b/drivers/gpu/drm/tyr/driver.rs
> @@ -189,10 +189,10 @@ fn probe(
> devres::register(pdev.as_ref(), job_irq, GFP_KERNEL)?;
>
> firmware.boot()?;
> -
> firmware
> .wait_ready(1000)
> .inspect_err(|_| pr_err!("Timed out waiting for firmware to be ready.\n"))?;
> + firmware.enable_global_interface()?;
>
> let data = try_pin_init!(TyrDrmDeviceData {
> pdev: platform.clone(),
> diff --git a/drivers/gpu/drm/tyr/fw.rs b/drivers/gpu/drm/tyr/fw.rs
> index 14815cdafac8..598e399a58ae 100644
> --- a/drivers/gpu/drm/tyr/fw.rs
> +++ b/drivers/gpu/drm/tyr/fw.rs
> @@ -36,7 +36,8 @@
> str::CString,
> sync::{
> Arc,
> - ArcBorrow, //
> + ArcBorrow,
> + Mutex, //
> },
> time,
> types::ARef, //
> @@ -47,9 +48,12 @@
> IoMem,
> TyrDrmDevice, //
> },
> - fw::parser::{
> - FwParser,
> - ParsedSection, //
> + fw::{
> + interfaces::GlobalInterface,
> + parser::{
> + FwParser,
> + ParsedSection, //
> + },
> },
> gem,
> gem::{
> @@ -73,12 +77,16 @@
> }, //
> };
>
> +mod interfaces;
> pub(crate) mod irq;
> mod parser;
>
> /// Maximum number of CSG interfaces supported by hardware.
> const MAX_CSG: usize = 16;
>
> +/// Maximum number of CS interfaces supported by hardware.
> +const MAX_CS: usize = 16;
> +
> impl_flags!(
> #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
> pub(super) struct SectionFlags(u32);
> @@ -100,6 +108,11 @@ pub(super) enum SectionFlag {
>
> pub(super) const CACHE_MODE_MASK: SectionFlags = SectionFlags(genmask_u32(3..=4));
>
> +/// MCU virtual address where the CSF shared memory region starts.
> +///
> +/// This region contains the firmware interface structures for communication between
> +/// the CPU driver and MCU firmware, including the GLB_CONTROL_BLOCK at this base address.
> +/// The firmware binary contains a section marked to be loaded at this address.
> pub(super) const CSF_MCU_SHARED_REGION_START: u32 = 0x04000000;
>
> impl SectionFlags {
> @@ -129,18 +142,18 @@ fn try_from(value: u32) -> Result<Self, Self::Error> {
> }
>
> /// A parsed section of the firmware binary.
> -struct Section {
> +pub(crate) struct Section {
> // Raw firmware section data for reset purposes
> #[expect(dead_code)]
> data: KVec<u8>,
>
> // Keep the BO backing this firmware section so that both the
> // GPU mapping and CPU mapping remain valid until the Section is dropped.
> - #[expect(dead_code)]
> mem: gem::KernelBo,
> }
>
> /// Loaded firmware with sections mapped into MCU VM.
> +#[pin_data(PinnedDrop)]
> pub(crate) struct Firmware {
> /// Platform device reference (needed to access the MCU JOB_IRQ registers).
> pdev: ARef<platform::Device>,
> @@ -152,7 +165,6 @@ pub(crate) struct Firmware {
> vm: Arc<Vm>,
>
> /// List of firmware sections.
> - #[expect(dead_code)]
> sections: KVec<Section>,
>
> /// A condvar representing a wait on a firmware event.
> @@ -160,10 +172,15 @@ pub(crate) struct Firmware {
>
> /// Latched to `true` by the IRQ handler when the firmware signals readiness via the GLB bit.
> pub(crate) fw_ready: Arc<AtomicBool>,
> +
> + /// The global FW interface.
> + #[pin]
> + global_iface: Mutex<GlobalInterface>,
> }
>
> -impl Drop for Firmware {
> - fn drop(&mut self) {
> +#[pinned_drop]
> +impl PinnedDrop for Firmware {
> + fn drop(self: Pin<&mut Self>) {
> // AS slots retain a VM ref, we need to kill the circular ref manually.
> self.vm.kill();
> }
> @@ -258,21 +275,36 @@ pub(crate) fn new(
> sections.push(Section { data, mem }, GFP_KERNEL)?;
> }
>
> - let firmware = Arc::new(
> - Firmware {
> + let firmware = Arc::pin_init(
> + try_pin_init!(Firmware {
> pdev: pdev.into(),
> iomem,
> vm,
> sections,
> ready_wait: new_wait!()?,
> fw_ready: Arc::new(AtomicBool::new(false), GFP_KERNEL)?,
> - },
> + global_iface <- new_mutex!(GlobalInterface::new()?),
> + }),
> GFP_KERNEL,
> )?;
>
> Ok(firmware)
> }
>
> + /// Get the shared memory section containing firmware interface structures.
> + pub(crate) fn shared_section(&self) -> Result<&Section> {
> + self.sections
> + .iter()
> + .find(|section| section.mem.va_range().start == u64::from(CSF_MCU_SHARED_REGION_START))
> + .ok_or_else(|| {
> + pr_err!(
> + "CSF shared section not found at 0x{:08x}\n",
> + CSF_MCU_SHARED_REGION_START
> + );
> + EINVAL
> + })
> + }
> +
> pub(crate) fn boot(&self) -> Result {
> // SAFETY: Boot is currently only called in the probe path, so we're sure we have a bound
> // device.
> @@ -303,4 +335,10 @@ pub(crate) fn wait_ready(&self, timeout_ms: u32) -> Result {
> }
> })
> }
> +
> + /// Enable the global interface.
> + pub(crate) fn enable_global_interface(&self) -> Result {
> + let shared_section = self.shared_section()?;
> + self.global_iface.lock().enable(shared_section)
> + }
> }
> diff --git a/drivers/gpu/drm/tyr/fw/interfaces.rs b/drivers/gpu/drm/tyr/fw/interfaces.rs
> new file mode 100644
> index 000000000000..07cdb1c76a3f
> --- /dev/null
> +++ b/drivers/gpu/drm/tyr/fw/interfaces.rs
> @@ -0,0 +1,2005 @@
> +// SPDX-License-Identifier: GPL-2.0 or MIT
> +
> +//! Code to control the global interface of the CSF firmware.
> +//!
> +//! For abbreviation definitions (CEU, CS, CSF, CSG, CSHW, GLB, JASID, MCU, MMU), see the top-level
> +//! module documentation in [`crate::regs`].
> +//!
> +//! # Interface Overview
> +//!
> +//! Tyr interacts with the CSF firmware running on the MCU through shared memory
> +//! interfaces. The CSF manages job submission via a hierarchy of:
> +//! - **GLB**: Global interface - controls operations common to all CSs
> +//! - **CSG**: Command Stream Groups - groups of related command streams
> +//! - **CS**: Command Streams - individual sequences of GPU commands
> +//!
> +//! ```
> +//! ┌──────────────────────────────────────────┐
> +//! │ GPU │
> +//! │ ┌─────┐ ┌──────────────────────────────┐ │
> +//! │ │ MMU │ │ CSF │ │
> +//! │ └─────┘ │ ┌────────────┐ ┌─────┐ │ │
> +//! │ │ │ CSHW (CEU) │ │ MCU │ │ │
> +//! │ │ └────────────┘ └─────┘ │ │
> +//! └─────────┼──────────────────────────────┼─┘
> +//! │ ┌──────────────────────────┐ │
> +//! │ │ Shared Memory │ │
> +//! │ │ ┌────────┐ ┌────┐ ┌────┐ │ │
> +//! │ │ │ CSG0 │ │GLB │ │ FW │ │ │
> +//! │ │ │ ┌────┐ │ └────┘ └────┘ │ │
> +//! │ │ │ │CS0 │ │ │ │
> +//! │ │ │ └────┘ │ │ │
> +//! │ │ └────────┘ │ │
> +//! │ └──────────────────────────┘ │
> +//! └──────────────┬───────────────┘
> +//! │
> +//! ┌───┴───┐
> +//! │ Tyr │
> +//! └───────┘
> +//! ```
> +//!
> +
> +use crate::fw::Section;
> +use iface::FwInterface;
> +use kernel::{
> + io::Io,
> + prelude::*, //
> +};
> +
> + }
[...]
> + }
> + }
> +}
> +
> +use cs::*;
> +use csg::*;
> +use glb::{
> + control::*,
> + *, //
> +};
Any reason having these imports at middle of the module?
> +
> +/// State of the global interface.
> +enum GlobalInterfaceState {
> + /// Interface is not yet initialized.
> + Disabled,
> + /// Interface is initialized and operational.
> + Enabled(EnabledGlobalInterface),
> +}
> +
> +/// When enabled, the Global Interface has control,
> +/// input, and output system memory interfaces, as well as
> +/// the discovered CSG interfaces.
> +#[expect(dead_code)]
> +struct EnabledGlobalInterface {
> + /// Control block interface - provides version, features, and CSG discovery.
> + glb_control: FwInterface<GLB_CONTROL_BLOCK_SIZE>,
> + /// Input block interface - driver writes requests here.
> + glb_input: FwInterface<GLB_INPUT_BLOCK_SIZE>,
> + /// Output block interface - firmware writes acknowledgements here.
> + glb_output: FwInterface<GLB_OUTPUT_BLOCK_SIZE>,
> + /// Runtime stride between CSG control blocks (read from GLB_GROUP_STRIDE).
> + csg_stride: usize,
> + /// Number of CSG interfaces reported by hardware.
> + csg_num: usize,
> + /// Discovered CSG interfaces.
> + csg: KVec<CsgInterface>,
> +}
> +
> +/// Global CSF Interface
> +///
> +/// The CSF controls operations that are common to all CSs.
> +pub(super) struct GlobalInterface {
> + /// Current interface state (Disabled or Enabled).
> + state: GlobalInterfaceState,
> +}
> +
> +impl GlobalInterface {
> + /// Creates a new CSF global interface, initially disabled.
> + pub(super) fn new() -> Result<Self> {
> + Ok(Self {
> + state: GlobalInterfaceState::Disabled,
> + })
> + }
> +
> + /// Enables the global interface and discovers the CSG interfaces.
> + ///
> + /// This reads the firmware's control block to set up the global input/output
> + /// interfaces; it configures timers and shader core allocation; and it discovers
> + /// available CSG interfaces.
> + pub(crate) fn enable(&mut self, shared_section: &Section) -> Result {
> + let vmap = shared_section.mem.bo.owned_vmap::<0>()?;
> + let va_range = shared_section.mem.va_range();
> +
> + let glb_control =
> + FwInterface::<GLB_CONTROL_BLOCK_SIZE>::new(&vmap, &va_range, va_range.start)?;
> +
> + let version = glb_control.read(GLB_VERSION);
> + if version.major().get() == 0 {
> + pr_err!("CSF interface version is 0. Firmware may have failed to boot.\n");
> + return Err(EINVAL);
> + }
> + pr_info!(
> + "CSF interface version: {}.{}.{}\n",
> + version.major().get(),
> + version.minor().get(),
> + version.patch().get()
> + );
> +
> + let input_va = glb_control.read(GLB_INPUT_VA);
> + let glb_input = FwInterface::<GLB_INPUT_BLOCK_SIZE>::new(
> + &vmap,
> + &va_range,
> + input_va.value().get().into(),
> + )?;
> +
> + let output_va = glb_control.read(GLB_OUTPUT_VA);
> + let glb_output = FwInterface::<GLB_OUTPUT_BLOCK_SIZE>::new(
> + &vmap,
> + &va_range,
> + output_va.value().get().into(),
> + )?;
> +
> + // Read how many CSG interfaces exist.
> + let csg_num = glb_control.read(GLB_GROUP_NUM).value().get();
> +
> + // Read the stride between CSG control blocks.
> + let csg_stride = glb_control.read(GLB_GROUP_STRIDE).value().get() as usize;
> +
> + if csg_stride < CSG_CONTROL_BLOCK_SIZE {
> + pr_err!(
> + "CSG stride {} is smaller than control block size {}\n",
> + csg_stride,
> + CSG_CONTROL_BLOCK_SIZE
> + );
> + return Err(EINVAL);
> + }
> +
> + // Validate the CSG number reported.
> + if csg_num as usize > super::MAX_CSG {
> + pr_err!(
> + "Too many CSGs: hardware reports {}, max supported {}\n",
> + csg_num,
> + super::MAX_CSG
> + );
> + return Err(EINVAL);
> + }
> +
> + let enabled = EnabledGlobalInterface {
> + glb_control,
> + glb_input,
> + glb_output,
> + csg_stride,
> + csg_num: csg_num as usize,
> + csg: KVec::with_capacity(csg_num as usize, GFP_KERNEL)?,
> + };
> +
> + self.state = GlobalInterfaceState::Enabled(enabled);
> + self.init_csg(shared_section)?;
> + Ok(())
> + }
> +
> + /// Initialize CSG interfaces.
> + ///
> + /// This uses the previously read CSG count to create and enable each CSG interface.
> + fn init_csg(&mut self, shared_section: &Section) -> Result {
> + let enabled = match &mut self.state {
> + GlobalInterfaceState::Enabled(e) => e,
> + GlobalInterfaceState::Disabled => return Err(EINVAL),
> + };
> +
> + for csg_idx in 0..enabled.csg_num {
> + // Create and enable the CSG interface.
> + let mut csg = CsgInterface::new(csg_idx)?;
> + csg.enable(shared_section, csg_idx, enabled.csg_stride)?;
> +
> + enabled.csg.push(csg, GFP_KERNEL)?;
> + }
> +
> + Ok(())
> + }
> +}
> +
> +/// State of a CSG interface.
> +enum CsgInterfaceState {
> + /// Interface is not yet initialized.
> + Disabled,
> + /// Interface is initialized and operational.
> + Enabled(EnabledCsgInterface),
> +}
> +
> +/// When enabled, a CSG Interface has control, input, and output system memory interfaces.
> +struct EnabledCsgInterface {
> + /// Control block interface - provides CSG capabilities and configuration.
> + #[expect(dead_code)]
> + csg_control: FwInterface<CSG_CONTROL_BLOCK_SIZE>,
> + /// Input block interface - driver writes CSG requests here.
> + #[expect(dead_code)]
> + csg_input: FwInterface<CSG_INPUT_BLOCK_SIZE>,
> + /// Output block interface - firmware writes CSG acknowledgements here.
> + #[expect(dead_code)]
> + csg_output: FwInterface<CSG_OUTPUT_BLOCK_SIZE>,
> + /// Runtime stride between CS control blocks (read from GROUP_STREAM_STRIDE).
> + cs_stride: usize,
> + /// Number of CS interfaces reported by hardware for this CSG.
> + cs_num: usize,
> + /// Discovered CS interfaces.
> + cs: KVec<CsInterface>,
> +}
> +
> +/// Command Stream Group Interface
> +///
> +/// The CSG interface controls operations for a specific CSG.
> +pub(crate) struct CsgInterface {
> + /// Current interface state (Disabled or Enabled).
> + state: CsgInterfaceState,
> + /// CSG identifier/index number.
> + #[expect(dead_code)]
> + csg_idx: usize,
> +}
> +
> +impl CsgInterface {
> + /// Creates a new disabled CSG interface.
> + pub(super) fn new(csg_idx: usize) -> Result<Self> {
> + Ok(Self {
> + state: CsgInterfaceState::Disabled,
> + csg_idx,
> + })
> + }
> +
> + /// Enables the CSG interface.
> + ///
> + /// This calculates the runtime offset of this CSG's control block and creates
> + /// a bounded interface to access it. It then reads the input/output interface
> + /// addresses from the CSG control block.
> + fn enable(&mut self, shared_section: &Section, csg_idx: usize, csg_stride: usize) -> Result {
> + use csg::control::{
> + GROUP_INPUT_VA,
> + GROUP_OUTPUT_VA,
> + GROUP_STREAM_NUM,
> + GROUP_STREAM_STRIDE, //
> + };
> + use kernel::io::Io;
Why are these imported inside the function? This is usually done when the
function is gated behind a feature flag to avoid clippy warnings when features
are disabled, but that doesn't seem to be the case here.
> +
> + let vmap = shared_section.mem.bo.owned_vmap::<0>()?;
> + let va_range = shared_section.mem.va_range();
> +
> + // Calculate the runtime offset for this CSG's control block.
> + // The CSG control blocks start at CSG_GROUP_CONTROL_OFFSET from the GLB control block,
> + // with each CSG spaced by csg_stride bytes.
> + let csg_control_offset = CSG_GROUP_CONTROL_OFFSET + csg_idx * csg_stride;
> +
> + // The CSG control block's MCU virtual address is relative to the shared section start.
> + let csg_control_va = va_range.start + csg_control_offset as u64;
> +
> + // Create a bounded interface for this CSG's control block at the calculated address.
> + let csg_control =
> + FwInterface::<CSG_CONTROL_BLOCK_SIZE>::new(&vmap, &va_range, csg_control_va)?;
> +
> + // Read the input and output VAs from the CSG control block.
> + let input_va = csg_control.read(GROUP_INPUT_VA).value().get();
> + let csg_input =
> + FwInterface::<CSG_INPUT_BLOCK_SIZE>::new(&vmap, &va_range, input_va.into())?;
> +
> + let output_va = csg_control.read(GROUP_OUTPUT_VA).value().get();
> + let csg_output =
> + FwInterface::<CSG_OUTPUT_BLOCK_SIZE>::new(&vmap, &va_range, output_va.into())?;
> +
> + // Read the runtime stride between CS control blocks.
> + let cs_stride = csg_control.read(GROUP_STREAM_STRIDE).value().get() as usize;
> +
> + if cs_stride < CS_CONTROL_BLOCK_SIZE {
> + pr_err!(
> + "CS stride {} is smaller than control block size {}\n",
> + cs_stride,
> + CS_CONTROL_BLOCK_SIZE
> + );
> + return Err(EINVAL);
> + }
> +
> + // Read how many CS interfaces exist for this CSG.
> + let cs_num = csg_control.read(GROUP_STREAM_NUM).value().get();
> +
> + // Validate that the hardware doesn't report more CS than we support.
> + if cs_num as usize > super::MAX_CS {
> + pr_err!(
> + "Too many CS: hardware reports {}, max supported {}\n",
> + cs_num,
> + super::MAX_CS
> + );
> + return Err(EINVAL);
> + }
> +
> + let enabled = EnabledCsgInterface {
> + csg_control,
> + csg_input,
> + csg_output,
> + cs_stride,
> + cs_num: cs_num as usize,
> + cs: KVec::with_capacity(cs_num as usize, GFP_KERNEL)?,
> + };
> +
> + self.state = CsgInterfaceState::Enabled(enabled);
> + self.init_cs(shared_section, csg_control_offset)?;
> + Ok(())
> + }
> +
> + /// Initialize and discover CS interfaces.
> + ///
> + /// This uses the previously read CS count to create and enable each CS interface.
> + fn init_cs(&mut self, shared_section: &Section, csg_control_offset: usize) -> Result {
> + let enabled = match &mut self.state {
> + CsgInterfaceState::Enabled(e) => e,
> + CsgInterfaceState::Disabled => return Err(EINVAL),
> + };
> +
> + for cs_idx in 0..enabled.cs_num {
> + // Create and enable the CS interface.
> + let mut cs = CsInterface::new(cs_idx)?;
> + cs.enable(
> + shared_section,
> + csg_control_offset,
> + cs_idx,
> + enabled.cs_stride,
> + )?;
> +
> + enabled.cs.push(cs, GFP_KERNEL)?;
> + }
> +
> + Ok(())
> + }
> +}
> +
> +/// State of a CS interface.
> +enum CsInterfaceState {
> + /// Interface is not yet initialized.
> + Disabled,
> + /// Interface is initialized and operational.
> + #[expect(dead_code)]
> + Enabled(EnabledCsInterface),
> +}
> +
> +/// When enabled, a CS Interface has control, input, and output system memory interfaces.
> +struct EnabledCsInterface {
> + /// Control block interface - provides CS capabilities and configuration.
> + #[expect(dead_code)]
> + cs_control: FwInterface<CS_CONTROL_BLOCK_SIZE>,
> + /// Input block interface - driver writes CS requests here.
> + #[expect(dead_code)]
> + cs_input: FwInterface<CS_KERNEL_INPUT_BLOCK_SIZE>,
> + /// Output block interface - firmware writes CS acknowledgements here.
> + #[expect(dead_code)]
> + cs_output: FwInterface<CS_KERNEL_OUTPUT_BLOCK_SIZE>,
> +}
> +
> +/// Command Stream Interface
> +///
> +/// The CS interface controls operations for a specific CS.
> +pub(crate) struct CsInterface {
> + /// Current interface state (Disabled or Enabled).
> + state: CsInterfaceState,
> + /// CS identifier/index number.
> + #[expect(dead_code)]
> + cs_idx: usize,
> +}
> +
> +impl CsInterface {
> + /// Creates a new disabled CS interface.
> + pub(super) fn new(cs_idx: usize) -> Result<Self> {
> + Ok(Self {
> + state: CsInterfaceState::Disabled,
> + cs_idx,
> + })
> + }
> +
> + /// Enables the CS interface.
> + ///
> + /// This calculates the runtime offset of this CS's control block and creates
> + /// a bounded interface to access it. It then reads the input/output interface
> + /// addresses from the CS control block.
> + fn enable(
> + &mut self,
> + shared_section: &Section,
> + csg_control_offset: usize,
> + cs_idx: usize,
> + cs_stride: usize,
> + ) -> Result {
> + use cs::control::{
> + STREAM_INPUT_VA,
> + STREAM_OUTPUT_VA, //
> + };
> + use kernel::io::Io;
> +
> + let vmap = shared_section.mem.bo.owned_vmap::<0>()?;
> + let va_range = shared_section.mem.va_range();
> +
> + // Calculate the runtime offset for this CS's control block.
> + let cs_control_offset = CS_CONTROL_OFFSET + cs_idx * cs_stride;
> +
> + // The CS control block's MCU virtual address is relative to the shared section start.
> + let cs_control_va = va_range.start + csg_control_offset as u64 + cs_control_offset as u64;
> +
> + // Create a bounded interface for this CS's control block at the calculated address.
> + let cs_control =
> + FwInterface::<CS_CONTROL_BLOCK_SIZE>::new(&vmap, &va_range, cs_control_va)?;
> +
> + // Read the input and output VAs from the CS control block.
> + let input_va = cs_control.read(STREAM_INPUT_VA).value().get();
> + let cs_input =
> + FwInterface::<CS_KERNEL_INPUT_BLOCK_SIZE>::new(&vmap, &va_range, input_va.into())?;
> +
> + let output_va = cs_control.read(STREAM_OUTPUT_VA).value().get();
> + let cs_output =
> + FwInterface::<CS_KERNEL_OUTPUT_BLOCK_SIZE>::new(&vmap, &va_range, output_va.into())?;
> +
> + let enabled = EnabledCsInterface {
> + cs_control,
> + cs_input,
> + cs_output,
> + };
> +
> + self.state = CsInterfaceState::Enabled(enabled);
> +
> + Ok(())
> + }
> +}
> diff --git a/drivers/gpu/drm/tyr/gem.rs b/drivers/gpu/drm/tyr/gem.rs
> index 4ec373e0bcfa..606d446aafd9 100644
> --- a/drivers/gpu/drm/tyr/gem.rs
> +++ b/drivers/gpu/drm/tyr/gem.rs
> @@ -151,6 +151,11 @@ pub(crate) fn new<Ctx: DeviceContext>(
> va_range: va..(va + size),
> })
> }
> +
> + /// Returns the GPU virtual address range occupied by this buffer.
> + pub(crate) fn va_range(&self) -> Range<u64> {
> + self.va_range.clone()
> + }
> }
>
> impl Drop for KernelBo {
>
> --
> 2.53.0
>