summaryrefslogtreecommitdiff
path: root/driver/xf86-video-nv/src/g80_output.c
diff options
context:
space:
mode:
Diffstat (limited to 'driver/xf86-video-nv/src/g80_output.c')
-rw-r--r--driver/xf86-video-nv/src/g80_output.c190
1 files changed, 150 insertions, 40 deletions
diff --git a/driver/xf86-video-nv/src/g80_output.c b/driver/xf86-video-nv/src/g80_output.c
index 1ec6a89af..4c8448fa6 100644
--- a/driver/xf86-video-nv/src/g80_output.c
+++ b/driver/xf86-video-nv/src/g80_output.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007 NVIDIA, Corporation
+ * Copyright (c) 2007-2008 NVIDIA, Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
@@ -32,16 +32,51 @@
#include "g80_display.h"
#include "g80_output.h"
+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;
+}
+
static Bool G80ReadPortMapping(int scrnIndex, G80Ptr pNv)
{
- unsigned char *table2;
- unsigned char headerSize, entries;
+ unsigned char *table2, *table3;
+ unsigned char headerSize, entries, table3Entries, table3EntSize;
int i;
CARD16 a;
CARD32 b;
/* Clear the i2c map to invalid */
- for(i = 0; i < 4; i++)
+ for(i = 0; i < G80_NUM_I2C_PORTS; i++)
pNv->i2cMap[i].dac = pNv->i2cMap[i].sor = -1;
if(*(CARD16*)pNv->table1 != 0xaa55) goto fail;
@@ -54,11 +89,16 @@ static Bool G80ReadPortMapping(int scrnIndex, G80Ptr pNv)
b = *(CARD32*)(table2 + 6);
if(b != 0x4edcbdcb) goto fail;
+ table3 = (unsigned char*)pNv->table1 + *(CARD16*)(table2 + 4);
+ table3Entries = table3[2];
+ table3EntSize = table3[3];
+ table3 += table3[1];
+
headerSize = table2[1];
entries = table2[2];
for(i = 0; i < entries; i++) {
- int type, port;
+ int type, port, portType;
ORNum or;
b = *(CARD32*)&table2[headerSize + 8*i];
@@ -66,50 +106,103 @@ static Bool G80ReadPortMapping(int scrnIndex, G80Ptr pNv)
port = (b >> 4) & 0xf;
or = ffs((b >> 24) & 0xf) - 1;
+ if(b & 0x300000)
+ /* Can't handle this type of output yet */
+ continue;
+
if(type == 0xe) break;
- if(type < 4) {
- switch(type) {
- case 0: /* CRT */
- 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;
+ switch(type) {
+ case 0: /* CRT */
+ if(port >= table3Entries) {
+ xf86DrvMsg(scrnIndex, X_WARNING,
+ "VGA%d: invalid port %d\n", or, port);
break;
- case 1: /* TV */
- /* Ignore TVs */
+ }
+ 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);
break;
-
- case 2: /* TMDS */
- 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;
+ }
+ 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);
break;
-
- case 3: /* LVDS */
- pNv->lvds.present = TRUE;
- pNv->lvds.or = or;
+ }
+ 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);
break;
- }
+ }
+ 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;
}
}
xf86DrvMsg(scrnIndex, X_PROBED, "Connector map:\n");
if(pNv->lvds.present)
xf86DrvMsg(scrnIndex, X_PROBED, " [N/A] -> SOR%i (LVDS)\n", pNv->lvds.or);
- for(i = 0; i < 4; i++) {
+ for(i = 0; i < G80_NUM_I2C_PORTS; i++) {
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);
}
+ pNv->loadVal = G80FindLoadVal(pNv->table1);
+ xf86DrvMsg(scrnIndex, X_PROBED, "Load detection: %d\n", pNv->loadVal);
+
return TRUE;
fail:
@@ -118,21 +211,24 @@ fail:
return FALSE;
}
+static CARD32 i2cAddr(const int port)
+{
+ const CARD32 base = (port > 3) ? 0x0000E1E0 : 0x0000E138;
+ return base + port * 0x18;
+}
+
static void G80_I2CPutBits(I2CBusPtr b, int clock, int data)
{
G80Ptr pNv = G80PTR(xf86Screens[b->scrnIndex]);
- const int off = b->DriverPrivate.val * 0x18;
-
- pNv->reg[(0x0000E138+off)/4] = 4 | clock | data << 1;
+ pNv->reg[i2cAddr(b->DriverPrivate.val)/4] = 4 | clock | data << 1;
}
static void G80_I2CGetBits(I2CBusPtr b, int *clock, int *data)
{
G80Ptr pNv = G80PTR(xf86Screens[b->scrnIndex]);
- const int off = b->DriverPrivate.val * 0x18;
unsigned char val;
- val = pNv->reg[(0x0000E138+off)/4];
+ val = pNv->reg[i2cAddr(b->DriverPrivate.val)/4];
*clock = !!(val & 1);
*data = !!(val & 2);
}
@@ -200,14 +296,15 @@ ProbeDDC(I2CBusPtr i2c)
ScrnInfoPtr pScrn = xf86Screens[i2c->scrnIndex];
G80Ptr pNv = G80PTR(pScrn);
xf86MonPtr monInfo = NULL;
- const int bus = i2c->DriverPrivate.val, off = bus * 0x18;
+ const int bus = i2c->DriverPrivate.val;
+ const CARD32 addr = i2cAddr(bus);
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"Probing for EDID on I2C bus %i...\n", bus);
- pNv->reg[(0x0000E138+off)/4] = 7;
+ pNv->reg[addr/4] = 7;
/* Should probably use xf86OutputGetEDID here */
monInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, i2c);
- pNv->reg[(0x0000E138+off)/4] = 3;
+ pNv->reg[addr/4] = 3;
if(monInfo) {
xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
@@ -304,7 +401,7 @@ G80CreateOutputs(ScrnInfoPtr pScrn)
return FALSE;
/* For each DDC port, create an output for the attached ORs */
- for(i = 0; i < 4; i++) {
+ for(i = 0; i < G80_NUM_I2C_PORTS; i++) {
xf86OutputPtr dac = NULL, sor = NULL;
I2CBusPtr i2c;
char i2cName[16];
@@ -348,6 +445,19 @@ G80CreateOutputs(ScrnInfoPtr pScrn)
G80OutputPrivPtr pPriv = lvds->driver_private;
pPriv->scale = G80_SCALE_ASPECT;
+
+ if(pNv->lvds.i2cPort != -1) {
+ I2CBusPtr i2c;
+ 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);
+ }
+ }
}
/* For each output, set the crtc and clone masks */