g80_output.c revision f3561b8b
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
25fc5a983dSmrg#ifdef HAVE_CONFIG_H
26fc5a983dSmrg#include "config.h"
27fc5a983dSmrg#endif
28fc5a983dSmrg
29fc5a983dSmrg#include <strings.h>
30fc5a983dSmrg
31fc5a983dSmrg#include "g80_type.h"
32fc5a983dSmrg#include "g80_display.h"
33fc5a983dSmrg#include "g80_output.h"
34fc5a983dSmrg
35fc5a983dSmrgstatic unsigned G80FindLoadVal(const unsigned char *table1)
36fc5a983dSmrg{
37fc5a983dSmrg    const unsigned char *p = table1;
38fc5a983dSmrg    int count;
39fc5a983dSmrg    const CARD32 def = 340;
40fc5a983dSmrg
41fc5a983dSmrg    for(p = table1; *(CARD16*)p != 0xb8ff && p < table1 + 64000; p += 2);
42fc5a983dSmrg    if(p == table1 + 64000)
43fc5a983dSmrg        return def;
44fc5a983dSmrg    p += 2;
45fc5a983dSmrg    if(*(CARD32*)p != 0x544942)
46fc5a983dSmrg        return def;
47fc5a983dSmrg    p += 4;
48fc5a983dSmrg    if(*(CARD16*)p != 0x100)
49fc5a983dSmrg        return def;
50fc5a983dSmrg    p += 2;
51fc5a983dSmrg    if(*p != 12)
52fc5a983dSmrg        return def;
53fc5a983dSmrg    p++;
54fc5a983dSmrg    if(*p != 6)
55fc5a983dSmrg        return def;
56fc5a983dSmrg    p++;
57fc5a983dSmrg    count = *p;
58fc5a983dSmrg    p += 2;
59fc5a983dSmrg    for(; *p != 'A' && count >= 0; count--, p += 6);
60fc5a983dSmrg    if(count == -1)
61fc5a983dSmrg        return def;
62fc5a983dSmrg    p += 4;
63fc5a983dSmrg    p = table1 + *(CARD16*)p;
64fc5a983dSmrg    p = table1 + *(CARD16*)p;
65fc5a983dSmrg    if(p[0] != 0x10 || p[1] != 4 || p[2] != 4 || p[3] != 2)
66fc5a983dSmrg        return def;
67fc5a983dSmrg    return *(CARD32*)(p + 4) & 0x3ff;
68fc5a983dSmrg}
69fc5a983dSmrg
70fc5a983dSmrgstatic Bool G80ReadPortMapping(int scrnIndex, G80Ptr pNv)
71fc5a983dSmrg{
72fc5a983dSmrg    unsigned char *table2, *table3;
73fc5a983dSmrg    unsigned char headerSize, entries, table3Entries, table3EntSize;
74fc5a983dSmrg    int i;
75fc5a983dSmrg    CARD16 a;
76fc5a983dSmrg    CARD32 b;
77fc5a983dSmrg
78fc5a983dSmrg    /* Clear the i2c map to invalid */
79fc5a983dSmrg    for(i = 0; i < G80_NUM_I2C_PORTS; i++)
80fc5a983dSmrg        pNv->i2cMap[i].dac = pNv->i2cMap[i].sor = -1;
81fc5a983dSmrg
82fc5a983dSmrg    if(*(CARD16*)pNv->table1 != 0xaa55) goto fail;
83fc5a983dSmrg
84fc5a983dSmrg    a = *(CARD16*)(pNv->table1 + 0x36);
85fc5a983dSmrg    table2 = (unsigned char*)pNv->table1 + a;
86fc5a983dSmrg
87fc5a983dSmrg    if(table2[0] != 0x40) goto fail;
88fc5a983dSmrg
89fc5a983dSmrg    b = *(CARD32*)(table2 + 6);
90fc5a983dSmrg    if(b != 0x4edcbdcb) goto fail;
91fc5a983dSmrg
92fc5a983dSmrg    table3 = (unsigned char*)pNv->table1 + *(CARD16*)(table2 + 4);
93fc5a983dSmrg    table3Entries = table3[2];
94fc5a983dSmrg    table3EntSize = table3[3];
95fc5a983dSmrg    table3 += table3[1];
96fc5a983dSmrg
97fc5a983dSmrg    headerSize = table2[1];
98fc5a983dSmrg    entries = table2[2];
99fc5a983dSmrg
100fc5a983dSmrg    for(i = 0; i < entries; i++) {
101fc5a983dSmrg        int type, port, portType;
102fc5a983dSmrg        ORNum or;
103fc5a983dSmrg
104fc5a983dSmrg        b = *(CARD32*)&table2[headerSize + 8*i];
105fc5a983dSmrg        type = b & 0xf;
106fc5a983dSmrg        port = (b >> 4) & 0xf;
107fc5a983dSmrg        or = ffs((b >> 24) & 0xf) - 1;
108fc5a983dSmrg
109fc5a983dSmrg        if(b & 0x300000)
110fc5a983dSmrg            /* Can't handle this type of output yet */
111fc5a983dSmrg            continue;
112fc5a983dSmrg
113fc5a983dSmrg        if(type == 0xe) break;
114fc5a983dSmrg
115fc5a983dSmrg        switch(type) {
116fc5a983dSmrg            case 0: /* CRT */
117fc5a983dSmrg                if(port >= table3Entries) {
118fc5a983dSmrg                    xf86DrvMsg(scrnIndex, X_WARNING,
119fc5a983dSmrg                               "VGA%d: invalid port %d\n", or, port);
120fc5a983dSmrg                    break;
121fc5a983dSmrg                }
122fc5a983dSmrg                b = *(CARD32*)&table3[table3EntSize * port];
123fc5a983dSmrg                port = b & 0xff;
124fc5a983dSmrg                portType = b >> 24;
125fc5a983dSmrg                if(portType != 5) {
126fc5a983dSmrg                    xf86DrvMsg(scrnIndex, X_WARNING,
127fc5a983dSmrg                               "VGA%d: invalid port type %d\n", or, portType);
128fc5a983dSmrg                    break;
129fc5a983dSmrg                }
130fc5a983dSmrg                if(pNv->i2cMap[port].dac != -1) {
131fc5a983dSmrg                    xf86DrvMsg(scrnIndex, X_WARNING,
132fc5a983dSmrg                               "DDC routing table corrupt!  DAC %i -> %i for "
133fc5a983dSmrg                               "port %i\n", or, pNv->i2cMap[port].dac, port);
134fc5a983dSmrg                }
135fc5a983dSmrg                pNv->i2cMap[port].dac = or;
136fc5a983dSmrg                break;
137fc5a983dSmrg            case 1: /* TV */
138fc5a983dSmrg                /* Ignore TVs */
139fc5a983dSmrg                break;
140fc5a983dSmrg
141fc5a983dSmrg            case 2: /* TMDS */
142fc5a983dSmrg                if(port >= table3Entries) {
143fc5a983dSmrg                    xf86DrvMsg(scrnIndex, X_WARNING,
144fc5a983dSmrg                               "DVI%d: invalid port %d\n", or, port);
145fc5a983dSmrg                    break;
146fc5a983dSmrg                }
147fc5a983dSmrg                b = *(CARD32*)&table3[table3EntSize * port];
148fc5a983dSmrg                port = b & 0xff;
149fc5a983dSmrg                portType = b >> 24;
150fc5a983dSmrg                if(portType != 5) {
151fc5a983dSmrg                    xf86DrvMsg(scrnIndex, X_WARNING,
152fc5a983dSmrg                               "DVI%d: invalid port type %d\n", or, portType);
153fc5a983dSmrg                    break;
154fc5a983dSmrg                }
155fc5a983dSmrg                if(pNv->i2cMap[port].sor != -1)
156fc5a983dSmrg                    xf86DrvMsg(scrnIndex, X_WARNING,
157fc5a983dSmrg                               "DDC routing table corrupt!  SOR %i -> %i for "
158fc5a983dSmrg                               "port %i\n", or, pNv->i2cMap[port].sor, port);
159fc5a983dSmrg                pNv->i2cMap[port].sor = or;
160fc5a983dSmrg                break;
161fc5a983dSmrg
162fc5a983dSmrg            case 3: /* LVDS */
163fc5a983dSmrg                pNv->lvds.present = TRUE;
164fc5a983dSmrg                pNv->lvds.or = or;
165fc5a983dSmrg                pNv->lvds.i2cPort = -1;
166fc5a983dSmrg
167fc5a983dSmrg                if(port == 15) {
168fc5a983dSmrg                    xf86DrvMsg(scrnIndex, X_INFO, "LVDS has no I2C port\n");
169fc5a983dSmrg                    break;
170fc5a983dSmrg                }
171fc5a983dSmrg                if(port >= table3Entries) {
172fc5a983dSmrg                    xf86DrvMsg(scrnIndex, X_WARNING,
173fc5a983dSmrg                               "LVDS: invalid port %d\n", port);
174fc5a983dSmrg                    break;
175fc5a983dSmrg                }
176fc5a983dSmrg                b = *(CARD32*)&table3[table3EntSize * port];
177fc5a983dSmrg                port = b & 0xff;
178fc5a983dSmrg                portType = b >> 24;
179fc5a983dSmrg                if(portType != 5) {
180fc5a983dSmrg                    xf86DrvMsg(scrnIndex, X_WARNING,
181fc5a983dSmrg                               "LVDS: invalid port type %d\n", portType);
182fc5a983dSmrg                    break;
183fc5a983dSmrg                }
184fc5a983dSmrg                pNv->lvds.i2cPort = port;
185fc5a983dSmrg
186fc5a983dSmrg                break;
187fc5a983dSmrg
188fc5a983dSmrg            default:
189fc5a983dSmrg                break;
190fc5a983dSmrg        }
191fc5a983dSmrg    }
192fc5a983dSmrg
193fc5a983dSmrg    xf86DrvMsg(scrnIndex, X_PROBED, "Connector map:\n");
194f3561b8bSmrg    if(pNv->lvds.present) {
195f3561b8bSmrg        if (pNv->lvds.i2cPort != -1)
196f3561b8bSmrg            xf86DrvMsg(scrnIndex, X_PROBED, "  Bus %i -> SOR%i (LVDS)\n", pNv->lvds.i2cPort, pNv->lvds.or);
197f3561b8bSmrg        else
198f3561b8bSmrg            xf86DrvMsg(scrnIndex, X_PROBED, "  [N/A] -> SOR%i (LVDS)\n", pNv->lvds.or);
199f3561b8bSmrg    }
200fc5a983dSmrg    for(i = 0; i < G80_NUM_I2C_PORTS; i++) {
201fc5a983dSmrg        if(pNv->i2cMap[i].dac != -1)
202fc5a983dSmrg            xf86DrvMsg(scrnIndex, X_PROBED, "  Bus %i -> DAC%i\n", i, pNv->i2cMap[i].dac);
203fc5a983dSmrg        if(pNv->i2cMap[i].sor != -1)
204fc5a983dSmrg            xf86DrvMsg(scrnIndex, X_PROBED, "  Bus %i -> SOR%i\n", i, pNv->i2cMap[i].sor);
205fc5a983dSmrg    }
206fc5a983dSmrg
207fc5a983dSmrg    pNv->loadVal = G80FindLoadVal(pNv->table1);
208fc5a983dSmrg    xf86DrvMsg(scrnIndex, X_PROBED, "Load detection: %d\n", pNv->loadVal);
209fc5a983dSmrg
210fc5a983dSmrg    return TRUE;
211fc5a983dSmrg
212fc5a983dSmrgfail:
213fc5a983dSmrg    xf86DrvMsg(scrnIndex, X_ERROR, "Couldn't find the DDC routing table.  "
214fc5a983dSmrg               "Mode setting will probably fail!\n");
215fc5a983dSmrg    return FALSE;
216fc5a983dSmrg}
217fc5a983dSmrg
218fc5a983dSmrgstatic CARD32 i2cAddr(const int port)
219fc5a983dSmrg{
220fc5a983dSmrg    const CARD32 base = (port > 3) ? 0x0000E1E0 : 0x0000E138;
221fc5a983dSmrg    return base + port * 0x18;
222fc5a983dSmrg}
223fc5a983dSmrg
224fc5a983dSmrgstatic void G80_I2CPutBits(I2CBusPtr b, int clock, int data)
225fc5a983dSmrg{
226fc5a983dSmrg    G80Ptr pNv = G80PTR(xf86Screens[b->scrnIndex]);
227fc5a983dSmrg    pNv->reg[i2cAddr(b->DriverPrivate.val)/4] = 4 | clock | data << 1;
228fc5a983dSmrg}
229fc5a983dSmrg
230fc5a983dSmrgstatic void G80_I2CGetBits(I2CBusPtr b, int *clock, int *data)
231fc5a983dSmrg{
232fc5a983dSmrg    G80Ptr pNv = G80PTR(xf86Screens[b->scrnIndex]);
233fc5a983dSmrg    unsigned char val;
234fc5a983dSmrg
235fc5a983dSmrg    val = pNv->reg[i2cAddr(b->DriverPrivate.val)/4];
236fc5a983dSmrg    *clock = !!(val & 1);
237fc5a983dSmrg    *data = !!(val & 2);
238fc5a983dSmrg}
239fc5a983dSmrg
240fc5a983dSmrgstatic I2CBusPtr
241fc5a983dSmrgG80I2CInit(ScrnInfoPtr pScrn, const char *name, const int port)
242fc5a983dSmrg{
243fc5a983dSmrg    I2CBusPtr i2c;
244fc5a983dSmrg
245fc5a983dSmrg    /* Allocate the I2C bus structure */
246fc5a983dSmrg    i2c = xf86CreateI2CBusRec();
247fc5a983dSmrg    if(!i2c) return NULL;
248fc5a983dSmrg
249fc5a983dSmrg    i2c->BusName = strdup(name);
250fc5a983dSmrg    i2c->scrnIndex = pScrn->scrnIndex;
251fc5a983dSmrg    i2c->I2CPutBits = G80_I2CPutBits;
252fc5a983dSmrg    i2c->I2CGetBits = G80_I2CGetBits;
253fc5a983dSmrg    i2c->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */
254fc5a983dSmrg    i2c->StartTimeout = 550;
255fc5a983dSmrg    i2c->BitTimeout = 40;
256fc5a983dSmrg    i2c->ByteTimeout = 40;
257fc5a983dSmrg    i2c->AcknTimeout = 40;
258fc5a983dSmrg    i2c->DriverPrivate.val = port;
259fc5a983dSmrg
260fc5a983dSmrg    if(xf86I2CBusInit(i2c)) {
261fc5a983dSmrg        return i2c;
262fc5a983dSmrg    } else {
263fc5a983dSmrg        xfree(i2c);
264fc5a983dSmrg        return NULL;
265fc5a983dSmrg    }
266fc5a983dSmrg}
267fc5a983dSmrg
268fc5a983dSmrgvoid
269fc5a983dSmrgG80OutputSetPClk(xf86OutputPtr output, int pclk)
270fc5a983dSmrg{
271fc5a983dSmrg    G80OutputPrivPtr pPriv = output->driver_private;
272fc5a983dSmrg    if(pPriv->set_pclk)
273fc5a983dSmrg        pPriv->set_pclk(output, pclk);
274fc5a983dSmrg}
275fc5a983dSmrg
276fc5a983dSmrgint
277fc5a983dSmrgG80OutputModeValid(xf86OutputPtr output, DisplayModePtr mode)
278fc5a983dSmrg{
279fc5a983dSmrg    if(mode->Clock > 400000)
280fc5a983dSmrg        return MODE_CLOCK_HIGH;
281fc5a983dSmrg    if(mode->Clock < 25000)
282fc5a983dSmrg        return MODE_CLOCK_LOW;
283fc5a983dSmrg
284fc5a983dSmrg    return MODE_OK;
285fc5a983dSmrg}
286fc5a983dSmrg
287fc5a983dSmrgvoid
288fc5a983dSmrgG80OutputPrepare(xf86OutputPtr output)
289fc5a983dSmrg{
290fc5a983dSmrg}
291fc5a983dSmrg
292fc5a983dSmrgvoid
293fc5a983dSmrgG80OutputCommit(xf86OutputPtr output)
294fc5a983dSmrg{
295fc5a983dSmrg}
296fc5a983dSmrg
297fc5a983dSmrgstatic xf86MonPtr
298fc5a983dSmrgProbeDDC(I2CBusPtr i2c)
299fc5a983dSmrg{
300fc5a983dSmrg    ScrnInfoPtr pScrn = xf86Screens[i2c->scrnIndex];
301fc5a983dSmrg    G80Ptr pNv = G80PTR(pScrn);
302fc5a983dSmrg    xf86MonPtr monInfo = NULL;
303fc5a983dSmrg    const int bus = i2c->DriverPrivate.val;
304fc5a983dSmrg    const CARD32 addr = i2cAddr(bus);
305fc5a983dSmrg
306fc5a983dSmrg    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
307fc5a983dSmrg            "Probing for EDID on I2C bus %i...\n", bus);
308fc5a983dSmrg    pNv->reg[addr/4] = 7;
309fc5a983dSmrg    /* Should probably use xf86OutputGetEDID here */
310f3561b8bSmrg#ifdef EDID_COMPLETE_RAWDATA
311f3561b8bSmrg    monInfo = xf86DoEEDID(pScrn->scrnIndex, i2c, TRUE);
312f3561b8bSmrg#else
313fc5a983dSmrg    monInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, i2c);
314f3561b8bSmrg#endif
315fc5a983dSmrg    pNv->reg[addr/4] = 3;
316fc5a983dSmrg
317fc5a983dSmrg    if(monInfo) {
318fc5a983dSmrg        xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
319fc5a983dSmrg                "DDC detected a %s:\n", monInfo->features.input_type ?
320fc5a983dSmrg                "DFP" : "CRT");
321fc5a983dSmrg        xf86PrintEDID(monInfo);
322fc5a983dSmrg    } else {
323fc5a983dSmrg        xf86DrvMsg(pScrn->scrnIndex, X_INFO, "  ... none found\n");
324fc5a983dSmrg    }
325fc5a983dSmrg
326fc5a983dSmrg    return monInfo;
327fc5a983dSmrg}
328fc5a983dSmrg
329fc5a983dSmrg/*
330fc5a983dSmrg * Read an EDID from the i2c port.  Perform load detection on the DAC (if
331fc5a983dSmrg * present) to see if the display is connected via VGA.  Sets the cached status
332fc5a983dSmrg * of both outputs.  The status is marked dirty again in the BlockHandler.
333fc5a983dSmrg */
334fc5a983dSmrgvoid G80OutputPartnersDetect(xf86OutputPtr dac, xf86OutputPtr sor, I2CBusPtr i2c)
335fc5a983dSmrg{
336fc5a983dSmrg    xf86MonPtr monInfo = ProbeDDC(i2c);
337fc5a983dSmrg    xf86OutputPtr connected = NULL;
338fc5a983dSmrg    Bool load = dac && G80DacLoadDetect(dac);
339fc5a983dSmrg
340fc5a983dSmrg    if(dac) {
341fc5a983dSmrg        G80OutputPrivPtr pPriv = dac->driver_private;
342fc5a983dSmrg
343fc5a983dSmrg        if(load) {
344fc5a983dSmrg            pPriv->cached_status = XF86OutputStatusConnected;
345fc5a983dSmrg            connected = dac;
346fc5a983dSmrg        } else {
347fc5a983dSmrg            pPriv->cached_status = XF86OutputStatusDisconnected;
348fc5a983dSmrg        }
349fc5a983dSmrg    }
350fc5a983dSmrg
351fc5a983dSmrg    if(sor) {
352fc5a983dSmrg        G80OutputPrivPtr pPriv = sor->driver_private;
353fc5a983dSmrg
354fc5a983dSmrg        if(monInfo && !load) {
355fc5a983dSmrg            pPriv->cached_status = XF86OutputStatusConnected;
356fc5a983dSmrg            connected = sor;
357fc5a983dSmrg        } else {
358fc5a983dSmrg            pPriv->cached_status = XF86OutputStatusDisconnected;
359fc5a983dSmrg        }
360fc5a983dSmrg    }
361fc5a983dSmrg
362fc5a983dSmrg    if(connected)
363fc5a983dSmrg        xf86OutputSetEDID(connected, monInfo);
364fc5a983dSmrg}
365fc5a983dSmrg
366fc5a983dSmrg/*
367fc5a983dSmrg * Reset the cached output status for all outputs.  Called from G80BlockHandler.
368fc5a983dSmrg */
369fc5a983dSmrgvoid
370fc5a983dSmrgG80OutputResetCachedStatus(ScrnInfoPtr pScrn)
371fc5a983dSmrg{
372fc5a983dSmrg    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
373fc5a983dSmrg    int i;
374fc5a983dSmrg
375fc5a983dSmrg    for(i = 0; i < xf86_config->num_output; i++) {
376fc5a983dSmrg        G80OutputPrivPtr pPriv = xf86_config->output[i]->driver_private;
377fc5a983dSmrg        pPriv->cached_status = XF86OutputStatusUnknown;
378fc5a983dSmrg    }
379fc5a983dSmrg}
380fc5a983dSmrg
381fc5a983dSmrgDisplayModePtr
382fc5a983dSmrgG80OutputGetDDCModes(xf86OutputPtr output)
383fc5a983dSmrg{
384fc5a983dSmrg    /* The EDID is read as part of the detect step */
385fc5a983dSmrg    output->funcs->detect(output);
386fc5a983dSmrg    return xf86OutputGetEDIDModes(output);
387fc5a983dSmrg}
388fc5a983dSmrg
389fc5a983dSmrgvoid
390fc5a983dSmrgG80OutputDestroy(xf86OutputPtr output)
391fc5a983dSmrg{
392fc5a983dSmrg    G80OutputPrivPtr pPriv = output->driver_private;
393fc5a983dSmrg
394fc5a983dSmrg    if(pPriv->partner)
395fc5a983dSmrg        ((G80OutputPrivPtr)pPriv->partner->driver_private)->partner = NULL;
396fc5a983dSmrg    else
397fc5a983dSmrg        xf86DestroyI2CBusRec(pPriv->i2c, TRUE, TRUE);
398fc5a983dSmrg    pPriv->i2c = NULL;
399fc5a983dSmrg}
400fc5a983dSmrg
401fc5a983dSmrgBool
402fc5a983dSmrgG80CreateOutputs(ScrnInfoPtr pScrn)
403fc5a983dSmrg{
404fc5a983dSmrg    G80Ptr pNv = G80PTR(pScrn);
405fc5a983dSmrg    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
406fc5a983dSmrg    int i;
407fc5a983dSmrg
408fc5a983dSmrg    if(!G80ReadPortMapping(pScrn->scrnIndex, pNv))
409fc5a983dSmrg        return FALSE;
410fc5a983dSmrg
411fc5a983dSmrg    /* For each DDC port, create an output for the attached ORs */
412fc5a983dSmrg    for(i = 0; i < G80_NUM_I2C_PORTS; i++) {
413fc5a983dSmrg        xf86OutputPtr dac = NULL, sor = NULL;
414fc5a983dSmrg        I2CBusPtr i2c;
415fc5a983dSmrg        char i2cName[16];
416fc5a983dSmrg
417fc5a983dSmrg        if(pNv->i2cMap[i].dac == -1 && pNv->i2cMap[i].sor == -1)
418fc5a983dSmrg            /* No outputs on this port */
419fc5a983dSmrg            continue;
420fc5a983dSmrg
421fc5a983dSmrg        snprintf(i2cName, sizeof(i2cName), "I2C%i", i);
422fc5a983dSmrg        i2c = G80I2CInit(pScrn, i2cName, i);
423fc5a983dSmrg        if(!i2c) {
424fc5a983dSmrg            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
425fc5a983dSmrg                       "Failed to initialize I2C for port %i.\n",
426fc5a983dSmrg                       i);
427fc5a983dSmrg            continue;
428fc5a983dSmrg        }
429fc5a983dSmrg
430fc5a983dSmrg        if(pNv->i2cMap[i].dac != -1)
431fc5a983dSmrg            dac = G80CreateDac(pScrn, pNv->i2cMap[i].dac);
432fc5a983dSmrg        if(pNv->i2cMap[i].sor != -1)
433fc5a983dSmrg            sor = G80CreateSor(pScrn, pNv->i2cMap[i].sor, TMDS);
434fc5a983dSmrg
435fc5a983dSmrg        if(dac) {
436fc5a983dSmrg            G80OutputPrivPtr pPriv = dac->driver_private;
437fc5a983dSmrg
438fc5a983dSmrg            pPriv->partner = sor;
439fc5a983dSmrg            pPriv->i2c = i2c;
440fc5a983dSmrg            pPriv->scale = G80_SCALE_OFF;
441fc5a983dSmrg        }
442fc5a983dSmrg        if(sor) {
443fc5a983dSmrg            G80OutputPrivPtr pPriv = sor->driver_private;
444fc5a983dSmrg
445fc5a983dSmrg            pPriv->partner = dac;
446fc5a983dSmrg            pPriv->i2c = i2c;
447fc5a983dSmrg            pPriv->scale = G80_SCALE_ASPECT;
448fc5a983dSmrg        }
449fc5a983dSmrg    }
450fc5a983dSmrg
451fc5a983dSmrg    if(pNv->lvds.present) {
452fc5a983dSmrg        xf86OutputPtr lvds = G80CreateSor(pScrn, pNv->lvds.or, LVDS);
453fc5a983dSmrg        G80OutputPrivPtr pPriv = lvds->driver_private;
454fc5a983dSmrg
455fc5a983dSmrg        pPriv->scale = G80_SCALE_ASPECT;
456fc5a983dSmrg
457fc5a983dSmrg        if(pNv->lvds.i2cPort != -1) {
458fc5a983dSmrg            char i2cName[16];
459fc5a983dSmrg
460fc5a983dSmrg            snprintf(i2cName, sizeof(i2cName), "I2C%i (LVDS)", pNv->lvds.i2cPort);
461fc5a983dSmrg            pPriv->i2c = G80I2CInit(pScrn, i2cName, pNv->lvds.i2cPort);
462fc5a983dSmrg            if(!pPriv->i2c) {
463fc5a983dSmrg                xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
464fc5a983dSmrg                           "Failed to initialize I2C for port %i (LVDS)!\n",
465fc5a983dSmrg                           pNv->lvds.i2cPort);
466fc5a983dSmrg            }
467fc5a983dSmrg        }
468fc5a983dSmrg    }
469fc5a983dSmrg
470fc5a983dSmrg    /* For each output, set the crtc and clone masks */
471fc5a983dSmrg    for(i = 0; i < xf86_config->num_output; i++) {
472fc5a983dSmrg        xf86OutputPtr output = xf86_config->output[i];
473fc5a983dSmrg
474fc5a983dSmrg        /* Any output can connect to any head */
475fc5a983dSmrg        output->possible_crtcs = 0x3;
476fc5a983dSmrg        output->possible_clones = 0;
477fc5a983dSmrg    }
478fc5a983dSmrg
479fc5a983dSmrg    return TRUE;
480fc5a983dSmrg}
481