drm/nouveau/clock: pull in the implementation from all over the place

Still missing the main bits we use to change performance levels, I'll get
to it after all the hard yakka has been finished.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
Ben Skeggs
2012-07-10 17:26:46 +10:00
parent 8aceb7de47
commit 70790f4f81
28 changed files with 1329 additions and 1214 deletions

View File

@@ -23,11 +23,14 @@ nouveau-y += core/subdev/bios/bit.o
nouveau-y += core/subdev/bios/dcb.o
nouveau-y += core/subdev/bios/gpio.o
nouveau-y += core/subdev/bios/i2c.o
nouveau-y += core/subdev/bios/pll.o
nouveau-y += core/subdev/clock/nv04.o
nouveau-y += core/subdev/clock/nv40.o
nouveau-y += core/subdev/clock/nv50.o
nouveau-y += core/subdev/clock/nva3.o
nouveau-y += core/subdev/clock/nvc0.o
nouveau-y += core/subdev/clock/pllnv04.o
nouveau-y += core/subdev/clock/pllnva3.o
nouveau-y += core/subdev/device/base.o
nouveau-y += core/subdev/device/nv04.o
nouveau-y += core/subdev/device/nv10.o
@@ -114,7 +117,6 @@ nouveau-y += nouveau_drm.o nouveau_compat.o \
nv50_cursor.o nv50_display.o \
nvd0_display.o \
nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o \
nv50_calc.o \
nv04_pm.o nv40_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o \
nouveau_prime.o

View File

@@ -0,0 +1,77 @@
#ifndef __NVBIOS_PLL_H__
#define __NVBIOS_PLL_H__
/*XXX: kill me */
struct nouveau_pll_vals {
union {
struct {
#ifdef __BIG_ENDIAN
uint8_t N1, M1, N2, M2;
#else
uint8_t M1, N1, M2, N2;
#endif
};
struct {
uint16_t NM1, NM2;
} __attribute__((packed));
};
int log2P;
int refclk;
};
struct nouveau_bios;
/* these match types in pll limits table version 0x40,
* nouveau uses them on all chipsets internally where a
* specific pll needs to be referenced, but the exact
* register isn't known.
*/
enum nvbios_pll_type {
PLL_CORE = 0x01,
PLL_SHADER = 0x02,
PLL_UNK03 = 0x03,
PLL_MEMORY = 0x04,
PLL_VDEC = 0x05,
PLL_UNK40 = 0x40,
PLL_UNK41 = 0x41,
PLL_UNK42 = 0x42,
PLL_VPLL0 = 0x80,
PLL_VPLL1 = 0x81,
PLL_MAX = 0xff
};
struct nvbios_pll {
enum nvbios_pll_type type;
u32 reg;
u32 refclk;
u8 min_p;
u8 max_p;
u8 bias_p;
/*
* for most pre nv50 cards setting a log2P of 7 (the common max_log2p
* value) is no different to 6 (at least for vplls) so allowing the MNP
* calc to use 7 causes the generated clock to be out by a factor of 2.
* however, max_log2p cannot be fixed-up during parsing as the
* unmodified max_log2p value is still needed for setting mplls, hence
* an additional max_usable_log2p member
*/
u8 max_p_usable;
struct {
u32 min_freq;
u32 max_freq;
u32 min_inputfreq;
u32 max_inputfreq;
u8 min_m;
u8 max_m;
u8 min_n;
u8 max_n;
} vco1, vco2;
};
int nvbios_pll_parse(struct nouveau_bios *, u32 type, struct nvbios_pll *);
#endif

View File

@@ -4,9 +4,21 @@
#include <core/device.h>
#include <core/subdev.h>
struct nouveau_pll_vals;
struct nvbios_pll;
struct nouveau_clock {
struct nouveau_subdev base;
void (*pll_set)(struct nouveau_clock *, u32 type, u32 freq);
int (*pll_set)(struct nouveau_clock *, u32 type, u32 freq);
/*XXX: die, these are here *only* to support the completely
* bat-shit insane what-was-nouveau_hw.c code
*/
int (*pll_calc)(struct nouveau_clock *, struct nvbios_pll *,
int clk, struct nouveau_pll_vals *pv);
int (*pll_prog)(struct nouveau_clock *, u32 reg1,
struct nouveau_pll_vals *pv);
};
static inline struct nouveau_clock *
@@ -37,4 +49,11 @@ extern struct nouveau_oclass nv50_clock_oclass;
extern struct nouveau_oclass nva3_clock_oclass;
extern struct nouveau_oclass nvc0_clock_oclass;
int nv04_clock_pll_set(struct nouveau_clock *, u32 type, u32 freq);
int nv04_clock_pll_calc(struct nouveau_clock *, struct nvbios_pll *,
int clk, struct nouveau_pll_vals *);
int nv04_clock_pll_prog(struct nouveau_clock *, u32 reg1,
struct nouveau_pll_vals *);
#endif

View File

@@ -1,6 +1,8 @@
#ifndef __NOUVEAU_VGA_H__
#define __NOUVEAU_VGA_H__
#include <core/os.h>
/* access to various legacy io ports */
u8 nv_rdport(void *obj, int head, u16 port);
void nv_wrport(void *obj, int head, u16 port, u8 value);

View File

