use super::{PdmDownsampleRate, PdmError, PdmRxClockConfig, PdmTxClockConfig};
use crate::{i2s::master::private::I2sClockDividers, soc, time::Rate};
pub(crate) const PDM_BCK_FACTOR: u32 = 64;
pub(crate) const PDM_TX_BCLK_DIV_MIN: u32 = 8;
pub(crate) const PDM_RX_BCLK_DIV_MIN: u32 = 8;
pub(crate) struct PdmTxClockResult {
pub dividers: I2sClockDividers,
pub over_sample_ratio: u32,
}
pub(crate) struct PdmRxClockResult {
pub dividers: I2sClockDividers,
}
fn calculate_mclk_dividers(sclk: u32, mclk: u32) -> Result<I2sClockDividers, PdmError> {
if (sclk as u64) * 100 <= (mclk as u64) * 199 {
return Err(PdmError::InvalidClock);
}
let dividers = I2sClockDividers::from_frequencies(sclk, mclk, 0);
if dividers.mclk_divider >= 256 {
return Err(PdmError::InvalidClock);
}
Ok(dividers)
}
pub(crate) fn calculate_tx_clock(
clk: &PdmTxClockConfig,
pcm: bool,
) -> Result<PdmTxClockResult, PdmError> {
if clk.up_sample_fs > 480 {
return Err(PdmError::InvalidClock);
}
let rate = clk.sample_rate.as_hz();
let bclk_div = clk.bclk_div.max(PDM_TX_BCLK_DIV_MIN);
let (bclk, over_sample_ratio) = if pcm {
let over_sample_ratio = clk.up_sample_fp / clk.up_sample_fs.max(1);
(rate * PDM_BCK_FACTOR * over_sample_ratio, over_sample_ratio)
} else {
(rate * 2, 0)
};
let mclk = bclk * bclk_div;
let sclk = soc::i2s_sclk_frequency();
let mut dividers = calculate_mclk_dividers(sclk, mclk)?;
dividers.bclk_divider = bclk_div;
Ok(PdmTxClockResult {
dividers,
over_sample_ratio,
})
}
const PDM_RX_CLK_LIMIT_COEFF: u32 = 128;
pub(crate) fn calculate_rx_clock(
clk: &PdmRxClockConfig,
pcm: bool,
slot_mask: u16,
) -> Result<PdmRxClockResult, PdmError> {
let rate = clk.sample_rate.as_hz();
let dn_sample_factor = PDM_BCK_FACTOR * clk.downsample_rate.factor();
let slot_num = slot_mask.count_ones().max(1);
let bclk = if pcm {
rate * dn_sample_factor
} else {
rate * 2
};
let bclk_limit = (PDM_RX_CLK_LIMIT_COEFF * slot_num).div_ceil(dn_sample_factor);
let bclk_div = clk.bclk_div.max(PDM_RX_BCLK_DIV_MIN).max(bclk_limit);
let mclk = bclk * bclk_div;
let sclk = soc::i2s_sclk_frequency();
let mut dividers = calculate_mclk_dividers(sclk, mclk)?;
dividers.bclk_divider = bclk_div;
Ok(PdmRxClockResult { dividers })
}
impl PdmDownsampleRate {
fn factor(self) -> u32 {
match self {
PdmDownsampleRate::Dsr8s => 1,
PdmDownsampleRate::Dsr16s => 2,
}
}
}
impl PdmTxClockConfig {
pub fn codec_default(sample_rate: Rate) -> Self {
Self {
sample_rate,
up_sample_fp: 960,
up_sample_fs: 480,
bclk_div: PDM_TX_BCLK_DIV_MIN,
}
}
pub fn dac_default(sample_rate: Rate) -> Self {
Self {
sample_rate,
up_sample_fp: 960,
up_sample_fs: sample_rate.as_hz() / 100,
bclk_div: 13,
}
}
}
impl PdmRxClockConfig {
pub fn default(sample_rate: Rate) -> Self {
Self {
sample_rate,
downsample_rate: PdmDownsampleRate::Dsr8s,
bclk_div: PDM_RX_BCLK_DIV_MIN,
}
}
}