From 60480a5d9d8c3d4ef4466eda3cc729008cc0bd37 Mon Sep 17 00:00:00 2001 From: Admin Pupkin Date: Tue, 2 Jun 2026 11:29:12 +0300 Subject: [PATCH] intel: SNPS PHY for DG2+ + GT manager documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../redox-drm/source/src/drivers/intel/gt.rs | 14 +++ .../source/src/drivers/intel/snps_phy.rs | 90 +++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 local/recipes/gpu/redox-drm/source/src/drivers/intel/snps_phy.rs diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/gt.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/gt.rs index 6ce98c757f..64952a9d67 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/intel/gt.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/gt.rs @@ -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; diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/snps_phy.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/snps_phy.rs new file mode 100644 index 0000000000..eb54ce0652 --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/snps_phy.rs @@ -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, + enabled: bool, + port: u8, + frl_mode: bool, +} + +impl SnpsPhy { + pub fn new(mmio: Arc, 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 } +}