Skip to content

Commit

Permalink
Brightness control logic w/ Catalina & experimental HDR/OLED support
Browse files Browse the repository at this point in the history
Co-authored-by: Zormeister <[email protected]>
Signed-off-by: Visual Ehrmanntraut <[email protected]>
  • Loading branch information
VisualEhrmanntraut and Zormeister committed Mar 12, 2024
1 parent 047b926 commit d6554a3
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 24 deletions.
7 changes: 7 additions & 0 deletions NootedRed/AMDCommon.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,13 @@ static const UInt32 ddiCapsRenoir[16] = {0x800005, 0x500011FE, 0x80000, 0x110010

static const UInt32 ravenDevAttrFlags = 0x49;

constexpr UInt8 DC_DPCD_EXT_CAPS_SDR_SUPPORTS_AUX = 0x1;
constexpr UInt8 DC_DPCD_EXT_CAPS_HDR_SUPPORTS_AUX = 0x2;
constexpr UInt8 DC_DPCD_EXT_CAPS_OLED = 0x10;

constexpr UInt32 DC_SIGNAL_TYPE_LVDS = 0x8;
constexpr UInt32 DC_SIGNAL_TYPE_EDP = 0x80;

//---- Golden Settings ----//

static const CAILIPGoldenRegister gcGoldenSettingsRaven[] = {
Expand Down
3 changes: 1 addition & 2 deletions NootedRed/NRed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ void NRed::processPatcher(KernelPatcher &patcher) {
bool backlightBootArg = false;
PE_parse_boot_argn("AMDBacklight", &backlightBootArg, sizeof(backlightBootArg));
this->enableBacklight =
getKernelVersion() != KernelVersion::Catalina &&
(backlightBootArg || BaseDeviceInfo::get().modelType == WIOKit::ComputerModel::ComputerLaptop);
backlightBootArg || BaseDeviceInfo::get().modelType == WIOKit::ComputerModel::ComputerLaptop;
if (!this->enableBacklight) {
kextBacklight.switchOff();
kextMCCSControl.switchOff();
Expand Down
96 changes: 82 additions & 14 deletions NootedRed/X6000FB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,26 @@ bool X6000FB::processKext(KernelPatcher &patcher, size_t id, mach_vm_address_t s
SolveRequestPlus solveRequest {"__ZL20CAIL_ASIC_CAPS_TABLE", orgAsicCapsTable, kCailAsicCapsTablePattern};
PANIC_COND(!solveRequest.solve(patcher, id, slide, size), "X6000FB", "Failed to resolve CAIL_ASIC_CAPS_TABLE");

bool sonoma144 = getKernelVersion() > KernelVersion::Sonoma ||
(getKernelVersion() == KernelVersion::Sonoma && getKernelMinorVersion() >= 4);

if (NRed::callback->enableBacklight) {
SolveRequestPlus solveRequest {"_dce_driver_set_backlight", this->orgDceDriverSetBacklight,
kDceDriverSetBacklight};
if (sonoma144) {
SolveRequestPlus solveRequest {"_dc_link_set_backlight_level", this->orgDcLinkSetBacklightLevel,
kDcLinkSetBacklightLevelPattern14_4};
PANIC_COND(!solveRequest.solve(patcher, id, slide, size), "X6000FB",
"Failed to resolve dc_link_set_backlight_level");
} else {
SolveRequestPlus solveRequest {"_dc_link_set_backlight_level", this->orgDcLinkSetBacklightLevel,
kDcLinkSetBacklightLevelPattern};
PANIC_COND(!solveRequest.solve(patcher, id, slide, size), "X6000FB",
"Failed to resolve dc_link_set_backlight_level");
}

SolveRequestPlus solveRequest {"_dc_link_set_backlight_level_nits", this->orgDcLinkSetBacklightLevelNits,
kDcLinkSetBacklightLevelNitsPattern, kDcLinkSetBacklightLevelNitsMask};
PANIC_COND(!solveRequest.solve(patcher, id, slide, size), "X6000FB",
"Failed to resolve dce_driver_set_backlight");
"Failed to resolve dc_link_set_backlight_level_nits");
}

bool ventura = getKernelVersion() >= KernelVersion::Ventura;
Expand Down Expand Up @@ -80,8 +95,7 @@ bool X6000FB::processKext(KernelPatcher &patcher, size_t id, mach_vm_address_t s

if (NRed::callback->enableBacklight) {
RouteRequestPlus requests[] = {
{"_dce_panel_cntl_hw_init", wrapDcePanelCntlHwInit, this->orgDcePanelCntlHwInit,
kDcePanelCntlHwInitPattern},
{"_link_create", wrapLinkCreate, this->orgLinkCreate, kLinkCreatePattern, kLinkCreateMask},
{"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25setAttributeForConnectionEijm",
wrapSetAttributeForConnection, this->orgSetAttributeForConnection},
{"__ZN35AMDRadeonX6000_AmdRadeonFramebuffer25getAttributeForConnectionEijPm",
Expand Down Expand Up @@ -263,11 +277,6 @@ void X6000FB::registerDispMaxBrightnessNotif() {
OSSafeReleaseNULL(matching);
}

UInt32 X6000FB::wrapDcePanelCntlHwInit(void *panelCntl) {
callback->panelCntlPtr = panelCntl;
return FunctionCast(wrapDcePanelCntlHwInit, callback->orgDcePanelCntlHwInit)(panelCntl);
}

IOReturn X6000FB::wrapSetAttributeForConnection(IOService *framebuffer, IOIndex connectIndex, IOSelect attribute,
uintptr_t value) {
auto ret = FunctionCast(wrapSetAttributeForConnection, callback->orgSetAttributeForConnection)(framebuffer,
Expand All @@ -279,16 +288,33 @@ IOReturn X6000FB::wrapSetAttributeForConnection(IOService *framebuffer, IOIndex
return kIOReturnSuccess;
}

if (!callback->panelCntlPtr) {
DBGLOG("X6000FB", "setAttributeForConnection: May not control backlight at this time; panelCntl is null");
if (callback->embeddedPanelLink == nullptr) {
DBGLOG("X6000FB",
"setAttributeForConnection: May not control backight at this time; embeddedPanelLink is nullptr");
return kIOReturnSuccess;
}

//! Set the backlight
callback->curPwmBacklightLvl = static_cast<UInt32>(value);
UInt32 percentage = callback->curPwmBacklightLvl * 100 / callback->maxPwmBacklightLvl;
UInt32 pwmValue = percentage >= 100 ? 0x1FF00 : ((percentage * 0xFF) / 100) << 8U;
callback->orgDceDriverSetBacklight(callback->panelCntlPtr, pwmValue);

bool out;

//! AMDGPU doesn't use AUX on HDR/SDR displays that can use it. Why?
if (callback->supportsAux) {
//! TODO: Obtain the actual max brightness for the screen
UInt32 auxValue = (callback->maxOLED * percentage) / 100;
//! dc_link_set_backlight_level_nits doesn't print the new backlight level, so we'll do it
DBGLOG("X6000FB", "setAttributeForConnection: New AUX brightness: %d millinits (%d nits)", auxValue,
(auxValue / 1000));
out = callback->orgDcLinkSetBacklightLevelNits(callback->embeddedPanelLink, callback->isHDR, auxValue, 15000);
} else {
UInt32 pwmValue = percentage >= 100 ? 0x1FF00 : ((percentage * 0xFF) / 100) << 8U;
out = callback->orgDcLinkSetBacklightLevel(callback->embeddedPanelLink, pwmValue, 0);
}

DBGLOG_COND(!out, "X6000FB", "Failed to set backlight level");

return kIOReturnSuccess;
}

Expand Down Expand Up @@ -350,3 +376,45 @@ void X6000FB::wrapDpReceiverPowerCtrl(void *link, bool power_on) {
FunctionCast(wrapDpReceiverPowerCtrl, callback->orgDpReceiverPowerCtrl)(link, power_on);
IOSleep(250); //! Link needs a bit of delay to change power state
}

void *X6000FB::wrapLinkCreate(void *data) {
void *ret = FunctionCast(wrapLinkCreate, callback->orgLinkCreate)(data);
UInt32 signalType = getMember<UInt32>(ret, 0x38);
if (signalType == DC_SIGNAL_TYPE_LVDS || signalType == DC_SIGNAL_TYPE_EDP) {
callback->embeddedPanelLink = ret;
UInt32 fieldBase = 0;
switch (getKernelVersion()) {
case KernelVersion::Catalina:
fieldBase = 0x1EA;
break;
case KernelVersion::BigSur:
fieldBase = 0x26C;
break;
case KernelVersion::Monterey:
fieldBase = 0x284;
break;
case KernelVersion::Ventura... KernelVersion::Sonoma:
fieldBase = 0x28C;
break;
default:
PANIC("X6000FB", "Unsupported kernel version %d", getKernelVersion());
}
if (getMember<UInt8>(ret, fieldBase) & DC_DPCD_EXT_CAPS_OLED) {
DBGLOG("X6000FB", "Display is OLED, using AUX brightness control");
callback->supportsAux = true;
callback->isHDR = true;
} else if (getKernelVersion() == KernelVersion::Catalina &&
getMember<UInt8>(ret, fieldBase) & DC_DPCD_EXT_CAPS_HDR_SUPPORTS_AUX) {
DBGLOG("X6000FB", "Display supports AUX and we are on Catalina, enabling AUX control.");
callback->supportsAux =
true; //! dc_link_set_brightness_nits or somewhere along the chain will boot us out of setting it
callback->isHDR = true;
} else if (getKernelVersion() == KernelVersion::Catalina &&
getMember<UInt8>(ret, fieldBase) & DC_DPCD_EXT_CAPS_SDR_SUPPORTS_AUX) {
DBGLOG("X6000FB", "Display supports AUX and we are on Catalina, enabling AUX control.");
callback->supportsAux =
true; //! dc_link_set_brightness_nits or somewhere along the chain will boot us out of setting it
}
}
return ret;
}
37 changes: 29 additions & 8 deletions NootedRed/X6000FB.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
#include <IOKit/IOService.h>
#include <IOKit/graphics/IOGraphicsTypes.h>

using t_DceDriverSetBacklight = void (*)(void *panelCntl, UInt32 backlightPwm);
using t_MessageAccelerator = IOReturn (*)(void *that, UInt32 requestType, void *arg2, void *arg3, void *arg4);
using t_DcLinkSetBacklightLevel = bool (*)(void *link, UInt32 backlightPwm, UInt32 frameRamp);
using t_DcLinkSetBacklightLevelNits = bool (*)(void *link, UInt32 backlightMillinits, bool isHDR,
UInt32 transitionTimeMs);

class X6000FB {
friend class NRed;
Expand All @@ -18,24 +20,27 @@ class X6000FB {
bool processKext(KernelPatcher &patcher, size_t id, mach_vm_address_t slide, size_t size);

private:
t_DceDriverSetBacklight orgDceDriverSetBacklight {nullptr};
mach_vm_address_t orgDcePanelCntlHwInit {0};
mach_vm_address_t orgSetAttributeForConnection {0}, orgGetAttributeForConnection {0};
UInt32 curPwmBacklightLvl {0}, maxPwmBacklightLvl {0xFFFF};
void *panelCntlPtr {nullptr};
UInt32 maxOLED {1000 * 512};
void *embeddedPanelLink {nullptr};
bool supportsAux {false};
bool isHDR {false};
IONotifier *dispNotif {nullptr};
mach_vm_address_t orgGetNumberOfConnectors {0};
mach_vm_address_t orgIH40IVRingInitHardware {0}, orgIRQMGRWriteRegister {0};
t_MessageAccelerator orgMessageAccelerator {nullptr};
mach_vm_address_t orgControllerPowerUp {0};
mach_vm_address_t orgDpReceiverPowerCtrl {0};
mach_vm_address_t orgLinkCreate {0};
t_DcLinkSetBacklightLevel orgDcLinkSetBacklightLevel {0};
t_DcLinkSetBacklightLevelNits orgDcLinkSetBacklightLevelNits {0};

static bool OnAppleBacklightDisplayLoad(void *target, void *refCon, IOService *newService, IONotifier *notifier);
void registerDispMaxBrightnessNotif();

static UInt16 wrapGetEnumeratedRevision();
static IOReturn wrapPopulateVramInfo(void *that, void *fwInfo);
static UInt32 wrapDcePanelCntlHwInit(void *panelCntl);
static IOReturn wrapSetAttributeForConnection(IOService *framebuffer, IOIndex connectIndex, IOSelect attribute,
uintptr_t value);
static IOReturn wrapGetAttributeForConnection(IOService *framebuffer, IOIndex connectIndex, IOSelect attribute,
Expand All @@ -45,6 +50,7 @@ class X6000FB {
static void wrapIRQMGRWriteRegister(void *ctx, UInt64 index, UInt32 value);
static UInt32 wrapControllerPowerUp(void *that);
static void wrapDpReceiverPowerCtrl(void *link, bool power_on);
static void *wrapLinkCreate(void *data);
};

//------ Patterns ------//
Expand All @@ -69,16 +75,31 @@ static const UInt8 kIH40IVRingInitHardwarePattern[] = {0x55, 0x48, 0x89, 0xE5, 0
static const UInt8 kIH40IVRingInitHardwareMask[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF};

static const UInt8 kDcePanelCntlHwInitPattern[] = {0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41,
0x54, 0x53, 0x50, 0x49, 0x89, 0xFD, 0x4C, 0x8D, 0x45, 0xD4, 0x41, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00};

static const UInt8 kIRQMGRWriteRegisterPattern[] = {0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41,
0x54, 0x53, 0x50, 0x41, 0x89, 0xD6, 0x49, 0x89, 0xF7, 0x48, 0x89, 0xFB, 0x48, 0x8B, 0x87, 0xB0, 0x00, 0x00, 0x00,
0x48, 0x85, 0xC0};

static const UInt8 kDpReceiverPowerCtrl[] = {0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x54, 0x53, 0x48,
0x83, 0xEC, 0x10, 0x89, 0xF3, 0xB0, 0x02, 0x28, 0xD8};

static const UInt8 kLinkCreatePattern[] = {0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53,
0x48, 0x81, 0xEC, 0x00, 0x03, 0x00, 0x00, 0x49, 0x89, 0xFD, 0x48, 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8B,
0x00, 0x48, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00};
static const UInt8 kLinkCreateMask[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00};

static const UInt8 kDcLinkSetBacklightLevelPattern[] = {0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55,
0x41, 0x54, 0x53, 0x50, 0x41, 0x89, 0xD6, 0x41, 0x89, 0xF4};
//! 14.4 has an extra `mov` operation
static const UInt8 kDcLinkSetBacklightLevelPattern14_4[] = {0x55, 0x48, 0x89, 0xE5, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55,
0x41, 0x54, 0x53, 0x50, 0x89, 0xD3, 0x41, 0x89, 0xF6, 0x49, 0x89, 0xFC};

static const UInt8 kDcLinkSetBacklightLevelNitsPattern[] = {0x55, 0x48, 0x89, 0xE5, 0x53, 0x50, 0x40, 0x88, 0x75, 0x00,
0x48, 0x85, 0xFF, 0x74, 0x00};
static const UInt8 kDcLinkSetBacklightLevelNitsMask[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00};

//------ Patches ------//

//! Fix register read (0xD31 -> 0xD2F) and family ID (0x8F -> 0x8E).
Expand Down

0 comments on commit d6554a3

Please sign in to comment.