@@ -0,0 +1,417 @@
/*
* Copyright 2005-2006 Erik Waling
* Copyright 2006 Stephane Marchesin
* Copyright 2007-2009 Stuart Bennett
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <subdev/vga.h>
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/bmp.h>
#include <subdev/bios/pll.h>
struct pll_mapping {
u8 type;
u32 reg;
};
static struct pll_mapping
nv04_pll_mapping[] = {
{ PLL_CORE , 0x680500 },
{ PLL_MEMORY, 0x680504 },
{ PLL_VPLL0 , 0x680508 },
{ PLL_VPLL1 , 0x680520 },
{}
};
static struct pll_mapping
nv40_pll_mapping[] = {
{ PLL_CORE , 0x004000 },
{ PLL_MEMORY, 0x004020 },
{ PLL_VPLL0 , 0x680508 },
{ PLL_VPLL1 , 0x680520 },
{}
};
static struct pll_mapping
nv50_pll_mapping[] = {
{ PLL_CORE , 0x004028 },
{ PLL_SHADER, 0x004020 },
{ PLL_UNK03 , 0x004000 },
{ PLL_MEMORY, 0x004008 },
{ PLL_UNK40 , 0x00e810 },
{ PLL_UNK41 , 0x00e818 },
{ PLL_UNK42 , 0x00e824 },
{ PLL_VPLL0 , 0x614100 },
{ PLL_VPLL1 , 0x614900 },
{}
};
static struct pll_mapping
nv84_pll_mapping[] = {
{ PLL_CORE , 0x004028 },
{ PLL_SHADER, 0x004020 },
{ PLL_MEMORY, 0x004008 },
{ PLL_VDEC , 0x004030 },
{ PLL_UNK41 , 0x00e818 },
{ PLL_VPLL0 , 0x614100 },
{ PLL_VPLL1 , 0x614900 },
{}
};
static u16
pll_limits_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
struct bit_entry bit_C;
if (!bit_entry(bios, 'C', &bit_C) && bit_C.length >= 10) {
u16 data = nv_ro16(bios, bit_C.offset + 8);
if (data) {
*ver = nv_ro08(bios, data + 0);
*hdr = nv_ro08(bios, data + 1);
*len = nv_ro08(bios, data + 2);
*cnt = nv_ro08(bios, data + 3);
return data;
}
}
if (bmp_version(bios) >= 0x0524) {
u16 data = nv_ro16(bios, bios->bmp_offset + 142);
if (data) {
*ver = nv_ro08(bios, data + 0);
*hdr = 1;
*cnt = 1;
*len = 0x18;
return data;
}
}
*ver = 0x00;
return 0x0000;
}
static struct pll_mapping *
pll_map(struct nouveau_bios *bios)
{
switch (nv_device(bios)->card_type) {
case NV_04:
case NV_10:
case NV_20:
case NV_30:
return nv04_pll_mapping;
break;
case NV_40:
return nv40_pll_mapping;
case NV_50:
if (nv_device(bios)->chipset == 0x50)
return nv50_pll_mapping;
else
if (nv_device(bios)->chipset < 0xa3 ||
nv_device(bios)->chipset == 0xaa ||
nv_device(bios)->chipset == 0xac)
return nv84_pll_mapping;
default:
return NULL;
}
}
static u16
pll_map_reg(struct nouveau_bios *bios, u32 reg, u32 *type, u8 *ver, u8 *len)
{
struct pll_mapping *map;
u8 hdr, cnt;
u16 data;
data = pll_limits_table(bios, ver, &hdr, &cnt, len);
if (data && *ver >= 0x30) {
data += hdr;
while (cnt--) {
if (nv_ro32(bios, data + 3) == reg) {
*type = nv_ro08(bios, data + 0);
return data;
}
data += *len;
}
return 0x0000;
}
map = pll_map(bios);
while (map->reg) {
if (map->reg == reg && *ver >= 0x20) {
u16 addr = (data += hdr);
while (cnt--) {
if (nv_ro32(bios, data) == map->reg) {
*type = map->type;
return data;
}
data += *len;
}
return addr;
} else
if (map->reg == reg) {
*type = map->type;
return data + 1;
}
map++;
}
return 0x0000;
}
static u16
pll_map_type(struct nouveau_bios *bios, u8 type, u32 *reg, u8 *ver, u8 *len)
{
struct pll_mapping *map;
u8 hdr, cnt;
u16 data;
data = pll_limits_table(bios, ver, &hdr, &cnt, len);
if (data && *ver >= 0x30) {
data += hdr;
while (cnt--) {
if (nv_ro08(bios, data + 0) == type) {
*reg = nv_ro32(bios, data + 3);
return data;
}
data += *len;
}
return 0x0000;
}
map = pll_map(bios);
while (map->reg) {
if (map->type == type && *ver >= 0x20) {
u16 addr = (data += hdr);
while (cnt--) {
if (nv_ro32(bios, data) == map->reg) {
*reg = map->reg;
return data;
}
data += *len;
}
return addr;
} else
if (map->type == type) {
*reg = map->reg;
return data + 1;
}
map++;
}
return 0x0000;
}
int
nvbios_pll_parse(struct nouveau_bios *bios, u32 type, struct nvbios_pll *info)
{
u8 ver, len;
u32 reg = type;
u16 data;
if (type > PLL_MAX) {
reg = type;
data = pll_map_reg(bios, reg, &type, &ver, &len);
} else {
data = pll_map_type(bios, type, &reg, &ver, &len);
}
if (ver && !data)
return -ENOENT;
memset(info, 0, sizeof(*info));
info->type = type;
info->reg = reg;
switch (ver) {
case 0x00:
break;
case 0x10:
case 0x11:
info->vco1.min_freq = nv_ro32(bios, data + 0);
info->vco1.max_freq = nv_ro32(bios, data + 4);
info->vco2.min_freq = nv_ro32(bios, data + 8);
info->vco2.max_freq = nv_ro32(bios, data + 12);
info->vco1.min_inputfreq = nv_ro32(bios, data + 16);
info->vco2.min_inputfreq = nv_ro32(bios, data + 20);
info->vco1.max_inputfreq = INT_MAX;
info->vco2.max_inputfreq = INT_MAX;
info->max_p = 0x7;
info->max_p_usable = 0x6;
/* these values taken from nv30/31/36 */
switch (bios->version.chip) {
case 0x36:
info->vco1.min_n = 0x5;
break;
default:
info->vco1.min_n = 0x1;
break;
}
info->vco1.max_n = 0xff;
info->vco1.min_m = 0x1;
info->vco1.max_m = 0xd;
/*
* On nv30, 31, 36 (i.e. all cards with two stage PLLs with this
* table version (apart from nv35)), N2 is compared to
* maxN2 (0x46) and 10 * maxM2 (0x4), so set maxN2 to 0x28 and
* save a comparison
*/
info->vco2.min_n = 0x4;
switch (bios->version.chip) {
case 0x30:
case 0x35:
info->vco2.max_n = 0x1f;
break;
default:
info->vco2.max_n = 0x28;
break;
}
info->vco2.min_m = 0x1;
info->vco2.max_m = 0x4;
break;
case 0x20:
case 0x21:
info->vco1.min_freq = nv_ro16(bios, data + 4) * 1000;
info->vco1.max_freq = nv_ro16(bios, data + 6) * 1000;
info->vco2.min_freq = nv_ro16(bios, data + 8) * 1000;
info->vco2.max_freq = nv_ro16(bios, data + 10) * 1000;
info->vco1.min_inputfreq = nv_ro16(bios, data + 12) * 1000;
info->vco2.min_inputfreq = nv_ro16(bios, data + 14) * 1000;
info->vco1.max_inputfreq = nv_ro16(bios, data + 16) * 1000;
info->vco2.max_inputfreq = nv_ro16(bios, data + 18) * 1000;
info->vco1.min_n = nv_ro08(bios, data + 20);
info->vco1.max_n = nv_ro08(bios, data + 21);
info->vco1.min_m = nv_ro08(bios, data + 22);
info->vco1.max_m = nv_ro08(bios, data + 23);
info->vco2.min_n = nv_ro08(bios, data + 24);
info->vco2.max_n = nv_ro08(bios, data + 25);
info->vco2.min_m = nv_ro08(bios, data + 26);
info->vco2.max_m = nv_ro08(bios, data + 27);
info->max_p = nv_ro08(bios, data + 29);
info->max_p_usable = info->max_p;
if (bios->version.chip < 0x60)
info->max_p_usable = 0x6;
info->bias_p = nv_ro08(bios, data + 30);
if (len > 0x22)
info->refclk = nv_ro32(bios, data + 31);
break;
case 0x30:
data = nv_ro16(bios, data + 1);
info->vco1.min_freq = nv_ro16(bios, data + 0) * 1000;
info->vco1.max_freq = nv_ro16(bios, data + 2) * 1000;
info->vco2.min_freq = nv_ro16(bios, data + 4) * 1000;
info->vco2.max_freq = nv_ro16(bios, data + 6) * 1000;
info->vco1.min_inputfreq = nv_ro16(bios, data + 8) * 1000;
info->vco2.min_inputfreq = nv_ro16(bios, data + 10) * 1000;
info->vco1.max_inputfreq = nv_ro16(bios, data + 12) * 1000;
info->vco2.max_inputfreq = nv_ro16(bios, data + 14) * 1000;
info->vco1.min_n = nv_ro08(bios, data + 16);
info->vco1.max_n = nv_ro08(bios, data + 17);
info->vco1.min_m = nv_ro08(bios, data + 18);
info->vco1.max_m = nv_ro08(bios, data + 19);
info->vco2.min_n = nv_ro08(bios, data + 20);
info->vco2.max_n = nv_ro08(bios, data + 21);
info->vco2.min_m = nv_ro08(bios, data + 22);
info->vco2.max_m = nv_ro08(bios, data + 23);
info->max_p_usable = info->max_p = nv_ro08(bios, data + 25);
info->bias_p = nv_ro08(bios, data + 27);
info->refclk = nv_ro32(bios, data + 28);
break;
case 0x40:
info->refclk = nv_ro16(bios, data + 9) * 1000;
data = nv_ro16(bios, data + 1);
info->vco1.min_freq = nv_ro16(bios, data + 0) * 1000;
info->vco1.max_freq = nv_ro16(bios, data + 2) * 1000;
info->vco1.min_inputfreq = nv_ro16(bios, data + 4) * 1000;
info->vco1.max_inputfreq = nv_ro16(bios, data + 6) * 1000;
info->vco1.min_m = nv_ro08(bios, data + 8);
info->vco1.max_m = nv_ro08(bios, data + 9);
info->vco1.min_n = nv_ro08(bios, data + 10);
info->vco1.max_n = nv_ro08(bios, data + 11);
info->min_p = nv_ro08(bios, data + 12);
info->max_p = nv_ro08(bios, data + 13);
break;
default:
nv_error(bios, "unknown pll limits version 0x%02x\n", ver);
return -EINVAL;
}
if (!info->refclk) {
info->refclk = nv_device(bios)->crystal;
if (bios->version.chip == 0x51) {
u32 sel_clk = nv_rd32(bios, 0x680524);
if ((info->reg == 0x680508 && sel_clk & 0x20) ||
(info->reg == 0x680520 && sel_clk & 0x80)) {
if (nv_rdvgac(bios, 0, 0x27) < 0xa3)
info->refclk = 200000;
else
info->refclk = 25000;
}
}
}
/*
* By now any valid limit table ought to have set a max frequency for
* vco1, so if it's zero it's either a pre limit table bios, or one
* with an empty limit table (seen on nv18)
*/
if (!info->vco1.max_freq) {
info->vco1.max_freq = nv_ro32(bios, bios->bmp_offset + 67);
info->vco1.min_freq = nv_ro32(bios, bios->bmp_offset + 71);
if (bmp_version(bios) < 0x0506) {
info->vco1.max_freq = 256000;
info->vco1.min_freq = 128000;
}
info->vco1.min_inputfreq = 0;
info->vco1.max_inputfreq = INT_MAX;
info->vco1.min_n = 0x1;
info->vco1.max_n = 0xff;
info->vco1.min_m = 0x1;
if (nv_device(bios)->crystal == 13500) {
/* nv05 does this, nv11 doesn't, nv10 unknown */
if (bios->version.chip < 0x11)
info->vco1.min_m = 0x7;
info->vco1.max_m = 0xd;
} else {
if (bios->version.chip < 0x11)
info->vco1.min_m = 0x8;
info->vco1.max_m = 0xe;
}
if (bios->version.chip < 0x17 ||
bios->version.chip == 0x1a ||
bios->version.chip == 0x20)
info->max_p = 4;
else
info->max_p = 5;
info->max_p_usable = info->max_p;
}
return 0;
}

