1 /*
2 * Copyright (c) 2016, Freescale Semiconductor, Inc.
3 * Copyright 2016-2021 NXP
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8 #include "fsl_wm8960.h"
9
10 /*******************************************************************************
11 * Definitations
12 ******************************************************************************/
13 #define WM8960_CHECK_RET(x, status) \
14 (status) = (x); \
15 if ((status) != kStatus_Success) \
16 { \
17 return (status); \
18 }
19
20 /*! @brief WM8960 f2 better performance range */
21 #define WM8960_PLL_F2_MIN_FREQ 90000000U
22 #define WM8960_PLL_F2_MAX_FREQ 100000000U
23 /*! @brief WM8960 PLLN range */
24 #define WM8960_PLL_N_MIN_VALUE 6U
25 #define WM8960_PLL_N_MAX_VALUE 12U
26 /*******************************************************************************
27 * Prototypes
28 ******************************************************************************/
29
30 /*******************************************************************************
31 * Variables
32 ******************************************************************************/
33 /*
34 * wm8960 register cache
35 * We can't read the WM8960 register space when we are
36 * using 2 wire for device control, so we cache them instead.
37 */
38 static const uint16_t wm8960_reg[WM8960_CACHEREGNUM] = {
39 0x0097, 0x0097, 0x0000, 0x0000, 0x0000, 0x0008, 0x0000, 0x000a, 0x01c0, 0x0000, 0x00ff, 0x00ff, 0x0000, 0x0000,
40 0x0000, 0x0000, 0x0000, 0x007b, 0x0100, 0x0032, 0x0000, 0x00c3, 0x00c3, 0x01c0, 0x0000, 0x0000, 0x0000, 0x0000,
41 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0100, 0x0050, 0x0050, 0x0050, 0x0050, 0x0000, 0x0000, 0x0000, 0x0000,
42 0x0040, 0x0000, 0x0000, 0x0050, 0x0050, 0x0000, 0x0002, 0x0037, 0x004d, 0x0080, 0x0008, 0x0031, 0x0026, 0x00e9,
43 };
44
45 static uint16_t reg_cache[WM8960_CACHEREGNUM];
46 /*******************************************************************************
47 * Code
48 ******************************************************************************/
WM8960_SetInternalPllConfig(wm8960_handle_t * handle,uint32_t inputMclk,uint32_t outputClk,uint32_t sampleRate,uint32_t bitWidth)49 static status_t WM8960_SetInternalPllConfig(
50 wm8960_handle_t *handle, uint32_t inputMclk, uint32_t outputClk, uint32_t sampleRate, uint32_t bitWidth)
51 {
52 status_t ret = kStatus_Success;
53 uint32_t pllF2 = outputClk * 4U, pllPrescale = 0U, sysclkDiv = 1U, pllR = 0, pllN = 0, pllK = 0U, fracMode = 0U;
54
55 /* disable PLL power */
56 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_POWER2, 1U, 0U), ret);
57 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_CLOCK1, 7U, 0U), ret);
58
59 pllN = pllF2 / inputMclk;
60 if (pllN < WM8960_PLL_N_MIN_VALUE)
61 {
62 inputMclk >>= 1U;
63 pllPrescale = 1;
64 pllN = pllF2 / inputMclk;
65 if (pllN < WM8960_PLL_N_MIN_VALUE)
66 {
67 sysclkDiv = 2U;
68 pllN = (pllF2 * sysclkDiv) / inputMclk;
69 }
70 }
71
72 if ((pllN < WM8960_PLL_N_MIN_VALUE) || (pllN > WM8960_PLL_N_MAX_VALUE))
73 {
74 return kStatus_InvalidArgument;
75 }
76
77 pllR = (uint32_t)(((uint64_t)pllF2 * sysclkDiv * 1000U) / (inputMclk / 1000U));
78 pllK = (uint32_t)(((1UL << 24U) * ((uint64_t)pllR - (uint64_t)pllN * 1000U * 1000U)) / 1000U / 1000U);
79 if (pllK != 0U)
80 {
81 fracMode = 1U;
82 }
83 WM8960_CHECK_RET(
84 WM8960_WriteReg(handle, WM8960_PLL1,
85 ((uint16_t)fracMode << 5U) | ((uint16_t)pllPrescale << 4U) | ((uint16_t)pllN & 0xFU)),
86 ret);
87 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_PLL2, (uint16_t)(pllK >> 16U) & 0xFFU), ret);
88 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_PLL3, (uint16_t)(pllK >> 8U) & 0xFFU), ret);
89 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_PLL4, (uint16_t)pllK & 0xFFU), ret);
90 /* enable PLL power */
91 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_POWER2, 1U, 1U), ret);
92
93 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_CLOCK1, 7U, (uint16_t)(((sysclkDiv == 1U ? 0U : sysclkDiv) << 1U) | 1U)), ret);
94
95 return ret;
96 }
97
WM8960_SetMasterClock(wm8960_handle_t * handle,uint32_t sysclk,uint32_t sampleRate,uint32_t bitWidth)98 static status_t WM8960_SetMasterClock(wm8960_handle_t *handle, uint32_t sysclk, uint32_t sampleRate, uint32_t bitWidth)
99 {
100 uint32_t bitClockDivider = 0U, regDivider = 0U;
101 status_t ret = kStatus_Success;
102
103 bitClockDivider = (sysclk * 2U) / (sampleRate * bitWidth * 2U);
104
105 switch (bitClockDivider)
106 {
107 case 2:
108 regDivider = 0U;
109 break;
110 case 3:
111 regDivider = 1U;
112 break;
113 case 4:
114 regDivider = 2U;
115 break;
116 case 6:
117 regDivider = 3U;
118 break;
119 case 8:
120 regDivider = 4U;
121 break;
122 case 11:
123 regDivider = 5U;
124 break;
125 case 12:
126 regDivider = 6U;
127 break;
128 case 16:
129 regDivider = 7U;
130 break;
131 case 22:
132 regDivider = 8U;
133 break;
134 case 24:
135 regDivider = 9U;
136 break;
137 case 32:
138 regDivider = 10U;
139 break;
140 case 44:
141 regDivider = 11U;
142 break;
143 case 48:
144 regDivider = 12U;
145 break;
146
147 default:
148 ret = kStatus_InvalidArgument;
149 break;
150 }
151 if (ret == kStatus_Success)
152 {
153 /* configure the master bit clock divider will be better */
154 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_CLOCK2, WM8960_CLOCK2_BCLK_DIV_MASK, (uint16_t)regDivider),
155 ret);
156 }
157
158 return ret;
159 }
160
WM8960_Init(wm8960_handle_t * handle,const wm8960_config_t * config)161 status_t WM8960_Init(wm8960_handle_t *handle, const wm8960_config_t *config)
162 {
163 status_t ret = kStatus_Success;
164
165 handle->config = config;
166 uint32_t sysclk = config->format.mclk_HZ;
167
168 /* i2c bus initialization */
169 if (CODEC_I2C_Init(handle->i2cHandle, config->i2cConfig.codecI2CInstance, WM8960_I2C_BAUDRATE,
170 config->i2cConfig.codecI2CSourceClock) != (status_t)kStatus_HAL_I2cSuccess)
171 {
172 return kStatus_Fail;
173 }
174 /* load wm8960 register map */
175 (void)memcpy(reg_cache, wm8960_reg, sizeof(wm8960_reg));
176
177 /* Reset the codec */
178 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RESET, 0x00), ret);
179 /*
180 * VMID=50K, Enable VREF, AINL, AINR, ADCL and ADCR
181 * I2S_IN (bit 0), I2S_OUT (bit 1), DAP (bit 4), DAC (bit 5), ADC (bit 6) are powered on
182 */
183 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_POWER1, 0xFE), ret);
184 /*
185 * Enable DACL, DACR, LOUT1, ROUT1, PLL down, SPKL, SPKR
186 */
187 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_POWER2, 0x1F8), ret);
188 /*
189 * Enable left and right channel input PGA, left and right output mixer
190 */
191 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_POWER3, 0x3C), ret);
192 /* ADC and DAC uses same clock */
193 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_IFACE2, 0x40), ret);
194 /* set data route */
195 WM8960_CHECK_RET(WM8960_SetDataRoute(handle, config->route), ret);
196 /* set data protocol */
197 WM8960_CHECK_RET(WM8960_SetProtocol(handle, config->bus), ret);
198
199 if ((config->masterClock.sysclkSource == kWM8960_SysClkSourceInternalPLL))
200 {
201 WM8960_CHECK_RET(WM8960_SetInternalPllConfig(handle, sysclk, config->masterClock.sysclkFreq,
202 config->format.sampleRate, config->format.bitWidth),
203 ret);
204 sysclk = config->masterClock.sysclkFreq;
205 }
206 /* set master or slave */
207 if (config->master_slave)
208 {
209 WM8960_CHECK_RET(WM8960_SetMasterClock(handle, sysclk, config->format.sampleRate, config->format.bitWidth),
210 ret);
211 }
212 WM8960_SetMasterSlave(handle, config->master_slave);
213 /* select left input */
214 WM8960_CHECK_RET(WM8960_SetLeftInput(handle, config->leftInputSource), ret);
215 /* select right input */
216 WM8960_CHECK_RET(WM8960_SetRightInput(handle, config->rightInputSource), ret);
217 /* speaker power */
218 if (config->enableSpeaker)
219 {
220 WM8960_CHECK_RET(WM8960_SetModule(handle, kWM8960_ModuleSpeaker, true), ret);
221 }
222
223 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_ADDCTL1, 0x0C0), ret);
224 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_ADDCTL4, 0x40), ret);
225
226 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_BYPASS1, 0x0), ret);
227 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_BYPASS2, 0x0), ret);
228 /*
229 * ADC volume, 0dB
230 */
231 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LADC, 0x1C3), ret);
232 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RADC, 0x1C3), ret);
233
234 /*
235 * Digital DAC volume, -15.5dB
236 */
237 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LDAC, 0x1E0), ret);
238 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RDAC, 0x1E0), ret);
239
240 /*
241 * Headphone volume, LOUT1 and ROUT1, -10dB
242 */
243 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LOUT1, 0x16F), ret);
244 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_ROUT1, 0x16F), ret);
245
246 /* speaker volume 6dB */
247 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LOUT2, 0x1ff), ret);
248 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_ROUT2, 0x1ff), ret);
249 /* enable class D output */
250 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_CLASSD1, 0xf7), ret);
251
252 /* Unmute DAC. */
253 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_DACCTL1, 0x0000), ret);
254 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LINVOL, 0x117), ret);
255 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RINVOL, 0x117), ret);
256
257 WM8960_CHECK_RET(WM8960_ConfigDataFormat(handle, sysclk, config->format.sampleRate, config->format.bitWidth), ret);
258
259 return ret;
260 }
261
WM8960_Deinit(wm8960_handle_t * handle)262 status_t WM8960_Deinit(wm8960_handle_t *handle)
263 {
264 status_t ret = kStatus_Success;
265
266 /* Reinit I2C in case it has been stopped by concurrent codec driver */
267 if (CODEC_I2C_Init(handle->i2cHandle, handle->config->i2cConfig.codecI2CInstance, WM8960_I2C_BAUDRATE,
268 handle->config->i2cConfig.codecI2CSourceClock) != (status_t)kStatus_HAL_I2cSuccess)
269 return kStatus_Fail;
270
271 WM8960_CHECK_RET(WM8960_SetModule(handle, kWM8960_ModuleADC, false), ret);
272 WM8960_CHECK_RET(WM8960_SetModule(handle, kWM8960_ModuleDAC, false), ret);
273 WM8960_CHECK_RET(WM8960_SetModule(handle, kWM8960_ModuleVREF, false), ret);
274 WM8960_CHECK_RET(WM8960_SetModule(handle, kWM8960_ModuleLineIn, false), ret);
275 WM8960_CHECK_RET(WM8960_SetModule(handle, kWM8960_ModuleLineOut, false), ret);
276 WM8960_CHECK_RET(WM8960_SetModule(handle, kWM8960_ModuleSpeaker, false), ret);
277 WM8960_CHECK_RET(CODEC_I2C_Deinit(handle->i2cHandle), ret);
278
279 return ret;
280 }
281
WM8960_SetMasterSlave(wm8960_handle_t * handle,bool master)282 void WM8960_SetMasterSlave(wm8960_handle_t *handle, bool master)
283 {
284 if (master)
285 {
286 (void)WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_MS_MASK, WM8960_IFACE1_MS(WM8960_IFACE1_MASTER));
287 }
288 else
289 {
290 (void)WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_MS_MASK, WM8960_IFACE1_MS(WM8960_IFACE1_SLAVE));
291 }
292 }
293
WM8960_SetModule(wm8960_handle_t * handle,wm8960_module_t module,bool isEnabled)294 status_t WM8960_SetModule(wm8960_handle_t *handle, wm8960_module_t module, bool isEnabled)
295 {
296 status_t ret = kStatus_Success;
297 switch (module)
298 {
299 case kWM8960_ModuleADC:
300 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_POWER1, WM8960_POWER1_ADCL_MASK,
301 ((uint16_t)isEnabled << WM8960_POWER1_ADCL_SHIFT)),
302 ret);
303 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_POWER1, WM8960_POWER1_ADCR_MASK,
304 ((uint16_t)isEnabled << WM8960_POWER1_ADCR_SHIFT)),
305 ret);
306 break;
307 case kWM8960_ModuleDAC:
308 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_POWER2, WM8960_POWER2_DACL_MASK,
309 ((uint16_t)isEnabled << WM8960_POWER2_DACL_SHIFT)),
310 ret);
311 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_POWER2, WM8960_POWER2_DACR_MASK,
312 ((uint16_t)isEnabled << WM8960_POWER2_DACR_SHIFT)),
313 ret);
314 break;
315 case kWM8960_ModuleVREF:
316 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_POWER1, WM8960_POWER1_VREF_MASK,
317 ((uint16_t)isEnabled << WM8960_POWER1_VREF_SHIFT)),
318 ret);
319 break;
320 case kWM8960_ModuleLineIn:
321 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_POWER1, WM8960_POWER1_AINL_MASK,
322 ((uint16_t)isEnabled << WM8960_POWER1_AINL_SHIFT)),
323 ret);
324 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_POWER1, WM8960_POWER1_AINR_MASK,
325 ((uint16_t)isEnabled << WM8960_POWER1_AINR_SHIFT)),
326 ret);
327 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_POWER3, WM8960_POWER3_LMIC_MASK,
328 ((uint16_t)isEnabled << WM8960_POWER3_LMIC_SHIFT)),
329 ret);
330 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_POWER3, WM8960_POWER3_RMIC_MASK,
331 ((uint16_t)isEnabled << WM8960_POWER3_RMIC_SHIFT)),
332 ret);
333 break;
334 case kWM8960_ModuleLineOut:
335 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_POWER2, WM8960_POWER2_LOUT1_MASK,
336 ((uint16_t)isEnabled << WM8960_POWER2_LOUT1_SHIFT)),
337 ret);
338 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_POWER2, WM8960_POWER2_ROUT1_MASK,
339 ((uint16_t)isEnabled << WM8960_POWER2_ROUT1_SHIFT)),
340 ret);
341 break;
342 case kWM8960_ModuleMICB:
343 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_POWER1, WM8960_POWER1_MICB_MASK,
344 ((uint16_t)isEnabled << WM8960_POWER1_MICB_SHIFT)),
345 ret);
346 break;
347 case kWM8960_ModuleSpeaker:
348 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_POWER2, WM8960_POWER2_SPKL_MASK,
349 ((uint16_t)isEnabled << WM8960_POWER2_SPKL_SHIFT)),
350 ret);
351 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_POWER2, WM8960_POWER2_SPKR_MASK,
352 ((uint16_t)isEnabled << WM8960_POWER2_SPKR_SHIFT)),
353 ret);
354 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_CLASSD1, 0xF7), ret);
355 break;
356 case kWM8960_ModuleOMIX:
357 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_POWER3, WM8960_POWER3_LOMIX_MASK,
358 ((uint16_t)isEnabled << WM8960_POWER3_LOMIX_SHIFT)),
359 ret);
360 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_POWER3, WM8960_POWER3_ROMIX_MASK,
361 ((uint16_t)isEnabled << WM8960_POWER3_ROMIX_SHIFT)),
362 ret);
363 break;
364 default:
365 ret = kStatus_InvalidArgument;
366 break;
367 }
368 return ret;
369 }
370
WM8960_SetDataRoute(wm8960_handle_t * handle,wm8960_route_t route)371 status_t WM8960_SetDataRoute(wm8960_handle_t *handle, wm8960_route_t route)
372 {
373 status_t ret = kStatus_Success;
374 switch (route)
375 {
376 case kWM8960_RouteBypass:
377 /* Bypass means from line-in to HP*/
378 /*
379 * Left LINPUT3 to left output mixer, LINPUT3 left output mixer volume = 0dB
380 */
381 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LOUTMIX, 0x80), ret);
382
383 /*
384 * Right RINPUT3 to right output mixer, RINPUT3 right output mixer volume = 0dB
385 */
386 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_ROUTMIX, 0x80), ret);
387 break;
388 case kWM8960_RoutePlayback:
389 /* Data route I2S_IN-> DAC-> HP */
390 /*
391 * Left DAC to left output mixer, LINPUT3 left output mixer volume = 0dB
392 */
393 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LOUTMIX, 0x100), ret);
394
395 /*
396 * Right DAC to right output mixer, RINPUT3 right output mixer volume = 0dB
397 */
398 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_ROUTMIX, 0x100), ret);
399 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_POWER3, 0x0C), ret);
400 /* Set power for DAC */
401 WM8960_CHECK_RET(WM8960_SetModule(handle, kWM8960_ModuleDAC, true), ret);
402 WM8960_CHECK_RET(WM8960_SetModule(handle, kWM8960_ModuleOMIX, true), ret);
403 WM8960_CHECK_RET(WM8960_SetModule(handle, kWM8960_ModuleLineOut, true), ret);
404 break;
405 case kWM8960_RoutePlaybackandRecord:
406 /*
407 * Left DAC to left output mixer, LINPUT3 left output mixer volume = 0dB
408 */
409 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LOUTMIX, 0x100), ret);
410
411 /*
412 * Right DAC to right output mixer, RINPUT3 right output mixer volume = 0dB
413 */
414 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_ROUTMIX, 0x100), ret);
415 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_POWER3, 0x3C), ret);
416 WM8960_CHECK_RET(WM8960_SetModule(handle, kWM8960_ModuleDAC, true), ret);
417 WM8960_CHECK_RET(WM8960_SetModule(handle, kWM8960_ModuleADC, true), ret);
418 WM8960_CHECK_RET(WM8960_SetModule(handle, kWM8960_ModuleLineIn, true), ret);
419 WM8960_CHECK_RET(WM8960_SetModule(handle, kWM8960_ModuleOMIX, true), ret);
420 WM8960_CHECK_RET(WM8960_SetModule(handle, kWM8960_ModuleLineOut, true), ret);
421 break;
422 case kWM8960_RouteRecord:
423 /* LINE_IN->ADC->I2S_OUT */
424 /*
425 * Left and right input boost, LIN3BOOST and RIN3BOOST = 0dB
426 */
427 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_POWER3, 0x30), ret);
428 /* Power up ADC and AIN */
429 WM8960_CHECK_RET(WM8960_SetModule(handle, kWM8960_ModuleLineIn, true), ret);
430 WM8960_CHECK_RET(WM8960_SetModule(handle, kWM8960_ModuleADC, true), ret);
431 break;
432 default:
433 ret = kStatus_InvalidArgument;
434 break;
435 }
436 return ret;
437 }
438
WM8960_SetLeftInput(wm8960_handle_t * handle,wm8960_input_t input)439 status_t WM8960_SetLeftInput(wm8960_handle_t *handle, wm8960_input_t input)
440 {
441 status_t ret = kStatus_Success;
442 uint16_t val = 0;
443
444 switch (input)
445 {
446 case kWM8960_InputClosed:
447 WM8960_CHECK_RET(WM8960_ReadReg(WM8960_POWER1, &val), ret);
448 val &= (uint16_t) ~(WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK);
449 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_POWER1, val), ret);
450 break;
451 case kWM8960_InputSingleEndedMic:
452 /* Only LMN1 enabled, LMICBOOST to 13db, LMIC2B enabled */
453 WM8960_CHECK_RET(WM8960_ReadReg(WM8960_POWER1, &val), ret);
454 val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK | WM8960_POWER1_MICB_MASK);
455 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_POWER1, val), ret);
456 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LINPATH, 0x138), ret);
457 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LINVOL, 0x117), ret);
458 break;
459 case kWM8960_InputDifferentialMicInput2:
460 WM8960_CHECK_RET(WM8960_ReadReg(WM8960_POWER1, &val), ret);
461 val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK | WM8960_POWER1_MICB_MASK);
462 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_POWER1, val), ret);
463 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LINPATH, 0x178), ret);
464 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LINVOL, 0x117), ret);
465 break;
466 case kWM8960_InputDifferentialMicInput3:
467 WM8960_CHECK_RET(WM8960_ReadReg(WM8960_POWER1, &val), ret);
468 val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK | WM8960_POWER1_MICB_MASK);
469 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_POWER1, val), ret);
470 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LINPATH, 0x1B8), ret);
471 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LINVOL, 0x117), ret);
472 break;
473 case kWM8960_InputLineINPUT2:
474 WM8960_CHECK_RET(WM8960_ReadReg(WM8960_POWER1, &val), ret);
475 val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK);
476 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_POWER1, val), ret);
477 WM8960_CHECK_RET(WM8960_ReadReg(WM8960_INBMIX1, &val), ret);
478 val |= 0xEU;
479 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_INBMIX1, val), ret);
480 break;
481 case kWM8960_InputLineINPUT3:
482 WM8960_CHECK_RET(WM8960_ReadReg(WM8960_POWER1, &val), ret);
483 val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK);
484 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_POWER1, val), ret);
485 WM8960_CHECK_RET(WM8960_ReadReg(WM8960_INBMIX1, &val), ret);
486 val |= 0x70U;
487 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_INBMIX1, val), ret);
488 break;
489 default:
490 ret = kStatus_InvalidArgument;
491 break;
492 }
493
494 return ret;
495 }
496
WM8960_SetRightInput(wm8960_handle_t * handle,wm8960_input_t input)497 status_t WM8960_SetRightInput(wm8960_handle_t *handle, wm8960_input_t input)
498 {
499 status_t ret = kStatus_Success;
500 uint16_t val = 0;
501
502 switch (input)
503 {
504 case kWM8960_InputClosed:
505 WM8960_CHECK_RET(WM8960_ReadReg(WM8960_POWER1, &val), ret);
506 val &= (uint16_t) ~(WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK);
507 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_POWER1, val), ret);
508 break;
509 case kWM8960_InputSingleEndedMic:
510 /* Only LMN1 enabled, LMICBOOST to 13db, LMIC2B enabled */
511 WM8960_CHECK_RET(WM8960_ReadReg(WM8960_POWER1, &val), ret);
512 val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK | WM8960_POWER1_MICB_MASK);
513 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_POWER1, val), ret);
514 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RINPATH, 0x138), ret);
515 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RINVOL, 0x117), ret);
516 break;
517 case kWM8960_InputDifferentialMicInput2:
518 WM8960_CHECK_RET(WM8960_ReadReg(WM8960_POWER1, &val), ret);
519 val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK | WM8960_POWER1_MICB_MASK);
520 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_POWER1, val), ret);
521 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RINPATH, 0x178), ret);
522 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RINVOL, 0x117), ret);
523 break;
524 case kWM8960_InputDifferentialMicInput3:
525 WM8960_CHECK_RET(WM8960_ReadReg(WM8960_POWER1, &val), ret);
526 val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK | WM8960_POWER1_MICB_MASK);
527 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_POWER1, val), ret);
528 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RINPATH, 0x1B8), ret);
529 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RINVOL, 0x117), ret);
530 break;
531 case kWM8960_InputLineINPUT2:
532 WM8960_CHECK_RET(WM8960_ReadReg(WM8960_POWER1, &val), ret);
533 val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK);
534 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_POWER1, val), ret);
535 WM8960_CHECK_RET(WM8960_ReadReg(WM8960_INBMIX2, &val), ret);
536 val |= 0xEU;
537 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_INBMIX2, val), ret);
538 break;
539 case kWM8960_InputLineINPUT3:
540 WM8960_CHECK_RET(WM8960_ReadReg(WM8960_POWER1, &val), ret);
541 val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK);
542 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_POWER1, val), ret);
543 WM8960_CHECK_RET(WM8960_ReadReg(WM8960_INBMIX2, &val), ret);
544 val |= 0x70U;
545 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_INBMIX2, val), ret);
546 break;
547 default:
548 ret = kStatus_InvalidArgument;
549 break;
550 }
551
552 return ret;
553 }
554
WM8960_SetProtocol(wm8960_handle_t * handle,wm8960_protocol_t protocol)555 status_t WM8960_SetProtocol(wm8960_handle_t *handle, wm8960_protocol_t protocol)
556 {
557 return WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_FORMAT_MASK | WM8960_IFACE1_LRP_MASK,
558 (uint16_t)protocol);
559 }
560
WM8960_SetVolume(wm8960_handle_t * handle,wm8960_module_t module,uint32_t volume)561 status_t WM8960_SetVolume(wm8960_handle_t *handle, wm8960_module_t module, uint32_t volume)
562 {
563 uint16_t vol = 0;
564 status_t ret = kStatus_Success;
565 switch (module)
566 {
567 case kWM8960_ModuleADC:
568 if (volume > 255U)
569 {
570 ret = kStatus_InvalidArgument;
571 }
572 else
573 {
574 vol = (uint16_t)volume;
575 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LADC, vol), ret);
576 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RADC, vol), ret);
577 /* Update volume */
578 vol = (uint16_t)(0x100U | volume);
579 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LADC, vol), ret);
580 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RADC, vol), ret);
581 }
582 break;
583 case kWM8960_ModuleDAC:
584 if (volume > 255U)
585 {
586 ret = kStatus_InvalidArgument;
587 }
588 else
589 {
590 vol = (uint16_t)volume;
591 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LDAC, vol), ret);
592 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RDAC, vol), ret);
593 vol = 0x100U | (uint16_t)volume;
594 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LDAC, vol), ret);
595 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RDAC, vol), ret);
596 }
597 break;
598 case kWM8960_ModuleHP:
599 if (volume > 0x7FU)
600 {
601 ret = kStatus_InvalidArgument;
602 }
603 else
604 {
605 vol = (uint16_t)volume;
606 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LOUT1, vol), ret);
607 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_ROUT1, vol), ret);
608 vol = 0x100U | (uint16_t)volume;
609 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LOUT1, vol), ret);
610 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_ROUT1, vol), ret);
611 }
612 break;
613 case kWM8960_ModuleLineIn:
614 if (volume > 0x3FU)
615 {
616 ret = kStatus_InvalidArgument;
617 }
618 else
619 {
620 vol = (uint16_t)volume;
621 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LINVOL, vol), ret);
622 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RINVOL, vol), ret);
623 vol = 0x100U | (uint16_t)volume;
624 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LINVOL, vol), ret);
625 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RINVOL, vol), ret);
626 }
627 break;
628 case kWM8960_ModuleSpeaker:
629 if (volume > 0x7FU)
630 {
631 ret = kStatus_InvalidArgument;
632 }
633 else
634 {
635 vol = (uint16_t)volume;
636 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LOUT2, vol), ret);
637 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_ROUT2, vol), ret);
638 vol = 0x100U | (uint16_t)volume;
639 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LOUT2, vol), ret);
640 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_ROUT2, vol), ret);
641 }
642 break;
643 default:
644 ret = kStatus_InvalidArgument;
645 break;
646 }
647 return ret;
648 }
649
WM8960_GetVolume(wm8960_handle_t * handle,wm8960_module_t module)650 uint32_t WM8960_GetVolume(wm8960_handle_t *handle, wm8960_module_t module)
651 {
652 uint16_t vol = 0;
653
654 switch (module)
655 {
656 case kWM8960_ModuleADC:
657 (void)WM8960_ReadReg(WM8960_LADC, &vol);
658 vol &= 0xFFU;
659 break;
660 case kWM8960_ModuleDAC:
661 (void)WM8960_ReadReg(WM8960_LDAC, &vol);
662 vol &= 0xFFU;
663 break;
664 case kWM8960_ModuleHP:
665 (void)WM8960_ReadReg(WM8960_LOUT1, &vol);
666 vol &= 0x7FU;
667 break;
668 case kWM8960_ModuleLineOut:
669 (void)WM8960_ReadReg(WM8960_LINVOL, &vol);
670 vol &= 0x3FU;
671 break;
672 default:
673 vol = 0;
674 break;
675 }
676 return vol;
677 }
678
WM8960_SetMute(wm8960_handle_t * handle,wm8960_module_t module,bool isEnabled)679 status_t WM8960_SetMute(wm8960_handle_t *handle, wm8960_module_t module, bool isEnabled)
680 {
681 status_t ret = kStatus_Success;
682 switch (module)
683 {
684 case kWM8960_ModuleADC:
685 /*
686 * Digital Mute
687 */
688 if (isEnabled)
689 {
690 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LADC, 0x100), ret);
691 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RADC, 0x100), ret);
692 }
693 else
694 {
695 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LADC, 0x1C3), ret);
696 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RADC, 0x1C3), ret);
697 }
698 break;
699 case kWM8960_ModuleDAC:
700 /*
701 * Digital mute
702 */
703 if (isEnabled)
704 {
705 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LDAC, 0x100), ret);
706 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RDAC, 0x100), ret);
707 }
708 else
709 {
710 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LDAC, 0x1FF), ret);
711 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_RDAC, 0x1FF), ret);
712 }
713 break;
714 case kWM8960_ModuleHP:
715 /*
716 * Analog mute
717 */
718 if (isEnabled)
719 {
720 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LOUT1, 0x100), ret);
721 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_ROUT1, 0x100), ret);
722 }
723 else
724 {
725 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LOUT1, 0x16F), ret);
726 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_ROUT1, 0x16F), ret);
727 }
728 break;
729
730 case kWM8960_ModuleSpeaker:
731 if (isEnabled)
732 {
733 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LOUT2, 0x100), ret);
734 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_ROUT2, 0x100), ret);
735 }
736 else
737 {
738 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_LOUT2, 0x16F), ret);
739 WM8960_CHECK_RET(WM8960_WriteReg(handle, WM8960_ROUT2, 0x16f), ret);
740 }
741 break;
742
743 case kWM8960_ModuleLineOut:
744 break;
745 default:
746 ret = kStatus_InvalidArgument;
747 break;
748 }
749 return ret;
750 }
751
WM8960_ConfigDataFormat(wm8960_handle_t * handle,uint32_t sysclk,uint32_t sample_rate,uint32_t bits)752 status_t WM8960_ConfigDataFormat(wm8960_handle_t *handle, uint32_t sysclk, uint32_t sample_rate, uint32_t bits)
753 {
754 status_t retval = kStatus_Success;
755 uint32_t divider = 0;
756 uint16_t val = 0;
757
758 /* Compute sample rate divider, dac and adc are the same sample rate */
759 divider = sysclk / sample_rate;
760 if (divider == 256U)
761 {
762 val = 0;
763 }
764 else if (divider > 256U)
765 {
766 val = (uint16_t)(((divider / 256U) << 6U) | ((divider / 256U) << 3U));
767 }
768 else
769 {
770 return kStatus_InvalidArgument;
771 }
772
773 retval = WM8960_ModifyReg(handle, WM8960_CLOCK1, 0x1F8U, val);
774 if (retval != kStatus_Success)
775 {
776 return retval;
777 }
778
779 /*
780 * Slave mode (MS = 0), LRP = 0, 32bit WL, left justified (FORMAT[1:0]=0b01)
781 */
782 switch (bits)
783 {
784 case 16:
785 retval = WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_WL_MASK,
786 WM8960_IFACE1_WL(WM8960_IFACE1_WL_16BITS));
787 break;
788 case 20:
789 retval = WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_WL_MASK,
790 WM8960_IFACE1_WL(WM8960_IFACE1_WL_20BITS));
791 break;
792 case 24:
793 retval = WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_WL_MASK,
794 WM8960_IFACE1_WL(WM8960_IFACE1_WL_24BITS));
795 break;
796 case 32:
797 retval = WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_WL_MASK,
798 WM8960_IFACE1_WL(WM8960_IFACE1_WL_32BITS));
799 break;
800 default:
801 retval = kStatus_InvalidArgument;
802 break;
803 }
804
805 return retval;
806 }
807
WM8960_SetJackDetect(wm8960_handle_t * handle,bool isEnabled)808 status_t WM8960_SetJackDetect(wm8960_handle_t *handle, bool isEnabled)
809 {
810 status_t retval = 0;
811 uint16_t val = 0;
812
813 if (WM8960_ReadReg(WM8960_ADDCTL2, &val) != kStatus_Success)
814 {
815 return kStatus_Fail;
816 }
817
818 if (isEnabled)
819 {
820 val |= 0x40U;
821 }
822 else
823 {
824 val &= 0xCFU;
825 }
826
827 retval = WM8960_WriteReg(handle, WM8960_ADDCTL2, val);
828
829 return retval;
830 }
831
WM8960_WriteReg(wm8960_handle_t * handle,uint8_t reg,uint16_t val)832 status_t WM8960_WriteReg(wm8960_handle_t *handle, uint8_t reg, uint16_t val)
833 {
834 uint8_t cmd;
835 uint8_t buff = (uint8_t)val & 0xFFU;
836
837 /* The register address */
838 cmd = (reg << 1U) | (uint8_t)((val >> 8U) & 0x0001U);
839
840 reg_cache[reg] = val;
841
842 return CODEC_I2C_Send(handle->i2cHandle, handle->config->slaveAddress, cmd, 1U, &buff, 1U);
843 }
844
WM8960_ReadReg(uint8_t reg,uint16_t * val)845 status_t WM8960_ReadReg(uint8_t reg, uint16_t *val)
846 {
847 if (reg >= WM8960_CACHEREGNUM)
848 {
849 return kStatus_InvalidArgument;
850 }
851
852 *val = reg_cache[reg];
853
854 return kStatus_Success;
855 }
856
WM8960_ModifyReg(wm8960_handle_t * handle,uint8_t reg,uint16_t mask,uint16_t val)857 status_t WM8960_ModifyReg(wm8960_handle_t *handle, uint8_t reg, uint16_t mask, uint16_t val)
858 {
859 status_t retval = 0;
860 uint16_t reg_val = 0;
861 retval = WM8960_ReadReg(reg, ®_val);
862 if (retval != kStatus_Success)
863 {
864 return kStatus_Fail;
865 }
866 reg_val &= (uint16_t)~mask;
867 reg_val |= val;
868 retval = WM8960_WriteReg(handle, reg, reg_val);
869 if (retval != kStatus_Success)
870 {
871 return kStatus_Fail;
872 }
873 return kStatus_Success;
874 }
875
WM8960_SetPlay(wm8960_handle_t * handle,uint32_t playSource)876 status_t WM8960_SetPlay(wm8960_handle_t *handle, uint32_t playSource)
877 {
878 status_t ret = kStatus_Success;
879
880 if (((uint32_t)kWM8960_PlaySourcePGA & playSource) != 0U)
881 {
882 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_BYPASS1, 0x80U, 0x80U), ret);
883 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_BYPASS2, 0x80U, 0x80U), ret);
884 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_LOUTMIX, 0x180U, 0U), ret);
885 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_ROUTMIX, 0x180U, 0U), ret);
886 }
887
888 if ((playSource & (uint32_t)kWM8960_PlaySourceDAC) != 0U)
889 {
890 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_BYPASS1, 0x80U, 0x00U), ret);
891 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_BYPASS2, 0x80U, 0x00U), ret);
892 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_LOUTMIX, 0x180U, 0x100U), ret);
893 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_ROUTMIX, 0x180U, 0x100U), ret);
894 }
895
896 if ((playSource & (uint32_t)kWM8960_PlaySourceInput) != 0U)
897 {
898 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_BYPASS1, 0x80U, 0x0U), ret);
899 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_BYPASS2, 0x80U, 0x0U), ret);
900 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_LOUTMIX, 0x180U, 0x80U), ret);
901 WM8960_CHECK_RET(WM8960_ModifyReg(handle, WM8960_ROUTMIX, 0x180U, 0x80U), ret);
902 }
903
904 return ret;
905 }
906