xf86Modes.c revision 05b261ec
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#ifdef HAVE_XORG_CONFIG_H
29#include <xorg-config.h>
30#else
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34#endif
35
36#include "xf86Modes.h"
37#include "xf86Priv.h"
38
39extern XF86ConfigPtr xf86configptr;
40
41/**
42 * @file this file contains symbols from xf86Mode.c and friends that are static
43 * there but we still want to use.  We need to come up with better API here.
44 */
45
46#if XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(7,2,99,2,0)
47/**
48 * Calculates the horizontal sync rate of a mode.
49 *
50 * Exact copy of xf86Mode.c's.
51 */
52_X_EXPORT double
53xf86ModeHSync(DisplayModePtr mode)
54{
55    double hsync = 0.0;
56
57    if (mode->HSync > 0.0)
58	    hsync = mode->HSync;
59    else if (mode->HTotal > 0)
60	    hsync = (float)mode->Clock / (float)mode->HTotal;
61
62    return hsync;
63}
64
65/**
66 * Calculates the vertical refresh rate of a mode.
67 *
68 * Exact copy of xf86Mode.c's.
69 */
70_X_EXPORT double
71xf86ModeVRefresh(DisplayModePtr mode)
72{
73    double refresh = 0.0;
74
75    if (mode->VRefresh > 0.0)
76	refresh = mode->VRefresh;
77    else if (mode->HTotal > 0 && mode->VTotal > 0) {
78	refresh = mode->Clock * 1000.0 / mode->HTotal / mode->VTotal;
79	if (mode->Flags & V_INTERLACE)
80	    refresh *= 2.0;
81	if (mode->Flags & V_DBLSCAN)
82	    refresh /= 2.0;
83	if (mode->VScan > 1)
84	    refresh /= (float)(mode->VScan);
85    }
86    return refresh;
87}
88
89_X_EXPORT int
90xf86ModeWidth (DisplayModePtr mode, Rotation rotation)
91{
92    switch (rotation & 0xf) {
93    case RR_Rotate_0:
94    case RR_Rotate_180:
95	return mode->HDisplay;
96    case RR_Rotate_90:
97    case RR_Rotate_270:
98	return mode->VDisplay;
99    default:
100	return 0;
101    }
102}
103
104_X_EXPORT int
105xf86ModeHeight (DisplayModePtr mode, Rotation rotation)
106{
107    switch (rotation & 0xf) {
108    case RR_Rotate_0:
109    case RR_Rotate_180:
110	return mode->VDisplay;
111    case RR_Rotate_90:
112    case RR_Rotate_270:
113	return mode->HDisplay;
114    default:
115	return 0;
116    }
117}
118
119/** Sets a default mode name of <width>x<height> on a mode. */
120_X_EXPORT void
121xf86SetModeDefaultName(DisplayModePtr mode)
122{
123    if (mode->name != NULL)
124	xfree(mode->name);
125
126    mode->name = XNFprintf("%dx%d", mode->HDisplay, mode->VDisplay);
127}
128
129/*
130 * xf86SetModeCrtc
131 *
132 * Initialises the Crtc parameters for a mode.  The initialisation includes
133 * adjustments for interlaced and double scan modes.
134 *
135 * Exact copy of xf86Mode.c's.
136 */
137_X_EXPORT void
138xf86SetModeCrtc(DisplayModePtr p, int adjustFlags)
139{
140    if ((p == NULL) || ((p->type & M_T_CRTC_C) == M_T_BUILTIN))
141	return;
142
143    p->CrtcHDisplay             = p->HDisplay;
144    p->CrtcHSyncStart           = p->HSyncStart;
145    p->CrtcHSyncEnd             = p->HSyncEnd;
146    p->CrtcHTotal               = p->HTotal;
147    p->CrtcHSkew                = p->HSkew;
148    p->CrtcVDisplay             = p->VDisplay;
149    p->CrtcVSyncStart           = p->VSyncStart;
150    p->CrtcVSyncEnd             = p->VSyncEnd;
151    p->CrtcVTotal               = p->VTotal;
152    if (p->Flags & V_INTERLACE) {
153	if (adjustFlags & INTERLACE_HALVE_V) {
154	    p->CrtcVDisplay         /= 2;
155	    p->CrtcVSyncStart       /= 2;
156	    p->CrtcVSyncEnd         /= 2;
157	    p->CrtcVTotal           /= 2;
158	}
159	/* Force interlaced modes to have an odd VTotal */
160	/* maybe we should only do this when INTERLACE_HALVE_V is set? */
161	p->CrtcVTotal |= 1;
162    }
163
164    if (p->Flags & V_DBLSCAN) {
165        p->CrtcVDisplay         *= 2;
166        p->CrtcVSyncStart       *= 2;
167        p->CrtcVSyncEnd         *= 2;
168        p->CrtcVTotal           *= 2;
169    }
170    if (p->VScan > 1) {
171        p->CrtcVDisplay         *= p->VScan;
172        p->CrtcVSyncStart       *= p->VScan;
173        p->CrtcVSyncEnd         *= p->VScan;
174        p->CrtcVTotal           *= p->VScan;
175    }
176    p->CrtcVBlankStart = min(p->CrtcVSyncStart, p->CrtcVDisplay);
177    p->CrtcVBlankEnd = max(p->CrtcVSyncEnd, p->CrtcVTotal);
178    p->CrtcHBlankStart = min(p->CrtcHSyncStart, p->CrtcHDisplay);
179    p->CrtcHBlankEnd = max(p->CrtcHSyncEnd, p->CrtcHTotal);
180
181    p->CrtcHAdjusted = FALSE;
182    p->CrtcVAdjusted = FALSE;
183}
184
185/**
186 * Allocates and returns a copy of pMode, including pointers within pMode.
187 */
188_X_EXPORT DisplayModePtr
189xf86DuplicateMode(DisplayModePtr pMode)
190{
191    DisplayModePtr pNew;
192
193    pNew = xnfalloc(sizeof(DisplayModeRec));
194    *pNew = *pMode;
195    pNew->next = NULL;
196    pNew->prev = NULL;
197    if (pNew->name == NULL) {
198	xf86SetModeDefaultName(pMode);
199    } else {
200	pNew->name = xnfstrdup(pMode->name);
201    }
202
203    return pNew;
204}
205
206/**
207 * Duplicates every mode in the given list and returns a pointer to the first
208 * mode.
209 *
210 * \param modeList doubly-linked mode list
211 */
212_X_EXPORT DisplayModePtr
213xf86DuplicateModes(ScrnInfoPtr pScrn, DisplayModePtr modeList)
214{
215    DisplayModePtr first = NULL, last = NULL;
216    DisplayModePtr mode;
217
218    for (mode = modeList; mode != NULL; mode = mode->next) {
219	DisplayModePtr new;
220
221	new = xf86DuplicateMode(mode);
222
223	/* Insert pNew into modeList */
224	if (last) {
225	    last->next = new;
226	    new->prev = last;
227	} else {
228	    first = new;
229	    new->prev = NULL;
230	}
231	new->next = NULL;
232	last = new;
233    }
234
235    return first;
236}
237
238/**
239 * Returns true if the given modes should program to the same timings.
240 *
241 * This doesn't use Crtc values, as it might be used on ModeRecs without the
242 * Crtc values set.  So, it's assumed that the other numbers are enough.
243 *
244 * This isn't in xf86Modes.c, but it might deserve to be there.
245 */
246_X_EXPORT Bool
247xf86ModesEqual(DisplayModePtr pMode1, DisplayModePtr pMode2)
248{
249     if (pMode1->Clock == pMode2->Clock &&
250	 pMode1->HDisplay == pMode2->HDisplay &&
251	 pMode1->HSyncStart == pMode2->HSyncStart &&
252	 pMode1->HSyncEnd == pMode2->HSyncEnd &&
253	 pMode1->HTotal == pMode2->HTotal &&
254	 pMode1->HSkew == pMode2->HSkew &&
255	 pMode1->VDisplay == pMode2->VDisplay &&
256	 pMode1->VSyncStart == pMode2->VSyncStart &&
257	 pMode1->VSyncEnd == pMode2->VSyncEnd &&
258	 pMode1->VTotal == pMode2->VTotal &&
259	 pMode1->VScan == pMode2->VScan &&
260	 pMode1->Flags == pMode2->Flags)
261     {
262	return TRUE;
263     } else {
264	return FALSE;
265     }
266}
267
268/* exact copy of xf86Mode.c */
269static void
270add(char **p, char *new)
271{
272    *p = xnfrealloc(*p, strlen(*p) + strlen(new) + 2);
273    strcat(*p, " ");
274    strcat(*p, new);
275}
276
277/**
278 * Print out a modeline.
279 *
280 * Convenient VRefresh printing was added, though, compared to xf86Mode.c
281 */
282_X_EXPORT void
283xf86PrintModeline(int scrnIndex,DisplayModePtr mode)
284{
285    char tmp[256];
286    char *flags = xnfcalloc(1, 1);
287
288    if (mode->HSkew) {
289	snprintf(tmp, 256, "hskew %i", mode->HSkew);
290	add(&flags, tmp);
291    }
292    if (mode->VScan) {
293	snprintf(tmp, 256, "vscan %i", mode->VScan);
294	add(&flags, tmp);
295    }
296    if (mode->Flags & V_INTERLACE) add(&flags, "interlace");
297    if (mode->Flags & V_CSYNC) add(&flags, "composite");
298    if (mode->Flags & V_DBLSCAN) add(&flags, "doublescan");
299    if (mode->Flags & V_BCAST) add(&flags, "bcast");
300    if (mode->Flags & V_PHSYNC) add(&flags, "+hsync");
301    if (mode->Flags & V_NHSYNC) add(&flags, "-hsync");
302    if (mode->Flags & V_PVSYNC) add(&flags, "+vsync");
303    if (mode->Flags & V_NVSYNC) add(&flags, "-vsync");
304    if (mode->Flags & V_PCSYNC) add(&flags, "+csync");
305    if (mode->Flags & V_NCSYNC) add(&flags, "-csync");
306#if 0
307    if (mode->Flags & V_CLKDIV2) add(&flags, "vclk/2");
308#endif
309    xf86DrvMsg(scrnIndex, X_INFO,
310		   "Modeline \"%s\"x%.01f  %6.2f  %i %i %i %i  %i %i %i %i%s "
311		   "(%.01f kHz)\n",
312		   mode->name, mode->VRefresh, mode->Clock/1000., mode->HDisplay,
313		   mode->HSyncStart, mode->HSyncEnd, mode->HTotal,
314		   mode->VDisplay, mode->VSyncStart, mode->VSyncEnd,
315		   mode->VTotal, flags, xf86ModeHSync(mode));
316    xfree(flags);
317}
318#endif /* XORG_VERSION_CURRENT <= 7.2.99.2 */
319
320/**
321 * Marks as bad any modes with unsupported flags.
322 *
323 * \param modeList doubly-linked or circular list of modes.
324 * \param flags flags supported by the driver.
325 *
326 * \bug only V_INTERLACE and V_DBLSCAN are supported.  Is that enough?
327 *
328 * This is not in xf86Modes.c, but would be part of the proposed new API.
329 */
330_X_EXPORT void
331xf86ValidateModesFlags(ScrnInfoPtr pScrn, DisplayModePtr modeList,
332			    int flags)
333{
334    DisplayModePtr mode;
335
336    for (mode = modeList; mode != NULL; mode = mode->next) {
337	if (mode->Flags & V_INTERLACE && !(flags & V_INTERLACE))
338	    mode->status = MODE_NO_INTERLACE;
339	if (mode->Flags & V_DBLSCAN && !(flags & V_DBLSCAN))
340	    mode->status = MODE_NO_DBLESCAN;
341    }
342}
343
344/**
345 * Marks as bad any modes extending beyond the given max X, Y, or pitch.
346 *
347 * \param modeList doubly-linked or circular list of modes.
348 *
349 * This is not in xf86Modes.c, but would be part of the proposed new API.
350 */
351_X_EXPORT void
352xf86ValidateModesSize(ScrnInfoPtr pScrn, DisplayModePtr modeList,
353			  int maxX, int maxY, int maxPitch)
354{
355    DisplayModePtr mode;
356
357    for (mode = modeList; mode != NULL; mode = mode->next) {
358	if (maxPitch > 0 && mode->HDisplay > maxPitch)
359	    mode->status = MODE_BAD_WIDTH;
360
361	if (maxX > 0 && mode->HDisplay > maxX)
362	    mode->status = MODE_VIRTUAL_X;
363
364	if (maxY > 0 && mode->VDisplay > maxY)
365	    mode->status = MODE_VIRTUAL_Y;
366
367	if (mode->next == modeList)
368	    break;
369    }
370}
371
372/**
373 * Marks as bad any modes that aren't supported by the given monitor's
374 * hsync and vrefresh ranges.
375 *
376 * \param modeList doubly-linked or circular list of modes.
377 *
378 * This is not in xf86Modes.c, but would be part of the proposed new API.
379 */
380_X_EXPORT void
381xf86ValidateModesSync(ScrnInfoPtr pScrn, DisplayModePtr modeList,
382			  MonPtr mon)
383{
384    DisplayModePtr mode;
385
386    for (mode = modeList; mode != NULL; mode = mode->next) {
387	Bool bad;
388	int i;
389
390	bad = TRUE;
391	for (i = 0; i < mon->nHsync; i++) {
392	    if (xf86ModeHSync(mode) >= mon->hsync[i].lo &&
393		xf86ModeHSync(mode) <= mon->hsync[i].hi)
394	    {
395		bad = FALSE;
396	    }
397	}
398	if (bad)
399	    mode->status = MODE_HSYNC;
400
401	bad = TRUE;
402	for (i = 0; i < mon->nVrefresh; i++) {
403	    if (xf86ModeVRefresh(mode) >= mon->vrefresh[i].lo &&
404		xf86ModeVRefresh(mode) <= mon->vrefresh[i].hi)
405	    {
406		bad = FALSE;
407	    }
408	}
409	if (bad)
410	    mode->status = MODE_VSYNC;
411
412	if (mode->next == modeList)
413	    break;
414    }
415}
416
417/**
418 * Marks as bad any modes extending beyond outside of the given clock ranges.
419 *
420 * \param modeList doubly-linked or circular list of modes.
421 * \param min pointer to minimums of clock ranges
422 * \param max pointer to maximums of clock ranges
423 * \param n_ranges number of ranges.
424 *
425 * This is not in xf86Modes.c, but would be part of the proposed new API.
426 */
427_X_EXPORT void
428xf86ValidateModesClocks(ScrnInfoPtr pScrn, DisplayModePtr modeList,
429			    int *min, int *max, int n_ranges)
430{
431    DisplayModePtr mode;
432    int i;
433
434    for (mode = modeList; mode != NULL; mode = mode->next) {
435	Bool good = FALSE;
436	for (i = 0; i < n_ranges; i++) {
437	    if (mode->Clock >= min[i] && mode->Clock <= max[i]) {
438		good = TRUE;
439		break;
440	    }
441	}
442	if (!good)
443	    mode->status = MODE_CLOCK_RANGE;
444    }
445}
446
447/**
448 * If the user has specified a set of mode names to use, mark as bad any modes
449 * not listed.
450 *
451 * The user mode names specified are prefixes to names of modes, so "1024x768"
452 * will match modes named "1024x768", "1024x768x75", "1024x768-good", but
453 * "1024x768x75" would only match "1024x768x75" from that list.
454 *
455 * MODE_BAD is used as the rejection flag, for lack of a better flag.
456 *
457 * \param modeList doubly-linked or circular list of modes.
458 *
459 * This is not in xf86Modes.c, but would be part of the proposed new API.
460 */
461_X_EXPORT void
462xf86ValidateModesUserConfig(ScrnInfoPtr pScrn, DisplayModePtr modeList)
463{
464    DisplayModePtr mode;
465
466    if (pScrn->display->modes[0] == NULL)
467	return;
468
469    for (mode = modeList; mode != NULL; mode = mode->next) {
470	int i;
471	Bool good = FALSE;
472
473	for (i = 0; pScrn->display->modes[i] != NULL; i++) {
474	    if (strncmp(pScrn->display->modes[i], mode->name,
475			strlen(pScrn->display->modes[i])) == 0) {
476		good = TRUE;
477		break;
478	    }
479	}
480	if (!good)
481	    mode->status = MODE_BAD;
482    }
483}
484
485
486/**
487 * Frees any modes from the list with a status other than MODE_OK.
488 *
489 * \param modeList pointer to a doubly-linked or circular list of modes.
490 * \param verbose determines whether the reason for mode invalidation is
491 *	  printed.
492 *
493 * This is not in xf86Modes.c, but would be part of the proposed new API.
494 */
495_X_EXPORT void
496xf86PruneInvalidModes(ScrnInfoPtr pScrn, DisplayModePtr *modeList,
497			  Bool verbose)
498{
499    DisplayModePtr mode;
500
501    for (mode = *modeList; mode != NULL;) {
502	DisplayModePtr next = mode->next, first = *modeList;
503
504	if (mode->status != MODE_OK) {
505	    if (verbose) {
506		char *type = "";
507		if (mode->type & M_T_BUILTIN)
508		    type = "built-in ";
509		else if (mode->type & M_T_DEFAULT)
510		    type = "default ";
511		xf86DrvMsg(pScrn->scrnIndex, X_INFO,
512			   "Not using %smode \"%s\" (%s)\n", type, mode->name,
513			   xf86ModeStatusToString(mode->status));
514	    }
515	    xf86DeleteMode(modeList, mode);
516	}
517
518	if (next == first)
519	    break;
520	mode = next;
521    }
522}
523
524/**
525 * Adds the new mode into the mode list, and returns the new list
526 *
527 * \param modes doubly-linked mode list.
528 */
529_X_EXPORT DisplayModePtr
530xf86ModesAdd(DisplayModePtr modes, DisplayModePtr new)
531{
532    if (modes == NULL)
533	return new;
534
535    if (new) {
536	DisplayModePtr mode = modes;
537
538	while (mode->next)
539	    mode = mode->next;
540
541	mode->next = new;
542	new->prev = mode;
543    }
544
545    return modes;
546}
547
548/**
549 * Build a mode list from a list of config file modes
550 */
551static DisplayModePtr
552xf86GetConfigModes (XF86ConfModeLinePtr conf_mode)
553{
554    DisplayModePtr  head = NULL, prev = NULL, mode;
555
556    for (; conf_mode; conf_mode = (XF86ConfModeLinePtr) conf_mode->list.next)
557    {
558        mode = xcalloc(1, sizeof(DisplayModeRec));
559	if (!mode)
560	    continue;
561        mode->name       = xstrdup(conf_mode->ml_identifier);
562	if (!mode->name)
563	{
564	    xfree (mode);
565	    continue;
566	}
567	mode->type       = 0;
568        mode->Clock      = conf_mode->ml_clock;
569        mode->HDisplay   = conf_mode->ml_hdisplay;
570        mode->HSyncStart = conf_mode->ml_hsyncstart;
571        mode->HSyncEnd   = conf_mode->ml_hsyncend;
572        mode->HTotal     = conf_mode->ml_htotal;
573        mode->VDisplay   = conf_mode->ml_vdisplay;
574        mode->VSyncStart = conf_mode->ml_vsyncstart;
575        mode->VSyncEnd   = conf_mode->ml_vsyncend;
576        mode->VTotal     = conf_mode->ml_vtotal;
577        mode->Flags      = conf_mode->ml_flags;
578        mode->HSkew      = conf_mode->ml_hskew;
579        mode->VScan      = conf_mode->ml_vscan;
580
581        mode->prev = prev;
582	mode->next = NULL;
583	if (prev)
584	    prev->next = mode;
585	else
586	    head = mode;
587	prev = mode;
588    }
589    return head;
590}
591
592/**
593 * Build a mode list from a monitor configuration
594 */
595_X_EXPORT DisplayModePtr
596xf86GetMonitorModes (ScrnInfoPtr pScrn, XF86ConfMonitorPtr conf_monitor)
597{
598    DisplayModePtr	    modes = NULL;
599    XF86ConfModesLinkPtr    modes_link;
600
601    if (!conf_monitor)
602	return NULL;
603
604    /*
605     * first we collect the mode lines from the UseModes directive
606     */
607    for (modes_link = conf_monitor->mon_modes_sect_lst;
608	 modes_link;
609	 modes_link = modes_link->list.next)
610    {
611	/* If this modes link hasn't been resolved, go look it up now */
612	if (!modes_link->ml_modes)
613	    modes_link->ml_modes = xf86findModes (modes_link->ml_modes_str,
614						  xf86configptr->conf_modes_lst);
615	if (modes_link->ml_modes)
616	    modes = xf86ModesAdd (modes,
617				  xf86GetConfigModes (modes_link->ml_modes->mon_modeline_lst));
618    }
619
620    return xf86ModesAdd (modes,
621			 xf86GetConfigModes (conf_monitor->mon_modeline_lst));
622}
623
624/**
625 * Build a mode list containing all of the default modes
626 */
627_X_EXPORT DisplayModePtr
628xf86GetDefaultModes (Bool interlaceAllowed, Bool doubleScanAllowed)
629{
630    DisplayModePtr  head = NULL, prev = NULL, mode;
631    int		    i;
632
633    for (i = 0; xf86DefaultModes[i].name != NULL; i++)
634    {
635	DisplayModePtr	defMode = &xf86DefaultModes[i];
636
637	if (!interlaceAllowed && (defMode->Flags & V_INTERLACE))
638	    continue;
639	if (!doubleScanAllowed && (defMode->Flags & V_DBLSCAN))
640	    continue;
641
642	mode = xalloc(sizeof(DisplayModeRec));
643	if (!mode)
644	    continue;
645        memcpy(mode,&xf86DefaultModes[i],sizeof(DisplayModeRec));
646        mode->name = xstrdup(xf86DefaultModes[i].name);
647        if (!mode->name)
648	{
649	    xfree (mode);
650	    continue;
651	}
652        mode->prev = prev;
653	mode->next = NULL;
654	if (prev)
655	    prev->next = mode;
656	else
657	    head = mode;
658	prev = mode;
659    }
660    return head;
661}
662