diff options
Diffstat (limited to 'xc/programs/Xserver/hw/xfree86/accel/p9000/I2061Acal.c')
-rw-r--r-- | xc/programs/Xserver/hw/xfree86/accel/p9000/I2061Acal.c | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/xc/programs/Xserver/hw/xfree86/accel/p9000/I2061Acal.c b/xc/programs/Xserver/hw/xfree86/accel/p9000/I2061Acal.c new file mode 100644 index 000000000..bf4cde495 --- /dev/null +++ b/xc/programs/Xserver/hw/xfree86/accel/p9000/I2061Acal.c @@ -0,0 +1,245 @@ +/* $XConsortium: $ */ +/* $XFree86: xc/programs/Xserver/hw/xfree86/accel/p9000/I2061Acal.c,v 3.1 1994/09/07 15:50:41 dawes Exp $ */ +/* Id: ICD2061Acal.c,v 4.0 1994/05/28 01:24:17 nygren Exp */ +/* Based on the number 9 Inc code */ +/* Copyright (c) 1992, Number Nine Computer Corp. All Rights Reserved. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Number Nine Computer Corp not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Number Nine Computer Corp + * makes no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * NUMBER NINE COMPUTER CORP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, + * IN NO EVENT SHALL NUMBER NINE COMPUTER CORP BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Modifications - Copyright (c) 1994, Harry Langenbacher,All Rights Reserved. + * HARRY LANGENBACHER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, + * IN NO EVENT SHALL HARRY LANGENBACHER BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. Harry Langenbacher + * makes no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + */ + +#include "X.h" +#include "input.h" +#include "xf86_OSlib.h" +#include "xf86.h" +#include "ICD2061A.h" + +#undef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#undef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#define MAX_NUMERATOR 130 +#define MAX_DENOMINATOR MIN(129, CRYSTAL_FREQUENCY / 400000) +#define MIN_DENOMINATOR MAX(3, CRYSTAL_FREQUENCY / 2000000) + +/* Index register frequency ranges for ICD2061A chip */ +/* "The Index Field is used to preset the VCO to an appropriate range." pg. 13 */ +static long vclk_range[14] = + { + 51000000, + 53200000, + 58500000, + 60700000, + 64400000, + 66800000, + 73500000, + 75600000, + 80900000, + 83200000, + 91500000, + 100000000, + 120000000, + 135000000 /* BE WARNED THIS COULD BE VERY VERY BAD (i.e. , I'm guessing)!!!!!!! */ + } ; + +unsigned long ICD2061ACalcClock + ( long frequency , int chipreg ) /* frequency in Hz */ + /* chipreg - select one of 3 registers in icd2061a to be selected + to hold new frequency parameters */ + { + int Q_Divider ; + int P_Divider ; + int Mux ; /* divider after vco */ + + register int index; + unsigned long temp; + int Best_Qdivider, Best_Pdivider; + long min_diff; + long diff; + + min_diff = 0xFFFFFFF; + Best_Pdivider = 1; + Best_Qdivider = 1; +#ifdef DEBUG + ErrorF ( "Buiding clock control word for freq=%d, chipreg=%d\n" , + frequency , chipreg ) ; +#endif + /* Calculate 17 bit ( 7 + 3 + 7 ) frequency control field */ + + /* find what post VCO divider we need */ + Mux = 0; + if (frequency < MIN_VCO_FREQUENCY) + Mux = 1; + if (frequency < MIN_VCO_FREQUENCY / 2) + Mux = 2; + if (frequency < MIN_VCO_FREQUENCY / 4) + Mux = 3; /* boy, this is a really low frequency (50Mhz/4?)*/ + + frequency <<= Mux ; /* make frequency = the VCO frequency we have to get */ + for (P_Divider = 4; P_Divider <= MAX_NUMERATOR; P_Divider++) + { + index = CRYSTAL_FREQUENCY / (frequency / P_Divider); + if (index > MAX_DENOMINATOR) + index = MAX_DENOMINATOR; + if (index < MIN_DENOMINATOR) + index = MIN_DENOMINATOR; + for (Q_Divider = index - 3; Q_Divider < index + 4; Q_Divider++) + if (Q_Divider >= MIN_DENOMINATOR && Q_Divider <= MAX_DENOMINATOR) + { + diff = (CRYSTAL_FREQUENCY / Q_Divider) * P_Divider - frequency; + if (diff < 0) + diff = -diff; + if ( Best_Qdivider * ICD2061AGCD(Q_Divider, P_Divider) / + ICD2061AGCD(Best_Qdivider, Best_Pdivider) == Q_Divider + && + Best_Pdivider * ICD2061AGCD(Q_Divider, P_Divider) / + ICD2061AGCD(Best_Qdivider, Best_Pdivider) == P_Divider ) + { + if (diff > min_diff) + diff = min_diff; + } + if (diff <= min_diff) + { + min_diff = diff; + Best_Qdivider = Q_Divider; + Best_Pdivider = P_Divider; + } + } + } + Q_Divider = Best_Qdivider; + P_Divider = Best_Pdivider; + + /* Calculate the resultant VCO frequency */ + temp = CalcVCOfreq ( Q_Divider , P_Divider ) ; + + /* look up the index field value appropriate for this vco freq */ + index = 0 ; + while ( ( vclk_range [ index ] < temp ) && ( index < 14 ) ) + index ++ ; + + if ( index == 14 ) /* vco freq too high */ + { + ErrorF ("\a\aProgram error - vco freq too high !\n" ) ; + } + + /* Pack the control word for the frequency snthesizer , + packed into bits 0-23, not 1-24 like before */ + + temp = ( (long) chipreg << 21 ) | + ( (long) index << 17 ) | + ( (long) ( P_Divider - 3 ) << 10 ) | + ( (long) Mux << 7 ) | + ( Q_Divider - 2 ) ; + +#ifdef DEBUG + ErrorF ("Clock Control Word is 0x%06X\n" , temp ) ; +#endif + + return temp; +} + +/* Number theoretic function - GCD (Greatest Common Divisor) */ +int ICD2061AGCD ( int number1 , int number2 ) + { + register int remainder = number1 % number2 ; + while ( remainder ) + { + number1 = number2 ; + number2 = remainder ; + remainder = number1 % number2 ; + } + return number2 ; + } + +unsigned int ICD2061AGetClock ( unsigned long control_word ) + /* value now in bits 0-23 ( not 1-24 like before ) */ + { + unsigned int Q_Divider ; + unsigned int Feedback_Divider_P ; + unsigned int Post_VCO_Divider_M ; + unsigned int FinalFreq ; + unsigned int VCOFreq ; + unsigned int Index ; + unsigned int select ; + + /* Unpack the clock value */ + select = ( control_word >> 21 ) & 7 ; + Index = ( ( control_word >> 17 ) & 0x0F) ; /* P */ + Feedback_Divider_P = ( ( control_word >> 10 ) & 0x7F) + 3 ; /* P */ + Post_VCO_Divider_M = ( ( control_word >> 7 ) & 0x07) ; /* M */ + Q_Divider = ( ( control_word ) & 0x7F) + 2 ; /* Q */ + + /* f(VCO) = Prescale * f(ref) * P / Q */ + /* where Prescale is determined by a bit in the control reg, and will + be 2 or 4 , default 2 */ + /* then the VCO output is divided by 2^^M */ + + VCOFreq = CalcVCOfreq ( Q_Divider , Feedback_Divider_P ) ; + FinalFreq = VCOFreq >> Post_VCO_Divider_M ; + /* (2*f(ref)*P/Q)/2^^Post_VCO_Divider_M */ + +#ifdef DEBUG + ErrorF ("\ + register select=%d\n\ + Index Field =%d=0x%X\n\ + Feedback_Divider_P=%d=0x%X\n\ + Post_VCO_Divider_M=%d\n\ + Q_Divider=%d=0x%X\n\ + VCOFreq=%d Hz, FinalFreq=%d Hz\n" , + select , Index , Index , Feedback_Divider_P , Feedback_Divider_P , + Post_VCO_Divider_M , Q_Divider , Q_Divider , + VCOFreq , FinalFreq ) ; +#endif + + return FinalFreq ; + } + +unsigned long CalcVCOfreq + ( unsigned int Q_Divider , unsigned int Feedback_Divider_P ) + { + unsigned long VCO_Freq ; + + /* f(VCO) = Prescale * f(ref) * P / Q */ + /* where Prescale is determined by a bit in the control reg, and will + be 2 or 4 , default 2 */ + /* assume a (standard?) 14.318180 Mhz Crystal */ + VCO_Freq = ( 2 * 14318180 * Feedback_Divider_P ) / Q_Divider ; + return VCO_Freq ; + } + + + + + |