From 2f454cf1261ba913e2f660b7555864b340502c60 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 12 Sep 2012 18:54:14 -0400 Subject: [PATCH] drm/radeon: allow PPLL sharing on non-DP displays If several non-DP displays use the same pixel clock we can use the same PPLL for all of them. If all relevant displays have the same pixel clock, this allows the driver to: - use fewer PPLLs which saves power - support more than two non-DP displays on DCE4+ The current drm modesetting infrastructure doesn't really provide a good framework for validating combinations that work or won't work, so it's possible you could go from a working configuration to a non-working one by changing the mode a one of the displays. However, there this is better than what was there before. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/atombios_crtc.c | 113 +++++++++++++++++++------ 1 file changed, 86 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index c5a040677d48..2f7cc9e3b175 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -1535,6 +1535,49 @@ static int radeon_get_shared_dp_ppll(struct drm_crtc *crtc) return ATOM_PPLL_INVALID; } +/** + * radeon_get_shared_nondp_ppll - return the PPLL used by another non-DP crtc + * + * @crtc: drm crtc + * @encoder: drm encoder + * + * Returns the PPLL (Pixel PLL) used by another non-DP crtc/encoder which can + * be shared (i.e., same clock). + */ +static int radeon_get_shared_nondp_ppll(struct drm_crtc *crtc, + struct drm_encoder *encoder) +{ + struct drm_device *dev = crtc->dev; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_encoder *test_encoder; + struct radeon_crtc *radeon_test_crtc; + struct radeon_encoder *test_radeon_encoder; + u32 target_clock, test_clock; + + if (radeon_encoder->native_mode.clock) + target_clock = radeon_encoder->native_mode.clock; + else + target_clock = crtc->mode.clock; + + list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) { + if (test_encoder->crtc && (test_encoder->crtc != crtc)) { + if (!ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_encoder))) { + test_radeon_encoder = to_radeon_encoder(test_encoder); + radeon_test_crtc = to_radeon_crtc(test_encoder->crtc); + /* for non-DP check the clock */ + if (test_radeon_encoder->native_mode.clock) + test_clock = test_radeon_encoder->native_mode.clock; + else + test_clock = test_encoder->crtc->mode.clock; + if ((target_clock == test_clock) && + (radeon_test_crtc->pll_id != ATOM_PPLL_INVALID)) + return radeon_test_crtc->pll_id; + } + } + } + return ATOM_PPLL_INVALID; +} + /** * radeon_atom_pick_pll - Allocate a PPLL for use by the crtc. * @@ -1568,7 +1611,6 @@ static int radeon_get_shared_dp_ppll(struct drm_crtc *crtc) */ static int radeon_atom_pick_pll(struct drm_crtc *crtc) { - struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); struct drm_device *dev = crtc->dev; struct radeon_device *rdev = dev->dev_private; struct drm_encoder *test_encoder; @@ -1599,6 +1641,11 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc) if (pll != ATOM_PPLL_INVALID) return pll; } + } else { + /* use the same PPLL for all monitors with the same clock */ + pll = radeon_get_shared_nondp_ppll(crtc, test_encoder); + if (pll != ATOM_PPLL_INVALID) + return pll; } break; } @@ -1640,28 +1687,9 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc) if (pll != ATOM_PPLL_INVALID) return pll; } - } - break; - } - } - /* all other cases */ - pll_in_use = radeon_get_pll_use_mask(crtc); - if (!(pll_in_use & (1 << ATOM_PPLL2))) - return ATOM_PPLL2; - if (!(pll_in_use & (1 << ATOM_PPLL1))) - return ATOM_PPLL1; - DRM_ERROR("unable to allocate a PPLL\n"); - return ATOM_PPLL_INVALID; - } else if (ASIC_IS_DCE3(rdev)) { - list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) { - if (test_encoder->crtc && (test_encoder->crtc == crtc)) { - /* in DP mode, the DP ref clock can come from either PPLL - * depending on the asic: - * DCE3: PPLL1 or PPLL2 - */ - if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_encoder))) { - /* use the same PPLL for all DP monitors */ - pll = radeon_get_shared_dp_ppll(crtc); + } else { + /* use the same PPLL for all monitors with the same clock */ + pll = radeon_get_shared_nondp_ppll(crtc, test_encoder); if (pll != ATOM_PPLL_INVALID) return pll; } @@ -1676,10 +1704,41 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc) return ATOM_PPLL1; DRM_ERROR("unable to allocate a PPLL\n"); return ATOM_PPLL_INVALID; - } else - /* use PPLL1 or PPLL2 */ - return radeon_crtc->crtc_id; - + } else { + /* on pre-R5xx asics, the crtc to pll mapping is hardcoded */ + if (!ASIC_IS_AVIVO(rdev)) { + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + return radeon_crtc->crtc_id; + } + list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) { + if (test_encoder->crtc && (test_encoder->crtc == crtc)) { + /* in DP mode, the DP ref clock can come from either PPLL + * depending on the asic: + * DCE3: PPLL1 or PPLL2 + */ + if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_encoder))) { + /* use the same PPLL for all DP monitors */ + pll = radeon_get_shared_dp_ppll(crtc); + if (pll != ATOM_PPLL_INVALID) + return pll; + } else { + /* use the same PPLL for all monitors with the same clock */ + pll = radeon_get_shared_nondp_ppll(crtc, test_encoder); + if (pll != ATOM_PPLL_INVALID) + return pll; + } + break; + } + } + /* all other cases */ + pll_in_use = radeon_get_pll_use_mask(crtc); + if (!(pll_in_use & (1 << ATOM_PPLL2))) + return ATOM_PPLL2; + if (!(pll_in_use & (1 << ATOM_PPLL1))) + return ATOM_PPLL1; + DRM_ERROR("unable to allocate a PPLL\n"); + return ATOM_PPLL_INVALID; + } } void radeon_atom_disp_eng_pll_init(struct radeon_device *rdev)