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