#!/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 ", 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)))