View File

@@ -23,17 +23,309 @@
*/
#include <subdev/clock.h>
#include <subdev/bios.h>
#include <subdev/bios/pll.h>
#include "pll.h"
struct nv04_clock_priv {
struct nouveau_clock base;
};
static int
powerctrl_1_shift(int chip_version, int reg)
{
int shift = -4;
if (chip_version < 0x17 || chip_version == 0x1a || chip_version == 0x20)
return shift;
switch (reg) {
case 0x680520:
shift += 4;
case 0x680508:
shift += 4;
case 0x680504:
shift += 4;
case 0x680500:
shift += 4;
}
/*
* the shift for vpll regs is only used for nv3x chips with a single
* stage pll
*/
if (shift > 4 && (chip_version < 0x32 || chip_version == 0x35 ||
chip_version == 0x36 || chip_version >= 0x40))
shift = -4;
return shift;
}
static void
setPLL_single(struct nv04_clock_priv *priv, u32 reg,
struct nouveau_pll_vals *pv)
{
int chip_version = nouveau_bios(priv)->version.chip;
uint32_t oldpll = nv_rd32(priv, reg);
int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff;
uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1;
uint32_t saved_powerctrl_1 = 0;
int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg);
if (oldpll == pll)
return; /* already set */
if (shift_powerctrl_1 >= 0) {
saved_powerctrl_1 = nv_rd32(priv, 0x001584);
nv_wr32(priv, 0x001584,
(saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
1 << shift_powerctrl_1);
}
if (oldM && pv->M1 && (oldN / oldM < pv->N1 / pv->M1))
/* upclock -- write new post divider first */
nv_wr32(priv, reg, pv->log2P << 16 | (oldpll & 0xffff));
else
/* downclock -- write new NM first */
nv_wr32(priv, reg, (oldpll & 0xffff0000) | pv->NM1);
if (chip_version < 0x17 && chip_version != 0x11)
/* wait a bit on older chips */
msleep(64);
nv_rd32(priv, reg);
/* then write the other half as well */
nv_wr32(priv, reg, pll);
if (shift_powerctrl_1 >= 0)
nv_wr32(priv, 0x001584, saved_powerctrl_1);
}
static uint32_t
new_ramdac580(uint32_t reg1, bool ss, uint32_t ramdac580)
{
bool head_a = (reg1 == 0x680508);
if (ss) /* single stage pll mode */
ramdac580 |= head_a ? 0x00000100 : 0x10000000;
else
ramdac580 &= head_a ? 0xfffffeff : 0xefffffff;
return ramdac580;
}
static void
setPLL_double_highregs(struct nv04_clock_priv *priv, u32 reg1,
struct nouveau_pll_vals *pv)
{
int chip_version = nouveau_bios(priv)->version.chip;
bool nv3035 = chip_version == 0x30 || chip_version == 0x35;
uint32_t reg2 = reg1 + ((reg1 == 0x680520) ? 0x5c : 0x70);
uint32_t oldpll1 = nv_rd32(priv, reg1);
uint32_t oldpll2 = !nv3035 ? nv_rd32(priv, reg2) : 0;
uint32_t pll1 = (oldpll1 & 0xfff80000) | pv->log2P << 16 | pv->NM1;
uint32_t pll2 = (oldpll2 & 0x7fff0000) | 1 << 31 | pv->NM2;
uint32_t oldramdac580 = 0, ramdac580 = 0;
bool single_stage = !pv->NM2 || pv->N2 == pv->M2; /* nv41+ only */
uint32_t saved_powerctrl_1 = 0, savedc040 = 0;
int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg1);
/* model specific additions to generic pll1 and pll2 set up above */
if (nv3035) {
pll1 = (pll1 & 0xfcc7ffff) | (pv->N2 & 0x18) << 21 |
(pv->N2 & 0x7) << 19 | 8 << 4 | (pv->M2 & 7) << 4;
pll2 = 0;
}
if (chip_version > 0x40 && reg1 >= 0x680508) { /* !nv40 */
oldramdac580 = nv_rd32(priv, 0x680580);
ramdac580 = new_ramdac580(reg1, single_stage, oldramdac580);
if (oldramdac580 != ramdac580)
oldpll1 = ~0; /* force mismatch */
if (single_stage)
/* magic value used by nvidia in single stage mode */
pll2 |= 0x011f;
}
if (chip_version > 0x70)
/* magic bits set by the blob (but not the bios) on g71-73 */
pll1 = (pll1 & 0x7fffffff) | (single_stage ? 0x4 : 0xc) << 28;
if (oldpll1 == pll1 && oldpll2 == pll2)
return; /* already set */
if (shift_powerctrl_1 >= 0) {
saved_powerctrl_1 = nv_rd32(priv, 0x001584);
nv_wr32(priv, 0x001584,
(saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
1 << shift_powerctrl_1);
}
if (chip_version >= 0x40) {
int shift_c040 = 14;
switch (reg1) {
case 0x680504:
shift_c040 += 2;
case 0x680500:
shift_c040 += 2;
case 0x680520:
shift_c040 += 2;
case 0x680508:
shift_c040 += 2;
}
savedc040 = nv_rd32(priv, 0xc040);
if (shift_c040 != 14)
nv_wr32(priv, 0xc040, savedc040 & ~(3 << shift_c040));
}
if (oldramdac580 != ramdac580)
nv_wr32(priv, 0x680580, ramdac580);
if (!nv3035)
nv_wr32(priv, reg2, pll2);
nv_wr32(priv, reg1, pll1);
if (shift_powerctrl_1 >= 0)
nv_wr32(priv, 0x001584, saved_powerctrl_1);
if (chip_version >= 0x40)
nv_wr32(priv, 0xc040, savedc040);
}
static void
setPLL_double_lowregs(struct nv04_clock_priv *priv, u32 NMNMreg,
struct nouveau_pll_vals *pv)
{
/* When setting PLLs, there is a merry game of disabling and enabling
* various bits of hardware during the process. This function is a
* synthesis of six nv4x traces, nearly each card doing a subtly
* different thing. With luck all the necessary bits for each card are
* combined herein. Without luck it deviates from each card's formula
* so as to not work on any :)
*/
uint32_t Preg = NMNMreg - 4;
bool mpll = Preg == 0x4020;
uint32_t oldPval = nv_rd32(priv, Preg);
uint32_t NMNM = pv->NM2 << 16 | pv->NM1;
uint32_t Pval = (oldPval & (mpll ? ~(0x77 << 16) : ~(7 << 16))) |
0xc << 28 | pv->log2P << 16;
uint32_t saved4600 = 0;
/* some cards have different maskc040s */
uint32_t maskc040 = ~(3 << 14), savedc040;
bool single_stage = !pv->NM2 || pv->N2 == pv->M2;
if (nv_rd32(priv, NMNMreg) == NMNM && (oldPval & 0xc0070000) == Pval)
return;
if (Preg == 0x4000)
maskc040 = ~0x333;
if (Preg == 0x4058)
maskc040 = ~(0xc << 24);
if (mpll) {
struct nvbios_pll info;
uint8_t Pval2;
if (nvbios_pll_parse(nouveau_bios(priv), Preg, &info))
return;
Pval2 = pv->log2P + info.bias_p;
if (Pval2 > info.max_p)
Pval2 = info.max_p;
Pval |= 1 << 28 | Pval2 << 20;
saved4600 = nv_rd32(priv, 0x4600);
nv_wr32(priv, 0x4600, saved4600 | 8 << 28);
}
if (single_stage)
Pval |= mpll ? 1 << 12 : 1 << 8;
nv_wr32(priv, Preg, oldPval | 1 << 28);
nv_wr32(priv, Preg, Pval & ~(4 << 28));
if (mpll) {
Pval |= 8 << 20;
nv_wr32(priv, 0x4020, Pval & ~(0xc << 28));
nv_wr32(priv, 0x4038, Pval & ~(0xc << 28));
}
savedc040 = nv_rd32(priv, 0xc040);
nv_wr32(priv, 0xc040, savedc040 & maskc040);
nv_wr32(priv, NMNMreg, NMNM);
if (NMNMreg == 0x4024)
nv_wr32(priv, 0x403c, NMNM);
nv_wr32(priv, Preg, Pval);
if (mpll) {
Pval &= ~(8 << 20);
nv_wr32(priv, 0x4020, Pval);
nv_wr32(priv, 0x4038, Pval);
nv_wr32(priv, 0x4600, saved4600);
}
nv_wr32(priv, 0xc040, savedc040);
if (mpll) {
nv_wr32(priv, 0x4020, Pval & ~(1 << 28));
nv_wr32(priv, 0x4038, Pval & ~(1 << 28));
}
}
int
nv04_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq)
{
struct nv04_clock_priv *priv = (void *)clk;
struct nouveau_pll_vals pv;
struct nvbios_pll info;
int ret;
nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
ret = nvbios_pll_parse(nouveau_bios(priv), type > 0x405c ?
type : type - 4, &info);
if (ret)
return ret;
ret = clk->pll_calc(clk, &info, freq, &pv);
if (!ret)
return ret;
return clk->pll_prog(clk, type, &pv);
}
int
nv04_clock_pll_calc(struct nouveau_clock *clock, struct nvbios_pll *info,
int clk, struct nouveau_pll_vals *pv)
{
int N1, M1, N2, M2, P;
int ret = nv04_pll_calc(clock, info, clk, &N1, &M1, &N2, &M2, &P);
if (ret) {
pv->refclk = info->refclk;
pv->N1 = N1;
pv->M1 = M1;
pv->N2 = N2;
pv->M2 = M2;
pv->log2P = P;
}
return ret;
}
int
nv04_clock_pll_prog(struct nouveau_clock *clk, u32 reg1,
struct nouveau_pll_vals *pv)
{
struct nv04_clock_priv *priv = (void *)clk;
int cv = nouveau_bios(clk)->version.chip;
if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
cv >= 0x40) {
if (reg1 > 0x405c)
setPLL_double_highregs(priv, reg1, pv);
else
setPLL_double_lowregs(priv, reg1, pv);
} else
setPLL_single(priv, reg1, pv);
return 0;
}
static int
@@ -50,6 +342,8 @@ nv04_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
return ret;
priv->base.pll_set = nv04_clock_pll_set;
priv->base.pll_calc = nv04_clock_pll_calc;
priv->base.pll_prog = nv04_clock_pll_prog;
return 0;
}

View File

@@ -28,14 +28,6 @@ struct nv40_clock_priv {
struct nouveau_clock base;
};
static void
nv40_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq)
{
struct nv40_clock_priv *priv = (void *)clk;
nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
}
static int
nv40_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
@@ -49,7 +41,9 @@ nv40_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
priv->base.pll_set = nv40_clock_pll_set;
priv->base.pll_set = nv04_clock_pll_set;
priv->base.pll_calc = nv04_clock_pll_calc;
priv->base.pll_prog = nv04_clock_pll_prog;
return 0;
}

View File

@@ -23,17 +23,57 @@
*/
#include <subdev/clock.h>
#include <subdev/bios.h>
#include <subdev/bios/pll.h>
#include "pll.h"
struct nv50_clock_priv {
struct nouveau_clock base;
};
static void
static int
nv50_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq)
{
struct nv50_clock_priv *priv = (void *)clk;
struct nouveau_bios *bios = nouveau_bios(priv);
struct nvbios_pll info;
int N1, M1, N2, M2, P;
int ret;
nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
ret = nvbios_pll_parse(bios, type, &info);
if (ret) {
nv_error(clk, "failed to retrieve pll data, %d\n", ret);
return ret;
}
ret = nv04_pll_calc(clk, &info, freq, &N1, &M1, &N2, &M2, &P);
if (!ret) {
nv_error(clk, "failed pll calculation\n");
return ret;
}
switch (info.type) {
case PLL_VPLL0:
case PLL_VPLL1:
nv_wr32(priv, info.reg + 0, 0x10000611);
nv_mask(priv, info.reg + 4, 0x00ff00ff, (M1 << 16) | N1);
nv_mask(priv, info.reg + 8, 0x7fff00ff, (P << 28) |
(M2 << 16) | N2);
break;
case PLL_MEMORY:
nv_mask(priv, info.reg + 0, 0x01ff0000, (P << 22) |
(info.bias_p << 19) |
(P << 16));
nv_wr32(priv, info.reg + 4, (N1 << 8) | M1);
break;
default:
nv_mask(priv, info.reg + 0, 0x00070000, (P << 16));
nv_wr32(priv, info.reg + 4, (N1 << 8) | M1);
break;
}
return 0;
}
static int

View File

@@ -23,17 +23,47 @@
*/
#include <subdev/clock.h>
#include <subdev/bios.h>
#include <subdev/bios/pll.h>
#include "pll.h"
struct nva3_clock_priv {
struct nouveau_clock base;
};
static void
static int
nva3_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq)
{
struct nva3_clock_priv *priv = (void *)clk;
struct nouveau_bios *bios = nouveau_bios(priv);
struct nvbios_pll info;
int N, fN, M, P;
int ret;
nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
ret = nvbios_pll_parse(bios, type, &info);
if (ret)
return ret;
ret = nva3_pll_calc(clk, &info, freq, &N, &fN, &M, &P);
if (ret < 0)
return ret;
switch (info.type) {
case PLL_VPLL0:
case PLL_VPLL1:
nv_wr32(priv, info.reg + 0, 0x50000610);
nv_mask(priv, info.reg + 4, 0x003fffff,
(P << 16) | (M << 8) | N);
nv_wr32(priv, info.reg + 8, fN);
break;
default:
nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
ret = -EINVAL;
break;
}
return ret;
}
static int

View File

@@ -23,17 +23,46 @@
*/
#include <subdev/clock.h>
#include <subdev/bios.h>
#include <subdev/bios/pll.h>
#include "pll.h"
struct nvc0_clock_priv {
struct nouveau_clock base;
};
static void
static int
nvc0_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq)
{
struct nvc0_clock_priv *priv = (void *)clk;
struct nouveau_bios *bios = nouveau_bios(priv);
struct nvbios_pll info;
int N, fN, M, P;
int ret;
nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
ret = nvbios_pll_parse(bios, type, &info);
if (ret)
return ret;
ret = nva3_pll_calc(clk, &info, freq, &N, &fN, &M, &P);
if (ret < 0)
return ret;
switch (info.type) {
case PLL_VPLL0:
case PLL_VPLL1:
nv_mask(priv, info.reg + 0x0c, 0x00000000, 0x00000100);
nv_wr32(priv, info.reg + 0x04, (P << 16) | (N << 8) | M);
nv_wr32(priv, info.reg + 0x10, fN << 16);
break;
default:
nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
ret = -EINVAL;
break;
}
return ret;
}
static int

View File

@@ -0,0 +1,9 @@
#ifndef __NOUVEAU_PLL_H__
#define __NOUVEAU_PLL_H__
int nv04_pll_calc(struct nouveau_clock *, struct nvbios_pll *, u32 freq,
int *N1, int *M1, int *N2, int *M2, int *P);
int nva3_pll_calc(struct nouveau_clock *, struct nvbios_pll *, u32 freq,
int *N, int *fN, int *M, int *P);
#endif

View File

