You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1424 lines
45 KiB
1424 lines
45 KiB
/**
|
|
******************************************************************************
|
|
* @file wm8994.c
|
|
* @author MCD Application Team
|
|
* @brief This file provides the WM8994 Audio Codec driver.
|
|
******************************************************************************
|
|
* @attention
|
|
*
|
|
* <h2><center>© Copyright (c) 2014 STMicroelectronics.
|
|
* All rights reserved.</center></h2>
|
|
*
|
|
* This software component is licensed by ST under BSD 3-Clause license,
|
|
* the "License"; You may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at:
|
|
* opensource.org/licenses/BSD-3-Clause
|
|
*
|
|
******************************************************************************
|
|
*/
|
|
|
|
/* Includes ------------------------------------------------------------------*/
|
|
#include "wm8994.h"
|
|
|
|
/** @addtogroup BSP
|
|
* @{
|
|
*/
|
|
|
|
/** @addtogroup Components
|
|
* @{
|
|
*/
|
|
|
|
/** @defgroup WM8994 WM8994
|
|
* @brief This file provides a set of functions needed to drive the
|
|
* WM8994 audio codec.
|
|
* @{
|
|
*/
|
|
|
|
/** @defgroup WM8994_Private_Types Private Types
|
|
* @{
|
|
*/
|
|
/* Audio codec driver structure initialization */
|
|
WM8994_Drv_t WM8994_Driver =
|
|
{
|
|
WM8994_Init,
|
|
WM8994_DeInit,
|
|
WM8994_ReadID,
|
|
WM8994_Play,
|
|
WM8994_Pause,
|
|
WM8994_Resume,
|
|
WM8994_Stop,
|
|
WM8994_SetFrequency,
|
|
WM8994_GetFrequency,
|
|
WM8994_SetVolume,
|
|
WM8994_GetVolume,
|
|
WM8994_SetMute,
|
|
WM8994_SetOutputMode,
|
|
WM8994_SetResolution,
|
|
WM8994_GetResolution,
|
|
WM8994_SetProtocol,
|
|
WM8994_GetProtocol,
|
|
WM8994_Reset
|
|
};
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
/** @defgroup WM8994_Function_Prototypes Function Prototypes
|
|
* @{
|
|
*/
|
|
static int32_t WM8994_ReadRegWrap(void *handle, uint16_t Reg, uint8_t* Data, uint16_t Length);
|
|
static int32_t WM8994_WriteRegWrap(void *handle, uint16_t Reg, uint8_t* Data, uint16_t Length);
|
|
static int32_t WM8994_Delay(WM8994_Object_t *pObj, uint32_t Delay);
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
/** @defgroup WM8994_Exported_Functions Exported Functions
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief Initializes the audio codec and the control interface.
|
|
* @param pObj pointer to component object
|
|
* @param pInit pointer de component init structure
|
|
* @retval 0 if correct communication, else wrong communication
|
|
*/
|
|
int32_t WM8994_Init(WM8994_Object_t *pObj, WM8994_Init_t *pInit)
|
|
{
|
|
int32_t ret;
|
|
static uint8_t ColdStartup = 1;
|
|
uint16_t tmp;
|
|
|
|
/* wm8994 Errata Work-Arounds */
|
|
tmp = 0x0003;
|
|
ret = wm8994_write_reg(&pObj->Ctx, 0x102, &tmp, 2);
|
|
tmp = 0x0000;
|
|
ret += wm8994_write_reg(&pObj->Ctx, 0x817, &tmp, 2);
|
|
ret += wm8994_write_reg(&pObj->Ctx, 0x102, &tmp, 2);
|
|
|
|
/* Enable VMID soft start (fast), Start-up Bias Current Enabled: 0x006C at reg 0x39 */
|
|
/* Bias Enable */
|
|
tmp = 0x006C;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_ANTIPOP2, &tmp, 2);
|
|
|
|
/* Enable bias generator, Enable VMID */
|
|
if (pInit->InputDevice != WM8994_IN_NONE)
|
|
{
|
|
tmp = 0x0013;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_1, &tmp, 2);
|
|
}
|
|
else
|
|
{
|
|
tmp = 0x0003;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_1, &tmp, 2);
|
|
}
|
|
|
|
/* Add Delay */
|
|
(void)WM8994_Delay(pObj, 50);
|
|
|
|
/* Path Configurations for output */
|
|
switch (pInit->OutputDevice)
|
|
{
|
|
case WM8994_OUT_SPEAKER:
|
|
/* Enable DAC1 (Left), Enable DAC1 (Right),
|
|
Disable DAC2 (Left), Disable DAC2 (Right)*/
|
|
tmp = 0x0C0C;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_5, &tmp, 2);
|
|
|
|
/* Disable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path */
|
|
tmp = 0x0000;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_LMR, &tmp, 2);
|
|
|
|
/* Disable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_RMR, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */
|
|
tmp = 0x0002;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_LMR, &tmp, 2);
|
|
|
|
/* Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_RMR, &tmp, 2);
|
|
break;
|
|
|
|
case WM8994_OUT_HEADPHONE:
|
|
/* Disable DAC1 (Left), Disable DAC1 (Right),
|
|
Enable DAC2 (Left), Enable DAC2 (Right)*/
|
|
tmp = 0x0303;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_5, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path */
|
|
tmp = 0x0001;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_LMR, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_RMR, &tmp, 2);
|
|
|
|
/* Disable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */
|
|
tmp = 0x0000;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_LMR, &tmp, 2);
|
|
|
|
/* Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_RMR, &tmp, 2);
|
|
break;
|
|
|
|
case WM8994_OUT_BOTH:
|
|
if (pInit->InputDevice == WM8994_IN_MIC1_MIC2)
|
|
{
|
|
/* Enable DAC1 (Left), Enable DAC1 (Right),
|
|
also Enable DAC2 (Left), Enable DAC2 (Right)*/
|
|
tmp = 0x0F0F;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_5, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path
|
|
Enable the AIF1 Timeslot 1 (Left) to DAC 1 (Left) mixer path */
|
|
tmp = 0x0003;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_LMR, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path
|
|
Enable the AIF1 Timeslot 1 (Right) to DAC 1 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_RMR, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 0 (Left) to DAC 2 (Left) mixer path
|
|
Enable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_LMR, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 0 (Right) to DAC 2 (Right) mixer path
|
|
Enable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_RMR, &tmp, 2);
|
|
}
|
|
else
|
|
{
|
|
/* Enable DAC1 (Left), Enable DAC1 (Right),
|
|
also Enable DAC2 (Left), Enable DAC2 (Right)*/
|
|
tmp = 0x0F0F;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_5, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path */
|
|
tmp = 0x0001;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_LMR, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_RMR, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */
|
|
tmp = 0x0002;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_LMR, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_RMR, &tmp, 2);
|
|
}
|
|
break;
|
|
|
|
case WM8994_OUT_NONE:
|
|
break;
|
|
case WM8994_OUT_AUTO :
|
|
default:
|
|
/* Disable DAC1 (Left), Disable DAC1 (Right),
|
|
Enable DAC2 (Left), Enable DAC2 (Right)*/
|
|
tmp = 0x0303;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_5, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path */
|
|
tmp = 0x0001;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_LMR, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_RMR, &tmp, 2);
|
|
|
|
/* Disable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */
|
|
tmp = 0x0000;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_LMR, &tmp, 2);
|
|
|
|
/* Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_RMR, &tmp, 2);
|
|
break;
|
|
}
|
|
|
|
/* Path Configurations for input */
|
|
switch (pInit->InputDevice)
|
|
{
|
|
case WM8994_IN_MIC2 :
|
|
/* Enable AIF1ADC2 (Left), Enable AIF1ADC2 (Right)
|
|
* Enable DMICDAT2 (Left), Enable DMICDAT2 (Right)
|
|
* Enable Left ADC, Enable Right ADC */
|
|
tmp = 0x0C30;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_4, &tmp, 2);
|
|
|
|
/* Enable AIF1 DRC2 Signal Detect & DRC in AIF1ADC2 Left/Right Timeslot 1 */
|
|
tmp = 0x00DB;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DRC2, &tmp, 2);
|
|
|
|
/* Disable IN1L, IN1R, IN2L, IN2R, Enable Thermal sensor & shutdown */
|
|
tmp = 0x6000;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_2, &tmp, 2);
|
|
|
|
/* Enable the DMIC2(Left) to AIF1 Timeslot 1 (Left) mixer path */
|
|
tmp = 0x0002;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_ADC2_LMR, &tmp, 2);
|
|
|
|
/* Enable the DMIC2(Right) to AIF1 Timeslot 1 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_ADC2_RMR, &tmp, 2);
|
|
|
|
/* GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC2 signal detect */
|
|
tmp = 0x000E;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_GPIO1, &tmp, 2);
|
|
break;
|
|
|
|
case WM8994_IN_LINE1 :
|
|
/* IN1LN_TO_IN1L, IN1RN_TO_IN1R */
|
|
tmp = 0x0011;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_INPUT_MIXER_2, &tmp, 2);
|
|
|
|
/* Disable mute on IN1L_TO_MIXINL and +30dB on IN1L PGA output */
|
|
tmp = 0x0035;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_INPUT_MIXER_3, &tmp, 2);
|
|
|
|
/* Disable mute on IN1R_TO_MIXINL, Gain = +30dB */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_INPUT_MIXER_4, &tmp, 2);
|
|
|
|
/* Enable AIF1ADC1 (Left), Enable AIF1ADC1 (Right)
|
|
* Enable Left ADC, Enable Right ADC */
|
|
tmp = 0x0303;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_4, &tmp, 2);
|
|
|
|
/* Enable AIF1 DRC1 Signal Detect & DRC in AIF1ADC1 Left/Right Timeslot 0 */
|
|
tmp = 0x00DB;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DRC1, &tmp, 2);
|
|
|
|
/* Enable IN1L and IN1R, Disable IN2L and IN2R, Enable Thermal sensor & shutdown */
|
|
tmp = 0x6350;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_2, &tmp, 2);
|
|
|
|
/* Enable the ADCL(Left) to AIF1 Timeslot 0 (Left) mixer path */
|
|
tmp = 0x0002;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_ADC1_LMR, &tmp, 2);
|
|
|
|
/* Enable the ADCR(Right) to AIF1 Timeslot 0 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_ADC1_RMR, &tmp, 2);
|
|
|
|
/* GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC1 signal detect */
|
|
tmp = 0x800D;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_GPIO1, &tmp, 2);
|
|
break;
|
|
|
|
case WM8994_IN_MIC1 :
|
|
/* Enable AIF1ADC1 (Left), Enable AIF1ADC1 (Right)
|
|
* Enable DMICDAT1 (Left), Enable DMICDAT1 (Right)
|
|
* Enable Left ADC, Enable Right ADC */
|
|
tmp = 0x030C;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_4, &tmp, 2);
|
|
|
|
/* Enable AIF1 DRC1 Signal Detect & DRC in AIF1ADC1 Left/Right Timeslot 0 */
|
|
tmp = 0x00DB;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DRC1, &tmp, 2);
|
|
|
|
/* Enable IN1L and IN1R, Disable IN2L and IN2R, Enable Thermal sensor & shutdown */
|
|
tmp = 0x6350;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_2, &tmp, 2);
|
|
|
|
/* Enable the ADCL(Left) to AIF1 Timeslot 0 (Left) mixer path */
|
|
tmp = 0x0002;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_ADC1_LMR, &tmp, 2);
|
|
|
|
/* Enable the ADCR(Right) to AIF1 Timeslot 0 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_ADC1_RMR, &tmp, 2);
|
|
|
|
/* GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC1 signal detect */
|
|
tmp = 0x000D;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_GPIO1, &tmp, 2);
|
|
break;
|
|
|
|
case WM8994_IN_MIC1_MIC2 :
|
|
/* Enable AIF1ADC1 (Left), Enable AIF1ADC1 (Right)
|
|
* Enable DMICDAT1 (Left), Enable DMICDAT1 (Right)
|
|
* Enable Left ADC, Enable Right ADC */
|
|
tmp = 0x0F3C;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_4, &tmp, 2);
|
|
|
|
/* Enable AIF1 DRC2 Signal Detect & DRC in AIF1ADC2 Left/Right Timeslot 1 */
|
|
tmp = 0x00DB;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DRC2, &tmp, 2);
|
|
|
|
/* Enable AIF1 DRC2 Signal Detect & DRC in AIF1ADC1 Left/Right Timeslot 0 */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DRC1, &tmp, 2);
|
|
|
|
/* Disable IN1L, IN1R, Enable IN2L, IN2R, Thermal sensor & shutdown */
|
|
tmp = 0x63A0;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_2, &tmp, 2);
|
|
|
|
/* Enable the ADCL(Left) to AIF1 Timeslot 0 (Left) mixer path */
|
|
tmp = 0x0002;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_ADC1_LMR, &tmp, 2);
|
|
|
|
/* Enable the ADCR(Right) to AIF1 Timeslot 0 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_ADC1_RMR, &tmp, 2);
|
|
|
|
/* Enable the DMIC2(Left) to AIF1 Timeslot 1 (Left) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_ADC2_LMR, &tmp, 2);
|
|
|
|
/* Enable the DMIC2(Right) to AIF1 Timeslot 1 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_ADC2_RMR, &tmp, 2);
|
|
|
|
/* GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC1 signal detect */
|
|
tmp = 0x000D;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_GPIO1, &tmp, 2);
|
|
|
|
break;
|
|
case WM8994_IN_LINE2 :
|
|
case WM8994_IN_NONE:
|
|
default:
|
|
/* Actually, no other input devices supported */
|
|
break;
|
|
}
|
|
|
|
/* Clock Configurations */
|
|
ret += WM8994_SetFrequency(pObj, pInit->Frequency);
|
|
|
|
if(pInit->InputDevice == WM8994_IN_MIC1_MIC2)
|
|
{
|
|
/* AIF1 Word Length = 16-bits, AIF1 Format = DSP mode */
|
|
ret += WM8994_SetResolution(pObj, WM8994_RESOLUTION_16b);
|
|
ret += WM8994_SetProtocol(pObj, WM8994_PROTOCOL_DSP);
|
|
ret += wm8994_aif1_control1_adcr_src(&pObj->Ctx, 1);
|
|
}
|
|
else
|
|
{
|
|
/* AIF1 Word Length = 16-bits, AIF1 Format = I2S (Default Register Value) */
|
|
ret += WM8994_SetResolution(pObj, pInit->Resolution);
|
|
ret += WM8994_SetProtocol(pObj, WM8994_PROTOCOL_I2S);
|
|
ret += wm8994_aif1_control1_adcr_src(&pObj->Ctx, 1);
|
|
}
|
|
|
|
/* slave mode */
|
|
tmp = 0x0000;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_MASTER_SLAVE, &tmp, 2);
|
|
|
|
/* Enable the DSP processing clock for AIF1, Enable the core clock */
|
|
tmp = 0x000A;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_CLOCKING1, &tmp, 2);
|
|
|
|
/* Enable AIF1 Clock, AIF1 Clock Source = MCLK1 pin */
|
|
tmp = 0x0001;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_CLOCKING1, &tmp, 2);
|
|
|
|
if (pInit->OutputDevice != WM8994_OUT_NONE) /* Audio output selected */
|
|
{
|
|
if ((pInit->OutputDevice == WM8994_OUT_HEADPHONE) && (pInit->InputDevice == WM8994_IN_NONE))
|
|
{
|
|
tmp = 0x0100;
|
|
/* Select DAC1 (Left) to Left Headphone Output PGA (HPOUT1LVOL) path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_OUTPUT_MIXER_1, &tmp, 2);
|
|
|
|
/* Select DAC1 (Right) to Right Headphone Output PGA (HPOUT1RVOL) path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_OUTPUT_MIXER_2, &tmp, 2);
|
|
|
|
/* Startup sequence for Headphone */
|
|
if(ColdStartup == 1U)
|
|
{
|
|
/* Enable/Start the write sequencer */
|
|
tmp = 0x8100;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_WRITE_SEQ_CTRL1, &tmp, 2);
|
|
|
|
ColdStartup=0;
|
|
/* Add Delay */
|
|
(void)WM8994_Delay(pObj, 325);
|
|
}
|
|
else
|
|
{
|
|
/* Headphone Warm Start-Up */
|
|
tmp = 0x8108;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_WRITE_SEQ_CTRL1, &tmp, 2);
|
|
|
|
/* Add Delay */
|
|
(void)WM8994_Delay(pObj, 50);
|
|
}
|
|
|
|
/* Soft un-Mute the AIF1 Timeslot 0 DAC1 path L&R */
|
|
tmp = 0x0000;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_FILTER1, &tmp, 2);
|
|
}
|
|
else
|
|
{
|
|
/* Analog Output Configuration */
|
|
|
|
/* Enable SPKRVOL PGA, Enable SPKMIXR, Enable SPKLVOL PGA, Enable SPKMIXL */
|
|
tmp = 0x0300;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_3, &tmp, 2);
|
|
|
|
/* Left Speaker Mixer Volume = 0dB */
|
|
tmp = 0x0000;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_SPKMIXL_ATT, &tmp, 2);
|
|
|
|
/* Speaker output mode = Class D, Right Speaker Mixer Volume = 0dB ((0x23, 0x0100) = class AB)*/
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_SPKMIXR_ATT, &tmp, 2);
|
|
|
|
/* Unmute DAC2 (Left) to Left Speaker Mixer (SPKMIXL) path,
|
|
Unmute DAC2 (Right) to Right Speaker Mixer (SPKMIXR) path */
|
|
tmp = 0x0300;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_SPEAKER_MIXER, &tmp, 2);
|
|
|
|
/* Enable bias generator, Enable VMID, Enable SPKOUTL, Enable SPKOUTR */
|
|
tmp = 0x3003;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_1, &tmp, 2);
|
|
/* Headphone/Speaker Enable */
|
|
|
|
if (pInit->InputDevice == WM8994_IN_MIC1_MIC2)
|
|
{
|
|
/* Enable Class W, Class W Envelope Tracking = AIF1 Timeslots 0 and 1 */
|
|
tmp = 0x0205;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_CLASS_W, &tmp, 2);
|
|
}
|
|
else
|
|
{
|
|
/* Enable Class W, Class W Envelope Tracking = AIF1 Timeslot 0 */
|
|
tmp = 0x0005;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_CLASS_W, &tmp, 2);
|
|
}
|
|
|
|
/* Enable bias generator, Enable VMID, Enable HPOUT1 (Left) and Enable HPOUT1 (Right) input stages */
|
|
/* idem for Speaker */
|
|
tmp = 0x3303;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_1, &tmp, 2);
|
|
|
|
/* Enable HPOUT1 (Left) and HPOUT1 (Right) intermediate stages */
|
|
tmp = 0x0022;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_ANALOG_HP, &tmp, 2);
|
|
|
|
/* Enable Charge Pump */
|
|
tmp = 0x9F25;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_CHARGE_PUMP1, &tmp, 2);
|
|
|
|
/* Add Delay */
|
|
(void)WM8994_Delay(pObj, 15);
|
|
|
|
tmp = 0x0001;
|
|
/* Select DAC1 (Left) to Left Headphone Output PGA (HPOUT1LVOL) path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_OUTPUT_MIXER_1, &tmp, 2);
|
|
|
|
/* Select DAC1 (Right) to Right Headphone Output PGA (HPOUT1RVOL) path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_OUTPUT_MIXER_2, &tmp, 2);
|
|
|
|
/* Enable Left Output Mixer (MIXOUTL), Enable Right Output Mixer (MIXOUTR) */
|
|
/* idem for SPKOUTL and SPKOUTR */
|
|
tmp = 0x0330;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_3, &tmp, 2);
|
|
|
|
/* Enable DC Servo and trigger start-up mode on left and right channels */
|
|
tmp = 0x0033;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_DC_SERVO1, &tmp, 2);
|
|
|
|
/* Add Delay */
|
|
(void)WM8994_Delay(pObj, 257);
|
|
|
|
/* Enable HPOUT1 (Left) and HPOUT1 (Right) intermediate and output stages. Remove clamps */
|
|
tmp = 0x00EE;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_ANALOG_HP, &tmp, 2);
|
|
}
|
|
|
|
/* Unmutes */
|
|
|
|
/* Unmute DAC 1 (Left) */
|
|
tmp = 0x00C0;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_DAC1_LEFT_VOL, &tmp, 2);
|
|
|
|
/* Unmute DAC 1 (Right) */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_DAC1_RIGHT_VOL, &tmp, 2);
|
|
|
|
/* Unmute the AIF1 Timeslot 0 DAC path */
|
|
tmp = 0x0010;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_FILTER1, &tmp, 2);
|
|
|
|
/* Unmute DAC 2 (Left) */
|
|
tmp = 0x00C0;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_DAC2_LEFT_VOL, &tmp, 2);
|
|
|
|
/* Unmute DAC 2 (Right) */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_DAC2_RIGHT_VOL, &tmp, 2);
|
|
|
|
/* Unmute the AIF1 Timeslot 1 DAC2 path */
|
|
tmp = 0x0010;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_FILTER1, &tmp, 2);
|
|
|
|
/* Volume Control */
|
|
ret += WM8994_SetVolume(pObj, VOLUME_OUTPUT, (uint8_t)pInit->Volume);
|
|
}
|
|
|
|
if (pInit->InputDevice != WM8994_IN_NONE) /* Audio input selected */
|
|
{
|
|
if ((pInit->InputDevice == WM8994_IN_MIC1) || (pInit->InputDevice == WM8994_IN_MIC2))
|
|
{
|
|
/* Enable Microphone bias 1 generator, Enable VMID */
|
|
tmp = 0x0013;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_1, &tmp, 2);
|
|
|
|
/* ADC oversample enable */
|
|
tmp = 0x0002;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_OVERSAMPLING, &tmp, 2);
|
|
|
|
/* AIF ADC2 HPF enable, HPF cut = voice mode 1 fc=127Hz at fs=8kHz */
|
|
tmp = 0x3800;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_ADC2_FILTERS, &tmp, 2);
|
|
}
|
|
else if(pInit->InputDevice == WM8994_IN_MIC1_MIC2)
|
|
{
|
|
/* Enable Microphone bias 1 generator, Enable VMID */
|
|
tmp = 0x0013;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_1, &tmp, 2);
|
|
|
|
/* ADC oversample enable */
|
|
tmp = 0x0002;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_OVERSAMPLING, &tmp, 2);
|
|
|
|
/* AIF ADC1 HPF enable, HPF cut = voice mode 1 fc=127Hz at fs=8kHz */
|
|
tmp = 0x1800;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_ADC1_FILTERS, &tmp, 2);
|
|
|
|
/* AIF ADC2 HPF enable, HPF cut = voice mode 1 fc=127Hz at fs=8kHz */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_ADC2_FILTERS, &tmp, 2);
|
|
}
|
|
else /* ((pInit->InputDevice == WM8994_IN_LINE1) || (pInit->InputDevice == WM8994_IN_LINE2)) */
|
|
{
|
|
/* Disable mute on IN1L, IN1L Volume = +0dB */
|
|
tmp = 0x000B;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_LEFT_LINE_IN12_VOL, &tmp, 2);
|
|
|
|
/* Disable mute on IN1R, IN1R Volume = +0dB */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_RIGHT_LINE_IN12_VOL, &tmp, 2);
|
|
|
|
/* AIF ADC1 HPF enable, HPF cut = voice mode 1 fc=127Hz at fs=8kHz */
|
|
tmp = 0x1800;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_ADC1_FILTERS, &tmp, 2);
|
|
}
|
|
/* Volume Control */
|
|
ret += WM8994_SetVolume(pObj, VOLUME_INPUT, (uint8_t)pInit->Volume);
|
|
}
|
|
|
|
if(ret != WM8994_OK)
|
|
{
|
|
ret = WM8994_ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Deinitializes the audio codec.
|
|
* @param pObj pointer to component object
|
|
* @retval Component status
|
|
*/
|
|
int32_t WM8994_DeInit(WM8994_Object_t *pObj)
|
|
{
|
|
/* De-Initialize Audio Codec interface */
|
|
return WM8994_Stop(pObj, WM8994_PDWN_HW);
|
|
}
|
|
|
|
/**
|
|
* @brief Get the WM8994 ID.
|
|
* @param pObj pointer to component object
|
|
* @param Id component ID
|
|
* @retval Component status
|
|
*/
|
|
int32_t WM8994_ReadID(WM8994_Object_t *pObj, uint32_t *Id)
|
|
{
|
|
int32_t ret;
|
|
uint16_t wm8994_id;
|
|
|
|
/* Initialize the Control interface of the Audio Codec */
|
|
pObj->IO.Init();
|
|
/* Get ID from component */
|
|
ret = wm8994_sw_reset_r(&pObj->Ctx, &wm8994_id);
|
|
|
|
*Id = wm8994_id;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Start the audio Codec play feature.
|
|
* @note For this codec no Play options are required.
|
|
* @param pObj pointer to component object
|
|
* @retval Component status
|
|
*/
|
|
int32_t WM8994_Play(WM8994_Object_t *pObj)
|
|
{
|
|
/* Resumes the audio file playing */
|
|
/* Unmute the output first */
|
|
return WM8994_SetMute(pObj, WM8994_MUTE_OFF);
|
|
}
|
|
|
|
/**
|
|
* @brief Pauses playing on the audio codec.
|
|
* @param pObj pointer to component object
|
|
* @retval Component status
|
|
*/
|
|
int32_t WM8994_Pause(WM8994_Object_t *pObj)
|
|
{
|
|
int32_t ret;
|
|
uint16_t tmp = 0x0001;
|
|
|
|
/* Pause the audio file playing */
|
|
/* Mute the output first */
|
|
if(WM8994_SetMute(pObj, WM8994_MUTE_ON) != WM8994_OK)
|
|
{
|
|
ret = WM8994_ERROR;
|
|
}/* Put the Codec in Power save mode */
|
|
else if(wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_2, &tmp, 2) != WM8994_OK)
|
|
{
|
|
ret = WM8994_ERROR;
|
|
}
|
|
else
|
|
{
|
|
ret = WM8994_OK;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Resumes playing on the audio codec.
|
|
* @param pObj pointer to component object
|
|
* @retval Component status
|
|
*/
|
|
int32_t WM8994_Resume(WM8994_Object_t *pObj)
|
|
{
|
|
/* Resumes the audio file playing */
|
|
/* Unmute the output first */
|
|
return WM8994_SetMute(pObj, WM8994_MUTE_OFF);
|
|
}
|
|
|
|
/**
|
|
* @brief Stops audio Codec playing. It powers down the codec.
|
|
* @param pObj pointer to component object
|
|
* @param CodecPdwnMode selects the power down mode.
|
|
* - WM8994_PDWN_SW: only mutes the audio codec. When resuming from this
|
|
* mode the codec keeps the previous initialization
|
|
* (no need to re-Initialize the codec registers).
|
|
* - WM8994_PDWN_HW: Physically power down the codec. When resuming from this
|
|
* mode, the codec is set to default configuration
|
|
* (user should re-Initialize the codec in order to
|
|
* play again the audio stream).
|
|
* @retval 0 if correct communication, else wrong communication
|
|
*/
|
|
int32_t WM8994_Stop(WM8994_Object_t *pObj, uint32_t CodecPdwnMode)
|
|
{
|
|
int32_t ret;
|
|
uint16_t tmp;
|
|
|
|
/* Mute the output first */
|
|
ret = WM8994_SetMute(pObj, WM8994_MUTE_ON);
|
|
|
|
if (CodecPdwnMode == WM8994_PDWN_SW)
|
|
{
|
|
/* Only output mute required*/
|
|
}
|
|
else /* WM8994_PDWN_HW */
|
|
{
|
|
tmp = 0x0200;
|
|
/* Mute the AIF1 Timeslot 0 DAC1 path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_FILTER1, &tmp, 2);
|
|
|
|
/* Mute the AIF1 Timeslot 1 DAC2 path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_FILTER1, &tmp, 2);
|
|
|
|
tmp = 0x0000;
|
|
/* Disable DAC1L_TO_HPOUT1L */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_OUTPUT_MIXER_1, &tmp, 2);
|
|
|
|
/* Disable DAC1R_TO_HPOUT1R */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_OUTPUT_MIXER_2, &tmp, 2);
|
|
|
|
/* Disable DAC1 and DAC2 */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_5, &tmp, 2);
|
|
|
|
/* Reset Codec by writing in 0x0000 address register */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_SW_RESET, &tmp, 2);
|
|
}
|
|
|
|
if(ret != WM8994_OK)
|
|
{
|
|
ret = WM8994_ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set higher or lower the codec volume level.
|
|
* @param pObj pointer to component object
|
|
* @param InputOutput Input or Output volume
|
|
* @param Volume a byte value from 0 to 63 for output and from 0 to 240 for input
|
|
* (refer to codec registers description for more details).
|
|
* @retval Component status
|
|
*/
|
|
int32_t WM8994_SetVolume(WM8994_Object_t *pObj, uint32_t InputOutput, uint8_t Volume)
|
|
{
|
|
int32_t ret;
|
|
uint16_t tmp;
|
|
|
|
/* Output volume */
|
|
if (InputOutput == VOLUME_OUTPUT)
|
|
{
|
|
if(Volume > 0x3EU)
|
|
{
|
|
/* Unmute audio codec */
|
|
ret = WM8994_SetMute(pObj, WM8994_MUTE_OFF);
|
|
tmp = 0x3FU | 0x140U;
|
|
|
|
/* Left Headphone Volume */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_LEFT_OUTPUT_VOL, &tmp, 2);
|
|
|
|
/* Right Headphone Volume */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_RIGHT_OUTPUT_VOL, &tmp, 2);
|
|
|
|
/* Left Speaker Volume */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_SPK_LEFT_VOL, &tmp, 2);
|
|
|
|
/* Right Speaker Volume */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_SPK_RIGHT_VOL, &tmp, 2);
|
|
}
|
|
else if (Volume == 0U)
|
|
{
|
|
/* Mute audio codec */
|
|
ret = WM8994_SetMute(pObj, WM8994_MUTE_ON);
|
|
}
|
|
else
|
|
{
|
|
/* Unmute audio codec */
|
|
ret = WM8994_SetMute(pObj, WM8994_MUTE_OFF);
|
|
|
|
tmp = Volume | 0x140U;
|
|
|
|
/* Left Headphone Volume */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_LEFT_OUTPUT_VOL, &tmp, 2);
|
|
|
|
/* Right Headphone Volume */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_RIGHT_OUTPUT_VOL, &tmp, 2);
|
|
|
|
/* Left Speaker Volume */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_SPK_LEFT_VOL, &tmp, 2);
|
|
|
|
/* Right Speaker Volume */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_SPK_RIGHT_VOL, &tmp, 2);
|
|
}
|
|
}
|
|
else /* Input volume: VOLUME_INPUT */
|
|
{
|
|
tmp = Volume | 0x100U;
|
|
|
|
/* Left AIF1 ADC1 volume */
|
|
ret = wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_ADC1_LEFT_VOL, &tmp, 2);
|
|
|
|
/* Right AIF1 ADC1 volume */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_ADC1_RIGHT_VOL, &tmp, 2);
|
|
|
|
/* Left AIF1 ADC2 volume */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_ADC2_LEFT_VOL, &tmp, 2);
|
|
|
|
/* Right AIF1 ADC2 volume */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_ADC2_RIGHT_VOL, &tmp, 2);
|
|
}
|
|
|
|
if(ret != WM8994_OK)
|
|
{
|
|
ret = WM8994_ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Get higher or lower the codec volume level.
|
|
* @param pObj pointer to component object
|
|
* @param InputOutput Input or Output volume
|
|
* @param Volume audio volume
|
|
* @retval Component status
|
|
*/
|
|
int32_t WM8994_GetVolume(WM8994_Object_t *pObj, uint32_t InputOutput, uint8_t *Volume)
|
|
{
|
|
int32_t ret = WM8994_OK;
|
|
uint16_t invertedvol;
|
|
|
|
/* Output volume */
|
|
if (InputOutput == VOLUME_OUTPUT)
|
|
{
|
|
if(wm8994_lo_hpout1l_vol_r(&pObj->Ctx, &invertedvol) != WM8994_OK)
|
|
{
|
|
ret = WM8994_ERROR;
|
|
}
|
|
else
|
|
{
|
|
*Volume = VOLUME_OUT_INVERT(invertedvol);
|
|
}
|
|
}
|
|
else /* Input volume: VOLUME_INPUT */
|
|
{
|
|
if(wm8994_aif1_adc1_left_vol_adc1l_r(&pObj->Ctx, &invertedvol) != WM8994_OK)
|
|
{
|
|
ret = WM8994_ERROR;
|
|
}
|
|
else
|
|
{
|
|
*Volume = VOLUME_IN_INVERT(invertedvol);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Enables or disables the mute feature on the audio codec.
|
|
* @param pObj pointer to component object
|
|
* @param Cmd WM8994_MUTE_ON to enable the mute or WM8994_MUTE_OFF to disable the
|
|
* mute mode.
|
|
* @retval 0 if correct communication, else wrong communication
|
|
*/
|
|
int32_t WM8994_SetMute(WM8994_Object_t *pObj, uint32_t Cmd)
|
|
{
|
|
int32_t ret;
|
|
uint16_t tmp;
|
|
|
|
/* Set the Mute mode */
|
|
if(Cmd == WM8994_MUTE_ON)
|
|
{
|
|
tmp = 0x0200;
|
|
/* Soft Mute the AIF1 Timeslot 0 DAC1 path L&R */
|
|
ret = wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_FILTER1, &tmp, 2);
|
|
|
|
/* Soft Mute the AIF1 Timeslot 1 DAC2 path L&R */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_FILTER1, &tmp, 2);
|
|
}
|
|
else /* WM8994_MUTE_OFF Disable the Mute */
|
|
{
|
|
tmp = 0x0010;
|
|
/* Unmute the AIF1 Timeslot 0 DAC1 path L&R */
|
|
ret = wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_FILTER1, &tmp, 2);
|
|
|
|
/* Unmute the AIF1 Timeslot 1 DAC2 path L&R */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_FILTER1, &tmp, 2);
|
|
}
|
|
|
|
if(ret != WM8994_OK)
|
|
{
|
|
ret = WM8994_ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Switch dynamically (while audio file is played) the output target
|
|
* (speaker or headphone).
|
|
* @param pObj pointer to component object
|
|
* @param Output specifies the audio output target: WM8994_OUT_SPEAKER,
|
|
* WM8994_OUT_HEADPHONE, WM8994_OUT_BOTH or WM8994_OUT_AUTO
|
|
* @retval 0 if correct communication, else wrong communication
|
|
*/
|
|
int32_t WM8994_SetOutputMode(WM8994_Object_t *pObj, uint32_t Output)
|
|
{
|
|
int32_t ret;
|
|
uint16_t tmp;
|
|
|
|
if((Output == WM8994_OUT_HEADPHONE) || (Output == WM8994_OUT_AUTO))
|
|
{
|
|
/* Disable bias generator, Enable VMID, Enable SPKOUTL, Enable SPKOUTR */
|
|
tmp = 0x0000;
|
|
ret = wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_1, &tmp, 2);
|
|
|
|
/* Disable DAC1 (Left), Disable DAC1 (Right),
|
|
Enable DAC2 (Left), Enable DAC2 (Right)*/
|
|
tmp = 0x0303;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_5, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path */
|
|
tmp = 0x0001;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_LMR, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_RMR, &tmp, 2);
|
|
|
|
/* Disable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */
|
|
tmp = 0x0000;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_LMR, &tmp, 2);
|
|
|
|
/* Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_RMR, &tmp, 2);
|
|
|
|
/* Select DAC1 (Left) to Left Headphone Output PGA (HPOUT1LVOL) path */
|
|
tmp = 0x0100;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_OUTPUT_MIXER_1, &tmp, 2);
|
|
|
|
/* Select DAC1 (Right) to Right Headphone Output PGA (HPOUT1RVOL) path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_OUTPUT_MIXER_2, &tmp, 2);
|
|
|
|
/* Startup sequence for Headphone */
|
|
/* Enable/Start the write sequencer */
|
|
tmp = 0x8100;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_WRITE_SEQ_CTRL1, &tmp, 2);
|
|
|
|
/* Add Delay */
|
|
(void)WM8994_Delay(pObj, 300);
|
|
|
|
/* Soft un-Mute the AIF1 Timeslot 0 DAC1 path L&R */
|
|
tmp = 0x0000;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_FILTER1, &tmp, 2);
|
|
}
|
|
else
|
|
{
|
|
switch (Output)
|
|
{
|
|
case WM8994_OUT_SPEAKER:
|
|
/* Enable DAC1 (Left), Enable DAC1 (Right),
|
|
Disable DAC2 (Left), Disable DAC2 (Right)*/
|
|
tmp = 0x0C0C;
|
|
ret = wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_5, &tmp, 2);
|
|
|
|
/* Disable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path */
|
|
tmp = 0x0000;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_LMR, &tmp, 2);
|
|
|
|
/* Disable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_RMR, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */
|
|
tmp = 0x0002;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_LMR, &tmp, 2);
|
|
|
|
/* Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_RMR, &tmp, 2);
|
|
break;
|
|
|
|
case WM8994_OUT_BOTH:
|
|
default:
|
|
/* Enable DAC1 (Left), Enable DAC1 (Right),
|
|
also Enable DAC2 (Left), Enable DAC2 (Right)*/
|
|
tmp = 0x0F0F;
|
|
ret = wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_5, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path */
|
|
tmp = 0x0001;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_LMR, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC1_RMR, &tmp, 2);
|
|
|
|
/* Enable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */
|
|
tmp = 0x0002;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_DAC2_LMR, &tmp, 2);
|
|
break;
|
|
}
|
|
|
|
/* Enable SPKRVOL PGA, Enable SPKMIXR, Enable SPKLVOL PGA, Enable SPKMIXL */
|
|
tmp = 0x0300;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_3, &tmp, 2);
|
|
|
|
/* Left Speaker Mixer Volume = 0dB */
|
|
tmp = 0x0000;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_SPKMIXL_ATT, &tmp, 2);
|
|
|
|
/* Speaker output mode = Class D, Right Speaker Mixer Volume = 0dB ((0x23, 0x0100) = class AB)*/
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_SPKMIXR_ATT, &tmp, 2);
|
|
|
|
/* Unmute DAC2 (Left) to Left Speaker Mixer (SPKMIXL) path,
|
|
Unmute DAC2 (Right) to Right Speaker Mixer (SPKMIXR) path */
|
|
tmp = 0x0300;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_SPEAKER_MIXER, &tmp, 2);
|
|
|
|
/* Enable bias generator, Enable VMID, Enable SPKOUTL, Enable SPKOUTR */
|
|
tmp = 0x3003;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_1, &tmp, 2);
|
|
/* Headphone/Speaker Enable */
|
|
|
|
/* Enable Class W, Class W Envelope Tracking = AIF1 Timeslot 0 */
|
|
tmp = 0x0005;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_CLASS_W, &tmp, 2);
|
|
|
|
/* Enable bias generator, Enable VMID, Enable HPOUT1 (Left) and Enable HPOUT1 (Right) input stages */
|
|
/* idem for Speaker */
|
|
tmp = 0x3303;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_1, &tmp, 2);
|
|
|
|
/* Enable HPOUT1 (Left) and HPOUT1 (Right) intermediate stages */
|
|
tmp = 0x0022;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_ANALOG_HP, &tmp, 2);
|
|
|
|
/* Enable Charge Pump */
|
|
tmp = 0x9F25;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_CHARGE_PUMP1, &tmp, 2);
|
|
|
|
/* Add Delay */
|
|
(void)WM8994_Delay(pObj, 15);
|
|
|
|
/* Select DAC1 (Left) to Left Headphone Output PGA (HPOUT1LVOL) path */
|
|
tmp = 0x0001;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_OUTPUT_MIXER_1, &tmp, 2);
|
|
|
|
/* Select DAC1 (Right) to Right Headphone Output PGA (HPOUT1RVOL) path */
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_OUTPUT_MIXER_2, &tmp, 2);
|
|
|
|
/* Enable Left Output Mixer (MIXOUTL), Enable Right Output Mixer (MIXOUTR) */
|
|
/* idem for SPKOUTL and SPKOUTR */
|
|
tmp = 0x0330;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_PWR_MANAGEMENT_3, &tmp, 2);
|
|
|
|
/* Enable DC Servo and trigger start-up mode on left and right channels */
|
|
tmp = 0x0033;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_DC_SERVO1, &tmp, 2);
|
|
|
|
/* Add Delay */
|
|
(void)WM8994_Delay(pObj, 257);
|
|
|
|
/* Enable HPOUT1 (Left) and HPOUT1 (Right) intermediate and output stages. Remove clamps */
|
|
tmp = 0x00EE;
|
|
ret += wm8994_write_reg(&pObj->Ctx, WM8994_ANALOG_HP, &tmp, 2);
|
|
}
|
|
|
|
if(ret != WM8994_OK)
|
|
{
|
|
ret = WM8994_ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set Audio resolution.
|
|
* @param pObj pointer to component object
|
|
* @param Resolution Audio resolution. Can be:
|
|
* WM8994_RESOLUTION_16b, WM8994_RESOLUTION_20b,
|
|
* WM8994_RESOLUTION_24b or WM8994_RESOLUTION_32b
|
|
* @retval Component status
|
|
*/
|
|
int32_t WM8994_SetResolution(WM8994_Object_t *pObj, uint32_t Resolution)
|
|
{
|
|
int32_t ret = WM8994_OK;
|
|
|
|
if(wm8994_aif1_control1_wl(&pObj->Ctx, (uint16_t)Resolution) != WM8994_OK)
|
|
{
|
|
ret = WM8994_ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Get Audio resolution.
|
|
* @param pObj pointer to component object
|
|
* @retval Audio resolution
|
|
*/
|
|
int32_t WM8994_GetResolution(WM8994_Object_t *pObj, uint32_t *Resolution)
|
|
{
|
|
int32_t ret = WM8994_OK;
|
|
uint16_t resolution = 0;
|
|
|
|
if(wm8994_aif1_control1_wl_r(&pObj->Ctx, &resolution) != WM8994_OK)
|
|
{
|
|
ret = WM8994_ERROR;
|
|
}
|
|
else
|
|
{
|
|
switch(resolution)
|
|
{
|
|
case 0:
|
|
*Resolution = WM8994_RESOLUTION_16b;
|
|
break;
|
|
case 1:
|
|
*Resolution = WM8994_RESOLUTION_20b;
|
|
break;
|
|
case 2:
|
|
*Resolution = WM8994_RESOLUTION_24b;
|
|
break;
|
|
case 3:
|
|
*Resolution = WM8994_RESOLUTION_32b;
|
|
break;
|
|
default:
|
|
*Resolution = WM8994_RESOLUTION_16b;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set Audio Protocol.
|
|
* @param pObj pointer to component object
|
|
* @param Protocol Audio Protocol. Can be:
|
|
* WM8994_PROTOCOL_R_JUSTIFIED, WM8994_PROTOCOL_L_JUSTIFIED,
|
|
* WM8994_PROTOCOL_I2S or WM8994_PROTOCOL_DSP
|
|
* @retval Component status
|
|
*/
|
|
int32_t WM8994_SetProtocol(WM8994_Object_t *pObj, uint32_t Protocol)
|
|
{
|
|
int32_t ret = WM8994_OK;
|
|
|
|
if(wm8994_aif1_control1_fmt(&pObj->Ctx, (uint16_t)Protocol) != WM8994_OK)
|
|
{
|
|
ret = WM8994_ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Get Audio Protocol.
|
|
* @param pObj pointer to component object
|
|
* @retval Component status
|
|
*/
|
|
int32_t WM8994_GetProtocol(WM8994_Object_t *pObj, uint32_t *Protocol)
|
|
{
|
|
int32_t ret = WM8994_OK;
|
|
uint16_t protocol;
|
|
|
|
if(wm8994_aif1_control1_fmt_r(&pObj->Ctx, &protocol) != WM8994_OK)
|
|
{
|
|
ret = WM8994_ERROR;
|
|
}
|
|
else
|
|
{
|
|
*Protocol = protocol;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Sets new frequency.
|
|
* @param pObj pointer to component object
|
|
* @param AudioFreq Audio frequency
|
|
* @retval Component status
|
|
*/
|
|
int32_t WM8994_SetFrequency(WM8994_Object_t *pObj, uint32_t AudioFreq)
|
|
{
|
|
int32_t ret;
|
|
uint16_t tmp;
|
|
|
|
switch (AudioFreq)
|
|
{
|
|
case WM8994_FREQUENCY_8K:
|
|
/* AIF1 Sample Rate = 8 (KHz), ratio=256 */
|
|
tmp = 0x0003;
|
|
ret = wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_RATE, &tmp, 2);
|
|
break;
|
|
|
|
case WM8994_FREQUENCY_16K:
|
|
/* AIF1 Sample Rate = 16 (KHz), ratio=256 */
|
|
tmp = 0x0033;
|
|
ret = wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_RATE, &tmp, 2);
|
|
break;
|
|
|
|
case WM8994_FREQUENCY_32K:
|
|
/* AIF1 Sample Rate = 32 (KHz), ratio=256 */
|
|
tmp = 0x0063;
|
|
ret = wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_RATE, &tmp, 2);
|
|
break;
|
|
|
|
case WM8994_FREQUENCY_96K:
|
|
/* AIF1 Sample Rate = 96 (KHz), ratio=256 */
|
|
tmp = 0x00A3;
|
|
ret = wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_RATE, &tmp, 2);
|
|
break;
|
|
|
|
case WM8994_FREQUENCY_11K:
|
|
/* AIF1 Sample Rate = 11.025 (KHz), ratio=256 */
|
|
tmp = 0x0013;
|
|
ret = wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_RATE, &tmp, 2);
|
|
break;
|
|
|
|
case WM8994_FREQUENCY_22K:
|
|
/* AIF1 Sample Rate = 22.050 (KHz), ratio=256 */
|
|
tmp = 0x0043;
|
|
ret = wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_RATE, &tmp, 2);
|
|
break;
|
|
|
|
case WM8994_FREQUENCY_44K:
|
|
/* AIF1 Sample Rate = 44.1 (KHz), ratio=256 */
|
|
tmp = 0x0073;
|
|
ret = wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_RATE, &tmp, 2);
|
|
break;
|
|
|
|
case WM8994_FREQUENCY_48K:
|
|
default:
|
|
/* AIF1 Sample Rate = 48 (KHz), ratio=256 */
|
|
tmp = 0x0083;
|
|
ret = wm8994_write_reg(&pObj->Ctx, WM8994_AIF1_RATE, &tmp, 2);
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Get frequency.
|
|
* @param pObj pointer to component object
|
|
* @param AudioFreq Audio frequency
|
|
* @retval Component status
|
|
*/
|
|
int32_t WM8994_GetFrequency(WM8994_Object_t *pObj, uint32_t *AudioFreq)
|
|
{
|
|
int32_t ret = WM8994_OK;
|
|
uint16_t freq = 0;
|
|
|
|
if(wm8994_aif1_sr_r(&pObj->Ctx, &freq) != WM8994_OK)
|
|
{
|
|
ret = WM8994_ERROR;
|
|
}
|
|
else
|
|
{
|
|
switch(freq)
|
|
{
|
|
case 0:
|
|
*AudioFreq = WM8994_FREQUENCY_8K;
|
|
break;
|
|
case 1:
|
|
*AudioFreq = WM8994_FREQUENCY_11K;
|
|
break;
|
|
case 3:
|
|
*AudioFreq = WM8994_FREQUENCY_16K;
|
|
break;
|
|
case 4:
|
|
*AudioFreq = WM8994_FREQUENCY_22K;
|
|
break;
|
|
case 6:
|
|
*AudioFreq = WM8994_FREQUENCY_32K;
|
|
break;
|
|
case 7:
|
|
*AudioFreq = WM8994_FREQUENCY_44K;
|
|
break;
|
|
case 8:
|
|
*AudioFreq = WM8994_FREQUENCY_48K;
|
|
break;
|
|
case 10:
|
|
*AudioFreq = WM8994_FREQUENCY_96K;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Resets wm8994 registers.
|
|
* @param pObj pointer to component object
|
|
* @retval Component status if correct communication, else wrong communication
|
|
*/
|
|
int32_t WM8994_Reset(WM8994_Object_t *pObj)
|
|
{
|
|
int32_t ret = WM8994_OK;
|
|
|
|
/* Reset Codec by writing in 0x0000 address register */
|
|
if(wm8994_sw_reset_w(&pObj->Ctx, 0x0000) != WM8994_OK)
|
|
{
|
|
ret = WM8994_ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/******************** Static functions ****************************************/
|
|
/**
|
|
* @brief Function
|
|
* @param Component object pointer
|
|
* @retval error status
|
|
*/
|
|
int32_t WM8994_RegisterBusIO (WM8994_Object_t *pObj, WM8994_IO_t *pIO)
|
|
{
|
|
int32_t ret;
|
|
|
|
if (pObj == NULL)
|
|
{
|
|
ret = WM8994_ERROR;
|
|
}
|
|
else
|
|
{
|
|
pObj->IO.Init = pIO->Init;
|
|
pObj->IO.DeInit = pIO->DeInit;
|
|
pObj->IO.Address = pIO->Address;
|
|
pObj->IO.WriteReg = pIO->WriteReg;
|
|
pObj->IO.ReadReg = pIO->ReadReg;
|
|
pObj->IO.GetTick = pIO->GetTick;
|
|
|
|
pObj->Ctx.ReadReg = WM8994_ReadRegWrap;
|
|
pObj->Ctx.WriteReg = WM8994_WriteRegWrap;
|
|
pObj->Ctx.handle = pObj;
|
|
|
|
if(pObj->IO.Init != NULL)
|
|
{
|
|
ret = pObj->IO.Init();
|
|
}
|
|
else
|
|
{
|
|
ret = WM8994_ERROR;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function provides accurate delay (in milliseconds)
|
|
* @param pObj pointer to component object
|
|
* @param Delay: specifies the delay time length, in milliseconds
|
|
* @retval Component status
|
|
*/
|
|
static int32_t WM8994_Delay(WM8994_Object_t *pObj, uint32_t Delay)
|
|
{
|
|
uint32_t tickstart;
|
|
|
|
tickstart = pObj->IO.GetTick();
|
|
while((pObj->IO.GetTick() - tickstart) < Delay)
|
|
{
|
|
}
|
|
return WM8994_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Function
|
|
* @param handle Component object handle
|
|
* @param Reg The target register address to write
|
|
* @param pData The target register value to be written
|
|
* @param Length buffer size to be written
|
|
* @retval error status
|
|
*/
|
|
static int32_t WM8994_ReadRegWrap(void *handle, uint16_t Reg, uint8_t* pData, uint16_t Length)
|
|
{
|
|
WM8994_Object_t *pObj = (WM8994_Object_t *)handle;
|
|
|
|
return pObj->IO.ReadReg(pObj->IO.Address, Reg, pData, Length);
|
|
}
|
|
|
|
/**
|
|
* @brief Function
|
|
* @param handle Component object handle
|
|
* @param Reg The target register address to write
|
|
* @param pData The target register value to be written
|
|
* @param Length buffer size to be written
|
|
* @retval error status
|
|
*/
|
|
static int32_t WM8994_WriteRegWrap(void *handle, uint16_t Reg, uint8_t* pData, uint16_t Length)
|
|
{
|
|
WM8994_Object_t *pObj = (WM8994_Object_t *)handle;
|
|
|
|
return pObj->IO.WriteReg(pObj->IO.Address, Reg, pData, Length);
|
|
}
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
|
|