mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-27 16:58:09 +01:00
109d1f581c
Signed-off-by: SergeySlice <sergey.slice@gmail.com>
1167 lines
48 KiB
C
1167 lines
48 KiB
C
/*
|
|
* 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;
|
|
}
|
|
|