I have been working on this issue for several days, and I want to share what I have found so far.
So my issue is that the internal display stays at the lowest brightness level and becomes normal after about 3 minutes, and I think
@Amandeep's issue is exactly the same as mine. The only difference is that the lowest brightness level on his machine is "black screen".
In order to figure out which registers are related to the brightness level, I read several data sheets and conducted a number of experiment under Windows and Mac on my laptop. It turns out that Coffee Lake now has three key registers related to backlight control.
Code:
Offset UInt32 Value KBL::Name APL::Name CFL::Name
0xC8250 0x80000000 SBLC_PWM_CTL1 BLC_PWM_CTL ??
0xC8254 0x0001D4C0 SBLC_PWM_CTL2 BLC_PWM_FREQ ??
0xC8258 0x00008BB0 Unused BLC_PWM_DUTY ??
Notes:
1. KBL::Name means the register name in Kaby Lake data sheet.
2. APL::Name means the register name in Apollo Lake data sheet. (https://01.org/sites/default/files/...src-bxt-vol02b-commandreference-registers.pdf, Page 13, 14, 15)
I was able to read from and write to these registers under Windows via R/W Everything, and I found that 0xC8258 holds the value of the actual brightness level and the other two registers values remain unchanged. i.e. when I wrote a new value to 0xC8258, the brightness changed accordingly.
While the register at 0xC8258 is not used on Kaby Lake platform, I found an example usage in the data sheet for Apollo Lake, so I made a hypothesis that these three registers on Coffee Lake platform now have the same meanings and usages as Apollo Lake.
So I made a new SSDT for Device(PNLF) and simply initialized values (hard-coding) for these three registers inside Method (_INI).
Guess what? The brightness level becomes normal (I say normal because the display brightness now stays at 0x8BB0.) during system boot.
But the bad news is the brightness stays at the lowest level again
AFTER the framebuffer driver loads and sets up the internal display.
Hmm, interesting. It seems that the framebuffer driver writes a new value (which is the lowest brightness level) during setting up the display.
So I started to find related functions inside the framebuffer driver that might write new values to this register.
And there are three key functions and we should also keep an eye on their callers.
Code:
// Called by doSetPowerState(), LightUpEDP(), etc.
AppleIntelFramebufferController::hwSetPanelPower(UInt32 arg0)
{
......
AppleIntelFramebufferController::WriteRegister32(arg0, 0xc8254); // 0x3a9e3 (macOS 10.14.1 b1)
AppleIntelFramebufferController::WriteRegister32(arg0, 0xc8258);
......
}
AppleIntelFramebufferController::hwSetBacklight(UInt32 arg0)
AppleIntelFramebufferController::LightUpEDP(AppleIntelFramebuffer* arg0, AppleIntelDisplayPath* arg1, IODetailedTimingInformationV2* arg2)
After investigating these functions and their callers for a while, I started to realized that this backlight issue might be related to the issue I found (see this
post) in the framebuffer driver a few days ago.
I kept researching on it and found that the kernel panic happens because Apple's driver fails to read the DPCD info.
Sample Kernel Logs:
(When the internal 4K display is working with the divide-by-zero patch and CoreDisplayFixup)
Code:
// Inside AppleIntelFramebufferController::GetDPCDInfo()
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB0: OUI for display
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] 38 EC 11 0
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] 0 0 0 0
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB0: Display port config ver is 1.4
// AppleIntelFramebufferController::GetDPCDInfo() returns
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB0: Setting display mode 3840 x 2160 -> 0 x 0 encoded with 0x1 2 bpc with color 1 and range 1
// Inside AppleIntelFramebufferController::SetupClock()
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][ERROR ] Invalid link parameters..re-attempt a DPCD read
// Inside AppleIntelFramebufferController::GetDPCDInfo()
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB0: OUI for display
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] 38 EC 11 0
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] 0 0 0 0
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB0: Display port config ver is 1.4
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][ERROR ] DPCD read re-attempt too failed
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB0: Panel Fitter modeset
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB0: Transitioning wsaa from 0 to 0
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB0: Setting display mode 3840 x 2160 -> 0 x 0 encoded with 0x1 2 bpc with color 1 and range 1
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][ERROR ] Invalid link parameters..re-attempt a DPCD read
Sample Kernel Logs:
(When CoreDisplayFixup is disabled (and therefore the internal 4K display is off) and with HDMI plugged in)
Code:
// Inside AppleIntelFramebufferController::GetDPCDInfo()
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB2: OUI for display
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] 0 0 0 0
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] 0 0 0 0
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB2: Display port config ver is 1.2
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB2 Maximum link rate is 0x14
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB2: Maximum lanes is 4
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB2: Display has 1 downstream ports
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB2: Down stream port number 0 is HDMI
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB2: Max dongle clock=600000000
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB2: Dongle BPC=2
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB2: YUV422 pass throughSupport = 0 YUV420 pass through support = 0 YUV444->YUV422 = 0 and YUV444->YUV420 = 0
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB2: VSC SDP extention [colorimetry_supported] = 0 VSC Ext VESA SDP support = 0 VSC Ext CEA SDP supported = 0 and Sink sleep to wake timeOut request = 0
// AppleIntelFramebufferController::GetDPCDInfo() returns
kernel: (AppleIntelCFLGraphicsFramebuffer) [IGFB][INFO ] FB0: Transitioning wsaa from 0 to 0
So another important function comes to play, and I translated it a little bit.
Code:
// AppleIntelFramebuffer:
// 0x2384: (UInt8) dp port config full ver (e.g. 1.4)
// 0x2385: (UInt8) dp port config minor ver (1.[4])
// 0x2386: (UInt8) dp port config major ver ([1].4)
// 0x2387: (UInt8) maximum link rate
// 0x2388: (UInt8)
// 0x2389: (UInt8) maximum # of lanes
int AppleIntelFramebufferController::GetDPCDInfo(/* hidden arg self */ AppleIntelFramebuffer* arg0, AppleIntelDisplayPath* arg1)
{
r14 = rdi; // self (AppleIntelFramebufferController*)
r12 = rsi; // arg0 (AppleIntelFramebuffer*)
r15 = rdx; // arg1 (AppleIntelDisplayPath*)
// These two local variables contains very important information
UInt64 var_30; // var_30's value is from a global var in the __got section.
UInt64 var_38; // I don't know how this local var is initialized
// I managed to read these two local vars values
// var_30 = 0x683db337149800a1
// var_38 = 0x00000b0000000002
// And you can see from below that these values cause
// this function to return an unsupported value.
// Set dp power state
// ... some code...
// Get OUI for display
// ... some code...
// Get dp port config ver
// ... some code...
// var_3F: UInt8 link rate???
switch (var_3F) // rax = var_3F
{
case 0x1E:
// Some check
rcx = 0x1E;
case 0x14:
// Some check
rcx = 0x14;
case 0x06:
// NO CHECK
rcx = 0x06;
case 0x0A:
// NO CHECK
rcx = 0x0A;
default:
// Unsupported
return failed;
}
// loc_8876:
arg0->field@0x2387 = rcx;
if (some check)
{
// Adjust the maximum link rate
arg0->field@0x2387 = 0xC;
}
kprintf("Maximum link rate is 0x%x\n", rcx);
// var_3E: UInt8 (lower 5 bits) contains maximum # of lanes
rax = var_3E & 0x1F;
arg0->field@0x2389 = rax;
switch (var_3E & 0x1F)
{
case 1:
goto loc_88ea;
case 2:
goto loc_88ea;
case 4:
goto loc_88ea;
default:
// Unsupported
// lanes can only be one of 1, 2 and 4.
return failed;
}
// loc_88ea:
kprintf("Maximum lanes is %d\n", rax);
arg0->field@0x238a = (rax >> 0x6) & 0x1 & 0xFF;
if (arg0->dpVersion == 1.0)
{
arg0->field@0x2388 = 0x0;
}
else
{
arg0->field@0x2388 = rax >> 0x7;
}
... some other code...
}
I learnt that DPCD info is retrieved via the AUX channel and a special I2C-over-AUX protocol is used for the communication.
And Apple does have related functions to do that:
Code:
ReadAUX(), WriteAUX(), RunAUXCommand() (This is the foundation for the first two AUX functions)
ReadI2COverAUX(), WriteI2COverAUX(), RunI2COverAUX() (This is the foundation for the first two I2COverAUX functions)
I am still trying to figure out why Apple's driver fails to get the DPCD info, and I guess
if we could solve the DPCD issue, the brightness issue should go away, because the framebuffer driver stops reading the DPCD info (probably because of too many failures/attempts) when the brightness becomes normal.
- Is is due to the lack of I2C driver (like VoodooI2C.kext)?
I tried VoodooI2C but no luck. VoodooI2C does not officially support Coffee Lake.
Besides this does not make sense to me, as KBL framebuffer driver has the same logic to get DPCD info and we don't have similar issues on KBL laptops.
I am following this thread, and I will post it if I find something new.