1 /*
2 * Copyright (c) 2018, Freescale Semiconductor, Inc.
3 * Copyright 2019 NXP
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8
9 #include "fsl_pdm_sdma.h"
10 /* Component ID definition, used by tools. */
11 #ifndef FSL_COMPONENT_ID
12 #define FSL_COMPONENT_ID "platform.drivers.pdm_sdma"
13 #endif
14
15 /*******************************************************************************
16 * Definitations
17 ******************************************************************************/
18
19 /*<! Structure definition for uart_sdma_private_handle_t. The structure is private. */
20 typedef struct _pdm_sdma_private_handle
21 {
22 PDM_Type *base;
23 pdm_sdma_handle_t *handle;
24 } pdm_sdma_private_handle_t;
25
26 /* Base pointer array */
27 static PDM_Type *const s_pdmBases[] = PDM_BASE_PTRS;
28
29 /*<! Private handle only used for internally. */
30 static pdm_sdma_private_handle_t s_sdmaPrivateHandle[ARRAY_SIZE(s_pdmBases)];
31
32 /*******************************************************************************
33 * Prototypes
34 ******************************************************************************/
35 /*!
36 * @brief PDM SDMA callback for send.
37 *
38 * @param handle pointer to pdm_sdma_handle_t structure which stores the transfer state.
39 * @param userData Parameter for user callback.
40 * @param done If the DMA transfer finished.
41 * @param tcds The TCD index.
42 */
43 static void PDM_SDMACallback(sdma_handle_t *handle, void *userData, bool done, uint32_t tcds);
44 /*******************************************************************************
45 * Code
46 ******************************************************************************/
PDM_SDMACallback(sdma_handle_t * handle,void * userData,bool done,uint32_t tcds)47 static void PDM_SDMACallback(sdma_handle_t *handle, void *userData, bool done, uint32_t tcds)
48 {
49 pdm_sdma_private_handle_t *privHandle = (pdm_sdma_private_handle_t *)userData;
50 pdm_sdma_handle_t *pdmHandle = privHandle->handle;
51
52 /* If finished a block, call the callback function */
53 (void)memset(&pdmHandle->pdmQueue[pdmHandle->queueDriver], 0, sizeof(pdm_transfer_t));
54 pdmHandle->queueDriver = (pdmHandle->queueDriver + 1U) % PDM_XFER_QUEUE_SIZE;
55 if (pdmHandle->callback != NULL)
56 {
57 (pdmHandle->callback)(privHandle->base, pdmHandle, kStatus_PDM_Idle, pdmHandle->userData);
58 }
59
60 /* If all data finished, just stop the transfer */
61 if (pdmHandle->pdmQueue[pdmHandle->queueDriver].data == NULL)
62 {
63 PDM_TransferAbortReceiveSDMA(privHandle->base, pdmHandle);
64 }
65 }
66
67 /*!
68 * brief Initializes the PDM eDMA handle.
69 *
70 * This function initializes the PDM DMA handle, which can be used for other PDM master transactional APIs.
71 * Usually, for a specified PDM instance, call this API once to get the initialized handle.
72 *
73 * param base PDM base pointer.
74 * param handle PDM eDMA handle pointer.
75 * param base PDM peripheral base address.
76 * param callback Pointer to user callback function.
77 * param userData User parameter passed to the callback function.
78 * param dmaHandle eDMA handle pointer, this handle shall be static allocated by users.
79 * param dma request source.
80 */
PDM_TransferCreateHandleSDMA(PDM_Type * base,pdm_sdma_handle_t * handle,pdm_sdma_callback_t callback,void * userData,sdma_handle_t * dmaHandle,uint32_t eventSource)81 void PDM_TransferCreateHandleSDMA(PDM_Type *base,
82 pdm_sdma_handle_t *handle,
83 pdm_sdma_callback_t callback,
84 void *userData,
85 sdma_handle_t *dmaHandle,
86 uint32_t eventSource)
87 {
88 assert((handle != NULL) && (dmaHandle != NULL));
89
90 uint32_t instance = PDM_GetInstance(base);
91
92 /* Zero the handle */
93 (void)memset(handle, 0, sizeof(*handle));
94
95 /* Set pdm base to handle */
96 handle->dmaHandle = dmaHandle;
97 handle->callback = callback;
98 handle->userData = userData;
99 handle->eventSource = eventSource;
100 handle->fifoWidth = FSL_FEATURE_PDM_FIFO_WIDTH;
101
102 /* Set PDM state to idle */
103 handle->state = kStatus_PDM_Idle;
104
105 s_sdmaPrivateHandle[instance].base = base;
106 s_sdmaPrivateHandle[instance].handle = handle;
107
108 /* Need to use scatter gather */
109 SDMA_InstallBDMemory(dmaHandle, handle->bdPool, PDM_XFER_QUEUE_SIZE);
110
111 /* Install callback for Tx dma channel */
112 SDMA_SetCallback(dmaHandle, PDM_SDMACallback, &s_sdmaPrivateHandle[instance]);
113 }
114
115 /*!
116 * brief PDM channel configurations.
117 *
118 * param base PDM base pointer.
119 * param handle PDM eDMA handle pointer.
120 * param channel channel number.
121 * param config channel configurations.
122 */
PDM_SetChannelConfigSDMA(PDM_Type * base,pdm_sdma_handle_t * handle,uint32_t channel,const pdm_channel_config_t * config)123 void PDM_SetChannelConfigSDMA(PDM_Type *base,
124 pdm_sdma_handle_t *handle,
125 uint32_t channel,
126 const pdm_channel_config_t *config)
127 {
128 assert(NULL != config);
129
130 /* channel configurations */
131 PDM_SetChannelConfig(base, channel, config);
132
133 /* record end channel number */
134 handle->endChannel = (uint8_t)channel;
135 /* increase totoal enabled channel number */
136 handle->channelNums++;
137 /* increase count pre channel numbers */
138 handle->count = (uint8_t)(handle->channelNums * (base->FIFO_CTRL & PDM_FIFO_CTRL_FIFOWMK_MASK));
139 }
140
141 /*!
142 * brief Performs a non-blocking PDM receive using eDMA.
143 *
144 * note This interface returns immediately after the transfer initiates. Call
145 * the PDM_GetReceiveRemainingBytes to poll the transfer status and check whether the PDM transfer is finished.
146 *
147 * param base PDM base pointer
148 * param handle PDM eDMA handle pointer.
149 * param xfer Pointer to DMA transfer structure.
150 * retval kStatus_Success Start a PDM eDMA receive successfully.
151 * retval kStatus_InvalidArgument The input argument is invalid.
152 * retval kStatus_RxBusy PDM is busy receiving data.
153 */
PDM_TransferReceiveSDMA(PDM_Type * base,pdm_sdma_handle_t * handle,pdm_transfer_t * xfer)154 status_t PDM_TransferReceiveSDMA(PDM_Type *base, pdm_sdma_handle_t *handle, pdm_transfer_t *xfer)
155 {
156 assert((handle != NULL) && (xfer != NULL));
157 assert((xfer->dataSize % (handle->fifoWidth)) == 0U);
158
159 sdma_transfer_config_t config = {0};
160 uint32_t startAddr =
161 PDM_GetDataRegisterAddress(base, ((uint32_t)handle->endChannel - ((uint32_t)handle->channelNums - 1U)));
162 sdma_peripheral_t perType = kSDMA_PeripheralMultiFifoPDM;
163
164 /* Check if input parameter invalid */
165 if ((xfer->data == NULL) || (xfer->dataSize == 0U) || ((xfer->dataSize % (handle->fifoWidth)) != 0U))
166 {
167 return kStatus_InvalidArgument;
168 }
169
170 if (handle->pdmQueue[handle->queueUser].data != NULL)
171 {
172 return kStatus_PDM_QueueFull;
173 }
174
175 /* Update queue state */
176 handle->transferSize[handle->queueUser] = xfer->dataSize;
177 handle->pdmQueue[handle->queueUser].data = xfer->data;
178 handle->pdmQueue[handle->queueUser].dataSize = xfer->dataSize;
179
180 /* Prepare sdma configure */
181 SDMA_PrepareTransfer(&config, startAddr, (uint32_t)xfer->data, handle->fifoWidth, handle->fifoWidth,
182 (uint32_t)handle->count * handle->fifoWidth, xfer->dataSize, handle->eventSource, perType,
183 kSDMA_PeripheralToMemory);
184
185 /* multi fifo configurations */
186 SDMA_SetMultiFifoConfig(&config, handle->channelNums,
187 (uint32_t)FSL_FEATURE_PDM_FIFO_OFFSET / sizeof(uint32_t) - 1U);
188 /* enable sw done for PDM */
189 SDMA_SetDoneConfig(handle->dmaHandle->base, &config, kSDMA_PeripheralMultiFifoPDM, kSDMA_DoneSrcSW);
190
191 if (handle->queueUser == PDM_XFER_QUEUE_SIZE - 1U)
192 {
193 SDMA_ConfigBufferDescriptor(&handle->bdPool[handle->queueUser], startAddr, (uint32_t)xfer->data,
194 config.destTransferSize, xfer->dataSize, true, true, true,
195 kSDMA_PeripheralToMemory);
196 }
197 else
198 {
199 SDMA_ConfigBufferDescriptor(&handle->bdPool[handle->queueUser], startAddr, (uint32_t)xfer->data,
200 config.destTransferSize, xfer->dataSize, true, true, false,
201 kSDMA_PeripheralToMemory);
202 }
203
204 handle->queueUser = (handle->queueUser + 1U) % PDM_XFER_QUEUE_SIZE;
205
206 if (handle->state != (uint32_t)kStatus_PDM_Busy)
207 {
208 SDMA_SubmitTransfer(handle->dmaHandle, &config);
209
210 /* Start DMA transfer */
211 SDMA_StartTransfer(handle->dmaHandle);
212 }
213
214 handle->state = kStatus_PDM_Busy;
215
216 /* Enable DMA enable bit */
217 PDM_EnableDMA(base, true);
218 /* enable PDM */
219 PDM_Enable(base, true);
220
221 return kStatus_Success;
222 }
223
224 /*!
225 * brief Aborts a PDM receive using eDMA.
226 *
227 * param base PDM base pointer
228 * param handle PDM eDMA handle pointer.
229 */
PDM_TransferAbortReceiveSDMA(PDM_Type * base,pdm_sdma_handle_t * handle)230 void PDM_TransferAbortReceiveSDMA(PDM_Type *base, pdm_sdma_handle_t *handle)
231 {
232 assert(handle != NULL);
233
234 /* Disable dma */
235 SDMA_AbortTransfer(handle->dmaHandle);
236
237 /* Disable DMA enable bit */
238 PDM_EnableDMA(base, false);
239
240 /* Set the handle state */
241 handle->state = kStatus_PDM_Idle;
242 }
243
244 /*!
245 * brief Terminate all the PDM sdma receive transfer.
246 *
247 * param base PDM base pointer.
248 * param handle PDM SDMA handle pointer.
249 */
PDM_TransferTerminateReceiveSDMA(PDM_Type * base,pdm_sdma_handle_t * handle)250 void PDM_TransferTerminateReceiveSDMA(PDM_Type *base, pdm_sdma_handle_t *handle)
251 {
252 assert(handle != NULL);
253
254 /* abort current transfer */
255 PDM_TransferAbortReceiveSDMA(base, handle);
256
257 /* Clear all the internal information */
258 (void)memset(handle->bdPool, 0, sizeof(handle->bdPool));
259 (void)memset(handle->pdmQueue, 0, sizeof(handle->pdmQueue));
260 (void)memset(handle->transferSize, 0, sizeof(handle->transferSize));
261
262 handle->queueUser = 0U;
263 handle->queueDriver = 0U;
264 }
265