/* * The Clear BSD License * Copyright (c) 2016, Freescale Semiconductor, Inc. * Copyright 2016-2017 NXP * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted (subject to the limitations in the disclaimer below) provided * that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, this list * of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. * * o Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #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) { /* 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 != kI2S_DmaStateIdle) { DMA_EnableChannelInterrupts(handle->dmaHandle->base, handle->dmaHandle->channel); } } 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); assert(dmaHandle); uint32_t instance = I2S_GetInstance(base); i2s_dma_private_handle_t *privateHandle = &(s_DmaPrivateHandle[instance]); memset(handle, 0U, sizeof(*handle)); handle->state = kI2S_DmaStateIdle; handle->dmaHandle = dmaHandle; handle->completionCallback = callback; handle->userData = userData; memset(privateHandle, 0U, sizeof(*privateHandle)); privateHandle->base = base; privateHandle->handle = handle; DMA_SetCallback(dmaHandle, I2S_DMACallback, privateHandle); } 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 == kI2S_DmaStateIdle) { handle->state = 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; } void I2S_TransferAbortDMA(I2S_Type *base, i2s_dma_handle_t *handle) { assert(handle); assert(handle->dmaHandle); 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 == kI2S_DmaStateTx) { /* Wait until all transmitted data get out of FIFO */ while ((base->FIFOSTAT & I2S_FIFOSTAT_TXEMPTY_MASK) == 0U) { } /* The last piece of valid data can be still being transmitted from I2S at this moment */ /* Write additional data to FIFO */ base->FIFOWR = 0U; while ((base->FIFOSTAT & I2S_FIFOSTAT_TXEMPTY_MASK) == 0U) { } /* At this moment the additional data are out of FIFO, starting being transmitted. * This means the preceding valid data has been just transmitted and we can stop I2S. */ I2S_TxEnableDMA(base, false); } else { I2S_RxEnableDMA(base, false); } I2S_Disable(base); /* Reset state */ handle->state = kI2S_DmaStateIdle; /* Clear transfer queue */ memset((void *)&(handle->i2sQueue), 0U, sizeof(handle->i2sQueue)); handle->queueDriver = 0U; handle->queueUser = 0U; /* Clear internal state */ memset((void *)&(privateHandle->descriptorQueue), 0U, sizeof(privateHandle->descriptorQueue)); memset((void *)&(privateHandle->enqueuedBytes), 0U, sizeof(privateHandle->enqueuedBytes)); privateHandle->enqueuedBytesStart = 0U; privateHandle->enqueuedBytesEnd = 0U; privateHandle->dmaDescriptorsUsed = 0U; privateHandle->descriptor = 0U; privateHandle->queueDescriptor = 0U; privateHandle->intA = false; } 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); } 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 == kI2S_DmaStateIdle) { handle->state = 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); uint16_t transferBytes; if (transfer->dataSize >= (2 * DMA_MAX_TRANSFER_BYTES)) { transferBytes = DMA_MAX_TRANSFER_BYTES; } else if (transfer->dataSize > DMA_MAX_TRANSFER_BYTES) { transferBytes = transfer->dataSize / 2U; if ((transferBytes % 4U) != 0U) { transferBytes -= (transferBytes % 4U); } } else { transferBytes = transfer->dataSize; } return transferBytes; } static status_t I2S_StartTransferDMA(I2S_Type *base, i2s_dma_handle_t *handle) { status_t status; dma_transfer_config_t xferConfig = {0}; i2s_dma_private_handle_t *privateHandle; volatile i2s_transfer_t *transfer; uint16_t transferBytes; uint32_t instance; int i; dma_descriptor_t *descriptor; dma_descriptor_t *nextDescriptor; dma_xfercfg_t xfercfg; instance = I2S_GetInstance(base); privateHandle = &(s_DmaPrivateHandle[instance]); transfer = &(privateHandle->descriptorQueue[privateHandle->queueDescriptor]); transferBytes = I2S_GetTransferBytes(transfer); /* Prepare transfer of data via initial DMA transfer descriptor */ DMA_PrepareTransfer( &xferConfig, (void *)((handle->state == kI2S_DmaStateTx) ? (uint32_t)transfer->data : (uint32_t)(&(base->FIFORD))), (void *)((handle->state == kI2S_DmaStateTx) ? (uint32_t)(&(base->FIFOWR)) : (uint32_t)transfer->data), sizeof(uint32_t), transferBytes, (handle->state == kI2S_DmaStateTx) ? kDMA_MemoryToPeripheral : kDMA_PeripheralToMemory, (void *)&(s_DmaDescriptors[(instance * DMA_DESCRIPTORS) + 0U])); /* Initial descriptor is stored in another place in memory, but treat it as another descriptor for simplicity */ privateHandle->dmaDescriptorsUsed = 1U; privateHandle->intA = false; privateHandle->enqueuedBytes[privateHandle->enqueuedBytesEnd] = transferBytes; privateHandle->enqueuedBytesEnd = (privateHandle->enqueuedBytesEnd + 1U) % DMA_DESCRIPTORS; transfer->dataSize -= transferBytes; 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 */ for (i = 0; i < DMA_DESCRIPTORS; i++) { descriptor = &(s_DmaDescriptors[(instance * DMA_DESCRIPTORS) + i]); nextDescriptor = &(s_DmaDescriptors[(instance * DMA_DESCRIPTORS) + ((i + 1) % DMA_DESCRIPTORS)]); xfercfg.valid = true; xfercfg.reload = true; xfercfg.swtrig = false; xfercfg.clrtrig = false; xfercfg.intA = false; xfercfg.intB = false; xfercfg.byteWidth = sizeof(uint32_t); xfercfg.srcInc = 0U; xfercfg.dstInc = 0U; xfercfg.transferCount = 8U; DMA_CreateDescriptor(descriptor, &xfercfg, ((handle->state == kI2S_DmaStateTx) ? (void *)&s_DummyBufferTx : (void *)(uint32_t)(&(base->FIFORD))), ((handle->state == kI2S_DmaStateTx) ? (void *)(uint32_t)(&(base->FIFOWR)) : (void *)&s_DummyBufferRx), (void *)nextDescriptor); } /* Submit and start initial DMA transfer */ if (handle->state == kI2S_DmaStateTx) { I2S_TxEnableDMA(base, true); } else { I2S_RxEnableDMA(base, true); } status = DMA_SubmitTransfer(handle->dmaHandle, &xferConfig); if (status != kStatus_Success) { return status; } DMA_StartTransfer(handle->dmaHandle); I2S_Enable(base); return kStatus_Success; } static void I2S_AddTransferDMA(I2S_Type *base, i2s_dma_handle_t *handle) { dma_xfercfg_t xfercfg; 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 srcAddr = 0, destAddr = 0; instance = I2S_GetInstance(base); privateHandle = &(s_DmaPrivateHandle[instance]); while (privateHandle->dmaDescriptorsUsed < DMA_DESCRIPTORS) { transfer = &(privateHandle->descriptorQueue[privateHandle->queueDescriptor]); if (transfer->dataSize == 0U) { /* Nothing to be added */ return; } /* 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; /* Configure descriptor */ xfercfg.valid = true; xfercfg.reload = true; xfercfg.swtrig = false; xfercfg.clrtrig = false; xfercfg.intA = privateHandle->intA; xfercfg.intB = !privateHandle->intA; xfercfg.byteWidth = sizeof(uint32_t); xfercfg.srcInc = (handle->state == kI2S_DmaStateTx) ? 1U : 0U; xfercfg.dstInc = (handle->state == kI2S_DmaStateTx) ? 0U : 1U; xfercfg.transferCount = transferBytes / sizeof(uint32_t); srcAddr = ((handle->state == kI2S_DmaStateTx) ? (uint32_t)transfer->data : (uint32_t)&(base->FIFORD)); destAddr = ((handle->state == kI2S_DmaStateTx) ? (uint32_t)&(base->FIFOWR) : (uint32_t)transfer->data); DMA_CreateDescriptor(descriptor, &xfercfg, (void *)srcAddr, (void *)destAddr, (void *)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; } } } 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; if ((!transferDone) || (i2sHandle->state == kI2S_DmaStateIdle)) { return; } if (privateHandle->dmaDescriptorsUsed > 0U) { /* Finished descriptor, decrease amount of data to be processed */ i2sHandle->i2sQueue[i2sHandle->queueDriver].dataSize -= privateHandle->enqueuedBytes[privateHandle->enqueuedBytesStart]; i2sHandle->i2sQueue[i2sHandle->queueDriver].data += privateHandle->enqueuedBytes[privateHandle->enqueuedBytesStart]; privateHandle->enqueuedBytes[privateHandle->enqueuedBytesStart] = 0U; privateHandle->enqueuedBytesStart = (privateHandle->enqueuedBytesStart + 1U) % DMA_DESCRIPTORS; privateHandle->dmaDescriptorsUsed--; if (i2sHandle->i2sQueue[i2sHandle->queueDriver].dataSize == 0U) { /* Entire user buffer sent or received - advance to next one */ i2sHandle->i2sQueue[i2sHandle->queueDriver].data = NULL; i2sHandle->queueDriver = (i2sHandle->queueDriver + 1U) % I2S_NUM_BUFFERS; /* Notify user about buffer completion */ if (i2sHandle->completionCallback) { (i2sHandle->completionCallback)(base, i2sHandle, kStatus_I2S_BufferComplete, i2sHandle->userData); } } } if (i2sHandle->i2sQueue[i2sHandle->queueDriver].dataSize == 0U) { /* All user buffers processed */ I2S_TransferAbortDMA(base, i2sHandle); /* Notify user about completion of the final buffer */ if (i2sHandle->completionCallback) { (i2sHandle->completionCallback)(base, i2sHandle, kStatus_I2S_Done, i2sHandle->userData); } } else { /* Enqueue another user buffer to DMA if it could not be done when in I2S_Rx/TxTransferSendDMA */ I2S_AddTransferDMA(base, i2sHandle); } }