1 /*
2  * Copyright 2019-2020 NXP
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "fsl_asrc_edma.h"
9 
10 /* Component ID definition, used by tools. */
11 #ifndef FSL_COMPONENT_ID
12 #define FSL_COMPONENT_ID "platform.drivers.asrc_edma"
13 #endif
14 
15 /*******************************************************************************
16  * Definitations
17  ******************************************************************************/
18 /* Used for 32byte aligned */
19 #define STCD_ADDR(address) (edma_tcd_t *)(((uint32_t)(address) + 32U) & ~0x1FU)
20 
21 /*<! Structure definition for uart_edma_private_handle_t. The structure is private. */
22 typedef struct _asrc_edma_private_handle
23 {
24     ASRC_Type *base;
25     asrc_edma_handle_t *handle;
26 } asrc_edma_private_handle_t;
27 
28 /*<! Private handle only used for internally. */
29 static asrc_edma_private_handle_t s_edmaPrivateHandle[FSL_FEATURE_SOC_ASRC_COUNT][FSL_ASRC_CHANNEL_PAIR_COUNT];
30 
31 /*******************************************************************************
32  * Prototypes
33  ******************************************************************************/
34 /*!
35  * @brief ASRC EDMA callback for input.
36  *
37  * @param handle pointer to asrc_edma_handle_t structure which stores the transfer state.
38  * @param userData Parameter for user callback.
39  * @param done If the DMA transfer finished.
40  * @param tcds The TCD index.
41  */
42 static void ASRC_InEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds);
43 
44 /*!
45  * @brief ASRC EDMA callback for output.
46  *
47  * @param handle pointer to asrc_edma_handle_t structure which stores the transfer state.
48  * @param userData Parameter for user callback.
49  * @param done If the DMA transfer finished.
50  * @param tcds The TCD index.
51  */
52 static void ASRC_OutEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds);
53 /*******************************************************************************
54  * Code
55  ******************************************************************************/
ASRC_InEDMACallback(edma_handle_t * handle,void * userData,bool done,uint32_t tcds)56 static void ASRC_InEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds)
57 {
58     asrc_edma_private_handle_t *privHandle = (asrc_edma_private_handle_t *)userData;
59     asrc_edma_handle_t *asrcHandle         = privHandle->handle;
60     asrc_in_edma_handle_t *asrcInHandle    = &(privHandle->handle->in);
61     status_t inStatus                      = kStatus_ASRCInIdle;
62     /* If finished a block, call the callback function */
63     asrcInHandle->asrcQueue[asrcInHandle->queueDriver] = NULL;
64     asrcInHandle->queueDriver                          = (asrcInHandle->queueDriver + 1U) % ASRC_XFER_QUEUE_SIZE;
65 
66     /* If all data finished, just stop the transfer */
67     if (asrcInHandle->asrcQueue[asrcInHandle->queueDriver] == NULL)
68     {
69         EDMA_AbortTransfer(asrcInHandle->inDmaHandle);
70         inStatus = kStatus_ASRCInQueueIdle;
71     }
72 
73     if (asrcHandle->callback != NULL)
74     {
75         (asrcHandle->callback)(privHandle->base, asrcHandle, inStatus, asrcHandle->userData);
76     }
77 }
78 
ASRC_OutEDMACallback(edma_handle_t * handle,void * userData,bool done,uint32_t tcds)79 static void ASRC_OutEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds)
80 {
81     asrc_edma_private_handle_t *privHandle = (asrc_edma_private_handle_t *)userData;
82     asrc_edma_handle_t *asrcHandle         = privHandle->handle;
83     asrc_out_edma_handle_t *asrcOutHandle  = &(privHandle->handle->out);
84     uint32_t queueDriverIndex              = asrcOutHandle->queueDriver;
85     status_t callbackStatus                = kStatus_ASRCOutIdle;
86 
87     /* If finished a block, call the callback function */
88     asrcOutHandle->asrcQueue[queueDriverIndex] = NULL;
89     asrcOutHandle->queueDriver                 = (uint8_t)((queueDriverIndex + 1U) % ASRC_XFER_OUT_QUEUE_SIZE);
90 
91     /* If all data finished, just stop the transfer */
92     if (asrcOutHandle->asrcQueue[asrcOutHandle->queueDriver] == NULL)
93     {
94         EDMA_AbortTransfer(asrcOutHandle->outDmaHandle);
95         callbackStatus = kStatus_ASRCOutQueueIdle;
96     }
97 
98     if (asrcHandle->callback != NULL)
99     {
100         (asrcHandle->callback)(privHandle->base, asrcHandle, callbackStatus, asrcHandle->userData);
101     }
102 }
103 /*!
104  * brief Initializes the ASRC IN eDMA handle.
105  *
106  * This function initializes the ASRC DMA handle, which can be used for other ASRC transactional APIs.
107  * Usually, for a specified ASRC channel pair, call this API once to get the initialized handle.
108  *
109  * param base ASRC base pointer.
110  * param channelPair ASRC channel pair
111  * param handle ASRC eDMA handle pointer.
112  * param callback Pointer to user callback function.
113  * param txDmaHandle ASRC send edma handle pointer.
114  * param periphConfig peripheral configuration.
115  * param userData User parameter passed to the callback function.
116  */
ASRC_TransferInCreateHandleEDMA(ASRC_Type * base,asrc_edma_handle_t * handle,asrc_channel_pair_t channelPair,asrc_edma_callback_t callback,edma_handle_t * inDmaHandle,const asrc_p2p_edma_config_t * periphConfig,void * userData)117 void ASRC_TransferInCreateHandleEDMA(ASRC_Type *base,
118                                      asrc_edma_handle_t *handle,
119                                      asrc_channel_pair_t channelPair,
120                                      asrc_edma_callback_t callback,
121                                      edma_handle_t *inDmaHandle,
122                                      const asrc_p2p_edma_config_t *periphConfig,
123                                      void *userData)
124 {
125     assert((handle != NULL) && (inDmaHandle != NULL));
126 
127     uint32_t instance = ASRC_GetInstance(base);
128 
129     /* Zero the handle */
130     (void)memset(&handle->in, 0, sizeof(asrc_in_edma_handle_t));
131 
132     handle->in.inDmaHandle = inDmaHandle;
133     handle->callback       = callback;
134     handle->userData       = userData;
135 
136     /* Set ASRC state to idle */
137     handle->in.state            = kStatus_ASRCIdle;
138     handle->channelPair         = channelPair;
139     handle->in.peripheralConfig = periphConfig;
140 
141     s_edmaPrivateHandle[instance][channelPair].base   = base;
142     s_edmaPrivateHandle[instance][channelPair].handle = handle;
143 
144     /* Need to use scatter gather */
145     EDMA_InstallTCDMemory(inDmaHandle, (edma_tcd_t *)(STCD_ADDR(handle->in.tcd)), ASRC_XFER_OUT_QUEUE_SIZE);
146     /* Install callback for Tx dma channel */
147     EDMA_SetCallback(inDmaHandle, ASRC_InEDMACallback, &s_edmaPrivateHandle[instance][channelPair]);
148 }
149 
150 /*!
151  * brief Initializes the ASRC OUT eDMA handle.
152  *
153  * This function initializes the ASRC DMA handle, which can be used for other ASRC transactional APIs.
154  * Usually, for a specified ASRC channel pair, call this API once to get the initialized handle.
155  *
156  * param base ASRC base pointer.
157  * param channelPair ASRC channel pair
158  * param handle ASRC eDMA handle pointer.
159  * param callback Pointer to user callback function.
160  * param txDmaHandle ASRC send edma handle pointer.
161  * param periphConfig peripheral configuration.
162  * param userData User parameter passed to the callback function.
163  */
ASRC_TransferOutCreateHandleEDMA(ASRC_Type * base,asrc_edma_handle_t * handle,asrc_channel_pair_t channelPair,asrc_edma_callback_t callback,edma_handle_t * outDmaHandle,const asrc_p2p_edma_config_t * periphConfig,void * userData)164 void ASRC_TransferOutCreateHandleEDMA(ASRC_Type *base,
165                                       asrc_edma_handle_t *handle,
166                                       asrc_channel_pair_t channelPair,
167                                       asrc_edma_callback_t callback,
168                                       edma_handle_t *outDmaHandle,
169                                       const asrc_p2p_edma_config_t *periphConfig,
170                                       void *userData)
171 {
172     assert((handle != NULL) && (NULL != outDmaHandle));
173 
174     uint32_t instance = ASRC_GetInstance(base);
175 
176     /* Zero the handle */
177     (void)memset(&handle->out, 0, sizeof(asrc_out_edma_handle_t));
178 
179     handle->out.outDmaHandle = outDmaHandle;
180     handle->callback         = callback;
181     handle->userData         = userData;
182 
183     /* Set ASRC state to idle */
184     handle->out.state            = kStatus_ASRCIdle;
185     handle->channelPair          = channelPair;
186     handle->out.peripheralConfig = periphConfig;
187 
188     s_edmaPrivateHandle[instance][channelPair].base   = base;
189     s_edmaPrivateHandle[instance][channelPair].handle = handle;
190 
191     /* Need to use scatter gather */
192     EDMA_InstallTCDMemory(outDmaHandle, (edma_tcd_t *)(STCD_ADDR(handle->out.tcd)), ASRC_XFER_OUT_QUEUE_SIZE);
193     /* Install callback for Tx dma channel */
194     EDMA_SetCallback(outDmaHandle, ASRC_OutEDMACallback, &s_edmaPrivateHandle[instance][channelPair]);
195 }
196 
197 /*!
198  * brief Configures the ASRC channel pair.
199  *
200  * param base ASRC base pointer.
201  * param handle ASRC eDMA handle pointer.
202  * param asrcConfig asrc configurations.
203  * param periphConfig peripheral configuration.
204  * param inputSampleRate ASRC input sample rate.
205  * param outputSampleRate ASRC output sample rate.
206  */
ASRC_TransferSetChannelPairConfigEDMA(ASRC_Type * base,asrc_edma_handle_t * handle,asrc_channel_pair_config_t * asrcConfig,uint32_t inSampleRate,uint32_t outSampleRate)207 status_t ASRC_TransferSetChannelPairConfigEDMA(ASRC_Type *base,
208                                                asrc_edma_handle_t *handle,
209                                                asrc_channel_pair_config_t *asrcConfig,
210                                                uint32_t inSampleRate,
211                                                uint32_t outSampleRate)
212 {
213     assert((handle != NULL) && (NULL != asrcConfig));
214 
215     /* Configure the audio format to ASRC registers */
216     if (ASRC_SetChannelPairConfig(base, handle->channelPair, asrcConfig, inSampleRate, outSampleRate) !=
217         kStatus_Success)
218     {
219         return kStatus_ASRCChannelPairConfigureFailed;
220     }
221     handle->in.fifoThreshold  = (asrcConfig->inFifoThreshold) * (uint32_t)asrcConfig->audioDataChannels;
222     handle->out.fifoThreshold = (asrcConfig->outFifoThreshold) * (uint32_t)asrcConfig->audioDataChannels;
223     (void)ASRC_MapSamplesWidth(base, handle->channelPair, &handle->in.sampleWidth, &handle->out.sampleWidth);
224 
225     return kStatus_Success;
226 }
227 
228 /*!
229  * brief Get output sample buffer size can be transferred by edma.
230  *
231  * note This API is depends on the ASRC output configuration, should be called after the
232  * ASRC_TransferSetChannelPairConfigEDMA.
233  *
234  * param base asrc base pointer.
235  * param handle ASRC channel pair edma handle.
236  * param inSampleRate input sample rate.
237  * param outSampleRate output sample rate.
238  * param inSamples input sampleS size.
239  * retval output buffer size in byte.
240  */
ASRC_GetOutSamplesSizeEDMA(ASRC_Type * base,asrc_edma_handle_t * handle,uint32_t inSampleRate,uint32_t outSampleRate,uint32_t inSamplesize)241 uint32_t ASRC_GetOutSamplesSizeEDMA(
242     ASRC_Type *base, asrc_edma_handle_t *handle, uint32_t inSampleRate, uint32_t outSampleRate, uint32_t inSamplesize)
243 {
244     uint32_t outputSize = ASRC_GetOutSamplesSize(base, handle->channelPair, inSampleRate, outSampleRate, inSamplesize);
245 
246     return outputSize - outputSize % handle->out.fifoThreshold;
247 }
248 
ASRC_TransferOutSubmitEDMA(ASRC_Type * base,asrc_edma_handle_t * handle,uint32_t * outDataAddr,uint32_t outDataSize)249 static status_t ASRC_TransferOutSubmitEDMA(ASRC_Type *base,
250                                            asrc_edma_handle_t *handle,
251                                            uint32_t *outDataAddr,
252                                            uint32_t outDataSize)
253 {
254     assert(outDataAddr != NULL);
255     assert(outDataSize != 0U);
256 
257     uint32_t outAddr              = ASRC_GetOutputDataRegisterAddress(base, handle->channelPair);
258     edma_transfer_config_t config = {0};
259     edma_transfer_type_t type     = kEDMA_PeripheralToMemory;
260 
261     if (handle->out.asrcQueue[handle->out.queueUser] != NULL)
262     {
263         return kStatus_ASRCQueueFull;
264     }
265 
266     if (handle->out.peripheralConfig != NULL)
267     {
268         type = kEDMA_PeripheralToPeripheral;
269     }
270 
271     if (handle->out.asrcQueue[handle->out.queueUser] != NULL)
272     {
273         return kStatus_ASRCQueueFull;
274     }
275 
276     handle->out.asrcQueue[handle->out.queueUser]    = outDataAddr;
277     handle->out.transferSize[handle->out.queueUser] = outDataSize;
278     handle->out.queueUser                           = (handle->out.queueUser + 1U) % ASRC_XFER_OUT_QUEUE_SIZE;
279     /* Prepare ASRC output edma configuration */
280     EDMA_PrepareTransfer(&config, (uint32_t *)outAddr, handle->out.sampleWidth, outDataAddr, handle->out.sampleWidth,
281                          handle->out.fifoThreshold * handle->out.sampleWidth, outDataSize, type);
282 
283     if (handle->out.state != (uint32_t)kStatus_ASRCBusy)
284     {
285         (void)EDMA_SubmitTransfer(handle->out.outDmaHandle, &config);
286 
287         EDMA_StartTransfer(handle->out.outDmaHandle);
288 
289         if ((handle->out.peripheralConfig != NULL) && (handle->out.peripheralConfig->startPeripheral != NULL))
290         {
291             handle->out.peripheralConfig->startPeripheral(true);
292         }
293     }
294 
295     return kStatus_Success;
296 }
297 
ASRC_TransferInSubmitEDMA(ASRC_Type * base,asrc_edma_handle_t * handle,uint32_t * inDataAddr,uint32_t inDataSize)298 static status_t ASRC_TransferInSubmitEDMA(ASRC_Type *base,
299                                           asrc_edma_handle_t *handle,
300                                           uint32_t *inDataAddr,
301                                           uint32_t inDataSize)
302 {
303     assert(inDataAddr != NULL);
304     assert(inDataSize != 0U);
305 
306     uint32_t inAddr               = ASRC_GetInputDataRegisterAddress(base, handle->channelPair);
307     edma_transfer_config_t config = {0};
308 
309     if (handle->in.asrcQueue[handle->in.queueUser] != NULL)
310     {
311         return kStatus_ASRCQueueFull;
312     }
313 
314     /* Add into queue */
315     handle->in.asrcQueue[handle->in.queueUser]    = inDataAddr;
316     handle->in.transferSize[handle->in.queueUser] = inDataSize;
317     handle->in.queueUser                          = (handle->in.queueUser + 1U) % ASRC_XFER_IN_QUEUE_SIZE;
318 
319     /* Prepare ASRC input edma configuration */
320     EDMA_PrepareTransfer(&config, (uint32_t *)inDataAddr, handle->in.sampleWidth, (uint32_t *)inAddr,
321                          handle->in.sampleWidth, handle->in.fifoThreshold * handle->in.sampleWidth, inDataSize,
322                          handle->in.peripheralConfig == NULL ? kEDMA_MemoryToPeripheral : kEDMA_PeripheralToPeripheral);
323 
324     if (handle->in.state != (uint32_t)kStatus_ASRCBusy)
325     {
326         (void)EDMA_SubmitTransfer(handle->in.inDmaHandle, &config);
327         EDMA_StartTransfer(handle->in.inDmaHandle);
328         /* start peripheral */
329         if ((handle->in.peripheralConfig != NULL) && (handle->in.peripheralConfig->startPeripheral != NULL))
330         {
331             handle->in.peripheralConfig->startPeripheral(true);
332         }
333     }
334 
335     return kStatus_Success;
336 }
337 
338 /*!
339  * brief Performs a non-blocking ASRC convert using EDMA.
340  *
341  * note This interface returns immediately after the transfer initiates.
342 
343  * param base ASRC base pointer.
344  * param handle ASRC eDMA handle pointer.
345  * param xfer Pointer to the DMA transfer structure.
346  * retval kStatus_Success Start a ASRC eDMA send successfully.
347  * retval kStatus_InvalidArgument The input argument is invalid.
348  * retval kStatus_ASRCQueueFull ASRC EDMA driver queue is full.
349  */
ASRC_TransferEDMA(ASRC_Type * base,asrc_edma_handle_t * handle,asrc_transfer_t * xfer)350 status_t ASRC_TransferEDMA(ASRC_Type *base, asrc_edma_handle_t *handle, asrc_transfer_t *xfer)
351 {
352     assert(handle != NULL);
353 
354     if (ASRC_TransferOutSubmitEDMA(base, handle, xfer->outData, xfer->outDataSize) != kStatus_Success)
355     {
356         return kStatus_ASRCQueueFull;
357     }
358 
359     if (ASRC_TransferInSubmitEDMA(base, handle, xfer->inData, xfer->inDataSize) != kStatus_Success)
360     {
361         return kStatus_ASRCQueueFull;
362     }
363 
364     return kStatus_Success;
365 }
366 
367 /*!
368  * brief Aborts a ASRC IN transfer using eDMA.
369  *
370  * This function only aborts the current transfer slots, the other transfer slots' information still kept
371  * in the handler. If users want to terminate all transfer slots, just call ASRC_TransferTerminalP2PEDMA.
372  *
373  * param base ASRC base pointer.
374  * param handle ASRC eDMA handle pointer.
375  */
ASRC_TransferInAbortEDMA(ASRC_Type * base,asrc_edma_handle_t * handle)376 void ASRC_TransferInAbortEDMA(ASRC_Type *base, asrc_edma_handle_t *handle)
377 {
378     assert(handle != NULL);
379 
380     /* Disable dma */
381     EDMA_AbortTransfer(handle->in.inDmaHandle);
382 
383     /* Handle the queue index */
384     handle->in.asrcQueue[handle->in.queueDriver] = NULL;
385     handle->in.queueDriver                       = (handle->in.queueDriver + 1U) % ASRC_XFER_QUEUE_SIZE;
386 
387     /* Set the handle state */
388     handle->in.state = kStatus_ASRCIdle;
389 }
390 
391 /*!
392  * brief Aborts a ASRC OUT transfer using eDMA.
393  *
394  * This function only aborts the current transfer slots, the other transfer slots' information still kept
395  * in the handler. If users want to terminate all transfer slots, just call ASRC_TransferTerminalP2PEDMA.
396  *
397  * param base ASRC base pointer.
398  * param handle ASRC eDMA handle pointer.
399  */
ASRC_TransferOutAbortEDMA(ASRC_Type * base,asrc_edma_handle_t * handle)400 void ASRC_TransferOutAbortEDMA(ASRC_Type *base, asrc_edma_handle_t *handle)
401 {
402     assert(handle != NULL);
403 
404     /* Disable dma */
405     EDMA_AbortTransfer(handle->out.outDmaHandle);
406 
407     /* Handle the queue index */
408     handle->out.asrcQueue[handle->out.queueDriver] = NULL;
409     handle->out.queueDriver                        = (handle->out.queueDriver + 1U) % ASRC_XFER_QUEUE_SIZE;
410 
411     /* Set the handle state */
412     handle->out.state = kStatus_ASRCIdle;
413 }
414 
415 /*!
416  * brief Terminate In ASRC Convert.
417  *
418  * This function will clear all transfer slots buffered in the asrc queue. If users only want to abort the
419  * current transfer slot, please call ASRC_TransferAbortPP2PEDMA.
420  *
421  * param base ASRC base pointer.
422  * param handle ASRC eDMA handle pointer.
423  */
ASRC_TransferInTerminalEDMA(ASRC_Type * base,asrc_edma_handle_t * handle)424 void ASRC_TransferInTerminalEDMA(ASRC_Type *base, asrc_edma_handle_t *handle)
425 {
426     assert(handle != NULL);
427 
428     /* Abort the current transfer */
429     ASRC_TransferInAbortEDMA(base, handle);
430     /* stop peripheral */
431     if ((handle->in.peripheralConfig != NULL) && (handle->in.peripheralConfig->startPeripheral != NULL))
432     {
433         handle->in.peripheralConfig->startPeripheral(false);
434     }
435 
436     /* Clear all the internal information */
437     (void)memset(handle->in.tcd, 0, sizeof(handle->in.tcd));
438     (void)memset(handle->in.asrcQueue, 0, sizeof(handle->in.asrcQueue));
439     (void)memset(handle->in.transferSize, 0, sizeof(handle->in.transferSize));
440     handle->in.queueUser   = 0U;
441     handle->in.queueDriver = 0U;
442 }
443 
444 /*!
445  * brief Terminate Out ASRC Convert.
446  *
447  * This function will clear all transfer slots buffered in the asrc queue. If users only want to abort the
448  * current transfer slot, please call ASRC_TransferAbortPP2PEDMA.
449  *
450  * param base ASRC base pointer.
451  * param handle ASRC eDMA handle pointer.
452  */
ASRC_TransferOutTerminalEDMA(ASRC_Type * base,asrc_edma_handle_t * handle)453 void ASRC_TransferOutTerminalEDMA(ASRC_Type *base, asrc_edma_handle_t *handle)
454 {
455     assert(handle != NULL);
456 
457     /* Abort the current transfer */
458     ASRC_TransferOutAbortEDMA(base, handle);
459 
460     if ((handle->out.peripheralConfig != NULL) && (handle->out.peripheralConfig->startPeripheral != NULL))
461     {
462         handle->out.peripheralConfig->startPeripheral(false);
463     }
464 
465     (void)memset(handle->out.tcd, 0, sizeof(handle->out.tcd));
466     (void)memset(handle->out.asrcQueue, 0, sizeof(handle->out.asrcQueue));
467     (void)memset(handle->out.transferSize, 0, sizeof(handle->out.transferSize));
468     handle->out.queueUser   = 0U;
469     handle->out.queueDriver = 0U;
470 }
471