intel: execlists — GPU execution list submission (Phase 4)
Add execlists.rs implementing GPU context submission via execlist ports. - ExeclistPort: manages 2-slot ELSP (Execlist Submission Port) at RING_ELSP (base + 0x230), context control (base + 0x244), context status pointer (base + 0x3A0), and execlist control (base + 0x550) - init(): enable execlist control, configure context restore inhibit + RS context enable, clear CSB pointer - submit(): queue ExeclistContext to next available ELSP slot, flush to hardware via ELSP register writes - check_completion(): read RING_EXECLIST_STATUS_LO for completed context count, update active counter - create_lrc_descriptor(): allocate 4KB LRC in GGTT with ELSP_VALID + ELSP_PRIVILEGE_ACCESS flags Linux reference: intel_execlists_submission.c, i915_reg.h (ELSP)
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use log::{debug, info, warn};
|
||||
use redox_driver_sys::memory::MmioRegion;
|
||||
|
||||
use super::gtt::IntelGtt;
|
||||
use crate::driver::Result;
|
||||
use crate::driver::DriverError;
|
||||
|
||||
const RING_ELSP_OFFSET: usize = 0x230;
|
||||
const RING_EXECLIST_STATUS_LO: usize = 0x234;
|
||||
const RING_CONTEXT_CONTROL_OFFSET: usize = 0x244;
|
||||
const RING_CONTEXT_STATUS_PTR_OFFSET: usize = 0x3A0;
|
||||
const RING_EXECLIST_CONTROL_OFFSET: usize = 0x550;
|
||||
|
||||
const RING_ELSP_WRITE_COUNT: usize = 2;
|
||||
const LRC_DESC_SIZE: usize = 4096;
|
||||
|
||||
const CTX_CTRL_CTX_RESTORE_INHIBIT: u32 = 1 << 0;
|
||||
const CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT: u32 = 1 << 1;
|
||||
const CTX_CTRL_RS_CTX_ENABLE: u32 = 1 << 3;
|
||||
|
||||
const EXECLIST_CONTROL_ENABLE: u32 = 1 << 0;
|
||||
|
||||
const ELSP_VALID: u32 = 1 << 0;
|
||||
const ELSP_PRIVILEGE_ACCESS: u32 = 1 << 8;
|
||||
|
||||
const CSB_ENTRY_SIZE: usize = 8;
|
||||
const MAX_CSB_ENTRIES: usize = 6;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ExeclistContext {
|
||||
pub desc: u64,
|
||||
pub engine_class: u8,
|
||||
pub engine_instance: u8,
|
||||
}
|
||||
|
||||
pub struct ExeclistPort {
|
||||
mmio: Arc<MmioRegion>,
|
||||
ring_base: usize,
|
||||
pending: [Option<ExeclistContext>; RING_ELSP_WRITE_COUNT],
|
||||
active: u32,
|
||||
}
|
||||
|
||||
impl ExeclistPort {
|
||||
pub fn new(mmio: Arc<MmioRegion>, ring_base: usize) -> Self {
|
||||
Self {
|
||||
mmio,
|
||||
ring_base,
|
||||
pending: [None, None],
|
||||
active: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(&mut self) -> Result<()> {
|
||||
let ctrl = self.ring_base + RING_EXECLIST_CONTROL_OFFSET;
|
||||
self.mmio.write_u32(ctrl, EXECLIST_CONTROL_ENABLE);
|
||||
|
||||
let ctx_ctrl = self.ring_base + RING_CONTEXT_CONTROL_OFFSET;
|
||||
self.mmio.write_u32(ctx_ctrl,
|
||||
CTX_CTRL_CTX_RESTORE_INHIBIT
|
||||
| CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT
|
||||
| CTX_CTRL_RS_CTX_ENABLE,
|
||||
);
|
||||
|
||||
let csb_ptr = self.ring_base + RING_CONTEXT_STATUS_PTR_OFFSET;
|
||||
self.mmio.write_u32(csb_ptr, 0);
|
||||
|
||||
info!("redox-drm-intel: execlist port initialized at {:#06x}", self.ring_base);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn submit(&mut self, ctx: ExeclistContext) -> Result<()> {
|
||||
let slot = if self.pending[0].is_none() { 0 } else { 1 };
|
||||
self.pending[slot] = Some(ctx);
|
||||
|
||||
self.flush_pending()?;
|
||||
self.active += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush_pending(&mut self) -> Result<()> {
|
||||
for slot in 0..RING_ELSP_WRITE_COUNT {
|
||||
if let Some(ref ctx) = self.pending[slot] {
|
||||
let elsp = self.ring_base + RING_ELSP_OFFSET + slot * 4;
|
||||
let desc_lo = ctx.desc as u32;
|
||||
let desc_hi = (ctx.desc >> 32) as u32;
|
||||
self.mmio.write_u32(elsp, desc_lo);
|
||||
self.mmio.write_u32(elsp + 4, desc_hi);
|
||||
self.pending[slot] = None;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn check_completion(&mut self) -> usize {
|
||||
let status_lo = self.ring_base + RING_EXECLIST_STATUS_LO;
|
||||
let status = self.mmio.read_u32(status_lo);
|
||||
|
||||
let completed = (status & 0x07) as usize;
|
||||
if completed > 0 {
|
||||
self.active = self.active.saturating_sub(completed);
|
||||
debug!("redox-drm-intel: {} contexts completed, {} active",
|
||||
completed, self.active);
|
||||
}
|
||||
completed
|
||||
}
|
||||
|
||||
pub fn active_count(&self) -> u32 {
|
||||
self.active
|
||||
}
|
||||
|
||||
pub fn is_idle(&self) -> bool {
|
||||
self.active == 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_lrc_descriptor(gtt: &IntelGtt, ring_addr: u64, indirect_ctx: bool) -> Result<u64> {
|
||||
let lrc_addr = gtt.allocate_range(LRC_DESC_SIZE)?;
|
||||
|
||||
let desc = if indirect_ctx {
|
||||
lrc_addr | ELSP_VALID | ELSP_PRIVILEGE_ACCESS | (1 << 42)
|
||||
} else {
|
||||
lrc_addr | ELSP_VALID | ELSP_PRIVILEGE_ACCESS
|
||||
};
|
||||
|
||||
debug!("redox-drm-intel: LRC descriptor {:#018x} (ring {:#018x})", desc, ring_addr);
|
||||
Ok(desc)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_execlist_context() {
|
||||
let ctx = ExeclistContext {
|
||||
desc: 0x1000 | ELSP_VALID as u64,
|
||||
engine_class: 0,
|
||||
engine_instance: 0,
|
||||
};
|
||||
assert!(ctx.desc & ELSP_VALID as u64 != 0);
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ pub mod display_power;
|
||||
pub mod display_watermark;
|
||||
pub mod dp_aux;
|
||||
pub mod dp_link;
|
||||
pub mod execlists;
|
||||
pub mod fence;
|
||||
pub mod gmbus;
|
||||
pub mod gtt;
|
||||
|
||||
Reference in New Issue
Block a user