/* * HDA injector, part of the Chameleon Boot Loader Project * * Ported and adapted by Fabio (ErmaC), October 2016. * * HDA injector is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HDA injector is distributed in the hope that it will be useful, but * 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 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 // Only use angled for Platform, else, xcode project won't compile #include "FixBiosDsdt.h" #include "../include/Devices.h" #include "../include/Pci.h" #include "device_inject.h" #include "../Platform/Settings.h" #ifndef DEBUG_HDA #ifndef DEBUG_ALL #define DEBUG_HDA 1 #else #define DEBUG_HDA DEBUG_ALL #endif #endif #if DEBUG_HDA == 0 #define DBG(...) #else #define DBG(...) DebugLog(DEBUG_HDA, __VA_ARGS__) #endif // HDA layout-id device injection by dmazar /* CODECs */ /* * ErmaC: There's definitely a lot of different versions of the same audio codec variant out there... * in the next struct you will find a "generic" but IMHO detailed list of * possible codec... anyway to specific a new one or find difference beetween revision * check it under linux enviroment with: * $cat /proc/asound/Intel/codec#0 * -------------------------------- * Codec: Analog Devices AD1989B * Address: 0 * AFG Function Id: 0x1 (unsol 0) * Vendor Id: 0x11d4989b * Subsystem Id: 0x10438372 * Revision Id: 0x100300 * -------------------------------- * or * $cat /proc/asound/NVidia/codec#0 * -------------------------------- * Codec: Nvidia GPU 14 HDMI/DP * Address: 0 * AFG Function Id: 0x1 (unsol 0) * Vendor Id: 0x10de0014 * Subsystem Id: 0x10de0101 * Revision Id: 0x100100 * -------------------------------- */ /***************** * Device Methods *****************/ #if 0 // executing HDA verb command using Immediate Command Input and Output Registers UINT32 HDA_IC_sendVerb(EFI_PCI_IO_PROTOCOL *PciIo, UINT32 codecAdr, UINT32 nodeId, UINT32 verb) { EFI_STATUS Status; UINT16 ics = 0; UINT32 data32 = 0; UINT64 data64 = 0; // about that polling below ... // spec says that delay is in 100ns units. value 1.000.000.0 // should then be 1 second, but users of newer Aptio boards were reporting // delays of 10-20 secs when this value was used. maybe this polling timeout // value does not mean the same on all implementations? // anyway, delay is lowered now to 10.000.0 (10 millis). // poll ICS[0] to become 0 Status = PciIo->PollMem(PciIo, EfiPciIoWidthUint16, 0/*bar*/, HDA_ICS/*offset*/, 0x1/*mask*/, 0/*value*/, 100000/*delay in 100ns*/, &data64); ics = (UINT16)(data64 & 0xFFFF); //DBG("poll ICS[0] == 0: Status=%s, ICS=%X, ICS[0]=%d\n", efiStrError(Status), ics, (ics & 0x0001)); if (EFI_ERROR(Status)) return 0; // prepare and write verb to ICO data32 = codecAdr << 28 | ((nodeId & 0xFF)<<20) | (verb & 0xFFFFF); Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint32, 0, HDA_ICO, 1, &data32); //DBG("ICO write verb Codec=%X, Node=%X, verb=%X, command verb=%X: Status=%s\n", codecAdr, nodeId, verb, data32, efiStrError(Status)); if (EFI_ERROR(Status)) return 0; // write 11b to ICS[1:0] to send command ics |= 0x3; Status = PciIo->Mem.Write(PciIo, EfiPciIoWidthUint16, 0, HDA_ICS, 1, &ics); //DBG("ICS[1:0] = 11b: Status=%s\n", efiStrError(Status)); if (EFI_ERROR(Status)) return 0; // poll ICS[1:0] to become 10b Status = PciIo->PollMem(PciIo, EfiPciIoWidthUint16, 0/*bar*/, HDA_ICS/*offset*/, 0x3/*mask*/, 0x2/*value*/, 100000/*delay in 100ns*/, &data64); //DBG("poll ICS[0] == 0: Status=%s\n", efiStrError(Status)); if (EFI_ERROR(Status)) return 0; // read IRI for VendorId/DeviceId Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint32, 0, HDA_IRI, 1, &data32); if (EFI_ERROR(Status)) return 0; return data32; } #endif XBool EFIAPI IsHDMIAudio(EFI_HANDLE PciDevHandle) { EFI_STATUS Status; EFI_PCI_IO_PROTOCOL *PciIo; UINTN Segment; UINTN Bus; UINTN Device; UINTN Function; UINTN Index; // get device PciIo protocol Status = gBS->OpenProtocol(PciDevHandle, gEfiPciIoProtocolGuid, (void **)&PciIo, gImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(Status)) { return false; } // get device location Status = PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Function); if (EFI_ERROR(Status)) { return false; } // iterate over all GFX devices and check for sibling for (Index = 0; Index < gConf.GfxPropertiesArray.size(); Index++) { if (gConf.GfxPropertiesArray[Index].Segment == Segment && gConf.GfxPropertiesArray[Index].Bus == Bus && gConf.GfxPropertiesArray[Index].Device == Device) { return true; } } return false; } XBool setup_hda_devprop(EFI_PCI_IO_PROTOCOL *PciIo, pci_dt_t *hda_dev, const MacOsVersion& OSVersion) { DevPropDevice *device = NULL; UINT32 layoutId = 0; UINT32 codecId = 0; XBool Injected = false; UINTN i; if (!device_inject_string) { device_inject_string = devprop_create_string(); } if (IsHDMIAudio(hda_dev->DeviceHandle)) { if (!gSettings.Devices.HDMIInjection) { return false; } if (hda_dev && !hda_dev->used) { device = devprop_add_device_pci(device_inject_string, hda_dev, NULL); hda_dev->used = true; } if (!device) { return false; } if (gSettings.Devices.AddPropertyArray.size() != 0xFFFE) { // Looks like NrAddProperties == 0xFFFE is not used anymore for (i = 0; i < gSettings.Devices.AddPropertyArray.size(); i++) { if (gSettings.Devices.AddPropertyArray[i].Device != DEV_HDMI) { continue; } Injected = true; if (!gSettings.Devices.AddPropertyArray[i].MenuItem.BValue) { //DBG(" disabled property Key: %s, len: %d\n", gSettings.Devices.AddPropertyArray[i].Key, gSettings.Devices.AddPropertyArray[i].ValueLen); } else { devprop_add_value(device, gSettings.Devices.AddPropertyArray[i].Key, gSettings.Devices.AddPropertyArray[i].Value); //DBG(" added property Key: %s, len: %d\n", gSettings.Devices.AddPropertyArray[i].Key, gSettings.Devices.AddPropertyArray[i].ValueLen); } } } if (Injected) { DBG("Additional HDMI properties injected, continue\n"); //return true; } else { if (gSettings.Devices.UseIntelHDMI) { DBG(" HDMI Audio, used with HDA setting hda-gfx=onboard-2\n"); devprop_add_value(device, "hda-gfx", (UINT8*)"onboard-2", 10); } else { DBG(" HDMI Audio, used without HDA setting hda-gfx=onboard-1\n"); devprop_add_value(device, "hda-gfx", (UINT8*)"onboard-1", 10); } } } else { if (!gSettings.Devices.Audio.HDAInjection) { return false; } if (hda_dev && !hda_dev->used) { device = devprop_add_device_pci(device_inject_string, hda_dev, NULL); hda_dev->used = true; } if (!device) { return false; } // HDA - determine layout-id if (gSettings.Devices.Audio.HDALayoutId > 0) { // layoutId is specified - use it layoutId = (UINT32)gSettings.Devices.Audio.HDALayoutId; DBG(" setting specified layout-id=%d (0x%X)\n", layoutId, layoutId); } else { layoutId = 12; } if (gSettings.Devices.AddPropertyArray.size() != 0xFFFE) { // Looks like NrAddProperties == 0xFFFE is not used anymore for (i = 0; i < gSettings.Devices.AddPropertyArray.size(); i++) { if (gSettings.Devices.AddPropertyArray[i].Device != DEV_HDA) { continue; } Injected = true; if (!gSettings.Devices.AddPropertyArray[i].MenuItem.BValue) { //DBG(" disabled property Key: %s, len: %d\n", gSettings.Devices.AddPropertyArray[i].Key, gSettings.Devices.AddPropertyArray[i].ValueLen); } else { devprop_add_value(device, gSettings.Devices.AddPropertyArray[i].Key, gSettings.Devices.AddPropertyArray[i].Value); //DBG(" added property Key: %s, len: %d\n", gSettings.Devices.AddPropertyArray[i].Key, gSettings.Devices.AddPropertyArray[i].ValueLen); } } } if (!Injected) { if ( (OSVersion.notEmpty() && OSVersion < MacOsVersion("10.8"_XS8)) || gSettings.Devices.Audio.HDALayoutId > 0 ) { devprop_add_value(device, "layout-id", (UINT8 *)&layoutId, 4); } layoutId = 0; // reuse variable if (gSettings.Devices.UseIntelHDMI) { devprop_add_value(device, "hda-gfx", (UINT8 *)"onboard-1", 10); } codecId = 1; // reuse variable again if (gSettings.Devices.Audio.AFGLowPowerState) { devprop_add_value(device, "AFGLowPowerState", (UINT8 *)&codecId, 4); } devprop_add_value(device, "MaximumBootBeepVolume", (UINT8 *)&layoutId, 1); devprop_add_value(device, "PinConfigurations", (UINT8 *)&layoutId, 1); } } return true; } void ResetHDA() { EFI_STATUS Status; UINTN HandleCount = 0; EFI_HANDLE *HandleArray = NULL; EFI_PCI_IO_PROTOCOL *PciIo; PCI_TYPE00 Pci; UINTN Index; UINTN Segment = 0; UINTN Bus = 0; UINTN Device = 0; UINTN Function = 0; XStringW GopDevicePathStr; // Get GOP handle, in order to check to which GPU the monitor is currently connected Status = gBS->LocateHandleBuffer(ByProtocol, &gEfiGraphicsOutputProtocolGuid, NULL, &HandleCount, &HandleArray); if (!EFI_ERROR(Status)) { GopDevicePathStr = DevicePathToXStringW(DevicePathFromHandle(HandleArray[0])); DBG("GOP found at: %ls\n", GopDevicePathStr.wc_str()); } // Scan PCI handles Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiPciIoProtocolGuid, NULL, &HandleCount, &HandleArray ); if (!EFI_ERROR(Status)) { for (Index = 0; Index < HandleCount; ++Index) { Status = gBS->HandleProtocol(HandleArray[Index], &gEfiPciIoProtocolGuid, (void **)&PciIo); if (!EFI_ERROR(Status)) { // Read PCI BUS PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Function); Status = PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint32, 0, sizeof (Pci) / sizeof (UINT32), &Pci ); // DBG("PCI (%02llX|%02llX:%02llX.%02llX) : %04hX %04hX class=%02hhX%02hhX%02hhX\n", // Segment, // Bus, // Device, // Function, // Pci.Hdr.VendorId, // Pci.Hdr.DeviceId, // Pci.Hdr.ClassCode[2], // Pci.Hdr.ClassCode[1], // Pci.Hdr.ClassCode[0] // ); if ( Pci.Hdr.ClassCode[2] == PCI_CLASS_MEDIA && ( Pci.Hdr.ClassCode[1] == PCI_CLASS_MEDIA_HDA || Pci.Hdr.ClassCode[1] == PCI_CLASS_MEDIA_AUDIO ) ) { //Slice method from VoodooHDA //PCI_HDA_TCSEL_OFFSET = 0x44 UINT8 Value = 0; Status = PciIo->Pci.Read(PciIo, EfiPciIoWidthUint8, 0x44, 1, &Value); if ( !EFI_ERROR(Status) ) { Value &= 0xf8; PciIo->Pci.Write(PciIo, EfiPciIoWidthUint8, 0x44, 1, &Value); } //ResetControllerHDA(); } } } } }