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