LPCOpen Platform for LPC112X microcontrollers  112X
LPCOpen Platform for the NXP LPC112X family of Microcontrollers
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;
62 
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. */
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 */
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 ) {
126 
127  /* Delay the requested number of mS */
128  StopWatch_DelayMs( seq[i].reg_val );
129  break;
130 
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. */
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 
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);
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 */
221 
222  /* Send soft reset */
224 
225  /* Enable the master clock and give the part time to respond 2 milSeconds */
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 */
237 
238  return ret;
239 }
240 
241 void WM8903_DeInit(void)
242 {
244 }
245 
246 typedef struct
247 {
248  char* name;
249  uint16_t address;
251 
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 
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