]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
drm/nouveau: rework init ordering so nv50_instmem.c can be less bad
authorBen Skeggs <bskeggs@redhat.com>
Wed, 1 Sep 2010 05:24:34 +0000 (15:24 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Fri, 24 Sep 2010 06:22:42 +0000 (16:22 +1000)
Reviewed-by: Francisco Jerez <currojerez@riseup.net>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nouveau_bo.c
drivers/gpu/drm/nouveau/nouveau_drv.h
drivers/gpu/drm/nouveau/nouveau_mem.c
drivers/gpu/drm/nouveau/nouveau_object.c
drivers/gpu/drm/nouveau/nouveau_state.c
drivers/gpu/drm/nouveau/nv04_instmem.c
drivers/gpu/drm/nouveau/nv50_instmem.c

index 841c63f2886705fc63ee553180c4c2cd85d00841..8e4a9bce4f3b603cd327c7b71835b3b4f13bbad2 100644 (file)
@@ -403,7 +403,10 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
                man->available_caching = TTM_PL_FLAG_UNCACHED |
                                         TTM_PL_FLAG_WC;
                man->default_caching = TTM_PL_FLAG_WC;
-               man->gpu_offset = dev_priv->vm_vram_base;
+               if (dev_priv->card_type == NV_50)
+                       man->gpu_offset = 0x40000000;
+               else
+                       man->gpu_offset = 0;
                break;
        case TTM_PL_TT:
                switch (dev_priv->gart_info.type) {
index 150fdeea11a1b144b9683f7a1676331ae7e21bc7..3ba7a649fe510e4a120a70cbbff1714af43d4b13 100644 (file)
@@ -700,8 +700,10 @@ extern bool nouveau_wait_for_idle(struct drm_device *);
 extern int  nouveau_card_init(struct drm_device *);
 
 /* nouveau_mem.c */
-extern int  nouveau_mem_detect(struct drm_device *dev);
-extern int  nouveau_mem_init(struct drm_device *);
+extern int  nouveau_mem_vram_init(struct drm_device *);
+extern void nouveau_mem_vram_fini(struct drm_device *);
+extern int  nouveau_mem_gart_init(struct drm_device *);
+extern void nouveau_mem_gart_fini(struct drm_device *);
 extern int  nouveau_mem_init_agp(struct drm_device *);
 extern int  nouveau_mem_reset_agp(struct drm_device *);
 extern void nouveau_mem_close(struct drm_device *);
index 6eeaeac56293a1bac60ed08bf04d8f402c7d0841..fb15a1b0dda9eb578b9aa76149f4e0994196f8dd 100644 (file)
@@ -221,7 +221,7 @@ nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size)
  * Cleanup everything
  */
 void
-nouveau_mem_close(struct drm_device *dev)
+nouveau_mem_vram_fini(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
 
@@ -232,6 +232,19 @@ nouveau_mem_close(struct drm_device *dev)
 
        nouveau_ttm_global_release(dev_priv);
 
+       if (dev_priv->fb_mtrr >= 0) {
+               drm_mtrr_del(dev_priv->fb_mtrr,
+                            pci_resource_start(dev->pdev, 1),
+                            pci_resource_len(dev->pdev, 1), DRM_MTRR_WC);
+               dev_priv->fb_mtrr = -1;
+       }
+}
+
+void
+nouveau_mem_gart_fini(struct drm_device *dev)
+{
+       nouveau_sgdma_takedown(dev);
+
        if (drm_core_has_AGP(dev) && dev->agp) {
                struct drm_agp_mem *entry, *tempe;
 
@@ -251,13 +264,6 @@ nouveau_mem_close(struct drm_device *dev)
                dev->agp->acquired = 0;
                dev->agp->enabled = 0;
        }
-
-       if (dev_priv->fb_mtrr) {
-               drm_mtrr_del(dev_priv->fb_mtrr,
-                            pci_resource_start(dev->pdev, 1),
-                            pci_resource_len(dev->pdev, 1), DRM_MTRR_WC);
-               dev_priv->fb_mtrr = -1;
-       }
 }
 
 static uint32_t
@@ -363,7 +369,7 @@ nvaa_vram_preinit(struct drm_device *dev)
        dev_priv->vram_rblock_size = 1;
 }
 
-int
+static int
 nouveau_mem_detect(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -500,24 +506,27 @@ nouveau_mem_init_agp(struct drm_device *dev)
 }
 
 int
-nouveau_mem_init(struct drm_device *dev)
+nouveau_mem_vram_init(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        struct ttm_bo_device *bdev = &dev_priv->ttm.bdev;
-       int ret, dma_bits = 32;
-
-       dev_priv->fb_phys = pci_resource_start(dev->pdev, 1);
-       dev_priv->gart_info.type = NOUVEAU_GART_NONE;
+       int ret, dma_bits;
 
        if (dev_priv->card_type >= NV_50 &&
            pci_dma_supported(dev->pdev, DMA_BIT_MASK(40)))
                dma_bits = 40;
+       else
+               dma_bits = 32;
 
        ret = pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(dma_bits));
