CirrusClk.c revision 1ae1b5e8
176888252Smrg/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/cirrus/CirrusClk.c,v 1.8 1998/12/06 06:08:28 dawes Exp $ */
276888252Smrg
376888252Smrg#ifdef HAVE_CONFIG_H
476888252Smrg#include "config.h"
576888252Smrg#endif
676888252Smrg
776888252Smrg/*
876888252Smrg * Programming of the built-in Cirrus clock generator.
976888252Smrg * Harm Hanemaayer <hhanemaa@cs.ruu.nl>
1076888252Smrg *
1176888252Smrg * VCO stability criterion code added by Koen Gadeyne (koen.gadeyne@barco.com)
1276888252Smrg * Max clock specification added by Harm Hanemaayer (H.Hanemaayer@inter.nl.net)
1376888252Smrg *
1476888252Smrg * Minor changes and cleanup Itai Nahshon.
1576888252Smrg *
1676888252Smrg * Made this completly chipset independent,  and moved chipset dependent parts
1776888252Smrg * into the specific sub-drivers.  Derek Fawcus <derek@spider.com>
1876888252Smrg */
1976888252Smrg
2076888252Smrg#include "xf86.h"
2176888252Smrg#include "xf86_OSproc.h"
2276888252Smrg#include "xf86PciInfo.h"
2376888252Smrg#include "xf86Pci.h"
2476888252Smrg
2576888252Smrg#include "cir.h"
2676888252Smrg
2776888252Smrg/* CLOCK_FACTOR is double the osc freq in kHz (osc = 14.31818 MHz) */
2876888252Smrg#define CLOCK_FACTOR 28636
2976888252Smrg
3076888252Smrg/* Stability constraints for internal VCO -- MAX_VCO also determines
3176888252Smrg * the maximum Video pixel clock
3276888252Smrg */
3376888252Smrg#define MIN_VCO CLOCK_FACTOR
3476888252Smrg#define MAX_VCO 111000
3576888252Smrg
3676888252Smrg/* clock in kHz is (numer * CLOCK_FACTOR / (denom & 0x3E)) >> (denom & 1) */
3776888252Smrg#define VCOVAL(n, d) \
3876888252Smrg	((((n) & 0x7F) * CLOCK_FACTOR / ((d) & 0x3E)) )
3976888252Smrg
4076888252Smrg#define CLOCKVAL(n, d) \
4176888252Smrg	(VCOVAL(n, d) >> ((d) & 1))
4276888252Smrg
4376888252Smrgtypedef struct {
4476888252Smrg	unsigned char numer;
4576888252Smrg	unsigned char denom;
4676888252Smrg} cirrusClockRec;
4776888252Smrg
4876888252Smrgstatic cirrusClockRec cirrusClockTab[] = {
4976888252Smrg	{ 0x2C, 0x33 },		/* 12.599 */
5076888252Smrg	{ 0x4A, 0x2B },		/* 25.227 */
5176888252Smrg	{ 0x5B, 0x2F },		/* 28.325 */
5276888252Smrg	{ 0x45, 0x30 },		/* 41.164 */
5376888252Smrg	{ 0x7E, 0x33 },		/* 36.082 */
5476888252Smrg	{ 0x42, 0x1F },		/* 31.500 */
5576888252Smrg	{ 0x51, 0x3A },		/* 39.992 */
5676888252Smrg	{ 0x55, 0x36 },		/* 45.076 */
5776888252Smrg	{ 0x65, 0x3A },		/* 49.867 */
5876888252Smrg	{ 0x76, 0x34 },		/* 64.983 */
5976888252Smrg	{ 0x7E, 0x32 },		/* 72.163 */
6076888252Smrg	{ 0x6E, 0x2A },		/* 75.000 */
6176888252Smrg	{ 0x5F, 0x22 },		/* 80.013 */
6276888252Smrg	{ 0x7D, 0x2A },		/* 85.226 */
6376888252Smrg	{ 0x58, 0x1C },		/* 89.998 */
6476888252Smrg	{ 0x49, 0x16 },		/* 95.019 */
6576888252Smrg	{ 0x46, 0x14 },		/* 100.226 */
6676888252Smrg	{ 0x53, 0x16 },		/* 108.035 */
6776888252Smrg	{ 0x5C, 0x18 },		/* 110.248 */
6876888252Smrg
6976888252Smrg	{ 0x6D, 0x1A },		/* 120.050 */
7076888252Smrg	{ 0x58, 0x14 },		/* 125.998 */
7176888252Smrg	{ 0x6D, 0x18 },		/* 130.055 */
7276888252Smrg	{ 0x42, 0x0E },		/* 134.998 */
7376888252Smrg
7476888252Smrg	{ 0x69, 0x14 },		/* 150.341 */
7576888252Smrg	{ 0x5E, 0x10 },		/* 168.239 */
7676888252Smrg	{ 0x5C, 0x0E },		/* 188.182 */
7776888252Smrg	{ 0x67, 0x0E },		/* 210.682 */
7876888252Smrg	{ 0x60, 0x0C },		/* 229.091 */
7976888252Smrg};
8076888252Smrg
8176888252Smrg#define NU_FIXED_CLOCKS (sizeof(cirrusClockTab)/sizeof(cirrusClockTab[0]))
8276888252Smrg
8376888252Smrg
8476888252Smrg/*
8576888252Smrg * This function returns the 7-bit numerator and 6-bit denominator/post-scalar
8676888252Smrg * value that corresponds to the closest clock found.
8776888252Smrg * If a frequency close to one of the tested clock values is found,
8876888252Smrg * use the tested clock since others can be unstable.
8976888252Smrg */
9076888252Smrg
911ae1b5e8Smrg_X_EXPORT Bool
9276888252SmrgCirrusFindClock(int *rfreq, int max_clock, int *num_out, int *den_out)
9376888252Smrg{
9476888252Smrg	int n, i;
9576888252Smrg	int num = 0, den = 0;
9676888252Smrg	int freq, ffreq = 0, mindiff = 0;
9776888252Smrg
9876888252Smrg	freq = *rfreq;
9976888252Smrg	/* Prefer a tested value if it matches within 0.1%. */
10076888252Smrg	for (i = 0; i < NU_FIXED_CLOCKS; i++) {
10176888252Smrg		int diff;
10276888252Smrg		diff = abs(CLOCKVAL(cirrusClockTab[i].numer,
10376888252Smrg					cirrusClockTab[i].denom) - freq);
10476888252Smrg		if (diff < freq / 1000) {
10576888252Smrg			num = cirrusClockTab[i].numer;
10676888252Smrg			den = cirrusClockTab[i].denom;
10776888252Smrg			ffreq = CLOCKVAL(num, den);
10876888252Smrg			goto foundclock;
10976888252Smrg		}
11076888252Smrg	}
11176888252Smrg
11276888252Smrg	/*
11376888252Smrg	 * If max_clock is greater than the MAX_VCO default, ignore
11476888252Smrg	 * MAX_VCO. On the other hand, if MAX_VCO is higher than max_clock,
11576888252Smrg	 * make use of the higher MAX_VCO value.
11676888252Smrg	 */
11776888252Smrg	if (MAX_VCO > max_clock)
11876888252Smrg		max_clock = MAX_VCO;
11976888252Smrg
12076888252Smrg	mindiff = freq;
12176888252Smrg	for (n = 0x10; n < 0x7f; n++) {
12276888252Smrg		int d;
12376888252Smrg		for (d = 0x14; d < 0x3f; d++) {
12476888252Smrg			int c, diff;
12576888252Smrg
12676888252Smrg			/* Avoid combinations that can be unstable. */
12776888252Smrg			if ((VCOVAL(n, d) < MIN_VCO) || (VCOVAL(n, d) > max_clock))
12876888252Smrg				continue;
12976888252Smrg			c = CLOCKVAL(n, d);
13076888252Smrg			diff = abs(c - freq);
13176888252Smrg			if (diff < mindiff) {
13276888252Smrg				mindiff = diff;
13376888252Smrg				num = n;
13476888252Smrg				den = d;
13576888252Smrg				ffreq = c;
13676888252Smrg			}
13776888252Smrg		}
13876888252Smrg	}
13976888252Smrg
14076888252Smrg	if (0 == num || 0 == den)
14176888252Smrg		return FALSE;
14276888252Smrg
14376888252Smrgfoundclock:
14476888252Smrg	*num_out = num;
14576888252Smrg	*den_out = den;
14676888252Smrg	*rfreq = ffreq;
14776888252Smrg
14876888252Smrg	return TRUE;
14976888252Smrg}
150