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