radeon_hd: Rework PLL picking code

* Rewrote to be more efficient and more accurate.
* Fix a DP bug where we didn't choose the correct
  external PLL.
This commit is contained in:
Alexander von Gluck IV
2014-05-26 22:26:24 -05:00
parent 4ffdf2ed40
commit e938ebdae0
3 changed files with 156 additions and 55 deletions
+26 -31
View File
@@ -1005,6 +1005,23 @@ display_crtc_ss(pll_info* pll, int command)
TRACE("%s\n", __func__); TRACE("%s\n", __func__);
radeon_shared_info &info = *gInfo->shared_info; radeon_shared_info &info = *gInfo->shared_info;
if (command == ATOM_ENABLE) {
if (pll->ssPercentage == 0) {
TRACE("%s: ssPercentage 0, ignoring SS request\n", __func__);
return;
}
if ((pll->ssType & ATOM_EXTERNAL_SS_MASK) != 0) {
TRACE("%s: external SS, ignoring SS request\n", __func__);
return;
}
} else {
if (pll_usage_count(pll->id) > 1) {
// TODO: Check if PLL has SS enabled on any other displays, if so
// we need to also skip this function.
TRACE("%s: TODO: shared PLL detected!\n", __func__);
}
}
int index = GetIndexIntoMasterTable(COMMAND, EnableSpreadSpectrumOnPPLL); int index = GetIndexIntoMasterTable(COMMAND, EnableSpreadSpectrumOnPPLL);
union enableSS { union enableSS {
@@ -1025,31 +1042,21 @@ display_crtc_ss(pll_info* pll, int command)
switch (pll->id) { switch (pll->id) {
case ATOM_PPLL1: case ATOM_PPLL1:
args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P1PLL; 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; break;
case ATOM_PPLL2: case ATOM_PPLL2:
args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P2PLL; 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; break;
case ATOM_DCPLL: case ATOM_DCPLL:
args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_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; break;
case ATOM_PPLL_INVALID:
return;
default: default:
ERROR("%s: BUG: Invalid PLL ID!\n", __func__); ERROR("%s: BUG: Invalid PLL ID!\n", __func__);
return; return;
} }
if (pll->ssPercentage == 0 args.v3.usSpreadSpectrumAmount = B_HOST_TO_LENDIAN_INT16(pll->ssAmount);
|| ((pll->ssType & ATOM_EXTERNAL_SS_MASK) != 0)) { args.v3.usSpreadSpectrumStep = B_HOST_TO_LENDIAN_INT16(pll->ssStep);
command = ATOM_DISABLE;
}
args.v3.ucEnable = command; args.v3.ucEnable = command;
} else if (info.dceMajor >= 4) { } else if (info.dceMajor >= 4) {
args.v2.usSpreadSpectrumPercentage args.v2.usSpreadSpectrumPercentage
@@ -1059,32 +1066,21 @@ display_crtc_ss(pll_info* pll, int command)
switch (pll->id) { switch (pll->id) {
case ATOM_PPLL1: case ATOM_PPLL1:
args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V2_P1PLL; 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; break;
case ATOM_PPLL2: case ATOM_PPLL2:
args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P2PLL; 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; break;
case ATOM_DCPLL: case ATOM_DCPLL:
args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_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; break;
case ATOM_PPLL_INVALID:
return;
default: default:
ERROR("%s: BUG: Invalid PLL ID!\n", __func__); ERROR("%s: BUG: Invalid PLL ID!\n", __func__);
return; return;
} }
if (pll->ssPercentage == 0 args.v2.usSpreadSpectrumAmount = B_HOST_TO_LENDIAN_INT16(pll->ssAmount);
|| ((pll->ssType & ATOM_EXTERNAL_SS_MASK) != 0) args.v2.usSpreadSpectrumStep = B_HOST_TO_LENDIAN_INT16(pll->ssStep);
|| (info.chipsetFlags & CHIP_APU) != 0 ) {
command = ATOM_DISABLE;
}
args.v2.ucEnable = command; args.v2.ucEnable = command;
} else if (info.dceMajor >= 3) { } else if (info.dceMajor >= 3) {
args.v1.usSpreadSpectrumPercentage args.v1.usSpreadSpectrumPercentage
@@ -1097,8 +1093,7 @@ display_crtc_ss(pll_info* pll, int command)
args.v1.ucPpll = pll->id; args.v1.ucPpll = pll->id;
args.v1.ucEnable = command; args.v1.ucEnable = command;
} else if (info.dceMajor >= 2) { } else if (info.dceMajor >= 2) {
if ((command == ATOM_DISABLE) || (pll->ssPercentage == 0) if (command == ATOM_DISABLE) {
|| (pll->ssType & ATOM_EXTERNAL_SS_MASK)) {
radeon_gpu_ss_control(pll, false); radeon_gpu_ss_control(pll, false);
return; return;
} }
+126 -24
View File
@@ -1012,50 +1012,152 @@ pll_external_init()
} else if (info.dceMajor >= 4) { } else if (info.dceMajor >= 4) {
// Create our own pseudo pll // Create our own pseudo pll
pll_info pll; pll_info pll;
bool ssPresent = pll_asic_ss_probe(&pll, ASIC_INTERNAL_SS_ON_DCPLL) pll_asic_ss_probe(&pll, ASIC_INTERNAL_SS_ON_DCPLL);
== B_OK ? true : false; if (pll.ssEnabled)
if (ssPresent)
display_crtc_ss(&pll, ATOM_DISABLE); display_crtc_ss(&pll, ATOM_DISABLE);
pll_external_set(gInfo->displayClockFrequency); pll_external_set(gInfo->displayClockFrequency);
if (ssPresent) if (pll.ssEnabled)
display_crtc_ss(&pll, ATOM_ENABLE); display_crtc_ss(&pll, ATOM_ENABLE);
} }
} }
/**
* pll_usage_mask - Calculate which PLL's are in use
*
* Returns the mask of which PLL's are in use
*/
uint32
pll_usage_mask()
{
uint32 pllMask = 0;
for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) {
if (gConnector[id]->valid == true) {
pll_info* pll = &gConnector[id]->encoder.pll;
if (pll->id != ATOM_PPLL_INVALID)
pllMask |= (1 << pll->id);
}
}
return pllMask;
}
/**
* pll_usage_count - Find number of connectors attached to a PLL
*
* Returns the count of connectors using specified PLL
*/
uint32
pll_usage_count(uint32 pllID)
{
uint32 pllCount = 0;
for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) {
if (gConnector[id]->valid == true) {
pll_info* pll = &gConnector[id]->encoder.pll;
if (pll->id == pllID)
pllCount++;
}
}
return pllCount;
}
/**
* pll_shared_dp - Find any existing PLL's used for DP connectors
*
* Returns the PLL shared by other DP connectors
*/
uint32
pll_shared_dp()
{
for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) {
if (gConnector[id]->valid == true) {
if (connector_is_dp(id)) {
pll_info* pll = &gConnector[id]->encoder.pll;
return pll->id;
}
}
}
return ATOM_PPLL_INVALID;
}
/**
* pll_next_available - Find the next available non-DP PLL
*
* Returns the next available PLL
*/
uint32
pll_next_available()
{
radeon_shared_info &info = *gInfo->shared_info;
uint32 dceVersion = (info.dceMajor * 100) + info.dceMinor;
uint32 pllMask = pll_usage_mask();
if (dceVersion == 802 || dceVersion == 601) {
if (!(pllMask & (1 << ATOM_PPLL0)))
return ATOM_PPLL0;
}
if (!(pllMask & (1 << ATOM_PPLL1)))
return ATOM_PPLL1;
if (dceVersion != 601) {
if (!(pllMask & (1 << ATOM_PPLL2)))
return ATOM_PPLL2;
}
// TODO: If this starts happening, we likely need to
// add the sharing of PLL's with identical clock rates
// (see radeon_atom_pick_pll in drm)
ERROR("%s: Unable to find a PLL! (0x%" B_PRIX32 ")\n", __func__, pllMask);
return ATOM_PPLL_INVALID;
}
status_t status_t
pll_pick(uint32 connectorIndex) pll_pick(uint32 connectorIndex)
{ {
pll_info* pll = &gConnector[connectorIndex]->encoder.pll; pll_info* pll = &gConnector[connectorIndex]->encoder.pll;
radeon_shared_info &info = *gInfo->shared_info; radeon_shared_info &info = *gInfo->shared_info;
uint32 dceVersion = (info.dceMajor * 100) + info.dceMinor;
bool linkB = gConnector[connectorIndex]->encoder.linkEnumeration bool linkB = gConnector[connectorIndex]->encoder.linkEnumeration
== GRAPH_OBJECT_ENUM_ID2 ? true : false; == GRAPH_OBJECT_ENUM_ID2 ? true : false;
if (info.dceMajor == 6 && info.dceMinor == 1) { pll->id = ATOM_PPLL_INVALID;
// DCE 6.1 APU
if (gConnector[connectorIndex]->encoder.objectID // DCE 6.1 APU, UNIPHYA requires PLL2
== ENCODER_OBJECT_ID_INTERNAL_UNIPHY && !linkB) { if (gConnector[connectorIndex]->encoder.objectID
pll->id = ATOM_PPLL2; == ENCODER_OBJECT_ID_INTERNAL_UNIPHY && !linkB) {
pll->id = ATOM_PPLL2;
return B_OK;
}
if (connector_is_dp(connectorIndex)) {
// If DP external clock, set to invalid except on DCE 6.1
if (gInfo->dpExternalClock && !(dceVersion == 601)) {
pll->id = ATOM_PPLL_INVALID;
return B_OK; return B_OK;
} }
// TODO: check for used PLL1 and use PLL2?
pll->id = ATOM_PPLL1; // DCE 6.1+, we can share DP PLLs. See if any other DP connectors
return B_OK; // have been assigned a PLL yet.
} else if (info.dceMajor >= 4) { if (dceVersion >= 601) {
if (connector_is_dp(connectorIndex)) { pll->id = pll_shared_dp();
if (gInfo->dpExternalClock) { if (pll->id != ATOM_PPLL_INVALID)
pll->id = ATOM_PPLL_INVALID;
return B_OK; return B_OK;
} else if (info.dceMajor >= 6) { // Continue through to pll_next_available
pll->id = ATOM_PPLL1; } else if (dceVersion == 600) {
return B_OK; pll->id = ATOM_PPLL0;
} else if (info.dceMajor >= 5) { return B_OK;
pll->id = ATOM_DCPLL; } else if (info.dceMajor >= 5) {
return B_OK; pll->id = ATOM_DCPLL;
} return B_OK;
} }
pll->id = ATOM_PPLL1; }
if (info.dceMajor >= 4) {
pll->id = pll_next_available();
return B_OK; return B_OK;
} }
+4
View File
@@ -104,6 +104,10 @@ struct pll_info {
void pll_external_init(); void pll_external_init();
status_t pll_external_set(uint32 clock); status_t pll_external_set(uint32 clock);
status_t pll_adjust(pll_info* pll, display_mode* mode, uint8 crtcID); status_t pll_adjust(pll_info* pll, display_mode* mode, uint8 crtcID);
uint32 pll_usage_mask();
uint32 pll_usage_count(uint32 pllID);
uint32 pll_shared_dp();
uint32 pll_next_available();
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);