2 * Copyright 2008 Maarten Maathuis
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 #include "nv50_randr.h"
24 #include "X11/Xatom.h"
27 * A randr-1.2 wrapper around the NV50 driver.
35 nv50_crtc_dpms(xf86CrtcPtr crtc, int mode)
37 ScrnInfoPtr pScrn = crtc->scrn;
38 NV50CrtcPrivatePtr nv_crtc = crtc->driver_private;
39 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_crtc_dpms is called with mode %d for %s.\n", mode, nv_crtc->crtc->index ? "CRTC1" : "CRTC0");
43 nv_crtc->crtc->active = TRUE;
49 nv_crtc->crtc->active = FALSE;
55 nv50_crtc_lock(xf86CrtcPtr crtc)
61 nv50_crtc_mode_fixup(xf86CrtcPtr crtc, DisplayModePtr mode,
62 DisplayModePtr adjusted_mode)
68 nv50_crtc_prepare(xf86CrtcPtr crtc)
70 ScrnInfoPtr pScrn = crtc->scrn;
71 NV50CrtcPrivatePtr nv_crtc = crtc->driver_private;
72 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_crtc_prepare is called for %s.\n", nv_crtc->crtc->index ? "CRTC1" : "CRTC0");
74 NVPtr pNv = NVPTR(pScrn);
76 nv_crtc->crtc->active = TRUE;
77 nv_crtc->crtc->modeset_lock = TRUE;
79 nouveauOutputPtr output;
81 /* Detach any unused outputs. */
82 for (output = pNv->output; output != NULL; output = output->next) {
84 output->ModeSet(output, NULL);
89 nv50_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, DisplayModePtr adjusted_mode, int x, int y)
91 ScrnInfoPtr pScrn = crtc->scrn;
92 NV50CrtcPrivatePtr nv_crtc = crtc->driver_private;
93 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_crtc_mode_set is called for %s.\n", nv_crtc->crtc->index ? "CRTC1" : "CRTC0");
95 NVPtr pNv = NVPTR(pScrn);
97 /* Maybe move this elsewhere? */
98 if (crtc->rotatedData) {
99 nv_crtc->crtc->SetFB(nv_crtc->crtc, nv_crtc->shadow);
100 nv_crtc->crtc->SetFBOffset(nv_crtc->crtc, 0, 0);
102 nv_crtc->crtc->SetFB(nv_crtc->crtc, pNv->FB);
103 nv_crtc->crtc->SetFBOffset(nv_crtc->crtc, x, y);
105 nv_crtc->crtc->ModeSet(nv_crtc->crtc, mode);
109 nv50_crtc_gamma_set(xf86CrtcPtr crtc, CARD16 *red, CARD16 *green, CARD16 *blue, int size)
111 ScrnInfoPtr pScrn = crtc->scrn;
112 NV50CrtcPrivatePtr nv_crtc = crtc->driver_private;
113 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_crtc_gamma_set is called for %s.\n", nv_crtc->crtc->index ? "CRTC1" : "CRTC0");
115 nv_crtc->crtc->GammaSet(nv_crtc->crtc, (uint16_t *) red, (uint16_t *) green, (uint16_t *) blue, size);
119 nv50_crtc_commit(xf86CrtcPtr crtc)
121 ScrnInfoPtr pScrn = crtc->scrn;
122 NV50CrtcPrivatePtr nv_crtc = crtc->driver_private;
123 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_crtc_commit is called for %s.\n", nv_crtc->crtc->index ? "CRTC1" : "CRTC0");
125 NVPtr pNv = NVPTR(pScrn);
127 /* Let's detect any outputs and connectors that have gone inactive. */
128 uint8_t crtc_active_mask = 0;
130 nouveauOutputPtr output;
132 for (i = 0; i < MAX_NUM_DCB_ENTRIES; i++) {
133 Bool connector_active = FALSE;
134 for (j = 0; j < MAX_OUTPUTS_PER_CONNECTOR; j++) {
135 output = pNv->connector[i]->outputs[j];
138 crtc_active_mask |= 1 << output->crtc->index;
139 connector_active = TRUE;
141 output->active = FALSE;
146 pNv->connector[i]->active = connector_active;
149 /* Blank any crtc's that are inactive. */
150 if (!(crtc_active_mask & (1 << 0)))
151 pNv->crtc[0]->Blank(pNv->crtc[0], TRUE);
153 if (!(crtc_active_mask & (1 << 1)))
154 pNv->crtc[1]->Blank(pNv->crtc[1], TRUE);
156 xf86_reload_cursors(pScrn->pScreen);
158 NV50DisplayCommand(pScrn, NV50_UPDATE_DISPLAY, 0);
160 nv_crtc->crtc->modeset_lock = FALSE;
168 nv50_crtc_show_cursor(xf86CrtcPtr crtc)
170 //ScrnInfoPtr pScrn = crtc->scrn;
171 NV50CrtcPrivatePtr nv_crtc = crtc->driver_private;
172 //xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_crtc_show_cursor is called for %s.\n", nv_crtc->crtc->index ? "CRTC1" : "CRTC0");
174 nv_crtc->crtc->ShowCursor(nv_crtc->crtc, FALSE);
178 nv50_crtc_hide_cursor(xf86CrtcPtr crtc)
180 //ScrnInfoPtr pScrn = crtc->scrn;
181 NV50CrtcPrivatePtr nv_crtc = crtc->driver_private;
182 //xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_crtc_hide_cursor is called for %s.\n", nv_crtc->crtc->index ? "CRTC1" : "CRTC0");
184 nv_crtc->crtc->HideCursor(nv_crtc->crtc, FALSE);
188 nv50_crtc_set_cursor_position(xf86CrtcPtr crtc, int x, int y)
190 NV50CrtcPrivatePtr nv_crtc = crtc->driver_private;
192 nv_crtc->crtc->SetCursorPosition(nv_crtc->crtc, x, y);
196 nv50_crtc_load_cursor_argb(xf86CrtcPtr crtc, CARD32 *src)
198 //ScrnInfoPtr pScrn = crtc->scrn;
199 NV50CrtcPrivatePtr nv_crtc = crtc->driver_private;
200 //xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_crtc_load_cursor_argb is called for %s.\n", nv_crtc->crtc->index ? "CRTC1" : "CRTC0");
202 nv_crtc->crtc->LoadCursor(nv_crtc->crtc, TRUE, (uint32_t *) src);
205 /* This stuff isn't ready for NOUVEAU_EXA_PIXMAPS, but can be easily ported. */
207 nv50_crtc_shadow_allocate (xf86CrtcPtr crtc, int width, int height)
209 ScrnInfoPtr pScrn = crtc->scrn;
210 NV50CrtcPrivatePtr nv_crtc = crtc->driver_private;
211 NVPtr pNv = NVPTR(pScrn);
214 ErrorF("nv50_crtc_shadow_allocate\n");
216 pitch = pScrn->displayWidth * (pScrn->bitsPerPixel/8);
217 size = pitch * height;
219 if (nouveau_bo_new(pNv->dev, NOUVEAU_BO_VRAM | NOUVEAU_BO_PIN,
220 64, size, &nv_crtc->shadow)) {
221 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to allocate memory for shadow buffer!\n");
225 if (nv_crtc->shadow && nouveau_bo_map(nv_crtc->shadow, NOUVEAU_BO_RDWR)) {
226 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
227 "Failed to map shadow buffer.\n");
231 pNv->shadow[nv_crtc->crtc->index] = nv_crtc->shadow;
233 return nv_crtc->shadow->map;
237 nv50_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
239 ScrnInfoPtr pScrn = crtc->scrn;
241 PixmapPtr rotate_pixmap;
243 ErrorF("nv50_crtc_shadow_create\n");
246 data = crtc->funcs->shadow_allocate (crtc, width, height);
248 pitch = pScrn->displayWidth * (pScrn->bitsPerPixel/8);
250 rotate_pixmap = GetScratchPixmapHeader(pScrn->pScreen,
257 if (rotate_pixmap == NULL) {
258 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
259 "Couldn't allocate shadow pixmap for rotated CRTC\n");
262 return rotate_pixmap;
266 nv50_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data)
268 ScrnInfoPtr pScrn = crtc->scrn;
269 NV50CrtcPrivatePtr nv_crtc = crtc->driver_private;
270 NVPtr pNv = NVPTR(pScrn);
271 ScreenPtr pScreen = pScrn->pScreen;
273 ErrorF("nv50_crtc_shadow_destroy\n");
276 pScreen->DestroyPixmap(rotate_pixmap);
279 nouveau_bo_del(&nv_crtc->shadow);
281 nv_crtc->shadow = NULL;
282 /* for easy acces by exa */
283 pNv->shadow[nv_crtc->crtc->index] = NULL;
287 nv50_crtc_destroy(xf86CrtcPtr crtc)
289 xfree(crtc->driver_private);
292 static const xf86CrtcFuncsRec nv50_crtc_funcs = {
293 .dpms = nv50_crtc_dpms,
296 .lock = nv50_crtc_lock,
298 .mode_fixup = nv50_crtc_mode_fixup,
299 .prepare = nv50_crtc_prepare,
300 .mode_set = nv50_crtc_mode_set,
301 .gamma_set = nv50_crtc_gamma_set,
302 .commit = nv50_crtc_commit,
303 .shadow_create = nv50_crtc_shadow_create,
304 .shadow_allocate = nv50_crtc_shadow_allocate,
305 .shadow_destroy = nv50_crtc_shadow_destroy,
306 .set_cursor_position = nv50_crtc_set_cursor_position,
307 .show_cursor = nv50_crtc_show_cursor,
308 .hide_cursor = nv50_crtc_hide_cursor,
309 .load_cursor_argb = nv50_crtc_load_cursor_argb,
310 .destroy = nv50_crtc_destroy,
314 nv50_crtc_init(ScrnInfoPtr pScrn, int crtc_num)
316 NVPtr pNv = NVPTR(pScrn);
318 NV50CrtcPrivatePtr nv_crtc;
320 crtc = xf86CrtcCreate(pScrn, &nv50_crtc_funcs);
324 nv_crtc = xnfcalloc (sizeof (NV50CrtcPrivateRec), 1);
325 nv_crtc->crtc = pNv->crtc[crtc_num];
327 crtc->driver_private = nv_crtc;
336 nv50_output_dpms(xf86OutputPtr output, int mode)
338 ScrnInfoPtr pScrn = output->scrn;
339 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_dpms is called with mode %d.\n", mode);
341 NVPtr pNv = NVPTR(pScrn);
342 NV50OutputPrivatePtr nv_output = output->driver_private;
344 /* Keep the crtc wiring consistent with randr-1.2 */
346 NV50CrtcPrivatePtr nv_crtc = output->crtc->driver_private;
347 nv_output->output->crtc = nv_crtc->crtc;
349 nv_output->output->crtc = NULL;
352 /* Our crtc's map 1:1 onto randr-1.2 crtc's. */
355 nv_output->output->active = TRUE;
357 case DPMSModeSuspend:
358 case DPMSModeStandby:
361 nv_output->output->active = FALSE;
365 /* Set dpms on all outputs for ths connector, just to be safe. */
366 nouveauConnectorPtr connector = pNv->connector[nv_output->output->dcb->bus];
368 for (i = 0; i < MAX_OUTPUTS_PER_CONNECTOR; i++) {
369 if (connector->outputs[i])
370 connector->outputs[i]->SetPowerMode(connector->outputs[i], mode);
375 nv50_output_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
377 ScrnInfoPtr pScrn = output->scrn;
378 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_mode_valid is called.\n");
380 NV50OutputPrivatePtr nv_output = output->driver_private;
382 return nv_output->output->ModeValid(nv_output->output, mode);
386 nv50_output_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode)
392 nv50_output_mode_set(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode)
394 ScrnInfoPtr pScrn = output->scrn;
395 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_mode_set is called.\n");
397 NV50OutputPrivatePtr nv_output = output->driver_private;
399 nv_output->output->ModeSet(nv_output->output, mode);
402 static xf86OutputStatus
403 nv50_output_detect(xf86OutputPtr output)
405 ScrnInfoPtr pScrn = output->scrn;
406 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_detect is called.\n");
408 NVPtr pNv = NVPTR(pScrn);
409 NV50OutputPrivatePtr nv_output = output->driver_private;
410 nouveauConnectorPtr connector = pNv->connector[nv_output->output->dcb->bus];
413 return XF86OutputStatusDisconnected;
415 Bool detect_present = FALSE;
416 Bool detect_digital = FALSE;
417 xf86MonPtr ddc_mon = connector->DDCDetect(connector);
421 for (i = 0; i < MAX_OUTPUTS_PER_CONNECTOR; i++) {
422 if (connector->outputs[i] && connector->outputs[i]->Detect) {
423 detect_present = connector->outputs[i]->Detect(connector->outputs[i]);
424 if (detect_present) {
425 if (connector->outputs[i]->type == OUTPUT_TMDS || connector->outputs[i]->type == OUTPUT_LVDS)
426 detect_digital = TRUE;
433 /* HACK: assume that a connector only holds output in the case of a tv-out */
434 if (nv_output->output->type == OUTPUT_TV)
435 return XF86OutputStatusUnknown;
438 * We abuse randr-1.2 outputs as connector, so here we have to determine what actual output is connected to the connector.
440 if (ddc_mon || detect_present) {
441 Bool is_digital = FALSE;
443 nouveauCrtcPtr crtc_backup = nv_output->output->crtc;
444 nv_output->output->crtc = NULL;
445 nv_output->output->connector = NULL;
448 is_digital = ddc_mon->features.input_type;
450 is_digital = detect_digital;
452 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Detected a %s output on %s\n", is_digital ? "Digital" : "Analog", connector->name);
454 for (i = 0; i < MAX_OUTPUTS_PER_CONNECTOR; i++) {
455 if (!is_digital && (connector->outputs[i]->type == OUTPUT_ANALOG || connector->outputs[i]->type == OUTPUT_TV)) {
457 } else if (is_digital && (connector->outputs[i]->type == OUTPUT_TMDS || connector->outputs[i]->type == OUTPUT_LVDS)) {
461 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Found a suitable output, index %d\n", i);
462 connector->connected_output = i;
463 connector->outputs[i]->connector = connector;
464 connector->outputs[i]->crtc = crtc_backup;
465 nv_output->output = connector->outputs[i];
471 if (ddc_mon || detect_present)
472 return XF86OutputStatusConnected;
474 return XF86OutputStatusDisconnected;
477 static DisplayModePtr
478 nv50_output_get_modes(xf86OutputPtr output)
480 ScrnInfoPtr pScrn = output->scrn;
481 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_detect is called.\n");
483 NVPtr pNv = NVPTR(pScrn);
484 NV50OutputPrivatePtr nv_output = output->driver_private;
485 nouveauConnectorPtr connector = pNv->connector[nv_output->output->dcb->bus];
487 xf86MonPtr ddc_mon = connector->DDCDetect(connector);
489 xf86OutputSetEDID(output, ddc_mon);
491 DisplayModePtr ddc_modes = connector->GetDDCModes(connector);
493 xf86DeleteMode(&nv_output->output->native_mode, nv_output->output->native_mode);
494 nv_output->output->native_mode = NULL;
495 if (nv_output->output->crtc)
496 nv_output->output->crtc->native_mode = NULL;
498 /* typically only LVDS will hit this code path. */
500 if (pNv->VBIOS.fp.native_mode && nv_output->output->type == OUTPUT_LVDS) {
501 ddc_modes = xf86DuplicateMode(pNv->VBIOS.fp.native_mode);
502 xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "LVDS: Using a bios mode, which should work, if it doesn't please report.\n");
506 if (!ddc_modes && nv_output->output->type == OUTPUT_LVDS) {
507 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "LVDS and no modes found, bailing out.\n");
511 /* NV5x hardware can also do scaling on analog connections. */
513 /* Use the first preferred mode as native mode. */
516 /* Find the preferred mode. */
517 for (mode = ddc_modes; mode != NULL; mode = mode->next) {
518 if (mode->type & M_T_PREFERRED) {
519 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
520 "%s: preferred mode is %s\n",
521 output->name, mode->name);
526 /* TODO: Scaling needs a native mode, maybe fail in a better way. */
529 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
530 "%s: no preferred mode found, using %s\n",
531 output->name, mode->name);
534 nv_output->output->native_mode = xf86DuplicateMode(mode);
537 /* No ddc means no native mode, so make one up to avoid crashes. */
538 if (!nv_output->output->native_mode)
539 nv_output->output->native_mode = xf86CVTMode(1024, 768, 60.0, FALSE, FALSE);
541 xf86SetModeCrtc(nv_output->output->native_mode, 0);
543 if (nv_output->output->crtc)
544 nv_output->output->crtc->native_mode = nv_output->output->native_mode;
550 nv50_output_destroy(xf86OutputPtr output)
552 NV50OutputPrivatePtr nv_output = output->driver_private;
554 xf86DeleteMode(&nv_output->output->native_mode, nv_output->output->native_mode);
556 xfree(output->driver_private);
557 output->driver_private = NULL;
561 nv50_output_prepare(xf86OutputPtr output)
563 ScrnInfoPtr pScrn = output->scrn;
564 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_prepare is called.\n");
566 NV50OutputPrivatePtr nv_output = output->driver_private;
567 NV50CrtcPrivatePtr nv_crtc = output->crtc->driver_private;
569 /* Set the real crtc now. */
570 nv_output->output->crtc = nv_crtc->crtc;
572 /* Transfer some output properties to the crtc for easy access. */
573 nv_output->output->crtc->scale_mode = nv_output->output->scale_mode;
574 nv_output->output->crtc->dithering = nv_output->output->dithering;
575 nv_output->output->crtc->native_mode = nv_output->output->native_mode;
577 if (nv_output->output->scale_mode != SCALE_PANEL)
578 nv_output->output->crtc->use_native_mode = TRUE;
580 nv_output->output->crtc->use_native_mode = FALSE;
584 nv50_output_commit(xf86OutputPtr output)
586 ScrnInfoPtr pScrn = output->scrn;
587 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_commit is called.\n");
595 * Several scaling modes exist, let the user choose.
597 #define SCALING_MODE_NAME "SCALING_MODE"
598 static const struct {
600 enum scaling_modes mode;
602 { "panel", SCALE_PANEL },
603 { "fullscreen", SCALE_FULLSCREEN },
604 { "aspect", SCALE_ASPECT },
605 { "noscale", SCALE_NOSCALE },
606 { NULL, SCALE_INVALID}
608 static Atom scaling_mode_atom;
610 #define DITHERING_MODE_NAME "DITHERING"
611 static Atom dithering_atom;
614 nv_scaling_mode_lookup(char *name, int size)
618 /* for when name is zero terminated */
622 for (i = 0; scaling_mode[i].name; i++)
623 /* We're getting non-terminated strings */
624 if (strlen(scaling_mode[i].name) >= size &&
625 !strncasecmp(name, scaling_mode[i].name, size))
628 return scaling_mode[i].mode;
632 nv50_output_create_resources(xf86OutputPtr output)
634 NV50OutputPrivatePtr nv_output = output->driver_private;
635 ScrnInfoPtr pScrn = output->scrn;
636 INT32 dithering_range[2] = { 0, 1 };
640 * Setup scaling mode property.
642 scaling_mode_atom = MakeAtom(SCALING_MODE_NAME, sizeof(SCALING_MODE_NAME) - 1, TRUE);
644 error = RRConfigureOutputProperty(output->randr_output,
645 scaling_mode_atom, TRUE, FALSE, FALSE,
649 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
650 "RRConfigureOutputProperty error, %d\n", error);
653 char *existing_scale_name = NULL;
654 for (i = 0; scaling_mode[i].name; i++)
655 if (scaling_mode[i].mode == nv_output->output->scale_mode)
656 existing_scale_name = scaling_mode[i].name;
658 error = RRChangeOutputProperty(output->randr_output, scaling_mode_atom,
659 XA_STRING, 8, PropModeReplace,
660 strlen(existing_scale_name),
661 existing_scale_name, FALSE, TRUE);
664 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
665 "Failed to set scaling mode, %d\n", error);
668 if (nv_output->output->type == OUTPUT_TMDS || nv_output->output->type == OUTPUT_LVDS) {
670 * Setup dithering property.
672 dithering_atom = MakeAtom(DITHERING_MODE_NAME, sizeof(DITHERING_MODE_NAME) - 1, TRUE);
674 error = RRConfigureOutputProperty(output->randr_output,
675 dithering_atom, TRUE, TRUE, FALSE,
679 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
680 "RRConfigureOutputProperty error, %d\n", error);
683 error = RRChangeOutputProperty(output->randr_output, dithering_atom,
684 XA_INTEGER, 32, PropModeReplace, 1, &nv_output->output->dithering,
688 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
689 "Failed to set dithering mode, %d\n", error);
695 nv50_output_set_property(xf86OutputPtr output, Atom property,
696 RRPropertyValuePtr value)
698 NV50OutputPrivatePtr nv_output = output->driver_private;
700 if (property == scaling_mode_atom) {
704 if (value->type != XA_STRING || value->format != 8)
707 name = (char *) value->data;
709 /* Match a string to a scaling mode */
710 ret = nv_scaling_mode_lookup(name, value->size);
711 if (ret == SCALE_INVALID)
714 /* LVDS must always use gpu scaling. */
715 if (ret == SCALE_PANEL && nv_output->output->type == OUTPUT_LVDS)
718 nv_output->output->scale_mode = ret;
719 if (nv_output->output->crtc) /* normally prepare sets all these things for the crtc. */
720 nv_output->output->crtc->scale_mode = ret;
722 } else if (property == dithering_atom) {
723 if (value->type != XA_INTEGER || value->format != 32)
726 int32_t val = *(int32_t *) value->data;
728 if (val < 0 || val > 1)
731 nv_output->output->dithering = val;
732 if (nv_output->output->crtc) /* normally prepare sets all these things for the crtc. */
733 nv_output->output->crtc->dithering = val;
740 static const xf86OutputFuncsRec nv50_output_funcs = {
741 .dpms = nv50_output_dpms,
744 .mode_valid = nv50_output_mode_valid,
745 .mode_fixup = nv50_output_mode_fixup,
746 .mode_set = nv50_output_mode_set,
747 .detect = nv50_output_detect,
748 .get_modes = nv50_output_get_modes,
749 .destroy = nv50_output_destroy,
750 .prepare = nv50_output_prepare,
751 .commit = nv50_output_commit,
752 .create_resources = nv50_output_create_resources,
753 .set_property = nv50_output_set_property,
757 nv50_output_create(ScrnInfoPtr pScrn)
759 NVPtr pNv = NVPTR(pScrn);
760 xf86OutputPtr output;
761 NV50OutputPrivatePtr nv_output;
764 /* this is a 1:1 hookup of the connectors. */
765 for (i = 0; i < MAX_NUM_DCB_ENTRIES; i++) {
766 if (!(pNv->connector[i]->outputs[0]))
767 continue; /* An empty connector is not useful. */
769 if (!(output = xf86OutputCreate(pScrn, &nv50_output_funcs, pNv->connector[i]->name)))
772 if (!(nv_output = xnfcalloc(sizeof(NV50OutputPrivateRec), 1)))
775 output->driver_private = nv_output;
777 nv_output->output = pNv->connector[i]->outputs[0]; /* initially just wire up the first output available. */
779 output->possible_crtcs = nv_output->output->allowed_crtc;
780 output->possible_clones = 0;
782 if (nv_output->output->type == OUTPUT_TMDS || nv_output->output->type == OUTPUT_LVDS) {
783 output->doubleScanAllowed = false;
784 output->interlaceAllowed = false;
786 output->doubleScanAllowed = true;
787 output->interlaceAllowed = true;