diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/display_dpll.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/display_dpll.rs new file mode 100644 index 0000000000..ac1dc7334d --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/display_dpll.rs @@ -0,0 +1,125 @@ +use std::sync::Arc; +use std::time::{Duration, Instant}; + +use log::{debug, info, warn}; +use redox_driver_sys::memory::MmioRegion; + +use super::info::IntelDeviceInfo; +use crate::driver::Result; +use crate::driver::DriverError; + +const LCPLL1_CTL: usize = 0x46010; +const LCPLL2_CTL: usize = 0x46014; +const WRPLL_CTL1: usize = 0x46040; +const WRPLL_CTL2: usize = 0x46060; +const DPLL_CTRL1: usize = 0x6C058; +const DPLL_CTRL2: usize = 0x6C05C; + +const PLL_ENABLE: u32 = 1 << 31; +const PLL_LOCK: u32 = 1 << 30; +const PLL_POWER_ENABLE: u32 = 1 << 30; +const PLL_TIMEOUT_MS: u64 = 5; + +const WRPLL_REF_BCLK: u32 = 0 << 28; +const LCPLL_PLL_ENABLE: u32 = 1 << 31; + +pub struct DpllConfig { + pub id: u8, + pub frequency_khz: u32, + pub pdiv: u32, + pub qdiv: u32, + pub kdiv: u32, +} + +pub struct DisplayPll { + mmio: Arc, + is_xe2: bool, +} + +impl DisplayPll { + pub fn new(mmio: Arc, device_info: &IntelDeviceInfo) -> Self { + let is_xe2 = device_info.generation == super::info::IntelGeneration::GenXe2; + Self { mmio, is_xe2 } + } + + pub fn init(&self) -> Result<()> { + if self.is_xe2 { + self.init_xe2() + } else { + self.init_gen9() + } + } + + fn init_gen9(&self) -> Result<()> { + info!("redox-drm-intel: initializing Gen9 DPLLs"); + + self.enable_lcpll(LCPLL1_CTL, "LCPLL1")?; + self.enable_lcpll(LCPLL2_CTL, "LCPLL2")?; + + let wrpll = WRPLL_CTL1; + let current = self.mmio.read_u32(wrpll); + if current & PLL_ENABLE == 0 { + self.mmio.write_u32(wrpll, current | PLL_ENABLE | WRPLL_REF_BCLK); + self.wait_for_lock(wrpll, "WRPLL1")?; + } + info!("redox-drm-intel: Gen9 DPLLs ready"); + Ok(()) + } + + fn init_xe2(&self) -> Result<()> { + info!("redox-drm-intel: initializing Xe2 DPLLs"); + + let dpll_ctrl1 = self.mmio.read_u32(DPLL_CTRL1); + if dpll_ctrl1 & PLL_ENABLE == 0 { + self.mmio.write_u32(DPLL_CTRL1, dpll_ctrl1 | PLL_POWER_ENABLE | PLL_ENABLE); + self.wait_for_lock(DPLL_CTRL1, "Xe2 DPLL1")?; + } + + let dpll_ctrl2 = self.mmio.read_u32(DPLL_CTRL2); + if dpll_ctrl2 & PLL_ENABLE == 0 { + self.mmio.write_u32(DPLL_CTRL2, dpll_ctrl2 | PLL_POWER_ENABLE | PLL_ENABLE); + self.wait_for_lock(DPLL_CTRL2, "Xe2 DPLL2")?; + } + info!("redox-drm-intel: Xe2 DPLLs ready"); + Ok(()) + } + + pub fn get_pll_for_clock(&self, pixel_clock_khz: u32) -> Result { + let pdiv = if pixel_clock_khz > 300_000 { 2 } else { 1 }; + let qdiv = 1; + let kdiv = 0; + Ok(DpllConfig { + id: 0, + frequency_khz: pixel_clock_khz, + pdiv, + qdiv, + kdiv, + }) + } + + fn enable_lcpll(&self, reg: usize, name: &str) -> Result<()> { + let current = self.mmio.read_u32(reg); + if current & LCPLL_PLL_ENABLE != 0 { + debug!("redox-drm-intel: {} already enabled", name); + return Ok(()); + } + self.mmio.write_u32(reg, current | LCPLL_PLL_ENABLE); + self.wait_for_lock(reg, name) + } + + fn wait_for_lock(&self, reg: usize, name: &str) -> Result<()> { + let deadline = Instant::now() + Duration::from_millis(PLL_TIMEOUT_MS); + loop { + let status = self.mmio.read_u32(reg); + if status & PLL_LOCK != 0 { + debug!("redox-drm-intel: {} locked", name); + return Ok(()); + } + if Instant::now() > deadline { + warn!("redox-drm-intel: {} lock timeout: {:#010x}", name, status); + return Err(DriverError::Other(format!("{} lock timeout", name))); + } + std::hint::spin_loop(); + } + } +} 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 6ba211537e..bc3ae2de1e 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 @@ -2,6 +2,7 @@ pub mod display; pub mod display_cdclk; pub mod display_combo_phy; pub mod display_dmc; +pub mod display_dpll; pub mod display_power; pub mod dp_aux; pub mod gmbus; @@ -32,6 +33,7 @@ use self::display::{DisplayPipe, IntelDisplay}; use self::display_cdclk::DisplayClock; use self::display_combo_phy::ComboPhy; use self::display_dmc::DmcFirmware; +use self::display_dpll::DisplayPll; use self::display_power::DisplayPower; use self::dp_aux::DpAux; use self::gmbus::GmbusController; @@ -73,6 +75,7 @@ pub struct IntelDriver { dp_aux: Vec, combo_phy: Option, display_power: DisplayPower, + dpll: DisplayPll, dmc: DmcFirmware, cdclk: DisplayClock, } @@ -184,6 +187,9 @@ impl IntelDriver { cdclk_state.frequency_khz, cdclk_state.voltage_level ); + let dpll = DisplayPll::new(mmio_arc.clone(), &device_info); + dpll.init()?; + 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)?;