EDID

From OSDev Wiki
Jump to navigation Jump to search

This page or section refers to its readers or editors using I, my, we or us. It should be edited to be in an encyclopedic tone.

Extended Display Identification Data (EDID) is data about the monitor communicated to the video card's BIOS by a monitor's control hardware. It contains a lot of information, most notably probably being the "preferred" or "native" resolutions that that monitor supports. Where VBE can return a list of all software-supported video modes, EDID will provide special modes supported by the hardware. This usually includes "extended" modes like 1920x1080 or 4K. Getting a "preferred" video mode from the EDID can be an important step towards providing a satisfying desktop experience for an end user, as they can customize their "preferred" mode to their liking and save it for future desktop sessions. Note that the video mode(s) recieved from EDID querying will still need to be converted to a VBE video mode ID through the VBE supported mode list! You won't just be provided a video mode ID like some more simple methods of setting up video.

The first step in understanding the EDID record is to actually, well, GET it. The VESA VBE/DDC extension provides the means by which you will achieve this. For some background, VESA is an organization behind a lot of very important low-level graphics standards, including SVGA, DisplayID, E-EDID, and the VBE family of extensions. You will likely find some kind of VESA compliant video hardware in every video-capable machine introduced since the 90s. However, the specific VESA API this article is going to discuss is the VBE/DDC API, which stands for the Display Data Channel VESA BIOS Extension. This provides a standard way to get detailed video information, including the EDID block. The VBE extension family mostly uses BIOS interrupt 0x10 with AH=0x4F to communicate, and the DDC extension specifically uses AH=0x4F and AL=0x15. The two functions this article requires are as follows:

int 0x10 AX=0x4F15 BL=0x00 - VBE/DDC - INSTALLATION CHECK

int 0x10 AX=0x4F15 BL=0x01 - VBE/DDC - READ EDID

There is a secret, third DDC function called READ VDIF, though documentation for it is sparse, even in the standard itself; it is meant to give you the format that display information will be passed to you in, but EDID is the only format from that standard that has survived the test of time, so you really do not have to worry about running into more specifications.

Getting the EDID Structure

First, you must check to make sure that the VBE/DDC extension is even supported by your BIOS, some do not support it even if they support SVGA! To do this, the first listed function should be used. While this also returns capability information that you could theoretically parse through, it usually is not particularly useful and can be safely ignored. However, it is a very quick function, so it can be used as an efficient installation check. You could theoretically skip the installation check and just rely on the return value of the READ EDID function, but this runs the risk of running into uninitialized memory and causing problems internally--as far as I know, the installation check is far more widely supported, even on BIOSes that don't have true VBE/DDC support.

In NASM assembly, you would invoke the installation check like this:

mov ax, 0x4F15
xor bx, bx

; While not listed in Ralf Brown's interrupt list, the below sneaky requirements
; ARE listed in the standard, and must be set on strictly-compliant machines.
xor cx, cx ; Controller unit 0 (primary)
; ES:DI MUST be a null pointer, for reasons known only to the standards body.
mov es, bx
xor di, di

int 0x10
cmp al, 0x4F
jne .vbe_ddc_not_supported
cmp ah, 0x00
jne .vbe_ddc_install_check_error

If this code executes successfully, you have a VBE/DDC-compatible BIOS. Now, you must actually get the EDID structure. This can be achieved in a very similar way:

mov ax, 0x4F15
mov bl, 0x01
xor dx, dx

; If you're storing the EDID on a segment other than the first (0x0000) you
; should set that here.

mov di, edid_block

int 0x10
cmp al, 0x4F
jne .vbe_ddc_get_edid_unsupported
cmp ah, 0x00
jne .vbe_ddc_get_edid_failure

After this code segment, you will have your EDID information in the location pointed to by ES:DI. This EDID structure is 128-bytes long, and contains the following information (packed):

