Skip to main content

ws63_hal/
i2s.rs

1//! I2S (Inter-IC Sound) / PCM audio interface driver for WS63.
2//!
3//! The WS63 I2S peripheral supports both I2S and PCM modes, master and slave
4//! operation, 2-8 channels, configurable data widths, and separate TX/RX
5//! FIFOs with threshold interrupts.
6//!
7//! # Clock configuration
8//!
9//! In master mode, BCLK and FS (frame sync) are generated from the system
10//! clock via programmable dividers.
11//!
12//! * BCLK = I2S_CLK / (BCLK_DIV_NUM + 1)
13//! * FS = BCLK / (FS_DIV_NUM + 1)
14
15use crate::peripherals::I2s;
16
17/// I2S operating mode.
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum I2sMode {
20    /// I2S (Philips) mode.
21    I2s,
22    /// PCM mode.
23    Pcm,
24}
25
26/// Master/slave role.
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum I2sRole {
29    /// Slave (receives BCLK and FS externally).
30    Slave,
31    /// Master (generates BCLK and FS).
32    Master,
33}
34
35/// Clock edge selection.
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum ClockEdge {
38    /// Rising edge.
39    Rising,
40    /// Falling edge.
41    Falling,
42}
43
44/// Number of audio channels.
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum ChannelCount {
47    /// 2 channels (stereo).
48    Two = 0,
49    /// 4 channels.
50    Four = 1,
51    /// 6 channels.
52    Six = 2,
53    /// 8 channels.
54    Eight = 3,
55}
56
57/// TX/RX data width mode.
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub enum DataWidth {
60    /// 8-bit data.
61    Bits8 = 0,
62    /// 10-bit data.
63    Bits10 = 1,
64    /// 12-bit data.
65    Bits12 = 2,
66    /// 14-bit data.
67    Bits14 = 3,
68    /// 16-bit data.
69    Bits16 = 4,
70    /// 18-bit data.
71    Bits18 = 5,
72    /// 20-bit data.
73    Bits20 = 6,
74    /// 24-bit data.
75    Bits24 = 7,
76}
77
78/// I2S configuration.
79#[derive(Debug, Clone, Copy)]
80pub struct I2sConfig {
81    /// I2S or PCM mode.
82    pub mode: I2sMode,
83    /// Master or slave.
84    pub role: I2sRole,
85    /// Clock edge (only used in slave mode).
86    pub clock_edge: ClockEdge,
87    /// Number of channels.
88    pub channels: ChannelCount,
89    /// TX data width.
90    pub tx_width: DataWidth,
91    /// RX data width.
92    pub rx_width: DataWidth,
93    /// BCLK divider in master mode (0-127).
94    pub bclk_div: u8,
95    /// FS divider numerator in master mode (0-1023).
96    pub fs_div_num: u16,
97    /// FS divider ratio in master mode (0-2047).
98    pub fs_div_ratio: u16,
99    /// TX FIFO threshold.
100    pub tx_fifo_threshold: u8,
101    /// RX FIFO threshold.
102    pub rx_fifo_threshold: u8,
103    /// Enable loopback mode (TX → RX internally).
104    pub loopback: bool,
105}
106
107impl Default for I2sConfig {
108    fn default() -> Self {
109        Self {
110            mode: I2sMode::I2s,
111            role: I2sRole::Master,
112            clock_edge: ClockEdge::Rising,
113            channels: ChannelCount::Two,
114            tx_width: DataWidth::Bits16,
115            rx_width: DataWidth::Bits16,
116            bclk_div: 0,
117            fs_div_num: 0,
118            fs_div_ratio: 0,
119            tx_fifo_threshold: 8,
120            rx_fifo_threshold: 8,
121            loopback: false,
122        }
123    }
124}
125
126/// I2S audio interface driver.
127pub struct I2sDriver<'d> {
128    _i2s: I2s<'d>,
129}
130
131impl<'d> I2sDriver<'d> {
132    /// Create a new I2S driver.
133    pub fn new(i2s: I2s<'d>) -> Self {
134        Self { _i2s: i2s }
135    }
136
137    fn regs(&self) -> &'static ws63_pac::i2s::RegisterBlock {
138        // SAFETY: PAC peripheral pointer is a static physical MMIO address, always valid
139        unsafe { &*I2s::ptr() }
140    }
141
142    /// Configure the I2S interface.
143    pub fn configure(&mut self, config: &I2sConfig) {
144        let r = self.regs();
145
146        // Set mode register
147        let mut mode: u32 = 0;
148        mode |= (config.channels as u32) & 0x03; // channels [0:1]
149        if matches!(config.clock_edge, ClockEdge::Falling) {
150            mode |= 1 << 2; // clk_edge
151        }
152        if matches!(config.role, I2sRole::Master) {
153            mode |= 1 << 3; // master_slave
154        }
155        if matches!(config.mode, I2sMode::Pcm) {
156            mode |= 1 << 4; // pcm_mode
157        }
158        unsafe {
159            r.mode().write(|w| w.bits(mode));
160        }
161
162        // Set loopback mode
163        if config.loopback {
164            unsafe {
165                r.version().write(|w| w.bits(1 << 8));
166            }
167        }
168
169        // Set data width modes
170        let dw = ((config.tx_width as u32) & 0x07) | (((config.rx_width as u32) & 0x07) << 8);
171        unsafe {
172            r.data_width_set().write(|w| w.bits(dw));
173        }
174
175        // Set FIFO thresholds
176        let thresh = (config.tx_fifo_threshold as u32 & 0xFF) | ((config.rx_fifo_threshold as u32 & 0xFF) << 8);
177        unsafe {
178            r.fifo_threshold().write(|w| w.bits(thresh));
179        }
180
181        // Set clock dividers (for master mode)
182        unsafe {
183            r.i2s_bclk_div_num().write(|w| w.bits(config.bclk_div as u32 & 0x7F));
184            r.i2s_fs_div_num().write(|w| w.bits(config.fs_div_num as u32 & 0x3FF));
185            r.i2s_fs_div_ratio_num().write(|w| w.bits(config.fs_div_ratio as u32 & 0x7FF));
186        }
187
188        // Enable clock in master mode
189        let crg_val = if matches!(config.role, I2sRole::Master) {
190            0x100 // clk_en = 1
191        } else {
192            0
193        };
194        unsafe {
195            r.i2s_crg().write(|w| w.bits(crg_val));
196        }
197
198        // Enable signed extension (for correct audio sample handling)
199        unsafe {
200            r.signed_ext().write(|w| w.bits(0x01));
201        }
202    }
203
204    /// Enable TX.
205    pub fn enable_tx(&mut self) {
206        unsafe {
207            self.regs().ct_set().write(|w| w.bits(0x01)); // tx_en
208        }
209    }
210
211    /// Enable RX.
212    pub fn enable_rx(&mut self) {
213        unsafe {
214            self.regs().ct_set().write(|w| w.bits(0x02)); // rx_en
215        }
216    }
217
218    /// Disable TX.
219    pub fn disable_tx(&mut self) {
220        unsafe {
221            self.regs().ct_clr().write(|w| w.bits(0x01));
222        }
223    }
224
225    /// Disable RX.
226    pub fn disable_rx(&mut self) {
227        unsafe {
228            self.regs().ct_clr().write(|w| w.bits(0x02));
229        }
230    }
231
232    /// Reset TX FIFO.
233    pub fn reset_tx(&mut self) {
234        unsafe {
235            self.regs().ct_set().write(|w| w.bits(0x04));
236        }
237    }
238
239    /// Reset RX FIFO.
240    pub fn reset_rx(&mut self) {
241        unsafe {
242            self.regs().ct_set().write(|w| w.bits(0x08));
243        }
244    }
245
246    /// Enable TX interrupt.
247    pub fn enable_tx_interrupt(&mut self) {
248        unsafe {
249            self.regs().ct_set().write(|w| w.bits(0x10));
250        }
251    }
252
253    /// Enable RX interrupt.
254    pub fn enable_rx_interrupt(&mut self) {
255        unsafe {
256            self.regs().ct_set().write(|w| w.bits(0x20));
257        }
258    }
259
260    /// Write a sample to the left TX channel.
261    pub fn write_left(&mut self, data: u32) {
262        unsafe {
263            self.regs().left_tx().write(|w| w.bits(data));
264        }
265    }
266
267    /// Write a sample to the right TX channel.
268    pub fn write_right(&mut self, data: u32) {
269        unsafe {
270            self.regs().right_tx().write(|w| w.bits(data));
271        }
272    }
273
274    /// Read a sample from the left RX channel.
275    pub fn read_left(&self) -> u32 {
276        self.regs().left_rx().read().bits()
277    }
278
279    /// Read a sample from the right RX channel.
280    pub fn read_right(&self) -> u32 {
281        self.regs().right_rx().read().bits()
282    }
283
284    /// Get TX FIFO depth (left channel).
285    pub fn tx_fifo_left_depth(&self) -> u8 {
286        (self.regs().tx_sta().read().bits() & 0xFF) as u8
287    }
288
289    /// Get TX FIFO depth (right channel).
290    pub fn tx_fifo_right_depth(&self) -> u8 {
291        ((self.regs().tx_sta().read().bits() >> 8) & 0xFF) as u8
292    }
293
294    /// Get RX FIFO depth (left channel).
295    pub fn rx_fifo_left_depth(&self) -> u8 {
296        (self.regs().rx_sta().read().bits() & 0xFF) as u8
297    }
298
299    /// Get RX FIFO depth (right channel).
300    pub fn rx_fifo_right_depth(&self) -> u8 {
301        ((self.regs().rx_sta().read().bits() >> 8) & 0xFF) as u8
302    }
303
304    /// Check interrupt status.
305    ///
306    /// Returns `(rx_int, tx_int, rx_overflow, tx_underflow)`.
307    pub fn interrupt_status(&self) -> (bool, bool, bool, bool) {
308        let sts = self.regs().intstatus().read().bits();
309        ((sts & 0x01) != 0, (sts & 0x02) != 0, (sts & 0x04) != 0, (sts & 0x08) != 0)
310    }
311
312    /// Clear all interrupts.
313    pub fn clear_interrupts(&mut self) {
314        unsafe {
315            self.regs().intclr().write(|w| w.bits(0x0F));
316        }
317    }
318
319    /// Set the interrupt mask.
320    ///
321    /// * `rx_int_mask` — Mask RX interrupt
322    /// * `tx_int_mask` — Mask TX interrupt
323    /// * `rx_overflow_mask` — Mask RX overflow interrupt
324    /// * `tx_underflow_mask` — Mask TX underflow interrupt
325    pub fn set_interrupt_mask(
326        &mut self,
327        rx_int_mask: bool,
328        tx_int_mask: bool,
329        rx_overflow_mask: bool,
330        tx_underflow_mask: bool,
331    ) {
332        let mut val: u32 = 0;
333        if rx_int_mask {
334            val |= 0x01;
335        }
336        if tx_int_mask {
337            val |= 0x02;
338        }
339        if rx_overflow_mask {
340            val |= 0x04;
341        }
342        if tx_underflow_mask {
343            val |= 0x08;
344        }
345        unsafe {
346            self.regs().intmask().write(|w| w.bits(val));
347        }
348    }
349
350    /// Get the I2S IP version.
351    pub fn version(&self) -> u8 {
352        (self.regs().version().read().bits() & 0xFF) as u8
353    }
354}