drm/nv50: fix iommu errors caused by device reading from address 0
[safe/jmp/linux-2.6] / drivers / gpu / drm / nouveau / nv50_fifo.c
index 77ae1aa..e20c0e2 100644 (file)
@@ -243,6 +243,7 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
        struct drm_device *dev = chan->dev;
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        struct nouveau_gpuobj *ramfc = NULL;
+       unsigned long flags;
        int ret;
 
        NV_DEBUG(dev, "ch%d\n", chan->id);
@@ -272,25 +273,27 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
                        return ret;
                ramfc = chan->ramfc->gpuobj;
 
-               ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 4096, 256,
+               ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 4096, 1024,
                                             0, &chan->cache);
                if (ret)
                        return ret;
        }
 
+       spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+
        dev_priv->engine.instmem.prepare_access(dev, true);
 
-       nv_wo32(dev, ramfc, 0x08/4, chan->pushbuf_base);
-       nv_wo32(dev, ramfc, 0x10/4, chan->pushbuf_base);
        nv_wo32(dev, ramfc, 0x48/4, chan->pushbuf->instance >> 4);
        nv_wo32(dev, ramfc, 0x80/4, (0xc << 24) | (chan->ramht->instance >> 4));
-       nv_wo32(dev, ramfc, 0x3c/4, 0x00086078);
        nv_wo32(dev, ramfc, 0x44/4, 0x2101ffff);
        nv_wo32(dev, ramfc, 0x60/4, 0x7fffffff);
        nv_wo32(dev, ramfc, 0x40/4, 0x00000000);
        nv_wo32(dev, ramfc, 0x7c/4, 0x30000001);
        nv_wo32(dev, ramfc, 0x78/4, 0x00000000);
-       nv_wo32(dev, ramfc, 0x4c/4, 0xffffffff);
+       nv_wo32(dev, ramfc, 0x3c/4, 0x403f6078);
+       nv_wo32(dev, ramfc, 0x50/4, chan->pushbuf_base +
+                                   chan->dma.ib_base * 4);
+       nv_wo32(dev, ramfc, 0x54/4, drm_order(chan->dma.ib_max + 1) << 16);
 
        if (!IS_G80) {
                nv_wo32(dev, chan->ramin->gpuobj, 0, chan->id);
@@ -306,10 +309,12 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
        ret = nv50_fifo_channel_enable(dev, chan->id, false);
        if (ret) {
                NV_ERROR(dev, "error enabling ch%d: %d\n", chan->id, ret);
+               spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
                nouveau_gpuobj_ref_del(dev, &chan->ramfc);
                return ret;
        }
 
+       spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
        return 0;
 }
 
@@ -317,17 +322,20 @@ void
 nv50_fifo_destroy_context(struct nouveau_channel *chan)
 {
        struct drm_device *dev = chan->dev;
+       struct nouveau_gpuobj_ref *ramfc = chan->ramfc;
 
        NV_DEBUG(dev, "ch%d\n", chan->id);
 
-       nouveau_gpuobj_ref_del(dev, &chan->ramfc);
-       nouveau_gpuobj_ref_del(dev, &chan->cache);
-
+       /* This will ensure the channel is seen as disabled. */
+       chan->ramfc = NULL;
        nv50_fifo_channel_disable(dev, chan->id, false);
 
        /* Dummy channel, also used on ch 127 */
        if (chan->id == 0)
                nv50_fifo_channel_disable(dev, 127, false);
+
+       nouveau_gpuobj_ref_del(dev, &ramfc);
+       nouveau_gpuobj_ref_del(dev, &chan->cache);
 }
 
 int
@@ -384,8 +392,8 @@ nv50_fifo_load_context(struct nouveau_channel *chan)
                nv_wr32(dev, NV40_PFIFO_CACHE1_DATA(ptr),
                        nv_ro32(dev, cache, (ptr * 2) + 1));
        }
-       nv_wr32(dev, 0x3210, cnt << 2);
-       nv_wr32(dev, 0x3270, 0);
+       nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, cnt << 2);
+       nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
 
        /* guessing that all the 0x34xx regs aren't on NV50 */
        if (!IS_G80) {
@@ -398,8 +406,6 @@ nv50_fifo_load_context(struct nouveau_channel *chan)
 
        dev_priv->engine.instmem.finish_access(dev);
 
-       nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
-       nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
        nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, chan->id | (1<<16));
        return 0;
 }
@@ -416,7 +422,7 @@ nv50_fifo_unload_context(struct drm_device *dev)
        NV_DEBUG(dev, "\n");
 
        chid = pfifo->channel_id(dev);
-       if (chid < 0 || chid >= dev_priv->engine.fifo.channels)
+       if (chid < 1 || chid >= dev_priv->engine.fifo.channels - 1)
                return 0;
 
        chan = dev_priv->fifos[chid];