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