1 /*
2 * Copyright 2024 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <errno.h>
7
8 #include <zephyr/device.h>
9 #include <zephyr/drivers/i2c.h>
10 #include <zephyr/drivers/clock_control.h>
11 #include <zephyr/audio/codec.h>
12 #include <zephyr/devicetree/clocks.h>
13
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(wolfson_wm8904);
16
17 #include "wm8904.h"
18
19 #define DT_DRV_COMPAT wolfson_wm8904
20
21 struct wm8904_driver_config {
22 struct i2c_dt_spec i2c;
23 int clock_source;
24 const struct device *mclk_dev;
25 clock_control_subsys_t mclk_name;
26 };
27
28 #define DEV_CFG(dev) ((const struct wm8904_driver_config *const)dev->config)
29
30 static void wm8904_write_reg(const struct device *dev, uint8_t reg, uint16_t val);
31 static void wm8904_read_reg(const struct device *dev, uint8_t reg, uint16_t *val);
32 static void wm8904_update_reg(const struct device *dev, uint8_t reg, uint16_t mask, uint16_t val);
33 static void wm8904_soft_reset(const struct device *dev);
34
35 static void wm8904_configure_output(const struct device *dev);
36
37 static void wm8904_configure_input(const struct device *dev);
38
wm8904_protocol_config(const struct device * dev,audio_dai_type_t dai_type)39 static int wm8904_protocol_config(const struct device *dev, audio_dai_type_t dai_type)
40 {
41 wm8904_protocol_t proto;
42
43 switch (dai_type) {
44 case AUDIO_DAI_TYPE_I2S:
45 proto = kWM8904_ProtocolI2S;
46 break;
47 case AUDIO_DAI_TYPE_LEFT_JUSTIFIED:
48 proto = kWM8904_ProtocolLeftJustified;
49 break;
50 case AUDIO_DAI_TYPE_RIGHT_JUSTIFIED:
51 proto = kWM8904_ProtocolRightJustified;
52 break;
53 case AUDIO_DAI_TYPE_PCMA:
54 proto = kWM8904_ProtocolPCMA;
55 break;
56 case AUDIO_DAI_TYPE_PCMB:
57 proto = kWM8904_ProtocolPCMB;
58 break;
59 default:
60 return -EINVAL;
61 }
62
63 wm8904_update_reg(dev, WM8904_REG_AUDIO_IF_1, (0x0003U | (1U << 4U)), (uint16_t)proto);
64
65 LOG_DBG("Codec protocol: %#x", proto);
66 return 0;
67 }
68
wm8904_audio_fmt_config(const struct device * dev,audio_dai_cfg_t * cfg,uint32_t mclk)69 static int wm8904_audio_fmt_config(const struct device *dev, audio_dai_cfg_t *cfg, uint32_t mclk)
70 {
71 wm8904_sample_rate_t wm_sample_rate;
72 uint32_t fs;
73 uint16_t wmfs_ratio;
74 uint16_t mclkDiv;
75 uint16_t word_size = cfg->i2s.word_size;
76
77 switch (cfg->i2s.frame_clk_freq) {
78 case 8000:
79 wm_sample_rate = kWM8904_SampleRate8kHz;
80 break;
81 case 11025:
82 wm_sample_rate = kWM8904_SampleRate11025Hz;
83 break;
84 case 12000:
85 wm_sample_rate = kWM8904_SampleRate12kHz;
86 break;
87 case 16000:
88 wm_sample_rate = kWM8904_SampleRate16kHz;
89 break;
90 case 22050:
91 wm_sample_rate = kWM8904_SampleRate22050Hz;
92 break;
93 case 24000:
94 wm_sample_rate = kWM8904_SampleRate24kHz;
95 break;
96 case 32000:
97 wm_sample_rate = kWM8904_SampleRate32kHz;
98 break;
99 case 44100:
100 wm_sample_rate = kWM8904_SampleRate44100Hz;
101 break;
102 case 48000:
103 wm_sample_rate = kWM8904_SampleRate48kHz;
104 break;
105 default:
106 LOG_WRN("Invalid codec sample rate: %d", cfg->i2s.frame_clk_freq);
107 return -EINVAL;
108 }
109
110 wm8904_read_reg(dev, WM8904_REG_CLK_RATES_0, &mclkDiv);
111 fs = (mclk >> (mclkDiv & 0x1U)) / cfg->i2s.frame_clk_freq;
112
113 switch (fs) {
114 case 64:
115 wmfs_ratio = kWM8904_FsRatio64X;
116 break;
117 case 128:
118 wmfs_ratio = kWM8904_FsRatio128X;
119 break;
120 case 192:
121 wmfs_ratio = kWM8904_FsRatio192X;
122 break;
123 case 256:
124 wmfs_ratio = kWM8904_FsRatio256X;
125 break;
126 case 384:
127 wmfs_ratio = kWM8904_FsRatio384X;
128 break;
129 case 512:
130 wmfs_ratio = kWM8904_FsRatio512X;
131 break;
132 case 768:
133 wmfs_ratio = kWM8904_FsRatio768X;
134 break;
135 case 1024:
136 wmfs_ratio = kWM8904_FsRatio1024X;
137 break;
138 case 1408:
139 wmfs_ratio = kWM8904_FsRatio1408X;
140 break;
141 case 1536:
142 wmfs_ratio = kWM8904_FsRatio1536X;
143 break;
144 default:
145 LOG_WRN("Invalid Fs ratio: %d", fs);
146 return -EINVAL;
147 }
148
149 /* Disable SYSCLK */
150 wm8904_write_reg(dev, WM8904_REG_CLK_RATES_2, 0x00);
151
152 /* Set Clock ratio and sample rate */
153 wm8904_write_reg(dev, WM8904_REG_CLK_RATES_1,
154 ((wmfs_ratio) << 10U) | (uint16_t)(wm_sample_rate));
155
156 switch (cfg->i2s.word_size) {
157 case 16:
158 word_size = 0;
159 break;
160 case 20:
161 word_size = 1;
162 break;
163 case 24:
164 word_size = 2;
165 break;
166 case 32:
167 word_size = 3;
168 break;
169 default:
170 LOG_ERR("Word size %d bits not supported; falling back to 16 bits",
171 cfg->i2s.word_size);
172 word_size = 0;
173 break;
174 }
175 /* Set bit resolution */
176 wm8904_update_reg(dev, WM8904_REG_AUDIO_IF_1, (0x000CU), ((uint16_t)(word_size) << 2U));
177
178 /* Enable SYSCLK */
179 wm8904_write_reg(dev, WM8904_REG_CLK_RATES_2, 0x1007);
180 return 0;
181 }
182
wm8904_out_update(const struct device * dev,audio_channel_t channel,uint16_t val,uint16_t mask)183 static int wm8904_out_update(
184 const struct device *dev,
185 audio_channel_t channel,
186 uint16_t val,
187 uint16_t mask
188 )
189 {
190 switch (channel) {
191 case AUDIO_CHANNEL_FRONT_LEFT:
192 wm8904_update_reg(dev, WM8904_REG_ANALOG_OUT2_LEFT, mask, val);
193 return 0;
194
195 case AUDIO_CHANNEL_FRONT_RIGHT:
196 wm8904_update_reg(dev, WM8904_REG_ANALOG_OUT2_RIGHT, mask, val);
197 return 0;
198
199 case AUDIO_CHANNEL_HEADPHONE_LEFT:
200 wm8904_update_reg(dev, WM8904_REG_ANALOG_OUT1_LEFT, mask, val);
201 return 0;
202
203 case AUDIO_CHANNEL_HEADPHONE_RIGHT:
204 wm8904_update_reg(dev, WM8904_REG_ANALOG_OUT1_RIGHT, mask, val);
205 return 0;
206
207 case AUDIO_CHANNEL_ALL:
208 wm8904_update_reg(dev, WM8904_REG_ANALOG_OUT1_LEFT, mask, val);
209 wm8904_update_reg(dev, WM8904_REG_ANALOG_OUT1_RIGHT, mask, val);
210 wm8904_update_reg(dev, WM8904_REG_ANALOG_OUT2_LEFT, mask, val);
211 wm8904_update_reg(dev, WM8904_REG_ANALOG_OUT2_RIGHT, mask, val);
212 return 0;
213
214 default:
215 return -EINVAL;
216 }
217 }
218
wm8904_out_volume_config(const struct device * dev,audio_channel_t channel,int volume)219 static int wm8904_out_volume_config(const struct device *dev, audio_channel_t channel, int volume)
220 {
221 /* Set volume values with VU = 0 */
222 const uint16_t val = WM8904_REGVAL_OUT_VOL(0, 0, 1, volume);
223 const uint16_t mask = WM8904_REGMASK_OUT_VU
224 | WM8904_REGMASK_OUT_ZC
225 | WM8904_REGMASK_OUT_VOL;
226
227 return wm8904_out_update(dev, channel, val, mask);
228 }
229
wm8904_out_mute_config(const struct device * dev,audio_channel_t channel,bool mute)230 static int wm8904_out_mute_config(const struct device *dev, audio_channel_t channel, bool mute)
231 {
232 const uint16_t val = WM8904_REGVAL_OUT_VOL(mute, 0, 0, 0);
233 const uint16_t mask = WM8904_REGMASK_OUT_MUTE;
234
235 return wm8904_out_update(dev, channel, val, mask);
236 }
237
wm8904_in_update(const struct device * dev,audio_channel_t channel,uint16_t mask,uint16_t val)238 static int wm8904_in_update(
239 const struct device *dev,
240 audio_channel_t channel,
241 uint16_t mask,
242 uint16_t val
243 )
244 {
245 switch (channel) {
246 case AUDIO_CHANNEL_FRONT_LEFT:
247 wm8904_update_reg(dev, WM8904_REG_ANALOG_LEFT_IN_0, mask, val);
248 return 0;
249
250 case AUDIO_CHANNEL_FRONT_RIGHT:
251 wm8904_update_reg(dev, WM8904_REG_ANALOG_RIGHT_IN_0, mask, val);
252 return 0;
253
254 case AUDIO_CHANNEL_ALL:
255 wm8904_update_reg(dev, WM8904_REG_ANALOG_LEFT_IN_0, mask, val);
256 wm8904_update_reg(dev, WM8904_REG_ANALOG_RIGHT_IN_0, mask, val);
257 return 0;
258
259 default:
260 return -EINVAL;
261 }
262 }
263
wm8904_in_volume_config(const struct device * dev,audio_channel_t channel,int volume)264 static int wm8904_in_volume_config(const struct device *dev, audio_channel_t channel, int volume)
265 {
266 const uint16_t val = WM8904_REGVAL_IN_VOL(0, volume);
267 const uint16_t mask = WM8904_REGMASK_IN_MUTE;
268
269 return wm8904_in_update(dev, channel, val, mask);
270 }
271
wm8904_in_mute_config(const struct device * dev,audio_channel_t channel,bool mute)272 static int wm8904_in_mute_config(const struct device *dev, audio_channel_t channel, bool mute)
273 {
274 const uint16_t val = WM8904_REGVAL_IN_VOL(mute, 0);
275 const uint16_t mask = WM8904_REGMASK_IN_MUTE;
276
277 return wm8904_in_update(dev, channel, val, mask);
278 }
279
wm8904_route_input(const struct device * dev,audio_channel_t channel,uint32_t input)280 static int wm8904_route_input(const struct device *dev, audio_channel_t channel, uint32_t input)
281 {
282 if (input < 1 || input > 3) {
283 return -EINVAL;
284 }
285
286 uint8_t val = WM8904_REGVAL_INSEL(0, input - 1, input - 1, 0);
287 uint8_t mask = WM8904_REGMASK_INSEL_CMENA
288 | WM8904_REGMASK_INSEL_IP_SEL_P
289 | WM8904_REGMASK_INSEL_IP_SEL_N
290 | WM8904_REGMASK_INSEL_MODE;
291 uint8_t reg;
292
293 switch (channel) {
294 case AUDIO_CHANNEL_FRONT_LEFT:
295 reg = WM8904_REG_ANALOG_LEFT_IN_1;
296 break;
297
298 case AUDIO_CHANNEL_FRONT_RIGHT:
299 reg = WM8904_REG_ANALOG_RIGHT_IN_1;
300 break;
301
302 default:
303 return -EINVAL;
304 }
305
306 wm8904_update_reg(dev, reg, mask, val);
307 return 0;
308 }
309
wm8904_set_master_clock(const struct device * dev,audio_dai_cfg_t * cfg,uint32_t sysclk)310 static void wm8904_set_master_clock(const struct device *dev, audio_dai_cfg_t *cfg, uint32_t sysclk)
311 {
312 uint32_t sampleRate = cfg->i2s.frame_clk_freq;
313 uint32_t bitWidth = cfg->i2s.word_size;
314 uint32_t bclk = sampleRate * bitWidth * 2U;
315 uint32_t bclkDiv = 0U;
316 uint16_t audioInterface = 0U;
317 uint16_t sysclkDiv = 0U;
318
319 wm8904_read_reg(dev, WM8904_REG_CLK_RATES_0, &sysclkDiv);
320 sysclk = sysclk >> (sysclkDiv & 0x1U);
321 LOG_DBG("Codec sysclk: %d", sysclk);
322
323 if ((sysclk / bclk > 48U) || (bclk / sampleRate > 2047U) || (bclk / sampleRate < 8U)) {
324 LOG_ERR("Invalid BCLK clock divider configured.");
325 return;
326 }
327
328 wm8904_read_reg(dev, WM8904_REG_AUDIO_IF_2, &audioInterface);
329
330 audioInterface &= ~(uint16_t)0x1FU;
331 bclkDiv = (sysclk * 10U) / bclk;
332 LOG_INF("blk %d", bclk);
333
334 switch (bclkDiv) {
335 case 10:
336 audioInterface |= 0U;
337 break;
338 case 15:
339 audioInterface |= 1U;
340 break;
341 case 20:
342 /* Avoid MISRA 16.4 violation */
343 break;
344 case 30:
345 /* Avoid MISRA 16.4 violation */
346 break;
347 case 40:
348 /* Avoid MISRA 16.4 violation */
349 break;
350 case 50:
351 audioInterface |= (uint16_t)bclkDiv / 10U;
352 break;
353 case 55:
354 audioInterface |= 6U;
355 break;
356 case 60:
357 audioInterface |= 7U;
358 break;
359 case 80:
360 audioInterface |= 8U;
361 break;
362 case 100:
363 /* Avoid MISRA 16.4 violation */
364 break;
365 case 110:
366 /* Avoid MISRA 16.4 violation */
367 break;
368 case 120:
369 audioInterface |= (uint16_t)bclkDiv / 10U - 1U;
370 break;
371 case 160:
372 audioInterface |= 12U;
373 break;
374 case 200:
375 audioInterface |= 13U;
376 break;
377 case 220:
378 audioInterface |= 14U;
379 break;
380 case 240:
381 audioInterface |= 15U;
382 break;
383 case 250:
384 audioInterface |= 16U;
385 break;
386 case 300:
387 audioInterface |= 17U;
388 break;
389 case 320:
390 audioInterface |= 18U;
391 break;
392 case 440:
393 audioInterface |= 19U;
394 break;
395 case 480:
396 audioInterface |= 20U;
397 break;
398 default:
399 LOG_ERR("invalid audio interface for wm8904 %d", bclkDiv);
400 return;
401 }
402
403 /* bclk divider */
404 wm8904_write_reg(dev, WM8904_REG_AUDIO_IF_2, audioInterface);
405 /* bclk direction output */
406 wm8904_update_reg(dev, WM8904_REG_AUDIO_IF_1, 1U << 6U, 1U << 6U);
407
408 wm8904_update_reg(dev, WM8904_REG_GPIO_CONTROL_4, 0x8FU, 1U);
409 /* LRCLK direction and divider */
410 audioInterface = (uint16_t)((1UL << 11U) | (bclk / sampleRate));
411 wm8904_update_reg(dev, WM8904_REG_AUDIO_IF_3, 0xFFFU, audioInterface);
412 }
413
wm8904_configure(const struct device * dev,struct audio_codec_cfg * cfg)414 static int wm8904_configure(const struct device *dev, struct audio_codec_cfg *cfg)
415 {
416 uint16_t value;
417 const struct wm8904_driver_config *const dev_cfg = DEV_CFG(dev);
418
419 if (cfg->dai_type >= AUDIO_DAI_TYPE_INVALID) {
420 LOG_ERR("dai_type not supported");
421 return -EINVAL;
422 }
423
424 wm8904_soft_reset(dev);
425
426 if (cfg->dai_route == AUDIO_ROUTE_BYPASS) {
427 return 0;
428 }
429
430 /* MCLK_INV=0, SYSCLK_SRC=0, TOCLK_RATE=0, OPCLK_ENA=1,
431 * CLK_SYS_ENA=1, CLK_DSP_ENA=1, TOCLK_ENA=1
432 */
433 wm8904_write_reg(dev, WM8904_REG_CLK_RATES_2, 0x000F);
434
435 /* WSEQ_ENA=1, WSEQ_WRITE_INDEX=0_0000 */
436 wm8904_write_reg(dev, WM8904_REG_WRT_SEQUENCER_0, 0x0100);
437
438 /* WSEQ_ABORT=0, WSEQ_START=1, WSEQ_START_INDEX=00_0000 */
439 wm8904_write_reg(dev, WM8904_REG_WRT_SEQUENCER_3, 0x0100);
440
441 do {
442 wm8904_read_reg(dev, WM8904_REG_WRT_SEQUENCER_4, &value);
443 } while (((value & 1U) != 0U));
444
445 /* TOCLK_RATE_DIV16=0, TOCLK_RATE_x4=1, SR_MODE=0, MCLK_DIV=1
446 * (Required for MMCs: SGY, KRT see erratum CE000546)
447 */
448 wm8904_write_reg(dev, WM8904_REG_CLK_RATES_0, 0xA45F);
449
450 /* INL_ENA=1, INR ENA=1 */
451 wm8904_write_reg(dev, WM8904_REG_POWER_MGMT_0, 0x0003);
452
453 /* HPL_PGA_ENA=1, HPR_PGA_ENA=1 */
454 wm8904_write_reg(dev, WM8904_REG_POWER_MGMT_2, 0x0003);
455
456 /* DACL_ENA=1, DACR_ENA=1, ADCL_ENA=1, ADCR_ENA=1 */
457 wm8904_write_reg(dev, WM8904_REG_POWER_MGMT_6, 0x000F);
458
459 /* ADC_OSR128=1 */
460 wm8904_write_reg(dev, WM8904_REG_ANALOG_ADC_0, 0x0001);
461
462 /* DACL_DATINV=0, DACR_DATINV=0, DAC_BOOST=00, LOOPBACK=0, AIFADCL_SRC=0,
463 * AIFADCR_SRC=1, AIFDACL_SRC=0, AIFDACR_SRC=1, ADC_COMP=0, ADC_COMPMODE=0,
464 * DAC_COMP=0, DAC_COMPMODE=0
465 */
466 wm8904_write_reg(dev, WM8904_REG_AUDIO_IF_0, 0x0050);
467
468 /* DAC_MONO=0, DAC_SB_FILT-0, DAC_MUTERATE=0, DAC_UNMUTE RAMP=0,
469 * DAC_OSR128=1, DAC_MUTE=0, DEEMPH=0 (none)
470 */
471 wm8904_write_reg(dev, WM8904_REG_DAC_DIG_1, 0x0040);
472
473 /* Enable DC servos for headphone out */
474 wm8904_write_reg(dev, WM8904_REG_DC_SERVO_0, 0x0003);
475
476 /* HPL_RMV_SHORT=1, HPL_ENA_OUTP=1, HPL_ENA_DLY=1, HPL_ENA=1,
477 * HPR_RMV_SHORT=1, HPR_ENA_OUTP=1, HPR_ENA_DLY=1, HPR_ENA=1
478 */
479 wm8904_write_reg(dev, WM8904_REG_ANALOG_HP_0, 0x00FF);
480
481 /* CP_DYN_PWR=1 */
482 wm8904_write_reg(dev, WM8904_REG_CLS_W_0, 0x0001);
483
484 /* CP_ENA=1 */
485 wm8904_write_reg(dev, WM8904_REG_CHRG_PUMP_0, 0x0001);
486
487 wm8904_protocol_config(dev, cfg->dai_type);
488 wm8904_update_reg(dev, WM8904_REG_CLK_RATES_2, (uint16_t)(1UL << 14U),
489 (uint16_t)(dev_cfg->clock_source));
490
491 if (dev_cfg->clock_source == 0) {
492 int err = clock_control_on(dev_cfg->mclk_dev, dev_cfg->mclk_name);
493
494 if (err < 0) {
495 LOG_ERR("MCLK clock source enable fail: %d", err);
496 }
497
498 err = clock_control_get_rate(dev_cfg->mclk_dev, dev_cfg->mclk_name,
499 &cfg->mclk_freq);
500 if (err < 0) {
501 LOG_ERR("MCLK clock source freq acquire fail: %d", err);
502 }
503 }
504
505 wm8904_audio_fmt_config(dev, &cfg->dai_cfg, cfg->mclk_freq);
506
507 if ((cfg->dai_cfg.i2s.options & I2S_OPT_FRAME_CLK_MASTER) == I2S_OPT_FRAME_CLK_MASTER) {
508 wm8904_set_master_clock(dev, &cfg->dai_cfg, cfg->mclk_freq);
509 } else {
510 /* BCLK/LRCLK default direction input */
511 wm8904_update_reg(dev, WM8904_REG_AUDIO_IF_1, 1U << 6U, 0U);
512 wm8904_update_reg(dev, WM8904_REG_AUDIO_IF_3, (uint16_t)(1UL << 11U), 0U);
513 }
514
515 switch (cfg->dai_route) {
516 case AUDIO_ROUTE_PLAYBACK:
517 wm8904_configure_output(dev);
518 break;
519
520 case AUDIO_ROUTE_CAPTURE:
521 wm8904_configure_input(dev);
522 break;
523
524 case AUDIO_ROUTE_PLAYBACK_CAPTURE:
525 wm8904_configure_output(dev);
526 wm8904_configure_input(dev);
527 break;
528
529 default:
530 break;
531 }
532
533 return 0;
534 }
535
wm8904_start_output(const struct device * dev)536 static void wm8904_start_output(const struct device *dev)
537 {
538 }
539
wm8904_stop_output(const struct device * dev)540 static void wm8904_stop_output(const struct device *dev)
541 {
542 }
543
wm8904_set_property(const struct device * dev,audio_property_t property,audio_channel_t channel,audio_property_value_t val)544 static int wm8904_set_property(const struct device *dev, audio_property_t property,
545 audio_channel_t channel, audio_property_value_t val)
546 {
547 switch (property) {
548 case AUDIO_PROPERTY_OUTPUT_VOLUME:
549 return wm8904_out_volume_config(dev, channel, val.vol);
550
551 case AUDIO_PROPERTY_OUTPUT_MUTE:
552 return wm8904_out_mute_config(dev, channel, val.mute);
553
554 case AUDIO_PROPERTY_INPUT_VOLUME:
555 return wm8904_in_volume_config(dev, channel, val.vol);
556
557 case AUDIO_PROPERTY_INPUT_MUTE:
558 return wm8904_in_mute_config(dev, channel, val.mute);
559 }
560
561 return -EINVAL;
562 }
563
wm8904_apply_properties(const struct device * dev)564 static int wm8904_apply_properties(const struct device *dev)
565 {
566 /**
567 * Set VU = 1 for all output channels, VU takes effect for the whole
568 * channel pair.
569 */
570 wm8904_update_reg(
571 dev,
572 WM8904_REG_ANALOG_OUT1_LEFT,
573 WM8904_REGVAL_OUT_VOL(0, 1, 0, 0),
574 WM8904_REGMASK_OUT_MUTE
575 );
576 wm8904_update_reg(dev,
577 WM8904_REG_ANALOG_OUT2_LEFT,
578 WM8904_REGVAL_OUT_VOL(0, 1, 0, 0),
579 WM8904_REGMASK_OUT_MUTE
580 );
581
582 return 0;
583 }
584
wm8904_write_reg(const struct device * dev,uint8_t reg,uint16_t val)585 static void wm8904_write_reg(const struct device *dev, uint8_t reg, uint16_t val)
586 {
587 const struct wm8904_driver_config *const dev_cfg = DEV_CFG(dev);
588 uint8_t data[3];
589 int ret;
590
591 /* data is reversed */
592 data[0] = reg;
593 data[1] = (val >> 8) & 0xff;
594 data[2] = val & 0xff;
595
596 ret = i2c_write(dev_cfg->i2c.bus, data, 3, dev_cfg->i2c.addr);
597
598 if (ret != 0) {
599 LOG_ERR("i2c write to codec error %d", ret);
600 }
601
602 LOG_DBG("REG:%02u VAL:%#02x", reg, val);
603 }
604
wm8904_read_reg(const struct device * dev,uint8_t reg,uint16_t * val)605 static void wm8904_read_reg(const struct device *dev, uint8_t reg, uint16_t *val)
606 {
607 const struct wm8904_driver_config *const dev_cfg = DEV_CFG(dev);
608 uint16_t value;
609 int ret;
610
611 ret = i2c_write_read(dev_cfg->i2c.bus, dev_cfg->i2c.addr, ®, sizeof(reg), &value,
612 sizeof(value));
613 if (ret == 0) {
614 *val = (value >> 8) & 0xff;
615 *val += ((value & 0xff) << 8);
616 /* update cache*/
617 LOG_DBG("REG:%02u VAL:%#02x", reg, *val);
618 }
619 }
620
wm8904_update_reg(const struct device * dev,uint8_t reg,uint16_t mask,uint16_t val)621 static void wm8904_update_reg(const struct device *dev, uint8_t reg, uint16_t mask, uint16_t val)
622 {
623 uint16_t reg_val = 0;
624 uint16_t new_value = 0;
625
626 if (reg == 0x19) {
627 LOG_DBG("try write mask %#x val %#x", mask, val);
628 }
629 wm8904_read_reg(dev, reg, ®_val);
630 if (reg == 0x19) {
631 LOG_DBG("read %#x = %x", reg, reg_val);
632 }
633 new_value = (reg_val & ~mask) | (val & mask);
634 if (reg == 0x19) {
635 LOG_DBG("write %#x = %x", reg, new_value);
636 }
637 wm8904_write_reg(dev, reg, new_value);
638 }
639
wm8904_soft_reset(const struct device * dev)640 static void wm8904_soft_reset(const struct device *dev)
641 {
642 wm8904_write_reg(dev, WM8904_REG_RESET, 0x00);
643 }
644
wm8904_configure_output(const struct device * dev)645 static void wm8904_configure_output(const struct device *dev)
646 {
647 wm8904_out_volume_config(dev, AUDIO_CHANNEL_ALL, WM8904_OUTPUT_VOLUME_DEFAULT);
648 wm8904_out_mute_config(dev, AUDIO_CHANNEL_ALL, false);
649
650 wm8904_apply_properties(dev);
651 }
652
wm8904_configure_input(const struct device * dev)653 static void wm8904_configure_input(const struct device *dev)
654 {
655 wm8904_route_input(dev, AUDIO_CHANNEL_FRONT_LEFT, 2);
656 wm8904_route_input(dev, AUDIO_CHANNEL_FRONT_RIGHT, 2);
657
658 wm8904_in_volume_config(dev, AUDIO_CHANNEL_ALL, WM8904_INPUT_VOLUME_DEFAULT);
659 wm8904_in_mute_config(dev, AUDIO_CHANNEL_ALL, false);
660 }
661
662 static const struct audio_codec_api wm8904_driver_api = {
663 .configure = wm8904_configure,
664 .start_output = wm8904_start_output,
665 .stop_output = wm8904_stop_output,
666 .set_property = wm8904_set_property,
667 .apply_properties = wm8904_apply_properties,
668 .route_input = wm8904_route_input
669 };
670
671 #define WM8904_INIT(n) \
672 static const struct wm8904_driver_config wm8904_device_config_##n = { \
673 .i2c = I2C_DT_SPEC_INST_GET(n), \
674 .clock_source = DT_INST_PROP_OR(n, clk_source, 0), \
675 .mclk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(n, mclk)), \
676 .mclk_name = (clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(n, mclk, name)}; \
677 \
678 DEVICE_DT_INST_DEFINE(n, NULL, NULL, NULL, &wm8904_device_config_##n, \
679 POST_KERNEL, CONFIG_AUDIO_CODEC_INIT_PRIORITY, &wm8904_driver_api);
680
681 DT_INST_FOREACH_STATUS_OKAY(WM8904_INIT)
682