diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index 2964d71d3996bac6e38ff5a9b63c931a43acef43..2574fd35122acd23df2972b3582433ef143fb0e2 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -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
 
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/pll.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/pll.h
new file mode 100644
index 0000000000000000000000000000000000000000..c345097592f20bcf191f1d71db11beddfb044053
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/pll.h
@@ -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
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/clock.h b/drivers/gpu/drm/nouveau/core/include/subdev/clock.h
index 137c4598a1b676ec93d705a002fed8be97660f3e..39e73b91d360a3753dbcc3c210ca1e77f00332ec 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/clock.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/clock.h
@@ -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
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/vga.h b/drivers/gpu/drm/nouveau/core/include/subdev/vga.h
index d81df1ae442ddcb7c35ea831ce51437cb59b300f..fee09ad818e48899d84cb4bfd2bbd4fc6228d5d3 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/vga.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/vga.h
@@ -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);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/pll.c b/drivers/gpu/drm/nouveau/core/subdev/bios/pll.c
new file mode 100644
index 0000000000000000000000000000000000000000..5e5f4cddae3cc7248ed527c07b94e8d0c15e97e4
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/pll.c
@@ -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;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c
index 3a3b3b149690e5f9acc503a4fcbde2b050c2e90b..b7fd1151166e0adcedec217fed8e186ce5ab2cd8 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c
@@ -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;
+
+	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);
+}
 
-	nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
+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;
 }
 
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c
index 60d1ca44e39cf64990a8db6db232421ac355a6e4..a4b2b7ebf9af6f48c3b44c50e11a5a07cbd99341 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c
@@ -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;
 }
 
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c
index 82804bdcec31fca0f1340259c0f2cb884e99642e..fd181fbceddb9313cc58e1c7b04b77659c41eff8 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c
@@ -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;
+
+	ret = nvbios_pll_parse(bios, type, &info);
+	if (ret) {
+		nv_error(clk, "failed to retrieve pll data, %d\n", ret);
+		return ret;
+	}
 
-	nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
+	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
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c
index 876ec46e2b411e55992392611e6e7665723b4510..cc8d7d162d7c348da6770d7404f195e9191d41e1 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c
@@ -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;
+
+	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;
+	}
 
-	nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
+	return ret;
 }
 
 static int
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c
index 00641566f2e7ed2979e4c9ad16937b9253f52165..5ccce0b17bf3dd209bde225486b41bab55fcdf79 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c
@@ -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;
+
+	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;
+	}
 
-	nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
+	return ret;
 }
 
 static int
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/pll.h b/drivers/gpu/drm/nouveau/core/subdev/clock/pll.h
new file mode 100644
index 0000000000000000000000000000000000000000..ef2c0078f337155a1e093c8cb5d8c5b71b967638
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/pll.h
@@ -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
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/pllnv04.c b/drivers/gpu/drm/nouveau/core/subdev/clock/pllnv04.c
new file mode 100644
index 0000000000000000000000000000000000000000..a2ab6d051ba8668f7db2f5a8a8284d3ec85c0dc2
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/pllnv04.c
@@ -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;
+}
diff --git a/drivers/gpu/drm/nouveau/nv50_calc.c b/drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c
similarity index 52%
rename from drivers/gpu/drm/nouveau/nv50_calc.c
rename to drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c
index 8cf63a8b30cdb96198e8b8be469e48e2c42604a4..eed5c16cf610a0d531fe3c5424b3df20eec1f5b1 100644
--- a/drivers/gpu/drm/nouveau/nv50_calc.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c
@@ -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>
 
-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;
-}
+#include "pll.h"
 
 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;
 }
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index 35b0a8f9c008f36e2c0082af27a546d239559623..98eaac9da30e356c3ae19bd2c5a6270699e1df7d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -441,70 +441,6 @@ io_condition_met(struct nvbios *bios, uint16_t offset, uint8_t cond)
 	return (data == cmpval);
 }
 
-static int
-nv50_pll_set(struct drm_device *dev, uint32_t reg, uint32_t clk)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_pll_vals pll;
-	struct pll_lims pll_limits;
-	u32 ctrl, mask, coef;
-	int ret;
-
-	ret = get_pll_limits(dev, reg, &pll_limits);
-	if (ret)
-		return ret;
-
-	clk = nouveau_calc_pll_mnp(dev, &pll_limits, clk, &pll);
-	if (!clk)
-		return -ERANGE;
-
-	coef = pll.N1 << 8 | pll.M1;
-	ctrl = pll.log2P << 16;
-	mask = 0x00070000;
-	if (reg == 0x004008) {
-		mask |= 0x01f80000;
-		ctrl |= (pll_limits.log2p_bias << 19);
-		ctrl |= (pll.log2P << 22);
-	}
-
-	if (!dev_priv->vbios.execute)
-		return 0;
-
-	nv_mask(dev, reg + 0, mask, ctrl);
-	nv_wr32(dev, reg + 4, coef);
-	return 0;
-}
-
-static int
-setPLL(struct nvbios *bios, uint32_t reg, uint32_t clk)
-{
-	struct drm_device *dev = bios->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	/* clk in kHz */
-	struct pll_lims pll_lim;
-	struct nouveau_pll_vals pllvals;
-	int ret;
-
-	if (dev_priv->card_type >= NV_50)
-		return nv50_pll_set(dev, reg, clk);
-
-	/* high regs (such as in the mac g5 table) are not -= 4 */
-	ret = get_pll_limits(dev, reg > 0x405c ? reg : reg - 4, &pll_lim);
-	if (ret)
-		return ret;
-
-	clk = nouveau_calc_pll_mnp(dev, &pll_lim, clk, &pllvals);
-	if (!clk)
-		return -ERANGE;
-
-	if (bios->execute) {
-		still_alive();
-		nouveau_hw_setpll(dev, reg, &pllvals);
-	}
-
-	return 0;
-}
-
 static int dcb_entry_idx_from_crtchead(struct drm_device *dev)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -752,7 +688,7 @@ init_io_restrict_pll(struct nvbios *bios, uint16_t offset,
 	BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Config: 0x%02X, Freq: %d0kHz\n",
 		offset, reg, config, freq);
 
-	setPLL(bios, reg, freq * 10);
+	setPLL(bios->dev, reg, freq * 10);
 
 	return len;
 }
@@ -1110,7 +1046,7 @@ init_io_restrict_pll2(struct nvbios *bios, uint16_t offset,
 	BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Config: 0x%02X, Freq: %dkHz\n",
 		offset, reg, config, freq);
 
-	setPLL(bios, reg, freq);
+	setPLL(bios->dev, reg, freq);
 
 	return len;
 }
@@ -1137,7 +1073,7 @@ init_pll2(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
 	BIOSLOG(bios, "0x%04X: Reg: 0x%04X, Freq: %dkHz\n",
 		offset, reg, freq);
 
-	setPLL(bios, reg, freq);
+	setPLL(bios->dev, reg, freq);
 	return 9;
 }
 
@@ -2376,12 +2312,12 @@ init_configure_clk(struct nvbios *bios, uint16_t offset,
 		return 0;
 
 	clock = ROM16(bios->data[meminitoffs + 4]) * 10;
-	setPLL(bios, NV_PRAMDAC_NVPLL_COEFF, clock);
+	setPLL(bios->dev, NV_PRAMDAC_NVPLL_COEFF, clock);
 
 	clock = ROM16(bios->data[meminitoffs + 2]) * 10;
 	if (bios->data[meminitoffs] & 1) /* DDR */
 		clock *= 2;
-	setPLL(bios, NV_PRAMDAC_MPLL_COEFF, clock);
+	setPLL(bios->dev, NV_PRAMDAC_MPLL_COEFF, clock);
 
 	return 1;
 }
@@ -2820,7 +2756,7 @@ init_pll(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
 
 	BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Freq: %d0kHz\n", offset, reg, freq);
 
-	setPLL(bios, reg, freq * 10);
+	setPLL(bios->dev, reg, freq * 10);
 
 	return 7;
 }
@@ -2898,7 +2834,7 @@ init_ram_restrict_pll(struct nvbios *bios, uint16_t offset,
 				      "Type %02x Reg 0x%08x Freq %dKHz\n",
 				offset, type, reg, freq);
 
-			setPLL(bios, reg, freq);
+			setPLL(bios->dev, reg, freq);
 			return len;
 		}
 	}
@@ -4305,447 +4241,6 @@ int run_tmds_table(struct drm_device *dev, struct dcb_entry *dcbent, int head, i
 	return 0;
 }
 