-       if (ret) {
-               NV_ERROR(dev, "Error setting DMA mask: %d\n", ret);
+       if (ret)
                return ret;
-       }
+
+       ret = nouveau_mem_detect(dev);
+       if (ret)
+               return ret;
+
+       dev_priv->fb_phys = pci_resource_start(dev->pdev, 1);
 
        ret = nouveau_ttm_global_init(dev_priv);
        if (ret)
@@ -541,7 +550,16 @@ nouveau_mem_init(struct drm_device *dev)
                        pci_resource_len(dev->pdev, 1);
        dev_priv->fb_mappable_pages >>= PAGE_SHIFT;
 
-       /* remove reserved space at end of vram from available amount */
+       /* reserve space at end of VRAM for PRAMIN */
+       if (dev_priv->chipset == 0x40 || dev_priv->chipset == 0x47 ||
+           dev_priv->chipset == 0x49 || dev_priv->chipset == 0x4b)
+               dev_priv->ramin_rsvd_vram = (2 * 1024 * 1024);
+       else
+       if (dev_priv->card_type >= NV_40)
+               dev_priv->ramin_rsvd_vram = (1 * 1024 * 1024);
+       else
+               dev_priv->ramin_rsvd_vram = (512 * 1024);
+
        dev_priv->fb_available_size -= dev_priv->ramin_rsvd_vram;
        dev_priv->fb_aper_free = dev_priv->fb_available_size;
 
@@ -562,7 +580,21 @@ nouveau_mem_init(struct drm_device *dev)
                nouveau_bo_ref(NULL, &dev_priv->vga_ram);
        }
 
-       /* GART */
+       dev_priv->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 1),
+                                        pci_resource_len(dev->pdev, 1),
+                                        DRM_MTRR_WC);
+       return 0;
+}
+
+int
+nouveau_mem_gart_init(struct drm_device *dev)
+{
+       struct drm_nouveau_private *dev_priv = dev->dev_private;
+       struct ttm_bo_device *bdev = &dev_priv->ttm.bdev;
+       int ret;
+
+       dev_priv->gart_info.type = NOUVEAU_GART_NONE;
+
 #if !defined(__powerpc__) && !defined(__ia64__)
        if (drm_device_is_agp(dev) && dev->agp && !nouveau_noagp) {
                ret = nouveau_mem_init_agp(dev);
@@ -590,11 +622,6 @@ nouveau_mem_init(struct drm_device *dev)
                return ret;
        }
 
-       dev_priv->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 1),
-                                        pci_resource_len(dev->pdev, 1),
-                                        DRM_MTRR_WC);
-
        return 0;
 }
 
-
index df445fcb8321c34e7f95c94dbe3aa52586501b0d..b68922f2fe544b18d6de24df7003c5e014a99d7f 100644 (file)
@@ -130,7 +130,7 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan,
 
        /* if we got a chunk of the aperture, map pages into it */
        gpuobj->im_pramin = ramin;
