/******************************************************************************** ** Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved. ** ** Portions Copyright (c) 1998-1999 Intel Corporation ** ********************************************************************************/ // Every debug output has "Modulname text" static char STR_MODULENAME[] = "ICH Common: "; #include "common.h" /***************************************************************************** * Static Members ***************************************************************************** */ // // This is the register cache including registry names and default values. The // first WORD contains the register value and the second WORD contains a flag. // Currently, we only set SHREG_INVALID if we have to read the register at // startup (that's true when there is no constant default value for the // register). Note that we cache the registers only to prevent read access to // the AC97 CoDec during runtime, because this is slow (40us). // We only set SHREG_INIT if we want to set the register to default at driver // startup. If needed, the third field contains the registry name and the // forth field contains a default value that is used when there is no registry // entry. // The flag SHREG_NOCACHE is used when we don't want to cache the register // at all. This is neccessary for status registers and sample rate registers. // tAC97Registers CAdapterCommon::m_stAC97Registers[] = { {0x0000, SHREG_INVALID, NULL, 0}, // AC97REG_RESET {0x8000, SHREG_INIT, L"MasterVolume", 0x0000}, // AC97REG_MASTER_VOLUME {0x8000, SHREG_INIT, L"HeadphoneVolume", 0x0000}, // AC97REG_HPHONE_VOLUME {0x8000, SHREG_INIT, L"MonooutVolume", 0x0000}, // AC97REG_MMONO_VOLUME {0x0F0F, SHREG_INIT, L"ToneControls", 0x0F0F}, // AC97REG_MASTER_TONE {0x0000, SHREG_INVALID | SHREG_INIT, L"BeepVolume", 0x0000}, // AC97REG_BEEP_VOLUME {0x8008, SHREG_INIT, L"PhoneVolume", 0x8008}, // AC97REG_PHONE_VOLUME {0x8008, SHREG_INIT, L"MicVolume", 0x8008}, // AC97REG_MIC_VOLUME {0x8808, SHREG_INIT, L"LineInVolume", 0x0808}, // AC97REG_LINE_IN_VOLUME {0x8808, SHREG_INIT, L"CDVolume", 0x0808}, // AC97REG_CD_VOLUME {0x8808, SHREG_INIT, L"VideoVolume", 0x0808}, // AC97REG_VIDEO_VOLUME {0x8808, SHREG_INIT, L"AUXVolume", 0x0808}, // AC97REG_AUX_VOLUME {0x8808, SHREG_INIT, L"WaveOutVolume", 0x0808}, // AC97REG_PCM_OUT_VOLUME {0x0000, SHREG_INIT, L"RecordSelect", 0x0404}, // AC97REG_RECORD_SELECT {0x8000, SHREG_INIT, L"RecordGain", 0x0000}, // AC97REG_RECORD_GAIN {0x8000, SHREG_INIT, L"RecordGainMic", 0x0000}, // AC97REG_RECORD_GAIN_MIC {0x0000, SHREG_INIT, L"GeneralPurpose", 0x0000}, // AC97REG_GENERAL {0x0000, SHREG_INIT, L"3DControl", 0x0000}, // AC97REG_3D_CONTROL {0x0000, SHREG_NOCACHE, NULL, 0}, // AC97REG_RESERVED {0x0000, SHREG_NOCACHE | SHREG_INIT, L"PowerDown", 0}, // AC97REG_POWERDOWN // AC97-2.0 registers {0x0000, SHREG_INVALID, NULL, 0}, // AC97REG_EXT_AUDIO_ID {0x0000, SHREG_NOCACHE | SHREG_INIT, L"ExtAudioCtrl", 0x4001}, // AC97REG_EXT_AUDIO_CTRL {0xBB80, SHREG_NOCACHE, NULL, 0}, // AC97REG_FRONT_SAMPLERATE {0xBB80, SHREG_NOCACHE, NULL, 0}, // AC97REG_SURROUND_SAMPLERATE {0xBB80, SHREG_NOCACHE, NULL, 0}, // AC97REG_LFE_SAMPLERATE {0xBB80, SHREG_NOCACHE, NULL, 0}, // AC97REG_RECORD_SAMPLERATE {0xBB80, SHREG_NOCACHE, NULL, 0}, // AC97REG_MIC_SAMPLERATE {0x8080, SHREG_INIT, L"CenterLFEVolume", 0x0000}, // AC97REG_CENTER_LFE_VOLUME {0x8080, SHREG_INIT, L"SurroundVolume", 0x0000}, // AC97REG_SURROUND_VOLUME {0x0000, SHREG_NOCACHE, NULL, 0} // AC97REG_RESERVED2 // We leave the other values blank. There would be a huge gap with 31 // elements that are currently unused, and then there would be 2 other // (used) values, the vendor IDs. We just force a read from the vendor // IDs in the end of ProbeHWConfig to fill the cache. }; // // This is the hardware configuration information. The first struct is for // nodes, which we default to FALSE. The second struct is for Pins, which // contains the configuration (FALSE) and the registry string which is the // reason for making a static struct so we can just fill in the name. // tHardwareConfig CAdapterCommon::m_stHardwareConfig = { // Nodes {{FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}}, // Pins {{FALSE, L"DisablePCBeep"}, // PINC_PCBEEP_PRESENT {FALSE, L"DisablePhone"}, // PINC_PHONE_PRESENT {FALSE, L"DisableMic2"}, // PINC_MIC2_PRESENT {FALSE, L"DisableVideo"}, // PINC_VIDEO_PRESENT {FALSE, L"DisableAUX"}, // PINC_AUX_PRESENT {FALSE, L"DisableHeadphone"}, // PINC_HPOUT_PRESENT {FALSE, L"DisableMonoOut"}, // PINC_MONOOUT_PRESENT {FALSE, L"DisableMicIn"}, // PINC_MICIN_PRESENT {FALSE, L"DisableMic"}, // PINC_MIC_PRESENT {FALSE, L"DisableLineIn"}, // PINC_LINEIN_PRESENT {FALSE, L"DisableCD"}, // PINC_CD_PRESENT {FALSE, L"DisableSurround"}, // PINC_SURROUND_PRESENT {FALSE, L"DisableCenterLFE"}} // PINC_CENTER_LFE_PRESENT }; #pragma code_seg("PAGE") /***************************************************************************** * NewAdapterCommon ***************************************************************************** * Create a new adapter common object. */ NTSTATUS NewAdapterCommon ( OUT PUNKNOWN *Unknown, IN REFCLSID, IN PUNKNOWN UnknownOuter OPTIONAL, IN POOL_TYPE PoolType ) { PAGED_CODE (); ASSERT (Unknown); DOUT (DBG_PRINT, ("[NewAdapterCommon]")); STD_CREATE_BODY_ ( CAdapterCommon, Unknown, UnknownOuter, PoolType, PADAPTERCOMMON ); } /***************************************************************************** * CAdapterCommon::Init ***************************************************************************** * Initialize the adapter common object -> initialize and probe HW. * Pass only checked resources. */ STDMETHODIMP_(NTSTATUS) CAdapterCommon::Init ( IN PRESOURCELIST ResourceList, IN PDEVICE_OBJECT DeviceObject ) { PAGED_CODE (); ASSERT (ResourceList); ASSERT (DeviceObject); NTSTATUS ntStatus = STATUS_SUCCESS; DOUT (DBG_PRINT, ("[CAdapterCommon::Init]")); // // Set the topology pointer to NULL. // m_Topology = NULL; // // Save the device object // m_pDeviceObject = DeviceObject; // // Get the base address for the AC97 codec and bus master. // ASSERT (ResourceList->FindTranslatedPort (0)); m_pCodecBase = (PUSHORT)ResourceList->FindTranslatedPort (0)-> u.Port.Start.QuadPart; ASSERT (ResourceList->FindTranslatedPort (1)); m_pBusMasterBase = (PUCHAR)ResourceList->FindTranslatedPort (1)-> u.Port.Start.QuadPart; DOUT (DBG_SYSINFO, ("Configuration:\n" " Bus Master = 0x%X\n" " Codec = 0x%X", m_pBusMasterBase, m_pCodecBase)); // // Set m_bDirectRead to TRUE so that all AC97 register read and // writes are going directly to the HW // m_bDirectRead = TRUE; // // Initialize the hardware. // ntStatus = InitAC97 (); if (!NT_SUCCESS (ntStatus)) return ntStatus; // // Probe hardware configuration // ntStatus = ProbeHWConfig (); if (!NT_SUCCESS (ntStatus)) { DOUT (DBG_ERROR, ("Probing of hardware configuration failed!")); return ntStatus; } // // Now, every AC97 read access goes to the cache. // m_bDirectRead = FALSE; // // Restore the AC97 registers now. // #if (DBG) DumpConfig (); #endif ntStatus = SetAC97Default (); // // Initialize the device state. // m_PowerState = PowerDeviceD0; return ntStatus; } /***************************************************************************** * CAdapterCommon::~CAdapterCommon ***************************************************************************** * Destructor. */ CAdapterCommon::~CAdapterCommon () { PAGED_CODE (); DOUT (DBG_PRINT, ("[CAdapterCommon::~CAdapterCommon]")); } #if (DBG) /***************************************************************************** * CAdapterCommon::DumpConfig ***************************************************************************** * Dumps the HW configuration for the AC97 codec. */ void CAdapterCommon::DumpConfig (void) { PAGED_CODE (); DOUT (DBG_PRINT, ("[CAdapterCommon::DumpConfig]")); // // Print debug output for MICIN. // if (GetPinConfig (PINC_MICIN_PRESENT)) { DOUT (DBG_PROBE, ("MICIN found")); } else { DOUT (DBG_PROBE, ("No MICIN found")); } // // Print debug output for tone controls. // if (GetNodeConfig (NODEC_TONE_PRESENT)) { DOUT (DBG_PROBE, ("Tone controls found")); } else { DOUT (DBG_PROBE, ("No tone controls found")); } // // Print debug output for mono out. // if (!GetPinConfig (PINC_MONOOUT_PRESENT)) { DOUT (DBG_PROBE, ("No mono out found")); } // // Print debug output for headphones. // if (!GetPinConfig (PINC_HPOUT_PRESENT)) { DOUT (DBG_PROBE, ("No headphone out found")); } // // Print debug output for loudness. // if (GetNodeConfig (NODEC_LOUDNESS_PRESENT)) { DOUT (DBG_PROBE, ("Loudness found")); } else { DOUT (DBG_PROBE, ("No Loudness found")); } // // Print debug output for 3D. // if (GetNodeConfig (NODEC_3D_PRESENT)) { DOUT (DBG_PROBE, ("3D controls found")); } else { DOUT (DBG_PROBE, ("No 3D controls found")); } // // Print debug output for pc beep. // if (GetPinConfig (PINC_PCBEEP_PRESENT)) { DOUT (DBG_PROBE, ("PC beep found")); } else { DOUT (DBG_PROBE, ("No PC beep found")); } // // Print debug output for phone line (or mono line input). // if (GetPinConfig (PINC_PHONE_PRESENT)) { DOUT (DBG_PROBE, ("Phone found")); } else { DOUT (DBG_PROBE, ("No Phone found")); } // // Print debug output for video. // if (GetPinConfig (PINC_VIDEO_PRESENT)) { DOUT (DBG_PROBE, ("Video in found")); } else { DOUT (DBG_PROBE, ("No Video in found")); } // // Print debug output for AUX. // if (GetPinConfig (PINC_AUX_PRESENT)) { DOUT (DBG_PROBE, ("AUX in found")); } else { DOUT (DBG_PROBE, ("No AUX in found")); } // // Print debug output for second miorophone. // if (GetPinConfig (PINC_MIC2_PRESENT)) { DOUT (DBG_PROBE, ("MIC2 found")); } else { DOUT (DBG_PROBE, ("No MIC2 found")); } // // Print debug output for 3D stuff. // if (GetNodeConfig (NODEC_3D_PRESENT)) { if (GetNodeConfig (NODEC_3D_CENTER_ADJUSTABLE)) { DOUT (DBG_PROBE, ("Adjustable 3D center control found")); } if (GetNodeConfig (NODEC_3D_DEPTH_ADJUSTABLE)) { DOUT (DBG_PROBE, ("Nonadjustable 3D depth control found")); } } // // Print debug output for quality of master volume. // if (GetNodeConfig (NODEC_6BIT_MASTER_VOLUME)) { DOUT (DBG_PROBE, ("6bit master out found")); } else { DOUT (DBG_PROBE, ("5bit master out found")); } // // Print debug output for quality of headphones volume. // if (GetPinConfig (PINC_HPOUT_PRESENT)) { if (GetNodeConfig (NODEC_6BIT_HPOUT_VOLUME)) { DOUT (DBG_PROBE, ("6bit headphone out found")); } else { DOUT (DBG_PROBE, ("5bit headphone out found")); } } // // Print debug output for quality of mono out volume. // if (GetPinConfig (PINC_MONOOUT_PRESENT)) { if (GetNodeConfig (NODEC_6BIT_MONOOUT_VOLUME)) { DOUT (DBG_PROBE, ("6bit mono out found")); } else { DOUT (DBG_PROBE, ("5bit mono out found")); } } // // Print sample rate information. // if (GetNodeConfig (NODEC_PCM_VARIABLERATE_SUPPORTED)) { DOUT (DBG_PROBE, ("PCM variable sample rate supported")); } else { DOUT (DBG_PROBE, ("only 48KHz PCM supported")); } // // Print double rate information. // if (GetNodeConfig (NODEC_PCM_DOUBLERATE_SUPPORTED)) { DOUT (DBG_PROBE, ("PCM double sample rate supported")); } // // Print mic rate information. // if (GetNodeConfig (NODEC_MIC_VARIABLERATE_SUPPORTED)) { DOUT (DBG_PROBE, ("MIC variable sample rate supported")); } else { DOUT (DBG_PROBE, ("only 48KHz MIC supported")); } // print DAC information if (GetNodeConfig (NODEC_CENTER_DAC_PRESENT)) { DOUT (DBG_PROBE, ("center DAC found")); } if (GetNodeConfig (NODEC_SURROUND_DAC_PRESENT)) { DOUT (DBG_PROBE, ("surround DAC found")); } if (GetNodeConfig (NODEC_LFE_DAC_PRESENT)) { DOUT (DBG_PROBE, ("LFE DAC found")); } } #endif /***************************************************************************** * CAdapterCommon::NonDelegatingQueryInterface ***************************************************************************** * Obtains an interface. This function works just like a COM QueryInterface * call and is used if the object is not being aggregated. * We basically just check any GUID we know and return this object in case we * know it. */ STDMETHODIMP_(NTSTATUS) CAdapterCommon::NonDelegatingQueryInterface ( IN REFIID Interface, OUT PVOID * Object ) { PAGED_CODE (); ASSERT (Object); DOUT (DBG_PRINT, ("[CAdapterCommon::NonDelegatingQueryInterface]")); // Is it IID_IUnknown? if (IsEqualGUIDAligned (Interface, IID_IUnknown)) { *Object = (PVOID)(PUNKNOWN)(PADAPTERCOMMON)this; } else // or IID_IAdapterCommon ... if (IsEqualGUIDAligned (Interface, IID_IAdapterCommon)) { *Object = (PVOID)(PADAPTERCOMMON)this; } else // or IID_IAdapterPowerManagement ... if (IsEqualGUIDAligned (Interface, IID_IAdapterPowerManagement)) { *Object = (PVOID)(PADAPTERPOWERMANAGEMENT)this; } else { // nothing found, must be an unknown interface. *Object = NULL; return STATUS_INVALID_PARAMETER; } // // We reference the interface for the caller. // ((PUNKNOWN)*Object)->AddRef (); return STATUS_SUCCESS; } /***************************************************************************** * CAdapterCommon::InitAC97 ***************************************************************************** * Initialize the ICH (without hosing the modem if it got installed first). */ NTSTATUS CAdapterCommon::InitAC97 (void) { PAGED_CODE (); DOUT (DBG_PRINT, ("[CAdapterCommon::InitAC97]")); // // First check if there is an AC link to the primary CoDec. // NTSTATUS ntStatus = PrimaryCodecReady (); if (NT_SUCCESS (ntStatus)) { // // Second, reset this primary CoDec; If this is a AMC97 CoDec, only // the audio registers are reset. If this is a MC97 CoDec, the CoDec // should ignore the reset (according to the spec). // WriteCodecRegister (AC97REG_RESET, 0x00, -1); ntStatus = PowerUpCodec (); } else { DOUT (DBG_ERROR, ("Initialization of AC97 CoDec failed.")); } return ntStatus; } /***************************************************************************** * CAdapterCommon::Check6thBitSupport ***************************************************************************** * Probes for 6th bit volume control support. * The passed parameters are the AC97 register that has the volume control and * the node config that should be set in this case. */ NTSTATUS CAdapterCommon::Check6thBitSupport ( IN AC97Register AC97Reg, IN TopoNodeConfig Config ) { NTSTATUS ntStatus; WORD wCodecReg; WORD wOriginal; // Read the current value. ntStatus = ReadCodecRegister (AC97Reg, &wOriginal); if (!NT_SUCCESS (ntStatus)) return ntStatus; // Write the 6th bit; for mono controls we write 0x20, for stereo // controls 0x2020. ntStatus = WriteCodecRegister (AC97Reg, (AC97Reg == AC97REG_MMONO_VOLUME) ? 0x0020 : 0x2020, -1); if (!NT_SUCCESS (ntStatus)) return ntStatus; // And read back. ntStatus = ReadCodecRegister (AC97Reg, &wCodecReg); if (!NT_SUCCESS (ntStatus)) return ntStatus; // Check return. For mono 0x20 and for stereo 0x2020. if (((wCodecReg & 0x0020) && (AC97Reg == AC97REG_MMONO_VOLUME)) || (wCodecReg & 0x2020)) { SetNodeConfig (Config, TRUE); } else { SetNodeConfig (Config, FALSE); } // Restore original value. WriteCodecRegister (AC97Reg, wOriginal, -1); return ntStatus; } /***************************************************************************** * CAdapterCommon::ProbeHWConfig ***************************************************************************** * Probes the hardware configuration. * If this function returns with an error, then the configuration is not * complete! Probing the registers is done by reading them (and comparing with * the HW default value) or when the default is unknown, writing to them and * reading back + restoring. * Additionally, we read the registry so that a HW vendor can overwrite (means * disable) found registers in case the adapter (e.g. video) is not visible to * the user (he can't plug in a video audio there). * * This is a very long function with all of the error checking! */ NTSTATUS CAdapterCommon::ProbeHWConfig (void) { PAGED_CODE (); NTSTATUS ntStatus = STATUS_SUCCESS; DWORD dwGlobalStatus; WORD wCodecID; WORD wCodecReg; DOUT (DBG_PRINT, ("[CAdapterCommon::ProbeHWConfig]")); // // Wait for the whatever 97 to complete reset and establish a link. // ntStatus = PrimaryCodecReady (); if (!NT_SUCCESS (ntStatus)) return ntStatus; // // Master volume is one of the supported registers on an AC97 // ntStatus = ReadCodecRegister (AC97REG_MASTER_VOLUME, &wCodecReg); if (!NT_SUCCESS (ntStatus)) return ntStatus; // Default is x8000. if (wCodecReg != 0x8000) return STATUS_NO_SUCH_DEVICE; // // This gives us information about the AC97 CoDec // ntStatus = ReadCodecRegister (AC97REG_RESET, &wCodecID); if (!NT_SUCCESS (ntStatus)) return ntStatus; // // Fill out the configuration stuff. // SetPinConfig (PINC_MICIN_PRESENT, wCodecID & 0x0001); // Check if OEM wants to disable MIC record line. if (DisableAC97Pin (PINC_MICIN_PRESENT)) SetPinConfig (PINC_MICIN_PRESENT, FALSE); // If we still have MIC record line, enable the DAC in ext. audio register. if (GetPinConfig (PINC_MICIN_PRESENT)) // Enable ADC MIC. WriteCodecRegister (AC97REG_EXT_AUDIO_CTRL, 0, 0x4000); else // Disable ADC MIC. WriteCodecRegister (AC97REG_EXT_AUDIO_CTRL, 0x4000, 0x4000); // // Continue setting configuration information. // SetNodeConfig (NODEC_TONE_PRESENT, wCodecID & 0x0004); SetNodeConfig (NODEC_SIMUL_STEREO_PRESENT, wCodecID & 0x0008); SetPinConfig (PINC_HPOUT_PRESENT, wCodecID & 0x0010); // Check if OEM wants to disable headphone output. if (DisableAC97Pin (PINC_HPOUT_PRESENT)) SetPinConfig (PINC_HPOUT_PRESENT, FALSE); SetNodeConfig (NODEC_LOUDNESS_PRESENT, wCodecID & 0x0020); SetNodeConfig (NODEC_3D_PRESENT, wCodecID & 0x7C00); // // Test for the input pins that are always there but could be disabled // by the HW vender // // Check if OEM wants to disable mic input. SetPinConfig (PINC_MIC_PRESENT, !DisableAC97Pin (PINC_MIC_PRESENT)); // Check if OEM wants to disable line input. SetPinConfig (PINC_LINEIN_PRESENT, !DisableAC97Pin (PINC_LINEIN_PRESENT)); // Check if OEM wants to disable CD input. SetPinConfig (PINC_CD_PRESENT, !DisableAC97Pin (PINC_CD_PRESENT)); // // For the rest, we have to probe the registers. // // // Test for Mono out. // ntStatus = ReadCodecRegister (AC97REG_MMONO_VOLUME, &wCodecReg); if (!NT_SUCCESS (ntStatus)) return ntStatus; // Default is x8000. SetPinConfig (PINC_MONOOUT_PRESENT, (wCodecReg == 0x8000)); // Check if OEM wants to disable mono output. if (DisableAC97Pin (PINC_MONOOUT_PRESENT)) SetPinConfig (PINC_MONOOUT_PRESENT, FALSE); // // Test for PC beeper support. // ntStatus = ReadCodecRegister (AC97REG_BEEP_VOLUME, &wCodecReg); if (!NT_SUCCESS (ntStatus)) return ntStatus; // default is x0 or x8000. If it's 0x8000 then we know for sure that the // CoDec has a PcBeep, otherwise we have to check the register if (wCodecReg == 0x8000) SetPinConfig (PINC_PCBEEP_PRESENT, TRUE); else if (!wCodecReg) { // mute the pc beeper. ntStatus = WriteCodecRegister (AC97REG_BEEP_VOLUME, 0x8000, -1); if (!NT_SUCCESS (ntStatus)) return ntStatus; // read back ntStatus = ReadCodecRegister (AC97REG_BEEP_VOLUME, &wCodecReg); if (!NT_SUCCESS (ntStatus)) return ntStatus; if (wCodecReg == 0x8000) { // yep, we have support. SetPinConfig (PINC_PCBEEP_PRESENT, TRUE); // reset to default value. WriteCodecRegister (AC97REG_BEEP_VOLUME, 0x0, -1); } else // nope, not present SetPinConfig (PINC_PCBEEP_PRESENT, FALSE); } else // any other value then 0x0 and 0x8000. SetPinConfig (PINC_PCBEEP_PRESENT, FALSE); // Check if OEM wants to disable beeper support. if (DisableAC97Pin (PINC_PCBEEP_PRESENT)) SetPinConfig (PINC_PCBEEP_PRESENT, FALSE); // // Test for phone support. // ntStatus = ReadCodecRegister (AC97REG_PHONE_VOLUME, &wCodecReg); if (!NT_SUCCESS (ntStatus)) return ntStatus; // Default is x8008. SetPinConfig (PINC_PHONE_PRESENT, (wCodecReg == 0x8008)); // Check if OEM wants to disable phone input. if (DisableAC97Pin (PINC_PHONE_PRESENT)) SetPinConfig (PINC_PHONE_PRESENT, FALSE); // // Test for video support. // ntStatus = ReadCodecRegister (AC97REG_VIDEO_VOLUME, &wCodecReg); if (!NT_SUCCESS (ntStatus)) return ntStatus; // Default is x8808. SetPinConfig (PINC_VIDEO_PRESENT, (wCodecReg == 0x8808)); // Check if OEM wants to disable video input. if (DisableAC97Pin (PINC_VIDEO_PRESENT)) SetPinConfig (PINC_VIDEO_PRESENT, FALSE); // // Test for Aux support. // ntStatus = ReadCodecRegister (AC97REG_AUX_VOLUME, &wCodecReg); if (!NT_SUCCESS (ntStatus)) return ntStatus; // Default is 0x8808. SetPinConfig (PINC_AUX_PRESENT, (wCodecReg == 0x8808)); // Check if OEM wants to disable aux input. if (DisableAC97Pin (PINC_AUX_PRESENT)) SetPinConfig (PINC_AUX_PRESENT, FALSE); // // Test for Mic2 source. // ntStatus = ReadCodecRegister (AC97REG_GENERAL, &wCodecReg); if (!NT_SUCCESS (ntStatus)) return ntStatus; // Test for Mic2 select bit. if (wCodecReg & 0x0100) SetPinConfig (PINC_MIC2_PRESENT, TRUE); else { // Select Mic2 as source. ntStatus = WriteCodecRegister (AC97REG_GENERAL, 0x0100, 0x0100); if (!NT_SUCCESS (ntStatus)) return ntStatus; // Read back. ntStatus = ReadCodecRegister (AC97REG_GENERAL, &wCodecReg); if (!NT_SUCCESS (ntStatus)) return ntStatus; if (wCodecReg & 0x0100) { // Yep, we have support so set it to the default value. SetPinConfig (PINC_MIC2_PRESENT, TRUE); // reset to default value. WriteCodecRegister (AC97REG_GENERAL, 0, 0x0100); } else SetPinConfig (PINC_MIC2_PRESENT, FALSE); } // Check if OEM wants to disable mic2 input. if (DisableAC97Pin (PINC_MIC2_PRESENT)) SetPinConfig (PINC_MIC2_PRESENT, FALSE); // // Test the 3D controls. // if (GetNodeConfig (NODEC_3D_PRESENT)) { // // First test for fixed 3D controls. Write default value ... // ntStatus = WriteCodecRegister (AC97REG_3D_CONTROL, 0, -1); if (!NT_SUCCESS (ntStatus)) return ntStatus; // Read 3D register. Default is 0 when adjustable, otherwise it is // a fixed value. ntStatus = ReadCodecRegister (AC97REG_3D_CONTROL, &wCodecReg); if (!NT_SUCCESS (ntStatus)) return ntStatus; // // Check center and depth separately. // // For center SetNodeConfig (NODEC_3D_CENTER_ADJUSTABLE, !(wCodecReg & 0x0F00)); // For depth SetNodeConfig (NODEC_3D_DEPTH_ADJUSTABLE, !(wCodecReg & 0x000F)); // // Test for adjustable controls. // WriteCodecRegister (AC97REG_3D_CONTROL, 0x0A0A, -1); // Read 3D register. Now it should be 0x0A0A for adjustable controls, // otherwise it is a fixed control or simply not there. ReadCodecRegister (AC97REG_3D_CONTROL, &wCodecReg); // Restore the default value WriteCodecRegister (AC97REG_3D_CONTROL, 0, -1); // Check the center control for beeing adjustable if (GetNodeConfig (NODEC_3D_CENTER_ADJUSTABLE) && (wCodecReg & 0x0F00) != 0x0A00) { SetNodeConfig (NODEC_3D_CENTER_ADJUSTABLE, FALSE); } // Check the depth control for beeing adjustable if (GetNodeConfig (NODEC_3D_DEPTH_ADJUSTABLE) && (wCodecReg & 0x000F) != 0x000A) { SetNodeConfig (NODEC_3D_DEPTH_ADJUSTABLE, FALSE); } } // // Check for 6th bit support in volume controls. To check the 6th bit, // we first have to write a value (with 6th bit set) and then read it // back. After that, we should restore the register to its default value. // // // Start with the master volume. // Check6thBitSupport (AC97REG_MASTER_VOLUME, NODEC_6BIT_MASTER_VOLUME); // // Check for a headphone volume control. // if (GetPinConfig (PINC_HPOUT_PRESENT)) { Check6thBitSupport (AC97REG_HPHONE_VOLUME, NODEC_6BIT_HPOUT_VOLUME); } // // Mono out there? // if (GetPinConfig (PINC_MONOOUT_PRESENT)) { Check6thBitSupport (AC97REG_MMONO_VOLUME, NODEC_6BIT_MONOOUT_VOLUME); } // // Get extended AC97 V2.0 information // ntStatus = ReadCodecRegister (AC97REG_EXT_AUDIO_ID, &wCodecReg); if (!NT_SUCCESS (ntStatus)) return ntStatus; // // Store the information // SetNodeConfig (NODEC_PCM_VARIABLERATE_SUPPORTED, wCodecReg & 0x0001); SetNodeConfig (NODEC_PCM_DOUBLERATE_SUPPORTED, wCodecReg & 0x0002); SetNodeConfig (NODEC_MIC_VARIABLERATE_SUPPORTED, wCodecReg & 0x0008); SetNodeConfig (NODEC_CENTER_DAC_PRESENT, wCodecReg & 0x0040); SetNodeConfig (NODEC_SURROUND_DAC_PRESENT, wCodecReg & 0x0080); SetNodeConfig (NODEC_LFE_DAC_PRESENT, wCodecReg & 0x0100); // // In case we have some features get some more information and program // the codec. // if (wCodecReg) { // // Enable variable sample rate in the control register and disable // double rate. Also enable all DACs. // WriteCodecRegister (AC97REG_EXT_AUDIO_CTRL, wCodecReg & 0x0009, 0x380B); // // Check for codecs that have only one sample rate converter. These // codecs will stick registers AC97REG_FRONT_SAMPLERATE and // AC97REG_RECORD_SAMPLERATE together. // if (GetNodeConfig (NODEC_PCM_VARIABLERATE_SUPPORTED)) { // The default of the sample rate registers should be 0xBB80. WriteCodecRegister (AC97REG_FRONT_SAMPLERATE, 0xBB80, 0xFFFF); // Write 44.1KHz into record VSR, then check playback again. WriteCodecRegister (AC97REG_RECORD_SAMPLERATE, 0xAC44, 0xFFFF); ntStatus = ReadCodecRegister (AC97REG_FRONT_SAMPLERATE, &wCodecReg); WriteCodecRegister (AC97REG_RECORD_SAMPLERATE, 0xBB80, 0xFFFF); if (!NT_SUCCESS (ntStatus)) return ntStatus; // // Set the flag accordingly // SetNodeConfig (NODEC_PCM_VSR_INDEPENDENT_RATES, (wCodecReg == 0xBB80)); } // // Check multichanel support on the ICH. // if (GetNodeConfig (NODEC_SURROUND_DAC_PRESENT)) { dwGlobalStatus = ReadBMControlRegister32 (GLOB_STA); // // Codec supports >2 chanel, does ICH too? // if ((GetNodeConfig (NODEC_CENTER_DAC_PRESENT) || GetNodeConfig (NODEC_LFE_DAC_PRESENT)) && (dwGlobalStatus & GLOB_STA_MC6)) { SetPinConfig (PINC_CENTER_LFE_PRESENT, TRUE); } else { SetPinConfig (PINC_CENTER_LFE_PRESENT, FALSE); } // // Do we support at least 4 channels? // SetPinConfig (PINC_SURROUND_PRESENT, (dwGlobalStatus & GLOB_STA_MC4)); } else { // // Only 2 channel (stereo) support. // SetPinConfig (PINC_CENTER_LFE_PRESENT, FALSE); SetPinConfig (PINC_SURROUND_PRESENT, FALSE); } } // Check if OEM wants to disable surround output. if (DisableAC97Pin (PINC_SURROUND_PRESENT)) SetPinConfig (PINC_SURROUND_PRESENT, FALSE); // Check if OEM wants to disable center and LFE output. if (DisableAC97Pin (PINC_CENTER_LFE_PRESENT)) SetPinConfig (PINC_CENTER_LFE_PRESENT, FALSE); // // Check the 6th bit support for the additional channels. // if (GetPinConfig (PINC_SURROUND_PRESENT)) Check6thBitSupport (AC97REG_SURROUND_VOLUME, NODEC_6BIT_SURROUND_VOLUME); if (GetPinConfig (PINC_CENTER_LFE_PRESENT)) Check6thBitSupport (AC97REG_CENTER_LFE_VOLUME, NODEC_6BIT_CENTER_LFE_VOLUME); // // We read these registers because they are dependent on the codec. // ReadCodecRegister (AC97REG_VENDOR_ID1, &wCodecReg); ReadCodecRegister (AC97REG_VENDOR_ID2, &wCodecReg); return STATUS_SUCCESS; } /***************************************************************************** * CAdapterCommon::AcquireCodecSemiphore ***************************************************************************** * Acquires the AC97 semiphore. This can not be called at dispatch level * because it can timeout if a lower IRQL thread has the semaphore. */ NTSTATUS CAdapterCommon::AcquireCodecSemiphore () { PAGED_CODE (); DOUT (DBG_PRINT, ("[CAdapterCommon::AcquireCodecSemiphore]")); ULONG ulCount = 0; while (READ_PORT_UCHAR (m_pBusMasterBase + CAS) & CAS_CAS) { // // Do we want to give up?? // if (ulCount++ > 100) { DOUT (DBG_ERROR, ("Cannot acquire semaphore.")); return STATUS_IO_TIMEOUT; } // // Let's wait a little, 40us and then try again. // KeStallExecutionProcessor (40L); } return STATUS_SUCCESS; } /***************************************************************************** * CAdapterCommon::ReadCodecRegister ***************************************************************************** * Reads a AC97 register. Don't call at PASSIVE_LEVEL. */ STDMETHODIMP_(NTSTATUS) CAdapterCommon::ReadCodecRegister ( IN AC97Register reg, OUT PWORD wData ) { PAGED_CODE (); ASSERT (wData); ASSERT (reg < AC97REG_INVALID); // audio can only be in the primary codec NTSTATUS ntStatus; ULONG Status; DOUT (DBG_PRINT, ("[CAdapterCommon::ReadCodecRegister]")); // // Check if we have to access the HW directly. // if (m_bDirectRead || (m_stAC97Registers[reg].wFlags & SHREG_INVALID) || (m_stAC97Registers[reg].wFlags & SHREG_NOCACHE)) { // // Grab the codec access semiphore. // ntStatus = AcquireCodecSemiphore (); if (!NT_SUCCESS (ntStatus)) { DOUT (DBG_ERROR, ("ReadCodecRegister couldn't acquire the semiphore" " for reg. %s", reg <= AC97REG_RESERVED2 ? RegStrings[reg] : reg == AC97REG_VENDOR_ID1 ? "REG_VENDOR_ID1" : reg == AC97REG_VENDOR_ID2 ? "REG_VENDOR_ID2" : "REG_INVALID")); return ntStatus; } // // Read the data. // *wData = READ_PORT_USHORT (m_pCodecBase + reg); // // Check to see if the read was successful. // Status = READ_PORT_ULONG ((PULONG)(m_pBusMasterBase + GLOB_STA)); if (Status & GLOB_STA_RCS) { // // clear the timeout bit // WRITE_PORT_ULONG ((PULONG)(m_pBusMasterBase + GLOB_STA), Status); *wData = 0; DOUT (DBG_ERROR, ("ReadCodecRegister timed out for register %s", reg <= AC97REG_RESERVED2 ? RegStrings[reg] : reg == AC97REG_VENDOR_ID1 ? "REG_VENDOR_ID1" : reg == AC97REG_VENDOR_ID2 ? "REG_VENDOR_ID2" : "REG_INVALID")); return STATUS_IO_TIMEOUT; } // // Clear invalid flag // m_stAC97Registers[reg].wCache = *wData; m_stAC97Registers[reg].wFlags &= ~SHREG_INVALID; DOUT (DBG_REGS, ("AC97READ: %s = 0x%04x (HW)", reg <= AC97REG_RESERVED2 ? RegStrings[reg] : reg == AC97REG_VENDOR_ID1 ? "REG_VENDOR_ID1" : reg == AC97REG_VENDOR_ID2 ? "REG_VENDOR_ID2" : "REG_INVALID", *wData)); } else { // // Otherwise, use the value in the cache. // *wData = m_stAC97Registers[reg].wCache; DOUT (DBG_REGS, ("AC97READ: %s = 0x%04x (C)", reg <= AC97REG_RESERVED2 ? RegStrings[reg] : reg == AC97REG_VENDOR_ID1 ? "REG_VENDOR_ID1" : reg == AC97REG_VENDOR_ID2 ? "REG_VENDOR_ID2" : "REG_INVALID", *wData)); } return STATUS_SUCCESS; } /***************************************************************************** * CAdapterCommon::WriteCodecRegister ***************************************************************************** * Writes to a AC97 register. This can only be done at passive level because * the AcquireCodecSemiphore call could fail! */ STDMETHODIMP_(NTSTATUS) CAdapterCommon::WriteCodecRegister ( IN AC97Register reg, IN WORD wData, IN WORD wMask ) { PAGED_CODE (); ASSERT (reg < AC97REG_INVALID); // audio can only be in the primary codec WORD TempData = 0; NTSTATUS ntStatus = STATUS_SUCCESS; DOUT (DBG_PRINT, ("[CAdapterCommon::WriteCodecRegister]")); // // No mask? Could happen when you try to prg. left channel of a // mono volume. // if (!wMask) return STATUS_SUCCESS; // // Check to see if we are only writing specific bits. If so, we want // to leave some bits in the register alone. // if (wMask != 0xffff) { // // Read the current register contents. // ntStatus = ReadCodecRegister (reg, &TempData); if (!NT_SUCCESS (ntStatus)) { DOUT (DBG_ERROR, ("WriteCodecRegiser read for mask failed")); return ntStatus; } // // Do the masking. // TempData &= ~wMask; TempData |= (wMask & wData); } else { TempData = wData; } // // Grab the codec access semiphore. // ntStatus = AcquireCodecSemiphore (); if (!NT_SUCCESS (ntStatus)) { DOUT (DBG_ERROR, ("WriteCodecRegister failed for register %s", reg <= AC97REG_RESERVED2 ? RegStrings[reg] : reg == AC97REG_VENDOR_ID1 ? "REG_VENDOR_ID1" : reg == AC97REG_VENDOR_ID2 ? "REG_VENDOR_ID2" : "REG_INVALID")); return ntStatus; } // // Write the data. // WRITE_PORT_USHORT (m_pCodecBase + reg, TempData); // // Update cache. // m_stAC97Registers[reg].wCache = TempData; DOUT (DBG_REGS, ("AC97WRITE: %s -> 0x%04x", reg <= AC97REG_RESERVED2 ? RegStrings[reg] : reg == AC97REG_VENDOR_ID1 ? "REG_VENDOR_ID1" : reg == AC97REG_VENDOR_ID2 ? "REG_VENDOR_ID2" : "REG_INVALID", TempData)); return STATUS_SUCCESS; } /***************************************************************************** * CAdapterCommon::PrimaryCodecReady ***************************************************************************** * Checks whether the primary codec is present and ready. This may take * awhile if we are bringing it up from a cold reset so give it a second * before giving up. */ NTSTATUS CAdapterCommon::PrimaryCodecReady (void) { PAGED_CODE (); DOUT (DBG_PRINT, ("[CAdapterCommon::PrimaryCodecReady]")); // // Enable the AC link and raise the reset line. // DWORD dwRegValue = ReadBMControlRegister32 (GLOB_CNT); // If someone enabled GPI Interrupt Enable, then he hopefully handles that // too. dwRegValue = (dwRegValue | GLOB_CNT_COLD) & ~(GLOB_CNT_ACLOFF | GLOB_CNT_PRIE); WriteBMControlRegister (GLOB_CNT, dwRegValue); // // Wait for the Codec to be ready. // ULONG WaitCycles = 200; LARGE_INTEGER WaitTime = RtlConvertLongToLargeInteger (-50000); // wait 5000us (5ms) relative do { if (READ_PORT_ULONG ((PULONG)(m_pBusMasterBase + GLOB_STA)) & GLOB_STA_PCR) { return STATUS_SUCCESS; } KeDelayExecutionThread (KernelMode, FALSE, &WaitTime); } while (WaitCycles--); DOUT (DBG_ERROR, ("PrimaryCodecReady timed out!")); return STATUS_IO_TIMEOUT; } /***************************************************************************** * CAdapterCommon::PowerUpCodec ***************************************************************************** * Sets the Codec to the highest power state and waits until the Codec reports * that the power state is reached. */ NTSTATUS CAdapterCommon::PowerUpCodec (void) { PAGED_CODE (); WORD wCodecReg; NTSTATUS ntStatus; DOUT (DBG_PRINT, ("[CAdapterCommon::PowerUpCodec]")); // // Power up the Codec. // WriteCodecRegister (AC97REG_POWERDOWN, 0x00, -1); // // Wait for the Codec to be powered up. // ULONG WaitCycles = 200; LARGE_INTEGER WaitTime = RtlConvertLongToLargeInteger (-50000); // wait 5000us (5ms) relative do { // // Read the power management register. // ntStatus = ReadCodecRegister (AC97REG_POWERDOWN, &wCodecReg); if (!NT_SUCCESS (ntStatus)) { wCodecReg = 0; // Will cause an error. break; } // // Check the power state. Should be ready. // if ((wCodecReg & 0x0f) == 0x0f) break; // // Let's wait a little, 5ms and then try again. // KeDelayExecutionThread (KernelMode, FALSE, &WaitTime); } while (WaitCycles--); // Check if we timed out. if ((wCodecReg & 0x0f) != 0x0f) { DOUT (DBG_ERROR, ("PowerUpCodec timed out. CoDec not powered up.")); ntStatus = STATUS_DEVICE_NOT_READY; } return ntStatus; } /***************************************************************************** * CAdapterCommon::ProgramSampleRate ***************************************************************************** * Programs the sample rate. If the rate cannot be programmed, the routine * restores the register and returns STATUS_UNSUCCESSFUL. * We don't handle double rate sample rates here, because the Intel ICH con- * troller cannot serve CoDecs with double rate or surround sound. If you want * to modify this driver for another AC97 controller, then you might want to * change this function too. */ STDMETHODIMP_(NTSTATUS) CAdapterCommon::ProgramSampleRate ( IN AC97Register Register, IN DWORD dwSampleRate ) { PAGED_CODE (); WORD wOldRateReg, wCodecReg; NTSTATUS ntStatus; DOUT (DBG_PRINT, ("[CAdapterCommon::ProgramSampleRate]")); // // Check if we support variable sample rate. // switch(Register) { case AC97REG_MIC_SAMPLERATE: // // Variable sample rate supported? // if (GetNodeConfig (NODEC_MIC_VARIABLERATE_SUPPORTED)) { // Range supported? if (dwSampleRate > 48000ul) { // Not possible. DOUT (DBG_VSR, ("Samplerate %d not supported", dwSampleRate)); return STATUS_NOT_SUPPORTED; } } else { // Only 48000KHz possible. if (dwSampleRate != 48000ul) { DOUT (DBG_VSR, ("Samplerate %d not supported", dwSampleRate)); return STATUS_NOT_SUPPORTED; } return STATUS_SUCCESS; } break; case AC97REG_FRONT_SAMPLERATE: case AC97REG_SURROUND_SAMPLERATE: case AC97REG_LFE_SAMPLERATE: case AC97REG_RECORD_SAMPLERATE: // // Variable sample rate supported? // if (GetNodeConfig (NODEC_PCM_VARIABLERATE_SUPPORTED)) { // // Check range supported // if (dwSampleRate > 48000ul) { DOUT (DBG_VSR, ("Samplerate %d not supported", dwSampleRate)); return STATUS_NOT_SUPPORTED; } } else { // Only 48KHz possible. if (dwSampleRate != 48000ul) { DOUT (DBG_VSR, ("Samplerate %d not supported", dwSampleRate)); return STATUS_NOT_SUPPORTED; } return STATUS_SUCCESS; } break; default: DOUT (DBG_ERROR, ("Invalid sample rate register!")); return STATUS_UNSUCCESSFUL; } // // Save the old sample rate register. // ntStatus = ReadCodecRegister (Register, &wOldRateReg); if (!NT_SUCCESS (ntStatus)) return ntStatus; // // program the rate. // ntStatus = WriteCodecRegister (Register, (WORD)dwSampleRate, -1); if (!NT_SUCCESS (ntStatus)) { DOUT (DBG_ERROR, ("Cannot program sample rate.")); return ntStatus; } // // Read it back. // ntStatus = ReadCodecRegister (Register, &wCodecReg); if (!NT_SUCCESS (ntStatus)) { DOUT (DBG_ERROR, ("Cannot read sample rate.")); return ntStatus; } // // Validate. // if (wCodecReg != dwSampleRate) { // // restore sample rate and ctrl register. // WriteCodecRegister (Register, wOldRateReg, -1); DOUT (DBG_VSR, ("Samplerate %d not supported", dwSampleRate)); return STATUS_NOT_SUPPORTED; } DOUT (DBG_VSR, ("Samplerate changed to %d.", dwSampleRate)); return STATUS_SUCCESS; } /***************************************************************************** * CAdapterCommon::PowerChangeState ***************************************************************************** * Change power state for the device. We handle the codec, PowerChangeNotify * in the wave miniport handles the DMA registers. */ STDMETHODIMP_(void) CAdapterCommon::PowerChangeState ( IN POWER_STATE NewState ) { PAGED_CODE (); NTSTATUS ntStatus = STATUS_SUCCESS; DOUT (DBG_PRINT, ("[CAdapterCommon::PowerChangeNotify]")); // // Check to see if this is the current power state. // if (NewState.DeviceState == m_PowerState) { DOUT (DBG_POWER, ("New device state equals old state.")); return; } // // Check the new device state. // if ((NewState.DeviceState < PowerDeviceD0) || (NewState.DeviceState > PowerDeviceD3)) { DOUT (DBG_ERROR, ("Unknown device state: D%d.", (ULONG)NewState.DeviceState - (ULONG)PowerDeviceD0)); return; } DOUT (DBG_POWER, ("Changing state to D%d.", (ULONG)NewState.DeviceState - (ULONG)PowerDeviceD0)); // // Switch on new state. // switch (NewState.DeviceState) { case PowerDeviceD0: // // If we are coming from D2 or D3 we have to restore the registers cause // there might have been a power loss. // if ((m_PowerState == PowerDeviceD3) || (m_PowerState == PowerDeviceD2)) { // // Reset AD3 to indicate that we are now awake. // Because the system has only one power irp at a time, we are sure // that the modem driver doesn't get called while we are restoring // power. // WriteBMControlRegister (GLOB_STA, ReadBMControlRegister32 (GLOB_STA) & ~GLOB_STA_AD3); // // Restore codec registers. // ntStatus = RestoreCodecRegisters (); } else // We are coming from power state D1 { ntStatus = PowerUpCodec (); } // Print error code. if (!NT_SUCCESS (ntStatus)) { DOUT (DBG_ERROR, ("PowerChangeState failed to restore the codec.")); } break; case PowerDeviceD1: // // This sleep state is the lowest latency sleep state with respect // to the latency time required to return to D0. If the // driver is not being used an inactivity timer in portcls will // place the driver in this state after a timeout period // controllable via the registry. // // Let's power down the DAC/ADC's and analog mixer. WriteCodecRegister (AC97REG_POWERDOWN, 0x0700, -1); break; case PowerDeviceD2: case PowerDeviceD3: // // This is a full hibernation state and is the longest latency sleep // state. In this modes the power could be removed or reduced that // much that the AC97 controller looses information, so we save // whatever we have to save. // // // Powerdown ADC, DAC, Mixer, Vref, HP amp, and Exernal Amp but not // AC-link and Clk // WriteCodecRegister (AC97REG_POWERDOWN, 0xCF00, -1); // // Only in D3 mode we set the AD3 bit and evtl. shut off the AC link. // if (NewState.DeviceState == PowerDeviceD3) { // // Set the AD3 bit. // ULONG ulReg = ReadBMControlRegister32 (GLOB_STA); WriteBMControlRegister (GLOB_STA, ulReg | GLOB_STA_AD3); // // We check if the modem is sleeping. If it is, we can shut off the // AC link also. We shut off the AC link also if the modem is not // there. // if ((ulReg & GLOB_STA_MD3) || !(ulReg & GLOB_STA_SCR)) { // Set Codec to super sleep WriteCodecRegister (AC97REG_POWERDOWN, 0xFF00, -1); // Disable the AC-link signals ulReg = ReadBMControlRegister32 (GLOB_CNT); WriteBMControlRegister (GLOB_CNT, (ulReg | GLOB_CNT_ACLOFF) & ~GLOB_CNT_COLD); } } break; } // // Save the new state. This local value is used to determine when to // cache property accesses and when to permit the driver from accessing // the hardware. // m_PowerState = NewState.DeviceState; DOUT (DBG_POWER, ("Entering D%d", (ULONG)m_PowerState - (ULONG)PowerDeviceD0)); } /***************************************************************************** * CAdapterCommon::QueryPowerChangeState ***************************************************************************** * Query to see if the device can change to this power state */ STDMETHODIMP_(NTSTATUS) CAdapterCommon::QueryPowerChangeState ( IN POWER_STATE NewState ) { PAGED_CODE (); DOUT (DBG_PRINT, ("[CAdapterCommon::QueryPowerChangeState]")); // Check here to see of a legitimate state is being requested // based on the device state and fail the call if the device/driver // cannot support the change requested. Otherwise, return STATUS_SUCCESS. // Note: A QueryPowerChangeState() call is not guaranteed to always preceed // a PowerChangeState() call. // check the new state being requested switch (NewState.DeviceState) { case PowerDeviceD0: case PowerDeviceD1: case PowerDeviceD2: case PowerDeviceD3: return STATUS_SUCCESS; default: DOUT (DBG_ERROR, ("Unknown device state: D%d.", (ULONG)NewState.DeviceState - (ULONG)PowerDeviceD0)); return STATUS_NOT_IMPLEMENTED; } } /***************************************************************************** * CAdapterCommon::QueryDeviceCapabilities ***************************************************************************** * Called at startup to get the caps for the device. This structure provides * the system with the mappings between system power state and device power * state. This typically will not need modification by the driver. * If the driver modifies these mappings then the driver is not allowed to * change the mapping to a weaker power state (e.g. from S1->D3 to S1->D1). * */ STDMETHODIMP_(NTSTATUS) CAdapterCommon::QueryDeviceCapabilities ( IN PDEVICE_CAPABILITIES PowerDeviceCaps ) { PAGED_CODE (); DOUT (DBG_PRINT, ("[CAdapterCommon::QueryDeviceCapabilities]")); return STATUS_SUCCESS; } /***************************************************************************** * CAdapterCommon::RestoreAC97Registers ***************************************************************************** * Preset the AC97 registers with default values. The routine first checks if * There are registry entries for the default values. If not, we have hard * coded values too ;) */ NTSTATUS CAdapterCommon::SetAC97Default (void) { PAGED_CODE (); PREGISTRYKEY DriverKey; PREGISTRYKEY SettingsKey; UNICODE_STRING sKeyName; ULONG ulDisposition; ULONG ulResultLength; PVOID KeyInfo = NULL; DOUT (DBG_PRINT, ("[CAdapterCommon::SetAC97Default]")); // open the driver registry key NTSTATUS ntStatus = PcNewRegistryKey (&DriverKey, // IRegistryKey NULL, // OuterUnknown DriverRegistryKey, // Registry key type KEY_READ, // Access flags m_pDeviceObject, // Device object NULL, // Subdevice NULL, // ObjectAttributes 0, // Create options NULL); // Disposition if (NT_SUCCESS (ntStatus)) { // make a unicode string for the subkey name RtlInitUnicodeString (&sKeyName, L"Settings"); // open the settings subkey ntStatus = DriverKey->NewSubKey (&SettingsKey, // Subkey NULL, // OuterUnknown KEY_READ, // Access flags &sKeyName, // Subkey name REG_OPTION_NON_VOLATILE, // Create options &ulDisposition); if (NT_SUCCESS (ntStatus)) { // allocate data to hold key info KeyInfo = ExAllocatePool (PagedPool, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(WORD)); if (NULL != KeyInfo) { // loop through all mixer settings for (AC97Register i = AC97REG_RESET; i <= AC97REG_RESERVED2; i = (AC97Register)(i + 1)) { if (m_stAC97Registers[i].wFlags & SHREG_INIT) { // init key name RtlInitUnicodeString (&sKeyName, m_stAC97Registers[i].sRegistryName); // query the value key ntStatus = SettingsKey->QueryValueKey (&sKeyName, KeyValuePartialInformation, KeyInfo, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(WORD), &ulResultLength); if (NT_SUCCESS (ntStatus)) { PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyInfo; if (PartialInfo->DataLength == sizeof(WORD)) { // set mixer register to registry value WriteCodecRegister (i, *(PWORD)PartialInfo->Data, -1); } else // write the hard coded default { // if key access failed, set to default WriteCodecRegister (i, m_stAC97Registers[i].wWantedDefault, -1); } } else // write the hard coded default { // if key access failed, set to default WriteCodecRegister (i, m_stAC97Registers[i].wWantedDefault, -1); } } } // we want to return status success even if the last QueryValueKey // failed. ntStatus = STATUS_SUCCESS; // free the key info ExFreePool (KeyInfo); } // release the settings key SettingsKey->Release (); } // release the driver key DriverKey->Release (); } // in case we did not query the registry (cause of lack of resources) // restore default values and return insufficient resources. if (!NT_SUCCESS (ntStatus) || !KeyInfo) { // copy hard coded default settings for (AC97Register i = AC97REG_RESET; i < AC97REG_RESERVED2; i = (AC97Register)(i + 1)) { if (m_stAC97Registers[i].wFlags & SHREG_INIT) { WriteCodecRegister (i, m_stAC97Registers[i].wWantedDefault, -1); } } ntStatus = STATUS_INSUFFICIENT_RESOURCES; } return ntStatus; } /***************************************************************************** * CAdapterCommon::DisableAC97Pin ***************************************************************************** * Returns TRUE when the HW vendor wants to disable the pin. A disabled pin is * not shown to the user (means it is not included in the topology). The * reason for doing this could be that some of the input lines like Aux or * Video are not available to the user (to plug in something) but the codec * can handle those lines. */ BOOL CAdapterCommon::DisableAC97Pin ( IN TopoPinConfig pin ) { PAGED_CODE (); PREGISTRYKEY DriverKey; PREGISTRYKEY SettingsKey; UNICODE_STRING sKeyName; ULONG ulDisposition; ULONG ulResultLength; PVOID KeyInfo = NULL; BOOL bDisable = FALSE; DOUT (DBG_PRINT, ("[CAdapterCommon::DisableAC97Pin]")); // open the driver registry key NTSTATUS ntStatus = PcNewRegistryKey (&DriverKey, // IRegistryKey NULL, // OuterUnknown DriverRegistryKey, // Registry key type KEY_READ, // Access flags m_pDeviceObject, // Device object NULL, // Subdevice NULL, // ObjectAttributes 0, // Create options NULL); // Disposition if (NT_SUCCESS (ntStatus)) { // make a unicode string for the subkey name RtlInitUnicodeString (&sKeyName, L"Settings"); // open the settings subkey ntStatus = DriverKey->NewSubKey (&SettingsKey, // Subkey NULL, // OuterUnknown KEY_READ, // Access flags &sKeyName, // Subkey name REG_OPTION_NON_VOLATILE, // Create options &ulDisposition); if (NT_SUCCESS (ntStatus)) { // allocate data to hold key info KeyInfo = ExAllocatePool (PagedPool, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(BYTE)); if (NULL != KeyInfo) { // init key name RtlInitUnicodeString (&sKeyName, m_stHardwareConfig. Pins[pin].sRegistryName); // query the value key ntStatus = SettingsKey->QueryValueKey (&sKeyName, KeyValuePartialInformation, KeyInfo, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(BYTE), &ulResultLength ); if (NT_SUCCESS (ntStatus)) { PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyInfo; if (PartialInfo->DataLength == sizeof(BYTE)) { // store the value if (*(PBYTE)PartialInfo->Data) bDisable = TRUE; else bDisable = FALSE; } } // free the key info ExFreePool (KeyInfo); } // release the settings key SettingsKey->Release (); } // release the driver key DriverKey->Release (); } // if one of the stuff above fails we return the default, which is FALSE. return bDisable; } /***************************************************************************** * CAdapterCommon::RestoreCodecRegisters ***************************************************************************** * write back cached mixer values to codec registers */ NTSTATUS CAdapterCommon::RestoreCodecRegisters (void) { PAGED_CODE (); DOUT (DBG_PRINT, ("[CAdapterCommon::RestoreCodecRegisters]")); // // Initialize the AC97 codec. // NTSTATUS ntStatus = InitAC97 (); if (!NT_SUCCESS (ntStatus)) return ntStatus; // // Restore all codec registers. Failure is not critical. // for (AC97Register i = AC97REG_MASTER_VOLUME; i < AC97REG_RESERVED2; i = (AC97Register)(i + 1)) { WriteCodecRegister (i, m_stAC97Registers[i].wCache, -1); } return STATUS_SUCCESS; } /***************************************************************************** * CAdapterCommon::ReadChannelConfigDefault ***************************************************************************** * This function reads the default channel config from the registry. The * registry entry "ChannelConfig" is set every every time we get a * KSPROPERTY_AUDIO_CHANNEL_CONFIG for the DAC node. * In case the key doesn't exist we assume a channel config of stereo speakers, * cause that is the default of DSOUND. */ STDMETHODIMP_(void) CAdapterCommon::ReadChannelConfigDefault ( PDWORD pdwChannelConfig, PWORD pwChannels ) { PAGED_CODE (); PREGISTRYKEY DriverKey; PREGISTRYKEY SettingsKey; UNICODE_STRING sKeyName; ULONG ulDisposition; ULONG ulResultLength; PVOID KeyInfo = NULL; DOUT (DBG_PRINT, ("[CAdapterCommon::ReadChannelConfigDefault]")); // This is the default: 2 speakers, stereo. *pdwChannelConfig = KSAUDIO_SPEAKER_STEREO; *pwChannels = 2; // open the driver registry key NTSTATUS ntStatus = PcNewRegistryKey (&DriverKey, // IRegistryKey NULL, // OuterUnknown DriverRegistryKey, // Registry key type KEY_READ, // Access flags m_pDeviceObject, // Device object NULL, // Subdevice NULL, // ObjectAttributes 0, // Create options NULL); // Disposition if (NT_SUCCESS (ntStatus)) { // make a unicode string for the subkey name RtlInitUnicodeString (&sKeyName, L"Settings"); // open the settings subkey ntStatus = DriverKey->NewSubKey (&SettingsKey, // Subkey NULL, // OuterUnknown KEY_READ, // Access flags &sKeyName, // Subkey name REG_OPTION_NON_VOLATILE, // Create options &ulDisposition); if (NT_SUCCESS (ntStatus)) { // allocate data to hold key info KeyInfo = ExAllocatePool (PagedPool, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(DWORD)); if (NULL != KeyInfo) { // init key name RtlInitUnicodeString (&sKeyName, L"ChannelConfig"); // query the value key ntStatus = SettingsKey->QueryValueKey (&sKeyName, KeyValuePartialInformation, KeyInfo, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(DWORD), &ulResultLength ); if (NT_SUCCESS (ntStatus)) { PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyInfo; if (PartialInfo->DataLength == sizeof(DWORD)) { switch (*(PLONG)PartialInfo->Data) { case KSAUDIO_SPEAKER_QUAD: case KSAUDIO_SPEAKER_SURROUND: if (GetPinConfig (PINC_SURROUND_PRESENT)) { *pdwChannelConfig = *(PDWORD)PartialInfo->Data; *pwChannels = 4; } break; case KSAUDIO_SPEAKER_5POINT1: if (GetPinConfig (PINC_SURROUND_PRESENT) && GetPinConfig (PINC_CENTER_LFE_PRESENT)) { *pdwChannelConfig = *(PDWORD)PartialInfo->Data; *pwChannels = 6; } break; } } } // free the key info ExFreePool (KeyInfo); } // release the settings key SettingsKey->Release (); } // release the driver key DriverKey->Release (); } } /***************************************************************************** * CAdapterCommon::WriteChannelConfigDefault ***************************************************************************** * This function writes the default channel config to the registry. The * registry entry "ChannelConfig" is set every every time we get a * KSPROPERTY_AUDIO_CHANNEL_CONFIG for the DAC node. */ STDMETHODIMP_(void) CAdapterCommon::WriteChannelConfigDefault (DWORD dwChannelConfig) { PAGED_CODE (); PREGISTRYKEY DriverKey; PREGISTRYKEY SettingsKey; UNICODE_STRING sKeyName; ULONG ulDisposition; DOUT (DBG_PRINT, ("[CAdapterCommon::WriteChannelConfigDefault]")); // open the driver registry key NTSTATUS ntStatus = PcNewRegistryKey (&DriverKey, // IRegistryKey NULL, // OuterUnknown DriverRegistryKey, // Registry key type KEY_WRITE, // Access flags m_pDeviceObject, // Device object NULL, // Subdevice NULL, // ObjectAttributes 0, // Create options NULL); // Disposition if (NT_SUCCESS (ntStatus)) { // make a unicode string for the subkey name RtlInitUnicodeString (&sKeyName, L"Settings"); // open the settings subkey ntStatus = DriverKey->NewSubKey (&SettingsKey, // Subkey NULL, // OuterUnknown KEY_WRITE, // Access flags &sKeyName, // Subkey name REG_OPTION_NON_VOLATILE, // Create options &ulDisposition); if (NT_SUCCESS (ntStatus)) { // init key name RtlInitUnicodeString (&sKeyName, L"ChannelConfig"); // query the value key ntStatus = SettingsKey->SetValueKey (&sKeyName, REG_DWORD, &dwChannelConfig, sizeof (DWORD)); if (!NT_SUCCESS (ntStatus)) { DOUT (DBG_ERROR, ("Could not write the ChannelConfig to registry.")); } // release the settings key SettingsKey->Release (); } // release the driver key DriverKey->Release (); } } /***************************************************************************** * Non paged code begins here ***************************************************************************** */ #pragma code_seg() /***************************************************************************** * CAdapterCommon::WriteBMControlRegister ***************************************************************************** * Writes a byte (UCHAR) to BusMaster Control register. */ STDMETHODIMP_(void) CAdapterCommon::WriteBMControlRegister ( IN ULONG ulOffset, IN UCHAR ucValue ) { DOUT (DBG_PRINT, ("[CAdapterCommon::WriteBMControlRegister] (UCHAR)")); WRITE_PORT_UCHAR ((PUCHAR)(m_pBusMasterBase + ulOffset), ucValue); DOUT (DBG_REGS, ("WriteBMControlRegister wrote 0x%2x to 0x%4x.", ucValue, m_pBusMasterBase + ulOffset)); } /***************************************************************************** * CAdapterCommon::WriteBMControlRegister ***************************************************************************** * Writes a word (USHORT) to BusMaster Control register. */ STDMETHODIMP_(void) CAdapterCommon::WriteBMControlRegister ( IN ULONG ulOffset, IN USHORT usValue ) { DOUT (DBG_PRINT, ("[CAdapterCommon::WriteBMControlRegister (USHORT)]")); WRITE_PORT_USHORT ((PUSHORT)(m_pBusMasterBase + ulOffset), usValue); DOUT (DBG_REGS, ("WriteBMControlRegister wrote 0x%4x to 0x%4x", usValue, m_pBusMasterBase + ulOffset)); } /***************************************************************************** * CAdapterCommon::WriteBMControlRegister ***************************************************************************** * Writes a DWORD (ULONG) to BusMaster Control register. */ STDMETHODIMP_(void) CAdapterCommon::WriteBMControlRegister ( IN ULONG ulOffset, IN ULONG ulValue ) { DOUT (DBG_PRINT, ("[CAdapterCommon::WriteBMControlRegister (ULONG)]")); WRITE_PORT_ULONG ((PULONG)(m_pBusMasterBase + ulOffset), ulValue); DOUT (DBG_REGS, ("WriteBMControlRegister wrote 0x%8x to 0x%4x.", ulValue, m_pBusMasterBase + ulOffset)); } /***************************************************************************** * CAdapterCommon::ReadBMControlRegister8 ***************************************************************************** * Read a byte (UCHAR) from BusMaster Control register. */ STDMETHODIMP_(UCHAR) CAdapterCommon::ReadBMControlRegister8 ( IN ULONG ulOffset ) { UCHAR ucValue = UCHAR(-1); DOUT (DBG_PRINT, ("[CAdapterCommon::ReadBMControlRegister8]")); ucValue = READ_PORT_UCHAR ((PUCHAR)(m_pBusMasterBase + ulOffset)); DOUT (DBG_REGS, ("ReadBMControlRegister read 0x%2x from 0x%4x.", ucValue, m_pBusMasterBase + ulOffset)); return ucValue; } /***************************************************************************** * CAdapterCommon::ReadBMControlRegister16 ***************************************************************************** * Read a word (USHORT) from BusMaster Control register. */ STDMETHODIMP_(USHORT) CAdapterCommon::ReadBMControlRegister16 ( IN ULONG ulOffset ) { USHORT usValue = USHORT(-1); DOUT (DBG_PRINT, ("[CAdapterCommon::ReadBMControlRegister16]")); usValue = READ_PORT_USHORT ((PUSHORT)(m_pBusMasterBase + ulOffset)); DOUT (DBG_REGS, ("ReadBMControlRegister read 0x%4x = 0x%4x", usValue, m_pBusMasterBase + ulOffset)); return usValue; } /***************************************************************************** * CAdapterCommon::ReadBMControlRegister32 ***************************************************************************** * Read a dword (ULONG) from BusMaster Control register. */ STDMETHODIMP_(ULONG) CAdapterCommon::ReadBMControlRegister32 ( IN ULONG ulOffset ) { ULONG ulValue = ULONG(-1); DOUT (DBG_PRINT, ("[CAdapterCommon::ReadBMControlRegister32]")); ulValue = READ_PORT_ULONG ((PULONG)(m_pBusMasterBase + ulOffset)); DOUT (DBG_REGS, ("ReadBMControlRegister read 0x%8x = 0x%4x", ulValue, m_pBusMasterBase + ulOffset)); return ulValue; }