1706f2543Smrg/*
2706f2543Smrg * Copyright 2005-2006 Luc Verhaegen.
3706f2543Smrg *
4706f2543Smrg * Permission is hereby granted, free of charge, to any person obtaining a
5706f2543Smrg * copy of this software and associated documentation files (the "Software"),
6706f2543Smrg * to deal in the Software without restriction, including without limitation
7706f2543Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8706f2543Smrg * and/or sell copies of the Software, and to permit persons to whom the
9706f2543Smrg * Software is furnished to do so, subject to the following conditions:
10706f2543Smrg *
11706f2543Smrg * The above copyright notice and this permission notice shall be included in
12706f2543Smrg * all copies or substantial portions of the Software.
13706f2543Smrg *
14706f2543Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15706f2543Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16706f2543Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17706f2543Smrg * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18706f2543Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19706f2543Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20706f2543Smrg * OTHER DEALINGS IN THE SOFTWARE.
21706f2543Smrg *
22706f2543Smrg */
23706f2543Smrg
24706f2543Smrg/* Standalone VESA CVT standard timing modelines generator. */
25706f2543Smrg
26706f2543Smrg#include "xf86.h"
27706f2543Smrg#include "xf86Modes.h"
28706f2543Smrg
29706f2543Smrg/* Error implementation used by the server code we built in */
30706f2543Smrgvoid
31706f2543SmrgError(const char *str)
32706f2543Smrg{
33706f2543Smrg    perror(str);
34706f2543Smrg}
35706f2543Smrg
36706f2543Smrg/* FatalError implementation used by the server code we built in */
37706f2543Smrgvoid
38706f2543SmrgFatalError(const char *f, ...)
39706f2543Smrg{
40706f2543Smrg    va_list args;
41706f2543Smrg    va_start(args, f);
42706f2543Smrg    vfprintf(stderr, f, args);
43706f2543Smrg    va_end(args);
44706f2543Smrg    exit(1);
45706f2543Smrg}
46706f2543Smrg
47706f2543Smrg/* xnfalloc implementation used by the server code we built in */
48706f2543Smrgpointer
49706f2543SmrgXNFalloc(unsigned long n)
50706f2543Smrg{
51706f2543Smrg    pointer r;
52706f2543Smrg
53706f2543Smrg    r = malloc(n);
54706f2543Smrg    if (!r) {
55706f2543Smrg        perror("malloc failed");
56706f2543Smrg        exit(1);
57706f2543Smrg    }
58706f2543Smrg    return r;
59706f2543Smrg}
60706f2543Smrg
61706f2543Smrg/* xnfcalloc implementation used by the server code we built in */
62706f2543Smrgpointer
63706f2543SmrgXNFcalloc(unsigned long n)
64706f2543Smrg{
65706f2543Smrg    pointer r;
66706f2543Smrg
67706f2543Smrg    r = calloc(1, n);
68706f2543Smrg    if (!r) {
69706f2543Smrg        perror("calloc failed");
70706f2543Smrg        exit(1);
71706f2543Smrg    }
72706f2543Smrg    return r;
73706f2543Smrg}
74706f2543Smrg
75706f2543Smrg/*
76706f2543Smrg * Quickly check wether this is a CVT standard mode.
77706f2543Smrg */
78706f2543Smrgstatic Bool
79706f2543SmrgCVTCheckStandard(int HDisplay, int VDisplay, float VRefresh, Bool Reduced,
80706f2543Smrg                     Bool Verbose)
81706f2543Smrg{
82706f2543Smrg    Bool  IsCVT = TRUE;
83706f2543Smrg
84706f2543Smrg    if ((!(VDisplay % 3) && ((VDisplay * 4 / 3) == HDisplay)) ||
85706f2543Smrg        (!(VDisplay % 9) && ((VDisplay * 16 / 9) == HDisplay)) ||
86706f2543Smrg        (!(VDisplay % 10) && ((VDisplay * 16 / 10) == HDisplay)) ||
87706f2543Smrg        (!(VDisplay % 4) && ((VDisplay * 5 / 4) == HDisplay)) ||
88706f2543Smrg        (!(VDisplay % 9) && ((VDisplay * 15 / 9) == HDisplay)))
89706f2543Smrg        ;
90706f2543Smrg    else {
91706f2543Smrg        if (Verbose)
92706f2543Smrg            fprintf(stderr, "Warning: Aspect Ratio is not CVT standard.\n");
93706f2543Smrg        IsCVT = FALSE;
94706f2543Smrg    }
95706f2543Smrg
96706f2543Smrg    if ((VRefresh != 50.0) && (VRefresh != 60.0) &&
97706f2543Smrg        (VRefresh != 75.0) && (VRefresh != 85.0)) {
98706f2543Smrg        if (Verbose)
99706f2543Smrg            fprintf(stderr, "Warning: Refresh Rate is not CVT standard "
100706f2543Smrg                    "(50, 60, 75 or 85Hz).\n");
101706f2543Smrg        IsCVT = FALSE;
102706f2543Smrg    }
103706f2543Smrg
104706f2543Smrg    return IsCVT;
105706f2543Smrg}
106706f2543Smrg
107706f2543Smrg
108706f2543Smrg/*
109706f2543Smrg * I'm not documenting --interlaced for obvious reasons, even though I did
110706f2543Smrg * implement it. I also can't deny having looked at gtf here.
111706f2543Smrg */
112706f2543Smrgstatic void
113706f2543SmrgPrintUsage(char *Name)
114706f2543Smrg{
115706f2543Smrg    fprintf(stderr, "\n");
116706f2543Smrg    fprintf(stderr, "usage: %s [-v|--verbose] [-r|--reduced] X Y [refresh]\n",
117706f2543Smrg            Name);
118706f2543Smrg    fprintf(stderr, "\n");
119706f2543Smrg    fprintf(stderr, " -v|--verbose : Warn about CVT standard adherance.\n");
120706f2543Smrg    fprintf(stderr, " -r|--reduced : Create a mode with reduced blanking "
121706f2543Smrg            "(default: normal blanking).\n");
122706f2543Smrg    fprintf(stderr, "            X : Desired horizontal resolution "
123706f2543Smrg            "(multiple of 8, required).\n");
124706f2543Smrg    fprintf(stderr, "            Y : Desired vertical resolution (required).\n");
125706f2543Smrg    fprintf(stderr, "      refresh : Desired refresh rate (default: 60.0Hz).\n");
126706f2543Smrg    fprintf(stderr, "\n");
127706f2543Smrg
128706f2543Smrg    fprintf(stderr, "Calculates VESA CVT (Coordinated Video Timing) modelines"
129706f2543Smrg	    " for use with X.\n");
130706f2543Smrg}
131706f2543Smrg
132706f2543Smrg
133706f2543Smrg/*
134706f2543Smrg *
135706f2543Smrg */
136706f2543Smrgstatic void
137706f2543SmrgPrintComment(DisplayModeRec *Mode, Bool CVT, Bool Reduced)
138706f2543Smrg{
139706f2543Smrg    printf("# %dx%d %.2f Hz ", Mode->HDisplay, Mode->VDisplay, Mode->VRefresh);
140706f2543Smrg
141706f2543Smrg    if (CVT) {
142706f2543Smrg        printf("(CVT %.2fM",
143706f2543Smrg               ((float) Mode->HDisplay * Mode->VDisplay) / 1000000.0);
144706f2543Smrg
145706f2543Smrg        if (!(Mode->VDisplay % 3) &&
146706f2543Smrg            ((Mode->VDisplay * 4 / 3) == Mode->HDisplay))
147706f2543Smrg            printf("3");
148706f2543Smrg        else if (!(Mode->VDisplay % 9) &&
149706f2543Smrg                 ((Mode->VDisplay * 16 / 9) == Mode->HDisplay))
150706f2543Smrg            printf("9");
151706f2543Smrg        else if (!(Mode->VDisplay % 10) &&
152706f2543Smrg                 ((Mode->VDisplay * 16 / 10) == Mode->HDisplay))
153706f2543Smrg            printf("A");
154706f2543Smrg        else if (!(Mode->VDisplay % 4) &&
155706f2543Smrg                 ((Mode->VDisplay * 5 / 4) == Mode->HDisplay))
156706f2543Smrg            printf("4");
157706f2543Smrg        else if (!(Mode->VDisplay % 9) &&
158706f2543Smrg                 ((Mode->VDisplay * 15 / 9) == Mode->HDisplay))
159706f2543Smrg            printf("9");
160706f2543Smrg
161706f2543Smrg        if (Reduced)
162706f2543Smrg            printf("-R");
163706f2543Smrg
164706f2543Smrg        printf(") ");
165706f2543Smrg    } else
166706f2543Smrg        printf("(CVT) ");
167706f2543Smrg
168706f2543Smrg    printf("hsync: %.2f kHz; ", Mode->HSync);
169706f2543Smrg    printf("pclk: %.2f MHz", ((float ) Mode->Clock) / 1000.0);
170706f2543Smrg
171706f2543Smrg    printf("\n");
172706f2543Smrg}
173706f2543Smrg
174706f2543Smrg
175706f2543Smrg/*
176706f2543Smrg * Originally grabbed from xf86Mode.c.
177706f2543Smrg *
178706f2543Smrg * Ignoring the actual Mode->name, as the user will want something solid
179706f2543Smrg * to grab hold of.
180706f2543Smrg */
181706f2543Smrgstatic void
182706f2543SmrgPrintModeline(DisplayModePtr Mode, int HDisplay, int VDisplay, float VRefresh,
183706f2543Smrg              Bool Reduced)
184706f2543Smrg{
185706f2543Smrg    if (Reduced)
186706f2543Smrg        printf("Modeline \"%dx%dR\"  ", HDisplay, VDisplay);
187706f2543Smrg    else
188706f2543Smrg        printf("Modeline \"%dx%d_%.2f\"  ", HDisplay, VDisplay, VRefresh);
189706f2543Smrg
190706f2543Smrg    printf("%6.2f  %i %i %i %i  %i %i %i %i", Mode->Clock/1000., Mode->HDisplay,
191706f2543Smrg           Mode->HSyncStart, Mode->HSyncEnd, Mode->HTotal, Mode->VDisplay,
192706f2543Smrg           Mode->VSyncStart, Mode->VSyncEnd, Mode->VTotal);
193706f2543Smrg
194706f2543Smrg    if (Mode->Flags & V_INTERLACE)
195706f2543Smrg        printf(" interlace");
196706f2543Smrg    if (Mode->Flags & V_PHSYNC)
197706f2543Smrg        printf(" +hsync");
198706f2543Smrg    if (Mode->Flags & V_NHSYNC)
199706f2543Smrg        printf(" -hsync");
200706f2543Smrg    if (Mode->Flags & V_PVSYNC)
201706f2543Smrg        printf(" +vsync");
202706f2543Smrg    if (Mode->Flags & V_NVSYNC)
203706f2543Smrg        printf(" -vsync");
204706f2543Smrg
205706f2543Smrg    printf("\n");
206706f2543Smrg}
207706f2543Smrg
208706f2543Smrg
209706f2543Smrg/*
210706f2543Smrg *
211706f2543Smrg */
212706f2543Smrgint
213706f2543Smrgmain (int argc, char *argv[])
214706f2543Smrg{
215706f2543Smrg    DisplayModeRec  *Mode;
216706f2543Smrg    int  HDisplay = 0, VDisplay = 0;
217706f2543Smrg    float  VRefresh = 0.0;
218706f2543Smrg    Bool  Reduced = FALSE, Verbose = FALSE, IsCVT;
219706f2543Smrg    Bool  Interlaced = FALSE;
220706f2543Smrg    int  n;
221706f2543Smrg
222706f2543Smrg    if ((argc < 3) || (argc > 7)) {
223706f2543Smrg        PrintUsage(argv[0]);
224706f2543Smrg        return 1;
225706f2543Smrg    }
226706f2543Smrg
227706f2543Smrg    /* This doesn't filter out bad flags properly. Bad flags get passed down
228706f2543Smrg     * to atoi/atof, which then return 0, so that these variables can get
229706f2543Smrg     * filled next time round. So this is just a cosmetic problem.
230706f2543Smrg     */
231706f2543Smrg    for (n = 1; n < argc; n++) {
232706f2543Smrg        if (!strcmp(argv[n], "-r") || !strcmp(argv[n], "--reduced"))
233706f2543Smrg            Reduced = TRUE;
234706f2543Smrg        else if (!strcmp(argv[n], "-i") || !strcmp(argv[n], "--interlaced"))
235706f2543Smrg            Interlaced = TRUE;
236706f2543Smrg        else if (!strcmp(argv[n], "-v") || !strcmp(argv[n], "--verbose"))
237706f2543Smrg            Verbose = TRUE;
238706f2543Smrg        else if (!strcmp(argv[n], "-h") || !strcmp(argv[n], "--help")) {
239706f2543Smrg            PrintUsage(argv[0]);
240706f2543Smrg            return 0;
241706f2543Smrg        } else if (!HDisplay) {
242706f2543Smrg            HDisplay = atoi(argv[n]);
243706f2543Smrg	    if (!HDisplay) {
244706f2543Smrg		PrintUsage(argv[0]);
245706f2543Smrg		return 1;
246706f2543Smrg	    }
247706f2543Smrg	}
248706f2543Smrg        else if (!VDisplay) {
249706f2543Smrg            VDisplay = atoi(argv[n]);
250706f2543Smrg	    if (!VDisplay) {
251706f2543Smrg		PrintUsage(argv[0]);
252706f2543Smrg		return 1;
253706f2543Smrg	    }
254706f2543Smrg	}
255706f2543Smrg        else if (!VRefresh) {
256706f2543Smrg            VRefresh = atof(argv[n]);
257706f2543Smrg	    if (!VRefresh) {
258706f2543Smrg		PrintUsage(argv[0]);
259706f2543Smrg		return 1;
260706f2543Smrg	    }
261706f2543Smrg	}
262706f2543Smrg        else {
263706f2543Smrg            PrintUsage(argv[0]);
264706f2543Smrg            return 1;
265706f2543Smrg        }
266706f2543Smrg    }
267706f2543Smrg
268706f2543Smrg    if (!HDisplay || !VDisplay) {
269706f2543Smrg        PrintUsage(argv[0]);
270706f2543Smrg        return 0;
271706f2543Smrg    }
272706f2543Smrg
273706f2543Smrg    /* Default to 60.0Hz */
274706f2543Smrg    if (!VRefresh)
275706f2543Smrg        VRefresh = 60.0;
276706f2543Smrg
277706f2543Smrg    /* Horizontal timing is always a multiple of 8: round up. */
278706f2543Smrg    if (HDisplay & 0x07) {
279706f2543Smrg        HDisplay &= ~0x07;
280706f2543Smrg        HDisplay += 8;
281706f2543Smrg    }
282706f2543Smrg
283706f2543Smrg    if (Reduced) {
284706f2543Smrg	if ((VRefresh / 60.0) != floor(VRefresh / 60.0)) {
285706f2543Smrg	    fprintf(stderr,
286706f2543Smrg		    "\nERROR: Multiple of 60Hz refresh rate required for "
287706f2543Smrg		    " reduced blanking.\n");
288706f2543Smrg	    PrintUsage(argv[0]);
289706f2543Smrg	    return 0;
290706f2543Smrg	}
291706f2543Smrg    }
292706f2543Smrg
293706f2543Smrg    IsCVT = CVTCheckStandard(HDisplay, VDisplay, VRefresh, Reduced, Verbose);
294706f2543Smrg
295706f2543Smrg    Mode = xf86CVTMode(HDisplay, VDisplay, VRefresh, Reduced, Interlaced);
296706f2543Smrg
297706f2543Smrg    PrintComment(Mode, IsCVT, Reduced);
298706f2543Smrg    PrintModeline(Mode, HDisplay, VDisplay, VRefresh, Reduced);
299706f2543Smrg
300706f2543Smrg    return 0;
301706f2543Smrg}
302