From: Deborah Brouwer <deborah.brouwer@collabora.com>
To: dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org
Cc: daniel.almeida@collabora.com, aliceryhl@google.com,
boris.brezillon@collabora.com, beata.michalska@arm.com,
lyude@redhat.com, work@onurozkan.dev,
Deborah Brouwer <deborah.brouwer@collabora.com>
Subject: [PATCH v2 08/12] drm/tyr: add MMU module
Date: Mon, 2 Mar 2026 15:24:56 -0800 [thread overview]
Message-ID: <20260302232500.244489-9-deborah.brouwer@collabora.com> (raw)
In-Reply-To: <20260302232500.244489-1-deborah.brouwer@collabora.com>
From: Boris Brezillon <boris.brezillon@collabora.com>
Add a Memory Management Unit (MMU) driver for Tyr. The MMU wraps a
SlotManager for allocating hardware address space slots. The underlying
AddressSpaceManager performs MMU operations including enabling/disabling
address spaces, flushing page tables, and locking regions for page table
updates.
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Co-developed-by: Deborah Brouwer <deborah.brouwer@collabora.com>
Signed-off-by: Deborah Brouwer <deborah.brouwer@collabora.com>
---
Changes in v2:
- Add documentation.
- Propagate errors returned by as_flush/disable().
- Refactor VmAsData to use in-place pinned initialization for the page table.
- Make struct AddressSpaceConfig private.
drivers/gpu/drm/tyr/driver.rs | 3 +
drivers/gpu/drm/tyr/mmu.rs | 123 ++++++
drivers/gpu/drm/tyr/mmu/address_space.rs | 493 +++++++++++++++++++++++
drivers/gpu/drm/tyr/tyr.rs | 1 +
4 files changed, 620 insertions(+)
create mode 100644 drivers/gpu/drm/tyr/mmu.rs
create mode 100644 drivers/gpu/drm/tyr/mmu/address_space.rs
diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
index 781502c6db36..7174ab2fd011 100644
--- a/drivers/gpu/drm/tyr/driver.rs
+++ b/drivers/gpu/drm/tyr/driver.rs
@@ -43,6 +43,7 @@
gem::BoData,
gpu,
gpu::GpuInfo,
+ mmu::Mmu,
regs, //
};
@@ -146,6 +147,8 @@ fn probe(
let uninit_ddev = UnregisteredDevice::<TyrDrmDriver>::new(pdev.as_ref())?;
let platform: ARef<platform::Device> = pdev.into();
+ let _mmu = Mmu::new(pdev, iomem.as_arc_borrow(), &gpu_info)?;
+
let data = try_pin_init!(TyrDrmDeviceData {
pdev: platform.clone(),
clks <- new_mutex!(Clocks {
diff --git a/drivers/gpu/drm/tyr/mmu.rs b/drivers/gpu/drm/tyr/mmu.rs
new file mode 100644
index 000000000000..2c7ac1fb1ac2
--- /dev/null
+++ b/drivers/gpu/drm/tyr/mmu.rs
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+
+//! Memory Management Unit (MMU) driver for the Tyr GPU.
+//!
+//! This module manages GPU address spaces and virtual memory (VM) operations through
+//! hardware MMU slots. It provides functionality for flushing page tables and
+//! managing VM updates for active address spaces.
+//!
+//! The MMU coordinates with the [`AddressSpaceManager`] to handle hardware
+//! address space allocation and page table operations, using [`SlotManager`]
+//! to track which address spaces are currently active in hardware slots.
+//!
+//! [`AddressSpaceManager`]: address_space::AddressSpaceManager
+//! [`SlotManager`]: crate::slot::SlotManager
+#![allow(dead_code)]
+
+use core::ops::Range;
+
+use kernel::{
+ devres::Devres,
+ new_mutex,
+ platform,
+ prelude::*,
+ sync::{
+ Arc,
+ ArcBorrow,
+ Mutex, //
+ }, //
+};
+
+use crate::{
+ driver::IoMem,
+ gpu::GpuInfo,
+ mmu::address_space::{
+ AddressSpaceManager,
+ VmAsData, //
+ },
+ regs::MAX_AS_REGISTERS,
+ slot::SlotManager, //
+};
+
+pub(crate) mod address_space;
+
+pub(crate) type AsSlotManager = SlotManager<AddressSpaceManager, MAX_AS_REGISTERS>;
+
+/// MMU component of the GPU.
+///
+/// This is used to bind VM objects to an AS (Address Space) slot
+/// and make the VM active on the GPU.
+///
+/// All operations acquire an internal lock, allowing concurrent access from multiple
+/// threads. Methods may block if another thread holds the lock.
+#[pin_data]
+pub(crate) struct Mmu {
+ /// Manages the allocation of hardware MMU slots to GPU address spaces.
+ ///
+ /// Tracks which address spaces are currently active in hardware slots and
+ /// coordinates address space operations like flushing and VM updates.
+ ///
+ /// This mutex also protects individual [`Seat`]s that are wrapped with
+ /// `LockedBy<Seat, SlotManager<...>>` to share the same lock protection.
+ ///
+ /// [`Seat`]: crate::slot::Seat
+ #[pin]
+ pub(crate) as_manager: Mutex<AsSlotManager>,
+}
+
+impl Mmu {
+ /// Create an MMU component for this device.
+ pub(crate) fn new(
+ pdev: &platform::Device,
+ iomem: ArcBorrow<'_, Devres<IoMem>>,
+ gpu_info: &GpuInfo,
+ ) -> Result<Arc<Mmu>> {
+ let slot_count = gpu_info.as_present.count_ones().try_into()?;
+ let as_manager = AddressSpaceManager::new(pdev, iomem, gpu_info.as_present)?;
+ let mmu_init = try_pin_init!(Self{
+ as_manager <- new_mutex!(SlotManager::new(as_manager, slot_count)?),
+ });
+ Arc::pin_init(mmu_init, GFP_KERNEL)
+ }
+
+ /// Make a VM active.
+ ///
+ /// This implies assigning the VM to an AS slot through the slot manager.
+ pub(crate) fn activate_vm(&self, vm: ArcBorrow<'_, VmAsData>) -> Result {
+ self.as_manager.lock().activate_vm(vm)
+ }
+
+ /// Make the VM inactive.
+ ///
+ /// Evicts the VM from its AS slot through the slot manager.
+ pub(crate) fn deactivate_vm(&self, vm: &VmAsData) -> Result {
+ self.as_manager.lock().deactivate_vm(vm)
+ }
+
+ /// Flush caches after a VM update.
+ ///
+ /// If the VM is no longer resident, this is a NOP, otherwise, the
+ /// AS manager will flush the GPU and MMU Translation Lookaside Buffer (TLB) caches.
+ pub(crate) fn flush_vm(&self, vm: &VmAsData) -> Result {
+ self.as_manager.lock().flush_vm(vm)
+ }
+
+ /// Flags the start of a VM update.
+ ///
+ /// If the VM is resident, any GPU access on the memory range being
+ /// updated will be blocked until `Mmu::end_vm_update()` is called.
+ /// This guarantees the atomicity of a VM update.
+ /// If the VM is not resident, this is a NOP.
+ pub(crate) fn start_vm_update(&self, vm: &VmAsData, region: &Range<u64>) -> Result {
+ self.as_manager.lock().start_vm_update(vm, region)
+ }
+
+ /// Flags the end of a VM update.
+ ///
+ /// If the VM is resident, this will let GPU accesses on the updated
+ /// range go through, in case any of them were blocked.
+ /// If the VM is not resident, this is a NOP.
+ pub(crate) fn end_vm_update(&self, vm: &VmAsData) -> Result {
+ self.as_manager.lock().end_vm_update(vm)
+ }
+}
diff --git a/drivers/gpu/drm/tyr/mmu/address_space.rs b/drivers/gpu/drm/tyr/mmu/address_space.rs
new file mode 100644
index 000000000000..05ada3274466
--- /dev/null
+++ b/drivers/gpu/drm/tyr/mmu/address_space.rs
@@ -0,0 +1,493 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+
+//! GPU address space management and hardware operations.
+//!
+//! This module manages GPU hardware address spaces (AS), including configuration,
+//! command submission, and page table update regions. It handles the hardware
+//! interaction for MMU operations through MMIO register access.
+//!
+//! The [`AddressSpaceManager`] implements [`SlotOperations`] to integrate with
+//! the slot management system, enabling and configuring address spaces in the
+//! hardware slots as needed.
+//!
+//! [`SlotOperations`]: crate::slot::SlotOperations
+
+use core::ops::Range;
+
+use kernel::{
+ bits::*,
+ device::{
+ Bound,
+ Device, //
+ },
+ devres::Devres,
+ error::Result,
+ io,
+ iommu::pgtable::{
+ Config,
+ IoPageTable,
+ ARM64LPAES1, //
+ },
+ platform,
+ prelude::*,
+ sizes::{
+ SZ_2M,
+ SZ_4K, //
+ },
+ sync::{
+ aref::ARef,
+ Arc,
+ ArcBorrow,
+ LockedBy, //
+ },
+ time::Delta, //
+};
+
+use crate::{
+ driver::IoMem,
+ mmu::{
+ AsSlotManager,
+ Mmu, //
+ },
+ regs::*,
+ slot::{
+ Seat,
+ SlotOperations, //
+ }, //
+};
+
+/// Hardware address space configuration registers.
+///
+/// Contains register values for configuring a GPU MMU address space.
+#[derive(Clone, Copy)]
+struct AddressSpaceConfig {
+ /// Translation configuration.
+ ///
+ /// Controls address translation mode, address range restrictions, translation table
+ /// walk attributes, and access permission settings for this address space.
+ transcfg: u64,
+
+ /// Translation table base address.
+ ///
+ /// The address of the top level of a translation table structure.
+ transtab: u64,
+
+ /// Memory attributes.
+ ///
+ /// Defines memory attribute indirection entries that control cacheability
+ /// and other memory access properties for the address space.
+ memattr: u64,
+}
+
+/// Virtual memory (VM) address space data for GPU MMU operations.
+///
+/// Contains all resources and information needed by the [`AddressSpaceManager`]
+/// to activate a VM in a hardware address space slot.
+///
+/// On activation, we will pass an [`Arc`]<[`VmAsData`]> that will be stored in
+/// the slot to make sure the page table and the underlying resources
+/// (pages) used by the AS slot won't go away while the MMU points to
+/// those.
+///
+/// The `as_seat` field uses [`LockedBy`] to ensure safe concurrent access to
+/// the slot assignment state, protected by the [`AsSlotManager`] lock.
+#[pin_data]
+pub(crate) struct VmAsData {
+ /// Tracks this VM's binding to a hardware address space slot.
+ as_seat: LockedBy<Seat, AsSlotManager>,
+
+ /// Virtual address bits for this address space.
+ va_bits: u8,
+
+ /// Page table.
+ ///
+ /// Managed by devres to ensure proper cleanup. The page table maps
+ /// GPU virtual addresses to physical addresses for this VM.
+ #[pin]
+ pub(crate) page_table: Devres<IoPageTable<ARM64LPAES1>>,
+}
+
+impl VmAsData {
+ /// Creates a new VM address space data structure.
+ ///
+ /// Initializes the page table for the address space.
+ pub(crate) fn new<'a>(
+ mmu: &'a Mmu,
+ pdev: &'a platform::Device,
+ va_bits: u32,
+ pa_bits: u32,
+ ) -> impl pin_init::PinInit<VmAsData, Error> + 'a {
+ // SAFETY: pdev is a bound device.
+ let dev = unsafe { pdev.as_ref().as_bound() };
+
+ let pt_config = Config {
+ quirks: 0,
+ pgsize_bitmap: SZ_4K | SZ_2M,
+ ias: va_bits,
+ oas: pa_bits,
+ coherent_walk: false,
+ };
+
+ let page_table_init = IoPageTable::new(dev, pt_config);
+
+ try_pin_init!(Self {
+ as_seat: LockedBy::new(&mmu.as_manager, Seat::NoSeat),
+ va_bits: va_bits as u8,
+ page_table <- page_table_init,
+ }? Error)
+ }
+
+ /// Computes the hardware configuration for this address space.
+ ///
+ /// The caller must ensure that the address space is evicted and cleaned up
+ /// before the `VmAsData` is dropped.
+ fn as_config(&self, dev: &Device<Bound>) -> Result<AddressSpaceConfig> {
+ let pt = self.page_table.access(dev)?;
+
+ Ok(AddressSpaceConfig {
+ transcfg: AS_TRANSCFG_PTW_MEMATTR_WB
+ | AS_TRANSCFG_PTW_RA
+ | AS_TRANSCFG_ADRMODE_AARCH64_4K
+ | as_transcfg_ina_bits(u64::from(55 - self.va_bits)),
+ // SAFETY: Caller ensures proper cleanup.
+ transtab: unsafe { pt.ttbr() },
+ memattr: mair_to_memattr(pt.mair()),
+ })
+ }
+}
+
+/// Converts AArch64 MAIR register value to GPU memory attributes.
+fn mair_to_memattr(mair: u64) -> u64 {
+ let mut memattr: u64 = 0;
+
+ for i in 0..8 {
+ let in_attr = (mair >> (8 * i)) as u8;
+ let outer = in_attr >> 4;
+ let inner = in_attr & 0xf;
+
+ // For caching to be enabled, inner and outer caching policy
+ // have to be both write-back, if one of them is write-through
+ // or non-cacheable, we just choose non-cacheable. Device
+ // memory is also translated to non-cacheable.
+ let out_attr = if (outer & 3 == 0) || (outer & 4 == 0) || (inner & 4 == 0) {
+ AS_MEMATTR_AARCH64_INNER_OUTER_NC
+ | AS_MEMATTR_AARCH64_SH_MIDGARD_INNER
+ | as_memattr_aarch64_inner_alloc_expl(false, false)
+ } else {
+ // Use SH_CPU_INNER mode so SH_IS, which is used when
+ // IOMMU_CACHE is set, actually maps to the standard
+ // definition of inner-shareable and not Mali's
+ // internal-shareable mode.
+ //
+ // TODO: this assumes a non-coherent system.
+ AS_MEMATTR_AARCH64_INNER_OUTER_WB
+ | AS_MEMATTR_AARCH64_SH_MIDGARD_INNER
+ | as_memattr_aarch64_inner_alloc_expl(inner & 1 != 0, inner & 2 != 0)
+ };
+
+ memattr |= (u64::from(out_attr)) << (8 * i);
+ }
+
+ memattr
+}
+
+/// Manages GPU hardware address spaces via MMIO register operations.
+///
+/// Coordinates all hardware-level address space operations including enabling,
+/// disabling, flushing, and updating address spaces. Implements [`SlotOperations`]
+/// to integrate with the generic slot management system.
+///
+/// [`SlotOperations`]: crate::slot::SlotOperations
+pub(crate) struct AddressSpaceManager {
+ /// Platform device reference for DMA and device operations.
+ pdev: ARef<platform::Device>,
+
+ /// Memory-mapped I/O region for GPU register access.
+ iomem: Arc<Devres<IoMem>>,
+
+ /// Bitmask of available address space slots from GPU_AS_PRESENT register.
+ as_present: u32,
+}
+
+impl SlotOperations for AddressSpaceManager {
+ /// VM address space data stored in each hardware slot.
+ type SlotData = Arc<VmAsData>;
+
+ /// Activates an address space in a hardware slot.
+ fn activate(&mut self, slot_idx: usize, slot_data: &Self::SlotData) -> Result {
+ let as_config = slot_data.as_config(self.dev())?;
+ self.as_enable(slot_idx, &as_config)
+ }
+
+ /// Evicts an address space from a hardware slot.
+ fn evict(&mut self, slot_idx: usize, _slot_data: &Self::SlotData) -> Result {
+ if self.iomem.try_access().is_some() {
+ self.as_flush(slot_idx)?;
+ self.as_disable(slot_idx)?;
+ }
+ Ok(())
+ }
+}
+
+impl AddressSpaceManager {
+ /// Creates a new address space manager.
+ ///
+ /// Initializes the manager with references to the platform device and
+ /// I/O memory region, along with the bitmask of available AS slots.
+ pub(super) fn new(
+ pdev: &platform::Device,
+ iomem: ArcBorrow<'_, Devres<IoMem>>,
+ as_present: u32,
+ ) -> Result<AddressSpaceManager> {
+ Ok(Self {
+ pdev: pdev.into(),
+ iomem: iomem.into(),
+ as_present,
+ })
+ }
+
+ /// Returns a reference to the bound device.
+ fn dev(&self) -> &Device<Bound> {
+ // SAFETY: pdev is a bound device.
+ unsafe { self.pdev.as_ref().as_bound() }
+ }
+
+ /// Validates that an AS slot number is within range and present in hardware.
+ ///
+ /// Checks that the slot index is less than [`MAX_AS_REGISTERS`] and that
+ /// the corresponding bit is set in the `as_present` mask read from the GPU.
+ ///
+ /// Returns [`EINVAL`] if the slot is out of range or not present in hardware.
+ fn validate_as_slot(&self, as_nr: usize) -> Result {
+ if as_nr >= MAX_AS_REGISTERS {
+ pr_err!(
+ "AS slot {} out of valid range (max {})\n",
+ as_nr,
+ MAX_AS_REGISTERS
+ );
+ return Err(EINVAL);
+ }
+
+ if (self.as_present & (1 << as_nr)) == 0 {
+ pr_err!(
+ "AS slot {} not present in hardware (AS_PRESENT={:#x})\n",
+ as_nr,
+ self.as_present
+ );
+ return Err(EINVAL);
+ }
+
+ Ok(())
+ }
+
+ /// Waits for an AS slot to become ready (not active).
+ ///
+ /// Returns an error if polling times out after 10ms or if register access fails.
+ fn as_wait_ready(&self, as_nr: usize) -> Result {
+ let op = || as_status(as_nr)?.read(self.dev(), &self.iomem);
+ let cond = |status: &u32| -> bool { *status & AS_STATUS_ACTIVE == 0 };
+ let _ =
+ io::poll::read_poll_timeout(op, cond, Delta::from_millis(0), Delta::from_millis(10))?;
+
+ Ok(())
+ }
+
+ /// Sends a command to an AS slot.
+ ///
+ /// Returns an error if waiting for ready times out or if register write fails.
+ fn as_send_cmd(&mut self, as_nr: usize, cmd: u32) -> Result {
+ self.as_wait_ready(as_nr)?;
+ as_command(as_nr)?.write(self.dev(), &self.iomem, cmd)?;
+ Ok(())
+ }
+
+ /// Sends a command to an AS slot and waits for completion.
+ ///
+ /// Returns an error if sending the command fails or if waiting for completion times out.
+ fn as_send_cmd_and_wait(&mut self, as_nr: usize, cmd: u32) -> Result {
+ self.as_send_cmd(as_nr, cmd)?;
+ self.as_wait_ready(as_nr)?;
+ Ok(())
+ }
+
+ /// Enables an AS slot with the provided configuration.
+ ///
+ /// Returns an error if the slot is invalid or if register writes/commands fail.
+ fn as_enable(&mut self, as_nr: usize, as_config: &AddressSpaceConfig) -> Result {
+ self.validate_as_slot(as_nr)?;
+
+ let transtab = as_config.transtab;
+ let transcfg = as_config.transcfg;
+ let memattr = as_config.memattr;
+
+ let transtab_lo = (transtab & 0xffffffff) as u32;
+ let transtab_hi = (transtab >> 32) as u32;
+
+ let transcfg_lo = (transcfg & 0xffffffff) as u32;
+ let transcfg_hi = (transcfg >> 32) as u32;
+
+ let memattr_lo = (memattr & 0xffffffff) as u32;
+ let memattr_hi = (memattr >> 32) as u32;
+
+ let dev = self.dev();
+ as_transtab_lo(as_nr)?.write(dev, &self.iomem, transtab_lo)?;
+ as_transtab_hi(as_nr)?.write(dev, &self.iomem, transtab_hi)?;
+
+ as_transcfg_lo(as_nr)?.write(dev, &self.iomem, transcfg_lo)?;
+ as_transcfg_hi(as_nr)?.write(dev, &self.iomem, transcfg_hi)?;
+
+ as_memattr_lo(as_nr)?.write(dev, &self.iomem, memattr_lo)?;
+ as_memattr_hi(as_nr)?.write(dev, &self.iomem, memattr_hi)?;
+
+ self.as_send_cmd_and_wait(as_nr, AS_COMMAND_UPDATE)?;
+
+ Ok(())
+ }
+
+ /// Disables an AS slot and clears its configuration.
+ ///
+ /// Returns an error if the slot is invalid or if register writes/commands fail.
+ fn as_disable(&mut self, as_nr: usize) -> Result {
+ self.validate_as_slot(as_nr)?;
+
+ // Flush AS before disabling
+ self.as_send_cmd_and_wait(as_nr, AS_COMMAND_FLUSH_MEM)?;
+
+ let dev = self.dev();
+ as_transtab_lo(as_nr)?.write(dev, &self.iomem, 0)?;
+ as_transtab_hi(as_nr)?.write(dev, &self.iomem, 0)?;
+
+ as_memattr_lo(as_nr)?.write(dev, &self.iomem, 0)?;
+ as_memattr_hi(as_nr)?.write(dev, &self.iomem, 0)?;
+
+ as_transcfg_lo(as_nr)?.write(dev, &self.iomem, AS_TRANSCFG_ADRMODE_UNMAPPED as u32)?;
+ as_transcfg_hi(as_nr)?.write(dev, &self.iomem, 0)?;
+
+ self.as_send_cmd_and_wait(as_nr, AS_COMMAND_UPDATE)?;
+
+ Ok(())
+ }
+
+ /// Starts an atomic translation table update for a memory region.
+ ///
+ /// Returns an error if the slot is invalid or if register writes/commands fail.
+ fn as_start_update(&mut self, as_nr: usize, region: &Range<u64>) -> Result {
+ self.validate_as_slot(as_nr)?;
+
+ // The locked region is a naturally aligned power of 2 block encoded as
+ // log2 minus 1.
+ //
+ // Calculate the desired start/end and look for the highest bit which
+ // differs. The smallest naturally aligned block must include this bit
+ // change, the desired region starts with this bit (and subsequent bits)
+ // zeroed and ends with the bit (and subsequent bits) set to one.
+ let region_width = core::cmp::max(
+ 64 - (region.start ^ (region.end - 1)).leading_zeros() as u8,
+ AS_LOCK_REGION_MIN_SIZE.trailing_zeros() as u8,
+ ) - 1;
+
+ // Mask off the low bits of region.start, which would be ignored by the
+ // hardware anyways.
+ let region_start =
+ region.start & genmask_checked_u64(u32::from(region_width)..=63).ok_or(EINVAL)?;
+
+ let region = (u64::from(region_width)) | region_start;
+
+ let region_lo = (region & 0xffffffff) as u32;
+ let region_hi = (region >> 32) as u32;
+
+ // Lock the region that needs to be updated.
+ let dev = self.dev();
+ as_lockaddr_lo(as_nr)?.write(dev, &self.iomem, region_lo)?;
+ as_lockaddr_hi(as_nr)?.write(dev, &self.iomem, region_hi)?;
+
+ self.as_send_cmd(as_nr, AS_COMMAND_LOCK)
+ }
+
+ /// Completes an atomic translation table update.
+ ///
+ /// Returns an error if the slot is invalid or if the flush command fails.
+ fn as_end_update(&mut self, as_nr: usize) -> Result {
+ self.validate_as_slot(as_nr)?;
+ self.as_send_cmd_and_wait(as_nr, AS_COMMAND_FLUSH_PT)
+ }
+
+ /// Flushes the translation table cache for an AS slot.
+ ///
+ /// Returns an error if the slot is invalid or if the flush command fails.
+ fn as_flush(&mut self, as_nr: usize) -> Result {
+ self.validate_as_slot(as_nr)?;
+ self.as_send_cmd(as_nr, AS_COMMAND_FLUSH_PT)
+ }
+}
+
+impl AsSlotManager {
+ /// Locks a region for translation table updates if the VM has an active slot.
+ ///
+ /// If the VM is currently assigned to a hardware slot, locks the specified
+ /// memory region to make translation table updates atomic. GPU accesses to the
+ /// region will be blocked until [`end_vm_update`] is called.
+ ///
+ /// If the VM is not resident in a hardware slot, this is a no-op.
+ pub(super) fn start_vm_update(&mut self, vm: &VmAsData, region: &Range<u64>) -> Result {
+ let seat = vm.as_seat.access(self);
+ match seat.slot() {
+ Some(slot) => {
+ let as_nr = slot as usize;
+ self.as_start_update(as_nr, region)
+ }
+ _ => Ok(()),
+ }
+ }
+
+ /// Completes translation table updates and unlocks the region.
+ ///
+ /// If the VM is currently assigned to a hardware slot, flushes the translation
+ /// table cache and unlocks the region that was locked by [`start_vm_update`],
+ /// allowing GPU accesses to proceed with the updated translation tables.
+ ///
+ /// If the VM is not resident in a hardware slot, this is a no-op.
+ pub(super) fn end_vm_update(&mut self, vm: &VmAsData) -> Result {
+ let seat = vm.as_seat.access(self);
+ match seat.slot() {
+ Some(slot) => {
+ let as_nr = slot as usize;
+ self.as_end_update(as_nr)
+ }
+ _ => Ok(()),
+ }
+ }
+
+ /// Flushes translation table cache if the VM has an active slot.
+ ///
+ /// If the VM is currently assigned to a hardware slot, invalidates cached
+ /// translation table entries to ensure subsequent GPU accesses use updated translations.
+ ///
+ /// If the VM is not resident in a hardware slot, this is a no-op.
+ pub(super) fn flush_vm(&mut self, vm: &VmAsData) -> Result {
+ let seat = vm.as_seat.access(self);
+ match seat.slot() {
+ Some(slot) => {
+ let as_nr = slot as usize;
+ self.as_flush(as_nr)
+ }
+ _ => Ok(()),
+ }
+ }
+
+ /// Activates a VM by assigning it to a hardware slot.
+ ///
+ /// Allocates a hardware address space slot for the VM and configures
+ /// it with the VM's translation table and memory attributes.
+ pub(super) fn activate_vm(&mut self, vm: ArcBorrow<'_, VmAsData>) -> Result {
+ self.activate(&vm.as_seat, vm.into())
+ }
+
+ /// Deactivates a VM by evicting it from its hardware slot.
+ ///
+ /// Flushes any pending operations and clears the hardware slot's
+ /// configuration, freeing the slot for use by other VMs.
+ pub(super) fn deactivate_vm(&mut self, vm: &VmAsData) -> Result {
+ self.evict(&vm.as_seat)
+ }
+}
diff --git a/drivers/gpu/drm/tyr/tyr.rs b/drivers/gpu/drm/tyr/tyr.rs
index 20b38120e20e..9f9f31ea02e3 100644
--- a/drivers/gpu/drm/tyr/tyr.rs
+++ b/drivers/gpu/drm/tyr/tyr.rs
@@ -11,6 +11,7 @@
mod file;
mod gem;
mod gpu;
+mod mmu;
mod regs;
mod slot;
--
2.52.0
next prev parent reply other threads:[~2026-03-02 23:25 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-02 23:24 [PATCH v2 0/12] drm/tyr: firmware loading and MCU boot support Deborah Brouwer
2026-03-02 23:24 ` [PATCH v2 01/12] drm/tyr: select DRM abstractions in Kconfig Deborah Brouwer
2026-03-03 2:48 ` Claude review: " Claude Code Review Bot
2026-03-02 23:24 ` [PATCH v2 02/12] drm/tyr: move clock cleanup into Clocks Drop impl Deborah Brouwer
2026-03-03 2:48 ` Claude review: " Claude Code Review Bot
2026-03-02 23:24 ` [PATCH v2 03/12] drm/tyr: rename TyrObject to BoData Deborah Brouwer
2026-03-03 2:48 ` Claude review: " Claude Code Review Bot
2026-03-02 23:24 ` [PATCH v2 04/12] drm/tyr: set DMA mask using GPU physical address Deborah Brouwer
2026-03-03 2:48 ` Claude review: " Claude Code Review Bot
2026-03-02 23:24 ` [PATCH v2 05/12] drm/tyr: add MMU address space registers Deborah Brouwer
2026-03-03 2:48 ` Claude review: " Claude Code Review Bot
2026-03-02 23:24 ` [PATCH v2 06/12] drm/tyr: add shmem backing for GEM objects Deborah Brouwer
2026-03-03 2:48 ` Claude review: " Claude Code Review Bot
2026-03-02 23:24 ` [PATCH v2 07/12] drm/tyr: Add generic slot manager Deborah Brouwer
2026-03-03 2:48 ` Claude review: " Claude Code Review Bot
2026-03-02 23:24 ` Deborah Brouwer [this message]
2026-03-03 2:48 ` Claude review: drm/tyr: add MMU module Claude Code Review Bot
2026-03-02 23:24 ` [PATCH v2 09/12] drm/tyr: add GPU virtual memory module Deborah Brouwer
2026-03-03 2:48 ` Claude review: " Claude Code Review Bot
2026-03-02 23:24 ` [PATCH v2 10/12] drm/tyr: add a kernel buffer object Deborah Brouwer
2026-03-03 2:48 ` Claude review: " Claude Code Review Bot
2026-03-02 23:24 ` [PATCH v2 11/12] drm/tyr: add parser for firmware binary Deborah Brouwer
2026-03-03 2:48 ` Claude review: " Claude Code Review Bot
2026-03-02 23:25 ` [PATCH v2 12/12] drm/tyr: add firmware loading and MCU boot support Deborah Brouwer
2026-03-03 2:48 ` Claude review: " Claude Code Review Bot
2026-03-03 2:48 ` Claude review: drm/tyr: " Claude Code Review Bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260302232500.244489-9-deborah.brouwer@collabora.com \
--to=deborah.brouwer@collabora.com \
--cc=aliceryhl@google.com \
--cc=beata.michalska@arm.com \
--cc=boris.brezillon@collabora.com \
--cc=daniel.almeida@collabora.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=lyude@redhat.com \
--cc=rust-for-linux@vger.kernel.org \
--cc=work@onurozkan.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox