1 /**************************************************************************//**
2  * @file     i2s.c
3  * @version  V3.00
4  * @brief    I2S driver source file
5  *
6  * @copyright SPDX-License-Identifier: Apache-2.0
7  * @copyright Copyright (C) 2020 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 The base address of I2S module.
30   * @return I2S source clock frequency (Hz).
31   * @details Return the source clock frequency according to the setting of I2S0SEL (CLK_CLKSEL3[17:16]).
32   */
I2S_GetSourceClockFreq(I2S_T * i2s)33 static uint32_t I2S_GetSourceClockFreq(I2S_T *i2s)
34 {
35     (void)i2s;
36     uint32_t u32Freq, u32ClkSrcSel;
37 
38     u32ClkSrcSel = CLK_GetModuleClockSource(I2S0_MODULE) << CLK_CLKSEL3_I2S0SEL_Pos;
39 
40     switch(u32ClkSrcSel)
41     {
42         case CLK_CLKSEL3_I2S0SEL_HXT:
43             u32Freq = __HXT;
44             break;
45 
46         case CLK_CLKSEL3_I2S0SEL_PLL:
47             u32Freq = CLK_GetPLLClockFreq();
48             break;
49 
50         case CLK_CLKSEL3_I2S0SEL_HIRC:
51             u32Freq = __HIRC;
52             break;
53 
54         case CLK_CLKSEL3_I2S0SEL_PCLK0:
55             u32Freq = CLK_GetPCLK0Freq();
56             break;
57 
58         default:
59             u32Freq = CLK_GetPCLK0Freq();
60             break;
61     }
62 
63     return u32Freq;
64 }
65 
66 /**
67   * @brief  This function configures some parameters of I2S interface for general purpose use.
68   * @param[in] i2s The base address of I2S module.
69   * @param[in] u32MasterSlave I2S operation mode. Valid values are:
70   *                                     - \ref I2S_MODE_MASTER
71   *                                     - \ref I2S_MODE_SLAVE
72   * @param[in] u32SampleRate Sample rate
73   * @param[in] u32WordWidth Data length. Valid values are:
74   *                                     - \ref I2S_DATABIT_8
75   *                                     - \ref I2S_DATABIT_16
76   *                                     - \ref I2S_DATABIT_24
77   *                                     - \ref I2S_DATABIT_32
78   * @param[in] u32MonoData Set audio data to mono or not. Valid values are:
79   *                                     - \ref I2S_ENABLE_MONO
80   *                                     - \ref I2S_DISABLE_MONO
81   * @param[in] u32DataFormat Data format. This is also used to select I2S or PCM(TDM) function. Valid values are:
82   *                                     - \ref I2S_FORMAT_I2S
83   *                                     - \ref I2S_FORMAT_I2S_MSB
84   *                                     - \ref I2S_FORMAT_I2S_LSB
85   *                                     - \ref I2S_FORMAT_PCM
86   *                                     - \ref I2S_FORMAT_PCM_MSB
87   *                                     - \ref I2S_FORMAT_PCM_LSB
88   * @return Real sample rate.
89   * @details Set TX and RX FIFO threshold to middle value.
90   *          The sample rate may not be used from the parameter, it depends on system's clock settings,
91   *          but real sample rate used by system will be returned for reference.
92   * @note   I2S will be reset in initialization only for Secure.
93   */
I2S_Open(I2S_T * i2s,uint32_t u32MasterSlave,uint32_t u32SampleRate,uint32_t u32WordWidth,uint32_t u32MonoData,uint32_t u32DataFormat)94 uint32_t I2S_Open(I2S_T *i2s, uint32_t u32MasterSlave, uint32_t u32SampleRate, uint32_t u32WordWidth, uint32_t u32MonoData, uint32_t u32DataFormat)
95 {
96     uint16_t u16Divider;
97     uint32_t u32BitRate, u32SrcClk;
98 
99     if(!(__PC() & NS_OFFSET))
100     {
101         /* Reset I2S */
102         SYS->IPRST1 |= SYS_IPRST1_I2S0RST_Msk;
103         SYS->IPRST1 &= ~SYS_IPRST1_I2S0RST_Msk;
104     }
105 
106     /* Configure I2S controller according to input parameters. */
107     i2s->CTL0 = u32MasterSlave | u32WordWidth | u32MonoData | u32DataFormat;
108     i2s->CTL1 = I2S_FIFO_TX_LEVEL_WORD_8 | I2S_FIFO_RX_LEVEL_WORD_8;
109 
110     /* Get I2S source clock frequency */
111     u32SrcClk = I2S_GetSourceClockFreq(i2s);
112 
113     /* Calculate bit clock rate */
114     u32BitRate = u32SampleRate * (((u32WordWidth >> 4UL) & 0x3UL) + 1UL) * 16UL;
115     u16Divider = (uint16_t)((((((u32SrcClk * 10UL) / u32BitRate) >> 1UL) + 5UL) / 10UL) - 1UL); /* Round to the nearest integer */
116     i2s->CLKDIV = (i2s->CLKDIV & ~I2S_CLKDIV_BCLKDIV_Msk) | ((uint32_t)u16Divider << 8UL);
117 
118     /* Calculate real sample rate */
119     u32BitRate = u32SrcClk / (((uint32_t)u16Divider + 1UL) * 2UL);
120     u32SampleRate = u32BitRate / ((((u32WordWidth >> 4UL) & 0x3UL) + 1UL) * 16UL);
121 
122     /* Enable I2S controller */
123     i2s->CTL0 |= I2S_CTL0_I2SEN_Msk;
124 
125     return u32SampleRate;
126 }
127 
128 /**
129   * @brief  Disable I2S function.
130   * @param[in]  i2s The base address of I2S module.
131   * @return None
132   * @details Clear I2SEN (I2S_CTL0[0]) to disable I2S function.
133   */
I2S_Close(I2S_T * i2s)134 void I2S_Close(I2S_T *i2s)
135 {
136     i2s->CTL0 &= ~I2S_CTL0_I2SEN_Msk;
137 }
138 
139 /**
140   * @brief Enable interrupt function.
141   * @param[in] i2s The base address of I2S module.
142   * @param[in] u32Mask The combination of all related interrupt enable bits.
143   *            Each bit corresponds to a interrupt bit.
144   * @return None
145   * @details This function enables the interrupt according to the mask parameter.
146   */
I2S_EnableInt(I2S_T * i2s,uint32_t u32Mask)147 void I2S_EnableInt(I2S_T *i2s, uint32_t u32Mask)
148 {
149     i2s->IEN |= u32Mask;
150 }
151 
152 /**
153   * @brief Disable interrupt function.
154   * @param[in] i2s The base address of I2S module.
155   * @param[in] u32Mask The combination of all related interrupt enable bits.
156   *            Each bit corresponds to a interrupt bit.
157   * @return None
158   * @details This function disables the interrupt according to the mask parameter.
159   */
I2S_DisableInt(I2S_T * i2s,uint32_t u32Mask)160 void I2S_DisableInt(I2S_T *i2s, uint32_t u32Mask)
161 {
162     i2s->IEN &= ~u32Mask;
163 }
164 
165 /**
166   * @brief  Enable master clock (MCLK).
167   * @param[in] i2s The base address of I2S module.
168   * @param[in] u32BusClock The target MCLK clock.
169   * @return Actual MCLK clock
170   * @details Set the master clock rate according to u32BusClock parameter and enable master clock output.
171   *          The actual master clock rate may be different from the target master clock rate. The real master clock rate will be returned for reference.
172   */
I2S_EnableMCLK(I2S_T * i2s,uint32_t u32BusClock)173 uint32_t I2S_EnableMCLK(I2S_T *i2s, uint32_t u32BusClock)
174 {
175     uint8_t u8Divider;
176     uint32_t u32SrcClk, u32Reg, u32Clock;
177 
178     u32SrcClk = I2S_GetSourceClockFreq(i2s);
179     if(u32BusClock == u32SrcClk)
180     {
181         u8Divider = (uint8_t)0UL;
182     }
183     else
184     {
185         u8Divider = (uint8_t)(u32SrcClk / u32BusClock) >> 1UL;
186     }
187 
188     i2s->CLKDIV = (i2s->CLKDIV & ~I2S_CLKDIV_MCLKDIV_Msk) | u8Divider;
189 
190     i2s->CTL0 |= I2S_CTL0_MCLKEN_Msk;
191 
192     u32Reg = i2s->CLKDIV & I2S_CLKDIV_MCLKDIV_Msk;
193 
194     if(u32Reg == 0UL)
195     {
196         u32Clock = u32SrcClk;
197     }
198     else
199     {
200         u32Clock = ((u32SrcClk >> 1UL) / u32Reg);
201     }
202 
203     return u32Clock;
204 }
205 
206 /**
207   * @brief  Disable master clock (MCLK).
208   * @param[in] i2s The base address of I2S module.
209   * @return None
210   * @details Disable master clock output.
211   */
I2S_DisableMCLK(I2S_T * i2s)212 void I2S_DisableMCLK(I2S_T *i2s)
213 {
214     i2s->CTL0 &= ~I2S_CTL0_MCLKEN_Msk;
215 }
216 
217 /**
218   * @brief  Configure FIFO threshold setting.
219   * @param[in]  i2s The pointer of the specified I2S module.
220   * @param[in]  u32TxThreshold Decides the TX FIFO threshold. It could be 0 ~ 15.
221   * @param[in]  u32RxThreshold Decides the RX FIFO threshold. It could be 0 ~ 15.
222   * @return None
223   * @details Set TX FIFO threshold and RX FIFO threshold configurations.
224   */
I2S_SetFIFO(I2S_T * i2s,uint32_t u32TxThreshold,uint32_t u32RxThreshold)225 void I2S_SetFIFO(I2S_T *i2s, uint32_t u32TxThreshold, uint32_t u32RxThreshold)
226 {
227     i2s->CTL1 = (i2s->CTL1 & ~(I2S_CTL1_TXTH_Msk | I2S_CTL1_RXTH_Msk)) |
228                 (u32TxThreshold << I2S_CTL1_TXTH_Pos) |
229                 (u32RxThreshold << I2S_CTL1_RXTH_Pos);
230 }
231 
232 /**
233   * @brief  Configure PCM(TDM) function parameters, such as channel width, channel number and sync pulse width
234   * @param[in]  i2s The pointer of the specified I2S module.
235   * @param[in]  u32ChannelWidth Channel width. Valid values are:
236   *                                                             - \ref I2S_TDM_WIDTH_8BIT
237   *                                                             - \ref I2S_TDM_WIDTH_16BIT
238   *                                                             - \ref I2S_TDM_WIDTH_24BIT
239   *                                                             - \ref I2S_TDM_WIDTH_32BIT
240   * @param[in]  u32ChannelNum Channel number. Valid values are:
241   *                                                             - \ref I2S_TDM_2CH
242   *                                                             - \ref I2S_TDM_4CH
243   *                                                             - \ref I2S_TDM_6CH
244   *                                                             - \ref I2S_TDM_8CH
245   * @param[in]  u32SyncWidth Width for sync pulse. Valid values are:
246   *                                                             - \ref I2S_TDM_SYNC_ONE_BCLK
247   *                                                             - \ref I2S_TDM_SYNC_ONE_CHANNEL
248   * @return None
249   * @details Set TX FIFO threshold and RX FIFO threshold configurations.
250   */
I2S_ConfigureTDM(I2S_T * i2s,uint32_t u32ChannelWidth,uint32_t u32ChannelNum,uint32_t u32SyncWidth)251 void I2S_ConfigureTDM(I2S_T *i2s, uint32_t u32ChannelWidth, uint32_t u32ChannelNum, uint32_t u32SyncWidth)
252 {
253     i2s->CTL0 = (i2s->CTL0 & ~(I2S_CTL0_TDMCHNUM_Msk | I2S_CTL0_CHWIDTH_Msk | I2S_CTL0_PCMSYNC_Msk)) |
254                 (u32ChannelWidth << I2S_CTL0_CHWIDTH_Pos) |
255                 (u32ChannelNum << I2S_CTL0_TDMCHNUM_Pos) |
256                 (u32SyncWidth << I2S_CTL0_PCMSYNC_Pos);
257 }
258 
259 /**@}*/ /* end of group I2S_EXPORTED_FUNCTIONS */
260 
261 /**@}*/ /* end of group I2S_Driver */
262 
263 /**@}*/ /* end of group Standard_Driver */
264