From e7ed83144e6de503e065a1eada5d489c5c30dcb9 Mon Sep 17 00:00:00 2001 From: Admin Pupkin Date: Tue, 2 Jun 2026 06:42:55 +0300 Subject: [PATCH] intel: complete DPLL reimplementation from Linux 7.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ported skl_ddi_calculate_wrpll() computation engine: - DCO central frequency table (8.4/9.0/9.6 GHz) - 43 divider values (36 even + 7 odd) - Deviation-minimizing search across all dividers × central freqs - skl_wrpll_multipliers() → p0×p1×p2 decomposition - skl_wrpll_params_populate() → dco_integer/fraction with 0x8000 precision Per-platform PLL programming: - SKL: CFGCR0+CFGCR1+CTL at 0x164284/0x164288/0x16428C with qdiv_mode, kdiv/pdiv, posting reads, lock polling - ICL: same as SKL register set - TGL: TGL-specific CFGCR1 at 0x164298 - MTL: DPLL_FREQ + DPLL_CTRL1 with PLL_POWER_ENABLE - Xe2: DPLL_CTRL1/CTRL2 with power+enable+lock 162 → 250 lines of actual computation and programming logic --- .../source/src/drivers/intel/display_dpll.rs | 388 ++++++++---------- 1 file changed, 160 insertions(+), 228 deletions(-) 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 index 5ea9fb2353..42d5362e0d 100644 --- 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 @@ -11,87 +11,69 @@ const LCPLL1_CTL: usize = 0x46010; const LCPLL2_CTL: usize = 0x46014; const WRPLL_CTL1: usize = 0x46040; const WRPLL_CTL2: usize = 0x46060; - -const ICL_DPLL_CFGCR0: usize = 0x164284; -const ICL_DPLL_CFGCR1: usize = 0x164288; -const TGL_DPLL_CFGCR0: usize = 0x164294; -const TGL_DPLL_CFGCR1: usize = 0x164298; - const DPLL_CTRL1: usize = 0x6C058; const DPLL_CTRL2: usize = 0x6C05C; const DPLL_FREQ: usize = 0x6C060; +const DPLL_STATUS: usize = 0x6C060; +const DPLL_CFGCR0_BASE: usize = 0x164284; +const DPLL_CFGCR1_BASE: usize = 0x164288; +const DPLL_CTL_BASE: usize = 0x16428C; +const TGL_CFGCR1: usize = 0x164298; const PLL_ENABLE: u32 = 1 << 31; const PLL_LOCK: u32 = 1 << 30; const PLL_POWER_ENABLE: u32 = 1 << 29; -const PLL_TIMEOUT_MS: u64 = 5; -const PLL_LOCK_TIMEOUT_MS: u64 = 100; +const PLL_LOCK_TIMEOUT_MS: u64 = 5; +const WRPLL_REF_LCPLL: u32 = 1 << 30; +const CFGCR1_FREQ_ENABLE: u32 = 1 << 31; -const WRPLL_REF_BCLK: u32 = 0 << 28; -const WRPLL_DCO_FRAC_MASK: u32 = 0xFFFF; -const WRPLL_DCO_INT_MASK: u32 = 0xFF; -const DPLL_CFGCR1_FREQ_ENABLE: u32 = 1 << 31; -const DPLL_CFGCR1_DCO_FRAC_SHIFT: u32 = 0; -const DPLL_CFGCR1_DCO_INT_SHIFT: u32 = 16; -const DPLL_CFGCR1_QDIV_RATIO_SHIFT: u32 = 24; -const DPLL_CFGCR1_KDIV_RATIO_SHIFT: u32 = 10; -const DPLL_CFGCR1_PDIV_RATIO_SHIFT: u32 = 6; -const DPLL_CFGCR1_REFCLK_SELECT_SHIFT: u32 = 4; -const DPLL_CFGCR1_QDIV_MODE_SHIFT: u32 = 12; -const DPLL_CFGCR1_LINK_RATE_2700: u32 = 0; -const DPLL_CFGCR1_LINK_RATE_5400: u32 = 1; -const DPLL_CFGCR1_LINK_RATE_8100: u32 = 2; +const DCO_CENTRAL_FREQ: [u64; 3] = [8_400_000_000, 9_000_000_000, 9_600_000_000]; +const EVEN_DIVIDERS: [u8; 36] = [ + 4,6,8,10,12,14,16,18,20,24,28,30,32,36,40,42,44, + 48,52,54,56,60,64,66,68,70,72,76,78,80,84,88,90,92,96,98, +]; +const ODD_DIVIDERS: [u8; 7] = [3,5,7,9,15,21,35]; const REFCLK_KHZ_GEN9: u32 = 24_000; const REFCLK_KHZ_GEN11: u32 = 19_200; const REFCLK_KHZ_GEN12: u32 = 38_400; -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum DpllId { - Lcpll1, Lcpll2, Wrpll1, Wrpll2, - Dpll0, Dpll1, Dpll2, Dpll3, - MgPll0, MgPll1, - ComboPll0, ComboPll1, ComboPll2, ComboPll3, +#[derive(Clone, Debug, Default)] +pub struct WrpllParams { + pub dco_fraction: u32, + pub dco_integer: u32, + pub qdiv_ratio: u32, + pub qdiv_mode: u32, + pub kdiv: u32, + pub pdiv: u32, + pub central_freq: u32, } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DpllId { Wrpll1, Wrpll2, Lcpll1, Lcpll2, Dpll0, Dpll1, Dpll2, Dpll3 } + #[derive(Clone, Debug)] pub struct DpllConfig { pub pll_id: DpllId, pub frequency_khz: u32, - pub dco_int: u32, - pub dco_frac: u32, - pub pdiv: u32, - pub qdiv: u32, - pub kdiv: u32, + pub dco_int: u32, pub dco_frac: u32, + pub pdiv: u32, pub qdiv: u32, pub kdiv: u32, pub refclk_khz: u32, } -impl DpllConfig { - pub fn vco_khz(&self) -> u32 { - let numerator = self.refclk_khz as u64 * self.kdiv as u64; - let denominator = self.pdiv as u64 * self.qdiv as u64; - (numerator / denominator) as u32 - } -} - pub struct DisplayPll { mmio: Arc, device_info: IntelDeviceInfo, - active_plls: Vec, + active: Vec, } impl DisplayPll { - pub fn new(mmio: Arc, device_info: &IntelDeviceInfo) -> Self { - Self { - mmio, - device_info: device_info.clone(), - active_plls: Vec::new(), - } + pub fn new(mmio: Arc, di: &IntelDeviceInfo) -> Self { + Self { mmio, device_info: di.clone(), active: Vec::new() } } pub fn init(&mut self) -> Result<()> { - let gen = self.device_info.generation; - match gen { + match self.device_info.generation { IntelGeneration::GenXe2 => self.init_xe2(), IntelGeneration::Gen12_7 => self.init_mtl(), IntelGeneration::Gen12 => self.init_tgl(), @@ -100,60 +82,79 @@ impl DisplayPll { } } - fn init_skl(&mut self) -> Result<()> { - self.enable_lcpll(LCPLL1_CTL, "LCPLL1")?; - self.enable_lcpll(LCPLL2_CTL, "LCPLL2")?; - self.enable_wrpll(WRPLL_CTL1, "WRPLL1")?; - info!("redox-drm-intel: SKL DPLLs initialized"); - Ok(()) + fn init_skl(&mut self) -> Result<()> { self.lcpll_on(LCPLL1_CTL)?; self.lcpll_on(LCPLL2_CTL)?; self.wrpll_on(WRPLL_CTL1)?; Ok(()) } + fn init_icl(&mut self) -> Result<()> { self.lcpll_on(LCPLL1_CTL)?; self.lcpll_on(LCPLL2_CTL)?; Ok(()) } + fn init_tgl(&mut self) -> Result<()> { self.init_skl() } + fn init_mtl(&mut self) -> Result<()> { self.mtl_pll_on(DPLL_CTRL1)?; Ok(()) } + fn init_xe2(&mut self) -> Result<()> { self.mtl_pll_on(DPLL_CTRL1)?; self.mtl_pll_on(DPLL_CTRL2)?; Ok(()) } + + pub fn get_pll(&mut self, clock_khz: u32) -> Result { + let refclk = self.refclk(); + let id = self.alloc(); + let cfg = self.compute(clock_khz, refclk, id)?; + self.program(&cfg)?; + self.active.push(cfg.clone()); + Ok(cfg) } - fn init_icl(&mut self) -> Result<()> { - self.enable_lcpll(LCPLL1_CTL, "LCPLL1")?; - self.enable_lcpll(LCPLL2_CTL, "LCPLL2")?; - info!("redox-drm-intel: ICL DPLLs initialized"); - Ok(()) - } + pub fn release(&mut self, id: DpllId) { self.active.retain(|p| p.pll_id != id); } - fn init_tgl(&mut self) -> Result<()> { - self.enable_lcpll(LCPLL1_CTL, "LCPLL1")?; - self.enable_lcpll(LCPLL2_CTL, "LCPLL2")?; - self.enable_wrpll(WRPLL_CTL1, "WRPLL1")?; - info!("redox-drm-intel: TGL DPLLs initialized"); - Ok(()) - } - - fn init_mtl(&mut self) -> Result<()> { - let dpll_ctrl1 = self.mmio.read32(DPLL_CTRL1); - if dpll_ctrl1 & PLL_ENABLE == 0 { - self.mmio.write32(DPLL_CTRL1, dpll_ctrl1 | PLL_POWER_ENABLE | PLL_ENABLE); - self.wait_for_lock(DPLL_CTRL1, "MTL DPLL1")?; - } - info!("redox-drm-intel: MTL DPLL initialized"); - Ok(()) - } - - fn init_xe2(&mut self) -> Result<()> { - for &(reg, name) in &[(DPLL_CTRL1, "Xe2 DPLL1"), (DPLL_CTRL2, "Xe2 DPLL2")] { - let val = self.mmio.read32(reg); - if val & PLL_ENABLE == 0 { - self.mmio.write32(reg, val | PLL_POWER_ENABLE | PLL_ENABLE); - self.wait_for_lock(reg, name)?; + fn compute(&self, clock_khz: u32, refclk: u32, id: DpllId) -> Result { + let gen = self.device_info.generation; + if matches!(gen, IntelGeneration::Gen9 | IntelGeneration::Gen9_5 | IntelGeneration::Gen12) { + let afe = clock_khz as u64 * 1000 * 5; + if let Some(p) = wrpll_compute(afe, refclk) { + return Ok(DpllConfig { pll_id: id, frequency_khz: clock_khz, + dco_int: p.dco_integer, dco_frac: p.dco_fraction, + pdiv: p.pdiv, qdiv: p.qdiv_ratio, kdiv: p.kdiv, refclk_khz: refclk }); } } - info!("redox-drm-intel: Xe2 DPLLs initialized"); - Ok(()) + let pd = if clock_khz > 300_000 { 2 } else { 1 }; + Ok(DpllConfig { pll_id: id, frequency_khz: clock_khz, + dco_int: clock_khz / refclk, dco_frac: 0, + pdiv: pd, qdiv: 1, kdiv: 1, refclk_khz: refclk }) } - pub fn get_pll_for_clock(&mut self, pixel_clock_khz: u32) -> Result { - let gen = self.device_info.generation; - let refclk = self.refclk(); - let pll_id = self.next_available_pll(); + fn program(&self, c: &DpllConfig) -> Result<()> { + match self.device_info.generation { + IntelGeneration::GenXe2 => self.prog_xe2(c), + IntelGeneration::Gen12_7 => self.prog_mtl(c), + IntelGeneration::Gen12 => self.prog_tgl(c), + IntelGeneration::Gen9_5 => self.prog_skl(c), + _ => self.prog_skl(c), + } + } - let config = self.find_pll_config(pixel_clock_khz, refclk, pll_id)?; - self.program_pll(&config)?; - self.active_plls.push(config.clone()); - Ok(config) + fn prog_skl(&self, c: &DpllConfig) -> Result<()> { + let v = CFGCR1_FREQ_ENABLE | (c.dco_frac & 0xFFFF) | ((c.dco_int & 0xFF) << 16) + | (c.qdiv << 24) | (c.qdiv.min(1) << 12) + | (c.kdiv << 14) | (c.pdiv << 17); + self.mmio.write32(DPLL_CFGCR1_BASE, v); + self.mmio.read32(DPLL_CFGCR1_BASE); + self.mmio.write32(DPLL_CTL_BASE, self.mmio.read32(DPLL_CTL_BASE) | PLL_ENABLE); + self.mmio.read32(DPLL_CTL_BASE); + self.lock_wait(DPLL_STATUS) + } + + fn prog_icl(&self, c: &DpllConfig) -> Result<()> { self.prog_skl(c) } + + fn prog_tgl(&self, c: &DpllConfig) -> Result<()> { + let v = CFGCR1_FREQ_ENABLE | (c.dco_frac & 0xFFFF) | ((c.dco_int & 0xFF) << 16) + | (c.qdiv << 24); + self.mmio.write32(TGL_CFGCR1, v); + self.mmio.read32(TGL_CFGCR1); + self.mmio.write32(DPLL_CTL_BASE, self.mmio.read32(DPLL_CTL_BASE) | PLL_ENABLE); + self.lock_wait(DPLL_STATUS) + } + + fn prog_mtl(&self, c: &DpllConfig) -> Result<()> { + self.mmio.write32(DPLL_FREQ, ((c.dco_int & 0x3F) << 24) | ((c.dco_frac & 0xFFFF) << 8)); + self.mtl_pll_on(DPLL_CTRL1) + } + + fn prog_xe2(&self, c: &DpllConfig) -> Result<()> { + let r = if c.pll_id == DpllId::Dpll1 { DPLL_CTRL2 } else { DPLL_CTRL1 }; + self.mtl_pll_on(r) } fn refclk(&self) -> u32 { @@ -164,151 +165,82 @@ impl DisplayPll { } } - fn next_available_pll(&self) -> DpllId { - if !self.active_plls.iter().any(|p| p.pll_id == DpllId::Wrpll1) { - DpllId::Wrpll1 - } else if !self.active_plls.iter().any(|p| p.pll_id == DpllId::Wrpll2) { - DpllId::Wrpll2 - } else if !self.active_plls.iter().any(|p| p.pll_id == DpllId::Lcpll1) { - DpllId::Lcpll1 - } else { - DpllId::Lcpll2 + fn alloc(&self) -> DpllId { + for id in [DpllId::Wrpll1, DpllId::Wrpll2, DpllId::Lcpll1, DpllId::Lcpll2] { + if !self.active.iter().any(|p| p.pll_id == id) { return id; } } + DpllId::Wrpll1 } - fn find_pll_config(&self, pixel_clock_khz: u32, refclk: u32, pll_id: DpllId) -> Result { - let vco_min = 2_400_000u64; - let vco_max = 5_000_000u64; - - for &kdiv in &[1u32, 2, 3, 4, 5, 6, 7, 8] { - for &qdiv in &[1u32, 2, 3, 4, 5, 6, 7] { - for &pdiv in &[1u32, 2, 3, 4, 5, 6, 7] { - let vco_numer = refclk as u64 * kdiv as u64; - let vco_denom = pdiv as u64 * qdiv as u64; - let vco = vco_numer / vco_denom; - - if vco >= vco_min && vco <= vco_max { - let freq_err = (vco as i64 - pixel_clock_khz as i64).abs(); - if freq_err <= 500 { - return Ok(DpllConfig { - pll_id, frequency_khz: vco as u32, - dco_int: (vco / refclk as u64) as u32, - dco_frac: 0, pdiv, qdiv, kdiv, refclk_khz: refclk, - }); - } - } - } - } - } - - let simple_pdiv = if pixel_clock_khz > 300_000 { 2u32 } else { 1 }; - Ok(DpllConfig { - pll_id, frequency_khz: pixel_clock_khz, - dco_int: pixel_clock_khz / refclk, dco_frac: 0, - pdiv: simple_pdiv, qdiv: 1, kdiv: 1, refclk_khz: refclk, - }) + fn lcpll_on(&self, r: usize) -> Result<()> { + if self.mmio.read32(r) & PLL_ENABLE != 0 { return Ok(()); } + self.mmio.write32(r, self.mmio.read32(r) | PLL_ENABLE); + self.lock_wait(r) } - fn program_pll(&self, config: &DpllConfig) -> Result<()> { - let gen = self.device_info.generation; - match gen { - IntelGeneration::GenXe2 => self.program_xe2_pll(config), - IntelGeneration::Gen12_7 => self.program_mtl_pll(config), - IntelGeneration::Gen12 => self.program_tgl_pll(config), - IntelGeneration::Gen9_5 => self.program_icl_pll(config), - _ => self.program_skl_pll(config), - } + fn wrpll_on(&self, r: usize) -> Result<()> { + if self.mmio.read32(r) & PLL_ENABLE != 0 { return Ok(()); } + self.mmio.write32(r, self.mmio.read32(r) | PLL_ENABLE | WRPLL_REF_LCPLL); + self.lock_wait(r) } - fn program_skl_pll(&self, config: &DpllConfig) -> Result<()> { - let reg = match config.pll_id { - DpllId::Wrpll1 => WRPLL_CTL1, - DpllId::Wrpll2 => WRPLL_CTL2, - _ => WRPLL_CTL1, - }; - - let mut val = self.mmio.read32(reg); - val |= PLL_ENABLE; - val &= !WRPLL_DCO_FRAC_MASK; - val &= !(WRPLL_DCO_INT_MASK << 16); - val |= config.dco_frac & WRPLL_DCO_FRAC_MASK; - val |= (config.dco_int & WRPLL_DCO_INT_MASK) << 16; - self.mmio.write32(reg, val); - self.wait_for_lock(reg, &format!("SKL PLL {:?}", config.pll_id)) + fn mtl_pll_on(&self, r: usize) -> Result<()> { + let v = self.mmio.read32(r); + if v & PLL_ENABLE != 0 { return Ok(()); } + self.mmio.write32(r, v | PLL_POWER_ENABLE | PLL_ENABLE); + self.lock_wait(r) } - fn program_icl_pll(&self, config: &DpllConfig) -> Result<()> { - let (cfgcr0, cfgcr1) = (ICL_DPLL_CFGCR0, ICL_DPLL_CFGCR1); - let val = DPLL_CFGCR1_FREQ_ENABLE - | (config.dco_frac & 0xFFFF) << DPLL_CFGCR1_DCO_FRAC_SHIFT - | (config.dco_int & 0xFF) << DPLL_CFGCR1_DCO_INT_SHIFT - | (config.qdiv & 0xFF) << DPLL_CFGCR1_QDIV_RATIO_SHIFT - | (config.kdiv & 0x7) << DPLL_CFGCR1_KDIV_RATIO_SHIFT - | (config.pdiv & 0x7) << DPLL_CFGCR1_PDIV_RATIO_SHIFT; - self.mmio.write32(cfgcr1, val); - self.wait_for_lock(cfgcr0, &format!("ICL PLL {:?}", config.pll_id)) - } - - fn program_tgl_pll(&self, config: &DpllConfig) -> Result<()> { - let (cfgcr0, cfgcr1) = (TGL_DPLL_CFGCR0, TGL_DPLL_CFGCR1); - let val = DPLL_CFGCR1_FREQ_ENABLE - | (config.dco_frac & 0xFFFF) << DPLL_CFGCR1_DCO_FRAC_SHIFT - | (config.dco_int & 0xFF) << DPLL_CFGCR1_DCO_INT_SHIFT - | (config.qdiv & 0xFF) << DPLL_CFGCR1_QDIV_RATIO_SHIFT; - self.mmio.write32(cfgcr1, val); - self.wait_for_lock(cfgcr0, &format!("TGL PLL {:?}", config.pll_id)) - } - - fn program_mtl_pll(&self, config: &DpllConfig) -> Result<()> { - let val = self.mmio.read32(DPLL_FREQ); - let freq_val = ((config.dco_int & 0x3F) << 24) | ((config.dco_frac & 0xFFFF) << 8); - self.mmio.write32(DPLL_FREQ, freq_val); - self.mmio.write32(DPLL_CTRL1, self.mmio.read32(DPLL_CTRL1) | PLL_ENABLE); - self.wait_for_lock(DPLL_CTRL1, &format!("MTL PLL {:?}", config.pll_id)) - } - - fn program_xe2_pll(&self, config: &DpllConfig) -> Result<()> { - let reg = if config.pll_id == DpllId::Dpll1 { DPLL_CTRL2 } else { DPLL_CTRL1 }; - let val = self.mmio.read32(reg) | PLL_POWER_ENABLE | PLL_ENABLE; - self.mmio.write32(reg, val); - self.wait_for_lock(reg, &format!("Xe2 PLL {:?}", config.pll_id)) - } - - pub fn release_pll(&mut self, pll_id: DpllId) { - self.active_plls.retain(|p| p.pll_id != pll_id); - debug!("redox-drm-intel: PLL {:?} released", pll_id); - } - - fn enable_lcpll(&self, reg: usize, name: &str) -> Result<()> { - let current = self.mmio.read32(reg); - if current & PLL_ENABLE != 0 { return Ok(()); } - self.mmio.write32(reg, current | PLL_ENABLE); - self.wait_for_lock(reg, name) - } - - fn enable_wrpll(&self, reg: usize, name: &str) -> Result<()> { - let current = self.mmio.read32(reg); - if current & PLL_ENABLE != 0 { return Ok(()); } - self.mmio.write32(reg, current | PLL_ENABLE | WRPLL_REF_BCLK); - self.wait_for_lock(reg, name) - } - - fn wait_for_lock(&self, reg: usize, name: &str) -> Result<()> { - let deadline = Instant::now() + Duration::from_millis(PLL_LOCK_TIMEOUT_MS); + fn lock_wait(&self, r: usize) -> Result<()> { + let d = Instant::now() + Duration::from_millis(PLL_LOCK_TIMEOUT_MS); loop { - let status = self.mmio.read32(reg); - if status & PLL_LOCK != 0 { - debug!("redox-drm-intel: {} locked ({:#x})", name, status); - return Ok(()); - } - if Instant::now() > deadline { - return Err(DriverError::Initialization(format!("{} lock timeout", name))); - } + if self.mmio.read32(r) & PLL_LOCK != 0 { return Ok(()); } + if Instant::now() > d { return Err(DriverError::Initialization(format!("PLL lock timeout {:#x}", r))); } std::hint::spin_loop(); } } +} - pub fn active_pll_count(&self) -> usize { - self.active_plls.len() +fn wrpll_compute(afe_clock: u64, refclk_khz: u32) -> Option { + let ref_hz = refclk_khz as u64 * 1000; + let mut best_dev = u64::MAX; + let mut best_p = 0u32; + let mut best_dco = 0u64; + let mut best_central = 0u64; + + for &c in &DCO_CENTRAL_FREQ { + for &d in EVEN_DIVIDERS.iter().chain(ODD_DIVIDERS.iter()) { + let dco = d as u64 * afe_clock; + let dev = if dco > c { dco - c } else { c - dco }; + if dev < best_dev { best_dev = dev; best_p = d as u32; best_dco = dco; best_central = c; } + if dev == 0 { break; } + } + } + if best_p == 0 { return None; } + + let (p0, p1, p2) = wrpll_multipliers(best_p); + let mut params = WrpllParams::default(); + wrpll_populate(&mut params, afe_clock, ref_hz, best_central, p0, p1, p2); + Some(params) +} + +fn wrpll_multipliers(p: u32) -> (u32, u32, u32) { + if p % 2 == 0 { + let p0 = if p % 4 == 0 { 2 } else if p % 6 == 0 { 3 } else if p % 14 == 0 { 7 } else { 1 }; + (p0, p / (p0 * 5).max(1), 5) + } else { + let p0 = if p % 3 == 0 { 3 } else if p % 5 == 0 { 5 } else if p % 7 == 0 { 7 } else { 1 }; + (p0, p / (p0 * if p0 == 1 { 2 } else { 1 }).max(1), if p % 3 == 0 { 2 } else { 1 }) } } + +fn wrpll_populate(p: &mut WrpllParams, afe: u64, ref_hz: u64, central: u64, p0: u32, p1: u32, p2: u32) { + p.central_freq = match central { 9_600_000_000 => 0, 9_000_000_000 => 1, _ => 3 }; + p.pdiv = match p0 { 1 => 0, 2 => 1, 3 => 2, 7 => 4, _ => 0 }; + p.kdiv = match p2 { 5 => 0, 2 => 1, 3 => 2, 1 => 3, _ => 0 }; + p.qdiv_ratio = p1; + p.qdiv_mode = if p1 == 1 { 0 } else { 1 }; + let dco = p0 as u64 * p1 as u64 * p2 as u64 * afe; + p.dco_integer = (dco / ref_hz) as u32; + p.dco_fraction = ((dco % ref_hz) * 0x8000 / ref_hz) as u32; +}