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:
2026-05-30 08:25:07 +03:00
parent 58f8e8c6a7
commit aafb835eee
2 changed files with 58 additions and 48 deletions
@@ -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 {})",