intel: transcoder programming for Xe2/Gen12+ (Phase 2)

Add display_transcoder.rs — TRANS_DDI_FUNC_CTL programming for
platforms with separate transcoders (Xe2/Gen12+ where pipe != transcoder).

- Transcoder::configure(): program TRANS_DDI_FUNC_CTL (0x60400 +
  0x1000 per transcoder) with DDI select, DP/HDMI mode select,
  port width (1/2/4 lanes), and enable bit
- disable(): clear TRANS_DDI_FUNC_ENABLE
- is_enabled(): check TRANS_DDI_FUNC_ENABLE status
- EDP transcoder at 0x6F400 for pipe 3

Wire into IntelDriver::set_crtc — configure transcoder after
modesetting, using pipe.port and TransDdiMode::Dp with 4 lanes.
Only active when has_separate_transcoder == true.

Linux reference: intel_ddi.c (TRANS_DDI_FUNC_CTL programming)
This commit is contained in:
2026-05-30 09:24:55 +03:00
parent 89eee72a0f
commit b21494dacf
2 changed files with 101 additions and 0 deletions
@@ -0,0 +1,90 @@
use std::sync::Arc;
use log::{debug, info};
use redox_driver_sys::memory::MmioRegion;
use super::info::IntelDeviceInfo;
use crate::driver::Result;
const TRANS_DDI_FUNC_CTL_BASE: usize = 0x60400;
const TRANS_STRIDE: usize = 0x1000;
const TRANS_DDI_FUNC_CTL_EDP: usize = 0x6F400;
const TRANS_DDI_FUNC_ENABLE: u32 = 1 << 31;
const TRANS_DDI_SELECT_MASK: u32 = 0x7 << 28;
const TRANS_DDI_MODE_SELECT_MASK: u32 = 0x7 << 24;
const TRANS_DDI_PORT_WIDTH_X1: u32 = 0 << 1;
const TRANS_DDI_PORT_WIDTH_X2: u32 = 1 << 1;
const TRANS_DDI_PORT_WIDTH_X4: u32 = 3 << 1;
const TRANS_DDI_DP_MODE: u32 = 2 << 24;
const TRANS_DDI_HDMI_MODE: u32 = 3 << 24;
pub enum TransDdiMode {
Dp,
Hdmi,
}
pub struct Transcoder {
mmio: Arc<MmioRegion>,
has_separate_transcoder: bool,
}
impl Transcoder {
pub fn new(mmio: Arc<MmioRegion>, device_info: &IntelDeviceInfo) -> Self {
let has_separate_transcoder = device_info.has_separate_transcoder;
Self { mmio, has_separate_transcoder }
}
pub fn configure(&self, pipe: u8, port: u8, mode: TransDdiMode, lane_count: u8) -> Result<()> {
if !self.has_separate_transcoder {
return Ok(());
}
let ddi_func_ctl = if pipe == 3 { TRANS_DDI_FUNC_CTL_EDP }
else { TRANS_DDI_FUNC_CTL_BASE + (pipe as usize) * TRANS_STRIDE };
let mut ctl = 0u32;
let ddi_select = (port as u32) << 28;
ctl |= ddi_select;
let mode_select = match mode {
TransDdiMode::Dp => TRANS_DDI_DP_MODE,
TransDdiMode::Hdmi => TRANS_DDI_HDMI_MODE,
};
ctl |= mode_select;
let width = match lane_count {
4 => TRANS_DDI_PORT_WIDTH_X4,
2 => TRANS_DDI_PORT_WIDTH_X2,
_ => TRANS_DDI_PORT_WIDTH_X1,
};
ctl |= width;
ctl |= TRANS_DDI_FUNC_ENABLE;
self.mmio.write_u32(ddi_func_ctl, ctl);
info!("redox-drm-intel: transcoder {} configured (port {}, {:?}, {} lanes, ctl {:#010x})",
pipe, port, mode, lane_count, ctl);
Ok(())
}
pub fn disable(&self, pipe: u8) -> Result<()> {
if !self.has_separate_transcoder { return Ok(()); }
let ddi_func_ctl = if pipe == 3 { TRANS_DDI_FUNC_CTL_EDP }
else { TRANS_DDI_FUNC_CTL_BASE + (pipe as usize) * TRANS_STRIDE };
self.mmio.write_u32(ddi_func_ctl, 0);
debug!("redox-drm-intel: transcoder {} disabled", pipe);
Ok(())
}
pub fn is_enabled(&self, pipe: u8) -> bool {
if !self.has_separate_transcoder { return true; }
let ddi_func_ctl = if pipe == 3 { TRANS_DDI_FUNC_CTL_EDP }
else { TRANS_DDI_FUNC_CTL_BASE + (pipe as usize) * TRANS_STRIDE };
let val = self.mmio.read_u32(ddi_func_ctl);
val & TRANS_DDI_FUNC_ENABLE != 0
}
}
@@ -6,6 +6,7 @@ pub mod display_combo_phy;
pub mod display_dmc;
pub mod display_dpll;
pub mod display_power;
pub mod display_transcoder;
pub mod display_watermark;
pub mod dp_aux;
pub mod dp_link;
@@ -46,6 +47,7 @@ use self::display_combo_phy::ComboPhy;
use self::display_dmc::DmcFirmware;
use self::display_dpll::DisplayPll;
use self::display_power::DisplayPower;
use self::display_transcoder::{TransDdiMode, Transcoder};
use self::display_watermark::DisplayWatermark;
use self::dp_aux::DpAux;
use self::gmbus::GmbusController;
@@ -89,6 +91,7 @@ pub struct IntelDriver {
dp_aux: Vec<DpAux>,
combo_phy: Option<ComboPhy>,
display_power: DisplayPower,
transcoder: Transcoder,
watermark: DisplayWatermark,
dpll: DisplayPll,
dmc: DmcFirmware,
@@ -188,6 +191,8 @@ impl IntelDriver {
let display_power = DisplayPower::new(mmio_arc.clone(), regs, &device_info);
display_power.init_domains()?;
let transcoder = Transcoder::new(mmio_arc.clone(), &device_info);
let watermark = DisplayWatermark::new(mmio_arc.clone(), &device_info);
watermark.init()?;
@@ -284,6 +289,7 @@ impl IntelDriver {
dp_aux,
combo_phy,
display_power,
transcoder,
watermark,
dpll,
dmc,
@@ -581,6 +587,11 @@ impl GpuDriver for IntelDriver {
pipe.port = Some(self.connector_port(connectors[0])?);
self.display.set_mode(&pipe, mode)?;
if let Some(port) = pipe.port {
self.transcoder.configure(pipe.index, port, TransDdiMode::Dp, 4)?;
}
self.display.page_flip(&pipe, fb_addr)?;
let mut crtcs = self