Commit 6a4a47cf authored by Roy Spliet's avatar Roy Spliet Committed by Ben Skeggs
Browse files

drm/nva3/clk: Set PLL refclk



Signed-off-by: default avatarRoy Spliet <rspliet@eclipso.eu>
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 3d896d34
Loading
Loading
Loading
Loading
+46 −27
Original line number Diff line number Diff line
@@ -163,17 +163,12 @@ nva3_clock_read(struct nouveau_clock *clk, enum nv_clk_src src)
}

int
nva3_clock_info(struct nouveau_clock *clock, int clk, u32 pll, u32 khz,
nva3_clk_info(struct nouveau_clock *clock, int clk, u32 khz,
		struct nva3_clock_info *info)
{
	struct nouveau_bios *bios = nouveau_bios(clock);
	struct nva3_clock_priv *priv = (void *)clock;
	struct nvbios_pll limits;
	u32 oclk, sclk, sdiv;
	int P, N, M, diff;
	int ret;
	u32 oclk, sclk, sdiv, diff;

	info->pll = 0;
	info->clk = 0;

	switch (khz) {
@@ -188,40 +183,64 @@ nva3_clock_info(struct nouveau_clock *clock, int clk, u32 pll, u32 khz,
		return khz;
	default:
		sclk = read_vco(priv, clk);
		sdiv = min((sclk * 2) / (khz - 2999), (u32)65);
		/* if the clock has a PLL attached, and we can get a within
		 * [-2, 3) MHz of a divider, we'll disable the PLL and use
		 * the divider instead.
		 *
		 * divider can go as low as 2, limited here because NVIDIA
		sdiv = min((sclk * 2) / khz, (u32)65);
		oclk = (sclk * 2) / sdiv;
		diff = ((khz + 3000) - oclk);

		/* When imprecise, play it safe and aim for a clock lower than
		 * desired rather than higher */
		if (diff < 0) {
			sdiv++;
			oclk = (sclk * 2) / sdiv;
		}

		/* divider can go as low as 2, limited here because NVIDIA
		 * and the VBIOS on my NVA8 seem to prefer using the PLL
		 * for 810MHz - is there a good reason?
		 */
		 * XXX: PLLs with refclk 810MHz?  */
		if (sdiv > 4) {
			oclk = (sclk * 2) / sdiv;
			diff = khz - oclk;
			if (!pll || (diff >= -2000 && diff < 3000)) {
			info->clk = (((sdiv - 2) << 16) | 0x00003100);
			return oclk;
		}

		break;
	}

		if (!pll)
	return -ERANGE;
		break;
}

int
nva3_pll_info(struct nouveau_clock *clock, int clk, u32 pll, u32 khz,
		struct nva3_clock_info *info)
{
	struct nouveau_bios *bios = nouveau_bios(clock);
	struct nva3_clock_priv *priv = (void *)clock;
	int clk_khz;
	struct nvbios_pll limits;
	int P, N, M, diff;
	int ret;

	info->pll = 0;

	/* If we can get a within [-2, 3) MHz of a divider, we'll disable the
	 * PLL and use the divider instead. */
	clk_khz = nva3_clk_info(clock, clk, khz, info);
	diff = khz - clk_khz;
	if (!pll || (diff >= -2000 && diff < 3000)) {
		return clk_khz;
	}

	/* Try with PLL */
	ret = nvbios_pll_parse(bios, pll, &limits);
	if (ret)
		return ret;

	limits.refclk = read_clk(priv, clk - 0x10, true);
	if (!limits.refclk)
	clk_khz = nva3_clk_info(clock, clk - 0x10, limits.refclk, info);
	if (clk_khz != limits.refclk)
		return -EINVAL;

	ret = nva3_pll_calc(nv_subdev(priv), &limits, khz, &N, NULL, &M, &P);
	if (ret >= 0) {
		info->clk = nv_rd32(priv, 0x4120 + (clk * 4));
		info->pll = (P << 16) | (N << 8) | M;
	}

@@ -232,7 +251,7 @@ static int
calc_clk(struct nva3_clock_priv *priv, struct nouveau_cstate *cstate,
	 int clk, u32 pll, int idx)
{
	int ret = nva3_clock_info(&priv->base, clk, pll, cstate->domain[idx],
	int ret = nva3_pll_info(&priv->base, clk, pll, cstate->domain[idx],
				  &priv->eng[idx]);
	if (ret >= 0)
		return 0;
@@ -249,7 +268,7 @@ prog_pll(struct nva3_clock_priv *priv, int clk, u32 pll, int idx)
	const u32 coef = pll + 4;

	if (info->pll) {
		nv_mask(priv, src0, 0x00000101, 0x00000101);
		nv_mask(priv, src0, 0x003f3141, 0x00000101 | info->clk);
		nv_wr32(priv, coef, info->pll);
		nv_mask(priv, ctrl, 0x00000015, 0x00000015);
		nv_mask(priv, ctrl, 0x00000010, 0x00000000);
+1 −1
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@ struct nva3_clock_info {
	u32 pll;
};

int nva3_clock_info(struct nouveau_clock *, int, u32, u32,
int nva3_pll_info(struct nouveau_clock *, int, u32, u32,
		    struct nva3_clock_info *);

#endif
+1 −1
Original line number Diff line number Diff line
@@ -123,7 +123,7 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq)
		timing.data = 0;
	}

	ret = nva3_clock_info(nouveau_clock(pfb), 0x12, 0x4000, freq, &mclk);
	ret = nva3_pll_info(nouveau_clock(pfb), 0x12, 0x4000, freq, &mclk);
	if (ret < 0) {
		nv_error(pfb, "failed mclk calculation\n");
		return ret;