intel: SNPS PHY for DG2+ + GT manager documentation
snps_phy.rs (90 lines): Synopsys HDMI 2.1 PHY
DG2/Alchemist + Battlemage discrete GPU HDMI output
TMDS (up to 6 Gbps) + FRL (up to 12 Gbps) mode support
PLL lock + PHY ready timeout sequences
HDMI-only (no DP — uses separate PHY)
gt.rs: module header documentation
Forcewake per generation: Gen4-5 (none), Gen6 (MT), Gen7-8 (MT+RENDER),
Gen9 (RENDER), Gen12+ (MT multi-cast)
RPS governor: interactive fast-up/slow-down
RC6: hardware-managed power state
Ported from Linux 7.1:
intel_snps_phy.c → SnpsPhy
Intel driver: 93 files, 0 errors
This commit is contained in:
@@ -8,6 +8,20 @@ use super::info::IntelDeviceInfo;
|
||||
use super::info::IntelGeneration;
|
||||
use crate::driver::{DriverError, Result};
|
||||
|
||||
// ── Intel GT Manager — Graphics Technology Engine ───────────────────────
|
||||
// Manages GPU compute/render engine: forcewake (Gen6+), RPS frequency
|
||||
// scaling, RC6 power states, GT interrupts, and platform workarounds.
|
||||
//
|
||||
// Forcewake mechanism varies by generation:
|
||||
// Gen4-5: No forcewake (INSTPM sync or GT FIFO polling)
|
||||
// Gen6: FORCEWAKE_MT at 0xA188, ACK at 0x130044 (bit 0)
|
||||
// Gen7-8: FORCEWAKE_MT + FORCEWAKE_RENDER with MT_ACK
|
||||
// Gen9: FORCEWAKE_RENDER at 0xA278, MT_ACK at 0x130040
|
||||
// Gen12+: FORCEWAKE_MT with 0xDFC multi-cast ACK
|
||||
//
|
||||
// RPS governor: interactive fast-up/slow-down with evaluation intervals.
|
||||
// RC6: hardware-managed power state with enable/disable toggle.
|
||||
|
||||
const GT_FREQ_BASE: usize = 0xA008;
|
||||
const GT_MIN_FREQ_OFFSET: usize = 0;
|
||||
const GT_MAX_FREQ_OFFSET: usize = 4;
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use log::{debug, info, warn};
|
||||
use redox_driver_sys::memory::MmioRegion;
|
||||
|
||||
use crate::driver::{DriverError, Result};
|
||||
|
||||
// ── SNPS PHY — Synopsys HDMI 2.1 PHY for DG2/Battlemage ──────────────────
|
||||
// The Synopsys (SNPS) PHY is used on DG2/Alchemist, Battlemage, and
|
||||
// discrete Arc GPUs for HDMI 2.1 output. It provides TMDS (up to 6 Gbps)
|
||||
// and FRL (up to 12 Gbps per lane) signaling for HDMI 2.1.
|
||||
//
|
||||
// Unlike the Intel Combo PHY which handles both DP and HDMI, the SNPS PHY
|
||||
// is HDMI-only and uses a separate register space in the PHY MMIO range.
|
||||
// Programming sequence: power on → configure TMDS/FRL mode → enable → lock.
|
||||
|
||||
const SNPS_PHY_CTL_BASE: usize = 0x168000;
|
||||
const SNPS_PHY_PLL_BASE: usize = 0x168100;
|
||||
const SNPS_PHY_LANE_BASE: usize = 0x168200;
|
||||
const SNPS_PHY_STRIDE: usize = 0x10000;
|
||||
|
||||
const SNPS_PHY_ENABLE: u32 = 1 << 31;
|
||||
const SNPS_PHY_READY: u32 = 1 << 30;
|
||||
const SNPS_PHY_PLL_LOCK: u32 = 1 << 31;
|
||||
const SNPS_PHY_TMDS_MODE: u32 = 0;
|
||||
const SNPS_PHY_FRL_MODE: u32 = 1 << 0;
|
||||
const SNPS_PHY_TIMEOUT_MS: u64 = 10;
|
||||
|
||||
pub struct SnpsPhy {
|
||||
mmio: Arc<MmioRegion>,
|
||||
enabled: bool,
|
||||
port: u8,
|
||||
frl_mode: bool,
|
||||
}
|
||||
|
||||
impl SnpsPhy {
|
||||
pub fn new(mmio: Arc<MmioRegion>, port: u8) -> Self {
|
||||
Self { mmio, enabled: false, port, frl_mode: false }
|
||||
}
|
||||
|
||||
pub fn init(&mut self, use_frl: bool) -> Result<()> {
|
||||
self.frl_mode = use_frl;
|
||||
let base = SNPS_PHY_CTL_BASE + self.port as usize * SNPS_PHY_STRIDE;
|
||||
|
||||
self.mmio.write32(base + SNPS_PHY_PLL_BASE,
|
||||
self.mmio.read32(base + SNPS_PHY_PLL_BASE) | SNPS_PHY_ENABLE);
|
||||
|
||||
let deadline = Instant::now() + Duration::from_millis(SNPS_PHY_TIMEOUT_MS);
|
||||
loop {
|
||||
if self.mmio.read32(base + SNPS_PHY_PLL_BASE) & SNPS_PHY_PLL_LOCK != 0 { break; }
|
||||
if Instant::now() > deadline {
|
||||
return Err(DriverError::Initialization(format!("SNPS PLL lock timeout port {}", self.port)));
|
||||
}
|
||||
std::hint::spin_loop();
|
||||
}
|
||||
|
||||
let mode = if self.frl_mode { SNPS_PHY_FRL_MODE } else { SNPS_PHY_TMDS_MODE };
|
||||
let mut ctl = self.mmio.read32(base);
|
||||
ctl |= SNPS_PHY_ENABLE | mode;
|
||||
self.mmio.write32(base, ctl);
|
||||
|
||||
let deadline = Instant::now() + Duration::from_millis(SNPS_PHY_TIMEOUT_MS);
|
||||
loop {
|
||||
if self.mmio.read32(base) & SNPS_PHY_READY != 0 { break; }
|
||||
if Instant::now() > deadline {
|
||||
warn!("redox-drm-intel: SNPS PHY {} ready timeout", self.port);
|
||||
return Err(DriverError::Initialization(format!("SNPS PHY {} ready timeout", self.port)));
|
||||
}
|
||||
std::hint::spin_loop();
|
||||
}
|
||||
|
||||
self.enabled = true;
|
||||
info!("redox-drm-intel: SNPS PHY {} initialized ({} mode)",
|
||||
self.port, if self.frl_mode { "FRL" } else { "TMDS" });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn disable(&mut self) -> Result<()> {
|
||||
if !self.enabled { return Ok(()); }
|
||||
let base = SNPS_PHY_CTL_BASE + self.port as usize * SNPS_PHY_STRIDE;
|
||||
self.mmio.write32(base, self.mmio.read32(base) & !SNPS_PHY_ENABLE);
|
||||
self.mmio.write32(base + SNPS_PHY_PLL_BASE,
|
||||
self.mmio.read32(base + SNPS_PHY_PLL_BASE) & !SNPS_PHY_ENABLE);
|
||||
self.enabled = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_enabled(&self) -> bool { self.enabled }
|
||||
}
|
||||
Reference in New Issue
Block a user