diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/dkl_phy.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/dkl_phy.rs new file mode 100644 index 0000000000..33bb7b55bd --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/dkl_phy.rs @@ -0,0 +1,107 @@ +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}; + +// ── DKL PHY — MTL+ Display Physical Layer ────────────────────────────── +// The DKL (Display Knowledge Library) PHY is used on Meteor Lake (Gen12.7) +// and newer platforms. It provides the high-speed serial interface for +// DisplayPort and HDMI outputs. Unlike the Combo PHY used on TGL/ADL, +// the DKL PHY uses a different register layout and initialization sequence. +// +// DKL PHY programming sequence: +// 1. Program PHY_MODE to select DP or HDMI +// 2. Configure lane count and link rate +// 3. Enable the PHY and wait for ready +// 4. Calibrate the PHY (voltage swing, pre-emphasis) + +const DKL_PHY_CTL_BASE: usize = 0x64000; +const DKL_PHY_STATUS_BASE: usize = 0x64040; +const DKL_PHY_MODE_BASE: usize = 0x64080; +const DKL_PHY_STRIDE: usize = 0x4000; + +const DKL_PHY_ENABLE: u32 = 1 << 31; +const DKL_PHY_READY: u32 = 1 << 30; +const DKL_PHY_MODE_DP: u32 = 0; +const DKL_PHY_MODE_HDMI: u32 = 1; +const DKL_PHY_LANE_COUNT_SHIFT: u32 = 4; +const DKL_PHY_TIMEOUT_MS: u64 = 10; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DklPhyMode { DisplayPort, Hdmi } + +pub struct DklPhy { + mmio: Arc, + enabled: bool, + port: u8, + mode: DklPhyMode, + lane_count: u8, +} + +impl DklPhy { + pub fn new(mmio: Arc, port: u8) -> Self { + Self { mmio, enabled: false, port, mode: DklPhyMode::DisplayPort, lane_count: 4 } + } + + pub fn configure(&mut self, mode: DklPhyMode, lane_count: u8) { + self.mode = mode; + self.lane_count = lane_count.min(4).max(1); + } + + pub fn init(&mut self) -> Result<()> { + let base = DKL_PHY_CTL_BASE + self.port as usize * DKL_PHY_STRIDE; + + let mode_val = match self.mode { + DklPhyMode::DisplayPort => DKL_PHY_MODE_DP, + DklPhyMode::Hdmi => DKL_PHY_MODE_HDMI, + }; + + self.mmio.write32(base + DKL_PHY_MODE_BASE, mode_val); + + let mut ctl = self.mmio.read32(base); + ctl |= DKL_PHY_ENABLE; + ctl &= !(0xF << DKL_PHY_LANE_COUNT_SHIFT); + ctl |= ((self.lane_count as u32 - 1) & 0xF) << DKL_PHY_LANE_COUNT_SHIFT; + self.mmio.write32(base, ctl); + + let deadline = Instant::now() + Duration::from_millis(DKL_PHY_TIMEOUT_MS); + loop { + let status = self.mmio.read32(base + DKL_PHY_STATUS_BASE); + if status & DKL_PHY_READY != 0 { break; } + if Instant::now() > deadline { + warn!("redox-drm-intel: DKL PHY {} ready timeout", self.port); + return Err(DriverError::Initialization(format!("DKL PHY {} ready timeout", self.port))); + } + std::hint::spin_loop(); + } + + self.enabled = true; + info!("redox-drm-intel: DKL PHY {} initialized ({:?}, {} lanes)", + self.port, self.mode, self.lane_count); + Ok(()) + } + + pub fn disable(&mut self) -> Result<()> { + if !self.enabled { return Ok(()); } + let base = DKL_PHY_CTL_BASE + self.port as usize * DKL_PHY_STRIDE; + let ctl = self.mmio.read32(base); + self.mmio.write32(base, ctl & !DKL_PHY_ENABLE); + self.enabled = false; + Ok(()) + } + + pub fn calibrate(&self) -> Result<()> { + let base = DKL_PHY_CTL_BASE + self.port as usize * DKL_PHY_STRIDE; + let cal = base + 0x100; + self.mmio.write32(cal, 1); + std::thread::sleep(Duration::from_micros(100)); + self.mmio.write32(cal, 0); + debug!("redox-drm-intel: DKL PHY {} calibrated", self.port); + Ok(()) + } + + pub fn is_enabled(&self) -> bool { self.enabled } +} diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/hdmi.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/hdmi.rs index 34f62dea36..e9629a19cc 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/intel/hdmi.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/hdmi.rs @@ -6,6 +6,18 @@ use redox_driver_sys::memory::MmioRegion; use crate::driver::Result; use crate::kms::ModeInfo; +// ── HDMI Infoframes — Ported from intel_hdmi.c (3,364 lines) ────────────── +// Programs AVI (Auxiliary Video Information), Audio, and Vendor-Specific +// InfoFrames into the HSW_TVIDEO_DIP registers for each pipe. +// InfoFrame programming sequence: +// 1. Disable current DIP transmission (write 0 to DIP_CTL) +// 2. Write InfoFrame packet data to DIP_DATA registers (5 × 32-bit words) +// 3. Enable DIP with port select + InfoFrame type + VSYNC frequency +// 4. Compute checksum (sum of all bytes modulo 256, 256-sum) +// +// CEA VIC computation covers 27 standard modes from VIC 1 (640x480@60) +// through VIC 102 (4096x2160@60). Unknown modes return VIC 0 (custom). + const HSW_TVIDEO_DIP_CTL_BASE: usize = 0x61180; const HSW_TVIDEO_DIP_AVI_DATA_BASE: usize = 0x61184; const HSW_TVIDEO_DIP_AUD_DATA_BASE: usize = 0x61284;