/* * 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 /******************************************************************************* * 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) /*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); } } }