-       if (!chan && gpuobj->im_pramin) {
+       if (!chan && gpuobj->im_pramin && dev_priv->ramin_available) {
                ret = engine->instmem.bind(dev, gpuobj);
                if (ret) {
                        nouveau_gpuobj_ref(NULL, &gpuobj);
@@ -173,7 +173,7 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan,
 }
 
 int
-nouveau_gpuobj_early_init(struct drm_device *dev)
+nouveau_gpuobj_init(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
 
@@ -186,29 +186,6 @@ nouveau_gpuobj_early_init(struct drm_device *dev)
        return 0;
 }
 
-int
-nouveau_gpuobj_init(struct drm_device *dev)
-{
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nouveau_gpuobj *ramht = NULL;
-       int ret;
-
-       NV_DEBUG(dev, "\n");
-
-       if (dev_priv->card_type >= NV_50)
-               return 0;
-
-       ret = nouveau_gpuobj_new_fake(dev, dev_priv->ramht_offset, ~0,
-                                     dev_priv->ramht_size,
-                                     NVOBJ_FLAG_ZERO_ALLOC, &ramht);
-       if (ret)
-               return ret;
-
-       ret = nouveau_ramht_new(dev, ramht, &dev_priv->ramht);
-       nouveau_gpuobj_ref(NULL, &ramht);
-       return ret;
-}
-
 void
 nouveau_gpuobj_takedown(struct drm_device *dev)
 {
index fec29522298d520fec6e3f1f1c47975aad4bf7ca..19eb06dca899545b97d9240599f65bb958bcb8c1 100644 (file)
@@ -532,35 +532,26 @@ nouveau_card_init(struct drm_device *dev)
        if (ret)
                goto out_display_early;
 
-       ret = nouveau_mem_detect(dev);
+       ret = nouveau_mem_vram_init(dev);
        if (ret)
                goto out_bios;
 
-       ret = nouveau_gpuobj_early_init(dev);
+       ret = nouveau_gpuobj_init(dev);
        if (ret)
-               goto out_bios;
+               goto out_vram;
 
-       /* Initialise instance memory, must happen before mem_init so we
-        * know exactly how much VRAM we're able to use for "normal"
-        * purposes.
-        */
        ret = engine->instmem.init(dev);
        if (ret)
-               goto out_gpuobj_early;
+               goto out_gpuobj;
 
-       /* Setup the memory manager */
-       ret = nouveau_mem_init(dev);
+       ret = nouveau_mem_gart_init(dev);
        if (ret)
                goto out_instmem;
 
-       ret = nouveau_gpuobj_init(dev);
-       if (ret)
-               goto out_mem;
-
        /* PMC */
        ret = engine->mc.init(dev);
        if (ret)
-               goto out_gpuobj;
+               goto out_gart;
 
        /* PGPIO */
        ret = engine->gpio.init(dev);
@@ -640,15 +631,14 @@ out_gpio:
        engine->gpio.takedown(dev);
 out_mc:
        engine->mc.takedown(dev);
-out_gpuobj:
-       nouveau_gpuobj_takedown(dev);
-out_mem:
-       nouveau_sgdma_takedown(dev);
-       nouveau_mem_close(dev);
+out_gart:
+       nouveau_mem_gart_fini(dev);
 out_instmem:
        engine->instmem.takedown(dev);
-out_gpuobj_early:
-       nouveau_gpuobj_late_takedown(dev);
+out_gpuobj:
+       nouveau_gpuobj_takedown(dev);
+out_vram:
+       nouveau_mem_vram_fini(dev);
 out_bios:
        nouveau_bios_takedown(dev);
 out_display_early:
@@ -684,15 +674,14 @@ static void nouveau_card_takedown(struct drm_device *dev)
        ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM);
        ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_TT);
        mutex_unlock(&dev->struct_mutex);
-       nouveau_sgdma_takedown(dev);
+       nouveau_mem_gart_fini(dev);
 
-       nouveau_gpuobj_takedown(dev);
-       nouveau_mem_close(dev);
        engine->instmem.takedown(dev);
+       nouveau_gpuobj_takedown(dev);
+       nouveau_mem_vram_fini(dev);
 
        drm_irq_uninstall(dev);
 
-       nouveau_gpuobj_late_takedown(dev);
        nouveau_bios_takedown(dev);
 
        vga_client_register(dev->pdev, NULL, NULL, NULL);
index 3aba7674560cf722812fdef4cc9ed429b9401af8..15cd468f4c290b5b1c27c7710b217ae1f15661c0 100644 (file)
@@ -1,6 +1,7 @@
 #include "drmP.h"
 #include "drm.h"
 #include "nouveau_drv.h"
+#include "nouveau_ramht.h"
 
 /* returns the size of fifo context */
 static int
@@ -17,42 +18,6 @@ nouveau_fifo_ctx_size(struct drm_device *dev)
        return 32;
 }
 
