1 /**************************************************************************//**
2  * @file     i2s.c
3  * @version  V3.00
4  * @brief    M460 series I2S driver source file
5  *
6  * @copyright SPDX-License-Identifier: Apache-2.0
7  * @copyright Copyright (C) 2021 Nuvoton Technology Corp. All rights reserved.
8 *****************************************************************************/
9 
10 #include <stdio.h>
11 #include "NuMicro.h"
12 
13 /** @addtogroup Standard_Driver Standard Driver
14   @{
15 */
16 
17 /** @addtogroup I2S_Driver I2S Driver
18   @{
19 */
20 
21 /** @addtogroup I2S_EXPORTED_FUNCTIONS I2S Exported Functions
22   @{
23 */
24 
25 static uint32_t I2S_GetSourceClockFreq(I2S_T *i2s);
26 
27 /**
28   * @brief  This function is used to get I2S source clock frequency.
29   * @param[in]  i2s is the base address of I2S module.
30   * @return I2S source clock frequency (Hz).
31   */
I2S_GetSourceClockFreq(I2S_T * i2s)32 static uint32_t I2S_GetSourceClockFreq(I2S_T *i2s)
33 {
34     uint32_t u32Freq = 0UL, u32ClkSrcSel;
35 
36     if(i2s == I2S0)
37     {
38         /* get I2S selection clock source */
39         u32ClkSrcSel = CLK->CLKSEL3 & CLK_CLKSEL3_I2S0SEL_Msk;
40 
41         switch(u32ClkSrcSel)
42         {
43             case CLK_CLKSEL3_I2S0SEL_HXT:
44                 u32Freq = __HXT;
45                 break;
46 
47             case CLK_CLKSEL3_I2S0SEL_PLL_DIV2:
48                 u32Freq = (CLK_GetPLLClockFreq() >> 1);
49                 break;
50 
51             case CLK_CLKSEL3_I2S0SEL_PCLK0:
52                 u32Freq = CLK_GetPCLK0Freq();
53                 break;
54 
55             case CLK_CLKSEL3_I2S0SEL_HIRC:
56                 u32Freq = __HIRC;
57                 break;
58 
59             case CLK_CLKSEL3_I2S0SEL_HIRC48M:
60                 u32Freq = __HIRC48M;
61                 break;
62 
63             case CLK_CLKSEL3_I2S0SEL_PLLFN_DIV2:
64                 u32Freq = (CLK_GetPLLFNClockFreq() >> 1);
65                 break;
66 
67             default:
68                 u32Freq = __HXT;
69                 break;
70         }
71     }
72     else if(i2s == I2S1)
73     {
74         /* get I2S selection clock source */
75         u32ClkSrcSel = CLK->CLKSEL2 & CLK_CLKSEL2_I2S1SEL_Msk;
76 
77         switch(u32ClkSrcSel)
78         {
79             case CLK_CLKSEL2_I2S1SEL_HXT:
80                 u32Freq = __HXT;
81                 break;
82 
83             case CLK_CLKSEL2_I2S1SEL_PLL_DIV2:
84                 u32Freq = (CLK_GetPLLClockFreq() >> 1);
85                 break;
86 
87             case CLK_CLKSEL2_I2S1SEL_PCLK1:
88                 u32Freq = CLK_GetPCLK1Freq();
89                 break;
90 
91             case CLK_CLKSEL2_I2S1SEL_HIRC:
92                 u32Freq = __HIRC;
93                 break;
94 
95             case CLK_CLKSEL2_I2S1SEL_HIRC48M:
96                 u32Freq = __HIRC48M;
97                 break;
98 
99             case CLK_CLKSEL2_I2S1SEL_PLLFN_DIV2:
100                 u32Freq = (CLK_GetPLLFNClockFreq() >> 1);
101                 break;
102 
103             default:
104                 u32Freq = __HXT;
105                 break;
106         }
107     }
108 
109     return u32Freq;
110 }
111 
112 /**
113   * @brief  This function configures some parameters of I2S interface for general purpose use.
114   *         The sample rate may not be used from the parameter, it depends on system's clock settings,
115   *         but real sample rate used by system will be returned for reference.
116   * @param[in] i2s is the base address of I2S module.
117   * @param[in] u32MasterSlave I2S operation mode. Valid values are:
118   *                                     - \ref I2S_MODE_MASTER
119   *                                     - \ref I2S_MODE_SLAVE
120   * @param[in] u32SampleRate Sample rate
121   * @param[in] u32WordWidth Data length. Valid values are:
122   *                                     - \ref I2S_DATABIT_8
123   *                                     - \ref I2S_DATABIT_16
124   *                                     - \ref I2S_DATABIT_24
125   *                                     - \ref I2S_DATABIT_32
126   * @param[in] u32MonoData: Set audio data to mono or not. Valid values are:
127   *                                     - \ref I2S_ENABLE_MONO
128   *                                     - \ref I2S_DISABLE_MONO
129   * @param[in] u32DataFormat: Data format. This is also used to select I2S or PCM(TDM) function. Valid values are:
130   *                                     - \ref I2S_FORMAT_I2S
131   *                                     - \ref I2S_FORMAT_I2S_MSB
132   *                                     - \ref I2S_FORMAT_I2S_LSB
133   *                                     - \ref I2S_FORMAT_PCM
134   *                                     - \ref I2S_FORMAT_PCM_MSB
135   *                                     - \ref I2S_FORMAT_PCM_LSB
136   * @return Real sample rate.
137   */
I2S_Open(I2S_T * i2s,uint32_t u32MasterSlave,uint32_t u32SampleRate,uint32_t u32WordWidth,uint32_t u32MonoData,uint32_t u32DataFormat)138 uint32_t I2S_Open(I2S_T *i2s, uint32_t u32MasterSlave, uint32_t u32SampleRate, uint32_t u32WordWidth, uint32_t u32MonoData, uint32_t u32DataFormat)
139 {
140     uint16_t u16Divider;
141     uint32_t u32BitRate, u32SrcClk;
142 
143     if(i2s == I2S0)
144     {
145         SYS->IPRST1 |= SYS_IPRST1_I2S0RST_Msk;
146         SYS->IPRST1 &= ~SYS_IPRST1_I2S0RST_Msk;
147     }
148     else if(i2s == I2S1)
149     {
150         SYS->IPRST2 |= SYS_IPRST2_I2S1RST_Msk;
151         SYS->IPRST2 &= ~SYS_IPRST2_I2S1RST_Msk;
152     }
153 
154     i2s->CTL0 = u32MasterSlave | u32WordWidth | u32MonoData | u32DataFormat;
155     i2s->CTL1 = I2S_FIFO_TX_LEVEL_WORD_8 | I2S_FIFO_RX_LEVEL_WORD_8;
156 
157     u32SrcClk = I2S_GetSourceClockFreq(i2s);
158 
159     u32BitRate = u32SampleRate * (((u32WordWidth >> 4U) & 0x3U) + 1U) * 16U;
160     u16Divider = (uint16_t)((((u32SrcClk * 10UL / u32BitRate) >> 1U) + 5UL) / 10UL) - 1U; /* Round to the nearest integer */
161     i2s->CLKDIV = (i2s->CLKDIV & ~I2S_CLKDIV_BCLKDIV_Msk) | ((uint32_t)u16Divider << 8U);
162 
163     /* Calculate real sample rate */
164     u32BitRate = u32SrcClk / (2U * ((uint32_t)u16Divider + 1U));
165     u32SampleRate = u32BitRate / ((((u32WordWidth >> 4U) & 0x3U) + 1U) * 16U);
166 
167     i2s->CTL0 |= I2S_CTL0_I2SEN_Msk;
168 
169     return u32SampleRate;
170 }
171 
172 /**
173   * @brief  Disable I2S function and I2S clock.
174   * @param[in]  i2s is the base address of I2S module.
175   * @return none
176   */
I2S_Close(I2S_T * i2s)177 void I2S_Close(I2S_T *i2s)
178 {
179     i2s->CTL0 &= ~I2S_CTL0_I2SEN_Msk;
180 }
181 
182 /**
183   * @brief This function enables the interrupt according to the mask parameter.
184   * @param[in] i2s is the base address of I2S module.
185   * @param[in] u32Mask is the combination of all related interrupt enable bits.
186   *            Each bit corresponds to a interrupt bit.
187   * @return none
188   */
I2S_EnableInt(I2S_T * i2s,uint32_t u32Mask)189 void I2S_EnableInt(I2S_T *i2s, uint32_t u32Mask)
190 {
191     i2s->IEN |= u32Mask;
192 }
193 
194 /**
195   * @brief This function disables the interrupt according to the mask parameter.
196   * @param[in] i2s is the base address of I2S module.
197   * @param[in] u32Mask is the combination of all related interrupt enable bits.
198   *            Each bit corresponds to a interrupt bit.
199   * @return none
200   */
I2S_DisableInt(I2S_T * i2s,uint32_t u32Mask)201 void I2S_DisableInt(I2S_T *i2s, uint32_t u32Mask)
202 {
203     i2s->IEN &= ~u32Mask;
204 }
205 
206 /**
207   * @brief  Enable MCLK .
208   * @param[in] i2s is the base address of I2S module.
209   * @param[in] u32BusClock is the target MCLK clock
210   * @return Actual MCLK clock
211   */
I2S_EnableMCLK(I2S_T * i2s,uint32_t u32BusClock)212 uint32_t I2S_EnableMCLK(I2S_T *i2s, uint32_t u32BusClock)
213 {
214     uint8_t u8Divider;
215     uint32_t u32SrcClk, u32Reg, u32Clock;
216 
217     u32SrcClk = I2S_GetSourceClockFreq(i2s);
218     if(u32BusClock == u32SrcClk)
219     {
220         u8Divider = 0U;
221     }
222     else
223     {
224         u8Divider = (uint8_t)(u32SrcClk / u32BusClock) >> 1U;
225     }
226 
227     i2s->CLKDIV = (i2s->CLKDIV & ~I2S_CLKDIV_MCLKDIV_Msk) | u8Divider;
228 
229     i2s->CTL0 |= I2S_CTL0_MCLKEN_Msk;
230 
231     u32Reg = i2s->CLKDIV & I2S_CLKDIV_MCLKDIV_Msk;
232 
233     if(u32Reg == 0U)
234     {
235         u32Clock = u32SrcClk;
236     }
237     else
238     {
239         u32Clock = (u32SrcClk >> 1U) / u32Reg;
240     }
241 
242     return u32Clock;
243 }
244 
245 /**
246   * @brief  Disable MCLK .
247   * @param[in] i2s is the base address of I2S module.
248   * @return none
249   */
I2S_DisableMCLK(I2S_T * i2s)250 void I2S_DisableMCLK(I2S_T *i2s)
251 {
252     i2s->CTL0 &= ~I2S_CTL0_MCLKEN_Msk;
253 }
254 
255 /**
256   * @brief  Configure FIFO threshold setting.
257   * @param[in]  i2s The pointer of the specified I2S module.
258   * @param[in]  u32TxThreshold Decides the TX FIFO threshold. It could be 0 ~ 15.
259   * @param[in]  u32RxThreshold Decides the RX FIFO threshold. It could be 0 ~ 15.
260   * @return None
261   * @details Set TX FIFO threshold and RX FIFO threshold configurations.
262   */
I2S_SetFIFO(I2S_T * i2s,uint32_t u32TxThreshold,uint32_t u32RxThreshold)263 void I2S_SetFIFO(I2S_T *i2s, uint32_t u32TxThreshold, uint32_t u32RxThreshold)
264 {
265     i2s->CTL1 = (i2s->CTL1 & ~(I2S_CTL1_TXTH_Msk | I2S_CTL1_RXTH_Msk)) |
266                 (u32TxThreshold << I2S_CTL1_TXTH_Pos) |
267                 (u32RxThreshold << I2S_CTL1_RXTH_Pos);
268 }
269 
270 /**
271   * @brief  Configure PCM(TDM) function parameters, such as channel width, channel number and sync pulse width
272   * @param[in]  i2s The pointer of the specified I2S module.
273   * @param[in]  u32ChannelWidth Channel width. Valid values are:
274   *                                                             - \ref I2S_TDM_WIDTH_8BIT
275   *                                                             - \ref I2S_TDM_WIDTH_16BIT
276   *                                                             - \ref I2S_TDM_WIDTH_24BIT
277   *                                                             - \ref I2S_TDM_WIDTH_32BIT
278   * @param[in]  u32ChannelNum Channel number. Valid values are:
279   *                                                             - \ref I2S_TDM_2CH
280   *                                                             - \ref I2S_TDM_4CH
281   *                                                             - \ref I2S_TDM_6CH
282   *                                                             - \ref I2S_TDM_8CH
283   * @param[in]  u32SyncWidth Width for sync pulse. Valid values are:
284   *                                                             - \ref I2S_TDM_SYNC_ONE_BCLK
285   *                                                             - \ref I2S_TDM_SYNC_ONE_CHANNEL
286   * @return None
287   * @details Set TX FIFO threshold and RX FIFO threshold configurations.
288   */
I2S_ConfigureTDM(I2S_T * i2s,uint32_t u32ChannelWidth,uint32_t u32ChannelNum,uint32_t u32SyncWidth)289 void I2S_ConfigureTDM(I2S_T *i2s, uint32_t u32ChannelWidth, uint32_t u32ChannelNum, uint32_t u32SyncWidth)
290 {
291     i2s->CTL0 = (i2s->CTL0 & ~(I2S_CTL0_TDMCHNUM_Msk | I2S_CTL0_CHWIDTH_Msk | I2S_CTL0_PCMSYNC_Msk)) |
292                 (u32ChannelWidth << I2S_CTL0_CHWIDTH_Pos) |
293                 (u32ChannelNum << I2S_CTL0_TDMCHNUM_Pos) |
294                 (u32SyncWidth << I2S_CTL0_PCMSYNC_Pos);
295 }
296 
297 /*@}*/ /* end of group I2S_EXPORTED_FUNCTIONS */
298 
299 /*@}*/ /* end of group I2S_Driver */
300 
301 /*@}*/ /* end of group Standard_Driver */
302