Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save HarryAnkers/62da15b4957d9ace6cd72275777e8c19 to your computer and use it in GitHub Desktop.

Select an option

Save HarryAnkers/62da15b4957d9ace6cd72275777e8c19 to your computer and use it in GitHub Desktop.
Headless 4K120 HDR virtual display on NVIDIA + Wayland (CachyOS) — June 2026 status: HDR/4K60/native-res work, 4K120 blocked by FRL bring-up on sink-less connector

Headless 4K120 HDR virtual display on NVIDIA + Wayland — June 2026 status

Goal: a completely headless Linux box that presents a virtual display and streams 4K 120Hz HDR to a Mac/iPad over Sunshine → Moonlight, with nothing physically plugged into the GPU. This is a running log of how far that gets on current NVIDIA + KDE Wayland, what works, and exactly where it hits a wall.

TL;DR: HDR works. 4K60 works. Arbitrary native resolutions work. 120Hz does not — the NVIDIA driver refuses to bring up HDMI 2.1 FRL (or DP DSC) on a force-enabled connector that has no real sink doing the link handshake, so anything needing more than ~600 MHz TMDS (i.e. 4K120) gets pruned to 4K60. Filed upstream at NVIDIA/open-gpu-kernel-modules#1184.

Setup

  • GPU: RTX 3060 Ti (Ampere / GA104)
  • Driver: nvidia-open 610.43.02
  • OS: CachyOS, kernel 7.0.11-cachyos
  • Desktop: KDE Plasma 6.6 on Wayland
  • Streaming: Sunshine 2026.516.143833 → Moonlight (Mac / iPad)
  • No monitor attached — the target is a headless streaming host.

How the virtual display is created

A connector is force-enabled at boot with a custom EDID baked into the initramfs, via the kernel drm.edid_firmware mechanism (no dummy plug, no write-protected hacks):

# /etc/default/limine  (Limine cmdline)
KERNEL_CMDLINE[default]+=" drm.edid_firmware=HDMI-A-1:edid/macipad-hdr.bin video=HDMI-A-1:e"
# /etc/mkinitcpio.conf  — EDID must be bundled into the initramfs
FILES=(/usr/lib/firmware/edid/macipad-hdr.bin)

Then sudo limine-update && sudo mkinitcpio -P && reboot. The connector comes up connected with the modes the EDID advertises, even with nothing plugged in.

Sunshine captures it via the KMS (DRM) backend and encodes HEVC Main10 for HDR. HDR is toggled at stream start with kscreen-doctor output.HDMI-A-1.hdr.enable.

✅ What works

  • HDR end to end. kscreen-doctor output.HDMI-A-1.hdr.enable succeeds and Sunshine encodes true HDR: Color coding: HDR (Rec. 2020 + SMPTE 2084 PQ). Confirmed on the Mac.
  • 4K60 HDR (3840×2160 @ 60, 10-bit, BT.2020 + PQ).
  • Arbitrary native client resolutions. Injected as detailed timings into the EDID so the host framebuffer is pixel-perfect for the client:
    • 3024×1890 @ 60 (MacBook Pro 14", below-notch area) — HDR ✅
    • 2752×2064 @ 60 (iPad Pro M4 13") — HDR ✅

Two hard requirements for HDR to enable

HDR is rejected unless both are true:

  1. The connector is HDMI. DisplayPort virtual connectors reject HDR enable outright (applying config failed! The driver rejected the output configuration).
  2. The EDID is real / well-formed enough. A hand-built synthetic EDID is rejected for HDR even on HDMI. A dumped real-monitor EDID (here an AOC unit with proper HDR Static Metadata + HDMI Forum VSDB + BT2020 colorimetry blocks) is accepted.

So the driver does honor HDR signaling on a sink-less connector — it just demands the sink descriptor look genuine.

❌ What does not work: 4K120 (and 120Hz in general)

No matter what the EDID declares, the force-enabled connector caps at 4K60. 4K120 needs HDMI 2.1 FRL (up to 48 Gbps) or DP DSC, and the driver won't bring either up without a real sink completing the SCDC (HDMI) / DPCD (DP) link-training handshake.

This also caps the custom native modes at 60Hz: 3024×1890 / 2752×2064 @ 120 would need ~740 MHz pixel clock, over the TMDS ceiling, so they hit the same wall and prune to 60.

The smoking gun

Fed the connector an EDID declaring Max_FRL_Rate = 6 (48 Gbps) with VIC 118 (3840×2160@120) in the normal RGB video data block. edid-decode validates it clean, and the kernel reads the FRL declaration back correctly:

$ cat /sys/class/drm/card1-HDMI-A-1/edid | edid-decode
...
Maximum TMDS Character Rate: 600 MHz
Max Fixed Rate Link: 3 and 6 Gbps per lane on 3 lanes, 6, 8, 10 and 12 Gbps on 4 lanes

Despite reading that, the driver only ever offers 3840x2160@50/59/60 and prunes 4K120. So it is not a malformed-EDID problem — the driver sees the FRL capability and still refuses to bring up the high-bandwidth link because nothing is physically attached to negotiate it.

Why this looks like driver policy, not a hardware limit

  • gamescope --backend headless renders 4K120 and HDR fine with no connector involved at all — so the GPU and NVENC can clearly do it.
  • amdgpu does not impose this restriction on virtual displays.

It is specific to NVIDIA's connector-mode validation on a sink-less output.

Capture-side dead end (for completeness)

Tried bypassing KMS by rendering into a headless gamescope and capturing that instead. Doesn't work with Sunshine: Sunshine's wlroots/KWin capture paths need zwlr_screencopy_manager_v1 + zxdg_output_manager_v1, and headless gamescope exposes neither — only gamescope_pipewire. Bridging that is real feature work, not a config.

The custom-EDID technique (reusable)

To add native client resolutions without breaking HDR: take a real HDR monitor EDID and overwrite its unused Dummy Descriptor slots in the base block with CVT reduced-blanking detailed timings, then fix only the base-block checksum. The CTA-861 extension (HDR static metadata, HDMI Forum VSDB, BT2020) is left byte-for-byte intact, so HDR keeps working.

# CVT reduced-blanking modelines used (≈371 MHz, safely under the TMDS ceiling):
3024x1890R  371.25  3024 3072 3104 3184  1890 1893 1899 1944 +hsync -vsync
2752x2064R  370.75  2752 2800 2832 2912  2064 2067 2071 2123 +hsync -vsync

Result (edid-decode of the patched EDID):

DTD 1:  1920x1080   60.00 Hz   (preferred)
DTD 2:  3024x1890   59.98 Hz   371.25 MHz   <- injected
DTD 3:  2752x2064   59.97 Hz   370.75 MHz   <- injected
...HDR Static Metadata: SMPTE ST2084, Hybrid Log-Gamma
...Colorimetry: BT2020RGB

The ask (upstream)

For headless / virtual-display streaming: let the driver honor an EDID-declared FRL/DSC capability on a force-enabled connector, or expose a way to set the virtual connector's max link rate. Right now the only route to 4K120 is to physically plug a real 4K120 display in — which defeats the entire point of a headless streaming box.

Links

@alexdilley

Copy link
Copy Markdown

Great write-up, @HarryAnkers! I also stream to an OLED iPad Pro so am keen to see this issue progress to eventually enable HDR 4K120 on Linux. I've not even been successful with this with a physical display connected (re your other gist).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment