nouveau-y += core/subdev/devinit/nv1a.o
 nouveau-y += core/subdev/devinit/nv20.o
 nouveau-y += core/subdev/devinit/nv50.o
+nouveau-y += core/subdev/devinit/nva3.o
+nouveau-y += core/subdev/devinit/nvc0.o
 nouveau-y += core/subdev/fb/base.o
 nouveau-y += core/subdev/fb/nv04.o
 nouveau-y += core/subdev/fb/nv10.o
 
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
+               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nva3_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
                device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
+               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nva3_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
                device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
+               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nva3_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
                device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
+               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nva3_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
                device->oclass[NVDEV_SUBDEV_BUS    ] = &nv50_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
+               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
                device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
+               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
                device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
+               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
                device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
+               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
                device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
+               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
                device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
+               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
                device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
+               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
                device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
+               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
                device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
+               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
                device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
+               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
                device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
+               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
                device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
+               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
                device->oclass[NVDEV_SUBDEV_BUS    ] = &nvc0_bus_oclass;
                device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
 
 #include <subdev/bios/disp.h>
 #include <subdev/bios/init.h>
 #include <subdev/bios/pll.h>
+#include <subdev/devinit.h>
 #include <subdev/timer.h>
 #include <subdev/fb.h>
-#include <subdev/clock.h>
 
 #include "nv50.h"
 
 static void
 nv50_disp_intr_unk20_1(struct nv50_disp_priv *priv, int head)
 {
-       struct nouveau_clock *clk = nouveau_clock(priv);
+       struct nouveau_devinit *devinit = nouveau_devinit(priv);
        u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
        if (pclk)
-               clk->pll_set(clk, PLL_VPLL0 + head, pclk);
+               devinit->pll_set(devinit, PLL_VPLL0 + head, pclk);
 }
 
 static void
 
 
 #include <engine/disp.h>
 
-#include <subdev/timer.h>
-#include <subdev/fb.h>
-#include <subdev/clock.h>
-
 #include <subdev/bios.h>
 #include <subdev/bios/dcb.h>
 #include <subdev/bios/disp.h>
 #include <subdev/bios/init.h>
 #include <subdev/bios/pll.h>
+#include <subdev/devinit.h>
+#include <subdev/fb.h>
+#include <subdev/timer.h>
 
 #include "nv50.h"
 
 static void
 nvd0_disp_intr_unk2_1(struct nv50_disp_priv *priv, int head)
 {
-       struct nouveau_clock *clk = nouveau_clock(priv);
+       struct nouveau_devinit *devinit = nouveau_devinit(priv);
        u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
        if (pclk)
-               clk->pll_set(clk, PLL_VPLL0 + head, pclk);
+               devinit->pll_set(devinit, PLL_VPLL0 + head, pclk);
        nv_wr32(priv, 0x612200 + (head * 0x800), 0x00000000);
 }
 
 
 struct nouveau_clock {
        struct nouveau_subdev base;
 
-       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
         */
 
        struct nouveau_subdev base;
        bool post;
        void (*meminit)(struct nouveau_devinit *);
+       int  (*pll_set)(struct nouveau_devinit *, u32 type, u32 freq);
+
 };
 
 static inline struct nouveau_devinit *
        nouveau_devinit_create_((p), (e), (o), sizeof(**d), (void **)d)
 #define nouveau_devinit_destroy(p)                                             \
        nouveau_subdev_destroy(&(p)->base)
+#define nouveau_devinit_init(p) ({                                             \
+       struct nouveau_devinit *d = (p);                                       \
+       _nouveau_devinit_init(nv_object(d));                                   \
+})
+#define nouveau_devinit_fini(p,s) ({                                           \
+       struct nouveau_devinit *d = (p);                                       \
+       _nouveau_devinit_fini(nv_object(d), (s));                              \
+})
 
 int nouveau_devinit_create_(struct nouveau_object *, struct nouveau_object *,
                            struct nouveau_oclass *, int, void **);