EDID Structure Anatomy
Offset Size Description Notes
0x00 8 bytes Padding Either all 0xFF or 0x00, 0xFF, ..., 0xFF, 0x00.
0x08 1 word Manufacturer ID Big endian, stored as letter "indices" (0x01 = A, 0x02 = B, etc).
0x0A 1 word Monitor EDID ID Sometimes can identify monitor model.
0x0C 1 dword Serial Number Not always provided, and some manufacturers put this in a strange format.
0x10 1 byte Manufacture Week
0x11 1 byte Manufacture Year Add 1990 to this value to get the actual year.
0x12 1 byte EDID Version
0x13 1 byte EDID Revision
0x14 1 byte Video Input Type This is a bitfield; see the "Extra Tables" section.
0x15 1 byte Maximum Horizontal Size In centimeters.
0x16 1 byte Maximum Vertical Size In centimeters.
0x17 1 byte Gamma Factor The monitor's gamma can be calculated as <1.0 + this/100>.
0x18 1 byte DPMS Flags This is a bitfield; see the "Extra Tables" section.
0x19 10 bytes Chroma Information This is a structure; see the "Extra Tables" section.
0x23 1 byte Established Timings This is a bitfield; see the "Extra Tables" section.
0x24 1 byte 2nd Established Timings This is a bitfield; see the "Extra Tables" section.
0x25 1 byte Manufacturer Timing Cleared to 0x00 if the manufacturer has not reserved a timing.
0x26 8 words Standard Timings1 8 standard modes; see the "Extra Tables" section.
0x36 18 bytes Detailed Timing Description This is a structure; continue the article for anatomy.
0x48 18 bytes Detailed Timing Description This is a structure; continue the article for anatomy.
0x5A 18 bytes Detailed Timing Description This is a structure; continue the article for anatomy.
0x6C 18 bytes Detailed Timing Description This is a structure; continue the article for anatomy.
0x7E 1 byte Unused? Unknown what purpose this byte serves. Maybe reserved?
0x7F 1 byte Checksum Calculated via <256 - the low byte of the structure summed up>.

1: The low byte of each mode descriptor is the resolution, which can be used to calculate the X resolution via <(byte + 31) * 8>.

Most of this information is not particularly useful to many smaller operating system projects. However, the main piece of important information within an EDID record is the first "detailed timing description", widely referred to as the "preferred resolution" for the graphics device. This is the resolution that the user or manufacturer has specified as the most preferable graphics mode for general operation. This is structure can come in two forms; one of which is far less useful to us. Typically, this very first record will ONLY be a "non-text" version, which is much easier to parse and contains much better information. That structure is 18 bytes long, and each item within it is exactly one byte long. Below is its specification;

EDID Detailed Timing Descriptor Anatomy
Offset Size Description Notes
0x00 1 byte Horizontal Frequency In kHz, but if 0x00, the record may be in text form.
0x01 1 byte Vertical Frequency In Hz. This would be the "refresh rate" of your monitor.
0x02 1 byte Horizontal Active Time Measured in pixels, and also referred to as the "X Resolution".
0x03 1 byte Horizontal Blanking Time In Pixels.
0x04 1 byte Horizontal Time Extensions1 The first four bits are for active, and the second four bits are for blanking.
0x05 1 byte Vertical Active Time Measured in lines, and also referred to as the "Y Resolution".
0x06 1 byte Vertical Blanking Time In lines.
0x07 1 byte Vertical Time Extensions1 The first four bits are for active, and the second four bits are for blanking.
0x08 1 byte Horizontal Sync Offset In pixels.
0x09 1 byte Horizontal Sync Pulsewidth In pixels.
0x0A 1 byte Vertical Sync Offset/Pulsewidth2 The first four bits are offset, the second four bits are for pulsewidth.
0x0B 1 byte Sync Offset 2 / Sync Pulsewidth 2 This member is ambiguous; it can either be for the vertical or the horizontal.
0x0C 1 byte Horizontal Image Size In milimeters.
0x0D 1 byte Vertical Image Size In milimeters.
0x0E 1 byte Image Size Extensions
0x0F 1 byte Horizontal Border In pixels, you have to double this to get the full border.
0x10 1 byte Vertical BorderBorder In lines, you have to double this to get the full border.
0x11 1 byte Display Type This is a bitfield, see the "Extra Tables" section.

