Skip to content

Commit

Permalink
chore: cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
elagil committed Aug 28, 2024
1 parent 330ee5e commit 590c608
Showing 1 changed file with 130 additions and 48 deletions.
178 changes: 130 additions & 48 deletions embassy-stm32/src/spdifrx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,134 @@

use core::marker::PhantomData;

use embassy_hal_internal::PeripheralRef;
use embassy_hal_internal::{into_ref, PeripheralRef};

use crate::dma::ringbuffer::OverrunError;
pub use crate::dma::word;
#[cfg(not(gpdma))]
use crate::dma::ReadableRingBuffer;
use crate::dma::{Channel, TransferOptions};
use crate::gpio::{AfType, AnyPin, Pull, SealedPin as _};
use crate::interrupt::typelevel::Interrupt;
use crate::pac::spdifrx::Spdifrx as Regs;
use crate::rcc::{self, RccInfo, SealedRccPeripheral};
use crate::rcc::{RccInfo, SealedRccPeripheral};
use crate::{interrupt, peripherals, Peripheral};
use embassy_sync::waitqueue::AtomicWaker;

/// SPDIFRX driver.
/// Ring-buffered SPDIFRX driver.
///
/// Data and, optionally, channel status information are read by DMAs and stored in ring buffers.
pub struct Spdifrx<'d, T: Instance> {
_peri: PeripheralRef<'d, T>,
spdifrx_in: Option<PeripheralRef<'d, AnyPin>>,
data_ring_buffer: ReadableRingBuffer<'d, u32>,
status_ring_buffer: Option<ReadableRingBuffer<'d, u32>>,
channel_status_ring_buffer: Option<ReadableRingBuffer<'d, u32>>,
}

