From 57b35e29cf4e45eb163631c4ece10dbc259ddf30 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 17 Sep 2012 17:34:45 -0400 Subject: [PATCH] drm/radeon: work around KMS modeset limitations in PLL allocation (v2) Since the current KMS API sets the mode independantly on each crtc, we may end up with resource conflicts. The PLL allocation is one of those cases. In the following example we have 3 crtcs in use driving 2 DVI connectors and 1 DP connector. On the initial kernel modeset for fbdev, the display topology ends up as follows: crtc0 -> DP-0 crtc1 -> DVI-0 crtc2 -> DVI-1 Because this is the first modeset, all of the PLLs are available as none have been assigned. So we end up with the following: crtc0 uses DCPLL crtc1 uses PPLL2 crtc2 uses PPLL1 When X starts, it assigns a different topology: crtc0 -> DVI-0 crtc1 -> DP-0 crtc2 -> DVI-1 However, since the KMS API is per crtc, we set the mode on each crtc independantly. When it comes time to set the mode on crtc0, the topology for crtc1 and crtc2 are still intact. crtc1 and crtc2 are already assigned PPLL2 and PPLL1 so when it comes time to set the mode on crtc0, crtc1 and crtc2 have not been torn down yet, so there appears to be no PLLs available. In reality, we are reconfiguring the entire display topology, however, since each crtc is handled independantly, we don't know that in the driver at each crtc mode set time. This patch checks to see if the same connector is being driven by another crtc, and if so, uses the PLL already associated with it. v2: store connector in the radeon crtc struct, simplify checking. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/atombios_crtc.c | 66 +++++++++++++++----------- drivers/gpu/drm/radeon/radeon_mode.h | 1 + 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 721f80e9568d..8e32c5891be1 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -1492,16 +1492,16 @@ static u32 radeon_get_pll_use_mask(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_crtc *test_crtc; - struct radeon_crtc *radeon_test_crtc; + struct radeon_crtc *test_radeon_crtc; u32 pll_in_use = 0; list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) { if (crtc == test_crtc) continue; - radeon_test_crtc = to_radeon_crtc(test_crtc); - if (radeon_test_crtc->pll_id != ATOM_PPLL_INVALID) - pll_in_use |= (1 << radeon_test_crtc->pll_id); + test_radeon_crtc = to_radeon_crtc(test_crtc); + if (test_radeon_crtc->pll_id != ATOM_PPLL_INVALID) + pll_in_use |= (1 << test_radeon_crtc->pll_id); } return pll_in_use; } @@ -1518,17 +1518,18 @@ static u32 radeon_get_pll_use_mask(struct drm_crtc *crtc) static int radeon_get_shared_dp_ppll(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; - struct drm_encoder *test_encoder; + struct drm_crtc *test_crtc; struct radeon_crtc *test_radeon_crtc; - 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))) { - /* for DP use the same PLL for all */ - test_radeon_crtc = to_radeon_crtc(test_encoder->crtc); - if (test_radeon_crtc->pll_id != ATOM_PPLL_INVALID) - return test_radeon_crtc->pll_id; - } + list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) { + if (crtc == test_crtc) + continue; + test_radeon_crtc = to_radeon_crtc(test_crtc); + if (test_radeon_crtc->encoder && + ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_radeon_crtc->encoder))) { + /* for DP use the same PLL for all */ + if (test_radeon_crtc->pll_id != ATOM_PPLL_INVALID) + return test_radeon_crtc->pll_id; } } return ATOM_PPLL_INVALID; @@ -1547,10 +1548,8 @@ static int radeon_get_shared_nondp_ppll(struct drm_crtc *crtc) { struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); struct drm_device *dev = crtc->dev; - struct drm_encoder *test_encoder; struct drm_crtc *test_crtc; struct radeon_crtc *test_radeon_crtc; - struct radeon_encoder *test_radeon_encoder; u32 adjusted_clock, test_adjusted_clock; adjusted_clock = radeon_crtc->adjusted_clock; @@ -1558,20 +1557,25 @@ static int radeon_get_shared_nondp_ppll(struct drm_crtc *crtc) if (adjusted_clock == 0) return ATOM_PPLL_INVALID; - 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); - test_crtc = test_encoder->crtc; - test_radeon_crtc = to_radeon_crtc(test_crtc); - /* for non-DP check the clock */ - test_adjusted_clock = test_radeon_crtc->adjusted_clock; - if ((crtc->mode.clock == test_crtc->mode.clock) && - (adjusted_clock == test_adjusted_clock) && - (radeon_crtc->ss_enabled == test_radeon_crtc->ss_enabled) && - (test_radeon_crtc->pll_id != ATOM_PPLL_INVALID)) + list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) { + if (crtc == test_crtc) + continue; + test_radeon_crtc = to_radeon_crtc(test_crtc); + if (test_radeon_crtc->encoder && + !ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_radeon_crtc->encoder))) { + /* check if we are already driving this connector with another crtc */ + if (test_radeon_crtc->connector == radeon_crtc->connector) { + /* if we are, return that pll */ + if (test_radeon_crtc->pll_id != ATOM_PPLL_INVALID) return test_radeon_crtc->pll_id; } + /* for non-DP check the clock */ + test_adjusted_clock = test_radeon_crtc->adjusted_clock; + if ((crtc->mode.clock == test_crtc->mode.clock) && + (adjusted_clock == test_adjusted_clock) && + (radeon_crtc->ss_enabled == test_radeon_crtc->ss_enabled) && + (test_radeon_crtc->pll_id != ATOM_PPLL_INVALID)) + return test_radeon_crtc->pll_id; } } return ATOM_PPLL_INVALID; @@ -1793,11 +1797,15 @@ static bool atombios_crtc_mode_fixup(struct drm_crtc *crtc, list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { if (encoder->crtc == crtc) { radeon_crtc->encoder = encoder; + radeon_crtc->connector = radeon_get_connector_for_encoder(encoder); break; } } - if (radeon_crtc->encoder == NULL) + if ((radeon_crtc->encoder == NULL) || (radeon_crtc->connector == NULL)) { + radeon_crtc->encoder = NULL; + radeon_crtc->connector = NULL; return false; + } if (!radeon_crtc_scaling_mode_fixup(crtc, mode, adjusted_mode)) return false; if (!atombios_crtc_prepare_pll(crtc, adjusted_mode)) @@ -1874,6 +1882,7 @@ static void atombios_crtc_disable(struct drm_crtc *crtc) radeon_crtc->pll_id = ATOM_PPLL_INVALID; radeon_crtc->adjusted_clock = 0; radeon_crtc->encoder = NULL; + radeon_crtc->connector = NULL; } static const struct drm_crtc_helper_funcs atombios_helper_funcs = { @@ -1925,5 +1934,6 @@ void radeon_atombios_init_crtc(struct drm_device *dev, radeon_crtc->pll_id = ATOM_PPLL_INVALID; radeon_crtc->adjusted_clock = 0; radeon_crtc->encoder = NULL; + radeon_crtc->connector = NULL; drm_crtc_helper_add(&radeon_crtc->base, &atombios_helper_funcs); } diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 2d78645576d7..917d02750cf7 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -327,6 +327,7 @@ struct radeon_crtc { u32 pll_post_div; u32 pll_flags; struct drm_encoder *encoder; + struct drm_connector *connector; }; struct radeon_encoder_primary_dac { -- GitLab