diff options
Diffstat (limited to 'ReferenceCode/Chipset/LynxPoint/PchInit/Dxe/PchAzalia.c')
-rw-r--r-- | ReferenceCode/Chipset/LynxPoint/PchInit/Dxe/PchAzalia.c | 915 |
1 files changed, 915 insertions, 0 deletions
diff --git a/ReferenceCode/Chipset/LynxPoint/PchInit/Dxe/PchAzalia.c b/ReferenceCode/Chipset/LynxPoint/PchInit/Dxe/PchAzalia.c new file mode 100644 index 0000000..a0c7535 --- /dev/null +++ b/ReferenceCode/Chipset/LynxPoint/PchInit/Dxe/PchAzalia.c @@ -0,0 +1,915 @@ +/** @file + Initializes the PCH Azalia codec. + +@copyright + Copyright (c) 1999 - 2013 Intel Corporation. All rights reserved + This software and associated documentation (if any) is furnished + under a license and may only be used or copied in accordance + with the terms of the license. Except as permitted by such + license, no part of this software or documentation may be + reproduced, stored in a retrieval system, or transmitted in any + form or by any means without the express written consent of + Intel Corporation. + + This file contains an 'Intel Peripheral Driver' and uniquely + identified as "Intel Reference Module" and is + licensed for Intel CPUs and chipsets under the terms of your + license agreement with Intel or your vendor. This file may + be modified by the user, subject to additional terms of the + license agreement + +**/ +#include "PchInit.h" + +/** + Polling the Status bit + + @param[in] StatusReg The regsiter address to read the status + @param[in] PollingBitMap The bit mapping for polling + @param[in] PollingData The Data for polling + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_TIMEOUT Polling the bit map time out +**/ +EFI_STATUS +StatusPolling ( + IN UINT32 StatusReg, + IN UINT16 PollingBitMap, + IN UINT16 PollingData + ) +{ + UINT32 LoopTime; + + for (LoopTime = 0; LoopTime < AZALIA_MAX_LOOP_TIME; LoopTime++) { + if ((MmioRead16 (StatusReg) & PollingBitMap) == PollingData) { + break; + } else { + PchPmTimerStall (AZALIA_WAIT_PERIOD); + } + } + + if (LoopTime >= AZALIA_MAX_LOOP_TIME) { + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +/** + Send the command to the codec via the Immediate Command mechanism is written + to the IC register + + @param[in] HdaBar Base address of Intel HD Audio memory mapped configuration registers + @param[in, out] CodecCommandData The Codec Command to be sent to the codec + @param[in] ReadBack Whether to get the response received from the codec + + @retval EFI_DEVICE_ERROR Device status error, operation failed + @retval EFI_SUCCESS The function completed successfully +**/ +EFI_STATUS +SendCodecCommand ( + IN UINT32 HdaBar, + IN OUT UINT32 *CodecCommandData, + IN BOOLEAN ReadBack + ) +{ + EFI_STATUS Status; + + Status = StatusPolling (HdaBar + R_HDA_IRS, (UINT16) B_HDA_IRS_ICB, (UINT16) 0); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "ICB bit is not zero before SendCodecCommand! \n")); + return EFI_DEVICE_ERROR; + } + + MmioWrite32 (HdaBar + R_HDA_IC, *CodecCommandData); + MmioOr16 ((UINTN) (HdaBar + R_HDA_IRS), (UINT16) ((B_HDA_IRS_IRV | B_HDA_IRS_ICB))); + + Status = StatusPolling (HdaBar + R_HDA_IRS, (UINT16) B_HDA_IRS_ICB, (UINT16) 0); + if (EFI_ERROR (Status)) { + MmioAnd16 ((UINTN) (HdaBar + R_HDA_IRS), (UINT16)~(B_HDA_IRS_ICB)); + return Status; + } + + if (ReadBack == TRUE) { + if ((MmioRead16 (HdaBar + R_HDA_IRS) & B_HDA_IRS_IRV) != 0) { + *CodecCommandData = MmioRead32 (HdaBar + R_HDA_IR); + } else { + DEBUG ((EFI_D_ERROR, "SendCodecCommand: ReadBack fail! \n")); + return EFI_DEVICE_ERROR; + } + } + + return EFI_SUCCESS; +} + +/** + Set a "Send Codec Command" S3 dispatch item + + @param[in] HdaBar Base address of Intel HD Audio memory mapped configuration registers + @param[in, out] CodecCommandData The Codec Command to be sent to the codec + + @retval EFI_SUCCESS The function completed successfully +**/ +EFI_STATUS +SendCodecCommandS3Item ( + IN UINT32 HdaBar, + IN OUT UINT32 CodecCommandData + ) +{ + EFI_STATUS Status; +#ifdef EFI_S3_RESUME + STATIC EFI_PCH_S3_SUPPORT_PROTOCOL *PchS3Support; + STATIC EFI_PCH_S3_PARAMETER_SEND_CODEC_COMMAND S3ParameterSendCodecCommand; + STATIC EFI_PCH_S3_DISPATCH_ITEM S3DispatchItem = { + PchS3ItemTypeSendCodecCommand, + &S3ParameterSendCodecCommand + }; + EFI_PHYSICAL_ADDRESS S3DispatchEntryPoint; + + if (!PchS3Support) { + /// + /// Get the PCH S3 Support Protocol + /// + Status = gBS->LocateProtocol ( + &gEfiPchS3SupportProtocolGuid, + NULL, + (VOID **) &PchS3Support + ); + ASSERT_EFI_ERROR (Status); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + S3ParameterSendCodecCommand.HdaBar = HdaBar; + S3ParameterSendCodecCommand.CodecCmdData = CodecCommandData; + Status = PchS3Support->SetDispatchItem ( + PchS3Support, + &S3DispatchItem, + &S3DispatchEntryPoint + ); + ASSERT_EFI_ERROR (Status); + + /// + /// Save the script dispatch item in the Boot Script + /// + SCRIPT_DISPATCH (EFI_ACPI_S3_RESUME_SCRIPT_TABLE, S3DispatchEntryPoint); +#else + Status = EFI_SUCCESS; +#endif + return Status; +} + +/** + Initialize the Intel High Definition Audio Codec(s) present in the system. + For each codec, a predefined codec verb table should be programmed. + The list contains 32-bit verbs to be sent to the corresponding codec. + If it is not programmed, the codec uses the default verb table, which may or may not + correspond to the platform jack information. + + @param[in] PchPlatformPolicy The PCH Platform Policy protocol instance + @param[in] RootComplexBar RootComplexBar address of this PCH device + @param[in, out] AzaliaStarted Whether Azalia is successfully started + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Provided VerbTableData is null +**/ +EFI_STATUS +DetectAndInitializeAzalia ( + IN DXE_PCH_PLATFORM_POLICY_PROTOCOL *PchPlatformPolicy, + IN UINT32 RootComplexBar, + IN OUT BOOLEAN *AzaliaStarted + ) +{ + EFI_STATUS Status; + UINT32 Index; + UINT32 VendorDeviceId; + UINT32 RevisionId; + UINT8 ByteReg; + UINTN AzaliaBase; + UINT8 AzaliaSDINo; + UINT32 HdaBar; + UINT32 *VerbTable; + UINT32 LoopTime; + PCH_AZALIA_VERB_TABLE_HEADER *VerbHeaderTable; + EFI_PHYSICAL_ADDRESS BaseAddressBarMem; + UINT8 VerbTableNum; + PCH_AZALIA_CONFIG *AzaliaConfig; + UINT32 Data32And; + UINT32 Data32Or; + UINT16 Data16And; + UINT16 Data16Or; + UINT8 Data8And; + UINT8 Data8Or; + UINT32 CodecCmdData; + UINTN PciD31F0RegBase; + UINT16 LpcDeviceId; + UINT16 Data16; + UINT16 BitMask; + UINT16 BitValue; + PCH_SERIES PchSeries; + + PchSeries = GetPchSeries(); + AzaliaConfig = PchPlatformPolicy->AzaliaConfig; + AzaliaBase = MmPciAddress ( + 0, + PchPlatformPolicy->BusNumber, + PCI_DEVICE_NUMBER_PCH_AZALIA, + PCI_FUNCTION_NUMBER_PCH_AZALIA, + 0 + ); + PciD31F0RegBase = MmPciAddress ( + 0, + PchPlatformPolicy->BusNumber, + PCI_DEVICE_NUMBER_PCH_LPC, + PCI_FUNCTION_NUMBER_PCH_LPC, + 0 + ); + LpcDeviceId = MmioRead16 (PciD31F0RegBase + R_PCH_LPC_DEVICE_ID); + + Data32And = 0xF8FFFF01; + if ((MmioRead32 ((UINTN) (RootComplexBar + R_PCH_RCRB_CIR2030)) & (UINT32) BIT31) != 0) { + /// + /// PCH BIOS Spec Rev 0.5.0 Section 9.4.2 High Definition Audio VCi Configuration + /// For Sever + /// Step 1 + /// Configure and enable Vcp on DMI, done on PchDmiPeim.c + /// Step 2 + /// Assign a Vcp ID value of 2 to High Definition Audio VCi ID field of VCi Resource Control register + /// D27:F0:Reg 120h[26:24] = 2 + /// Step 3 + /// Map Tcp to VCP. Set bit 2 of TC/VCi Map field of High Definition Audio VCi Resource Control register + /// D27:F0:Reg 120h[7:1] + /// + Data32Or = BIT25; + Data32Or |= MmioRead32 ((UINTN) (RootComplexBar + R_PCH_RCRB_CIR2030)) & V_PCH_RCRB_V1CTL_TVM_MASK; + MmioAndThenOr32 ( + (UINTN) (AzaliaBase + R_PCH_HDA_VCICTL), + Data32And, // Data to be ANDed + Data32Or // Data to be ORed + ); + SCRIPT_MEM_READ_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint32, + (UINTN) (AzaliaBase + R_PCH_HDA_VCICTL), + &Data32Or, // Data to be ORed + &Data32And // Data to be ANDed + ); + /// + /// Step 4 + /// Avoid isochronous transfers to use VC1, Clear No Snoop Enable of Device Control Register + /// D27:F0:Reg 78h[11] = 0b + /// + if (PchSeries == PchH) { + MmioAnd16 ( + (UINTN) (AzaliaBase + R_PCH_HDA_DEVC), + (UINT16) (~B_PCH_HDA_DEVC_NSNPEN) + ); + SCRIPT_MEM_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint16, + (UINTN) (AzaliaBase + R_PCH_HDA_DEVC), + 1, + (VOID *) (UINTN) (AzaliaBase + R_PCH_HDA_DEVC) + ); + } + } + if ((MmioRead32 ((UINTN) (AzaliaBase + R_PCH_HDA_VCICTL)) & B_PCH_HDA_VCICTL_ID) != 0) { + /// + /// Step 5 + /// Clear the TC/VC0 Map field of VC0 Resource Control register + /// D27:F0:Reg 114h[7:1] = 0 + /// + MmioAnd32 ( + (UINTN) (AzaliaBase + R_PCH_HDA_VC0CTL), + (UINT32) (~B_PCH_HDA_VC0CTL_TCVC0_MAP) + ); + SCRIPT_MEM_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint32, + (UINTN) (AzaliaBase + R_PCH_HDA_VC0CTL), + 1, + (VOID *) (UINTN) (AzaliaBase + R_PCH_HDA_VC0CTL) + ); + /// + /// Step 6 + /// For LPT-H, Set VCi Enable bit of VCi Resource Control register + /// D27:F0:Reg 120h[31] = 1 + /// For LPT-LP, Clear VCi Enable bit of VCi Resource Control register + /// D27:F0:Reg 120h[31] = 0 + /// + if (PchSeries == PchH) { + MmioOr32 ((UINTN) (AzaliaBase + R_PCH_HDA_VCICTL), (UINT32) (B_PCH_HDA_VCICTL_EN)); + } + if (PchSeries == PchLp) { + MmioAnd32 ((UINTN) (AzaliaBase + R_PCH_HDA_VCICTL), (UINT32)~(B_PCH_HDA_VCICTL_EN)); + } + SCRIPT_MEM_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint32, + (UINTN) (AzaliaBase + R_PCH_HDA_VCICTL), + 1, + (VOID *) (UINTN) (AzaliaBase + R_PCH_HDA_VCICTL) + ); + } + /// + /// Firstly Initialize Azalia to be not started. + /// + *AzaliaStarted = FALSE; + + /// + /// Allocate resource for HDBAR + /// + BaseAddressBarMem = 0x0FFFFFFFF; + Status = gDS->AllocateMemorySpace ( + EfiGcdAllocateMaxAddressSearchBottomUp, + EfiGcdMemoryTypeMemoryMappedIo, + 14, + V_PCH_HDA_HDBAR_SIZE, + &BaseAddressBarMem, + mImageHandle, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + /// + /// System BIOS should ensure that the High Definition Audio HDBAR D27:F0:Reg 10-17h contains a valid address value + /// and is enabled by setting D27:F0:Reg 04h[1]. + /// + HdaBar = (UINT32) BaseAddressBarMem; + MmioWrite32 (AzaliaBase + R_PCH_HDA_HDBARL, HdaBar); + SCRIPT_MEM_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint32, + (UINTN) (AzaliaBase + R_PCH_HDA_HDBARL), + 1, + (VOID *) (UINTN) (AzaliaBase + R_PCH_HDA_HDBARL) + ); + + MmioWrite32 (AzaliaBase + R_PCH_HDA_HDBARU, 0); + SCRIPT_MEM_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint32, + (UINTN) (AzaliaBase + R_PCH_HDA_HDBARU), + 1, + (VOID *) (UINTN) (AzaliaBase + R_PCH_HDA_HDBARU) + ); + + MmioOr16 ((UINTN) (AzaliaBase + R_PCH_HDA_COMMAND), (UINT16) B_PCH_HDA_COMMAND_MSE); + SCRIPT_MEM_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint16, + (UINTN) (AzaliaBase + R_PCH_HDA_COMMAND), + 1, + (VOID *) (UINTN) (AzaliaBase + R_PCH_HDA_COMMAND) + ); + + /// + /// PCH BIOS Spec Rev 0.5.0 Section 9.5 + /// Additional High Definition Audio Programming Steps + /// + if(PchSeries == PchH) { + /// + /// Step 1 + /// Set D27:F0:43h[4] = 1b + /// + Data8And = (UINT8) ~0; + Data8Or = BIT4; + MmioOr8 ((UINTN) (AzaliaBase + 0x43), Data8Or); + SCRIPT_MEM_READ_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint8, + (UINTN) (AzaliaBase + 0x43), + &Data8Or, // Data to be ORed + &Data8And // Data to be ANDed + ); + /// + /// Step 2 + /// Set D27:F0:C0h[17] = 1b + /// + Data32And = (UINT32) ~0; + Data32Or = BIT17; + MmioOr32 ((UINTN) (AzaliaBase + 0xC0), Data32Or); + SCRIPT_MEM_READ_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint32, + (UINTN) (AzaliaBase + 0xC0), + &Data32Or, // Data to be ORed + &Data32And // Data to be ANDed + ); + } + /// + /// For LPT-LP, clear D27:F0:43h[6] = 0b + /// + if(PchSeries == PchLp) { + /// + /// Step 1 + /// Set D27:F0:43h[6] = 0b + /// + MmioAnd8 ((UINTN) (AzaliaBase + 0x43), (UINT8) (~BIT6)); + SCRIPT_MEM_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint8, + (UINTN) (AzaliaBase + 0x43), + 1, + (VOID *) (UINTN) (AzaliaBase + 0x43) + ); + } + + /// + /// Step 3 + /// For LPT-H, Set D27:F0:C4h[14] = 1b + /// For LPT-LP, Set D27:F0:C4h[24] = 1b + /// + Data32And = (UINT32) ~0; + Data32Or = 0; + if (PchSeries == PchH) { + Data32Or |= BIT14; + } + if (PchSeries == PchLp) { + Data32Or |= BIT24; + } + MmioOr32 ((UINTN) (AzaliaBase + 0xC4), Data32Or); + SCRIPT_MEM_READ_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint32, + (UINTN) (AzaliaBase + 0xC4), + &Data32Or, // Data to be ORed + &Data32And // Data to be ANDed + ); + + if (PchSeries == PchH) { + /// + /// Step 4 + /// Set D27:F0:D0h[31] = 0b + /// + Data32And = ~BIT31; + Data32Or = (UINT32) 0; + MmioAnd32 ((UINTN) (AzaliaBase + 0xD0), Data32And); + SCRIPT_MEM_READ_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint32, + (UINTN) (AzaliaBase + 0xD0), + &Data32Or, // Data to be ORed + &Data32And // Data to be ANDed + ); + } + + if (AzaliaConfig->DS == PCH_DEVICE_DISABLE) { + MmioAnd8 ((UINTN) (AzaliaBase + R_PCH_HDA_DCKSTS), (UINT8) (~B_PCH_HDA_DCKSTS_DS)); + SCRIPT_MEM_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint8, + (UINTN) (AzaliaBase + R_PCH_HDA_DCKSTS), + 1, + (VOID *) (UINTN) (AzaliaBase + R_PCH_HDA_DCKSTS) + ); + } else if (AzaliaConfig->DA != PCH_DEVICE_DISABLE) { + if ((MmioRead8 (AzaliaBase + R_PCH_HDA_DCKSTS) & B_PCH_HDA_DCKSTS_DM) == 0) { + MmioOr8 ((UINTN) (AzaliaBase + R_PCH_HDA_DCKCTL), (UINT8) (B_PCH_HDA_DCKCTL_DA)); + SCRIPT_MEM_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint8, + (UINTN) (AzaliaBase + R_PCH_HDA_DCKCTL), + 1, + (VOID *) (UINTN) (AzaliaBase + R_PCH_HDA_DCKCTL) + ); + } + } + + if (PchSeries == PchLp) { + /// + /// @todo: Policy check to bypass for PO + /// Set Hdabar + 0x0012h[0] to 1b + /// + Data16And = (UINT16)~BIT0; + Data16Or = (UINT16) (BIT0); + MmioOr16 ((UINTN) (HdaBar + 0x0012), Data16Or); + SCRIPT_MEM_READ_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint16, + (UINTN) (HdaBar + 0x0012), + &Data16Or, // Data to be ORed + &Data16And // Data to be ANDed + ); + + /// + /// W/A: Azalia BCLK is not at full swing when operating in high voltage mode + /// Set D27:F0:42h[2] = 1b - disabling Auto Voltage Detector. + /// + MmioOr8 ((UINTN)(AzaliaBase + R_PCH_HDA_AZIOBC), (UINT8)B_PCH_HDA_AZIOBC_AVDDIS); + SCRIPT_MEM_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint8, + (UINTN) (AzaliaBase + R_PCH_HDA_AZIOBC), + 1, + (VOID *) (UINTN) (AzaliaBase + R_PCH_HDA_AZIOBC) + ); + } + /// + /// PCH BIOS Spec Rev 0.5.0 Section 9.1.3 Codec Initialization Programming Sequence + /// System BIOS should also ensure that the Controller Reset# bit of Global Control register + /// in memory-mapped space (HDBAR+08h[0]) is set to 1 and read back as 1. + /// Deassert the HDA controller RESET# to start up the link + /// + Data32And = 0xFFFFFFFF; + Data32Or = (UINT32) (B_HDA_GCTL_CRST); + MmioOr32 ((UINTN) (HdaBar + R_HDA_GCTL), Data32Or); + SCRIPT_MEM_READ_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint32, + (UINTN) (HdaBar + R_HDA_GCTL), + &Data32Or, // Data to be ORed + &Data32And // Data to be ANDed + ); + + BitMask = (UINT16) B_HDA_GCTL_CRST; + BitValue = (UINT16) B_HDA_GCTL_CRST; + Status = StatusPolling (HdaBar + R_HDA_GCTL, BitMask, BitValue); + SCRIPT_MEM_POLL ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint16, + HdaBar + R_HDA_GCTL, + &BitMask, + &BitValue, + AZALIA_WAIT_PERIOD, + AZALIA_MAX_LOOP_TIME + ); + /// + /// PCH BIOS Spec Rev 0.5.0 Section 9.1.3 Codec Initialization Programming Sequence + /// Read GCAP and write the same value back to the register once after Controller Reset# bit is set + /// + Data16 = MmioRead16 (HdaBar + R_HDA_GCAP); + MmioWrite16 (HdaBar + R_HDA_GCAP, Data16); + SCRIPT_MEM_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint16, + (UINTN) (HdaBar + R_HDA_GCAP), + 1, + (VOID *) (UINTN) (HdaBar + R_HDA_GCAP) + ); + + /// + /// Clear the "State Change Status Register" STATESTS bits for + /// each of the "SDIN Stat Change Status Flag" + /// + Data16 = AZALIA_MAX_SID_MASK_PCH_H; + if (PchSeries == PchLp) { + Data16 = AZALIA_MAX_SID_MASK_PCH_LP; + } + MmioOr8 ((UINTN) (HdaBar + R_HDA_STATESTS), (UINT8) (Data16)); + SCRIPT_MEM_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint8, + (UINTN) (HdaBar + R_HDA_STATESTS), + 1, + (VOID *) (UINTN) (HdaBar + R_HDA_STATESTS) + ); + + /// + /// Turn off the link and poll RESET# bit until it reads back as 0 to get hardware reset report + /// + Data32And = (UINT32) (~B_HDA_GCTL_CRST); + Data32Or = (UINT32) 0; + MmioAnd32 ((UINTN) (HdaBar + R_HDA_GCTL), Data32And); + SCRIPT_MEM_READ_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint32, + (UINTN) (HdaBar + R_HDA_GCTL), + &Data32Or, // Data to be ORed + &Data32And // Data to be ANDed + ); + + BitMask = (UINT16) B_HDA_GCTL_CRST; + BitValue = 0; + Status = StatusPolling (HdaBar + R_HDA_GCTL, BitMask, BitValue); + SCRIPT_MEM_POLL ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint16, + HdaBar + R_HDA_GCTL, + &BitMask, + &BitValue, + AZALIA_WAIT_PERIOD, + AZALIA_MAX_LOOP_TIME + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Reset High Definition Audio (Azalia) Codec Time Out - 1! \n")); + goto ExitInitAzalia; + } + /// + /// Turn on the link and poll RESET# bit until it reads back as 1 + /// + Data32And = 0xFFFFFFFF; + Data32Or = (UINT32) (B_HDA_GCTL_CRST); + MmioOr32 ((UINTN) (HdaBar + R_HDA_GCTL), Data32Or); + SCRIPT_MEM_READ_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint32, + (UINTN) (HdaBar + R_HDA_GCTL), + &Data32Or, // Data to be ORed + &Data32And // Data to be ANDed + ); + /// + /// For some combo card that will need this delay because each codec has different latency to come out from RESET. + /// This delay can make sure all codecs be recognized by BIOS after RESET sequence. + /// Additional delay might be required to allow codec coming out of reset prior to subsequent operations, + /// please contact your codec vendor for detail. When clearing this bit and setting it afterward, + /// BIOS must ensure that minimum link timing requirements (minimum RESET# assertion time, etc.) are met.. + /// + PchPmTimerStall (AzaliaConfig->ResetWaitTimer); + SCRIPT_STALL (EFI_ACPI_S3_RESUME_SCRIPT_TABLE, AzaliaConfig->ResetWaitTimer); + + BitMask = (UINT16) B_HDA_GCTL_CRST; + BitValue = (UINT16) B_HDA_GCTL_CRST; + Status = StatusPolling (HdaBar + R_HDA_GCTL, BitMask, BitValue); + SCRIPT_MEM_POLL ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint16, + HdaBar + R_HDA_GCTL, + &BitMask, + &BitValue, + AZALIA_WAIT_PERIOD, + AZALIA_MAX_LOOP_TIME + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Reset High Definition Audio (Azalia) Codec Time Out - 2! \n")); + goto ExitInitAzalia; + } + /// + /// Read the "State Change Status Register" STATESTS bits twice to find out if any SDIN is connected + /// to a codec. + /// + Data16 = AZALIA_MAX_SID_MASK_PCH_H; + if (PchSeries == PchLp) { + Data16 = AZALIA_MAX_SID_MASK_PCH_LP; + } + for (LoopTime = 0, ByteReg = 0, AzaliaSDINo = 0; LoopTime < AZALIA_MAX_LOOP_TIME; LoopTime++) { + ByteReg = (UINT8)(MmioRead8 (HdaBar + R_HDA_STATESTS) & Data16); + if (ByteReg != 0 && (ByteReg == AzaliaSDINo)) { + break; + } else { + AzaliaSDINo = ByteReg; + } + + PchPmTimerStall (AZALIA_WAIT_PERIOD); + } + /// + /// BIT3(1000) -- SDI3 + /// BIT2(0100) -- SDI2 + /// BIT1(0010) -- SDI1 + /// BIT0(0001) -- SDI0 + /// + if (ByteReg == 0) { + /// + /// No Azalia Detected + /// + /// + /// Turn off the link + /// + DEBUG ((EFI_D_ERROR, "No Azalia device is detected.\n")); + Data32And = (UINT32) (~B_HDA_GCTL_CRST); + Data32Or = (UINT32) 0; + MmioAnd32 ((UINTN) (HdaBar + R_HDA_GCTL), Data32And); + SCRIPT_MEM_READ_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint32, + (UINTN) (HdaBar + R_HDA_GCTL), + &Data32Or, // Data to be ORed + &Data32And // Data to be ANDed + ); + Status = EFI_DEVICE_ERROR; + goto ExitInitAzalia; + } + /// + /// PME Enable for Audio controller, this bit is in the resume well + /// + if (AzaliaConfig->Pme == PCH_DEVICE_ENABLE) { + MmioOr32 ((UINTN) (AzaliaBase + R_PCH_HDA_PCS), (UINT32) (B_PCH_HDA_PCS_PMEE)); +#ifdef SUS_WELL_RESTORE + /// + /// To support RapidStart resume from G3 state, all resume well registers need to be saved + /// into S3 Script table. + /// + SCRIPT_MEM_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint32, + (UINTN) (AzaliaBase + R_PCH_HDA_PCS), + 1, + (VOID *) (UINTN) (AzaliaBase + R_PCH_HDA_PCS) + ); +#endif + } + + Data16 = AZALIA_MAX_SID_NUMBER_PCH_H; + if (PchSeries == PchLp) { + Data16 = AZALIA_MAX_SID_NUMBER_PCH_LP; + } + for (AzaliaSDINo = 0; AzaliaSDINo < Data16; AzaliaSDINo++, ByteReg >>= 1) { + if ((ByteReg & 0x1) == 0) { + /// + /// SDIx has no Azalia Device + /// + DEBUG ((EFI_D_ERROR, "SDI%d has no Azalia device.\n", AzaliaSDINo)); + continue; + } + /// + /// PME Enable for each existing codec, these bits are in the resume well + /// + if (AzaliaConfig->Pme != PCH_DEVICE_DISABLE) { + MmioOr16 ( + (UINTN) (HdaBar + R_HDA_WAKEEN), + (UINT16) ((B_HDA_WAKEEN_SDI_0 << AzaliaSDINo)) + ); +#ifdef SUS_WELL_RESTORE + /// + /// To support RapidStart resume from G3 state, all resume well registers need to be saved + /// into S3 Script table. + /// + SCRIPT_MEM_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint16, + (UINTN) (HdaBar + R_HDA_WAKEEN), + 1, + (VOID *) (UINTN) (HdaBar + R_HDA_WAKEEN) + ); +#endif + } + /// + /// Verb: 31~28 27 26~20 19~0 + /// CAd 1 NID Verb Command and data + /// 0/1/2 + /// + /// Read the Vendor ID/Device ID pair from the attached codec + /// + VendorDeviceId = 0x000F0000 | (AzaliaSDINo << 28); + Status = SendCodecCommand (HdaBar, &VendorDeviceId, TRUE); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Read the Codec Vendor ID/Device ID fail! \n")); + goto ExitInitAzalia; + } + /// + /// Read the Revision ID from the attached codec + /// + RevisionId = 0x000F0002 | (AzaliaSDINo << 28); + Status = SendCodecCommand (HdaBar, &RevisionId, TRUE); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Read the Codec Revision ID fail! \n")); + goto ExitInitAzalia; + } + + RevisionId = (RevisionId >> 8) & 0xFF; + + /// + /// Get the match codec verb table, RevID of 0xFF applies to all steppings. + /// + for (VerbTableNum = 0, VerbHeaderTable = NULL, VerbTable = NULL; + VerbTableNum < AzaliaConfig->AzaliaVerbTableNum; + VerbTableNum++) { + if ((VendorDeviceId == AzaliaConfig->AzaliaVerbTable[VerbTableNum].VerbTableHeader.VendorDeviceId) && + ((AzaliaConfig->AzaliaVerbTable[VerbTableNum].VerbTableHeader.RevisionId == 0xFF) || + ( RevisionId == AzaliaConfig->AzaliaVerbTable[VerbTableNum].VerbTableHeader.RevisionId))) { + VerbHeaderTable = &(AzaliaConfig->AzaliaVerbTable[VerbTableNum].VerbTableHeader); + VerbTable = AzaliaConfig->AzaliaVerbTable[VerbTableNum].VerbTableData; + if (VerbTable == 0) { + DEBUG ((EFI_D_ERROR | EFI_D_INFO, "VerbTableData of VendorID:0x%X is null.\n", VendorDeviceId)); + Status = EFI_INVALID_PARAMETER; + goto ExitInitAzalia; + } + DEBUG ((EFI_D_INFO, "Detected Azalia Codec with verb table, VendorID = 0x%X", VendorDeviceId)); + DEBUG ((EFI_D_INFO, " on SDI%d, revision = 0x%0x.\n", AzaliaSDINo, RevisionId)); + /// + /// Send the entire list of verbs in the matching verb table one by one to the codec + /// + for (Index = 0; + Index < (UINT32) ((VerbHeaderTable->NumberOfFrontJacks + VerbHeaderTable->NumberOfRearJacks) * 4); + Index++) { + /// + /// Clear CAd Field + /// + CodecCmdData = VerbTable[Index] & (UINT32) ~(BIT31 | BIT30 | BIT29 | BIT28); + /// + /// Program CAd Field per the SDI number got during codec detection + /// + CodecCmdData |= (UINT32) (AzaliaSDINo << 28); + Status = SendCodecCommand (HdaBar, &CodecCmdData, FALSE); + if (EFI_ERROR (Status)) { + /// + /// Skip the Azalia verb table loading when find the verb table content is not + /// properly matched with the HDA hardware, though IDs match. + /// + DEBUG ( + (EFI_D_ERROR | EFI_D_INFO, + "Detected Azalia Codec of VendorID:0x%X, error occurs during loading verb table.\n", + VendorDeviceId) + ); + goto ExitInitAzalia; + } + SendCodecCommandS3Item (HdaBar, CodecCmdData); + } + break; + } + } + + if (VerbTableNum >= AzaliaConfig->AzaliaVerbTableNum) { + DEBUG ( + (EFI_D_ERROR, + "Detected High Definition Audio (Azalia) Codec, VendorID = 0x%08x on SDI%d,", + VendorDeviceId, + AzaliaSDINo) + ); + DEBUG ((EFI_D_ERROR, " but no matching verb table found.\n")); + } + } + /// + /// end of for + /// + *AzaliaStarted = TRUE; + Status = EFI_SUCCESS; + +ExitInitAzalia: + /// + /// Clear AZBAR and disable memory map access + /// + MmioAnd16 ((UINTN) (AzaliaBase + R_PCH_HDA_COMMAND), (UINT16) (~B_PCH_HDA_COMMAND_MSE)); + SCRIPT_MEM_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint16, + (UINTN) (AzaliaBase + R_PCH_HDA_COMMAND), + 1, + (VOID *) (UINTN) (AzaliaBase + R_PCH_HDA_COMMAND) + ); + + MmioWrite32 (AzaliaBase + R_PCH_HDA_HDBARL, 0); + SCRIPT_MEM_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint32, + (UINTN) (AzaliaBase + R_PCH_HDA_HDBARL), + 1, + (VOID *) (UINTN) (AzaliaBase + R_PCH_HDA_HDBARL) + ); + + MmioWrite32 (AzaliaBase + R_PCH_HDA_HDBARU, 0); + SCRIPT_MEM_WRITE ( + EFI_ACPI_S3_RESUME_SCRIPT_TABLE, + EfiBootScriptWidthUint32, + (UINTN) (AzaliaBase + R_PCH_HDA_HDBARU), + 1, + (VOID *) (UINTN) (AzaliaBase + R_PCH_HDA_HDBARU) + ); + + gDS->FreeMemorySpace ( + BaseAddressBarMem, + V_PCH_HDA_HDBAR_SIZE + ); + + return Status; +} + +/** + Detect and initialize the type of codec (AC'97 and HDA) present in the system. + + @param[in] PchPlatformPolicy The PCH Platform Policy protocol instance + @param[in] RootComplexBar RootComplexBar value of this PCH device + @param[in, out] AzaliaEnable Returned with TRUE if Azalia High Definition Audio codec + is detected and initialized. + + @retval EFI_SUCCESS Codec is detected and initialized. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to initialize the codec. +**/ +EFI_STATUS +ConfigureAzalia ( + IN DXE_PCH_PLATFORM_POLICY_PROTOCOL *PchPlatformPolicy, + IN UINT32 RootComplexBar, + IN OUT BOOLEAN *AzaliaEnable + ) +{ + EFI_STATUS Status; + + DEBUG ((EFI_D_INFO, "ConfigureAzalia() Start\n")); + + *AzaliaEnable = FALSE; + + /// + /// If all codec devices are to be disabled, skip the detection code + /// + if (PchPlatformPolicy->DeviceEnabling->Azalia == PCH_DEVICE_DISABLE) { + DEBUG ((EFI_D_ERROR | EFI_D_INFO, "Skip Azalia Codec detection.\n")); + return EFI_SUCCESS; + } + + Status = DetectAndInitializeAzalia (PchPlatformPolicy, RootComplexBar, AzaliaEnable); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR | EFI_D_INFO, "Azalia detection / initialization failure!\n")); + + if (PchPlatformPolicy->DeviceEnabling->Azalia == PCH_DEVICE_ENABLE) { + *AzaliaEnable = TRUE; + } + } + + DEBUG ((EFI_D_INFO, "ConfigureAzalia() End\n")); + return EFI_SUCCESS; +} |