#[cfg(spdifrx_v1)]
fn dr(r: Regs) -> *mut u32 {
r.dr().as_ptr() as _
fn dr_address(r: Regs) -> *mut u32 {
#[cfg(spdifrx_v1)]
let address = r.dr().as_ptr() as _;
#[cfg(spdifrx_h7)]
let address = r.fmt0_dr().as_ptr() as _; // All fmtx_dr() implementations have the same address.

return address;
}

#[cfg(spdifrx_h7)]
fn dr(r: Regs) -> *mut u32 {
// All fmtx_dr() implementations have the same address.
r.fmt0_dr().as_ptr() as _
fn csr_address(r: Regs) -> *mut u32 {
r.csr().as_ptr() as _
}

fn sr(r: Regs) -> *mut u32 {
r.sr().as_ptr() as _
pub enum ControlChannelSelection {
A,
B,
}

pub struct Config {}
pub struct Config {
control_channel_selection: ControlChannelSelection,
enable_preamble_bits: bool,
enable_channel_and_user_bits: bool,
enable_validity_bit: bool,
enable_parity_bit: bool,
}

#[cfg(not(gpdma))]
impl<'d, T: Instance> Spdifrx<'d, T> {
/// Create a new `Spdifrx` instance.
fn dma_opts() -> TransferOptions {
TransferOptions {
half_transfer_ir: true,
// new_write() and new_read() always use circular mode
..Default::default()
}
}

/// Create a new `Spdifrx` instance with only data readout.
pub fn new_data_only(
peri: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::GlobalInterrupt, GlobalInterruptHandler<T>> + 'd,
config: Config,
spdifrx_in: impl Peripheral<P = impl In0Pin<T>> + 'd,
data_dma: impl Peripheral<P = impl Channel + Dma<T>> + 'd,
data_dma_buf: &'d mut [u32],
) -> Self {
let spdifrx_in = new_pin!(spdifrx_in, AfType::input(Pull::None));
Self::setup(false, config);

into_ref!(peri, data_dma);

let regs = T::info().regs;
let dr_request = data_dma.request();
let dr_ring_buffer =
unsafe { ReadableRingBuffer::new(data_dma, dr_request, dr_address(regs), data_dma_buf, Self::dma_opts()) };

Self {
_peri: peri,
spdifrx_in,
data_ring_buffer: dr_ring_buffer,
channel_status_ring_buffer: None,
}
}

/// Create a new `Spdifrx` instance with data and channel-status-register readout.
pub fn new(
peri: T,
peri: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::GlobalInterrupt, GlobalInterruptHandler<T>> + 'd,
config: Config,
spdifrx_in: impl Peripheral<P = impl In0Pin<T>> + 'd,
data_dma: impl Peripheral<P = impl Dma<T>> + 'd,
data_dma: impl Peripheral<P = impl Channel + Dma<T>> + 'd,
data_dma_buf: &'d mut [u32],
channel_status_dma: impl Peripheral<P = impl Channel + Dma<T>> + 'd,
channel_status_dma_buf: &'d mut [u32],
) -> Self {
let peri = peri.into_ref();
let spdifrx_in = new_pin!(spdifrx_in, AfType::input(Pull::None));
Self::setup(true, config);

into_ref!(peri, data_dma, channel_status_dma);

let regs = T::info().regs;
let dr_request = data_dma.request();
let dr_ring_buffer =
unsafe { ReadableRingBuffer::new(data_dma, dr_request, dr_address(regs), data_dma_buf, Self::dma_opts()) };

let csr_request = channel_status_dma.request();
let csr_ring_buffer = unsafe {
ReadableRingBuffer::new(
channel_status_dma,
csr_request,
csr_address(regs),
channel_status_dma_buf,
Self::dma_opts(),
)
};

Self {
_peri: peri,
spdifrx_in,
data_ring_buffer: dr_ring_buffer,
channel_status_ring_buffer: Some(csr_ring_buffer),
}
}

fn setup(read_channel_info: bool, config: Config) {
T::info().rcc.enable_and_reset();
T::GlobalInterrupt::unpend();
unsafe { T::GlobalInterrupt::enable() };
Expand All @@ -64,61 +143,64 @@ impl<'d, T: Instance> Spdifrx<'d, T> {
imr.set_syncdie(true); // Enables SYNCD interrupt.
});

regs.cr().modify(|cr| {
regs.cr().write(|cr| {
cr.set_spdifen(0x01); // Enable SPDIF receiver synchronization.
cr.set_rxdmaen(true); // Use RX DMA.
cr.set_rxdmaen(true); // Use RX DMA for data.
cr.set_cbdmaen(read_channel_info); // Use RX DMA for channel info.
cr.set_rxsteo(true); // Operate in stereo mode.
cr.set_drfmt(0x01); // Data is left-aligned (MSB).
cr.set_pmsk(false); // Write parity error bit to the data register.
cr.set_vmsk(false); // Write validity to the data register.
cr.set_cumsk(false); // C and U bits are written to the data register.
cr.set_ptmsk(false); // Preamble bits are written to the data register.
cr.set_chsel(false); // Channel status is read from sub-frame A.

cr.set_pmsk(config.enable_parity_bit); // Write parity error bit to the data register.
cr.set_vmsk(config.enable_validity_bit); // Write validity to the data register.
cr.set_cumsk(config.enable_channel_and_user_bits); // C and U bits are written to the data register.
cr.set_ptmsk(config.enable_preamble_bits); // Preamble bits are written to the data register.

cr.set_chsel(match config.control_channel_selection {
ControlChannelSelection::A => false,
ControlChannelSelection::B => true,
}); // Channel status is read from sub-frame A.

cr.set_nbtr(0x02); // 16 attempts are allowed.
cr.set_wfa(true); // Wait for activity before going to synchronization phase.
cr.set_insel(0); // FIXME: Input selection.
cr.set_cksen(true); // Generate a symbol clock.
cr.set_cksbkpen(false); // Do not generate a backup symbol clock.
cr.set_cksbkpen(true); // Do not generate a backup symbol clock.
});

let opts = Default::default();

let data_dma = new_dma!(data_dma).unwrap();

let data_ring_buffer =
unsafe { ReadableRingBuffer::new(data_dma.channel, data_dma.request, dr(regs), data_dma_buf, opts) };

Self {
_peri: peri,
spdifrx_in: new_pin!(spdifrx_in, AfType::input(Pull::None)),
data_ring_buffer: data_ring_buffer,
status_ring_buffer: None,
}
}

/// Start the SPDIFRX driver.
pub fn start(&mut self) {
self.data_ring_buffer.start();

if let Some(status_ring_buffer) = self.status_ring_buffer.as_mut() {
status_ring_buffer.start();
if let Some(csr_ring_buffer) = self.channel_status_ring_buffer.as_mut() {
csr_ring_buffer.start();
}
}

/// Reset SPDIFRX operation.
pub fn reset() {
rcc::enable_and_reset::<T>();
}

/// Read from the SPDIFRX data ring buffer.
///
/// The peripheral is configured not to store any channel information in the data register.
/// Therefore, the upper 24 bit are audio sample information, and the lower 8 bit are always zero.
///
/// SPDIFRX is always receiving data in the background. This function pops already-received
/// data from the buffer.
///
/// If there's less than `data.len()` data in the buffer, this waits until there is.
pub async fn read_data(&mut self, data: &mut [u32]) -> Result<usize, OverrunError> {
self.data_ring_buffer.read_exact(data).await
}

/// Read from the SPDIFRX channel status ring buffer.
///
/// Panics, if the instance is not configured for reception of the channel status.
///
/// SPDIFRX is always receiving channel status updates in the background. This function pops already-received
/// data from the buffer.
///
/// If there's less than `data.len()` data in the buffer, this waits until there is.
pub async fn read_channel_status(&mut self, data: &mut [u32]) -> Result<usize, OverrunError> {
self.channel_status_ring_buffer.as_mut().unwrap().read_exact(data).await
}
}

impl<'d, T: Instance> Drop for Spdifrx<'d, T> {
Expand Down Expand Up @@ -180,7 +262,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::GlobalInterrupt> for GlobalIn
regs.cr().modify(|cr| cr.set_spdifen(0x01));
} else if sr.syncd() {
// Synchronization was successful, now enable SPDIFRX.
regs.cr().modify(|cr| cr.set_spdifen(0x11));
regs.cr().modify(|cr| cr.set_spdifen(0x3));
}

// Clear interrupt flags.
Expand Down

0 comments on commit 590c608

Please sign in to comment.