mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-23 11:35:19 +01:00
82347b1e8d
Signed-off-by: Sergey Isakov <isakov-sl@bk.ru>
501 lines
16 KiB
C
501 lines
16 KiB
C
/*
|
|
* File: HdaControllerHdaIo.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>
|
|
|
|
// HDA I/O Device Path GUID.
|
|
EFI_GUID gEfiHdaIoDevicePathGuid = EFI_HDA_IO_DEVICE_PATH_GUID;
|
|
|
|
/**
|
|
Retrieves this codec's address.
|
|
|
|
@param[in] This A pointer to the HDA_IO_PROTOCOL instance.
|
|
@param[out] CodecAddress The codec's address.
|
|
|
|
@retval EFI_SUCCESS The codec's address was returned.
|
|
@retval EFI_INVALID_PARAMETER One or more parameters are invalid.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerHdaIoGetAddress(
|
|
IN EFI_HDA_IO_PROTOCOL *This,
|
|
OUT UINT8 *CodecAddress) {
|
|
HDA_IO_PRIVATE_DATA *HdaPrivateData;
|
|
|
|
// If parameters are NULL, return error.
|
|
if (This == NULL || CodecAddress == NULL)
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
// Get private data and codec address.
|
|
HdaPrivateData = HDA_IO_PRIVATE_DATA_FROM_THIS(This);
|
|
*CodecAddress = HdaPrivateData->HdaCodecAddress;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Sends a single command to the codec.
|
|
|
|
@param[in] This A pointer to the HDA_IO_PROTOCOL instance.
|
|
@param[in] Node The destination node.
|
|
@param[in] Verb The verb to send.
|
|
@param[out] Response The response received.
|
|
|
|
@retval EFI_SUCCESS The verb was sent successfully and a response received.
|
|
@retval EFI_INVALID_PARAMETER One or more parameters are invalid.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerHdaIoSendCommand(
|
|
IN EFI_HDA_IO_PROTOCOL *This,
|
|
IN UINT8 Node,
|
|
IN UINT32 Verb,
|
|
OUT UINT32 *Response)
|
|
{
|
|
UINT32 Verba = Verb;
|
|
// Create verb list with single item.
|
|
EFI_HDA_IO_VERB_LIST HdaCodecVerbList;
|
|
HdaCodecVerbList.Count = 1;
|
|
HdaCodecVerbList.Verbs = &Verba;
|
|
HdaCodecVerbList.Responses = Response;
|
|
|
|
// Call SendCommands().
|
|
return HdaControllerHdaIoSendCommands(This, Node, &HdaCodecVerbList);
|
|
}
|
|
|
|
/**
|
|
Sends a set of commands to the codec.
|
|
|
|
@param[in] This A pointer to the HDA_IO_PROTOCOL instance.
|
|
@param[in] Node The destination node.
|
|
@param[in] Verbs The verbs to send. Responses will be delievered in the same list.
|
|
|
|
@retval EFI_SUCCESS The verbs were sent successfully and all responses received.
|
|
@retval EFI_INVALID_PARAMETER One or more parameters are invalid.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerHdaIoSendCommands(
|
|
IN EFI_HDA_IO_PROTOCOL *This,
|
|
IN UINT8 Node,
|
|
IN EFI_HDA_IO_VERB_LIST *Verbs)
|
|
{
|
|
// Create variables.
|
|
HDA_IO_PRIVATE_DATA *HdaPrivateData;
|
|
|
|
// If parameters are NULL, return error.
|
|
if (This == NULL || Verbs == NULL)
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
// Get private data and send commands.
|
|
HdaPrivateData = HDA_IO_PRIVATE_DATA_FROM_THIS(This);
|
|
return HdaControllerSendCommands(HdaPrivateData->HdaControllerDev, HdaPrivateData->HdaCodecAddress, Node, Verbs);
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerHdaIoSetupStream(
|
|
IN EFI_HDA_IO_PROTOCOL *This,
|
|
IN EFI_HDA_IO_PROTOCOL_TYPE Type,
|
|
IN UINT16 Format,
|
|
OUT UINT8 *StreamId)
|
|
{
|
|
//DEBUG((DEBUG_INFO, "HdaControllerHdaIoSetupStream(): start\n"));
|
|
|
|
// Create variables.
|
|
EFI_STATUS Status;
|
|
HDA_IO_PRIVATE_DATA *HdaIoPrivateData;
|
|
HDA_CONTROLLER_DEV *HdaControllerDev;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
|
|
// Stream.
|
|
HDA_STREAM *HdaStream;
|
|
UINT16 HdaStreamFormat = 0;
|
|
UINT8 HdaStreamId = 0;
|
|
EFI_TPL OldTpl = 0;
|
|
UINT8 i;
|
|
|
|
// If a parameter is invalid, return error.
|
|
if ((This == NULL) || (Type >= EfiHdaIoTypeMaximum) || (StreamId == NULL))
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
// Get private data.
|
|
HdaIoPrivateData = HDA_IO_PRIVATE_DATA_FROM_THIS(This);
|
|
HdaControllerDev = HdaIoPrivateData->HdaControllerDev;
|
|
PciIo = HdaControllerDev->PciIo;
|
|
|
|
// Get stream.
|
|
if (Type == EfiHdaIoTypeOutput)
|
|
HdaStream = HdaIoPrivateData->HdaOutputStream;
|
|
else
|
|
HdaStream = HdaIoPrivateData->HdaInputStream;
|
|
|
|
// Get current stream ID.
|
|
Status = HdaControllerGetStreamId(HdaStream, &HdaStreamId);
|
|
if (EFI_ERROR(Status))
|
|
goto DONE;
|
|
|
|
// Is a stream ID allocated already? If so that means the stream is already
|
|
// set up and we'll need to tear it down first.
|
|
if (HdaStreamId > 0) {
|
|
Status = EFI_ALREADY_STARTED;
|
|
goto DONE;
|
|
}
|
|
|
|
// Raise TPL so we can't be messed with.
|
|
OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL);
|
|
|
|
// Find and allocate stream ID.
|
|
for (i = HDA_STREAM_ID_MIN; i <= HDA_STREAM_ID_MAX; i++) {
|
|
if (!(HdaControllerDev->StreamIdMapping & (1 << i))) {
|
|
HdaControllerDev->StreamIdMapping |= (1 << i);
|
|
HdaStreamId = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If stream ID is still zero, fail.
|
|
if (HdaStreamId == 0) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto DONE;
|
|
}
|
|
|
|
// Get current format.
|
|
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint16, PCI_HDA_BAR,
|
|
HDA_REG_SDNFMT(HdaStream->Index), 1, &HdaStreamFormat);
|
|
if (EFI_ERROR(Status))
|
|
goto DONE;
|
|
|
|
// Reset stream if format has changed.
|
|
if (Format != HdaStreamFormat) {
|
|
// Reset stream.
|
|
// DEBUG((DEBUG_INFO, "HdaControllerHdaIoSetupStream(): format changed, resetting stream\n"));
|
|
HdaControllerDev->DmaPositions[HdaStream->Index].Position = 0;
|
|
Status = HdaControllerResetStream(HdaStream);
|
|
if (EFI_ERROR(Status))
|
|
goto DONE;
|
|
}
|
|
|
|
// Set stream ID.
|
|
Status = HdaControllerSetStreamId(HdaIoPrivateData->HdaOutputStream, HdaStreamId);
|
|
if (EFI_ERROR(Status))
|
|
goto DONE;
|
|
*StreamId = HdaStreamId;
|
|
|
|
// Set stream format.
|
|
//DEBUG((DEBUG_INFO, "HdaControllerHdaIoSetupStream(): setting format 0x%X\n", Format));
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint16, PCI_HDA_BAR,
|
|
HDA_REG_SDNFMT(HdaStream->Index), 1, &Format);
|
|
// if (EFI_ERROR(Status))
|
|
// goto DONE;
|
|
|
|
// Stream is ready.
|
|
// Status = EFI_SUCCESS;
|
|
|
|
DONE:
|
|
// Restore TPL if needed.
|
|
if (OldTpl)
|
|
gBS->RestoreTPL(OldTpl);
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerHdaIoCloseStream(
|
|
IN EFI_HDA_IO_PROTOCOL *This,
|
|
IN EFI_HDA_IO_PROTOCOL_TYPE Type)
|
|
{
|
|
//DEBUG((DEBUG_INFO, "HdaControllerHdaIoCloseStream(): start\n"));
|
|
|
|
// Create variables.
|
|
EFI_STATUS Status;
|
|
HDA_IO_PRIVATE_DATA *HdaIoPrivateData;
|
|
HDA_CONTROLLER_DEV *HdaControllerDev;
|
|
|
|
// Stream.
|
|
HDA_STREAM *HdaStream;
|
|
UINT8 HdaStreamId = 0;
|
|
EFI_TPL OldTpl = 0;
|
|
|
|
// If a parameter is invalid, return error.
|
|
if ((This == NULL) || (Type >= EfiHdaIoTypeMaximum))
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
// Get private data.
|
|
HdaIoPrivateData = HDA_IO_PRIVATE_DATA_FROM_THIS(This);
|
|
HdaControllerDev = HdaIoPrivateData->HdaControllerDev;
|
|
|
|
// Get stream.
|
|
if (Type == EfiHdaIoTypeOutput)
|
|
HdaStream = HdaIoPrivateData->HdaOutputStream;
|
|
else
|
|
HdaStream = HdaIoPrivateData->HdaInputStream;
|
|
|
|
// Get current stream ID.
|
|
Status = HdaControllerGetStreamId(HdaStream, &HdaStreamId);
|
|
if (EFI_ERROR(Status))
|
|
goto DONE;
|
|
|
|
// Is a stream ID already at zero?
|
|
if (HdaStreamId == 0) {
|
|
Status = EFI_SUCCESS;
|
|
goto DONE;
|
|
}
|
|
|
|
// Raise TPL so we can't be messed with.
|
|
OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL);
|
|
|
|
// Stop stream.
|
|
Status = HdaControllerHdaIoStopStream(This, Type);
|
|
if (EFI_ERROR(Status))
|
|
goto DONE;
|
|
|
|
// Set stream ID to zero.
|
|
Status = HdaControllerSetStreamId(HdaStream, 0);
|
|
if (EFI_ERROR(Status))
|
|
goto DONE;
|
|
|
|
// De-allocate stream ID from bitmap.
|
|
HdaControllerDev->StreamIdMapping &= ~(1 << HdaStreamId);
|
|
|
|
// Stream closed successfully.
|
|
// Status = EFI_SUCCESS; // already done
|
|
|
|
DONE:
|
|
// Restore TPL if needed.
|
|
if (OldTpl)
|
|
gBS->RestoreTPL(OldTpl);
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerHdaIoGetStream(
|
|
IN EFI_HDA_IO_PROTOCOL *This,
|
|
IN EFI_HDA_IO_PROTOCOL_TYPE Type,
|
|
OUT BOOLEAN *State)
|
|
{
|
|
//DEBUG((DEBUG_INFO, "HdaControllerHdaIoGetStream(): start\n"));
|
|
|
|
// Create variables.
|
|
HDA_IO_PRIVATE_DATA *HdaIoPrivateData;
|
|
HDA_STREAM *HdaStream;
|
|
|
|
// If a parameter is invalid, return error.
|
|
if ((This == NULL) || (Type >= EfiHdaIoTypeMaximum) || (State == NULL))
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
// Get private data.
|
|
HdaIoPrivateData = HDA_IO_PRIVATE_DATA_FROM_THIS(This);
|
|
|
|
// Get stream.
|
|
if (Type == EfiHdaIoTypeOutput)
|
|
HdaStream = HdaIoPrivateData->HdaOutputStream;
|
|
else
|
|
HdaStream = HdaIoPrivateData->HdaInputStream;
|
|
|
|
// Get stream state.
|
|
return HdaControllerGetStream(HdaStream, State);
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerHdaIoStartStream(
|
|
IN EFI_HDA_IO_PROTOCOL *This,
|
|
IN EFI_HDA_IO_PROTOCOL_TYPE Type,
|
|
IN VOID *Buffer,
|
|
IN UINTN BufferLength,
|
|
IN UINTN BufferPosition OPTIONAL,
|
|
IN EFI_HDA_IO_STREAM_CALLBACK Callback OPTIONAL,
|
|
IN VOID *Context1 OPTIONAL,
|
|
IN VOID *Context2 OPTIONAL,
|
|
IN VOID *Context3 OPTIONAL)
|
|
{
|
|
//DEBUG((DEBUG_INFO, "HdaControllerHdaIoStartStream(): start\n"));
|
|
|
|
// Create variables.
|
|
EFI_STATUS Status;
|
|
HDA_IO_PRIVATE_DATA *HdaIoPrivateData;
|
|
HDA_CONTROLLER_DEV *HdaControllerDev;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
|
|
// Stream.
|
|
HDA_STREAM *HdaStream;
|
|
UINT8 HdaStreamId = 0;
|
|
UINT16 HdaStreamSts;
|
|
UINT32 HdaStreamDmaPos;
|
|
UINTN HdaStreamDmaRemainingLength;
|
|
UINTN HdaStreamCurrentBlock;
|
|
UINTN HdaStreamNextBlock;
|
|
|
|
// If a parameter is invalid, return error.
|
|
if ((This == NULL) || (Type >= EfiHdaIoTypeMaximum) ||
|
|
(Buffer == NULL) || (BufferLength == 0) || (BufferPosition >= BufferLength))
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
// Get private data.
|
|
HdaIoPrivateData = HDA_IO_PRIVATE_DATA_FROM_THIS(This);
|
|
HdaControllerDev = HdaIoPrivateData->HdaControllerDev;
|
|
PciIo = HdaControllerDev->PciIo;
|
|
|
|
// Get stream.
|
|
if (Type == EfiHdaIoTypeOutput)
|
|
HdaStream = HdaIoPrivateData->HdaOutputStream;
|
|
else
|
|
HdaStream = HdaIoPrivateData->HdaInputStream;
|
|
|
|
// Get current stream ID.
|
|
Status = HdaControllerGetStreamId(HdaStream, &HdaStreamId);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Is a stream ID zero? If so that means the stream is not setup yet.
|
|
if (HdaStreamId == 0)
|
|
return EFI_NOT_READY;
|
|
|
|
// Reset completion bit.
|
|
HdaStreamSts = HDA_REG_SDNSTS_BCIS;
|
|
Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint8, PCI_HDA_BAR, HDA_REG_SDNSTS(HdaStream->Index), 1, &HdaStreamSts);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Get current DMA position.
|
|
HdaStreamDmaPos = HdaControllerDev->DmaPositions[HdaStream->Index].Position;
|
|
HdaStreamCurrentBlock = HdaStreamDmaPos / HDA_BDL_BLOCKSIZE;
|
|
HdaStreamNextBlock = HdaStreamCurrentBlock + 1;
|
|
HdaStreamNextBlock %= HDA_BDL_ENTRY_COUNT;
|
|
// DEBUG((DEBUG_INFO, "HdaControllerHdaIoStartStream(): stream %u DMA pos 0x%X\n",
|
|
// HdaStream->Index, HdaStreamDmaPos));
|
|
|
|
// Save pointer to buffer.
|
|
HdaStream->BufferSource = Buffer;
|
|
HdaStream->BufferSourceLength = BufferLength;
|
|
HdaStream->BufferSourcePosition = BufferPosition;
|
|
HdaStream->Callback = Callback;
|
|
HdaStream->CallbackContext1 = Context1;
|
|
HdaStream->CallbackContext2 = Context2;
|
|
HdaStream->CallbackContext3 = Context3;
|
|
|
|
// Zero out buffer.
|
|
gBS->SetMem(HdaStream->BufferData, HDA_STREAM_BUF_SIZE, 0);
|
|
|
|
// Fill rest of current block.
|
|
HdaStreamDmaRemainingLength = HDA_BDL_BLOCKSIZE - (HdaStreamDmaPos - (HdaStreamCurrentBlock * HDA_BDL_BLOCKSIZE));
|
|
if ((HdaStream->BufferSourcePosition + HdaStreamDmaRemainingLength) > BufferLength)
|
|
HdaStreamDmaRemainingLength = BufferLength;
|
|
CopyMem(HdaStream->BufferData + HdaStreamDmaPos, HdaStream->BufferSource + HdaStream->BufferSourcePosition, HdaStreamDmaRemainingLength);
|
|
HdaStream->BufferSourcePosition += HdaStreamDmaRemainingLength;
|
|
// DEBUG((DEBUG_INFO, "%u (0x%X) bytes written to 0x%X (block %u of %u)\n", HdaStreamDmaRemainingLength, HdaStreamDmaRemainingLength,
|
|
// HdaStream->BufferData + HdaStreamDmaPos, HdaStreamCurrentBlock, HDA_BDL_ENTRY_COUNT));
|
|
|
|
// Fill next block.
|
|
if (HdaStream->BufferSourcePosition < BufferLength) {
|
|
HdaStreamDmaRemainingLength = HDA_BDL_BLOCKSIZE;
|
|
if ((HdaStream->BufferSourcePosition + HdaStreamDmaRemainingLength) > BufferLength)
|
|
HdaStreamDmaRemainingLength = BufferLength;
|
|
CopyMem(HdaStream->BufferData + (HdaStreamNextBlock * HDA_BDL_BLOCKSIZE), HdaStream->BufferSource + HdaStream->BufferSourcePosition, HdaStreamDmaRemainingLength);
|
|
HdaStream->BufferSourcePosition += HdaStreamDmaRemainingLength;
|
|
// DEBUG((DEBUG_INFO, "%u (0x%X) bytes written to 0x%X (block %u of %u)\n", HdaStreamDmaRemainingLength, HdaStreamDmaRemainingLength,
|
|
// HdaStream->BufferData + (HdaStreamNextBlock * HDA_BDL_BLOCKSIZE), HdaStreamNextBlock, HDA_BDL_ENTRY_COUNT));
|
|
}
|
|
|
|
// Setup polling timer.
|
|
HdaStream->BufferSourceDone = FALSE;
|
|
Status = gBS->SetTimer(HdaStream->PollTimer, TimerPeriodic, HDA_STREAM_POLL_TIME);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Change stream state.
|
|
Status = HdaControllerSetStream(HdaStream, TRUE);
|
|
if (EFI_ERROR(Status))
|
|
HdaControllerHdaIoStopStream(This, Type);
|
|
// return EFI_SUCCESS;
|
|
|
|
//STOP_STREAM:
|
|
// Stop stream.
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
HdaControllerHdaIoStopStream(
|
|
IN EFI_HDA_IO_PROTOCOL *This,
|
|
IN EFI_HDA_IO_PROTOCOL_TYPE Type)
|
|
{
|
|
//DEBUG((DEBUG_INFO, "HdaControllerHdaIoStopStream(): start\n"));
|
|
|
|
// Create variables.
|
|
EFI_STATUS Status;
|
|
HDA_IO_PRIVATE_DATA *HdaIoPrivateData;
|
|
|
|
// Stream.
|
|
HDA_STREAM *HdaStream;
|
|
UINT8 HdaStreamId;
|
|
|
|
// If a parameter is invalid, return error.
|
|
if ((This == NULL) || (Type >= EfiHdaIoTypeMaximum))
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
// Get private data.
|
|
HdaIoPrivateData = HDA_IO_PRIVATE_DATA_FROM_THIS(This);
|
|
|
|
// Get stream.
|
|
if (Type == EfiHdaIoTypeOutput)
|
|
HdaStream = HdaIoPrivateData->HdaOutputStream;
|
|
else
|
|
HdaStream = HdaIoPrivateData->HdaInputStream;
|
|
|
|
// Get current stream ID.
|
|
Status = HdaControllerGetStreamId(HdaStream, &HdaStreamId);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Is the stream ID zero? If so that means the stream is not setup yet.
|
|
if (HdaStreamId == 0)
|
|
return EFI_NOT_READY;
|
|
|
|
// Cancel polling timer.
|
|
Status = gBS->SetTimer(HdaStream->PollTimer, TimerCancel, 0);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Stop stream.
|
|
Status = HdaControllerSetStream(HdaStream, FALSE);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Remove source buffer pointer.
|
|
HdaStream->BufferSource = NULL;
|
|
HdaStream->BufferSourceLength = 0;
|
|
HdaStream->BufferSourcePosition = 0;
|
|
HdaStream->Callback = NULL;
|
|
HdaStream->CallbackContext1 = NULL;
|
|
HdaStream->CallbackContext2 = NULL;
|
|
HdaStream->CallbackContext3 = NULL;
|
|
return EFI_SUCCESS;
|
|
}
|