mirror of
https://github.com/X11Libre/xf86-video-nv.git
synced 2026-03-24 01:24:21 +00:00
G80: LVDS support.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
141
src/g80_sor.c
141
src/g80_sor.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user