LPCOpen Platform for LPC112X microcontrollers  112X
LPCOpen Platform for the NXP LPC112X family of Microcontrollers
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;
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 
59 };
60 
61 #define WM8904_INIT_STEPS 31
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
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 {
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);
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) {
231  }
232 
233  if (ret) {
235  }
236 
237  if (ret && input) {
238  /* Disable Power On for ADCR, PGAR, PGAL to get mic sound more clearly */
241 
242  if (ret) {
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