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