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) {
473 return XF86OutputStatusConnected;
475 return XF86OutputStatusDisconnected;
479 static DisplayModePtr
480 nv50_output_get_modes(xf86OutputPtr output)
482 ScrnInfoPtr pScrn = output->scrn;
483 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_detect is called.\n");
485 NVPtr pNv = NVPTR(pScrn);
486 NV50OutputPrivatePtr nv_output = output->driver_private;
487 nouveauConnectorPtr connector = pNv->connector[nv_output->output->dcb->bus];
489 xf86MonPtr ddc_mon = connector->DDCDetect(connector);
491 xf86OutputSetEDID(output, ddc_mon);
493 DisplayModePtr ddc_modes = connector->GetDDCModes(connector);
495 xf86DeleteMode(&nv_output->output->native_mode, nv_output->output->native_mode);
496 nv_output->output->native_mode = NULL;
497 if (nv_output->output->crtc)
498 nv_output->output->crtc->native_mode = NULL;
500 /* typically only LVDS will hit this code path. */
502 if (pNv->VBIOS.fp.native_mode && nv_output->output->type == OUTPUT_LVDS) {
503 ddc_modes = xf86DuplicateMode(pNv->VBIOS.fp.native_mode);
504 xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "LVDS: Using a bios mode, which should work, if it doesn't please report.\n");
508 if (!ddc_modes && nv_output->output->type == OUTPUT_LVDS) {
509 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "LVDS and no modes found, bailing out.\n");
513 /* NV5x hardware can also do scaling on analog connections. */
515 /* Use the first preferred mode as native mode. */
518 /* Find the preferred mode. */
519 for (mode = ddc_modes; mode != NULL; mode = mode->next) {
520 if (mode->type & M_T_PREFERRED) {
521 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
522 "%s: preferred mode is %s\n",
523 output->name, mode->name);
528 /* TODO: Scaling needs a native mode, maybe fail in a better way. */
531 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
532 "%s: no preferred mode found, using %s\n",
533 output->name, mode->name);
536 nv_output->output->native_mode = xf86DuplicateMode(mode);
539 /* No ddc means no native mode, so make one up to avoid crashes. */
540 if (!nv_output->output->native_mode)
541 nv_output->output->native_mode = xf86CVTMode(1024, 768, 60.0, FALSE, FALSE);
543 xf86SetModeCrtc(nv_output->output->native_mode, 0);
545 if (nv_output->output->crtc)
546 nv_output->output->crtc->native_mode = nv_output->output->native_mode;
552 nv50_output_destroy(xf86OutputPtr output)
554 NV50OutputPrivatePtr nv_output = output->driver_private;
556 xf86DeleteMode(&nv_output->output->native_mode, nv_output->output->native_mode);
558 xfree(output->driver_private);
559 output->driver_private = NULL;
563 nv50_output_prepare(xf86OutputPtr output)
565 ScrnInfoPtr pScrn = output->scrn;
566 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_prepare is called.\n");
568 NV50OutputPrivatePtr nv_output = output->driver_private;
569 NV50CrtcPrivatePtr nv_crtc = output->crtc->driver_private;
571 /* Set the real crtc now. */
572 nv_output->output->crtc = nv_crtc->crtc;
574 /* Transfer some output properties to the crtc for easy access. */
575 nv_output->output->crtc->scale_mode = nv_output->output->scale_mode;
576 nv_output->output->crtc->dithering = nv_output->output->dithering;
577 nv_output->output->crtc->native_mode = nv_output->output->native_mode;
579 if (nv_output->output->scale_mode != SCALE_PANEL)
580 nv_output->output->crtc->use_native_mode = TRUE;
582 nv_output->output->crtc->use_native_mode = FALSE;
586 nv50_output_commit(xf86OutputPtr output)
588 ScrnInfoPtr pScrn = output->scrn;
589 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_commit is called.\n");
597 * Several scaling modes exist, let the user choose.
599 #define SCALING_MODE_NAME "SCALING_MODE"
600 static const struct {
602 enum scaling_modes mode;
604 { "panel", SCALE_PANEL },
605 { "fullscreen", SCALE_FULLSCREEN },
606 { "aspect", SCALE_ASPECT },
607 { "noscale", SCALE_NOSCALE },
608 { NULL, SCALE_INVALID}
610 static Atom scaling_mode_atom;
612 #define DITHERING_MODE_NAME "DITHERING"
613 static Atom dithering_atom;
616 nv_scaling_mode_lookup(char *name, int size)
620 /* for when name is zero terminated */
624 for (i = 0; scaling_mode[i].name; i++)
625 /* We're getting non-terminated strings */
626 if (strlen(scaling_mode[i].name) >= size &&
627 !strncasecmp(name, scaling_mode[i].name, size))
630 return scaling_mode[i].mode;
634 nv50_output_create_resources(xf86OutputPtr output)
636 NV50OutputPrivatePtr nv_output = output->driver_private;
637 ScrnInfoPtr pScrn = output->scrn;
638 INT32 dithering_range[2] = { 0, 1 };
642 * Setup scaling mode property.
644 scaling_mode_atom = MakeAtom(SCALING_MODE_NAME, sizeof(SCALING_MODE_NAME) - 1, TRUE);
646 error = RRConfigureOutputProperty(output->randr_output,
647 scaling_mode_atom, TRUE, FALSE, FALSE,
651 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
652 "RRConfigureOutputProperty error, %d\n", error);
655 char *existing_scale_name = NULL;
656 for (i = 0; scaling_mode[i].name; i++)
657 if (scaling_mode[i].mode == nv_output->output->scale_mode)
658 existing_scale_name = scaling_mode[i].name;
660 error = RRChangeOutputProperty(output->randr_output, scaling_mode_atom,
661 XA_STRING, 8, PropModeReplace,
662 strlen(existing_scale_name),
663 existing_scale_name, FALSE, TRUE);
666 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
667 "Failed to set scaling mode, %d\n", error);
670 if (nv_output->output->type == OUTPUT_TMDS || nv_output->output->type == OUTPUT_LVDS) {
672 * Setup dithering property.
674 dithering_atom = MakeAtom(DITHERING_MODE_NAME, sizeof(DITHERING_MODE_NAME) - 1, TRUE);
676 error = RRConfigureOutputProperty(output->randr_output,
677 dithering_atom, TRUE, TRUE, FALSE,
681 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
682 "RRConfigureOutputProperty error, %d\n", error);
685 error = RRChangeOutputProperty(output->randr_output, dithering_atom,
686 XA_INTEGER, 32, PropModeReplace, 1, &nv_output->output->dithering,
690 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
691 "Failed to set dithering mode, %d\n", error);
697 nv50_output_set_property(xf86OutputPtr output, Atom property,
698 RRPropertyValuePtr value)
700 NV50OutputPrivatePtr nv_output = output->driver_private;
702 if (property == scaling_mode_atom) {
706 if (value->type != XA_STRING || value->format != 8)
709 name = (char *) value->data;
711 /* Match a string to a scaling mode */
712 ret = nv_scaling_mode_lookup(name, value->size);
713 if (ret == SCALE_INVALID)
716 /* LVDS must always use gpu scaling. */
717 if (ret == SCALE_PANEL && nv_output->output->type == OUTPUT_LVDS)
720 nv_output->output->scale_mode = ret;
721 if (nv_output->output->crtc) /* normally prepare sets all these things for the crtc. */
722 nv_output->output->crtc->scale_mode = ret;
724 } else if (property == dithering_atom) {
725 if (value->type != XA_INTEGER || value->format != 32)
728 int32_t val = *(int32_t *) value->data;
730 if (val < 0 || val > 1)
733 nv_output->output->dithering = val;
734 if (nv_output->output->crtc) /* normally prepare sets all these things for the crtc. */
735 nv_output->output->crtc->dithering = val;
742 static const xf86OutputFuncsRec nv50_output_funcs = {
743 .dpms = nv50_output_dpms,
746 .mode_valid = nv50_output_mode_valid,
747 .mode_fixup = nv50_output_mode_fixup,
748 .mode_set = nv50_output_mode_set,
749 .detect = nv50_output_detect,
750 .get_modes = nv50_output_get_modes,
751 .destroy = nv50_output_destroy,
752 .prepare = nv50_output_prepare,
753 .commit = nv50_output_commit,
754 .create_resources = nv50_output_create_resources,
755 .set_property = nv50_output_set_property,
759 nv50_output_create(ScrnInfoPtr pScrn)
761 NVPtr pNv = NVPTR(pScrn);
762 xf86OutputPtr output;
763 NV50OutputPrivatePtr nv_output;
766 /* this is a 1:1 hookup of the connectors. */
767 for (i = 0; i < MAX_NUM_DCB_ENTRIES; i++) {
768 if (!(pNv->connector[i]->outputs[0]))
769 continue; /* An empty connector is not useful. */
771 if (!(output = xf86OutputCreate(pScrn, &nv50_output_funcs, pNv->connector[i]->name)))
774 if (!(nv_output = xnfcalloc(sizeof(NV50OutputPrivateRec), 1)))
777 output->driver_private = nv_output;
779 nv_output->output = pNv->connector[i]->outputs[0]; /* initially just wire up the first output available. */
781 output->possible_crtcs = nv_output->output->allowed_crtc;
782 output->possible_clones = 0;
784 if (nv_output->output->type == OUTPUT_TMDS || nv_output->output->type == OUTPUT_LVDS) {
785 output->doubleScanAllowed = false;
786 output->interlaceAllowed = false;
788 output->doubleScanAllowed = true;
789 output->interlaceAllowed = true;