-static void
-nv04_instmem_determine_amount(struct drm_device *dev)
-{
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
-       int i;
-
-       /* Figure out how much instance memory we need */
-       if (dev_priv->card_type >= NV_40) {
-               /* We'll want more instance memory than this on some NV4x cards.
-                * There's a 16MB aperture to play with that maps onto the end
-                * of vram.  For now, only reserve a small piece until we know
-                * more about what each chipset requires.
-                */
-               switch (dev_priv->chipset) {
-               case 0x40:
-               case 0x47:
-               case 0x49:
-               case 0x4b:
-                       dev_priv->ramin_rsvd_vram = (2 * 1024 * 1024);
-                       break;
-               default:
-                       dev_priv->ramin_rsvd_vram = (1 * 1024 * 1024);
-                       break;
-               }
-       } else {
-               /*XXX: what *are* the limits on <NV40 cards?
-                */
-               dev_priv->ramin_rsvd_vram = (512 * 1024);
-       }
-       NV_DEBUG(dev, "RAMIN size: %dKiB\n", dev_priv->ramin_rsvd_vram >> 10);
-
-       /* Clear all of it, except the BIOS image that's in the first 64KiB */
-       for (i = 64 * 1024; i < dev_priv->ramin_rsvd_vram; i += 4)
-               nv_wi32(dev, i, 0x00000000);
-}
-
 static void
 nv04_instmem_configure_fixed_tables(struct drm_device *dev)
 {
@@ -103,12 +68,24 @@ nv04_instmem_configure_fixed_tables(struct drm_device *dev)
 int nv04_instmem_init(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
+       struct nouveau_gpuobj *ramht = NULL;
        uint32_t offset;
        int ret;
 
-       nv04_instmem_determine_amount(dev);
        nv04_instmem_configure_fixed_tables(dev);
 
+       /* Setup shared RAMHT */
+       ret = nouveau_gpuobj_new_fake(dev, dev_priv->ramht_offset, ~0,
+                                     dev_priv->ramht_size,
+                                     NVOBJ_FLAG_ZERO_ALLOC, &ramht);
+       if (ret)
+               return ret;
+
+       ret = nouveau_ramht_new(dev, ramht, &dev_priv->ramht);
+       nouveau_gpuobj_ref(NULL, &ramht);
+       if (ret)
+               return ret;
+
        /* Create a heap to manage RAMIN allocations, we don't allocate
         * the space that was reserved for RAMHT/FC/RO.
         */
index 5c617f807b237a64976f07a849c35400e7cb29b5..d932594449c1c74312fb208ea196c4c69939edce 100644 (file)
@@ -37,27 +37,82 @@ struct nv50_instmem_priv {
        struct nouveau_gpuobj *fb_bar;
 };
 
-#define NV50_INSTMEM_PAGE_SHIFT 12
-#define NV50_INSTMEM_PAGE_SIZE  (1 << NV50_INSTMEM_PAGE_SHIFT)
-#define NV50_INSTMEM_PT_SIZE(a)        (((a) >> 12) << 3)
+static void
+nv50_channel_del(struct nouveau_channel **pchan)
+{
+       struct nouveau_channel *chan;
 
-/*NOTE: - Assumes 0x1700 already covers the correct MiB of PRAMIN
- */
-#define BAR0_WI32(g, o, v) do {                                   \
-       u32 offset = (g)->vinst + (o);                            \
-       nv_wr32(dev, NV_RAMIN + (offset & 0xfffff), (v));         \
-} while (0)
+       chan = *pchan;
+       *pchan = NULL;
+       if (!chan)
+               return;
+
+       nouveau_gpuobj_ref(NULL, &chan->ramfc);
+       nouveau_gpuobj_ref(NULL, &chan->vm_pd);
+       if (chan->ramin_heap.free_stack.next)
+               drm_mm_takedown(&chan->ramin_heap);
+       nouveau_gpuobj_ref(NULL, &chan->ramin);
+       kfree(chan);
+}
+
+static int
+nv50_channel_new(struct drm_device *dev, u32 size,
+                struct nouveau_channel **pchan)
+{
+       struct drm_nouveau_private *dev_priv = dev->dev_private;
+       u32 pgd = (dev_priv->chipset == 0x50) ? 0x1400 : 0x0200;
+       u32  fc = (dev_priv->chipset == 0x50) ? 0x0000 : 0x4200;
+       struct nouveau_channel *chan;
+       int ret;
+
+       chan = kzalloc(sizeof(*chan), GFP_KERNEL);
+       if (!chan)
+               return -ENOMEM;
+       chan->dev = dev;
+
+       ret = nouveau_gpuobj_new(dev, NULL, size, 0x1000, 0, &chan->ramin);
+       if (ret) {
+               nv50_channel_del(&chan);
+               return ret;
+       }
+
+       ret = drm_mm_init(&chan->ramin_heap, 0x6000, chan->ramin->size);
+       if (ret) {
+               nv50_channel_del(&chan);
+               return ret;
+       }
+
+       ret = nouveau_gpuobj_new_fake(dev, chan->ramin->pinst == ~0 ? ~0 :
+                                     chan->ramin->pinst + pgd,
+                                     chan->ramin->vinst + pgd,
+                                     0x4000, NVOBJ_FLAG_ZERO_ALLOC,
+                                     &chan->vm_pd);
+       if (ret) {
+               nv50_channel_del(&chan);
+               return ret;
+       }
+
+       ret = nouveau_gpuobj_new_fake(dev, chan->ramin->pinst == ~0 ? ~0 :
+                                     chan->ramin->pinst + fc,
+                                     chan->ramin->vinst + fc, 0x100,
+                                     NVOBJ_FLAG_ZERO_ALLOC, &chan->ramfc);
+       if (ret) {
+               nv50_channel_del(&chan);
+               return ret;
+       }
+
+       *pchan = chan;
+       return 0;
+}
 
 int
 nv50_instmem_init(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nouveau_channel *chan;
-       uint32_t c_offset, c_size, c_ramfc, c_vmpd, c_base, pt_size;
-       uint32_t save_nv001700;
-       uint64_t v;
        struct nv50_instmem_priv *priv;
+       struct nouveau_channel *chan;
        int ret, i;
+       u32 tmp;
 
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv)
@@ -68,206 +123,113 @@ nv50_instmem_init(struct drm_device *dev)
        for (i = 0x1700; i <= 0x1710; i += 4)
                priv->save1700[(i-0x1700)/4] = nv_rd32(dev, i);
 
-       /* Reserve the last MiB of VRAM, we should probably try to avoid
-        * setting up the below tables over the top of the VBIOS image at
-        * some point.
-        */
-       dev_priv->ramin_rsvd_vram = 1 << 20;
-       c_offset = dev_priv->vram_size - dev_priv->ramin_rsvd_vram;
-       c_size   = 128 << 10;
-       c_vmpd   = ((dev_priv->chipset & 0xf0) == 0x50) ? 0x1400 : 0x200;
-       c_ramfc  = ((dev_priv->chipset & 0xf0) == 0x50) ? 0x0 : 0x20;
-       c_base   = c_vmpd + 0x4000;
-       pt_size  = NV50_INSTMEM_PT_SIZE(dev_priv->ramin_size);
-
-       NV_DEBUG(dev, " Rsvd VRAM base: 0x%08x\n", c_offset);
-       NV_DEBUG(dev, "    VBIOS image: 0x%08x\n",
-                               (nv_rd32(dev, 0x619f04) & ~0xff) << 8);
-       NV_DEBUG(dev, "  Aperture size: %d MiB\n", dev_priv->ramin_size >> 20);
-       NV_DEBUG(dev, "        PT size: %d KiB\n", pt_size >> 10);
-
-       /* Determine VM layout, we need to do this first to make sure
-        * we allocate enough memory for all the page tables.
-        */
-       dev_priv->vm_gart_base = roundup(NV50_VM_BLOCK, NV50_VM_BLOCK);
-       dev_priv->vm_gart_size = NV50_VM_BLOCK;
-
-       dev_priv->vm_vram_base = dev_priv->vm_gart_base + dev_priv->vm_gart_size;
-       dev_priv->vm_vram_size = dev_priv->vram_size;
-       if (dev_priv->vm_vram_size > NV50_VM_MAX_VRAM)
-               dev_priv->vm_vram_size = NV50_VM_MAX_VRAM;
-       dev_priv->vm_vram_size = roundup(dev_priv->vm_vram_size, NV50_VM_BLOCK);
-       dev_priv->vm_vram_pt_nr = dev_priv->vm_vram_size / NV50_VM_BLOCK;
-
-       dev_priv->vm_end = dev_priv->vm_vram_base + dev_priv->vm_vram_size;
-
-       NV_DEBUG(dev, "NV50VM: GART 0x%016llx-0x%016llx\n",
-                dev_priv->vm_gart_base,
-                dev_priv->vm_gart_base + dev_priv->vm_gart_size - 1);
-       NV_DEBUG(dev, "NV50VM: VRAM 0x%016llx-0x%016llx\n",
-                dev_priv->vm_vram_base,
-                dev_priv->vm_vram_base + dev_priv->vm_vram_size - 1);
-
-       c_size += dev_priv->vm_vram_pt_nr * (NV50_VM_BLOCK / 65536 * 8);
-
-       /* Map BAR0 PRAMIN aperture over the memory we want to use */
-       save_nv001700 = nv_rd32(dev, NV50_PUNK_BAR0_PRAMIN);
-       nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, (c_offset >> 16));
-
-       /* Create a fake channel, and use it as our "dummy" channels 0/127.
-        * The main reason for creating a channel is so we can use the gpuobj
-        * code.  However, it's probably worth noting that NVIDIA also setup
-        * their channels 0/127 with the same values they configure here.
-        * So, there may be some other reason for doing this.
-        *
-        * Have to create the entire channel manually, as the real channel
-        * creation code assumes we have PRAMIN access, and we don't until
-        * we're done here.
-        */
-       chan = kzalloc(sizeof(*chan), GFP_KERNEL);
-       if (!chan)
+       /* Global PRAMIN heap */
+       ret = drm_mm_init(&dev_priv->ramin_heap, 0, dev_priv->ramin_size);
+       if (ret) {
+               NV_ERROR(dev, "Failed to init RAMIN heap\n");
                return -ENOMEM;
-       chan->id = 0;
-       chan->dev = dev;
-       chan->file_priv = (struct drm_file *)-2;
-       dev_priv->fifos[0] = dev_priv->fifos[127] = chan;
-
-       INIT_LIST_HEAD(&chan->ramht_refs);
+       }
 
