g80_output.c revision bd2f6fc9
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                }
130bd2f6fc9Smrg                if(port >= G80_NUM_I2C_PORTS) {
131bd2f6fc9Smrg                    xf86DrvMsg(scrnIndex, X_WARNING,
132bd2f6fc9Smrg                               "VGA%d: unrecognized port %d\n", or, port);
133bd2f6fc9Smrg                    break;
134bd2f6fc9Smrg                }
135fc5a983dSmrg                if(pNv->i2cMap[port].dac != -1) {
136fc5a983dSmrg                    xf86DrvMsg(scrnIndex, X_WARNING,
137fc5a983dSmrg                               "DDC routing table corrupt!  DAC %i -> %i for "
138fc5a983dSmrg                               "port %i\n", or, pNv->i2cMap[port].dac, port);
139fc5a983dSmrg                }
140fc5a983dSmrg                pNv->i2cMap[port].dac = or;
141fc5a983dSmrg                break;
142fc5a983dSmrg            case 1: /* TV */
143fc5a983dSmrg                /* Ignore TVs */
144fc5a983dSmrg                break;
145fc5a983dSmrg
146fc5a983dSmrg            case 2: /* TMDS */
147fc5a983dSmrg                if(port >= table3Entries) {
148fc5a983dSmrg                    xf86DrvMsg(scrnIndex, X_WARNING,
149fc5a983dSmrg                               "DVI%d: invalid port %d\n", or, port);
150fc5a983dSmrg                    break;
151fc5a983dSmrg                }
152fc5a983dSmrg                b = *(CARD32*)&table3[table3EntSize * port];
153fc5a983dSmrg                port = b & 0xff;
154fc5a983dSmrg                portType = b >> 24;
155fc5a983dSmrg                if(portType != 5) {
156fc5a983dSmrg                    xf86DrvMsg(scrnIndex, X_WARNING,
157fc5a983dSmrg                               "DVI%d: invalid port type %d\n", or, portType);
158fc5a983dSmrg                    break;
159fc5a983dSmrg                }
160bd2f6fc9Smrg                if(port >= G80_NUM_I2C_PORTS) {
161bd2f6fc9Smrg                    xf86DrvMsg(scrnIndex, X_WARNING,
162bd2f6fc9Smrg                               "DVI%d: unrecognized port %d\n", or, port);
163bd2f6fc9Smrg                    break;
164bd2f6fc9Smrg                }
165fc5a983dSmrg                if(pNv->i2cMap[port].sor != -1)
166fc5a983dSmrg                    xf86DrvMsg(scrnIndex, X_WARNING,
167fc5a983dSmrg                               "DDC routing table corrupt!  SOR %i -> %i for "
168fc5a983dSmrg                               "port %i\n", or, pNv->i2cMap[port].sor, port);
169fc5a983dSmrg                pNv->i2cMap[port].sor = or;
170fc5a983dSmrg                break;
171fc5a983dSmrg
172fc5a983dSmrg            case 3: /* LVDS */
173fc5a983dSmrg                pNv->lvds.present = TRUE;
174fc5a983dSmrg                pNv->lvds.or = or;
175fc5a983dSmrg                pNv->lvds.i2cPort = -1;
176fc5a983dSmrg
177fc5a983dSmrg                if(port == 15) {
178fc5a983dSmrg                    xf86DrvMsg(scrnIndex, X_INFO, "LVDS has no I2C port\n");
179fc5a983dSmrg                    break;
180fc5a983dSmrg                }
181fc5a983dSmrg                if(port >= table3Entries) {
182fc5a983dSmrg                    xf86DrvMsg(scrnIndex, X_WARNING,
183fc5a983dSmrg                               "LVDS: invalid port %d\n", port);
184fc5a983dSmrg                    break;
185fc5a983dSmrg                }
186fc5a983dSmrg                b = *(CARD32*)&table3[table3EntSize * port];
187fc5a983dSmrg                port = b & 0xff;
188fc5a983dSmrg                portType = b >> 24;
189fc5a983dSmrg                if(portType != 5) {
190fc5a983dSmrg                    xf86DrvMsg(scrnIndex, X_WARNING,
191fc5a983dSmrg                               "LVDS: invalid port type %d\n", portType);
192fc5a983dSmrg                    break;
193fc5a983dSmrg                }
194bd2f6fc9Smrg                if(port >= G80_NUM_I2C_PORTS) {
195bd2f6fc9Smrg                    xf86DrvMsg(scrnIndex, X_WARNING,
196bd2f6fc9Smrg                               "LVDS: unrecognized port %d\n", port);
197bd2f6fc9Smrg                    break;
198bd2f6fc9Smrg                }
199fc5a983dSmrg                pNv->lvds.i2cPort = port;
200fc5a983dSmrg
201fc5a983dSmrg                break;
202fc5a983dSmrg
203fc5a983dSmrg            default:
204fc5a983dSmrg                break;
205fc5a983dSmrg        }
206fc5a983dSmrg    }
207fc5a983dSmrg
208fc5a983dSmrg    xf86DrvMsg(scrnIndex, X_PROBED, "Connector map:\n");
209f3561b8bSmrg    if(pNv->lvds.present) {
210f3561b8bSmrg        if (pNv->lvds.i2cPort != -1)
211f3561b8bSmrg            xf86DrvMsg(scrnIndex, X_PROBED, "  Bus %i -> SOR%i (LVDS)\n", pNv->lvds.i2cPort, pNv->lvds.or);
212f3561b8bSmrg        else
213f3561b8bSmrg            xf86DrvMsg(scrnIndex, X_PROBED, "  [N/A] -> SOR%i (LVDS)\n", pNv->lvds.or);
214f3561b8bSmrg    }
215fc5a983dSmrg    for(i = 0; i < G80_NUM_I2C_PORTS; i++) {
216fc5a983dSmrg        if(pNv->i2cMap[i].dac != -1)
217fc5a983dSmrg            xf86DrvMsg(scrnIndex, X_PROBED, "  Bus %i -> DAC%i\n", i, pNv->i2cMap[i].dac);
218fc5a983dSmrg        if(pNv->i2cMap[i].sor != -1)
219fc5a983dSmrg            xf86DrvMsg(scrnIndex, X_PROBED, "  Bus %i -> SOR%i\n", i, pNv->i2cMap[i].sor);
220fc5a983dSmrg    }
221fc5a983dSmrg
222fc5a983dSmrg    pNv->loadVal = G80FindLoadVal(pNv->table1);
223fc5a983dSmrg    xf86DrvMsg(scrnIndex, X_PROBED, "Load detection: %d\n", pNv->loadVal);
224fc5a983dSmrg
225fc5a983dSmrg    return TRUE;
226fc5a983dSmrg
227fc5a983dSmrgfail:
228fc5a983dSmrg    xf86DrvMsg(scrnIndex, X_ERROR, "Couldn't find the DDC routing table.  "
229fc5a983dSmrg               "Mode setting will probably fail!\n");
230fc5a983dSmrg    return FALSE;
231fc5a983dSmrg}
232fc5a983dSmrg
233fc5a983dSmrgstatic CARD32 i2cAddr(const int port)
234fc5a983dSmrg{
235bd2f6fc9Smrg    const CARD32 addrs[G80_NUM_I2C_PORTS] = {
236bd2f6fc9Smrg        0xE138, 0xE150, 0xE168, 0xE180, 0xE254, 0xE274, 0xE764, 0xE780, 0xE79C,
237bd2f6fc9Smrg        0xE7B8
238bd2f6fc9Smrg    };
239bd2f6fc9Smrg    return addrs[port];
240fc5a983dSmrg}
241fc5a983dSmrg
242fc5a983dSmrgstatic void G80_I2CPutBits(I2CBusPtr b, int clock, int data)
243fc5a983dSmrg{
244fc5a983dSmrg    G80Ptr pNv = G80PTR(xf86Screens[b->scrnIndex]);
245fc5a983dSmrg    pNv->reg[i2cAddr(b->DriverPrivate.val)/4] = 4 | clock | data << 1;
246fc5a983dSmrg}
247fc5a983dSmrg
248fc5a983dSmrgstatic void G80_I2CGetBits(I2CBusPtr b, int *clock, int *data)
249fc5a983dSmrg{
250fc5a983dSmrg    G80Ptr pNv = G80PTR(xf86Screens[b->scrnIndex]);
251fc5a983dSmrg    unsigned char val;
252fc5a983dSmrg
253fc5a983dSmrg    val = pNv->reg[i2cAddr(b->DriverPrivate.val)/4];
254fc5a983dSmrg    *clock = !!(val & 1);
255fc5a983dSmrg    *data = !!(val & 2);
256fc5a983dSmrg}
257fc5a983dSmrg
258fc5a983dSmrgstatic I2CBusPtr
259fc5a983dSmrgG80I2CInit(ScrnInfoPtr pScrn, const char *name, const int port)
260fc5a983dSmrg{
261fc5a983dSmrg    I2CBusPtr i2c;
262fc5a983dSmrg
263fc5a983dSmrg    /* Allocate the I2C bus structure */
264fc5a983dSmrg    i2c = xf86CreateI2CBusRec();
265fc5a983dSmrg    if(!i2c) return NULL;
266fc5a983dSmrg
267fc5a983dSmrg    i2c->BusName = strdup(name);
268fc5a983dSmrg    i2c->scrnIndex = pScrn->scrnIndex;
269fc5a983dSmrg    i2c->I2CPutBits = G80_I2CPutBits;
270fc5a983dSmrg    i2c->I2CGetBits = G80_I2CGetBits;
271fc5a983dSmrg    i2c->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */
272fc5a983dSmrg    i2c->StartTimeout = 550;
273fc5a983dSmrg    i2c->BitTimeout = 40;
274fc5a983dSmrg    i2c->ByteTimeout = 40;
275fc5a983dSmrg    i2c->AcknTimeout = 40;
276fc5a983dSmrg    i2c->DriverPrivate.val = port;
277fc5a983dSmrg
278fc5a983dSmrg    if(xf86I2CBusInit(i2c)) {
279fc5a983dSmrg        return i2c;
280fc5a983dSmrg    } else {
281fc5a983dSmrg        xfree(i2c);
282fc5a983dSmrg        return NULL;
283fc5a983dSmrg    }
284fc5a983dSmrg}
285fc5a983dSmrg
286fc5a983dSmrgvoid
287fc5a983dSmrgG80OutputSetPClk(xf86OutputPtr output, int pclk)
288fc5a983dSmrg{
289fc5a983dSmrg    G80OutputPrivPtr pPriv = output->driver_private;
290fc5a983dSmrg    if(pPriv->set_pclk)
291fc5a983dSmrg        pPriv->set_pclk(output, pclk);
292fc5a983dSmrg}
293fc5a983dSmrg
294fc5a983dSmrgint
295fc5a983dSmrgG80OutputModeValid(xf86OutputPtr output, DisplayModePtr mode)
296fc5a983dSmrg{
297fc5a983dSmrg    if(mode->Clock > 400000)
298fc5a983dSmrg        return MODE_CLOCK_HIGH;
299fc5a983dSmrg    if(mode->Clock < 25000)
300fc5a983dSmrg        return MODE_CLOCK_LOW;
301fc5a983dSmrg
302fc5a983dSmrg    return MODE_OK;
303fc5a983dSmrg}
304fc5a983dSmrg
305fc5a983dSmrgvoid
306fc5a983dSmrgG80OutputPrepare(xf86OutputPtr output)
307fc5a983dSmrg{
308fc5a983dSmrg}
309fc5a983dSmrg
310fc5a983dSmrgvoid
311fc5a983dSmrgG80OutputCommit(xf86OutputPtr output)
312fc5a983dSmrg{
313fc5a983dSmrg}
314fc5a983dSmrg
315fc5a983dSmrgstatic xf86MonPtr
316fc5a983dSmrgProbeDDC(I2CBusPtr i2c)
317fc5a983dSmrg{
318fc5a983dSmrg    ScrnInfoPtr pScrn = xf86Screens[i2c->scrnIndex];
319fc5a983dSmrg    G80Ptr pNv = G80PTR(pScrn);
320fc5a983dSmrg    xf86MonPtr monInfo = NULL;
321fc5a983dSmrg    const int bus = i2c->DriverPrivate.val;
322fc5a983dSmrg    const CARD32 addr = i2cAddr(bus);
323fc5a983dSmrg
324fc5a983dSmrg    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
325fc5a983dSmrg            "Probing for EDID on I2C bus %i...\n", bus);
326fc5a983dSmrg    pNv->reg[addr/4] = 7;
327fc5a983dSmrg    /* Should probably use xf86OutputGetEDID here */
328f3561b8bSmrg#ifdef EDID_COMPLETE_RAWDATA
329f3561b8bSmrg    monInfo = xf86DoEEDID(pScrn->scrnIndex, i2c, TRUE);
330f3561b8bSmrg#else
331fc5a983dSmrg    monInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, i2c);
332f3561b8bSmrg#endif
333fc5a983dSmrg    pNv->reg[addr/4] = 3;
334fc5a983dSmrg
335fc5a983dSmrg    if(monInfo) {
336fc5a983dSmrg        xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
337fc5a983dSmrg                "DDC detected a %s:\n", monInfo->features.input_type ?
338fc5a983dSmrg                "DFP" : "CRT");
339fc5a983dSmrg        xf86PrintEDID(monInfo);
340fc5a983dSmrg    } else {
341fc5a983dSmrg        xf86DrvMsg(pScrn->scrnIndex, X_INFO, "  ... none found\n");
342fc5a983dSmrg    }
343fc5a983dSmrg
344fc5a983dSmrg    return monInfo;
345fc5a983dSmrg}
346fc5a983dSmrg
347fc5a983dSmrg/*
348fc5a983dSmrg * Read an EDID from the i2c port.  Perform load detection on the DAC (if
349fc5a983dSmrg * present) to see if the display is connected via VGA.  Sets the cached status
350fc5a983dSmrg * of both outputs.  The status is marked dirty again in the BlockHandler.
351fc5a983dSmrg */
352fc5a983dSmrgvoid G80OutputPartnersDetect(xf86OutputPtr dac, xf86OutputPtr sor, I2CBusPtr i2c)
353fc5a983dSmrg{
354fc5a983dSmrg    xf86MonPtr monInfo = ProbeDDC(i2c);
355fc5a983dSmrg    xf86OutputPtr connected = NULL;
356fc5a983dSmrg    Bool load = dac && G80DacLoadDetect(dac);
357fc5a983dSmrg
358fc5a983dSmrg    if(dac) {
359fc5a983dSmrg        G80OutputPrivPtr pPriv = dac->driver_private;
360fc5a983dSmrg
361fc5a983dSmrg        if(load) {
362fc5a983dSmrg            pPriv->cached_status = XF86OutputStatusConnected;
363fc5a983dSmrg            connected = dac;
364fc5a983dSmrg        } else {
365fc5a983dSmrg            pPriv->cached_status = XF86OutputStatusDisconnected;
366fc5a983dSmrg        }
367fc5a983dSmrg    }
368fc5a983dSmrg
369fc5a983dSmrg    if(sor) {
370fc5a983dSmrg        G80OutputPrivPtr pPriv = sor->driver_private;
371fc5a983dSmrg
372fc5a983dSmrg        if(monInfo && !load) {
373fc5a983dSmrg            pPriv->cached_status = XF86OutputStatusConnected;
374fc5a983dSmrg            connected = sor;
375fc5a983dSmrg        } else {
376fc5a983dSmrg            pPriv->cached_status = XF86OutputStatusDisconnected;
377fc5a983dSmrg        }
378fc5a983dSmrg    }
379fc5a983dSmrg
380fc5a983dSmrg    if(connected)
381fc5a983dSmrg        xf86OutputSetEDID(connected, monInfo);
382fc5a983dSmrg}
383fc5a983dSmrg
384fc5a983dSmrg/*
385fc5a983dSmrg * Reset the cached output status for all outputs.  Called from G80BlockHandler.
386fc5a983dSmrg */
387fc5a983dSmrgvoid
388fc5a983dSmrgG80OutputResetCachedStatus(ScrnInfoPtr pScrn)
389fc5a983dSmrg{
390fc5a983dSmrg    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
391fc5a983dSmrg    int i;
392fc5a983dSmrg
393fc5a983dSmrg    for(i = 0; i < xf86_config->num_output; i++) {
394fc5a983dSmrg        G80OutputPrivPtr pPriv = xf86_config->output[i]->driver_private;
395fc5a983dSmrg        pPriv->cached_status = XF86OutputStatusUnknown;
396fc5a983dSmrg    }
397fc5a983dSmrg}
398fc5a983dSmrg
399fc5a983dSmrgDisplayModePtr
400fc5a983dSmrgG80OutputGetDDCModes(xf86OutputPtr output)
401fc5a983dSmrg{
402fc5a983dSmrg    /* The EDID is read as part of the detect step */
403fc5a983dSmrg    output->funcs->detect(output);
404fc5a983dSmrg    return xf86OutputGetEDIDModes(output);
405fc5a983dSmrg}
406fc5a983dSmrg
407fc5a983dSmrgvoid
408fc5a983dSmrgG80OutputDestroy(xf86OutputPtr output)
409fc5a983dSmrg{
410fc5a983dSmrg    G80OutputPrivPtr pPriv = output->driver_private;
411fc5a983dSmrg
412fc5a983dSmrg    if(pPriv->partner)
413fc5a983dSmrg        ((G80OutputPrivPtr)pPriv->partner->driver_private)->partner = NULL;
414fc5a983dSmrg    else
415fc5a983dSmrg        xf86DestroyI2CBusRec(pPriv->i2c, TRUE, TRUE);
416fc5a983dSmrg    pPriv->i2c = NULL;
417fc5a983dSmrg}
418fc5a983dSmrg
419fc5a983dSmrgBool
420fc5a983dSmrgG80CreateOutputs(ScrnInfoPtr pScrn)
421fc5a983dSmrg{
422fc5a983dSmrg    G80Ptr pNv = G80PTR(pScrn);
423fc5a983dSmrg    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
424fc5a983dSmrg    int i;
425fc5a983dSmrg
426fc5a983dSmrg    if(!G80ReadPortMapping(pScrn->scrnIndex, pNv))
427fc5a983dSmrg        return FALSE;
428fc5a983dSmrg
429fc5a983dSmrg    /* For each DDC port, create an output for the attached ORs */
430fc5a983dSmrg    for(i = 0; i < G80_NUM_I2C_PORTS; i++) {
431fc5a983dSmrg        xf86OutputPtr dac = NULL, sor = NULL;
432fc5a983dSmrg        I2CBusPtr i2c;
433fc5a983dSmrg        char i2cName[16];
434fc5a983dSmrg
435fc5a983dSmrg        if(pNv->i2cMap[i].dac == -1 && pNv->i2cMap[i].sor == -1)
436fc5a983dSmrg            /* No outputs on this port */
437fc5a983dSmrg            continue;
438fc5a983dSmrg
439fc5a983dSmrg        snprintf(i2cName, sizeof(i2cName), "I2C%i", i);
440fc5a983dSmrg        i2c = G80I2CInit(pScrn, i2cName, i);
441fc5a983dSmrg        if(!i2c) {
442fc5a983dSmrg            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
443fc5a983dSmrg                       "Failed to initialize I2C for port %i.\n",
444fc5a983dSmrg                       i);
445fc5a983dSmrg            continue;
446fc5a983dSmrg        }
447fc5a983dSmrg
448fc5a983dSmrg        if(pNv->i2cMap[i].dac != -1)
449fc5a983dSmrg            dac = G80CreateDac(pScrn, pNv->i2cMap[i].dac);
450fc5a983dSmrg        if(pNv->i2cMap[i].sor != -1)
451fc5a983dSmrg            sor = G80CreateSor(pScrn, pNv->i2cMap[i].sor, TMDS);
452fc5a983dSmrg
453fc5a983dSmrg        if(dac) {
454fc5a983dSmrg            G80OutputPrivPtr pPriv = dac->driver_private;
455fc5a983dSmrg
456fc5a983dSmrg            pPriv->partner = sor;
457fc5a983dSmrg            pPriv->i2c = i2c;
458fc5a983dSmrg            pPriv->scale = G80_SCALE_OFF;
459fc5a983dSmrg        }
460fc5a983dSmrg        if(sor) {
461fc5a983dSmrg            G80OutputPrivPtr pPriv = sor->driver_private;
462fc5a983dSmrg
463fc5a983dSmrg            pPriv->partner = dac;
464fc5a983dSmrg            pPriv->i2c = i2c;
465fc5a983dSmrg            pPriv->scale = G80_SCALE_ASPECT;
466fc5a983dSmrg        }
467fc5a983dSmrg    }
468fc5a983dSmrg
469fc5a983dSmrg    if(pNv->lvds.present) {
470fc5a983dSmrg        xf86OutputPtr lvds = G80CreateSor(pScrn, pNv->lvds.or, LVDS);
471fc5a983dSmrg        G80OutputPrivPtr pPriv = lvds->driver_private;
472fc5a983dSmrg
473fc5a983dSmrg        pPriv->scale = G80_SCALE_ASPECT;
474fc5a983dSmrg
475fc5a983dSmrg        if(pNv->lvds.i2cPort != -1) {
476fc5a983dSmrg            char i2cName[16];
477fc5a983dSmrg
478fc5a983dSmrg            snprintf(i2cName, sizeof(i2cName), "I2C%i (LVDS)", pNv->lvds.i2cPort);
479fc5a983dSmrg            pPriv->i2c = G80I2CInit(pScrn, i2cName, pNv->lvds.i2cPort);
480fc5a983dSmrg            if(!pPriv->i2c) {
481fc5a983dSmrg                xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
482fc5a983dSmrg                           "Failed to initialize I2C for port %i (LVDS)!\n",
483fc5a983dSmrg                           pNv->lvds.i2cPort);
484fc5a983dSmrg            }
485fc5a983dSmrg        }
486fc5a983dSmrg    }
487fc5a983dSmrg
488fc5a983dSmrg    /* For each output, set the crtc and clone masks */
489fc5a983dSmrg    for(i = 0; i < xf86_config->num_output; i++) {
490fc5a983dSmrg        xf86OutputPtr output = xf86_config->output[i];
491fc5a983dSmrg
492fc5a983dSmrg        /* Any output can connect to any head */
493fc5a983dSmrg        output->possible_crtcs = 0x3;
494fc5a983dSmrg        output->possible_clones = 0;
495fc5a983dSmrg    }
496fc5a983dSmrg
497fc5a983dSmrg    return TRUE;
498fc5a983dSmrg}
499