/*
* Copyright 2010 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 <linux/firmware.h>
#include <linux/module.h>
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_mm.h"
#include "nvc0_graph.h"
#include "nvc0_grhub.fuc.h"
#include "nvc0_grgpc.fuc.h"
static void
nvc0_graph_ctxctl_debug_unit(struct drm_device *dev, u32 base)
{
NV_INFO(dev, "PGRAPH: %06x - done 0x%08x\n", base,
nv_rd32(dev, base + 0x400));
NV_INFO(dev, "PGRAPH: %06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base,
nv_rd32(dev, base + 0x800), nv_rd32(dev, base + 0x804),
nv_rd32(dev, base + 0x808), nv_rd32(dev, base + 0x80c));
NV_INFO(dev, "PGRAPH: %06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base,
nv_rd32(dev, base + 0x810), nv_rd32(dev, base + 0x814),
nv_rd32(dev, base + 0x818), nv_rd32(dev, base + 0x81c));
}
static void
nvc0_graph_ctxctl_debug(struct drm_device *dev)
{
u32 gpcnr = nv_rd32(dev, 0x409604) & 0xffff;
u32 gpc;
nvc0_graph_ctxctl_debug_unit(dev, 0x409000);
for (gpc = 0; gpc < gpcnr; gpc++)
nvc0_graph_ctxctl_debug_unit(dev, 0x502000 + (gpc * 0x8000));
}
static int
nvc0_graph_load_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
nv_wr32(dev, 0x409840, 0x00000030);
nv_wr32(dev, 0x409500, 0x80000000 | chan->ramin->vinst >> 12);
nv_wr32(dev, 0x409504, 0x00000003);
if (!nv_wait(dev, 0x409800, 0x00000010, 0x00000010))
NV_ERROR(dev, "PGRAPH: load_ctx timeout\n");
return 0;
}
static int
nvc0_graph_unload_context_to(struct drm_device *dev, u64 chan)
{
nv_wr32(dev, 0x409840, 0x00000003);
nv_wr32(dev, 0x409500, 0x80000000 | chan >> 12);
nv_wr32(dev, 0x409504, 0x00000009);
if (!nv_wait(dev, 0x409800, 0x00000001, 0x00000000)) {
NV_ERROR(dev, "PGRAPH: unload_ctx timeout\n");
return -EBUSY;
}
return 0;
}
static int
nvc0_graph_construct_context(struct nouveau_channel *chan)
{
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
struct nvc0_graph_priv *priv = nv_engine(chan->dev, NVOBJ_ENGINE_GR);
struct nvc0_graph_chan *grch = chan->engctx[NVOBJ_ENGINE_GR];
struct drm_device *dev = chan->dev;
int ret, i;
u32 *ctx;
ctx = kmalloc(priv->grctx_size, GFP_KERNEL);
if (!ctx)
return -ENOMEM;
if (!nouveau_ctxfw) {
nv_wr32(dev, 0x409840, 0x80000000);
nv_wr32(dev, 0x409500, 0x80000000 | chan->ramin->vinst >> 12);
nv_wr32(dev, 0x409504, 0x00000001);
if (!nv_wait(dev, 0x409800, 0x80000000, 0x80000000)) {
NV_ERROR(dev, "PGRAPH: HUB_SET_CHAN timeout\n");
nvc0_graph_ctxctl_debug(dev);
ret = -EBUSY;
goto err;
}
} else {
nvc0_graph_load_context(chan);
nv_wo32(grch->grctx, 0x1c, 1);
nv_wo32(grch->grctx, 0x20, 0);
nv_wo32(grch->grctx, 0x28, 0);
nv_wo32(grch->grctx, 0x2c, 0);
dev_priv->engine.instmem.flush(dev);
}
ret = nvc0_grctx_generate(chan);
if (ret)
goto err;
if (!nouveau_ctxfw) {
nv_wr32(dev, 0x409840, 0x80000000);
nv_wr32(dev, 0x409500, 0x80000000 | chan->ramin->vinst >> 12);
nv_wr32(dev, 0x409504, 0x00000002);
if (!nv_wait(dev, 0x409800, 0x80000000, 0x80000000)) {
NV_ERROR(dev, "PGRAPH: HUB_CTX_SAVE timeout\n");
nvc0_graph_ctxctl_debug(dev);
ret = -EBUSY;
goto err;
}
} else {
ret = nvc0_graph_unload_context_to(dev, chan->ramin->vinst);
if (ret)
goto err;
}
for (i