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, ®_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