1 /***************************************************************************//**
2  * @file
3  * @brief Pulse Density Modulation (PDM) peripheral API
4  *******************************************************************************
5  * # License
6  * <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
7  *******************************************************************************
8  *
9  * SPDX-License-Identifier: Zlib
10  *
11  * The licensor of this software is Silicon Laboratories Inc.
12  *
13  * This software is provided 'as-is', without any express or implied
14  * warranty. In no event will the authors be held liable for any damages
15  * arising from the use of this software.
16  *
17  * Permission is granted to anyone to use this software for any purpose,
18  * including commercial applications, and to alter it and redistribute it
19  * freely, subject to the following restrictions:
20  *
21  * 1. The origin of this software must not be misrepresented; you must not
22  *    claim that you wrote the original software. If you use this software
23  *    in a product, an acknowledgment in the product documentation would be
24  *    appreciated but is not required.
25  * 2. Altered source versions must be plainly marked as such, and must not be
26  *    misrepresented as being the original software.
27  * 3. This notice may not be removed or altered from any source distribution.
28  *
29  ******************************************************************************/
30 
31 #include "em_pdm.h"
32 #if defined(PDM_PRESENT) && (PDM_COUNT == 1)
33 
34 #include "sl_assert.h"
35 
36 /***************************************************************************//**
37  * @addtogroup pdm
38  * @{
39  ******************************************************************************/
40 
41 /***************************************************************************//**
42  * @brief
43  *   De-initialize the PDM peripheral.
44  *
45  * @details
46  *   This function will stop the PDM filter and set PDM control registers to
47  *   their reset values.
48  *
49  * @param[in] pdm
50  *   A pointer to the PDM peripheral register block.
51  ******************************************************************************/
PDM_DeInit(PDM_TypeDef * pdm)52 void PDM_DeInit(PDM_TypeDef *pdm)
53 {
54   PDM_Stop(pdm);
55   PDM_Clear(pdm);
56   PDM_FifoFlush(pdm);
57   PDM_Reset(pdm);
58 }
59 
60 /***************************************************************************//**
61  * @brief
62  *   Initialize the PDM peripheral.
63  *
64  * @details
65  *   This function will configure basic settings in PDM according to values
66  *   in the initialization data structure.
67  *
68  *   Notice that enabling of PDM clock, setup of PDM pins and setup of PRS
69  *   is not covered by this function.
70  *
71  * @param[in] pdm
72  *   A pointer to the PDM peripheral register block.
73  *
74  * @param[in] init
75  *   A pointer to the initialization structure used to configure the PDM.
76  ******************************************************************************/
PDM_Init(PDM_TypeDef * pdm,const PDM_Init_TypeDef * init)77 void PDM_Init(PDM_TypeDef *pdm, const PDM_Init_TypeDef *init)
78 {
79   EFM_ASSERT(init->dsr <= (_PDM_CTRL_DSR_MASK >> _PDM_CTRL_DSR_SHIFT));
80   EFM_ASSERT(init->gain <= (_PDM_CTRL_GAIN_MASK >> _PDM_CTRL_GAIN_SHIFT));
81   EFM_ASSERT(init->prescaler <= (_PDM_CFG1_PRESC_MASK >> _PDM_CFG1_PRESC_SHIFT));
82 
83   while (pdm->SYNCBUSY != 0U) {
84     // Wait for any pending CMD synchronization
85   }
86 
87   pdm->EN = PDM_EN_EN_DISABLE;
88 
89 #if defined(PDM_CFG0_NUMCH_THREE)
90   pdm->CFG0 = ((uint32_t)init->ch3ClkPolarity       << _PDM_CFG0_CH3CLKPOL_SHIFT)
91               | ((uint32_t)init->ch2ClkPolarity     << _PDM_CFG0_CH2CLKPOL_SHIFT)
92               | ((uint32_t)init->ch1ClkPolarity     << _PDM_CFG0_CH1CLKPOL_SHIFT)
93               | ((uint32_t)init->ch0ClkPolarity     << _PDM_CFG0_CH0CLKPOL_SHIFT)
94               | ((uint32_t)init->fifoValidWatermark << _PDM_CFG0_FIFODVL_SHIFT)
95               | ((uint32_t)init->dataFormat         << _PDM_CFG0_DATAFORMAT_SHIFT)
96               | ((uint32_t)init->numChannels        << _PDM_CFG0_NUMCH_SHIFT)
97               | ((uint32_t)init->filterOrder        << _PDM_CFG0_FORDER_SHIFT)
98               | (init->enableCh2Ch3Stereo ? PDM_CFG0_STEREOMODECH23_CH23ENABLE : 0U)
99               | (init->enableCh0Ch1Stereo ? PDM_CFG0_STEREOMODECH01_CH01ENABLE : 0U);
100 #else
101   pdm->CFG0 = ((uint32_t)init->ch1ClkPolarity       << _PDM_CFG0_CH1CLKPOL_SHIFT)
102               | ((uint32_t)init->ch0ClkPolarity     << _PDM_CFG0_CH0CLKPOL_SHIFT)
103               | ((uint32_t)init->fifoValidWatermark << _PDM_CFG0_FIFODVL_SHIFT)
104               | ((uint32_t)init->dataFormat         << _PDM_CFG0_DATAFORMAT_SHIFT)
105               | ((uint32_t)init->numChannels        << _PDM_CFG0_NUMCH_SHIFT)
106               | ((uint32_t)init->filterOrder        << _PDM_CFG0_FORDER_SHIFT)
107               | (init->enableCh0Ch1Stereo ? PDM_CFG0_STEREOMODECH01_CH01ENABLE : 0U);
108 #endif
109 
110   pdm->CFG1 = init->prescaler << _PDM_CFG1_PRESC_SHIFT;
111 
112   pdm->EN = PDM_EN_EN_ENABLE;
113 
114   pdm->CTRL = (init->dsr    << _PDM_CTRL_DSR_SHIFT)
115 #if defined(PDM_CTRL_OUTCLKEN)
116               | (init->outClkEn ? PDM_CTRL_OUTCLKEN : 0U)
117 #endif
118               | (init->gain << _PDM_CTRL_GAIN_SHIFT);
119 
120   if (init->start) {
121     PDM_Clear(pdm);
122     PDM_FifoFlush(pdm);
123     PDM_Start(pdm);
124   }
125 }
126 
127 /***************************************************************************//**
128  * @brief
129  *   Initialize PDM registers with reset values.
130  *
131  * @param[in] pdm
132  *   A pointer to the PDM peripheral register block.
133  ******************************************************************************/
PDM_Reset(PDM_TypeDef * pdm)134 void PDM_Reset(PDM_TypeDef *pdm)
135 {
136   while (pdm->SYNCBUSY != 0U) {
137     // Wait for any pending CMD synchronization
138   }
139 
140   if (pdm->EN != 0U) {
141     pdm->CMD = PDM_CMD_FIFOFL | PDM_CMD_CLEAR | PDM_CMD_STOP;
142     while (pdm->SYNCBUSY != 0U) {
143     }
144   }
145 
146 #if defined(PDM_HAS_SET_CLEAR)
147   pdm->EN_SET    = PDM_EN_EN;
148 #endif
149   pdm->CTRL      = _PDM_CTRL_RESETVALUE;
150   pdm->IEN       = _PDM_IEN_RESETVALUE;
151 #if defined(PDM_HAS_SET_CLEAR)
152   pdm->IF_CLR    = _PDM_IF_MASK;
153 #else
154   pdm->IFC       = _PDM_IFC_MASK;
155 #endif
156 #if defined(_PDM_ROUTEPEN_MASK)
157   pdm->ROUTEPEN  = _PDM_ROUTEPEN_RESETVALUE;
158   pdm->ROUTELOC0 = _PDM_ROUTELOC0_RESETVALUE;
159   pdm->ROUTELOC1 = _PDM_ROUTELOC1_RESETVALUE;
160 #endif
161   while (pdm->SYNCBUSY != 0U) {
162     /* Must wait for SYNCBUSY before disabling an enabled pdm. */
163   }
164   pdm->EN        = _PDM_EN_RESETVALUE;
165   pdm->CFG0      = _PDM_CFG0_RESETVALUE;
166   pdm->CFG1      = _PDM_CFG1_RESETVALUE;
167 
168   while (pdm->SYNCBUSY != 0U) {
169     // Wait for any pending CMD synchronization
170   }
171 }
172 
173 /** @} (end addtogroup pdm) */
174 
175 #endif // defined(PDM_PRESENT) && (PDM_COUNT == 1)
176