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