-       /* Channel's PRAMIN object + heap */
-       ret = nouveau_gpuobj_new_fake(dev, 0, c_offset, c_size, 0, &chan->ramin);
+       /* we need a channel to plug into the hw to control the BARs */
+       ret = nv50_channel_new(dev, 128*1024, &dev_priv->fifos[0]);
        if (ret)
                return ret;
+       chan = dev_priv->fifos[127] = dev_priv->fifos[0];
 
-       if (drm_mm_init(&chan->ramin_heap, c_base, c_size - c_base))
-               return -ENOMEM;
-
-       /* RAMFC + zero channel's PRAMIN up to start of VM pagedir */
-       ret = nouveau_gpuobj_new_fake(dev, c_ramfc, c_offset + c_ramfc,
-                                     0x4000, 0, &chan->ramfc);
+       /* allocate page table for PRAMIN BAR */
+       ret = nouveau_gpuobj_new(dev, chan, (dev_priv->ramin_size >> 12) * 8,
+                                0x1000, NVOBJ_FLAG_ZERO_ALLOC,
+                                &priv->pramin_pt);
        if (ret)
                return ret;
 
-       for (i = 0; i < c_vmpd; i += 4)
-               BAR0_WI32(chan->ramin, i, 0);
+       nv_wo32(chan->vm_pd, 0x0000, priv->pramin_pt->vinst | 0x63);
+       nv_wo32(chan->vm_pd, 0x0004, 0);
 