@@ -0,0 +1,242 @@
/*
* Copyright 1993-2003 NVIDIA, Corporation
* Copyright 2007-2009 Stuart Bennett
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <subdev/clock.h>
#include <subdev/bios.h>
#include <subdev/bios/pll.h>
#include "pll.h"
static int
getMNP_single(struct nouveau_clock *clock, struct nvbios_pll *info, int clk,
int *pN, int *pM, int *pP)
{
/* Find M, N and P for a single stage PLL
*
* Note that some bioses (NV3x) have lookup tables of precomputed MNP
* values, but we're too lazy to use those atm
*
* "clk" parameter in kHz
* returns calculated clock
*/
int cv = nouveau_bios(clock)->version.chip;
int minvco = info->vco1.min_freq, maxvco = info->vco1.max_freq;
int minM = info->vco1.min_m, maxM = info->vco1.max_m;
int minN = info->vco1.min_n, maxN = info->vco1.max_n;
int minU = info->vco1.min_inputfreq;
int maxU = info->vco1.max_inputfreq;
int minP = info->min_p;
int maxP = info->max_p_usable;
int crystal = info->refclk;
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;
}
P = 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 (thisP = minP; thisP <= maxP; thisP++) {
P = 1 << thisP;
clkP = clk * P;
if (clkP < minvco)
continue;
if (clkP > maxvco)
return bestclk;
for (M = minM; M <= maxM; M++) {
if (crystal/M < minU)
return bestclk;
if (crystal/M > maxU)
continue;
/* add crystal/2 to round better */
N = (clkP * M + crystal/2) / crystal;
if (N < minN)
continue;
if (N > maxN)
break;
/* more rounding additions */
calcclk = ((N * crystal + P/2) / P + M/2) / M;
delta = abs(calcclk - clk);
/* we do an exhaustive search rather than terminating
* on an optimality condition...
*/
if (delta < bestdelta) {
bestdelta = delta;
bestclk = calcclk;
*pN = N;
*pM = M;
*pP = thisP;
if (delta == 0) /* except this one */
return bestclk;
}
}
}
return bestclk;
}
static int
getMNP_double(struct nouveau_clock *clock, struct nvbios_pll *info, int clk,
int *pN1, int *pM1, int *pN2, int *pM2, int *pP)
{
/* Find M, N and P for a two stage PLL
*
* Note that some bioses (NV30+) have lookup tables of precomputed MNP
* values, but we're too lazy to use those atm
*
* "clk" parameter in kHz
* returns calculated clock
*/
int chip_version = nouveau_bios(clock)->version.chip;
int minvco1 = info->vco1.min_freq, maxvco1 = info->vco1.max_freq;
int minvco2 = info->vco2.min_freq, maxvco2 = info->vco2.max_freq;
int minU1 = info->vco1.min_inputfreq, minU2 = info->vco2.min_inputfreq;
int maxU1 = info->vco1.max_inputfreq, maxU2 = info->vco2.max_inputfreq;
int minM1 = info->vco1.min_m, maxM1 = info->vco1.max_m;
int minN1 = info->vco1.min_n, maxN1 = info->vco1.max_n;
int minM2 = info->vco2.min_m, maxM2 = info->vco2.max_m;
int minN2 = info->vco2.min_n, maxN2 = info->vco2.max_n;
int maxlog2P = info->max_p_usable;
int crystal = info->refclk;
bool fixedgain2 = (minM2 == maxM2 && minN2 == maxN2);
int M1, N1, M2, N2, log2P;
int clkP, calcclk1, calcclk2, calcclkout;
int delta, bestdelta = INT_MAX;
int bestclk = 0;
int vco2 = (maxvco2 - maxvco2/200) / 2;
for (log2P = 0; clk && log2P < maxlog2P && clk <= (vco2 >> log2P); log2P++)
;
clkP = clk << log2P;
if (maxvco2 < clk + clk/200) /* +0.5% */
maxvco2 = clk + clk/200;
for (M1 = minM1; M1 <= maxM1; M1++) {
if (crystal/M1 < minU1)
return bestclk;
if (crystal/M1 > maxU1)
continue;
for (N1 = minN1; N1 <= maxN1; N1++) {
calcclk1 = crystal * N1 / M1;
if (calcclk1 < minvco1)
continue;
if (calcclk1 > maxvco1)
break;
for (M2 = minM2; M2 <= maxM2; M2++) {
if (calcclk1/M2 < minU2)
break;
if (calcclk1/M2 > maxU2)
continue;
/* add calcclk1/2 to round better */
N2 = (clkP * M2 + calcclk1/2) / calcclk1;
if (N2 < minN2)
continue;
if (N2 > maxN2)
break;
if (!fixedgain2) {
if (chip_version < 0x60)
if (N2/M2 < 4 || N2/M2 > 10)
continue;
calcclk2 = calcclk1 * N2 / M2;
if (calcclk2 < minvco2)
break;
if (calcclk2 > maxvco2)
continue;
} else
calcclk2 = calcclk1;
calcclkout = calcclk2 >> log2P;
delta = abs(calcclkout - clk);
/* we do an exhaustive search rather than terminating
* on an optimality condition...
*/
if (delta < bestdelta) {
bestdelta = delta;
bestclk = calcclkout;
*pN1 = N1;
*pM1 = M1;
*pN2 = N2;
*pM2 = M2;
*pP = log2P;
if (delta == 0) /* except this one */
return bestclk;
}
}
}
}
return bestclk;
}
int
nv04_pll_calc(struct nouveau_clock *clk, struct nvbios_pll *info, u32 freq,
int *N1, int *M1, int *N2, int *M2, int *P)
{
int ret;
if (!info->vco2.max_freq) {
ret = getMNP_single(clk, info, freq, N1, M1, P);
*N2 = 1;
*M2 = 1;
} else {
ret = getMNP_double(clk, info, freq, N1, M1, N2, M2, P);
}
if (!ret)
nv_error(clk, "unable to compute acceptable pll values\n");
return ret;
}

View File

