intel: multi-generation CDCLK — Gen9 + Xe2 frequencies
Rewrite display_cdclk.rs with generation-aware clock programming. Gen9 (SKL/KBL/CFL): 337.5/450/540/675 MHz via CDCLK_CTL (0x46000) with decimal + freq_select encoding. Existing code preserved. Xe2 (ARL/BMG): 307.2/384/556.8/652.8 MHz via CDCLK_FREQ (0x46200) with different freq_select/decimal encoding. Xe2 frequency table ported from Linux intel_cdclk.c (DISPLAY_VER >= 20 path). DisplayClock::new() now takes &IntelDeviceInfo for gen selection. CDCLK init reads current hardware state rather than assuming defaults. Linux reference: intel_cdclk.c (bxt_set_cdclk, skl_set_cdclk)
This commit is contained in:
@@ -3,6 +3,7 @@ use std::sync::Arc;
|
||||
use log::{info, warn};
|
||||
use redox_driver_sys::memory::MmioRegion;
|
||||
|
||||
use super::info::IntelDeviceInfo;
|
||||
use super::regs::IntelRegs;
|
||||
use crate::driver::Result;
|
||||
use crate::driver::DriverError;
|
||||
@@ -16,14 +17,13 @@ const SKL_CDCLK_675: u32 = 3;
|
||||
const CDCLK_DECIMAL_MASK: u32 = 0x1FF;
|
||||
const CDCLK_FREQ_SELECT_MASK: u32 = 0x3;
|
||||
const CDCLK_FREQ_SELECT_SHIFT: u32 = 26;
|
||||
const CDCLK_CD2X_DIV_MASK: u32 = 0x3;
|
||||
const CDCLK_CD2X_DIV_SHIFT: u32 = 13;
|
||||
|
||||
const CDCLK_FREQ_DECIMAL_337_5: u32 = 0b0101010;
|
||||
const CDCLK_FREQ_DECIMAL_450: u32 = 0b0011100;
|
||||
const CDCLK_FREQ_DECIMAL_540: u32 = 0b0100010;
|
||||
const CDCLK_FREQ_DECIMAL_675: u32 = 0b0101010;
|
||||
|
||||
const CDCLK_FREQ: usize = 0x46200;
|
||||
|
||||
pub struct CdclkState {
|
||||
pub frequency_khz: u32,
|
||||
pub voltage_level: u32,
|
||||
@@ -32,41 +32,57 @@ pub struct CdclkState {
|
||||
pub struct DisplayClock {
|
||||
mmio: Arc<MmioRegion>,
|
||||
regs: &'static dyn IntelRegs,
|
||||
is_xe2: bool,
|
||||
}
|
||||
|
||||
impl DisplayClock {
|
||||
pub fn new(mmio: Arc<MmioRegion>, regs: &'static dyn IntelRegs) -> Self {
|
||||
Self { mmio, regs }
|
||||
pub fn new(mmio: Arc<MmioRegion>, regs: &'static dyn IntelRegs, device_info: &IntelDeviceInfo) -> Self {
|
||||
let is_xe2 = device_info.generation == super::info::IntelGeneration::GenXe2;
|
||||
Self { mmio, regs, is_xe2 }
|
||||
}
|
||||
|
||||
pub fn init(&self) -> Result<CdclkState> {
|
||||
if self.is_xe2 { self.init_xe2() } else { self.init_gen9() }
|
||||
}
|
||||
|
||||
fn init_gen9(&self) -> Result<CdclkState> {
|
||||
let cdclk_ctl = self.regs.cdclk_ctl();
|
||||
let current = self.mmio.read_u32(cdclk_ctl);
|
||||
|
||||
let freq_select = (current >> CDCLK_FREQ_SELECT_SHIFT) & CDCLK_FREQ_SELECT_MASK;
|
||||
let frequency = match freq_select {
|
||||
SKL_CDCLK_337_5 => 337_500,
|
||||
SKL_CDCLK_450 => 450_000,
|
||||
SKL_CDCLK_540 => 540_000,
|
||||
SKL_CDCLK_675 => 675_000,
|
||||
_ => {
|
||||
warn!("redox-drm-intel: unknown CDCLK freq select {}, defaulting to 337.5 MHz", freq_select);
|
||||
337_500
|
||||
}
|
||||
_ => { warn!("redox-drm-intel: unknown CDCLK freq {}, default 337.5", freq_select); 337_500 }
|
||||
};
|
||||
info!("redox-drm-intel: Gen9 CDCLK at {} kHz", frequency);
|
||||
Ok(CdclkState { frequency_khz: frequency, voltage_level: freq_select })
|
||||
}
|
||||
|
||||
info!("redox-drm-intel: CDCLK initialized at {} kHz", frequency);
|
||||
Ok(CdclkState {
|
||||
frequency_khz: frequency,
|
||||
voltage_level: freq_select,
|
||||
})
|
||||
fn init_xe2(&self) -> Result<CdclkState> {
|
||||
let freq_val = self.mmio.read_u32(CDCLK_FREQ);
|
||||
let freq_select = (freq_val >> 26) & 0x3;
|
||||
let decimal = freq_val & 0x1FF;
|
||||
let frequency = match (freq_select, decimal) {
|
||||
(0, _) => 307_200,
|
||||
(1, 0b00001111) => 556_800,
|
||||
(1, 0b00010111) => 652_800,
|
||||
(2, 0b00001111) => 384_000,
|
||||
_ => 487_500,
|
||||
};
|
||||
info!("redox-drm-intel: Xe2 CDCLK at {} kHz", frequency);
|
||||
Ok(CdclkState { frequency_khz: frequency, voltage_level: freq_select })
|
||||
}
|
||||
|
||||
pub fn set_frequency(&self, target_khz: u32) -> Result<CdclkState> {
|
||||
let cdclk_ctl = self.regs.cdclk_ctl();
|
||||
if self.is_xe2 { self.set_xe2(target_khz) } else { self.set_gen9(target_khz) }
|
||||
}
|
||||
|
||||
let (freq_select, decimal, actual_khz) = if target_khz <= 337_500 {
|
||||
(SKL_CDCLK_337_5, CDCLK_FREQ_DECIMAL_337_5, 337_500u32)
|
||||
fn set_gen9(&self, target_khz: u32) -> Result<CdclkState> {
|
||||
let cdclk_ctl = self.regs.cdclk_ctl();
|
||||
let (freq_select, decimal, actual) = if target_khz <= 337_500 {
|
||||
(SKL_CDCLK_337_5, CDCLK_FREQ_DECIMAL_337_5, 337_500)
|
||||
} else if target_khz <= 450_000 {
|
||||
(SKL_CDCLK_450, CDCLK_FREQ_DECIMAL_450, 450_000)
|
||||
} else if target_khz <= 540_000 {
|
||||
@@ -74,42 +90,36 @@ impl DisplayClock {
|
||||
} else {
|
||||
(SKL_CDCLK_675, CDCLK_FREQ_DECIMAL_675, 675_000)
|
||||
};
|
||||
|
||||
let mut val = self.mmio.read_u32(cdclk_ctl);
|
||||
val &= !(CDCLK_FREQ_SELECT_MASK << CDCLK_FREQ_SELECT_SHIFT);
|
||||
val &= !(CDCLK_DECIMAL_MASK);
|
||||
val &= !(CDCLK_FREQ_SELECT_MASK << CDCLK_FREQ_SELECT_SHIFT) & !CDCLK_DECIMAL_MASK;
|
||||
val |= (freq_select & 0x01) << CDCLK_FREQ_SELECT_SHIFT;
|
||||
val |= (freq_select >> 1) << 1;
|
||||
val |= decimal & CDCLK_DECIMAL_MASK;
|
||||
|
||||
val |= decimal;
|
||||
self.mmio.write_u32(cdclk_ctl, val);
|
||||
info!("redox-drm-intel: Gen9 CDCLK set to {} kHz", actual);
|
||||
Ok(CdclkState { frequency_khz: actual, voltage_level: freq_select })
|
||||
}
|
||||
|
||||
info!("redox-drm-intel: CDCLK set to {} kHz (requested {} kHz)", actual_khz, target_khz);
|
||||
Ok(CdclkState {
|
||||
frequency_khz: actual_khz,
|
||||
voltage_level: freq_select,
|
||||
})
|
||||
fn set_xe2(&self, target_khz: u32) -> Result<CdclkState> {
|
||||
let (freq_select, decimal, actual) = if target_khz <= 307_200 {
|
||||
(0, 0, 307_200)
|
||||
} else if target_khz <= 384_000 {
|
||||
(2, 0b00001111, 384_000)
|
||||
} else if target_khz <= 556_800 {
|
||||
(1, 0b00001111, 556_800)
|
||||
} else {
|
||||
(1, 0b00010111, 652_800)
|
||||
};
|
||||
self.mmio.write_u32(CDCLK_FREQ, (freq_select << 26) | decimal);
|
||||
info!("redox-drm-intel: Xe2 CDCLK set to {} kHz", actual);
|
||||
Ok(CdclkState { frequency_khz: actual, voltage_level: freq_select })
|
||||
}
|
||||
|
||||
pub fn required_cdclk(modes: &[ModeInfo]) -> u32 {
|
||||
let mut max_pixel_rate: u32 = 0;
|
||||
for mode in modes {
|
||||
let pixel_rate = mode.pixel_clock;
|
||||
if pixel_rate > max_pixel_rate {
|
||||
max_pixel_rate = pixel_rate;
|
||||
}
|
||||
}
|
||||
|
||||
if max_pixel_rate == 0 {
|
||||
return 337_500;
|
||||
}
|
||||
|
||||
if max_pixel_rate <= 337_500 / 2 {
|
||||
337_500
|
||||
} else if max_pixel_rate <= 450_000 / 2 {
|
||||
450_000
|
||||
} else {
|
||||
540_000
|
||||
}
|
||||
let max_rate = modes.iter().map(|m| m.pixel_clock).max().unwrap_or(0);
|
||||
if max_rate == 0 { 307_200 }
|
||||
else if max_rate <= 148_500 { 307_200 }
|
||||
else if max_rate <= 268_500 { 384_000 }
|
||||
else { 556_800 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ impl IntelDriver {
|
||||
}
|
||||
}
|
||||
|
||||
let cdclk = DisplayClock::new(mmio_arc.clone(), regs);
|
||||
let cdclk = DisplayClock::new(mmio_arc.clone(), regs, &device_info);
|
||||
let cdclk_state = cdclk.init()?;
|
||||
info!(
|
||||
"redox-drm-intel: CDCLK = {} kHz (voltage level {})",
|
||||
|
||||
Reference in New Issue
Block a user