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