/* * Copyright 2007, 2008 Luc Verhaegen * Copyright 2007, 2008 Matthias Hopf * Copyright 2007, 2008 Egbert Eich * 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 #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 }