/**************************************************************************** * * 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 #include #include #include #include #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 -------------------------------*/ static double round(double v) { 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 [[Hz] [KHz] [MHz]] [I]\n"); printf("\n"); printf("where is the horizontal resolution of the mode, is the\n"); printf("vertical resolution of the mode. The 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 */