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