1 /*
2  * Copyright 2022 NXP
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 #include "fsl_wm8962.h"
8 
9 /*******************************************************************************
10  * Definitations
11  ******************************************************************************/
12 #define WM8962_CHECK_RET(x, status)  \
13     (status) = (x);                  \
14     if ((status) != kStatus_Success) \
15     {                                \
16         return (status);             \
17     }
18 /*! @brief WM8962 max clock */
19 #define WM8962_MAX_DSP_CLOCK (24576000U)
20 #define WM8962_MAX_SYS_CLOCK (12288000U)
21 /*! @brief WM8962 f2 better performance range */
22 #define WM8962_FLL_VCO_MIN_FREQ 90000000U
23 #define WM8962_FLL_VCO_MAX_FREQ 100000000U
24 #define WM8962_FLL_LOCK_TIMEOUT 10000000U
25 /*! @brief WM8962 FLLN range */
26 #define WM8962_FLL_N_MIN_VALUE              6U
27 #define WM8962_FLL_N_MAX_VALUE              12U
28 #define WM8962_FLL_MAX_REFERENCE_CLOCK      (13500000U)
29 #define WM8962_SWAP_UINT16_BYTE_SEQUENCE(x) ((((x)&0x00ffU) << 8U) | (((x)&0xff00U) >> 8U))
30 /*! @brief WM8962 default sequence */
31 typedef enum _wm8962_sequence_id
32 {
33     kWM8962_SequenceDACToHeadphonePowerUp = 0x80U, /*!< dac to headphone power up sequence */
34     kWM8962_SequenceAnalogueInputPowerUp  = 0x92U, /*!< Analogue input power up sequence */
35     kWM8962_SequenceChipPowerDown         = 0x9BU, /*!< Chip power down sequence */
36     kWM8962_SequenceSpeakerSleep          = 0xE4U, /*!< Speaker sleep sequence */
37     kWM8962_SequenceSpeakerWake           = 0xE8U, /*!< speaker wake sequence */
38 } wm8962_sequence_id_t;
39 
40 /*******************************************************************************
41  * Prototypes
42  ******************************************************************************/
43 static status_t WM8962_StartSequence(wm8962_handle_t *handle, wm8962_sequence_id_t id);
44 /*******************************************************************************
45  * Variables
46  ******************************************************************************/
47 /*******************************************************************************
48  * Code
49  ******************************************************************************/
WM8962_StartSequence(wm8962_handle_t * handle,wm8962_sequence_id_t id)50 static status_t WM8962_StartSequence(wm8962_handle_t *handle, wm8962_sequence_id_t id)
51 {
52     uint32_t delayUs      = 93000U;
53     uint16_t sequenceStat = 0U;
54     status_t ret          = kStatus_Success;
55 
56     switch (id)
57     {
58         case kWM8962_SequenceDACToHeadphonePowerUp:
59             delayUs = 93000U;
60             break;
61         case kWM8962_SequenceAnalogueInputPowerUp:
62             delayUs = 75000U;
63             break;
64         case kWM8962_SequenceChipPowerDown:
65             delayUs = 32000U;
66             break;
67         case kWM8962_SequenceSpeakerSleep:
68             delayUs = 2000U;
69             break;
70         case kWM8962_SequenceSpeakerWake:
71             delayUs = 2000U;
72             break;
73         default:
74             delayUs = 93000U;
75             break;
76     }
77 
78     WM8962_CHECK_RET(WM8962_WriteReg(handle, 0x57, 0x20U), ret);
79     WM8962_CHECK_RET(WM8962_WriteReg(handle, 0x5A, (uint16_t)id), ret);
80     while (delayUs != 0U)
81     {
82         WM8962_CHECK_RET(WM8962_ReadReg(handle, 0x5D, &sequenceStat), ret);
83         if ((sequenceStat & 1U) == 0U)
84         {
85             break;
86         }
87         SDK_DelayAtLeastUs(1000U, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
88         delayUs -= 1000U;
89     }
90 
91     return (sequenceStat & 1U) == 0U ? kStatus_Success : kStatus_Fail;
92 }
93 
WM8962_GCD_FLL(uint32_t a,uint32_t b)94 static uint32_t WM8962_GCD_FLL(uint32_t a, uint32_t b)
95 {
96     uint32_t value = 0U;
97 
98     while (b != 0U)
99     {
100         value = a % b;
101 
102         a = b;
103         b = value;
104     }
105 
106     return a;
107 }
108 
WM8962_GetClockDivider(uint32_t inputClock,uint32_t maxClock,uint16_t * divider)109 static status_t WM8962_GetClockDivider(uint32_t inputClock, uint32_t maxClock, uint16_t *divider)
110 {
111     if ((inputClock >> 2U) > maxClock)
112     {
113         return kStatus_InvalidArgument;
114     }
115     else
116     {
117         /* fll reference clock divider */
118         if (inputClock > maxClock)
119         {
120             if ((inputClock >> 1U) > maxClock)
121             {
122                 *divider = 2U;
123             }
124             else
125             {
126                 *divider = 1U;
127             }
128         }
129         else
130         {
131             *divider = 0U;
132         }
133     }
134 
135     return kStatus_Success;
136 }
137 
WM8962_SetInternalFllConfig(wm8962_handle_t * handle,const wm8962_fll_clk_config_t * fllConfig)138 static status_t WM8962_SetInternalFllConfig(wm8962_handle_t *handle, const wm8962_fll_clk_config_t *fllConfig)
139 {
140     assert(fllConfig->fllReferenceClockFreq != 0U);
141 
142     status_t ret    = kStatus_Success;
143     uint16_t refDiv = 0U, fllLock = 0U;
144     uint32_t refClock = fllConfig->fllReferenceClockFreq, fllRatio = 0U, outClk = fllConfig->fllOutputFreq,
145              fllOutDiv = 0U, fVCO = 0U;
146     uint32_t fllGCD = 0U, fllTheta = 0U, fllLambda = 0U, nDivider = 0U, delayUs = WM8962_FLL_LOCK_TIMEOUT;
147 
148     WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_FLL_CTRL_1, 1U, 0U), ret);
149     WM8962_CHECK_RET(WM8962_GetClockDivider(fllConfig->fllReferenceClockFreq, WM8962_FLL_MAX_REFERENCE_CLOCK, &refDiv),
150                      ret);
151 
152     refClock = fllConfig->fllReferenceClockFreq / (uint32_t)(1UL << (refDiv & 3U));
153 
154     if (refClock > 1000000U)
155     {
156         fllRatio = 0U;
157     }
158     else if (refClock > 256000U)
159     {
160         fllRatio = 1U;
161     }
162     else if (refClock > 128000U)
163     {
164         fllRatio = 2U;
165     }
166     else if (refClock > 64000U)
167     {
168         fllRatio = 3U;
169     }
170     else
171     {
172         fllRatio = 4U;
173     }
174 
175     if ((outClk < 2083300U) && ((outClk > 1875000U)))
176     {
177         fllOutDiv = 0x2FU;
178     }
179     else if ((outClk < 3125000U) && ((outClk > 2812500U)))
180     {
181         fllOutDiv = 0x1FU;
182     }
183     else if ((outClk < 4166700U) && ((outClk > 3750000U)))
184     {
185         fllOutDiv = 0x17U;
186     }
187     else if ((outClk < 6250000U) && ((outClk > 5625000U)))
188     {
189         fllOutDiv = 0xFU;
190     }
191     else if ((outClk < 12500000U) && ((outClk > 11250000U)))
192     {
193         fllOutDiv = 7U;
194     }
195     else if ((outClk < 20000000U) && ((outClk > 18000000U)))
196     {
197         fllOutDiv = 4U;
198     }
199     else if ((outClk < 25000000U) && ((outClk > 22500000U)))
200     {
201         fllOutDiv = 3U;
202     }
203     else if ((outClk < 50000000U) && ((outClk > 40000000U)))
204     {
205         fllOutDiv = 1U;
206     }
207     else
208     {
209         return kStatus_InvalidArgument;
210     }
211 
212     fVCO     = (fllOutDiv + 1U) * outClk;
213     nDivider = fVCO / ((uint32_t)(1UL << fllRatio) * refClock);
214 
215     fllGCD = WM8962_GCD_FLL((uint32_t)(1UL << fllRatio) * refClock, fVCO);
216 
217     fllTheta  = (fVCO - nDivider * (uint32_t)(1UL << fllRatio) * refClock) / fllGCD;
218     fllLambda = ((uint32_t)(1UL << fllRatio) * refClock) / fllGCD;
219 
220     /* FLL configurations */
221     WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_FLL_CTRL_2, 0x1FBU, (uint16_t)(refDiv | (fllOutDiv << 3U))), ret);
222     WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_FLL_CTRL_3, 7U, (uint16_t)fllRatio), ret);
223     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_FLL_CTRL_6, (uint16_t)fllTheta), ret);
224     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_FLL_CTRL_7, (uint16_t)fllLambda), ret);
225     WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_FLL_CTRL_8, 0x3FFU, (uint16_t)nDivider), ret);
226     WM8962_CHECK_RET(
227         WM8962_ModifyReg(handle, WM8962_FLL_CTRL_1, 0x65U,
228                          (uint16_t)((fllConfig->fllClockSource == kWM8962_FLLClkSourceMCLK ? 0x0UL : 0x20UL) | 0x5UL)),
229         ret);
230 
231     /* Polling FLL lock status */
232     while (delayUs != 0U)
233     {
234         WM8962_CHECK_RET(WM8962_ReadReg(handle, WM8962_INT_STATUS_2, &fllLock), ret);
235         if ((fllLock & 0x20U) == 0x20U)
236         {
237             break;
238         }
239         SDK_DelayAtLeastUs(1000U, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
240         delayUs -= 1000U;
241     }
242     ret = ((fllLock & 0x20U) == 0x20U) ? kStatus_Success : kStatus_Fail;
243 
244     return ret;
245 }
246 
WM8962_SetMasterClock(wm8962_handle_t * handle,uint32_t dspClock,uint32_t sampleRate,uint32_t bitWidth)247 static status_t WM8962_SetMasterClock(wm8962_handle_t *handle,
248                                       uint32_t dspClock,
249                                       uint32_t sampleRate,
250                                       uint32_t bitWidth)
251 {
252     uint32_t bitClockDivider = 0U, regDivider = 0U;
253     status_t ret = kStatus_Success;
254 
255     bitClockDivider = dspClock / (sampleRate * bitWidth * 2U);
256 
257     switch (bitClockDivider)
258     {
259         case 1:
260             regDivider = 0U;
261             break;
262         case 2:
263             regDivider = 2U;
264             break;
265         case 3:
266             regDivider = 3U;
267             break;
268         case 4:
269             regDivider = 4U;
270             break;
271         case 6:
272             regDivider = 6U;
273             break;
274         case 8:
275             regDivider = 7U;
276             break;
277         case 12:
278             regDivider = 9U;
279             break;
280         case 16:
281             regDivider = 10U;
282             break;
283         case 24:
284             regDivider = 11U;
285             break;
286         case 32:
287             regDivider = 13U;
288             break;
289 
290         default:
291             ret = kStatus_InvalidArgument;
292             break;
293     }
294     if (ret == kStatus_Success)
295     {
296         WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_CLOCK2, WM8962_CLOCK2_BCLK_DIV_MASK, (uint16_t)regDivider),
297                          ret);
298         WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_IFACE2, (uint16_t)(bitWidth * 2U)), ret);
299     }
300 
301     return ret;
302 }
303 
WM8962_Init(wm8962_handle_t * handle,const wm8962_config_t * config)304 status_t WM8962_Init(wm8962_handle_t *handle, const wm8962_config_t *config)
305 {
306     status_t ret = kStatus_Success;
307 
308     handle->config = config;
309     uint32_t mclk = config->format.mclk_HZ, sysClk = 0U;
310     uint16_t clockDiv = 0U;
311 
312     /* i2c bus initialization */
313     if (CODEC_I2C_Init(handle->i2cHandle, config->i2cConfig.codecI2CInstance, WM8962_I2C_BAUDRATE,
314                        config->i2cConfig.codecI2CSourceClock) != (status_t)kStatus_HAL_I2cSuccess)
315     {
316         return kStatus_Fail;
317     }
318 
319     /* Reset the codec */
320     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RESET, 0x6243U), ret);
321     /* disable internal osc/FLL2/FLL3/FLL*/
322     WM8962_CHECK_RET(WM8962_WriteReg(handle, 0x81U, 0), ret);
323     WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_FLL_CTRL_1, 1U, 0U), ret);
324     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_CLOCK2, 0x9E4), ret);
325     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_POWER1, 0x1FE), ret);
326     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_POWER2, 0x1E0), ret);
327 
328     WM8962_CHECK_RET(WM8962_StartSequence(handle, kWM8962_SequenceDACToHeadphonePowerUp), ret);
329     WM8962_CHECK_RET(WM8962_StartSequence(handle, kWM8962_SequenceAnalogueInputPowerUp), ret);
330     WM8962_CHECK_RET(WM8962_StartSequence(handle, kWM8962_SequenceSpeakerWake), ret);
331 
332     /* master clock configuration if it is master */
333     if (config->masterSlave && config->sysclkSource == kWM8962_SysClkSourceFLL)
334     {
335         /* Internal FLL config */
336         WM8962_CHECK_RET(WM8962_SetInternalFllConfig(handle, &config->fllClock), ret);
337         // WM8962_SetInternalFllConfig(handle, &config->fllClock);
338         mclk = config->fllClock.fllOutputFreq;
339         /* SYS clock source FLL */
340         WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_CLOCK2, 0x20U, 0U), ret);
341         WM8962_CHECK_RET(
342             WM8962_ModifyReg(handle, WM8962_CLOCK2, (uint16_t)((3U << 9U) | 0x20U), (uint16_t)((1U << 9U) | 0x20U)),
343             ret);
344     }
345 
346     /* DSP clock divider, maximum 24.576MHZ */
347     WM8962_CHECK_RET(WM8962_GetClockDivider(mclk, WM8962_MAX_DSP_CLOCK, &clockDiv), ret);
348     WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_CLOCK1, 3U << 9U, (uint16_t)(clockDiv << 9U)), ret);
349     if (config->masterSlave)
350     {
351         WM8962_CHECK_RET(WM8962_SetMasterClock(handle, mclk / (uint32_t)(1UL << clockDiv), config->format.sampleRate,
352                                                config->format.bitWidth),
353                          ret);
354         WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_IFACE0, 1U << 6U, 1U << 6U), ret);
355     }
356 
357     /* enable system clock */
358     WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_CLOCK2, 0x20U, 0x20U), ret);
359 
360     /* sysclk clock divider, maximum 12.288MHZ */
361     WM8962_CHECK_RET(WM8962_ReadReg(handle, WM8962_CLOCK1, &clockDiv), ret);
362     sysClk = mclk / (1UL << (clockDiv & 3U));
363 
364     /* set data route */
365     WM8962_CHECK_RET(WM8962_SetDataRoute(handle, &config->route), ret);
366     /* set data protocol */
367     WM8962_CHECK_RET(WM8962_SetProtocol(handle, config->bus), ret);
368     /*
369      * ADC volume, 0dB
370      */
371     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LADC, 0x1C0), ret);
372     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RADC, 0x1C0), ret);
373     /*
374      * Digital DAC volume, -15.5dB
375      */
376     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LDAC, 0x1C0), ret);
377     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RDAC, 0x1C0), ret);
378     /* speaker volume 6dB */
379     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LOUT2, 0x1ff), ret);
380     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ROUT2, 0x1ff), ret);
381     /* input PGA volume */
382     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LINVOL, 0x13F), ret);
383     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RINVOL, 0x13F), ret);
384     /* Headphone volume */
385     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LOUT1, 0x16B), ret);
386     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ROUT1, 0x16B), ret);
387     WM8962_CHECK_RET(WM8962_ConfigDataFormat(handle, sysClk, config->format.sampleRate, config->format.bitWidth), ret);
388 
389     return ret;
390 }
391 
WM8962_Deinit(wm8962_handle_t * handle)392 status_t WM8962_Deinit(wm8962_handle_t *handle)
393 {
394     status_t ret = kStatus_Success;
395 
396     /* power down */
397     WM8962_CHECK_RET(WM8962_StartSequence(handle, kWM8962_SequenceChipPowerDown), ret);
398 
399     WM8962_CHECK_RET(CODEC_I2C_Deinit(handle->i2cHandle), ret);
400 
401     return ret;
402 }
403 
WM8962_SetProtocol(wm8962_handle_t * handle,wm8962_protocol_t protocol)404 status_t WM8962_SetProtocol(wm8962_handle_t *handle, wm8962_protocol_t protocol)
405 {
406     status_t ret = kStatus_Success;
407 
408     if (protocol == kWM8962_BusPCMB)
409     {
410         WM8962_CHECK_RET(
411             WM8962_ModifyReg(handle, WM8962_IFACE0, WM8962_IFACE0_FORMAT_MASK, ((uint16_t)protocol | 0x10U)), ret);
412     }
413     else if (protocol == kWM8962_BusPCMA)
414     {
415         WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_IFACE0, WM8962_IFACE0_FORMAT_MASK, ((uint16_t)protocol - 1U)),
416                          ret);
417     }
418     else
419     {
420         WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_IFACE0, WM8962_IFACE0_FORMAT_MASK, (uint16_t)protocol), ret);
421     }
422 
423     return ret;
424 }
425 
WM8962_SetModulePower(wm8962_handle_t * handle,wm8962_module_t module,bool isEnabled)426 status_t WM8962_SetModulePower(wm8962_handle_t *handle, wm8962_module_t module, bool isEnabled)
427 {
428     status_t ret = kStatus_Success;
429 
430     switch (module)
431     {
432         case kWM8962_ModuleADC:
433             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER1, WM8962_POWER1_ADCL_MASK,
434                                               ((uint16_t)isEnabled << WM8962_POWER1_ADCL_SHIFT)),
435                              ret);
436             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER1, WM8962_POWER1_ADCR_MASK,
437                                               ((uint16_t)isEnabled << WM8962_POWER1_ADCR_SHIFT)),
438                              ret);
439             break;
440         case kWM8962_ModuleDAC:
441             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER2, WM8962_POWER2_DACL_MASK,
442                                               ((uint16_t)isEnabled << WM8962_POWER2_DACL_SHIFT)),
443                              ret);
444             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER2, WM8962_POWER2_DACR_MASK,
445                                               ((uint16_t)isEnabled << WM8962_POWER2_DACR_SHIFT)),
446                              ret);
447             break;
448         case kWM8962_ModuleLineIn:
449             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER1, WM8962_POWER1_AINL_MASK,
450                                               ((uint16_t)isEnabled << WM8962_POWER1_AINL_SHIFT)),
451                              ret);
452             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER1, WM8962_POWER1_AINR_MASK,
453                                               ((uint16_t)isEnabled << WM8962_POWER1_AINR_SHIFT)),
454                              ret);
455             break;
456         case kWM8962_ModuleHeadphone:
457             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER2, WM8962_POWER2_LOUT1_MASK,
458                                               ((uint16_t)isEnabled << WM8962_POWER2_LOUT1_SHIFT)),
459                              ret);
460             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER2, WM8962_POWER2_ROUT1_MASK,
461                                               ((uint16_t)isEnabled << WM8962_POWER2_ROUT1_SHIFT)),
462                              ret);
463             break;
464         case kWM8962_ModuleMICB:
465             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER1, WM8962_POWER1_MICB_MASK,
466                                               ((uint16_t)isEnabled << WM8962_POWER1_MICB_SHIFT)),
467                              ret);
468             break;
469         case kWM8962_ModuleSpeaker:
470             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER2, WM8962_POWER2_SPKL_MASK,
471                                               ((uint16_t)isEnabled << WM8962_POWER2_SPKL_SHIFT)),
472                              ret);
473             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER2, WM8962_POWER2_SPKR_MASK,
474                                               ((uint16_t)isEnabled << WM8962_POWER2_SPKR_SHIFT)),
475                              ret);
476             break;
477         default:
478             ret = kStatus_InvalidArgument;
479             break;
480     }
481 
482     return ret;
483 }
484 
WM8962_SetDataRoute(wm8962_handle_t * handle,const wm8962_route_config_t * route)485 status_t WM8962_SetDataRoute(wm8962_handle_t *handle, const wm8962_route_config_t *route)
486 {
487     status_t ret = kStatus_Success;
488 
489     /* Input PGA source */
490     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LEFT_INPUT_PGA, (0x10U | (uint16_t)route->leftInputPGASource)),
491                      ret);
492     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RIGHT_INPUT_PGA, (0x10U | (uint16_t)route->rightInputPGASource)),
493                      ret);
494 
495     /* Input MIXER source */
496     WM8962_CHECK_RET(
497         WM8962_WriteReg(handle, WM8962_INPUTMIX,
498                         (uint16_t)(((route->leftInputMixerSource & 7U) << 3U) | (route->rightInputMixerSource & 7U))),
499         ret);
500 
501     /* Output MIXER source */
502 
503     if (route->leftSpeakerPGASource == kWM8962_OutputPGASourceMixer)
504     {
505         WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LEFT_SPEAKER_MIXER, (uint16_t)route->leftSpeakerMixerSource),
506                          ret);
507     }
508     else
509     {
510         WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LEFT_SPEAKER_MIXER, 0U), ret);
511     }
512 
513     if (route->rightSpeakerPGASource == kWM8962_OutputPGASourceMixer)
514     {
515         WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RIGHT_SPEAKER_MIXER, (uint16_t)route->rightSpeakerMixerSource),
516                          ret);
517     }
518     else
519     {
520         WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RIGHT_SPEAKER_MIXER, 0U), ret);
521     }
522 
523     if (route->leftHeadphonePGASource == kWM8962_OutputPGASourceMixer)
524     {
525         WM8962_CHECK_RET(
526             WM8962_WriteReg(handle, WM8962_LEFT_HEADPHONE_MIXER, (uint16_t)route->leftHeadphoneMixerSource), ret);
527     }
528     else
529     {
530         WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LEFT_HEADPHONE_MIXER, 0U), ret);
531     }
532 
533     if (route->rightHeadphonePGASource == kWM8962_OutputPGASourceMixer)
534     {
535         WM8962_CHECK_RET(
536             WM8962_WriteReg(handle, WM8962_RIGHT_HEADPHONE_MIXER, (uint16_t)route->rightHeadphoneMixerSource), ret);
537     }
538     else
539     {
540         WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RIGHT_HEADPHONE_MIXER, 0U), ret);
541     }
542 
543     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_INPUT_MIXER_1, 3U), ret);
544 
545     return ret;
546 }
547 
WM8962_SetModuleVolume(wm8962_handle_t * handle,wm8962_module_t module,uint32_t volume)548 status_t WM8962_SetModuleVolume(wm8962_handle_t *handle, wm8962_module_t module, uint32_t volume)
549 {
550     uint16_t vol = 0;
551     status_t ret = kStatus_Success;
552     switch (module)
553     {
554         case kWM8962_ModuleADC:
555             if (volume > 255U)
556             {
557                 ret = kStatus_InvalidArgument;
558             }
559             else
560             {
561                 vol = (uint16_t)volume;
562                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LADC, vol), ret);
563                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RADC, vol), ret);
564                 /* Update volume */
565                 vol = (uint16_t)(0x100U | volume);
566                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LADC, vol), ret);
567                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RADC, vol), ret);
568             }
569             break;
570         case kWM8962_ModuleDAC:
571             if (volume > 255U)
572             {
573                 ret = kStatus_InvalidArgument;
574             }
575             else
576             {
577                 vol = (uint16_t)volume;
578                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LDAC, vol), ret);
579                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RDAC, vol), ret);
580                 vol = 0x100U | (uint16_t)volume;
581                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LDAC, vol), ret);
582                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RDAC, vol), ret);
583             }
584             break;
585         case kWM8962_ModuleHeadphone:
586             if (volume > 0x7FU)
587             {
588                 ret = kStatus_InvalidArgument;
589             }
590             else
591             {
592                 vol = (uint16_t)volume;
593                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LOUT1, vol), ret);
594                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ROUT1, vol), ret);
595                 vol = 0x100U | (uint16_t)volume;
596                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LOUT1, vol), ret);
597                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ROUT1, vol), ret);
598             }
599             break;
600         case kWM8962_ModuleLineIn:
601             if (volume > 0x3FU)
602             {
603                 ret = kStatus_InvalidArgument;
604             }
605             else
606             {
607                 vol = (uint16_t)volume;
608                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LINVOL, vol), ret);
609                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RINVOL, vol), ret);
610                 vol = 0x100U | (uint16_t)volume;
611                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LINVOL, vol), ret);
612                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RINVOL, vol), ret);
613             }
614             break;
615         case kWM8962_ModuleSpeaker:
616             if (volume > 0x7FU)
617             {
618                 ret = kStatus_InvalidArgument;
619             }
620             else
621             {
622                 vol = (uint16_t)volume;
623                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LOUT2, vol), ret);
624                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ROUT2, vol), ret);
625                 vol = 0x100U | (uint16_t)volume;
626                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LOUT2, vol), ret);
627                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ROUT2, vol), ret);
628             }
629             break;
630         default:
631             ret = kStatus_InvalidArgument;
632             break;
633     }
634     return ret;
635 }
636 
WM8962_GetModuleVolume(wm8962_handle_t * handle,wm8962_module_t module)637 uint32_t WM8962_GetModuleVolume(wm8962_handle_t *handle, wm8962_module_t module)
638 {
639     uint16_t vol = 0;
640 
641     switch (module)
642     {
643         case kWM8962_ModuleADC:
644             (void)WM8962_ReadReg(handle, WM8962_LADC, &vol);
645             vol &= 0xFFU;
646             break;
647         case kWM8962_ModuleDAC:
648             (void)WM8962_ReadReg(handle, WM8962_LDAC, &vol);
649             vol &= 0xFFU;
650             break;
651         case kWM8962_ModuleHeadphone:
652             (void)WM8962_ReadReg(handle, WM8962_LOUT1, &vol);
653             vol &= 0x7FU;
654             break;
655         default:
656             vol = 0;
657             break;
658     }
659     return vol;
660 }
661 
WM8962_SetModuleMute(wm8962_handle_t * handle,wm8962_module_t module,bool isEnabled)662 status_t WM8962_SetModuleMute(wm8962_handle_t *handle, wm8962_module_t module, bool isEnabled)
663 {
664     status_t ret = kStatus_Success;
665     switch (module)
666     {
667         case kWM8962_ModuleADC:
668             /*
669              * Digital Mute
670              */
671             if (isEnabled)
672             {
673                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LADC, 0x100), ret);
674                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RADC, 0x100), ret);
675             }
676             else
677             {
678                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LADC, 0x1C3), ret);
679                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RADC, 0x1C3), ret);
680             }
681             break;
682         case kWM8962_ModuleDAC:
683             /*
684              * Digital mute
685              */
686             if (isEnabled)
687             {
688                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LDAC, 0x100), ret);
689                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RDAC, 0x100), ret);
690             }
691             else
692             {
693                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LDAC, 0x1FF), ret);
694                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RDAC, 0x1FF), ret);
695             }
696             break;
697         case kWM8962_ModuleHeadphone:
698             /*
699              * Analog mute
700              */
701             if (isEnabled)
702             {
703                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LOUT1, 0x100), ret);
704                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ROUT1, 0x100), ret);
705             }
706             else
707             {
708                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LOUT1, 0x16F), ret);
709                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ROUT1, 0x16F), ret);
710             }
711             break;
712 
713         case kWM8962_ModuleSpeaker:
714             if (isEnabled)
715             {
716                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LOUT2, 0x100), ret);
717                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ROUT2, 0x100), ret);
718             }
719             else
720             {
721                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LOUT2, 0x16F), ret);
722                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ROUT2, 0x16F), ret);
723             }
724             break;
725         default:
726             ret = kStatus_InvalidArgument;
727             break;
728     }
729     return ret;
730 }
731 
WM8962_ConfigDataFormat(wm8962_handle_t * handle,uint32_t sysclk,uint32_t sample_rate,uint32_t bits)732 status_t WM8962_ConfigDataFormat(wm8962_handle_t *handle, uint32_t sysclk, uint32_t sample_rate, uint32_t bits)
733 {
734     status_t retval = kStatus_Success;
735     uint16_t val    = 0;
736 
737     /*
738      * Slave mode (MS = 0), LRP = 0, 32bit WL, left justified (FORMAT[1:0]=0b01)
739      */
740     switch (bits)
741     {
742         case 16:
743             retval = WM8962_ModifyReg(handle, WM8962_IFACE0, WM8962_IFACE0_WL_MASK,
744                                       WM8962_IFACE0_WL(WM8962_IFACE0_WL_16BITS));
745             break;
746         case 20:
747             retval = WM8962_ModifyReg(handle, WM8962_IFACE0, WM8962_IFACE0_WL_MASK,
748                                       WM8962_IFACE0_WL(WM8962_IFACE0_WL_20BITS));
749             break;
750         case 24:
751             retval = WM8962_ModifyReg(handle, WM8962_IFACE0, WM8962_IFACE0_WL_MASK,
752                                       WM8962_IFACE0_WL(WM8962_IFACE0_WL_24BITS));
753             break;
754         case 32:
755             retval = WM8962_ModifyReg(handle, WM8962_IFACE0, WM8962_IFACE0_WL_MASK,
756                                       WM8962_IFACE0_WL(WM8962_IFACE0_WL_32BITS));
757             break;
758         default:
759             retval = kStatus_InvalidArgument;
760             break;
761     }
762 
763     if (kStatus_Success != retval)
764     {
765         return retval;
766     }
767 
768     switch (sample_rate)
769     {
770         case kWM8962_AudioSampleRate8KHz:
771             val = 0x15U;
772             break;
773         case kWM8962_AudioSampleRate11025Hz:
774             val = 0x04U;
775             break;
776         case kWM8962_AudioSampleRate12KHz:
777             val = 0x14U;
778             break;
779         case kWM8962_AudioSampleRate16KHz:
780             val = 0x13U;
781             break;
782         case kWM8962_AudioSampleRate22050Hz:
783             val = 0x02U;
784             break;
785         case kWM8962_AudioSampleRate24KHz:
786             val = 0x12U;
787             break;
788         case kWM8962_AudioSampleRate32KHz:
789             val = 0x11U;
790             break;
791         case kWM8962_AudioSampleRate44100Hz:
792             val = 0x00U;
793             break;
794         case kWM8962_AudioSampleRate48KHz:
795             val = 0x10U;
796             break;
797         case kWM8962_AudioSampleRate88200Hz:
798             val = 0x06U;
799             break;
800         case kWM8962_AudioSampleRate96KHz:
801             val = 0x16U;
802             break;
803         default:
804             retval = kStatus_InvalidArgument;
805             break;
806     }
807 
808     if (kStatus_Success != retval)
809     {
810         return retval;
811     }
812 
813     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ADDCTL3, val), retval);
814 
815     return retval;
816 }
817 
WM8962_WriteReg(wm8962_handle_t * handle,uint16_t reg,uint16_t val)818 status_t WM8962_WriteReg(wm8962_handle_t *handle, uint16_t reg, uint16_t val)
819 {
820     uint16_t buff = (uint16_t)(WM8962_SWAP_UINT16_BYTE_SEQUENCE(val) & 0xFFFFU);
821 
822     return CODEC_I2C_Send(handle->i2cHandle, handle->config->slaveAddress, reg, 2U, (uint8_t *)(uint32_t)&buff, 2U);
823 }
824 
WM8962_ReadReg(wm8962_handle_t * handle,uint16_t reg,uint16_t * val)825 status_t WM8962_ReadReg(wm8962_handle_t *handle, uint16_t reg, uint16_t *val)
826 {
827     status_t retval    = 0;
828     uint16_t readValue = 0U;
829 
830     retval = CODEC_I2C_Receive(handle->i2cHandle, handle->config->slaveAddress, reg, 2U, (uint8_t *)&readValue, 2U);
831     if (retval == kStatus_Success)
832     {
833         *val = WM8962_SWAP_UINT16_BYTE_SEQUENCE(readValue);
834     }
835     return retval;
836 }
837 
WM8962_ModifyReg(wm8962_handle_t * handle,uint16_t reg,uint16_t mask,uint16_t val)838 status_t WM8962_ModifyReg(wm8962_handle_t *handle, uint16_t reg, uint16_t mask, uint16_t val)
839 {
840     status_t retval  = 0;
841     uint16_t reg_val = 0;
842     retval           = WM8962_ReadReg(handle, reg, &reg_val);
843     if (retval != kStatus_Success)
844     {
845         return kStatus_Fail;
846     }
847     reg_val &= (uint16_t)~mask;
848     reg_val |= val;
849     retval = WM8962_WriteReg(handle, reg, reg_val);
850     if (retval != kStatus_Success)
851     {
852         return kStatus_Fail;
853     }
854     return kStatus_Success;
855 }
856 
857 #if DEBUG_WM8962_REGISTER
WM8962_ReadAllReg(wm8962_handle_t * handle,uint32_t endAddress)858 void WM8962_ReadAllReg(wm8962_handle_t *handle, uint32_t endAddress)
859 {
860     status_t retval    = 0;
861     uint16_t readValue = 0U, i = 0U;
862 
863     for (i = 0U; i < endAddress; i++)
864     {
865         retval = CODEC_I2C_Receive(handle->i2cHandle, handle->config->slaveAddress, i, 2U, (uint8_t *)&readValue, 2U);
866         if (retval == kStatus_Success)
867         {
868             PRINTF("REG %x, value %x\r\n", i, WM8962_SWAP_UINT16_BYTE_SEQUENCE(readValue));
869         }
870         else
871         {
872             assert(false);
873         }
874     }
875 }
876 #endif
877