#![macro_use]
#![cfg_attr(gpdma, allow(unused))]
use core::marker::PhantomData;
use embassy_sync::waitqueue::AtomicWaker;
use crate::dma::ringbuffer::Error as RingbufferError;
pub use crate::dma::word;
use crate::dma::{Channel, ReadableRingBuffer, TransferOptions};
use crate::gpio::{AfType, Flex, Pull};
use crate::interrupt::typelevel::Interrupt;
use crate::pac::spdifrx::Spdifrx as Regs;
use crate::rcc::{RccInfo, SealedRccPeripheral};
use crate::{Peri, interrupt, peripherals};
#[allow(dead_code)]
#[repr(u8)]
enum PreambleType {
Unused = 0x00,
B = 0x01,
M = 0x02,
W = 0x03,
}
macro_rules! new_spdifrx_pin {
($name:ident, $af_type:expr) => {{
let pin = $name;
let input_sel = pin.input_sel();
set_as_af!(pin, $af_type);
(Some(Flex::new(pin)), input_sel)
}};
}
macro_rules! impl_spdifrx_pin {
($inst:ident, $pin:ident, $af:expr, $sel:expr) => {
impl crate::spdifrx::InPin<peripherals::$inst> for crate::peripherals::$pin {
fn af_num(&self) -> u8 {
$af
}
fn input_sel(&self) -> u8 {
$sel
}
}
};
}
pub struct Spdifrx<'d, T: Instance> {
_peri: Peri<'d, T>,
_spdifrx_in: Option<Flex<'d>>,
data_ring_buffer: ReadableRingBuffer<'d, u32>,
}
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 _;
return address;
}
#[allow(unused)]
fn csr_address(r: Regs) -> *mut u32 {
r.csr().as_ptr() as _
}
pub enum ControlChannelSelection {
A,
B,
}
pub struct Config {
pub control_channel_selection: ControlChannelSelection,
}
#[derive(Debug)]
pub enum Error {
RingbufferError(RingbufferError),
ChannelSyncError,
}
impl From<RingbufferError> for Error {
fn from(error: RingbufferError) -> Self {
Self::RingbufferError(error)
}
}
impl Default for Config {
fn default() -> Self {
Self {
control_channel_selection: ControlChannelSelection::A,
}
}
}
impl<'d, T: Instance> Spdifrx<'d, T> {
fn dma_opts() -> TransferOptions {
TransferOptions {
half_transfer_ir: true,
..Default::default()
}
}
pub fn new<D>(
peri: Peri<'d, T>,
irq: impl interrupt::typelevel::Binding<T::GlobalInterrupt, GlobalInterruptHandler<T>>
+ interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>>
+ 'd,
config: Config,
spdifrx_in: Peri<'d, impl InPin<T>>,
data_dma: Peri<'d, D>,
data_dma_buf: &'d mut [u32],
) -> Self
where
D: Dma<T>,
{
let (spdifrx_in, input_sel) = new_spdifrx_pin!(spdifrx_in, AfType::input(Pull::None));
Self::setup(config, input_sel);
let regs = T::info().regs;
let dr_request = data_dma.request();
let dr_ring_buffer = unsafe {
ReadableRingBuffer::new(
Channel::new(data_dma, irq),
dr_request,
dr_address(regs),
data_dma_buf,
Self::dma_opts(),
)
};
Self {
_peri: peri,
_spdifrx_in: spdifrx_in,
data_ring_buffer: dr_ring_buffer,
}
}
fn setup(config: Config, input_sel: u8) {
T::info().rcc.enable_and_reset();
T::GlobalInterrupt::unpend();
unsafe { T::GlobalInterrupt::enable() };
let regs = T::info().regs;
regs.imr().write(|imr| {
imr.set_ifeie(true); imr.set_syncdie(true); });
regs.cr().write(|cr| {
cr.set_spdifen(0x00); cr.set_rxdmaen(true); cr.set_cbdmaen(false); cr.set_rxsteo(true); cr.set_drfmt(0x01);
cr.set_pmsk(false); cr.set_vmsk(false); cr.set_cumsk(true); cr.set_ptmsk(false);
cr.set_chsel(match config.control_channel_selection {
ControlChannelSelection::A => false,
ControlChannelSelection::B => true,
});
cr.set_nbtr(0x02); cr.set_wfa(true); cr.set_insel(input_sel);
#[cfg(stm32h7)]
cr.set_cksen(true);
#[cfg(stm32h7)]
cr.set_cksbkpen(true); });
}
pub fn start(&mut self) {
self.data_ring_buffer.start();
T::info().regs.cr().modify(|cr| {
cr.set_spdifen(0x03); });
}
pub async fn read(&mut self, data: &mut [u32]) -> Result<(), Error> {
self.data_ring_buffer.read_exact(data).await?;
let first_preamble = (data[0] >> 4) & 0b11_u32;
if (first_preamble as u8) == (PreambleType::W as u8) {
trace!("S/PDIF left/right mismatch");
self.data_ring_buffer.clear();
return Err(Error::ChannelSyncError);
};
for sample in data.as_mut() {
if (*sample & (0x0002_u32)) != 0 {
*sample = 0;
} else {
*sample &= 0xFFFFFF00;
}
}
Ok(())
}
}
impl<'d, T: Instance> Drop for Spdifrx<'d, T> {
fn drop(&mut self) {
T::info().regs.cr().modify(|cr| cr.set_spdifen(0x00));
}
}
struct State {
#[allow(unused)]
waker: AtomicWaker,
}
impl State {
const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
struct Info {
regs: crate::pac::spdifrx::Spdifrx,
rcc: RccInfo,
}
peri_trait!(
irqs: [GlobalInterrupt],
);
pub trait InPin<T: Instance>: crate::gpio::Pin {
fn af_num(&self) -> u8;
fn input_sel(&self) -> u8;
}
dma_trait!(Dma, Instance);
pub struct GlobalInterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::GlobalInterrupt> for GlobalInterruptHandler<T> {
unsafe fn on_interrupt() {
T::state().waker.wake();
let regs = T::info().regs;
let sr = regs.sr().read();
if sr.serr() || sr.terr() || sr.ferr() {
trace!("SPDIFRX error, resync");
regs.cr().modify(|cr| cr.set_spdifen(0x00));
regs.cr().modify(|cr| cr.set_spdifen(0x03));
} else if sr.syncd() {
trace!("SPDIFRX sync success");
}
regs.ifcr().write(|ifcr| {
ifcr.set_perrcf(true); ifcr.set_ovrcf(true); ifcr.set_sbdcf(true); ifcr.set_syncdcf(true); });
}
}
foreach_peripheral!(
(spdifrx, $inst:ident) => {
#[allow(private_interfaces)]
impl SealedInstance for peripherals::$inst {
fn info() -> &'static Info {
static INFO: Info = Info{
regs: crate::pac::$inst,
rcc: crate::peripherals::$inst::RCC_INFO,
};
&INFO
}
fn state() -> &'static State {
static STATE: State = State::new();
&STATE
}
}
impl Instance for peripherals::$inst {
type GlobalInterrupt = crate::_generated::peripheral_interrupts::$inst::GLOBAL;
}
};
);