CloverBootloader/Drivers/AudioDxe/HdaController/HdaControllerMem.c

862 lines
30 KiB
C
Raw Normal View History

/*
* 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;
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;
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;
}