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:
parent
4ffdf2ed40
commit
e938ebdae0
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user