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