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_parse requires 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; len may 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 edid in bytes (>= AXL_EDID_BLOCK_SIZE)

  • out – [out] decoded fields (untouched on error)

Returns:

AXL_OK on a valid base block (out fully populated), AXL_ERR if edid or out is NULL, len < AXL_EDID_BLOCK_SIZE, the header signature is wrong, or the checksum fails. out is 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 info is 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.