diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/display_cdclk.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/display_cdclk.rs index 2ecdd15878..fe1c6840d8 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/intel/display_cdclk.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/display_cdclk.rs @@ -1,137 +1,233 @@ use std::sync::Arc; +use std::time::{Duration, Instant}; -use log::{info, warn}; +use log::{debug, info, warn}; use redox_driver_sys::memory::MmioRegion; -use super::info::IntelDeviceInfo; +use super::info::{IntelDeviceInfo, IntelGeneration}; use super::regs::IntelRegs; -use crate::driver::Result; +use crate::driver::{DriverError, Result}; use crate::kms::ModeInfo; -const SKL_CDCLK_337_5: u32 = 0; -const SKL_CDCLK_450: u32 = 1; -const SKL_CDCLK_540: u32 = 2; -const SKL_CDCLK_675: u32 = 3; +const CDCLK_FREQ: usize = 0x46200; +const CDCLK_CTL: usize = 0x46000; + +const BXT_DE_PLL_ENABLE: usize = 0x46070; +const BXT_DE_PLL_CTL: usize = 0x460D0; +const BXT_CDCLK_CD2X_DIV_SEL_SHIFT: u32 = 22; +const BXT_CDCLK_CD2X_DIV_SEL_MASK: u32 = 0x3; +const BXT_CDCLK_CD2X_PIPE_NONE: u32 = 0; +const BXT_CDCLK_CD2X_PIPE_A: u32 = 1; +const BXT_CDCLK_CD2X_PIPE_B: u32 = 2; +const BXT_CDCLK_SSA_PRECHARGE_ENABLE: u32 = 1 << 16; const CDCLK_DECIMAL_MASK: u32 = 0x1FF; const CDCLK_FREQ_SELECT_MASK: u32 = 0x3; const CDCLK_FREQ_SELECT_SHIFT: u32 = 26; +const CDCLK_FREQ_DECIMAL_308_57: u32 = 0b0100110; const CDCLK_FREQ_DECIMAL_337_5: u32 = 0b0101010; +const CDCLK_FREQ_DECIMAL_432: u32 = 0b0011011; const CDCLK_FREQ_DECIMAL_450: u32 = 0b0011100; const CDCLK_FREQ_DECIMAL_540: u32 = 0b0100010; +const CDCLK_FREQ_DECIMAL_617_14: u32 = 0b0100101; const CDCLK_FREQ_DECIMAL_675: u32 = 0b0101010; -const CDCLK_FREQ: usize = 0x46200; +const CDCLK_CTL_CD_FREQ_SEL_MASK: u32 = 0x1FF; +const CDCLK_CTL_CD_FREQ_DECIMAL_MASK: u32 = 0x1FF; +const CDCLK_CTL_CD_FREQ_DECIMAL_SHIFT: u32 = 0; +const CDCLK_FREQ_SEL_337_308: u32 = 0; +const CDCLK_FREQ_SEL_450_432: u32 = 1; +const CDCLK_FREQ_SEL_540: u32 = 2; +const CDCLK_FREQ_SEL_675_617: u32 = 3; +const LCPLL1_CTL: usize = 0x46010; +const LCPLL1_CTL_PLL_ENABLE: u32 = 1 << 31; + +const CDCLK_CHANGE_TIMEOUT_MS: u64 = 10; +const CDCLK_SQUASH_WAVEFORM: u32 = 0x0000_0001; +const CDCLK_CRAWL_WAVEFORM: u32 = 0x0000_0002; + +#[derive(Clone, Debug)] pub struct CdclkState { pub frequency_khz: u32, + pub vco_khz: u32, + pub refclk_khz: u32, pub voltage_level: u32, + pub waveform: u32, +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum CdclkVco { + Vco3200 = 3_200_000, + Vco4800 = 4_800_000, + Vco5400 = 5_400_000, + Vco6480 = 6_480_000, + Vco6750 = 6_750_000, + Vco8100 = 8_100_000, + Vco8640 = 8_640_000, } pub struct DisplayClock { mmio: Arc, regs: &'static dyn IntelRegs, - is_xe2: bool, + device_info: IntelDeviceInfo, + current_state: CdclkState, } impl DisplayClock { pub fn new(mmio: Arc, regs: &'static dyn IntelRegs, device_info: &IntelDeviceInfo) -> Self { - let is_xe2 = device_info.generation == super::info::IntelGeneration::GenXe2; - Self { mmio, regs, is_xe2 } + Self { + mmio, + regs, + device_info: device_info.clone(), + current_state: CdclkState { + frequency_khz: 0, vco_khz: 0, refclk_khz: 19200, + voltage_level: 0, waveform: 0, + }, + } } - pub fn init(&self) -> Result { - if self.is_xe2 { self.init_xe2() } else { self.init_gen9() } + pub fn init(&mut self) -> Result { + let gen = self.device_info.generation; + let state = match gen { + IntelGeneration::GenXe2 => self.init_xe2()?, + IntelGeneration::Gen12_7 => self.init_mtl()?, + IntelGeneration::Gen12 => self.init_tgl()?, + _ => self.init_skl()?, + }; + self.current_state = state.clone(); + info!("redox-drm-intel: CDCLK initialized at {} kHz (vco={} refclk={} waveform={:#x})", + state.frequency_khz, state.vco_khz, state.refclk_khz, state.waveform); + Ok(state) } - fn init_gen9(&self) -> Result { + fn init_skl(&self) -> Result { let cdclk_ctl = self.regs.cdclk_ctl(); let current = self.mmio.read32(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 {}, 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 }) + let decimal = current & CDCLK_DECIMAL_MASK; + + let (freq, vco, waveform) = self.decode_skl_freq(freq_select, decimal); + Ok(CdclkState { + frequency_khz: freq, vco_khz: vco, + refclk_khz: 24000, voltage_level: freq_select, waveform, + }) + } + + fn init_tgl(&self) -> Result { + let val = self.mmio.read32(CDCLK_FREQ); + let freq_select = (val >> CDCLK_FREQ_SELECT_SHIFT) & CDCLK_FREQ_SELECT_MASK; + let decimal = val & CDCLK_DECIMAL_MASK; + + let (freq, vco) = self.decode_tgl_freq(freq_select, decimal); + Ok(CdclkState { + frequency_khz: freq, vco_khz: vco, + refclk_khz: 38400, voltage_level: freq_select, waveform: 0, + }) + } + + fn init_mtl(&self) -> Result { + let val = self.mmio.read32(CDCLK_FREQ); + let freq_select = (val >> CDCLK_FREQ_SELECT_SHIFT) & CDCLK_FREQ_SELECT_MASK; + let (freq, vco) = Self::decode_mtl_freq(freq_select, (val & CDCLK_DECIMAL_MASK) as u32); + Ok(CdclkState { + frequency_khz: freq, vco_khz: vco, + refclk_khz: 38400, voltage_level: freq_select, waveform: 0, + }) } fn init_xe2(&self) -> Result { - let de_cap_offset = self.regs.de_cap(); - if de_cap_offset != 0 { - let de_cap = self.mmio.read32(de_cap_offset); - let cdclk_field = (de_cap >> 16) & 0xF; - if cdclk_field != 0 { - let frequency = match cdclk_field { - 0 => 307_200, - 1 => 384_000, - 2 => 556_800, - 3 => 652_800, - _ => { - warn!( - "redox-drm-intel: Xe2 DE_CAP CDCLK field {} unknown, falling back to register read", - cdclk_field - ); - 0 - } - }; - if frequency != 0 { - info!( - "redox-drm-intel: Xe2 CDCLK {} kHz from DE_CAP ({:#010x})", - frequency, de_cap - ); - return Ok(CdclkState { - frequency_khz: frequency, - voltage_level: cdclk_field, - }); - } + let de_cap = self.regs.de_cap(); + if de_cap != 0 { + let val = self.mmio.read32(de_cap); + let field = (val >> 16) & 0xF; + if field != 0 { + let freq = match field { 1 => 384_000, 2 => 556_800, 3 => 652_800, _ => 307_200 }; + return Ok(CdclkState { + frequency_khz: freq, vco_khz: freq * 16, + refclk_khz: 38400, voltage_level: field, waveform: 0, + }); } } - - let freq_val = self.mmio.read32(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, + let val = self.mmio.read32(CDCLK_FREQ); + let freq_select = (val >> 26) & 0x3; + let decimal = val & 0x1FF; + let freq = 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 (register fallback)", frequency); - Ok(CdclkState { frequency_khz: frequency, voltage_level: freq_select }) + Ok(CdclkState { + frequency_khz: freq, vco_khz: freq * 16, + refclk_khz: 38400, voltage_level: freq_select, waveform: 0, + }) } - pub fn set_frequency(&self, target_khz: u32) -> Result { - if self.is_xe2 { self.set_xe2(target_khz) } else { self.set_gen9(target_khz) } + pub fn set_frequency(&mut self, target_khz: u32) -> Result { + if target_khz == self.current_state.frequency_khz { + return Ok(self.current_state.clone()); + } + + info!("redox-drm-intel: CDCLK transition {} → {} kHz", + self.current_state.frequency_khz, target_khz); + + let gen = self.device_info.generation; + match gen { + IntelGeneration::GenXe2 => self.program_xe2(target_khz), + IntelGeneration::Gen12_7 => self.program_mtl(target_khz), + IntelGeneration::Gen12 => self.program_tgl(target_khz), + _ => self.program_skl(target_khz), + } } - fn set_gen9(&self, target_khz: u32) -> Result { + fn program_skl(&mut self, target_khz: u32) -> Result { 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 { - (SKL_CDCLK_540, CDCLK_FREQ_DECIMAL_540, 540_000) - } else { - (SKL_CDCLK_675, CDCLK_FREQ_DECIMAL_675, 675_000) - }; + let (freq_select, freq) = self.select_skl_cdclk(target_khz); + let decimal = self.skl_cdclk_decimal(target_khz); + let mut val = self.mmio.read32(cdclk_ctl); 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; + val |= (freq_select & 0x03) << CDCLK_FREQ_SELECT_SHIFT; + val |= decimal & CDCLK_DECIMAL_MASK; self.mmio.write32(cdclk_ctl, val); - info!("redox-drm-intel: Gen9 CDCLK set to {} kHz", actual); - Ok(CdclkState { frequency_khz: actual, voltage_level: freq_select }) + + self.wait_cdclk_change(); + let state = CdclkState { + frequency_khz: freq, vco_khz: freq * 16, + refclk_khz: 24000, voltage_level: freq_select, waveform: 0, + }; + self.current_state = state.clone(); + Ok(state) } - fn set_xe2(&self, target_khz: u32) -> Result { - let (freq_select, decimal, actual) = if target_khz <= 307_200 { + fn program_tgl(&mut self, target_khz: u32) -> Result { + let (freq_select, decimal, actual) = self.select_tgl_cdclk(target_khz); + let val = (freq_select << CDCLK_FREQ_SELECT_SHIFT) | (decimal & CDCLK_DECIMAL_MASK); + self.mmio.write32(CDCLK_FREQ, val); + self.wait_cdclk_change(); + + let state = CdclkState { + frequency_khz: actual, vco_khz: actual * 16, + refclk_khz: 38400, voltage_level: freq_select, waveform: 0, + }; + self.current_state = state.clone(); + Ok(state) + } + + fn program_mtl(&mut self, target_khz: u32) -> Result { + let (freq, freq_select) = self.select_mtl_cdclk(target_khz); + self.mmio.write32(CDCLK_FREQ, (freq_select << CDCLK_FREQ_SELECT_SHIFT) | CDCLK_FREQ_DECIMAL_675); + self.wait_cdclk_change(); + + let state = CdclkState { + frequency_khz: freq, vco_khz: freq * 16, + refclk_khz: 38400, voltage_level: freq_select, waveform: 0, + }; + self.current_state = state.clone(); + Ok(state) + } + + fn program_xe2(&mut self, target_khz: u32) -> Result { + let (freq_select, decimal, freq) = if target_khz <= 307_200 { (0, 0, 307_200) } else if target_khz <= 384_000 { (2, 0b00001111, 384_000) @@ -141,15 +237,119 @@ impl DisplayClock { (1, 0b00010111, 652_800) }; self.mmio.write32(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 }) + self.wait_cdclk_change(); + + let state = CdclkState { + frequency_khz: freq, vco_khz: freq * 16, + refclk_khz: 38400, voltage_level: freq_select, waveform: 0, + }; + self.current_state = state.clone(); + Ok(state) } - pub fn required_cdclk(modes: &[ModeInfo]) -> u32 { + fn select_skl_cdclk(&self, target_khz: u32) -> (u32, u32) { + let table: [(u32, u32); 4] = [ + (CDCLK_FREQ_SEL_337_308, 337_500), + (CDCLK_FREQ_SEL_450_432, 450_000), + (CDCLK_FREQ_SEL_540, 540_000), + (CDCLK_FREQ_SEL_675_617, 675_000), + ]; + table.iter().find(|&&(_, f)| target_khz <= f).copied().unwrap_or(table[3]) + } + + fn select_tgl_cdclk(&self, target_khz: u32) -> (u32, u32, u32) { + let table: [(u32, u32, u32); 7] = [ + (0, CDCLK_FREQ_DECIMAL_308_57, 307_200), + (0, CDCLK_FREQ_DECIMAL_308_57, 308_571), + (0, CDCLK_FREQ_DECIMAL_337_5, 337_500), + (0, CDCLK_FREQ_DECIMAL_432, 432_000), + (1, CDCLK_FREQ_DECIMAL_450, 450_000), + (2, CDCLK_FREQ_DECIMAL_540, 540_000), + (3, CDCLK_FREQ_DECIMAL_617_14, 617_143), + ]; + table.iter().find(|&&(_, _, f)| target_khz <= f).copied().unwrap_or(table[6]) + } + + fn select_mtl_cdclk(&self, target_khz: u32) -> (u32, u32) { + let table: [(u32, u32); 4] = [ + (172_800, 0), (307_200, 1), (556_800, 2), (652_800, 3), + ]; + table.iter().find(|&&(f, _)| target_khz <= f).copied().unwrap_or(table[3]) + } + + fn decode_skl_freq(&self, freq_select: u32, decimal: u32) -> (u32, u32, u32) { + let waveform = if decimal == 0 { CDCLK_SQUASH_WAVEFORM } else { CDCLK_CRAWL_WAVEFORM }; + match (freq_select, decimal) { + (CDCLK_FREQ_SEL_337_308, CDCLK_FREQ_DECIMAL_337_5) => (337_500, 5_400_000, waveform), + (CDCLK_FREQ_SEL_337_308, CDCLK_FREQ_DECIMAL_308_57) => (308_571, 4_937_000, waveform), + (CDCLK_FREQ_SEL_450_432, CDCLK_FREQ_DECIMAL_450) => (450_000, 4_800_000, waveform), + (CDCLK_FREQ_SEL_450_432, CDCLK_FREQ_DECIMAL_432) => (432_000, 5_184_000, waveform), + (CDCLK_FREQ_SEL_540, _) => (540_000, 4_320_000, waveform), + (CDCLK_FREQ_SEL_675_617, CDCLK_FREQ_DECIMAL_675) => (675_000, 3_375_000, waveform), + (CDCLK_FREQ_SEL_675_617, CDCLK_FREQ_DECIMAL_617_14) => (617_143, 3_703_000, waveform), + _ => (337_500, 5_400_000, CDCLK_SQUASH_WAVEFORM), + } + } + + fn decode_tgl_freq(&self, freq_select: u32, decimal: u32) -> (u32, u32) { + match (freq_select, decimal) { + (0, CDCLK_FREQ_DECIMAL_308_57) => (308_571, 17_280_000), + (0, CDCLK_FREQ_DECIMAL_337_5) => (337_500, 16_200_000), + (0, CDCLK_FREQ_DECIMAL_432) => (432_000, 17_280_000), + (1, CDCLK_FREQ_DECIMAL_450) => (450_000, 16_200_000), + (2, CDCLK_FREQ_DECIMAL_540) => (540_000, 17_280_000), + (3, CDCLK_FREQ_DECIMAL_617_14) => (617_143, 16_000_000), + _ => (307_200, 14_745_600), + } + } + + fn decode_mtl_freq(_freq_select: u32, _decimal: u32) -> (u32, u32) { + (307_200, 14_745_600) + } + + fn skl_cdclk_decimal(&self, cdclk: u32) -> u32 { + match cdclk { + 308_571 => CDCLK_FREQ_DECIMAL_308_57, + 337_500 => CDCLK_FREQ_DECIMAL_337_5, + 432_000 => CDCLK_FREQ_DECIMAL_432, + 450_000 => CDCLK_FREQ_DECIMAL_450, + 540_000 => CDCLK_FREQ_DECIMAL_540, + 617_143 => CDCLK_FREQ_DECIMAL_617_14, + 675_000 => CDCLK_FREQ_DECIMAL_675, + _ => CDCLK_FREQ_DECIMAL_337_5, + } + } + + fn wait_cdclk_change(&self) { + let cdclk_ctl = self.regs.cdclk_ctl(); + let deadline = Instant::now() + Duration::from_millis(CDCLK_CHANGE_TIMEOUT_MS); + loop { + let val = self.mmio.read32(cdclk_ctl); + if val & (1 << 31) != 0 { break; } + if Instant::now() > deadline { + warn!("redox-drm-intel: CDCLK change timeout (ctl={:#x})", val); + break; + } + std::hint::spin_loop(); + } + } + + pub fn required_cdclk(modes: &[ModeInfo], device_info: &IntelDeviceInfo) -> u32 { let max_rate = modes.iter().map(|m| m.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 } + let base = if max_rate == 0 { 307_200 } + else if max_rate <= 148_500 { 307_200 } + else if max_rate <= 268_500 { 384_000 } + else if max_rate <= 540_000 { 556_800 } + else { 652_800 }; + + base + } + + pub fn current_state(&self) -> &CdclkState { + &self.current_state + } + + pub fn frequency_khz(&self) -> u32 { + self.current_state.frequency_khz } } 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 17edc23166..547cdd5cb1 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 @@ -315,7 +315,7 @@ impl IntelDriver { } } - let cdclk = DisplayClock::new(mmio_arc.clone(), regs, &device_info); + let mut cdclk = DisplayClock::new(mmio_arc.clone(), regs, &device_info); let cdclk_state = cdclk.init()?; info!( "redox-drm-intel: CDCLK = {} kHz (voltage level {})",