1 /*
2  * Copyright 2019 - 2020, NXP
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 #ifndef FSL_PDM_EDMA_H_
8 #define FSL_PDM_EDMA_H_
9 
10 #include "fsl_edma.h"
11 #include "fsl_pdm.h"
12 
13 /*!
14  * @addtogroup pdm_edma PDM EDMA Driver
15  * @ingroup pdm
16  * @{
17  */
18 
19 /*******************************************************************************
20  * Definitions
21  ******************************************************************************/
22 
23 /*! @name Driver version */
24 /*! @{ */
25 #define FSL_PDM_EDMA_DRIVER_VERSION (MAKE_VERSION(2, 6, 3)) /*!< Version 2.6.3 */
26 /*! @} */
27 
28 /*! @brief the PDM enable position When calling PDM_TransferReceiveEDMA */
29 #ifndef MCUX_SDK_PDM_EDMA_PDM_ENABLE_INTERNAL
30 #define MCUX_SDK_PDM_EDMA_PDM_ENABLE_INTERNAL 1U
31 #endif
32 
33 /*! @brief PDM edma handler */
34 typedef struct _pdm_edma_handle pdm_edma_handle_t;
35 
36 /*!@brief pdm multi channel interleave type */
37 typedef enum _pdm_edma_multi_channel_interleave
38 {
39     kPDM_EDMAMultiChannelInterleavePerChannelSample =
40         0U, /*!< multi channel PDM data interleave per channel sample
41              * -------------------------------------------------------------------------
42              * |CHANNEL0 | CHANNEL1 | CHANNEL0 | CHANNEL1 | CHANNEL0 | CHANNEL 1 | ....|
43              * -------------------------------------------------------------------------
44              */
45     kPDM_EDMAMultiChannelInterleavePerChannelBlock =
46         1U, /*!< multi channel PDM data interleave per channel block
47              * ----------------------------------------------------------------------------------------------------------------------------
48              * |CHANNEL0 | CHANNEL0 | CHANNEL0 | ...... | CHANNEL1 | CHANNEL 1 | CHANNEL 1 | ....| CHANNEL2 | CHANNEL 2
49              * | CHANNEL 2 | ....|
50              * ----------------------------------------------------------------------------------------------------------------------------
51              */
52 } pdm_edma_multi_channel_interleave_t;
53 
54 /*! @brief PDM edma transfer */
55 typedef struct _pdm_edma_transfer
56 {
57     volatile uint8_t *data;                  /*!< Data start address to transfer. */
58     volatile size_t dataSize;                /*!< Total Transfer bytes size. */
59     struct _pdm_edma_transfer *linkTransfer; /*!< linked transfer configurations */
60 } pdm_edma_transfer_t;
61 
62 /*! @brief PDM eDMA transfer callback function for finish and error */
63 typedef void (*pdm_edma_callback_t)(PDM_Type *base, pdm_edma_handle_t *handle, status_t status, void *userData);
64 
65 /*! @brief PDM DMA transfer handle, users should not touch the content of the handle.*/
66 struct _pdm_edma_handle
67 {
68     edma_handle_t *dmaHandle;     /*!< DMA handler for PDM send */
69     uint8_t count;                /*!< The transfer data count in a DMA request */
70     uint32_t receivedBytes;       /*!< total transfer count */
71     uint32_t state;               /*!< Internal state for PDM eDMA transfer */
72     pdm_edma_callback_t callback; /*!< Callback for users while transfer finish or error occurs */
73     bool isLoopTransfer;          /*!< loop transfer */
74     void *userData;               /*!< User callback parameter */
75     edma_tcd_t *tcd;              /*!< TCD pool for eDMA transfer. */
76     uint32_t tcdNum;              /*!< TCD number */
77     uint32_t tcdUser;             /*!< Index for user to queue transfer. */
78     uint32_t tcdDriver;           /*!< Index for driver to get the transfer data and size */
79     volatile uint32_t tcdUsedNum; /*!< Index for user to queue transfer. */
80 
81     pdm_edma_multi_channel_interleave_t interleaveType; /*!< multi channel transfer interleave type */
82 
83     uint8_t endChannel;  /*!< The last enabled channel */
84     uint8_t channelNums; /*!< total channel numbers */
85 };
86 
87 /*******************************************************************************
88  * APIs
89  ******************************************************************************/
90 #if defined(__cplusplus)
91 extern "C" {
92 #endif
93 
94 /*!
95  * @name PDM eDMA Transactional
96  * @{
97  */
98 
99 /*!
100  * @brief Install EDMA descriptor memory.
101  *
102  * @param handle Pointer to EDMA channel transfer handle.
103  * @param tcdAddr EDMA head descriptor address.
104  * @param tcdNum EDMA link descriptor address.
105  */
106 void PDM_TransferInstallEDMATCDMemory(pdm_edma_handle_t *handle, void *tcdAddr, size_t tcdNum);
107 
108 /*!
109  * @brief Initializes the PDM Rx eDMA handle.
110  *
111  * This function initializes the PDM slave DMA handle, which can be used for other PDM master transactional APIs.
112  * Usually, for a specified PDM instance, call this API once to get the initialized handle.
113  *
114  * @param base PDM base pointer.
115  * @param handle PDM eDMA handle pointer.
116  * @param callback Pointer to user callback function.
117  * @param userData User parameter passed to the callback function.
118  * @param dmaHandle eDMA handle pointer, this handle shall be static allocated by users.
119  */
120 void PDM_TransferCreateHandleEDMA(
121     PDM_Type *base, pdm_edma_handle_t *handle, pdm_edma_callback_t callback, void *userData, edma_handle_t *dmaHandle);
122 
123 /*!
124  * @brief Initializes the multi PDM channel interleave type.
125  *
126  * This function initializes the PDM DMA handle member interleaveType, it shall be called only when application would
127  * like to use type kPDM_EDMAMultiChannelInterleavePerChannelBlock, since the default interleaveType is
128  * kPDM_EDMAMultiChannelInterleavePerChannelSample always
129  *
130  * @param handle PDM eDMA handle pointer.
131  * @param multiChannelInterleaveType Multi channel interleave type.
132  */
133 void PDM_TransferSetMultiChannelInterleaveType(pdm_edma_handle_t *handle,
134                                                pdm_edma_multi_channel_interleave_t multiChannelInterleaveType);
135 
136 /*!
137  * @brief Configures the PDM channel.
138  *
139  * @param base PDM base pointer.
140  * @param handle PDM eDMA handle pointer.
141  * @param channel channel index.
142  * @param config pdm channel configurations.
143  */
144 void PDM_TransferSetChannelConfigEDMA(PDM_Type *base,
145                                       pdm_edma_handle_t *handle,
146                                       uint32_t channel,
147                                       const pdm_channel_config_t *config);
148 
149 /*!
150  * @brief Performs a non-blocking PDM receive using eDMA.
151  *
152  * @note This interface returns immediately after the transfer initiates. Call
153  * the PDM_GetReceiveRemainingBytes to poll the transfer status and check whether the PDM transfer is finished.
154  *
155  * Mcaro MCUX_SDK_PDM_EDMA_PDM_ENABLE_INTERNAL can control whether PDM is enabled internally or externally.
156  *
157  * 1. Scatter gather case:
158  * This functio support dynamic scatter gather and staic scatter gather,
159  * a. for the dynamic scatter gather case:
160  * Application should call PDM_TransferReceiveEDMA function continuously to make sure new receive request is submit
161  * before the previous one finish. b. for the static scatter gather case: Application should use the link transfer
162  * feature and make sure a loop link transfer is provided, such as:
163  * @code pdm_edma_transfer_t pdmXfer[2] =
164  *   {
165  *       {
166  *       .data  = s_buffer,
167  *       .dataSize = BUFFER_SIZE,
168  *       .linkTransfer = &pdmXfer[1],
169  *       },
170  *
171  *       {
172  *       .data  = &s_buffer[BUFFER_SIZE],
173  *       .dataSize = BUFFER_SIZE,
174  *       .linkTransfer = &pdmXfer[0]
175  *       },
176  *   };
177  * @endcode
178  *
179  * 2. Multi channel case:
180  * This function support receive multi pdm channel data, for example, if two channel is requested,
181  * @code
182  * PDM_TransferSetChannelConfigEDMA(DEMO_PDM, &s_pdmRxHandle_0, DEMO_PDM_ENABLE_CHANNEL_0, &channelConfig);
183  * PDM_TransferSetChannelConfigEDMA(DEMO_PDM, &s_pdmRxHandle_0, DEMO_PDM_ENABLE_CHANNEL_1, &channelConfig);
184  * PDM_TransferReceiveEDMA(DEMO_PDM, &s_pdmRxHandle_0, pdmXfer);
185  * @endcode
186  * The output data will be formatted as below if handle->interleaveType =
187  * kPDM_EDMAMultiChannelInterleavePerChannelSample :
188  * -------------------------------------------------------------------------
189  * |CHANNEL0 | CHANNEL1 | CHANNEL0 | CHANNEL1 | CHANNEL0 | CHANNEL 1 | ....|
190  * -------------------------------------------------------------------------
191  *
192  * The output data will be formatted as below if handle->interleaveType = kPDM_EDMAMultiChannelInterleavePerChannelBlock
193  * :
194  * ----------------------------------------------------------------------------------------------------------------------
195  * |CHANNEL3 | CHANNEL3 | CHANNEL3 | .... | CHANNEL4 | CHANNEL 4 | CHANNEL4 |....| CHANNEL5 | CHANNEL 5 | CHANNEL5
196  * |....|
197  * ----------------------------------------------------------------------------------------------------------------------
198  * Note: the dataSize of xfer is the total data size, while application using
199  * kPDM_EDMAMultiChannelInterleavePerChannelBlock, the buffer size for each PDM channel is channelSize = dataSize /
200  * channelNums, then there are limitation for this feature,
201  * 1. 3 DMIC array: the dataSize shall be 4 * (channelSize)
202  * The addtional buffer is mandantory for edma modulo feature.
203  * 2. The kPDM_EDMAMultiChannelInterleavePerChannelBlock feature support below dmic array only,
204  *    2 DMIC array: CHANNEL3, CHANNEL4
205  *    3 DMIC array: CHANNEL3, CHANNEL4, CHANNEL5
206  *    4 DMIC array: CHANNEL3, CHANNEL4, CHANNEL5, CHANNEL6
207  * Any other combinations is not support, that is to SAY, THE FEATURE SUPPORT RECEIVE START FROM CHANNEL3 ONLY AND 4
208  * MAXIMUM DMIC CHANNELS.
209  *
210  * @param base PDM base pointer
211  * @param handle PDM eDMA handle pointer.
212  * @param xfer Pointer to DMA transfer structure.
213  * @retval kStatus_Success Start a PDM eDMA receive successfully.
214  * @retval kStatus_InvalidArgument The input argument is invalid.
215  * @retval kStatus_RxBusy PDM is busy receiving data.
216  */
217 status_t PDM_TransferReceiveEDMA(PDM_Type *base, pdm_edma_handle_t *handle, pdm_edma_transfer_t *xfer);
218 
219 /*!
220  * @brief Terminate all PDM receive.
221  *
222  * This function will clear all transfer slots buffered in the pdm queue. If users only want to abort the
223  * current transfer slot, please call PDM_TransferAbortReceiveEDMA.
224  *
225  * @param base PDM base pointer.
226  * @param handle PDM eDMA handle pointer.
227  */
228 void PDM_TransferTerminateReceiveEDMA(PDM_Type *base, pdm_edma_handle_t *handle);
229 
230 /*!
231  * @brief Aborts a PDM receive using eDMA.
232  *
233  * This function only aborts the current transfer slots, the other transfer slots' information still kept
234  * in the handler. If users want to terminate all transfer slots, just call PDM_TransferTerminateReceiveEDMA.
235  *
236  * @param base PDM base pointer
237  * @param handle PDM eDMA handle pointer.
238  */
239 void PDM_TransferAbortReceiveEDMA(PDM_Type *base, pdm_edma_handle_t *handle);
240 
241 /*!
242  * @brief Gets byte count received by PDM.
243  *
244  * @param base PDM base pointer
245  * @param handle PDM eDMA handle pointer.
246  * @param count Bytes count received by PDM.
247  * @retval kStatus_Success Succeed get the transfer count.
248  * @retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress.
249  */
250 status_t PDM_TransferGetReceiveCountEDMA(PDM_Type *base, pdm_edma_handle_t *handle, size_t *count);
251 
252 /*! @} */
253 
254 #if defined(__cplusplus)
255 }
256 #endif
257 
258 /*!
259  * @}
260  */
261 #endif
262