1
use super::MclkFraction;
2
use crate::{
3
    clock::ll::{ClockTree, I2sClkConfig, I2sClkSclk, I2sInstance, I2sMclkOutConfig},
4
    pac::i2s0::RegisterBlock,
5
};
6

            
7
fn clk_sel(sclk: I2sClkSclk) -> u8 {
8
    cfg_select! {
9
25
        esp32c3 => match sclk {
10
            I2sClkSclk::Xtal => 0,
11
            I2sClkSclk::Pll160m => 2,
12
        },
13
        esp32s3 => match sclk {
14
            I2sClkSclk::Xtal => 0,
15
            I2sClkSclk::PllD2 => 1,
16
            I2sClkSclk::Pll160m => 2,
17
        },
18
    }
19
}
20

            
21
impl I2sInstance {
22
    fn regs(self) -> &'static RegisterBlock {
23
        match self {
24
            Self::I2s0 => crate::peripherals::I2S0::regs(),
25
            #[cfg(soc_has_i2s1)]
26
            Self::I2s1 => unsafe { &*crate::peripherals::I2S1::PTR.cast::<RegisterBlock>() },
27
        }
28
    }
29

            
30
    // I2S_TX_CLK
31

            
32
    pub(crate) fn enable_tx_clk_impl(self, _clocks: &mut ClockTree, en: bool) {
33
        self.regs().tx_clkm_conf().modify(|_, w| {
34
            w.clk_en().bit(en);
35
            w.tx_clk_active().bit(en)
36
        });
37
    }
38

            
39
25
    pub(crate) fn configure_tx_clk_impl(
40
        self,
41
        _clocks: &mut ClockTree,
42
        _old_config: Option<I2sClkConfig>,
43
        new_config: I2sClkConfig,
44
    ) {
45
25
        let fraction = MclkFraction::new(new_config.div_a(), new_config.div_b());
46

            
47
        // The divider applies twice, unless the coefficients are set in this sequence, with a
48
        // small division before the target one.
49
        self.regs()
50
            .tx_clkm_conf()
51
            .modify(|_, w| unsafe { w.tx_clkm_div_num().bits(2) });
52
        self.regs().tx_clkm_div_conf().modify(|_, w| unsafe {
53
            w.tx_clkm_div_yn1().clear_bit();
54
            w.tx_clkm_div_y().bits(1);
55
            w.tx_clkm_div_z().bits(0);
56
            w.tx_clkm_div_x().bits(0)
57
        });
58

            
59
        self.regs().tx_clkm_div_conf().modify(|_, w| unsafe {
60
            w.tx_clkm_div_yn1().bit(fraction.yn1);
61
            w.tx_clkm_div_z().bits(fraction.z);
62
            w.tx_clkm_div_y().bits(fraction.y);
63
            w.tx_clkm_div_x().bits(fraction.x)
64
        });
65
        self.regs().tx_clkm_conf().modify(|_, w| unsafe {
66
            w.tx_clk_sel().bits(clk_sel(new_config.sclk()));
67
            w.tx_clkm_div_num().bits(new_config.div_num() as u8)
68
        });
69
25
    }
70

            
71
    // I2S_RX_CLK
72

            
73
    pub(crate) fn enable_rx_clk_impl(self, _clocks: &mut ClockTree, en: bool) {
74
        self.regs()
75
            .rx_clkm_conf()
76
            .modify(|_, w| w.rx_clk_active().bit(en));
77
    }
78

            
79
25
    pub(crate) fn configure_rx_clk_impl(
80
        self,
81
        _clocks: &mut ClockTree,
82
        _old_config: Option<I2sClkConfig>,
83
        new_config: I2sClkConfig,
84
    ) {
85
25
        let fraction = MclkFraction::new(new_config.div_a(), new_config.div_b());
86

            
87
        self.regs()
88
            .rx_clkm_conf()
89
            .modify(|_, w| unsafe { w.rx_clkm_div_num().bits(2) });
90
        self.regs().rx_clkm_div_conf().modify(|_, w| unsafe {
91
            w.rx_clkm_div_yn1().clear_bit();
92
            w.rx_clkm_div_y().bits(1);
93
            w.rx_clkm_div_z().bits(0);
94
            w.rx_clkm_div_x().bits(0)
95
        });
96

            
97
        self.regs().rx_clkm_div_conf().modify(|_, w| unsafe {
98
            w.rx_clkm_div_yn1().bit(fraction.yn1);
99
            w.rx_clkm_div_z().bits(fraction.z);
100
            w.rx_clkm_div_y().bits(fraction.y);
101
            w.rx_clkm_div_x().bits(fraction.x)
102
        });
103
        self.regs().rx_clkm_conf().modify(|_, w| unsafe {
104
            w.rx_clk_sel().bits(clk_sel(new_config.sclk()));
105
            w.rx_clkm_div_num().bits(new_config.div_num() as u8)
106
        });
107
25
    }
108

            
109
    // I2S_MCLK_OUT
110

            
111
    pub(crate) fn enable_mclk_out_impl(self, _clocks: &mut ClockTree, _en: bool) {
112
        // The pad is driven as long as the selected module clock runs.
113
    }
114

            
115
    pub(crate) fn configure_mclk_out_impl(
116
        self,
117
        _clocks: &mut ClockTree,
118
        _old_config: Option<I2sMclkOutConfig>,
119
        new_config: I2sMclkOutConfig,
120
    ) {
121
        self.regs()
122
            .rx_clkm_conf()
123
            .modify(|_, w| w.mclk_sel().bit(matches!(new_config, I2sMclkOutConfig::Rx)));
124
    }
125
}