LPCOpen Platform for LPC18XX/43XX microcontrollers: C:/w/lpcopen_docs/software/lpc_core/lpc_board/board_common/wm8903.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

wm8903.c
Go to the documentation of this file.
1 /*
2 * @brief WM8903 Audio codec interface file
3 *
4 * @note
5 * Copyright(C) NXP Semiconductors, 2014
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 "wm8903.h"
34
35 /*****************************************************************************
36 * Private types/enumerations/variables
37 ****************************************************************************/
38 #define I2S_MCK_PORT 1
39 #define I2S_MCK_PIN 17
40 #define I2S_MCK_MUX 2
41
42 #define I2S_SCK_PORT 0
43 #define I2S_SCK_PIN 4
44 #define I2S_SCK_MUX 1
45
46 #define I2S_WS_PORT 0
47 #define I2S_WS_PIN 5
48 #define I2S_WS_MUX 1
49
50
51 #define I2CDEV_CODEC_ADDR 0x1A
52 #define CODEC_I2C_BUS LPC_I2C0
53 #define CODEC_I2C_BUS_ID I2C0
54
55 #define MULTI_REGISTER_DELAY_COMMAND 0xFFFF
56 #define MULTI_REGISTER_WAIT_FOR_SEQUENCER_NOT_BUSY 0xFFFE
57
58 typedef struct __WM8903_Init_Seq {
59 uint16_t reg_adr;
60 uint16_t reg_val;
61 } WM8903_Init_Seq_t;
62
63 const WM8903_Init_Seq_t g_wm8903[] = {
64
65 /* Note! This sequence assumes MCLK is supplied to CODEC. */
66
67 /* execute default start=up sequence */
68 { WM8903_SW_RESET_AND_ID, 0x0000},
69 { WM8903_AUDIO_INTERFACE_1, 0x0002}, /* audio interface I2S 32-bit */
70 { WM8903_WRITE_SEQUENCER_0, 0x0100}, /* WSEQ_ENA=1, WSEQ_WRITE_INDEX=0_0000 */
71 { WM8903_CLOCK_RATES_2, 0x0004}, /* clock enable SYS */
72 { WM8903_WRITE_SEQUENCER_3, 0x0100}, /* WSEQ_ABORT=0, WSEQ_START=1, WSEQ_START_INDEX=00_0000 */
73
74 /* Wait up to 4 seconds for sequencer not busy. */
75 { MULTI_REGISTER_WAIT_FOR_SEQUENCER_NOT_BUSY, 4000 },
76
77 { WM8903_CLOCK_RATES_2, 0x0006}, /* clock enable SYS and DSP */
78
79 /* DAC setup */
80 { WM8903_DC_SERVO_0, 0x001c}, /* d.c servo enable on headphone outputs */
81 { WM8903_ANALOGUE_LINEOUT_0, 0x0000}, /* line out disabled */
82 { WM8903_POWER_MANAGEMENT_3, 0x0000}, /* disable line out PGAs */
83 { WM8903_CLASS_W_0, 0x0001}, /* dynamic CP control based on audio */
84
85 /* Set clock to 256k extrn MCK */
86 { WM8903_CLOCK_RATES_1, 0x0C08}, /* 256x and external MCLK */
87
88 /* Disable the DMIC */
89 { WM8903_CLOCK_RATE_TEST_4, (0<<9)}, /* DMIC off */
90
91 /* Set L/R gain and unmute */
92 { WM8903_ANALOGUE_OUT1_LEFT, 0x0039}, /* HP left 0dB */
93 { WM8903_ANALOGUE_OUT1_RIGHT, 0x00b9}, /* HP right 0dB */
94 { WM8903_ANALOGUE_LEFT_INPUT_0, 0x0005}, /* Un-mute 0dB left */
95 { WM8903_ANALOGUE_RIGHT_INPUT_0, 0x0005}, /* Un-mute 0dB right */
96
97 { WM8903_GPIO_CONTROL_1, ((0x06 << 8) | (0 << 7))},
98 { WM8903_GPIO_CONTROL_2, ((0x06 << 8) | (1 << 7))},
99
100 };
101
102 /* I2CM transfer record */
103 static I2CM_XFER_T i2cmXferRec;
104
105 /*****************************************************************************
106 * Public types/enumerations/variables
107 ****************************************************************************/
108
109
110 /*****************************************************************************
111 * Private functions
112 ****************************************************************************/
113
114 /* Write multiple registers in one go */
115 static int WM8903_MultiRegWrite(const WM8903_Init_Seq_t* seq, uint32_t cnt)
116 {
117 uint32_t timeoutTimer;
118 uint32_t timeoutTicks;
119 uint16_t rd_val = 1;
120 uint32_t i;
121
122 for (i = 0; i < cnt; i++) {
123
124 switch ( seq[i].reg_adr ) {
125 case MULTI_REGISTER_DELAY_COMMAND:
126
127 /* Delay the requested number of mS */
128 StopWatch_DelayMs( seq[i].reg_val );
129 break;
130
131 case MULTI_REGISTER_WAIT_FOR_SEQUENCER_NOT_BUSY:
132
133 /* Don't lockup if the write sequencer does. */
134 timeoutTimer = StopWatch_Start();
135 timeoutTicks = StopWatch_MsToTicks( seq[i].reg_val );
136
137 /* Wait in this loop until write sequencer is not busy. */
138 do {
139
140 if (StopWatch_Elapsed(timeoutTimer) > timeoutTicks) {
141 return 0;
142 }
143
144 /* Read WSEQ_BUSY flag. */
145 rd_val = WM8903_REG_Read(WM8903_WRITE_SEQUENCER_4);
146 } while (rd_val & WSEQ_BUSY_MASK);
147 break;
148
149 default:
150 WM8903_REG_Write(seq[i].reg_adr, seq[i].reg_val);
151 break;
152 }
153 }
154 return (SUCCESS);
155 }
156
157 /* Function to setup and execute I2C transfer request */
158 static void SetupXferRecAndExecute(uint8_t devAddr,
159 uint8_t *txBuffPtr,
160 uint16_t txSize,
161 uint8_t *rxBuffPtr,
162 uint16_t rxSize)
163 {
164 /* Setup I2C transfer record */
165 i2cmXferRec.slaveAddr = devAddr;
166 i2cmXferRec.options = 0;
167 i2cmXferRec.status = 0;
168 i2cmXferRec.txSz = txSize;
169 i2cmXferRec.rxSz = rxSize;
170 i2cmXferRec.txBuff = txBuffPtr;
171 i2cmXferRec.rxBuff = rxBuffPtr;
172 Chip_I2CM_XferBlocking(CODEC_I2C_BUS, &i2cmXferRec);
173 }
174
175 /*****************************************************************************
176 * Public functions
177 ****************************************************************************/
178
179 /* Read data from UDA register */
180 uint16_t WM8903_REG_Read(uint8_t reg) {
181 uint8_t rx_data[2];
182
183 SetupXferRecAndExecute(I2CDEV_CODEC_ADDR, &reg, 1, rx_data, 2);
184
185 /* Test for valid operation */
186 if (i2cmXferRec.status != I2CM_STATUS_OK) {
187 return 0xFFFF;
188 }
189
190 return (rx_data[0] << 8) | rx_data[1];
191 }
192
193 /* Write data to Codec register */
194 uint32_t WM8903_REG_Write(uint8_t reg, uint16_t val)
195 {
196 uint8_t dat[3];
197 dat[0] = reg; dat[1] = val >> 8; dat[2] = val & 0xFF;
198
199 SetupXferRecAndExecute(I2CDEV_CODEC_ADDR, dat, 3, NULL, 0);
200
201 return i2cmXferRec.status;
202 }
203
204 /* WM8903 initialize function */
205 int WM8903_Init(int input)
206 {
207 int ret;
208
209 /* Initialize I2C to 100khz */
210 Board_I2C_Init(CODEC_I2C_BUS_ID);
211 Chip_I2CM_Init(CODEC_I2C_BUS);
212 Chip_I2CM_SetBusSpeed(CODEC_I2C_BUS, 100000);
213
214 /* Disable the interrupt for the I2C */
215 NVIC_DisableIRQ(I2C0_IRQn);
216
217 /* Disable all clocks to device. Configuring as gpio disables I2S functionality */
218 Chip_IOCON_PinMuxSet(LPC_IOCON, I2S_MCK_PORT, I2S_MCK_PIN, 0x0);
219 Chip_IOCON_PinMuxSet(LPC_IOCON, I2S_SCK_PORT, I2S_SCK_PIN, 0x0);
220 Chip_IOCON_PinMuxSet(LPC_IOCON, I2S_WS_PORT, I2S_WS_PIN, 0x0);
221
222 /* Send soft reset */
223 WM8903_REG_Write(WM8903_SW_RESET_AND_ID, 0x0);
224
225 /* Enable the master clock and give the part time to respond 2 milSeconds */
226 Chip_IOCON_PinMuxSet(LPC_IOCON, I2S_MCK_PORT, I2S_MCK_PIN, 0x2);
227
228 /* Delay for 2 mSec */
229 StopWatch_DelayMs(2);
230
231 /* Initialize the default values */
232 ret = WM8903_MultiRegWrite(g_wm8903, sizeof(g_wm8903) / sizeof(WM8903_Init_Seq_t));
233
234 /* Lastly turn the other clocks back on */
235 Chip_IOCON_PinMuxSet(LPC_IOCON, I2S_SCK_PORT, I2S_SCK_PIN, 0x1);
236 Chip_IOCON_PinMuxSet(LPC_IOCON, I2S_WS_PORT, I2S_WS_PIN, 0x1);
237
238 return ret;
239 }
240
241 void WM8903_DeInit(void)
242 {
243 Chip_I2CM_DeInit(CODEC_I2C_BUS);
244 }
245
246 typedef struct
247 {
248 char* name;
249 uint16_t address;
250 } wm8903_register_lookup_table;
251
252 wm8903_register_lookup_table registerLookupTable[] =
253 {
254 { "SW_RESET_AND_ID", WM8903_SW_RESET_AND_ID },
255 { "REVISION", WM8903_REVISION },
256 { "BIAS_CONTROL_0", WM8903_BIAS_CONTROL_0 },
257 { "VMID_CONTROL_0", WM8903_VMID_CONTROL_0 },
258 { "MIC_BIAS_CONTROL_0", WM8903_MIC_BIAS_CONTROL_0 },
259 { "ANALOGUE_DAC_0", WM8903_ANALOGUE_DAC_0 },
260 { "ANALOGUE_ADC_0", WM8903_ANALOGUE_ADC_0 },
261 { "POWER_MANAGEMENT_0", WM8903_POWER_MANAGEMENT_0 },
262 { "POWER_MANAGEMENT_1", WM8903_POWER_MANAGEMENT_1 },
263 { "POWER_MANAGEMENT_2", WM8903_POWER_MANAGEMENT_2 },
264 { "POWER_MANAGEMENT_3", WM8903_POWER_MANAGEMENT_3 },
265 { "POWER_MANAGEMENT_4", WM8903_POWER_MANAGEMENT_4 },
266 { "POWER_MANAGEMENT_5", WM8903_POWER_MANAGEMENT_5 },
267 { "POWER_MANAGEMENT_6", WM8903_POWER_MANAGEMENT_6 },
268 { "CLOCK_RATES_0", WM8903_CLOCK_RATES_0 },
269 { "CLOCK_RATES_1", WM8903_CLOCK_RATES_1 },
270 { "CLOCK_RATES_2", WM8903_CLOCK_RATES_2 },
271 { "AUDIO_INTERFACE_0", WM8903_AUDIO_INTERFACE_0 },
272 { "AUDIO_INTERFACE_1", WM8903_AUDIO_INTERFACE_1 },
273 { "AUDIO_INTERFACE_2", WM8903_AUDIO_INTERFACE_2 },
274 { "AUDIO_INTERFACE_3", WM8903_AUDIO_INTERFACE_3 },
275 { "DAC_DIGITAL_VOLUME_LEFT", WM8903_DAC_DIGITAL_VOLUME_LEFT },
276 { "DAC_DIGITAL_VOLUME_RIGHT", WM8903_DAC_DIGITAL_VOLUME_RIGHT },
277 { "DAC_DIGITAL_0", WM8903_DAC_DIGITAL_0 },
278 { "DAC_DIGITAL_1", WM8903_DAC_DIGITAL_1 },
279 { "ADC_DIGITAL_VOLUME_LEFT", WM8903_ADC_DIGITAL_VOLUME_LEFT },
280 { "ADC_DIGITAL_VOLUME_RIGHT", WM8903_ADC_DIGITAL_VOLUME_RIGHT },
281 { "ADC_DIGITAL_0", WM8903_ADC_DIGITAL_0 },
282 { "DRC_0", WM8903_DRC_0 },
283 { "DRC_1", WM8903_DRC_1 },
284 { "DRC_2", WM8903_DRC_2 },
285 { "DRC_3", WM8903_DRC_3 },
286 { "ANALOGUE_LEFT_INPUT_0", WM8903_ANALOGUE_LEFT_INPUT_0 },
287 { "ANALOGUE_RIGHT_INPUT_0", WM8903_ANALOGUE_RIGHT_INPUT_0 },
288 { "ANALOGUE_LEFT_INPUT_1", WM8903_ANALOGUE_LEFT_INPUT_1 },
289 { "ANALOGUE_RIGHT_INPUT_1", WM8903_ANALOGUE_RIGHT_INPUT_1 },
290 { "ANALOGUE_LEFT_MIX_0", WM8903_ANALOGUE_LEFT_MIX_0 },
291 { "ANALOGUE_RIGHT_MIX_0", WM8903_ANALOGUE_RIGHT_MIX_0 },
292 { "ANALOGUE_SPK_MIX_LEFT_0", WM8903_ANALOGUE_SPK_MIX_LEFT_0 },
293 { "ANALOGUE_SPK_MIX_LEFT_1", WM8903_ANALOGUE_SPK_MIX_LEFT_1 },
294 { "ANALOGUE_SPK_MIX_RIGHT_0", WM8903_ANALOGUE_SPK_MIX_RIGHT_0 },
295 { "ANALOGUE_SPK_MIX_RIGHT_1", WM8903_ANALOGUE_SPK_MIX_RIGHT_1 },
296 { "ANALOGUE_OUT1_LEFT", WM8903_ANALOGUE_OUT1_LEFT },
297 { "ANALOGUE_OUT1_RIGHT", WM8903_ANALOGUE_OUT1_RIGHT },
298 { "ANALOGUE_OUT2_LEFT", WM8903_ANALOGUE_OUT2_LEFT },
299 { "ANALOGUE_OUT2_RIGHT", WM8903_ANALOGUE_OUT2_RIGHT },
300 { "ANALOGUE_OUT3_LEFT", WM8903_ANALOGUE_OUT3_LEFT },
301 { "ANALOGUE_OUT3_RIGHT", WM8903_ANALOGUE_OUT3_RIGHT },
302 { "ANALOGUE_SPK_OUTPUT_CONTROL_0", WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0 },
303 { "DC_SERVO_0", WM8903_DC_SERVO_0 },
304 { "DC_SERVO_2", WM8903_DC_SERVO_2 },
305 { "DC_SERVO_4", WM8903_DC_SERVO_4 },
306 { "DC_SERVO_5", WM8903_DC_SERVO_5 },
307 { "DC_SERVO_6", WM8903_DC_SERVO_6 },
308 { "DC_SERVO_7", WM8903_DC_SERVO_7 },
309 { "DC_SERVO_READBACK_1", WM8903_DC_SERVO_READBACK_1 },
310 { "DC_SERVO_READBACK_2", WM8903_DC_SERVO_READBACK_2 },
311 { "DC_SERVO_READBACK_3", WM8903_DC_SERVO_READBACK_3 },
312 { "DC_SERVO_READBACK_4", WM8903_DC_SERVO_READBACK_4 },
313 { "ANALOGUE_HP_0", WM8903_ANALOGUE_HP_0 },
314 { "ANALOGUE_LINEOUT_0", WM8903_ANALOGUE_LINEOUT_0 },
315 { "CHARGE_PUMP_0", WM8903_CHARGE_PUMP_0 },
316 { "CLASS_W_0", WM8903_CLASS_W_0 },
317 { "WRITE_SEQUENCER_0", WM8903_WRITE_SEQUENCER_0 },
318 { "WRITE_SEQUENCER_1", WM8903_WRITE_SEQUENCER_1 },
319 { "WRITE_SEQUENCER_2", WM8903_WRITE_SEQUENCER_2 },
320 { "WRITE_SEQUENCER_3", WM8903_WRITE_SEQUENCER_3 },
321 { "WRITE_SEQUENCER_4", WM8903_WRITE_SEQUENCER_4 },
322 { "GPIO_CONTROL_1", WM8903_GPIO_CONTROL_1 },
323 { "GPIO_CONTROL_2", WM8903_GPIO_CONTROL_2 },
324 { "GPIO_CONTROL_3", WM8903_GPIO_CONTROL_3 },
325 { "GPIO_CONTROL_4", WM8903_GPIO_CONTROL_4 },
326 { "GPIO_CONTROL_5", WM8903_GPIO_CONTROL_5 },
327 { "INTERRUPT_STATUS", WM8903_INTERRUPT_STATUS },
328 { "INTERRUPT_STATUS_MASK", WM8903_INTERRUPT_STATUS_MASK },
329 { "INTERRUPT_POLARITY_1", WM8903_INTERRUPT_POLARITY_1 },
330 { "INTERRUPT_CONTROL", WM8903_INTERRUPT_CONTROL },
331 { "FLL_CONTROL_1", WM8903_FLL_CONTROL_1 },
332 { "FLL_CONTROL_2", WM8903_FLL_CONTROL_2 },
333 { "FLL_CONTROL_3", WM8903_FLL_CONTROL_3 },
334 { "FLL_CONTROL_4", WM8903_FLL_CONTROL_4 },
335 { "CLOCK_RATE_TEST_4", WM8903_CLOCK_RATE_TEST_4 },
336 { "ANALOGUE_OUTPUT_BIAS_0", WM8903_ANALOGUE_OUTPUT_BIAS_0 },
337 { "ANALOGUE_OUTPUT_BIAS_2", WM8903_ANALOGUE_OUTPUT_BIAS_2 },
338 /* Last entry has NULL to mark the end */
339 { NULL, 0 }
340 };
341
342 void WM8903_DumpAllRegisters(void)
343 {
344 uint32_t ix;
345 DEBUGOUT( "Dump of all registers:\r\n" );
346
347 /* Iterate through the list of registers... Exit loop when end is reached. */
348 for (ix=0; registerLookupTable[ix].name;ix++) {
349
350 uint16_t valueRead = WM8903_REG_Read( registerLookupTable[ix].address );
351
352 DEBUGOUT( "%-30s = 0x%04X\r\n", registerLookupTable[ix].name, valueRead );
353 }
354 }
355
356