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