2023-11-23 00:11:45 +01:00
|
|
|
/****************************************************************************
|
|
|
|
*
|
|
|
|
* VESA Generalized Timing Formula (GTF)
|
|
|
|
* Version 1.1
|
|
|
|
*
|
|
|
|
* ========================================================================
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the SciTech MGL Public
|
|
|
|
* License Version 1.0 (the "License"); you may not use this file
|
|
|
|
* except in compliance with the License. You may obtain a copy of
|
|
|
|
* the License at http://www.scitechsoft.com/mgl-license.txt
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an
|
|
|
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
|
|
* implied. See the License for the specific language governing
|
|
|
|
* rights and limitations under the License.
|
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is SciTech Software, Inc.
|
|
|
|
* All Rights Reserved.
|
|
|
|
*
|
|
|
|
* ========================================================================
|
|
|
|
*
|
|
|
|
* Developed by: SciTech Software, Inc.
|
|
|
|
*
|
|
|
|
* Language: ANSI C
|
|
|
|
* Environment: Any.
|
|
|
|
*
|
|
|
|
* Description: C module for generating GTF compatible timings given a set
|
|
|
|
* of input requirements. Translated from the original GTF
|
|
|
|
* 1.14 spreadsheet definition.
|
|
|
|
*
|
|
|
|
* Compile with #define TESTING to build a command line test
|
|
|
|
* program.
|
|
|
|
*
|
|
|
|
* NOTE: The code in here has been written for clarity and
|
|
|
|
* to follow the original GTF spec as closely as
|
|
|
|
* possible.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include "gtf.h"
|
|
|
|
|
|
|
|
/*------------------------- Global Variables ------------------------------*/
|
|
|
|
|
|
|
|
static GTF_constants GC = {
|
|
|
|
1.8, /* Margin size as percentage of display */
|
|
|
|
8, /* Character cell granularity */
|
|
|
|
1, /* Minimum front porch in lines/chars */
|
|
|
|
3, /* Width of V sync in lines */
|
|
|
|
8, /* Width of H sync as percent of total */
|
|
|
|
550, /* Minimum vertical sync + back porch (us) */
|
|
|
|
600, /* Blanking formula gradient */
|
|
|
|
40, /* Blanking formula offset */
|
|
|
|
128, /* Blanking formula scaling factor */
|
|
|
|
20, /* Blanking formula scaling factor weight */
|
|
|
|
};
|
|
|
|
|
|
|
|
/*-------------------------- Implementation -------------------------------*/
|
|
|
|
|
2023-11-23 00:35:14 +01:00
|
|
|
double round(double v)
|
2023-11-23 00:11:45 +01:00
|
|
|
{
|
|
|
|
return floor(v + 0.5);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void GetInternalConstants(GTF_constants *c)
|
|
|
|
/****************************************************************************
|
|
|
|
*
|
|
|
|
* Function: GetInternalConstants
|
|
|
|
* Parameters: c - Place to store the internal constants
|
|
|
|
*
|
|
|
|
* Description: Calculates the rounded, internal set of GTF constants.
|
|
|
|
* These constants are different to the real GTF constants
|
|
|
|
* that can be set up for the monitor. The calculations to
|
|
|
|
* get these real constants are defined in the 'Work Area'
|
|
|
|
* after the constants are defined in the Excel spreadsheet.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
{
|
|
|
|
c->margin = GC.margin;
|
|
|
|
c->cellGran = round(GC.cellGran);
|
|
|
|
c->minPorch = round(GC.minPorch);
|
|
|
|
c->vSyncRqd = round(GC.vSyncRqd);
|
|
|
|
c->hSync = GC.hSync;
|
|
|
|
c->minVSyncBP = GC.minVSyncBP;
|
|
|
|
if (GC.k == 0)
|
|
|
|
c->k = 0.001;
|
|
|
|
else
|
|
|
|
c->k = GC.k;
|
|
|
|
c->m = (c->k / 256) * GC.m;
|
|
|
|
c->c = (GC.c - GC.j) * (c->k / 256) + GC.j;
|
|
|
|
c->j = GC.j;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GTF_calcTimings(double hPixels,double vLines,double freq,
|
|
|
|
int type,ibool wantMargins,ibool wantInterlace,GTF_timings *t)
|
|
|
|
/****************************************************************************
|
|
|
|
*
|
|
|
|
* Function: GTF_calcTimings
|
|
|
|
* Parameters: hPixels - X resolution
|
|
|
|
* vLines - Y resolution
|
|
|
|
* freq - Frequency (Hz, KHz or MHz depending on type)
|
|
|
|
* type - 1 - vertical, 2 - horizontal, 3 - dot clock
|
|
|
|
* margins - True if margins should be generated
|
|
|
|
* interlace - True if interlaced timings to be generated
|
|
|
|
* t - Place to store the resulting timings
|
|
|
|
*
|
|
|
|
* Description: Calculates a set of GTF timing parameters given a specified
|
|
|
|
* resolution and vertical frequency. The horizontal frequency
|
|
|
|
* and dot clock will be automatically generated by this
|
|
|
|
* routines.
|
|
|
|
*
|
|
|
|
* For interlaced modes the CRTC parameters are calculated for
|
|
|
|
* a single field, so will be half what would be used in
|
|
|
|
* a non-interlaced mode.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
{
|
|
|
|
double interlace,vFieldRate,hPeriod;
|
|
|
|
double topMarginLines,botMarginLines;
|
|
|
|
double leftMarginPixels,rightMarginPixels;
|
|
|
|
double hPeriodEst,vSyncBP,vBackPorch;
|
|
|
|
double vTotalLines,vFieldRateEst;
|
|
|
|
double hTotalPixels,hTotalActivePixels,hBlankPixels;
|
|
|
|
double idealDutyCycle,hSyncWidth,hSyncBP,hBackPorch;
|
|
|
|
double idealHPeriod;
|
|
|
|
double vFreq,hFreq,dotClock;
|
|
|
|
GTF_constants c;
|
|
|
|
|
|
|
|
/* Get rounded GTF constants used for internal calculations */
|
|
|
|
GetInternalConstants(&c);
|
|
|
|
|
|
|
|
/* Move input parameters into appropriate variables */
|
|
|
|
vFreq = hFreq = dotClock = freq;
|
|
|
|
|
|
|
|
/* Round pixels to character cell granularity */
|
|
|
|
hPixels = round(hPixels / c.cellGran) * c.cellGran;
|
|
|
|
|
|
|
|
/* For interlaced mode halve the vertical parameters, and double
|
|
|
|
* the required field refresh rate.
|
|
|
|
*/
|
|
|
|
if (wantInterlace) {
|
|
|
|
vLines = round(vLines / 2);
|
|
|
|
vFieldRate = vFreq * 2;
|
|
|
|
dotClock = dotClock * 2;
|
|
|
|
interlace = 0.5;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
vFieldRate = vFreq;
|
|
|
|
interlace = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Determine the lines for margins */
|
|
|
|
if (wantMargins) {
|
|
|
|
topMarginLines = round(c.margin / 100 * vLines);
|
|
|
|
botMarginLines = round(c.margin / 100 * vLines);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
topMarginLines = 0;
|
|
|
|
botMarginLines = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type != GTF_lockPF) {
|
|
|
|
if (type == GTF_lockVF) {
|
|
|
|
/* Estimate the horizontal period */
|
|
|
|
hPeriodEst = ((1/vFieldRate) - (c.minVSyncBP/1000000)) /
|
|
|
|
(vLines + (2*topMarginLines) + c.minPorch + interlace) * 1000000;
|
|
|
|
|
|
|
|
/* Find the number of lines in vSync + back porch */
|
|
|
|
vSyncBP = round(c.minVSyncBP / hPeriodEst);
|
|
|
|
}
|
|
|
|
else if (type == GTF_lockHF) {
|
|
|
|
/* Find the number of lines in vSync + back porch */
|
|
|
|
vSyncBP = round((c.minVSyncBP * hFreq) / 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find the number of lines in the V back porch alone */
|
|
|
|
vBackPorch = vSyncBP - c.vSyncRqd;
|
|
|
|
|
|
|
|
/* Find the total number of lines in the vertical period */
|
|
|
|
vTotalLines = vLines + topMarginLines + botMarginLines + vSyncBP
|
|
|
|
+ interlace + c.minPorch;
|
|
|
|
|
|
|
|
if (type == GTF_lockVF) {
|
|
|
|
/* Estimate the vertical frequency */
|
|
|
|
vFieldRateEst = 1000000 / (hPeriodEst * vTotalLines);
|
|
|
|
|
|
|
|
/* Find the actual horizontal period */
|
|
|
|
hPeriod = (hPeriodEst * vFieldRateEst) / vFieldRate;
|
|
|
|
|
|
|
|
/* Find the actual vertical field frequency */
|
|
|
|
vFieldRate = 1000000 / (hPeriod * vTotalLines);
|
|
|
|
}
|
|
|
|
else if (type == GTF_lockHF) {
|
|
|
|
/* Find the actual vertical field frequency */
|
|
|
|
vFieldRate = (hFreq / vTotalLines) * 1000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find the number of pixels in the left and right margins */
|
|
|
|
if (wantMargins) {
|
|
|
|
leftMarginPixels = round(hPixels * c.margin) / (100 * c.cellGran);
|
|
|
|
rightMarginPixels = round(hPixels * c.margin) / (100 * c.cellGran);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
leftMarginPixels = 0;
|
|
|
|
rightMarginPixels = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find the total number of active pixels in image + margins */
|
|
|
|
hTotalActivePixels = hPixels + leftMarginPixels + rightMarginPixels;
|
|
|
|
|
|
|
|
if (type == GTF_lockVF) {
|
|
|
|
/* Find the ideal blanking duty cycle */
|
|
|
|
idealDutyCycle = c.c - ((c.m * hPeriod) / 1000);
|
|
|
|
}
|
|
|
|
else if (type == GTF_lockHF) {
|
|
|
|
/* Find the ideal blanking duty cycle */
|
|
|
|
idealDutyCycle = c.c - (c.m / hFreq);
|
|
|
|
}
|
|
|
|
else if (type == GTF_lockPF) {
|
|
|
|
/* Find ideal horizontal period from blanking duty cycle formula */
|
|
|
|
idealHPeriod = (((c.c - 100) + (sqrt((pow(100-c.c,2)) +
|
|
|
|
(0.4 * c.m * (hTotalActivePixels + rightMarginPixels +
|
|
|
|
leftMarginPixels) / dotClock)))) / (2 * c.m)) * 1000;
|
|
|
|
|
|
|
|
/* Find the ideal blanking duty cycle */
|
|
|
|
idealDutyCycle = c.c - ((c.m * idealHPeriod) / 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find the number of pixels in blanking time */
|
|
|
|
hBlankPixels = round((hTotalActivePixels * idealDutyCycle) /
|
|
|
|
((100 - idealDutyCycle) * 2 * c.cellGran)) * (2 * c.cellGran);
|
|
|
|
|
|
|
|
/* Find the total number of pixels */
|
|
|
|
hTotalPixels = hTotalActivePixels + hBlankPixels;
|
|
|
|
|
|
|
|
/* Find the horizontal back porch */
|
|
|
|
hBackPorch = round((hBlankPixels / 2) / c.cellGran) * c.cellGran;
|
|
|
|
|
|
|
|
/* Find the horizontal sync width */
|
|
|
|
hSyncWidth = round(((c.hSync/100) * hTotalPixels) / c.cellGran) * c.cellGran;
|
|
|
|
|
|
|
|
/* Find the horizontal sync + back porch */
|
|
|
|
hSyncBP = hBackPorch + hSyncWidth;
|
|
|
|
|
|
|
|
if (type == GTF_lockPF) {
|
|
|
|
/* Find the horizontal frequency */
|
|
|
|
hFreq = (dotClock / hTotalPixels) * 1000;
|
|
|
|
|
|
|
|
/* Find the horizontal period */
|
|
|
|
hPeriod = 1000 / hFreq;
|
|
|
|
|
|
|
|
/* Find the number of lines in vSync + back porch */
|
|
|
|
vSyncBP = round((c.minVSyncBP * hFreq) / 1000);
|
|
|
|
|
|
|
|
/* Find the number of lines in the V back porch alone */
|
|
|
|
vBackPorch = vSyncBP - c.vSyncRqd;
|
|
|
|
|
|
|
|
/* Find the total number of lines in the vertical period */
|
|
|
|
vTotalLines = vLines + topMarginLines + botMarginLines + vSyncBP
|
|
|
|
+ interlace + c.minPorch;
|
|
|
|
|
|
|
|
/* Find the actual vertical field frequency */
|
|
|
|
vFieldRate = (hFreq / vTotalLines) * 1000;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (type == GTF_lockVF) {
|
|
|
|
/* Find the horizontal frequency */
|
|
|
|
hFreq = 1000 / hPeriod;
|
|
|
|
}
|
|
|
|
else if (type == GTF_lockHF) {
|
|
|
|
/* Find the horizontal frequency */
|
|
|
|
hPeriod = 1000 / hFreq;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find the pixel clock frequency */
|
|
|
|
dotClock = hTotalPixels / hPeriod;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find the vertical frame frequency */
|
|
|
|
if (wantInterlace) {
|
|
|
|
vFreq = vFieldRate / 2;
|
|
|
|
dotClock = dotClock / 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
vFreq = vFieldRate;
|
|
|
|
|
|
|
|
/* Return the computed frequencies */
|
|
|
|
t->vFreq = vFreq;
|
|
|
|
t->hFreq = hFreq;
|
|
|
|
t->dotClock = dotClock;
|
|
|
|
|
|
|
|
/* Determine the vertical timing parameters */
|
|
|
|
t->h.hTotal = hTotalPixels;
|
|
|
|
t->h.hDisp = hTotalActivePixels;
|
|
|
|
t->h.hSyncStart = t->h.hTotal - hSyncBP;
|
|
|
|
t->h.hSyncEnd = t->h.hTotal - hBackPorch;
|
|
|
|
t->h.hFrontPorch = t->h.hSyncStart - t->h.hDisp;
|
|
|
|
t->h.hSyncWidth = hSyncWidth;
|
|
|
|
t->h.hBackPorch = hBackPorch;
|
|
|
|
|
|
|
|
/* Determine the vertical timing parameters */
|
|
|
|
t->v.vTotal = vTotalLines;
|
|
|
|
t->v.vDisp = vLines;
|
|
|
|
t->v.vSyncStart = t->v.vTotal - vSyncBP;
|
|
|
|
t->v.vSyncEnd = t->v.vTotal - vBackPorch;
|
|
|
|
t->v.vFrontPorch = t->v.vSyncStart - t->v.vDisp;
|
|
|
|
t->v.vSyncWidth = c.vSyncRqd;
|
|
|
|
t->v.vBackPorch = vBackPorch;
|
|
|
|
|
|
|
|
/* Mark as GTF timing using the sync polarities */
|
|
|
|
t->interlace = (wantInterlace) ? 'I' : 'N';
|
|
|
|
t->hSyncPol = '-';
|
|
|
|
t->vSyncPol = '+';
|
|
|
|
}
|
|
|
|
|
|
|
|
void GTF_getConstants(GTF_constants *constants)
|
|
|
|
{ *constants = GC; }
|
|
|
|
|
|
|
|
void GTF_setConstants(GTF_constants *constants)
|
|
|
|
{ GC = *constants; }
|
|
|
|
|
|
|
|
#ifdef TESTING_GTF
|
|
|
|
|
|
|
|
int main(int argc,char *argv[])
|
|
|
|
{
|
|
|
|
FILE *f;
|
|
|
|
double xPixels,yPixels,freq;
|
|
|
|
ibool interlace;
|
|
|
|
GTF_timings t;
|
|
|
|
|
|
|
|
if (argc != 5 && argc != 6) {
|
|
|
|
printf("Usage: GTFCALC <xPixels> <yPixels> <freq> [[Hz] [KHz] [MHz]] [I]\n");
|
|
|
|
printf("\n");
|
|
|
|
printf("where <xPixels> is the horizontal resolution of the mode, <yPixels> is the\n");
|
|
|
|
printf("vertical resolution of the mode. The <freq> value will be the frequency to\n");
|
|
|
|
printf("drive the calculations, and will be either the vertical frequency (in Hz)\n");
|
|
|
|
printf("the horizontal frequency (in KHz) or the dot clock (in MHz). To generate\n");
|
|
|
|
printf("timings for an interlaced mode, add 'I' to the end of the command line.\n");
|
|
|
|
printf("\n");
|
|
|
|
printf("For example to generate timings for 640x480 at 60Hz vertical:\n");
|
|
|
|
printf("\n");
|
|
|
|
printf(" GTFCALC 640 480 60 Hz\n");
|
|
|
|
printf("\n");
|
|
|
|
printf("For example to generate timings for 640x480 at 31.5KHz horizontal:\n");
|
|
|
|
printf("\n");
|
|
|
|
printf(" GTFCALC 640 480 31.5 KHz\n");
|
|
|
|
printf("\n");
|
|
|
|
printf("For example to generate timings for 640x480 with a 25.175Mhz dot clock:\n");
|
|
|
|
printf("\n");
|
|
|
|
printf(" GTFCALC 640 480 25.175 MHz\n");
|
|
|
|
printf("\n");
|
|
|
|
printf("GTFCALC will print a summary of the results found, and dump the CRTC\n");
|
|
|
|
printf("values to the UVCONFIG.CRT file in the format used by SciTech Display Doctor.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get values from command line */
|
|
|
|
xPixels = atof(argv[1]);
|
|
|
|
yPixels = atof(argv[2]);
|
|
|
|
freq = atof(argv[3]);
|
|
|
|
interlace = ((argc == 6) && (argv[5][0] == 'I'));
|
|
|
|
|
|
|
|
/* Compute the CRTC timings */
|
|
|
|
if (toupper(argv[4][0]) == 'H')
|
|
|
|
GTF_calcTimings(xPixels,yPixels,freq,GTF_lockVF,false,interlace,&t);
|
|
|
|
else if (toupper(argv[4][0]) == 'K')
|
|
|
|
GTF_calcTimings(xPixels,yPixels,freq,GTF_lockHF,false,interlace,&t);
|
|
|
|
else if (toupper(argv[4][0]) == 'M')
|
|
|
|
GTF_calcTimings(xPixels,yPixels,freq,GTF_lockPF,false,interlace,&t);
|
|
|
|
else {
|
|
|
|
printf("Unknown command line!\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dump summary info to standard output */
|
|
|
|
printf("CRTC values for %.0fx%.0f @ %.2f %s\n", xPixels, yPixels, freq, argv[4]);
|
|
|
|
printf("\n");
|
|
|
|
printf(" hTotal = %-4d vTotal = %-4d\n",
|
|
|
|
t.h.hTotal, t.v.vTotal);
|
|
|
|
printf(" hDisp = %-4d vDisp = %-4d\n",
|
|
|
|
t.h.hDisp, t.v.vDisp);
|
|
|
|
printf(" hSyncStart = %-4d vSyncStart = %-4d\n",
|
|
|
|
t.h.hSyncStart, t.v.vSyncStart);
|
|
|
|
printf(" hSyncEnd = %-4d vSyncEnd = %-4d\n",
|
|
|
|
t.h.hSyncEnd, t.v.vSyncEnd);
|
|
|
|
printf(" hFrontPorch = %-4d vFrontPorch = %-4d\n",
|
|
|
|
t.h.hFrontPorch, t.v.vFrontPorch);
|
|
|
|
printf(" hSyncWidth = %-4d vSyncWidth = %-4d\n",
|
|
|
|
t.h.hSyncWidth, t.v.vSyncWidth);
|
|
|
|
printf(" hBackPorch = %-4d vBackPorch = %-4d\n",
|
|
|
|
t.h.hBackPorch, t.v.vBackPorch);
|
|
|
|
printf("\n");
|
|
|
|
printf(" Interlaced = %s\n", (t.interlace == 'I') ? "Yes" : "No");
|
|
|
|
printf(" H sync pol = %c\n", t.hSyncPol);
|
|
|
|
printf(" V sync pol = %c\n", t.vSyncPol);
|
|
|
|
printf("\n");
|
|
|
|
printf(" Vert freq = %.2f Hz\n", t.vFreq);
|
|
|
|
printf(" Horiz freq = %.2f KHz\n", t.hFreq);
|
|
|
|
printf(" Dot Clock = %.2f Mhz\n", t.dotClock);
|
|
|
|
fprintf(stderr,"Modeline %c%ix%i@%.0f%c %.3f %i %i %i %i %i %i %i %i %s %chsync %cvsync\n",
|
|
|
|
'"',t.h.hDisp,t.v.vDisp,t.vFreq,'"',
|
|
|
|
t.dotClock,
|
|
|
|
t.h.hDisp,
|
|
|
|
t.h.hSyncStart,
|
|
|
|
t.h.hSyncEnd,
|
|
|
|
t.h.hTotal,
|
|
|
|
t.v.vDisp,
|
|
|
|
t.v.vSyncStart,
|
|
|
|
t.v.vSyncEnd,
|
|
|
|
t.v.vTotal,
|
|
|
|
(t.interlace == 'I') ? "Interlace" : "",
|
|
|
|
t.hSyncPol,
|
|
|
|
t.vSyncPol
|
|
|
|
);
|
|
|
|
|
|
|
|
/* Dump to file in format used by SciTech Display Doctor */
|
|
|
|
if ((f = fopen("UVCONFIG.CRT","wt")) != NULL) {
|
|
|
|
fprintf(f, "[%.0f %.0f]\n", xPixels, yPixels);
|
|
|
|
fprintf(f, "%d %d %d %d '%c' %s\n",
|
|
|
|
t.h.hTotal, t.h.hDisp,
|
|
|
|
t.h.hSyncStart, t.h.hSyncEnd,
|
|
|
|
t.hSyncPol, (t.interlace == 'I') ? "I" : "NI");
|
|
|
|
fprintf(f, "%d %d %d %d '%c'\n",
|
|
|
|
t.v.vTotal, t.v.vDisp,
|
|
|
|
t.v.vSyncStart, t.v.vSyncEnd,
|
|
|
|
t.vSyncPol);
|
|
|
|
fprintf(f, "%.2f\n", t.dotClock);
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* TESTING */
|