@@ -22,60 +22,43 @@
* Authors: Ben Skeggs
*/
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_hw.h"
#include <subdev/clock.h>
#include <subdev/bios.h>
#include <subdev/bios/pll.h>
#include "pll.h"
int
nv50_calc_pll(struct drm_device *dev, struct pll_lims *pll, int clk,
int *N1, int *M1, int *N2, int *M2, int *P)
{
struct nouveau_pll_vals pll_vals;
int ret;
ret = nouveau_calc_pll_mnp(dev, pll, clk, &pll_vals);
if (ret <= 0)
return ret;
*N1 = pll_vals.N1;
*M1 = pll_vals.M1;
*N2 = pll_vals.N2;
*M2 = pll_vals.M2;
*P = pll_vals.log2P;
return ret;
}
int
nva3_calc_pll(struct drm_device *dev, struct pll_lims *pll, int clk,
int *pN, int *pfN, int *pM, int *P)
nva3_pll_calc(struct nouveau_clock *clock, struct nvbios_pll *info,
u32 freq, int *pN, int *pfN, int *pM, int *P)
{
u32 best_err = ~0, err;
int M, lM, hM, N, fN;
*P = pll->vco1.maxfreq / clk;
if (*P > pll->max_p)
*P = pll->max_p;
if (*P < pll->min_p)
*P = pll->min_p;
*P = info->vco1.max_freq / freq;
if (*P > info->max_p)
*P = info->max_p;
if (*P < info->min_p)
*P = info->min_p;
lM = (pll->refclk + pll->vco1.max_inputfreq) / pll->vco1.max_inputfreq;
lM = max(lM, (int)pll->vco1.min_m);
hM = (pll->refclk + pll->vco1.min_inputfreq) / pll->vco1.min_inputfreq;
hM = min(hM, (int)pll->vco1.max_m);
lM = (info->refclk + info->vco1.max_inputfreq) / info->vco1.max_inputfreq;
lM = max(lM, (int)info->vco1.min_m);
hM = (info->refclk + info->vco1.min_inputfreq) / info->vco1.min_inputfreq;
hM = min(hM, (int)info->vco1.max_m);
for (M = lM; M <= hM; M++) {
u32 tmp = clk * *P * M;
N = tmp / pll->refclk;
fN = tmp % pll->refclk;
if (!pfN && fN >= pll->refclk / 2)
u32 tmp = freq * *P * M;
N = tmp / info->refclk;
fN = tmp % info->refclk;
if (!pfN && fN >= info->refclk / 2)
N++;
if (N < pll->vco1.min_n)
if (N < info->vco1.min_n)
continue;
if (N > pll->vco1.max_n)
if (N > info->vco1.max_n)
break;
err = abs(clk - (pll->refclk * N / M / *P));
err = abs(freq - (info->refclk * N / M / *P));
if (err < best_err) {
best_err = err;
*pN = N;
@@ -83,15 +66,15 @@ nva3_calc_pll(struct drm_device *dev, struct pll_lims *pll, int clk,
}
if (pfN) {
*pfN = (((fN << 13) / pll->refclk) - 4096) & 0xffff;
return clk;
*pfN = (((fN << 13) / info->refclk) - 4096) & 0xffff;
return freq;
}
}
if (unlikely(best_err == ~0)) {
NV_ERROR(dev, "unable to find matching pll values\n");
nv_error(clock, "unable to find matching pll values\n");
return -EINVAL;
}
return pll->refclk * *pN / *pM / *P;
return info->refclk * *pN / *pM / *P;
}

File diff suppressed because it is too large Load Diff

View File

@@ -144,58 +144,6 @@ enum LVDS_script {
LVDS_PANEL_OFF
};
/* these match types in pll limits table version 0x40,
* nouveau uses them on all chipsets internally where a
* specific pll needs to be referenced, but the exact
* register isn't known.
*/
enum pll_types {
PLL_CORE = 0x01,
PLL_SHADER = 0x02,
PLL_UNK03 = 0x03,
PLL_MEMORY = 0x04,
PLL_VDEC = 0x05,
PLL_UNK40 = 0x40,
PLL_UNK41 = 0x41,
PLL_UNK42 = 0x42,
PLL_VPLL0 = 0x80,
PLL_VPLL1 = 0x81,
PLL_MAX = 0xff
};
struct pll_lims {
u32 reg;
struct {
int minfreq;
int maxfreq;
int min_inputfreq;
int max_inputfreq;
uint8_t min_m;
uint8_t max_m;
uint8_t min_n;
uint8_t max_n;
} vco1, vco2;
uint8_t max_log2p;
/*
* for most pre nv50 cards setting a log2P of 7 (the common max_log2p
* value) is no different to 6 (at least for vplls) so allowing the MNP
* calc to use 7 causes the generated clock to be out by a factor of 2.
* however, max_log2p cannot be fixed-up during parsing as the
* unmodified max_log2p value is still needed for setting mplls, hence
* an additional max_usable_log2p member
*/
uint8_t max_usable_log2p;
uint8_t log2p_bias;
uint8_t min_p;
uint8_t max_p;
int refclk;
};
struct nvbios {
struct drm_device *dev;
enum {

View File

@@ -260,219 +260,3 @@ nouveau_calc_arb(struct drm_device *dev, int vclk, int bpp, int *burst, int *lwm
} else
nv20_update_arb(burst, lwm);
}
static int
getMNP_single(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
struct nouveau_pll_vals *bestpv)
{
/* Find M, N and P for a single stage PLL
*
* Note that some bioses (NV3x) have lookup tables of precomputed MNP
* values, but we're too lazy to use those atm
*
* "clk" parameter in kHz
* returns calculated clock
*/
struct drm_nouveau_private *dev_priv = dev->dev_private;
int cv = dev_priv->vbios.chip_version;
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;
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, 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 (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;
}
}
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 (thisP = minP; thisP <= maxP; thisP++) {
P = pll_lim->max_p ? thisP : (1 << thisP);
clkP = clk * P;
if (clkP < minvco)
continue;
if (clkP > maxvco)
return bestclk;
for (M = minM; M <= maxM; M++) {
if (crystal/M < minU)
return bestclk;
if (crystal/M > maxU)
continue;
/* add crystal/2 to round better */
N = (clkP * M + crystal/2) / crystal;
if (N < minN)
continue;
if (N > maxN)
break;
/* more rounding additions */
calcclk = ((N * crystal + P/2) / P + M/2) / M;
delta = abs(calcclk - clk);
/* we do an exhaustive search rather than terminating
* on an optimality condition...
*/
if (delta < bestdelta) {
bestdelta = delta;
bestclk = calcclk;
bestpv->N1 = N;
bestpv->M1 = M;
bestpv->log2P = thisP;
if (delta == 0) /* except this one */
return bestclk;
}
}
}
return bestclk;
}
static int
getMNP_double(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
struct nouveau_pll_vals *bestpv)
{
/* Find M, N and P for a two stage PLL
*
* Note that some bioses (NV30+) have lookup tables of precomputed MNP
* values, but we're too lazy to use those atm
*
* "clk" parameter in kHz
* returns calculated clock
*/
struct drm_nouveau_private *dev_priv = dev->dev_private;
int chip_version = dev_priv->vbios.chip_version;
int minvco1 = pll_lim->vco1.minfreq, maxvco1 = pll_lim->vco1.maxfreq;
int minvco2 = pll_lim->vco2.minfreq, maxvco2 = pll_lim->vco2.maxfreq;
int minU1 = pll_lim->vco1.min_inputfreq, minU2 = pll_lim->vco2.min_inputfreq;
int maxU1 = pll_lim->vco1.max_inputfreq, maxU2 = pll_lim->vco2.max_inputfreq;
int minM1 = pll_lim->vco1.min_m, maxM1 = pll_lim->vco1.max_m;
int minN1 = pll_lim->vco1.min_n, maxN1 = pll_lim->vco1.max_n;
int minM2 = pll_lim->vco2.min_m, maxM2 = pll_lim->vco2.max_m;
int minN2 = pll_lim->vco2.min_n, maxN2 = pll_lim->vco2.max_n;
int maxlog2P = pll_lim->max_usable_log2p;
int crystal = pll_lim->refclk;
bool fixedgain2 = (minM2 == maxM2 && minN2 == maxN2);
int M1, N1, M2, N2, log2P;
int clkP, calcclk1, calcclk2, calcclkout;
int delta, bestdelta = INT_MAX;
int bestclk = 0;
int vco2 = (maxvco2 - maxvco2/200) / 2;
for (log2P = 0; clk && log2P < maxlog2P && clk <= (vco2 >> log2P); log2P++)
;
clkP = clk << log2P;
if (maxvco2 < clk + clk/200) /* +0.5% */
maxvco2 = clk + clk/200;
for (M1 = minM1; M1 <= maxM1; M1++) {
if (crystal/M1 < minU1)
return bestclk;
if (crystal/M1 > maxU1)
continue;
for (N1 = minN1; N1 <= maxN1; N1++) {
calcclk1 = crystal * N1 / M1;
if (calcclk1 < minvco1)
continue;
if (calcclk1 > maxvco1)
break;
for (M2 = minM2; M2 <= maxM2; M2++) {
if (calcclk1/M2 < minU2)
break;
if (calcclk1/M2 > maxU2)
continue;
/* add calcclk1/2 to round better */
N2 = (clkP * M2 + calcclk1/2) / calcclk1;
if (N2 < minN2)
continue;
if (N2 > maxN2)
break;
if (!fixedgain2) {
if (chip_version < 0x60)
if (N2/M2 < 4 || N2/M2 > 10)
continue;
calcclk2 = calcclk1 * N2 / M2;
if (calcclk2 < minvco2)
break;
if (calcclk2 > maxvco2)
continue;
} else
calcclk2 = calcclk1;
calcclkout = calcclk2 >> log2P;
delta = abs(calcclkout - clk);
/* we do an exhaustive search rather than terminating
* on an optimality condition...
*/
if (delta < bestdelta) {
bestdelta = delta;
bestclk = calcclkout;
bestpv->N1 = N1;
bestpv->M1 = M1;
bestpv->N2 = N2;
bestpv->M2 = M2;
bestpv->log2P = log2P;
if (delta == 0) /* except this one */
return bestclk;
}
}
}
}
return bestclk;
}
int
nouveau_calc_pll_mnp(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
struct nouveau_pll_vals *pv)
{
int outclk;
if (!pll_lim->vco2.maxfreq)
outclk = getMNP_single(dev, pll_lim, clk, pv);
else
outclk = getMNP_double(dev, pll_lim, clk, pv);
if (!outclk)
NV_ERROR(dev, "Could not find a compatible set of PLL values\n");
return outclk;
}

View File

@@ -2,8 +2,10 @@
#include "nouveau_compat.h"
#include <subdev/bios.h>
#include <subdev/bios/pll.h>
#include <subdev/gpio.h>
#include <subdev/i2c.h>
#include <subdev/clock.h>
void *nouveau_newpriv(struct drm_device *);
@@ -180,3 +182,76 @@ auxch_wr(struct drm_device *dev, struct nouveau_i2c_port *port,
{
return nv_wraux(port, addr, data, size);
}
u32
get_pll_register(struct drm_device *dev, u32 type)
{
struct nouveau_drm *drm = nouveau_newpriv(dev);
struct nouveau_bios *bios = nouveau_bios(drm->device);
struct nvbios_pll info;
if (nvbios_pll_parse(bios, type, &info))
return 0;
return info.reg;
}
int
get_pll_limits(struct drm_device *dev, u32 type, struct nvbios_pll *info)
{
struct nouveau_drm *drm = nouveau_newpriv(dev);
struct nouveau_bios *bios = nouveau_bios(drm->device);
return nvbios_pll_parse(bios, type, info);
}
int
setPLL(struct drm_device *dev, u32 reg, u32 freq)
{
struct nouveau_drm *drm = nouveau_newpriv(dev);
struct nouveau_clock *clk = nouveau_clock(drm->device);
int ret = -ENODEV;
if (clk->pll_set)
ret = clk->pll_set(clk, reg, freq);
return ret;
}
int
nouveau_calc_pll_mnp(struct drm_device *dev, struct nvbios_pll *info,
int freq, struct nouveau_pll_vals *pv)
{
struct nouveau_drm *drm = nouveau_newpriv(dev);
struct nouveau_clock *clk = nouveau_clock(drm->device);
int ret = 0;
if (clk->pll_calc)
ret = clk->pll_calc(clk, info, freq, pv);
return ret;
}
int
nouveau_hw_setpll(struct drm_device *dev, u32 reg1,
struct nouveau_pll_vals *pv)
{
struct nouveau_drm *drm = nouveau_newpriv(dev);
struct nouveau_clock *clk = nouveau_clock(drm->device);
int ret = -ENODEV;
if (clk->pll_prog)
ret = clk->pll_prog(clk, reg1, pv);
return ret;
}
int nva3_pll_calc(struct nouveau_clock *, struct nvbios_pll *, u32 freq,
int *N, int *fN, int *M, int *P);
int
nva3_calc_pll(struct drm_device *dev, struct nvbios_pll *info, u32 freq,
int *N, int *fN, int *M, int *P)
{
struct nouveau_drm *drm = nouveau_newpriv(dev);
struct nouveau_clock *clk = nouveau_clock(drm->device);
return nva3_pll_calc(clk, info, freq, N, fN, M, P);
}

View File

@@ -32,5 +32,17 @@ int nouveau_i2c_identify(struct drm_device *dev, const char *what,
int auxch_rd(struct drm_device *, struct nouveau_i2c_port *, u32, u8 *, u8);
int auxch_wr(struct drm_device *, struct nouveau_i2c_port *, u32, u8 *, u8);
struct nvbios_pll;
struct nouveau_pll_vals;
u32 get_pll_register(struct drm_device *dev, u32 type);
int get_pll_limits(struct drm_device *, u32, struct nvbios_pll *);
int setPLL(struct drm_device *, u32 reg, u32 clk);
int nouveau_calc_pll_mnp(struct drm_device *, struct nvbios_pll *,
int, struct nouveau_pll_vals *);
int nva3_calc_pll(struct drm_device *dev, struct nvbios_pll *info, u32 freq,
int *N, int *fN, int *M, int *P);
int nouveau_hw_setpll(struct drm_device *, u32, struct nouveau_pll_vals *);
#endif

View File

@@ -68,6 +68,9 @@ struct nouveau_grctx;
struct nouveau_mem;
#include <subdev/vm.h>
#include <subdev/bios/pll.h>
#include "nouveau_compat.h"
#define MAX_NUM_DCB_ENTRIES 16
#define NOUVEAU_MAX_CHANNEL_NR 4096
@@ -549,24 +552,6 @@ struct nouveau_engine {
struct nouveau_vram_engine vram;
};
struct nouveau_pll_vals {
union {
struct {
#ifdef __BIG_ENDIAN
uint8_t N1, M1, N2, M2;
#else
uint8_t M1, N1, M2, N2;
#endif
};
struct {
uint16_t NM1, NM2;
} __attribute__((packed));
};
int log2P;
int refclk;
};
enum nv04_fp_display_regs {
FP_DISPLAY_END,
FP_TOTAL,
@@ -1060,9 +1045,6 @@ extern void nouveau_bios_run_init_table(struct drm_device *, uint16_t table,
extern void nouveau_bios_init_exec(struct drm_device *, uint16_t table);
extern struct dcb_connector_table_entry *
nouveau_bios_connector_entry(struct drm_device *, int index);
extern u32 get_pll_register(struct drm_device *, enum pll_types);
extern int get_pll_limits(struct drm_device *, uint32_t limit_match,
struct pll_lims *);
extern int nouveau_bios_run_display_table(struct drm_device *, u16 id, int clk,
struct dcb_entry *, int crtc);
extern bool nouveau_bios_fp_mode(struct drm_device *, struct drm_display_mode *);
@@ -1365,12 +1347,6 @@ int nouveau_display_dumb_map_offset(struct drm_file *, struct drm_device *,
int nouveau_display_dumb_destroy(struct drm_file *, struct drm_device *,
uint32_t handle);
/* nv50_calc.c */
int nv50_calc_pll(struct drm_device *, struct pll_lims *, int clk,
int *N1, int *M1, int *N2, int *M2, int *P);
int nva3_calc_pll(struct drm_device *, struct pll_lims *,
int clk, int *N, int *fN, int *M, int *P);
#ifndef ioread32_native
#ifdef __BIG_ENDIAN
#define ioread16_native ioread16be
@@ -1398,7 +1374,6 @@ static inline void nvchan_wr32(struct nouveau_channel *chan,
}
/* register access */
#include "nouveau_compat.h"
#define nv_rd08 _nv_rd08
#define nv_wr08 _nv_wr08
#define nv_rd32 _nv_rd32

View File

@@ -26,6 +26,8 @@
#include "nouveau_drv.h"
#include "nouveau_hw.h"
#include <subdev/bios/pll.h>
#define CHIPSET_NFORCE 0x01a0
#define CHIPSET_NFORCE2 0x01f0
@@ -122,270 +124,6 @@ NVBlankScreen(struct drm_device *dev, int head, bool blank)
NVVgaSeqReset(dev, head, false);
}
/*
* PLL setting
*/
static int
powerctrl_1_shift(int chip_version, int reg)
{
int shift = -4;
if (chip_version < 0x17 || chip_version == 0x1a || chip_version == 0x20)
return shift;
switch (reg) {
case NV_RAMDAC_VPLL2:
shift += 4;
case NV_PRAMDAC_VPLL_COEFF:
shift += 4;
case NV_PRAMDAC_MPLL_COEFF:
shift += 4;
case NV_PRAMDAC_NVPLL_COEFF:
shift += 4;
}
/*
* the shift for vpll regs is only used for nv3x chips with a single
* stage pll
*/
if (shift > 4 && (chip_version < 0x32 || chip_version == 0x35 ||
chip_version == 0x36 || chip_version >= 0x40))
shift = -4;
return shift;
}
static void
setPLL_single(struct drm_device *dev, uint32_t reg, struct nouveau_pll_vals *pv)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
int chip_version = dev_priv->vbios.chip_version;
uint32_t oldpll = NVReadRAMDAC(dev, 0, reg);
int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff;
uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1;
uint32_t saved_powerctrl_1 = 0;
int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg);
if (oldpll == pll)
return; /* already set */
if (shift_powerctrl_1 >= 0) {
saved_powerctrl_1 = nvReadMC(dev, NV_PBUS_POWERCTRL_1);
nvWriteMC(dev, NV_PBUS_POWERCTRL_1,
(saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
1 << shift_powerctrl_1);
}
if (oldM && pv->M1 && (oldN / oldM < pv->N1 / pv->M1))
/* upclock -- write new post divider first */
NVWriteRAMDAC(dev, 0, reg, pv->log2P << 16 | (oldpll & 0xffff));
else
/* downclock -- write new NM first */
NVWriteRAMDAC(dev, 0, reg, (oldpll & 0xffff0000) | pv->NM1);
if (chip_version < 0x17 && chip_version != 0x11)
/* wait a bit on older chips */
msleep(64);
NVReadRAMDAC(dev, 0, reg);
/* then write the other half as well */
NVWriteRAMDAC(dev, 0, reg, pll);
if (shift_powerctrl_1 >= 0)
nvWriteMC(dev, NV_PBUS_POWERCTRL_1, saved_powerctrl_1);
}
static uint32_t
new_ramdac580(uint32_t reg1, bool ss, uint32_t ramdac580)
{
bool head_a = (reg1 == NV_PRAMDAC_VPLL_COEFF);
if (ss) /* single stage pll mode */
ramdac580 |= head_a ? NV_RAMDAC_580_VPLL1_ACTIVE :
NV_RAMDAC_580_VPLL2_ACTIVE;
else
ramdac580 &= head_a ? ~NV_RAMDAC_580_VPLL1_ACTIVE :
~NV_RAMDAC_580_VPLL2_ACTIVE;
return ramdac580;
}
static void
setPLL_double_highregs(struct drm_device *dev, uint32_t reg1,
struct nouveau_pll_vals *pv)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
int chip_version = dev_priv->vbios.chip_version;
bool nv3035 = chip_version == 0x30 || chip_version == 0x35;
uint32_t reg2 = reg1 + ((reg1 == NV_RAMDAC_VPLL2) ? 0x5c : 0x70);
uint32_t oldpll1 = NVReadRAMDAC(dev, 0, reg1);
uint32_t oldpll2 = !nv3035 ? NVReadRAMDAC(dev, 0, reg2) : 0;
uint32_t pll1 = (oldpll1 & 0xfff80000) | pv->log2P << 16 | pv->NM1;
uint32_t pll2 = (oldpll2 & 0x7fff0000) | 1 << 31 | pv->NM2;
uint32_t oldramdac580 = 0, ramdac580 = 0;
bool single_stage = !pv->NM2 || pv->N2 == pv->M2; /* nv41+ only */
uint32_t saved_powerctrl_1 = 0, savedc040 = 0;
int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg1);
/* model specific additions to generic pll1 and pll2 set up above */
if (nv3035) {
pll1 = (pll1 & 0xfcc7ffff) | (pv->N2 & 0x18) << 21 |
(pv->N2 & 0x7) << 19 | 8 << 4 | (pv->M2 & 7) << 4;
pll2 = 0;
}
if (chip_version > 0x40 && reg1 >= NV_PRAMDAC_VPLL_COEFF) { /* !nv40 */
oldramdac580 = NVReadRAMDAC(dev, 0, NV_PRAMDAC_580);
ramdac580 = new_ramdac580(reg1, single_stage, oldramdac580);
if (oldramdac580 != ramdac580)
oldpll1 = ~0; /* force mismatch */
if (single_stage)
/* magic value used by nvidia in single stage mode */
pll2 |= 0x011f;
}
if (chip_version > 0x70)
/* magic bits set by the blob (but not the bios) on g71-73 */
pll1 = (pll1 & 0x7fffffff) | (single_stage ? 0x4 : 0xc) << 28;
if (oldpll1 == pll1 && oldpll2 == pll2)
return; /* already set */
if (shift_powerctrl_1 >= 0) {
saved_powerctrl_1 = nvReadMC(dev, NV_PBUS_POWERCTRL_1);
nvWriteMC(dev, NV_PBUS_POWERCTRL_1,
(saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
1 << shift_powerctrl_1);
}
if (chip_version >= 0x40) {
int shift_c040 = 14;
switch (reg1) {
case NV_PRAMDAC_MPLL_COEFF:
shift_c040 += 2;
case NV_PRAMDAC_NVPLL_COEFF:
shift_c040 += 2;
case NV_RAMDAC_VPLL2:
shift_c040 += 2;
case NV_PRAMDAC_VPLL_COEFF:
shift_c040 += 2;
}
savedc040 = nvReadMC(dev, 0xc040);
if (shift_c040 != 14)
nvWriteMC(dev, 0xc040, savedc040 & ~(3 << shift_c040));
}
if (oldramdac580 != ramdac580)
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_580, ramdac580);
if (!nv3035)
NVWriteRAMDAC(dev, 0, reg2, pll2);
NVWriteRAMDAC(dev, 0, reg1, pll1);
if (shift_powerctrl_1 >= 0)
nvWriteMC(dev, NV_PBUS_POWERCTRL_1, saved_powerctrl_1);
if (chip_version >= 0x40)
nvWriteMC(dev, 0xc040, savedc040);
}
static void
setPLL_double_lowregs(struct drm_device *dev, uint32_t NMNMreg,
struct nouveau_pll_vals *pv)
{
/* When setting PLLs, there is a merry game of disabling and enabling
* various bits of hardware during the process. This function is a
* synthesis of six nv4x traces, nearly each card doing a subtly
* different thing. With luck all the necessary bits for each card are
* combined herein. Without luck it deviates from each card's formula
* so as to not work on any :)
*/
uint32_t Preg = NMNMreg - 4;
bool mpll = Preg == 0x4020;
uint32_t oldPval = nvReadMC(dev, Preg);
uint32_t NMNM = pv->NM2 << 16 | pv->NM1;
uint32_t Pval = (oldPval & (mpll ? ~(0x77 << 16) : ~(7 << 16))) |
0xc << 28 | pv->log2P << 16;
uint32_t saved4600 = 0;
/* some cards have different maskc040s */
uint32_t maskc040 = ~(3 << 14), savedc040;
bool single_stage = !pv->NM2 || pv->N2 == pv->M2;
if (nvReadMC(dev, NMNMreg) == NMNM && (oldPval & 0xc0070000) == Pval)
return;
if (Preg == 0x4000)
maskc040 = ~0x333;
if (Preg == 0x4058)
maskc040 = ~(0xc << 24);
if (mpll) {
struct pll_lims pll_lim;
uint8_t Pval2;
if (get_pll_limits(dev, Preg, &pll_lim))
return;
Pval2 = pv->log2P + pll_lim.log2p_bias;
if (Pval2 > pll_lim.max_log2p)
Pval2 = pll_lim.max_log2p;
Pval |= 1 << 28 | Pval2 << 20;
saved4600 = nvReadMC(dev, 0x4600);
nvWriteMC(dev, 0x4600, saved4600 | 8 << 28);
}
if (single_stage)
Pval |= mpll ? 1 << 12 : 1 << 8;
nvWriteMC(dev, Preg, oldPval | 1 << 28);
nvWriteMC(dev, Preg, Pval & ~(4 << 28));
if (mpll) {
Pval |= 8 << 20;
nvWriteMC(dev, 0x4020, Pval & ~(0xc << 28));
nvWriteMC(dev, 0x4038, Pval & ~(0xc << 28));
}
savedc040 = nvReadMC(dev, 0xc040);
nvWriteMC(dev, 0xc040, savedc040 & maskc040);
nvWriteMC(dev, NMNMreg, NMNM);
if (NMNMreg == 0x4024)
nvWriteMC(dev, 0x403c, NMNM);
nvWriteMC(dev, Preg, Pval);
if (mpll) {
Pval &= ~(8 << 20);
nvWriteMC(dev, 0x4020, Pval);
nvWriteMC(dev, 0x4038, Pval);
nvWriteMC(dev, 0x4600, saved4600);
}
nvWriteMC(dev, 0xc040, savedc040);
if (mpll) {
nvWriteMC(dev, 0x4020, Pval & ~(1 << 28));
nvWriteMC(dev, 0x4038, Pval & ~(1 << 28));
}
}
void
nouveau_hw_setpll(struct drm_device *dev, uint32_t reg1,
struct nouveau_pll_vals *pv)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
int cv = dev_priv->vbios.chip_version;
if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
cv >= 0x40) {
if (reg1 > 0x405c)
setPLL_double_highregs(dev, reg1, pv);
else
setPLL_double_lowregs(dev, reg1, pv);
} else
setPLL_single(dev, reg1, pv);
}
/*
* PLL getting
*/
@@ -423,12 +161,12 @@ nouveau_hw_decode_pll(struct drm_device *dev, uint32_t reg1, uint32_t pll1,
}
int
nouveau_hw_get_pllvals(struct drm_device *dev, enum pll_types plltype,
nouveau_hw_get_pllvals(struct drm_device *dev, enum nvbios_pll_type plltype,
struct nouveau_pll_vals *pllvals)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t reg1 = get_pll_register(dev, plltype), pll1, pll2 = 0;
struct pll_lims pll_lim;
struct nvbios_pll pll_lim;
int ret;
if (reg1 == 0)
@@ -478,7 +216,7 @@ nouveau_hw_pllvals_to_clk(struct nouveau_pll_vals *pv)
}
int
nouveau_hw_get_clock(struct drm_device *dev, enum pll_types plltype)
nouveau_hw_get_clock(struct drm_device *dev, enum nvbios_pll_type plltype)
{
struct nouveau_pll_vals pllvals;
int ret;
@@ -517,9 +255,9 @@ nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head)
* when such a condition detected. only seen on nv11 to date
*/
struct pll_lims pll_lim;
struct nvbios_pll pll_lim;
struct nouveau_pll_vals pv;
enum pll_types pll = head ? PLL_VPLL1 : PLL_VPLL0;
enum nvbios_pll_type pll = head ? PLL_VPLL1 : PLL_VPLL0;
if (get_pll_limits(dev, pll, &pll_lim))
return;
@@ -527,7 +265,7 @@ nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head)
if (pv.M1 >= pll_lim.vco1.min_m && pv.M1 <= pll_lim.vco1.max_m &&
pv.N1 >= pll_lim.vco1.min_n && pv.N1 <= pll_lim.vco1.max_n &&
pv.log2P <= pll_lim.max_log2p)
pv.log2P <= pll_lim.max_p)
return;
NV_WARN(dev, "VPLL %d outwith limits, attempting to fix\n", head + 1);
@@ -535,7 +273,7 @@ nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head)
/* set lowest clock within static limits */
pv.M1 = pll_lim.vco1.max_m;
pv.N1 = pll_lim.vco1.min_n;
pv.log2P = pll_lim.max_usable_log2p;
pv.log2P = pll_lim.max_p_usable;
nouveau_hw_setpll(dev, pll_lim.reg, &pv);
}

Some files were not shown because too many files have changed in this diff Show More