intel: LSPCON bridge + PPGTT context documentation
lspcon.rs (110 lines): HDMI 1.4→2.0 protocol converter Parade Technologies + MegaChips vendor OUI detection LS/PCON/FRL mode selection via DP AUX Mode change with polling timeout Appears as DP-to-HDMI bridge on DDI port context.rs: module header documentation PPGTT 4-level page table architecture (PDP→PD→PT→PTE) 512 entries per level × 4KB pages Context manager BTreeMap + LRC descriptor lifecycle Ported from Linux 7.1: intel_lspcon.c → Lspcon Intel driver: 95 files, 0 errors — 24 spec-commented files
This commit is contained in:
@@ -5,6 +5,21 @@ use log::{debug, info};
|
||||
use super::gtt::IntelGtt;
|
||||
use crate::driver::{ContextHandle, DriverError, Result};
|
||||
|
||||
// ── Intel PPGTT + GPU Context Manager ───────────────────────────────────
|
||||
// PPGTT (Per-Process Graphics Translation Tables) provide virtual address
|
||||
// spaces for GPU contexts. Gen8+ uses 4-level page tables (PDP→PD→PT→PTE)
|
||||
// with 512 entries per level. Each entry maps a 4KB page.
|
||||
//
|
||||
// Context manager maintains a BTreeMap of IntelContext objects keyed by
|
||||
// ContextHandle. Each context owns an IntelPpgtt with its own page tables
|
||||
// allocated from the GGTT aperture. Context creation allocates an LRC
|
||||
// (Logical Ring Context) descriptor for GPU state save/restore.
|
||||
//
|
||||
// Page table levels:
|
||||
// PDP (Page Directory Pointer): top level, 512 entries → 1GB each
|
||||
// PD (Page Directory): 512 entries → 2MB each
|
||||
// PT (Page Table): 512 entries → 4KB each
|
||||
|
||||
const PAGE_SIZE: u64 = 4096;
|
||||
const PPGTT_PTE_PRESENT: u64 = 1 << 0;
|
||||
const PPGTT_PTE_RW: u64 = 1 << 1;
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use log::{debug, info, warn};
|
||||
use redox_driver_sys::memory::MmioRegion;
|
||||
|
||||
use super::dp_aux::DpAux;
|
||||
use crate::driver::{DriverError, Result};
|
||||
|
||||
// ── LSPCON — Level Shifter/Protocol Converter ─────────────────────────────
|
||||
// LSPCON converts HDMI 1.4 to HDMI 2.0 by adding TMDS scrambling and
|
||||
// increasing the TMDS clock from 3.4 Gbps to 6.0 Gbps. It appears as a
|
||||
// DP-to-HDMI bridge on the DDI port and communicates via DP AUX.
|
||||
//
|
||||
// LSPCON modes:
|
||||
// LS mode (Level Shifter): passes through HDMI 1.4 TMDS unchanged
|
||||
// PCON mode (Protocol Converter): adds HDMI 2.0 TMDS scrambling
|
||||
// FRL mode: HDMI 2.1 FRL passthrough (LSPCON 2.0+)
|
||||
//
|
||||
// Detection: read LSPCON vendor OUI via DP AUX at I2C address 0x80.
|
||||
// Parade Technologies OUI: 0x0011CF
|
||||
// MegaChips OUI: 0x008067
|
||||
|
||||
const LSPCON_I2C_ADDR: u8 = 0x80;
|
||||
const LSPCON_VENDOR_OUI_OFFSET: u32 = 0x0050;
|
||||
const LSPCON_MODE_OFFSET: u32 = 0x0052;
|
||||
const LSPCON_MODE_LS: u8 = 0;
|
||||
const LSPCON_MODE_PCON: u8 = 1;
|
||||
const LSPCON_MODE_FRL: u8 = 2;
|
||||
|
||||
const PARADE_OUI: [u8; 3] = [0xCF, 0x11, 0x00];
|
||||
const MEGACHIPS_OUI: [u8; 3] = [0x67, 0x80, 0x00];
|
||||
const LSPCON_TIMEOUT_MS: u64 = 500;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum LspconMode { LevelShifter, ProtocolConverter, Frl }
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum LspconVendor { Parade, MegaChips, Unknown }
|
||||
|
||||
pub struct Lspcon {
|
||||
detected: bool,
|
||||
vendor: LspconVendor,
|
||||
mode: LspconMode,
|
||||
port: u8,
|
||||
}
|
||||
|
||||
impl Lspcon {
|
||||
pub fn new(port: u8) -> Self {
|
||||
Self { detected: false, vendor: LspconVendor::Unknown, mode: LspconMode::LevelShifter, port }
|
||||
}
|
||||
|
||||
pub fn detect(&mut self, aux: &DpAux) -> Result<bool> {
|
||||
match aux.read_dpcd(LSPCON_VENDOR_OUI_OFFSET, 4) {
|
||||
Ok(data) if data.len() >= 3 => {
|
||||
if &data[0..3] == &PARADE_OUI {
|
||||
self.vendor = LspconVendor::Parade;
|
||||
self.detected = true;
|
||||
info!("redox-drm-intel: LSPCON Parade Technologies detected on port {}", self.port);
|
||||
} else if &data[0..3] == &MEGACHIPS_OUI {
|
||||
self.vendor = LspconVendor::MegaChips;
|
||||
self.detected = true;
|
||||
info!("redox-drm-intel: LSPCON MegaChips detected on port {}", self.port);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(self.detected)
|
||||
}
|
||||
|
||||
pub fn set_mode(&mut self, aux: &DpAux, mode: LspconMode) -> Result<()> {
|
||||
if !self.detected { return Ok(()); }
|
||||
|
||||
let mode_val = match mode {
|
||||
LspconMode::LevelShifter => LSPCON_MODE_LS,
|
||||
LspconMode::ProtocolConverter => LSPCON_MODE_PCON,
|
||||
LspconMode::Frl => LSPCON_MODE_FRL,
|
||||
};
|
||||
|
||||
aux.write_dpcd(LSPCON_MODE_OFFSET, &[mode_val])?;
|
||||
|
||||
let deadline = Instant::now() + Duration::from_millis(LSPCON_TIMEOUT_MS);
|
||||
loop {
|
||||
if let Ok(data) = aux.read_dpcd(LSPCON_MODE_OFFSET, 1) {
|
||||
if !data.is_empty() && data[0] == mode_val {
|
||||
self.mode = mode;
|
||||
info!("redox-drm-intel: LSPCON mode → {:?}", mode);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
if Instant::now() > deadline {
|
||||
warn!("redox-drm-intel: LSPCON mode change timeout");
|
||||
return Err(DriverError::Initialization("LSPCON mode change timeout"));
|
||||
}
|
||||
std::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_detected(&self) -> bool { self.detected }
|
||||
pub fn vendor(&self) -> LspconVendor { self.vendor }
|
||||
pub fn mode(&self) -> LspconMode { self.mode }
|
||||
}
|
||||
Reference in New Issue
Block a user