Skip to content

[Bug] CFL framebuffer fails first eDP link training on eDP 1.4 rate-table panel (divide-by-zero / black screen); sleep-wake retraining succeeds #2541

@Vakarian15

Description

@Vakarian15

Hardware

Component Detail
Laptop Lenovo Yoga C740-15IML (81TD)
CPU Intel Core i7-10510U (Comet Lake-U)
iGPU Intel UHD 620 (PCI device 8086:9B41)
Panel LG LP156WFC-SPU1, 1920×1080 60 Hz, eDP 1.4
DVMT Pre-allocated 64 MB, CFG Lock unlocked

Software

Component Version
macOS Tahoe 26.5.1 (25F80) — also reproduced on Sequoia 15.x and Sonoma 14.x
OpenCore Latest stable
Lilu 1.7.3 (dortania nightly)
WhateverGreen 1.7.1 (dortania nightly)
SMBIOS MacBookPro16,1

Framebuffer configuration

AAPL,ig-platform-id = 00009B3E   (0x3E9B0000, CFL mobile, 3 connectors)
device-id            = 9B3E0000
framebuffer-patch-enable = 01000000
framebuffer-stolenmem    = 00003001
framebuffer-fbmem        = 00009000
enable-backlight-registers-alternative-fix = 01000000
enable-backlight-smoother = 01000000
enable-dpcd-max-link-rate-fix = 01000000
dpcd-max-link-rate = 0A000000
AAPL00,override-no-connect = <EDID blob, see attached>

Boot args: -v debug=0x100 keepsyms=1 -amfipassbeta -vi2c-force-polling agdpmod=vit9696 watchdog=0

Panel characteristics (verified in Linux)

From Linux i915_display_info (see attached):

[CONNECTOR:106:eDP-1]: status: connected
  DPCD rev: 13
  port_clock=270000, lane_count=2
  [ENCODER:105:DDI A/PHY A]
  mode: 1920x1080 @ 60 Hz, pixel clock 138600 kHz

The panel is on DDI A (matching con0 busId=0x00 in the 0x3E9B0000 framebuffer), 2 lanes, 2.7 Gbps. Linux drives it without issues.

Critical detail: this panel reports MAX_LINK_RATE = 0x00 in DPCD register 0x00A. Per the eDP 1.4 specification, this means the panel uses the SUPPORTED_LINK_RATES table (DPCD 0x00010–0x0001F) instead of the legacy MAX_LINK_RATE register. Linux's i915 driver handles this by parsing the rate table and using LINK_RATE_SET (DPCD 0x00115) for link training instead of LINK_BW_SET.

Symptoms — three distinct failure modes depending on configuration

1. Without enable-dpcd-max-link-rate-fix → Divide-by-zero kernel panic

The CFL framebuffer driver reads MAX_LINK_RATE = 0 from DPCD, passes it into SetupDPTimings, and divides by zero.

panic(cpu 6): Kernel trap at 0x..., type = 0=divide error
  AppleIntelFramebufferController::SetupDPTimings + 0x1a6
  ← SetupClocks + 0x5cf
  ← hwSetMode + 0x295
  ← AppleIntelFramebuffer::setDisplayMode + 0xe46
Panicked task: pid 198: WindowServer

Full panic report: error-1-divide-by-zero.txt

2. With enable-dpcd-max-link-rate-fix + dpcd-max-link-rate = 0x0A → Black screen with backlight

The divide-by-zero is avoided (the fix substitutes 0x0A for the zero value). The driver proceeds to program the pixel clock (confirmed: IOFBCurrentPixelClock = 0x842DE40 = 138.6 MHz in ioreg). An AppleBacklightDisplay object is created for the correct panel (vendor 0x30E4 / product 0x0630). The backlight turns on.

However, the actual eDP link training fails silently — the panel never receives valid pixel data. WindowServer's main thread blocks indefinitely inside the IOGraphics kernel stack waiting for a

modeset completion event that never arrives. After 120 seconds, watchdogd kills WindowServer, which causes a watchdog panic.

