Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:01

0001 /*
0002  * Copyright (c) 2015, Freescale Semiconductor, Inc.
0003  * Copyright 2016-2021 NXP
0004  * All rights reserved.
0005  *
0006  * SPDX-License-Identifier: BSD-3-Clause
0007  */
0008 
0009 #include "fsl_sai_edma.h"
0010 
0011 /* Component ID definition, used by tools. */
0012 #ifndef FSL_COMPONENT_ID
0013 #define FSL_COMPONENT_ID "platform.drivers.sai_edma"
0014 #endif
0015 
0016 /*******************************************************************************
0017  * Definitions
0018  ******************************************************************************/
0019 /* Used for 32byte aligned */
0020 #define STCD_ADDR(address) (edma_tcd_t *)(((uint32_t)(address) + 32UL) & ~0x1FU)
0021 
0022 static I2S_Type *const s_saiBases[] = I2S_BASE_PTRS;
0023 /* Only support 2 and 4 channel */
0024 #define SAI_CHANNEL_MAP_MODULO(channel) (channel == 2U ? kEDMA_Modulo8bytes : kEDMA_Modulo16bytes)
0025 
0026 /*<! Structure definition for uart_edma_private_handle_t. The structure is private. */
0027 typedef struct sai_edma_private_handle
0028 {
0029     I2S_Type *base;
0030     sai_edma_handle_t *handle;
0031 } sai_edma_private_handle_t;
0032 
0033 /*! @brief sai_edma_transfer_state, sai edma transfer state.*/
0034 enum
0035 {
0036     kSAI_Busy = 0x0U,      /*!< SAI is busy */
0037     kSAI_BusyLoopTransfer, /*!< SAI is busy for Loop transfer */
0038     kSAI_Idle,             /*!< Transfer is done. */
0039 };
0040 
0041 /*<! Private handle only used for internally. */
0042 static sai_edma_private_handle_t s_edmaPrivateHandle[ARRAY_SIZE(s_saiBases)][2];
0043 
0044 /*******************************************************************************
0045  * Prototypes
0046  ******************************************************************************/
0047 /*!
0048  * @brief Get the instance number for SAI.
0049  *
0050  * @param base SAI base pointer.
0051  */
0052 static uint32_t SAI_GetInstance(I2S_Type *base);
0053 
0054 /*!
0055  * @brief SAI EDMA callback for send.
0056  *
0057  * @param handle pointer to sai_edma_handle_t structure which stores the transfer state.
0058  * @param userData Parameter for user callback.
0059  * @param done If the DMA transfer finished.
0060  * @param tcds The TCD index.
0061  */
0062 static void SAI_TxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds);
0063 
0064 /*!
0065  * @brief SAI EDMA callback for receive.
0066  *
0067  * @param handle pointer to sai_edma_handle_t structure which stores the transfer state.
0068  * @param userData Parameter for user callback.
0069  * @param done If the DMA transfer finished.
0070  * @param tcds The TCD index.
0071  */
0072 static void SAI_RxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds);
0073 
0074 /*******************************************************************************
0075  * Code
0076  ******************************************************************************/
0077 static uint32_t SAI_GetInstance(I2S_Type *base)
0078 {
0079     uint32_t instance;
0080 
0081     /* Find the instance index from base address mappings. */
0082     for (instance = 0; instance < ARRAY_SIZE(s_saiBases); instance++)
0083     {
0084         if (s_saiBases[instance] == base)
0085         {
0086             break;
0087         }
0088     }
0089 
0090     assert(instance < ARRAY_SIZE(s_saiBases));
0091 
0092     return instance;
0093 }
0094 
0095 static void SAI_TxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds)
0096 {
0097     sai_edma_private_handle_t *privHandle = (sai_edma_private_handle_t *)userData;
0098     sai_edma_handle_t *saiHandle          = privHandle->handle;
0099     status_t status                       = kStatus_SAI_TxBusy;
0100 
0101     if (saiHandle->state != (uint32_t)kSAI_BusyLoopTransfer)
0102     {
0103         if (saiHandle->queueDriver + tcds > (uint32_t)SAI_XFER_QUEUE_SIZE)
0104         {
0105             (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0,
0106                          sizeof(sai_transfer_t) * ((uint32_t)SAI_XFER_QUEUE_SIZE - saiHandle->queueDriver));
0107             (void)memset(&saiHandle->saiQueue[0U], 0,
0108                          sizeof(sai_transfer_t) * (saiHandle->queueDriver + tcds - (uint32_t)SAI_XFER_QUEUE_SIZE));
0109         }
0110         else
0111         {
0112             (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0, sizeof(sai_transfer_t) * tcds);
0113         }
0114         saiHandle->queueDriver = (uint8_t)((saiHandle->queueDriver + tcds) % (uint32_t)SAI_XFER_QUEUE_SIZE);
0115 
0116         /* If all data finished, just stop the transfer */
0117         if (saiHandle->saiQueue[saiHandle->queueDriver].data == NULL)
0118         {
0119             /* Disable DMA enable bit */
0120             SAI_TxEnableDMA(privHandle->base, kSAI_FIFORequestDMAEnable, false);
0121             EDMA_AbortTransfer(handle);
0122             status = kStatus_SAI_TxIdle;
0123         }
0124     }
0125 
0126     /* If finished a block, call the callback function */
0127     if (saiHandle->callback != NULL)
0128     {
0129         (saiHandle->callback)(privHandle->base, saiHandle, status, saiHandle->userData);
0130     }
0131 }
0132 
0133 static void SAI_RxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds)
0134 {
0135     sai_edma_private_handle_t *privHandle = (sai_edma_private_handle_t *)userData;
0136     sai_edma_handle_t *saiHandle          = privHandle->handle;
0137     status_t status                       = kStatus_SAI_RxBusy;
0138 
0139     if (saiHandle->state != (uint32_t)kSAI_BusyLoopTransfer)
0140     {
0141         if (saiHandle->queueDriver + tcds > (uint32_t)SAI_XFER_QUEUE_SIZE)
0142         {
0143             (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0,
0144                          sizeof(sai_transfer_t) * ((uint32_t)SAI_XFER_QUEUE_SIZE - saiHandle->queueDriver));
0145             (void)memset(&saiHandle->saiQueue[0U], 0,
0146                          sizeof(sai_transfer_t) * (saiHandle->queueDriver + tcds - (uint32_t)SAI_XFER_QUEUE_SIZE));
0147         }
0148         else
0149         {
0150             (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0, sizeof(sai_transfer_t) * tcds);
0151         }
0152         saiHandle->queueDriver = (uint8_t)((saiHandle->queueDriver + tcds) % (uint32_t)SAI_XFER_QUEUE_SIZE);
0153 
0154         /* If all data finished, just stop the transfer */
0155         if (saiHandle->saiQueue[saiHandle->queueDriver].data == NULL)
0156         {
0157             /* Disable DMA enable bit */
0158             SAI_RxEnableDMA(privHandle->base, kSAI_FIFORequestDMAEnable, false);
0159             EDMA_AbortTransfer(handle);
0160             status = kStatus_SAI_RxIdle;
0161         }
0162     }
0163 
0164     /* If finished a block, call the callback function */
0165     if (saiHandle->callback != NULL)
0166     {
0167         (saiHandle->callback)(privHandle->base, saiHandle, status, saiHandle->userData);
0168     }
0169 }
0170 
0171 /*!
0172  * brief Initializes the SAI eDMA handle.
0173  *
0174  * This function initializes the SAI master DMA handle, which can be used for other SAI master transactional APIs.
0175  * Usually, for a specified SAI instance, call this API once to get the initialized handle.
0176  *
0177  * param base SAI base pointer.
0178  * param handle SAI eDMA handle pointer.
0179  * param base SAI peripheral base address.
0180  * param callback Pointer to user callback function.
0181  * param userData User parameter passed to the callback function.
0182  * param dmaHandle eDMA handle pointer, this handle shall be static allocated by users.
0183  */
0184 void SAI_TransferTxCreateHandleEDMA(
0185     I2S_Type *base, sai_edma_handle_t *handle, sai_edma_callback_t callback, void *userData, edma_handle_t *txDmaHandle)
0186 {
0187     assert((handle != NULL) && (txDmaHandle != NULL));
0188 
0189     uint32_t instance = SAI_GetInstance(base);
0190 
0191     /* Zero the handle */
0192     (void)memset(handle, 0, sizeof(*handle));
0193 
0194     /* Set sai base to handle */
0195     handle->dmaHandle = txDmaHandle;
0196     handle->callback  = callback;
0197     handle->userData  = userData;
0198 
0199     /* Set SAI state to idle */
0200     handle->state = (uint32_t)kSAI_Idle;
0201 
0202     s_edmaPrivateHandle[instance][0].base   = base;
0203     s_edmaPrivateHandle[instance][0].handle = handle;
0204 
0205     /* Need to use scatter gather */
0206     EDMA_InstallTCDMemory(txDmaHandle, (edma_tcd_t *)(STCD_ADDR(handle->tcd)), SAI_XFER_QUEUE_SIZE);
0207 
0208     /* Install callback for Tx dma channel */
0209     EDMA_SetCallback(txDmaHandle, SAI_TxEDMACallback, &s_edmaPrivateHandle[instance][0]);
0210 }
0211 
0212 /*!
0213  * brief Initializes the SAI Rx eDMA handle.
0214  *
0215  * This function initializes the SAI slave DMA handle, which can be used for other SAI master transactional APIs.
0216  * Usually, for a specified SAI instance, call this API once to get the initialized handle.
0217  *
0218  * param base SAI base pointer.
0219  * param handle SAI eDMA handle pointer.
0220  * param base SAI peripheral base address.
0221  * param callback Pointer to user callback function.
0222  * param userData User parameter passed to the callback function.
0223  * param dmaHandle eDMA handle pointer, this handle shall be static allocated by users.
0224  */
0225 void SAI_TransferRxCreateHandleEDMA(
0226     I2S_Type *base, sai_edma_handle_t *handle, sai_edma_callback_t callback, void *userData, edma_handle_t *rxDmaHandle)
0227 {
0228     assert((handle != NULL) && (rxDmaHandle != NULL));
0229 
0230     uint32_t instance = SAI_GetInstance(base);
0231 
0232     /* Zero the handle */
0233     (void)memset(handle, 0, sizeof(*handle));
0234 
0235     /* Set sai base to handle */
0236     handle->dmaHandle = rxDmaHandle;
0237     handle->callback  = callback;
0238     handle->userData  = userData;
0239 
0240     /* Set SAI state to idle */
0241     handle->state = (uint32_t)kSAI_Idle;
0242 
0243     s_edmaPrivateHandle[instance][1].base   = base;
0244     s_edmaPrivateHandle[instance][1].handle = handle;
0245 
0246     /* Need to use scatter gather */
0247     EDMA_InstallTCDMemory(rxDmaHandle, STCD_ADDR(handle->tcd), SAI_XFER_QUEUE_SIZE);
0248 
0249     /* Install callback for Tx dma channel */
0250     EDMA_SetCallback(rxDmaHandle, SAI_RxEDMACallback, &s_edmaPrivateHandle[instance][1]);
0251 }
0252 
0253 /*!
0254  * brief Configures the SAI Tx audio format.
0255  *
0256  * deprecated Do not use this function.  It has been superceded by ref SAI_TransferTxSetConfigEDMA
0257  *
0258  * The audio format can be changed at run-time. This function configures the sample rate and audio data
0259  * format to be transferred. This function also sets the eDMA parameter according to formatting requirements.
0260  *
0261  * param base SAI base pointer.
0262  * param handle SAI eDMA handle pointer.
0263  * param format Pointer to SAI audio data format structure.
0264  * param mclkSourceClockHz SAI master clock source frequency in Hz.
0265  * param bclkSourceClockHz SAI bit clock source frequency in Hz. If bit clock source is master
0266  * clock, this value should equals to masterClockHz in format.
0267  * retval kStatus_Success Audio format set successfully.
0268  * retval kStatus_InvalidArgument The input argument is invalid.
0269  */
0270 void SAI_TransferTxSetFormatEDMA(I2S_Type *base,
0271                                  sai_edma_handle_t *handle,
0272                                  sai_transfer_format_t *format,
0273                                  uint32_t mclkSourceClockHz,
0274                                  uint32_t bclkSourceClockHz)
0275 {
0276     assert((handle != NULL) && (format != NULL));
0277 
0278     /* Configure the audio format to SAI registers */
0279     SAI_TxSetFormat(base, format, mclkSourceClockHz, bclkSourceClockHz);
0280 
0281     /* Get the transfer size from format, this should be used in EDMA configuration */
0282     if (format->bitWidth == 24U)
0283     {
0284         handle->bytesPerFrame = 4U;
0285     }
0286     else
0287     {
0288         handle->bytesPerFrame = (uint8_t)(format->bitWidth / 8U);
0289     }
0290 
0291     /* Update the data channel SAI used */
0292     handle->channel = format->channel;
0293 
0294     /* Clear the channel enable bits until do a send/receive */
0295     base->TCR3 &= ~I2S_TCR3_TCE_MASK;
0296 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
0297     handle->count = (uint8_t)((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base) - format->watermark);
0298 #else
0299     handle->count = 1U;
0300 #endif /* FSL_FEATURE_SAI_HAS_FIFO */
0301 }
0302 
0303 /*!
0304  * brief Configures the SAI Tx.
0305  *
0306  * note SAI eDMA supports data transfer in a multiple SAI channels if the FIFO Combine feature is supported.
0307  * To activate the multi-channel transfer enable SAI channels by filling the channelMask
0308  * of sai_transceiver_t with the corresponding values of _sai_channel_mask enum, enable the FIFO Combine
0309  * mode by assigning kSAI_FifoCombineModeEnabledOnWrite to the fifoCombine member of sai_fifo_combine_t
0310  * which is a member of sai_transceiver_t.
0311  * This is an example of multi-channel data transfer configuration step.
0312  *  code
0313  *   sai_transceiver_t config;
0314  *   SAI_GetClassicI2SConfig(&config, kSAI_WordWidth16bits, kSAI_Stereo, kSAI_Channel0Mask|kSAI_Channel1Mask);
0315  *   config.fifo.fifoCombine = kSAI_FifoCombineModeEnabledOnWrite;
0316  *   SAI_TransferTxSetConfigEDMA(I2S0, &edmaHandle, &config);
0317  *  endcode
0318  * param base SAI base pointer.
0319  * param handle SAI eDMA handle pointer.
0320  * param saiConfig sai configurations.
0321  */
0322 void SAI_TransferTxSetConfigEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transceiver_t *saiConfig)
0323 {
0324     assert((handle != NULL) && (saiConfig != NULL));
0325 
0326     /* Configure the audio format to SAI registers */
0327     SAI_TxSetConfig(base, saiConfig);
0328 
0329 #if defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE
0330     /* Allow multi-channel transfer only if FIFO Combine mode is enabled */
0331     assert(
0332         (saiConfig->channelNums <= 1U) ||
0333         ((saiConfig->channelNums > 1U) && ((saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnWrite) ||
0334                                            (saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnReadWrite))));
0335 #endif
0336 
0337     /* Get the transfer size from format, this should be used in EDMA configuration */
0338     if (saiConfig->serialData.dataWordLength == 24U)
0339     {
0340         handle->bytesPerFrame = 4U;
0341     }
0342     else
0343     {
0344         handle->bytesPerFrame = saiConfig->serialData.dataWordLength / 8U;
0345     }
0346     /* Update the data channel SAI used */
0347     handle->channel     = saiConfig->startChannel;
0348     handle->channelMask = saiConfig->channelMask;
0349     handle->channelNums = saiConfig->channelNums;
0350 
0351     /* Clear the channel enable bits until do a send/receive */
0352     base->TCR3 &= ~I2S_TCR3_TCE_MASK;
0353 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
0354     handle->count = (uint8_t)((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base) - saiConfig->fifo.fifoWatermark);
0355 #else
0356     handle->count = 1U;
0357 #endif /* FSL_FEATURE_SAI_HAS_FIFO */
0358 }
0359 
0360 /*!
0361  * brief Configures the SAI Rx audio format.
0362  *
0363  * deprecated Do not use this function.  It has been superceded by ref SAI_TransferRxSetConfigEDMA
0364  *
0365  * The audio format can be changed at run-time. This function configures the sample rate and audio data
0366  * format to be transferred. This function also sets the eDMA parameter according to formatting requirements.
0367  *
0368  * param base SAI base pointer.
0369  * param handle SAI eDMA handle pointer.
0370  * param format Pointer to SAI audio data format structure.
0371  * param mclkSourceClockHz SAI master clock source frequency in Hz.
0372  * param bclkSourceClockHz SAI bit clock source frequency in Hz. If a bit clock source is the master
0373  * clock, this value should equal to masterClockHz in format.
0374  * retval kStatus_Success Audio format set successfully.
0375  * retval kStatus_InvalidArgument The input argument is invalid.
0376  */
0377 void SAI_TransferRxSetFormatEDMA(I2S_Type *base,
0378                                  sai_edma_handle_t *handle,
0379                                  sai_transfer_format_t *format,
0380                                  uint32_t mclkSourceClockHz,
0381                                  uint32_t bclkSourceClockHz)
0382 {
0383     assert((handle != NULL) && (format != NULL));
0384 
0385     /* Configure the audio format to SAI registers */
0386     SAI_RxSetFormat(base, format, mclkSourceClockHz, bclkSourceClockHz);
0387 
0388     /* Get the transfer size from format, this should be used in EDMA configuration */
0389     if (format->bitWidth == 24U)
0390     {
0391         handle->bytesPerFrame = 4U;
0392     }
0393     else
0394     {
0395         handle->bytesPerFrame = (uint8_t)(format->bitWidth / 8U);
0396     }
0397 
0398     /* Update the data channel SAI used */
0399     handle->channel = format->channel;
0400 
0401     /* Clear the channel enable bits until do a send/receive */
0402     base->RCR3 &= ~I2S_RCR3_RCE_MASK;
0403 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
0404     handle->count = format->watermark;
0405 #else
0406     handle->count = 1U;
0407 #endif /* FSL_FEATURE_SAI_HAS_FIFO */
0408 }
0409 
0410 /*!
0411  * brief Configures the SAI Rx.
0412  *
0413  * note SAI eDMA supports data transfer in a multiple SAI channels if the FIFO Combine feature is supported.
0414  * To activate the multi-channel transfer enable SAI channels by filling the channelMask
0415  * of sai_transceiver_t with the corresponding values of _sai_channel_mask enum, enable the FIFO Combine
0416  * mode by assigning kSAI_FifoCombineModeEnabledOnRead to the fifoCombine member of sai_fifo_combine_t
0417  * which is a member of sai_transceiver_t.
0418  * This is an example of multi-channel data transfer configuration step.
0419  *  code
0420  *   sai_transceiver_t config;
0421  *   SAI_GetClassicI2SConfig(&config, kSAI_WordWidth16bits, kSAI_Stereo, kSAI_Channel0Mask|kSAI_Channel1Mask);
0422  *   config.fifo.fifoCombine = kSAI_FifoCombineModeEnabledOnRead;
0423  *   SAI_TransferRxSetConfigEDMA(I2S0, &edmaHandle, &config);
0424  *  endcode
0425  * param base SAI base pointer.
0426  * param handle SAI eDMA handle pointer.
0427  * param saiConfig sai configurations.
0428  */
0429 void SAI_TransferRxSetConfigEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transceiver_t *saiConfig)
0430 {
0431     assert((handle != NULL) && (saiConfig != NULL));
0432 
0433     /* Configure the audio format to SAI registers */
0434     SAI_RxSetConfig(base, saiConfig);
0435 
0436 #if defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE
0437     /* Allow multi-channel transfer only if FIFO Combine mode is enabled */
0438     assert(
0439         (saiConfig->channelNums <= 1U) ||
0440         ((saiConfig->channelNums > 1U) && ((saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnRead) ||
0441                                            (saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnReadWrite))));
0442 #endif
0443 
0444     /* Get the transfer size from format, this should be used in EDMA configuration */
0445     if (saiConfig->serialData.dataWordLength == 24U)
0446     {
0447         handle->bytesPerFrame = 4U;
0448     }
0449     else
0450     {
0451         handle->bytesPerFrame = saiConfig->serialData.dataWordLength / 8U;
0452     }
0453 
0454     /* Update the data channel SAI used */
0455     handle->channel     = saiConfig->startChannel;
0456     handle->channelMask = saiConfig->channelMask;
0457     handle->channelNums = saiConfig->channelNums;
0458     /* Clear the channel enable bits until do a send/receive */
0459     base->RCR3 &= ~I2S_RCR3_RCE_MASK;
0460 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
0461     handle->count = saiConfig->fifo.fifoWatermark;
0462 #else
0463     handle->count = 1U;
0464 #endif /* FSL_FEATURE_SAI_HAS_FIFO */
0465 }
0466 
0467 /*!
0468  * brief Performs a non-blocking SAI transfer using DMA.
0469  *
0470  * note This interface returns immediately after the transfer initiates. Call
0471  * SAI_GetTransferStatus to poll the transfer status and check whether the SAI transfer is finished.
0472  *
0473  * This function support multi channel transfer,
0474  * 1. for the sai IP support fifo combine mode, application should enable the fifo combine mode, no limitation
0475  *    on channel numbers
0476  * 2. for the sai IP not support fifo combine mode, sai edma provide another solution which using
0477  *    EDMA modulo feature, but support 2 or 4 channels only.
0478  *
0479  * param base SAI base pointer.
0480  * param handle SAI eDMA handle pointer.
0481  * param xfer Pointer to the DMA transfer structure.
0482  * retval kStatus_Success Start a SAI eDMA send successfully.
0483  * retval kStatus_InvalidArgument The input argument is invalid.
0484  * retval kStatus_TxBusy SAI is busy sending data.
0485  */
0486 status_t SAI_TransferSendEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transfer_t *xfer)
0487 {
0488     assert((handle != NULL) && (xfer != NULL));
0489 
0490     edma_transfer_config_t config = {0};
0491     uint32_t destAddr             = SAI_TxGetDataRegisterAddress(base, handle->channel);
0492     uint32_t destOffset           = 0U;
0493 
0494     /* Check if input parameter invalid */
0495     if ((xfer->data == NULL) || (xfer->dataSize == 0U))
0496     {
0497         return kStatus_InvalidArgument;
0498     }
0499 
0500     if (handle->saiQueue[handle->queueUser].data != NULL)
0501     {
0502         return kStatus_SAI_QueueFull;
0503     }
0504 
0505     /* Change the state of handle */
0506     handle->state = (uint32_t)kSAI_Busy;
0507 
0508     /* Update the queue state */
0509     handle->transferSize[handle->queueUser]      = xfer->dataSize;
0510     handle->saiQueue[handle->queueUser].data     = xfer->data;
0511     handle->saiQueue[handle->queueUser].dataSize = xfer->dataSize;
0512     handle->queueUser                            = (handle->queueUser + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE;
0513 
0514 #if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE)
0515     if (handle->channelNums > 1U)
0516     {
0517         destOffset = sizeof(uint32_t);
0518     }
0519 #endif
0520 
0521     /* Prepare edma configure */
0522     EDMA_PrepareTransferConfig(&config, xfer->data, (uint32_t)handle->bytesPerFrame, (int16_t)handle->bytesPerFrame,
0523                                (uint32_t *)destAddr, (uint32_t)handle->bytesPerFrame, (int16_t)destOffset,
0524                                (uint32_t)handle->count * handle->bytesPerFrame, xfer->dataSize);
0525 
0526     /* Store the initially configured eDMA minor byte transfer count into the SAI handle */
0527     handle->nbytes = handle->count * handle->bytesPerFrame;
0528 
0529     if (EDMA_SubmitTransfer(handle->dmaHandle, &config) != kStatus_Success)
0530     {
0531         return kStatus_SAI_QueueFull;
0532     }
0533 
0534 #if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE)
0535     if (handle->channelNums > 1U)
0536     {
0537         if ((handle->channelNums % 2U) != 0U)
0538         {
0539             return kStatus_InvalidArgument;
0540         }
0541 
0542         EDMA_SetModulo(handle->dmaHandle->base, handle->dmaHandle->channel, kEDMA_ModuloDisable,
0543                        SAI_CHANNEL_MAP_MODULO(handle->channelNums));
0544     }
0545 #endif
0546     /* Start DMA transfer */
0547     EDMA_StartTransfer(handle->dmaHandle);
0548 
0549     /* Enable DMA enable bit */
0550     SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
0551 
0552     /* Enable SAI Tx clock */
0553     SAI_TxEnable(base, true);
0554 
0555     /* Enable the channel FIFO */
0556     base->TCR3 |= I2S_TCR3_TCE(handle->channelMask);
0557 
0558     return kStatus_Success;
0559 }
0560 
0561 /*!
0562  * brief Performs a non-blocking SAI receive using eDMA.
0563  *
0564  * note This interface returns immediately after the transfer initiates. Call
0565  * the SAI_GetReceiveRemainingBytes to poll the transfer status and check whether the SAI transfer is finished.
0566  *
0567  * This function support multi channel transfer,
0568  * 1. for the sai IP support fifo combine mode, application should enable the fifo combine mode, no limitation
0569  *    on channel numbers
0570  * 2. for the sai IP not support fifo combine mode, sai edma provide another solution which using
0571  *    EDMA modulo feature, but support 2 or 4 channels only.
0572  *
0573  * param base SAI base pointer
0574  * param handle SAI eDMA handle pointer.
0575  * param xfer Pointer to DMA transfer structure.
0576  * retval kStatus_Success Start a SAI eDMA receive successfully.
0577  * retval kStatus_InvalidArgument The input argument is invalid.
0578  * retval kStatus_RxBusy SAI is busy receiving data.
0579  */
0580 status_t SAI_TransferReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transfer_t *xfer)
0581 {
0582     assert((handle != NULL) && (xfer != NULL));
0583 
0584     edma_transfer_config_t config = {0};
0585     uint32_t srcAddr              = SAI_RxGetDataRegisterAddress(base, handle->channel);
0586     uint32_t srcOffset            = 0U;
0587 
0588     /* Check if input parameter invalid */
0589     if ((xfer->data == NULL) || (xfer->dataSize == 0U))
0590     {
0591         return kStatus_InvalidArgument;
0592     }
0593 
0594     if (handle->saiQueue[handle->queueUser].data != NULL)
0595     {
0596         return kStatus_SAI_QueueFull;
0597     }
0598 
0599     /* Change the state of handle */
0600     handle->state = (uint32_t)kSAI_Busy;
0601 
0602     /* Update queue state  */
0603     handle->transferSize[handle->queueUser]      = xfer->dataSize;
0604     handle->saiQueue[handle->queueUser].data     = xfer->data;
0605     handle->saiQueue[handle->queueUser].dataSize = xfer->dataSize;
0606     handle->queueUser                            = (handle->queueUser + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE;
0607 
0608 #if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE)
0609     if (handle->channelNums > 1U)
0610     {
0611         srcOffset = sizeof(uint32_t);
0612     }
0613 #endif
0614 
0615     /* Prepare edma configure */
0616     EDMA_PrepareTransferConfig(&config, (uint32_t *)srcAddr, (uint32_t)handle->bytesPerFrame, (int16_t)srcOffset,
0617                                xfer->data, (uint32_t)handle->bytesPerFrame, (int16_t)handle->bytesPerFrame,
0618                                (uint32_t)handle->count * handle->bytesPerFrame, xfer->dataSize);
0619     /* Store the initially configured eDMA minor byte transfer count into the SAI handle */
0620     handle->nbytes = handle->count * handle->bytesPerFrame;
0621 
0622     if (EDMA_SubmitTransfer(handle->dmaHandle, &config) != kStatus_Success)
0623     {
0624         return kStatus_SAI_QueueFull;
0625     }
0626 
0627 #if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE)
0628     if (handle->channelNums > 1U)
0629     {
0630         if ((handle->channelNums % 2U) != 0U)
0631         {
0632             return kStatus_InvalidArgument;
0633         }
0634 
0635         EDMA_SetModulo(handle->dmaHandle->base, handle->dmaHandle->channel, SAI_CHANNEL_MAP_MODULO(handle->channelNums),
0636                        kEDMA_ModuloDisable);
0637     }
0638 #endif
0639     /* Start DMA transfer */
0640     EDMA_StartTransfer(handle->dmaHandle);
0641 
0642     /* Enable DMA enable bit */
0643     SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
0644 
0645     /* Enable the channel FIFO */
0646     base->RCR3 |= I2S_RCR3_RCE(handle->channelMask);
0647 
0648     /* Enable SAI Rx clock */
0649     SAI_RxEnable(base, true);
0650 
0651     return kStatus_Success;
0652 }
0653 
0654 /*!
0655  * brief Performs a non-blocking SAI loop transfer using eDMA.
0656  *
0657  * note This function support loop transfer only,such as A->B->...->A, application must be aware of
0658  * that the more counts of the loop transfer, then more tcd memory required, as the function use the tcd pool in
0659  * sai_edma_handle_t, so application could redefine the SAI_XFER_QUEUE_SIZE to determine the proper TCD pool size.
0660  * This function support one sai channel only.
0661  *
0662  * Once the loop transfer start, application can use function SAI_TransferAbortSendEDMA to stop the loop transfer.
0663  *
0664  * param base SAI base pointer.
0665  * param handle SAI eDMA handle pointer.
0666  * param xfer Pointer to the DMA transfer structure, should be a array with elements counts >=1(loopTransferCount).
0667  * param loopTransferCount the counts of xfer array.
0668  * retval kStatus_Success Start a SAI eDMA send successfully.
0669  * retval kStatus_InvalidArgument The input argument is invalid.
0670  */
0671 status_t SAI_TransferSendLoopEDMA(I2S_Type *base,
0672                                   sai_edma_handle_t *handle,
0673                                   sai_transfer_t *xfer,
0674                                   uint32_t loopTransferCount)
0675 {
0676     assert((handle != NULL) && (xfer != NULL));
0677 
0678     edma_transfer_config_t config = {0};
0679     uint32_t destAddr             = SAI_TxGetDataRegisterAddress(base, handle->channel);
0680     sai_transfer_t *transfer      = xfer;
0681     edma_tcd_t *currentTCD        = STCD_ADDR(handle->tcd);
0682     uint32_t tcdIndex             = 0U;
0683 
0684     /* Change the state of handle */
0685     handle->state = (uint32_t)kSAI_Busy;
0686 
0687     for (uint32_t i = 0U; i < loopTransferCount; i++)
0688     {
0689         transfer = &xfer[i];
0690 
0691         if ((transfer->data == NULL) || (transfer->dataSize == 0U) || (tcdIndex >= (uint32_t)SAI_XFER_QUEUE_SIZE))
0692         {
0693             return kStatus_InvalidArgument;
0694         }
0695 
0696         /* Update the queue state */
0697         handle->transferSize[tcdIndex]      = transfer->dataSize;
0698         handle->saiQueue[tcdIndex].data     = transfer->data;
0699         handle->saiQueue[tcdIndex].dataSize = transfer->dataSize;
0700 
0701         /* Prepare edma configure */
0702         EDMA_PrepareTransfer(&config, transfer->data, handle->bytesPerFrame, (uint32_t *)destAddr,
0703                              handle->bytesPerFrame, (uint32_t)handle->count * handle->bytesPerFrame, transfer->dataSize,
0704                              kEDMA_MemoryToPeripheral);
0705 
0706         if (i == (loopTransferCount - 1U))
0707         {
0708             EDMA_TcdSetTransferConfig(&currentTCD[tcdIndex], &config, &currentTCD[0U]);
0709             EDMA_TcdEnableInterrupts(&currentTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable);
0710             handle->state = (uint32_t)kSAI_BusyLoopTransfer;
0711             break;
0712         }
0713         else
0714         {
0715             EDMA_TcdSetTransferConfig(&currentTCD[tcdIndex], &config, &currentTCD[tcdIndex + 1U]);
0716             EDMA_TcdEnableInterrupts(&currentTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable);
0717         }
0718 
0719         tcdIndex = tcdIndex + 1U;
0720     }
0721 
0722     EDMA_InstallTCD(handle->dmaHandle->base, handle->dmaHandle->channel, &currentTCD[0]);
0723     /* Start DMA transfer */
0724     EDMA_StartTransfer(handle->dmaHandle);
0725 
0726     /* Enable DMA enable bit */
0727     SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
0728 
0729     /* Enable SAI Tx clock */
0730     SAI_TxEnable(base, true);
0731 
0732     /* Enable the channel FIFO */
0733     base->TCR3 |= I2S_TCR3_TCE(1UL << handle->channel);
0734 
0735     return kStatus_Success;
0736 }
0737 
0738 /*!
0739  * brief Performs a non-blocking SAI loop transfer using eDMA.
0740  *
0741  * note This function support loop transfer only,such as A->B->...->A, application must be aware of
0742  * that the more counts of the loop transfer, then more tcd memory required, as the function use the tcd pool in
0743  * sai_edma_handle_t, so application could redefine the SAI_XFER_QUEUE_SIZE to determine the proper TCD pool size.
0744  * This function support one sai channel only.
0745  *
0746  * Once the loop transfer start, application can use function SAI_TransferAbortReceiveEDMA to stop the loop transfer.
0747  *
0748  * param base SAI base pointer.
0749  * param handle SAI eDMA handle pointer.
0750  * param xfer Pointer to the DMA transfer structure, should be a array with elements counts >=1(loopTransferCount).
0751  * param loopTransferCount the counts of xfer array.
0752  * retval kStatus_Success Start a SAI eDMA receive successfully.
0753  * retval kStatus_InvalidArgument The input argument is invalid.
0754  */
0755 status_t SAI_TransferReceiveLoopEDMA(I2S_Type *base,
0756                                      sai_edma_handle_t *handle,
0757                                      sai_transfer_t *xfer,
0758                                      uint32_t loopTransferCount)
0759 {
0760     assert((handle != NULL) && (xfer != NULL));
0761 
0762     edma_transfer_config_t config = {0};
0763     uint32_t srcAddr              = SAI_RxGetDataRegisterAddress(base, handle->channel);
0764     sai_transfer_t *transfer      = xfer;
0765     edma_tcd_t *currentTCD        = STCD_ADDR(handle->tcd);
0766     uint32_t tcdIndex             = 0U;
0767 
0768     /* Change the state of handle */
0769     handle->state = (uint32_t)kSAI_Busy;
0770 
0771     for (uint32_t i = 0U; i < loopTransferCount; i++)
0772     {
0773         transfer = &xfer[i];
0774 
0775         if ((tcdIndex >= (uint32_t)SAI_XFER_QUEUE_SIZE) || (xfer->data == NULL) || (xfer->dataSize == 0U))
0776         {
0777             return kStatus_InvalidArgument;
0778         }
0779 
0780         /* Update the queue state */
0781         handle->transferSize[tcdIndex]      = transfer->dataSize;
0782         handle->saiQueue[tcdIndex].data     = transfer->data;
0783         handle->saiQueue[tcdIndex].dataSize = transfer->dataSize;
0784 
0785         /* Prepare edma configure */
0786         EDMA_PrepareTransfer(&config, (uint32_t *)srcAddr, handle->bytesPerFrame, transfer->data, handle->bytesPerFrame,
0787                              (uint32_t)handle->count * handle->bytesPerFrame, transfer->dataSize,
0788                              kEDMA_PeripheralToMemory);
0789 
0790         if (i == (loopTransferCount - 1U))
0791         {
0792             EDMA_TcdSetTransferConfig(&currentTCD[tcdIndex], &config, &currentTCD[0U]);
0793             EDMA_TcdEnableInterrupts(&currentTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable);
0794             handle->state = (uint32_t)kSAI_BusyLoopTransfer;
0795             break;
0796         }
0797         else
0798         {
0799             EDMA_TcdSetTransferConfig(&currentTCD[tcdIndex], &config, &currentTCD[tcdIndex + 1U]);
0800             EDMA_TcdEnableInterrupts(&currentTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable);
0801         }
0802 
0803         tcdIndex = tcdIndex + 1U;
0804     }
0805 
0806     EDMA_InstallTCD(handle->dmaHandle->base, handle->dmaHandle->channel, &currentTCD[0]);
0807     /* Start DMA transfer */
0808     EDMA_StartTransfer(handle->dmaHandle);
0809     /* Enable DMA enable bit */
0810     SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
0811 
0812     /* Enable the channel FIFO */
0813     base->RCR3 |= I2S_RCR3_RCE(1UL << handle->channel);
0814 
0815     /* Enable SAI Rx clock */
0816     SAI_RxEnable(base, true);
0817 
0818     return kStatus_Success;
0819 }
0820 
0821 /*!
0822  * brief Aborts a SAI transfer using eDMA.
0823  *
0824  * This function only aborts the current transfer slots, the other transfer slots' information still kept
0825  * in the handler. If users want to terminate all transfer slots, just call SAI_TransferTerminateSendEDMA.
0826  *
0827  * param base SAI base pointer.
0828  * param handle SAI eDMA handle pointer.
0829  */
0830 void SAI_TransferAbortSendEDMA(I2S_Type *base, sai_edma_handle_t *handle)
0831 {
0832     assert(handle != NULL);
0833 
0834     /* Disable dma */
0835     EDMA_AbortTransfer(handle->dmaHandle);
0836 
0837     /* Disable the channel FIFO */
0838     base->TCR3 &= ~I2S_TCR3_TCE_MASK;
0839 
0840     /* Disable DMA enable bit */
0841     SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, false);
0842 
0843     /* Disable Tx */
0844     SAI_TxEnable(base, false);
0845 
0846     /* If Tx is disabled, reset the FIFO pointer and clear error flags */
0847     if ((base->TCSR & I2S_TCSR_TE_MASK) == 0UL)
0848     {
0849         base->TCSR |= (I2S_TCSR_FR_MASK | I2S_TCSR_SR_MASK);
0850         base->TCSR &= ~I2S_TCSR_SR_MASK;
0851     }
0852 
0853     /* Handle the queue index */
0854     (void)memset(&handle->saiQueue[handle->queueDriver], 0, sizeof(sai_transfer_t));
0855     handle->queueDriver = (handle->queueDriver + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE;
0856 
0857     /* Set the handle state */
0858     handle->state = (uint32_t)kSAI_Idle;
0859 }
0860 
0861 /*!
0862  * brief Aborts a SAI receive using eDMA.
0863  *
0864  * This function only aborts the current transfer slots, the other transfer slots' information still kept
0865  * in the handler. If users want to terminate all transfer slots, just call SAI_TransferTerminateReceiveEDMA.
0866  *
0867  * param base SAI base pointer.
0868  * param handle SAI eDMA handle pointer.
0869  */
0870 void SAI_TransferAbortReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle)
0871 {
0872     assert(handle != NULL);
0873 
0874     /* Disable dma */
0875     EDMA_AbortTransfer(handle->dmaHandle);
0876 
0877     /* Disable the channel FIFO */
0878     base->RCR3 &= ~I2S_RCR3_RCE_MASK;
0879 
0880     /* Disable DMA enable bit */
0881     SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, false);
0882 
0883     /* Disable Rx */
0884     SAI_RxEnable(base, false);
0885 
0886     /* If Rx is disabled, reset the FIFO pointer and clear error flags */
0887     if ((base->RCSR & I2S_RCSR_RE_MASK) == 0UL)
0888     {
0889         base->RCSR |= (I2S_RCSR_FR_MASK | I2S_RCSR_SR_MASK);
0890         base->RCSR &= ~I2S_RCSR_SR_MASK;
0891     }
0892 
0893     /* Handle the queue index */
0894     (void)memset(&handle->saiQueue[handle->queueDriver], 0, sizeof(sai_transfer_t));
0895     handle->queueDriver = (handle->queueDriver + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE;
0896 
0897     /* Set the handle state */
0898     handle->state = (uint32_t)kSAI_Idle;
0899 }
0900 
0901 /*!
0902  * brief Terminate all SAI send.
0903  *
0904  * This function will clear all transfer slots buffered in the sai queue. If users only want to abort the
0905  * current transfer slot, please call SAI_TransferAbortSendEDMA.
0906  *
0907  * param base SAI base pointer.
0908  * param handle SAI eDMA handle pointer.
0909  */
0910 void SAI_TransferTerminateSendEDMA(I2S_Type *base, sai_edma_handle_t *handle)
0911 {
0912     assert(handle != NULL);
0913 
0914     /* Abort the current transfer */
0915     SAI_TransferAbortSendEDMA(base, handle);
0916 
0917     /* Clear all the internal information */
0918     (void)memset(handle->tcd, 0, sizeof(handle->tcd));
0919     (void)memset(handle->saiQueue, 0, sizeof(handle->saiQueue));
0920     (void)memset(handle->transferSize, 0, sizeof(handle->transferSize));
0921 
0922     handle->queueUser   = 0U;
0923     handle->queueDriver = 0U;
0924 }
0925 
0926 /*!
0927  * brief Terminate all SAI receive.
0928  *
0929  * This function will clear all transfer slots buffered in the sai queue. If users only want to abort the
0930  * current transfer slot, please call SAI_TransferAbortReceiveEDMA.
0931  *
0932  * param base SAI base pointer.
0933  * param handle SAI eDMA handle pointer.
0934  */
0935 void SAI_TransferTerminateReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle)
0936 {
0937     assert(handle != NULL);
0938 
0939     /* Abort the current transfer */
0940     SAI_TransferAbortReceiveEDMA(base, handle);
0941 
0942     /* Clear all the internal information */
0943     (void)memset(handle->tcd, 0, sizeof(handle->tcd));
0944     (void)memset(handle->saiQueue, 0, sizeof(handle->saiQueue));
0945     (void)memset(handle->transferSize, 0, sizeof(handle->transferSize));
0946 
0947     handle->queueUser   = 0U;
0948     handle->queueDriver = 0U;
0949 }
0950 
0951 /*!
0952  * brief Gets byte count sent by SAI.
0953  *
0954  * param base SAI base pointer.
0955  * param handle SAI eDMA handle pointer.
0956  * param count Bytes count sent by SAI.
0957  * retval kStatus_Success Succeed get the transfer count.
0958  * retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress.
0959  */
0960 status_t SAI_TransferGetSendCountEDMA(I2S_Type *base, sai_edma_handle_t *handle, size_t *count)
0961 {
0962     assert(handle != NULL);
0963 
0964     status_t status = kStatus_Success;
0965 
0966     if (handle->state != (uint32_t)kSAI_Busy)
0967     {
0968         status = kStatus_NoTransferInProgress;
0969     }
0970     else
0971     {
0972         *count = (handle->transferSize[handle->queueDriver] -
0973                   (uint32_t)handle->nbytes *
0974                       EDMA_GetRemainingMajorLoopCount(handle->dmaHandle->base, handle->dmaHandle->channel));
0975     }
0976 
0977     return status;
0978 }
0979 
0980 /*!
0981  * brief Gets byte count received by SAI.
0982  *
0983  * param base SAI base pointer
0984  * param handle SAI eDMA handle pointer.
0985  * param count Bytes count received by SAI.
0986  * retval kStatus_Success Succeed get the transfer count.
0987  * retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress.
0988  */
0989 status_t SAI_TransferGetReceiveCountEDMA(I2S_Type *base, sai_edma_handle_t *handle, size_t *count)
0990 {
0991     assert(handle != NULL);
0992 
0993     status_t status = kStatus_Success;
0994 
0995     if (handle->state != (uint32_t)kSAI_Busy)
0996     {
0997         status = kStatus_NoTransferInProgress;
0998     }
0999     else
1000     {
1001         *count = (handle->transferSize[handle->queueDriver] -
1002                   (uint32_t)handle->nbytes *
1003                       EDMA_GetRemainingMajorLoopCount(handle->dmaHandle->base, handle->dmaHandle->channel));
1004     }
1005 
1006     return status;
1007 }
1008 
1009 /*!
1010  * @rief Gets valid transfer slot.
1011  *
1012  * This function can be used to query the valid transfer request slot that the application can submit.
1013  * It should be called in the critical section, that means the application could call it in the corresponding callback
1014  * function or disable IRQ before calling it in the application, otherwise, the returned value may not correct.
1015  *
1016  * param base SAI base pointer
1017  * param handle SAI eDMA handle pointer.
1018  * retval valid slot count that application submit.
1019  */
1020 uint32_t SAI_TransferGetValidTransferSlotsEDMA(I2S_Type *base, sai_edma_handle_t *handle)
1021 {
1022     uint32_t validSlot = 0U;
1023 
1024     for (uint32_t i = 0U; i < (uint32_t)SAI_XFER_QUEUE_SIZE; i++)
1025     {
1026         if (handle->saiQueue[i].data == NULL)
1027         {
1028             validSlot++;
1029         }
1030     }
1031 
1032     return validSlot;
1033 }