Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /* |
2 | * Copyright (c) 1997-2003 by The XFree86 Project, Inc. | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
20 | * OTHER DEALINGS IN THE SOFTWARE. | |
21 | * | |
22 | * Except as contained in this notice, the name of the copyright holder(s) | |
23 | * and author(s) shall not be used in advertising or otherwise to promote | |
24 | * the sale, use or other dealings in this Software without prior written | |
25 | * authorization from the copyright holder(s) and author(s). | |
26 | */ | |
27 | ||
28 | /* | |
29 | * LCM() and scanLineWidth() are: | |
30 | * | |
31 | * Copyright 1997 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org | |
32 | * | |
33 | * Permission to use, copy, modify, distribute, and sell this software and its | |
34 | * documentation for any purpose is hereby granted without fee, provided that | |
35 | * the above copyright notice appear in all copies and that both that copyright | |
36 | * notice and this permission notice appear in supporting documentation, and | |
37 | * that the name of Marc Aurele La France not be used in advertising or | |
38 | * publicity pertaining to distribution of the software without specific, | |
39 | * written prior permission. Marc Aurele La France makes no representations | |
40 | * about the suitability of this software for any purpose. It is provided | |
41 | * "as-is" without express or implied warranty. | |
42 | * | |
43 | * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | |
44 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO | |
45 | * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR | |
46 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | |
47 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | |
48 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | |
49 | * PERFORMANCE OF THIS SOFTWARE. | |
50 | * | |
51 | * Copyright 1990,91,92,93 by Thomas Roell, Germany. | |
52 | * Copyright 1991,92,93 by SGCS (Snitily Graphics Consulting Services), USA. | |
53 | * | |
54 | * Permission to use, copy, modify, distribute, and sell this software | |
55 | * and its documentation for any purpose is hereby granted without fee, | |
56 | * provided that the above copyright notice appear in all copies and | |
57 | * that both that copyright notice and this permission notice appear | |
58 | * in supporting documentation, and that the name of Thomas Roell nor | |
59 | * SGCS be used in advertising or publicity pertaining to distribution | |
60 | * of the software without specific, written prior permission. | |
61 | * Thomas Roell nor SGCS makes no representations about the suitability | |
62 | * of this software for any purpose. It is provided "as is" without | |
63 | * express or implied warranty. | |
64 | * | |
65 | * THOMAS ROELL AND SGCS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS | |
66 | * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
67 | * FITNESS, IN NO EVENT SHALL THOMAS ROELL OR SGCS BE LIABLE FOR ANY | |
68 | * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER | |
69 | * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF | |
70 | * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |
71 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
72 | */ | |
73 | ||
74 | /* | |
75 | * Authors: Dirk Hohndel <hohndel@XFree86.Org> | |
76 | * David Dawes <dawes@XFree86.Org> | |
77 | * Marc La France <tsi@XFree86.Org> | |
78 | * ... and others | |
79 | * | |
80 | * This file includes helper functions for mode related things. | |
81 | */ | |
82 | ||
83 | #ifdef HAVE_XORG_CONFIG_H | |
84 | #include <xorg-config.h> | |
85 | #endif | |
86 | ||
87 | #include <X11/X.h> | |
88 | #include "xf86Modes.h" | |
89 | #include "os.h" | |
90 | #include "servermd.h" | |
91 | #include "globals.h" | |
92 | #include "xf86.h" | |
93 | #include "xf86Priv.h" | |
94 | #include "edid.h" | |
95 | ||
96 | static void | |
97 | printModeRejectMessage(int index, DisplayModePtr p, int status) | |
98 | { | |
99 | const char *type; | |
100 | ||
101 | if (p->type & M_T_BUILTIN) | |
102 | type = "built-in "; | |
103 | else if (p->type & M_T_DEFAULT) | |
104 | type = "default "; | |
105 | else if (p->type & M_T_DRIVER) | |
106 | type = "driver "; | |
107 | else | |
108 | type = ""; | |
109 | ||
110 | xf86DrvMsg(index, X_INFO, "Not using %smode \"%s\" (%s)\n", type, p->name, | |
111 | xf86ModeStatusToString(status)); | |
112 | } | |
113 | ||
114 | /* | |
115 | * xf86GetNearestClock -- | |
116 | * Find closest clock to given frequency (in kHz). This assumes the | |
117 | * number of clocks is greater than zero. | |
118 | */ | |
119 | int | |
120 | xf86GetNearestClock(ScrnInfoPtr scrp, int freq, Bool allowDiv2, | |
121 | int DivFactor, int MulFactor, int *divider) | |
122 | { | |
123 | int nearestClock = 0, nearestDiv = 1; | |
124 | int minimumGap = abs(freq - scrp->clock[0]); | |
125 | int i, j, k, gap; | |
126 | ||
127 | if (allowDiv2) | |
128 | k = 2; | |
129 | else | |
130 | k = 1; | |
131 | ||
132 | /* Must set this here in case the best match is scrp->clock[0] */ | |
133 | if (divider != NULL) | |
134 | *divider = 0; | |
135 | ||
136 | for (i = 0; i < scrp->numClocks; i++) { | |
137 | for (j = 1; j <= k; j++) { | |
138 | gap = abs((freq * j) - ((scrp->clock[i] * DivFactor) / MulFactor)); | |
139 | if ((gap < minimumGap) || ((gap == minimumGap) && (j < nearestDiv))) { | |
140 | minimumGap = gap; | |
141 | nearestClock = i; | |
142 | nearestDiv = j; | |
143 | if (divider != NULL) | |
144 | *divider = (j - 1) * V_CLKDIV2; | |
145 | } | |
146 | } | |
147 | } | |
148 | return nearestClock; | |
149 | } | |
150 | ||
151 | /* | |
152 | * xf86ModeStatusToString | |
153 | * | |
154 | * Convert a ModeStatus value to a printable message | |
155 | */ | |
156 | ||
157 | const char * | |
158 | xf86ModeStatusToString(ModeStatus status) | |
159 | { | |
160 | switch (status) { | |
161 | case MODE_OK: | |
162 | return "Mode OK"; | |
163 | case MODE_HSYNC: | |
164 | return "hsync out of range"; | |
165 | case MODE_VSYNC: | |
166 | return "vrefresh out of range"; | |
167 | case MODE_H_ILLEGAL: | |
168 | return "illegal horizontal timings"; | |
169 | case MODE_V_ILLEGAL: | |
170 | return "illegal vertical timings"; | |
171 | case MODE_BAD_WIDTH: | |
172 | return "width requires unsupported line pitch"; | |
173 | case MODE_NOMODE: | |
174 | return "no mode of this name"; | |
175 | case MODE_NO_INTERLACE: | |
176 | return "interlace mode not supported"; | |
177 | case MODE_NO_DBLESCAN: | |
178 | return "doublescan mode not supported"; | |
179 | case MODE_NO_VSCAN: | |
180 | return "multiscan mode not supported"; | |
181 | case MODE_MEM: | |
182 | return "insufficient memory for mode"; | |
183 | case MODE_VIRTUAL_X: | |
184 | return "width too large for virtual size"; | |
185 | case MODE_VIRTUAL_Y: | |
186 | return "height too large for virtual size"; | |
187 | case MODE_MEM_VIRT: | |
188 | return "insufficient memory given virtual size"; | |
189 | case MODE_NOCLOCK: | |
190 | return "no clock available for mode"; | |
191 | case MODE_CLOCK_HIGH: | |
192 | return "mode clock too high"; | |
193 | case MODE_CLOCK_LOW: | |
194 | return "mode clock too low"; | |
195 | case MODE_CLOCK_RANGE: | |
196 | return "bad mode clock/interlace/doublescan"; | |
197 | case MODE_BAD_HVALUE: | |
198 | return "horizontal timing out of range"; | |
199 | case MODE_BAD_VVALUE: | |
200 | return "vertical timing out of range"; | |
201 | case MODE_BAD_VSCAN: | |
202 | return "VScan value out of range"; | |
203 | case MODE_HSYNC_NARROW: | |
204 | return "horizontal sync too narrow"; | |
205 | case MODE_HSYNC_WIDE: | |
206 | return "horizontal sync too wide"; | |
207 | case MODE_HBLANK_NARROW: | |
208 | return "horizontal blanking too narrow"; | |
209 | case MODE_HBLANK_WIDE: | |
210 | return "horizontal blanking too wide"; | |
211 | case MODE_VSYNC_NARROW: | |
212 | return "vertical sync too narrow"; | |
213 | case MODE_VSYNC_WIDE: | |
214 | return "vertical sync too wide"; | |
215 | case MODE_VBLANK_NARROW: | |
216 | return "vertical blanking too narrow"; | |
217 | case MODE_VBLANK_WIDE: | |
218 | return "vertical blanking too wide"; | |
219 | case MODE_PANEL: | |
220 | return "exceeds panel dimensions"; | |
221 | case MODE_INTERLACE_WIDTH: | |
222 | return "width too large for interlaced mode"; | |
223 | case MODE_ONE_WIDTH: | |
224 | return "all modes must have the same width"; | |
225 | case MODE_ONE_HEIGHT: | |
226 | return "all modes must have the same height"; | |
227 | case MODE_ONE_SIZE: | |
228 | return "all modes must have the same resolution"; | |
229 | case MODE_NO_REDUCED: | |
230 | return "monitor doesn't support reduced blanking"; | |
231 | case MODE_BANDWIDTH: | |
232 | return "mode requires too much memory bandwidth"; | |
233 | case MODE_BAD: | |
234 | return "unknown reason"; | |
235 | case MODE_ERROR: | |
236 | return "internal error"; | |
237 | default: | |
238 | return "unknown"; | |
239 | } | |
240 | } | |
241 | ||
242 | /* | |
243 | * xf86ShowClockRanges() -- Print the clock ranges allowed | |
244 | * and the clock values scaled by ClockMulFactor and ClockDivFactor | |
245 | */ | |
246 | void | |
247 | xf86ShowClockRanges(ScrnInfoPtr scrp, ClockRangePtr clockRanges) | |
248 | { | |
249 | ClockRangePtr cp; | |
250 | int MulFactor = 1; | |
251 | int DivFactor = 1; | |
252 | int i, j; | |
253 | int scaledClock; | |
254 | ||
255 | for (cp = clockRanges; cp != NULL; cp = cp->next) { | |
256 | DivFactor = max(1, cp->ClockDivFactor); | |
257 | MulFactor = max(1, cp->ClockMulFactor); | |
258 | if (scrp->progClock) { | |
259 | if (cp->minClock) { | |
260 | if (cp->maxClock) { | |
261 | xf86DrvMsg(scrp->scrnIndex, X_INFO, | |
262 | "Clock range: %6.2f to %6.2f MHz\n", | |
263 | (double) cp->minClock / 1000.0, | |
264 | (double) cp->maxClock / 1000.0); | |
265 | } | |
266 | else { | |
267 | xf86DrvMsg(scrp->scrnIndex, X_INFO, | |
268 | "Minimum clock: %6.2f MHz\n", | |
269 | (double) cp->minClock / 1000.0); | |
270 | } | |
271 | } | |
272 | else { | |
273 | if (cp->maxClock) { | |
274 | xf86DrvMsg(scrp->scrnIndex, X_INFO, | |
275 | "Maximum clock: %6.2f MHz\n", | |
276 | (double) cp->maxClock / 1000.0); | |
277 | } | |
278 | } | |
279 | } | |
280 | else if (DivFactor > 1 || MulFactor > 1) { | |
281 | j = 0; | |
282 | for (i = 0; i < scrp->numClocks; i++) { | |
283 | scaledClock = (scrp->clock[i] * DivFactor) / MulFactor; | |
284 | if (scaledClock >= cp->minClock && scaledClock <= cp->maxClock) { | |
285 | if ((j % 8) == 0) { | |
286 | if (j > 0) | |
287 | xf86ErrorF("\n"); | |
288 | xf86DrvMsg(scrp->scrnIndex, X_INFO, "scaled clocks:"); | |
289 | } | |
290 | xf86ErrorF(" %6.2f", (double) scaledClock / 1000.0); | |
291 | j++; | |
292 | } | |
293 | } | |
294 | xf86ErrorF("\n"); | |
295 | } | |
296 | } | |
297 | } | |
298 | ||
299 | static Bool | |
300 | modeInClockRange(ClockRangePtr cp, DisplayModePtr p) | |
301 | { | |
302 | return ((p->Clock >= cp->minClock) && | |
303 | (p->Clock <= cp->maxClock) && | |
304 | (cp->interlaceAllowed || !(p->Flags & V_INTERLACE)) && | |
305 | (cp->doubleScanAllowed || | |
306 | ((p->VScan <= 1) && !(p->Flags & V_DBLSCAN)))); | |
307 | } | |
308 | ||
309 | /* | |
310 | * xf86FindClockRangeForMode() [... like the name says ...] | |
311 | */ | |
312 | static ClockRangePtr | |
313 | xf86FindClockRangeForMode(ClockRangePtr clockRanges, DisplayModePtr p) | |
314 | { | |
315 | ClockRangePtr cp; | |
316 | ||
317 | for (cp = clockRanges;; cp = cp->next) | |
318 | if (!cp || modeInClockRange(cp, p)) | |
319 | return cp; | |
320 | } | |
321 | ||
322 | /* | |
323 | * xf86HandleBuiltinMode() - handles built-in modes | |
324 | */ | |
325 | static ModeStatus | |
326 | xf86HandleBuiltinMode(ScrnInfoPtr scrp, | |
327 | DisplayModePtr p, | |
328 | DisplayModePtr modep, | |
329 | ClockRangePtr clockRanges, Bool allowDiv2) | |
330 | { | |
331 | ClockRangePtr cp; | |
332 | int extraFlags = 0; | |
333 | int MulFactor = 1; | |
334 | int DivFactor = 1; | |
335 | int clockIndex; | |
336 | ||
337 | /* Reject previously rejected modes */ | |
338 | if (p->status != MODE_OK) | |
339 | return p->status; | |
340 | ||
341 | /* Reject previously considered modes */ | |
342 | if (p->prev) | |
343 | return MODE_NOMODE; | |
344 | ||
345 | if ((p->type & M_T_CLOCK_C) == M_T_CLOCK_C) { | |
346 | /* Check clock is in range */ | |
347 | cp = xf86FindClockRangeForMode(clockRanges, p); | |
348 | if (cp == NULL) { | |
349 | modep->type = p->type; | |
350 | p->status = MODE_CLOCK_RANGE; | |
351 | return MODE_CLOCK_RANGE; | |
352 | } | |
353 | DivFactor = cp->ClockDivFactor; | |
354 | MulFactor = cp->ClockMulFactor; | |
355 | if (!scrp->progClock) { | |
356 | clockIndex = xf86GetNearestClock(scrp, p->Clock, allowDiv2, | |
357 | cp->ClockDivFactor, | |
358 | cp->ClockMulFactor, &extraFlags); | |
359 | modep->Clock = (scrp->clock[clockIndex] * DivFactor) | |
360 | / MulFactor; | |
361 | modep->ClockIndex = clockIndex; | |
362 | modep->SynthClock = scrp->clock[clockIndex]; | |
363 | if (extraFlags & V_CLKDIV2) { | |
364 | modep->Clock /= 2; | |
365 | modep->SynthClock /= 2; | |
366 | } | |
367 | } | |
368 | else { | |
369 | modep->Clock = p->Clock; | |
370 | modep->ClockIndex = -1; | |
371 | modep->SynthClock = (modep->Clock * MulFactor) | |
372 | / DivFactor; | |
373 | } | |
374 | modep->PrivFlags = cp->PrivFlags; | |
375 | } | |
376 | else { | |
377 | if (!scrp->progClock) { | |
378 | modep->Clock = p->Clock; | |
379 | modep->ClockIndex = p->ClockIndex; | |
380 | modep->SynthClock = p->SynthClock; | |
381 | } | |
382 | else { | |
383 | modep->Clock = p->Clock; | |
384 | modep->ClockIndex = -1; | |
385 | modep->SynthClock = p->SynthClock; | |
386 | } | |
387 | modep->PrivFlags = p->PrivFlags; | |
388 | } | |
389 | modep->type = p->type; | |
390 | modep->HDisplay = p->HDisplay; | |
391 | modep->HSyncStart = p->HSyncStart; | |
392 | modep->HSyncEnd = p->HSyncEnd; | |
393 | modep->HTotal = p->HTotal; | |
394 | modep->HSkew = p->HSkew; | |
395 | modep->VDisplay = p->VDisplay; | |
396 | modep->VSyncStart = p->VSyncStart; | |
397 | modep->VSyncEnd = p->VSyncEnd; | |
398 | modep->VTotal = p->VTotal; | |
399 | modep->VScan = p->VScan; | |
400 | modep->Flags = p->Flags | extraFlags; | |
401 | modep->CrtcHDisplay = p->CrtcHDisplay; | |
402 | modep->CrtcHBlankStart = p->CrtcHBlankStart; | |
403 | modep->CrtcHSyncStart = p->CrtcHSyncStart; | |
404 | modep->CrtcHSyncEnd = p->CrtcHSyncEnd; | |
405 | modep->CrtcHBlankEnd = p->CrtcHBlankEnd; | |
406 | modep->CrtcHTotal = p->CrtcHTotal; | |
407 | modep->CrtcHSkew = p->CrtcHSkew; | |
408 | modep->CrtcVDisplay = p->CrtcVDisplay; | |
409 | modep->CrtcVBlankStart = p->CrtcVBlankStart; | |
410 | modep->CrtcVSyncStart = p->CrtcVSyncStart; | |
411 | modep->CrtcVSyncEnd = p->CrtcVSyncEnd; | |
412 | modep->CrtcVBlankEnd = p->CrtcVBlankEnd; | |
413 | modep->CrtcVTotal = p->CrtcVTotal; | |
414 | modep->CrtcHAdjusted = p->CrtcHAdjusted; | |
415 | modep->CrtcVAdjusted = p->CrtcVAdjusted; | |
416 | modep->HSync = p->HSync; | |
417 | modep->VRefresh = p->VRefresh; | |
418 | modep->Private = p->Private; | |
419 | modep->PrivSize = p->PrivSize; | |
420 | ||
421 | p->prev = modep; | |
422 | ||
423 | return MODE_OK; | |
424 | } | |
425 | ||
426 | /* | |
427 | * xf86LookupMode | |
428 | * | |
429 | * This function returns a mode from the given list which matches the | |
430 | * given name. When multiple modes with the same name are available, | |
431 | * the method of picking the matching mode is determined by the | |
432 | * strategy selected. | |
433 | * | |
434 | * This function takes the following parameters: | |
435 | * scrp ScrnInfoPtr | |
436 | * modep pointer to the returned mode, which must have the name | |
437 | * field filled in. | |
438 | * clockRanges a list of clock ranges. This is optional when all the | |
439 | * modes are built-in modes. | |
440 | * strategy how to decide which mode to use from multiple modes with | |
441 | * the same name | |
442 | * | |
443 | * In addition, the following fields from the ScrnInfoRec are used: | |
444 | * modePool the list of monitor modes compatible with the driver | |
445 | * clocks a list of discrete clocks | |
446 | * numClocks number of discrete clocks | |
447 | * progClock clock is programmable | |
448 | * | |
449 | * If a mode was found, its values are filled in to the area pointed to | |
450 | * by modep, If a mode was not found the return value indicates the | |
451 | * reason. | |
452 | */ | |
453 | ||
454 | ModeStatus | |
455 | xf86LookupMode(ScrnInfoPtr scrp, DisplayModePtr modep, | |
456 | ClockRangePtr clockRanges, LookupModeFlags strategy) | |
457 | { | |
458 | DisplayModePtr p, bestMode = NULL; | |
459 | ClockRangePtr cp; | |
460 | int i, k, gap, minimumGap = CLOCK_TOLERANCE + 1; | |
461 | double refresh, bestRefresh = 0.0; | |
462 | Bool found = FALSE; | |
463 | int extraFlags = 0; | |
464 | int clockIndex = -1; | |
465 | int MulFactor = 1; | |
466 | int DivFactor = 1; | |
467 | int ModePrivFlags = 0; | |
468 | ModeStatus status = MODE_NOMODE; | |
469 | Bool allowDiv2 = (strategy & LOOKUP_CLKDIV2) != 0; | |
470 | int n; | |
471 | ||
472 | const int types[] = { | |
473 | M_T_BUILTIN | M_T_PREFERRED, | |
474 | M_T_BUILTIN, | |
475 | M_T_USERDEF | M_T_PREFERRED, | |
476 | M_T_USERDEF, | |
477 | M_T_DRIVER | M_T_PREFERRED, | |
478 | M_T_DRIVER, | |
479 | 0 | |
480 | }; | |
481 | const int ntypes = sizeof(types) / sizeof(int); | |
482 | ||
483 | strategy &= ~(LOOKUP_CLKDIV2 | LOOKUP_OPTIONAL_TOLERANCES); | |
484 | ||
485 | /* Some sanity checking */ | |
486 | if (scrp == NULL || scrp->modePool == NULL || | |
487 | (!scrp->progClock && scrp->numClocks == 0)) { | |
488 | ErrorF("xf86LookupMode: called with invalid scrnInfoRec\n"); | |
489 | return MODE_ERROR; | |
490 | } | |
491 | if (modep == NULL || modep->name == NULL) { | |
492 | ErrorF("xf86LookupMode: called with invalid modep\n"); | |
493 | return MODE_ERROR; | |
494 | } | |
495 | for (cp = clockRanges; cp != NULL; cp = cp->next) { | |
496 | /* DivFactor and MulFactor must be > 0 */ | |
497 | cp->ClockDivFactor = max(1, cp->ClockDivFactor); | |
498 | cp->ClockMulFactor = max(1, cp->ClockMulFactor); | |
499 | } | |
500 | ||
501 | /* Scan the mode pool for matching names */ | |
502 | for (n = 0; n < ntypes; n++) { | |
503 | int type = types[n]; | |
504 | ||
505 | for (p = scrp->modePool; p != NULL; p = p->next) { | |
506 | ||
507 | /* scan through the modes in the sort order above */ | |
508 | if ((p->type & type) != type) | |
509 | continue; | |
510 | ||
511 | if (strcmp(p->name, modep->name) == 0) { | |
512 | ||
513 | /* Skip over previously rejected modes */ | |
514 | if (p->status != MODE_OK) { | |
515 | if (!found) | |
516 | status = p->status; | |
517 | continue; | |
518 | } | |
519 | ||
520 | /* Skip over previously considered modes */ | |
521 | if (p->prev) | |
522 | continue; | |
523 | ||
524 | if (p->type & M_T_BUILTIN) { | |
525 | return xf86HandleBuiltinMode(scrp, p, modep, clockRanges, | |
526 | allowDiv2); | |
527 | } | |
528 | ||
529 | /* Check clock is in range */ | |
530 | cp = xf86FindClockRangeForMode(clockRanges, p); | |
531 | if (cp == NULL) { | |
532 | /* | |
533 | * XXX Could do more here to provide a more detailed | |
534 | * reason for not finding a mode. | |
535 | */ | |
536 | p->status = MODE_CLOCK_RANGE; | |
537 | if (!found) | |
538 | status = MODE_CLOCK_RANGE; | |
539 | continue; | |
540 | } | |
541 | ||
542 | /* | |
543 | * If programmable clock and strategy is not | |
544 | * LOOKUP_BEST_REFRESH, the required mode has been found, | |
545 | * otherwise record the refresh and continue looking. | |
546 | */ | |
547 | if (scrp->progClock) { | |
548 | found = TRUE; | |
549 | if (strategy != LOOKUP_BEST_REFRESH) { | |
550 | bestMode = p; | |
551 | DivFactor = cp->ClockDivFactor; | |
552 | MulFactor = cp->ClockMulFactor; | |
553 | ModePrivFlags = cp->PrivFlags; | |
554 | break; | |
555 | } | |
556 | refresh = xf86ModeVRefresh(p); | |
557 | if (p->Flags & V_INTERLACE) | |
558 | refresh /= INTERLACE_REFRESH_WEIGHT; | |
559 | if (refresh > bestRefresh) { | |
560 | bestMode = p; | |
561 | DivFactor = cp->ClockDivFactor; | |
562 | MulFactor = cp->ClockMulFactor; | |
563 | ModePrivFlags = cp->PrivFlags; | |
564 | bestRefresh = refresh; | |
565 | } | |
566 | continue; | |
567 | } | |
568 | ||
569 | /* | |
570 | * Clock is in range, so if it is not a programmable clock, find | |
571 | * a matching clock. | |
572 | */ | |
573 | ||
574 | i = xf86GetNearestClock(scrp, p->Clock, allowDiv2, | |
575 | cp->ClockDivFactor, cp->ClockMulFactor, | |
576 | &k); | |
577 | /* | |
578 | * If the clock is too far from the requested clock, this | |
579 | * mode is no good. | |
580 | */ | |
581 | if (k & V_CLKDIV2) | |
582 | gap = abs((p->Clock * 2) - | |
583 | ((scrp->clock[i] * cp->ClockDivFactor) / | |
584 | cp->ClockMulFactor)); | |
585 | else | |
586 | gap = abs(p->Clock - | |
587 | ((scrp->clock[i] * cp->ClockDivFactor) / | |
588 | cp->ClockMulFactor)); | |
589 | if (gap > minimumGap) { | |
590 | p->status = MODE_NOCLOCK; | |
591 | if (!found) | |
592 | status = MODE_NOCLOCK; | |
593 | continue; | |
594 | } | |
595 | found = TRUE; | |
596 | ||
597 | if (strategy == LOOKUP_BEST_REFRESH) { | |
598 | refresh = xf86ModeVRefresh(p); | |
599 | if (p->Flags & V_INTERLACE) | |
600 | refresh /= INTERLACE_REFRESH_WEIGHT; | |
601 | if (refresh > bestRefresh) { | |
602 | bestMode = p; | |
603 | DivFactor = cp->ClockDivFactor; | |
604 | MulFactor = cp->ClockMulFactor; | |
605 | ModePrivFlags = cp->PrivFlags; | |
606 | extraFlags = k; | |
607 | clockIndex = i; | |
608 | bestRefresh = refresh; | |
609 | } | |
610 | continue; | |
611 | } | |
612 | if (strategy == LOOKUP_CLOSEST_CLOCK) { | |
613 | if (gap < minimumGap) { | |
614 | bestMode = p; | |
615 | DivFactor = cp->ClockDivFactor; | |
616 | MulFactor = cp->ClockMulFactor; | |
617 | ModePrivFlags = cp->PrivFlags; | |
618 | extraFlags = k; | |
619 | clockIndex = i; | |
620 | minimumGap = gap; | |
621 | } | |
622 | continue; | |
623 | } | |
624 | /* | |
625 | * If strategy is neither LOOKUP_BEST_REFRESH or | |
626 | * LOOKUP_CLOSEST_CLOCK the required mode has been found. | |
627 | */ | |
628 | bestMode = p; | |
629 | DivFactor = cp->ClockDivFactor; | |
630 | MulFactor = cp->ClockMulFactor; | |
631 | ModePrivFlags = cp->PrivFlags; | |
632 | extraFlags = k; | |
633 | clockIndex = i; | |
634 | break; | |
635 | } | |
636 | } | |
637 | if (found) | |
638 | break; | |
639 | } | |
640 | if (!found || bestMode == NULL) | |
641 | return status; | |
642 | ||
643 | /* Fill in the mode parameters */ | |
644 | if (scrp->progClock) { | |
645 | modep->Clock = bestMode->Clock; | |
646 | modep->ClockIndex = -1; | |
647 | modep->SynthClock = (modep->Clock * MulFactor) / DivFactor; | |
648 | } | |
649 | else { | |
650 | modep->Clock = (scrp->clock[clockIndex] * DivFactor) / MulFactor; | |
651 | modep->ClockIndex = clockIndex; | |
652 | modep->SynthClock = scrp->clock[clockIndex]; | |
653 | if (extraFlags & V_CLKDIV2) { | |
654 | modep->Clock /= 2; | |
655 | modep->SynthClock /= 2; | |
656 | } | |
657 | } | |
658 | modep->type = bestMode->type; | |
659 | modep->PrivFlags = ModePrivFlags; | |
660 | modep->HDisplay = bestMode->HDisplay; | |
661 | modep->HSyncStart = bestMode->HSyncStart; | |
662 | modep->HSyncEnd = bestMode->HSyncEnd; | |
663 | modep->HTotal = bestMode->HTotal; | |
664 | modep->HSkew = bestMode->HSkew; | |
665 | modep->VDisplay = bestMode->VDisplay; | |
666 | modep->VSyncStart = bestMode->VSyncStart; | |
667 | modep->VSyncEnd = bestMode->VSyncEnd; | |
668 | modep->VTotal = bestMode->VTotal; | |
669 | modep->VScan = bestMode->VScan; | |
670 | modep->Flags = bestMode->Flags | extraFlags; | |
671 | modep->CrtcHDisplay = bestMode->CrtcHDisplay; | |
672 | modep->CrtcHBlankStart = bestMode->CrtcHBlankStart; | |
673 | modep->CrtcHSyncStart = bestMode->CrtcHSyncStart; | |
674 | modep->CrtcHSyncEnd = bestMode->CrtcHSyncEnd; | |
675 | modep->CrtcHBlankEnd = bestMode->CrtcHBlankEnd; | |
676 | modep->CrtcHTotal = bestMode->CrtcHTotal; | |
677 | modep->CrtcHSkew = bestMode->CrtcHSkew; | |
678 | modep->CrtcVDisplay = bestMode->CrtcVDisplay; | |
679 | modep->CrtcVBlankStart = bestMode->CrtcVBlankStart; | |
680 | modep->CrtcVSyncStart = bestMode->CrtcVSyncStart; | |
681 | modep->CrtcVSyncEnd = bestMode->CrtcVSyncEnd; | |
682 | modep->CrtcVBlankEnd = bestMode->CrtcVBlankEnd; | |
683 | modep->CrtcVTotal = bestMode->CrtcVTotal; | |
684 | modep->CrtcHAdjusted = bestMode->CrtcHAdjusted; | |
685 | modep->CrtcVAdjusted = bestMode->CrtcVAdjusted; | |
686 | modep->HSync = bestMode->HSync; | |
687 | modep->VRefresh = bestMode->VRefresh; | |
688 | modep->Private = bestMode->Private; | |
689 | modep->PrivSize = bestMode->PrivSize; | |
690 | ||
691 | bestMode->prev = modep; | |
692 | ||
693 | return MODE_OK; | |
694 | } | |
695 | ||
696 | /* | |
697 | * xf86CheckModeForMonitor | |
698 | * | |
699 | * This function takes a mode and monitor description, and determines | |
700 | * if the mode is valid for the monitor. | |
701 | */ | |
702 | ModeStatus | |
703 | xf86CheckModeForMonitor(DisplayModePtr mode, MonPtr monitor) | |
704 | { | |
705 | int i; | |
706 | ||
707 | /* Sanity checks */ | |
708 | if (mode == NULL || monitor == NULL) { | |
709 | ErrorF("xf86CheckModeForMonitor: called with invalid parameters\n"); | |
710 | return MODE_ERROR; | |
711 | } | |
712 | ||
713 | DebugF("xf86CheckModeForMonitor(%p %s, %p %s)\n", | |
714 | mode, mode->name, monitor, monitor->id); | |
715 | ||
716 | /* Some basic mode validity checks */ | |
717 | if (0 >= mode->HDisplay || mode->HDisplay > mode->HSyncStart || | |
718 | mode->HSyncStart >= mode->HSyncEnd || mode->HSyncEnd >= mode->HTotal) | |
719 | return MODE_H_ILLEGAL; | |
720 | ||
721 | if (0 >= mode->VDisplay || mode->VDisplay > mode->VSyncStart || | |
722 | mode->VSyncStart >= mode->VSyncEnd || mode->VSyncEnd >= mode->VTotal) | |
723 | return MODE_V_ILLEGAL; | |
724 | ||
725 | if (monitor->nHsync > 0) { | |
726 | /* Check hsync against the allowed ranges */ | |
727 | float hsync = xf86ModeHSync(mode); | |
728 | ||
729 | for (i = 0; i < monitor->nHsync; i++) | |
730 | if ((hsync > monitor->hsync[i].lo * (1.0 - SYNC_TOLERANCE)) && | |
731 | (hsync < monitor->hsync[i].hi * (1.0 + SYNC_TOLERANCE))) | |
732 | break; | |
733 | ||
734 | /* Now see whether we ran out of sync ranges without finding a match */ | |
735 | if (i == monitor->nHsync) | |
736 | return MODE_HSYNC; | |
737 | } | |
738 | ||
739 | if (monitor->nVrefresh > 0) { | |
740 | /* Check vrefresh against the allowed ranges */ | |
741 | float vrefrsh = xf86ModeVRefresh(mode); | |
742 | ||
743 | for (i = 0; i < monitor->nVrefresh; i++) | |
744 | if ((vrefrsh > monitor->vrefresh[i].lo * (1.0 - SYNC_TOLERANCE)) && | |
745 | (vrefrsh < monitor->vrefresh[i].hi * (1.0 + SYNC_TOLERANCE))) | |
746 | break; | |
747 | ||
748 | /* Now see whether we ran out of refresh ranges without finding a match */ | |
749 | if (i == monitor->nVrefresh) | |
750 | return MODE_VSYNC; | |
751 | } | |
752 | ||
753 | /* Force interlaced modes to have an odd VTotal */ | |
754 | if (mode->Flags & V_INTERLACE) | |
755 | mode->CrtcVTotal = mode->VTotal |= 1; | |
756 | ||
757 | /* | |
758 | * This code stops cvt -r modes, and only cvt -r modes, from hitting 15y+ | |
759 | * old CRTs which might, when there is a lot of solar flare activity and | |
760 | * when the celestial bodies are unfavourably aligned, implode trying to | |
761 | * sync to it. It's called "Protecting the user from doing anything stupid". | |
762 | * -- libv | |
763 | */ | |
764 | ||
765 | if (xf86ModeIsReduced(mode)) { | |
766 | if (!monitor->reducedblanking && !(mode->type & M_T_DRIVER)) | |
767 | return MODE_NO_REDUCED; | |
768 | } | |
769 | ||
770 | if ((monitor->maxPixClock) && (mode->Clock > monitor->maxPixClock)) | |
771 | return MODE_CLOCK_HIGH; | |
772 | ||
773 | return MODE_OK; | |
774 | } | |
775 | ||
776 | /* | |
777 | * xf86CheckModeSize | |
778 | * | |
779 | * An internal routine to check if a mode fits in video memory. This tries to | |
780 | * avoid overflows that would otherwise occur when video memory size is greater | |
781 | * than 256MB. | |
782 | */ | |
783 | static Bool | |
784 | xf86CheckModeSize(ScrnInfoPtr scrp, int w, int x, int y) | |
785 | { | |
786 | int bpp = scrp->fbFormat.bitsPerPixel, pad = scrp->fbFormat.scanlinePad; | |
787 | int lineWidth, lastWidth; | |
788 | ||
789 | if (scrp->depth == 4) | |
790 | pad *= 4; /* 4 planes */ | |
791 | ||
792 | /* Sanity check */ | |
793 | if ((w < 0) || (x < 0) || (y <= 0)) | |
794 | return FALSE; | |
795 | ||
796 | lineWidth = (((w * bpp) + pad - 1) / pad) * pad; | |
797 | lastWidth = x * bpp; | |
798 | ||
799 | /* | |
800 | * At this point, we need to compare | |
801 | * | |
802 | * (lineWidth * (y - 1)) + lastWidth | |
803 | * | |
804 | * against | |
805 | * | |
806 | * scrp->videoRam * (1024 * 8) | |
807 | * | |
808 | * These are bit quantities. To avoid overflows, do the comparison in | |
809 | * terms of BITMAP_SCANLINE_PAD units. This assumes BITMAP_SCANLINE_PAD | |
810 | * is a power of 2. We currently use 32, which limits us to a video | |
811 | * memory size of 8GB. | |
812 | */ | |
813 | ||
814 | lineWidth = (lineWidth + (BITMAP_SCANLINE_PAD - 1)) / BITMAP_SCANLINE_PAD; | |
815 | lastWidth = (lastWidth + (BITMAP_SCANLINE_PAD - 1)) / BITMAP_SCANLINE_PAD; | |
816 | ||
817 | if ((lineWidth * (y - 1) + lastWidth) > | |
818 | (scrp->videoRam * ((1024 * 8) / BITMAP_SCANLINE_PAD))) | |
819 | return FALSE; | |
820 | ||
821 | return TRUE; | |
822 | } | |
823 | ||
824 | /* | |
825 | * xf86InitialCheckModeForDriver | |
826 | * | |
827 | * This function checks if a mode satisfies a driver's initial requirements: | |
828 | * - mode size fits within the available pixel area (memory) | |
829 | * - width lies within the range of supported line pitches | |
830 | * - mode size fits within virtual size (if fixed) | |
831 | * - horizontal timings are in range | |
832 | * | |
833 | * This function takes the following parameters: | |
834 | * scrp ScrnInfoPtr | |
835 | * mode mode to check | |
836 | * maxPitch (optional) maximum line pitch | |
837 | * virtualX (optional) virtual width requested | |
838 | * virtualY (optional) virtual height requested | |
839 | * | |
840 | * In addition, the following fields from the ScrnInfoRec are used: | |
841 | * monitor pointer to structure for monitor section | |
842 | * fbFormat pixel format for the framebuffer | |
843 | * videoRam video memory size (in kB) | |
844 | * maxHValue maximum horizontal timing value | |
845 | * maxVValue maximum vertical timing value | |
846 | */ | |
847 | ||
848 | ModeStatus | |
849 | xf86InitialCheckModeForDriver(ScrnInfoPtr scrp, DisplayModePtr mode, | |
850 | ClockRangePtr clockRanges, | |
851 | LookupModeFlags strategy, | |
852 | int maxPitch, int virtualX, int virtualY) | |
853 | { | |
854 | ClockRangePtr cp; | |
855 | ModeStatus status; | |
856 | Bool allowDiv2 = (strategy & LOOKUP_CLKDIV2) != 0; | |
857 | int i, needDiv2; | |
858 | ||
859 | /* Sanity checks */ | |
860 | if (!scrp || !mode || !clockRanges) { | |
861 | ErrorF("xf86InitialCheckModeForDriver: " | |
862 | "called with invalid parameters\n"); | |
863 | return MODE_ERROR; | |
864 | } | |
865 | ||
866 | DebugF("xf86InitialCheckModeForDriver(%p, %p %s, %p, 0x%x, %d, %d, %d)\n", | |
867 | scrp, mode, mode->name, clockRanges, strategy, maxPitch, virtualX, | |
868 | virtualY); | |
869 | ||
870 | /* Some basic mode validity checks */ | |
871 | if (0 >= mode->HDisplay || mode->HDisplay > mode->HSyncStart || | |
872 | mode->HSyncStart >= mode->HSyncEnd || mode->HSyncEnd >= mode->HTotal) | |
873 | return MODE_H_ILLEGAL; | |
874 | ||
875 | if (0 >= mode->VDisplay || mode->VDisplay > mode->VSyncStart || | |
876 | mode->VSyncStart >= mode->VSyncEnd || mode->VSyncEnd >= mode->VTotal) | |
877 | return MODE_V_ILLEGAL; | |
878 | ||
879 | if (!xf86CheckModeSize(scrp, mode->HDisplay, mode->HDisplay, | |
880 | mode->VDisplay)) | |
881 | return MODE_MEM; | |
882 | ||
883 | if (maxPitch > 0 && mode->HDisplay > maxPitch) | |
884 | return MODE_BAD_WIDTH; | |
885 | ||
886 | if (virtualX > 0 && mode->HDisplay > virtualX) | |
887 | return MODE_VIRTUAL_X; | |
888 | ||
889 | if (virtualY > 0 && mode->VDisplay > virtualY) | |
890 | return MODE_VIRTUAL_Y; | |
891 | ||
892 | if (scrp->maxHValue > 0 && mode->HTotal > scrp->maxHValue) | |
893 | return MODE_BAD_HVALUE; | |
894 | ||
895 | if (scrp->maxVValue > 0 && mode->VTotal > scrp->maxVValue) | |
896 | return MODE_BAD_VVALUE; | |
897 | ||
898 | /* | |
899 | * The use of the DisplayModeRec's Crtc* and SynthClock elements below is | |
900 | * provisional, in that they are later reused by the driver at mode-set | |
901 | * time. Here, they are temporarily enlisted to contain the mode timings | |
902 | * as seen by the CRT or panel (rather than the CRTC). The driver's | |
903 | * ValidMode() is allowed to modify these so it can deal with such things | |
904 | * as mode stretching and/or centering. The driver should >NOT< modify the | |
905 | * user-supplied values as these are reported back when mode validation is | |
906 | * said and done. | |
907 | */ | |
908 | /* | |
909 | * NOTE: We (ab)use the mode->Crtc* values here to store timing | |
910 | * information for the calculation of Hsync and Vrefresh. Before | |
911 | * these values are calculated the driver is given the opportunity | |
912 | * to either set these HSync and VRefresh itself or modify the timing | |
913 | * values. | |
914 | * The difference to the final calculation is small but imortand: | |
915 | * here we pass the flag INTERLACE_HALVE_V regardless if the driver | |
916 | * sets it or not. This way our calculation of VRefresh has the same | |
917 | * effect as if we do if (flags & V_INTERLACE) refresh *= 2.0 | |
918 | * This dual use of the mode->Crtc* values will certainly create | |
919 | * confusion and is bad software design. However since it's part of | |
920 | * the driver API it's hard to change. | |
921 | */ | |
922 | ||
923 | if (scrp->ValidMode) { | |
924 | ||
925 | xf86SetModeCrtc(mode, INTERLACE_HALVE_V); | |
926 | ||
927 | cp = xf86FindClockRangeForMode(clockRanges, mode); | |
928 | if (!cp) | |
929 | return MODE_CLOCK_RANGE; | |
930 | ||
931 | if (cp->ClockMulFactor < 1) | |
932 | cp->ClockMulFactor = 1; | |
933 | if (cp->ClockDivFactor < 1) | |
934 | cp->ClockDivFactor = 1; | |
935 | ||
936 | /* | |
937 | * XXX The effect of clock dividers and multipliers on the monitor's | |
938 | * pixel clock needs to be verified. | |
939 | */ | |
940 | if (scrp->progClock) { | |
941 | mode->SynthClock = mode->Clock; | |
942 | } | |
943 | else { | |
944 | i = xf86GetNearestClock(scrp, mode->Clock, allowDiv2, | |
945 | cp->ClockDivFactor, cp->ClockMulFactor, | |
946 | &needDiv2); | |
947 | mode->SynthClock = (scrp->clock[i] * cp->ClockDivFactor) / | |
948 | cp->ClockMulFactor; | |
949 | if (needDiv2 & V_CLKDIV2) | |
950 | mode->SynthClock /= 2; | |
951 | } | |
952 | ||
953 | status = (*scrp->ValidMode) (scrp, mode, FALSE, | |
954 | MODECHECK_INITIAL); | |
955 | if (status != MODE_OK) | |
956 | return status; | |
957 | ||
958 | if (mode->HSync <= 0.0) | |
959 | mode->HSync = (float) mode->SynthClock / (float) mode->CrtcHTotal; | |
960 | if (mode->VRefresh <= 0.0) | |
961 | mode->VRefresh = (mode->SynthClock * 1000.0) | |
962 | / (mode->CrtcHTotal * mode->CrtcVTotal); | |
963 | } | |
964 | ||
965 | mode->HSync = xf86ModeHSync(mode); | |
966 | mode->VRefresh = xf86ModeVRefresh(mode); | |
967 | ||
968 | /* Assume it is OK */ | |
969 | return MODE_OK; | |
970 | } | |
971 | ||
972 | /* | |
973 | * xf86CheckModeForDriver | |
974 | * | |
975 | * This function is for checking modes while the server is running (for | |
976 | * use mainly by the VidMode extension). | |
977 | * | |
978 | * This function checks if a mode satisfies a driver's requirements: | |
979 | * - width lies within the line pitch | |
980 | * - mode size fits within virtual size | |
981 | * - horizontal/vertical timings are in range | |
982 | * | |
983 | * This function takes the following parameters: | |
984 | * scrp ScrnInfoPtr | |
985 | * mode mode to check | |
986 | * flags not (currently) used | |
987 | * | |
988 | * In addition, the following fields from the ScrnInfoRec are used: | |
989 | * maxHValue maximum horizontal timing value | |
990 | * maxVValue maximum vertical timing value | |
991 | * virtualX virtual width | |
992 | * virtualY virtual height | |
993 | * clockRanges allowable clock ranges | |
994 | */ | |
995 | ||
996 | ModeStatus | |
997 | xf86CheckModeForDriver(ScrnInfoPtr scrp, DisplayModePtr mode, int flags) | |
998 | { | |
999 | ClockRangePtr cp; | |
1000 | int i, k, gap, minimumGap = CLOCK_TOLERANCE + 1; | |
1001 | int extraFlags = 0; | |
1002 | int clockIndex = -1; | |
1003 | int MulFactor = 1; | |
1004 | int DivFactor = 1; | |
1005 | int ModePrivFlags = 0; | |
1006 | ModeStatus status = MODE_NOMODE; | |
1007 | ||
1008 | /* Some sanity checking */ | |
1009 | if (scrp == NULL || (!scrp->progClock && scrp->numClocks == 0)) { | |
1010 | ErrorF("xf86CheckModeForDriver: called with invalid scrnInfoRec\n"); | |
1011 | return MODE_ERROR; | |
1012 | } | |
1013 | if (mode == NULL) { | |
1014 | ErrorF("xf86CheckModeForDriver: called with invalid modep\n"); | |
1015 | return MODE_ERROR; | |
1016 | } | |
1017 | ||
1018 | /* Check the mode size */ | |
1019 | if (mode->HDisplay > scrp->virtualX) | |
1020 | return MODE_VIRTUAL_X; | |
1021 | ||
1022 | if (mode->VDisplay > scrp->virtualY) | |
1023 | return MODE_VIRTUAL_Y; | |
1024 | ||
1025 | if (scrp->maxHValue > 0 && mode->HTotal > scrp->maxHValue) | |
1026 | return MODE_BAD_HVALUE; | |
1027 | ||
1028 | if (scrp->maxVValue > 0 && mode->VTotal > scrp->maxVValue) | |
1029 | return MODE_BAD_VVALUE; | |
1030 | ||
1031 | for (cp = scrp->clockRanges; cp != NULL; cp = cp->next) { | |
1032 | /* DivFactor and MulFactor must be > 0 */ | |
1033 | cp->ClockDivFactor = max(1, cp->ClockDivFactor); | |
1034 | cp->ClockMulFactor = max(1, cp->ClockMulFactor); | |
1035 | } | |
1036 | ||
1037 | if (scrp->progClock) { | |
1038 | /* Check clock is in range */ | |
1039 | for (cp = scrp->clockRanges; cp != NULL; cp = cp->next) { | |
1040 | if (modeInClockRange(cp, mode)) | |
1041 | break; | |
1042 | } | |
1043 | if (cp == NULL) { | |
1044 | return MODE_CLOCK_RANGE; | |
1045 | } | |
1046 | /* | |
1047 | * If programmable clock the required mode has been found | |
1048 | */ | |
1049 | DivFactor = cp->ClockDivFactor; | |
1050 | MulFactor = cp->ClockMulFactor; | |
1051 | ModePrivFlags = cp->PrivFlags; | |
1052 | } | |
1053 | else { | |
1054 | status = MODE_CLOCK_RANGE; | |
1055 | /* Check clock is in range */ | |
1056 | for (cp = scrp->clockRanges; cp != NULL; cp = cp->next) { | |
1057 | if (modeInClockRange(cp, mode)) { | |
1058 | /* | |
1059 | * Clock is in range, so if it is not a programmable clock, | |
1060 | * find a matching clock. | |
1061 | */ | |
1062 | ||
1063 | i = xf86GetNearestClock(scrp, mode->Clock, 0, | |
1064 | cp->ClockDivFactor, cp->ClockMulFactor, | |
1065 | &k); | |
1066 | /* | |
1067 | * If the clock is too far from the requested clock, this | |
1068 | * mode is no good. | |
1069 | */ | |
1070 | if (k & V_CLKDIV2) | |
1071 | gap = abs((mode->Clock * 2) - | |
1072 | ((scrp->clock[i] * cp->ClockDivFactor) / | |
1073 | cp->ClockMulFactor)); | |
1074 | else | |
1075 | gap = abs(mode->Clock - | |
1076 | ((scrp->clock[i] * cp->ClockDivFactor) / | |
1077 | cp->ClockMulFactor)); | |
1078 | if (gap > minimumGap) { | |
1079 | status = MODE_NOCLOCK; | |
1080 | continue; | |
1081 | } | |
1082 | ||
1083 | DivFactor = cp->ClockDivFactor; | |
1084 | MulFactor = cp->ClockMulFactor; | |
1085 | ModePrivFlags = cp->PrivFlags; | |
1086 | extraFlags = k; | |
1087 | clockIndex = i; | |
1088 | break; | |
1089 | } | |
1090 | } | |
1091 | if (cp == NULL) | |
1092 | return status; | |
1093 | } | |
1094 | ||
1095 | /* Fill in the mode parameters */ | |
1096 | if (scrp->progClock) { | |
1097 | mode->ClockIndex = -1; | |
1098 | mode->SynthClock = (mode->Clock * MulFactor) / DivFactor; | |
1099 | } | |
1100 | else { | |
1101 | mode->Clock = (scrp->clock[clockIndex] * DivFactor) / MulFactor; | |
1102 | mode->ClockIndex = clockIndex; | |
1103 | mode->SynthClock = scrp->clock[clockIndex]; | |
1104 | if (extraFlags & V_CLKDIV2) { | |
1105 | mode->Clock /= 2; | |
1106 | mode->SynthClock /= 2; | |
1107 | } | |
1108 | } | |
1109 | mode->PrivFlags = ModePrivFlags; | |
1110 | ||
1111 | return MODE_OK; | |
1112 | } | |
1113 | ||
1114 | static int | |
1115 | inferVirtualSize(ScrnInfoPtr scrp, DisplayModePtr modes, int *vx, int *vy) | |
1116 | { | |
1117 | float aspect = 0.0; | |
1118 | MonPtr mon = scrp->monitor; | |
1119 | xf86MonPtr DDC; | |
1120 | int x = 0, y = 0; | |
1121 | DisplayModePtr mode; | |
1122 | ||
1123 | if (!mon) | |
1124 | return 0; | |
1125 | DDC = mon->DDC; | |
1126 | ||
1127 | if (DDC && DDC->ver.revision >= 4) { | |
1128 | /* For 1.4, we might actually get native pixel format. How novel. */ | |
1129 | if (PREFERRED_TIMING_MODE(DDC->features.msc)) { | |
1130 | for (mode = modes; mode; mode = mode->next) { | |
1131 | if (mode->type & (M_T_DRIVER | M_T_PREFERRED)) { | |
1132 | x = mode->HDisplay; | |
1133 | y = mode->VDisplay; | |
1134 | goto found; | |
1135 | } | |
1136 | } | |
1137 | } | |
1138 | /* | |
1139 | * Even if we don't, we might get aspect ratio from extra CVT info | |
1140 | * or from the monitor size fields. TODO. | |
1141 | */ | |
1142 | } | |
1143 | ||
1144 | /* | |
1145 | * Technically this triggers if either is zero. That wasn't legal | |
1146 | * before EDID 1.4, but right now we'll get that wrong. TODO. | |
1147 | */ | |
1148 | if (!aspect) { | |
1149 | if (!mon->widthmm || !mon->heightmm) | |
1150 | aspect = 4.0 / 3.0; | |
1151 | else | |
1152 | aspect = (float) mon->widthmm / (float) mon->heightmm; | |
1153 | } | |
1154 | ||
1155 | /* find the largest M_T_DRIVER mode with that aspect ratio */ | |
1156 | for (mode = modes; mode; mode = mode->next) { | |
1157 | float mode_aspect, metaspect; | |
1158 | ||
1159 | if (!(mode->type & (M_T_DRIVER | M_T_USERDEF))) | |
1160 | continue; | |
1161 | mode_aspect = (float) mode->HDisplay / (float) mode->VDisplay; | |
1162 | metaspect = aspect / mode_aspect; | |
1163 | /* 5% slop or so, since we only get size in centimeters */ | |
1164 | if (fabs(1.0 - metaspect) < 0.05) { | |
1165 | if ((mode->HDisplay > x) && (mode->VDisplay > y)) { | |
1166 | x = mode->HDisplay; | |
1167 | y = mode->VDisplay; | |
1168 | } | |
1169 | } | |
1170 | } | |
1171 | ||
1172 | if (!x || !y) { | |
1173 | xf86DrvMsg(scrp->scrnIndex, X_WARNING, | |
1174 | "Unable to estimate virtual size\n"); | |
1175 | return 0; | |
1176 | } | |
1177 | ||
1178 | found: | |
1179 | *vx = x; | |
1180 | *vy = y; | |
1181 | ||
1182 | xf86DrvMsg(scrp->scrnIndex, X_INFO, | |
1183 | "Estimated virtual size for aspect ratio %.4f is %dx%d\n", | |
1184 | aspect, *vx, *vy); | |
1185 | ||
1186 | return 1; | |
1187 | } | |
1188 | ||
1189 | /* Least common multiple */ | |
1190 | static unsigned int | |
1191 | LCM(unsigned int x, unsigned int y) | |
1192 | { | |
1193 | unsigned int m = x, n = y, o; | |
1194 | ||
1195 | while ((o = m % n)) { | |
1196 | m = n; | |
1197 | n = o; | |
1198 | } | |
1199 | ||
1200 | return (x / n) * y; | |
1201 | } | |
1202 | ||
1203 | /* | |
1204 | * Given various screen attributes, determine the minimum scanline width such | |
1205 | * that each scanline is server and DDX padded and any pixels with imbedded | |
1206 | * bank boundaries are off-screen. This function returns -1 if such a width | |
1207 | * cannot exist. | |
1208 | */ | |
1209 | static int | |
1210 | scanLineWidth(unsigned int xsize, /* pixels */ | |
1211 | unsigned int ysize, /* pixels */ | |
1212 | unsigned int width, /* pixels */ | |
1213 | unsigned long BankSize, /* char's */ | |
1214 | PixmapFormatRec * pBankFormat, unsigned int nWidthUnit /* bits */ | |
1215 | ) | |
1216 | { | |
1217 | unsigned long nBitsPerBank, nBitsPerScanline, nBitsPerScanlinePadUnit; | |
1218 | unsigned long minBitsPerScanline, maxBitsPerScanline; | |
1219 | ||
1220 | /* Sanity checks */ | |
1221 | ||
1222 | if (!nWidthUnit || !pBankFormat) | |
1223 | return -1; | |
1224 | ||
1225 | nBitsPerBank = BankSize * 8; | |
1226 | if (nBitsPerBank % pBankFormat->scanlinePad) | |
1227 | return -1; | |
1228 | ||
1229 | if (xsize > width) | |
1230 | width = xsize; | |
1231 | nBitsPerScanlinePadUnit = LCM(pBankFormat->scanlinePad, nWidthUnit); | |
1232 | nBitsPerScanline = | |
1233 | (((width * pBankFormat->bitsPerPixel) + nBitsPerScanlinePadUnit - 1) / | |
1234 | nBitsPerScanlinePadUnit) * nBitsPerScanlinePadUnit; | |
1235 | width = nBitsPerScanline / pBankFormat->bitsPerPixel; | |
1236 | ||
1237 | if (!xsize || !(nBitsPerBank % pBankFormat->bitsPerPixel)) | |
1238 | return (int) width; | |
1239 | ||
1240 | /* | |
1241 | * Scanlines will be server-pad aligned at this point. They will also be | |
1242 | * a multiple of nWidthUnit bits long. Ensure that pixels with imbedded | |
1243 | * bank boundaries are off-screen. | |
1244 | * | |
1245 | * It seems reasonable to limit total frame buffer size to 1/16 of the | |
1246 | * theoretical maximum address space size. On a machine with 32-bit | |
1247 | * addresses (to 8-bit quantities) this turns out to be 256MB. Not only | |
1248 | * does this provide a simple limiting condition for the loops below, but | |
1249 | * it also prevents unsigned long wraparounds. | |
1250 | */ | |
1251 | if (!ysize) | |
1252 | return -1; | |
1253 | ||
1254 | minBitsPerScanline = xsize * pBankFormat->bitsPerPixel; | |
1255 | if (minBitsPerScanline > nBitsPerBank) | |
1256 | return -1; | |
1257 | ||
1258 | if (ysize == 1) | |
1259 | return (int) width; | |
1260 | ||
1261 | maxBitsPerScanline = | |
1262 | (((unsigned long) (-1) >> 1) - minBitsPerScanline) / (ysize - 1); | |
1263 | while (nBitsPerScanline <= maxBitsPerScanline) { | |
1264 | unsigned long BankBase, BankUnit; | |
1265 | ||
1266 | BankUnit = ((nBitsPerBank + nBitsPerScanline - 1) / nBitsPerBank) * | |
1267 | nBitsPerBank; | |
1268 | if (!(BankUnit % nBitsPerScanline)) | |
1269 | return (int) width; | |
1270 | ||
1271 | for (BankBase = BankUnit;; BankBase += nBitsPerBank) { | |
1272 | unsigned long x, y; | |
1273 | ||
1274 | y = BankBase / nBitsPerScanline; | |
1275 | if (y >= ysize) | |
1276 | return (int) width; | |
1277 | ||
1278 | x = BankBase % nBitsPerScanline; | |
1279 | if (!(x % pBankFormat->bitsPerPixel)) | |
1280 | continue; | |
1281 | ||
1282 | if (x < minBitsPerScanline) { | |
1283 | /* | |
1284 | * Skip ahead certain widths by dividing the excess scanline | |
1285 | * amongst the y's. | |
1286 | */ | |
1287 | y *= nBitsPerScanlinePadUnit; | |
1288 | nBitsPerScanline += ((x + y - 1) / y) * nBitsPerScanlinePadUnit; | |
1289 | width = nBitsPerScanline / pBankFormat->bitsPerPixel; | |
1290 | break; | |
1291 | } | |
1292 | ||
1293 | if (BankBase != BankUnit) | |
1294 | continue; | |
1295 | ||
1296 | if (!(nBitsPerScanline % x)) | |
1297 | return (int) width; | |
1298 | ||
1299 | BankBase = ((nBitsPerScanline - minBitsPerScanline) / | |
1300 | (nBitsPerScanline - x)) * BankUnit; | |
1301 | } | |
1302 | } | |
1303 | ||
1304 | return -1; | |
1305 | } | |
1306 | ||
1307 | /* | |
1308 | * xf86ValidateModes | |
1309 | * | |
1310 | * This function takes a set of mode names, modes and limiting conditions, | |
1311 | * and selects a set of modes and parameters based on those conditions. | |
1312 | * | |
1313 | * This function takes the following parameters: | |
1314 | * scrp ScrnInfoPtr | |
1315 | * availModes the list of modes available for the monitor | |
1316 | * modeNames (optional) list of mode names that the screen is requesting | |
1317 | * clockRanges a list of clock ranges | |
1318 | * linePitches (optional) a list of line pitches | |
1319 | * minPitch (optional) minimum line pitch (in pixels) | |
1320 | * maxPitch (optional) maximum line pitch (in pixels) | |
1321 | * pitchInc (mandatory) pitch increment (in bits) | |
1322 | * minHeight (optional) minimum virtual height (in pixels) | |
1323 | * maxHeight (optional) maximum virtual height (in pixels) | |
1324 | * virtualX (optional) virtual width requested (in pixels) | |
1325 | * virtualY (optional) virtual height requested (in pixels) | |
1326 | * apertureSize size of video aperture (in bytes) | |
1327 | * strategy how to decide which mode to use from multiple modes with | |
1328 | * the same name | |
1329 | * | |
1330 | * In addition, the following fields from the ScrnInfoRec are used: | |
1331 | * clocks a list of discrete clocks | |
1332 | * numClocks number of discrete clocks | |
1333 | * progClock clock is programmable | |
1334 | * monitor pointer to structure for monitor section | |
1335 | * fbFormat format of the framebuffer | |
1336 | * videoRam video memory size | |
1337 | * maxHValue maximum horizontal timing value | |
1338 | * maxVValue maximum vertical timing value | |
1339 | * xInc horizontal timing increment (defaults to 8 pixels) | |
1340 | * | |
1341 | * The function fills in the following ScrnInfoRec fields: | |
1342 | * modePool A subset of the modes available to the monitor which | |
1343 | * are compatible with the driver. | |
1344 | * modes one mode entry for each of the requested modes, with the | |
1345 | * status field filled in to indicate if the mode has been | |
1346 | * accepted or not. | |
1347 | * virtualX the resulting virtual width | |
1348 | * virtualY the resulting virtual height | |
1349 | * displayWidth the resulting line pitch | |
1350 | * | |
1351 | * The function's return value is the number of matching modes found, or -1 | |
1352 | * if an unrecoverable error was encountered. | |
1353 | */ | |
1354 | ||
1355 | int | |
1356 | xf86ValidateModes(ScrnInfoPtr scrp, DisplayModePtr availModes, | |
1357 | char **modeNames, ClockRangePtr clockRanges, | |
1358 | int *linePitches, int minPitch, int maxPitch, int pitchInc, | |
1359 | int minHeight, int maxHeight, int virtualX, int virtualY, | |
1360 | int apertureSize, LookupModeFlags strategy) | |
1361 | { | |
1362 | DisplayModePtr p, q, r, new, last, *endp; | |
1363 | int i, numModes = 0; | |
1364 | ModeStatus status; | |
1365 | int linePitch = -1, virtX = 0, virtY = 0; | |
1366 | int newLinePitch, newVirtX, newVirtY; | |
1367 | int modeSize; /* in pixels */ | |
1368 | Bool validateAllDefaultModes = FALSE; | |
1369 | Bool userModes = FALSE; | |
1370 | int saveType; | |
1371 | PixmapFormatRec *BankFormat; | |
1372 | ClockRangePtr cp; | |
1373 | int numTimings = 0; | |
1374 | range hsync[MAX_HSYNC]; | |
1375 | range vrefresh[MAX_VREFRESH]; | |
1376 | Bool inferred_virtual = FALSE; | |
1377 | ||
1378 | DebugF | |
1379 | ("xf86ValidateModes(%p, %p, %p, %p,\n\t\t %p, %d, %d, %d, %d, %d, %d, %d, %d, 0x%x)\n", | |
1380 | scrp, availModes, modeNames, clockRanges, linePitches, minPitch, | |
1381 | maxPitch, pitchInc, minHeight, maxHeight, virtualX, virtualY, | |
1382 | apertureSize, strategy); | |
1383 | ||
1384 | /* Some sanity checking */ | |
1385 | if (scrp == NULL || scrp->name == NULL || !scrp->monitor || | |
1386 | (!scrp->progClock && scrp->numClocks == 0)) { | |
1387 | ErrorF("xf86ValidateModes: called with invalid scrnInfoRec\n"); | |
1388 | return -1; | |
1389 | } | |
1390 | if (linePitches != NULL && linePitches[0] <= 0) { | |
1391 | ErrorF("xf86ValidateModes: called with invalid linePitches\n"); | |
1392 | return -1; | |
1393 | } | |
1394 | if (pitchInc <= 0) { | |
1395 | ErrorF("xf86ValidateModes: called with invalid pitchInc\n"); | |
1396 | return -1; | |
1397 | } | |
1398 | if ((virtualX > 0) != (virtualY > 0)) { | |
1399 | ErrorF("xf86ValidateModes: called with invalid virtual resolution\n"); | |
1400 | return -1; | |
1401 | } | |
1402 | ||
1403 | /* | |
1404 | * If requested by the driver, allow missing hsync and/or vrefresh ranges | |
1405 | * in the monitor section. | |
1406 | */ | |
1407 | if (strategy & LOOKUP_OPTIONAL_TOLERANCES) { | |
1408 | strategy &= ~LOOKUP_OPTIONAL_TOLERANCES; | |
1409 | } | |
1410 | else { | |
1411 | const char *type = ""; | |
1412 | Bool specified = FALSE; | |
1413 | ||
1414 | if (scrp->monitor->nHsync <= 0) { | |
1415 | if (numTimings > 0) { | |
1416 | scrp->monitor->nHsync = numTimings; | |
1417 | for (i = 0; i < numTimings; i++) { | |
1418 | scrp->monitor->hsync[i].lo = hsync[i].lo; | |
1419 | scrp->monitor->hsync[i].hi = hsync[i].hi; | |
1420 | } | |
1421 | } | |
1422 | else { | |
1423 | scrp->monitor->hsync[0].lo = 31.5; | |
1424 | scrp->monitor->hsync[0].hi = 48.0; | |
1425 | scrp->monitor->nHsync = 1; | |
1426 | } | |
1427 | type = "default "; | |
1428 | } | |
1429 | else { | |
1430 | specified = TRUE; | |
1431 | } | |
1432 | for (i = 0; i < scrp->monitor->nHsync; i++) { | |
1433 | if (scrp->monitor->hsync[i].lo == scrp->monitor->hsync[i].hi) | |
1434 | xf86DrvMsg(scrp->scrnIndex, X_INFO, | |
1435 | "%s: Using %shsync value of %.2f kHz\n", | |
1436 | scrp->monitor->id, type, scrp->monitor->hsync[i].lo); | |
1437 | else | |
1438 | xf86DrvMsg(scrp->scrnIndex, X_INFO, | |
1439 | "%s: Using %shsync range of %.2f-%.2f kHz\n", | |
1440 | scrp->monitor->id, type, | |
1441 | scrp->monitor->hsync[i].lo, | |
1442 | scrp->monitor->hsync[i].hi); | |
1443 | } | |
1444 | ||
1445 | type = ""; | |
1446 | if (scrp->monitor->nVrefresh <= 0) { | |
1447 | if (numTimings > 0) { | |
1448 | scrp->monitor->nVrefresh = numTimings; | |
1449 | for (i = 0; i < numTimings; i++) { | |
1450 | scrp->monitor->vrefresh[i].lo = vrefresh[i].lo; | |
1451 | scrp->monitor->vrefresh[i].hi = vrefresh[i].hi; | |
1452 | } | |
1453 | } | |
1454 | else { | |
1455 | scrp->monitor->vrefresh[0].lo = 50; | |
1456 | scrp->monitor->vrefresh[0].hi = 70; | |
1457 | scrp->monitor->nVrefresh = 1; | |
1458 | } | |
1459 | type = "default "; | |
1460 | } | |
1461 | else { | |
1462 | specified = TRUE; | |
1463 | } | |
1464 | for (i = 0; i < scrp->monitor->nVrefresh; i++) { | |
1465 | if (scrp->monitor->vrefresh[i].lo == scrp->monitor->vrefresh[i].hi) | |
1466 | xf86DrvMsg(scrp->scrnIndex, X_INFO, | |
1467 | "%s: Using %svrefresh value of %.2f Hz\n", | |
1468 | scrp->monitor->id, type, | |
1469 | scrp->monitor->vrefresh[i].lo); | |
1470 | else | |
1471 | xf86DrvMsg(scrp->scrnIndex, X_INFO, | |
1472 | "%s: Using %svrefresh range of %.2f-%.2f Hz\n", | |
1473 | scrp->monitor->id, type, | |
1474 | scrp->monitor->vrefresh[i].lo, | |
1475 | scrp->monitor->vrefresh[i].hi); | |
1476 | } | |
1477 | ||
1478 | type = ""; | |
1479 | if (!scrp->monitor->maxPixClock && !specified) { | |
1480 | type = "default "; | |
1481 | scrp->monitor->maxPixClock = 65000.0; | |
1482 | } | |
1483 | if (scrp->monitor->maxPixClock) { | |
1484 | xf86DrvMsg(scrp->scrnIndex, X_INFO, | |
1485 | "%s: Using %smaximum pixel clock of %.2f MHz\n", | |
1486 | scrp->monitor->id, type, | |
1487 | (float) scrp->monitor->maxPixClock / 1000.0); | |
1488 | } | |
1489 | } | |
1490 | ||
1491 | /* | |
1492 | * Store the clockRanges for later use by the VidMode extension. | |
1493 | */ | |
1494 | nt_list_for_each_entry(cp, clockRanges, next) { | |
1495 | ClockRangePtr newCR = xnfalloc(sizeof(ClockRange)); | |
1496 | memcpy(newCR, cp, sizeof(ClockRange)); | |
1497 | newCR->next = NULL; | |
1498 | if (scrp->clockRanges == NULL) | |
1499 | scrp->clockRanges = newCR; | |
1500 | else | |
1501 | nt_list_append(newCR, scrp->clockRanges, ClockRange, next); | |
1502 | } | |
1503 | ||
1504 | /* Determine which pixmap format to pass to scanLineWidth() */ | |
1505 | if (scrp->depth > 4) | |
1506 | BankFormat = &scrp->fbFormat; | |
1507 | else | |
1508 | BankFormat = xf86GetPixFormat(scrp, 1); /* >not< scrp->depth! */ | |
1509 | ||
1510 | if (scrp->xInc <= 0) | |
1511 | scrp->xInc = 8; /* Suitable for VGA and others */ | |
1512 | ||
1513 | #define _VIRTUALX(x) ((((x) + scrp->xInc - 1) / scrp->xInc) * scrp->xInc) | |
1514 | ||
1515 | /* | |
1516 | * Determine maxPitch if it wasn't given explicitly. Note linePitches | |
1517 | * always takes precedence if is non-NULL. In that case the minPitch and | |
1518 | * maxPitch values passed are ignored. | |
1519 | */ | |
1520 | if (linePitches) { | |
1521 | minPitch = maxPitch = linePitches[0]; | |
1522 | for (i = 1; linePitches[i] > 0; i++) { | |
1523 | if (linePitches[i] > maxPitch) | |
1524 | maxPitch = linePitches[i]; | |
1525 | if (linePitches[i] < minPitch) | |
1526 | minPitch = linePitches[i]; | |
1527 | } | |
1528 | } | |
1529 | ||
1530 | /* Initial check of virtual size against other constraints */ | |
1531 | scrp->virtualFrom = X_PROBED; | |
1532 | /* | |
1533 | * Initialise virtX and virtY if the values are fixed. | |
1534 | */ | |
1535 | if (virtualY > 0) { | |
1536 | if (maxHeight > 0 && virtualY > maxHeight) { | |
1537 | xf86DrvMsg(scrp->scrnIndex, X_ERROR, | |
1538 | "Virtual height (%d) is too large for the hardware " | |
1539 | "(max %d)\n", virtualY, maxHeight); | |
1540 | return -1; | |
1541 | } | |
1542 | ||
1543 | if (minHeight > 0 && virtualY < minHeight) { | |
1544 | xf86DrvMsg(scrp->scrnIndex, X_ERROR, | |
1545 | "Virtual height (%d) is too small for the hardware " | |
1546 | "(min %d)\n", virtualY, minHeight); | |
1547 | return -1; | |
1548 | } | |
1549 | ||
1550 | virtualX = _VIRTUALX(virtualX); | |
1551 | if (linePitches != NULL) { | |
1552 | for (i = 0; linePitches[i] != 0; i++) { | |
1553 | if ((linePitches[i] >= virtualX) && | |
1554 | (linePitches[i] == | |
1555 | scanLineWidth(virtualX, virtualY, linePitches[i], | |
1556 | apertureSize, BankFormat, pitchInc))) { | |
1557 | linePitch = linePitches[i]; | |
1558 | break; | |
1559 | } | |
1560 | } | |
1561 | } | |
1562 | else { | |
1563 | linePitch = scanLineWidth(virtualX, virtualY, minPitch, | |
1564 | apertureSize, BankFormat, pitchInc); | |
1565 | } | |
1566 | ||
1567 | if ((linePitch < minPitch) || (linePitch > maxPitch)) { | |
1568 | xf86DrvMsg(scrp->scrnIndex, X_ERROR, | |
1569 | "Virtual width (%d) is too large for the hardware " | |
1570 | "(max %d)\n", virtualX, maxPitch); | |
1571 | return -1; | |
1572 | } | |
1573 | ||
1574 | if (!xf86CheckModeSize(scrp, linePitch, virtualX, virtualY)) { | |
1575 | xf86DrvMsg(scrp->scrnIndex, X_ERROR, | |
1576 | "Virtual size (%dx%d) (pitch %d) exceeds video memory\n", | |
1577 | virtualX, virtualY, linePitch); | |
1578 | return -1; | |
1579 | } | |
1580 | ||
1581 | virtX = virtualX; | |
1582 | virtY = virtualY; | |
1583 | scrp->virtualFrom = X_CONFIG; | |
1584 | } | |
1585 | else if (!modeNames || !*modeNames) { | |
1586 | /* No virtual size given in the config, try to infer */ | |
1587 | /* XXX this doesn't take m{in,ax}Pitch into account; oh well */ | |
1588 | inferred_virtual = inferVirtualSize(scrp, availModes, &virtX, &virtY); | |
1589 | if (inferred_virtual) | |
1590 | linePitch = scanLineWidth(virtX, virtY, minPitch, apertureSize, | |
1591 | BankFormat, pitchInc); | |
1592 | } | |
1593 | ||
1594 | /* Print clock ranges and scaled clocks */ | |
1595 | xf86ShowClockRanges(scrp, clockRanges); | |
1596 | ||
1597 | /* | |
1598 | * If scrp->modePool hasn't been setup yet, set it up now. This allows the | |
1599 | * modes that the driver definitely can't use to be weeded out early. Note | |
1600 | * that a modePool mode's prev field is used to hold a pointer to the | |
1601 | * member of the scrp->modes list for which a match was considered. | |
1602 | */ | |
1603 | if (scrp->modePool == NULL) { | |
1604 | q = NULL; | |
1605 | for (p = availModes; p != NULL; p = p->next) { | |
1606 | status = xf86InitialCheckModeForDriver(scrp, p, clockRanges, | |
1607 | strategy, maxPitch, | |
1608 | virtX, virtY); | |
1609 | ||
1610 | if (status == MODE_OK) { | |
1611 | status = xf86CheckModeForMonitor(p, scrp->monitor); | |
1612 | } | |
1613 | ||
1614 | if (status == MODE_OK) { | |
1615 | new = xnfalloc(sizeof(DisplayModeRec)); | |
1616 | *new = *p; | |
1617 | new->next = NULL; | |
1618 | if (!q) { | |
1619 | scrp->modePool = new; | |
1620 | } | |
1621 | else { | |
1622 | q->next = new; | |
1623 | } | |
1624 | new->prev = NULL; | |
1625 | q = new; | |
1626 | q->name = xnfstrdup(p->name); | |
1627 | q->status = MODE_OK; | |
1628 | } | |
1629 | else { | |
1630 | printModeRejectMessage(scrp->scrnIndex, p, status); | |
1631 | } | |
1632 | } | |
1633 | ||
1634 | if (scrp->modePool == NULL) { | |
1635 | xf86DrvMsg(scrp->scrnIndex, X_WARNING, "Mode pool is empty\n"); | |
1636 | return 0; | |
1637 | } | |
1638 | } | |
1639 | else { | |
1640 | for (p = scrp->modePool; p != NULL; p = p->next) { | |
1641 | p->prev = NULL; | |
1642 | p->status = MODE_OK; | |
1643 | } | |
1644 | } | |
1645 | ||
1646 | /* | |
1647 | * Allocate one entry in scrp->modes for each named mode. | |
1648 | */ | |
1649 | while (scrp->modes) | |
1650 | xf86DeleteMode(&scrp->modes, scrp->modes); | |
1651 | endp = &scrp->modes; | |
1652 | last = NULL; | |
1653 | if (modeNames != NULL) { | |
1654 | for (i = 0; modeNames[i] != NULL; i++) { | |
1655 | userModes = TRUE; | |
1656 | new = xnfcalloc(1, sizeof(DisplayModeRec)); | |
1657 | new->prev = last; | |
1658 | new->type = M_T_USERDEF; | |
1659 | new->name = xnfstrdup(modeNames[i]); | |
1660 | if (new->prev) | |
1661 | new->prev->next = new; | |
1662 | *endp = last = new; | |
1663 | endp = &new->next; | |
1664 | } | |
1665 | } | |
1666 | ||
1667 | /* Lookup each mode */ | |
1668 | #ifdef RANDR | |
1669 | if (!xf86Info.disableRandR | |
1670 | #ifdef PANORAMIX | |
1671 | && noPanoramiXExtension | |
1672 | #endif | |
1673 | ) | |
1674 | validateAllDefaultModes = TRUE; | |
1675 | #endif | |
1676 | ||
1677 | for (p = scrp->modes;; p = p->next) { | |
1678 | Bool repeat; | |
1679 | ||
1680 | /* | |
1681 | * If the supplied mode names don't produce a valid mode, scan through | |
1682 | * unconsidered modePool members until one survives validation. This | |
1683 | * is done in decreasing order by mode pixel area. | |
1684 | */ | |
1685 | ||
1686 | if (p == NULL) { | |
1687 | if ((numModes > 0) && !validateAllDefaultModes) | |
1688 | break; | |
1689 | ||
1690 | validateAllDefaultModes = TRUE; | |
1691 | r = NULL; | |
1692 | modeSize = 0; | |
1693 | for (q = scrp->modePool; q != NULL; q = q->next) { | |
1694 | if ((q->prev == NULL) && (q->status == MODE_OK)) { | |
1695 | /* | |
1696 | * Deal with the case where this mode wasn't considered | |
1697 | * because of a builtin mode of the same name. | |
1698 | */ | |
1699 | for (p = scrp->modes; p != NULL; p = p->next) { | |
1700 | if ((p->status != MODE_OK) && !strcmp(p->name, q->name)) | |
1701 | break; | |
1702 | } | |
1703 | ||
1704 | if (p != NULL) | |
1705 | q->prev = p; | |
1706 | else { | |
1707 | /* | |
1708 | * A quick check to not allow default modes with | |
1709 | * horizontal timing parameters that CRTs may have | |
1710 | * problems with. | |
1711 | */ | |
1712 | if (!scrp->monitor->reducedblanking && | |
1713 | (q->type & M_T_DEFAULT) && | |
1714 | ((double) q->HTotal / (double) q->HDisplay) < 1.15) | |
1715 | continue; | |
1716 | ||
1717 | if (modeSize < (q->HDisplay * q->VDisplay)) { | |
1718 | r = q; | |
1719 | modeSize = q->HDisplay * q->VDisplay; | |
1720 | } | |
1721 | } | |
1722 | } | |
1723 | } | |
1724 | ||
1725 | if (r == NULL) | |
1726 | break; | |
1727 | ||
1728 | p = xnfcalloc(1, sizeof(DisplayModeRec)); | |
1729 | p->prev = last; | |
1730 | p->name = xnfstrdup(r->name); | |
1731 | if (!userModes) | |
1732 | p->type = M_T_USERDEF; | |
1733 | if (p->prev) | |
1734 | p->prev->next = p; | |
1735 | *endp = last = p; | |
1736 | endp = &p->next; | |
1737 | } | |
1738 | ||
1739 | repeat = FALSE; | |
1740 | lookupNext: | |
1741 | if (repeat && ((status = p->status) != MODE_OK)) | |
1742 | printModeRejectMessage(scrp->scrnIndex, p, status); | |
1743 | saveType = p->type; | |
1744 | status = xf86LookupMode(scrp, p, clockRanges, strategy); | |
1745 | if (repeat && status == MODE_NOMODE) | |
1746 | continue; | |
1747 | if (status != MODE_OK) | |
1748 | printModeRejectMessage(scrp->scrnIndex, p, status); | |
1749 | if (status == MODE_ERROR) { | |
1750 | ErrorF("xf86ValidateModes: " | |
1751 | "unexpected result from xf86LookupMode()\n"); | |
1752 | return -1; | |
1753 | } | |
1754 | if (status != MODE_OK) { | |
1755 | if (p->status == MODE_OK) | |
1756 | p->status = status; | |
1757 | continue; | |
1758 | } | |
1759 | p->type |= saveType; | |
1760 | repeat = TRUE; | |
1761 | ||
1762 | newLinePitch = linePitch; | |
1763 | newVirtX = virtX; | |
1764 | newVirtY = virtY; | |
1765 | ||
1766 | /* | |
1767 | * Don't let non-user defined modes increase the virtual size | |
1768 | */ | |
1769 | if (!(p->type & M_T_USERDEF) && (numModes > 0)) { | |
1770 | if (p->HDisplay > virtX) { | |
1771 | p->status = MODE_VIRTUAL_X; | |
1772 | goto lookupNext; | |
1773 | } | |
1774 | if (p->VDisplay > virtY) { | |
1775 | p->status = MODE_VIRTUAL_Y; | |
1776 | goto lookupNext; | |
1777 | } | |
1778 | } | |
1779 | /* | |
1780 | * Adjust virtual width and height if the mode is too large for the | |
1781 | * current values and if they are not fixed. | |
1782 | */ | |
1783 | if (virtualX <= 0 && p->HDisplay > newVirtX) | |
1784 | newVirtX = _VIRTUALX(p->HDisplay); | |
1785 | if (virtualY <= 0 && p->VDisplay > newVirtY) { | |
1786 | if (maxHeight > 0 && p->VDisplay > maxHeight) { | |
1787 | p->status = MODE_VIRTUAL_Y; /* ? */ | |
1788 | goto lookupNext; | |
1789 | } | |
1790 | newVirtY = p->VDisplay; | |
1791 | } | |
1792 | ||
1793 | /* | |
1794 | * If virtual resolution is to be increased, revalidate it. | |
1795 | */ | |
1796 | if ((virtX != newVirtX) || (virtY != newVirtY)) { | |
1797 | if (linePitches != NULL) { | |
1798 | newLinePitch = -1; | |
1799 | for (i = 0; linePitches[i] != 0; i++) { | |
1800 | if ((linePitches[i] >= newVirtX) && | |
1801 | (linePitches[i] >= linePitch) && | |
1802 | (linePitches[i] == | |
1803 | scanLineWidth(newVirtX, newVirtY, linePitches[i], | |
1804 | apertureSize, BankFormat, pitchInc))) { | |
1805 | newLinePitch = linePitches[i]; | |
1806 | break; | |
1807 | } | |
1808 | } | |
1809 | } | |
1810 | else { | |
1811 | if (linePitch < minPitch) | |
1812 | linePitch = minPitch; | |
1813 | newLinePitch = scanLineWidth(newVirtX, newVirtY, linePitch, | |
1814 | apertureSize, BankFormat, | |
1815 | pitchInc); | |
1816 | } | |
1817 | if ((newLinePitch < minPitch) || (newLinePitch > maxPitch)) { | |
1818 | p->status = MODE_BAD_WIDTH; | |
1819 | goto lookupNext; | |
1820 | } | |
1821 | ||
1822 | /* | |
1823 | * Check that the pixel area required by the new virtual height | |
1824 | * and line pitch isn't too large. | |
1825 | */ | |
1826 | if (!xf86CheckModeSize(scrp, newLinePitch, newVirtX, newVirtY)) { | |
1827 | p->status = MODE_MEM_VIRT; | |
1828 | goto lookupNext; | |
1829 | } | |
1830 | } | |
1831 | ||
1832 | if (scrp->ValidMode) { | |
1833 | /* | |
1834 | * Give the driver a final say, passing it the proposed virtual | |
1835 | * geometry. | |
1836 | */ | |
1837 | scrp->virtualX = newVirtX; | |
1838 | scrp->virtualY = newVirtY; | |
1839 | scrp->displayWidth = newLinePitch; | |
1840 | p->status = (scrp->ValidMode) (scrp, p, FALSE, | |
1841 | MODECHECK_FINAL); | |
1842 | ||
1843 | if (p->status != MODE_OK) { | |
1844 | goto lookupNext; | |
1845 | } | |
1846 | } | |
1847 | ||
1848 | /* Mode has passed all the tests */ | |
1849 | virtX = newVirtX; | |
1850 | virtY = newVirtY; | |
1851 | linePitch = newLinePitch; | |
1852 | p->status = MODE_OK; | |
1853 | numModes++; | |
1854 | } | |
1855 | ||
1856 | /* | |
1857 | * If we estimated the virtual size above, we may have filtered away all | |
1858 | * the modes that maximally match that size; scan again to find out and | |
1859 | * fix up if so. | |
1860 | */ | |
1861 | if (inferred_virtual) { | |
1862 | int vx = 0, vy = 0; | |
1863 | ||
1864 | for (p = scrp->modes; p; p = p->next) { | |
1865 | if (p->HDisplay > vx && p->VDisplay > vy) { | |
1866 | vx = p->HDisplay; | |
1867 | vy = p->VDisplay; | |
1868 | } | |
1869 | } | |
1870 | if (vx < virtX || vy < virtY) { | |
1871 | const int types[] = { | |
1872 | M_T_BUILTIN | M_T_PREFERRED, | |
1873 | M_T_BUILTIN, | |
1874 | M_T_DRIVER | M_T_PREFERRED, | |
1875 | M_T_DRIVER, | |
1876 | 0 | |
1877 | }; | |
1878 | const int ntypes = sizeof(types) / sizeof(int); | |
1879 | int n; | |
1880 | ||
1881 | /* | |
1882 | * We did not find the estimated virtual size. So now we want to | |
1883 | * find the largest mode available, but we want to search in the | |
1884 | * modes in the order of "types" listed above. | |
1885 | */ | |
1886 | for (n = 0; n < ntypes; n++) { | |
1887 | int type = types[n]; | |
1888 | ||
1889 | vx = 0; | |
1890 | vy = 0; | |
1891 | for (p = scrp->modes; p; p = p->next) { | |
1892 | /* scan through the modes in the sort order above */ | |
1893 | if ((p->type & type) != type) | |
1894 | continue; | |
1895 | if (p->HDisplay > vx && p->VDisplay > vy) { | |
1896 | vx = p->HDisplay; | |
1897 | vy = p->VDisplay; | |
1898 | } | |
1899 | } | |
1900 | if (vx && vy) | |
1901 | /* Found one */ | |
1902 | break; | |
1903 | } | |
1904 | xf86DrvMsg(scrp->scrnIndex, X_WARNING, | |
1905 | "Shrinking virtual size estimate from %dx%d to %dx%d\n", | |
1906 | virtX, virtY, vx, vy); | |
1907 | virtX = _VIRTUALX(vx); | |
1908 | virtY = vy; | |
1909 | for (p = scrp->modes; p; p = p->next) { | |
1910 | if (numModes > 0) { | |
1911 | if (p->HDisplay > virtX) | |
1912 | p->status = MODE_VIRTUAL_X; | |
1913 | if (p->VDisplay > virtY) | |
1914 | p->status = MODE_VIRTUAL_Y; | |
1915 | if (p->status != MODE_OK) { | |
1916 | numModes--; | |
1917 | printModeRejectMessage(scrp->scrnIndex, p, p->status); | |
1918 | } | |
1919 | } | |
1920 | } | |
1921 | if (linePitches != NULL) { | |
1922 | for (i = 0; linePitches[i] != 0; i++) { | |
1923 | if ((linePitches[i] >= virtX) && | |
1924 | (linePitches[i] == | |
1925 | scanLineWidth(virtX, virtY, linePitches[i], | |
1926 | apertureSize, BankFormat, pitchInc))) { | |
1927 | linePitch = linePitches[i]; | |
1928 | break; | |
1929 | } | |
1930 | } | |
1931 | } | |
1932 | else { | |
1933 | linePitch = scanLineWidth(virtX, virtY, minPitch, | |
1934 | apertureSize, BankFormat, pitchInc); | |
1935 | } | |
1936 | } | |
1937 | } | |
1938 | ||
1939 | /* Update the ScrnInfoRec parameters */ | |
1940 | ||
1941 | scrp->virtualX = virtX; | |
1942 | scrp->virtualY = virtY; | |
1943 | scrp->displayWidth = linePitch; | |
1944 | ||
1945 | if (numModes <= 0) | |
1946 | return 0; | |
1947 | ||
1948 | /* Make the mode list into a circular list by joining up the ends */ | |
1949 | p = scrp->modes; | |
1950 | while (p->next != NULL) | |
1951 | p = p->next; | |
1952 | /* p is now the last mode on the list */ | |
1953 | p->next = scrp->modes; | |
1954 | scrp->modes->prev = p; | |
1955 | ||
1956 | if (minHeight > 0 && virtY < minHeight) { | |
1957 | xf86DrvMsg(scrp->scrnIndex, X_ERROR, | |
1958 | "Virtual height (%d) is too small for the hardware " | |
1959 | "(min %d)\n", virtY, minHeight); | |
1960 | return -1; | |
1961 | } | |
1962 | ||
1963 | return numModes; | |
1964 | } | |
1965 | ||
1966 | /* | |
1967 | * xf86DeleteMode | |
1968 | * | |
1969 | * This function removes a mode from a list of modes. | |
1970 | * | |
1971 | * There are different types of mode lists: | |
1972 | * | |
1973 | * - singly linked linear lists, ending in NULL | |
1974 | * - doubly linked linear lists, starting and ending in NULL | |
1975 | * - doubly linked circular lists | |
1976 | * | |
1977 | */ | |
1978 | ||
1979 | void | |
1980 | xf86DeleteMode(DisplayModePtr * modeList, DisplayModePtr mode) | |
1981 | { | |
1982 | /* Catch the easy/insane cases */ | |
1983 | if (modeList == NULL || *modeList == NULL || mode == NULL) | |
1984 | return; | |
1985 | ||
1986 | /* If the mode is at the start of the list, move the start of the list */ | |
1987 | if (*modeList == mode) | |
1988 | *modeList = mode->next; | |
1989 | ||
1990 | /* If mode is the only one on the list, set the list to NULL */ | |
1991 | if ((mode == mode->prev) && (mode == mode->next)) { | |
1992 | *modeList = NULL; | |
1993 | } | |
1994 | else { | |
1995 | if ((mode->prev != NULL) && (mode->prev->next == mode)) | |
1996 | mode->prev->next = mode->next; | |
1997 | if ((mode->next != NULL) && (mode->next->prev == mode)) | |
1998 | mode->next->prev = mode->prev; | |
1999 | } | |
2000 | ||
2001 | free(mode->name); | |
2002 | free(mode); | |
2003 | } | |
2004 | ||
2005 | /* | |
2006 | * xf86PruneDriverModes | |
2007 | * | |
2008 | * Remove modes from the driver's mode list which have been marked as | |
2009 | * invalid. | |
2010 | */ | |
2011 | ||
2012 | void | |
2013 | xf86PruneDriverModes(ScrnInfoPtr scrp) | |
2014 | { | |
2015 | DisplayModePtr first, p, n; | |
2016 | ||
2017 | p = scrp->modes; | |
2018 | if (p == NULL) | |
2019 | return; | |
2020 | ||
2021 | do { | |
2022 | if (!(first = scrp->modes)) | |
2023 | return; | |
2024 | n = p->next; | |
2025 | if (p->status != MODE_OK) { | |
2026 | xf86DeleteMode(&(scrp->modes), p); | |
2027 | } | |
2028 | p = n; | |
2029 | } while (p != NULL && p != first); | |
2030 | ||
2031 | /* modePool is no longer needed, turf it */ | |
2032 | while (scrp->modePool) { | |
2033 | /* | |
2034 | * A modePool mode's prev field is used to hold a pointer to the | |
2035 | * member of the scrp->modes list for which a match was considered. | |
2036 | * Clear that pointer first, otherwise xf86DeleteMode might get | |
2037 | * confused | |
2038 | */ | |
2039 | scrp->modePool->prev = NULL; | |
2040 | xf86DeleteMode(&scrp->modePool, scrp->modePool); | |
2041 | } | |
2042 | } | |
2043 | ||
2044 | /* | |
2045 | * xf86SetCrtcForModes | |
2046 | * | |
2047 | * Goes through the screen's mode list, and initialises the Crtc | |
2048 | * parameters for each mode. The initialisation includes adjustments | |
2049 | * for interlaced and double scan modes. | |
2050 | */ | |
2051 | void | |
2052 | xf86SetCrtcForModes(ScrnInfoPtr scrp, int adjustFlags) | |
2053 | { | |
2054 | DisplayModePtr p; | |
2055 | ||
2056 | /* | |
2057 | * Store adjustFlags for use with the VidMode extension. There is an | |
2058 | * implicit assumption here that SetCrtcForModes is called once. | |
2059 | */ | |
2060 | scrp->adjustFlags = adjustFlags; | |
2061 | ||
2062 | p = scrp->modes; | |
2063 | if (p == NULL) | |
2064 | return; | |
2065 | ||
2066 | do { | |
2067 | xf86SetModeCrtc(p, adjustFlags); | |
2068 | DebugF("%sMode %s: %d (%d) %d %d (%d) %d %d (%d) %d %d (%d) %d\n", | |
2069 | (p->type & M_T_DEFAULT) ? "Default " : "", | |
2070 | p->name, p->CrtcHDisplay, p->CrtcHBlankStart, | |
2071 | p->CrtcHSyncStart, p->CrtcHSyncEnd, p->CrtcHBlankEnd, | |
2072 | p->CrtcHTotal, p->CrtcVDisplay, p->CrtcVBlankStart, | |
2073 | p->CrtcVSyncStart, p->CrtcVSyncEnd, p->CrtcVBlankEnd, | |
2074 | p->CrtcVTotal); | |
2075 | p = p->next; | |
2076 | } while (p != NULL && p != scrp->modes); | |
2077 | } | |
2078 | ||
2079 | void | |
2080 | xf86PrintModes(ScrnInfoPtr scrp) | |
2081 | { | |
2082 | DisplayModePtr p; | |
2083 | float hsync, refresh = 0; | |
2084 | const char *desc, *desc2, *prefix, *uprefix; | |
2085 | ||
2086 | if (scrp == NULL) | |
2087 | return; | |
2088 | ||
2089 | xf86DrvMsg(scrp->scrnIndex, scrp->virtualFrom, "Virtual size is %dx%d " | |
2090 | "(pitch %d)\n", scrp->virtualX, scrp->virtualY, | |
2091 | scrp->displayWidth); | |
2092 | ||
2093 | p = scrp->modes; | |
2094 | if (p == NULL) | |
2095 | return; | |
2096 | ||
2097 | do { | |
2098 | desc = desc2 = ""; | |
2099 | hsync = xf86ModeHSync(p); | |
2100 | refresh = xf86ModeVRefresh(p); | |
2101 | if (p->Flags & V_INTERLACE) { | |
2102 | desc = " (I)"; | |
2103 | } | |
2104 | if (p->Flags & V_DBLSCAN) { | |
2105 | desc = " (D)"; | |
2106 | } | |
2107 | if (p->VScan > 1) { | |
2108 | desc2 = " (VScan)"; | |
2109 | } | |
2110 | if (p->type & M_T_BUILTIN) | |
2111 | prefix = "Built-in mode"; | |
2112 | else if (p->type & M_T_DEFAULT) | |
2113 | prefix = "Default mode"; | |
2114 | else if (p->type & M_T_DRIVER) | |
2115 | prefix = "Driver mode"; | |
2116 | else | |
2117 | prefix = "Mode"; | |
2118 | if (p->type & M_T_USERDEF) | |
2119 | uprefix = "*"; | |
2120 | else | |
2121 | uprefix = " "; | |
2122 | if (hsync == 0 || refresh == 0) { | |
2123 | if (p->name) | |
2124 | xf86DrvMsg(scrp->scrnIndex, X_CONFIG, | |
2125 | "%s%s \"%s\"\n", uprefix, prefix, p->name); | |
2126 | else | |
2127 | xf86DrvMsg(scrp->scrnIndex, X_PROBED, | |
2128 | "%s%s %dx%d (unnamed)\n", | |
2129 | uprefix, prefix, p->HDisplay, p->VDisplay); | |
2130 | } | |
2131 | else if (p->Clock == p->SynthClock) { | |
2132 | xf86DrvMsg(scrp->scrnIndex, X_CONFIG, | |
2133 | "%s%s \"%s\": %.1f MHz, %.1f kHz, %.1f Hz%s%s\n", | |
2134 | uprefix, prefix, p->name, p->Clock / 1000.0, | |
2135 | hsync, refresh, desc, desc2); | |
2136 | } | |
2137 | else { | |
2138 | xf86DrvMsg(scrp->scrnIndex, X_CONFIG, | |
2139 | "%s%s \"%s\": %.1f MHz (scaled from %.1f MHz), " | |
2140 | "%.1f kHz, %.1f Hz%s%s\n", | |
2141 | uprefix, prefix, p->name, p->Clock / 1000.0, | |
2142 | p->SynthClock / 1000.0, hsync, refresh, desc, desc2); | |
2143 | } | |
2144 | if (hsync != 0 && refresh != 0) | |
2145 | xf86PrintModeline(scrp->scrnIndex, p); | |
2146 | p = p->next; | |
2147 | } while (p != NULL && p != scrp->modes); | |
2148 | } |