From the WindowServer crash stackshot (see error-2-watchdog.txt):

  • Thread 912 (WindowServer main, ws_main_thread): state TH_WAIT|TH_UNINT, blocked inside AppleIntelCFLGraphicsFramebuffer / IOGraphicsFamily, holding the controller mutex.
  • All subsequent calls to system_profiler SPDisplaysDataType and pmset sleepnow also hang, confirming a kernel-level deadlock on the framebuffer lock.

Full ioreg dump showing the successfully programmed but non-functional framebuffer: igpu-e1.txt

3. Without EDID injection (AAPL00,override-no-connect removed) → Boot hang

The driver attempts to read EDID via the eDP AUX channel. On this panel, AUX communication for EDID appears to work (Linux reads it fine), but the Apple driver's AUX transaction stalls during early framebuffer initialization, hanging the boot process before WindowServer even starts. Adding EDID injection reliably bypasses this hang.

The key finding: sleep-wake retraining succeeds

With any configuration that avoids the divide-by-zero panic (i.e., with enable-dpcd-max-link-rate-fix), the internal display lights up perfectly after a lid-close sleep/wake cycle:

  1. Boot with the fix → black screen + backlight (link training failed)
  2. Close the laptop lid → system enters sleep
  3. Wait ~2 minutes, open lid → system wakes
  4. Internal display shows the login screen at 1920×1080 with full GPU acceleration and Metal 3 support

This is 100% reproducible across macOS Tahoe, Sequoia, and Sonoma. It proves:

  • The framebuffer configuration, connector mapping, and DDI port assignment are all correct.
  • The panel hardware and AUX channel are fully functional.
  • The Apple CFL driver can successfully train this panel's eDP link — but only on the re-training path triggered by display wake, not on the initial training path at boot/first modeset.

Root cause hypothesis

The panel uses eDP 1.4 SUPPORTED_LINK_RATES table and does not populate the legacy MAX_LINK_RATE register (reports 0x00). The WEG enable-dpcd-max-link-rate-fix correctly substitutes a valid rate value (0x0A = HBR) to prevent the divide-by-zero. However, the underlying Apple CFL framebuffer driver still uses LINK_BW_SET (DPCD 0x00100) for link training rather than LINK_RATE_SET (DPCD 0x00115). This panel may require LINK_RATE_SET for initial link training but may be more tolerant during re-training (wake path), or the wake path may use a different initialization sequence that happens to work.

The Linux kernel has explicit handling for this case (see recent patches for eDP 1.4 rate table support in both i915 and MSM DP drivers), and the WEG PR #72 mentions awareness of this issue but may not fully address the initial training path in the CFL driver.

Attempted configurations (all tested, none fix the initial boot)

Configuration Result
ig-platform-id 0x3E9B0000 only Divide-by-zero panic
+ enable-dpcd-max-link-rate-fix + dpcd-max-link-rate=0x0A Black screen, watchdog panic
+ enable-dpcd-max-link-rate-fix (no manual rate, auto-probe) Black screen, watchdog panic
+ force-online Kernel deadlock on phantom connectors (unrelated)
+ enable-force-complete-modeset on con0 Black screen, watchdog panic
ig-platform-id 0x3EA50005 + device-id 0x3EA5 Divide-by-zero panic
ig-platform-id 0x3EA50009 + device-id 0x9BC8 Divide-by-zero panic
Any of the above + lid close/open Display works perfectly

Current workaround

A LaunchDaemon that triggers pmset displaysleepnow followed by caffeinate -u 25 seconds after boot, forcing a display sleep/wake cycle that retrains the link. Functional but inelegant.

Attached files

See list below.
Lenovo-Yoga-C740-15IML-OpenCore-Hackintosh
linux-i915-display-info.txt
error-1-divide-by-zero.txt
error-2-watchdog.txt
igpu-e1.txt
igpu-e1a-auto-probe.txt
ioreg-fb0.txt
linux-dmesg-edp.txt

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions