xref: /plan9/sys/src/cmd/aux/vga/icd2061a.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1 /*
2  * IC Designs ICD2061A Dual Programmable Graphics Clock Generator.
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <bio.h>
7 
8 #include "pci.h"
9 #include "vga.h"
10 
11 enum {
12 	Prescale	= 2,			/* P counter prescale (default) */
13 	NIndex		= 14,			/* number of index field values */
14 };
15 
16 /*
17  * For an index value of x, the appropriate VCO range
18  * is >= index[x] && <= index[x+1]. The higher index is
19  * prefered if VCO is on a boundary.
20  */
21 static ulong index[NIndex] = {
22 	 50000000,
23 	 51000000,
24 	 53200000,
25 	 58500000,
26 	 60700000,
27 	 64400000,
28 	 66800000,
29 	 73500000,
30 	 75600000,
31 	 80900000,
32 	 83200000,
33 	 91500000,
34 	100000000,
35 	120000000,
36 };
37 
38 static void
init(Vga * vga,Ctlr * ctlr)39 init(Vga* vga, Ctlr* ctlr)
40 {
41 	int f;
42 	ulong d, dmax, fmin, n;
43 
44 	if(ctlr->flag & Finit)
45 		return;
46 
47 	if(vga->f[0] == 0)
48 		vga->f[0] = vga->mode->frequency;
49 
50 	if(vga->mode->z > 8)
51 		error("depth %d not supported\n", vga->mode->z);
52 
53 	/*
54 	 * Post-VCO divisor. Constraint:
55 	 * 	50MHz <= vga->f <= 120MHz
56 	 */
57 	for(vga->p[0] = 0; vga->f[0] <= 50000000; vga->p[0]++)
58 		vga->f[0] <<= 1;
59 
60 	/*
61 	 * Determine index.
62 	 */
63 	for(vga->i[0] = NIndex-1; vga->f[0] < index[vga->i[0]] && vga->i[0]; vga->i[0]--)
64 		;
65 
66 	/*
67 	 * Denominator. Constraints:
68 	 *	200KHz <= RefFreq/d <= 1MHz
69 	 * and
70 	 *	3 <= d <= 129
71 	 *
72 	 * Numerator. Constraint:
73 	 *	4 <= n <= 130
74 	 */
75 	d = RefFreq/1000000 > 3 ? RefFreq/1000000: 3;
76 	dmax = RefFreq/200000 < 129 ? RefFreq/200000: 129;
77 
78 	/*
79 	 * Now look for values of p and q that give
80 	 * the least error for
81 	 *	vga->f = (Prescale*RefFreq*n/d);
82 	 */
83 	vga->d[0] = d;
84 	vga->n[0] = 4;
85 	for(fmin = vga->f[0]; d <= dmax; d++){
86 		for(n = 4; n <= 130; n++){
87 			f = vga->f[0] - (Prescale*RefFreq*n/d);
88 			if(f < 0)
89 				f = -f;
90 			if(f < fmin){
91 				fmin = f;
92 				vga->d[0] = d;
93 				vga->n[0] = n;
94 			}
95 		}
96 	}
97 
98 	/*
99 	 * The serial word to be loaded into the icd2061a is
100 	 *	(2<<21)|(vga->i<<17)|((vga->n)<<10)|(vga->p<<7)|vga->d
101 	 * Always select ICD2061A REG2.
102 	 */
103 	vga->f[0] = (Prescale*RefFreq*vga->n[0]/vga->d[0]);
104 	vga->d[0] -= 2;
105 	vga->n[0] -= 3;
106 
107 	ctlr->flag |= Finit;
108 }
109 
110 Ctlr icd2061a = {
111 	"icd2061a",
112 	0,				/* snarf */
113 	0,				/* options */
114 	init,				/* init */
115 	0,				/* load */
116 	0,				/* dump */
117 };
118