radeon_hd: Add spread spectrum control functions

* Store SS information with PLL
* Probe SS information for PLL
* Disable SS more correctly
* May resolve mode setting issues on newer cards
This commit is contained in:
Alexander von Gluck IV 2012-03-22 16:55:01 -05:00
parent 67da9f0716
commit 151b499622
5 changed files with 329 additions and 2 deletions

View File

@ -631,7 +631,7 @@ connector_probe()
} }
// External encoders are behind DVO or UNIPHY // External encoders are behind DVO or UNIPHY
if(encoder_is_external(encoderID)) { if (encoder_is_external(encoderID)) {
encoder_info* encoder encoder_info* encoder
= &connector->encoderExternal; = &connector->encoderExternal;
encoder->isExternal = true; encoder->isExternal = true;

View File

@ -768,6 +768,136 @@ display_crtc_set_dtd(uint8 crtcID, display_mode* mode)
} }
void
display_crtc_ss(uint8 crtcID, int command)
{
TRACE("%s\n", __func__);
radeon_shared_info &info = *gInfo->shared_info;
int index = GetIndexIntoMasterTable(COMMAND, EnableSpreadSpectrumOnPPLL);
if (command != ATOM_DISABLE) {
ERROR("%s: TODO: SS was enabled, however functionality incomplete\n",
__func__);
command = ATOM_DISABLE;
}
union enableSS {
ENABLE_LVDS_SS_PARAMETERS lvds_ss;
ENABLE_LVDS_SS_PARAMETERS_V2 lvds_ss_2;
ENABLE_SPREAD_SPECTRUM_ON_PPLL_PS_ALLOCATION v1;
ENABLE_SPREAD_SPECTRUM_ON_PPLL_V2 v2;
ENABLE_SPREAD_SPECTRUM_ON_PPLL_V3 v3;
};
union enableSS args;
memset(&args, 0, sizeof(args));
uint32 connectorIndex = gDisplay[crtcID]->connectorIndex;
pll_info* pll = &gConnector[connectorIndex]->encoder.pll;
if (info.dceMajor >= 5) {
args.v3.usSpreadSpectrumAmountFrac = B_HOST_TO_LENDIAN_INT16(0);
args.v3.ucSpreadSpectrumType
= pll->ssType & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
switch (pll->id) {
case ATOM_PPLL1:
args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P1PLL;
args.v3.usSpreadSpectrumAmount
= B_HOST_TO_LENDIAN_INT16(pll->ssAmount);
args.v3.usSpreadSpectrumStep
= B_HOST_TO_LENDIAN_INT16(pll->ssStep);
break;
case ATOM_PPLL2:
args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P2PLL;
args.v3.usSpreadSpectrumAmount
= B_HOST_TO_LENDIAN_INT16(pll->ssAmount);
args.v3.usSpreadSpectrumStep
= B_HOST_TO_LENDIAN_INT16(pll->ssStep);
break;
case ATOM_DCPLL:
args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_DCPLL;
args.v3.usSpreadSpectrumAmount = B_HOST_TO_LENDIAN_INT16(0);
args.v3.usSpreadSpectrumStep = B_HOST_TO_LENDIAN_INT16(0);
break;
default:
ERROR("%s: BUG: Invalid PLL ID!\n", __func__);
return;
}
if (pll->ssPercentage == 0
|| ((pll->ssType & ATOM_EXTERNAL_SS_MASK) != 0)) {
command = ATOM_DISABLE;
}
args.v3.ucEnable = command;
} else if (info.dceMajor >= 4) {
args.v2.usSpreadSpectrumPercentage
= B_HOST_TO_LENDIAN_INT16(pll->ssPercentage);
args.v2.ucSpreadSpectrumType
= pll->ssType & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
switch (pll->id) {
case ATOM_PPLL1:
args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V2_P1PLL;
args.v2.usSpreadSpectrumAmount
= B_HOST_TO_LENDIAN_INT16(pll->ssAmount);
args.v2.usSpreadSpectrumStep
= B_HOST_TO_LENDIAN_INT16(pll->ssStep);
break;
case ATOM_PPLL2:
args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P2PLL;
args.v2.usSpreadSpectrumAmount
= B_HOST_TO_LENDIAN_INT16(pll->ssAmount);
args.v2.usSpreadSpectrumStep
= B_HOST_TO_LENDIAN_INT16(pll->ssStep);
break;
case ATOM_DCPLL:
args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_DCPLL;
args.v2.usSpreadSpectrumAmount = B_HOST_TO_LENDIAN_INT16(0);
args.v2.usSpreadSpectrumStep = B_HOST_TO_LENDIAN_INT16(0);
break;
default:
ERROR("%s: BUG: Invalid PLL ID!\n", __func__);
return;
}
if (pll->ssPercentage == 0
|| ((pll->ssType & ATOM_EXTERNAL_SS_MASK) != 0)
|| (info.chipsetFlags & CHIP_APU) != 0 ) {
command = ATOM_DISABLE;
}
args.v2.ucEnable = command;
} else if (info.dceMajor >= 3) {
args.v1.usSpreadSpectrumPercentage
= B_HOST_TO_LENDIAN_INT16(pll->ssPercentage);
args.v1.ucSpreadSpectrumType
= pll->ssType & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
args.v1.ucSpreadSpectrumStep = pll->ssStep;
args.v1.ucSpreadSpectrumDelay = pll->ssDelay;
args.v1.ucSpreadSpectrumRange = pll->ssRange;
args.v1.ucPpll = pll->id;
args.v1.ucEnable = command;
} else if (info.dceMajor >= 2) {
if ((command == ATOM_DISABLE) || (pll->ssPercentage == 0)
|| (pll->ssType & ATOM_EXTERNAL_SS_MASK)) {
// TODO: gpu_ss_disable needs pll id
radeon_gpu_ss_disable();
return;
}
args.lvds_ss_2.usSpreadSpectrumPercentage
= B_HOST_TO_LENDIAN_INT16(pll->ssPercentage);
args.lvds_ss_2.ucSpreadSpectrumType
= pll->ssType & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
args.lvds_ss_2.ucSpreadSpectrumStep = pll->ssStep;
args.lvds_ss_2.ucSpreadSpectrumDelay = pll->ssDelay;
args.lvds_ss_2.ucSpreadSpectrumRange = pll->ssRange;
args.lvds_ss_2.ucEnable = command;
} else {
ERROR("%s: TODO: Old card SS control\n", __func__);
return;
}
atom_execute_table(gAtomContext, index, (uint32*)&args);
}
void void
display_crtc_power(uint8 crtcID, int command) display_crtc_power(uint8 crtcID, int command)
{ {

View File

@ -20,6 +20,7 @@ status_t detect_displays();
void debug_displays(); void debug_displays();
uint32 display_get_encoder_mode(uint32 connectorIndex); uint32 display_get_encoder_mode(uint32 connectorIndex);
void display_crtc_ss(uint8 crtcID, int command);
void display_crtc_lock(uint8 crtcID, int command); void display_crtc_lock(uint8 crtcID, int command);
void display_crtc_blank(uint8 crtcID, int command); void display_crtc_blank(uint8 crtcID, int command);
void display_crtc_dpms(uint8 crtcID, int mode); void display_crtc_dpms(uint8 crtcID, int mode);

View File

@ -134,6 +134,177 @@ pll_limit_probe(pll_info* pll)
} }
status_t
pll_dp_ss_probe(pll_info* pll)
{
uint8 tableMajor;
uint8 tableMinor;
uint16 headerOffset;
uint16 headerSize;
int index = GetIndexIntoMasterTable(DATA, PPLL_SS_Info);
if (atom_parse_data_header(gAtomContext, index, &headerSize,
&tableMajor, &tableMinor, &headerOffset) != B_OK) {
ERROR("%s: Couldn't parse data header\n", __func__);
return B_ERROR;
}
struct _ATOM_SPREAD_SPECTRUM_INFO *ss_info
= (struct _ATOM_SPREAD_SPECTRUM_INFO*)((uint16*)gAtomContext->bios
+ headerOffset);
int indices = (headerSize - sizeof(ATOM_COMMON_TABLE_HEADER))
/ sizeof(ATOM_SPREAD_SPECTRUM_ASSIGNMENT);
int i;
for (i = 0; i < indices; i++) {
if (ss_info->asSS_Info[i].ucSS_Id == pll->id) {
pll->ssPercentage = B_LENDIAN_TO_HOST_INT16(
ss_info->asSS_Info[i].usSpreadSpectrumPercentage);
pll->ssType = ss_info->asSS_Info[i].ucSpreadSpectrumType;
pll->ssStep = ss_info->asSS_Info[i].ucSS_Step;
pll->ssDelay = ss_info->asSS_Info[i].ucSS_Delay;
pll->ssRange = ss_info->asSS_Info[i].ucSS_Range;
pll->ssReferenceDiv
= ss_info->asSS_Info[i].ucRecommendedRef_Div;
return B_OK;
}
}
pll->ssPercentage = 0;
pll->ssType = 0;
pll->ssStep = 0;
pll->ssDelay = 0;
pll->ssRange = 0;
pll->ssReferenceDiv = 0;
return B_ERROR;
}
status_t
pll_asic_ss_probe(pll_info* pll)
{
uint8 tableMajor;
uint8 tableMinor;
uint16 headerOffset;
uint16 headerSize;
int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info);
if (atom_parse_data_header(gAtomContext, index, &headerSize,
&tableMajor, &tableMinor, &headerOffset) != B_OK) {
ERROR("%s: Couldn't parse data header\n", __func__);
return B_ERROR;
}
union asicSSInfo {
struct _ATOM_ASIC_INTERNAL_SS_INFO info;
struct _ATOM_ASIC_INTERNAL_SS_INFO_V2 info_2;
struct _ATOM_ASIC_INTERNAL_SS_INFO_V3 info_3;
};
union asicSSInfo *ss_info
= (union asicSSInfo*)((uint16*)gAtomContext->bios + headerOffset);
int i;
int indices;
switch (tableMajor) {
case 1:
indices = (headerSize - sizeof(ATOM_COMMON_TABLE_HEADER))
/ sizeof(ATOM_ASIC_SS_ASSIGNMENT);
for (i = 0; i < indices; i++) {
if (ss_info->info.asSpreadSpectrum[i].ucClockIndication
!= pll->id) {
continue;
}
TRACE("%s: ss match found\n", __func__);
if (pll->pixelClock > B_LENDIAN_TO_HOST_INT32(
ss_info->info.asSpreadSpectrum[i].ulTargetClockRange)) {
TRACE("%s: pixelClock > targetClockRange!\n", __func__);
continue;
}
pll->ssPercentage = B_LENDIAN_TO_HOST_INT16(
ss_info->info.asSpreadSpectrum[i].usSpreadSpectrumPercentage
);
pll->ssType
= ss_info->info.asSpreadSpectrum[i].ucSpreadSpectrumMode;
pll->ssRate = B_LENDIAN_TO_HOST_INT16(
ss_info->info.asSpreadSpectrum[i].usSpreadRateInKhz);
return B_OK;
}
break;
case 2:
indices = (headerSize - sizeof(ATOM_COMMON_TABLE_HEADER))
/ sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2);
for (i = 0; i < indices; i++) {
if (ss_info->info_2.asSpreadSpectrum[i].ucClockIndication
!= pll->id) {
continue;
}
TRACE("%s: ss match found\n", __func__);
if (pll->pixelClock > B_LENDIAN_TO_HOST_INT32(
ss_info->info_2.asSpreadSpectrum[i].ulTargetClockRange)) {
TRACE("%s: pixelClock > targetClockRange!\n", __func__);
continue;
}
pll->ssPercentage = B_LENDIAN_TO_HOST_INT16(
ss_info
->info_2.asSpreadSpectrum[i].usSpreadSpectrumPercentage
);
pll->ssType
= ss_info->info_2.asSpreadSpectrum[i].ucSpreadSpectrumMode;
pll->ssRate = B_LENDIAN_TO_HOST_INT16(
ss_info->info_2.asSpreadSpectrum[i].usSpreadRateIn10Hz);
return B_OK;
}
break;
case 3:
indices = (headerSize - sizeof(ATOM_COMMON_TABLE_HEADER))
/ sizeof(ATOM_ASIC_SS_ASSIGNMENT_V3);
for (i = 0; i < indices; i++) {
if (ss_info->info_3.asSpreadSpectrum[i].ucClockIndication
!= pll->id) {
continue;
}
TRACE("%s: ss match found\n", __func__);
if (pll->pixelClock > B_LENDIAN_TO_HOST_INT32(
ss_info->info_3.asSpreadSpectrum[i].ulTargetClockRange)) {
TRACE("%s: pixelClock > targetClockRange!\n", __func__);
continue;
}
pll->ssPercentage = B_LENDIAN_TO_HOST_INT16(
ss_info
->info_3.asSpreadSpectrum[i].usSpreadSpectrumPercentage
);
pll->ssType
= ss_info->info_3.asSpreadSpectrum[i].ucSpreadSpectrumMode;
pll->ssRate = B_LENDIAN_TO_HOST_INT16(
ss_info->info_3.asSpreadSpectrum[i].usSpreadRateIn10Hz);
return B_OK;
}
break;
default:
ERROR("%s: Unknown SS table version!\n", __func__);
return B_ERROR;
}
pll->ssPercentage = 0;
pll->ssType = 0;
pll->ssRate = 0;
ERROR("%s: No potential spread spectrum data found!\n", __func__);
return B_ERROR;
}
void void
pll_compute_post_divider(pll_info* pll) pll_compute_post_divider(pll_info* pll)
{ {
@ -494,6 +665,12 @@ pll_set(uint8 pllID, uint32 pixelClock, uint8 crtcID)
pll_compute(pll); pll_compute(pll);
// compute dividers // compute dividers
pll_asic_ss_probe(pll);
// probe spread spectrum metrics (TODO: pll_dp_ss_probe)
display_crtc_ss(crtcID, ATOM_DISABLE);
// disable ss
uint8 tableMajor; uint8 tableMajor;
uint8 tableMinor; uint8 tableMinor;
@ -624,5 +801,10 @@ pll_set(uint8 pllID, uint32 pixelClock, uint8 crtcID)
TRACE("%s: set adjusted pixel clock %" B_PRIu32 " (was %" B_PRIu32 ")\n", TRACE("%s: set adjusted pixel clock %" B_PRIu32 " (was %" B_PRIu32 ")\n",
__func__, pll->pixelClock, pixelClock); __func__, pll->pixelClock, pixelClock);
return atom_execute_table(gAtomContext, index, (uint32*)&args); status_t result = atom_execute_table(gAtomContext, index, (uint32*)&args);
//display_crtc_ss(crtcID, ATOM_ENABLE);
// Not yet, lets avoid this.
return result;
} }

View File

@ -84,6 +84,18 @@ struct pll_info {
uint32 maxFeedbackDiv; uint32 maxFeedbackDiv;
uint32 minFeedbackDivFrac; uint32 minFeedbackDivFrac;
uint32 maxFeedbackDivFrac; uint32 maxFeedbackDivFrac;
/* spread spectrum info */
uint8 ssType;
uint8 ssDelay;
uint8 ssRange;
uint8 ssReferenceDiv;
uint16 ssPercentage;
uint16 ssStep;
/* asic spread spectrum */
uint16 ssRate;
uint16 ssAmount;
}; };
@ -91,6 +103,8 @@ status_t pll_adjust(pll_info* pll, uint8 crtcID);
status_t pll_compute(pll_info* pll); status_t pll_compute(pll_info* pll);
void pll_setup_flags(pll_info* pll, uint8 crtcID); void pll_setup_flags(pll_info* pll, uint8 crtcID);
status_t pll_limit_probe(pll_info* pll); status_t pll_limit_probe(pll_info* pll);
status_t pll_dp_ss_probe(pll_info* pll);
status_t pll_asic_ss_probe(pll_info* pll);
status_t pll_set(uint8 pllID, uint32 pixelClock, uint8 crtcID); status_t pll_set(uint8 pllID, uint32 pixelClock, uint8 crtcID);