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

Commit 2539eb02 authored by Christian König's avatar Christian König Committed by Alex Deucher
Browse files

drm/radeon: add set_uvd_clocks callback for SI

parent a8b4925c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1887,6 +1887,7 @@ static struct radeon_asic si_asic = {
		.get_pcie_lanes = NULL,
		.set_pcie_lanes = NULL,
		.set_clock_gating = NULL,
		.set_uvd_clocks = &si_set_uvd_clocks,
	},
	.pflip = {
		.pre_page_flip = &evergreen_pre_page_flip,
+1 −0
Original line number Diff line number Diff line
@@ -545,5 +545,6 @@ int si_copy_dma(struct radeon_device *rdev,
void si_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
u32 si_get_xclk(struct radeon_device *rdev);
uint64_t si_get_gpu_clock_counter(struct radeon_device *rdev);
int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk);

#endif
+167 −0
Original line number Diff line number Diff line
@@ -4627,3 +4627,170 @@ uint64_t si_get_gpu_clock_counter(struct radeon_device *rdev)
	mutex_unlock(&rdev->gpu_clock_mutex);
	return clock;
}

static int si_uvd_calc_post_div(unsigned target_freq,
				unsigned vco_freq,
				unsigned *div)
{
	/* target larger than vco frequency ? */
	if (vco_freq < target_freq)
		return -1; /* forget it */

	/* Fclk = Fvco / PDIV */
	*div = vco_freq / target_freq;

	/* we alway need a frequency less than or equal the target */
	if ((vco_freq / *div) > target_freq)
		*div += 1;

	/* dividers above 5 must be even */
	if (*div > 5 && *div % 2)
		*div += 1;

	/* out of range ? */
	if (*div >= 128)
		return -1; /* forget it */

	return vco_freq / *div;
}

static int si_uvd_send_upll_ctlreq(struct radeon_device *rdev)
{
	unsigned i;

	/* assert UPLL_CTLREQ */
	WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK);

	/* wait for CTLACK and CTLACK2 to get asserted */
	for (i = 0; i < 100; ++i) {
		uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK;
		if ((RREG32(CG_UPLL_FUNC_CNTL) & mask) == mask)
			break;
		mdelay(10);
	}
	if (i == 100)
		return -ETIMEDOUT;

	/* deassert UPLL_CTLREQ */
	WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_CTLREQ_MASK);

	return 0;
}

