*/
        nouveau_fence_fini(chan);
 
-       /* Ensure the channel is no longer active on the GPU */
+       /* This will prevent pfifo from switching channels. */
        pfifo->reassign(dev, false);
 
+       /* We want to give pgraph a chance to idle and get rid of all potential
+        * errors. We need to do this before the lock, otherwise the irq handler
+        * is unable to process them.
+        */
+       if (pgraph->channel(dev) == chan)
+               nouveau_wait_for_idle(dev);
+
+       spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+
        pgraph->fifo_access(dev, false);
        if (pgraph->channel(dev) == chan)
                pgraph->unload_context(dev);
 
        pfifo->reassign(dev, true);
 
+       spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+
        /* Release the channel's resources */
        nouveau_gpuobj_ref_del(dev, &chan->pushbuf);
        if (chan->pushbuf_bo) {
 
        struct nouveau_engine engine;
        struct nouveau_channel *channel;
 
+       /* For PFIFO and PGRAPH. */
+       spinlock_t context_switch_lock;
+
        /* RAMIN configuration, RAMFC, RAMHT and RAMRO offsets */
        struct nouveau_gpuobj *ramht;
        uint32_t ramin_rsvd_vram;
 
        struct drm_device *dev = (struct drm_device *)arg;
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        uint32_t status, fbdev_flags = 0;
+       unsigned long flags;
 
        status = nv_rd32(dev, NV03_PMC_INTR_0);
        if (!status)
                return IRQ_NONE;
 
+       spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+
        if (dev_priv->fbdev_info) {
                fbdev_flags = dev_priv->fbdev_info->flags;
                dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
        if (dev_priv->fbdev_info)
                dev_priv->fbdev_info->flags = fbdev_flags;
 
+       spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+
        return IRQ_HANDLED;
 }
 
                goto out;
        engine = &dev_priv->engine;
        dev_priv->init_state = NOUVEAU_CARD_INIT_FAILED;
+       spin_lock_init(&dev_priv->context_switch_lock);
 
        /* Parse BIOS tables / Run init tables if card not POSTed */
        if (drm_core_check_feature(dev, DRIVER_MODESET)) {
 
 {
        struct drm_device *dev = chan->dev;
        struct drm_nouveau_private *dev_priv = dev->dev_private;
+       unsigned long flags;
        int ret;
 
        ret = nouveau_gpuobj_new_fake(dev, NV04_RAMFC(chan->id), ~0,
        if (ret)
                return ret;
 
+       spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+
        /* Setup initial state */
        dev_priv->engine.instmem.prepare_access(dev, true);
        RAMFC_WR(DMA_PUT, chan->pushbuf_base);
        /* enable the fifo dma operation */
        nv_wr32(dev, NV04_PFIFO_MODE,
                nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
+
+       spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
        return 0;
 }
 
 
        struct drm_device *dev = chan->dev;
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        uint32_t fc = NV40_RAMFC(chan->id);
+       unsigned long flags;
        int ret;
 
        ret = nouveau_gpuobj_new_fake(dev, NV40_RAMFC(chan->id), ~0,
        if (ret)
                return ret;
 
+       spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+
        dev_priv->engine.instmem.prepare_access(dev, true);
        nv_wi32(dev, fc +  0, chan->pushbuf_base);
        nv_wi32(dev, fc +  4, chan->pushbuf_base);
        /* enable the fifo dma operation */
        nv_wr32(dev, NV04_PFIFO_MODE,
                nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
+
+       spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
        return 0;
 }
 
 
        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);
                        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);
        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;
 }