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.
31 #include <X11/extensions/dpms.h>
32 #include <X11/Xatom.h>
34 #include "nv_include.h"
35 #include "nv50_type.h"
36 #include "nv50_display.h"
37 #include "nv50_output.h"
40 NV50SorSetPClk(xf86OutputPtr output, int pclk)
42 NVPtr pNv = NVPTR(output->scrn);
43 NV50OutputPrivPtr pPriv = output->driver_private;
44 const int orOff = 0x800 * pPriv->or;
45 const int limit = 165000;
47 pNv->REGS[(0x00614300+orOff)/4] = (pclk > limit) ? 0x101 : 0;
51 NV50SorDPMSSet(xf86OutputPtr output, int mode)
53 NVPtr pNv = NVPTR(output->scrn);
54 NV50OutputPrivPtr pPriv = output->driver_private;
55 const int off = 0x800 * pPriv->or;
58 while(pNv->REGS[(0x0061C004+off)/4] & 0x80000000);
60 tmp = pNv->REGS[(0x0061C004+off)/4];
63 if(mode == DPMSModeOn)
68 pNv->REGS[(0x0061C004+off)/4] = tmp;
69 while((pNv->REGS[(0x0061c030+off)/4] & 0x10000000));
73 NV50TMDSModeValid(xf86OutputPtr output, DisplayModePtr mode)
75 // Disable dual-link modes until I can find a way to make them work
77 if (mode->Clock > 165000)
78 return MODE_CLOCK_HIGH;
80 return NV50OutputModeValid(output, mode);
84 NV50LVDSModeValid(xf86OutputPtr output, DisplayModePtr mode)
86 NV50OutputPrivPtr pPriv = output->driver_private;
87 DisplayModePtr native = pPriv->nativeMode;
89 // Ignore modes larger than the native res.
90 if (mode->HDisplay > native->HDisplay || mode->VDisplay > native->VDisplay)
93 return NV50OutputModeValid(output, mode);
97 NV50SorModeSet(xf86OutputPtr output, DisplayModePtr mode,
98 DisplayModePtr adjusted_mode)
100 ScrnInfoPtr pScrn = output->scrn;
101 NV50OutputPrivPtr pPriv = output->driver_private;
102 const int sorOff = 0x40 * pPriv->or;
106 /* Disconnect the SOR */
107 NV50DisplayCommand(pScrn, 0x600 + sorOff, 0);
111 if (pPriv->panelType == LVDS) {
114 if (adjusted_mode->Clock > 165000) {
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);
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));
130 NV50CrtcSetScale(output->crtc, adjusted_mode, pPriv->scale);
133 static xf86OutputStatus
134 NV50SorDetect(xf86OutputPtr output)
136 NV50OutputPrivPtr pPriv = output->driver_private;
138 /* Assume physical status isn't going to change before the BlockHandler */
139 if(pPriv->cached_status != XF86OutputStatusUnknown)
140 return pPriv->cached_status;
142 NV50OutputPartnersDetect(pPriv->partner, output, pPriv->i2c);
143 return pPriv->cached_status;
146 static xf86OutputStatus
147 NV50SorLVDSDetect(xf86OutputPtr output)
149 /* Assume LVDS is always connected */
150 return XF86OutputStatusConnected;
154 NV50SorDestroy(xf86OutputPtr output)
156 NV50OutputPrivPtr pPriv = output->driver_private;
158 NV50OutputDestroy(output);
160 xf86DeleteMode(&pPriv->nativeMode, pPriv->nativeMode);
162 xfree(output->driver_private);
163 output->driver_private = NULL;
167 NV50SorSetModeBackend(DisplayModePtr dst, const DisplayModePtr src)
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;
190 NV50SorModeFixup(xf86OutputPtr output, DisplayModePtr mode,
191 DisplayModePtr adjusted_mode)
193 NV50OutputPrivPtr pPriv = output->driver_private;
194 DisplayModePtr native = pPriv->nativeMode;
196 if(native && pPriv->scale != NV50_SCALE_OFF) {
197 NV50SorSetModeBackend(adjusted_mode, native);
198 // This mode is already "fixed"
199 NV50CrtcSkipModeFixup(output->crtc);
206 NV50SorTMDSModeFixup(xf86OutputPtr output, DisplayModePtr mode,
207 DisplayModePtr adjusted_mode)
209 int scrnIndex = output->scrn->scrnIndex;
210 NV50OutputPrivPtr pPriv = output->driver_private;
211 DisplayModePtr modes = output->probed_modes;
213 xf86DeleteMode(&pPriv->nativeMode, pPriv->nativeMode);
216 // Find the preferred mode and use that as the "native" mode.
217 // If no preferred mode is available, use the first one.
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);
230 // XXX: May not want to allow scaling if no preferred mode is found.
233 xf86DrvMsgVerb(scrnIndex, X_INFO, 5,
234 "%s: no preferred mode found, using %s\n",
235 output->name, mode->name);
238 pPriv->nativeMode = xf86DuplicateMode(mode);
239 NV50CrtcDoModeFixup(pPriv->nativeMode, mode);
242 return NV50SorModeFixup(output, mode, adjusted_mode);
245 static DisplayModePtr
246 NV50SorGetLVDSModes(xf86OutputPtr output)
248 NV50OutputPrivPtr pPriv = output->driver_private;
249 return xf86DuplicateMode(pPriv->nativeMode);
252 #ifdef RANDR_12_INTERFACE
253 #define MAKE_ATOM(a) MakeAtom((a), sizeof(a) - 1, TRUE);
261 struct property dither;
262 struct property scale;
266 NV50SorCreateResources(xf86OutputPtr output)
268 ScrnInfoPtr pScrn = output->scrn;
269 NVPtr pNv = NVPTR(pScrn);
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);
281 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
282 "Failed to configure dithering property for %s: error %d\n",
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,
291 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
292 "Failed to set dithering property for %s: error %d\n",
295 /******** scaling ********/
296 properties.scale.atom = MAKE_ATOM("scale");
297 err = RRConfigureOutputProperty(output->randr_output,
298 properties.scale.atom, FALSE, FALSE,
301 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
302 "Failed to configure scaling property for %s: error %d\n",
305 // Set the default value
307 err = RRChangeOutputProperty(output->randr_output, properties.scale.atom,
308 XA_STRING, 8, PropModeReplace, strlen(s),
309 (pointer)s, FALSE, FALSE);
311 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
312 "Failed to set scaling property for %s: error %d\n",
317 NV50SorSetProperty(xf86OutputPtr output, Atom prop, RRPropertyValuePtr val)
319 NV50OutputPrivPtr pPriv = output->driver_private;
321 if(prop == properties.dither.atom) {
324 if(val->type != XA_INTEGER || val->format != 32 || val->size != 1)
327 i = *(INT32*)val->data;
328 if(i < properties.dither.range[0] || i > properties.dither.range[1])
331 NV50CrtcSetDither(output->crtc, i, TRUE);
333 } else if(prop == properties.scale.atom) {
335 enum NV50ScaleMode oldScale, scale;
339 enum NV50ScaleMode scale;
341 { "off", NV50_SCALE_OFF },
342 { "aspect", NV50_SCALE_ASPECT },
343 { "fill", NV50_SCALE_FILL },
344 { "center", NV50_SCALE_CENTER },
348 if(val->type != XA_STRING || val->format != 8)
350 s = (char*)val->data;
352 for(i = 0; modes[i].name; i++) {
353 const char *name = modes[i].name;
354 const int len = strlen(name);
356 if(val->size == len && !strncmp(name, s, len)) {
357 scale = modes[i].scale;
363 if(scale == NV50_SCALE_OFF && pPriv->panelType == LVDS)
364 // LVDS requires scaling
367 oldScale = pPriv->scale;
368 pPriv->scale = scale;
370 xf86CrtcPtr crtc = output->crtc;
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);
378 // Restore old scale and try again.
379 pPriv->scale = oldScale;
380 if(!xf86CrtcSetMode(crtc, &crtc->desiredMode,
381 crtc->desiredRotation, crtc->desiredX,
383 xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
384 "Failed to restore old scaling for output %s\n",
396 #endif // RANDR_12_INTERFACE
398 static const xf86OutputFuncsRec NV50SorTMDSOutputFuncs = {
399 .dpms = NV50SorDPMSSet,
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,
413 .destroy = NV50SorDestroy,
416 static const xf86OutputFuncsRec NV50SorLVDSOutputFuncs = {
417 .dpms = NV50SorDPMSSet,
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,
431 .destroy = NV50SorDestroy,
434 static DisplayModePtr
435 GetLVDSNativeMode(NVPtr pNv)
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;
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];
450 mode->next = mode->prev = NULL;
451 mode->status = MODE_OK;
452 mode->type = M_T_DRIVER | M_T_PREFERRED;
454 xf86SetModeDefaultName(mode);
460 NV50CreateSor(ScrnInfoPtr pScrn, ORNum or, PanelType panelType)
462 NVPtr pNv = NVPTR(pScrn);
463 NV50OutputPrivPtr pPriv = xnfcalloc(sizeof(*pPriv), 1);
464 const int off = 0x800 * or;
465 xf86OutputPtr output;
467 const xf86OutputFuncsRec *funcs;
472 if(panelType == LVDS) {
473 strcpy(orName, "LVDS");
474 funcs = &NV50SorLVDSOutputFuncs;
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;
484 output = xf86OutputCreate(pScrn, funcs, orName);
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;
496 if(panelType == LVDS) {
497 pPriv->nativeMode = GetLVDSNativeMode(pNv);
499 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "%s native size %dx%d\n",
500 orName, pPriv->nativeMode->HDisplay,
501 pPriv->nativeMode->VDisplay);
507 #endif /* ENABLE_RANDR12 */