diff options
Diffstat (limited to 'driver/xf86-video-nv/src/g80_output.c')
-rw-r--r-- | driver/xf86-video-nv/src/g80_output.c | 190 |
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 */ |