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