STM32H7 code for virtual analog synthesiser
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

/**
******************************************************************************
* @file wm8994.c
* @author MCD Application Team
* @brief This file provides the WM8994 Audio Codec driver.
******************************************************************************
* @attention
*
* <h2><center>&copy; 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****/