summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Deucher <alexander.deucher@amd.com>2012-08-31 11:56:50 -0400
committerBen Hutchings <ben@decadent.org.uk>2012-09-19 15:05:23 +0100
commit8654636ef7426f41a713430cb93f57365a6bfcc4 (patch)
tree7b9af5c8e2c964fe054e81c7e4f4f3ef0d0bda38
parentca9d5df1dce289bf297c94d6e3fa5afa01885146 (diff)
drm/radeon: rework pll selection (v3)
commit 985f61f7ee647ad570c05eab0b74915da2ac8e19 upstream. For DP we can use the same PPLL for all active DP encoders. Take advantage of that to prevent cases where we may end up sharing a PPLL between DP and non-DP which won't work. Also clean up the code a bit. v2: - fix missing pll_id assignment in crtc init v3: - fix DP PPLL check - document functions - break in main encoder search loop after matching. no need to keep checking additional encoders. fixes: https://bugs.freedesktop.org/show_bug.cgi?id=54471 Signed-off-by: Alex Deucher <alexander.deucher@amd.com> [bwh: Backported to 3.2: drop the DCE6 case] Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c126
1 files changed, 106 insertions, 20 deletions
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index a4011b0bd3e4..ceffd202ce29 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -1446,14 +1446,98 @@ static void radeon_legacy_atom_fixup(struct drm_crtc *crtc)
}
}
+/**
+ * radeon_get_pll_use_mask - look up a mask of which pplls are in use
+ *
+ * @crtc: drm crtc
+ *
+ * Returns the mask of which PPLLs (Pixel PLLs) are in use.
+ */
+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;
+ 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);
+ }
+ return pll_in_use;
+}
+
+/**
+ * radeon_get_shared_dp_ppll - return the PPLL used by another crtc for DP
+ *
+ * @crtc: drm crtc
+ *
+ * Returns the PPLL (Pixel PLL) used by another crtc/encoder which is
+ * also in DP mode. For DP, a single PPLL can be used for all DP
+ * crtcs/encoders.
+ */
+static int radeon_get_shared_dp_ppll(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_encoder *test_encoder;
+ struct radeon_crtc *radeon_test_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 */
+ radeon_test_crtc = to_radeon_crtc(test_encoder->crtc);
+ if (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
+ *
+ * Returns the PPLL (Pixel PLL) to be used by the crtc. For DP monitors
+ * a single PPLL can be used for all DP crtcs/encoders. For non-DP
+ * monitors a dedicated PPLL must be used. If a particular board has
+ * an external DP PLL, return ATOM_PPLL_INVALID to skip PLL programming
+ * as there is no need to program the PLL itself. If we are not able to
+ * allocate a PLL, return ATOM_PPLL_INVALID to skip PLL programming to
+ * avoid messing up an existing monitor.
+ *
+ * Asic specific PLL information
+ *
+ * DCE 6.1
+ * - PPLL2 is only available to UNIPHYA (both DP and non-DP)
+ * - PPLL0, PPLL1 are available for UNIPHYB/C/D/E/F (both DP and non-DP)
+ *
+ * DCE 6.0
+ * - PPLL0 is available to all UNIPHY (DP only)
+ * - PPLL1, PPLL2 are available for all UNIPHY (both DP and non-DP) and DAC
+ *
+ * DCE 5.0
+ * - DCPLL is available to all UNIPHY (DP only)
+ * - PPLL1, PPLL2 are available for all UNIPHY (both DP and non-DP) and DAC
+ *
+ * DCE 3.0/4.0/4.1
+ * - PPLL1, PPLL2 are available for all UNIPHY (both DP and non-DP) and DAC
+ *
+ */
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;
- struct drm_crtc *test_crtc;
- uint32_t pll_in_use = 0;
+ u32 pll_in_use;
+ int pll;
if (ASIC_IS_DCE4(rdev)) {
list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) {
@@ -1461,7 +1545,7 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc)
/* in DP mode, the DP ref clock can come from PPLL, DCPLL, or ext clock,
* depending on the asic:
* DCE4: PPLL or ext clock
- * DCE5: DCPLL or ext clock
+ * DCE5: PPLL, DCPLL, or ext clock
*
* Setting ATOM_PPLL_INVALID will cause SetPixelClock to skip
* PPLL/DCPLL programming and only program the DP DTO for the
@@ -1469,29 +1553,31 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc)
*/
if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_encoder))) {
if (rdev->clock.dp_extclk)
+ /* skip PPLL programming if using ext clock */
return ATOM_PPLL_INVALID;
else if (ASIC_IS_DCE5(rdev))
+ /* use DCPLL for all DP */
return ATOM_DCPLL;
+ else {
+ /* use the same PPLL for all DP monitors */
+ pll = radeon_get_shared_dp_ppll(crtc);
+ if (pll != ATOM_PPLL_INVALID)
+ return pll;
+ }
}
+ break;
}
}
-
- /* otherwise, pick one of the plls */
- list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
- struct radeon_crtc *radeon_test_crtc;
-
- if (crtc == test_crtc)
- continue;
-
- radeon_test_crtc = to_radeon_crtc(test_crtc);
- if ((radeon_test_crtc->pll_id >= ATOM_PPLL1) &&
- (radeon_test_crtc->pll_id <= ATOM_PPLL2))
- pll_in_use |= (1 << radeon_test_crtc->pll_id);
- }
- if (!(pll_in_use & 1))
+ /* 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;
- return ATOM_PPLL2;
+ DRM_ERROR("unable to allocate a PPLL\n");
+ return ATOM_PPLL_INVALID;
} else
+ /* use PPLL1 or PPLL2 */
return radeon_crtc->crtc_id;
}
@@ -1610,7 +1696,7 @@ static void atombios_crtc_disable(struct drm_crtc *crtc)
break;
}
done:
- radeon_crtc->pll_id = -1;
+ radeon_crtc->pll_id = ATOM_PPLL_INVALID;
}
static const struct drm_crtc_helper_funcs atombios_helper_funcs = {
@@ -1659,6 +1745,6 @@ void radeon_atombios_init_crtc(struct drm_device *dev,
else
radeon_crtc->crtc_offset = 0;
}
- radeon_crtc->pll_id = -1;
+ radeon_crtc->pll_id = ATOM_PPLL_INVALID;
drm_crtc_helper_add(&radeon_crtc->base, &atombios_helper_funcs);
}