diff --git a/driver/patches/gdev-nouveau.patch b/driver/patches/gdev-nouveau.patch new file mode 100644 index 00000000..c83bfca0 --- /dev/null +++ b/driver/patches/gdev-nouveau.patch @@ -0,0 +1,669 @@ +diff --git drivers/gpu/drm/nouveau/Makefile drivers/gpu/drm/nouveau/Makefile +index 1a2ad7e..a88eec5 100644 +--- drivers/gpu/drm/nouveau/Makefile ++++ 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 drivers/gpu/drm/nouveau/gdev_interface.c drivers/gpu/drm/nouveau/gdev_interface.c +new file mode 100644 +index 0000000..9489632 +--- /dev/null ++++ drivers/gpu/drm/nouveau/gdev_interface.c +@@ -0,0 +1,456 @@ ++#include ++#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, &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 drivers/gpu/drm/nouveau/gdev_interface.h drivers/gpu/drm/nouveau/gdev_interface.h +new file mode 100644 +index 0000000..9d11c25 +--- /dev/null ++++ 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 drivers/gpu/drm/nouveau/nouveau_channel.c drivers/gpu/drm/nouveau/nouveau_channel.c +index a018def..06a617f 100644 +--- drivers/gpu/drm/nouveau/nouveau_channel.c ++++ drivers/gpu/drm/nouveau/nouveau_channel.c +@@ -188,7 +188,7 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret, + chan->user_put = 0x40; + chan->user_get = 0x44; + if (dev_priv->card_type >= NV_50) +- chan->user_get_hi = 0x60; ++ chan->user_get_hi = 0x60; + + /* disable the fifo caches */ + pfifo->reassign(dev, false); +diff --git drivers/gpu/drm/nouveau/nouveau_drv.c drivers/gpu/drm/nouveau/nouveau_drv.c +index 4f2030b..e678716 100644 +--- drivers/gpu/drm/nouveau/nouveau_drv.c ++++ drivers/gpu/drm/nouveau/nouveau_drv.c +@@ -460,8 +460,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 drivers/gpu/drm/nouveau/nouveau_state.c drivers/gpu/drm/nouveau/nouveau_state.c +index 1e04305..79a962c 100644 +--- drivers/gpu/drm/nouveau/nouveau_state.c ++++ drivers/gpu/drm/nouveau/nouveau_state.c +@@ -990,6 +990,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; +@@ -1161,6 +1164,9 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) + if (ret) + goto err_ramin; + ++ if (dev->primary->index < nouveau_device_count) ++ nouveau_drm[dev->primary->index] = dev; ++ + return 0; + + err_ramin: +diff --git drivers/gpu/drm/nouveau/nv50_vm.c drivers/gpu/drm/nouveau/nv50_vm.c +index f06f4ad..44fbac9 100644 +--- drivers/gpu/drm/nouveau/nv50_vm.c ++++ drivers/gpu/drm/nouveau/nv50_vm.c +@@ -85,7 +85,7 @@ nv50_vm_map(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt, + target = 3; + } + +- phys = vm_addr(vma, phys, mem->memtype, 0); ++ phys = vm_addr(vma, phys, mem->memtype, target); + pte <<= 3; + cnt <<= 3; + +diff --git drivers/gpu/drm/nouveau/nvc0_graph.c drivers/gpu/drm/nouveau/nvc0_graph.c +index 8ee3963..bbc6420 100644 +--- drivers/gpu/drm/nouveau/nvc0_graph.c ++++ drivers/gpu/drm/nouveau/nvc0_graph.c +@@ -662,6 +662,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) + { +@@ -675,6 +677,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] "