use super::{
PdmConfig,
PdmDataFormat,
PdmError,
PdmSlotMode,
PdmTxLineMode,
clock,
hp_filter::cut_off_coefficients,
};
use crate::i2s::master::Info;
pub(crate) fn configure_pdm(i2s: &Info, config: &PdmConfig) -> Result<(), PdmError> {
if let Some(tx) = &config.tx {
configure_tx(i2s, tx)?;
}
if let Some(rx) = &config.rx {
configure_rx(i2s, rx)?;
}
Ok(())
}
fn configure_tx(i2s: &Info, config: &super::PdmTxConfig) -> Result<(), PdmError> {
if !i2s.pdm_tx {
return Err(PdmError::UnsupportedInstance);
}
let pcm = config.slot.data_format == PdmDataFormat::Pcm;
let clock = clock::calculate_tx_clock(&config.clock, pcm)?;
let is_mono = config.slot.slot_mode == PdmSlotMode::Mono;
i2s.regs().tx_conf().modify(|_, w| w.tx_reset().set_bit());
i2s.regs().tx_conf().modify(|_, w| {
w.tx_slave_mod().clear_bit();
w.tx_tdm_en().clear_bit();
w.tx_pcm_bypass().clear_bit();
w.tx_mono().bit(is_mono);
w.tx_mono_fst_vld().set_bit();
#[cfg(i2s_version = "3")]
w.tx_msb_shift().clear_bit();
w.tx_ws_idle_pol().clear_bit()
});
i2s.regs()
.tx_conf()
.modify(|_, w| unsafe { w.tx_chan_mod().bits(if is_mono { 3 } else { 0 }) });
#[cfg(i2s_version = "2")]
i2s.regs()
.tx_conf1()
.modify(|_, w| w.tx_msb_shift().clear_bit());
let slot_bits = if is_mono { 16 } else { 32 };
i2s.regs().tx_conf1().modify(|_, w| unsafe {
w.tx_bits_mod().bits(15);
w.tx_tdm_chan_bits().bits(slot_bits - 1);
w.tx_half_sample_bits().bits(15);
w.tx_tdm_ws_width().bits(0)
});
if pcm {
let freq_x10 = (config.slot.hp_cut_off_freq_hz * 10.0) as u32;
let (param0, param5) = cut_off_coefficients(freq_x10);
i2s.regs().tx_pcm2pdm_conf1().modify(|_, w| unsafe {
w.tx_pdm_fp().bits(config.clock.up_sample_fp as u16);
w.tx_pdm_fs().bits(config.clock.up_sample_fs as u16);
w.tx_iir_hp_mult12_0().bits(param0 as u8);
w.tx_iir_hp_mult12_5().bits(param5 as u8)
});
i2s.regs().tx_pcm2pdm_conf().modify(|_, w| unsafe {
w.tx_pdm_sinc_osr2().bits(clock.over_sample_ratio as u8);
w.tx_pdm_prescale().bits(config.slot.sd_prescale);
w.tx_pdm_sigmadelta_in_shift()
.bits(config.slot.sd_scale.to_register());
w.tx_pdm_hp_in_shift()
.bits(config.slot.hp_scale.to_register());
w.tx_pdm_lp_in_shift()
.bits(config.slot.lp_scale.to_register());
w.tx_pdm_sinc_in_shift()
.bits(config.slot.sinc_scale.to_register());
w.tx_pdm_dac_mode_en()
.bit(config.slot.line_mode != PdmTxLineMode::OneLineCodec);
w.tx_pdm_dac_2out_en()
.bit(config.slot.line_mode == PdmTxLineMode::TwoLineDac);
w.tx_pdm_sigmadelta_dither().bit(config.slot.sd_dither != 0);
w.tx_pdm_sigmadelta_dither2()
.bit(config.slot.sd_dither2 != 0);
w.tx_pdm_hp_bypass().bit(!config.slot.hp_en)
});
}
cfg_select! {
i2s_clock_configured_by_pcr => {
crate::peripherals::PCR::regs()
.i2s_rx_clkm_conf()
.modify(|_, w| w.i2s_mclk_sel().clear_bit());
}
i2s_clock_configured_by_hp_sys_clkrst => {
}
_ => {
i2s.regs()
.rx_clkm_conf()
.modify(|_, w| w.mclk_sel().clear_bit());
}
}
set_pdm_tx_clock(i2s, &clock);
i2s.regs().tx_conf().modify(|_, w| {
w.tx_pdm_en().set_bit();
w.tx_tdm_en().clear_bit()
});
i2s.regs()
.tx_pcm2pdm_conf()
.modify(|_, w| w.pcm2pdm_conv_en().bit(pcm));
Ok(())
}
fn stereo_pdm_rx_slot_mask(slot_mask: u16) -> u16 {
let mut stereo_mask = 0u16;
for i in 0..8 {
let pair = 0b11u16 << (2 * i);
if slot_mask & pair != 0 {
stereo_mask |= pair;
}
}
stereo_mask
}
fn configure_rx(i2s: &Info, config: &super::PdmRxConfig) -> Result<(), PdmError> {
if !i2s.pdm_rx {
return Err(PdmError::UnsupportedInstance);
}
let pcm = config.slot.data_format == PdmDataFormat::Pcm;
let mut slot_mask = config.slot.slot_mask.bits();
if config.slot.slot_mode == PdmSlotMode::Stereo {
slot_mask = stereo_pdm_rx_slot_mask(slot_mask);
}
let clock = clock::calculate_rx_clock(&config.clock, pcm, slot_mask)?;
i2s.regs().rx_conf().modify(|_, w| {
w.rx_reset().set_bit();
w.rx_fifo_reset().set_bit()
});
i2s.regs().rx_conf().modify(|_, w| {
w.rx_slave_mod().clear_bit();
w.rx_pcm_bypass().set_bit();
w.rx_mono().clear_bit();
w
});
#[cfg(i2s_version = "3")]
i2s.regs()
.rx_conf()
.modify(|_, w| w.rx_msb_shift().clear_bit());
#[cfg(i2s_version = "2")]
i2s.regs()
.rx_conf()
.modify(|_, w| unsafe { w.rx_stop_mode().bits(2) });
#[cfg(i2s_version = "2")]
i2s.regs()
.rx_conf1()
.modify(|_, w| w.rx_msb_shift().clear_bit());
i2s.regs().rx_conf1().modify(|_, w| unsafe {
w.rx_bits_mod().bits(15);
w.rx_tdm_chan_bits().bits(15);
w.rx_half_sample_bits().bits(15)
});
i2s.regs()
.rx_tdm_ctrl()
.modify(|_, w| unsafe { w.rx_tdm_tot_chan_num().bits(1) });
i2s.regs()
.rx_tdm_ctrl()
.modify(|r, w| unsafe { w.bits((r.bits() & 0xFFFF0000) | u32::from(slot_mask)) });
cfg_select! {
i2s_clock_configured_by_pcr => {
crate::peripherals::PCR::regs()
.i2s_rx_clkm_conf()
.modify(|_, w| w.i2s_mclk_sel().set_bit());
}
i2s_clock_configured_by_hp_sys_clkrst => {
}
_ => {
i2s.regs()
.rx_clkm_conf()
.modify(|_, w| w.mclk_sel().set_bit());
}
}
i2s.set_rx_clock(clock.dividers);
#[cfg(all(i2s_supports_pdm2pcm, esp32p4))]
{
let dsr16 = config.clock.downsample_rate == super::PdmDownsampleRate::Dsr16s;
let freq_x10 = (config.slot.hp_cut_off_freq_hz * 10.0) as u32;
let (param0, param5) = cut_off_coefficients(freq_x10);
i2s.regs().rx_pdm2pcm_conf().modify(|_, w| {
w.rx_pdm2pcm_en().bit(pcm);
w.rx_pdm_sinc_dsr_16_en().bit(dsr16);
w.rx_pdm_hp_bypass().bit(!config.slot.hp_en);
unsafe {
w.rx_pdm2pcm_amplify_num()
.bits(config.slot.amplify_num.clamp(1, 15) as u8);
w.rx_iir_hp_mult12_0().bits(param0 as u8);
w.rx_iir_hp_mult12_5().bits(param5 as u8)
}
});
}
i2s.regs().rx_conf().modify(|_, w| {
w.rx_pdm_en().set_bit();
w.rx_tdm_en().clear_bit();
#[cfg(all(i2s_supports_pdm2pcm, not(esp32p4)))]
{
w.rx_pdm2pcm_en().bit(pcm);
w.rx_pdm_sinc_dsr_16_en()
.bit(config.clock.downsample_rate == super::PdmDownsampleRate::Dsr16s);
}
w
});
Ok(())
}
fn set_pdm_tx_clock(i2s: &Info, clock: &clock::PdmTxClockResult) {
set_pdm_tx_clock_common(i2s, clock);
#[cfg(not(any(i2s_clock_configured_by_pcr, i2s_clock_configured_by_hp_sys_clkrst)))]
apply_tx_pdm_clock_workaround(i2s, clock.dividers.mclk_divider);
}
fn set_pdm_tx_clock_common(i2s: &Info, clock: &clock::PdmTxClockResult) {
use crate::i2s::master::private::I2sClockDividers;
let dividers = I2sClockDividers {
mclk_divider: clock.dividers.mclk_divider,
bclk_divider: clock.dividers.bclk_divider,
denominator: clock.dividers.denominator,
numerator: clock.dividers.numerator,
};
i2s.set_tx_clock(dividers);
}
#[cfg(not(any(i2s_clock_configured_by_pcr, i2s_clock_configured_by_hp_sys_clkrst)))]
fn apply_tx_pdm_clock_workaround(i2s: &Info, mclk_div: u32) {
i2s.regs()
.tx_clkm_conf()
.modify(|_, w| unsafe { w.tx_clkm_div_num().bits(2) });
i2s.regs().tx_clkm_div_conf().modify(|_, w| unsafe {
w.tx_clkm_div_yn1().clear_bit();
w.tx_clkm_div_y().bits(1);
w.tx_clkm_div_z().bits(0);
w.tx_clkm_div_x().bits(0)
});
i2s.regs().tx_clkm_div_conf().modify(|_, w| unsafe {
w.tx_clkm_div_yn1().clear_bit();
w.tx_clkm_div_y().bits(1);
w.tx_clkm_div_z().bits(0);
w.tx_clkm_div_x().bits(1)
});
i2s.regs()
.tx_clkm_conf()
.modify(|_, w| unsafe { w.tx_clkm_div_num().bits(mclk_div as u8) });
}