[ 131/135] drm/radeon: rework pll selection (v3)

From: Ben Hutchings
Date: Sun Sep 16 2012 - 21:23:28 EST


3.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Alex Deucher <alexander.deucher@xxxxxxx>

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@xxxxxxx>
[bwh: Backported to 3.2: drop the DCE6 case]
Signed-off-by: Ben Hutchings <ben@xxxxxxxxxxxxxxx>
---
drivers/gpu/drm/radeon/atombios_crtc.c | 163 +++++++++++++++++++++++++-------
1 file changed, 129 insertions(+), 34 deletions(-)

--- 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(str
}
}

+/**
+ * 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 d
/* 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 d
*/
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
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 dr
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);
}


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/