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 * The reason for having this function in a file of its own is
25706f2543Smrg * so that ../utils/cvt/cvt can link to it, and that xf86CVTMode
26706f2543Smrg * code is shared directly.
27706f2543Smrg */
28706f2543Smrg
29706f2543Smrg#ifdef HAVE_XORG_CONFIG_H
30706f2543Smrg#include <xorg-config.h>
31706f2543Smrg#else
32706f2543Smrg#ifdef HAVE_CONFIG_H
33706f2543Smrg#include <config.h>
34706f2543Smrg#endif
35706f2543Smrg#endif
36706f2543Smrg
37706f2543Smrg#include "xf86.h"
38706f2543Smrg#include "xf86Modes.h"
39706f2543Smrg
40706f2543Smrg#include <string.h>
41706f2543Smrg
42706f2543Smrg/*
43706f2543Smrg * Generate a CVT standard mode from HDisplay, VDisplay and VRefresh.
44706f2543Smrg *
45706f2543Smrg * These calculations are stolen from the CVT calculation spreadsheet written
46706f2543Smrg * by Graham Loveridge. He seems to be claiming no copyright and there seems to
47706f2543Smrg * be no license attached to this. He apparently just wants to see his name
48706f2543Smrg * mentioned.
49706f2543Smrg *
50706f2543Smrg * This file can be found at http://www.vesa.org/Public/CVT/CVTd6r1.xls
51706f2543Smrg *
52706f2543Smrg * Comments and structure corresponds to the comments and structure of the xls.
53706f2543Smrg * This should ease importing of future changes to the standard (not very
54706f2543Smrg * likely though).
55706f2543Smrg *
56706f2543Smrg * About margins; i'm sure that they are to be the bit between HDisplay and
57706f2543Smrg * HBlankStart, HBlankEnd and HTotal, VDisplay and VBlankStart, VBlankEnd and
58706f2543Smrg * VTotal, where the overscan colour is shown. FB seems to call _all_ blanking
59706f2543Smrg * outside sync "margin" for some reason. Since we prefer seeing proper
60706f2543Smrg * blanking instead of the overscan colour, and since the Crtc* values will
61706f2543Smrg * probably get altered after us, we will disable margins altogether. With
62706f2543Smrg * these calculations, Margins will plainly expand H/VDisplay, and we don't
63706f2543Smrg * want that. -- libv
64706f2543Smrg *
65706f2543Smrg */
66706f2543SmrgDisplayModePtr
67706f2543Smrgxf86CVTMode(int HDisplay, int VDisplay, float VRefresh, Bool Reduced,
68706f2543Smrg	    Bool Interlaced)
69706f2543Smrg{
70706f2543Smrg    DisplayModeRec  *Mode = xnfcalloc(1, sizeof(DisplayModeRec));
71706f2543Smrg
72706f2543Smrg    /* 1) top/bottom margin size (% of height) - default: 1.8 */
73706f2543Smrg#define CVT_MARGIN_PERCENTAGE 1.8
74706f2543Smrg
75706f2543Smrg    /* 2) character cell horizontal granularity (pixels) - default 8 */
76706f2543Smrg#define CVT_H_GRANULARITY 8
77706f2543Smrg
78706f2543Smrg    /* 4) Minimum vertical porch (lines) - default 3 */
79706f2543Smrg#define CVT_MIN_V_PORCH 3
80706f2543Smrg
81706f2543Smrg    /* 4) Minimum number of vertical back porch lines - default 6 */
82706f2543Smrg#define CVT_MIN_V_BPORCH 6
83706f2543Smrg
84706f2543Smrg    /* Pixel Clock step (kHz) */
85706f2543Smrg#define CVT_CLOCK_STEP 250
86706f2543Smrg
87706f2543Smrg    Bool Margins = FALSE;
88706f2543Smrg    float  VFieldRate, HPeriod;
89706f2543Smrg    int  HDisplayRnd, HMargin;
90706f2543Smrg    int  VDisplayRnd, VMargin, VSync;
91706f2543Smrg    float  Interlace; /* Please rename this */
92706f2543Smrg
93706f2543Smrg    /* CVT default is 60.0Hz */
94706f2543Smrg    if (!VRefresh)
95706f2543Smrg        VRefresh = 60.0;
96706f2543Smrg
97706f2543Smrg    /* 1. Required field rate */
98706f2543Smrg    if (Interlaced)
99706f2543Smrg        VFieldRate = VRefresh * 2;
100706f2543Smrg    else
101706f2543Smrg        VFieldRate = VRefresh;
102706f2543Smrg
103706f2543Smrg    /* 2. Horizontal pixels */
104706f2543Smrg    HDisplayRnd = HDisplay - (HDisplay % CVT_H_GRANULARITY);
105706f2543Smrg
106706f2543Smrg    /* 3. Determine left and right borders */
107706f2543Smrg    if (Margins) {
108706f2543Smrg        /* right margin is actually exactly the same as left */
109706f2543Smrg        HMargin = (((float) HDisplayRnd) * CVT_MARGIN_PERCENTAGE / 100.0);
110706f2543Smrg        HMargin -= HMargin % CVT_H_GRANULARITY;
111706f2543Smrg    } else
112706f2543Smrg        HMargin = 0;
113706f2543Smrg
114706f2543Smrg    /* 4. Find total active pixels */
115706f2543Smrg    Mode->HDisplay = HDisplayRnd + 2*HMargin;
116706f2543Smrg
117706f2543Smrg    /* 5. Find number of lines per field */
118706f2543Smrg    if (Interlaced)
119706f2543Smrg        VDisplayRnd = VDisplay / 2;
120706f2543Smrg    else
121706f2543Smrg        VDisplayRnd = VDisplay;
122706f2543Smrg
123706f2543Smrg    /* 6. Find top and bottom margins */
124706f2543Smrg    /* nope. */
125706f2543Smrg    if (Margins)
126706f2543Smrg        /* top and bottom margins are equal again. */
127706f2543Smrg        VMargin = (((float) VDisplayRnd) * CVT_MARGIN_PERCENTAGE / 100.0);
128706f2543Smrg    else
129706f2543Smrg        VMargin = 0;
130706f2543Smrg
131706f2543Smrg    Mode->VDisplay = VDisplay + 2*VMargin;
132706f2543Smrg
133706f2543Smrg    /* 7. Interlace */
134706f2543Smrg    if (Interlaced)
135706f2543Smrg        Interlace = 0.5;
136706f2543Smrg    else
137706f2543Smrg        Interlace = 0.0;
138706f2543Smrg
139706f2543Smrg    /* Determine VSync Width from aspect ratio */
140706f2543Smrg    if (!(VDisplay % 3) && ((VDisplay * 4 / 3) == HDisplay))
141706f2543Smrg        VSync = 4;
142706f2543Smrg    else if (!(VDisplay % 9) && ((VDisplay * 16 / 9) == HDisplay))
143706f2543Smrg        VSync = 5;
144706f2543Smrg    else if (!(VDisplay % 10) && ((VDisplay * 16 / 10) == HDisplay))
145706f2543Smrg        VSync = 6;
146706f2543Smrg    else if (!(VDisplay % 4) && ((VDisplay * 5 / 4) == HDisplay))
147706f2543Smrg        VSync = 7;
148706f2543Smrg    else if (!(VDisplay % 9) && ((VDisplay * 15 / 9) == HDisplay))
149706f2543Smrg        VSync = 7;
150706f2543Smrg    else /* Custom */
151706f2543Smrg        VSync = 10;
152706f2543Smrg
153706f2543Smrg    if (!Reduced) { /* simplified GTF calculation */
154706f2543Smrg
155706f2543Smrg        /* 4) Minimum time of vertical sync + back porch interval (�s)
156706f2543Smrg         * default 550.0 */
157706f2543Smrg#define CVT_MIN_VSYNC_BP 550.0
158706f2543Smrg
159706f2543Smrg        /* 3) Nominal HSync width (% of line period) - default 8 */
160706f2543Smrg#define CVT_HSYNC_PERCENTAGE 8
161706f2543Smrg
162706f2543Smrg        float  HBlankPercentage;
163706f2543Smrg        int  VSyncAndBackPorch, VBackPorch;
164706f2543Smrg        int  HBlank;
165706f2543Smrg
166706f2543Smrg        /* 8. Estimated Horizontal period */
167706f2543Smrg        HPeriod = ((float) (1000000.0 / VFieldRate - CVT_MIN_VSYNC_BP)) /
168706f2543Smrg            (VDisplayRnd + 2 * VMargin + CVT_MIN_V_PORCH + Interlace);
169706f2543Smrg
170706f2543Smrg        /* 9. Find number of lines in sync + backporch */
171706f2543Smrg        if (((int)(CVT_MIN_VSYNC_BP / HPeriod) + 1) < (VSync + CVT_MIN_V_PORCH))
172706f2543Smrg            VSyncAndBackPorch = VSync + CVT_MIN_V_PORCH;
173706f2543Smrg        else
174706f2543Smrg            VSyncAndBackPorch = (int)(CVT_MIN_VSYNC_BP / HPeriod) + 1;
175706f2543Smrg
176706f2543Smrg        /* 10. Find number of lines in back porch */
177706f2543Smrg        VBackPorch = VSyncAndBackPorch - VSync;
178706f2543Smrg
179706f2543Smrg        /* 11. Find total number of lines in vertical field */
180706f2543Smrg        Mode->VTotal = VDisplayRnd + 2 * VMargin + VSyncAndBackPorch + Interlace
181706f2543Smrg            + CVT_MIN_V_PORCH;
182706f2543Smrg
183706f2543Smrg        /* 5) Definition of Horizontal blanking time limitation */
184706f2543Smrg        /* Gradient (%/kHz) - default 600 */
185706f2543Smrg#define CVT_M_FACTOR 600
186706f2543Smrg
187706f2543Smrg        /* Offset (%) - default 40 */
188706f2543Smrg#define CVT_C_FACTOR 40
189706f2543Smrg
190706f2543Smrg        /* Blanking time scaling factor - default 128 */
191706f2543Smrg#define CVT_K_FACTOR 128
192706f2543Smrg
193706f2543Smrg        /* Scaling factor weighting - default 20 */
194706f2543Smrg#define CVT_J_FACTOR 20
195706f2543Smrg
196706f2543Smrg#define CVT_M_PRIME CVT_M_FACTOR * CVT_K_FACTOR / 256
197706f2543Smrg#define CVT_C_PRIME (CVT_C_FACTOR - CVT_J_FACTOR) * CVT_K_FACTOR / 256 + \
198706f2543Smrg        CVT_J_FACTOR
199706f2543Smrg
200706f2543Smrg        /* 12. Find ideal blanking duty cycle from formula */
201706f2543Smrg        HBlankPercentage = CVT_C_PRIME - CVT_M_PRIME * HPeriod/1000.0;
202706f2543Smrg
203706f2543Smrg        /* 13. Blanking time */
204706f2543Smrg        if (HBlankPercentage < 20)
205706f2543Smrg            HBlankPercentage = 20;
206706f2543Smrg
207706f2543Smrg        HBlank = Mode->HDisplay * HBlankPercentage/(100.0 - HBlankPercentage);
208706f2543Smrg        HBlank -= HBlank % (2*CVT_H_GRANULARITY);
209706f2543Smrg
210706f2543Smrg        /* 14. Find total number of pixels in a line. */
211706f2543Smrg        Mode->HTotal = Mode->HDisplay + HBlank;
212706f2543Smrg
213706f2543Smrg        /* Fill in HSync values */
214706f2543Smrg        Mode->HSyncEnd = Mode->HDisplay + HBlank / 2;
215706f2543Smrg
216706f2543Smrg        Mode->HSyncStart = Mode->HSyncEnd -
217706f2543Smrg            (Mode->HTotal * CVT_HSYNC_PERCENTAGE) / 100;
218706f2543Smrg        Mode->HSyncStart += CVT_H_GRANULARITY -
219706f2543Smrg            Mode->HSyncStart % CVT_H_GRANULARITY;
220706f2543Smrg
221706f2543Smrg        /* Fill in VSync values */
222706f2543Smrg        Mode->VSyncStart = Mode->VDisplay + CVT_MIN_V_PORCH;
223706f2543Smrg        Mode->VSyncEnd = Mode->VSyncStart + VSync;
224706f2543Smrg
225706f2543Smrg    } else { /* Reduced blanking */
226706f2543Smrg        /* Minimum vertical blanking interval time (�s) - default 460 */
227706f2543Smrg#define CVT_RB_MIN_VBLANK 460.0
228706f2543Smrg
229706f2543Smrg        /* Fixed number of clocks for horizontal sync */
230706f2543Smrg#define CVT_RB_H_SYNC 32.0
231706f2543Smrg
232706f2543Smrg        /* Fixed number of clocks for horizontal blanking */
233706f2543Smrg#define CVT_RB_H_BLANK 160.0
234706f2543Smrg
235706f2543Smrg        /* Fixed number of lines for vertical front porch - default 3 */
236706f2543Smrg#define CVT_RB_VFPORCH 3
237706f2543Smrg
238706f2543Smrg        int  VBILines;
239706f2543Smrg
240706f2543Smrg        /* 8. Estimate Horizontal period. */
241706f2543Smrg        HPeriod = ((float) (1000000.0 / VFieldRate - CVT_RB_MIN_VBLANK)) /
242706f2543Smrg            (VDisplayRnd + 2*VMargin);
243706f2543Smrg
244706f2543Smrg        /* 9. Find number of lines in vertical blanking */
245706f2543Smrg        VBILines = ((float) CVT_RB_MIN_VBLANK) / HPeriod + 1;
246706f2543Smrg
247706f2543Smrg        /* 10. Check if vertical blanking is sufficient */
248706f2543Smrg        if (VBILines < (CVT_RB_VFPORCH + VSync + CVT_MIN_V_BPORCH))
249706f2543Smrg            VBILines = CVT_RB_VFPORCH + VSync + CVT_MIN_V_BPORCH;
250706f2543Smrg
251706f2543Smrg        /* 11. Find total number of lines in vertical field */
252706f2543Smrg        Mode->VTotal = VDisplayRnd + 2 * VMargin + Interlace + VBILines;
253706f2543Smrg
254706f2543Smrg        /* 12. Find total number of pixels in a line */
255706f2543Smrg        Mode->HTotal = Mode->HDisplay + CVT_RB_H_BLANK;
256706f2543Smrg
257706f2543Smrg        /* Fill in HSync values */
258706f2543Smrg        Mode->HSyncEnd = Mode->HDisplay + CVT_RB_H_BLANK / 2;
259706f2543Smrg        Mode->HSyncStart = Mode->HSyncEnd - CVT_RB_H_SYNC;
260706f2543Smrg
261706f2543Smrg        /* Fill in VSync values */
262706f2543Smrg        Mode->VSyncStart = Mode->VDisplay + CVT_RB_VFPORCH;
263706f2543Smrg        Mode->VSyncEnd = Mode->VSyncStart + VSync;
264706f2543Smrg    }
265706f2543Smrg
266706f2543Smrg    /* 15/13. Find pixel clock frequency (kHz for xf86) */
267706f2543Smrg    Mode->Clock = Mode->HTotal * 1000.0 / HPeriod;
268706f2543Smrg    Mode->Clock -= Mode->Clock % CVT_CLOCK_STEP;
269706f2543Smrg
270706f2543Smrg    /* 16/14. Find actual Horizontal Frequency (kHz) */
271706f2543Smrg    Mode->HSync = ((float) Mode->Clock) / ((float) Mode->HTotal);
272706f2543Smrg
273706f2543Smrg    /* 17/15. Find actual Field rate */
274706f2543Smrg    Mode->VRefresh = (1000.0 * ((float) Mode->Clock)) /
275706f2543Smrg        ((float) (Mode->HTotal * Mode->VTotal));
276706f2543Smrg
277706f2543Smrg    /* 18/16. Find actual vertical frame frequency */
278706f2543Smrg    /* ignore - just set the mode flag for interlaced */
279706f2543Smrg    if (Interlaced)
280706f2543Smrg        Mode->VTotal *= 2;
281706f2543Smrg
282706f2543Smrg    XNFasprintf(&Mode->name, "%dx%d", HDisplay, VDisplay);
283706f2543Smrg
284706f2543Smrg    if (Reduced)
285706f2543Smrg        Mode->Flags |= V_PHSYNC | V_NVSYNC;
286706f2543Smrg    else
287706f2543Smrg        Mode->Flags |= V_NHSYNC | V_PVSYNC;
288706f2543Smrg
289706f2543Smrg    if (Interlaced)
290706f2543Smrg        Mode->Flags |= V_INTERLACE;
291706f2543Smrg
292706f2543Smrg    return Mode;
293706f2543Smrg}
294