-int nouveau_devinit_init(struct nouveau_devinit *);
-int nouveau_devinit_fini(struct nouveau_devinit *, bool suspend);
+#define _nouveau_devinit_dtor _nouveau_subdev_dtor
+int _nouveau_devinit_init(struct nouveau_object *);
+int _nouveau_devinit_fini(struct nouveau_object *, bool suspend);
 
 extern struct nouveau_oclass nv04_devinit_oclass;
 extern struct nouveau_oclass nv05_devinit_oclass;
 extern struct nouveau_oclass nv1a_devinit_oclass;
 extern struct nouveau_oclass nv20_devinit_oclass;
 extern struct nouveau_oclass nv50_devinit_oclass;
-
-void nv04_devinit_dtor(struct nouveau_object *);
-int  nv04_devinit_init(struct nouveau_object *);
-int  nv04_devinit_fini(struct nouveau_object *, bool);
+extern struct nouveau_oclass nva3_devinit_oclass;
+extern struct nouveau_oclass nvc0_devinit_oclass;
 
 #endif
 
 #include <subdev/bios/gpio.h>
 #include <subdev/bios/init.h>
 #include <subdev/devinit.h>
-#include <subdev/clock.h>
 #include <subdev/i2c.h>
 #include <subdev/vga.h>
 #include <subdev/gpio.h>
 static void
 init_prog_pll(struct nvbios_init *init, u32 id, u32 freq)
 {
-       struct nouveau_clock *clk = nouveau_clock(init->bios);
-       if (clk && clk->pll_set && init_exec(init)) {
-               int ret = clk->pll_set(clk, id, freq);
+       struct nouveau_devinit *devinit = nouveau_devinit(init->bios);
+       if (devinit->pll_set && init_exec(init)) {
+               int ret = devinit->pll_set(devinit, id, freq);
                if (ret)
                        warn("failed to prog pll 0x%08x to %dkHz\n", id, freq);
        }
 
  * Authors: Ben Skeggs
  */
 
-#include <subdev/clock.h>
 #include <subdev/bios.h>
 #include <subdev/bios/pll.h>
+#include <subdev/clock.h>
+#include <subdev/devinit/priv.h>
 
 #include "pll.h"
 
        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);
-}
-
 int
 nv04_clock_pll_calc(struct nouveau_clock *clock, struct nvbios_pll *info,
                    int clk, struct nouveau_pll_vals *pv)
 nv04_clock_pll_prog(struct nouveau_clock *clk, u32 reg1,
                    struct nouveau_pll_vals *pv)
 {
-       struct nv04_clock_priv *priv = (void *)clk;
+       struct nouveau_devinit *devinit = nouveau_devinit(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);
+                       setPLL_double_highregs(devinit, reg1, pv);
                else
-                       setPLL_double_lowregs(priv, reg1, pv);
+                       setPLL_double_lowregs(devinit, reg1, pv);
        } else
-               setPLL_single(priv, reg1, pv);
+               setPLL_single(devinit, reg1, pv);
 
        return 0;
 }
        if (ret)
                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;
 
        if (ret)
                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;
 
        struct nouveau_clock base;
 };
 
-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;
-       }
-
-       ret = nv04_pll_calc(nv_subdev(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
 nv50_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                struct nouveau_oclass *oclass, void *data, u32 size,
        if (ret)
                return ret;
 
-       priv->base.pll_set = nv50_clock_pll_set;
        priv->base.pll_calc = nv04_clock_pll_calc;
        return 0;
 }
 
        struct nouveau_clock base;
 };
 
