📄 ac97codec.cpp
字号:
//**********************************************************************
//
// Filename: ac97audio.cpp
//
// Description: interface for the AC97 codec.
//
// 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.
//
// Use of this source code is subject to the terms of the Cirrus 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.
//
// Copyright(c) Cirrus Logic Corporation 2002, All Rights Reserved
//
//**********************************************************************
#include "wavecommon.h"
#include "ac97regs.h"
#include <hwdefs.h>
//
// Set the codec read timeout to 1ms
//
#define TIMEOUT_AC97_READ 1000
#define TIMEOUT_AC97_RESET 20000
//****************************************************************************
// AC97Codec::AC97Codec
//****************************************************************************
//
//
//
AC97Codec::AC97Codec(void)
{
}
//****************************************************************************
// AC97Codec::~AC97Codec
//****************************************************************************
//
//
//
AC97Codec::~AC97Codec(void)
{
}
//****************************************************************************
// AC97Codec::Initialize
//****************************************************************************
//
//
//
MMRESULT AC97Codec::Initialize(void )
{
ULONG ulRawIntStatus, ulSR;
BOOL b;
LARGE_INTEGER liStart, liCurrent;
//
// Write the following registers.
//
HalWriteCommonReg
(
CSC_DEVCFG,
DEVCFG_I2SONSSP | DEVCFG_I2SONAC97 | DEVCFG_A1ONG | DEVCFG_A2ONG,
0
);
//
// Initialize the AC97 controller to use channel 1.
//
m_puAC97Ch = AC97I_CH1_BASE;
//
// Enable the AC97 controller internal loopback. Override codec ready.
//
*AC97I_GCR = GCR_AC97IFE;
//
// Set the TIMEDRESET bit. Will cause a > 1uSec reset of the ac-link.
// This bit is self resetting.
//
*AC97I_RESET = RESET_TIMEDRESET;
Sleep(1);
//
// Wait for the codec ready signal from the AC97 codec.
//
b = QueryPerformanceCounter(&liStart);
ASSERT(b);
for(;;)
{
b = QueryPerformanceCounter(&liCurrent);
//
// Check to see if the codec is ready.
//
ulRawIntStatus = *AC97I_RGIS;
if(ulRawIntStatus & GCR_OCODECREADY)
break;
//
// See if we hit the timeout.
//
if(liStart.QuadPart + TIMEOUT_AC97_RESET <liCurrent.QuadPart)
{
ERRMSG((L"AAC: Codec interface Failed to Reset \r\n"));
return MMSYSERR_NOTENABLED;
}
}
//
// Set up the rx and tx channels
//
m_puAC97Ch[AC97I_RXCR>>2] = (m_ulRXCR = RXCR_CM | RXCR_RSIZE_16 | RXCR_SLOT3 | RXCR_SLOT4) | RXCR_REN;
m_puAC97Ch[AC97I_TXCR>>2] = (m_ulTXCR = TXCR_CM | TXCR_RSIZE_16 | TXCR_SLOT3 | TXCR_SLOT4) | TXCR_TEN;
//
// Wait until Rx fifo is no longer empty, which means that
// the codec is pumping ADC data across the AC-link.
//
b = QueryPerformanceCounter(&liStart);
ASSERT(b);
for(;;)
{
Sleep(2);
b = QueryPerformanceCounter(&liCurrent);
//
// Check to see if there is any data in the receive fifo.
//
ulSR = m_puAC97Ch[AC97I_SR>>2];
if(!(ulSR & SR_RXFE))
break;
if(liStart.QuadPart + TIMEOUT_AC97_RESET <liCurrent.QuadPart)
{
ERRMSG((L"AC97: Receive Interface Failed to Initialize.\r\n"));
return MMSYSERR_NOTENABLED;
}
}
//
// Disable rx and txchannel
//
m_puAC97Ch[AC97I_RXCR>>2] = m_ulRXCR;
m_puAC97Ch[AC97I_TXCR>>2] = m_ulTXCR;
PokeAC97(AC97_MASTER_VOLUME, 0x0);
PokeAC97(AC97_PCMOUT_VOLUME, 0x0);
//
// Set up the codec so that variable sample rates are enabled.
//
PokeAC97(AC97_EXT_AUDIO_POWER, EXT_AUDIO_POWER_VRA);
// PokeAC97(AC97_RECORD_SELECT, RECORD_SELECT_LINEIN);
// PokeAC97(AC97_RECORD_GAIN, 0x0);
//SetPlaybackSampleRate(44100,0);
//SetRecordSampleRate(44100,0);
//CaptureNPlay();
return MMSYSERR_NOERROR;
}
//****************************************************************************
// AC97Codec::SetVolume
//****************************************************************************
//
//
//
MMRESULT AC97Codec::SetVolume
(
ULONG ulRightVolume,
ULONG ulLeftVolume,
ULONG ulChannel
)
{
ULONG ulRight, ulLeft;
//
// Convert the volume to AC97 volume values.
//
// Input Codec Value db Value
// 0xFFFF 0 12.5db
// 0 0x1F -34.5db
// 1.5 db steps
//
ulRight = 0x1F - (ulRightVolume>>27);
ulLeft = 0x1F - (ulLeftVolume>>27);
//
// Writ to the AC97 codec register.
//
PokeAC97(AC97_PCMOUT_VOLUME, (ulRight << 8) | ulLeft);
return MMSYSERR_NOERROR;
}
//****************************************************************************
// AC97Codec::SetRecordSampleRate
//****************************************************************************
//
//
//
MMRESULT AC97Codec::SetRecordSampleRate(ULONG ulFrequency, ULONG ulChannel)
{
return PokeAC97(AC97_PCM_LR_ADC_RATE, ulFrequency);
}
//****************************************************************************
// AC97Codec::SetPlaybackSampleRate
//****************************************************************************
//
//
//
MMRESULT AC97Codec::SetPlaybackSampleRate(ULONG ulFrequency, ULONG ulChannel)
{
return PokeAC97(AC97_PCM_FRONT_DAC_RATE, ulFrequency);
}
//****************************************************************************
// AC97Codec::StartPlayback
//****************************************************************************
//
//
//
MMRESULT AC97Codec::StartPlayback(ULONG ulChannel)
{
m_puAC97Ch[AC97I_TXCR>>2] = m_ulTXCR | TXCR_TEN;
return MMSYSERR_NOERROR;
}
//****************************************************************************
// AC97Codec::StopPlayback
//****************************************************************************
//
//
//
MMRESULT AC97Codec::StopPlayback(ULONG ulChannel)
{
m_puAC97Ch[AC97I_TXCR>>2] = m_ulTXCR ;
return MMSYSERR_NOERROR;
}
//****************************************************************************
// AC97Codec::StartRecord
//****************************************************************************
//
//
//
MMRESULT AC97Codec::StartRecord(ULONG ulChannel)
{
m_puAC97Ch[AC97I_RXCR>>2] = m_ulRXCR | RXCR_REN;
return MMSYSERR_NOERROR;
}
//****************************************************************************
// AC97Codec::StopRecord
//****************************************************************************
//
//
//
MMRESULT AC97Codec::StopRecord(ULONG ulChannel)
{
m_puAC97Ch[AC97I_RXCR>>2] = m_ulRXCR;
return MMSYSERR_NOERROR;
}
//****************************************************************************
// AC97Codec::GetDmaPort
//****************************************************************************
//
//
//
ULONG AC97Codec::GetDmaPort(void)
{
return PRALLOC_AAC1;
}
//****************************************************************************
// PeekAC97
//****************************************************************************
// Read an AC97 codec Register.
//
// ulCodecReg - AC97 Codec Register
// pulValue - Codec Register value.
//
// Return MMSYSERROR_NOERROR
// MMSYSERROR_ERROR
//
MMRESULT AC97Codec::PeekAC97(UINT ulCodecReg, PULONG pulValue)
{
ULONG ulRGIS;
LARGE_INTEGER liStart, liCurrent;
BOOL b;
//
// How it is supposed to work is:
// - The ac97 controller sends out a read addr in slot 1.
// - In the next frame, the codec will echo that address back in slot 1
// and send the data in slot 2. SLOT2RXVALID will be set to 1.
//
// Read until SLOT2RXVALID goes to 1. Reading the data in AACS2DATA
// clears SLOT2RXVALID.
//
b = QueryPerformanceCounter(&liStart);
for(;;)
{
//
// Sleep for a little bit.
//
//Sleep(1);
b = QueryPerformanceCounter(&liCurrent);
//
// Check to see if there is any data in the receive fifo.
//
ulRGIS = *AC97I_RGIS;
if(ulRGIS & GIS_SLOT1TXCOMPLETE)
break;
if((liStart.QuadPart + TIMEOUT_AC97_READ) <liCurrent.QuadPart)
{
ERRMSG((L"PeekAC97 SLOT2TXVALID timed out. reg = 0x%02x.\r\n", ulCodecReg));
return MMSYSERR_ERROR;
}
}
//if( !(ulRGIS & GIS_SLOT2RXVALID) )
//{
// ERRMSG((L"PeekAC97: SLOT2RXVALID timed out, reg = 0x%02x..\n", ulCodecReg));
// return MMSYSERR_ERROR;
//}
//
// Write the address to AACS1DATA.
//
*AC97I_S1DATA = ulCodecReg;
b = QueryPerformanceCounter(&liStart);
for(;;)
{
//
// Sleep for a little bit.
//
// Sleep(1);
b = QueryPerformanceCounter(&liCurrent);
//
// Check to see if there is any data in the receive fifo.
//
ulRGIS = *AC97I_RGIS;
if((ulRGIS & (GIS_SLOT1TXCOMPLETE | GIS_SLOT2RXVALID)) ==
(GIS_SLOT1TXCOMPLETE | GIS_SLOT2RXVALID))
break;
if((liStart.QuadPart + TIMEOUT_AC97_READ) <liCurrent.QuadPart)
{
ERRMSG((L"PeekAC97 timed out reading reg = 0x%04x.\r\n", ulCodecReg));
return MMSYSERR_ERROR;
}
}
//
// Read in the value from S2Data
//
*pulValue = *AC97I_S2DATA;
AC97_MSG((L"PeekAC97: Reg = 0x%02x, Value = 0x%04x\r\n",ulCodecReg, *pulValue));
return MMSYSERR_NOERROR;
}
//****************************************************************************
// PokeAC97
//****************************************************************************
// Writes an AC97 codec Register.
//
// ulCodecReg - AC97 Codec Register
// ulValue - New Codec Register valuie.
//
// Return MMSYSERROR_NOERROR
// MMSYSERROR_ERROR
//
MMRESULT AC97Codec::PokeAC97(ULONG ulCodecReg, ULONG ulValue)
{
BOOL b;
ULONG ulRGIS;
LARGE_INTEGER liStart, liCurrent;
AC97_MSG((L"PokeAC97: Reg = 0x%02x, Value = 0x%04x\r\n",ulCodecReg, ulValue));
//
// Read AACS2DATA to be sure and clear the SLOT2RXVALID bit.
// May not be necessary, not sure.
//
// uiTemp = Ac97Global->AACS2DATA.Value;
//
// Write the data to AACS2DATA, then the address to AACS1DATA.
//
*AC97I_S2DATA = ulValue;
*AC97I_S1DATA = ulCodecReg;
b = QueryPerformanceCounter(&liStart);
for(;;)
{
//
// Sleep for a little bit.
//
// Sleep(1);
b = QueryPerformanceCounter(&liCurrent);
//
// Check to see if there is any data in the receive fifo.
//
ulRGIS = *AC97I_RGIS;
if(ulRGIS & GIS_SLOT2TXCOMPLETE)
break;
if((liStart.QuadPart + TIMEOUT_AC97_READ) <liCurrent.QuadPart)
{
ERRMSG((L"PokeAC97: SLOT2TXVALID timed out reg = 0x%02x.\r\n", ulCodecReg));
return MMSYSERR_ERROR;
}
}
return MMSYSERR_NOERROR;
}
static volatile ULONG ulJunk;
//****************************************************************************
// I2SCodec::ProducePIOTone
//****************************************************************************
// For Debug only.
//
// Uses I2S to produce a PIO tone.
//
//
void AC97Codec::ProducePIOTone(void)
{
ULONG ulCount;
volatile ULONG ulTemp;
volatile ULONG ulSR;
ULONG ulMax= 0x80008000, ulMin=0x7FFF7FFF;
//
// Enable the AC97 transmit channel 0.
//
m_puAC97Ch[AC97I_TXCR>>2] = m_ulTXCR | TXCR_TEN;
ulTemp = ulMax;
for(ulCount = 0; ;ulCount ++)
{
if(ulCount& 0x40)
{
ulTemp = ulMax;
}
else
{
ulTemp = ulMin;
}
do
{
ulSR = m_puAC97Ch[AC97I_SR >>2];
} while( ulSR & SR_TXFF);
m_puAC97Ch[AC97I_DR >>2] = ulTemp;
}
}
//****************************************************************************
// I2SCodec::CapturePIO
//****************************************************************************
// For Debug only.
//
// Uses I2S to produce a PIO tone.
//
//
void AC97Codec::CapturePIO(void)
{
ULONG ulCount;
volatile ULONG ulTemp[100];
volatile ULONG ulSR;
//
// Enable the AC97 capture channel 0.
//
m_puAC97Ch[AC97I_RXCR>>2] = m_ulRXCR | RXCR_REN;
for(;;)
{
for(ulCount = 0; ulCount <100;ulCount ++)
{
do
{
ulSR = m_puAC97Ch[AC97I_SR >>2];
} while( ulSR & SR_RXFE);
ulTemp[ulCount] = m_puAC97Ch[AC97I_DR >>2] ;
}
}
}
//****************************************************************************
// I2SCodec::CaptureNPlay
//****************************************************************************
// Play and capture to test the AC97 codec interface.
//
//
void AC97Codec::CaptureNPlay(void)
{
volatile ULONG ulTemp;
volatile ULONG ulSR;
//
// Enable the AC97 transmitt channel 0.
//
m_puAC97Ch[AC97I_RXCR>>2] = m_ulRXCR | RXCR_REN;
m_puAC97Ch[AC97I_TXCR>>2] = m_ulTXCR | TXCR_TEN;
for(;;)
{
do
{
ulSR = m_puAC97Ch[AC97I_SR >>2];
} while( ulSR & SR_RXFE);
ulTemp = m_puAC97Ch[AC97I_DR >>2] ;
ulSR = m_puAC97Ch[AC97I_SR >>2];
if(!( ulSR & SR_TXFF))
{
m_puAC97Ch[AC97I_DR >>2] = ulTemp;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -