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