diff options
Diffstat (limited to 'src/CirrusClk.c')
-rw-r--r-- | src/CirrusClk.c | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/src/CirrusClk.c b/src/CirrusClk.c new file mode 100644 index 0000000..c548f1f --- /dev/null +++ b/src/CirrusClk.c @@ -0,0 +1,146 @@ +/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/cirrus/CirrusClk.c,v 1.9 1999/12/26 18:24:14 robin Exp $ */ + +/* + * Programming of the built-in Cirrus clock generator. + * Harm Hanemaayer <hhanemaa@cs.ruu.nl> + * + * VCO stability criterion code added by Koen Gadeyne (koen.gadeyne@barco.com) + * Max clock specification added by Harm Hanemaayer (H.Hanemaayer@inter.nl.net) + * + * Minor changes and cleanup Itai Nahshon. + * + * Made this completly chipset independent, and moved chipset dependent parts + * into the specific sub-drivers. Derek Fawcus <derek@spider.com> + */ + +#include "xf86.h" +#include "xf86_OSproc.h" +#include "xf86_ansic.h" +#include "xf86PciInfo.h" +#include "xf86Pci.h" + +#include "cir.h" + +/* CLOCK_FACTOR is double the osc freq in kHz (osc = 14.31818 MHz) */ +#define CLOCK_FACTOR 28636 + +/* Stability constraints for internal VCO -- MAX_VCO also determines + * the maximum Video pixel clock + */ +#define MIN_VCO CLOCK_FACTOR +#define MAX_VCO 111000 + +/* clock in kHz is (numer * CLOCK_FACTOR / (denom & 0x3E)) >> (denom & 1) */ +#define VCOVAL(n, d) \ + ((((n) & 0x7F) * CLOCK_FACTOR / ((d) & 0x3E)) ) + +#define CLOCKVAL(n, d) \ + (VCOVAL(n, d) >> ((d) & 1)) + +typedef struct { + unsigned char numer; + unsigned char denom; +} cirrusClockRec; + +static cirrusClockRec cirrusClockTab[] = { + { 0x2C, 0x33 }, /* 12.599 */ + { 0x4A, 0x2B }, /* 25.227 */ + { 0x5B, 0x2F }, /* 28.325 */ + { 0x45, 0x30 }, /* 41.164 */ + { 0x7E, 0x33 }, /* 36.082 */ + { 0x42, 0x1F }, /* 31.500 */ + { 0x51, 0x3A }, /* 39.992 */ + { 0x55, 0x36 }, /* 45.076 */ + { 0x65, 0x3A }, /* 49.867 */ + { 0x76, 0x34 }, /* 64.983 */ + { 0x7E, 0x32 }, /* 72.163 */ + { 0x6E, 0x2A }, /* 75.000 */ + { 0x5F, 0x22 }, /* 80.013 */ + { 0x7D, 0x2A }, /* 85.226 */ + { 0x58, 0x1C }, /* 89.998 */ + { 0x49, 0x16 }, /* 95.019 */ + { 0x46, 0x14 }, /* 100.226 */ + { 0x53, 0x16 }, /* 108.035 */ + { 0x5C, 0x18 }, /* 110.248 */ + + { 0x6D, 0x1A }, /* 120.050 */ + { 0x58, 0x14 }, /* 125.998 */ + { 0x6D, 0x18 }, /* 130.055 */ + { 0x42, 0x0E }, /* 134.998 */ + + { 0x69, 0x14 }, /* 150.341 */ + { 0x5E, 0x10 }, /* 168.239 */ + { 0x5C, 0x0E }, /* 188.182 */ + { 0x67, 0x0E }, /* 210.682 */ + { 0x60, 0x0C }, /* 229.091 */ +}; + +#define NU_FIXED_CLOCKS (sizeof(cirrusClockTab)/sizeof(cirrusClockTab[0])) + + +/* + * This function returns the 7-bit numerator and 6-bit denominator/post-scalar + * value that corresponds to the closest clock found. + * If a frequency close to one of the tested clock values is found, + * use the tested clock since others can be unstable. + */ + +Bool +CirrusFindClock(int *rfreq, int max_clock, int *num_out, int *den_out) +{ + int n, i; + int num = 0, den = 0; + int freq, ffreq = 0, mindiff = 0; + + freq = *rfreq; + /* Prefer a tested value if it matches within 0.1%. */ + for (i = 0; i < NU_FIXED_CLOCKS; i++) { + int diff; + diff = abs(CLOCKVAL(cirrusClockTab[i].numer, + cirrusClockTab[i].denom) - freq); + if (diff < freq / 1000) { + num = cirrusClockTab[i].numer; + den = cirrusClockTab[i].denom; + ffreq = CLOCKVAL(num, den); + goto foundclock; + } + } + + /* + * If max_clock is greater than the MAX_VCO default, ignore + * MAX_VCO. On the other hand, if MAX_VCO is higher than max_clock, + * make use of the higher MAX_VCO value. + */ + if (MAX_VCO > max_clock) + max_clock = MAX_VCO; + + mindiff = freq; + for (n = 0x10; n < 0x7f; n++) { + int d; + for (d = 0x14; d < 0x3f; d++) { + int c, diff; + + /* Avoid combinations that can be unstable. */ + if ((VCOVAL(n, d) < MIN_VCO) || (VCOVAL(n, d) > max_clock)) + continue; + c = CLOCKVAL(n, d); + diff = abs(c - freq); + if (diff < mindiff) { + mindiff = diff; + num = n; + den = d; + ffreq = c; + } + } + } + + if (0 == num || 0 == den) + return FALSE; + +foundclock: + *num_out = num; + *den_out = den; + *rfreq = ffreq; + + return TRUE; +} |