An interactive command-line wizard to generate custom EDID binary files, compliant with VESA and CTA-861 standards.
EDID (Extended Display Identification Data) is a block of binary data that every monitor transmits to the computer to describe its capabilities: supported resolutions, refresh rates, color depth, audio support, and more. The operating system reads this information to automatically configure the video output.
In certain scenarios (custom monitors, KVM emulators, headless setups, virtual displays) it is useful to generate a custom EDID. This tool lets you do so through an interactive wizard, producing a .bin file that can be loaded directly into the Linux kernel.
- Interactive wizard with step-by-step TUI interface
- Preset resolutions (from 640x480 up to 7680x4320) and arbitrary custom resolutions
- Multiple refresh rates per resolution (24-360 Hz and beyond)
- HDMI and DisplayPort interfaces with automatic protocol-specific handling
- Color depth: 6, 8, 10, or 12 bits per channel
- HDR10: BT.2020 chromaticity, ST2084 (PQ) EOTF, static luminance metadata
- HDMI audio: stereo, 5.1 and 7.1 surround (LPCM) with speaker allocation
- HDMI 2.1: Fixed Rate Link (FRL) up to 48 Gbps, Display Stream Compression (DSC 1.2), ALLM
- Native CVT Reduced Blanking v1 timing calculation with no external dependencies
- Optional validation via
edid-decode(if installed)
Requirements: Node.js (version with ES modules support).
npm installnpm run generateThe wizard will guide you through the configuration:
- Display name (max 13 characters)
- Manufacturer ID (3 uppercase letters, e.g.
DEL,SAM,ACR) - Interface: HDMI or DisplayPort
- Color depth: 6, 8, 10, or 12 bpc
- HDR: available when color depth >= 10 bpc
- Audio: none, stereo, 5.1, or 7.1 (HDMI only)
- DPI: used to compute the physical display dimensions
- Resolutions and refresh rates: selectable from VIC presets or entered manually
- Preferred mode: the primary resolution/refresh rate for the display
- Output file: path of the
.binfile to generate
Upon completion, the command to load the EDID into the Linux kernel is displayed:
sudo cp output.bin /usr/lib/firmware/edid/
# In /etc/default/grub or your bootloader configuration:
# drm_kms_helper.edid_firmware=HDMI-A-1:edid/output.binnpm testTests verify the correctness of EDID generation, checksum conformance, and (if edid-decode is installed) validation with the reference tool.
An EDID file is composed of one or more 128-byte blocks. The first block is mandatory and contains the display's basic information; subsequent blocks are extensions that add advanced capabilities.
The base block follows the EDID 1.3 (for HDMI) or EDID 1.4 (for DisplayPort) standard and contains:
- Fixed 8-byte header that identifies the file as a valid EDID
- Manufacturer and product ID: 3-letter manufacturer code and device identifier
- Week and year of manufacture
- EDID version: 1.3 or 1.4 depending on the interface (the HDMI specification requires 1.3)
- Basic video parameters: signal type (digital), color depth, interface type
- Physical display dimensions in centimeters, computed from DPI and resolution
- Chromaticity: sRGB color coordinates (or BT.2020 for HDR) describing the display's color gamut
- Established and standard timings: bitmap of supported classic video modes (640x480, 800x600, etc.)
- Descriptors (4 slots of 18 bytes each):
- Preferred DTD: the Detailed Timing Descriptor of the primary resolution/refresh rate, encoding pixel clock, active area, blanking, and sync signals
- Range limits: supported horizontal and vertical frequency range, maximum pixel clock
- Display name: ASCII string up to 13 characters
- Serial number (or an additional DTD)
- Checksum: the last byte is computed so that the sum of all 128 bytes equals 0 modulo 256
The CTA-861 extension (also known as CEA extension) is the standard format for declaring advanced capabilities of HDMI and modern displays. It contains:
- Short Video Descriptor (SVD): a list of VICs (Video Identification Codes), standardized numeric codes from CTA-861 that uniquely identify well-known combinations of resolution, refresh rate, and aspect ratio (e.g. VIC 16 = 1920x1080@60Hz 16:9). Up to 31 VICs per EDID.
- HDMI data block (HDMI Vendor Specific Data Block): declares HDMI-specific capabilities such as Deep Color support (10/12 bit) and maximum TMDS clock.
- HDMI Forum data block (HDMI 2.1): declares Fixed Rate Link (FRL) support with the appropriate bandwidth level, Display Stream Compression (DSC), and Auto Low Latency Mode (ALLM).
- Audio Data Block: supported codecs (LPCM), channel count, sample rates (32/44.1/48 kHz), and bit depths (16/20/24 bit).
- Speaker Allocation: speaker channel map (front, surround, subwoofer, etc.).
- Video Capability Data Block (VCDB): declares underscan and basic audio support.
- Colorimetry Data Block: supported color spaces (sRGB, BT.2020 for HDR).
- HDR Static Metadata: supported EOTFs (SDR, ST2084/PQ), maximum/minimum luminance values.
- VFPDB and NVRDB: blocks declaring the preferred video format and native resolution, used for 4K and higher resolutions.
- Additional DTDs: detailed timings for video modes that lack a standard VIC, with pixel clocks up to 655.35 MHz.
When more DTDs or data are needed than fit in a single block, additional chained CTA-861 blocks are generated.
For ultra-high-bandwidth video modes whose pixel clock exceeds 655.35 MHz (the limit of the traditional DTD format), the generator uses DisplayID 2.0 blocks containing:
- Display Parameters Data Block: resolution, physical size, chromaticity, luminance, and color depth.
- Type VII Detailed Timing: an extended timing format supporting pixel clocks up to 16+ GHz with 1 kHz resolution, sufficient for any high-refresh-rate 8K mode.
Each DisplayID block can hold multiple timings (3 in the first block, 5 in subsequent ones). If there are many high-bandwidth modes, multiple chained DisplayID blocks are generated.
When the EDID contains more than one data extension and the version is 1.3, a block map (tag 0xF0) is inserted that lists the types of all extensions present, as required by the specification.
+-------------------+
| Base Block | 128 bytes - EDID 1.3 or 1.4
| (header, DTD, | Basic info, preferred timing,
| range, name) | range limits, display name
+-------------------+
| Block Map | 128 bytes - EDID 1.3 with 2+ extensions only
| (optional) | Extension block map
+-------------------+
| CTA-861 #1 | 128 bytes - SVD, HDMI VSDB, audio,
| | HDR, colorimetry, additional DTDs
+-------------------+
| CTA-861 #2..N | 128 bytes - Additional DTDs (if needed)
| (optional) |
+-------------------+
| DisplayID #1 | 128 bytes - Display params + Type VII timing
| (optional) | For modes with pixel clock > 655.35 MHz
+-------------------+
| DisplayID #2..N | 128 bytes - Additional Type VII timings
| (optional) |
+-------------------+
The total number of blocks varies depending on the complexity of the configuration: a 1080p@60Hz HDMI display typically produces 2 blocks (256 bytes), while a 4K display with multiple refresh rates, HDR, and audio can reach 6 or more blocks (768+ bytes).
This project was born out of a specific use case: remote desktop streaming on a Wayland/Hyprland setup with an Nvidia GPU.
The setup consists of 3 physical monitors connected to the GPU plus a DisplayPort dummy plug. When working remotely, the physical monitors are disabled and the dummy plug output is enabled, then streamed to a client via Moonlight + Sunshine.
The reason for the dummy plug is the capture method. On Wayland with Nvidia, the available options are:
- NvFBC — Nvidia's frame buffer capture API, fast and efficient, but not compatible with Wayland.
- wlroots — can capture virtual outputs (no physical connection needed), but in practice caused GPU memory leaks, XWayland breakage, and frequent system crashes.
- KMS — kernel-level capture, stable and reliable, but it can only capture outputs that are physically connected to the GPU. It cannot see virtual outputs.
KMS is the only stable capture method on this setup, but it requires a real signal path from the GPU to a physical connector. A dummy plug satisfies this requirement: the GPU treats it as a real monitor, KMS can capture its framebuffer, and Sunshine streams it to Moonlight. The custom EDID loaded onto the dummy plug output controls what resolutions, refresh rates, and color capabilities are available for the remote session.
When using a DisplayPort connection on Linux, the GPU negotiates the actual link parameters with the receiving device through a process called link training. During this negotiation, the GPU and the device agree on two physical characteristics of the connection: the number of lanes (1, 2, or 4) and the link rate per lane (1.62, 2.7, 5.4, or 8.1 Gbps). The product of these two values determines the total available bandwidth.
Cheap DP dummy plugs, adapters, and some KVM switches often support only a subset of lanes or lower link rates. For instance, a device limited to 2 lanes at 2.7 Gbps provides roughly 5.4 Gbps of total bandwidth — far less than the ~12.5 Gbps required for 4K@60Hz at 8 bpc.
The Nvidia driver compares each mode declared in the EDID against the bandwidth actually available on the link. Any mode that exceeds the negotiated bandwidth is silently discarded, regardless of what the EDID says. This means that loading a custom EDID with 4K@60Hz will have no effect if the physical link can only carry 4K@30Hz worth of data: the mode simply won't appear in the list of available resolutions.
This is not an EDID or a driver bug — it is the expected behavior of the DisplayPort standard, which unlike HDMI does not allow a source to send more data than the link can physically carry.
This behavior has been observed with Nvidia proprietary drivers. Whether Intel or AMD GPUs apply the same kind of filtering is unknown — it may depend on the driver implementation.
Possible workarounds:
- Use a dummy plug or adapter that supports 4 lanes at HBR2 (5.4 Gbps/lane) or higher — this provides ~21.6 Gbps, enough for 4K@120Hz at 8 bpc.
- If the target resolution is not bandwidth-critical, try lowering the refresh rate or color depth to fit within the available link capacity.
- Switch to an HDMI dummy plug if the GPU has an HDMI output: HDMI does not perform this kind of pre-filtering, and the EDID modes will be accepted as declared.
generate-edid.mjs CLI wizard (entry point)
lib/
edid-builder.mjs Full EDID binary assembly
cea-extension.mjs CTA-861 extension block builder
displayid.mjs DisplayID 2.0 extension block builder
timing.mjs DTD encoding, standard timings, range limits
cvt.mjs CVT Reduced Blanking v1 timing calculation
constants.mjs VIC table, audio configurations, FRL levels
tests/
edid.test.mjs Test suite
helpers.mjs Test utilities and edid-decode integration
This software is provided as-is, with no warranty of any kind. Use it at your own risk.
The generated EDID binaries are validated with a test suite and checked against edid-decode where possible, and they have worked correctly in every configuration I have personally tested. That said, this is a side project built to solve a specific personal need — it is not continuously exercised across a wide range of hardware and configurations. Unusual or untested combinations of options may produce malformed EDIDs. If you encounter issues, bug reports and pull requests are welcome.
- akatrevorjay/edid-generator — the original project that inspired this one.
MIT