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