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

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
...@@ -163,17 +163,12 @@ nva3_clock_read(struct nouveau_clock *clk, enum nv_clk_src src) ...@@ -163,17 +163,12 @@ nva3_clock_read(struct nouveau_clock *clk, enum nv_clk_src src)
} }
int 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 nva3_clock_info *info)
{ {
struct nouveau_bios *bios = nouveau_bios(clock);
struct nva3_clock_priv *priv = (void *)clock; struct nva3_clock_priv *priv = (void *)clock;
struct nvbios_pll limits; u32 oclk, sclk, sdiv, diff;
u32 oclk, sclk, sdiv;
int P, N, M, diff;
int ret;
info->pll = 0;
info->clk = 0; info->clk = 0;
switch (khz) { switch (khz) {
...@@ -188,40 +183,64 @@ nva3_clock_info(struct nouveau_clock *clock, int clk, u32 pll, u32 khz, ...@@ -188,40 +183,64 @@ nva3_clock_info(struct nouveau_clock *clock, int clk, u32 pll, u32 khz,
return khz; return khz;
default: default:
sclk = read_vco(priv, clk); sclk = read_vco(priv, clk);
sdiv = min((sclk * 2) / (khz - 2999), (u32)65); sdiv = min((sclk * 2) / khz, (u32)65);
/* if the clock has a PLL attached, and we can get a within oclk = (sclk * 2) / sdiv;
* [-2, 3) MHz of a divider, we'll disable the PLL and use diff = ((khz + 3000) - oclk);
* the divider instead.
* /* When imprecise, play it safe and aim for a clock lower than
* divider can go as low as 2, limited here because NVIDIA * 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 * and the VBIOS on my NVA8 seem to prefer using the PLL
* for 810MHz - is there a good reason? * for 810MHz - is there a good reason?
*/ * XXX: PLLs with refclk 810MHz? */
if (sdiv > 4) { if (sdiv > 4) {
oclk = (sclk * 2) / sdiv;
diff = khz - oclk;
if (!pll || (diff >= -2000 && diff < 3000)) {
info->clk = (((sdiv - 2) << 16) | 0x00003100); info->clk = (((sdiv - 2) << 16) | 0x00003100);
return oclk; return oclk;
} }
break;
} }
if (!pll)
return -ERANGE; 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); ret = nvbios_pll_parse(bios, pll, &limits);
if (ret) if (ret)
return ret; return ret;
limits.refclk = read_clk(priv, clk - 0x10, true); clk_khz = nva3_clk_info(clock, clk - 0x10, limits.refclk, info);
if (!limits.refclk) if (clk_khz != limits.refclk)
return -EINVAL; return -EINVAL;
ret = nva3_pll_calc(nv_subdev(priv), &limits, khz, &N, NULL, &M, &P); ret = nva3_pll_calc(nv_subdev(priv), &limits, khz, &N, NULL, &M, &P);
if (ret >= 0) { if (ret >= 0) {
info->clk = nv_rd32(priv, 0x4120 + (clk * 4));
info->pll = (P << 16) | (N << 8) | M; info->pll = (P << 16) | (N << 8) | M;
} }
...@@ -232,7 +251,7 @@ static int ...@@ -232,7 +251,7 @@ static int
calc_clk(struct nva3_clock_priv *priv, struct nouveau_cstate *cstate, calc_clk(struct nva3_clock_priv *priv, struct nouveau_cstate *cstate,
int clk, u32 pll, int idx) 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]); &priv->eng[idx]);
if (ret >= 0) if (ret >= 0)
return 0; return 0;
...@@ -249,7 +268,7 @@ prog_pll(struct nva3_clock_priv *priv, int clk, u32 pll, int idx) ...@@ -249,7 +268,7 @@ prog_pll(struct nva3_clock_priv *priv, int clk, u32 pll, int idx)
const u32 coef = pll + 4; const u32 coef = pll + 4;
if (info->pll) { if (info->pll) {
nv_mask(priv, src0, 0x00000101, 0x00000101); nv_mask(priv, src0, 0x003f3141, 0x00000101 | info->clk);
nv_wr32(priv, coef, info->pll); nv_wr32(priv, coef, info->pll);
nv_mask(priv, ctrl, 0x00000015, 0x00000015); nv_mask(priv, ctrl, 0x00000015, 0x00000015);
nv_mask(priv, ctrl, 0x00000010, 0x00000000); nv_mask(priv, ctrl, 0x00000010, 0x00000000);
......
...@@ -8,7 +8,7 @@ struct nva3_clock_info { ...@@ -8,7 +8,7 @@ struct nva3_clock_info {
u32 pll; 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 *); struct nva3_clock_info *);
#endif #endif
...@@ -123,7 +123,7 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq) ...@@ -123,7 +123,7 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq)
timing.data = 0; 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) { if (ret < 0) {
nv_error(pfb, "failed mclk calculation\n"); nv_error(pfb, "failed mclk calculation\n");
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment