1 /*
2  * Copyright 2022-2023 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     return ret;
400 }
401 
WM8962_SetProtocol(wm8962_handle_t * handle,wm8962_protocol_t protocol)402 status_t WM8962_SetProtocol(wm8962_handle_t *handle, wm8962_protocol_t protocol)
403 {
404     status_t ret = kStatus_Success;
405 
406     if (protocol == kWM8962_BusPCMB)
407     {
408         WM8962_CHECK_RET(
409             WM8962_ModifyReg(handle, WM8962_IFACE0, WM8962_IFACE0_FORMAT_MASK, ((uint16_t)protocol | 0x10U)), ret);
410     }
411     else if (protocol == kWM8962_BusPCMA)
412     {
413         WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_IFACE0, WM8962_IFACE0_FORMAT_MASK, ((uint16_t)protocol - 1U)),
414                          ret);
415     }
416     else
417     {
418         WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_IFACE0, WM8962_IFACE0_FORMAT_MASK, (uint16_t)protocol), ret);
419     }
420 
421     return ret;
422 }
423 
WM8962_SetModulePower(wm8962_handle_t * handle,wm8962_module_t module,bool isEnabled)424 status_t WM8962_SetModulePower(wm8962_handle_t *handle, wm8962_module_t module, bool isEnabled)
425 {
426     status_t ret = kStatus_Success;
427 
428     switch (module)
429     {
430         case kWM8962_ModuleADC:
431             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER1, WM8962_POWER1_ADCL_MASK,
432                                               ((uint16_t)isEnabled << WM8962_POWER1_ADCL_SHIFT)),
433                              ret);
434             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER1, WM8962_POWER1_ADCR_MASK,
435                                               ((uint16_t)isEnabled << WM8962_POWER1_ADCR_SHIFT)),
436                              ret);
437             break;
438         case kWM8962_ModuleDAC:
439             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER2, WM8962_POWER2_DACL_MASK,
440                                               ((uint16_t)isEnabled << WM8962_POWER2_DACL_SHIFT)),
441                              ret);
442             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER2, WM8962_POWER2_DACR_MASK,
443                                               ((uint16_t)isEnabled << WM8962_POWER2_DACR_SHIFT)),
444                              ret);
445             break;
446         case kWM8962_ModuleLineIn:
447             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER1, WM8962_POWER1_AINL_MASK,
448                                               ((uint16_t)isEnabled << WM8962_POWER1_AINL_SHIFT)),
449                              ret);
450             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER1, WM8962_POWER1_AINR_MASK,
451                                               ((uint16_t)isEnabled << WM8962_POWER1_AINR_SHIFT)),
452                              ret);
453             break;
454         case kWM8962_ModuleHeadphone:
455             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER2, WM8962_POWER2_LOUT1_MASK,
456                                               ((uint16_t)isEnabled << WM8962_POWER2_LOUT1_SHIFT)),
457                              ret);
458             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER2, WM8962_POWER2_ROUT1_MASK,
459                                               ((uint16_t)isEnabled << WM8962_POWER2_ROUT1_SHIFT)),
460                              ret);
461             break;
462         case kWM8962_ModuleMICB:
463             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER1, WM8962_POWER1_MICB_MASK,
464                                               ((uint16_t)isEnabled << WM8962_POWER1_MICB_SHIFT)),
465                              ret);
466             break;
467         case kWM8962_ModuleSpeaker:
468             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER2, WM8962_POWER2_SPKL_MASK,
469                                               ((uint16_t)isEnabled << WM8962_POWER2_SPKL_SHIFT)),
470                              ret);
471             WM8962_CHECK_RET(WM8962_ModifyReg(handle, WM8962_POWER2, WM8962_POWER2_SPKR_MASK,
472                                               ((uint16_t)isEnabled << WM8962_POWER2_SPKR_SHIFT)),
473                              ret);
474             break;
475         default:
476             ret = kStatus_InvalidArgument;
477             break;
478     }
479 
480     return ret;
481 }
482 
WM8962_SetDataRoute(wm8962_handle_t * handle,const wm8962_route_config_t * route)483 status_t WM8962_SetDataRoute(wm8962_handle_t *handle, const wm8962_route_config_t *route)
484 {
485     status_t ret = kStatus_Success;
486 
487     /* Input PGA source */
488     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LEFT_INPUT_PGA, (0x10U | (uint16_t)route->leftInputPGASource)),
489                      ret);
490     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RIGHT_INPUT_PGA, (0x10U | (uint16_t)route->rightInputPGASource)),
491                      ret);
492 
493     /* Input MIXER source */
494     WM8962_CHECK_RET(
495         WM8962_WriteReg(handle, WM8962_INPUTMIX,
496                         (uint16_t)(((route->leftInputMixerSource & 7U) << 3U) | (route->rightInputMixerSource & 7U))),
497         ret);
498 
499     /* Output MIXER source */
500 
501     if (route->leftSpeakerPGASource == kWM8962_OutputPGASourceMixer)
502     {
503         WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LEFT_SPEAKER_MIXER, (uint16_t)route->leftSpeakerMixerSource),
504                          ret);
505     }
506     else
507     {
508         WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LEFT_SPEAKER_MIXER, 0U), ret);
509     }
510 
511     if (route->rightSpeakerPGASource == kWM8962_OutputPGASourceMixer)
512     {
513         WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RIGHT_SPEAKER_MIXER, (uint16_t)route->rightSpeakerMixerSource),
514                          ret);
515     }
516     else
517     {
518         WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RIGHT_SPEAKER_MIXER, 0U), ret);
519     }
520 
521     if (route->leftHeadphonePGASource == kWM8962_OutputPGASourceMixer)
522     {
523         WM8962_CHECK_RET(
524             WM8962_WriteReg(handle, WM8962_LEFT_HEADPHONE_MIXER, (uint16_t)route->leftHeadphoneMixerSource), ret);
525     }
526     else
527     {
528         WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LEFT_HEADPHONE_MIXER, 0U), ret);
529     }
530 
531     if (route->rightHeadphonePGASource == kWM8962_OutputPGASourceMixer)
532     {
533         WM8962_CHECK_RET(
534             WM8962_WriteReg(handle, WM8962_RIGHT_HEADPHONE_MIXER, (uint16_t)route->rightHeadphoneMixerSource), ret);
535     }
536     else
537     {
538         WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RIGHT_HEADPHONE_MIXER, 0U), ret);
539     }
540 
541     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_INPUT_MIXER_1, 3U), ret);
542 
543     return ret;
544 }
545 
WM8962_SetModuleVolume(wm8962_handle_t * handle,wm8962_module_t module,uint32_t volume)546 status_t WM8962_SetModuleVolume(wm8962_handle_t *handle, wm8962_module_t module, uint32_t volume)
547 {
548     uint16_t vol = 0;
549     status_t ret = kStatus_Success;
550     switch (module)
551     {
552         case kWM8962_ModuleADC:
553             if (volume > 255U)
554             {
555                 ret = kStatus_InvalidArgument;
556             }
557             else
558             {
559                 vol = (uint16_t)volume;
560                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LADC, vol), ret);
561                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RADC, vol), ret);
562                 /* Update volume */
563                 vol = (uint16_t)(0x100U | volume);
564                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LADC, vol), ret);
565                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RADC, vol), ret);
566             }
567             break;
568         case kWM8962_ModuleDAC:
569             if (volume > 255U)
570             {
571                 ret = kStatus_InvalidArgument;
572             }
573             else
574             {
575                 vol = (uint16_t)volume;
576                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LDAC, vol), ret);
577                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RDAC, vol), ret);
578                 vol = 0x100U | (uint16_t)volume;
579                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LDAC, vol), ret);
580                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RDAC, vol), ret);
581             }
582             break;
583         case kWM8962_ModuleHeadphone:
584             if (volume > 0x7FU)
585             {
586                 ret = kStatus_InvalidArgument;
587             }
588             else
589             {
590                 vol = (uint16_t)volume;
591                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LOUT1, vol), ret);
592                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ROUT1, vol), ret);
593                 vol = 0x100U | (uint16_t)volume;
594                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LOUT1, vol), ret);
595                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ROUT1, vol), ret);
596             }
597             break;
598         case kWM8962_ModuleLineIn:
599             if (volume > 0x3FU)
600             {
601                 ret = kStatus_InvalidArgument;
602             }
603             else
604             {
605                 vol = (uint16_t)volume;
606                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LINVOL, vol), ret);
607                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RINVOL, vol), ret);
608                 vol = 0x100U | (uint16_t)volume;
609                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LINVOL, vol), ret);
610                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RINVOL, vol), ret);
611             }
612             break;
613         case kWM8962_ModuleSpeaker:
614             if (volume > 0x7FU)
615             {
616                 ret = kStatus_InvalidArgument;
617             }
618             else
619             {
620                 vol = (uint16_t)volume;
621                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LOUT2, vol), ret);
622                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ROUT2, vol), ret);
623                 vol = 0x100U | (uint16_t)volume;
624                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LOUT2, vol), ret);
625                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ROUT2, vol), ret);
626             }
627             break;
628         default:
629             ret = kStatus_InvalidArgument;
630             break;
631     }
632     return ret;
633 }
634 
WM8962_GetModuleVolume(wm8962_handle_t * handle,wm8962_module_t module)635 uint32_t WM8962_GetModuleVolume(wm8962_handle_t *handle, wm8962_module_t module)
636 {
637     uint16_t vol = 0;
638 
639     switch (module)
640     {
641         case kWM8962_ModuleADC:
642             (void)WM8962_ReadReg(handle, WM8962_LADC, &vol);
643             vol &= 0xFFU;
644             break;
645         case kWM8962_ModuleDAC:
646             (void)WM8962_ReadReg(handle, WM8962_LDAC, &vol);
647             vol &= 0xFFU;
648             break;
649         case kWM8962_ModuleHeadphone:
650             (void)WM8962_ReadReg(handle, WM8962_LOUT1, &vol);
651             vol &= 0x7FU;
652             break;
653         default:
654             vol = 0;
655             break;
656     }
657     return vol;
658 }
659 
WM8962_SetModuleMute(wm8962_handle_t * handle,wm8962_module_t module,bool isEnabled)660 status_t WM8962_SetModuleMute(wm8962_handle_t *handle, wm8962_module_t module, bool isEnabled)
661 {
662     uint16_t vol = 0;
663     status_t ret = kStatus_Success;
664     switch (module)
665     {
666         case kWM8962_ModuleADC:
667             /*
668              * Digital Mute
669              */
670             if (isEnabled)
671             {
672                 WM8962_CHECK_RET(WM8962_ReadReg(handle, WM8962_LADC, &handle->volume[kWM8962_ModuleADC]), ret);
673                 WM8962_CHECK_RET(WM8962_ReadReg(handle, WM8962_RADC, &handle->volume[kWM8962_ModuleADC]), ret);
674                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LADC, 0x100), ret);
675                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RADC, 0x100), ret);
676             }
677             else
678             {
679                 vol = 0x100U | (uint16_t)handle->volume[kWM8962_ModuleADC];
680                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LADC, vol), ret);
681                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RADC, vol), ret);
682             }
683             break;
684         case kWM8962_ModuleDAC:
685             /*
686              * Digital mute
687              */
688             if (isEnabled)
689             {
690                 WM8962_CHECK_RET(WM8962_ReadReg(handle, WM8962_LDAC, &handle->volume[kWM8962_ModuleDAC]), ret);
691                 WM8962_CHECK_RET(WM8962_ReadReg(handle, WM8962_RDAC, &handle->volume[kWM8962_ModuleDAC]), ret);
692                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LDAC, 0x100), ret);
693                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RDAC, 0x100), ret);
694             }
695             else
696             {
697                 vol = 0x100U | (uint16_t)handle->volume[kWM8962_ModuleDAC];
698                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LDAC, vol), ret);
699                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_RDAC, vol), ret);
700             }
701             break;
702         case kWM8962_ModuleHeadphone:
703             /*
704              * Analog mute
705              */
706             if (isEnabled)
707             {
708                 WM8962_CHECK_RET(WM8962_ReadReg(handle, WM8962_LOUT1, &handle->volume[kWM8962_ModuleHeadphone]), ret);
709                 WM8962_CHECK_RET(WM8962_ReadReg(handle, WM8962_ROUT1, &handle->volume[kWM8962_ModuleHeadphone]), ret);
710                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LOUT1, 0x100), ret);
711                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ROUT1, 0x100), ret);
712             }
713             else
714             {
715                 vol = 0x100U | (uint16_t)handle->volume[kWM8962_ModuleHeadphone];
716                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LOUT1, vol), ret);
717                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ROUT1, vol), ret);
718             }
719             break;
720 
721         case kWM8962_ModuleSpeaker:
722             if (isEnabled)
723             {
724                 WM8962_CHECK_RET(WM8962_ReadReg(handle, WM8962_LOUT2, &handle->volume[kWM8962_ModuleSpeaker]), ret);
725                 WM8962_CHECK_RET(WM8962_ReadReg(handle, WM8962_ROUT2, &handle->volume[kWM8962_ModuleSpeaker]), ret);
726                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LOUT2, 0x100), ret);
727                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ROUT2, 0x100), ret);
728             }
729             else
730             {
731                 vol = 0x100U | (uint16_t)handle->volume[kWM8962_ModuleSpeaker];
732                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_LOUT2, vol), ret);
733                 WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ROUT2, vol), ret);
734             }
735             break;
736         default:
737             ret = kStatus_InvalidArgument;
738             break;
739     }
740     return ret;
741 }
742 
WM8962_ConfigDataFormat(wm8962_handle_t * handle,uint32_t sysclk,uint32_t sample_rate,uint32_t bits)743 status_t WM8962_ConfigDataFormat(wm8962_handle_t *handle, uint32_t sysclk, uint32_t sample_rate, uint32_t bits)
744 {
745     status_t retval = kStatus_Success;
746     uint16_t val    = 0;
747     uint32_t ratio  = sysclk / sample_rate;
748 
749     /*
750      * Slave mode (MS = 0), LRP = 0, 32bit WL, left justified (FORMAT[1:0]=0b01)
751      */
752     switch (bits)
753     {
754         case 16:
755             retval = WM8962_ModifyReg(handle, WM8962_IFACE0, WM8962_IFACE0_WL_MASK,
756                                       WM8962_IFACE0_WL(WM8962_IFACE0_WL_16BITS));
757             break;
758         case 20:
759             retval = WM8962_ModifyReg(handle, WM8962_IFACE0, WM8962_IFACE0_WL_MASK,
760                                       WM8962_IFACE0_WL(WM8962_IFACE0_WL_20BITS));
761             break;
762         case 24:
763             retval = WM8962_ModifyReg(handle, WM8962_IFACE0, WM8962_IFACE0_WL_MASK,
764                                       WM8962_IFACE0_WL(WM8962_IFACE0_WL_24BITS));
765             break;
766         case 32:
767             retval = WM8962_ModifyReg(handle, WM8962_IFACE0, WM8962_IFACE0_WL_MASK,
768                                       WM8962_IFACE0_WL(WM8962_IFACE0_WL_32BITS));
769             break;
770         default:
771             retval = kStatus_InvalidArgument;
772             break;
773     }
774 
775     if (kStatus_Success != retval)
776     {
777         return retval;
778     }
779 
780     switch (sample_rate)
781     {
782         case kWM8962_AudioSampleRate8KHz:
783             val = 0x15U;
784             break;
785         case kWM8962_AudioSampleRate11025Hz:
786             val = 0x04U;
787             break;
788         case kWM8962_AudioSampleRate12KHz:
789             val = 0x14U;
790             break;
791         case kWM8962_AudioSampleRate16KHz:
792             val = 0x13U;
793             break;
794         case kWM8962_AudioSampleRate22050Hz:
795             val = 0x02U;
796             break;
797         case kWM8962_AudioSampleRate24KHz:
798             val = 0x12U;
799             break;
800         case kWM8962_AudioSampleRate32KHz:
801             val = 0x11U;
802             break;
803         case kWM8962_AudioSampleRate44100Hz:
804             val = 0x00U;
805             break;
806         case kWM8962_AudioSampleRate48KHz:
807             val = 0x10U;
808             break;
809         case kWM8962_AudioSampleRate88200Hz:
810             val = 0x06U;
811             break;
812         case kWM8962_AudioSampleRate96KHz:
813             val = 0x16U;
814             break;
815         default:
816             retval = kStatus_InvalidArgument;
817             break;
818     }
819 
820     if (kStatus_Success != retval)
821     {
822         return retval;
823     }
824 
825     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_ADDCTL3, val), retval);
826 
827     switch (ratio)
828     {
829         case 64:
830             val = 0x00U;
831             break;
832         case 128:
833             val = 0x02U;
834             break;
835         case 192:
836             val = 0x04U;
837             break;
838         case 256:
839             val = 0x06U;
840             break;
841         case 384:
842             val = 0x08U;
843             break;
844         case 512:
845             val = 0x0AU;
846             break;
847         case 768:
848             val = 0x0CU;
849             break;
850         case 1024:
851             val = 0x0EU;
852             break;
853         case 1536:
854             val = 0x12U;
855             break;
856         case 3072:
857             val = 0x14U;
858             break;
859         case 6144:
860             val = 0x16U;
861             break;
862         default:
863             retval = kStatus_InvalidArgument;
864             break;
865     }
866 
867     if (kStatus_Success != retval)
868     {
869         return retval;
870     }
871 
872     WM8962_CHECK_RET(WM8962_WriteReg(handle, WM8962_CLK4, val), retval);
873 
874     return retval;
875 }
876 
WM8962_WriteReg(wm8962_handle_t * handle,uint16_t reg,uint16_t val)877 status_t WM8962_WriteReg(wm8962_handle_t *handle, uint16_t reg, uint16_t val)
878 {
879     uint16_t buff = (uint16_t)(WM8962_SWAP_UINT16_BYTE_SEQUENCE(val) & 0xFFFFU);
880 
881     return CODEC_I2C_Send(handle->i2cHandle, handle->config->slaveAddress, reg, 2U, (uint8_t *)(uintptr_t)&buff, 2U);
882 }
883 
WM8962_ReadReg(wm8962_handle_t * handle,uint16_t reg,uint16_t * val)884 status_t WM8962_ReadReg(wm8962_handle_t *handle, uint16_t reg, uint16_t *val)
885 {
886     status_t retval    = 0;
887     uint16_t readValue = 0U;
888 
889     retval = CODEC_I2C_Receive(handle->i2cHandle, handle->config->slaveAddress, reg, 2U, (uint8_t *)&readValue, 2U);
890     if (retval == kStatus_Success)
891     {
892         *val = WM8962_SWAP_UINT16_BYTE_SEQUENCE(readValue);
893     }
894     return retval;
895 }
896 
WM8962_ModifyReg(wm8962_handle_t * handle,uint16_t reg,uint16_t mask,uint16_t val)897 status_t WM8962_ModifyReg(wm8962_handle_t *handle, uint16_t reg, uint16_t mask, uint16_t val)
898 {
899     status_t retval  = 0;
900     uint16_t reg_val = 0;
901     retval           = WM8962_ReadReg(handle, reg, &reg_val);
902     if (retval != kStatus_Success)
903     {
904         return kStatus_Fail;
905     }
906     reg_val &= (uint16_t)~mask;
907     reg_val |= val;
908     retval = WM8962_WriteReg(handle, reg, reg_val);
909     if (retval != kStatus_Success)
910     {
911         return kStatus_Fail;
912     }
913     return kStatus_Success;
914 }
915 
916 #if DEBUG_WM8962_REGISTER
WM8962_ReadAllReg(wm8962_handle_t * handle,uint32_t endAddress)917 void WM8962_ReadAllReg(wm8962_handle_t *handle, uint32_t endAddress)
918 {
919     status_t retval    = 0;
920     uint16_t readValue = 0U, i = 0U;
921 
922     for (i = 0U; i < endAddress; i++)
923     {
924         retval = CODEC_I2C_Receive(handle->i2cHandle, handle->config->slaveAddress, i, 2U, (uint8_t *)&readValue, 2U);
925         if (retval == kStatus_Success)
926         {
927             PRINTF("REG %x, value %x\r\n", i, WM8962_SWAP_UINT16_BYTE_SEQUENCE(readValue));
928         }
929         else
930         {
931             assert(false);
932         }
933     }
934 }
935 #endif
936