-       /* VM page directory */
-       ret = nouveau_gpuobj_new_fake(dev, c_vmpd, c_offset + c_vmpd,
-                                     0x4000, 0, &chan->vm_pd);
+       /* DMA object for PRAMIN BAR */
+       ret = nouveau_gpuobj_new(dev, chan, 6*4, 16, 0, &priv->pramin_bar);
        if (ret)
                return ret;
-       for (i = 0; i < 0x4000; i += 8) {
-               BAR0_WI32(chan->vm_pd, i + 0x00, 0x00000000);
-               BAR0_WI32(chan->vm_pd, i + 0x04, 0x00000000);
-       }
-
-       /* PRAMIN page table, cheat and map into VM at 0x0000000000.
-        * We map the entire fake channel into the start of the PRAMIN BAR
-        */
-       ret = nouveau_gpuobj_new(dev, chan, pt_size, 0x1000, 0,
-                                &priv->pramin_pt);
+       nv_wo32(priv->pramin_bar, 0x00, 0x7fc00000);
+       nv_wo32(priv->pramin_bar, 0x04, dev_priv->ramin_size - 1);
+       nv_wo32(priv->pramin_bar, 0x08, 0x00000000);
+       nv_wo32(priv->pramin_bar, 0x0c, 0x00000000);
+       nv_wo32(priv->pramin_bar, 0x10, 0x00000000);
+       nv_wo32(priv->pramin_bar, 0x14, 0x00000000);
+
+       /* map channel into PRAMIN, gpuobj didn't do it for us */
+       ret = nv50_instmem_bind(dev, chan->ramin);
        if (ret)
                return ret;
 
-       v = c_offset | 1;
-       if (dev_priv->vram_sys_base) {
-               v += dev_priv->vram_sys_base;
-               v |= 0x30;
-       }
+       /* poke regs... */
+       nv_wr32(dev, 0x001704, 0x00000000 | (chan->ramin->vinst >> 12));
+       nv_wr32(dev, 0x001704, 0x40000000 | (chan->ramin->vinst >> 12));
+       nv_wr32(dev, 0x00170c, 0x80000000 | (priv->pramin_bar->cinst >> 4));
 
