105b261ecSmrg/* gtf.c  Generate mode timings using the GTF Timing Standard
205b261ecSmrg *
305b261ecSmrg * gcc gtf.c -o gtf -lm -Wall
405b261ecSmrg *
505b261ecSmrg * Copyright (c) 2001, Andy Ritger  aritger@nvidia.com
605b261ecSmrg * All rights reserved.
735c4bbdfSmrg *
805b261ecSmrg * Redistribution and use in source and binary forms, with or without
905b261ecSmrg * modification, are permitted provided that the following conditions
1005b261ecSmrg * are met:
1135c4bbdfSmrg *
1205b261ecSmrg * o Redistributions of source code must retain the above copyright
1305b261ecSmrg *   notice, this list of conditions and the following disclaimer.
1405b261ecSmrg * o Redistributions in binary form must reproduce the above copyright
1505b261ecSmrg *   notice, this list of conditions and the following disclaimer
1605b261ecSmrg *   in the documentation and/or other materials provided with the
1705b261ecSmrg *   distribution.
1805b261ecSmrg * o Neither the name of NVIDIA nor the names of its contributors
1905b261ecSmrg *   may be used to endorse or promote products derived from this
2005b261ecSmrg *   software without specific prior written permission.
2105b261ecSmrg *
2205b261ecSmrg *
2305b261ecSmrg * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2405b261ecSmrg * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
2505b261ecSmrg * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
2605b261ecSmrg * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
2705b261ecSmrg * THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2805b261ecSmrg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2905b261ecSmrg * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
3005b261ecSmrg * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
3105b261ecSmrg * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3205b261ecSmrg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
3305b261ecSmrg * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3405b261ecSmrg * POSSIBILITY OF SUCH DAMAGE.
3505b261ecSmrg *
3635c4bbdfSmrg *
3705b261ecSmrg *
3805b261ecSmrg * This program is based on the Generalized Timing Formula(GTF TM)
3905b261ecSmrg * Standard Version: 1.0, Revision: 1.0
4005b261ecSmrg *
4105b261ecSmrg * The GTF Document contains the following Copyright information:
4205b261ecSmrg *
4305b261ecSmrg * Copyright (c) 1994, 1995, 1996 - Video Electronics Standards
4405b261ecSmrg * Association. Duplication of this document within VESA member
4505b261ecSmrg * companies for review purposes is permitted. All other rights
4605b261ecSmrg * reserved.
4705b261ecSmrg *
4805b261ecSmrg * While every precaution has been taken in the preparation
4905b261ecSmrg * of this standard, the Video Electronics Standards Association and
5005b261ecSmrg * its contributors assume no responsibility for errors or omissions,
5105b261ecSmrg * and make no warranties, expressed or implied, of functionality
5205b261ecSmrg * of suitability for any purpose. The sample code contained within
5305b261ecSmrg * this standard may be used without restriction.
5405b261ecSmrg *
5535c4bbdfSmrg *
5605b261ecSmrg *
5705b261ecSmrg * The GTF EXCEL(TM) SPREADSHEET, a sample (and the definitive)
5805b261ecSmrg * implementation of the GTF Timing Standard, is available at:
5905b261ecSmrg *
6005b261ecSmrg * ftp://ftp.vesa.org/pub/GTF/GTF_V1R1.xls
6105b261ecSmrg *
6205b261ecSmrg *
6305b261ecSmrg *
6405b261ecSmrg * This program takes a desired resolution and vertical refresh rate,
6505b261ecSmrg * and computes mode timings according to the GTF Timing Standard.
6605b261ecSmrg * These mode timings can then be formatted as an XServer modeline
6705b261ecSmrg * or a mode description for use by fbset(8).
6805b261ecSmrg *
6905b261ecSmrg *
7005b261ecSmrg *
7105b261ecSmrg * NOTES:
7205b261ecSmrg *
7305b261ecSmrg * The GTF allows for computation of "margins" (the visible border
7405b261ecSmrg * surrounding the addressable video); on most non-overscan type
7505b261ecSmrg * systems, the margin period is zero.  I've implemented the margin
7605b261ecSmrg * computations but not enabled it because 1) I don't really have
7705b261ecSmrg * any experience with this, and 2) neither XServer modelines nor
7805b261ecSmrg * fbset fb.modes provide an obvious way for margin timings to be
7905b261ecSmrg * included in their mode descriptions (needs more investigation).
8035c4bbdfSmrg *
8105b261ecSmrg * The GTF provides for computation of interlaced mode timings;
8205b261ecSmrg * I've implemented the computations but not enabled them, yet.
8305b261ecSmrg * I should probably enable and test this at some point.
8405b261ecSmrg *
8535c4bbdfSmrg *
8605b261ecSmrg *
8705b261ecSmrg * TODO:
8805b261ecSmrg *
8905b261ecSmrg * o Add support for interlaced modes.
9005b261ecSmrg *
9105b261ecSmrg * o Implement the other portions of the GTF: compute mode timings
9205b261ecSmrg *   given either the desired pixel clock or the desired horizontal
9305b261ecSmrg *   frequency.
9405b261ecSmrg *
9505b261ecSmrg * o It would be nice if this were more general purpose to do things
9605b261ecSmrg *   outside the scope of the GTF: like generate double scan mode
9705b261ecSmrg *   timings, for example.
9835c4bbdfSmrg *
9905b261ecSmrg * o Printing digits to the right of the decimal point when the
10005b261ecSmrg *   digits are 0 annoys me.
10105b261ecSmrg *
10205b261ecSmrg * o Error checking.
10305b261ecSmrg *
10405b261ecSmrg */
10505b261ecSmrg
10605b261ecSmrg#ifdef HAVE_XORG_CONFIG_H
10735c4bbdfSmrg#include <xorg-config.h>
10805b261ecSmrg#endif
10905b261ecSmrg
11005b261ecSmrg#include <stdio.h>
11105b261ecSmrg#include <stdlib.h>
11205b261ecSmrg#include <string.h>
11305b261ecSmrg#include <math.h>
11405b261ecSmrg
11505b261ecSmrg#define MARGIN_PERCENT    1.8   /* % of active vertical image                */
11605b261ecSmrg#define CELL_GRAN         8.0   /* assumed character cell granularity        */
11705b261ecSmrg#define MIN_PORCH         1     /* minimum front porch                       */
11805b261ecSmrg#define V_SYNC_RQD        3     /* width of vsync in lines                   */
11905b261ecSmrg#define H_SYNC_PERCENT    8.0   /* width of hsync as % of total line         */
12005b261ecSmrg#define MIN_VSYNC_PLUS_BP 550.0 /* min time of vsync + back porch (microsec) */
12105b261ecSmrg#define M                 600.0 /* blanking formula gradient                 */
12205b261ecSmrg#define C                 40.0  /* blanking formula offset                   */
12305b261ecSmrg#define K                 128.0 /* blanking formula scaling factor           */
12405b261ecSmrg#define J                 20.0  /* blanking formula scaling factor           */
12505b261ecSmrg
12605b261ecSmrg/* C' and M' are part of the Blanking Duty Cycle computation */
12705b261ecSmrg
12805b261ecSmrg#define C_PRIME           (((C - J) * K/256.0) + J)
12905b261ecSmrg#define M_PRIME           (K/256.0 * M)
13005b261ecSmrg
13105b261ecSmrg/* struct definitions */
13205b261ecSmrg
13335c4bbdfSmrgtypedef struct __mode {
13405b261ecSmrg    int hr, hss, hse, hfl;
13505b261ecSmrg    int vr, vss, vse, vfl;
13605b261ecSmrg    float pclk, h_freq, v_freq;
13705b261ecSmrg} mode;
13805b261ecSmrg
13935c4bbdfSmrgtypedef struct __options {
14005b261ecSmrg    int x, y;
14105b261ecSmrg    int xorgmode, fbmode;
14205b261ecSmrg    float v_freq;
14305b261ecSmrg} options;
14405b261ecSmrg
14505b261ecSmrg/* prototypes */
14605b261ecSmrg
14735c4bbdfSmrgvoid print_value(int n, const char *name, float val);
14835c4bbdfSmrgvoid print_xf86_mode(mode * m);
14935c4bbdfSmrgvoid print_fb_mode(mode * m);
15035c4bbdfSmrgmode *vert_refresh(int h_pixels, int v_lines, float freq,
15135c4bbdfSmrg                   int interlaced, int margins);
15235c4bbdfSmrgoptions *parse_command_line(int argc, char *argv[]);
15305b261ecSmrg
15405b261ecSmrg/*
15505b261ecSmrg * print_value() - print the result of the named computation; this is
15605b261ecSmrg * useful when comparing against the GTF EXCEL spreadsheet.
15705b261ecSmrg */
15805b261ecSmrg
15905b261ecSmrgint global_verbose = 0;
16005b261ecSmrg
16135c4bbdfSmrgvoid
16235c4bbdfSmrgprint_value(int n, const char *name, float val)
16305b261ecSmrg{
16405b261ecSmrg    if (global_verbose) {
16505b261ecSmrg        printf("%2d: %-27s: %15f\n", n, name, val);
16605b261ecSmrg    }
16705b261ecSmrg}
16805b261ecSmrg
16905b261ecSmrg/* print_xf86_mode() - print the XServer modeline, given mode timings. */
17005b261ecSmrg
17135c4bbdfSmrgvoid
17235c4bbdfSmrgprint_xf86_mode(mode * m)
17305b261ecSmrg{
17435c4bbdfSmrg    printf("\n");
17535c4bbdfSmrg    printf("  # %dx%d @ %.2f Hz (GTF) hsync: %.2f kHz; pclk: %.2f MHz\n",
17635c4bbdfSmrg           m->hr, m->vr, m->v_freq, m->h_freq, m->pclk);
17705b261ecSmrg
17835c4bbdfSmrg    printf("  Modeline \"%dx%d_%.2f\"  %.2f"
17935c4bbdfSmrg           "  %d %d %d %d"
18035c4bbdfSmrg           "  %d %d %d %d"
18135c4bbdfSmrg           "  -HSync +Vsync\n\n",
18235c4bbdfSmrg           m->hr, m->vr, m->v_freq, m->pclk,
18335c4bbdfSmrg           m->hr, m->hss, m->hse, m->hfl, m->vr, m->vss, m->vse, m->vfl);
18405b261ecSmrg
18535c4bbdfSmrg}
18605b261ecSmrg
18705b261ecSmrg/*
18805b261ecSmrg * print_fb_mode() - print a mode description in fbset(8) format;
18905b261ecSmrg * see the fb.modes(8) manpage.  The timing description used in
19005b261ecSmrg * this is rather odd; they use "left and right margin" to refer
19105b261ecSmrg * to the portion of the hblank before and after the sync pulse
19205b261ecSmrg * by conceptually wrapping the portion of the blank after the pulse
19305b261ecSmrg * to infront of the visible region; ie:
19435c4bbdfSmrg *
19505b261ecSmrg *
19605b261ecSmrg * Timing description I'm accustomed to:
19705b261ecSmrg *
19805b261ecSmrg *
19905b261ecSmrg *
20005b261ecSmrg *     <--------1--------> <--2--> <--3--> <--4-->
20105b261ecSmrg *                                _________
20205b261ecSmrg *    |-------------------|_______|       |_______
20305b261ecSmrg *
20405b261ecSmrg *                        R       SS      SE     FL
20535c4bbdfSmrg *
20605b261ecSmrg * 1: visible image
20705b261ecSmrg * 2: blank before sync (aka front porch)
20805b261ecSmrg * 3: sync pulse
20905b261ecSmrg * 4: blank after sync (aka back porch)
21005b261ecSmrg * R: Resolution
21105b261ecSmrg * SS: Sync Start
21205b261ecSmrg * SE: Sync End
21305b261ecSmrg * FL: Frame Length
21405b261ecSmrg *
21505b261ecSmrg *
21605b261ecSmrg * But the fb.modes format is:
21705b261ecSmrg *
21805b261ecSmrg *
21935c4bbdfSmrg *    <--4--> <--------1--------> <--2--> <--3-->
22005b261ecSmrg *                                       _________
22105b261ecSmrg *    _______|-------------------|_______|       |
22235c4bbdfSmrg *
22305b261ecSmrg * The fb.modes(8) manpage refers to <4> and <2> as the left and
22405b261ecSmrg * right "margin" (as well as upper and lower margin in the vertical
22505b261ecSmrg * direction) -- note that this has nothing to do with the term
22605b261ecSmrg * "margin" used in the GTF Timing Standard.
22705b261ecSmrg *
22805b261ecSmrg * XXX always prints the 32 bit mode -- should I provide a command
22905b261ecSmrg * line option to specify the bpp?  It's simple enough for a user
23005b261ecSmrg * to edit the mode description after it's generated.
23105b261ecSmrg */
23205b261ecSmrg
23335c4bbdfSmrgvoid
23435c4bbdfSmrgprint_fb_mode(mode * m)
23505b261ecSmrg{
23635c4bbdfSmrg    printf("\n");
23735c4bbdfSmrg    printf("mode \"%dx%d %.2fHz 32bit (GTF)\"\n", m->hr, m->vr, m->v_freq);
23835c4bbdfSmrg    printf("    # PCLK: %.2f MHz, H: %.2f kHz, V: %.2f Hz\n",
23935c4bbdfSmrg           m->pclk, m->h_freq, m->v_freq);
24035c4bbdfSmrg    printf("    geometry %d %d %d %d 32\n", m->hr, m->vr, m->hr, m->vr);
2411b5d61b8Smrg    printf("    timings %d %d %d %d %d %d %d\n", (int)lrint(1000000.0 / m->pclk),       /* pixclock in picoseconds */
24235c4bbdfSmrg           m->hfl - m->hse,     /* left margin (in pixels) */
24335c4bbdfSmrg           m->hss - m->hr,      /* right margin (in pixels) */
24435c4bbdfSmrg           m->vfl - m->vse,     /* upper margin (in pixel lines) */
24535c4bbdfSmrg           m->vss - m->vr,      /* lower margin (in pixel lines) */
24635c4bbdfSmrg           m->hse - m->hss,     /* horizontal sync length (pixels) */
24735c4bbdfSmrg           m->vse - m->vss);    /* vert sync length (pixel lines) */
24835c4bbdfSmrg    printf("    hsync low\n");
24935c4bbdfSmrg    printf("    vsync high\n");
25035c4bbdfSmrg    printf("endmode\n\n");
25105b261ecSmrg
25235c4bbdfSmrg}
25305b261ecSmrg
25405b261ecSmrg/*
25505b261ecSmrg * vert_refresh() - as defined by the GTF Timing Standard, compute the
25605b261ecSmrg * Stage 1 Parameters using the vertical refresh frequency.  In other
25705b261ecSmrg * words: input a desired resolution and desired refresh rate, and
25805b261ecSmrg * output the GTF mode timings.
25905b261ecSmrg *
26005b261ecSmrg * XXX All the code is in place to compute interlaced modes, but I don't
26105b261ecSmrg * feel like testing it right now.
26205b261ecSmrg *
26305b261ecSmrg * XXX margin computations are implemented but not tested (nor used by
26405b261ecSmrg * XServer of fbset mode descriptions, from what I can tell).
26505b261ecSmrg */
26605b261ecSmrg
26735c4bbdfSmrgmode *
26835c4bbdfSmrgvert_refresh(int h_pixels, int v_lines, float freq, int interlaced, int margins)
26905b261ecSmrg{
27005b261ecSmrg    float h_pixels_rnd;
27105b261ecSmrg    float v_lines_rnd;
27205b261ecSmrg    float v_field_rate_rqd;
27305b261ecSmrg    float top_margin;
27405b261ecSmrg    float bottom_margin;
27505b261ecSmrg    float interlace;
27605b261ecSmrg    float h_period_est;
27705b261ecSmrg    float vsync_plus_bp;
27805b261ecSmrg    float v_back_porch;
27905b261ecSmrg    float total_v_lines;
28005b261ecSmrg    float v_field_rate_est;
28105b261ecSmrg    float h_period;
28205b261ecSmrg    float v_field_rate;
28305b261ecSmrg    float v_frame_rate;
28405b261ecSmrg    float left_margin;
28505b261ecSmrg    float right_margin;
28605b261ecSmrg    float total_active_pixels;
28705b261ecSmrg    float ideal_duty_cycle;
28805b261ecSmrg    float h_blank;
28905b261ecSmrg    float total_pixels;
29005b261ecSmrg    float pixel_freq;
29105b261ecSmrg    float h_freq;
29205b261ecSmrg
29305b261ecSmrg    float h_sync;
29405b261ecSmrg    float h_front_porch;
29505b261ecSmrg    float v_odd_front_porch_lines;
29605b261ecSmrg
29735c4bbdfSmrg    mode *m = (mode *) malloc(sizeof(mode));
29835c4bbdfSmrg
29905b261ecSmrg    /*  1. In order to give correct results, the number of horizontal
30005b261ecSmrg     *  pixels requested is first processed to ensure that it is divisible
30105b261ecSmrg     *  by the character size, by rounding it to the nearest character
30205b261ecSmrg     *  cell boundary:
30305b261ecSmrg     *
30405b261ecSmrg     *  [H PIXELS RND] = ((ROUND([H PIXELS]/[CELL GRAN RND],0))*[CELLGRAN RND])
30505b261ecSmrg     */
30635c4bbdfSmrg
30705b261ecSmrg    h_pixels_rnd = rint((float) h_pixels / CELL_GRAN) * CELL_GRAN;
30835c4bbdfSmrg
30905b261ecSmrg    print_value(1, "[H PIXELS RND]", h_pixels_rnd);
31005b261ecSmrg
31105b261ecSmrg    /*  2. If interlace is requested, the number of vertical lines assumed
31205b261ecSmrg     *  by the calculation must be halved, as the computation calculates
31305b261ecSmrg     *  the number of vertical lines per field. In either case, the
31405b261ecSmrg     *  number of lines is rounded to the nearest integer.
31535c4bbdfSmrg     *
31605b261ecSmrg     *  [V LINES RND] = IF([INT RQD?]="y", ROUND([V LINES]/2,0),
31705b261ecSmrg     *                                     ROUND([V LINES],0))
31805b261ecSmrg     */
31905b261ecSmrg
32005b261ecSmrg    v_lines_rnd = interlaced ?
32135c4bbdfSmrg        rint((float) v_lines) / 2.0 : rint((float) v_lines);
32235c4bbdfSmrg
32305b261ecSmrg    print_value(2, "[V LINES RND]", v_lines_rnd);
32435c4bbdfSmrg
32505b261ecSmrg    /*  3. Find the frame rate required:
32605b261ecSmrg     *
32705b261ecSmrg     *  [V FIELD RATE RQD] = IF([INT RQD?]="y", [I/P FREQ RQD]*2,
32805b261ecSmrg     *                                          [I/P FREQ RQD])
32905b261ecSmrg     */
33005b261ecSmrg
33105b261ecSmrg    v_field_rate_rqd = interlaced ? (freq * 2.0) : (freq);
33205b261ecSmrg
33305b261ecSmrg    print_value(3, "[V FIELD RATE RQD]", v_field_rate_rqd);
33405b261ecSmrg
33505b261ecSmrg    /*  4. Find number of lines in Top margin:
33605b261ecSmrg     *
33705b261ecSmrg     *  [TOP MARGIN (LINES)] = IF([MARGINS RQD?]="Y",
33805b261ecSmrg     *          ROUND(([MARGIN%]/100*[V LINES RND]),0),
33905b261ecSmrg     *          0)
34005b261ecSmrg     */
34105b261ecSmrg
34205b261ecSmrg    top_margin = margins ? rint(MARGIN_PERCENT / 100.0 * v_lines_rnd) : (0.0);
34305b261ecSmrg
34405b261ecSmrg    print_value(4, "[TOP MARGIN (LINES)]", top_margin);
34505b261ecSmrg
34605b261ecSmrg    /*  5. Find number of lines in Bottom margin:
34705b261ecSmrg     *
34805b261ecSmrg     *  [BOT MARGIN (LINES)] = IF([MARGINS RQD?]="Y",
34905b261ecSmrg     *          ROUND(([MARGIN%]/100*[V LINES RND]),0),
35005b261ecSmrg     *          0)
35105b261ecSmrg     */
35205b261ecSmrg
35335c4bbdfSmrg    bottom_margin =
35435c4bbdfSmrg        margins ? rint(MARGIN_PERCENT / 100.0 * v_lines_rnd) : (0.0);
35505b261ecSmrg
35605b261ecSmrg    print_value(5, "[BOT MARGIN (LINES)]", bottom_margin);
35705b261ecSmrg
35805b261ecSmrg    /*  6. If interlace is required, then set variable [INTERLACE]=0.5:
35935c4bbdfSmrg     *
36005b261ecSmrg     *  [INTERLACE]=(IF([INT RQD?]="y",0.5,0))
36105b261ecSmrg     */
36205b261ecSmrg
36305b261ecSmrg    interlace = interlaced ? 0.5 : 0.0;
36405b261ecSmrg
36505b261ecSmrg    print_value(6, "[INTERLACE]", interlace);
36605b261ecSmrg
36705b261ecSmrg    /*  7. Estimate the Horizontal period
36805b261ecSmrg     *
36905b261ecSmrg     *  [H PERIOD EST] = ((1/[V FIELD RATE RQD]) - [MIN VSYNC+BP]/1000000) /
37005b261ecSmrg     *                    ([V LINES RND] + (2*[TOP MARGIN (LINES)]) +
37105b261ecSmrg     *                     [MIN PORCH RND]+[INTERLACE]) * 1000000
37205b261ecSmrg     */
37305b261ecSmrg
37435c4bbdfSmrg    h_period_est = (((1.0 / v_field_rate_rqd) - (MIN_VSYNC_PLUS_BP / 1000000.0))
37535c4bbdfSmrg                    / (v_lines_rnd + (2 * top_margin) + MIN_PORCH + interlace)
37605b261ecSmrg                    * 1000000.0);
37705b261ecSmrg
37805b261ecSmrg    print_value(7, "[H PERIOD EST]", h_period_est);
37905b261ecSmrg
38005b261ecSmrg    /*  8. Find the number of lines in V sync + back porch:
38105b261ecSmrg     *
38205b261ecSmrg     *  [V SYNC+BP] = ROUND(([MIN VSYNC+BP]/[H PERIOD EST]),0)
38305b261ecSmrg     */
38405b261ecSmrg
38535c4bbdfSmrg    vsync_plus_bp = rint(MIN_VSYNC_PLUS_BP / h_period_est);
38605b261ecSmrg
38705b261ecSmrg    print_value(8, "[V SYNC+BP]", vsync_plus_bp);
38835c4bbdfSmrg
38905b261ecSmrg    /*  9. Find the number of lines in V back porch alone:
39005b261ecSmrg     *
39105b261ecSmrg     *  [V BACK PORCH] = [V SYNC+BP] - [V SYNC RND]
39205b261ecSmrg     *
39305b261ecSmrg     *  XXX is "[V SYNC RND]" a typo? should be [V SYNC RQD]?
39405b261ecSmrg     */
39535c4bbdfSmrg
39605b261ecSmrg    v_back_porch = vsync_plus_bp - V_SYNC_RQD;
39735c4bbdfSmrg
39805b261ecSmrg    print_value(9, "[V BACK PORCH]", v_back_porch);
39905b261ecSmrg
40005b261ecSmrg    /*  10. Find the total number of lines in Vertical field period:
40105b261ecSmrg     *
40205b261ecSmrg     *  [TOTAL V LINES] = [V LINES RND] + [TOP MARGIN (LINES)] +
40305b261ecSmrg     *                    [BOT MARGIN (LINES)] + [V SYNC+BP] + [INTERLACE] +
40405b261ecSmrg     *                    [MIN PORCH RND]
40505b261ecSmrg     */
40605b261ecSmrg
40705b261ecSmrg    total_v_lines = v_lines_rnd + top_margin + bottom_margin + vsync_plus_bp +
40805b261ecSmrg        interlace + MIN_PORCH;
40935c4bbdfSmrg
41005b261ecSmrg    print_value(10, "[TOTAL V LINES]", total_v_lines);
41105b261ecSmrg
41205b261ecSmrg    /*  11. Estimate the Vertical field frequency:
41305b261ecSmrg     *
41405b261ecSmrg     *  [V FIELD RATE EST] = 1 / [H PERIOD EST] / [TOTAL V LINES] * 1000000
41505b261ecSmrg     */
41605b261ecSmrg
41705b261ecSmrg    v_field_rate_est = 1.0 / h_period_est / total_v_lines * 1000000.0;
41835c4bbdfSmrg
41905b261ecSmrg    print_value(11, "[V FIELD RATE EST]", v_field_rate_est);
42005b261ecSmrg
42105b261ecSmrg    /*  12. Find the actual horizontal period:
42205b261ecSmrg     *
42305b261ecSmrg     *  [H PERIOD] = [H PERIOD EST] / ([V FIELD RATE RQD] / [V FIELD RATE EST])
42405b261ecSmrg     */
42505b261ecSmrg
42605b261ecSmrg    h_period = h_period_est / (v_field_rate_rqd / v_field_rate_est);
42735c4bbdfSmrg
42805b261ecSmrg    print_value(12, "[H PERIOD]", h_period);
42905b261ecSmrg
43005b261ecSmrg    /*  13. Find the actual Vertical field frequency:
43105b261ecSmrg     *
43205b261ecSmrg     *  [V FIELD RATE] = 1 / [H PERIOD] / [TOTAL V LINES] * 1000000
43305b261ecSmrg     */
43405b261ecSmrg
43505b261ecSmrg    v_field_rate = 1.0 / h_period / total_v_lines * 1000000.0;
43605b261ecSmrg
43705b261ecSmrg    print_value(13, "[V FIELD RATE]", v_field_rate);
43805b261ecSmrg
43905b261ecSmrg    /*  14. Find the Vertical frame frequency:
44005b261ecSmrg     *
44105b261ecSmrg     *  [V FRAME RATE] = (IF([INT RQD?]="y", [V FIELD RATE]/2, [V FIELD RATE]))
44205b261ecSmrg     */
44305b261ecSmrg
44405b261ecSmrg    v_frame_rate = interlaced ? v_field_rate / 2.0 : v_field_rate;
44505b261ecSmrg
44605b261ecSmrg    print_value(14, "[V FRAME RATE]", v_frame_rate);
44705b261ecSmrg
44805b261ecSmrg    /*  15. Find number of pixels in left margin:
44905b261ecSmrg     *
45005b261ecSmrg     *  [LEFT MARGIN (PIXELS)] = (IF( [MARGINS RQD?]="Y",
45105b261ecSmrg     *          (ROUND( ([H PIXELS RND] * [MARGIN%] / 100 /
45205b261ecSmrg     *                   [CELL GRAN RND]),0)) * [CELL GRAN RND],
45305b261ecSmrg     *          0))
45405b261ecSmrg     */
45505b261ecSmrg
45605b261ecSmrg    left_margin = margins ?
45705b261ecSmrg        rint(h_pixels_rnd * MARGIN_PERCENT / 100.0 / CELL_GRAN) * CELL_GRAN :
45805b261ecSmrg        0.0;
45935c4bbdfSmrg
46005b261ecSmrg    print_value(15, "[LEFT MARGIN (PIXELS)]", left_margin);
46105b261ecSmrg
46205b261ecSmrg    /*  16. Find number of pixels in right margin:
46305b261ecSmrg     *
46405b261ecSmrg     *  [RIGHT MARGIN (PIXELS)] = (IF( [MARGINS RQD?]="Y",
46505b261ecSmrg     *          (ROUND( ([H PIXELS RND] * [MARGIN%] / 100 /
46605b261ecSmrg     *                   [CELL GRAN RND]),0)) * [CELL GRAN RND],
46705b261ecSmrg     *          0))
46805b261ecSmrg     */
46935c4bbdfSmrg
47005b261ecSmrg    right_margin = margins ?
47105b261ecSmrg        rint(h_pixels_rnd * MARGIN_PERCENT / 100.0 / CELL_GRAN) * CELL_GRAN :
47205b261ecSmrg        0.0;
47335c4bbdfSmrg
47405b261ecSmrg    print_value(16, "[RIGHT MARGIN (PIXELS)]", right_margin);
47505b261ecSmrg
47605b261ecSmrg    /*  17. Find total number of active pixels in image and left and right
47705b261ecSmrg     *  margins:
47805b261ecSmrg     *
47905b261ecSmrg     *  [TOTAL ACTIVE PIXELS] = [H PIXELS RND] + [LEFT MARGIN (PIXELS)] +
48005b261ecSmrg     *                          [RIGHT MARGIN (PIXELS)]
48105b261ecSmrg     */
48205b261ecSmrg
48305b261ecSmrg    total_active_pixels = h_pixels_rnd + left_margin + right_margin;
48435c4bbdfSmrg
48505b261ecSmrg    print_value(17, "[TOTAL ACTIVE PIXELS]", total_active_pixels);
48635c4bbdfSmrg
48705b261ecSmrg    /*  18. Find the ideal blanking duty cycle from the blanking duty cycle
48805b261ecSmrg     *  equation:
48905b261ecSmrg     *
49005b261ecSmrg     *  [IDEAL DUTY CYCLE] = [C'] - ([M']*[H PERIOD]/1000)
49105b261ecSmrg     */
49205b261ecSmrg
49305b261ecSmrg    ideal_duty_cycle = C_PRIME - (M_PRIME * h_period / 1000.0);
49435c4bbdfSmrg
49505b261ecSmrg    print_value(18, "[IDEAL DUTY CYCLE]", ideal_duty_cycle);
49605b261ecSmrg
49705b261ecSmrg    /*  19. Find the number of pixels in the blanking time to the nearest
49805b261ecSmrg     *  double character cell:
49905b261ecSmrg     *
50005b261ecSmrg     *  [H BLANK (PIXELS)] = (ROUND(([TOTAL ACTIVE PIXELS] *
50105b261ecSmrg     *                               [IDEAL DUTY CYCLE] /
50205b261ecSmrg     *                               (100-[IDEAL DUTY CYCLE]) /
50305b261ecSmrg     *                               (2*[CELL GRAN RND])), 0))
50405b261ecSmrg     *                       * (2*[CELL GRAN RND])
50505b261ecSmrg     */
50605b261ecSmrg
50705b261ecSmrg    h_blank = rint(total_active_pixels *
50805b261ecSmrg                   ideal_duty_cycle /
50905b261ecSmrg                   (100.0 - ideal_duty_cycle) /
51005b261ecSmrg                   (2.0 * CELL_GRAN)) * (2.0 * CELL_GRAN);
51135c4bbdfSmrg
51205b261ecSmrg    print_value(19, "[H BLANK (PIXELS)]", h_blank);
51305b261ecSmrg
51405b261ecSmrg    /*  20. Find total number of pixels:
51505b261ecSmrg     *
51605b261ecSmrg     *  [TOTAL PIXELS] = [TOTAL ACTIVE PIXELS] + [H BLANK (PIXELS)]
51705b261ecSmrg     */
51805b261ecSmrg
51905b261ecSmrg    total_pixels = total_active_pixels + h_blank;
52035c4bbdfSmrg
52105b261ecSmrg    print_value(20, "[TOTAL PIXELS]", total_pixels);
52205b261ecSmrg
52305b261ecSmrg    /*  21. Find pixel clock frequency:
52405b261ecSmrg     *
52505b261ecSmrg     *  [PIXEL FREQ] = [TOTAL PIXELS] / [H PERIOD]
52605b261ecSmrg     */
52735c4bbdfSmrg
52805b261ecSmrg    pixel_freq = total_pixels / h_period;
52935c4bbdfSmrg
53005b261ecSmrg    print_value(21, "[PIXEL FREQ]", pixel_freq);
53105b261ecSmrg
53205b261ecSmrg    /*  22. Find horizontal frequency:
53305b261ecSmrg     *
53405b261ecSmrg     *  [H FREQ] = 1000 / [H PERIOD]
53505b261ecSmrg     */
53605b261ecSmrg
53705b261ecSmrg    h_freq = 1000.0 / h_period;
53805b261ecSmrg
53935c4bbdfSmrg    print_value(22, "[H FREQ]", h_freq);
54005b261ecSmrg
54105b261ecSmrg    /* Stage 1 computations are now complete; I should really pass
54205b261ecSmrg       the results to another function and do the Stage 2
54305b261ecSmrg       computations, but I only need a few more values so I'll just
54405b261ecSmrg       append the computations here for now */
54505b261ecSmrg
54605b261ecSmrg    /*  17. Find the number of pixels in the horizontal sync period:
54705b261ecSmrg     *
54805b261ecSmrg     *  [H SYNC (PIXELS)] =(ROUND(([H SYNC%] / 100 * [TOTAL PIXELS] /
54905b261ecSmrg     *                             [CELL GRAN RND]),0))*[CELL GRAN RND]
55005b261ecSmrg     */
55105b261ecSmrg
55235c4bbdfSmrg    h_sync =
55335c4bbdfSmrg        rint(H_SYNC_PERCENT / 100.0 * total_pixels / CELL_GRAN) * CELL_GRAN;
55405b261ecSmrg
55505b261ecSmrg    print_value(17, "[H SYNC (PIXELS)]", h_sync);
55605b261ecSmrg
55705b261ecSmrg    /*  18. Find the number of pixels in the horizontal front porch period:
55805b261ecSmrg     *
55905b261ecSmrg     *  [H FRONT PORCH (PIXELS)] = ([H BLANK (PIXELS)]/2)-[H SYNC (PIXELS)]
56005b261ecSmrg     */
56105b261ecSmrg
56205b261ecSmrg    h_front_porch = (h_blank / 2.0) - h_sync;
56305b261ecSmrg
56405b261ecSmrg    print_value(18, "[H FRONT PORCH (PIXELS)]", h_front_porch);
56535c4bbdfSmrg
56605b261ecSmrg    /*  36. Find the number of lines in the odd front porch period:
56705b261ecSmrg     *
56805b261ecSmrg     *  [V ODD FRONT PORCH(LINES)]=([MIN PORCH RND]+[INTERLACE])
56905b261ecSmrg     */
57035c4bbdfSmrg
57105b261ecSmrg    v_odd_front_porch_lines = MIN_PORCH + interlace;
57235c4bbdfSmrg
57305b261ecSmrg    print_value(36, "[V ODD FRONT PORCH(LINES)]", v_odd_front_porch_lines);
57405b261ecSmrg
57505b261ecSmrg    /* finally, pack the results in the mode struct */
57635c4bbdfSmrg
57735c4bbdfSmrg    m->hr = (int) (h_pixels_rnd);
57805b261ecSmrg    m->hss = (int) (h_pixels_rnd + h_front_porch);
57905b261ecSmrg    m->hse = (int) (h_pixels_rnd + h_front_porch + h_sync);
58005b261ecSmrg    m->hfl = (int) (total_pixels);
58105b261ecSmrg
58235c4bbdfSmrg    m->vr = (int) (v_lines_rnd);
58305b261ecSmrg    m->vss = (int) (v_lines_rnd + v_odd_front_porch_lines);
58405b261ecSmrg    m->vse = (int) (int) (v_lines_rnd + v_odd_front_porch_lines + V_SYNC_RQD);
58505b261ecSmrg    m->vfl = (int) (total_v_lines);
58605b261ecSmrg
58735c4bbdfSmrg    m->pclk = pixel_freq;
58805b261ecSmrg    m->h_freq = h_freq;
58905b261ecSmrg    m->v_freq = freq;
59005b261ecSmrg
5916747b715Smrg    return m;
59205b261ecSmrg
59335c4bbdfSmrg}
59405b261ecSmrg
59505b261ecSmrg/*
59605b261ecSmrg * parse_command_line() - parse the command line and return an
59705b261ecSmrg * alloced structure containing the results.  On error print usage
59805b261ecSmrg * and return NULL.
59935c4bbdfSmrg */
60005b261ecSmrg
60135c4bbdfSmrgoptions *
60235c4bbdfSmrgparse_command_line(int argc, char *argv[])
60305b261ecSmrg{
60405b261ecSmrg    int n;
60505b261ecSmrg
60635c4bbdfSmrg    options *o = (options *) calloc(1, sizeof(options));
60705b261ecSmrg
60835c4bbdfSmrg    if (argc < 4)
60935c4bbdfSmrg        goto bad_option;
61005b261ecSmrg
61135c4bbdfSmrg    o->x = atoi(argv[1]);
61235c4bbdfSmrg    o->y = atoi(argv[2]);
61335c4bbdfSmrg    o->v_freq = atof(argv[3]);
61405b261ecSmrg
61505b261ecSmrg    /* XXX should check for errors in the above */
61635c4bbdfSmrg
61705b261ecSmrg    n = 4;
61805b261ecSmrg
61905b261ecSmrg    while (n < argc) {
62035c4bbdfSmrg        if ((strcmp(argv[n], "-v") == 0) || (strcmp(argv[n], "--verbose") == 0)) {
62105b261ecSmrg            global_verbose = 1;
62235c4bbdfSmrg        }
62335c4bbdfSmrg        else if ((strcmp(argv[n], "-f") == 0) ||
62435c4bbdfSmrg                 (strcmp(argv[n], "--fbmode") == 0)) {
62505b261ecSmrg            o->fbmode = 1;
62635c4bbdfSmrg        }
62735c4bbdfSmrg        else if ((strcmp(argv[n], "-x") == 0) ||
62835c4bbdfSmrg                 (strcmp(argv[n], "--xorgmode") == 0) ||
62935c4bbdfSmrg                 (strcmp(argv[n], "--xf86mode") == 0)) {
63005b261ecSmrg            o->xorgmode = 1;
63135c4bbdfSmrg        }
63235c4bbdfSmrg        else {
63305b261ecSmrg            goto bad_option;
63405b261ecSmrg        }
63535c4bbdfSmrg
63605b261ecSmrg        n++;
63705b261ecSmrg    }
63805b261ecSmrg
63905b261ecSmrg    /* if neither xorgmode nor fbmode were requested, default to
64005b261ecSmrg       xorgmode */
64105b261ecSmrg
64235c4bbdfSmrg    if (!o->fbmode && !o->xorgmode)
64335c4bbdfSmrg        o->xorgmode = 1;
64435c4bbdfSmrg
6456747b715Smrg    return o;
64635c4bbdfSmrg
64705b261ecSmrg bad_option:
64805b261ecSmrg
64935c4bbdfSmrg    fprintf(stderr, "\n");
65035c4bbdfSmrg    fprintf(stderr, "usage: %s x y refresh [-v|--verbose] "
65135c4bbdfSmrg            "[-f|--fbmode] [-x|--xorgmode]\n", argv[0]);
65205b261ecSmrg
65335c4bbdfSmrg    fprintf(stderr, "\n");
65435c4bbdfSmrg
65535c4bbdfSmrg    fprintf(stderr, "            x : the desired horizontal "
65635c4bbdfSmrg            "resolution (required)\n");
65735c4bbdfSmrg    fprintf(stderr, "            y : the desired vertical "
65835c4bbdfSmrg            "resolution (required)\n");
65935c4bbdfSmrg    fprintf(stderr, "      refresh : the desired refresh " "rate (required)\n");
66035c4bbdfSmrg    fprintf(stderr, " -v|--verbose : enable verbose printouts "
66135c4bbdfSmrg            "(traces each step of the computation)\n");
66235c4bbdfSmrg    fprintf(stderr, "  -f|--fbmode : output an fbset(8)-style mode "
66335c4bbdfSmrg            "description\n");
66435c4bbdfSmrg    fprintf(stderr, " -x|--xorgmode : output an " __XSERVERNAME__ "-style mode "
66535c4bbdfSmrg            "description (this is the default\n"
66635c4bbdfSmrg            "                if no mode description is requested)\n");
66705b261ecSmrg
66835c4bbdfSmrg    fprintf(stderr, "\n");
66935c4bbdfSmrg
67035c4bbdfSmrg    free(o);
67135c4bbdfSmrg    return NULL;
67205b261ecSmrg
67335c4bbdfSmrg}
67405b261ecSmrg
67535c4bbdfSmrgint
67635c4bbdfSmrgmain(int argc, char *argv[])
67705b261ecSmrg{
67805b261ecSmrg    mode *m;
67905b261ecSmrg    options *o;
68005b261ecSmrg
68135c4bbdfSmrg    o = parse_command_line(argc, argv);
68235c4bbdfSmrg    if (!o)
68335c4bbdfSmrg        exit(1);
68435c4bbdfSmrg
68535c4bbdfSmrg    m = vert_refresh(o->x, o->y, o->v_freq, 0, 0);
68635c4bbdfSmrg    if (!m)
68735c4bbdfSmrg        exit(1);
68805b261ecSmrg
68905b261ecSmrg    if (o->xorgmode)
69005b261ecSmrg        print_xf86_mode(m);
69135c4bbdfSmrg
69205b261ecSmrg    if (o->fbmode)
69305b261ecSmrg        print_fb_mode(m);
69435c4bbdfSmrg
6951b5d61b8Smrg    free(m);
6961b5d61b8Smrg
69705b261ecSmrg    return 0;
69835c4bbdfSmrg
69905b261ecSmrg}
700