-struct pll_mapping {
-	u8  type;
-	u32 reg;
-};
-
-static struct pll_mapping nv04_pll_mapping[] = {
-	{ PLL_CORE  , NV_PRAMDAC_NVPLL_COEFF },
-	{ PLL_MEMORY, NV_PRAMDAC_MPLL_COEFF },
-	{ PLL_VPLL0 , NV_PRAMDAC_VPLL_COEFF },
-	{ PLL_VPLL1 , NV_RAMDAC_VPLL2 },
-	{}
-};
-
-static struct pll_mapping nv40_pll_mapping[] = {
-	{ PLL_CORE  , 0x004000 },
-	{ PLL_MEMORY, 0x004020 },
-	{ PLL_VPLL0 , NV_PRAMDAC_VPLL_COEFF },
-	{ PLL_VPLL1 , NV_RAMDAC_VPLL2 },
-	{}
-};
-
-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 },
-	{}
-};
-
-u32
-get_pll_register(struct drm_device *dev, enum pll_types type)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nvbios *bios = &dev_priv->vbios;
-	struct pll_mapping *map;
-	int i;
-
-	if (dev_priv->card_type < NV_40)
-		map = nv04_pll_mapping;
-	else
-	if (dev_priv->card_type < NV_50)
-		map = nv40_pll_mapping;
-	else {
-		u8 *plim = &bios->data[bios->pll_limit_tbl_ptr];
-
-		if (plim[0] >= 0x30) {
-			u8 *entry = plim + plim[1];
-			for (i = 0; i < plim[3]; i++, entry += plim[2]) {
-				if (entry[0] == type)
-					return ROM32(entry[3]);
-			}
-
-			return 0;
-		}
-
-		if (dev_priv->chipset == 0x50)
-			map = nv50_pll_mapping;
-		else
-			map = nv84_pll_mapping;
-	}
-
-	while (map->reg) {
-		if (map->type == type)
-			return map->reg;
-		map++;
-	}
-
-	return 0;
-}
-
-int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims *pll_lim)
-{
-	/*
-	 * PLL limits table
-	 *
-	 * Version 0x10: NV30, NV31
-	 * One byte header (version), one record of 24 bytes
-	 * Version 0x11: NV36 - Not implemented
-	 * Seems to have same record style as 0x10, but 3 records rather than 1
-	 * Version 0x20: Found on Geforce 6 cards
-	 * Trivial 4 byte BIT header. 31 (0x1f) byte record length
-	 * Version 0x21: Found on Geforce 7, 8 and some Geforce 6 cards
-	 * 5 byte header, fifth byte of unknown purpose. 35 (0x23) byte record
-	 * length in general, some (integrated) have an extra configuration byte
-	 * Version 0x30: Found on Geforce 8, separates the register mapping
-	 * from the limits tables.
-	 */
-
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nvbios *bios = &dev_priv->vbios;
-	int cv = bios->chip_version, pllindex = 0;
-	uint8_t pll_lim_ver = 0, headerlen = 0, recordlen = 0, entries = 0;
-	uint32_t crystal_strap_mask, crystal_straps;
-
-	if (!bios->pll_limit_tbl_ptr) {
-		if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
-		    cv >= 0x40) {
-			NV_ERROR(dev, "Pointer to PLL limits table invalid\n");
-			return -EINVAL;
-		}
-	} else
-		pll_lim_ver = bios->data[bios->pll_limit_tbl_ptr];
-
-	crystal_strap_mask = 1 << 6;
-	/* open coded dev->twoHeads test */
-	if (cv > 0x10 && cv != 0x15 && cv != 0x1a && cv != 0x20)
-		crystal_strap_mask |= 1 << 22;
-	crystal_straps = nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) &
-							crystal_strap_mask;
-
-	switch (pll_lim_ver) {
-	/*
-	 * We use version 0 to indicate a pre limit table bios (single stage
-	 * pll) and load the hard coded limits instead.
-	 */
-	case 0:
-		break;
-	case 0x10:
-	case 0x11:
-		/*
-		 * Strictly v0x11 has 3 entries, but the last two don't seem
-		 * to get used.
-		 */
-		headerlen = 1;
-		recordlen = 0x18;
-		entries = 1;
-		pllindex = 0;
-		break;
-	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];
-		break;
-	default:
-		NV_ERROR(dev, "PLL limits table revision 0x%X not currently "
-				"supported\n", pll_lim_ver);
-		return -ENOSYS;
-	}
-
-	/* initialize all members to zero */
-	memset(pll_lim, 0, sizeof(struct pll_lims));
-
-	/* if we were passed a type rather than a register, figure
-	 * out the register and store it
-	 */
-	if (limit_match > PLL_MAX)
-		pll_lim->reg = limit_match;
-	else {
-		pll_lim->reg = get_pll_register(dev, limit_match);
-		if (!pll_lim->reg)
-			return -ENOENT;
-	}
-
-	if (pll_lim_ver == 0x10 || pll_lim_ver == 0x11) {
-		uint8_t *pll_rec = &bios->data[bios->pll_limit_tbl_ptr + headerlen + recordlen * pllindex];
-
-		pll_lim->vco1.minfreq = ROM32(pll_rec[0]);
-		pll_lim->vco1.maxfreq = ROM32(pll_rec[4]);
-		pll_lim->vco2.minfreq = ROM32(pll_rec[8]);
-		pll_lim->vco2.maxfreq = ROM32(pll_rec[12]);
-		pll_lim->vco1.min_inputfreq = ROM32(pll_rec[16]);
-		pll_lim->vco2.min_inputfreq = ROM32(pll_rec[20]);
-		pll_lim->vco1.max_inputfreq = pll_lim->vco2.max_inputfreq = INT_MAX;
-
-		/* these values taken from nv30/31/36 */
-		pll_lim->vco1.min_n = 0x1;
-		if (cv == 0x36)
-			pll_lim->vco1.min_n = 0x5;
-		pll_lim->vco1.max_n = 0xff;
-		pll_lim->vco1.min_m = 0x1;
-		pll_lim->vco1.max_m = 0xd;
-		pll_lim->vco2.min_n = 0x4;
-		/*
-		 * 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
-		 */
-		pll_lim->vco2.max_n = 0x28;
-		if (cv == 0x30 || cv == 0x35)
-			/* only 5 bits available for N2 on nv30/35 */
-			pll_lim->vco2.max_n = 0x1f;
-		pll_lim->vco2.min_m = 0x1;
-		pll_lim->vco2.max_m = 0x4;
-		pll_lim->max_log2p = 0x7;
-		pll_lim->max_usable_log2p = 0x6;
-	} else if (pll_lim_ver == 0x20 || pll_lim_ver == 0x21) {
-		uint16_t plloffs = bios->pll_limit_tbl_ptr + headerlen;
-		uint8_t *pll_rec;
-		int i;
-
-		/*
-		 * First entry is default match, if nothing better. warn if
-		 * reg field nonzero
-		 */
-		if (ROM32(bios->data[plloffs]))
-			NV_WARN(dev, "Default PLL limit entry has non-zero "
-				       "register field\n");
-
-		for (i = 1; i < entries; i++)
-			if (ROM32(bios->data[plloffs + recordlen * i]) == pll_lim->reg) {
-				pllindex = i;
-				break;
-			}
-
-		if ((dev_priv->card_type >= NV_50) && (pllindex == 0)) {
-			NV_ERROR(dev, "Register 0x%08x not found in PLL "
-				 "limits table", pll_lim->reg);
-			return -ENOENT;
-		}
-
-		pll_rec = &bios->data[plloffs + recordlen * pllindex];
-
-		BIOSLOG(bios, "Loading PLL limits for reg 0x%08x\n",
-			pllindex ? pll_lim->reg : 0);
-
-		/*
-		 * Frequencies are stored in tables in MHz, kHz are more
-		 * useful, so we convert.
-		 */
-
-		/* What output frequencies can each VCO generate? */
-		pll_lim->vco1.minfreq = ROM16(pll_rec[4]) * 1000;
-		pll_lim->vco1.maxfreq = ROM16(pll_rec[6]) * 1000;
-		pll_lim->vco2.minfreq = ROM16(pll_rec[8]) * 1000;
-		pll_lim->vco2.maxfreq = ROM16(pll_rec[10]) * 1000;
-
-		/* What input frequencies they accept (past the m-divider)? */
-		pll_lim->vco1.min_inputfreq = ROM16(pll_rec[12]) * 1000;
-		pll_lim->vco2.min_inputfreq = ROM16(pll_rec[14]) * 1000;
-		pll_lim->vco1.max_inputfreq = ROM16(pll_rec[16]) * 1000;
-		pll_lim->vco2.max_inputfreq = ROM16(pll_rec[18]) * 1000;
-
-		/* What values are accepted as multiplier and divider? */
-		pll_lim->vco1.min_n = pll_rec[20];
-		pll_lim->vco1.max_n = pll_rec[21];
-		pll_lim->vco1.min_m = pll_rec[22];
-		pll_lim->vco1.max_m = pll_rec[23];
-		pll_lim->vco2.min_n = pll_rec[24];
-		pll_lim->vco2.max_n = pll_rec[25];
-		pll_lim->vco2.min_m = pll_rec[26];
-		pll_lim->vco2.max_m = pll_rec[27];
-
-		pll_lim->max_usable_log2p = pll_lim->max_log2p = pll_rec[29];
-		if (pll_lim->max_log2p > 0x7)
-			/* pll decoding in nv_hw.c assumes never > 7 */
-			NV_WARN(dev, "Max log2 P value greater than 7 (%d)\n",
-				pll_lim->max_log2p);
-		if (cv < 0x60)
-			pll_lim->max_usable_log2p = 0x6;
-		pll_lim->log2p_bias = pll_rec[30];
-
-		if (recordlen > 0x22)
-			pll_lim->refclk = ROM32(pll_rec[31]);
-
-		if (recordlen > 0x23 && pll_rec[35])
-			NV_WARN(dev,
-				"Bits set in PLL configuration byte (%x)\n",
-				pll_rec[35]);
-
-		/* C51 special not seen elsewhere */
-		if (cv == 0x51 && !pll_lim->refclk) {
-			uint32_t sel_clk = bios_rd32(bios, NV_PRAMDAC_SEL_CLK);
-
-			if ((pll_lim->reg == NV_PRAMDAC_VPLL_COEFF && sel_clk & 0x20) ||
-			    (pll_lim->reg == NV_RAMDAC_VPLL2 && sel_clk & 0x80)) {
-				if (bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_CHIP_ID_INDEX) < 0xa3)
-					pll_lim->refclk = 200000;
-				else
-					pll_lim->refclk = 25000;
-			}
-		}
-	} 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;
-
-		BIOSLOG(bios, "Loading PLL limits for register 0x%08x\n",
-			pll_lim->reg);
-
-		for (i = 0; i < entries; i++, entry += recordlen) {
-			if (ROM32(entry[3]) == pll_lim->reg) {
-				record = &bios->data[ROM16(entry[1])];
-				break;
-			}
-		}
-
-		if (!record) {
-			NV_ERROR(dev, "Register 0x%08x not found in PLL "
-				 "limits table", pll_lim->reg);
-			return -ENOENT;
-		}
-
-		pll_lim->vco1.minfreq = ROM16(record[0]) * 1000;
-		pll_lim->vco1.maxfreq = ROM16(record[2]) * 1000;
-		pll_lim->vco2.minfreq = ROM16(record[4]) * 1000;
-		pll_lim->vco2.maxfreq = ROM16(record[6]) * 1000;
-		pll_lim->vco1.min_inputfreq = ROM16(record[8]) * 1000;
-		pll_lim->vco2.min_inputfreq = ROM16(record[10]) * 1000;
-		pll_lim->vco1.max_inputfreq = ROM16(record[12]) * 1000;
-		pll_lim->vco2.max_inputfreq = ROM16(record[14]) * 1000;
-		pll_lim->vco1.min_n = record[16];
-		pll_lim->vco1.max_n = record[17];
-		pll_lim->vco1.min_m = record[18];
-		pll_lim->vco1.max_m = record[19];
-		pll_lim->vco2.min_n = record[20];
-		pll_lim->vco2.max_n = record[21];
-		pll_lim->vco2.min_m = record[22];
-		pll_lim->vco2.max_m = record[23];
-		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",
-			pll_lim->reg);
-
-		for (i = 0; i < entries; i++, entry += recordlen) {
-			if (ROM32(entry[3]) == pll_lim->reg) {
-				record = &bios->data[ROM16(entry[1])];
-				break;
-			}
-		}
-
-		if (!record) {
-			NV_ERROR(dev, "Register 0x%08x not found in PLL "
-				 "limits table", pll_lim->reg);
-			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];
-		pll_lim->refclk = ROM16(entry[9]) * 1000;
-	}
-
-	/*
-	 * 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 (!pll_lim->vco1.maxfreq) {
-		pll_lim->vco1.minfreq = bios->fminvco;
-		pll_lim->vco1.maxfreq = bios->fmaxvco;
-		pll_lim->vco1.min_inputfreq = 0;
-		pll_lim->vco1.max_inputfreq = INT_MAX;
-		pll_lim->vco1.min_n = 0x1;
-		pll_lim->vco1.max_n = 0xff;
-		pll_lim->vco1.min_m = 0x1;
-		if (crystal_straps == 0) {
-			/* nv05 does this, nv11 doesn't, nv10 unknown */
-			if (cv < 0x11)
-				pll_lim->vco1.min_m = 0x7;
-			pll_lim->vco1.max_m = 0xd;
-		} else {
-			if (cv < 0x11)
-				pll_lim->vco1.min_m = 0x8;
-			pll_lim->vco1.max_m = 0xe;
-		}
-		if (cv < 0x17 || cv == 0x1a || cv == 0x20)
-			pll_lim->max_log2p = 4;
-		else
-			pll_lim->max_log2p = 5;
-		pll_lim->max_usable_log2p = pll_lim->max_log2p;
-	}
-
-	if (!pll_lim->refclk)
-		switch (crystal_straps) {
-		case 0:
-			pll_lim->refclk = 13500;
-			break;
-		case (1 << 6):
-			pll_lim->refclk = 14318;
-			break;
-		case (1 << 22):
-			pll_lim->refclk = 27000;
-			break;
-		case (1 << 22 | 1 << 6):
-			pll_lim->refclk = 25000;
-			break;
-		}
-
-	NV_DEBUG(dev, "pll.vco1.minfreq: %d\n", pll_lim->vco1.minfreq);
-	NV_DEBUG(dev, "pll.vco1.maxfreq: %d\n", pll_lim->vco1.maxfreq);
-	NV_DEBUG(dev, "pll.vco1.min_inputfreq: %d\n", pll_lim->vco1.min_inputfreq);
-	NV_DEBUG(dev, "pll.vco1.max_inputfreq: %d\n", pll_lim->vco1.max_inputfreq);
-	NV_DEBUG(dev, "pll.vco1.min_n: %d\n", pll_lim->vco1.min_n);
-	NV_DEBUG(dev, "pll.vco1.max_n: %d\n", pll_lim->vco1.max_n);
-	NV_DEBUG(dev, "pll.vco1.min_m: %d\n", pll_lim->vco1.min_m);
-	NV_DEBUG(dev, "pll.vco1.max_m: %d\n", pll_lim->vco1.max_m);
-	if (pll_lim->vco2.maxfreq) {
-		NV_DEBUG(dev, "pll.vco2.minfreq: %d\n", pll_lim->vco2.minfreq);
-		NV_DEBUG(dev, "pll.vco2.maxfreq: %d\n", pll_lim->vco2.maxfreq);
-		NV_DEBUG(dev, "pll.vco2.min_inputfreq: %d\n", pll_lim->vco2.min_inputfreq);
-		NV_DEBUG(dev, "pll.vco2.max_inputfreq: %d\n", pll_lim->vco2.max_inputfreq);
-		NV_DEBUG(dev, "pll.vco2.min_n: %d\n", pll_lim->vco2.min_n);
-		NV_DEBUG(dev, "pll.vco2.max_n: %d\n", pll_lim->vco2.max_n);
-		NV_DEBUG(dev, "pll.vco2.min_m: %d\n", pll_lim->vco2.min_m);
-		NV_DEBUG(dev, "pll.vco2.max_m: %d\n", pll_lim->vco2.max_m);
-	}
-	if (!pll_lim->max_p) {
-		NV_DEBUG(dev, "pll.max_log2p: %d\n", pll_lim->max_log2p);
-		NV_DEBUG(dev, "pll.log2p_bias: %d\n", pll_lim->log2p_bias);
-	} else {
-		NV_DEBUG(dev, "pll.min_p: %d\n", pll_lim->min_p);
-		NV_DEBUG(dev, "pll.max_p: %d\n", pll_lim->max_p);
-	}
-	NV_DEBUG(dev, "pll.refclk: %d\n", pll_lim->refclk);
-
-	return 0;
-}
-
 static void parse_bios_version(struct drm_device *dev, struct nvbios *bios, uint16_t offset)
 {
 	/*
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h
index 52fce11e9d26a1ecd1bc9703fbeee0032f3099f9..e9bb14ec8c3b9c3badecc2324d0f5b259314d793 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.h
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
@@ -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 {
diff --git a/drivers/gpu/drm/nouveau/nouveau_calc.c b/drivers/gpu/drm/nouveau/nouveau_calc.c
index dad96cce5e390a77ff3de98d63658632c9603cc0..5b487655d6b893ebf054dd1c5d51d66132a1a7bf 100644
--- a/drivers/gpu/drm/nouveau/nouveau_calc.c
+++ b/drivers/gpu/drm/nouveau/nouveau_calc.c
@@ -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;
-}
diff --git a/drivers/gpu/drm/nouveau/nouveau_compat.c b/drivers/gpu/drm/nouveau/nouveau_compat.c
index 76582b0a9df831a16a8f2718716185ece2d49b1b..30431c820bb3b61aefc21bcaedd7269e06c5b7f5 100644
--- a/drivers/gpu/drm/nouveau/nouveau_compat.c
+++ b/drivers/gpu/drm/nouveau/nouveau_compat.c
@@ -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);
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_compat.h b/drivers/gpu/drm/nouveau/nouveau_compat.h
index 9b3298bfe4ed23ac9749733ad4c0fee1ae91e23f..8bf5bec81710e2177beeecabcb16fac7c9cab6ea 100644
--- a/drivers/gpu/drm/nouveau/nouveau_compat.h
+++ b/drivers/gpu/drm/nouveau/nouveau_compat.h
@@ -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
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index afe978702097549ccd3ef6bba3d2e1703717f3f0..2b519b5cfac951e8549ef71220d79ccca1dfe19a 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -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
diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c b/drivers/gpu/drm/nouveau/nouveau_hw.c
index b87ad3bd7739cb9a0ad69ec01730761ed9708638..fdd667b705287e269a035a04dfff073f945223bc 100644
--- a/drivers/gpu/drm/nouveau/nouveau_hw.c
+++ b/drivers/gpu/drm/nouveau/nouveau_hw.c
@@ -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);
 }
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.h b/drivers/gpu/drm/nouveau/nouveau_hw.h
index 2989090b94349abdcd05096f89735f641a748c3b..ff7f31c279d8bb9624a0c02cc5aa4de404202f39 100644
--- a/drivers/gpu/drm/nouveau/nouveau_hw.h
+++ b/drivers/gpu/drm/nouveau/nouveau_hw.h
@@ -26,6 +26,8 @@
 #include "drmP.h"
 #include "nouveau_drv.h"
 
+#include <subdev/bios/pll.h>
+
 #define MASK(field) ( \
 	(0xffffffff >> (31 - ((1 ? field) - (0 ? field)))) << (0 ? field))
 
@@ -38,12 +40,10 @@ void NVWriteVgaGr(struct drm_device *, int head, uint8_t index, uint8_t value);
 uint8_t NVReadVgaGr(struct drm_device *, int head, uint8_t index);
 void NVSetOwner(struct drm_device *, int owner);
 void NVBlankScreen(struct drm_device *, int head, bool blank);
-void nouveau_hw_setpll(struct drm_device *, uint32_t reg1,
-		       struct nouveau_pll_vals *pv);
-int nouveau_hw_get_pllvals(struct drm_device *, enum pll_types plltype,
+int nouveau_hw_get_pllvals(struct drm_device *, enum nvbios_pll_type plltype,
 			   struct nouveau_pll_vals *pllvals);
 int nouveau_hw_pllvals_to_clk(struct nouveau_pll_vals *pllvals);
-int nouveau_hw_get_clock(struct drm_device *, enum pll_types plltype);
+int nouveau_hw_get_clock(struct drm_device *, enum nvbios_pll_type plltype);
 void nouveau_hw_save_vga_fonts(struct drm_device *, bool save);
 void nouveau_hw_save_state(struct drm_device *, int head,
 			   struct nv04_mode_state *state);
@@ -55,8 +55,6 @@ void nouveau_hw_load_state_palette(struct drm_device *, int head,
 /* nouveau_calc.c */
 extern void nouveau_calc_arb(struct drm_device *, int vclk, int bpp,
 			     int *burst, int *lwm);
-extern int nouveau_calc_pll_mnp(struct drm_device *, struct pll_lims *pll_lim,
-				int clk, struct nouveau_pll_vals *pv);
 
 static inline uint32_t
 nvReadMC(struct drm_device *dev, uint32_t reg)
diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c
index 43accc11102f551e85882db7d150cf64e93ce895..93ca09b90da55077af903589d0c273eac15e1a9c 100644
--- a/drivers/gpu/drm/nouveau/nv04_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv04_crtc.c
@@ -108,7 +108,7 @@ static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mod
 	struct nv04_mode_state *state = &dev_priv->mode_reg;
 	struct nv04_crtc_reg *regp = &state->crtc_reg[nv_crtc->index];
 	struct nouveau_pll_vals *pv = &regp->pllvals;
-	struct pll_lims pll_lim;
+	struct nvbios_pll pll_lim;
 
 	if (get_pll_limits(dev, nv_crtc->index ? PLL_VPLL1 : PLL_VPLL0, &pll_lim))
 		return;
@@ -126,7 +126,7 @@ static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mod
 	 * has yet been observed in allowing the use a single stage pll on all
 	 * nv43 however.  the behaviour of single stage use is untested on nv40
 	 */
-	if (dev_priv->chipset > 0x40 && dot_clock <= (pll_lim.vco1.maxfreq / 2))
+	if (dev_priv->chipset > 0x40 && dot_clock <= (pll_lim.vco1.max_freq / 2))
 		memset(&pll_lim.vco2, 0, sizeof(pll_lim.vco2));
 
 	if (!nouveau_calc_pll_mnp(dev, &pll_lim, dot_clock, pv))
diff --git a/drivers/gpu/drm/nouveau/nv04_pm.c b/drivers/gpu/drm/nouveau/nv04_pm.c
index 6e7589918fa93dbcf91eb7d4b27e082b3a78e86d..4528d48dc52bce30d47951634cd53cdd2a437ad4 100644
--- a/drivers/gpu/drm/nouveau/nv04_pm.c
+++ b/drivers/gpu/drm/nouveau/nv04_pm.c
@@ -46,7 +46,7 @@ nv04_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
 }
 
 struct nv04_pm_clock {
-	struct pll_lims pll;
+	struct nvbios_pll pll;
 	struct nouveau_pll_vals calc;
 };
 
