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) {