windows-nt/Source/XPSP1/NT/drivers/video/matrox/mga/mini/clock.c
2020-09-26 16:20:57 +08:00

1539 lines
41 KiB
C

/* ======================================================================= */
/*
/* Filename : CLOCK.C
/* Creation : Dominique Leblanc 92/10/26
/*
/*
/* This file contains the different routines to program the ICD2061
/* oscillator for the MGA family.
/*
/* Modifications List:
/* D.L. 92/11/30: modification for a limited condition of
/* searching for an exact frequency.
/*
/* Bart Simpson: Adaptation for CADDI
/*
/* ======================================================================= */
#include "switches.h"
#include "g3dstd.h"
#include "caddi.h"
#include "def.h"
#include "mga.h"
#include "global.h"
#include "proto.h"
#include "mgai_c.h"
#include "mgai.h"
#ifdef WINDOWS_NT
#if defined(ALLOC_PRAGMA)
#pragma alloc_text(PAGE,setFrequence)
#pragma alloc_text(PAGE,programme_clock)
#pragma alloc_text(PAGE,dummyCallDelai)
#pragma alloc_text(PAGE,programme_reg_icd)
#pragma alloc_text(PAGE,send_unlock)
#pragma alloc_text(PAGE,send_data)
#pragma alloc_text(PAGE,send_full_clock)
#pragma alloc_text(PAGE,send_start)
#pragma alloc_text(PAGE,send_0)
#pragma alloc_text(PAGE,send_1)
#pragma alloc_text(PAGE,send_stop)
#pragma alloc_text(PAGE,setTVP3026Freq)
#endif
#if defined(ALLOC_PRAGMA)
#pragma data_seg("PAGE")
#endif
#include "video.h"
#endif /* #ifdef WINDOWS_NT */
static volatile BYTE _Far * pDevice;
typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned long dword;
/* this keep mclk f(vco) value to use with jitter VCLK=MCLK output value. */
/* updated by function <ProgrammeClock> in module mtxinit.c */
extern void delay_us(dword delai);
extern long presentMclk[]; /* MCLK currently in use (module mtxinit.c) */
extern byte iBoard; /* index of current selected board (module mtxinit.c)*/
typedef struct
{
short p;
short q;
short m;
short i;
long trueFout;
long fvco; /* for jitter problem, keep osc. value */
long ppmError;
} ST_RESULTAT;
static ST_RESULTAT result;
static byte init_misc;
typedef union
{
byte var8;
byte byte;
byte octet;
struct
{
byte enable_color : 1; /* 1=COLOR:0x3D?, 0=MONO:0x3B? */
byte enable_RAM : 1;
byte cs0 : 1;
byte cs1 : 1;
byte reserved : 1;
byte page_select : 1;
byte h_pol : 1;
byte v_pol : 1;
} bit;
struct
{
byte unused : 2;
byte pgmclk : 1;
byte pgmdata : 1;
byte reserved : 4;
} clock_bit;
struct
{
byte unused : 2;
byte select : 2;
byte reserved : 4;
} sel_reg_output;
} ST_MISC_OUTPUT;
#define MISC_OUTPUT_WRITE 0x03C2
#define MISC_OUTPUT_READ 0x03CC
#define NBRE_M_POSSIBLE 7
static ST_MISC_OUTPUT misc;
typedef union
{
dword var32;
dword dmot;
dword ulong;
struct
{
dword q_prime : 7;
dword m_diviseur : 3;
dword p_prime : 7;
dword index_field : 4;
dword unused : 11;
} bit;
} ST_REG_PROGRAM_DATA;
ST_REG_PROGRAM_DATA reg_clock[4] = { { 0x5A8BCL }, /* REG0 video 1 */
{ 0x960ACL }, /* REG1 video 2 */
{ 0x960ACL }, /* REG2 video 3 */
{ 0xD44A3L }, /* MREG memory */
};
static long diviseur_m [ NBRE_M_POSSIBLE ] =
{
1, /* 0 */
2, /* 1 */
4, /* 2 */
8, /* 3 */
16, /* 4 */
32, /* 5 */
64, /* 6 */
/* 64, /* 7 */ /* we don't use this case in our calculations */
};
/* static long limites_i [ NBRE_I_POSSIBLE ] = old see below */
static long limites_i [] =
{/* min, max, I */
50000L, /* 47500000 0 */
51000L, /* 47500000 1 */
53200L, /* 52200000 2 */
58500L, /* 56300000 3 */
60700L, /* 61900000 4 */
64400L, /* 65000000 5 */
66800L, /* 68100000 6 */
73500L, /* 82300000 7 */
75600L, /* 86000000 8 */
80900L, /* 88000000 9 */
83200L, /* 90500000 10 */
91500L, /* 95000000 11 */
100000L, /* 1.0E+08 12 */
120000L, /* 1.2E+08 13 */
/* 120000000L, /* 14 */
};
#define NBRE_I_POSSIBLE ( (sizeof(limites_i)) / (sizeof(long)) )
/* old frequency & keep default value (power up)
* there is output frequency value f(o)
*/
long old_fr_reg[4] = { { 25175L }, /* REG0 video 1 */
{ 28322L }, /* REG1 video 2 */
{ 28322L }, /* REG2 video 3 */
{ 32500L }, /* MREG memory */
};
/**************** LIST OF REGISTERS OF ICD2061 ************************/
#define NBRE_REG_ICD2061 6
#define VIDEO_CLOCK_1 0
#define VIDEO_CLOCK_2 1
#define VIDEO_CLOCK_3 2
#define MEMORY_CLOCK 3
#define POWERDWN_REG 4
#define CONTROL_ICD2061 6
static long fref;
/**************************** list of routines in this file *************/
dword programme_clock ( short reg, short p, short q, short m, short i );
void programme_reg_icd ( volatile BYTE _Far * pDeviceParam, short reg, dword data );
static void send_unlock ( void );
static void send_data ( short reg, dword data );
static void send_full_clock ( void );
static void send_start ( void );
static void send_0 ( void );
static void send_1 ( void );
static void send_stop ( void );
static byte selectIcdOutputReg ( long reg );
static void LowerVCO(long Fout, byte pll, byte pwidth, dword fvcomax, byte dac_rev1);
/*** Temporary delay to be put after each acces to programable register ***/
void dummyCallDelai()
{
byte TmpByte;
mgaReadBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), TmpByte);
}
/***************************************************************************/
/*/ setFrequence ( volatile BYTE _Far *pDeviceParam, long fout, long reg )
*
* For a given frequency, determines the best values to put for the
* programming of the ICD2061.
* Programs the register.
* If outgoing frequency is 0, this function only chooses the output register.
*
* Problemes :
* Concu : Dominique Leblanc:92/10/29
*
* Parametres: [0]-frequency output.
* [1]-register (0, 1, 2 ou 3) which will be used
* (the register 3 MCLK cannot be used as output
*
* Returns : programmed frequency (or 0)
*
*/
long setFrequence ( volatile BYTE _Far *pDeviceParam, long fout, long reg )
{
short i;
long p, q, m;
short index;
short i_find;
long old_frequency;
long fvco, trueFout, desiredFout;
long fdivise;
long ppmError;
long bestError;
long ftmp;
fref = 1431818; /* frequence XTAL */
pDevice = pDeviceParam;
mgaReadBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_R), init_misc);
misc.var8 = init_misc;
/* special condition: if VCLK is a multiple of MCLK,
* use special programming multiple VCLK=MCLK
*/
if ( reg != MEMORY_CLOCK )
{
/*
* 4&3rd case include in same one "for(i=0;...
* at place of check1(), and for(i=1;...)"
*
* 4rd case: MCLK is soo far as VCLK (.5%)
* look for jither problems if both oscillator clock are same or
* closer one from the other, change video frequency to
* use MCLK redirection.
* 3rd case: MCLK is multiple of VCLK
* if mclk is multiple of mclk, divide vclk
* ex. mclk=50MHz, vclk=25MHz,
* vclk(fvco)=mclk(fvco)
* mclk==> 50mhz,
* vclk==> 50mhz/2
*/
i_find = NO;
for ( i = 0 ; ( i < NBRE_M_POSSIBLE && i_find == NO ) ; i++ )
{
ftmp = ( fout * 1000L ) * diviseur_m[i];
if ( ( ftmp >= ( presentMclk[iBoard] - (presentMclk[iBoard]/200) ) ) &&
( ftmp <= ( presentMclk[iBoard] + (presentMclk[iBoard]/200) ) ) )
{
i_find = YES; /* MCLK is multiple of VCLK */
index = i; /* keep divisor value */
}
}
if ( i_find == YES )
{
old_frequency = old_fr_reg[reg];
old_fr_reg[reg] = fout / 1000L;
/*
* We program VCLK since it use same MCLK fvco origin.
* We reprogram only I value in register to use MCLK on
* VCLCK, and m divisor (see ICD2061A for more details).
*/
/***** putMclkOnVclk ( reg, index ); *****/
reg_clock[reg].bit.m_diviseur = index;
reg_clock[reg].bit.index_field = 0xF; /* MCLK fvco value */
programme_reg_icd (pDevice, (short)reg, reg_clock[reg].var32);
dummyCallDelai();
selectIcdOutputReg ( reg );
return ( old_frequency ); /* QUIT */
}
}
/***************************************************/
/*
/* MUST ONLY PROGRAM THE REGISTER
/*
/***************************************************/
if ( fout == 0L )
{
switch ( reg )
{
case VIDEO_CLOCK_1:
case VIDEO_CLOCK_2:
misc.sel_reg_output.select = (byte)reg;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
break;
case VIDEO_CLOCK_3:
misc.sel_reg_output.select = 3;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
break;
default:
/* par defaut, (si on a modifie MCLK), on restore VCLK
* initial
*/
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), init_misc);
break;
}
return ( NO );
}
/***************************************************/
/*
/* MUST CALCULATE A FREQUENCY
/*
/***************************************************/
desiredFout = fout;
index = 0;
bestError = 99999999; /* dummy start number */
for ( m = 0 ; m < NBRE_M_POSSIBLE ; m++ ) /* for all possible divisors */
{
fdivise = diviseur_m[m];
fvco = fdivise * desiredFout;
if ((fvco < 40000) || (fvco > 120000))
continue;
for ( q = 3 ; q <= 129 ; q++ )
{
if ( ((fref/q) < 20000) || ((fref/q) > 100000))
continue;
p = ((q * fvco * 100) / (2 * fref)) + 1;
if (p < 4 || p > 130)
continue;
/**************************************************
* now that we have all our values
* we determine the true f(output), and its error.
*/
trueFout = (2 * fref * p) / (q * fdivise * 100) ;
if (trueFout > desiredFout)
ppmError = trueFout - desiredFout;
else
ppmError = desiredFout - trueFout;
if ( ppmError < bestError )
{
/***************************************************
*
* HO!!! this result is better than the preceding one
*
*/
i_find = -1;
for ( i = 0 ; i < NBRE_I_POSSIBLE ; i++ )
{
if ( ( fvco >= limites_i[i] ) && ( fvco <= limites_i[i+1] ) )
i_find = i;
}
if ( i_find != -1 )
{
index = 0; /* reset result table */
result.p = (short)p;
result.q = (short)q;
result.m = (short)m;
result.i = i_find;
result.trueFout = trueFout;
result.fvco = fout; /* keep real value */
result.ppmError = ppmError;
index++; /* find a good value */
bestError = ppmError; /* reset reference erreur */
}
}
}
}
if ( index == 0 )
{
/* restore valeur de depart */
/* outb ( MISC_OUTPUT_WRITE , init_misc ); */
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), init_misc);
}
else
{
programme_clock ( (short)reg, result.p,
result.q,
result.m,
result.i );
selectIcdOutputReg ( reg );
/* SAVE PRESENT FREQUENCY in kHz */
old_frequency = old_fr_reg[reg]; /* RETURN VALUE */
old_fr_reg[reg] = fout / 1000L;
}
dummyCallDelai();
return (old_frequency);
}
/* ======================================================================= */
/*/
* NAME: selectIcdOutputReg ( long reg )
*
* Select desired output register.
*
*/
static byte selectIcdOutputReg ( long reg )
{
/********************************************/
/*
/* PROGRAMME CS : clock output select
/*
/********************************************/
switch ( reg )
{
case VIDEO_CLOCK_1:
case VIDEO_CLOCK_2:
misc.sel_reg_output.select = (byte)reg;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
break;
case VIDEO_CLOCK_3:
misc.sel_reg_output.select = 3;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
break;
default: /*** MEMORY CLOCK ***/
/*
* if we modify MCLK, restore VCLK initial value
*/
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), init_misc);
presentMclk[iBoard] = result.fvco;
break;
}
dummyCallDelai();
return ( misc.var8 );
}
/* ======================================================================= */
/*/
* NAME: programme_clock ( short p, short q, short m, short reg )
*
* Program the clock of the programmable oscillator for the desired
* frequency.
*
*/
dword programme_clock ( short reg, short p, short q, short m, short i )
{
reg_clock [ reg ].var32 = 0;
reg_clock [ reg ].bit.q_prime = q - 2;
reg_clock [ reg ].bit.m_diviseur = m;
reg_clock [ reg ].bit.p_prime = p - 3;
reg_clock [ reg ].bit.index_field = i;
programme_reg_icd ( pDevice, reg, reg_clock [ reg ].var32 );
dummyCallDelai();
return ( reg_clock [ reg ] .var32 );
}
/* ======================================================================= */
/*/
* NAME: programme_reg_icd ( volatile BYTE _Far *pDeviceParam, short reg, dword data )
*
* This routine permits the serial programming of the programmable oscillator
* (ICD2061). It uses the following communication protocol
* (from HARDWARE SPECs):
*
*
* ^ . . . . . .
* | . . . . . .
* | . _ _ _ _ _ ___ ____ ____ ___ _._
* CLK |_____| |_| |_| |_| |_| |___| . |___| . |___| . |___| . |___| .
* | . . . . . .
* | . . . . . .
* | . _____________________ . . ___ . ___________._
* DATA |___| |____________| |________| . .
* +----------------------------------------------------------------->
* | . unlock sequence . start . send 0 . send 1 . stop .
* | . . bit . . . bit .
* |
*
*
*
* Parameters: [0] - reg: address of internal register of the ICD2061 (3 bits).
* [1] - data: data to write in reg (21 bits).
*
*/
void programme_reg_icd ( volatile BYTE _Far *pDeviceParam, short reg, dword data )
{
pDevice = pDeviceParam;
send_unlock ();
send_start ();
send_data ( reg, data );
send_stop ();
}
/* ======================================================================= */
/*/
* NAME: send_unlock ( void )
*
* Send unlock sequence
*
*/
static void send_unlock ( void )
{
misc.clock_bit.pgmdata = 1;
misc.clock_bit.pgmclk = 0;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
dummyCallDelai();
send_full_clock ();
send_full_clock ();
send_full_clock ();
send_full_clock ();
send_full_clock ();
}
/* ======================================================================= */
/*/
* NAME: send_data ( void )
*
* Sends 21 bits of data + 3 bits for the register.
*
*/
static void send_data ( short reg, dword data )
{
short i;
for ( i = 0 ; i < 21 ; i++ )
{
if ( ( data & 1 ) == 1 )
send_1 ();
else
send_0 ();
data >>= 1;
}
for ( i = 0 ; i < 3 ; i++ )
{
if ( ( reg & 1 ) == 1 )
send_1 ();
else
send_0 ();
reg >>= 1;
}
}
/* ======================================================================= */
/*/
* NAME: send_full_clock ( void )
*
* Toggle one full clock (used only by send_unlock()").
*
*/
static void send_full_clock ( void )
{
misc.clock_bit.pgmclk = 1;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
/*** delay to be sure to respect the programming setup time of the ICD2061 */
dummyCallDelai();
misc.clock_bit.pgmclk = 0;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
/*** delay to be sure to respect the programming setup time of the ICD2061 */
dummyCallDelai();
}
/* ======================================================================= */
/*/
* NAME: send_start ( void )
*
* Send start sequence
*
*/
static void send_start ( void )
{
misc.clock_bit.pgmdata = 0;
misc.clock_bit.pgmclk = 0;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
dummyCallDelai();
send_full_clock ();
misc.clock_bit.pgmdata = 0;
misc.clock_bit.pgmclk = 1;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
dummyCallDelai();
}
/* ======================================================================= */
/*/
* NAME: send_0 ( void )
*
* Send data bit 0 (protocol "MANCHESTER").
*
*/
static void send_0 ( void )
{
misc.clock_bit.pgmdata = 0;
misc.clock_bit.pgmclk = 1;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
dummyCallDelai();
misc.clock_bit.pgmdata = 1;
misc.clock_bit.pgmclk = 1;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
dummyCallDelai();
misc.clock_bit.pgmdata = 1;
misc.clock_bit.pgmclk = 0;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
dummyCallDelai();
misc.clock_bit.pgmdata = 0;
misc.clock_bit.pgmclk = 0;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
dummyCallDelai();
misc.clock_bit.pgmdata = 0;
misc.clock_bit.pgmclk = 1;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
dummyCallDelai();
}
/* ======================================================================= */
/*/
* NAME: send_1 ( void )
*
* Send data bit 1 (protocol "MANCHESTER").
*
*/
static void send_1 ( void )
{
misc.clock_bit.pgmdata = 0;
misc.clock_bit.pgmclk = 1;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
dummyCallDelai();
misc.clock_bit.pgmdata = 0;
misc.clock_bit.pgmclk = 0;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
dummyCallDelai();
misc.clock_bit.pgmdata = 1;
misc.clock_bit.pgmclk = 0;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
dummyCallDelai();
misc.clock_bit.pgmdata = 1;
misc.clock_bit.pgmclk = 1;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
dummyCallDelai();
}
/* ======================================================================= */
/*/
* NAME: send_stop ( void )
*
* Send stop sequence
*
*/
static void send_stop ( void )
{
misc.clock_bit.pgmdata = 1;
misc.clock_bit.pgmclk = 1;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
dummyCallDelai();
misc.clock_bit.pgmdata = 1;
misc.clock_bit.pgmclk = 0;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
dummyCallDelai();
misc.clock_bit.pgmdata = 1;
misc.clock_bit.pgmclk = 1;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
dummyCallDelai();
misc.clock_bit.pgmdata = 0;
misc.clock_bit.pgmclk = 1;
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
dummyCallDelai();
}
/* ======================================================================= */
/***************************************************************************/
/*/ setTVP3026Freq ( volatile byte _Far *pDeviceParam, long desiredFout, long reg, word pWidth )
*
* Calculate best value to obtain a frequency output.
* This routine program and select the register.
* If fout is 0, just toggle to the desired
* output register.
*
* Problems :
* Designed : Patrick Servais:94/04/08
*
* Parameters: [0]-desired output frequency (in kHz).
* (0 -> select desired register)
* [1]-register (0, 1, 2 ou 3) to prgram and select.
* (the MCLOCK register (3) can be reprogrammable, but
* is always available for output - no selection is permit).
*
* Call :
*
* Used :
* Modify : Benoit Leblanc
*
* Return : old frequency value
*
* List of modifications :
*
*/
long setTVP3026Freq ( volatile byte _Far *pDeviceParam, long fout, long reg, byte pWidth )
{
word i;
short p, pixel_p, pixel_n, q, n, bestN;
int m, pixel_m, bestM, tmp;
short index;
short val;
long old_frequency, z;
long fvco, fTemp, trueFout, desiredFout;
long ppmError;
long bestError;
byte init_misc, dac_rev1;
byte tmpByte, saveByte;
word pixelWidth;
dword power;
dword config200Mhz, fvcoMax;
switch(pWidth)
{
case 0:
pixelWidth = 8;
break;
case 1:
pixelWidth = 16;
break;
case 2:
pixelWidth = 32;
break;
}
/* Hard to 16*/
pDevice = pDeviceParam;
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), 0x01); /* Silicon revision */
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte);
if (tmpByte >= 0x10)
dac_rev1 = 1;
else
dac_rev1 = 0;
/* Read 200Mhz straps saved in config register */
mgaReadDWORD(*(pDevice+TITAN_OFFSET + TITAN_CONFIG), config200Mhz);
if (config200Mhz & 0x00000004)
{
fvcoMax = (dword)220000000; /* 200Mhz support */
}
else
{
fvcoMax = (dword)175000000; /* 200Mhz not support */
}
mgaReadBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_R), init_misc);
misc.var8 = init_misc;
/***************************************************/
/*
/* CALCULATE FREQUENCY
/*
/***************************************************/
bestError = 99999999; /* dummy start number */
fref = 14318180; /* frequence clock ref */
index = 0;
bestError = 5000000;
desiredFout = fout * 1000; /* Scale it from KHz to Hz */
if ((dword) desiredFout>= (fvcoMax >> 1))
p = 0;
else if ((dword) desiredFout>=(fvcoMax >> 2))
p = 1;
else if ((dword) desiredFout>=(fvcoMax >> 3))
p = 2;
else
p = 3;
power = 1;
for(i=0; i<p; i++)
power = power * 2;
for ( n=1;n<=63;n++ )
{
m = (650 - (((((dword)desiredFout*10)/fref) * ((65-n) * power)) / 8)) / 10;
fTemp = fref / (65-n);
fvco = fTemp * 8 * (65-m);
trueFout = fvco / power;
if (trueFout < desiredFout)
ppmError = desiredFout - trueFout;
else
ppmError = trueFout - desiredFout;
if ((ppmError < bestError) &&
(m > 0) && (m <= 63) &&
(fTemp > 500000) &&
((dword)fvco >= (fvcoMax >> 1) ) && (fvco <= (dword)220000000))
{
index = 1;
bestError = ppmError;
bestM = m;
bestN = n;
}
}
m = bestM;
n = bestN;
fTemp = fref / (65-n);
fvco = fTemp * 8 * (65-m);
{
dword num;
num = ((65 - m)*10) / (65-n);
num = num * 8 * fref;
trueFout = (num / power) / 10;
}
if ( index == 0 ) /* no solution find */
{
/* ***ERROR: setFrequence() NONE RESULT (IMPOSSIBLE?!?) */
/* restore valeur de depart */
old_frequency = 0L; /* ERREUR RETURN */
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), init_misc);
}
else
{
/**********************************************************************
*
* SET THE DESIRED FREQUENCY OUTPUT REGISTER
*
*/
switch ( reg )
{
case VIDEO_CLOCK_3: /* NOTE 1: header */
misc.sel_reg_output.select = 3; /* NOTE 1: header */
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), misc.var8);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PLL_ADDR);
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte & 0xfc);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PIX_CLK_DATA );
if (dac_rev1)
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), ((n&0x3f)|0xc0) );
else
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), ((n&0x3f)|0x80) );
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), (m&0x3f) );
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), ((p&0x03)|0xf0) );
tmp = 0;
do
{
tmp += 1;
delay_us(10);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PIX_CLK_DATA);
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte);
tmpByte &= 0x40;
} while((tmpByte != 0x40) && (tmp < 5000));
if((tmp == 5000) && (fout < 1100000) && (fvcoMax == 220000000))
LowerVCO(fout, 0, pWidth, fvcoMax, dac_rev1);
/* searching for loop clock parameters */
n = 65 - ((4*64)/pixelWidth); /* 64 is the Pixel bus Width */
m = 0x3d;
z = ((65L-n)*2750L)/(fout/1000);
q = 0;
p = 3;
if (z <= 200)
p = 0;
else if (z <= 400)
p = 1;
else if (z <= 800)
p = 2;
else if (z <=1600)
p = 3;
else
q = (short)(z/1600);
if (dac_rev1)
n |= 0xc0;
else
n |= 0x80;
if ((dac_rev1 == 0) && (fout <= 175000))
p |= 0xb0;
else
p |= 0xf0;
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_MCLK_CTL);
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte);
val = tmpByte;
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), ((val&0xf8)|q) | 0x20);
if ((pWidth == TITAN_PWIDTH_PW24) && (dac_rev1))
{
if (desiredFout >= 50000000)
{n = 0xf9; p = 0xf9; m=0x3e;}
else
{n = 0xf9; p = 0xfb; m=0x3e;}
}
else if ((pWidth == TITAN_PWIDTH_PW24) && (dac_rev1 == 0))
{
if (desiredFout >= 50000000)
{n = 0xb9; p = 0xf9; m=0x3e;}
else
{n = 0xb9; p = 0xfb; m=0x3e;}
}
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PLL_ADDR);
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte & 0xcf);
/* DAT Patrick Servais, on ajoute ce qui suis */
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_LOAD_CLK_DATA);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), 0);
delay_us(100L);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), 0);
delay_us(100L);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), 0);
delay_us(100L);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PLL_ADDR);
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte & 0xcf);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_LOAD_CLK_DATA);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), (byte)n);
delay_us(100L);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), (byte)m);
delay_us(100L);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), (byte)p);
delay_us(100L);
tmp = 0;
do
{
tmp += 1;
delay_us(10);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_LOAD_CLK_DATA);
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte);
tmpByte &= 0x40;
} while((tmpByte != 0x40) && (tmp < 5000));
if(tmp == 5000)
LowerVCO(fout, 1, pWidth, fvcoMax, dac_rev1);
old_frequency = old_fr_reg[reg]; /* RETURN VALUE */
old_fr_reg[reg] = trueFout / 1000L;
/* SAVE PRESENT FREQUENCY in kHz */
break;
/*******************************************************************
*
* the programmation line is used to modify register internal value
* of TVP3026, and to select output video register (with internal
* muxer) at the end of programmation. For the system clock
* MCLOCK programmation, at the end, we put on programmation line
* the initial value of the video register.
*
*/
case MEMORY_CLOCK:
/* par defaut, (si on a modifie MCLK), on restore VCLK
* initial
*/
mgaReadBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_R), saveByte);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PLL_ADDR);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), 0xfc);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PIX_CLK_DATA);
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), pixel_n);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PLL_ADDR);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), 0xfd);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PIX_CLK_DATA);
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), pixel_m);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PLL_ADDR);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), 0xfe);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PIX_CLK_DATA);
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), pixel_p);
/*------------*/
/* 1st step */
/*------------*/
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PLL_ADDR);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), 0xfc);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PIX_CLK_DATA);
if (dac_rev1)
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), ((n&0x3f)|0xc0));
else
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), ((n&0x3f)|0x80));
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), (m&0x3f));
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), ((p&0x03)|0xb0));
do
{
delay_us(1);
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte);
tmpByte &= 0x40;
} while(tmpByte != 0x40);
/*------------*/
/* 2d step */
/*------------*/
/* Select programmable clock */
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), 0x0f);
delay_us(2000);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PLL_ADDR);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), 0xff);
do
{
delay_us(1);
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte);
tmpByte &= 0x40;
} while(tmpByte != 0x40);
/* Select internal pclk instead of external pclk0 */
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_CLK_SEL);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), 5);
/*------------*/
/* 3rd step */
/*------------*/
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_MCLK_CTL);
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), val);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), (val&0xe7));
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), (val&0xe7)|0x08);
/*------------*/
/* 4th step */
/*------------*/
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PLL_ADDR);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), 0xf3);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_MEM_CLK_DATA);
if (dac_rev1)
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), ((n&0x3f)|0xc0));
else
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), ((n&0x3f)|0x80));
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), (m&0x3f));
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), ((p&0x03)|0xb0));
delay_us(3500);
do
{
delay_us(1);
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte);
tmpByte &= 0x40;
} while(tmpByte != 0x40);
/*------------*/
/* 5th step */
/*------------*/
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_MCLK_CTL);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), (val&0xe7)|0x10);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), (val&0xe7)|0x18);
/*------------*/
/* 6th step */
/*------------*/
/* Restore clock select */
mgaWriteBYTE(*(pDevice + TITAN_OFFSET + TITAN_MISC_OUT_W), saveByte);
/* Reselect external pclk0 */
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_CLK_SEL);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), 7);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PLL_ADDR);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), 0xfc);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PIX_CLK_DATA);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), (byte)pixel_n);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), (byte)pixel_m);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), (byte)pixel_p);
do
{
delay_us(1);
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte);
tmpByte &= 0x40;
} while(tmpByte != 0x40);
old_fr_reg[reg] = trueFout / 1000L; /* SAVE PRESENT FREQUENCY */
break;
default:
/***ERROR: REGISTER ICD UNKNOWN: */
old_frequency = 0L; /* ERROR RETURN */
break;
}
}
/* wait 10ms before quit....this is not suppose
* to necessary outside of the programmation
* register, but, well, we are not too secure
*/
dummyCallDelai();
/* delay ( 10 ); */
return ( old_frequency );
}
void LowerVCO(long Fout, byte pll, byte pwidth, dword vcomax, byte dacrev1)
{
word i;
short p, q, n, bestN;
int m, bestM;
short val;
long z;
long fvco, fvco_l, fTemp, trueFout, desiredFout;
long ppmError;
long bestError;
byte tmpByte;
word pixelwidth, div_ratio;
dword power;
dword fvcomax;
switch(pwidth)
{
case 0:
pixelwidth = 8;
div_ratio = 8;
break;
case 1:
pixelwidth = 16;
div_ratio = 4;
break;
case 2:
pixelwidth = 32;
div_ratio = 2;
break;
}
desiredFout = Fout * 1000; /* Scale it from KHz to Hz */
switch(pll)
{
case 0:
fvcomax = (dword)175000000; /* Patch pour eviter la Deadzone */
bestError = 99999999; /* dummy start number */
fref = 14318180; /* frequence clock ref */
if ((dword)desiredFout>= (fvcomax >> 1))
p = 0;
else if ((dword)desiredFout>=(fvcomax >> 2))
p = 1;
else if ((dword)desiredFout>=(fvcomax >> 3))
p = 2;
else
p = 3;
power = 1;
for(i=0; i<p; i++)
power = power * 2;
for ( n=40;n<=62;n++ )
{
m = (650 - (((((dword)desiredFout*10)/fref) * ((65-n) * power)) / 8)) / 10;
fTemp = fref / (65 - n);
fvco = fTemp * 8 * (65 - m);
trueFout = fvco / power;
if (trueFout < desiredFout)
ppmError = desiredFout - trueFout;
else
ppmError = trueFout - desiredFout;
if ((ppmError < bestError) &&
(m > 0) && (m <= 63) &&
(fTemp > 500000) &&
((dword)fvco >= (fvcomax >> 1) ) && (fvco <= (dword)220000000))
{
bestError = ppmError;
bestM = m;
bestN = n;
}
}
m = bestM;
n = bestN;
{
dword num;
num = ((65 - m)*10) / (65-n);
num = num * 8 * fref;
trueFout = (num / power) / 10;
}
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PLL_ADDR);
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte & 0xfc);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PIX_CLK_DATA );
if (dacrev1)
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), ((n&0x3f)|0xc0) );
else
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), ((n&0x3f)|0x80) );
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), (m&0x3f) );
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), ((p&0x03)|0xf0) );
do
{
delay_us(10);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PIX_CLK_DATA);
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte);
tmpByte &= 0x40;
} while(tmpByte != 0x40);
break;
case 1:
/* searching for loop clock parameters */
n = 65 - ((4*64)/pixelwidth); /* 64 is the Pixel bus Width */
m = 0x3d;
z = ((65L-(long)n)*2750L)/(Fout/1000);
q = 0;
p = 3;
if (z <= 200)
p = 0;
else if (z <= 400)
p = 1;
else if (z <= 800)
p = 2;
else if (z <=1600)
p = 3;
else
q = (short)(z/1600);
/* Patch: si vco du loop clock pll est > 180 MHz, on le divise par deux */
/* pour ne plus qu'il soit dans la dead zone */
div_ratio = div_ratio * 10;
if (pwidth == TITAN_PWIDTH_PW24)
div_ratio = 80/3; /* meilleurs precision */
fvco_l = ((Fout/div_ratio) << p) * (2*(q+1)) * 10;
if ((p > 0) && (fvco_l > 180000))
p = p-1;
if (dacrev1)
n |= 0xc0;
else
n |= 0x80;
if ((dacrev1 == 0) && (Fout <= 175000))
p |= 0xb0;
else
p |= 0xf0;
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_MCLK_CTL);
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte);
val = tmpByte;
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), ((val&0xf8)|q) | 0x20);
if ((pwidth == TITAN_PWIDTH_PW24) && (dacrev1))
{
if (desiredFout >= 50000000)
{n = 0xf9; p = 0xf9; m=0x3e;}
else
{n = 0xf9; p = 0xfb; m=0x3e;}
}
else if ((pwidth == TITAN_PWIDTH_PW24) && (dacrev1 == 0))
{
if (desiredFout >= 50000000)
{n = 0xb9; p = 0xf9; m=0x3e;}
else
{n = 0xb9; p = 0xfb; m=0x3e;}
}
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_PLL_ADDR);
mgaReadBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), tmpByte & 0xcf);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_INDEX), TVP3026_LOAD_CLK_DATA);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), (byte)n);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), (byte)m);
mgaWriteBYTE(*(pDevice + RAMDAC_OFFSET + TVP3026_DATA), (byte)p);
break;
}
}