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 | 71 +++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index c5a0406..2f7cc9e 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -1536,6 +1536,49 @@ static int radeon_get_shared_dp_ppll(struct drm_crtc *crtc) } /** + * 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. * * @crtc: drm 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,6 +1687,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; } @@ -1652,7 +1704,12 @@ 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 if (ASIC_IS_DCE3(rdev)) { + } 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 @@ -1664,6 +1721,11 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc) 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; } @@ -1676,10 +1738,7 @@ 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; - + } } void radeon_atom_disp_eng_pll_init(struct radeon_device *rdev) -- 2.7.4