mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-29 12:35:53 +01:00
109d1f581c
Signed-off-by: SergeySlice <sergey.slice@gmail.com>
862 lines
30 KiB
C
862 lines
30 KiB
C
/*
|
|
* File: HdaControllerMem.c
|
|
*
|
|
* Copyright (c) 2018 John Davis
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include "HdaController.h"
|
|
#include <Library/HdaRegisters.h>
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerInitCorb(
|
|
IN HDA_CONTROLLER_DEV *HdaDev)
|
|
{
|
|
// DEBUG((DEBUG_INFO, "HdaControllerInitCorb(): start\n"));
|
|
|
|
// Status and PCI I/O protocol.
|
|
EFI_STATUS Status;
|
|
EFI_PCI_IO_PROTOCOL *PciIo = HdaDev->PciIo;
|
|
|
|
// HDA register values.
|
|
UINT8 HdaCorbSize = 0;
|
|
UINT32 HdaLowerCorbBaseAddr;
|
|
UINT32 HdaUpperCorbBaseAddr;
|
|
UINT16 HdaCorbWp;
|
|
UINT16 HdaCorbRp;
|
|
UINT64 HdaCorbRpPollResult;
|
|
|
|
// CORB buffer.
|
|
VOID *CorbBuffer = NULL;
|
|
UINTN CorbLength;
|
|
UINTN CorbLengthActual;
|
|
VOID *CorbMapping = NULL;
|
|
EFI_PHYSICAL_ADDRESS CorbPhysAddr;
|
|
|
|
// Get value of CORBSIZE register.
|
|
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_CORBSIZE, 1, &HdaCorbSize);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Determine size of CORB.
|
|
if (HdaCorbSize & HDA_REG_CORBSIZE_CORBSZCAP_256) {
|
|
CorbLength = 256 * HDA_CORB_ENTRY_SIZE;
|
|
HdaCorbSize = (HdaCorbSize & ~HDA_REG_CORBSIZE_MASK) | HDA_REG_CORBSIZE_ENT256;
|
|
} else if (HdaCorbSize & HDA_REG_CORBSIZE_CORBSZCAP_16) {
|
|
CorbLength = 16 * HDA_CORB_ENTRY_SIZE;
|
|
HdaCorbSize = (HdaCorbSize & ~HDA_REG_CORBSIZE_MASK) | HDA_REG_CORBSIZE_ENT16;
|
|
} else if (HdaCorbSize & HDA_REG_CORBSIZE_CORBSZCAP_2) {
|
|
CorbLength = 2 * HDA_CORB_ENTRY_SIZE;
|
|
HdaCorbSize = (HdaCorbSize & ~HDA_REG_CORBSIZE_MASK) | HDA_REG_CORBSIZE_ENT2;
|
|
} else {
|
|
// Unsupported size.
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
// Allocate outbound buffer.
|
|
Status = PciIo->AllocateBuffer(PciIo, AllocateAnyPages, EfiBootServicesData, EFI_SIZE_TO_PAGES(CorbLength), &CorbBuffer, 0);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
SetMem(CorbBuffer, CorbLength, 0);
|
|
|
|
// Map outbound buffer.
|
|
CorbLengthActual = CorbLength;
|
|
Status = PciIo->Map(PciIo, EfiPciIoOperationBusMasterCommonBuffer, CorbBuffer, &CorbLengthActual, &CorbPhysAddr, &CorbMapping);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
if (CorbLengthActual != CorbLength) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto FREE_BUFFER;
|
|
}
|
|
|
|
// Disable CORB.
|
|
Status = HdaControllerSetCorb(HdaDev, FALSE);
|
|
ASSERT_EFI_ERROR(Status);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
|
|
// Set outbound buffer lower base address.
|
|
HdaLowerCorbBaseAddr = (UINT32)CorbPhysAddr;
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint32, PCI_HDA_BAR, HDA_REG_CORBLBASE, 1, &HdaLowerCorbBaseAddr);
|
|
// ASSERT_EFI_ERROR(Status);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
|
|
// If 64-bit supported, set upper base address.
|
|
if (HdaDev->Capabilities & HDA_REG_GCAP_64OK) {
|
|
HdaUpperCorbBaseAddr = (UINT32)RShiftU64(CorbPhysAddr, 32);
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint32, PCI_HDA_BAR, HDA_REG_CORBUBASE, 1, &HdaUpperCorbBaseAddr);
|
|
ASSERT_EFI_ERROR(Status);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
}
|
|
|
|
// Reset write pointer to zero.
|
|
HdaCorbWp = 0;
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint16, PCI_HDA_BAR, HDA_REG_CORBWP, 1, &HdaCorbWp);
|
|
ASSERT_EFI_ERROR(Status);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
|
|
// Reset read pointer by setting bit.
|
|
HdaCorbRp = HDA_REG_CORBRP_RST;
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint16, PCI_HDA_BAR, HDA_REG_CORBRP, 1, &HdaCorbRp);
|
|
ASSERT_EFI_ERROR(Status);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
|
|
// Docs state we need to clear the bit and wait for it to clear.
|
|
HdaCorbRp = 0;
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint16, PCI_HDA_BAR, HDA_REG_CORBRP, 1, &HdaCorbRp);
|
|
ASSERT_EFI_ERROR(Status);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
Status = PciIo->PollMem(PciIo, EfiPciIoWidthUint16, PCI_HDA_BAR, HDA_REG_CORBRP, HDA_REG_CORBRP_RST, 0, 50000000, &HdaCorbRpPollResult);
|
|
ASSERT_EFI_ERROR(Status);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
|
|
// Populate device object properties.
|
|
HdaDev->CorbBuffer = CorbBuffer;
|
|
HdaDev->CorbEntryCount = (UINT32)(CorbLength / HDA_CORB_ENTRY_SIZE);
|
|
HdaDev->CorbMapping = CorbMapping;
|
|
HdaDev->CorbPhysAddr = CorbPhysAddr;
|
|
HdaDev->CorbWritePointer = HdaCorbWp;
|
|
|
|
// Buffer allocation successful.
|
|
DEBUG((DEBUG_INFO, "HDA controller CORB allocated @ 0x%p (0x%p) (%u entries)\n",
|
|
HdaDev->CorbBuffer, HdaDev->CorbPhysAddr, HdaDev->CorbEntryCount));
|
|
return EFI_SUCCESS;
|
|
|
|
FREE_BUFFER:
|
|
// Unmap if needed.
|
|
if (CorbMapping)
|
|
PciIo->Unmap(PciIo, CorbMapping);
|
|
|
|
// Free buffer.
|
|
PciIo->FreeBuffer(PciIo, EFI_SIZE_TO_PAGES(CorbLength), CorbBuffer);
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerCleanupCorb(
|
|
IN HDA_CONTROLLER_DEV *HdaDev)
|
|
{
|
|
// DEBUG((DEBUG_INFO, "HdaControllerCleanupCorb(): start\n"));
|
|
|
|
// Create variables.
|
|
EFI_STATUS Status;
|
|
EFI_PCI_IO_PROTOCOL *PciIo = HdaDev->PciIo;
|
|
|
|
// Stop CORB.
|
|
Status = HdaControllerSetCorb(HdaDev, FALSE);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Unmap CORB and free buffer.
|
|
if (HdaDev->CorbMapping)
|
|
PciIo->Unmap(PciIo, HdaDev->CorbMapping);
|
|
PciIo->FreeBuffer(PciIo, EFI_SIZE_TO_PAGES(HdaDev->CorbEntryCount * HDA_CORB_ENTRY_SIZE), HdaDev->CorbBuffer);
|
|
|
|
// Clear device object properties.
|
|
HdaDev->CorbBuffer = NULL;
|
|
HdaDev->CorbEntryCount = 0;
|
|
HdaDev->CorbMapping = NULL;
|
|
HdaDev->CorbPhysAddr = 0;
|
|
HdaDev->CorbWritePointer = 0;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerSetCorb(
|
|
IN HDA_CONTROLLER_DEV *HdaDev,
|
|
IN BOOLEAN Enable)
|
|
{
|
|
// DEBUG((DEBUG_INFO, "HdaControllerSetCorb(): start\n"));
|
|
|
|
// Create variables.
|
|
EFI_STATUS Status;
|
|
EFI_PCI_IO_PROTOCOL *PciIo = HdaDev->PciIo;
|
|
UINT8 HdaCorbCtl = 0;
|
|
UINT64 Tmp;
|
|
|
|
// Get current value of CORBCTL.
|
|
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_CORBCTL, 1, &HdaCorbCtl);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Change CORB operation.
|
|
if (Enable)
|
|
HdaCorbCtl |= HDA_REG_CORBCTL_CORBRUN;
|
|
else
|
|
HdaCorbCtl &= ~HDA_REG_CORBCTL_CORBRUN;
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_CORBCTL, 1, &HdaCorbCtl);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Wait for bit to cycle.
|
|
return PciIo->PollMem(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_CORBCTL, HDA_REG_CORBCTL_CORBRUN,
|
|
Enable ? HDA_REG_CORBCTL_CORBRUN : 0, MS_TO_NANOSECOND(50), &Tmp);
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerInitRirb(
|
|
IN HDA_CONTROLLER_DEV *HdaDev)
|
|
{
|
|
// DEBUG((DEBUG_INFO, "HdaControllerInitRirb(): start\n"));
|
|
|
|
// Status and PCI I/O protocol.
|
|
EFI_STATUS Status;
|
|
EFI_PCI_IO_PROTOCOL *PciIo = HdaDev->PciIo;
|
|
|
|
// HDA register values.
|
|
UINT8 HdaRirbSize;
|
|
UINT32 HdaLowerRirbBaseAddr;
|
|
UINT32 HdaUpperRirbBaseAddr;
|
|
UINT16 HdaRirbWp;
|
|
|
|
// RIRB buffer.
|
|
VOID *RirbBuffer = NULL;
|
|
UINTN RirbLength;
|
|
UINTN RirbLengthActual;
|
|
VOID *RirbMapping = NULL;
|
|
EFI_PHYSICAL_ADDRESS RirbPhysAddr;
|
|
|
|
// Get value of RIRBSIZE register.
|
|
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_RIRBSIZE, 1, &HdaRirbSize);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Determine size of RIRB.
|
|
if (HdaRirbSize & HDA_REG_RIRBSIZE_RIRBSZCAP_256) {
|
|
RirbLength = 256 * HDA_RIRB_ENTRY_SIZE;
|
|
HdaRirbSize = (HdaRirbSize & ~HDA_REG_RIRBSIZE_MASK) | HDA_REG_RIRBSIZE_ENT256;
|
|
} else if (HdaRirbSize & HDA_REG_RIRBSIZE_RIRBSZCAP_16) {
|
|
RirbLength = 16 * HDA_RIRB_ENTRY_SIZE;
|
|
HdaRirbSize = (HdaRirbSize & ~HDA_REG_RIRBSIZE_MASK) | HDA_REG_RIRBSIZE_ENT16;
|
|
} else if (HdaRirbSize & HDA_REG_RIRBSIZE_RIRBSZCAP_2) {
|
|
RirbLength = 2 * HDA_RIRB_ENTRY_SIZE;
|
|
HdaRirbSize = (HdaRirbSize & ~HDA_REG_RIRBSIZE_MASK) | HDA_REG_RIRBSIZE_ENT2;
|
|
} else {
|
|
// Unsupported size.
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
// Allocate outbound buffer.
|
|
Status = PciIo->AllocateBuffer(PciIo, AllocateAnyPages, EfiBootServicesData, EFI_SIZE_TO_PAGES(RirbLength), &RirbBuffer, 0);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
ZeroMem(RirbBuffer, RirbLength);
|
|
|
|
// Map outbound buffer.
|
|
RirbLengthActual = RirbLength;
|
|
Status = PciIo->Map(PciIo, EfiPciIoOperationBusMasterCommonBuffer, RirbBuffer, &RirbLengthActual, &RirbPhysAddr, &RirbMapping);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
if (RirbLengthActual != RirbLength) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto FREE_BUFFER;
|
|
}
|
|
|
|
// Disable RIRB.
|
|
Status = HdaControllerSetRirb(HdaDev, FALSE);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
|
|
// Set outbound buffer lower base address.
|
|
HdaLowerRirbBaseAddr = (UINT32)RirbPhysAddr;
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint32, PCI_HDA_BAR, HDA_REG_RIRBLBASE, 1, &HdaLowerRirbBaseAddr);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
|
|
// If 64-bit supported, set upper base address.
|
|
if (HdaDev->Capabilities & HDA_REG_GCAP_64OK) {
|
|
HdaUpperRirbBaseAddr = (UINT32)(RirbPhysAddr >> 32);
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint32, PCI_HDA_BAR, HDA_REG_RIRBUBASE, 1, &HdaUpperRirbBaseAddr);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
}
|
|
|
|
// Reset write pointer by setting reset bit.
|
|
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint16, PCI_HDA_BAR, HDA_REG_RIRBWP, 1, &HdaRirbWp);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
HdaRirbWp |= HDA_REG_RIRBWP_RST;
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint16, PCI_HDA_BAR, HDA_REG_RIRBWP, 1, &HdaRirbWp);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
|
|
// Populate device object properties.
|
|
HdaDev->RirbBuffer = RirbBuffer;
|
|
HdaDev->RirbEntryCount = (UINT32)(RirbLength / HDA_RIRB_ENTRY_SIZE);
|
|
HdaDev->RirbMapping = RirbMapping;
|
|
HdaDev->RirbPhysAddr = RirbPhysAddr;
|
|
HdaDev->RirbReadPointer = 0;
|
|
|
|
// Buffer allocation successful.
|
|
DEBUG((DEBUG_INFO, "HDA controller RIRB allocated @ 0x%p (0x%p) (%u entries)\n",
|
|
HdaDev->RirbBuffer, HdaDev->RirbPhysAddr, HdaDev->RirbEntryCount));
|
|
return EFI_SUCCESS;
|
|
|
|
FREE_BUFFER:
|
|
// Unmap if needed.
|
|
if (RirbMapping)
|
|
PciIo->Unmap(PciIo, RirbMapping);
|
|
|
|
// Free buffer.
|
|
PciIo->FreeBuffer(PciIo, EFI_SIZE_TO_PAGES(RirbLength), RirbBuffer);
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerCleanupRirb(
|
|
IN HDA_CONTROLLER_DEV *HdaDev)
|
|
{
|
|
// DEBUG((DEBUG_INFO, "HdaControllerCleanupRirb(): start\n"));
|
|
|
|
// Create variables.
|
|
EFI_STATUS Status;
|
|
EFI_PCI_IO_PROTOCOL *PciIo = HdaDev->PciIo;
|
|
|
|
// Stop RIRB.
|
|
Status = HdaControllerSetRirb(HdaDev, FALSE);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Unmap RIRB and free buffer.
|
|
if (HdaDev->RirbMapping)
|
|
PciIo->Unmap(PciIo, HdaDev->RirbMapping);
|
|
PciIo->FreeBuffer(PciIo, EFI_SIZE_TO_PAGES(HdaDev->RirbEntryCount * HDA_RIRB_ENTRY_SIZE), HdaDev->RirbBuffer);
|
|
|
|
// Clear device object properties.
|
|
HdaDev->RirbBuffer = NULL;
|
|
HdaDev->RirbEntryCount = 0;
|
|
HdaDev->RirbMapping = NULL;
|
|
HdaDev->RirbPhysAddr = 0;
|
|
HdaDev->RirbReadPointer = 0;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerSetRirb(
|
|
IN HDA_CONTROLLER_DEV *HdaDev,
|
|
IN BOOLEAN Enable)
|
|
{
|
|
// DEBUG((DEBUG_INFO, "HdaControllerSetRirb(): start\n"));
|
|
|
|
// Create variables.
|
|
EFI_STATUS Status;
|
|
EFI_PCI_IO_PROTOCOL *PciIo = HdaDev->PciIo;
|
|
UINT8 HdaRirbCtl;
|
|
UINT64 Tmp;
|
|
|
|
// Get current value of RIRBCTL.
|
|
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_RIRBCTL, 1, &HdaRirbCtl);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Change RIRB operation.
|
|
if (Enable)
|
|
HdaRirbCtl |= HDA_REG_RIRBCTL_RIRBDMAEN;
|
|
else
|
|
HdaRirbCtl &= ~HDA_REG_RIRBCTL_RIRBDMAEN;
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_RIRBCTL, 1, &HdaRirbCtl);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Wait for bit to cycle.
|
|
return PciIo->PollMem(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_RIRBCTL, HDA_REG_RIRBCTL_RIRBDMAEN,
|
|
Enable ? HDA_REG_RIRBCTL_RIRBDMAEN : 0, MS_TO_NANOSECOND(50), &Tmp);
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerInitStreams(
|
|
IN HDA_CONTROLLER_DEV *HdaControllerDev)
|
|
{
|
|
// DEBUG((DEBUG_INFO, "HdaControllerInitStreams(): start\n"));
|
|
|
|
// Status and PCI I/O protocol.
|
|
EFI_STATUS Status;
|
|
EFI_PCI_IO_PROTOCOL *PciIo = HdaControllerDev->PciIo;
|
|
UINT32 LowerBaseAddr;
|
|
UINT32 UpperBaseAddr;
|
|
EFI_PHYSICAL_ADDRESS DataBlockAddr;
|
|
HDA_STREAM *HdaStream;
|
|
UINT8 i;
|
|
UINT8 InputStreamsOffset;
|
|
UINT8 OutputStreamsOffset;
|
|
|
|
|
|
// Buffers.
|
|
UINTN BdlLengthActual;
|
|
UINTN DataLengthActual;
|
|
UINTN DmaPositionsLengthActual;
|
|
|
|
// Reset stream ID bitmap so stream 0 is allocated (reserved).
|
|
HdaControllerDev->StreamIdMapping = BIT0;
|
|
|
|
// Determine number of streams.
|
|
HdaControllerDev->BidirStreamsCount = HDA_REG_GCAP_BSS(HdaControllerDev->Capabilities);
|
|
HdaControllerDev->InputStreamsCount = HDA_REG_GCAP_ISS(HdaControllerDev->Capabilities);
|
|
HdaControllerDev->OutputStreamsCount = HDA_REG_GCAP_OSS(HdaControllerDev->Capabilities);
|
|
HdaControllerDev->TotalStreamsCount = HdaControllerDev->BidirStreamsCount +
|
|
HdaControllerDev->InputStreamsCount + HdaControllerDev->OutputStreamsCount;
|
|
|
|
// Initialize stream arrays.
|
|
HdaControllerDev->BidirStreams = AllocateZeroPool(sizeof(HDA_STREAM) * HdaControllerDev->BidirStreamsCount);
|
|
HdaControllerDev->InputStreams = AllocateZeroPool(sizeof(HDA_STREAM) * HdaControllerDev->InputStreamsCount);
|
|
HdaControllerDev->OutputStreams = AllocateZeroPool(sizeof(HDA_STREAM) * HdaControllerDev->OutputStreamsCount);
|
|
|
|
// Initialize streams.
|
|
InputStreamsOffset = HdaControllerDev->BidirStreamsCount;
|
|
OutputStreamsOffset = InputStreamsOffset + HdaControllerDev->InputStreamsCount;
|
|
// DEBUG((DEBUG_INFO, "HdaControllerInitStreams(): in offset %u, out offset %u\n", InputStreamsOffset, OutputStreamsOffset));
|
|
for (i = 0; i < HdaControllerDev->TotalStreamsCount; i++) {
|
|
// Get pointer to stream and set type.
|
|
if (i < InputStreamsOffset) {
|
|
HdaStream = HdaControllerDev->BidirStreams + i;
|
|
HdaStream->Type = HDA_STREAM_TYPE_BIDIR;
|
|
} else if (i < OutputStreamsOffset) {
|
|
HdaStream = HdaControllerDev->InputStreams + (i - InputStreamsOffset);
|
|
HdaStream->Type = HDA_STREAM_TYPE_IN;
|
|
} else {
|
|
HdaStream = HdaControllerDev->OutputStreams + (i - OutputStreamsOffset);
|
|
HdaStream->Type = HDA_STREAM_TYPE_OUT;
|
|
}
|
|
|
|
// Set parent controller and index.
|
|
HdaStream->HdaControllerDev = HdaControllerDev;
|
|
HdaStream->Index = i;
|
|
HdaStream->Output = (HdaStream->Type == HDA_STREAM_TYPE_OUT);
|
|
|
|
// Initialize polling timer.
|
|
Status = gBS->CreateEvent(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
|
|
(EFI_EVENT_NOTIFY)HdaControllerStreamPollTimerHandler, HdaStream, &HdaStream->PollTimer);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
|
|
// Allocate buffer descriptor list.
|
|
Status = PciIo->AllocateBuffer(PciIo, AllocateAnyPages, EfiBootServicesData, EFI_SIZE_TO_PAGES(HDA_BDL_SIZE),
|
|
(VOID**)&HdaStream->BufferList, 0);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
SetMem(HdaStream->BufferList, HDA_BDL_SIZE, 0);
|
|
|
|
// Map buffer descriptor list.
|
|
BdlLengthActual = HDA_BDL_SIZE;
|
|
Status = PciIo->Map(PciIo, EfiPciIoOperationBusMasterCommonBuffer, HdaStream->BufferList, &BdlLengthActual,
|
|
&HdaStream->BufferListPhysAddr, &HdaStream->BufferListMapping);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
if (BdlLengthActual != HDA_BDL_SIZE) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto FREE_BUFFER;
|
|
}
|
|
|
|
// Reset stream.
|
|
Status = HdaControllerResetStream(HdaStream);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
|
|
// Allocate buffer for data.
|
|
Status = PciIo->AllocateBuffer(PciIo, AllocateAnyPages, EfiBootServicesData, EFI_SIZE_TO_PAGES(HDA_STREAM_BUF_SIZE),
|
|
(VOID**)&HdaStream->BufferData, 0);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
SetMem(HdaStream->BufferData, HDA_STREAM_BUF_SIZE, 0);
|
|
|
|
// Map buffer descriptor list.
|
|
DataLengthActual = HDA_STREAM_BUF_SIZE;
|
|
Status = PciIo->Map(PciIo, EfiPciIoOperationBusMasterCommonBuffer, HdaStream->BufferData, &DataLengthActual,
|
|
&HdaStream->BufferDataPhysAddr, &HdaStream->BufferDataMapping);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
if (DataLengthActual != HDA_STREAM_BUF_SIZE) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto FREE_BUFFER;
|
|
}
|
|
|
|
// Fill buffer list.
|
|
for (UINTN b = 0; b < HDA_BDL_ENTRY_COUNT; b++) {
|
|
// Set address and length of entry.
|
|
DataBlockAddr = HdaStream->BufferDataPhysAddr + (b * HDA_BDL_BLOCKSIZE);
|
|
HdaStream->BufferList[b].Address = (UINT32)DataBlockAddr;
|
|
HdaStream->BufferList[b].AddressHigh = (UINT32)(DataBlockAddr >> 32);
|
|
HdaStream->BufferList[b].Length = HDA_BDL_BLOCKSIZE;
|
|
HdaStream->BufferList[b].InterruptOnCompletion = TRUE;
|
|
}
|
|
}
|
|
|
|
// Allocate space for DMA positions structure.
|
|
HdaControllerDev->DmaPositionsSize = sizeof(HDA_DMA_POS_ENTRY) * HdaControllerDev->TotalStreamsCount;
|
|
Status = PciIo->AllocateBuffer(PciIo, AllocateAnyPages, EfiBootServicesData,
|
|
EFI_SIZE_TO_PAGES(HdaControllerDev->DmaPositionsSize), (VOID**)&HdaControllerDev->DmaPositions, 0);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
SetMem(HdaControllerDev->DmaPositions, HdaControllerDev->DmaPositionsSize, 0);
|
|
|
|
// Map buffer descriptor list.
|
|
DmaPositionsLengthActual = HdaControllerDev->DmaPositionsSize;
|
|
Status = PciIo->Map(PciIo, EfiPciIoOperationBusMasterCommonBuffer, HdaControllerDev->DmaPositions,
|
|
&DmaPositionsLengthActual, &HdaControllerDev->DmaPositionsPhysAddr, &HdaControllerDev->DmaPositionsMapping);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
if (DmaPositionsLengthActual != HdaControllerDev->DmaPositionsSize) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto FREE_BUFFER;
|
|
}
|
|
|
|
// Set DMA positions lower base address.
|
|
LowerBaseAddr = ((UINT32)HdaControllerDev->DmaPositionsPhysAddr) | HDA_REG_DPLBASE_EN;
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint32, PCI_HDA_BAR, HDA_REG_DPLBASE, 1, &LowerBaseAddr);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
|
|
// If 64-bit supported, set DMA positions upper base address.
|
|
if (HdaControllerDev->Capabilities & HDA_REG_GCAP_64OK) {
|
|
UpperBaseAddr = (UINT32)RShiftU64(HdaControllerDev->DmaPositionsPhysAddr, 32);
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint32, PCI_HDA_BAR, HDA_REG_DPUBASE, 1, &UpperBaseAddr);
|
|
if (EFI_ERROR(Status))
|
|
goto FREE_BUFFER;
|
|
}
|
|
|
|
// Success.
|
|
return EFI_SUCCESS;
|
|
|
|
FREE_BUFFER:
|
|
// Free stream arrays. TODO
|
|
FreePool(HdaControllerDev->BidirStreams);
|
|
FreePool(HdaControllerDev->InputStreams);
|
|
FreePool(HdaControllerDev->OutputStreams);
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerResetStream(
|
|
IN HDA_STREAM *HdaStream)
|
|
{
|
|
// Create variables.
|
|
EFI_STATUS Status;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
UINT32 LowerBaseAddr;
|
|
UINT32 UpperBaseAddr;
|
|
UINT64 Tmp;
|
|
|
|
// Stream regs.
|
|
UINT8 HdaStreamCtl1 = 0;
|
|
UINT16 StreamLvi;
|
|
UINT32 StreamCbl;
|
|
|
|
if (!HdaStream || !HdaStream->HdaControllerDev)
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
PciIo = HdaStream->HdaControllerDev->PciIo;
|
|
|
|
// Disable stream.
|
|
Status = HdaControllerSetStreamId(HdaStream, 0);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Get value of control register.
|
|
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_SDNCTL1(HdaStream->Index), 1, &HdaStreamCtl1);
|
|
// ASSERT_EFI_ERROR(Status);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Reset stream and wait for bit to be set.
|
|
HdaStreamCtl1 |= HDA_REG_SDNCTL1_SRST;
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_SDNCTL1(HdaStream->Index), 1, &HdaStreamCtl1);
|
|
// ASSERT_EFI_ERROR(Status);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
Status = PciIo->PollMem(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_SDNCTL1(HdaStream->Index),
|
|
HDA_REG_SDNCTL1_SRST, HDA_REG_SDNCTL1_SRST, MS_TO_NANOSECOND(100), &Tmp);
|
|
// ASSERT_EFI_ERROR(Status);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Get value of control register.
|
|
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_SDNCTL1(HdaStream->Index), 1, &HdaStreamCtl1);
|
|
// ASSERT_EFI_ERROR(Status);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Docs state we need to clear the bit and wait for it to clear.
|
|
HdaStreamCtl1 &= ~HDA_REG_SDNCTL1_SRST;
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_SDNCTL1(HdaStream->Index), 1, &HdaStreamCtl1);
|
|
// ASSERT_EFI_ERROR(Status);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
Status = PciIo->PollMem(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_SDNCTL1(HdaStream->Index),
|
|
HDA_REG_SDNCTL1_SRST, 0, MS_TO_NANOSECOND(100), &Tmp);
|
|
// ASSERT_EFI_ERROR(Status);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Set buffer list lower base address.
|
|
LowerBaseAddr = (UINT32)HdaStream->BufferListPhysAddr;
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint32, PCI_HDA_BAR, HDA_REG_SDNBDPL(HdaStream->Index), 1, &LowerBaseAddr);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// If 64-bit supported, set buffer list upper base address.
|
|
if (HdaStream->HdaControllerDev->Capabilities & HDA_REG_GCAP_64OK) {
|
|
UpperBaseAddr = (UINT32)RShiftU64(HdaStream->BufferListPhysAddr, 32);
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint32, PCI_HDA_BAR, HDA_REG_SDNBDPU(HdaStream->Index), 1, &UpperBaseAddr);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
}
|
|
|
|
// Set last valid index (LVI).
|
|
StreamLvi = HDA_BDL_ENTRY_LAST;
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint16, PCI_HDA_BAR, HDA_REG_SDNLVI(HdaStream->Index), 1, &StreamLvi);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Set total buffer length.
|
|
StreamCbl = HDA_STREAM_BUF_SIZE;
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint32, PCI_HDA_BAR, HDA_REG_SDNCBL(HdaStream->Index), 1, &StreamCbl);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
EFIAPI
|
|
HdaControllerCleanupStreams(
|
|
IN HDA_CONTROLLER_DEV *HdaControllerDev)
|
|
{
|
|
// DEBUG((DEBUG_INFO, "HdaControllerInitStreams(): start\n"));
|
|
|
|
// Status and PCI I/O protocol.
|
|
EFI_PCI_IO_PROTOCOL *PciIo = HdaControllerDev->PciIo;
|
|
UINT8 InputStreamsOffset = HdaControllerDev->BidirStreamsCount;
|
|
UINT8 OutputStreamsOffset = InputStreamsOffset + HdaControllerDev->InputStreamsCount;
|
|
HDA_STREAM *HdaStream;
|
|
UINT32 Tmp;
|
|
UINT8 i;
|
|
|
|
// Clean streams.
|
|
for (i = 0; i < HdaControllerDev->TotalStreamsCount; i++) {
|
|
// Get pointer to stream and set type.
|
|
if (i < InputStreamsOffset) {
|
|
HdaStream = HdaControllerDev->BidirStreams + i;
|
|
HdaStream->Type = HDA_STREAM_TYPE_BIDIR;
|
|
} else if (i < OutputStreamsOffset) {
|
|
HdaStream = HdaControllerDev->InputStreams + (i - InputStreamsOffset);
|
|
HdaStream->Type = HDA_STREAM_TYPE_IN;
|
|
} else {
|
|
HdaStream = HdaControllerDev->OutputStreams + (i - OutputStreamsOffset);
|
|
HdaStream->Type = HDA_STREAM_TYPE_OUT;
|
|
}
|
|
|
|
// Close polling timer.
|
|
gBS->CloseEvent(HdaStream->PollTimer);
|
|
|
|
// Stop stream.
|
|
HdaControllerSetStreamId(HdaStream, 0);
|
|
|
|
// Unmap and free buffer descriptor list.
|
|
if (HdaStream->BufferListMapping != NULL)
|
|
PciIo->Unmap(PciIo, HdaStream->BufferListMapping);
|
|
if (HdaStream->BufferList != NULL)
|
|
PciIo->FreeBuffer(PciIo, EFI_SIZE_TO_PAGES(HDA_BDL_SIZE), HdaStream->BufferList);
|
|
|
|
// Unmap and free data buffer.
|
|
if (HdaStream->BufferDataMapping != NULL)
|
|
PciIo->Unmap(PciIo, HdaStream->BufferDataMapping);
|
|
if (HdaStream->BufferData != NULL)
|
|
PciIo->FreeBuffer(PciIo, EFI_SIZE_TO_PAGES(HDA_STREAM_BUF_SIZE), HdaStream->BufferData);
|
|
}
|
|
|
|
// Clear DMA positions structure base address.
|
|
Tmp = 0;
|
|
PciIo->Mem.Write(PciIo, EfiPciIoWidthUint32, PCI_HDA_BAR, HDA_REG_DPLBASE, 1, &Tmp);
|
|
if (HdaControllerDev->Capabilities & HDA_REG_GCAP_64OK) {
|
|
Tmp = 0;
|
|
PciIo->Mem.Write(PciIo, EfiPciIoWidthUint32, PCI_HDA_BAR, HDA_REG_DPUBASE, 1, &Tmp);
|
|
}
|
|
|
|
// Unmap and free DMA positions structure.
|
|
if (HdaControllerDev->DmaPositionsMapping != NULL)
|
|
PciIo->Unmap(PciIo, HdaControllerDev->DmaPositionsMapping);
|
|
if (HdaControllerDev->DmaPositions != NULL)
|
|
PciIo->FreeBuffer(PciIo, EFI_SIZE_TO_PAGES(HdaControllerDev->DmaPositionsSize), HdaControllerDev->DmaPositions);
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerGetStream(
|
|
IN HDA_STREAM *HdaStream,
|
|
OUT BOOLEAN *Run)
|
|
{
|
|
//DEBUG((DEBUG_INFO, "HdaControllerGetStream(%u): start\n", HdaStream->Index));
|
|
|
|
// Create variables.
|
|
EFI_STATUS Status;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
UINT8 HdaStreamCtl1 = 0;
|
|
|
|
if (!Run || !HdaStream || !HdaStream->HdaControllerDev)
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
PciIo = HdaStream->HdaControllerDev->PciIo;
|
|
|
|
// Get current value of register.
|
|
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_SDNCTL1(HdaStream->Index), 1, &HdaStreamCtl1);
|
|
// if (EFI_ERROR(Status))
|
|
// return Status;
|
|
|
|
// Success.
|
|
*Run = (HdaStreamCtl1 & HDA_REG_SDNCTL1_RUN) != 0;
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerSetStream(
|
|
IN HDA_STREAM *HdaStream,
|
|
IN BOOLEAN Run)
|
|
{
|
|
// DEBUG((DEBUG_INFO, "HdaControllerSetStream(%u, %u): start\n", HdaStream->Index, Run));
|
|
|
|
// Create variables.
|
|
EFI_STATUS Status;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
UINT8 HdaStreamCtl1 = 0;
|
|
UINT64 Tmp = 0;
|
|
|
|
if (!HdaStream || !HdaStream->HdaControllerDev)
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
PciIo = HdaStream->HdaControllerDev->PciIo;
|
|
|
|
// Get current value of register.
|
|
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_SDNCTL1(HdaStream->Index), 1, &HdaStreamCtl1);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Update stream operation.
|
|
if (Run)
|
|
HdaStreamCtl1 |= HDA_REG_SDNCTL1_RUN;
|
|
else
|
|
HdaStreamCtl1 &= ~HDA_REG_SDNCTL1_RUN;
|
|
|
|
// Write register.
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_SDNCTL1(HdaStream->Index), 1, &HdaStreamCtl1);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Wait for bit to be set or cleared.
|
|
Status = PciIo->PollMem(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_SDNCTL1(HdaStream->Index),
|
|
HDA_REG_SDNCTL1_RUN, HdaStreamCtl1 & HDA_REG_SDNCTL1_RUN, MS_TO_NANOSECOND(10), &Tmp);
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerGetStreamLinkPos(
|
|
IN HDA_STREAM *HdaStream,
|
|
OUT UINT32 *Position)
|
|
{
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
|
|
if (!Position || !HdaStream || !HdaStream->HdaControllerDev)
|
|
return EFI_INVALID_PARAMETER;
|
|
//DEBUG((DEBUG_INFO, "HdaControllerGetStreamLinkPos(%u): start\n", HdaStream->Index));
|
|
|
|
// Get current value of register.
|
|
PciIo = HdaStream->HdaControllerDev->PciIo;
|
|
return PciIo->Mem.Read(PciIo, EfiPciIoWidthUint32, PCI_HDA_BAR, HDA_REG_SDNLPIB(HdaStream->Index), 1, Position);
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerGetStreamId(
|
|
IN HDA_STREAM *HdaStream,
|
|
OUT UINT8 *Index)
|
|
{
|
|
// Create variables.
|
|
EFI_STATUS Status;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
UINT8 HdaStreamCtl3 = 0;
|
|
|
|
if (!Index || !HdaStream || !HdaStream->HdaControllerDev)
|
|
return EFI_INVALID_PARAMETER;
|
|
//DEBUG((DEBUG_INFO, "HdaControllerGetStreamId(%u): start\n", HdaStream->Index));
|
|
PciIo = HdaStream->HdaControllerDev->PciIo;
|
|
|
|
// Get current value of register.
|
|
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_SDNCTL3(HdaStream->Index), 1, &HdaStreamCtl3);
|
|
// if (EFI_ERROR(Status))
|
|
// return Status;
|
|
|
|
// Update stream index.
|
|
*Index = HDA_REG_SDNCTL3_STRM_GET(HdaStreamCtl3);
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerSetStreamId(
|
|
IN HDA_STREAM *HdaStream,
|
|
IN UINT8 Index)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
UINT8 HdaStreamCtl3;
|
|
|
|
if (!HdaStream || !HdaStream->HdaControllerDev)
|
|
return EFI_INVALID_PARAMETER;
|
|
//DEBUG((DEBUG_INFO, "HdaControllerSetStreamId(%u, %u): start\n", HdaStream->Index, Index));
|
|
|
|
PciIo = HdaStream->HdaControllerDev->PciIo;
|
|
|
|
// Stop stream.
|
|
Status = HdaControllerSetStream(HdaStream, FALSE);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Get current value of register.
|
|
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_SDNCTL3(HdaStream->Index), 1, &HdaStreamCtl3);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Update stream index.
|
|
HdaStreamCtl3 = HDA_REG_SDNCTL3_STRM_SET(HdaStreamCtl3, Index);
|
|
|
|
// Write register.
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_SDNCTL3(HdaStream->Index), 1, &HdaStreamCtl3);
|
|
// if (EFI_ERROR(Status))
|
|
// return Status;
|
|
|
|
// Success.
|
|
return Status;
|
|
}
|