drm: implement AMD private CS submit/wait via SDMA ring buffer
- Add RingManager::submit_batch() — submits dwords with auto-fence - Add RingManager::last_seqno() — returns latest sequence number - Add RingManager::sync_from_hw() — reads hardware-completed seqno from fence buffer - Add RingManager::flush() — memory fence + hardware sync - Wire AmdDriver::redox_private_cs_submit — reads batch from GEM, submits to SDMA ring with flush, returns fence seqno - Wire AmdDriver::redox_private_cs_wait — polls SDMA fence seqno with configurable timeout, uses ring.sync_from_hw() for hardware completion tracking Replaces the Unsupported stub. All three GPU backends (VirtIO, Intel, AMD) now have real private command submission via their respective ring buffer implementations.
This commit is contained in:
@@ -10,7 +10,10 @@ use log::{debug, info, warn};
|
||||
use redox_driver_sys::memory::MmioRegion;
|
||||
use redox_driver_sys::pci::{PciBarInfo, PciDevice, PciDeviceInfo};
|
||||
|
||||
use crate::driver::{DriverError, DriverEvent, GpuDriver, Result};
|
||||
use crate::driver::{
|
||||
DriverError, DriverEvent, GpuDriver, RedoxPrivateCsSubmit, RedoxPrivateCsSubmitResult,
|
||||
RedoxPrivateCsWait, RedoxPrivateCsWaitResult, Result,
|
||||
};
|
||||
use crate::drivers::interrupt::InterruptHandle;
|
||||
use crate::gem::{GemHandle, GemManager};
|
||||
use crate::kms::connector::{synthetic_edid, Connector};
|
||||
@@ -585,6 +588,130 @@ impl GpuDriver for AmdDriver {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn redox_private_cs_submit(
|
||||
&self,
|
||||
submit: &RedoxPrivateCsSubmit,
|
||||
) -> Result<RedoxPrivateCsSubmitResult> {
|
||||
let dword_count = (submit.byte_count / core::mem::size_of::<u32>() as u64) as usize;
|
||||
if dword_count == 0 || dword_count > 1024 {
|
||||
return Err(DriverError::InvalidArgument(
|
||||
"AMD batch size out of range",
|
||||
));
|
||||
}
|
||||
|
||||
let gem = self
|
||||
.gem
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Buffer("AMD GEM poisoned".into()))?;
|
||||
let object = gem.object(submit.src_handle)?;
|
||||
if submit.src_offset + submit.byte_count > object.size {
|
||||
return Err(DriverError::InvalidArgument(
|
||||
"AMD batch read past GEM end",
|
||||
));
|
||||
}
|
||||
|
||||
let src_ptr = unsafe {
|
||||
(object.virt_addr as *const u8).add(submit.src_offset as usize)
|
||||
};
|
||||
let mut dwords = vec![0u32; dword_count];
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(
|
||||
src_ptr as *const u32,
|
||||
dwords.as_mut_ptr(),
|
||||
dwords.len(),
|
||||
);
|
||||
}
|
||||
drop(gem);
|
||||
|
||||
let mut ring = self
|
||||
.ring
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Initialization("AMD ring poisoned".into()))?;
|
||||
ring.submit_batch(&dwords)
|
||||
.map_err(|e| DriverError::Io(format!("AMD ring submit failed: {e}")))?;
|
||||
ring.flush()
|
||||
.map_err(|e| DriverError::Io(format!("AMD ring flush failed: {e}")))?;
|
||||
let seqno = ring.last_seqno();
|
||||
|
||||
Ok(RedoxPrivateCsSubmitResult { seqno })
|
||||
}
|
||||
|
||||
fn redox_private_cs_wait(
|
||||
&self,
|
||||
wait: &RedoxPrivateCsWait,
|
||||
) -> Result<RedoxPrivateCsWaitResult> {
|
||||
let mut ring = self
|
||||
.ring
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Initialization("AMD ring poisoned".into()))?;
|
||||
ring.sync_from_hw()
|
||||
.map_err(|e| DriverError::Io(format!("AMD ring sync_from_hw: {e}")))?;
|
||||
let current = ring.last_seqno();
|
||||
drop(ring);
|
||||
|
||||
if current >= wait.seqno {
|
||||
return Ok(RedoxPrivateCsWaitResult {
|
||||
completed: true,
|
||||
completed_seqno: current,
|
||||
});
|
||||
}
|
||||
|
||||
if wait.timeout_ns == 0 {
|
||||
return Ok(RedoxPrivateCsWaitResult {
|
||||
completed: false,
|
||||
completed_seqno: current,
|
||||
});
|
||||
}
|
||||
|
||||
let start = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map(|d| d.as_nanos())
|
||||
.unwrap_or(0);
|
||||
|
||||
for _ in 0..2_000_000u64 {
|
||||
let mut ring = self
|
||||
.ring
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Initialization("AMD ring poisoned".into()))?;
|
||||
ring.sync_from_hw()
|
||||
.map_err(|e| DriverError::Io(format!("AMD ring sync_from_hw: {e}")))?;
|
||||
let cur = ring.last_seqno();
|
||||
drop(ring);
|
||||
|
||||
if cur >= wait.seqno {
|
||||
return Ok(RedoxPrivateCsWaitResult {
|
||||
completed: true,
|
||||
completed_seqno: cur,
|
||||
});
|
||||
}
|
||||
|
||||
if wait.timeout_ns > 0 {
|
||||
let now = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map(|d| d.as_nanos())
|
||||
.unwrap_or(0);
|
||||
if now.saturating_sub(start) >= wait.timeout_ns as u128 {
|
||||
return Ok(RedoxPrivateCsWaitResult {
|
||||
completed: false,
|
||||
completed_seqno: cur,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
let cur = self
|
||||
.ring
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Initialization("AMD ring poisoned".into()))?
|
||||
.last_seqno();
|
||||
Ok(RedoxPrivateCsWaitResult {
|
||||
completed: false,
|
||||
completed_seqno: cur,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn detect_display_topology(display: &DisplayCore) -> Result<(Vec<Connector>, Vec<Encoder>)> {
|
||||
|
||||
@@ -168,6 +168,45 @@ impl RingManager {
|
||||
self.submit(&packet, seqno)
|
||||
}
|
||||
|
||||
pub fn submit_batch(&mut self, buffer: &[u32]) -> Result<()> {
|
||||
self.ensure_initialized()?;
|
||||
|
||||
let seqno = self.next_seqno;
|
||||
self.next_seqno = self.next_seqno.saturating_add(1);
|
||||
|
||||
let mut packet = Vec::with_capacity(buffer.len() + 4);
|
||||
packet.extend_from_slice(buffer);
|
||||
self.emit_fence(&mut packet, seqno)?;
|
||||
|
||||
self.submit(&packet, seqno)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn last_seqno(&self) -> u64 {
|
||||
self.next_seqno.saturating_sub(1)
|
||||
}
|
||||
|
||||
pub fn sync_from_hw(&mut self) -> Result<()> {
|
||||
self.ensure_initialized()?;
|
||||
self.refresh_read_ptr();
|
||||
|
||||
if let Some(ref fence) = self.fence_buffer {
|
||||
let ptr = unsafe { fence.as_ptr().add(FENCE_OFFSET_BYTES) as *const u64 };
|
||||
let completed = unsafe { core::ptr::read_volatile(ptr) };
|
||||
if completed > self.last_signaled_seqno {
|
||||
self.last_signaled_seqno = completed;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn flush(&mut self) -> Result<()> {
|
||||
self.ensure_initialized()?;
|
||||
fence(Ordering::SeqCst);
|
||||
self.sync_from_hw()
|
||||
}
|
||||
|
||||
pub(crate) fn bind_mmio(mmio: &MmioRegion) {
|
||||
MMIO_BASE.store(mmio.as_ptr() as *mut u8, Ordering::Release);
|
||||
MMIO_SIZE.store(mmio.size(), Ordering::Release);
|
||||
|
||||
Reference in New Issue
Block a user