提交 027a2ef1 编写于 作者: M MikeBeaton 提交者: mikebeaton

AudioDxe: Use PinCaps for outputs, switch to general purpose VREF and GPIO enable

上级 ee280771
......@@ -6,16 +6,17 @@ OpenCore Changelog
- Fixed typo in `Cpuid1Data` recommendations for Intel Rocket Lake and newer
- Updated builtin firmware versions for SMBIOS and the rest
- Updated underlying EDK II package to edk2-stable202111
- Resolved two possible crashes in QEMU in AudioDxe
- Resolved crashes in QEMU with AudioDxe
- Added AudioDxe settings caching (avoids non-needed setup delays)
- Added DisconnectHda quirk to allow UEFI sound on Apple hardware (and others)
- Added autodetected Cirrus Logic GPIO quirk to enable UEFI audio on some Apple hardware
- Added workarounds for bugs in QEMU intel-hda driver to allow UEFI sound in QEMU
- Added DisconnectHda quirk to allow UEFI sound on Apple hardware and others
- Added workarounds for bugs in QEMU `intel-hda` driver to allow UEFI sound in QEMU
- Implemented multi-channel (e.g. bass+main speaker; speakers+headphones) UEFI sound with `AudioOutMask`
- Fixed AudioDxe startup stalls when Nvidia HDA audio present
- Fixed AudioDxe startup stalls when Nvidia HDA audio is present
- Resolved AudioDxe disabling sound in Windows on some firmware
- Added pointer polling period tuning in the builtin AppleEvent implementation
- Added pointer device list tuning in the builtin AppleEvent implementation
- Added audio GPIO and VREF handling to support UEFI sound on more Apple hardware
- Updated audio output channel detection to support UEFI sound on more Apple hardware
#### v0.7.6
- Fixed stack canary support when compiling with GCC
......
b8ee5f959708f44a41c5560f6eeee820
5833db8773b4797dcb202f66d0972912
......@@ -6952,14 +6952,15 @@ with the boot menu.
\texttt{OCAU: 2/3 \textit{\textbf{PciRoot(0x0)/Pci(0x3,0x0)}}/VenMsg(<redacted>,00000000) (1 outputs)}\\
\texttt{OCAU: 3/3 \textit{\textbf{PciRoot(0x0)/Pci(0x1B,0x0)}}/VenMsg(<redacted>,02000000) (7 outputs)}
If using \texttt{AudioDxe}, the required device path is also output as:
If using \texttt{AudioDxe}, the available controller device paths are also output on lines formatted like this:
\texttt{HDA: Connecting controller - \textit{\textbf{PciRoot(0x0)/Pci(0x1B,0x0)}}}
Finally, \texttt{gfxutil -f HDEF} command can be used in macOS to obtain the device path.
Specifying an empty device path will result in the first available audio controller being used, and can
be a convenient option to get UEFI audio working if only one audio controller is present.
Specifying an empty device path results in the first available codec and audio controller being used. The value
of \texttt{AudioCodec} is ignored in this case. This can be a convenient initial option to try to get UEFI
audio working. Manual settings as above will be required when this default value does not work.
\item
\texttt{AudioOutMask}\\
......
\documentclass[]{article}
%DIF LATEXDIFF DIFFERENCE FILE
%DIF DEL PreviousConfiguration.tex Wed Dec 8 21:04:04 2021
%DIF ADD ../Configuration.tex Mon Jan 3 20:26:21 2022
%DIF DEL PreviousConfiguration.tex Fri Dec 17 13:37:32 2021
%DIF ADD ../Configuration.tex Tue Jan 4 07:23:37 2022
\usepackage{lmodern}
\usepackage{amssymb,amsmath}
......@@ -7033,7 +7033,7 @@ with the boot menu.
\texttt{OCAU: 2/3 \textit{\textbf{PciRoot(0x0)/Pci(0x3,0x0)}}/VenMsg(<redacted>,00000000) (1 outputs)}\\
\texttt{OCAU: 3/3 \textit{\textbf{PciRoot(0x0)/Pci(0x1B,0x0)}}/VenMsg(<redacted>,02000000) (7 outputs)}
\DIFdelbegin \DIFdel{As an alternative, }\DIFdelend \DIFaddbegin \DIFadd{If using }\texttt{\DIFadd{AudioDxe}}\DIFadd{, the required device path is also output as:
\DIFdelbegin \DIFdel{As an alternative, }\DIFdelend \DIFaddbegin \DIFadd{If using }\texttt{\DIFadd{AudioDxe}}\DIFadd{, the available controller device paths are also output on lines formatted like this:
}
\texttt{\DIFadd{HDA: Connecting controller - }\textit{\textbf{\DIFadd{PciRoot(0x0)/Pci(0x1B,0x0)}}}}
......@@ -7041,8 +7041,10 @@ with the boot menu.
\DIFadd{Finally, }\DIFaddend \texttt{gfxutil -f HDEF} command can be used in macOS \DIFdelbegin \DIFdel{. }\DIFdelend \DIFaddbegin \DIFadd{to obtain the device path.
}
\DIFaddend Specifying an empty device path will result in the first available audio controller being used\DIFaddbegin \DIFadd{, and can
be a convenient option to get UEFI audio working if only one audio controller is present}\DIFaddend .
\DIFaddend Specifying an empty device path \DIFdelbegin \DIFdel{will result }\DIFdelend \DIFaddbegin \DIFadd{results }\DIFaddend in the first available \DIFaddbegin \DIFadd{codec and }\DIFaddend audio controller being used. \DIFaddbegin \DIFadd{The value
of }\texttt{\DIFadd{AudioCodec}} \DIFadd{is ignored in this case. This can be a convenient initial option to try to get UEFI
audio working. Manual settings as above will be required when this default value does not work.
}\DIFaddend
\item
\texttt{\DIFdelbegin \DIFdel{AudioOut}\DIFdelend \DIFaddbegin \DIFadd{AudioOutMask}\DIFaddend }\\
......
......@@ -108,15 +108,20 @@
// Get/Set Pin Widget Control.
#define HDA_VERB_GET_PIN_WIDGET_CONTROL 0xF07
#define HDA_VERB_SET_PIN_WIDGET_CONTROL 0x707
#define HDA_PIN_WIDGET_CONTROL_VREF(a) ((UINT8)(a & 0x3))
#define HDA_PIN_WIDGET_CONTROL_VREF_EN BIT2
#define HDA_PIN_WIDGET_CONTROL_VREF(a) ((UINT8)(a & (0x7)))
#define HDA_PIN_WIDGET_CONTROL_VREF_HIZ 0 ///< Hi-Z
#define HDA_PIN_WIDGET_CONTROL_VREF_50 1 ///< 50%
#define HDA_PIN_WIDGET_CONTROL_VREF_GRD 2 ///< Ground
#define HDA_PIN_WIDGET_CONTROL_VREF_80 4 ///< 80%
#define HDA_PIN_WIDGET_CONTROL_VREF_100 5 ///< 100%
#define HDA_PIN_WIDGET_CONTROL_IN_EN BIT5
#define HDA_PIN_WIDGET_CONTROL_OUT_EN BIT6
#define HDA_PIN_WIDGET_CONTROL_HP_EN BIT7
#define HDA_VERB_SET_PIN_WIDGET_CONTROL_PAYLOAD(vref, vrefEn, in, out, hp) \
((UINT8)(HDA_PIN_WIDGET_CONTROL_VREF(vref) | (vrefEn ? HDA_PIN_WIDGET_CONTROL_VREF_EN : 0) \
| (in ? HDA_PIN_WIDGET_CONTROL_IN_EN : 0) | (out ? HDA_PIN_WIDGET_CONTROL_OUT_EN : 0) \
| (hp ? HDA_PIN_WIDGET_CONTROL_HP_EN : 0)))
#define HDA_VERB_SET_PIN_WIDGET_CONTROL_PAYLOAD(vref, in, out, hp) \
((UINT8)(HDA_PIN_WIDGET_CONTROL_VREF (vref) \
| (in ? HDA_PIN_WIDGET_CONTROL_IN_EN : 0) \
| (out ? HDA_PIN_WIDGET_CONTROL_OUT_EN : 0) \
| (hp ? HDA_PIN_WIDGET_CONTROL_HP_EN : 0)))
// Get/Set Unsolicited Response.
#define HDA_VERB_GET_UNSOL_RESPONSE 0xF08
......@@ -459,6 +464,11 @@
#define HDA_PARAMETER_PIN_CAPS_BALANCED BIT6
#define HDA_PARAMETER_PIN_CAPS_HDMI BIT7
#define HDA_PARAMETER_PIN_CAPS_VREF(a) ((UINT8)(a >> 8))
#define HDA_PARAMETER_PIN_CAPS_VREF_HIZ (1 << 0) ///< Hi-Z
#define HDA_PARAMETER_PIN_CAPS_VREF_50 (1 << 1) ///< 50%
#define HDA_PARAMETER_PIN_CAPS_VREF_GRD (1 << 2) ///< Ground
#define HDA_PARAMETER_PIN_CAPS_VREF_80 (1 << 4) ///< 80%
#define HDA_PARAMETER_PIN_CAPS_VREF_100 (1 << 5) ///< 100%
#define HDA_PARAMETER_PIN_CAPS_EAPD BIT16
#define HDA_PARAMETER_PIN_CAPS_DISPLAYPORT BIT24
#define HDA_PARAMETER_PIN_CAPS_HBR BIT27
......
......@@ -296,6 +296,8 @@ PrintWidget (
)
{
UINT32 Index;
UINT8 VrefCaps;
UINT8 VrefCtrl;
//
// Node header and widget capabilities.
......@@ -428,6 +430,22 @@ PrintWidget (
if (HdaWidget->PinCapabilities & HDA_PARAMETER_PIN_CAPS_DISPLAYPORT) {
OcAsciiPrintBuffer (AsciiBuffer, AsciiBufferSize, " DP");
}
VrefCaps = HDA_PARAMETER_PIN_CAPS_VREF (HdaWidget->PinCapabilities);
if (VrefCaps & HDA_PARAMETER_PIN_CAPS_VREF_HIZ) {
OcAsciiPrintBuffer (AsciiBuffer, AsciiBufferSize, " VREF_HIZ");
}
if (VrefCaps & HDA_PARAMETER_PIN_CAPS_VREF_GRD) {
OcAsciiPrintBuffer (AsciiBuffer, AsciiBufferSize, " VREF_GRD");
}
if (VrefCaps & HDA_PARAMETER_PIN_CAPS_VREF_50) {
OcAsciiPrintBuffer (AsciiBuffer, AsciiBufferSize, " VREF_50");
}
if (VrefCaps & HDA_PARAMETER_PIN_CAPS_VREF_80) {
OcAsciiPrintBuffer (AsciiBuffer, AsciiBufferSize, " VREF_80");
}
if (VrefCaps & HDA_PARAMETER_PIN_CAPS_VREF_100) {
OcAsciiPrintBuffer (AsciiBuffer, AsciiBufferSize, " VREF_100");
}
OcAsciiPrintBuffer (AsciiBuffer, AsciiBufferSize, "\n");
if (HdaWidget->PinCapabilities & HDA_PARAMETER_PIN_CAPS_EAPD) {
......@@ -472,9 +490,6 @@ PrintWidget (
);
OcAsciiPrintBuffer (AsciiBuffer, AsciiBufferSize, "Pin-ctls: 0x%2X:", HdaWidget->DefaultPinControl);
if (HdaWidget->DefaultPinControl & HDA_PIN_WIDGET_CONTROL_VREF_EN) {
OcAsciiPrintBuffer (AsciiBuffer, AsciiBufferSize, " VREF");
}
if (HdaWidget->DefaultPinControl & HDA_PIN_WIDGET_CONTROL_IN_EN) {
OcAsciiPrintBuffer (AsciiBuffer, AsciiBufferSize, " IN");
}
......@@ -484,6 +499,22 @@ PrintWidget (
if (HdaWidget->DefaultPinControl & HDA_PIN_WIDGET_CONTROL_HP_EN) {
OcAsciiPrintBuffer (AsciiBuffer, AsciiBufferSize, " HP");
}
VrefCtrl = HDA_PIN_WIDGET_CONTROL_VREF (HdaWidget->DefaultPinControl);
if (VrefCtrl == HDA_PIN_WIDGET_CONTROL_VREF_HIZ) {
OcAsciiPrintBuffer (AsciiBuffer, AsciiBufferSize, " VREF_HIZ");
}
if (VrefCtrl == HDA_PIN_WIDGET_CONTROL_VREF_GRD) {
OcAsciiPrintBuffer (AsciiBuffer, AsciiBufferSize, " VREF_GRD");
}
if (VrefCtrl == HDA_PIN_WIDGET_CONTROL_VREF_50) {
OcAsciiPrintBuffer (AsciiBuffer, AsciiBufferSize, " VREF_50");
}
if (VrefCtrl == HDA_PIN_WIDGET_CONTROL_VREF_80) {
OcAsciiPrintBuffer (AsciiBuffer, AsciiBufferSize, " VREF_80");
}
if (VrefCtrl == HDA_PIN_WIDGET_CONTROL_VREF_100) {
OcAsciiPrintBuffer (AsciiBuffer, AsciiBufferSize, " VREF_100");
}
OcAsciiPrintBuffer (AsciiBuffer, AsciiBufferSize, "\n");
}
......
......@@ -735,6 +735,12 @@
## @Prompt Try EFI_OPEN_PROTOCOL_GET_PROTOCOL if EFI_OPEN_PROTOCOL_BY_DRIVER fails.
gOpenCorePkgTokenSpaceGuid.PcdAudioControllerTryProtocolGetMode|FALSE|BOOLEAN|0x00000007
## Indicates if AudioDxe will use Pin Capabilities to identify outputs.<BR><BR>
## TRUE - Use Pin Capabilities to identify outputs.<BR>
## FALSE - Use default device type to identify outputs.<BR>
## @Prompt Use Pin Capabilities to identify audio outputs.
gOpenCorePkgTokenSpaceGuid.PcdAudioControllerUsePinCapsForOutputs|TRUE|BOOLEAN|0x00000008
[PcdsFixedAtBuild]
## Defines the Console Control initialization mode set on entry.<BR><BR>
## 0 - EfiConsoleControlScreenText<BR>
......
......@@ -56,7 +56,7 @@
#include <Protocol/HdaControllerInfo.h>
// Driver version
#define AUDIODXE_VERSION 0xB
#define AUDIODXE_VERSION 0xC
#define AUDIODXE_PKG_VERSION 1
// Driver Bindings.
......
......@@ -64,6 +64,7 @@
gOpenCorePkgTokenSpaceGuid.PcdAudioCodecErrorOnNoOutputs ## CONSUMES
gOpenCorePkgTokenSpaceGuid.PcdAudioControllerResetPciOnExitBootServices ## CONSUMES
gOpenCorePkgTokenSpaceGuid.PcdAudioControllerTryProtocolGetMode ## CONSUMES
gOpenCorePkgTokenSpaceGuid.PcdAudioControllerUsePinCapsForOutputs ## CONSUMES
[Sources]
HdaCodec/HdaCodecComponentName.h
......
......@@ -580,10 +580,6 @@ HdaCodecProbeCodec(
return Status;
DEBUG((DEBUG_INFO, "HDA: | Codec ID: 0x%X:0x%X\n", HDA_PARAMETER_VENDOR_ID_VEN(HdaCodecDev->VendorId), HDA_PARAMETER_VENDOR_ID_DEV(HdaCodecDev->VendorId)));
if (GET_CODEC_VENDOR_ID(HdaCodecDev->VendorId) == VEN_CIRRUSLOGIC_ID) {
HdaCodecDev->Quirks |= HDA_CODEC_QUIRK_CIRRUSLOGIC;
}
// Get revision ID.
Status = HdaIo->SendCommand(HdaIo, HDA_NID_ROOT,
HDA_CODEC_VERB(HDA_VERB_GET_PARAMETER, HDA_PARAMETER_REVISION_ID), &HdaCodecDev->RevisionId);
......@@ -673,16 +669,20 @@ HdaCodecFindUpstreamOutput(
EFI_STATUS
EFIAPI
HdaCodecParsePorts(
IN HDA_CODEC_DEV *HdaCodecDev) {
//DEBUG((DEBUG_INFO, "HdaCodecParsePorts(): start\n"));
// Create variables.
IN HDA_CODEC_DEV *HdaCodecDev
)
{
EFI_STATUS Status;
EFI_HDA_IO_PROTOCOL *HdaIo = HdaCodecDev->HdaIo;
EFI_HDA_IO_PROTOCOL *HdaIo;
HDA_FUNC_GROUP *HdaFuncGroup;
HDA_WIDGET_DEV *HdaWidget;
UINT8 DefaultDeviceType;
UINT32 Response;
BOOLEAN IsOutput;
//DEBUG((DEBUG_INFO, "HdaCodecParsePorts(): start\n"));
HdaIo = HdaCodecDev->HdaIo;
// Loop through each function group.
for (UINT8 f = 0; f < HdaCodecDev->FuncGroupsCount; f++) {
......@@ -699,16 +699,33 @@ HdaCodecParsePorts(
// 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))
(HDA_VERB_GET_CONFIGURATION_DEFAULT_ASSOCIATION(HdaWidget->DefaultConfiguration) == 0)) {
DEBUG((
DEBUG_VERBOSE,
"HDA: | Ignoring widget @ 0x%X\n",
HdaWidget->NodeId
));
continue;
}
// Determine if port is an output based on the device type.
// The types reported here do not correspond particularly well to the real hardware.
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)) {
if (PcdGetBool (PcdAudioControllerUsePinCapsForOutputs)) {
// Use PinCaps to identify all pin complexes which can be configured as outputs.
// On certain systes, e.g. MacPro5,1, ports which are inputs according to default
// type are the correct output channels to use on the system.
IsOutput = (HdaWidget->PinCapabilities & HDA_PARAMETER_PIN_CAPS_OUTPUT) != 0;
} else {
// Determine if port is an output based on the default device type.
// The types reported here do not correspond particularly well to the real hardware.
DefaultDeviceType = HDA_VERB_GET_CONFIGURATION_DEFAULT_DEVICE(HdaWidget->DefaultConfiguration);
IsOutput = (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);
}
if (IsOutput) {
// Try to get upstream output.
Status = HdaCodecFindUpstreamOutput(HdaWidget, 0);
if (EFI_ERROR(Status)) {
......@@ -721,25 +738,13 @@ HdaCodecParsePorts(
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)) {
DEBUG((
DEBUG_WARN,
"HDA: Widget @ 0x%X enable output amp - %r\n",
HdaWidget->NodeId,
Status
));
continue;
}
// Report output.
DEBUG((
DEBUG_INFO,
"HDA: | Port widget @ 0x%X is an output (pin defaults 0x%X) (bitmask %u)\n",
HdaWidget->NodeId,
HdaWidget->DefaultConfiguration, 1 << HdaCodecDev->OutputPortsCount
HdaWidget->DefaultConfiguration,
1 << HdaCodecDev->OutputPortsCount
));
// If EAPD is present, enable.
......@@ -945,7 +950,7 @@ HdaCodecDisableWidgetPath(
// 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);
HDA_VERB_SET_PIN_WIDGET_CONTROL_PAYLOAD(0, FALSE, FALSE, FALSE)), &Response);
if (EFI_ERROR(Status))
return Status;
}
......@@ -964,17 +969,22 @@ HdaCodecEnableWidgetPath(
IN HDA_WIDGET_DEV *HdaWidget,
IN UINT8 Volume,
IN UINT8 StreamId,
IN UINT16 StreamFormat) {
IN UINT16 StreamFormat
)
{
EFI_STATUS Status;
EFI_HDA_IO_PROTOCOL *HdaIo;
UINT32 Response;
UINT8 VrefCaps;
UINT8 VrefCtrl;
//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;
// Create variables.
EFI_STATUS Status;
EFI_HDA_IO_PROTOCOL *HdaIo = HdaWidget->FuncGroup->HdaCodecDev->HdaIo;
UINT32 Response;
HdaIo = HdaWidget->FuncGroup->HdaCodecDev->HdaIo;
// Crawl through widget path.
while (HdaWidget != NULL) {
......@@ -982,8 +992,37 @@ HdaCodecEnableWidgetPath(
// 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);
VrefCaps = HDA_PARAMETER_PIN_CAPS_VREF (HdaWidget->PinCapabilities);
// If voltage reference control is available, choose the lowest supported voltage.
// This is similar to how Linux drivers enable audio e.g. on Realtek devices on Mac,
// but this is an attempt to make a more general purpose system.
// REF: https://github.com/torvalds/linux/blob/6f513529296fd4f696afb4354c46508abe646541/sound/pci/hda/patch_realtek.c#L1999-L2012
VrefCtrl = HDA_PIN_WIDGET_CONTROL_VREF_HIZ;
if (VrefCaps & HDA_PARAMETER_PIN_CAPS_VREF_50) {
VrefCtrl = HDA_PIN_WIDGET_CONTROL_VREF_50;
} else if (VrefCaps & HDA_PARAMETER_PIN_CAPS_VREF_80) {
VrefCtrl = HDA_PIN_WIDGET_CONTROL_VREF_80;
} else if (VrefCaps & HDA_PARAMETER_PIN_CAPS_VREF_100) {
VrefCtrl = HDA_PIN_WIDGET_CONTROL_VREF_100;
}
Status = HdaIo->SendCommand (
HdaIo,
HdaWidget->NodeId,
HDA_CODEC_VERB (
HDA_VERB_SET_PIN_WIDGET_CONTROL,
HDA_VERB_SET_PIN_WIDGET_CONTROL_PAYLOAD (VrefCtrl, FALSE, TRUE, FALSE)
),
&Response
);
DEBUG ((
EFI_ERROR (Status) ? DEBUG_WARN : DEBUG_INFO,
"HDA: Widget @ 0x%X enable output amp vref %u - %r\n",
HdaWidget->NodeId,
VrefCtrl,
Status
));
if (EFI_ERROR(Status))
return Status;
......
......@@ -101,28 +101,6 @@ struct _HDA_FUNC_GROUP {
UINT8 WidgetsCount;
};
//
// Quirks.
//
//
// Apple hardware Cirrus logic codecs require speakers and headphones bits set
// in GPIO to enable sound. Speakers are always BIT3, headphones are BIT1 or BIT2
// depending on the model; we just enable all.
// REF:
// - https://github.com/torvalds/linux/blob/6f513529296fd4f696afb4354c46508abe646541/sound/pci/hda/patch_cirrus.c#L43-L57
// - https://github.com/torvalds/linux/blob/6f513529296fd4f696afb4354c46508abe646541/sound/pci/hda/patch_cirrus.c#L493-L517
//
#define HDA_CODEC_QUIRK_CIRRUSLOGIC BIT0
#define HDA_CIRRUSLOGIC_GPIO_HEADPHONES1 BIT1
#define HDA_CIRRUSLOGIC_GPIO_HEADPHONES2 BIT2
#define HDA_CIRRUSLOGIC_GPIO_SPEAKERS BIT3
#define HDA_CIRRUSLOGIC_GPIO_ALL (\
HDA_CIRRUSLOGIC_GPIO_HEADPHONES1 |\
HDA_CIRRUSLOGIC_GPIO_HEADPHONES2 |\
HDA_CIRRUSLOGIC_GPIO_SPEAKERS )
struct _HDA_CODEC_DEV {
// Signature.
UINTN Signature;
......@@ -150,9 +128,6 @@ struct _HDA_CODEC_DEV {
HDA_WIDGET_DEV **InputPorts;
UINTN OutputPortsCount;
UINTN InputPortsCount;
// Required quirks.
UINTN Quirks;
};
// HDA Codec Info private data.
......
......@@ -243,7 +243,9 @@ HdaCodecAudioIoSetupPlayback(
EFI_HDA_IO_PROTOCOL *HdaIo;
UINTN Index;
UINT64 IndexMask;
UINT32 GpioData;
UINT32 Response;
UINT8 NumGpios;
UINT8 Payload;
// Widgets.
HDA_WIDGET_DEV *PinWidget;
......@@ -503,13 +505,50 @@ HdaCodecAudioIoSetupPlayback(
goto CLOSE_STREAM;
}
// Apply any codec-specific quirks.
if (HdaCodecDev->Quirks & HDA_CODEC_QUIRK_CIRRUSLOGIC) {
Status = HdaIo->SendCommand (HdaIo, 0x01,
HDA_CODEC_VERB (HDA_VERB_SET_GPIO_DATA, HDA_CIRRUSLOGIC_GPIO_ALL), &GpioData);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "HDA: Failed to apply Cirrus Logic output enable, continuing anyway\n"));
//
// Enable any GPIO pins. This is similar to how Linux drivers enable audio on e.g. Cirrus Logic and
// Realtek devices on Mac, by enabling specific GPIO pins as required, but this is an attempt to
// make a more general purpose system.
// REF:
// - https://github.com/torvalds/linux/blob/6f513529296fd4f696afb4354c46508abe646541/sound/pci/hda/patch_cirrus.c#L43-L57
// - https://github.com/torvalds/linux/blob/6f513529296fd4f696afb4354c46508abe646541/sound/pci/hda/patch_cirrus.c#L493-L517
// - https://github.com/torvalds/linux/blob/6f513529296fd4f696afb4354c46508abe646541/sound/pci/hda/patch_realtek.c#L244-L258
//
NumGpios = HDA_PARAMETER_GPIO_COUNT_NUM_GPIOS (HdaCodecDev->AudioFuncGroup->GpioCapabilities);
if (NumGpios) {
ASSERT (NumGpios <= 7); ///< According to Intel HDA spec this can be from 0 to 7
Payload = (1 << NumGpios) - 1; ///< Enable all available pins
Status = HdaIo->SendCommand (
HdaIo,
HdaCodecDev->AudioFuncGroup->NodeId,
HDA_CODEC_VERB (HDA_VERB_SET_GPIO_ENABLE_MASK, Payload),
&Response
);
if (!EFI_ERROR (Status)) {
Status = HdaIo->SendCommand (
HdaIo,
HdaCodecDev->AudioFuncGroup->NodeId,
HDA_CODEC_VERB (HDA_VERB_SET_GPIO_DIRECTION, Payload),
&Response
);
}
if (!EFI_ERROR (Status)) {
gBS->Stall (MS_TO_MICROSECONDS (1));
Status = HdaIo->SendCommand (
HdaIo,
HdaCodecDev->AudioFuncGroup->NodeId,
HDA_CODEC_VERB (HDA_VERB_SET_GPIO_DATA, Payload),
&Response
);
}
DEBUG ((
EFI_ERROR (Status) ? DEBUG_WARN : DEBUG_INFO,
"HDA: GPIO output enable 0x%02X - %r\n",
Payload,
Status
));
if (EFI_ERROR(Status))
goto CLOSE_STREAM;
}
//
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册