1 /*
2  * Copyright 2017 - 2021 NXP
3  * All rights reserved.
4  *
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 #include "fsl_sai_sdma.h"
10 
11 /* Component ID definition, used by tools. */
12 #ifndef FSL_COMPONENT_ID
13 #define FSL_COMPONENT_ID "platform.drivers.sai_sdma"
14 #endif
15 
16 /*******************************************************************************
17  * Definitations
18  ******************************************************************************/
19 /*<! Structure definition for uart_sdma_private_handle_t. The structure is private. */
20 typedef struct _sai_sdma_private_handle
21 {
22     I2S_Type *base;
23     sai_sdma_handle_t *handle;
24 } sai_sdma_private_handle_t;
25 
26 /*!@brief _sai_sdma_transfer_state */
27 enum
28 {
29     kSAI_Busy = 0x0U, /*!< SAI is busy */
30     kSAI_Idle,        /*!< Transfer is done. */
31 };
32 
33 static I2S_Type *const s_saiBases[] = I2S_BASE_PTRS;
34 
35 /*<! Private handle only used for internally. */
36 static sai_sdma_private_handle_t s_sdmaPrivateHandle[ARRAY_SIZE(s_saiBases)][2];
37 
38 /*******************************************************************************
39  * Prototypes
40  ******************************************************************************/
41 /*!
42  * @brief Get the instance number for SAI.
43  *
44  * @param base SAI base pointer.
45  */
46 static uint32_t SAI_GetInstance(I2S_Type *base);
47 
48 /*!
49  * @brief SAI SDMA callback for send.
50  *
51  * @param handle pointer to sai_sdma_handle_t structure which stores the transfer state.
52  * @param userData Parameter for user callback.
53  * @param done If the DMA transfer finished.
54  * @param tcds The TCD index.
55  */
56 static void SAI_TxSDMACallback(sdma_handle_t *handle, void *userData, bool transferDone, uint32_t bdIndex);
57 
58 /*!
59  * @brief SAI SDMA callback for receive.
60  *
61  * @param handle pointer to sai_sdma_handle_t structure which stores the transfer state.
62  * @param userData Parameter for user callback.
63  * @param done If the DMA transfer finished.
64  * @param tcds The TCD index.
65  */
66 static void SAI_RxSDMACallback(sdma_handle_t *handle, void *userData, bool transferDone, uint32_t bdIndex);
67 
68 /*******************************************************************************
69  * Code
70  ******************************************************************************/
SAI_GetInstance(I2S_Type * base)71 static uint32_t SAI_GetInstance(I2S_Type *base)
72 {
73     uint32_t instance;
74 
75     /* Find the instance index from base address mappings. */
76     for (instance = 0; instance < ARRAY_SIZE(s_saiBases); instance++)
77     {
78         if (s_saiBases[instance] == base)
79         {
80             break;
81         }
82     }
83 
84     assert(instance < ARRAY_SIZE(s_saiBases));
85 
86     return instance;
87 }
88 
SAI_TxSDMACallback(sdma_handle_t * handle,void * userData,bool transferDone,uint32_t bdIndex)89 static void SAI_TxSDMACallback(sdma_handle_t *handle, void *userData, bool transferDone, uint32_t bdIndex)
90 {
91     sai_sdma_private_handle_t *privHandle = (sai_sdma_private_handle_t *)userData;
92     sai_sdma_handle_t *saiHandle          = privHandle->handle;
93 
94     /* If finished a block, call the callback function */
95     (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0, sizeof(sai_transfer_t));
96     saiHandle->queueDriver = (saiHandle->queueDriver + 1U) % SAI_XFER_QUEUE_SIZE;
97     /* Stop SDMA transfer */
98     SDMA_StopChannel(handle->base, handle->channel);
99     if (saiHandle->callback != NULL)
100     {
101         (saiHandle->callback)(privHandle->base, saiHandle, kStatus_SAI_TxIdle, saiHandle->userData);
102     }
103 
104     /* If all data finished, just stop the transfer */
105     if (saiHandle->saiQueue[saiHandle->queueDriver].data == NULL)
106     {
107         /* Disable dma */
108         SDMA_AbortTransfer(handle);
109         /* Disable DMA enable bit */
110         SAI_TxEnableDMA(privHandle->base, kSAI_FIFORequestDMAEnable, false);
111         /* Set the handle state */
112         saiHandle->state = (uint32_t)kSAI_Idle;
113     }
114 }
115 
SAI_RxSDMACallback(sdma_handle_t * handle,void * userData,bool transferDone,uint32_t bdIndex)116 static void SAI_RxSDMACallback(sdma_handle_t *handle, void *userData, bool transferDone, uint32_t bdIndex)
117 {
118     sai_sdma_private_handle_t *privHandle = (sai_sdma_private_handle_t *)userData;
119     sai_sdma_handle_t *saiHandle          = privHandle->handle;
120 
121     /* If finished a block, call the callback function */
122     (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0, sizeof(sai_transfer_t));
123     saiHandle->queueDriver = (saiHandle->queueDriver + 1U) % SAI_XFER_QUEUE_SIZE;
124     if (saiHandle->callback != NULL)
125     {
126         (saiHandle->callback)(privHandle->base, saiHandle, kStatus_SAI_RxIdle, saiHandle->userData);
127     }
128 
129     /* If all data finished, just stop the transfer */
130     if (saiHandle->saiQueue[saiHandle->queueDriver].data == NULL)
131     {
132         /* Disable dma */
133         SDMA_AbortTransfer(handle);
134         /* Disable DMA enable bit */
135         SAI_RxEnableDMA(privHandle->base, kSAI_FIFORequestDMAEnable, false);
136         /* Set the handle state */
137         saiHandle->state = (uint32_t)kSAI_Idle;
138     }
139 }
140 
141 /*!
142  * brief Initializes the SAI SDMA handle.
143  *
144  * This function initializes the SAI master DMA handle, which can be used for other SAI master transactional APIs.
145  * Usually, for a specified SAI instance, call this API once to get the initialized handle.
146  *
147  * param base SAI base pointer.
148  * param handle SAI SDMA handle pointer.
149  * param base SAI peripheral base address.
150  * param callback Pointer to user callback function.
151  * param userData User parameter passed to the callback function.
152  * param dmaHandle SDMA handle pointer, this handle shall be static allocated by users.
153  */
SAI_TransferTxCreateHandleSDMA(I2S_Type * base,sai_sdma_handle_t * handle,sai_sdma_callback_t callback,void * userData,sdma_handle_t * dmaHandle,uint32_t eventSource)154 void SAI_TransferTxCreateHandleSDMA(I2S_Type *base,
155                                     sai_sdma_handle_t *handle,
156                                     sai_sdma_callback_t callback,
157                                     void *userData,
158                                     sdma_handle_t *dmaHandle,
159                                     uint32_t eventSource)
160 {
161     assert((handle != NULL) && (dmaHandle != NULL));
162 
163     uint32_t instance = SAI_GetInstance(base);
164 
165     /* Zero the handle */
166     (void)memset(handle, 0, sizeof(*handle));
167 
168     /* Set sai base to handle */
169     handle->dmaHandle   = dmaHandle;
170     handle->callback    = callback;
171     handle->userData    = userData;
172     handle->eventSource = eventSource;
173 
174     /* Set SAI state to idle */
175     handle->state = (uint32_t)kSAI_Idle;
176 
177     s_sdmaPrivateHandle[instance][0].base   = base;
178     s_sdmaPrivateHandle[instance][0].handle = handle;
179 
180     SDMA_InstallBDMemory(dmaHandle, handle->bdPool, SAI_XFER_QUEUE_SIZE);
181 
182     /* Install callback for Tx dma channel */
183     SDMA_SetCallback(dmaHandle, SAI_TxSDMACallback, &s_sdmaPrivateHandle[instance][0]);
184 }
185 
186 /*!
187  * brief Initializes the SAI Rx SDMA handle.
188  *
189  * This function initializes the SAI slave DMA handle, which can be used for other SAI master transactional APIs.
190  * Usually, for a specified SAI instance, call this API once to get the initialized handle.
191  *
192  * param base SAI base pointer.
193  * param handle SAI SDMA handle pointer.
194  * param base SAI peripheral base address.
195  * param callback Pointer to user callback function.
196  * param userData User parameter passed to the callback function.
197  * param dmaHandle SDMA handle pointer, this handle shall be static allocated by users.
198  */
SAI_TransferRxCreateHandleSDMA(I2S_Type * base,sai_sdma_handle_t * handle,sai_sdma_callback_t callback,void * userData,sdma_handle_t * dmaHandle,uint32_t eventSource)199 void SAI_TransferRxCreateHandleSDMA(I2S_Type *base,
200                                     sai_sdma_handle_t *handle,
201                                     sai_sdma_callback_t callback,
202                                     void *userData,
203                                     sdma_handle_t *dmaHandle,
204                                     uint32_t eventSource)
205 {
206     assert((handle != NULL) && (dmaHandle != NULL));
207 
208     uint32_t instance = SAI_GetInstance(base);
209 
210     /* Zero the handle */
211     (void)memset(handle, 0, sizeof(*handle));
212 
213     /* Set sai base to handle */
214     handle->dmaHandle   = dmaHandle;
215     handle->callback    = callback;
216     handle->userData    = userData;
217     handle->eventSource = eventSource;
218 
219     /* Set SAI state to idle */
220     handle->state = (uint32_t)kSAI_Idle;
221 
222     s_sdmaPrivateHandle[instance][1].base   = base;
223     s_sdmaPrivateHandle[instance][1].handle = handle;
224 
225     SDMA_InstallBDMemory(dmaHandle, handle->bdPool, SAI_XFER_QUEUE_SIZE);
226 
227     /* Install callback for Tx dma channel */
228     SDMA_SetCallback(dmaHandle, SAI_RxSDMACallback, &s_sdmaPrivateHandle[instance][1]);
229 }
230 
231 /*!
232  * brief Configures the SAI Tx audio format.
233  *
234  * The audio format can be changed at run-time. This function configures the sample rate and audio data
235  * format to be transferred. This function also sets the SDMA parameter according to formatting requirements.
236  *
237  * param base SAI base pointer.
238  * param handle SAI SDMA handle pointer.
239  * param format Pointer to SAI audio data format structure.
240  * param mclkSourceClockHz SAI master clock source frequency in Hz.
241  * param bclkSourceClockHz SAI bit clock source frequency in Hz. If bit clock source is master
242  * clock, this value should equals to masterClockHz in format.
243  * retval kStatus_Success Audio format set successfully.
244  * retval kStatus_InvalidArgument The input argument is invalid.
245  */
SAI_TransferTxSetFormatSDMA(I2S_Type * base,sai_sdma_handle_t * handle,sai_transfer_format_t * format,uint32_t mclkSourceClockHz,uint32_t bclkSourceClockHz)246 void SAI_TransferTxSetFormatSDMA(I2S_Type *base,
247                                  sai_sdma_handle_t *handle,
248                                  sai_transfer_format_t *format,
249                                  uint32_t mclkSourceClockHz,
250                                  uint32_t bclkSourceClockHz)
251 {
252     assert((handle != NULL) && (format != NULL));
253 
254     /* Configure the audio format to SAI registers */
255     SAI_TxSetFormat(base, format, mclkSourceClockHz, bclkSourceClockHz);
256 
257     /* Get the transfer size from format, this should be used in SDMA configuration */
258     if (format->bitWidth == 24U)
259     {
260         handle->bytesPerFrame = 4U;
261     }
262     else
263     {
264         handle->bytesPerFrame = (uint8_t)(format->bitWidth / 8U);
265     }
266 
267     /* Update the data channel SAI used */
268     handle->channel = format->channel;
269 
270     if (format->channelNums == 0U)
271     {
272         format->channelNums = 1U;
273     }
274     handle->channelNums = format->channelNums;
275     handle->channelMask = format->channelMask;
276     if (format->channelNums > 1U)
277     {
278         /* fifo address offset, 4U is the address offset between each fifo */
279         handle->fifoOffset = ((format->endChannel - format->channel) * 4U) / (format->channelNums - 1U);
280     }
281     else
282     {
283         handle->fifoOffset = 0U;
284     }
285 
286 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
287     handle->count =
288         ((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base) - (uint32_t)format->watermark) * (uint32_t)format->channelNums;
289 #else
290     handle->count = 1U * format->channelNums;
291 #endif /* FSL_FEATURE_SAI_HAS_FIFO */
292 
293     /* Clear the channel enable bits until do a send/receive */
294     base->TCR3 &= ~I2S_TCR3_TCE_MASK;
295 }
296 
297 /*!
298  * brief Configures the SAI Tx audio.
299  *
300  * param base SAI base pointer.
301  * param handle SAI SDMA handle pointer.
302  * param saiConfig sai configurations
303  */
SAI_TransferTxSetConfigSDMA(I2S_Type * base,sai_sdma_handle_t * handle,sai_transceiver_t * saiConfig)304 void SAI_TransferTxSetConfigSDMA(I2S_Type *base, sai_sdma_handle_t *handle, sai_transceiver_t *saiConfig)
305 {
306     assert((handle != NULL) && (saiConfig != NULL));
307 
308     /* Configure the audio format to SAI registers */
309     SAI_TxSetConfig(base, saiConfig);
310 
311     handle->bytesPerFrame = saiConfig->serialData.dataWordLength / 8U;
312 
313     /* Update the data channel SAI used */
314     handle->channel = saiConfig->startChannel;
315 
316     if (saiConfig->channelNums == 0U)
317     {
318         saiConfig->channelNums = 1U;
319     }
320     handle->channelNums = saiConfig->channelNums;
321     handle->channelMask = saiConfig->channelMask;
322     if (saiConfig->channelNums > 1U)
323     {
324         /* fifo address offset, 4U is the address offset between each fifo */
325         handle->fifoOffset = ((saiConfig->endChannel - saiConfig->startChannel) * 4U) / (saiConfig->channelNums - 1U);
326     }
327     else
328     {
329         handle->fifoOffset = 0U;
330     }
331 
332 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
333     handle->count = ((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base) - (uint32_t)saiConfig->fifo.fifoWatermark) *
334                     (uint32_t)saiConfig->channelNums;
335 #else
336     handle->count = 1U * saiConfig->channelNums;
337 #endif /* FSL_FEATURE_SAI_HAS_FIFO */
338 
339     /* Clear the channel enable bits until do a send/receive */
340     base->TCR3 &= ~I2S_TCR3_TCE_MASK;
341 }
342 
343 /*!
344  * brief Configures the SAI Rx audio format.
345  *
346  * The audio format can be changed at run-time. This function configures the sample rate and audio data
347  * format to be transferred. This function also sets the SDMA parameter according to formatting requirements.
348  *
349  * param base SAI base pointer.
350  * param handle SAI SDMA handle pointer.
351  * param format Pointer to SAI audio data format structure.
352  * param mclkSourceClockHz SAI master clock source frequency in Hz.
353  * param bclkSourceClockHz SAI bit clock source frequency in Hz. If a bit clock source is the master
354  * clock, this value should equal to masterClockHz in format.
355  * retval kStatus_Success Audio format set successfully.
356  * retval kStatus_InvalidArgument The input argument is invalid.
357  */
SAI_TransferRxSetFormatSDMA(I2S_Type * base,sai_sdma_handle_t * handle,sai_transfer_format_t * format,uint32_t mclkSourceClockHz,uint32_t bclkSourceClockHz)358 void SAI_TransferRxSetFormatSDMA(I2S_Type *base,
359                                  sai_sdma_handle_t *handle,
360                                  sai_transfer_format_t *format,
361                                  uint32_t mclkSourceClockHz,
362                                  uint32_t bclkSourceClockHz)
363 {
364     assert((handle != NULL) && (format != NULL));
365 
366     /* Configure the audio format to SAI registers */
367     SAI_RxSetFormat(base, format, mclkSourceClockHz, bclkSourceClockHz);
368 
369     /* Get the transfer size from format, this should be used in SDMA configuration */
370     if (format->bitWidth == 24U)
371     {
372         handle->bytesPerFrame = 4U;
373     }
374     else
375     {
376         handle->bytesPerFrame = (uint8_t)(format->bitWidth / 8U);
377     }
378 
379     /* configurations for multififo */
380     if (format->channelNums == 0U)
381     {
382         format->channelNums = 1U;
383     }
384 
385     handle->channelNums = format->channelNums;
386     handle->channelMask = format->channelMask;
387 
388     if (format->channelNums > 1U)
389     {
390         /* fifo address offset, 4U is the address offset between each fifo */
391         handle->fifoOffset = ((format->endChannel - format->channel) * 4U) / (format->channelNums - 1U);
392     }
393     else
394     {
395         handle->fifoOffset = 0U;
396     }
397     /* Update the data channel SAI used */
398     handle->channel = format->channel;
399 
400 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
401     handle->count = (uint32_t)format->watermark * (uint32_t)format->channelNums;
402 #else
403     handle->count = 1U * format->channelNums;
404 #endif /* FSL_FEATURE_SAI_HAS_FIFO */
405 
406     /* Clear the channel enable bits until do a send/receive */
407     base->RCR3 &= ~I2S_RCR3_RCE_MASK;
408 }
409 
410 /*!
411  * brief Configures the SAI Rx audio.
412  *
413  * param base SAI base pointer.
414  * param handle SAI SDMA handle pointer.
415  * param saiConig sai configurations.
416  */
SAI_TransferRxSetConfigSDMA(I2S_Type * base,sai_sdma_handle_t * handle,sai_transceiver_t * saiConfig)417 void SAI_TransferRxSetConfigSDMA(I2S_Type *base, sai_sdma_handle_t *handle, sai_transceiver_t *saiConfig)
418 {
419     assert((handle != NULL) && (saiConfig != NULL));
420 
421     /* Configure the audio format to SAI registers */
422     SAI_RxSetConfig(base, saiConfig);
423 
424     handle->bytesPerFrame = saiConfig->serialData.dataWordLength / 8U;
425 
426     /* configurations for multififo */
427     if (saiConfig->channelNums == 0U)
428     {
429         saiConfig->channelNums = 1U;
430     }
431 
432     handle->channelNums = saiConfig->channelNums;
433     handle->channelMask = saiConfig->channelMask;
434 
435     if (saiConfig->channelNums > 1U)
436     {
437         /* fifo address offset, 4U is the address offset between each fifo */
438         handle->fifoOffset = ((saiConfig->endChannel - saiConfig->startChannel) * 4U) / (saiConfig->channelNums - 1U);
439     }
440     else
441     {
442         handle->fifoOffset = 0U;
443     }
444     /* Update the data channel SAI used */
445     handle->channel = saiConfig->startChannel;
446 
447 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
448     handle->count = (uint32_t)saiConfig->fifo.fifoWatermark * (uint32_t)saiConfig->channelNums;
449 #else
450     handle->count = 1U * saiConfig->channelNums;
451 #endif /* FSL_FEATURE_SAI_HAS_FIFO */
452 
453     /* Clear the channel enable bits until do a send/receive */
454     base->RCR3 &= ~I2S_RCR3_RCE_MASK;
455 }
456 
457 /*!
458  * brief Performs a non-blocking SAI transfer using DMA.
459  *
460  * note This interface returns immediately after the transfer initiates. Call
461  * SAI_GetTransferStatus to poll the transfer status and check whether the SAI transfer is finished.
462  *
463  * param base SAI base pointer.
464  * param handle SAI SDMA handle pointer.
465  * param xfer Pointer to the DMA transfer structure.
466  * retval kStatus_Success Start a SAI SDMA send successfully.
467  * retval kStatus_InvalidArgument The input argument is invalid.
468  * retval kStatus_TxBusy SAI is busy sending data.
469  */
SAI_TransferSendSDMA(I2S_Type * base,sai_sdma_handle_t * handle,sai_transfer_t * xfer)470 status_t SAI_TransferSendSDMA(I2S_Type *base, sai_sdma_handle_t *handle, sai_transfer_t *xfer)
471 {
472     assert((handle != NULL) && (xfer != NULL));
473     assert((xfer->dataSize % (handle->bytesPerFrame)) == 0U);
474 
475     sdma_transfer_config_t config = {0};
476     uint32_t destAddr             = SAI_TxGetDataRegisterAddress(base, handle->channel);
477     sdma_handle_t *dmaHandle      = handle->dmaHandle;
478     sdma_peripheral_t perType     = kSDMA_PeripheralNormal;
479 
480     /* Check if input parameter invalid */
481     if ((xfer->data == NULL) || (xfer->dataSize == 0U) || ((handle->channelNums > 1U) && (handle->fifoOffset == 0U)) ||
482         ((handle->channelNums > 1U) &&
483          ((uint16_t)handle->count * handle->bytesPerFrame > (uint16_t)kSDMA_MultiFifoWatermarkLevelMask)) ||
484         ((xfer->dataSize % (handle->bytesPerFrame)) != 0U))
485     {
486         return kStatus_InvalidArgument;
487     }
488 
489     if (handle->saiQueue[handle->queueUser].data != NULL)
490     {
491         return kStatus_SAI_QueueFull;
492     }
493 
494     /* Change the state of handle */
495     handle->transferSize[handle->queueUser]      = xfer->dataSize;
496     handle->saiQueue[handle->queueUser].data     = xfer->data;
497     handle->saiQueue[handle->queueUser].dataSize = xfer->dataSize;
498 
499 #if defined(FSL_FEATURE_SOC_SPBA_COUNT) && (FSL_FEATURE_SOC_SPBA_COUNT > 0)
500     bool isSpba = SDMA_IsPeripheralInSPBA((uint32_t)base);
501     /* Judge if the instance is located in SPBA */
502     if (isSpba)
503     {
504         perType = kSDMA_PeripheralNormal_SP;
505     }
506 #endif /* FSL_FEATURE_SOC_SPBA_COUNT */
507 
508     /* if channel numbers > 1U, should enable multififo */
509     if (handle->channelNums > 1U)
510     {
511         perType = kSDMA_PeripheralMultiFifoSaiTX;
512         /* multi fifo configurations */
513         SDMA_SetMultiFifoConfig(&config, handle->channelNums, (uint32_t)handle->fifoOffset / sizeof(uint32_t) - 1UL);
514     }
515 
516     /* Prepare sdma configure */
517     SDMA_PrepareTransfer(&config, (uint32_t)xfer->data, destAddr, handle->bytesPerFrame, handle->bytesPerFrame,
518                          (uint32_t)handle->count * handle->bytesPerFrame, xfer->dataSize, handle->eventSource, perType,
519                          kSDMA_MemoryToPeripheral);
520 
521     if (handle->queueUser == SAI_XFER_QUEUE_SIZE - 1U)
522     {
523         SDMA_ConfigBufferDescriptor(&dmaHandle->BDPool[handle->queueUser], (uint32_t)(xfer->data), destAddr,
524                                     config.destTransferSize, xfer->dataSize, true, true, true,
525                                     kSDMA_MemoryToPeripheral);
526     }
527     else
528     {
529         SDMA_ConfigBufferDescriptor(&dmaHandle->BDPool[handle->queueUser], (uint32_t)(xfer->data), destAddr,
530                                     config.destTransferSize, xfer->dataSize, true, true, false,
531                                     kSDMA_MemoryToPeripheral);
532     }
533 
534     handle->queueUser = (handle->queueUser + 1U) % SAI_XFER_QUEUE_SIZE;
535 
536     if (handle->state != (uint32_t)kSAI_Busy)
537     {
538         SDMA_SubmitTransfer(handle->dmaHandle, &config);
539 
540         /* Start DMA transfer */
541         SDMA_StartTransfer(handle->dmaHandle);
542 
543         /* Enable DMA enable bit */
544         SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
545 
546         /* Enable SAI Tx clock */
547         SAI_TxEnable(base, true);
548 
549         /* Enable the channel FIFO */
550         base->TCR3 |= I2S_TCR3_TCE(handle->channelMask);
551     }
552     handle->state = (uint32_t)kSAI_Busy;
553 
554     return kStatus_Success;
555 }
556 
557 /*!
558  * brief Performs a non-blocking SAI receive using SDMA.
559  *
560  * note This interface returns immediately after the transfer initiates. Call
561  * the SAI_GetReceiveRemainingBytes to poll the transfer status and check whether the SAI transfer is finished.
562  *
563  * param base SAI base pointer
564  * param handle SAI SDMA handle pointer.
565  * param xfer Pointer to DMA transfer structure.
566  * retval kStatus_Success Start a SAI SDMA receive successfully.
567  * retval kStatus_InvalidArgument The input argument is invalid.
568  * retval kStatus_RxBusy SAI is busy receiving data.
569  */
SAI_TransferReceiveSDMA(I2S_Type * base,sai_sdma_handle_t * handle,sai_transfer_t * xfer)570 status_t SAI_TransferReceiveSDMA(I2S_Type *base, sai_sdma_handle_t *handle, sai_transfer_t *xfer)
571 {
572     assert((handle != NULL) && (xfer != NULL));
573     assert((xfer->dataSize % (handle->bytesPerFrame)) == 0U);
574 
575     sdma_transfer_config_t config = {0};
576     sdma_handle_t *dmaHandle      = handle->dmaHandle;
577     uint32_t srcAddr              = SAI_RxGetDataRegisterAddress(base, handle->channel);
578     sdma_peripheral_t perType     = kSDMA_PeripheralNormal;
579 
580     /* Check if input parameter invalid */
581     if ((xfer->data == NULL) || (xfer->dataSize == 0U) || ((handle->channelNums > 1U) && (handle->fifoOffset == 0U)) ||
582         ((handle->channelNums > 1U) &&
583          ((uint16_t)handle->count * handle->bytesPerFrame > (uint16_t)kSDMA_MultiFifoWatermarkLevelMask)) ||
584         ((xfer->dataSize % (handle->bytesPerFrame)) != 0U))
585     {
586         return kStatus_InvalidArgument;
587     }
588 
589     if (handle->saiQueue[handle->queueUser].data != NULL)
590     {
591         return kStatus_SAI_QueueFull;
592     }
593 
594     /* Update queue state  */
595     handle->transferSize[handle->queueUser]      = xfer->dataSize;
596     handle->saiQueue[handle->queueUser].data     = xfer->data;
597     handle->saiQueue[handle->queueUser].dataSize = xfer->dataSize;
598 
599 #if defined(FSL_FEATURE_SOC_SPBA_COUNT) && (FSL_FEATURE_SOC_SPBA_COUNT > 0)
600     bool isSpba = SDMA_IsPeripheralInSPBA((uint32_t)base);
601     /* Judge if the instance is located in SPBA */
602     if (isSpba)
603     {
604         perType = kSDMA_PeripheralNormal_SP;
605     }
606 #endif /* FSL_FEATURE_SOC_SPBA_COUNT */
607 
608     /* if channel numbers > 1U, should enable multififo */
609     if (handle->channelNums > 1U)
610     {
611         perType = kSDMA_PeripheralMultiFifoSaiRX;
612         /* multi fifo configurations */
613         SDMA_SetMultiFifoConfig(&config, handle->channelNums, (uint32_t)handle->fifoOffset / sizeof(uint32_t) - 1UL);
614     }
615 
616     /* Prepare sdma configure */
617     SDMA_PrepareTransfer(&config, srcAddr, (uint32_t)xfer->data, handle->bytesPerFrame, handle->bytesPerFrame,
618                          (uint32_t)handle->count * handle->bytesPerFrame, xfer->dataSize, handle->eventSource, perType,
619                          kSDMA_PeripheralToMemory);
620 
621     if (handle->queueUser == SAI_XFER_QUEUE_SIZE - 1U)
622     {
623         SDMA_ConfigBufferDescriptor(&dmaHandle->BDPool[handle->queueUser], srcAddr, (uint32_t)xfer->data,
624                                     config.destTransferSize, xfer->dataSize, true, true, true,
625                                     kSDMA_PeripheralToMemory);
626     }
627     else
628     {
629         SDMA_ConfigBufferDescriptor(&dmaHandle->BDPool[handle->queueUser], srcAddr, (uint32_t)xfer->data,
630                                     config.destTransferSize, xfer->dataSize, true, true, false,
631                                     kSDMA_PeripheralToMemory);
632     }
633 
634     handle->queueUser = (handle->queueUser + 1U) % SAI_XFER_QUEUE_SIZE;
635 
636     if (handle->state != (uint32_t)kSAI_Busy)
637     {
638         SDMA_SubmitTransfer(handle->dmaHandle, &config);
639 
640         /* Start DMA transfer */
641         SDMA_StartTransfer(handle->dmaHandle);
642 
643         /* Enable DMA enable bit */
644         SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
645 
646         /* Enable SAI Rx clock */
647         SAI_RxEnable(base, true);
648 
649         /* Enable the channel FIFO */
650         base->RCR3 |= I2S_RCR3_RCE(handle->channelMask);
651     }
652 
653     handle->state = (uint32_t)kSAI_Busy;
654 
655     return kStatus_Success;
656 }
657 
658 /*!
659  * brief Aborts a SAI transfer using SDMA.
660  *
661  * param base SAI base pointer.
662  * param handle SAI SDMA handle pointer.
663  */
SAI_TransferAbortSendSDMA(I2S_Type * base,sai_sdma_handle_t * handle)664 void SAI_TransferAbortSendSDMA(I2S_Type *base, sai_sdma_handle_t *handle)
665 {
666     assert(handle != NULL);
667 
668     /* Disable dma */
669     SDMA_AbortTransfer(handle->dmaHandle);
670 
671     /* Disable the channel FIFO */
672     base->TCR3 &= ~I2S_TCR3_TCE_MASK;
673 
674     /* Disable DMA enable bit */
675     SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, false);
676 
677     /* Reset the FIFO pointer, at the same time clear all error flags if set */
678     base->TCSR |= (I2S_TCSR_FR_MASK | I2S_TCSR_SR_MASK);
679     base->TCSR &= ~I2S_TCSR_SR_MASK;
680 
681     /* Disable Tx */
682     SAI_TxEnable(base, false);
683 
684     /* Handle the queue index */
685     (void)memset(&handle->saiQueue[handle->queueDriver], 0, sizeof(sai_transfer_t));
686     handle->queueDriver = (handle->queueDriver + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE;
687 
688     /* Set the handle state */
689     handle->state = (uint32_t)kSAI_Idle;
690 }
691 
692 /*!
693  * brief Terminate all the SAI sdma send transfer.
694  *
695  * param base SAI base pointer.
696  * param handle SAI SDMA handle pointer.
697  */
SAI_TransferTerminateSendSDMA(I2S_Type * base,sai_sdma_handle_t * handle)698 void SAI_TransferTerminateSendSDMA(I2S_Type *base, sai_sdma_handle_t *handle)
699 {
700     assert(handle != NULL);
701 
702     /* abort current transfer */
703     SAI_TransferAbortSendSDMA(base, handle);
704 
705     /* Clear all the internal information */
706     (void)memset(handle->bdPool, 0, sizeof(handle->bdPool));
707     (void)memset(handle->saiQueue, 0, sizeof(handle->saiQueue));
708     (void)memset(handle->transferSize, 0, sizeof(handle->transferSize));
709 
710     handle->queueUser   = 0U;
711     handle->queueDriver = 0U;
712 
713     /* Reset the internal state of bd pool */
714     SDMA_InstallBDMemory(handle->dmaHandle, handle->bdPool, handle->dmaHandle->bdCount);
715 }
716 
717 /*!
718  * brief Aborts a SAI receive using SDMA.
719  *
720  * param base SAI base pointer
721  * param handle SAI SDMA handle pointer.
722  */
SAI_TransferAbortReceiveSDMA(I2S_Type * base,sai_sdma_handle_t * handle)723 void SAI_TransferAbortReceiveSDMA(I2S_Type *base, sai_sdma_handle_t *handle)
724 {
725     assert(handle != NULL);
726 
727     /* Disable dma */
728     SDMA_AbortTransfer(handle->dmaHandle);
729 
730     /* Disable the channel FIFO */
731     base->RCR3 &= ~I2S_RCR3_RCE_MASK;
732 
733     /* Disable DMA enable bit */
734     SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, false);
735 
736     /* Disable Rx */
737     SAI_RxEnable(base, false);
738 
739     /* Reset the FIFO pointer, at the same time clear all error flags if set */
740     base->RCSR |= (I2S_RCSR_FR_MASK | I2S_RCSR_SR_MASK);
741     base->RCSR &= ~I2S_RCSR_SR_MASK;
742 
743     /* Handle the queue index */
744     (void)memset(&handle->saiQueue[handle->queueDriver], 0, sizeof(sai_transfer_t));
745     handle->queueDriver = (handle->queueDriver + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE;
746 
747     /* Set the handle state */
748     handle->state = (uint32_t)kSAI_Idle;
749 }
750 
751 /*!
752  * brief Terminate all the SAI sdma receive transfer.
753  *
754  * param base SAI base pointer.
755  * param handle SAI SDMA handle pointer.
756  */
SAI_TransferTerminateReceiveSDMA(I2S_Type * base,sai_sdma_handle_t * handle)757 void SAI_TransferTerminateReceiveSDMA(I2S_Type *base, sai_sdma_handle_t *handle)
758 {
759     assert(handle != NULL);
760 
761     /* abort current transfer */
762     SAI_TransferAbortReceiveSDMA(base, handle);
763 
764     /* Clear all the internal information */
765     (void)memset(handle->bdPool, 0, sizeof(handle->bdPool));
766     (void)memset(handle->saiQueue, 0, sizeof(handle->saiQueue));
767     (void)memset(handle->transferSize, 0, sizeof(handle->transferSize));
768 
769     handle->queueUser   = 0U;
770     handle->queueDriver = 0U;
771 
772     /* Reset the internal state of bd pool */
773     SDMA_InstallBDMemory(handle->dmaHandle, handle->bdPool, handle->dmaHandle->bdCount);
774 }
775