1 /*
2 * Copyright (c) 2015, Freescale Semiconductor, Inc.
3 * Copyright 2016-2018 NXP
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8
9 #include "fsl_flexio_i2s_dma.h"
10
11 /* Component ID definition, used by tools. */
12 #ifndef FSL_COMPONENT_ID
13 #define FSL_COMPONENT_ID "platform.drivers.flexio_i2s_dma"
14 #endif
15
16 /*******************************************************************************
17 * Definitations
18 ******************************************************************************/
19
20 /*<! Structure definition for flexio_i2s_dma_private_handle_t. The structure is private. */
21 typedef struct _flexio_i2s_dma_private_handle
22 {
23 FLEXIO_I2S_Type *base;
24 flexio_i2s_dma_handle_t *handle;
25 } flexio_i2s_dma_private_handle_t;
26
27 /*!brief _flexio_i2s_dma_transfer_state */
28 enum
29 {
30 kFLEXIO_I2S_Busy = 0x0U, /*!< FLEXIO I2S is busy */
31 kFLEXIO_I2S_Idle, /*!< Transfer is done. */
32 };
33
34 /*<! Private handle only used for internally. */
35 static flexio_i2s_dma_private_handle_t s_dmaPrivateHandle[2];
36
37 /*******************************************************************************
38 * Prototypes
39 ******************************************************************************/
40 /*!
41 * @brief FLEXIO I2S DMA callback for send.
42 *
43 * @param handle pointer to flexio_i2s_dma_handle_t structure which stores the transfer state.
44 * @param userData Parameter for user callback.
45 */
46 static void FLEXIO_I2S_TxDMACallback(dma_handle_t *handle, void *userData);
47
48 /*!
49 * @brief FLEXIO I2S DMA callback for receive.
50 *
51 * @param handle pointer to flexio_i2s_dma_handle_t structure which stores the transfer state.
52 * @param userData Parameter for user callback.
53 */
54 static void FLEXIO_I2S_RxDMACallback(dma_handle_t *handle, void *userData);
55
56 /*******************************************************************************
57 * Code
58 ******************************************************************************/
FLEXIO_I2S_TxDMACallback(dma_handle_t * handle,void * userData)59 static void FLEXIO_I2S_TxDMACallback(dma_handle_t *handle, void *userData)
60 {
61 flexio_i2s_dma_private_handle_t *privHandle = (flexio_i2s_dma_private_handle_t *)userData;
62 flexio_i2s_dma_handle_t *flexio_i2sHandle = privHandle->handle;
63
64 /* If finished a block, call the callback function */
65 (void)memset(&flexio_i2sHandle->queue[flexio_i2sHandle->queueDriver], 0, sizeof(flexio_i2s_transfer_t));
66 flexio_i2sHandle->queueDriver = (flexio_i2sHandle->queueDriver + 1U) % FLEXIO_I2S_XFER_QUEUE_SIZE;
67 if (flexio_i2sHandle->callback != NULL)
68 {
69 (flexio_i2sHandle->callback)(privHandle->base, flexio_i2sHandle, kStatus_Success, flexio_i2sHandle->userData);
70 }
71
72 /* If all data finished, just stop the transfer */
73 if (flexio_i2sHandle->queue[flexio_i2sHandle->queueDriver].data == NULL)
74 {
75 FLEXIO_I2S_TransferAbortSendDMA(privHandle->base, flexio_i2sHandle);
76 }
77 }
78
FLEXIO_I2S_RxDMACallback(dma_handle_t * handle,void * userData)79 static void FLEXIO_I2S_RxDMACallback(dma_handle_t *handle, void *userData)
80 {
81 flexio_i2s_dma_private_handle_t *privHandle = (flexio_i2s_dma_private_handle_t *)userData;
82 flexio_i2s_dma_handle_t *flexio_i2sHandle = privHandle->handle;
83
84 /* If finished a block, call the callback function */
85 (void)memset(&flexio_i2sHandle->queue[flexio_i2sHandle->queueDriver], 0, sizeof(flexio_i2s_transfer_t));
86 flexio_i2sHandle->queueDriver = (flexio_i2sHandle->queueDriver + 1U) % FLEXIO_I2S_XFER_QUEUE_SIZE;
87 if (flexio_i2sHandle->callback != NULL)
88 {
89 (flexio_i2sHandle->callback)(privHandle->base, flexio_i2sHandle, kStatus_Success, flexio_i2sHandle->userData);
90 }
91
92 /* If all data finished, just stop the transfer */
93 if (flexio_i2sHandle->queue[flexio_i2sHandle->queueDriver].data == NULL)
94 {
95 FLEXIO_I2S_TransferAbortReceiveDMA(privHandle->base, flexio_i2sHandle);
96 }
97 }
98
99 /*!
100 * brief Initializes the FlexIO I2S DMA handle.
101 *
102 * This function initializes the FlexIO I2S master DMA handle which can be used for other FlexIO I2S master
103 * transactional APIs.
104 * Usually, for a specified FlexIO I2S instance, call this API once to get the initialized handle.
105 *
106 * param base FlexIO I2S peripheral base address.
107 * param handle FlexIO I2S DMA handle pointer.
108 * param callback FlexIO I2S DMA callback function called while finished a block.
109 * param userData User parameter for callback.
110 * param dmaHandle DMA handle for FlexIO I2S. This handle is a static value allocated by users.
111 */
FLEXIO_I2S_TransferTxCreateHandleDMA(FLEXIO_I2S_Type * base,flexio_i2s_dma_handle_t * handle,flexio_i2s_dma_callback_t callback,void * userData,dma_handle_t * dmaHandle)112 void FLEXIO_I2S_TransferTxCreateHandleDMA(FLEXIO_I2S_Type *base,
113 flexio_i2s_dma_handle_t *handle,
114 flexio_i2s_dma_callback_t callback,
115 void *userData,
116 dma_handle_t *dmaHandle)
117 {
118 assert((handle != NULL) && (dmaHandle != NULL));
119
120 /* Zero the handle */
121 (void)memset(handle, 0, sizeof(*handle));
122
123 /* Set flexio_i2s base to handle */
124 handle->dmaHandle = dmaHandle;
125 handle->callback = callback;
126 handle->userData = userData;
127
128 /* Set FLEXIO I2S state to idle */
129 handle->state = (uint32_t)kFLEXIO_I2S_Idle;
130
131 s_dmaPrivateHandle[0].base = base;
132 s_dmaPrivateHandle[0].handle = handle;
133
134 /* Install callback for Tx dma channel */
135 DMA_SetCallback(dmaHandle, FLEXIO_I2S_TxDMACallback, &s_dmaPrivateHandle[0]);
136 }
137
138 /*!
139 * brief Initializes the FlexIO I2S Rx DMA handle.
140 *
141 * This function initializes the FlexIO I2S slave DMA handle which can be used for other FlexIO I2S master transactional
142 * APIs.
143 * Usually, for a specified FlexIO I2S instance, call this API once to get the initialized handle.
144 *
145 * param base FlexIO I2S peripheral base address.
146 * param handle FlexIO I2S DMA handle pointer.
147 * param callback FlexIO I2S DMA callback function called while finished a block.
148 * param userData User parameter for callback.
149 * param dmaHandle DMA handle for FlexIO I2S. This handle is a static value allocated by users.
150 */
FLEXIO_I2S_TransferRxCreateHandleDMA(FLEXIO_I2S_Type * base,flexio_i2s_dma_handle_t * handle,flexio_i2s_dma_callback_t callback,void * userData,dma_handle_t * dmaHandle)151 void FLEXIO_I2S_TransferRxCreateHandleDMA(FLEXIO_I2S_Type *base,
152 flexio_i2s_dma_handle_t *handle,
153 flexio_i2s_dma_callback_t callback,
154 void *userData,
155 dma_handle_t *dmaHandle)
156 {
157 assert((handle != NULL) && (dmaHandle != NULL));
158
159 /* Zero the handle */
160 (void)memset(handle, 0, sizeof(*handle));
161
162 /* Set flexio_i2s base to handle */
163 handle->dmaHandle = dmaHandle;
164 handle->callback = callback;
165 handle->userData = userData;
166
167 /* Set FLEXIO I2S state to idle */
168 handle->state = (uint32_t)kFLEXIO_I2S_Idle;
169
170 s_dmaPrivateHandle[1].base = base;
171 s_dmaPrivateHandle[1].handle = handle;
172
173 /* Install callback for Tx dma channel */
174 DMA_SetCallback(dmaHandle, FLEXIO_I2S_RxDMACallback, &s_dmaPrivateHandle[1]);
175 }
176
177 /*!
178 * brief Configures the FlexIO I2S Tx audio format.
179 *
180 * Audio format can be changed at run-time of FlexIO I2S. This function configures the sample rate and audio data
181 * format to be transferred. This function also sets the DMA parameter according to the format.
182 *
183 * param base FlexIO I2S peripheral base address.
184 * param handle FlexIO I2S DMA handle pointer
185 * param format Pointer to FlexIO I2S audio data format structure.
186 * param srcClock_Hz FlexIO I2S clock source frequency in Hz. It should be 0 while in slave mode.
187 */
FLEXIO_I2S_TransferSetFormatDMA(FLEXIO_I2S_Type * base,flexio_i2s_dma_handle_t * handle,flexio_i2s_format_t * format,uint32_t srcClock_Hz)188 void FLEXIO_I2S_TransferSetFormatDMA(FLEXIO_I2S_Type *base,
189 flexio_i2s_dma_handle_t *handle,
190 flexio_i2s_format_t *format,
191 uint32_t srcClock_Hz)
192 {
193 assert((handle != NULL) && (format != NULL));
194
195 /* Configure the audio format to FLEXIO I2S registers */
196 if (srcClock_Hz != 0U)
197 {
198 /* It is master */
199 FLEXIO_I2S_MasterSetFormat(base, format, srcClock_Hz);
200 }
201 else
202 {
203 FLEXIO_I2S_SlaveSetFormat(base, format);
204 }
205
206 /* Get the transfer size from format, this should be used in DMA configuration */
207 handle->bytesPerFrame = format->bitWidth / 8U;
208 }
209
210 /*!
211 * brief Performs a non-blocking FlexIO I2S transfer using DMA.
212 *
213 * note This interface returns immediately after transfer initiates. Call
214 * FLEXIO_I2S_GetTransferStatus to poll the transfer status and check whether FLEXIO I2S transfer finished.
215 *
216 * param base FlexIO I2S peripheral base address.
217 * param handle FlexIO I2S DMA handle pointer.
218 * param xfer Pointer to DMA transfer structure.
219 * retval kStatus_Success Start a FlexIO I2S DMA send successfully.
220 * retval kStatus_InvalidArgument The input arguments is invalid.
221 * retval kStatus_TxBusy FlexIO I2S is busy sending data.
222 */
FLEXIO_I2S_TransferSendDMA(FLEXIO_I2S_Type * base,flexio_i2s_dma_handle_t * handle,flexio_i2s_transfer_t * xfer)223 status_t FLEXIO_I2S_TransferSendDMA(FLEXIO_I2S_Type *base, flexio_i2s_dma_handle_t *handle, flexio_i2s_transfer_t *xfer)
224 {
225 assert((handle != NULL) && (xfer != NULL));
226
227 dma_transfer_config_t config = {0};
228 uint32_t destAddr = FLEXIO_I2S_TxGetDataRegisterAddress(base) + (4UL - handle->bytesPerFrame);
229
230 /* Check if input parameter invalid */
231 if ((xfer->data == NULL) || (xfer->dataSize == 0U))
232 {
233 return kStatus_InvalidArgument;
234 }
235
236 if (handle->queue[handle->queueUser].data != NULL)
237 {
238 return kStatus_FLEXIO_I2S_QueueFull;
239 }
240
241 /* Change the state of handle */
242 handle->state = (uint32_t)kFLEXIO_I2S_Busy;
243
244 /* Update the queue state */
245 handle->queue[handle->queueUser].data = xfer->data;
246 handle->queue[handle->queueUser].dataSize = xfer->dataSize;
247 handle->transferSize[handle->queueUser] = xfer->dataSize;
248 handle->queueUser = (handle->queueUser + 1U) % FLEXIO_I2S_XFER_QUEUE_SIZE;
249
250 DMA_PrepareTransfer(&config, xfer->data, handle->bytesPerFrame, (uint32_t *)destAddr, handle->bytesPerFrame,
251 xfer->dataSize, kDMA_MemoryToPeripheral);
252
253 /* Configure DMA channel */
254 (void)DMA_SubmitTransfer(handle->dmaHandle, &config, 1UL);
255
256 /* Start DMA transfer */
257 DMA_StartTransfer(handle->dmaHandle);
258
259 /* Enable DMA enable bit */
260 FLEXIO_I2S_TxEnableDMA(base, true);
261
262 /* Enable FLEXIO I2S Tx clock */
263 FLEXIO_I2S_Enable(base, true);
264
265 return kStatus_Success;
266 }
267
268 /*!
269 * brief Performs a non-blocking FlexIO I2S receive using DMA.
270 *
271 * note This interface returns immediately after transfer initiates. Call
272 * FLEXIO_I2S_GetReceiveRemainingBytes to poll the transfer status to check whether the FlexIO I2S transfer is finished.
273 *
274 * param base FlexIO I2S peripheral base address.
275 * param handle FlexIO I2S DMA handle pointer.
276 * param xfer Pointer to DMA transfer structure.
277 * retval kStatus_Success Start a FlexIO I2S DMA receive successfully.
278 * retval kStatus_InvalidArgument The input arguments is invalid.
279 * retval kStatus_RxBusy FlexIO I2S is busy receiving data.
280 */
FLEXIO_I2S_TransferReceiveDMA(FLEXIO_I2S_Type * base,flexio_i2s_dma_handle_t * handle,flexio_i2s_transfer_t * xfer)281 status_t FLEXIO_I2S_TransferReceiveDMA(FLEXIO_I2S_Type *base,
282 flexio_i2s_dma_handle_t *handle,
283 flexio_i2s_transfer_t *xfer)
284 {
285 assert((handle != NULL) && (xfer != NULL));
286
287 dma_transfer_config_t config = {0};
288 uint32_t srcAddr = FLEXIO_I2S_RxGetDataRegisterAddress(base);
289
290 /* Check if input parameter invalid */
291 if ((xfer->data == NULL) || (xfer->dataSize == 0U))
292 {
293 return kStatus_InvalidArgument;
294 }
295
296 if (handle->queue[handle->queueUser].data != NULL)
297 {
298 return kStatus_FLEXIO_I2S_QueueFull;
299 }
300
301 /* Change the state of handle */
302 handle->state = (uint32_t)kFLEXIO_I2S_Busy;
303
304 /* Update queue state */
305 handle->queue[handle->queueUser].data = xfer->data;
306 handle->queue[handle->queueUser].dataSize = xfer->dataSize;
307 handle->transferSize[handle->queueUser] = xfer->dataSize;
308 handle->queueUser = (handle->queueUser + 1U) % FLEXIO_I2S_XFER_QUEUE_SIZE;
309
310 /* Prepare dma configure */
311 DMA_PrepareTransfer(&config, (uint32_t *)srcAddr, handle->bytesPerFrame, xfer->data, handle->bytesPerFrame,
312 xfer->dataSize, kDMA_PeripheralToMemory);
313
314 (void)DMA_SubmitTransfer(handle->dmaHandle, &config, 1UL);
315
316 /* Start DMA transfer */
317 DMA_StartTransfer(handle->dmaHandle);
318
319 /* Enable DMA enable bit */
320 FLEXIO_I2S_RxEnableDMA(base, true);
321
322 /* Enable FLEXIO I2S Rx clock */
323 FLEXIO_I2S_Enable(base, true);
324
325 return kStatus_Success;
326 }
327
328 /*!
329 * brief Aborts a FlexIO I2S transfer using DMA.
330 *
331 * param base FlexIO I2S peripheral base address.
332 * param handle FlexIO I2S DMA handle pointer.
333 */
FLEXIO_I2S_TransferAbortSendDMA(FLEXIO_I2S_Type * base,flexio_i2s_dma_handle_t * handle)334 void FLEXIO_I2S_TransferAbortSendDMA(FLEXIO_I2S_Type *base, flexio_i2s_dma_handle_t *handle)
335 {
336 assert(handle != NULL);
337
338 /* Disable dma */
339 DMA_AbortTransfer(handle->dmaHandle);
340
341 /* Disable DMA enable bit */
342 FLEXIO_I2S_TxEnableDMA(base, false);
343
344 /* Set the handle state */
345 handle->state = (uint32_t)kFLEXIO_I2S_Idle;
346 }
347
348 /*!
349 * brief Aborts a FlexIO I2S receive using DMA.
350 *
351 * param base FlexIO I2S peripheral base address.
352 * param handle FlexIO I2S DMA handle pointer.
353 */
FLEXIO_I2S_TransferAbortReceiveDMA(FLEXIO_I2S_Type * base,flexio_i2s_dma_handle_t * handle)354 void FLEXIO_I2S_TransferAbortReceiveDMA(FLEXIO_I2S_Type *base, flexio_i2s_dma_handle_t *handle)
355 {
356 assert(handle != NULL);
357
358 /* Disable dma */
359 DMA_AbortTransfer(handle->dmaHandle);
360
361 /* Disable DMA enable bit */
362 FLEXIO_I2S_RxEnableDMA(base, false);
363
364 /* Set the handle state */
365 handle->state = (uint32_t)kFLEXIO_I2S_Idle;
366 }
367
368 /*!
369 * brief Gets the remaining bytes to be sent.
370 *
371 * param base FlexIO I2S peripheral base address.
372 * param handle FlexIO I2S DMA handle pointer.
373 * param count Bytes sent.
374 * retval kStatus_Success Succeed get the transfer count.
375 * retval kStatus_NoTransferInProgress There is not a non-blocking transaction currently in progress.
376 */
FLEXIO_I2S_TransferGetSendCountDMA(FLEXIO_I2S_Type * base,flexio_i2s_dma_handle_t * handle,size_t * count)377 status_t FLEXIO_I2S_TransferGetSendCountDMA(FLEXIO_I2S_Type *base, flexio_i2s_dma_handle_t *handle, size_t *count)
378 {
379 assert(handle != NULL);
380
381 status_t status = kStatus_Success;
382
383 if (handle->state != (uint32_t)kFLEXIO_I2S_Busy)
384 {
385 status = kStatus_NoTransferInProgress;
386 }
387 else
388 {
389 *count = handle->transferSize[handle->queueDriver] -
390 DMA_GetRemainingBytes(handle->dmaHandle->base, handle->dmaHandle->channel);
391 }
392
393 return status;
394 }
395
396 /*!
397 * brief Gets the remaining bytes to be received.
398 *
399 * param base FlexIO I2S peripheral base address.
400 * param handle FlexIO I2S DMA handle pointer.
401 * param count Bytes received.
402 * retval kStatus_Success Succeed get the transfer count.
403 * retval kStatus_NoTransferInProgress There is not a non-blocking transaction currently in progress.
404 */
FLEXIO_I2S_TransferGetReceiveCountDMA(FLEXIO_I2S_Type * base,flexio_i2s_dma_handle_t * handle,size_t * count)405 status_t FLEXIO_I2S_TransferGetReceiveCountDMA(FLEXIO_I2S_Type *base, flexio_i2s_dma_handle_t *handle, size_t *count)
406 {
407 assert(handle != NULL);
408
409 status_t status = kStatus_Success;
410
411 if (handle->state != (uint32_t)kFLEXIO_I2S_Busy)
412 {
413 status = kStatus_NoTransferInProgress;
414 }
415 else
416 {
417 *count = handle->transferSize[handle->queueDriver] -
418 DMA_GetRemainingBytes(handle->dmaHandle->base, handle->dmaHandle->channel);
419 }
420
421 return status;
422 }
423