1: Because resolutions can go higher than 255 pixels, an extension must be made to include those pesky widescreen resolutions like...640x480. To get the full resolution number, you should use the four bits of whichever time you're extending (active/blanking) and use it as the top 4 bits of the resulting 12-bit number. To carry out this operation simply, one would do the following procedure;

  • Move the full byte into the lower half of a register.
  • Move the extension byte into an auxiliary register.
  • If you're planning on getting the high half of the byte (active time) you'd shift the auxiliary register right by four. If you're planning on getting the low half, you'd bitwise AND the register by 0x0F.
  • Move the auxiliary register into the upper half of the full byte's register.

2: I honestly have zero idea why the standards committee would have possibly decided this was a good idea; thankfully, this information is not very interesting when received from the EDID--better information can be gotten from the SVGA mode descriptor. However, you would theoretically split this byte in half like you would the resolution extension byte1 and then bitwise AND in the second offset/pulsewidth? Although, the second offset/pulsewidth can be either for the vertical or horizontal measurements, so just be careful, and you'd be much better off simply receiving this from SVGA.

Parsing the EDID Structure

Parsing the EDID structure is fairly straightforward, especially in assembly. It is very much preferable to define a structural container of some kind at the location where you're loading the EDID, as you will have to rely on magic offsets much less. Below is an example of a valid NASM EDID structure that makes parsing through the EDID data much more readable and easy;

struc vbe_edid_t
    .padding:                              resb 8
    .manufacturer_id:                      resw 1
    .monitor_id:                           resw 1
    .monitor_serial:                       resd 1
    .manufacturer_week:                    resb 1
    .manufacturer_year:                    resb 1
    .structure_version:                    resb 1
    .structure_revision:                   resb 1
    .video_input_type:                     resb 1
    .max_horizontal_size:                  resb 1
    .max_vertical_size:                    resb 1
    .gamma:                                resb 1
    .video_input_flags:                    resb 1
    .chroma_green_red:                     resb 1
    .chroma_white_blue:                    resb 1
    .chroma_red_y:                         resb 1
    .chroma_red_x:                         resb 1
    .chroma_green_y:                       resb 1
    .chroma_green_x:                       resb 1
    .chroma_blue_y:                        resb 1
    .chroma_blue_x:                        resb 1
    .chroma_white_y:                       resb 1
    .chroma_white_x:                       resb 1
    .timing_table_1:                       resb 1
    .timing_table_2:                       resb 1
    .manufacturer_timing:                  resb 1
    .standard_timing:                      resw 8
    .preferred_horizontal_frequency:       resb 1
    .preferred_vertical_frequency:         resb 1
    .preferred_horizontal_active_pixels:   resb 1
    .preferred_horizontal_blanking_pixels: resb 1
    .preferred_horizontal_pixels_2:        resb 1
    .preferred_vertical_active_lines:      resb 1
    .preferred_vertical_blanking_lines:    resb 1
    .preferred_vertical_lines_2:           resb 1
    .preferred_horizontal_sync_offset:     resb 1
    .preferred_horizontal_sync_pulsewidth: resb 1
    .preferred_vertical_sync_1:            resb 1
    .preferred_vertical_sync_2:            resb 1
    .preferred_horizontal_image_size:      resb 1
    .preferred_vertical_image_size:        resb 1
    .preferred_image_size_2:               resb 1
    .preferred_horizontal_border_pixels:   resb 1
    .preferred_vertical_border_lines:      resb 1
    .preferred_display_type:               resb 1
    .detailed_timing_2:                    resb 18
    .detailed_timing_3:                    resb 18
    .detailed_timing_4:                    resb 18
    .unused:                               resb 1 
    .checksum:                             resb 1 
endstruc

For attribution's sake, that code block was taken from LavenderOS's install media bootloader (written by the same author as this article). To specifically get the preferred resolution's width and height, for the reason of checking against VBE mode numbers or the like, you would call a very simple set of instructions like this:

; Get the X resolution
mov bl, byte [es:vbe_edid + vbe_edid_t.preferred_horizontal_active_pixels]
mov cl, byte[es:vbe_edid + vbe_edid_t.preferred_horizontal_pixels_2]
shr cl, 0x04
mov bh, cl

