CloverBootloader/Drivers/AudioDxe/HdaCodec/HdaCodec.c

1164 lines
48 KiB
C
Raw Normal View History

/*
* File: HdaCodec.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 "HdaCodec.h"
#include "HdaCodecComponentName.h"
//#include <IndustryStandard/HdaCodec.h>
#include <Library/HdaModels.h>
EFI_STATUS
EFIAPI
HdaCodecProbeWidget(
IN HDA_WIDGET_DEV *HdaWidget)
{
//DEBUG((DEBUG_INFO, "HdaCodecProbeWidget(): start\n"));
// Create variables.
EFI_STATUS Status;
EFI_HDA_IO_PROTOCOL *HdaIo = HdaWidget->FuncGroup->HdaCodecDev->HdaIo;
UINT32 Response = 0;
UINT8 ConnectionListThresh;
UINT8 AmpInCount;
// Get widget capabilities.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_WIDGET_CAPS), &HdaWidget->Capabilities);
if (EFI_ERROR(Status))
return Status;
HdaWidget->Type = HDA_PARAMETER_WIDGET_CAPS_TYPE(HdaWidget->Capabilities);
HdaWidget->AmpOverride = HdaWidget->Capabilities & HDA_PARAMETER_WIDGET_CAPS_AMP_OVERRIDE;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X type: 0x%X\n", HdaWidget->NodeId, HdaWidget->Type));
//DEBUG((DEBUG_INFO, "Widget @ 0x%X capabilities: 0x%X\n", HdaWidget->NodeId, HdaWidget->Capabilities));
// Get default unsolicitation.
if (HdaWidget->Capabilities & HDA_PARAMETER_WIDGET_CAPS_UNSOL_CAPABLE) {
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_UNSOL_RESPONSE, 0), &Response);
if (EFI_ERROR(Status))
return Status;
HdaWidget->DefaultUnSol = (UINT8)Response;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X unsolicitation: 0x%X\n", HdaWidget->NodeId, HdaWidget->DefaultUnSol));
}
// Get connections.
if (HdaWidget->Capabilities & HDA_PARAMETER_WIDGET_CAPS_CONN_LIST) {
// Get connection list length.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_CONN_LIST_LENGTH), &HdaWidget->ConnectionListLength);
if (EFI_ERROR(Status))
return Status;
HdaWidget->ConnectionCount = HDA_PARAMETER_CONN_LIST_LENGTH_LEN(HdaWidget->ConnectionListLength);
//DEBUG((DEBUG_INFO, "Widget @ 0x%X connection list length: 0x%X\n", HdaWidget->NodeId, HdaWidget->ConnectionListLength));
// Get connections.
HdaWidget->Connections = AllocateZeroPool(sizeof(UINT16) * HdaWidget->ConnectionCount);
if (HdaWidget->Connections == NULL)
return EFI_OUT_OF_RESOURCES;
ConnectionListThresh = (HdaWidget->ConnectionListLength & HDA_PARAMETER_CONN_LIST_LENGTH_LONG) ? 2 : 4;
for (UINT8 c = 0; c < HdaWidget->ConnectionCount; c++) {
// Do we need to get entries?
if (!(c % ConnectionListThresh)) {
// Get connection entries.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_CONN_LIST_ENTRY, c), &Response);
if (EFI_ERROR(Status))
return Status;
}
// Populate entry list.
if ((HdaWidget->ConnectionListLength & HDA_PARAMETER_CONN_LIST_LENGTH_LONG))
HdaWidget->Connections[c] = HDA_VERB_GET_CONN_LIST_ENTRY_LONG(Response, c % 2);
else
HdaWidget->Connections[c] = HDA_VERB_GET_CONN_LIST_ENTRY_SHORT(Response, c % 4);
}
}
// Print connections.
//DEBUG((DEBUG_INFO, "Widget @ 0x%X connections (%u):", HdaWidget->NodeId, HdaWidget->ConnectionCount));
//for (UINT8 c = 0; c < HdaWidget->ConnectionCount; c++)
//DEBUG((DEBUG_INFO, " 0x%X", HdaWidget->Connections[c]));
//DEBUG((DEBUG_INFO, "\n"));
// Does the widget support power management?
if (HdaWidget->Capabilities & HDA_PARAMETER_WIDGET_CAPS_POWER_CNTRL) {
// Get supported power states.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_SUPPORTED_POWER_STATES), &HdaWidget->SupportedPowerStates);
if (EFI_ERROR(Status))
return Status;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X supported power states: 0x%X\n", HdaWidget->NodeId, HdaWidget->SupportedPowerStates));
// Get default power state.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_POWER_STATE, 0), &HdaWidget->DefaultPowerState);
if (EFI_ERROR(Status))
return Status;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X power state: 0x%X\n", HdaWidget->NodeId, HdaWidget->DefaultPowerState));
}
// Do we have input amps?
if (HdaWidget->Capabilities & HDA_PARAMETER_WIDGET_CAPS_IN_AMP) {
// Get input amp capabilities.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_AMP_CAPS_INPUT), &HdaWidget->AmpInCapabilities);
if (EFI_ERROR(Status))
return Status;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X input amp capabilities: 0x%X\n", HdaWidget->NodeId, HdaWidget->AmpInCapabilities));
// Determine number of input amps and allocate arrays.
AmpInCount = HdaWidget->ConnectionCount;
if (AmpInCount < 1)
AmpInCount = 1;
HdaWidget->AmpInLeftDefaultGainMute = AllocateZeroPool(sizeof(UINT8) * AmpInCount);
HdaWidget->AmpInRightDefaultGainMute = AllocateZeroPool(sizeof(UINT8) * AmpInCount);
if ((HdaWidget->AmpInLeftDefaultGainMute == NULL) || (HdaWidget->AmpInRightDefaultGainMute == NULL))
return EFI_OUT_OF_RESOURCES;
// Get default gain/mute for input amps.
for (UINT8 i = 0; i < AmpInCount; i++) {
// Get left.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_AMP_GAIN_MUTE,
HDA_VERB_GET_AMP_GAIN_MUTE_PAYLOAD(i, TRUE, FALSE)), &Response);
if (EFI_ERROR(Status))
return Status;
HdaWidget->AmpInLeftDefaultGainMute[i] = (UINT8)Response;
// Get right.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_AMP_GAIN_MUTE,
HDA_VERB_GET_AMP_GAIN_MUTE_PAYLOAD(i, FALSE, FALSE)), &Response);
if (EFI_ERROR(Status))
return Status;
HdaWidget->AmpInRightDefaultGainMute[i] = (UINT8)Response;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X input amp %u defaults: 0x%X 0x%X\n", HdaWidget->NodeId, i,
// HdaWidget->AmpInLeftDefaultGainMute[i], HdaWidget->AmpInRightDefaultGainMute[i]));
}
}
// Do we have an output amp?
if (HdaWidget->Capabilities & HDA_PARAMETER_WIDGET_CAPS_OUT_AMP) {
// Get output amp capabilities.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_AMP_CAPS_OUTPUT), &HdaWidget->AmpOutCapabilities);
if (EFI_ERROR(Status))
return Status;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X output amp capabilities: 0x%X\n", HdaWidget->NodeId, HdaWidget->AmpOutCapabilities));
// Get left.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_AMP_GAIN_MUTE,
HDA_VERB_GET_AMP_GAIN_MUTE_PAYLOAD(0, TRUE, TRUE)), &Response);
if (EFI_ERROR(Status))
return Status;
HdaWidget->AmpOutLeftDefaultGainMute = (UINT8)Response;
// Get right.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_AMP_GAIN_MUTE,
HDA_VERB_GET_AMP_GAIN_MUTE_PAYLOAD(0, FALSE, TRUE)), &Response);
if (EFI_ERROR(Status))
return Status;
HdaWidget->AmpOutRightDefaultGainMute = (UINT8)Response;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X output amp defaults: 0x%X 0x%X\n", HdaWidget->NodeId,
// HdaWidget->AmpOutLeftDefaultGainMute, HdaWidget->AmpOutRightDefaultGainMute));
}
// Is the widget an Input or Output?
if (HdaWidget->Type == HDA_WIDGET_TYPE_INPUT || HdaWidget->Type == HDA_WIDGET_TYPE_OUTPUT) {
if (HdaWidget->Capabilities & HDA_PARAMETER_WIDGET_CAPS_FORMAT_OVERRIDE) {
// Get supported PCM sizes/rates.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES), &HdaWidget->SupportedPcmRates);
if (EFI_ERROR(Status))
return Status;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X supported PCM sizes/rates: 0x%X\n", HdaWidget->NodeId, HdaWidget->SupportedPcmRates));
// Get supported stream formats.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_SUPPORTED_STREAM_FORMATS), &HdaWidget->SupportedFormats);
if (EFI_ERROR(Status))
return Status;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X supported formats: 0x%X\n", HdaWidget->NodeId, HdaWidget->SupportedFormats));
}
// Get default converter format.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_CONVERTER_FORMAT, 0), &Response);
if (EFI_ERROR(Status))
return Status;
HdaWidget->DefaultConvFormat = (UINT16)Response;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X default format: 0x%X\n", HdaWidget->NodeId, HdaWidget->DefaultConvFormat));
// Get default converter stream/channel.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_CONVERTER_STREAM_CHANNEL, 0), &Response);
if (EFI_ERROR(Status))
return Status;
HdaWidget->DefaultConvStreamChannel = (UINT8)Response;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X default stream/channel: 0x%X\n", HdaWidget->NodeId, HdaWidget->DefaultConvStreamChannel));
// Get default converter channel count.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_CONVERTER_CHANNEL_COUNT, 0), &Response);
if (EFI_ERROR(Status))
return Status;
HdaWidget->DefaultConvChannelCount = (UINT8)Response;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X default channel count: 0x%X\n", HdaWidget->NodeId, HdaWidget->DefaultConvChannelCount));
} else if (HdaWidget->Type == HDA_WIDGET_TYPE_PIN_COMPLEX) { // Is the widget a Pin Complex?
// Get pin capabilities.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_PIN_CAPS), &HdaWidget->PinCapabilities);
if (EFI_ERROR(Status))
return Status;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X pin capabilities: 0x%X\n", HdaWidget->NodeId, HdaWidget->PinCapabilities));
// Get default EAPD.
if (HdaWidget->PinCapabilities & HDA_PARAMETER_PIN_CAPS_EAPD) {
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_EAPD_BTL_ENABLE, 0), &Response);
if (EFI_ERROR(Status))
return Status;
HdaWidget->DefaultEapd = (UINT8)Response;
HdaWidget->DefaultEapd &= 0x7;
HdaWidget->DefaultEapd |= HDA_EAPD_BTL_ENABLE_EAPD;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X EAPD: 0x%X\n", HdaWidget->NodeId, HdaWidget->DefaultEapd));
}
// Get default pin control.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PIN_WIDGET_CONTROL, 0), &Response);
if (EFI_ERROR(Status))
return Status;
HdaWidget->DefaultPinControl = (UINT8)Response;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X default pin control: 0x%X\n", HdaWidget->NodeId, HdaWidget->DefaultPinControl));
// Get default pin configuration.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_CONFIGURATION_DEFAULT, 0), &HdaWidget->DefaultConfiguration);
if (EFI_ERROR(Status))
return Status;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X default pin configuration: 0x%X\n", HdaWidget->NodeId, HdaWidget->DefaultConfiguration));
} else if (HdaWidget->Type == HDA_WIDGET_TYPE_VOLUME_KNOB) { // Is the widget a Volume Knob?
// Get volume knob capabilities.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_VOLUME_KNOB_CAPS), &HdaWidget->VolumeCapabilities);
if (EFI_ERROR(Status))
return Status;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X volume knob capabilities: 0x%X\n", HdaWidget->NodeId, HdaWidget->VolumeCapabilities));
// Get default volume.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_VOLUME_KNOB, 0), &Response);
if (EFI_ERROR(Status))
return Status;
HdaWidget->DefaultVolume = (UINT8)Response;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X default volume: 0x%X\n", HdaWidget->NodeId, HdaWidget->DefaultVolume));
}
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
HdaCodecProbeFuncGroup(
IN HDA_FUNC_GROUP *FuncGroup) {
//DEBUG((DEBUG_INFO, "HdaCodecProbeFuncGroup(): start\n"));
// Create variables.
EFI_STATUS Status;
EFI_HDA_IO_PROTOCOL *HdaIo = FuncGroup->HdaCodecDev->HdaIo;
UINT32 Response;
UINT8 WidgetStart;
UINT8 WidgetEnd;
UINT8 WidgetCount;
HDA_WIDGET_DEV *HdaWidget;
HDA_WIDGET_DEV *HdaConnectedWidget;
// Get function group type.
Status = HdaIo->SendCommand(HdaIo, FuncGroup->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_FUNC_GROUP_TYPE), &Response);
if (EFI_ERROR(Status))
return Status;
FuncGroup->Type = HDA_PARAMETER_FUNC_GROUP_TYPE_NODETYPE(Response);
FuncGroup->UnsolCapable = (Response & HDA_PARAMETER_FUNC_GROUP_TYPE_UNSOL) != 0;
// Determine if function group is an audio one. If not, we cannot support it.
DEBUG((DEBUG_INFO, "Function group @ 0x%X is of type 0x%X\n", FuncGroup->NodeId, FuncGroup->Type));
if (FuncGroup->Type != HDA_FUNC_GROUP_TYPE_AUDIO)
return EFI_UNSUPPORTED;
// Get function group capabilities.
Status = HdaIo->SendCommand(HdaIo, FuncGroup->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_FUNC_GROUP_CAPS), &FuncGroup->Capabilities);
if (EFI_ERROR(Status))
return Status;
//DEBUG((DEBUG_INFO, "Function group @ 0x%X capabilities: 0x%X\n", FuncGroup->NodeId, FuncGroup->Capabilities));
// Get default supported PCM sizes/rates.
Status = HdaIo->SendCommand(HdaIo, FuncGroup->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_SUPPORTED_PCM_SIZE_RATES), &FuncGroup->SupportedPcmRates);
if (EFI_ERROR(Status))
return Status;
//DEBUG((DEBUG_INFO, "Function group @ 0x%X supported PCM sizes/rates: 0x%X\n", FuncGroup->NodeId, FuncGroup->SupportedPcmRates));
// Get default supported stream formats.
Status = HdaIo->SendCommand(HdaIo, FuncGroup->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_SUPPORTED_STREAM_FORMATS), &FuncGroup->SupportedFormats);
if (EFI_ERROR(Status))
return Status;
//DEBUG((DEBUG_INFO, "Function group @ 0x%X supported formats: 0x%X\n", FuncGroup->NodeId, FuncGroup->SupportedFormats));
// Get default input amp capabilities.
Status = HdaIo->SendCommand(HdaIo, FuncGroup->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_AMP_CAPS_INPUT), &FuncGroup->AmpInCapabilities);
if (EFI_ERROR(Status))
return Status;
//DEBUG((DEBUG_INFO, "Function group @ 0x%X input amp capabilities: 0x%X\n", FuncGroup->NodeId, FuncGroup->AmpInCapabilities));
// Get default output amp capabilities.
Status = HdaIo->SendCommand(HdaIo, FuncGroup->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_AMP_CAPS_OUTPUT), &FuncGroup->AmpOutCapabilities);
if (EFI_ERROR(Status))
return Status;
//DEBUG((DEBUG_INFO, "Function group @ 0x%X output amp capabilities: 0x%X\n", FuncGroup->NodeId, FuncGroup->AmpOutCapabilities));
// Get supported power states.
Status = HdaIo->SendCommand(HdaIo, FuncGroup->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_SUPPORTED_POWER_STATES), &FuncGroup->SupportedPowerStates);
if (EFI_ERROR(Status))
return Status;
//DEBUG((DEBUG_INFO, "Function group @ 0x%X supported power states: 0x%X\n", FuncGroup->NodeId, FuncGroup->SupportedPowerStates));
// Get GPIO capabilities.
Status = HdaIo->SendCommand(HdaIo, FuncGroup->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_GPIO_COUNT), &FuncGroup->GpioCapabilities);
if (EFI_ERROR(Status))
return Status;
//DEBUG((DEBUG_INFO, "Function group @ 0x%X GPIO capabilities: 0x%X\n", FuncGroup->NodeId, FuncGroup->GpioCapabilities));
// Get number of widgets in function group.
Status = HdaIo->SendCommand(HdaIo, FuncGroup->NodeId,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_SUBNODE_COUNT), &Response);
if (EFI_ERROR(Status))
return Status;
WidgetStart = HDA_PARAMETER_SUBNODE_COUNT_START(Response);
WidgetCount = HDA_PARAMETER_SUBNODE_COUNT_TOTAL(Response);
WidgetEnd = WidgetStart + WidgetCount - 1;
DEBUG((DEBUG_INFO, "Function group @ 0x%X contains %u widgets, start @ 0x%X, end @ 0x%X\n",
FuncGroup->NodeId, WidgetCount, WidgetStart, WidgetEnd));
// Power up.
Status = HdaIo->SendCommand(HdaIo, FuncGroup->NodeId, HDA_CODEC_VERB(HDA_VERB_SET_POWER_STATE, 0), &Response);
ASSERT_EFI_ERROR(Status);
// Ensure there are widgets.
if (WidgetCount == 0)
return EFI_UNSUPPORTED;
// Allocate space for widgets.
FuncGroup->Widgets = AllocateZeroPool(sizeof(HDA_WIDGET_DEV) * WidgetCount);
if (FuncGroup->Widgets == NULL)
return EFI_OUT_OF_RESOURCES;
FuncGroup->WidgetsCount = WidgetCount;
// Probe widgets.
DEBUG((DEBUG_INFO, "HdaCodecProbeFuncGroup(): probing widgets\n"));
for (UINT8 w = 0; w < WidgetCount; w++) {
// Get widget.
HdaWidget = FuncGroup->Widgets + w;
// Probe widget.
HdaWidget->FuncGroup = FuncGroup;
HdaWidget->NodeId = WidgetStart + w;
Status = HdaCodecProbeWidget(HdaWidget);
ASSERT_EFI_ERROR(Status);
// Power up.
if (HdaWidget->Capabilities & HDA_PARAMETER_WIDGET_CAPS_POWER_CNTRL) {
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_SET_POWER_STATE, 0), &Response);
ASSERT_EFI_ERROR(Status);
}
}
// Probe widget connections.
DEBUG((DEBUG_INFO, "HdaCodecProbeFuncGroup(): probing widget connections\n"));
for (UINT8 w = 0; w < WidgetCount; w++) {
// Get widget.
HdaWidget = FuncGroup->Widgets + w;
// Get connections.
if (HdaWidget->ConnectionCount > 0) {
// Allocate array of widget pointers.
HdaWidget->WidgetConnections = AllocateZeroPool(sizeof(HDA_WIDGET_DEV*) * HdaWidget->ConnectionCount);
if (HdaWidget->WidgetConnections == NULL)
return EFI_OUT_OF_RESOURCES;
// Populate array.
for (UINT8 c = 0; c < HdaWidget->ConnectionCount; c++) {
// Get widget index.
// This can be gotten using the node ID of the connection minus our starting node ID.
UINT16 WidgetIndex = HdaWidget->Connections[c] - WidgetStart;
if (WidgetIndex < 0) {
DEBUG((DEBUG_INFO, "Widget @ 0x%X error connection to index %u (0x%X) is invalid\n", WidgetIndex, HdaWidget->Connections[c]));
continue;
}
// Save pointer to widget.
HdaConnectedWidget = FuncGroup->Widgets + WidgetIndex;
//DEBUG((DEBUG_INFO, "Widget @ 0x%X found connection to index %u (0x%X, type 0x%X)\n",
// HdaWidget->NodeId, WidgetIndex, HdaConnectedWidget->NodeId, HdaConnectedWidget->Type));
HdaWidget->WidgetConnections[c] = HdaConnectedWidget;
}
}
}
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
HdaCodecProbeCodec(
IN HDA_CODEC_DEV *HdaCodecDev)
{
//DEBUG((DEBUG_INFO, "HdaCodecProbeCodec(): start\n"));
// Create variables.
EFI_STATUS Status;
EFI_HDA_IO_PROTOCOL *HdaIo = HdaCodecDev->HdaIo;
UINT32 Response;
UINT8 FuncStart;
UINT8 FuncEnd;
UINT8 FuncCount;
// Get vendor and device ID.
Status = HdaIo->SendCommand(HdaIo, HDA_NID_ROOT,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_VENDOR_ID), &HdaCodecDev->VendorId);
if (EFI_ERROR(Status))
return Status;
DEBUG((DEBUG_INFO, "Codec ID: 0x%X:0x%X\n", HDA_PARAMETER_VENDOR_ID_VEN(HdaCodecDev->VendorId), HDA_PARAMETER_VENDOR_ID_DEV(HdaCodecDev->VendorId)));
// Get revision ID.
Status = HdaIo->SendCommand(HdaIo, HDA_NID_ROOT,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_REVISION_ID), &HdaCodecDev->RevisionId);
if (EFI_ERROR(Status))
return Status;
HdaCodecGetName(HdaCodecDev->VendorId, (UINT16)HdaCodecDev->RevisionId, &HdaCodecDev->Name);
// Get function group count.
Status = HdaIo->SendCommand(HdaIo, HDA_NID_ROOT,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_SUBNODE_COUNT), &Response);
if (EFI_ERROR(Status))
return Status;
FuncStart = HDA_PARAMETER_SUBNODE_COUNT_START(Response);
FuncCount = HDA_PARAMETER_SUBNODE_COUNT_TOTAL(Response);
FuncEnd = FuncStart + FuncCount - 1;
DEBUG((DEBUG_INFO, "Codec contains %u function groups, start @ 0x%X, end @ 0x%X\n", FuncCount, FuncStart, FuncEnd));
// Ensure there are functions.
if (FuncCount == 0)
return EFI_UNSUPPORTED;
// Allocate space for function groups.
HdaCodecDev->FuncGroups = AllocateZeroPool(sizeof(HDA_FUNC_GROUP) * FuncCount);
if (HdaCodecDev->FuncGroups == NULL)
return EFI_OUT_OF_RESOURCES;
HdaCodecDev->FuncGroupsCount = FuncCount;
HdaCodecDev->AudioFuncGroup = NULL;
// Probe functions.
for (UINT8 i = 0; i < FuncCount; i++) {
HdaCodecDev->FuncGroups[i].HdaCodecDev = HdaCodecDev;
HdaCodecDev->FuncGroups[i].NodeId = FuncStart + i;
Status = HdaCodecProbeFuncGroup(HdaCodecDev->FuncGroups + i);
if (!(EFI_ERROR(Status)) && (HdaCodecDev->AudioFuncGroup == NULL))
HdaCodecDev->AudioFuncGroup = HdaCodecDev->FuncGroups + i;
}
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
HdaCodecFindUpstreamOutput(
IN HDA_WIDGET_DEV *HdaWidget,
IN UINT8 Level)
{
EFI_STATUS Status;
HDA_WIDGET_DEV *HdaConnectedWidget;
//DEBUG((DEBUG_INFO, "HdaCodecFindUpstreamOutput(): start\n"));
// If level is above 15, we may have entered an infinite loop so just give up.
if (Level > 15) {
return EFI_ABORTED;
}
// Go through connections and check for Output widgets.
for (UINT8 c = 0; c < HdaWidget->ConnectionCount; c++) {
// Get connected widget.
HdaConnectedWidget = HdaWidget->WidgetConnections[c];
for (UINT8 i = 0; i <= Level; i++) {
DEBUG((DEBUG_INFO, " "));
}
DEBUG((DEBUG_INFO, "Widget @ 0x%X (type 0x%X)\n", HdaConnectedWidget->NodeId, HdaConnectedWidget->Type));
// If this is an Output, we are done.
if (HdaConnectedWidget->Type == HDA_WIDGET_TYPE_OUTPUT) {
HdaWidget->UpstreamWidget = HdaConnectedWidget;
HdaWidget->UpstreamIndex = c;
return EFI_SUCCESS;
}
// Check connections of connected widget.
// If a success status is returned, that means an Output widget was found and we are done.
Status = HdaCodecFindUpstreamOutput(HdaConnectedWidget, Level + 1);
if (Status == EFI_SUCCESS) {
HdaWidget->UpstreamWidget = HdaConnectedWidget;
HdaWidget->UpstreamIndex = c;
return EFI_SUCCESS;
}
}
// We didn't find an Output if we got here (probably zero connections).
return EFI_NOT_FOUND;
}
EFI_STATUS
EFIAPI
HdaCodecParsePorts(
IN HDA_CODEC_DEV *HdaCodecDev) {
//DEBUG((DEBUG_INFO, "HdaCodecParsePorts(): start\n"));
// Create variables.
EFI_STATUS Status;
EFI_HDA_IO_PROTOCOL *HdaIo = HdaCodecDev->HdaIo;
HDA_FUNC_GROUP *HdaFuncGroup;
HDA_WIDGET_DEV *HdaWidget;
UINT8 DefaultDeviceType;
UINT32 Response;
// Loop through each function group.
for (UINT8 f = 0; f < HdaCodecDev->FuncGroupsCount; f++) {
// Get function group.
HdaFuncGroup = HdaCodecDev->FuncGroups + f;
// Loop through each widget.
for (UINT8 w = 0; w < HdaFuncGroup->WidgetsCount; w++) {
// Get widget.
HdaWidget = HdaFuncGroup->Widgets + w;
// Is the widget a pin complex? If not, ignore it.
// If this is a pin complex but it has no connection to a port, also ignore it.
// If the default association for the pin complex is zero, also ignore it.
if ((HdaWidget->Type != HDA_WIDGET_TYPE_PIN_COMPLEX) ||
(HDA_VERB_GET_CONFIGURATION_DEFAULT_PORT_CONN(HdaWidget->DefaultConfiguration) == HDA_CONFIG_DEFAULT_PORT_CONN_NONE) ||
(HDA_VERB_GET_CONFIGURATION_DEFAULT_ASSOCIATION(HdaWidget->DefaultConfiguration) == 0))
continue;
// Determine if port is an output based on the device type.
DefaultDeviceType = HDA_VERB_GET_CONFIGURATION_DEFAULT_DEVICE(HdaWidget->DefaultConfiguration);
if ((DefaultDeviceType == HDA_CONFIG_DEFAULT_DEVICE_LINE_OUT) || (DefaultDeviceType == HDA_CONFIG_DEFAULT_DEVICE_SPEAKER) ||
(DefaultDeviceType == HDA_CONFIG_DEFAULT_DEVICE_HEADPHONE_OUT) || (DefaultDeviceType == HDA_CONFIG_DEFAULT_DEVICE_SPDIF_OUT) ||
(DefaultDeviceType == HDA_CONFIG_DEFAULT_DEVICE_OTHER_DIGITAL_OUT)) {
// Try to get upstream output.
DEBUG((DEBUG_INFO, "Port widget @ 0x%X is an output (pin defaults 0x%X)\n", HdaWidget->NodeId, HdaWidget->DefaultConfiguration));
Status = HdaCodecFindUpstreamOutput(HdaWidget, 0);
if (EFI_ERROR(Status))
continue;
// Enable output amp.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_SET_PIN_WIDGET_CONTROL,
HDA_VERB_SET_PIN_WIDGET_CONTROL_PAYLOAD(0, FALSE, FALSE, TRUE, FALSE)), &Response);
if (EFI_ERROR(Status))
continue;
// If EAPD is present, enable.
if (HdaWidget->PinCapabilities & HDA_PARAMETER_PIN_CAPS_EAPD) {
// Get current EAPD setting.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_GET_EAPD_BTL_ENABLE, 0), &Response);
if (EFI_ERROR(Status))
return Status;
// If the EAPD is not set, set it.
if (!(Response & HDA_EAPD_BTL_ENABLE_EAPD)) {
Response |= HDA_EAPD_BTL_ENABLE_EAPD;
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_SET_EAPD_BTL_ENABLE,
(UINT8)Response), &Response);
if (EFI_ERROR(Status))
return Status;
}
}
// If the output amp supports muting, unmute.
if (HdaWidget->AmpOutCapabilities & HDA_PARAMETER_AMP_CAPS_MUTE) {
UINT8 offset = HDA_PARAMETER_AMP_CAPS_OFFSET(HdaWidget->AmpOutCapabilities); // TODO set volume.
// If there are no overriden amp capabilities, check function group.
if (!(HdaWidget->AmpOverride))
offset = HDA_PARAMETER_AMP_CAPS_OFFSET(HdaWidget->FuncGroup->AmpOutCapabilities);
// Unmute amp.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_SET_AMP_GAIN_MUTE,
HDA_VERB_SET_AMP_GAIN_MUTE_PAYLOAD(0, offset, FALSE, TRUE, TRUE, FALSE, TRUE)), &Response);
if (EFI_ERROR(Status))
return Status;
}
// Reallocate output array.
HdaCodecDev->OutputPorts = ReallocatePool(sizeof(HDA_WIDGET_DEV*) * HdaCodecDev->OutputPortsCount, sizeof(HDA_WIDGET_DEV*) * (HdaCodecDev->OutputPortsCount + 1), HdaCodecDev->OutputPorts);
if (HdaCodecDev->OutputPorts == NULL)
return EFI_OUT_OF_RESOURCES;
HdaCodecDev->OutputPortsCount++;
// Add widget to output array.
HdaCodecDev->OutputPorts[HdaCodecDev->OutputPortsCount - 1] = HdaWidget;
}
}
}
// Wait 1000ms for all widgets to fully come on.
gBS->Stall(MS_TO_MICROSECOND(1000));
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
HdaCodecInstallProtocols(
IN HDA_CODEC_DEV *HdaCodecDev) {
DEBUG((DEBUG_INFO, "HdaCodecInstallProtocols(): start\n"));
// Create variables.
EFI_STATUS Status;
HDA_CODEC_INFO_PRIVATE_DATA *HdaCodecInfoData;
AUDIO_IO_PRIVATE_DATA *AudioIoData;
// Allocate space for protocol data.
HdaCodecInfoData = AllocateZeroPool(sizeof(HDA_CODEC_INFO_PRIVATE_DATA));
AudioIoData = AllocateZeroPool(sizeof(AUDIO_IO_PRIVATE_DATA));
if ((HdaCodecInfoData == NULL) || (AudioIoData == NULL)) {
Status = EFI_OUT_OF_RESOURCES;
goto FREE_POOLS;
}
// Populate info protocol data.
HdaCodecInfoData->Signature = HDA_CODEC_PRIVATE_DATA_SIGNATURE;
HdaCodecInfoData->HdaCodecDev = HdaCodecDev;
HdaCodecInfoData->HdaCodecInfo.GetName = HdaCodecInfoGetCodecName;
HdaCodecInfoData->HdaCodecInfo.GetVendorId = HdaCodecInfoGetVendorId;
HdaCodecInfoData->HdaCodecInfo.GetRevisionId = HdaCodecInfoGetRevisionId;
HdaCodecInfoData->HdaCodecInfo.GetAudioFuncId = HdaCodecInfoGetAudioFuncId;
HdaCodecInfoData->HdaCodecInfo.GetDefaultRatesFormats = HdaCodecInfoGetDefaultRatesFormats;
HdaCodecInfoData->HdaCodecInfo.GetDefaultAmpCaps = HdaCodecInfoGetDefaultAmpCaps;
HdaCodecInfoData->HdaCodecInfo.GetWidgets = HdaCodecInfoGetWidgets;
HdaCodecInfoData->HdaCodecInfo.FreeWidgetsBuffer = HdaCodecInfoFreeWidgetsBuffer;
HdaCodecDev->HdaCodecInfoData = HdaCodecInfoData;
// Populate I/O protocol data.
AudioIoData->Signature = HDA_CODEC_PRIVATE_DATA_SIGNATURE;
AudioIoData->HdaCodecDev = HdaCodecDev;
AudioIoData->AudioIo.GetOutputs = HdaCodecAudioIoGetOutputs;
AudioIoData->AudioIo.SetupPlayback = HdaCodecAudioIoSetupPlayback;
AudioIoData->AudioIo.StartPlayback = HdaCodecAudioIoStartPlayback;
AudioIoData->AudioIo.StartPlaybackAsync = HdaCodecAudioIoStartPlaybackAsync;
AudioIoData->AudioIo.StopPlayback = HdaCodecAudioIoStopPlayback;
HdaCodecDev->AudioIoData = AudioIoData;
// Install protocols.
Status = gBS->InstallMultipleProtocolInterfaces(&HdaCodecDev->ControllerHandle,
&gEfiHdaCodecInfoProtocolGuid, &HdaCodecInfoData->HdaCodecInfo,
&gEfiAudioIoProtocolGuid, &AudioIoData->AudioIo,
&gEfiCallerIdGuid, HdaCodecDev, NULL);
if (EFI_ERROR(Status))
goto FREE_POOLS;
return EFI_SUCCESS;
FREE_POOLS:
if (HdaCodecInfoData != NULL)
FreePool(HdaCodecInfoData);
if (AudioIoData != NULL)
FreePool(AudioIoData);
return Status;
}
EFI_STATUS
EFIAPI
HdaCodecGetOutputDac(
IN HDA_WIDGET_DEV *HdaWidget,
OUT HDA_WIDGET_DEV **HdaOutputWidget)
{
DEBUG((DEBUG_INFO, "HdaCodecGetOutputDac(): start\n"));
// Check that parameters are valid.
if ((HdaWidget == NULL) || (HdaOutputWidget == NULL))
return EFI_INVALID_PARAMETER;
// Crawl through widget path looking for output DAC.
while (HdaWidget != NULL) {
// Is this widget an output DAC?
if (HdaWidget->Type == HDA_WIDGET_TYPE_OUTPUT) {
*HdaOutputWidget = HdaWidget;
return EFI_SUCCESS;
}
// Move to upstream widget.
HdaWidget = HdaWidget->UpstreamWidget;
}
// If we get here, we couldn't find the DAC.
return EFI_NOT_FOUND;
}
EFI_STATUS
EFIAPI
HdaCodecGetSupportedPcmRates(
IN HDA_WIDGET_DEV *HdaPinWidget,
OUT UINT32 *SupportedRates)
{
EFI_STATUS Status;
HDA_WIDGET_DEV *HdaOutputWidget;
// DEBUG((DEBUG_INFO, "HdaCodecGetSupportedPcmRates(): start\n"));
// Check that parameters are valid.
if ((HdaPinWidget == NULL) || (SupportedRates == NULL))
return EFI_INVALID_PARAMETER;
// Get output DAC widget.
Status = HdaCodecGetOutputDac(HdaPinWidget, &HdaOutputWidget);
if (EFI_ERROR(Status))
return Status;
// Does the widget specify format info?
if (HdaOutputWidget->Capabilities & HDA_PARAMETER_WIDGET_CAPS_FORMAT_OVERRIDE) {
// Check widget for PCM support.
if (!(HdaOutputWidget->SupportedFormats & HDA_PARAMETER_SUPPORTED_STREAM_FORMATS_PCM))
return EFI_UNSUPPORTED;
*SupportedRates = HdaOutputWidget->SupportedPcmRates;
} else {
// Check function group for PCM support.
if (!(HdaOutputWidget->FuncGroup->SupportedFormats & HDA_PARAMETER_SUPPORTED_STREAM_FORMATS_PCM))
return EFI_UNSUPPORTED;
*SupportedRates = HdaOutputWidget->FuncGroup->SupportedPcmRates;
}
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
HdaCodecDisableWidgetPath(
IN HDA_WIDGET_DEV *HdaWidget)
{
// Create variables.
EFI_STATUS Status;
EFI_HDA_IO_PROTOCOL *HdaIo = HdaWidget->FuncGroup->HdaCodecDev->HdaIo;
UINT32 Response = 0;
//DEBUG((DEBUG_INFO, "HdaCodecDisableWidgetPath(): start\n"));
// Check if widget is valid.
if (HdaWidget == NULL)
return EFI_INVALID_PARAMETER;
// Crawl through widget path.
while (HdaWidget != NULL) {
// If Output, disable stream.
if (HdaWidget->Type == HDA_WIDGET_TYPE_OUTPUT) {
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_SET_CONVERTER_STREAM_CHANNEL,
HDA_VERB_SET_CONVERTER_STREAM_PAYLOAD(0, 0)), &Response);
if (EFI_ERROR(Status))
return Status;
}
// If widget is a pin complex, disable output.
if (HdaWidget->Type == HDA_WIDGET_TYPE_PIN_COMPLEX) {
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_SET_PIN_WIDGET_CONTROL,
HDA_VERB_SET_PIN_WIDGET_CONTROL_PAYLOAD(0, FALSE, FALSE, FALSE, FALSE)), &Response);
if (EFI_ERROR(Status))
return Status;
}
// Move to upstream widget.
HdaWidget = HdaWidget->UpstreamWidget;
}
// Path disabled.
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
HdaCodecEnableWidgetPath(
IN HDA_WIDGET_DEV *HdaWidget,
IN UINT8 Volume,
IN UINT8 StreamId,
IN UINT16 StreamFormat)
{
// Create variables.
EFI_STATUS Status;
EFI_HDA_IO_PROTOCOL *HdaIo = HdaWidget->FuncGroup->HdaCodecDev->HdaIo;
UINT32 Response = 0;
//DEBUG((DEBUG_INFO, "HdaCodecEnableWidgetPath(): start\n"));
// Check if widget is valid.
if ((HdaWidget == NULL) || (Volume > EFI_AUDIO_IO_PROTOCOL_MAX_VOLUME))
return EFI_INVALID_PARAMETER;
// Crawl through widget path.
while (HdaWidget != NULL) {
DEBUG((DEBUG_INFO, "Widget @ 0x%X setting up\n", HdaWidget->NodeId));
// If pin complex, set as output.
if (HdaWidget->Type == HDA_WIDGET_TYPE_PIN_COMPLEX) {
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_SET_PIN_WIDGET_CONTROL,
HDA_VERB_SET_PIN_WIDGET_CONTROL_PAYLOAD(0, FALSE, FALSE, TRUE, FALSE)), &Response);
if (EFI_ERROR(Status))
return Status;
// If EAPD, enable.
if (HdaWidget->PinCapabilities & HDA_PARAMETER_PIN_CAPS_EAPD) {
// Get current EAPD setting.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_GET_EAPD_BTL_ENABLE, 0), &Response);
if (EFI_ERROR(Status))
return Status;
// If the EAPD is not set, set it.
if (!(Response & HDA_EAPD_BTL_ENABLE_EAPD)) {
Response |= HDA_EAPD_BTL_ENABLE_EAPD;
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_SET_EAPD_BTL_ENABLE,
(UINT8)Response), &Response);
if (EFI_ERROR(Status))
return Status;
}
}
}
// If this is a digital widget, enable digital output.
if (HdaWidget->Capabilities & HDA_PARAMETER_WIDGET_CAPS_DIGITAL) {
// Enable digital output.
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_SET_DIGITAL_CONV_CONTROL1,
HDA_DIGITAL_CONV_CONTROL_DIGEN), &Response);
if (EFI_ERROR(Status))
return Status;
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_SET_ASP_MAPPING,
0x00), &Response);
if (EFI_ERROR(Status))
return Status;
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_SET_ASP_MAPPING,
0x11), &Response);
if (EFI_ERROR(Status))
return Status;
}
// If there is an output amp, unmute.
if (HdaWidget->Capabilities & HDA_PARAMETER_WIDGET_CAPS_OUT_AMP) {
UINT8 offset = HDA_PARAMETER_AMP_CAPS_OFFSET(HdaWidget->AmpOutCapabilities); // TODO set volume.
// If there are no overriden amp capabilities, check function group.
if (!(HdaWidget->AmpOverride))
offset = HDA_PARAMETER_AMP_CAPS_OFFSET(HdaWidget->FuncGroup->AmpOutCapabilities);
// Calculate offset.
offset = (offset * Volume) / EFI_AUDIO_IO_PROTOCOL_MAX_VOLUME;
DEBUG((DEBUG_INFO, "HdaCodecEnableWidgetPath(): Amp out offset 0x%X\n", offset));
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_SET_AMP_GAIN_MUTE,
HDA_VERB_SET_AMP_GAIN_MUTE_PAYLOAD(0, offset, FALSE, TRUE, TRUE, FALSE, TRUE)), &Response);
if (EFI_ERROR(Status))
return Status;
}
// If there are input amps, mute all but the upstream.
if (HdaWidget->Capabilities & HDA_PARAMETER_WIDGET_CAPS_IN_AMP) {
DEBUG((DEBUG_INFO, "Widget @ 0x%X in amp\n", HdaWidget->NodeId));
for (UINT8 c = 0; c < HdaWidget->ConnectionCount; c++) {
if (HdaWidget->UpstreamIndex == c) {
UINT8 offset = HDA_PARAMETER_AMP_CAPS_OFFSET(HdaWidget->AmpInCapabilities);
// If there are no overriden amp capabilities, check function group.
if (!(HdaWidget->AmpOverride))
offset = HDA_PARAMETER_AMP_CAPS_OFFSET(HdaWidget->FuncGroup->AmpInCapabilities);
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_SET_AMP_GAIN_MUTE,
HDA_VERB_SET_AMP_GAIN_MUTE_PAYLOAD(c, offset, FALSE, TRUE, TRUE, TRUE, FALSE)), &Response);
if (EFI_ERROR(Status))
return Status;
} else {
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_SET_AMP_GAIN_MUTE,
HDA_VERB_SET_AMP_GAIN_MUTE_PAYLOAD(c, 0, TRUE, TRUE, TRUE, TRUE, FALSE)), &Response);
if (EFI_ERROR(Status))
return Status;
}
}
}
// If there is more than one connection, select our upstream.
if (HdaWidget->ConnectionCount > 1) {
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_SET_CONN_SELECT_CONTROL,
HdaWidget->UpstreamIndex), &Response);
if (EFI_ERROR(Status))
return Status;
}
// If Output, set up stream.
if (HdaWidget->Type == HDA_WIDGET_TYPE_OUTPUT) {
DEBUG((DEBUG_INFO, "Widget @ 0x%X output\n", HdaWidget->NodeId));
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_SET_CONVERTER_FORMAT,
StreamFormat), &Response);
if (EFI_ERROR(Status))
return Status;
Status = HdaIo->SendCommand(HdaIo, HdaWidget->NodeId, HDA_CODEC_VERB(HDA_VERB_SET_CONVERTER_STREAM_CHANNEL,
HDA_VERB_SET_CONVERTER_STREAM_PAYLOAD(0, StreamId)), &Response);
if (EFI_ERROR(Status))
return Status;
}
// Move to upstream widget.
HdaWidget = HdaWidget->UpstreamWidget;
}
return EFI_SUCCESS;
}
VOID
EFIAPI
HdaCodecCleanup(
IN HDA_CODEC_DEV *HdaCodecDev)
{
// DEBUG((DEBUG_INFO, "HdaCodecCleanup(): start\n"));
// Create variables.
EFI_STATUS Status;
HDA_FUNC_GROUP *HdaFuncGroup;
HDA_WIDGET_DEV *HdaWidget;
// If codec is already clear, we are done.
if (HdaCodecDev == NULL)
return;
// Clean HDA Codec Info protocol.
if (HdaCodecDev->HdaCodecInfoData != NULL) {
// Uninstall protocol.
DEBUG((DEBUG_INFO, "HdaCodecCleanup(): clean Hda Codec Info\n"));
Status = gBS->UninstallProtocolInterface(HdaCodecDev->ControllerHandle,
&gEfiHdaCodecInfoProtocolGuid, &HdaCodecDev->HdaCodecInfoData->HdaCodecInfo);
ASSERT_EFI_ERROR(Status);
// Free data.
FreePool(HdaCodecDev->HdaCodecInfoData);
}
// Clean Audio I/O protocol.
if (HdaCodecDev->AudioIoData != NULL) {
// Uninstall protocol.
DEBUG((DEBUG_INFO, "HdaCodecCleanup(): clean Audio I/O\n"));
Status = gBS->UninstallProtocolInterface(HdaCodecDev->ControllerHandle,
&gEfiAudioIoProtocolGuid, &HdaCodecDev->AudioIoData->AudioIo);
ASSERT_EFI_ERROR(Status);
// Free data.
FreePool(HdaCodecDev->AudioIoData);
}
// Clean up input and output port arrays.
if (HdaCodecDev->OutputPorts != NULL)
FreePool(HdaCodecDev->OutputPorts);
if (HdaCodecDev->InputPorts != NULL)
FreePool(HdaCodecDev->InputPorts);
// Clean function groups.
if (HdaCodecDev->FuncGroups != NULL) {
// Clean each function group.
for (UINT8 f = 0; f < HdaCodecDev->FuncGroupsCount; f++) {
HdaFuncGroup = HdaCodecDev->FuncGroups + f;
// Clean widgets in function group.
if (HdaFuncGroup->Widgets != NULL) {
for (UINT8 w = 0; w < HdaFuncGroup->WidgetsCount; w++) {
HdaWidget = HdaFuncGroup->Widgets + w;
// Clean input amp default arrays.
if (HdaWidget->AmpInLeftDefaultGainMute != NULL)
FreePool(HdaWidget->AmpInLeftDefaultGainMute);
if (HdaWidget->AmpInRightDefaultGainMute != NULL)
FreePool(HdaWidget->AmpInRightDefaultGainMute);
// Clean connections array.
if (HdaWidget->WidgetConnections != NULL)
FreePool(HdaWidget->WidgetConnections);
if (HdaWidget->Connections != NULL)
FreePool(HdaWidget->Connections);
}
// Free widgets array.
FreePool(HdaFuncGroup->Widgets);
}
}
// Free function group array.
FreePool(HdaCodecDev->FuncGroups);
}
// Free codec device.
gBS->UninstallProtocolInterface(HdaCodecDev->ControllerHandle,
&gEfiCallerIdGuid, HdaCodecDev);
FreePool(HdaCodecDev);
}
EFI_STATUS
EFIAPI
HdaCodecDriverBindingSupported(
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL)
{
// Create variables.
EFI_STATUS Status;
EFI_HDA_IO_PROTOCOL *HdaIo;
UINT8 CodecAddress;
// Attempt to open the HDA codec protocol. If it can be opened, we can support it.
Status = gBS->OpenProtocol(ControllerHandle, &gEfiHdaIoProtocolGuid, (VOID**)&HdaIo,
This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
if (EFI_ERROR(Status))
return Status;
// Get address of codec.
Status = HdaIo->GetAddress(HdaIo, &CodecAddress);
if (EFI_ERROR(Status))
goto CLOSE_CODEC;
// Codec can be supported.
DEBUG((DEBUG_INFO, "HdaCodecDriverBindingSupported(): attaching to codec 0x%X\n", CodecAddress));
Status = EFI_SUCCESS;
CLOSE_CODEC:
// Close protocol.
gBS->CloseProtocol(ControllerHandle, &gEfiHdaIoProtocolGuid, This->DriverBindingHandle, ControllerHandle);
return Status;
}
EFI_STATUS
EFIAPI
HdaCodecDriverBindingStart(
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL)
{
// DEBUG((DEBUG_INFO, "HdaCodecDriverBindingStart(): start\n"));
// Create variables.
EFI_STATUS Status;
EFI_HDA_IO_PROTOCOL *HdaIo;
EFI_DEVICE_PATH_PROTOCOL *HdaCodecDevicePath;
HDA_CODEC_DEV *HdaCodecDev;
// Open HDA I/O protocol.
Status = gBS->OpenProtocol(ControllerHandle, &gEfiHdaIoProtocolGuid, (VOID**)&HdaIo,
This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
if (EFI_ERROR(Status))
return Status;
// Open Device Path protocol.
Status = gBS->OpenProtocol(ControllerHandle, &gEfiDevicePathProtocolGuid, (VOID**)&HdaCodecDevicePath,
This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
if (EFI_ERROR(Status))
goto CLOSE_CODEC;
// Allocate codec device.
HdaCodecDev = AllocateZeroPool(sizeof(HDA_CODEC_DEV));
if (HdaCodecDev == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto CLOSE_CODEC;
}
// Fill codec device data.
HdaCodecDev->Signature = HDA_CODEC_PRIVATE_DATA_SIGNATURE;
HdaCodecDev->HdaIo = HdaIo;
HdaCodecDev->DevicePath = HdaCodecDevicePath;
HdaCodecDev->ControllerHandle = ControllerHandle;
// Probe codec.
Status = HdaCodecProbeCodec(HdaCodecDev);
if (EFI_ERROR(Status))
goto FREE_CODEC;
// Get ports.
Status = HdaCodecParsePorts(HdaCodecDev);
if (EFI_ERROR(Status))
goto FREE_CODEC;
// Publish protocols.
Status = HdaCodecInstallProtocols(HdaCodecDev);
ASSERT_EFI_ERROR(Status);
if (EFI_ERROR(Status))
goto FREE_CODEC;
// Success.
return EFI_SUCCESS;
FREE_CODEC:
// Cleanup codec.
HdaCodecCleanup(HdaCodecDev);
CLOSE_CODEC:
// Close protocols.
gBS->CloseProtocol(ControllerHandle, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, ControllerHandle);
gBS->CloseProtocol(ControllerHandle, &gEfiHdaIoProtocolGuid, This->DriverBindingHandle, ControllerHandle);
return Status;
}
EFI_STATUS
EFIAPI
HdaCodecDriverBindingStop(
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer OPTIONAL)
{
// DEBUG((DEBUG_INFO, "HdaCodecDriverBindingStop(): start\n"));
// Create variables.
EFI_STATUS Status;
HDA_CODEC_DEV *HdaCodecDev;
// Get codec device.
Status = gBS->OpenProtocol(ControllerHandle, &gEfiCallerIdGuid, (VOID**)&HdaCodecDev,
This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (!(EFI_ERROR(Status))) {
// Ensure codec device is valid.
if (HdaCodecDev->Signature != HDA_CODEC_PRIVATE_DATA_SIGNATURE)
return EFI_INVALID_PARAMETER;
// Cleanup codec.
HdaCodecCleanup(HdaCodecDev);
}
// Close protocols.
gBS->CloseProtocol(ControllerHandle, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, ControllerHandle);
gBS->CloseProtocol(ControllerHandle, &gEfiHdaIoProtocolGuid, This->DriverBindingHandle, ControllerHandle);
return EFI_SUCCESS;
}