📄 hw_ac97.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
// -----------------------------------------------------------------------------
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// -----------------------------------------------------------------------------
//
// hw_ac97.cpp
//
// functions for controlling hardware features of the AC97 Codec
//
#include "es1371.h"
#include <wavedbg.h>
#define STR_MODULENAME "CES1371: "
// AC_Delay: stall for t milliseconds: dwDelay < t <= dwDelay + 1
void
AC_Delay (DWORD dwDelay)
{
DWORD dwStart = GetTickCount();
while ((GetTickCount() - dwStart) <= dwDelay);
}
void
CES1371::InitCodec()
{
USHORT usCodecRead;
UCHAR i;
// write the Codec reset
WriteCodecRegister( AC97_RESET, 0xffff );
// read all the registers so we sync the states
for ( i = AC97_RESET; i <= AC97_POWER_CONTROL; i += 2)
usCodecRead = ReadCodecRegister( i );
// get the Vendor ID and revision
m_ulCodecVendorID = ULONG(ReadCodecRegister( AC97_VENDOR_ID1 )) << 16;
usCodecRead = ReadCodecRegister( AC97_VENDOR_ID2 );
m_ulCodecVendorID |= ULONG(usCodecRead) & 0x0000ff00;
m_ulCodecRevision = ULONG(usCodecRead) & 0x000000ff;
DEBUGMSG(ZONE_ERROR, (TEXT("ES1371 Codec Vendor ID: %08x\n"), m_ulCodecVendorID));
DEBUGMSG(ZONE_ERROR, (TEXT("ES1371 Codec Revision: %02x\n"), m_ulCodecRevision));
if ( !( m_ulCodecVendorID == AC97_VENDOR_CRYSTAL && m_ulCodecRevision == 0x13))
{
// now power down the Analog section of the AC97
// and power it back up. This forces the Crystal
// AC97 to recalibrate its analog levels.
Codec_SetPowerState( AC97_PWR_ANLOFF );
//<mod:ce> KeStallExecutionProcessor (100);
AC_Delay(1);
Codec_SetPowerState( AC97_PWR_D0 );
}
// write the wave out volume
WriteCodecRegister( AC97_PCMOUT_VOL, 0x0808 ); // 0dB Gain
// all the other analog inputs (Line In, CD, etc. are left in the default muted state)
// all the record settings are left at default - higher-level code sets this later on
// turn on the mic boost but leave it muted
WriteCodecRegister( AC97_MIC_VOL, 0x8048 );
// and unmute it
WriteCodecRegister( AC97_MASTER_VOL_MONO, 0x0000 );
}
void
CES1371::WriteCodecRegister(UCHAR Reg, USHORT Val)
{
ULONG i;
// ULONG dtemp, dinit;
// Don't write if the value is the same as
// what is in the register but always let writes
// to the reset register go through.
if ( (m_usCRegs[Reg/2] == Val) &&
(AC97_RESET != Reg) )
return;
#ifdef DBG
_DbgPrintF( DEBUGLVL_VERBOSE, ("[CodecWrite] Reg %x Val %x", Reg, Val));
#endif
/* wait for WIP to go away */
for( i = 0; i < 0x1000UL; ++i )
if( !(READ_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dCODECCTL_OFF)) & (1UL << 30)) )
break;
// /* save the current state for later */
// dinit = READ_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dSRCIO_OFF));
//
// /* enable SRC state data in SRC mux */
// for( i = 0; i < 0x1000UL; ++i )
// if( !((dtemp = READ_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dSRCIO_OFF))) & SRC_BUSY) )
// break;
// WRITE_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dSRCIO_OFF), (dtemp & SRC_CTLMASK) | 0x00010000UL);
//
// /* wait for a SAFE time to write addr/data and then do it, dammit */
//// _disable();
// for( i = 0; i < 0x1000UL; ++i )
// if( (READ_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dSRCIO_OFF)) & 0x00870000UL) ==
// 0x00010000UL )
// break;
WRITE_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dCODECCTL_OFF),
((ULONG) Reg << 16) | Val);
//// _enable();
//
// /* restore SRC reg */
// for( i = 0; i < 0x1000UL; ++i )
// if( !(READ_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dSRCIO_OFF)) & SRC_BUSY) )
// break;
// WRITE_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dSRCIO_OFF), dinit & SRC_CTLMASK );
/* store the written value in our local storage */
m_usCRegs[Reg/2] = Val;
return;
}
USHORT
CES1371::ReadCodecRegister( UCHAR Reg )
{
ULONG i, dtemp;
// ULONG dinit;
/* wait for WIP to go away saving the current state for later */
for( i = 0; i < 0x1000UL; ++i )
if( !(READ_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dCODECCTL_OFF)) & (1UL << 30)) )
break;
/* write addr w/data=0 and assert read request ... */
// /* save the current state for later */
// dinit = READ_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dSRCIO_OFF));
//
// /* enable SRC state data in SRC mux */
// for( i = 0; i < 0x1000UL; ++i )
// if( !((dtemp = READ_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dSRCIO_OFF))) & SRC_BUSY) )
// break;
// WRITE_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dSRCIO_OFF), (dtemp & SRC_CTLMASK) | 0x00010000UL);
//
// /* wait for a SAFE time to write a read request and then do it, dammit */
//// _disable();
// for( i = 0; i < 0x1000UL; ++i )
// if( (READ_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dSRCIO_OFF)) & 0x00870000UL) ==
// 0x00010000UL )
// break;
//
WRITE_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dCODECCTL_OFF),
((ULONG) Reg << 16) | (1UL << 23));
// _enable();
// /* restore SRC reg */
// for( i = 0; i < 0x1000UL; ++i )
// if( !(READ_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dSRCIO_OFF)) & SRC_BUSY) )
// break;
// WRITE_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dSRCIO_OFF), dinit & SRC_CTLMASK );
//
/* now wait for the data (RDY) */
for( i = 0; i < 0x1000UL; ++i )
if( READ_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dCODECCTL_OFF)) & (1UL << 31) )
break;
dtemp = READ_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dCODECCTL_OFF));
/* store the read value in our local storage */
m_usCRegs[Reg/2] = (USHORT) dtemp;
return (USHORT) dtemp;
}
USHORT
CES1371::GetCodecRegisterValue( UCHAR Reg )
{
return m_usCRegs[Reg/2];
}
void
CES1371::Codec_SaveRegisterState( USHORT *pusRegisters )
{
//<mod:ce> RtlCopyMemory( (VOID *)pusRegisters, (VOID *)m_usCRegs, 80 );
memcpy( (VOID *)pusRegisters, (VOID *)m_usCRegs, 80 );
}
void
CES1371::Codec_RestoreRegisterState( USHORT *pusRegisters )
{
UCHAR i;
for ( i = AC97_MASTER_VOL_STEREO; i <= AC97_3D_CONTROL; i+=2 )
WriteCodecRegister( i, pusRegisters[i/2] );
}
//--------------------------------------------------------------------------
//
// Codec_SetPowerState
// Description:
// The AC97 Codec has 4 power states (that are meaningful to us):
// 0x0000 - normal operation
// 0x1700 - fully powered down
// AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR4
// 0x1300 - powered down with analog mixer active
// AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR4
// 0x0800 - turn off analog mixer section
// AC97_PWR_PR3
//
// This procedure will transition the Codec from its current
// state to the specified one.
//
// Input:
// ulNewState - the new AC97 power state
//
//--------------------------------------------------------------------------
ULONG
CES1371::Codec_SetPowerState( ULONG ulNewState )
{
USHORT usAC97_PowerReg;
ULONG delay_count;
// if the AC97 is asleep wake it up. since we only leave it asleep
// or fully awake if it isn't awake it must be asleep
if ( m_ulCodecPowerState )
{
UCHAR ucMiscCtl;
// wake up the AC97 using the warm reset method
// from 5.2.1.2 AC97 spec a warm reset is signaled by
// driving SYNC high for a minimum of one microsecond
// in the absence of bit clock. Do this using the
// SYNC_RES bit in byte 1 of the es1371 chip.
if ( AC97_PWR_PR4 & m_ulCodecPowerState )
{
ucMiscCtl = READ_PORT_UCHAR((PUCHAR)(m_pPciAddr + ES1371_bMISCCTL_OFF));
WRITE_PORT_UCHAR((PUCHAR)(m_pPciAddr + ES1371_bMISCCTL_OFF),
ucMiscCtl | ES1371_MISCCTL_SYNC_RES );
// now wait around long enough to be sure 1.3 microseconds have gone by
//<mod:ce> KeStallExecutionProcessor( 2 );
AC_Delay(1);
WRITE_PORT_UCHAR((PUCHAR)(m_pPciAddr + ES1371_bMISCCTL_OFF),
ucMiscCtl & ~ES1371_MISCCTL_SYNC_RES );
// now wait around long enough to be sure a microsecond has gone by
//<mod:ce> KeStallExecutionProcessor( 1 );
AC_Delay(1);
}
// now the ACLink should be active. take the Codec through the steps
// to bring it back up to fully awake.
// read the power register to see what the current state is
usAC97_PowerReg = ReadCodecRegister( AC97_POWER_CONTROL );
#ifdef DBG
_DbgPrintF( DEBUGLVL_VERBOSE, ("AC97 power state 1: %x", usAC97_PowerReg));
#endif
// is the analog section awake
if ( !(AC97_PWR_ANL & usAC97_PowerReg ) )
WriteCodecRegister( AC97_POWER_CONTROL, usAC97_PowerReg & ~(AC97_PWR_PR2|AC97_PWR_PR3) );
Codec_WaitForPowerState( AC97_PWR_ANL );
// read the power register to see what the current state is
usAC97_PowerReg = ReadCodecRegister( AC97_POWER_CONTROL );
#ifdef DBG
_DbgPrintF( DEBUGLVL_VERBOSE, ("AC97 power state 2: %x", usAC97_PowerReg));
#endif
// is the DAC section awake
if ( !(AC97_PWR_DAC & usAC97_PowerReg ) )
WriteCodecRegister( AC97_POWER_CONTROL, usAC97_PowerReg & ~AC97_PWR_PR1 );
Codec_WaitForPowerState( AC97_PWR_DAC );
// read the power register to see what the current state is
usAC97_PowerReg = ReadCodecRegister( AC97_POWER_CONTROL );
#ifdef DBG
_DbgPrintF( DEBUGLVL_VERBOSE, ("AC97 power state 3: %x", usAC97_PowerReg));
#endif
// is the ADC section awake
if ( !(AC97_PWR_ADC & usAC97_PowerReg ) )
WriteCodecRegister( AC97_POWER_CONTROL, usAC97_PowerReg & ~AC97_PWR_PR0 );
Codec_WaitForPowerState( AC97_PWR_ADC );
// read the power register to see what the current state is
usAC97_PowerReg = ReadCodecRegister( AC97_POWER_CONTROL );
#ifdef DBG
_DbgPrintF( DEBUGLVL_VERBOSE, ("AC97 power state 3: %x", usAC97_PowerReg));
#endif
}
// now the AC97 Codec if fully awake. if it should be in a power down
// mode do that now.
if ( ulNewState )
{
// start the process of shutting down the Codec
usAC97_PowerReg = 0;
// shut down the ADC section
if ( AC97_PWR_PR0 & ulNewState )
{
usAC97_PowerReg |= AC97_PWR_PR0;
WriteCodecRegister( AC97_POWER_CONTROL, usAC97_PowerReg );
}
// shut down the DAC section
if ( AC97_PWR_PR1 & ulNewState )
{
usAC97_PowerReg |= AC97_PWR_PR1;
WriteCodecRegister( AC97_POWER_CONTROL, usAC97_PowerReg );
}
// if it is called for shut off the analog section
if ( AC97_PWR_PR2 & ulNewState )
{
usAC97_PowerReg |= AC97_PWR_PR2;
WriteCodecRegister( AC97_POWER_CONTROL, usAC97_PowerReg );
}
// if it is called for shut off the analog section
if ( AC97_PWR_PR3 & ulNewState )
{
usAC97_PowerReg |= AC97_PWR_PR3;
WriteCodecRegister( AC97_POWER_CONTROL, usAC97_PowerReg );
}
if ( AC97_PWR_PR4 & ulNewState )
{
// shut down the digital interface section
usAC97_PowerReg |= AC97_PWR_PR4;
WriteCodecRegister( AC97_POWER_CONTROL, usAC97_PowerReg );
}
// poll the WIP bit of the Codec control register to make sure
// it is clear before we power anything else down.
for( delay_count = 0; delay_count < 0x1000UL; ++delay_count )
if( !(READ_PORT_ULONG((PULONG)(m_pPciAddr + ES1371_dCODECCTL_OFF)) & (1UL << 30)) )
break;
// the AC97 Codec is now powered down
}
// save the new AC97 Codec power state
m_ulCodecPowerState = ulNewState;
return ulNewState;
}
void
CES1371::Codec_WaitForPowerState( USHORT usState )
{
ULONG i;
for (i=0; i<0x100000; ++i)
{
if ( ReadCodecRegister ( AC97_POWER_CONTROL ) & usState ) {
return;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -