kolibrios-fun/programs/system/drivers/rhd/rhd_dac.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

1100 lines
30 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>
# include <string.h>
# include <stdio.h>
#endif
#include "rhd.h"
#include "rhd_connector.h"
#include "rhd_output.h"
#include "rhd_crtc.h"
#include "rhd_regs.h"
#ifdef ATOM_BIOS
# include "rhd_atombios.h"
#endif
#define REG_DACA_OFFSET 0
#define RV620_REG_DACA_OFFSET 0
#define REG_DACB_OFFSET 0x200
#define RV620_REG_DACB_OFFSET 0x100
struct rhdDACPrivate {
Bool Stored;
CARD32 Store_Powerdown;
CARD32 Store_Force_Output_Control;
CARD32 Store_Force_Data;
CARD32 Store_Source_Select;
CARD32 Store_Sync_Select;
CARD32 Store_Enable;
CARD32 Store_Control1;
CARD32 Store_Control2;
CARD32 Store_Tristate_Control;
CARD32 Store_Auto_Calib_Control;
CARD32 Store_Dac_Bgadj_Src;
};
/* ----------------------------------------------------------- */
/*
*
*/
static unsigned char
DACSense(struct rhdOutput *Output, CARD32 offset, Bool TV)
{
CARD32 CompEnable, Control1, Control2, DetectControl, Enable;
CARD8 ret;
CompEnable = RHDRegRead(Output, offset + DACA_COMPARATOR_ENABLE);
Control1 = RHDRegRead(Output, offset + DACA_CONTROL1);
Control2 = RHDRegRead(Output, offset + DACA_CONTROL2);
DetectControl = RHDRegRead(Output, offset + DACA_AUTODETECT_CONTROL);
Enable = RHDRegRead(Output, offset + DACA_ENABLE);
RHDRegWrite(Output, offset + DACA_ENABLE, 1);
/* ack autodetect */
RHDRegMask(Output, offset + DACA_AUTODETECT_INT_CONTROL, 0x01, 0x01);
RHDRegMask(Output, offset + DACA_AUTODETECT_CONTROL, 0, 0x00000003);
RHDRegMask(Output, offset + DACA_CONTROL2, 0, 0x00000001);
RHDRegMask(Output, offset + DACA_CONTROL2, 0, 0x00ff0000);
if (offset) { /* We can do TV on DACA but only DACB has mux for separate connector */
if (TV)
RHDRegMask(Output, offset + DACA_CONTROL2, 0x00000100, 0x00000100);
else
RHDRegMask(Output, offset + DACA_CONTROL2, 0, 0x00000100);
}
RHDRegWrite(Output, offset + DACA_FORCE_DATA, 0);
RHDRegMask(Output, offset + DACA_CONTROL2, 0x00000001, 0x0000001);
RHDRegMask(Output, offset + DACA_COMPARATOR_ENABLE, 0x00070000, 0x00070101);
RHDRegWrite(Output, offset + DACA_CONTROL1, 0x00050802);
RHDRegMask(Output, offset + DACA_POWERDOWN, 0, 0x00000001); /* Shut down Bandgap Voltage Reference Power */
usleep(5);
RHDRegMask(Output, offset + DACA_POWERDOWN, 0, 0x01010100); /* Shut down RGB */
RHDRegWrite(Output, offset + DACA_FORCE_DATA, 0x1e6); /* 486 out of 1024 */
usleep(200);
RHDRegMask(Output, offset + DACA_POWERDOWN, 0x01010100, 0x01010100); /* Enable RGB */
usleep(88);
RHDRegMask(Output, offset + DACA_POWERDOWN, 0, 0x01010100); /* Shut down RGB */
RHDRegMask(Output, offset + DACA_COMPARATOR_ENABLE, 0x00000100, 0x00000100);
usleep(100);
/* Get RGB detect values
* If only G is detected, we could have a monochrome monitor,
* but we don't bother with this at the moment.
*/
ret = (RHDRegRead(Output, offset + DACA_COMPARATOR_OUTPUT) & 0x0E) >> 1;
RHDRegMask(Output, offset + DACA_COMPARATOR_ENABLE, CompEnable, 0x00FFFFFF);
RHDRegWrite(Output, offset + DACA_CONTROL1, Control1);
RHDRegMask(Output, offset + DACA_CONTROL2, Control2, 0x000001FF);
RHDRegMask(Output, offset + DACA_AUTODETECT_CONTROL, DetectControl, 0x000000FF);
RHDRegMask(Output, offset + DACA_ENABLE, Enable, 0x000000FF);
RHDDebug(Output->scrnIndex, "%s: DAC: 0x0%1X\n", __func__, ret);
return ret;
}
/*
*
*/
static enum rhdSensedOutput
DACASense(struct rhdOutput *Output, struct rhdConnector *Connector)
{
enum rhdConnectorType Type = Connector->Type;
RHDFUNC(Output);
switch (Type) {
case RHD_CONNECTOR_DVI:
case RHD_CONNECTOR_DVI_SINGLE:
case RHD_CONNECTOR_VGA:
return (DACSense(Output, REG_DACA_OFFSET, FALSE) == 0x7)
? RHD_SENSED_VGA
: RHD_SENSED_NONE;
default:
xf86DrvMsg(Output->scrnIndex, X_WARNING,
"%s: connector type %d is not supported on DACA.\n",
__func__, Type);
return RHD_SENSED_NONE;
}
}
/*
*
*/
static enum rhdSensedOutput
DACBSense(struct rhdOutput *Output, struct rhdConnector *Connector)
{
enum rhdConnectorType Type = Connector->Type;
RHDFUNC(Output);
switch (Type) {
case RHD_CONNECTOR_DVI:
case RHD_CONNECTOR_DVI_SINGLE:
case RHD_CONNECTOR_VGA:
return (DACSense(Output, REG_DACB_OFFSET, FALSE) == 0x7)
? RHD_SENSED_VGA
: RHD_SENSED_NONE;
case RHD_CONNECTOR_TV:
switch (DACSense(Output, REG_DACB_OFFSET, TRUE) & 0x7) {
case 0x7:
return RHD_SENSED_TV_COMPONENT;
case 0x6:
return RHD_SENSED_TV_SVIDEO;
case 0x1:
return RHD_SENSED_TV_COMPOSITE;
default:
return RHD_SENSED_NONE;
}
default:
xf86DrvMsg(Output->scrnIndex, X_WARNING,
"%s: connector type %d is not supported on DACB.\n",
__func__, Type);
return RHD_SENSED_NONE;
}
}
enum outputType {
TvPAL = 0,
TvNTSC,
VGA,
TvCV,
typeLast = VGA
};
/*
*
*/
static void
DACGetElectrical(RHDPtr rhdPtr, enum outputType type, int dac, CARD8 *bandgap, CARD8 *whitefine)
{
#ifdef ATOM_BIOS
enum _AtomBiosRequestID bg = 0, wf = 0;
AtomBiosArgRec atomBiosArg;
#endif
struct
{
CARD16 pciIdMin;
CARD16 pciIdMax;
CARD8 bandgap[2][4];
CARD8 whitefine[2][4];
} list[] = {
{ 0x791E, 0x791F,
{ { 0x07, 0x07, 0x07, 0x07 },
{ 0x07, 0x07, 0x07, 0x07 } },
{ { 0x09, 0x09, 0x04, 0x09 },
{ 0x09, 0x09, 0x04, 0x09 } },
},
{ 0x793F, 0x7942,
{ { 0x09, 0x09, 0x09, 0x09 },
{ 0x09, 0x09, 0x09, 0x09 } },
{ { 0x0a, 0x0a, 0x08, 0x0a },
{ 0x0a, 0x0a, 0x08, 0x0a } },
},
{ 0x9500, 0x9519,
{ { 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00 } },
{ { 0x00, 0x00, 0x20, 0x00 },
{ 0x25, 0x25, 0x26, 0x26 } },
},
{ 0, 0,
{ { 0, 0, 0, 0 },
{ 0, 0, 0, 0 } },
{ { 0, 0, 0, 0 },
{ 0, 0, 0, 0 } }
}
};
*bandgap = *whitefine = 0;
#ifdef ATOM_BIOS
switch (type) {
case TvPAL:
bg = ATOM_DAC2_PAL_BG_ADJ;
wf = ATOM_DAC2_PAL_DAC_ADJ;
break;
case TvNTSC:
bg = ATOM_DAC2_NTSC_BG_ADJ;
wf = ATOM_DAC2_NTSC_DAC_ADJ;
break;
case TvCV:
bg = ATOM_DAC2_CV_BG_ADJ;
wf = ATOM_DAC2_CV_DAC_ADJ;
break;
case VGA:
switch (dac) {
case 0:
bg = ATOM_DAC1_BG_ADJ;
wf = ATOM_DAC1_DAC_ADJ;
break;
default:
bg = ATOM_DAC2_CRTC2_BG_ADJ;
wf = ATOM_DAC2_CRTC2_DAC_ADJ;
break;
}
break;
}
if (RHDAtomBiosFunc(rhdPtr->scrnIndex, rhdPtr->atomBIOS, bg, &atomBiosArg)
== ATOM_SUCCESS) {
*bandgap = atomBiosArg.val;
RHDDebug(rhdPtr->scrnIndex, "%s: BandGap found in CompassionateData.\n",__func__);
}
if (RHDAtomBiosFunc(rhdPtr->scrnIndex, rhdPtr->atomBIOS, wf, &atomBiosArg)
== ATOM_SUCCESS) {
*whitefine = atomBiosArg.val;
RHDDebug(rhdPtr->scrnIndex, "%s: WhiteFine found in CompassionateData.\n",__func__);
}
if (*whitefine == 0) {
CARD8 w_f = 0, b_g = 0;
if (atomBiosArg.val = 0x18,
RHDAtomBiosFunc(rhdPtr->scrnIndex, rhdPtr->atomBIOS,
ATOMBIOS_GET_CODE_DATA_TABLE,
&atomBiosArg) == ATOM_SUCCESS) {
struct AtomDacCodeTableData *data
= (struct AtomDacCodeTableData *)atomBiosArg.CommandDataTable.loc;
if (atomBiosArg.CommandDataTable.size
< (sizeof (struct AtomDacCodeTableData) >> (dac ? 0 : 1))) { /* IGPs only have 1 DAC -> table_size / 2 */
xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR,
"Code table data size: %i doesn't match expected size: %u\n",
atomBiosArg.CommandDataTable.size,
(unsigned int) sizeof (struct AtomDacCodeTableData));
return;
}
RHDDebug(rhdPtr->scrnIndex, "%s: WhiteFine found in Code Table.\n",__func__);
switch (type) {
case TvPAL:
w_f = dac ? data->DAC2PALWhiteFine : data->DAC1PALWhiteFine;
b_g = dac ? data->DAC2PALBandGap : data->DAC1PALBandGap;
break;
case TvNTSC:
w_f = dac ? data->DAC2NTSCWhiteFine : data->DAC1NTSCWhiteFine;
b_g = dac ? data->DAC2NTSCBandGap : data->DAC1NTSCBandGap;
break;
case TvCV:
w_f = dac ? data->DAC2CVWhiteFine : data->DAC1CVWhiteFine;
b_g = dac ? data->DAC2CVBandGap : data->DAC1CVBandGap;
break;
case VGA:
w_f = dac ? data->DAC2VGAWhiteFine : data->DAC1VGAWhiteFine;
b_g = dac ? data->DAC2VGABandGap : data->DAC1VGABandGap;
break;
}
*whitefine = w_f;
if (rhdPtr->ChipSet >= RHD_RV770) /* Dunno why this is broken on older ASICs */
*bandgap = b_g;
}
}
#endif
if (*bandgap == 0 || *whitefine == 0) {
int i = 0;
while (list[i].pciIdMin != 0) {
if (list[i].pciIdMin <= rhdPtr->PciDeviceID
&& list[i].pciIdMax >= rhdPtr->PciDeviceID) {
#if 0
ErrorF(">> %x %x %x -- %x %x\n",list[i].pciIdMin,
rhdPtr->PciDeviceID,list[i].pciIdMax,
list[i].bandgap[dac][type],list[i].whitefine[dac][type]);
ErrorF(">> %i %i\n",dac,type);
#endif
if (*bandgap == 0) *bandgap = list[i].bandgap[dac][type];
if (*whitefine == 0) *whitefine = list[i].whitefine[dac][type];
break;
}
i++;
}
if (list[i].pciIdMin != 0)
RHDDebug(rhdPtr->scrnIndex, "%s: BandGap and WhiteFine found in Table.\n",__func__);
}
RHDDebug(rhdPtr->scrnIndex, "%s: DAC[%i] BandGap: 0x%2.2x WhiteFine: 0x%2.2x\n",
__func__, dac, *bandgap, *whitefine);
}
/*
*
*/
static inline void
DACSet(struct rhdOutput *Output, CARD16 offset)
{
RHDPtr rhdPtr = RHDPTRI(Output);
CARD8 Standard, WhiteFine, Bandgap;
Bool TV;
CARD32 Mask = 0;
switch (Output->SensedType) {
case RHD_SENSED_TV_SVIDEO:
case RHD_SENSED_TV_COMPOSITE:
/* might want to selectively enable lines based on type */
TV = TRUE;
switch (rhdPtr->tvMode) {
case RHD_TV_NTSC:
case RHD_TV_NTSCJ:
DACGetElectrical(rhdPtr, TvNTSC, offset ? 1 : 0, &Bandgap, &WhiteFine);
Standard = 1; /* NTSC */
break;
case RHD_TV_PAL:
case RHD_TV_PALN:
case RHD_TV_PALCN:
case RHD_TV_PAL60:
default:
DACGetElectrical(rhdPtr, TvPAL, offset ? 1 : 0, &Bandgap, &WhiteFine);
Standard = 0; /* PAL */
break;
}
break;
case RHD_SENSED_TV_COMPONENT:
TV = TRUE;
DACGetElectrical(rhdPtr, TvCV, offset ? 1 : 0, &Bandgap, &WhiteFine);
Standard = 3; /* HDTV */
break;
case RHD_SENSED_VGA:
default:
TV = FALSE;
DACGetElectrical(rhdPtr, VGA, offset ? 1 : 0, &Bandgap, &WhiteFine);
Standard = 2; /* VGA */
break;
}
if (Bandgap) Mask |= 0xFF << 16;
if (WhiteFine) Mask |= 0xFF << 8;
RHDRegMask(Output, offset + DACA_CONTROL1, Standard, 0x000000FF);
/* white level fine adjust */
RHDRegMask(Output, offset + DACA_CONTROL1, (Bandgap << 16) | (WhiteFine << 8), Mask);
if (TV) {
/* tv enable */
if (offset) /* TV mux only available on DACB */
RHDRegMask(Output, offset + DACA_CONTROL2, 0x00000100, 0x0000FF00);
/* select tv encoder */
RHDRegMask(Output, offset + DACA_SOURCE_SELECT, 0x00000002, 0x00000003);
} else {
if (offset) /* TV mux only available on DACB */
RHDRegMask(Output, offset + DACA_CONTROL2, 0, 0x0000FF00);
/* select a crtc */
RHDRegMask(Output, offset + DACA_SOURCE_SELECT, Output->Crtc->Id & 0x01, 0x00000003);
}
RHDRegMask(Output, offset + DACA_FORCE_OUTPUT_CNTL, 0x00000701, 0x00000701);
RHDRegMask(Output, offset + DACA_FORCE_DATA, 0, 0x0000FFFF);
}
/*
*
*/
static void
DACASet(struct rhdOutput *Output, DisplayModePtr unused)
{
RHDFUNC(Output);
DACSet(Output, REG_DACA_OFFSET);
}
/*
*
*/
static void
DACBSet(struct rhdOutput *Output, DisplayModePtr unused)
{
RHDFUNC(Output);
DACSet(Output, REG_DACB_OFFSET);
}
/*
*
*/
static inline void
DACPower(struct rhdOutput *Output, CARD16 offset, int Power)
{
CARD32 powerdown;
RHDDebug(Output->scrnIndex, "%s(%s,%s)\n",__func__,Output->Name,
rhdPowerString[Power]);
switch (Power) {
case RHD_POWER_ON:
switch (Output->SensedType) {
case RHD_SENSED_TV_SVIDEO:
powerdown = 0 /* 0x100 */;
break;
case RHD_SENSED_TV_COMPOSITE:
powerdown = 0 /* 0x1010000 */;
break;
case RHD_SENSED_TV_COMPONENT:
powerdown = 0;
break;
case RHD_SENSED_VGA:
default:
powerdown = 0;
break;
}
RHDRegWrite(Output, offset + DACA_ENABLE, 1);
RHDRegWrite(Output, offset + DACA_POWERDOWN, 0);
usleep (14);
RHDRegMask(Output, offset + DACA_POWERDOWN, powerdown, 0xFFFFFF00);
usleep(2);
RHDRegWrite(Output, offset + DACA_FORCE_OUTPUT_CNTL, 0);
RHDRegMask(Output, offset + DACA_SYNC_SELECT, 0, 0x00000101);
RHDRegWrite(Output, offset + DACA_SYNC_TRISTATE_CONTROL, 0);
return;
case RHD_POWER_RESET: /* don't bother */
return;
case RHD_POWER_SHUTDOWN:
default:
RHDRegMask(Output, offset + DACA_FORCE_DATA, 0, 0x0000FFFF);
RHDRegMask(Output, offset + DACA_FORCE_OUTPUT_CNTL, 0x0000701, 0x0000701);
RHDRegWrite(Output, offset + DACA_POWERDOWN, 0x01010100);
RHDRegWrite(Output, offset + DACA_POWERDOWN, 0x01010101);
RHDRegWrite(Output, offset + DACA_ENABLE, 0);
RHDRegWrite(Output, offset + DACA_ENABLE, 0);
return;
}
}
/*
*
*/
static void
DACAPower(struct rhdOutput *Output, int Power)
{
RHDFUNC(Output);
DACPower(Output, REG_DACA_OFFSET, Power);
}
/*
*
*/
static void
DACBPower(struct rhdOutput *Output, int Power)
{
RHDFUNC(Output);
DACPower(Output, REG_DACB_OFFSET, Power);
}
/*
*
*/
static inline void
DACSave(struct rhdOutput *Output, CARD16 offset)
{
struct rhdDACPrivate *Private = (struct rhdDACPrivate *) Output->Private;
Private->Store_Powerdown = RHDRegRead(Output, offset + DACA_POWERDOWN);
Private->Store_Force_Output_Control = RHDRegRead(Output, offset + DACA_FORCE_OUTPUT_CNTL);
Private->Store_Force_Data = RHDRegRead(Output, offset + DACA_FORCE_DATA);
Private->Store_Source_Select = RHDRegRead(Output, offset + DACA_SOURCE_SELECT);
Private->Store_Sync_Select = RHDRegRead(Output, offset + DACA_SYNC_SELECT);
Private->Store_Enable = RHDRegRead(Output, offset + DACA_ENABLE);
Private->Store_Control1 = RHDRegRead(Output, offset + DACA_CONTROL1);
Private->Store_Control2 = RHDRegRead(Output, offset + DACA_CONTROL2);
Private->Store_Tristate_Control = RHDRegRead(Output, offset + DACA_SYNC_TRISTATE_CONTROL);
Private->Stored = TRUE;
}
/*
*
*/
static void
DACASave(struct rhdOutput *Output)
{
RHDFUNC(Output);
DACSave(Output, REG_DACA_OFFSET);
}
/*
*
*/
static void
DACBSave(struct rhdOutput *Output)
{
RHDFUNC(Output);
DACSave(Output, REG_DACB_OFFSET);
}
/*
*
*/
static inline void
DACRestore(struct rhdOutput *Output, CARD16 offset)
{
struct rhdDACPrivate *Private = (struct rhdDACPrivate *) Output->Private;
RHDRegWrite(Output, offset + DACA_POWERDOWN, Private->Store_Powerdown);
RHDRegWrite(Output, offset + DACA_FORCE_OUTPUT_CNTL, Private->Store_Force_Output_Control);
RHDRegWrite(Output, offset + DACA_FORCE_DATA, Private->Store_Force_Data);
RHDRegWrite(Output, offset + DACA_SOURCE_SELECT, Private->Store_Source_Select);
RHDRegWrite(Output, offset + DACA_SYNC_SELECT, Private->Store_Sync_Select);
RHDRegWrite(Output, offset + DACA_ENABLE, Private->Store_Enable);
RHDRegWrite(Output, offset + DACA_CONTROL1, Private->Store_Control1);
RHDRegWrite(Output, offset + DACA_CONTROL2, Private->Store_Control2);
RHDRegWrite(Output, offset + DACA_SYNC_TRISTATE_CONTROL, Private->Store_Tristate_Control);
}
/*
*
*/
static void
DACARestore(struct rhdOutput *Output)
{
RHDFUNC(Output);
if (!((struct rhdDACPrivate *) Output->Private)->Stored) {
xf86DrvMsg(Output->scrnIndex, X_ERROR,
"%s: No registers stored.\n", __func__);
return;
}
DACRestore(Output, REG_DACA_OFFSET);
}
/*
*
*/
static void
DACBRestore(struct rhdOutput *Output)
{
RHDFUNC(Output);
if (!((struct rhdDACPrivate *) Output->Private)->Stored) {
xf86DrvMsg(Output->scrnIndex, X_ERROR,
"%s: No registers stored.\n", __func__);
return;
}
DACRestore(Output, REG_DACB_OFFSET);
}
/* ----------------------------------------------------------- */
/*
*
*/
static CARD32
DACSenseRV620(struct rhdOutput *Output, CARD32 offset, Bool TV)
{
CARD32 ret;
CARD32 DetectControl, AutodetectIntCtl, ForceData,
Control1, Control2, CompEnable;
RHDFUNC(Output);
Control1 = RHDRegRead(Output, offset + RV620_DACA_MACRO_CNTL); /* 7ef4 */
Control2 = RHDRegRead(Output, offset + RV620_DACA_CONTROL2); /* 7058 */
ForceData = RHDRegRead(Output, offset + RV620_DACA_FORCE_DATA);
AutodetectIntCtl = RHDRegRead(Output, offset + RV620_DACA_AUTODETECT_INT_CONTROL);
DetectControl = RHDRegRead(Output, offset + RV620_DACA_AUTODETECT_CONTROL);
CompEnable = RHDRegRead(Output, offset + RV620_DACA_COMPARATOR_ENABLE);
if (offset) { /* We can do TV on DACA but only DACB has mux for separate connector */
if (TV)
RHDRegMask(Output, offset + RV620_DACA_CONTROL2, 0x100, 0xff00);
else
RHDRegMask(Output, offset + RV620_DACA_CONTROL2, 0x00, 0xff00);
}
RHDRegMask(Output, offset + RV620_DACA_FORCE_DATA, 0x18, 0xffff);
RHDRegMask(Output, offset + RV620_DACA_AUTODETECT_INT_CONTROL, 0x01, 0x01);
RHDRegMask(Output, offset + RV620_DACA_AUTODETECT_CONTROL, 0x00, 0xff);
RHDRegMask(Output, offset + RV620_DACA_MACRO_CNTL,
(offset > 0) ? 0x2502 : 0x2002, 0xffff);
/* enable comparators for R/G/B, disable DDET and SDET reference */
RHDRegMask(Output, offset + RV620_DACA_COMPARATOR_ENABLE, 0x70000, 0x070101);
RHDRegMask(Output, offset + RV620_DACA_AUTODETECT_CONTROL, 0x01, 0xff);
usleep(32);
ret = RHDRegRead(Output, offset + RV620_DACA_AUTODETECT_STATUS);
RHDRegWrite(Output, offset + RV620_DACA_AUTODETECT_CONTROL, DetectControl);
RHDRegWrite(Output, offset + RV620_DACA_MACRO_CNTL, Control1);
RHDRegWrite(Output, offset + RV620_DACA_CONTROL2, Control2);
RHDRegWrite(Output, offset + RV620_DACA_FORCE_DATA, ForceData);
RHDRegWrite(Output, offset + RV620_DACA_AUTODETECT_INT_CONTROL, AutodetectIntCtl);
#ifdef DEBUG
RHDDebug(Output->scrnIndex, "DAC%i: ret = 0x%x %s\n",offset ? "A" : "B",
ret,TV ? "TV" : "");
#endif
return ret;
}
/*
*
*/
static enum rhdSensedOutput
DACASenseRV620(struct rhdOutput *Output, struct rhdConnector *Connector)
{
enum rhdConnectorType Type = Connector->Type;
RHDFUNC(Output);
switch (Type) {
case RHD_CONNECTOR_DVI:
case RHD_CONNECTOR_DVI_SINGLE:
case RHD_CONNECTOR_VGA:
return (DACSenseRV620(Output, RV620_REG_DACA_OFFSET, FALSE)
& 0x1010100) ? RHD_SENSED_VGA : RHD_SENSED_NONE;
case RHD_CONNECTOR_TV:
switch (DACSenseRV620(Output, RV620_REG_DACA_OFFSET, TRUE)
& 0x1010100) {
case 0x1010100:
return RHD_SENSED_NONE; /* on DAC A we cannot distinguish VGA and CV */
case 0x10100:
return RHD_SENSED_TV_SVIDEO;
case 0x1000000:
return RHD_SENSED_TV_COMPOSITE;
default:
return RHD_SENSED_NONE;
}
default:
xf86DrvMsg(Output->scrnIndex, X_WARNING,
"%s: connector type %d is not supported.\n",
__func__, Type);
return RHD_SENSED_NONE;
}
}
/*
*
*/
static enum rhdSensedOutput
DACBSenseRV620(struct rhdOutput *Output, struct rhdConnector *Connector)
{
enum rhdConnectorType Type = Connector->Type;
RHDFUNC(Output);
switch (Type) {
case RHD_CONNECTOR_DVI:
case RHD_CONNECTOR_DVI_SINGLE:
case RHD_CONNECTOR_VGA:
return (DACSenseRV620(Output, RV620_REG_DACB_OFFSET, FALSE)
& 0x1010100) ? RHD_SENSED_VGA : RHD_SENSED_NONE;
case RHD_CONNECTOR_TV:
switch (DACSenseRV620(Output, RV620_REG_DACB_OFFSET, TRUE)
& 0x1010100) {
case 0x1000000:
return RHD_SENSED_TV_COMPONENT;
case 0x1010100:
return RHD_SENSED_TV_SVIDEO;
case 0x10100:
return RHD_SENSED_TV_COMPOSITE;
default:
return RHD_SENSED_NONE;
}
default:
xf86DrvMsg(Output->scrnIndex, X_WARNING,
"%s: connector type %d is not supported.\n",
__func__, Type);
return RHD_SENSED_NONE;
}
}
/*
*
*/
static inline void
DACSetRV620(struct rhdOutput *Output, CARD16 offset)
{
RHDPtr rhdPtr = RHDPTRI(Output);
CARD32 Source;
CARD32 Mode;
CARD32 TV;
CARD8 WhiteFine, Bandgap;
CARD32 Mask = 0;
switch (Output->SensedType) {
case RHD_SENSED_TV_SVIDEO:
case RHD_SENSED_TV_COMPOSITE:
TV = 0x1;
Source = 0x2; /* tv encoder */
switch (rhdPtr->tvMode) {
case RHD_TV_NTSC:
case RHD_TV_NTSCJ:
DACGetElectrical(rhdPtr, TvNTSC, offset ? 1 : 0, &Bandgap, &WhiteFine);
Mode = 1;
break;
case RHD_TV_PAL:
case RHD_TV_PALN:
case RHD_TV_PALCN:
case RHD_TV_PAL60:
default:
DACGetElectrical(rhdPtr, TvPAL, offset ? 1 : 0, &Bandgap, &WhiteFine);
Mode = 0;
break;
}
break;
case RHD_SENSED_TV_COMPONENT:
DACGetElectrical(rhdPtr, TvCV, offset ? 1 : 0, &Bandgap, &WhiteFine);
Mode = 3; /* HDTV */
TV = 0x1; /* tv on?? */
Source = 0x2; /* tv encoder ?? */
break;
case RHD_SENSED_VGA:
default:
DACGetElectrical(rhdPtr, VGA, offset ? 1 : 0, &Bandgap, &WhiteFine);
Mode = 2;
TV = 0;
Source = Output->Crtc->Id;
break;
}
if (Bandgap) Mask |= 0xFF << 16;
if (WhiteFine) Mask |= 0xFF << 8;
RHDRegMask(Output, offset + RV620_DACA_MACRO_CNTL, Mode, 0xFF); /* no fine control yet */
RHDRegMask(Output, offset + RV620_DACA_SOURCE_SELECT, Source, 0x00000003);
if (offset) /* TV mux only present on DACB */
RHDRegMask(Output, offset + RV620_DACA_CONTROL2, TV << 8, 0x0100); /* tv enable/disable */
/* use fine control from white_fine control register */
RHDRegMask(Output, offset + RV620_DACA_AUTO_CALIB_CONTROL, 0x0, 0x4);
RHDRegMask(Output, offset + RV620_DACA_BGADJ_SRC, 0x0, 0x30);
RHDRegMask(Output, offset + RV620_DACA_MACRO_CNTL, (Bandgap << 16) | (WhiteFine << 8), Mask);
/* Reset the FMT register on CRTC leading to this output */
Output->Crtc->FMTModeSet(Output->Crtc, NULL);
}
/*
*
*/
static void
DACASetRV620(struct rhdOutput *Output, DisplayModePtr unused)
{
RHDFUNC(Output);
DACSetRV620(Output, RV620_REG_DACA_OFFSET);
}
/*
*
*/
static void
DACBSetRV620(struct rhdOutput *Output, DisplayModePtr unused)
{
RHDFUNC(Output);
DACSetRV620(Output, RV620_REG_DACB_OFFSET);
}
/*
*
*/
static inline void
DACPowerRV620(struct rhdOutput *Output, CARD16 offset, int Power)
{
CARD32 powerdown;
switch (Power) {
case RHD_POWER_ON:
switch (Output->SensedType) {
case RHD_SENSED_TV_SVIDEO:
powerdown = 0 /* 0x100 */;
break;
case RHD_SENSED_TV_COMPOSITE:
powerdown = 0 /* 0x1010000 */;
break;
case RHD_SENSED_TV_COMPONENT:
powerdown = 0;
break;
case RHD_SENSED_VGA:
default:
powerdown = 0;
break;
}
if (!(RHDRegRead(Output, offset + RV620_DACA_ENABLE) & 0x01))
RHDRegMask(Output, offset + RV620_DACA_ENABLE, 0x1, 0xff);
RHDRegMask(Output, offset + RV620_DACA_FORCE_OUTPUT_CNTL, 0x01, 0x01);
RHDRegMask(Output, offset + RV620_DACA_POWERDOWN, 0x0, 0xff);
usleep (0x14);
RHDRegMask(Output, offset + RV620_DACA_POWERDOWN, powerdown, 0xffffff00);
usleep(2);
RHDRegMask(Output, offset + RV620_DACA_FORCE_DATA, 0, 0x0000ffff);
RHDRegWrite(Output, offset + RV620_DACA_FORCE_OUTPUT_CNTL, 0x0);
RHDRegWrite(Output, offset + RV620_DACA_SYNC_TRISTATE_CONTROL, 0);
return;
case RHD_POWER_RESET: /* don't bother */
return;
case RHD_POWER_SHUTDOWN:
default:
RHDRegWrite(Output, offset + RV620_DACA_POWERDOWN, 0x01010100);
RHDRegWrite(Output, offset + RV620_DACA_POWERDOWN, 0x01010101);
RHDRegWrite(Output, offset + RV620_DACA_ENABLE, 0);
RHDRegMask(Output, offset + RV620_DACA_FORCE_DATA, 0, 0xffff);
RHDRegMask(Output, offset + RV620_DACA_FORCE_OUTPUT_CNTL, 0x701, 0x701);
return;
}
}
/*
*
*/
static void
DACAPowerRV620(struct rhdOutput *Output, int Power)
{
RHDFUNC(Output);
DACPowerRV620(Output, RV620_REG_DACA_OFFSET, Power);
}
/*
*
*/
static void
DACBPowerRV620(struct rhdOutput *Output, int Power)
{
RHDFUNC(Output);
DACPowerRV620(Output, RV620_REG_DACB_OFFSET, Power);
}
/*
*
*/
static inline void
DACSaveRV620(struct rhdOutput *Output, CARD16 offset)
{
struct rhdDACPrivate *Private = (struct rhdDACPrivate *) Output->Private;
Private->Store_Powerdown = RHDRegRead(Output, offset + RV620_DACA_POWERDOWN);
Private->Store_Force_Output_Control = RHDRegRead(Output, offset + RV620_DACA_FORCE_OUTPUT_CNTL);
Private->Store_Force_Data = RHDRegRead(Output, offset + RV620_DACA_FORCE_DATA);
Private->Store_Source_Select = RHDRegRead(Output, offset + RV620_DACA_SOURCE_SELECT);
Private->Store_Enable = RHDRegRead(Output, offset + RV620_DACA_ENABLE);
Private->Store_Control1 = RHDRegRead(Output, offset + RV620_DACA_MACRO_CNTL);
Private->Store_Control2 = RHDRegRead(Output, offset + RV620_DACA_CONTROL2);
Private->Store_Tristate_Control = RHDRegRead(Output, offset + RV620_DACA_SYNC_TRISTATE_CONTROL);
Private->Store_Auto_Calib_Control = RHDRegRead(Output, offset + RV620_DACA_AUTO_CALIB_CONTROL);
Private->Store_Dac_Bgadj_Src = RHDRegRead(Output, offset + RV620_DACA_BGADJ_SRC);
Private->Stored = TRUE;
}
/*
*
*/
static void
DACASaveRV620(struct rhdOutput *Output)
{
RHDFUNC(Output);
DACSaveRV620(Output, RV620_REG_DACA_OFFSET);
}
/*
*
*/
static void
DACBSaveRV620(struct rhdOutput *Output)
{
RHDFUNC(Output);
DACSaveRV620(Output, RV620_REG_DACB_OFFSET);
}
/*
*
*/
static inline void
DACRestoreRV620(struct rhdOutput *Output, CARD16 offset)
{
struct rhdDACPrivate *Private = (struct rhdDACPrivate *) Output->Private;
RHDRegWrite(Output, offset + RV620_DACA_BGADJ_SRC, Private->Store_Dac_Bgadj_Src);
RHDRegWrite(Output, offset + RV620_DACA_AUTO_CALIB_CONTROL, Private->Store_Auto_Calib_Control);
RHDRegWrite(Output, offset + RV620_DACA_POWERDOWN, Private->Store_Powerdown);
RHDRegWrite(Output, offset + RV620_DACA_FORCE_OUTPUT_CNTL, Private->Store_Force_Output_Control);
RHDRegWrite(Output, offset + RV620_DACA_FORCE_DATA, Private->Store_Force_Data);
RHDRegWrite(Output, offset + RV620_DACA_SOURCE_SELECT, Private->Store_Source_Select);
RHDRegWrite(Output, offset + RV620_DACA_ENABLE, Private->Store_Enable);
RHDRegWrite(Output, offset + RV620_DACA_MACRO_CNTL, Private->Store_Control1);
RHDRegWrite(Output, offset + RV620_DACA_CONTROL2, Private->Store_Control2);
RHDRegWrite(Output, offset + RV620_DACA_SYNC_TRISTATE_CONTROL, Private->Store_Tristate_Control);
}
/*
*
*/
static void
DACARestoreRV620(struct rhdOutput *Output)
{
RHDFUNC(Output);
if (!((struct rhdDACPrivate *) Output->Private)->Stored) {
xf86DrvMsg(Output->scrnIndex, X_ERROR,
"%s: No registers stored.\n", __func__);
return;
}
DACRestoreRV620(Output, RV620_REG_DACA_OFFSET);
}
/*
*
*/
static void
DACBRestoreRV620(struct rhdOutput *Output)
{
RHDFUNC(Output);
if (!((struct rhdDACPrivate *) Output->Private)->Stored) {
xf86DrvMsg(Output->scrnIndex, X_ERROR,
"%s: No registers stored.\n", __func__);
return;
}
DACRestoreRV620(Output, RV620_REG_DACB_OFFSET);
}
/* ----------------------------------------------------------- */
/*
*
*/
static ModeStatus
DACModeValid(struct rhdOutput *Output, DisplayModePtr Mode)
{
RHDFUNC(Output);
if (Mode->Clock < 20000)
return MODE_CLOCK_LOW;
if (Mode->Clock > 400000)
return MODE_CLOCK_HIGH;
return MODE_OK;
}
/*
*
*/
static void
DACDestroy(struct rhdOutput *Output)
{
RHDFUNC(Output);
if (!Output->Private)
return;
xfree(Output->Private);
Output->Private = NULL;
}
/*
*
*/
struct rhdOutput *
RHDDACAInit(RHDPtr rhdPtr)
{
struct rhdOutput *Output;
struct rhdDACPrivate *Private;
RHDFUNC(rhdPtr);
Output = xnfcalloc(sizeof(struct rhdOutput), 1);
Output->scrnIndex = rhdPtr->scrnIndex;
Output->Name = "DAC A";
Output->Id = RHD_OUTPUT_DACA;
if (rhdPtr->ChipSet < RHD_RV620) {
Output->Sense = DACASense;
Output->Mode = DACASet;
Output->Power = DACAPower;
Output->Save = DACASave;
Output->Restore = DACARestore;
} else {
Output->Sense = DACASenseRV620;
Output->Mode = DACASetRV620;
Output->Power = DACAPowerRV620;
Output->Save = DACASaveRV620;
Output->Restore = DACARestoreRV620;
}
Output->ModeValid = DACModeValid;
Output->Destroy = DACDestroy;
Private = xnfcalloc(sizeof(struct rhdDACPrivate), 1);
Output->Private = Private;
return Output;
}
/*
*
*/
struct rhdOutput *
RHDDACBInit(RHDPtr rhdPtr)
{
struct rhdOutput *Output;
struct rhdDACPrivate *Private;
RHDFUNC(rhdPtr);
Output = xnfcalloc(sizeof(struct rhdOutput), 1);
Output->scrnIndex = rhdPtr->scrnIndex;
Output->Name = "DAC B";
Output->Id = RHD_OUTPUT_DACB;
if (rhdPtr->ChipSet < RHD_RV620) {
Output->Sense = DACBSense;
Output->Mode = DACBSet;
Output->Power = DACBPower;
Output->Save = DACBSave;
Output->Restore = DACBRestore;
} else {
Output->Sense = DACBSenseRV620;
Output->Mode = DACBSetRV620;
Output->Power = DACBPowerRV620;
Output->Save = DACBSaveRV620;
Output->Restore = DACBRestoreRV620;
}
Output->ModeValid = DACModeValid;
Output->Destroy = DACDestroy;
Private = xnfcalloc(sizeof(struct rhdDACPrivate), 1);
Output->Private = Private;
return Output;
}