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