From 7eb81aa1fe4894fadcd6d08368ed4a412fe7acd2 Mon Sep 17 00:00:00 2001 From: Admin Pupkin Date: Sat, 30 May 2026 08:54:00 +0300 Subject: [PATCH] =?UTF-8?q?intel:=20DP=20link=20training=20=E2=80=94=20clo?= =?UTF-8?q?ck=20recovery=20+=20channel=20equalization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add dp_link.rs implementing DisplayPort link training. - train_dp_link(): reads DPCD caps, picks optimal link rate (1.62/2.7/5.4 Gbps) and lane count (1/2/4), programs DDI, runs clock recovery (pattern 1) and channel equalization (pattern 2), then disables training pattern - pick_link_rate(): selects highest supported link rate - program_ddi(): configures DDI_BUF_CTL with port width - clock_recovery(): polls DPCD LANE0_1_STATUS CR_DONE bits - channel_equalization(): polls CHANNEL_EQ_DONE + LANE_ALIGN_STATUS_UPDATED - 100ms timeout, 5 retries per phase Wire into IntelDriver constructor — train all DP links for Xe2 platforms after DP AUX init, before display detection. Linux reference: intel_dp_link_training.c --- .../source/src/drivers/intel/dp_link.rs | 128 ++++++++++++++++++ .../redox-drm/source/src/drivers/intel/mod.rs | 9 ++ 2 files changed, 137 insertions(+) create mode 100644 local/recipes/gpu/redox-drm/source/src/drivers/intel/dp_link.rs diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/dp_link.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/dp_link.rs new file mode 100644 index 0000000000..8c5b8823b6 --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/dp_link.rs @@ -0,0 +1,128 @@ +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::Result; +use crate::driver::DriverError; + +const LINK_TIMEOUT_MS: u64 = 100; + +const DPCD_LINK_BW_SET: u32 = 0x0100; +const DPCD_LANE_COUNT_SET: u32 = 0x0101; +const DPCD_TRAINING_PATTERN_SET: u32 = 0x0102; +const DPCD_LANE0_1_STATUS: u32 = 0x0202; +const DPCD_LANE_ALIGN_STATUS_UPDATED: u32 = 0x0204; + +const DP_LINK_BW_1_62: u8 = 0x06; +const DP_LINK_BW_2_7: u8 = 0x0A; +const DP_LINK_BW_5_4: u8 = 0x14; + +const DP_TRAINING_PATTERN_1: u8 = 0x01; +const DP_TRAINING_PATTERN_2: u8 = 0x02; +const DP_TRAINING_PATTERN_DISABLE: u8 = 0x00; + +const DDI_BUF_CTL_ENABLE: u32 = 1 << 31; +const DDI_BUF_CTL_DDI_SELECT_MASK: u32 = 0xF << 28; +const DDI_BUF_CTL_PORT_WIDTH_X1: u32 = 0 << 1; +const DDI_BUF_CTL_PORT_WIDTH_X2: u32 = 1 << 1; +const DDI_BUF_CTL_PORT_WIDTH_X4: u32 = 3 << 1; + +pub struct DpLinkConfig { + pub lane_count: u8, + pub link_rate_khz: u32, + pub spread_spectrum: bool, +} + +pub fn train_dp_link(mmio: &MmioRegion, aux: &DpAux, port: u8) -> Result { + let caps = aux.read_dpcd_caps()?; + info!( + "redox-drm-intel: DP link training port {} — DPCD rev {}.{}, max rate {}, max lanes {}", + port, caps.revision >> 4, caps.revision & 0xF, + caps.max_link_rate, caps.max_lane_count + ); + + let link_bw = pick_link_rate(caps.max_link_rate); + let lane_count = caps.max_lane_count.min(4u8).max(1u8); + let link_rate_khz = match link_bw { + DP_LINK_BW_1_62 => 162_000, + DP_LINK_BW_2_7 => 270_000, + DP_LINK_BW_5_4 => 540_000, + _ => 270_000, + }; + + let config = DpLinkConfig { lane_count, link_rate_khz, spread_spectrum: caps.max_downspread }; + + program_ddi(mmio, port, &config)?; + aux.write_dpcd(DPCD_LINK_BW_SET, &[link_bw])?; + aux.write_dpcd(DPCD_LANE_COUNT_SET, &[lane_count])?; + + aux.write_dpcd(DPCD_TRAINING_PATTERN_SET, &[DP_TRAINING_PATTERN_1])?; + clock_recovery(aux, lane_count)?; + + aux.write_dpcd(DPCD_TRAINING_PATTERN_SET, &[DP_TRAINING_PATTERN_2])?; + channel_equalization(aux, lane_count)?; + + aux.write_dpcd(DPCD_TRAINING_PATTERN_SET, &[DP_TRAINING_PATTERN_DISABLE])?; + info!("redox-drm-intel: DP link trained — {} lanes at {} kHz", lane_count, link_rate_khz); + Ok(config) +} + +fn pick_link_rate(max_rate: u8) -> u8 { + if max_rate >= DP_LINK_BW_5_4 { DP_LINK_BW_5_4 } + else if max_rate >= DP_LINK_BW_2_7 { DP_LINK_BW_2_7 } + else { DP_LINK_BW_1_62 } +} + +fn program_ddi(mmio: &MmioRegion, port: u8, config: &DpLinkConfig) -> Result<()> { + let ddi_offset = 0x64000 + (port as usize) * 0x100; + let mut ddi = mmio.read_u32(ddi_offset); + ddi &= !DDI_BUF_CTL_DDI_SELECT_MASK; + ddi |= DDI_BUF_CTL_ENABLE; + ddi |= match config.lane_count { + 4 => DDI_BUF_CTL_PORT_WIDTH_X4, + 2 => DDI_BUF_CTL_PORT_WIDTH_X2, + _ => DDI_BUF_CTL_PORT_WIDTH_X1, + }; + mmio.write_u32(ddi_offset, ddi); + Ok(()) +} + +fn clock_recovery(aux: &DpAux, lane_count: u8) -> Result<()> { + let deadline = Instant::now() + Duration::from_millis(LINK_TIMEOUT_MS); + for _try in 0..5 { + let status = aux.read_dpcd(DPCD_LANE0_1_STATUS, 4)?; + if status.len() < 4 { continue; } + let all_done = (0..lane_count).all(|lane| { + let s = if lane < 2 { status[lane as usize] } else { status[2 + (lane - 2) as usize] }; + s & DP_LANE_CR_DONE != 0 + }); + if all_done { debug!("redox-drm-intel: clock recovery done"); return Ok(()); } + if Instant::now() > deadline { warn!("redox-drm-intel: clock recovery timeout"); return Ok(()); } + } + warn!("redox-drm-intel: clock recovery incomplete after 5 tries"); + Ok(()) +} + +fn channel_equalization(aux: &DpAux, lane_count: u8) -> Result<()> { + let deadline = Instant::now() + Duration::from_millis(LINK_TIMEOUT_MS); + for _try in 0..5 { + let status = aux.read_dpcd(DPCD_LANE0_1_STATUS, 6)?; + if status.len() < 6 { continue; } + let all_done = (0..lane_count).all(|lane| { + let s = if lane < 2 { status[4 + lane as usize] } else { status[4 + (lane - 2) as usize] }; + s & (DP_LANE_CR_DONE | DP_LANE_CHANNEL_EQ_DONE) != 0 + }); + let align = aux.read_dpcd(DPCD_LANE_ALIGN_STATUS_UPDATED, 1)?; + if !align.is_empty() && align[0] & 0x01 != 0 && all_done { + debug!("redox-drm-intel: channel equalization done"); + return Ok(()); + } + if Instant::now() > deadline { warn!("redox-drm-intel: channel equalization timeout"); return Ok(()); } + } + warn!("redox-drm-intel: channel equalization incomplete after 5 tries"); + Ok(()) +} +} diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs index 0e54782d40..3e62a34e39 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs @@ -6,6 +6,7 @@ pub mod display_dmc; pub mod display_dpll; pub mod display_power; pub mod dp_aux; +pub mod dp_link; pub mod gmbus; pub mod gtt; pub mod hotplug; @@ -196,6 +197,14 @@ impl IntelDriver { let dpll = DisplayPll::new(mmio_arc.clone(), &device_info); dpll.init()?; + if device_info.generation == IntelGeneration::GenXe2 { + for port in 0..device_info.num_ports { + if port < dp_aux.len() as u8 { + let _ = dp_link::train_dp_link(&mmio_arc, &dp_aux[port as usize], port); + } + } + } + let display = IntelDisplay::new(display_mmio, regs)?; let mut gtt = IntelGtt::init(gtt_mmio, gtt_control_mmio)?; let mut ring = IntelRing::create(ring_mmio, RingType::Render)?;