mirror of
https://gitlab.freedesktop.org/emersion/libdisplay-info.git
synced 2024-12-25 21:59:08 +01:00
7d5003c5ac
Signed-off-by: Simon Ser <contact@emersion.fr>
149 lines
4.7 KiB
Python
Executable file
149 lines
4.7 KiB
Python
Executable file
#!/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)
|
|
reduced_blanking = "reduced blanking" in rest.lower()
|
|
|
|
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,
|
|
"reduced_blanking": "true" if reduced_blanking else "false",
|
|
}
|
|
|
|
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)))
|