Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

nouveau: patch against Linux v3.5-rc5 #4

Closed
wants to merge 1 commit into from

2 participants

@kris7t

With this new patch I could get the driver and gdev compile and load.

  • Some changes in gdev-nouveau.patch were already in the kernel tree, so I removed them completely.
  • Primary indexes seem to start from 1 not 0, and this prevented gdev from initializing the cards properly.

The patch is against the tag v3.5-rc5 from @torvalds' tree, but should apply to any recent one, I think.

@kris7t kris7t nouveau: patch against Linux v3.5-rc5
With this new patch I could get the driver and gdev compile and load.

However, running a CUDA application causes 100% CPU load and an
uninterruptable thread. That is possibly caused by some locking in gdev
incompatible with the newest nouveau drivers.
e388f30
@kris7t

Forget about this. There may be some serious bugs that cause 100% cpu utilization and messages like

Jul  8 14:10:40 localhost kernel: [ 5156.711545] [drm] nouveau 0000:01:00.0: PFIFO: read fault at 0x0000000000 [PT_NOT_PRESENT] from PGRAPH/CTXCTL on channel 0x000011a000
Jul  8 14:10:40 localhost kernel: [ 5156.711562] [drm] nouveau 0000:01:00.0: PFIFO: unknown status 0x40000000

when trying to run an application using CUDA.

@kris7t kris7t closed this
@shinpei0208
Owner

Okay. If you get a new patch working, however, please send it to me :D

Shinpei

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 8, 2012
  1. @kris7t

    nouveau: patch against Linux v3.5-rc5

    kris7t authored
    With this new patch I could get the driver and gdev compile and load.
    
    However, running a CUDA application causes 100% CPU load and an
    uninterruptable thread. That is possibly caused by some locking in gdev
    incompatible with the newest nouveau drivers.
This page is out of date. Refresh to see the latest.
Showing with 644 additions and 0 deletions.
  1. +644 −0 mod/patches/gdev-nouveau-3.5.patch
