AxlEdid — Display EDID Parser
A pure decoder for the 128-byte VESA E-EDID base block a display
publishes (via EFI_EDID_DISCOVERED_PROTOCOL under UEFI — see
axl_gfx_get_edid — or any transport that yields the raw bytes).
axl_edid_parse validates the EDID header signature and the
base-block checksum, then decodes monitor identity (manufacturer /
product / serial / name), EDID version, physical image size, and the
panel’s native timing (resolution + pixel clock) from Detailed Timing
Descriptor #1. axl_edid_dpi derives display DPI from the native
resolution and physical size.
Because it takes a caller-supplied byte buffer and never touches
firmware, the decode logic unit-tests against canned blobs with no
hardware. Extension blocks (CEA-861, DisplayID) are counted via
extension_count but not decoded.
Header: <axl/axl-edid.h>
API Reference
VESA E-EDID base-block parser.
Decodes the 128-byte EDID base block a display publishes (via EFI_EDID_DISCOVERED_PROTOCOL under UEFI, or any other transport that yields the raw bytes) into a flat AxlEdidInfo: monitor identity, EDID version, physical size, and the panel’s native timing.
This module is a pure decoder — it takes a caller-supplied byte buffer and never touches firmware, so it unit-tests against canned blobs with zero hardware. The display-side plumbing that obtains the bytes lives in AxlGfx (axl_gfx_get_edid); keeping the parse separate is what makes the decode logic testable.
Scope: the EDID 1.x/2.x base block (the first 128 bytes). Extension blocks (CEA-861, DisplayID) are counted via extension_count but not decoded — they carry HDMI/audio/extra timing data outside what AXL’s display layer needs today.
Defines
-
AXL_EDID_BLOCK_SIZE
Size of the EDID base block.
axl_edid_parserequires at least this many bytes; extension blocks (if any) follow it.
-
AXL_EDID_STRING_MAX
Capacity (including NUL) of the monitor name / serial strings, which EDID descriptor blocks carry as up to 13 ASCII bytes.
Functions
-
int axl_edid_parse(const uint8_t *edid, size_t len, AxlEdidInfo *out)
Parse an EDID base block into
out.Validates the 8-byte EDID header signature (
00 FF FF FF FF FF FF 00) and the base-block checksum (all 128 bytes sum to 0 mod 256) before decoding — a buffer that fails either check is rejected so callers get a clean “this is a real EDID” signal rather than garbage decoded from random bytes.Only the base block is read;
lenmay be larger (extension blocks present) but must be at least AXL_EDID_BLOCK_SIZE. The number of extension blocks is reported via AxlEdidInfo::extension_count but their contents are not parsed.The native-timing fields (AxlEdidInfo::native_width etc.) come from Detailed Timing Descriptor #1 (the preferred timing per the EDID spec). If the first descriptor slot holds a display descriptor instead of a timing (pixel clock == 0), those fields are left 0.
- Parameters:
edid – raw EDID bytes (base block + optional extensions)
len – length of
edidin bytes (>= AXL_EDID_BLOCK_SIZE)out – [out] decoded fields (untouched on error)
- Returns:
AXL_OK on a valid base block (
outfully populated), AXL_ERR ifedidoroutis NULL,len< AXL_EDID_BLOCK_SIZE, the header signature is wrong, or the checksum fails.outis left untouched on error.
-
int axl_edid_dpi(const AxlEdidInfo *info, uint32_t *dpi_x, uint32_t *dpi_y)
Compute display DPI from a parsed EDID.
DPI is derived from the native pixel resolution and the physical image size:
dpi = round(pixels * 25.4 / mm), computed independently per axis. Uses the Detailed Timing #1 resolution and image-size-in-mm fields, which describe the same rectangle, so the ratio is meaningful.Either out parameter may be NULL if the caller wants only one axis.
- Parameters:
info – parsed EDID
dpi_x – [out, optional] horizontal DPI
dpi_y – [out, optional] vertical DPI
- Returns:
AXL_OK with the requested axes filled, or AXL_ERR if
infois NULL or the EDID lacks a usable native resolution or image size (any of native_width / native_height / image_width_mm / image_height_mm is 0) — in which case the out parameters are untouched.
-
struct AxlEdidInfo
- #include <axl-edid.h>
Decoded fields from an EDID base block.
Numeric fields are 0 when the EDID does not specify them (e.g. a display with no Detailed Timing Descriptor leaves native_width / native_height at 0). String fields are empty ([0] == \0’`) when the corresponding descriptor block is absent.
Public Members
-
char manufacturer[4]
PNP vendor ID (3 letters + NUL), e.g. “DEL”. Decoded faithfully from the packed 5-bit fields — a non-conformant block that still passes the checksum could yield a non-letter (a 0 field decodes to ‘@’).
-
uint16_t product_code
manufacturer product code (bytes 10-11)
-
uint32_t serial_number
numeric serial (bytes 12-15); 0 if unset
-
uint8_t manufacture_week
week of manufacture (1-54), 0 if unspecified, 0xFF = model year
-
uint16_t manufacture_year
full year (1990 + raw byte 17), 0 if unspecified
-
uint8_t version
EDID structure version (byte 18)
-
uint8_t revision
EDID structure revision (byte 19)
-
bool digital
true if a digital input (byte 20 bit 7), false if analog
-
uint16_t native_width
native horizontal resolution from Detailed Timing #1, px (0 if none)
-
uint16_t native_height
native vertical resolution from Detailed Timing #1, px (0 if none)
-
uint32_t native_pixel_clock_khz
Detailed Timing #1 pixel clock in kHz (0 if none)
-
uint16_t image_width_mm
Detailed Timing #1 horizontal image size, mm (0 if unknown)
-
uint16_t image_height_mm
Detailed Timing #1 vertical image size, mm (0 if unknown)
-
uint8_t extension_count
number of 128-byte extension blocks following (byte 126)
-
char monitor_name[14u]
Monitor Name descriptor (0xFC), trimmed; “” if absent.
-
char monitor_serial[14u]
Monitor Serial descriptor (0xFF), trimmed; “” if absent.
-
char manufacturer[4]