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 32byte aligned */
20 #define STCD_ADDR(address) (edma_tcd_t *)(((uint32_t)(address) + 32UL) & ~0x1FU)
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 (s_saiBases[instance] == 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 
199     /* Set SAI state to idle */
200     handle->state = (uint32_t)kSAI_Idle;
201 
202     s_edmaPrivateHandle[instance][0].base   = base;
203     s_edmaPrivateHandle[instance][0].handle = handle;
204 
205     /* Need to use scatter gather */
206     EDMA_InstallTCDMemory(txDmaHandle, (edma_tcd_t *)(STCD_ADDR(handle->tcd)), SAI_XFER_QUEUE_SIZE);
207 
208     /* Install callback for Tx dma channel */
209     EDMA_SetCallback(txDmaHandle, SAI_TxEDMACallback, &s_edmaPrivateHandle[instance][0]);
210 }
211 
212 /*!
213  * brief Initializes the SAI Rx eDMA handle.
214  *
215  * This function initializes the SAI slave DMA handle, which can be used for other SAI master transactional APIs.
216  * Usually, for a specified SAI instance, call this API once to get the initialized handle.
217  *
218  * param base SAI base pointer.
219  * param handle SAI eDMA handle pointer.
220  * param base SAI peripheral base address.
221  * param callback Pointer to user callback function.
222  * param userData User parameter passed to the callback function.
223  * param dmaHandle eDMA handle pointer, this handle shall be static allocated by users.
224  */
SAI_TransferRxCreateHandleEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_edma_callback_t callback,void * userData,edma_handle_t * rxDmaHandle)225 void SAI_TransferRxCreateHandleEDMA(
226     I2S_Type *base, sai_edma_handle_t *handle, sai_edma_callback_t callback, void *userData, edma_handle_t *rxDmaHandle)
227 {
228     assert((handle != NULL) && (rxDmaHandle != NULL));
229 
230     uint32_t instance = SAI_GetInstance(base);
231 
232     /* Zero the handle */
233     (void)memset(handle, 0, sizeof(*handle));
234 
235     /* Set sai base to handle */
236     handle->dmaHandle = rxDmaHandle;
237     handle->callback  = callback;
238     handle->userData  = userData;
239 
240     /* Set SAI state to idle */
241     handle->state = (uint32_t)kSAI_Idle;
242 
243     s_edmaPrivateHandle[instance][1].base   = base;
244     s_edmaPrivateHandle[instance][1].handle = handle;
245 
246     /* Need to use scatter gather */
247     EDMA_InstallTCDMemory(rxDmaHandle, STCD_ADDR(handle->tcd), SAI_XFER_QUEUE_SIZE);
248 
249     /* Install callback for Tx dma channel */
250     EDMA_SetCallback(rxDmaHandle, SAI_RxEDMACallback, &s_edmaPrivateHandle[instance][1]);
251 }
252 
253 /*!
254  * brief Configures the SAI Tx audio format.
255  *
256  * deprecated Do not use this function.  It has been superceded by ref SAI_TransferTxSetConfigEDMA
257  *
258  * The audio format can be changed at run-time. This function configures the sample rate and audio data
259  * format to be transferred. This function also sets the eDMA parameter according to formatting requirements.
260  *
261  * param base SAI base pointer.
262  * param handle SAI eDMA handle pointer.
263  * param format Pointer to SAI audio data format structure.
264  * param mclkSourceClockHz SAI master clock source frequency in Hz.
265  * param bclkSourceClockHz SAI bit clock source frequency in Hz. If bit clock source is master
266  * clock, this value should equals to masterClockHz in format.
267  * retval kStatus_Success Audio format set successfully.
268  * retval kStatus_InvalidArgument The input argument is invalid.
269  */
SAI_TransferTxSetFormatEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transfer_format_t * format,uint32_t mclkSourceClockHz,uint32_t bclkSourceClockHz)270 void SAI_TransferTxSetFormatEDMA(I2S_Type *base,
271                                  sai_edma_handle_t *handle,
272                                  sai_transfer_format_t *format,
273                                  uint32_t mclkSourceClockHz,
274                                  uint32_t bclkSourceClockHz)
275 {
276     assert((handle != NULL) && (format != NULL));
277 
278     /* Configure the audio format to SAI registers */
279     SAI_TxSetFormat(base, format, mclkSourceClockHz, bclkSourceClockHz);
280 
281     /* Get the transfer size from format, this should be used in EDMA configuration */
282     if (format->bitWidth == 24U)
283     {
284         handle->bytesPerFrame = 4U;
285     }
286     else
287     {
288         handle->bytesPerFrame = (uint8_t)(format->bitWidth / 8U);
289     }
290 
291     /* Update the data channel SAI used */
292     handle->channel = format->channel;
293 
294     /* Clear the channel enable bits until do a send/receive */
295     base->TCR3 &= ~I2S_TCR3_TCE_MASK;
296 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
297     handle->count = (uint8_t)((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base) - format->watermark);
298 #else
299     handle->count = 1U;
300 #endif /* FSL_FEATURE_SAI_HAS_FIFO */
301 }
302 
303 /*!
304  * brief Configures the SAI Tx.
305  *
306  * note SAI eDMA supports data transfer in a multiple SAI channels if the FIFO Combine feature is supported.
307  * To activate the multi-channel transfer enable SAI channels by filling the channelMask
308  * of sai_transceiver_t with the corresponding values of _sai_channel_mask enum, enable the FIFO Combine
309  * mode by assigning kSAI_FifoCombineModeEnabledOnWrite to the fifoCombine member of sai_fifo_combine_t
310  * which is a member of sai_transceiver_t.
311  * This is an example of multi-channel data transfer configuration step.
312  *  code
313  *   sai_transceiver_t config;
314  *   SAI_GetClassicI2SConfig(&config, kSAI_WordWidth16bits, kSAI_Stereo, kSAI_Channel0Mask|kSAI_Channel1Mask);
315  *   config.fifo.fifoCombine = kSAI_FifoCombineModeEnabledOnWrite;
316  *   SAI_TransferTxSetConfigEDMA(I2S0, &edmaHandle, &config);
317  *  endcode
318  * param base SAI base pointer.
319  * param handle SAI eDMA handle pointer.
320  * param saiConfig sai configurations.
321  */
SAI_TransferTxSetConfigEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transceiver_t * saiConfig)322 void SAI_TransferTxSetConfigEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transceiver_t *saiConfig)
323 {
324     assert((handle != NULL) && (saiConfig != NULL));
325 
326     /* Configure the audio format to SAI registers */
327     SAI_TxSetConfig(base, saiConfig);
328 
329 #if defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE
330     /* Allow multi-channel transfer only if FIFO Combine mode is enabled */
331     assert(
332         (saiConfig->channelNums <= 1U) ||
333         ((saiConfig->channelNums > 1U) && ((saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnWrite) ||
334                                            (saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnReadWrite))));
335 #endif
336 
337     /* Get the transfer size from format, this should be used in EDMA configuration */
338     if (saiConfig->serialData.dataWordLength == 24U)
339     {
340         handle->bytesPerFrame = 4U;
341     }
342     else
343     {
344         handle->bytesPerFrame = saiConfig->serialData.dataWordLength / 8U;
345     }
346     /* Update the data channel SAI used */
347     handle->channel     = saiConfig->startChannel;
348     handle->channelMask = saiConfig->channelMask;
349     handle->channelNums = saiConfig->channelNums;
350 
351     /* Clear the channel enable bits until do a send/receive */
352     base->TCR3 &= ~I2S_TCR3_TCE_MASK;
353 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
354     handle->count = (uint8_t)((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base) - saiConfig->fifo.fifoWatermark);
355 #else
356     handle->count = 1U;
357 #endif /* FSL_FEATURE_SAI_HAS_FIFO */
358 }
359 
360 /*!
361  * brief Configures the SAI Rx audio format.
362  *
363  * deprecated Do not use this function.  It has been superceded by ref SAI_TransferRxSetConfigEDMA
364  *
365  * The audio format can be changed at run-time. This function configures the sample rate and audio data
366  * format to be transferred. This function also sets the eDMA parameter according to formatting requirements.
367  *
368  * param base SAI base pointer.
369  * param handle SAI eDMA handle pointer.
370  * param format Pointer to SAI audio data format structure.
371  * param mclkSourceClockHz SAI master clock source frequency in Hz.
372  * param bclkSourceClockHz SAI bit clock source frequency in Hz. If a bit clock source is the master
373  * clock, this value should equal to masterClockHz in format.
374  * retval kStatus_Success Audio format set successfully.
375  * retval kStatus_InvalidArgument The input argument is invalid.
376  */
SAI_TransferRxSetFormatEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transfer_format_t * format,uint32_t mclkSourceClockHz,uint32_t bclkSourceClockHz)377 void SAI_TransferRxSetFormatEDMA(I2S_Type *base,
378                                  sai_edma_handle_t *handle,
379                                  sai_transfer_format_t *format,
380                                  uint32_t mclkSourceClockHz,
381                                  uint32_t bclkSourceClockHz)
382 {
383     assert((handle != NULL) && (format != NULL));
384 
385     /* Configure the audio format to SAI registers */
386     SAI_RxSetFormat(base, format, mclkSourceClockHz, bclkSourceClockHz);
387 
388     /* Get the transfer size from format, this should be used in EDMA configuration */
389     if (format->bitWidth == 24U)
390     {
391         handle->bytesPerFrame = 4U;
392     }
393     else
394     {
395         handle->bytesPerFrame = (uint8_t)(format->bitWidth / 8U);
396     }
397 
398     /* Update the data channel SAI used */
399     handle->channel = format->channel;
400 
401     /* Clear the channel enable bits until do a send/receive */
402     base->RCR3 &= ~I2S_RCR3_RCE_MASK;
403 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
404     handle->count = format->watermark;
405 #else
406     handle->count = 1U;
407 #endif /* FSL_FEATURE_SAI_HAS_FIFO */
408 }
409 
410 /*!
411  * brief Configures the SAI Rx.
412  *
413  * note SAI eDMA supports data transfer in a multiple SAI channels if the FIFO Combine feature is supported.
414  * To activate the multi-channel transfer enable SAI channels by filling the channelMask
415  * of sai_transceiver_t with the corresponding values of _sai_channel_mask enum, enable the FIFO Combine
416  * mode by assigning kSAI_FifoCombineModeEnabledOnRead to the fifoCombine member of sai_fifo_combine_t
417  * which is a member of sai_transceiver_t.
418  * This is an example of multi-channel data transfer configuration step.
419  *  code
420  *   sai_transceiver_t config;
421  *   SAI_GetClassicI2SConfig(&config, kSAI_WordWidth16bits, kSAI_Stereo, kSAI_Channel0Mask|kSAI_Channel1Mask);
422  *   config.fifo.fifoCombine = kSAI_FifoCombineModeEnabledOnRead;
423  *   SAI_TransferRxSetConfigEDMA(I2S0, &edmaHandle, &config);
424  *  endcode
425  * param base SAI base pointer.
426  * param handle SAI eDMA handle pointer.
427  * param saiConfig sai configurations.
428  */
SAI_TransferRxSetConfigEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transceiver_t * saiConfig)429 void SAI_TransferRxSetConfigEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transceiver_t *saiConfig)
430 {
431     assert((handle != NULL) && (saiConfig != NULL));
432 
433     /* Configure the audio format to SAI registers */
434     SAI_RxSetConfig(base, saiConfig);
435 
436 #if defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE
437     /* Allow multi-channel transfer only if FIFO Combine mode is enabled */
438     assert(
439         (saiConfig->channelNums <= 1U) ||
440         ((saiConfig->channelNums > 1U) && ((saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnRead) ||
441                                            (saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnReadWrite))));
442 #endif
443 
444     /* Get the transfer size from format, this should be used in EDMA configuration */
445     if (saiConfig->serialData.dataWordLength == 24U)
446     {
447         handle->bytesPerFrame = 4U;
448     }
449     else
450     {
451         handle->bytesPerFrame = saiConfig->serialData.dataWordLength / 8U;
452     }
453 
454     /* Update the data channel SAI used */
455     handle->channel     = saiConfig->startChannel;
456     handle->channelMask = saiConfig->channelMask;
457     handle->channelNums = saiConfig->channelNums;
458     /* Clear the channel enable bits until do a send/receive */
459     base->RCR3 &= ~I2S_RCR3_RCE_MASK;
460 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
461     handle->count = saiConfig->fifo.fifoWatermark;
462 #else
463     handle->count = 1U;
464 #endif /* FSL_FEATURE_SAI_HAS_FIFO */
465 }
466 
467 /*!
468  * brief Performs a non-blocking SAI transfer using DMA.
469  *
470  * note This interface returns immediately after the transfer initiates. Call
471  * SAI_GetTransferStatus to poll the transfer status and check whether the SAI transfer is finished.
472  *
473  * This function support multi channel transfer,
474  * 1. for the sai IP support fifo combine mode, application should enable the fifo combine mode, no limitation
475  *    on channel numbers
476  * 2. for the sai IP not support fifo combine mode, sai edma provide another solution which using
477  *    EDMA modulo feature, but support 2 or 4 channels only.
478  *
479  * param base SAI base pointer.
480  * param handle SAI eDMA handle pointer.
481  * param xfer Pointer to the DMA transfer structure.
482  * retval kStatus_Success Start a SAI eDMA send successfully.
483  * retval kStatus_InvalidArgument The input argument is invalid.
484  * retval kStatus_TxBusy SAI is busy sending data.
485  */
SAI_TransferSendEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transfer_t * xfer)486 status_t SAI_TransferSendEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transfer_t *xfer)
487 {
488     assert((handle != NULL) && (xfer != NULL));
489 
490     edma_transfer_config_t config = {0};
491     uint32_t destAddr             = SAI_TxGetDataRegisterAddress(base, handle->channel);
492     uint32_t destOffset           = 0U;
493 
494     /* Check if input parameter invalid */
495     if ((xfer->data == NULL) || (xfer->dataSize == 0U))
496     {
497         return kStatus_InvalidArgument;
498     }
499 
500     if (handle->saiQueue[handle->queueUser].data != NULL)
501     {
502         return kStatus_SAI_QueueFull;
503     }
504 
505     /* Change the state of handle */
506     handle->state = (uint32_t)kSAI_Busy;
507 
508     /* Update the queue state */
509     handle->transferSize[handle->queueUser]      = xfer->dataSize;
510     handle->saiQueue[handle->queueUser].data     = xfer->data;
511     handle->saiQueue[handle->queueUser].dataSize = xfer->dataSize;
512     handle->queueUser                            = (handle->queueUser + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE;
513 
514 #if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE)
515     if (handle->channelNums > 1U)
516     {
517         destOffset = sizeof(uint32_t);
518     }
519 #endif
520 
521     /* Prepare edma configure */
522     EDMA_PrepareTransferConfig(&config, xfer->data, (uint32_t)handle->bytesPerFrame, (int16_t)handle->bytesPerFrame,
523                                (uint32_t *)destAddr, (uint32_t)handle->bytesPerFrame, (int16_t)destOffset,
524                                (uint32_t)handle->count * handle->bytesPerFrame, xfer->dataSize);
525 
526     /* Store the initially configured eDMA minor byte transfer count into the SAI handle */
527     handle->nbytes = handle->count * handle->bytesPerFrame;
528 
529     if (EDMA_SubmitTransfer(handle->dmaHandle, &config) != kStatus_Success)
530     {
531         return kStatus_SAI_QueueFull;
532     }
533 
534 #if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE)
535     if (handle->channelNums > 1U)
536     {
537         if ((handle->channelNums % 2U) != 0U)
538         {
539             return kStatus_InvalidArgument;
540         }
541 
542         EDMA_SetModulo(handle->dmaHandle->base, handle->dmaHandle->channel, kEDMA_ModuloDisable,
543                        SAI_CHANNEL_MAP_MODULO(handle->channelNums));
544     }
545 #endif
546     /* Start DMA transfer */
547     EDMA_StartTransfer(handle->dmaHandle);
548 
549     /* Enable DMA enable bit */
550     SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
551 
552     /* Enable SAI Tx clock */
553     SAI_TxEnable(base, true);
554 
555     /* Enable the channel FIFO */
556     base->TCR3 |= I2S_TCR3_TCE(handle->channelMask);
557 
558     return kStatus_Success;
559 }
560 
561 /*!
562  * brief Performs a non-blocking SAI receive using eDMA.
563  *
564  * note This interface returns immediately after the transfer initiates. Call
565  * the SAI_GetReceiveRemainingBytes to poll the transfer status and check whether the SAI transfer is finished.
566  *
567  * This function support multi channel transfer,
568  * 1. for the sai IP support fifo combine mode, application should enable the fifo combine mode, no limitation
569  *    on channel numbers
570  * 2. for the sai IP not support fifo combine mode, sai edma provide another solution which using
571  *    EDMA modulo feature, but support 2 or 4 channels only.
572  *
573  * param base SAI base pointer
574  * param handle SAI eDMA handle pointer.
575  * param xfer Pointer to DMA transfer structure.
576  * retval kStatus_Success Start a SAI eDMA receive successfully.
577  * retval kStatus_InvalidArgument The input argument is invalid.
578  * retval kStatus_RxBusy SAI is busy receiving data.
579  */
SAI_TransferReceiveEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transfer_t * xfer)580 status_t SAI_TransferReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transfer_t *xfer)
581 {
582     assert((handle != NULL) && (xfer != NULL));
583 
584     edma_transfer_config_t config = {0};
585     uint32_t srcAddr              = SAI_RxGetDataRegisterAddress(base, handle->channel);
586     uint32_t srcOffset            = 0U;
587 
588     /* Check if input parameter invalid */
589     if ((xfer->data == NULL) || (xfer->dataSize == 0U))
590     {
591         return kStatus_InvalidArgument;
592     }
593 
594     if (handle->saiQueue[handle->queueUser].data != NULL)
595     {
596         return kStatus_SAI_QueueFull;
597     }
598 
599     /* Change the state of handle */
600     handle->state = (uint32_t)kSAI_Busy;
601 
602     /* Update queue state  */
603     handle->transferSize[handle->queueUser]      = xfer->dataSize;
604     handle->saiQueue[handle->queueUser].data     = xfer->data;
605     handle->saiQueue[handle->queueUser].dataSize = xfer->dataSize;
606     handle->queueUser                            = (handle->queueUser + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE;
607 
608 #if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE)
609     if (handle->channelNums > 1U)
610     {
611         srcOffset = sizeof(uint32_t);
612     }
613 #endif
614 
615     /* Prepare edma configure */
616     EDMA_PrepareTransferConfig(&config, (uint32_t *)srcAddr, (uint32_t)handle->bytesPerFrame, (int16_t)srcOffset,
617                                xfer->data, (uint32_t)handle->bytesPerFrame, (int16_t)handle->bytesPerFrame,
618                                (uint32_t)handle->count * handle->bytesPerFrame, xfer->dataSize);
619     /* Store the initially configured eDMA minor byte transfer count into the SAI handle */
620     handle->nbytes = handle->count * handle->bytesPerFrame;
621 
622     if (EDMA_SubmitTransfer(handle->dmaHandle, &config) != kStatus_Success)
623     {
624         return kStatus_SAI_QueueFull;
625     }
626 
627 #if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE)
628     if (handle->channelNums > 1U)
629     {
630         if ((handle->channelNums % 2U) != 0U)
631         {
632             return kStatus_InvalidArgument;
633         }
634 
635         EDMA_SetModulo(handle->dmaHandle->base, handle->dmaHandle->channel, SAI_CHANNEL_MAP_MODULO(handle->channelNums),
636                        kEDMA_ModuloDisable);
637     }
638 #endif
639     /* Start DMA transfer */
640     EDMA_StartTransfer(handle->dmaHandle);
641 
642     /* Enable DMA enable bit */
643     SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
644 
645     /* Enable the channel FIFO */
646     base->RCR3 |= I2S_RCR3_RCE(handle->channelMask);
647 
648     /* Enable SAI Rx clock */
649     SAI_RxEnable(base, true);
650 
651     return kStatus_Success;
652 }
653 
654 /*!
655  * brief Performs a non-blocking SAI loop transfer using eDMA.
656  *
657  * note This function support loop transfer only,such as A->B->...->A, application must be aware of
658  * that the more counts of the loop transfer, then more tcd memory required, as the function use the tcd pool in
659  * sai_edma_handle_t, so application could redefine the SAI_XFER_QUEUE_SIZE to determine the proper TCD pool size.
660  * This function support one sai channel only.
661  *
662  * Once the loop transfer start, application can use function SAI_TransferAbortSendEDMA to stop the loop transfer.
663  *
664  * param base SAI base pointer.
665  * param handle SAI eDMA handle pointer.
666  * param xfer Pointer to the DMA transfer structure, should be a array with elements counts >=1(loopTransferCount).
667  * param loopTransferCount the counts of xfer array.
668  * retval kStatus_Success Start a SAI eDMA send successfully.
669  * retval kStatus_InvalidArgument The input argument is invalid.
670  */
SAI_TransferSendLoopEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transfer_t * xfer,uint32_t loopTransferCount)671 status_t SAI_TransferSendLoopEDMA(I2S_Type *base,
672                                   sai_edma_handle_t *handle,
673                                   sai_transfer_t *xfer,
674                                   uint32_t loopTransferCount)
675 {
676     assert((handle != NULL) && (xfer != NULL));
677 
678     edma_transfer_config_t config = {0};
679     uint32_t destAddr             = SAI_TxGetDataRegisterAddress(base, handle->channel);
680     sai_transfer_t *transfer      = xfer;
681     edma_tcd_t *currentTCD        = STCD_ADDR(handle->tcd);
682     uint32_t tcdIndex             = 0U;
683 
684     /* Change the state of handle */
685     handle->state = (uint32_t)kSAI_Busy;
686 
687     for (uint32_t i = 0U; i < loopTransferCount; i++)
688     {
689         transfer = &xfer[i];
690 
691         if ((transfer->data == NULL) || (transfer->dataSize == 0U) || (tcdIndex >= (uint32_t)SAI_XFER_QUEUE_SIZE))
692         {
693             return kStatus_InvalidArgument;
694         }
695 
696         /* Update the queue state */
697         handle->transferSize[tcdIndex]      = transfer->dataSize;
698         handle->saiQueue[tcdIndex].data     = transfer->data;
699         handle->saiQueue[tcdIndex].dataSize = transfer->dataSize;
700 
701         /* Prepare edma configure */
702         EDMA_PrepareTransfer(&config, transfer->data, handle->bytesPerFrame, (uint32_t *)destAddr,
703                              handle->bytesPerFrame, (uint32_t)handle->count * handle->bytesPerFrame, transfer->dataSize,
704                              kEDMA_MemoryToPeripheral);
705 
706         if (i == (loopTransferCount - 1U))
707         {
708             EDMA_TcdSetTransferConfig(&currentTCD[tcdIndex], &config, &currentTCD[0U]);
709             EDMA_TcdEnableInterrupts(&currentTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable);
710             handle->state = (uint32_t)kSAI_BusyLoopTransfer;
711             break;
712         }
713         else
714         {
715             EDMA_TcdSetTransferConfig(&currentTCD[tcdIndex], &config, &currentTCD[tcdIndex + 1U]);
716             EDMA_TcdEnableInterrupts(&currentTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable);
717         }
718 
719         tcdIndex = tcdIndex + 1U;
720     }
721 
722     EDMA_InstallTCD(handle->dmaHandle->base, handle->dmaHandle->channel, &currentTCD[0]);
723     /* Start DMA transfer */
724     EDMA_StartTransfer(handle->dmaHandle);
725 
726     /* Enable DMA enable bit */
727     SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
728 
729     /* Enable SAI Tx clock */
730     SAI_TxEnable(base, true);
731 
732     /* Enable the channel FIFO */
733     base->TCR3 |= I2S_TCR3_TCE(1UL << handle->channel);
734 
735     return kStatus_Success;
736 }
737 
738 /*!
739  * brief Performs a non-blocking SAI loop transfer using eDMA.
740  *
741  * note This function support loop transfer only,such as A->B->...->A, application must be aware of
742  * that the more counts of the loop transfer, then more tcd memory required, as the function use the tcd pool in
743  * sai_edma_handle_t, so application could redefine the SAI_XFER_QUEUE_SIZE to determine the proper TCD pool size.
744  * This function support one sai channel only.
745  *
746  * Once the loop transfer start, application can use function SAI_TransferAbortReceiveEDMA to stop the loop transfer.
747  *
748  * param base SAI base pointer.
749  * param handle SAI eDMA handle pointer.
750  * param xfer Pointer to the DMA transfer structure, should be a array with elements counts >=1(loopTransferCount).
751  * param loopTransferCount the counts of xfer array.
752  * retval kStatus_Success Start a SAI eDMA receive successfully.
753  * retval kStatus_InvalidArgument The input argument is invalid.
754  */
SAI_TransferReceiveLoopEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transfer_t * xfer,uint32_t loopTransferCount)755 status_t SAI_TransferReceiveLoopEDMA(I2S_Type *base,
756                                      sai_edma_handle_t *handle,
757                                      sai_transfer_t *xfer,
758                                      uint32_t loopTransferCount)
759 {
760     assert((handle != NULL) && (xfer != NULL));
761 
762     edma_transfer_config_t config = {0};
763     uint32_t srcAddr              = SAI_RxGetDataRegisterAddress(base, handle->channel);
764     sai_transfer_t *transfer      = xfer;
765     edma_tcd_t *currentTCD        = STCD_ADDR(handle->tcd);
766     uint32_t tcdIndex             = 0U;
767 
768     /* Change the state of handle */
769     handle->state = (uint32_t)kSAI_Busy;
770 
771     for (uint32_t i = 0U; i < loopTransferCount; i++)
772     {
773         transfer = &xfer[i];
774 
775         if ((tcdIndex >= (uint32_t)SAI_XFER_QUEUE_SIZE) || (xfer->data == NULL) || (xfer->dataSize == 0U))
776         {
777             return kStatus_InvalidArgument;
778         }
779 
780         /* Update the queue state */
781         handle->transferSize[tcdIndex]      = transfer->dataSize;
782         handle->saiQueue[tcdIndex].data     = transfer->data;
783         handle->saiQueue[tcdIndex].dataSize = transfer->dataSize;
784 
785         /* Prepare edma configure */
786         EDMA_PrepareTransfer(&config, (uint32_t *)srcAddr, handle->bytesPerFrame, transfer->data, handle->bytesPerFrame,
787                              (uint32_t)handle->count * handle->bytesPerFrame, transfer->dataSize,
788                              kEDMA_PeripheralToMemory);
789 
790         if (i == (loopTransferCount - 1U))
791         {
792             EDMA_TcdSetTransferConfig(&currentTCD[tcdIndex], &config, &currentTCD[0U]);
793             EDMA_TcdEnableInterrupts(&currentTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable);
794             handle->state = (uint32_t)kSAI_BusyLoopTransfer;
795             break;
796         }
797         else
798         {
799             EDMA_TcdSetTransferConfig(&currentTCD[tcdIndex], &config, &currentTCD[tcdIndex + 1U]);
800             EDMA_TcdEnableInterrupts(&currentTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable);
801         }
802 
803         tcdIndex = tcdIndex + 1U;
804     }
805 
806     EDMA_InstallTCD(handle->dmaHandle->base, handle->dmaHandle->channel, &currentTCD[0]);
807     /* Start DMA transfer */
808     EDMA_StartTransfer(handle->dmaHandle);
809     /* Enable DMA enable bit */
810     SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
811 
812     /* Enable the channel FIFO */
813     base->RCR3 |= I2S_RCR3_RCE(1UL << handle->channel);
814 
815     /* Enable SAI Rx clock */
816     SAI_RxEnable(base, true);
817 
818     return kStatus_Success;
819 }
820 
821 /*!
822  * brief Aborts a SAI transfer using eDMA.
823  *
824  * This function only aborts the current transfer slots, the other transfer slots' information still kept
825  * in the handler. If users want to terminate all transfer slots, just call SAI_TransferTerminateSendEDMA.
826  *
827  * param base SAI base pointer.
828  * param handle SAI eDMA handle pointer.
829  */
SAI_TransferAbortSendEDMA(I2S_Type * base,sai_edma_handle_t * handle)830 void SAI_TransferAbortSendEDMA(I2S_Type *base, sai_edma_handle_t *handle)
831 {
832     assert(handle != NULL);
833 
834     /* Disable dma */
835     EDMA_AbortTransfer(handle->dmaHandle);
836 
837     /* Disable the channel FIFO */
838     base->TCR3 &= ~I2S_TCR3_TCE_MASK;
839 
840     /* Disable DMA enable bit */
841     SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, false);
842 
843     /* Disable Tx */
844     SAI_TxEnable(base, false);
845 
846     /* If Tx is disabled, reset the FIFO pointer and clear error flags */
847     if ((base->TCSR & I2S_TCSR_TE_MASK) == 0UL)
848     {
849         base->TCSR |= (I2S_TCSR_FR_MASK | I2S_TCSR_SR_MASK);
850         base->TCSR &= ~I2S_TCSR_SR_MASK;
851     }
852 
853     /* Handle the queue index */
854     (void)memset(&handle->saiQueue[handle->queueDriver], 0, sizeof(sai_transfer_t));
855     handle->queueDriver = (handle->queueDriver + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE;
856 
857     /* Set the handle state */
858     handle->state = (uint32_t)kSAI_Idle;
859 }
860 
861 /*!
862  * brief Aborts a SAI receive using eDMA.
863  *
864  * This function only aborts the current transfer slots, the other transfer slots' information still kept
865  * in the handler. If users want to terminate all transfer slots, just call SAI_TransferTerminateReceiveEDMA.
866  *
867  * param base SAI base pointer.
868  * param handle SAI eDMA handle pointer.
869  */
SAI_TransferAbortReceiveEDMA(I2S_Type * base,sai_edma_handle_t * handle)870 void SAI_TransferAbortReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle)
871 {
872     assert(handle != NULL);
873 
874     /* Disable dma */
875     EDMA_AbortTransfer(handle->dmaHandle);
876 
877     /* Disable the channel FIFO */
878     base->RCR3 &= ~I2S_RCR3_RCE_MASK;
879 
880     /* Disable DMA enable bit */
881     SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, false);
882 
883     /* Disable Rx */
884     SAI_RxEnable(base, false);
885 
886     /* If Rx is disabled, reset the FIFO pointer and clear error flags */
887     if ((base->RCSR & I2S_RCSR_RE_MASK) == 0UL)
888     {
889         base->RCSR |= (I2S_RCSR_FR_MASK | I2S_RCSR_SR_MASK);
890         base->RCSR &= ~I2S_RCSR_SR_MASK;
891     }
892 
893     /* Handle the queue index */
894     (void)memset(&handle->saiQueue[handle->queueDriver], 0, sizeof(sai_transfer_t));
895     handle->queueDriver = (handle->queueDriver + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE;
896 
897     /* Set the handle state */
898     handle->state = (uint32_t)kSAI_Idle;
899 }
900 
901 /*!
902  * brief Terminate all SAI send.
903  *
904  * This function will clear all transfer slots buffered in the sai queue. If users only want to abort the
905  * current transfer slot, please call SAI_TransferAbortSendEDMA.
906  *
907  * param base SAI base pointer.
908  * param handle SAI eDMA handle pointer.
909  */
SAI_TransferTerminateSendEDMA(I2S_Type * base,sai_edma_handle_t * handle)910 void SAI_TransferTerminateSendEDMA(I2S_Type *base, sai_edma_handle_t *handle)
911 {
912     assert(handle != NULL);
913 
914     /* Abort the current transfer */
915     SAI_TransferAbortSendEDMA(base, handle);
916 
917     /* Clear all the internal information */
918     (void)memset(handle->tcd, 0, sizeof(handle->tcd));
919     (void)memset(handle->saiQueue, 0, sizeof(handle->saiQueue));
920     (void)memset(handle->transferSize, 0, sizeof(handle->transferSize));
921 
922     handle->queueUser   = 0U;
923     handle->queueDriver = 0U;
924 }
925 
926 /*!
927  * brief Terminate all SAI receive.
928  *
929  * This function will clear all transfer slots buffered in the sai queue. If users only want to abort the
930  * current transfer slot, please call SAI_TransferAbortReceiveEDMA.
931  *
932  * param base SAI base pointer.
933  * param handle SAI eDMA handle pointer.
934  */
SAI_TransferTerminateReceiveEDMA(I2S_Type * base,sai_edma_handle_t * handle)935 void SAI_TransferTerminateReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle)
936 {
937     assert(handle != NULL);
938 
939     /* Abort the current transfer */
940     SAI_TransferAbortReceiveEDMA(base, handle);
941 
942     /* Clear all the internal information */
943     (void)memset(handle->tcd, 0, sizeof(handle->tcd));
944     (void)memset(handle->saiQueue, 0, sizeof(handle->saiQueue));
945     (void)memset(handle->transferSize, 0, sizeof(handle->transferSize));
946 
947     handle->queueUser   = 0U;
948     handle->queueDriver = 0U;
949 }
950 
951 /*!
952  * brief Gets byte count sent by SAI.
953  *
954  * param base SAI base pointer.
955  * param handle SAI eDMA handle pointer.
956  * param count Bytes count sent by SAI.
957  * retval kStatus_Success Succeed get the transfer count.
958  * retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress.
959  */
SAI_TransferGetSendCountEDMA(I2S_Type * base,sai_edma_handle_t * handle,size_t * count)960 status_t SAI_TransferGetSendCountEDMA(I2S_Type *base, sai_edma_handle_t *handle, size_t *count)
961 {
962     assert(handle != NULL);
963 
964     status_t status = kStatus_Success;
965 
966     if (handle->state != (uint32_t)kSAI_Busy)
967     {
968         status = kStatus_NoTransferInProgress;
969     }
970     else
971     {
972         *count = (handle->transferSize[handle->queueDriver] -
973                   (uint32_t)handle->nbytes *
974                       EDMA_GetRemainingMajorLoopCount(handle->dmaHandle->base, handle->dmaHandle->channel));
975     }
976 
977     return status;
978 }
979 
980 /*!
981  * brief Gets byte count received by SAI.
982  *
983  * param base SAI base pointer
984  * param handle SAI eDMA handle pointer.
985  * param count Bytes count received by SAI.
986  * retval kStatus_Success Succeed get the transfer count.
987  * retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress.
988  */
SAI_TransferGetReceiveCountEDMA(I2S_Type * base,sai_edma_handle_t * handle,size_t * count)989 status_t SAI_TransferGetReceiveCountEDMA(I2S_Type *base, sai_edma_handle_t *handle, size_t *count)
990 {
991     assert(handle != NULL);
992 
993     status_t status = kStatus_Success;
994 
995     if (handle->state != (uint32_t)kSAI_Busy)
996     {
997         status = kStatus_NoTransferInProgress;
998     }
999     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  */
SAI_TransferGetValidTransferSlotsEDMA(I2S_Type * base,sai_edma_handle_t * handle)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 }
1034