-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(nv_subdev(clk), &info, freq, &N, &fN, &M, &P);
-       if (ret < 0)
-               return ret;
-
-       switch (info.type) {
-       case PLL_VPLL0:
-       case PLL_VPLL1:
-               nv_wr32(priv, info.reg + 0, 0x50000610);
-               nv_mask(priv, info.reg + 4, 0x003fffff,
-                                           (P << 16) | (M << 8) | N);
-               nv_wr32(priv, info.reg + 8, fN);
-               break;
-       default:
-               nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
-               ret = -EINVAL;
-               break;
-       }
-
-       return ret;
-}
-
 int
 nva3_clock_pll_calc(struct nouveau_clock *clock, struct nvbios_pll *info,
                    int clk, struct nouveau_pll_vals *pv)
        if (ret)
                return ret;
 
-       priv->base.pll_set = nva3_clock_pll_set;
        priv->base.pll_calc = nva3_clock_pll_calc;
        return 0;
 }
 
        struct nouveau_clock base;
 };
 
-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(nv_subdev(clk), &info, freq, &N, &fN, &M, &P);
-       if (ret < 0)
-               return ret;
-
-       switch (info.type) {
-       case PLL_VPLL0:
-       case PLL_VPLL1:
-       case PLL_VPLL2:
-       case PLL_VPLL3:
-               nv_mask(priv, info.reg + 0x0c, 0x00000000, 0x00000100);
-               nv_wr32(priv, info.reg + 0x04, (P << 16) | (N << 8) | M);
-               nv_wr32(priv, info.reg + 0x10, fN << 16);
-               break;
-       default:
-               nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
-               ret = -EINVAL;
-               break;
-       }
-
-       return ret;
-}
-
 static int
 nvc0_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                struct nouveau_oclass *oclass, void *data, u32 size,
        if (ret)
                return ret;
 
-       priv->base.pll_set = nvc0_clock_pll_set;
        priv->base.pll_calc = nva3_clock_pll_calc;
        return 0;
 }
 
 #include <subdev/bios/init.h>
 
 int
-nouveau_devinit_init(struct nouveau_devinit *devinit)
+_nouveau_devinit_fini(struct nouveau_object *object, bool suspend)
 {
-       int ret = nouveau_subdev_init(&devinit->base);
-       if (ret)
-               return ret;
+       struct nouveau_devinit *devinit = (void *)object;
 
-       return nvbios_init(&devinit->base, devinit->post);
-}
-
-int
-nouveau_devinit_fini(struct nouveau_devinit *devinit, bool suspend)
-{
        /* force full reinit on resume */
        if (suspend)
                devinit->post = true;
        return nouveau_subdev_fini(&devinit->base, suspend);
 }
 
