390 lines
12 KiB
C
390 lines
12 KiB
C
|
/*
|
||
|
* 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
|
||
|
}
|