1 /*
2  * Copyright (c) 2016, Freescale Semiconductor, Inc.
3  * Copyright 2016-2020 NXP
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 #include "fsl_esai_edma.h"
10 
11 /* Component ID definition, used by tools. */
12 #ifndef FSL_COMPONENT_ID
13 #define FSL_COMPONENT_ID "platform.drivers.esai_edma"
14 #endif
15 
16 /*******************************************************************************
17  * Definitations
18  ******************************************************************************/
19 /* Used for 32byte aligned */
20 #define STCD_ADDR(address) (edma_tcd_t *)(((uint32_t)(address) + 32U) & ~0x1FU)
21 
22 /*<! Structure definition for uart_edma_private_handle_t. The structure is private. */
23 typedef struct _esai_edma_private_handle
24 {
25     ESAI_Type *base;
26     esai_edma_handle_t *handle;
27 } esai_edma_private_handle_t;
28 
29 /*!@brief _esai_edma_transfer_state */
30 enum
31 {
32     kESAI_Busy = 0x0U, /*!< ESAI is busy */
33     kESAI_Idle,        /*!< Transfer is done. */
34 };
35 
36 /*<! Private handle only used for internally. */
37 static esai_edma_private_handle_t s_edmaPrivateHandle[FSL_FEATURE_SOC_ESAI_COUNT][2];
38 
39 /*******************************************************************************
40  * Prototypes
41  ******************************************************************************/
42 /*!
43  * @brief ESAI EDMA callback for send.
44  *
45  * @param handle pointer to esai_edma_handle_t structure which stores the transfer state.
46  * @param userData Parameter for user callback.
47  * @param done If the DMA transfer finished.
48  * @param tcds The TCD index.
49  */
50 static void ESAI_TxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds);
51 
52 /*!
53  * @brief ESAI EDMA callback for receive.
54  *
55  * @param handle pointer to esai_edma_handle_t structure which stores the transfer state.
56  * @param userData Parameter for user callback.
57  * @param done If the DMA transfer finished.
58  * @param tcds The TCD index.
59  */
60 static void ESAI_RxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds);
61 
62 /*******************************************************************************
63  * Code
64  ******************************************************************************/
ESAI_TxEDMACallback(edma_handle_t * handle,void * userData,bool done,uint32_t tcds)65 static void ESAI_TxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds)
66 {
67     esai_edma_private_handle_t *privHandle = (esai_edma_private_handle_t *)userData;
68     esai_edma_handle_t *esaiHandle         = privHandle->handle;
69 
70     /* If finished a block, call the callback function */
71     (void)memset(&esaiHandle->esaiQueue[esaiHandle->queueDriver], 0, sizeof(esai_transfer_t));
72     esaiHandle->queueDriver = (esaiHandle->queueDriver + 1U) % ESAI_XFER_QUEUE_SIZE;
73     if (esaiHandle->callback != NULL)
74     {
75         (esaiHandle->callback)(privHandle->base, esaiHandle, kStatus_ESAI_TxIdle, esaiHandle->userData);
76     }
77 
78     /* If all data finished, just stop the transfer */
79     if (esaiHandle->esaiQueue[esaiHandle->queueDriver].data == NULL)
80     {
81         ESAI_TransferAbortSendEDMA(privHandle->base, esaiHandle);
82     }
83 }
84 
ESAI_RxEDMACallback(edma_handle_t * handle,void * userData,bool done,uint32_t tcds)85 static void ESAI_RxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds)
86 {
87     esai_edma_private_handle_t *privHandle = (esai_edma_private_handle_t *)userData;
88     esai_edma_handle_t *esaiHandle         = privHandle->handle;
89 
90     /* If finished a block, call the callback function */
91     (void)memset(&esaiHandle->esaiQueue[esaiHandle->queueDriver], 0, sizeof(esai_transfer_t));
92     esaiHandle->queueDriver = (esaiHandle->queueDriver + 1U) % ESAI_XFER_QUEUE_SIZE;
93     if (esaiHandle->callback != NULL)
94     {
95         (esaiHandle->callback)(privHandle->base, esaiHandle, kStatus_ESAI_RxIdle, esaiHandle->userData);
96     }
97 
98     /* If all data finished, just stop the transfer */
99     if (esaiHandle->esaiQueue[esaiHandle->queueDriver].data == NULL)
100     {
101         ESAI_TransferAbortReceiveEDMA(privHandle->base, esaiHandle);
102     }
103 }
104 
105 /*!
106  * brief Initializes the ESAI eDMA handle.
107  *
108  * This function initializes the ESAI master DMA handle, which can be used for other ESAI master transactional APIs.
109  * Usually, for a specified ESAI instance, call this API once to get the initialized handle.
110  *
111  * param base ESAI base pointer.
112  * param handle ESAI eDMA handle pointer.
113  * param base ESAI peripheral base address.
114  * param callback Pointer to user callback function.
115  * param userData User parameter passed to the callback function.
116  * param dmaHandle eDMA handle pointer, this handle shall be static allocated by users.
117  */
ESAI_TransferTxCreateHandleEDMA(ESAI_Type * base,esai_edma_handle_t * handle,esai_edma_callback_t callback,void * userData,edma_handle_t * dmaHandle)118 void ESAI_TransferTxCreateHandleEDMA(ESAI_Type *base,
119                                      esai_edma_handle_t *handle,
120                                      esai_edma_callback_t callback,
121                                      void *userData,
122                                      edma_handle_t *dmaHandle)
123 {
124     assert((handle != NULL) && (dmaHandle != NULL));
125 
126     uint32_t instance = ESAI_GetInstance(base);
127 
128     /* Zero the handle */
129     (void)memset(handle, 0, sizeof(*handle));
130 
131     /* Set esai base to handle */
132     handle->dmaHandle = dmaHandle;
133     handle->callback  = callback;
134     handle->userData  = userData;
135 
136     /* Set ESAI state to idle */
137     handle->state = (uint32_t)kESAI_Idle;
138 
139     s_edmaPrivateHandle[instance][0].base   = base;
140     s_edmaPrivateHandle[instance][0].handle = handle;
141 
142     /* Need to use scatter gather */
143     EDMA_InstallTCDMemory(dmaHandle, STCD_ADDR(handle->tcd), ESAI_XFER_QUEUE_SIZE);
144 
145     /* Install callback for Tx dma channel */
146     EDMA_SetCallback(dmaHandle, ESAI_TxEDMACallback, &s_edmaPrivateHandle[instance][0]);
147 }
148 
149 /*!
150  * brief Initializes the ESAI Rx eDMA handle.
151  *
152  * This function initializes the ESAI slave DMA handle, which can be used for other ESAI master transactional APIs.
153  * Usually, for a specified ESAI instance, call this API once to get the initialized handle.
154  *
155  * param base ESAI base pointer.
156  * param handle ESAI eDMA handle pointer.
157  * param base ESAI peripheral base address.
158  * param callback Pointer to user callback function.
159  * param userData User parameter passed to the callback function.
160  * param dmaHandle eDMA handle pointer, this handle shall be static allocated by users.
161  */
ESAI_TransferRxCreateHandleEDMA(ESAI_Type * base,esai_edma_handle_t * handle,esai_edma_callback_t callback,void * userData,edma_handle_t * dmaHandle)162 void ESAI_TransferRxCreateHandleEDMA(ESAI_Type *base,
163                                      esai_edma_handle_t *handle,
164                                      esai_edma_callback_t callback,
165                                      void *userData,
166                                      edma_handle_t *dmaHandle)
167 {
168     assert((handle != NULL) && (dmaHandle != NULL));
169 
170     uint32_t instance = ESAI_GetInstance(base);
171 
172     /* Zero the handle */
173     (void)memset(handle, 0, sizeof(*handle));
174 
175     /* Set esai base to handle */
176     handle->dmaHandle = dmaHandle;
177     handle->callback  = callback;
178     handle->userData  = userData;
179 
180     /* Set ESAI state to idle */
181     handle->state = (uint32_t)kESAI_Idle;
182 
183     s_edmaPrivateHandle[instance][1].base   = base;
184     s_edmaPrivateHandle[instance][1].handle = handle;
185 
186     /* Need to use scatter gather */
187     EDMA_InstallTCDMemory(dmaHandle, STCD_ADDR(handle->tcd), ESAI_XFER_QUEUE_SIZE);
188 
189     /* Install callback for Tx dma channel */
190     EDMA_SetCallback(dmaHandle, ESAI_RxEDMACallback, &s_edmaPrivateHandle[instance][1]);
191 }
192 
193 /*!
194  * brief Configures the ESAI Tx audio format.
195  *
196  * The audio format can be changed at run-time. This function configures the sample rate and audio data
197  * format to be transferred. This function also sets the eDMA parameter according to formatting requirements.
198  *
199  * param base ESAI base pointer.
200  * param handle ESAI eDMA handle pointer.
201  * param format Pointer to ESAI audio data format structure.
202  * param hckClockHz HCK clock frequency in Hz.
203  * param hclkSourceClockHz HCK clock source frequency in Hz.
204  * retval kStatus_Success Audio format set successfully.
205  * retval kStatus_InvalidArgument The input argument is invalid.
206  */
ESAI_TransferTxSetFormatEDMA(ESAI_Type * base,esai_edma_handle_t * handle,esai_format_t * format,uint32_t hckClockHz,uint32_t hclkSourceClockHz)207 void ESAI_TransferTxSetFormatEDMA(
208     ESAI_Type *base, esai_edma_handle_t *handle, esai_format_t *format, uint32_t hckClockHz, uint32_t hclkSourceClockHz)
209 {
210     assert((handle != NULL) && (format != NULL));
211 
212     /* Configure the audio format to ESAI registers */
213     ESAI_TxSetFormat(base, format, hckClockHz, hclkSourceClockHz);
214 
215     /* Get the transfer size from format, this should be used in EDMA configuration */
216     ESAI_AnalysisSlot(format->slotType, &handle->slotLen, &handle->bitWidth);
217     handle->sectionMap = format->sectionMap;
218 
219     /* Update the data channel ESAI used */
220     handle->count = (uint8_t)FSL_FEATURE_ESAI_FIFO_SIZEn(base) -
221                     (uint8_t)((base->TFCR & ESAI_TFCR_TFWM_MASK) >> ESAI_TFCR_TFWM_SHIFT);
222 }
223 
224 /*!
225  * brief Configures the ESAI Rx audio format.
226  *
227  * The audio format can be changed at run-time. This function configures the sample rate and audio data
228  * format to be transferred. This function also sets the eDMA parameter according to formatting requirements.
229  *
230  * param base ESAI base pointer.
231  * param handle ESAI eDMA handle pointer.
232  * param format Pointer to ESAI audio data format structure.
233  * param hckClockHz HCK clock frequency in Hz.
234  * param hclkSourceClockHz HCK clock source frequency in Hz.
235  * retval kStatus_Success Audio format set successfully.
236  * retval kStatus_InvalidArgument The input argument is invalid.
237  */
ESAI_TransferRxSetFormatEDMA(ESAI_Type * base,esai_edma_handle_t * handle,esai_format_t * format,uint32_t hckClockHz,uint32_t hclkSourceClockHz)238 void ESAI_TransferRxSetFormatEDMA(
239     ESAI_Type *base, esai_edma_handle_t *handle, esai_format_t *format, uint32_t hckClockHz, uint32_t hclkSourceClockHz)
240 {
241     assert((handle != NULL) && (format != NULL));
242 
243     /* Configure the audio format to ESAI registers */
244     ESAI_RxSetFormat(base, format, hckClockHz, hclkSourceClockHz);
245 
246     /* Get the transfer size from format, this should be used in EDMA configuration */
247     ESAI_AnalysisSlot(format->slotType, &handle->slotLen, &handle->bitWidth);
248     handle->sectionMap = format->sectionMap;
249 
250     /* Update the data channel ESAI used */
251     handle->count = (uint8_t)((base->TFCR & ESAI_TFCR_TFWM_MASK) >> ESAI_TFCR_TFWM_SHIFT);
252 }
253 
254 /*!
255  * brief Performs a non-blocking ESAI transfer using DMA.
256  *
257  * note This interface returns immediately after the transfer initiates. Call
258  * ESAI_GetTransferStatus to poll the transfer status and check whether the ESAI transfer is finished.
259  *
260  * param base ESAI base pointer.
261  * param handle ESAI eDMA handle pointer.
262  * param xfer Pointer to the DMA transfer structure.
263  * retval kStatus_Success Start a ESAI eDMA send successfully.
264  * retval kStatus_InvalidArgument The input argument is invalid.
265  * retval kStatus_TxBusy ESAI is busy sending data.
266  */
ESAI_TransferSendEDMA(ESAI_Type * base,esai_edma_handle_t * handle,esai_transfer_t * xfer)267 status_t ESAI_TransferSendEDMA(ESAI_Type *base, esai_edma_handle_t *handle, esai_transfer_t *xfer)
268 {
269     assert((handle != NULL) && (xfer != NULL));
270 
271     edma_transfer_config_t config = {0};
272     uint32_t destAddr             = ESAI_TxGetDataRegisterAddress(base);
273 
274     /* Check if input parameter invalid */
275     if ((xfer->data == NULL) || (xfer->dataSize == 0U))
276     {
277         return kStatus_InvalidArgument;
278     }
279 
280     if (handle->esaiQueue[handle->queueUser].data != NULL)
281     {
282         return kStatus_ESAI_QueueFull;
283     }
284 
285     /* Change the state of handle */
286     handle->state = (uint32_t)kESAI_Busy;
287 
288     /* Update the queue state */
289     handle->transferSize[handle->queueUser]       = xfer->dataSize;
290     handle->esaiQueue[handle->queueUser].data     = xfer->data;
291     handle->esaiQueue[handle->queueUser].dataSize = xfer->dataSize;
292     handle->queueUser                             = (handle->queueUser + 1U) % ESAI_XFER_QUEUE_SIZE;
293 
294     /* Prepare edma configure */
295     EDMA_PrepareTransfer(&config, xfer->data, handle->bitWidth / 8UL, (void *)(uint32_t *)destAddr,
296                          handle->bitWidth / 8UL, (uint32_t)handle->count * handle->bitWidth / 8U, xfer->dataSize,
297                          kEDMA_MemoryToPeripheral);
298 
299     /* Store the initially configured eDMA minor byte transfer count into the ESAI handle */
300     handle->nbytes = handle->count * handle->bitWidth / 8U;
301 
302     (void)EDMA_SubmitTransfer(handle->dmaHandle, &config);
303 
304     /* Start DMA transfer */
305     EDMA_StartTransfer(handle->dmaHandle);
306 
307     /* Enable ESAI Tx clock */
308     ESAI_TxEnable(base, handle->sectionMap);
309 
310     return kStatus_Success;
311 }
312 
313 /*!
314  * brief Performs a non-blocking ESAI receive using eDMA.
315  *
316  * note This interface returns immediately after the transfer initiates. Call
317  * the ESAI_GetReceiveRemainingBytes to poll the transfer status and check whether the ESAI transfer is finished.
318  *
319  * param base ESAI base pointer
320  * param handle ESAI eDMA handle pointer.
321  * param xfer Pointer to DMA transfer structure.
322  * retval kStatus_Success Start a ESAI eDMA receive successfully.
323  * retval kStatus_InvalidArgument The input argument is invalid.
324  * retval kStatus_RxBusy ESAI is busy receiving data.
325  */
ESAI_TransferReceiveEDMA(ESAI_Type * base,esai_edma_handle_t * handle,esai_transfer_t * xfer)326 status_t ESAI_TransferReceiveEDMA(ESAI_Type *base, esai_edma_handle_t *handle, esai_transfer_t *xfer)
327 {
328     assert((handle != NULL) && (xfer != NULL));
329 
330     edma_transfer_config_t config = {0};
331     uint32_t srcAddr              = ESAI_RxGetDataRegisterAddress(base);
332 
333     /* Check if input parameter invalid */
334     if ((xfer->data == NULL) || (xfer->dataSize == 0U))
335     {
336         return kStatus_InvalidArgument;
337     }
338 
339     if (handle->esaiQueue[handle->queueUser].data != NULL)
340     {
341         return kStatus_ESAI_QueueFull;
342     }
343 
344     /* Change the state of handle */
345     handle->state = (uint32_t)kESAI_Busy;
346 
347     /* Update queue state  */
348     handle->transferSize[handle->queueUser]       = xfer->dataSize;
349     handle->esaiQueue[handle->queueUser].data     = xfer->data;
350     handle->esaiQueue[handle->queueUser].dataSize = xfer->dataSize;
351     handle->queueUser                             = (handle->queueUser + 1U) % ESAI_XFER_QUEUE_SIZE;
352 
353     /* Prepare edma configure */
354     EDMA_PrepareTransfer(&config, (void *)(uint32_t *)srcAddr, handle->bitWidth / 8UL, xfer->data,
355                          handle->bitWidth / 8UL, (uint32_t)handle->count * handle->bitWidth / 8U, xfer->dataSize,
356                          kEDMA_PeripheralToMemory);
357 
358     /* Store the initially configured eDMA minor byte transfer count into the ESAI handle */
359     handle->nbytes = handle->count * handle->bitWidth / 8U;
360 
361     (void)EDMA_SubmitTransfer(handle->dmaHandle, &config);
362 
363     /* Start DMA transfer */
364     EDMA_StartTransfer(handle->dmaHandle);
365 
366     /* Enable ESAI Rx clock */
367     ESAI_RxEnable(base, handle->sectionMap);
368 
369     return kStatus_Success;
370 }
371 
372 /*!
373  * brief Aborts a ESAI transfer using eDMA.
374  *
375  * param base ESAI base pointer.
376  * param handle ESAI eDMA handle pointer.
377  */
ESAI_TransferAbortSendEDMA(ESAI_Type * base,esai_edma_handle_t * handle)378 void ESAI_TransferAbortSendEDMA(ESAI_Type *base, esai_edma_handle_t *handle)
379 {
380     assert(handle != NULL);
381 
382     /* Disable dma */
383     EDMA_AbortTransfer(handle->dmaHandle);
384 
385     /* Disable Tx */
386     ESAI_TxEnable(base, 0x0);
387 
388     /* Set the handle state */
389     handle->state = (uint32_t)kESAI_Idle;
390 }
391 
392 /*!
393  * brief Aborts a ESAI receive using eDMA.
394  *
395  * param base ESAI base pointer
396  * param handle ESAI eDMA handle pointer.
397  */
ESAI_TransferAbortReceiveEDMA(ESAI_Type * base,esai_edma_handle_t * handle)398 void ESAI_TransferAbortReceiveEDMA(ESAI_Type *base, esai_edma_handle_t *handle)
399 {
400     assert(handle != NULL);
401 
402     /* Disable dma */
403     EDMA_AbortTransfer(handle->dmaHandle);
404 
405     /* Disable Rx */
406     ESAI_RxEnable(base, 0x0);
407 
408     /* Set the handle state */
409     handle->state = (uint32_t)kESAI_Idle;
410 }
411 
412 /*!
413  * brief Gets byte count sent by ESAI.
414  *
415  * param base ESAI base pointer.
416  * param handle ESAI eDMA handle pointer.
417  * param count Bytes count sent by ESAI.
418  * retval kStatus_Success Succeed get the transfer count.
419  * retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress.
420  */
ESAI_TransferGetSendCountEDMA(ESAI_Type * base,esai_edma_handle_t * handle,size_t * count)421 status_t ESAI_TransferGetSendCountEDMA(ESAI_Type *base, esai_edma_handle_t *handle, size_t *count)
422 {
423     assert(handle != NULL);
424 
425     status_t status = kStatus_Success;
426 
427     if (handle->state != (uint32_t)kESAI_Busy)
428     {
429         status = kStatus_NoTransferInProgress;
430     }
431     else
432     {
433         *count = (handle->transferSize[handle->queueDriver] -
434                   (uint32_t)handle->nbytes *
435                       EDMA_GetRemainingMajorLoopCount(handle->dmaHandle->base, handle->dmaHandle->channel));
436     }
437 
438     return status;
439 }
440 
441 /*!
442  * brief Gets byte count received by ESAI.
443  *
444  * param base ESAI base pointer
445  * param handle ESAI eDMA handle pointer.
446  * param count Bytes count received by ESAI.
447  * retval kStatus_Success Succeed get the transfer count.
448  * retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress.
449  */
ESAI_TransferGetReceiveCountEDMA(ESAI_Type * base,esai_edma_handle_t * handle,size_t * count)450 status_t ESAI_TransferGetReceiveCountEDMA(ESAI_Type *base, esai_edma_handle_t *handle, size_t *count)
451 {
452     assert(handle != NULL);
453 
454     status_t status = kStatus_Success;
455 
456     if (handle->state != (uint32_t)kESAI_Busy)
457     {
458         status = kStatus_NoTransferInProgress;
459     }
460     else
461     {
462         *count = (handle->transferSize[handle->queueDriver] -
463                   (uint32_t)handle->nbytes *
464                       EDMA_GetRemainingMajorLoopCount(handle->dmaHandle->base, handle->dmaHandle->channel));
465     }
466 
467     return status;
468 }
469