1
//! PDM clock calculations.
2

            
3
use super::{PdmDownsampleRate, PdmError, PdmRxClockConfig, PdmTxClockConfig};
4
use crate::{
5
    i2s::master::{private::I2sClockDividers, source_frequency},
6
    time::Rate,
7
};
8

            
9
pub(crate) const PDM_BCK_FACTOR: u32 = 64;
10
pub(crate) const PDM_TX_BCLK_DIV_MIN: u32 = 8;
11
pub(crate) const PDM_RX_BCLK_DIV_MIN: u32 = 8;
12

            
13
pub(crate) struct PdmTxClockResult {
14
    pub dividers: I2sClockDividers,
15
    pub over_sample_ratio: u32,
16
}
17

            
18
pub(crate) struct PdmRxClockResult {
19
    pub dividers: I2sClockDividers,
20
}
21

            
22
fn calculate_mclk_dividers(sclk: u32, mclk: u32) -> Result<I2sClockDividers, PdmError> {
23
    // Require `sclk > mclk * 1.99` — use integer math to avoid soft-float.
24
1
    if (sclk as u64) * 100 <= (mclk as u64) * 199 {
25
        return Err(PdmError::InvalidClock);
26
    }
27

            
28
1
    let dividers = I2sClockDividers::from_frequencies(sclk, mclk, 0);
29

            
30
1
    if dividers.mclk_divider >= 256 {
31
        return Err(PdmError::InvalidClock);
32
    }
33

            
34
1
    Ok(dividers)
35
}
36

            
37
pub(crate) fn calculate_tx_clock(
38
    clk: &PdmTxClockConfig,
39
    pcm: bool,
40
) -> Result<PdmTxClockResult, PdmError> {
41
1
    if clk.up_sample_fs > 480 {
42
        return Err(PdmError::InvalidClock);
43
    }
44

            
45
    let rate = clk.sample_rate.as_hz();
46
1
    let bclk_div = clk.bclk_div.max(PDM_TX_BCLK_DIV_MIN);
47

            
48
1
    let (bclk, over_sample_ratio) = if pcm {
49
        let over_sample_ratio = clk.up_sample_fp / clk.up_sample_fs.max(1);
50
1
        (rate * PDM_BCK_FACTOR * over_sample_ratio, over_sample_ratio)
51
    } else {
52
        (rate * 2, 0)
53
    };
54

            
55
1
    let mclk = bclk * bclk_div;
56
1
    let sclk = source_frequency(clk.clock_source);
57
    let mut dividers = calculate_mclk_dividers(sclk, mclk)?;
58
    dividers.bclk_divider = bclk_div;
59

            
60
    Ok(PdmTxClockResult {
61
        dividers,
62
        over_sample_ratio,
63
    })
64
}
65

            
66
const PDM_RX_CLK_LIMIT_COEFF: u32 = 128;
67

            
68
pub(crate) fn calculate_rx_clock(
69
    clk: &PdmRxClockConfig,
70
    pcm: bool,
71
    slot_mask: u16,
72
) -> Result<PdmRxClockResult, PdmError> {
73
    let rate = clk.sample_rate.as_hz();
74
1
    let dn_sample_factor = PDM_BCK_FACTOR * clk.downsample_rate.factor();
75
    let slot_num = slot_mask.count_ones().max(1);
76

            
77
1
    let bclk = if pcm {
78
        rate * dn_sample_factor
79
    } else {
80
1
        rate * 2
81
    };
82

            
83
1
    let bclk_limit = (PDM_RX_CLK_LIMIT_COEFF * slot_num).div_ceil(dn_sample_factor);
84
1
    let bclk_div = clk.bclk_div.max(PDM_RX_BCLK_DIV_MIN).max(bclk_limit);
85

            
86
1
    let mclk = bclk * bclk_div;
87
1
    let sclk = source_frequency(clk.clock_source);
88
    let mut dividers = calculate_mclk_dividers(sclk, mclk)?;
89
    dividers.bclk_divider = bclk_div;
90

            
91
    Ok(PdmRxClockResult { dividers })
92
}
93

            
94
impl PdmDownsampleRate {
95
    fn factor(self) -> u32 {
96
        match self {
97
            PdmDownsampleRate::Dsr8s => 1,
98
            PdmDownsampleRate::Dsr16s => 2,
99
        }
100
    }
101
}
102

            
103
impl PdmTxClockConfig {
104
    /// Default codec-line PDM TX clock (`I2S_PDM_TX_CLK_DEFAULT_CONFIG`).
105
    pub fn codec_default(sample_rate: Rate) -> Self {
106
        Self {
107
            sample_rate,
108
            up_sample_fp: 960,
109
            up_sample_fs: 480,
110
            bclk_div: PDM_TX_BCLK_DIV_MIN,
111
            clock_source: Default::default(),
112
        }
113
    }
114

            
115
    /// Default DAC-line PDM TX clock (`I2S_PDM_TX_CLK_DAC_DEFAULT_CONFIG`).
116
    pub fn dac_default(sample_rate: Rate) -> Self {
117
        Self {
118
            sample_rate,
119
            up_sample_fp: 960,
120
            up_sample_fs: sample_rate.as_hz() / 100,
121
            bclk_div: 13,
122
            clock_source: Default::default(),
123
        }
124
    }
125
}
126

            
127
impl PdmRxClockConfig {
128
    /// Default PDM RX clock (`I2S_PDM_RX_CLK_DEFAULT_CONFIG`).
129
    pub fn default(sample_rate: Rate) -> Self {
130
        Self {
131
            sample_rate,
132
            downsample_rate: PdmDownsampleRate::Dsr8s,
133
            bclk_div: PDM_RX_BCLK_DIV_MIN,
134
            clock_source: Default::default(),
135
        }
136
    }
137
}