Commit ebb945a9 authored by Ben Skeggs's avatar Ben Skeggs

drm/nouveau: port all engines to new engine module format

This is a HUGE commit, but it's not nearly as bad as it looks - any problems
can be isolated to a particular chipset and engine combination.  It was
simply too difficult to port each one at a time, the compat layers are
*already* ridiculous.

Most of the changes here are simply to the glue, the process for each of the
engine modules was to start with a standard skeleton and copy+paste the old
code into the appropriate places, fixing up variable names etc as needed.

v2: Marcin Slusarz <marcin.slusarz@gmail.com>
- fix find/replace bug in license header

v3: Ben Skeggs <bskeggs@redhat.com>
- bump indirect pushbuf size to 8KiB, 4KiB barely enough for userspace and
  left no space for kernel's requirements during GEM pushbuf submission.
- fix duplicate assignments noticed by clang

v4: Marcin Slusarz <marcin.slusarz@gmail.com>
- add sparse annotations to nv04_fifo_pause/nv04_fifo_start
- use ioread32_native/iowrite32_native for fifo control registers

v5: Ben Skeggs <bskeggs@redhat.com>
- rebase on v3.6-rc4, modified to keep copy engine fix intact
- nv10/fence: unmap fence bo before destroying
- fixed fermi regression when using nvidia gr fuc
- fixed typo in supported dma_mask checking
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent ac1499d9
...@@ -4,9 +4,11 @@ ...@@ -4,9 +4,11 @@
ccflags-y := -Iinclude/drm -DCONFIG_NOUVEAU_DEBUG=7 -DCONFIG_NOUVEAU_DEBUG_DEFAULT=3 ccflags-y := -Iinclude/drm -DCONFIG_NOUVEAU_DEBUG=7 -DCONFIG_NOUVEAU_DEBUG_DEFAULT=3
ccflags-y += -I$(src)/core/include ccflags-y += -I$(src)/core/include
ccflags-y += -I$(src)/core
ccflags-y += -I$(src) ccflags-y += -I$(src)
nouveau-y := core/core/client.o nouveau-y := core/core/client.o
nouveau-y += core/core/engctx.o
nouveau-y += core/core/engine.o nouveau-y += core/core/engine.o
nouveau-y += core/core/enum.o nouveau-y += core/core/enum.o
nouveau-y += core/core/gpuobj.o nouveau-y += core/core/gpuobj.o
...@@ -90,12 +92,20 @@ nouveau-y += core/subdev/vm/nv44.o ...@@ -90,12 +92,20 @@ nouveau-y += core/subdev/vm/nv44.o
nouveau-y += core/subdev/vm/nv50.o nouveau-y += core/subdev/vm/nv50.o
nouveau-y += core/subdev/vm/nvc0.o nouveau-y += core/subdev/vm/nvc0.o
nouveau-y += core/engine/dmaobj/base.o
nouveau-y += core/engine/dmaobj/nv04.o
nouveau-y += core/engine/dmaobj/nv50.o
nouveau-y += core/engine/dmaobj/nvc0.o
nouveau-y += core/engine/bsp/nv84.o nouveau-y += core/engine/bsp/nv84.o
nouveau-y += core/engine/copy/nva3.o nouveau-y += core/engine/copy/nva3.o
nouveau-y += core/engine/copy/nvc0.o nouveau-y += core/engine/copy/nvc0.o
nouveau-y += core/engine/crypt/nv84.o nouveau-y += core/engine/crypt/nv84.o
nouveau-y += core/engine/crypt/nv98.o nouveau-y += core/engine/crypt/nv98.o
nouveau-y += core/engine/disp/nv04.o
nouveau-y += core/engine/disp/nv50.o
nouveau-y += core/engine/disp/nvd0.o
nouveau-y += core/engine/disp/vga.o nouveau-y += core/engine/disp/vga.o
nouveau-y += core/engine/fifo/base.o
nouveau-y += core/engine/fifo/nv04.o nouveau-y += core/engine/fifo/nv04.o
nouveau-y += core/engine/fifo/nv10.o nouveau-y += core/engine/fifo/nv10.o
nouveau-y += core/engine/fifo/nv17.o nouveau-y += core/engine/fifo/nv17.o
...@@ -111,41 +121,82 @@ nouveau-y += core/engine/graph/ctxnve0.o ...@@ -111,41 +121,82 @@ nouveau-y += core/engine/graph/ctxnve0.o
nouveau-y += core/engine/graph/nv04.o nouveau-y += core/engine/graph/nv04.o
nouveau-y += core/engine/graph/nv10.o nouveau-y += core/engine/graph/nv10.o
nouveau-y += core/engine/graph/nv20.o nouveau-y += core/engine/graph/nv20.o
nouveau-y += core/engine/graph/nv25.o
nouveau-y += core/engine/graph/nv2a.o
nouveau-y += core/engine/graph/nv30.o
nouveau-y += core/engine/graph/nv34.o
nouveau-y += core/engine/graph/nv35.o
nouveau-y += core/engine/graph/nv40.o nouveau-y += core/engine/graph/nv40.o
nouveau-y += core/engine/graph/nv50.o nouveau-y += core/engine/graph/nv50.o
nouveau-y += core/engine/graph/nvc0.o nouveau-y += core/engine/graph/nvc0.o
nouveau-y += core/engine/graph/nve0.o nouveau-y += core/engine/graph/nve0.o
nouveau-y += core/engine/mpeg/nv31.o nouveau-y += core/engine/mpeg/nv31.o
nouveau-y += core/engine/mpeg/nv40.o
nouveau-y += core/engine/mpeg/nv50.o nouveau-y += core/engine/mpeg/nv50.o
nouveau-y += core/engine/mpeg/nv84.o
nouveau-y += core/engine/ppp/nv98.o nouveau-y += core/engine/ppp/nv98.o
nouveau-y += core/engine/software/nv04.o
nouveau-y += core/engine/software/nv10.o
nouveau-y += core/engine/software/nv50.o
nouveau-y += core/engine/software/nvc0.o
nouveau-y += core/engine/vp/nv84.o nouveau-y += core/engine/vp/nv84.o
nouveau-y += nouveau_drm.o nouveau_compat.o \ # drm/compat - will go away
nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nouveau-y += nouveau_compat.o nouveau_revcompat.o
nouveau_gpuobj.o nouveau_irq.o nouveau_notifier.o \
nouveau_sgdma.o nouveau_dma.o nouveau_util.o \ # drm/core
nouveau_bo.o nouveau_fence.o nouveau_gem.o nouveau_ttm.o \ nouveau-y += nouveau_drm.o nouveau_chan.o nouveau_dma.o nouveau_fence.o
nouveau_hw.o nouveau_calc.o \ nouveau-y += nouveau_agp.o
nouveau_display.o nouveau_connector.o nouveau_fbcon.o \ nouveau-y += nouveau_ttm.o nouveau_sgdma.o nouveau_bo.o nouveau_gem.o
nouveau_hdmi.o nouveau_dp.o \
nouveau_pm.o nouveau_volt.o nouveau_perf.o nouveau_temp.o \ nouveau-y += nouveau_abi16.o
nouveau_mxm.o nouveau_agp.o \ nouveau-y += nv04_fence.o nv10_fence.o nv50_fence.o nv84_fence.o nvc0_fence.o
nouveau_abi16.o \
nouveau_bios.o \ # drm/kms/common
nv04_fence.o nv10_fence.o nv50_fence.o nv84_fence.o nvc0_fence.o \ nouveau-y += nouveau_fbcon.o
nv04_software.o nv50_software.o nvc0_software.o \
nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \ # drm/kms/nv04:nv50
nv04_crtc.o nv04_display.o nv04_cursor.o \ nouveau-y += nv04_fbcon.o
nv50_evo.o nv50_crtc.o nv50_dac.o nv50_sor.o \
nv50_cursor.o nv50_display.o \ # drm/kms/nv50:nvd9
nvd0_display.o \ nouveau-y += nv50_fbcon.o nvc0_fbcon.o
nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o \
nv04_pm.o nv40_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o \ # drm/kms/nvd9-
nouveau_prime.o
##
nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o ## unported bits below
##
# drm/core
nouveau-y += nouveau_drv.o nouveau_state.o nouveau_irq.o
nouveau-y += nouveau_prime.o
# drm/kms/bios
nouveau-y += nouveau_mxm.o nouveau_bios.o
# drm/kms/common
nouveau-y += nouveau_display.o nouveau_connector.o
nouveau-y += nouveau_hdmi.o nouveau_dp.o
# drm/kms/nv04:nv50
nouveau-y += nouveau_hw.o nouveau_calc.o
nouveau-y += nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o
nouveau-y += nv04_crtc.o nv04_display.o nv04_cursor.o
# drm/kms/nv50-
nouveau-y += nv50_display.o nvd0_display.o
nouveau-y += nv50_crtc.o nv50_dac.o nv50_sor.o nv50_cursor.o
nouveau-y += nv50_evo.o
# drm/pm
nouveau-y += nouveau_pm.o nouveau_volt.o nouveau_perf.o nouveau_temp.o
nouveau-y += nv04_pm.o nv40_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o
nouveau-y += nouveau_mem.o
# optional stuff
nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o
nouveau-$(CONFIG_ACPI) += nouveau_acpi.o nouveau-$(CONFIG_ACPI) += nouveau_acpi.o
obj-$(CONFIG_DRM_NOUVEAU)+= nouveau.o obj-$(CONFIG_DRM_NOUVEAU)+= nouveau.o
/* /*
* Copyright 2010 Red Hat Inc. * Copyright 2012 Red Hat Inc.
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
...@@ -18,289 +18,92 @@ ...@@ -18,289 +18,92 @@
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/ */
#include "drmP.h" #include <core/object.h>
#include "nouveau_drv.h"
#include <core/ramht.h> #include <core/ramht.h>
#include <core/math.h>
#include <subdev/bar.h>
static u32 static u32
nouveau_ramht_hash_handle(struct nouveau_channel *chan, u32 handle) nouveau_ramht_hash(struct nouveau_ramht *ramht, int chid, u32 handle)
{ {
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_ramht *ramht = chan->ramht;
u32 hash = 0; u32 hash = 0;
int i;
NV_DEBUG(dev, "ch%d handle=0x%08x\n", chan->id, handle); while (handle) {
for (i = 32; i > 0; i -= ramht->bits) {
hash ^= (handle & ((1 << ramht->bits) - 1)); hash ^= (handle & ((1 << ramht->bits) - 1));
handle >>= ramht->bits; handle >>= ramht->bits;
} }
if (dev_priv->card_type < NV_50) hash ^= chid << (ramht->bits - 4);
hash ^= chan->id << (ramht->bits - 4); hash = hash << 3;
hash <<= 3;
NV_DEBUG(dev, "hash=0x%08x\n", hash);
return hash; return hash;
} }
static int
nouveau_ramht_entry_valid(struct drm_device *dev, struct nouveau_gpuobj *ramht,
u32 offset)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
u32 ctx = nv_ro32(ramht, offset + 4);
if (dev_priv->card_type < NV_40)
return ((ctx & NV_RAMHT_CONTEXT_VALID) != 0);
return (ctx != 0);
}
static int
nouveau_ramht_entry_same_channel(struct nouveau_channel *chan,
struct nouveau_gpuobj *ramht, u32 offset)
{
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
u32 ctx = nv_ro32(ramht, offset + 4);
if (dev_priv->card_type >= NV_50)
return true;
else if (dev_priv->card_type >= NV_40)
return chan->id ==
((ctx >> NV40_RAMHT_CONTEXT_CHANNEL_SHIFT) & 0x1f);
else
return chan->id ==
((ctx >> NV_RAMHT_CONTEXT_CHANNEL_SHIFT) & 0x1f);
}
int int
nouveau_ramht_insert(struct nouveau_channel *chan, u32 handle, nouveau_ramht_insert(struct nouveau_ramht *ramht, int chid,
struct nouveau_gpuobj *gpuobj) u32 handle, u32 context)
{ {
struct drm_device *dev = chan->dev; struct nouveau_bar *bar = nouveau_bar(ramht);
struct drm_nouveau_private *dev_priv = dev->dev_private; u32 co, ho;
struct nouveau_ramht_entry *entry;
struct nouveau_gpuobj *ramht = chan->ramht->gpuobj;
unsigned long flags;
u32 ctx, co, ho;
if (nouveau_ramht_find(chan, handle))
return -EEXIST;
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
entry->channel = chan;
entry->gpuobj = NULL;
entry->handle = handle;
nouveau_gpuobj_ref(gpuobj, &entry->gpuobj);
if (dev_priv->card_type < NV_40) {
ctx = NV_RAMHT_CONTEXT_VALID | (gpuobj->addr >> 4) |
(chan->id << NV_RAMHT_CONTEXT_CHANNEL_SHIFT) |
(gpuobj->engine << NV_RAMHT_CONTEXT_ENGINE_SHIFT);
} else
if (dev_priv->card_type < NV_50) {
ctx = (gpuobj->addr >> 4) |
(chan->id << NV40_RAMHT_CONTEXT_CHANNEL_SHIFT) |
(gpuobj->engine << NV40_RAMHT_CONTEXT_ENGINE_SHIFT);
} else {
if (gpuobj->engine == NVOBJ_ENGINE_DISPLAY) {
ctx = (gpuobj->node->offset << 10) |
(chan->id << 28) |
chan->id; /* HASH_TAG */
} else {
ctx = (gpuobj->node->offset >> 4) |
((gpuobj->engine <<
NV40_RAMHT_CONTEXT_ENGINE_SHIFT));
}
}
spin_lock_irqsave(&chan->ramht->lock, flags);
list_add(&entry->head, &chan->ramht->entries);
co = ho = nouveau_ramht_hash_handle(chan, handle); co = ho = nouveau_ramht_hash(ramht, chid, handle);
do { do {
if (!nouveau_ramht_entry_valid(dev, ramht, co)) { if (!nv_ro32(ramht, co + 4)) {
NV_DEBUG(dev,
"insert ch%d 0x%08x: h=0x%08x, c=0x%08x\n",
chan->id, co, handle, ctx);
nv_wo32(ramht, co + 0, handle); nv_wo32(ramht, co + 0, handle);
nv_wo32(ramht, co + 4, ctx); nv_wo32(ramht, co + 4, context);
if (bar)
spin_unlock_irqrestore(&chan->ramht->lock, flags); bar->flush(bar);
nvimem_flush(dev); return co;
return 0;
} }
NV_DEBUG(dev, "collision ch%d 0x%08x: h=0x%08x\n",
chan->id, co, nv_ro32(ramht, co));
co += 8; co += 8;
if (co >= ramht->size) if (co >= nv_gpuobj(ramht)->size)
co = 0; co = 0;
} while (co != ho); } while (co != ho);
NV_ERROR(dev, "RAMHT space exhausted. ch=%d\n", chan->id);
list_del(&entry->head);
spin_unlock_irqrestore(&chan->ramht->lock, flags);
kfree(entry);
return -ENOMEM; return -ENOMEM;
} }
static struct nouveau_ramht_entry * void
nouveau_ramht_remove_entry(struct nouveau_channel *chan, u32 handle) nouveau_ramht_remove(struct nouveau_ramht *ramht, int cookie)
{
struct nouveau_ramht *ramht = chan ? chan->ramht : NULL;
struct nouveau_ramht_entry *entry;
unsigned long flags;
if (!ramht)
return NULL;
spin_lock_irqsave(&ramht->lock, flags);
list_for_each_entry(entry, &ramht->entries, head) {
if (entry->channel == chan &&
(!handle || entry->handle == handle)) {
list_del(&entry->head);
spin_unlock_irqrestore(&ramht->lock, flags);
return entry;
}
}
spin_unlock_irqrestore(&ramht->lock, flags);
return NULL;
}
static void
nouveau_ramht_remove_hash(struct nouveau_channel *chan, u32 handle)
{
struct drm_device *dev = chan->dev;
struct nouveau_gpuobj *ramht = chan->ramht->gpuobj;
unsigned long flags;
u32 co, ho;
spin_lock_irqsave(&chan->ramht->lock, flags);
co = ho = nouveau_ramht_hash_handle(chan, handle);
do {
if (nouveau_ramht_entry_valid(dev, ramht, co) &&
nouveau_ramht_entry_same_channel(chan, ramht, co) &&
(handle == nv_ro32(ramht, co))) {
NV_DEBUG(dev,
"remove ch%d 0x%08x: h=0x%08x, c=0x%08x\n",
chan->id, co, handle, nv_ro32(ramht, co + 4));
nv_wo32(ramht, co + 0, 0x00000000);
nv_wo32(ramht, co + 4, 0x00000000);
nvimem_flush(dev);
goto out;
}
co += 8;
if (co >= ramht->size)
co = 0;
} while (co != ho);
NV_ERROR(dev, "RAMHT entry not found. ch=%d, handle=0x%08x\n",
chan->id, handle);
out:
spin_unlock_irqrestore(&chan->ramht->lock, flags);
}
int
nouveau_ramht_remove(struct nouveau_channel *chan, u32 handle)
{ {
struct nouveau_ramht_entry *entry; struct nouveau_bar *bar = nouveau_bar(ramht);
nv_wo32(ramht, cookie + 0, 0x00000000);
entry = nouveau_ramht_remove_entry(chan, handle); nv_wo32(ramht, cookie + 4, 0x00000000);
if (!entry) if (bar)
return -ENOENT; bar->flush(bar);
nouveau_ramht_remove_hash(chan, entry->handle);
nouveau_gpuobj_ref(NULL, &entry->gpuobj);
kfree(entry);
return 0;
} }
struct nouveau_gpuobj * static struct nouveau_oclass
nouveau_ramht_find(struct nouveau_channel *chan, u32 handle) nouveau_ramht_oclass = {
{ .handle = 0x0000abcd,
struct nouveau_ramht *ramht = chan->ramht; .ofuncs = &(struct nouveau_ofuncs) {
struct nouveau_ramht_entry *entry; .ctor = NULL,
struct nouveau_gpuobj *gpuobj = NULL; .dtor = _nouveau_gpuobj_dtor,
unsigned long flags; .init = _nouveau_gpuobj_init,
.fini = _nouveau_gpuobj_fini,
if (unlikely(!chan->ramht)) .rd32 = _nouveau_gpuobj_rd32,
return NULL; .wr32 = _nouveau_gpuobj_wr32,
},
spin_lock_irqsave(&ramht->lock, flags); };
list_for_each_entry(entry, &chan->ramht->entries, head) {
if (entry->channel == chan && entry->handle == handle) {
gpuobj = entry->gpuobj;
break;
}
}
spin_unlock_irqrestore(&ramht->lock, flags);
return gpuobj;
}
int int
nouveau_ramht_new(struct drm_device *dev, struct nouveau_gpuobj *gpuobj, nouveau_ramht_new(struct nouveau_object *parent, struct nouveau_object *pargpu,
struct nouveau_ramht **pramht) u32 size, u32 align, struct nouveau_ramht **pramht)
{ {
struct nouveau_ramht *ramht; struct nouveau_ramht *ramht;
int ret;
ramht = kzalloc(sizeof(*ramht), GFP_KERNEL); ret = nouveau_gpuobj_create(parent, parent->engine ?
if (!ramht) parent->engine : parent, /* <nv50 ramht */
return -ENOMEM; &nouveau_ramht_oclass, 0, pargpu, size,
align, NVOBJ_FLAG_ZERO_ALLOC, &ramht);
ramht->dev = dev;
kref_init(&ramht->refcount);
ramht->bits = drm_order(gpuobj->size / 8);
INIT_LIST_HEAD(&ramht->entries);
spin_lock_init(&ramht->lock);
nouveau_gpuobj_ref(gpuobj, &ramht->gpuobj);
*pramht = ramht; *pramht = ramht;
return 0; if (ret)
} return ret;
static void ramht->bits = log2i(nv_gpuobj(ramht)->size >> 3);
nouveau_ramht_del(struct kref *ref) return 0;
{
struct nouveau_ramht *ramht =
container_of(ref, struct nouveau_ramht, refcount);
nouveau_gpuobj_ref(NULL, &ramht->gpuobj);
kfree(ramht);
}
void
nouveau_ramht_ref(struct nouveau_ramht *ref, struct nouveau_ramht **ptr,
struct nouveau_channel *chan)
{
struct nouveau_ramht_entry *entry;
struct nouveau_ramht *ramht;
if (ref)
kref_get(&ref->refcount);
ramht = *ptr;
if (ramht) {
while ((entry = nouveau_ramht_remove_entry(chan, 0))) {
nouveau_ramht_remove_hash(chan, entry->handle);
nouveau_gpuobj_ref(NULL, &entry->gpuobj);
kfree(entry);
}
kref_put(&ramht->refcount, nouveau_ramht_del);
}
*ptr = ref;
} }
/* /*
* Copyright 2011 Red Hat Inc. * Copyright 2012 Red Hat Inc.
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
...@@ -22,61 +22,154 @@ ...@@ -22,61 +22,154 @@
* Authors: Ben Skeggs * Authors: Ben Skeggs
*/ */
#include "drmP.h" #include <core/os.h>
#include "nouveau_drv.h" #include <core/class.h>
#include "nouveau_util.h" #include <core/engctx.h>
#include <core/ramht.h>
/*XXX: This stub is currently used on NV98+ also, as soon as this becomes #include <engine/bsp.h>
* more than just an enable/disable stub this needs to be split out to
* nv98_bsp.c... struct nv84_bsp_priv {
*/ struct nouveau_bsp base;
};
struct nv84_bsp_engine { struct nv84_bsp_chan {
struct nouveau_exec_engine base; struct nouveau_bsp_chan base;
}; };
/*******************************************************************************
* BSP object classes
******************************************************************************/
static struct nouveau_oclass
nv84_bsp_sclass[] = {
{},
};
/*******************************************************************************
* BSP context
******************************************************************************/
static int static int
nv84_bsp_fini(struct drm_device *dev, int engine, bool suspend) nv84_bsp_context_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{ {
if (!(nv_rd32(dev, 0x000200) & 0x00008000)) struct nv84_bsp_chan *priv;
return 0; int ret;
ret = nouveau_bsp_context_create(parent, engine, oclass, NULL,
0, 0, 0, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
nv_mask(dev, 0x000200, 0x00008000, 0x00000000);
return 0; return 0;
} }
static void
nv84_bsp_context_dtor(struct nouveau_object *object)
{
struct nv84_bsp_chan *priv = (void *)object;
nouveau_bsp_context_destroy(&priv->base);
}
static int static int
nv84_bsp_init(struct drm_device *dev, int engine) nv84_bsp_context_init(struct nouveau_object *object)
{ {
nv_mask(dev, 0x000200, 0x00008000, 0x00000000); struct nv84_bsp_chan *priv = (void *)object;
nv_mask(dev, 0x000200, 0x00008000, 0x00008000); int ret;
ret = nouveau_bsp_context_init(&priv->base);
if (ret)
return ret;
return 0; return 0;
} }
static int
nv84_bsp_context_fini(struct nouveau_object *object, bool suspend)
{
struct nv84_bsp_chan *priv = (void *)object;
return nouveau_bsp_context_fini(&priv->base, suspend);
}
static struct nouveau_oclass
nv84_bsp_cclass = {
.handle = NV_ENGCTX(BSP, 0x84),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv84_bsp_context_ctor,
.dtor = nv84_bsp_context_dtor,
.init = nv84_bsp_context_init,
.fini = nv84_bsp_context_fini,
.rd32 = _nouveau_bsp_context_rd32,
.wr32 = _nouveau_bsp_context_wr32,
},
};
/*******************************************************************************
* BSP engine/subdev functions
******************************************************************************/
static void static void
nv84_bsp_destroy(struct drm_device *dev, int engine) nv84_bsp_intr(struct nouveau_subdev *subdev)
{ {
struct nv84_bsp_engine *pbsp = nv_engine(dev, engine); }
NVOBJ_ENGINE_DEL(dev, BSP); static int
nv84_bsp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
struct nv84_bsp_priv *priv;
int ret;
ret = nouveau_bsp_create(parent, engine, oclass, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
kfree(pbsp); nv_subdev(priv)->unit = 0x04008000;
nv_subdev(priv)->intr = nv84_bsp_intr;
nv_engine(priv)->cclass = &nv84_bsp_cclass;
nv_engine(priv)->sclass = nv84_bsp_sclass;
return 0;
} }
int static void
nv84_bsp_create(struct drm_device *dev) nv84_bsp_dtor(struct nouveau_object *object)
{ {
struct nv84_bsp_engine *pbsp; struct nv84_bsp_priv *priv = (void *)object;
nouveau_bsp_destroy(&priv->base);
}
pbsp = kzalloc(sizeof(*pbsp), GFP_KERNEL); static int
if (!pbsp) nv84_bsp_init(struct nouveau_object *object)
return -ENOMEM; {
struct nv84_bsp_priv *priv = (void *)object;
int ret;
pbsp->base.destroy = nv84_bsp_destroy; ret = nouveau_bsp_init(&priv->base);
pbsp->base.init = nv84_bsp_init; if (ret)
pbsp->base.fini = nv84_bsp_fini; return ret;
NVOBJ_ENGINE_ADD(dev, BSP, &pbsp->base);
return 0; return 0;
} }
static int
nv84_bsp_fini(struct nouveau_object *object, bool suspend)
{
struct nv84_bsp_priv *priv = (void *)object;
return nouveau_bsp_fini(&priv->base, suspend);
}
struct nouveau_oclass
nv84_bsp_oclass = {
.handle = NV_ENGINE(BSP, 0x84),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv84_bsp_ctor,
.dtor = nv84_bsp_dtor,
.init = nv84_bsp_init,
.fini = nv84_bsp_fini,
},
};
/* /*
* Copyright 2011 Red Hat Inc. * Copyright 2012 Red Hat Inc.
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
...@@ -22,112 +22,75 @@ ...@@ -22,112 +22,75 @@
* Authors: Ben Skeggs * Authors: Ben Skeggs
*/ */
#include <linux/firmware.h> #include <core/os.h>
#include "drmP.h" #include <core/enum.h>
#include "nouveau_drv.h" #include <core/class.h>
#include "nouveau_util.h" #include <core/engctx.h>
#include <core/ramht.h>
#include "fuc/nva3.fuc.h"
struct nva3_copy_engine {
struct nouveau_exec_engine base;
};
static int
nva3_copy_context_new(struct nouveau_channel *chan, int engine)
{
struct drm_device *dev = chan->dev;
struct nouveau_gpuobj *ramin = chan->ramin;
struct nouveau_gpuobj *ctx = NULL;
int ret;
NV_DEBUG(dev, "ch%d\n", chan->id);
ret = nouveau_gpuobj_new(dev, chan, 256, 0, NVOBJ_FLAG_ZERO_ALLOC | #include <subdev/fb.h>
NVOBJ_FLAG_ZERO_FREE, &ctx); #include <subdev/vm.h>
if (ret)
return ret;
nv_wo32(ramin, 0xc0, 0x00190000); #include <engine/copy.h>
nv_wo32(ramin, 0xc4, ctx->addr + ctx->size - 1);
nv_wo32(ramin, 0xc8, ctx->addr);
nv_wo32(ramin, 0xcc, 0x00000000);
nv_wo32(ramin, 0xd0, 0x00000000);
nv_wo32(ramin, 0xd4, 0x00000000);
nvimem_flush(dev);
nvvm_engref(chan->vm, engine, 1); #include "fuc/nva3.fuc.h"
chan->engctx[engine] = ctx;
return 0;
}
static int
nva3_copy_object_new(struct nouveau_channel *chan, int engine,
u32 handle, u16 class)
{
struct nouveau_gpuobj *ctx = chan->engctx[engine];
/* fuc engine doesn't need an object, our ramht code does.. */ struct nva3_copy_priv {
ctx->engine = 3; struct nouveau_copy base;
ctx->class = class; };
return nouveau_ramht_insert(chan, handle, ctx);
}
static void struct nva3_copy_chan {
nva3_copy_context_del(struct nouveau_channel *chan, int engine) struct nouveau_copy_chan base;
{ };
struct nouveau_gpuobj *ctx = chan->engctx[engine];
int i;
for (i = 0xc0; i <= 0xd4; i += 4) /*******************************************************************************
nv_wo32(chan->ramin, i, 0x00000000); * Copy object classes
******************************************************************************/
nvvm_engref(chan->vm, engine, -1); static struct nouveau_oclass
nouveau_gpuobj_ref(NULL, &ctx); nva3_copy_sclass[] = {
chan->engctx[engine] = ctx; { 0x85b5, &nouveau_object_ofuncs },
} {}
};
static void /*******************************************************************************
nva3_copy_tlb_flush(struct drm_device *dev, int engine) * PCOPY context
{ ******************************************************************************/
nv50_vm_flush_engine(dev, 0x0d);
}
static int static int
nva3_copy_init(struct drm_device *dev, int engine) nva3_copy_context_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{ {
int i; struct nva3_copy_chan *priv;
int ret;
nv_mask(dev, 0x000200, 0x00002000, 0x00000000);
nv_mask(dev, 0x000200, 0x00002000, 0x00002000);
nv_wr32(dev, 0x104014, 0xffffffff); /* disable all interrupts */
/* upload ucode */
nv_wr32(dev, 0x1041c0, 0x01000000);
for (i = 0; i < sizeof(nva3_pcopy_data) / 4; i++)
nv_wr32(dev, 0x1041c4, nva3_pcopy_data[i]);
nv_wr32(dev, 0x104180, 0x01000000); ret = nouveau_copy_context_create(parent, engine, oclass, NULL, 256, 0,
for (i = 0; i < sizeof(nva3_pcopy_code) / 4; i++) { NVOBJ_FLAG_ZERO_ALLOC, &priv);
if ((i & 0x3f) == 0) *pobject = nv_object(priv);
nv_wr32(dev, 0x104188, i >> 6); if (ret)
nv_wr32(dev, 0x104184, nva3_pcopy_code[i]); return ret;
}
/* start it running */
nv_wr32(dev, 0x10410c, 0x00000000);
nv_wr32(dev, 0x104104, 0x00000000); /* ENTRY */
nv_wr32(dev, 0x104100, 0x00000002); /* TRIGGER */
return 0; return 0;
} }
static int static struct nouveau_oclass
nva3_copy_fini(struct drm_device *dev, int engine, bool suspend) nva3_copy_cclass = {
{ .handle = NV_ENGCTX(COPY0, 0xa3),
nv_mask(dev, 0x104048, 0x00000003, 0x00000000); .ofuncs = &(struct nouveau_ofuncs) {
nv_wr32(dev, 0x104014, 0xffffffff); .ctor = nva3_copy_context_ctor,
return 0; .dtor = _nouveau_copy_context_dtor,
} .init = _nouveau_copy_context_init,
.fini = _nouveau_copy_context_fini,
.rd32 = _nouveau_copy_context_rd32,
.wr32 = _nouveau_copy_context_wr32,
},
};
/*******************************************************************************
* PCOPY engine/subdev functions
******************************************************************************/
static struct nouveau_enum nva3_copy_isr_error_name[] = { static struct nouveau_enum nva3_copy_isr_error_name[] = {
{ 0x0001, "ILLEGAL_MTHD" }, { 0x0001, "ILLEGAL_MTHD" },
...@@ -137,65 +100,114 @@ static struct nouveau_enum nva3_copy_isr_error_name[] = { ...@@ -137,65 +100,114 @@ static struct nouveau_enum nva3_copy_isr_error_name[] = {
}; };
static void static void
nva3_copy_isr(struct drm_device *dev) nva3_copy_intr(struct nouveau_subdev *subdev)
{ {
u32 dispatch = nv_rd32(dev, 0x10401c); struct nva3_copy_priv *priv = (void *)subdev;
u32 stat = nv_rd32(dev, 0x104008) & dispatch & ~(dispatch >> 16); u32 dispatch = nv_rd32(priv, 0x10401c);
u32 inst = nv_rd32(dev, 0x104050) & 0x3fffffff; u32 stat = nv_rd32(priv, 0x104008) & dispatch & ~(dispatch >> 16);
u32 ssta = nv_rd32(dev, 0x104040) & 0x0000ffff; u32 inst = nv_rd32(priv, 0x104050) & 0x3fffffff;
u32 addr = nv_rd32(dev, 0x104040) >> 16; u32 ssta = nv_rd32(priv, 0x104040) & 0x0000ffff;
u32 addr = nv_rd32(priv, 0x104040) >> 16;
u32 mthd = (addr & 0x07ff) << 2; u32 mthd = (addr & 0x07ff) << 2;
u32 subc = (addr & 0x3800) >> 11; u32 subc = (addr & 0x3800) >> 11;
u32 data = nv_rd32(dev, 0x104044); u32 data = nv_rd32(priv, 0x104044);
int chid = nv50_graph_isr_chid(dev, inst);
if (stat & 0x00000040) { if (stat & 0x00000040) {
NV_INFO(dev, "PCOPY: DISPATCH_ERROR ["); nv_error(priv, "DISPATCH_ERROR [");
nouveau_enum_print(nva3_copy_isr_error_name, ssta); nouveau_enum_print(nva3_copy_isr_error_name, ssta);
printk("] ch %d [0x%08x] subc %d mthd 0x%04x data 0x%08x\n", printk("] ch 0x%08x subc %d mthd 0x%04x data 0x%08x\n",
chid, inst, subc, mthd, data); inst, subc, mthd, data);
nv_wr32(dev, 0x104004, 0x00000040); nv_wr32(priv, 0x104004, 0x00000040);
stat &= ~0x00000040; stat &= ~0x00000040;
} }
if (stat) { if (stat) {
NV_INFO(dev, "PCOPY: unhandled intr 0x%08x\n", stat); nv_error(priv, "unhandled intr 0x%08x\n", stat);
nv_wr32(dev, 0x104004, stat); nv_wr32(priv, 0x104004, stat);
} }
nv50_fb_vm_trap(dev, 1);
nv50_fb_trap(nouveau_fb(priv), 1);
} }
static void static int
nva3_copy_destroy(struct drm_device *dev, int engine) nva3_copy_tlb_flush(struct nouveau_engine *engine)
{ {
struct nva3_copy_engine *pcopy = nv_engine(dev, engine); nv50_vm_flush_engine(&engine->base, 0x0d);
return 0;
}
nouveau_irq_unregister(dev, 22); static int
nva3_copy_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
bool enable = (nv_device(parent)->chipset != 0xaf);
struct nva3_copy_priv *priv;
int ret;
NVOBJ_ENGINE_DEL(dev, COPY0); ret = nouveau_copy_create(parent, engine, oclass, enable, 0, &priv);
kfree(pcopy); *pobject = nv_object(priv);
if (ret)
return ret;
nv_subdev(priv)->unit = 0x00802000;
nv_subdev(priv)->intr = nva3_copy_intr;
nv_engine(priv)->cclass = &nva3_copy_cclass;
nv_engine(priv)->sclass = nva3_copy_sclass;
nv_engine(priv)->tlb_flush = nva3_copy_tlb_flush;
return 0;
} }
int static int
nva3_copy_create(struct drm_device *dev) nva3_copy_init(struct nouveau_object *object)
{ {
struct nva3_copy_engine *pcopy; struct nva3_copy_priv *priv = (void *)object;
int ret, i;
pcopy = kzalloc(sizeof(*pcopy), GFP_KERNEL); ret = nouveau_copy_init(&priv->base);
if (!pcopy) if (ret)
return -ENOMEM; return ret;
pcopy->base.destroy = nva3_copy_destroy; /* disable all interrupts */
pcopy->base.init = nva3_copy_init; nv_wr32(priv, 0x104014, 0xffffffff);
pcopy->base.fini = nva3_copy_fini;
pcopy->base.context_new = nva3_copy_context_new;
pcopy->base.context_del = nva3_copy_context_del;
pcopy->base.object_new = nva3_copy_object_new;
pcopy->base.tlb_flush = nva3_copy_tlb_flush;
nouveau_irq_register(dev, 22, nva3_copy_isr); /* upload ucode */
nv_wr32(priv, 0x1041c0, 0x01000000);
for (i = 0; i < sizeof(nva3_pcopy_data) / 4; i++)
nv_wr32(priv, 0x1041c4, nva3_pcopy_data[i]);
nv_wr32(priv, 0x104180, 0x01000000);
for (i = 0; i < sizeof(nva3_pcopy_code) / 4; i++) {
if ((i & 0x3f) == 0)
nv_wr32(priv, 0x104188, i >> 6);
nv_wr32(priv, 0x104184, nva3_pcopy_code[i]);
}
NVOBJ_ENGINE_ADD(dev, COPY0, &pcopy->base); /* start it running */
NVOBJ_CLASS(dev, 0x85b5, COPY0); nv_wr32(priv, 0x10410c, 0x00000000);
nv_wr32(priv, 0x104104, 0x00000000); /* ENTRY */
nv_wr32(priv, 0x104100, 0x00000002); /* TRIGGER */
return 0; return 0;
} }
static int
nva3_copy_fini(struct nouveau_object *object, bool suspend)
{
struct nva3_copy_priv *priv = (void *)object;
nv_mask(priv, 0x104048, 0x00000003, 0x00000000);
nv_wr32(priv, 0x104014, 0xffffffff);
return nouveau_copy_fini(&priv->base, suspend);
}
struct nouveau_oclass
nva3_copy_oclass = {
.handle = NV_ENGINE(COPY0, 0xa3),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nva3_copy_ctor,
.dtor = _nouveau_copy_dtor,
.init = nva3_copy_init,
.fini = nva3_copy_fini,
},
};
/* /*
* Copyright 2010 Red Hat Inc. * Copyright 2012 Red Hat Inc.
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
...@@ -22,99 +22,106 @@ ...@@ -22,99 +22,106 @@
* Authors: Ben Skeggs * Authors: Ben Skeggs
*/ */
#include "drmP.h" #include <core/os.h>
#include "nouveau_drv.h" #include <core/enum.h>
#include "nouveau_util.h" #include <core/class.h>
#include <core/ramht.h> #include <core/engctx.h>
#include <core/gpuobj.h>
struct nv84_crypt_engine { #include <subdev/fb.h>
struct nouveau_exec_engine base;
#include <engine/crypt.h>
struct nv84_crypt_priv {
struct nouveau_crypt base;
}; };
struct nv84_crypt_chan {
struct nouveau_crypt_chan base;
};
/*******************************************************************************
* Crypt object classes
******************************************************************************/
static int static int
nv84_crypt_context_new(struct nouveau_channel *chan, int engine) nv84_crypt_object_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{ {
struct drm_device *dev = chan->dev; struct nouveau_gpuobj *obj;
struct nouveau_gpuobj *ramin = chan->ramin;
struct nouveau_gpuobj *ctx;
int ret; int ret;
NV_DEBUG(dev, "ch%d\n", chan->id); ret = nouveau_gpuobj_create(parent, engine, oclass, 0, parent,
16, 16, 0, &obj);
ret = nouveau_gpuobj_new(dev, chan, 256, 0, NVOBJ_FLAG_ZERO_ALLOC | *pobject = nv_object(obj);
NVOBJ_FLAG_ZERO_FREE, &ctx);
if (ret) if (ret)
return ret; return ret;
nv_wo32(ramin, 0xa0, 0x00190000); nv_wo32(obj, 0x00, nv_mclass(obj));
nv_wo32(ramin, 0xa4, ctx->addr + ctx->size - 1); nv_wo32(obj, 0x04, 0x00000000);
nv_wo32(ramin, 0xa8, ctx->addr); nv_wo32(obj, 0x08, 0x00000000);
nv_wo32(ramin, 0xac, 0); nv_wo32(obj, 0x0c, 0x00000000);
nv_wo32(ramin, 0xb0, 0);
nv_wo32(ramin, 0xb4, 0);
nvimem_flush(dev);
nvvm_engref(chan->vm, engine, 1);
chan->engctx[engine] = ctx;
return 0; return 0;
} }
static void static struct nouveau_ofuncs
nv84_crypt_context_del(struct nouveau_channel *chan, int engine) nv84_crypt_ofuncs = {
{ .ctor = nv84_crypt_object_ctor,
struct nouveau_gpuobj *ctx = chan->engctx[engine]; .dtor = _nouveau_gpuobj_dtor,
struct drm_device *dev = chan->dev; .init = _nouveau_gpuobj_init,
u32 inst; .fini = _nouveau_gpuobj_fini,
.rd32 = _nouveau_gpuobj_rd32,
inst = (chan->ramin->addr >> 12); .wr32 = _nouveau_gpuobj_wr32,
inst |= 0x80000000; };
/* mark context as invalid if still on the hardware, not static struct nouveau_oclass
* doing this causes issues the next time PCRYPT is used, nv84_crypt_sclass[] = {
* unsurprisingly :) { 0x74c1, &nv84_crypt_ofuncs },
*/ {}
nv_wr32(dev, 0x10200c, 0x00000000); };
if (nv_rd32(dev, 0x102188) == inst)
nv_mask(dev, 0x102188, 0x80000000, 0x00000000); /*******************************************************************************
if (nv_rd32(dev, 0x10218c) == inst) * PCRYPT context
nv_mask(dev, 0x10218c, 0x80000000, 0x00000000); ******************************************************************************/
nv_wr32(dev, 0x10200c, 0x00000010);
nouveau_gpuobj_ref(NULL, &ctx);
nvvm_engref(chan->vm, engine, -1);
chan->engctx[engine] = NULL;
}
static int static int
nv84_crypt_object_new(struct nouveau_channel *chan, int engine, nv84_crypt_context_ctor(struct nouveau_object *parent,
u32 handle, u16 class) struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{ {
struct drm_device *dev = chan->dev; struct nv84_crypt_chan *priv;
struct nouveau_gpuobj *obj = NULL;
int ret; int ret;
ret = nouveau_gpuobj_new(dev, chan, 16, 16, NVOBJ_FLAG_ZERO_FREE, &obj); ret = nouveau_crypt_context_create(parent, engine, oclass, NULL, 256,
0, NVOBJ_FLAG_ZERO_ALLOC, &priv);
*pobject = nv_object(priv);
if (ret) if (ret)
return ret; return ret;
obj->engine = 5;
obj->class = class;
nv_wo32(obj, 0x00, class); return 0;
nvimem_flush(dev);
ret = nouveau_ramht_insert(chan, handle, obj);
nouveau_gpuobj_ref(NULL, &obj);
return ret;
} }
static void static struct nouveau_oclass
nv84_crypt_tlb_flush(struct drm_device *dev, int engine) nv84_crypt_cclass = {
{ .handle = NV_ENGCTX(CRYPT, 0x84),
nv50_vm_flush_engine(dev, 0x0a); .ofuncs = &(struct nouveau_ofuncs) {
} .ctor = nv84_crypt_context_ctor,
.dtor = _nouveau_crypt_context_dtor,
.init = _nouveau_crypt_context_init,
.fini = _nouveau_crypt_context_fini,
.rd32 = _nouveau_crypt_context_rd32,
.wr32 = _nouveau_crypt_context_wr32,
},
};
/*******************************************************************************
* PCRYPT engine/subdev functions
******************************************************************************/
static struct nouveau_bitfield nv84_crypt_intr[] = { static struct nouveau_bitfield nv84_crypt_intr_mask[] = {
{ 0x00000001, "INVALID_STATE" }, { 0x00000001, "INVALID_STATE" },
{ 0x00000002, "ILLEGAL_MTHD" }, { 0x00000002, "ILLEGAL_MTHD" },
{ 0x00000004, "ILLEGAL_CLASS" }, { 0x00000004, "ILLEGAL_CLASS" },
...@@ -124,79 +131,78 @@ static struct nouveau_bitfield nv84_crypt_intr[] = { ...@@ -124,79 +131,78 @@ static struct nouveau_bitfield nv84_crypt_intr[] = {
}; };
static void static void
nv84_crypt_isr(struct drm_device *dev) nv84_crypt_intr(struct nouveau_subdev *subdev)
{ {
u32 stat = nv_rd32(dev, 0x102130); struct nv84_crypt_priv *priv = (void *)subdev;
u32 mthd = nv_rd32(dev, 0x102190); u32 stat = nv_rd32(priv, 0x102130);
u32 data = nv_rd32(dev, 0x102194); u32 mthd = nv_rd32(priv, 0x102190);
u64 inst = (u64)(nv_rd32(dev, 0x102188) & 0x7fffffff) << 12; u32 data = nv_rd32(priv, 0x102194);
int show = nouveau_ratelimit(); u32 inst = nv_rd32(priv, 0x102188) & 0x7fffffff;
int chid = nv50_graph_isr_chid(dev, inst);
if (stat) {
if (show) { nv_error(priv, "");
NV_INFO(dev, "PCRYPT:"); nouveau_bitfield_print(nv84_crypt_intr_mask, stat);
nouveau_bitfield_print(nv84_crypt_intr, stat); printk(" ch 0x%010llx mthd 0x%04x data 0x%08x\n",
printk(KERN_CONT " ch %d (0x%010llx) mthd 0x%04x data 0x%08x\n", (u64)inst << 12, mthd, data);
chid, inst, mthd, data);
} }
nv_wr32(dev, 0x102130, stat); nv_wr32(priv, 0x102130, stat);
nv_wr32(dev, 0x10200c, 0x10); nv_wr32(priv, 0x10200c, 0x10);
nv50_fb_vm_trap(dev, show); nv50_fb_trap(nouveau_fb(priv), 1);
} }
static int static int
nv84_crypt_fini(struct drm_device *dev, int engine, bool suspend) nv84_crypt_tlb_flush(struct nouveau_engine *engine)
{ {
nv_wr32(dev, 0x102140, 0x00000000); nv50_vm_flush_engine(&engine->base, 0x0a);
return 0; return 0;
} }
static int static int
nv84_crypt_init(struct drm_device *dev, int engine) nv84_crypt_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{ {
nv_mask(dev, 0x000200, 0x00004000, 0x00000000); struct nv84_crypt_priv *priv;
nv_mask(dev, 0x000200, 0x00004000, 0x00004000); int ret;
nv_wr32(dev, 0x102130, 0xffffffff); ret = nouveau_crypt_create(parent, engine, oclass, &priv);
nv_wr32(dev, 0x102140, 0xffffffbf); *pobject = nv_object(priv);
if (ret)
return ret;
nv_wr32(dev, 0x10200c, 0x00000010); nv_subdev(priv)->unit = 0x00004000;
nv_subdev(priv)->intr = nv84_crypt_intr;
nv_engine(priv)->cclass = &nv84_crypt_cclass;
nv_engine(priv)->sclass = nv84_crypt_sclass;
nv_engine(priv)->tlb_flush = nv84_crypt_tlb_flush;
return 0; return 0;
} }
static void static int
nv84_crypt_destroy(struct drm_device *dev, int engine) nv84_crypt_init(struct nouveau_object *object)
{
struct nv84_crypt_engine *pcrypt = nv_engine(dev, engine);
NVOBJ_ENGINE_DEL(dev, CRYPT);
nouveau_irq_unregister(dev, 14);
kfree(pcrypt);
}
int
nv84_crypt_create(struct drm_device *dev)
{ {
struct nv84_crypt_engine *pcrypt; struct nv84_crypt_priv *priv = (void *)object;
int ret;
pcrypt = kzalloc(sizeof(*pcrypt), GFP_KERNEL);
if (!pcrypt)
return -ENOMEM;
pcrypt->base.destroy = nv84_crypt_destroy;
pcrypt->base.init = nv84_crypt_init;
pcrypt->base.fini = nv84_crypt_fini;
pcrypt->base.context_new = nv84_crypt_context_new;
pcrypt->base.context_del = nv84_crypt_context_del;
pcrypt->base.object_new = nv84_crypt_object_new;
pcrypt->base.tlb_flush = nv84_crypt_tlb_flush;
nouveau_irq_register(dev, 14, nv84_crypt_isr); ret = nouveau_crypt_init(&priv->base);
if (ret)
return ret;
NVOBJ_ENGINE_ADD(dev, CRYPT, &pcrypt->base); nv_wr32(priv, 0x102130, 0xffffffff);
NVOBJ_CLASS (dev, 0x74c1, CRYPT); nv_wr32(priv, 0x102140, 0xffffffbf);
nv_wr32(priv, 0x10200c, 0x00000010);
return 0; return 0;
} }
struct nouveau_oclass
nv84_crypt_oclass = {
.handle = NV_ENGINE(CRYPT, 0x84),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv84_crypt_ctor,
.dtor = _nouveau_crypt_dtor,
.init = nv84_crypt_init,
.fini = _nouveau_crypt_fini,
},
};
/* /*
* Copyright 2011 Red Hat Inc. * Copyright 2012 Red Hat Inc.
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
...@@ -22,124 +22,74 @@ ...@@ -22,124 +22,74 @@
* Authors: Ben Skeggs * Authors: Ben Skeggs
*/ */
#include "drmP.h" #include <core/os.h>
#include <core/enum.h>
#include <core/class.h>
#include <core/engctx.h>
#include "nouveau_drv.h" #include <subdev/timer.h>
#include "nouveau_util.h" #include <subdev/fb.h>
#include <core/ramht.h>
#include <engine/crypt.h>
#include "fuc/nv98.fuc.h" #include "fuc/nv98.fuc.h"
struct nv98_crypt_priv { struct nv98_crypt_priv {
struct nouveau_exec_engine base; struct nouveau_crypt base;
}; };
struct nv98_crypt_chan { struct nv98_crypt_chan {
struct nouveau_gpuobj *mem; struct nouveau_crypt_chan base;
}; };
static int /*******************************************************************************
nv98_crypt_context_new(struct nouveau_channel *chan, int engine) * Crypt object classes
{ ******************************************************************************/
struct drm_device *dev = chan->dev;
struct nv98_crypt_priv *priv = nv_engine(dev, engine);
struct nv98_crypt_chan *cctx;
int ret;
cctx = chan->engctx[engine] = kzalloc(sizeof(*cctx), GFP_KERNEL);
if (!cctx)
return -ENOMEM;
nvvm_engref(chan->vm, engine, 1);
ret = nouveau_gpuobj_new(dev, chan, 256, 0, NVOBJ_FLAG_ZERO_ALLOC |
NVOBJ_FLAG_ZERO_FREE, &cctx->mem);
if (ret)
goto error;
nv_wo32(chan->ramin, 0xa0, 0x00190000);
nv_wo32(chan->ramin, 0xa4, cctx->mem->addr + cctx->mem->size - 1);
nv_wo32(chan->ramin, 0xa8, cctx->mem->addr);
nv_wo32(chan->ramin, 0xac, 0x00000000);
nv_wo32(chan->ramin, 0xb0, 0x00000000);
nv_wo32(chan->ramin, 0xb4, 0x00000000);
nvimem_flush(dev);
error:
if (ret)
priv->base.context_del(chan, engine);
return ret;
}
static void
nv98_crypt_context_del(struct nouveau_channel *chan, int engine)
{
struct nv98_crypt_chan *cctx = chan->engctx[engine];
int i;
for (i = 0xa0; i < 0xb4; i += 4)
nv_wo32(chan->ramin, i, 0x00000000);
nouveau_gpuobj_ref(NULL, &cctx->mem); static struct nouveau_oclass
nv98_crypt_sclass[] = {
{ 0x88b4, &nouveau_object_ofuncs },
{},
};
nvvm_engref(chan->vm, engine, -1); /*******************************************************************************
chan->engctx[engine] = NULL; * PCRYPT context
kfree(cctx); ******************************************************************************/
}
static int static int
nv98_crypt_object_new(struct nouveau_channel *chan, int engine, nv98_crypt_context_ctor(struct nouveau_object *parent,
u32 handle, u16 class) struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{ {
struct nv98_crypt_chan *cctx = chan->engctx[engine]; struct nv98_crypt_chan *priv;
int ret;
/* fuc engine doesn't need an object, our ramht code does.. */
cctx->mem->engine = 5;
cctx->mem->class = class;
return nouveau_ramht_insert(chan, handle, cctx->mem);
}
static void ret = nouveau_crypt_context_create(parent, engine, oclass, NULL, 256,
nv98_crypt_tlb_flush(struct drm_device *dev, int engine) 256, NVOBJ_FLAG_ZERO_ALLOC, &priv);
{ *pobject = nv_object(priv);
nv50_vm_flush_engine(dev, 0x0a); if (ret)
} return ret;
static int
nv98_crypt_fini(struct drm_device *dev, int engine, bool suspend)
{
nv_mask(dev, 0x000200, 0x00004000, 0x00000000);
return 0; return 0;
} }
static int static struct nouveau_oclass
nv98_crypt_init(struct drm_device *dev, int engine) nv98_crypt_cclass = {
{ .handle = NV_ENGCTX(CRYPT, 0x98),
int i; .ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv98_crypt_context_ctor,
/* reset! */ .dtor = _nouveau_crypt_context_dtor,
nv_mask(dev, 0x000200, 0x00004000, 0x00000000); .init = _nouveau_crypt_context_init,
nv_mask(dev, 0x000200, 0x00004000, 0x00004000); .fini = _nouveau_crypt_context_fini,
.rd32 = _nouveau_crypt_context_rd32,
/* wait for exit interrupt to signal */ .wr32 = _nouveau_crypt_context_wr32,
nv_wait(dev, 0x087008, 0x00000010, 0x00000010); },
nv_wr32(dev, 0x087004, 0x00000010); };
/* upload microcode code and data segments */
nv_wr32(dev, 0x087ff8, 0x00100000);
for (i = 0; i < ARRAY_SIZE(nv98_pcrypt_code); i++)
nv_wr32(dev, 0x087ff4, nv98_pcrypt_code[i]);
nv_wr32(dev, 0x087ff8, 0x00000000);
for (i = 0; i < ARRAY_SIZE(nv98_pcrypt_data); i++)
nv_wr32(dev, 0x087ff4, nv98_pcrypt_data[i]);
/* start it running */ /*******************************************************************************
nv_wr32(dev, 0x08710c, 0x00000000); * PCRYPT engine/subdev functions
nv_wr32(dev, 0x087104, 0x00000000); /* ENTRY */ ******************************************************************************/
nv_wr32(dev, 0x087100, 0x00000002); /* TRIGGER */
return 0;
}
static struct nouveau_enum nv98_crypt_isr_error_name[] = { static struct nouveau_enum nv98_crypt_isr_error_name[] = {
{ 0x0000, "ILLEGAL_MTHD" }, { 0x0000, "ILLEGAL_MTHD" },
...@@ -150,65 +100,100 @@ static struct nouveau_enum nv98_crypt_isr_error_name[] = { ...@@ -150,65 +100,100 @@ static struct nouveau_enum nv98_crypt_isr_error_name[] = {
}; };
static void static void
nv98_crypt_isr(struct drm_device *dev) nv98_crypt_intr(struct nouveau_subdev *subdev)
{ {
u32 disp = nv_rd32(dev, 0x08701c); struct nv98_crypt_priv *priv = (void *)subdev;
u32 stat = nv_rd32(dev, 0x087008) & disp & ~(disp >> 16); u32 disp = nv_rd32(priv, 0x08701c);
u32 inst = nv_rd32(dev, 0x087050) & 0x3fffffff; u32 stat = nv_rd32(priv, 0x087008) & disp & ~(disp >> 16);
u32 ssta = nv_rd32(dev, 0x087040) & 0x0000ffff; u32 inst = nv_rd32(priv, 0x087050) & 0x3fffffff;
u32 addr = nv_rd32(dev, 0x087040) >> 16; u32 ssta = nv_rd32(priv, 0x087040) & 0x0000ffff;
u32 addr = nv_rd32(priv, 0x087040) >> 16;
u32 mthd = (addr & 0x07ff) << 2; u32 mthd = (addr & 0x07ff) << 2;
u32 subc = (addr & 0x3800) >> 11; u32 subc = (addr & 0x3800) >> 11;
u32 data = nv_rd32(dev, 0x087044); u32 data = nv_rd32(priv, 0x087044);
int chid = nv50_graph_isr_chid(dev, inst);
if (stat & 0x00000040) { if (stat & 0x00000040) {
NV_INFO(dev, "PCRYPT: DISPATCH_ERROR ["); nv_error(priv, "DISPATCH_ERROR [");
nouveau_enum_print(nv98_crypt_isr_error_name, ssta); nouveau_enum_print(nv98_crypt_isr_error_name, ssta);
printk("] ch %d [0x%08x] subc %d mthd 0x%04x data 0x%08x\n", printk("] ch 0x%08x subc %d mthd 0x%04x data 0x%08x\n",
chid, inst, subc, mthd, data); inst, subc, mthd, data);
nv_wr32(dev, 0x087004, 0x00000040); nv_wr32(priv, 0x087004, 0x00000040);
stat &= ~0x00000040; stat &= ~0x00000040;
} }
if (stat) { if (stat) {
NV_INFO(dev, "PCRYPT: unhandled intr 0x%08x\n", stat); nv_error(priv, "unhandled intr 0x%08x\n", stat);
nv_wr32(dev, 0x087004, stat); nv_wr32(priv, 0x087004, stat);
} }
nv50_fb_vm_trap(dev, 1); nv50_fb_trap(nouveau_fb(priv), 1);
} }
static void static int
nv98_crypt_destroy(struct drm_device *dev, int engine) nv98_crypt_tlb_flush(struct nouveau_engine *engine)
{ {
struct nv98_crypt_priv *priv = nv_engine(dev, engine); nv50_vm_flush_engine(&engine->base, 0x0a);
return 0;
nouveau_irq_unregister(dev, 14);
NVOBJ_ENGINE_DEL(dev, CRYPT);
kfree(priv);
} }
int static int
nv98_crypt_create(struct drm_device *dev) nv98_crypt_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{ {
struct nv98_crypt_priv *priv; struct nv98_crypt_priv *priv;
int ret;
ret = nouveau_crypt_create(parent, engine, oclass, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
nv_subdev(priv)->unit = 0x00004000;
nv_subdev(priv)->intr = nv98_crypt_intr;
nv_engine(priv)->cclass = &nv98_crypt_cclass;
nv_engine(priv)->sclass = nv98_crypt_sclass;
nv_engine(priv)->tlb_flush = nv98_crypt_tlb_flush;
return 0;
}
static int
nv98_crypt_init(struct nouveau_object *object)
{
struct nv98_crypt_priv *priv = (void *)object;
int ret, i;
ret = nouveau_crypt_init(&priv->base);
if (ret)
return ret;
priv = kzalloc(sizeof(*priv), GFP_KERNEL); /* wait for exit interrupt to signal */
if (!priv) nv_wait(priv, 0x087008, 0x00000010, 0x00000010);
return -ENOMEM; nv_wr32(priv, 0x087004, 0x00000010);
priv->base.destroy = nv98_crypt_destroy; /* upload microcode code and data segments */
priv->base.init = nv98_crypt_init; nv_wr32(priv, 0x087ff8, 0x00100000);
priv->base.fini = nv98_crypt_fini; for (i = 0; i < ARRAY_SIZE(nv98_pcrypt_code); i++)
priv->base.context_new = nv98_crypt_context_new; nv_wr32(priv, 0x087ff4, nv98_pcrypt_code[i]);
priv->base.context_del = nv98_crypt_context_del;
priv->base.object_new = nv98_crypt_object_new;
priv->base.tlb_flush = nv98_crypt_tlb_flush;
nouveau_irq_register(dev, 14, nv98_crypt_isr); nv_wr32(priv, 0x087ff8, 0x00000000);
for (i = 0; i < ARRAY_SIZE(nv98_pcrypt_data); i++)
nv_wr32(priv, 0x087ff4, nv98_pcrypt_data[i]);
NVOBJ_ENGINE_ADD(dev, CRYPT, &priv->base); /* start it running */
NVOBJ_CLASS(dev, 0x88b4, CRYPT); nv_wr32(priv, 0x08710c, 0x00000000);
nv_wr32(priv, 0x087104, 0x00000000); /* ENTRY */
nv_wr32(priv, 0x087100, 0x00000002); /* TRIGGER */
return 0; return 0;
} }
struct nouveau_oclass
nv98_crypt_oclass = {
.handle = NV_ENGINE(CRYPT, 0x98),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv98_crypt_ctor,
.dtor = _nouveau_crypt_dtor,
.init = nv98_crypt_init,
.fini = _nouveau_crypt_fini,
},
};
...@@ -22,88 +22,69 @@ ...@@ -22,88 +22,69 @@
* Authors: Ben Skeggs * Authors: Ben Skeggs
*/ */
#include "drmP.h" #include <engine/disp.h>
#include "nouveau_drv.h" struct nv04_disp_priv {
#include <core/ramht.h> struct nouveau_disp base;
#include "nouveau_software.h"
#include "nv50_display.h"
struct nvc0_software_priv {
struct nouveau_software_priv base;
}; };
struct nvc0_software_chan { static struct nouveau_oclass
struct nouveau_software_chan base; nv04_disp_sclass[] = {
{},
}; };
static int static void
nvc0_software_context_new(struct nouveau_channel *chan, int engine) nv04_disp_intr_vblank(struct nv04_disp_priv *priv, int crtc)
{ {
struct nvc0_software_chan *pch; struct nouveau_disp *disp = &priv->base;
if (disp->vblank.notify)
pch = kzalloc(sizeof(*pch), GFP_KERNEL); disp->vblank.notify(disp->vblank.data, crtc);
if (!pch)
return -ENOMEM;
nouveau_software_context_new(chan, &pch->base);
chan->engctx[engine] = pch;
return 0;
} }
static void static void
nvc0_software_context_del(struct nouveau_channel *chan, int engine) nv04_disp_intr(struct nouveau_subdev *subdev)
{ {
struct nvc0_software_chan *pch = chan->engctx[engine]; struct nv04_disp_priv *priv = (void *)subdev;
chan->engctx[engine] = NULL; u32 crtc0 = nv_rd32(priv, 0x600100);
kfree(pch); u32 crtc1 = nv_rd32(priv, 0x602100);
if (crtc0 & 0x00000001) {
nv04_disp_intr_vblank(priv, 0);
nv_wr32(priv, 0x600100, 0x00000001);
}
if (crtc1 & 0x00000001) {
nv04_disp_intr_vblank(priv, 1);
nv_wr32(priv, 0x602100, 0x00000001);
}
} }
static int static int
nvc0_software_object_new(struct nouveau_channel *chan, int engine, nv04_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
u32 handle, u16 class) struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{ {
return 0; struct nv04_disp_priv *priv;
} int ret;
static int ret = nouveau_disp_create(parent, engine, oclass, "DISPLAY",
nvc0_software_init(struct drm_device *dev, int engine) "display", &priv);
{ *pobject = nv_object(priv);
return 0; if (ret)
} return ret;
static int nv_engine(priv)->sclass = nv04_disp_sclass;
nvc0_software_fini(struct drm_device *dev, int engine, bool suspend) nv_subdev(priv)->intr = nv04_disp_intr;
{
return 0; return 0;
} }
static void struct nouveau_oclass
nvc0_software_destroy(struct drm_device *dev, int engine) nv04_disp_oclass = {
{ .handle = NV_ENGINE(DISP, 0x04),
struct nvc0_software_priv *psw = nv_engine(dev, engine); .ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv04_disp_ctor,
NVOBJ_ENGINE_DEL(dev, SW); .dtor = _nouveau_disp_dtor,
kfree(psw); .init = _nouveau_disp_init,
} .fini = _nouveau_disp_fini,
},
int };
nvc0_software_create(struct drm_device *dev)
{
struct nvc0_software_priv *psw = kzalloc(sizeof(*psw), GFP_KERNEL);
if (!psw)
return -ENOMEM;
psw->base.base.destroy = nvc0_software_destroy;
psw->base.base.init = nvc0_software_init;
psw->base.base.fini = nvc0_software_fini;
psw->base.base.context_new = nvc0_software_context_new;
psw->base.base.context_del = nvc0_software_context_del;
psw->base.base.object_new = nvc0_software_object_new;
nouveau_software_create(&psw->base);
NVOBJ_ENGINE_ADD(dev, SW, &psw->base.base);
NVOBJ_CLASS(dev, 0x906e, SW);
return 0;
}
/*
* 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"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <engine/software.h>
#include <engine/disp.h>
struct nv50_disp_priv {
struct nouveau_disp base;
};
static struct nouveau_oclass
nv50_disp_sclass[] = {
{},
};
static void
nv50_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc)
{
struct nouveau_disp *disp = &priv->base;
struct nouveau_software_chan *chan, *temp;
unsigned long flags;
spin_lock_irqsave(&disp->vblank.lock, flags);
list_for_each_entry_safe(chan, temp, &disp->vblank.list, vblank.head) {
if (chan->vblank.crtc != crtc)
continue;
nv_wr32(priv, 0x001704, chan->vblank.channel);
nv_wr32(priv, 0x001710, 0x80000000 | chan->vblank.ctxdma);
if (nv_device(priv)->chipset == 0x50) {
nv_wr32(priv, 0x001570, chan->vblank.offset);
nv_wr32(priv, 0x001574, chan->vblank.value);
} else {
if (nv_device(priv)->chipset >= 0xc0) {
nv_wr32(priv, 0x06000c,
upper_32_bits(chan->vblank.offset));
}
nv_wr32(priv, 0x060010, chan->vblank.offset);
nv_wr32(priv, 0x060014, chan->vblank.value);
}
list_del(&chan->vblank.head);
if (disp->vblank.put)
disp->vblank.put(disp->vblank.data, crtc);
}
spin_unlock_irqrestore(&disp->vblank.lock, flags);
if (disp->vblank.notify)
disp->vblank.notify(disp->vblank.data, crtc);
}
static void
nv50_disp_intr(struct nouveau_subdev *subdev)
{
struct nv50_disp_priv *priv = (void *)subdev;
u32 stat1 = nv_rd32(priv, 0x610024);
if (stat1 & 0x00000004) {
nv50_disp_intr_vblank(priv, 0);
nv_wr32(priv, 0x610024, 0x00000004);
stat1 &= ~0x00000004;
}
if (stat1 & 0x00000008) {
nv50_disp_intr_vblank(priv, 1);
nv_wr32(priv, 0x610024, 0x00000008);
stat1 &= ~0x00000008;
}
}
static int
nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
struct nv50_disp_priv *priv;
int ret;
ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
"display", &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
nv_engine(priv)->sclass = nv50_disp_sclass;
nv_subdev(priv)->intr = nv50_disp_intr;
INIT_LIST_HEAD(&priv->base.vblank.list);
spin_lock_init(&priv->base.vblank.lock);
return 0;
}
struct nouveau_oclass
nv50_disp_oclass = {
.handle = NV_ENGINE(DISP, 0x50),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv50_disp_ctor,
.dtor = _nouveau_disp_dtor,
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
};
/*
* 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"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <subdev/bar.h>
#include <engine/software.h>
#include <engine/disp.h>
struct nvd0_disp_priv {
struct nouveau_disp base;
};
static struct nouveau_oclass
nvd0_disp_sclass[] = {
{},
};
static void
nvd0_disp_intr_vblank(struct nvd0_disp_priv *priv, int crtc)
{
struct nouveau_bar *bar = nouveau_bar(priv);
struct nouveau_disp *disp = &priv->base;
struct nouveau_software_chan *chan, *temp;
unsigned long flags;
spin_lock_irqsave(&disp->vblank.lock, flags);
list_for_each_entry_safe(chan, temp, &disp->vblank.list, vblank.head) {
if (chan->vblank.crtc != crtc)
continue;
nv_wr32(priv, 0x001718, 0x80000000 | chan->vblank.channel);
bar->flush(bar);
nv_wr32(priv, 0x06000c, upper_32_bits(chan->vblank.offset));
nv_wr32(priv, 0x060010, lower_32_bits(chan->vblank.offset));
nv_wr32(priv, 0x060014, chan->vblank.value);
list_del(&chan->vblank.head);
if (disp->vblank.put)
disp->vblank.put(disp->vblank.data, crtc);
}
spin_unlock_irqrestore(&disp->vblank.lock, flags);
if (disp->vblank.notify)
disp->vblank.notify(disp->vblank.data, crtc);
}
static void
nvd0_disp_intr(struct nouveau_subdev *subdev)
{
struct nvd0_disp_priv *priv = (void *)subdev;
u32 intr = nv_rd32(priv, 0x610088);
int i;
for (i = 0; i < 4; i++) {
u32 mask = 0x01000000 << i;
if (mask & intr) {
u32 stat = nv_rd32(priv, 0x6100bc + (i * 0x800));
if (stat & 0x00000001)
nvd0_disp_intr_vblank(priv, i);
nv_mask(priv, 0x6100bc + (i * 0x800), 0, 0);
nv_rd32(priv, 0x6100c0 + (i * 0x800));
}
}
}
static int
nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
struct nvd0_disp_priv *priv;
int ret;
ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
"display", &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
nv_engine(priv)->sclass = nvd0_disp_sclass;
nv_subdev(priv)->intr = nvd0_disp_intr;
INIT_LIST_HEAD(&priv->base.vblank.list);
spin_lock_init(&priv->base.vblank.lock);
return 0;
}
struct nouveau_oclass
nvd0_disp_oclass = {
.handle = NV_ENGINE(DISP, 0xd0),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nvd0_disp_ctor,
.dtor = _nouveau_disp_dtor,
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
};
/*
* 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"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <core/object.h>
#include <core/class.h>
#include <subdev/fb.h>
#include <engine/dmaobj.h>
int
nouveau_dmaobj_create_(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass,
void *data, u32 size, int len, void **pobject)
{
struct nv_dma_class *args = data;
struct nouveau_dmaobj *object;
int ret;
if (size < sizeof(*args))
return -EINVAL;
ret = nouveau_object_create_(parent, engine, oclass, 0, len, pobject);
object = *pobject;
if (ret)
return ret;
switch (args->flags & NV_DMA_TARGET_MASK) {
case NV_DMA_TARGET_VM:
object->target = NV_MEM_TARGET_VM;
break;
case NV_DMA_TARGET_VRAM:
object->target = NV_MEM_TARGET_VRAM;
break;
case NV_DMA_TARGET_PCI:
object->target = NV_MEM_TARGET_PCI;
break;
case NV_DMA_TARGET_PCI_US:
case NV_DMA_TARGET_AGP:
object->target = NV_MEM_TARGET_PCI_NOSNOOP;
break;
default:
return -EINVAL;
}
switch (args->flags & NV_DMA_ACCESS_MASK) {
case NV_DMA_ACCESS_VM:
object->access = NV_MEM_ACCESS_VM;
break;
case NV_DMA_ACCESS_RD:
object->access = NV_MEM_ACCESS_RO;
break;
case NV_DMA_ACCESS_WR:
object->access = NV_MEM_ACCESS_WO;
break;
case NV_DMA_ACCESS_RDWR:
object->access = NV_MEM_ACCESS_RW;
break;
default:
return -EINVAL;
}
object->start = args->start;
object->limit = args->limit;
return 0;
}
/*
* 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"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <core/gpuobj.h>
#include <subdev/fb.h>
#include <subdev/vm/nv04.h>
#include <engine/dmaobj.h>
struct nv04_dmaeng_priv {
struct nouveau_dmaeng base;
};
struct nv04_dmaobj_priv {
struct nouveau_dmaobj base;
};
static int
nv04_dmaobj_bind(struct nouveau_dmaeng *dmaeng,
struct nouveau_object *parent,
struct nouveau_dmaobj *dmaobj,
struct nouveau_gpuobj **pgpuobj)
{
struct nouveau_gpuobj *gpuobj;
u32 flags0 = nv_mclass(dmaobj);
u32 flags2 = 0x00000000;
u32 offset = (dmaobj->start & 0xfffff000);
u32 adjust = (dmaobj->start & 0x00000fff);
u32 length = dmaobj->limit - dmaobj->start;
int ret;
if (dmaobj->target == NV_MEM_TARGET_VM) {
gpuobj = nv04_vmmgr(dmaeng)->vm->pgt[0].obj[0];
if (dmaobj->start == 0)
return nouveau_gpuobj_dup(parent, gpuobj, pgpuobj);
offset = nv_ro32(gpuobj, 8 + (offset >> 10));
offset &= 0xfffff000;
dmaobj->target = NV_MEM_TARGET_PCI;
dmaobj->access = NV_MEM_ACCESS_RW;
}
switch (dmaobj->target) {
case NV_MEM_TARGET_VRAM:
flags0 |= 0x00003000;
break;
case NV_MEM_TARGET_PCI:
flags0 |= 0x00023000;
break;
case NV_MEM_TARGET_PCI_NOSNOOP:
flags0 |= 0x00033000;
break;
default:
return -EINVAL;
}
switch (dmaobj->access) {
case NV_MEM_ACCESS_RO:
flags0 |= 0x00004000;
break;
case NV_MEM_ACCESS_WO:
flags0 |= 0x00008000;
case NV_MEM_ACCESS_RW:
flags2 |= 0x00000002;
break;
default:
return -EINVAL;
}
ret = nouveau_gpuobj_new(parent, parent, 16, 16, 0, &gpuobj);
*pgpuobj = gpuobj;
if (ret == 0) {
nv_wo32(*pgpuobj, 0x00, flags0 | (adjust << 20));
nv_wo32(*pgpuobj, 0x04, length);
nv_wo32(*pgpuobj, 0x08, flags2 | offset);
nv_wo32(*pgpuobj, 0x0c, flags2 | offset);
}
return ret;
}
static int
nv04_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
struct nouveau_dmaeng *dmaeng = (void *)engine;
struct nv04_dmaobj_priv *dmaobj;
struct nouveau_gpuobj *gpuobj;
int ret;
ret = nouveau_dmaobj_create(parent, engine, oclass,
data, size, &dmaobj);
*pobject = nv_object(dmaobj);
if (ret)
return ret;
switch (nv_mclass(parent)) {
case 0x006e:
ret = dmaeng->bind(dmaeng, *pobject, &dmaobj->base, &gpuobj);
nouveau_object_ref(NULL, pobject);
*pobject = nv_object(gpuobj);
break;
default:
break;
}
return ret;
}
static struct nouveau_ofuncs
nv04_dmaobj_ofuncs = {
.ctor = nv04_dmaobj_ctor,
.dtor = _nouveau_dmaobj_dtor,
.init = _nouveau_dmaobj_init,
.fini = _nouveau_dmaobj_fini,
};
static struct nouveau_oclass
nv04_dmaobj_sclass[] = {
{ 0x0002, &nv04_dmaobj_ofuncs },
{ 0x0003, &nv04_dmaobj_ofuncs },
{ 0x003d, &nv04_dmaobj_ofuncs },
{}
};
static int
nv04_dmaeng_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
struct nv04_dmaeng_priv *priv;
int ret;
ret = nouveau_dmaeng_create(parent, engine, oclass, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
priv->base.base.sclass = nv04_dmaobj_sclass;
priv->base.bind = nv04_dmaobj_bind;
return 0;
}
struct nouveau_oclass
nv04_dmaeng_oclass = {
.handle = NV_ENGINE(DMAOBJ, 0x04),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv04_dmaeng_ctor,
.dtor = _nouveau_dmaeng_dtor,
.init = _nouveau_dmaeng_init,
.fini = _nouveau_dmaeng_fini,
},
};
/*
* 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"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <core/gpuobj.h>
#include <subdev/fb.h>
#include <engine/dmaobj.h>
struct nv50_dmaeng_priv {
struct nouveau_dmaeng base;
};
struct nv50_dmaobj_priv {
struct nouveau_dmaobj base;
};
static int
nv50_dmaobj_bind(struct nouveau_dmaeng *dmaeng,
struct nouveau_object *parent,
struct nouveau_dmaobj *dmaobj,
struct nouveau_gpuobj **pgpuobj)
{
u32 flags = nv_mclass(dmaobj);
int ret;
switch (dmaobj->target) {
case NV_MEM_TARGET_VM:
flags |= 0x00000000;
flags |= 0x60000000; /* COMPRESSION_USEVM */
flags |= 0x1fc00000; /* STORAGE_TYPE_USEVM */
break;
case NV_MEM_TARGET_VRAM:
flags |= 0x00010000;
flags |= 0x00100000; /* ACCESSUS_USER_SYSTEM */
break;
case NV_MEM_TARGET_PCI:
flags |= 0x00020000;
flags |= 0x00100000; /* ACCESSUS_USER_SYSTEM */
break;
case NV_MEM_TARGET_PCI_NOSNOOP:
flags |= 0x00030000;
flags |= 0x00100000; /* ACCESSUS_USER_SYSTEM */
break;
default:
return -EINVAL;
}
switch (dmaobj->access) {
case NV_MEM_ACCESS_VM:
break;
case NV_MEM_ACCESS_RO:
flags |= 0x00040000;
break;
case NV_MEM_ACCESS_WO:
case NV_MEM_ACCESS_RW:
flags |= 0x00080000;
break;
}
ret = nouveau_gpuobj_new(parent, parent, 24, 32, 0, pgpuobj);
if (ret == 0) {
nv_wo32(*pgpuobj, 0x00, flags);
nv_wo32(*pgpuobj, 0x04, lower_32_bits(dmaobj->limit));
nv_wo32(*pgpuobj, 0x08, lower_32_bits(dmaobj->start));
nv_wo32(*pgpuobj, 0x0c, upper_32_bits(dmaobj->limit) << 24 |
upper_32_bits(dmaobj->start));
nv_wo32(*pgpuobj, 0x10, 0x00000000);
nv_wo32(*pgpuobj, 0x14, 0x00000000);
}
return ret;
}
static int
nv50_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
struct nouveau_dmaeng *dmaeng = (void *)engine;
struct nv50_dmaobj_priv *dmaobj;
struct nouveau_gpuobj *gpuobj;
int ret;
ret = nouveau_dmaobj_create(parent, engine, oclass,
data, size, &dmaobj);
*pobject = nv_object(dmaobj);
if (ret)
return ret;
switch (nv_mclass(parent)) {
case 0x506f:
case 0x826f:
ret = dmaeng->bind(dmaeng, *pobject, &dmaobj->base, &gpuobj);
nouveau_object_ref(NULL, pobject);
*pobject = nv_object(gpuobj);
break;
default:
break;
}
return ret;
}
static struct nouveau_ofuncs
nv50_dmaobj_ofuncs = {
.ctor = nv50_dmaobj_ctor,
.dtor = _nouveau_dmaobj_dtor,
.init = _nouveau_dmaobj_init,
.fini = _nouveau_dmaobj_fini,
};
static struct nouveau_oclass
nv50_dmaobj_sclass[] = {
{ 0x0002, &nv50_dmaobj_ofuncs },
{ 0x0003, &nv50_dmaobj_ofuncs },
{ 0x003d, &nv50_dmaobj_ofuncs },
{}
};
static int
nv50_dmaeng_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
struct nv50_dmaeng_priv *priv;
int ret;
ret = nouveau_dmaeng_create(parent, engine, oclass, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
priv->base.base.sclass = nv50_dmaobj_sclass;
priv->base.bind = nv50_dmaobj_bind;
return 0;
}
struct nouveau_oclass
nv50_dmaeng_oclass = {
.handle = NV_ENGINE(DMAOBJ, 0x50),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv50_dmaeng_ctor,
.dtor = _nouveau_dmaeng_dtor,
.init = _nouveau_dmaeng_init,
.fini = _nouveau_dmaeng_fini,
},
};
/*
* 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"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <core/gpuobj.h>
#include <subdev/fb.h>
#include <engine/dmaobj.h>
struct nvc0_dmaeng_priv {
struct nouveau_dmaeng base;
};
struct nvc0_dmaobj_priv {
struct nouveau_dmaobj base;
};
static int
nvc0_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
struct nvc0_dmaobj_priv *dmaobj;
int ret;
ret = nouveau_dmaobj_create(parent, engine, oclass, data, size, &dmaobj);
*pobject = nv_object(dmaobj);
if (ret)
return ret;
if (dmaobj->base.target != NV_MEM_TARGET_VM || dmaobj->base.start)
return -EINVAL;
return 0;
}
static struct nouveau_ofuncs
nvc0_dmaobj_ofuncs = {
.ctor = nvc0_dmaobj_ctor,
.dtor = _nouveau_dmaobj_dtor,
.init = _nouveau_dmaobj_init,
.fini = _nouveau_dmaobj_fini,
};
static struct nouveau_oclass
nvc0_dmaobj_sclass[] = {
{ 0x0002, &nvc0_dmaobj_ofuncs },
{ 0x0003, &nvc0_dmaobj_ofuncs },
{ 0x003d, &nvc0_dmaobj_ofuncs },
{}
};
static int
nvc0_dmaeng_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
struct nvc0_dmaeng_priv *priv;
int ret;
ret = nouveau_dmaeng_create(parent, engine, oclass, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
priv->base.base.sclass = nvc0_dmaobj_sclass;
return 0;
}
struct nouveau_oclass
nvc0_dmaeng_oclass = {
.handle = NV_ENGINE(DMAOBJ, 0xc0),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nvc0_dmaeng_ctor,
.dtor = _nouveau_dmaeng_dtor,
.init = _nouveau_dmaeng_init,
.fini = _nouveau_dmaeng_fini,
},
};
/*
* 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"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <core/object.h>
#include <core/handle.h>
#include <engine/dmaobj.h>
#include <engine/fifo.h>
int
nouveau_fifo_channel_create_(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass,
int bar, u32 addr, u32 size, u32 pushbuf,
u32 engmask, int len, void **ptr)
{
struct nouveau_device *device = nv_device(engine);
struct nouveau_fifo *priv = (void *)engine;
struct nouveau_fifo_chan *chan;
struct nouveau_dmaeng *dmaeng;
unsigned long flags;
int ret;
/* create base object class */
ret = nouveau_namedb_create_(parent, engine, oclass, 0, NULL,
engmask, len, ptr);
chan = *ptr;
if (ret)
return ret;
/* validate dma object representing push buffer */
chan->pushdma = (void *)nouveau_handle_ref(parent, pushbuf);
if (!chan->pushdma)
return -ENOENT;
dmaeng = (void *)chan->pushdma->base.engine;
switch (chan->pushdma->base.oclass->handle) {
case 0x0002:
case 0x003d:
break;
default:
return -EINVAL;
}
if (dmaeng->bind) {
ret = dmaeng->bind(dmaeng, parent, chan->pushdma, &chan->pushgpu);
if (ret)
return ret;
}
/* find a free fifo channel */
spin_lock_irqsave(&priv->lock, flags);
for (chan->chid = priv->min; chan->chid < priv->max; chan->chid++) {
if (!priv->channel[chan->chid]) {
priv->channel[chan->chid] = nv_object(chan);
break;
}
}
spin_unlock_irqrestore(&priv->lock, flags);
if (chan->chid == priv->max) {
nv_error(priv, "no free channels\n");
return -ENOSPC;
}
/* map fifo control registers */
chan->user = ioremap(pci_resource_start(device->pdev, bar) + addr +
(chan->chid * size), size);
if (!chan->user)
return -EFAULT;
chan->size = size;
return 0;
}
void
nouveau_fifo_channel_destroy(struct nouveau_fifo_chan *chan)
{
struct nouveau_fifo *priv = (void *)nv_object(chan)->engine;
unsigned long flags;
iounmap(chan->user);
spin_lock_irqsave(&priv->lock, flags);
priv->channel[chan->chid] = NULL;
spin_unlock_irqrestore(&priv->lock, flags);
nouveau_gpuobj_ref(NULL, &chan->pushgpu);
nouveau_object_ref(NULL, (struct nouveau_object **)&chan->pushdma);
nouveau_namedb_destroy(&chan->base);
}
void
_nouveau_fifo_channel_dtor(struct nouveau_object *object)
{
struct nouveau_fifo_chan *chan = (void *)object;
nouveau_fifo_channel_destroy(chan);
}
u32
_nouveau_fifo_channel_rd32(struct nouveau_object *object, u32 addr)
{
struct nouveau_fifo_chan *chan = (void *)object;
return ioread32_native(chan->user + addr);
}
void
_nouveau_fifo_channel_wr32(struct nouveau_object *object, u32 addr, u32 data)
{
struct nouveau_fifo_chan *chan = (void *)object;
iowrite32_native(data, chan->user + addr);
}
void
nouveau_fifo_destroy(struct nouveau_fifo *priv)
{
kfree(priv->channel);
nouveau_engine_destroy(&priv->base);
}
int
nouveau_fifo_create_(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass,
int min, int max, int length, void **pobject)
{
struct nouveau_fifo *priv;
int ret;
ret = nouveau_engine_create_(parent, engine, oclass, true, "PFIFO",
"fifo", length, pobject);
priv = *pobject;
if (ret)
return ret;
priv->min = min;
priv->max = max;
priv->channel = kzalloc(sizeof(*priv->channel) * (max + 1), GFP_KERNEL);
if (!priv->channel)
return -ENOMEM;
spin_lock_init(&priv->lock);
return 0;
}
This diff is collapsed.
/* /*
* Copyright (C) 2012 Ben Skeggs. * Copyright 2012 Red Hat Inc.
* All Rights Reserved.
* *
* Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining a
* a copy of this software and associated documentation files (the * copy of this software and associated documentation files (the "Software"),
* "Software"), to deal in the Software without restriction, including * to deal in the Software without restriction, including without limitation
* without limitation the rights to use, copy, modify, merge, publish, * the rights to use, copy, modify, merge, publish, distribute, sublicense,
* distribute, sublicense, and/or sell copies of the Software, and to * and/or sell copies of the Software, and to permit persons to whom the
* permit persons to whom the Software is furnished to do so, subject to * Software is furnished to do so, subject to the following conditions:
* the following conditions:
* *
* The above copyright notice and this permission notice (including the * The above copyright notice and this permission notice shall be included in
* next paragraph) shall be included in all copies or substantial * all copies or substantial portions of the Software.
* portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
* *
* Authors: Ben Skeggs
*/ */
#include "drmP.h" #include <core/os.h>
#include "drm.h" #include <core/class.h>
#include "nouveau_drv.h" #include <core/engctx.h>
#include <engine/fifo.h>
#include "nouveau_util.h"
#include <core/ramht.h> #include <core/ramht.h>
static struct ramfc_desc { #include <subdev/instmem.h>
unsigned bits:6; #include <subdev/instmem/nv04.h>
unsigned ctxs:5; #include <subdev/fb.h>
unsigned ctxp:8;
unsigned regs:5; #include <engine/fifo.h>
unsigned regp;
} nv10_ramfc[] = { #include "nv04.h"
static struct ramfc_desc
nv10_ramfc[] = {
{ 32, 0, 0x00, 0, NV04_PFIFO_CACHE1_DMA_PUT }, { 32, 0, 0x00, 0, NV04_PFIFO_CACHE1_DMA_PUT },
{ 32, 0, 0x04, 0, NV04_PFIFO_CACHE1_DMA_GET }, { 32, 0, 0x04, 0, NV04_PFIFO_CACHE1_DMA_GET },
{ 32, 0, 0x08, 0, NV10_PFIFO_CACHE1_REF_CNT }, { 32, 0, 0x08, 0, NV10_PFIFO_CACHE1_REF_CNT },
...@@ -50,87 +49,122 @@ static struct ramfc_desc { ...@@ -50,87 +49,122 @@ static struct ramfc_desc {
{} {}
}; };
struct nv10_fifo_priv { /*******************************************************************************
struct nouveau_fifo_priv base; * FIFO channel objects
struct ramfc_desc *ramfc_desc; ******************************************************************************/
struct nouveau_gpuobj *ramro;
struct nouveau_gpuobj *ramfc;
};
struct nv10_fifo_chan {
struct nouveau_fifo_chan base;
u32 ramfc;
};
static int static int
nv10_fifo_context_new(struct nouveau_channel *chan, int engine) nv10_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_device *dev = chan->dev; struct nv04_fifo_priv *priv = (void *)engine;
struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv04_fifo_chan *chan;
struct nv10_fifo_priv *priv = nv_engine(dev, engine); struct nv_channel_dma_class *args = data;
struct nv10_fifo_chan *fctx;
unsigned long flags;
int ret; int ret;
fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL); if (size < sizeof(*args))
if (!fctx) return -EINVAL;
return -ENOMEM;
ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0x800000,
fctx->ramfc = chan->id * 32; 0x10000, args->pushbuf,
(1 << NVDEV_ENGINE_DMAOBJ) |
/* map channel control registers */ (1 << NVDEV_ENGINE_SW) |
chan->user = ioremap(pci_resource_start(dev->pdev, 0) + (1 << NVDEV_ENGINE_GR), &chan);
NV03_USER(chan->id), PAGE_SIZE); *pobject = nv_object(chan);
if (!chan->user) { if (ret)
ret = -ENOMEM; return ret;
goto error;
} nv_parent(chan)->object_attach = nv04_fifo_object_attach;
nv_parent(chan)->object_detach = nv04_fifo_object_detach;
/* initialise default fifo context */ chan->ramfc = chan->base.chid * 32;
nv_wo32(priv->ramfc, fctx->ramfc + 0x00, chan->pushbuf_base);
nv_wo32(priv->ramfc, fctx->ramfc + 0x04, chan->pushbuf_base); nv_wo32(priv->ramfc, chan->ramfc + 0x00, args->offset);
nv_wo32(priv->ramfc, fctx->ramfc + 0x0c, chan->pushbuf->addr >> 4); nv_wo32(priv->ramfc, chan->ramfc + 0x04, args->offset);
nv_wo32(priv->ramfc, fctx->ramfc + 0x14, nv_wo32(priv->ramfc, chan->ramfc + 0x0c, chan->base.pushgpu->addr >> 4);
nv_wo32(priv->ramfc, chan->ramfc + 0x14,
NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES | NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES | NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
#ifdef __BIG_ENDIAN #ifdef __BIG_ENDIAN
NV_PFIFO_CACHE1_BIG_ENDIAN | NV_PFIFO_CACHE1_BIG_ENDIAN |
#endif #endif
NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8); NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8);
return 0;
}
/* enable dma mode on the channel */ static struct nouveau_ofuncs
spin_lock_irqsave(&dev_priv->context_switch_lock, flags); nv10_fifo_ofuncs = {
nv_mask(dev, NV04_PFIFO_MODE, (1 << chan->id), (1 << chan->id)); .ctor = nv10_fifo_chan_ctor,
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); .dtor = nv04_fifo_chan_dtor,
.init = nv04_fifo_chan_init,
.fini = nv04_fifo_chan_fini,
.rd32 = _nouveau_fifo_channel_rd32,
.wr32 = _nouveau_fifo_channel_wr32,
};
error: static struct nouveau_oclass
if (ret) nv10_fifo_sclass[] = {
priv->base.base.context_del(chan, engine); { 0x006e, &nv10_fifo_ofuncs },
return ret; {}
} };
/*******************************************************************************
* FIFO context - basically just the instmem reserved for the channel
******************************************************************************/
static struct nouveau_oclass
nv10_fifo_cclass = {
.handle = NV_ENGCTX(FIFO, 0x10),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv04_fifo_context_ctor,
.dtor = _nouveau_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
******************************************************************************/
int static int
nv10_fifo_create(struct drm_device *dev) nv10_fifo_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 nv04_instmem_priv *imem = nv04_instmem(parent);
struct nv10_fifo_priv *priv; struct nv04_fifo_priv *priv;
int ret;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
nouveau_gpuobj_ref(nvimem_ramro(dev), &priv->ramro);
nouveau_gpuobj_ref(nvimem_ramfc(dev), &priv->ramfc);
priv->base.base.destroy = nv04_fifo_destroy;
priv->base.base.init = nv04_fifo_init;
priv->base.base.fini = nv04_fifo_fini;
priv->base.base.context_new = nv10_fifo_context_new;
priv->base.base.context_del = nv04_fifo_context_del;
priv->base.channels = 31;
priv->ramfc_desc = nv10_ramfc;
dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base;
nouveau_irq_register(dev, 8, nv04_fifo_isr); ret = nouveau_fifo_create(parent, engine, oclass, 0, 31, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
nouveau_ramht_ref(imem->ramht, &priv->ramht);
nouveau_gpuobj_ref(imem->ramro, &priv->ramro);
nouveau_gpuobj_ref(imem->ramfc, &priv->ramfc);
nv_subdev(priv)->unit = 0x00000100;
nv_subdev(priv)->intr = nv04_fifo_intr;
nv_engine(priv)->cclass = &nv10_fifo_cclass;
nv_engine(priv)->sclass = nv10_fifo_sclass;
priv->base.pause = nv04_fifo_pause;
priv->base.start = nv04_fifo_start;
priv->ramfc_desc = nv10_ramfc;
return 0; return 0;
} }
struct nouveau_oclass
nv10_fifo_oclass = {
.handle = NV_ENGINE(FIFO, 0x10),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv10_fifo_ctor,
.dtor = nv04_fifo_dtor,
.init = nv04_fifo_init,
.fini = _nouveau_fifo_fini,
},
};
#ifndef __NV50_FIFO_H__
#define __NV50_FIFO_H__
struct nv50_fifo_priv {
struct nouveau_fifo base;
struct nouveau_gpuobj *playlist[2];
int cur_playlist;
};
struct nv50_fifo_base {
struct nouveau_fifo_base base;
struct nouveau_gpuobj *ramfc;
struct nouveau_gpuobj *cache;
struct nouveau_gpuobj *eng;
struct nouveau_gpuobj *pgd;
struct nouveau_vm *vm;
};
struct nv50_fifo_chan {
struct nouveau_fifo_chan base;
u32 subc[8];
struct nouveau_ramht *ramht;
};
void nv50_fifo_playlist_update(struct nv50_fifo_priv *);
void nv50_fifo_object_detach(struct nouveau_object *, int);
void nv50_fifo_chan_dtor(struct nouveau_object *);
int nv50_fifo_chan_fini(struct nouveau_object *, bool);
void nv50_fifo_context_dtor(struct nouveau_object *);
void nv50_fifo_dtor(struct nouveau_object *);
int nv50_fifo_init(struct nouveau_object *);
#endif
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
#define __NOUVEAU_GRCTX_H__ #define __NOUVEAU_GRCTX_H__
struct nouveau_grctx { struct nouveau_grctx {
struct drm_device *dev; struct nouveau_device *device;
enum { enum {
NOUVEAU_GRCTX_PROG, NOUVEAU_GRCTX_PROG,
...@@ -10,18 +10,18 @@ struct nouveau_grctx { ...@@ -10,18 +10,18 @@ struct nouveau_grctx {
} mode; } mode;
void *data; void *data;
uint32_t ctxprog_max; u32 ctxprog_max;
uint32_t ctxprog_len; u32 ctxprog_len;
uint32_t ctxprog_reg; u32 ctxprog_reg;
int ctxprog_label[32]; int ctxprog_label[32];
uint32_t ctxvals_pos; u32 ctxvals_pos;
uint32_t ctxvals_base; u32 ctxvals_base;
}; };
static inline void static inline void
cp_out(struct nouveau_grctx *ctx, uint32_t inst) cp_out(struct nouveau_grctx *ctx, u32 inst)
{ {
uint32_t *ctxprog = ctx->data; u32 *ctxprog = ctx->data;
if (ctx->mode != NOUVEAU_GRCTX_PROG) if (ctx->mode != NOUVEAU_GRCTX_PROG)
return; return;
...@@ -31,13 +31,13 @@ cp_out(struct nouveau_grctx *ctx, uint32_t inst) ...@@ -31,13 +31,13 @@ cp_out(struct nouveau_grctx *ctx, uint32_t inst)
} }
static inline void static inline void
cp_lsr(struct nouveau_grctx *ctx, uint32_t val) cp_lsr(struct nouveau_grctx *ctx, u32 val)
{ {
cp_out(ctx, CP_LOAD_SR | val); cp_out(ctx, CP_LOAD_SR | val);
} }
static inline void static inline void
cp_ctx(struct nouveau_grctx *ctx, uint32_t reg, uint32_t length) cp_ctx(struct nouveau_grctx *ctx, u32 reg, u32 length)
{ {
ctx->ctxprog_reg = (reg - 0x00400000) >> 2; ctx->ctxprog_reg = (reg - 0x00400000) >> 2;
...@@ -55,7 +55,7 @@ cp_ctx(struct nouveau_grctx *ctx, uint32_t reg, uint32_t length) ...@@ -55,7 +55,7 @@ cp_ctx(struct nouveau_grctx *ctx, uint32_t reg, uint32_t length)
static inline void static inline void
cp_name(struct nouveau_grctx *ctx, int name) cp_name(struct nouveau_grctx *ctx, int name)
{ {
uint32_t *ctxprog = ctx->data; u32 *ctxprog = ctx->data;
int i; int i;
if (ctx->mode != NOUVEAU_GRCTX_PROG) if (ctx->mode != NOUVEAU_GRCTX_PROG)
...@@ -115,7 +115,7 @@ cp_pos(struct nouveau_grctx *ctx, int offset) ...@@ -115,7 +115,7 @@ cp_pos(struct nouveau_grctx *ctx, int offset)
} }
static inline void static inline void
gr_def(struct nouveau_grctx *ctx, uint32_t reg, uint32_t val) gr_def(struct nouveau_grctx *ctx, u32 reg, u32 val)
{ {
if (ctx->mode != NOUVEAU_GRCTX_VALS) if (ctx->mode != NOUVEAU_GRCTX_VALS)
return; return;
......
#ifndef __NV20_GRAPH_H__
#define __NV20_GRAPH_H__
#include <core/enum.h>
#include <engine/graph.h>
#include <engine/fifo.h>
struct nv20_graph_priv {
struct nouveau_graph base;
struct nouveau_gpuobj *ctxtab;
};
struct nv20_graph_chan {
struct nouveau_graph_chan base;
int chid;
};
extern struct nouveau_oclass nv25_graph_sclass[];
int nv20_graph_context_init(struct nouveau_object *);
int nv20_graph_context_fini(struct nouveau_object *, bool);
void nv20_graph_tile_prog(struct nouveau_engine *, int);
void nv20_graph_intr(struct nouveau_subdev *);
void nv20_graph_dtor(struct nouveau_object *);
int nv20_graph_init(struct nouveau_object *);
int nv30_graph_init(struct nouveau_object *);
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#ifndef __NV40_GRAPH_H__
#define __NV40_GRAPH_H__
/* returns 1 if device is one of the nv4x using the 0x4497 object class,
* helpful to determine a number of other hardware features
*/
static inline int
nv44_graph_class(void *priv)
{
struct nouveau_device *device = nv_device(priv);
if ((device->chipset & 0xf0) == 0x60)
return 1;
return !(0x0baf & (1 << (device->chipset & 0x0f)));
}
void nv40_grctx_init(struct nouveau_device *, u32 *size);
void nv40_grctx_fill(struct nouveau_device *, struct nouveau_gpuobj *);
#endif
#ifndef __NV50_GRAPH_H__
#define __NV50_GRAPH_H__
int nv50_grctx_init(struct nouveau_device *, u32 *size);
void nv50_grctx_fill(struct nouveau_device *, struct nouveau_gpuobj *);
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#ifndef __NOUVEAU_BSP_H__
#define __NOUVEAU_BSP_H__
#include <core/engine.h>
#include <core/engctx.h>
struct nouveau_bsp_chan {
struct nouveau_engctx base;
};
#define nouveau_bsp_context_create(p,e,c,g,s,a,f,d) \
nouveau_engctx_create((p), (e), (c), (g), (s), (a), (f), (d))
#define nouveau_bsp_context_destroy(d) \
nouveau_engctx_destroy(&(d)->base)
#define nouveau_bsp_context_init(d) \
nouveau_engctx_init(&(d)->base)
#define nouveau_bsp_context_fini(d,s) \
nouveau_engctx_fini(&(d)->base, (s))
#define _nouveau_bsp_context_dtor _nouveau_engctx_dtor
#define _nouveau_bsp_context_init _nouveau_engctx_init
#define _nouveau_bsp_context_fini _nouveau_engctx_fini
#define _nouveau_bsp_context_rd32 _nouveau_engctx_rd32
#define _nouveau_bsp_context_wr32 _nouveau_engctx_wr32
struct nouveau_bsp {
struct nouveau_engine base;
};
#define nouveau_bsp_create(p,e,c,d) \
nouveau_engine_create((p), (e), (c), true, "PBSP", "bsp", (d))
#define nouveau_bsp_destroy(d) \
nouveau_engine_destroy(&(d)->base)
#define nouveau_bsp_init(d) \
nouveau_engine_init(&(d)->base)
#define nouveau_bsp_fini(d,s) \
nouveau_engine_fini(&(d)->base, (s))
#define _nouveau_bsp_dtor _nouveau_engine_dtor
#define _nouveau_bsp_init _nouveau_engine_init
#define _nouveau_bsp_fini _nouveau_engine_fini
extern struct nouveau_oclass nv84_bsp_oclass;
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
int nouveau_device_create_(struct pci_dev *, u64 name, const char *sname, int nouveau_device_create_(struct pci_dev *, u64 name, const char *sname,
const char *cfg, const char *dbg, int, void **); const char *cfg, const char *dbg, int, void **);
void nouveau_device_destroy(struct nouveau_device **);
int nv04_identify(struct nouveau_device *); int nv04_identify(struct nouveau_device *);
int nv10_identify(struct nouveau_device *); int nv10_identify(struct nouveau_device *);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment