CirrusClk.c revision 1ae1b5e8
1/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/cirrus/CirrusClk.c,v 1.8 1998/12/06 06:08:28 dawes Exp $ */ 2 3#ifdef HAVE_CONFIG_H 4#include "config.h" 5#endif 6 7/* 8 * Programming of the built-in Cirrus clock generator. 9 * Harm Hanemaayer <hhanemaa@cs.ruu.nl> 10 * 11 * VCO stability criterion code added by Koen Gadeyne (koen.gadeyne@barco.com) 12 * Max clock specification added by Harm Hanemaayer (H.Hanemaayer@inter.nl.net) 13 * 14 * Minor changes and cleanup Itai Nahshon. 15 * 16 * Made this completly chipset independent, and moved chipset dependent parts 17 * into the specific sub-drivers. Derek Fawcus <derek@spider.com> 18 */ 19 20#include "xf86.h" 21#include "xf86_OSproc.h" 22#include "xf86PciInfo.h" 23#include "xf86Pci.h" 24 25#include "cir.h" 26 27/* CLOCK_FACTOR is double the osc freq in kHz (osc = 14.31818 MHz) */ 28#define CLOCK_FACTOR 28636 29 30/* Stability constraints for internal VCO -- MAX_VCO also determines 31 * the maximum Video pixel clock 32 */ 33#define MIN_VCO CLOCK_FACTOR 34#define MAX_VCO 111000 35 36/* clock in kHz is (numer * CLOCK_FACTOR / (denom & 0x3E)) >> (denom & 1) */ 37#define VCOVAL(n, d) \ 38 ((((n) & 0x7F) * CLOCK_FACTOR / ((d) & 0x3E)) ) 39 40#define CLOCKVAL(n, d) \ 41 (VCOVAL(n, d) >> ((d) & 1)) 42 43typedef struct { 44 unsigned char numer; 45 unsigned char denom; 46} cirrusClockRec; 47 48static cirrusClockRec cirrusClockTab[] = { 49 { 0x2C, 0x33 }, /* 12.599 */ 50 { 0x4A, 0x2B }, /* 25.227 */ 51 { 0x5B, 0x2F }, /* 28.325 */ 52 { 0x45, 0x30 }, /* 41.164 */ 53 { 0x7E, 0x33 }, /* 36.082 */ 54 { 0x42, 0x1F }, /* 31.500 */ 55 { 0x51, 0x3A }, /* 39.992 */ 56 { 0x55, 0x36 }, /* 45.076 */ 57 { 0x65, 0x3A }, /* 49.867 */ 58 { 0x76, 0x34 }, /* 64.983 */ 59 { 0x7E, 0x32 }, /* 72.163 */ 60 { 0x6E, 0x2A }, /* 75.000 */ 61 { 0x5F, 0x22 }, /* 80.013 */ 62 { 0x7D, 0x2A }, /* 85.226 */ 63 { 0x58, 0x1C }, /* 89.998 */ 64 { 0x49, 0x16 }, /* 95.019 */ 65 { 0x46, 0x14 }, /* 100.226 */ 66 { 0x53, 0x16 }, /* 108.035 */ 67 { 0x5C, 0x18 }, /* 110.248 */ 68 69 { 0x6D, 0x1A }, /* 120.050 */ 70 { 0x58, 0x14 }, /* 125.998 */ 71 { 0x6D, 0x18 }, /* 130.055 */ 72 { 0x42, 0x0E }, /* 134.998 */ 73 74 { 0x69, 0x14 }, /* 150.341 */ 75 { 0x5E, 0x10 }, /* 168.239 */ 76 { 0x5C, 0x0E }, /* 188.182 */ 77 { 0x67, 0x0E }, /* 210.682 */ 78 { 0x60, 0x0C }, /* 229.091 */ 79}; 80 81#define NU_FIXED_CLOCKS (sizeof(cirrusClockTab)/sizeof(cirrusClockTab[0])) 82 83 84/* 85 * This function returns the 7-bit numerator and 6-bit denominator/post-scalar 86 * value that corresponds to the closest clock found. 87 * If a frequency close to one of the tested clock values is found, 88 * use the tested clock since others can be unstable. 89 */ 90 91_X_EXPORT Bool 92CirrusFindClock(int *rfreq, int max_clock, int *num_out, int *den_out) 93{ 94 int n, i; 95 int num = 0, den = 0; 96 int freq, ffreq = 0, mindiff = 0; 97 98 freq = *rfreq; 99 /* Prefer a tested value if it matches within 0.1%. */ 100 for (i = 0; i < NU_FIXED_CLOCKS; i++) { 101 int diff; 102 diff = abs(CLOCKVAL(cirrusClockTab[i].numer, 103 cirrusClockTab[i].denom) - freq); 104 if (diff < freq / 1000) { 105 num = cirrusClockTab[i].numer; 106 den = cirrusClockTab[i].denom; 107 ffreq = CLOCKVAL(num, den); 108 goto foundclock; 109 } 110 } 111 112 /* 113 * If max_clock is greater than the MAX_VCO default, ignore 114 * MAX_VCO. On the other hand, if MAX_VCO is higher than max_clock, 115 * make use of the higher MAX_VCO value. 116 */ 117 if (MAX_VCO > max_clock) 118 max_clock = MAX_VCO; 119 120 mindiff = freq; 121 for (n = 0x10; n < 0x7f; n++) { 122 int d; 123 for (d = 0x14; d < 0x3f; d++) { 124 int c, diff; 125 126 /* Avoid combinations that can be unstable. */ 127 if ((VCOVAL(n, d) < MIN_VCO) || (VCOVAL(n, d) > max_clock)) 128 continue; 129 c = CLOCKVAL(n, d); 130 diff = abs(c - freq); 131 if (diff < mindiff) { 132 mindiff = diff; 133 num = n; 134 den = d; 135 ffreq = c; 136 } 137 } 138 } 139 140 if (0 == num || 0 == den) 141 return FALSE; 142 143foundclock: 144 *num_out = num; 145 *den_out = den; 146 *rfreq = ffreq; 147 148 return TRUE; 149} 150