diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c | 594 |
1 files changed, 368 insertions, 226 deletions
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c index 0b356f1b6864..aaff086dfd2a 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c @@ -1,5 +1,5 @@ /* - * Copyright 2010 Red Hat Inc. + * Copyright 2012 Red Hat Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -22,25 +22,30 @@ * Authors: Ben Skeggs */ -#include "drmP.h" +#include <core/client.h> +#include <core/handle.h> +#include <core/namedb.h> +#include <core/gpuobj.h> +#include <core/engctx.h> +#include <core/class.h> +#include <core/math.h> +#include <core/enum.h> -#include "nouveau_drv.h" -#include <core/mm.h> -#include <engine/fifo.h> -#include "nouveau_software.h" - -#define NVE0_FIFO_ENGINE_NUM 32 +#include <subdev/timer.h> +#include <subdev/bar.h> +#include <subdev/vm.h> -static void nve0_fifo_isr(struct drm_device *); +#include <engine/dmaobj.h> +#include <engine/fifo.h> -struct nve0_fifo_engine { +struct nve0_fifo_engn { struct nouveau_gpuobj *playlist[2]; int cur_playlist; }; struct nve0_fifo_priv { - struct nouveau_fifo_priv base; - struct nve0_fifo_engine engine[NVE0_FIFO_ENGINE_NUM]; + struct nouveau_fifo base; + struct nve0_fifo_engn engine[16]; struct { struct nouveau_gpuobj *mem; struct nouveau_vma bar; @@ -48,194 +53,286 @@ struct nve0_fifo_priv { int spoon_nr; }; +struct nve0_fifo_base { + struct nouveau_fifo_base base; + struct nouveau_gpuobj *pgd; + struct nouveau_vm *vm; +}; + struct nve0_fifo_chan { struct nouveau_fifo_chan base; u32 engine; }; +/******************************************************************************* + * FIFO channel objects + ******************************************************************************/ + static void -nve0_fifo_playlist_update(struct drm_device *dev, u32 engine) +nve0_fifo_playlist_update(struct nve0_fifo_priv *priv, u32 engine) { - struct nve0_fifo_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FIFO); - struct nve0_fifo_engine *peng = &priv->engine[engine]; + struct nouveau_bar *bar = nouveau_bar(priv); + struct nve0_fifo_engn *engn = &priv->engine[engine]; struct nouveau_gpuobj *cur; u32 match = (engine << 16) | 0x00000001; - int ret, i, p; + int i, p; - cur = peng->playlist[peng->cur_playlist]; + cur = engn->playlist[engn->cur_playlist]; if (unlikely(cur == NULL)) { - ret = nouveau_gpuobj_new(dev, NULL, 0x8000, 0x1000, 0, &cur); + int ret = nouveau_gpuobj_new(nv_object(priv)->parent, NULL, + 0x8000, 0x1000, 0, &cur); if (ret) { - NV_ERROR(dev, "PFIFO: playlist alloc failed\n"); + nv_error(priv, "playlist alloc failed\n"); return; } - peng->playlist[peng->cur_playlist] = cur; + engn->playlist[engn->cur_playlist] = cur; } - peng->cur_playlist = !peng->cur_playlist; + engn->cur_playlist = !engn->cur_playlist; - for (i = 0, p = 0; i < priv->base.channels; i++) { - u32 ctrl = nv_rd32(dev, 0x800004 + (i * 8)) & 0x001f0001; + for (i = 0, p = 0; i < priv->base.max; i++) { + u32 ctrl = nv_rd32(priv, 0x800004 + (i * 8)) & 0x001f0001; if (ctrl != match) continue; nv_wo32(cur, p + 0, i); nv_wo32(cur, p + 4, 0x00000000); p += 8; } - nvimem_flush(dev); + bar->flush(bar); - nv_wr32(dev, 0x002270, cur->addr >> 12); - nv_wr32(dev, 0x002274, (engine << 20) | (p >> 3)); - if (!nv_wait(dev, 0x002284 + (engine * 4), 0x00100000, 0x00000000)) - NV_ERROR(dev, "PFIFO: playlist %d update timeout\n", engine); + nv_wr32(priv, 0x002270, cur->addr >> 12); + nv_wr32(priv, 0x002274, (engine << 20) | (p >> 3)); + if (!nv_wait(priv, 0x002284 + (engine * 4), 0x00100000, 0x00000000)) + nv_error(priv, "playlist %d update timeout\n", engine); } static int -nve0_fifo_context_new(struct nouveau_channel *chan, int engine) +nve0_fifo_context_attach(struct nouveau_object *parent, + struct nouveau_object *object) { - struct drm_device *dev = chan->dev; - struct nve0_fifo_priv *priv = nv_engine(dev, engine); - struct nve0_fifo_chan *fctx; - u64 usermem = priv->user.mem->addr + chan->id * 512; - u64 ib_virt = chan->pushbuf_base + chan->dma.ib_base * 4; - int ret = 0, i; - - fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL); - if (!fctx) - return -ENOMEM; - - fctx->engine = 0; /* PGRAPH */ - - /* allocate vram for control regs, map into polling area */ - chan->user = ioremap_wc(pci_resource_start(dev->pdev, 1) + - priv->user.bar.offset + (chan->id * 512), 512); - if (!chan->user) { - ret = -ENOMEM; - goto error; + struct nouveau_bar *bar = nouveau_bar(parent); + struct nve0_fifo_base *base = (void *)parent->parent; + struct nouveau_engctx *ectx = (void *)object; + u32 addr; + int ret; + + switch (nv_engidx(object->engine)) { + case NVDEV_ENGINE_SW : return 0; + case NVDEV_ENGINE_GR : addr = 0x0210; break; + default: + return -EINVAL; } - for (i = 0; i < 0x100; i += 4) - nv_wo32(chan->ramin, i, 0x00000000); - nv_wo32(chan->ramin, 0x08, lower_32_bits(usermem)); - nv_wo32(chan->ramin, 0x0c, upper_32_bits(usermem)); - nv_wo32(chan->ramin, 0x10, 0x0000face); - nv_wo32(chan->ramin, 0x30, 0xfffff902); - nv_wo32(chan->ramin, 0x48, lower_32_bits(ib_virt)); - nv_wo32(chan->ramin, 0x4c, drm_order(chan->dma.ib_max + 1) << 16 | - upper_32_bits(ib_virt)); - nv_wo32(chan->ramin, 0x84, 0x20400000); - nv_wo32(chan->ramin, 0x94, 0x30000001); - nv_wo32(chan->ramin, 0x9c, 0x00000100); - nv_wo32(chan->ramin, 0xac, 0x0000001f); - nv_wo32(chan->ramin, 0xe4, 0x00000000); - nv_wo32(chan->ramin, 0xe8, chan->id); - nv_wo32(chan->ramin, 0xf8, 0x10003080); /* 0x002310 */ - nv_wo32(chan->ramin, 0xfc, 0x10000010); /* 0x002350 */ - nvimem_flush(dev); - - nv_wr32(dev, 0x800000 + (chan->id * 8), 0x80000000 | - (chan->ramin->addr >> 12)); - nv_mask(dev, 0x800004 + (chan->id * 8), 0x00000400, 0x00000400); - nve0_fifo_playlist_update(dev, fctx->engine); - nv_mask(dev, 0x800004 + (chan->id * 8), 0x00000400, 0x00000400); - -error: - if (ret) - priv->base.base.context_del(chan, engine); - return ret; + if (!ectx->vma.node) { + ret = nouveau_gpuobj_map_vm(nv_gpuobj(ectx), base->vm, + NV_MEM_ACCESS_RW, &ectx->vma); + if (ret) + return ret; + } + + nv_wo32(base, addr + 0x00, lower_32_bits(ectx->vma.offset) | 4); + nv_wo32(base, addr + 0x04, upper_32_bits(ectx->vma.offset)); + bar->flush(bar); + return 0; } -static void -nve0_fifo_context_del(struct nouveau_channel *chan, int engine) +static int +nve0_fifo_context_detach(struct nouveau_object *parent, bool suspend, + struct nouveau_object *object) { - struct nve0_fifo_chan *fctx = chan->engctx[engine]; - struct drm_device *dev = chan->dev; - - nv_mask(dev, 0x800004 + (chan->id * 8), 0x00000800, 0x00000800); - nv_wr32(dev, 0x002634, chan->id); - if (!nv_wait(dev, 0x0002634, 0xffffffff, chan->id)) - NV_WARN(dev, "0x2634 != chid: 0x%08x\n", nv_rd32(dev, 0x2634)); - nve0_fifo_playlist_update(dev, fctx->engine); - nv_wr32(dev, 0x800000 + (chan->id * 8), 0x00000000); - - if (chan->user) { - iounmap(chan->user); - chan->user = NULL; + struct nouveau_bar *bar = nouveau_bar(parent); + struct nve0_fifo_priv *priv = (void *)parent->engine; + struct nve0_fifo_base *base = (void *)parent->parent; + struct nve0_fifo_chan *chan = (void *)parent; + u32 addr; + + switch (nv_engidx(object->engine)) { + case NVDEV_ENGINE_SW : return 0; + case NVDEV_ENGINE_GR : addr = 0x0210; break; + default: + return -EINVAL; + } + + nv_wo32(base, addr + 0x00, 0x00000000); + nv_wo32(base, addr + 0x04, 0x00000000); + bar->flush(bar); + + nv_wr32(priv, 0x002634, chan->base.chid); + if (!nv_wait(priv, 0x002634, 0xffffffff, chan->base.chid)) { + nv_error(priv, "channel %d kick timeout\n", chan->base.chid); + if (suspend) + return -EBUSY; } - chan->engctx[NVOBJ_ENGINE_FIFO] = NULL; - kfree(fctx); + return 0; } static int -nve0_fifo_init(struct drm_device *dev, int engine) +nve0_fifo_chan_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nve0_fifo_priv *priv = nv_engine(dev, engine); - struct nve0_fifo_chan *fctx; - int i; + struct nouveau_bar *bar = nouveau_bar(parent); + struct nve0_fifo_priv *priv = (void *)engine; + struct nve0_fifo_base *base = (void *)parent; + struct nve0_fifo_chan *chan; + struct nv_channel_ind_class *args = data; + u64 usermem, ioffset, ilength; + int ret, i; + + if (size < sizeof(*args)) + return -EINVAL; + + ret = nouveau_fifo_channel_create(parent, engine, oclass, 1, + priv->user.bar.offset, 0x200, + args->pushbuf, + (1 << NVDEV_ENGINE_SW) | + (1 << NVDEV_ENGINE_GR), &chan); + *pobject = nv_object(chan); + if (ret) + return ret; + + nv_parent(chan)->context_attach = nve0_fifo_context_attach; + nv_parent(chan)->context_detach = nve0_fifo_context_detach; + + usermem = chan->base.chid * 0x200; + ioffset = args->ioffset; + ilength = log2i(args->ilength / 8); + + for (i = 0; i < 0x200; i += 4) + nv_wo32(priv->user.mem, usermem + i, 0x00000000); + + nv_wo32(base, 0x08, lower_32_bits(priv->user.mem->addr + usermem)); + nv_wo32(base, 0x0c, upper_32_bits(priv->user.mem->addr + usermem)); + nv_wo32(base, 0x10, 0x0000face); + nv_wo32(base, 0x30, 0xfffff902); + nv_wo32(base, 0x48, lower_32_bits(ioffset)); + nv_wo32(base, 0x4c, upper_32_bits(ioffset) | (ilength << 16)); + nv_wo32(base, 0x84, 0x20400000); + nv_wo32(base, 0x94, 0x30000001); + nv_wo32(base, 0x9c, 0x00000100); + nv_wo32(base, 0xac, 0x0000001f); + nv_wo32(base, 0xe8, chan->base.chid); + nv_wo32(base, 0xb8, 0xf8000000); + nv_wo32(base, 0xf8, 0x10003080); /* 0x002310 */ + nv_wo32(base, 0xfc, 0x10000010); /* 0x002350 */ + bar->flush(bar); + return 0; +} - /* reset PFIFO, enable all available PSUBFIFO areas */ - nv_mask(dev, 0x000200, 0x00000100, 0x00000000); - nv_mask(dev, 0x000200, 0x00000100, 0x00000100); - nv_wr32(dev, 0x000204, 0xffffffff); +static int +nve0_fifo_chan_init(struct nouveau_object *object) +{ + struct nouveau_gpuobj *base = nv_gpuobj(object->parent); + struct nve0_fifo_priv *priv = (void *)object->engine; + struct nve0_fifo_chan *chan = (void *)object; + u32 chid = chan->base.chid; + int ret; - priv->spoon_nr = hweight32(nv_rd32(dev, 0x000204)); - NV_DEBUG(dev, "PFIFO: %d subfifo(s)\n", priv->spoon_nr); + ret = nouveau_fifo_channel_init(&chan->base); + if (ret) + return ret; - /* PSUBFIFO[n] */ - for (i = 0; i < priv->spoon_nr; i++) { - nv_mask(dev, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000); - nv_wr32(dev, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */ - nv_wr32(dev, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTR_EN */ - } + nv_wr32(priv, 0x800000 + (chid * 8), 0x80000000 | base->addr >> 12); + nv_mask(priv, 0x800004 + (chid * 8), 0x00000400, 0x00000400); + nve0_fifo_playlist_update(priv, chan->engine); + nv_mask(priv, 0x800004 + (chid * 8), 0x00000400, 0x00000400); + return 0; +} - nv_wr32(dev, 0x002254, 0x10000000 | priv->user.bar.offset >> 12); +static int +nve0_fifo_chan_fini(struct nouveau_object *object, bool suspend) +{ + struct nve0_fifo_priv *priv = (void *)object->engine; + struct nve0_fifo_chan *chan = (void *)object; + u32 chid = chan->base.chid; - nv_wr32(dev, 0x002a00, 0xffffffff); - nv_wr32(dev, 0x002100, 0xffffffff); - nv_wr32(dev, 0x002140, 0xbfffffff); + nv_mask(priv, 0x800004 + (chid * 8), 0x00000800, 0x00000800); + nve0_fifo_playlist_update(priv, chan->engine); + nv_wr32(priv, 0x800000 + (chid * 8), 0x00000000); - /* restore PFIFO context table */ - for (i = 0; i < priv->base.channels; i++) { - struct nouveau_channel *chan = dev_priv->channels.ptr[i]; - if (!chan || !(fctx = chan->engctx[engine])) - continue; + return nouveau_fifo_channel_fini(&chan->base, suspend); +} - nv_wr32(dev, 0x800000 + (i * 8), 0x80000000 | - (chan->ramin->addr >> 12)); - nv_mask(dev, 0x800004 + (i * 8), 0x00000400, 0x00000400); - nve0_fifo_playlist_update(dev, fctx->engine); - nv_mask(dev, 0x800004 + (i * 8), 0x00000400, 0x00000400); - } +static struct nouveau_ofuncs +nve0_fifo_ofuncs = { + .ctor = nve0_fifo_chan_ctor, + .dtor = _nouveau_fifo_channel_dtor, + .init = nve0_fifo_chan_init, + .fini = nve0_fifo_chan_fini, + .rd32 = _nouveau_fifo_channel_rd32, + .wr32 = _nouveau_fifo_channel_wr32, +}; - return 0; -} +static struct nouveau_oclass +nve0_fifo_sclass[] = { + { 0xa06f, &nve0_fifo_ofuncs }, + {} +}; + +/******************************************************************************* + * FIFO context - instmem heap and vm setup + ******************************************************************************/ static int -nve0_fifo_fini(struct drm_device *dev, int engine, bool suspend) +nve0_fifo_context_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) { - struct nve0_fifo_priv *priv = nv_engine(dev, engine); - int i; + struct nve0_fifo_base *base; + int ret; - for (i = 0; i < priv->base.channels; i++) { - if (!(nv_rd32(dev, 0x800004 + (i * 8)) & 1)) - continue; + ret = nouveau_fifo_context_create(parent, engine, oclass, NULL, 0x1000, + 0x1000, NVOBJ_FLAG_ZERO_ALLOC, &base); + *pobject = nv_object(base); + if (ret) + return ret; - nv_mask(dev, 0x800004 + (i * 8), 0x00000800, 0x00000800); - nv_wr32(dev, 0x002634, i); - if (!nv_wait(dev, 0x002634, 0xffffffff, i)) { - NV_INFO(dev, "PFIFO: kick ch %d failed: 0x%08x\n", - i, nv_rd32(dev, 0x002634)); - return -EBUSY; - } - } + ret = nouveau_gpuobj_new(parent, NULL, 0x10000, 0x1000, 0, &base->pgd); + if (ret) + return ret; + + nv_wo32(base, 0x0200, lower_32_bits(base->pgd->addr)); + nv_wo32(base, 0x0204, upper_32_bits(base->pgd->addr)); + nv_wo32(base, 0x0208, 0xffffffff); + nv_wo32(base, 0x020c, 0x000000ff); + + ret = nouveau_vm_ref(nouveau_client(parent)->vm, &base->vm, base->pgd); + if (ret) + return ret; - nv_wr32(dev, 0x002140, 0x00000000); return 0; } +static void +nve0_fifo_context_dtor(struct nouveau_object *object) +{ + struct nve0_fifo_base *base = (void *)object; + nouveau_vm_ref(NULL, &base->vm, base->pgd); + nouveau_gpuobj_ref(NULL, &base->pgd); + nouveau_fifo_context_destroy(&base->base); +} + +static struct nouveau_oclass +nve0_fifo_cclass = { + .handle = NV_ENGCTX(FIFO, 0xe0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nve0_fifo_context_ctor, + .dtor = nve0_fifo_context_dtor, + .init = _nouveau_fifo_context_init, + .fini = _nouveau_fifo_context_fini, + .rd32 = _nouveau_fifo_context_rd32, + .wr32 = _nouveau_fifo_context_wr32, + }, +}; + +/******************************************************************************* + * PFIFO engine + ******************************************************************************/ + struct nouveau_enum nve0_fifo_fault_unit[] = { {} }; @@ -268,16 +365,16 @@ struct nouveau_bitfield nve0_fifo_subfifo_intr[] = { }; static void -nve0_fifo_isr_vm_fault(struct drm_device *dev, int unit) +nve0_fifo_isr_vm_fault(struct nve0_fifo_priv *priv, int unit) { - u32 inst = nv_rd32(dev, 0x2800 + (unit * 0x10)); - u32 valo = nv_rd32(dev, 0x2804 + (unit * 0x10)); - u32 vahi = nv_rd32(dev, 0x2808 + (unit * 0x10)); - u32 stat = nv_rd32(dev, 0x280c + (unit * 0x10)); + u32 inst = nv_rd32(priv, 0x2800 + (unit * 0x10)); + u32 valo = nv_rd32(priv, 0x2804 + (unit * 0x10)); + u32 vahi = nv_rd32(priv, 0x2808 + (unit * 0x10)); + u32 stat = nv_rd32(priv, 0x280c + (unit * 0x10)); u32 client = (stat & 0x00001f00) >> 8; - NV_INFO(dev, "PFIFO: %s fault at 0x%010llx [", - (stat & 0x00000080) ? "write" : "read", (u64)vahi << 32 | valo); + nv_error(priv, "PFIFO: %s fault at 0x%010llx [", (stat & 0x00000080) ? + "write" : "read", (u64)vahi << 32 | valo); nouveau_enum_print(nve0_fifo_fault_reason, stat & 0x0000000f); printk("] from "); nouveau_enum_print(nve0_fifo_fault_unit, unit); @@ -292,160 +389,205 @@ nve0_fifo_isr_vm_fault(struct drm_device *dev, int unit) } static int -nve0_fifo_page_flip(struct drm_device *dev, u32 chid) +nve0_fifo_swmthd(struct nve0_fifo_priv *priv, u32 chid, u32 mthd, u32 data) { - struct nve0_fifo_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FIFO); - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_channel *chan = NULL; + struct nve0_fifo_chan *chan = NULL; + struct nouveau_handle *bind; unsigned long flags; int ret = -EINVAL; - spin_lock_irqsave(&dev_priv->channels.lock, flags); - if (likely(chid >= 0 && chid < priv->base.channels)) { - chan = dev_priv->channels.ptr[chid]; - if (likely(chan)) { - struct nouveau_software_chan *swch = - chan->engctx[NVOBJ_ENGINE_SW]; - ret = swch->flip(swch->flip_data); - } + spin_lock_irqsave(&priv->base.lock, flags); + if (likely(chid >= priv->base.min && chid <= priv->base.max)) + chan = (void *)priv->base.channel[chid]; + if (unlikely(!chan)) + goto out; + + bind = nouveau_namedb_get_class(nv_namedb(chan), 0x906e); + if (likely(bind)) { + if (!mthd || !nv_call(bind->object, mthd, data)) + ret = 0; + nouveau_namedb_put(bind); } - spin_unlock_irqrestore(&dev_priv->channels.lock, flags); + +out: + spin_unlock_irqrestore(&priv->base.lock, flags); return ret; } static void -nve0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit) +nve0_fifo_isr_subfifo_intr(struct nve0_fifo_priv *priv, int unit) { - u32 stat = nv_rd32(dev, 0x040108 + (unit * 0x2000)); - u32 addr = nv_rd32(dev, 0x0400c0 + (unit * 0x2000)); - u32 data = nv_rd32(dev, 0x0400c4 + (unit * 0x2000)); - u32 chid = nv_rd32(dev, 0x040120 + (unit * 0x2000)) & 0xfff; - u32 subc = (addr & 0x00070000); + u32 stat = nv_rd32(priv, 0x040108 + (unit * 0x2000)); + u32 addr = nv_rd32(priv, 0x0400c0 + (unit * 0x2000)); + u32 data = nv_rd32(priv, 0x0400c4 + (unit * 0x2000)); + u32 chid = nv_rd32(priv, 0x040120 + (unit * 0x2000)) & 0xfff; + u32 subc = (addr & 0x00070000) >> 16; u32 mthd = (addr & 0x00003ffc); u32 show = stat; if (stat & 0x00200000) { if (mthd == 0x0054) { - if (!nve0_fifo_page_flip(dev, chid)) + if (!nve0_fifo_swmthd(priv, chid, 0x0500, 0x00000000)) show &= ~0x00200000; } } + if (stat & 0x00800000) { + if (!nve0_fifo_swmthd(priv, chid, mthd, data)) + show &= ~0x00800000; + } + if (show) { - NV_INFO(dev, "PFIFO%d:", unit); + nv_error(priv, "SUBFIFO%d:", unit); nouveau_bitfield_print(nve0_fifo_subfifo_intr, show); - NV_INFO(dev, "PFIFO%d: ch %d subc %d mthd 0x%04x data 0x%08x\n", - unit, chid, subc, mthd, data); + printk("\n"); + nv_error(priv, "SUBFIFO%d: ch %d subc %d mthd 0x%04x " + "data 0x%08x\n", + unit, chid, subc, mthd, data); } - nv_wr32(dev, 0x0400c0 + (unit * 0x2000), 0x80600008); - nv_wr32(dev, 0x040108 + (unit * 0x2000), stat); + nv_wr32(priv, 0x0400c0 + (unit * 0x2000), 0x80600008); + nv_wr32(priv, 0x040108 + (unit * 0x2000), stat); } static void -nve0_fifo_isr(struct drm_device *dev) +nve0_fifo_intr(struct nouveau_subdev *subdev) { - u32 mask = nv_rd32(dev, 0x002140); - u32 stat = nv_rd32(dev, 0x002100) & mask; + struct nve0_fifo_priv *priv = (void *)subdev; + u32 mask = nv_rd32(priv, 0x002140); + u32 stat = nv_rd32(priv, 0x002100) & mask; if (stat & 0x00000100) { - NV_INFO(dev, "PFIFO: unknown status 0x00000100\n"); - nv_wr32(dev, 0x002100, 0x00000100); + nv_warn(priv, "unknown status 0x00000100\n"); + nv_wr32(priv, 0x002100, 0x00000100); stat &= ~0x00000100; } if (stat & 0x10000000) { - u32 units = nv_rd32(dev, 0x00259c); + u32 units = nv_rd32(priv, 0x00259c); u32 u = units; while (u) { int i = ffs(u) - 1; - nve0_fifo_isr_vm_fault(dev, i); + nve0_fifo_isr_vm_fault(priv, i); u &= ~(1 << i); } - nv_wr32(dev, 0x00259c, units); + nv_wr32(priv, 0x00259c, units); stat &= ~0x10000000; } if (stat & 0x20000000) { - u32 units = nv_rd32(dev, 0x0025a0); + u32 units = nv_rd32(priv, 0x0025a0); u32 u = units; while (u) { int i = ffs(u) - 1; - nve0_fifo_isr_subfifo_intr(dev, i); + nve0_fifo_isr_subfifo_intr(priv, i); u &= ~(1 << i); } - nv_wr32(dev, 0x0025a0, units); + nv_wr32(priv, 0x0025a0, units); stat &= ~0x20000000; } if (stat & 0x40000000) { - NV_INFO(dev, "PFIFO: unknown status 0x40000000\n"); - nv_mask(dev, 0x002a00, 0x00000000, 0x00000000); + nv_warn(priv, "unknown status 0x40000000\n"); + nv_mask(priv, 0x002a00, 0x00000000, 0x00000000); stat &= ~0x40000000; } if (stat) { - NV_INFO(dev, "PFIFO: unhandled status 0x%08x\n", stat); - nv_wr32(dev, 0x002100, stat); - nv_wr32(dev, 0x002140, 0); + nv_fatal(priv, "unhandled status 0x%08x\n", stat); + nv_wr32(priv, 0x002100, stat); + nv_wr32(priv, 0x002140, 0); } } +static int +nve0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nve0_fifo_priv *priv; + int ret; + + ret = nouveau_fifo_create(parent, engine, oclass, 0, 4095, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + ret = nouveau_gpuobj_new(parent, NULL, 4096 * 0x200, 0x1000, + NVOBJ_FLAG_ZERO_ALLOC, &priv->user.mem); + if (ret) + return ret; + + ret = nouveau_gpuobj_map(priv->user.mem, NV_MEM_ACCESS_RW, + &priv->user.bar); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00000100; + nv_subdev(priv)->intr = nve0_fifo_intr; + nv_engine(priv)->cclass = &nve0_fifo_cclass; + nv_engine(priv)->sclass = nve0_fifo_sclass; + return 0; +} + static void -nve0_fifo_destroy(struct drm_device *dev, int engine) +nve0_fifo_dtor(struct nouveau_object *object) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nve0_fifo_priv *priv = nv_engine(dev, engine); + struct nve0_fifo_priv *priv = (void *)object; int i; nouveau_gpuobj_unmap(&priv->user.bar); nouveau_gpuobj_ref(NULL, &priv->user.mem); - for (i = 0; i < NVE0_FIFO_ENGINE_NUM; i++) { - nouveau_gpuobj_ref(NULL, &priv->engine[i].playlist[0]); + for (i = 0; i < ARRAY_SIZE(priv->engine); i++) { nouveau_gpuobj_ref(NULL, &priv->engine[i].playlist[1]); + nouveau_gpuobj_ref(NULL, &priv->engine[i].playlist[0]); } - dev_priv->eng[engine] = NULL; - kfree(priv); + nouveau_fifo_destroy(&priv->base); } -int -nve0_fifo_create(struct drm_device *dev) +static int +nve0_fifo_init(struct nouveau_object *object) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nve0_fifo_priv *priv; - int ret; + struct nve0_fifo_priv *priv = (void *)object; + int ret, i; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + ret = nouveau_fifo_init(&priv->base); + if (ret) + return ret; - priv->base.base.destroy = nve0_fifo_destroy; - priv->base.base.init = nve0_fifo_init; - priv->base.base.fini = nve0_fifo_fini; - priv->base.base.context_new = nve0_fifo_context_new; - priv->base.base.context_del = nve0_fifo_context_del; - priv->base.channels = 4096; - dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base; + /* enable all available PSUBFIFOs */ + nv_wr32(priv, 0x000204, 0xffffffff); + priv->spoon_nr = hweight32(nv_rd32(priv, 0x000204)); + nv_debug(priv, "%d subfifo(s)\n", priv->spoon_nr); - ret = nouveau_gpuobj_new(dev, NULL, priv->base.channels * 512, 0x1000, - NVOBJ_FLAG_ZERO_ALLOC, &priv->user.mem); - if (ret) - goto error; + /* PSUBFIFO[n] */ + for (i = 0; i < priv->spoon_nr; i++) { + nv_mask(priv, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000); + nv_wr32(priv, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */ + nv_wr32(priv, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTREN */ + } - ret = nouveau_gpuobj_map_bar(priv->user.mem, NV_MEM_ACCESS_RW, - &priv->user.bar); - if (ret) - goto error; + nv_wr32(priv, 0x002254, 0x10000000 | priv->user.bar.offset >> 12); - nouveau_irq_register(dev, 8, nve0_fifo_isr); -error: - if (ret) - priv->base.base.destroy(dev, NVOBJ_ENGINE_FIFO); - return ret; + nv_wr32(priv, 0x002a00, 0xffffffff); + nv_wr32(priv, 0x002100, 0xffffffff); + nv_wr32(priv, 0x002140, 0xbfffffff); + return 0; } + +struct nouveau_oclass +nve0_fifo_oclass = { + .handle = NV_ENGINE(FIFO, 0xe0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nve0_fifo_ctor, + .dtor = nve0_fifo_dtor, + .init = nve0_fifo_init, + .fini = _nouveau_fifo_fini, + }, +}; |