1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016-2017,2024 NXP
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 #include "fsl_sai_dma.h"
10 
11 /*******************************************************************************
12  * Definitions
13  ******************************************************************************/
14 
15 /* Component ID definition, used by tools. */
16 #ifndef FSL_COMPONENT_ID
17 #define FSL_COMPONENT_ID "platform.drivers.sai_dma"
18 #endif
19 
20 /*<! Structure definition for sai_dma_private_handle_t. The structure is private. */
21 typedef struct _sai_dma_private_handle
22 {
23     I2S_Type *base;
24     sai_dma_handle_t *handle;
25 } sai_dma_private_handle_t;
26 
27 /*!@brief _sai_dma_states */
28 enum
29 {
30     kSAI_Idle = 0x0U,
31     kSAI_Busy = 0x1U,
32 };
33 
34 static I2S_Type *const s_saiBases[] = I2S_BASE_PTRS;
35 
36 /*<! Private handle only used for internally. */
37 static sai_dma_private_handle_t s_dmaPrivateHandle[ARRAY_SIZE(s_saiBases)][2];
38 
39 /*******************************************************************************
40  * Prototypes
41  ******************************************************************************/
42 /*!
43  * @brief Get the instance number for SAI.
44  *
45  * @param base SAI base pointer.
46  */
47 static uint32_t SAI_GetInstance(I2S_Type *base);
48 
49 /*!
50  * @brief SAI EDMA callback for send.
51  *
52  * @param handle pointer to sai_dma_handle_t structure which stores the transfer state.
53  * @param userData Parameter for user callback.
54  */
55 static void SAI_TxDMACallback(dma_handle_t *handle, void *userData);
56 
57 /*!
58  * @brief SAI EDMA callback for receive.
59  *
60  * @param handle pointer to sai_dma_handle_t structure which stores the transfer state.
61  * @param userData Parameter for user callback.
62  */
63 static void SAI_RxDMACallback(dma_handle_t *handle, void *userData);
64 
65 /*******************************************************************************
66  * Code
67  ******************************************************************************/
SAI_GetInstance(I2S_Type * base)68 static uint32_t SAI_GetInstance(I2S_Type *base)
69 {
70     uint32_t instance;
71 
72     /* Find the instance index from base address mappings. */
73     for (instance = 0; instance < ARRAY_SIZE(s_saiBases); instance++)
74     {
75         if (s_saiBases[instance] == base)
76         {
77             break;
78         }
79     }
80 
81     assert(instance < ARRAY_SIZE(s_saiBases));
82 
83     return instance;
84 }
85 
SAI_TxDMACallback(dma_handle_t * handle,void * userData)86 static void SAI_TxDMACallback(dma_handle_t *handle, void *userData)
87 {
88     sai_dma_private_handle_t *privHandle = (sai_dma_private_handle_t *)userData;
89     sai_dma_handle_t *saiHandle          = privHandle->handle;
90 
91     /* Update queue counter */
92     (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0, sizeof(sai_transfer_t));
93     saiHandle->queueDriver = (saiHandle->queueDriver + 1U) % SAI_XFER_QUEUE_SIZE;
94 
95     /* Call callback function */
96     if (saiHandle->callback != NULL)
97     {
98         (saiHandle->callback)(privHandle->base, saiHandle, kStatus_SAI_TxIdle, saiHandle->userData);
99     }
100 
101     /* If all data finished, just stop the transfer */
102     if (saiHandle->saiQueue[saiHandle->queueDriver].data == NULL)
103     {
104         SAI_TransferAbortSendDMA(privHandle->base, saiHandle);
105     }
106 }
107 
SAI_RxDMACallback(dma_handle_t * handle,void * userData)108 static void SAI_RxDMACallback(dma_handle_t *handle, void *userData)
109 {
110     sai_dma_private_handle_t *privHandle = (sai_dma_private_handle_t *)userData;
111     sai_dma_handle_t *saiHandle          = privHandle->handle;
112 
113     /* Update queue counter */
114     (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0, sizeof(sai_transfer_t));
115     saiHandle->queueDriver = (saiHandle->queueDriver + 1U) % SAI_XFER_QUEUE_SIZE;
116 
117     /* Call callback function */
118     if (saiHandle->callback != NULL)
119     {
120         (saiHandle->callback)(privHandle->base, saiHandle, kStatus_SAI_RxIdle, saiHandle->userData);
121     }
122 
123     /* If all data finished, just stop the transfer */
124     if (saiHandle->saiQueue[saiHandle->queueDriver].data == NULL)
125     {
126         SAI_TransferAbortReceiveDMA(privHandle->base, saiHandle);
127     }
128 }
129 
130 /*!
131  * brief Initializes the SAI master DMA handle.
132  *
133  * This function initializes the SAI master DMA handle, which can be used for other SAI master transactional APIs.
134  * Usually, for a specified SAI instance, call this API once to get the initialized handle.
135  *
136  * param base SAI base pointer.
137  * param handle SAI DMA handle pointer.
138  * param base SAI peripheral base address.
139  * param callback Pointer to user callback function.
140  * param userData User parameter passed to the callback function.
141  * param dmaHandle DMA handle pointer, this handle shall be static allocated by users.
142  */
SAI_TransferTxCreateHandleDMA(I2S_Type * base,sai_dma_handle_t * handle,sai_dma_callback_t callback,void * userData,dma_handle_t * dmaHandle)143 void SAI_TransferTxCreateHandleDMA(
144     I2S_Type *base, sai_dma_handle_t *handle, sai_dma_callback_t callback, void *userData, dma_handle_t *dmaHandle)
145 {
146     assert((handle != NULL) && (dmaHandle != NULL));
147 
148     uint32_t instance = SAI_GetInstance(base);
149 
150     /* Zero the handle */
151     (void)memset(handle, 0, sizeof(*handle));
152 
153     /* Set sai base to handle */
154     handle->dmaHandle = dmaHandle;
155     handle->callback  = callback;
156     handle->userData  = userData;
157 
158     /* Set SAI state to idle */
159     handle->state = (uint32_t)kSAI_Idle;
160 
161     s_dmaPrivateHandle[instance][0].base   = base;
162     s_dmaPrivateHandle[instance][0].handle = handle;
163 
164 /* Use FIFO error continue nstead of using interrupt to handle error */
165 #if defined(FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR) && (FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR)
166     base->TCR4 |= I2S_TCR4_FCONT_MASK;
167 #endif
168 
169     /* Install callback for Tx dma channel */
170     DMA_SetCallback(dmaHandle, SAI_TxDMACallback, &s_dmaPrivateHandle[instance][0]);
171 }
172 
173 /*!
174  * brief Initializes the SAI slave DMA handle.
175  *
176  * This function initializes the SAI slave DMA handle, which can be used for other SAI master transactional APIs.
177  * Usually, for a specified SAI instance, call this API once to get the initialized handle.
178  *
179  * param base SAI base pointer.
180  * param handle SAI DMA handle pointer.
181  * param base SAI peripheral base address.
182  * param callback Pointer to user callback function.
183  * param userData User parameter passed to the callback function.
184  * param dmaHandle DMA handle pointer, this handle shall be static allocated by users.
185  */
SAI_TransferRxCreateHandleDMA(I2S_Type * base,sai_dma_handle_t * handle,sai_dma_callback_t callback,void * userData,dma_handle_t * dmaHandle)186 void SAI_TransferRxCreateHandleDMA(
187     I2S_Type *base, sai_dma_handle_t *handle, sai_dma_callback_t callback, void *userData, dma_handle_t *dmaHandle)
188 {
189     assert((handle != NULL) && (dmaHandle != NULL));
190 
191     uint32_t instance = SAI_GetInstance(base);
192 
193     /* Zero the handle */
194     (void)memset(handle, 0, sizeof(*handle));
195 
196     /* Set sai base to handle */
197     handle->dmaHandle = dmaHandle;
198     handle->callback  = callback;
199     handle->userData  = userData;
200 
201     /* Set SAI state to idle */
202     handle->state = (uint32_t)kSAI_Idle;
203 
204     s_dmaPrivateHandle[instance][1].base   = base;
205     s_dmaPrivateHandle[instance][1].handle = handle;
206 
207 /* Use FIFO error continue nstead of using interrupt to handle error */
208 #if defined(FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR) && (FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR)
209     base->RCR4 |= I2S_RCR4_FCONT_MASK;
210 #endif
211 
212     /* Install callback for Tx dma channel */
213     DMA_SetCallback(dmaHandle, SAI_RxDMACallback, &s_dmaPrivateHandle[instance][1]);
214 }
215 
216 /*!
217  * @brief Configures the SAI Rx.
218  *
219  *
220  * @param base SAI base pointer.
221  * @param handle SAI DMA handle pointer.
222  * @param saiConfig sai configurations.
223  */
SAI_TransferRxSetConfigDMA(I2S_Type * base,sai_dma_handle_t * handle,sai_transceiver_t * saiConfig)224 void SAI_TransferRxSetConfigDMA(I2S_Type *base, sai_dma_handle_t *handle, sai_transceiver_t *saiConfig)
225 {
226     assert((handle != NULL) && (saiConfig != NULL));
227 
228     dma_transfer_config_t config = {0};
229 
230     /* Configure the audio format to SAI registers */
231     SAI_RxSetConfig(base, saiConfig);
232 
233     handle->channel = saiConfig->startChannel;
234 
235     /* Configure the data format into DMA register */
236     config.srcAddr             = SAI_RxGetDataRegisterAddress(base, handle->channel);
237     config.enableDestIncrement = true;
238     config.enableSrcIncrement  = false;
239     switch (saiConfig->frameSync.frameSyncWidth)
240     {
241         case 8:
242             config.srcSize        = kDMA_Transfersize8bits;
243             config.destSize       = kDMA_Transfersize8bits;
244             handle->bytesPerFrame = 1U;
245             break;
246         case 16:
247             config.srcSize        = kDMA_Transfersize16bits;
248             config.destSize       = kDMA_Transfersize16bits;
249             handle->bytesPerFrame = 2U;
250             break;
251         default:
252             config.srcSize        = kDMA_Transfersize32bits;
253             config.destSize       = kDMA_Transfersize32bits;
254             handle->bytesPerFrame = 4U;
255             break;
256     }
257 
258     /* Configure DMA channel */
259     (void)DMA_SubmitTransfer(handle->dmaHandle, &config, 1UL);
260 }
261 
262 /*!
263  * @brief Configures the SAI Tx.
264  *
265  *
266  * @param base SAI base pointer.
267  * @param handle SAI DMA handle pointer.
268  * @param saiConfig sai configurations.
269  */
SAI_TransferTxSetConfigDMA(I2S_Type * base,sai_dma_handle_t * handle,sai_transceiver_t * saiConfig)270 void SAI_TransferTxSetConfigDMA(I2S_Type *base, sai_dma_handle_t *handle, sai_transceiver_t *saiConfig)
271 {
272     assert((handle != NULL) && (saiConfig != NULL));
273 
274     dma_transfer_config_t config = {0};
275 
276     /* Configure the audio format to SAI registers */
277     SAI_TxSetConfig(base, saiConfig);
278 
279     handle->channel = saiConfig->startChannel;
280 
281     /* Configure the data format into DMA register */
282     config.destAddr            = SAI_TxGetDataRegisterAddress(base, handle->channel);
283     config.enableDestIncrement = false;
284     config.enableSrcIncrement  = true;
285     switch (saiConfig->frameSync.frameSyncWidth)
286     {
287         case 8:
288             config.srcSize        = kDMA_Transfersize8bits;
289             config.destSize       = kDMA_Transfersize8bits;
290             handle->bytesPerFrame = 1U;
291             break;
292         case 16:
293             config.srcSize        = kDMA_Transfersize16bits;
294             config.destSize       = kDMA_Transfersize16bits;
295             handle->bytesPerFrame = 2U;
296             break;
297         default:
298             config.srcSize        = kDMA_Transfersize32bits;
299             config.destSize       = kDMA_Transfersize32bits;
300             handle->bytesPerFrame = 4U;
301             break;
302     }
303 
304     /* Configure DMA channel */
305     (void)DMA_SubmitTransfer(handle->dmaHandle, &config, 1UL);
306 }
307 
308 /*!
309  * brief Performs a non-blocking SAI transfer using DMA.
310  *
311  * note This interface returns immediately after the transfer initiates. Call
312  * the SAI_GetTransferStatus to poll the transfer status to check whether the SAI transfer finished.
313  *
314  * param base SAI base pointer.
315  * param handle SAI DMA handle pointer.
316  * param xfer Pointer to DMA transfer structure.
317  * retval kStatus_Success Successfully start the data receive.
318  * retval kStatus_SAI_TxBusy Previous receive still not finished.
319  * retval kStatus_InvalidArgument The input parameter is invalid.
320  */
SAI_TransferSendDMA(I2S_Type * base,sai_dma_handle_t * handle,sai_transfer_t * xfer)321 status_t SAI_TransferSendDMA(I2S_Type *base, sai_dma_handle_t *handle, sai_transfer_t *xfer)
322 {
323     assert((handle != NULL) && (xfer != NULL));
324 
325     /* Check if input parameter invalid */
326     if ((xfer->data == NULL) || (xfer->dataSize == 0U))
327     {
328         return kStatus_InvalidArgument;
329     }
330 
331     if (handle->saiQueue[handle->queueUser].data != NULL)
332     {
333         return kStatus_SAI_QueueFull;
334     }
335 
336     handle->transferSize[handle->queueUser]      = xfer->dataSize;
337     handle->saiQueue[handle->queueUser].data     = xfer->data;
338     handle->saiQueue[handle->queueUser].dataSize = xfer->dataSize;
339     handle->queueUser                            = (handle->queueUser + 1U) % SAI_XFER_QUEUE_SIZE;
340 
341     /* Set the source address */
342     DMA_SetSourceAddress(handle->dmaHandle->base, handle->dmaHandle->channel, (uint32_t)(xfer->data));
343 
344     /* Set the transfer size */
345     DMA_SetTransferSize(handle->dmaHandle->base, handle->dmaHandle->channel, xfer->dataSize);
346 
347     /* Change the state of handle */
348     handle->state = (uint32_t)kSAI_Busy;
349 
350     /* Start DMA transfer */
351     DMA_StartTransfer(handle->dmaHandle);
352 
353 /* Enable DMA request and start SAI */
354 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
355     SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
356 #else
357     SAI_TxEnableDMA(base, kSAI_FIFOWarningDMAEnable, true);
358 #endif
359     SAI_TxEnable(base, true);
360 
361     return kStatus_Success;
362 }
363 
364 /*!
365  * brief Performs a non-blocking SAI transfer using DMA.
366  *
367  * note This interface returns immediately after transfer initiates. Call
368  * SAI_GetTransferStatus to poll the transfer status to check whether the SAI transfer is finished.
369  *
370  * param base SAI base pointer
371  * param handle SAI DMA handle pointer.
372  * param xfer Pointer to DMA transfer structure.
373  * retval kStatus_Success Successfully start the data receive.
374  * retval kStatus_SAI_RxBusy Previous receive still not finished.
375  * retval kStatus_InvalidArgument The input parameter is invalid.
376  */
SAI_TransferReceiveDMA(I2S_Type * base,sai_dma_handle_t * handle,sai_transfer_t * xfer)377 status_t SAI_TransferReceiveDMA(I2S_Type *base, sai_dma_handle_t *handle, sai_transfer_t *xfer)
378 {
379     assert((handle != NULL) && (xfer != NULL));
380 
381     /* Check if input parameter invalid */
382     if ((xfer->data == NULL) || (xfer->dataSize == 0U))
383     {
384         return kStatus_InvalidArgument;
385     }
386 
387     if (handle->saiQueue[handle->queueUser].data != NULL)
388     {
389         return kStatus_SAI_QueueFull;
390     }
391 
392     /* Add into queue */
393     handle->transferSize[handle->queueUser]      = xfer->dataSize;
394     handle->saiQueue[handle->queueUser].data     = xfer->data;
395     handle->saiQueue[handle->queueUser].dataSize = xfer->dataSize;
396     handle->queueUser                            = (handle->queueUser + 1U) % SAI_XFER_QUEUE_SIZE;
397 
398     /* Set the source address */
399     DMA_SetDestinationAddress(handle->dmaHandle->base, handle->dmaHandle->channel, (uint32_t)(xfer->data));
400 
401     /* Set the transfer size */
402     DMA_SetTransferSize(handle->dmaHandle->base, handle->dmaHandle->channel, xfer->dataSize);
403 
404     /* Change the state of handle */
405     handle->state = (uint32_t)kSAI_Busy;
406 
407     /* Start DMA transfer */
408     DMA_StartTransfer(handle->dmaHandle);
409 
410 /* Enable DMA request and start SAI */
411 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
412     SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
413 #else
414     SAI_RxEnableDMA(base, kSAI_FIFOWarningDMAEnable, true);
415 #endif
416     SAI_RxEnable(base, true);
417 
418     return kStatus_Success;
419 }
420 
421 /*!
422  * brief Aborts a SAI transfer using DMA.
423  *
424  * param base SAI base pointer.
425  * param handle SAI DMA handle pointer.
426  */
SAI_TransferAbortSendDMA(I2S_Type * base,sai_dma_handle_t * handle)427 void SAI_TransferAbortSendDMA(I2S_Type *base, sai_dma_handle_t *handle)
428 {
429     assert(handle != NULL);
430 
431     /* Disable dma */
432     DMA_AbortTransfer(handle->dmaHandle);
433 
434 /* Disable DMA enable bit */
435 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
436     SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, false);
437 #else
438     SAI_TxEnableDMA(base, kSAI_FIFOWarningDMAEnable, false);
439 #endif
440 
441     /* Disable Tx */
442     SAI_TxEnable(base, false);
443 
444     /* Set the handle state */
445     handle->state = (uint32_t)kSAI_Idle;
446 
447     /* Clear the queue */
448     (void)memset(handle->saiQueue, 0, sizeof(sai_transfer_t) * SAI_XFER_QUEUE_SIZE);
449     handle->queueDriver = 0;
450     handle->queueUser   = 0;
451 }
452 
453 /*!
454  * brief Aborts a SAI transfer using DMA.
455  *
456  * param base SAI base pointer.
457  * param handle SAI DMA handle pointer.
458  */
SAI_TransferAbortReceiveDMA(I2S_Type * base,sai_dma_handle_t * handle)459 void SAI_TransferAbortReceiveDMA(I2S_Type *base, sai_dma_handle_t *handle)
460 {
461     assert(handle != NULL);
462 
463     /* Disable dma */
464     DMA_AbortTransfer(handle->dmaHandle);
465 
466 /* Disable DMA enable bit */
467 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
468     SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, false);
469 #else
470     SAI_RxEnableDMA(base, kSAI_FIFOWarningDMAEnable, false);
471 #endif
472 
473     /* Disable Rx */
474     SAI_RxEnable(base, false);
475 
476     /* Set the handle state */
477     handle->state = (uint32_t)kSAI_Idle;
478 
479     /* Clear the queue */
480     (void)memset(handle->saiQueue, 0, sizeof(sai_transfer_t) * SAI_XFER_QUEUE_SIZE);
481     handle->queueDriver = 0;
482     handle->queueUser   = 0;
483 }
484 
485 /*!
486  * brief Gets byte count sent by SAI.
487  *
488  * param base SAI base pointer.
489  * param handle SAI DMA handle pointer.
490  * param count Bytes count sent by SAI.
491  * retval kStatus_Success Succeed get the transfer count.
492  * retval kStatus_NoTransferInProgress There is not a non-blocking transaction currently in progress.
493  */
SAI_TransferGetSendCountDMA(I2S_Type * base,sai_dma_handle_t * handle,size_t * count)494 status_t SAI_TransferGetSendCountDMA(I2S_Type *base, sai_dma_handle_t *handle, size_t *count)
495 {
496     assert(handle != NULL);
497 
498     status_t status = kStatus_Success;
499 
500     if (handle->state != (uint32_t)kSAI_Busy)
501     {
502         status = kStatus_NoTransferInProgress;
503     }
504     else
505     {
506         *count = handle->transferSize[handle->queueDriver] -
507                  DMA_GetRemainingBytes(handle->dmaHandle->base, handle->dmaHandle->channel);
508     }
509 
510     return status;
511 }
512 
513 /*!
514  * brief Gets byte count received by SAI.
515  *
516  * param base SAI base pointer.
517  * param handle SAI DMA handle pointer.
518  * param count Bytes count received by SAI.
519  * retval kStatus_Success Succeed get the transfer count.
520  * retval kStatus_NoTransferInProgress There is not a non-blocking transaction currently in progress.
521  */
SAI_TransferGetReceiveCountDMA(I2S_Type * base,sai_dma_handle_t * handle,size_t * count)522 status_t SAI_TransferGetReceiveCountDMA(I2S_Type *base, sai_dma_handle_t *handle, size_t *count)
523 {
524     assert(handle != NULL);
525 
526     status_t status = kStatus_Success;
527 
528     if (handle->state != (uint32_t)kSAI_Busy)
529     {
530         status = kStatus_NoTransferInProgress;
531     }
532     else
533     {
534         *count = handle->transferSize[handle->queueDriver] -
535                  DMA_GetRemainingBytes(handle->dmaHandle->base, handle->dmaHandle->channel);
536     }
537 
538     return status;
539 }
540