git-log is no longer valid for git 1.6, switch to git log.
[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                 return XF86OutputStatusConnected;
473         else
474                 return XF86OutputStatusDisconnected;
475 }
476
477 static DisplayModePtr
478 nv50_output_get_modes(xf86OutputPtr output)
479 {
480         ScrnInfoPtr pScrn = output->scrn;
481         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_detect is called.\n");
482
483         NVPtr pNv = NVPTR(pScrn);
484         NV50OutputPrivatePtr nv_output = output->driver_private;
485         nouveauConnectorPtr connector = pNv->connector[nv_output->output->dcb->bus];
486
487         xf86MonPtr ddc_mon = connector->DDCDetect(connector);
488
489         xf86OutputSetEDID(output, ddc_mon);
490
491         DisplayModePtr ddc_modes = connector->GetDDCModes(connector);
492
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;
497
498         /* typically only LVDS will hit this code path. */
499         if (!ddc_modes) {
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");
503                 }
504         }
505
506         if (!ddc_modes && nv_output->output->type == OUTPUT_LVDS) {
507                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "LVDS and no modes found, bailing out.\n");
508                 return NULL;
509         }
510
511         /* NV5x hardware can also do scaling on analog connections. */
512         if (ddc_modes) {
513                 /* Use the first preferred mode as native mode. */
514                 DisplayModePtr mode;
515
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);
522                                 break;
523                         }
524                 }
525
526                 /* TODO: Scaling needs a native mode, maybe fail in a better way. */
527                 if (!mode) {
528                         mode = ddc_modes;
529                         xf86DrvMsg(pScrn->scrnIndex, X_INFO,
530                                 "%s: no preferred mode found, using %s\n",
531                                 output->name, mode->name);
532                 }
533
534                 nv_output->output->native_mode = xf86DuplicateMode(mode);
535         }
536
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);
540
541         xf86SetModeCrtc(nv_output->output->native_mode, 0);
542
543         if (nv_output->output->crtc)
544                 nv_output->output->crtc->native_mode = nv_output->output->native_mode;
545
546         return ddc_modes;
547 }
548
549 static void
550 nv50_output_destroy(xf86OutputPtr output)
551 {
552         NV50OutputPrivatePtr nv_output = output->driver_private;
553
554         xf86DeleteMode(&nv_output->output->native_mode, nv_output->output->native_mode);
555
556         xfree(output->driver_private);
557         output->driver_private = NULL;
558 }
559
560 void
561 nv50_output_prepare(xf86OutputPtr output)
562 {
563         ScrnInfoPtr pScrn = output->scrn;
564         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_prepare is called.\n");
565
566         NV50OutputPrivatePtr nv_output = output->driver_private;
567         NV50CrtcPrivatePtr nv_crtc = output->crtc->driver_private;
568
569         /* Set the real crtc now. */
570         nv_output->output->crtc = nv_crtc->crtc;
571
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;
576
577         if (nv_output->output->scale_mode != SCALE_PANEL)
578                 nv_output->output->crtc->use_native_mode = TRUE;
579         else
580                 nv_output->output->crtc->use_native_mode = FALSE;
581 }
582
583 static void
584 nv50_output_commit(xf86OutputPtr output)
585 {
586         ScrnInfoPtr pScrn = output->scrn;
587         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv50_output_commit is called.\n");
588 }
589
590 /*
591  * Output properties.
592  */
593
594 /*
595  * Several scaling modes exist, let the user choose.
596  */
597 #define SCALING_MODE_NAME "SCALING_MODE"
598 static const struct {
599         char *name;
600         enum scaling_modes mode;
601 } scaling_mode[] = {
602         { "panel", SCALE_PANEL },
603         { "fullscreen", SCALE_FULLSCREEN },
604         { "aspect", SCALE_ASPECT },
605         { "noscale", SCALE_NOSCALE },
606         { NULL, SCALE_INVALID}
607 };
608 static Atom scaling_mode_atom;
609
610 #define DITHERING_MODE_NAME "DITHERING"
611 static Atom dithering_atom;
612
613 int
614 nv_scaling_mode_lookup(char *name, int size)
615 {
616         int i;
617
618         /* for when name is zero terminated */
619         if (size < 0)
620                 size = strlen(name);
621
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))
626                         break;
627
628         return scaling_mode[i].mode;
629 }
630
631 void
632 nv50_output_create_resources(xf86OutputPtr output)
633 {
634         NV50OutputPrivatePtr nv_output = output->driver_private;
635         ScrnInfoPtr pScrn = output->scrn;
636         INT32 dithering_range[2] = { 0, 1 };
637         int error, i;
638
639         /*
640          * Setup scaling mode property.
641          */
642         scaling_mode_atom = MakeAtom(SCALING_MODE_NAME, sizeof(SCALING_MODE_NAME) - 1, TRUE);
643
644         error = RRConfigureOutputProperty(output->randr_output,
645                                         scaling_mode_atom, TRUE, FALSE, FALSE,
646                                         0, NULL);
647
648         if (error != 0) {
649                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
650                         "RRConfigureOutputProperty error, %d\n", error);
651         }
652
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;
657
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);
662
663         if (error != 0) {
664                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
665                         "Failed to set scaling mode, %d\n", error);
666         }
667
668         if (nv_output->output->type == OUTPUT_TMDS || nv_output->output->type == OUTPUT_LVDS) {
669                 /*
670                  * Setup dithering property.
671                  */
672                 dithering_atom = MakeAtom(DITHERING_MODE_NAME, sizeof(DITHERING_MODE_NAME) - 1, TRUE);
673
674                 error = RRConfigureOutputProperty(output->randr_output,
675                                                 dithering_atom, TRUE, TRUE, FALSE,
676                                                 2, dithering_range);
677
678                 if (error != 0) {
679                         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
680                                 "RRConfigureOutputProperty error, %d\n", error);
681                 }
682
683                 error = RRChangeOutputProperty(output->randr_output, dithering_atom,
684                                                 XA_INTEGER, 32, PropModeReplace, 1, &nv_output->output->dithering,
685                                                 FALSE, TRUE);
686
687                 if (error != 0) {
688                         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
689                                 "Failed to set dithering mode, %d\n", error);
690                 }
691         }
692 }
693
694 Bool
695 nv50_output_set_property(xf86OutputPtr output, Atom property,
696                                 RRPropertyValuePtr value)
697 {
698         NV50OutputPrivatePtr nv_output = output->driver_private;
699
700         if (property == scaling_mode_atom) {
701                 int32_t ret;
702                 char *name = NULL;
703
704                 if (value->type != XA_STRING || value->format != 8)
705                         return FALSE;
706
707                 name = (char *) value->data;
708
709                 /* Match a string to a scaling mode */
710                 ret = nv_scaling_mode_lookup(name, value->size);
711                 if (ret == SCALE_INVALID)
712                         return FALSE;
713
714                 /* LVDS must always use gpu scaling. */
715                 if (ret == SCALE_PANEL && nv_output->output->type == OUTPUT_LVDS)
716                         return FALSE;
717
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;
721                 return TRUE;
722         } else if (property == dithering_atom) {
723                 if (value->type != XA_INTEGER || value->format != 32)
724                         return FALSE;
725
726                 int32_t val = *(int32_t *) value->data;
727
728                 if (val < 0 || val > 1)
729                         return FALSE;
730
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;
734                 return TRUE;
735         }
736
737         return TRUE;
738 }
739
740 static const xf86OutputFuncsRec nv50_output_funcs = {
741         .dpms = nv50_output_dpms,
742         .save = NULL,
743         .restore = NULL,
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,
754 };
755
756 void
757 nv50_output_create(ScrnInfoPtr pScrn)
758 {
759         NVPtr pNv = NVPTR(pScrn);
760         xf86OutputPtr output;
761         NV50OutputPrivatePtr nv_output;
762         int i;
763
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. */
768
769                 if (!(output = xf86OutputCreate(pScrn, &nv50_output_funcs, pNv->connector[i]->name)))
770                         return;
771
772                 if (!(nv_output = xnfcalloc(sizeof(NV50OutputPrivateRec), 1)))
773                         return;
774
775                 output->driver_private = nv_output;
776
777                 nv_output->output = pNv->connector[i]->outputs[0]; /* initially just wire up the first output available. */
778
779                 output->possible_crtcs = nv_output->output->allowed_crtc;
780                 output->possible_clones = 0;
781
782                 if (nv_output->output->type == OUTPUT_TMDS || nv_output->output->type == OUTPUT_LVDS) {
783                         output->doubleScanAllowed = false;
784                         output->interlaceAllowed = false;
785                 } else {
786                         output->doubleScanAllowed = true;
787                         output->interlaceAllowed = true;
788                 }
789         }
790 }