Rev 54 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
54 | pj | 1 | /* |
2 | * icd2061a.c |
||
3 | * |
||
4 | * support for the ICD 2061A programmable clockchip and compatibles |
||
5 | * outside of the DAC |
||
6 | * |
||
7 | * Rev history: |
||
8 | * Andreas Arens Dec 95: Created |
||
9 | * |
||
10 | * Andreas Arens Feb 15 1996: A few minor fixes |
||
11 | */ |
||
12 | |||
13 | #include "timing.h" |
||
14 | #include "libvga.h" |
||
15 | #include "ramdac.h" |
||
16 | #include "clockchip.h" |
||
17 | #include "driver.h" |
||
18 | #include "vga.h" |
||
19 | |||
20 | /* |
||
21 | * ATTENTION: The ICD 2061A does not support reading of the currently selected |
||
22 | * pixelclock. XFree86 also fails to restore this value correctly, but always |
||
23 | * estores a 45 MHz pixelclock. My standard text mode (132x25) uses 40 MHz, |
||
24 | * which is the value selected here. |
||
25 | * You can use the SVGATextMode-1.0 'clockprobe' tool right after boot to |
||
26 | * determine the value used with your card and modify here, but since 40 MHz |
||
27 | * is the VESA suggested pixelclock for a 70 Hz 132x25 mode, the value here |
||
28 | * seems fine. Note that 80xXX modes use 25 or 28 MHz clocks, which are fixed |
||
29 | * and not affected by this. This might not be true for Diamond boards using |
||
30 | * the DCS2824-0 clockchip, which is an ICD 2061A clone. |
||
31 | */ |
||
32 | #define I2061A_DEFAULT_TEXT_FREQUENCY (40000L) /* kHz */ |
||
33 | |||
34 | /* |
||
35 | * Clockchip code is derived from I2051Aalt.c in XFree86/common_hw which |
||
36 | * in turn is derived from code available from the STB bulletin board. |
||
37 | * A number of modifications have been made to fit this into SVGAlib. |
||
38 | */ |
||
39 | |||
40 | #define I2061A_CRYSTAL_FREQUENCY (14.31818 * 2.0) |
||
41 | |||
42 | static double I2061A_range[15] = |
||
43 | {50.0, 51.0, 53.2, 58.5, 60.7, 64.4, 66.8, 73.5, |
||
44 | 75.6, 80.9, 83.2, 91.5, 100.0, 120.0, 120.0000001}; |
||
45 | |||
46 | static long I2061A_SelectClock(long frequency) |
||
47 | /* in KHz */ |
||
48 | { |
||
49 | unsigned int m; |
||
50 | int i; |
||
51 | double freq, fvco; |
||
52 | double dev, devx; |
||
53 | double delta, deltax; |
||
54 | double f0; |
||
55 | unsigned int p, q; |
||
56 | unsigned int bestp = 0, bestq = 0, bestm = 0, besti = 0; |
||
57 | |||
58 | freq = ((double) frequency) / 1000.0; |
||
59 | if (freq > I2061A_range[13]) |
||
60 | freq = I2061A_range[13]; |
||
61 | else if (freq < 7.0) |
||
62 | freq = 7.0; |
||
63 | |||
64 | /* |
||
65 | * Calculate values to load into ICD 2061A clock chip to set frequency |
||
66 | */ |
||
67 | delta = 999.0; |
||
68 | dev = 999.0; |
||
69 | |||
70 | for (m = 0; m < 8; m++) { |
||
71 | fvco = freq * (1 << m); |
||
72 | if (fvco < 50.0 || fvco > 120.0) |
||
73 | continue; |
||
74 | |||
75 | f0 = fvco / I2061A_CRYSTAL_FREQUENCY; |
||
76 | |||
77 | for (q = 14; q <= 71; q++) { /* q={15..71}:Constraint 2 on page 14 */ |
||
78 | p = (int) (f0 * q + 0.5); |
||
79 | if (p < 4 || p > 130) /* p={4..130}:Constraint 5 on page 14 */ |
||
80 | continue; |
||
81 | deltax = (double) (p) / (double) (q) - f0; |
||
82 | if (deltax < 0) |
||
83 | deltax = -deltax; |
||
84 | if (deltax <= delta) { |
||
85 | for (i = 13; i >= 0; i--) |
||
86 | if (fvco >= I2061A_range[i]) |
||
87 | break; |
||
88 | devx = (fvco - (I2061A_range[i] + I2061A_range[i + 1]) / 2) / fvco; |
||
89 | if (devx < 0) |
||
90 | devx = -devx; |
||
91 | if (deltax < delta || devx < dev) { |
||
92 | delta = deltax; |
||
93 | dev = devx; |
||
94 | bestp = p; |
||
95 | bestq = q; |
||
96 | bestm = m; |
||
97 | besti = i; |
||
98 | } |
||
99 | } |
||
100 | } |
||
101 | } |
||
102 | return ((((((long) besti << 7) | (bestp - 3)) << 3) | bestm) << 7) | (bestq - 2); |
||
103 | } |
||
104 | |||
105 | static int I2061A_GetClock(long dwv) |
||
106 | { |
||
107 | int clock_q = (dwv & 0x7f) + 2; |
||
108 | int clock_m = (dwv >> 7) & 7; |
||
109 | int clock_p = ((dwv >> 10) & 0x7f) + 3; |
||
110 | double fvco; |
||
111 | |||
112 | fvco = I2061A_CRYSTAL_FREQUENCY / (1 << clock_m); |
||
113 | return (int) (((fvco * clock_p) / clock_q) * 1000); |
||
114 | } |
||
115 | |||
116 | /* needs some delay for really fast cpus */ |
||
117 | #define wrt_clk_bit(v) outb(MIS_W, v), (void)inb(crtcaddr), (void)inb(crtcaddr) |
||
118 | |||
119 | /* ATTENTION: This assumes CRTC registers and S3 registers to be UNLOCKED! */ |
||
120 | static void I2061A_init_clock(unsigned long setup) |
||
121 | { |
||
122 | unsigned char nclk[2], clk[2]; |
||
123 | unsigned short restore42; |
||
124 | unsigned short oldclk; |
||
125 | unsigned short bitval; |
||
126 | int i; |
||
127 | unsigned char c; |
||
128 | unsigned short crtcaddr = (inb(MIS_R) & 0x01) ? CRT_IC : CRT_IM; |
||
129 | |||
130 | oldclk = inb(MIS_R); |
||
131 | |||
132 | outb(crtcaddr, 0x42); |
||
133 | restore42 = inb(crtcaddr + 1); |
||
134 | |||
135 | outw(SEQ_I, 0x0100); |
||
136 | |||
137 | outb(SEQ_I, 1); |
||
138 | c = inb(SEQ_D); |
||
139 | outb(SEQ_D, 0x20 | c); |
||
140 | |||
141 | outb(crtcaddr, 0x42); |
||
142 | outb(crtcaddr + 1, 0x03); |
||
143 | |||
144 | outw(SEQ_I, 0x0300); |
||
145 | |||
146 | nclk[0] = oldclk & 0xF3; |
||
147 | nclk[1] = nclk[0] | 0x08; |
||
148 | clk[0] = nclk[0] | 0x04; |
||
149 | clk[1] = nclk[0] | 0x0C; |
||
150 | |||
151 | outb(crtcaddr, 0x42); |
||
152 | (void) inb(crtcaddr + 1); |
||
153 | |||
154 | outw(SEQ_I, 0x0100); |
||
155 | |||
156 | wrt_clk_bit(oldclk | 0x08); |
||
157 | wrt_clk_bit(oldclk | 0x0C); |
||
158 | for (i = 0; i < 5; i++) { |
||
159 | wrt_clk_bit(nclk[1]); |
||
160 | wrt_clk_bit(clk[1]); |
||
161 | } |
||
162 | wrt_clk_bit(nclk[1]); |
||
163 | wrt_clk_bit(nclk[0]); |
||
164 | wrt_clk_bit(clk[0]); |
||
165 | wrt_clk_bit(nclk[0]); |
||
166 | wrt_clk_bit(clk[0]); |
||
167 | for (i = 0; i < 24; i++) { |
||
168 | bitval = setup & 0x01; |
||
169 | setup >>= 1; |
||
170 | wrt_clk_bit(clk[1 - bitval]); |
||
171 | wrt_clk_bit(nclk[1 - bitval]); |
||
172 | wrt_clk_bit(nclk[bitval]); |
||
173 | wrt_clk_bit(clk[bitval]); |
||
174 | } |
||
175 | wrt_clk_bit(clk[1]); |
||
176 | wrt_clk_bit(nclk[1]); |
||
177 | wrt_clk_bit(clk[1]); |
||
178 | |||
179 | outb(SEQ_I, 1); |
||
180 | c = inb(SEQ_D); |
||
181 | outb(SEQ_D, 0xDF & c); |
||
182 | |||
183 | outb(crtcaddr, 0x42); |
||
184 | outb(crtcaddr + 1, restore42); |
||
185 | |||
186 | outb(MIS_W, oldclk); |
||
187 | |||
188 | outw(SEQ_I, 0x0300); |
||
189 | |||
190 | vga_waitretrace(); |
||
191 | vga_waitretrace(); |
||
192 | vga_waitretrace(); |
||
193 | vga_waitretrace(); |
||
194 | vga_waitretrace(); |
||
195 | vga_waitretrace(); |
||
196 | vga_waitretrace(); /* 0.10 second delay... */ |
||
197 | } |
||
198 | |||
199 | static int I2061A_match_programmable_clock(int desiredclock) |
||
200 | { |
||
201 | long dvw; |
||
202 | |||
203 | dvw = I2061A_SelectClock((long) desiredclock); |
||
204 | if (dvw) |
||
205 | return I2061A_GetClock(dvw); |
||
206 | return 0; |
||
207 | } |
||
208 | |||
209 | static void I2061A_saveState(unsigned char *regs) |
||
210 | { |
||
211 | long *dvwp; |
||
212 | |||
213 | if (__svgalib_I2061A_clockchip_methods.DAC_saveState) |
||
214 | __svgalib_I2061A_clockchip_methods.DAC_saveState(regs); |
||
215 | |||
216 | dvwp = (long *) (regs + __svgalib_I2061A_clockchip_methods.DAC_stateSize); |
||
217 | *dvwp = I2061A_SelectClock(__svgalib_I2061A_clockchip_methods.TextFrequency); |
||
218 | } |
||
219 | |||
220 | static void I2061A_restoreState(const unsigned char *regs) |
||
221 | { |
||
222 | unsigned int clknum = 2; |
||
223 | long *dvwp; |
||
224 | |||
225 | if (__svgalib_I2061A_clockchip_methods.DAC_restoreState) |
||
226 | __svgalib_I2061A_clockchip_methods.DAC_restoreState(regs); |
||
227 | dvwp = (long *) (regs + __svgalib_I2061A_clockchip_methods.DAC_stateSize); |
||
228 | if (*dvwp) { |
||
229 | /* |
||
230 | * Write ICD 2061A clock chip - assumes S3 to be unlocked! |
||
231 | */ |
||
232 | I2061A_init_clock(((unsigned long) *dvwp) | (((long) clknum) << 21)); |
||
233 | } |
||
234 | } |
||
235 | |||
236 | static void I2061A_initializeState(unsigned char *regs, int bpp, int colormode, int pixelclock) |
||
237 | { |
||
238 | long *dvwp; |
||
239 | |||
240 | if (__svgalib_I2061A_clockchip_methods.DAC_initializeState) |
||
241 | __svgalib_I2061A_clockchip_methods.DAC_initializeState(regs, bpp, colormode, pixelclock); |
||
242 | |||
243 | dvwp = (long *) (regs + __svgalib_I2061A_clockchip_methods.DAC_stateSize); |
||
244 | |||
245 | if (bpp > 16) |
||
246 | pixelclock *= 4; |
||
247 | else if (bpp > 8) |
||
248 | pixelclock *= 2; |
||
249 | |||
250 | *dvwp = I2061A_SelectClock((long) pixelclock); |
||
251 | } |
||
252 | |||
253 | /* This functions patches the DacMethod to route through the ClockChip Method */ |
||
254 | static void I2061A_init(CardSpecs * cardspecs, DacMethods * DAC) |
||
255 | { |
||
256 | if (DAC && !__svgalib_I2061A_clockchip_methods.DAC_initializeState) { |
||
257 | if (__svgalib_driver_report) |
||
81 | giacomo | 258 | cprintf("svgalib: Using ICD2061A or compatible clockchip.\n"); |
54 | pj | 259 | __svgalib_I2061A_clockchip_methods.DAC_initializeState = DAC->initializeState; |
260 | __svgalib_I2061A_clockchip_methods.DAC_saveState = DAC->saveState; |
||
261 | __svgalib_I2061A_clockchip_methods.DAC_restoreState = DAC->restoreState; |
||
262 | __svgalib_I2061A_clockchip_methods.DAC_stateSize = DAC->stateSize; |
||
263 | DAC->initializeState = I2061A_initializeState; |
||
264 | DAC->saveState = I2061A_saveState; |
||
265 | DAC->restoreState = I2061A_restoreState; |
||
266 | DAC->stateSize += sizeof(long); |
||
267 | cardspecs->matchProgrammableClock = I2061A_match_programmable_clock; |
||
268 | cardspecs->flags |= CLOCK_PROGRAMMABLE; |
||
269 | } |
||
270 | } |
||
271 | |||
272 | ClockChipMethods __svgalib_I2061A_clockchip_methods = |
||
273 | { |
||
274 | I2061A_init, |
||
275 | I2061A_saveState, |
||
276 | I2061A_restoreState, |
||
277 | I2061A_initializeState, |
||
278 | NULL, /* DAC function save area */ |
||
279 | NULL, |
||
280 | NULL, |
||
281 | I2061A_DEFAULT_TEXT_FREQUENCY, |
||
282 | 0, |
||
283 | }; |