arduino-audio-driver
Loading...
Searching...
No Matches
WM8904.h
Go to the documentation of this file.
1
8#pragma once
9
11
12namespace audio_driver {
13
15enum class WM8904Protocol {
16 RightJustified = 0x0,
17 LeftJustified = 0x1,
18 I2S = 0x2,
19 PCMA = 0x3,
20 PCMB = 0x3 | (1 << 4),
21};
22
24enum class WM8904FsRatio {
25 Ratio64X = 0x0,
26 Ratio128X = 0x1,
27 Ratio192X = 0x2,
28 Ratio256X = 0x3,
29 Ratio384X = 0x4,
30 Ratio512X = 0x5,
31 Ratio768X = 0x6,
32 Ratio1024X = 0x7,
33 Ratio1408X = 0x8,
34 Ratio1536X = 0x9,
35};
36
38enum class WM8904SampleRate {
39 Rate8kHz = 0x0,
40 Rate12kHz = 0x1,
41 Rate16kHz = 0x2,
42 Rate24kHz = 0x3,
43 Rate32kHz = 0x4,
44 Rate48kHz = 0x5,
45};
46
48enum class WM8904Channel {
53 All,
54};
55
63class WM8904 : public ZephyrDriverCommon {
64 public:
65 // ---- Register addresses ----
114
115 // ---- Volume defaults / limits ----
116 static constexpr uint16_t OUTPUT_VOLUME_MIN = 0b000000;
117 static constexpr uint16_t OUTPUT_VOLUME_MAX = 0b111111;
118 static constexpr uint16_t OUTPUT_VOLUME_DEFAULT = 0b101101;
119 static constexpr uint16_t INPUT_VOLUME_MIN = 0b00000;
120 static constexpr uint16_t INPUT_VOLUME_MAX = 0b11111;
121 static constexpr uint16_t INPUT_VOLUME_DEFAULT = 0b00101;
122
123 // ---- Output register bit masks (REG_ANALOG_OUT1/2_LEFT/RIGHT) ----
124 static constexpr uint16_t REGMASK_OUT_MUTE = 0b100000000;
125 static constexpr uint16_t REGMASK_OUT_VU = 0b010000000;
126 static constexpr uint16_t REGMASK_OUT_ZC = 0b001000000;
127 static constexpr uint16_t REGMASK_OUT_VOL = 0b000111111;
128
129 // ---- Input register bit masks (REG_ANALOG_LEFT/RIGHT_IN_0) ----
130 static constexpr uint8_t REGMASK_IN_MUTE = 0b10000000;
131 static constexpr uint8_t REGMASK_IN_VOLUME = 0b00011111;
132
133 // ---- Input select masks (REG_ANALOG_LEFT/RIGHT_IN_1) ----
134 static constexpr uint8_t REGMASK_INSEL_CMENA = 0b01000000;
135 static constexpr uint8_t REGMASK_INSEL_IP_SEL_N = 0b00110000;
136 static constexpr uint8_t REGMASK_INSEL_IP_SEL_P = 0b00001100;
137 static constexpr uint8_t REGMASK_INSEL_MODE = 0b00000011;
138
139 WM8904() { i2c_addr = 0x1A; }
140
153 bool begin(uint32_t sample_rate, uint8_t bits) {
154 return begin(WM8904Protocol::I2S, sample_rate, bits);
155 }
156
161 bool begin(uint32_t sample_rate, uint8_t bits, codec_mode_t mode,
162 i2s_format_t fmt, bool is_master, uint8_t channels) override {
163 (void)is_master;
164 (void)channels;
166 switch (fmt) {
167 case I2S_LEFT:
169 break;
170 case I2S_RIGHT:
172 break;
173 default:
174 protocol = WM8904Protocol::I2S;
175 break;
176 }
177 return begin(protocol, sample_rate, bits, mode & CODEC_MODE_DECODE,
178 mode & CODEC_MODE_ENCODE);
179 }
180
182 uint32_t sample_rate = 44100, uint8_t word_size = 16,
183 bool playback = true, bool capture = true) {
184 bool rc = true;
185
186 rc &= softReset();
187
188 /* MCLK_INV=0, SYSCLK_SRC=0, TOCLK_RATE=0, OPCLK_ENA=1,
189 * CLK_SYS_ENA=1, CLK_DSP_ENA=1, TOCLK_ENA=1 */
190 rc &= writeReg16(REG_CLK_RATES_2, 0x000F);
191
192 /* WSEQ_ENA=1, WSEQ_WRITE_INDEX=0_0000 */
193 rc &= writeReg16(REG_WRT_SEQUENCER_0, 0x0100);
194
195 /* WSEQ_ABORT=0, WSEQ_START=1, WSEQ_START_INDEX=00_0000 */
196 rc &= writeReg16(REG_WRT_SEQUENCER_3, 0x0100);
197
198 // wait until the write sequencer has finished
199 uint16_t value = 1;
200 int retries = 1000;
201 while ((value & 1U) != 0U && retries-- > 0) {
202 if (!readReg16(REG_WRT_SEQUENCER_4, value)) break;
203 }
204
205 /* TOCLK_RATE_DIV16=0, TOCLK_RATE_x4=1, SR_MODE=0, MCLK_DIV=1 */
206 rc &= writeReg16(REG_CLK_RATES_0, 0xA45F);
207
208 /* INL_ENA=1, INR_ENA=1 */
209 rc &= writeReg16(REG_POWER_MGMT_0, 0x0003);
210
211 /* HPL_PGA_ENA=1, HPR_PGA_ENA=1 */
212 rc &= writeReg16(REG_POWER_MGMT_2, 0x0003);
213
214 /* DACL_ENA=1, DACR_ENA=1, ADCL_ENA=1, ADCR_ENA=1 */
215 rc &= writeReg16(REG_POWER_MGMT_6, 0x000F);
216
217 /* ADC_OSR128=1 */
218 rc &= writeReg16(REG_ANALOG_ADC_0, 0x0001);
219
220 /* AIFADCR_SRC=1, AIFDACR_SRC=1 */
221 rc &= writeReg16(REG_AUDIO_IF_0, 0x0050);
222
223 /* DAC_OSR128=1 */
224 rc &= writeReg16(REG_DAC_DIG_1, 0x0040);
225
226 /* Enable DC servos for headphone out */
227 rc &= writeReg16(REG_DC_SERVO_0, 0x0003);
228
229 /* HPL_RMV_SHORT=1, HPL_ENA_OUTP=1, HPL_ENA_DLY=1, HPL_ENA=1,
230 * HPR_RMV_SHORT=1, HPR_ENA_OUTP=1, HPR_ENA_DLY=1, HPR_ENA=1 */
231 rc &= writeReg16(REG_ANALOG_HP_0, 0x00FF);
232
233 /* CP_DYN_PWR=1 */
234 rc &= writeReg16(REG_CLS_W_0, 0x0001);
235
236 /* CP_ENA=1 */
237 rc &= writeReg16(REG_CHRG_PUMP_0, 0x0001);
238
239 rc &= setProtocol(protocol);
240 rc &= setAudioFormat(sample_rate, word_size);
241
242 if (playback) rc &= configureOutput();
243 if (capture) rc &= configureInput();
244
245 return rc;
246 }
247
249 bool softReset() { return writeReg16(REG_RESET, 0x00); }
250
253 return updateReg16(REG_AUDIO_IF_1, (0x0003U | (1U << 4U)),
254 (uint16_t)protocol);
255 }
256
266 bool setAudioFormat(uint32_t sample_rate, uint8_t word_size = 16) {
267 bool rc = true;
268 WM8904SampleRate wm_sample_rate;
269
270 switch (sample_rate) {
271 case 8000:
272 wm_sample_rate = WM8904SampleRate::Rate8kHz;
273 break;
274 case 11025:
275 case 12000:
276 wm_sample_rate = WM8904SampleRate::Rate12kHz;
277 break;
278 case 16000:
279 wm_sample_rate = WM8904SampleRate::Rate16kHz;
280 break;
281 case 22050:
282 case 24000:
283 wm_sample_rate = WM8904SampleRate::Rate24kHz;
284 break;
285 case 32000:
286 wm_sample_rate = WM8904SampleRate::Rate32kHz;
287 break;
288 case 44100:
289 case 48000:
290 wm_sample_rate = WM8904SampleRate::Rate48kHz;
291 break;
292 default:
293 return false;
294 }
295
296 /* Disable SYSCLK */
297 rc &= writeReg16(REG_CLK_RATES_2, 0x00);
298
299 /* Set Clock ratio and sample rate (fs ratio fixed to 256X, matches
300 * MCLK_DIV=1 in REG_CLK_RATES_0 written during begin()) */
302 << 10U) |
303 (uint16_t)wm_sample_rate);
304
305 uint16_t word_size_bits = 0;
306 switch (word_size) {
307 case 16:
308 word_size_bits = 0;
309 break;
310 case 20:
311 word_size_bits = 1;
312 break;
313 case 24:
314 word_size_bits = 2;
315 break;
316 case 32:
317 word_size_bits = 3;
318 break;
319 default:
320 word_size_bits = 0;
321 break;
322 }
323 /* Set bit resolution */
324 rc &= updateReg16(REG_AUDIO_IF_1, 0x000CU, (word_size_bits << 2U));
325
326 /* Enable SYSCLK */
327 rc &= writeReg16(REG_CLK_RATES_2, 0x1007);
328
329 return rc;
330 }
331
333 bool setOutputVolume(uint8_t volume, WM8904Channel channel = WM8904Channel::All) {
334 if (volume > OUTPUT_VOLUME_MAX) volume = OUTPUT_VOLUME_MAX;
335 const uint16_t val = (uint16_t)((1U << 6U) | (volume & REGMASK_OUT_VOL));
336 const uint16_t mask = REGMASK_OUT_VU | REGMASK_OUT_ZC | REGMASK_OUT_VOL;
337 return updateOutput(channel, val, mask);
338 }
339
341 bool setOutputMute(bool mute, WM8904Channel channel = WM8904Channel::All) {
342 const uint16_t val = (uint16_t)((mute ? 1U : 0U) << 8U);
343 const uint16_t mask = REGMASK_OUT_MUTE;
344 return updateOutput(channel, val, mask);
345 }
346
348 bool setVolume(int volume) override {
349 volume_percent = volume < 0 ? 0 : (volume > 100 ? 100 : volume);
350 uint8_t vol = (uint8_t)((volume_percent * OUTPUT_VOLUME_MAX) / 100);
352 }
353
355 bool setMute(bool mute) override { return setOutputMute(mute, WM8904Channel::All); }
356
359 (void)input_device;
360 this->output_device = output_device;
361 return true;
362 }
363
366 bool setActive(codec_mode_t mode) override {
369 return rc;
370 }
371
373 bool setInputVolume(uint8_t volume, WM8904Channel channel = WM8904Channel::All) {
374 if (volume > INPUT_VOLUME_MAX) volume = INPUT_VOLUME_MAX;
375 const uint8_t val = (uint8_t)(volume & REGMASK_IN_VOLUME);
376 const uint8_t mask = REGMASK_IN_VOLUME;
377 return updateInput(channel, val, mask);
378 }
379
381 bool setInputVolume(int volume) override {
382 input_volume_percent = volume < 0 ? 0 : (volume > 100 ? 100 : volume);
383 uint8_t vol = (uint8_t)((input_volume_percent * INPUT_VOLUME_MAX) / 100);
385 }
386
387
389 bool isInputVolumeSupported() override { return true; }
390
392 bool setInputMute(bool mute, WM8904Channel channel = WM8904Channel::All) {
393 const uint8_t val = (uint8_t)((mute ? 1U : 0U) << 7U);
394 const uint8_t mask = REGMASK_IN_MUTE;
395 return updateInput(channel, val, mask);
396 }
397
404 bool routeInput(uint32_t input, WM8904Channel channel = WM8904Channel::All) {
405 if (input < 1 || input > 3) return false;
406
407 uint8_t val = (uint8_t)(((input - 1) & 0x03) << 4U) |
408 (uint8_t)(((input - 1) & 0x03) << 2U);
411
412 bool rc = true;
413 if (channel == WM8904Channel::FrontLeft || channel == WM8904Channel::All)
414 rc &= updateReg(REG_ANALOG_LEFT_IN_1, mask, val);
415 if (channel == WM8904Channel::FrontRight || channel == WM8904Channel::All)
416 rc &= updateReg(REG_ANALOG_RIGHT_IN_1, mask, val);
417 return rc;
418 }
419
422 bool rc = true;
424 (uint16_t)(1U << 7U));
426 (uint16_t)(1U << 7U));
427 return rc;
428 }
429
446
449 bool rc = true;
453 rc &= setInputMute(false, WM8904Channel::All);
454 return rc;
455 }
456
457 protected:
460
461 bool updateOutput(WM8904Channel channel, uint16_t val, uint16_t mask) {
462 bool rc = true;
463 switch (channel) {
465 return updateReg16(REG_ANALOG_OUT2_LEFT, mask, val);
467 return updateReg16(REG_ANALOG_OUT2_RIGHT, mask, val);
469 return updateReg16(REG_ANALOG_OUT1_LEFT, mask, val);
471 return updateReg16(REG_ANALOG_OUT1_RIGHT, mask, val);
473 rc &= updateReg16(REG_ANALOG_OUT1_LEFT, mask, val);
474 rc &= updateReg16(REG_ANALOG_OUT1_RIGHT, mask, val);
475 rc &= updateReg16(REG_ANALOG_OUT2_LEFT, mask, val);
476 rc &= updateReg16(REG_ANALOG_OUT2_RIGHT, mask, val);
477 return rc;
478 default:
479 return false;
480 }
481 }
482
483 bool updateInput(WM8904Channel channel, uint8_t val, uint8_t mask) {
484 bool rc = true;
485 switch (channel) {
487 return updateReg(REG_ANALOG_LEFT_IN_0, mask, val);
489 return updateReg(REG_ANALOG_RIGHT_IN_0, mask, val);
491 rc &= updateReg(REG_ANALOG_LEFT_IN_0, mask, val);
492 rc &= updateReg(REG_ANALOG_RIGHT_IN_0, mask, val);
493 return rc;
494 default:
495 return false;
496 }
497 }
498};
499
500} // namespace audio_driver
Header only C++ driver for the WM8904 audio codec.
Definition WM8904.h:63
bool setDevices(input_device_t input_device, output_device_t output_device) override
Stores the output device selection for use by configureOutput()
Definition WM8904.h:358
bool begin(uint32_t sample_rate, uint8_t bits)
Initialize the codec: soft reset, power up the analog/digital blocks, configure the digital audio int...
Definition WM8904.h:153
bool setAudioFormat(uint32_t sample_rate, uint8_t word_size=16)
Configure the sample rate and word size of the digital audio interface. The SYSCLK / fs ratio is dete...
Definition WM8904.h:266
WM8904()
Definition WM8904.h:139
bool softReset()
Soft reset of the codec (writes 0x00 to REG_RESET)
Definition WM8904.h:249
bool setInputVolume(uint8_t volume, WM8904Channel channel=WM8904Channel::All)
Set the input volume (0..31) for the given channel(s)
Definition WM8904.h:373
static constexpr uint8_t REGMASK_IN_MUTE
Definition WM8904.h:130
bool isInputVolumeSupported() override
Returns true: the input volume is supported.
Definition WM8904.h:389
static constexpr uint16_t OUTPUT_VOLUME_DEFAULT
Definition WM8904.h:118
bool configureInput()
Configure the default input path: route input 2, default volume, unmuted.
Definition WM8904.h:448
bool setMute(bool mute) override
Mutes/unmutes all outputs.
Definition WM8904.h:355
static constexpr uint8_t REGMASK_INSEL_MODE
Definition WM8904.h:137
bool begin(WM8904Protocol protocol=WM8904Protocol::I2S, uint32_t sample_rate=44100, uint8_t word_size=16, bool playback=true, bool capture=true)
Definition WM8904.h:181
bool routeInput(uint32_t input, WM8904Channel channel=WM8904Channel::All)
Route an analog input (1..3) to the given channel's PGA. Only FrontLeft, FrontRight and All are valid...
Definition WM8904.h:404
output_device_t output_device
Output device selection set via setDevices(), used by configureOutput()
Definition WM8904.h:459
bool setInputMute(bool mute, WM8904Channel channel=WM8904Channel::All)
Mute / unmute the input for the given channel(s)
Definition WM8904.h:392
bool configureOutput()
Definition WM8904.h:433
static constexpr uint8_t REGMASK_INSEL_CMENA
Definition WM8904.h:134
static constexpr uint8_t REGMASK_INSEL_IP_SEL_N
Definition WM8904.h:135
Reg
Definition WM8904.h:66
@ REG_ANALOG_RIGHT_IN_0
Definition WM8904.h:87
@ REG_CLK_RATES_1
Definition WM8904.h:74
@ REG_DAC_DIG_1
Definition WM8904.h:82
@ REG_ANALOG_RIGHT_IN_1
Definition WM8904.h:89
@ REG_GPIO_CONTROL_3
Definition WM8904.h:109
@ REG_AUDIO_IF_3
Definition WM8904.h:79
@ REG_DAC_DIGITAL_VOLUME_RIGHT
Definition WM8904.h:81
@ REG_DC_SERVO_0
Definition WM8904.h:95
@ REG_ANALOG_OUT1_LEFT
Definition WM8904.h:90
@ REG_CLK_RATES_0
Definition WM8904.h:73
@ REG_POWER_MGMT_2
Definition WM8904.h:70
@ REG_CLK_RATES_2
Definition WM8904.h:75
@ REG_GPIO_CONTROL_1
Definition WM8904.h:107
@ REG_AUDIO_IF_0
Definition WM8904.h:76
@ REG_WRT_SEQUENCER_0
Definition WM8904.h:99
@ REG_ANALOG_OUT12_ZC
Definition WM8904.h:94
@ REG_CLS_W_0
Definition WM8904.h:98
@ REG_ANALOG_OUT1_RIGHT
Definition WM8904.h:91
@ REG_DAC_DIGITAL_VOLUME_LEFT
Definition WM8904.h:80
@ REG_FLL_CONTROL_4
Definition WM8904.h:105
@ REG_CHRG_PUMP_0
Definition WM8904.h:97
@ REG_DAC_DIG_0
Definition WM8904.h:85
@ REG_AUDIO_IF_1
Definition WM8904.h:77
@ REG_FLL_NCO_TEST_0
Definition WM8904.h:111
@ REG_ANALOG_OUT2_RIGHT
Definition WM8904.h:93
@ REG_ADC_DIGITAL_VOLUME_RIGHT
Definition WM8904.h:84
@ REG_FLL_CONTROL_2
Definition WM8904.h:103
@ REG_POWER_MGMT_0
Definition WM8904.h:69
@ REG_FLL_NCO_TEST_1
Definition WM8904.h:112
@ REG_ANALOG_OUT2_LEFT
Definition WM8904.h:92
@ REG_RESET
Definition WM8904.h:67
@ REG_FLL_CONTROL_1
Definition WM8904.h:102
@ REG_ANALOG_LEFT_IN_1
Definition WM8904.h:88
@ REG_POWER_MGMT_3
Definition WM8904.h:71
@ REG_AUDIO_IF_2
Definition WM8904.h:78
@ REG_ANALOG_HP_0
Definition WM8904.h:96
@ REG_WRT_SEQUENCER_3
Definition WM8904.h:100
@ REG_FLL_CONTROL_3
Definition WM8904.h:104
@ REG_WRT_SEQUENCER_4
Definition WM8904.h:101
@ REG_ADC_DIGITAL_VOLUME_LEFT
Definition WM8904.h:83
@ REG_ANALOG_LEFT_IN_0
Definition WM8904.h:86
@ REG_GPIO_CONTROL_4
Definition WM8904.h:110
@ REG_GPIO_CONTROL_2
Definition WM8904.h:108
@ REG_ANALOG_ADC_0
Definition WM8904.h:68
@ REG_POWER_MGMT_6
Definition WM8904.h:72
@ REG_FLL_CONTROL_5
Definition WM8904.h:106
static constexpr uint16_t OUTPUT_VOLUME_MAX
Definition WM8904.h:117
static constexpr uint16_t REGMASK_OUT_VU
Definition WM8904.h:125
static constexpr uint16_t REGMASK_OUT_ZC
Definition WM8904.h:126
static constexpr uint16_t INPUT_VOLUME_MAX
Definition WM8904.h:120
static constexpr uint16_t REGMASK_OUT_VOL
Definition WM8904.h:127
static constexpr uint8_t REGMASK_IN_VOLUME
Definition WM8904.h:131
bool setOutputMute(bool mute, WM8904Channel channel=WM8904Channel::All)
Mute / unmute the output for the given channel(s)
Definition WM8904.h:341
bool updateOutput(WM8904Channel channel, uint16_t val, uint16_t mask)
Definition WM8904.h:461
bool begin(uint32_t sample_rate, uint8_t bits, codec_mode_t mode, i2s_format_t fmt, bool is_master, uint8_t channels) override
Definition WM8904.h:161
bool applyProperties()
Apply the pending volume changes (set VU bit for both output channel pairs)
Definition WM8904.h:421
bool setOutputVolume(uint8_t volume, WM8904Channel channel=WM8904Channel::All)
Set the output volume (0..63) for the given channel(s)
Definition WM8904.h:333
bool setActive(codec_mode_t mode) override
Definition WM8904.h:366
static constexpr uint16_t INPUT_VOLUME_MIN
Definition WM8904.h:119
static constexpr uint16_t REGMASK_OUT_MUTE
Definition WM8904.h:124
bool updateInput(WM8904Channel channel, uint8_t val, uint8_t mask)
Definition WM8904.h:483
bool setInputVolume(int volume) override
Sets the input volume in % (0...100) for all channels.
Definition WM8904.h:381
static constexpr uint16_t INPUT_VOLUME_DEFAULT
Definition WM8904.h:121
bool setVolume(int volume) override
Sets the output volume in % (0...100) for all channels.
Definition WM8904.h:348
static constexpr uint16_t OUTPUT_VOLUME_MIN
Definition WM8904.h:116
bool setProtocol(WM8904Protocol protocol)
Configure the digital audio interface protocol (I2S, LJ, RJ, PCMA/B)
Definition WM8904.h:252
static constexpr uint8_t REGMASK_INSEL_IP_SEL_P
Definition WM8904.h:136
Common parent class for codec drivers that have been ported from the Zephyr RTOS audio driver impleme...
Definition ZephyrDriverCommon.h:22
uint8_t i2c_addr
Definition ZephyrDriverCommon.h:87
bool readReg16(uint8_t reg, uint16_t &value)
Reads a 16 bit (big endian) value from an 8 bit register address.
Definition ZephyrDriverCommon.h:126
bool updateReg(uint8_t reg, uint8_t mask, uint8_t value)
Read-Modify-Write of a single byte register.
Definition ZephyrDriverCommon.h:110
bool writeReg16(uint8_t reg, uint16_t value)
Writes a 16 bit (big endian) value to an 8 bit register address.
Definition ZephyrDriverCommon.h:119
int volume_percent
Last volume (in %) provided to setVolume()
Definition ZephyrDriverCommon.h:89
bool updateReg16(uint8_t reg, uint16_t mask, uint16_t value)
Read-Modify-Write of a 16 bit (big endian) register.
Definition ZephyrDriverCommon.h:136
int input_volume_percent
Last input volume (in %) provided to setInputVolume()
Definition ZephyrDriverCommon.h:91
Namespace for audio driver components.
Definition AudioBoard.h:5
WM8904FsRatio
SYSCLK / fs ratio.
Definition WM8904.h:24
WM8904Channel
Logical audio channels.
Definition WM8904.h:48
WM8904SampleRate
Sample rate (WM8904_REG_CLK_RATES_1 SAMPLE_RATE field)
Definition WM8904.h:38
input_device_t
Definition DriverCommon.h:170
codec_mode_t
Definition DriverCommon.h:290
@ CODEC_MODE_ENCODE
Definition DriverCommon.h:293
@ CODEC_MODE_DECODE
Definition DriverCommon.h:294
output_device_t
Definition DriverCommon.h:186
@ DAC_OUTPUT_LINE1
Definition DriverCommon.h:188
@ DAC_OUTPUT_ALL
Definition DriverCommon.h:190
@ DAC_OUTPUT_LINE2
Definition DriverCommon.h:189
WM8904Protocol
Audio data transfer protocol.
Definition WM8904.h:15
i2s_format_t
Definition DriverCommon.h:250
@ I2S_RIGHT
Definition DriverCommon.h:253
@ I2S_LEFT
Definition DriverCommon.h:252