1 /*
2  * Copyright (c) 2016, Freescale Semiconductor, Inc.
3  * Copyright 2016-2022 NXP
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 #include "fsl_dmic_dma.h"
10 #include "fsl_dmic.h"
11 /*******************************************************************************
12  * Definitions
13  ******************************************************************************/
14 
15 /* Component ID definition, used by tools. */
16 #ifndef FSL_COMPONENT_ID
17 #define FSL_COMPONENT_ID "platform.drivers.dmic_dma"
18 #endif
19 
20 /*! @brief _dmic_dma_states_t DMIC transfer state, which is used for DMIC transactiaonl APIs' internal state. */
21 enum
22 {
23     kDMIC_Idle = 0x0, /*!< DMIC is idle state */
24     kDMIC_Busy        /*!< DMIC is busy tranferring data. */
25 };
26 /*******************************************************************************
27  * Prototypes
28  ******************************************************************************/
29 
30 /*******************************************************************************
31  * Variables
32  ******************************************************************************/
33 
34 /*******************************************************************************
35  * Code
36  ********************************************************************************/
37 
DMIC_TransferReceiveDMACallback(dma_handle_t * handle,void * param,bool transferDone,uint32_t intmode)38 static void DMIC_TransferReceiveDMACallback(dma_handle_t *handle, void *param, bool transferDone, uint32_t intmode)
39 {
40     assert(handle != NULL);
41     assert(param != NULL);
42 
43     dmic_dma_handle_t *dmicHandle = (dmic_dma_handle_t *)param;
44 
45     /* if no link transfer, dmic status set to IDLE. */
46     if (dmicHandle->desLink == NULL)
47     {
48         dmicHandle->state = kDMIC_Idle;
49     }
50 
51     if (dmicHandle->callback != NULL)
52     {
53         dmicHandle->callback(dmicHandle->base, dmicHandle, kStatus_DMIC_Idle, dmicHandle->userData);
54     }
55 }
56 
57 /*!
58  * brief Install DMA descriptor memory.
59  *
60  * This function used to register DMA descriptor memory for linked transfer, a typical case is ping pong
61  * transfer which will request more than one DMA descriptor memory space.
62  * User should be take care about the address of DMA descriptor pool which required align with 512BYTE.
63  *
64  * param handle Pointer to DMA channel transfer handle.
65  * param linkAddr DMA link descriptor address.
66  * param num DMA link descriptor number.
67  */
DMIC_InstallDMADescriptorMemory(dmic_dma_handle_t * handle,void * linkAddr,size_t linkNum)68 void DMIC_InstallDMADescriptorMemory(dmic_dma_handle_t *handle, void *linkAddr, size_t linkNum)
69 {
70     assert(handle != NULL);
71 
72     handle->desLink = (dma_descriptor_t *)linkAddr;
73     handle->linkNum = linkNum;
74 }
75 
76 /*!
77  * brief Initializes the DMIC handle which is used in transactional functions.
78  * param base DMIC peripheral base address.
79  * param handle Pointer to dmic_dma_handle_t structure.
80  * param callback Callback function.
81  * param userData User data.
82  * param rxDmaHandle User-requested DMA handle for RX DMA transfer.
83  */
DMIC_TransferCreateHandleDMA(DMIC_Type * base,dmic_dma_handle_t * handle,dmic_dma_transfer_callback_t callback,void * userData,dma_handle_t * rxDmaHandle)84 status_t DMIC_TransferCreateHandleDMA(DMIC_Type *base,
85                                       dmic_dma_handle_t *handle,
86                                       dmic_dma_transfer_callback_t callback,
87                                       void *userData,
88                                       dma_handle_t *rxDmaHandle)
89 {
90     assert(NULL != base);
91     assert(NULL != handle);
92     assert(NULL != rxDmaHandle);
93 
94     (void)memset(handle, 0, sizeof(*handle));
95 
96     handle->callback    = callback;
97     handle->userData    = userData;
98     handle->rxDmaHandle = rxDmaHandle;
99 
100     /* Set DMIC state to idle */
101     handle->state = kDMIC_Idle;
102     /* register callback. */
103     DMA_SetCallback(rxDmaHandle, DMIC_TransferReceiveDMACallback, handle);
104 
105     return kStatus_Success;
106 }
107 
108 /*!
109  * brief Receives data using DMA.
110  *
111  * This function receives data using DMA. This is a non-blocking function, which returns
112  * right away. When all data is received, the receive callback function is called.
113  *
114  * param base USART peripheral base address.
115  * param handle Pointer to usart_dma_handle_t structure.
116  * param xfer DMIC DMA transfer structure. See #dmic_transfer_t.
117  * param dmic_channel DMIC start channel number
118  * retval kStatus_Success
119  */
DMIC_TransferReceiveDMA(DMIC_Type * base,dmic_dma_handle_t * handle,dmic_transfer_t * xfer,uint32_t channel)120 status_t DMIC_TransferReceiveDMA(DMIC_Type *base, dmic_dma_handle_t *handle, dmic_transfer_t *xfer, uint32_t channel)
121 {
122     assert(handle != NULL);
123     assert(handle->rxDmaHandle != NULL);
124     assert(xfer != NULL);
125 
126     dma_channel_config_t transferConfig = {0U};
127     uint32_t srcAddr                    = (uint32_t)&base->CHANNEL[channel].FIFO_DATA;
128     uint32_t desNum                     = 0U;
129     dma_descriptor_t *linkDesc          = (handle->desLink != NULL) ? &(handle->desLink[desNum + 1U]) : NULL;
130     dmic_transfer_t *currentTransfer    = xfer->linkTransfer;
131     bool loopEnd = false, intA = true;
132     uint32_t interleaveWidth = kDMA_AddressInterleave0xWidth;
133 
134     if ((xfer->linkTransfer != NULL) && (handle->desLink == NULL))
135     {
136         return kStatus_InvalidArgument;
137     }
138 
139     if (handle->state == (uint8_t)kDMIC_Busy)
140     {
141         return kStatus_DMIC_Busy;
142     }
143 
144     /* Initialize DMIC channel */
145     if(handle->isChannelValid == false)
146     {
147         handle->isChannelValid = true;
148         handle->channel        = channel;
149     }
150 
151     while (currentTransfer != NULL)
152     {
153         /* set up linked descriptor */
154         DMA_SetupDescriptor(&handle->desLink[desNum],
155                             DMA_CHANNEL_XFER(currentTransfer->linkTransfer != NULL ? true : false, false, intA, !intA,
156                                              currentTransfer->dataWidth, (uint8_t)interleaveWidth,
157                                              currentTransfer->dataAddrInterleaveSize, currentTransfer->dataSize),
158                             (uint32_t *)srcAddr, currentTransfer->data, linkDesc);
159 
160         intA = intA == true ? false : true;
161         /* break for wrap transfer */
162         if (loopEnd)
163         {
164             break;
165         }
166 
167         if (++desNum == handle->linkNum)
168         {
169             return kStatus_Fail;
170         }
171 
172         linkDesc = &handle->desLink[desNum + 1U];
173 
174         currentTransfer = currentTransfer->linkTransfer;
175         /* if current transfer need wrap, then create one more descriptor, since the first descriptor cannot be used
176          * anymore, this is
177          * the limitation of the DMA module
178          */
179         if (currentTransfer == xfer)
180         {
181             linkDesc = handle->desLink; /* point to the first one */
182             loopEnd  = true;
183             continue;
184         }
185     }
186     /* transferSize make sense to non link transfer only */
187     handle->transferSize += xfer->dataSize;
188 
189     /* code to keep compatibility for the case that not use link transfer */
190     if ((xfer->dataWidth != (uint8_t)kDMA_Transfer16BitWidth) && (xfer->dataWidth != (uint8_t)kDMA_Transfer32BitWidth))
191     {
192         xfer->dataWidth = kDMA_Transfer16BitWidth; /* use 16bit width as default value */
193     }
194     /* code to keep compatibility for the case that not use link transfer*/
195     if ((xfer->dataAddrInterleaveSize == (uint8_t)kDMA_AddressInterleave0xWidth) ||
196         (xfer->dataAddrInterleaveSize > (uint8_t)kDMA_AddressInterleave4xWidth))
197     {
198         xfer->dataAddrInterleaveSize = kDMA_AddressInterleave1xWidth; /* use interleave1Xwidth as default value. */
199     }
200 
201     /* prepare channel tranfer */
202     DMA_PrepareChannelTransfer(
203         &transferConfig, (uint32_t *)srcAddr, xfer->data,
204         DMA_CHANNEL_XFER(xfer->linkTransfer == NULL ? false : true, false, intA, !intA, xfer->dataWidth,
205                          (uint8_t)interleaveWidth, xfer->dataAddrInterleaveSize, xfer->dataSize),
206         kDMA_PeripheralToMemory, NULL, handle->desLink);
207     /* Submit transfer. */
208     if (DMA_SubmitChannelTransfer(handle->rxDmaHandle, &transferConfig) == kStatus_DMA_Busy)
209     {
210         return kStatus_DMIC_Busy;
211     }
212 
213     /* enable channel */
214     DMIC_EnableChannnel(DMIC0, 1UL << channel);
215     /* enable dmic channel dma request */
216     DMIC_EnableChannelDma(DMIC0, (dmic_channel_t)channel, true);
217 
218     /* start transfer */
219     DMA_StartTransfer(handle->rxDmaHandle);
220 
221     handle->state = kDMIC_Busy;
222 
223     return kStatus_Success;
224 }
225 
226 /*!
227  * brief Aborts the received data using DMA.
228  *
229  * This function aborts the received data using DMA.
230  *
231  * param base DMIC peripheral base address
232  * param handle Pointer to dmic_dma_handle_t structure
233  */
DMIC_TransferAbortReceiveDMA(DMIC_Type * base,dmic_dma_handle_t * handle)234 void DMIC_TransferAbortReceiveDMA(DMIC_Type *base, dmic_dma_handle_t *handle)
235 {
236     assert(NULL != handle);
237     assert(NULL != handle->rxDmaHandle);
238 
239     /* Stop transfer. */
240     DMA_AbortTransfer(handle->rxDmaHandle);
241 
242     /* Disable channel */
243     base->CHANEN &= ~(1UL << (handle->channel));
244 
245     /* Disable dmic channel dma request */
246     DMIC_EnableChannelDma(base, (dmic_channel_t)(handle->channel), false);
247 
248     /* Set the handle state */
249     handle->state = kDMIC_Idle;
250 }
251 
252 /*!
253  * brief Get the number of bytes that have been received.
254  *
255  * This function gets the number of bytes that have been received.
256  * Note: Do not trying to use this api to get the number of received bytes, it make no sense to link transfer.
257  * param base DMIC peripheral base address.
258  * param handle DMIC handle pointer.
259  * param count Receive bytes count.
260  * retval kStatus_NoTransferInProgress No receive in progress.
261  * retval kStatus_InvalidArgument Parameter is invalid.
262  * retval kStatus_Success Get successfully through the parameter count;
263  */
DMIC_TransferGetReceiveCountDMA(DMIC_Type * base,dmic_dma_handle_t * handle,uint32_t * count)264 status_t DMIC_TransferGetReceiveCountDMA(DMIC_Type *base, dmic_dma_handle_t *handle, uint32_t *count)
265 {
266     assert(handle != NULL);
267     assert(handle->rxDmaHandle != NULL);
268     assert(count != NULL);
269 
270     if ((uint8_t)kDMIC_Idle == handle->state)
271     {
272         return kStatus_NoTransferInProgress;
273     }
274 
275     *count = handle->transferSize - DMA_GetRemainingBytes(handle->rxDmaHandle->base, handle->rxDmaHandle->channel);
276 
277     return kStatus_Success;
278 }
279