G80: LVDS support.

This commit is contained in:
Aaron Plattner
2007-06-04 23:15:42 -07:00
parent 70b304cd0d
commit 6b71721439
4 changed files with 144 additions and 15 deletions

View File

@@ -85,6 +85,7 @@ static Bool G80ReadPortMapping(int scrnIndex, G80Ptr pNv)
"for port %i\n",
or, pNv->i2cMap[port].sor, port);
pNv->i2cMap[port].sor = or;
pNv->i2cMap[port].panelType = (type == 2) ? TMDS : LVDS;
break;
}
}
@@ -317,7 +318,8 @@ G80CreateOutputs(ScrnInfoPtr pScrn)
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);
sor = G80CreateSor(pScrn, pNv->i2cMap[i].sor,
pNv->i2cMap[i].panelType);
if(dac) {
G80OutputPrivPtr pPriv = dac->driver_private;

View File

@@ -1,6 +1,8 @@
typedef struct G80OutputPrivRec {
ORType type;
ORNum or;
PanelType panelType;
DisplayModePtr nativeMode;
xf86OutputPtr partner;
I2CBusPtr i2c;
@@ -26,4 +28,4 @@ xf86OutputPtr G80CreateDac(ScrnInfoPtr, ORNum);
Bool G80DacLoadDetect(xf86OutputPtr);
/* g80_sor.c */
xf86OutputPtr G80CreateSor(ScrnInfoPtr, ORNum);
xf86OutputPtr G80CreateSor(ScrnInfoPtr, ORNum, PanelType);

View File

@@ -38,8 +38,9 @@ G80SorSetPClk(xf86OutputPtr output, int pclk)
G80Ptr pNv = G80PTR(output->scrn);
G80OutputPrivPtr pPriv = output->driver_private;
const int orOff = 0x800 * pPriv->or;
const int limit = pPriv->panelType == LVDS ? 112000 : 165000;
pNv->reg[(0x00614300+orOff)/4] = (pclk > 165000) ? 0x101 : 0;
pNv->reg[(0x00614300+orOff)/4] = (pclk > limit) ? 0x101 : 0;
}
static void
@@ -61,6 +62,7 @@ G80SorDPMSSet(xf86OutputPtr output, int mode)
tmp &= ~1;
pNv->reg[(0x0061C004+off)/4] = tmp;
while((pNv->reg[(0x61C030+off)/4] & 0x10000000));
}
static void
@@ -70,6 +72,7 @@ G80SorModeSet(xf86OutputPtr output, DisplayModePtr mode,
ScrnInfoPtr pScrn = output->scrn;
G80OutputPrivPtr pPriv = output->driver_private;
const int sorOff = 0x40 * pPriv->or;
CARD32 type;
if(!adjusted_mode) {
/* Disconnect the SOR */
@@ -77,6 +80,13 @@ G80SorModeSet(xf86OutputPtr output, DisplayModePtr mode,
return;
}
if(pPriv->panelType == LVDS)
type = 0;
else if(adjusted_mode->Clock > 165000)
type = 0x500;
else
type = 0x100;
// This wouldn't be necessary, but the server is stupid and calls
// G80SorDPMSSet after the output is disconnected, even though the hardware
// turns it off automatically.
@@ -84,7 +94,7 @@ G80SorModeSet(xf86OutputPtr output, DisplayModePtr mode,
C(0x00000600 + sorOff,
(G80CrtcGetHead(output->crtc) == HEAD0 ? 1 : 2) |
(adjusted_mode->Clock > 165000 ? 0x500 : 0x100) |
type |
((adjusted_mode->Flags & V_NHSYNC) ? 0x1000 : 0) |
((adjusted_mode->Flags & V_NVSYNC) ? 0x2000 : 0));
}
@@ -92,7 +102,6 @@ G80SorModeSet(xf86OutputPtr output, DisplayModePtr mode,
static xf86OutputStatus
G80SorDetect(xf86OutputPtr output)
{
G80OutputPrivPtr pPriv = output->driver_private;
/* Assume physical status isn't going to change before the BlockHandler */
@@ -103,16 +112,71 @@ G80SorDetect(xf86OutputPtr output)
return pPriv->cached_status;
}
static xf86OutputStatus
G80SorLVDSDetect(xf86OutputPtr output)
{
/* Assume LVDS is always connected */
return XF86OutputStatusConnected;
}
static void
G80SorDestroy(xf86OutputPtr output)
{
G80OutputPrivPtr pPriv = output->driver_private;
G80OutputDestroy(output);
if(pPriv->nativeMode) {
if(pPriv->nativeMode->name)
xfree(pPriv->nativeMode->name);
xfree(pPriv->nativeMode);
}
xfree(output->driver_private);
output->driver_private = NULL;
}
static const xf86OutputFuncsRec G80SorOutputFuncs = {
/******************** LVDS ********************/
static Bool
G80SorModeFixupScale(xf86OutputPtr output, DisplayModePtr mode,
DisplayModePtr adjusted_mode)
{
G80OutputPrivPtr pPriv = output->driver_private;
DisplayModePtr native = pPriv->nativeMode;
// Stash the saved mode timings in adjusted_mode
adjusted_mode->Clock = native->Clock;
adjusted_mode->Flags = native->Flags;
adjusted_mode->CrtcHDisplay = native->CrtcHDisplay;
adjusted_mode->CrtcHBlankStart = native->CrtcHBlankStart;
adjusted_mode->CrtcHSyncStart = native->CrtcHSyncStart;
adjusted_mode->CrtcHSyncEnd = native->CrtcHSyncEnd;
adjusted_mode->CrtcHBlankEnd = native->CrtcHBlankEnd;
adjusted_mode->CrtcHTotal = native->CrtcHTotal;
adjusted_mode->CrtcHSkew = native->CrtcHSkew;
adjusted_mode->CrtcVDisplay = native->CrtcVDisplay;
adjusted_mode->CrtcVBlankStart = native->CrtcVBlankStart;
adjusted_mode->CrtcVSyncStart = native->CrtcVSyncStart;
adjusted_mode->CrtcVSyncEnd = native->CrtcVSyncEnd;
adjusted_mode->CrtcVBlankEnd = native->CrtcVBlankEnd;
adjusted_mode->CrtcVTotal = native->CrtcVTotal;
adjusted_mode->CrtcHAdjusted = native->CrtcHAdjusted;
adjusted_mode->CrtcVAdjusted = native->CrtcVAdjusted;
// This mode is already "fixed"
G80CrtcSkipModeFixup(output->crtc);
return TRUE;
}
static DisplayModePtr
G80SorGetLVDSModes(xf86OutputPtr output)
{
G80OutputPrivPtr pPriv = output->driver_private;
return xf86DuplicateMode(pPriv->nativeMode);
}
static const xf86OutputFuncsRec G80SorTMDSOutputFuncs = {
.dpms = G80SorDPMSSet,
.save = NULL,
.restore = NULL,
@@ -126,33 +190,88 @@ static const xf86OutputFuncsRec G80SorOutputFuncs = {
.destroy = G80SorDestroy,
};
static const xf86OutputFuncsRec G80SorLVDSOutputFuncs = {
.dpms = G80SorDPMSSet,
.save = NULL,
.restore = NULL,
.mode_valid = G80OutputModeValid,
.mode_fixup = G80SorModeFixupScale,
.prepare = G80OutputPrepare,
.commit = G80OutputCommit,
.mode_set = G80SorModeSet,
.detect = G80SorLVDSDetect,
.get_modes = G80SorGetLVDSModes,
.destroy = G80SorDestroy,
};
static DisplayModePtr
GetLVDSNativeMode(G80Ptr pNv)
{
DisplayModePtr mode = xnfcalloc(1, sizeof(DisplayModeRec));
const CARD32 size = pNv->reg[0x00610B4C/4];
const int width = size & 0x3fff;
const int height = (size >> 16) & 0x3fff;
mode->HDisplay = mode->CrtcHDisplay = width;
mode->VDisplay = mode->CrtcVDisplay = height;
mode->Clock = pNv->reg[0x610AD4/4] & 0x3fffff;
mode->CrtcHBlankStart = pNv->reg[0x610AFC/4];
mode->CrtcHSyncEnd = pNv->reg[0x610B04/4];
mode->CrtcHBlankEnd = pNv->reg[0x610AE8/4];
mode->CrtcHTotal = pNv->reg[0x610AF4/4];
mode->next = mode->prev = NULL;
mode->status = MODE_OK;
mode->type = M_T_DRIVER | M_T_PREFERRED;
xf86SetModeDefaultName(mode);
return mode;
}
xf86OutputPtr
G80CreateSor(ScrnInfoPtr pScrn, ORNum or)
G80CreateSor(ScrnInfoPtr pScrn, ORNum or, PanelType panelType)
{
G80Ptr pNv = G80PTR(pScrn);
G80OutputPrivPtr pPriv = xnfcalloc(sizeof(*pPriv), 1);
const int off = 0x800 * or;
xf86OutputPtr output;
char orName[5];
const xf86OutputFuncsRec *funcs;
if(!pPriv)
return FALSE;
pNv->reg[(0x61C00C+off)/4] = 0x03010700;
pNv->reg[(0x61C010+off)/4] = 0x0000152f;
pNv->reg[(0x61C014+off)/4] = 0x00000000;
pNv->reg[(0x61C018+off)/4] = 0x00245af8;
if(panelType == LVDS) {
strcpy(orName, "LVDS");
funcs = &G80SorLVDSOutputFuncs;
} else {
snprintf(orName, 5, "DVI%d", or);
pNv->reg[(0x61C00C+off)/4] = 0x03010700;
pNv->reg[(0x61C010+off)/4] = 0x0000152f;
pNv->reg[(0x61C014+off)/4] = 0x00000000;
pNv->reg[(0x61C018+off)/4] = 0x00245af8;
funcs = &G80SorTMDSOutputFuncs;
}
snprintf(orName, 5, "DVI%i", or);
output = xf86OutputCreate(pScrn, &G80SorOutputFuncs, orName);
output = xf86OutputCreate(pScrn, funcs, orName);
pPriv->type = SOR;
pPriv->or = or;
pPriv->panelType = panelType;
pPriv->cached_status = XF86OutputStatusUnknown;
pPriv->set_pclk = G80SorSetPClk;
output->driver_private = pPriv;
output->interlaceAllowed = TRUE;
output->doubleScanAllowed = TRUE;
if(panelType == LVDS) {
pPriv->nativeMode = GetLVDSNativeMode(pNv);
xf86DrvMsg(pScrn->scrnIndex, X_INFO, "%s native size %dx%d\n",
orName, pPriv->nativeMode->HDisplay,
pPriv->nativeMode->VDisplay);
}
return output;
}

View File

@@ -25,6 +25,11 @@ typedef enum ORNum {
SOR1 = 1
} ORNum;
typedef enum PanelType {
TMDS,
LVDS,
} PanelType;
typedef enum AccelMethod {
XAA,
EXA,
@@ -41,8 +46,9 @@ typedef struct G80Rec {
const unsigned char*table1;
int offscreenHeight;
struct {
ORNum dac;
ORNum sor;
ORNum dac;
ORNum sor;
PanelType panelType;
} i2cMap[4];
xf86Int10InfoPtr int10;