1 /*
2  * Copyright (c) 2018, Freescale Semiconductor, Inc.
3  * Copyright 2019-2020 NXP
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 #include "fsl_pdm.h"
10 /* Component ID definition, used by tools. */
11 #ifndef FSL_COMPONENT_ID
12 #define FSL_COMPONENT_ID "platform.drivers.pdm"
13 #endif
14 
15 /*******************************************************************************
16  * Definitations
17  ******************************************************************************/
18 /*! @brief Typedef for pdm rx interrupt handler. */
19 typedef void (*pdm_isr_t)(PDM_Type *base, pdm_handle_t *pdmHandle);
20 /*******************************************************************************
21  * Prototypes
22  ******************************************************************************/
23 #if !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV)
24 /*!
25  * @brief Get the instance number for PDM.
26  *
27  * @param channelMask enabled channel.
28  * @param qualitymode selected quality mode.
29  * @param osr      oversample rate.
30  * @param regdiv   register divider.
31  */
32 static status_t PDM_ValidateSrcClockRate(uint32_t channelMask,
33                                          pdm_df_quality_mode_t qualityMode,
34                                          uint8_t osr,
35                                          uint32_t regDiv);
36 #endif
37 
38 /*******************************************************************************
39  * Variables
40  ******************************************************************************/
41 /* Base pointer array */
42 static PDM_Type *const s_pdmBases[] = PDM_BASE_PTRS;
43 /*!@brief PDM handle pointer */
44 static pdm_handle_t *s_pdmHandle[ARRAY_SIZE(s_pdmBases)];
45 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
46 /* Clock name array */
47 static const clock_ip_name_t s_pdmClock[] = PDM_CLOCKS;
48 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
49 
50 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
51 #if defined(FSL_PDM_HAS_FILTER_CLOCK_GATE) && FSL_PDM_HAS_FILTER_CLOCK_GATE
52 /* Clock name array */
53 static const clock_ip_name_t s_pdmFilterClock[] = PDM_FILTER_CLOCKS;
54 #endif
55 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
56 
57 /*! @brief Pointer to tx IRQ handler for each instance. */
58 static pdm_isr_t s_pdmIsr;
59 #if !(defined(FSL_FEATURE_PDM_HAS_NO_HWVAD) && FSL_FEATURE_PDM_HAS_NO_HWVAD)
60 /*! @brief callback for hwvad. */
61 static pdm_hwvad_notification_t s_pdm_hwvad_notification[ARRAY_SIZE(s_pdmBases)];
62 #endif
63 /*******************************************************************************
64  * Code
65  ******************************************************************************/
PDM_GetInstance(PDM_Type * base)66 uint32_t PDM_GetInstance(PDM_Type *base)
67 {
68     uint32_t instance;
69 
70     /* Find the instance index from base address mappings. */
71     for (instance = 0; instance < ARRAY_SIZE(s_pdmBases); instance++)
72     {
73         if (s_pdmBases[instance] == base)
74         {
75             break;
76         }
77     }
78 
79     assert(instance < ARRAY_SIZE(s_pdmBases));
80 
81     return instance;
82 }
83 
84 /*!
85  * brief PDM read fifo.
86  * Note: This function support 16 bit only for IP version that only supports 16bit.
87  *
88  * param base PDM base pointer.
89  * param startChannel start channel number.
90  * param channelNums total enabled channelnums.
91  * param buffer received buffer address.
92  * param size number of samples to read.
93  * param dataWidth sample width.
94  */
PDM_ReadFifo(PDM_Type * base,uint32_t startChannel,uint32_t channelNums,void * buffer,size_t size,uint32_t dataWidth)95 void PDM_ReadFifo(
96     PDM_Type *base, uint32_t startChannel, uint32_t channelNums, void *buffer, size_t size, uint32_t dataWidth)
97 {
98     uint32_t i = 0, j = 0U;
99     uint32_t *dataAddr = (uint32_t *)buffer;
100 
101     for (i = 0U; i < size; i++)
102     {
103         for (j = 0; j < channelNums; j++)
104         {
105 #if defined(FSL_FEATURE_PDM_FIFO_WIDTH) && (FSL_FEATURE_PDM_FIFO_WIDTH != 2U)
106             *dataAddr = base->DATACH[startChannel + j] >> (dataWidth == 4U ? 0U : 8U);
107             dataAddr  = (uint32_t *)((uint32_t)dataAddr + dataWidth);
108 #else
109             *dataAddr = base->DATACH[startChannel + j];
110             dataAddr  = (uint32_t *)((uint32_t)dataAddr + 2U);
111 #endif
112         }
113     }
114 }
115 
116 #if defined(FSL_FEATURE_PDM_FIFO_WIDTH) && (FSL_FEATURE_PDM_FIFO_WIDTH == 2U)
117 /*!
118  * brief PDM read data non blocking, only support 16bit data read.
119  * So the actually read data byte size in this function is (size * 2 * channelNums).
120  * param base PDM base pointer.
121  * param startChannel start channel number.
122  * param channelNums total enabled channelnums.
123  * param buffer received buffer address.
124  * param size number of 16bit data to read.
125  */
PDM_ReadNonBlocking(PDM_Type * base,uint32_t startChannel,uint32_t channelNums,int16_t * buffer,size_t size)126 void PDM_ReadNonBlocking(PDM_Type *base, uint32_t startChannel, uint32_t channelNums, int16_t *buffer, size_t size)
127 {
128     uint32_t i = 0, j = 0U;
129 
130     for (i = 0U; i < size; i++)
131     {
132         for (j = 0; j < channelNums; j++)
133         {
134             *buffer++ = (int16_t)base->DATACH[startChannel + j];
135         }
136     }
137 }
138 #endif
139 
140 #if !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV)
PDM_ValidateSrcClockRate(uint32_t channelMask,pdm_df_quality_mode_t qualityMode,uint8_t osr,uint32_t regDiv)141 static status_t PDM_ValidateSrcClockRate(uint32_t channelMask,
142                                          pdm_df_quality_mode_t qualityMode,
143                                          uint8_t osr,
144                                          uint32_t regDiv)
145 {
146     uint32_t enabledChannel = 0U, i = 0U, factor = 0U, k = 0U;
147 
148     for (i = 0U; i < (uint32_t)FSL_FEATURE_PDM_CHANNEL_NUM; i++)
149     {
150         if (((channelMask >> i) & 0x01U) != 0U)
151         {
152             enabledChannel++;
153         }
154     }
155 
156     switch (qualityMode)
157     {
158         case kPDM_QualityModeMedium:
159             factor = FSL_FEATURE_PDM_HIGH_QUALITY_CLKDIV_FACTOR;
160             k      = 2U;
161             break;
162 
163         case kPDM_QualityModeHigh:
164             factor = FSL_FEATURE_PDM_HIGH_QUALITY_CLKDIV_FACTOR;
165             k      = 1U;
166             break;
167 
168         case kPDM_QualityModeLow:
169             factor = FSL_FEATURE_PDM_HIGH_QUALITY_CLKDIV_FACTOR;
170             k      = 4U;
171             break;
172 
173         case kPDM_QualityModeVeryLow0:
174             factor = FSL_FEATURE_PDM_VERY_LOW_QUALITY_CLKDIV_FACTOR;
175             k      = 2U;
176             break;
177 
178         case kPDM_QualityModeVeryLow1:
179             factor = FSL_FEATURE_PDM_VERY_LOW_QUALITY_CLKDIV_FACTOR;
180             k      = 4U;
181             break;
182 
183         case kPDM_QualityModeVeryLow2:
184             factor = FSL_FEATURE_PDM_VERY_LOW_QUALITY_CLKDIV_FACTOR;
185             k      = 8U;
186             break;
187 
188         default:
189             assert(false);
190             break;
191     }
192 
193     /* validate the minimum clock divider */
194     /* 2U is for canculating k, 100U is for determing the specific float number of clock divider */
195     if (((regDiv * k) / 2U * 100U) < (((10U + factor * enabledChannel) * 100U / (8U * osr)) * k / 2U))
196     {
197         return kStatus_Fail;
198     }
199 
200     return kStatus_Success;
201 }
202 #endif
203 
204 /*!
205  * brief PDM set sample rate.
206  *
207  * note This function is depend on the configuration of the PDM and PDM channel, so the correct call sequence is
208  * code
209  * PDM_Init(base, pdmConfig)
210  * PDM_SetChannelConfig(base, channel, &channelConfig)
211  * PDM_SetSampleRateConfig(base, source, sampleRate)
212  * endcode
213  * param base PDM base pointer
214  * param sourceClock_HZ PDM source clock frequency.
215  * param sampleRate_HZ PDM sample rate.
216  */
PDM_SetSampleRateConfig(PDM_Type * base,uint32_t sourceClock_HZ,uint32_t sampleRate_HZ)217 status_t PDM_SetSampleRateConfig(PDM_Type *base, uint32_t sourceClock_HZ, uint32_t sampleRate_HZ)
218 {
219     uint32_t osr = (base->CTRL_2 & PDM_CTRL_2_CICOSR_MASK) >> PDM_CTRL_2_CICOSR_SHIFT;
220 #if !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV)
221     pdm_df_quality_mode_t qualityMode =
222         (pdm_df_quality_mode_t)(uint32_t)((base->CTRL_2 & PDM_CTRL_2_QSEL_MASK) >> PDM_CTRL_2_QSEL_SHIFT);
223     uint32_t enabledChannelMask = base->CTRL_1 & (uint32_t)kPDM_EnableChannelAll;
224 #endif
225 
226     uint32_t pdmClockRate = 0U;
227     uint32_t regDiv       = 0U;
228 
229     /* get divider */
230     osr          = (PDM_CTRL_2_CICOSR_MASK >> PDM_CTRL_2_CICOSR_SHIFT) + 1U - osr;
231     pdmClockRate = sampleRate_HZ * osr * 8U;
232     regDiv       = sourceClock_HZ / pdmClockRate;
233 
234     if (regDiv > PDM_CTRL_2_CLKDIV_MASK)
235     {
236         return kStatus_Fail;
237     }
238 
239 #if !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV)
240     if (PDM_ValidateSrcClockRate(enabledChannelMask, qualityMode, (uint8_t)osr, regDiv) == kStatus_Fail)
241     {
242         return kStatus_Fail;
243     }
244 #endif
245 
246     base->CTRL_2 = (base->CTRL_2 & (~PDM_CTRL_2_CLKDIV_MASK)) | PDM_CTRL_2_CLKDIV(regDiv);
247 
248     return kStatus_Success;
249 }
250 
251 /*!
252  * brief PDM set sample rate.
253  *
254  * deprecated Do not use this function.  It has been superceded by @ref PDM_SetSampleRateConfig
255  * param base PDM base pointer
256  * param enableChannelMask PDM channel enable mask.
257  * param qualityMode quality mode.
258  * param osr cic oversample rate
259  * param clkDiv clock divider
260  */
PDM_SetSampleRate(PDM_Type * base,uint32_t enableChannelMask,pdm_df_quality_mode_t qualityMode,uint8_t osr,uint32_t clkDiv)261 status_t PDM_SetSampleRate(
262     PDM_Type *base, uint32_t enableChannelMask, pdm_df_quality_mode_t qualityMode, uint8_t osr, uint32_t clkDiv)
263 {
264 #if !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV)
265     uint8_t realOsr = (PDM_CTRL_2_CICOSR_MASK >> PDM_CTRL_2_CICOSR_SHIFT) + 1U -
266                       (osr & (PDM_CTRL_2_CICOSR_MASK >> PDM_CTRL_2_CICOSR_SHIFT));
267 #endif
268     uint32_t regDiv = clkDiv >> 1U;
269 
270     switch (qualityMode)
271     {
272         case kPDM_QualityModeHigh:
273             regDiv <<= 1U;
274             break;
275         case kPDM_QualityModeLow:
276         case kPDM_QualityModeVeryLow1:
277             regDiv >>= 1U;
278             break;
279         case kPDM_QualityModeVeryLow2:
280             regDiv >>= 2U;
281             break;
282         default:
283             assert(false);
284             break;
285     }
286 
287 #if !(defined FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV && FSL_FEATURE_PDM_HAS_NO_MINIMUM_CLKDIV)
288     if (PDM_ValidateSrcClockRate(enableChannelMask, qualityMode, realOsr, regDiv) == kStatus_Fail)
289     {
290         return kStatus_Fail;
291     }
292 #endif
293 
294     assert(regDiv <= PDM_CTRL_2_CLKDIV_MASK);
295     base->CTRL_2 = (base->CTRL_2 & (~PDM_CTRL_2_CLKDIV_MASK)) | PDM_CTRL_2_CLKDIV(regDiv);
296 
297     return kStatus_Success;
298 }
299 
300 /*!
301  * brief Initializes the PDM peripheral.
302  *
303  * Ungates the PDM clock, resets the module, and configures PDM with a configuration structure.
304  * The configuration structure can be custom filled or set with default values by
305  * PDM_GetDefaultConfig().
306  *
307  * note  This API should be called at the beginning of the application to use
308  * the PDM driver. Otherwise, accessing the PDM module can cause a hard fault
309  * because the clock is not enabled.
310  *
311  * param base PDM base pointer
312  * param config PDM configuration structure.
313  */
PDM_Init(PDM_Type * base,const pdm_config_t * config)314 void PDM_Init(PDM_Type *base, const pdm_config_t *config)
315 {
316     assert(config != NULL);
317     assert(config->fifoWatermark <= PDM_FIFO_CTRL_FIFOWMK_MASK);
318 
319 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
320     /* Enable the PDM clock */
321     CLOCK_EnableClock(s_pdmClock[PDM_GetInstance(base)]);
322 #if defined(FSL_PDM_HAS_FILTER_CLOCK_GATE) && FSL_PDM_HAS_FILTER_CLOCK_GATE
323     CLOCK_EnableClock(s_pdmFilterClock[PDM_GetInstance(base)]);
324 #endif
325 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
326 
327     /* Enable the module and disable the interface/all channel */
328     base->CTRL_1 &=
329         ~(PDM_CTRL_1_MDIS_MASK | PDM_CTRL_1_PDMIEN_MASK | PDM_CTRL_1_ERREN_MASK | (uint32_t)kPDM_EnableChannelAll);
330 
331     /* wait all filter stopped */
332     while ((base->STAT & PDM_STAT_BSY_FIL_MASK) != 0U)
333     {
334     }
335 
336     /* software reset */
337     base->CTRL_1 |= PDM_CTRL_1_SRES_MASK;
338 
339     /* Set the configure settings */
340 #if !(defined(FSL_FEATURE_PDM_HAS_NO_DOZEN) && FSL_FEATURE_PDM_HAS_NO_DOZEN)
341     PDM_EnableDoze(base, config->enableDoze);
342 #endif
343     base->CTRL_2 = (base->CTRL_2 & (~(PDM_CTRL_2_CICOSR_MASK | PDM_CTRL_2_QSEL_MASK))) |
344                    PDM_CTRL_2_CICOSR(config->cicOverSampleRate) | PDM_CTRL_2_QSEL(config->qualityMode);
345 
346 #if defined(FSL_FEATURE_PDM_HAS_DECIMATION_FILTER_BYPASS) && FSL_FEATURE_PDM_HAS_DECIMATION_FILTER_BYPASS
347     base->CTRL_2 = (base->CTRL_2 & ~PDM_CTRL_2_DEC_BYPASS_MASK) | PDM_CTRL_2_DEC_BYPASS(config->enableFilterBypass);
348 #endif
349     /* Set the watermark */
350     base->FIFO_CTRL = PDM_FIFO_CTRL_FIFOWMK(config->fifoWatermark);
351 }
352 
353 /*!
354  * brief De-initializes the PDM peripheral.
355  *
356  * This API gates the PDM clock. The PDM module can't operate unless PDM_Init
357  * is called to enable the clock.
358  *
359  * param base PDM base pointer
360  */
PDM_Deinit(PDM_Type * base)361 void PDM_Deinit(PDM_Type *base)
362 {
363     /* disable PDM interface */
364     PDM_DisableInterrupts(base, (uint32_t)kPDM_FIFOInterruptEnable | (uint32_t)kPDM_ErrorInterruptEnable);
365     PDM_Enable(base, false);
366 
367 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
368     CLOCK_DisableClock(s_pdmClock[PDM_GetInstance(base)]);
369 #if defined(FSL_PDM_HAS_FILTER_CLOCK_GATE) && FSL_PDM_HAS_FILTER_CLOCK_GATE
370     CLOCK_DisableClock(s_pdmFilterClock[PDM_GetInstance(base)]);
371 #endif
372 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
373 }
374 
375 /*!
376  * brief Enables the PDM interrupt requests.
377  *
378  * param base PDM base pointer
379  * param mask interrupt source
380  *     The parameter can be a combination of the following sources if defined.
381  *     arg kPDM_ErrorInterruptEnable
382  *     arg kPDM_FIFOInterruptEnable
383  */
PDM_EnableInterrupts(PDM_Type * base,uint32_t mask)384 void PDM_EnableInterrupts(PDM_Type *base, uint32_t mask)
385 {
386     if ((mask & (uint32_t)kPDM_FIFOInterruptEnable) != 0U)
387     {
388         base->CTRL_1 = (base->CTRL_1 & (~PDM_CTRL_1_DISEL_MASK)) | (uint32_t)kPDM_FIFOInterruptEnable;
389     }
390     if ((mask & (uint32_t)kPDM_ErrorInterruptEnable) != 0U)
391     {
392         base->CTRL_1 = (base->CTRL_1 & (~PDM_CTRL_1_ERREN_MASK)) | (uint32_t)kPDM_ErrorInterruptEnable;
393     }
394 }
395 
396 /*!
397  * brief PDM one channel configurations.
398  *
399  * param base PDM base pointer
400  * param config PDM channel configurations.
401  * param channel channel number.
402  * after completing the current frame in debug mode.
403  */
PDM_SetChannelConfig(PDM_Type * base,uint32_t channel,const pdm_channel_config_t * config)404 void PDM_SetChannelConfig(PDM_Type *base, uint32_t channel, const pdm_channel_config_t *config)
405 {
406     assert(config != NULL);
407     assert(channel <= (uint32_t)FSL_FEATURE_PDM_CHANNEL_NUM);
408 
409     uint32_t dcCtrl = 0U;
410 
411 #if (defined(FSL_FEATURE_PDM_HAS_DC_OUT_CTRL) && (FSL_FEATURE_PDM_HAS_DC_OUT_CTRL))
412     dcCtrl = base->DC_OUT_CTRL;
413     /* configure gain and cut off freq */
414     dcCtrl &= ~((uint32_t)PDM_DC_OUT_CTRL_DCCONFIG0_MASK << (channel << 1U));
415     dcCtrl |= (uint32_t)config->outputCutOffFreq << (channel << 1U);
416     base->DC_OUT_CTRL = dcCtrl;
417 #endif
418 
419 #if !(defined(FSL_FEATURE_PDM_DC_CTRL_VALUE_FIXED) && (FSL_FEATURE_PDM_DC_CTRL_VALUE_FIXED))
420     dcCtrl = base->DC_CTRL;
421     /* configure gain and cut off freq */
422     dcCtrl &= ~((uint32_t)PDM_DC_CTRL_DCCONFIG0_MASK << (channel << 1U));
423     dcCtrl |= (uint32_t)config->cutOffFreq << (channel << 1U);
424     base->DC_CTRL = dcCtrl;
425 #endif
426 
427     PDM_SetChannelGain(base, channel, config->gain);
428 
429     /* enable channel */
430     base->CTRL_1 |= 1UL << channel;
431 }
432 
433 /*!
434  * brief Set the PDM channel gain.
435  *
436  * Please note for different quality mode, the valid gain value is different, reference RM for detail.
437  * param base PDM base pointer.
438  * param channel PDM channel index.
439  * param gain channel gain, the register gain value range is 0 - 15.
440  */
PDM_SetChannelGain(PDM_Type * base,uint32_t channel,pdm_df_output_gain_t gain)441 void PDM_SetChannelGain(PDM_Type *base, uint32_t channel, pdm_df_output_gain_t gain)
442 {
443     assert(channel <= (uint32_t)FSL_FEATURE_PDM_CHANNEL_NUM);
444 
445 #if defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL
446     uint32_t outCtrl = base->RANGE_CTRL;
447 #else
448     uint32_t outCtrl = base->OUT_CTRL;
449 #endif
450 
451 #if defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL
452     outCtrl &= ~((uint32_t)PDM_RANGE_CTRL_RANGEADJ0_MASK << (channel << 2U));
453 #else
454     outCtrl &= ~((uint32_t)PDM_OUT_CTRL_OUTGAIN0_MASK << (channel << 2U));
455 #endif
456 
457     outCtrl |= (uint32_t)gain << (channel << 2U);
458 
459 #if defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL
460     base->RANGE_CTRL = outCtrl;
461 #else
462     base->OUT_CTRL = outCtrl;
463 #endif
464 }
465 
466 /*!
467  * brief PDM set channel transfer config.
468  *
469  * param base PDM base pointer.
470  * param handle PDM handle pointer.
471  * param channel PDM channel.
472  * param config channel config.
473  * param format data format.
474  */
PDM_TransferSetChannelConfig(PDM_Type * base,pdm_handle_t * handle,uint32_t channel,const pdm_channel_config_t * config,uint32_t format)475 status_t PDM_TransferSetChannelConfig(
476     PDM_Type *base, pdm_handle_t *handle, uint32_t channel, const pdm_channel_config_t *config, uint32_t format)
477 {
478     assert(handle != NULL);
479 
480     PDM_SetChannelConfig(base, channel, config);
481 
482     handle->format = format;
483 
484     if (handle->channelNums == 0U)
485     {
486         handle->startChannel = (uint8_t)channel;
487     }
488 
489     handle->channelNums++;
490 
491     if (handle->channelNums > (uint8_t)FSL_FEATURE_PDM_CHANNEL_NUM)
492     {
493         return kStatus_PDM_ChannelConfig_Failed;
494     }
495 
496     return kStatus_Success;
497 }
498 
499 /*!
500  * brief Initializes the PDM handle.
501  *
502  * This function initializes the handle for the PDM transactional APIs. Call
503  * this function once to get the handle initialized.
504  *
505  * param base PDM base pointer.
506  * param handle PDM handle pointer.
507  * param callback Pointer to the user callback function.
508  * param userData User parameter passed to the callback function.
509  */
PDM_TransferCreateHandle(PDM_Type * base,pdm_handle_t * handle,pdm_transfer_callback_t callback,void * userData)510 void PDM_TransferCreateHandle(PDM_Type *base, pdm_handle_t *handle, pdm_transfer_callback_t callback, void *userData)
511 {
512     assert(handle != NULL);
513 
514     /* Zero the handle */
515     (void)memset(handle, 0, sizeof(*handle));
516 
517     s_pdmHandle[PDM_GetInstance(base)] = handle;
518 
519     handle->callback  = callback;
520     handle->userData  = userData;
521     handle->watermark = (uint8_t)(base->FIFO_CTRL & PDM_FIFO_CTRL_FIFOWMK_MASK);
522 
523     /* Set the isr pointer */
524     s_pdmIsr = PDM_TransferHandleIRQ;
525 
526     /* Enable RX event IRQ */
527     (void)EnableIRQ(PDM_EVENT_IRQn);
528 #if !(defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ)
529     /* Enable FIFO error IRQ */
530     (void)EnableIRQ(PDM_ERROR_IRQn);
531 #endif
532 }
533 
534 /*!
535  * brief Performs an interrupt non-blocking receive transfer on PDM.
536  *
537  * note This API returns immediately after the transfer initiates.
538  * Call the PDM_RxGetTransferStatusIRQ to poll the transfer status and check whether
539  * the transfer is finished. If the return status is not kStatus_PDM_Busy, the transfer
540  * is finished.
541  *
542  * param base PDM base pointer
543  * param handle Pointer to the pdm_handle_t structure which stores the transfer state.
544  * param xfer Pointer to the pdm_transfer_t structure.
545  * retval kStatus_Success Successfully started the data receive.
546  * retval kStatus_PDM_Busy Previous receive still not finished.
547  */
PDM_TransferReceiveNonBlocking(PDM_Type * base,pdm_handle_t * handle,pdm_transfer_t * xfer)548 status_t PDM_TransferReceiveNonBlocking(PDM_Type *base, pdm_handle_t *handle, pdm_transfer_t *xfer)
549 {
550     assert(handle != NULL);
551 
552     /* Check if the queue is full */
553     if (handle->pdmQueue[handle->queueUser].data != NULL)
554     {
555         return kStatus_PDM_QueueFull;
556     }
557 
558     /* Add into queue */
559     handle->transferSize[handle->queueUser]      = xfer->dataSize;
560     handle->pdmQueue[handle->queueUser].data     = xfer->data;
561     handle->pdmQueue[handle->queueUser].dataSize = xfer->dataSize;
562     handle->queueUser                            = (handle->queueUser + 1U) % PDM_XFER_QUEUE_SIZE;
563 
564     /* Set state to busy */
565     handle->state = kStatus_PDM_Busy;
566 
567     /* Enable interrupt */
568     PDM_EnableInterrupts(base, (uint32_t)kPDM_FIFOInterruptEnable);
569 
570     PDM_Enable(base, true);
571 
572     return kStatus_Success;
573 }
574 
575 /*!
576  * brief Aborts the current IRQ receive.
577  *
578  * note This API can be called when an interrupt non-blocking transfer initiates
579  * to abort the transfer early.
580  *
581  * param base PDM base pointer
582  * param handle Pointer to the pdm_handle_t structure which stores the transfer state.
583  */
PDM_TransferAbortReceive(PDM_Type * base,pdm_handle_t * handle)584 void PDM_TransferAbortReceive(PDM_Type *base, pdm_handle_t *handle)
585 {
586     assert(handle != NULL);
587 
588     /* Use FIFO request interrupt and fifo error */
589     PDM_DisableInterrupts(base, (uint32_t)kPDM_FIFOInterruptEnable | (uint32_t)kPDM_ErrorInterruptEnable);
590     PDM_Enable(base, false);
591     handle->state = kStatus_PDM_Idle;
592     /* Clear the queue */
593     (void)memset(handle->pdmQueue, 0, sizeof(pdm_transfer_t) * PDM_XFER_QUEUE_SIZE);
594     handle->queueDriver = 0;
595     handle->queueUser   = 0;
596 }
597 
598 /*!
599  * brief Tx interrupt handler.
600  *
601  * param base PDM base pointer.
602  * param handle Pointer to the pdm_handle_t structure.
603  */
PDM_TransferHandleIRQ(PDM_Type * base,pdm_handle_t * handle)604 void PDM_TransferHandleIRQ(PDM_Type *base, pdm_handle_t *handle)
605 {
606     assert(handle != NULL);
607 
608 #if (defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ)
609     uint32_t status = 0U;
610 
611 #if (defined(FSL_FEATURE_PDM_HAS_STATUS_LOW_FREQ) && (FSL_FEATURE_PDM_HAS_STATUS_LOW_FREQ == 1U))
612     if (PDM_GetStatus(base) & PDM_STAT_LOWFREQF_MASK)
613     {
614         PDM_ClearStatus(base, PDM_STAT_LOWFREQF_MASK);
615         if (handle->callback != NULL)
616         {
617             (handle->callback)(base, handle, kStatus_PDM_CLK_LOW, handle->userData);
618         }
619     }
620 #endif
621     status = PDM_GetFifoStatus(base);
622     if (status != 0U)
623     {
624         PDM_ClearFIFOStatus(base, status);
625         if (handle->callback != NULL)
626         {
627             (handle->callback)(base, handle, kStatus_PDM_FIFO_ERROR, handle->userData);
628         }
629     }
630 
631 #if !(defined(FSL_FEATURE_PDM_HAS_RANGE_CTRL) && FSL_FEATURE_PDM_HAS_RANGE_CTRL)
632     status = PDM_GetOutputStatus(base);
633     if (status != 0U)
634     {
635         PDM_ClearOutputStatus(base, status);
636         if (handle->callback != NULL)
637         {
638             (handle->callback)(base, handle, kStatus_PDM_Output_ERROR, handle->userData);
639         }
640     }
641 #endif
642 #endif
643 
644     /* Handle transfer */
645     if (((base->STAT & 0xFFU) != 0U) && (handle->channelNums != 0U) &&
646         ((base->CTRL_1 & PDM_CTRL_1_DISEL_MASK) == (0x2UL << PDM_CTRL_1_DISEL_SHIFT)))
647     {
648         PDM_ClearStatus(base, 0xFFU);
649         /* Judge if the data need to transmit is less than space */
650         uint8_t size = (uint8_t)MIN((handle->pdmQueue[handle->queueDriver].dataSize),
651                                     ((uint32_t)handle->watermark * handle->channelNums * handle->format));
652 
653         PDM_ReadFifo(base, handle->startChannel, handle->channelNums,
654                      (uint8_t *)(uint32_t)handle->pdmQueue[handle->queueDriver].data,
655                      ((size_t)size / handle->channelNums / handle->format), handle->format);
656 
657         /* Update the internal counter */
658         handle->pdmQueue[handle->queueDriver].dataSize -= size;
659         handle->pdmQueue[handle->queueDriver].data = &(handle->pdmQueue[handle->queueDriver].data[size]);
660     }
661 
662     /* If finished a block, call the callback function */
663     if (handle->pdmQueue[handle->queueDriver].dataSize == 0U)
664     {
665         handle->pdmQueue[handle->queueDriver].data = NULL;
666         handle->queueDriver                        = (handle->queueDriver + 1U) % PDM_XFER_QUEUE_SIZE;
667         if (handle->callback != NULL)
668         {
669             (handle->callback)(base, handle, kStatus_PDM_Idle, handle->userData);
670         }
671     }
672 
673     /* If all data finished, just stop the transfer */
674     if (handle->pdmQueue[handle->queueDriver].data == NULL)
675     {
676         PDM_TransferAbortReceive(base, handle);
677     }
678 }
679 
680 #if !(defined(FSL_FEATURE_PDM_HAS_NO_HWVAD) && FSL_FEATURE_PDM_HAS_NO_HWVAD)
681 /*!
682  * brief set HWVAD in envelope based mode .
683  * Recommand configurations,
684  * code
685  * static const pdm_hwvad_config_t hwvadConfig = {
686  *   .channel           = 0,
687  *   .initializeTime    = 10U,
688  *   .cicOverSampleRate = 0U,
689  *   .inputGain         = 0U,
690  *   .frameTime         = 10U,
691  *   .cutOffFreq        = kPDM_HwvadHpfBypassed,
692  *   .enableFrameEnergy = false,
693  *   .enablePreFilter   = true,
694 };
695 
696  * static const pdm_hwvad_noise_filter_t noiseFilterConfig = {
697  *   .enableAutoNoiseFilter = false,
698  *   .enableNoiseMin        = true,
699  *   .enableNoiseDecimation = true,
700  *   .noiseFilterAdjustment = 0U,
701  *   .noiseGain             = 7U,
702  *   .enableNoiseDetectOR   = true,
703  * };
704  * code
705  * param base PDM base pointer.
706  * param hwvadConfig internal filter status.
707  * param noiseConfig Voice activity detector noise filter configure structure pointer.
708  * param zcdConfig Voice activity detector zero cross detector configure structure pointer .
709  * param signalGain signal gain value.
710  */
PDM_SetHwvadInEnvelopeBasedMode(PDM_Type * base,const pdm_hwvad_config_t * hwvadConfig,const pdm_hwvad_noise_filter_t * noiseConfig,const pdm_hwvad_zero_cross_detector_t * zcdConfig,uint32_t signalGain)711 void PDM_SetHwvadInEnvelopeBasedMode(PDM_Type *base,
712                                      const pdm_hwvad_config_t *hwvadConfig,
713                                      const pdm_hwvad_noise_filter_t *noiseConfig,
714                                      const pdm_hwvad_zero_cross_detector_t *zcdConfig,
715                                      uint32_t signalGain)
716 {
717     assert(hwvadConfig != NULL);
718     assert(noiseConfig != NULL);
719 
720     uint32_t i = 0U;
721 
722     PDM_SetHwvadConfig(base, hwvadConfig);
723     PDM_SetHwvadSignalFilterConfig(base, true, signalGain);
724     PDM_SetHwvadNoiseFilterConfig(base, noiseConfig);
725     PDM_EnableHwvad(base, true);
726 
727     if (NULL != zcdConfig)
728     {
729         PDM_SetHwvadZeroCrossDetectorConfig(base, zcdConfig);
730     }
731 
732     PDM_Enable(base, true);
733 
734     while (PDM_GetHwvadInitialFlag(base) != 0U)
735     {
736     }
737 
738     for (i = 0; i < 3U; i++)
739     {
740         /* set HWVAD interal filter stauts initial */
741         PDM_SetHwvadInternalFilterStatus(base, kPDM_HwvadInternalFilterInitial);
742     }
743 
744     PDM_SetHwvadInternalFilterStatus(base, kPDM_HwvadInternalFilterNormalOperation);
745 }
746 
747 /*!
748  * brief set HWVAD in energy based mode .
749  * Recommand configurations,
750  * code
751  * static const pdm_hwvad_config_t hwvadConfig = {
752  *   .channel           = 0,
753  *   .initializeTime    = 10U,
754  *   .cicOverSampleRate = 0U,
755  *   .inputGain         = 0U,
756  *   .frameTime         = 10U,
757  *   .cutOffFreq        = kPDM_HwvadHpfBypassed,
758  *   .enableFrameEnergy = true,
759  *   .enablePreFilter   = true,
760 };
761 
762  * static const pdm_hwvad_noise_filter_t noiseFilterConfig = {
763  *   .enableAutoNoiseFilter = true,
764  *   .enableNoiseMin        = false,
765  *   .enableNoiseDecimation = false,
766  *   .noiseFilterAdjustment = 0U,
767  *   .noiseGain             = 7U,
768  *   .enableNoiseDetectOR   = false,
769  * };
770  * code
771  * param base PDM base pointer.
772  * param hwvadConfig internal filter status.
773  * param noiseConfig Voice activity detector noise filter configure structure pointer.
774  * param zcdConfig Voice activity detector zero cross detector configure structure pointer .
775  * param signalGain signal gain value, signal gain value should be properly according to application.
776  */
PDM_SetHwvadInEnergyBasedMode(PDM_Type * base,const pdm_hwvad_config_t * hwvadConfig,const pdm_hwvad_noise_filter_t * noiseConfig,const pdm_hwvad_zero_cross_detector_t * zcdConfig,uint32_t signalGain)777 void PDM_SetHwvadInEnergyBasedMode(PDM_Type *base,
778                                    const pdm_hwvad_config_t *hwvadConfig,
779                                    const pdm_hwvad_noise_filter_t *noiseConfig,
780                                    const pdm_hwvad_zero_cross_detector_t *zcdConfig,
781                                    uint32_t signalGain)
782 {
783     assert(hwvadConfig != NULL);
784     assert(noiseConfig != NULL);
785 
786     PDM_SetHwvadConfig(base, hwvadConfig);
787     /* signal filter need to disable, but signal gain value should be set */
788     base->VAD0_SCONFIG = PDM_VAD0_SCONFIG_VADSGAIN(signalGain);
789     PDM_SetHwvadNoiseFilterConfig(base, noiseConfig);
790     PDM_EnableHwvad(base, true);
791 
792     if (NULL != zcdConfig)
793     {
794         PDM_SetHwvadZeroCrossDetectorConfig(base, zcdConfig);
795     }
796 
797     PDM_Enable(base, true);
798 }
799 
800 /*!
801  * brief Configure voice activity detector.
802  *
803  * param base PDM base pointer
804  * param config Voice activity detector configure structure pointer .
805  */
PDM_SetHwvadConfig(PDM_Type * base,const pdm_hwvad_config_t * config)806 void PDM_SetHwvadConfig(PDM_Type *base, const pdm_hwvad_config_t *config)
807 {
808     assert(config != NULL);
809 
810     uint32_t ctrl1 = base->VAD0_CTRL_1;
811 
812     /* Configure VAD0_CTRL_1 register */
813     ctrl1 &= ~(PDM_VAD0_CTRL_1_VADCHSEL_MASK | PDM_VAD0_CTRL_1_VADCICOSR_MASK | PDM_VAD0_CTRL_1_VADINITT_MASK);
814     ctrl1 |= (PDM_VAD0_CTRL_1_VADCHSEL(config->channel) | PDM_VAD0_CTRL_1_VADCICOSR(config->cicOverSampleRate) |
815               PDM_VAD0_CTRL_1_VADINITT(config->initializeTime));
816     base->VAD0_CTRL_1 = ctrl1;
817 
818     /* Configure VAD0_CTRL_2 register */
819     base->VAD0_CTRL_2 =
820         (PDM_VAD0_CTRL_2_VADFRENDIS((config->enableFrameEnergy == true) ? 0U : 1U) |
821          PDM_VAD0_CTRL_2_VADPREFEN(config->enablePreFilter) | PDM_VAD0_CTRL_2_VADFRAMET(config->frameTime) |
822          PDM_VAD0_CTRL_2_VADINPGAIN(config->inputGain) | PDM_VAD0_CTRL_2_VADHPF(config->cutOffFreq));
823 }
824 
825 /*!
826  * brief Configure voice activity detector signal filter.
827  *
828  * param base PDM base pointer
829  * param enableMaxBlock If signal maximum block enabled.
830  * param signalGain Gain value for the signal energy.
831  */
PDM_SetHwvadSignalFilterConfig(PDM_Type * base,bool enableMaxBlock,uint32_t signalGain)832 void PDM_SetHwvadSignalFilterConfig(PDM_Type *base, bool enableMaxBlock, uint32_t signalGain)
833 {
834     uint32_t signalConfig = base->VAD0_SCONFIG;
835 
836     signalConfig &= ~(PDM_VAD0_SCONFIG_VADSMAXEN_MASK | PDM_VAD0_SCONFIG_VADSGAIN_MASK);
837     signalConfig |= (PDM_VAD0_SCONFIG_VADSMAXEN(enableMaxBlock) | PDM_VAD0_SCONFIG_VADSGAIN(signalGain)) |
838                     PDM_VAD0_SCONFIG_VADSFILEN_MASK;
839     base->VAD0_SCONFIG = signalConfig;
840 }
841 
842 /*!
843  * brief Configure voice activity detector noise filter.
844  *
845  * param base PDM base pointer
846  * param config Voice activity detector noise filter configure structure pointer .
847  */
PDM_SetHwvadNoiseFilterConfig(PDM_Type * base,const pdm_hwvad_noise_filter_t * config)848 void PDM_SetHwvadNoiseFilterConfig(PDM_Type *base, const pdm_hwvad_noise_filter_t *config)
849 {
850     assert(config != NULL);
851 
852     base->VAD0_NCONFIG =
853         (PDM_VAD0_NCONFIG_VADNFILAUTO(config->enableAutoNoiseFilter) |
854          PDM_VAD0_NCONFIG_VADNOREN(config->enableNoiseDetectOR) | PDM_VAD0_NCONFIG_VADNMINEN(config->enableNoiseMin) |
855          PDM_VAD0_NCONFIG_VADNDECEN(config->enableNoiseDecimation) |
856          PDM_VAD0_NCONFIG_VADNFILADJ(config->noiseFilterAdjustment) | PDM_VAD0_NCONFIG_VADNGAIN(config->noiseGain));
857 }
858 
859 /*!
860  * brief Configure voice activity detector zero cross detector.
861  *
862  * param base PDM base pointer
863  * param config Voice activity detector zero cross detector configure structure pointer .
864  */
PDM_SetHwvadZeroCrossDetectorConfig(PDM_Type * base,const pdm_hwvad_zero_cross_detector_t * config)865 void PDM_SetHwvadZeroCrossDetectorConfig(PDM_Type *base, const pdm_hwvad_zero_cross_detector_t *config)
866 {
867     assert(config != NULL);
868 
869     uint32_t zcd = (base->VAD0_ZCD & (~(PDM_VAD0_ZCD_VADZCDTH_MASK | PDM_VAD0_ZCD_VADZCDADJ_MASK |
870                                         PDM_VAD0_ZCD_VADZCDAUTO_MASK | PDM_VAD0_ZCD_VADZCDAND_MASK)));
871 
872     zcd |= (PDM_VAD0_ZCD_VADZCDTH(config->threshold) | PDM_VAD0_ZCD_VADZCDADJ(config->adjustmentThreshold) |
873             PDM_VAD0_ZCD_VADZCDAUTO(config->enableAutoThreshold) | PDM_VAD0_ZCD_VADZCDAND(config->zcdAnd)) |
874            PDM_VAD0_ZCD_VADZCDEN_MASK;
875 
876     base->VAD0_ZCD = zcd;
877 }
878 
879 /*!
880  * brief   Enable/Disable  hwvad callback.
881 
882  * This function enable/disable the hwvad interrupt for the selected PDM peripheral.
883  *
884  * param base Base address of the PDM peripheral.
885  * param vadCallback callback Pointer to store callback function, should be NULL when disable.
886  * param userData user data.
887  * param enable true is enable, false is disable.
888  * retval None.
889  */
PDM_EnableHwvadInterruptCallback(PDM_Type * base,pdm_hwvad_callback_t vadCallback,void * userData,bool enable)890 void PDM_EnableHwvadInterruptCallback(PDM_Type *base, pdm_hwvad_callback_t vadCallback, void *userData, bool enable)
891 {
892     uint32_t instance = PDM_GetInstance(base);
893 
894     if (enable)
895     {
896         PDM_EnableHwvadInterrupts(base, (uint32_t)kPDM_HwvadErrorInterruptEnable | (uint32_t)kPDM_HwvadInterruptEnable);
897         NVIC_ClearPendingIRQ(PDM_HWVAD_EVENT_IRQn);
898         (void)EnableIRQ(PDM_HWVAD_EVENT_IRQn);
899 #if !(defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ)
900         NVIC_ClearPendingIRQ(PDM_HWVAD_ERROR_IRQn);
901         (void)EnableIRQ(PDM_HWVAD_ERROR_IRQn);
902 #endif
903         s_pdm_hwvad_notification[instance].callback = vadCallback;
904         s_pdm_hwvad_notification[instance].userData = userData;
905     }
906     else
907     {
908         PDM_DisableHwvadInterrupts(base,
909                                    (uint32_t)kPDM_HwvadErrorInterruptEnable | (uint32_t)kPDM_HwvadInterruptEnable);
910         (void)DisableIRQ(PDM_HWVAD_EVENT_IRQn);
911 #if !(defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ)
912         (void)DisableIRQ(PDM_HWVAD_ERROR_IRQn);
913         NVIC_ClearPendingIRQ(PDM_HWVAD_ERROR_IRQn);
914 #endif
915         s_pdm_hwvad_notification[instance].callback = NULL;
916         s_pdm_hwvad_notification[instance].userData = NULL;
917         NVIC_ClearPendingIRQ(PDM_HWVAD_EVENT_IRQn);
918     }
919 }
920 
921 #if (defined PDM)
922 void PDM_HWVAD_EVENT_DriverIRQHandler(void);
PDM_HWVAD_EVENT_DriverIRQHandler(void)923 void PDM_HWVAD_EVENT_DriverIRQHandler(void)
924 {
925     if ((PDM_GetHwvadInterruptStatusFlags(PDM) & (uint32_t)kPDM_HwvadStatusVoiceDetectFlag) != 0U)
926     {
927         PDM_ClearHwvadInterruptStatusFlags(PDM, (uint32_t)kPDM_HwvadStatusVoiceDetectFlag);
928         if (s_pdm_hwvad_notification[0].callback != NULL)
929         {
930             s_pdm_hwvad_notification[0].callback(kStatus_PDM_HWVAD_VoiceDetected, s_pdm_hwvad_notification[0].userData);
931         }
932     }
933 #if (defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ)
934     else
935     {
936         PDM_ClearHwvadInterruptStatusFlags(PDM, (uint32_t)kPDM_HwvadStatusInputSaturation);
937         if (s_pdm_hwvad_notification[0].callback != NULL)
938         {
939             s_pdm_hwvad_notification[0].callback(kStatus_PDM_HWVAD_Error, s_pdm_hwvad_notification[0].userData);
940         }
941     }
942 #endif
943     SDK_ISR_EXIT_BARRIER;
944 }
945 
946 #if !(defined FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ && FSL_FEATURE_PDM_HAS_NO_INDEPENDENT_ERROR_IRQ)
947 void PDM_HWVAD_ERROR_DriverIRQHandler(void);
PDM_HWVAD_ERROR_DriverIRQHandler(void)948 void PDM_HWVAD_ERROR_DriverIRQHandler(void)
949 {
950     PDM_ClearHwvadInterruptStatusFlags(PDM, (uint32_t)kPDM_HwvadStatusInputSaturation);
951     if (s_pdm_hwvad_notification[0].callback != NULL)
952     {
953         s_pdm_hwvad_notification[0].callback(kStatus_PDM_HWVAD_Error, s_pdm_hwvad_notification[0].userData);
954     }
955     SDK_ISR_EXIT_BARRIER;
956 }
957 #endif
958 #endif
959 #endif
960 
961 #if defined(PDM)
962 void PDM_EVENT_DriverIRQHandler(void);
PDM_EVENT_DriverIRQHandler(void)963 void PDM_EVENT_DriverIRQHandler(void)
964 {
965     assert(s_pdmHandle[0] != NULL);
966     s_pdmIsr(PDM, s_pdmHandle[0]);
967     SDK_ISR_EXIT_BARRIER;
968 }
969 #endif
970