diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2010-09-01 15:24:33 +1000 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2010-09-24 16:20:28 +1000 |
commit | 5125bfd88608012d58652ac7ea6a03a78773200f (patch) | |
tree | 8ccb36c6a023392e3c8a9f2c4e84c74ccdcaeb44 /drivers/gpu/drm/nouveau/nouveau_object.c | |
parent | 43efc9ce25c6956133c07394a6fa44ef2c9268a4 (diff) |
drm/nv50: allow gpuobjs that aren't mapped into aperture
Reviewed-by: Francisco Jerez <currojerez@riseup.net>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_object.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_object.c | 102 |
1 files changed, 77 insertions, 25 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c index 4bcea11f54e..df445fcb832 100644 --- a/drivers/gpu/drm/nouveau/nouveau_object.c +++ b/drivers/gpu/drm/nouveau/nouveau_object.c @@ -75,7 +75,7 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan, struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_engine *engine = &dev_priv->engine; struct nouveau_gpuobj *gpuobj; - struct drm_mm *pramin = NULL; + struct drm_mm_node *ramin = NULL; int ret; NV_DEBUG(dev, "ch%d size=%u align=%d flags=0x%08x\n", @@ -95,36 +95,42 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan, list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list); - /* Choose between global instmem heap, and per-channel private - * instmem heap. On <NV50 allow requests for private instmem - * to be satisfied from global heap if no per-channel area - * available. - */ if (chan) { NV_DEBUG(dev, "channel heap\n"); - pramin = &chan->ramin_heap; + + ramin = drm_mm_search_free(&chan->ramin_heap, size, align, 0); + if (ramin) + ramin = drm_mm_get_block(ramin, size, align); + + if (!ramin) { + nouveau_gpuobj_ref(NULL, &gpuobj); + return -ENOMEM; + } } else { NV_DEBUG(dev, "global heap\n"); - pramin = &dev_priv->ramin_heap; + /* allocate backing pages, sets vinst */ ret = engine->instmem.populate(dev, gpuobj, &size); if (ret) { nouveau_gpuobj_ref(NULL, &gpuobj); return ret; } - } - /* Allocate a chunk of the PRAMIN aperture */ - gpuobj->im_pramin = drm_mm_search_free(pramin, size, align, 0); - if (gpuobj->im_pramin) - gpuobj->im_pramin = drm_mm_get_block(gpuobj->im_pramin, size, align); + /* try and get aperture space */ + ramin = drm_mm_search_free(&dev_priv->ramin_heap, size, align, 0); + if (ramin) + ramin = drm_mm_get_block(ramin, size, align); - if (!gpuobj->im_pramin) { - nouveau_gpuobj_ref(NULL, &gpuobj); - return -ENOMEM; + /* on nv50 it's ok to fail, we have a fallback path */ + if (!ramin && dev_priv->card_type < NV_50) { + nouveau_gpuobj_ref(NULL, &gpuobj); + return -ENOMEM; + } } - if (!chan) { + /* if we got a chunk of the aperture, map pages into it */ + gpuobj->im_pramin = ramin; + if (!chan && gpuobj->im_pramin) { ret = engine->instmem.bind(dev, gpuobj); if (ret) { nouveau_gpuobj_ref(NULL, &gpuobj); @@ -134,7 +140,10 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan, /* calculate the various different addresses for the object */ if (chan) { - gpuobj->pinst = gpuobj->im_pramin->start + chan->ramin->pinst; + gpuobj->pinst = chan->ramin->pinst; + if (gpuobj->pinst != ~0) + gpuobj->pinst += gpuobj->im_pramin->start; + if (dev_priv->card_type < NV_50) { gpuobj->cinst = gpuobj->pinst; } else { @@ -143,7 +152,10 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan, chan->ramin->vinst; } } else { - gpuobj->pinst = gpuobj->im_pramin->start; + if (gpuobj->im_pramin) + gpuobj->pinst = gpuobj->im_pramin->start; + else + gpuobj->pinst = ~0; gpuobj->cinst = 0xdeadbeef; } @@ -168,6 +180,8 @@ nouveau_gpuobj_early_init(struct drm_device *dev) NV_DEBUG(dev, "\n"); INIT_LIST_HEAD(&dev_priv->gpuobj_list); + spin_lock_init(&dev_priv->ramin_lock); + dev_priv->ramin_base = ~0; return 0; } @@ -650,12 +664,15 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan, * locations determined during init. */ if (dev_priv->card_type >= NV_50) { - uint32_t vm_offset, pde; + u32 pgd_offs = (dev_priv->chipset == 0x50) ? 0x1400 : 0x0200; + u64 vm_vinst = chan->ramin->vinst + pgd_offs; + u32 vm_pinst = chan->ramin->pinst; + u32 pde; - vm_offset = (dev_priv->chipset & 0xf0) == 0x50 ? 0x1400 : 0x200; - vm_offset += chan->ramin->im_pramin->start; + if (vm_pinst != ~0) + vm_pinst += pgd_offs; - ret = nouveau_gpuobj_new_fake(dev, vm_offset, ~0, 0x4000, + ret = nouveau_gpuobj_new_fake(dev, vm_pinst, vm_vinst, 0x4000, 0, &chan->vm_pd); if (ret) return ret; @@ -941,11 +958,46 @@ int nouveau_ioctl_gpuobj_free(struct drm_device *dev, void *data, u32 nv_ro32(struct nouveau_gpuobj *gpuobj, u32 offset) { - return nv_ri32(gpuobj->dev, gpuobj->pinst + offset); + struct drm_nouveau_private *dev_priv = gpuobj->dev->dev_private; + struct drm_device *dev = gpuobj->dev; + + if (gpuobj->pinst == ~0 || !dev_priv->ramin_available) { + u64 ptr = gpuobj->vinst + offset; + u32 base = ptr >> 16; + u32 val; + + spin_lock(&dev_priv->ramin_lock); + if (dev_priv->ramin_base != base) { + dev_priv->ramin_base = base; + nv_wr32(dev, 0x001700, dev_priv->ramin_base); + } + val = nv_rd32(dev, 0x700000 + (ptr & 0xffff)); + spin_unlock(&dev_priv->ramin_lock); + return val; + } + + return nv_ri32(dev, gpuobj->pinst + offset); } void nv_wo32(struct nouveau_gpuobj *gpuobj, u32 offset, u32 val) { - nv_wi32(gpuobj->dev, gpuobj->pinst + offset, val); + struct drm_nouveau_private *dev_priv = gpuobj->dev->dev_private; + struct drm_device *dev = gpuobj->dev; + + if (gpuobj->pinst == ~0 || !dev_priv->ramin_available) { + u64 ptr = gpuobj->vinst + offset; + u32 base = ptr >> 16; + + spin_lock(&dev_priv->ramin_lock); + if (dev_priv->ramin_base != base) { + dev_priv->ramin_base = base; + nv_wr32(dev, 0x001700, dev_priv->ramin_base); + } + nv_wr32(dev, 0x700000 + (ptr & 0xffff), val); + spin_unlock(&dev_priv->ramin_lock); + return; + } + + nv_wi32(dev, gpuobj->pinst + offset, val); } |