diff --git a/di-edid-decode.c b/di-edid-decode.c index 553a8ea..ab99e50 100644 --- a/di-edid-decode.c +++ b/di-edid-decode.c @@ -6,9 +6,10 @@ #include #include +#include #include #include -#include +#include #include static struct { @@ -57,11 +58,14 @@ print_standard_timing(const struct di_edid_standard_timing *t) int32_t vert_video; const struct di_dmt_timing *dmt; int hbl, vbl, horiz_total, vert_total; - double refresh, horiz_freq_hz, pixel_clock_mhz; + double refresh, horiz_freq_hz, pixel_clock_mhz, pixel_clock_khz; + struct di_gtf_options gtf_options; + struct di_gtf_timing gtf; vert_video = di_edid_standard_timing_get_vert_video(t); dmt = di_edid_standard_timing_get_dmt(t); + printf(" "); if (dmt) { hbl = dmt->horiz_blank - 2 * dmt->horiz_border; vbl = dmt->vert_blank - 2 * dmt->vert_border; @@ -70,15 +74,37 @@ print_standard_timing(const struct di_edid_standard_timing *t) refresh = (double) dmt->pixel_clock_hz / (horiz_total * vert_total); horiz_freq_hz = (double) dmt->pixel_clock_hz / horiz_total; pixel_clock_mhz = (double) dmt->pixel_clock_hz / (1000 * 1000); + + printf("DMT 0x%02x", dmt ? dmt->dmt_id : 0); } else { - /* TODO: GTF and CVT timings */ - refresh = 0; - horiz_freq_hz = 0; - pixel_clock_mhz = 0; + /* TODO: CVT timings */ + + gtf_options = (struct di_gtf_options) { + .h_pixels = t->horiz_video, + .v_lines = vert_video, + .ip_param = DI_GTF_IP_PARAM_V_FRAME_RATE, + .ip_freq_rqd = t->refresh_rate_hz, + .m = DI_GTF_DEFAULT_M, + .c = DI_GTF_DEFAULT_C, + .k = DI_GTF_DEFAULT_K, + .j = DI_GTF_DEFAULT_J, + }; + di_gtf_compute(>f, >f_options); + + hbl = gtf.h_front_porch + gtf.h_sync + gtf.h_back_porch + 2 * gtf.h_border; + vbl = gtf.v_front_porch + gtf.v_sync + gtf.v_back_porch + 2 * gtf.v_border; + horiz_total = gtf.h_pixels + hbl; + vert_total = gtf.v_lines + vbl; + /* Upstream edid-decode rounds the pixel clock to kHz... */ + pixel_clock_khz = round(gtf.pixel_freq_mhz * 1000); + refresh = (pixel_clock_khz * 1000) / (horiz_total * vert_total); + horiz_freq_hz = (pixel_clock_khz * 1000) / horiz_total; + pixel_clock_mhz = pixel_clock_khz / 1000; + + printf("GTF "); } - printf(" "); - printf("DMT 0x%02x:", dmt ? dmt->dmt_id : 0); + printf(":"); printf(" %5dx%-5d", t->horiz_video, vert_video); printf(" %10.6f Hz", refresh); printf(" %s ", standard_timing_aspect_ratio_name(t->aspect_ratio)); diff --git a/gtf.c b/gtf.c new file mode 100644 index 0000000..d2b2d95 --- /dev/null +++ b/gtf.c @@ -0,0 +1,129 @@ +/* + * Copyright 2006-2012 Red Hat, Inc. + * Copyright 2018-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * Originally imported from edid-decode. + */ + +#include + +#include + +/** + * The assumed character cell granularity of the graphics system, in pixels. + */ +#define CELL_GRAN 8.0 +/** + * The size of the top and bottom overscan margin as a percentage of the active + * vertical image. + */ +#define MARGIN_PERC 1.8 +/** + * The minimum front porch in lines (vertical) and character cells (horizontal). + */ +#define MIN_PORCH 1.0 +/** + * The width of the V sync in lines. + */ +#define V_SYNC_RQD 3.0 +/** + * The width of the H sync as a percentage of the total line period. + */ +#define H_SYNC_PERC 8.0 +/** + * Minimum time of vertical sync + back porch interval (µs). + */ +#define MIN_VSYNC_BP 550.0 + +void di_gtf_compute(struct di_gtf_timing *t, const struct di_gtf_options *options) +{ + double c_prime, m_prime, h_pixels_rnd, v_lines_rnd, h_margin, + v_margin, interlace, total_active_pixels, pixel_freq, + h_blank_pixels, total_pixels, v_sync_bp, v_field_rate_rqd, + h_period_est, total_v_lines, v_field_rate_est, h_period, + ideal_duty_cycle, h_freq, ideal_h_period, v_back_porch, h_sync, + h_front_porch; + + /* C' and M' are part of the Blanking Duty Cycle computation */ + c_prime = ((options->c - options->j) * options->k / 256.0) + options->j; + m_prime = options->k / 256.0 * options->m; + + h_pixels_rnd = round(options->h_pixels / CELL_GRAN) * CELL_GRAN; + v_lines_rnd = options->int_rqd ? + round(options->v_lines / 2.0) : + options->v_lines; + h_margin = options->margins_rqd ? + round(h_pixels_rnd * MARGIN_PERC / 100.0 / CELL_GRAN) * CELL_GRAN : + 0; + v_margin = options->margins_rqd ? + round(MARGIN_PERC / 100.0 * v_lines_rnd) : + 0; + interlace = options->int_rqd ? 0.5 : 0; + total_active_pixels = h_pixels_rnd + h_margin * 2; + + switch (options->ip_param) { + case DI_GTF_IP_PARAM_V_FRAME_RATE: + // vertical frame frequency (Hz) + v_field_rate_rqd = options->int_rqd ? + options->ip_freq_rqd * 2 : + options->ip_freq_rqd; + h_period_est = (1.0 / v_field_rate_rqd - MIN_VSYNC_BP / 1000000.0) / + (v_lines_rnd + v_margin * 2 + MIN_PORCH + interlace) * 1000000.0; + v_sync_bp = round(MIN_VSYNC_BP / h_period_est); + total_v_lines = v_lines_rnd + v_margin * 2 + + v_sync_bp + interlace + MIN_PORCH; + v_field_rate_est = 1.0 / h_period_est / total_v_lines * 1000000.0; + h_period = h_period_est / (v_field_rate_rqd / v_field_rate_est); + ideal_duty_cycle = c_prime - m_prime * h_period / 1000.0; + h_blank_pixels = round(total_active_pixels * ideal_duty_cycle / + (100.0 - ideal_duty_cycle) / + (2 * CELL_GRAN)) * 2 * CELL_GRAN; + total_pixels = total_active_pixels + h_blank_pixels; + pixel_freq = total_pixels / h_period; + break; + case DI_GTF_IP_PARAM_H_FREQ: + // horizontal frequency (kHz) + h_freq = options->ip_freq_rqd; + v_sync_bp = round(MIN_VSYNC_BP * h_freq / 1000.0); + ideal_duty_cycle = c_prime - m_prime / h_freq; + h_blank_pixels = round(total_active_pixels * ideal_duty_cycle / + (100.0 - ideal_duty_cycle) / + (2 * CELL_GRAN)) * 2 * CELL_GRAN; + total_pixels = total_active_pixels + h_blank_pixels; + pixel_freq = total_pixels * h_freq / 1000.0; + break; + case DI_GTF_IP_PARAM_H_PIXELS: + // pixel clock rate (MHz) + pixel_freq = options->ip_freq_rqd; + ideal_h_period = (c_prime - 100.0 + + sqrt((100.0 - c_prime) * (100.0 - c_prime) + + 0.4 * m_prime * (total_active_pixels + h_margin * 2) / pixel_freq)) + / 2.0 / m_prime * 1000.0; + ideal_duty_cycle = c_prime - m_prime * ideal_h_period / 1000.0; + h_blank_pixels = round(total_active_pixels * ideal_duty_cycle / + (100.0 - ideal_duty_cycle) / + (2 * CELL_GRAN)) * 2 * CELL_GRAN; + total_pixels = total_active_pixels + h_blank_pixels; + h_freq = pixel_freq / total_pixels * 1000.0; + v_sync_bp = round(MIN_VSYNC_BP * h_freq / 1000.0); + break; + } + + v_back_porch = v_sync_bp - V_SYNC_RQD; + h_sync = round(H_SYNC_PERC / 100.0 * total_pixels / CELL_GRAN) * CELL_GRAN; + h_front_porch = h_blank_pixels / 2.0 - h_sync; + + *t = (struct di_gtf_timing) { + .h_pixels = (int) h_pixels_rnd, + .v_lines = options->v_lines, + .v_sync = V_SYNC_RQD, + .h_sync = (int) h_sync, + .v_front_porch = MIN_PORCH, + .v_back_porch = (int) v_back_porch, + .h_front_porch = (int) h_front_porch, + .h_back_porch = (int) (h_front_porch + h_sync), + .h_border = (int) h_margin, + .v_border = (int) v_margin, + .pixel_freq_mhz = pixel_freq, + }; +} diff --git a/include/libdisplay-info/gtf.h b/include/libdisplay-info/gtf.h new file mode 100644 index 0000000..7a7e582 --- /dev/null +++ b/include/libdisplay-info/gtf.h @@ -0,0 +1,72 @@ +#ifndef DI_GTF_H +#define DI_GTF_H + +#include + +/** + * Low-level API for Generalized Timing Formula Standard version 1.1. + */ + +/** + * Type of frequency parameter used in di_gtf_options.ip_freq_rqd. + */ +enum di_gtf_ip_param { + /* Vertical frame frequency (Hz) */ + DI_GTF_IP_PARAM_V_FRAME_RATE, + /* Horizontal frequency (kHz) */ + DI_GTF_IP_PARAM_H_FREQ, + /* Pixel clock rate (MHz) */ + DI_GTF_IP_PARAM_H_PIXELS, +}; + +/** + * Input options for GTF. + */ +struct di_gtf_options { + /* Number of active image pixels displayed on a line, not including any + * margin */ + int h_pixels; + /* Number of vertical lines in the displayed image */ + int v_lines; + /* Whether margins are required */ + bool margins_rqd; + /* Indicates which frequency parameter is specified in ip_freq_rqd */ + enum di_gtf_ip_param ip_param; + /* Vertical frame frequency (in Hz), horizontal frequency (in kHz) or + * pixel clock rate (in MHz) */ + double ip_freq_rqd; + /* Whether interlaced is required */ + bool int_rqd; + /* Blanking formula gradient */ + double m; + /* Blanking formula offset */ + double c; + /* Blanking formula scaling factor */ + double k; + /* Blanking formula scaling factor weighting */ + double j; +}; + +#define DI_GTF_DEFAULT_M 600.0 +#define DI_GTF_DEFAULT_C 40.0 +#define DI_GTF_DEFAULT_K 128.0 +#define DI_GTF_DEFAULT_J 20.0 + +/** + * Output timing data for GTF. + */ +struct di_gtf_timing { + int h_pixels, v_lines; + int h_sync, v_sync; + int h_front_porch, h_back_porch; + int v_front_porch, v_back_porch; + int h_border, v_border; + double pixel_freq_mhz; /* in mega-hertz */ +}; + +/** + * Compute a timing via the GTF formula. + */ +void di_gtf_compute(struct di_gtf_timing *t, const struct di_gtf_options *options); + +#endif diff --git a/meson.build b/meson.build index 49086f1..2bcbbdc 100644 --- a/meson.build +++ b/meson.build @@ -39,6 +39,7 @@ di_lib = library( 'displayid.c', 'dmt-table.c', 'edid.c', + 'gtf.c', 'info.c', 'log.c', ], diff --git a/test/data/acer-p1276.diff b/test/data/acer-p1276.diff index 9aadd84..70d9d64 100644 --- a/test/data/acer-p1276.diff +++ b/test/data/acer-p1276.diff @@ -1,18 +1,5 @@ --- ref +++ di -@@ -37,9 +37,9 @@ - DMT 0x24: 1280x1024 75.024675 Hz 5:4 79.976 kHz 135.000000 MHz - Apple : 1152x870 75.061550 Hz 192:145 68.681 kHz 100.000000 MHz - Standard Timings: -- GTF : 640x480 119.999084 Hz 4:3 61.800 kHz 52.406000 MHz -- GTF : 800x600 119.999886 Hz 4:3 77.160 kHz 83.950000 MHz -- GTF : 1024x768 119.999931 Hz 4:3 98.760 kHz 139.054000 MHz -+ DMT 0x00: 640x480 0.000000 Hz 4:3 0.000 kHz 0.000000 MHz -+ DMT 0x00: 800x600 0.000000 Hz 4:3 0.000 kHz 0.000000 MHz -+ DMT 0x00: 1024x768 0.000000 Hz 4:3 0.000 kHz 0.000000 MHz - DMT 0x23: 1280x1024 60.019740 Hz 5:4 63.981 kHz 108.000000 MHz - DMT 0x33: 1600x1200 60.000000 Hz 4:3 75.000 kHz 162.000000 MHz - DMT 0x2f: 1440x900 59.887445 Hz 16:10 55.935 kHz 106.500000 MHz @@ -65,23 +65,8 @@ DTD 2: 1366x768 59.789541 Hz 683:384 47.712 kHz 85.500000 MHz Hfront 70 Hsync 143 Hback 213 Hpol P diff --git a/test/data/goldstar-ite6604-hdmi.diff b/test/data/goldstar-ite6604-hdmi.diff index 09d3909..a35a7ce 100644 --- a/test/data/goldstar-ite6604-hdmi.diff +++ b/test/data/goldstar-ite6604-hdmi.diff @@ -1,6 +1,6 @@ --- ref +++ di -@@ -24,12 +24,12 @@ +@@ -24,7 +24,7 @@ DMT 0x09: 800x600 60.316541 Hz 4:3 37.879 kHz 40.000000 MHz DMT 0x10: 1024x768 60.003840 Hz 4:3 48.363 kHz 65.000000 MHz Standard Timings: @@ -9,12 +9,6 @@ DMT 0x09: 800x600 60.316541 Hz 4:3 37.879 kHz 40.000000 MHz DMT 0x10: 1024x768 60.003840 Hz 4:3 48.363 kHz 65.000000 MHz DMT 0x23: 1280x1024 60.019740 Hz 5:4 63.981 kHz 108.000000 MHz - DMT 0x55: 1280x720 60.000000 Hz 16:9 45.000 kHz 74.250000 MHz -- GTF : 1368x769 60.000000 Hz 16:9 47.760 kHz 85.968000 MHz -+ DMT 0x00: 1368x769 0.000000 Hz 16:9 0.000 kHz 0.000000 MHz - DMT 0x52: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz - Detailed Timing Descriptors: - DTD 1: 1920x1080 59.933878 Hz 16:9 66.587 kHz 138.500000 MHz (1000 mm x 550 mm) @@ -40,14 +40,14 @@ Vfront 3 Vsync 5 Vback 22 Vpol P Display Product Name: 'ITE6604' diff --git a/test/data/sun-gh19ps-dvi.diff b/test/data/sun-gh19ps-dvi.diff index 56e9640..e553215 100644 --- a/test/data/sun-gh19ps-dvi.diff +++ b/test/data/sun-gh19ps-dvi.diff @@ -1,16 +1,5 @@ --- ref +++ di -@@ -35,8 +35,8 @@ - DMT 0x24: 1280x1024 75.024675 Hz 5:4 79.976 kHz 135.000000 MHz - Standard Timings: - DMT 0x23: 1280x1024 60.019740 Hz 5:4 63.981 kHz 108.000000 MHz -- GTF : 1280x1024 75.999957 Hz 5:4 81.320 kHz 141.822000 MHz -- GTF : 1152x921 66.000114 Hz 5:4 63.162 kHz 97.017000 MHz -+ DMT 0x00: 1280x1024 0.000000 Hz 5:4 0.000 kHz 0.000000 MHz -+ DMT 0x00: 1152x921 0.000000 Hz 5:4 0.000 kHz 0.000000 MHz - DMT 0x24: 1280x1024 75.024675 Hz 5:4 79.976 kHz 135.000000 MHz - Detailed Timing Descriptors: - DTD 1: 1280x1024 60.019740 Hz 5:4 63.981 kHz 108.000000 MHz (digital composite, serrate, 380 mm x 300 mm) @@ -50,15 +50,4 @@ ----------------