HDR, wide-gamut colour, and NVIDIA feature parity for Wayland compositors that
have no colour management — COSMIC, wlroots, and anything else that hasn't
implemented wp-color-management-v1 yet.
Most Linux desktops can't drive an HDR or wide-gamut display because the
compositor never programs the display's colour pipeline. kms-hdr does it
directly through the kernel's atomic KMS API — the same DRM colour properties a
compositor would use — so you get vivid, calibrated HDR output today, without
waiting for the compositor to catch up.
┌─ kms-hdr ──────────────────────────────────────────────────────────────────┐
│ DEGAMMA (sRGB→linear) → CTM (BT.709 → BT.2020/DCI-P3 × saturation × white │
│ balance) → GAMMA (linear → midtone γ → highlight roll-off → black lift → PQ)│
│ → HDR_OUTPUT_METADATA (ST 2084) + Colorspace=BT2020_RGB + max_bpc │
└─────────────────────────────────────────────────────────────────────────────┘
- Compositors gate colour management. On Wayland the compositor owns the
display pipeline. If it doesn't implement
wp-color-management-v1(COSMIC doesn't yet), nothing can put the panel into HDR or a wide gamut.kms-hdrtakes DRM master and programs the CRTC'sDEGAMMA_LUT/CTM/GAMMA_LUT/HDR_OUTPUT_METADATA/Colorspaceproperties itself. - NVIDIA exposes no atomic colour properties. The proprietary driver has no
CTM/DEGAMMA_LUT/GAMMA_LUTatomic props — so on NVIDIA the only path is legacy per-channel gamma ramps (drmModeCrtcSetGamma).kms-hdrdetects the GPU and folds the entire transform (white balance, gamma, tone-map) into those ramps so NVIDIA users still get HDR. - It's real colour science. Correct sRGB→linear de-gamma, a BT.709→BT.2020
matrix, linear-light saturation, a CCT-derived white point, ST 2084 (PQ)
encoding, and a BT.2390-style highlight roll-off — all quantised into 16-bit
LUTs and committed atomically (with a
TEST_ONLYdry-run so a rejected state never flashes the screen).
This is a Cargo workspace monorepo with one shared source of truth.
| Path | What |
|---|---|
core/ |
The C injector (kms-hdr.c, libdrm) — the kernel-facing colour pipeline, daemon, polkit policy, systemd unit. |
crates/kms-hdr-core |
Shared Rust library: config I/O, EDID parsing, GPU detection, calibration profiles, injector invocation. Pure std, no heavy deps. Unit-tested. |
crates/kms-hdr-cal |
Native GTK4 calibration wizard (below). |
docs/ROADMAP.md |
The plan to grow this into a full NVIDIA control panel for COSMIC. |
The libcosmic settings panel and the cosmic-settings integration consume
kms-hdr-core (migration in progress — see the roadmap).
kms-hdr-cal is a ten-step, eyeball-driven wizard that previews live
through the injector — going beyond KDE's single-slider HDR calibration:
- Display info — EDID peak, advertised gamut/EOTF, OLED + GPU pipeline.
- Panel preset — a one-click factory-tuned starting point for the detected panel (see below), then fine-tune the steps that follow.
- Black level — PLUGE-style near-black patches to set the shadow floor (OLED black-crush recovery).
- Peak luminance — the clipping-cross test: lower peak nits until the bright detail just disappears, finding the panel's true peak.
- SDR white reference — how bright SDR/desktop white appears in HDR.
- Gamut & vividness — output gamut + saturation, judged against a skin-tone patch.
- White balance — temperature (CCT) + tint against a neutral grey field. KDE has no white-balance control.
- Midtone gamma — a luminance ramp for contrast vs shadow detail.
- Highlight roll-off — soft BT.2390 knee instead of hard clipping. KDE has none.
- Summary & save — apply and optionally store a named profile.
Profiles are saved under ~/.config/kms-hdr/profiles/ in the same format as the
system config, so any profile can be applied verbatim by the injector.
The wizard reads the EDID manufacturer + product name and recommends a tuned
profile, so a known panel is correct in one click. Each preset is just a starting
[Conf] you can still fine-tune:
| Preset | Panel | Peak | Gamut | Notes |
|---|---|---|---|---|
lg-g6 |
LG G6 OLED (2026) | ~2700 | DCI-P3 | 4-stack tandem WOLED, provisional |
lg-g5 |
LG G5 OLED (2025) | ~2300 | DCI-P3 | Primary RGB Tandem WOLED |
lg-c6 |
LG C6 OLED (2026) | ~1300 | DCI-P3 | evo WOLED |
woled |
Generic WOLED | ~800 | DCI-P3 | LG-display panel, model unknown |
qd-oled |
Generic QD-OLED | ~1000 | BT.2020 | Samsung/Sony QD-OLED |
oled |
Generic OLED | ~700 | DCI-P3 | unknown OLED |
hisense-oled |
Hisense OLED | ~600 | DCI-P3 | — |
oled-desktop |
OLED-safe desktop | ~600 | DCI-P3 | dim SDR white + fast auto-dim, longevity-first |
LG WOLED covers ~96–99% DCI-P3 but only ~72–83% BT.2020, so the LG presets target DCI-P3 — BT.2020 expansion would overshoot what the panel can render. The presets assume the TV's own dynamic tone-mapping is off so the injector owns the PQ roll-off: on LG, set the input icon to PC or enable HGiG (Game Optimiser), otherwise the TV double-maps highlights. 2026 figures (G6/C6) are provisional and err low until measured; the wizard keeps the EDID-reported peak if it's higher.
Panel lottery by size: the bright 4-stack "Primary RGB Tandem" panel is not in every size. G5/G6 are tandem at 55″+ (the 48″ is standard WOLED). The C6 is tandem only at 77″/83″ — 42–65″ C6 is standard WOLED (~1100–1200 nits, C-class), not G-class. So a 65″ C6 behaves like
lg-c6/woled, while a 77″ C6 is closer to the G presets. Peak is a starting point; the clipping-cross step dials it in.
Requires: libdrm, a Rust toolchain, GTK 4, pkexec (polkit).
make # builds the C injector + the Rust workspace
sudo make installThis installs kms-hdr, kms-hdr-cal, hdr-game, the systemd unit, the polkit
policy (active-session, no password), and a desktop entry.
# enable HDR with sane defaults (auto-detects card/connector/EDID peak)
sudo kms-hdr
# tune individual knobs and persist them
sudo kms-hdr --peak-nits 1000 --gamut-mode bt2020 --white-temp 6504 \
--tonemap rolloff --tonemap-knee 60 --save
# run the calibration wizard (applies live, saves on finish)
kms-hdr-cal
# restore SDR
sudo kms-hdr reset
# run as a daemon that re-applies on config change / SIGUSR1
sudo kms-hdr --daemonRun kms-hdr --help for the full flag list.
Used daily on an RTX 5090 (Blackwell) + 4K HDR display under COSMIC. AMD/Intel
get the full atomic DEGAMMA+CTM+GAMMA pipeline; NVIDIA gets the legacy-gamma
fallback. HDR signalling (PQ/BT.2020/DCI-P3) works on both. The
roadmap tracks the path to full NVIDIA feature parity
(RTX-HDR-alike inverse tone-mapping, VRR, sharpening, per-app profiles).
GPL-3.0-or-later.