kolibrios-fun/programs/system/drivers/rhd/rhd_ddia.c
Sergey Semyonov (Serge) 4cc87fbe7d Radeon modesetting. Father of all "C" drivers.
git-svn-id: svn://kolibrios.org@955 a494cfbc-eb01-0410-851d-a64ba20cac60
2008-12-13 07:51:28 +00:00

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
}