1 /*
2  * Copyright (c) 2016, Freescale Semiconductor, Inc.
3  * Copyright 2016-2019 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 headAddr DMA head descriptor address.
66  * param linkAddr DMA link descriptor address.
67  * param num DMA link descriptor number.
68  */
DMIC_InstallDMADescriptorMemory(dmic_dma_handle_t * handle,void * linkAddr,size_t linkNum)69 void DMIC_InstallDMADescriptorMemory(dmic_dma_handle_t *handle, void *linkAddr, size_t linkNum)
70 {
71     assert(handle != NULL);
72 
73     handle->desLink = (dma_descriptor_t *)linkAddr;
74     handle->linkNum = linkNum;
75 }
76 
77 /*!
78  * brief Initializes the DMIC handle which is used in transactional functions.
79  * param base DMIC peripheral base address.
80  * param handle Pointer to dmic_dma_handle_t structure.
81  * param callback Callback function.
82  * param userData User data.
83  * param rxDmaHandle User-requested DMA handle for RX DMA transfer.
84  */
DMIC_TransferCreateHandleDMA(DMIC_Type * base,dmic_dma_handle_t * handle,dmic_dma_transfer_callback_t callback,void * userData,dma_handle_t * rxDmaHandle)85 status_t DMIC_TransferCreateHandleDMA(DMIC_Type *base,
86                                       dmic_dma_handle_t *handle,
87                                       dmic_dma_transfer_callback_t callback,
88                                       void *userData,
89                                       dma_handle_t *rxDmaHandle)
90 {
91     assert(NULL != base);
92     assert(NULL != handle);
93     assert(NULL != rxDmaHandle);
94 
95     (void)memset(handle, 0, sizeof(*handle));
96 
97     handle->callback    = callback;
98     handle->userData    = userData;
99     handle->rxDmaHandle = rxDmaHandle;
100 
101     /* Set DMIC state to idle */
102     handle->state = kDMIC_Idle;
103     /* register callback. */
104     DMA_SetCallback(rxDmaHandle, DMIC_TransferReceiveDMACallback, handle);
105 
106     return kStatus_Success;
107 }
108 
109 /*!
110  * brief Receives data using DMA.
111  *
112  * This function receives data using DMA. This is a non-blocking function, which returns
113  * right away. When all data is received, the receive callback function is called.
114  *
115  * param base USART peripheral base address.
116  * param handle Pointer to usart_dma_handle_t structure.
117  * param xfer DMIC DMA transfer structure. See #dmic_transfer_t.
118  * param dmic_channel DMIC start channel number
119  * retval kStatus_Success
120  */
DMIC_TransferReceiveDMA(DMIC_Type * base,dmic_dma_handle_t * handle,dmic_transfer_t * xfer,uint32_t channel)121 status_t DMIC_TransferReceiveDMA(DMIC_Type *base, dmic_dma_handle_t *handle, dmic_transfer_t *xfer, uint32_t channel)
122 {
123     assert(handle != NULL);
124     assert(handle->rxDmaHandle != NULL);
125     assert(xfer != NULL);
126 
127     dma_channel_config_t transferConfig = {0U};
128     uint32_t srcAddr                    = (uint32_t)&base->CHANNEL[channel].FIFO_DATA;
129     uint32_t desNum                     = 0U;
130     dma_descriptor_t *linkDesc          = (handle->desLink != NULL) ? &(handle->desLink[desNum + 1U]) : NULL;
131     dmic_transfer_t *currentTransfer    = xfer->linkTransfer;
132     bool loopEnd = false, intA = true;
133     uint32_t interleaveWidth = kDMA_AddressInterleave0xWidth;
134 
135     if ((xfer->linkTransfer != NULL) && (handle->desLink == NULL))
136     {
137         return kStatus_InvalidArgument;
138     }
139 
140     if (handle->state == (uint8_t)kDMIC_Busy)
141     {
142         return kStatus_DMIC_Busy;
143     }
144 
145     while (currentTransfer != NULL)
146     {
147         /* set up linked descriptor */
148         DMA_SetupDescriptor(&handle->desLink[desNum],
149                             DMA_CHANNEL_XFER(currentTransfer->linkTransfer != NULL ? 1UL : 0UL, 0UL, intA, !intA,
150                                              currentTransfer->dataWidth, interleaveWidth,
151                                              currentTransfer->dataAddrInterleaveSize, currentTransfer->dataSize),
152                             (uint32_t *)srcAddr, currentTransfer->data, linkDesc);
153 
154         intA = intA == true ? false : true;
155         /* break for wrap transfer */
156         if (loopEnd)
157         {
158             break;
159         }
160 
161         if (++desNum == handle->linkNum)
162         {
163             return kStatus_Fail;
164         }
165 
166         linkDesc = &handle->desLink[desNum + 1U];
167 
168         currentTransfer = currentTransfer->linkTransfer;
169         /* if current transfer need wrap, then create one more descriptor, since the first descriptor cannot be used
170          * anymore, this is
171          * the limitation of the DMA module
172          */
173         if (currentTransfer == xfer)
174         {
175             linkDesc = handle->desLink; /* point to the first one */
176             loopEnd  = true;
177             continue;
178         }
179     }
180     /* transferSize make sense to non link transfer only */
181     handle->transferSize += xfer->dataSize;
182 
183     /* code to keep compatibility for the case that not use link transfer */
184     if ((xfer->dataWidth != (uint8_t)kDMA_Transfer16BitWidth) && (xfer->dataWidth != (uint8_t)kDMA_Transfer32BitWidth))
185     {
186         xfer->dataWidth = kDMA_Transfer16BitWidth; /* use 16bit width as default value */
187     }
188     /* code to keep compatibility for the case that not use link transfer*/
189     if ((xfer->dataAddrInterleaveSize == (uint8_t)kDMA_AddressInterleave0xWidth) ||
190         (xfer->dataAddrInterleaveSize > (uint8_t)kDMA_AddressInterleave4xWidth))
191     {
192         xfer->dataAddrInterleaveSize = kDMA_AddressInterleave1xWidth; /* use interleave1Xwidth as default value. */
193     }
194 
195     /* prepare channel tranfer */
196     DMA_PrepareChannelTransfer(
197         &transferConfig, (uint32_t *)srcAddr, xfer->data,
198         DMA_CHANNEL_XFER(xfer->linkTransfer == NULL ? 0UL : 1UL, 0UL, intA, !intA, xfer->dataWidth, interleaveWidth,
199                          xfer->dataAddrInterleaveSize, xfer->dataSize),
200         kDMA_PeripheralToMemory, NULL, handle->desLink);
201     /* Submit transfer. */
202     if (DMA_SubmitChannelTransfer(handle->rxDmaHandle, &transferConfig) == kStatus_DMA_Busy)
203     {
204         return kStatus_DMIC_Busy;
205     }
206 
207     /* enable channel */
208     DMIC_EnableChannnel(DMIC0, 1UL << channel);
209     /* enable dmic channel dma request */
210     DMIC_EnableChannelDma(DMIC0, (dmic_channel_t)channel, true);
211 
212     /* start transfer */
213     DMA_StartTransfer(handle->rxDmaHandle);
214 
215     handle->state = kDMIC_Busy;
216 
217     return kStatus_Success;
218 }
219 
220 /*!
221  * brief Aborts the received data using DMA.
222  *
223  * This function aborts the received data using DMA.
224  *
225  * param base DMIC peripheral base address
226  * param handle Pointer to dmic_dma_handle_t structure
227  */
DMIC_TransferAbortReceiveDMA(DMIC_Type * base,dmic_dma_handle_t * handle)228 void DMIC_TransferAbortReceiveDMA(DMIC_Type *base, dmic_dma_handle_t *handle)
229 {
230     assert(NULL != handle);
231     assert(NULL != handle->rxDmaHandle);
232 
233     /* Stop transfer. */
234     DMA_AbortTransfer(handle->rxDmaHandle);
235     handle->state = kDMIC_Idle;
236 }
237 
238 /*!
239  * brief Get the number of bytes that have been received.
240  *
241  * This function gets the number of bytes that have been received.
242  * Note: Do not trying to use this api to get the number of received bytes, it make no sense to link transfer.
243  * param base DMIC peripheral base address.
244  * param handle DMIC handle pointer.
245  * param count Receive bytes count.
246  * retval kStatus_NoTransferInProgress No receive in progress.
247  * retval kStatus_InvalidArgument Parameter is invalid.
248  * retval kStatus_Success Get successfully through the parameter count;
249  */
DMIC_TransferGetReceiveCountDMA(DMIC_Type * base,dmic_dma_handle_t * handle,uint32_t * count)250 status_t DMIC_TransferGetReceiveCountDMA(DMIC_Type *base, dmic_dma_handle_t *handle, uint32_t *count)
251 {
252     assert(handle != NULL);
253     assert(handle->rxDmaHandle != NULL);
254     assert(count != NULL);
255 
256     if ((uint8_t)kDMIC_Idle == handle->state)
257     {
258         return kStatus_NoTransferInProgress;
259     }
260 
261     *count = handle->transferSize - DMA_GetRemainingBytes(handle->rxDmaHandle->base, handle->rxDmaHandle->channel);
262 
263     return kStatus_Success;
264 }
265