LPCOpen Platform for LPC18XX/43XX microcontrollers: C:/w/lpcopen_docs/software/lpc_core/lpc_board/board_common/wm8904.c Source File

| LPCOpen Platform for LPC18XX/43XX microcontrollers 18XX43XX LPCOpen Platform for the NXP LPC18XX/43XX family of Microcontrollers

- Main Page
- Modules
- Data Structures
- Files

- File List
- Globals

wm8904.c
Go to the documentation of this file.
1 /*
2 * @brief WM8904 Audio codec interface file
3 *
4 * @note
5 * Copyright(C) NXP Semiconductors, 2012
6 * All rights reserved.
7 *
8 * @par
9 * Software that is described herein is for illustrative purposes only
10 * which provides customers with programming information regarding the
11 * LPC products. This software is supplied "AS IS" without any warranties of
12 * any kind, and NXP Semiconductors and its licensor disclaim any and
13 * all warranties, express or implied, including all implied warranties of
14 * merchantability, fitness for a particular purpose and non-infringement of
15 * intellectual property rights. NXP Semiconductors assumes no responsibility
16 * or liability for the use of the software, conveys no license or rights under any
17 * patent, copyright, mask work right, or any other intellectual property rights in
18 * or to any products. NXP Semiconductors reserves the right to make changes
19 * in the software without notification. NXP Semiconductors also makes no
20 * representation or warranty that such application will be suitable for the
21 * specified use without further testing or modification.
22 *
23 * @par
24 * Permission to use, copy, modify, and distribute this software and its
25 * documentation is hereby granted, under NXP Semiconductors' and its
26 * licensor's relevant copyrights in the software, without fee, provided that it
27 * is used in conjunction with NXP Semiconductors microcontrollers. This
28 * copyright, permission, and disclaimer notice must appear in all copies of
29 * this code.
30 */
31
32 #include "board.h"
33 #include "wm8904.h"
34
35 /*****************************************************************************
36 * Private types/enumerations/variables
37 ****************************************************************************/
38
39 #define WM8904_STATE_OFF 0
40 #define WM8904_STATE_ON 1
41
42 #define WM8904_LOCK_TIMEOUT 10
43
44 typedef struct __WM8904_Init_Seq {
45 uint16_t reg_adr;
46 uint16_t reg_val;
47 } WM8904_Init_Seq_t;
48
49 /* codec constants */
50 typedef struct __Codec_Cfg
51 {
52 uint16_t fll_k;
53 uint16_t fll_n;
54 } Codec_Cfg_t;
55
56 const Codec_Cfg_t g_CodecCfgs[2] = {
57 { WM8904_FLL_1288MHZ_K, WM8904_FLL_1288MHZ_N}, /*I2S_48000*/
58 { WM8904_FLL_1128MHZ_K, WM8904_FLL_1128MHZ_N}, /*I2S_44100*/
59 };
60
61 #define WM8904_INIT_STEPS 31
62 const WM8904_Init_Seq_t g_wm8904[] = {
63
64 { 0x00, 0x0000}, // SW Reset and ID(00H): 0000 SW_RST_DEV_ID1=0000_0000_0000_0000
65 { 0x16, 0x0006 | 0x8}, /* MCLK_INV=0, SYSCLK_SRC=0, MCLK_SRC=0, TOCLK_RATE=0, ADC_DIV=000, DAC_DIV=000, OPCLK_ENA=0, CLK_SYS_ENA=1, CLK_DSP_ENA=1, TOCLK_ENA=0 */
66 /* execute default start=up sequence */
67 { 0x6C, 0x0100}, // Write Sequencer 0(6CH): 0100 WSEQ_ENA=1, WSEQ_WRITE_INDEX=0_0000
68 { 0x6F, 0x0100}, // Write Sequencer 3(6FH): 0100 WSEQ_ABORT=0, WSEQ_START=1, WSEQ_START_INDEX=00_0000
69 { 0xFF, 300}, // insert_delay_ms 500 needs to pause for write sequencer to finish before further writes
70
71 { 0x21, WM8904_DAC_DIGITAL_1_VALUE}, // DAC Digital 1(21H): 0000 DAC_MONO=0, DAC_SB_FILT=0, DAC_MUTERATE=0, DAC_UNMUTE_RAMP=1, DAC_OSR128=1, DAC_MUTE=0, DEEMPH=00
72 { 0x68, 0x0005}, // Class W 0(68H): 0005 CP_DYN_PWR=1
73
74 #if 0 // I2S Slave
75 { 0x19, 0x0002}, // AIFDAC_TDM=0, AIFDAC_TDM_CHAN=0, AIFADC_TDM=0, AIFADC_TDM_CHAN=0, AIF_TRIS=0, AIF_BCLK_INV=0, BCLK_DIR=0, AIF_LRCLK_INV=0, AIF_WL=00, AIF_FMT=10
76 { 0x1B, 0x0020}, /* LRCLK_DIR=0, LRCLK_RATE=000_0100_0000 */
77 #endif
78 #if 1 // I2S Master
79 { 0x19, 0x0042}, // AIFDAC_TDM=0, AIFDAC_TDM_CHAN=0, AIFADC_TDM=0, AIFADC_TDM_CHAN=0, AIF_TRIS=0, AIF_BCLK_INV=0, BCLK_DIR=0, AIF_LRCLK_INV=0, AIF_WL=00, AIF_FMT=10
80 { 0x1B, 0x0820}, /* LRCLK_DIR=0, LRCLK_RATE=000_0100_0000 */
81 #endif
82 { 0x1A, 0x000F}, /* BCLK_DIV= 8 for 64FS 16bit stereo*/
83 { 0x15, 0x1802}, // CLK_SYS_RAT=0110 SAMPLE_RATE=010
84
85 { 0x7F, 0x0004}, // clear FLL Lock interrupt status if set
86 { 0x80, 0xFFFB}, // enable FLL lock interrupt
87
88 { WM8904_FLL_CONTROL_1, 0x0000}, /* FLL_FRACN_ENA=1, FLL_OSC_ENA=0, FLL_ENA=0 */
89 #if 1 // Ref clk = 12 MHz MCLK
90 { WM8904_FLL_CONTROL_2, 0x0700}, /* FLL_OUTDIV=00_0111, FLL_CTRL_RATE=000, FLL_FRATIO=000 */
91 { WM8904_FLL_CONTROL_3, WM8904_FLL_1288MHZ_K}, /* FLL_K = 0 */
92 { WM8904_FLL_CONTROL_4, WM8904_FLL_1288MHZ_N}, /* FLL_N = 96 */
93 { WM8904_FLL_CONTROL_5, 0x0000}, /* FLL Clock src = 0 (MCLK) */
94 #endif
95
96 #if 0 // Ref clk = 0.512 MHz Bit Clk
97 { WM8904_FLL_CONTROL_2, 0x0701}, /* FLL_OUTDIV=00_0111, FLL_CTRL_RATE=000, FLL_FRATIO=001 */
98 { WM8904_FLL_CONTROL_3, 0}, /* FLL_K = 0 */
99 { WM8904_FLL_CONTROL_4, 0x600}, /* FLL_N = 96 */
100 { WM8904_FLL_CONTROL_5, 0x0001}, /* FLL Clock src = 0 (MCLK) */
101 #endif
102
103 { WM8904_FLL_CONTROL_1, 0x0005}, /* FLL_FRACN_ENA=1, FLL_OSC_ENA=0, FLL_ENA=1 */
104
105 { 0x39, 0x003F}, // Analogue OUT1 Left(39H): 0039 HPOUTL_MUTE=0, HPOUT_VU=0, HPOUTLZC=0, HPOUTL_VOL=11_1001
106 { 0x3A, 0x003F}, // Analogue OUT1 Right(3AH): 00B9 HPOUTR_MUTE=0, HPOUT_VU=1, HPOUTRZC=0, HPOUTR_VOL=11_1001
107 // /* enable ADC for line-in capture */
108 { 0x0C, 0x0003}, // INL_ENA=1, INR_ENA=1
109 { 0x12, 0x000F}, // DACL_ENA=1, DACR_ENA=1, ADCL_ENA=1, ADCR_ENA=1
110 { 0x26, 0x0003}, // ADC_HPF_CU=01, ADC_HPF = 1
111 { 0x2C, 0x001C}, // LINMUTE=0, LIN_VOL=0_0101
112 { 0x2D, 0x001C}, // RINMUTE=0, LIN_VOL=0_0101
113 { 0x2E, 0x0054}, // INL_CM_ENA=1, L_IP_SEL_N=IN2L, L_IP_SEL_P=IN2L, L_MODE=Single-Ended
114 { 0x2F, 0x0054}, // INR_CM_ENA=1, R_IP_SEL_N=IN2R, R_IP_SEL_P=IN2R, R_MODE=Single-Ended
115 { 0x06, 0x0001}, // MICDET_THR=0.070mA, MICSHORT_THR=0.520mA, MICDET_ENA=0, MIC_BIAS_ENA=1
116 { 0x07, 0x0001}, // MICBIAS_SEL=2.0V
117 #if 0 // Use Digital MIC
118 /* Output Digital MIC clock */
119 { WM8904_GPIO_CONTROL_1, 0x8},
120 { 0x27, 0x1000}, // Digital MIC DMCDAT1
121 { 0x24, 0x00C0}, // ADC Digital Volume Left
122 { 0x25, 0x01C0}, // ADC Digital Volume Right
123 #endif
124 };
125
126 #define WM8904_INPUT_INIT_STEPS 4
127 const WM8904_Init_Seq_t g_wm8904_input_init[] = {
128 /* enable ADC for line-in capture */
129 { 0x0C, 0x0003}, // INL_ENA=1, INR_ENA=1
130 //{ 0x0A, 0x0001}, // ADC_OSR128=1
131 //{ 0xC6, 0x0005}, // ADC_128_OSR_TST_MODE=1, ADC_BIASX1P5=1
132 { 0x12, 0x000F}, // DACL_ENA=1, DACR_ENA=1, ADCL_ENA=1, ADCR_ENA=1
133 { 0x2C, 0x0005}, // LINMUTE=0, LIN_VOL=0_0101
134 { 0x2D, 0x0005}, // RINMUTE=0, LIN_VOL=0_0101
135
136 };
137
138 /*****************************************************************************
139 * Public types/enumerations/variables
140 ****************************************************************************/
141 int WM8904_MultiRegWrite(const WM8904_Init_Seq_t* seq, uint32_t cnt);
142
143 /*****************************************************************************
144 * Private functions
145 ****************************************************************************/
146 /* Set the default values to the codec registers */
147 static int Audio_Codec_SetDefaultValues(const uint8_t *values, int sz)
148 {
149 int ret;
150 uint16_t rd_val = 1;
151
152 /* Set System register's default values */
153 ret = WM8904_MultiRegWrite((const WM8904_Init_Seq_t *)values, sz);
154 rd_val = WM8904_REG_Read(0x7F);
155 while ( (rd_val & 0x4) != 0x4 )
156 {
157 rd_val = WM8904_REG_Read(0x7F);
158 }
159 WM8904_REG_Write(0x16, 0x4006 | 0x8);
160
161 return ret;
162 }
163
164 /*****************************************************************************
165 * Public functions
166 ****************************************************************************/
167
168 /* Read data from UDA register */
169 uint16_t WM8904_REG_Read(uint8_t reg) {
170 uint8_t rx_data[2];
171 if (Chip_I2C_MasterCmdRead(WM8904_I2C_BUS, I2CDEV_WM8904_ADDR, reg, rx_data, 2) == 2) {
172 return (rx_data[0] << 8) | rx_data[1];
173 }
174 return 0;
175 }
176
177 /* Write data to Codec register */
178 void WM8904_REG_Write(uint8_t reg, uint16_t val)
179 {
180 uint8_t dat[3];
181 dat[0] = reg; dat[1] = val >> 8; dat[2] = val & 0xFF;
182 Chip_I2C_MasterSend(WM8904_I2C_BUS, I2CDEV_WM8904_ADDR, dat, sizeof(dat));
183 }
184
185 /* Write data to codec register and verify the value by reading it back */
186 int WM8904_REG_WriteVerify(uint8_t reg, uint16_t val)
187 {
188 uint16_t ret;
189 WM8904_REG_Write(reg, val);
190 ret = WM8904_REG_Read(reg);
191 return ret == val;
192 }
193
194 /* Multiple value verification function */
195 int WM8904_REG_VerifyMult(uint8_t reg, const uint8_t *value, uint8_t *buff, int len)
196 {
197 int i;
198 if (Chip_I2C_MasterCmdRead(WM8904_I2C_BUS, I2CDEV_WM8904_ADDR, reg, buff, len) != len) {
199 return 0; /* Partial read */
200
201 }
202 /* Compare the values */
203 for (i = 0; i < len; i++) {
204 if (value[i] != buff[i]) {
205 break;
206 }
207 }
208
209 return i == len;
210 }
211
212 /* WM8904 initialize function */
213 int WM8904_Init(int input)
214 {
215 I2C_EVENTHANDLER_T old = Chip_I2C_GetMasterEventHandler(WM8904_I2C_BUS);
216 int ret;
217
218 /* Initialize I2C */
219 Board_I2C_Init(WM8904_I2C_BUS);
220 Chip_I2C_Init(WM8904_I2C_BUS);
221 Chip_I2C_SetClockRate(WM8904_I2C_BUS, 100000);
222 Chip_I2C_SetMasterEventHandler(WM8904_I2C_BUS, Chip_I2C_EventHandlerPolling);
223
224 /* Initialize the default values */
225 ret = Audio_Codec_SetDefaultValues((void *)&g_wm8904[0], sizeof(g_wm8904)/sizeof(WM8904_Init_Seq_t));
226
227
228 #if 0
229 if (ret) {
230 ret = Audio_Codec_SetDefaultValues(UDA_interfil_regs_dat, sizeof(UDA_interfil_regs_dat));
231 }
232
233 if (ret) {
234 ret = Audio_Codec_SetDefaultValues(UDA_decimator_regs_dat, sizeof(UDA_decimator_regs_dat));
235 }
236
237 if (ret && input) {
238 /* Disable Power On for ADCR, PGAR, PGAL to get mic sound more clearly */
239 ret = UDA1380_REG_WriteVerify(UDA_POWER_CTRL,
240 UDA1380_REG_PWRCTRL_DEFAULT_VALUE & (~(0x0B)));
241
242 if (ret) {
243 ret = UDA1380_REG_WriteVerify(UDA_ADC_CTRL,
244 UDA1380_REG_ADC_DEFAULT_VALUE | input);
245 }
246 }
247 #endif
248 Chip_I2C_SetMasterEventHandler(WM8904_I2C_BUS, old);
249
250 return ret;
251 }
252
253 static void delay(uint32_t i) {
254 while (i--) {}
255 }
256
257 /* Write multiple registers in one go */
258 int WM8904_MultiRegWrite(const WM8904_Init_Seq_t* seq, uint32_t cnt)
259 {
260 uint16_t rd_val = 1;
261 uint32_t i;
262
263 for (i = 0; i < cnt; i++) {
264 /* check if it is delay register */
265 if (seq[i].reg_adr == 0xFF) {
266
267 delay(seq[i].reg_val * 1000);
268 do {
269 rd_val = WM8904_REG_Read(0x70);
270 } while (rd_val & 1);
271 } else {
272 WM8904_REG_Write(seq[i].reg_adr, seq[i].reg_val);
273 rd_val = WM8904_REG_Read(seq[i].reg_adr);
274 }
275 }
276 return (SUCCESS);
277 }
278