diff --git a/payloads/Kexts/Acidanthera/WhateverGreen-Navi-Backlight.md b/payloads/Kexts/Acidanthera/WhateverGreen-Navi-Backlight.md new file mode 100644 index 000000000..556f381f1 --- /dev/null +++ b/payloads/Kexts/Acidanthera/WhateverGreen-Navi-Backlight.md @@ -0,0 +1,2 @@ +Following patch required for iMacs with Navi MXM GPUs +Source: https://github.com/kingo132/a51m-r2-5700m-hackintosh/issues/13 diff --git a/payloads/Kexts/Acidanthera/WhateverGreen-Navi-Backlight.patch b/payloads/Kexts/Acidanthera/WhateverGreen-Navi-Backlight.patch new file mode 100644 index 000000000..49c205785 --- /dev/null +++ b/payloads/Kexts/Acidanthera/WhateverGreen-Navi-Backlight.patch @@ -0,0 +1,3319 @@ +diff --git a/WhateverGreen/kern_rad.cpp b/WhateverGreen/kern_rad.cpp +index b4f271f..162dc82 100644 +--- a/WhateverGreen/kern_rad.cpp ++++ b/WhateverGreen/kern_rad.cpp +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -190,1015 +191,2399 @@ void RAD::processKernel(KernelPatcher &patcher, DeviceInfo *info) { + } + + void RAD::updatePwmMaxBrightnessFromInternalDisplay() { +- OSDictionary * matching = IOService::serviceMatching("AppleBacklightDisplay"); +- if (matching == nullptr) { +- DBGLOG("igfx", "isRadeonX6000WiredToInternalDisplay null AppleBacklightDisplay"); +- return; +- } +- +- OSIterator *iter = IOService::getMatchingServices(matching); +- if (iter == nullptr) { +- DBGLOG("igfx", "isRadeonX6000WiredToInternalDisplay null matching"); +- matching->release(); +- return; +- } +- +- IORegistryEntry* display = OSDynamicCast(IORegistryEntry, iter->getNextObject()); +- if (display == nullptr) { +- DBGLOG("igfx", "isRadeonX6000WiredToInternalDisplay null display"); +- iter->release(); +- matching->release(); +- return; +- } +- +- OSDictionary* iodispparm = OSDynamicCast(OSDictionary, display->getProperty("IODisplayParameters")); +- if (iodispparm == nullptr) { +- DBGLOG("igfx", "isRadeonX6000WiredToInternalDisplay null IODisplayParameters"); +- iter->release(); +- matching->release(); +- return; +- } +- +- OSDictionary* linearbri = OSDynamicCast(OSDictionary, iodispparm->getObject("linear-brightness")); +- if (linearbri == nullptr) { +- DBGLOG("igfx", "isRadeonX6000WiredToInternalDisplay null linear-brightness"); +- iter->release(); +- matching->release(); +- return; +- } +- +- OSNumber* maxbri = OSDynamicCast(OSNumber, linearbri->getObject("max")); +- if (maxbri == nullptr) { +- DBGLOG("igfx", "isRadeonX6000WiredToInternalDisplay null max"); +- iter->release(); +- matching->release(); +- return; +- } ++ OSDictionary * matching = IOService::serviceMatching("AppleBacklightDisplay"); ++ if (matching == nullptr) { ++ DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay null AppleBacklightDisplay"); ++ return; ++ } ++ ++ OSIterator *iter = IOService::getMatchingServices(matching); ++ if (iter == nullptr) { ++ DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay null matching"); ++ matching->release(); ++ return; ++ } ++ ++ IORegistryEntry* display = OSDynamicCast(IORegistryEntry, iter->getNextObject()); ++ if (display == nullptr) { ++ DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay null display"); ++ iter->release(); ++ matching->release(); ++ return; ++ } ++ ++ OSDictionary* iodispparm = OSDynamicCast(OSDictionary, display->getProperty("IODisplayParameters")); ++ if (iodispparm == nullptr) { ++ DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay null IODisplayParameters"); ++ iter->release(); ++ matching->release(); ++ return; ++ } ++ ++ OSDictionary* linearbri = OSDynamicCast(OSDictionary, iodispparm->getObject("linear-brightness")); ++ if (linearbri == nullptr) { ++ DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay null linear-brightness"); ++ iter->release(); ++ matching->release(); ++ return; ++ } ++ ++ OSNumber* maxbri = OSDynamicCast(OSNumber, linearbri->getObject("max")); ++ if (maxbri == nullptr) { ++ DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay null max"); ++ iter->release(); ++ matching->release(); ++ return; ++ } ++ ++ callbackRAD->maxPwmBacklightLvl = maxbri->unsigned32BitValue(); ++ DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay get max brightness: 0x%x", callbackRAD->maxPwmBacklightLvl); ++ ++ iter->release(); ++ matching->release(); ++} + +- callbackRAD->maxPwmBacklightLvl = maxbri->unsigned32BitValue(); +- DBGLOG("igfx", "updatePwmMaxBrightnessFromInternalDisplay get max brightness: 0x%x", callbackRAD->maxPwmBacklightLvl); ++typedef int64_t __int64; ++typedef int64_t _QWORD; ++typedef int32_t _DWORD; ++typedef int8_t __int8; + +- iter->release(); +- matching->release(); +-} ++struct dc_stream_state { ++ void *sink; ++ void *link; ++}; + +-uint32_t RAD::wrapDcePanelCntlHwInit(void *panel_cntl) { +- callbackRAD->panelCntlPtr = panel_cntl; +- callbackRAD->updatePwmMaxBrightnessFromInternalDisplay(); // read max brightness value from IOReg +- uint32_t ret = FunctionCast(wrapDcePanelCntlHwInit, callbackRAD->orgDcePanelCntlHwInit)(panel_cntl); +- return ret; +-} ++#define MAX_SINKS_PER_LINK 4 + +-IOReturn RAD::wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute(IOService *framebuffer, IOIndex connectIndex, IOSelect attribute, uintptr_t value) { +- IOReturn ret = FunctionCast(wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute, callbackRAD->orgAMDRadeonX6000AmdRadeonFramebufferSetAttribute)(framebuffer, connectIndex, attribute, value); +- if (attribute != (UInt32)'bklt') { +- return ret; +- } +- +- if (callbackRAD->maxPwmBacklightLvl == 0) { +- DBGLOG("igfx", "wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute zero maxPwmBacklightLvl"); +- return 0; +- } +- +- if (callbackRAD->panelCntlPtr == nullptr) { +- DBGLOG("igfx", "wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute null panel cntl"); +- return 0; +- } +- +- if (callbackRAD->orgDceDriverSetBacklight == nullptr) { +- DBGLOG("igfx", "wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute null orgDcLinkSetBacklightLevel"); +- return 0; +- } +- +- // set the backlight of AMD navi10 driver +- callbackRAD->curPwmBacklightLvl = (uint32_t)value; +- uint32_t btlper = callbackRAD->curPwmBacklightLvl * 100 / callbackRAD->maxPwmBacklightLvl; +- uint32_t pwmval = 0; +- if (btlper >= 100) { +- // This is from the dmcu_set_backlight_level function of Linux source +- // ... +- // if (backlight_pwm_u16_16 & 0x10000) +- // backlight_8_bit = 0xFF; +- // else +- // backlight_8_bit = (backlight_pwm_u16_16 >> 8) & 0xFF; +- // ... +- // The max brightness should have 0x10000 bit set +- pwmval = 0x1FF00; +- } else { +- pwmval = ((btlper * 0xFF) / 100) << 8U; +- } ++struct dc_link_settings { ++ uint32_t lane_count; ++ uint32_t link_rate; ++ uint32_t link_spread; ++ bool use_link_rate_set; ++ uint8_t link_rate_set; ++}; + +- callbackRAD->orgDceDriverSetBacklight(callbackRAD->panelCntlPtr, pwmval); +- return 0; +-} ++struct dc_lane_settings { ++ uint32_t VOLTAGE_SWING; ++ uint32_t PRE_EMPHASIS; ++ uint32_t POST_CURSOR2; ++}; + +-IOReturn RAD::wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute(IOService *framebuffer, IOIndex connectIndex, IOSelect attribute, uintptr_t * value) { +- IOReturn ret = FunctionCast(wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute, callbackRAD->orgAMDRadeonX6000AmdRadeonFramebufferGetAttribute)(framebuffer, connectIndex, attribute, value); +- if (attribute == (UInt32)'bklt') { +- // enable the backlight feature of AMD navi10 driver +- *value = callbackRAD->curPwmBacklightLvl; +- ret = 0; +- } +- return ret; +-} ++struct dc_link_training_overrides { ++ uint32_t *voltage_swing; ++ uint32_t *pre_emphasis; ++ uint32_t *post_cursor2; ++ ++ uint16_t *cr_pattern_time; ++ uint16_t *eq_pattern_time; ++ uint32_t *pattern_for_cr; ++ uint32_t *pattern_for_eq; ++ ++ uint32_t *downspread; ++ bool *alternate_scrambler_reset; ++ bool *enhanced_framing; ++ bool *mst_enable; ++ bool *fec_enable; ++}; + +-bool RAD::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { +- if (kextRadeonX6000Framebuffer.loadIndex == index) { +- KernelPatcher::RouteRequest requests[] = { +- {"_dce_panel_cntl_hw_init", wrapDcePanelCntlHwInit, orgDcePanelCntlHwInit}, +- {"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25setAttributeForConnectionEijm", wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute, orgAMDRadeonX6000AmdRadeonFramebufferSetAttribute}, +- {"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25getAttributeForConnectionEijPm", wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute, orgAMDRadeonX6000AmdRadeonFramebufferGetAttribute}, +- }; ++struct dp_audio_test_data_flags { ++ uint8_t test_requested :1; ++ uint8_t disable_video :1; ++}; + +- if (!patcher.routeMultiple(index, requests, address, size, true, true)) +- SYSLOG("igfx", "Failed to route redeon x6000 gpu tracing."); +- +- orgDceDriverSetBacklight = reinterpret_cast(patcher.solveSymbol(index, "_dce_driver_set_backlight")); +- if (patcher.getError() != KernelPatcher::Error::NoError) { +- SYSLOG("igfx", "failed to resolve _dce_driver_set_backlight"); +- patcher.clearError(); +- } +- } +- +- if (kextRadeonFramebuffer.loadIndex == index) { +- if (force24BppMode) +- process24BitOutput(patcher, kextRadeonFramebuffer, address, size); +- return true; +- } ++struct dp_audio_test_data { + +- if (kextRadeonLegacyFramebuffer.loadIndex == index) { +- if (force24BppMode) +- process24BitOutput(patcher, kextRadeonLegacyFramebuffer, address, size); +- return true; +- } ++ struct dp_audio_test_data_flags flags; ++ uint8_t sampling_rate; ++ uint8_t channel_count; ++ uint8_t pattern_type; ++ uint8_t pattern_period[8]; ++}; + +- if (kextRadeonSupport.loadIndex == index) { +- processConnectorOverrides(patcher, address, size, true); ++union compliance_test_state { ++ struct { ++ unsigned char STEREO_3D_RUNNING : 1; ++ unsigned char RESERVED : 7; ++ } bits; ++ unsigned char raw; ++}; + +- if (getKernelVersion() > KernelVersion::Mojave || +- (getKernelVersion() == KernelVersion::Mojave && getKernelMinorVersion() >= 5)) { +- KernelPatcher::RouteRequest request("__ZN13ATIController8TestVRAME13PCI_REG_INDEXb", doNotTestVram); +- patcher.routeMultiple(index, &request, 1, address, size); +- } ++struct graphics_object_id { ++ uint32_t id:8; ++ uint32_t enum_id:4; ++ uint32_t type:4; ++ uint32_t reserved:16; /* for padding. total size should be u32 */ ++}; + +- if (useCustomAgdpDecision) { +- KernelPatcher::RouteRequest request("__ZN16AtiDeviceControl16notifyLinkChangeE31kAGDCRegisterLinkControlEvent_tmj", wrapNotifyLinkChange, orgNotifyLinkChange); +- patcher.routeMultiple(index, &request, 1, address, size); +- } ++union ddi_channel_mapping { ++ struct mapping { ++ uint8_t lane0:2; /* Mapping for lane 0 */ ++ uint8_t lane1:2; /* Mapping for lane 1 */ ++ uint8_t lane2:2; /* Mapping for lane 2 */ ++ uint8_t lane3:2; /* Mapping for lane 3 */ ++ } mapping; ++ uint8_t raw; ++}; + +- return true; +- } ++struct device_id { ++ uint32_t device_type:16; ++ uint32_t enum_id:16; /* 1 based enum */ ++ uint16_t raw_device_tag; ++}; + +- if (kextRadeonLegacySupport.loadIndex == index) { +- processConnectorOverrides(patcher, address, size, false); +- return true; +- } ++struct connector_device_tag_info { ++ uint32_t acpi_device; ++ struct device_id dev_id; ++}; + +- if (kextPolarisController.loadIndex == index) { +- KernelPatcher::RouteRequest request("__ZN17AMD9500Controller23findProjectByPartNumberEP20ControllerProperties", findProjectByPartNumber); +- patcher.routeMultiple(index, &request, 1, address, size); +- } ++union dpcd_rev { ++ struct { ++ uint8_t MINOR:4; ++ uint8_t MAJOR:4; ++ } bits; ++ uint8_t raw; ++}; + +- for (size_t i = 0; i < maxHardwareKexts; i++) { +- if (kextRadeonHardware[i].loadIndex == index) { +- processHardwareKext(patcher, i, address, size); +- return true; +- } +- } ++union max_lane_count { ++ struct { ++ uint8_t MAX_LANE_COUNT:5; ++ uint8_t POST_LT_ADJ_REQ_SUPPORTED:1; ++ uint8_t TPS3_SUPPORTED:1; ++ uint8_t ENHANCED_FRAME_CAP:1; ++ } bits; ++ uint8_t raw; ++}; + +- return false; +-} ++union max_down_spread { ++ struct { ++ uint8_t MAX_DOWN_SPREAD:1; ++ uint8_t RESERVED:5; ++ uint8_t NO_AUX_HANDSHAKE_LINK_TRAINING:1; ++ uint8_t TPS4_SUPPORTED:1; ++ } bits; ++ uint8_t raw; ++}; + +-void RAD::initHardwareKextMods() { +- // Decide on kext amount present for optimal performance. +- // 10.15+ has X4000, X5000, and X6000 +- // 10.14+ has X4000 and X5000 +- // 10.13.4+ has X3000, X4000, and X5000 +- if (getKernelVersion() >= KernelVersion::Catalina) +- maxHardwareKexts = MaxRadeonHardwareCatalina; +- else if (getKernelVersion() >= KernelVersion::Mojave) +- maxHardwareKexts = MaxRadeonHardwareMojave; +- else if (getKernelVersion() == KernelVersion::HighSierra && getKernelMinorVersion() >= 5) +- maxHardwareKexts = MaxRadeonHardwareModernHighSierra; +- +- // 10.13.4 fixed black screen issues +- if (maxHardwareKexts != MaxRadeonHardware) { +- for (size_t i = 0; i < MaxGetFrameBufferProcs; i++) +- getFrameBufferProcNames[IndexRadeonHardwareX4000][i] = nullptr; +- +- // We have nothing to do for these kexts on recent systems +- if (!fixConfigName && !forceOpenGL && !forceCodecInfo) { +- // X4000 kext is not included in this list as we need to fix GVA properties for most of its GPUs +- kextRadeonHardware[IndexRadeonHardwareX5000].switchOff(); +- kextRadeonHardware[IndexRadeonHardwareX6000].switchOff(); +- } +- } ++union dprx_feature { ++ struct { ++ uint8_t GTC_CAP:1; // bit 0: DP 1.3+ ++ uint8_t SST_SPLIT_SDP_CAP:1; // bit 1: DP 1.4 ++ uint8_t AV_SYNC_CAP:1; // bit 2: DP 1.3+ ++ uint8_t VSC_SDP_COLORIMETRY_SUPPORTED:1; // bit 3: DP 1.3+ ++ uint8_t VSC_EXT_VESA_SDP_SUPPORTED:1; // bit 4: DP 1.4 ++ uint8_t VSC_EXT_VESA_SDP_CHAINING_SUPPORTED:1; // bit 5: DP 1.4 ++ uint8_t VSC_EXT_CEA_SDP_SUPPORTED:1; // bit 6: DP 1.4 ++ uint8_t VSC_EXT_CEA_SDP_CHAINING_SUPPORTED:1; // bit 7: DP 1.4 ++ } bits; ++ uint8_t raw; ++}; + +- if (getKernelVersion() < KernelVersion::Catalina) { +- kextRadeonHardware[IndexRadeonHardwareX6000].switchOff(); +- } ++union sink_count { ++ struct { ++ uint8_t SINK_COUNT:6; ++ uint8_t CPREADY:1; ++ uint8_t RESERVED:1; ++ } bits; ++ uint8_t raw; ++}; + +- if (getKernelVersion() < KernelVersion::HighSierra) { +- // Versions before 10.13 do not support X4250 and X5000 +- kextRadeonHardware[IndexRadeonHardwareX4250].switchOff(); +- kextRadeonHardware[IndexRadeonHardwareX5000].switchOff(); ++struct dc_dongle_caps { ++ /* dongle type (DP converter, CV smart dongle) */ ++ uint32_t dongle_type; ++ bool extendedCapValid; ++ /* If dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER, ++ indicates 'Frame Sequential-to-lllFrame Pack' conversion capability.*/ ++ bool is_dp_hdmi_s3d_converter; ++ bool is_dp_hdmi_ycbcr422_pass_through; ++ bool is_dp_hdmi_ycbcr420_pass_through; ++ bool is_dp_hdmi_ycbcr422_converter; ++ bool is_dp_hdmi_ycbcr420_converter; ++ uint32_t dp_hdmi_max_bpc; ++ uint32_t dp_hdmi_max_pixel_clk_in_khz; ++}; + +- // Versions before 10.13 have legacy X3000 and X4000 IDs +- kextRadeonHardware[IndexRadeonHardwareX3000].id = idRadeonX3000Old; +- kextRadeonHardware[IndexRadeonHardwareX4000].id = idRadeonX4000Old; ++union dpcd_fec_capability { ++ struct { ++ uint8_t FEC_CAPABLE:1; ++ uint8_t UNCORRECTED_BLOCK_ERROR_COUNT_CAPABLE:1; ++ uint8_t CORRECTED_BLOCK_ERROR_COUNT_CAPABLE:1; ++ uint8_t BIT_ERROR_COUNT_CAPABLE:1; ++ uint8_t RESERVED:4; ++ } bits; ++ uint8_t raw; ++}; + +- bool preSierra = getKernelVersion() < KernelVersion::Sierra; ++union dpcd_dsc_branch_decoder_capabilities { ++ struct { ++ uint8_t BRANCH_OVERALL_THROUGHPUT_0; ++ uint8_t BRANCH_OVERALL_THROUGHPUT_1; ++ uint8_t BRANCH_MAX_LINE_WIDTH; ++ } fields; ++ uint8_t raw[3]; ++}; + +- if (preSierra) { +- // Versions before 10.12 do not support X4100 +- kextRadeonHardware[IndexRadeonHardwareX4100].switchOff(); +- } ++struct dpcd_dsc_support { ++ uint8_t DSC_SUPPORT :1; ++ uint8_t DSC_PASSTHROUGH_SUPPORT :1; ++ uint8_t RESERVED :6; ++}; + +- if (preSierra || (getKernelVersion() == KernelVersion::Sierra && getKernelMinorVersion() < 7)) { +- // Versions before 10.12.6 do not support X4150, X4200 +- kextRadeonHardware[IndexRadeonHardwareX4150].switchOff(); +- kextRadeonHardware[IndexRadeonHardwareX4200].switchOff(); +- } +- } ++struct dpcd_dsc_algorithm_revision { ++ uint8_t DSC_VERSION_MAJOR :4; ++ uint8_t DSC_VERSION_MINOR :4; ++}; + +- lilu.onKextLoadForce(kextRadeonHardware, maxHardwareKexts); +-} ++struct dpcd_dsc_rc_buffer_block_size { ++ uint8_t RC_BLOCK_BUFFER_SIZE :2; ++ uint8_t RESERVED :6; ++}; + +-void RAD::process24BitOutput(KernelPatcher &patcher, KernelPatcher::KextInfo &info, mach_vm_address_t address, size_t size) { +- auto bitsPerComponent = patcher.solveSymbol(info.loadIndex, "__ZL18BITS_PER_COMPONENT", address, size); +- if (bitsPerComponent) { +- while (bitsPerComponent && *bitsPerComponent) { +- if (*bitsPerComponent == 10) { +- auto ret = MachInfo::setKernelWriting(true, KernelPatcher::kernelWriteLock); +- if (ret == KERN_SUCCESS) { +- DBGLOG("rad", "fixing BITS_PER_COMPONENT"); +- *bitsPerComponent = 8; +- MachInfo::setKernelWriting(false, KernelPatcher::kernelWriteLock); +- } else { +- SYSLOG("rad", "failed to disable write protection for BITS_PER_COMPONENT"); +- } +- } +- bitsPerComponent++; +- } +- } else { +- SYSLOG("rad", "failed to find BITS_PER_COMPONENT"); +- patcher.clearError(); +- } ++struct dpcd_dsc_slice_capability1 { ++ uint8_t ONE_SLICE_PER_DP_DSC_SINK_DEVICE :1; ++ uint8_t TWO_SLICES_PER_DP_DSC_SINK_DEVICE :1; ++ uint8_t RESERVED :1; ++ uint8_t FOUR_SLICES_PER_DP_DSC_SINK_DEVICE :1; ++ uint8_t SIX_SLICES_PER_DP_DSC_SINK_DEVICE :1; ++ uint8_t EIGHT_SLICES_PER_DP_DSC_SINK_DEVICE :1; ++ uint8_t TEN_SLICES_PER_DP_DSC_SINK_DEVICE :1; ++ uint8_t TWELVE_SLICES_PER_DP_DSC_SINK_DEVICE :1; ++}; + +- DBGLOG("rad", "fixing pixel types"); ++struct dpcd_dsc_line_buffer_bit_depth { ++ uint8_t LINE_BUFFER_BIT_DEPTH :4; ++ uint8_t RESERVED :4; ++}; + +- KernelPatcher::LookupPatch pixelPatch { +- &info, +- reinterpret_cast("--RRRRRRRRRRGGGGGGGGGGBBBBBBBBBB"), +- reinterpret_cast("--------RRRRRRRRGGGGGGGGBBBBBBBB"), +- 32, 2 +- }; ++struct dpcd_dsc_block_prediction_support { ++ uint8_t BLOCK_PREDICTION_SUPPORT:1; ++ uint8_t RESERVED :7; ++}; + +- patcher.applyLookupPatch(&pixelPatch); +- if (patcher.getError() != KernelPatcher::Error::NoError) { +- SYSLOG("rad", "failed to patch RGB mask for 24-bit output"); +- patcher.clearError(); +- } +-} ++struct dpcd_maximum_bits_per_pixel_supported_by_the_decompressor { ++ uint8_t MAXIMUM_BITS_PER_PIXEL_SUPPORTED_BY_THE_DECOMPRESSOR_LOW :7; ++ uint8_t MAXIMUM_BITS_PER_PIXEL_SUPPORTED_BY_THE_DECOMPRESSOR_HIGH :7; ++ uint8_t RESERVED :2; ++}; + +-void RAD::processConnectorOverrides(KernelPatcher &patcher, mach_vm_address_t address, size_t size, bool modern) { +- if (modern) { +- if (getKernelVersion() >= KernelVersion::HighSierra) { +- KernelPatcher::RouteRequest requests[] { +- KernelPatcher::RouteRequest("__ZN14AtiBiosParser116getConnectorInfoEP13ConnectorInfoRh", wrapGetConnectorsInfoV1, orgGetConnectorsInfoV1), +- KernelPatcher::RouteRequest("__ZN14AtiBiosParser216getConnectorInfoEP13ConnectorInfoRh", wrapGetConnectorsInfoV2, orgGetConnectorsInfoV2), +- KernelPatcher::RouteRequest("__ZN14AtiBiosParser126translateAtomConnectorInfoERN30AtiObjectInfoTableInterface_V117AtomConnectorInfoER13ConnectorInfo", +- wrapTranslateAtomConnectorInfoV1, orgTranslateAtomConnectorInfoV1), +- KernelPatcher::RouteRequest("__ZN14AtiBiosParser226translateAtomConnectorInfoERN30AtiObjectInfoTableInterface_V217AtomConnectorInfoER13ConnectorInfo", +- wrapTranslateAtomConnectorInfoV2, orgTranslateAtomConnectorInfoV2), +- KernelPatcher::RouteRequest("__ZN13ATIController5startEP9IOService", wrapATIControllerStart, orgATIControllerStart) +- }; +- patcher.routeMultiple(kextRadeonSupport.loadIndex, requests, address, size); +- } else { +- KernelPatcher::RouteRequest requests[] { +- KernelPatcher::RouteRequest("__ZN23AtiAtomBiosDceInterface17getConnectorsInfoEP13ConnectorInfoRh", wrapGetConnectorsInfoV1, orgGetConnectorsInfoV1), +- KernelPatcher::RouteRequest("__ZN13ATIController5startEP9IOService", wrapATIControllerStart, orgATIControllerStart), +- }; +- patcher.routeMultiple(kextRadeonSupport.loadIndex, requests, address, size); +- +- orgGetAtomObjectTableForType = reinterpret_cast(patcher.solveSymbol(kextRadeonSupport.loadIndex, +- "__ZN20AtiAtomBiosUtilities25getAtomObjectTableForTypeEhRh", address, size)); +- if (!orgGetAtomObjectTableForType) { +- SYSLOG("rad", "failed to find AtiAtomBiosUtilities::getAtomObjectTableForType"); +- patcher.clearError(); +- } +- } +- } else { +- KernelPatcher::RouteRequest requests[] { +- KernelPatcher::RouteRequest("__ZN23AtiAtomBiosDceInterface17getConnectorsInfoEP13ConnectorInfoRh", wrapLegacyGetConnectorsInfo, orgLegacyGetConnectorsInfo), +- KernelPatcher::RouteRequest("__ZN19AMDLegacyController5startEP9IOService", wrapLegacyATIControllerStart, orgLegacyATIControllerStart), +- }; +- patcher.routeMultiple(kextRadeonLegacySupport.loadIndex, requests, address, size); ++struct dpcd_dsc_decoder_color_format_capabilities { ++ uint8_t RGB_SUPPORT :1; ++ uint8_t Y_CB_CR_444_SUPPORT :1; ++ uint8_t Y_CB_CR_SIMPLE_422_SUPPORT :1; ++ uint8_t Y_CB_CR_NATIVE_422_SUPPORT :1; ++ uint8_t Y_CB_CR_NATIVE_420_SUPPORT :1; ++ uint8_t RESERVED :3; ++}; + +- orgLegacyGetAtomObjectTableForType = patcher.solveSymbol(kextRadeonLegacySupport.loadIndex, +- "__ZN20AtiAtomBiosUtilities25getAtomObjectTableForTypeEhRh", address, size); +- if (!orgLegacyGetAtomObjectTableForType) { +- SYSLOG("rad", "failed to find AtiAtomBiosUtilities::getAtomObjectTableForType"); +- patcher.clearError(); +- } +- } +-} ++struct dpcd_dsc_decoder_color_depth_capabilities { ++ uint8_t RESERVED0 :1; ++ uint8_t EIGHT_BITS_PER_COLOR_SUPPORT :1; ++ uint8_t TEN_BITS_PER_COLOR_SUPPORT :1; ++ uint8_t TWELVE_BITS_PER_COLOR_SUPPORT :1; ++ uint8_t RESERVED1 :4; ++}; + +-void RAD::processHardwareKext(KernelPatcher &patcher, size_t hwIndex, mach_vm_address_t address, size_t size) { +- auto getFrame = getFrameBufferProcNames[hwIndex]; +- auto &hardware = kextRadeonHardware[hwIndex]; +- +- // Fix boot and wake to black screen +- for (size_t j = 0; j < MaxGetFrameBufferProcs && getFrame[j] != nullptr; j++) { +- auto getFB = patcher.solveSymbol(hardware.loadIndex, getFrame[j], address, size); +- if (getFB) { +- // Initially it was discovered that the only problematic register is PRIMARY_SURFACE_ADDRESS_HIGH (0x1A07). +- // This register must be nulled to solve most of the issues. +- // Depending on the amount of connected screens PRIMARY_SURFACE_ADDRESS (0x1A04) may not be null. +- // However, as of AMD Vega drivers in 10.13 DP1 both of these registers are now ignored. +- // Furthermore, there are no (extra) issues from just returning 0 in framebuffer base address. +- +- // xor rax, rax +- // ret +- uint8_t ret[] {0x48, 0x31, 0xC0, 0xC3}; +- patcher.routeBlock(getFB, ret, sizeof(ret)); +- if (patcher.getError() == KernelPatcher::Error::NoError) { +- DBGLOG("rad", "patched %s", getFrame[j]); +- } else { +- SYSLOG("rad", "failed to patch %s code %d", getFrame[j], patcher.getError()); +- patcher.clearError(); +- } +- } else { +- SYSLOG("rad", "failed to find %s code %d", getFrame[j], patcher.getError()); +- patcher.clearError(); +- } +- } ++struct dpcd_peak_dsc_throughput_dsc_sink { ++ uint8_t THROUGHPUT_MODE_0:4; ++ uint8_t THROUGHPUT_MODE_1:4; ++}; + +- // Fix reported Accelerator name to support WhateverName.app +- // Also fix GVA properties for X4000. +- if (fixConfigName || hwIndex == IndexRadeonHardwareX4000) { +- KernelPatcher::RouteRequest request(populateAccelConfigProcNames[hwIndex], wrapPopulateAccelConfig[hwIndex], orgPopulateAccelConfig[hwIndex]); +- patcher.routeMultiple(hardware.loadIndex, &request, 1, address, size); +- } ++struct dpcd_dsc_slice_capabilities_2 { ++ uint8_t SIXTEEN_SLICES_PER_DSC_SINK_DEVICE :1; ++ uint8_t TWENTY_SLICES_PER_DSC_SINK_DEVICE :1; ++ uint8_t TWENTYFOUR_SLICES_PER_DSC_SINK_DEVICE :1; ++ uint8_t RESERVED :5; ++}; + +- // Enforce OpenGL support if requested +- if (forceOpenGL) { +- DBGLOG("rad", "disabling Metal support"); +- uint8_t find1[] {0x4D, 0x65, 0x74, 0x61, 0x6C, 0x53, 0x74, 0x61}; +- uint8_t find2[] {0x4D, 0x65, 0x74, 0x61, 0x6C, 0x50, 0x6C, 0x75}; +- uint8_t repl1[] {0x50, 0x65, 0x74, 0x61, 0x6C, 0x53, 0x74, 0x61}; +- uint8_t repl2[] {0x50, 0x65, 0x74, 0x61, 0x6C, 0x50, 0x6C, 0x75}; +- +- KernelPatcher::LookupPatch antimetal[] { +- {&hardware, find1, repl1, sizeof(find1), 2}, +- {&hardware, find2, repl2, sizeof(find1), 2} +- }; ++struct dpcd_bits_per_pixel_increment{ ++ uint8_t INCREMENT_OF_BITS_PER_PIXEL_SUPPORTED :3; ++ uint8_t RESERVED :5; ++}; ++union dpcd_dsc_basic_capabilities { ++ struct { ++ struct dpcd_dsc_support dsc_support; ++ struct dpcd_dsc_algorithm_revision dsc_algorithm_revision; ++ struct dpcd_dsc_rc_buffer_block_size dsc_rc_buffer_block_size; ++ uint8_t dsc_rc_buffer_size; ++ struct dpcd_dsc_slice_capability1 dsc_slice_capabilities_1; ++ struct dpcd_dsc_line_buffer_bit_depth dsc_line_buffer_bit_depth; ++ struct dpcd_dsc_block_prediction_support dsc_block_prediction_support; ++ struct dpcd_maximum_bits_per_pixel_supported_by_the_decompressor maximum_bits_per_pixel_supported_by_the_decompressor; ++ struct dpcd_dsc_decoder_color_format_capabilities dsc_decoder_color_format_capabilities; ++ struct dpcd_dsc_decoder_color_depth_capabilities dsc_decoder_color_depth_capabilities; ++ struct dpcd_peak_dsc_throughput_dsc_sink peak_dsc_throughput_dsc_sink; ++ uint8_t dsc_maximum_slice_width; ++ struct dpcd_dsc_slice_capabilities_2 dsc_slice_capabilities_2; ++ uint8_t reserved; ++ struct dpcd_bits_per_pixel_increment bits_per_pixel_increment; ++ } fields; ++ uint8_t raw[16]; ++}; + +- for (auto &p : antimetal) { +- patcher.applyLookupPatch(&p); +- patcher.clearError(); +- } +- } ++struct dpcd_dsc_capabilities { ++ union dpcd_dsc_basic_capabilities dsc_basic_caps; ++ union dpcd_dsc_branch_decoder_capabilities dsc_branch_decoder_caps; ++}; + +- // Patch AppleGVA support for non-supported models +- if (forceCodecInfo && getHWInfoProcNames[hwIndex] != nullptr) { +- KernelPatcher::RouteRequest request(getHWInfoProcNames[hwIndex], wrapGetHWInfo[hwIndex], orgGetHWInfo[hwIndex]); +- patcher.routeMultiple(hardware.loadIndex, &request, 1, address, size); +- } +-} ++#define MAX_REPEATER_CNT 8 + +-void RAD::mergeProperty(OSDictionary *props, const char *name, OSObject *value) { +- // The only type we could make from device properties is data. +- // To be able to override other types we do a conversion here. +- auto data = OSDynamicCast(OSData, value); +- if (data) { +- // It is hard to make a boolean even from ACPI, so we make a hack here: +- // 1-byte OSData with 0x01 / 0x00 values becomes boolean. +- auto val = static_cast(data->getBytesNoCopy()); +- auto len = data->getLength(); +- if (val && len == sizeof(uint8_t)) { +- if (val[0] == 1) { +- props->setObject(name, kOSBooleanTrue); +- DBGLOG("rad", "prop %s was merged as kOSBooleanTrue", name); +- return; +- } else if (val[0] == 0) { +- props->setObject(name, kOSBooleanFalse); +- DBGLOG("rad", "prop %s was merged as kOSBooleanFalse", name); +- return; +- } +- } ++struct dc_lttpr_caps { ++ union dpcd_rev revision; ++ uint8_t mode; ++ uint8_t max_lane_count; ++ uint8_t max_link_rate; ++ uint8_t phy_repeater_cnt; ++ uint8_t max_ext_timeout; ++ uint8_t aux_rd_interval[MAX_REPEATER_CNT - 1]; ++}; + +- // Consult the original value to make a decision +- auto orgValue = props->getObject(name); +- if (val && orgValue) { +- DBGLOG("rad", "prop %s has original value", name); +- if (len == sizeof(uint32_t) && OSDynamicCast(OSNumber, orgValue)) { +- auto num = *reinterpret_cast(val); +- auto osnum = OSNumber::withNumber(num, 32); +- if (osnum) { +- DBGLOG("rad", "prop %s was merged as number %u", name, num); +- props->setObject(name, osnum); +- osnum->release(); +- } +- return; +- } else if (len > 0 && val[len-1] == '\0' && OSDynamicCast(OSString, orgValue)) { +- auto str = reinterpret_cast(val); +- auto osstr = OSString::withCString(str); +- if (osstr) { +- DBGLOG("rad", "prop %s was merged as string %s", name, str); +- props->setObject(name, osstr); +- osstr->release(); +- } +- return; +- } +- } else { +- DBGLOG("rad", "prop %s has no original value", name); +- } +- } ++struct psr_caps { ++ unsigned char psr_version; ++ unsigned int psr_rfb_setup_time; ++ bool psr_exit_link_training_required; ++}; + +- // Default merge as is +- props->setObject(name, value); +- DBGLOG("rad", "prop %s was merged", name); +-} ++struct dpcd_caps { ++ union dpcd_rev dpcd_rev; ++ union max_lane_count max_ln_count; ++ union max_down_spread max_down_spread; ++ union dprx_feature dprx_feature; ++ ++ /* valid only for eDP v1.4 or higher*/ ++ uint8_t edp_supported_link_rates_count; ++ uint32_t edp_supported_link_rates[8]; ++ ++ /* dongle type (DP converter, CV smart dongle) */ ++ uint32_t dongle_type; ++ /* branch device or sink device */ ++ bool is_branch_dev; ++ /* Dongle's downstream count. */ ++ union sink_count sink_count; ++ /* If dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER, ++ indicates 'Frame Sequential-to-lllFrame Pack' conversion capability.*/ ++ struct dc_dongle_caps dongle_caps; ++ ++ uint32_t sink_dev_id; ++ int8_t sink_dev_id_str[6]; ++ int8_t sink_hw_revision; ++ int8_t sink_fw_revision[2]; ++ ++ uint32_t branch_dev_id; ++ int8_t branch_dev_name[6]; ++ int8_t branch_hw_revision; ++ int8_t branch_fw_revision[2]; ++ ++ bool allow_invalid_MSA_timing_param; ++ bool panel_mode_edp; ++ bool dpcd_display_control_capable; ++ bool ext_receiver_cap_field_present; ++ bool dynamic_backlight_capable_edp; ++ union dpcd_fec_capability fec_cap; ++ struct dpcd_dsc_capabilities dsc_caps; ++ struct dc_lttpr_caps lttpr_caps; ++ struct psr_caps psr_caps; + +-void RAD::mergeProperties(OSDictionary *props, const char *prefix, IOService *provider) { +- // Should be ok, but in case there are issues switch to dictionaryWithProperties(); +- auto dict = provider->getPropertyTable(); +- if (dict) { +- auto iterator = OSCollectionIterator::withCollection(dict); +- if (iterator) { +- OSSymbol *propname; +- size_t prefixlen = strlen(prefix); +- while ((propname = OSDynamicCast(OSSymbol, iterator->getNextObject())) != nullptr) { +- auto name = propname->getCStringNoCopy(); +- if (name && propname->getLength() > prefixlen && !strncmp(name, prefix, prefixlen)) { +- auto prop = dict->getObject(propname); +- if (prop) +- mergeProperty(props, name + prefixlen, prop); +- else +- DBGLOG("rad", "prop %s was not merged due to no value", name); +- } else { +- //DBGLOG("rad", "prop %s does not match %s prefix", safeString(name), prefix); +- } +- } ++}; + +- iterator->release(); +- } else { +- SYSLOG("rad", "prop merge failed to iterate over properties"); +- } +- } else { +- SYSLOG("rad", "prop merge failed to get properties"); +- } ++union dpcd_sink_ext_caps { ++ struct { ++ /* 0 - Sink supports backlight adjust via PWM during SDR/HDR mode ++ * 1 - Sink supports backlight adjust via AUX during SDR/HDR mode. ++ */ ++ uint8_t sdr_aux_backlight_control : 1; ++ uint8_t hdr_aux_backlight_control : 1; ++ uint8_t reserved_1 : 2; ++ uint8_t oled : 1; ++ uint8_t reserved : 3; ++ } bits; ++ uint8_t raw; ++}; + +- if (!strcmp(prefix, "CAIL,")) { +- for (size_t i = 0; i < arrsize(powerGatingFlags); i++) { +- if (powerGatingFlags[i] && props->getObject(powerGatingFlags[i])) { +- DBGLOG("rad", "cail prop merge found %s, replacing", powerGatingFlags[i]); +- auto num = OSNumber::withNumber(1, 32); +- if (num) { +- props->setObject(powerGatingFlags[i], num); +- num->release(); +- } +- } +- } +- } +-} ++struct dc; ++ ++typedef struct { ++ void *remote_sinks[MAX_SINKS_PER_LINK]; ++ unsigned int sink_count; ++ void *local_sink; ++ unsigned int link_index; ++ uint32_t type; ++ uint32_t connector_signal; ++ uint32_t irq_source_hpd; ++ uint32_t irq_source_hpd_rx;/* aka DP Short Pulse */ ++ bool is_hpd_filter_disabled; ++ bool dp_ss_off; ++ bool link_state_valid; ++ bool aux_access_disabled; ++ bool sync_lt_in_progress; ++ uint32_t lttpr_mode; ++ bool is_internal_display; ++ ++ /* TODO: Rename. Flag an endpoint as having a programmable mapping to a ++ * DIG encoder. */ ++ bool is_dig_mapping_flexible; ++ bool hpd_status; /* HPD status of link without physical HPD pin. */ ++ ++ bool edp_sink_present; ++ ++ /* caps is the same as reported_link_cap. link_traing use ++ * reported_link_cap. Will clean up. TODO ++ */ ++ struct dc_link_settings reported_link_cap; ++ struct dc_link_settings verified_link_cap; ++ struct dc_link_settings cur_link_settings; ++ struct dc_lane_settings cur_lane_setting; ++ struct dc_link_settings preferred_link_setting; ++ struct dc_link_training_overrides preferred_training_settings; ++ struct dp_audio_test_data audio_test_data; ++ ++ uint8_t ddc_hw_inst; ++ ++ uint8_t hpd_src; ++ ++ uint8_t link_enc_hw_inst; ++ /* DIG link encoder ID. Used as index in link encoder resource pool. ++ * For links with fixed mapping to DIG, this is not changed after dc_link ++ * object creation. ++ */ ++ uint32_t eng_id; ++ ++ bool test_pattern_enabled; ++ union compliance_test_state compliance_test_state; ++ ++ void *priv; ++ ++ void *ddc; ++ ++ bool aux_mode; ++ ++ /* Private to DC core */ ++ ++ struct dc *dc; ++ ++ void *ctx; ++ ++ void *panel_cntl; ++ void *link_enc; ++ struct graphics_object_id link_id; ++ /* Endpoint type distinguishes display endpoints which do not have entries ++ * in the BIOS connector table from those that do. Helps when tracking link ++ * encoder to display endpoint assignments. ++ */ ++ uint32_t ep_type; ++ union ddi_channel_mapping ddi_channel_mapping; ++ struct connector_device_tag_info device_tag; ++ struct dpcd_caps dpcd_caps; ++ uint32_t dongle_max_pix_clk; ++ unsigned short chip_caps; ++ unsigned int dpcd_sink_count; ++//#if defined(CONFIG_DRM_AMD_DC_HDCP) ++// struct hdcp_caps hdcp_caps; ++//#endif ++ uint32_t edp_revision; ++ union dpcd_sink_ext_caps dpcd_sink_ext_caps; ++ ++ /* ++ struct psr_settings psr_settings; ++ ++ // MST record stream using this link ++ struct link_flags { ++ bool dp_keep_receiver_powered; ++ bool dp_skip_DID2; ++ bool dp_skip_reset_segment; ++ } wa_flags; ++ struct link_mst_stream_allocation_table mst_stream_alloc_table; ++ ++ struct dc_link_status link_status; ++ ++ struct link_trace link_trace; ++ struct gpio *hpd_gpio; ++ enum dc_link_fec_state fec_state; ++ */ ++} my_dc_link_t; ++ ++typedef struct { ++ void* plane_state; ++ struct dc_stream_state *stream; ++} my_pipe_ctx_t; ++ ++struct panel_cntl_backlight_registers { ++ unsigned int BL_PWM_CNTL; ++ unsigned int BL_PWM_CNTL2; ++ unsigned int BL_PWM_PERIOD_CNTL; ++ unsigned int LVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV; ++}; + +-void RAD::applyPropertyFixes(IOService *service, uint32_t connectorNum) { +- if (service && getKernelVersion() >= KernelVersion::HighSierra) { +- // Starting with 10.13.2 this is important to fix sleep issues due to enforced 6 screens +- if (!service->getProperty("CFG,CFG_FB_LIMIT")) { +- DBGLOG("rad", "setting fb limit to %u", connectorNum); +- service->setProperty("CFG_FB_LIMIT", connectorNum, 32); +- } ++struct hw_asic_id { ++ uint32_t chip_id; ++ uint32_t chip_family; ++ uint32_t pci_revision_id; ++ uint32_t hw_internal_rev; ++ uint32_t vram_type; ++ uint32_t vram_width; ++ uint32_t feature_flags; ++ uint32_t fake_paths_num; ++ void *atombios_base_address; ++}; + +- // In the past we set CFG_USE_AGDC to false, which caused visual glitches and broken multimonitor support. +- // A better workaround is to disable AGDP just like we do globally. +- } +-} ++struct dc_vram_info { ++ unsigned int num_chans; ++ unsigned int dram_channel_width_bytes; ++}; + +-void RAD::updateConnectorsInfo(void *atomutils, t_getAtomObjectTableForType gettable, IOService *ctrl, RADConnectors::Connector *connectors, uint8_t *sz) { +- if (atomutils) { +- DBGLOG("rad", "getConnectorsInfo found %u connectors", *sz); +- RADConnectors::print(connectors, *sz); +- } ++struct dc_golden_table { ++ uint16_t dc_golden_table_ver; ++ uint32_t aux_dphy_rx_control0_val; ++ uint32_t aux_dphy_tx_control_val; ++ uint32_t aux_dphy_rx_control1_val; ++ uint32_t dc_gpio_aux_ctrl_0_val; ++ uint32_t dc_gpio_aux_ctrl_1_val; ++ uint32_t dc_gpio_aux_ctrl_2_val; ++ uint32_t dc_gpio_aux_ctrl_3_val; ++ uint32_t dc_gpio_aux_ctrl_4_val; ++ uint32_t dc_gpio_aux_ctrl_5_val; ++}; + +- // Check if the user wants to override automatically detected connectors +- auto cons = ctrl->getProperty("connectors"); +- if (cons) { +- auto consData = OSDynamicCast(OSData, cons); +- if (consData) { +- auto consPtr = consData->getBytesNoCopy(); +- auto consSize = consData->getLength(); +- +- uint32_t consCount; +- if (WIOKit::getOSDataValue(ctrl, "connector-count", consCount)) { +- *sz = consCount; +- DBGLOG("rad", "getConnectorsInfo got size override to %u", *sz); +- } ++struct dc_firmware_info { ++ struct pll_info { ++ uint32_t crystal_frequency; /* in KHz */ ++ uint32_t min_input_pxl_clk_pll_frequency; /* in KHz */ ++ uint32_t max_input_pxl_clk_pll_frequency; /* in KHz */ ++ uint32_t min_output_pxl_clk_pll_frequency; /* in KHz */ ++ uint32_t max_output_pxl_clk_pll_frequency; /* in KHz */ ++ } pll_info; ++ ++ struct firmware_feature { ++ uint32_t memory_clk_ss_percentage; ++ uint32_t engine_clk_ss_percentage; ++ } feature; ++ ++ uint32_t default_display_engine_pll_frequency; /* in KHz */ ++ uint32_t external_clock_source_frequency_for_dp; /* in KHz */ ++ uint32_t smu_gpu_pll_output_freq; /* in KHz */ ++ uint8_t min_allowed_bl_level; ++ uint8_t remote_display_config; ++ uint32_t default_memory_clk; /* in KHz */ ++ uint32_t default_engine_clk; /* in KHz */ ++ uint32_t dp_phy_ref_clk; /* in KHz - DCE12 only */ ++ uint32_t i2c_engine_ref_clk; /* in KHz - DCE12 only */ ++ bool oem_i2c_present; ++ uint8_t oem_i2c_obj_id; + +- if (consPtr && consSize > 0 && *sz > 0 && RADConnectors::valid(consSize, *sz)) { +- RADConnectors::copy(connectors, *sz, static_cast(consPtr), consSize); +- DBGLOG("rad", "getConnectorsInfo installed %u connectors", *sz); +- applyPropertyFixes(ctrl, *sz); +- } else { +- DBGLOG("rad", "getConnectorsInfo conoverrides have invalid size %u for %u num", consSize, *sz); +- } +- } else { +- DBGLOG("rad", "getConnectorsInfo conoverrides have invalid type"); +- } +- } else { +- if (atomutils) { +- DBGLOG("rad", "getConnectorsInfo attempting to autofix connectors"); +- uint8_t sHeader = 0, displayPathNum = 0, connectorObjectNum = 0; +- auto baseAddr = static_cast(gettable(atomutils, AtomObjectTableType::Common, &sHeader)) - sizeof(uint32_t); +- auto displayPaths = static_cast(gettable(atomutils, AtomObjectTableType::DisplayPath, &displayPathNum)); +- auto connectorObjects = static_cast(gettable(atomutils, AtomObjectTableType::ConnectorObject, &connectorObjectNum)); +- if (displayPathNum == connectorObjectNum) +- autocorrectConnectors(baseAddr, displayPaths, displayPathNum, connectorObjects, connectorObjectNum, connectors, *sz); +- else +- DBGLOG("rad", "getConnectorsInfo found different displaypaths %u and connectors %u", displayPathNum, connectorObjectNum); +- } ++}; + +- applyPropertyFixes(ctrl, *sz); +- +- // Prioritise connectors, since it may cause black screen on e.g. R9 370 +- const uint8_t *senseList = nullptr; +- uint8_t senseNum = 0; +- auto priData = OSDynamicCast(OSData, ctrl->getProperty("connector-priority")); +- if (priData) { +- senseList = static_cast(priData->getBytesNoCopy()); +- senseNum = static_cast(priData->getLength()); +- DBGLOG("rad", "getConnectorInfo found %u senses in connector-priority", senseNum); +- reprioritiseConnectors(senseList, senseNum, connectors, *sz); +- } else { +- DBGLOG("rad", "getConnectorInfo leaving unchaged priority"); +- } +- } ++#define NUMBER_OF_UCHAR_FOR_GUID 16 ++#define MAX_NUMBER_OF_EXT_DISPLAY_PATH 7 ++#define NUMBER_OF_CSR_M3_ARB 10 ++#define NUMBER_OF_DISP_CLK_VOLTAGE 4 ++#define NUMBER_OF_AVAILABLE_SCLK 5 + +- DBGLOG("rad", "getConnectorsInfo resulting %u connectors follow", *sz); +- RADConnectors::print(connectors, *sz); +-} ++struct i2c_reg_info { ++ unsigned char i2c_reg_index; ++ unsigned char i2c_reg_val; ++}; + +-void RAD::autocorrectConnectors(uint8_t *baseAddr, AtomDisplayObjectPath *displayPaths, uint8_t displayPathNum, AtomConnectorObject *connectorObjects, +- uint8_t connectorObjectNum, RADConnectors::Connector *connectors, uint8_t sz) { +- for (uint8_t i = 0; i < displayPathNum; i++) { +- if (!isEncoder(displayPaths[i].usGraphicObjIds)) { +- DBGLOG("rad", "autocorrectConnectors not encoder %X at %u", displayPaths[i].usGraphicObjIds, i); +- continue; +- } ++struct edp_info { ++ uint16_t edp_backlight_pwm_hz; ++ uint16_t edp_ss_percentage; ++ uint16_t edp_ss_rate_10hz; ++ uint8_t edp_pwr_on_off_delay; ++ uint8_t edp_pwr_on_vary_bl_to_blon; ++ uint8_t edp_pwr_down_bloff_to_vary_bloff; ++ uint8_t edp_panel_bpc; ++ uint8_t edp_bootup_bl_level; ++}; + +- uint8_t txmit = 0, enc = 0; +- if (!getTxEnc(displayPaths[i].usGraphicObjIds, txmit, enc)) +- continue; ++struct integrated_info { ++ struct clock_voltage_caps { ++ /* The Voltage Index indicated by FUSE, same voltage index ++ shared with SCLK DPM fuse table */ ++ uint32_t voltage_index; ++ /* Maximum clock supported with specified voltage index */ ++ uint32_t max_supported_clk; /* in KHz */ ++ } disp_clk_voltage[NUMBER_OF_DISP_CLK_VOLTAGE]; ++ ++ struct display_connection_info { ++ struct external_display_path { ++ /* A bit vector to show what devices are supported */ ++ uint32_t device_tag; ++ /* 16bit device ACPI id. */ ++ uint32_t device_acpi_enum; ++ /* A physical connector for displays to plug in, ++ using object connector definitions */ ++ struct graphics_object_id device_connector_id; ++ /* An index into external AUX/DDC channel LUT */ ++ uint8_t ext_aux_ddc_lut_index; ++ /* An index into external HPD pin LUT */ ++ uint8_t ext_hpd_pin_lut_index; ++ /* external encoder object id */ ++ struct graphics_object_id ext_encoder_obj_id; ++ /* XBAR mapping of the PHY channels */ ++ union ddi_channel_mapping channel_mapping; ++ ++ unsigned short caps; ++ } path[MAX_NUMBER_OF_EXT_DISPLAY_PATH]; ++ ++ uint8_t gu_id[NUMBER_OF_UCHAR_FOR_GUID]; ++ uint8_t checksum; ++ } ext_disp_conn_info; /* exiting long long time */ ++ ++ struct available_s_clk_list { ++ /* Maximum clock supported with specified voltage index */ ++ uint32_t supported_s_clk; /* in KHz */ ++ /* The Voltage Index indicated by FUSE for specified SCLK */ ++ uint32_t voltage_index; ++ /* The Voltage ID indicated by FUSE for specified SCLK */ ++ uint32_t voltage_id; ++ } avail_s_clk[NUMBER_OF_AVAILABLE_SCLK]; ++ ++ uint8_t memory_type; ++ uint8_t ma_channel_number; ++ uint32_t boot_up_engine_clock; /* in KHz */ ++ uint32_t dentist_vco_freq; /* in KHz */ ++ uint32_t boot_up_uma_clock; /* in KHz */ ++ uint32_t boot_up_req_display_vector; ++ uint32_t other_display_misc; ++ uint32_t gpu_cap_info; ++ uint32_t sb_mmio_base_addr; ++ uint32_t system_config; ++ uint32_t cpu_cap_info; ++ uint32_t max_nb_voltage; ++ uint32_t min_nb_voltage; ++ uint32_t boot_up_nb_voltage; ++ uint32_t ext_disp_conn_info_offset; ++ uint32_t csr_m3_arb_cntl_default[NUMBER_OF_CSR_M3_ARB]; ++ uint32_t csr_m3_arb_cntl_uvd[NUMBER_OF_CSR_M3_ARB]; ++ uint32_t csr_m3_arb_cntl_fs3d[NUMBER_OF_CSR_M3_ARB]; ++ uint32_t gmc_restore_reset_time; ++ uint32_t minimum_n_clk; ++ uint32_t idle_n_clk; ++ uint32_t ddr_dll_power_up_time; ++ uint32_t ddr_pll_power_up_time; ++ /* start for V6 */ ++ uint32_t pcie_clk_ss_type; ++ uint32_t lvds_ss_percentage; ++ uint32_t lvds_sspread_rate_in_10hz; ++ uint32_t hdmi_ss_percentage; ++ uint32_t hdmi_sspread_rate_in_10hz; ++ uint32_t dvi_ss_percentage; ++ uint32_t dvi_sspread_rate_in_10_hz; ++ uint32_t sclk_dpm_boost_margin; ++ uint32_t sclk_dpm_throttle_margin; ++ uint32_t sclk_dpm_tdp_limit_pg; ++ uint32_t sclk_dpm_tdp_limit_boost; ++ uint32_t boost_engine_clock; ++ uint32_t boost_vid_2bit; ++ uint32_t enable_boost; ++ uint32_t gnb_tdp_limit; ++ /* Start from V7 */ ++ uint32_t max_lvds_pclk_freq_in_single_link; ++ uint32_t lvds_misc; ++ uint32_t lvds_pwr_on_seq_dig_on_to_de_in_4ms; ++ uint32_t lvds_pwr_on_seq_de_to_vary_bl_in_4ms; ++ uint32_t lvds_pwr_off_seq_vary_bl_to_de_in4ms; ++ uint32_t lvds_pwr_off_seq_de_to_dig_on_in4ms; ++ uint32_t lvds_off_to_on_delay_in_4ms; ++ uint32_t lvds_pwr_on_seq_vary_bl_to_blon_in_4ms; ++ uint32_t lvds_pwr_off_seq_blon_to_vary_bl_in_4ms; ++ uint32_t lvds_reserved1; ++ uint32_t lvds_bit_depth_control_val; ++ //Start from V9 ++ unsigned char dp0_ext_hdmi_slv_addr; ++ unsigned char dp0_ext_hdmi_reg_num; ++ struct i2c_reg_info dp0_ext_hdmi_reg_settings[9]; ++ unsigned char dp0_ext_hdmi_6g_reg_num; ++ struct i2c_reg_info dp0_ext_hdmi_6g_reg_settings[3]; ++ unsigned char dp1_ext_hdmi_slv_addr; ++ unsigned char dp1_ext_hdmi_reg_num; ++ struct i2c_reg_info dp1_ext_hdmi_reg_settings[9]; ++ unsigned char dp1_ext_hdmi_6g_reg_num; ++ struct i2c_reg_info dp1_ext_hdmi_6g_reg_settings[3]; ++ unsigned char dp2_ext_hdmi_slv_addr; ++ unsigned char dp2_ext_hdmi_reg_num; ++ struct i2c_reg_info dp2_ext_hdmi_reg_settings[9]; ++ unsigned char dp2_ext_hdmi_6g_reg_num; ++ struct i2c_reg_info dp2_ext_hdmi_6g_reg_settings[3]; ++ unsigned char dp3_ext_hdmi_slv_addr; ++ unsigned char dp3_ext_hdmi_reg_num; ++ struct i2c_reg_info dp3_ext_hdmi_reg_settings[9]; ++ unsigned char dp3_ext_hdmi_6g_reg_num; ++ struct i2c_reg_info dp3_ext_hdmi_6g_reg_settings[3]; ++ /* V11 */ ++ uint32_t dp_ss_control; ++ /* V2.1 */ ++ struct edp_info edp1_info; ++ struct edp_info edp2_info; ++}; + +- uint8_t sense = getSenseID(baseAddr + connectorObjects[i].usRecordOffset); +- if (!sense) { +- DBGLOG("rad", "autocorrectConnectors failed to detect sense for %u connector", i); +- continue; +- } ++struct dc_bios { ++ void *funcs; + +- DBGLOG("rad", "autocorrectConnectors found txmit %02X enc %02X sense %02X for %u connector", txmit, enc, sense, i); ++ uint8_t *bios; ++ uint32_t bios_size; + +- autocorrectConnector(getConnectorID(displayPaths[i].usConnObjectId), sense, txmit, enc, connectors, sz); +- } +-} ++ uint8_t *bios_local_image; + +-void RAD::autocorrectConnector(uint8_t connector, uint8_t sense, uint8_t txmit, uint8_t enc, RADConnectors::Connector *connectors, uint8_t sz) { +- // This function attempts to fix the following issues: +- // +- // 1. Incompatible DVI transmitter on 290X, 370 and probably some other models +- // In this case a correct transmitter is detected by AtiAtomBiosDce60::getPropertiesForEncoderObject, however, later +- // in AtiAtomBiosDce60::getPropertiesForConnectorObject for DVI DL and TITFP513 this value is conjuncted with 0xCF, +- // which makes it wrong: 0x10 -> 0, 0x11 -> 1. As a result one gets black screen when connecting multiple displays. +- // getPropertiesForEncoderObject takes usGraphicObjIds and getPropertiesForConnectorObject takes usConnObjectId +- +- if (callbackRAD->dviSingleLink) { +- if (connector != CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I && +- connector != CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D && +- connector != CONNECTOR_OBJECT_ID_LVDS) { +- DBGLOG("rad", "autocorrectConnector found unsupported connector type %02X", connector); +- return; +- } ++ void *ctx; ++ void *regs; ++ struct integrated_info *integrated_info; ++ struct dc_firmware_info fw_info; ++ bool fw_info_valid; ++ struct dc_vram_info vram_info; ++ struct dc_golden_table golden_table; ++}; + +- auto fixTransmit = [](auto &con, uint8_t idx, uint8_t sense, uint8_t txmit) { +- if (con.sense == sense) { +- if (con.transmitter != txmit && (con.transmitter & 0xCF) == con.transmitter) { +- DBGLOG("rad", "autocorrectConnector replacing txmit %02X with %02X for %u connector sense %02X", +- con.transmitter, txmit, idx, sense); +- con.transmitter = txmit; +- } +- return true; +- } +- return false; +- }; ++struct dmcu_version { ++ unsigned int interface_version; ++ unsigned int abm_version; ++ unsigned int psr_version; ++ unsigned int build_version; ++}; + +- bool isModern = RADConnectors::modern(); +- for (uint8_t j = 0; j < sz; j++) { +- if (isModern) { +- auto &con = (&connectors->modern)[j]; +- if (fixTransmit(con, j, sense, txmit)) +- break; +- } else { +- auto &con = (&connectors->legacy)[j]; +- if (fixTransmit(con, j, sense, txmit)) +- break; +- } +- } +- } else { +- DBGLOG("rad", "autocorrectConnector use -raddvi to enable dvi autocorrection"); +- } ++struct dc_versions { ++ const char *dc_ver; ++ struct dmcu_version dmcu_version; ++}; ++ ++#define MAX_PLANES 6 ++ ++struct dc_plane_cap { ++ uint32_t type; ++ uint32_t blends_with_above : 1; ++ uint32_t blends_with_below : 1; ++ uint32_t per_pixel_alpha : 1; ++ struct { ++ uint32_t argb8888 : 1; ++ uint32_t nv12 : 1; ++ uint32_t fp16 : 1; ++ uint32_t p010 : 1; ++ uint32_t ayuv : 1; ++ } pixel_format_support; ++ // max upscaling factor x1000 ++ // upscaling factors are always >= 1 ++ // for example, 1080p -> 8K is 4.0, or 4000 raw value ++ struct { ++ uint32_t argb8888; ++ uint32_t nv12; ++ uint32_t fp16; ++ } max_upscale_factor; ++ // max downscale factor x1000 ++ // downscale factors are always <= 1 ++ // for example, 8K -> 1080p is 0.25, or 250 raw value ++ struct { ++ uint32_t argb8888; ++ uint32_t nv12; ++ uint32_t fp16; ++ } max_downscale_factor; ++ // minimal width/height ++ uint32_t min_width; ++ uint32_t min_height; ++}; ++ ++struct rom_curve_caps { ++ uint16_t srgb : 1; ++ uint16_t bt2020 : 1; ++ uint16_t gamma2_2 : 1; ++ uint16_t pq : 1; ++ uint16_t hlg : 1; ++}; ++ ++struct dpp_color_caps { ++ uint16_t dcn_arch : 1; // all DCE generations treated the same ++ // input lut is different than most LUTs, just plain 256-entry lookup ++ uint16_t input_lut_shared : 1; // shared with DGAM ++ uint16_t icsc : 1; ++ uint16_t dgam_ram : 1; ++ uint16_t post_csc : 1; // before gamut remap ++ uint16_t gamma_corr : 1; ++ ++ // hdr_mult and gamut remap always available in DPP (in that order) ++ // 3d lut implies shaper LUT, ++ // it may be shared with MPC - check MPC:shared_3d_lut flag ++ uint16_t hw_3d_lut : 1; ++ uint16_t ogam_ram : 1; // blnd gam ++ uint16_t ocsc : 1; ++ uint16_t dgam_rom_for_yuv : 1; ++ struct rom_curve_caps dgam_rom_caps; ++ struct rom_curve_caps ogam_rom_caps; ++}; ++ ++struct mpc_color_caps { ++ uint16_t gamut_remap : 1; ++ uint16_t ogam_ram : 1; ++ uint16_t ocsc : 1; ++ uint16_t num_3dluts : 3; //3d lut always assumes a preceding shaper LUT ++ uint16_t shared_3d_lut:1; //can be in either DPP or MPC, but single instance ++ ++ struct rom_curve_caps ogam_rom_caps; ++}; ++ ++struct dc_color_caps { ++ struct dpp_color_caps dpp; ++ struct mpc_color_caps mpc; ++}; ++ ++struct dc_caps { ++ uint32_t max_streams; ++ uint32_t max_links; ++ uint32_t max_audios; ++ uint32_t max_slave_planes; ++ uint32_t max_slave_yuv_planes; ++ uint32_t max_slave_rgb_planes; ++ uint32_t max_planes; ++ uint32_t max_downscale_ratio; ++ uint32_t i2c_speed_in_khz; ++ uint32_t i2c_speed_in_khz_hdcp; ++ uint32_t dmdata_alloc_size; ++ unsigned int max_cursor_size; ++ unsigned int max_video_width; ++ unsigned int min_horizontal_blanking_period; ++ int linear_pitch_alignment; ++ bool dcc_const_color; ++ bool dynamic_audio; ++ bool is_apu; ++ bool dual_link_dvi; ++ bool post_blend_color_processing; ++ bool force_dp_tps4_for_cp2520; ++ bool disable_dp_clk_share; ++ bool psp_setup_panel_mode; ++ bool extended_aux_timeout_support; ++ bool dmcub_support; ++ uint32_t num_of_internal_disp; ++ uint32_t max_dp_protocol_version; ++ unsigned int mall_size_per_mem_channel; ++ unsigned int mall_size_total; ++ unsigned int cursor_cache_size; ++ struct dc_plane_cap planes[MAX_PLANES]; ++ struct dc_color_caps color; ++ bool vbios_lttpr_aware; ++ bool vbios_lttpr_enable; ++}; ++ ++struct dc_cap_funcs { ++ bool (*get_dcc_compression_cap)(const struct dc *dc, ++ const struct dc_dcc_surface_param *input, ++ struct dc_surface_dcc_cap *output); ++}; ++ ++struct dc_config { ++ bool gpu_vm_support; ++ bool disable_disp_pll_sharing; ++ bool fbc_support; ++ bool disable_fractional_pwm; ++ bool allow_seamless_boot_optimization; ++ bool power_down_display_on_boot; ++ bool edp_not_connected; ++ bool edp_no_power_sequencing; ++ bool force_enum_edp; ++ bool forced_clocks; ++ bool allow_lttpr_non_transparent_mode; ++ bool multi_mon_pp_mclk_switch; ++ bool disable_dmcu; ++ bool enable_4to1MPC; ++ bool allow_edp_hotplug_detection; ++//#if defined(CONFIG_DRM_AMD_DC_DCN) ++// bool clamp_min_dcfclk; ++//#endif ++ uint64_t vblank_alignment_dto_params; ++ uint8_t vblank_alignment_max_frame_time_diff; ++ bool is_asymmetric_memory; ++ bool is_single_rank_dimm; ++}; ++ ++struct dc_bw_validation_profile { ++ bool enable; ++ ++ unsigned long long total_ticks; ++ unsigned long long voltage_level_ticks; ++ unsigned long long watermark_ticks; ++ unsigned long long rq_dlg_ticks; ++ ++ unsigned long long total_count; ++ unsigned long long skip_fast_count; ++ unsigned long long skip_pass_count; ++ unsigned long long skip_fail_count; ++}; ++ ++union mem_low_power_enable_options { ++ struct { ++ bool vga: 1; ++ bool i2c: 1; ++ bool dmcu: 1; ++ bool dscl: 1; ++ bool cm: 1; ++ bool mpc: 1; ++ bool optc: 1; ++ } bits; ++ uint32_t u32All; ++}; ++ ++struct dc_debug_options { ++ uint32_t visual_confirm; ++ bool sanity_checks; ++ bool max_disp_clk; ++ bool surface_trace; ++ bool timing_trace; ++ bool clock_trace; ++ bool validation_trace; ++ bool bandwidth_calcs_trace; ++ int max_downscale_src_width; ++ ++ /* stutter efficiency related */ ++ bool disable_stutter; ++ bool use_max_lb; ++ uint32_t disable_dcc; ++ uint32_t pipe_split_policy; ++ bool force_single_disp_pipe_split; ++ bool voltage_align_fclk; ++ bool disable_min_fclk; ++ ++ bool disable_dfs_bypass; ++ bool disable_dpp_power_gate; ++ bool disable_hubp_power_gate; ++ bool disable_dsc_power_gate; ++ int dsc_min_slice_height_override; ++ int dsc_bpp_increment_div; ++ bool native422_support; ++ bool disable_pplib_wm_range; ++ uint32_t pplib_wm_report_mode; ++ unsigned int min_disp_clk_khz; ++ unsigned int min_dpp_clk_khz; ++ int sr_exit_time_dpm0_ns; ++ int sr_enter_plus_exit_time_dpm0_ns; ++ int sr_exit_time_ns; ++ int sr_enter_plus_exit_time_ns; ++ int urgent_latency_ns; ++ uint32_t underflow_assert_delay_us; ++ int percent_of_ideal_drambw; ++ int dram_clock_change_latency_ns; ++ bool optimized_watermark; ++ int always_scale; ++ bool disable_pplib_clock_request; ++ bool disable_clock_gate; ++ bool disable_mem_low_power; ++//#if defined(CONFIG_DRM_AMD_DC_DCN) ++// bool pstate_enabled; ++//#endif ++ bool disable_dmcu; ++ bool disable_psr; ++ bool force_abm_enable; ++ bool disable_stereo_support; ++ bool vsr_support; ++ bool performance_trace; ++ bool az_endpoint_mute_only; ++ bool always_use_regamma; ++ bool recovery_enabled; ++ bool avoid_vbios_exec_table; ++ bool scl_reset_length10; ++ bool hdmi20_disable; ++ bool skip_detection_link_training; ++ uint32_t edid_read_retry_times; ++ bool remove_disconnect_edp; ++ unsigned int force_odm_combine; //bit vector based on otg inst ++//#if defined(CONFIG_DRM_AMD_DC_DCN) ++// unsigned int force_odm_combine_4to1; //bit vector based on otg inst ++// bool disable_z9_mpc; ++//#endif ++ unsigned int force_fclk_khz; ++ bool enable_tri_buf; ++ bool dmub_offload_enabled; ++ bool dmcub_emulation; ++//#if defined(CONFIG_DRM_AMD_DC_DCN) ++// bool disable_idle_power_optimizations; ++// unsigned int mall_size_override; ++// unsigned int mall_additional_timer_percent; ++// bool mall_error_as_fatal; ++//#endif ++ bool dmub_command_table; /* for testing only */ ++ struct dc_bw_validation_profile bw_val_profile; ++ bool disable_fec; ++ bool disable_48mhz_pwrdwn; ++ /* This forces a hard min on the DCFCLK requested to SMU/PP ++ * watermarks are not affected. ++ */ ++ unsigned int force_min_dcfclk_mhz; ++//#if defined(CONFIG_DRM_AMD_DC_DCN) ++// int dwb_fi_phase; ++//#endif ++ bool disable_timing_sync; ++ bool cm_in_bypass; ++ int force_clock_mode;/*every mode change.*/ ++ ++ bool disable_dram_clock_change_vactive_support; ++ bool validate_dml_output; ++ bool enable_dmcub_surface_flip; ++ bool usbc_combo_phy_reset_wa; ++ bool disable_dsc; ++ bool enable_dram_clock_change_one_display_vactive; ++ union mem_low_power_enable_options enable_mem_low_power; ++ bool force_vblank_alignment; ++ ++ /* Enable dmub aux for legacy ddc */ ++ bool enable_dmub_aux_for_legacy_ddc; ++ bool optimize_edp_link_rate; /* eDP ILR */ ++ /* force enable edp FEC */ ++ bool force_enable_edp_fec; ++ /* FEC/PSR1 sequence enable delay in 100us */ ++ uint8_t fec_enable_delay_in100us; ++//#if defined(CONFIG_DRM_AMD_DC_DCN) ++// bool disable_z10; ++// bool enable_sw_cntl_psr; ++//#endif ++}; ++ ++struct dc_bounding_box_overrides { ++ int sr_exit_time_ns; ++ int sr_enter_plus_exit_time_ns; ++ int urgent_latency_ns; ++ int percent_of_ideal_drambw; ++ int dram_clock_change_latency_ns; ++ int dummy_clock_change_latency_ns; ++ /* This forces a hard min on the DCFCLK we use ++ * for DML. Unlike the debug option for forcing ++ * DCFCLK, this override affects watermark calculations ++ */ ++ int min_dcfclk_mhz; ++}; ++ ++struct dc_bug_wa { ++ bool no_connect_phy_config; ++ bool dedcn20_305_wa; ++ bool skip_clock_update; ++ bool lt_early_cr_pattern; ++}; ++ ++struct dc { ++ struct dc_versions versions; ++ struct dc_caps caps; ++ struct dc_cap_funcs cap_funcs; ++ struct dc_config config; ++ struct dc_debug_options debug; ++ struct dc_bounding_box_overrides bb_overrides; ++ struct dc_bug_wa work_arounds; ++ /* ++ struct dc_context *ctx; ++ struct dc_phy_addr_space_config vm_pa_config; ++ ++ uint8_t link_count; ++ struct dc_link *links[MAX_PIPES * 2]; ++ ++ struct dc_state *current_state; ++ struct resource_pool *res_pool; ++ ++ struct clk_mgr *clk_mgr; ++ ++ // Display Engine Clock levels ++ struct dm_pp_clock_levels sclk_lvls; ++ ++ // Inputs into BW and WM calculations. ++ struct bw_calcs_dceip *bw_dceip; ++ struct bw_calcs_vbios *bw_vbios; ++#ifdef CONFIG_DRM_AMD_DC_DCN ++ struct dcn_soc_bounding_box *dcn_soc; ++ struct dcn_ip_params *dcn_ip; ++ struct display_mode_lib dml; ++#endif ++ ++ // HW functions ++ struct hw_sequencer_funcs hwss; ++ struct dce_hwseq *hwseq; ++ ++ // Require to optimize clocks and bandwidth for added/removed planes ++ bool optimized_required; ++ bool wm_optimized_required; ++#if defined(CONFIG_DRM_AMD_DC_DCN) ++ bool idle_optimizations_allowed; ++#endif ++ ++ // Require to maintain clocks and bandwidth for UEFI enabled HW ++ ++ // FBC compressor ++ struct compressor *fbc_compressor; ++ ++ struct dc_debug_data debug_data; ++ struct dpcd_vendor_signature vendor_signature; ++ ++ const char *build_id; ++ struct vm_helper *vm_helper; ++ */ ++}; ++ ++struct dc_context { ++ struct dc *dc; ++ ++ void *driver_context; /* e.g. amdgpu_device */ ++ void *perf_trace; ++ void *cgs_device; ++ ++ uint32_t dce_environment; ++ struct hw_asic_id asic_id; ++ ++ /* todo: below should probably move to dc. to facilitate removal ++ * of AS we will store these here ++ */ ++ uint32_t dce_version; ++ struct dc_bios *dc_bios; ++ bool created_bios; ++ void *gpio_service; ++ uint32_t dc_sink_id_count; ++ uint32_t dc_stream_id_count; ++ uint32_t dc_edp_id_count; ++ uint64_t fbc_gpu_addr; ++ void *dmub_srv; ++}; ++ ++struct panel_cntl_funcs { ++ void (*destroy)(struct panel_cntl **panel_cntl); ++ uint32_t (*hw_init)(struct panel_cntl *panel_cntl); ++ bool (*is_panel_backlight_on)(struct panel_cntl *panel_cntl); ++ bool (*is_panel_powered_on)(struct panel_cntl *panel_cntl); ++ void (*store_backlight_level)(struct panel_cntl *panel_cntl); ++ void (*driver_set_backlight)(struct panel_cntl *panel_cntl, ++ uint32_t backlight_pwm_u16_16); ++ uint32_t (*get_current_backlight)(struct panel_cntl *panel_cntl); ++}; ++struct panel_cntl { ++ const struct panel_cntl_funcs *funcs; ++ struct dc_context *ctx; ++ uint32_t inst; ++ /* registers setting needs to be saved and restored at InitBacklight */ ++ struct panel_cntl_backlight_registers stored_backlight_registers; ++}; ++bool g_is_sleep = false; ++uint64_t g_last_sleep_time = 0; ++static my_dc_link_t* g_dc_link_ptr = NULL; ++static mach_vm_address_t orig_dce110_edp_backlight_control; ++ ++static mach_vm_address_t orig_dce110_edp_power_control; ++static void wrap_dce110_edp_power_control(my_dc_link_t *dc_link, bool power_up) { ++ g_dc_link_ptr = dc_link; ++ if (orig_dce110_edp_power_control) { ++ FunctionCast(wrap_dce110_edp_power_control, orig_dce110_edp_power_control)(dc_link, power_up); ++ } ++ SYSLOG("igfx", "wrap_dce110_edp_power_control %d end - %p", power_up, g_dc_link_ptr); + } + +-void RAD::reprioritiseConnectors(const uint8_t *senseList, uint8_t senseNum, RADConnectors::Connector *connectors, uint8_t sz) { +- static constexpr uint32_t typeList[] { +- RADConnectors::ConnectorLVDS, +- RADConnectors::ConnectorDigitalDVI, +- RADConnectors::ConnectorHDMI, +- RADConnectors::ConnectorDP, +- RADConnectors::ConnectorVGA +- }; +- static constexpr uint8_t typeNum {static_cast(arrsize(typeList))}; +- +- bool isModern = RADConnectors::modern(); +- uint16_t priCount = 1; +- // Automatically detected connectors have equal priority (0), which often results in black screen +- // This allows to change this firstly by user-defined list, then by type list. +- //TODO: priority is ignored for 5xxx and 6xxx GPUs, should we manually reorder items? +- for (uint8_t i = 0; i < senseNum + typeNum + 1; i++) { +- for (uint8_t j = 0; j < sz; j++) { +- auto reorder = [&](auto &con) { +- if (i == senseNum + typeNum) { +- if (con.priority == 0) +- con.priority = priCount++; +- } else if (i < senseNum) { +- if (con.sense == senseList[i]) { +- DBGLOG("rad", "reprioritiseConnectors setting priority of sense %02X to %u by sense", con.sense, priCount); +- con.priority = priCount++; +- return true; +- } +- } else { +- if (con.priority == 0 && con.type == typeList[i-senseNum]) { +- DBGLOG("rad", "reprioritiseConnectors setting priority of sense %02X to %u by type", con.sense, priCount); +- con.priority = priCount++; +- } +- } +- return false; +- }; +- +- if ((isModern && reorder((&connectors->modern)[j])) || +- (!isModern && reorder((&connectors->legacy)[j]))) +- break; +- } +- } ++static char wrap_dce110_edp_backlight_control(my_dc_link_t *that, uint8_t a2) { ++ //SYSLOG("igfx", "wrap_dce110_edp_backlight_control start %p:%lu,%lu,%lu,%lu,%lu", that, on_or_off, a3, a4, a5, a6); ++ g_dc_link_ptr = that; ++ //if (g_dc_link_ptr && g_dc_link_ptr->dc) { ++ // SYSLOG("igfx", "dce110_edp_backlight_control disable_fractional_pwm: %u", g_dc_link_ptr->dc->config.disable_fractional_pwm); ++ //} ++ char ret = FunctionCast(wrap_dce110_edp_backlight_control, orig_dce110_edp_backlight_control)(that, a2); ++ SYSLOG("igfx", "wrap_dce110_edp_backlight_control end - %p, %d", g_dc_link_ptr, ret); ++ ++ if (g_is_sleep) { ++ g_is_sleep = false; ++ ++ if (g_dc_link_ptr) { ++ wrap_dce110_edp_backlight_control(g_dc_link_ptr, false); ++ wrap_dce110_edp_power_control(g_dc_link_ptr, false); ++ IOSleep(200); ++ wrap_dce110_edp_power_control(g_dc_link_ptr, true); ++ wrap_dce110_edp_backlight_control(g_dc_link_ptr, true); ++ } ++ } ++ ++ return ret; + } + +-void RAD::setGvaProperties(IOService *accelService) { +- auto codecStr = OSDynamicCast(OSString, accelService->getProperty("IOGVACodec")); +- if (codecStr == nullptr) { +- DBGLOG("rad", "updating X4000 accelerator IOGVACodec to VCE"); +- accelService->setProperty("IOGVACodec", "VCE"); +- } else { +- auto codec = codecStr->getCStringNoCopy(); +- DBGLOG("rad", "X4000 accelerator IOGVACodec is already set to %s", safeString(codec)); +- if (codec != nullptr && strncmp(codec, "AMD", strlen("AMD")) == 0) { +- bool needsDecode = accelService->getProperty("IOGVAHEVCDecode") == nullptr; +- bool needsEncode = accelService->getProperty("IOGVAHEVCEncode") == nullptr; +- if (needsDecode) { +- OSObject *VTMaxDecodeLevel = OSNumber::withNumber(153, 32); +- OSString *VTMaxDecodeLevelKey = OSString::withCString("VTMaxDecodeLevel"); +- OSDictionary *VTPerProfileDetailsInner = OSDictionary::withCapacity(1); +- OSDictionary *VTPerProfileDetails = OSDictionary::withCapacity(3); +- OSString *VTPerProfileDetailsKey1 = OSString::withCString("1"); +- OSString *VTPerProfileDetailsKey2 = OSString::withCString("2"); +- OSString *VTPerProfileDetailsKey3 = OSString::withCString("3"); +- +- OSArray *VTSupportedProfileArray = OSArray::withCapacity(3); +- OSNumber *VTSupportedProfileArray1 = OSNumber::withNumber(1, 32); +- OSNumber *VTSupportedProfileArray2 = OSNumber::withNumber(2, 32); +- OSNumber *VTSupportedProfileArray3 = OSNumber::withNumber(3, 32); +- +- OSDictionary *IOGVAHEVCDecodeCapabilities = OSDictionary::withCapacity(2); +- OSString *VTPerProfileDetailsKey = OSString::withCString("VTPerProfileDetails"); +- OSString *VTSupportedProfileArrayKey = OSString::withCString("VTSupportedProfileArray"); +- +- if (VTMaxDecodeLevel != nullptr && VTMaxDecodeLevelKey != nullptr && VTPerProfileDetailsInner != nullptr && +- VTPerProfileDetails != nullptr && VTPerProfileDetailsKey1 != nullptr && VTPerProfileDetailsKey2 != nullptr && +- VTPerProfileDetailsKey3 != nullptr && VTSupportedProfileArrayKey != nullptr && VTSupportedProfileArray1 != nullptr && +- VTSupportedProfileArray2 != nullptr && VTSupportedProfileArray3 != nullptr && VTSupportedProfileArray != nullptr && +- VTPerProfileDetailsKey != nullptr && IOGVAHEVCDecodeCapabilities != nullptr) { +- VTPerProfileDetailsInner->setObject(VTMaxDecodeLevelKey, VTMaxDecodeLevel); +- VTPerProfileDetails->setObject(VTPerProfileDetailsKey1, VTPerProfileDetailsInner); +- VTPerProfileDetails->setObject(VTPerProfileDetailsKey2, VTPerProfileDetailsInner); +- VTPerProfileDetails->setObject(VTPerProfileDetailsKey3, VTPerProfileDetailsInner); +- +- VTSupportedProfileArray->setObject(VTSupportedProfileArray1); +- VTSupportedProfileArray->setObject(VTSupportedProfileArray2); +- VTSupportedProfileArray->setObject(VTSupportedProfileArray3); +- +- IOGVAHEVCDecodeCapabilities->setObject(VTPerProfileDetailsKey, VTPerProfileDetails); +- IOGVAHEVCDecodeCapabilities->setObject(VTSupportedProfileArrayKey, VTSupportedProfileArray); +- +- accelService->setProperty("IOGVAHEVCDecode", "1"); +- accelService->setProperty("IOGVAHEVCDecodeCapabilities", IOGVAHEVCDecodeCapabilities); +- +- DBGLOG("rad", "recovering IOGVAHEVCDecode"); +- } else { +- SYSLOG("rad", "allocation failure in IOGVAHEVCDecode"); +- } +- +- OSSafeReleaseNULL(VTMaxDecodeLevel); +- OSSafeReleaseNULL(VTMaxDecodeLevelKey); +- OSSafeReleaseNULL(VTPerProfileDetailsInner); +- OSSafeReleaseNULL(VTPerProfileDetails); +- OSSafeReleaseNULL(VTPerProfileDetailsKey1); +- OSSafeReleaseNULL(VTPerProfileDetailsKey2); +- OSSafeReleaseNULL(VTPerProfileDetailsKey3); +- OSSafeReleaseNULL(VTSupportedProfileArrayKey); +- OSSafeReleaseNULL(VTSupportedProfileArray1); +- OSSafeReleaseNULL(VTSupportedProfileArray2); +- OSSafeReleaseNULL(VTSupportedProfileArray3); +- OSSafeReleaseNULL(VTSupportedProfileArray); +- OSSafeReleaseNULL(VTPerProfileDetailsKey); +- OSSafeReleaseNULL(IOGVAHEVCDecodeCapabilities); +- } ++IOReturn RAD::wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute(IOService *framebuffer, IOIndex connectIndex, IOSelect attribute, uintptr_t value) { ++ IOReturn ret = FunctionCast(wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute, callbackRAD->orgAMDRadeonX6000AmdRadeonFramebufferSetAttribute)(framebuffer, connectIndex, attribute, value); ++ if (attribute != (UInt32)'bklt') { ++ return ret; ++ } ++ ++ if (callbackRAD->maxPwmBacklightLvl == 0) { ++ DBGLOG("igfx", "wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute zero maxPwmBacklightLvl"); ++ return 0; ++ } ++ ++ if (callbackRAD->panelCntlPtr == nullptr) { ++ DBGLOG("igfx", "wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute null panel cntl"); ++ return 0; ++ } ++ ++ if (callbackRAD->orgDceDriverSetBacklight == nullptr) { ++ DBGLOG("igfx", "wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute null orgDcLinkSetBacklightLevel"); ++ return 0; ++ } ++ ++ // set the backlight of AMD navi10 driver ++ callbackRAD->curPwmBacklightLvl = (uint32_t)value; ++ uint32_t btlper = callbackRAD->curPwmBacklightLvl*100.0 / callbackRAD->maxPwmBacklightLvl; ++ if (btlper < 0) { ++ btlper = 0; ++ } else if (btlper > 100) { ++ btlper = 100; ++ } ++ ++ int pwmval = (int)((btlper / 100.0) * 0xFF) << 8; ++ if (pwmval >= 0xFF00) { ++ // This is from the dmcu_set_backlight_level function of Linux source ++ // ... ++ // if (backlight_pwm_u16_16 & 0x10000) ++ // backlight_8_bit = 0xFF; ++ // else ++ // backlight_8_bit = (backlight_pwm_u16_16 >> 8) & 0xFF; ++ // ... ++ // The max brightness should have 0x10000 bit set ++ pwmval = 0x1FF00; ++ } ++ ++ /*if (g_dc_link_ptr) { ++ __int64 v10 = 0; ++ __int64 v9 = *(_QWORD *)(*(_QWORD *)((__int64)g_dc_link_ptr + 304) + 944LL); ++ while ( true ) { ++ __int64 stream = *(_QWORD *)(v9 + v10 + 496); ++ if ( stream ) { ++ if (*(_QWORD *)(stream + 8) == (__int64)g_dc_link_ptr) { ++ __int64 pipe_ctx = (v9 + v10 + 488); ++ //SYSLOG("igfx", "my_set_backlight_lvl %p:%p, %d, %d", g_dc_link_ptr, pipe_ctx, backlight_pwm_u16_16, ramp); ++ _QWORD *a1 = (_QWORD *)pipe_ctx; ++ __int64 v5 = *(_QWORD *)(a1[1] + 8LL); ++ __int64 v6 = *(_QWORD *)(v5 + 320); ++ struct panel_cntl *panel_cntl = (struct panel_cntl *)v6; ++ SYSLOG("igfx", "panel_cntl: %p", panel_cntl); ++ break; ++ } else { ++ SYSLOG("igfx", "steam link %lu != %p", *(_QWORD *)(stream + 8), g_dc_link_ptr); ++ } ++ } ++ v10 += 1280LL; ++ if ( v10 >= 7680 ) ++ break; ++ } ++ } else { ++ DBGLOG("igfx", "null g_dc_link_ptr"); ++ }*/ ++ ++ struct panel_cntl* ppc = (struct panel_cntl*)callbackRAD->panelCntlPtr; ++ DBGLOG("igfx", "fb 0x%p, idx: %d, set brightness: 0x%p -> 0x%x, st: %d", framebuffer, connectIndex, callbackRAD->panelCntlPtr, pwmval, ppc->funcs->is_panel_powered_on(ppc)); ++ callbackRAD->orgDceDriverSetBacklight(callbackRAD->panelCntlPtr, pwmval); ++ return 0; ++} + +- if (needsEncode) { +- OSObject *VTMaxEncodeLevel = OSNumber::withNumber(153, 32); +- OSString *VTMaxEncodeLevelKey = OSString::withCString("VTMaxEncodeLevel"); +- +- OSDictionary *VTPerProfileDetailsInner = OSDictionary::withCapacity(1); +- OSDictionary *VTPerProfileDetails = OSDictionary::withCapacity(1); +- OSString *VTPerProfileDetailsKey1 = OSString::withCString("1"); +- +- OSArray *VTSupportedProfileArray = OSArray::withCapacity(1); +- OSNumber *VTSupportedProfileArray1 = OSNumber::withNumber(1, 32); +- +- OSDictionary *IOGVAHEVCEncodeCapabilities = OSDictionary::withCapacity(4); +- OSString *VTPerProfileDetailsKey = OSString::withCString("VTPerProfileDetails"); +- OSString *VTQualityRatingKey = OSString::withCString("VTQualityRating"); +- OSNumber *VTQualityRating = OSNumber::withNumber(50, 32); +- OSString *VTRatingKey = OSString::withCString("VTRating"); +- OSNumber *VTRating = OSNumber::withNumber(350, 32); +- OSString *VTSupportedProfileArrayKey = OSString::withCString("VTSupportedProfileArray"); +- +- if (VTMaxEncodeLevel != nullptr && VTMaxEncodeLevelKey != nullptr && VTPerProfileDetailsInner != nullptr && +- VTPerProfileDetails != nullptr && VTPerProfileDetailsKey1 != nullptr && VTSupportedProfileArrayKey != nullptr && +- VTSupportedProfileArray1 != nullptr && VTSupportedProfileArray != nullptr && VTPerProfileDetailsKey != nullptr && +- VTQualityRatingKey != nullptr && VTQualityRating != nullptr && VTRatingKey != nullptr && VTRating != nullptr && +- IOGVAHEVCEncodeCapabilities != nullptr) { +- +- VTPerProfileDetailsInner->setObject(VTMaxEncodeLevelKey, VTMaxEncodeLevel); +- VTPerProfileDetails->setObject(VTPerProfileDetailsKey1, VTPerProfileDetailsInner); +- VTSupportedProfileArray->setObject(VTSupportedProfileArray1); +- +- IOGVAHEVCEncodeCapabilities->setObject(VTPerProfileDetailsKey, VTPerProfileDetails); +- IOGVAHEVCEncodeCapabilities->setObject(VTQualityRatingKey, VTQualityRating); +- IOGVAHEVCEncodeCapabilities->setObject(VTRatingKey, VTRating); +- IOGVAHEVCEncodeCapabilities->setObject(VTSupportedProfileArrayKey, VTSupportedProfileArray); +- +- accelService->setProperty("IOGVAHEVCEncode", "1"); +- accelService->setProperty("IOGVAHEVCEncodeCapabilities", IOGVAHEVCEncodeCapabilities); +- +- DBGLOG("rad", "recovering IOGVAHEVCEncode"); +- } else { +- SYSLOG("rad", "allocation failure in IOGVAHEVCEncode"); +- } +- +- OSSafeReleaseNULL(VTMaxEncodeLevel); +- OSSafeReleaseNULL(VTMaxEncodeLevelKey); +- OSSafeReleaseNULL(VTPerProfileDetailsInner); +- OSSafeReleaseNULL(VTPerProfileDetails); +- OSSafeReleaseNULL(VTPerProfileDetailsKey1); +- OSSafeReleaseNULL(VTSupportedProfileArrayKey); +- OSSafeReleaseNULL(VTSupportedProfileArray1); +- OSSafeReleaseNULL(VTSupportedProfileArray); +- OSSafeReleaseNULL(VTPerProfileDetailsKey); +- OSSafeReleaseNULL(VTQualityRatingKey); +- OSSafeReleaseNULL(VTQualityRating); +- OSSafeReleaseNULL(VTRatingKey); +- OSSafeReleaseNULL(VTRating); +- OSSafeReleaseNULL(IOGVAHEVCEncodeCapabilities); +- } +- } +- } ++uint32_t RAD::wrapDcePanelCntlHwInit(void *panel_cntl) { ++ callbackRAD->panelCntlPtr = panel_cntl; ++ callbackRAD->updatePwmMaxBrightnessFromInternalDisplay(); // read max brightness value from IOReg ++ struct panel_cntl* ppc = (struct panel_cntl*)callbackRAD->panelCntlPtr; ++ DBGLOG("igfx", "wrapDcePanelCntlHwInit: %p, bl: %d, pw: %d, bl: %d", panel_cntl, ppc->funcs->is_panel_backlight_on(ppc), ppc->funcs->is_panel_powered_on(ppc), ppc->funcs->get_current_backlight(ppc)); ++ uint32_t ret = FunctionCast(wrapDcePanelCntlHwInit, callbackRAD->orgDcePanelCntlHwInit)(panel_cntl); ++ //if (ppc->funcs->is_panel_backlight_on(ppc) == 0 && ppc->funcs->is_panel_powered_on(ppc) == 1 && g_dc_link_ptr) { ++ //wrap_dce110_edp_backlight_control(g_dc_link_ptr, 0); ++ //wrap_dce110_edp_power_control(g_dc_link_ptr, false); ++ // g_is_sleep = true; ++ //} ++ DBGLOG("igfx", "wrapDcePanelCntlHwInit: %p - %u, st: %d", panel_cntl, ret, ppc->funcs->is_panel_powered_on(ppc)); ++ return ret; + } + +-void RAD::updateAccelConfig(size_t hwIndex, IOService *accelService, const char **accelConfig) { +- if (accelService && accelConfig) { +- if (fixConfigName) { +- auto gpuService = accelService->getParentEntry(gIOServicePlane); +- +- if (gpuService) { +- auto model = OSDynamicCast(OSData, gpuService->getProperty("model")); +- if (model) { +- auto modelStr = static_cast(model->getBytesNoCopy()); +- if (modelStr) { +- if (modelStr[0] == 'A' && ((modelStr[1] == 'M' && modelStr[2] == 'D') || +- (modelStr[1] == 'T' && modelStr[2] == 'I')) && modelStr[3] == ' ') { +- modelStr += 4; +- } +- +- DBGLOG("rad", "updateAccelConfig found gpu model %s", modelStr); +- *accelConfig = modelStr; +- } else { +- DBGLOG("rad", "updateAccelConfig found null gpu model"); +- } +- } else { +- DBGLOG("rad", "updateAccelConfig failed to find gpu model"); +- } +- +- } else { +- DBGLOG("rad", "updateAccelConfig failed to find accelerator parent"); +- } +- } ++IOReturn RAD::wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute(IOService *framebuffer, IOIndex connectIndex, IOSelect attribute, uintptr_t * value) { ++ IOReturn ret = FunctionCast(wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute, callbackRAD->orgAMDRadeonX6000AmdRadeonFramebufferGetAttribute)(framebuffer, connectIndex, attribute, value); ++ if (attribute == (UInt32)'bklt') { ++ // enable the backlight feature of AMD navi10 driver ++ *value = callbackRAD->curPwmBacklightLvl; ++ ret = 0; ++ } ++ return ret; ++} + +- if (enableGvaSupport && hwIndex == IndexRadeonHardwareX4000) { +- setGvaProperties(accelService); +- } +- } ++static mach_vm_address_t Orig_AMDRadeonX6000_AmdInterruptManager_sleepInterrupts; ++static int64_t Wrap_AMDRadeonX6000_AmdInterruptManager_sleepInterrupts(__int64 a1, __int64 a2) { ++ SYSLOG("igfx", "Wrap_""AMDRadeonX6000_AmdInterruptManager_sleepInterrupts"" start " "%lu:%lu", a1, a2); ++ uint64_t currNs = getCurrentTimeNs(); ++ if (currNs - g_last_sleep_time >= 1000000000) { ++ g_is_sleep = true; ++ g_last_sleep_time = getCurrentTimeNs(); ++ } ++ int64_t ret = FunctionCast(Wrap_AMDRadeonX6000_AmdInterruptManager_sleepInterrupts, Orig_AMDRadeonX6000_AmdInterruptManager_sleepInterrupts)(a1, a2); ++ SYSLOG("igfx", "Wrap_""AMDRadeonX6000_AmdInterruptManager_sleepInterrupts"" end - " "%lu", ret); ++ return ret; + } + +-bool RAD::wrapSetProperty(IORegistryEntry *that, const char *aKey, void *bytes, unsigned length) { +- if (length > 10 && aKey && reinterpret_cast(aKey)[0] == 'edom' && reinterpret_cast(aKey)[2] == 'l') { +- DBGLOG("rad", "SetProperty caught model %u (%.*s)", length, length, static_cast(bytes)); +- if (*static_cast(bytes) == ' DMA' || *static_cast(bytes) == ' ITA' || *static_cast(bytes) == 'edaR') { +- if (FunctionCast(wrapGetProperty, callbackRAD->orgGetProperty)(that, aKey)) { +- DBGLOG("rad", "SetProperty ignored setting %s to %s", aKey, static_cast(bytes)); +- return true; +- } +- DBGLOG("rad", "SetProperty missing %s, fallback to %s", aKey, static_cast(bytes)); +- } +- } ++static mach_vm_address_t Orig_AMDRadeonX6000_AmdLogger_setDebugLevel; ++static int64_t Wrap_AMDRadeonX6000_AmdLogger_setDebugLevel(__int64 a1, int a2) { ++ SYSLOG("igfx", "Wrap_""AMDRadeonX6000_AmdLogger_setDebugLevel"" start " "%lu:%d", a1, a2); ++ a2 = 9999; ++ int64_t ret = FunctionCast(Wrap_AMDRadeonX6000_AmdLogger_setDebugLevel, Orig_AMDRadeonX6000_AmdLogger_setDebugLevel)(a1, a2); ++ SYSLOG("igfx", "Wrap_""AMDRadeonX6000_AmdLogger_setDebugLevel"" end - " "%lu", ret); ++ return ret; ++} + +- return FunctionCast(wrapSetProperty, callbackRAD->orgSetProperty)(that, aKey, bytes, length); ++static mach_vm_address_t Orig_my_dm_logger_write; ++static char g_buffer[81920] = {0}; ++static int64_t Wrap_my_dm_logger_write(int64_t a1, unsigned int a2, const char *a3, ...) { ++ va_list args; ++ va_start(args, a3); ++ vsnprintf(g_buffer, 8192, a3, args); ++ SYSLOG("igfx", "dm log, type: %d, msg: %s", a2, g_buffer); ++ va_end(args); ++ return 0; + } + +-OSObject *RAD::wrapGetProperty(IORegistryEntry *that, const char *aKey) { +- auto obj = FunctionCast(wrapGetProperty, callbackRAD->orgGetProperty)(that, aKey); +- auto props = OSDynamicCast(OSDictionary, obj); +- +- if (props && aKey) { +- const char *prefix {nullptr}; +- auto provider = OSDynamicCast(IOService, that->getParentEntry(gIOServicePlane)); +- if (provider) { +- if (aKey[0] == 'a') { +- if (!strcmp(aKey, "aty_config")) +- prefix = "CFG,"; +- else if (!strcmp(aKey, "aty_properties")) +- prefix = "PP,"; +- } else if (aKey[0] == 'c' && !strcmp(aKey, "cail_properties")) { +- prefix = "CAIL,"; +- } ++bool RAD::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { ++ if (kextRadeonX6000Framebuffer.loadIndex == index) { ++ SYSLOG("igfx", "RAD::processKext, index: %lu, address: %p, size: %lu", index, address, size); ++ if (getKernelVersion() >= KernelVersion::Monterey) { ++ KernelPatcher::RouteRequest requests[] = { ++ {"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25setAttributeForConnectionEijm", wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute, orgAMDRadeonX6000AmdRadeonFramebufferSetAttribute}, ++ {"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25getAttributeForConnectionEijPm", wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute, orgAMDRadeonX6000AmdRadeonFramebufferGetAttribute}, ++ //{"__ZN24AMDRadeonX6000_AmdLogger13setDebugLevelE11LogSeverity", Wrap_AMDRadeonX6000_AmdLogger_setDebugLevel, Orig_AMDRadeonX6000_AmdLogger_setDebugLevel}, ++ //{"__ZN34AMDRadeonX6000_AmdInterruptManager15sleepInterruptsEv", Wrap_AMDRadeonX6000_AmdInterruptManager_sleepInterrupts, Orig_AMDRadeonX6000_AmdInterruptManager_sleepInterrupts}, ++ }; ++ ++ if (!patcher.routeMultiple(index, requests, address, size, true, true)) ++ SYSLOG("igfx", "Failed to route redeon x6000 gpu tracing."); ++ ++ SYSLOG("igfx", "route _dce_panel_cntl_hw_init"); ++ mach_vm_address_t dpchi_cal = address + 0x12CB94; // For Monterey ++ if (getKernelVersion() >= KernelVersion::Ventura) { ++ dpchi_cal = address + 0x12EC5F; // For Ventura ++ } ++ ++ // Verify the expect code of the destinate address, otherwise will not call to prevent kernel panic ++ const uint8_t dpchi_expect[] = { 0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53, 0x50, 0x49, 0x89, 0xFD, 0x4C, 0x8D, 0x45 }; ++ unsigned char* dpchi_cal_charp = (unsigned char*)dpchi_cal; ++ bool expect_match = true; ++ for (int i = 0; i < 20; i++) { ++ //SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); ++ if (dpchi_cal_charp[i] != dpchi_expect[i]) { ++ SYSLOG("igfx", "_dce_panel_cntl_hw_init dismatch: %d => %x != %x", i, dpchi_cal_charp[i], dpchi_expect[i]); ++ expect_match = false; ++ break; ++ } ++ } ++ ++ if (expect_match) { ++ orgDcePanelCntlHwInit = patcher.routeFunction(dpchi_cal, reinterpret_cast(wrapDcePanelCntlHwInit), true); ++ if (patcher.getError() == KernelPatcher::Error::NoError) { ++ DBGLOG("igfx", "routed _dce_panel_cntl_hw_init"); ++ } else { ++ SYSLOG("igfx", "failed to route _dce_panel_cntl_hw_init %d", patcher.getError()); ++ patcher.clearError(); ++ } ++ ++ mach_vm_address_t ddsb_cal = address + 0x12CFC9; ++ if (getKernelVersion() >= KernelVersion::Ventura) { ++ ddsb_cal = address + 0x12F094; ++ } ++ SYSLOG("igfx", "got Monterey _dce_driver_set_backlight address: %p", ddsb_cal); ++ const uint8_t ddsb_expect[] = { 0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53, 0x50, 0x41, 0x89, 0xF7, 0x49, 0x89, 0xFE }; ++ unsigned char* ddsb_cal_charp = (unsigned char*)ddsb_cal; ++ expect_match = true; ++ for (int i = 0; i < 20; i++) { ++ //SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); ++ if (ddsb_cal_charp[i] != ddsb_expect[i]) { ++ SYSLOG("igfx", "_dce_driver_set_backlight dismatch: %d => %x != %x", i, ddsb_cal_charp[i], ddsb_expect[i]); ++ expect_match = false; ++ break; ++ } ++ } ++ ++ //for (int i = 0; i < 0x200000; i++) { ++ // ddsb_cal_charp = (unsigned char*)(address + i); ++ // int j = 0; ++ // for (j = 0; j < 20; j++) { ++ // //SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); ++ // if (ddsb_cal_charp[j] != ddsb_expect[j]) { ++ // //SYSLOG("igfx", "_dce_panel_cntl_hw_init dismatch: %d => %x != %x", i, dpchi_cal_charp[i], dpchi_expect[i]); ++ // //expect_match = false; ++ // break; ++ // } ++ // } ++ // if (j == 20) { ++ // SYSLOG("igfx", "found address %u", i); ++ // } ++ //} ++ ++ if (expect_match) { ++ orgDceDriverSetBacklight = reinterpret_cast(ddsb_cal); ++ } ++ } ++ ++ /*SYSLOG("igfx", "route _dm_logger_write"); ++ mach_vm_address_t dlw_cal = address + 0x169117; ++ const uint8_t dlw_expect[] = { 0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53, 0x48, 0x81, 0xEC, 0x88, 0x04, 0x00, 0x00 }; ++ unsigned char* dlw_cal_charp = (unsigned char*)dlw_cal; ++ expect_match = true; ++ for (int i = 0; i < 20; i++) { ++ //SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); ++ if (dlw_cal_charp[i] != dlw_expect[i]) { ++ SYSLOG("igfx", "_dm_logger_write dismatch: %d => %x != %x", i, dlw_cal_charp[i], dlw_expect[i]); ++ expect_match = false; ++ break; ++ } ++ } ++ if (expect_match) { ++ Orig_my_dm_logger_write = patcher.routeFunction(dlw_cal, reinterpret_cast(Wrap_my_dm_logger_write), true); ++ if (patcher.getError() == KernelPatcher::Error::NoError) { ++ DBGLOG("igfx", "routed _dm_logger_write"); ++ } else { ++ SYSLOG("igfx", "failed to route _dm_logger_write %d", patcher.getError()); ++ patcher.clearError(); ++ } ++ }*/ ++ ++ /* ++ SYSLOG("igfx", "route _dce110_edp_power_control"); ++ mach_vm_address_t depc_cal = address + 0x1B04B9; ++ const uint8_t depc_expect[] = { 0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53, 0x48, 0x83, 0xEC, 0x48, 0x4C, 0x8B, 0xB7 }; ++ unsigned char* depc_cal_charp = (unsigned char*)depc_cal; ++ expect_match = true; ++ for (int i = 0; i < 20; i++) { ++ //SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); ++ if (depc_cal_charp[i] != depc_expect[i]) { ++ SYSLOG("igfx", "_dce110_edp_power_control dismatch: %d => %x != %x", i, depc_cal_charp[i], depc_expect[i]); ++ expect_match = false; ++ break; ++ } ++ } ++ if (expect_match) { ++ orig_dce110_edp_power_control = patcher.routeFunction(depc_cal, reinterpret_cast(wrap_dce110_edp_power_control), true); ++ if (patcher.getError() == KernelPatcher::Error::NoError) { ++ DBGLOG("igfx", "routed _dce110_edp_power_control"); ++ } else { ++ SYSLOG("igfx", "failed to route _dce110_edp_power_control %d", patcher.getError()); ++ patcher.clearError(); ++ } ++ } ++ ++ SYSLOG("igfx", "route _dce110_edp_backlight_control"); ++ mach_vm_address_t debc_cal = address + 0x1B0926; ++ const uint8_t debc_expect[] = { 0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53, 0x48, 0x83, 0xEC, 0x48, 0x4C, 0x8B, 0xAF }; ++ unsigned char* debc_cal_charp = (unsigned char*)debc_cal; ++ expect_match = true; ++ for (int i = 0; i < 20; i++) { ++ //SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); ++ if (debc_cal_charp[i] != debc_expect[i]) { ++ SYSLOG("igfx", "_dce110_edp_backlight_control dismatch: %d => %x != %x", i, debc_cal_charp[i], debc_expect[i]); ++ expect_match = false; ++ break; ++ } ++ } ++ if (expect_match) { ++ orig_dce110_edp_backlight_control = patcher.routeFunction(debc_cal, reinterpret_cast(wrap_dce110_edp_backlight_control), true); ++ if (patcher.getError() == KernelPatcher::Error::NoError) { ++ DBGLOG("igfx", "routed _dce110_edp_backlight_control"); ++ } else { ++ SYSLOG("igfx", "failed to route _dce110_edp_backlight_control %d", patcher.getError()); ++ patcher.clearError(); ++ } ++ }*/ ++ } else { ++ KernelPatcher::RouteRequest requests[] = { ++ {"_dce_panel_cntl_hw_init", wrapDcePanelCntlHwInit, orgDcePanelCntlHwInit}, ++ {"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25setAttributeForConnectionEijm", wrapAMDRadeonX6000AmdRadeonFramebufferSetAttribute, orgAMDRadeonX6000AmdRadeonFramebufferSetAttribute}, ++ {"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25getAttributeForConnectionEijPm", wrapAMDRadeonX6000AmdRadeonFramebufferGetAttribute, orgAMDRadeonX6000AmdRadeonFramebufferGetAttribute}, ++ //{"_dc_link_bandwidth_kbps", wrapDcLinkBandwidthKbps, orgDcLinkBandwidthKbps}, ++ }; ++ ++ mach_vm_address_t dpchi_cal = address + 0x124F56; ++ unsigned char* dpchi_cal_charp = (unsigned char*)dpchi_cal; ++ for (int i = 0; i < 20; i++) { ++ SYSLOG("igfx", "%d => %x", i, dpchi_cal_charp[i]); ++ } ++ mach_vm_address_t ddsb_cal = address + 0x124B21; ++ unsigned char* ddsb_cal_charp = (unsigned char*)ddsb_cal; ++ for (int i = 0; i < 20; i++) { ++ SYSLOG("igfx", "%d => %x", i, ddsb_cal_charp[i]); ++ } ++ ++ if (!patcher.routeMultiple(index, requests, address, size, true, true)) ++ SYSLOG("igfx", "Failed to route redeon x6000 gpu tracing."); ++ ++ mach_vm_address_t ddsb = patcher.solveSymbol(index, "_dce_driver_set_backlight"); ++ SYSLOG("igfx", "got Bigsur _dce_driver_set_backlight address: %p", ddsb); ++ orgDceDriverSetBacklight = reinterpret_cast(ddsb); ++ } ++ //mach_vm_address_t ddsb_cal = address + 0x124F56; ++ //SYSLOG("igfx", "got _dce_driver_set_backlight address: %p, %p", ddsb, ddsb_cal); ++ //orgDceDriverSetBacklight = reinterpret_cast(ddsb); ++ if (patcher.getError() != KernelPatcher::Error::NoError) { ++ SYSLOG("igfx", "failed to resolve _dce_driver_set_backlight"); ++ patcher.clearError(); ++ return false; ++ } ++ } ++ ++ if (kextRadeonFramebuffer.loadIndex == index) { ++ if (force24BppMode) ++ process24BitOutput(patcher, kextRadeonFramebuffer, address, size); ++ return true; ++ } ++ ++ if (kextRadeonLegacyFramebuffer.loadIndex == index) { ++ if (force24BppMode) ++ process24BitOutput(patcher, kextRadeonLegacyFramebuffer, address, size); ++ return true; ++ } ++ ++ if (kextRadeonSupport.loadIndex == index) { ++ processConnectorOverrides(patcher, address, size, true); ++ ++ if (getKernelVersion() > KernelVersion::Mojave || ++ (getKernelVersion() == KernelVersion::Mojave && getKernelMinorVersion() >= 5)) { ++ KernelPatcher::RouteRequest request("__ZN13ATIController8TestVRAME13PCI_REG_INDEXb", doNotTestVram); ++ patcher.routeMultiple(index, &request, 1, address, size); ++ } ++ ++ if (useCustomAgdpDecision) { ++ KernelPatcher::RouteRequest request("__ZN16AtiDeviceControl16notifyLinkChangeE31kAGDCRegisterLinkControlEvent_tmj", wrapNotifyLinkChange, orgNotifyLinkChange); ++ patcher.routeMultiple(index, &request, 1, address, size); ++ } ++ ++ return true; ++ } ++ ++ if (kextRadeonLegacySupport.loadIndex == index) { ++ processConnectorOverrides(patcher, address, size, false); ++ return true; ++ } ++ ++ if (kextPolarisController.loadIndex == index) { ++ KernelPatcher::RouteRequest request("__ZN17AMD9500Controller23findProjectByPartNumberEP20ControllerProperties", findProjectByPartNumber); ++ patcher.routeMultiple(index, &request, 1, address, size); ++ } ++ ++ for (size_t i = 0; i < maxHardwareKexts; i++) { ++ if (kextRadeonHardware[i].loadIndex == index) { ++ processHardwareKext(patcher, i, address, size); ++ return true; ++ } ++ } ++ ++ return false; ++} + +- if (prefix) { +- DBGLOG("rad", "GetProperty discovered property merge request for %s", aKey); +- auto rawProps = props->copyCollection(); +- if (rawProps) { +- auto newProps = OSDynamicCast(OSDictionary, rawProps); +- if (newProps) { +- callbackRAD->mergeProperties(newProps, prefix, provider); +- that->setProperty(aKey, newProps); +- obj = newProps; +- } +- rawProps->release(); +- } ++void RAD::initHardwareKextMods() { ++ // Decide on kext amount present for optimal performance. ++ // 10.15+ has X4000, X5000, and X6000 ++ // 10.14+ has X4000 and X5000 ++ // 10.13.4+ has X3000, X4000, and X5000 ++ if (getKernelVersion() >= KernelVersion::Catalina) ++ maxHardwareKexts = MaxRadeonHardwareCatalina; ++ else if (getKernelVersion() >= KernelVersion::Mojave) ++ maxHardwareKexts = MaxRadeonHardwareMojave; ++ else if (getKernelVersion() == KernelVersion::HighSierra && getKernelMinorVersion() >= 5) ++ maxHardwareKexts = MaxRadeonHardwareModernHighSierra; ++ ++ // 10.13.4 fixed black screen issues ++ if (maxHardwareKexts != MaxRadeonHardware) { ++ for (size_t i = 0; i < MaxGetFrameBufferProcs; i++) ++ getFrameBufferProcNames[IndexRadeonHardwareX4000][i] = nullptr; ++ ++ // We have nothing to do for these kexts on recent systems ++ if (!fixConfigName && !forceOpenGL && !forceCodecInfo) { ++ // X4000 kext is not included in this list as we need to fix GVA properties for most of its GPUs ++ kextRadeonHardware[IndexRadeonHardwareX5000].switchOff(); ++ kextRadeonHardware[IndexRadeonHardwareX6000].switchOff(); ++ } ++ } ++ ++ if (getKernelVersion() < KernelVersion::Catalina) { ++ kextRadeonHardware[IndexRadeonHardwareX6000].switchOff(); ++ } ++ ++ if (getKernelVersion() < KernelVersion::HighSierra) { ++ // Versions before 10.13 do not support X4250 and X5000 ++ kextRadeonHardware[IndexRadeonHardwareX4250].switchOff(); ++ kextRadeonHardware[IndexRadeonHardwareX5000].switchOff(); ++ ++ // Versions before 10.13 have legacy X3000 and X4000 IDs ++ kextRadeonHardware[IndexRadeonHardwareX3000].id = idRadeonX3000Old; ++ kextRadeonHardware[IndexRadeonHardwareX4000].id = idRadeonX4000Old; ++ ++ bool preSierra = getKernelVersion() < KernelVersion::Sierra; ++ ++ if (preSierra) { ++ // Versions before 10.12 do not support X4100 ++ kextRadeonHardware[IndexRadeonHardwareX4100].switchOff(); ++ } ++ ++ if (preSierra || (getKernelVersion() == KernelVersion::Sierra && getKernelMinorVersion() < 7)) { ++ // Versions before 10.12.6 do not support X4150, X4200 ++ kextRadeonHardware[IndexRadeonHardwareX4150].switchOff(); ++ kextRadeonHardware[IndexRadeonHardwareX4200].switchOff(); ++ } ++ } ++ ++ lilu.onKextLoadForce(kextRadeonHardware, maxHardwareKexts); ++} + +- } +- } +- } ++void RAD::process24BitOutput(KernelPatcher &patcher, KernelPatcher::KextInfo &info, mach_vm_address_t address, size_t size) { ++ auto bitsPerComponent = patcher.solveSymbol(info.loadIndex, "__ZL18BITS_PER_COMPONENT", address, size); ++ if (bitsPerComponent) { ++ while (bitsPerComponent && *bitsPerComponent) { ++ if (*bitsPerComponent == 10) { ++ auto ret = MachInfo::setKernelWriting(true, KernelPatcher::kernelWriteLock); ++ if (ret == KERN_SUCCESS) { ++ DBGLOG("rad", "fixing BITS_PER_COMPONENT"); ++ *bitsPerComponent = 8; ++ MachInfo::setKernelWriting(false, KernelPatcher::kernelWriteLock); ++ } else { ++ SYSLOG("rad", "failed to disable write protection for BITS_PER_COMPONENT"); ++ } ++ } ++ bitsPerComponent++; ++ } ++ } else { ++ SYSLOG("rad", "failed to find BITS_PER_COMPONENT"); ++ patcher.clearError(); ++ } ++ ++ DBGLOG("rad", "fixing pixel types"); ++ ++ KernelPatcher::LookupPatch pixelPatch { ++ &info, ++ reinterpret_cast("--RRRRRRRRRRGGGGGGGGGGBBBBBBBBBB"), ++ reinterpret_cast("--------RRRRRRRRGGGGGGGGBBBBBBBB"), ++ 32, 2 ++ }; ++ ++ patcher.applyLookupPatch(&pixelPatch); ++ if (patcher.getError() != KernelPatcher::Error::NoError) { ++ SYSLOG("rad", "failed to patch RGB mask for 24-bit output"); ++ patcher.clearError(); ++ } ++} + +- return obj; ++void RAD::processConnectorOverrides(KernelPatcher &patcher, mach_vm_address_t address, size_t size, bool modern) { ++ if (modern) { ++ if (getKernelVersion() >= KernelVersion::HighSierra) { ++ KernelPatcher::RouteRequest requests[] { ++ KernelPatcher::RouteRequest("__ZN14AtiBiosParser116getConnectorInfoEP13ConnectorInfoRh", wrapGetConnectorsInfoV1, orgGetConnectorsInfoV1), ++ KernelPatcher::RouteRequest("__ZN14AtiBiosParser216getConnectorInfoEP13ConnectorInfoRh", wrapGetConnectorsInfoV2, orgGetConnectorsInfoV2), ++ KernelPatcher::RouteRequest("__ZN14AtiBiosParser126translateAtomConnectorInfoERN30AtiObjectInfoTableInterface_V117AtomConnectorInfoER13ConnectorInfo", ++ wrapTranslateAtomConnectorInfoV1, orgTranslateAtomConnectorInfoV1), ++ KernelPatcher::RouteRequest("__ZN14AtiBiosParser226translateAtomConnectorInfoERN30AtiObjectInfoTableInterface_V217AtomConnectorInfoER13ConnectorInfo", ++ wrapTranslateAtomConnectorInfoV2, orgTranslateAtomConnectorInfoV2), ++ KernelPatcher::RouteRequest("__ZN13ATIController5startEP9IOService", wrapATIControllerStart, orgATIControllerStart) ++ }; ++ patcher.routeMultiple(kextRadeonSupport.loadIndex, requests, address, size); ++ } else { ++ KernelPatcher::RouteRequest requests[] { ++ KernelPatcher::RouteRequest("__ZN23AtiAtomBiosDceInterface17getConnectorsInfoEP13ConnectorInfoRh", wrapGetConnectorsInfoV1, orgGetConnectorsInfoV1), ++ KernelPatcher::RouteRequest("__ZN13ATIController5startEP9IOService", wrapATIControllerStart, orgATIControllerStart), ++ }; ++ patcher.routeMultiple(kextRadeonSupport.loadIndex, requests, address, size); ++ ++ orgGetAtomObjectTableForType = reinterpret_cast(patcher.solveSymbol(kextRadeonSupport.loadIndex, ++ "__ZN20AtiAtomBiosUtilities25getAtomObjectTableForTypeEhRh", address, size)); ++ if (!orgGetAtomObjectTableForType) { ++ SYSLOG("rad", "failed to find AtiAtomBiosUtilities::getAtomObjectTableForType"); ++ patcher.clearError(); ++ } ++ } ++ } else { ++ KernelPatcher::RouteRequest requests[] { ++ KernelPatcher::RouteRequest("__ZN23AtiAtomBiosDceInterface17getConnectorsInfoEP13ConnectorInfoRh", wrapLegacyGetConnectorsInfo, orgLegacyGetConnectorsInfo), ++ KernelPatcher::RouteRequest("__ZN19AMDLegacyController5startEP9IOService", wrapLegacyATIControllerStart, orgLegacyATIControllerStart), ++ }; ++ patcher.routeMultiple(kextRadeonLegacySupport.loadIndex, requests, address, size); ++ ++ orgLegacyGetAtomObjectTableForType = patcher.solveSymbol(kextRadeonLegacySupport.loadIndex, ++ "__ZN20AtiAtomBiosUtilities25getAtomObjectTableForTypeEhRh", address, size); ++ if (!orgLegacyGetAtomObjectTableForType) { ++ SYSLOG("rad", "failed to find AtiAtomBiosUtilities::getAtomObjectTableForType"); ++ patcher.clearError(); ++ } ++ } + } + +-uint32_t RAD::wrapGetConnectorsInfoV1(void *that, RADConnectors::Connector *connectors, uint8_t *sz) { +- uint32_t code = FunctionCast(wrapGetConnectorsInfoV1, callbackRAD->orgGetConnectorsInfoV1)(that, connectors, sz); +- auto props = callbackRAD->currentPropProvider.get(); +- +- if (code == 0 && sz && props && *props) { +- if (getKernelVersion() >= KernelVersion::HighSierra) +- callbackRAD->updateConnectorsInfo(nullptr, nullptr, *props, connectors, sz); +- else +- callbackRAD->updateConnectorsInfo(static_cast(that)[1], callbackRAD->orgGetAtomObjectTableForType, *props, connectors, sz); +- } else { +- DBGLOG("rad", "getConnectorsInfoV1 failed %X or undefined %d", code, props == nullptr); +- } ++void RAD::processHardwareKext(KernelPatcher &patcher, size_t hwIndex, mach_vm_address_t address, size_t size) { ++ auto getFrame = getFrameBufferProcNames[hwIndex]; ++ auto &hardware = kextRadeonHardware[hwIndex]; ++ ++ // Fix boot and wake to black screen ++ for (size_t j = 0; j < MaxGetFrameBufferProcs && getFrame[j] != nullptr; j++) { ++ auto getFB = patcher.solveSymbol(hardware.loadIndex, getFrame[j], address, size); ++ if (getFB) { ++ // Initially it was discovered that the only problematic register is PRIMARY_SURFACE_ADDRESS_HIGH (0x1A07). ++ // This register must be nulled to solve most of the issues. ++ // Depending on the amount of connected screens PRIMARY_SURFACE_ADDRESS (0x1A04) may not be null. ++ // However, as of AMD Vega drivers in 10.13 DP1 both of these registers are now ignored. ++ // Furthermore, there are no (extra) issues from just returning 0 in framebuffer base address. ++ ++ // xor rax, rax ++ // ret ++ uint8_t ret[] {0x48, 0x31, 0xC0, 0xC3}; ++ patcher.routeBlock(getFB, ret, sizeof(ret)); ++ if (patcher.getError() == KernelPatcher::Error::NoError) { ++ DBGLOG("rad", "patched %s", getFrame[j]); ++ } else { ++ SYSLOG("rad", "failed to patch %s code %d", getFrame[j], patcher.getError()); ++ patcher.clearError(); ++ } ++ } else { ++ SYSLOG("rad", "failed to find %s code %d", getFrame[j], patcher.getError()); ++ patcher.clearError(); ++ } ++ } ++ ++ // Fix reported Accelerator name to support WhateverName.app ++ // Also fix GVA properties for X4000. ++ if (fixConfigName || hwIndex == IndexRadeonHardwareX4000) { ++ KernelPatcher::RouteRequest request(populateAccelConfigProcNames[hwIndex], wrapPopulateAccelConfig[hwIndex], orgPopulateAccelConfig[hwIndex]); ++ patcher.routeMultiple(hardware.loadIndex, &request, 1, address, size); ++ } ++ ++ // Enforce OpenGL support if requested ++ if (forceOpenGL) { ++ DBGLOG("rad", "disabling Metal support"); ++ uint8_t find1[] {0x4D, 0x65, 0x74, 0x61, 0x6C, 0x53, 0x74, 0x61}; ++ uint8_t find2[] {0x4D, 0x65, 0x74, 0x61, 0x6C, 0x50, 0x6C, 0x75}; ++ uint8_t repl1[] {0x50, 0x65, 0x74, 0x61, 0x6C, 0x53, 0x74, 0x61}; ++ uint8_t repl2[] {0x50, 0x65, 0x74, 0x61, 0x6C, 0x50, 0x6C, 0x75}; ++ ++ KernelPatcher::LookupPatch antimetal[] { ++ {&hardware, find1, repl1, sizeof(find1), 2}, ++ {&hardware, find2, repl2, sizeof(find1), 2} ++ }; ++ ++ for (auto &p : antimetal) { ++ patcher.applyLookupPatch(&p); ++ patcher.clearError(); ++ } ++ } ++ ++ // Patch AppleGVA support for non-supported models ++ if (forceCodecInfo && getHWInfoProcNames[hwIndex] != nullptr) { ++ KernelPatcher::RouteRequest request(getHWInfoProcNames[hwIndex], wrapGetHWInfo[hwIndex], orgGetHWInfo[hwIndex]); ++ patcher.routeMultiple(hardware.loadIndex, &request, 1, address, size); ++ } ++} + +- return code; ++void RAD::mergeProperty(OSDictionary *props, const char *name, OSObject *value) { ++ // The only type we could make from device properties is data. ++ // To be able to override other types we do a conversion here. ++ auto data = OSDynamicCast(OSData, value); ++ if (data) { ++ // It is hard to make a boolean even from ACPI, so we make a hack here: ++ // 1-byte OSData with 0x01 / 0x00 values becomes boolean. ++ auto val = static_cast(data->getBytesNoCopy()); ++ auto len = data->getLength(); ++ if (val && len == sizeof(uint8_t)) { ++ if (val[0] == 1) { ++ props->setObject(name, kOSBooleanTrue); ++ DBGLOG("rad", "prop %s was merged as kOSBooleanTrue", name); ++ return; ++ } else if (val[0] == 0) { ++ props->setObject(name, kOSBooleanFalse); ++ DBGLOG("rad", "prop %s was merged as kOSBooleanFalse", name); ++ return; ++ } ++ } ++ ++ // Consult the original value to make a decision ++ auto orgValue = props->getObject(name); ++ if (val && orgValue) { ++ DBGLOG("rad", "prop %s has original value", name); ++ if (len == sizeof(uint32_t) && OSDynamicCast(OSNumber, orgValue)) { ++ auto num = *reinterpret_cast(val); ++ auto osnum = OSNumber::withNumber(num, 32); ++ if (osnum) { ++ DBGLOG("rad", "prop %s was merged as number %u", name, num); ++ props->setObject(name, osnum); ++ osnum->release(); ++ } ++ return; ++ } else if (len > 0 && val[len-1] == '\0' && OSDynamicCast(OSString, orgValue)) { ++ auto str = reinterpret_cast(val); ++ auto osstr = OSString::withCString(str); ++ if (osstr) { ++ DBGLOG("rad", "prop %s was merged as string %s", name, str); ++ props->setObject(name, osstr); ++ osstr->release(); ++ } ++ return; ++ } ++ } else { ++ DBGLOG("rad", "prop %s has no original value", name); ++ } ++ } ++ ++ // Default merge as is ++ props->setObject(name, value); ++ DBGLOG("rad", "prop %s was merged", name); + } + +-uint32_t RAD::wrapGetConnectorsInfoV2(void *that, RADConnectors::Connector *connectors, uint8_t *sz) { +- uint32_t code = FunctionCast(wrapGetConnectorsInfoV2, callbackRAD->orgGetConnectorsInfoV2)(that, connectors, sz); +- auto props = callbackRAD->currentPropProvider.get(); ++void RAD::mergeProperties(OSDictionary *props, const char *prefix, IOService *provider) { ++ // Should be ok, but in case there are issues switch to dictionaryWithProperties(); ++ auto dict = provider->getPropertyTable(); ++ if (dict) { ++ auto iterator = OSCollectionIterator::withCollection(dict); ++ if (iterator) { ++ OSSymbol *propname; ++ size_t prefixlen = strlen(prefix); ++ while ((propname = OSDynamicCast(OSSymbol, iterator->getNextObject())) != nullptr) { ++ auto name = propname->getCStringNoCopy(); ++ if (name && propname->getLength() > prefixlen && !strncmp(name, prefix, prefixlen)) { ++ auto prop = dict->getObject(propname); ++ if (prop) ++ mergeProperty(props, name + prefixlen, prop); ++ else ++ DBGLOG("rad", "prop %s was not merged due to no value", name); ++ } else { ++ //DBGLOG("rad", "prop %s does not match %s prefix", safeString(name), prefix); ++ } ++ } ++ ++ iterator->release(); ++ } else { ++ SYSLOG("rad", "prop merge failed to iterate over properties"); ++ } ++ } else { ++ SYSLOG("rad", "prop merge failed to get properties"); ++ } ++ ++ if (!strcmp(prefix, "CAIL,")) { ++ for (size_t i = 0; i < arrsize(powerGatingFlags); i++) { ++ if (powerGatingFlags[i] && props->getObject(powerGatingFlags[i])) { ++ DBGLOG("rad", "cail prop merge found %s, replacing", powerGatingFlags[i]); ++ auto num = OSNumber::withNumber(1, 32); ++ if (num) { ++ props->setObject(powerGatingFlags[i], num); ++ num->release(); ++ } ++ } ++ } ++ } ++} + +- if (code == 0 && sz && props && *props) +- callbackRAD->updateConnectorsInfo(nullptr, nullptr, *props, connectors, sz); +- else +- DBGLOG("rad", "getConnectorsInfoV2 failed %X or undefined %d", code, props == nullptr); ++void RAD::applyPropertyFixes(IOService *service, uint32_t connectorNum) { ++ if (service && getKernelVersion() >= KernelVersion::HighSierra) { ++ // Starting with 10.13.2 this is important to fix sleep issues due to enforced 6 screens ++ if (!service->getProperty("CFG,CFG_FB_LIMIT")) { ++ DBGLOG("rad", "setting fb limit to %u", connectorNum); ++ service->setProperty("CFG_FB_LIMIT", connectorNum, 32); ++ } ++ ++ // In the past we set CFG_USE_AGDC to false, which caused visual glitches and broken multimonitor support. ++ // A better workaround is to disable AGDP just like we do globally. ++ } ++} + +- return code; ++void RAD::updateConnectorsInfo(void *atomutils, t_getAtomObjectTableForType gettable, IOService *ctrl, RADConnectors::Connector *connectors, uint8_t *sz) { ++ if (atomutils) { ++ DBGLOG("rad", "getConnectorsInfo found %u connectors", *sz); ++ RADConnectors::print(connectors, *sz); ++ } ++ ++ // Check if the user wants to override automatically detected connectors ++ auto cons = ctrl->getProperty("connectors"); ++ if (cons) { ++ auto consData = OSDynamicCast(OSData, cons); ++ if (consData) { ++ auto consPtr = consData->getBytesNoCopy(); ++ auto consSize = consData->getLength(); ++ ++ uint32_t consCount; ++ if (WIOKit::getOSDataValue(ctrl, "connector-count", consCount)) { ++ *sz = consCount; ++ DBGLOG("rad", "getConnectorsInfo got size override to %u", *sz); ++ } ++ ++ if (consPtr && consSize > 0 && *sz > 0 && RADConnectors::valid(consSize, *sz)) { ++ RADConnectors::copy(connectors, *sz, static_cast(consPtr), consSize); ++ DBGLOG("rad", "getConnectorsInfo installed %u connectors", *sz); ++ applyPropertyFixes(ctrl, *sz); ++ } else { ++ DBGLOG("rad", "getConnectorsInfo conoverrides have invalid size %u for %u num", consSize, *sz); ++ } ++ } else { ++ DBGLOG("rad", "getConnectorsInfo conoverrides have invalid type"); ++ } ++ } else { ++ if (atomutils) { ++ DBGLOG("rad", "getConnectorsInfo attempting to autofix connectors"); ++ uint8_t sHeader = 0, displayPathNum = 0, connectorObjectNum = 0; ++ auto baseAddr = static_cast(gettable(atomutils, AtomObjectTableType::Common, &sHeader)) - sizeof(uint32_t); ++ auto displayPaths = static_cast(gettable(atomutils, AtomObjectTableType::DisplayPath, &displayPathNum)); ++ auto connectorObjects = static_cast(gettable(atomutils, AtomObjectTableType::ConnectorObject, &connectorObjectNum)); ++ if (displayPathNum == connectorObjectNum) ++ autocorrectConnectors(baseAddr, displayPaths, displayPathNum, connectorObjects, connectorObjectNum, connectors, *sz); ++ else ++ DBGLOG("rad", "getConnectorsInfo found different displaypaths %u and connectors %u", displayPathNum, connectorObjectNum); ++ } ++ ++ applyPropertyFixes(ctrl, *sz); ++ ++ // Prioritise connectors, since it may cause black screen on e.g. R9 370 ++ const uint8_t *senseList = nullptr; ++ uint8_t senseNum = 0; ++ auto priData = OSDynamicCast(OSData, ctrl->getProperty("connector-priority")); ++ if (priData) { ++ senseList = static_cast(priData->getBytesNoCopy()); ++ senseNum = static_cast(priData->getLength()); ++ DBGLOG("rad", "getConnectorInfo found %u senses in connector-priority", senseNum); ++ reprioritiseConnectors(senseList, senseNum, connectors, *sz); ++ } else { ++ DBGLOG("rad", "getConnectorInfo leaving unchaged priority"); ++ } ++ } ++ ++ DBGLOG("rad", "getConnectorsInfo resulting %u connectors follow", *sz); ++ RADConnectors::print(connectors, *sz); + } + +-uint32_t RAD::wrapLegacyGetConnectorsInfo(void *that, RADConnectors::Connector *connectors, uint8_t *sz) { +- uint32_t code = FunctionCast(wrapLegacyGetConnectorsInfo, callbackRAD->orgLegacyGetConnectorsInfo)(that, connectors, sz); +- auto props = callbackRAD->currentLegacyPropProvider.get(); ++void RAD::autocorrectConnectors(uint8_t *baseAddr, AtomDisplayObjectPath *displayPaths, uint8_t displayPathNum, AtomConnectorObject *connectorObjects, ++ uint8_t connectorObjectNum, RADConnectors::Connector *connectors, uint8_t sz) { ++ for (uint8_t i = 0; i < displayPathNum; i++) { ++ if (!isEncoder(displayPaths[i].usGraphicObjIds)) { ++ DBGLOG("rad", "autocorrectConnectors not encoder %X at %u", displayPaths[i].usGraphicObjIds, i); ++ continue; ++ } ++ ++ uint8_t txmit = 0, enc = 0; ++ if (!getTxEnc(displayPaths[i].usGraphicObjIds, txmit, enc)) ++ continue; ++ ++ uint8_t sense = getSenseID(baseAddr + connectorObjects[i].usRecordOffset); ++ if (!sense) { ++ DBGLOG("rad", "autocorrectConnectors failed to detect sense for %u connector", i); ++ continue; ++ } ++ ++ DBGLOG("rad", "autocorrectConnectors found txmit %02X enc %02X sense %02X for %u connector", txmit, enc, sense, i); ++ ++ autocorrectConnector(getConnectorID(displayPaths[i].usConnObjectId), sense, txmit, enc, connectors, sz); ++ } ++} + +- if (code == 0 && sz && props && *props) +- callbackRAD->updateConnectorsInfo(static_cast(that)[1], callbackRAD->orgLegacyGetAtomObjectTableForType, *props, connectors, sz); +- else +- DBGLOG("rad", "legacy getConnectorsInfo failed %X or undefined %d", code, props == nullptr); ++void RAD::autocorrectConnector(uint8_t connector, uint8_t sense, uint8_t txmit, uint8_t enc, RADConnectors::Connector *connectors, uint8_t sz) { ++ // This function attempts to fix the following issues: ++ // ++ // 1. Incompatible DVI transmitter on 290X, 370 and probably some other models ++ // In this case a correct transmitter is detected by AtiAtomBiosDce60::getPropertiesForEncoderObject, however, later ++ // in AtiAtomBiosDce60::getPropertiesForConnectorObject for DVI DL and TITFP513 this value is conjuncted with 0xCF, ++ // which makes it wrong: 0x10 -> 0, 0x11 -> 1. As a result one gets black screen when connecting multiple displays. ++ // getPropertiesForEncoderObject takes usGraphicObjIds and getPropertiesForConnectorObject takes usConnObjectId ++ ++ if (callbackRAD->dviSingleLink) { ++ if (connector != CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I && ++ connector != CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D && ++ connector != CONNECTOR_OBJECT_ID_LVDS) { ++ DBGLOG("rad", "autocorrectConnector found unsupported connector type %02X", connector); ++ return; ++ } ++ ++ auto fixTransmit = [](auto &con, uint8_t idx, uint8_t sense, uint8_t txmit) { ++ if (con.sense == sense) { ++ if (con.transmitter != txmit && (con.transmitter & 0xCF) == con.transmitter) { ++ DBGLOG("rad", "autocorrectConnector replacing txmit %02X with %02X for %u connector sense %02X", ++ con.transmitter, txmit, idx, sense); ++ con.transmitter = txmit; ++ } ++ return true; ++ } ++ return false; ++ }; ++ ++ bool isModern = RADConnectors::modern(); ++ for (uint8_t j = 0; j < sz; j++) { ++ if (isModern) { ++ auto &con = (&connectors->modern)[j]; ++ if (fixTransmit(con, j, sense, txmit)) ++ break; ++ } else { ++ auto &con = (&connectors->legacy)[j]; ++ if (fixTransmit(con, j, sense, txmit)) ++ break; ++ } ++ } ++ } else { ++ DBGLOG("rad", "autocorrectConnector use -raddvi to enable dvi autocorrection"); ++ } ++} + +- return code; ++void RAD::reprioritiseConnectors(const uint8_t *senseList, uint8_t senseNum, RADConnectors::Connector *connectors, uint8_t sz) { ++ static constexpr uint32_t typeList[] { ++ RADConnectors::ConnectorLVDS, ++ RADConnectors::ConnectorDigitalDVI, ++ RADConnectors::ConnectorHDMI, ++ RADConnectors::ConnectorDP, ++ RADConnectors::ConnectorVGA ++ }; ++ static constexpr uint8_t typeNum {static_cast(arrsize(typeList))}; ++ ++ bool isModern = RADConnectors::modern(); ++ uint16_t priCount = 1; ++ // Automatically detected connectors have equal priority (0), which often results in black screen ++ // This allows to change this firstly by user-defined list, then by type list. ++ //TODO: priority is ignored for 5xxx and 6xxx GPUs, should we manually reorder items? ++ for (uint8_t i = 0; i < senseNum + typeNum + 1; i++) { ++ for (uint8_t j = 0; j < sz; j++) { ++ auto reorder = [&](auto &con) { ++ if (i == senseNum + typeNum) { ++ if (con.priority == 0) ++ con.priority = priCount++; ++ } else if (i < senseNum) { ++ if (con.sense == senseList[i]) { ++ DBGLOG("rad", "reprioritiseConnectors setting priority of sense %02X to %u by sense", con.sense, priCount); ++ con.priority = priCount++; ++ return true; ++ } ++ } else { ++ if (con.priority == 0 && con.type == typeList[i-senseNum]) { ++ DBGLOG("rad", "reprioritiseConnectors setting priority of sense %02X to %u by type", con.sense, priCount); ++ con.priority = priCount++; ++ } ++ } ++ return false; ++ }; ++ ++ if ((isModern && reorder((&connectors->modern)[j])) || ++ (!isModern && reorder((&connectors->legacy)[j]))) ++ break; ++ } ++ } + } + +-uint32_t RAD::wrapTranslateAtomConnectorInfoV1(void *that, RADConnectors::AtomConnectorInfo *info, RADConnectors::Connector *connector) { +- uint32_t code = FunctionCast(wrapTranslateAtomConnectorInfoV1, callbackRAD->orgTranslateAtomConnectorInfoV1)(that, info, connector); +- +- if (code == 0 && info && connector) { +- RADConnectors::print(connector, 1); +- +- uint8_t sense = getSenseID(info->i2cRecord); +- if (sense) { +- DBGLOG("rad", "translateAtomConnectorInfoV1 got sense id %02X", sense); +- +- // We need to extract usGraphicObjIds from info->hpdRecord, which is of type ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT: +- // struct ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT { +- // uint8_t ucNumberOfSrc; +- // uint16_t usSrcObjectID[ucNumberOfSrc]; +- // uint8_t ucNumberOfDst; +- // uint16_t usDstObjectID[ucNumberOfDst]; +- // }; +- // The value we need is in usSrcObjectID. The structure is byte-packed. +- +- uint8_t ucNumberOfSrc = info->hpdRecord[0]; +- for (uint8_t i = 0; i < ucNumberOfSrc; i++) { +- auto usSrcObjectID = *reinterpret_cast(info->hpdRecord + sizeof(uint8_t) + i * sizeof(uint16_t)); +- DBGLOG("rad", "translateAtomConnectorInfoV1 checking %04X object id", usSrcObjectID); +- if (((usSrcObjectID & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT) == GRAPH_OBJECT_TYPE_ENCODER) { +- uint8_t txmit = 0, enc = 0; +- if (getTxEnc(usSrcObjectID, txmit, enc)) +- callbackRAD->autocorrectConnector(getConnectorID(info->usConnObjectId), getSenseID(info->i2cRecord), txmit, enc, connector, 1); +- break; +- } +- } ++void RAD::setGvaProperties(IOService *accelService) { ++ auto codecStr = OSDynamicCast(OSString, accelService->getProperty("IOGVACodec")); ++ if (codecStr == nullptr) { ++ DBGLOG("rad", "updating X4000 accelerator IOGVACodec to VCE"); ++ accelService->setProperty("IOGVACodec", "VCE"); ++ } else { ++ auto codec = codecStr->getCStringNoCopy(); ++ DBGLOG("rad", "X4000 accelerator IOGVACodec is already set to %s", safeString(codec)); ++ if (codec != nullptr && strncmp(codec, "AMD", strlen("AMD")) == 0) { ++ bool needsDecode = accelService->getProperty("IOGVAHEVCDecode") == nullptr; ++ bool needsEncode = accelService->getProperty("IOGVAHEVCEncode") == nullptr; ++ if (needsDecode) { ++ OSObject *VTMaxDecodeLevel = OSNumber::withNumber(153, 32); ++ OSString *VTMaxDecodeLevelKey = OSString::withCString("VTMaxDecodeLevel"); ++ OSDictionary *VTPerProfileDetailsInner = OSDictionary::withCapacity(1); ++ OSDictionary *VTPerProfileDetails = OSDictionary::withCapacity(3); ++ OSString *VTPerProfileDetailsKey1 = OSString::withCString("1"); ++ OSString *VTPerProfileDetailsKey2 = OSString::withCString("2"); ++ OSString *VTPerProfileDetailsKey3 = OSString::withCString("3"); ++ ++ OSArray *VTSupportedProfileArray = OSArray::withCapacity(3); ++ OSNumber *VTSupportedProfileArray1 = OSNumber::withNumber(1, 32); ++ OSNumber *VTSupportedProfileArray2 = OSNumber::withNumber(2, 32); ++ OSNumber *VTSupportedProfileArray3 = OSNumber::withNumber(3, 32); ++ ++ OSDictionary *IOGVAHEVCDecodeCapabilities = OSDictionary::withCapacity(2); ++ OSString *VTPerProfileDetailsKey = OSString::withCString("VTPerProfileDetails"); ++ OSString *VTSupportedProfileArrayKey = OSString::withCString("VTSupportedProfileArray"); ++ ++ if (VTMaxDecodeLevel != nullptr && VTMaxDecodeLevelKey != nullptr && VTPerProfileDetailsInner != nullptr && ++ VTPerProfileDetails != nullptr && VTPerProfileDetailsKey1 != nullptr && VTPerProfileDetailsKey2 != nullptr && ++ VTPerProfileDetailsKey3 != nullptr && VTSupportedProfileArrayKey != nullptr && VTSupportedProfileArray1 != nullptr && ++ VTSupportedProfileArray2 != nullptr && VTSupportedProfileArray3 != nullptr && VTSupportedProfileArray != nullptr && ++ VTPerProfileDetailsKey != nullptr && IOGVAHEVCDecodeCapabilities != nullptr) { ++ VTPerProfileDetailsInner->setObject(VTMaxDecodeLevelKey, VTMaxDecodeLevel); ++ VTPerProfileDetails->setObject(VTPerProfileDetailsKey1, VTPerProfileDetailsInner); ++ VTPerProfileDetails->setObject(VTPerProfileDetailsKey2, VTPerProfileDetailsInner); ++ VTPerProfileDetails->setObject(VTPerProfileDetailsKey3, VTPerProfileDetailsInner); ++ ++ VTSupportedProfileArray->setObject(VTSupportedProfileArray1); ++ VTSupportedProfileArray->setObject(VTSupportedProfileArray2); ++ VTSupportedProfileArray->setObject(VTSupportedProfileArray3); ++ ++ IOGVAHEVCDecodeCapabilities->setObject(VTPerProfileDetailsKey, VTPerProfileDetails); ++ IOGVAHEVCDecodeCapabilities->setObject(VTSupportedProfileArrayKey, VTSupportedProfileArray); ++ ++ accelService->setProperty("IOGVAHEVCDecode", "1"); ++ accelService->setProperty("IOGVAHEVCDecodeCapabilities", IOGVAHEVCDecodeCapabilities); ++ ++ DBGLOG("rad", "recovering IOGVAHEVCDecode"); ++ } else { ++ SYSLOG("rad", "allocation failure in IOGVAHEVCDecode"); ++ } ++ ++ OSSafeReleaseNULL(VTMaxDecodeLevel); ++ OSSafeReleaseNULL(VTMaxDecodeLevelKey); ++ OSSafeReleaseNULL(VTPerProfileDetailsInner); ++ OSSafeReleaseNULL(VTPerProfileDetails); ++ OSSafeReleaseNULL(VTPerProfileDetailsKey1); ++ OSSafeReleaseNULL(VTPerProfileDetailsKey2); ++ OSSafeReleaseNULL(VTPerProfileDetailsKey3); ++ OSSafeReleaseNULL(VTSupportedProfileArrayKey); ++ OSSafeReleaseNULL(VTSupportedProfileArray1); ++ OSSafeReleaseNULL(VTSupportedProfileArray2); ++ OSSafeReleaseNULL(VTSupportedProfileArray3); ++ OSSafeReleaseNULL(VTSupportedProfileArray); ++ OSSafeReleaseNULL(VTPerProfileDetailsKey); ++ OSSafeReleaseNULL(IOGVAHEVCDecodeCapabilities); ++ } ++ ++ if (needsEncode) { ++ OSObject *VTMaxEncodeLevel = OSNumber::withNumber(153, 32); ++ OSString *VTMaxEncodeLevelKey = OSString::withCString("VTMaxEncodeLevel"); ++ ++ OSDictionary *VTPerProfileDetailsInner = OSDictionary::withCapacity(1); ++ OSDictionary *VTPerProfileDetails = OSDictionary::withCapacity(1); ++ OSString *VTPerProfileDetailsKey1 = OSString::withCString("1"); ++ ++ OSArray *VTSupportedProfileArray = OSArray::withCapacity(1); ++ OSNumber *VTSupportedProfileArray1 = OSNumber::withNumber(1, 32); ++ ++ OSDictionary *IOGVAHEVCEncodeCapabilities = OSDictionary::withCapacity(4); ++ OSString *VTPerProfileDetailsKey = OSString::withCString("VTPerProfileDetails"); ++ OSString *VTQualityRatingKey = OSString::withCString("VTQualityRating"); ++ OSNumber *VTQualityRating = OSNumber::withNumber(50, 32); ++ OSString *VTRatingKey = OSString::withCString("VTRating"); ++ OSNumber *VTRating = OSNumber::withNumber(350, 32); ++ OSString *VTSupportedProfileArrayKey = OSString::withCString("VTSupportedProfileArray"); ++ ++ if (VTMaxEncodeLevel != nullptr && VTMaxEncodeLevelKey != nullptr && VTPerProfileDetailsInner != nullptr && ++ VTPerProfileDetails != nullptr && VTPerProfileDetailsKey1 != nullptr && VTSupportedProfileArrayKey != nullptr && ++ VTSupportedProfileArray1 != nullptr && VTSupportedProfileArray != nullptr && VTPerProfileDetailsKey != nullptr && ++ VTQualityRatingKey != nullptr && VTQualityRating != nullptr && VTRatingKey != nullptr && VTRating != nullptr && ++ IOGVAHEVCEncodeCapabilities != nullptr) { ++ ++ VTPerProfileDetailsInner->setObject(VTMaxEncodeLevelKey, VTMaxEncodeLevel); ++ VTPerProfileDetails->setObject(VTPerProfileDetailsKey1, VTPerProfileDetailsInner); ++ VTSupportedProfileArray->setObject(VTSupportedProfileArray1); ++ ++ IOGVAHEVCEncodeCapabilities->setObject(VTPerProfileDetailsKey, VTPerProfileDetails); ++ IOGVAHEVCEncodeCapabilities->setObject(VTQualityRatingKey, VTQualityRating); ++ IOGVAHEVCEncodeCapabilities->setObject(VTRatingKey, VTRating); ++ IOGVAHEVCEncodeCapabilities->setObject(VTSupportedProfileArrayKey, VTSupportedProfileArray); ++ ++ accelService->setProperty("IOGVAHEVCEncode", "1"); ++ accelService->setProperty("IOGVAHEVCEncodeCapabilities", IOGVAHEVCEncodeCapabilities); ++ ++ DBGLOG("rad", "recovering IOGVAHEVCEncode"); ++ } else { ++ SYSLOG("rad", "allocation failure in IOGVAHEVCEncode"); ++ } ++ ++ OSSafeReleaseNULL(VTMaxEncodeLevel); ++ OSSafeReleaseNULL(VTMaxEncodeLevelKey); ++ OSSafeReleaseNULL(VTPerProfileDetailsInner); ++ OSSafeReleaseNULL(VTPerProfileDetails); ++ OSSafeReleaseNULL(VTPerProfileDetailsKey1); ++ OSSafeReleaseNULL(VTSupportedProfileArrayKey); ++ OSSafeReleaseNULL(VTSupportedProfileArray1); ++ OSSafeReleaseNULL(VTSupportedProfileArray); ++ OSSafeReleaseNULL(VTPerProfileDetailsKey); ++ OSSafeReleaseNULL(VTQualityRatingKey); ++ OSSafeReleaseNULL(VTQualityRating); ++ OSSafeReleaseNULL(VTRatingKey); ++ OSSafeReleaseNULL(VTRating); ++ OSSafeReleaseNULL(IOGVAHEVCEncodeCapabilities); ++ } ++ } ++ } ++} + ++void RAD::updateAccelConfig(size_t hwIndex, IOService *accelService, const char **accelConfig) { ++ if (accelService && accelConfig) { ++ if (fixConfigName) { ++ auto gpuService = accelService->getParentEntry(gIOServicePlane); ++ ++ if (gpuService) { ++ auto model = OSDynamicCast(OSData, gpuService->getProperty("model")); ++ if (model) { ++ auto modelStr = static_cast(model->getBytesNoCopy()); ++ if (modelStr) { ++ if (modelStr[0] == 'A' && ((modelStr[1] == 'M' && modelStr[2] == 'D') || ++ (modelStr[1] == 'T' && modelStr[2] == 'I')) && modelStr[3] == ' ') { ++ modelStr += 4; ++ } ++ ++ DBGLOG("rad", "updateAccelConfig found gpu model %s", modelStr); ++ *accelConfig = modelStr; ++ } else { ++ DBGLOG("rad", "updateAccelConfig found null gpu model"); ++ } ++ } else { ++ DBGLOG("rad", "updateAccelConfig failed to find gpu model"); ++ } ++ ++ } else { ++ DBGLOG("rad", "updateAccelConfig failed to find accelerator parent"); ++ } ++ } ++ ++ if (enableGvaSupport && hwIndex == IndexRadeonHardwareX4000) { ++ setGvaProperties(accelService); ++ } ++ } ++} + +- } else { +- DBGLOG("rad", "translateAtomConnectorInfoV1 failed to detect sense for translated connector"); +- } +- } ++bool RAD::wrapSetProperty(IORegistryEntry *that, const char *aKey, void *bytes, unsigned length) { ++ if (length > 10 && aKey && reinterpret_cast(aKey)[0] == 'edom' && reinterpret_cast(aKey)[2] == 'l') { ++ DBGLOG("rad", "SetProperty caught model %u (%.*s)", length, length, static_cast(bytes)); ++ if (*static_cast(bytes) == ' DMA' || *static_cast(bytes) == ' ITA' || *static_cast(bytes) == 'edaR') { ++ if (FunctionCast(wrapGetProperty, callbackRAD->orgGetProperty)(that, aKey)) { ++ DBGLOG("rad", "SetProperty ignored setting %s to %s", aKey, static_cast(bytes)); ++ return true; ++ } ++ DBGLOG("rad", "SetProperty missing %s, fallback to %s", aKey, static_cast(bytes)); ++ } ++ } ++ ++ return FunctionCast(wrapSetProperty, callbackRAD->orgSetProperty)(that, aKey, bytes, length); ++} + +- return code; ++OSObject *RAD::wrapGetProperty(IORegistryEntry *that, const char *aKey) { ++ auto obj = FunctionCast(wrapGetProperty, callbackRAD->orgGetProperty)(that, aKey); ++ auto props = OSDynamicCast(OSDictionary, obj); ++ ++ if (props && aKey) { ++ const char *prefix {nullptr}; ++ auto provider = OSDynamicCast(IOService, that->getParentEntry(gIOServicePlane)); ++ if (provider) { ++ if (aKey[0] == 'a') { ++ if (!strcmp(aKey, "aty_config")) ++ prefix = "CFG,"; ++ else if (!strcmp(aKey, "aty_properties")) ++ prefix = "PP,"; ++ } else if (aKey[0] == 'c' && !strcmp(aKey, "cail_properties")) { ++ prefix = "CAIL,"; ++ } ++ ++ if (prefix) { ++ DBGLOG("rad", "GetProperty discovered property merge request for %s", aKey); ++ auto rawProps = props->copyCollection(); ++ if (rawProps) { ++ auto newProps = OSDynamicCast(OSDictionary, rawProps); ++ if (newProps) { ++ callbackRAD->mergeProperties(newProps, prefix, provider); ++ that->setProperty(aKey, newProps); ++ obj = newProps; ++ } ++ rawProps->release(); ++ } ++ ++ } ++ } ++ } ++ ++ return obj; + } + +-uint32_t RAD::wrapTranslateAtomConnectorInfoV2(void *that, RADConnectors::AtomConnectorInfo *info, RADConnectors::Connector *connector) { +- uint32_t code = FunctionCast(wrapTranslateAtomConnectorInfoV2, callbackRAD->orgTranslateAtomConnectorInfoV2)(that, info, connector); ++uint32_t RAD::wrapGetConnectorsInfoV1(void *that, RADConnectors::Connector *connectors, uint8_t *sz) { ++ uint32_t code = FunctionCast(wrapGetConnectorsInfoV1, callbackRAD->orgGetConnectorsInfoV1)(that, connectors, sz); ++ auto props = callbackRAD->currentPropProvider.get(); ++ ++ if (code == 0 && sz && props && *props) { ++ if (getKernelVersion() >= KernelVersion::HighSierra) ++ callbackRAD->updateConnectorsInfo(nullptr, nullptr, *props, connectors, sz); ++ else ++ callbackRAD->updateConnectorsInfo(static_cast(that)[1], callbackRAD->orgGetAtomObjectTableForType, *props, connectors, sz); ++ } else { ++ DBGLOG("rad", "getConnectorsInfoV1 failed %X or undefined %d", code, props == nullptr); ++ } ++ ++ return code; ++} + +- if (code == 0 && info && connector) { +- RADConnectors::print(connector, 1); ++uint32_t RAD::wrapGetConnectorsInfoV2(void *that, RADConnectors::Connector *connectors, uint8_t *sz) { ++ uint32_t code = FunctionCast(wrapGetConnectorsInfoV2, callbackRAD->orgGetConnectorsInfoV2)(that, connectors, sz); ++ auto props = callbackRAD->currentPropProvider.get(); + +- uint8_t sense = getSenseID(info->i2cRecord); +- if (sense) { +- DBGLOG("rad", "translateAtomConnectorInfoV2 got sense id %02X", sense); +- uint8_t txmit = 0, enc = 0; +- if (getTxEnc(info->usGraphicObjIds, txmit, enc)) +- callbackRAD->autocorrectConnector(getConnectorID(info->usConnObjectId), getSenseID(info->i2cRecord), txmit, enc, connector, 1); +- } else { +- DBGLOG("rad", "translateAtomConnectorInfoV2 failed to detect sense for translated connector"); +- } +- } ++ if (code == 0 && sz && props && *props) ++ callbackRAD->updateConnectorsInfo(nullptr, nullptr, *props, connectors, sz); ++ else ++ DBGLOG("rad", "getConnectorsInfoV2 failed %X or undefined %d", code, props == nullptr); + +- return code; ++ return code; + } + +-bool RAD::wrapATIControllerStart(IOService *ctrl, IOService *provider) { +- DBGLOG("rad", "starting controller " PRIKADDR, CASTKADDR(current_thread())); +- if (callbackRAD->forceVesaMode) { +- DBGLOG("rad", "disabling video acceleration on request"); +- return false; +- } ++uint32_t RAD::wrapLegacyGetConnectorsInfo(void *that, RADConnectors::Connector *connectors, uint8_t *sz) { ++ uint32_t code = FunctionCast(wrapLegacyGetConnectorsInfo, callbackRAD->orgLegacyGetConnectorsInfo)(that, connectors, sz); ++ auto props = callbackRAD->currentLegacyPropProvider.get(); + +- callbackRAD->currentPropProvider.set(provider); +- bool r = FunctionCast(wrapATIControllerStart, callbackRAD->orgATIControllerStart)(ctrl, provider); +- DBGLOG("rad", "starting controller done %d " PRIKADDR, r, CASTKADDR(current_thread())); +- callbackRAD->currentPropProvider.erase(); ++ if (code == 0 && sz && props && *props) ++ callbackRAD->updateConnectorsInfo(static_cast(that)[1], callbackRAD->orgLegacyGetAtomObjectTableForType, *props, connectors, sz); ++ else ++ DBGLOG("rad", "legacy getConnectorsInfo failed %X or undefined %d", code, props == nullptr); + +- return r; ++ return code; + } + +-bool RAD::wrapLegacyATIControllerStart(IOService *ctrl, IOService *provider) { +- DBGLOG("rad", "starting legacy controller " PRIKADDR, CASTKADDR(current_thread())); +- if (callbackRAD->forceVesaMode) { +- DBGLOG("rad", "disabling legacy video acceleration on request"); +- return false; +- } ++uint32_t RAD::wrapTranslateAtomConnectorInfoV1(void *that, RADConnectors::AtomConnectorInfo *info, RADConnectors::Connector *connector) { ++ uint32_t code = FunctionCast(wrapTranslateAtomConnectorInfoV1, callbackRAD->orgTranslateAtomConnectorInfoV1)(that, info, connector); ++ ++ if (code == 0 && info && connector) { ++ RADConnectors::print(connector, 1); ++ ++ uint8_t sense = getSenseID(info->i2cRecord); ++ if (sense) { ++ DBGLOG("rad", "translateAtomConnectorInfoV1 got sense id %02X", sense); ++ ++ // We need to extract usGraphicObjIds from info->hpdRecord, which is of type ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT: ++ // struct ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT { ++ // uint8_t ucNumberOfSrc; ++ // uint16_t usSrcObjectID[ucNumberOfSrc]; ++ // uint8_t ucNumberOfDst; ++ // uint16_t usDstObjectID[ucNumberOfDst]; ++ // }; ++ // The value we need is in usSrcObjectID. The structure is byte-packed. ++ ++ uint8_t ucNumberOfSrc = info->hpdRecord[0]; ++ for (uint8_t i = 0; i < ucNumberOfSrc; i++) { ++ auto usSrcObjectID = *reinterpret_cast(info->hpdRecord + sizeof(uint8_t) + i * sizeof(uint16_t)); ++ DBGLOG("rad", "translateAtomConnectorInfoV1 checking %04X object id", usSrcObjectID); ++ if (((usSrcObjectID & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT) == GRAPH_OBJECT_TYPE_ENCODER) { ++ uint8_t txmit = 0, enc = 0; ++ if (getTxEnc(usSrcObjectID, txmit, enc)) ++ callbackRAD->autocorrectConnector(getConnectorID(info->usConnObjectId), getSenseID(info->i2cRecord), txmit, enc, connector, 1); ++ break; ++ } ++ } ++ ++ ++ } else { ++ DBGLOG("rad", "translateAtomConnectorInfoV1 failed to detect sense for translated connector"); ++ } ++ } ++ ++ return code; ++} + +- callbackRAD->currentLegacyPropProvider.set(provider); +- bool r = FunctionCast(wrapLegacyATIControllerStart, callbackRAD->orgLegacyATIControllerStart)(ctrl, provider); +- DBGLOG("rad", "starting legacy legacy controller done %d " PRIKADDR, r, CASTKADDR(current_thread())); +- callbackRAD->currentLegacyPropProvider.erase(); ++uint32_t RAD::wrapTranslateAtomConnectorInfoV2(void *that, RADConnectors::AtomConnectorInfo *info, RADConnectors::Connector *connector) { ++ uint32_t code = FunctionCast(wrapTranslateAtomConnectorInfoV2, callbackRAD->orgTranslateAtomConnectorInfoV2)(that, info, connector); ++ ++ if (code == 0 && info && connector) { ++ RADConnectors::print(connector, 1); ++ ++ uint8_t sense = getSenseID(info->i2cRecord); ++ if (sense) { ++ DBGLOG("rad", "translateAtomConnectorInfoV2 got sense id %02X", sense); ++ uint8_t txmit = 0, enc = 0; ++ if (getTxEnc(info->usGraphicObjIds, txmit, enc)) ++ callbackRAD->autocorrectConnector(getConnectorID(info->usConnObjectId), getSenseID(info->i2cRecord), txmit, enc, connector, 1); ++ } else { ++ DBGLOG("rad", "translateAtomConnectorInfoV2 failed to detect sense for translated connector"); ++ } ++ } ++ ++ return code; ++} + +- return r; ++bool RAD::wrapATIControllerStart(IOService *ctrl, IOService *provider) { ++ DBGLOG("rad", "starting controller " PRIKADDR, CASTKADDR(current_thread())); ++ if (callbackRAD->forceVesaMode) { ++ DBGLOG("rad", "disabling video acceleration on request"); ++ return false; ++ } ++ ++ callbackRAD->currentPropProvider.set(provider); ++ bool r = FunctionCast(wrapATIControllerStart, callbackRAD->orgATIControllerStart)(ctrl, provider); ++ DBGLOG("rad", "starting controller done %d " PRIKADDR, r, CASTKADDR(current_thread())); ++ callbackRAD->currentPropProvider.erase(); ++ ++ return r; ++} ++ ++bool RAD::wrapLegacyATIControllerStart(IOService *ctrl, IOService *provider) { ++ DBGLOG("rad", "starting legacy controller " PRIKADDR, CASTKADDR(current_thread())); ++ if (callbackRAD->forceVesaMode) { ++ DBGLOG("rad", "disabling legacy video acceleration on request"); ++ return false; ++ } ++ ++ callbackRAD->currentLegacyPropProvider.set(provider); ++ bool r = FunctionCast(wrapLegacyATIControllerStart, callbackRAD->orgLegacyATIControllerStart)(ctrl, provider); ++ DBGLOG("rad", "starting legacy legacy controller done %d " PRIKADDR, r, CASTKADDR(current_thread())); ++ callbackRAD->currentLegacyPropProvider.erase(); ++ ++ return r; + } + + IOReturn RAD::findProjectByPartNumber(IOService *ctrl, void *properties) {