0,0 → 1,389 |
/* |
* Copyright 2007, 2008 Luc Verhaegen <lverhaegen@novell.com> |
* Copyright 2007, 2008 Matthias Hopf <mhopf@novell.com> |
* Copyright 2007, 2008 Egbert Eich <eich@novell.com> |
* Copyright 2007, 2008 Advanced Micro Devices, Inc. |
* |
* 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "xf86.h" |
|
/* for usleep */ |
#if HAVE_XF86_ANSIC_H |
# include "xf86_ansic.h" |
#else |
# include <unistd.h> |
#endif |
|
#include "rhd.h" |
#include "rhd_crtc.h" |
#include "rhd_connector.h" |
#include "rhd_output.h" |
#include "rhd_regs.h" |
#ifdef ATOM_BIOS |
#include "rhd_atombios.h" |
#endif |
|
struct DDIAPrivate |
{ |
Bool RunDualLink; |
CARD32 PcieCfgReg7; |
CARD32 CapabilityFlag; |
|
Bool Stored; |
|
CARD32 DdiaPathControl; |
CARD32 DdiaCntl; |
CARD32 DdiaDcbalancerControl; |
CARD32 DdiaPcieLinkControl2; |
CARD32 DdiaBitDepthControl; |
}; |
|
/* |
* |
*/ |
static ModeStatus |
DDIAModeValid(struct rhdOutput *Output, DisplayModePtr Mode) |
{ |
RHDFUNC(Output); |
|
if (Mode->Flags & V_INTERLACE) |
return MODE_NO_INTERLACE; |
|
if (Mode->Clock < 25000) |
return MODE_CLOCK_LOW; |
|
if (Output->Connector->Type == RHD_CONNECTOR_DVI_SINGLE) { |
if (Mode->Clock > 165000) |
return MODE_CLOCK_HIGH; |
} else if (Output->Connector->Type == RHD_CONNECTOR_DVI) { |
if (Mode->Clock > 330000) /* could go higher still */ |
return MODE_CLOCK_HIGH; |
} |
|
return MODE_OK; |
} |
|
/* |
* |
*/ |
static void |
DDIAMode(struct rhdOutput *Output, DisplayModePtr Mode) |
{ |
struct DDIAPrivate *Private = (struct DDIAPrivate *)Output->Private; |
CARD32 mux0, mux1, mux2, mux3; |
Bool LaneReversal; |
RHDPtr rhdPtr = RHDPTRI(Output); |
|
RHDFUNC(Output); |
|
if (Mode->SynthClock >= 165000) |
Private->RunDualLink = TRUE; |
else |
Private->RunDualLink = FALSE; |
|
/* reset on - will be enabled at POWER_ON */ |
RHDRegMask(Output, RS69_DDIA_PATH_CONTROL, RS69_DDIA_PIXVLD_RESET, RS69_DDIA_PIXVLD_RESET); |
/* RGB 4:4:4 */ |
RHDRegMask(Output, RS69_DDIA_CNTL, 0, RS69_DDIA_PIXEL_ENCODING); |
/* TMDS_AC */ |
RHDRegMask(Output, RS69_DDIA_PATH_CONTROL, |
2 << RS69_DDIA_PATH_SELECT_SHIFT, |
0x3 << RS69_DDIA_PATH_SELECT_SHIFT); |
/* dual link */ |
RHDRegMask(Output, RS69_DDIA_CNTL, Private->RunDualLink ? |
RS69_DDIA_DUAL_LINK_ENABLE : 0, RS69_DDIA_DUAL_LINK_ENABLE); |
RHDRegMask(Output, RS69_DDIA_DCBALANCER_CONTROL, |
RS69_DDIA_DCBALANCER_EN, |
RS69_DDIA_SYNC_DCBAL_EN_MASK | RS69_DDIA_DCBALANCER_EN); |
|
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL2, 0x0, 0x80); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL2, 0x0, 0x100); |
|
mux0 = Private->PcieCfgReg7 & 0x3; |
mux1 = (Private->PcieCfgReg7 >> 2) & 0x3; |
mux2 = (Private->PcieCfgReg7 >> 4) & 0x3; |
mux3 = (Private->PcieCfgReg7 >> 6) & 0x3; |
|
RHDRegMask(Output, RS69_DDIA_PCIE_LINK_CONTROL2, |
(mux0 << RS69_DDIA_PCIE_OUTPUT_MUX_SEL0) |
| (mux1 << RS69_DDIA_PCIE_OUTPUT_MUX_SEL1) |
| (mux2 << RS69_DDIA_PCIE_OUTPUT_MUX_SEL2) |
| (mux3 << RS69_DDIA_PCIE_OUTPUT_MUX_SEL3), |
(3 << RS69_DDIA_PCIE_OUTPUT_MUX_SEL0) |
| (3 << RS69_DDIA_PCIE_OUTPUT_MUX_SEL1) |
| (3 << RS69_DDIA_PCIE_OUTPUT_MUX_SEL2) |
| (3 << RS69_DDIA_PCIE_OUTPUT_MUX_SEL3) |
); |
LaneReversal = Private->PcieCfgReg7 & (0x1 << 10); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL2, 0x0, 0x3); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL2, 0x2, 0x2); |
|
RHDRegMask(Output, RS69_DDIA_PCIE_LINK_CONTROL3, |
LaneReversal ? RS69_DDIA_PCIE_MIRROR_EN : 0, |
RS69_DDIA_PCIE_MIRROR_EN); |
|
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL2, 0x70, 0x70); |
|
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0, 0x10); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0, 0x60); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0, 0x4000000); |
|
switch (rhdPtr->PciDeviceID) { |
case 0x791E: |
if (Mode->SynthClock <= 25000) { |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x2780, 0x3f80); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x0, 0xc000); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x039f0000, 0x03000000 | 0x039f0000); |
} else if (Mode->SynthClock <= 60000) { |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x2780, 0x3f80); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x0, 0xc000); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x024f0000, 0x03000000 | 0x024f0000); |
} else { |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x0980, 0x3f80); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x0, 0xc000); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x01270000, 0x03000000 | 0x01270000); |
} |
break; |
case 0x791F: |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x0980, 0x3f80); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x4000, 0xc000); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x00ac0000, 0x03000000 | 0x00ac0000); |
if (Private->CapabilityFlag & 0x10) { |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x0, 0xc000); |
if (Mode->SynthClock <= 6500) |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x01ac0000, 0x03ff0000); |
else |
RHDRegMaskD(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x01110000, 0x03ff0000); |
} |
break; |
} |
usleep (1); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x04000000, 0x04000000); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x60, 0x60); |
usleep(30); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x01, 0x01); |
usleep(1); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x02, 0x02); |
usleep(1); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x04, 0x04); |
usleep(1); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x08, 0x08); |
usleep(1); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x10, 0x10); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x0, 0xf); |
|
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL2, 0x0180, 0x0180); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL2, 0x600, 0x600); |
usleep(5); |
RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL2, 0x0, 0x600); |
|
/* hw reset will be turned off at POWER_ON */ |
|
/* select crtc source, sync_a, no stereosync */ |
RHDRegMask(Output, RS69_DDIA_SOURCE_SELECT, Output->Crtc->Id, |
RS69_DDIA_SOURCE_SELECT_BIT |
| RS69_DDIA_SYNC_SELECT |
| RS69_DDIA_STEREOSYNC_SELECT); |
} |
|
/* |
* |
*/ |
static void |
DDIAPower(struct rhdOutput *Output, int Power) |
{ |
RHDDebug(Output->scrnIndex, "%s(%s,%s)\n",__func__,Output->Name, |
rhdPowerString[Power]); |
|
switch (Power) { |
case RHD_POWER_ON: |
RHDRegMask(Output, RS69_DDIA_PATH_CONTROL, RS69_DDIA_PIXVLD_RESET, |
RS69_DDIA_PIXVLD_RESET); |
RHDRegWrite(Output, RS69_DDIA_BIT_DEPTH_CONTROL, 0); |
RHDRegMask(Output, RS69_DDIA_BIT_DEPTH_CONTROL, |
RS69_DDIA_TEMPORAL_DITHER_RESET, RS69_DDIA_TEMPORAL_DITHER_RESET); |
RHDRegMask(Output, RS69_DDIA_BIT_DEPTH_CONTROL, |
0, RS69_DDIA_TEMPORAL_DITHER_RESET); |
RHDRegMask(Output, RS69_DDIA_CNTL, RS69_DDIA_ENABLE, RS69_DDIA_ENABLE); |
RHDRegMask(Output, RS69_DDIA_PATH_CONTROL, 0, RS69_DDIA_PIXVLD_RESET); |
return; |
case RHD_POWER_RESET: |
RHDRegMask(Output, RS69_DDIA_CNTL, 0, RS69_DDIA_ENABLE); |
return; |
case RHD_POWER_SHUTDOWN: |
RHDRegMask(Output, RS69_DDIA_BIT_DEPTH_CONTROL, |
RS69_DDIA_TEMPORAL_DITHER_RESET, RS69_DDIA_TEMPORAL_DITHER_RESET); |
RHDRegMask(Output, RS69_DDIA_BIT_DEPTH_CONTROL, |
0, RS69_DDIA_TEMPORAL_DITHER_RESET); |
RHDRegMask(Output, RS69_DDIA_BIT_DEPTH_CONTROL, |
0, |
RS69_DDIA_TRUNCATE_EN |
| RS69_DDIA_TRUNCATE_DEPTH |
| RS69_DDIA_SPATIAL_DITHER_EN |
| RS69_DDIA_SPATIAL_DITHER_DEPTH); |
RHDRegMask(Output, RS69_DDIA_BIT_DEPTH_CONTROL, |
0, |
RS69_DDIA_TEMPORAL_DITHER_EN |
| RS69_DDIA_TEMPORAL_DITHER_EN |
| RS69_DDIA_TEMPORAL_DITHER_DEPTH |
| RS69_DDIA_TEMPORAL_LEVEL); |
RHDRegMask(Output, RS69_DDIA_CNTL, 0, RS69_DDIA_ENABLE); |
return; |
default: |
return; |
} |
} |
|
/* |
* |
*/ |
static void |
DDIASave(struct rhdOutput *Output) |
{ |
struct DDIAPrivate *Private = (struct DDIAPrivate *)Output->Private; |
|
RHDFUNC(Output); |
|
Private->DdiaPathControl = RHDRegRead(Output, RS69_DDIA_PATH_CONTROL); |
Private->DdiaCntl = RHDRegRead(Output, RS69_DDIA_CNTL); |
Private->DdiaDcbalancerControl = RHDRegRead(Output, RS69_DDIA_DCBALANCER_CONTROL); |
Private->DdiaPcieLinkControl2 = RHDRegRead(Output, RS69_DDIA_PCIE_LINK_CONTROL2); |
Private->DdiaBitDepthControl = RHDRegRead(Output, RS69_DDIA_BIT_DEPTH_CONTROL); |
|
Private->Stored = TRUE; |
} |
|
/* |
* |
*/ |
static void |
DDIARestore(struct rhdOutput *Output) |
{ |
struct DDIAPrivate *Private = (struct DDIAPrivate *)Output->Private; |
RHDFUNC(Output); |
|
if (!Private->Stored) |
return; |
|
/* disalbe */ |
RHDRegMask(Output, RS69_DDIA_CNTL, 0, RS69_DDIA_ENABLE); |
/* reset on */ |
RHDRegMask(Output, RS69_DDIA_PATH_CONTROL, RS69_DDIA_PIXVLD_RESET, RS69_DDIA_PIXVLD_RESET); |
RHDRegWrite(Output, RS69_DDIA_PATH_CONTROL, Private->DdiaPathControl | RS69_DDIA_PIXVLD_RESET); |
|
RHDRegWrite(Output, RS69_DDIA_BIT_DEPTH_CONTROL, Private->DdiaBitDepthControl); |
/* temporal dither reset on */ |
RHDRegWrite(Output, RS69_DDIA_BIT_DEPTH_CONTROL, Private->DdiaBitDepthControl |
| RS69_DDIA_TEMPORAL_DITHER_RESET); |
/* temporal dither reset off */ |
RHDRegWrite(Output, RS69_DDIA_BIT_DEPTH_CONTROL, Private->DdiaBitDepthControl); |
|
RHDRegWrite(Output, RS69_DDIA_DCBALANCER_CONTROL, Private->DdiaDcbalancerControl); |
RHDRegWrite(Output, RS69_DDIA_PCIE_LINK_CONTROL2, Private->DdiaPcieLinkControl2); |
/* enable if enabled at startup */ |
RHDRegWrite(Output, RS69_DDIA_CNTL, Private->DdiaCntl); |
/* reset off */ |
RHDRegWrite(Output, RS69_DDIA_PATH_CONTROL, Private->DdiaPathControl); |
} |
|
/* |
* |
*/ |
static void |
DDIADestroy(struct rhdOutput *Output) |
{ |
struct DDIAPrivate *Private = (struct DDIAPrivate *)Output->Private; |
|
RHDFUNC(Output); |
|
xfree(Private); |
Output->Private = NULL; |
} |
|
/* |
* |
*/ |
struct rhdOutput * |
RHDDDIAInit(RHDPtr rhdPtr) |
{ |
#ifdef ATOM_BIOS |
struct rhdOutput *Output; |
struct DDIAPrivate *Private; |
AtomBiosArgRec data; |
|
RHDFUNC(rhdPtr); |
|
/* |
* This needs to be handled separately |
* for now we only deal with it here. |
*/ |
if (rhdPtr->ChipSet < RHD_RS600 || rhdPtr->ChipSet >= RHD_RS740) |
return FALSE; |
|
Output = xnfcalloc(sizeof(struct rhdOutput), 1); |
|
Output->Name = "DDIA"; |
|
Output->scrnIndex = rhdPtr->scrnIndex; |
Output->Id = RHD_OUTPUT_DVO; |
|
Output->Sense = NULL; |
Output->ModeValid = DDIAModeValid; |
Output->Mode = DDIAMode; |
Output->Power = DDIAPower; |
Output->Save = DDIASave; |
Output->Restore = DDIARestore; |
Output->Destroy = DDIADestroy; |
|
Private = xnfcalloc(1, sizeof(struct DDIAPrivate)); |
Output->Private = Private; |
Private->Stored = FALSE; |
|
if (RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS, |
ATOM_GET_PCIENB_CFG_REG7, &data) == ATOM_SUCCESS) { |
Private->PcieCfgReg7 = data.val; |
} else { |
xf86DrvMsg(Output->scrnIndex, X_ERROR, "Retrieval of PCIE MUX values failed. " |
"no DDIA block support available\n"); |
goto error; |
} |
if (RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS, |
ATOM_GET_CAPABILITY_FLAG, &data) == ATOM_SUCCESS) { |
Private->CapabilityFlag = data.val; |
} else { |
xf86DrvMsg(Output->scrnIndex, X_ERROR, "Retrieval of Capability flag failed. " |
"no DDIA block support available\n"); |
goto error; |
} |
|
return Output; |
error: |
xfree(Private); |
return NULL; |
|
#else |
return NULL; |
#endif |
} |