diff --git a/drivers/gpu/drm/nouveau/nv40_pm.c b/drivers/gpu/drm/nouveau/nv40_pm.c
index 661d9cfd980d031e78ad862b4fe2f99bfff5e355..d857525666ee5437366c81e89c83a535618c129c 100644
--- a/drivers/gpu/drm/nouveau/nv40_pm.c
+++ b/drivers/gpu/drm/nouveau/nv40_pm.c
@@ -107,7 +107,7 @@ struct nv40_pm_state {
 };
 
 static int
-nv40_calc_pll(struct drm_device *dev, u32 reg, struct pll_lims *pll,
+nv40_calc_pll(struct drm_device *dev, u32 reg, struct nvbios_pll *pll,
 	      u32 clk, int *N1, int *M1, int *N2, int *M2, int *log2P)
 {
 	struct nouveau_pll_vals coef;
@@ -117,8 +117,8 @@ nv40_calc_pll(struct drm_device *dev, u32 reg, struct pll_lims *pll,
 	if (ret)
 		return ret;
 
-	if (clk < pll->vco1.maxfreq)
-		pll->vco2.maxfreq = 0;
+	if (clk < pll->vco1.max_freq)
+		pll->vco2.max_freq = 0;
 
 	ret = nouveau_calc_pll_mnp(dev, pll, clk, &coef);
 	if (ret == 0)
@@ -127,7 +127,7 @@ nv40_calc_pll(struct drm_device *dev, u32 reg, struct pll_lims *pll,
 	*N1 = coef.N1;
 	*M1 = coef.M1;
 	if (N2 && M2) {
-		if (pll->vco2.maxfreq) {
+		if (pll->vco2.max_freq) {
 			*N2 = coef.N2;
 			*M2 = coef.M2;
 		} else {
@@ -143,7 +143,7 @@ void *
 nv40_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
 {
 	struct nv40_pm_state *info;
-	struct pll_lims pll;
+	struct nvbios_pll pll;
 	int N1, N2, M1, M2, log2P;
 	int ret;
 
@@ -191,7 +191,7 @@ nv40_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
 		goto out;
 
 	info->mpll_ctrl  = 0x80000000 | (log2P << 16);
-	info->mpll_ctrl |= min2(pll.log2p_bias + log2P, pll.max_log2p) << 20;
+	info->mpll_ctrl |= min2(pll.bias_p + log2P, pll.max_p) << 20;
 	if (N2 == M2) {
 		info->mpll_ctrl |= 0x00000100;
 		info->mpll_coef  = (N1 << 8) | M1;
diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c
index 22cebd5dd694ecd584a3243bc45a21064a9293e7..40042c1bbc6ec05d5af62ef1aae48797e6a32856 100644
--- a/drivers/gpu/drm/nouveau/nv50_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
@@ -329,55 +329,7 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update)
 int
 nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct pll_lims pll;
-	uint32_t reg1, reg2;
-	int ret, N1, M1, N2, M2, P;
-
-	ret = get_pll_limits(dev, PLL_VPLL0 + head, &pll);
-	if (ret)
-		return ret;
-
-	if (pll.vco2.maxfreq) {
-		ret = nv50_calc_pll(dev, &pll, pclk, &N1, &M1, &N2, &M2, &P);
-		if (ret <= 0)
-			return 0;
-
-		NV_DEBUG(dev, "pclk %d out %d NM1 %d %d NM2 %d %d P %d\n",
-			 pclk, ret, N1, M1, N2, M2, P);
-
-		reg1 = nv_rd32(dev, pll.reg + 4) & 0xff00ff00;
-		reg2 = nv_rd32(dev, pll.reg + 8) & 0x8000ff00;
-		nv_wr32(dev, pll.reg + 0, 0x10000611);
-		nv_wr32(dev, pll.reg + 4, reg1 | (M1 << 16) | N1);
-		nv_wr32(dev, pll.reg + 8, reg2 | (P << 28) | (M2 << 16) | N2);
-	} else
-	if (dev_priv->chipset < NV_C0) {
-		ret = nva3_calc_pll(dev, &pll, pclk, &N1, &N2, &M1, &P);
-		if (ret <= 0)
-			return 0;
-
-		NV_DEBUG(dev, "pclk %d out %d N %d fN 0x%04x M %d P %d\n",
-			 pclk, ret, N1, N2, M1, P);
-
-		reg1 = nv_rd32(dev, pll.reg + 4) & 0xffc00000;
-		nv_wr32(dev, pll.reg + 0, 0x50000610);
-		nv_wr32(dev, pll.reg + 4, reg1 | (P << 16) | (M1 << 8) | N1);
-		nv_wr32(dev, pll.reg + 8, N2);
-	} else {
-		ret = nva3_calc_pll(dev, &pll, pclk, &N1, &N2, &M1, &P);
-		if (ret <= 0)
-			return 0;
-
-		NV_DEBUG(dev, "pclk %d out %d N %d fN 0x%04x M %d P %d\n",
-			 pclk, ret, N1, N2, M1, P);
-
-		nv_mask(dev, pll.reg + 0x0c, 0x00000000, 0x00000100);
-		nv_wr32(dev, pll.reg + 0x04, (P << 16) | (N1 << 8) | M1);
-		nv_wr32(dev, pll.reg + 0x10, N2 << 16);
-	}
-
-	return 0;
+	return setPLL(dev, PLL_VPLL0 + head, pclk);
 }
 
 static void
diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c
index be5704fd4a868f097eb0c82d33563915ed079744..378ca8ca9d6a8d392d814fe464ef654de0e01083 100644
--- a/drivers/gpu/drm/nouveau/nv50_pm.c
+++ b/drivers/gpu/drm/nouveau/nv50_pm.c
@@ -363,7 +363,7 @@ struct nv50_pm_state {
 };
 
 static u32
-calc_pll(struct drm_device *dev, u32 reg, struct pll_lims *pll,
+calc_pll(struct drm_device *dev, u32 reg, struct nvbios_pll *pll,
 	 u32 clk, int *N1, int *M1, int *log2P)
 {
 	struct nouveau_pll_vals coef;
@@ -373,7 +373,7 @@ calc_pll(struct drm_device *dev, u32 reg, struct pll_lims *pll,
 	if (ret)
 		return 0;
 
-	pll->vco2.maxfreq = 0;
+	pll->vco2.max_freq = 0;
 	pll->refclk = read_pll_ref(dev, reg);
 	if (!pll->refclk)
 		return 0;
@@ -542,7 +542,7 @@ calc_mclk(struct drm_device *dev, struct nouveau_pm_level *perflvl,
 		.priv = info
 	};
 	struct hwsq_ucode *hwsq = &info->mclk_hwsq;
-	struct pll_lims pll;
+	struct nvbios_pll pll;
 	int N, M, P;
 	int ret;
 
@@ -550,14 +550,14 @@ calc_mclk(struct drm_device *dev, struct nouveau_pm_level *perflvl,
 	info->mctrl  = nv_rd32(dev, 0x004008);
 	info->mctrl &= ~0x81ff0200;
 	if (clk_same(perflvl->memory, read_clk(dev, clk_src_href))) {
-		info->mctrl |= 0x00000200 | (pll.log2p_bias << 19);
+		info->mctrl |= 0x00000200 | (pll.bias_p << 19);
 	} else {
 		ret = calc_pll(dev, 0x4008, &pll, perflvl->memory, &N, &M, &P);
 		if (ret == 0)
 			return -EINVAL;
 
 		info->mctrl |= 0x80000000 | (P << 22) | (P << 16);
-		info->mctrl |= pll.log2p_bias << 19;
+		info->mctrl |= pll.bias_p << 19;
 		info->mcoef  = (N << 8) | M;
 	}
 
@@ -590,7 +590,7 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nv50_pm_state *info;
 	struct hwsq_ucode *hwsq;
-	struct pll_lims pll;
+	struct nvbios_pll pll;
 	u32 out, mast, divs, ctrl;
 	int clk, ret = -EINVAL;
 	int N, M, P1, P2;
diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c
index ff0fafbfe6214976475e782a80464bd37fa974e6..847c616de12ff07cbf1fc0efbf70b021df13a77a 100644
--- a/drivers/gpu/drm/nouveau/nva3_pm.c
+++ b/drivers/gpu/drm/nouveau/nva3_pm.c
@@ -111,7 +111,7 @@ struct creg {
 static int
 calc_clk(struct drm_device *dev, int clk, u32 pll, u32 khz, struct creg *reg)
 {
-	struct pll_lims limits;
+	struct nvbios_pll limits;
 	u32 oclk, sclk, sdiv;
 	int P, N, M, diff;
 	int ret;
diff --git a/drivers/gpu/drm/nouveau/nvc0_pm.c b/drivers/gpu/drm/nouveau/nvc0_pm.c
index 81b649864fa103dca2de884c24272df2d83ac29e..4a21a7264c3c1a8ad068b2ed263383a3305a14e6 100644
--- a/drivers/gpu/drm/nouveau/nvc0_pm.c
+++ b/drivers/gpu/drm/nouveau/nvc0_pm.c
@@ -212,7 +212,7 @@ calc_src(struct drm_device *dev, int clk, u32 freq, u32 *dsrc, u32 *ddiv)
 static u32
 calc_pll(struct drm_device *dev, int clk, u32 freq, u32 *coef)
 {
-	struct pll_lims limits;
+	struct nvbios_pll limits;
 	int N, M, P, ret;
 
 	ret = get_pll_limits(dev, 0x137000 + (clk * 0x20), &limits);
@@ -308,7 +308,7 @@ calc_clk(struct drm_device *dev, int clk, struct nvc0_pm_clock *info, u32 freq)
 static int
 calc_mem(struct drm_device *dev, struct nvc0_pm_clock *info, u32 freq)
 {
-	struct pll_lims pll;
+	struct nvbios_pll pll;
 	int N, M, P, ret;
 	u32 ctrl;