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