Rev 54 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
54 | pj | 1 | /* |
2 | * ics_gendac.c: |
||
3 | * |
||
4 | * This works only with ARK, since it has ARK specific code. |
||
5 | */ |
||
6 | |||
7 | #include <stdio.h> |
||
8 | #include "libvga.h" |
||
9 | #include "timing.h" |
||
10 | #include "vgaregs.h" |
||
11 | #include "driver.h" /* for __svgalib_driver_report */ |
||
12 | #include "ramdac.h" |
||
13 | |||
14 | /* SDAC/GENDAC registers */ |
||
15 | #define SDAC_COMMAND 0 |
||
16 | #define GENDAC_COMMAND 0 |
||
17 | #define SDAC_PLL_WRITEINDEX 1 |
||
18 | #define SDAC_PLL_READINDEX 2 |
||
19 | #define SDAC_PLL_M 3 /* f2 programmed clock */ |
||
20 | #define SDAC_PLL_N1_N2 4 |
||
21 | #define SDAC_PLL_CONTROL 5 |
||
22 | |||
23 | #define GENDAC_STATESIZE 6 |
||
24 | |||
25 | static void GENDAC_init(void) |
||
26 | { |
||
27 | } |
||
28 | |||
29 | |||
30 | /* |
||
31 | * From XFree86 common_hw/S3gendac.c and S3gendac.h. |
||
32 | * |
||
33 | * Progaming of the S3 gendac programable clocks, from the S3 Gendac |
||
34 | * programing documentation by S3 Inc. |
||
35 | * Jon Tombs <jon@esix2.us.es> |
||
36 | * |
||
37 | * Returns nonzero if success, 0 if failure. |
||
38 | */ |
||
39 | #define BASE_FREQ 14.31818 /* MHz */ |
||
40 | |||
41 | #define DEBUG_FINDCLOCK 0 |
||
42 | |||
43 | static int S3dacsFindClock(int freq_in, int min_n2, int freq_min, int freq_max, |
||
44 | int *best_m_out, int *best_n1_out, int *best_n2_out) |
||
45 | { |
||
46 | double ffreq_in, ffreq_min, ffreq_max; |
||
47 | double ffreq_out, diff, best_diff; |
||
48 | unsigned int m; |
||
49 | unsigned char n1, n2; |
||
50 | unsigned char best_n1 = 16 + 2, best_n2 = 2, best_m = 125 + 2; |
||
51 | |||
52 | #if DEBUG_FINDCLOCK |
||
81 | giacomo | 53 | cprintf("S3dacsFindClock: Trying to match clock of %0.3f MHz\n", freq_in / 1000.0); |
54 | pj | 54 | #endif |
55 | |||
56 | ffreq_in = freq_in / 1000.0 / BASE_FREQ; |
||
57 | ffreq_min = freq_min / 1000.0 / BASE_FREQ; |
||
58 | ffreq_max = freq_max / 1000.0 / BASE_FREQ; |
||
59 | |||
60 | /* Check if getting freq_in is possible at all */ |
||
61 | if (freq_in < freq_min / 8) { |
||
62 | #if DEBUG_FINDCLOCK |
||
81 | giacomo | 63 | cprintf("S3dacsFindClock: %0.3f MHz is too low (lowest is %0.3f MHz)\n", |
54 | pj | 64 | freq_in / 1000.0, freq_min / 1000.0 / 8); |
65 | #endif |
||
66 | return 0; |
||
67 | } |
||
68 | if (freq_in > freq_max / (1 << min_n2)) { |
||
69 | #if DEBUG_FINDCLOCK |
||
81 | giacomo | 70 | cprintf("S3dacsFindClock: %0.3f MHz is too high (highest is %0.3f MHz)\n", |
54 | pj | 71 | freq_in / 1000.0, freq_max / 1000.0 / (1 << min_n2)); |
72 | #endif |
||
73 | return 0; |
||
74 | } |
||
75 | |||
76 | /* work out suitable timings */ |
||
77 | best_diff = ffreq_in; |
||
78 | for (n2 = min_n2; n2 <= 3; n2++) { |
||
79 | for (n1 = 1 + 2; n1 <= 31 + 2; n1++) { |
||
80 | m = (int) (ffreq_in * n1 * (1 << n2) + 0.5); |
||
81 | if (m < 1 + 2 || m > 127 + 2) |
||
82 | continue; |
||
83 | ffreq_out = (double) (m) / (double) (n1); |
||
84 | if ((ffreq_out >= ffreq_min) && (ffreq_out <= ffreq_max)) { |
||
85 | diff = ffreq_in - ffreq_out / (1 << n2); |
||
86 | if (diff < 0.0) |
||
87 | diff = -diff; |
||
88 | if (diff < best_diff) { |
||
89 | best_diff = diff; |
||
90 | best_m = m; |
||
91 | best_n1 = n1; |
||
92 | best_n2 = n2; |
||
93 | } |
||
94 | } |
||
95 | } |
||
96 | } |
||
97 | |||
98 | #if DEBUG_FINDCLOCK |
||
81 | giacomo | 99 | cprintf("S3dacsFindClock: clock wanted %1.6f MHz, found %1.6f MHz (m %d, n1 %d, n2 %d)\n", |
54 | pj | 100 | freq_in / 1000.0, |
101 | best_m / ((double) best_n1 * (1 << best_n2)) * BASE_FREQ, |
||
102 | best_m, best_n1, best_n2); |
||
103 | #endif |
||
104 | |||
105 | *best_m_out = best_m; |
||
106 | *best_n1_out = best_n1; |
||
107 | *best_n2_out = best_n2; |
||
108 | |||
109 | return 1; |
||
110 | } |
||
111 | |||
112 | static int GENDAC_match_programmable_clock(int desiredclock) |
||
113 | { |
||
114 | int min_m, min_n1, n2; |
||
115 | |||
116 | /* Note: For ICS5342, min_n2 parameter should be one. */ |
||
117 | if (!S3dacsFindClock(desiredclock, 0, 100000, 250000, &min_m, &min_n1, &n2)) |
||
118 | return 0; |
||
119 | |||
120 | return ((float) (min_m) / (float) (min_n1) / (1 << n2)) * BASE_FREQ * 1000; |
||
121 | } |
||
122 | |||
123 | static void GENDAC_initialize_clock_state(unsigned char *regs, int freq) |
||
124 | { |
||
125 | int min_m, min_n1, n2; |
||
126 | int n, m; |
||
127 | |||
128 | if (!S3dacsFindClock(freq, 0, 100000, 250000, &min_m, &min_n1, &n2)) { |
||
81 | giacomo | 129 | cprintf("Bad dot clock %0.3f MHz.\n", freq / 1000.0); |
54 | pj | 130 | return; |
131 | } |
||
132 | |||
133 | n = (min_n1 - 2) | (n2 << 5); |
||
134 | m = min_m - 2; |
||
135 | regs[SDAC_PLL_M] = m; |
||
136 | regs[SDAC_PLL_N1_N2] = n; |
||
137 | #if 0 |
||
138 | if (__svgalib_driver_report) |
||
81 | giacomo | 139 | cprintf("Initializing DAC PLL values; 0x%02X, 0x%02X.\n", m, n); |
54 | pj | 140 | #endif |
141 | } |
||
142 | |||
143 | static void GENDAC_savestate(unsigned char *regs) |
||
144 | { |
||
145 | unsigned char tmp; |
||
146 | tmp = __svgalib_inSR(0x1c); |
||
147 | __svgalib_outSR(0x1c, tmp | 0x80); |
||
148 | |||
149 | regs[SDAC_COMMAND] = inb(0x3c6); |
||
150 | regs[SDAC_PLL_WRITEINDEX] = inb(0x3c8); /* PLL write index */ |
||
151 | regs[SDAC_PLL_READINDEX] = inb(0x3c7); /* PLL read index */ |
||
152 | outb(0x3c7, 2); /* index to f2 reg */ |
||
153 | regs[SDAC_PLL_M] = inb(0x3c9); /* f2 PLL M divider */ |
||
154 | regs[SDAC_PLL_N1_N2] = inb(0x3c9); /* f2 PLL N1/N2 divider */ |
||
155 | outb(0x3c7, 0x0e); /* index to PLL control */ |
||
156 | regs[SDAC_PLL_CONTROL] = inb(0x3c9); /* PLL control */ |
||
157 | |||
158 | __svgalib_outSR(0x1c, tmp & ~0x80); |
||
159 | } |
||
160 | |||
161 | static void GENDAC_restorestate(const unsigned char *regs) |
||
162 | { |
||
163 | unsigned char tmp; |
||
164 | |||
165 | tmp = __svgalib_inseq(0x1c); |
||
166 | __svgalib_outseq(0x1c, tmp | 0x80); |
||
167 | |||
168 | outb(0x3c6, regs[SDAC_COMMAND]); |
||
169 | outb(0x3c8, 2); /* index to f2 reg */ |
||
170 | outb(0x3c9, regs[SDAC_PLL_M]); /* f2 PLL M divider */ |
||
171 | outb(0x3c9, regs[SDAC_PLL_N1_N2]); /* f2 PLL N1/N2 divider */ |
||
172 | outb(0x3c8, 0x0e); /* index to PLL control */ |
||
173 | outb(0x3c9, regs[SDAC_PLL_CONTROL]); /* PLL control */ |
||
174 | outb(0x3c8, regs[SDAC_PLL_WRITEINDEX]); /* PLL write index */ |
||
175 | outb(0x3c7, regs[SDAC_PLL_READINDEX]); /* PLL read index */ |
||
176 | |||
177 | __svgalib_outseq(0x1c, tmp); |
||
178 | } |
||
179 | |||
180 | /* |
||
181 | * SDAC: 16-bit DAC, 110 MHz raw clock limit. |
||
182 | * |
||
183 | * The 135 MHz version supports pixel multiplexing in 8bpp modes with a |
||
184 | * halved raw clock. (SL: at least mine doesn't.) |
||
185 | */ |
||
186 | |||
187 | static int GENDAC_probe(void) |
||
188 | { |
||
189 | int i; |
||
190 | inb(0x3c6); |
||
191 | inb(0x3c6); |
||
192 | inb(0x3c6); |
||
193 | i=inb(0x3c6); |
||
194 | if(i==177) return 1; |
||
195 | return 0; |
||
196 | } |
||
197 | |||
198 | static int GENDAC_map_clock(int bpp, int pixelclock) |
||
199 | { |
||
200 | if (bpp == 16) |
||
201 | return pixelclock * 2; |
||
202 | if (bpp == 24) |
||
203 | return pixelclock * 3; |
||
204 | if (bpp == 32) |
||
205 | return pixelclock * 4; |
||
206 | return pixelclock; |
||
207 | } |
||
208 | |||
209 | static int GENDAC_map_horizontal_crtc(int bpp, int pixelclock, int htiming) |
||
210 | { |
||
211 | /* XXXX Not sure. */ |
||
212 | if (bpp == 24) |
||
213 | return htiming * 3; |
||
214 | if (bpp == 16) |
||
215 | return htiming * 2; |
||
216 | return htiming; |
||
217 | } |
||
218 | |||
219 | static void GENDAC_initializestate(unsigned char *regs, int bpp, int colormode, |
||
220 | int pixelclock) |
||
221 | { |
||
222 | int daccomm; /* DAC command register. */ |
||
223 | daccomm = 0; |
||
224 | if (colormode == RGB16_555) |
||
225 | daccomm = 0x20; |
||
226 | else if (colormode == RGB16_565) |
||
227 | daccomm = 0x60; |
||
228 | else if (colormode == RGB24_888_B) |
||
229 | daccomm = 0x40; |
||
230 | regs[GENDAC_COMMAND] = daccomm; |
||
231 | GENDAC_initialize_clock_state(regs, |
||
232 | GENDAC_map_clock(bpp, pixelclock)); |
||
233 | } |
||
234 | |||
235 | static void GENDAC_qualify_cardspecs(CardSpecs * cardspecs, int dacspeed) |
||
236 | { |
||
237 | dacspeed = __svgalib_setDacSpeed(dacspeed, 110000); |
||
238 | cardspecs->maxPixelClock4bpp = dacspeed; |
||
239 | cardspecs->maxPixelClock8bpp = dacspeed; |
||
240 | cardspecs->maxPixelClock16bpp = dacspeed / 2; |
||
241 | cardspecs->maxPixelClock24bpp = dacspeed / 3; |
||
242 | cardspecs->maxPixelClock32bpp = 0; |
||
243 | cardspecs->mapClock = GENDAC_map_clock; |
||
244 | cardspecs->matchProgrammableClock = GENDAC_match_programmable_clock; |
||
245 | cardspecs->mapHorizontalCrtc = GENDAC_map_horizontal_crtc; |
||
246 | cardspecs->flags |= CLOCK_PROGRAMMABLE; |
||
247 | } |
||
248 | |||
249 | DacMethods __svgalib_ICS_GENDAC_methods = |
||
250 | { |
||
251 | S3_GENDAC, |
||
252 | "ICS-GENDAC (5342)", |
||
253 | DAC_HAS_PROGRAMMABLE_CLOCKS, |
||
254 | GENDAC_probe, |
||
255 | GENDAC_init, |
||
256 | GENDAC_qualify_cardspecs, |
||
257 | GENDAC_savestate, |
||
258 | GENDAC_restorestate, |
||
259 | GENDAC_initializestate, |
||
260 | GENDAC_STATESIZE |
||
261 | }; |