Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f523f74e authored by Alex Deucher's avatar Alex Deucher Committed by Dave Airlie
Browse files

drm/radeon/kms: add new pll algo for avivo asics



Based on the vbios code.  This should hopefully
fix the pll problems on a number of avivo asics
once it's enabled.

Signed-off-by: default avatarAlex Deucher <alexdeucher@gmail.com>
Cc: stable@kernel.org
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 51d4bf84
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -951,7 +951,7 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
	/* adjust pixel clock as needed */
	adjusted_clock = atombios_adjust_pll(crtc, mode, pll, ss_enabled, &ss);

	radeon_compute_pll(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div,
	radeon_compute_pll_legacy(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div,
				  &ref_div, &post_div);

	atombios_crtc_program_ss(crtc, ATOM_DISABLE, radeon_crtc->pll_id, &ss);
+116 −7
Original line number Diff line number Diff line
@@ -780,6 +780,115 @@ static int radeon_ddc_dump(struct drm_connector *connector)
	return ret;
}

/* avivo */
static void avivo_get_fb_div(struct radeon_pll *pll,
			     u32 target_clock,
			     u32 post_div,
			     u32 ref_div,
			     u32 *fb_div,
			     u32 *frac_fb_div)
{
	u32 tmp = post_div * ref_div;

	tmp *= target_clock;
	*fb_div = tmp / pll->reference_freq;
	*frac_fb_div = tmp % pll->reference_freq;
}

static u32 avivo_get_post_div(struct radeon_pll *pll,
			      u32 target_clock)
{
	u32 vco, post_div, tmp;

	if (pll->flags & RADEON_PLL_USE_POST_DIV)
		return pll->post_div;

	if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) {
		if (pll->flags & RADEON_PLL_IS_LCD)
			vco = pll->lcd_pll_out_min;
		else
			vco = pll->pll_out_min;
	} else {
		if (pll->flags & RADEON_PLL_IS_LCD)
			vco = pll->lcd_pll_out_max;
		else
			vco = pll->pll_out_max;
	}

	post_div = vco / target_clock;
	tmp = vco % target_clock;

	if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) {
		if (tmp)
			post_div++;
	} else {
		if (!tmp)
			post_div--;
	}

	return post_div;
}

#define MAX_TOLERANCE 10

void radeon_compute_pll_avivo(struct radeon_pll *pll,
			      u32 freq,
			      u32 *dot_clock_p,
			      u32 *fb_div_p,
			      u32 *frac_fb_div_p,
			      u32 *ref_div_p,
			      u32 *post_div_p)
{
	u32 target_clock = freq / 10;
	u32 post_div = avivo_get_post_div(pll, target_clock);
	u32 ref_div = pll->min_ref_div;
	u32 fb_div = 0, frac_fb_div = 0, tmp;

	if (pll->flags & RADEON_PLL_USE_REF_DIV)
		ref_div = pll->reference_div;

	if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
		avivo_get_fb_div(pll, target_clock, post_div, ref_div, &fb_div, &frac_fb_div);
		frac_fb_div = (100 * frac_fb_div) / pll->reference_freq;
		if (frac_fb_div >= 5) {
			frac_fb_div -= 5;
			frac_fb_div = frac_fb_div / 10;
			frac_fb_div++;
		}
		if (frac_fb_div >= 10) {
			fb_div++;
			frac_fb_div = 0;
		}
	} else {
		while (ref_div <= pll->max_ref_div) {
			avivo_get_fb_div(pll, target_clock, post_div, ref_div,
					 &fb_div, &frac_fb_div);
			if (frac_fb_div >= (pll->reference_freq / 2))
				fb_div++;
			frac_fb_div = 0;
			tmp = (pll->reference_freq * fb_div) / (post_div * ref_div);
			tmp = (tmp * 10000) / target_clock;

			if (tmp > (10000 + MAX_TOLERANCE))
				ref_div++;
			else if (tmp >= (10000 - MAX_TOLERANCE))
				break;
			else
				ref_div++;
		}
	}

	*dot_clock_p = ((pll->reference_freq * fb_div * 10) + (pll->reference_freq * frac_fb_div)) /
		(ref_div * post_div * 10);
	*fb_div_p = fb_div;
	*frac_fb_div_p = frac_fb_div;
	*ref_div_p = ref_div;
	*post_div_p = post_div;
	DRM_DEBUG_KMS("%d, pll dividers - fb: %d.%d ref: %d, post %d\n",
		      *dot_clock_p, fb_div, frac_fb_div, ref_div, post_div);
}

/* pre-avivo */
static inline uint32_t radeon_div(uint64_t n, uint32_t d)
{
	uint64_t mod;
@@ -790,7 +899,7 @@ static inline uint32_t radeon_div(uint64_t n, uint32_t d)
	return n;
}

void radeon_compute_pll(struct radeon_pll *pll,
void radeon_compute_pll_legacy(struct radeon_pll *pll,
			       uint64_t freq,
			       uint32_t *dot_clock_p,
			       uint32_t *fb_div_p,
+3 −3
Original line number Diff line number Diff line
@@ -778,7 +778,7 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
	DRM_DEBUG_KMS("\n");

	if (!use_bios_divs) {
		radeon_compute_pll(pll, mode->clock,
		radeon_compute_pll_legacy(pll, mode->clock,
					  &freq, &feedback_div, &frac_fb_div,
					  &reference_div, &post_divider);

+16 −7
Original line number Diff line number Diff line
@@ -149,6 +149,7 @@ struct radeon_tmds_pll {
#define RADEON_PLL_PREFER_CLOSEST_LOWER (1 << 11)
#define RADEON_PLL_USE_POST_DIV         (1 << 12)
#define RADEON_PLL_IS_LCD               (1 << 13)
#define RADEON_PLL_PREFER_MINM_OVER_MAXP (1 << 14)

struct radeon_pll {
	/* reference frequency */
@@ -510,7 +511,7 @@ extern bool radeon_atombios_get_asic_ss_info(struct radeon_device *rdev,
					     struct radeon_atom_ss *ss,
					     int id, u32 clock);

extern void radeon_compute_pll(struct radeon_pll *pll,
extern void radeon_compute_pll_legacy(struct radeon_pll *pll,
				      uint64_t freq,
				      uint32_t *dot_clock_p,
				      uint32_t *fb_div_p,
@@ -518,6 +519,14 @@ extern void radeon_compute_pll(struct radeon_pll *pll,
				      uint32_t *ref_div_p,
				      uint32_t *post_div_p);

extern void radeon_compute_pll_avivo(struct radeon_pll *pll,
				     u32 freq,
				     u32 *dot_clock_p,
				     u32 *fb_div_p,
				     u32 *frac_fb_div_p,
				     u32 *ref_div_p,
				     u32 *post_div_p);

extern void radeon_setup_encoder_clones(struct drm_device *dev);

struct drm_encoder *radeon_encoder_legacy_lvds_add(struct drm_device *dev, int bios_index);