openmv/lib/alif/drivers/source/i2s.c
iabdalkader daf2bb30da misc: Restructure repo.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-04-13 08:28:34 +02:00

450 lines
16 KiB
C

/* Copyright (C) 2023 Alif Semiconductor - All Rights Reserved.
* Use, distribution and modification of this code is permitted under the
* terms stated in the Alif Semiconductor Software License Agreement
*
* You should have received a copy of the Alif Semiconductor Software
* License Agreement with this file. If not, please write to:
* contact@alifsemi.com, or visit: https://alifsemi.com/license
*
*/
/**************************************************************************//**
* @file i2s.c
* @author Sudhir Sreedharan
* @email sudhir@alifsemi.com
* @version V1.0.0
* @date 06-06-2023
* @brief Low Level Source File for I2S
* @bug None.
* @Note None
******************************************************************************/
#include "i2s.h"
/**
\fn void i2s_tx_irq_handler(I2S_Type *i2s, i2s_transfer_t *transfer)
\brief Handle interrupts for the I2S Tx.
\param[in] i2s Pointer to the I2S register map
\param[in] transfer The transfer structure for the I2S instance
\return none
*/
void i2s_tx_irq_handler(I2S_Type *i2s, i2s_transfer_t *transfer)
{
uint32_t isr = i2s->I2S_ISR0;
uint32_t tx_fifo_avail = I2S_FIFO_DEPTH - i2s->I2S_TFCR0 - 1;
const uint8_t *buff = transfer->tx_buff;
uint8_t last_lap = 0, num_bytes = 0, count = 0;
I2S_WLEN wlen = (I2S_WLEN)i2s->I2S_TCR0;
uint32_t frames = 0;
/* Copy the data only for the interrupt mode */
if((isr & I2S_ISR_TXFE) && !(i2s->I2S_DMACR & I2S_DMACR_DMAEN_TXBLOCK))
{
if((wlen > I2S_WLEN_RES_NONE) && (wlen <= I2S_WLEN_RES_16_BIT))
num_bytes = I2S_16BIT_BUF_TYPE_BYTES;
else
num_bytes = I2S_32BIT_BUF_TYPE_BYTES;
/* Check if it is the last lap */
if((transfer->tx_current_cnt + (2 * tx_fifo_avail * num_bytes)) > transfer->tx_total_cnt)
{
/* Assign the number of iterations required */
frames = (transfer->tx_total_cnt - transfer->tx_current_cnt) / (2 * num_bytes);
last_lap = 1;
}
else
{
frames = tx_fifo_avail;
}
for(count = 0; count < frames; count++)
{
/* Assuming that application uses 16bit buffer for 16bit data resolution */
if(num_bytes == I2S_16BIT_BUF_TYPE_BYTES)
{
if(transfer->mono_mode)
{
i2s->I2S_LTHR0 = *(const uint16_t*)(buff + transfer->tx_current_cnt);
i2s->I2S_RTHR0 = 0U;
transfer->tx_current_cnt += num_bytes;
}
else
{
i2s->I2S_LTHR0 = *(const uint16_t*)(buff + transfer->tx_current_cnt);
i2s->I2S_RTHR0 = *(const uint16_t*)(buff + transfer->tx_current_cnt + num_bytes);
transfer->tx_current_cnt += (2 * num_bytes);
}
}
else /* For > 16bit data resolution consider as 32bit buffer */
{
if(transfer->mono_mode)
{
i2s->I2S_LTHR0 = *(const uint32_t*)(buff + transfer->tx_current_cnt);
i2s->I2S_RTHR0 = 0U;
transfer->tx_current_cnt += num_bytes;
}
else
{
i2s->I2S_LTHR0 = *(const uint32_t*)(buff + transfer->tx_current_cnt);
i2s->I2S_RTHR0 = *(const uint32_t*)(buff + transfer->tx_current_cnt + num_bytes);
transfer->tx_current_cnt += (2 * num_bytes);
}
}
}
if(last_lap && (transfer->tx_current_cnt < transfer->tx_total_cnt))
{
if(num_bytes == I2S_16BIT_BUF_TYPE_BYTES)
{
/* Write the Left sample and fill right with 0 */
i2s->I2S_LTHR0 = *(const uint16_t*)(buff + transfer->tx_current_cnt);
i2s->I2S_RTHR0 = 0U;
transfer->tx_current_cnt += num_bytes;
}
else
{
/* Write the Left sample and fill right with 0 */
i2s->I2S_LTHR0 = *(const uint32_t*)(buff + transfer->tx_current_cnt);
i2s->I2S_RTHR0 = 0U;
transfer->tx_current_cnt += num_bytes;
}
}
/* Send complete event once all the data is copied to FIFO */
if(transfer->tx_current_cnt >= transfer->tx_total_cnt)
{
/* Disable Tx Interrupt */
i2s_disable_tx_interrupt(i2s);
transfer->status |= I2S_TRANSFER_STATUS_TX_COMPLETE;
}
}
/* This should not happen */
if(isr & I2S_ISR_TXFO)
{
i2s_clear_tx_overrun(i2s);
}
}
/**
\fn void i2s_rx_irq_handler(I2S_Type *i2s, i2s_transfer_t *transfer)
\brief Handle interrupts for the I2S Rx.
\param[in] i2s Pointer to the I2S register map
\param[in] transfer The transfer structure for the I2S instance
\return none
*/
void i2s_rx_irq_handler(I2S_Type *i2s, i2s_transfer_t *transfer)
{
uint32_t rx_fifo_avail = i2s->I2S_RFCR0 + 1;
uint8_t *const buff = transfer->rx_buff;
uint8_t last_lap = 0, num_bytes = 0, count = 0;
I2S_WLEN wlen = (I2S_WLEN)i2s->I2S_RCR0;
uint32_t isr = i2s->I2S_ISR0;
uint32_t frames = 0;
if(isr & I2S_ISR_RXFO)
{
/* Clear overrun interrupt */
i2s_clear_rx_overrun(i2s);
/*
* Disable the Rx Overflow interrupt for now. This will
* be enabled again when Receive function is called
*/
i2s_disable_rx_overflow_interrupt(i2s);
transfer->status |= I2S_TRANSFER_STATUS_RX_OVERFLOW;
}
/* Copy the data only for the interrupt mode */
if((isr & I2S_ISR_RXDA) && !(i2s->I2S_DMACR & I2S_DMACR_DMAEN_RXBLOCK))
{
if((wlen > I2S_WLEN_RES_NONE) && (wlen <= I2S_WLEN_RES_16_BIT))
num_bytes = I2S_16BIT_BUF_TYPE_BYTES;
else
num_bytes = I2S_32BIT_BUF_TYPE_BYTES;
/* Check if it is the last lap */
if((transfer->rx_current_cnt + (2 * rx_fifo_avail * num_bytes)) > transfer->rx_total_cnt)
{
/* Assign the number of iterations required */
frames = (transfer->rx_total_cnt - transfer->rx_current_cnt) / (2 * num_bytes);
last_lap = 1;
}
else
{
frames = rx_fifo_avail;
}
for(count = 0; count < frames; count++)
{
/* Assuming that application uses 16bit buffer for 16bit data resolution */
if(num_bytes == I2S_16BIT_BUF_TYPE_BYTES)
{
uint16_t left_data = (uint16_t)i2s->I2S_LRBR0;
uint16_t right_data = (uint16_t)i2s->I2S_RRBR0;
if(transfer->mono_mode)
{
*(uint16_t*)(buff + transfer->rx_current_cnt) = left_data;
transfer->rx_current_cnt += num_bytes;
}
else
{
*(uint16_t*)(buff + transfer->rx_current_cnt) = left_data;
*(uint16_t*)(buff + transfer->rx_current_cnt + num_bytes) = right_data;
transfer->rx_current_cnt += (2 * num_bytes);
}
}
else /* For > 16bit data resolution consider as 32bit buffer*/
{
uint32_t left_data = i2s->I2S_LRBR0;
uint32_t right_data = i2s->I2S_RRBR0;
if(transfer->mono_mode)
{
*(uint32_t*)(buff + transfer->rx_current_cnt) = left_data;
transfer->rx_current_cnt += num_bytes;
}
else
{
*(uint32_t*)(buff + transfer->rx_current_cnt) = left_data;
*(uint32_t*)(buff + transfer->rx_current_cnt + num_bytes) = right_data;
transfer->rx_current_cnt += (2 * num_bytes);
}
}
}
if(last_lap && (transfer->rx_current_cnt < transfer->rx_total_cnt))
{
if(num_bytes == I2S_16BIT_BUF_TYPE_BYTES)
{
uint16_t left_data = (uint16_t)i2s->I2S_LRBR0;
/* Read the last sample from left */
*(uint16_t*)(buff + transfer->rx_current_cnt) = left_data;
(void)i2s->I2S_RRBR0;
transfer->rx_current_cnt = transfer->rx_current_cnt + num_bytes;
}
else
{
uint32_t left_data = i2s->I2S_LRBR0;
/* Read the last sample from left */
*(uint32_t*)(buff + transfer->rx_current_cnt) = left_data;
(void)i2s->I2S_RRBR0;
transfer->rx_current_cnt = transfer->rx_current_cnt + num_bytes;
}
}
/* Once the buffer is full, send complete event with interrupt disabled */
if(transfer->rx_current_cnt >= transfer->rx_total_cnt)
{
/* Disable Rx Interrupt */
i2s_disable_rx_interrupt(i2s);
transfer->status |= I2S_TRANSFER_STATUS_RX_COMPLETE;
}
}
}
/**
\fn void i2s_send_blocking(I2S_Type *i2s, i2s_transfer_t *transfer)
\brief Execute a blocking I2S send described by the transfer structure.
\param[in] i2s Pointer to the I2S register map
\param[in] transfer Pointer to transfer structure
\return none
*/
void i2s_send_blocking(I2S_Type *i2s, i2s_transfer_t *transfer)
{
const uint8_t *buff = transfer->tx_buff;
I2S_WLEN wlen = (I2S_WLEN)i2s->I2S_TCR0;
uint8_t num_bytes = 0;
uint32_t frames = 0;
uint32_t count = 0;
/* Enable Tx Channel */
i2s_txchannel_enable(i2s);
if((wlen > I2S_WLEN_RES_NONE) && (wlen <= I2S_WLEN_RES_16_BIT))
num_bytes = I2S_16BIT_BUF_TYPE_BYTES;
else
num_bytes = I2S_32BIT_BUF_TYPE_BYTES;
frames = transfer->tx_total_cnt / (2 * num_bytes);
for(count = 0; count < frames; count++)
{
/* Wait Till FIFO gets empty */
while(!(i2s->I2S_ISR0 & I2S_ISR_TXFE));
/* Assuming that application uses 16bit buffer for 16bit data resolution */
if(num_bytes == I2S_16BIT_BUF_TYPE_BYTES)
{
if(transfer->mono_mode)
{
i2s->I2S_LTHR0 = *(const uint16_t*)(buff + transfer->tx_current_cnt);
i2s->I2S_RTHR0 = 0U;
transfer->tx_current_cnt += num_bytes;
}
else
{
i2s->I2S_LTHR0 = *(const uint16_t*)(buff + transfer->tx_current_cnt);
i2s->I2S_RTHR0 = *(const uint16_t*)(buff + transfer->tx_current_cnt + num_bytes);
transfer->tx_current_cnt += (2 * num_bytes);
}
}
else /* For > 16bit data resolution consider as 32bit buffer */
{
if(transfer->mono_mode)
{
i2s->I2S_LTHR0 = *(const uint32_t*)(buff + transfer->tx_current_cnt);
i2s->I2S_RTHR0 = 0U;
transfer->tx_current_cnt += num_bytes;
}
else
{
i2s->I2S_LTHR0 = *(const uint32_t*)(buff + transfer->tx_current_cnt);
i2s->I2S_RTHR0 = *(const uint32_t*)(buff + transfer->tx_current_cnt + num_bytes);
transfer->tx_current_cnt += (2 * num_bytes);
}
}
}
/* Last Lap */
if(transfer->tx_current_cnt < transfer->tx_total_cnt)
{
/* Wait Till FIFO gets empty */
while(!(i2s->I2S_ISR0 & I2S_ISR_TXFE));
if(num_bytes == I2S_16BIT_BUF_TYPE_BYTES)
{
/* Write the Left sample and fill right with 0 */
i2s->I2S_LTHR0 = *(const uint16_t*)(buff + transfer->tx_current_cnt);
i2s->I2S_RTHR0 = 0U;
transfer->tx_current_cnt += num_bytes;
}
else
{
/* Write the Left sample and fill right with 0 */
i2s->I2S_LTHR0 = *(const uint32_t*)(buff + transfer->tx_current_cnt);
i2s->I2S_RTHR0 = 0U;
transfer->tx_current_cnt += num_bytes;
}
}
/* Set complete event */
transfer->status |= I2S_TRANSFER_STATUS_TX_COMPLETE;
}
/**
\fn void i2s_receive_blocking(I2S_Type *i2s, i2s_transfer_t *transfer)
\brief Execute a blocking I2S receive described by the transfer structure.
\param[in] i2s Pointer to the I2S register map
\param[in] transfer Pointer to transfer structure
\return none
*/
void i2s_receive_blocking(I2S_Type *i2s, i2s_transfer_t *transfer)
{
uint8_t *const buff = transfer->rx_buff;
I2S_WLEN wlen = (I2S_WLEN)i2s->I2S_RCR0;
uint8_t num_bytes = 0;
uint32_t frames = 0;
uint32_t count = 0;
/* Enable Rx Channel */
i2s_rxchannel_enable(i2s);
if((wlen > I2S_WLEN_RES_NONE) && (wlen <= I2S_WLEN_RES_16_BIT))
num_bytes = I2S_16BIT_BUF_TYPE_BYTES;
else
num_bytes = I2S_32BIT_BUF_TYPE_BYTES;
/* Assign the number of iterations required */
frames = transfer->rx_total_cnt / (2 * num_bytes);
for(count = 0; count < frames; count++)
{
/* Wait Till DATA is available */
while(!(i2s->I2S_ISR0 & I2S_ISR_RXDA));
/* Assuming that application uses 16bit buffer for 16bit data resolution */
if(num_bytes == I2S_16BIT_BUF_TYPE_BYTES)
{
uint16_t left_data = (uint16_t)i2s->I2S_LRBR0;
uint16_t right_data = (uint16_t)i2s->I2S_RRBR0;
if(transfer->mono_mode)
{
*(uint16_t*)(buff + transfer->rx_current_cnt) = left_data;
transfer->rx_current_cnt += num_bytes;
}
else
{
*(uint16_t*)(buff + transfer->rx_current_cnt) = left_data;
*(uint16_t*)(buff + transfer->rx_current_cnt + num_bytes) = right_data;
transfer->rx_current_cnt += (2 * num_bytes);
}
}
else /* For > 16bit data resolution consider as 32bit buffer*/
{
uint32_t left_data = i2s->I2S_LRBR0;
uint32_t right_data = i2s->I2S_RRBR0;
if(transfer->mono_mode)
{
*(uint32_t*)(buff + transfer->rx_current_cnt) = left_data;
transfer->rx_current_cnt += num_bytes;
}
else
{
*(uint32_t*)(buff + transfer->rx_current_cnt) = left_data;
*(uint32_t*)(buff + transfer->rx_current_cnt + num_bytes) = right_data;
transfer->rx_current_cnt += (2 * num_bytes);
}
}
}
if(transfer->rx_current_cnt < transfer->rx_total_cnt)
{
/* Wait Till DATA is available */
while(!(i2s->I2S_ISR0 & I2S_ISR_RXDA));
if(num_bytes == I2S_16BIT_BUF_TYPE_BYTES)
{
uint16_t left_data = (uint16_t)i2s->I2S_LRBR0;
/* Read the last sample from left */
*(uint16_t*)(buff + transfer->rx_current_cnt) = left_data;
(void)i2s->I2S_RRBR0;
transfer->rx_current_cnt = transfer->rx_current_cnt + num_bytes;
}
else
{
uint32_t left_data = i2s->I2S_LRBR0;
/* Read the last sample from left */
*(uint32_t*)(buff + transfer->rx_current_cnt) = left_data;
(void)i2s->I2S_RRBR0;
transfer->rx_current_cnt = transfer->rx_current_cnt + num_bytes;
}
}
if(i2s->I2S_ISR0 & I2S_ISR_RXFO)
{
/* Clear overrun interrupt */
i2s_clear_rx_overrun(i2s);
transfer->status |= I2S_TRANSFER_STATUS_RX_OVERFLOW;
}
/*send complete event */
transfer->status |= I2S_TRANSFER_STATUS_RX_COMPLETE;
}