From 151b4996221d7bc9db571d71efeb2929431bf936 Mon Sep 17 00:00:00 2001 From: Alexander von Gluck IV Date: Thu, 22 Mar 2012 16:55:01 -0500 Subject: [PATCH] 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 --- .../accelerants/radeon_hd/connector.cpp | 2 +- src/add-ons/accelerants/radeon_hd/display.cpp | 130 +++++++++++++ src/add-ons/accelerants/radeon_hd/display.h | 1 + src/add-ons/accelerants/radeon_hd/pll.cpp | 184 +++++++++++++++++- src/add-ons/accelerants/radeon_hd/pll.h | 14 ++ 5 files changed, 329 insertions(+), 2 deletions(-) diff --git a/src/add-ons/accelerants/radeon_hd/connector.cpp b/src/add-ons/accelerants/radeon_hd/connector.cpp index 829bbf83ff..b1021f2043 100644 --- a/src/add-ons/accelerants/radeon_hd/connector.cpp +++ b/src/add-ons/accelerants/radeon_hd/connector.cpp @@ -631,7 +631,7 @@ connector_probe() } // External encoders are behind DVO or UNIPHY - if(encoder_is_external(encoderID)) { + if (encoder_is_external(encoderID)) { encoder_info* encoder = &connector->encoderExternal; encoder->isExternal = true; diff --git a/src/add-ons/accelerants/radeon_hd/display.cpp b/src/add-ons/accelerants/radeon_hd/display.cpp index 5cb1bd4d7f..ac5a36ce32 100644 --- a/src/add-ons/accelerants/radeon_hd/display.cpp +++ b/src/add-ons/accelerants/radeon_hd/display.cpp @@ -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 display_crtc_power(uint8 crtcID, int command) { diff --git a/src/add-ons/accelerants/radeon_hd/display.h b/src/add-ons/accelerants/radeon_hd/display.h index a132647104..67a0e6770f 100644 --- a/src/add-ons/accelerants/radeon_hd/display.h +++ b/src/add-ons/accelerants/radeon_hd/display.h @@ -20,6 +20,7 @@ status_t detect_displays(); void debug_displays(); 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_blank(uint8 crtcID, int command); void display_crtc_dpms(uint8 crtcID, int mode); diff --git a/src/add-ons/accelerants/radeon_hd/pll.cpp b/src/add-ons/accelerants/radeon_hd/pll.cpp index a827d14b7b..f2eca47614 100644 --- a/src/add-ons/accelerants/radeon_hd/pll.cpp +++ b/src/add-ons/accelerants/radeon_hd/pll.cpp @@ -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 pll_compute_post_divider(pll_info* pll) { @@ -494,6 +665,12 @@ pll_set(uint8 pllID, uint32 pixelClock, uint8 crtcID) pll_compute(pll); // 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 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", __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; } diff --git a/src/add-ons/accelerants/radeon_hd/pll.h b/src/add-ons/accelerants/radeon_hd/pll.h index aae7c4c12f..be358572f4 100644 --- a/src/add-ons/accelerants/radeon_hd/pll.h +++ b/src/add-ons/accelerants/radeon_hd/pll.h @@ -84,6 +84,18 @@ struct pll_info { uint32 maxFeedbackDiv; uint32 minFeedbackDivFrac; 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); void pll_setup_flags(pll_info* pll, uint8 crtcID); 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);