drm/nvc0/vram: support non-uniform memory size per controller
authorBen Skeggs <bskeggs@redhat.com>
Fri, 24 Jun 2011 02:47:46 +0000 (12:47 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Tue, 20 Sep 2011 06:04:11 +0000 (16:04 +1000)
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nvc0_vram.c

index abed0d3d579223678a46e1d5c01ab6dabaac1323..edbfe9360ae2f0a2703176c0ea1fcd1dfeed83a0 100644 (file)
@@ -106,12 +106,50 @@ nvc0_vram_init(struct drm_device *dev)
        struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
        const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
        const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
-       u32 length;
+       u32 parts = nv_rd32(dev, 0x121c74);
+       u32 bsize = nv_rd32(dev, 0x10f20c);
+       u32 offset, length;
+       bool uniform = true;
+       int ret, i;
 
-       dev_priv->vram_size  = nv_rd32(dev, 0x10f20c) << 20;
-       dev_priv->vram_size *= nv_rd32(dev, 0x121c74);
+       NV_DEBUG(dev, "0x100800: 0x%08x\n", nv_rd32(dev, 0x100800));
+       NV_DEBUG(dev, "parts 0x%08x bcast_mem_amount 0x%08x\n", parts, bsize);
 
-       length = (dev_priv->vram_size >> 12) - rsvd_head - rsvd_tail;
+       /* read amount of vram attached to each memory controller */
+       for (i = 0; i < parts; i++) {
+               u32 psize = nv_rd32(dev, 0x11020c + (i * 0x1000));
+               if (psize != bsize) {
+                       if (psize < bsize)
+                               bsize = psize;
+                       uniform = false;
+               }
+
+               NV_DEBUG(dev, "%d: mem_amount 0x%08x\n", i, psize);
+
+               dev_priv->vram_size += (u64)psize << 20;
+       }
+
+       /* if all controllers have the same amount attached, there's no holes */
+       if (uniform) {
+               offset = rsvd_head;
+               length = (dev_priv->vram_size >> 12) - rsvd_head - rsvd_tail;
+               return nouveau_mm_init(&vram->mm, offset, length, 1);
+       }
 
-       return nouveau_mm_init(&vram->mm, rsvd_head, length, 1);
+       /* otherwise, address lowest common amount from 0GiB */
+       ret = nouveau_mm_init(&vram->mm, rsvd_head, (bsize << 8) * parts, 1);
+       if (ret)
+               return ret;
+
+       /* and the rest starting from (8GiB + common_size) */
+       offset = (0x0200000000ULL >> 12) + (bsize << 8);
+       length = (dev_priv->vram_size >> 12) - (bsize << 8) - rsvd_tail;
+
+       ret = nouveau_mm_init(&vram->mm, offset, length, 0);
+       if (ret) {
+               nouveau_mm_fini(&vram->mm);
+               return ret;
+       }
+
+       return 0;
 }