diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c | 269 |
1 files changed, 229 insertions, 40 deletions
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c index 57353015329..b19a406e55d 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c @@ -35,6 +35,156 @@ nv_icmd(struct drm_device *priv, u32 icmd, u32 data) while (nv_rd32(priv, 0x400700) & 2) {} } +int +nvc0_grctx_init(struct drm_device *priv, struct nvc0_graph_priv *oprv, + struct nvc0_grctx *info) +{ + struct nouveau_gpuobj *chan; + u32 size = (0x80000 + oprv->size + 4095) & ~4095; + int ret, i; + + /* allocate memory to for a "channel", which we'll use to generate + * the default context values + */ + ret = nouveau_gpuobj_new(priv, NULL, size, 0x1000, + NVOBJ_FLAG_ZERO_ALLOC, &info->chan); + chan = info->chan; + if (ret) { + NV_ERROR(priv, "failed to allocate channel memory, %d\n", ret); + return ret; + } + + /* PGD pointer */ + nv_wo32(chan, 0x0200, lower_32_bits(chan->addr + 0x1000)); + nv_wo32(chan, 0x0204, upper_32_bits(chan->addr + 0x1000)); + nv_wo32(chan, 0x0208, 0xffffffff); + nv_wo32(chan, 0x020c, 0x000000ff); + + /* PGT[0] pointer */ + nv_wo32(chan, 0x1000, 0x00000000); + nv_wo32(chan, 0x1004, 0x00000001 | (chan->addr + 0x2000) >> 8); + + /* identity-map the whole "channel" into its own vm */ + for (i = 0; i < size / 4096; i++) { + u64 addr = ((chan->addr + (i * 4096)) >> 8) | 1; + nv_wo32(chan, 0x2000 + (i * 8), lower_32_bits(addr)); + nv_wo32(chan, 0x2004 + (i * 8), upper_32_bits(addr)); + } + + /* context pointer (virt) */ + nv_wo32(chan, 0x0210, 0x00080004); + nv_wo32(chan, 0x0214, 0x00000000); + + nvimem_flush(priv); + + nv_wr32(priv, 0x100cb8, (chan->addr + 0x1000) >> 8); + nv_wr32(priv, 0x100cbc, 0x80000001); + nv_wait(priv, 0x100c80, 0x00008000, 0x00008000); + + /* setup default state for mmio list construction */ + info->dev = priv; + info->data = oprv->mmio_data; + info->mmio = oprv->mmio_list; + info->addr = 0x2000 + (i * 8); + info->priv = oprv; + info->buffer_nr = 0; + + if (oprv->firmware) { + nv_wr32(priv, 0x409840, 0x00000030); + nv_wr32(priv, 0x409500, 0x80000000 | chan->addr >> 12); + nv_wr32(priv, 0x409504, 0x00000003); + if (!nv_wait(priv, 0x409800, 0x00000010, 0x00000010)) + NV_ERROR(priv, "load_ctx timeout\n"); + + nv_wo32(chan, 0x8001c, 1); + nv_wo32(chan, 0x80020, 0); + nv_wo32(chan, 0x80028, 0); + nv_wo32(chan, 0x8002c, 0); + nvimem_flush(priv); + return 0; + } + + /* HUB_FUC(SET_CHAN) */ + nv_wr32(priv, 0x409840, 0x80000000); + nv_wr32(priv, 0x409500, 0x80000000 | chan->addr >> 12); + nv_wr32(priv, 0x409504, 0x00000001); + if (!nv_wait(priv, 0x409800, 0x80000000, 0x80000000)) { + NV_ERROR(priv, "HUB_SET_CHAN timeout\n"); + nvc0_graph_ctxctl_debug(priv); + nouveau_gpuobj_ref(NULL, &info->chan); + return -EBUSY; + } + + return 0; +} + +void +nvc0_grctx_data(struct nvc0_grctx *info, u32 size, u32 align, u32 access) +{ + info->buffer[info->buffer_nr] = info->addr; + info->buffer[info->buffer_nr] += (align - 1); + info->buffer[info->buffer_nr] &= ~(align - 1); + info->addr = info->buffer[info->buffer_nr++] + size; + + info->data->size = size; + info->data->align = align; + info->data->access = access; + info->data++; +} + +void +nvc0_grctx_mmio(struct nvc0_grctx *info, u32 addr, u32 data, u32 shift, u32 buf) +{ + info->mmio->addr = addr; + info->mmio->data = data; + info->mmio->shift = shift; + info->mmio->buffer = buf; + info->mmio++; + + if (shift) + data |= info->buffer[buf] >> shift; + nv_wr32(info->dev, addr, data); +} + +int +nvc0_grctx_fini(struct nvc0_grctx *info) +{ + struct nvc0_graph_priv *priv = info->priv; + int i; + + if (priv->firmware) { + nv_wr32(info->dev, 0x409840, 0x00000003); + nv_wr32(info->dev, 0x409500, 0x80000000 | info->chan->addr >> 12); + nv_wr32(info->dev, 0x409504, 0x00000009); + if (!nv_wait(info->dev, 0x409800, 0x00000001, 0x00000000)) { + NV_ERROR(info->dev, "unload_ctx timeout\n"); + return -EBUSY; + } + + goto save; + } + + /* HUB_FUC(CTX_SAVE) */ + nv_wr32(info->dev, 0x409840, 0x80000000); + nv_wr32(info->dev, 0x409500, 0x80000000 | info->chan->addr >> 12); + nv_wr32(info->dev, 0x409504, 0x00000002); + if (!nv_wait(info->dev, 0x409800, 0x80000000, 0x80000000)) { + NV_ERROR(info->dev, "HUB_CTX_SAVE timeout\n"); + nvc0_graph_ctxctl_debug(info->dev); + return -EBUSY; + } + +save: + priv->data = kmalloc(priv->size, GFP_KERNEL); + if (priv->data) { + for (i = 0; i < priv->size; i += 4) + priv->data[i / 4] = nv_ro32(info->chan, 0x80000 + i); + } + + nouveau_gpuobj_ref(NULL, &info->chan); + return priv->data ? 0 : -ENOMEM; +} + static void nvc0_grctx_generate_9097(struct drm_device *priv) { @@ -1779,16 +1929,19 @@ nvc0_grctx_generate_tp(struct drm_device *priv) } int -nvc0_grctx_generate(struct nouveau_channel *chan) +nvc0_grctx_generate(struct drm_device *priv) { - struct drm_nouveau_private *dev_priv = chan->dev->dev_private; - struct nvc0_graph_priv *oprv = nv_engine(chan->dev, NVOBJ_ENGINE_GR); - struct nvc0_graph_chan *grch = chan->engctx[NVOBJ_ENGINE_GR]; - struct drm_device *priv = chan->dev; - int i, gpc, tp, id; + struct drm_nouveau_private *dev_priv = priv->dev_private; + struct nvc0_graph_priv *oprv = nv_engine(priv, NVOBJ_ENGINE_GR); + struct nvc0_grctx info; + int ret, i, gpc, tpc, id; u32 fermi = nvc0_graph_class(priv); u32 r000260, tmp; + ret = nvc0_grctx_init(priv, oprv, &info); + if (ret) + return ret; + r000260 = nv_rd32(priv, 0x000260); nv_wr32(priv, 0x000260, r000260 & ~1); nv_wr32(priv, 0x400208, 0x00000000); @@ -1808,19 +1961,55 @@ nvc0_grctx_generate(struct nouveau_channel *chan) nv_wr32(priv, 0x404154, 0x00000000); - /* fuc "mmio list" writes */ - for (i = 0; i < grch->mmio_nr * 8; i += 8) { - u32 reg = nv_ro32(grch->mmio, i + 0); - nv_wr32(priv, reg, nv_ro32(grch->mmio, i + 4)); + /* generate per-context mmio list data */ + mmio_data(0x002000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); + mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); + mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW); + mmio_list(0x408004, 0x00000000, 8, 0); + mmio_list(0x408008, 0x80000018, 0, 0); + mmio_list(0x40800c, 0x00000000, 8, 1); + mmio_list(0x408010, 0x80000000, 0, 0); + mmio_list(0x418810, 0x80000000, 12, 2); + mmio_list(0x419848, 0x10000000, 12, 2); + mmio_list(0x419004, 0x00000000, 8, 1); + mmio_list(0x419008, 0x00000000, 0, 0); + mmio_list(0x418808, 0x00000000, 8, 0); + mmio_list(0x41880c, 0x80000018, 0, 0); + if (dev_priv->chipset != 0xc1) { + tmp = 0x02180000; + mmio_list(0x405830, tmp, 0, 0); + for (gpc = 0; gpc < oprv->gpc_nr; gpc++) { + for (tpc = 0; tpc < oprv->tpc_nr[gpc]; tpc++) { + u32 reg = TPC_UNIT(gpc, tpc, 0x0520); + mmio_list(reg, tmp, 0, 0); + tmp += 0x0324; + } + } + } else { + tmp = 0x02180000; + mmio_list(0x405830, 0x00000218 | tmp, 0, 0); + mmio_list(0x4064c4, 0x0086ffff, 0, 0); + for (gpc = 0; gpc < oprv->gpc_nr; gpc++) { + for (tpc = 0; tpc < oprv->tpc_nr[gpc]; tpc++) { + u32 reg = TPC_UNIT(gpc, tpc, 0x0520); + mmio_list(reg, 0x10000000 | tmp, 0, 0); + tmp += 0x0324; + } + for (tpc = 0; tpc < oprv->tpc_nr[gpc]; tpc++) { + u32 reg = TPC_UNIT(gpc, tpc, 0x0544); + mmio_list(reg, tmp, 0, 0); + tmp += 0x0324; + } + } } - for (tp = 0, id = 0; tp < 4; tp++) { + for (tpc = 0, id = 0; tpc < 4; tpc++) { for (gpc = 0; gpc < oprv->gpc_nr; gpc++) { - if (tp < oprv->tpc_nr[gpc]) { - nv_wr32(priv, TPC_UNIT(gpc, tp, 0x698), id); - nv_wr32(priv, TPC_UNIT(gpc, tp, 0x4e8), id); - nv_wr32(priv, GPC_UNIT(gpc, 0x0c10 + tp * 4), id); - nv_wr32(priv, TPC_UNIT(gpc, tp, 0x088), id); + if (tpc < oprv->tpc_nr[gpc]) { + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x698), id); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x4e8), id); + nv_wr32(priv, GPC_UNIT(gpc, 0x0c10 + tpc * 4), id); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x088), id); id++; } @@ -1843,18 +2032,18 @@ nvc0_grctx_generate(struct nouveau_channel *chan) nv_wr32(priv, 0x40587c, 0x00000000); if (1) { - u8 tpnr[GPC_MAX], data[TPC_MAX]; + u8 tpcnr[GPC_MAX], data[TPC_MAX]; - memcpy(tpnr, oprv->tpc_nr, sizeof(oprv->tpc_nr)); + memcpy(tpcnr, oprv->tpc_nr, sizeof(oprv->tpc_nr)); memset(data, 0x1f, sizeof(data)); gpc = -1; - for (tp = 0; tp < oprv->tpc_total; tp++) { + for (tpc = 0; tpc < oprv->tpc_total; tpc++) { do { gpc = (gpc + 1) % oprv->gpc_nr; - } while (!tpnr[gpc]); - tpnr[gpc]--; - data[tp] = gpc; + } while (!tpcnr[gpc]); + tpcnr[gpc]--; + data[tpc] = gpc; } for (i = 0; i < 4; i++) @@ -1863,24 +2052,24 @@ nvc0_grctx_generate(struct nouveau_channel *chan) if (1) { u32 data[6] = {}, data2[2] = {}; - u8 tpnr[GPC_MAX]; + u8 tpcnr[GPC_MAX]; u8 shift, ntpcv; /* calculate first set of magics */ - memcpy(tpnr, oprv->tpc_nr, sizeof(oprv->tpc_nr)); + memcpy(tpcnr, oprv->tpc_nr, sizeof(oprv->tpc_nr)); gpc = -1; - for (tp = 0; tp < oprv->tpc_total; tp++) { + for (tpc = 0; tpc < oprv->tpc_total; tpc++) { do { gpc = (gpc + 1) % oprv->gpc_nr; - } while (!tpnr[gpc]); - tpnr[gpc]--; + } while (!tpcnr[gpc]); + tpcnr[gpc]--; - data[tp / 6] |= gpc << ((tp % 6) * 5); + data[tpc / 6] |= gpc << ((tpc % 6) * 5); } - for (; tp < 32; tp++) - data[tp / 6] |= 7 << ((tp % 6) * 5); + for (; tpc < 32; tpc++) + data[tpc / 6] |= 7 << ((tpc % 6) * 5); /* and the second... */ shift = 0; @@ -1918,12 +2107,12 @@ nvc0_grctx_generate(struct nouveau_channel *chan) } if (1) { - u32 tp_mask = 0, tp_set = 0; - u8 tpnr[GPC_MAX], a, b; + u32 tpc_mask = 0, tpc_set = 0; + u8 tpcnr[GPC_MAX], a, b; - memcpy(tpnr, oprv->tpc_nr, sizeof(oprv->tpc_nr)); + memcpy(tpcnr, oprv->tpc_nr, sizeof(oprv->tpc_nr)); for (gpc = 0; gpc < oprv->gpc_nr; gpc++) - tp_mask |= ((1 << oprv->tpc_nr[gpc]) - 1) << (gpc * 8); + tpc_mask |= ((1 << oprv->tpc_nr[gpc]) - 1) << (gpc * 8); for (i = 0, gpc = -1, b = -1; i < 32; i++) { a = (i * (oprv->tpc_total - 1)) / 32; @@ -1931,14 +2120,14 @@ nvc0_grctx_generate(struct nouveau_channel *chan) b = a; do { gpc = (gpc + 1) % oprv->gpc_nr; - } while (!tpnr[gpc]); - tp = oprv->tpc_nr[gpc] - tpnr[gpc]--; + } while (!tpcnr[gpc]); + tpc = oprv->tpc_nr[gpc] - tpcnr[gpc]--; - tp_set |= 1 << ((gpc * 8) + tp); + tpc_set |= 1 << ((gpc * 8) + tpc); } - nv_wr32(priv, 0x406800 + (i * 0x20), tp_set); - nv_wr32(priv, 0x406c00 + (i * 0x20), tp_set ^ tp_mask); + nv_wr32(priv, 0x406800 + (i * 0x20), tpc_set); + nv_wr32(priv, 0x406c00 + (i * 0x20), tpc_set ^ tpc_mask); } } @@ -2867,5 +3056,5 @@ nvc0_grctx_generate(struct nouveau_channel *chan) nvc0_grctx_generate_90c0(priv); nv_wr32(priv, 0x000260, r000260); - return 0; + return nvc0_grctx_fini(&info); } |