Rev 54 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
54 | pj | 1 | /* |
2 | * s3dacs.c: |
||
3 | * |
||
4 | * RAMDAC definitions for the S3-SDAC (86C716), S3-GENDAC, and Trio64. |
||
5 | * |
||
6 | * These contain S3-specific code. |
||
7 | */ |
||
8 | |||
9 | //#include <stdio.h> |
||
10 | #include "libvga.h" |
||
11 | #include "timing.h" |
||
12 | #include "vgaregs.h" |
||
13 | #include "driver.h" /* for __svgalib_driver_report */ |
||
14 | #include "ramdac.h" |
||
15 | |||
16 | /* SDAC/GENDAC registers */ |
||
17 | #if defined(INCLUDE_S3_SDAC_DAC) || defined(INCLUDE_S3_GENDAC_DAC) |
||
18 | #define SDAC_COMMAND 0 /* Register offsets into state. */ |
||
19 | #define GENDAC_COMMAND 0 |
||
20 | #define SDAC_PLL_WRITEINDEX 1 |
||
21 | #define SDAC_PLL_READINDEX 2 |
||
22 | #define SDAC_PLL_M 3 /* f2 programmed clock */ |
||
23 | #define SDAC_PLL_N1_N2 4 |
||
24 | #define SDAC_PLL_CONTROL 5 |
||
25 | |||
26 | #define SDAC_STATESIZE 6 /* 6 registers. */ |
||
27 | #define GENDAC_STATESIZE 6 |
||
28 | #endif |
||
29 | |||
30 | #if defined(INCLUDE_S3_SDAC_DAC_TEST) || defined(INCLUDE_S3_GENDAC_DAC_TEST) |
||
31 | static int GENDAC_SDAC_probe(void) |
||
32 | { |
||
33 | /* Taken from XFree86, accel/s3.c. */ |
||
34 | /* Return 1 if GENDAC found, 2 if SDAC, 0 otherwise. */ |
||
35 | /* probe for S3 GENDAC or SDAC */ |
||
36 | /* |
||
37 | * S3 GENDAC and SDAC have two fixed read only PLL clocks |
||
38 | * CLK0 f0: 25.255MHz M-byte 0x28 N-byte 0x61 |
||
39 | * CLK0 f1: 28.311MHz M-byte 0x3d N-byte 0x62 |
||
40 | * which can be used to detect GENDAC and SDAC since there is no chip-id |
||
41 | * for the GENDAC. |
||
42 | * |
||
43 | * NOTE: for the GENDAC on a MIRO 10SD (805+GENDAC) reading PLL values |
||
44 | * for CLK0 f0 and f1 always returns 0x7f (but is documented "read only") |
||
45 | */ |
||
46 | |||
47 | unsigned char saveCR55, savelut[6]; |
||
48 | int i; |
||
49 | long clock01, clock23; |
||
50 | |||
51 | saveCR55 = __svgalib_inCR(0x55); |
||
52 | __svgalib_outbCR(0x55, saveCR55 & ~1); |
||
53 | |||
54 | outb(0x3c7, 0); |
||
55 | for (i = 0; i < 2 * 3; i++) /* save first two LUT entries */ |
||
56 | savelut[i] = inb(0x3c9); |
||
57 | outb(0x3c8, 0); |
||
58 | for (i = 0; i < 2 * 3; i++) /* set first two LUT entries to zero */ |
||
59 | outb(0x3c9, 0); |
||
60 | |||
61 | __svgalib_outbCR(0x55, saveCR55 | 1); |
||
62 | |||
63 | outb(0x3c7, 0); |
||
64 | for (i = clock01 = 0; i < 4; i++) |
||
65 | clock01 = (clock01 << 8) | (inb(0x3c9) & 0xff); |
||
66 | for (i = clock23 = 0; i < 4; i++) |
||
67 | clock23 = (clock23 << 8) | (inb(0x3c9) & 0xff); |
||
68 | |||
69 | __svgalib_outbCR(0x55, saveCR55 & ~1); |
||
70 | |||
71 | outb(0x3c8, 0); |
||
72 | for (i = 0; i < 2 * 3; i++) /* restore first two LUT entries */ |
||
73 | outb(0x3c9, savelut[i]); |
||
74 | |||
75 | __svgalib_outbCR(0x55, saveCR55); |
||
76 | |||
77 | if (clock01 == 0x28613d62 || |
||
78 | (clock01 == 0x7f7f7f7f && clock23 != 0x7f7f7f7f)) { |
||
79 | |||
80 | inb(0x3c8); /* dactopel */ |
||
81 | |||
82 | inb(0x3c6); |
||
83 | inb(0x3c6); |
||
84 | inb(0x3c6); |
||
85 | |||
86 | /* the forth read will show the SDAC chip ID and revision */ |
||
87 | if (((i = inb(0x3c6)) & 0xf0) == 0x70) { |
||
88 | return 2; /* SDAC found. */ |
||
89 | } else { |
||
90 | return 1; /* GENDAC found. */ |
||
91 | } |
||
92 | inb(0x3c8); /* dactopel */ |
||
93 | } |
||
94 | return 0; |
||
95 | } |
||
96 | #endif |
||
97 | |||
98 | #if defined(INCLUDE_S3_SDAC_DAC) || defined(INCLUDE_S3_GENDAC_DAC) |
||
99 | static void GENDAC_SDAC_init(void) |
||
100 | { |
||
101 | unsigned char val; |
||
102 | int m, n, n1, n2, MCLK; |
||
103 | val = __svgalib_inCR(0x55); |
||
104 | __svgalib_outbCR(0x55, val | 0x01); |
||
105 | |||
106 | outb(0x3C7, 10); /* Read MCLK. */ |
||
107 | m = inb(0x3C9); |
||
108 | n = inb(0x3C9); |
||
109 | |||
110 | __svgalib_outbCR(0x55, val); /* Restore CR55. */ |
||
111 | |||
112 | m &= 0x7f; |
||
113 | n1 = n & 0x1f; |
||
114 | n2 = (n >> 5) & 0x03; |
||
115 | /* Calculate MCLK in kHz. */ |
||
116 | MCLK = 14318 * (m + 2) / (n1 + 2) / (1 << n2); |
||
117 | if (__svgalib_driver_report) |
||
118 | printk(KERN_INFO "svgalib: S3-GENDAC/SDAC: MCLK = %d.%03d MHz\n", |
||
119 | MCLK / 1000, MCLK % 1000); |
||
120 | } |
||
121 | #endif |
||
122 | |||
123 | |||
124 | #if defined(INCLUDE_S3_SDAC_DAC) || defined(INCLUDE_S3_GENDAC_DAC) || defined(INCLUDE_S3_TRIO64_DAC) |
||
125 | /* |
||
126 | * From XFree86 common_hw/S3gendac.c and S3gendac.h. |
||
127 | * |
||
128 | * Progaming of the S3 gendac programable clocks, from the S3 Gendac |
||
129 | * programing documentation by S3 Inc. |
||
130 | * Jon Tombs <jon@esix2.us.es> |
||
131 | * |
||
132 | * Returns nonzero if success, 0 if failure. |
||
133 | */ |
||
134 | #define BASE_FREQ 14.31818 /* MHz */ |
||
135 | |||
136 | #define DEBUG_FINDCLOCK 0 |
||
137 | |||
138 | static int S3dacsFindClock(int freq_in, int min_n2, int freq_min, int freq_max, |
||
139 | int *best_m_out, int *best_n1_out, int *best_n2_out) |
||
140 | { |
||
141 | double ffreq_in, ffreq_min, ffreq_max; |
||
142 | double ffreq_out, diff, best_diff; |
||
143 | unsigned int m; |
||
144 | unsigned char n1, n2; |
||
145 | unsigned char best_n1 = 16 + 2, best_n2 = 2, best_m = 125 + 2; |
||
146 | |||
147 | #if DEBUG_FINDCLOCK |
||
148 | printk(KERN_INFO "S3dacsFindClock: Trying to match clock of %0.3f MHz\n", freq_in / 1000.0); |
||
149 | #endif |
||
150 | |||
151 | ffreq_in = freq_in / 1000.0 / BASE_FREQ; |
||
152 | ffreq_min = freq_min / 1000.0 / BASE_FREQ; |
||
153 | ffreq_max = freq_max / 1000.0 / BASE_FREQ; |
||
154 | |||
155 | /* Check if getting freq_in is possible at all */ |
||
156 | if (freq_in < freq_min / 8) { |
||
157 | #if DEBUG_FINDCLOCK |
||
158 | printk(KERN_INFO "S3dacsFindClock: %0.3f MHz is too low (lowest is %0.3f MHz)\n", |
||
159 | freq_in / 1000.0, freq_min / 1000.0 / 8); |
||
160 | #endif |
||
161 | return 0; |
||
162 | } |
||
163 | if (freq_in > freq_max / (1 << min_n2)) { |
||
164 | #if DEBUG_FINDCLOCK |
||
165 | printk(KERN_INFO "S3dacsFindClock: %0.3f MHz is too high (highest is %0.3f MHz)\n", |
||
166 | freq_in / 1000.0, freq_max / 1000.0 / (1 << min_n2)); |
||
167 | #endif |
||
168 | return 0; |
||
169 | } |
||
170 | |||
171 | /* work out suitable timings */ |
||
172 | best_diff = ffreq_in; |
||
173 | for (n2 = min_n2; n2 <= 3; n2++) { |
||
174 | for (n1 = 1 + 2; n1 <= 31 + 2; n1++) { |
||
175 | m = (int) (ffreq_in * n1 * (1 << n2) + 0.5); |
||
176 | if (m < 1 + 2 || m > 127 + 2) |
||
177 | continue; |
||
178 | ffreq_out = (double) (m) / (double) (n1); |
||
179 | if ((ffreq_out >= ffreq_min) && (ffreq_out <= ffreq_max)) { |
||
180 | diff = ffreq_in - ffreq_out / (1 << n2); |
||
181 | if (diff < 0.0) |
||
182 | diff = -diff; |
||
183 | if (diff < best_diff) { |
||
184 | best_diff = diff; |
||
185 | best_m = m; |
||
186 | best_n1 = n1; |
||
187 | best_n2 = n2; |
||
188 | } |
||
189 | } |
||
190 | } |
||
191 | } |
||
192 | |||
193 | #if DEBUG_FINDCLOCK |
||
194 | printk(KERN_INFO "S3dacsFindClock: clock wanted %1.6f MHz, found %1.6f MHz (m %d, n1 %d, n2 %d)\n", |
||
195 | freq_in / 1000.0, |
||
196 | best_m / ((double) best_n1 * (1 << best_n2)) * BASE_FREQ, |
||
197 | best_m, best_n1, best_n2); |
||
198 | #endif |
||
199 | |||
200 | *best_m_out = best_m; |
||
201 | *best_n1_out = best_n1; |
||
202 | *best_n2_out = best_n2; |
||
203 | |||
204 | return 1; |
||
205 | } |
||
206 | #endif |
||
207 | |||
208 | #if defined(INCLUDE_S3_SDAC_DAC) || defined(INCLUDE_S3_GENDAC_DAC) |
||
209 | static int GENDAC_SDAC_match_programmable_clock(int desiredclock) |
||
210 | { |
||
211 | int min_m, min_n1, n2; |
||
212 | |||
213 | /* Note: For ICS5342, min_n2 parameter should be one. */ |
||
214 | if (!S3dacsFindClock(desiredclock, 0, 100000, 250000, &min_m, &min_n1, &n2)) |
||
215 | return 0; |
||
216 | |||
217 | return ((float) (min_m) / (float) (min_n1) / (1 << n2)) * BASE_FREQ * 1000; |
||
218 | } |
||
219 | |||
220 | #if 0 /* Retained for reference. */ |
||
221 | static void setdacpll(reg, data1, data2) |
||
222 | int reg; |
||
223 | unsigned char data1; |
||
224 | unsigned char data2; |
||
225 | { |
||
226 | unsigned char tmp, tmp1; |
||
227 | int vgaCRIndex = vgaIOBase + 4; |
||
228 | int vgaCRReg = vgaIOBase + 5; |
||
229 | |||
230 | /* set RS2 via CR55, yuck */ |
||
231 | tmp = __svgalib_inCR(0x55) & 0xFC; |
||
232 | __svgalib_outCR(tmp | 0x01); |
||
233 | tmp1 = inb(GENDAC_INDEX); |
||
234 | |||
235 | outb(GENDAC_INDEX, reg); |
||
236 | outb(GENDAC_DATA, data1); |
||
237 | outb(GENDAC_DATA, data2); |
||
238 | |||
239 | /* Now clean up our mess */ |
||
240 | outb(GENDAC_INDEX, tmp1); |
||
241 | __svgalib_outbCR(0x55, tmp); |
||
242 | } |
||
243 | #endif |
||
244 | |||
245 | static void GENDAC_SDAC_initialize_clock_state(unsigned char *regs, int freq) |
||
246 | { |
||
247 | int min_m, min_n1, n2; |
||
248 | int n, m; |
||
249 | |||
250 | if (!S3dacsFindClock(freq, 0, 100000, 250000, &min_m, &min_n1, &n2)) { |
||
251 | printk(KERN_INFO "Bad dot clock %0.3f MHz.\n", freq / 1000.0); |
||
252 | return; |
||
253 | } |
||
254 | |||
255 | n = (min_n1 - 2) | (n2 << 5); |
||
256 | m = min_m - 2; |
||
257 | regs[SDAC_PLL_M] = m; |
||
258 | regs[SDAC_PLL_N1_N2] = n; |
||
259 | if (__svgalib_driver_report) |
||
260 | printk(KERN_INFO "Initializing DAC PLL values; 0x%02X, 0x%02X.\n", m, n); |
||
261 | } |
||
262 | |||
263 | static void GENDAC_SDAC_savestate(unsigned char *regs) |
||
264 | { |
||
265 | unsigned char tmp; |
||
266 | tmp = __svgalib_inCR(0x55); |
||
267 | __svgalib_outbCR(0x55, tmp | 1); |
||
268 | |||
269 | regs[SDAC_COMMAND] = inb(0x3c6); |
||
270 | regs[SDAC_PLL_WRITEINDEX] = inb(0x3c8); /* PLL write index */ |
||
271 | regs[SDAC_PLL_READINDEX] = inb(0x3c7); /* PLL read index */ |
||
272 | outb(0x3c7, 2); /* index to f2 reg */ |
||
273 | regs[SDAC_PLL_M] = inb(0x3c9); /* f2 PLL M divider */ |
||
274 | regs[SDAC_PLL_N1_N2] = inb(0x3c9); /* f2 PLL N1/N2 divider */ |
||
275 | outb(0x3c7, 0x0e); /* index to PLL control */ |
||
276 | regs[SDAC_PLL_CONTROL] = inb(0x3c9); /* PLL control */ |
||
277 | |||
278 | __svgalib_outbCR(0x55, tmp & ~1); |
||
279 | } |
||
280 | |||
281 | static void GENDAC_SDAC_restorestate(const unsigned char *regs) |
||
282 | { |
||
283 | unsigned char tmp; |
||
284 | |||
285 | /* set RS2 via CR55, yuck */ |
||
286 | tmp = __svgalib_inCR(0x55) & 0xFC; |
||
287 | __svgalib_outbCR(0x55, tmp | 0x01); |
||
288 | |||
289 | #ifdef DEBUG |
||
290 | do { |
||
291 | int m, n1, n2, clk; |
||
292 | |||
293 | m = regs[SDAC_PLL_M] & 0x7f; |
||
294 | n1 = regs[SDAC_PLL_N1_N2] & 0x1f; |
||
295 | n2 = (regs[SDAC_PLL_N1_N2] & 0x60) >> 5; |
||
296 | |||
297 | clk = 14318 * (m + 2) / (n1 + 2) / (1 << n2); |
||
298 | printk(KERN_INFO "SDAC.restorestate, setting clock 0x%02X 0x%02X (%d.%3dMHz)\n", |
||
299 | regs[SDAC_PLL_M], |
||
300 | regs[SDAC_PLL_N1_N2], clk / 1000, clk % 1000); |
||
301 | } while (0); |
||
302 | #endif |
||
303 | |||
304 | outb(0x3c6, regs[SDAC_COMMAND]); |
||
305 | outb(0x3c8, 2); /* index to f2 reg */ |
||
306 | outb(0x3c9, regs[SDAC_PLL_M]); /* f2 PLL M divider */ |
||
307 | outb(0x3c9, regs[SDAC_PLL_N1_N2]); /* f2 PLL N1/N2 divider */ |
||
308 | outb(0x3c8, 0x0e); /* index to PLL control */ |
||
309 | outb(0x3c9, regs[SDAC_PLL_CONTROL]); /* PLL control */ |
||
310 | outb(0x3c8, regs[SDAC_PLL_WRITEINDEX]); /* PLL write index */ |
||
311 | outb(0x3c7, regs[SDAC_PLL_READINDEX]); /* PLL read index */ |
||
312 | |||
313 | __svgalib_outbCR(0x55, tmp); |
||
314 | } |
||
315 | |||
316 | #endif /* defined(INCLUDE_S3_SDAC_DAC) || defined(INCLUDE_S3_GENDAC_DAC) */ |
||
317 | |||
318 | /* |
||
319 | * SDAC: 16-bit DAC, 110 MHz raw clock limit. |
||
320 | * |
||
321 | * The 135 MHz version supports pixel multiplexing in 8bpp modes with a |
||
322 | * halved raw clock. (SL: at least mine doesn't.) |
||
323 | */ |
||
324 | |||
325 | #ifdef INCLUDE_S3_SDAC_DAC_TEST |
||
326 | static int SDAC_probe(void) |
||
327 | { |
||
328 | return GENDAC_SDAC_probe() == 2; |
||
329 | } |
||
330 | #else |
||
331 | #define SDAC_probe 0 |
||
332 | #endif |
||
333 | |||
334 | #ifdef INCLUDE_S3_SDAC_DAC |
||
335 | static int SDAC_map_clock(int bpp, int pixelclock) |
||
336 | { |
||
337 | switch (bpp) { |
||
338 | case 4: |
||
339 | case 8: |
||
340 | #ifdef SDAC_8BPP_PIXMUX /* SL: AFAIK it doesn't work */ |
||
341 | if (pixelclock >= 67500) |
||
342 | /* Use pixel multiplexing. */ |
||
343 | return pixelclock / 2; |
||
344 | #endif |
||
345 | break; |
||
346 | case 24: |
||
347 | return pixelclock * 3 / 2; |
||
348 | case 32: |
||
349 | return pixelclock * 2; |
||
350 | } |
||
351 | return pixelclock; |
||
352 | } |
||
353 | |||
354 | static int SDAC_map_horizontal_crtc(int bpp, int pixelclock, int htiming) |
||
355 | { |
||
356 | switch (bpp) { |
||
357 | case 16: |
||
358 | return htiming * 2; |
||
359 | case 24: |
||
360 | return htiming * 3; |
||
361 | case 32: |
||
362 | return htiming * 4; |
||
363 | } |
||
364 | return htiming; |
||
365 | } |
||
366 | |||
367 | static void SDAC_initializestate(unsigned char *regs, int bpp, int colormode, |
||
368 | int pixelclock) |
||
369 | { |
||
370 | int pixmux; /* SDAC command register. */ |
||
371 | pixmux = 0; |
||
372 | switch (colormode) { |
||
373 | case CLUT8_6: |
||
374 | #ifdef SDAC_8BPP_PIXMUX |
||
375 | if (pixelclock >= 67500) |
||
376 | pixmux = 0x10; |
||
377 | #endif |
||
378 | break; |
||
379 | case RGB16_555: |
||
380 | pixmux = 0x30; |
||
381 | break; |
||
382 | case RGB16_565: |
||
383 | pixmux = 0x50; |
||
384 | break; |
||
385 | case RGB24_888_B: |
||
386 | /* Use 0x40 for 3 VCLK/pixel. Change SDAC_map_clock and CR67 as well. */ |
||
387 | pixmux = 0x90; |
||
388 | break; |
||
389 | case RGB32_888_B: |
||
390 | pixmux = 0x70; |
||
391 | break; |
||
392 | } |
||
393 | regs[SDAC_COMMAND] = pixmux; |
||
394 | GENDAC_SDAC_initialize_clock_state(regs, |
||
395 | SDAC_map_clock(bpp, pixelclock)); |
||
396 | } |
||
397 | |||
398 | static void SDAC_qualify_cardspecs(CardSpecs * cardspecs, int dacspeed) |
||
399 | { |
||
400 | dacspeed = __svgalib_setDacSpeed(dacspeed, 110000); /* most can do 135MHz. */ |
||
401 | cardspecs->maxPixelClock4bpp = dacspeed; |
||
402 | cardspecs->maxPixelClock8bpp = dacspeed; |
||
403 | cardspecs->maxPixelClock16bpp = dacspeed; |
||
404 | cardspecs->maxPixelClock24bpp = dacspeed * 2 / 3; |
||
405 | cardspecs->maxPixelClock32bpp = dacspeed / 2; |
||
406 | cardspecs->mapClock = SDAC_map_clock; |
||
407 | cardspecs->matchProgrammableClock = GENDAC_SDAC_match_programmable_clock; |
||
408 | cardspecs->mapHorizontalCrtc = SDAC_map_horizontal_crtc; |
||
409 | cardspecs->flags |= CLOCK_PROGRAMMABLE; |
||
410 | } |
||
411 | |||
412 | DacMethods __svgalib_S3_SDAC_methods = |
||
413 | { |
||
414 | S3_SDAC, |
||
415 | "S3-SDAC (86C716)", |
||
416 | DAC_HAS_PROGRAMMABLE_CLOCKS, |
||
417 | SDAC_probe, |
||
418 | GENDAC_SDAC_init, |
||
419 | SDAC_qualify_cardspecs, |
||
420 | GENDAC_SDAC_savestate, |
||
421 | GENDAC_SDAC_restorestate, |
||
422 | SDAC_initializestate, |
||
423 | SDAC_STATESIZE |
||
424 | }; |
||
425 | #endif |
||
426 | |||
427 | |||
428 | /* S3-GENDAC, 8-bit DAC. */ |
||
429 | |||
430 | #ifdef INCLUDE_S3_GENDAC_DAC_TEST |
||
431 | static int GENDAC_probe(void) |
||
432 | { |
||
433 | return GENDAC_SDAC_probe() == 1; |
||
434 | } |
||
435 | #else |
||
436 | #define GENDAC_probe 0 |
||
437 | #endif |
||
438 | |||
439 | #ifdef INCLUDE_S3_GENDAC_DAC |
||
440 | static int GENDAC_map_clock(int bpp, int pixelclock) |
||
441 | { |
||
442 | if (bpp == 16) |
||
443 | return pixelclock * 2; |
||
444 | if (bpp == 24) |
||
445 | return pixelclock * 3; |
||
446 | if (bpp == 32) |
||
447 | return pixelclock * 4; |
||
448 | return pixelclock; |
||
449 | } |
||
450 | |||
451 | static int GENDAC_map_horizontal_crtc(int bpp, int pixelclock, int htiming) |
||
452 | { |
||
453 | /* XXXX Not sure. */ |
||
454 | if (bpp == 24) |
||
455 | return htiming * 3; |
||
456 | if (bpp == 16) |
||
457 | return htiming * 2; |
||
458 | return htiming; |
||
459 | } |
||
460 | |||
461 | static void GENDAC_initializestate(unsigned char *regs, int bpp, int colormode, |
||
462 | int pixelclock) |
||
463 | { |
||
464 | int daccomm; /* DAC command register. */ |
||
465 | daccomm = 0; |
||
466 | if (colormode == RGB16_555) |
||
467 | daccomm = 0x20; |
||
468 | else if (colormode == RGB16_565) |
||
469 | daccomm = 0x60; |
||
470 | else if (colormode == RGB24_888_B) |
||
471 | daccomm = 0x40; |
||
472 | regs[GENDAC_COMMAND] = daccomm; |
||
473 | GENDAC_SDAC_initialize_clock_state(regs, |
||
474 | GENDAC_map_clock(bpp, pixelclock)); |
||
475 | } |
||
476 | |||
477 | static void GENDAC_qualify_cardspecs(CardSpecs * cardspecs, int dacspeed) |
||
478 | { |
||
479 | dacspeed = __svgalib_setDacSpeed(dacspeed, 110000); |
||
480 | cardspecs->maxPixelClock4bpp = dacspeed; |
||
481 | cardspecs->maxPixelClock8bpp = dacspeed; |
||
482 | cardspecs->maxPixelClock16bpp = dacspeed / 2; |
||
483 | cardspecs->maxPixelClock24bpp = dacspeed / 3; |
||
484 | cardspecs->maxPixelClock32bpp = 0; |
||
485 | cardspecs->mapClock = GENDAC_map_clock; |
||
486 | cardspecs->matchProgrammableClock = GENDAC_SDAC_match_programmable_clock; |
||
487 | cardspecs->mapHorizontalCrtc = GENDAC_map_horizontal_crtc; |
||
488 | cardspecs->flags |= CLOCK_PROGRAMMABLE; |
||
489 | } |
||
490 | |||
491 | DacMethods __svgalib_S3_GENDAC_methods = |
||
492 | { |
||
493 | S3_GENDAC, |
||
494 | "S3-GENDAC (86C708)", |
||
495 | DAC_HAS_PROGRAMMABLE_CLOCKS, |
||
496 | GENDAC_probe, |
||
497 | GENDAC_SDAC_init, |
||
498 | GENDAC_qualify_cardspecs, |
||
499 | GENDAC_SDAC_savestate, |
||
500 | GENDAC_SDAC_restorestate, |
||
501 | GENDAC_initializestate, |
||
502 | GENDAC_STATESIZE |
||
503 | }; |
||
504 | #endif |
||
505 | |||
506 | |||
507 | #ifdef INCLUDE_S3_TRIO64_DAC |
||
508 | /* S3-Trio64, 16-bit integrated DAC. */ |
||
509 | |||
510 | #define TRIO64_SR15 0 |
||
511 | #define TRIO64_SR18 1 |
||
512 | #define TRIO64_PLL_N1_N2 2 |
||
513 | #define TRIO64_PLL_M 3 |
||
514 | #define TRIO64_CR67 4 |
||
515 | #define TRIO64_SRB 5 |
||
516 | #define TRIO64_STATESIZE 6 |
||
517 | |||
518 | /* Note: s3.c also defines CR67, but doesn't use it for the Trio64. */ |
||
519 | |||
520 | extern int __svgalib_s3_s3Mclk; |
||
521 | |||
522 | static int Trio64_get_mclk(void) |
||
523 | { |
||
524 | unsigned char sr8; |
||
525 | int m, n, n1, n2; |
||
526 | |||
527 | outb(0x3c4, 0x08); |
||
528 | sr8 = inb(0x3c5); |
||
529 | outb(0x3c5, 0x06); |
||
530 | |||
531 | outb(0x3c4, 0x11); |
||
532 | m = inb(0x3c5); |
||
533 | outb(0x3c4, 0x10); |
||
534 | n = inb(0x3c5); |
||
535 | |||
536 | outb(0x3c4, 0x08); |
||
537 | outb(0x3c5, sr8); |
||
538 | |||
539 | m &= 0x7f; |
||
540 | n1 = n & 0x1f; |
||
541 | n2 = (n >> 5) & 0x03; |
||
542 | /* Calculate MCLK in kHz. */ |
||
543 | return ((1431818 * (m + 2)) / (n1 + 2) / (1 << n2) + 50) / 100; |
||
544 | } |
||
545 | |||
546 | #if 0 |
||
547 | static void Trio64_set_mclk(int khz) |
||
548 | /* Doesn't work. Locks computer up. Why? */ |
||
549 | { |
||
550 | int sr8; |
||
551 | int min_m, min_n1, n2; |
||
552 | |||
553 | if (!S3dacsFindClock(khz, 0, 40000, 70000, &min_m, &min_n1, &n2)) { |
||
81 | giacomo | 554 | printk(KERN_INFO "Bad MCLK %0.3f MHz.\n", khz / 1000.0); |
54 | pj | 555 | return; |
556 | } |
||
557 | |||
81 | giacomo | 558 | printk(KERN_INFO "%0.3f MHz MCLK, m = %d, n = %d, r = %d\n", khz / 1000.0, min_m - 2, min_n1 - 2, n2); |
54 | pj | 559 | outb(0x3C4, 0x08); |
560 | sr8 = inb(0x3C5); |
||
561 | outb(0x3C5, 0x06); /* Unlock. */ |
||
562 | |||
563 | outb(0x3c4, 0x15); |
||
564 | outb(0x3c5, inb(0x3c5) & ~0x20); |
||
565 | |||
566 | /* MCLK. */ |
||
567 | __svgalib_outSR(0x10, (min_n1 - 2) | (n2 << 5)); |
||
568 | __svgalib_outSR(0x11, min_m - 2); |
||
569 | |||
570 | outb(0x3c4, 0x15); |
||
571 | outb(0x3c5, inb(0x3c5) | 0x20); |
||
572 | outb(0x3c5, inb(0x3c5) & ~0x20); |
||
573 | |||
574 | __svgalib_outSR(0x08, sr8); |
||
575 | } |
||
576 | #endif |
||
577 | |||
578 | static void Trio64_init(void) |
||
579 | { |
||
580 | int mclk; |
||
581 | |||
582 | mclk = Trio64_get_mclk(); |
||
583 | if (__svgalib_driver_report) |
||
584 | printk(KERN_INFO "svgalib: RAMDAC: Trio64: MCLK = %0.3f MHz\n", |
||
585 | mclk / 1000.0); |
||
586 | __svgalib_s3_s3Mclk = mclk; |
||
587 | } |
||
588 | |||
589 | static int Trio64_map_clock(int bpp, int pixelclock) |
||
590 | { |
||
591 | if (bpp == 8 && pixelclock >= 67500) /* use pixel doubling */ |
||
592 | return pixelclock / 2; |
||
593 | if (bpp == 24) |
||
594 | return pixelclock * 3; |
||
595 | return pixelclock; |
||
596 | } |
||
597 | |||
598 | static int Trio64_map_horizontal_crtc(int bpp, int pixelclock, int htiming) |
||
599 | { |
||
600 | if (bpp == 16) |
||
601 | return htiming * 2; |
||
602 | /* Normal mapping for 8bpp and 32bpp. */ |
||
603 | return htiming; |
||
604 | } |
||
605 | |||
606 | static void Trio64_initialize_clock_state(unsigned char *regs, int freq) |
||
607 | { |
||
608 | int min_m, min_n1, n2; |
||
609 | int n, m; |
||
610 | |||
611 | if (!S3dacsFindClock(freq, 0, 130000, 270000, &min_m, &min_n1, &n2)) { |
||
612 | printk(KERN_INFO "Bad dot clock %0.3f MHz.\n", freq / 1000.0); |
||
613 | return; |
||
614 | } |
||
615 | |||
616 | n = (min_n1 - 2) | (n2 << 5); |
||
617 | m = min_m - 2; |
||
618 | regs[TRIO64_PLL_M] = m; |
||
619 | regs[TRIO64_PLL_N1_N2] = n; |
||
620 | } |
||
621 | |||
622 | static int Trio64_match_programmable_clock(int desiredclock) |
||
623 | { |
||
624 | int min_m, min_n1, n2; |
||
625 | |||
626 | if (!S3dacsFindClock(desiredclock, 0, 130000, 270000, &min_m, &min_n1, &n2)) |
||
627 | return 0; |
||
628 | |||
629 | return ((float) (min_m) / (float) (min_n1) / (1 << n2)) * BASE_FREQ * 1000; |
||
630 | } |
||
631 | |||
632 | static void Trio64_initializestate(unsigned char *regs, int bpp, int colormode, |
||
633 | int pixelclock) |
||
634 | { |
||
635 | int pixmux, reserved_CR67_1; |
||
636 | |||
637 | regs[TRIO64_SR15] &= ~0x50; |
||
638 | regs[TRIO64_SR18] &= ~0x80; |
||
639 | pixmux = 0; |
||
640 | reserved_CR67_1 = 0; |
||
641 | if (bpp == 8 && pixelclock >= 67500) { |
||
642 | pixmux = 0x10; |
||
643 | reserved_CR67_1 = 2; |
||
644 | regs[TRIO64_SR15] |= 0x50; |
||
645 | regs[TRIO64_SR18] |= 0x80; |
||
646 | } else if (bpp == 16) { |
||
647 | /* moderegs[S3_CR33] |= 0x08; *//* done in s3.c. */ |
||
648 | if (colormode == RGB16_555) |
||
649 | pixmux = 0x30; |
||
650 | else |
||
651 | pixmux = 0x50; |
||
652 | reserved_CR67_1 = 2; |
||
653 | } else if (colormode == RGB24_888_B) { |
||
654 | /* remember to adjust SRB as well. */ |
||
655 | pixmux = 0x00; |
||
656 | } else if (colormode == RGB32_888_B) { |
||
657 | pixmux = 0xD0; /* 32-bit color, 2 VCLKs/pixel. */ |
||
658 | reserved_CR67_1 = 2; |
||
659 | } |
||
660 | regs[TRIO64_CR67] = pixmux | reserved_CR67_1; |
||
661 | |||
662 | Trio64_initialize_clock_state(regs, pixelclock); |
||
663 | } |
||
664 | |||
665 | static void Trio64_savestate(unsigned char *regs) |
||
666 | { |
||
667 | unsigned char sr8; |
||
668 | outb(0x3C4, 0x08); |
||
669 | sr8 = inb(0x3C5); |
||
670 | outb(0x3C5, 0x06); /* Unlock. */ |
||
671 | |||
672 | regs[TRIO64_SR15] = __svgalib_inSR(0x15); |
||
673 | regs[TRIO64_SR18] = __svgalib_inSR(0x18); |
||
674 | regs[TRIO64_PLL_N1_N2] = __svgalib_inSR(0x12); |
||
675 | regs[TRIO64_PLL_M] = __svgalib_inSR(0x13); |
||
676 | regs[TRIO64_CR67] = __svgalib_inCR(0x67); |
||
677 | |||
678 | __svgalib_outSR(0x08, sr8); |
||
679 | } |
||
680 | |||
681 | static void Trio64_restorestate(const unsigned char *regs) |
||
682 | { |
||
683 | unsigned char sr8, tmp; |
||
684 | |||
685 | outb(0x3C4, 0x08); |
||
686 | sr8 = inb(0x3C5); |
||
687 | outb(0x3C5, 0x06); /* Unlock. */ |
||
688 | |||
689 | __svgalib_outCR(0x67, regs[TRIO64_CR67]); |
||
690 | |||
691 | __svgalib_outSR(0x15, regs[TRIO64_SR15]); |
||
692 | __svgalib_outSR(0x18, regs[TRIO64_SR18]); |
||
693 | |||
694 | /* Clock. */ |
||
695 | __svgalib_outSR(0x12, regs[TRIO64_PLL_N1_N2]); |
||
696 | __svgalib_outSR(0x13, regs[TRIO64_PLL_M]); |
||
697 | |||
698 | #if 0 |
||
699 | /* |
||
700 | * XFree86 XF86_S3 (common_hw/gendac.c) has this, but it looks |
||
701 | * incorrect, it should flip the bit by writing to 0x3c5, not |
||
702 | * 0x3c4. |
||
703 | */ |
||
704 | outb(0x3c4, 0x15); |
||
705 | tmp = inb(0x3c5); |
||
706 | outb(0x3c4, tmp & ~0x20); |
||
707 | outb(0x3c4, tmp | 0x20); |
||
708 | outb(0x3c4, tmp & ~0x20); |
||
709 | #else |
||
710 | outb(0x3c4, 0x15); |
||
711 | tmp = inb(0x3c5); |
||
712 | outb(0x3c5, tmp & ~0x20); |
||
713 | outb(0x3c5, tmp | 0x20); |
||
714 | outb(0x3c5, tmp & ~0x20); |
||
715 | #endif |
||
716 | |||
717 | __svgalib_outSR(0x08, sr8); |
||
718 | } |
||
719 | |||
720 | |||
721 | static void Trio64_qualify_cardspecs(CardSpecs * cardspecs, int dacspeed) |
||
722 | { |
||
723 | if (dacspeed) { |
||
724 | if (__svgalib_driver_report) |
||
725 | printk(KERN_INFO "svgalib: using 'dacspeed' not recommended for this RAMDAC.\n"); |
||
726 | cardspecs->maxPixelClock4bpp = dacspeed; |
||
727 | cardspecs->maxPixelClock8bpp = 135000; |
||
728 | cardspecs->maxPixelClock16bpp = dacspeed; |
||
729 | cardspecs->maxPixelClock24bpp = 0; /* dacspeed / 3; *//* How to program? */ |
||
730 | cardspecs->maxPixelClock32bpp = 50000; |
||
731 | } else { |
||
732 | cardspecs->maxPixelClock4bpp = 80000; |
||
733 | cardspecs->maxPixelClock8bpp = 135000; |
||
734 | cardspecs->maxPixelClock16bpp = 80000; |
||
735 | cardspecs->maxPixelClock24bpp = 0; /* 25000; *//* How to program? */ |
||
736 | cardspecs->maxPixelClock32bpp = 50000; |
||
737 | } |
||
738 | cardspecs->mapClock = Trio64_map_clock; |
||
739 | cardspecs->matchProgrammableClock = Trio64_match_programmable_clock; |
||
740 | cardspecs->mapHorizontalCrtc = Trio64_map_horizontal_crtc; |
||
741 | cardspecs->flags |= CLOCK_PROGRAMMABLE; |
||
742 | } |
||
743 | |||
744 | DacMethods __svgalib_Trio64_methods = |
||
745 | { |
||
746 | TRIO64, |
||
747 | "S3-Trio64 internal DAC", |
||
748 | DAC_HAS_PROGRAMMABLE_CLOCKS, |
||
749 | NULL, /* probe */ |
||
750 | Trio64_init, |
||
751 | Trio64_qualify_cardspecs, |
||
752 | Trio64_savestate, |
||
753 | Trio64_restorestate, |
||
754 | Trio64_initializestate, |
||
755 | TRIO64_STATESIZE |
||
756 | }; |
||
757 | #endif |