• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016-2021 NXP
4  * Copyright (c) 2022 HPMicro
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  *
8  */
9 
10 #include "hpm_wm8960.h"
11 
12 #ifndef HPM_WM8960_MCLK_TOLERANCE
13 #define HPM_WM8960_MCLK_TOLERANCE (4U)
14 #endif
15 
16 /* wm8960 register default value */
17 static const uint16_t wm8960_default_reg_val[WM8960_REG_NUM] = {
18     0x0097, 0x0097, 0x0000, 0x0000, 0x0000, 0x0008, 0x0000, 0x000a, 0x01c0, 0x0000, 0x00ff, 0x00ff, 0x0000, 0x0000,
19     0x0000, 0x0000, 0x0000, 0x007b, 0x0100, 0x0032, 0x0000, 0x00c3, 0x00c3, 0x01c0, 0x0000, 0x0000, 0x0000, 0x0000,
20     0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0100, 0x0050, 0x0050, 0x0050, 0x0050, 0x0000, 0x0000, 0x0000, 0x0000,
21     0x0040, 0x0000, 0x0000, 0x0050, 0x0050, 0x0000, 0x0002, 0x0037, 0x004d, 0x0080, 0x0008, 0x0031, 0x0026, 0x00e9,
22 };
23 
24 /* store reg value */
25 static uint16_t wm8960_reg_val[WM8960_REG_NUM];
26 
wm8960_init(wm8960_control_t * control,wm8960_config_t * config)27 hpm_stat_t wm8960_init(wm8960_control_t *control, wm8960_config_t *config)
28 {
29     assert(control != NULL);
30     assert(config != NULL);
31 
32     hpm_stat_t stat = status_success;
33 
34     (void)memcpy(wm8960_reg_val, wm8960_default_reg_val, sizeof(wm8960_default_reg_val));
35 
36     /* Reset */
37     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RESET, 0x00));
38 
39     /* Power on input modules */
40     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, 0xFE));
41     /* Power on output modules */
42     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER2, 0x1F8));
43     /* Power on PGA and mixer */
44     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER3, 0x3C));
45 
46     /* ADC and DAC uses same clock */
47     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_IFACE2, 0x40));
48 
49     /* set data protocol */
50     HPM_CHECK_RET(wm8960_set_protocol(control, config->bus));
51 
52     /* set wm8960 as slave */
53     HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_IFACE1, WM8960_IFACE1_MS_MASK, WM8960_IFACE1_MS_SET(0)));
54 
55     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_ADDCTL1, 0xC0));
56     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_ADDCTL4, 0x40));
57 
58     /* ADC volume, 8dB */
59     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LADC, 0x1D3));
60     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RADC, 0x1D3));
61 
62     /* Digital DAC volume, 0dB */
63     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LDAC, 0x1E0));
64     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RDAC, 0x1E0));
65 
66     /* Headphone volume, LOUT1 and ROUT1, 6dB */
67     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LOUT1, 0x17F));
68     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_ROUT1, 0x17F));
69 
70     /* speaker volume 6dB */
71     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LOUT2, 0x1ff));
72     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_ROUT2, 0x1ff));
73     /* enable class D output */
74     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_CLASSD1, 0xf7));
75 
76     /* Unmute DAC. */
77     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_DACCTL1, 0x0000));
78     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LINVOL, 0x117));
79     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RINVOL, 0x117));
80 
81     HPM_CHECK_RET(wm8960_set_data_format(control, config->format.mclk_hz, config->format.sample_rate, config->format.bit_width));
82 
83     /* set data route */
84     HPM_CHECK_RET(wm8960_set_data_route(control, config));
85 
86     return status_success;
87 }
88 
wm8960_deinit(wm8960_control_t * control)89 hpm_stat_t wm8960_deinit(wm8960_control_t *control)
90 {
91     hpm_stat_t stat = status_success;
92 
93     /* power off all modules */
94     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, 0x00U));
95     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER2, 0x00U));
96     HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER3, 0x00U));
97 
98     return status_success;
99 }
100 
wm8960_set_protocol(wm8960_control_t * control,wm8960_protocol_t protocol)101 hpm_stat_t wm8960_set_protocol(wm8960_control_t *control, wm8960_protocol_t protocol)
102 {
103     return wm8960_modify_reg(control, WM8960_IFACE1, WM8960_IFACE1_FORMAT_MASK, (uint16_t)protocol);
104 }
105 
wm8960_set_module(wm8960_control_t * control,wm8960_module_t module,bool enable)106 hpm_stat_t wm8960_set_module(wm8960_control_t *control, wm8960_module_t module, bool enable)
107 {
108     hpm_stat_t stat = status_success;
109     switch (module) {
110     case wm8960_module_adc:
111         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER1, WM8960_POWER1_ADCL_MASK,
112                                             ((uint16_t)enable << WM8960_POWER1_ADCL_SHIFT)));
113         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER1, WM8960_POWER1_ADCR_MASK,
114                                             ((uint16_t)enable << WM8960_POWER1_ADCR_SHIFT)));
115         break;
116     case wm8960_module_dac:
117         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER2, WM8960_POWER2_DACL_MASK,
118                                             ((uint16_t)enable << WM8960_POWER2_DACL_SHIFT)));
119         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER2, WM8960_POWER2_DACR_MASK,
120                                             ((uint16_t)enable << WM8960_POWER2_DACR_SHIFT)));
121         break;
122     case wm8960_module_vref:
123         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER1, WM8960_POWER1_VREF_MASK,
124                                             ((uint16_t)enable << WM8960_POWER1_VREF_SHIFT)));
125         break;
126     case wm8960_module_ana_in:
127         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER1, WM8960_POWER1_AINL_MASK,
128                                             ((uint16_t)enable << WM8960_POWER1_AINL_SHIFT)));
129         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER1, WM8960_POWER1_AINR_MASK,
130                                             ((uint16_t)enable << WM8960_POWER1_AINR_SHIFT)));
131         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER3, WM8960_POWER3_LMIC_MASK,
132                                             ((uint16_t)enable << WM8960_POWER3_LMIC_SHIFT)));
133         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER3, WM8960_POWER3_RMIC_MASK,
134                                             ((uint16_t)enable << WM8960_POWER3_RMIC_SHIFT)));
135         break;
136     case wm8960_module_lineout:
137         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER2, WM8960_POWER2_LOUT1_MASK,
138                                             ((uint16_t)enable << WM8960_POWER2_LOUT1_SHIFT)));
139         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER2, WM8960_POWER2_ROUT1_MASK,
140                                             ((uint16_t)enable << WM8960_POWER2_ROUT1_SHIFT)));
141         break;
142     case wm8960_module_micbais:
143         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER1, WM8960_POWER1_MICB_MASK,
144                                             ((uint16_t)enable << WM8960_POWER1_MICB_SHIFT)));
145         break;
146     case wm8960_module_speaker:
147         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER2, WM8960_POWER2_SPKL_MASK,
148                                             ((uint16_t)enable << WM8960_POWER2_SPKL_SHIFT)));
149         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER2, WM8960_POWER2_SPKR_MASK,
150                                             ((uint16_t)enable << WM8960_POWER2_SPKR_SHIFT)));
151         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_CLASSD1, 0xF7));
152         break;
153     case wm8960_module_output_mixer:
154         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER3, WM8960_POWER3_LOMIX_MASK,
155                                             ((uint16_t)enable << WM8960_POWER3_LOMIX_SHIFT)));
156         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_POWER3, WM8960_POWER3_ROMIX_MASK,
157                                             ((uint16_t)enable << WM8960_POWER3_ROMIX_SHIFT)));
158         break;
159     default:
160         stat = status_invalid_argument;
161         break;
162     }
163     return stat;
164 }
165 
wm8960_set_data_route(wm8960_control_t * control,wm8960_config_t * config)166 hpm_stat_t wm8960_set_data_route(wm8960_control_t *control, wm8960_config_t *config)
167 {
168     hpm_stat_t stat = status_success;
169 
170     /* select left input */
171     HPM_CHECK_RET(wm8960_set_left_input(control, config->left_input));
172     /* select right input */
173     HPM_CHECK_RET(wm8960_set_right_input(control, config->right_input));
174     /* select source to output mixer */
175     HPM_CHECK_RET(wm8960_config_input_to_output_mixer(control, config->play_source));
176 
177     switch (config->route) {
178     case wm8960_route_bypass:
179         HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_micbais, true));
180         HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_ana_in, true));
181         HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_output_mixer, true));
182         HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_lineout, true));
183         break;
184     case wm8960_route_playback:
185         /* I2S_IN-> DAC-> HP */
186         /* Set power for DAC */
187         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER3, 0x0C));
188         HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_dac, true));
189         HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_output_mixer, true));
190         HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_lineout, true));
191         break;
192     case wm8960_route_playback_and_record:
193         /* Set power */
194         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER3, 0x3C));
195         HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_adc, true));
196         HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_micbais, true));
197         HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_ana_in, true));
198         HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_dac, true));
199         HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_output_mixer, true));
200         HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_lineout, true));
201         break;
202     case wm8960_route_record:
203         /* ANA_IN->ADC->I2S_OUT */
204         /* Power up ADC and AIN */
205         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER3, 0x30));
206         HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_micbais, true));
207         HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_ana_in, true));
208         HPM_CHECK_RET(wm8960_set_module(control, wm8960_module_adc, true));
209         break;
210     default:
211         stat = status_invalid_argument;
212         break;
213     }
214     return stat;
215 }
216 
wm8960_set_left_input(wm8960_control_t * control,wm8960_input_t input)217 hpm_stat_t wm8960_set_left_input(wm8960_control_t *control, wm8960_input_t input)
218 {
219     hpm_stat_t stat = status_success;
220     uint16_t val = 0;
221 
222     switch (input) {
223     case wm8960_input_closed:
224         HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
225         val &= (uint16_t) ~(WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK);
226         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
227         break;
228     case wm8960_input_single_ended_mic:
229         /* Only LMN1 enabled, LMICBOOST to 13db, LMIC2B enabled */
230         HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
231         val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK | WM8960_POWER1_MICB_MASK);
232         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
233         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LINPATH, 0x138));
234         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LINVOL, 0x117));
235         break;
236     case wm8960_input_differential_mic_input2:
237         HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
238         val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK | WM8960_POWER1_MICB_MASK);
239         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
240         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LINPATH, 0x178));
241         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LINVOL, 0x117));
242         break;
243     case wm8960_input_differential_mic_input3:
244         HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
245         val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK | WM8960_POWER1_MICB_MASK);
246         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
247         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LINPATH, 0x1B8));
248         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LINVOL, 0x117));
249         break;
250     case wm8960_input_line_input2:
251         HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
252         val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK);
253         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
254         HPM_CHECK_RET(wm8960_read_reg(WM8960_INBMIX1, &val));
255         val |= 0xEU;
256         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_INBMIX1, val));
257         break;
258     case wm8960_input_line_input3:
259         HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
260         val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK);
261         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
262         HPM_CHECK_RET(wm8960_read_reg(WM8960_INBMIX1, &val));
263         val |= 0x70U;
264         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_INBMIX1, val));
265         break;
266     default:
267         stat = status_invalid_argument;
268         break;
269     }
270 
271     return stat;
272 }
273 
wm8960_set_right_input(wm8960_control_t * control,wm8960_input_t input)274 hpm_stat_t wm8960_set_right_input(wm8960_control_t *control, wm8960_input_t input)
275 {
276     hpm_stat_t stat = status_success;
277     uint16_t val = 0;
278 
279     switch (input) {
280     case wm8960_input_closed:
281         HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
282         val &= (uint16_t) ~(WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK);
283         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
284         break;
285     case wm8960_input_single_ended_mic:
286         /* Only LMN1 enabled, LMICBOOST to 13db, LMIC2B enabled */
287         HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
288         val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK | WM8960_POWER1_MICB_MASK);
289         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
290         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RINPATH, 0x138));
291         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RINVOL, 0x117));
292         break;
293     case wm8960_input_differential_mic_input2:
294         HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
295         val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK | WM8960_POWER1_MICB_MASK);
296         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
297         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RINPATH, 0x178));
298         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RINVOL, 0x117));
299         break;
300     case wm8960_input_differential_mic_input3:
301         HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
302         val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK | WM8960_POWER1_MICB_MASK);
303         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
304         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RINPATH, 0x1B8));
305         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RINVOL, 0x117));
306         break;
307     case wm8960_input_line_input2:
308         HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
309         val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK);
310         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
311         HPM_CHECK_RET(wm8960_read_reg(WM8960_INBMIX2, &val));
312         val |= 0xEU;
313         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_INBMIX2, val));
314         break;
315     case wm8960_input_line_input3:
316         HPM_CHECK_RET(wm8960_read_reg(WM8960_POWER1, &val));
317         val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK);
318         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_POWER1, val));
319         HPM_CHECK_RET(wm8960_read_reg(WM8960_INBMIX2, &val));
320         val |= 0x70U;
321         HPM_CHECK_RET(wm8960_write_reg(control, WM8960_INBMIX2, val));
322         break;
323     default:
324         stat = status_invalid_argument;
325         break;
326     }
327 
328     return stat;
329 }
330 
wm8960_set_volume(wm8960_control_t * control,wm8960_module_t module,uint32_t volume)331 hpm_stat_t wm8960_set_volume(wm8960_control_t *control, wm8960_module_t module, uint32_t volume)
332 {
333     uint16_t vol = 0;
334     hpm_stat_t stat = status_success;
335     switch (module) {
336     case wm8960_module_adc:
337         if (volume > 255U) {
338             stat = status_invalid_argument;
339         } else {
340             vol = (uint16_t)volume;
341             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LADC, vol));
342             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RADC, vol));
343             /* Update volume */
344             vol = (uint16_t)(0x100U | volume);
345             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LADC, vol));
346             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RADC, vol));
347         }
348         break;
349     case wm8960_module_dac:
350         if (volume > 255U) {
351             stat = status_invalid_argument;
352         } else {
353             vol = (uint16_t)volume;
354             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LDAC, vol));
355             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RDAC, vol));
356             vol = 0x100U | (uint16_t)volume;
357             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LDAC, vol));
358             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RDAC, vol));
359         }
360         break;
361     case wm8960_module_headphone:
362         if (volume > 0x7FU) {
363             stat = status_invalid_argument;
364         } else {
365             vol = (uint16_t)volume;
366             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LOUT1, vol));
367             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_ROUT1, vol));
368             vol = 0x100U | (uint16_t)volume;
369             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LOUT1, vol));
370             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_ROUT1, vol));
371         }
372         break;
373     case wm8960_module_ana_in:
374         if (volume > 0x3FU) {
375             stat = status_invalid_argument;
376         } else {
377             vol = (uint16_t)volume;
378             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LINVOL, vol));
379             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RINVOL, vol));
380             vol = 0x100U | (uint16_t)volume;
381             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LINVOL, vol));
382             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_RINVOL, vol));
383         }
384         break;
385     case wm8960_module_speaker:
386         if (volume > 0x7FU) {
387             stat = status_invalid_argument;
388         } else {
389             vol = (uint16_t)volume;
390             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LOUT2, vol));
391             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_ROUT2, vol));
392             vol = 0x100U | (uint16_t)volume;
393             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_LOUT2, vol));
394             HPM_CHECK_RET(wm8960_write_reg(control, WM8960_ROUT2, vol));
395         }
396         break;
397     default:
398         stat = status_invalid_argument;
399         break;
400     }
401     return stat;
402 }
403 
wm8960_check_clock_tolerance(uint32_t source,uint32_t target)404 static bool wm8960_check_clock_tolerance(uint32_t source, uint32_t target)
405 {
406     uint32_t delta = (source >= target) ? (source - target) : (target - source);
407     if (delta * 100 <= HPM_WM8960_MCLK_TOLERANCE * target) {
408         return true;
409     }
410     return false;
411 }
412 
wm8960_set_data_format(wm8960_control_t * control,uint32_t sysclk,uint32_t sample_rate,uint32_t bits)413 hpm_stat_t wm8960_set_data_format(wm8960_control_t *control, uint32_t sysclk, uint32_t sample_rate, uint32_t bits)
414 {
415     hpm_stat_t stat = status_success;
416     uint16_t val = 0;
417     uint32_t ratio[7] = {256, 256 * 1.5, 256 * 2, 256 * 3, 256 * 4, 256 * 5.5, 256 * 6};
418     bool clock_meet_requirement = false;
419 
420     if (sysclk / sample_rate > 256 * 6) {
421         sysclk = sysclk / 2;
422         val = WM8960_CLOCK1_SYSCLKDIV_SET(2U); /* SYSCLK Pre-divider */
423     }
424 
425     for (uint8_t i = 0; i < 7; i++) {
426         if (wm8960_check_clock_tolerance(sysclk, sample_rate * ratio[i])) {
427             val |= ((i << WM8960_CLOCK1_ADCDIV_SHIFT) | (i << WM8960_CLOCK1_DACDIV_SHIFT));
428             clock_meet_requirement = true;
429             break;
430         }
431     }
432 
433     if (!clock_meet_requirement) {
434         return status_invalid_argument;
435     }
436     HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_CLOCK1, 0x1FEU, val));
437 
438     /* set sample bit */
439     switch (bits) {
440     case 16:
441         stat = wm8960_modify_reg(control, WM8960_IFACE1, WM8960_IFACE1_WL_MASK, WM8960_IFACE1_WL_SET(0U));
442         break;
443     case 20:
444         stat = wm8960_modify_reg(control, WM8960_IFACE1, WM8960_IFACE1_WL_MASK, WM8960_IFACE1_WL_SET(1U));
445         break;
446     case 24:
447         stat = wm8960_modify_reg(control, WM8960_IFACE1, WM8960_IFACE1_WL_MASK, WM8960_IFACE1_WL_SET(2U));
448         break;
449     case 32:
450         stat = wm8960_modify_reg(control, WM8960_IFACE1, WM8960_IFACE1_WL_MASK, WM8960_IFACE1_WL_SET(3U));
451         break;
452     default:
453         stat = status_invalid_argument;
454         break;
455     }
456 
457     return stat;
458 }
459 
wm8960_config_input_to_output_mixer(wm8960_control_t * control,uint32_t play_source)460 hpm_stat_t wm8960_config_input_to_output_mixer(wm8960_control_t *control, uint32_t play_source)
461 {
462     hpm_stat_t stat = status_success;
463 
464     if ((play_source & (uint32_t)wm8960_play_source_input_mixer) != 0U) {
465         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_BYPASS1, 0x80U, 0x80U));
466         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_BYPASS2, 0x80U, 0x80U));
467         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_LOUTMIX, 0x180U, 0U));
468         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_ROUTMIX, 0x180U, 0U));
469     }
470 
471     if ((play_source & (uint32_t)wm8960_play_source_dac) != 0U) {
472         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_BYPASS1, 0x80U, 0x00U));
473         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_BYPASS2, 0x80U, 0x00U));
474         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_LOUTMIX, 0x180U, 0x100U));
475         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_ROUTMIX, 0x180U, 0x100U));
476     }
477 
478     if ((play_source & (uint32_t)wm8960_play_source_input3) != 0U) {
479         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_BYPASS1, 0x80U, 0x0U));
480         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_BYPASS2, 0x80U, 0x0U));
481         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_LOUTMIX, 0x180U, 0x80U));
482         HPM_CHECK_RET(wm8960_modify_reg(control, WM8960_ROUTMIX, 0x180U, 0x80U));
483     }
484 
485     return stat;
486 }
487 
488 
wm8960_write_reg(wm8960_control_t * control,uint8_t reg,uint16_t val)489 hpm_stat_t wm8960_write_reg(wm8960_control_t *control, uint8_t reg, uint16_t val)
490 {
491     uint8_t buff[2];
492     /* The first 7 bits (B15 to B9) are address bits that select which control register */
493     /* is accessed. The remaining 9 bits (B8 to B0) are data bits */
494     buff[0] = (reg << 1) | (uint8_t)((val >> 8U) & 0x0001U);
495     buff[1] = (uint8_t)(val & 0xFFU);
496 
497     /* record reg val */
498     wm8960_reg_val[reg] = val;
499 
500     return i2c_master_write(control->ptr, control->slave_address, buff, 2U);
501 }
502 
wm8960_read_reg(uint8_t reg,uint16_t * val)503 hpm_stat_t wm8960_read_reg(uint8_t reg, uint16_t *val)
504 {
505     if (reg >= WM8960_REG_NUM) {
506         return status_invalid_argument;
507     }
508     *val = wm8960_reg_val[reg];
509 
510     return status_success;
511 }
512 
wm8960_modify_reg(wm8960_control_t * control,uint8_t reg,uint16_t mask,uint16_t val)513 hpm_stat_t wm8960_modify_reg(wm8960_control_t *control, uint8_t reg, uint16_t mask, uint16_t val)
514 {
515     hpm_stat_t stat = 0;
516     uint16_t reg_val;
517 
518     /* Read the register value out */
519     stat = wm8960_read_reg(reg, &reg_val);
520     if (stat != status_success) {
521         return status_fail;
522     }
523 
524     /* Modify the value */
525     reg_val &= (uint16_t)~mask;
526     reg_val |= val;
527 
528     /* Write the data to register */
529     stat = wm8960_write_reg(control, reg, reg_val);
530     if (stat != status_success) {
531         return status_fail;
532     }
533 
534     return status_success;
535 }
536