|
@@ -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 drm_nouveau_private *dev_priv = dev->dev_private;
|
|
struct nouveau_engine *engine = &dev_priv->engine;
|
|
struct nouveau_engine *engine = &dev_priv->engine;
|
|
struct nouveau_gpuobj *gpuobj;
|
|
struct nouveau_gpuobj *gpuobj;
|
|
- struct drm_mm *pramin = NULL;
|
|
|
|
|
|
+ struct drm_mm_node *ramin = NULL;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
NV_DEBUG(dev, "ch%d size=%u align=%d flags=0x%08x\n",
|
|
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);
|
|
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) {
|
|
if (chan) {
|
|
NV_DEBUG(dev, "channel heap\n");
|
|
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 {
|
|
} else {
|
|
NV_DEBUG(dev, "global heap\n");
|
|
NV_DEBUG(dev, "global heap\n");
|
|
- pramin = &dev_priv->ramin_heap;
|
|
|
|
|
|
|
|
|
|
+ /* allocate backing pages, sets vinst */
|
|
ret = engine->instmem.populate(dev, gpuobj, &size);
|
|
ret = engine->instmem.populate(dev, gpuobj, &size);
|
|
if (ret) {
|
|
if (ret) {
|
|
nouveau_gpuobj_ref(NULL, &gpuobj);
|
|
nouveau_gpuobj_ref(NULL, &gpuobj);
|
|
return ret;
|
|
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);
|
|
ret = engine->instmem.bind(dev, gpuobj);
|
|
if (ret) {
|
|
if (ret) {
|
|
nouveau_gpuobj_ref(NULL, &gpuobj);
|
|
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 */
|
|
/* calculate the various different addresses for the object */
|
|
if (chan) {
|
|
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) {
|
|
if (dev_priv->card_type < NV_50) {
|
|
gpuobj->cinst = gpuobj->pinst;
|
|
gpuobj->cinst = gpuobj->pinst;
|
|
} else {
|
|
} else {
|
|
@@ -143,7 +152,10 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan,
|
|
chan->ramin->vinst;
|
|
chan->ramin->vinst;
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
- gpuobj->pinst = gpuobj->im_pramin->start;
|
|
|
|
|
|
+ if (gpuobj->im_pramin)
|
|
|
|
+ gpuobj->pinst = gpuobj->im_pramin->start;
|
|
|
|
+ else
|
|
|
|
+ gpuobj->pinst = ~0;
|
|
gpuobj->cinst = 0xdeadbeef;
|
|
gpuobj->cinst = 0xdeadbeef;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -168,6 +180,8 @@ nouveau_gpuobj_early_init(struct drm_device *dev)
|
|
NV_DEBUG(dev, "\n");
|
|
NV_DEBUG(dev, "\n");
|
|
|
|
|
|
INIT_LIST_HEAD(&dev_priv->gpuobj_list);
|
|
INIT_LIST_HEAD(&dev_priv->gpuobj_list);
|
|
|
|
+ spin_lock_init(&dev_priv->ramin_lock);
|
|
|
|
+ dev_priv->ramin_base = ~0;
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -650,12 +664,15 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
|
|
* locations determined during init.
|
|
* locations determined during init.
|
|
*/
|
|
*/
|
|
if (dev_priv->card_type >= NV_50) {
|
|
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);
|
|
0, &chan->vm_pd);
|
|
if (ret)
|
|
if (ret)
|
|
return ret;
|
|
return ret;
|
|
@@ -941,11 +958,46 @@ int nouveau_ioctl_gpuobj_free(struct drm_device *dev, void *data,
|
|
u32
|
|
u32
|
|
nv_ro32(struct nouveau_gpuobj *gpuobj, u32 offset)
|
|
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
|
|
void
|
|
nv_wo32(struct nouveau_gpuobj *gpuobj, u32 offset, u32 val)
|
|
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);
|
|
}
|
|
}
|