intel: DPLL initialization for Gen9 + Xe2
Add display_dpll.rs — pixel clock PLL management. Gen9 (SKL/KBL/CFL): enable LCPLL1/LCPLL2 at 0x46010/0x46014 and WRPLL1 at 0x46040 with WRPLL_REF_BCLK reference clock. Poll PLL_LOCK bit for confirmation. Xe2 (ARL/BMG): enable DPLL_CTRL1/DPLL_CTRL2 at 0x6C058/0x6C05C with PLL_POWER_ENABLE. get_pll_for_clock() returns pdiv=1 or 2 based on pixel clock threshold (300 MHz). Wire into IntelDriver constructor between CDCLK and display init. Linux reference: intel_dpll_mgr.c (skl_wrpll, icl_dpll)
This commit is contained in:
@@ -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<MmioRegion>,
|
||||
is_xe2: bool,
|
||||
}
|
||||
|
||||
impl DisplayPll {
|
||||
pub fn new(mmio: Arc<MmioRegion>, 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<DpllConfig> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<DpAux>,
|
||||
combo_phy: Option<ComboPhy>,
|
||||
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)?;
|
||||
|
||||
Reference in New Issue
Block a user