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