-       i = 0;
-       while (v < dev_priv->vram_sys_base + c_offset + c_size) {
-               BAR0_WI32(priv->pramin_pt, i + 0, lower_32_bits(v));
-               BAR0_WI32(priv->pramin_pt, i + 4, upper_32_bits(v));
-               v += 0x1000;
-               i += 8;
+       tmp = nv_ri32(dev, 0);
+       nv_wi32(dev, 0, ~tmp);
+       if (nv_ri32(dev, 0) != ~tmp) {
+               NV_ERROR(dev, "PRAMIN readback failed\n");
+               return -EIO;
        }
+       nv_wi32(dev, 0, tmp);
 
-       while (i < pt_size) {
-               BAR0_WI32(priv->pramin_pt, i + 0, 0x00000000);
-               BAR0_WI32(priv->pramin_pt, i + 4, 0x00000000);
-               i += 8;
-       }
+       dev_priv->ramin_available = true;
+
+       /* Determine VM layout */
+       dev_priv->vm_gart_base = roundup(NV50_VM_BLOCK, NV50_VM_BLOCK);
+       dev_priv->vm_gart_size = NV50_VM_BLOCK;
+
+       dev_priv->vm_vram_base = dev_priv->vm_gart_base + dev_priv->vm_gart_size;
+       dev_priv->vm_vram_size = dev_priv->vram_size;
+       if (dev_priv->vm_vram_size > NV50_VM_MAX_VRAM)
+               dev_priv->vm_vram_size = NV50_VM_MAX_VRAM;
+       dev_priv->vm_vram_size = roundup(dev_priv->vm_vram_size, NV50_VM_BLOCK);
+       dev_priv->vm_vram_pt_nr = dev_priv->vm_vram_size / NV50_VM_BLOCK;
 
-       BAR0_WI32(chan->vm_pd, 0x00, priv->pramin_pt->vinst | 0x63);
-       BAR0_WI32(chan->vm_pd, 0x04, 0x00000000);
+       dev_priv->vm_end = dev_priv->vm_vram_base + dev_priv->vm_vram_size;
+
+       NV_DEBUG(dev, "NV50VM: GART 0x%016llx-0x%016llx\n",
+                dev_priv->vm_gart_base,
+                dev_priv->vm_gart_base + dev_priv->vm_gart_size - 1);
+       NV_DEBUG(dev, "NV50VM: VRAM 0x%016llx-0x%016llx\n",
+                dev_priv->vm_vram_base,
+                dev_priv->vm_vram_base + dev_priv->vm_vram_size - 1);
 
        /* VRAM page table(s), mapped into VM at +1GiB  */
        for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) {
-               ret = nouveau_gpuobj_new(dev, chan, NV50_VM_BLOCK / 0x10000 * 8,
-                                        0, 0, &chan->vm_vram_pt[i]);
+               ret = nouveau_gpuobj_new(dev, NULL, NV50_VM_BLOCK / 0x10000 * 8,
+                                        0, NVOBJ_FLAG_ZERO_ALLOC,
+                                        &chan->vm_vram_pt[i]);
                if (ret) {
-                       NV_ERROR(dev, "Error creating VRAM page tables: %d\n",
-                                                                       ret);
+                       NV_ERROR(dev, "Error creating VRAM PGT: %d\n", ret);
                        dev_priv->vm_vram_pt_nr = i;
                        return ret;
                }
-               /*XXX: double-check this is ok */
                dev_priv->vm_vram_pt[i] = chan->vm_vram_pt[i];
 
-               for (v = 0; v < dev_priv->vm_vram_pt[i]->size; v += 4)
-                       BAR0_WI32(dev_priv->vm_vram_pt[i], v, 0);
-
-               BAR0_WI32(chan->vm_pd, 0x10 + (i*8),
-                         chan->vm_vram_pt[i]->vinst | 0x61);
-               BAR0_WI32(chan->vm_pd, 0x14 + (i*8), 0);
+               nv_wo32(chan->vm_pd, 0x10 + (i*8),
+                       chan->vm_vram_pt[i]->vinst | 0x61);
+               nv_wo32(chan->vm_pd, 0x14 + (i*8), 0);
        }
 
-       /* DMA object for PRAMIN BAR */
-       ret = nouveau_gpuobj_new(dev, chan, 6*4, 16, 0, &priv->pramin_bar);
-       if (ret)
-               return ret;
-       BAR0_WI32(priv->pramin_bar, 0x00, 0x7fc00000);
-       BAR0_WI32(priv->pramin_bar, 0x04, dev_priv->ramin_size - 1);
-       BAR0_WI32(priv->pramin_bar, 0x08, 0x00000000);
-       BAR0_WI32(priv->pramin_bar, 0x0c, 0x00000000);
-       BAR0_WI32(priv->pramin_bar, 0x10, 0x00000000);
-       BAR0_WI32(priv->pramin_bar, 0x14, 0x00000000);
-
        /* DMA object for FB BAR */
        ret = nouveau_gpuobj_new(dev, chan, 6*4, 16, 0, &priv->fb_bar);
        if (ret)
                return ret;
-       BAR0_WI32(priv->fb_bar, 0x00, 0x7fc00000);
-       BAR0_WI32(priv->fb_bar, 0x04, 0x40000000 +
-                                     pci_resource_len(dev->pdev, 1) - 1);
-       BAR0_WI32(priv->fb_bar, 0x08, 0x40000000);
-       BAR0_WI32(priv->fb_bar, 0x0c, 0x00000000);
-       BAR0_WI32(priv->fb_bar, 0x10, 0x00000000);
-       BAR0_WI32(priv->fb_bar, 0x14, 0x00000000);
-
-       /* Poke the relevant regs, and pray it works :) */
-       nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->vinst >> 12));
-       nv_wr32(dev, NV50_PUNK_UNK1710, 0);
-       nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->vinst >> 12) |
-                                        NV50_PUNK_BAR_CFG_BASE_VALID);
-       nv_wr32(dev, NV50_PUNK_BAR1_CTXDMA, (priv->fb_bar->cinst >> 4) |
-                                       NV50_PUNK_BAR1_CTXDMA_VALID);
-       nv_wr32(dev, NV50_PUNK_BAR3_CTXDMA, (priv->pramin_bar->cinst >> 4) |
-                                       NV50_PUNK_BAR3_CTXDMA_VALID);
-
+       nv_wo32(priv->fb_bar, 0x00, 0x7fc00000);
+       nv_wo32(priv->fb_bar, 0x04, 0x40000000 +
+                                   pci_resource_len(dev->pdev, 1) - 1);
+       nv_wo32(priv->fb_bar, 0x08, 0x40000000);
+       nv_wo32(priv->fb_bar, 0x0c, 0x00000000);
+       nv_wo32(priv->fb_bar, 0x10, 0x00000000);
+       nv_wo32(priv->fb_bar, 0x14, 0x00000000);
+
+       nv_wr32(dev, 0x001708, 0x80000000 | (priv->fb_bar->cinst >> 4));
        for (i = 0; i < 8; i++)
                nv_wr32(dev, 0x1900 + (i*4), 0);
 
