2007-09-30 08:17:11 -06:00
|
|
|
/*
|
2008-07-29 14:04:57 -06:00
|
|
|
* Copyright (c) 2007-2008 NVIDIA, Corporation
|
2007-09-30 08:17:11 -06:00
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
* copy of this software and associated documentation files (the
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
* the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included
|
|
|
|
* in all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
|
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
|
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
|
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <strings.h>
|
|
|
|
|
|
|
|
#include "g80_type.h"
|
|
|
|
#include "g80_display.h"
|
|
|
|
#include "g80_output.h"
|
|
|
|
|
2008-07-29 14:04:57 -06:00
|
|
|
static unsigned G80FindLoadVal(const unsigned char *table1)
|
|
|
|
{
|
|
|
|
const unsigned char *p = table1;
|
|
|
|
int count;
|
|
|
|
const CARD32 def = 340;
|
|
|
|
|
|
|
|
for(p = table1; *(CARD16*)p != 0xb8ff && p < table1 + 64000; p += 2);
|
|
|
|
if(p == table1 + 64000)
|
|
|
|
return def;
|
|
|
|
p += 2;
|
|
|
|
if(*(CARD32*)p != 0x544942)
|
|
|
|
return def;
|
|
|
|
p += 4;
|
|
|
|
if(*(CARD16*)p != 0x100)
|
|
|
|
return def;
|
|
|
|
p += 2;
|
|
|
|
if(*p != 12)
|
|
|
|
return def;
|
|
|
|
p++;
|
|
|
|
if(*p != 6)
|
|
|
|
return def;
|
|
|
|
p++;
|
|
|
|
count = *p;
|
|
|
|
p += 2;
|
|
|
|
for(; *p != 'A' && count >= 0; count--, p += 6);
|
|
|
|
if(count == -1)
|
|
|
|
return def;
|
|
|
|
p += 4;
|
|
|
|
p = table1 + *(CARD16*)p;
|
|
|
|
p = table1 + *(CARD16*)p;
|
|
|
|
if(p[0] != 0x10 || p[1] != 4 || p[2] != 4 || p[3] != 2)
|
|
|
|
return def;
|
|
|
|
return *(CARD32*)(p + 4) & 0x3ff;
|
|
|
|
}
|
|
|
|
|
2007-09-30 08:17:11 -06:00
|
|
|
static Bool G80ReadPortMapping(int scrnIndex, G80Ptr pNv)
|
|
|
|
{
|
2008-07-29 14:04:57 -06:00
|
|
|
unsigned char *table2, *table3;
|
|
|
|
unsigned char headerSize, entries, table3Entries, table3EntSize;
|
2007-09-30 08:17:11 -06:00
|
|
|
int i;
|
|
|
|
CARD16 a;
|
|
|
|
CARD32 b;
|
|
|
|
|
|
|
|
/* Clear the i2c map to invalid */
|
2008-07-29 14:04:57 -06:00
|
|
|
for(i = 0; i < G80_NUM_I2C_PORTS; i++)
|
2007-09-30 08:17:11 -06:00
|
|
|
pNv->i2cMap[i].dac = pNv->i2cMap[i].sor = -1;
|
|
|
|
|
|
|
|
if(*(CARD16*)pNv->table1 != 0xaa55) goto fail;
|
|
|
|
|
|
|
|
a = *(CARD16*)(pNv->table1 + 0x36);
|
|
|
|
table2 = (unsigned char*)pNv->table1 + a;
|
|
|
|
|
|
|
|
if(table2[0] != 0x40) goto fail;
|
|
|
|
|
|
|
|
b = *(CARD32*)(table2 + 6);
|
|
|
|
if(b != 0x4edcbdcb) goto fail;
|
|
|
|
|
2008-07-29 14:04:57 -06:00
|
|
|
table3 = (unsigned char*)pNv->table1 + *(CARD16*)(table2 + 4);
|
|
|
|
table3Entries = table3[2];
|
|
|
|
table3EntSize = table3[3];
|
|
|
|
table3 += table3[1];
|
|
|
|
|
2007-09-30 08:17:11 -06:00
|
|
|
headerSize = table2[1];
|
|
|
|
entries = table2[2];
|
|
|
|
|
|
|
|
for(i = 0; i < entries; i++) {
|
2008-07-29 14:04:57 -06:00
|
|
|
int type, port, portType;
|
2007-09-30 08:17:11 -06:00
|
|
|
ORNum or;
|
|
|
|
|
|
|
|
b = *(CARD32*)&table2[headerSize + 8*i];
|
|
|
|
type = b & 0xf;
|
|
|
|
port = (b >> 4) & 0xf;
|
|
|
|
or = ffs((b >> 24) & 0xf) - 1;
|
|
|
|
|
2008-07-29 14:04:57 -06:00
|
|
|
if(b & 0x300000)
|
|
|
|
/* Can't handle this type of output yet */
|
|
|
|
continue;
|
|
|
|
|
2007-09-30 08:17:11 -06:00
|
|
|
if(type == 0xe) break;
|
|
|
|
|
2008-07-29 14:04:57 -06:00
|
|
|
switch(type) {
|
|
|
|
case 0: /* CRT */
|
|
|
|
if(port >= table3Entries) {
|
|
|
|
xf86DrvMsg(scrnIndex, X_WARNING,
|
|
|
|
"VGA%d: invalid port %d\n", or, port);
|
2007-09-30 08:17:11 -06:00
|
|
|
break;
|
2008-07-29 14:04:57 -06:00
|
|
|
}
|
|
|
|
b = *(CARD32*)&table3[table3EntSize * port];
|
|
|
|
port = b & 0xff;
|
|
|
|
portType = b >> 24;
|
|
|
|
if(portType != 5) {
|
|
|
|
xf86DrvMsg(scrnIndex, X_WARNING,
|
|
|
|
"VGA%d: invalid port type %d\n", or, portType);
|
2007-09-30 08:17:11 -06:00
|
|
|
break;
|
2008-07-29 14:04:57 -06:00
|
|
|
}
|
|
|
|
if(pNv->i2cMap[port].dac != -1) {
|
|
|
|
xf86DrvMsg(scrnIndex, X_WARNING,
|
|
|
|
"DDC routing table corrupt! DAC %i -> %i for "
|
|
|
|
"port %i\n", or, pNv->i2cMap[port].dac, port);
|
|
|
|
}
|
|
|
|
pNv->i2cMap[port].dac = or;
|
|
|
|
break;
|
|
|
|
case 1: /* TV */
|
|
|
|
/* Ignore TVs */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: /* TMDS */
|
|
|
|
if(port >= table3Entries) {
|
|
|
|
xf86DrvMsg(scrnIndex, X_WARNING,
|
|
|
|
"DVI%d: invalid port %d\n", or, port);
|
2007-09-30 08:17:11 -06:00
|
|
|
break;
|
2008-07-29 14:04:57 -06:00
|
|
|
}
|
|
|
|
b = *(CARD32*)&table3[table3EntSize * port];
|
|
|
|
port = b & 0xff;
|
|
|
|
portType = b >> 24;
|
|
|
|
if(portType != 5) {
|
|
|
|
xf86DrvMsg(scrnIndex, X_WARNING,
|
|
|
|
"DVI%d: invalid port type %d\n", or, portType);
|
2007-09-30 08:17:11 -06:00
|
|
|
break;
|
2008-07-29 14:04:57 -06:00
|
|
|
}
|
|
|
|
if(pNv->i2cMap[port].sor != -1)
|
|
|
|
xf86DrvMsg(scrnIndex, X_WARNING,
|
|
|
|
"DDC routing table corrupt! SOR %i -> %i for "
|
|
|
|
"port %i\n", or, pNv->i2cMap[port].sor, port);
|
|
|
|
pNv->i2cMap[port].sor = or;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: /* LVDS */
|
|
|
|
pNv->lvds.present = TRUE;
|
|
|
|
pNv->lvds.or = or;
|
|
|
|
pNv->lvds.i2cPort = -1;
|
|
|
|
|
|
|
|
if(port == 15) {
|
|
|
|
xf86DrvMsg(scrnIndex, X_INFO, "LVDS has no I2C port\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(port >= table3Entries) {
|
|
|
|
xf86DrvMsg(scrnIndex, X_WARNING,
|
|
|
|
"LVDS: invalid port %d\n", port);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
b = *(CARD32*)&table3[table3EntSize * port];
|
|
|
|
port = b & 0xff;
|
|
|
|
portType = b >> 24;
|
|
|
|
if(portType != 5) {
|
|
|
|
xf86DrvMsg(scrnIndex, X_WARNING,
|
|
|
|
"LVDS: invalid port type %d\n", portType);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pNv->lvds.i2cPort = port;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2007-09-30 08:17:11 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
xf86DrvMsg(scrnIndex, X_PROBED, "Connector map:\n");
|
2008-11-03 13:35:47 -07:00
|
|
|
if(pNv->lvds.present) {
|
|
|
|
if (pNv->lvds.i2cPort != -1)
|
|
|
|
xf86DrvMsg(scrnIndex, X_PROBED, " Bus %i -> SOR%i (LVDS)\n", pNv->lvds.i2cPort, pNv->lvds.or);
|
|
|
|
else
|
|
|
|
xf86DrvMsg(scrnIndex, X_PROBED, " [N/A] -> SOR%i (LVDS)\n", pNv->lvds.or);
|
|
|
|
}
|
2008-07-29 14:04:57 -06:00
|
|
|
for(i = 0; i < G80_NUM_I2C_PORTS; i++) {
|
2007-09-30 08:17:11 -06:00
|
|
|
if(pNv->i2cMap[i].dac != -1)
|
|
|
|
xf86DrvMsg(scrnIndex, X_PROBED, " Bus %i -> DAC%i\n", i, pNv->i2cMap[i].dac);
|
|
|
|
if(pNv->i2cMap[i].sor != -1)
|
|
|
|
xf86DrvMsg(scrnIndex, X_PROBED, " Bus %i -> SOR%i\n", i, pNv->i2cMap[i].sor);
|
|
|
|
}
|
|
|
|
|
2008-07-29 14:04:57 -06:00
|
|
|
pNv->loadVal = G80FindLoadVal(pNv->table1);
|
|
|
|
xf86DrvMsg(scrnIndex, X_PROBED, "Load detection: %d\n", pNv->loadVal);
|
|
|
|
|
2007-09-30 08:17:11 -06:00
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
xf86DrvMsg(scrnIndex, X_ERROR, "Couldn't find the DDC routing table. "
|
|
|
|
"Mode setting will probably fail!\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2008-07-29 14:04:57 -06:00
|
|
|
static CARD32 i2cAddr(const int port)
|
|
|
|
{
|
|
|
|
const CARD32 base = (port > 3) ? 0x0000E1E0 : 0x0000E138;
|
|
|
|
return base + port * 0x18;
|
|
|
|
}
|
|
|
|
|
2007-09-30 08:17:11 -06:00
|
|
|
static void G80_I2CPutBits(I2CBusPtr b, int clock, int data)
|
|
|
|
{
|
|
|
|
G80Ptr pNv = G80PTR(xf86Screens[b->scrnIndex]);
|
2008-07-29 14:04:57 -06:00
|
|
|
pNv->reg[i2cAddr(b->DriverPrivate.val)/4] = 4 | clock | data << 1;
|
2007-09-30 08:17:11 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void G80_I2CGetBits(I2CBusPtr b, int *clock, int *data)
|
|
|
|
{
|
|
|
|
G80Ptr pNv = G80PTR(xf86Screens[b->scrnIndex]);
|
|
|
|
unsigned char val;
|
|
|
|
|
2008-07-29 14:04:57 -06:00
|
|
|
val = pNv->reg[i2cAddr(b->DriverPrivate.val)/4];
|
2007-09-30 08:17:11 -06:00
|
|
|
*clock = !!(val & 1);
|
|
|
|
*data = !!(val & 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static I2CBusPtr
|
|
|
|
G80I2CInit(ScrnInfoPtr pScrn, const char *name, const int port)
|
|
|
|
{
|
|
|
|
I2CBusPtr i2c;
|
|
|
|
|
|
|
|
/* Allocate the I2C bus structure */
|
|
|
|
i2c = xf86CreateI2CBusRec();
|
|
|
|
if(!i2c) return NULL;
|
|
|
|
|
|
|
|
i2c->BusName = strdup(name);
|
|
|
|
i2c->scrnIndex = pScrn->scrnIndex;
|
|
|
|
i2c->I2CPutBits = G80_I2CPutBits;
|
|
|
|
i2c->I2CGetBits = G80_I2CGetBits;
|
|
|
|
i2c->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */
|
|
|
|
i2c->StartTimeout = 550;
|
|
|
|
i2c->BitTimeout = 40;
|
|
|
|
i2c->ByteTimeout = 40;
|
|
|
|
i2c->AcknTimeout = 40;
|
|
|
|
i2c->DriverPrivate.val = port;
|
|
|
|
|
|
|
|
if(xf86I2CBusInit(i2c)) {
|
|
|
|
return i2c;
|
|
|
|
} else {
|
|
|
|
xfree(i2c);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
G80OutputSetPClk(xf86OutputPtr output, int pclk)
|
|
|
|
{
|
|
|
|
G80OutputPrivPtr pPriv = output->driver_private;
|
|
|
|
if(pPriv->set_pclk)
|
|
|
|
pPriv->set_pclk(output, pclk);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
G80OutputModeValid(xf86OutputPtr output, DisplayModePtr mode)
|
|
|
|
{
|
|
|
|
if(mode->Clock > 400000)
|
|
|
|
return MODE_CLOCK_HIGH;
|
|
|
|
if(mode->Clock < 25000)
|
|
|
|
return MODE_CLOCK_LOW;
|
|
|
|
|
|
|
|
return MODE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
G80OutputPrepare(xf86OutputPtr output)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
G80OutputCommit(xf86OutputPtr output)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static xf86MonPtr
|
|
|
|
ProbeDDC(I2CBusPtr i2c)
|
|
|
|
{
|
|
|
|
ScrnInfoPtr pScrn = xf86Screens[i2c->scrnIndex];
|
|
|
|
G80Ptr pNv = G80PTR(pScrn);
|
|
|
|
xf86MonPtr monInfo = NULL;
|
2008-07-29 14:04:57 -06:00
|
|
|
const int bus = i2c->DriverPrivate.val;
|
|
|
|
const CARD32 addr = i2cAddr(bus);
|
2007-09-30 08:17:11 -06:00
|
|
|
|
|
|
|
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
|
|
|
|
"Probing for EDID on I2C bus %i...\n", bus);
|
2008-07-29 14:04:57 -06:00
|
|
|
pNv->reg[addr/4] = 7;
|
2007-09-30 08:17:11 -06:00
|
|
|
/* Should probably use xf86OutputGetEDID here */
|
2009-05-03 10:42:23 -06:00
|
|
|
#ifdef EDID_COMPLETE_RAWDATA
|
|
|
|
monInfo = xf86DoEEDID(pScrn->scrnIndex, i2c, TRUE);
|
|
|
|
#else
|
2007-09-30 08:17:11 -06:00
|
|
|
monInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, i2c);
|
2009-05-03 10:42:23 -06:00
|
|
|
#endif
|
2008-07-29 14:04:57 -06:00
|
|
|
pNv->reg[addr/4] = 3;
|
2007-09-30 08:17:11 -06:00
|
|
|
|
|
|
|
if(monInfo) {
|
|
|
|
xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
|
|
|
|
"DDC detected a %s:\n", monInfo->features.input_type ?
|
|
|
|
"DFP" : "CRT");
|
|
|
|
xf86PrintEDID(monInfo);
|
|
|
|
} else {
|
|
|
|
xf86DrvMsg(pScrn->scrnIndex, X_INFO, " ... none found\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return monInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read an EDID from the i2c port. Perform load detection on the DAC (if
|
|
|
|
* present) to see if the display is connected via VGA. Sets the cached status
|
|
|
|
* of both outputs. The status is marked dirty again in the BlockHandler.
|
|
|
|
*/
|
|
|
|
void G80OutputPartnersDetect(xf86OutputPtr dac, xf86OutputPtr sor, I2CBusPtr i2c)
|
|
|
|
{
|
|
|
|
xf86MonPtr monInfo = ProbeDDC(i2c);
|
|
|
|
xf86OutputPtr connected = NULL;
|
|
|
|
Bool load = dac && G80DacLoadDetect(dac);
|
|
|
|
|
|
|
|
if(dac) {
|
|
|
|
G80OutputPrivPtr pPriv = dac->driver_private;
|
|
|
|
|
|
|
|
if(load) {
|
|
|
|
pPriv->cached_status = XF86OutputStatusConnected;
|
|
|
|
connected = dac;
|
|
|
|
} else {
|
|
|
|
pPriv->cached_status = XF86OutputStatusDisconnected;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(sor) {
|
|
|
|
G80OutputPrivPtr pPriv = sor->driver_private;
|
|
|
|
|
|
|
|
if(monInfo && !load) {
|
|
|
|
pPriv->cached_status = XF86OutputStatusConnected;
|
|
|
|
connected = sor;
|
|
|
|
} else {
|
|
|
|
pPriv->cached_status = XF86OutputStatusDisconnected;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(connected)
|
|
|
|
xf86OutputSetEDID(connected, monInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the cached output status for all outputs. Called from G80BlockHandler.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
G80OutputResetCachedStatus(ScrnInfoPtr pScrn)
|
|
|
|
{
|
|
|
|
xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for(i = 0; i < xf86_config->num_output; i++) {
|
|
|
|
G80OutputPrivPtr pPriv = xf86_config->output[i]->driver_private;
|
|
|
|
pPriv->cached_status = XF86OutputStatusUnknown;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DisplayModePtr
|
|
|
|
G80OutputGetDDCModes(xf86OutputPtr output)
|
|
|
|
{
|
|
|
|
/* The EDID is read as part of the detect step */
|
|
|
|
output->funcs->detect(output);
|
|
|
|
return xf86OutputGetEDIDModes(output);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
G80OutputDestroy(xf86OutputPtr output)
|
|
|
|
{
|
|
|
|
G80OutputPrivPtr pPriv = output->driver_private;
|
|
|
|
|
|
|
|
if(pPriv->partner)
|
|
|
|
((G80OutputPrivPtr)pPriv->partner->driver_private)->partner = NULL;
|
|
|
|
else
|
|
|
|
xf86DestroyI2CBusRec(pPriv->i2c, TRUE, TRUE);
|
|
|
|
pPriv->i2c = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Bool
|
|
|
|
G80CreateOutputs(ScrnInfoPtr pScrn)
|
|
|
|
{
|
|
|
|
G80Ptr pNv = G80PTR(pScrn);
|
|
|
|
xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if(!G80ReadPortMapping(pScrn->scrnIndex, pNv))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
/* For each DDC port, create an output for the attached ORs */
|
2008-07-29 14:04:57 -06:00
|
|
|
for(i = 0; i < G80_NUM_I2C_PORTS; i++) {
|
2007-09-30 08:17:11 -06:00
|
|
|
xf86OutputPtr dac = NULL, sor = NULL;
|
|
|
|
I2CBusPtr i2c;
|
|
|
|
char i2cName[16];
|
|
|
|
|
|
|
|
if(pNv->i2cMap[i].dac == -1 && pNv->i2cMap[i].sor == -1)
|
|
|
|
/* No outputs on this port */
|
|
|
|
continue;
|
|
|
|
|
|
|
|
snprintf(i2cName, sizeof(i2cName), "I2C%i", i);
|
|
|
|
i2c = G80I2CInit(pScrn, i2cName, i);
|
|
|
|
if(!i2c) {
|
|
|
|
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
|
|
|
|
"Failed to initialize I2C for port %i.\n",
|
|
|
|
i);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pNv->i2cMap[i].dac != -1)
|
|
|
|
dac = G80CreateDac(pScrn, pNv->i2cMap[i].dac);
|
|
|
|
if(pNv->i2cMap[i].sor != -1)
|
|
|
|
sor = G80CreateSor(pScrn, pNv->i2cMap[i].sor, TMDS);
|
|
|
|
|
|
|
|
if(dac) {
|
|
|
|
G80OutputPrivPtr pPriv = dac->driver_private;
|
|
|
|
|
|
|
|
pPriv->partner = sor;
|
|
|
|
pPriv->i2c = i2c;
|
|
|
|
pPriv->scale = G80_SCALE_OFF;
|
|
|
|
}
|
|
|
|
if(sor) {
|
|
|
|
G80OutputPrivPtr pPriv = sor->driver_private;
|
|
|
|
|
|
|
|
pPriv->partner = dac;
|
|
|
|
pPriv->i2c = i2c;
|
|
|
|
pPriv->scale = G80_SCALE_ASPECT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pNv->lvds.present) {
|
|
|
|
xf86OutputPtr lvds = G80CreateSor(pScrn, pNv->lvds.or, LVDS);
|
|
|
|
G80OutputPrivPtr pPriv = lvds->driver_private;
|
|
|
|
|
|
|
|
pPriv->scale = G80_SCALE_ASPECT;
|
2008-07-29 14:04:57 -06:00
|
|
|
|
|
|
|
if(pNv->lvds.i2cPort != -1) {
|
|
|
|
char i2cName[16];
|
|
|
|
|
|
|
|
snprintf(i2cName, sizeof(i2cName), "I2C%i (LVDS)", pNv->lvds.i2cPort);
|
|
|
|
pPriv->i2c = G80I2CInit(pScrn, i2cName, pNv->lvds.i2cPort);
|
|
|
|
if(!pPriv->i2c) {
|
|
|
|
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
|
|
|
|
"Failed to initialize I2C for port %i (LVDS)!\n",
|
|
|
|
pNv->lvds.i2cPort);
|
|
|
|
}
|
|
|
|
}
|
2007-09-30 08:17:11 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/* For each output, set the crtc and clone masks */
|
|
|
|
for(i = 0; i < xf86_config->num_output; i++) {
|
|
|
|
xf86OutputPtr output = xf86_config->output[i];
|
|
|
|
|
|
|
|
/* Any output can connect to any head */
|
|
|
|
output->possible_crtcs = 0x3;
|
|
|
|
output->possible_clones = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|