1 /*
2 * Copyright (c) 2024 HPMicro
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7 #include "hpm_wm8978.h"
8
9 #define WM8978_I2C_SLAVE_ADDRESS1 (0x1A)
10 #define WM8978_I2C_SLAVE_ADDRESS2 (0x1A)
11
12 /* store reg value */
13 static volatile uint16_t wm8978_reg_val[] = {
14 0x000, 0x000, 0x000, 0x000, 0x050, 0x000, 0x140, 0x000,
15 0x000, 0x000, 0x000, 0x0FF, 0x0FF, 0x000, 0x100, 0x0FF,
16 0x0FF, 0x000, 0x12C, 0x02C, 0x02C, 0x02C, 0x02C, 0x000,
17 0x032, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
18 0x038, 0x00B, 0x032, 0x000, 0x008, 0x00C, 0x093, 0x0E9,
19 0x000, 0x000, 0x000, 0x000, 0x003, 0x010, 0x010, 0x100,
20 0x100, 0x002, 0x001, 0x001, 0x039, 0x039, 0x039, 0x039,
21 };
22 ;
23
wm8979_init(wm8978_context_t * control)24 hpm_stat_t wm8979_init(wm8978_context_t *control)
25 {
26 hpm_stat_t stat;
27 uint8_t i;
28 for (i = 0; i < 0x7F; i++) {
29 if (i2c_master_write(control->ptr, i, NULL, 0) == status_success) {
30 if ((i == WM8978_I2C_SLAVE_ADDRESS1) || (i == WM8978_I2C_SLAVE_ADDRESS2)) {
31 control->device_address = i;
32 break;
33 }
34 }
35 }
36 if (i == 0x7F) {
37 return status_fail;
38 }
39 stat = wm8978_reset(control);
40 return stat;
41 }
42
wm8978_reset(wm8978_context_t * control)43 hpm_stat_t wm8978_reset(wm8978_context_t *control)
44 {
45 hpm_stat_t stat = status_success;
46 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_RESET, 0));
47 return stat;
48 }
49
wm8978_set_out_volume(wm8978_context_t * control,wm8978_out_channel_t channel,uint8_t volume)50 hpm_stat_t wm8978_set_out_volume(wm8978_context_t *control, wm8978_out_channel_t channel, uint8_t volume)
51 {
52 hpm_stat_t stat = status_success;
53 uint8_t l_out_reg;
54 uint8_t r_out_reg;
55 if (volume > WM8978_OUT_VOLUME_MASK) {
56 volume = WM8978_OUT_VOLUME_MASK;
57 }
58 if (channel == wm8978_out1_channel) {
59 l_out_reg = WM8978_LOUT1_VOLUME_CTRL;
60 r_out_reg = WM8978_ROUT1_VOLUME_CTRL;
61 } else if (channel == wm8978_out2_channel) {
62 l_out_reg = WM8978_LOUT2_VOLUME_CTRL;
63 r_out_reg = WM8978_ROUT2_VOLUME_CTRL;
64 } else {
65 return status_invalid_argument;
66 }
67 HPM_CHECK_RET(wm8978_write_reg(control, l_out_reg, WM8978_OUT_VOLUME_SET(volume) | WM8978_OUT_SPKVU_SET(0)));
68 /* LOUT1/2 and ROUT1/2 volumes do not update untila 1 is written to SPKkVU */
69 HPM_CHECK_RET(wm8978_write_reg(control, r_out_reg, WM8978_OUT_VOLUME_SET(volume) | WM8978_OUT_SPKVU_SET(1)));
70 return stat;
71 }
72
wm8978_get_out_volume(wm8978_context_t * control,wm8978_out_channel_t channel,uint8_t * volume)73 hpm_stat_t wm8978_get_out_volume(wm8978_context_t *control, wm8978_out_channel_t channel, uint8_t *volume)
74 {
75 hpm_stat_t stat = status_success;
76 uint8_t out_reg;
77 uint16_t val;
78 if (channel == wm8978_out1_channel) {
79 out_reg = WM8978_LOUT1_VOLUME_CTRL;
80 } else if (channel == wm8978_out2_channel) {
81 out_reg = WM8978_LOUT2_VOLUME_CTRL;
82 } else {
83 return status_invalid_argument;
84 }
85 HPM_CHECK_RET(wm8978_read_reg(control, out_reg, &val));
86 *volume = WM8978_OUT_VOLUME_GET(val);
87 return stat;
88 }
89
wm8978_set_out_mute(wm8978_context_t * control,wm8978_out_channel_t channel,bool mute)90 hpm_stat_t wm8978_set_out_mute(wm8978_context_t *control, wm8978_out_channel_t channel, bool mute)
91 {
92 hpm_stat_t stat = status_success;
93 uint16_t val;
94 uint8_t l_out_reg;
95 uint8_t r_out_reg;
96 if (channel == wm8978_out1_channel) {
97 l_out_reg = WM8978_LOUT1_VOLUME_CTRL;
98 r_out_reg = WM8978_ROUT1_VOLUME_CTRL;
99 } else if (channel == wm8978_out2_channel) {
100 l_out_reg = WM8978_LOUT2_VOLUME_CTRL;
101 r_out_reg = WM8978_ROUT2_VOLUME_CTRL;
102 } else {
103 return status_invalid_argument;
104 }
105 if (mute == true) {
106 HPM_CHECK_RET(wm8978_read_reg(control, l_out_reg, &val));
107 val |= WM8978_OUT_MUTE_MASK;
108 HPM_CHECK_RET(wm8978_write_reg(control, l_out_reg, val));
109 HPM_CHECK_RET(wm8978_read_reg(control, r_out_reg, &val));
110 val |= WM8978_OUT_MUTE_MASK;
111 HPM_CHECK_RET(wm8978_write_reg(control, r_out_reg, val));
112 } else {
113 HPM_CHECK_RET(wm8978_read_reg(control, l_out_reg, &val));
114 val &= ~WM8978_OUT_MUTE_MASK;
115 HPM_CHECK_RET(wm8978_write_reg(control, l_out_reg, val));
116 HPM_CHECK_RET(wm8978_read_reg(control, r_out_reg, &val));
117 val &= ~WM8978_OUT_MUTE_MASK;
118 HPM_CHECK_RET(wm8978_write_reg(control, r_out_reg, val));
119 }
120 return stat;
121 }
122
wm8978_set_mic_gain(wm8978_context_t * control,uint8_t gain)123 hpm_stat_t wm8978_set_mic_gain(wm8978_context_t *control, uint8_t gain)
124 {
125 hpm_stat_t stat = status_success;
126 if (gain > WM8978_INPPGA_VOL_MASK) {
127 gain = WM8978_INPPGA_VOL_MASK;
128 }
129 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_LINP_PGA_GAIM_CTRL, WM8978_INPPGA_VOL_SET(gain)));
130 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_LINP_PGA_GAIM_CTRL, WM8978_INPPGA_VOL_SET(gain) | WM8978_INPGA_UPDATE_SET(1)));
131 return stat;
132 }
133
wm8978_set_line_gain(wm8978_context_t * control,uint8_t gain)134 hpm_stat_t wm8978_set_line_gain(wm8978_context_t *control, uint8_t gain)
135 {
136 uint16_t val;
137 hpm_stat_t stat = status_success;
138 if (gain > WM8978_AUXL2BOOSTVOL_MASK) {
139 gain = WM8978_AUXL2BOOSTVOL_MASK;
140 }
141 HPM_CHECK_RET(wm8978_read_reg(control, WM8978_LADC_BOOST_CTRL, &val));
142 val &= (~WM8978_2_2_BOOSTVOL_MASK);
143 val |= WM8978_2_2_BOOSTVOL_SET(gain);
144 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_LADC_BOOST_CTRL, val));
145
146 HPM_CHECK_RET(wm8978_read_reg(control, WM8978_RADC_BOOST_CTRL, &val));
147 val &= ~WM8978_2_2_BOOSTVOL_MASK;
148 val |= WM8978_2_2_BOOSTVOL_SET(gain);
149 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_RADC_BOOST_CTRL, val));
150 return stat;
151 }
152
wm8978_power_down(wm8978_context_t * control)153 hpm_stat_t wm8978_power_down(wm8978_context_t *control)
154 {
155 return wm8978_reset(control);
156 }
157
wm8978_cfg_audio_interface(wm8978_context_t * control,wm8978_audio_interface_t standard,wm8978_word_length_t word_len)158 hpm_stat_t wm8978_cfg_audio_interface(wm8978_context_t *control, wm8978_audio_interface_t standard, wm8978_word_length_t word_len)
159 {
160 hpm_stat_t stat = status_success;
161 uint16_t usReg = 0;
162 usReg |= WM8978_FMT_SET(standard) | WM8978_WL_SET(word_len);
163 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_AUDIO_INTERFACE, usReg));
164 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_CLOCK_GEN_CTRL, 0x000));
165 return stat;
166 }
167
wm8978_cfg_audio_channel(wm8978_context_t * control,input_channel_flags_t in_flags,output_channel_flag_t out_flags)168 hpm_stat_t wm8978_cfg_audio_channel(wm8978_context_t *control, input_channel_flags_t in_flags, output_channel_flag_t out_flags)
169 {
170 uint16_t reg_val = 0;
171 hpm_stat_t stat = status_success;
172 if ((in_flags == input_off) && (out_flags == output_off)) {
173 wm8978_power_down(control);
174 return stat;
175 }
176
177 reg_val = WM8978_BIASEN_R1_MASK | WM8978_VMIDSEL_R1_SET(3);
178 if (out_flags & out_3_4_on) {
179 reg_val |= (WM8978_OUT4MIXEN_R1_MASK | WM8978_OUT3MIXEN_R1_MASK);
180 }
181 if ((in_flags & mic_left_on) || (in_flags & mic_right_on)) {
182 reg_val |= WM8978_MICBEN_R1_MASK;
183 }
184 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_POWER_MANAGET_1, reg_val));
185
186 reg_val = 0;
187 if (out_flags & earphone_left_on) {
188 reg_val |= WM8978_LOUT1EN_R2_MASK;
189 }
190 if (out_flags & earphone_right_on) {
191 reg_val |= WM8978_ROUT1EN_R2_MASK;
192 }
193 if (in_flags & mic_left_on) {
194 reg_val |= (WM8978_BOOSTENL_R2_MASK | WM8978_INPPGAENL_R2_MASK);
195 }
196 if (in_flags & mic_right_on) {
197 reg_val |= (WM8978_BOOSTENR_R2_MASK | WM8978_INPPGAENR_R2_MASK);
198 }
199 if (in_flags & line_on) {
200 reg_val |= (WM8978_BOOSTENL_R2_MASK | WM8978_BOOSTENR_R2_MASK);
201 }
202 if (in_flags & adc_on) {
203 reg_val |= (WM8978_ADCENR_R2_MASK | WM8978_ADCENL_R2_MASK);
204 }
205 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_POWER_MANAGET_2, reg_val));
206
207 reg_val = 0;
208 if (out_flags & out_3_4_on) {
209 reg_val |= (WM8978_OUT4EN_R3_MASK | WM8978_OUT3EN_R3_MASK);
210 }
211 if (out_flags & spk_on) {
212 reg_val |= (WM8978_LOUT2EN_R3_MASK | WM8978_ROUT2EN_R3_MASK);
213 }
214 if (out_flags != output_off) {
215 reg_val |= (WM8978_RMIXEN_R3_MASK | WM8978_LMIXEN_R3_MASK);
216 }
217 if (in_flags & dac_on) {
218 reg_val |= (WM8978_DACENR_R3_MASK | WM8978_DACENL_R3_MASK);
219 }
220 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_POWER_MANAGET_3, reg_val));
221
222 reg_val = 0 << 8;
223 if (in_flags & line_on) {
224 reg_val |= (WM8978_R2_2INPPGA_R44_MASK | WM8978_L2_2INPPGA_R44_MASK);
225 }
226 if (in_flags & mic_right_on) {
227 reg_val |= (WM8978_RIN2INPPGA_R44_MASK | WM8978_RIP2INPPGA_R44_MASK);
228 }
229 if (in_flags & mic_left_on) {
230 reg_val |= (WM8978_LIN2INPPGA_R44_MASK | WM8978_LIP2INPPGA_R44_MASK);
231 }
232 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_INPUT_CTRL, reg_val));
233 reg_val = 0;
234 if (in_flags & adc_on) {
235 reg_val |= (WM8978_ADCOSR128_R14_MASK) | (0 << 8) | (4 << 0);
236 } else {
237 reg_val = 0;
238 }
239 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_ADC_CONTROL, reg_val));
240
241 if (in_flags & adc_on) {
242 reg_val = (0 << 7);
243 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_NOTCH_FILTER1, reg_val));
244 reg_val = 0;
245 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_NOTCH_FILTER2, reg_val));
246 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_NOTCH_FILTER3, reg_val));
247 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_NOTCH_FILTER4, reg_val));
248 }
249 reg_val = 0;
250 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_ALC_CONTROL1, reg_val));
251 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_ALC_CONTROL2, reg_val));
252 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_ALC_CONTROL3, reg_val));
253
254 /* Disable automatic gain control */
255 reg_val = (3 << 1) | (7 << 0);
256 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_NOISE_GATE, reg_val));
257
258 reg_val = 0;
259 if ((in_flags & mic_left_on) || (in_flags & mic_right_on)) {
260 reg_val |= (1 << 8); /* MIC gain = +20dB */
261 }
262 if (in_flags & aux_on) {
263 reg_val |= (3 << 0); /* Aux = 3*/
264 }
265 if (in_flags & line_on) {
266 reg_val |= (3 << 4); /* Line gain = 3 */
267 }
268 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_LADC_BOOST_CTRL, reg_val));
269 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_RADC_BOOST_CTRL, reg_val));
270
271 reg_val = 0xFF;
272 /* Select 0dB to cache the left channel first */
273 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_LEFT_ADC_VOL, reg_val));
274 reg_val = 0x1FF;
275 /* Update left and right channels simultaneously */
276 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_RIGHT_ADC_VOL, reg_val));
277
278 reg_val = 0;
279 if (out_flags & spk_on) {
280 /* ROUT2 is inverted and used to drive the speaker */
281 reg_val |= (1 << 4);
282 }
283 if (in_flags & aux_on) {
284 reg_val |= ((7 << 1) | (1 << 0));
285 }
286 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_BEEP_CONTROL, reg_val));
287
288 reg_val = 0;
289 if (in_flags & dac_on) {
290 reg_val |= ((1 << 6) | (1 << 5));
291 }
292 if (out_flags & spk_on) {
293 /* SPK 1.5x gain, thermal protection enabled */
294 reg_val |= ((1 << 2) | (1 << 1));
295 }
296 if (out_flags & out_3_4_on) {
297 /* BOOT3 BOOT4 1.5x gain */
298 reg_val |= ((1 << 4) | (1 << 3));
299 }
300 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_OUTPUT_CTRL, reg_val));
301
302 reg_val = 0;
303 if (in_flags & aux_on) {
304 reg_val |= ((7 << 6) | (1 << 5));
305 }
306 if ((in_flags & line_on) || (in_flags & mic_left_on) || (in_flags & mic_right_on)) {
307 reg_val |= ((7 << 2) | (1 << 1));
308 }
309 if (in_flags & dac_on) {
310 reg_val |= (1 << 0);
311 }
312 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_LEFT_MIXER_CTRL, reg_val));
313 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_RIGHT_MIXER_CTRL, reg_val));
314
315 reg_val = 0;
316 if (out_flags & out_3_4_on) {
317 reg_val |= (1 << 3);
318 }
319 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_OUT3_MIXER_CTRL, reg_val));
320
321 reg_val = 0;
322 if (out_flags & out_3_4_on) {
323 reg_val |= ((1 << 4) | (1 << 1));
324 }
325 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_OUT4_MIXER_CTRL, reg_val));
326
327 if (in_flags & dac_on) {
328 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_LEFT_DAC_VOL, 0xFF));
329 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_RIGHT_DAC_VOL, 0x1FF));
330 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_DAC_CTRL, 0));
331 } else {
332 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_LEFT_DAC_VOL, 0));
333 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_RIGHT_DAC_VOL, 0x100));
334 }
335 return stat;
336 }
337
wm8978_notch_filter(wm8978_context_t * control,uint16_t nfa0,uint16_t nfa1)338 hpm_stat_t wm8978_notch_filter(wm8978_context_t *control, uint16_t nfa0, uint16_t nfa1)
339 {
340 hpm_stat_t stat = status_success;
341 uint16_t reg_val;
342 reg_val = (1 << 7) | (nfa0 & 0x3F);
343 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_NOTCH_FILTER1, reg_val));
344
345 reg_val = ((nfa0 >> 7) & 0x3F);
346 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_NOTCH_FILTER2, reg_val));
347
348 reg_val = (nfa1 & 0x3F);
349 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_NOTCH_FILTER3, reg_val));
350
351 reg_val = (1 << 8) | ((nfa1 >> 7) & 0x3F);
352 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_NOTCH_FILTER4, reg_val));
353 return stat;
354 }
355
wm8978_ctrl_gpio1(wm8978_context_t * control,bool value)356 hpm_stat_t wm8978_ctrl_gpio1(wm8978_context_t *control, bool value)
357 {
358 hpm_stat_t stat = status_success;
359 uint16_t reg_val;
360 if (value == false) {
361 reg_val = 6; /* B2:0 = 110 */
362 } else {
363 reg_val = 7; /* B2:0 = 111 */
364 }
365 HPM_CHECK_RET(wm8978_write_reg(control, WM8978_GPIO_CTRL, reg_val));
366 return stat;
367 }
368
wm8978_write_reg(wm8978_context_t * control,uint8_t reg,uint16_t val)369 hpm_stat_t wm8978_write_reg(wm8978_context_t *control, uint8_t reg, uint16_t val)
370 {
371 uint8_t buff[2];
372 /* The first 7 bits (B15 to B9) are address bits that select which control register */
373 /* is accessed. The remaining 9 bits (B8 to B0) are data bits */
374 buff[0] = (reg << 1) | (uint8_t)((val >> 8U) & 0x0001U);
375 buff[1] = (uint8_t)(val & 0xFFU);
376
377 /* record reg val */
378 wm8978_reg_val[reg] = val;
379 return i2c_master_write(control->ptr, control->device_address, buff, 2U);
380 }
381
wm8978_read_reg(wm8978_context_t * control,uint8_t reg,uint16_t * val)382 hpm_stat_t wm8978_read_reg(wm8978_context_t *control, uint8_t reg, uint16_t *val)
383 {
384 (void)control;
385 *val = wm8978_reg_val[reg];
386 return status_success;
387 }
388
wm8978_modify_reg(wm8978_context_t * control,uint8_t reg,uint16_t mask,uint16_t val)389 hpm_stat_t wm8978_modify_reg(wm8978_context_t *control, uint8_t reg, uint16_t mask, uint16_t val)
390 {
391 hpm_stat_t stat = status_success;
392 uint16_t reg_val;
393 /* Read the register value out */
394 HPM_CHECK_RET(wm8978_read_reg(control, reg, ®_val));
395 /* Modify the value */
396 reg_val &= (uint16_t)~mask;
397 reg_val |= val;
398 /* Write the data to register */
399 HPM_CHECK_RET(wm8978_write_reg(control, reg, reg_val));
400 if (stat != status_success) {
401 return status_fail;
402 }
403 return stat;
404 }
405
406