libdisplay-info/tool/gen-dmt.py

148 lines
4.6 KiB
Python
Raw Permalink Normal View History

#!/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_pixel_param(l):
l = l[2].split(" ")
assert(len(l) >= 2 and l[1] == "Pixels")
return int(l[0])
def parse_line_param(l):
l = l[1].split(" ")
assert(len(l) >= 2 and l[1] == "lines")
return int(l[0])
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"])
pixel_clock_mhz = float(params["Pixel Clock"][0].split(";")[0])
horiz_blank = parse_pixel_param(params["Hor Blank Time"])
horiz_front_porch = parse_pixel_param(params["// H Front Porch"])
horiz_sync_pulse = parse_pixel_param(params["Hor Sync Time"])
horiz_border = parse_pixel_param(params["// H Right Border"])
assert(horiz_border == parse_pixel_param(params["// H Left Border"]))
vert_blank = parse_line_param(params["Ver Blank Time"])
vert_front_porch = parse_line_param(params["// V Front Porch"])
vert_sync_pulse = parse_line_param(params["Ver Sync Time"])
vert_border = parse_line_param(params["// V Bottom Border"])
assert(vert_border == parse_line_param(params["// V Top Border"]))
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,
"pixel_clock_hz": int(pixel_clock_mhz * 1000 * 1000),
"horiz_blank": horiz_blank,
"horiz_front_porch": horiz_front_porch,
"horiz_sync_pulse": horiz_sync_pulse,
"horiz_border": horiz_border,
"vert_blank": vert_blank,
"vert_front_porch": vert_front_porch,
"vert_sync_pulse": vert_sync_pulse,
"vert_border": vert_border,
}
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)))