Use custom EDID in Linux

Extended Display Identification Data (EDID) is a metadata format for display devices to describe their capabilities such as resolution, display size, timing, bit depth and update frequency. It's a 128-byte (EDID) or 256-byte (Enhanced-EDID) structure transferred from the display device over the Display Data Channel (DDC) protocol, which is a layer on top of the I2C specification.

The EDID is accessible via the I2C address 0x50 and can usually be read even if the display is turned off, which is quite nice.

Before Video Electronics Standard Association (VESA) came up with this standard, there were multiple non-standard ways out there to provide some kind of basic identification for video device.

Handle all these non-standard ways is of course an unmanageable situation. In that good old days we had to explicitly set all graphics parameters in the xorg.conf file.

Hooray for standards!

Read out the EDID structure

The EDID structure is available for DRM (Direct Rendring Manager) devices via sysfs in raw binary format:

$od  -Anone -t x1 /sys/devices/pci0000:00/0000:00:02.0/drm/card1/card1-DP-4/edid
     00 ff ff ff ff ff ff 00 41 0c c9 c0 ae 00 00 00
     1a 17 01 03 80 3c 22 78 2a 25 95 a9 54 4f a1 26
     0a 50 54 bd 4b 00 d1 00 d1 c0 81 80 95 0f 95 00
     b3 00 81 c0 a9 40 56 5e 00 a0 a0 a0 29 50 30 20
     35 00 55 50 21 00 00 1e 00 00 00 ff 00 41 55 34
     31 33 32 36 30 30 30 31 37 34 00 00 00 fc 00 50
     68 69 6c 69 70 73 20 32 37 32 43 34 00 00 00 fd
     00 32 4c 1e 63 21 00 0a 20 20 20 20 20 20 00 ac

read_edid [1] provide some tools to retrieve and interpret monitor specifications using the VESA DDC protocol. parse-edid is part of this package and we can use it to parse the EDID structure above:

$ parse-edid < /sys/devices/pci0000:00/0000:00:02.0/drm/card1/card1-DP-4/edid
Checksum Correct

Section "Monitor"
    Identifier "Philips 272C4"
    ModelName "Philips 272C4"
    VendorName "PHL"
    # Monitor Manufactured week 26 of 2013
    # EDID version 1.3
    # Digital Display
    DisplaySize 600 340
    Gamma 2.20
    Option "DPMS" "true"
    Horizsync 30-99
    VertRefresh 50-76
    # Maximum pixel clock is 330MHz
    #Not giving standard mode: 1920x1200, 60Hz
    #Not giving standard mode: 1920x1080, 60Hz
    #Not giving standard mode: 1280x1024, 60Hz
    #Not giving standard mode: 1440x900, 75Hz
    #Not giving standard mode: 1440x900, 60Hz
    #Not giving standard mode: 1680x1050, 60Hz
    #Not giving standard mode: 1280x720, 60Hz
    #Not giving standard mode: 1600x1200, 60Hz
    Modeline        "Mode 0" +hsync +vsync
EndSection

This is the EDID for my Philips Monitor.

Provide custom EDID structure to DRM

I'm working with a custom projector (yes, projectors are display devices too) board for an embedded Linux system. Unfortunately, the processor has an errata for the DDC channel which causes the retrieved EDID structure to be corrupt, so I have to manually provide EDID information for the DRM layer.

For such situations, the kernel has introduced the CONFIG_DRM_LOAD_EDID_FIRMWARE configuration item. It let you provide a individually prepared EDID data in the lib/firmware directory to be loaded instead of retrieving it on the DDC channel. The functionality is disabled by default as it's mostly a workaround for broken hardware, and you will, luckily enough, have to search hard to find such a hardware these days.

The sources also contains a few built-in [2] structures for commonly used screen resolutions for us to use:

#define GENERIC_EDIDS 6
static const char * const generic_edid_name[GENERIC_EDIDS] = {
    "edid/800x600.bin",
    "edid/1024x768.bin",
    "edid/1280x1024.bin",
    "edid/1600x1200.bin",
    "edid/1680x1050.bin",
    "edid/1920x1080.bin",
};

See the kernel documentation [3] for more details.

Use the custom EDID structure

We could either place our custom EDID data in /lib/firmware/edid/ or use one of those build-in structures. Either way, pass drm_kms_helper.edid_firmware pointing to the right structure as argument to the kernel.

Example on bootargs that use the built-in 800x600 EDID structure:

drm_kms_helper.edid_firmware=edid/800x600.bin

Here is my projector in action showing the Qt Analog Clock [4] example.

/media/edid-clock.jpg

(Yes, crappy image, it looks much better IRL)