1
//! PDM register programming for I2S hardware after ESP32 (v2+).
2
//!
3
//! Chip-specific differences are handled via `#[cfg(i2s_version = "...")]`.
4

            
5
use super::{
6
    PdmConfig,
7
    PdmDataFormat,
8
    PdmError,
9
    PdmSlotMode,
10
    PdmTxLineMode,
11
    clock,
12
    hp_filter::cut_off_coefficients,
13
};
14
use crate::i2s::master::Info;
15

            
16
pub(crate) fn configure_pdm(i2s: &Info, config: &PdmConfig) -> Result<(), PdmError> {
17
2
    i2s.configure_mclk_pad(config.mclk_out);
18

            
19
2
    if let Some(tx) = &config.tx {
20
        configure_tx(i2s, tx)?;
21
    }
22
1
    if let Some(rx) = &config.rx {
23
        configure_rx(i2s, rx)?;
24
    }
25
    Ok(())
26
}
27

            
28
fn configure_tx(i2s: &Info, config: &super::PdmTxConfig) -> Result<(), PdmError> {
29
1
    if !i2s.pdm_tx {
30
        return Err(PdmError::UnsupportedInstance);
31
    }
32

            
33
    let pcm = config.slot.data_format == PdmDataFormat::Pcm;
34
1
    let clock = clock::calculate_tx_clock(&config.clock, pcm)?;
35

            
36
    let is_mono = config.slot.slot_mode == PdmSlotMode::Mono;
37

            
38
    // Slot/filter setup before clock enable.
39
    i2s.regs().tx_conf().modify(|_, w| w.tx_reset().set_bit());
40
    i2s.regs().tx_conf().modify(|_, w| {
41
        w.tx_slave_mod().clear_bit();
42
        w.tx_tdm_en().clear_bit();
43
        w.tx_pcm_bypass().clear_bit();
44
        w.tx_mono().bit(is_mono);
45
        w.tx_mono_fst_vld().set_bit();
46
        #[cfg(i2s_version = "3")]
47
        w.tx_msb_shift().clear_bit();
48
        w.tx_ws_idle_pol().clear_bit()
49
    });
50
    i2s.regs()
51
        .tx_conf()
52
1
        .modify(|_, w| unsafe { w.tx_chan_mod().bits(if is_mono { 3 } else { 0 }) });
53

            
54
    #[cfg(i2s_version = "2")]
55
    i2s.regs()
56
        .tx_conf1()
57
        .modify(|_, w| w.tx_msb_shift().clear_bit());
58

            
59
    let slot_bits = if is_mono { 16 } else { 32 };
60
    i2s.regs().tx_conf1().modify(|_, w| unsafe {
61
        w.tx_bits_mod().bits(15);
62
        w.tx_tdm_chan_bits().bits(slot_bits - 1);
63
        w.tx_half_sample_bits().bits(15);
64
        w.tx_tdm_ws_width().bits(0)
65
    });
66

            
67
1
    if pcm {
68
1
        let freq_x10 = (config.slot.hp_cut_off_freq_hz * 10.0) as u32;
69
        let (param0, param5) = cut_off_coefficients(freq_x10);
70

            
71
        // Set fp/fs before clock enable.
72
        i2s.regs().tx_pcm2pdm_conf1().modify(|_, w| unsafe {
73
1
            w.tx_pdm_fp().bits(config.clock.up_sample_fp as u16);
74
            w.tx_pdm_fs().bits(config.clock.up_sample_fs as u16);
75
            w.tx_iir_hp_mult12_0().bits(param0 as u8);
76
            w.tx_iir_hp_mult12_5().bits(param5 as u8)
77
        });
78

            
79
        i2s.regs().tx_pcm2pdm_conf().modify(|_, w| unsafe {
80
            w.tx_pdm_sinc_osr2().bits(clock.over_sample_ratio as u8);
81
1
            w.tx_pdm_prescale().bits(config.slot.sd_prescale);
82
            w.tx_pdm_sigmadelta_in_shift()
83
1
                .bits(config.slot.sd_scale.to_register());
84
            w.tx_pdm_hp_in_shift()
85
1
                .bits(config.slot.hp_scale.to_register());
86
            w.tx_pdm_lp_in_shift()
87
1
                .bits(config.slot.lp_scale.to_register());
88
            w.tx_pdm_sinc_in_shift()
89
1
                .bits(config.slot.sinc_scale.to_register());
90
            w.tx_pdm_dac_mode_en()
91
                .bit(config.slot.line_mode != PdmTxLineMode::OneLineCodec);
92
            w.tx_pdm_dac_2out_en()
93
                .bit(config.slot.line_mode == PdmTxLineMode::TwoLineDac);
94
1
            w.tx_pdm_sigmadelta_dither().bit(config.slot.sd_dither != 0);
95
            w.tx_pdm_sigmadelta_dither2()
96
1
                .bit(config.slot.sd_dither2 != 0);
97
            w.tx_pdm_hp_bypass().bit(!config.slot.hp_en)
98
        });
99
    }
100

            
101
    set_pdm_tx_clock(i2s, config.clock.clock_source, &clock);
102

            
103
    i2s.regs().tx_conf().modify(|_, w| {
104
        w.tx_pdm_en().set_bit();
105
        w.tx_tdm_en().clear_bit()
106
    });
107
    i2s.regs()
108
        .tx_pcm2pdm_conf()
109
        .modify(|_, w| w.pcm2pdm_conv_en().bit(pcm));
110

            
111
    Ok(())
112
}
113

            
114
/// Expands a stereo slot mask to include both slots on every active PDM line.
115
fn stereo_pdm_rx_slot_mask(slot_mask: u16) -> u16 {
116
    let mut stereo_mask = 0u16;
117
    for i in 0..8 {
118
        let pair = 0b11u16 << (2 * i);
119
        if slot_mask & pair != 0 {
120
            stereo_mask |= pair;
121
        }
122
    }
123
    stereo_mask
124
}
125

            
126
fn configure_rx(i2s: &Info, config: &super::PdmRxConfig) -> Result<(), PdmError> {
127
1
    if !i2s.pdm_rx {
128
        return Err(PdmError::UnsupportedInstance);
129
    }
130

            
131
    let pcm = config.slot.data_format == PdmDataFormat::Pcm;
132
1
    let mut slot_mask = config.slot.slot_mask.bits();
133
1
    if config.slot.slot_mode == PdmSlotMode::Stereo {
134
        slot_mask = stereo_pdm_rx_slot_mask(slot_mask);
135
    }
136

            
137
1
    let clock = clock::calculate_rx_clock(&config.clock, pcm, slot_mask)?;
138

            
139
    i2s.regs().rx_conf().modify(|_, w| {
140
        w.rx_reset().set_bit();
141
        w.rx_fifo_reset().set_bit()
142
    });
143
    i2s.regs().rx_conf().modify(|_, w| {
144
        w.rx_slave_mod().clear_bit();
145
        w.rx_pcm_bypass().set_bit();
146
        // Mono mode stays disabled for PDM RX on HW v2+.
147
        w.rx_mono().clear_bit();
148
        w
149
    });
150
    #[cfg(i2s_version = "3")]
151
    i2s.regs()
152
        .rx_conf()
153
        .modify(|_, w| w.rx_msb_shift().clear_bit());
154
    #[cfg(i2s_version = "2")]
155
    i2s.regs()
156
        .rx_conf()
157
        .modify(|_, w| unsafe { w.rx_stop_mode().bits(2) });
158

            
159
    #[cfg(i2s_version = "2")]
160
    i2s.regs()
161
        .rx_conf1()
162
        .modify(|_, w| w.rx_msb_shift().clear_bit());
163

            
164
    i2s.regs().rx_conf1().modify(|_, w| unsafe {
165
        w.rx_bits_mod().bits(15);
166
        w.rx_tdm_chan_bits().bits(15);
167
        w.rx_half_sample_bits().bits(15)
168
    });
169

            
170
    // PDM always uses a 2-slot frame; mono DMA expects one active slot only.
171
    i2s.regs()
172
        .rx_tdm_ctrl()
173
        .modify(|_, w| unsafe { w.rx_tdm_tot_chan_num().bits(1) });
174
    i2s.regs()
175
        .rx_tdm_ctrl()
176
1
        .modify(|r, w| unsafe { w.bits((r.bits() & 0xFFFF0000) | u32::from(slot_mask)) });
177

            
178
1
    i2s.configure_rx_mclk(config.clock.clock_source, &clock.dividers);
179
    i2s.set_rx_bclk(clock.dividers.bclk_divider);
180

            
181
    #[cfg(all(i2s_supports_pdm2pcm, i2s_supports_pdm_rx_hp_filter))]
182
    {
183
        let dsr16 = config.clock.downsample_rate == super::PdmDownsampleRate::Dsr16s;
184
        let freq_x10 = (config.slot.hp_cut_off_freq_hz * 10.0) as u32;
185
        let (param0, param5) = cut_off_coefficients(freq_x10);
186
        i2s.regs().rx_pdm2pcm_conf().modify(|_, w| {
187
            w.rx_pdm2pcm_en().bit(pcm);
188
            w.rx_pdm_sinc_dsr_16_en().bit(dsr16);
189
            w.rx_pdm_hp_bypass().bit(!config.slot.hp_en);
190
            unsafe {
191
                w.rx_pdm2pcm_amplify_num()
192
                    .bits(config.slot.amplify_num.clamp(1, 15) as u8);
193
                w.rx_iir_hp_mult12_0().bits(param0 as u8);
194
                w.rx_iir_hp_mult12_5().bits(param5 as u8)
195
            }
196
        });
197
    }
198

            
199
    i2s.regs().rx_conf().modify(|_, w| {
200
        w.rx_pdm_en().set_bit();
201
        w.rx_tdm_en().clear_bit();
202
        #[cfg(all(i2s_supports_pdm2pcm, not(i2s_supports_pdm_rx_hp_filter)))]
203
        {
204
            w.rx_pdm2pcm_en().bit(pcm);
205
            w.rx_pdm_sinc_dsr_16_en()
206
                .bit(config.clock.downsample_rate == super::PdmDownsampleRate::Dsr16s);
207
        }
208
        w
209
    });
210

            
211
    Ok(())
212
}
213

            
214
fn set_pdm_tx_clock(
215
    i2s: &Info,
216
    source: crate::i2s::master::I2sClockSource,
217
    clock: &clock::PdmTxClockResult,
218
) {
219
1
    i2s.configure_tx_mclk(source, &clock.dividers);
220
    i2s.set_tx_bclk(clock.dividers.bclk_divider);
221
}