+int
+_nouveau_devinit_init(struct nouveau_object *object)
+{
+       struct nouveau_devinit *devinit = (void *)object;
+       int ret = nouveau_subdev_init(&devinit->base);
+       if (ret)
+               return ret;
+
+       return nvbios_init(&devinit->base, devinit->post);
+}
+
 int
 nouveau_devinit_create_(struct nouveau_object *parent,
                        struct nouveau_object *engine,
 
  *
  */
 
-#include <subdev/devinit.h>
 #include <subdev/vga.h>
 
 #include "fbmem.h"
+#include "priv.h"
 
 struct nv04_devinit_priv {
        struct nouveau_devinit base;
 }
 
 static int
-nv04_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-                 struct nouveau_oclass *oclass, void *data, u32 size,
-                 struct nouveau_object **pobject)
+powerctrl_1_shift(int chip_version, int reg)
 {
-       struct nv04_devinit_priv *priv;
+       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;
+}
+
+void
+setPLL_single(struct nouveau_devinit *devinit, u32 reg,
+             struct nouveau_pll_vals *pv)
+{
+       int chip_version = nouveau_bios(devinit)->version.chip;
+       uint32_t oldpll = nv_rd32(devinit, 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(devinit, 0x001584);
+               nv_wr32(devinit, 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(devinit, reg, pv->log2P << 16 | (oldpll & 0xffff));
+       else
+               /* downclock -- write new NM first */
+               nv_wr32(devinit, reg, (oldpll & 0xffff0000) | pv->NM1);
+
+       if (chip_version < 0x17 && chip_version != 0x11)
+               /* wait a bit on older chips */
+               msleep(64);
+       nv_rd32(devinit, reg);
+
+       /* then write the other half as well */
+       nv_wr32(devinit, reg, pll);
+
+       if (shift_powerctrl_1 >= 0)
+               nv_wr32(devinit, 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;
+}
+
+void
+setPLL_double_highregs(struct nouveau_devinit *devinit, u32 reg1,
+                      struct nouveau_pll_vals *pv)
+{
+       int chip_version = nouveau_bios(devinit)->version.chip;
+       bool nv3035 = chip_version == 0x30 || chip_version == 0x35;
+       uint32_t reg2 = reg1 + ((reg1 == 0x680520) ? 0x5c : 0x70);
+       uint32_t oldpll1 = nv_rd32(devinit, reg1);
+       uint32_t oldpll2 = !nv3035 ? nv_rd32(devinit, 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(devinit, 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(devinit, 0x001584);
+               nv_wr32(devinit, 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(devinit, 0xc040);
+               if (shift_c040 != 14)
+                       nv_wr32(devinit, 0xc040, savedc040 & ~(3 << shift_c040));
+       }
+
+       if (oldramdac580 != ramdac580)
+               nv_wr32(devinit, 0x680580, ramdac580);
+
+       if (!nv3035)
+               nv_wr32(devinit, reg2, pll2);
+       nv_wr32(devinit, reg1, pll1);
+
+       if (shift_powerctrl_1 >= 0)
+               nv_wr32(devinit, 0x001584, saved_powerctrl_1);
+       if (chip_version >= 0x40)
+               nv_wr32(devinit, 0xc040, savedc040);
+}
+
+void
+setPLL_double_lowregs(struct nouveau_devinit *devinit, 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(devinit, 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(devinit, 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(devinit), 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(devinit, 0x4600);
+               nv_wr32(devinit, 0x4600, saved4600 | 8 << 28);
+       }
+       if (single_stage)
+               Pval |= mpll ? 1 << 12 : 1 << 8;
+
+       nv_wr32(devinit, Preg, oldPval | 1 << 28);
+       nv_wr32(devinit, Preg, Pval & ~(4 << 28));
+       if (mpll) {
+               Pval |= 8 << 20;
+               nv_wr32(devinit, 0x4020, Pval & ~(0xc << 28));
+               nv_wr32(devinit, 0x4038, Pval & ~(0xc << 28));
+       }
+
+       savedc040 = nv_rd32(devinit, 0xc040);
+       nv_wr32(devinit, 0xc040, savedc040 & maskc040);
+
+       nv_wr32(devinit, NMNMreg, NMNM);
+       if (NMNMreg == 0x4024)
+               nv_wr32(devinit, 0x403c, NMNM);
+
+       nv_wr32(devinit, Preg, Pval);
+       if (mpll) {
+               Pval &= ~(8 << 20);
+               nv_wr32(devinit, 0x4020, Pval);
+               nv_wr32(devinit, 0x4038, Pval);
+               nv_wr32(devinit, 0x4600, saved4600);
+       }
+
+       nv_wr32(devinit, 0xc040, savedc040);
+
+       if (mpll) {
+               nv_wr32(devinit, 0x4020, Pval & ~(1 << 28));
+               nv_wr32(devinit, 0x4038, Pval & ~(1 << 28));
+       }
+}
+
+int
+nv04_devinit_pll_set(struct nouveau_devinit *devinit, u32 type, u32 freq)
+{
+       struct nouveau_bios *bios = nouveau_bios(devinit);
+       struct nouveau_pll_vals pv;
+       struct nvbios_pll info;
+       int cv = bios->version.chip;
+       int N1, M1, N2, M2, P;
        int ret;
 
-       ret = nouveau_devinit_create(parent, engine, oclass, &priv);
-       *pobject = nv_object(priv);
+       ret = nvbios_pll_parse(bios, type > 0x405c ? type : type - 4, &info);
        if (ret)
                return ret;
 
-       priv->base.meminit = nv04_devinit_meminit;
-       priv->owner = -1;
+       ret = nv04_pll_calc(nv_subdev(devinit), &info, freq,
+                          &N1, &M1, &N2, &M2, &P);
+       if (!ret)
+               return -EINVAL;
+
+       pv.refclk = info.refclk;
+       pv.N1 = N1;
+       pv.M1 = M1;
+       pv.N2 = N2;
+       pv.M2 = M2;
+       pv.log2P = P;
+
+       if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
+           cv >= 0x40) {
+               if (type > 0x405c)
+                       setPLL_double_highregs(devinit, type, &pv);
+               else
+                       setPLL_double_lowregs(devinit, type, &pv);
+       } else
+               setPLL_single(devinit, type, &pv);
+
        return 0;
 }
 
-void
-nv04_devinit_dtor(struct nouveau_object *object)
+int
+nv04_devinit_fini(struct nouveau_object *object, bool suspend)
 {
        struct nv04_devinit_priv *priv = (void *)object;
 
-       /* restore vga owner saved at first init, and lock crtc regs  */
-       nv_wrvgaowner(priv, priv->owner);
-       nv_lockvgac(priv, true);
+       /* make i2c busses accessible */
+       nv_mask(priv, 0x000200, 0x00000001, 0x00000001);
 
-       nouveau_devinit_destroy(&priv->base);
+       /* unlock extended vga crtc regs, and unslave crtcs */
+       nv_lockvgac(priv, false);
+       if (priv->owner < 0)
+               priv->owner = nv_rdvgaowner(priv);
+       nv_wrvgaowner(priv, 0);
+
+       return nouveau_devinit_fini(&priv->base, suspend);
 }
 
 int
        return nouveau_devinit_init(&priv->base);
 }
 
-int
-nv04_devinit_fini(struct nouveau_object *object, bool suspend)
+void
+nv04_devinit_dtor(struct nouveau_object *object)
 {
        struct nv04_devinit_priv *priv = (void *)object;
 
-       /* make i2c busses accessible */
-       nv_mask(priv, 0x000200, 0x00000001, 0x00000001);
+       /* restore vga owner saved at first init, and lock crtc regs  */
+       nv_wrvgaowner(priv, priv->owner);
+       nv_lockvgac(priv, true);
 
-       /* unlock extended vga crtc regs, and unslave crtcs */
-       nv_lockvgac(priv, false);
-       if (priv->owner < 0)
-               priv->owner = nv_rdvgaowner(priv);
-       nv_wrvgaowner(priv, 0);
+       nouveau_devinit_destroy(&priv->base);
+}
 
-       return nouveau_devinit_fini(&priv->base, suspend);
+static int
+nv04_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                 struct nouveau_oclass *oclass, void *data, u32 size,
+                 struct nouveau_object **pobject)
+{
+       struct nv04_devinit_priv *priv;
+       int ret;
+
+       ret = nouveau_devinit_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.meminit = nv04_devinit_meminit;
+       priv->base.pll_set = nv04_devinit_pll_set;
+       priv->owner = -1;
+       return 0;
 }
 
 struct nouveau_oclass
 
  *
  */
 
-#include <subdev/devinit.h>
 #include <subdev/bios.h>
 #include <subdev/bios/bmp.h>
 #include <subdev/vga.h>
 
 #include "fbmem.h"
+#include "priv.h"
 
 struct nv05_devinit_priv {
        struct nouveau_devinit base;
                return ret;
 
        priv->base.meminit = nv05_devinit_meminit;
+       priv->base.pll_set = nv04_devinit_pll_set;
        return 0;
 }
 
 
  *
  */
 
-#include <subdev/devinit.h>
 #include <subdev/vga.h>
 
 #include "fbmem.h"
+#include "priv.h"
 
 struct nv10_devinit_priv {
        struct nouveau_devinit base;
                return ret;
 
        priv->base.meminit = nv10_devinit_meminit;
+       priv->base.pll_set = nv04_devinit_pll_set;
        return 0;
 }
 
 
  * Authors: Ben Skeggs
  */
 
-#include <subdev/devinit.h>
-#include <subdev/vga.h>
+#include "priv.h"
 
 struct nv1a_devinit_priv {
        struct nouveau_devinit base;
        if (ret)
                return ret;
 
+       priv->base.pll_set = nv04_devinit_pll_set;
        return 0;
 }
 
 
  *
  */
 
-#include <subdev/devinit.h>
-#include <subdev/vga.h>
-
+#include "priv.h"
 #include "fbmem.h"
 
 struct nv20_devinit_priv {
                return ret;
 
        priv->base.meminit = nv20_devinit_meminit;
+       priv->base.pll_set = nv04_devinit_pll_set;
        return 0;
 }
 
 
 /*
- * Copyright 2012 Red Hat Inc.
+ * Copyright 2013 Red Hat Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
 #include <subdev/bios/dcb.h>
 #include <subdev/bios/disp.h>
 #include <subdev/bios/init.h>
-#include <subdev/devinit.h>
 #include <subdev/vga.h>
 
-struct nv50_devinit_priv {
-       struct nouveau_devinit base;
-};
+#include "priv.h"
 
 static int
-nv50_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-                 struct nouveau_oclass *oclass, void *data, u32 size,
-                 struct nouveau_object **pobject)
+nv50_devinit_pll_set(struct nouveau_devinit *devinit, u32 type, u32 freq)
 {
-       struct nv50_devinit_priv *priv;
+       struct nv50_devinit_priv *priv = (void *)devinit;
+       struct nouveau_bios *bios = nouveau_bios(priv);
+       struct nvbios_pll info;
+       int N1, M1, N2, M2, P;
        int ret;
 
-       ret = nouveau_devinit_create(parent, engine, oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
+       ret = nvbios_pll_parse(bios, type, &info);
+       if (ret) {
+               nv_error(devinit, "failed to retrieve pll data, %d\n", ret);
                return ret;
+       }
 
-       return 0;
-}
+       ret = nv04_pll_calc(nv_subdev(devinit), &info, freq, &N1, &M1, &N2, &M2, &P);
+       if (!ret) {
+               nv_error(devinit, "failed pll calculation\n");
+               return ret;
+       }
 
-static void
-nv50_devinit_dtor(struct nouveau_object *object)
-{
-       struct nv50_devinit_priv *priv = (void *)object;
-       nouveau_devinit_destroy(&priv->base);
+       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
+int
 nv50_devinit_init(struct nouveau_object *object)
 {
        struct nouveau_bios *bios = nouveau_bios(object);
 }
 
 static int
-nv50_devinit_fini(struct nouveau_object *object, bool suspend)
+nv50_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                 struct nouveau_oclass *oclass, void *data, u32 size,
+                 struct nouveau_object **pobject)
 {
-       struct nv50_devinit_priv *priv = (void *)object;
-       return nouveau_devinit_fini(&priv->base, suspend);
+       struct nv50_devinit_priv *priv;
+       int ret;
+
+       ret = nouveau_devinit_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.pll_set = nv50_devinit_pll_set;
+       return 0;
 }
 
 struct nouveau_oclass
        .handle = NV_SUBDEV(DEVINIT, 0x50),
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv50_devinit_ctor,
-               .dtor = nv50_devinit_dtor,
+               .dtor = _nouveau_devinit_dtor,
                .init = nv50_devinit_init,
-               .fini = nv50_devinit_fini,
+               .fini = _nouveau_devinit_fini,
        },
 };
 
--- /dev/null
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "priv.h"
+
+static int
+nva3_devinit_pll_set(struct nouveau_devinit *devinit, u32 type, u32 freq)
+{
+       struct nva3_devinit_priv *priv = (void *)devinit;
+       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(nv_subdev(devinit), &info, freq, &N, &fN, &M, &P);
+       if (ret < 0)
+               return ret;
+
+       switch (info.type) {
+       case PLL_VPLL0:
+       case PLL_VPLL1:
+               nv_wr32(priv, info.reg + 0, 0x50000610);
+               nv_mask(priv, info.reg + 4, 0x003fffff,
+                                           (P << 16) | (M << 8) | N);
+               nv_wr32(priv, info.reg + 8, fN);
+               break;
+       default:
+               nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static int
+nva3_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                 struct nouveau_oclass *oclass, void *data, u32 size,
+                 struct nouveau_object **pobject)
+{
+       struct nv50_devinit_priv *priv;
+       int ret;
+
+       ret = nouveau_devinit_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.pll_set = nva3_devinit_pll_set;
+       return 0;
+}
+
+struct nouveau_oclass
+nva3_devinit_oclass = {
+       .handle = NV_SUBDEV(DEVINIT, 0xa3),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nva3_devinit_ctor,
+               .dtor = _nouveau_devinit_dtor,
+               .init = nv50_devinit_init,
+               .fini = _nouveau_devinit_fini,
+       },
+};
 
--- /dev/null
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "priv.h"
+
+static int
+nvc0_devinit_pll_set(struct nouveau_devinit *devinit, u32 type, u32 freq)
+{
+       struct nvc0_devinit_priv *priv = (void *)devinit;
+       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(nv_subdev(devinit), &info, freq, &N, &fN, &M, &P);
+       if (ret < 0)
+               return ret;
+
+       switch (info.type) {
+       case PLL_VPLL0:
+       case PLL_VPLL1:
+       case PLL_VPLL2:
+       case PLL_VPLL3:
+               nv_mask(priv, info.reg + 0x0c, 0x00000000, 0x00000100);
+               nv_wr32(priv, info.reg + 0x04, (P << 16) | (N << 8) | M);
+               nv_wr32(priv, info.reg + 0x10, fN << 16);
+               break;
+       default:
+               nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static int
+nvc0_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                 struct nouveau_oclass *oclass, void *data, u32 size,
+                 struct nouveau_object **pobject)
+{
+       struct nv50_devinit_priv *priv;
+       int ret;
+
+       ret = nouveau_devinit_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.pll_set = nvc0_devinit_pll_set;
+       return 0;
+}
+
+struct nouveau_oclass
+nvc0_devinit_oclass = {
+       .handle = NV_SUBDEV(DEVINIT, 0xa3),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_devinit_ctor,
+               .dtor = _nouveau_devinit_dtor,
+               .init = nv50_devinit_init,
+               .fini = _nouveau_devinit_fini,
+       },
+};
 
--- /dev/null
+#ifndef __NVKM_DEVINIT_PRIV_H__
+#define __NVKM_DEVINIT_PRIV_H__
+
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+#include <subdev/clock/pll.h>
+#include <subdev/devinit.h>
+
+void nv04_devinit_dtor(struct nouveau_object *);
+int  nv04_devinit_init(struct nouveau_object *);
+int  nv04_devinit_fini(struct nouveau_object *, bool);
+int  nv04_devinit_pll_set(struct nouveau_devinit *, u32, u32);
+
+void setPLL_single(struct nouveau_devinit *, u32, struct nouveau_pll_vals *);
+void setPLL_double_highregs(struct nouveau_devinit *, u32, struct nouveau_pll_vals *);
+void setPLL_double_lowregs(struct nouveau_devinit *, u32, struct nouveau_pll_vals *);
+
+
+struct nv50_devinit_priv {
+       struct nouveau_devinit base;
+};
+
+int  nv50_devinit_init(struct nouveau_object *);
+
+#endif