; Get the Y resolution
mov dl, byte[vbe_edid + vbe_edid_t.preferred_vertical_active_lines]
mov cl, byte[es:vbe_edid + vbe_edid_t.preferred_vertical_lines_2]
shr cl, 0x04
mov dh, cl

Now you have the full EDID record in your program! Make sure to follow the standard's requirements for function interfacing strictly, for even if your emulator is lax, real hardware may not be.

Extra Tables

Video Input Type Bitfield
Bit Description Notes
0 Separate Sync
1 Composite Sync
2 Sync on Green
3-4 Unused? The purpose of this field is unknown--it is likely reserved.
5-6 Voltage Level 00 = 0.700V/0.300V, 01 = 0.714V/0.286V, 10 = 0.100V/0.400V, 11 = reserved
7 Signal Type 0 = Analog, 1 = Digital
DPMS Flag Bitfield
Bit Description Notes
0-2 Unused? The purpose of this field is unknown--it is likely reserved.
3 Display Type 0 = Non-RGB, 1 = RGB
4 Unused? The purpose of this field is unknown--it is likely reserved.
5 Active Off Supported
6 Suspend Supported
7 Standby Supported
Chroma Information Anatomy
Offset Size Description Notes
0x00 1 byte Green X'/Y' and Red X'/Y'
0x01 1 byte White X'/Y' and Blue X'/Y'
0x02 1 byte Red Y
0x03 1 byte Red X
0x04 1 byte Green Y
0x05 1 byte Green X
0x06 1 byte Blue Y
0x07 1 byte Blue X
0x08 1 byte White Y
0x09 1 byte White X
Established Timings 1
Bit Timing Notes
0 720x400 @ 70 Hz Supported by IBM PCs, on VGA this actually indicates 640x480.
1 720x400 @ 88 Hz Supported by XGA2-compatible machines.
2 640x480 @ 60 Hz Supported by all VGA-compatible machines.
3 640x480 @ 67 Hz Supported by the Apple Mac II.
4 640x480 @ 72 Hz Supported by all VESA-compatible machines.
5 640x480 @ 75 Hz Supported by all VESA-compatible machines.
6 800x600 @ 56 Hz Supported by all VESA-compatible machines.
7 800x600 @ 60 Hz Supported by all VESA-compatible machines.
Established Timings 2
Bit Timing Notes
0 800x600 @ 72 Hz Supported by all VESA-compatible machines.
1 800x600 @ 75 Hz Supported by all VESA-compatible machines.
2 832x624 @ 75 Hz Supported by the Apple Mac II.
3 1024x768 @ 87 Hz Interlaced format, supported by the 8514A.
4 1024x768 @ 60 Hz Supported by all VESA-compatible machines.
5 1024x768 @ 70 Hz Supported by all VESA-compatible machines.
6 1024x768 @ 75 Hz Supported by all VESA-compatible machines.
7 1280x1024 @ 75 Hz Supported by all VESA-compatible machines.
Standard Timing Bitfield
Bit Description Notes
0-5 Vertical Refresh Frequency Add 60 to this to get the measurement in Hertz.
6-7 Aspect Ratio You can calculate the Y resolution of the standard timing via <X * this>.

0x00 = Unknown, 0x01 = 0.75, 0x10 = 0.8, 0x11 = 0.5625

Display Type Bitfield
Bit Description Notes
0 Unused? The purpose of this field is unknown--it is likely reserved.
1 Sync Location / Horizontal Sync Polarity Horizontal sync polarity if sync type is digital separate, 0 = negative, 1 = positive.
2 Serrate / Vertical Sync Polarity Vertical sync polarity if sync type is digital separate, 0 = negative, 1 = positive.
3-4 Sync Type 0x00 = Analog composite, 0x01 = Bipolar analog composite, 0x10 = Digital Composite, 0x11 = Digital separate
5-6 Stereo Mode 0x00 = Normal display, 0x01 = Stereo right sync high, 0x10 = Stereo left sync high, 0x11 = Undefined
7 Interlaced

See Also

Articles

External Links