DevCodec.cpp revision c58f1213e628a545081c70e26c6b67a841cff880
/* $Id$ */
/** @file
* DevCodec - VBox ICH Intel HD Audio Codec.
*/
/*
* Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DEV_AUDIO
#include "VBoxDD.h"
extern "C" {
#include "audio.h"
}
#include "DevCodec.h"
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
#define CODECNODE_F0_PARAM_LENGTH 0x14
#define CODECNODE_F02_PARAM_LENGTH 16
typedef struct CODECCOMMONNODE
{
/** The node name. */
char const *pszName;
/* RPM 5.3.6 */
AssertCompile(sizeof(a_Node) - sizeof(CODECCOMMONNODE) == (((a_cParams) * sizeof(uint32_t) + sizeof(void *) - 1) & ~(sizeof(void *) - 1)))
typedef struct ROOTCODECNODE
{
#define AMPLIFIER_SIZE 60
#define AMPLIFIER_IN 0
#define AMPLIFIER_OUT 1
#define AMPLIFIER_LEFT 1
#define AMPLIFIER_RIGHT 0
typedef struct DACNODE
{
typedef struct ADCNODE
{
typedef struct SPDIFOUTNODE
{
typedef struct SPDIFINNODE
{
} SPDIFINNODE, *PSPDIFINNODE;
typedef struct AFGCODECNODE
{
typedef struct PORTNODE
{
typedef struct DIGOUTNODE
{
} DIGOUTNODE, *PDIGOUTNODE;
typedef struct DIGINNODE
{
} DIGINNODE, *PDIGINNODE;
typedef struct ADCMUXNODE
{
} ADCMUXNODE, *PADCMUXNODE;
typedef struct PCBEEPNODE
{
} PCBEEPNODE, *PPCBEEPNODE;
typedef struct CDNODE
{
typedef struct VOLUMEKNOBNODE
{
typedef struct ADCVOLNODE
{
} ADCVOLNODE, *PADCVOLNODE;
typedef struct RESNODE
{
/**
* Used for the saved state.
*/
typedef struct CODECSAVEDSTATENODE
{
typedef union CODECNODE
{
} CODECNODE, *PCODECNODE;
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/* STAC9220 - Referenced thru STAC9220WIDGET in the constructor below. */
/** SSM description of a CODECNODE. */
static SSMFIELD const g_aCodecNodeFields[] =
{
};
/** Backward compatibility with v1 of the CODECNODE. */
static SSMFIELD const g_aCodecNodeFieldsV1[] =
{
};
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
{
return VINF_SUCCESS;
}
{
switch (nodenum)
{
/* Root Node*/
case 0:
break;
case 1:
| CODEC_F00_0C_CAP_IMPENDANCE_SENSE;//(17 << 8)|RT_BIT(6)|RT_BIT(5)|RT_BIT(2)|RT_BIT(1)|RT_BIT(0);
pNode->node.au32F00_param[0xD] = CODEC_MAKE_F00_0D(1, 0x5, 0xE, 0);//RT_BIT(31)|(0x5 << 16)|(0xE)<<8;
pNode->afg.u32F05_param = CODEC_MAKE_F05(0, 0, 0, CODEC_F05_D2, CODEC_F05_D2);//0x2 << 4| 0x2; /* PS-Act: D3, PS->Set D3 */
break;
case 2:
case 3:
case 4:
case 5:
pNode->dac.u32A_param = CODEC_MAKE_A(0, 1, CODEC_A_MULT_1X, CODEC_A_DIV_1X, CODEC_A_16_BIT, 1);//RT_BIT(14)|(0x1 << 4)|0x1; /* 441000Hz/16bit/2ch */
| CODEC_F00_09_CAP_LSB;//(0xD << 16) | RT_BIT(11) | RT_BIT(10) | RT_BIT(2) | RT_BIT(0);
pNode->dac.u32F05_param = CODEC_MAKE_F05(0, 0, 0, CODEC_F05_D3, CODEC_F05_D3);//0x3 << 4 | 0x3; /* PS-Act: D3, Set: D3 */
break;
case 6:
goto adc_init;
case 7:
pNode->adc.u32A_param = CODEC_MAKE_A(0, 1, CODEC_A_MULT_1X, CODEC_A_DIV_1X, CODEC_A_16_BIT, 1);//RT_BIT(14)|(0x1 << 3)|0x1; /* 441000Hz/16bit/2ch */
pNode->adc.u32F05_param = CODEC_MAKE_F05(0, 0, 0, CODEC_F05_D3, CODEC_F05_D3);//0x3 << 4 | 0x3; /* PS-Act: D3 Set: D3 */
| CODEC_F00_09_CAP_LSB;//RT_BIT(20)| (0xd << 16) | RT_BIT(10) | RT_BIT(8) | RT_BIT(6)| RT_BIT(0);
break;
case 8:
pNode->spdifout.u32A_param = CODEC_MAKE_A(0, 1, CODEC_A_MULT_1X, CODEC_A_DIV_1X, CODEC_A_16_BIT, 1);//(1<<14)|(0x1<<4) | 0x1;
| CODEC_F00_09_CAP_LSB;//(4 << 16) | RT_BIT(9)|RT_BIT(4)|0x1;
break;
case 9:
pNode->spdifin.u32A_param = CODEC_MAKE_A(0, 1, CODEC_A_MULT_1X, CODEC_A_DIV_1X, CODEC_A_16_BIT, 1);//(0x1<<4) | 0x1;
| CODEC_F00_09_CAP_LSB;//(0x1 << 20)|(4 << 16) | RT_BIT(9)| RT_BIT(8)|RT_BIT(4)|0x1;
break;
case 0xA:
| CODEC_F00_0C_CAP_IMPENDANCE_SENSE;//0x173f;
0x2, 0);//RT_MAKE_U32_FROM_U8(0x20, 0x40, 0x21, 0x02);
goto port_init;
case 0xB:
| CODEC_F00_0C_CAP_IMPENDANCE_SENSE;//0x1737;
0x1, 0x1);//RT_MAKE_U32_FROM_U8(0x11, 0x60, 0x11, 0x01);
goto port_init;
case 0xC:
| CODEC_F00_0C_CAP_IMPENDANCE_SENSE;//0x1737;
0x0, 0x1, 0x0);//RT_MAKE_U32_FROM_U8(0x10, 0x40, 0x11, 0x01);
goto port_init;
case 0xD:
| CODEC_F00_0C_CAP_IMPENDANCE_SENSE;//0x1737;
0x0, 0x5, 0x0);//RT_MAKE_U32_FROM_U8(0x50, 0x90, 0xA1, 0x02); /* Microphone */
| CODEC_F00_09_CAP_LSB;//(4 << 20)|RT_BIT(8)|RT_BIT(7)|RT_BIT(0);
break;
case 0xE:
| CODEC_F00_09_CAP_LSB;//(4 << 20)|RT_BIT(7)|RT_BIT(0);
| CODEC_F00_0C_CAP_PRESENSE_DETECT;//0x34;
0x0, 0x4, 0x0);//0x01013040; /* Line Out */
break;
case 0xF:
| CODEC_F00_09_CAP_LSB;//(4 << 20)|RT_BIT(8)|RT_BIT(7)|RT_BIT(2)|RT_BIT(0);
/* | CODEC_F00_0C_CAP_TRIGGER_REQUIRED
| CODEC_F00_0C_CAP_IMPENDANCE_SENSE */;//0x37;
0x0, 0x1, 0x2);//RT_MAKE_U32_FROM_U8(0x12, 0x60, 0x11, 0x01);
break;
case 0x10:
| CODEC_F00_09_CAP_LSB;//(4<<20)|RT_BIT(9)|RT_BIT(8)|RT_BIT(0);
0x0, 0x3, 0x0);//RT_MAKE_U32_FROM_U8(0x30, 0x10, 0x45, 0x01);
break;
case 0x11:
| CODEC_F00_0C_CAP_PRESENSE_DETECT;//RT_BIT(16)| RT_BIT(5)|RT_BIT(2);
pNode->digin.u32F05_param = CODEC_MAKE_F05(0, 0, 0, CODEC_F05_D3, CODEC_F05_D3);//0x3 << 4 | 0x3; /* PS-Act: D3 -> D3 */
0x0, 0x6, 0x0);//(0x1 << 24) | (0xc5 << 16) | (0x10 << 8) | 0x60;
break;
case 0x12:
goto adcmux_init;
case 0x13:
| CODEC_F00_09_CAP_LSB;//(3<<20)|RT_BIT(8)|RT_BIT(3)|RT_BIT(2)|RT_BIT(0);
/* STAC 9220 v10 6.21-22.{4,5} both(left and right) out amplefiers inited with 0*/
break;
case 0x14:
| CODEC_F00_09_CAP_OUT_AMP_PRESENT;//(7 << 20) | RT_BIT(3) | RT_BIT(2);
break;
case 0x15:
| CODEC_F00_09_CAP_LSB;//(4 << 20)|RT_BIT(0);
0x0, 0x7, 0x0);//RT_MAKE_U32_FROM_U8(0x70, 0x0, 0x33, 0x90);
break;
case 0x16:
pNode->node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_VOLUME_KNOB, 0x0, 0x0);//(0x6 << 20);
break;
case 0x17:
goto adcvol_init;
case 0x18:
| CODEC_F00_09_CAP_LSB;//(0x3 << 20)|RT_BIT(11)|RT_BIT(8)|RT_BIT(1)|RT_BIT(0);
break;
case 0x19:
| CODEC_F00_09_CAP_LSB;//(0xF << 20)|(0x3 << 16)|RT_BIT(9)|RT_BIT(0);
break;
case 0x1A:
| CODEC_F00_09_CAP_LSB;//(0x3 << 16)|RT_BIT(9)|RT_BIT(0);
break;
case 0x1B:
| CODEC_F00_09_CAP_LSB;//(0x4 << 20)|RT_BIT(9)|RT_BIT(8)|RT_BIT(0);
0x0, 0x0, 0xf);//0x4000000f;
break;
default:
break;
}
return VINF_SUCCESS;
}
/* generic */
#define DECLISNODEOFTYPE(type) \
{ \
return 1; \
return 0; \
}
/* codecIsPortNode */
/* codecIsDacNode */
/* codecIsAdcVolNode */
/* codecIsAdcNode */
/* codecIsAdcMuxNode */
/* codecIsPcbeepNode */
/* codecIsSpdifOutNode */
/* codecIsSpdifInNode */
/* codecIsDigInPinNode */
/* codecIsDigOutPinNode */
/* codecIsCdNode */
/* codecIsVolKnobNode */
/* codecIsReservedNode */
static inline void codecSetRegister(uint32_t *pu32Reg, uint32_t u32Cmd, uint8_t u8Offset, uint32_t mask)
{
}
{
}
{
}
{
*pResp = 0;
return VINF_SUCCESS;
}
{
int rc;
return rc;
}
/* B-- */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
/* HDA spec 7.3.3.7 Note A */
/* @todo: if index out of range response should be 0 */
u8Index);
u8Index);
u8Index);
u8Index);
u8Index);
u8Index);
else{
AssertMsgReturn(0, ("access to fields of %x need to be implemented\n", CODEC_NID(cmd)), VINF_SUCCESS);
}
return VINF_SUCCESS;
}
/* 3-- */
{
bool fIsLeft = false;
bool fIsRight = false;
bool fIsOut = false;
bool fIsIn = false;
{
return VINF_SUCCESS;
}
*pResp = 0;
if (pAmplifier)
{
return VINF_SUCCESS;
if (fIsIn)
{
if (fIsLeft)
codecSetRegisterU8(&LIFIER_REGISTER(*pAmplifier, AMPLIFIER_IN, AMPLIFIER_LEFT, u8Index), cmd, 0);
if (fIsRight)
codecSetRegisterU8(&LIFIER_REGISTER(*pAmplifier, AMPLIFIER_IN, AMPLIFIER_RIGHT, u8Index), cmd, 0);
}
if (fIsOut)
{
if (fIsLeft)
codecSetRegisterU8(&LIFIER_REGISTER(*pAmplifier, AMPLIFIER_OUT, AMPLIFIER_LEFT, u8Index), cmd, 0);
if (fIsRight)
codecSetRegisterU8(&LIFIER_REGISTER(*pAmplifier, AMPLIFIER_OUT, AMPLIFIER_RIGHT, u8Index), cmd, 0);
}
}
return VINF_SUCCESS;
}
{
{
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
}
*pResp = 0;
return VINF_SUCCESS;
}
/* F01 */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
return VINF_SUCCESS;
}
/* 701 */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
if (pu32Reg)
return VINF_SUCCESS;
}
/* F07 */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
else
AssertMsgFailed(("Unsupported"));
return VINF_SUCCESS;
}
/* 707 */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
if (pu32Reg)
return VINF_SUCCESS;
}
/* F08 */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
else
return VINF_SUCCESS;
}
/* 708 */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
else
if(pu32Reg)
return VINF_SUCCESS;
}
/* F09 */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
else
return VINF_SUCCESS;
}
/* 709 */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
if(pu32Reg)
return VINF_SUCCESS;
}
{
*pResp = 0;
{
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
}
return VINF_SUCCESS;
}
/* F03 */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
return VINF_SUCCESS;
}
/* 703 */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
{
}
return VINF_SUCCESS;
}
/* F0D */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
return VINF_SUCCESS;
}
static int codecSetDigitalConverter(struct CODECState *pState, uint32_t cmd, uint8_t u8Offset, uint64_t *pResp)
{
{
return VINF_SUCCESS;
}
*pResp = 0;
return VINF_SUCCESS;
}
/* 70D */
{
}
/* 70E */
{
}
/* F20 */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
{
}
return VINF_SUCCESS;
}
{
{
return VINF_SUCCESS;
}
if (pu32Reg)
return VINF_SUCCESS;
}
/* 720 */
{
*pResp = 0;
}
/* 721 */
{
*pResp = 0;
}
/* 722 */
{
*pResp = 0;
}
/* 723 */
{
*pResp = 0;
}
{
&& pState->pfnCodecNodeReset)
{
uint8_t i;
Log(("HDAcodec: enters reset\n"));
for (i = 0; i < pState->cTotalNodes; ++i)
{
}
Log(("HDAcodec: exits reset\n"));
}
*pResp = 0;
return VINF_SUCCESS;
}
/* F05 */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
return VINF_SUCCESS;
}
/* 705 */
{
if (!pu32F05_param)
return;
}
{
{
return VINF_SUCCESS;
}
*pResp = 0;
if (!pu32Reg)
return VINF_SUCCESS;
{
/*
* We shouldn't propogate actual power state, which actual for AFG
*/
CODEC_F05_SET(cmd));
}
/* Propagate next power state only if AFG is on or verb modifies AFG power state */
{
{
/* now we're powered on AFG and may propogate power states on nodes */
while (*(++pu8NodeIndex))
while (*(++pu8NodeIndex))
while (*(++pu8NodeIndex))
}
}
return VINF_SUCCESS;
}
{
{
return VINF_SUCCESS;
}
*pResp = 0;
return VINF_SUCCESS;
}
{
{
return VINF_SUCCESS;
}
*pResp = 0;
*pResp = 0;
if (pu32addr)
return VINF_SUCCESS;
}
{
{
return VINF_SUCCESS;
}
*pResp = 0;
return VINF_SUCCESS;
}
{
{
return VINF_SUCCESS;
}
*pResp = 0;
return VINF_SUCCESS;
}
/* F0C */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
return VINF_SUCCESS;
}
/* 70C */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
*pResp = 0;
if (pu32Reg)
return VINF_SUCCESS;
}
/* F0F */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
return VINF_SUCCESS;
}
/* 70F */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
if (pu32Reg)
return VINF_SUCCESS;
}
/* F17 */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
/* note: this is true for ALC885 */
return VINF_SUCCESS;
}
/* 717 */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
if (pu32Reg)
return VINF_SUCCESS;
}
/* F1C */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
return VINF_SUCCESS;
}
{
{
return VINF_SUCCESS;
}
if (pu32Reg)
return VINF_SUCCESS;
}
/* 71C */
{
*pResp = 0;
}
/* 71D */
{
*pResp = 0;
}
/* 71E */
{
*pResp = 0;
}
/* 71E */
{
*pResp = 0;
}
{
switch (mt)
{
case AUD_MIXER_VOLUME:
case AUD_MIXER_PCM:
dir = AMPLIFIER_OUT;
break;
case AUD_MIXER_LINE_IN:
dir = AMPLIFIER_IN;
break;
}
mute >>=7;
mute &= 0x1;
return VINF_SUCCESS;
}
static CODECVERB CODECVERBS[] =
{
/* verb | verb mask | callback */
/* ----------- -------------------- ----------------------- */
};
{
int rc = VINF_SUCCESS;
{
}
if ( CODEC_VERBDATA(cmd) == 0
{
//** @todo r=michaln: There needs to be a counter to avoid log flooding (see e.g. DevRTC.cpp)
return VINF_SUCCESS;
}
{
{
return VINF_SUCCESS;
}
}
return rc;
}
{
}
{
}
/**
*
* routines open one of the voices (IN, OUT) with corresponding parameters.
*
* @todo: probably passed settings should be verified (if AFG's declared proposed format) before enabling.
*/
int codecOpenVoice(CODECState *pState, ENMSOUNDSOURCE enmSoundSource, audsettings_t *pAudioSettings)
{
int rc = 0;
if ( !pState
|| !pAudioSettings)
return -1;
switch (enmSoundSource)
{
case PI_INDEX:
pState->SwVoiceIn = AUD_open_in(&pState->card, pState->SwVoiceIn, "hda.in", pState, pi_callback, pAudioSettings);
break;
case PO_INDEX:
pState->SwVoiceOut = AUD_open_out(&pState->card, pState->SwVoiceOut, "hda.out", pState, po_callback, pAudioSettings);
break;
default:
return -1;
}
if (!rc)
LogRel(("HDAcodec: can't open %s fmt(freq: %d)\n",
pAudioSettings->freq));
return rc;
}
{
int rc;
/* common root node initializers */
pState->pNodes[0].node.au32F00_param[0] = CODEC_MAKE_F00_00(pState->u16VendorId, pState->u16DeviceId);
/* common AFG node initializers */
pState->pNodes[1].afg.u32F20_param = CODEC_MAKE_F20(pState->u16VendorId, pState->u8BSKU, pState->u8AssemblyId);
//** @todo r=michaln: Was this meant to be 'HDA' or something like that? (AC'97 was on ICH0)
/* 44.1 kHz */
as.endianness = 0;
uint8_t i;
for (i = 0; i < pState->cTotalNodes; ++i)
{
}
/* If no host voices were created, then fallback to nul audio. */
LogRel (("HDA: WARNING: Unable to open PCM IN!\n"));
LogRel (("HDA: WARNING: Unable to open PCM OUT!\n"));
{
/* Was not able initialize *any* voice. Select the NULL audio driver instead */
AUD_init_null ();
N_ ("No audio devices could be opened. Selecting the NULL audio backend "
"with the consequence that no sound is audible"));
}
{
char szMissingVoices[128];
len += RTStrPrintf (szMissingVoices + len, sizeof(szMissingVoices) - len, len ? ", PCM_out" : "PCM_out");
N_ ("Some audio devices (%s) could not be opened. Guest applications generating audio "
"output or depending on audio input may hang. Make sure your host audio device "
"is working properly. Check the logfile for error messages of the audio "
"subsystem"), szMissingVoices);
}
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
}
{
AssertLogRelMsgReturn(pCodecState->cTotalNodes == 0x1c, ("cTotalNodes=%#x, should be 0x1c", pCodecState->cTotalNodes),
SSMR3PutStructEx(pSSM, &pCodecState->pNodes[idxNode].SavedState, sizeof(pCodecState->pNodes[idxNode].SavedState),
return VINF_SUCCESS;
}
{
switch (uVersion)
{
case HDA_SSM_VERSION_1:
break;
case HDA_SSM_VERSION_2:
case HDA_SSM_VERSION_3:
break;
case HDA_SSM_VERSION:
{
if (cNodes != 0x1c)
fFlags = 0;
break;
}
default:
}
{
if (RT_FAILURE(rc))
return rc;
}
/*
* Update stuff after changing the state.
*/
codecToAudVolume(&pCodecState->pNodes[pCodecState->u8DacLineOut].spdifout.B_params, AUD_MIXER_VOLUME);
codecToAudVolume(&pCodecState->pNodes[pCodecState->u8AdcVolsLineIn].adcvol.B_params, AUD_MIXER_LINE_IN);
return VINF_SUCCESS;
}