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, &reg, 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, &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