dmt: introduce generated table for DMT data

The Display Monitor Timing specification is a PDF with data tables.
A small Python script can be used to extract its contents in a
machine-readable form.

For now only extract a few fields. This can be extended later.

Signed-off-by: Simon Ser <contact@emersion.fr>
This commit is contained in:
Simon Ser 2022-06-22 13:04:13 +02:00
parent 676984a737
commit 0cc784971b
4 changed files with 840 additions and 0 deletions

696
dmt-table.c Normal file
View file

@ -0,0 +1,696 @@
/* DO NOT EDIT! This file has been generated by gen-dmt.py from DMT-r1-v13.pdf. */
#include "dmt.h"
const struct di_dmt_timing _di_dmt_timings[] = {
{
.dmt_id = 0x01,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 640,
.vert_video = 350,
.refresh_rate_hz = 85.0,
},
{
.dmt_id = 0x02,
.edid_std_id = 0x3119,
.cvt_id = 0,
.horiz_video = 640,
.vert_video = 400,
.refresh_rate_hz = 85.0,
},
{
.dmt_id = 0x03,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 720,
.vert_video = 400,
.refresh_rate_hz = 85.0,
},
{
.dmt_id = 0x04,
.edid_std_id = 0x3140,
.cvt_id = 0,
.horiz_video = 640,
.vert_video = 480,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x05,
.edid_std_id = 0x314C,
.cvt_id = 0,
.horiz_video = 640,
.vert_video = 480,
.refresh_rate_hz = 72.0,
},
{
.dmt_id = 0x06,
.edid_std_id = 0x314F,
.cvt_id = 0,
.horiz_video = 640,
.vert_video = 480,
.refresh_rate_hz = 75.0,
},
{
.dmt_id = 0x07,
.edid_std_id = 0x3159,
.cvt_id = 0,
.horiz_video = 640,
.vert_video = 480,
.refresh_rate_hz = 85.0,
},
{
.dmt_id = 0x08,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 800,
.vert_video = 600,
.refresh_rate_hz = 56.0,
},
{
.dmt_id = 0x09,
.edid_std_id = 0x4540,
.cvt_id = 0,
.horiz_video = 800,
.vert_video = 600,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x0A,
.edid_std_id = 0x454C,
.cvt_id = 0,
.horiz_video = 800,
.vert_video = 600,
.refresh_rate_hz = 72.0,
},
{
.dmt_id = 0x0B,
.edid_std_id = 0x454F,
.cvt_id = 0,
.horiz_video = 800,
.vert_video = 600,
.refresh_rate_hz = 75.0,
},
{
.dmt_id = 0x0C,
.edid_std_id = 0x4559,
.cvt_id = 0,
.horiz_video = 800,
.vert_video = 600,
.refresh_rate_hz = 85.0,
},
{
.dmt_id = 0x0D,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 800,
.vert_video = 600,
.refresh_rate_hz = 120.0,
},
{
.dmt_id = 0x0E,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 848,
.vert_video = 480,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x0F,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 1024,
.vert_video = 768,
.refresh_rate_hz = 43.0,
},
{
.dmt_id = 0x10,
.edid_std_id = 0x6140,
.cvt_id = 0,
.horiz_video = 1024,
.vert_video = 768,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x11,
.edid_std_id = 0x614A,
.cvt_id = 0,
.horiz_video = 1024,
.vert_video = 768,
.refresh_rate_hz = 70.0,
},
{
.dmt_id = 0x12,
.edid_std_id = 0x614F,
.cvt_id = 0,
.horiz_video = 1024,
.vert_video = 768,
.refresh_rate_hz = 75.0,
},
{
.dmt_id = 0x13,
.edid_std_id = 0x6159,
.cvt_id = 0,
.horiz_video = 1024,
.vert_video = 768,
.refresh_rate_hz = 85.0,
},
{
.dmt_id = 0x14,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 1024,
.vert_video = 768,
.refresh_rate_hz = 120.0,
},
{
.dmt_id = 0x15,
.edid_std_id = 0x714F,
.cvt_id = 0,
.horiz_video = 1152,
.vert_video = 864,
.refresh_rate_hz = 75.0,
},
{
.dmt_id = 0x55,
.edid_std_id = 0x81C0,
.cvt_id = 0,
.horiz_video = 1280,
.vert_video = 720,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x16,
.edid_std_id = 0,
.cvt_id = 0x7F1C21,
.horiz_video = 1280,
.vert_video = 768,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x17,
.edid_std_id = 0,
.cvt_id = 0x7F1C28,
.horiz_video = 1280,
.vert_video = 768,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x18,
.edid_std_id = 0,
.cvt_id = 0x7F1C44,
.horiz_video = 1280,
.vert_video = 768,
.refresh_rate_hz = 75.0,
},
{
.dmt_id = 0x19,
.edid_std_id = 0,
.cvt_id = 0x7F1C62,
.horiz_video = 1280,
.vert_video = 768,
.refresh_rate_hz = 85.0,
},
{
.dmt_id = 0x1A,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 1280,
.vert_video = 768,
.refresh_rate_hz = 120.0,
},
{
.dmt_id = 0x1B,
.edid_std_id = 0,
.cvt_id = 0x8F1821,
.horiz_video = 1280,
.vert_video = 800,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x1C,
.edid_std_id = 0x8100,
.cvt_id = 0x8F1828,
.horiz_video = 1280,
.vert_video = 800,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x1D,
.edid_std_id = 0x810F,
.cvt_id = 0x8F1844,
.horiz_video = 1280,
.vert_video = 800,
.refresh_rate_hz = 75.0,
},
{
.dmt_id = 0x1E,
.edid_std_id = 0x8119,
.cvt_id = 0x8F1862,
.horiz_video = 1280,
.vert_video = 800,
.refresh_rate_hz = 85.0,
},
{
.dmt_id = 0x1F,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 1280,
.vert_video = 800,
.refresh_rate_hz = 120.0,
},
{
.dmt_id = 0x20,
.edid_std_id = 0x8140,
.cvt_id = 0,
.horiz_video = 1280,
.vert_video = 960,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x21,
.edid_std_id = 0x8159,
.cvt_id = 0,
.horiz_video = 1280,
.vert_video = 960,
.refresh_rate_hz = 85.0,
},
{
.dmt_id = 0x22,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 1280,
.vert_video = 960,
.refresh_rate_hz = 120.0,
},
{
.dmt_id = 0x23,
.edid_std_id = 0x8180,
.cvt_id = 0,
.horiz_video = 1280,
.vert_video = 1024,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x24,
.edid_std_id = 0x818F,
.cvt_id = 0,
.horiz_video = 1280,
.vert_video = 1024,
.refresh_rate_hz = 75.0,
},
{
.dmt_id = 0x25,
.edid_std_id = 0x8199,
.cvt_id = 0,
.horiz_video = 1280,
.vert_video = 1024,
.refresh_rate_hz = 85.0,
},
{
.dmt_id = 0x26,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 1280,
.vert_video = 1024,
.refresh_rate_hz = 120.0,
},
{
.dmt_id = 0x27,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 1360,
.vert_video = 768,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x28,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 1360,
.vert_video = 768,
.refresh_rate_hz = 120.0,
},
{
.dmt_id = 0x51,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 1366,
.vert_video = 768,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x56,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 1366,
.vert_video = 768,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x29,
.edid_std_id = 0,
.cvt_id = 0x0C2021,
.horiz_video = 1400,
.vert_video = 1050,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x2A,
.edid_std_id = 0x9040,
.cvt_id = 0x0C2028,
.horiz_video = 1400,
.vert_video = 1050,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x2B,
.edid_std_id = 0x904F,
.cvt_id = 0x0C2044,
.horiz_video = 1400,
.vert_video = 1050,
.refresh_rate_hz = 75.0,
},
{
.dmt_id = 0x2C,
.edid_std_id = 0x9059,
.cvt_id = 0x0C2062,
.horiz_video = 1400,
.vert_video = 1050,
.refresh_rate_hz = 85.0,
},
{
.dmt_id = 0x2D,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 1400,
.vert_video = 1050,
.refresh_rate_hz = 120.0,
},
{
.dmt_id = 0x2E,
.edid_std_id = 0,
.cvt_id = 0xC11821,
.horiz_video = 1440,
.vert_video = 900,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x2F,
.edid_std_id = 0x9500,
.cvt_id = 0xC11828,
.horiz_video = 1440,
.vert_video = 900,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x30,
.edid_std_id = 0x950F,
.cvt_id = 0xC11844,
.horiz_video = 1440,
.vert_video = 900,
.refresh_rate_hz = 75.0,
},
{
.dmt_id = 0x31,
.edid_std_id = 0x9519,
.cvt_id = 0xC11868,
.horiz_video = 1440,
.vert_video = 900,
.refresh_rate_hz = 85.0,
},
{
.dmt_id = 0x32,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 1440,
.vert_video = 900,
.refresh_rate_hz = 120.0,
},
{
.dmt_id = 0x53,
.edid_std_id = 0xA9C0,
.cvt_id = 0,
.horiz_video = 1600,
.vert_video = 900,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x33,
.edid_std_id = 0xA940,
.cvt_id = 0,
.horiz_video = 1600,
.vert_video = 1200,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x34,
.edid_std_id = 0xA945,
.cvt_id = 0,
.horiz_video = 1600,
.vert_video = 1200,
.refresh_rate_hz = 65.0,
},
{
.dmt_id = 0x35,
.edid_std_id = 0xA94A,
.cvt_id = 0,
.horiz_video = 1600,
.vert_video = 1200,
.refresh_rate_hz = 70.0,
},
{
.dmt_id = 0x36,
.edid_std_id = 0xA94F,
.cvt_id = 0,
.horiz_video = 1600,
.vert_video = 1200,
.refresh_rate_hz = 75.0,
},
{
.dmt_id = 0x37,
.edid_std_id = 0xA959,
.cvt_id = 0,
.horiz_video = 1600,
.vert_video = 1200,
.refresh_rate_hz = 85.0,
},
{
.dmt_id = 0x38,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 1600,
.vert_video = 1200,
.refresh_rate_hz = 120.0,
},
{
.dmt_id = 0x39,
.edid_std_id = 0,
.cvt_id = 0x0C2821,
.horiz_video = 1680,
.vert_video = 1050,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x3A,
.edid_std_id = 0xB300,
.cvt_id = 0x0C2828,
.horiz_video = 1680,
.vert_video = 1050,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x3B,
.edid_std_id = 0xB30F,
.cvt_id = 0x0C2844,
.horiz_video = 1680,
.vert_video = 1050,
.refresh_rate_hz = 75.0,
},
{
.dmt_id = 0x3C,
.edid_std_id = 0xB319,
.cvt_id = 0x0C2868,
.horiz_video = 1680,
.vert_video = 1050,
.refresh_rate_hz = 85.0,
},
{
.dmt_id = 0x3D,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 1680,
.vert_video = 1050,
.refresh_rate_hz = 120.0,
},
{
.dmt_id = 0x3E,
.edid_std_id = 0xC140,
.cvt_id = 0,
.horiz_video = 1792,
.vert_video = 1344,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x3F,
.edid_std_id = 0xC14F,
.cvt_id = 0,
.horiz_video = 1792,
.vert_video = 1344,
.refresh_rate_hz = 75.0,
},
{
.dmt_id = 0x40,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 1792,
.vert_video = 1344,
.refresh_rate_hz = 120.0,
},
{
.dmt_id = 0x41,
.edid_std_id = 0xC940,
.cvt_id = 0,
.horiz_video = 1856,
.vert_video = 1392,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x42,
.edid_std_id = 0xC94F,
.cvt_id = 0,
.horiz_video = 1856,
.vert_video = 1392,
.refresh_rate_hz = 75.0,
},
{
.dmt_id = 0x43,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 1856,
.vert_video = 1392,
.refresh_rate_hz = 120.0,
},
{
.dmt_id = 0x52,
.edid_std_id = 0xD1C0,
.cvt_id = 0,
.horiz_video = 1920,
.vert_video = 1080,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x44,
.edid_std_id = 0,
.cvt_id = 0x572821,
.horiz_video = 1920,
.vert_video = 1200,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x45,
.edid_std_id = 0xD100,
.cvt_id = 0x572828,
.horiz_video = 1920,
.vert_video = 1200,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x46,
.edid_std_id = 0xD10F,
.cvt_id = 0x572844,
.horiz_video = 1920,
.vert_video = 1200,
.refresh_rate_hz = 75.0,
},
{
.dmt_id = 0x47,
.edid_std_id = 0xD119,
.cvt_id = 0x572862,
.horiz_video = 1920,
.vert_video = 1200,
.refresh_rate_hz = 85.0,
},
{
.dmt_id = 0x48,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 1920,
.vert_video = 1200,
.refresh_rate_hz = 120.0,
},
{
.dmt_id = 0x49,
.edid_std_id = 0xD140,
.cvt_id = 0,
.horiz_video = 1920,
.vert_video = 1440,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x4A,
.edid_std_id = 0xD14F,
.cvt_id = 0,
.horiz_video = 1920,
.vert_video = 1440,
.refresh_rate_hz = 75.0,
},
{
.dmt_id = 0x4B,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 1920,
.vert_video = 1440,
.refresh_rate_hz = 120.0,
},
{
.dmt_id = 0x54,
.edid_std_id = 0xE1C0,
.cvt_id = 0,
.horiz_video = 2048,
.vert_video = 1152,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x4C,
.edid_std_id = 0,
.cvt_id = 0x1F3821,
.horiz_video = 2560,
.vert_video = 1600,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x4D,
.edid_std_id = 0,
.cvt_id = 0x1F3828,
.horiz_video = 2560,
.vert_video = 1600,
.refresh_rate_hz = 60.0,
},
{
.dmt_id = 0x4E,
.edid_std_id = 0,
.cvt_id = 0x1F3844,
.horiz_video = 2560,
.vert_video = 1600,
.refresh_rate_hz = 75.0,
},
{
.dmt_id = 0x4F,
.edid_std_id = 0,
.cvt_id = 0x1F3862,
.horiz_video = 2560,
.vert_video = 1600,
.refresh_rate_hz = 85.0,
},
{
.dmt_id = 0x50,
.edid_std_id = 0,
.cvt_id = 0,
.horiz_video = 2560,
.vert_video = 1600,
.refresh_rate_hz = 120.0,
},
};
const size_t _di_dmt_timings_len = 86;

