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