diff --git a/src/add-ons/accelerants/radeon_hd/display.cpp b/src/add-ons/accelerants/radeon_hd/display.cpp index 46e35c54a0..45c87dbe5e 100644 --- a/src/add-ons/accelerants/radeon_hd/display.cpp +++ b/src/add-ons/accelerants/radeon_hd/display.cpp @@ -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; } diff --git a/src/add-ons/accelerants/radeon_hd/pll.cpp b/src/add-ons/accelerants/radeon_hd/pll.cpp index e1050999d1..feb91a6522 100644 --- a/src/add-ons/accelerants/radeon_hd/pll.cpp +++ b/src/add-ons/accelerants/radeon_hd/pll.cpp @@ -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; } diff --git a/src/add-ons/accelerants/radeon_hd/pll.h b/src/add-ons/accelerants/radeon_hd/pll.h index cd22e94d66..ed09138878 100644 --- a/src/add-ons/accelerants/radeon_hd/pll.h +++ b/src/add-ons/accelerants/radeon_hd/pll.h @@ -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);