850 lines
31 KiB
C
850 lines
31 KiB
C
/*
|
|
* Copyright (c) 2016, Freescale Semiconductor, Inc.
|
|
* Copyright 2016-2022 NXP
|
|
* All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include "fsl_dma.h"
|
|
#include "fsl_i2s_dma.h"
|
|
#include "fsl_flexcomm.h"
|
|
#include <string.h>
|
|
|
|
/*******************************************************************************
|
|
* Definitions
|
|
******************************************************************************/
|
|
|
|
/* Component ID definition, used by tools. */
|
|
#ifndef FSL_COMPONENT_ID
|
|
#define FSL_COMPONENT_ID "platform.drivers.flexcomm_i2s_dma"
|
|
#endif
|
|
|
|
#define DMA_MAX_TRANSFER_BYTES (DMA_MAX_TRANSFER_COUNT * sizeof(uint32_t))
|
|
#define DMA_DESCRIPTORS (2U)
|
|
|
|
/*<! @brief Structure for statically allocated private data. */
|
|
typedef struct _i2s_dma_private_handle
|
|
{
|
|
I2S_Type *base; /*!< I2S base address */
|
|
i2s_dma_handle_t *handle; /*!< I2S handle */
|
|
|
|
volatile uint16_t enqueuedBytes[DMA_DESCRIPTORS]; /*!< Number of bytes being transferred by DMA descriptors */
|
|
volatile uint8_t enqueuedBytesStart; /*!< First item in enqueuedBytes (for reading) */
|
|
volatile uint8_t enqueuedBytesEnd; /*!< Last item in enqueuedBytes (for adding) */
|
|
|
|
volatile uint8_t
|
|
dmaDescriptorsUsed; /*!< Number of DMA descriptors with valid data (in queue, excluding initial descriptor) */
|
|
volatile uint8_t descriptor; /*!< Index of next DMA descriptor in s_DmaDescriptors to be configured with data (does
|
|
not include I2S instance offset) */
|
|
|
|
volatile uint8_t queueDescriptor; /*!< Queue index of buffer to be actually consumed by DMA
|
|
* (queueUser - advanced when user adds a buffer,
|
|
* queueDescriptor - advanced when user buffer queued to DMA,
|
|
* queueDriver - advanced when DMA queued buffer sent out to I2S) */
|
|
volatile i2s_transfer_t descriptorQueue[I2S_NUM_BUFFERS]; /*!< Transfer data to be queued to DMA */
|
|
|
|
volatile bool intA; /*!< If next scheduled DMA transfer will cause interrupt A or B */
|
|
} i2s_dma_private_handle_t;
|
|
|
|
/*! @brief I2S DMA transfer private state. */
|
|
enum _i2s_dma_state
|
|
{
|
|
kI2S_DmaStateIdle = 0x0U, /*!< I2S is in idle state */
|
|
kI2S_DmaStateTx, /*!< I2S is busy transmitting data */
|
|
kI2S_DmaStateRx, /*!< I2S is busy receiving data */
|
|
kI2S_DmaStateBusyLoopTransfer, /*!< I2S is busy loop transfer */
|
|
};
|
|
|
|
/*******************************************************************************
|
|
* Prototypes
|
|
******************************************************************************/
|
|
|
|
static status_t I2S_EnqueueUserBuffer(I2S_Type *base, i2s_dma_handle_t *handle, i2s_transfer_t transfer);
|
|
static uint32_t I2S_GetInstance(I2S_Type *base);
|
|
static inline void I2S_DisableDMAInterrupts(i2s_dma_handle_t *handle);
|
|
static inline void I2S_EnableDMAInterrupts(i2s_dma_handle_t *handle);
|
|
static void I2S_TxEnableDMA(I2S_Type *base, bool enable);
|
|
static void I2S_RxEnableDMA(I2S_Type *base, bool enable);
|
|
static uint16_t I2S_GetTransferBytes(volatile i2s_transfer_t *transfer);
|
|
static status_t I2S_StartTransferDMA(I2S_Type *base, i2s_dma_handle_t *handle);
|
|
static void I2S_AddTransferDMA(I2S_Type *base, i2s_dma_handle_t *handle);
|
|
|
|
/*******************************************************************************
|
|
* Variables
|
|
******************************************************************************/
|
|
/*<! @brief Allocate DMA transfer descriptors. */
|
|
#if (defined(CPU_MIMXRT685SEVKA_dsp) || defined(CPU_MIMXRT685SFVKB_dsp))
|
|
AT_NONCACHEABLE_SECTION_ALIGN(static dma_descriptor_t s_DmaDescriptors[DMA_DESCRIPTORS * FSL_FEATURE_SOC_I2S_COUNT],
|
|
FSL_FEATURE_DMA_LINK_DESCRIPTOR_ALIGN_SIZE);
|
|
#else
|
|
SDK_ALIGN(static dma_descriptor_t s_DmaDescriptors[DMA_DESCRIPTORS * FSL_FEATURE_SOC_I2S_COUNT],
|
|
FSL_FEATURE_DMA_LINK_DESCRIPTOR_ALIGN_SIZE);
|
|
#endif
|
|
|
|
/*<! @brief Buffer with dummy TX data. */
|
|
SDK_ALIGN(static uint32_t s_DummyBufferTx, 4U);
|
|
|
|
/*<! @brief Buffer to fill with RX data to discard. */
|
|
SDK_ALIGN(static uint32_t s_DummyBufferRx, 4U);
|
|
|
|
/*<! @brief Private array of data associated with available I2S peripherals. */
|
|
static i2s_dma_private_handle_t s_DmaPrivateHandle[FSL_FEATURE_SOC_I2S_COUNT];
|
|
|
|
/*<! @brief Base addresses of available I2S peripherals. */
|
|
static const uint32_t s_I2sBaseAddrs[FSL_FEATURE_SOC_I2S_COUNT] = I2S_BASE_ADDRS;
|
|
|
|
/*******************************************************************************
|
|
* Code
|
|
******************************************************************************/
|
|
|
|
static status_t I2S_EnqueueUserBuffer(I2S_Type *base, i2s_dma_handle_t *handle, i2s_transfer_t transfer)
|
|
{
|
|
uint32_t instance = I2S_GetInstance(base);
|
|
i2s_dma_private_handle_t *privateHandle = &(s_DmaPrivateHandle[instance]);
|
|
|
|
/* Validate input data and transfer buffer */
|
|
|
|
assert(handle != NULL);
|
|
if (handle == NULL)
|
|
{
|
|
return kStatus_InvalidArgument;
|
|
}
|
|
|
|
assert((((uint32_t)transfer.data) % 4U) == 0U);
|
|
if ((((uint32_t)transfer.data) % 4U) != 0U)
|
|
{
|
|
/* Data not 4-bytes aligned */
|
|
return kStatus_InvalidArgument;
|
|
}
|
|
|
|
assert(transfer.dataSize != 0U);
|
|
if (transfer.dataSize == 0U)
|
|
{
|
|
/* No data to send or receive */
|
|
return kStatus_InvalidArgument;
|
|
}
|
|
|
|
assert((transfer.dataSize % 4U) == 0U);
|
|
if ((transfer.dataSize % 4U) != 0U)
|
|
{
|
|
/* Data length not multiply of 4 bytes */
|
|
return kStatus_InvalidArgument;
|
|
}
|
|
|
|
if (handle->i2sQueue[handle->queueUser].dataSize != 0UL)
|
|
{
|
|
/* Previously prepared buffers not processed yet, reject request */
|
|
return kStatus_I2S_Busy;
|
|
}
|
|
|
|
/* Enqueue data */
|
|
privateHandle->descriptorQueue[handle->queueUser].data = transfer.data;
|
|
privateHandle->descriptorQueue[handle->queueUser].dataSize = transfer.dataSize;
|
|
handle->i2sQueue[handle->queueUser].data = transfer.data;
|
|
handle->i2sQueue[handle->queueUser].dataSize = transfer.dataSize;
|
|
handle->queueUser = (handle->queueUser + 1U) % I2S_NUM_BUFFERS;
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
static uint32_t I2S_GetInstance(I2S_Type *base)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0U; i < ARRAY_SIZE(s_I2sBaseAddrs); i++)
|
|
{
|
|
if ((uint32_t)base == s_I2sBaseAddrs[i])
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
assert(false);
|
|
return 0U;
|
|
}
|
|
|
|
static inline void I2S_DisableDMAInterrupts(i2s_dma_handle_t *handle)
|
|
{
|
|
DMA_DisableChannelInterrupts(handle->dmaHandle->base, handle->dmaHandle->channel);
|
|
}
|
|
|
|
static inline void I2S_EnableDMAInterrupts(i2s_dma_handle_t *handle)
|
|
{
|
|
if (handle->state != (uint32_t)kI2S_DmaStateIdle)
|
|
{
|
|
DMA_EnableChannelInterrupts(handle->dmaHandle->base, handle->dmaHandle->channel);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* brief Initializes handle for transfer of audio data.
|
|
*
|
|
* param base I2S base pointer.
|
|
* param handle pointer to handle structure.
|
|
* param dmaHandle pointer to dma handle structure.
|
|
* param callback function to be called back when transfer is done or fails.
|
|
* param userData pointer to data passed to callback.
|
|
*/
|
|
void I2S_TxTransferCreateHandleDMA(I2S_Type *base,
|
|
i2s_dma_handle_t *handle,
|
|
dma_handle_t *dmaHandle,
|
|
i2s_dma_transfer_callback_t callback,
|
|
void *userData)
|
|
{
|
|
assert(handle != NULL);
|
|
assert(dmaHandle != NULL);
|
|
|
|
uint32_t instance = I2S_GetInstance(base);
|
|
i2s_dma_private_handle_t *privateHandle = &(s_DmaPrivateHandle[instance]);
|
|
|
|
(void)memset(handle, 0, sizeof(*handle));
|
|
handle->state = (uint32_t)kI2S_DmaStateIdle;
|
|
handle->dmaHandle = dmaHandle;
|
|
handle->completionCallback = callback;
|
|
handle->userData = userData;
|
|
|
|
handle->bytesPerFrame = (uint8_t)((((base->CFG1 & I2S_CFG1_DATALEN_MASK) >> I2S_CFG1_DATALEN_SHIFT) + 1U) / 8U);
|
|
/* if one channel is disabled, bytesPerFrame should be 4U, user should pay attention that when data length is
|
|
* shorter than 16, the data format: left data put in 0-15 bit and right data should put in 16-31
|
|
*/
|
|
if (((base->CFG1 & I2S_CFG1_ONECHANNEL_MASK) == 0U))
|
|
{
|
|
handle->bytesPerFrame = 4U;
|
|
}
|
|
/* since DMA do not support 24bit transfer width, use 32bit instead */
|
|
if (handle->bytesPerFrame == 3U)
|
|
{
|
|
handle->bytesPerFrame = 4U;
|
|
}
|
|
|
|
(void)memset(privateHandle, 0, sizeof(*privateHandle));
|
|
privateHandle->base = base;
|
|
privateHandle->handle = handle;
|
|
|
|
DMA_SetCallback(dmaHandle, I2S_DMACallback, privateHandle);
|
|
}
|
|
|
|
/*!
|
|
* brief Begins or queue sending of the given data.
|
|
*
|
|
* param base I2S base pointer.
|
|
* param handle pointer to handle structure.
|
|
* param transfer data buffer.
|
|
*
|
|
* retval kStatus_Success
|
|
* retval kStatus_I2S_Busy if all queue slots are occupied with unsent buffers.
|
|
*/
|
|
status_t I2S_TxTransferSendDMA(I2S_Type *base, i2s_dma_handle_t *handle, i2s_transfer_t transfer)
|
|
{
|
|
status_t status;
|
|
|
|
I2S_DisableDMAInterrupts(handle);
|
|
|
|
/* Enqueue transfer buffer */
|
|
status = I2S_EnqueueUserBuffer(base, handle, transfer);
|
|
if (status != kStatus_Success)
|
|
{
|
|
I2S_EnableDMAInterrupts(handle);
|
|
return status;
|
|
}
|
|
|
|
/* Initialize DMA transfer */
|
|
if (handle->state == (uint32_t)kI2S_DmaStateIdle)
|
|
{
|
|
handle->state = (uint32_t)kI2S_DmaStateTx;
|
|
status = I2S_StartTransferDMA(base, handle);
|
|
if (status != kStatus_Success)
|
|
{
|
|
I2S_EnableDMAInterrupts(handle);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
I2S_AddTransferDMA(base, handle);
|
|
I2S_EnableDMAInterrupts(handle);
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
/*!
|
|
* brief Aborts transfer of data.
|
|
*
|
|
* param base I2S base pointer.
|
|
* param handle pointer to handle structure.
|
|
*/
|
|
void I2S_TransferAbortDMA(I2S_Type *base, i2s_dma_handle_t *handle)
|
|
{
|
|
assert(handle != NULL);
|
|
assert(handle->dmaHandle != NULL);
|
|
|
|
uint32_t instance = I2S_GetInstance(base);
|
|
i2s_dma_private_handle_t *privateHandle = &(s_DmaPrivateHandle[instance]);
|
|
|
|
I2S_DisableDMAInterrupts(handle);
|
|
|
|
/* Abort operation */
|
|
DMA_AbortTransfer(handle->dmaHandle);
|
|
|
|
if (handle->state == (uint32_t)kI2S_DmaStateTx)
|
|
{
|
|
/* Disable TX */
|
|
I2S_TxEnableDMA(base, false);
|
|
}
|
|
else
|
|
{
|
|
/* Disable RX */
|
|
I2S_RxEnableDMA(base, false);
|
|
}
|
|
|
|
I2S_Disable(base);
|
|
|
|
/* Reset state */
|
|
handle->state = (uint32_t)kI2S_DmaStateIdle;
|
|
|
|
/* Clear transfer queue */
|
|
(void)memset((void *)&(handle->i2sQueue), 0, sizeof(handle->i2sQueue));
|
|
handle->queueDriver = 0U;
|
|
handle->queueUser = 0U;
|
|
|
|
/* Clear internal state */
|
|
(void)memset((void *)&(privateHandle->descriptorQueue), 0, sizeof(privateHandle->descriptorQueue));
|
|
(void)memset((void *)&(privateHandle->enqueuedBytes), 0, sizeof(privateHandle->enqueuedBytes));
|
|
privateHandle->enqueuedBytesStart = 0U;
|
|
privateHandle->enqueuedBytesEnd = 0U;
|
|
privateHandle->dmaDescriptorsUsed = 0U;
|
|
privateHandle->descriptor = 0U;
|
|
privateHandle->queueDescriptor = 0U;
|
|
privateHandle->intA = false;
|
|
}
|
|
|
|
/*!
|
|
* brief Initializes handle for reception of audio data.
|
|
*
|
|
* param base I2S base pointer.
|
|
* param handle pointer to handle structure.
|
|
* param dmaHandle pointer to dma handle structure.
|
|
* param callback function to be called back when transfer is done or fails.
|
|
* param userData pointer to data passed to callback.
|
|
*/
|
|
void I2S_RxTransferCreateHandleDMA(I2S_Type *base,
|
|
i2s_dma_handle_t *handle,
|
|
dma_handle_t *dmaHandle,
|
|
i2s_dma_transfer_callback_t callback,
|
|
void *userData)
|
|
{
|
|
I2S_TxTransferCreateHandleDMA(base, handle, dmaHandle, callback, userData);
|
|
}
|
|
|
|
/*!
|
|
* brief Begins or queue reception of data into given buffer.
|
|
*
|
|
* param base I2S base pointer.
|
|
* param handle pointer to handle structure.
|
|
* param transfer data buffer.
|
|
*
|
|
* retval kStatus_Success
|
|
* retval kStatus_I2S_Busy if all queue slots are occupied with buffers
|
|
* which are not full.
|
|
*/
|
|
status_t I2S_RxTransferReceiveDMA(I2S_Type *base, i2s_dma_handle_t *handle, i2s_transfer_t transfer)
|
|
{
|
|
status_t status;
|
|
|
|
I2S_DisableDMAInterrupts(handle);
|
|
|
|
/* Enqueue transfer buffer */
|
|
status = I2S_EnqueueUserBuffer(base, handle, transfer);
|
|
if (status != kStatus_Success)
|
|
{
|
|
I2S_EnableDMAInterrupts(handle);
|
|
return status;
|
|
}
|
|
|
|
/* Initialize DMA transfer */
|
|
if (handle->state == (uint32_t)kI2S_DmaStateIdle)
|
|
{
|
|
handle->state = (uint32_t)kI2S_DmaStateRx;
|
|
status = I2S_StartTransferDMA(base, handle);
|
|
if (status != kStatus_Success)
|
|
{
|
|
I2S_EnableDMAInterrupts(handle);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
I2S_AddTransferDMA(base, handle);
|
|
I2S_EnableDMAInterrupts(handle);
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
static void I2S_TxEnableDMA(I2S_Type *base, bool enable)
|
|
{
|
|
if (enable)
|
|
{
|
|
base->FIFOCFG |= I2S_FIFOCFG_DMATX_MASK;
|
|
}
|
|
else
|
|
{
|
|
base->FIFOCFG &= (~I2S_FIFOCFG_DMATX_MASK);
|
|
base->FIFOCFG |= I2S_FIFOCFG_EMPTYTX_MASK;
|
|
}
|
|
}
|
|
|
|
static void I2S_RxEnableDMA(I2S_Type *base, bool enable)
|
|
{
|
|
if (enable)
|
|
{
|
|
base->FIFOCFG |= I2S_FIFOCFG_DMARX_MASK;
|
|
}
|
|
else
|
|
{
|
|
base->FIFOCFG &= (~I2S_FIFOCFG_DMARX_MASK);
|
|
base->FIFOCFG |= I2S_FIFOCFG_EMPTYRX_MASK;
|
|
}
|
|
}
|
|
|
|
static uint16_t I2S_GetTransferBytes(volatile i2s_transfer_t *transfer)
|
|
{
|
|
assert(transfer != NULL);
|
|
|
|
uint16_t transferBytes;
|
|
|
|
if (transfer->dataSize >= (2UL * DMA_MAX_TRANSFER_BYTES))
|
|
{
|
|
transferBytes = DMA_MAX_TRANSFER_BYTES;
|
|
}
|
|
else if (transfer->dataSize > DMA_MAX_TRANSFER_BYTES)
|
|
{
|
|
transferBytes = (uint16_t)(transfer->dataSize / 2U);
|
|
if ((transferBytes % 4U) != 0U)
|
|
{
|
|
transferBytes -= (transferBytes % 4U);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
transferBytes = (uint16_t)transfer->dataSize;
|
|
}
|
|
|
|
return transferBytes;
|
|
}
|
|
|
|
/*!
|
|
* brief Install DMA descriptor memory for loop transfer only.
|
|
*
|
|
* This function used to register DMA descriptor memory for the i2s loop dma transfer.
|
|
*
|
|
* It must be callbed before I2S_TransferSendLoopDMA/I2S_TransferReceiveLoopDMA and after
|
|
* I2S_RxTransferCreateHandleDMA/I2S_TxTransferCreateHandleDMA.
|
|
*
|
|
* User should be take care about the address of DMA descriptor pool which required align with 16BYTE at least.
|
|
*
|
|
* param handle Pointer to i2s DMA transfer handle.
|
|
* param dmaDescriptorAddr DMA descriptor start address.
|
|
* param dmaDescriptorNum DMA descriptor number.
|
|
*/
|
|
void I2S_TransferInstallLoopDMADescriptorMemory(i2s_dma_handle_t *handle,
|
|
void *dmaDescriptorAddr,
|
|
size_t dmaDescriptorNum)
|
|
{
|
|
assert(handle != NULL);
|
|
assert((((uint32_t)(uint32_t *)dmaDescriptorAddr) & ((uint32_t)FSL_FEATURE_DMA_LINK_DESCRIPTOR_ALIGN_SIZE - 1UL)) ==
|
|
0UL);
|
|
|
|
handle->i2sLoopDMADescriptor = (dma_descriptor_t *)dmaDescriptorAddr;
|
|
handle->i2sLoopDMADescriptorNum = dmaDescriptorNum;
|
|
}
|
|
|
|
static status_t I2S_TransferLoopDMA(I2S_Type *base,
|
|
i2s_dma_handle_t *handle,
|
|
i2s_transfer_t *xfer,
|
|
uint32_t loopTransferCount)
|
|
{
|
|
assert(handle != NULL);
|
|
assert(handle->dmaHandle != NULL);
|
|
assert(xfer != NULL);
|
|
|
|
uint32_t *srcAddr = NULL, *destAddr = NULL, srcInc = 4UL, destInc = 4UL;
|
|
i2s_transfer_t *currentTransfer = xfer;
|
|
bool intA = true;
|
|
|
|
if (handle->i2sLoopDMADescriptor == NULL)
|
|
{
|
|
return kStatus_InvalidArgument;
|
|
}
|
|
|
|
if (handle->state == (uint32_t)kI2S_DmaStateBusyLoopTransfer)
|
|
{
|
|
return kStatus_I2S_Busy;
|
|
}
|
|
|
|
for (uint32_t i = 0U; i < loopTransferCount; i++)
|
|
{
|
|
currentTransfer = &xfer[i];
|
|
|
|
if ((currentTransfer->data == NULL) || (currentTransfer->dataSize == 0U) ||
|
|
(i >= handle->i2sLoopDMADescriptorNum) ||
|
|
(currentTransfer->dataSize / handle->bytesPerFrame > DMA_MAX_TRANSFER_COUNT))
|
|
{
|
|
return kStatus_InvalidArgument;
|
|
}
|
|
|
|
if (handle->state == (uint32_t)kI2S_DmaStateTx)
|
|
{
|
|
srcAddr = (uint32_t *)(uint32_t)currentTransfer->data;
|
|
destAddr = (uint32_t *)(uint32_t)(&(base->FIFOWR));
|
|
srcInc = 1U;
|
|
destInc = 0UL;
|
|
}
|
|
else
|
|
{
|
|
srcAddr = (uint32_t *)(uint32_t)(&(base->FIFORD));
|
|
destAddr = (uint32_t *)(uint32_t)currentTransfer->data;
|
|
srcInc = 0U;
|
|
destInc = 1UL;
|
|
}
|
|
|
|
intA = intA == true ? false : true;
|
|
|
|
if (i == (loopTransferCount - 1U))
|
|
{
|
|
/* set up linked descriptor */
|
|
DMA_SetupDescriptor(&handle->i2sLoopDMADescriptor[i],
|
|
DMA_CHANNEL_XFER(true, false, intA, !intA, handle->bytesPerFrame, (uint8_t)srcInc,
|
|
(uint8_t)destInc, currentTransfer->dataSize),
|
|
srcAddr, destAddr, &handle->i2sLoopDMADescriptor[0U]);
|
|
}
|
|
else
|
|
{
|
|
/* set up linked descriptor */
|
|
DMA_SetupDescriptor(&handle->i2sLoopDMADescriptor[i],
|
|
DMA_CHANNEL_XFER(true, false, intA, !intA, handle->bytesPerFrame, (uint8_t)srcInc,
|
|
(uint8_t)destInc, currentTransfer->dataSize),
|
|
srcAddr, destAddr, &handle->i2sLoopDMADescriptor[i + 1U]);
|
|
}
|
|
}
|
|
|
|
/* transferSize make sense to non link transfer only */
|
|
if (handle->state == (uint32_t)kI2S_DmaStateTx)
|
|
{
|
|
srcAddr = (uint32_t *)(uint32_t)xfer->data;
|
|
destAddr = (uint32_t *)(uint32_t)(&(base->FIFOWR));
|
|
srcInc = 1U;
|
|
destInc = 0UL;
|
|
}
|
|
else
|
|
{
|
|
srcAddr = (uint32_t *)(uint32_t)(&(base->FIFORD));
|
|
destAddr = (uint32_t *)(uint32_t)xfer->data;
|
|
srcInc = 0U;
|
|
destInc = 1UL;
|
|
}
|
|
|
|
DMA_SubmitChannelTransferParameter(handle->dmaHandle,
|
|
DMA_CHANNEL_XFER(true, false, false, true, handle->bytesPerFrame,
|
|
(uint8_t)srcInc, (uint8_t)destInc, (uint32_t)xfer->dataSize),
|
|
srcAddr, destAddr, (void *)&handle->i2sLoopDMADescriptor[1U]);
|
|
|
|
/* Submit and start initial DMA transfer */
|
|
if (handle->state == (uint32_t)kI2S_DmaStateTx)
|
|
{
|
|
I2S_TxEnableDMA(base, true);
|
|
}
|
|
else
|
|
{
|
|
I2S_RxEnableDMA(base, true);
|
|
}
|
|
DMA_EnableChannelPeriphRq(handle->dmaHandle->base, handle->dmaHandle->channel);
|
|
/* start transfer */
|
|
DMA_StartTransfer(handle->dmaHandle);
|
|
I2S_Enable(base);
|
|
|
|
handle->state = (uint32_t)kI2S_DmaStateBusyLoopTransfer;
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
/*!
|
|
* brief Send loop transfer data using DMA.
|
|
*
|
|
* This function receives data using DMA. This is a non-blocking function, which returns
|
|
* right away. When all data is received, the receive callback function is called.
|
|
*
|
|
* This function support loop transfer, such as A->B->...->A, the loop transfer chain
|
|
* will be converted into a chain of descriptor and submit to dma.
|
|
* Application must be aware of that the more counts of the loop transfer, then more DMA descriptor memory required,
|
|
* user can use function I2S_InstallDMADescriptorMemory to register the dma descriptor memory.
|
|
*
|
|
* As the DMA support maximum 1024 transfer count, so application must be aware of that this transfer function support
|
|
* maximum 1024 samples in each transfer, otherwise assert error or error status will be returned. Once the loop
|
|
* transfer start, application can use function I2S_TransferAbortDMA to stop the loop transfer.
|
|
*
|
|
* param base I2S peripheral base address.
|
|
* param handle Pointer to usart_dma_handle_t structure.
|
|
* param xfer I2S DMA transfer structure. See #i2s_transfer_t.
|
|
* param i2s_channel I2S start channel number
|
|
* retval kStatus_Success
|
|
*/
|
|
status_t I2S_TransferSendLoopDMA(I2S_Type *base,
|
|
i2s_dma_handle_t *handle,
|
|
i2s_transfer_t *xfer,
|
|
uint32_t loopTransferCount)
|
|
{
|
|
assert(handle != NULL);
|
|
assert(handle->i2sLoopDMADescriptor != NULL);
|
|
|
|
handle->state = (uint32_t)kI2S_DmaStateTx;
|
|
|
|
return I2S_TransferLoopDMA(base, handle, xfer, loopTransferCount);
|
|
}
|
|
|
|
/*!
|
|
* brief Receive loop transfer data using DMA.
|
|
*
|
|
* This function receives data using DMA. This is a non-blocking function, which returns
|
|
* right away. When all data is received, the receive callback function is called.
|
|
*
|
|
* This function support loop transfer, such as A->B->...->A, the loop transfer chain
|
|
* will be converted into a chain of descriptor and submit to dma.
|
|
* Application must be aware of that the more counts of the loop transfer, then more DMA descriptor memory required,
|
|
* user can use function I2S_InstallDMADescriptorMemory to register the dma descriptor memory.
|
|
*
|
|
* As the DMA support maximum 1024 transfer count, so application must be aware of that this transfer function support
|
|
* maximum 1024 samples in each transfer, otherwise assert error or error status will be returned. Once the loop
|
|
* transfer start, application can use function I2S_TransferAbortDMA to stop the loop transfer.
|
|
*
|
|
* param base I2S peripheral base address.
|
|
* param handle Pointer to usart_dma_handle_t structure.
|
|
* param xfer I2S DMA transfer structure. See #i2s_transfer_t.
|
|
* param i2s_channel I2S start channel number
|
|
* retval kStatus_Success
|
|
*/
|
|
status_t I2S_TransferReceiveLoopDMA(I2S_Type *base,
|
|
i2s_dma_handle_t *handle,
|
|
i2s_transfer_t *xfer,
|
|
uint32_t loopTransferCount)
|
|
{
|
|
assert(handle != NULL);
|
|
assert(handle->i2sLoopDMADescriptor != NULL);
|
|
|
|
handle->state = (uint32_t)kI2S_DmaStateRx;
|
|
|
|
return I2S_TransferLoopDMA(base, handle, xfer, loopTransferCount);
|
|
}
|
|
|
|
static status_t I2S_StartTransferDMA(I2S_Type *base, i2s_dma_handle_t *handle)
|
|
{
|
|
uint32_t instance = I2S_GetInstance(base);
|
|
i2s_dma_private_handle_t *privateHandle = &(s_DmaPrivateHandle[instance]);
|
|
volatile i2s_transfer_t *transfer = &(privateHandle->descriptorQueue[privateHandle->queueDescriptor]);
|
|
uint16_t transferBytes = I2S_GetTransferBytes(transfer);
|
|
uint32_t i = 0U;
|
|
uint32_t xferConfig = 0U;
|
|
uint32_t *srcAddr = NULL, *destAddr = NULL, srcInc = 4UL, destInc = 4UL;
|
|
|
|
if (handle->state == (uint32_t)kI2S_DmaStateTx)
|
|
{
|
|
srcAddr = (uint32_t *)(uint32_t)transfer->data;
|
|
destAddr = (uint32_t *)(uint32_t)(&(base->FIFOWR));
|
|
srcInc = 1U;
|
|
destInc = 0UL;
|
|
}
|
|
else
|
|
{
|
|
srcAddr = (uint32_t *)(uint32_t)(&(base->FIFORD));
|
|
destAddr = (uint32_t *)(uint32_t)transfer->data;
|
|
srcInc = 0U;
|
|
destInc = 1UL;
|
|
}
|
|
/* Initial descriptor is stored in another place in memory, but treat it as another descriptor for simplicity */
|
|
privateHandle->dmaDescriptorsUsed = 1U;
|
|
privateHandle->intA = false;
|
|
|
|
/* submit transfer parameter directly */
|
|
xferConfig = DMA_CHANNEL_XFER(true, false, false, true, handle->bytesPerFrame, (uint8_t)srcInc, (uint8_t)destInc,
|
|
(uint32_t)transferBytes);
|
|
|
|
DMA_SubmitChannelTransferParameter(handle->dmaHandle, xferConfig, srcAddr, destAddr,
|
|
(void *)&(s_DmaDescriptors[(instance * DMA_DESCRIPTORS) + 0U]));
|
|
|
|
privateHandle->enqueuedBytes[privateHandle->enqueuedBytesEnd] = transferBytes;
|
|
privateHandle->enqueuedBytesEnd = (privateHandle->enqueuedBytesEnd + 1U) % DMA_DESCRIPTORS;
|
|
|
|
transfer->dataSize -= transferBytes;
|
|
transfer->data = (uint8_t *)((uint32_t)transfer->data + transferBytes);
|
|
|
|
if (transfer->dataSize == 0U)
|
|
{
|
|
transfer->data = NULL;
|
|
privateHandle->queueDescriptor = (privateHandle->queueDescriptor + 1U) % I2S_NUM_BUFFERS;
|
|
}
|
|
|
|
/* Link the DMA descriptors for the case when no additional transfer is queued before the initial one finishes
|
|
* The configuration for the DMA dummy descriptor make no sense to tx or rx transfer, since it will be overwritten
|
|
* when another transfer request comes before the previous finished.
|
|
* To make sure the audio data transfer continuously, application must request another transfer by call
|
|
* I2S_RxTransferReceiveDMA or I2S_TxTransferSendDMA before previous transfer finished.
|
|
*/
|
|
for (i = 0; i < DMA_DESCRIPTORS; i++)
|
|
{
|
|
/* DMA_CHANNEL_XFER(1UL, 0UL, 0UL, 0UL, sizeof(uint32_t), 0U, 0U, 8U) = 0x10203UL */
|
|
DMA_SetupDescriptor(
|
|
&(s_DmaDescriptors[(instance * DMA_DESCRIPTORS) + i]), 0x10203UL,
|
|
((handle->state == (uint32_t)kI2S_DmaStateTx) ? &s_DummyBufferTx : (uint32_t *)(uint32_t)(&(base->FIFORD))),
|
|
((handle->state == (uint32_t)kI2S_DmaStateTx) ? (uint32_t *)(uint32_t)(&(base->FIFOWR)) : &s_DummyBufferRx),
|
|
&(s_DmaDescriptors[(instance * DMA_DESCRIPTORS) + ((i + 1U) % DMA_DESCRIPTORS)]));
|
|
}
|
|
|
|
/* Submit and start initial DMA transfer */
|
|
if (handle->state == (uint32_t)kI2S_DmaStateTx)
|
|
{
|
|
I2S_TxEnableDMA(base, true);
|
|
}
|
|
else
|
|
{
|
|
I2S_RxEnableDMA(base, true);
|
|
}
|
|
/* enable I2S peripheral request and put the channel into triggered status */
|
|
DMA_EnableChannelPeriphRq(handle->dmaHandle->base, handle->dmaHandle->channel);
|
|
DMA_StartTransfer(handle->dmaHandle);
|
|
|
|
I2S_Enable(base);
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
static void I2S_AddTransferDMA(I2S_Type *base, i2s_dma_handle_t *handle)
|
|
{
|
|
volatile i2s_transfer_t *transfer;
|
|
uint16_t transferBytes;
|
|
uint32_t instance;
|
|
i2s_dma_private_handle_t *privateHandle;
|
|
dma_descriptor_t *descriptor;
|
|
dma_descriptor_t *nextDescriptor;
|
|
uint32_t xferConfig = 0U;
|
|
bool intA = false;
|
|
uint32_t *srcAddr = NULL, *destAddr = NULL, srcInc = 4UL, destInc = 4UL;
|
|
|
|
instance = I2S_GetInstance(base);
|
|
privateHandle = &(s_DmaPrivateHandle[instance]);
|
|
|
|
while (privateHandle->dmaDescriptorsUsed < DMA_DESCRIPTORS)
|
|
{
|
|
transfer = &(privateHandle->descriptorQueue[privateHandle->queueDescriptor]);
|
|
intA = privateHandle->intA;
|
|
if (transfer->dataSize == 0U)
|
|
{
|
|
/* Nothing to be added */
|
|
return;
|
|
}
|
|
|
|
if (handle->state == (uint32_t)kI2S_DmaStateTx)
|
|
{
|
|
srcAddr = (uint32_t *)(uint32_t)transfer->data;
|
|
destAddr = (uint32_t *)(uint32_t)(&(base->FIFOWR));
|
|
srcInc = 1U;
|
|
destInc = 0UL;
|
|
}
|
|
else
|
|
{
|
|
srcAddr = (uint32_t *)(uint32_t)(&(base->FIFORD));
|
|
destAddr = (uint32_t *)(uint32_t)transfer->data;
|
|
srcInc = 0U;
|
|
destInc = 1UL;
|
|
}
|
|
|
|
/* Determine currently configured descriptor and the other which it will link to */
|
|
descriptor = &(s_DmaDescriptors[(instance * DMA_DESCRIPTORS) + privateHandle->descriptor]);
|
|
privateHandle->descriptor = (privateHandle->descriptor + 1U) % DMA_DESCRIPTORS;
|
|
nextDescriptor = &(s_DmaDescriptors[(instance * DMA_DESCRIPTORS) + privateHandle->descriptor]);
|
|
|
|
transferBytes = I2S_GetTransferBytes(transfer);
|
|
privateHandle->enqueuedBytes[privateHandle->enqueuedBytesEnd] = transferBytes;
|
|
privateHandle->enqueuedBytesEnd = (privateHandle->enqueuedBytesEnd + 1U) % DMA_DESCRIPTORS;
|
|
|
|
xferConfig = DMA_CHANNEL_XFER(true, false, !intA, intA, handle->bytesPerFrame, (uint8_t)srcInc,
|
|
(uint8_t)destInc, (uint32_t)transferBytes);
|
|
|
|
DMA_SetupDescriptor(descriptor, xferConfig, srcAddr, destAddr, nextDescriptor);
|
|
|
|
/* Advance internal state */
|
|
privateHandle->dmaDescriptorsUsed++;
|
|
privateHandle->intA = !privateHandle->intA;
|
|
|
|
transfer->dataSize -= transferBytes;
|
|
transfer->data += transferBytes;
|
|
if (transfer->dataSize == 0U)
|
|
{
|
|
transfer->data = NULL;
|
|
privateHandle->queueDescriptor = (privateHandle->queueDescriptor + 1U) % I2S_NUM_BUFFERS;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* brief Invoked from DMA interrupt handler.
|
|
*
|
|
* param handle pointer to DMA handle structure.
|
|
* param userData argument for user callback.
|
|
* param transferDone if transfer was done.
|
|
* param tcds
|
|
*/
|
|
void I2S_DMACallback(dma_handle_t *handle, void *userData, bool transferDone, uint32_t tcds)
|
|
{
|
|
i2s_dma_private_handle_t *privateHandle = (i2s_dma_private_handle_t *)userData;
|
|
i2s_dma_handle_t *i2sHandle = privateHandle->handle;
|
|
I2S_Type *base = privateHandle->base;
|
|
uint8_t queueDriverIndex = i2sHandle->queueDriver;
|
|
uint32_t enqueueBytes = privateHandle->enqueuedBytes[privateHandle->enqueuedBytesStart];
|
|
uint32_t queueDataAddr = (uint32_t)i2sHandle->i2sQueue[queueDriverIndex].data;
|
|
|
|
if ((!transferDone) || (i2sHandle->state == (uint32_t)kI2S_DmaStateIdle))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (privateHandle->dmaDescriptorsUsed > 0U)
|
|
{
|
|
/* Finished descriptor, decrease amount of data to be processed */
|
|
|
|
i2sHandle->i2sQueue[queueDriverIndex].dataSize -= enqueueBytes;
|
|
i2sHandle->i2sQueue[queueDriverIndex].data = (uint8_t *)(queueDataAddr + enqueueBytes);
|
|
privateHandle->enqueuedBytes[privateHandle->enqueuedBytesStart] = 0U;
|
|
privateHandle->enqueuedBytesStart = (privateHandle->enqueuedBytesStart + 1U) % DMA_DESCRIPTORS;
|
|
privateHandle->dmaDescriptorsUsed--;
|
|
}
|
|
|
|
if (i2sHandle->i2sQueue[queueDriverIndex].dataSize == 0U)
|
|
{
|
|
/* Entire user buffer sent or received - advance to next one */
|
|
i2sHandle->i2sQueue[queueDriverIndex].data = NULL;
|
|
i2sHandle->queueDriver = (queueDriverIndex + 1U) % I2S_NUM_BUFFERS;
|
|
/* Notify user about buffer completion */
|
|
if (i2sHandle->completionCallback != NULL)
|
|
{
|
|
(i2sHandle->completionCallback)(base, i2sHandle, kStatus_I2S_BufferComplete, i2sHandle->userData);
|
|
}
|
|
}
|
|
|
|
if (i2sHandle->state != (uint32_t)kI2S_DmaStateBusyLoopTransfer)
|
|
{
|
|
/* check next buffer queue is avaliable or not */
|
|
if (i2sHandle->i2sQueue[i2sHandle->queueDriver].dataSize == 0U)
|
|
{
|
|
if (i2sHandle->state == (uint32_t)kI2S_DmaStateTx)
|
|
{
|
|
(void)I2S_EmptyTxFifo(base);
|
|
}
|
|
/* All user buffers processed */
|
|
I2S_TransferAbortDMA(base, i2sHandle);
|
|
}
|
|
else
|
|
{
|
|
/* Enqueue another user buffer to DMA if it could not be done when in I2S_Rx/TxTransferSendDMA */
|
|
I2S_AddTransferDMA(base, i2sHandle);
|
|
}
|
|
}
|
|
}
|