1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016-2021 NXP
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 #include "fsl_sai_edma.h"
10 
11 /* Component ID definition, used by tools. */
12 #ifndef FSL_COMPONENT_ID
13 #define FSL_COMPONENT_ID "platform.drivers.sai_edma"
14 #endif
15 
16 /*******************************************************************************
17  * Definitions
18  ******************************************************************************/
19 /* Used for edma_tcd_t size aligned */
20 #define STCD_ADDR(address) (edma_tcd_t *)(((uint32_t)(address) + sizeof(edma_tcd_t)) & ~(sizeof(edma_tcd_t) - 1U))
21 
22 static I2S_Type *const s_saiBases[] = I2S_BASE_PTRS;
23 /* Only support 2 and 4 channel */
24 #define SAI_CHANNEL_MAP_MODULO(channel) ((channel) == 2U ? kEDMA_Modulo8bytes : kEDMA_Modulo16bytes)
25 
26 /*<! Structure definition for uart_edma_private_handle_t. The structure is private. */
27 typedef struct sai_edma_private_handle
28 {
29     I2S_Type *base;
30     sai_edma_handle_t *handle;
31 } sai_edma_private_handle_t;
32 
33 /*! @brief sai_edma_transfer_state, sai edma transfer state.*/
34 enum
35 {
36     kSAI_Busy = 0x0U,      /*!< SAI is busy */
37     kSAI_BusyLoopTransfer, /*!< SAI is busy for Loop transfer */
38     kSAI_Idle,             /*!< Transfer is done. */
39 };
40 
41 /*<! Private handle only used for internally. */
42 static sai_edma_private_handle_t s_edmaPrivateHandle[ARRAY_SIZE(s_saiBases)][2];
43 
44 /*******************************************************************************
45  * Prototypes
46  ******************************************************************************/
47 /*!
48  * @brief Get the instance number for SAI.
49  *
50  * @param base SAI base pointer.
51  */
52 static uint32_t SAI_GetInstance(I2S_Type *base);
53 
54 /*!
55  * @brief SAI EDMA callback for send.
56  *
57  * @param handle pointer to sai_edma_handle_t structure which stores the transfer state.
58  * @param userData Parameter for user callback.
59  * @param done If the DMA transfer finished.
60  * @param tcds The TCD index.
61  */
62 static void SAI_TxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds);
63 
64 /*!
65  * @brief SAI EDMA callback for receive.
66  *
67  * @param handle pointer to sai_edma_handle_t structure which stores the transfer state.
68  * @param userData Parameter for user callback.
69  * @param done If the DMA transfer finished.
70  * @param tcds The TCD index.
71  */
72 static void SAI_RxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds);
73 
74 /*******************************************************************************
75  * Code
76  ******************************************************************************/
SAI_GetInstance(I2S_Type * base)77 static uint32_t SAI_GetInstance(I2S_Type *base)
78 {
79     uint32_t instance;
80 
81     /* Find the instance index from base address mappings. */
82     for (instance = 0; instance < ARRAY_SIZE(s_saiBases); instance++)
83     {
84         if (MSDK_REG_SECURE_ADDR(s_saiBases[instance]) == MSDK_REG_SECURE_ADDR(base))
85         {
86             break;
87         }
88     }
89 
90     assert(instance < ARRAY_SIZE(s_saiBases));
91 
92     return instance;
93 }
94 
SAI_TxEDMACallback(edma_handle_t * handle,void * userData,bool done,uint32_t tcds)95 static void SAI_TxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds)
96 {
97     sai_edma_private_handle_t *privHandle = (sai_edma_private_handle_t *)userData;
98     sai_edma_handle_t *saiHandle          = privHandle->handle;
99     status_t status                       = kStatus_SAI_TxBusy;
100 
101     if (saiHandle->state != (uint32_t)kSAI_BusyLoopTransfer)
102     {
103         if (saiHandle->queueDriver + tcds > (uint32_t)SAI_XFER_QUEUE_SIZE)
104         {
105             (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0,
106                          sizeof(sai_transfer_t) * ((uint32_t)SAI_XFER_QUEUE_SIZE - saiHandle->queueDriver));
107             (void)memset(&saiHandle->saiQueue[0U], 0,
108                          sizeof(sai_transfer_t) * (saiHandle->queueDriver + tcds - (uint32_t)SAI_XFER_QUEUE_SIZE));
109         }
110         else
111         {
112             (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0, sizeof(sai_transfer_t) * tcds);
113         }
114         saiHandle->queueDriver = (uint8_t)((saiHandle->queueDriver + tcds) % (uint32_t)SAI_XFER_QUEUE_SIZE);
115 
116         /* If all data finished, just stop the transfer */
117         if (saiHandle->saiQueue[saiHandle->queueDriver].data == NULL)
118         {
119             /* Disable DMA enable bit */
120             SAI_TxEnableDMA(privHandle->base, kSAI_FIFORequestDMAEnable, false);
121             EDMA_AbortTransfer(handle);
122             status = kStatus_SAI_TxIdle;
123         }
124     }
125 
126     /* If finished a block, call the callback function */
127     if (saiHandle->callback != NULL)
128     {
129         (saiHandle->callback)(privHandle->base, saiHandle, status, saiHandle->userData);
130     }
131 }
132 
SAI_RxEDMACallback(edma_handle_t * handle,void * userData,bool done,uint32_t tcds)133 static void SAI_RxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds)
134 {
135     sai_edma_private_handle_t *privHandle = (sai_edma_private_handle_t *)userData;
136     sai_edma_handle_t *saiHandle          = privHandle->handle;
137     status_t status                       = kStatus_SAI_RxBusy;
138 
139     if (saiHandle->state != (uint32_t)kSAI_BusyLoopTransfer)
140     {
141         if (saiHandle->queueDriver + tcds > (uint32_t)SAI_XFER_QUEUE_SIZE)
142         {
143             (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0,
144                          sizeof(sai_transfer_t) * ((uint32_t)SAI_XFER_QUEUE_SIZE - saiHandle->queueDriver));
145             (void)memset(&saiHandle->saiQueue[0U], 0,
146                          sizeof(sai_transfer_t) * (saiHandle->queueDriver + tcds - (uint32_t)SAI_XFER_QUEUE_SIZE));
147         }
148         else
149         {
150             (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0, sizeof(sai_transfer_t) * tcds);
151         }
152         saiHandle->queueDriver = (uint8_t)((saiHandle->queueDriver + tcds) % (uint32_t)SAI_XFER_QUEUE_SIZE);
153 
154         /* If all data finished, just stop the transfer */
155         if (saiHandle->saiQueue[saiHandle->queueDriver].data == NULL)
156         {
157             /* Disable DMA enable bit */
158             SAI_RxEnableDMA(privHandle->base, kSAI_FIFORequestDMAEnable, false);
159             EDMA_AbortTransfer(handle);
160             status = kStatus_SAI_RxIdle;
161         }
162     }
163 
164     /* If finished a block, call the callback function */
165     if (saiHandle->callback != NULL)
166     {
167         (saiHandle->callback)(privHandle->base, saiHandle, status, saiHandle->userData);
168     }
169 }
170 
171 /*!
172  * brief Initializes the SAI eDMA handle.
173  *
174  * This function initializes the SAI master DMA handle, which can be used for other SAI master transactional APIs.
175  * Usually, for a specified SAI instance, call this API once to get the initialized handle.
176  *
177  * param base SAI base pointer.
178  * param handle SAI eDMA handle pointer.
179  * param base SAI peripheral base address.
180  * param callback Pointer to user callback function.
181  * param userData User parameter passed to the callback function.
182  * param dmaHandle eDMA handle pointer, this handle shall be static allocated by users.
183  */
SAI_TransferTxCreateHandleEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_edma_callback_t callback,void * userData,edma_handle_t * txDmaHandle)184 void SAI_TransferTxCreateHandleEDMA(
185     I2S_Type *base, sai_edma_handle_t *handle, sai_edma_callback_t callback, void *userData, edma_handle_t *txDmaHandle)
186 {
187     assert((handle != NULL) && (txDmaHandle != NULL));
188 
189     uint32_t instance = SAI_GetInstance(base);
190 
191     /* Zero the handle */
192     (void)memset(handle, 0, sizeof(*handle));
193 
194     /* Set sai base to handle */
195     handle->dmaHandle      = txDmaHandle;
196     handle->callback       = callback;
197     handle->userData       = userData;
198     handle->interleaveType = kSAI_EDMAInterleavePerChannelSample;
199 
200     /* Set SAI state to idle */
201     handle->state = (uint32_t)kSAI_Idle;
202 
203     s_edmaPrivateHandle[instance][0].base   = base;
204     s_edmaPrivateHandle[instance][0].handle = handle;
205 
206     /* Need to use scatter gather */
207     EDMA_InstallTCDMemory(txDmaHandle, (edma_tcd_t *)(STCD_ADDR(handle->tcd)), SAI_XFER_QUEUE_SIZE);
208 
209     /* Install callback for Tx dma channel */
210     EDMA_SetCallback(txDmaHandle, SAI_TxEDMACallback, &s_edmaPrivateHandle[instance][0]);
211 }
212 
213 /*!
214  * brief Initializes the SAI Rx eDMA handle.
215  *
216  * This function initializes the SAI slave DMA handle, which can be used for other SAI master transactional APIs.
217  * Usually, for a specified SAI instance, call this API once to get the initialized handle.
218  *
219  * param base SAI base pointer.
220  * param handle SAI eDMA handle pointer.
221  * param base SAI peripheral base address.
222  * param callback Pointer to user callback function.
223  * param userData User parameter passed to the callback function.
224  * param dmaHandle eDMA handle pointer, this handle shall be static allocated by users.
225  */
SAI_TransferRxCreateHandleEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_edma_callback_t callback,void * userData,edma_handle_t * rxDmaHandle)226 void SAI_TransferRxCreateHandleEDMA(
227     I2S_Type *base, sai_edma_handle_t *handle, sai_edma_callback_t callback, void *userData, edma_handle_t *rxDmaHandle)
228 {
229     assert((handle != NULL) && (rxDmaHandle != NULL));
230 
231     uint32_t instance = SAI_GetInstance(base);
232 
233     /* Zero the handle */
234     (void)memset(handle, 0, sizeof(*handle));
235 
236     /* Set sai base to handle */
237     handle->dmaHandle      = rxDmaHandle;
238     handle->callback       = callback;
239     handle->userData       = userData;
240     handle->interleaveType = kSAI_EDMAInterleavePerChannelSample;
241 
242     /* Set SAI state to idle */
243     handle->state = (uint32_t)kSAI_Idle;
244 
245     s_edmaPrivateHandle[instance][1].base   = base;
246     s_edmaPrivateHandle[instance][1].handle = handle;
247 
248     /* Need to use scatter gather */
249     EDMA_InstallTCDMemory(rxDmaHandle, STCD_ADDR(handle->tcd), SAI_XFER_QUEUE_SIZE);
250 
251     /* Install callback for Tx dma channel */
252     EDMA_SetCallback(rxDmaHandle, SAI_RxEDMACallback, &s_edmaPrivateHandle[instance][1]);
253 }
254 
255 /*!
256  * brief Initializes the SAI interleave type.
257  *
258  * This function initializes the SAI DMA handle member interleaveType, it shall be called only when application would
259  * like to use type kSAI_EDMAInterleavePerChannelBlock, since the default interleaveType is
260  * kSAI_EDMAInterleavePerChannelSample always
261  *
262  * param handle SAI eDMA handle pointer.
263  * param interleaveType SAI interleave type.
264  */
SAI_TransferSetInterleaveType(sai_edma_handle_t * handle,sai_edma_interleave_t interleaveType)265 void SAI_TransferSetInterleaveType(sai_edma_handle_t *handle, sai_edma_interleave_t interleaveType)
266 {
267     handle->interleaveType = interleaveType;
268 }
269 
270 /*!
271  * brief Configures the SAI Tx.
272  *
273  * note SAI eDMA supports data transfer in a multiple SAI channels if the FIFO Combine feature is supported.
274  * To activate the multi-channel transfer enable SAI channels by filling the channelMask
275  * of sai_transceiver_t with the corresponding values of _sai_channel_mask enum, enable the FIFO Combine
276  * mode by assigning kSAI_FifoCombineModeEnabledOnWrite to the fifoCombine member of sai_fifo_combine_t
277  * which is a member of sai_transceiver_t.
278  * This is an example of multi-channel data transfer configuration step.
279  *  code
280  *   sai_transceiver_t config;
281  *   SAI_GetClassicI2SConfig(&config, kSAI_WordWidth16bits, kSAI_Stereo, kSAI_Channel0Mask|kSAI_Channel1Mask);
282  *   config.fifo.fifoCombine = kSAI_FifoCombineModeEnabledOnWrite;
283  *   SAI_TransferTxSetConfigEDMA(I2S0, &edmaHandle, &config);
284  *  endcode
285  * param base SAI base pointer.
286  * param handle SAI eDMA handle pointer.
287  * param saiConfig sai configurations.
288  */
SAI_TransferTxSetConfigEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transceiver_t * saiConfig)289 void SAI_TransferTxSetConfigEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transceiver_t *saiConfig)
290 {
291     assert((handle != NULL) && (saiConfig != NULL));
292 
293     /* Configure the audio format to SAI registers */
294     SAI_TxSetConfig(base, saiConfig);
295 
296 #if defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE
297     /* Allow multi-channel transfer only if FIFO Combine mode is enabled */
298     assert(
299         (saiConfig->channelNums <= 1U) ||
300         ((saiConfig->channelNums > 1U) && ((saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnWrite) ||
301                                            (saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnReadWrite))));
302 #endif
303 
304     /* Get the transfer size from format, this should be used in EDMA configuration */
305     if (saiConfig->serialData.dataWordLength == 24U)
306     {
307         handle->bytesPerFrame = 4U;
308     }
309     else
310     {
311         handle->bytesPerFrame = saiConfig->serialData.dataWordLength / 8U;
312     }
313     /* Update the data channel SAI used */
314     handle->channel     = saiConfig->startChannel;
315     handle->channelMask = saiConfig->channelMask;
316     handle->channelNums = saiConfig->channelNums;
317 
318     /* Clear the channel enable bits until do a send/receive */
319     base->TCR3 &= ~I2S_TCR3_TCE_MASK;
320 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
321     handle->count = (uint8_t)((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base) - saiConfig->fifo.fifoWatermark);
322 #else
323     handle->count = 1U;
324 #endif /* FSL_FEATURE_SAI_HAS_FIFO */
325 }
326 
327 /*!
328  * brief Configures the SAI Rx.
329  *
330  * note SAI eDMA supports data transfer in a multiple SAI channels if the FIFO Combine feature is supported.
331  * To activate the multi-channel transfer enable SAI channels by filling the channelMask
332  * of sai_transceiver_t with the corresponding values of _sai_channel_mask enum, enable the FIFO Combine
333  * mode by assigning kSAI_FifoCombineModeEnabledOnRead to the fifoCombine member of sai_fifo_combine_t
334  * which is a member of sai_transceiver_t.
335  * This is an example of multi-channel data transfer configuration step.
336  *  code
337  *   sai_transceiver_t config;
338  *   SAI_GetClassicI2SConfig(&config, kSAI_WordWidth16bits, kSAI_Stereo, kSAI_Channel0Mask|kSAI_Channel1Mask);
339  *   config.fifo.fifoCombine = kSAI_FifoCombineModeEnabledOnRead;
340  *   SAI_TransferRxSetConfigEDMA(I2S0, &edmaHandle, &config);
341  *  endcode
342  * param base SAI base pointer.
343  * param handle SAI eDMA handle pointer.
344  * param saiConfig sai configurations.
345  */
SAI_TransferRxSetConfigEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transceiver_t * saiConfig)346 void SAI_TransferRxSetConfigEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transceiver_t *saiConfig)
347 {
348     assert((handle != NULL) && (saiConfig != NULL));
349 
350     /* Configure the audio format to SAI registers */
351     SAI_RxSetConfig(base, saiConfig);
352 
353 #if defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE
354     /* Allow multi-channel transfer only if FIFO Combine mode is enabled */
355     assert(
356         (saiConfig->channelNums <= 1U) ||
357         ((saiConfig->channelNums > 1U) && ((saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnRead) ||
358                                            (saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnReadWrite))));
359 #endif
360 
361     /* Get the transfer size from format, this should be used in EDMA configuration */
362     if (saiConfig->serialData.dataWordLength == 24U)
363     {
364         handle->bytesPerFrame = 4U;
365     }
366     else
367     {
368         handle->bytesPerFrame = saiConfig->serialData.dataWordLength / 8U;
369     }
370 
371     /* Update the data channel SAI used */
372     handle->channel     = saiConfig->startChannel;
373     handle->channelMask = saiConfig->channelMask;
374     handle->channelNums = saiConfig->channelNums;
375     /* Clear the channel enable bits until do a send/receive */
376     base->RCR3 &= ~I2S_RCR3_RCE_MASK;
377 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
378     handle->count = saiConfig->fifo.fifoWatermark;
379 #else
380     handle->count = 1U;
381 #endif /* FSL_FEATURE_SAI_HAS_FIFO */
382 }
383 
384 /*!
385  * brief Performs a non-blocking SAI transfer using DMA.
386  *
387  * note This interface returns immediately after the transfer initiates. Call
388  * SAI_GetTransferStatus to poll the transfer status and check whether the SAI transfer is finished.
389  *
390  * In classic I2S mode configuration.
391  * 1. The data source sent should be formatted as below if handle->interleaveType =
392  *    kSAI_EDMAInterleavePerChannelSample :
393  *    --------------------------------------------------------------------------------------------------
394  *    |LEFT CHANNEL | RIGHT CHANNEL | LEFT CHANNEL | RIGHT CHANNEL | LEFT CHANNEL | RIGHT CHANNEL | ...|
395  *    --------------------------------------------------------------------------------------------------
396  * 2. The data source sent should be formatted as below if handle->interleaveType =
397  *    kSAI_EDMAInterleavePerChannelBlock :
398  *    -------------------------------------------------------------------------------------------------------
399  *    |LEFT CHANNEL | LEFT CHANNEL | LEFT CHANNEL | ...| RIGHT CHANNEL | RIGHT CHANNEL | RIGHT CHANNEL | ...|
400  *    -------------------------------------------------------------------------------------------------------
401  *
402  * This function support multi channel transfer,
403  * 1. for the sai IP support fifo combine mode, application should enable the fifo combine mode, no limitation
404  *    on channel numbers
405  * 2. for the sai IP not support fifo combine mode, sai edma provide another solution which using
406  *    EDMA modulo feature, but support 2 or 4 channels only.
407  *
408  * param base SAI base pointer.
409  * param handle SAI eDMA handle pointer.
410  * param xfer Pointer to the DMA transfer structure.
411  * retval kStatus_Success Start a SAI eDMA send successfully.
412  * retval kStatus_InvalidArgument The input argument is invalid.
413  * retval kStatus_TxBusy SAI is busy sending data.
414  */
SAI_TransferSendEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transfer_t * xfer)415 status_t SAI_TransferSendEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transfer_t *xfer)
416 {
417     assert((handle != NULL) && (xfer != NULL));
418 
419     edma_transfer_config_t config          = {0};
420     uint32_t destAddr                      = SAI_TxGetDataRegisterAddress(base, handle->channel);
421     uint32_t destOffset                    = 0U;
422     uint32_t srcOffset                     = xfer->dataSize / 2U;
423     edma_tcd_t *currentTCD                 = STCD_ADDR(handle->tcd);
424     edma_minor_offset_config_t minorOffset = {.enableSrcMinorOffset  = true,
425                                               .enableDestMinorOffset = false,
426                                               .minorOffset = 0xFFFFFU - 2U * srcOffset + 1U + handle->bytesPerFrame};
427 
428     /* Check if input parameter invalid */
429     if ((xfer->data == NULL) || (xfer->dataSize == 0U))
430     {
431         return kStatus_InvalidArgument;
432     }
433 
434     if (handle->saiQueue[handle->queueUser].data != NULL)
435     {
436         return kStatus_SAI_QueueFull;
437     }
438 
439     /* Change the state of handle */
440     handle->state = (uint32_t)kSAI_Busy;
441 
442     /* Update the queue state */
443     handle->transferSize[handle->queueUser]      = xfer->dataSize;
444     handle->saiQueue[handle->queueUser].data     = xfer->data;
445     handle->saiQueue[handle->queueUser].dataSize = xfer->dataSize;
446     handle->queueUser                            = (handle->queueUser + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE;
447 
448 #if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE)
449     if (handle->channelNums > 1U)
450     {
451         destOffset = sizeof(uint32_t);
452     }
453 #endif
454 
455     if (handle->interleaveType == kSAI_EDMAInterleavePerChannelSample)
456     {
457         /* Prepare edma configure */
458         EDMA_PrepareTransferConfig(&config, xfer->data, (uint32_t)handle->bytesPerFrame, (int16_t)handle->bytesPerFrame,
459                                    (uint32_t *)destAddr, (uint32_t)handle->bytesPerFrame, (int16_t)destOffset,
460                                    (uint32_t)handle->count * handle->bytesPerFrame, xfer->dataSize);
461     }
462     else
463     {
464         EDMA_PrepareTransferConfig(&config, xfer->data, (uint32_t)handle->bytesPerFrame, (int16_t)srcOffset,
465                                    (uint32_t *)destAddr, (uint32_t)handle->bytesPerFrame, (int16_t)destOffset,
466                                    (uint32_t)2U * handle->bytesPerFrame, xfer->dataSize);
467 #if defined FSL_EDMA_DRIVER_EDMA4 && FSL_EDMA_DRIVER_EDMA4
468         EDMA_TcdSetTransferConfigExt(handle->dmaHandle->base, currentTCD, &config, NULL);
469         EDMA_TcdSetMinorOffsetConfigExt(handle->dmaHandle->base, currentTCD, &minorOffset);
470         EDMA_TcdEnableInterruptsExt(handle->dmaHandle->base, currentTCD, (uint32_t)kEDMA_MajorInterruptEnable);
471         EDMA_TcdEnableAutoStopRequestExt(handle->dmaHandle->base, currentTCD, true);
472 #else
473         EDMA_TcdSetTransferConfig(currentTCD, &config, NULL);
474         EDMA_TcdSetMinorOffsetConfig(currentTCD, &minorOffset);
475         EDMA_TcdEnableInterrupts(currentTCD, (uint32_t)kEDMA_MajorInterruptEnable);
476         EDMA_TcdEnableAutoStopRequest(currentTCD, true);
477 #endif
478         EDMA_InstallTCD(handle->dmaHandle->base, handle->dmaHandle->channel, currentTCD);
479     }
480     /* Store the initially configured eDMA minor byte transfer count into the SAI handle */
481     handle->nbytes = handle->count * handle->bytesPerFrame;
482 
483     if (EDMA_SubmitTransfer(handle->dmaHandle, &config) != kStatus_Success)
484     {
485         return kStatus_SAI_QueueFull;
486     }
487 
488 #if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE)
489     if (handle->channelNums > 1U)
490     {
491         if ((handle->channelNums % 2U) != 0U)
492         {
493             return kStatus_InvalidArgument;
494         }
495 
496         EDMA_SetModulo(handle->dmaHandle->base, handle->dmaHandle->channel, kEDMA_ModuloDisable,
497                        SAI_CHANNEL_MAP_MODULO(handle->channelNums));
498     }
499 #endif
500     /* Start DMA transfer */
501     EDMA_StartTransfer(handle->dmaHandle);
502 
503     /* Enable DMA enable bit */
504     SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
505 
506     /* Enable SAI Tx clock */
507     SAI_TxEnable(base, true);
508 
509     /* Enable the channel FIFO */
510     base->TCR3 |= I2S_TCR3_TCE(handle->channelMask);
511 
512     return kStatus_Success;
513 }
514 
515 /*!
516  * brief Performs a non-blocking SAI receive using eDMA.
517  *
518  * note This interface returns immediately after the transfer initiates. Call
519  * the SAI_GetReceiveRemainingBytes to poll the transfer status and check whether the SAI transfer is finished.
520  *
521  * In classic I2S mode configuration.
522  * 1. The output data will be formatted as below if handle->interleaveType =
523  *    kSAI_EDMAInterleavePerChannelSample :
524  *    --------------------------------------------------------------------------------------------------
525  *    |LEFT CHANNEL | RIGHT CHANNEL | LEFT CHANNEL | RIGHT CHANNEL | LEFT CHANNEL | RIGHT CHANNEL | ...|
526  *    --------------------------------------------------------------------------------------------------
527  * 2. The output data will be formatted as below if handle->interleaveType =
528  *    kSAI_EDMAInterleavePerChannelBlock :
529  *    -------------------------------------------------------------------------------------------------------
530  *    |LEFT CHANNEL | LEFT CHANNEL | LEFT CHANNEL | ...| RIGHT CHANNEL | RIGHT CHANNEL | RIGHT CHANNEL | ...|
531  *    -------------------------------------------------------------------------------------------------------
532  *
533  * This function support multi channel transfer,
534  * 1. for the sai IP support fifo combine mode, application should enable the fifo combine mode, no limitation
535  *    on channel numbers
536  * 2. for the sai IP not support fifo combine mode, sai edma provide another solution which using
537  *    EDMA modulo feature, but support 2 or 4 channels only.
538  *
539  * param base SAI base pointer
540  * param handle SAI eDMA handle pointer.
541  * param xfer Pointer to DMA transfer structure.
542  * retval kStatus_Success Start a SAI eDMA receive successfully.
543  * retval kStatus_InvalidArgument The input argument is invalid.
544  * retval kStatus_RxBusy SAI is busy receiving data.
545  */
SAI_TransferReceiveEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transfer_t * xfer)546 status_t SAI_TransferReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transfer_t *xfer)
547 {
548     assert((handle != NULL) && (xfer != NULL));
549 
550     edma_transfer_config_t config          = {0};
551     uint32_t srcAddr                       = SAI_RxGetDataRegisterAddress(base, handle->channel);
552     uint32_t srcOffset                     = 0U;
553     uint32_t destOffset                    = xfer->dataSize / 2U;
554     edma_tcd_t *currentTCD                 = STCD_ADDR(handle->tcd);
555     edma_minor_offset_config_t minorOffset = {
556         .enableSrcMinorOffset  = false,
557         .enableDestMinorOffset = true,
558         .minorOffset           = 0xFFFFFU - 2U * destOffset + 1U + (uint32_t)handle->bytesPerFrame};
559 
560     /* Check if input parameter invalid */
561     if ((xfer->data == NULL) || (xfer->dataSize == 0U))
562     {
563         return kStatus_InvalidArgument;
564     }
565 
566     if (handle->saiQueue[handle->queueUser].data != NULL)
567     {
568         return kStatus_SAI_QueueFull;
569     }
570 
571     /* Change the state of handle */
572     handle->state = (uint32_t)kSAI_Busy;
573 
574     /* Update queue state  */
575     handle->transferSize[handle->queueUser]      = xfer->dataSize;
576     handle->saiQueue[handle->queueUser].data     = xfer->data;
577     handle->saiQueue[handle->queueUser].dataSize = xfer->dataSize;
578     handle->queueUser                            = (handle->queueUser + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE;
579 
580 #if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE)
581     if (handle->channelNums > 1U)
582     {
583         srcOffset = sizeof(uint32_t);
584     }
585 #endif
586 
587     if (handle->interleaveType == kSAI_EDMAInterleavePerChannelSample)
588     {
589         /* Prepare edma configure */
590         EDMA_PrepareTransferConfig(&config, (uint32_t *)srcAddr, (uint32_t)handle->bytesPerFrame, (int16_t)srcOffset,
591                                    xfer->data, (uint32_t)handle->bytesPerFrame, (int16_t)handle->bytesPerFrame,
592                                    (uint32_t)handle->count * handle->bytesPerFrame, xfer->dataSize);
593     }
594     else
595     {
596         EDMA_PrepareTransferConfig(&config, (uint32_t *)srcAddr, (uint32_t)handle->bytesPerFrame, (int16_t)srcOffset,
597                                    xfer->data, (uint32_t)handle->bytesPerFrame, (int16_t)destOffset,
598                                    (uint32_t)2U * handle->bytesPerFrame, xfer->dataSize);
599 #if defined FSL_EDMA_DRIVER_EDMA4 && FSL_EDMA_DRIVER_EDMA4
600         EDMA_TcdSetTransferConfigExt(handle->dmaHandle->base, currentTCD, &config, NULL);
601         EDMA_TcdSetMinorOffsetConfigExt(handle->dmaHandle->base, currentTCD, &minorOffset);
602         EDMA_TcdEnableInterruptsExt(handle->dmaHandle->base, currentTCD, (uint32_t)kEDMA_MajorInterruptEnable);
603         EDMA_TcdEnableAutoStopRequestExt(handle->dmaHandle->base, currentTCD, true);
604 #else
605         EDMA_TcdSetTransferConfig(currentTCD, &config, NULL);
606         EDMA_TcdSetMinorOffsetConfig(currentTCD, &minorOffset);
607         EDMA_TcdEnableInterrupts(currentTCD, (uint32_t)kEDMA_MajorInterruptEnable);
608         EDMA_TcdEnableAutoStopRequest(currentTCD, true);
609 #endif
610         EDMA_InstallTCD(handle->dmaHandle->base, handle->dmaHandle->channel, currentTCD);
611     }
612 
613     /* Store the initially configured eDMA minor byte transfer count into the SAI handle */
614     handle->nbytes = handle->count * handle->bytesPerFrame;
615 
616     if (EDMA_SubmitTransfer(handle->dmaHandle, &config) != kStatus_Success)
617     {
618         return kStatus_SAI_QueueFull;
619     }
620 
621 #if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE)
622     if (handle->channelNums > 1U)
623     {
624         if ((handle->channelNums % 2U) != 0U)
625         {
626             return kStatus_InvalidArgument;
627         }
628 
629         EDMA_SetModulo(handle->dmaHandle->base, handle->dmaHandle->channel, SAI_CHANNEL_MAP_MODULO(handle->channelNums),
630                        kEDMA_ModuloDisable);
631     }
632 #endif
633     /* Start DMA transfer */
634     EDMA_StartTransfer(handle->dmaHandle);
635 
636     /* Enable DMA enable bit */
637     SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
638 
639     /* Enable the channel FIFO */
640     base->RCR3 |= I2S_RCR3_RCE(handle->channelMask);
641 
642     /* Enable SAI Rx clock */
643     SAI_RxEnable(base, true);
644 
645     return kStatus_Success;
646 }
647 
648 /*!
649  * brief Performs a non-blocking SAI loop transfer using eDMA.
650  *
651  * note This function support loop transfer only,such as A->B->...->A, application must be aware of
652  * that the more counts of the loop transfer, then more tcd memory required, as the function use the tcd pool in
653  * sai_edma_handle_t, so application could redefine the SAI_XFER_QUEUE_SIZE to determine the proper TCD pool size.
654  * This function support one sai channel only.
655  *
656  * Once the loop transfer start, application can use function SAI_TransferAbortSendEDMA to stop the loop transfer.
657  *
658  * param base SAI base pointer.
659  * param handle SAI eDMA handle pointer.
660  * param xfer Pointer to the DMA transfer structure, should be a array with elements counts >=1(loopTransferCount).
661  * param loopTransferCount the counts of xfer array.
662  * retval kStatus_Success Start a SAI eDMA send successfully.
663  * retval kStatus_InvalidArgument The input argument is invalid.
664  */
SAI_TransferSendLoopEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transfer_t * xfer,uint32_t loopTransferCount)665 status_t SAI_TransferSendLoopEDMA(I2S_Type *base,
666                                   sai_edma_handle_t *handle,
667                                   sai_transfer_t *xfer,
668                                   uint32_t loopTransferCount)
669 {
670     assert((handle != NULL) && (xfer != NULL));
671 
672     edma_transfer_config_t config = {0};
673     uint32_t destAddr             = SAI_TxGetDataRegisterAddress(base, handle->channel);
674     sai_transfer_t *transfer      = xfer;
675     edma_tcd_t *currentTCD        = STCD_ADDR(handle->tcd);
676     uint32_t tcdIndex             = 0U;
677 
678     /* Change the state of handle */
679     handle->state = (uint32_t)kSAI_Busy;
680 
681     for (uint32_t i = 0U; i < loopTransferCount; i++)
682     {
683         transfer = &xfer[i];
684 
685         if ((transfer->data == NULL) || (transfer->dataSize == 0U) || (tcdIndex >= (uint32_t)SAI_XFER_QUEUE_SIZE))
686         {
687             return kStatus_InvalidArgument;
688         }
689 
690         /* Update the queue state */
691         handle->transferSize[tcdIndex]      = transfer->dataSize;
692         handle->saiQueue[tcdIndex].data     = transfer->data;
693         handle->saiQueue[tcdIndex].dataSize = transfer->dataSize;
694 
695         /* Prepare edma configure */
696         EDMA_PrepareTransfer(&config, transfer->data, handle->bytesPerFrame, (uint32_t *)destAddr,
697                              handle->bytesPerFrame, (uint32_t)handle->count * handle->bytesPerFrame, transfer->dataSize,
698                              kEDMA_MemoryToPeripheral);
699 
700 #if defined FSL_EDMA_DRIVER_EDMA4 && FSL_EDMA_DRIVER_EDMA4
701         if (i == (loopTransferCount - 1U))
702         {
703             EDMA_TcdSetTransferConfigExt(handle->dmaHandle->base, &currentTCD[tcdIndex], &config, &currentTCD[0U]);
704             EDMA_TcdEnableInterruptsExt(handle->dmaHandle->base, &currentTCD[tcdIndex],
705                                         (uint32_t)kEDMA_MajorInterruptEnable);
706             handle->state = (uint32_t)kSAI_BusyLoopTransfer;
707             break;
708         }
709         else
710         {
711             EDMA_TcdSetTransferConfigExt(handle->dmaHandle->base, &currentTCD[tcdIndex], &config,
712                                          &currentTCD[tcdIndex + 1U]);
713             EDMA_TcdEnableInterruptsExt(handle->dmaHandle->base, &currentTCD[tcdIndex],
714                                         (uint32_t)kEDMA_MajorInterruptEnable);
715         }
716 #else
717         if (i == (loopTransferCount - 1U))
718         {
719             EDMA_TcdSetTransferConfig(&currentTCD[tcdIndex], &config, &currentTCD[0U]);
720             EDMA_TcdEnableInterrupts(&currentTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable);
721             handle->state = (uint32_t)kSAI_BusyLoopTransfer;
722             break;
723         }
724         else
725         {
726             EDMA_TcdSetTransferConfig(&currentTCD[tcdIndex], &config, &currentTCD[tcdIndex + 1U]);
727             EDMA_TcdEnableInterrupts(&currentTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable);
728         }
729 #endif
730 
731         tcdIndex = tcdIndex + 1U;
732     }
733 
734     EDMA_InstallTCD(handle->dmaHandle->base, handle->dmaHandle->channel, &currentTCD[0]);
735     /* Start DMA transfer */
736     EDMA_StartTransfer(handle->dmaHandle);
737 
738     /* Enable DMA enable bit */
739     SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
740 
741     /* Enable SAI Tx clock */
742     SAI_TxEnable(base, true);
743 
744     /* Enable the channel FIFO */
745     base->TCR3 |= I2S_TCR3_TCE(1UL << handle->channel);
746 
747     return kStatus_Success;
748 }
749 
750 /*!
751  * brief Performs a non-blocking SAI loop transfer using eDMA.
752  *
753  * note This function support loop transfer only,such as A->B->...->A, application must be aware of
754  * that the more counts of the loop transfer, then more tcd memory required, as the function use the tcd pool in
755  * sai_edma_handle_t, so application could redefine the SAI_XFER_QUEUE_SIZE to determine the proper TCD pool size.
756  * This function support one sai channel only.
757  *
758  * Once the loop transfer start, application can use function SAI_TransferAbortReceiveEDMA to stop the loop transfer.
759  *
760  * param base SAI base pointer.
761  * param handle SAI eDMA handle pointer.
762  * param xfer Pointer to the DMA transfer structure, should be a array with elements counts >=1(loopTransferCount).
763  * param loopTransferCount the counts of xfer array.
764  * retval kStatus_Success Start a SAI eDMA receive successfully.
765  * retval kStatus_InvalidArgument The input argument is invalid.
766  */
SAI_TransferReceiveLoopEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transfer_t * xfer,uint32_t loopTransferCount)767 status_t SAI_TransferReceiveLoopEDMA(I2S_Type *base,
768                                      sai_edma_handle_t *handle,
769                                      sai_transfer_t *xfer,
770                                      uint32_t loopTransferCount)
771 {
772     assert((handle != NULL) && (xfer != NULL));
773 
774     edma_transfer_config_t config = {0};
775     uint32_t srcAddr              = SAI_RxGetDataRegisterAddress(base, handle->channel);
776     sai_transfer_t *transfer      = xfer;
777     edma_tcd_t *currentTCD        = STCD_ADDR(handle->tcd);
778     uint32_t tcdIndex             = 0U;
779 
780     /* Change the state of handle */
781     handle->state = (uint32_t)kSAI_Busy;
782 
783     for (uint32_t i = 0U; i < loopTransferCount; i++)
784     {
785         transfer = &xfer[i];
786 
787         if ((tcdIndex >= (uint32_t)SAI_XFER_QUEUE_SIZE) || (xfer->data == NULL) || (xfer->dataSize == 0U))
788         {
789             return kStatus_InvalidArgument;
790         }
791 
792         /* Update the queue state */
793         handle->transferSize[tcdIndex]      = transfer->dataSize;
794         handle->saiQueue[tcdIndex].data     = transfer->data;
795         handle->saiQueue[tcdIndex].dataSize = transfer->dataSize;
796 
797         /* Prepare edma configure */
798         EDMA_PrepareTransfer(&config, (uint32_t *)srcAddr, handle->bytesPerFrame, transfer->data, handle->bytesPerFrame,
799                              (uint32_t)handle->count * handle->bytesPerFrame, transfer->dataSize,
800                              kEDMA_PeripheralToMemory);
801 
802 #if defined FSL_EDMA_DRIVER_EDMA4 && FSL_EDMA_DRIVER_EDMA4
803         if (i == (loopTransferCount - 1U))
804         {
805             EDMA_TcdSetTransferConfigExt(handle->dmaHandle->base, &currentTCD[tcdIndex], &config, &currentTCD[0U]);
806             EDMA_TcdEnableInterruptsExt(handle->dmaHandle->base, &currentTCD[tcdIndex],
807                                         (uint32_t)kEDMA_MajorInterruptEnable);
808             handle->state = (uint32_t)kSAI_BusyLoopTransfer;
809             break;
810         }
811         else
812         {
813             EDMA_TcdSetTransferConfigExt(handle->dmaHandle->base, &currentTCD[tcdIndex], &config,
814                                          &currentTCD[tcdIndex + 1U]);
815             EDMA_TcdEnableInterruptsExt(handle->dmaHandle->base, &currentTCD[tcdIndex],
816                                         (uint32_t)kEDMA_MajorInterruptEnable);
817         }
818 #else
819         if (i == (loopTransferCount - 1U))
820         {
821             EDMA_TcdSetTransferConfig(&currentTCD[tcdIndex], &config, &currentTCD[0U]);
822             EDMA_TcdEnableInterrupts(&currentTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable);
823             handle->state = (uint32_t)kSAI_BusyLoopTransfer;
824             break;
825         }
826         else
827         {
828             EDMA_TcdSetTransferConfig(&currentTCD[tcdIndex], &config, &currentTCD[tcdIndex + 1U]);
829             EDMA_TcdEnableInterrupts(&currentTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable);
830         }
831 #endif
832 
833         tcdIndex = tcdIndex + 1U;
834     }
835 
836     EDMA_InstallTCD(handle->dmaHandle->base, handle->dmaHandle->channel, &currentTCD[0]);
837     /* Start DMA transfer */
838     EDMA_StartTransfer(handle->dmaHandle);
839     /* Enable DMA enable bit */
840     SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
841 
842     /* Enable the channel FIFO */
843     base->RCR3 |= I2S_RCR3_RCE(1UL << handle->channel);
844 
845     /* Enable SAI Rx clock */
846     SAI_RxEnable(base, true);
847 
848     return kStatus_Success;
849 }
850 
851 /*!
852  * brief Aborts a SAI transfer using eDMA.
853  *
854  * This function only aborts the current transfer slots, the other transfer slots' information still kept
855  * in the handler. If users want to terminate all transfer slots, just call SAI_TransferTerminateSendEDMA.
856  *
857  * param base SAI base pointer.
858  * param handle SAI eDMA handle pointer.
859  */
SAI_TransferAbortSendEDMA(I2S_Type * base,sai_edma_handle_t * handle)860 void SAI_TransferAbortSendEDMA(I2S_Type *base, sai_edma_handle_t *handle)
861 {
862     assert(handle != NULL);
863 
864     /* Disable dma */
865     EDMA_AbortTransfer(handle->dmaHandle);
866 
867     /* Disable the channel FIFO */
868     base->TCR3 &= ~I2S_TCR3_TCE_MASK;
869 
870     /* Disable DMA enable bit */
871     SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, false);
872 
873     /* Disable Tx */
874     SAI_TxEnable(base, false);
875 
876     /* If Tx is disabled, reset the FIFO pointer and clear error flags */
877     if ((base->TCSR & I2S_TCSR_TE_MASK) == 0UL)
878     {
879         base->TCSR |= (I2S_TCSR_FR_MASK | I2S_TCSR_SR_MASK);
880         base->TCSR &= ~I2S_TCSR_SR_MASK;
881     }
882 
883     /* Handle the queue index */
884     (void)memset(&handle->saiQueue[handle->queueDriver], 0, sizeof(sai_transfer_t));
885     handle->queueDriver = (handle->queueDriver + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE;
886 
887     /* Set the handle state */
888     handle->state = (uint32_t)kSAI_Idle;
889 }
890 
891 /*!
892  * brief Aborts a SAI receive using eDMA.
893  *
894  * This function only aborts the current transfer slots, the other transfer slots' information still kept
895  * in the handler. If users want to terminate all transfer slots, just call SAI_TransferTerminateReceiveEDMA.
896  *
897  * param base SAI base pointer.
898  * param handle SAI eDMA handle pointer.
899  */
SAI_TransferAbortReceiveEDMA(I2S_Type * base,sai_edma_handle_t * handle)900 void SAI_TransferAbortReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle)
901 {
902     assert(handle != NULL);
903 
904     /* Disable dma */
905     EDMA_AbortTransfer(handle->dmaHandle);
906 
907     /* Disable the channel FIFO */
908     base->RCR3 &= ~I2S_RCR3_RCE_MASK;
909 
910     /* Disable DMA enable bit */
911     SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, false);
912 
913     /* Disable Rx */
914     SAI_RxEnable(base, false);
915 
916     /* If Rx is disabled, reset the FIFO pointer and clear error flags */
917     if ((base->RCSR & I2S_RCSR_RE_MASK) == 0UL)
918     {
919         base->RCSR |= (I2S_RCSR_FR_MASK | I2S_RCSR_SR_MASK);
920         base->RCSR &= ~I2S_RCSR_SR_MASK;
921     }
922 
923     /* Handle the queue index */
924     (void)memset(&handle->saiQueue[handle->queueDriver], 0, sizeof(sai_transfer_t));
925     handle->queueDriver = (handle->queueDriver + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE;
926 
927     /* Set the handle state */
928     handle->state = (uint32_t)kSAI_Idle;
929 }
930 
931 /*!
932  * brief Terminate all SAI send.
933  *
934  * This function will clear all transfer slots buffered in the sai queue. If users only want to abort the
935  * current transfer slot, please call SAI_TransferAbortSendEDMA.
936  *
937  * param base SAI base pointer.
938  * param handle SAI eDMA handle pointer.
939  */
SAI_TransferTerminateSendEDMA(I2S_Type * base,sai_edma_handle_t * handle)940 void SAI_TransferTerminateSendEDMA(I2S_Type *base, sai_edma_handle_t *handle)
941 {
942     assert(handle != NULL);
943 
944     /* Abort the current transfer */
945     SAI_TransferAbortSendEDMA(base, handle);
946 
947     /* Clear all the internal information */
948     (void)memset(handle->tcd, 0, sizeof(handle->tcd));
949     (void)memset(handle->saiQueue, 0, sizeof(handle->saiQueue));
950     (void)memset(handle->transferSize, 0, sizeof(handle->transferSize));
951 
952     handle->queueUser   = 0U;
953     handle->queueDriver = 0U;
954 }
955 
956 /*!
957  * brief Terminate all SAI receive.
958  *
959  * This function will clear all transfer slots buffered in the sai queue. If users only want to abort the
960  * current transfer slot, please call SAI_TransferAbortReceiveEDMA.
961  *
962  * param base SAI base pointer.
963  * param handle SAI eDMA handle pointer.
964  */
SAI_TransferTerminateReceiveEDMA(I2S_Type * base,sai_edma_handle_t * handle)965 void SAI_TransferTerminateReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle)
966 {
967     assert(handle != NULL);
968 
969     /* Abort the current transfer */
970     SAI_TransferAbortReceiveEDMA(base, handle);
971 
972     /* Clear all the internal information */
973     (void)memset(handle->tcd, 0, sizeof(handle->tcd));
974     (void)memset(handle->saiQueue, 0, sizeof(handle->saiQueue));
975     (void)memset(handle->transferSize, 0, sizeof(handle->transferSize));
976 
977     handle->queueUser   = 0U;
978     handle->queueDriver = 0U;
979 }
980 
981 /*!
982  * brief Gets byte count sent by SAI.
983  *
984  * param base SAI base pointer.
985  * param handle SAI eDMA handle pointer.
986  * param count Bytes count sent by SAI.
987  * retval kStatus_Success Succeed get the transfer count.
988  * retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress.
989  */
SAI_TransferGetSendCountEDMA(I2S_Type * base,sai_edma_handle_t * handle,size_t * count)990 status_t SAI_TransferGetSendCountEDMA(I2S_Type *base, sai_edma_handle_t *handle, size_t *count)
991 {
992     assert(handle != NULL);
993 
994     status_t status = kStatus_Success;
995 
996     if (handle->state != (uint32_t)kSAI_Busy)
997     {
998         status = kStatus_NoTransferInProgress;
999     }
1000     else
1001     {
1002         *count = (handle->transferSize[handle->queueDriver] -
1003                   (uint32_t)handle->nbytes *
1004                       EDMA_GetRemainingMajorLoopCount(handle->dmaHandle->base, handle->dmaHandle->channel));
1005     }
1006 
1007     return status;
1008 }
1009 
1010 /*!
1011  * brief Gets byte count received by SAI.
1012  *
1013  * param base SAI base pointer
1014  * param handle SAI eDMA handle pointer.
1015  * param count Bytes count received by SAI.
1016  * retval kStatus_Success Succeed get the transfer count.
1017  * retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress.
1018  */
SAI_TransferGetReceiveCountEDMA(I2S_Type * base,sai_edma_handle_t * handle,size_t * count)1019 status_t SAI_TransferGetReceiveCountEDMA(I2S_Type *base, sai_edma_handle_t *handle, size_t *count)
1020 {
1021     assert(handle != NULL);
1022 
1023     status_t status = kStatus_Success;
1024 
1025     if (handle->state != (uint32_t)kSAI_Busy)
1026     {
1027         status = kStatus_NoTransferInProgress;
1028     }
1029     else
1030     {
1031         *count = (handle->transferSize[handle->queueDriver] -
1032                   (uint32_t)handle->nbytes *
1033                       EDMA_GetRemainingMajorLoopCount(handle->dmaHandle->base, handle->dmaHandle->channel));
1034     }
1035 
1036     return status;
1037 }
1038 
1039 /*!
1040  * @rief Gets valid transfer slot.
1041  *
1042  * This function can be used to query the valid transfer request slot that the application can submit.
1043  * It should be called in the critical section, that means the application could call it in the corresponding callback
1044  * function or disable IRQ before calling it in the application, otherwise, the returned value may not correct.
1045  *
1046  * param base SAI base pointer
1047  * param handle SAI eDMA handle pointer.
1048  * retval valid slot count that application submit.
1049  */
SAI_TransferGetValidTransferSlotsEDMA(I2S_Type * base,sai_edma_handle_t * handle)1050 uint32_t SAI_TransferGetValidTransferSlotsEDMA(I2S_Type *base, sai_edma_handle_t *handle)
1051 {
1052     uint32_t validSlot = 0U;
1053 
1054     for (uint32_t i = 0U; i < (uint32_t)SAI_XFER_QUEUE_SIZE; i++)
1055     {
1056         if (handle->saiQueue[i].data == NULL)
1057         {
1058             validSlot++;
1059         }
1060     }
1061 
1062     return validSlot;
1063 }
1064