27
include/dmt.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef DMT_H
#define DMT_H
/**
* Private header for VESA Display Monitor Timing.
*/
#include <stdint.h>
#include <sys/types.h>
struct di_dmt_timing {
/* DMT ID */
uint8_t dmt_id;
/* EDID standard timing 2-byte code, zero if unset */
uint16_t edid_std_id;
/* CVT 3-byte code, zero if unset */
uint32_t cvt_id;
/* Addressable pixels */
int32_t horiz_video, vert_video;
/* Field Refresh Rate in Hz */
float refresh_rate_hz;
};
extern const struct di_dmt_timing _di_dmt_timings[];
extern const size_t _di_dmt_timings_len;
#endif

View file

@ -35,6 +35,7 @@ symbols_flag = '-Wl,--version-script,@0@'.format(meson.current_source_dir() / sy
di_lib = library(
'display-info',
[
'dmt-table.c',
'edid.c',
'info.c',
],

116
tool/gen-dmt.py Executable file
View file

@ -0,0 +1,116 @@
#!/usr/bin/env python3
import os
import subprocess
import sys
def parse_hex_byte(s):
assert(s.endswith("h"))
s = s[:-1]
assert(0 <= int(s, 16) <= 255)
return "0x" + s
def parse_hex_list(s):
if s == "n/a":
return None
if s.startswith("("):
assert(s.endswith(")h"))
s = s[1:-2]
l = [b.strip() for b in s.split(",")]
l = [b[:-1] if b.endswith("h") else b for b in l]
for b in l:
assert(0 <= int(b, 16) <= 255)
return "0x" + "".join(l)
def parse_timing(page):
lines = [l.strip() for l in page.splitlines()]
if len(lines) < 6:
return None
try:
i = lines.index("Detailed Timing Parameters")
except ValueError:
return None
raw_metadata = lines[1:i]
raw_params = lines[i+1:]
metadata = {}
for l in raw_metadata:
if not ": " in l:
continue
k, v = l.split(":", 1)
metadata[k.strip()] = v.strip()
assert("Resolution" in metadata and "EDID ID" in metadata)
if "Proposed" in metadata and not "Adopted" in metadata:
print("skipping proposed but not adopted timing: " + metadata["Resolution"],
file=sys.stderr)
return None
params = {}
for l in raw_params:
if not " = " in l:
continue
tokens = [l.strip() for l in l.split("=")]
params[tokens[0]] = tokens[1:]
assert("Timing Name" in params)
res = metadata["Resolution"]
size, rest = res.split(" at ", 1)
horiz_video, vert_video = size.split("x", 1)
refresh_rate_hz, rest = rest.split(" Hz ", 1)
horiz_video = int(horiz_video.strip())
vert_video = int(vert_video.strip())
refresh_rate_hz = float(refresh_rate_hz.strip())
ids = {}
for kv in metadata["EDID ID"].split(";"):
k, v = kv.split(":")
ids[k.strip()] = v.strip()
assert("DMT ID" in ids)
dmt_id = parse_hex_byte(ids["DMT ID"])
edid_std_id = parse_hex_list(ids["Std. 2 Byte Code"])
cvt_id = parse_hex_list(ids["CVT 3 Byte Code"])
return {
"dmt_id": dmt_id,
"edid_std_id": 0 if edid_std_id is None else edid_std_id,
"cvt_id": 0 if cvt_id is None else cvt_id,
"horiz_video": horiz_video,
"vert_video": vert_video,
"refresh_rate_hz": refresh_rate_hz,
}
if len(sys.argv) != 2:
print("usage: gen-dmt.py <DMT PDF>", file=sys.stderr)
sys.exit(1)
in_path = sys.argv[1]
in_basename = os.path.basename(in_path)
tool_dir = os.path.dirname(os.path.realpath(__file__))
out_path = tool_dir + "/../dmt-table.c"
cmd = ["pdftotext", "-nodiag", "-layout", in_path, "-"]
page = ""
timings = []
for l in subprocess.check_output(cmd, text=True):
if l.startswith("\f"):
t = parse_timing(page)
if t is not None:
timings.append(t)
page = l[1:]
else:
page += l
with open(out_path, "w+") as f:
f.write("/* DO NOT EDIT! This file has been generated by gen-dmt.py from {}. */\n\n".format(in_basename))
f.write('#include "dmt.h"\n\n')
f.write("const struct di_dmt_timing _di_dmt_timings[] = {\n")
for t in timings:
f.write("\t{\n")
for k, v in t.items():
f.write("\t\t.{} = {},\n".format(k, v))
f.write("\t},\n")
f.write("};\n\n")
f.write("const size_t _di_dmt_timings_len = {};\n".format(len(timings)))