Remove disable switches, big cleanup, requires xorgs server 1.3 again
[nouveau] / src / nv50_sor.c
1 /*
2  * Copyright (c) 2007 NVIDIA, Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23
24 #define DPMS_SERVER
25 #include <X11/extensions/dpms.h>
26 #include <X11/Xatom.h>
27
28 #include "nv_include.h"
29 #include "nv50_type.h"
30 #include "nv50_display.h"
31 #include "nv50_output.h"
32
33 static void
34 NV50SorSetPClk(xf86OutputPtr output, int pclk)
35 {
36         const int limit = 165000;
37
38         NV50OutputWrite(output, 0x4300, (pclk > limit) ? 0x101 : 0);
39 }
40
41 static void
42 NV50SorDPMSSet(xf86OutputPtr output, int mode)
43 {
44         CARD32 tmp;
45
46         while((NV50OutputRead(output, 0xc004) & 0x80000000));
47
48         tmp = NV50OutputRead(output, 0xc004);
49         tmp |= 0x80000000;
50
51         if(mode == DPMSModeOn)
52                 tmp |= 1;
53         else
54                 tmp &= ~1;
55
56         NV50OutputWrite(output, 0xc004, tmp);
57         while((NV50OutputRead(output, 0xc030) & 0x10000000));
58 }
59
60 static int
61 NV50TMDSModeValid(xf86OutputPtr output, DisplayModePtr mode)
62 {
63         // Disable dual-link modes until I can find a way to make them work
64         // reliably.
65         if (mode->Clock > 165000)
66                 return MODE_CLOCK_HIGH;
67
68         return NV50OutputModeValid(output, mode);
69 }
70
71 static int
72 NV50LVDSModeValid(xf86OutputPtr output, DisplayModePtr mode)
73 {
74         NV50OutputPrivPtr nv_output = output->driver_private;
75         DisplayModePtr native = nv_output->nativeMode;
76
77         // Ignore modes larger than the native res.
78         if (mode->HDisplay > native->HDisplay || mode->VDisplay > native->VDisplay)
79                 return MODE_PANEL;
80
81         return NV50OutputModeValid(output, mode);
82 }
83
84 static void
85 NV50SorModeSet(xf86OutputPtr output, DisplayModePtr mode,
86                 DisplayModePtr adjusted_mode)
87 {
88         ScrnInfoPtr pScrn = output->scrn;
89         NV50OutputPrivPtr nv_output = output->driver_private;
90         const int sorOff = 0x40 * nv_output->or;
91         CARD32 type;
92
93         if(!adjusted_mode) {
94                 /* Disconnect the SOR */
95                 NV50DisplayCommand(pScrn, 0x600 + sorOff, 0);
96                 return;
97         }
98
99         if (nv_output->panelType == LVDS) {
100                 type = 0;
101         } else
102         if (adjusted_mode->Clock > 165000) {
103                 type = 0x500;
104         } else {
105                 type = 0x100;
106         }
107
108         // This wouldn't be necessary, but the server is stupid and calls
109         // NV50SorDPMSSet after the output is disconnected, even though the hardware
110         // turns it off automatically.
111         NV50SorDPMSSet(output, DPMSModeOn);
112
113         NV50DisplayCommand(pScrn, 0x600 + sorOff,
114                 (NV50CrtcGetHead(output->crtc) == HEAD0 ? 1 : 2) | type |
115                 ((adjusted_mode->Flags & V_NHSYNC) ? 0x1000 : 0) |
116                 ((adjusted_mode->Flags & V_NVSYNC) ? 0x2000 : 0));
117
118         NV50CrtcSetScale(output->crtc, adjusted_mode, nv_output->scale);
119 }
120
121 static xf86OutputStatus
122 NV50SorDetect(xf86OutputPtr output)
123 {
124         NV50OutputPrivPtr nv_output = output->driver_private;
125
126         /* Assume physical status isn't going to change before the BlockHandler */
127         if(nv_output->cached_status != XF86OutputStatusUnknown)
128                 return nv_output->cached_status;
129
130         NV50OutputPartnersDetect(nv_output->partner, output, nv_output->i2c);
131         return nv_output->cached_status;
132 }
133
134 static xf86OutputStatus
135 NV50SorLVDSDetect(xf86OutputPtr output)
136 {
137         /* Assume LVDS is always connected */
138         return XF86OutputStatusConnected;
139 }
140
141 static void
142 NV50SorDestroy(xf86OutputPtr output)
143 {
144         NV50OutputPrivPtr nv_output = output->driver_private;
145
146         NV50OutputDestroy(output);
147
148         xf86DeleteMode(&nv_output->nativeMode, nv_output->nativeMode);
149
150         xfree(output->driver_private);
151         output->driver_private = NULL;
152 }
153
154 static void
155 NV50SorSetModeBackend(DisplayModePtr dst, const DisplayModePtr src)
156 {
157         // Stash the backend mode timings from src into dst
158         dst->Clock           = src->Clock;
159         dst->Flags           = src->Flags;
160         dst->CrtcHDisplay    = src->CrtcHDisplay;
161         dst->CrtcHBlankStart = src->CrtcHBlankStart;
162         dst->CrtcHSyncStart  = src->CrtcHSyncStart;
163         dst->CrtcHSyncEnd    = src->CrtcHSyncEnd;
164         dst->CrtcHBlankEnd   = src->CrtcHBlankEnd;
165         dst->CrtcHTotal      = src->CrtcHTotal;
166         dst->CrtcHSkew       = src->CrtcHSkew;
167         dst->CrtcVDisplay    = src->CrtcVDisplay;
168         dst->CrtcVBlankStart = src->CrtcVBlankStart;
169         dst->CrtcVSyncStart  = src->CrtcVSyncStart;
170         dst->CrtcVSyncEnd    = src->CrtcVSyncEnd;
171         dst->CrtcVBlankEnd   = src->CrtcVBlankEnd;
172         dst->CrtcVTotal      = src->CrtcVTotal;
173         dst->CrtcHAdjusted   = src->CrtcHAdjusted;
174         dst->CrtcVAdjusted   = src->CrtcVAdjusted;
175 }
176
177 static Bool
178 NV50SorModeFixup(xf86OutputPtr output, DisplayModePtr mode,
179                  DisplayModePtr adjusted_mode)
180 {
181         NV50OutputPrivPtr nv_output = output->driver_private;
182         DisplayModePtr native = nv_output->nativeMode;
183
184         if(native && nv_output->scale != NV50_SCALE_OFF) {
185                 NV50SorSetModeBackend(adjusted_mode, native);
186                 // This mode is already "fixed"
187                 NV50CrtcSkipModeFixup(output->crtc);
188         }
189
190         return TRUE;
191 }
192
193 static Bool
194 NV50SorTMDSModeFixup(xf86OutputPtr output, DisplayModePtr mode,
195                         DisplayModePtr adjusted_mode)
196 {
197         int scrnIndex = output->scrn->scrnIndex;
198         NV50OutputPrivPtr nv_output = output->driver_private;
199         DisplayModePtr modes = output->probed_modes;
200
201         xf86DeleteMode(&nv_output->nativeMode, nv_output->nativeMode);
202
203         if(modes) {
204                 // Find the preferred mode and use that as the "native" mode.
205                 // If no preferred mode is available, use the first one.
206                 DisplayModePtr mode;
207
208                 // Find the preferred mode.
209                 for(mode = modes; mode; mode = mode->next) {
210                         if(mode->type & M_T_PREFERRED) {
211                                 xf86DrvMsgVerb(scrnIndex, X_INFO, 5,
212                                                 "%s: preferred mode is %s\n",
213                                                 output->name, mode->name);
214                                 break;
215                         }
216                 }
217
218                 // XXX: May not want to allow scaling if no preferred mode is found.
219                 if(!mode) {
220                         mode = modes;
221                         xf86DrvMsgVerb(scrnIndex, X_INFO, 5,
222                                 "%s: no preferred mode found, using %s\n",
223                                 output->name, mode->name);
224                 }
225
226                 nv_output->nativeMode = xf86DuplicateMode(mode);
227                 NV50CrtcDoModeFixup(nv_output->nativeMode, mode);
228         }
229
230         return NV50SorModeFixup(output, mode, adjusted_mode);
231 }
232
233 static DisplayModePtr
234 NV50SorGetLVDSModes(xf86OutputPtr output)
235 {
236         NV50OutputPrivPtr nv_output = output->driver_private;
237         return xf86DuplicateMode(nv_output->nativeMode);
238 }
239
240 #ifdef RANDR_12_INTERFACE
241 #define MAKE_ATOM(a) MakeAtom((a), sizeof(a) - 1, TRUE);
242
243 struct property {
244         Atom atom;
245         INT32 range[2];
246 };
247
248 static struct {
249         struct property dither;
250         struct property scale;
251 } properties;
252
253 static void
254 NV50SorCreateResources(xf86OutputPtr output)
255 {
256         ScrnInfoPtr pScrn = output->scrn;
257         NVPtr pNv = NVPTR(pScrn);
258         int data, err;
259         const char *s;
260
261         /******** dithering ********/
262         properties.dither.atom = MAKE_ATOM("dither");
263         properties.dither.range[0] = 0;
264         properties.dither.range[1] = 1;
265         err = RRConfigureOutputProperty(output->randr_output,
266                                         properties.dither.atom, FALSE, TRUE, FALSE,
267                                         2, properties.dither.range);
268         if(err)
269                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
270                         "Failed to configure dithering property for %s: error %d\n",
271                         output->name, err);
272
273         // Set the default value
274         data = pNv->FPDither;
275         err = RRChangeOutputProperty(output->randr_output, properties.dither.atom,
276                                         XA_INTEGER, 32, PropModeReplace, 1, &data,
277                                         FALSE, FALSE);
278         if(err)
279                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
280                         "Failed to set dithering property for %s: error %d\n",
281                         output->name, err);
282
283         /******** scaling ********/
284         properties.scale.atom = MAKE_ATOM("scale");
285         err = RRConfigureOutputProperty(output->randr_output,
286                                         properties.scale.atom, FALSE, FALSE,
287                                         FALSE, 0, NULL);
288         if(err)
289                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
290                         "Failed to configure scaling property for %s: error %d\n",
291                         output->name, err);
292
293         // Set the default value
294         s = "aspect";
295         err = RRChangeOutputProperty(output->randr_output, properties.scale.atom,
296                                         XA_STRING, 8, PropModeReplace, strlen(s),
297                                         (pointer)s, FALSE, FALSE);
298         if(err)
299                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
300                         "Failed to set scaling property for %s: error %d\n",
301                         output->name, err);
302 }
303
304 static Bool
305 NV50SorSetProperty(xf86OutputPtr output, Atom prop, RRPropertyValuePtr val)
306 {
307         NV50OutputPrivPtr nv_output = output->driver_private;
308
309         if(prop == properties.dither.atom) {
310                 INT32 i;
311
312                 if (val->type != XA_INTEGER || val->format != 32 || val->size != 1)
313                         return FALSE;
314
315                 i = *(INT32*)val->data;
316                 if (i < properties.dither.range[0] || i > properties.dither.range[1])
317                         return FALSE;
318
319                 NV50CrtcSetDither(output->crtc, i, TRUE);
320                 return TRUE;
321         } else if (prop == properties.scale.atom) {
322                 const char *s;
323                 enum NV50ScaleMode oldScale, scale;
324                 int i;
325                 const struct {
326                         const char *name;
327                         enum NV50ScaleMode scale;
328                 } modes[] = {
329                         { "off",    NV50_SCALE_OFF },
330                         { "aspect", NV50_SCALE_ASPECT },
331                         { "fill",   NV50_SCALE_FILL },
332                         { "center", NV50_SCALE_CENTER },
333                         { NULL,     0 },
334                 };
335
336                 if (val->type != XA_STRING || val->format != 8)
337                         return FALSE;
338                 s = (char*)val->data;
339
340                 for (i = 0; modes[i].name; i++) {
341                         const char *name = modes[i].name;
342                         const int len = strlen(name);
343
344                         if(val->size == len && !strncmp(name, s, len)) {
345                                 scale = modes[i].scale;
346                                 break;
347                         }
348                 }
349                 if (!modes[i].name)
350                         return FALSE;
351                 if (scale == NV50_SCALE_OFF && nv_output->panelType == LVDS)
352                         // LVDS requires scaling
353                         return FALSE;
354
355                 oldScale = nv_output->scale;
356                 nv_output->scale = scale;
357                 if (output->crtc) {
358                         xf86CrtcPtr crtc = output->crtc;
359
360                         if (!xf86CrtcSetMode(crtc, &crtc->desiredMode, crtc->desiredRotation,
361                                         crtc->desiredX, crtc->desiredY)) {
362                                 xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
363                                                 "Failed to set scaling to %s for output %s\n",
364                                                 modes[i].name, output->name);
365
366                                 // Restore old scale and try again.
367                                 nv_output->scale = oldScale;
368                                 if (!xf86CrtcSetMode(crtc, &crtc->desiredMode,
369                                                         crtc->desiredRotation, crtc->desiredX,
370                                                         crtc->desiredY)) {
371                                         xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
372                                                 "Failed to restore old scaling for output %s\n",
373                                                 output->name);
374                                 }
375
376                                 return FALSE;
377                         }
378                 }
379                 return TRUE;
380         }
381
382         return FALSE;
383 }
384 #endif // RANDR_12_INTERFACE
385
386 static const xf86OutputFuncsRec NV50SorTMDSOutputFuncs = {
387         .dpms = NV50SorDPMSSet,
388         .save = NULL,
389         .restore = NULL,
390         .mode_valid = NV50TMDSModeValid,
391         .mode_fixup = NV50SorTMDSModeFixup,
392         .prepare = NV50OutputPrepare,
393         .commit = NV50OutputCommit,
394         .mode_set = NV50SorModeSet,
395         .detect = NV50SorDetect,
396         .get_modes = NV50OutputGetDDCModes,
397 #ifdef RANDR_12_INTERFACE
398         .create_resources = NV50SorCreateResources,
399         .set_property = NV50SorSetProperty,
400 #endif
401         .destroy = NV50SorDestroy,
402 };
403
404 static const xf86OutputFuncsRec NV50SorLVDSOutputFuncs = {
405         .dpms = NV50SorDPMSSet,
406         .save = NULL,
407         .restore = NULL,
408         .mode_valid = NV50LVDSModeValid,
409         .mode_fixup = NV50SorModeFixup,
410         .prepare = NV50OutputPrepare,
411         .commit = NV50OutputCommit,
412         .mode_set = NV50SorModeSet,
413         .detect = NV50SorLVDSDetect,
414         .get_modes = NV50SorGetLVDSModes,
415 #ifdef RANDR_12_INTERFACE
416         .create_resources = NV50SorCreateResources,
417         .set_property = NV50SorSetProperty,
418 #endif
419         .destroy = NV50SorDestroy,
420 };
421
422 static DisplayModePtr
423 ReadLVDSNativeMode(ScrnInfoPtr pScrn, const int off)
424 {
425         DisplayModePtr mode = xnfcalloc(1, sizeof(DisplayModeRec));
426         const CARD32 size = NV50DisplayRead(pScrn, 0xb4c + off);
427         const int width = size & 0x3fff;
428         const int height = (size >> 16) & 0x3fff;
429
430         mode->HDisplay = mode->CrtcHDisplay = width;
431         mode->VDisplay = mode->CrtcVDisplay = height;
432         mode->Clock = NV50DisplayRead(pScrn, 0xad4 + off) & 0x3fffff;
433         mode->CrtcHBlankStart = NV50DisplayRead(pScrn, 0xafc + off);
434         mode->CrtcHSyncEnd = NV50DisplayRead(pScrn, 0xb04 + off);
435         mode->CrtcHBlankEnd = NV50DisplayRead(pScrn, 0xae8 + off);
436         mode->CrtcHTotal = NV50DisplayRead(pScrn, 0xaf4 + off);
437
438         mode->next = mode->prev = NULL;
439         mode->status = MODE_OK;
440         mode->type = M_T_DRIVER | M_T_PREFERRED;
441
442         xf86SetModeDefaultName(mode);
443
444         return mode;
445 }
446
447 static DisplayModePtr
448 GetLVDSNativeMode(ScrnInfoPtr pScrn)
449 {
450         CARD32 val = NV50DisplayRead(pScrn, 0x50);
451
452         if ((val & 0x3) == 0x2) {
453                 return ReadLVDSNativeMode(pScrn, 0);
454         } else if ((val & 0x300) == 0x200) {
455                 return ReadLVDSNativeMode(pScrn, 0x540);
456         }
457
458         return NULL;
459 }
460
461 xf86OutputPtr
462 NV50CreateSor(ScrnInfoPtr pScrn, ORNum or, PanelType panelType)
463 {
464         NV50OutputPrivPtr nv_output = xnfcalloc(sizeof(*nv_output), 1);
465         xf86OutputPtr output;
466         char orName[5];
467         const xf86OutputFuncsRec *funcs;
468
469         if(!nv_output)
470                 return FALSE;
471
472         if(panelType == LVDS) {
473                 strcpy(orName, "LVDS");
474                 funcs = &NV50SorLVDSOutputFuncs;
475
476                 nv_output->nativeMode = GetLVDSNativeMode(pScrn);
477
478                 if(!nv_output->nativeMode) {
479                         xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
480                                 "Failed to find LVDS native mode\n");
481                         xfree(nv_output);
482                         return FALSE;
483                 }
484
485                 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "%s native size %dx%d\n",
486                         orName, nv_output->nativeMode->HDisplay,
487                         nv_output->nativeMode->VDisplay);
488         } else {
489                 snprintf(orName, 5, "DVI%d", or);
490                 funcs = &NV50SorTMDSOutputFuncs;
491         }
492
493         output = xf86OutputCreate(pScrn, funcs, orName);
494
495         nv_output->type = SOR;
496         nv_output->or = or;
497         nv_output->panelType = panelType;
498         nv_output->cached_status = XF86OutputStatusUnknown;
499         if (panelType == TMDS)
500                 nv_output->set_pclk = NV50SorSetPClk;
501         output->driver_private = nv_output;
502         output->interlaceAllowed = TRUE;
503         output->doubleScanAllowed = TRUE;
504
505         if (panelType != LVDS) {
506                 NV50OutputWrite(output, 0xc00c, 0x03010700);
507                 NV50OutputWrite(output, 0xc010, 0x0000152f);
508                 NV50OutputWrite(output, 0xc014, 0x00000000);
509                 NV50OutputWrite(output, 0xc018, 0x00245af8);
510         }
511
512         return output;
513 }
514