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 const int limit = 165000;
38 NV50OutputWrite(output, 0x4300, (pclk > limit) ? 0x101 : 0);
42 NV50SorDPMSSet(xf86OutputPtr output, int mode)
46 while((NV50OutputRead(output, 0xc004) & 0x80000000));
48 tmp = NV50OutputRead(output, 0xc004);
51 if(mode == DPMSModeOn)
56 NV50OutputWrite(output, 0xc004, tmp);
57 while((NV50OutputRead(output, 0xc030) & 0x10000000));
61 NV50TMDSModeValid(xf86OutputPtr output, DisplayModePtr mode)
63 // Disable dual-link modes until I can find a way to make them work
65 if (mode->Clock > 165000)
66 return MODE_CLOCK_HIGH;
68 return NV50OutputModeValid(output, mode);
72 NV50LVDSModeValid(xf86OutputPtr output, DisplayModePtr mode)
74 NV50OutputPrivPtr nv_output = output->driver_private;
75 DisplayModePtr native = nv_output->nativeMode;
77 // Ignore modes larger than the native res.
78 if (mode->HDisplay > native->HDisplay || mode->VDisplay > native->VDisplay)
81 return NV50OutputModeValid(output, mode);
85 NV50SorModeSet(xf86OutputPtr output, DisplayModePtr mode,
86 DisplayModePtr adjusted_mode)
88 ScrnInfoPtr pScrn = output->scrn;
89 NV50OutputPrivPtr nv_output = output->driver_private;
90 const int sorOff = 0x40 * nv_output->or;
94 /* Disconnect the SOR */
95 NV50DisplayCommand(pScrn, 0x600 + sorOff, 0);
99 if (nv_output->panelType == LVDS) {
102 if (adjusted_mode->Clock > 165000) {
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);
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));
118 NV50CrtcSetScale(output->crtc, adjusted_mode, nv_output->scale);
121 static xf86OutputStatus
122 NV50SorDetect(xf86OutputPtr output)
124 NV50OutputPrivPtr nv_output = output->driver_private;
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;
130 NV50OutputPartnersDetect(nv_output->partner, output, nv_output->i2c);
131 return nv_output->cached_status;
134 static xf86OutputStatus
135 NV50SorLVDSDetect(xf86OutputPtr output)
137 /* Assume LVDS is always connected */
138 return XF86OutputStatusConnected;
142 NV50SorDestroy(xf86OutputPtr output)
144 NV50OutputPrivPtr nv_output = output->driver_private;
146 NV50OutputDestroy(output);
148 xf86DeleteMode(&nv_output->nativeMode, nv_output->nativeMode);
150 xfree(output->driver_private);
151 output->driver_private = NULL;
155 NV50SorSetModeBackend(DisplayModePtr dst, const DisplayModePtr src)
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;
178 NV50SorModeFixup(xf86OutputPtr output, DisplayModePtr mode,
179 DisplayModePtr adjusted_mode)
181 NV50OutputPrivPtr nv_output = output->driver_private;
182 DisplayModePtr native = nv_output->nativeMode;
184 if(native && nv_output->scale != NV50_SCALE_OFF) {
185 NV50SorSetModeBackend(adjusted_mode, native);
186 // This mode is already "fixed"
187 NV50CrtcSkipModeFixup(output->crtc);
194 NV50SorTMDSModeFixup(xf86OutputPtr output, DisplayModePtr mode,
195 DisplayModePtr adjusted_mode)
197 int scrnIndex = output->scrn->scrnIndex;
198 NV50OutputPrivPtr nv_output = output->driver_private;
199 DisplayModePtr modes = output->probed_modes;
201 xf86DeleteMode(&nv_output->nativeMode, nv_output->nativeMode);
204 // Find the preferred mode and use that as the "native" mode.
205 // If no preferred mode is available, use the first one.
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);
218 // XXX: May not want to allow scaling if no preferred mode is found.
221 xf86DrvMsgVerb(scrnIndex, X_INFO, 5,
222 "%s: no preferred mode found, using %s\n",
223 output->name, mode->name);
226 nv_output->nativeMode = xf86DuplicateMode(mode);
227 NV50CrtcDoModeFixup(nv_output->nativeMode, mode);
230 return NV50SorModeFixup(output, mode, adjusted_mode);
233 static DisplayModePtr
234 NV50SorGetLVDSModes(xf86OutputPtr output)
236 NV50OutputPrivPtr nv_output = output->driver_private;
237 return xf86DuplicateMode(nv_output->nativeMode);
240 #ifdef RANDR_12_INTERFACE
241 #define MAKE_ATOM(a) MakeAtom((a), sizeof(a) - 1, TRUE);
249 struct property dither;
250 struct property scale;
254 NV50SorCreateResources(xf86OutputPtr output)
256 ScrnInfoPtr pScrn = output->scrn;
257 NVPtr pNv = NVPTR(pScrn);
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);
269 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
270 "Failed to configure dithering property for %s: error %d\n",
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,
279 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
280 "Failed to set dithering property for %s: error %d\n",
283 /******** scaling ********/
284 properties.scale.atom = MAKE_ATOM("scale");
285 err = RRConfigureOutputProperty(output->randr_output,
286 properties.scale.atom, FALSE, FALSE,
289 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
290 "Failed to configure scaling property for %s: error %d\n",
293 // Set the default value
295 err = RRChangeOutputProperty(output->randr_output, properties.scale.atom,
296 XA_STRING, 8, PropModeReplace, strlen(s),
297 (pointer)s, FALSE, FALSE);
299 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
300 "Failed to set scaling property for %s: error %d\n",
305 NV50SorSetProperty(xf86OutputPtr output, Atom prop, RRPropertyValuePtr val)
307 NV50OutputPrivPtr nv_output = output->driver_private;
309 if(prop == properties.dither.atom) {
312 if (val->type != XA_INTEGER || val->format != 32 || val->size != 1)
315 i = *(INT32*)val->data;
316 if (i < properties.dither.range[0] || i > properties.dither.range[1])
319 NV50CrtcSetDither(output->crtc, i, TRUE);
321 } else if (prop == properties.scale.atom) {
323 enum NV50ScaleMode oldScale, scale;
327 enum NV50ScaleMode scale;
329 { "off", NV50_SCALE_OFF },
330 { "aspect", NV50_SCALE_ASPECT },
331 { "fill", NV50_SCALE_FILL },
332 { "center", NV50_SCALE_CENTER },
336 if (val->type != XA_STRING || val->format != 8)
338 s = (char*)val->data;
340 for (i = 0; modes[i].name; i++) {
341 const char *name = modes[i].name;
342 const int len = strlen(name);
344 if(val->size == len && !strncmp(name, s, len)) {
345 scale = modes[i].scale;
351 if (scale == NV50_SCALE_OFF && nv_output->panelType == LVDS)
352 // LVDS requires scaling
355 oldScale = nv_output->scale;
356 nv_output->scale = scale;
358 xf86CrtcPtr crtc = output->crtc;
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);
366 // Restore old scale and try again.
367 nv_output->scale = oldScale;
368 if (!xf86CrtcSetMode(crtc, &crtc->desiredMode,
369 crtc->desiredRotation, crtc->desiredX,
371 xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
372 "Failed to restore old scaling for output %s\n",
384 #endif // RANDR_12_INTERFACE
386 static const xf86OutputFuncsRec NV50SorTMDSOutputFuncs = {
387 .dpms = NV50SorDPMSSet,
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,
401 .destroy = NV50SorDestroy,
404 static const xf86OutputFuncsRec NV50SorLVDSOutputFuncs = {
405 .dpms = NV50SorDPMSSet,
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,
419 .destroy = NV50SorDestroy,
422 static DisplayModePtr
423 ReadLVDSNativeMode(ScrnInfoPtr pScrn, const int off)
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;
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);
438 mode->next = mode->prev = NULL;
439 mode->status = MODE_OK;
440 mode->type = M_T_DRIVER | M_T_PREFERRED;
442 xf86SetModeDefaultName(mode);
447 static DisplayModePtr
448 GetLVDSNativeMode(ScrnInfoPtr pScrn)
450 CARD32 val = NV50DisplayRead(pScrn, 0x50);
452 if ((val & 0x3) == 0x2) {
453 return ReadLVDSNativeMode(pScrn, 0);
454 } else if ((val & 0x300) == 0x200) {
455 return ReadLVDSNativeMode(pScrn, 0x540);
462 NV50CreateSor(ScrnInfoPtr pScrn, ORNum or, PanelType panelType)
464 NV50OutputPrivPtr nv_output = xnfcalloc(sizeof(*nv_output), 1);
465 xf86OutputPtr output;
467 const xf86OutputFuncsRec *funcs;
472 if(panelType == LVDS) {
473 strcpy(orName, "LVDS");
474 funcs = &NV50SorLVDSOutputFuncs;
476 nv_output->nativeMode = GetLVDSNativeMode(pScrn);
478 if(!nv_output->nativeMode) {
479 xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
480 "Failed to find LVDS native mode\n");
485 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "%s native size %dx%d\n",
486 orName, nv_output->nativeMode->HDisplay,
487 nv_output->nativeMode->VDisplay);
489 snprintf(orName, 5, "DVI%d", or);
490 funcs = &NV50SorTMDSOutputFuncs;
493 output = xf86OutputCreate(pScrn, funcs, orName);
495 nv_output->type = SOR;
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;
505 if (panelType != LVDS) {
506 NV50OutputWrite(output, 0xc00c, 0x03010700);
507 NV50OutputWrite(output, 0xc010, 0x0000152f);
508 NV50OutputWrite(output, 0xc014, 0x00000000);
509 NV50OutputWrite(output, 0xc018, 0x00245af8);