-       dev_priv->ramin_available = true;
-
-       /* Assume that praying isn't enough, check that we can re-read the
-        * entire fake channel back from the PRAMIN BAR */
-       for (i = 0; i < c_size; i += 4) {
-               if (nv_rd32(dev, NV_RAMIN + i) != nv_ri32(dev, i)) {
-                       NV_ERROR(dev, "Error reading back PRAMIN at 0x%08x\n",
-                                                                       i);
-                       return -EINVAL;
-               }
-       }
-
-       nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, save_nv001700);
-
-       /* Global PRAMIN heap */
-       if (drm_mm_init(&dev_priv->ramin_heap, c_size, dev_priv->ramin_size - c_size)) {
-               NV_ERROR(dev, "Failed to init RAMIN heap\n");
-       }
-
        /*XXX: incorrect, but needed to make hash func "work" */
        dev_priv->ramht_offset = 0x10000;
        dev_priv->ramht_bits   = 9;
@@ -288,6 +250,8 @@ nv50_instmem_takedown(struct drm_device *dev)
        if (!priv)
                return;
 
+       dev_priv->ramin_available = false;
+
        /* Restore state from before init */
        for (i = 0x1700; i <= 0x1710; i += 4)
                nv_wr32(dev, i, priv->save1700[(i - 0x1700) / 4]);
@@ -302,13 +266,8 @@ nv50_instmem_takedown(struct drm_device *dev)
                        nouveau_gpuobj_ref(NULL, &chan->vm_vram_pt[i]);
                dev_priv->vm_vram_pt_nr = 0;
 
-               nouveau_gpuobj_ref(NULL, &chan->vm_pd);
-               nouveau_gpuobj_ref(NULL, &chan->ramfc);
-               nouveau_gpuobj_ref(NULL, &chan->ramin);
-               drm_mm_takedown(&chan->ramin_heap);
-
-               dev_priv->fifos[0] = dev_priv->fifos[127] = NULL;
-               kfree(chan);
+               nv50_channel_del(&dev_priv->fifos[0]);
+               dev_priv->fifos[127] = NULL;
        }
 
        dev_priv->engine.instmem.priv = NULL;
@@ -341,9 +300,11 @@ nv50_instmem_resume(struct drm_device *dev)
        struct nouveau_gpuobj *ramin = chan->ramin;
        int i;
 
-       nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, (ramin->vinst >> 16));
+       dev_priv->ramin_available = false;
+       dev_priv->ramin_base = ~0;
        for (i = 0; i < ramin->size; i += 4)
-               BAR0_WI32(ramin, i, ramin->im_backing_suspend[i/4]);
+               nv_wo32(ramin, i, ramin->im_backing_suspend[i/4]);
+       dev_priv->ramin_available = true;
        vfree(ramin->im_backing_suspend);
        ramin->im_backing_suspend = NULL;
 
@@ -370,7 +331,7 @@ nv50_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj,
        if (gpuobj->im_backing)
                return -EINVAL;
 
-       *sz = ALIGN(*sz, NV50_INSTMEM_PAGE_SIZE);
+       *sz = ALIGN(*sz, 4096);
        if (*sz == 0)
                return -EINVAL;
 
@@ -438,7 +399,7 @@ nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
        while (pte < pte_end) {
                nv_wo32(pramin_pt, (pte * 4) + 0, lower_32_bits(vram));
                nv_wo32(pramin_pt, (pte * 4) + 4, upper_32_bits(vram));
-               vram += NV50_INSTMEM_PAGE_SIZE;
+               vram += 0x1000;
                pte += 2;
        }
        dev_priv->engine.instmem.flush(dev);
@@ -460,6 +421,10 @@ nv50_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
        if (gpuobj->im_bound == 0)
                return -EINVAL;
 
+       /* can happen during late takedown */
+       if (unlikely(!dev_priv->ramin_available))
+               return 0;
+
        pte     = (gpuobj->im_pramin->start >> 12) << 1;
        pte_end = ((gpuobj->im_pramin->size >> 12) << 1) + pte;