nv50_randr: plug a small memory leak
[nouveau] / src / nv50_randr.c
1 /*
2  * Copyright 2008 Maarten Maathuis
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE 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
20  * SOFTWARE.
21  */
22
23 #include "nv50_randr.h"
24 #include "X11/Xatom.h"
25
26 /*
27  * A randr-1.2 wrapper around the NV50 driver.
28  */
29
30 /*
31  * CRTC stuff.
32  */
33
34 static void
35 nv50_crtc_dpms(xf86CrtcPtr crtc, int mode)
36 {
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");
40
41         switch (mode) {
42                 case DPMSModeOn:
43                         nv_crtc->crtc->active = TRUE;
44                         break;
45                 case DPMSModeSuspend:
46                 case DPMSModeStandby:
47                 case DPMSModeOff:
48                 default:
49                         nv_crtc->crtc->active = FALSE;
50                         break;
51         }
52 }
53
54 static Bool
55 nv50_crtc_lock(xf86CrtcPtr crtc)
56 {
57         return FALSE;
58 }
59
60 static Bool
61 nv50_crtc_mode_fixup(xf86CrtcPtr crtc, DisplayModePtr mode,
62                 DisplayModePtr adjusted_mode)
63 {
64         return TRUE;
65 }
66
67 static void
68 nv50_crtc_prepare(xf86CrtcPtr crtc)
69 {
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");
73
74         NVPtr pNv = NVPTR(pScrn);
75
76         nv_crtc->crtc->active = TRUE;
77         nv_crtc->crtc->modeset_lock = TRUE;
78
79         nouveauOutputPtr output;
80
81         /* Detach any unused outputs. */
82         for (output = pNv->output; output != NULL; output = output->next) {
83                 if (!output->crtc)
84                         output->ModeSet(output, NULL);
85         }
86 }
87
88 static void
89 nv50_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, DisplayModePtr adjusted_mode, int x, int y)
90 {
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");
94
95         NVPtr pNv = NVPTR(pScrn);
96
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);
101         } else {
102                 nv_crtc->crtc->SetFB(nv_crtc->crtc, pNv->FB);
103                 nv_crtc->crtc->SetFBOffset(nv_crtc->crtc, x, y);
104         }
105         nv_crtc->crtc->ModeSet(nv_crtc->crtc, mode);
106 }
107
108 static void
109 nv50_crtc_gamma_set(xf86CrtcPtr crtc, CARD16 *red, CARD16 *green, CARD16 *blue, int size)
110 {
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");
114
115         nv_crtc->crtc->GammaSet(nv_crtc->crtc, (uint16_t *) red, (uint16_t *) green, (uint16_t *) blue, size);
116 }
117
118 static void
119 nv50_crtc_commit(xf86CrtcPtr crtc)
120 {
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");
124
125         NVPtr pNv = NVPTR(pScrn);
126
127         /* Let's detect any outputs and connectors that have gone inactive. */
128         uint8_t crtc_active_mask = 0;
129         int i, j;
130         nouveauOutputPtr output;
131
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];
136                         if (output) {
137                                 if (output->crtc) {
138                                         crtc_active_mask |= 1 << output->crtc->index;
139                                         connector_active = TRUE;
140                                 } else {
141                                         output->active = FALSE;
142                                 }
143                         }
144                 }
145
146                 pNv->connector[i]->active = connector_active;
147         }
148
149         /* Blank any crtc's that are inactive. */
150         if (!(crtc_active_mask & (1 << 0)))
151                 pNv->crtc[0]->Blank(pNv->crtc[0], TRUE);
152
153         if (!(crtc_active_mask & (1 << 1)))
154                 pNv->crtc[1]->Blank(pNv->crtc[1], TRUE);
155
156         xf86_reload_cursors(pScrn->pScreen);
157
158         NV50DisplayCommand(pScrn, NV50_UPDATE_DISPLAY, 0);
159
160         nv_crtc->crtc->modeset_lock = FALSE;
161 }
162
163 /*
164  * Cursor CRTC stuff.
165  */
166
167 static void 
168 nv50_crtc_show_cursor(xf86CrtcPtr crtc)
169 {
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");
173
174         nv_crtc->crtc->ShowCursor(nv_crtc->crtc, FALSE);
175 }
176
177 static void
178 nv50_crtc_hide_cursor(xf86CrtcPtr crtc)
179 {
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");
183
184         nv_crtc->crtc->HideCursor(nv_crtc->crtc, FALSE);
185 }
186
187 static void
188 nv50_crtc_set_cursor_position(xf86CrtcPtr crtc, int x, int y)
189 {
190         NV50CrtcPrivatePtr nv_crtc = crtc->driver_private;
191
192         nv_crtc->crtc->SetCursorPosition(nv_crtc->crtc, x, y);
193 }
194
195 static void
196 nv50_crtc_load_cursor_argb(xf86CrtcPtr crtc, CARD32 *src)
197 {
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");
201
202         nv_crtc->crtc->LoadCursor(nv_crtc->crtc, TRUE, (uint32_t *) src);
203 }
204
205 /* This stuff isn't ready for NOUVEAU_EXA_PIXMAPS, but can be easily ported. */
206 static void *
207 nv50_crtc_shadow_allocate (xf86CrtcPtr crtc, int width, int height)
208 {
209         ScrnInfoPtr pScrn = crtc->scrn;
210         NV50CrtcPrivatePtr nv_crtc = crtc->driver_private;
211         NVPtr pNv = NVPTR(pScrn);
212         int size, pitch;
213
214         ErrorF("nv50_crtc_shadow_allocate\n");
215
216         pitch = pScrn->displayWidth * (pScrn->bitsPerPixel/8);
217         size = pitch * height;
218
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");
222                 return NULL;
223         }
224
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");
228                 return NULL;
229         }
230
231         pNv->shadow[nv_crtc->crtc->index] = nv_crtc->shadow;
232
233         return nv_crtc->shadow->map;
234 }
235
236 static PixmapPtr
237 nv50_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
238 {
239         ScrnInfoPtr pScrn = crtc->scrn;
240         uint32_t pitch;
241         PixmapPtr rotate_pixmap;
242
243         ErrorF("nv50_crtc_shadow_create\n");
244
245         if (!data)
246                 data = crtc->funcs->shadow_allocate (crtc, width, height);
247
248         pitch = pScrn->displayWidth * (pScrn->bitsPerPixel/8);
249
250         rotate_pixmap = GetScratchPixmapHeader(pScrn->pScreen,
251                                                 width, height,
252                                                 pScrn->depth,
253                                                 pScrn->bitsPerPixel,
254                                                 pitch,
255                                                 data);
256
257         if (rotate_pixmap == NULL) {
258                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
259                         "Couldn't allocate shadow pixmap for rotated CRTC\n");
260         }
261
262         return rotate_pixmap;
263 }
264
265 static void
266 nv50_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data)
267 {
268         ScrnInfoPtr pScrn = crtc->scrn;
269         NV50CrtcPrivatePtr nv_crtc = crtc->driver_private;
270         NVPtr pNv = NVPTR(pScrn);
271         ScreenPtr pScreen = pScrn->pScreen;
272
273         ErrorF("nv50_crtc_shadow_destroy\n");
274
275         if (rotate_pixmap)
276                 pScreen->DestroyPixmap(rotate_pixmap);
277
278         if (nv_crtc->shadow)
279                 nouveau_bo_del(&nv_crtc->shadow);
280
281         nv_crtc->shadow = NULL;
282         /* for easy acces by exa */
283         pNv->shadow[nv_crtc->crtc->index] = NULL;
284 }
285
286 static void
287 nv50_crtc_destroy(xf86CrtcPtr crtc)
288 {
289         xfree(crtc->driver_private);
290 }
291
292 static const xf86CrtcFuncsRec nv50_crtc_funcs = {
293         .dpms = nv50_crtc_dpms,
294         .save = NULL,
295         .restore = NULL,
296         .lock = nv50_crtc_lock,
297         .unlock = NULL,
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,
311 };
312
313 void
314 nv50_crtc_init(ScrnInfoPtr pScrn, int crtc_num)
315 {
316         NVPtr pNv = NVPTR(pScrn);
317         xf86CrtcPtr crtc;
318         NV50CrtcPrivatePtr nv_crtc;
319
320         crtc = xf86CrtcCreate(pScrn, &nv50_crtc_funcs);
321         if (crtc == NULL)
322                 return;
323
324         nv_crtc = xnfcalloc (sizeof (NV50CrtcPrivateRec), 1);
325         nv_crtc->crtc = pNv->crtc[crtc_num];
326
327         crtc->driver_private = nv_crtc;
328 }
329
330
331 /*
332  * "Output" stuff.
333  */
334
335 static void
336 nv50_output_dpms(xf86OutputPtr output, int mode)
337 {
338         ScrnInfoPtr pScrn = output->scrn;
339         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_dpms is called with mode %d.\n", mode);
340
341         NVPtr pNv = NVPTR(pScrn);
342         NV50OutputPrivatePtr nv_output = output->driver_private;
343
344         /* Keep the crtc wiring consistent with randr-1.2 */
345         if (output->crtc) {
346                 NV50CrtcPrivatePtr nv_crtc = output->crtc->driver_private;
347                 nv_output->output->crtc = nv_crtc->crtc;
348         } else {
349                 nv_output->output->crtc = NULL;
350         }
351
352         /* Our crtc's map 1:1 onto randr-1.2 crtc's. */
353         switch (mode) {
354                 case DPMSModeOn:
355                         nv_output->output->active = TRUE;
356                         break;
357                 case DPMSModeSuspend:
358                 case DPMSModeStandby:
359                 case DPMSModeOff:
360                 default:
361                         nv_output->output->active = FALSE;
362                         break;
363         }
364
365         /* Set dpms on all outputs for ths connector, just to be safe. */
366         nouveauConnectorPtr connector = pNv->connector[nv_output->output->dcb->bus];
367         int i;
368         for (i = 0; i < MAX_OUTPUTS_PER_CONNECTOR; i++) {
369                 if (connector->outputs[i])
370                         connector->outputs[i]->SetPowerMode(connector->outputs[i], mode);
371         }
372 }
373
374 static int
375 nv50_output_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
376 {
377         ScrnInfoPtr pScrn = output->scrn;
378         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_mode_valid is called.\n");
379
380         NV50OutputPrivatePtr nv_output = output->driver_private;
381
382         return nv_output->output->ModeValid(nv_output->output, mode);
383 }
384
385 static Bool
386 nv50_output_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode)
387 {
388         return TRUE;
389 }
390
391 static void
392 nv50_output_mode_set(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode)
393 {
394         ScrnInfoPtr pScrn = output->scrn;
395         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_mode_set is called.\n");
396
397         NV50OutputPrivatePtr nv_output = output->driver_private;
398
399         nv_output->output->ModeSet(nv_output->output, mode);
400 }
401
402 static xf86OutputStatus
403 nv50_output_detect(xf86OutputPtr output)
404 {
405         ScrnInfoPtr pScrn = output->scrn;
406         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_detect is called.\n");
407
408         NVPtr pNv = NVPTR(pScrn);
409         NV50OutputPrivatePtr nv_output = output->driver_private;
410         nouveauConnectorPtr connector = pNv->connector[nv_output->output->dcb->bus];
411
412         if (!connector)
413                 return XF86OutputStatusDisconnected;
414
415         Bool detect_present = FALSE;
416         Bool detect_digital = FALSE;
417         xf86MonPtr ddc_mon = connector->DDCDetect(connector);
418         int i;
419
420         if (!ddc_mon) {
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;
427                                         break;
428                                 }
429                         }
430                 }
431         }
432
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;
436
437         /*
438          * We abuse randr-1.2 outputs as connector, so here we have to determine what actual output is connected to the connector.
439          */
440         if (ddc_mon || detect_present) {
441                 Bool is_digital = FALSE;
442                 Bool found = FALSE;
443                 nouveauCrtcPtr crtc_backup = nv_output->output->crtc;
444                 nv_output->output->crtc = NULL;
445                 nv_output->output->connector = NULL;
446
447                 if (ddc_mon)
448                         is_digital = ddc_mon->features.input_type;
449                 else
450                         is_digital = detect_digital;
451
452                 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Detected a %s output on %s\n", is_digital ? "Digital" : "Analog", connector->name);
453
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)) {
456                                 found = TRUE;
457                         } else if (is_digital && (connector->outputs[i]->type == OUTPUT_TMDS || connector->outputs[i]->type == OUTPUT_LVDS)) {
458                                 found = TRUE;
459                         }
460                         if (found) {
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];
466                                 break;
467                         }
468                 }
469         }
470
471         if (ddc_mon || detect_present) {
472                 free(ddc_mon);
473                 return XF86OutputStatusConnected;
474         } else {
475                 return XF86OutputStatusDisconnected;
476         }
477 }
478
479 static DisplayModePtr
480 nv50_output_get_modes(xf86OutputPtr output)
481 {
482         ScrnInfoPtr pScrn = output->scrn;
483         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_detect is called.\n");
484
485         NVPtr pNv = NVPTR(pScrn);
486         NV50OutputPrivatePtr nv_output = output->driver_private;
487         nouveauConnectorPtr connector = pNv->connector[nv_output->output->dcb->bus];
488
489         xf86MonPtr ddc_mon = connector->DDCDetect(connector);
490
491         xf86OutputSetEDID(output, ddc_mon);
492
493         DisplayModePtr ddc_modes = connector->GetDDCModes(connector);
494
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;
499
500         /* typically only LVDS will hit this code path. */
501         if (!ddc_modes) {
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");
505                 }
506         }
507
508         if (!ddc_modes && nv_output->output->type == OUTPUT_LVDS) {
509                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "LVDS and no modes found, bailing out.\n");
510                 return NULL;
511         }
512
513         /* NV5x hardware can also do scaling on analog connections. */
514         if (ddc_modes) {
515                 /* Use the first preferred mode as native mode. */
516                 DisplayModePtr mode;
517
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);
524                                 break;
525                         }
526                 }
527
528                 /* TODO: Scaling needs a native mode, maybe fail in a better way. */
529                 if (!mode) {
530                         mode = ddc_modes;
531                         xf86DrvMsg(pScrn->scrnIndex, X_INFO,
532                                 "%s: no preferred mode found, using %s\n",
533                                 output->name, mode->name);
534                 }
535
536                 nv_output->output->native_mode = xf86DuplicateMode(mode);
537         }
538
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);
542
543         xf86SetModeCrtc(nv_output->output->native_mode, 0);
544
545         if (nv_output->output->crtc)
546                 nv_output->output->crtc->native_mode = nv_output->output->native_mode;
547
548         return ddc_modes;
549 }
550
551 static void
552 nv50_output_destroy(xf86OutputPtr output)
553 {
554         NV50OutputPrivatePtr nv_output = output->driver_private;
555
556         xf86DeleteMode(&nv_output->output->native_mode, nv_output->output->native_mode);
557
558         xfree(output->driver_private);
559         output->driver_private = NULL;
560 }
561
562 void
563 nv50_output_prepare(xf86OutputPtr output)
564 {
565         ScrnInfoPtr pScrn = output->scrn;
566         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_prepare is called.\n");
567
568         NV50OutputPrivatePtr nv_output = output->driver_private;
569         NV50CrtcPrivatePtr nv_crtc = output->crtc->driver_private;
570
571         /* Set the real crtc now. */
572         nv_output->output->crtc = nv_crtc->crtc;
573
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;
578
579         if (nv_output->output->scale_mode != SCALE_PANEL)
580                 nv_output->output->crtc->use_native_mode = TRUE;
581         else
582                 nv_output->output->crtc->use_native_mode = FALSE;
583 }
584
585 static void
586 nv50_output_commit(xf86OutputPtr output)
587 {
588         ScrnInfoPtr pScrn = output->scrn;
589         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_commit is called.\n");
590 }
591
592 /*
593  * Output properties.
594  */
595
596 /*
597  * Several scaling modes exist, let the user choose.
598  */
599 #define SCALING_MODE_NAME "SCALING_MODE"
600 static const struct {
601         char *name;
602         enum scaling_modes mode;
603 } scaling_mode[] = {
604         { "panel", SCALE_PANEL },
605         { "fullscreen", SCALE_FULLSCREEN },
606         { "aspect", SCALE_ASPECT },
607         { "noscale", SCALE_NOSCALE },
608         { NULL, SCALE_INVALID}
609 };
610 static Atom scaling_mode_atom;
611
612 #define DITHERING_MODE_NAME "DITHERING"
613 static Atom dithering_atom;
614
615 int
616 nv_scaling_mode_lookup(char *name, int size)
617 {
618         int i;
619
620         /* for when name is zero terminated */
621         if (size < 0)
622                 size = strlen(name);
623
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))
628                         break;
629
630         return scaling_mode[i].mode;
631 }
632
633 void
634 nv50_output_create_resources(xf86OutputPtr output)
635 {
636         NV50OutputPrivatePtr nv_output = output->driver_private;
637         ScrnInfoPtr pScrn = output->scrn;
638         INT32 dithering_range[2] = { 0, 1 };
639         int error, i;
640
641         /*
642          * Setup scaling mode property.
643          */
644         scaling_mode_atom = MakeAtom(SCALING_MODE_NAME, sizeof(SCALING_MODE_NAME) - 1, TRUE);
645
646         error = RRConfigureOutputProperty(output->randr_output,
647                                         scaling_mode_atom, TRUE, FALSE, FALSE,
648                                         0, NULL);
649
650         if (error != 0) {
651                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
652                         "RRConfigureOutputProperty error, %d\n", error);
653         }
654
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;
659
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);
664
665         if (error != 0) {
666                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
667                         "Failed to set scaling mode, %d\n", error);
668         }
669
670         if (nv_output->output->type == OUTPUT_TMDS || nv_output->output->type == OUTPUT_LVDS) {
671                 /*
672                  * Setup dithering property.
673                  */
674                 dithering_atom = MakeAtom(DITHERING_MODE_NAME, sizeof(DITHERING_MODE_NAME) - 1, TRUE);
675
676                 error = RRConfigureOutputProperty(output->randr_output,
677                                                 dithering_atom, TRUE, TRUE, FALSE,
678                                                 2, dithering_range);
679
680                 if (error != 0) {
681                         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
682                                 "RRConfigureOutputProperty error, %d\n", error);
683                 }
684
685                 error = RRChangeOutputProperty(output->randr_output, dithering_atom,
686                                                 XA_INTEGER, 32, PropModeReplace, 1, &nv_output->output->dithering,
687                                                 FALSE, TRUE);
688
689                 if (error != 0) {
690                         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
691                                 "Failed to set dithering mode, %d\n", error);
692                 }
693         }
694 }
695
696 Bool
697 nv50_output_set_property(xf86OutputPtr output, Atom property,
698                                 RRPropertyValuePtr value)
699 {
700         NV50OutputPrivatePtr nv_output = output->driver_private;
701
702         if (property == scaling_mode_atom) {
703                 int32_t ret;
704                 char *name = NULL;
705
706                 if (value->type != XA_STRING || value->format != 8)
707                         return FALSE;
708
709                 name = (char *) value->data;
710
711                 /* Match a string to a scaling mode */
712                 ret = nv_scaling_mode_lookup(name, value->size);
713                 if (ret == SCALE_INVALID)
714                         return FALSE;
715
716                 /* LVDS must always use gpu scaling. */
717                 if (ret == SCALE_PANEL && nv_output->output->type == OUTPUT_LVDS)
718                         return FALSE;
719
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;
723                 return TRUE;
724         } else if (property == dithering_atom) {
725                 if (value->type != XA_INTEGER || value->format != 32)
726                         return FALSE;
727
728                 int32_t val = *(int32_t *) value->data;
729
730                 if (val < 0 || val > 1)
731                         return FALSE;
732
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;
736                 return TRUE;
737         }
738
739         return TRUE;
740 }
741
742 static const xf86OutputFuncsRec nv50_output_funcs = {
743         .dpms = nv50_output_dpms,
744         .save = NULL,
745         .restore = NULL,
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,
756 };
757
758 void
759 nv50_output_create(ScrnInfoPtr pScrn)
760 {
761         NVPtr pNv = NVPTR(pScrn);
762         xf86OutputPtr output;
763         NV50OutputPrivatePtr nv_output;
764         int i;
765
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. */
770
771                 if (!(output = xf86OutputCreate(pScrn, &nv50_output_funcs, pNv->connector[i]->name)))
772                         return;
773
774                 if (!(nv_output = xnfcalloc(sizeof(NV50OutputPrivateRec), 1)))
775                         return;
776
777                 output->driver_private = nv_output;
778
779                 nv_output->output = pNv->connector[i]->outputs[0]; /* initially just wire up the first output available. */
780
781                 output->possible_crtcs = nv_output->output->allowed_crtc;
782                 output->possible_clones = 0;
783
784                 if (nv_output->output->type == OUTPUT_TMDS || nv_output->output->type == OUTPUT_LVDS) {
785                         output->doubleScanAllowed = false;
786                         output->interlaceAllowed = false;
787                 } else {
788                         output->doubleScanAllowed = true;
789                         output->interlaceAllowed = true;
790                 }
791         }
792 }