View
644 mod/patches/gdev-nouveau-3.5.patch
@@ -0,0 +1,644 @@
+diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
+index fe5267d..869f4fe 100644
+--- a/drivers/gpu/drm/nouveau/Makefile
++++ b/drivers/gpu/drm/nouveau/Makefile
+@@ -3,7 +3,8 @@
+ # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ ccflags-y := -Iinclude/drm
+-nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
++nouveau-y := gdev_interface.o \
++ nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
+ nouveau_object.o nouveau_irq.o nouveau_notifier.o \
+ nouveau_sgdma.o nouveau_dma.o nouveau_util.o \
+ nouveau_bo.o nouveau_fence.o nouveau_gem.o nouveau_ttm.o \
+diff --git a/drivers/gpu/drm/nouveau/gdev_interface.c b/drivers/gpu/drm/nouveau/gdev_interface.c
+new file mode 100644
+index 0000000..6a61da8
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/gdev_interface.c
+@@ -0,0 +1,456 @@
++#include <linux/module.h>
++#include "gdev_interface.h"
++#include "nouveau_drv.h"
++#include "nvc0_graph.h"
++
++#define VS_START 0x20000000
++#define VS_END (1ull << 40)
++
++extern int nouveau_device_count;
++extern struct drm_device **nouveau_drm;
++extern void (*nouveau_callback_notify)(int subc, uint32_t data);
++
++int gdev_drv_vspace_alloc(struct drm_device *drm, uint64_t size, struct gdev_drv_vspace *drv_vspace)
++{
++ struct nouveau_channel *chan;
++
++ if (nouveau_channel_alloc(drm, &chan, NULL, 0xbeef0201, 0xbeef0202)) {
++ printk("Failed to allocate nouveau channel\n");
++ return -ENOMEM;
++ }
++
++ drv_vspace->priv = (void *)chan;
++
++ return 0;
++}
++EXPORT_SYMBOL(gdev_drv_vspace_alloc);
++
++int gdev_drv_vspace_free(struct gdev_drv_vspace *drv_vspace)
++{
++ struct nouveau_channel *chan = (struct nouveau_channel *)drv_vspace->priv;
++
++ nouveau_channel_ref(NULL, &chan);
++
++ return 0;
++}
++EXPORT_SYMBOL(gdev_drv_vspace_free);
++
++int gdev_drv_chan_alloc(struct drm_device *drm, struct gdev_drv_vspace *drv_vspace, struct gdev_drv_chan *drv_chan)
++{
++ struct drm_nouveau_private *dev_priv = drm->dev_private;
++ struct nouveau_channel *chan = (struct nouveau_channel *)drv_vspace->priv;
++ struct nouveau_bo *ib_bo, *pb_bo;
++ uint32_t cid;
++ volatile uint32_t *regs;
++ uint32_t *ib_map, *pb_map;
++ uint32_t ib_order, pb_order;
++ uint64_t ib_base, pb_base;
++ uint32_t ib_mask, pb_mask;
++ uint32_t pb_size;
++ int ret;
++
++ /* channel ID. */
++ cid = chan->id;
++
++ /* FIFO push buffer setup. */
++ pb_order = 15; /* it's hardcoded. pscnv uses 20, nouveau uses 15. */
++ pb_bo = chan->pushbuf_bo;
++ pb_base = chan->pushbuf_vma.offset;
++ pb_map = chan->pushbuf_bo->kmap.virtual;
++ pb_mask = (1 << pb_order) - 1;
++ pb_size = (1 << pb_order);
++ if (chan->pushbuf_bo->bo.mem.size / 2 != pb_size)
++ printk("Pushbuf size mismatched!\n");
++
++ /* FIFO indirect buffer setup. */
++ ib_order = 12; /* it's hardcoded. pscnv uses 9, nouveau uses 12*/
++ ib_bo = NULL;
++ ib_base = pb_base + pb_size;
++ ib_map = (void *)((unsigned long)pb_bo->kmap.virtual + pb_size);
++ ib_mask = (1 << ib_order) - 1;
++
++ /* FIFO init: it has already been done in gdev_vas_new(). */
++
++ switch (dev_priv->chipset & 0xf0) {
++ case 0xc0:
++ /* FIFO command queue registers. */
++ regs = chan->user;
++ /* PCOPY engines. */
++ ret = dev_priv->eng[NVOBJ_ENGINE_COPY0]->context_new(chan, NVOBJ_ENGINE_COPY0);
++ if (ret)
++ goto fail_pcopy0;
++ ret = dev_priv->eng[NVOBJ_ENGINE_COPY1]->context_new(chan, NVOBJ_ENGINE_COPY1);
++ if (ret)
++ goto fail_pcopy1;
++ break;
++ default:
++ ret = -EINVAL;
++ goto fail_fifo_reg;
++ }
++
++ drv_chan->priv = chan;
++ drv_chan->cid = cid;
++ drv_chan->regs = regs;
++ drv_chan->ib_bo = ib_bo;
++ drv_chan->ib_map = ib_map;
++ drv_chan->ib_order = ib_order;
++ drv_chan->ib_base = ib_base;
++ drv_chan->ib_mask = ib_mask;
++ drv_chan->pb_bo = pb_bo;
++ drv_chan->pb_map = pb_map;
++ drv_chan->pb_order = pb_order;
++ drv_chan->pb_base = pb_base;
++ drv_chan->pb_mask = pb_mask;
++ drv_chan->pb_size = pb_size;
++
++ return 0;
++
++fail_fifo_reg:
++fail_pcopy1:
++ dev_priv->eng[NVOBJ_ENGINE_COPY0]->context_del(chan, NVOBJ_ENGINE_COPY0);
++fail_pcopy0:
++ return ret;
++}
++EXPORT_SYMBOL(gdev_drv_chan_alloc);
++
++int gdev_drv_chan_free(struct gdev_drv_vspace *drv_vspace, struct gdev_drv_chan *drv_chan)
++{
++ struct nouveau_channel *chan = (struct nouveau_channel *)drv_vspace->priv;
++ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
++
++ dev_priv->eng[NVOBJ_ENGINE_COPY1]->context_del(chan, NVOBJ_ENGINE_COPY1);
++ dev_priv->eng[NVOBJ_ENGINE_COPY0]->context_del(chan, NVOBJ_ENGINE_COPY0);
++
++ return 0;
++}
++EXPORT_SYMBOL(gdev_drv_chan_free);
++
++int gdev_drv_bo_alloc(struct drm_device *drm, uint64_t size, uint32_t drv_flags, struct gdev_drv_vspace *drv_vspace, struct gdev_drv_bo *drv_bo)
++{
++ struct drm_nouveau_private *dev_priv = drm->dev_private;
++ struct nouveau_channel *chan = (struct nouveau_channel *)drv_vspace->priv;
++ struct nouveau_bo *bo;
++ struct nouveau_vma *vma;
++ uint32_t flags = 0;
++ int ret;
++
++ /* set memory type. */
++ if (drv_flags & GDEV_DRV_BO_VRAM) {
++ flags |= TTM_PL_FLAG_VRAM;
++ }
++ if (drv_flags & GDEV_DRV_BO_SYSRAM) {
++ flags |= TTM_PL_FLAG_TT;
++ }
++
++ ret = nouveau_bo_new(drm, size, 0, flags, 0, 0, NULL, &bo);
++ if (ret)
++ goto fail_bo_new;
++
++ if (drv_flags & GDEV_DRV_BO_MAPPABLE) {
++ ret = nouveau_bo_map(bo);
++ if (ret)
++ goto fail_bo_map;
++ }
++ else
++ bo->kmap.virtual = NULL;
++
++ /* allocate virtual address space, if requested. */
++ if (drv_flags & GDEV_DRV_BO_VSPACE) {
++ if (dev_priv->card_type >= NV_50) {
++ vma = kzalloc(sizeof(*vma), GFP_KERNEL);
++ if (!vma) {
++ ret = -ENOMEM;
++ goto fail_vma_alloc;
++ }
++
++ ret = nouveau_bo_vma_add(bo, chan->vm, vma);
++ if (ret)
++ goto fail_vma_add;
++
++ drv_bo->addr = vma->offset;
++ }
++ else /* non-supported cards. */
++ drv_bo->addr = 0;
++ }
++ else
++ drv_bo->addr = 0;
++
++ /* address, size, and map. */
++ if (bo->kmap.virtual)
++ drv_bo->map = bo->kmap.virtual;
++ else
++ drv_bo->map = NULL;
++ drv_bo->size = bo->bo.mem.size;
++ drv_bo->priv = bo;
++
++ return 0;
++
++fail_vma_add:
++ kfree(vma);
++fail_vma_alloc:
++ nouveau_bo_unmap(bo);
++fail_bo_map:
++ nouveau_bo_ref(NULL, &bo);
++fail_bo_new:
++ return ret;
++
++}
++EXPORT_SYMBOL(gdev_drv_bo_alloc);
++
++int gdev_drv_bo_free(struct gdev_drv_vspace *drv_vspace, struct gdev_drv_bo *drv_bo)
++{
++ struct nouveau_channel *chan = (struct nouveau_channel *)drv_vspace->priv;
++ struct nouveau_bo *bo = (struct nouveau_bo *)drv_bo->priv;
++ struct nouveau_vma *vma;
++ uint64_t addr = drv_bo->addr;
++ void *map = drv_bo->map;
++
++ if (map && bo->kmap.bo) /* dirty validation.. */
++ nouveau_bo_unmap(bo);
++
++ if (addr) {
++ vma = nouveau_bo_vma_find(bo, chan->vm);
++#if 0 /* this function call crushes the system sometimes... */
++ if (vma) {
++ nouveau_bo_vma_del(bo, vma);
++ kfree(vma);
++ }
++ else {
++ return -ENOENT;
++ }
++#endif
++ }
++
++ nouveau_bo_ref(NULL, &bo);
++
++ return 0;
++}
++EXPORT_SYMBOL(gdev_drv_bo_free);
++
++int gdev_drv_bo_bind(struct drm_device *drm, struct gdev_drv_vspace *drv_vspace, struct gdev_drv_bo *drv_bo)
++{
++ struct drm_nouveau_private *dev_priv = drm->dev_private;
++ struct nouveau_channel *chan = (struct nouveau_channel *)drv_vspace->priv;
++ struct nouveau_bo *bo = (struct nouveau_bo *)drv_bo->priv;
++ struct nouveau_vma *vma;
++ int ret;
++
++ /* allocate virtual address space, if requested. */
++ if (dev_priv->card_type >= NV_50) {
++ vma = kzalloc(sizeof(*vma), GFP_KERNEL);
++ if (!vma) {
++ ret = -ENOMEM;
++ goto fail_vma_alloc;
++ }
++
++ ret = nouveau_bo_vma_add(bo, chan->vm, vma);
++ if (ret)
++ goto fail_vma_add;
++
++ drv_bo->addr = vma->offset;
++ }
++ else /* non-supported cards. */
++ drv_bo->addr = 0;
++
++ drv_bo->map = bo->kmap.virtual; /* could be NULL. */
++ drv_bo->size = bo->bo.mem.size;
++
++ return 0;
++
++fail_vma_add:
++ kfree(vma);
++fail_vma_alloc:
++ return ret;
++}
++EXPORT_SYMBOL(gdev_drv_bo_bind);
++
++int gdev_drv_bo_unbind(struct gdev_drv_vspace *drv_vspace, struct gdev_drv_bo *drv_bo)
++{
++ struct nouveau_channel *chan = (struct nouveau_channel *)drv_vspace->priv;
++ struct nouveau_bo *bo = (struct nouveau_bo *)drv_bo->priv;
++ struct nouveau_vma *vma;
++
++ vma = nouveau_bo_vma_find(bo, chan->vm);
++ if (vma) {
++#if 0 /* this function call crushes the system sometimes... */
++ nouveau_bo_vma_del(bo, vma);
++ kfree(vma);
++#endif
++ }
++ else
++ return -ENOENT;
++
++ return 0;
++}
++EXPORT_SYMBOL(gdev_drv_bo_unbind);
++
++int gdev_drv_bo_map(struct drm_device *drm, struct gdev_drv_bo *drv_bo)
++{
++ struct nouveau_bo *bo = (struct nouveau_bo *)drv_bo->priv;
++ int ret;
++
++ ret = nouveau_bo_map(bo);
++ if (ret)
++ return ret;
++
++ drv_bo->map = bo->kmap.virtual;
++
++ return 0;
++}
++EXPORT_SYMBOL(gdev_drv_bo_map);
++
++int gdev_drv_bo_unmap(struct gdev_drv_bo *drv_bo)
++{
++ struct nouveau_bo *bo = (struct nouveau_bo *)drv_bo->priv;
++
++ if (bo->kmap.bo) /* dirty validation.. */
++ nouveau_bo_unmap(bo);
++ else
++ return -ENOENT;
++
++ return 0;
++}
++EXPORT_SYMBOL(gdev_drv_bo_unmap);
++
++int gdev_drv_read32(struct drm_device *drm, struct gdev_drv_vspace *drv_vspace, struct gdev_drv_bo *drv_bo, uint64_t offset, uint32_t *p)
++{
++ if (drv_bo->map)
++ *p = ioread32_native(drv_bo->map + offset);
++ else
++ return -EINVAL;
++
++ return 0;
++}
++EXPORT_SYMBOL(gdev_drv_read32);
++
++int gdev_drv_write32(struct drm_device *drm, struct gdev_drv_vspace *drv_vspace, struct gdev_drv_bo *drv_bo, uint64_t offset, uint32_t val)
++{
++ if (drv_bo->map)
++ iowrite32_native(val, drv_bo->map + offset);
++ else
++ return -EINVAL;
++
++ return 0;
++}
++EXPORT_SYMBOL(gdev_drv_write32);
++
++int gdev_drv_read(struct drm_device *drm, struct gdev_drv_vspace *drv_vspace, struct gdev_drv_bo *drv_bo, uint64_t offset, uint64_t size, void *buf)
++{
++ if (drv_bo->map)
++ memcpy_fromio(buf, drv_bo->map + offset, size);
++ else
++ return -EINVAL;
++
++ return 0;
++}
++EXPORT_SYMBOL(gdev_drv_read);
++
++int gdev_drv_write(struct drm_device *drm, struct gdev_drv_vspace *drv_vspace, struct gdev_drv_bo *drv_bo, uint64_t offset, uint64_t size, const void *buf)
++{
++ if (drv_bo->map)
++ memcpy_toio(drv_bo->map + offset, buf, size);
++ else
++ return -EINVAL;
++
++ return 0;
++}
++EXPORT_SYMBOL(gdev_drv_write);
++
++int gdev_drv_getdevice(int *count)
++{
++ *count = nouveau_device_count;
++ return 0;
++}
++EXPORT_SYMBOL(gdev_drv_getdevice);
++
++int gdev_drv_getdrm(int minor, struct drm_device **pptr)
++{
++ if (minor < nouveau_device_count) {
++ if (nouveau_drm[minor]) {
++ *pptr = nouveau_drm[minor];
++ return 0;
++ }
++ }
++
++ *pptr = NULL;
++
++ return -ENODEV;
++}
++EXPORT_SYMBOL(gdev_drv_getdrm);
++
++int gdev_drv_getparam(struct drm_device *drm, uint32_t type, uint64_t *res)
++{
++ struct drm_nouveau_getparam getparam;
++ struct drm_nouveau_private *dev_priv = drm->dev_private;
++ int ret = 0;
++
++ switch (type) {
++ case GDEV_DRV_GETPARAM_MP_COUNT:
++ if ((dev_priv->chipset & 0xf0) == 0xc0) {
++ struct nvc0_graph_priv *priv = nv_engine(drm, NVOBJ_ENGINE_GR);
++ *res = priv->tp_total;
++ }
++ else {
++ *res = 0;
++ ret = -EINVAL;
++ }
++ break;
++ case GDEV_DRV_GETPARAM_FB_SIZE:
++ getparam.param = NOUVEAU_GETPARAM_FB_SIZE;
++ ret = nouveau_ioctl_getparam(drm, &getparam, NULL);
++ *res = getparam.value;
++ break;
++ case GDEV_DRV_GETPARAM_AGP_SIZE:
++ getparam.param = NOUVEAU_GETPARAM_AGP_SIZE;
++ ret = nouveau_ioctl_getparam(drm, &getparam, NULL);
++ *res = getparam.value;
++ break;
++ case GDEV_DRV_GETPARAM_CHIPSET_ID:
++ getparam.param = NOUVEAU_GETPARAM_CHIPSET_ID;
++ ret = nouveau_ioctl_getparam(drm, &getparam, NULL);
++ *res = getparam.value;
++ break;
++ default:
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++EXPORT_SYMBOL(gdev_drv_getparam);
++
++int gdev_drv_getaddr(struct drm_device *drm, struct gdev_drv_vspace *drv_vspace, struct gdev_drv_bo *drv_bo, uint64_t offset, uint64_t *addr)
++{
++ struct nouveau_bo *bo = (struct nouveau_bo *)drv_bo->priv;
++ int page = offset / PAGE_SIZE;
++ uint32_t x = offset - page * PAGE_SIZE;
++
++ if (drv_bo->map) {
++ if (bo->bo.mem.mem_type & TTM_PL_TT)
++ *addr = ((struct ttm_dma_tt *)bo->bo.ttm)->dma_address[page] + x;
++ else
++ *addr = bo->bo.mem.bus.base + bo->bo.mem.bus.offset + x;
++ }
++ else {
++ *addr = 0;
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL(gdev_drv_getaddr);
++
++int gdev_drv_setnotify(void (*func)(int subc, uint32_t data))
++{
++ nouveau_callback_notify = func;
++ return 0;
++}
++EXPORT_SYMBOL(gdev_drv_setnotify);
++
++int gdev_drv_unsetnotify(void (*func)(int subc, uint32_t data))
++{
++ if (nouveau_callback_notify != func)
++ return -EINVAL;
++ nouveau_callback_notify = NULL;
++
++ return 0;
++}
++EXPORT_SYMBOL(gdev_drv_unsetnotify);
+diff --git a/drivers/gpu/drm/nouveau/gdev_interface.h b/drivers/gpu/drm/nouveau/gdev_interface.h
+new file mode 100644
+index 0000000..9d11c25
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/gdev_interface.h
+@@ -0,0 +1,66 @@
++#ifndef __GDEV_INTERFACE_H__
++#define __GDEV_INTERFACE_H__
++
++#include "drmP.h"
++#include "drm.h"
++
++#define GDEV_DRV_BO_VRAM 0x1
++#define GDEV_DRV_BO_SYSRAM 0x2
++#define GDEV_DRV_BO_MAPPABLE 0x4
++#define GDEV_DRV_BO_VSPACE 0x8
++
++#define GDEV_DRV_GETPARAM_MP_COUNT 1
++#define GDEV_DRV_GETPARAM_FB_SIZE 2
++#define GDEV_DRV_GETPARAM_AGP_SIZE 3
++#define GDEV_DRV_GETPARAM_CHIPSET_ID 4
++
++struct gdev_drv_vspace {
++ void *priv;
++};
++
++struct gdev_drv_chan {
++ void *priv;
++ uint32_t cid;
++ volatile uint32_t *regs; /* channel control registers. */
++ void *ib_bo; /* driver private object. */
++ uint32_t *ib_map;
++ uint32_t ib_order;
++ uint64_t ib_base;
++ uint32_t ib_mask;
++ void *pb_bo; /* driver private object. */
++ uint32_t *pb_map;
++ uint32_t pb_order;
++ uint64_t pb_base;
++ uint32_t pb_mask;
++ uint32_t pb_size;
++};
++
++struct gdev_drv_bo {
++ void *priv;
++ uint64_t addr;
++ uint64_t size;
++ void *map;
++};
++
++int gdev_drv_vspace_alloc(struct drm_device *drm, uint64_t size, struct gdev_drv_vspace *drv_vspace);
++int gdev_drv_vspace_free(struct gdev_drv_vspace *drv_vspace);
++int gdev_drv_chan_alloc(struct drm_device *drm, struct gdev_drv_vspace *drv_vspace, struct gdev_drv_chan *drv_chan);
++int gdev_drv_chan_free(struct gdev_drv_vspace *drv_vspace, struct gdev_drv_chan *drv_chan);
++int gdev_drv_bo_alloc(struct drm_device *drm, uint64_t size, uint32_t flags, struct gdev_drv_vspace *drv_vspace, struct gdev_drv_bo *drv_bo);
++int gdev_drv_bo_free(struct gdev_drv_vspace *drv_vspace, struct gdev_drv_bo *drv_bo);
++int gdev_drv_bo_bind(struct drm_device *drm, struct gdev_drv_vspace *drv_vspace, struct gdev_drv_bo *drv_bo);
++int gdev_drv_bo_unbind(struct gdev_drv_vspace *drv_vspace, struct gdev_drv_bo *drv_bo);
++int gdev_drv_bo_map(struct drm_device *drm, struct gdev_drv_bo *drv_bo);
++int gdev_drv_bo_unmap(struct gdev_drv_bo *drv_bo);
++int gdev_drv_read32(struct drm_device *drm, struct gdev_drv_vspace *drv_vspace, struct gdev_drv_bo *drv_bo, uint64_t offset, uint32_t *p);
++int gdev_drv_write32(struct drm_device *drm, struct gdev_drv_vspace *drv_vspace, struct gdev_drv_bo *drv_bo, uint64_t offset, uint32_t val);
++int gdev_drv_read(struct drm_device *drm, struct gdev_drv_vspace *drv_vspace, struct gdev_drv_bo *drv_bo, uint64_t offset, uint64_t size, void *buf);
++int gdev_drv_write(struct drm_device *drm, struct gdev_drv_vspace *drv_vspace, struct gdev_drv_bo *drv_bo, uint64_t offset, uint64_t size, const void *buf);
++int gdev_drv_getdevice(int *count);
++int gdev_drv_getdrm(int minor, struct drm_device **pptr);
++int gdev_drv_getparam(struct drm_device *drm, uint32_t type, uint64_t *res);
++int gdev_drv_getaddr(struct drm_device *drm, struct gdev_drv_vspace *drv_vspace, struct gdev_drv_bo *drv_bo, uint64_t offset, uint64_t *addr);
++int gdev_drv_setnotify(void (*func)(int subc, uint32_t data));
++int gdev_drv_unsetnotify(void (*func)(int subc, uint32_t data));
++
++#endif
+diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c
+index cad254c..a489a12 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_drv.c
++++ b/drivers/gpu/drm/nouveau/nouveau_drv.c
+@@ -461,8 +461,38 @@ static struct pci_driver nouveau_pci_driver = {
+ .resume = nouveau_pci_resume
+ };
+
++static int __get_device_count(void)
++{
++ struct pci_dev *pdev = NULL;
++ const struct pci_device_id *pid;
++ int i;
++ int count = 0;
++
++ for (i = 0; pciidlist[i].vendor != 0; i++) {
++ pid = &pciidlist[i];
++ while ((pdev = pci_get_subsys(pid->vendor, pid->device, pid->subvendor, pid->subdevice, pdev)) != NULL) {
++ if ((pdev->class & pid->class_mask) != pid->class)
++ continue;
++ count++; /* physical device count */
++ }
++ }
++
++ return count;
++}
++
++int nouveau_device_count = 0;
++struct drm_device **nouveau_drm;
++
+ static int __init nouveau_init(void)
+ {
++ nouveau_device_count = __get_device_count();
++
++ nouveau_drm = kzalloc(sizeof(*nouveau_drm) * nouveau_device_count, GFP_KERNEL);
++ if (!nouveau_drm) {
++ printk(KERN_INFO "Failed to allocate nouveau drm array\n");
++ return -ENOMEM;
++ }
++
+ driver.num_ioctls = nouveau_max_ioctl;
+
+ if (nouveau_modeset == -1) {
+diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
+index 19706f0..ffc3ab4 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_state.c
++++ b/drivers/gpu/drm/nouveau/nouveau_state.c
+@@ -1022,6 +1022,9 @@ static int nouveau_remove_conflicting_drivers(struct drm_device *dev)
+ return 0;
+ }
+
++extern int nouveau_device_count;
++extern struct drm_device **nouveau_drm;
++
+ int nouveau_load(struct drm_device *dev, unsigned long flags)
+ {
+ struct drm_nouveau_private *dev_priv;
+@@ -1202,6 +1205,10 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
+ if (ret)
+ goto err_ramin;
+
++ /* Nouveau indexes primaries from 1, but we store them from 0 */
++ if (dev->primary->index && dev->primary->index <= nouveau_device_count)
++ nouveau_drm[dev->primary->index - 1] = dev;
++
+ return 0;
+
+ err_ramin:
+diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c
+index 2a01e6e..ce5bbb4 100644
+--- a/drivers/gpu/drm/nouveau/nvc0_graph.c
++++ b/drivers/gpu/drm/nouveau/nvc0_graph.c
+@@ -656,6 +656,8 @@ nvc0_graph_ctxctl_isr(struct drm_device *dev)
+ nv_wr32(dev, 0x409c20, ustat);
+ }
+
++void (*nouveau_callback_notify)(int subc, uint32_t data) = NULL;
++
+ static void
+ nvc0_graph_isr(struct drm_device *dev)
+ {
+@@ -669,6 +671,14 @@ nvc0_graph_isr(struct drm_device *dev)
+ u32 code = nv_rd32(dev, 0x400110);
+ u32 class = nv_rd32(dev, 0x404200 + (subc * 4));
+
++ if (stat & 0x00000001) {
++ if (nouveau_callback_notify) {
++ nouveau_callback_notify(subc, data);
++ }
++ nv_wr32(dev, 0x400100, 0x00000001);
++ stat &= ~0x00000001;
++ }
++
+ if (stat & 0x00000010) {
+ if (nouveau_gpuobj_mthd_call2(dev, chid, class, mthd, data)) {
+ NV_INFO(dev, "PGRAPH: ILLEGAL_MTHD ch %d [0x%010llx] "
Something went wrong with that request. Please try again.