/* cvt.c Generate mode timings using the CVT Standard * * gcc cvt.c -o cvt -lm * * Copyright (c) 2001, Andy Ritger aritger@nvidia.com * All rights reserved. * * work derivated from gtf.c by Pascal Rigaux pixel@mandriva.com * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * o Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * o Neither the name of NVIDIA nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * * This program is based on the VESA Coordinated Video Timing(CVT TM) * * The CVT EXCEL(TM) SPREADSHEET, a sample * implementation of the CVT Standard, is available at: * * http://www.vesa.org/Public/CVT/CVTd6r1.xls * * * * This program takes a desired resolution and vertical refresh rate, * and computes mode timings according to the CVT Standard. * These mode timings can then be formatted as an XServer modeline * or a mode description for use by fbset(8). * * * * NOTES: * * The CVT allows for computation of "margins" (the visible border * surrounding the addressable video); on most non-overscan type * systems, the margin period is zero. I've implemented the margin * computations but not enabled it because 1) I don't really have * any experience with this, and 2) neither XServer modelines nor * fbset fb.modes provide an obvious way for margin timings to be * included in their mode descriptions (needs more investigation). * * The CVT provides for computation of interlaced mode timings; * I've implemented the computations but not enabled them, yet. * I should probably enable and test this at some point. * */ #include #include #include #include #ifndef __XSERVERNAME__ #define __XSERVERNAME__ "Xorg" #endif #define max(a, b) ((a) > (b) ? (a) : (b)) #ifdef DEBUG #define debug_printf(s) printf s #else #define debug_printf(s) #endif #define K93 1.8 #define CELL_GRAN 8.0 /* assumed character cell granularity */ #define K104 8 #define K110 550.0 #define MIN_V_BPORCH 6 #define MIN_V_PORCH 3 /* width of vsync in lines */ #define GTF_M_VAR 600.0 /* blanking formula gradient */ #define GTF_C_VAR 40.0 /* blanking formula offset */ #define GTF_K_VAR 128.0 /* blanking formula scaling factor */ #define GTF_J_VAR 20.0 /* blanking formula scaling factor */ #define C_PRIME (((GTF_C_VAR - GTF_J_VAR) * GTF_K_VAR/256.0) + GTF_J_VAR) #define M_PRIME (GTF_K_VAR/256.0 * GTF_M_VAR) /* C' and M' are part of the Blanking Duty Cycle computation */ /* reduced blanking vars */ #define K130 160 #define K131 32 #define K133 460 #define RB_V_FPORCH 3 #define RB_MIN_V_FPORCH 6 #define CLOCK_STEP 0.25 #define CELL_GRAN_RND floor(CELL_GRAN) #define MARGIN_PER K93 #define MIN_V_PORCH_RND floor(MIN_V_PORCH) #define MIN_VSYNC_BP K110 #define H_SYNC_PER K104 #define RB_MIN_V_BLANK K133 #define RB_H_SYNC K131 #define RB_H_BLANK K130 #define Y19 RB_H_SYNC #define Y20 RB_H_BLANK /* struct definitions */ typedef struct __mode { int hr, hss, hse, hfl; int vr, vss, vse, vfl; float pclk, h_freq, v_freq; int reduced_blanking; } mode; typedef struct __options { int x, y; int xorgmode, fbmode, reduced_blanking; float v_freq; } options; /* prototypes */ void print_value(int n, char *name, float val); void print_xf86_mode (mode *m); void print_fb_mode (mode *m); options *parse_command_line (int argc, char *argv[]); const char *aspect_ratio(int width, int height, int *vsync_width) { struct { float val; const char *name; int vsync_width; } ratios[] = { { 4./ 3, "4:3", 4 }, { 16./ 9, "16:9", 5 }, { 16./10, "16:10", 6 }, { 5./ 4, "5:4", 7 }, { 15./ 9, "15:9", 7 }, { 0, "", 10 }, }; int i; for (i = 0; ratios[i].val; i++) if (width == CELL_GRAN_RND * floor(height * ratios[i].val / CELL_GRAN_RND)) break; if (vsync_width) *vsync_width = ratios[i].vsync_width; return ratios[i].name; } /* * print_value() - print the result of the named computation; this is * useful when comparing against the CVT EXCEL spreadsheet. */ int global_verbose = 0; void print_value(int n, char *name, float val) { if (global_verbose) { printf("%2d: %-27s: %15f\n", n, name, val); } } /* print_xf86_mode() - print the XServer modeline, given mode timings. */ void print_xf86_mode (mode *m) { printf ("\n"); printf (" # %dx%d @ %.2f Hz (CVT%s) hsync: %.2f kHz; pclk: %.2f MHz\n", m->hr, m->vr, m->v_freq, m->reduced_blanking ? " - Reduced Blanking" : "", m->h_freq, m->pclk); printf (" Modeline \"%dx%d_%.2f\" %.2f" " %d %d %d %d" " %d %d %d %d" " -HSync +Vsync\n\n", m->hr, m->vr, m->v_freq, m->pclk, m->hr, m->hss, m->hse, m->hfl, m->vr, m->vss, m->vse, m->vfl); } /* * print_fb_mode() - print a mode description in fbset(8) format; * see the fb.modes(8) manpage. The timing description used in * this is rather odd; they use "left and right margin" to refer * to the portion of the hblank before and after the sync pulse * by conceptually wrapping the portion of the blank after the pulse * to infront of the visible region; ie: * * * Timing description I'm accustomed to: * * * * <--------1--------> <--2--> <--3--> <--4--> * _________ * |-------------------|_______| |_______ * * R SS SE FL * * 1: visible image * 2: blank before sync (aka front porch) * 3: sync pulse * 4: blank after sync (aka back porch) * R: Resolution * SS: Sync Start * SE: Sync End * FL: Frame Length * * * But the fb.modes format is: * * * <--4--> <--------1--------> <--2--> <--3--> * _________ * _______|-------------------|_______| | * * The fb.modes(8) manpage refers to <4> and <2> as the left and * right "margin" (as well as upper and lower margin in the vertical * direction) -- note that this has nothing to do with the term * "margin" used in the CVT Standard. * * XXX always prints the 32 bit mode -- should I provide a command * line option to specify the bpp? It's simple enough for a user * to edit the mode description after it's generated. */ void print_fb_mode (mode *m) { printf ("\n"); printf ("mode \"%dx%d %.2fHz 32bit (CVT%s)\"\n", m->hr, m->vr, m->v_freq, m->reduced_blanking ? " - Reduced Blanking" : "" ); printf (" # PCLK: %.2f MHz, H: %.2f kHz, V: %.2f Hz\n", m->pclk, m->h_freq, m->v_freq); printf (" geometry %d %d %d %d 32\n", m->hr, m->vr, m->hr, m->vr); printf (" timings %d %d %d %d %d %d %d\n", (int) floor(1000000.0/m->pclk),/* pixclock in picoseconds */ m->hfl - m->hse, /* left margin (in pixels) */ m->hss - m->hr, /* right margin (in pixels) */ m->vfl - m->vse, /* upper margin (in pixel lines) */ m->vss - m->vr, /* lower margin (in pixel lines) */ m->hse - m->hss, /* horizontal sync length (pixels) */ m->vse - m->vss); /* vert sync length (pixel lines) */ printf (" hsync low\n"); printf (" vsync high\n"); printf ("endmode\n\n"); } /* * vert_refresh() - as defined by the CVT 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 All the code is in place to compute interlaced modes, but I don't * feel like testing it right now. * * XXX margin computations are implemented but not tested (nor used by * XServer of fbset mode descriptions, from what I can tell). */ mode vert_refresh(int H_PIXELS, int V_LINES, float IP_FREQ_RQD, int RED_BLANK_RQD, int INT_RQD, int MARGINS_RQD) { /* 1 */ float V_FIELD_RATE_RQD = INT_RQD ? IP_FREQ_RQD * 2 : IP_FREQ_RQD; /* 2 */ int H_PIXELS_RND = floor((float) H_PIXELS / CELL_GRAN_RND) * CELL_GRAN_RND; int F39 = H_PIXELS_RND; /* 3 */ int LEFT_MARGIN = MARGINS_RQD ? floor(H_PIXELS_RND * MARGIN_PER / 100 / CELL_GRAN_RND) * CELL_GRAN_RND : 0; int RIGHT_MARGIN = LEFT_MARGIN; /* 4 */ int TOTAL_ACTIVE_PIXELS = H_PIXELS_RND + LEFT_MARGIN + RIGHT_MARGIN; debug_printf(("4: %d\n", TOTAL_ACTIVE_PIXELS)); /* 5 */ int V_LINES_RND = floor(INT_RQD ? V_LINES / 2 : V_LINES); int F40 = V_LINES; int V_SYNC; /*const char *ASPECT_RATIO =*/ aspect_ratio(F39, F40, &V_SYNC); int V_SYNC_RND = V_SYNC; /*int Q76 = V_SYNC_RND;*/ debug_printf(("5: %d %d\n", V_LINES_RND, V_SYNC)); /* 6 */ int TOP_MARGIN = MARGINS_RQD ? floor(MARGIN_PER / 100 * V_LINES_RND) : 0; int BOT_MARGIN = TOP_MARGIN; debug_printf(("6: %d\n", TOP_MARGIN)); /* 7 */ float INTERLACE = INT_RQD ? 0.5 : 0; /* 8 */ float U23 = (1. / V_FIELD_RATE_RQD - MIN_VSYNC_BP / 1000000.) / (V_LINES_RND + 2 * TOP_MARGIN + MIN_V_PORCH_RND + INTERLACE) * 1000000; float Y23 = (1000000 / V_FIELD_RATE_RQD - RB_MIN_V_BLANK) / (V_LINES_RND + TOP_MARGIN + BOT_MARGIN); float H_PERIOD_EST = RED_BLANK_RQD ? Y23 : U23; debug_printf(("8: %f %f %f\n", U23, Y23, H_PERIOD_EST)); /* 9 */ int U26 = floor(MIN_VSYNC_BP / H_PERIOD_EST) + 1; /*float U27 = MIN_VSYNC_BP / H_PERIOD_EST;*/ int V_SYNC_BP = U26 < V_SYNC + MIN_V_BPORCH ? V_SYNC + MIN_V_BPORCH : U26; debug_printf(("9: %d %d\n", U26, V_SYNC_BP)); /* 9' */ int VBI_LINES = floor(RB_MIN_V_BLANK / H_PERIOD_EST) + 1; /*float Y27 = RB_MIN_V_BLANK / H_PERIOD_EST;*/ debug_printf(("9': %d\n", VBI_LINES)); /* 10 */ /*int U31 = V_SYNC_BP - V_SYNC_RND;*/ /* 10' */ int RB_MIN_VBI = RB_V_FPORCH + V_SYNC_RND + MIN_V_BPORCH; int ACT_VBI_LINES = VBI_LINES < RB_MIN_VBI ? RB_MIN_VBI : VBI_LINES; debug_printf(("10': %d %d\n", RB_MIN_VBI, ACT_VBI_LINES)); /* 11 */ int U34 = V_LINES_RND + TOP_MARGIN + BOT_MARGIN + V_SYNC_BP + INTERLACE + MIN_V_PORCH_RND; debug_printf(("11: %d\n", U34)); /* 11' */ int Y34 = ACT_VBI_LINES + V_LINES_RND + TOP_MARGIN + BOT_MARGIN + INTERLACE; int TOTAL_V_LINES = RED_BLANK_RQD ? Y34 : U34; debug_printf(("11': %d %d\n", Y34, TOTAL_V_LINES)); /* 12 */ float H_PERIOD = C_PRIME - M_PRIME*H_PERIOD_EST/1000; float IDEAL_DUTY_CYCLE = H_PERIOD; debug_printf(("12: %f %f\n", H_PERIOD, IDEAL_DUTY_CYCLE)); /* 13 */ float cycle = max(IDEAL_DUTY_CYCLE, 20); int V_FIELD_RATE = floor(TOTAL_ACTIVE_PIXELS * cycle / (100 - cycle) / 2 / CELL_GRAN_RND) * 2 * CELL_GRAN_RND; int U40 = V_FIELD_RATE; debug_printf(("13: %f %d\n", cycle, V_FIELD_RATE)); int H_BLANK = RED_BLANK_RQD ? Y20 : U40; /* 14 */ int V_FRAME_RATE = TOTAL_ACTIVE_PIXELS + H_BLANK; int U43 = V_FRAME_RATE; debug_printf(("14: %d %d\n", V_FRAME_RATE, U43)); /* 12' */ int Y37 = RB_H_BLANK + TOTAL_ACTIVE_PIXELS; int TOTAL_PIXELS = RED_BLANK_RQD ? Y37 : U43; debug_printf(("12': %d %d\n", Y37, TOTAL_PIXELS)); /* 15 */ float PIXEL_FREQ = CLOCK_STEP * floor(TOTAL_PIXELS / H_PERIOD_EST / CLOCK_STEP); float U46 = PIXEL_FREQ; /*float U47 = TOTAL_PIXELS / H_PERIOD_EST;*/ /* 13' */ float Y41 = V_FIELD_RATE_RQD * TOTAL_V_LINES * TOTAL_PIXELS / 1000000; float Y40 = CLOCK_STEP * floor(Y41 / CLOCK_STEP); float ACT_PIXEL_FREQ = RED_BLANK_RQD ? Y40 : U46; /* 16 */ float U50 = 1000 * ACT_PIXEL_FREQ / TOTAL_PIXELS; /* 14' */ float Y44 = 1000 * ACT_PIXEL_FREQ / TOTAL_PIXELS; float ACT_H_FREQ = RED_BLANK_RQD ? Y44 : U50; /* 17 */ float U53 = 1000 * ACT_H_FREQ / TOTAL_V_LINES; /* 15' */ float Y47 = 1000 * ACT_H_FREQ / TOTAL_V_LINES; float ACT_FIELD_RATE = RED_BLANK_RQD ? Y47 : U53; /* 18 */ float U56 = INT_RQD ? ACT_FIELD_RATE / 2 : ACT_FIELD_RATE; /* 16' */ float Y50 = INT_RQD ? ACT_FIELD_RATE / 2 : ACT_FIELD_RATE; /* results */ float ACT_FRAME_RATE = RED_BLANK_RQD ? Y50 : U56; float H_BACK_PORCH = H_BLANK / 2; float H_SYNC_RND = RED_BLANK_RQD ? Y19 : floor(H_SYNC_PER / 100. * TOTAL_PIXELS / CELL_GRAN_RND) * CELL_GRAN_RND; float H_FRONT_PORCH = H_BLANK - H_BACK_PORCH - H_SYNC_RND; /*float V_BLANK = RED_BLANK_RQD ? ACT_VBI_LINES : V_SYNC_BP + MIN_V_PORCH_RND;*/ float V_FRONT_PORCH = RED_BLANK_RQD ? RB_V_FPORCH : MIN_V_PORCH_RND; /*float V_BACK_PORCH = V_BLANK - V_FRONT_PORCH - Q76;*/ debug_printf(("H_BLANK %d\n", H_BLANK)); debug_printf(("H_BACK_PORCH %f\n", H_BACK_PORCH)); debug_printf(("H_SYNC_RND %f\n", H_SYNC_RND)); debug_printf(("H_FRONT_PORCH %f\n", H_FRONT_PORCH)); /* finally, pack the results in the mode struct */ mode m; 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); m.vr = (int) (V_LINES_RND); m.vss = (int) (V_LINES_RND + V_FRONT_PORCH + INTERLACE); m.vse = (int) (V_LINES_RND + V_FRONT_PORCH + INTERLACE + V_SYNC_RND); m.vfl = (int) (TOTAL_V_LINES); m.pclk = ACT_PIXEL_FREQ; m.h_freq = ACT_H_FREQ; m.v_freq = ACT_FRAME_RATE; m.reduced_blanking = RED_BLANK_RQD; return (m); } /* * parse_command_line() - parse the command line and return an * alloced structure containing the results. On error print usage * and return NULL. */ options *parse_command_line (int argc, char *argv[]) { int n; options *o = (options *) calloc (1, sizeof (options)); if (argc < 4) goto bad_option; o->x = atoi (argv[1]); o->y = atoi (argv[2]); o->v_freq = atof (argv[3]); /* XXX should check for errors in the above */ n = 4; while (n < argc) { if ((strcmp (argv[n], "-v") == 0) || (strcmp (argv[n], "--verbose") == 0)) { global_verbose = 1; } else if ((strcmp (argv[n], "--reduced-blanking") == 0)) { o->reduced_blanking = 1; } else if ((strcmp (argv[n], "-f") == 0) || (strcmp (argv[n], "--fbmode") == 0)) { o->fbmode = 1; } else if ((strcmp (argv[n], "-x") == 0) || (strcmp (argv[n], "--xorgmode") == 0) || (strcmp (argv[n], "--xf86mode") == 0)) { o->xorgmode = 1; } else { goto bad_option; } n++; } /* if neither xorgmode nor fbmode were requested, default to xorgmode */ if (!o->fbmode && !o->xorgmode) o->xorgmode = 1; return (o); bad_option: fprintf (stderr, "\n"); fprintf (stderr, "usage: %s x y refresh [-v|--verbose] " "[--reduced-blanking] [-f|--fbmode] [-x|--xorgmode]\n", argv[0]); fprintf (stderr, "\n"); fprintf (stderr, " x : the desired horizontal " "resolution (required)\n"); fprintf (stderr, " y : the desired vertical " "resolution (required)\n"); fprintf (stderr, " refresh : the desired refresh " "rate (required)\n"); fprintf (stderr, " -v|--verbose : enable verbose printouts " "(traces each step of the computation)\n"); fprintf (stderr, " --reduced-blanking : if you want reduced blanking\n"); fprintf (stderr, " -f|--fbmode : output an fbset(8)-style mode " "description\n"); fprintf (stderr, " -x|--xorgmode : output an "__XSERVERNAME__"-style mode " "description (this is the default\n" " if no mode description is requested)\n"); fprintf (stderr, "\n"); free (o); return (NULL); } int main (int argc, char *argv[]) { mode m; options *o; o = parse_command_line (argc, argv); if (!o) exit (1); m = vert_refresh (o->x, o->y, o->v_freq, o->reduced_blanking, 0, 0); if (o->xorgmode) print_xf86_mode(&m); if (o->fbmode) print_fb_mode(&m); return 0; }