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:19:21 -05:00
parent 4ffdf2ed40
commit e938ebdae0
3 changed files with 156 additions and 55 deletions

View File

@ -1005,6 +1005,23 @@ display_crtc_ss(pll_info* pll, int command)
TRACE("%s\n", __func__);
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);
union enableSS {
@ -1025,31 +1042,21 @@ display_crtc_ss(pll_info* pll, int command)
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;
case ATOM_PPLL_INVALID:
return;
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.usSpreadSpectrumAmount = B_HOST_TO_LENDIAN_INT16(pll->ssAmount);
args.v3.usSpreadSpectrumStep = B_HOST_TO_LENDIAN_INT16(pll->ssStep);
args.v3.ucEnable = command;
} else if (info.dceMajor >= 4) {
args.v2.usSpreadSpectrumPercentage
@ -1059,32 +1066,21 @@ display_crtc_ss(pll_info* pll, int command)
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;
case ATOM_PPLL_INVALID:
return;
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.usSpreadSpectrumAmount = B_HOST_TO_LENDIAN_INT16(pll->ssAmount);
args.v2.usSpreadSpectrumStep = B_HOST_TO_LENDIAN_INT16(pll->ssStep);
args.v2.ucEnable = command;
} else if (info.dceMajor >= 3) {
args.v1.usSpreadSpectrumPercentage
@ -1097,8 +1093,7 @@ display_crtc_ss(pll_info* pll, int command)
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)) {
if (command == ATOM_DISABLE) {
radeon_gpu_ss_control(pll, false);
return;
}

View File

@ -1012,50 +1012,152 @@ pll_external_init()
} else if (info.dceMajor >= 4) {
// Create our own pseudo pll
pll_info pll;
bool ssPresent = pll_asic_ss_probe(&pll, ASIC_INTERNAL_SS_ON_DCPLL)
== B_OK ? true : false;
if (ssPresent)
pll_asic_ss_probe(&pll, ASIC_INTERNAL_SS_ON_DCPLL);
if (pll.ssEnabled)
display_crtc_ss(&pll, ATOM_DISABLE);
pll_external_set(gInfo->displayClockFrequency);
if (ssPresent)
if (pll.ssEnabled)
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
pll_pick(uint32 connectorIndex)
{
pll_info* pll = &gConnector[connectorIndex]->encoder.pll;
radeon_shared_info &info = *gInfo->shared_info;
uint32 dceVersion = (info.dceMajor * 100) + info.dceMinor;
bool linkB = gConnector[connectorIndex]->encoder.linkEnumeration
== GRAPH_OBJECT_ENUM_ID2 ? true : false;
if (info.dceMajor == 6 && info.dceMinor == 1) {
// DCE 6.1 APU
if (gConnector[connectorIndex]->encoder.objectID
== ENCODER_OBJECT_ID_INTERNAL_UNIPHY && !linkB) {
pll->id = ATOM_PPLL2;
pll->id = ATOM_PPLL_INVALID;
// DCE 6.1 APU, UNIPHYA requires PLL2
if (gConnector[connectorIndex]->encoder.objectID
== 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;
}
// TODO: check for used PLL1 and use PLL2?
pll->id = ATOM_PPLL1;
return B_OK;
} else if (info.dceMajor >= 4) {
if (connector_is_dp(connectorIndex)) {
if (gInfo->dpExternalClock) {
pll->id = ATOM_PPLL_INVALID;
// DCE 6.1+, we can share DP PLLs. See if any other DP connectors
// have been assigned a PLL yet.
if (dceVersion >= 601) {
pll->id = pll_shared_dp();
if (pll->id != ATOM_PPLL_INVALID)
return B_OK;
} else if (info.dceMajor >= 6) {
pll->id = ATOM_PPLL1;
return B_OK;
} else if (info.dceMajor >= 5) {
pll->id = ATOM_DCPLL;
return B_OK;
}
// Continue through to pll_next_available
} else if (dceVersion == 600) {
pll->id = ATOM_PPLL0;
return B_OK;
} else if (info.dceMajor >= 5) {
pll->id = ATOM_DCPLL;
return B_OK;
}
pll->id = ATOM_PPLL1;
}
if (info.dceMajor >= 4) {
pll->id = pll_next_available();
return B_OK;
}

View File

@ -104,6 +104,10 @@ struct pll_info {
void pll_external_init();
status_t pll_external_set(uint32 clock);
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);
void pll_setup_flags(pll_info* pll, uint8 crtcID);
status_t pll_limit_probe(pll_info* pll);