summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/cvt.c552
-rw-r--r--src/cvt.h26
2 files changed, 578 insertions, 0 deletions
diff --git a/src/cvt.c b/src/cvt.c
new file mode 100644
index 0000000..9563f0f
--- /dev/null
+++ b/src/cvt.c
@@ -0,0 +1,552 @@
+#include "cvt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Based on the cvt util:
+ * http://www.uruk.org/projects/cvt/cvt.c
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#define CLOCK_STEP 0.25 /* Clock steps in MHz */
+#define MARGIN_PERCENT 1.8 /* % of active vertical image */
+#define H_SYNC_PER 8.0 /* sync % of horizontal image */
+#define CELL_GRAN 8.4999 /* assumed character cell granularity */
+#define CELL_GRAN_RND 8.0 /* assumed character cell granularity (round)*/
+#define MIN_V_BPORCH 3.0 /* width of vsync in lines */
+#define MIN_V_PORCH_RND 3.0 /* width of vsync in lines */
+#define M 600.0 /* blanking formula gradient */
+#define C 40.0 /* blanking formula offset */
+#define K 128.0 /* blanking formula scaling factor */
+#define J 20.0 /* blanking formula scaling factor */
+
+/* Standard Timing Parameters */
+#define MIN_VSYNC_BP 550.0 /* min time of vsync + back porch (us) */
+#define H_SYNC_PERCENT 8.0 /* width of hsync as % of total line */
+
+/* Reduced Blanking defines */
+#define RB_MIN_V_BPORCH 6.0 /* lines */
+#define RB_V_FPORCH 3.0 /* lines */
+#define RB_MIN_V_BLANK 460.0 /* us */
+#define RB_H_SYNC 32.0 /* pixels */
+#define RB_H_BLANK 160.0 /* pixels */
+
+/* C' and M' are part of the Blanking Duty Cycle computation */
+
+#define C_PRIME (((C - J) * K/256.0) + J)
+#define M_PRIME (K/256.0 * M)
+
+/* NOP out prints */
+#define print_value(...) (void)0
+
+typedef struct __options
+{
+ int x, y;
+ int reduced_blank, interlaced;
+ int xf86mode, fbmode;
+ float v_freq;
+} options;
+
+
+/*
+ * vert_refresh() - as defined by the CVT Timing Standard, compute the
+ * Stage 1 Parameters using the vertical refresh frequency. In other
+ * words: input a desired resolution and desired refresh rate, and
+ * output the CVT mode timings.
+ *
+ * XXX margin computations are implemented but not tested (nor used by
+ * XFree86 of fbset mode descriptions, from what I can tell).
+ */
+
+mode *vert_refresh (int h_pixels, int v_lines, float freq,
+ int interlaced, int reduced_blank, int margins)
+{
+ float h_pixels_rnd;
+ float v_lines_rnd;
+ float v_field_rate_rqd;
+ float top_margin;
+ float bot_margin;
+ float interlace;
+ float h_period_est;
+ float v_sync_bp;
+ float total_v_lines;
+ float left_margin;
+ float right_margin;
+ float total_active_pixels;
+ float ideal_duty_cycle;
+ float h_blank;
+ float total_pixels;
+
+ float cur_duty_cycle;
+ float v_sync;
+ float v_sync_rnd, h_sync_rnd;
+ float h_back_porch, v_front_porch, h_front_porch;
+
+ float vbi_lines, act_vbi_lines, rb_min_vbi;
+ float act_pixel_freq, act_h_freq;
+ float act_field_rate, act_frame_rate;
+ char *aspect_ratio;
+ int stage;
+
+ mode *m = (mode*) malloc (sizeof (mode));
+
+
+ /* 1. Required Field Rate
+ *
+ * This is slightly different from the spreadsheet because we use
+ * a different result for interlaced video modes. Simplifies this
+ * to the input field rate.
+ *
+ * [V FIELD RATE RQD] = [I/P FREQ RQD]
+ */
+
+ v_field_rate_rqd = freq;
+
+ print_value(1, "[V FIELD RATE RQD]", v_field_rate_rqd);
+
+
+ /* 2. Horizontal Pixels
+ *
+ * In order to give correct results, the number of horizontal
+ * pixels requested is first processed to ensure that it is divisible
+ * by the character size, by rounding it to the nearest character
+ * cell boundary.
+ *
+ * [H PIXELS RND] = ((ROUNDDOWN([H PIXELS]/[CELL GRAN RND],0))
+ * *[CELLGRAN RND])
+ */
+
+ h_pixels_rnd = floor((float) h_pixels / CELL_GRAN_RND) * CELL_GRAN_RND;
+
+ print_value(2, "[H PIXELS RND]", h_pixels_rnd);
+
+
+ /* 2.5th Calculation, aspect_ratio & v_sync_rnd
+ *
+ * [ASPECT_RATIO] = IF(H_PIXELS_RND = CELL_GRAN_RND*ROUND((V_LINES*
+ * 4.0/3.0)/CELL_GRAN_RND),"4:3")
+ * etc...
+ * [V_SYNC] = [value from table based on aspect ratio]
+ * [V_SYNC_RND] = ROUND(V_SYNC,0) // Not needed in principle
+ */
+
+ if (h_pixels_rnd == CELL_GRAN_RND * floor(((float)v_lines * 4.0 / 3.0)
+ / CELL_GRAN_RND)) {
+ aspect_ratio = "4:3";
+ v_sync = 4;
+ } else if (h_pixels_rnd == CELL_GRAN_RND * floor(((float)v_lines * 16.0
+ / 9.0) / CELL_GRAN_RND)) {
+ aspect_ratio = "16:9";
+ v_sync = 5;
+ } else if (h_pixels_rnd == CELL_GRAN_RND * floor(((float)v_lines * 16.0
+ / 10.0) / CELL_GRAN_RND)) {
+ aspect_ratio = "16:10";
+ v_sync = 6;
+ } else if (h_pixels_rnd == CELL_GRAN_RND * floor(((float)v_lines * 5.0
+ / 4.0) / CELL_GRAN_RND)) {
+ aspect_ratio = "5:4";
+ v_sync = 7;
+ } else if (h_pixels_rnd == CELL_GRAN_RND * floor(((float)v_lines * 15.0
+ / 9.0) / CELL_GRAN_RND)) {
+ aspect_ratio = "15:9";
+ v_sync = 7;
+ } else {
+ /* Default case of unknown aspect ratio */
+ aspect_ratio = "Custom";
+ v_sync = 10;
+ }
+ v_sync_rnd = v_sync;
+
+ /*
+ * 3. Determine Left & Right Borders
+ *
+ * Calculate the margins on the left and right side.
+ *
+ * [LEFT MARGIN (PIXELS)] = (IF( [MARGINS RQD?]="Y",
+ * (ROUNDDOWN( ([H PIXELS RND] * [MARGIN%] / 100 /
+ * [CELL GRAN RND]),0)) * [CELL GRAN RND],
+ * 0))
+ * [RIGHT MARGIN (PIXELS)] = (IF( [MARGINS RQD?]="Y",
+ * (ROUNDDOWN( ([H PIXELS RND] * [MARGIN%] / 100 /
+ * [CELL GRAN RND]),0)) * [CELL GRAN RND],
+ * 0))
+ */
+
+ left_margin = margins ?
+ floor(h_pixels_rnd * MARGIN_PERCENT / 100.0 / CELL_GRAN_RND)
+ * CELL_GRAN_RND : 0.0;
+ right_margin = left_margin;
+
+ print_value(3, "[LEFT MARGIN (PIXELS)]", left_margin);
+ print_value(3, "[RIGHT MARGIN (PIXELS)]", right_margin);
+
+
+ /* 4. Find total active pixels.
+ *
+ * Find total number of active pixels in image and left and right
+ * margins.
+ *
+ * [TOTAL ACTIVE PIXELS] = [H PIXELS RND] + [LEFT MARGIN (PIXELS)] +
+ * [RIGHT MARGIN (PIXELS)]
+ */
+
+ total_active_pixels = h_pixels_rnd + left_margin + right_margin;
+
+ print_value(4, "[TOTAL ACTIVE PIXELS]", total_active_pixels);
+
+
+ /* 5. Find number of lines per field.
+ *
+ * If interlace is requested, the number of vertical lines assumed
+ * by the calculation must be halved, as the computation calculates
+ * the number of vertical lines per field. In either case, the
+ * number of lines is rounded to the nearest integer.
+ *
+ * [V LINES RND] = IF([INT RQD?]="y", ROUNDDOWN([V LINES]/2,0),
+ * ROUNDDOWN([V LINES],0))
+ */
+
+ v_lines_rnd = interlaced ?
+ floor((float) v_lines / 2.0) :
+ floor((float) v_lines);
+
+ print_value(5, "[V LINES RND]", v_lines_rnd);
+
+
+ /* 6. Find Top and Bottom margins.
+ *
+ * [TOP MARGIN (LINES)] = IF([MARGINS RQD?]="Y",
+ * ROUNDDOWN(([MARGIN%]/100*[V LINES RND]),0),
+ * 0)
+ * [BOT MARGIN (LINES)] = IF([MARGINS RQD?]="Y",
+ * ROUNDDOWN(([MARGIN%]/100*[V LINES RND]),0),
+ * 0)
+ */
+
+ top_margin = margins ? floor(MARGIN_PERCENT / 100.0 * v_lines_rnd) : (0.0);
+ bot_margin = top_margin;
+
+ print_value(6, "[TOP MARGIN (LINES)]", top_margin);
+ print_value(6, "[BOT MARGIN (LINES)]", bot_margin);
+
+
+ /* 7. If interlace is required, then set variable [INTERLACE]=0.5:
+ *
+ * [INTERLACE]=(IF([INT RQD?]="y",0.5,0))
+ */
+
+ interlace = interlaced ? 0.5 : 0.0;
+
+ print_value(7, "[INTERLACE]", interlace);
+
+
+ /*
+ * Here it diverges for "reduced blanking" or normal blanking modes.
+ */
+
+ if (reduced_blank) {
+ h_blank = RB_H_BLANK;
+
+
+ /* 8. Estimate Horiz. Period (us).
+ *
+ * [H PERIOD EST] = ((1000000/V_FIELD_RATE_RQD)-RB_MIN_V_BLANK)/(V_LINES_RND+TOP_MARGIN+BOT_MARGIN)
+ */
+
+ h_period_est = (1000000.0/v_field_rate_rqd - RB_MIN_V_BLANK)
+ / (v_lines_rnd + top_margin + bot_margin);
+
+ print_value(8, "[H PERIOD EST]", h_period_est);
+
+
+ /* 9. Find number of lines in vertical blanking.
+ *
+ * [Actual VBI_LINES] = RB_MIN_V_BLANK/H_PERIOD_EST
+ * [VBI_LINES] = ROUNDDOWN(RB_MIN_V_BLANK/H_PERIOD_EST,0) + 1
+ */
+
+ vbi_lines = RB_MIN_V_BLANK/h_period_est;
+ print_value(9, "[Actual VBI LINES]", vbi_lines);
+
+ vbi_lines = floor(vbi_lines) + 1.0;
+ print_value(9, "[VBI LINES]", vbi_lines);
+
+
+ /* 10. Check Vertical Blanking is sufficient.
+ *
+ * [RB MIN VBI] = RB_V_FPORCH+V_SYNC_RND+RB_MIN_V_BPORCH
+ * [ACT VBI LINES] = IF(VBI_LINES<RB_MIN_VBI,RB_MIN_VBI,VBI_LINES)
+ */
+
+ rb_min_vbi = RB_V_FPORCH + v_sync_rnd + RB_MIN_V_BPORCH;
+ act_vbi_lines = (vbi_lines < rb_min_vbi) ? rb_min_vbi : vbi_lines;
+
+ print_value(10, "[Minimum VBI Lines]", rb_min_vbi);
+ print_value(10, "[ACT VBI LINES]", act_vbi_lines);
+
+
+ /* 11. Find total number of lines in vertical field.
+ *
+ * [TOTAL V LINES] = ACT_VBI_LINES+V_LINES_RND+TOP_MARGIN+BOT_MARGIN+INTERLACE
+ */
+
+ total_v_lines = act_vbi_lines + v_lines_rnd + top_margin
+ + bot_margin + interlace;
+
+ print_value(11, "[TOTAL V LINES]", total_v_lines);
+
+
+ /* 12. Find total number of pixels in a line (pixels).
+ *
+ * [TOTAL PIXELS] = RB_H_BLANK+TOTAL_ACTIVE_PIXELS
+ */
+
+ total_pixels = total_active_pixels + RB_H_BLANK;
+
+ print_value(12, "[TOTAL PIXELS]", total_pixels);
+
+
+ /* 13. Find Pixel Clock Frequency (MHz).
+ *
+ * [Non-rounded PIXEL_FREQ] = V_FIELD_RATE_RQD*TOTAL_V_LINES*TOTAL_PIXELS/1000000
+ * [ACT PIXEL FREQ] = CLOCK_STEP * ROUND((V_FIELD_RATE_RQD*TOTAL_V_LINES*TOTAL_PIXELS/1000000)/CLOCK_STEP,0)
+ */
+
+ act_pixel_freq = v_field_rate_rqd * total_v_lines
+ * total_pixels / 1000000.0;
+ print_value(13, "[Non-rounded PIXEL FREQ]", act_pixel_freq);
+
+ act_pixel_freq = CLOCK_STEP * floor(act_pixel_freq / CLOCK_STEP);
+ print_value(13, "[ACT PIXEL FREQ]", act_pixel_freq);
+
+
+ stage = 14;
+
+ } else { /* Normal Blanking */
+
+ /* 8. Estimate Horiz. Period (us).
+ *
+ * [H PERIOD EST] = ((1/V_FIELD_RATE_RQD)-MIN_VSYNC_BP/1000000)/(V_LINES_RND+(2*TOP_MARGIN)+MIN_V_PORCH_RND+INTERLACE)*1000000
+ */
+
+ h_period_est = ((1/v_field_rate_rqd) - MIN_VSYNC_BP/1000000.0)
+ / (v_lines_rnd + (2*top_margin) + MIN_V_PORCH_RND + interlace)
+ * 1000000.0;
+
+ print_value(8, "[H PERIOD EST]", h_period_est);
+
+
+ /* 9. Find number of lines in (SYNC + BACK PORCH).
+ *
+ * [Estimated V_SYNC_BP] = ROUNDDOWN((MIN_VSYNC_BP/H_PERIOD_EST),0)+1
+ * [Actual V_SYNC_BP] = MIN_VSYNC_BP/H_PERIOD_EST
+ * [V_SYNC_BP] = IF(Estimated V_SYNC_BP<(V_SYNC+MIN_V_BPORCH),
+ * V_SYNC+MIN_V_BPORCH,Estimated V_SYNC_BP)
+ */
+
+ v_sync_bp = MIN_VSYNC_BP/h_period_est;
+ print_value(9, "[Actual V_SYNC_BP]", v_sync_bp);
+
+ v_sync_bp = floor(v_sync_bp) + 1;
+ print_value(9, "[Estimated V_SYNC_BP]", v_sync_bp);
+
+ v_sync_bp = (v_sync_bp < v_sync + MIN_V_BPORCH) ?
+ v_sync + MIN_V_BPORCH : v_sync_bp;
+ print_value(9, "[V_SYNC_BP]", v_sync_bp);
+
+
+ /* 10. Find number of lines in back porch (Lines).
+ *
+ * [Back porch] = V_SYNC_BP - V_SYNC_RND;
+ */
+
+ print_value(10, "[Back porch]", v_sync_bp - v_sync_rnd);
+
+
+ /* 11. Find total number of lines in vertical field.
+ *
+ * [TOTAL V LINES] = V_LINES_RND+TOP_MARGIN+BOT_MARGIN
+ * +V_SYNC_BP+INTERLACE+MIN_V_PORCH_RND
+ */
+
+ total_v_lines = v_lines_rnd + top_margin + bot_margin
+ + v_sync_bp + interlace + MIN_V_PORCH_RND;
+
+ print_value(11, "[TOTAL V LINES]", total_v_lines);
+
+
+ /* 12. Find ideal blanking duty cycle from formula (%):
+ *
+ * [IDEAL DUTY CYCLE] = C_PRIME-(M_PRIME*H_PERIOD_EST/1000)
+ */
+
+ ideal_duty_cycle = C_PRIME - (M_PRIME * h_period_est / 1000.0);
+
+ print_value(12, "[IDEAL DUTY CYCLE]", ideal_duty_cycle);
+
+
+ /* 13. Find blanking time to nearest cell (Pixels).
+ *
+ * [H BLANK] = IF(IDEAL_DUTY_CYCLE<20,(ROUNDDOWN((TOTAL_ACTIVE_PIXELS*20/(100-20)/(2*CELL_GRAN_RND)),0))*(2*CELL_GRAN_RND),(ROUNDDOWN((TOTAL_ACTIVE_PIXELS*IDEAL_DUTY_CYCLE/(100-IDEAL_DUTY_CYCLE)/(2*CELL_GRAN_RND)),0))*(2*CELL_GRAN_RND))
+ */
+
+ cur_duty_cycle = (ideal_duty_cycle < 20.0) ? 20.0 : ideal_duty_cycle;
+ h_blank = floor((total_active_pixels * cur_duty_cycle/(100.0 - cur_duty_cycle)/(2.0*CELL_GRAN_RND))) * (2.0*CELL_GRAN_RND);
+
+ print_value(13, "[H BLANK]", h_blank);
+
+
+ /* 14. Find total number of pixels in a line (Pixels).
+ *
+ * [TOTAL PIXELS] = TOTAL_ACTIVE_PIXELS + H_BLANK
+ */
+
+ total_pixels = total_active_pixels + h_blank;
+
+ print_value(14, "[TOTAL PIXELS]", total_pixels);
+
+
+ /* 15. Find pixel clock frequency (MHz).
+ *
+ * [Non-rounded PIXEL FREQ] = TOTAL_PIXELS / H_PERIOD_EST
+ * [ACT PIXEL FREQ] = CLOCK_STEP * ROUNDDOWN(
+ */
+
+ act_pixel_freq = total_pixels / h_period_est;
+ print_value(15, "[Non-rounded PIXEL FREQ]", act_pixel_freq);
+
+ act_pixel_freq = CLOCK_STEP * floor(act_pixel_freq / CLOCK_STEP);
+ print_value(15, "[ACT PIXEL FREQ]", act_pixel_freq);
+
+
+ stage = 16;
+ }
+
+
+ /* 14/16. Find actual horizontal frequency (kHz)
+ *
+ * [ACT H FREQ] = 1000*ACT_PIXEL_FREQ/TOTAL_PIXELS
+ */
+
+ act_h_freq = 1000 * act_pixel_freq / total_pixels;
+
+ print_value(stage, "[ACT H FREQ]", act_h_freq);
+ stage += 1;
+
+
+ /* 15/17. Find actual field rate (Hz)
+ *
+ * [ACT FIELD RATE] = 1000*ACT_H_FREQ/TOTAL_V_LINES
+ */
+
+ act_field_rate = 1000 * act_h_freq / total_v_lines;
+
+ print_value(stage, "[ACT FIELD RATE]", act_field_rate);
+ stage += 1;
+
+
+ /* 16/18. Find actual vertical frame frequency (Hz)
+ *
+ * [ACT FRAME RATE] = IF(INT_RQD?=Y,ACT_FIELD_RATE/2,ACT_FIELD_RATE)
+ */
+
+ act_frame_rate = interlace ?
+ (act_field_rate / 2) : act_field_rate;
+
+ print_value(stage, "[ACT FRAME RATE]", act_frame_rate);
+
+
+
+ /*
+ * Extra computations not numbered in the CVT spreadsheet.
+ */
+
+
+ /* 20. Find Horizontal Back Porch.
+ *
+ * [H BACK PORCH] = H_BLANK/2
+ */
+
+ h_back_porch = h_blank/2;
+
+ print_value(20, "[H BACK PORCH]", h_back_porch);
+
+
+ /* 21. Find Horizontal Front Porch.
+ *
+ * [H SYNC RND] = IF(RED_BLANK_RQD?="Y",RB_H_SYNC,(ROUNDDOWN((H_SYNC_PER/100*TOTAL_PIXELS/CELL_GRAN_RND),0))*CELL_GRAN_RND)
+ */
+
+ if (reduced_blank) {
+ h_sync_rnd = RB_H_SYNC;
+ } else {
+ h_sync_rnd = floor(H_SYNC_PER/100.0*total_pixels/CELL_GRAN_RND)
+ * CELL_GRAN_RND;
+ }
+
+ print_value(21, "[H SYNC RND]", h_sync_rnd);
+
+
+ /* 22. Find Horizontal Front Porch.
+ *
+ * [H FRONT PORCH] = H_BLANK - H_BACK_PORCH - H_SYNC_RND
+ */
+
+ h_front_porch = h_blank - h_back_porch - h_sync_rnd;
+
+ print_value(22, "[H FRONT PORCH]", h_front_porch);
+
+
+ /* 23. Find Vertical Front Porch.
+ *
+ * [V FRONT PORCH] = IF(RED_BLANK_RQD?="y",RB_V_FPORCH,MIN_V_PORCH_RND)
+ */
+
+ v_front_porch = reduced_blank ? RB_V_FPORCH : MIN_V_PORCH_RND;
+
+ print_value(23, "[V FRONT PORCH]", v_front_porch);
+
+
+
+ /* finally, pack the results in the mode struct */
+
+ m->hr = (int) (h_pixels_rnd);
+ m->hss = (int) (h_pixels_rnd + h_front_porch);
+ m->hse = (int) (h_pixels_rnd + h_front_porch + h_sync_rnd);
+ m->hfl = (int) (total_pixels);
+
+#if 0
+ m->vr = (int) (v_lines_rnd);
+ m->vss = (int) (v_lines_rnd + v_front_porch);
+ m->vse = (int) (v_lines_rnd + v_front_porch + v_sync_rnd);
+ m->vfl = (int) (total_v_lines);
+#else
+ {
+ int real_v_lines = v_lines;
+ m->vr = (int) (real_v_lines);
+ m->vss = (int) (real_v_lines + v_front_porch);
+ m->vse = (int) (real_v_lines + v_front_porch + v_sync_rnd);
+ m->vfl = (int) (total_v_lines - v_lines_rnd + real_v_lines);
+ }
+#endif
+
+ m->pclk = act_pixel_freq;
+ m->h_freq = act_h_freq;
+ m->v_freq = freq;
+ m->real_v_rate = act_field_rate;
+
+ m->in = interlaced;
+ m->rb = reduced_blank;
+
+ return (m);
+
+} // vert_refresh()
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/cvt.h b/src/cvt.h
new file mode 100644
index 0000000..7f2ce74
--- /dev/null
+++ b/src/cvt.h
@@ -0,0 +1,26 @@
+#ifndef __CVT_H_
+#define __CVT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* struct definitions */
+
+typedef struct __mode
+{
+ int hr, hss, hse, hfl;
+ int vr, vss, vse, vfl;
+ float pclk, h_freq, v_freq;
+ float real_v_rate;
+ int rb, in;
+} mode;
+
+mode *vert_refresh (int h_pixels, int v_lines, float freq,
+ int interlaced, int reduced_blank, int margins);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif