7c0a5de1e7
git-svn-id: svn://kolibrios.org@1407 a494cfbc-eb01-0410-851d-a64ba20cac60
1386 lines
42 KiB
C
1386 lines
42 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.
|
|
*/
|
|
|
|
/*
|
|
* Deals with the Shared LVDS/TMDS encoder.
|
|
*
|
|
*/
|
|
|
|
#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"
|
|
#include "rhd_hdmi.h"
|
|
#ifdef ATOM_BIOS
|
|
#include "rhd_atombios.h"
|
|
#include "rhd_atomout.h"
|
|
#endif
|
|
|
|
/*
|
|
* First of all, make it more managable to code for both R500 and R600, as
|
|
* there was a 1 register shift, right in the middle of the register block.
|
|
* There are of course much nicer ways to do the workaround i am doing here,
|
|
* but speed is not an issue here.
|
|
*/
|
|
static inline CARD16
|
|
LVTMAChipGenerationSelect(int ChipSet, CARD32 R500, CARD32 R600)
|
|
{
|
|
if (ChipSet >= RHD_RS600)
|
|
return R600;
|
|
else
|
|
return R500;
|
|
}
|
|
|
|
#define LVTMAGENSEL(r500, r600) LVTMAChipGenerationSelect(rhdPtr->ChipSet, (r500), (r600))
|
|
#define LVTMA_DATA_SYNCHRONIZATION \
|
|
LVTMAGENSEL(LVTMA_R500_DATA_SYNCHRONIZATION, LVTMA_R600_DATA_SYNCHRONIZATION)
|
|
#define LVTMA_PWRSEQ_REF_DIV \
|
|
LVTMAGENSEL(LVTMA_R500_PWRSEQ_REF_DIV, LVTMA_R600_PWRSEQ_REF_DIV)
|
|
#define LVTMA_PWRSEQ_DELAY1 \
|
|
LVTMAGENSEL(LVTMA_R500_PWRSEQ_DELAY1, LVTMA_R600_PWRSEQ_DELAY1)
|
|
#define LVTMA_PWRSEQ_DELAY2 \
|
|
LVTMAGENSEL(LVTMA_R500_PWRSEQ_DELAY2, LVTMA_R600_PWRSEQ_DELAY2)
|
|
#define LVTMA_PWRSEQ_CNTL \
|
|
LVTMAGENSEL(LVTMA_R500_PWRSEQ_CNTL, LVTMA_R600_PWRSEQ_CNTL)
|
|
#define LVTMA_PWRSEQ_STATE \
|
|
LVTMAGENSEL(LVTMA_R500_PWRSEQ_STATE, LVTMA_R600_PWRSEQ_STATE)
|
|
#define LVTMA_LVDS_DATA_CNTL \
|
|
LVTMAGENSEL(LVTMA_R500_LVDS_DATA_CNTL, LVTMA_R600_LVDS_DATA_CNTL)
|
|
#define LVTMA_MODE LVTMAGENSEL(LVTMA_R500_MODE, LVTMA_R600_MODE)
|
|
#define LVTMA_TRANSMITTER_ENABLE \
|
|
LVTMAGENSEL(LVTMA_R500_TRANSMITTER_ENABLE, LVTMA_R600_TRANSMITTER_ENABLE)
|
|
#define LVTMA_MACRO_CONTROL \
|
|
LVTMAGENSEL(LVTMA_R500_MACRO_CONTROL, LVTMA_R600_MACRO_CONTROL)
|
|
#define LVTMA_TRANSMITTER_CONTROL \
|
|
LVTMAGENSEL(LVTMA_R500_TRANSMITTER_CONTROL, LVTMA_R600_TRANSMITTER_CONTROL)
|
|
#define LVTMA_REG_TEST_OUTPUT \
|
|
LVTMAGENSEL(LVTMA_R500_REG_TEST_OUTPUT, LVTMA_R600_REG_TEST_OUTPUT)
|
|
#define LVTMA_BL_MOD_CNTL \
|
|
LVTMAGENSEL(LVTMA_R500_BL_MOD_CNTL, LVTMA_R600_BL_MOD_CNTL)
|
|
|
|
#define LVTMA_DITHER_RESET_BIT LVTMAGENSEL(0x04000000, 0x02000000)
|
|
|
|
/*
|
|
*
|
|
* Handling for LVTMA block as LVDS.
|
|
*
|
|
*/
|
|
|
|
struct LVDSPrivate {
|
|
Bool DualLink;
|
|
Bool LVDS24Bit;
|
|
Bool FPDI; /* LDI otherwise */
|
|
CARD16 TXClockPattern;
|
|
int BlLevel;
|
|
CARD32 MacroControl;
|
|
|
|
/* Power timing for LVDS */
|
|
CARD16 PowerRefDiv;
|
|
CARD16 BlonRefDiv;
|
|
CARD16 PowerDigToDE;
|
|
CARD16 PowerDEToBL;
|
|
CARD16 OffDelay;
|
|
Bool TemporalDither;
|
|
Bool SpatialDither;
|
|
int GreyLevel;
|
|
|
|
Bool Stored;
|
|
|
|
CARD32 StoreControl;
|
|
CARD32 StoreSourceSelect;
|
|
CARD32 StoreBitDepthControl;
|
|
CARD32 StoreDataSynchronisation;
|
|
CARD32 StorePWRSEQRefDiv;
|
|
CARD32 StorePWRSEQDelay1;
|
|
CARD32 StorePWRSEQDelay2;
|
|
CARD32 StorePWRSEQControl;
|
|
CARD32 StorePWRSEQState;
|
|
CARD32 StoreLVDSDataControl;
|
|
CARD32 StoreMode;
|
|
CARD32 StoreTxEnable;
|
|
CARD32 StoreMacroControl;
|
|
CARD32 StoreTXControl;
|
|
CARD32 StoreBlModCntl;
|
|
#ifdef NOT_YET
|
|
/* to hook in AtomBIOS property callback */
|
|
Bool (*WrappedPropertyCallback) (struct rhdOutput *Output,
|
|
enum rhdPropertyAction Action, enum rhdOutputProperty Property, union rhdPropertyData *val);
|
|
void *PropertyPrivate;
|
|
#endif
|
|
};
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static ModeStatus
|
|
LVDSModeValid(struct rhdOutput *Output, DisplayModePtr Mode)
|
|
{
|
|
RHDFUNC(Output);
|
|
|
|
if (Mode->Flags & V_INTERLACE)
|
|
return MODE_NO_INTERLACE;
|
|
|
|
return MODE_OK;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static void
|
|
LVDSDebugBacklight(struct rhdOutput *Output)
|
|
{
|
|
RHDPtr rhdPtr = RHDPTRI(Output);
|
|
CARD32 tmp;
|
|
Bool Blon, BlonOvrd, BlonPol, BlModEn;
|
|
int BlModLevel, BlModRes = 0;
|
|
|
|
if (rhdPtr->verbosity < 7)
|
|
return;
|
|
|
|
tmp = (RHDRegRead(Output, LVTMA_PWRSEQ_STATE) >> 3) & 0x01;
|
|
RHDDebug(rhdPtr->scrnIndex, "%s: PWRSEQ BLON State: %s\n",
|
|
__func__, tmp ? "on" : "off");
|
|
tmp = RHDRegRead(rhdPtr, LVTMA_PWRSEQ_CNTL);
|
|
Blon = (tmp >> 24) & 0x1;
|
|
BlonOvrd = (tmp >> 25) & 0x1;
|
|
BlonPol = (tmp >> 26) & 0x1;
|
|
|
|
RHDDebug(rhdPtr->scrnIndex, "%s: BLON: %s BLON_OVRD: %s BLON_POL: %s\n",
|
|
__func__, Blon ? "on" : "off",
|
|
BlonOvrd ? "enabled" : "disabled",
|
|
BlonPol ? "invert" : "non-invert");
|
|
|
|
tmp = RHDRegRead(rhdPtr, LVTMA_BL_MOD_CNTL);
|
|
BlModEn = tmp & 0x1;
|
|
BlModLevel = (tmp >> 8) & 0xFF;
|
|
if (rhdPtr->ChipSet >= RHD_RS600)
|
|
BlModRes = (tmp >> 16) & 0xFF;
|
|
|
|
xf86DrvMsgVerb(rhdPtr->scrnIndex, X_INFO, 3,
|
|
"%s: BL_MOD: %s BL_MOD_LEVEL: %d BL_MOD_RES: %d\n",
|
|
__func__, BlModEn ? "enable" : "disable",
|
|
BlModLevel, BlModRes);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static void
|
|
LVDSSetBacklight(struct rhdOutput *Output, int level)
|
|
{
|
|
struct LVDSPrivate *Private = (struct LVDSPrivate *) Output->Private;
|
|
RHDPtr rhdPtr = RHDPTRI(Output);
|
|
|
|
Private->BlLevel = level;
|
|
|
|
xf86DrvMsg(rhdPtr->scrnIndex, X_INFO,
|
|
"%s: trying to set BL_MOD_LEVEL to: %d\n",
|
|
__func__, level);
|
|
|
|
if (rhdPtr->ChipSet >= RHD_RS600)
|
|
_RHDRegMask(rhdPtr, LVTMA_BL_MOD_CNTL,
|
|
0xFF << 16 | (level << 8) | 0x1,
|
|
0xFFFF01);
|
|
else
|
|
_RHDRegMask(rhdPtr, LVTMA_BL_MOD_CNTL,
|
|
(level << 8) | 0x1,
|
|
0xFF01);
|
|
|
|
/*
|
|
* Poor man's debug
|
|
*/
|
|
LVDSDebugBacklight(Output);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static Bool
|
|
LVDSPropertyControl(struct rhdOutput *Output, enum rhdPropertyAction Action,
|
|
enum rhdOutputProperty Property, union rhdPropertyData *val)
|
|
{
|
|
struct LVDSPrivate *Private = (struct LVDSPrivate *) Output->Private;
|
|
|
|
switch (Action) {
|
|
case rhdPropertyCheck:
|
|
switch (Property) {
|
|
if (Private->BlLevel < 0)
|
|
return FALSE;
|
|
case RHD_OUTPUT_BACKLIGHT:
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
case rhdPropertyGet:
|
|
switch (Property) {
|
|
case RHD_OUTPUT_BACKLIGHT:
|
|
if (Private->BlLevel < 0)
|
|
return FALSE;
|
|
val->integer = Private->BlLevel;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case rhdPropertySet:
|
|
switch (Property) {
|
|
case RHD_OUTPUT_BACKLIGHT:
|
|
if (Private->BlLevel < 0)
|
|
return FALSE;
|
|
LVDSSetBacklight(Output, val->integer);
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static void
|
|
LVDSSet(struct rhdOutput *Output, DisplayModePtr Mode)
|
|
{
|
|
struct LVDSPrivate *Private = (struct LVDSPrivate *) Output->Private;
|
|
RHDPtr rhdPtr = RHDPTRI(Output);
|
|
|
|
RHDFUNC(Output);
|
|
|
|
RHDRegMask(Output, LVTMA_CNTL, 0x00000001, 0x00000001); /* enable */
|
|
usleep(20);
|
|
|
|
RHDRegWrite(Output, LVTMA_MODE, 0); /* set to LVDS */
|
|
|
|
/* Select CRTC, select syncA, no stereosync */
|
|
RHDRegMask(Output, LVTMA_SOURCE_SELECT, Output->Crtc->Id, 0x00010101);
|
|
|
|
if (Private->LVDS24Bit) { /* 24bits */
|
|
RHDRegMask(Output, LVTMA_LVDS_DATA_CNTL, 0x00000001, 0x00000001); /* enable 24bits */
|
|
RHDRegMask(Output, LVTMA_BIT_DEPTH_CONTROL, 0x00101010, 0x00101010); /* dithering bit depth = 24 */
|
|
|
|
if (Private->FPDI) /* FPDI? */
|
|
RHDRegMask(Output, LVTMA_LVDS_DATA_CNTL, 0x00000010, 0x00000010); /* 24 bit format: FPDI or LDI? */
|
|
else
|
|
RHDRegMask(Output, LVTMA_LVDS_DATA_CNTL, 0, 0x00000010);
|
|
} else {
|
|
RHDRegMask(Output, LVTMA_LVDS_DATA_CNTL, 0, 0x00000001); /* disable 24bits */
|
|
RHDRegMask(Output, LVTMA_BIT_DEPTH_CONTROL, 0, 0x00101010); /* dithering bit depth != 24 */
|
|
}
|
|
|
|
/* enable temporal dithering, disable spatial dithering and disable truncation */
|
|
RHDRegMask(Output, LVTMA_BIT_DEPTH_CONTROL,
|
|
Private->TemporalDither ? 1 << 16 : 0
|
|
| Private->SpatialDither ? 1 << 8 : 0
|
|
| (Private->GreyLevel > 2) ? 1 << 24 : 0,
|
|
0x01010101);
|
|
|
|
/* reset the temporal dithering */
|
|
RHDRegMask(Output, LVTMA_BIT_DEPTH_CONTROL, LVTMA_DITHER_RESET_BIT, LVTMA_DITHER_RESET_BIT);
|
|
RHDRegMask(Output, LVTMA_BIT_DEPTH_CONTROL, 0, LVTMA_DITHER_RESET_BIT);
|
|
|
|
/* go for RGB 4:4:4 RGB/YCbCr */
|
|
RHDRegMask(Output, LVTMA_CNTL, 0, 0x00010000);
|
|
|
|
if (Private->DualLink)
|
|
RHDRegMask(Output, LVTMA_CNTL, 0x01000000, 0x01000000);
|
|
else
|
|
RHDRegMask(Output, LVTMA_CNTL, 0, 0x01000000);
|
|
|
|
/* PLL and TX voltages */
|
|
RHDRegWrite(Output, LVTMA_MACRO_CONTROL, Private->MacroControl);
|
|
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL, 0x00000010, 0x00000010); /* use pclk_lvtma_direct */
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL, 0, 0xCC000000);
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL, Private->TXClockPattern << 16, 0x03FF0000);
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL, 0x00000001, 0x00000001); /* enable PLL */
|
|
usleep(20);
|
|
|
|
/* reset transmitter */
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL, 0x00000002, 0x00000002);
|
|
usleep(2);
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL, 0, 0x00000002);
|
|
usleep(20);
|
|
|
|
/* start data synchronisation */
|
|
RHDRegMask(Output, LVTMA_DATA_SYNCHRONIZATION, 0x00000001, 0x00000001);
|
|
RHDRegMask(Output, LVTMA_DATA_SYNCHRONIZATION, 0x00000100, 0x00000100); /* reset */
|
|
usleep(2);
|
|
RHDRegMask(Output, LVTMA_DATA_SYNCHRONIZATION, 0, 0x00000100);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static void
|
|
LVDSPWRSEQInit(struct rhdOutput *Output)
|
|
{
|
|
struct LVDSPrivate *Private = (struct LVDSPrivate *) Output->Private;
|
|
RHDPtr rhdPtr = RHDPTRI(Output);
|
|
|
|
CARD32 tmp = 0;
|
|
|
|
tmp = Private->PowerDigToDE >> 2;
|
|
RHDRegMask(Output, LVTMA_PWRSEQ_DELAY1, tmp, 0x000000FF);
|
|
RHDRegMask(Output, LVTMA_PWRSEQ_DELAY1, tmp << 24, 0xFF000000);
|
|
|
|
tmp = Private->PowerDEToBL >> 2;
|
|
RHDRegMask(Output, LVTMA_PWRSEQ_DELAY1, tmp << 8, 0x0000FF00);
|
|
RHDRegMask(Output, LVTMA_PWRSEQ_DELAY1, tmp << 16, 0x00FF0000);
|
|
|
|
RHDRegWrite(Output, LVTMA_PWRSEQ_DELAY2, Private->OffDelay >> 2);
|
|
RHDRegWrite(Output, LVTMA_PWRSEQ_REF_DIV,
|
|
Private->PowerRefDiv | (Private->BlonRefDiv << 16));
|
|
|
|
/* Enable power sequencer and allow it to override everything */
|
|
RHDRegMask(Output, LVTMA_PWRSEQ_CNTL, 0x0000000D, 0x0000000D);
|
|
|
|
/* give full control to the sequencer */
|
|
RHDRegMask(Output, LVTMA_PWRSEQ_CNTL, 0, 0x02020200);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static void
|
|
LVDSEnable(struct rhdOutput *Output)
|
|
{
|
|
struct LVDSPrivate *Private = (struct LVDSPrivate *) Output->Private;
|
|
RHDPtr rhdPtr = RHDPTRI(Output);
|
|
CARD32 tmp = 0;
|
|
int i;
|
|
|
|
RHDFUNC(Output);
|
|
|
|
LVDSPWRSEQInit(Output);
|
|
|
|
/* set up the transmitter */
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_ENABLE, 0x0000001E, 0x0000001E);
|
|
if (Private->LVDS24Bit) /* 24bit ? */
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_ENABLE, 0x00000020, 0x00000020);
|
|
|
|
if (Private->DualLink) {
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_ENABLE, 0x00001E00, 0x00001E00);
|
|
|
|
if (Private->LVDS24Bit)
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_ENABLE, 0x00002000, 0x00002000);
|
|
}
|
|
|
|
RHDRegMask(Output, LVTMA_PWRSEQ_CNTL, 0x00000010, 0x00000010);
|
|
|
|
for (i = 0; i <= Private->OffDelay; i++) {
|
|
usleep(1000);
|
|
|
|
tmp = (RHDRegRead(Output, LVTMA_PWRSEQ_STATE) >> 8) & 0x0F;
|
|
if (tmp == 4)
|
|
break;
|
|
}
|
|
|
|
if (i == Private->OffDelay) {
|
|
xf86DrvMsg(Output->scrnIndex, X_ERROR, "%s: failed to reach "
|
|
"POWERUP_DONE state after %d loops (%d)\n",
|
|
__func__, i, (int) tmp);
|
|
}
|
|
if (Private->BlLevel >= 0) {
|
|
union rhdPropertyData data;
|
|
data.integer = Private->BlLevel;
|
|
Output->Property(Output, rhdPropertySet, RHD_OUTPUT_BACKLIGHT,
|
|
&data);
|
|
}
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static void
|
|
LVDSDisable(struct rhdOutput *Output)
|
|
{
|
|
struct LVDSPrivate *Private = (struct LVDSPrivate *) Output->Private;
|
|
RHDPtr rhdPtr = RHDPTRI(Output);
|
|
CARD32 tmp = 0;
|
|
int i;
|
|
|
|
RHDFUNC(Output);
|
|
|
|
if (!(RHDRegRead(Output, LVTMA_PWRSEQ_CNTL) & 0x00000010))
|
|
return;
|
|
|
|
LVDSPWRSEQInit(Output);
|
|
|
|
RHDRegMask(Output, LVTMA_PWRSEQ_CNTL, 0, 0x00000010);
|
|
|
|
for (i = 0; i <= Private->OffDelay; i++) {
|
|
usleep(1000);
|
|
|
|
tmp = (RHDRegRead(Output, LVTMA_PWRSEQ_STATE) >> 8) & 0x0F;
|
|
if (tmp == 9)
|
|
break;
|
|
}
|
|
|
|
if (i == Private->OffDelay) {
|
|
xf86DrvMsg(Output->scrnIndex, X_ERROR, "%s: failed to reach "
|
|
"POWERDOWN_DONE state after %d loops (%d)\n",
|
|
__func__, i, (int) tmp);
|
|
}
|
|
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_ENABLE, 0, 0x0000FFFF);
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
*
|
|
*/
|
|
static void
|
|
LVDSShutdown(struct rhdOutput *Output)
|
|
{
|
|
RHDPtr rhdPtr = RHDPTRI(Output);
|
|
RHDFUNC(Output);
|
|
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL, 0x00000002, 0x00000002); /* PLL in reset */
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL, 0, 0x00000001); /* disable LVDS */
|
|
RHDRegMask(Output, LVTMA_DATA_SYNCHRONIZATION, 0, 0x00000001);
|
|
RHDRegMask(Output, LVTMA_BIT_DEPTH_CONTROL, LVTMA_DITHER_RESET_BIT, LVTMA_DITHER_RESET_BIT); /* reset temp dithering */
|
|
RHDRegMask(Output, LVTMA_BIT_DEPTH_CONTROL, 0, 0x00111111); /* disable all dithering */
|
|
RHDRegWrite(Output, LVTMA_CNTL, 0); /* disable */
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static void
|
|
LVDSPower(struct rhdOutput *Output, int Power)
|
|
{
|
|
RHDDebug(Output->scrnIndex, "%s(%s,%s)\n",__func__,Output->Name,
|
|
rhdPowerString[Power]);
|
|
|
|
switch (Power) {
|
|
case RHD_POWER_ON:
|
|
LVDSEnable(Output);
|
|
break;
|
|
case RHD_POWER_RESET:
|
|
/* LVDSDisable(Output);
|
|
break;*/
|
|
case RHD_POWER_SHUTDOWN:
|
|
default:
|
|
LVDSDisable(Output);
|
|
/* LVDSShutdown(Output); */
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static void
|
|
LVDSSave(struct rhdOutput *Output)
|
|
{
|
|
struct LVDSPrivate *Private = (struct LVDSPrivate *) Output->Private;
|
|
RHDPtr rhdPtr = RHDPTRI(Output);
|
|
|
|
RHDFUNC(Output);
|
|
|
|
Private->StoreControl = RHDRegRead(Output, LVTMA_CNTL);
|
|
Private->StoreSourceSelect = RHDRegRead(Output, LVTMA_SOURCE_SELECT);
|
|
Private->StoreBitDepthControl = RHDRegRead(Output, LVTMA_BIT_DEPTH_CONTROL);
|
|
Private->StoreDataSynchronisation = RHDRegRead(Output, LVTMA_DATA_SYNCHRONIZATION);
|
|
Private->StorePWRSEQRefDiv = RHDRegRead(Output, LVTMA_PWRSEQ_REF_DIV);
|
|
Private->StorePWRSEQDelay1 = RHDRegRead(Output, LVTMA_PWRSEQ_DELAY1);
|
|
Private->StorePWRSEQDelay2 = RHDRegRead(Output, LVTMA_PWRSEQ_DELAY2);
|
|
Private->StorePWRSEQControl = RHDRegRead(Output, LVTMA_PWRSEQ_CNTL);
|
|
Private->StorePWRSEQState = RHDRegRead(Output, LVTMA_PWRSEQ_STATE);
|
|
Private->StoreLVDSDataControl = RHDRegRead(Output, LVTMA_LVDS_DATA_CNTL);
|
|
Private->StoreMode = RHDRegRead(Output, LVTMA_MODE);
|
|
Private->StoreTxEnable = RHDRegRead(Output, LVTMA_TRANSMITTER_ENABLE);
|
|
Private->StoreMacroControl = RHDRegRead(Output, LVTMA_MACRO_CONTROL);
|
|
Private->StoreTXControl = RHDRegRead(Output, LVTMA_TRANSMITTER_CONTROL);
|
|
Private->StoreBlModCntl = RHDRegRead(Output, LVTMA_BL_MOD_CNTL);
|
|
|
|
Private->Stored = TRUE;
|
|
}
|
|
|
|
/*
|
|
* This needs to reset things like the temporal dithering and the TX appropriately.
|
|
* Currently it's a dumb register dump.
|
|
*/
|
|
static void
|
|
LVDSRestore(struct rhdOutput *Output)
|
|
{
|
|
struct LVDSPrivate *Private = (struct LVDSPrivate *) Output->Private;
|
|
RHDPtr rhdPtr = RHDPTRI(Output);
|
|
|
|
RHDFUNC(Output);
|
|
|
|
if (!Private->Stored) {
|
|
xf86DrvMsg(Output->scrnIndex, X_ERROR,
|
|
"%s: No registers stored.\n", __func__);
|
|
return;
|
|
}
|
|
|
|
RHDRegWrite(Output, LVTMA_CNTL, Private->StoreControl);
|
|
RHDRegWrite(Output, LVTMA_SOURCE_SELECT, Private->StoreSourceSelect);
|
|
RHDRegWrite(Output, LVTMA_BIT_DEPTH_CONTROL, Private->StoreBitDepthControl);
|
|
RHDRegWrite(Output, LVTMA_DATA_SYNCHRONIZATION, Private->StoreDataSynchronisation);
|
|
RHDRegWrite(Output, LVTMA_PWRSEQ_REF_DIV, Private->StorePWRSEQRefDiv);
|
|
RHDRegWrite(Output, LVTMA_PWRSEQ_DELAY1, Private->StorePWRSEQDelay1);
|
|
RHDRegWrite(Output, LVTMA_PWRSEQ_DELAY2, Private->StorePWRSEQDelay2);
|
|
RHDRegWrite(Output, LVTMA_PWRSEQ_CNTL, Private->StorePWRSEQControl);
|
|
RHDRegWrite(Output, LVTMA_PWRSEQ_STATE, Private->StorePWRSEQState);
|
|
RHDRegWrite(Output, LVTMA_LVDS_DATA_CNTL, Private->StoreLVDSDataControl);
|
|
RHDRegWrite(Output, LVTMA_MODE, Private->StoreMode);
|
|
RHDRegWrite(Output, LVTMA_TRANSMITTER_ENABLE, Private->StoreTxEnable);
|
|
RHDRegWrite(Output, LVTMA_MACRO_CONTROL, Private->StoreMacroControl);
|
|
RHDRegWrite(Output, LVTMA_TRANSMITTER_CONTROL, Private->StoreTXControl);
|
|
RHDRegWrite(Output, LVTMA_BL_MOD_CNTL, Private->StoreBlModCntl);
|
|
|
|
/*
|
|
* Poor man's debug
|
|
*/
|
|
LVDSDebugBacklight(Output);
|
|
}
|
|
|
|
/*
|
|
* Here we pretty much assume that ATOM has either initialised the panel already
|
|
* or that we can find information from ATOM BIOS data tables. We know that the
|
|
* latter assumption is false for some values, but there is no getting around
|
|
* ATI clinging desperately to a broken concept.
|
|
*/
|
|
static struct LVDSPrivate *
|
|
LVDSInfoRetrieve(RHDPtr rhdPtr)
|
|
{
|
|
struct LVDSPrivate *Private = xnfcalloc(sizeof(struct LVDSPrivate), 1);
|
|
CARD32 tmp;
|
|
|
|
/* These values are not available from atombios data tables at all. */
|
|
Private->MacroControl = RHDRegRead(rhdPtr, LVTMA_MACRO_CONTROL);
|
|
Private->TXClockPattern =
|
|
(_RHDRegRead(rhdPtr, LVTMA_TRANSMITTER_CONTROL) >> 16) & 0x3FF;
|
|
|
|
/* For these values, we try to retrieve them from register space first,
|
|
and later override with atombios data table information */
|
|
Private->PowerDigToDE =
|
|
(_RHDRegRead(rhdPtr, LVTMA_PWRSEQ_DELAY1) & 0x000000FF) << 2;
|
|
|
|
Private->PowerDEToBL =
|
|
(_RHDRegRead(rhdPtr, LVTMA_PWRSEQ_DELAY1) & 0x0000FF00) >> 6;
|
|
|
|
Private->OffDelay = (_RHDRegRead(rhdPtr, LVTMA_PWRSEQ_DELAY2) & 0xFF) << 2;
|
|
|
|
tmp = _RHDRegRead(rhdPtr, LVTMA_PWRSEQ_REF_DIV);
|
|
Private->PowerRefDiv = tmp & 0x0FFF;
|
|
Private->BlonRefDiv = (tmp >> 16) & 0x0FFF;
|
|
tmp = _RHDRegRead(rhdPtr, LVTMA_BL_MOD_CNTL);
|
|
if (tmp & 0x1)
|
|
Private->BlLevel = (tmp >> 8) & 0xff;
|
|
else
|
|
Private->BlLevel = -1; /* Backlight control seems to be done some other way */
|
|
|
|
Private->DualLink = (_RHDRegRead(rhdPtr, LVTMA_CNTL) >> 24) & 0x00000001;
|
|
Private->LVDS24Bit = _RHDRegRead(rhdPtr, LVTMA_LVDS_DATA_CNTL) & 0x00000001;
|
|
Private->FPDI = _RHDRegRead(rhdPtr, LVTMA_LVDS_DATA_CNTL) & 0x00000010;
|
|
|
|
tmp = _RHDRegRead(rhdPtr, LVTMA_BIT_DEPTH_CONTROL);
|
|
Private->TemporalDither = ((tmp & (1 << 16)) != 0);
|
|
Private->SpatialDither = ((tmp & (1 << 8)) != 0);
|
|
Private->GreyLevel = (tmp & (1 << 24)) ? 4 : 2;
|
|
|
|
#ifdef ATOM_BIOS
|
|
{
|
|
AtomBiosArgRec data;
|
|
|
|
if(RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS,
|
|
ATOM_LVDS_SEQ_DIG_ONTO_DE, &data) == ATOM_SUCCESS)
|
|
Private->PowerDigToDE = data.val;
|
|
|
|
if (RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS,
|
|
ATOM_LVDS_SEQ_DE_TO_BL, &data) == ATOM_SUCCESS)
|
|
Private->PowerDEToBL = data.val;
|
|
|
|
if (RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS,
|
|
ATOM_LVDS_OFF_DELAY, &data) == ATOM_SUCCESS)
|
|
Private->OffDelay = data.val;
|
|
|
|
if (RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS,
|
|
ATOM_LVDS_DUALLINK, &data) == ATOM_SUCCESS)
|
|
Private->DualLink = data.val;
|
|
|
|
if (RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS,
|
|
ATOM_LVDS_24BIT, &data) == ATOM_SUCCESS)
|
|
Private->LVDS24Bit = data.val;
|
|
|
|
if (RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS,
|
|
ATOM_LVDS_FPDI, &data) == ATOM_SUCCESS)
|
|
Private->FPDI = data.val;
|
|
|
|
if (RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS,
|
|
ATOM_LVDS_TEMPORAL_DITHER, &data) == ATOM_SUCCESS)
|
|
Private->TemporalDither = data.val;
|
|
|
|
if (RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS,
|
|
ATOM_LVDS_SPATIAL_DITHER, &data) == ATOM_SUCCESS)
|
|
Private->SpatialDither = data.val;
|
|
|
|
if (RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS,
|
|
ATOM_LVDS_GREYLVL, &data) == ATOM_SUCCESS) {
|
|
Private->GreyLevel = data.val;
|
|
xf86DrvMsg(rhdPtr->scrnIndex, X_INFO, "AtomBIOS returned %i Grey Levels\n",
|
|
Private->GreyLevel);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (Private->LVDS24Bit)
|
|
xf86DrvMsg(rhdPtr->scrnIndex, X_PROBED,
|
|
"Detected a 24bit %s, %s link panel.\n",
|
|
Private->DualLink ? "dual" : "single",
|
|
Private->FPDI ? "FPDI": "LDI");
|
|
else
|
|
xf86DrvMsg(rhdPtr->scrnIndex, X_PROBED,
|
|
"Detected a 18bit %s link panel.\n",
|
|
Private->DualLink ? "dual" : "single");
|
|
|
|
/* extra noise */
|
|
RHDDebug(rhdPtr->scrnIndex, "Printing LVDS paramaters:\n");
|
|
xf86MsgVerb(X_NONE, LOG_DEBUG, "\tMacroControl: 0x%08X\n",
|
|
(unsigned int) Private->MacroControl);
|
|
xf86MsgVerb(X_NONE, LOG_DEBUG, "\tTXClockPattern: 0x%04X\n",
|
|
Private->TXClockPattern);
|
|
xf86MsgVerb(X_NONE, LOG_DEBUG, "\tPowerDigToDE: 0x%04X\n",
|
|
Private->PowerDigToDE);
|
|
xf86MsgVerb(X_NONE, LOG_DEBUG, "\tPowerDEToBL: 0x%04X\n",
|
|
Private->PowerDEToBL);
|
|
xf86MsgVerb(X_NONE, LOG_DEBUG, "\tOffDelay: 0x%04X\n",
|
|
Private->OffDelay);
|
|
xf86MsgVerb(X_NONE, LOG_DEBUG, "\tPowerRefDiv: 0x%04X\n",
|
|
Private->PowerRefDiv);
|
|
xf86MsgVerb(X_NONE, LOG_DEBUG, "\tBlonRefDiv: 0x%04X\n",
|
|
Private->BlonRefDiv);
|
|
|
|
return Private;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static void
|
|
LVDSDestroy(struct rhdOutput *Output)
|
|
{
|
|
|
|
struct LVDSPrivate *Private = (struct LVDSPrivate *) Output->Private;
|
|
|
|
RHDFUNC(Output);
|
|
|
|
if (!Private)
|
|
return;
|
|
|
|
#ifdef NOT_YET
|
|
if (Private->PropertyPrivate)
|
|
RhdAtomDestroyBacklightControlProperty(Output, Private->PropertyPrivate);
|
|
#endif
|
|
xfree(Private);
|
|
Output->Private = NULL;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* Handling for LVTMA block as TMDS.
|
|
*
|
|
*/
|
|
struct rhdTMDSBPrivate {
|
|
Bool RunsDualLink;
|
|
Bool Coherent;
|
|
DisplayModePtr Mode;
|
|
|
|
struct rhdHdmi *Hdmi;
|
|
|
|
Bool Stored;
|
|
|
|
CARD32 StoreControl;
|
|
CARD32 StoreSource;
|
|
CARD32 StoreFormat;
|
|
CARD32 StoreForce;
|
|
CARD32 StoreReduction;
|
|
CARD32 StoreDCBalancer;
|
|
CARD32 StoreDataSynchro;
|
|
CARD32 StoreMode;
|
|
CARD32 StoreTXEnable;
|
|
CARD32 StoreMacro;
|
|
CARD32 StoreTXControl;
|
|
CARD32 StoreTXAdjust;
|
|
CARD32 StoreTestOutput;
|
|
|
|
CARD32 StoreRs690Unknown;
|
|
CARD32 StoreRv600TXAdjust;
|
|
CARD32 StoreRv600PreEmphasis;
|
|
};
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static ModeStatus
|
|
TMDSBModeValid(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
|
|
RS600VoltageControl(struct rhdOutput *Output, DisplayModePtr Mode)
|
|
{
|
|
struct rhdTMDSBPrivate *Private = (struct rhdTMDSBPrivate *) Output->Private;
|
|
|
|
RHDFUNC(Output);
|
|
#ifdef NOTYET
|
|
if (Output->Connector == RHD_CONNECTOR_HDMI || Output->Connector == RHD_CONNECTOR_HDMI_DUAL) {
|
|
int clock = Mode->SynthClock;
|
|
|
|
if (Private->RunsDualLink)
|
|
clock >>= 1;
|
|
if (clock <= 75000) {
|
|
RHDRegWrite(Output, LVTMA_R600_MACRO_CONTROL, 0x00010213);
|
|
RHDRegWrite(Output, LVTMA_R600_REG_TEST_OUTPUT, 0x000a0000);
|
|
} else {
|
|
RHDRegWrite(Output, LVTMA_R600_MACRO_CONTROL, 0x00000213);
|
|
RHDRegWrite(Output, LVTMA_R600_REG_TEST_OUTPUT, 0x00100000);
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
if (Private->RunsDualLink) {
|
|
RHDRegWrite(Output, LVTMA_R600_MACRO_CONTROL, 0x0000020f);
|
|
RHDRegWrite(Output, LVTMA_R600_REG_TEST_OUTPUT, 0x00100000);
|
|
} else {
|
|
if (Mode->SynthClock < 39000)
|
|
RHDRegWrite(Output, LVTMA_R600_MACRO_CONTROL, 0x0002020f);
|
|
else
|
|
RHDRegWrite(Output, LVTMA_R600_MACRO_CONTROL, 0x0000020f);
|
|
RHDRegWrite(Output, LVTMA_R600_REG_TEST_OUTPUT, 0x00100000);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static void
|
|
RS690VoltageControl(struct rhdOutput *Output, DisplayModePtr Mode)
|
|
{
|
|
RHDPtr rhdPtr = RHDPTRI(Output);
|
|
struct rhdTMDSBPrivate *Private = (struct rhdTMDSBPrivate *) Output->Private;
|
|
CARD32 rev = (RHDRegRead(Output, CONFIG_CNTL) && RS69_CFG_ATI_REV_ID_MASK) >> RS69_CFG_ATI_REV_ID_SHIFT;
|
|
|
|
if (rev < 3) {
|
|
#ifdef NOTYET
|
|
if (Output->Connector == RHD_CONNECTOR_HDMI || Output->Connector == RHD_CONNECTOR_HDMI_DUAL) {
|
|
if (Mode->SynthClock > 75000) {
|
|
RHDRegWrite(Output, LVTMA_R600_MACRO_CONTROL, 0xa001632f);
|
|
RHDRegWrite(Output, LVTMA_R600_REG_TEST_OUTPUT, 0x05120000);
|
|
RHDRegMask(Output, LVTMA_R600_TRANSMITTER_CONTROL, 0x10000000, 0x10000000);
|
|
} else if (Mode->SynthClock > 41000) {
|
|
RHDRegWrite(Output, LVTMA_R600_MACRO_CONTROL, 0x0000632f);
|
|
RHDRegWrite(Output, LVTMA_R600_REG_TEST_OUTPUT, 0x05120000);
|
|
RHDRegMask(Output, LVTMA_R600_TRANSMITTER_CONTROL, 0x10000000, 0x10000000);
|
|
} else {
|
|
RHDRegWrite(Output, LVTMA_R600_MACRO_CONTROL, 0x0003632f);
|
|
RHDRegWrite(Output, LVTMA_R600_REG_TEST_OUTPUT, 0x050b000);
|
|
RHDRegMask(Output, LVTMA_R600_TRANSMITTER_CONTROL, 0x0, 0x10000000);
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
int clock = Mode->SynthClock;
|
|
|
|
if (Private->RunsDualLink)
|
|
clock >>= 1;
|
|
|
|
RHDRegWrite(Output, LVTMA_R600_REG_TEST_OUTPUT, 0x05120000);
|
|
|
|
if (clock > 75000) {
|
|
RHDRegWrite(Output, LVTMA_R600_MACRO_CONTROL, 0xa001631f);
|
|
RHDRegMask(Output, LVTMA_R600_TRANSMITTER_CONTROL, 0x10000000, 0x10000000);
|
|
} else if (clock > 41000) {
|
|
RHDRegWrite(Output, LVTMA_R600_MACRO_CONTROL, 0x0000631f);
|
|
RHDRegMask(Output, LVTMA_R600_TRANSMITTER_CONTROL, 0x10000000, 0x10000000);
|
|
} else {
|
|
RHDRegWrite(Output, LVTMA_R600_MACRO_CONTROL, 0x0003631f);
|
|
RHDRegMask(Output, LVTMA_R600_TRANSMITTER_CONTROL, 0x0, 0x10000000);
|
|
}
|
|
}
|
|
} else {
|
|
#ifdef NOTYET
|
|
if (Output->Connector == RHD_CONNECTOR_HDMI || Output->Connector == RHD_CONNECTOR_HDMI_DUAL) {
|
|
if (Mode->SynthClock <= 75000) {
|
|
RHDRegWrite(Output, LVTMA_R600_MACRO_CONTROL, 0x0002612f);
|
|
RHDRegWrite(Output, LVTMA_R600_REG_TEST_OUTPUT, 0x010b0000);
|
|
RHDRegMask(Output, LVTMA_R600_TRANSMITTER_CONTROL, 0x0, 0x10000000);
|
|
} else {
|
|
RHDRegWrite(Output, LVTMA_R600_MACRO_CONTROL, 0x0000642f);
|
|
RHDRegWrite(Output, LVTMA_R600_REG_TEST_OUTPUT, 0x01120000);
|
|
RHDRegMask(Output, LVTMA_R600_TRANSMITTER_CONTROL, 0x10000000, 0x10000000);
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
int clock = Mode->SynthClock;
|
|
|
|
if (Private->RunsDualLink)
|
|
clock >>= 1;
|
|
|
|
RHDRegWrite(Output, LVTMA_R600_REG_TEST_OUTPUT, 0x01120000);
|
|
RHDRegMask(Output, LVTMA_R600_TRANSMITTER_CONTROL, 0x10000000, 0x10000000);
|
|
|
|
if (Mode->SynthClock > 75000) {
|
|
RHDRegWrite(Output, LVTMA_R600_MACRO_CONTROL, 0x00016318);
|
|
} else {
|
|
{
|
|
#ifdef ATOM_BIOS
|
|
AtomBiosArgRec data;
|
|
|
|
if (RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS,
|
|
ATOM_GET_CAPABILITY_FLAG, &data) == ATOM_SUCCESS) {
|
|
if (((data.val & 0x60) == 0x20 || (data.val & 0x80))) {
|
|
RHDRegWrite(Output, LVTMA_R600_MACRO_CONTROL, 0x00016318);
|
|
} else {
|
|
RHDRegWrite(Output, LVTMA_R600_MACRO_CONTROL, 0x00006318);
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
RHDRegWrite(Output, LVTMA_R600_MACRO_CONTROL, 0x00006318);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This information is not provided in an atombios data table.
|
|
*/
|
|
static struct R5xxTMDSBMacro {
|
|
CARD16 Device;
|
|
CARD32 MacroSingle;
|
|
CARD32 MacroDual;
|
|
} R5xxTMDSBMacro[] = {
|
|
/*
|
|
* this list isn't complete yet.
|
|
* Some more values for dual need to be dug up
|
|
*/
|
|
{ 0x7104, 0x00F20616, 0x00F20616 }, /* R520 */
|
|
{ 0x7142, 0x00F2061C, 0x00F2061C }, /* RV515 */
|
|
{ 0x7145, 0x00F1061D, 0x00F2061D }, /**/
|
|
{ 0x7146, 0x00F1061D, 0x00F1061D }, /* RV515 */
|
|
{ 0x7147, 0x0082041D, 0x0082041D }, /* RV505 */
|
|
{ 0x7149, 0x00F1061D, 0x00D2061D }, /**/
|
|
{ 0x7152, 0x00F2061C, 0x00F2061C }, /* RV515 */
|
|
{ 0x7183, 0x00B2050C, 0x00B2050C }, /* RV530 */
|
|
{ 0x71C0, 0x00F1061F, 0x00f2061D }, /**/
|
|
{ 0x71C1, 0x0062041D, 0x0062041D }, /* RV535 *//**/
|
|
{ 0x71C2, 0x00F1061D, 0x00F2061D }, /* RV530 *//**/
|
|
{ 0x71C5, 0x00D1061D, 0x00D2061D }, /**/
|
|
{ 0x71C6, 0x00F2061D, 0x00F2061D }, /* RV530 */
|
|
{ 0x71D2, 0x00F10610, 0x00F20610 }, /* RV530: atombios uses 0x00F1061D *//**/
|
|
{ 0x7249, 0x00F1061D, 0x00F1061D }, /* R580 */
|
|
{ 0x724B, 0x00F10610, 0x00F10610 }, /* R580: atombios uses 0x00F1061D */
|
|
{ 0x7280, 0x0042041F, 0x0042041F }, /* RV570 *//**/
|
|
{ 0x7288, 0x0042041F, 0x0042041F }, /* RV570 */
|
|
{ 0x791E, 0x0001642F, 0x0001642F }, /* RS690 */
|
|
{ 0x791F, 0x0001642F, 0x0001642F }, /* RS690 */
|
|
{ 0x9400, 0x00020213, 0x00020213 }, /* R600 */
|
|
{ 0x9401, 0x00020213, 0x00020213 }, /* R600 */
|
|
{ 0x9402, 0x00020213, 0x00020213 }, /* R600 */
|
|
{ 0x9403, 0x00020213, 0x00020213 }, /* R600 */
|
|
{ 0x9405, 0x00020213, 0x00020213 }, /* R600 */
|
|
{ 0x940A, 0x00020213, 0x00020213 }, /* R600 */
|
|
{ 0x940B, 0x00020213, 0x00020213 }, /* R600 */
|
|
{ 0x940F, 0x00020213, 0x00020213 }, /* R600 */
|
|
{ 0, 0, 0 } /* End marker */
|
|
};
|
|
|
|
static struct RV6xxTMDSBMacro {
|
|
CARD16 Device;
|
|
CARD32 Macro;
|
|
CARD32 TX;
|
|
CARD32 PreEmphasis;
|
|
} RV6xxTMDSBMacro[] = {
|
|
{ 0x94C1, 0x01030311, 0x10001A00, 0x01801015}, /* RV610 */
|
|
{ 0x94C3, 0x01030311, 0x10001A00, 0x01801015}, /* RV610 */
|
|
{ 0x9501, 0x0533041A, 0x020010A0, 0x41002045}, /* RV670 */
|
|
{ 0x9505, 0x0533041A, 0x020010A0, 0x41002045}, /* RV670 */
|
|
{ 0x950F, 0x0533041A, 0x020010A0, 0x41002045}, /* R680 */
|
|
{ 0x9587, 0x01030311, 0x10001C00, 0x01C01011}, /* RV630 */
|
|
{ 0x9588, 0x01030311, 0x10001C00, 0x01C01011}, /* RV630 */
|
|
{ 0x9589, 0x01030311, 0x10001C00, 0x01C01011}, /* RV630 */
|
|
{ 0, 0, 0, 0} /* End marker */
|
|
};
|
|
|
|
static void
|
|
TMDSBVoltageControl(struct rhdOutput *Output, DisplayModePtr Mode)
|
|
{
|
|
struct rhdTMDSBPrivate *Private = (struct rhdTMDSBPrivate *) Output->Private;
|
|
RHDPtr rhdPtr = RHDPTRI(Output);
|
|
int i;
|
|
|
|
/* IGP chipsets are rather special */
|
|
if (rhdPtr->ChipSet == RHD_RS690) {
|
|
RS690VoltageControl(Output, Mode);
|
|
return;
|
|
} else if (rhdPtr->ChipSet == RHD_RS600) {
|
|
RS600VoltageControl(Output, Mode);
|
|
return;
|
|
}
|
|
|
|
/* TEST_OUTPUT register - IGPs are handled above */
|
|
if (rhdPtr->ChipSet < RHD_RS600) /* r5xx */
|
|
RHDRegMask(Output, LVTMA_REG_TEST_OUTPUT, 0x00200000, 0x00200000);
|
|
else if (rhdPtr->ChipSet < RHD_RV670)
|
|
RHDRegMask(Output, LVTMA_REG_TEST_OUTPUT, 0x00100000, 0x00100000);
|
|
|
|
/* macro control values */
|
|
if (rhdPtr->ChipSet < RHD_RV610) { /* R5xx and R600 */
|
|
for (i = 0; R5xxTMDSBMacro[i].Device; i++)
|
|
if (R5xxTMDSBMacro[i].Device == rhdPtr->PciDeviceID) {
|
|
if (!Private->RunsDualLink)
|
|
RHDRegWrite(Output, LVTMA_MACRO_CONTROL, R5xxTMDSBMacro[i].MacroSingle);
|
|
else
|
|
RHDRegWrite(Output, LVTMA_MACRO_CONTROL, R5xxTMDSBMacro[i].MacroDual);
|
|
return;
|
|
}
|
|
|
|
xf86DrvMsg(Output->scrnIndex, X_ERROR, "%s: unhandled chipset: 0x%04X.\n",
|
|
__func__, rhdPtr->PciDeviceID);
|
|
xf86DrvMsg(Output->scrnIndex, X_INFO, "LVTMA_MACRO_CONTROL: 0x%08X\n",
|
|
(unsigned int) RHDRegRead(Output, LVTMA_MACRO_CONTROL));
|
|
} else { /* RV6x0 and up */
|
|
for (i = 0; RV6xxTMDSBMacro[i].Device; i++)
|
|
if (RV6xxTMDSBMacro[i].Device == rhdPtr->PciDeviceID) {
|
|
RHDRegWrite(Output, LVTMA_MACRO_CONTROL, RV6xxTMDSBMacro[i].Macro);
|
|
RHDRegWrite(Output, LVTMA_TRANSMITTER_ADJUST, RV6xxTMDSBMacro[i].TX);
|
|
RHDRegWrite(Output, LVTMA_PREEMPHASIS_CONTROL, RV6xxTMDSBMacro[i].PreEmphasis);
|
|
return;
|
|
}
|
|
|
|
xf86DrvMsg(Output->scrnIndex, X_ERROR, "%s: unhandled chipset: 0x%04X.\n",
|
|
__func__, rhdPtr->PciDeviceID);
|
|
xf86DrvMsg(Output->scrnIndex, X_INFO, "LVTMA_MACRO_CONTROL: 0x%08X\n",
|
|
(unsigned int) RHDRegRead(Output, LVTMA_MACRO_CONTROL));
|
|
xf86DrvMsg(Output->scrnIndex, X_INFO, "LVTMA_TRANSMITTER_ADJUST: 0x%08X\n",
|
|
(unsigned int) RHDRegRead(Output, LVTMA_TRANSMITTER_ADJUST));
|
|
xf86DrvMsg(Output->scrnIndex, X_INFO, "LVTMA_PREEMPHASIS_CONTROL: 0x%08X\n",
|
|
(unsigned int) RHDRegRead(Output, LVTMA_PREEMPHASIS_CONTROL));
|
|
}
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static Bool
|
|
TMDSBPropertyControl(struct rhdOutput *Output,
|
|
enum rhdPropertyAction Action, enum rhdOutputProperty Property, union rhdPropertyData *val)
|
|
{
|
|
struct rhdTMDSBPrivate *Private = (struct rhdTMDSBPrivate *) Output->Private;
|
|
|
|
RHDFUNC(Output);
|
|
switch (Action) {
|
|
case rhdPropertyCheck:
|
|
switch (Property) {
|
|
case RHD_OUTPUT_COHERENT:
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
case rhdPropertyGet:
|
|
switch (Property) {
|
|
case RHD_OUTPUT_COHERENT:
|
|
val->Bool = Private->Coherent;
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case rhdPropertySet:
|
|
switch (Property) {
|
|
case RHD_OUTPUT_COHERENT:
|
|
Private->Coherent = val->Bool;
|
|
Output->Mode(Output, Private->Mode);
|
|
Output->Power(Output, RHD_POWER_ON);
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static void
|
|
TMDSBSet(struct rhdOutput *Output, DisplayModePtr Mode)
|
|
{
|
|
RHDPtr rhdPtr = RHDPTRI(Output);
|
|
struct rhdTMDSBPrivate *Private = (struct rhdTMDSBPrivate *) Output->Private;
|
|
|
|
RHDFUNC(Output);
|
|
|
|
RHDRegMask(Output, LVTMA_MODE, 0x00000001, 0x00000001); /* select TMDS */
|
|
|
|
/* Clear out some HPD events first: this should be under driver control. */
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL, 0, 0x0000000C);
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_ENABLE, 0, 0x00070000);
|
|
RHDRegMask(Output, LVTMA_CNTL, 0, 0x00000010);
|
|
|
|
/* Disable the transmitter */
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_ENABLE, 0, 0x00003E3E);
|
|
|
|
/* Disable bit reduction and reset temporal dither */
|
|
RHDRegMask(Output, LVTMA_BIT_DEPTH_CONTROL, 0, 0x00010101);
|
|
RHDRegMask(Output, LVTMA_BIT_DEPTH_CONTROL, LVTMA_DITHER_RESET_BIT, LVTMA_DITHER_RESET_BIT);
|
|
usleep(2);
|
|
RHDRegMask(Output, LVTMA_BIT_DEPTH_CONTROL, 0, LVTMA_DITHER_RESET_BIT);
|
|
RHDRegMask(Output, LVTMA_BIT_DEPTH_CONTROL, 0, 0xF0000000); /* not documented */
|
|
|
|
/* reset phase on vsync and use RGB */
|
|
RHDRegMask(Output, LVTMA_CNTL, 0x00001000, 0x00011000);
|
|
|
|
/* Select CRTC, select syncA, no stereosync */
|
|
RHDRegMask(Output, LVTMA_SOURCE_SELECT, Output->Crtc->Id, 0x00010101);
|
|
|
|
RHDRegWrite(Output, LVTMA_COLOR_FORMAT, 0);
|
|
|
|
Private->Mode = Mode;
|
|
if (Mode->SynthClock > 165000) {
|
|
RHDRegMask(Output, LVTMA_CNTL, 0x01000000, 0x01000000);
|
|
Private->RunsDualLink = TRUE; /* for TRANSMITTER_ENABLE in TMDSBPower */
|
|
} else {
|
|
RHDRegMask(Output, LVTMA_CNTL, 0, 0x01000000);
|
|
Private->RunsDualLink = FALSE;
|
|
}
|
|
|
|
if (rhdPtr->ChipSet > RHD_R600) /* Rv6xx: disable split mode */
|
|
RHDRegMask(Output, LVTMA_CNTL, 0, 0x20000000);
|
|
|
|
/* Disable force data */
|
|
RHDRegMask(Output, LVTMA_FORCE_OUTPUT_CNTL, 0, 0x00000001);
|
|
|
|
/* DC balancer enable */
|
|
RHDRegMask(Output, LVTMA_DCBALANCER_CONTROL, 0x00000001, 0x00000001);
|
|
|
|
TMDSBVoltageControl(Output, Mode);
|
|
|
|
/* use IDCLK */
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL, 0x00000010, 0x00000010);
|
|
/* LVTMA only: use clock selected by next write */
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL, 0x20000000, 0x20000000);
|
|
/* coherent mode */
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL,
|
|
Private->Coherent ? 0 : 0x10000000, 0x10000000);
|
|
/* clear LVDS clock pattern */
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL, 0, 0x03FF0000);
|
|
|
|
/* reset transmitter pll */
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL, 0x00000002, 0x00000002);
|
|
usleep(2);
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL, 0, 0x00000002);
|
|
usleep(20);
|
|
|
|
/* restart data synchronisation */
|
|
RHDRegMask(Output, LVTMA_DATA_SYNCHRONIZATION, 0x00000001, 0x00000001);
|
|
RHDRegMask(Output, LVTMA_DATA_SYNCHRONIZATION, 0x00000100, 0x00000100);
|
|
usleep(2);
|
|
RHDRegMask(Output, LVTMA_DATA_SYNCHRONIZATION, 0, 0x00000001);
|
|
|
|
RHDHdmiSetMode(Private->Hdmi, Mode);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static void
|
|
TMDSBPower(struct rhdOutput *Output, int Power)
|
|
{
|
|
RHDPtr rhdPtr = RHDPTRI(Output);
|
|
struct rhdTMDSBPrivate *Private = (struct rhdTMDSBPrivate *) Output->Private;
|
|
|
|
RHDDebug(Output->scrnIndex, "%s(%s,%s)\n",__func__,Output->Name,
|
|
rhdPowerString[Power]);
|
|
|
|
RHDRegMask(Output, LVTMA_MODE, 0x00000001, 0x00000001); /* select TMDS */
|
|
|
|
switch (Power) {
|
|
case RHD_POWER_ON:
|
|
RHDRegMask(Output, LVTMA_CNTL, 0x1, 0x00000001);
|
|
|
|
if (Private->RunsDualLink)
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_ENABLE, 0x00003E3E,0x00003E3E);
|
|
else
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_ENABLE, 0x0000003E, 0x00003E3E);
|
|
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL, 0x00000001, 0x00000001);
|
|
usleep(2);
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL, 0, 0x00000002);
|
|
if(Output->Connector != NULL && RHDConnectorEnableHDMI(Output->Connector))
|
|
RHDHdmiEnable(Private->Hdmi, TRUE);
|
|
else
|
|
RHDHdmiEnable(Private->Hdmi, FALSE);
|
|
return;
|
|
case RHD_POWER_RESET:
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_ENABLE, 0, 0x00003E3E);
|
|
return;
|
|
case RHD_POWER_SHUTDOWN:
|
|
default:
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL, 0x00000002, 0x00000002);
|
|
usleep(2);
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_CONTROL, 0, 0x00000001);
|
|
RHDRegMask(Output, LVTMA_TRANSMITTER_ENABLE, 0, 0x00003E3E);
|
|
RHDRegMask(Output, LVTMA_CNTL, 0, 0x00000001);
|
|
RHDHdmiEnable(Private->Hdmi, FALSE);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static void
|
|
TMDSBSave(struct rhdOutput *Output)
|
|
{
|
|
struct rhdTMDSBPrivate *Private = (struct rhdTMDSBPrivate *) Output->Private;
|
|
RHDPtr rhdPtr = RHDPTRI(Output);
|
|
|
|
RHDFUNC(Output);
|
|
|
|
Private->StoreControl = RHDRegRead(Output, LVTMA_CNTL);
|
|
Private->StoreSource = RHDRegRead(Output, LVTMA_SOURCE_SELECT);
|
|
Private->StoreFormat = RHDRegRead(Output, LVTMA_COLOR_FORMAT);
|
|
Private->StoreForce = RHDRegRead(Output, LVTMA_FORCE_OUTPUT_CNTL);
|
|
Private->StoreReduction = RHDRegRead(Output, LVTMA_BIT_DEPTH_CONTROL);
|
|
Private->StoreDCBalancer = RHDRegRead(Output, LVTMA_DCBALANCER_CONTROL);
|
|
|
|
Private->StoreDataSynchro = RHDRegRead(Output, LVTMA_DATA_SYNCHRONIZATION);
|
|
Private->StoreMode = RHDRegRead(Output, LVTMA_MODE);
|
|
Private->StoreTXEnable = RHDRegRead(Output, LVTMA_TRANSMITTER_ENABLE);
|
|
Private->StoreMacro = RHDRegRead(Output, LVTMA_MACRO_CONTROL);
|
|
Private->StoreTXControl = RHDRegRead(Output, LVTMA_TRANSMITTER_CONTROL);
|
|
Private->StoreTestOutput = RHDRegRead(Output, LVTMA_REG_TEST_OUTPUT);
|
|
|
|
if (rhdPtr->ChipSet > RHD_R600) { /* Rv6x0 */
|
|
Private->StoreRv600TXAdjust = RHDRegRead(Output, LVTMA_TRANSMITTER_ADJUST);
|
|
Private->StoreRv600PreEmphasis = RHDRegRead(Output, LVTMA_PREEMPHASIS_CONTROL);
|
|
}
|
|
|
|
RHDHdmiSave(Private->Hdmi);
|
|
|
|
Private->Stored = TRUE;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static void
|
|
TMDSBRestore(struct rhdOutput *Output)
|
|
{
|
|
struct rhdTMDSBPrivate *Private = (struct rhdTMDSBPrivate *) Output->Private;
|
|
RHDPtr rhdPtr = RHDPTRI(Output);
|
|
|
|
RHDFUNC(Output);
|
|
|
|
if (!Private->Stored) {
|
|
xf86DrvMsg(Output->scrnIndex, X_ERROR,
|
|
"%s: No registers stored.\n", __func__);
|
|
return;
|
|
}
|
|
|
|
RHDRegWrite(Output, LVTMA_CNTL, Private->StoreControl);
|
|
RHDRegWrite(Output, LVTMA_SOURCE_SELECT, Private->StoreSource);
|
|
RHDRegWrite(Output, LVTMA_COLOR_FORMAT, Private->StoreFormat);
|
|
RHDRegWrite(Output, LVTMA_FORCE_OUTPUT_CNTL, Private->StoreForce);
|
|
RHDRegWrite(Output, LVTMA_BIT_DEPTH_CONTROL, Private->StoreReduction);
|
|
RHDRegWrite(Output, LVTMA_DCBALANCER_CONTROL, Private->StoreDCBalancer);
|
|
|
|
RHDRegWrite(Output, LVTMA_DATA_SYNCHRONIZATION, Private->StoreDataSynchro);
|
|
RHDRegWrite(Output, LVTMA_MODE, Private->StoreMode);
|
|
RHDRegWrite(Output, LVTMA_TRANSMITTER_ENABLE, Private->StoreTXEnable);
|
|
RHDRegWrite(Output, LVTMA_MACRO_CONTROL, Private->StoreMacro);
|
|
RHDRegWrite(Output, LVTMA_TRANSMITTER_CONTROL, Private->StoreTXControl);
|
|
RHDRegWrite(Output, LVTMA_REG_TEST_OUTPUT, Private->StoreTestOutput);
|
|
|
|
if (rhdPtr->ChipSet > RHD_R600) { /* Rv6x0 */
|
|
RHDRegWrite(Output, LVTMA_TRANSMITTER_ADJUST, Private->StoreRv600TXAdjust);
|
|
RHDRegWrite(Output, LVTMA_PREEMPHASIS_CONTROL, Private->StoreRv600PreEmphasis);
|
|
}
|
|
|
|
RHDHdmiRestore(Private->Hdmi);
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static void
|
|
TMDSBDestroy(struct rhdOutput *Output)
|
|
{
|
|
struct rhdTMDSBPrivate *Private = (struct rhdTMDSBPrivate *) Output->Private;
|
|
RHDFUNC(Output);
|
|
|
|
if (!Private)
|
|
return;
|
|
|
|
RHDHdmiDestroy(Private->Hdmi);
|
|
|
|
xfree(Private);
|
|
Output->Private = NULL;
|
|
}
|
|
|
|
#ifdef NOT_YET
|
|
static Bool
|
|
LVDSPropertyWrapper(struct rhdOutput *Output,
|
|
enum rhdPropertyAction Action,
|
|
enum rhdOutputProperty Property,
|
|
union rhdPropertyData *val)
|
|
{
|
|
struct LVDSPrivate *Private = (struct LVDSPrivate *) Output->Private;
|
|
void *storePrivate = Output->Private;
|
|
Bool (*func)(struct rhdOutput *,enum rhdPropertyAction, enum rhdOutputProperty,
|
|
union rhdPropertyData *) = Private->WrappedPropertyCallback;
|
|
Bool ret;
|
|
|
|
Output->Private = Private->PropertyPrivate;
|
|
ret = func(Output, Action, Property, val);
|
|
Output->Private = storePrivate;
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
*
|
|
*/
|
|
struct rhdOutput *
|
|
RHDLVTMAInit(RHDPtr rhdPtr, CARD8 Type)
|
|
{
|
|
struct rhdOutput *Output;
|
|
|
|
RHDFUNC(rhdPtr);
|
|
|
|
/* Stop weird connector types */
|
|
if ((Type != RHD_CONNECTOR_PANEL)
|
|
&& (Type != RHD_CONNECTOR_DVI)
|
|
&& (Type != RHD_CONNECTOR_DVI_SINGLE)) {
|
|
xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "%s: unhandled connector type:"
|
|
" %d\n", __func__, Type);
|
|
return NULL;
|
|
}
|
|
|
|
Output = xnfcalloc(sizeof(struct rhdOutput), 1);
|
|
|
|
Output->scrnIndex = rhdPtr->scrnIndex;
|
|
Output->Id = RHD_OUTPUT_LVTMA;
|
|
|
|
Output->Sense = NULL; /* not implemented in hw */
|
|
|
|
if (Type == RHD_CONNECTOR_PANEL) {
|
|
struct LVDSPrivate *Private;
|
|
|
|
Output->Name = "LVDS";
|
|
|
|
Output->ModeValid = LVDSModeValid;
|
|
Output->Mode = LVDSSet;
|
|
Output->Power = LVDSPower;
|
|
Output->Save = LVDSSave;
|
|
Output->Restore = LVDSRestore;
|
|
Output->Property = LVDSPropertyControl;
|
|
Output->Destroy = LVDSDestroy;
|
|
Output->Private = Private = LVDSInfoRetrieve(rhdPtr);
|
|
#ifdef NOT_YET
|
|
if (Private->BlLevel < 0) {
|
|
Private->BlLevel = RhdAtomSetupBacklightControlProperty(Output, &Private->WrappedPropertyCallback,
|
|
&Private->PropertyPrivate);
|
|
if (Private->PropertyPrivate)
|
|
Output->Property = LVDSPropertyWrapper;
|
|
} else
|
|
#else
|
|
if (Private->BlLevel >= 0)
|
|
#endif
|
|
LVDSDebugBacklight(Output);
|
|
|
|
} else {
|
|
struct rhdTMDSBPrivate *Private = xnfcalloc(sizeof(struct rhdTMDSBPrivate), 1);
|
|
|
|
Output->Name = "TMDS B";
|
|
|
|
Output->ModeValid = TMDSBModeValid;
|
|
Output->Mode = TMDSBSet;
|
|
Output->Power = TMDSBPower;
|
|
Output->Save = TMDSBSave;
|
|
Output->Restore = TMDSBRestore;
|
|
Output->Property = TMDSBPropertyControl;
|
|
Output->Destroy = TMDSBDestroy;
|
|
|
|
Private->Hdmi = RHDHdmiInit(rhdPtr, Output);
|
|
Output->Private = Private;
|
|
|
|
Private->RunsDualLink = FALSE;
|
|
Private->Coherent = FALSE;
|
|
}
|
|
|
|
return Output;
|
|
}
|