diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 1f472ff..6c3483a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -4029,6 +4029,7 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims case 0x20: case 0x21: case 0x30: + case 0x40: headerlen = bios->data[bios->pll_limit_tbl_ptr + 1]; recordlen = bios->data[bios->pll_limit_tbl_ptr + 2]; entries = bios->data[bios->pll_limit_tbl_ptr + 3]; @@ -4177,7 +4178,7 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims pll_lim->refclk = 25000; } } - } else if (pll_lim_ver) { /* ver 0x30 */ + } else if (pll_lim_ver == 0x30) { /* ver 0x30 */ uint8_t *entry = &bios->data[bios->pll_limit_tbl_ptr + headerlen]; uint8_t *record = NULL; int i; @@ -4217,6 +4218,42 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims pll_lim->max_usable_log2p = pll_lim->max_log2p = record[25]; pll_lim->log2p_bias = record[27]; pll_lim->refclk = ROM32(record[28]); + } else if (pll_lim_ver) { /* ver 0x40 */ + uint8_t *entry = &bios->data[bios->pll_limit_tbl_ptr + headerlen]; + uint8_t *record = NULL; + int i; + + BIOSLOG(bios, "Loading PLL limits for register 0x%08x\n", + limit_match); + + for (i = 0; i < entries; i++, entry += recordlen) { + if (ROM32(entry[3]) == limit_match) { + record = &bios->data[ROM16(entry[1])]; + break; + } + } + + if (!record) { + NV_ERROR(dev, "Register 0x%08x not found in PLL " + "limits table", limit_match); + return -ENOENT; + } + + pll_lim->vco1.minfreq = ROM16(record[0]) * 1000; + pll_lim->vco1.maxfreq = ROM16(record[2]) * 1000; + pll_lim->vco1.min_inputfreq = ROM16(record[4]) * 1000; + pll_lim->vco1.max_inputfreq = ROM16(record[6]) * 1000; + pll_lim->vco1.min_m = record[8]; + pll_lim->vco1.max_m = record[9]; + pll_lim->vco1.min_n = record[10]; + pll_lim->vco1.max_n = record[11]; + pll_lim->min_p = record[12]; + pll_lim->max_p = record[13]; + /* where did this go to?? */ + if (limit_match == 0x00614100 || limit_match == 0x00614900) + pll_lim->refclk = 27000; + else + pll_lim->refclk = 100000; } /* diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index a1b8e88..d893e40 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -156,6 +156,10 @@ struct pll_lims { */ uint8_t max_usable_log2p; uint8_t log2p_bias; + + uint8_t min_p; + uint8_t max_p; + int refclk; }; diff --git a/drivers/gpu/drm/nouveau/nouveau_calc.c b/drivers/gpu/drm/nouveau/nouveau_calc.c index 3f80db8..2220ddf 100644 --- a/drivers/gpu/drm/nouveau/nouveau_calc.c +++ b/drivers/gpu/drm/nouveau/nouveau_calc.c @@ -432,40 +432,46 @@ getMNP_single(struct drm_device *dev, struct pll_lims *pll_lim, int clk, int minvco = pll_lim->vco1.minfreq, maxvco = pll_lim->vco1.maxfreq; int minM = pll_lim->vco1.min_m, maxM = pll_lim->vco1.max_m; int minN = pll_lim->vco1.min_n, maxN = pll_lim->vco1.max_n; - int minU = pll_lim->vco1.min_inputfreq, maxU = pll_lim->vco1.max_inputfreq; - int maxlog2P = pll_lim->max_usable_log2p; + int minU = pll_lim->vco1.min_inputfreq; + int maxU = pll_lim->vco1.max_inputfreq; + int minP = pll_lim->max_p ? pll_lim->min_p : 0; + int maxP = pll_lim->max_p ? pll_lim->max_p : pll_lim->max_usable_log2p; int crystal = pll_lim->refclk; - int M, N, log2P, P; + int M, N, thisP, P; int clkP, calcclk; int delta, bestdelta = INT_MAX; int bestclk = 0; /* this division verified for nv20, nv18, nv28 (Haiku), and nv34 */ /* possibly correlated with introduction of 27MHz crystal */ - if (cv < 0x17 || cv == 0x1a || cv == 0x20) { - if (clk > 250000) - maxM = 6; - if (clk > 340000) - maxM = 2; - } else if (cv < 0x40) { - if (clk > 150000) - maxM = 6; - if (clk > 200000) - maxM = 4; - if (clk > 340000) - maxM = 2; + if (dev_priv->card_type < NV_50) { + if (cv < 0x17 || cv == 0x1a || cv == 0x20) { + if (clk > 250000) + maxM = 6; + if (clk > 340000) + maxM = 2; + } else if (cv < 0x40) { + if (clk > 150000) + maxM = 6; + if (clk > 200000) + maxM = 4; + if (clk > 340000) + maxM = 2; + } } - if ((clk << maxlog2P) < minvco) { - minvco = clk << maxlog2P; + P = pll_lim->max_p ? maxP : (1 << maxP); + if ((clk * P) < minvco) { + minvco = clk * maxP; maxvco = minvco * 2; } + if (clk + clk/200 > maxvco) /* +0.5% */ maxvco = clk + clk/200; /* NV34 goes maxlog2P->0, NV20 goes 0->maxlog2P */ - for (log2P = 0; log2P <= maxlog2P; log2P++) { - P = 1 << log2P; + for (thisP = minP; thisP <= maxP; thisP++) { + P = pll_lim->max_p ? thisP : (1 << thisP); clkP = clk * P; if (clkP < minvco) @@ -498,7 +504,7 @@ getMNP_single(struct drm_device *dev, struct pll_lims *pll_lim, int clk, bestclk = calcclk; bestpv->N1 = N; bestpv->M1 = M; - bestpv->log2P = log2P; + bestpv->log2P = thisP; if (delta == 0) /* except this one */ return bestclk; }