Imported Upstream version 1.15.1
[deb_xorg-server.git] / hw / xfree86 / modes / xf86Crtc.c
CommitLineData
a09e091a
JB
1/*
2 * Copyright © 2006 Keith Packard
3 * Copyright © 2008 Red Hat, Inc.
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that copyright
8 * notice and this permission notice appear in supporting documentation, and
9 * that the name of the copyright holders not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission. The copyright holders make no representations
12 * about the suitability of this software for any purpose. It is provided "as
13 * is" without express or implied warranty.
14 *
15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21 * OF THIS SOFTWARE.
22 */
23
24#ifdef HAVE_XORG_CONFIG_H
25#include <xorg-config.h>
26#else
27#ifdef HAVE_CONFIG_H
28#include <config.h>
29#endif
30#endif
31
32#include <stddef.h>
33#include <string.h>
34#include <stdio.h>
35
36#include "xf86.h"
37#include "xf86DDC.h"
38#include "xf86Crtc.h"
39#include "xf86Modes.h"
40#include "xf86Priv.h"
41#include "xf86RandR12.h"
42#include "X11/extensions/render.h"
43#include "X11/extensions/dpmsconst.h"
44#include "X11/Xatom.h"
45#include "picturestr.h"
46
47#ifdef XV
48#include "xf86xv.h"
49#endif
50
51#define NO_OUTPUT_DEFAULT_WIDTH 1024
52#define NO_OUTPUT_DEFAULT_HEIGHT 768
53/*
54 * Initialize xf86CrtcConfig structure
55 */
56
57int xf86CrtcConfigPrivateIndex = -1;
58
59void
60xf86CrtcConfigInit(ScrnInfoPtr scrn, const xf86CrtcConfigFuncsRec * funcs)
61{
62 xf86CrtcConfigPtr config;
63
64 if (xf86CrtcConfigPrivateIndex == -1)
65 xf86CrtcConfigPrivateIndex = xf86AllocateScrnInfoPrivateIndex();
66 config = xnfcalloc(1, sizeof(xf86CrtcConfigRec));
67
68 config->funcs = funcs;
69 config->compat_output = -1;
70
71 scrn->privates[xf86CrtcConfigPrivateIndex].ptr = config;
72}
73
74void
75xf86CrtcSetSizeRange(ScrnInfoPtr scrn,
76 int minWidth, int minHeight, int maxWidth, int maxHeight)
77{
78 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
79
80 config->minWidth = minWidth;
81 config->minHeight = minHeight;
82 config->maxWidth = maxWidth;
83 config->maxHeight = maxHeight;
84}
85
86/*
87 * Crtc functions
88 */
89xf86CrtcPtr
90xf86CrtcCreate(ScrnInfoPtr scrn, const xf86CrtcFuncsRec * funcs)
91{
92 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
93 xf86CrtcPtr crtc, *crtcs;
94
95 crtc = calloc(sizeof(xf86CrtcRec), 1);
96 if (!crtc)
97 return NULL;
98 crtc->version = XF86_CRTC_VERSION;
99 crtc->scrn = scrn;
100 crtc->funcs = funcs;
101#ifdef RANDR_12_INTERFACE
102 crtc->randr_crtc = NULL;
103#endif
104 crtc->rotation = RR_Rotate_0;
105 crtc->desiredRotation = RR_Rotate_0;
106 pixman_transform_init_identity(&crtc->crtc_to_framebuffer);
107 pixman_f_transform_init_identity(&crtc->f_crtc_to_framebuffer);
108 pixman_f_transform_init_identity(&crtc->f_framebuffer_to_crtc);
109 crtc->filter = NULL;
110 crtc->params = NULL;
111 crtc->nparams = 0;
112 crtc->filter_width = 0;
113 crtc->filter_height = 0;
114 crtc->transform_in_use = FALSE;
115 crtc->transformPresent = FALSE;
116 crtc->desiredTransformPresent = FALSE;
117 memset(&crtc->bounds, '\0', sizeof(crtc->bounds));
118
119 /* Preallocate gamma at a sensible size. */
120 crtc->gamma_size = 256;
121 crtc->gamma_red = malloc(3 * crtc->gamma_size * sizeof(CARD16));
122 if (!crtc->gamma_red) {
123 free(crtc);
124 return NULL;
125 }
126 crtc->gamma_green = crtc->gamma_red + crtc->gamma_size;
127 crtc->gamma_blue = crtc->gamma_green + crtc->gamma_size;
128
129 if (xf86_config->crtc)
130 crtcs = realloc(xf86_config->crtc,
131 (xf86_config->num_crtc + 1) * sizeof(xf86CrtcPtr));
132 else
133 crtcs = malloc((xf86_config->num_crtc + 1) * sizeof(xf86CrtcPtr));
134 if (!crtcs) {
135 free(crtc->gamma_red);
136 free(crtc);
137 return NULL;
138 }
139 xf86_config->crtc = crtcs;
140 xf86_config->crtc[xf86_config->num_crtc++] = crtc;
141 return crtc;
142}
143
144void
145xf86CrtcDestroy(xf86CrtcPtr crtc)
146{
147 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
148 int c;
149
150 (*crtc->funcs->destroy) (crtc);
151 for (c = 0; c < xf86_config->num_crtc; c++)
152 if (xf86_config->crtc[c] == crtc) {
153 memmove(&xf86_config->crtc[c],
154 &xf86_config->crtc[c + 1],
155 ((xf86_config->num_crtc - (c + 1)) * sizeof(void *)));
156 xf86_config->num_crtc--;
157 break;
158 }
159 free(crtc->params);
160 free(crtc->gamma_red);
161 free(crtc);
162}
163
164/**
165 * Return whether any outputs are connected to the specified pipe
166 */
167
168Bool
169xf86CrtcInUse(xf86CrtcPtr crtc)
170{
171 ScrnInfoPtr pScrn = crtc->scrn;
172 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
173 int o;
174
175 for (o = 0; o < xf86_config->num_output; o++)
176 if (xf86_config->output[o]->crtc == crtc)
177 return TRUE;
178 return FALSE;
179}
180
181void
182xf86CrtcSetScreenSubpixelOrder(ScreenPtr pScreen)
183{
184 int subpixel_order = SubPixelUnknown;
185 Bool has_none = FALSE;
186 ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
187 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
188 int c, o;
189
190 for (c = 0; c < xf86_config->num_crtc; c++) {
191 xf86CrtcPtr crtc = xf86_config->crtc[c];
192
193 for (o = 0; o < xf86_config->num_output; o++) {
194 xf86OutputPtr output = xf86_config->output[o];
195
196 if (output->crtc == crtc) {
197 switch (output->subpixel_order) {
198 case SubPixelNone:
199 has_none = TRUE;
200 break;
201 case SubPixelUnknown:
202 break;
203 default:
204 subpixel_order = output->subpixel_order;
205 break;
206 }
207 }
208 if (subpixel_order != SubPixelUnknown)
209 break;
210 }
211 if (subpixel_order != SubPixelUnknown) {
212 static const int circle[4] = {
213 SubPixelHorizontalRGB,
214 SubPixelVerticalRGB,
215 SubPixelHorizontalBGR,
216 SubPixelVerticalBGR,
217 };
218 int rotate;
219 int c;
220
221 for (rotate = 0; rotate < 4; rotate++)
222 if (crtc->rotation & (1 << rotate))
223 break;
224 for (c = 0; c < 4; c++)
225 if (circle[c] == subpixel_order)
226 break;
227 c = (c + rotate) & 0x3;
228 if ((crtc->rotation & RR_Reflect_X) && !(c & 1))
229 c ^= 2;
230 if ((crtc->rotation & RR_Reflect_Y) && (c & 1))
231 c ^= 2;
232 subpixel_order = circle[c];
233 break;
234 }
235 }
236 if (subpixel_order == SubPixelUnknown && has_none)
237 subpixel_order = SubPixelNone;
238 PictureSetSubpixelOrder(pScreen, subpixel_order);
239}
240
241/**
242 * Sets the given video mode on the given crtc
243 */
244Bool
245xf86CrtcSetModeTransform(xf86CrtcPtr crtc, DisplayModePtr mode,
246 Rotation rotation, RRTransformPtr transform, int x,
247 int y)
248{
249 ScrnInfoPtr scrn = crtc->scrn;
250 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
251 int i;
252 Bool ret = FALSE;
253 Bool didLock = FALSE;
254 DisplayModePtr adjusted_mode;
255 DisplayModeRec saved_mode;
256 int saved_x, saved_y;
257 Rotation saved_rotation;
258 RRTransformRec saved_transform;
259 Bool saved_transform_present;
260
261 crtc->enabled = xf86CrtcInUse(crtc);
262
263 /* We only hit this if someone explicitly sends a "disabled" modeset. */
264 if (!crtc->enabled) {
265 /* Check everything for stuff that should be off. */
266 xf86DisableUnusedFunctions(scrn);
267 return TRUE;
268 }
269
270 adjusted_mode = xf86DuplicateMode(mode);
271
272 saved_mode = crtc->mode;
273 saved_x = crtc->x;
274 saved_y = crtc->y;
275 saved_rotation = crtc->rotation;
276 if (crtc->transformPresent) {
277 RRTransformInit(&saved_transform);
278 RRTransformCopy(&saved_transform, &crtc->transform);
279 }
280 saved_transform_present = crtc->transformPresent;
281
282 /* Update crtc values up front so the driver can rely on them for mode
283 * setting.
284 */
285 crtc->mode = *mode;
286 crtc->x = x;
287 crtc->y = y;
288 crtc->rotation = rotation;
289 if (transform) {
290 RRTransformCopy(&crtc->transform, transform);
291 crtc->transformPresent = TRUE;
292 }
293 else
294 crtc->transformPresent = FALSE;
295
296 if (crtc->funcs->set_mode_major) {
297 ret = crtc->funcs->set_mode_major(crtc, mode, rotation, x, y);
298 goto done;
299 }
300
301 didLock = crtc->funcs->lock(crtc);
302 /* Pass our mode to the outputs and the CRTC to give them a chance to
303 * adjust it according to limitations or output properties, and also
304 * a chance to reject the mode entirely.
305 */
306 for (i = 0; i < xf86_config->num_output; i++) {
307 xf86OutputPtr output = xf86_config->output[i];
308
309 if (output->crtc != crtc)
310 continue;
311
312 if (!output->funcs->mode_fixup(output, mode, adjusted_mode)) {
313 goto done;
314 }
315 }
316
317 if (!crtc->funcs->mode_fixup(crtc, mode, adjusted_mode)) {
318 goto done;
319 }
320
321 if (!xf86CrtcRotate(crtc))
322 goto done;
323
324 /* Prepare the outputs and CRTCs before setting the mode. */
325 for (i = 0; i < xf86_config->num_output; i++) {
326 xf86OutputPtr output = xf86_config->output[i];
327
328 if (output->crtc != crtc)
329 continue;
330
331 /* Disable the output as the first thing we do. */
332 output->funcs->prepare(output);
333 }
334
335 crtc->funcs->prepare(crtc);
336
337 /* Set up the DPLL and any output state that needs to adjust or depend
338 * on the DPLL.
339 */
340 crtc->funcs->mode_set(crtc, mode, adjusted_mode, crtc->x, crtc->y);
341 for (i = 0; i < xf86_config->num_output; i++) {
342 xf86OutputPtr output = xf86_config->output[i];
343
344 if (output->crtc == crtc)
345 output->funcs->mode_set(output, mode, adjusted_mode);
346 }
347
348 /* Only upload when needed, to avoid unneeded delays. */
349 if (!crtc->active && crtc->funcs->gamma_set)
350 crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
351 crtc->gamma_blue, crtc->gamma_size);
352
353 /* Now, enable the clocks, plane, pipe, and outputs that we set up. */
354 crtc->funcs->commit(crtc);
355 for (i = 0; i < xf86_config->num_output; i++) {
356 xf86OutputPtr output = xf86_config->output[i];
357
358 if (output->crtc == crtc)
359 output->funcs->commit(output);
360 }
361
362 ret = TRUE;
363
364 done:
365 if (ret) {
366 crtc->active = TRUE;
367 if (scrn->pScreen)
368 xf86CrtcSetScreenSubpixelOrder(scrn->pScreen);
369 if (scrn->ModeSet)
370 scrn->ModeSet(scrn);
371 }
372 else {
373 crtc->x = saved_x;
374 crtc->y = saved_y;
375 crtc->rotation = saved_rotation;
376 crtc->mode = saved_mode;
377 if (saved_transform_present)
378 RRTransformCopy(&crtc->transform, &saved_transform);
379 crtc->transformPresent = saved_transform_present;
380 }
381
382 free(adjusted_mode->name);
383 free(adjusted_mode);
384
385 if (didLock)
386 crtc->funcs->unlock(crtc);
387
388 return ret;
389}
390
391/**
392 * Sets the given video mode on the given crtc, but without providing
393 * a transform
394 */
395Bool
396xf86CrtcSetMode(xf86CrtcPtr crtc, DisplayModePtr mode, Rotation rotation,
397 int x, int y)
398{
399 return xf86CrtcSetModeTransform(crtc, mode, rotation, NULL, x, y);
400}
401
402/**
403 * Pans the screen, does not change the mode
404 */
405void
406xf86CrtcSetOrigin(xf86CrtcPtr crtc, int x, int y)
407{
408 ScrnInfoPtr scrn = crtc->scrn;
409
410 crtc->x = x;
411 crtc->y = y;
412 if (crtc->funcs->set_origin) {
413 if (!xf86CrtcRotate(crtc))
414 return;
415 crtc->funcs->set_origin(crtc, x, y);
416 if (scrn->ModeSet)
417 scrn->ModeSet(scrn);
418 }
419 else
420 xf86CrtcSetMode(crtc, &crtc->mode, crtc->rotation, x, y);
421}
422
423/*
424 * Output functions
425 */
426
427extern XF86ConfigPtr xf86configptr;
428
429typedef enum {
430 OPTION_PREFERRED_MODE,
431 OPTION_ZOOM_MODES,
432 OPTION_POSITION,
433 OPTION_BELOW,
434 OPTION_RIGHT_OF,
435 OPTION_ABOVE,
436 OPTION_LEFT_OF,
437 OPTION_ENABLE,
438 OPTION_DISABLE,
439 OPTION_MIN_CLOCK,
440 OPTION_MAX_CLOCK,
441 OPTION_IGNORE,
442 OPTION_ROTATE,
443 OPTION_PANNING,
444 OPTION_PRIMARY,
445 OPTION_DEFAULT_MODES,
446} OutputOpts;
447
448static OptionInfoRec xf86OutputOptions[] = {
449 {OPTION_PREFERRED_MODE, "PreferredMode", OPTV_STRING, {0}, FALSE},
450 {OPTION_ZOOM_MODES, "ZoomModes", OPTV_STRING, {0}, FALSE },
451 {OPTION_POSITION, "Position", OPTV_STRING, {0}, FALSE},
452 {OPTION_BELOW, "Below", OPTV_STRING, {0}, FALSE},
453 {OPTION_RIGHT_OF, "RightOf", OPTV_STRING, {0}, FALSE},
454 {OPTION_ABOVE, "Above", OPTV_STRING, {0}, FALSE},
455 {OPTION_LEFT_OF, "LeftOf", OPTV_STRING, {0}, FALSE},
456 {OPTION_ENABLE, "Enable", OPTV_BOOLEAN, {0}, FALSE},
457 {OPTION_DISABLE, "Disable", OPTV_BOOLEAN, {0}, FALSE},
458 {OPTION_MIN_CLOCK, "MinClock", OPTV_FREQ, {0}, FALSE},
459 {OPTION_MAX_CLOCK, "MaxClock", OPTV_FREQ, {0}, FALSE},
460 {OPTION_IGNORE, "Ignore", OPTV_BOOLEAN, {0}, FALSE},
461 {OPTION_ROTATE, "Rotate", OPTV_STRING, {0}, FALSE},
462 {OPTION_PANNING, "Panning", OPTV_STRING, {0}, FALSE},
463 {OPTION_PRIMARY, "Primary", OPTV_BOOLEAN, {0}, FALSE},
464 {OPTION_DEFAULT_MODES, "DefaultModes", OPTV_BOOLEAN, {0}, FALSE},
465 {-1, NULL, OPTV_NONE, {0}, FALSE},
466};
467
468enum {
469 OPTION_MODEDEBUG,
470};
471
472static OptionInfoRec xf86DeviceOptions[] = {
473 {OPTION_MODEDEBUG, "ModeDebug", OPTV_BOOLEAN, {0}, FALSE},
474 {-1, NULL, OPTV_NONE, {0}, FALSE},
475};
476
477static void
478xf86OutputSetMonitor(xf86OutputPtr output)
479{
480 char *option_name;
481 const char *monitor;
482
483 if (!output->name)
484 return;
485
486 free(output->options);
487
488 output->options = xnfalloc(sizeof(xf86OutputOptions));
489 memcpy(output->options, xf86OutputOptions, sizeof(xf86OutputOptions));
490
491 XNFasprintf(&option_name, "monitor-%s", output->name);
492 monitor = xf86findOptionValue(output->scrn->options, option_name);
493 if (!monitor)
494 monitor = output->name;
495 else
496 xf86MarkOptionUsedByName(output->scrn->options, option_name);
497 free(option_name);
498 output->conf_monitor = xf86findMonitor(monitor,
499 xf86configptr->conf_monitor_lst);
500 /*
501 * Find the monitor section of the screen and use that
502 */
503 if (!output->conf_monitor && output->use_screen_monitor)
504 output->conf_monitor = xf86findMonitor(output->scrn->monitor->id,
505 xf86configptr->conf_monitor_lst);
506 if (output->conf_monitor) {
507 xf86DrvMsg(output->scrn->scrnIndex, X_INFO,
508 "Output %s using monitor section %s\n",
509 output->name, output->conf_monitor->mon_identifier);
510 xf86ProcessOptions(output->scrn->scrnIndex,
511 output->conf_monitor->mon_option_lst,
512 output->options);
513 }
514 else
515 xf86DrvMsg(output->scrn->scrnIndex, X_INFO,
516 "Output %s has no monitor section\n", output->name);
517}
518
519static Bool
520xf86OutputEnabled(xf86OutputPtr output, Bool strict)
521{
522 Bool enable, disable;
523
524 /* check to see if this output was enabled in the config file */
525 if (xf86GetOptValBool(output->options, OPTION_ENABLE, &enable) && enable) {
526 xf86DrvMsg(output->scrn->scrnIndex, X_INFO,
527 "Output %s enabled by config file\n", output->name);
528 return TRUE;
529 }
530 /* or if this output was disabled in the config file */
531 if (xf86GetOptValBool(output->options, OPTION_DISABLE, &disable) && disable) {
532 xf86DrvMsg(output->scrn->scrnIndex, X_INFO,
533 "Output %s disabled by config file\n", output->name);
534 return FALSE;
535 }
536
537 /* If not, try to only light up the ones we know are connected */
538 if (strict) {
539 enable = output->status == XF86OutputStatusConnected;
540 }
541 /* But if that fails, try to light up even outputs we're unsure of */
542 else {
543 enable = output->status != XF86OutputStatusDisconnected;
544 }
545
546 xf86DrvMsg(output->scrn->scrnIndex, X_INFO,
547 "Output %s %sconnected\n", output->name, enable ? "" : "dis");
548 return enable;
549}
550
551static Bool
552xf86OutputIgnored(xf86OutputPtr output)
553{
554 return xf86ReturnOptValBool(output->options, OPTION_IGNORE, FALSE);
555}
556
557static const char *direction[4] = {
558 "normal",
559 "left",
560 "inverted",
561 "right"
562};
563
564static Rotation
565xf86OutputInitialRotation(xf86OutputPtr output)
566{
567 char *rotate_name = xf86GetOptValString(output->options,
568 OPTION_ROTATE);
569 int i;
570
571 if (!rotate_name) {
572 if (output->initial_rotation)
573 return output->initial_rotation;
574 return RR_Rotate_0;
575 }
576
577 for (i = 0; i < 4; i++)
578 if (xf86nameCompare(direction[i], rotate_name) == 0)
579 return 1 << i;
580 return RR_Rotate_0;
581}
582
583xf86OutputPtr
584xf86OutputCreate(ScrnInfoPtr scrn,
585 const xf86OutputFuncsRec * funcs, const char *name)
586{
587 xf86OutputPtr output, *outputs;
588 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
589 int len;
590 Bool primary;
591
592 if (name)
593 len = strlen(name) + 1;
594 else
595 len = 0;
596
597 output = calloc(sizeof(xf86OutputRec) + len, 1);
598 if (!output)
599 return NULL;
600 output->scrn = scrn;
601 output->funcs = funcs;
602 if (name) {
603 output->name = (char *) (output + 1);
604 strcpy(output->name, name);
605 }
606 output->subpixel_order = SubPixelUnknown;
607 /*
608 * Use the old per-screen monitor section for the first output
609 */
610 output->use_screen_monitor = (xf86_config->num_output == 0);
611#ifdef RANDR_12_INTERFACE
612 output->randr_output = NULL;
613#endif
614 if (name) {
615 xf86OutputSetMonitor(output);
616 if (xf86OutputIgnored(output)) {
617 free(output);
618 return FALSE;
619 }
620 }
621
622 if (xf86_config->output)
623 outputs = realloc(xf86_config->output,
624 (xf86_config->num_output +
625 1) * sizeof(xf86OutputPtr));
626 else
627 outputs = malloc((xf86_config->num_output + 1) * sizeof(xf86OutputPtr));
628 if (!outputs) {
629 free(output);
630 return NULL;
631 }
632
633 xf86_config->output = outputs;
634
635 if (xf86GetOptValBool(output->options, OPTION_PRIMARY, &primary) && primary) {
636 memmove(xf86_config->output + 1, xf86_config->output,
637 xf86_config->num_output * sizeof(xf86OutputPtr));
638 xf86_config->output[0] = output;
639 }
640 else {
641 xf86_config->output[xf86_config->num_output] = output;
642 }
643
644 xf86_config->num_output++;
645
646 return output;
647}
648
649Bool
650xf86OutputRename(xf86OutputPtr output, const char *name)
651{
652 char *newname = strdup(name);
653
654 if (!newname)
655 return FALSE; /* so sorry... */
656
657 if (output->name && output->name != (char *) (output + 1))
658 free(output->name);
659 output->name = newname;
660 xf86OutputSetMonitor(output);
661 if (xf86OutputIgnored(output))
662 return FALSE;
663 return TRUE;
664}
665
666void
667xf86OutputUseScreenMonitor(xf86OutputPtr output, Bool use_screen_monitor)
668{
669 if (use_screen_monitor != output->use_screen_monitor) {
670 output->use_screen_monitor = use_screen_monitor;
671 xf86OutputSetMonitor(output);
672 }
673}
674
675void
676xf86OutputDestroy(xf86OutputPtr output)
677{
678 ScrnInfoPtr scrn = output->scrn;
679 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
680 int o;
681
682 (*output->funcs->destroy) (output);
683 while (output->probed_modes)
684 xf86DeleteMode(&output->probed_modes, output->probed_modes);
685 for (o = 0; o < xf86_config->num_output; o++)
686 if (xf86_config->output[o] == output) {
687 memmove(&xf86_config->output[o],
688 &xf86_config->output[o + 1],
689 ((xf86_config->num_output - (o + 1)) * sizeof(void *)));
690 xf86_config->num_output--;
691 break;
692 }
693 if (output->name && output->name != (char *) (output + 1))
694 free(output->name);
695 free(output);
696}
697
698/*
699 * Called during CreateScreenResources to hook up RandR
700 */
701static Bool
702xf86CrtcCreateScreenResources(ScreenPtr screen)
703{
704 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
705 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
706
707 screen->CreateScreenResources = config->CreateScreenResources;
708
709 if (!(*screen->CreateScreenResources) (screen))
710 return FALSE;
711
712 if (!xf86RandR12CreateScreenResources(screen))
713 return FALSE;
714
715 return TRUE;
716}
717
718/*
719 * Clean up config on server reset
720 */
721static Bool
722xf86CrtcCloseScreen(ScreenPtr screen)
723{
724 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
725 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
726 int o, c;
727
728 screen->CloseScreen = config->CloseScreen;
729
730 xf86RotateCloseScreen(screen);
731
732 xf86RandR12CloseScreen(screen);
733
734 screen->CloseScreen(screen);
735
736 for (o = 0; o < config->num_output; o++) {
737 xf86OutputPtr output = config->output[o];
738
739 output->randr_output = NULL;
740 }
741 for (c = 0; c < config->num_crtc; c++) {
742 xf86CrtcPtr crtc = config->crtc[c];
743
744 crtc->randr_crtc = NULL;
745 }
746 /* detach any providers */
747 if (config->randr_provider) {
748 RRProviderDestroy(config->randr_provider);
749 config->randr_provider = NULL;
750 }
751 return TRUE;
752}
753
754/*
755 * Called at ScreenInit time to set up
756 */
757#ifdef RANDR_13_INTERFACE
758int
759#else
760Bool
761#endif
762xf86CrtcScreenInit(ScreenPtr screen)
763{
764 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
765 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
766 int c;
767
768 /* Rotation */
769 xf86DrvMsg(scrn->scrnIndex, X_INFO,
770 "RandR 1.2 enabled, ignore the following RandR disabled message.\n");
771 xf86DisableRandR(); /* Disable old RandR extension support */
772 xf86RandR12Init(screen);
773
774 /* support all rotations if every crtc has the shadow alloc funcs */
775 for (c = 0; c < config->num_crtc; c++) {
776 xf86CrtcPtr crtc = config->crtc[c];
777
778 if (!crtc->funcs->shadow_allocate || !crtc->funcs->shadow_create)
779 break;
780 }
781 if (c == config->num_crtc) {
782 xf86RandR12SetRotations(screen, RR_Rotate_0 | RR_Rotate_90 |
783 RR_Rotate_180 | RR_Rotate_270 |
784 RR_Reflect_X | RR_Reflect_Y);
785 xf86RandR12SetTransformSupport(screen, TRUE);
786 }
787 else {
788 xf86RandR12SetRotations(screen, RR_Rotate_0);
789 xf86RandR12SetTransformSupport(screen, FALSE);
790 }
791
792 /* Wrap CreateScreenResources so we can initialize the RandR code */
793 config->CreateScreenResources = screen->CreateScreenResources;
794 screen->CreateScreenResources = xf86CrtcCreateScreenResources;
795
796 config->CloseScreen = screen->CloseScreen;
797 screen->CloseScreen = xf86CrtcCloseScreen;
798
799 /* This might still be marked wrapped from a previous generation */
800 config->BlockHandler = NULL;
801
802#ifdef XFreeXDGA
803 _xf86_di_dga_init_internal(screen);
804#endif
805#ifdef RANDR_13_INTERFACE
806 return RANDR_INTERFACE_VERSION;
807#else
808 return TRUE;
809#endif
810}
811
812static DisplayModePtr
813xf86DefaultMode(xf86OutputPtr output, int width, int height)
814{
815 DisplayModePtr target_mode = NULL;
816 DisplayModePtr mode;
817 int target_diff = 0;
818 int target_preferred = 0;
819 int mm_height;
820
821 mm_height = output->mm_height;
822 if (!mm_height)
823 mm_height = (768 * 25.4) / DEFAULT_DPI;
824 /*
825 * Pick a mode closest to DEFAULT_DPI
826 */
827 for (mode = output->probed_modes; mode; mode = mode->next) {
828 int dpi;
829 int preferred = (((mode->type & M_T_PREFERRED) != 0) +
830 ((mode->type & M_T_USERPREF) != 0));
831 int diff;
832
833 if (xf86ModeWidth(mode, output->initial_rotation) > width ||
834 xf86ModeHeight(mode, output->initial_rotation) > height)
835 continue;
836
837 /* yes, use VDisplay here, not xf86ModeHeight */
838 dpi = (mode->VDisplay * 254) / (mm_height * 10);
839 diff = dpi - DEFAULT_DPI;
840 diff = diff < 0 ? -diff : diff;
841 if (target_mode == NULL || (preferred > target_preferred) ||
842 (preferred == target_preferred && diff < target_diff)) {
843 target_mode = mode;
844 target_diff = diff;
845 target_preferred = preferred;
846 }
847 }
848 return target_mode;
849}
850
851static DisplayModePtr
852xf86ClosestMode(xf86OutputPtr output,
853 DisplayModePtr match, Rotation match_rotation,
854 int width, int height)
855{
856 DisplayModePtr target_mode = NULL;
857 DisplayModePtr mode;
858 int target_diff = 0;
859
860 /*
861 * Pick a mode closest to the specified mode
862 */
863 for (mode = output->probed_modes; mode; mode = mode->next) {
864 int dx, dy;
865 int diff;
866
867 if (xf86ModeWidth(mode, output->initial_rotation) > width ||
868 xf86ModeHeight(mode, output->initial_rotation) > height)
869 continue;
870
871 /* exact matches are preferred */
872 if (output->initial_rotation == match_rotation &&
873 xf86ModesEqual(mode, match))
874 return mode;
875
876 dx = xf86ModeWidth(match, match_rotation) - xf86ModeWidth(mode,
877 output->
878 initial_rotation);
879 dy = xf86ModeHeight(match, match_rotation) - xf86ModeHeight(mode,
880 output->
881 initial_rotation);
882 diff = dx * dx + dy * dy;
883 if (target_mode == NULL || diff < target_diff) {
884 target_mode = mode;
885 target_diff = diff;
886 }
887 }
888 return target_mode;
889}
890
891static DisplayModePtr
892xf86OutputHasPreferredMode(xf86OutputPtr output, int width, int height)
893{
894 DisplayModePtr mode;
895
896 for (mode = output->probed_modes; mode; mode = mode->next) {
897 if (xf86ModeWidth(mode, output->initial_rotation) > width ||
898 xf86ModeHeight(mode, output->initial_rotation) > height)
899 continue;
900
901 if (mode->type & M_T_PREFERRED)
902 return mode;
903 }
904 return NULL;
905}
906
907static DisplayModePtr
908xf86OutputHasUserPreferredMode(xf86OutputPtr output)
909{
910 DisplayModePtr mode, first = output->probed_modes;
911
912 for (mode = first; mode && mode->next != first; mode = mode->next)
913 if (mode->type & M_T_USERPREF)
914 return mode;
915
916 return NULL;
917}
918
919static int
920xf86PickCrtcs(ScrnInfoPtr scrn,
921 xf86CrtcPtr * best_crtcs,
922 DisplayModePtr * modes, int n, int width, int height)
923{
924 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
925 int c, o;
926 xf86OutputPtr output;
927 xf86CrtcPtr crtc;
928 xf86CrtcPtr *crtcs;
929 xf86CrtcPtr best_crtc;
930 int best_score;
931 int score;
932 int my_score;
933
934 if (n == config->num_output)
935 return 0;
936 output = config->output[n];
937
938 /*
939 * Compute score with this output disabled
940 */
941 best_crtcs[n] = NULL;
942 best_crtc = NULL;
943 best_score = xf86PickCrtcs(scrn, best_crtcs, modes, n + 1, width, height);
944 if (modes[n] == NULL)
945 return best_score;
946
947 crtcs = malloc(config->num_output * sizeof(xf86CrtcPtr));
948 if (!crtcs)
949 return best_score;
950
951 my_score = 1;
952 /* Score outputs that are known to be connected higher */
953 if (output->status == XF86OutputStatusConnected)
954 my_score++;
955 /* Score outputs with preferred modes higher */
956 if (xf86OutputHasPreferredMode(output, width, height))
957 my_score++;
958 /*
959 * Select a crtc for this output and
960 * then attempt to configure the remaining
961 * outputs
962 */
963 for (c = 0; c < config->num_crtc; c++) {
964 if ((output->possible_crtcs & (1 << c)) == 0)
965 continue;
966
967 crtc = config->crtc[c];
968 /*
969 * Check to see if some other output is
970 * using this crtc
971 */
972 for (o = 0; o < n; o++)
973 if (best_crtcs[o] == crtc)
974 break;
975 if (o < n) {
976 /*
977 * If the two outputs desire the same mode,
978 * see if they can be cloned
979 */
980 if (xf86ModesEqual(modes[o], modes[n]) &&
981 config->output[o]->initial_rotation ==
982 config->output[n]->initial_rotation &&
983 config->output[o]->initial_x == config->output[n]->initial_x &&
984 config->output[o]->initial_y == config->output[n]->initial_y) {
985 if ((output->possible_clones & (1 << o)) == 0)
986 continue; /* nope, try next CRTC */
987 }
988 else
989 continue; /* different modes, can't clone */
990 }
991 crtcs[n] = crtc;
992 memcpy(crtcs, best_crtcs, n * sizeof(xf86CrtcPtr));
993 score =
994 my_score + xf86PickCrtcs(scrn, crtcs, modes, n + 1, width, height);
995 if (score > best_score) {
996 best_crtc = crtc;
997 best_score = score;
998 memcpy(best_crtcs, crtcs, config->num_output * sizeof(xf86CrtcPtr));
999 }
1000 }
1001 free(crtcs);
1002 return best_score;
1003}
1004
1005/*
1006 * Compute the virtual size necessary to place all of the available
1007 * crtcs in the specified configuration.
1008 *
1009 * canGrow indicates that the driver can make the screen larger than its initial
1010 * configuration. If FALSE, this function will enlarge the screen to include
1011 * the largest available mode.
1012 */
1013
1014static void
1015xf86DefaultScreenLimits(ScrnInfoPtr scrn, int *widthp, int *heightp,
1016 Bool canGrow)
1017{
1018 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
1019 int width = 0, height = 0;
1020 int o;
1021 int c;
1022 int s;
1023
1024 for (c = 0; c < config->num_crtc; c++) {
1025 int crtc_width = 0, crtc_height = 0;
1026 xf86CrtcPtr crtc = config->crtc[c];
1027
1028 if (crtc->enabled) {
1029 crtc_width =
1030 crtc->desiredX + xf86ModeWidth(&crtc->desiredMode,
1031 crtc->desiredRotation);
1032 crtc_height =
1033 crtc->desiredY + xf86ModeHeight(&crtc->desiredMode,
1034 crtc->desiredRotation);
1035 }
1036 if (!canGrow) {
1037 for (o = 0; o < config->num_output; o++) {
1038 xf86OutputPtr output = config->output[o];
1039
1040 for (s = 0; s < config->num_crtc; s++)
1041 if (output->possible_crtcs & (1 << s)) {
1042 DisplayModePtr mode;
1043
1044 for (mode = output->probed_modes; mode;
1045 mode = mode->next) {
1046 if (mode->HDisplay > crtc_width)
1047 crtc_width = mode->HDisplay;
1048 if (mode->VDisplay > crtc_width)
1049 crtc_width = mode->VDisplay;
1050 if (mode->VDisplay > crtc_height)
1051 crtc_height = mode->VDisplay;
1052 if (mode->HDisplay > crtc_height)
1053 crtc_height = mode->HDisplay;
1054 }
1055 }
1056 }
1057 }
1058 if (crtc_width > width)
1059 width = crtc_width;
1060 if (crtc_height > height)
1061 height = crtc_height;
1062 }
1063 if (config->maxWidth && width > config->maxWidth)
1064 width = config->maxWidth;
1065 if (config->maxHeight && height > config->maxHeight)
1066 height = config->maxHeight;
1067 if (config->minWidth && width < config->minWidth)
1068 width = config->minWidth;
1069 if (config->minHeight && height < config->minHeight)
1070 height = config->minHeight;
1071 *widthp = width;
1072 *heightp = height;
1073}
1074
1075#define POSITION_UNSET -100000
1076
1077/*
1078 * check if the user configured any outputs at all
1079 * with either a position or a relative setting or a mode.
1080 */
1081static Bool
1082xf86UserConfiguredOutputs(ScrnInfoPtr scrn, DisplayModePtr * modes)
1083{
1084 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
1085 int o;
1086 Bool user_conf = FALSE;
1087
1088 for (o = 0; o < config->num_output; o++) {
1089 xf86OutputPtr output = config->output[o];
1090 char *position;
1091 char *relative_name;
1092 OutputOpts relation;
1093 int r;
1094
1095 static const OutputOpts relations[] = {
1096 OPTION_BELOW, OPTION_RIGHT_OF, OPTION_ABOVE, OPTION_LEFT_OF
1097 };
1098
1099 position = xf86GetOptValString(output->options, OPTION_POSITION);
1100 if (position)
1101 user_conf = TRUE;
1102
1103 relation = 0;
1104 relative_name = NULL;
1105 for (r = 0; r < 4; r++) {
1106 relation = relations[r];
1107 relative_name = xf86GetOptValString(output->options, relation);
1108 if (relative_name)
1109 break;
1110 }
1111 if (relative_name)
1112 user_conf = TRUE;
1113
1114 modes[o] = xf86OutputHasUserPreferredMode(output);
1115 if (modes[o])
1116 user_conf = TRUE;
1117 }
1118
1119 return user_conf;
1120}
1121
1122static Bool
1123xf86InitialOutputPositions(ScrnInfoPtr scrn, DisplayModePtr * modes)
1124{
1125 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
1126 int o;
1127 int min_x, min_y;
1128
1129 for (o = 0; o < config->num_output; o++) {
1130 xf86OutputPtr output = config->output[o];
1131
1132 output->initial_x = output->initial_y = POSITION_UNSET;
1133 }
1134
1135 /*
1136 * Loop until all outputs are set
1137 */
1138 for (;;) {
1139 Bool any_set = FALSE;
1140 Bool keep_going = FALSE;
1141
1142 for (o = 0; o < config->num_output; o++) {
1143 static const OutputOpts relations[] = {
1144 OPTION_BELOW, OPTION_RIGHT_OF, OPTION_ABOVE, OPTION_LEFT_OF
1145 };
1146 xf86OutputPtr output = config->output[o];
1147 xf86OutputPtr relative;
1148 char *relative_name;
1149 char *position;
1150 OutputOpts relation;
1151 int r;
1152
1153 if (output->initial_x != POSITION_UNSET)
1154 continue;
1155 position = xf86GetOptValString(output->options, OPTION_POSITION);
1156 /*
1157 * Absolute position wins
1158 */
1159 if (position) {
1160 int x, y;
1161
1162 if (sscanf(position, "%d %d", &x, &y) == 2) {
1163 output->initial_x = x;
1164 output->initial_y = y;
1165 }
1166 else {
1167 xf86DrvMsg(scrn->scrnIndex, X_ERROR,
1168 "Output %s position not of form \"x y\"\n",
1169 output->name);
1170 output->initial_x = output->initial_y = 0;
1171 }
1172 any_set = TRUE;
1173 continue;
1174 }
1175 /*
1176 * Next comes relative positions
1177 */
1178 relation = 0;
1179 relative_name = NULL;
1180 for (r = 0; r < 4; r++) {
1181 relation = relations[r];
1182 relative_name = xf86GetOptValString(output->options, relation);
1183 if (relative_name)
1184 break;
1185 }
1186 if (relative_name) {
1187 int or;
1188
1189 relative = NULL;
1190 for (or = 0; or < config->num_output; or++) {
1191 xf86OutputPtr out_rel = config->output[or];
1192 XF86ConfMonitorPtr rel_mon = out_rel->conf_monitor;
1193
1194 if (rel_mon) {
1195 if (xf86nameCompare(rel_mon->mon_identifier,
1196 relative_name) == 0) {
1197 relative = config->output[or];
1198 break;
1199 }
1200 }
1201 if (strcmp(out_rel->name, relative_name) == 0) {
1202 relative = config->output[or];
1203 break;
1204 }
1205 }
1206 if (!relative) {
1207 xf86DrvMsg(scrn->scrnIndex, X_ERROR,
1208 "Cannot position output %s relative to unknown output %s\n",
1209 output->name, relative_name);
1210 output->initial_x = 0;
1211 output->initial_y = 0;
1212 any_set = TRUE;
1213 continue;
1214 }
1215 if (!modes[or]) {
1216 xf86DrvMsg(scrn->scrnIndex, X_ERROR,
1217 "Cannot position output %s relative to output %s without modes\n",
1218 output->name, relative_name);
1219 output->initial_x = 0;
1220 output->initial_y = 0;
1221 any_set = TRUE;
1222 continue;
1223 }
1224 if (relative->initial_x == POSITION_UNSET) {
1225 keep_going = TRUE;
1226 continue;
1227 }
1228 output->initial_x = relative->initial_x;
1229 output->initial_y = relative->initial_y;
1230 switch (relation) {
1231 case OPTION_BELOW:
1232 output->initial_y +=
1233 xf86ModeHeight(modes[or], relative->initial_rotation);
1234 break;
1235 case OPTION_RIGHT_OF:
1236 output->initial_x +=
1237 xf86ModeWidth(modes[or], relative->initial_rotation);
1238 break;
1239 case OPTION_ABOVE:
1240 if (modes[o])
1241 output->initial_y -=
1242 xf86ModeHeight(modes[o], output->initial_rotation);
1243 break;
1244 case OPTION_LEFT_OF:
1245 if (modes[o])
1246 output->initial_x -=
1247 xf86ModeWidth(modes[o], output->initial_rotation);
1248 break;
1249 default:
1250 break;
1251 }
1252 any_set = TRUE;
1253 continue;
1254 }
1255
1256 /* Nothing set, just stick them at 0,0 */
1257 output->initial_x = 0;
1258 output->initial_y = 0;
1259 any_set = TRUE;
1260 }
1261 if (!keep_going)
1262 break;
1263 if (!any_set) {
1264 for (o = 0; o < config->num_output; o++) {
1265 xf86OutputPtr output = config->output[o];
1266
1267 if (output->initial_x == POSITION_UNSET) {
1268 xf86DrvMsg(scrn->scrnIndex, X_ERROR,
1269 "Output position loop. Moving %s to 0,0\n",
1270 output->name);
1271 output->initial_x = output->initial_y = 0;
1272 break;
1273 }
1274 }
1275 }
1276 }
1277
1278 /*
1279 * normalize positions
1280 */
1281 min_x = 1000000;
1282 min_y = 1000000;
1283 for (o = 0; o < config->num_output; o++) {
1284 xf86OutputPtr output = config->output[o];
1285
1286 if (output->initial_x < min_x)
1287 min_x = output->initial_x;
1288 if (output->initial_y < min_y)
1289 min_y = output->initial_y;
1290 }
1291
1292 for (o = 0; o < config->num_output; o++) {
1293 xf86OutputPtr output = config->output[o];
1294
1295 output->initial_x -= min_x;
1296 output->initial_y -= min_y;
1297 }
1298 return TRUE;
1299}
1300
1301static void
1302xf86InitialPanning(ScrnInfoPtr scrn)
1303{
1304 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
1305 int o;
1306
1307 for (o = 0; o < config->num_output; o++) {
1308 xf86OutputPtr output = config->output[o];
1309 char *panning = xf86GetOptValString(output->options, OPTION_PANNING);
1310 int width, height, left, top;
1311 int track_width, track_height, track_left, track_top;
1312 int brdr[4];
1313
1314 memset(&output->initialTotalArea, 0, sizeof(BoxRec));
1315 memset(&output->initialTrackingArea, 0, sizeof(BoxRec));
1316 memset(output->initialBorder, 0, 4 * sizeof(INT16));
1317
1318 if (!panning)
1319 continue;
1320
1321 switch (sscanf(panning, "%dx%d+%d+%d/%dx%d+%d+%d/%d/%d/%d/%d",
1322 &width, &height, &left, &top,
1323 &track_width, &track_height, &track_left, &track_top,
1324 &brdr[0], &brdr[1], &brdr[2], &brdr[3])) {
1325 case 12:
1326 output->initialBorder[0] = brdr[0];
1327 output->initialBorder[1] = brdr[1];
1328 output->initialBorder[2] = brdr[2];
1329 output->initialBorder[3] = brdr[3];
1330 /* fall through */
1331 case 8:
1332 output->initialTrackingArea.x1 = track_left;
1333 output->initialTrackingArea.y1 = track_top;
1334 output->initialTrackingArea.x2 = track_left + track_width;
1335 output->initialTrackingArea.y2 = track_top + track_height;
1336 /* fall through */
1337 case 4:
1338 output->initialTotalArea.x1 = left;
1339 output->initialTotalArea.y1 = top;
1340 /* fall through */
1341 case 2:
1342 output->initialTotalArea.x2 = output->initialTotalArea.x1 + width;
1343 output->initialTotalArea.y2 = output->initialTotalArea.y1 + height;
1344 break;
1345 default:
1346 xf86DrvMsg(scrn->scrnIndex, X_ERROR,
1347 "Broken panning specification '%s' for output %s in config file\n",
1348 panning, output->name);
1349 }
1350 }
1351}
1352
1353/** Return - 0 + if a should be earlier, same or later than b in list
1354 */
1355static int
1356xf86ModeCompare(DisplayModePtr a, DisplayModePtr b)
1357{
1358 int diff;
1359
1360 diff = ((b->type & M_T_PREFERRED) != 0) - ((a->type & M_T_PREFERRED) != 0);
1361 if (diff)
1362 return diff;
1363 diff = b->HDisplay * b->VDisplay - a->HDisplay * a->VDisplay;
1364 if (diff)
1365 return diff;
1366 diff = b->Clock - a->Clock;
1367 return diff;
1368}
1369
1370/**
1371 * Insertion sort input in-place and return the resulting head
1372 */
1373static DisplayModePtr
1374xf86SortModes(DisplayModePtr input)
1375{
1376 DisplayModePtr output = NULL, i, o, n, *op, prev;
1377
1378 /* sort by preferred status and pixel area */
1379 while (input) {
1380 i = input;
1381 input = input->next;
1382 for (op = &output; (o = *op); op = &o->next)
1383 if (xf86ModeCompare(o, i) > 0)
1384 break;
1385 i->next = *op;
1386 *op = i;
1387 }
1388 /* prune identical modes */
1389 for (o = output; o && (n = o->next); o = n) {
1390 if (!strcmp(o->name, n->name) && xf86ModesEqual(o, n)) {
1391 o->next = n->next;
1392 free(n->name);
1393 free(n);
1394 n = o;
1395 }
1396 }
1397 /* hook up backward links */
1398 prev = NULL;
1399 for (o = output; o; o = o->next) {
1400 o->prev = prev;
1401 prev = o;
1402 }
1403 return output;
1404}
1405
1406static char *
1407preferredMode(ScrnInfoPtr pScrn, xf86OutputPtr output)
1408{
1409 char *preferred_mode = NULL;
1410
1411 /* Check for a configured preference for a particular mode */
1412 preferred_mode = xf86GetOptValString(output->options,
1413 OPTION_PREFERRED_MODE);
1414 if (preferred_mode)
1415 return preferred_mode;
1416
1417 if (pScrn->display->modes && *pScrn->display->modes)
1418 preferred_mode = *pScrn->display->modes;
1419
1420 return preferred_mode;
1421}
1422
1423/** identify a token
1424 * args
1425 * *src a string with zero or more tokens, e.g. "tok0 tok1",
1426 * **token stores a pointer to the first token character,
1427 * *len stores the token length.
1428 * return
1429 * a pointer into src[] at the token terminating character, or
1430 * NULL if no token is found.
1431 */
1432static const char *
1433gettoken(const char *src, const char **token, int *len)
1434{
1435 const char *delim = " \t";
1436 int skip;
1437
1438 if (!src)
1439 return NULL;
1440
1441 skip = strspn(src, delim);
1442 *token = &src[skip];
1443
1444 *len = strcspn(*token, delim);
1445 /* Support for backslash escaped delimiters could be implemented
1446 * here.
1447 */
1448
1449 /* (*token)[0] != '\0' <==> *len > 0 */
1450 if (*len > 0)
1451 return &(*token)[*len];
1452 else
1453 return NULL;
1454}
1455
1456/** Check for a user configured zoom mode list, Option "ZoomModes":
1457 *
1458 * Section "Monitor"
1459 * Identifier "a21inch"
1460 * Option "ZoomModes" "1600x1200 1280x1024 1280x1024 640x480"
1461 * EndSection
1462 *
1463 * Each user mode name is searched for independently so the list
1464 * specification order is free. An output mode is matched at most
1465 * once, a mode with an already set M_T_USERDEF type bit is skipped.
1466 * Thus a repeat mode name specification matches the next output mode
1467 * with the same name.
1468 *
1469 * Ctrl+Alt+Keypad-{Plus,Minus} zooms {in,out} by selecting the
1470 * {next,previous} M_T_USERDEF mode in the screen modes list, itself
1471 * sorted toward lower dot area or lower dot clock frequency, see
1472 * modes/xf86Crtc.c: xf86SortModes() xf86SetScrnInfoModes(), and
1473 * common/xf86Cursor.c: xf86ZoomViewport().
1474 */
1475static int
1476processZoomModes(xf86OutputPtr output)
1477{
1478 const char *zoom_modes;
1479 int count = 0;
1480
1481 zoom_modes = xf86GetOptValString(output->options, OPTION_ZOOM_MODES);
1482
1483 if (zoom_modes) {
1484 const char *token, *next;
1485 int len;
1486
1487 next = gettoken(zoom_modes, &token, &len);
1488 while (next) {
1489 DisplayModePtr mode;
1490
1491 for (mode = output->probed_modes; mode; mode = mode->next)
1492 if (!strncmp(token, mode->name, len) /* prefix match */
1493 && mode->name[len] == '\0' /* equal length */
1494 && !(mode->type & M_T_USERDEF)) { /* no rematch */
1495 mode->type |= M_T_USERDEF;
1496 break;
1497 }
1498
1499 count++;
1500 next = gettoken(next, &token, &len);
1501 }
1502 }
1503
1504 return count;
1505}
1506
1507static void
1508GuessRangeFromModes(MonPtr mon, DisplayModePtr mode)
1509{
1510 if (!mon || !mode)
1511 return;
1512
1513 mon->nHsync = 1;
1514 mon->hsync[0].lo = 1024.0;
1515 mon->hsync[0].hi = 0.0;
1516
1517 mon->nVrefresh = 1;
1518 mon->vrefresh[0].lo = 1024.0;
1519 mon->vrefresh[0].hi = 0.0;
1520
1521 while (mode) {
1522 if (!mode->HSync)
1523 mode->HSync = ((float) mode->Clock) / ((float) mode->HTotal);
1524
1525 if (!mode->VRefresh)
1526 mode->VRefresh = (1000.0 * ((float) mode->Clock)) /
1527 ((float) (mode->HTotal * mode->VTotal));
1528
1529 if (mode->HSync < mon->hsync[0].lo)
1530 mon->hsync[0].lo = mode->HSync;
1531
1532 if (mode->HSync > mon->hsync[0].hi)
1533 mon->hsync[0].hi = mode->HSync;
1534
1535 if (mode->VRefresh < mon->vrefresh[0].lo)
1536 mon->vrefresh[0].lo = mode->VRefresh;
1537
1538 if (mode->VRefresh > mon->vrefresh[0].hi)
1539 mon->vrefresh[0].hi = mode->VRefresh;
1540
1541 mode = mode->next;
1542 }
1543
1544 /* stretch out the bottom to fit 640x480@60 */
1545 if (mon->hsync[0].lo > 31.0)
1546 mon->hsync[0].lo = 31.0;
1547 if (mon->vrefresh[0].lo > 58.0)
1548 mon->vrefresh[0].lo = 58.0;
1549}
1550
1551enum det_monrec_source {
1552 sync_config, sync_edid, sync_default
1553};
1554
1555struct det_monrec_parameter {
1556 MonRec *mon_rec;
1557 int *max_clock;
1558 Bool set_hsync;
1559 Bool set_vrefresh;
1560 enum det_monrec_source *sync_source;
1561};
1562
1563static void
1564handle_detailed_monrec(struct detailed_monitor_section *det_mon, void *data)
1565{
1566 struct det_monrec_parameter *p;
1567
1568 p = (struct det_monrec_parameter *) data;
1569
1570 if (det_mon->type == DS_RANGES) {
1571 struct monitor_ranges *ranges = &det_mon->section.ranges;
1572
1573 if (p->set_hsync && ranges->max_h) {
1574 p->mon_rec->hsync[p->mon_rec->nHsync].lo = ranges->min_h;
1575 p->mon_rec->hsync[p->mon_rec->nHsync].hi = ranges->max_h;
1576 p->mon_rec->nHsync++;
1577 if (*p->sync_source == sync_default)
1578 *p->sync_source = sync_edid;
1579 }
1580 if (p->set_vrefresh && ranges->max_v) {
1581 p->mon_rec->vrefresh[p->mon_rec->nVrefresh].lo = ranges->min_v;
1582 p->mon_rec->vrefresh[p->mon_rec->nVrefresh].hi = ranges->max_v;
1583 p->mon_rec->nVrefresh++;
1584 if (*p->sync_source == sync_default)
1585 *p->sync_source = sync_edid;
1586 }
1587 if (ranges->max_clock * 1000 > *p->max_clock)
1588 *p->max_clock = ranges->max_clock * 1000;
1589 }
1590}
1591
1592void
1593xf86ProbeOutputModes(ScrnInfoPtr scrn, int maxX, int maxY)
1594{
1595 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
1596 int o;
1597
1598 /* When canGrow was TRUE in the initial configuration we have to
1599 * compare against the maximum values so that we don't drop modes.
1600 * When canGrow was FALSE, the maximum values would have been clamped
1601 * anyway.
1602 */
1603 if (maxX == 0 || maxY == 0) {
1604 maxX = config->maxWidth;
1605 maxY = config->maxHeight;
1606 }
1607
1608 /* Probe the list of modes for each output. */
1609 for (o = 0; o < config->num_output; o++) {
1610 xf86OutputPtr output = config->output[o];
1611 DisplayModePtr mode;
1612 DisplayModePtr config_modes = NULL, output_modes, default_modes = NULL;
1613 char *preferred_mode;
1614 xf86MonPtr edid_monitor;
1615 XF86ConfMonitorPtr conf_monitor;
1616 MonRec mon_rec;
1617 int min_clock = 0;
1618 int max_clock = 0;
1619 double clock;
1620 Bool add_default_modes;
1621 Bool debug_modes = config->debug_modes || xf86Initialising;
1622 enum det_monrec_source sync_source = sync_default;
1623
1624 while (output->probed_modes != NULL)
1625 xf86DeleteMode(&output->probed_modes, output->probed_modes);
1626
1627 /*
1628 * Check connection status
1629 */
1630 output->status = (*output->funcs->detect) (output);
1631
1632 if (output->status == XF86OutputStatusDisconnected &&
1633 !xf86ReturnOptValBool(output->options, OPTION_ENABLE, FALSE)) {
1634 xf86OutputSetEDID(output, NULL);
1635 continue;
1636 }
1637
1638 memset(&mon_rec, '\0', sizeof(mon_rec));
1639
1640 conf_monitor = output->conf_monitor;
1641
1642 if (conf_monitor) {
1643 int i;
1644
1645 for (i = 0; i < conf_monitor->mon_n_hsync; i++) {
1646 mon_rec.hsync[mon_rec.nHsync].lo =
1647 conf_monitor->mon_hsync[i].lo;
1648 mon_rec.hsync[mon_rec.nHsync].hi =
1649 conf_monitor->mon_hsync[i].hi;
1650 mon_rec.nHsync++;
1651 sync_source = sync_config;
1652 }
1653 for (i = 0; i < conf_monitor->mon_n_vrefresh; i++) {
1654 mon_rec.vrefresh[mon_rec.nVrefresh].lo =
1655 conf_monitor->mon_vrefresh[i].lo;
1656 mon_rec.vrefresh[mon_rec.nVrefresh].hi =
1657 conf_monitor->mon_vrefresh[i].hi;
1658 mon_rec.nVrefresh++;
1659 sync_source = sync_config;
1660 }
1661 config_modes = xf86GetMonitorModes(scrn, conf_monitor);
1662 }
1663
1664 output_modes = (*output->funcs->get_modes) (output);
1665
1666 /*
1667 * If the user has a preference, respect it.
1668 * Otherwise, don't second-guess the driver.
1669 */
1670 if (!xf86GetOptValBool(output->options, OPTION_DEFAULT_MODES,
1671 &add_default_modes))
1672 add_default_modes = (output_modes == NULL);
1673
1674 edid_monitor = output->MonInfo;
1675
1676 if (edid_monitor) {
1677 struct det_monrec_parameter p;
1678 struct disp_features *features = &edid_monitor->features;
1679
1680 /* if display is not continuous-frequency, don't add default modes */
1681 if (!GTF_SUPPORTED(features->msc))
1682 add_default_modes = FALSE;
1683
1684 p.mon_rec = &mon_rec;
1685 p.max_clock = &max_clock;
1686 p.set_hsync = mon_rec.nHsync == 0;
1687 p.set_vrefresh = mon_rec.nVrefresh == 0;
1688 p.sync_source = &sync_source;
1689
1690 xf86ForEachDetailedBlock(edid_monitor, handle_detailed_monrec, &p);
1691 }
1692
1693 if (xf86GetOptValFreq(output->options, OPTION_MIN_CLOCK,
1694 OPTUNITS_KHZ, &clock))
1695 min_clock = (int) clock;
1696 if (xf86GetOptValFreq(output->options, OPTION_MAX_CLOCK,
1697 OPTUNITS_KHZ, &clock))
1698 max_clock = (int) clock;
1699
1700 /* If we still don't have a sync range, guess wildly */
1701 if (!mon_rec.nHsync || !mon_rec.nVrefresh)
1702 GuessRangeFromModes(&mon_rec, output_modes);
1703
1704 /*
1705 * These limits will end up setting a 1024x768@60Hz mode by default,
1706 * which seems like a fairly good mode to use when nothing else is
1707 * specified
1708 */
1709 if (mon_rec.nHsync == 0) {
1710 mon_rec.hsync[0].lo = 31.0;
1711 mon_rec.hsync[0].hi = 55.0;
1712 mon_rec.nHsync = 1;
1713 }
1714 if (mon_rec.nVrefresh == 0) {
1715 mon_rec.vrefresh[0].lo = 58.0;
1716 mon_rec.vrefresh[0].hi = 62.0;
1717 mon_rec.nVrefresh = 1;
1718 }
1719
1720 if (add_default_modes)
1721 default_modes = xf86GetDefaultModes();
1722
1723 /*
1724 * If this is not an RB monitor, remove RB modes from the default
1725 * pool. RB modes from the config or the monitor itself are fine.
1726 */
1727 if (!mon_rec.reducedblanking)
1728 xf86ValidateModesReducedBlanking(scrn, default_modes);
1729
1730 if (sync_source == sync_config) {
1731 /*
1732 * Check output and config modes against sync range from config file
1733 */
1734 xf86ValidateModesSync(scrn, output_modes, &mon_rec);
1735 xf86ValidateModesSync(scrn, config_modes, &mon_rec);
1736 }
1737 /*
1738 * Check default modes against sync range
1739 */
1740 xf86ValidateModesSync(scrn, default_modes, &mon_rec);
1741 /*
1742 * Check default modes against monitor max clock
1743 */
1744 if (max_clock) {
1745 xf86ValidateModesClocks(scrn, default_modes,
1746 &min_clock, &max_clock, 1);
1747 xf86ValidateModesClocks(scrn, output_modes,
1748 &min_clock, &max_clock, 1);
1749 }
1750
1751 output->probed_modes = NULL;
1752 output->probed_modes = xf86ModesAdd(output->probed_modes, config_modes);
1753 output->probed_modes = xf86ModesAdd(output->probed_modes, output_modes);
1754 output->probed_modes =
1755 xf86ModesAdd(output->probed_modes, default_modes);
1756
1757 /*
1758 * Check all modes against max size, interlace, and doublescan
1759 */
1760 if (maxX && maxY)
1761 xf86ValidateModesSize(scrn, output->probed_modes, maxX, maxY, 0);
1762
1763 {
1764 int flags = (output->interlaceAllowed ? V_INTERLACE : 0) |
1765 (output->doubleScanAllowed ? V_DBLSCAN : 0);
1766 xf86ValidateModesFlags(scrn, output->probed_modes, flags);
1767 }
1768
1769 /*
1770 * Check all modes against output
1771 */
1772 for (mode = output->probed_modes; mode != NULL; mode = mode->next)
1773 if (mode->status == MODE_OK)
1774 mode->status = (*output->funcs->mode_valid) (output, mode);
1775
1776 xf86PruneInvalidModes(scrn, &output->probed_modes, debug_modes);
1777
1778 output->probed_modes = xf86SortModes(output->probed_modes);
1779
1780 /* Check for a configured preference for a particular mode */
1781 preferred_mode = preferredMode(scrn, output);
1782
1783 if (preferred_mode) {
1784 for (mode = output->probed_modes; mode; mode = mode->next) {
1785 if (!strcmp(preferred_mode, mode->name)) {
1786 if (mode != output->probed_modes) {
1787 if (mode->prev)
1788 mode->prev->next = mode->next;
1789 if (mode->next)
1790 mode->next->prev = mode->prev;
1791 mode->next = output->probed_modes;
1792 output->probed_modes->prev = mode;
1793 mode->prev = NULL;
1794 output->probed_modes = mode;
1795 }
1796 mode->type |= (M_T_PREFERRED | M_T_USERPREF);
1797 break;
1798 }
1799 }
1800 }
1801
1802 /* Ctrl+Alt+Keypad-{Plus,Minus} zoom mode: M_T_USERDEF mode type */
1803 processZoomModes(output);
1804
1805 output->initial_rotation = xf86OutputInitialRotation(output);
1806
1807 if (debug_modes) {
1808 if (output->probed_modes != NULL) {
1809 xf86DrvMsg(scrn->scrnIndex, X_INFO,
1810 "Printing probed modes for output %s\n",
1811 output->name);
1812 }
1813 else {
1814 xf86DrvMsg(scrn->scrnIndex, X_INFO,
1815 "No remaining probed modes for output %s\n",
1816 output->name);
1817 }
1818 }
1819 for (mode = output->probed_modes; mode != NULL; mode = mode->next) {
1820 /* The code to choose the best mode per pipe later on will require
1821 * VRefresh to be set.
1822 */
1823 mode->VRefresh = xf86ModeVRefresh(mode);
1824 xf86SetModeCrtc(mode, INTERLACE_HALVE_V);
1825
1826 if (debug_modes)
1827 xf86PrintModeline(scrn->scrnIndex, mode);
1828 }
1829 }
1830}
1831
1832/**
1833 * Copy one of the output mode lists to the ScrnInfo record
1834 */
1835
1836/* XXX where does this function belong? Here? */
1837void
1838 xf86RandR12GetOriginalVirtualSize(ScrnInfoPtr scrn, int *x, int *y);
1839
1840static DisplayModePtr
1841biggestMode(DisplayModePtr a, DisplayModePtr b)
1842{
1843 int A, B;
1844
1845 if (!a)
1846 return b;
1847 if (!b)
1848 return a;
1849
1850 A = a->HDisplay * a->VDisplay;
1851 B = b->HDisplay * b->VDisplay;
1852
1853 if (A > B)
1854 return a;
1855
1856 return b;
1857}
1858
1859static xf86OutputPtr
1860SetCompatOutput(xf86CrtcConfigPtr config)
1861{
1862 xf86OutputPtr output = NULL, test = NULL;
1863 DisplayModePtr maxmode = NULL, testmode, mode;
1864 int o, compat = -1, count, mincount = 0;
1865
1866 if (config->num_output == 0)
1867 return NULL;
1868
1869 /* Look for one that's definitely connected */
1870 for (o = 0; o < config->num_output; o++) {
1871 test = config->output[o];
1872 if (!test->crtc)
1873 continue;
1874 if (test->status != XF86OutputStatusConnected)
1875 continue;
1876 if (!test->probed_modes)
1877 continue;
1878
1879 testmode = mode = test->probed_modes;
1880 for (count = 0; mode; mode = mode->next, count++)
1881 testmode = biggestMode(testmode, mode);
1882
1883 if (!output) {
1884 output = test;
1885 compat = o;
1886 maxmode = testmode;
1887 mincount = count;
1888 }
1889 else if (maxmode == biggestMode(maxmode, testmode)) {
1890 output = test;
1891 compat = o;
1892 maxmode = testmode;
1893 mincount = count;
1894 }
1895 else if ((maxmode->HDisplay == testmode->HDisplay) &&
1896 (maxmode->VDisplay == testmode->VDisplay) &&
1897 count <= mincount) {
1898 output = test;
1899 compat = o;
1900 maxmode = testmode;
1901 mincount = count;
1902 }
1903 }
1904
1905 /* If we didn't find one, take anything we can get */
1906 if (!output) {
1907 for (o = 0; o < config->num_output; o++) {
1908 test = config->output[o];
1909 if (!test->crtc)
1910 continue;
1911 if (!test->probed_modes)
1912 continue;
1913
1914 if (!output) {
1915 output = test;
1916 compat = o;
1917 }
1918 else if (test->probed_modes->HDisplay <
1919 output->probed_modes->HDisplay) {
1920 output = test;
1921 compat = o;
1922 }
1923 }
1924 }
1925
1926 if (compat >= 0) {
1927 config->compat_output = compat;
1928 }
1929 else if (config->compat_output >= 0 && config->compat_output < config->num_output) {
1930 /* Don't change the compat output when no valid outputs found */
1931 output = config->output[config->compat_output];
1932 }
1933
1934 /* All outputs are disconnected, select one to fake */
1935 if (!output && config->num_output) {
1936 config->compat_output = 0;
1937 output = config->output[config->compat_output];
1938 }
1939
1940 return output;
1941}
1942
1943void
1944xf86SetScrnInfoModes(ScrnInfoPtr scrn)
1945{
1946 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
1947 xf86OutputPtr output;
1948 xf86CrtcPtr crtc;
1949 DisplayModePtr last, mode = NULL;
1950
1951 output = SetCompatOutput(config);
1952
1953 if (!output)
1954 return; /* punt */
1955
1956 crtc = output->crtc;
1957
1958 /* Clear any existing modes from scrn->modes */
1959 while (scrn->modes != NULL)
1960 xf86DeleteMode(&scrn->modes, scrn->modes);
1961
1962 /* Set scrn->modes to the mode list for the 'compat' output */
1963 scrn->modes = xf86DuplicateModes(scrn, output->probed_modes);
1964
1965 if (crtc) {
1966 for (mode = scrn->modes; mode; mode = mode->next)
1967 if (xf86ModesEqual(mode, &crtc->desiredMode))
1968 break;
1969 }
1970
1971 if (!scrn->modes) {
1972 scrn->modes = xf86ModesAdd(scrn->modes,
1973 xf86CVTMode(scrn->display->virtualX,
1974 scrn->display->virtualY,
1975 60, 0, 0));
1976 }
1977
1978 /* For some reason, scrn->modes is circular, unlike the other mode
1979 * lists. How great is that?
1980 */
1981 for (last = scrn->modes; last && last->next; last = last->next);
1982 last->next = scrn->modes;
1983 scrn->modes->prev = last;
1984 if (mode) {
1985 while (scrn->modes != mode)
1986 scrn->modes = scrn->modes->next;
1987 }
1988
1989 scrn->currentMode = scrn->modes;
1990#ifdef XFreeXDGA
1991 if (scrn->pScreen)
1992 _xf86_di_dga_reinit_internal(scrn->pScreen);
1993#endif
1994}
1995
1996static Bool
1997xf86CollectEnabledOutputs(ScrnInfoPtr scrn, xf86CrtcConfigPtr config,
1998 Bool *enabled)
1999{
2000 Bool any_enabled = FALSE;
2001 int o;
2002
2003 /*
2004 * Don't bother enabling outputs on GPU screens: a client needs to attach
2005 * it to a source provider before setting a mode that scans out a shared
2006 * pixmap.
2007 */
2008 if (scrn->is_gpu)
2009 return FALSE;
2010
2011 for (o = 0; o < config->num_output; o++)
2012 any_enabled |= enabled[o] = xf86OutputEnabled(config->output[o], TRUE);
2013
2014 if (!any_enabled) {
2015 xf86DrvMsg(scrn->scrnIndex, X_WARNING,
2016 "No outputs definitely connected, trying again...\n");
2017
2018 for (o = 0; o < config->num_output; o++)
2019 any_enabled |= enabled[o] =
2020 xf86OutputEnabled(config->output[o], FALSE);
2021 }
2022
2023 return any_enabled;
2024}
2025
2026static Bool
2027nextEnabledOutput(xf86CrtcConfigPtr config, Bool *enabled, int *index)
2028{
2029 int o = *index;
2030
2031 for (o++; o < config->num_output; o++) {
2032 if (enabled[o]) {
2033 *index = o;
2034 return TRUE;
2035 }
2036 }
2037
2038 return FALSE;
2039}
2040
2041static Bool
2042aspectMatch(float a, float b)
2043{
2044 return fabs(1 - (a / b)) < 0.05;
2045}
2046
2047static DisplayModePtr
2048nextAspectMode(xf86OutputPtr o, DisplayModePtr last, float aspect)
2049{
2050 DisplayModePtr m = NULL;
2051
2052 if (!o)
2053 return NULL;
2054
2055 if (!last)
2056 m = o->probed_modes;
2057 else
2058 m = last->next;
2059
2060 for (; m; m = m->next)
2061 if (aspectMatch(aspect, (float) m->HDisplay / (float) m->VDisplay))
2062 return m;
2063
2064 return NULL;
2065}
2066
2067static DisplayModePtr
2068bestModeForAspect(xf86CrtcConfigPtr config, Bool *enabled, float aspect)
2069{
2070 int o = -1, p;
2071 DisplayModePtr mode = NULL, test = NULL, match = NULL;
2072
2073 if (!nextEnabledOutput(config, enabled, &o))
2074 return NULL;
2075 while ((mode = nextAspectMode(config->output[o], mode, aspect))) {
2076 test = mode;
2077 for (p = o; nextEnabledOutput(config, enabled, &p);) {
2078 test = xf86OutputFindClosestMode(config->output[p], mode);
2079 if (!test)
2080 break;
2081 if (test->HDisplay != mode->HDisplay ||
2082 test->VDisplay != mode->VDisplay) {
2083 test = NULL;
2084 break;
2085 }
2086 }
2087
2088 /* if we didn't match it on all outputs, try the next one */
2089 if (!test)
2090 continue;
2091
2092 /* if it's bigger than the last one, save it */
2093 if (!match || (test->HDisplay > match->HDisplay))
2094 match = test;
2095 }
2096
2097 /* return the biggest one found */
2098 return match;
2099}
2100
2101static Bool
2102xf86TargetPreferred(ScrnInfoPtr scrn, xf86CrtcConfigPtr config,
2103 DisplayModePtr * modes, Bool *enabled,
2104 int width, int height)
2105{
2106 int o, p;
2107 int max_pref_width = 0, max_pref_height = 0;
2108 DisplayModePtr *preferred, *preferred_match;
2109 Bool ret = FALSE;
2110
2111 preferred = xnfcalloc(config->num_output, sizeof(DisplayModePtr));
2112 preferred_match = xnfcalloc(config->num_output, sizeof(DisplayModePtr));
2113
2114 /* Check if the preferred mode is available on all outputs */
2115 for (p = -1; nextEnabledOutput(config, enabled, &p);) {
2116 Rotation r = config->output[p]->initial_rotation;
2117 DisplayModePtr mode;
2118
2119 if ((preferred[p] = xf86OutputHasPreferredMode(config->output[p],
2120 width, height))) {
2121 int pref_width = xf86ModeWidth(preferred[p], r);
2122 int pref_height = xf86ModeHeight(preferred[p], r);
2123 Bool all_match = TRUE;
2124
2125 for (o = -1; nextEnabledOutput(config, enabled, &o);) {
2126 Bool match = FALSE;
2127 xf86OutputPtr output = config->output[o];
2128
2129 if (o == p)
2130 continue;
2131
2132 /*
2133 * First see if the preferred mode matches on the next
2134 * output as well. This catches the common case of identical
2135 * monitors and makes sure they all have the same timings
2136 * and refresh. If that fails, we fall back to trying to
2137 * match just width & height.
2138 */
2139 mode = xf86OutputHasPreferredMode(output, pref_width,
2140 pref_height);
2141 if (mode && xf86ModesEqual(mode, preferred[p])) {
2142 preferred[o] = mode;
2143 match = TRUE;
2144 }
2145 else {
2146 for (mode = output->probed_modes; mode; mode = mode->next) {
2147 Rotation r = output->initial_rotation;
2148
2149 if (xf86ModeWidth(mode, r) == pref_width &&
2150 xf86ModeHeight(mode, r) == pref_height) {
2151 preferred[o] = mode;
2152 match = TRUE;
2153 }
2154 }
2155 }
2156
2157 all_match &= match;
2158 }
2159
2160 if (all_match &&
2161 (pref_width * pref_height > max_pref_width * max_pref_height)) {
2162 for (o = -1; nextEnabledOutput(config, enabled, &o);)
2163 preferred_match[o] = preferred[o];
2164 max_pref_width = pref_width;
2165 max_pref_height = pref_height;
2166 ret = TRUE;
2167 }
2168 }
2169 }
2170
2171 /*
2172 * If there's no preferred mode, but only one monitor, pick the
2173 * biggest mode for its aspect ratio or 4:3, assuming one exists.
2174 */
2175 if (!ret)
2176 do {
2177 int i = 0;
2178 float aspect = 0.0;
2179 DisplayModePtr a = NULL, b = NULL;
2180
2181 /* count the number of enabled outputs */
2182 for (i = 0, p = -1; nextEnabledOutput(config, enabled, &p); i++);
2183
2184 if (i != 1)
2185 break;
2186
2187 p = -1;
2188 nextEnabledOutput(config, enabled, &p);
2189 if (config->output[p]->mm_height)
2190 aspect = (float) config->output[p]->mm_width /
2191 (float) config->output[p]->mm_height;
2192
2193 a = bestModeForAspect(config, enabled, 4.0/3.0);
2194 if (aspect)
2195 b = bestModeForAspect(config, enabled, aspect);
2196
2197 preferred_match[p] = biggestMode(a, b);
2198
2199 if (preferred_match[p])
2200 ret = TRUE;
2201
2202 } while (0);
2203
2204 if (ret) {
2205 /* oh good, there is a match. stash the selected modes and return. */
2206 memcpy(modes, preferred_match,
2207 config->num_output * sizeof(DisplayModePtr));
2208 }
2209
2210 free(preferred);
2211 free(preferred_match);
2212 return ret;
2213}
2214
2215static Bool
2216xf86TargetAspect(ScrnInfoPtr scrn, xf86CrtcConfigPtr config,
2217 DisplayModePtr * modes, Bool *enabled, int width, int height)
2218{
2219 int o;
2220 float aspect = 0.0, *aspects;
2221 xf86OutputPtr output;
2222 Bool ret = FALSE;
2223 DisplayModePtr guess = NULL, aspect_guess = NULL, base_guess = NULL;
2224
2225 aspects = xnfcalloc(config->num_output, sizeof(float));
2226
2227 /* collect the aspect ratios */
2228 for (o = -1; nextEnabledOutput(config, enabled, &o);) {
2229 output = config->output[o];
2230 if (output->mm_height)
2231 aspects[o] = (float) output->mm_width / (float) output->mm_height;
2232 else
2233 aspects[o] = 4.0 / 3.0;
2234 }
2235
2236 /* check that they're all the same */
2237 for (o = -1; nextEnabledOutput(config, enabled, &o);) {
2238 output = config->output[o];
2239 if (!aspect) {
2240 aspect = aspects[o];
2241 }
2242 else if (!aspectMatch(aspect, aspects[o])) {
2243 goto no_aspect_match;
2244 }
2245 }
2246
2247 /* if they're all 4:3, just skip ahead and save effort */
2248 if (!aspectMatch(aspect, 4.0 / 3.0))
2249 aspect_guess = bestModeForAspect(config, enabled, aspect);
2250
2251 no_aspect_match:
2252 base_guess = bestModeForAspect(config, enabled, 4.0 / 3.0);
2253
2254 guess = biggestMode(base_guess, aspect_guess);
2255
2256 if (!guess)
2257 goto out;
2258
2259 /* found a mode that works everywhere, now apply it */
2260 for (o = -1; nextEnabledOutput(config, enabled, &o);) {
2261 modes[o] = xf86OutputFindClosestMode(config->output[o], guess);
2262 }
2263 ret = TRUE;
2264
2265 out:
2266 free(aspects);
2267 return ret;
2268}
2269
2270static Bool
2271xf86TargetFallback(ScrnInfoPtr scrn, xf86CrtcConfigPtr config,
2272 DisplayModePtr * modes, Bool *enabled, int width, int height)
2273{
2274 DisplayModePtr target_mode = NULL;
2275 Rotation target_rotation = RR_Rotate_0;
2276 DisplayModePtr default_mode;
2277 int default_preferred, target_preferred = 0, o;
2278
2279 /* User preferred > preferred > other modes */
2280 for (o = -1; nextEnabledOutput(config, enabled, &o);) {
2281 default_mode = xf86DefaultMode(config->output[o], width, height);
2282 if (!default_mode)
2283 continue;
2284
2285 default_preferred = (((default_mode->type & M_T_PREFERRED) != 0) +
2286 ((default_mode->type & M_T_USERPREF) != 0));
2287
2288 if (default_preferred > target_preferred || !target_mode) {
2289 target_mode = default_mode;
2290 target_preferred = default_preferred;
2291 target_rotation = config->output[o]->initial_rotation;
2292 config->compat_output = o;
2293 }
2294 }
2295
2296 if (target_mode)
2297 modes[config->compat_output] = target_mode;
2298
2299 /* Fill in other output modes */
2300 for (o = -1; nextEnabledOutput(config, enabled, &o);) {
2301 if (!modes[o])
2302 modes[o] = xf86ClosestMode(config->output[o], target_mode,
2303 target_rotation, width, height);
2304 }
2305
2306 return target_mode != NULL;
2307}
2308
2309static Bool
2310xf86TargetUserpref(ScrnInfoPtr scrn, xf86CrtcConfigPtr config,
2311 DisplayModePtr * modes, Bool *enabled, int width, int height)
2312{
2313 int o;
2314
2315 if (xf86UserConfiguredOutputs(scrn, modes))
2316 return xf86TargetFallback(scrn, config, modes, enabled, width, height);
2317
2318 for (o = -1; nextEnabledOutput(config, enabled, &o);)
2319 if (xf86OutputHasUserPreferredMode(config->output[o]))
2320 return
2321 xf86TargetFallback(scrn, config, modes, enabled, width, height);
2322
2323 return FALSE;
2324}
2325
2326static Bool
2327xf86CrtcSetInitialGamma(xf86CrtcPtr crtc, float gamma_red, float gamma_green,
2328 float gamma_blue)
2329{
2330 int i, size = 256;
2331 CARD16 *red, *green, *blue;
2332
2333 red = malloc(3 * size * sizeof(CARD16));
2334 green = red + size;
2335 blue = green + size;
2336
2337 /* Only cause warning if user wanted gamma to be set. */
2338 if (!crtc->funcs->gamma_set &&
2339 (gamma_red != 1.0 || gamma_green != 1.0 || gamma_blue != 1.0)) {
2340 free(red);
2341 return FALSE;
2342 }
2343 else if (!crtc->funcs->gamma_set) {
2344 free(red);
2345 return TRUE;
2346 }
2347
2348 /* At this early stage none of the randr-interface stuff is up.
2349 * So take the default gamma size for lack of something better.
2350 */
2351 for (i = 0; i < size; i++) {
2352 if (gamma_red == 1.0)
2353 red[i] = i << 8;
2354 else
2355 red[i] = (CARD16) (pow((double) i / (double) (size - 1),
2356 1. / (double) gamma_red) * (double) (size -
2357 1) *
2358 256);
2359
2360 if (gamma_green == 1.0)
2361 green[i] = i << 8;
2362 else
2363 green[i] = (CARD16) (pow((double) i / (double) (size - 1),
2364 1. / (double) gamma_green) *
2365 (double) (size - 1) * 256);
2366
2367 if (gamma_blue == 1.0)
2368 blue[i] = i << 8;
2369 else
2370 blue[i] = (CARD16) (pow((double) i / (double) (size - 1),
2371 1. / (double) gamma_blue) * (double) (size -
2372 1) *
2373 256);
2374 }
2375
2376 /* Default size is 256, so anything else is failure. */
2377 if (size != crtc->gamma_size) {
2378 free(red);
2379 return FALSE;
2380 }
2381
2382 crtc->gamma_size = size;
2383 memcpy(crtc->gamma_red, red, crtc->gamma_size * sizeof(CARD16));
2384 memcpy(crtc->gamma_green, green, crtc->gamma_size * sizeof(CARD16));
2385 memcpy(crtc->gamma_blue, blue, crtc->gamma_size * sizeof(CARD16));
2386
2387 /* Do not set gamma now, delay until the crtc is activated. */
2388
2389 free(red);
2390
2391 return TRUE;
2392}
2393
2394static Bool
2395xf86OutputSetInitialGamma(xf86OutputPtr output)
2396{
2397 XF86ConfMonitorPtr mon = output->conf_monitor;
2398 float gamma_red = 1.0, gamma_green = 1.0, gamma_blue = 1.0;
2399
2400 if (!mon)
2401 return TRUE;
2402
2403 if (!output->crtc)
2404 return FALSE;
2405
2406 /* Get configured values, where they exist. */
2407 if (mon->mon_gamma_red >= GAMMA_MIN && mon->mon_gamma_red <= GAMMA_MAX)
2408 gamma_red = mon->mon_gamma_red;
2409
2410 if (mon->mon_gamma_green >= GAMMA_MIN && mon->mon_gamma_green <= GAMMA_MAX)
2411 gamma_green = mon->mon_gamma_green;
2412
2413 if (mon->mon_gamma_blue >= GAMMA_MIN && mon->mon_gamma_blue <= GAMMA_MAX)
2414 gamma_blue = mon->mon_gamma_blue;
2415
2416 /* This avoids setting gamma 1.0 in case another cloned output on this crtc has a specific gamma. */
2417 if (gamma_red != 1.0 || gamma_green != 1.0 || gamma_blue != 1.0) {
2418 xf86DrvMsg(output->scrn->scrnIndex, X_INFO,
2419 "Output %s wants gamma correction (%.1f, %.1f, %.1f)\n",
2420 output->name, gamma_red, gamma_green, gamma_blue);
2421 return xf86CrtcSetInitialGamma(output->crtc, gamma_red, gamma_green,
2422 gamma_blue);
2423 }
2424 else
2425 return TRUE;
2426}
2427
2428/**
2429 * Construct default screen configuration
2430 *
2431 * Given auto-detected (and, eventually, configured) values,
2432 * construct a usable configuration for the system
2433 *
2434 * canGrow indicates that the driver can resize the screen to larger than its
2435 * initially configured size via the config->funcs->resize hook. If TRUE, this
2436 * function will set virtualX and virtualY to match the initial configuration
2437 * and leave config->max{Width,Height} alone. If FALSE, it will bloat
2438 * virtual[XY] to include the largest modes and set config->max{Width,Height}
2439 * accordingly.
2440 */
2441
2442Bool
2443xf86InitialConfiguration(ScrnInfoPtr scrn, Bool canGrow)
2444{
2445 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
2446 int o, c;
2447 xf86CrtcPtr *crtcs;
2448 DisplayModePtr *modes;
2449 Bool *enabled;
2450 int width, height;
2451 int i = scrn->scrnIndex;
2452 Bool have_outputs = TRUE;
2453 Bool ret;
2454 Bool success = FALSE;
2455
2456 /* Set up the device options */
2457 config->options = xnfalloc(sizeof(xf86DeviceOptions));
2458 memcpy(config->options, xf86DeviceOptions, sizeof(xf86DeviceOptions));
2459 xf86ProcessOptions(scrn->scrnIndex, scrn->options, config->options);
2460 config->debug_modes = xf86ReturnOptValBool(config->options,
2461 OPTION_MODEDEBUG, FALSE);
2462
2463 if (scrn->display->virtualX && !scrn->is_gpu)
2464 width = scrn->display->virtualX;
2465 else
2466 width = config->maxWidth;
2467 if (scrn->display->virtualY && !scrn->is_gpu)
2468 height = scrn->display->virtualY;
2469 else
2470 height = config->maxHeight;
2471
2472 xf86ProbeOutputModes(scrn, width, height);
2473
2474 crtcs = xnfcalloc(config->num_output, sizeof(xf86CrtcPtr));
2475 modes = xnfcalloc(config->num_output, sizeof(DisplayModePtr));
2476 enabled = xnfcalloc(config->num_output, sizeof(Bool));
2477
2478 ret = xf86CollectEnabledOutputs(scrn, config, enabled);
2479 if (ret == FALSE && canGrow) {
2480 if (!scrn->is_gpu)
2481 xf86DrvMsg(i, X_WARNING,
2482 "Unable to find connected outputs - setting %dx%d "
2483 "initial framebuffer\n",
2484 NO_OUTPUT_DEFAULT_WIDTH, NO_OUTPUT_DEFAULT_HEIGHT);
2485 have_outputs = FALSE;
2486 }
2487 else {
2488 if (xf86TargetUserpref(scrn, config, modes, enabled, width, height))
2489 xf86DrvMsg(i, X_INFO, "Using user preference for initial modes\n");
2490 else if (xf86TargetPreferred
2491 (scrn, config, modes, enabled, width, height))
2492 xf86DrvMsg(i, X_INFO, "Using exact sizes for initial modes\n");
2493 else if (xf86TargetAspect(scrn, config, modes, enabled, width, height))
2494 xf86DrvMsg(i, X_INFO,
2495 "Using fuzzy aspect match for initial modes\n");
2496 else if (xf86TargetFallback
2497 (scrn, config, modes, enabled, width, height))
2498 xf86DrvMsg(i, X_INFO, "Using sloppy heuristic for initial modes\n");
2499 else
2500 xf86DrvMsg(i, X_WARNING, "Unable to find initial modes\n");
2501 }
2502
2503 for (o = -1; nextEnabledOutput(config, enabled, &o);) {
2504 if (!modes[o])
2505 xf86DrvMsg(scrn->scrnIndex, X_ERROR,
2506 "Output %s enabled but has no modes\n",
2507 config->output[o]->name);
2508 else
2509 xf86DrvMsg(scrn->scrnIndex, X_INFO,
2510 "Output %s using initial mode %s\n",
2511 config->output[o]->name, modes[o]->name);
2512 }
2513
2514 /*
2515 * Set the position of each output
2516 */
2517 if (!xf86InitialOutputPositions(scrn, modes))
2518 goto bailout;
2519
2520 /*
2521 * Set initial panning of each output
2522 */
2523 xf86InitialPanning(scrn);
2524
2525 /*
2526 * Assign CRTCs to fit output configuration
2527 */
2528 if (have_outputs && !xf86PickCrtcs(scrn, crtcs, modes, 0, width, height))
2529 goto bailout;
2530
2531 /* XXX override xf86 common frame computation code */
2532
2533 if (!scrn->is_gpu) {
2534 scrn->display->frameX0 = 0;
2535 scrn->display->frameY0 = 0;
2536 }
2537
2538 for (c = 0; c < config->num_crtc; c++) {
2539 xf86CrtcPtr crtc = config->crtc[c];
2540
2541 crtc->enabled = FALSE;
2542 memset(&crtc->desiredMode, '\0', sizeof(crtc->desiredMode));
2543 /* Set default gamma for all crtc's. */
2544 /* This is done to avoid problems later on with cloned outputs. */
2545 xf86CrtcSetInitialGamma(crtc, 1.0, 1.0, 1.0);
2546 }
2547
2548 if (xf86_crtc_supports_gamma(scrn))
2549 xf86DrvMsg(scrn->scrnIndex, X_INFO,
2550 "Using default gamma of (1.0, 1.0, 1.0) unless otherwise stated.\n");
2551
2552 /*
2553 * Set initial configuration
2554 */
2555 for (o = 0; o < config->num_output; o++) {
2556 xf86OutputPtr output = config->output[o];
2557 DisplayModePtr mode = modes[o];
2558 xf86CrtcPtr crtc = crtcs[o];
2559
2560 if (mode && crtc) {
2561 xf86SaveModeContents(&crtc->desiredMode, mode);
2562 crtc->desiredRotation = output->initial_rotation;
2563 crtc->desiredX = output->initial_x;
2564 crtc->desiredY = output->initial_y;
2565 crtc->desiredTransformPresent = FALSE;
2566 crtc->enabled = TRUE;
2567 memcpy(&crtc->panningTotalArea, &output->initialTotalArea,
2568 sizeof(BoxRec));
2569 memcpy(&crtc->panningTrackingArea, &output->initialTrackingArea,
2570 sizeof(BoxRec));
2571 memcpy(crtc->panningBorder, output->initialBorder,
2572 4 * sizeof(INT16));
2573 output->crtc = crtc;
2574 if (!xf86OutputSetInitialGamma(output))
2575 xf86DrvMsg(scrn->scrnIndex, X_WARNING,
2576 "Initial gamma correction for output %s: failed.\n",
2577 output->name);
2578 }
2579 else {
2580 output->crtc = NULL;
2581 }
2582 }
2583
2584 if (scrn->display->virtualX == 0 || scrn->is_gpu) {
2585 /*
2586 * Expand virtual size to cover the current config and potential mode
2587 * switches, if the driver can't enlarge the screen later.
2588 */
2589 xf86DefaultScreenLimits(scrn, &width, &height, canGrow);
2590
2591 if (have_outputs == FALSE) {
2592 if (width < NO_OUTPUT_DEFAULT_WIDTH &&
2593 height < NO_OUTPUT_DEFAULT_HEIGHT) {
2594 width = NO_OUTPUT_DEFAULT_WIDTH;
2595 height = NO_OUTPUT_DEFAULT_HEIGHT;
2596 }
2597 }
2598
2599 if (!scrn->is_gpu) {
2600 scrn->display->virtualX = width;
2601 scrn->display->virtualY = height;
2602 }
2603 }
2604
2605 if (width > scrn->virtualX)
2606 scrn->virtualX = width;
2607 if (height > scrn->virtualY)
2608 scrn->virtualY = height;
2609
2610 /*
2611 * Make sure the configuration isn't too small.
2612 */
2613 if (width < config->minWidth || height < config->minHeight)
2614 goto bailout;
2615
2616 /*
2617 * Limit the crtc config to virtual[XY] if the driver can't grow the
2618 * desktop.
2619 */
2620 if (!canGrow) {
2621 xf86CrtcSetSizeRange(scrn, config->minWidth, config->minHeight,
2622 width, height);
2623 }
2624
2625 xf86SetScrnInfoModes(scrn);
2626
2627 success = TRUE;
2628 bailout:
2629 free(crtcs);
2630 free(modes);
2631 free(enabled);
2632 return success;
2633}
2634
2635/*
2636 * Check the CRTC we're going to map each output to vs. it's current
2637 * CRTC. If they don't match, we have to disable the output and the CRTC
2638 * since the driver will have to re-route things.
2639 */
2640static void
2641xf86PrepareOutputs(ScrnInfoPtr scrn)
2642{
2643 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
2644 int o;
2645
2646 for (o = 0; o < config->num_output; o++) {
2647 xf86OutputPtr output = config->output[o];
2648
2649#if RANDR_GET_CRTC_INTERFACE
2650 /* Disable outputs that are unused or will be re-routed */
2651 if (!output->funcs->get_crtc ||
2652 output->crtc != (*output->funcs->get_crtc) (output) ||
2653 output->crtc == NULL)
2654#endif
2655 (*output->funcs->dpms) (output, DPMSModeOff);
2656 }
2657}
2658
2659static void
2660xf86PrepareCrtcs(ScrnInfoPtr scrn)
2661{
2662 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
2663 int c;
2664
2665 for (c = 0; c < config->num_crtc; c++) {
2666#if RANDR_GET_CRTC_INTERFACE
2667 xf86CrtcPtr crtc = config->crtc[c];
2668 xf86OutputPtr output = NULL;
2669 uint32_t desired_outputs = 0, current_outputs = 0;
2670 int o;
2671
2672 for (o = 0; o < config->num_output; o++) {
2673 output = config->output[o];
2674 if (output->crtc == crtc)
2675 desired_outputs |= (1 << o);
2676 /* If we can't tell where it's mapped, force it off */
2677 if (!output->funcs->get_crtc) {
2678 desired_outputs = 0;
2679 break;
2680 }
2681 if ((*output->funcs->get_crtc) (output) == crtc)
2682 current_outputs |= (1 << o);
2683 }
2684
2685 /*
2686 * If mappings are different or the CRTC is unused,
2687 * we need to disable it
2688 */
2689 if (desired_outputs != current_outputs || !desired_outputs)
2690 (*crtc->funcs->dpms) (crtc, DPMSModeOff);
2691#else
2692 (*crtc->funcs->dpms) (crtc, DPMSModeOff);
2693#endif
2694 }
2695}
2696
2697/*
2698 * Using the desired mode information in each crtc, set
2699 * modes (used in EnterVT functions, or at server startup)
2700 */
2701
2702Bool
2703xf86SetDesiredModes(ScrnInfoPtr scrn)
2704{
2705 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
2706 xf86CrtcPtr crtc = config->crtc[0];
2707 int enabled = 0, failed = 0;
2708 int c;
2709
2710 /* A driver with this hook will take care of this */
2711 if (!crtc->funcs->set_mode_major) {
2712 xf86PrepareOutputs(scrn);
2713 xf86PrepareCrtcs(scrn);
2714 }
2715
2716 for (c = 0; c < config->num_crtc; c++) {
2717 xf86OutputPtr output = NULL;
2718 int o;
2719 RRTransformPtr transform;
2720
2721 crtc = config->crtc[c];
2722
2723 /* Skip disabled CRTCs */
2724 if (!crtc->enabled)
2725 continue;
2726
2727 if (xf86CompatOutput(scrn) && xf86CompatCrtc(scrn) == crtc)
2728 output = xf86CompatOutput(scrn);
2729 else {
2730 for (o = 0; o < config->num_output; o++)
2731 if (config->output[o]->crtc == crtc) {
2732 output = config->output[o];
2733 break;
2734 }
2735 }
2736 /* paranoia */
2737 if (!output)
2738 continue;
2739
2740 /* Mark that we'll need to re-set the mode for sure */
2741 memset(&crtc->mode, 0, sizeof(crtc->mode));
2742 if (!crtc->desiredMode.CrtcHDisplay) {
2743 DisplayModePtr mode =
2744 xf86OutputFindClosestMode(output, scrn->currentMode);
2745
2746 if (!mode)
2747 return FALSE;
2748 xf86SaveModeContents(&crtc->desiredMode, mode);
2749 crtc->desiredRotation = RR_Rotate_0;
2750 crtc->desiredTransformPresent = FALSE;
2751 crtc->desiredX = 0;
2752 crtc->desiredY = 0;
2753 }
2754
2755 if (crtc->desiredTransformPresent)
2756 transform = &crtc->desiredTransform;
2757 else
2758 transform = NULL;
2759 if (xf86CrtcSetModeTransform
2760 (crtc, &crtc->desiredMode, crtc->desiredRotation, transform,
2761 crtc->desiredX, crtc->desiredY)) {
2762 ++enabled;
2763 } else {
2764 for (o = 0; o < config->num_output; o++)
2765 if (config->output[o]->crtc == crtc)
2766 config->output[o]->crtc = NULL;
2767 crtc->enabled = FALSE;
2768 ++failed;
2769 }
2770 }
2771
2772 xf86DisableUnusedFunctions(scrn);
2773 return enabled != 0 || failed == 0;
2774}
2775
2776/**
2777 * In the current world order, there are lists of modes per output, which may
2778 * or may not include the mode that was asked to be set by XFree86's mode
2779 * selection. Find the closest one, in the following preference order:
2780 *
2781 * - Equality
2782 * - Closer in size to the requested mode, but no larger
2783 * - Closer in refresh rate to the requested mode.
2784 */
2785
2786DisplayModePtr
2787xf86OutputFindClosestMode(xf86OutputPtr output, DisplayModePtr desired)
2788{
2789 DisplayModePtr best = NULL, scan = NULL;
2790
2791 for (scan = output->probed_modes; scan != NULL; scan = scan->next) {
2792 /* If there's an exact match, we're done. */
2793 if (xf86ModesEqual(scan, desired)) {
2794 best = desired;
2795 break;
2796 }
2797
2798 /* Reject if it's larger than the desired mode. */
2799 if (scan->HDisplay > desired->HDisplay ||
2800 scan->VDisplay > desired->VDisplay) {
2801 continue;
2802 }
2803
2804 /*
2805 * If we haven't picked a best mode yet, use the first
2806 * one in the size range
2807 */
2808 if (best == NULL) {
2809 best = scan;
2810 continue;
2811 }
2812
2813 /* Find if it's closer to the right size than the current best
2814 * option.
2815 */
2816 if ((scan->HDisplay > best->HDisplay &&
2817 scan->VDisplay >= best->VDisplay) ||
2818 (scan->HDisplay >= best->HDisplay &&
2819 scan->VDisplay > best->VDisplay)) {
2820 best = scan;
2821 continue;
2822 }
2823
2824 /* Find if it's still closer to the right refresh than the current
2825 * best resolution.
2826 */
2827 if (scan->HDisplay == best->HDisplay &&
2828 scan->VDisplay == best->VDisplay &&
2829 (fabs(scan->VRefresh - desired->VRefresh) <
2830 fabs(best->VRefresh - desired->VRefresh))) {
2831 best = scan;
2832 }
2833 }
2834 return best;
2835}
2836
2837/**
2838 * When setting a mode through XFree86-VidModeExtension or XFree86-DGA,
2839 * take the specified mode and apply it to the crtc connected to the compat
2840 * output. Then, find similar modes for the other outputs, as with the
2841 * InitialConfiguration code above. The goal is to clone the desired
2842 * mode across all outputs that are currently active.
2843 */
2844
2845Bool
2846xf86SetSingleMode(ScrnInfoPtr pScrn, DisplayModePtr desired, Rotation rotation)
2847{
2848 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
2849 Bool ok = TRUE;
2850 xf86OutputPtr compat_output;
2851 DisplayModePtr compat_mode = NULL;
2852 int c;
2853
2854 /*
2855 * Let the compat output drive the final mode selection
2856 */
2857 compat_output = xf86CompatOutput(pScrn);
2858 if (compat_output)
2859 compat_mode = xf86OutputFindClosestMode(compat_output, desired);
2860 if (compat_mode)
2861 desired = compat_mode;
2862
2863 for (c = 0; c < config->num_crtc; c++) {
2864 xf86CrtcPtr crtc = config->crtc[c];
2865 DisplayModePtr crtc_mode = NULL;
2866 int o;
2867
2868 if (!crtc->enabled)
2869 continue;
2870
2871 for (o = 0; o < config->num_output; o++) {
2872 xf86OutputPtr output = config->output[o];
2873 DisplayModePtr output_mode;
2874
2875 /* skip outputs not on this crtc */
2876 if (output->crtc != crtc)
2877 continue;
2878
2879 if (crtc_mode) {
2880 output_mode = xf86OutputFindClosestMode(output, crtc_mode);
2881 if (output_mode != crtc_mode)
2882 output->crtc = NULL;
2883 }
2884 else
2885 crtc_mode = xf86OutputFindClosestMode(output, desired);
2886 }
2887 if (!crtc_mode) {
2888 crtc->enabled = FALSE;
2889 continue;
2890 }
2891 if (!xf86CrtcSetModeTransform(crtc, crtc_mode, rotation, NULL, 0, 0))
2892 ok = FALSE;
2893 else {
2894 xf86SaveModeContents(&crtc->desiredMode, crtc_mode);
2895 crtc->desiredRotation = rotation;
2896 crtc->desiredTransformPresent = FALSE;
2897 crtc->desiredX = 0;
2898 crtc->desiredY = 0;
2899 }
2900 }
2901 xf86DisableUnusedFunctions(pScrn);
2902#ifdef RANDR_12_INTERFACE
2903 xf86RandR12TellChanged(pScrn->pScreen);
2904#endif
2905 return ok;
2906}
2907
2908/**
2909 * Set the DPMS power mode of all outputs and CRTCs.
2910 *
2911 * If the new mode is off, it will turn off outputs and then CRTCs.
2912 * Otherwise, it will affect CRTCs before outputs.
2913 */
2914void
2915xf86DPMSSet(ScrnInfoPtr scrn, int mode, int flags)
2916{
2917 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
2918 int i;
2919
2920 if (!scrn->vtSema)
2921 return;
2922
2923 if (mode == DPMSModeOff) {
2924 for (i = 0; i < config->num_output; i++) {
2925 xf86OutputPtr output = config->output[i];
2926
2927 if (output->crtc != NULL)
2928 (*output->funcs->dpms) (output, mode);
2929 }
2930 }
2931
2932 for (i = 0; i < config->num_crtc; i++) {
2933 xf86CrtcPtr crtc = config->crtc[i];
2934
2935 if (crtc->enabled)
2936 (*crtc->funcs->dpms) (crtc, mode);
2937 }
2938
2939 if (mode != DPMSModeOff) {
2940 for (i = 0; i < config->num_output; i++) {
2941 xf86OutputPtr output = config->output[i];
2942
2943 if (output->crtc != NULL)
2944 (*output->funcs->dpms) (output, mode);
2945 }
2946 }
2947}
2948
2949/**
2950 * Implement the screensaver by just calling down into the driver DPMS hooks.
2951 *
2952 * Even for monitors with no DPMS support, by the definition of our DPMS hooks,
2953 * the outputs will still get disabled (blanked).
2954 */
2955Bool
2956xf86SaveScreen(ScreenPtr pScreen, int mode)
2957{
2958 ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
2959
2960 if (xf86IsUnblank(mode))
2961 xf86DPMSSet(pScrn, DPMSModeOn, 0);
2962 else
2963 xf86DPMSSet(pScrn, DPMSModeOff, 0);
2964
2965 return TRUE;
2966}
2967
2968/**
2969 * Disable all inactive crtcs and outputs
2970 */
2971void
2972xf86DisableUnusedFunctions(ScrnInfoPtr pScrn)
2973{
2974 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
2975 int o, c;
2976
2977 for (o = 0; o < xf86_config->num_output; o++) {
2978 xf86OutputPtr output = xf86_config->output[o];
2979
2980 if (!output->crtc)
2981 (*output->funcs->dpms) (output, DPMSModeOff);
2982 }
2983
2984 for (c = 0; c < xf86_config->num_crtc; c++) {
2985 xf86CrtcPtr crtc = xf86_config->crtc[c];
2986
2987 if (!crtc->enabled) {
2988 crtc->funcs->dpms(crtc, DPMSModeOff);
2989 memset(&crtc->mode, 0, sizeof(crtc->mode));
2990 xf86RotateDestroy(crtc);
2991 crtc->active = FALSE;
2992 }
2993 }
2994 if (pScrn->pScreen)
2995 xf86_crtc_notify(pScrn->pScreen);
2996 if (pScrn->ModeSet)
2997 pScrn->ModeSet(pScrn);
2998}
2999
3000#ifdef RANDR_12_INTERFACE
3001
3002#define EDID_ATOM_NAME "EDID"
3003
3004/**
3005 * Set the RandR EDID property
3006 */
3007static void
3008xf86OutputSetEDIDProperty(xf86OutputPtr output, void *data, int data_len)
3009{
3010 Atom edid_atom = MakeAtom(EDID_ATOM_NAME, sizeof(EDID_ATOM_NAME) - 1, TRUE);
3011
3012 /* This may get called before the RandR resources have been created */
3013 if (output->randr_output == NULL)
3014 return;
3015
3016 if (data_len != 0) {
3017 RRChangeOutputProperty(output->randr_output, edid_atom, XA_INTEGER, 8,
3018 PropModeReplace, data_len, data, FALSE, TRUE);
3019 }
3020 else {
3021 RRDeleteOutputProperty(output->randr_output, edid_atom);
3022 }
3023}
3024
3025#endif
3026
3027/* Pull out a phyiscal size from a detailed timing if available. */
3028struct det_phySize_parameter {
3029 xf86OutputPtr output;
3030 ddc_quirk_t quirks;
3031 Bool ret;
3032};
3033
3034static void
3035handle_detailed_physical_size(struct detailed_monitor_section
3036 *det_mon, void *data)
3037{
3038 struct det_phySize_parameter *p;
3039
3040 p = (struct det_phySize_parameter *) data;
3041
3042 if (p->ret == TRUE)
3043 return;
3044
3045 xf86DetTimingApplyQuirks(det_mon, p->quirks,
3046 p->output->MonInfo->features.hsize,
3047 p->output->MonInfo->features.vsize);
3048 if (det_mon->type == DT &&
3049 det_mon->section.d_timings.h_size != 0 &&
3050 det_mon->section.d_timings.v_size != 0) {
3051 /* some sanity checking for aspect ratio:
3052 assume any h / v (or v / h) > 2.4 to be bogus.
3053 This would even include cinemascope */
3054 if (((det_mon->section.d_timings.h_size * 5) <
3055 (det_mon->section.d_timings.v_size * 12)) &&
3056 ((det_mon->section.d_timings.v_size * 5) <
3057 (det_mon->section.d_timings.h_size * 12))) {
3058 p->output->mm_width = det_mon->section.d_timings.h_size;
3059 p->output->mm_height = det_mon->section.d_timings.v_size;
3060 p->ret = TRUE;
3061 } else
3062 xf86DrvMsg(p->output->scrn->scrnIndex, X_WARNING,
3063 "Output %s: Strange aspect ratio (%i/%i), "
3064 "consider adding a quirk\n", p->output->name,
3065 det_mon->section.d_timings.h_size,
3066 det_mon->section.d_timings.v_size);
3067 }
3068}
3069
3070/**
3071 * Set the EDID information for the specified output
3072 */
3073void
3074xf86OutputSetEDID(xf86OutputPtr output, xf86MonPtr edid_mon)
3075{
3076 ScrnInfoPtr scrn = output->scrn;
3077 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
3078 Bool debug_modes = config->debug_modes || xf86Initialising;
3079
3080#ifdef RANDR_12_INTERFACE
3081 int size;
3082#endif
3083
3084 free(output->MonInfo);
3085
3086 output->MonInfo = edid_mon;
3087 output->mm_width = 0;
3088 output->mm_height = 0;
3089
3090 if (debug_modes) {
3091 xf86DrvMsg(scrn->scrnIndex, X_INFO, "EDID for output %s\n",
3092 output->name);
3093 xf86PrintEDID(edid_mon);
3094 }
3095
3096 /* Set the DDC properties for the 'compat' output */
3097 if (output == xf86CompatOutput(scrn))
3098 xf86SetDDCproperties(scrn, edid_mon);
3099
3100#ifdef RANDR_12_INTERFACE
3101 /* Set the RandR output properties */
3102 size = 0;
3103 if (edid_mon) {
3104 if (edid_mon->ver.version == 1) {
3105 size = 128;
3106 if (edid_mon->flags & EDID_COMPLETE_RAWDATA)
3107 size += edid_mon->no_sections * 128;
3108 }
3109 else if (edid_mon->ver.version == 2)
3110 size = 256;
3111 }
3112 xf86OutputSetEDIDProperty(output, edid_mon ? edid_mon->rawData : NULL,
3113 size);
3114#endif
3115
3116 if (edid_mon) {
3117
3118 struct det_phySize_parameter p;
3119
3120 p.output = output;
3121 p.quirks = xf86DDCDetectQuirks(scrn->scrnIndex, edid_mon, FALSE);
3122 p.ret = FALSE;
3123 xf86ForEachDetailedBlock(edid_mon, handle_detailed_physical_size, &p);
3124
3125 /* if no mm size is available from a detailed timing, check the max size field */
3126 if ((!output->mm_width || !output->mm_height) &&
3127 (edid_mon->features.hsize && edid_mon->features.vsize)) {
3128 output->mm_width = edid_mon->features.hsize * 10;
3129 output->mm_height = edid_mon->features.vsize * 10;
3130 }
3131 }
3132}
3133
3134/**
3135 * Return the list of modes supported by the EDID information
3136 * stored in 'output'
3137 */
3138DisplayModePtr
3139xf86OutputGetEDIDModes(xf86OutputPtr output)
3140{
3141 ScrnInfoPtr scrn = output->scrn;
3142 xf86MonPtr edid_mon = output->MonInfo;
3143
3144 if (!edid_mon)
3145 return NULL;
3146 return xf86DDCGetModes(scrn->scrnIndex, edid_mon);
3147}
3148
3149/* maybe we should care about DDC1? meh. */
3150xf86MonPtr
3151xf86OutputGetEDID(xf86OutputPtr output, I2CBusPtr pDDCBus)
3152{
3153 ScrnInfoPtr scrn = output->scrn;
3154 xf86MonPtr mon;
3155
3156 mon = xf86DoEEDID(scrn, pDDCBus, TRUE);
3157 if (mon)
3158 xf86DDCApplyQuirks(scrn->scrnIndex, mon);
3159
3160 return mon;
3161}
3162
3163static const char *_xf86ConnectorNames[] = {
3164 "None", "VGA", "DVI-I", "DVI-D",
3165 "DVI-A", "Composite", "S-Video",
3166 "Component", "LFP", "Proprietary",
3167 "HDMI", "DisplayPort",
3168};
3169
3170const char *
3171xf86ConnectorGetName(xf86ConnectorType connector)
3172{
3173 return _xf86ConnectorNames[connector];
3174}
3175
3176static void
3177x86_crtc_box_intersect(BoxPtr dest, BoxPtr a, BoxPtr b)
3178{
3179 dest->x1 = a->x1 > b->x1 ? a->x1 : b->x1;
3180 dest->x2 = a->x2 < b->x2 ? a->x2 : b->x2;
3181 dest->y1 = a->y1 > b->y1 ? a->y1 : b->y1;
3182 dest->y2 = a->y2 < b->y2 ? a->y2 : b->y2;
3183
3184 if (dest->x1 >= dest->x2 || dest->y1 >= dest->y2)
3185 dest->x1 = dest->x2 = dest->y1 = dest->y2 = 0;
3186}
3187
3188static void
3189x86_crtc_box(xf86CrtcPtr crtc, BoxPtr crtc_box)
3190{
3191 if (crtc->enabled) {
3192 crtc_box->x1 = crtc->x;
3193 crtc_box->x2 = crtc->x + xf86ModeWidth(&crtc->mode, crtc->rotation);
3194 crtc_box->y1 = crtc->y;
3195 crtc_box->y2 = crtc->y + xf86ModeHeight(&crtc->mode, crtc->rotation);
3196 }
3197 else
3198 crtc_box->x1 = crtc_box->x2 = crtc_box->y1 = crtc_box->y2 = 0;
3199}
3200
3201static int
3202xf86_crtc_box_area(BoxPtr box)
3203{
3204 return (int) (box->x2 - box->x1) * (int) (box->y2 - box->y1);
3205}
3206
3207#ifdef XV
3208/*
3209 * Return the crtc covering 'box'. If two crtcs cover a portion of
3210 * 'box', then prefer 'desired'. If 'desired' is NULL, then prefer the crtc
3211 * with greater coverage
3212 */
3213
3214static xf86CrtcPtr
3215xf86_covering_crtc(ScrnInfoPtr pScrn,
3216 BoxPtr box, xf86CrtcPtr desired, BoxPtr crtc_box_ret)
3217{
3218 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
3219 xf86CrtcPtr crtc, best_crtc;
3220 int coverage, best_coverage;
3221 int c;
3222 BoxRec crtc_box, cover_box;
3223
3224 best_crtc = NULL;
3225 best_coverage = 0;
3226 crtc_box_ret->x1 = 0;
3227 crtc_box_ret->x2 = 0;
3228 crtc_box_ret->y1 = 0;
3229 crtc_box_ret->y2 = 0;
3230 for (c = 0; c < xf86_config->num_crtc; c++) {
3231 crtc = xf86_config->crtc[c];
3232 x86_crtc_box(crtc, &crtc_box);
3233 x86_crtc_box_intersect(&cover_box, &crtc_box, box);
3234 coverage = xf86_crtc_box_area(&cover_box);
3235 if (coverage && crtc == desired) {
3236 *crtc_box_ret = crtc_box;
3237 return crtc;
3238 }
3239 else if (coverage > best_coverage) {
3240 *crtc_box_ret = crtc_box;
3241 best_crtc = crtc;
3242 best_coverage = coverage;
3243 }
3244 }
3245 return best_crtc;
3246}
3247
3248/*
3249 * For overlay video, compute the relevant CRTC and
3250 * clip video to that.
3251 *
3252 * returning FALSE means there was a memory failure of some kind,
3253 * not that the video shouldn't be displayed
3254 */
3255
3256Bool
3257xf86_crtc_clip_video_helper(ScrnInfoPtr pScrn,
3258 xf86CrtcPtr * crtc_ret,
3259 xf86CrtcPtr desired_crtc,
3260 BoxPtr dst,
3261 INT32 *xa,
3262 INT32 *xb,
3263 INT32 *ya,
3264 INT32 *yb, RegionPtr reg, INT32 width, INT32 height)
3265{
3266 Bool ret;
3267 RegionRec crtc_region_local;
3268 RegionPtr crtc_region = reg;
3269
3270 if (crtc_ret) {
3271 BoxRec crtc_box;
3272 xf86CrtcPtr crtc = xf86_covering_crtc(pScrn, dst,
3273 desired_crtc,
3274 &crtc_box);
3275
3276 if (crtc) {
3277 RegionInit(&crtc_region_local, &crtc_box, 1);
3278 crtc_region = &crtc_region_local;
3279 RegionIntersect(crtc_region, crtc_region, reg);
3280 }
3281 *crtc_ret = crtc;
3282 }
3283
3284 ret = xf86XVClipVideoHelper(dst, xa, xb, ya, yb,
3285 crtc_region, width, height);
3286
3287 if (crtc_region != reg)
3288 RegionUninit(&crtc_region_local);
3289
3290 return ret;
3291}
3292#endif
3293
3294xf86_crtc_notify_proc_ptr
3295xf86_wrap_crtc_notify(ScreenPtr screen, xf86_crtc_notify_proc_ptr new)
3296{
3297 if (xf86CrtcConfigPrivateIndex != -1) {
3298 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
3299 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
3300 xf86_crtc_notify_proc_ptr old;
3301
3302 old = config->xf86_crtc_notify;
3303 config->xf86_crtc_notify = new;
3304 return old;
3305 }
3306 return NULL;
3307}
3308
3309void
3310xf86_unwrap_crtc_notify(ScreenPtr screen, xf86_crtc_notify_proc_ptr old)
3311{
3312 if (xf86CrtcConfigPrivateIndex != -1) {
3313 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
3314 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
3315
3316 config->xf86_crtc_notify = old;
3317 }
3318}
3319
3320void
3321xf86_crtc_notify(ScreenPtr screen)
3322{
3323 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
3324 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
3325
3326 if (config->xf86_crtc_notify)
3327 config->xf86_crtc_notify(screen);
3328}
3329
3330Bool
3331xf86_crtc_supports_gamma(ScrnInfoPtr pScrn)
3332{
3333 if (xf86CrtcConfigPrivateIndex != -1) {
3334 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
3335 xf86CrtcPtr crtc;
3336
3337 /* for multiple drivers loaded we need this */
3338 if (!xf86_config)
3339 return FALSE;
3340 if (xf86_config->num_crtc == 0)
3341 return FALSE;
3342 crtc = xf86_config->crtc[0];
3343
3344 return crtc->funcs->gamma_set != NULL;
3345 }
3346
3347 return FALSE;
3348}
3349
3350void
3351xf86ProviderSetup(ScrnInfoPtr scrn,
3352 const xf86ProviderFuncsRec *funcs, const char *name)
3353{
3354 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
3355
3356 assert(!xf86_config->name);
3357 assert(name);
3358
3359 xf86_config->name = strdup(name);
3360 xf86_config->provider_funcs = funcs;
3361#ifdef RANDR_12_INTERFACE
3362 xf86_config->randr_provider = NULL;
3363#endif
3364}
3365
3366void
3367xf86DetachAllCrtc(ScrnInfoPtr scrn)
3368{
3369 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
3370 int i;
3371
3372 for (i = 0; i < xf86_config->num_crtc; i++) {
3373 xf86CrtcPtr crtc = xf86_config->crtc[i];
3374
3375 if (crtc->randr_crtc)
3376 RRCrtcDetachScanoutPixmap(crtc->randr_crtc);
3377
3378 /* dpms off */
3379 (*crtc->funcs->dpms) (crtc, DPMSModeOff);
3380 /* force a reset the next time its used */
3381 crtc->randr_crtc->mode = NULL;
3382 crtc->mode.HDisplay = 0;
3383 crtc->x = crtc->y = 0;
3384 }
3385}