int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk)
{
	/* start off with something large */
	int optimal_diff_score = 0x7FFFFFF;
	unsigned optimal_fb_div = 0, optimal_vclk_div = 0;
	unsigned optimal_dclk_div = 0, optimal_vco_freq = 0;
	unsigned vco_freq;
	int r;

	/* loop through vco from low to high */
	for (vco_freq = 125000; vco_freq <= 250000; vco_freq += 100) {
		unsigned fb_div = vco_freq / rdev->clock.spll.reference_freq * 16384;
		int calc_clk, diff_score, diff_vclk, diff_dclk;
		unsigned vclk_div, dclk_div;

		/* fb div out of range ? */
		if (fb_div > 0x03FFFFFF)
			break; /* it can oly get worse */

		/* calc vclk with current vco freq. */
		calc_clk = si_uvd_calc_post_div(vclk, vco_freq, &vclk_div);
		if (calc_clk == -1)
			break; /* vco is too big, it has to stop. */
		diff_vclk = vclk - calc_clk;

		/* calc dclk with current vco freq. */
		calc_clk = si_uvd_calc_post_div(dclk, vco_freq, &dclk_div);
		if (calc_clk == -1)
			break; /* vco is too big, it has to stop. */
		diff_dclk = dclk - calc_clk;

		/* determine if this vco setting is better than current optimal settings */
		diff_score = abs(diff_vclk) + abs(diff_dclk);
		if (diff_score < optimal_diff_score) {
			optimal_fb_div = fb_div;
			optimal_vclk_div = vclk_div;
			optimal_dclk_div = dclk_div;
			optimal_vco_freq = vco_freq;
			optimal_diff_score = diff_score;
			if (optimal_diff_score == 0)
				break; /* it can't get better than this */
		}
	}

	/* set RESET_ANTI_MUX to 0 */
	WREG32_P(CG_UPLL_FUNC_CNTL_5, 0, ~RESET_ANTI_MUX_MASK);

	/* set VCO_MODE to 1 */
	WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_VCO_MODE_MASK, ~UPLL_VCO_MODE_MASK);

	/* toggle UPLL_SLEEP to 1 then back to 0 */
	WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK);
	WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_SLEEP_MASK);

	/* deassert UPLL_RESET */
	WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK);

	mdelay(1);

	/* bypass vclk and dclk with bclk */
	WREG32_P(CG_UPLL_FUNC_CNTL_2,
		VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1),
		~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK));

	/* put PLL in bypass mode */
	WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK);

	r = si_uvd_send_upll_ctlreq(rdev);
	if (r)
		return r;

	/* assert UPLL_RESET again */
	WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_RESET_MASK, ~UPLL_RESET_MASK);

	/* disable spread spectrum. */
	WREG32_P(CG_UPLL_SPREAD_SPECTRUM, 0, ~SSEN_MASK);

	/* set feedback divider */
	WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(optimal_fb_div), ~UPLL_FB_DIV_MASK);

	/* set ref divider to 0 */
	WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_REF_DIV_MASK);

	if (optimal_vco_freq < 187500)
		WREG32_P(CG_UPLL_FUNC_CNTL_4, 0, ~UPLL_SPARE_ISPARE9);
	else
		WREG32_P(CG_UPLL_FUNC_CNTL_4, UPLL_SPARE_ISPARE9, ~UPLL_SPARE_ISPARE9);

	/* set PDIV_A and PDIV_B */
	WREG32_P(CG_UPLL_FUNC_CNTL_2,
		UPLL_PDIV_A(optimal_vclk_div) | UPLL_PDIV_B(optimal_dclk_div),
		~(UPLL_PDIV_A_MASK | UPLL_PDIV_B_MASK));

	/* give the PLL some time to settle */
	mdelay(15);

	/* deassert PLL_RESET */
	WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK);

	mdelay(15);

	/* switch from bypass mode to normal mode */
	WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK);

	r = si_uvd_send_upll_ctlreq(rdev);
	if (r)
		return r;

	/* switch VCLK and DCLK selection */
	WREG32_P(CG_UPLL_FUNC_CNTL_2,
		VCLK_SRC_SEL(2) | DCLK_SRC_SEL(2),
		~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK));

	mdelay(100);

	return 0;
}
+29 −0
Original line number Diff line number Diff line
@@ -29,6 +29,35 @@
#define TAHITI_GB_ADDR_CONFIG_GOLDEN        0x12011003
#define VERDE_GB_ADDR_CONFIG_GOLDEN         0x12010002

/* discrete uvd clocks */
#define	CG_UPLL_FUNC_CNTL				0x634
#	define UPLL_RESET_MASK				0x00000001
#	define UPLL_SLEEP_MASK				0x00000002
#	define UPLL_BYPASS_EN_MASK			0x00000004
#	define UPLL_CTLREQ_MASK				0x00000008
#	define UPLL_VCO_MODE_MASK			0x00000600
#	define UPLL_REF_DIV_MASK			0x001F0000
#	define UPLL_CTLACK_MASK				0x40000000
#	define UPLL_CTLACK2_MASK			0x80000000
#define	CG_UPLL_FUNC_CNTL_2				0x638
#	define UPLL_PDIV_A(x)				((x) << 0)
#	define UPLL_PDIV_A_MASK				0x0000007F
#	define UPLL_PDIV_B(x)				((x) << 8)
#	define UPLL_PDIV_B_MASK				0x00007F00
#	define VCLK_SRC_SEL(x)				((x) << 20)
#	define VCLK_SRC_SEL_MASK			0x01F00000
#	define DCLK_SRC_SEL(x)				((x) << 25)
#	define DCLK_SRC_SEL_MASK			0x3E000000
#define	CG_UPLL_FUNC_CNTL_3				0x63C
#	define UPLL_FB_DIV(x)				((x) << 0)
#	define UPLL_FB_DIV_MASK				0x01FFFFFF
#define	CG_UPLL_FUNC_CNTL_4                             0x644
#	define UPLL_SPARE_ISPARE9			0x00020000
#define	CG_UPLL_FUNC_CNTL_5				0x648
#	define RESET_ANTI_MUX_MASK			0x00000200
#define	CG_UPLL_SPREAD_SPECTRUM				0x650
#	define SSEN_MASK				0x00000001

#define	CG_MULT_THERMAL_STATUS					0x714
#define		ASIC_MAX_TEMP(x)				((x) << 0)
#define		ASIC_MAX_TEMP_MASK				0x000001ff