DevCodec.cpp revision ad27e1d5e48ca41245120c331cc88b50464813ce
/* $Id$ */
/** @file
* DevCodec - VBox ICH Intel HD Audio Codec.
*/
/*
* Copyright (C) 2006-2008 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.
*/
#define LOG_GROUP LOG_GROUP_DEV_AUDIO
#include "../Builtins.h"
extern "C" {
#include "audio.h"
}
#include "DevCodec.h"
#define CODEC_CAD_MASK 0xF0000000
#define CODEC_CAD_SHIFT 28
#define CODEC_NID_MASK 0x07F00000
#define CODEC_NID_SHIFT 20
#define CODEC_VERBDATA_MASK 0x000FFFFF
#define CODEC_VERB_4BIT_CMD 0x000FFFF0
#define CODEC_VERB_4BIT_DATA 0x0000000F
#define CODEC_VERB_8BIT_CMD 0x000FFF00
#define CODEC_VERB_8BIT_DATA 0x000000FF
#define CODEC_VERB_16BIT_CMD 0x000F0000
#define CODEC_VERB_16BIT_DATA 0x0000FFFF
#define CODEC_VERB_GET_AMP_INDEX 0x7
/* HDA spec 7.3.3.7 NoteA */
#define CODEC_GET_AMP_INDEX(cmd) (CODEC_GET_AMP_DIRECTION(cmd) ? 0 : ((cmd) & CODEC_VERB_GET_AMP_INDEX))
/* HDA spec 7.3.3.7 NoteC */
/* STAC9220 */
{
return VINF_SUCCESS;
}
{
switch (nodenum)
{
/* Root Node*/
case 0:
//** @todo r=michaln: I fear the use of RT_MAKE_U32_FROM_U8() here makes the
// code much harder to read, not easier.
pNode->node.au32F00_param[0] = RT_MAKE_U32_FROM_U8(0x80, 0x76, 0x84, 0x83); /* VendorID = STAC9220/ DevId = 0x7680 */
pNode->node.au32F00_param[4] = RT_MAKE_U32_FROM_U8(0x1, 0x00, 0x01, 0x00); /* node info (start node: 1, start id = 1) */
break;
case 1:
break;
case 2:
goto dac_init;
case 3:
goto dac_init;
case 4:
goto dac_init;
case 5:
break;
case 6:
goto adc_init;
case 7:
pNode->adc.node.au32F00_param[9] = RT_BIT(20)| (0xd << 16) | RT_BIT(10) | RT_BIT(8) | RT_BIT(6)| RT_BIT(0);
break;
case 8:
break;
case 9:
break;
case 0xA:
goto port_init;
case 0xB:
goto port_init;
case 0xC:
goto port_init;
case 0xD:
break;
case 0xE:
break;
case 0xF:
break;
case 0x10:
/* STAC9220 spec defines default connection list containing reserved nodes, that confuses some drivers. */
break;
case 0x11:
break;
case 0x12:
goto adcmux_init;
case 0x13:
/* STAC 9220 v10 6.21-22.{4,5} both(left and right) out amplefiers inited with 0*/
break;
case 0x14:
break;
case 0x15:
break;
case 0x16:
break;
case 0x17:
goto adcvol_init;
case 0x18:
break;
case 0x19:
break;
case 0x1A:
break;
case 0x1B:
break;
default:
break;
}
return VINF_SUCCESS;
}
/* ALC885 */
{
/* @todo: test more */
return VINF_SUCCESS;
}
{
switch (nodenum)
{
case 0: /* Root */
break;
case 0x1: /* AFG */
break;
/* DACs */
case 0x2:
goto dac_init;
case 0x3:
goto dac_init;
case 0x4:
goto dac_init;
case 0x5:
goto dac_init;
case 0x25:
break;
/* SPDIFs */
case 0x6:
break;
case 0xA:
break;
/* VENDOR DEFINE */
case 0x10:
goto vendor_define_init;
case 0x11:
goto vendor_define_init;
case 0x12:
goto vendor_define_init;
case 0x13:
goto vendor_define_init;
case 0x20:
break;
/* DIGPIN */
case 0x1E:
/* N = 0~3 */
break;
case 0x1F:
/* N = 0~3 */
break;
/* ADCs */
case 0x7:
goto adc_init;
break;
case 0x8:
goto adc_init;
break;
case 0x9:
break;
/* Ports */
case 0x14:
pNode->node.au32F00_param[0xC] = RT_BIT(13)|RT_BIT(12)|RT_BIT(11)|RT_BIT(10)|RT_BIT(9)|RT_BIT(8)|RT_BIT(5)|RT_BIT(4)|RT_BIT(3)|RT_BIT(2);
goto port_init;
break;
case 0x15:
pNode->node.au32F00_param[0xC] = RT_BIT(13)|RT_BIT(12)|RT_BIT(11)|RT_BIT(10)|RT_BIT(9)|RT_BIT(8)|RT_BIT(5)|RT_BIT(4)|RT_BIT(3)|RT_BIT(2);
goto port_init;
break;
case 0x16:
goto port_init;
break;
case 0x17:
goto port_init;
break;
case 0x18:
pNode->node.au32F00_param[0xC] = RT_BIT(13)|RT_BIT(12)|RT_BIT(11)|RT_BIT(10)|RT_BIT(9)|RT_BIT(8)|RT_BIT(5)|RT_BIT(4)|RT_BIT(3)|RT_BIT(2);
goto port_init;
break;
case 0x19:
pNode->node.au32F00_param[0xC] = RT_BIT(13)|RT_BIT(12)|RT_BIT(11)|RT_BIT(10)|RT_BIT(9)|RT_BIT(8)|RT_BIT(5)|RT_BIT(4)|RT_BIT(3)|RT_BIT(2);
goto port_init;
break;
case 0x1A:
pNode->node.au32F00_param[0xC] = RT_BIT(13)|RT_BIT(12)|RT_BIT(11)|RT_BIT(10)|RT_BIT(9)|RT_BIT(8)|RT_BIT(5)|RT_BIT(4)|RT_BIT(3)|RT_BIT(2);
goto port_init;
break;
case 0x1B:
pNode->node.au32F00_param[0xC] = RT_BIT(13)|RT_BIT(12)|RT_BIT(11)|RT_BIT(10)|RT_BIT(9)|RT_BIT(8)|RT_BIT(5)|RT_BIT(4)|RT_BIT(3)|RT_BIT(2);
/* N = 0~3 */
/* N = 4~7 */
break;
/* ADCVols */
case 0x26:
break;
case 0xF:
break;
case 0xE:
break;
case 0xD:
break;
case 0xC:
break;
case 0xB:
/* N = 0~3 */
/* N = 4~7 */
/* N = 8~11 */
break;
/* AdcMuxs */
case 0x22:
goto adc_mux_init;
case 0x23:
/* N = 0~3 */
/* N = 4~7 */
/* N = 8~11 */
break;
case 0x24:
/* N = 0~3 */
/* N = 4~7 */
/* N = 8~11 */
break;
/* PCBEEP */
case 0x1D:
break;
/* CD */
case 0x1C:
break;
case 0x21:
break;
default:
AssertMsgFailed(("Unsupported Node"));
}
return VINF_SUCCESS;
}
/* generic */
#define CODEC_POWER_MASK 0x3
#define CODEC_POWER_ACT_SHIFT (4)
#define CODEC_POWER_SET_SHIFT (0)
#define CODEC_POWER_D0 (0)
#define CODEC_POWER_D1 (1)
#define CODEC_POWER_D2 (2)
#define CODEC_POWER_D3 (3)
#define CODEC_POWER_PROPOGATE_STATE(node) \
do { \
}while(0)
#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;
}
{
{
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 */
{
{
return VINF_SUCCESS;
}
*pResp = 0;
if (!pu32Reg)
return VINF_SUCCESS;
{
*pu32Reg &= ~CODEC_VERB_8BIT_DATA;
}
else
*pu32Reg &= ~CODEC_VERB_4BIT_DATA;
/* Propagate next power state only if AFG is on or verb modifies AFG power state */
{
{
}
}
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;
}
{
paud->endianness = 0;
return VINF_SUCCESS;
}
{
}
{
}
{
}
{
int rc;
switch (enmCodec)
{
case STAC9220_CODEC:
break;
case ALC885_CODEC:
break;
default:
AssertMsgFailed(("Unsupported Codec"));
}
uint8_t i;
for (i = 0; i < pState->cTotalNodes; ++i)
{
}
//** @todo r=michaln: Was this meant to be 'HDA' or something like that? (AC'97 was on ICH0)
/* 44.1 kHz */
as.endianness = 0;
do{ \
AUDIO_FORMAT_SELECTOR(pState, Out, (base), (mult), (div)), name ".out", (pState), (out_callback), &(as)); \
AUDIO_FORMAT_SELECTOR(pState, In, (base), (mult), (div)), name ".in", (pState), (in_callback), &(as)); \
} while(0)
#define IS_FORMAT_SUPPORTED_BY_HOST(pState, base, mult, div) (AUDIO_FORMAT_SELECTOR((pState), Out, (base), (mult), (div)) \
SETUP_AUDIO_FORMAT(pState, AFMT_HZ_44_1K, AFMT_MULT_X1, AFMT_DIV_X1, "hda44_1", as, pi_callback, po_callback);
pState->pNodes[1].node.au32F00_param[0xA] |= IS_FORMAT_SUPPORTED_BY_HOST(pState, AFMT_HZ_44_1K, AFMT_MULT_X1, AFMT_DIV_X1) ? RT_BIT(5) : 0;
SETUP_AUDIO_FORMAT(pState, AFMT_HZ_44_1K, AFMT_MULT_X2, AFMT_DIV_X1, "hda44_1_2x", as, pi_callback, po_callback);
pState->pNodes[1].node.au32F00_param[0xA] |= IS_FORMAT_SUPPORTED_BY_HOST(pState, AFMT_HZ_44_1K, AFMT_MULT_X2, AFMT_DIV_X1) ? RT_BIT(7) : 0;
SETUP_AUDIO_FORMAT(pState, AFMT_HZ_44_1K, AFMT_MULT_X4, AFMT_DIV_X1, "hda44_1_4x", as, pi_callback, po_callback);
pState->pNodes[1].node.au32F00_param[0xA] |= IS_FORMAT_SUPPORTED_BY_HOST(pState, AFMT_HZ_44_1K, AFMT_MULT_X4, AFMT_DIV_X1) ? RT_BIT(9) : 0;
SETUP_AUDIO_FORMAT(pState, AFMT_HZ_48K, AFMT_MULT_X1, AFMT_DIV_X1, "hda48", as, pi_callback, po_callback);
pState->pNodes[1].node.au32F00_param[0xA] |= IS_FORMAT_SUPPORTED_BY_HOST(pState, AFMT_HZ_48K, AFMT_MULT_X1, AFMT_DIV_X1) ? RT_BIT(6) : 0;
# if 0
SETUP_AUDIO_FORMAT(pState, AFMT_HZ_48K, AFMT_MULT_X2, AFMT_DIV_X1, "hda48_2x", as, pi_callback, po_callback);
pState->pNodes[1].node.au32F00_param[0xA] |= IS_FORMAT_SUPPORTED_BY_HOST(pState, AFMT_HZ_48K, AFMT_MULT_X2, AFMT_DIV_X1) ? RT_BIT(8) : 0;
SETUP_AUDIO_FORMAT(pState, AFMT_HZ_48K, AFMT_MULT_X4, AFMT_DIV_X1, "hda48_4x", as, pi_callback, po_callback);
pState->pNodes[1].node.au32F00_param[0xA] |= IS_FORMAT_SUPPORTED_BY_HOST(pState, AFMT_HZ_48K, AFMT_MULT_X4, AFMT_DIV_X1) ? RT_BIT(10) : 0;
# endif
#endif
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
}
{
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;
}