NT4/private/ntos/dd/sound/necsnd/hardware.c
2020-09-30 17:12:29 +02:00

1241 lines
29 KiB
C

/*++
"@(#) NEC hardware.c 1.1 95/03/22 21:23:28"
Copyright (c) 1994 NEC Corporation
Copyright (c) 1993 Microsoft Corporation
Module Name:
hardware.c
Abstract:
This module contains code for communicating with the hardware
on the Microsoft sound system card.
This is just a port of the windows code
Environment:
Kernel mode
Revision History:
--*/
#include "sound.h"
// Internal routines
WAVE_INTERFACE_ROUTINE HwSetupDMA;
WAVE_INTERFACE_ROUTINE HwStopDMA;
// WAVE_INTERFACE_ROUTINE HwPauseDMA;
// WAVE_INTERFACE_ROUTINE HwResumeDMA;
VOID
HwEnterLPM(
PSOUND_HARDWARE pHw,
PUCHAR gbReg
);
VOID
HwLeaveLPM(
PSOUND_HARDWARE pHw,
PUCHAR gbReg,
PWAVE_INFO WaveInfo
);
VOID
HwExtMute(
PSOUND_HARDWARE pHw,
BOOLEAN On
);
BOOLEAN
CODECWaitForReady(
PSOUND_HARDWARE pHw
);
BOOLEAN
HwIsIoValid(
PSOUND_HARDWARE pHw
);
BOOLEAN
HwIsDMAValid(
PSOUND_HARDWARE pHw,
ULONG DmaChannel
);
BOOLEAN
HwIsInterruptValid(
PSOUND_HARDWARE pHw,
ULONG Interrupt
);
//
// Remove initialization stuff from resident memory
//
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,HwInitialize)
#pragma alloc_text(INIT,HwIsIoValid)
#pragma alloc_text(INIT,HwIsDMAValid)
#pragma alloc_text(INIT,HwIsInterruptValid)
#endif
/* globals - copied from windows driver */
/* crystals to use with what rates */
#ifdef USEONECRYSTAL
static CONST UCHAR rateSend[] = {
0x01, 0x0f,
0x03, 0x05, 0x07,
0x0d, 0x09,
0x0b
};
#define NUMRATES (sizeof(rateSend)/sizeof(rateSend[0]))
static CONST USHORT rates[] = {
(5510 + 6620) / 2, (6620 + 11025) / 2,
(USHORT)((11025 + 18900L) / 2),
(USHORT)((18900L + 22050L) / 2), (USHORT)((22050L + 33075L) / 2),
(USHORT)((33075L + 37800L) / 2), (USHORT)((37800L + 44100L) / 2)
};
static CONST USHORT rate[] = {
5510, 6620, 11025, 18900, 22050, 33075, 37800, 44100
};
#else
static CONST UCHAR rateSend[] = {
0x01, 0x0f,
0x00, 0x0e,
0x03, 0x02,
0x05, 0x07,
0x04, 0x06,
0x0d, 0x09,
0x0b, 0x0c
};
#define NUMRATES (sizeof(rateSend)/sizeof(rateSend[0]))
static CONST USHORT rates[] = {
(5510 + 6620) / 2, (6620 + 8000) / 2,
(8000 + 9600) / 2, (9600 + 11025) / 2,
(USHORT)(( 11025 + 16000) / 2), (USHORT)((16000L + 18900L) / 2),
(USHORT)((18900L + 22050L) / 2), (USHORT)((22050L + 27420L) / 2),
(USHORT)((27420L + 32000L) / 2), (USHORT)((32000L + 33075L) / 2),
(USHORT)((33075L + 37800L) / 2), (USHORT)((37800L + 44100L) / 2),
(USHORT)((44100L + 48000L) / 2)
};
static CONST USHORT rate[] = {
5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050,
27420, 32000, 33075, 37800, 44100, 48000
};
#endif
/*******************************************************************
*
* Configuration support routines (called first)
*
*******************************************************************/
// -----------------------------------------------------------
// Name: HwIsIoValid
// Desc: Tests if the currently selected IO port matches
// that on the card.
//
// Params: pHw - global device info
// Returns: TRUE if the device is there,
// FALSE otherwise
BOOLEAN HwIsIoValid( PSOUND_HARDWARE pHw )
{
UCHAR CodecAddress;
UCHAR CodecVersion;
// Check for CODEC presence - this is in three stages :
// 1. Check CODEC not busy
// 2. Verify CODEC version
// 3. Performn write/read test
//
// At the same time (if all valid) write into the hardware info
// whether is might be a compaq board.
CodecAddress = INPORT(pHw, CODEC_ADDRESS);
if (CodecAddress & CODEC_IS_BUSY)
{
dprintf2(("HwIsIoValid(): Codec is Busy!!"));
return FALSE;
}
// Set CODEC address to MISC_REGISTER - preserve the MCE
OUTPORT(pHw, CODEC_ADDRESS, (CodecAddress & 0x40) | REGISTER_MISC);
// Read the version
CodecVersion = INPORT(pHw, CODEC_DATA);
// Can no longer get to case statement checking for the
// CS4231. The ID number is the same as for the AD1845
// This driver now supports the AD1845. Another method
// will have to be developed to differentiate between them
// Ignore the following comments.
//
// CS4231 could be in Mode 2... we don't support Mode 2 yet.
// SO, turn this bit off and then compare the read to the
// chip version. The bit should've cleared.
// If bit 7 is set, we're talking to a CS part.
// Check the version
dprintf2(("Codec Version = %02lXH", (ULONG)CodecVersion));
switch(CodecVersion & 0x8F)
{
case VER_AD1848J:
pHw->CODECClass = CODEC_J_CLASS;
dprintf4(("CODEC Class = AD1848-J Class"));
break;
case VER_AD1848K:
pHw->CODECClass = CODEC_K_CLASS;
dprintf4(("CODEC Class = AD1848-K Class"));
break;
case VER_AD1845J:
pHw->CODECClass = CODEC_JPLUS_CLASS;
dprintf4(("CODEC Class = AD1848-K Class"));
break;
/**
* case VER_CS4248:
* // Clear mode 2 if set (we don't support it)
* if (CodecVersion & CS4231_MISC_MODE2)
* {
* OUTPORT(pHw, CODEC_DATA, VER_CS4248);
* OUTPORT(pHw, CODEC_DATA, CS4231_MISC_MODE2);
*
* if (INPORT(pHw, CODEC_DATA) & CS4231_MISC_MODE2)
* {
* pHw->CODECClass = CODEC_KPLUS_CLASS;
* dprintf4(("CODEC Class = CS4248-K-Plus Class"));
* }
* else
* {
* pHw->CODECClass = CODEC_K_CLASS;
* dprintf4(("CODEC Class = CS4248-K Class"));
* }
* }
* pHw->CODECClass = CODEC_K_CLASS;
* dprintf4(("CODEC Class = CS4248-K Class"));
* break;
**/
default:
// ERROR Try to retore Address register
OUTPORT(pHw, CODEC_ADDRESS, CodecAddress);
return FALSE;
}
// Do read/write test. Make sure not to modify the top 4 bits of
// the version as changing these can result in a undocumented test
// mode to be entered
// The lower nibble is not writeable so we should get the codec version
// back again.
OUTPORT(pHw, CODEC_DATA, CodecVersion ^ 0x0F);
if (INPORT(pHw, CODEC_DATA) == CodecVersion)
{
return TRUE;
}
// Shouldn't beable to get here. Must not
// be a codec. Try to restore the version info
// and address
OUTPORT(pHw, CODEC_DATA, CodecVersion);
OUTPORT(pHw, CODEC_ADDRESS, CodecAddress);
return FALSE;
}
BOOLEAN
HwIsDMAValid(
PSOUND_HARDWARE pHw,
ULONG DmaChannel
)
/*++
Routine Description :
Tests if the currently selected DMA channel is suitable
Arguments :
pHw - global device info
Return Value :
TRUE if the channel is OK, FALSE otherwise
--*/
{
return TRUE;
}
// ------------------------------------------------------
// Name: HwIsInterruptValid
// Desc: Tests if the currently selected DMA channel
// is suitable
//
// Params: pHw - global device info
// Returns: TRUE if the channel is OK, FALSE otherwise
BOOLEAN HwIsInterruptValid( PSOUND_HARDWARE pHw,
ULONG Interrupt )
{
pHw->ValidSet = TRUE;
dprintf3(("HwIsInterruptValid(); Interrupt = %d", Interrupt));
return (1 << Interrupt);
}
/*******************************************************************
*
* Synchronization
*
*******************************************************************/
VOID HwEnter( PSOUND_HARDWARE pHw )
{
KeWaitForSingleObject(&pHw->CODECMutex,
Executive,
KernelMode,
FALSE, // Not alertable
NULL);
}
VOID HwLeave( PSOUND_HARDWARE pHw )
{
KeReleaseMutex(&pHw->CODECMutex, FALSE);
}
/*******************************************************************
*
* Initialize structures and real hardware
*
*******************************************************************/
//---------------------------------------------------
// Name: HwInitialize
// Desc: Initialize everything on the card
//
// Params: pHw - global device info
// DmaChannel - DMA channel to use
// Interrupt - Interrupt to use
//
// Returns: TRUE if OK,
// FALSE otherwise (timeout on some write)
BOOLEAN HwInitialize( PWAVE_INFO WaveInfo,
PSOUND_HARDWARE pHw,
ULONG DmaChannel,
ULONG Interrupt,
ULONG InputSource )
{
UCHAR gbReg[NUMBER_OF_REGISTERS];
static CONST UCHAR InitRegValues[] = SOUND_REGISTER_INIT;
WaveInfo->HwContext = pHw;
WaveInfo->HwSetupDMA = HwSetupDMA;
WaveInfo->HwStopDMA = HwStopDMA;
WaveInfo->HwSetWaveFormat = HwSetWaveFormat;
// WaveInfo->HwPauseDMA = HwPauseDMA;
// WaveInfo->HwResumeDMA = HwResumeDMA;
pHw->Key = HARDWARE_KEY;
pHw->WaitLoop = 1000000;
// This value is not a rate used currently!
pHw->Format = 8;
// Initialize CODEC access mutex
KeInitializeMutex(&pHw->CODECMutex,
3 // Level
);
// Initialize CODEC, making sure its off and muted, and that
// everything is in a known state.
HwEnter(pHw);
HwExtMute(pHw, TRUE);
// Enter low power mode
HwEnterLPM(pHw, gbReg);
// Acknowledge any interrupts pending just in case
OUTPORT(pHw, CODEC_STATUS, 0);
CODECRegisterWrite (pHw, REGISTER_INTERFACE, 0x04); /* disable playback, etc. */
// If this is an AD1845 set it to mode 2 and setup the
// capture data format register with the same value as the
// play format register
if( pHw->CODECClass == CODEC_JPLUS_CLASS )
{
// Set the part to mode 2.
CODECRegisterWrite( pHw, REGISTER_MISC, AD1845_MISC_MODE2 );
CODECRegisterWrite( pHw, REGISTER_CAP_FORMAT, \
InitRegValues[REGISTER_DATAFORMAT] );
}
/* BUGFIX - no number - 7/28/92 - get rid of whining by using 44 kHz */
CODECRegisterWrite (pHw, REGISTER_DATAFORMAT,
InitRegValues[REGISTER_DATAFORMAT]);
// Leave Low Power Mode
HwLeaveLPM(pHw, gbReg, WaveInfo);
// Intialize the PBIC registers
// Even if this is an AD1845 we don't mess with the upper
// 16 registers. This driver presently only partly
// the AD1845.
{
UCHAR i;
for (i = 0; i < 15; i++)
{
CODECRegisterWrite(pHw, i, InitRegValues[i]);
}
}
// Un-Mute outputs
HwExtMute(pHw, FALSE);
HwLeave(pHw);
// See if writes were OK
return !pHw->BadBoard;
}
/*******************************************************************
*
* Volume and mixing control
*
*******************************************************************/
/*******************************************************************
*
* Wave format setting
*
*******************************************************************/
//-------------------------------------------------------
// Name: HwNearestRate
// Desc: Returns nearest rate we support to rate input
//
// params: samPerSec - Rate requested
// Returns: Nearest rate supported
ULONG HwNearestRate( ULONG samPerSec )
{
int i;
// find the closest sampling rate
// Careful this routine presumes the passed
// in parameter is always a reasonable request
// no exception handling
for (i = 0; i < (NUMRATES - 1); i++)
if (samPerSec < (ULONG)rates[i])
break;
return rate[i];
}
//-----------------------------------------------------------------
// Name: HwSetWaveFormat()
// Desc: Set the wave format as specified for the device. This
// function will build up a BYTE to send out to the data
// format register. If this is an AD1845 we send out the
// same word to the capture data format register. The bits
// for setting the sample rate are harmless to the capture
// format register. These bits should theoretically be
// written as 0's. OH-Well I'll fix it later.
//
// Params: Context - pointer to device's
// local device info
//
// Return: TRUE if format changed,
// FALSE otherwise
BOOLEAN HwSetWaveFormat( IN PWAVE_INFO WaveInfo )
{
UCHAR gbReg[NUMBER_OF_REGISTERS];
UCHAR send, i;
PSOUND_HARDWARE pHw;
pHw = (PSOUND_HARDWARE)WaveInfo->HwContext;
ASSERTMSG("Hardware structure invalid",
pHw != NULL && pHw->Key == HARDWARE_KEY);
// find the closest sampling rate
// Set the sample rate bits.
for (i = 0, send = rateSend[NUMRATES - 1]; i < (NUMRATES - 1); i++)
{
if (WaveInfo->SamplesPerSec < (ULONG)rates[i])
{
send = rateSend[i];
break;
};
};
// Set the stereo/mono Bit
send |= ((UCHAR) (WaveInfo->Channels - 1) << 4);
// Set any of the companded format bits
if (WaveInfo->WaveFormat != NULL)
{
switch (WaveInfo->WaveFormat->wFormatTag)
{
case WAVE_FORMAT_MULAW:
send |= FORMAT_MULAW << 5;
break;
case WAVE_FORMAT_ALAW:
send |= FORMAT_ALAW << 5;
break;
}
}
// Set the sample data size bits. 8/16 bits per sample
send |= (WaveInfo->BitsPerSample - 8) << 3;
// set codec to low power mode, write, and set to high power mode.
// Only do this if we have a different sampling rate than before
if (send != pHw->Format)
{
pHw->Format = (UCHAR) send;
// Enter hardware
HwEnter(pHw);
// No need to synchronize with the ISR because we're not
// running any DMA at this point
ASSERTMSG("Trying to set format while DMA running!", !WaveInfo->DMABusy);
// mute the outputs
HwExtMute (pHw, TRUE);
// Set the MCE bit if not an AD1845
if( pHw->CODECClass == CODEC_JPLUS_CLASS )
{
// Must be an AD1845
// Setup without MCE bit and write to teh capture
// format register as well.
CODECRegisterWrite (pHw, REGISTER_DATAFORMAT, pHw->Format);
CODECRegisterWrite (pHw, REGISTER_CAP_FORMAT, pHw->Format);
}
else
{
// Must be an older AD1848. Set the MCE bit then
// write to the format data register. there is no
// specific capture format register.
HwEnterLPM(pHw, gbReg);
CODECRegisterWrite (pHw, REGISTER_DATAFORMAT, pHw->Format);
HwLeaveLPM(pHw, gbReg, WaveInfo);
}
// unmute the outputs
HwExtMute (pHw, FALSE);
// Leave hardware
HwLeave(pHw);
return TRUE;
}
else
{
return FALSE;
}
}
//------------------------------------------------------
// Name: HwEnterLPM
// Desc: Enter low-power mode. This mutes the PBIC,
// and then tells it to enter LPM.
// ( This is otherwise known as setting the -
// MCE bit -- Why didn't MS just say that --
// who ever heard of low power mode? A.W. )
// HwLeaveLPM() must follow soon after this
// because this function mutes the output.
//
// Params: pHw - global device info
// Returns: None
//
VOID HwEnterLPM( PSOUND_HARDWARE pHw, PUCHAR gbReg )
{
UCHAR i;
UCHAR bTemp;
// remember the old volume registers & then mute each one of them
dprintf2(("HwEnterLPM()"));
for (i = REGISTER_LEFTAUX1; i <= REGISTER_RIGHTOUTPUT; i++)
{
gbReg[i] = bTemp = (UCHAR)CODECRegisterRead(pHw, i);
CODECRegisterWrite (pHw, i, (UCHAR)(bTemp | 0x80));
};
// make sure that the record gain is not too high 'cause
// if it is we get strange cliping results. This is a
// bug in the pbic
for (i = REGISTER_LEFTINPUT; i <= REGISTER_RIGHTINPUT; i++)
{
gbReg[i] = bTemp = CODECRegisterRead(pHw, i);
if ((bTemp & 0x0f) > 13)
bTemp = (bTemp & (UCHAR)0xf0) | (UCHAR)13;
CODECRegisterWrite (pHw, i, bTemp);
};
// turn MCE on
pHw->bPower = LOW_POWER;
// make sure that we're not initializing
if ( CODECWaitForReady(pHw) )
{
OUTPORT (pHw, CODEC_ADDRESS, pHw->bPower);
}
}
//---------------------------------------------------------------
// Name: HwLeaveLPM
// Desc: The leaves low-power mode. It tells the
// PBIC to leave low-power mode, waits for
// the autocalibration to stop, and then
// un-mutes.
//
// ( This is otherwise known as clearing the -
// MCE bit -- Why didn't MS just say that --
// who ever heard of low power mode? A.W. )
//
// A timer is set so that nobody accesses the
// device too soon afterwards. When the timer
// completes the timer Dpc routine will restore
// the register values
//
// Params: pHw - global device info
// Return: None
VOID HwLeaveLPM( PSOUND_HARDWARE pHw, PUCHAR gbReg, PWAVE_INFO WaveInfo )
{
UCHAR i, bAuto;
ULONG Time;
dprintf2(("HwLeaveLPM()"));
pHw->bPower = HIGH_POWER;
// make sure that we're not initializing
if ( CODECWaitForReady(pHw) )
{
// see if we're going to autocalibrate
OUTPORT (pHw, CODEC_ADDRESS, (UCHAR)(pHw->bPower | REGISTER_INTERFACE));
bAuto = INPORT(pHw, CODEC_DATA);
// if we're going to autocalibrate then wait for it to get done
if ( (bAuto & AUTO_CAL) )
{
OUTPORT (pHw, CODEC_ADDRESS, (UCHAR) (pHw->bPower | REGISTER_TEST));
// wait for autocalibration to start, and then stop. The current
// register then the test register.
Time = 30;
while (( (~INPORT(pHw, CODEC_DATA)) & 0x20) && (Time--))
{
KeStallExecutionProcessor(1);
}
Time = pHw->WaitLoop;
while (( (INPORT(pHw, CODEC_DATA)) & 0x20) && (Time--))
{
KeStallExecutionProcessor(1);
}
WaveInfo->Calibration = TRUE;
}
// wait 10 milliseconds before turning off the internal mute.
// We need to do this to get rid of clicks.
SoundDelay(TIMEDELAY);
// restore the old volume registers
for (i = REGISTER_LEFTINPUT; i <= REGISTER_RIGHTOUTPUT; i++)
CODECRegisterWrite (pHw, i, gbReg[i]);
}
}
//---------------------------------------------------------
// Name: HwExtMute
// Desc: This turns the external mute on/off, with the
// required delays (of 12 millisec when turning off)
//
// Params: pHw - global device info
// On - turns mute on
//
// Returns: None
VOID HwExtMute( PSOUND_HARDWARE pHw, BOOLEAN On )
{
UCHAR PrevDSP;
PrevDSP = (UCHAR)CODECRegisterRead(pHw, REGISTER_DSP) & ~(0xc0);
if (On)
{
pHw->gbMuteFilter |= (UCHAR) ((0x40));
}
else
{
//
// wait 10 milliseconds before turning off the external mute. We need
// do this to get rid of clicks.
//
SoundDelay(TIMEDELAY);
pHw->gbMuteFilter &= (UCHAR) (~(0x40));
}
CODECRegisterWrite( pHw,
REGISTER_DSP,
(UCHAR)(PrevDSP | pHw->gbMuteFilter));
}
// --------------------------------------------------------------
// Name: HwSetupDMA
// Desc: Start the DMA
//
// We need to be synchronized to do this so we don't get
// confused by interrupts
//
// If this is an AD1845 then we will also write out the
// DMA count to the Capture upper/lower base count
// registers. This driver presently will not support
// separate Capture/Playback information.
//
// Params: Context - global device info
//
// Returns: None
BOOLEAN HwSetupDMA( PWAVE_INFO WaveInfo )
{
UCHAR gbReg[NUMBER_OF_REGISTERS]; // For enter/leave lpm
ULONG CODECSamples;
PSOUND_HARDWARE pHw;
PGLOBAL_DEVICE_INFO pGDI;
ULONG adjusting;
ULONG i;
pHw = (PSOUND_HARDWARE)WaveInfo->HwContext;
pGDI = CONTAINING_RECORD(pHw, GLOBAL_DEVICE_INFO, Hw);
ASSERTMSG("Hardware structure invalid",
pHw != NULL && pHw->Key == HARDWARE_KEY);
HwEnter(pHw);
// clear any pending interrupts
// Why ???
CODECRegisterWrite (pHw, REGISTER_DSP, pHw->gbMuteFilter);
OUTPORT (pHw, CODEC_STATUS, 0);
if (!WaveInfo->Direction)
{
if (pHw->CODECClass == CODEC_J_CLASS)
{
// Mute the outputs for record
HwExtMute(pHw, TRUE);
}
MixSetADCHardware(pGDI, (ULONG)-1L);
// IMPORTANT: it seems that we need to enter LPM before
// recording or it doesnt record right!
// enter lpm
if (!WaveInfo->Calibration)
{
HwEnterLPM(pHw, gbReg);
}
}
else
{
// Set the volume for this device
MixSetVolume(pGDI, ControlLineoutWaveoutVolume);
}
// tell the codec's DMA how many samples between each interrupt
adjusting = 1;
CODECSamples = (WaveInfo->DoubleBuffer.BufferSize << 2 ) /
(WaveInfo->Channels * WaveInfo->BitsPerSample) -adjusting;
CODECRegisterWrite (pHw,
REGISTER_LOWERBASE,
(UCHAR) (CODECSamples & 0xFF)); /* low count */
CODECRegisterWrite (pHw,
REGISTER_UPPERBASE,
(UCHAR) ((CODECSamples >> 8) & 0xFF));/* high count */
if( pHw->CODECClass == CODEC_JPLUS_CLASS )
{
// Setup the capture DMA count registers as well
CODECRegisterWrite (pHw,
REGISTER_CAP_LOWERBASE,
(UCHAR) (CODECSamples & 0xFF)); /* low count */
CODECRegisterWrite (pHw,
REGISTER_CAP_UPPERBASE,
(UCHAR) ((CODECSamples >> 8) & 0xFF));/* high count */
}
// If we're paused we should be using resume
ASSERTMSG("Start output DMA called in paused state!",
!WaveInfo->Direction ||
((PLOCAL_DEVICE_INFO)WaveInfo->DeviceObject->DeviceExtension)->
State != WAVE_DD_STOPPED);
// say that we want to record or play
CODECRegisterWrite (pHw,
REGISTER_INTERFACE,
(UCHAR)(!WaveInfo->Direction ? 0x06 | AUTO_CAL:
0x05));
// start the dma - from now on we can get interrupts
CODECRegisterWrite (pHw,
REGISTER_DSP,
(UCHAR)(pHw->gbMuteFilter | (UCHAR)(0x02)));
if (!WaveInfo->Direction)
{
// we're going into high power to record
//
// We can get interrupts here but nobody else programs the DMA
// while it's running
if (!WaveInfo->Calibration)
{
HwLeaveLPM(pHw, gbReg, WaveInfo);
}
if (pHw->CODECClass == CODEC_J_CLASS)
{
HwExtMute(pHw, FALSE);
}
}
HwLeave(pHw);
return TRUE;
}
BOOLEAN
HwWaitForTxComplete(
IN PWAVE_INFO WaveInfo
)
/*++
Routine Description :
Wait until the device stops requesting so we don't shut off the DMA
while it's still trying to request.
Arguments :
WaveInfo - Wave parameters
Return Value :
None
--*/
{
ULONG ulCount ;
if (ulCount = HalReadDmaCounter( WaveInfo->DMABuf.AdapterObject[0] ))
{
ULONG i, ulLastCount = ulCount ;
for (i = 0;
(i < 4000) &&
(ulLastCount !=
(ulCount = HalReadDmaCounter( WaveInfo->DMABuf.AdapterObject[0] )));
i++)
{
ulLastCount = ulCount;
KeStallExecutionProcessor(10);
}
return (i < 4000);
}
else
return TRUE ;
}
BOOLEAN
HwStopDMA(
PWAVE_INFO WaveInfo
)
/*++
Routine Description :
Stop the DMA
Whoever calls this routine had better first make sure that no
Dpc routine is going to run in parallel!
For wave input the caller MUST NOT own the spin lock because
we're going to do waits in here
Note we're also NOT doing the windows hack for wave out
when the sampling rate is too low because that would
involve doing waits in a dpc or some other complex design.
Arguments :
Context - global device info
Return Value :
None
--*/
{
UCHAR gbReg[NUMBER_OF_REGISTERS]; // For enter/leave lpm
PSOUND_HARDWARE pHw;
PGLOBAL_DEVICE_INFO pGDI;
pHw = (PSOUND_HARDWARE)WaveInfo->HwContext;
pGDI = (PGLOBAL_DEVICE_INFO)CONTAINING_RECORD(pHw, GLOBAL_DEVICE_INFO, Hw);
ASSERTMSG("Hardware structure invalid",
pHw != NULL && pHw->Key == HARDWARE_KEY);
HwEnter(pHw);
if (pHw->CODECClass == CODEC_J_CLASS && !WaveInfo->Direction) {
HwExtMute (pHw, TRUE);
}
//
// turn off the DAC outputs, and minimized ADC gain
//
CODECRegisterWrite (pHw, REGISTER_LEFTOUTPUT, 0x3f);
CODECRegisterWrite (pHw, REGISTER_RIGHTOUTPUT, 0x3f);
CODECRegisterWrite (pHw, REGISTER_LEFTINPUT, 0x00);
CODECRegisterWrite (pHw, REGISTER_RIGHTINPUT, 0x00);
if (!WaveInfo->Direction) {
HwEnterLPM(pHw, gbReg);
}
//
// tell the DSP to shut off
//
CODECRegisterWrite (pHw,
REGISTER_DSP,
pHw->gbMuteFilter); /* diable the interrupts */
OUTPORT (pHw, CODEC_STATUS, 0x00); /* clear any pending */
CODECRegisterWrite (pHw,
REGISTER_INTERFACE,
0x00); /* kill DMA */
if (!WaveInfo->Direction) {
HwLeaveLPM(pHw, gbReg, WaveInfo);
}
HwWaitForTxComplete( WaveInfo );
//
// wait for the DMA to stop
//
if (WaveInfo->Direction) {
// HwDMAWaitForTxComplete(pHw);
// If we want to fix this we need a thread to run this on
/* BUGFIX:1199 - 7/21/92 - Mike Rozak */
/* if 11 kHz playback then enter/leave MCE to reduce the noise */
if (pHw->CODECClass == CODEC_J_CLASS &&
WaveInfo->SamplesPerSec <= 17000) {
// HwDo11KHz(pHw);
};
} else {
// HwDMAWaitForTxComplete(pHw);
};
if (pHw->CODECClass == CODEC_J_CLASS && !WaveInfo->Direction) {
HwExtMute (pHw, FALSE);
};
HwLeave(pHw);
return TRUE;
}
BOOLEAN
HwPauseDAC(
PWAVE_INFO WaveInfo
)
/*++
Routine Description :
Pause output DMA
This routine runs at DEVICE level because the DMA is probably
running
Arguments :
Context - global device info
Return Value :
None
--*/
{
PSOUND_HARDWARE pHw;
pHw = WaveInfo->HwContext;
HwEnter(pHw);
//
// However, someone may have paused us first to fix overrun
//
if (!pHw->Paused) {
/* turn off the DAC outputs */
CODECRegisterWrite (pHw, REGISTER_LEFTOUTPUT, 0x3f);
CODECRegisterWrite (pHw, REGISTER_RIGHTOUTPUT, 0x3f);
/* tell the CODEC to pause */
CODECRegisterWrite (pHw, REGISTER_INTERFACE, 0x04); /* dont want to play or record */
CODECRegisterWrite (pHw,
REGISTER_DSP,
(UCHAR)(pHw->gbMuteFilter |
(UCHAR) 0x00)); /* stop the interrupts */
}
HwLeave(pHw);
return TRUE;
}
#if 0
BOOLEAN
HwResumeDAC(
PVOID Context
)
/*++
Routine Description :
Resume output DMA
This routine runs at DEVICE level because the DMA is probably
running
Arguments :
Context - global device info
Return Value :
None
--*/
{
PSOUND_HARDWARE pHw;
pHw = Context;
//
// However, someone may have resumed us first to fix overrun
//
if (!pHw->Paused) {
HwEnter(pHw);
/* send to CODEC saying to continue */
CODECRegisterWrite (pHw, REGISTER_INTERFACE, 0x05); /* want to play & not record */
CODECRegisterWrite (pHw,
REGISTER_DSP,
(UCHAR)(pHw->gbMuteFilter | (UCHAR)0x02)); /* start interrupts */
/* turn on the DAC outputs */
HwSetVolume(...)
CODECRegisterWrite (pHw, REGISTER_LEFTOUTPUT, pHw->DACToPBICLeft);
CODECRegisterWrite (pHw, REGISTER_RIGHTOUTPUT, pHw->DACToPBICRight);
HwLeave(pHw);
}
}
#endif
// -----------------------------------------------------------------
// Routines to read/write the PBIC (??) registers
// -----------------------------------------------------------------
// -----------------------------------------------------------------
// Name: CODECWaitForReady
// Desc: Wait for PBIC to finish initializing (if it is). This
// function will timeout after XX milliseconds if the
// hardware is not functioning properly (return FALSE).
//
// If the PBIC is not initializing, then this function will
// return immediately (which should be the normal case).
//
// The timeout value must be at least 256 samples at 8kHz.
// We compute this for 300 samples at 8kHz to be safe.
//
// This should be 300/8192 = 36 milliseconds (!)
//
// Params: pHw - global device info
// Returns: TRUE if wait was successful
BOOLEAN CODECWaitForReady( IN PSOUND_HARDWARE pHw )
{
int i;
ASSERTMSG("Bad hardware structure key", pHw->Key == HARDWARE_KEY);
// Make sure the Mutex is being used
// Invalid: ASSERT(KeReadStateMutex(&pHw->CODECMutex) != 1);
if (pHw->BadBoard)
{
return FALSE; // Failed previously
}
if (!(INPORT(pHw, CODEC_ADDRESS) & CODEC_IS_BUSY))
{
return TRUE;
}
for (i = 0; i < 36000; i++)
{
KeStallExecutionProcessor(10); // Hope this only happens at
// PASSIVE level!
if (!(INPORT(pHw, CODEC_ADDRESS) & CODEC_IS_BUSY))
{
return TRUE;
}
}
// Timed out
pHw->BadBoard = TRUE;
return FALSE;
}
//----------------------------------------------------
// Name: CODECRegisterWrite
// Desc: Write to one of the wave registers
//
// Params: pHw - global device info
// RegisterNumber - register to use
// Value - value to write
//
// Returns: None
VOID CODECRegisterWrite( IN PSOUND_HARDWARE pHw,
IN UCHAR RegisterNumber,
IN UCHAR Value )
{
// Wait for PBIC
if (!CODECWaitForReady(pHw))
{
// Bad board!
return;
}
// Select the register
OUTPORT(pHw, CODEC_ADDRESS, pHw->bPower | RegisterNumber);
// Write the value to the data port
OUTPORT(pHw, CODEC_DATA, Value);
}
//----------------------------------------------------
// Name: CODECRegisterRead
// Desc: Read from one of the wave registers
//
// Params: pHw - global device info
// RegisterNumber - register to use
//
// Returns: Value read
UCHAR CODECRegisterRead( IN PSOUND_HARDWARE pHw,
IN UCHAR RegisterNumber )
{
// Wait for PBIC
if (!CODECWaitForReady(pHw))
{
// Bad board!
return 0xFF;
}
// Select the register
OUTPORT(pHw, CODEC_ADDRESS, pHw->bPower | RegisterNumber);
// Read the value from the data port
return INPORT(pHw, CODEC_DATA);
}