2 * Copyright (c) 2007 NVIDIA, Corporation
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:
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
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.
25 #include <X11/extensions/dpms.h>
26 #include <X11/Xatom.h>
28 #include "nv_include.h"
29 #include "nv50_type.h"
30 #include "nv50_display.h"
31 #include "nv50_output.h"
34 NV50SorSetPClk(xf86OutputPtr output, int pclk)
36 NVOutputPrivatePtr nv_output = output->driver_private;
37 ScrnInfoPtr pScrn = output->scrn;
38 NVPtr pNv = NVPTR(pScrn);
39 const int limit = 165000;
41 NVWrite(pNv, 0x00614300 + nv_output->output_resource * 0x800, (pclk > limit) ? 0x101 : 0);
45 NV50SorDPMSSet(xf86OutputPtr output, int mode)
47 NVOutputPrivatePtr nv_output = output->driver_private;
48 ScrnInfoPtr pScrn = output->scrn;
49 NVPtr pNv = NVPTR(pScrn);
52 while((NVRead(pNv, 0x0061c004 + nv_output->output_resource * 0x800) & 0x80000000));
54 tmp = NVRead(pNv, 0x0061c004 + nv_output->output_resource * 0x800);
57 if(mode == DPMSModeOn)
62 NVWrite(pNv, 0x0061c004 + nv_output->output_resource * 0x800, tmp);
63 while((NVRead(pNv, 0x0061c030 + nv_output->output_resource * 0x800) & 0x10000000));
67 NV50TMDSModeValid(xf86OutputPtr output, DisplayModePtr mode)
69 // Disable dual-link modes until I can find a way to make them work
71 if (mode->Clock > 165000)
72 return MODE_CLOCK_HIGH;
74 return NV50OutputModeValid(output, mode);
78 NV50LVDSModeValid(xf86OutputPtr output, DisplayModePtr mode)
80 NVOutputPrivatePtr nv_output = output->driver_private;
81 DisplayModePtr native = nv_output->native_mode;
83 // Ignore modes larger than the native res.
84 if (mode->HDisplay > native->HDisplay || mode->VDisplay > native->VDisplay)
87 return NV50OutputModeValid(output, mode);
91 NV50SorModeSet(xf86OutputPtr output, DisplayModePtr mode,
92 DisplayModePtr adjusted_mode)
94 ScrnInfoPtr pScrn = output->scrn;
95 NVOutputPrivatePtr nv_output = output->driver_private;
96 const int sorOff = 0x40 * nv_output->output_resource;
100 /* Disconnect the SOR */
101 NV50DisplayCommand(pScrn, 0x600 + sorOff, 0);
105 if (nv_output->type == OUTPUT_LVDS) {
108 if (adjusted_mode->Clock > 165000) {
114 // This wouldn't be necessary, but the server is stupid and calls
115 // NV50SorDPMSSet after the output is disconnected, even though the hardware
116 // turns it off automatically.
117 NV50SorDPMSSet(output, DPMSModeOn);
119 NV50DisplayCommand(pScrn, 0x600 + sorOff,
120 (NV50CrtcGetHead(output->crtc) == HEAD0 ? 1 : 2) | type |
121 ((adjusted_mode->Flags & V_NHSYNC) ? 0x1000 : 0) |
122 ((adjusted_mode->Flags & V_NVSYNC) ? 0x2000 : 0));
124 NV50CrtcSetScale(output->crtc, adjusted_mode, nv_output->scaling_mode);
127 static xf86OutputStatus
128 NV50SorDetect(xf86OutputPtr output)
130 NVOutputPrivatePtr nv_output = output->driver_private;
133 if (nv_output->pDDCBus == NULL)
134 return XF86OutputStatusDisconnected;
136 ddc_mon = xf86OutputGetEDID(output, nv_output->pDDCBus);
138 return XF86OutputStatusDisconnected;
140 if (!ddc_mon->features.input_type) /* Analog? */
141 return XF86OutputStatusDisconnected;
144 xf86OutputSetEDID(output, ddc_mon);
146 return XF86OutputStatusConnected;
149 static xf86OutputStatus
150 NV50SorLVDSDetect(xf86OutputPtr output)
152 /* Assume LVDS is always connected */
153 return XF86OutputStatusConnected;
157 NV50SorDestroy(xf86OutputPtr output)
159 NVOutputPrivatePtr nv_output = output->driver_private;
161 NV50OutputDestroy(output);
163 xf86DeleteMode(&nv_output->native_mode, nv_output->native_mode);
165 xfree(output->driver_private);
166 output->driver_private = NULL;
170 NV50SorSetModeBackend(DisplayModePtr dst, const DisplayModePtr src)
172 // Stash the backend mode timings from src into dst
173 dst->Clock = src->Clock;
174 dst->Flags = src->Flags;
175 dst->CrtcHDisplay = src->CrtcHDisplay;
176 dst->CrtcHBlankStart = src->CrtcHBlankStart;
177 dst->CrtcHSyncStart = src->CrtcHSyncStart;
178 dst->CrtcHSyncEnd = src->CrtcHSyncEnd;
179 dst->CrtcHBlankEnd = src->CrtcHBlankEnd;
180 dst->CrtcHTotal = src->CrtcHTotal;
181 dst->CrtcHSkew = src->CrtcHSkew;
182 dst->CrtcVDisplay = src->CrtcVDisplay;
183 dst->CrtcVBlankStart = src->CrtcVBlankStart;
184 dst->CrtcVSyncStart = src->CrtcVSyncStart;
185 dst->CrtcVSyncEnd = src->CrtcVSyncEnd;
186 dst->CrtcVBlankEnd = src->CrtcVBlankEnd;
187 dst->CrtcVTotal = src->CrtcVTotal;
188 dst->CrtcHAdjusted = src->CrtcHAdjusted;
189 dst->CrtcVAdjusted = src->CrtcVAdjusted;
193 NV50SorModeFixup(xf86OutputPtr output, DisplayModePtr mode,
194 DisplayModePtr adjusted_mode)
196 NVOutputPrivatePtr nv_output = output->driver_private;
197 DisplayModePtr native = nv_output->native_mode;
199 if(native && nv_output->scaling_mode != SCALE_PANEL) {
200 NV50SorSetModeBackend(adjusted_mode, native);
201 // This mode is already "fixed"
202 NV50CrtcSkipModeFixup(output->crtc);
209 NV50SorTMDSModeFixup(xf86OutputPtr output, DisplayModePtr mode,
210 DisplayModePtr adjusted_mode)
212 int scrnIndex = output->scrn->scrnIndex;
213 NVOutputPrivatePtr nv_output = output->driver_private;
214 DisplayModePtr modes = output->probed_modes;
216 xf86DeleteMode(&nv_output->native_mode, nv_output->native_mode);
219 // Find the preferred mode and use that as the "native" mode.
220 // If no preferred mode is available, use the first one.
223 // Find the preferred mode.
224 for(mode = modes; mode; mode = mode->next) {
225 if(mode->type & M_T_PREFERRED) {
226 xf86DrvMsgVerb(scrnIndex, X_INFO, 5,
227 "%s: preferred mode is %s\n",
228 output->name, mode->name);
233 // XXX: May not want to allow scaling if no preferred mode is found.
236 xf86DrvMsgVerb(scrnIndex, X_INFO, 5,
237 "%s: no preferred mode found, using %s\n",
238 output->name, mode->name);
241 nv_output->native_mode = xf86DuplicateMode(mode);
242 NV50CrtcDoModeFixup(nv_output->native_mode, mode);
245 return NV50SorModeFixup(output, mode, adjusted_mode);
248 static DisplayModePtr
249 NV50SorGetLVDSModes(xf86OutputPtr output)
251 NVOutputPrivatePtr nv_output = output->driver_private;
252 return xf86DuplicateMode(nv_output->native_mode);
255 #define MAKE_ATOM(a) MakeAtom((a), sizeof(a) - 1, TRUE);
263 struct property dither;
264 struct property scale;
268 NV50SorCreateResources(xf86OutputPtr output)
270 ScrnInfoPtr pScrn = output->scrn;
271 NVPtr pNv = NVPTR(pScrn);
275 /******** dithering ********/
276 properties.dither.atom = MAKE_ATOM("dither");
277 properties.dither.range[0] = 0;
278 properties.dither.range[1] = 1;
279 err = RRConfigureOutputProperty(output->randr_output,
280 properties.dither.atom, FALSE, TRUE, FALSE,
281 2, properties.dither.range);
283 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
284 "Failed to configure dithering property for %s: error %d\n",
287 // Set the default value
288 data = pNv->FPDither;
289 err = RRChangeOutputProperty(output->randr_output, properties.dither.atom,
290 XA_INTEGER, 32, PropModeReplace, 1, &data,
293 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
294 "Failed to set dithering property for %s: error %d\n",
297 /******** scaling ********/
298 properties.scale.atom = MAKE_ATOM("scale");
299 err = RRConfigureOutputProperty(output->randr_output,
300 properties.scale.atom, FALSE, FALSE,
303 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
304 "Failed to configure scaling property for %s: error %d\n",
307 // Set the default value
309 err = RRChangeOutputProperty(output->randr_output, properties.scale.atom,
310 XA_STRING, 8, PropModeReplace, strlen(s),
311 (pointer)s, FALSE, FALSE);
313 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
314 "Failed to set scaling property for %s: error %d\n",
319 NV50SorSetProperty(xf86OutputPtr output, Atom prop, RRPropertyValuePtr val)
321 NVOutputPrivatePtr nv_output = output->driver_private;
323 if(prop == properties.dither.atom) {
326 if (val->type != XA_INTEGER || val->format != 32 || val->size != 1)
329 i = *(INT32*)val->data;
330 if (i < properties.dither.range[0] || i > properties.dither.range[1])
333 NV50CrtcSetDither(output->crtc, i, TRUE);
335 } else if (prop == properties.scale.atom) {
337 enum scaling_modes oldScale, scale;
341 enum scaling_modes scale;
343 { "panel", SCALE_PANEL },
344 { "aspect", SCALE_ASPECT },
345 { "fullscreen", SCALE_FULLSCREEN },
346 { "noscale", SCALE_NOSCALE },
350 if (val->type != XA_STRING || val->format != 8)
352 s = (char*)val->data;
354 for (i = 0; modes[i].name; i++) {
355 const char *name = modes[i].name;
356 const int len = strlen(name);
358 if(val->size == len && !strncmp(name, s, len)) {
359 scale = modes[i].scale;
365 if (scale == SCALE_PANEL && nv_output->type == OUTPUT_LVDS)
366 // LVDS requires scaling
369 oldScale = nv_output->scaling_mode;
370 nv_output->scaling_mode = scale;
372 xf86CrtcPtr crtc = output->crtc;
374 if (!xf86CrtcSetMode(crtc, &crtc->desiredMode, crtc->desiredRotation,
375 crtc->desiredX, crtc->desiredY)) {
376 xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
377 "Failed to set scaling to %s for output %s\n",
378 modes[i].name, output->name);
380 // Restore old scale and try again.
381 nv_output->scaling_mode = oldScale;
382 if (!xf86CrtcSetMode(crtc, &crtc->desiredMode,
383 crtc->desiredRotation, crtc->desiredX,
385 xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
386 "Failed to restore old scaling for output %s\n",
399 static const xf86OutputFuncsRec NV50SorTMDSOutputFuncs = {
400 .dpms = NV50SorDPMSSet,
403 .mode_valid = NV50TMDSModeValid,
404 .mode_fixup = NV50SorTMDSModeFixup,
405 .prepare = NV50OutputPrepare,
406 .commit = NV50OutputCommit,
407 .mode_set = NV50SorModeSet,
408 .detect = NV50SorDetect,
409 .get_modes = NV50OutputGetDDCModes,
410 .create_resources = NV50SorCreateResources,
411 .set_property = NV50SorSetProperty,
412 .destroy = NV50SorDestroy,
415 static const xf86OutputFuncsRec NV50SorLVDSOutputFuncs = {
416 .dpms = NV50SorDPMSSet,
419 .mode_valid = NV50LVDSModeValid,
420 .mode_fixup = NV50SorModeFixup,
421 .prepare = NV50OutputPrepare,
422 .commit = NV50OutputCommit,
423 .mode_set = NV50SorModeSet,
424 .detect = NV50SorLVDSDetect,
425 .get_modes = NV50SorGetLVDSModes,
426 .create_resources = NV50SorCreateResources,
427 .set_property = NV50SorSetProperty,
428 .destroy = NV50SorDestroy,
431 static DisplayModePtr
432 ReadLVDSNativeMode(ScrnInfoPtr pScrn, const int off)
434 NVPtr pNv = NVPTR(pScrn);
435 DisplayModePtr mode = xnfcalloc(1, sizeof(DisplayModeRec));
436 const CARD32 size = NVRead(pNv, 0x00610b4c + off);
437 const int width = size & 0x3fff;
438 const int height = (size >> 16) & 0x3fff;
440 mode->HDisplay = mode->CrtcHDisplay = width;
441 mode->VDisplay = mode->CrtcVDisplay = height;
442 mode->Clock = NVRead(pNv, 0x00610ad4 + off) & 0x3fffff;
443 mode->CrtcHBlankStart = NVRead(pNv, 0x00610afc + off);
444 mode->CrtcHSyncEnd = NVRead(pNv, 0x00610b04 + off);
445 mode->CrtcHBlankEnd = NVRead(pNv, 0x00610ae8 + off);
446 mode->CrtcHTotal = NVRead(pNv, 0x00610af4 + off);
448 mode->next = mode->prev = NULL;
449 mode->status = MODE_OK;
450 mode->type = M_T_DRIVER | M_T_PREFERRED;
452 xf86SetModeDefaultName(mode);
457 static DisplayModePtr
458 GetLVDSNativeMode(ScrnInfoPtr pScrn)
460 NVPtr pNv = NVPTR(pScrn);
461 CARD32 val = NVRead(pNv, 0x00610050);
463 if ((val & 0x3) == 0x2) {
464 return ReadLVDSNativeMode(pScrn, 0);
465 } else if ((val & 0x300) == 0x200) {
466 return ReadLVDSNativeMode(pScrn, 0x540);
473 NV50CreateSor(ScrnInfoPtr pScrn, ORNum or, NVOutputType type)
475 NVOutputPrivatePtr nv_output = xnfcalloc(sizeof(*nv_output), 1);
476 NVPtr pNv = NVPTR(pScrn);
477 xf86OutputPtr output;
479 const xf86OutputFuncsRec *funcs;
484 if(type == OUTPUT_LVDS) {
485 strcpy(orName, "LVDS");
486 funcs = &NV50SorLVDSOutputFuncs;
488 nv_output->native_mode = GetLVDSNativeMode(pScrn);
490 if(!nv_output->native_mode) {
491 xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
492 "Failed to find LVDS native mode\n");
497 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "%s native size %dx%d\n",
498 orName, nv_output->native_mode->HDisplay,
499 nv_output->native_mode->VDisplay);
501 snprintf(orName, 5, "DVI%d", or);
502 funcs = &NV50SorTMDSOutputFuncs;
505 output = xf86OutputCreate(pScrn, funcs, orName);
507 nv_output->output_resource = or;
508 nv_output->type = type;
509 output->driver_private = nv_output;
510 output->interlaceAllowed = TRUE;
511 output->doubleScanAllowed = TRUE;
513 if (type != OUTPUT_LVDS) {
514 NVWrite(pNv, 0x0061c00c + nv_output->output_resource * 0x800, 0x03010700);
515 NVWrite(pNv, 0x0061c010 + nv_output->output_resource * 0x800, 0x0000152f);
516 NVWrite(pNv, 0x0061c014 + nv_output->output_resource * 0x800, 0x00000000);
517 NVWrite(pNv, 0x0061c018 + nv_output->output_resource * 0x800, 0x00245af8);