Generate custom EDID binaries with proper CVT 1.1 timing for non-standard display resolutions.
Useful for LED video walls (NovaStar, Linsn, Colorlight), digital signage, and any display that needs a custom resolution on Linux/Wayland where xrandr --newmode is not available.
Wayland compositors (GNOME/Mutter, KDE/KWin) don't support adding custom display modes at runtime. The only way to use a non-standard resolution is to provide an EDID binary that advertises it, injected via the kernel's drm.edid_firmware parameter.
This tool generates spec-compliant EDID binaries with timing values that exactly match the VESA CVT 1.1 standard (verified against libxcvt/cvt).
Python 3.6+ (no external dependencies).
# Standard CVT timing (recommended)
python3 edid-gen.py 3840 648
# Custom refresh rate
python3 edid-gen.py 3840 648 50
# CVT Reduced Blanking (lower bandwidth, tighter timing margins)
python3 edid-gen.py 3840 648 -r
# Custom monitor name and output path
python3 edid-gen.py 3840 648 -n "NovaStar LED" -o /tmp/novastar.binOutput:
Mode: 3840x648 59.87 Hz (CVT)
Pixel clock: 199.50 MHz
Modeline "3840x648_60.00" 199.50 3840 4000 4392 4944 648 651 661 674 -hsync +vsync
Horizontal: 3840 + fp=160 + sync=392 + bp=552 = 4944 total (blank=1104)
Vertical: 648 + fp=3 + sync=10 + bp=13 = 674 total (blank=26)
EDID written to /tmp/3840x648.bin (128 bytes, checksum OK)
After generating the EDID binary:
# 1. Copy to firmware directory
sudo mkdir -p /lib/firmware/edid
sudo cp /tmp/3840x648.bin /lib/firmware/edid/
# 2. Find your display connector
ls /sys/class/drm/ | grep -E "HDMI|DP"
# 3. Add kernel parameter (replace HDMI-A-1 with your connector)
sudo grubby --update-kernel=ALL \
--args="drm.edid_firmware=HDMI-A-1:edid/3840x648.bin"
# 4. Reboot
sudo reboot
# 5. Verify
cat /sys/class/drm/card*-HDMI-A-*/modesThe generated modelines match cvt (libxcvt) output exactly:
# Compare against system cvt
python3 edid-gen.py 1920 1080 | grep Modeline
cvt 1920 1080 60 | grep Modeline
# Identical outputEDID binaries pass edid-decode validation with zero errors and zero warnings:
python3 edid-gen.py 3840 648 -o /tmp/test.bin
edid-decode /tmp/test.binThe EDID Detailed Timing Descriptor has encoding limits. The tool validates these and rejects values that can't be encoded:
| Field | Max value |
|---|---|
| Horizontal active/blanking | 4095 pixels (12-bit) |
| Vertical active/blanking | 4095 lines (12-bit) |
| H sync offset/width | 1023 pixels (10-bit) |
| V sync offset/width | 63 lines (6-bit) |
| Pixel clock | 655.35 MHz (16-bit, 10 kHz units) |
Resolutions that exceed these limits (e.g., 5120x2880) will still show the modeline but won't generate an EDID binary.
| Mode | H blanking | Bandwidth | Use when |
|---|---|---|---|
Standard (default) |
Large (~1100px for 3840 wide) | Higher | Display needs generous timing margins (LED processors, older hardware) |
Reduced (-r) |
Fixed 160px | Lower | Display handles tight timing (modern LCDs, bandwidth-limited connections) |
For LED video processors (NovaStar, etc.), always use standard timing — the larger blanking periods give the processor more time to synchronize, avoiding pixel offset issues.
The tool implements the VESA CVT 1.1 algorithm (from the CVT calculation spreadsheet by Graham Loveridge), matching the reference implementation in libxcvt. Key steps:
- Round width to character cell granularity (8 pixels)
- Determine V sync lines from aspect ratio (4:3=4, 16:9=5, 16:10=6, 5:4=7, other=10)
- Calculate blanking duty cycle from the GTF secondary curve
- Compute H/V blanking, sync, and porch values
- Round pixel clock to nearest 250 kHz step
- Encode into a 128-byte EDID 1.3 base block with proper descriptors
The EDID binary includes all four required descriptors:
- DTD 1: The custom timing (preferred mode)
- Monitor name: Configurable via
-nflag - Range limits: Computed from the timing parameters
- Dummy descriptor: Required by EDID 1.3 spec
Copyright (C) 2026 Pau Aliagas linuxnow@gmail.com
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.