Commit e30441ad authored by Christoph Bumiller's avatar Christoph Bumiller Committed by Ben Skeggs

drm/nvc0-/ltcg: implement VRAM compression

Signed-off-by: default avatarChristoph Bumiller <e0425955@student.tuwien.ac.at>
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent de7b7d59
...@@ -4,8 +4,15 @@ ...@@ -4,8 +4,15 @@
#include <core/subdev.h> #include <core/subdev.h>
#include <core/device.h> #include <core/device.h>
struct nouveau_mm_node;
struct nouveau_ltcg { struct nouveau_ltcg {
struct nouveau_subdev base; struct nouveau_subdev base;
int (*tags_alloc)(struct nouveau_ltcg *, u32 count,
struct nouveau_mm_node **);
void (*tags_free)(struct nouveau_ltcg *, struct nouveau_mm_node **);
void (*tags_clear)(struct nouveau_ltcg *, u32 first, u32 count);
}; };
static inline struct nouveau_ltcg * static inline struct nouveau_ltcg *
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
*/ */
#include <subdev/fb.h> #include <subdev/fb.h>
#include <subdev/ltcg.h>
#include <subdev/bios.h> #include <subdev/bios.h>
struct nvc0_fb_priv { struct nvc0_fb_priv {
...@@ -31,34 +32,14 @@ struct nvc0_fb_priv { ...@@ -31,34 +32,14 @@ struct nvc0_fb_priv {
dma_addr_t r100c10; dma_addr_t r100c10;
}; };
/* 0 = unsupported extern const u8 nvc0_pte_storage_type_map[256];
* 1 = non-compressed
* 3 = compressed
*/
static const u8 types[256] = {
1, 1, 3, 3, 3, 3, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3,
3, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 3, 3, 3, 3, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
3, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3,
3, 3, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3, 0, 3, 0, 3,
3, 0, 3, 3, 3, 3, 3, 0, 0, 3, 0, 3, 0, 3, 3, 0,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 1, 1, 0
};
static bool static bool
nvc0_fb_memtype_valid(struct nouveau_fb *pfb, u32 tile_flags) nvc0_fb_memtype_valid(struct nouveau_fb *pfb, u32 tile_flags)
{ {
u8 memtype = (tile_flags & 0x0000ff00) >> 8; u8 memtype = (tile_flags & 0x0000ff00) >> 8;
return likely((types[memtype] == 1)); return likely((nvc0_pte_storage_type_map[memtype] != 0xff));
} }
static int static int
...@@ -130,6 +111,7 @@ nvc0_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, ...@@ -130,6 +111,7 @@ nvc0_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
int type = (memtype & 0x0ff); int type = (memtype & 0x0ff);
int back = (memtype & 0x800); int back = (memtype & 0x800);
int ret; int ret;
const bool comp = nvc0_pte_storage_type_map[type] != type;
size >>= 12; size >>= 12;
align >>= 12; align >>= 12;
...@@ -142,10 +124,22 @@ nvc0_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, ...@@ -142,10 +124,22 @@ nvc0_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
return -ENOMEM; return -ENOMEM;
INIT_LIST_HEAD(&mem->regions); INIT_LIST_HEAD(&mem->regions);
mem->memtype = type;
mem->size = size; mem->size = size;
mutex_lock(&pfb->base.mutex); mutex_lock(&pfb->base.mutex);
if (comp) {
struct nouveau_ltcg *ltcg = nouveau_ltcg(pfb->base.base.parent);
/* compression only works with lpages */
if (align == (1 << (17 - 12))) {
int n = size >> 5;
ltcg->tags_alloc(ltcg, n, &mem->tag);
}
if (unlikely(!mem->tag))
type = nvc0_pte_storage_type_map[type];
}
mem->memtype = type;
do { do {
if (back) if (back)
ret = nouveau_mm_tail(mm, 1, size, ncmin, align, &r); ret = nouveau_mm_tail(mm, 1, size, ncmin, align, &r);
...@@ -168,6 +162,17 @@ nvc0_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, ...@@ -168,6 +162,17 @@ nvc0_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
return 0; return 0;
} }
static void
nvc0_fb_vram_del(struct nouveau_fb *pfb, struct nouveau_mem **pmem)
{
struct nouveau_ltcg *ltcg = nouveau_ltcg(pfb->base.base.parent);
if ((*pmem)->tag)
ltcg->tags_free(ltcg, &(*pmem)->tag);
nv50_fb_vram_del(pfb, pmem);
}
static int static int
nvc0_fb_init(struct nouveau_object *object) nvc0_fb_init(struct nouveau_object *object)
{ {
...@@ -215,7 +220,7 @@ nvc0_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, ...@@ -215,7 +220,7 @@ nvc0_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->base.memtype_valid = nvc0_fb_memtype_valid; priv->base.memtype_valid = nvc0_fb_memtype_valid;
priv->base.ram.init = nvc0_fb_vram_init; priv->base.ram.init = nvc0_fb_vram_init;
priv->base.ram.get = nvc0_fb_vram_new; priv->base.ram.get = nvc0_fb_vram_new;
priv->base.ram.put = nv50_fb_vram_del; priv->base.ram.put = nvc0_fb_vram_del;
priv->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO); priv->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (priv->r100c10_page) { if (priv->r100c10_page) {
......
...@@ -23,10 +23,17 @@ ...@@ -23,10 +23,17 @@
*/ */
#include <subdev/ltcg.h> #include <subdev/ltcg.h>
#include <subdev/fb.h>
#include <subdev/timer.h>
struct nvc0_ltcg_priv { struct nvc0_ltcg_priv {
struct nouveau_ltcg base; struct nouveau_ltcg base;
u32 part_nr;
u32 part_mask;
u32 subp_nr; u32 subp_nr;
struct nouveau_mm tags;
u32 num_tags;
struct nouveau_mm_node *tag_ram;
}; };
static void static void
...@@ -61,12 +68,105 @@ nvc0_ltcg_intr(struct nouveau_subdev *subdev) ...@@ -61,12 +68,105 @@ nvc0_ltcg_intr(struct nouveau_subdev *subdev)
nv_mask(priv, 0x000640, 0x02000000, 0x00000000); nv_mask(priv, 0x000640, 0x02000000, 0x00000000);
} }
static int
nvc0_ltcg_tags_alloc(struct nouveau_ltcg *ltcg, u32 n,
struct nouveau_mm_node **pnode)
{
struct nvc0_ltcg_priv *priv = (struct nvc0_ltcg_priv *)ltcg;
int ret;
ret = nouveau_mm_head(&priv->tags, 1, n, n, 1, pnode);
if (ret)
*pnode = NULL;
return ret;
}
static void
nvc0_ltcg_tags_free(struct nouveau_ltcg *ltcg, struct nouveau_mm_node **pnode)
{
struct nvc0_ltcg_priv *priv = (struct nvc0_ltcg_priv *)ltcg;
nouveau_mm_free(&priv->tags, pnode);
}
static void
nvc0_ltcg_tags_clear(struct nouveau_ltcg *ltcg, u32 first, u32 count)
{
struct nvc0_ltcg_priv *priv = (struct nvc0_ltcg_priv *)ltcg;
u32 last = first + count - 1;
int p, i;
BUG_ON((first > last) || (last >= priv->num_tags));
nv_wr32(priv, 0x17e8cc, first);
nv_wr32(priv, 0x17e8d0, last);
nv_wr32(priv, 0x17e8c8, 0x4); /* trigger clear */
/* wait until it's finished with clearing */
for (p = 0; p < priv->part_nr; ++p) {
if (!(priv->part_mask & (1 << p)))
continue;
for (i = 0; i < priv->subp_nr; ++i)
nv_wait(priv, 0x1410c8 + p * 0x2000 + i * 0x400, ~0, 0);
}
}
/* TODO: Figure out tag memory details and drop the over-cautious allocation.
*/
static int
nvc0_ltcg_init_tag_ram(struct nouveau_fb *pfb, struct nvc0_ltcg_priv *priv)
{
u32 tag_size, tag_margin, tag_align;
int ret;
nv_wr32(priv, 0x17e8d8, priv->part_nr);
/* tags for 1/4 of VRAM should be enough (8192/4 per GiB of VRAM) */
priv->num_tags = (pfb->ram.size >> 17) / 4;
if (priv->num_tags > (1 << 17))
priv->num_tags = 1 << 17; /* we have 17 bits in PTE */
priv->num_tags = (priv->num_tags + 63) & ~63; /* round up to 64 */
tag_align = priv->part_nr * 0x800;
tag_margin = (tag_align < 0x6000) ? 0x6000 : tag_align;
/* 4 part 4 sub: 0x2000 bytes for 56 tags */
/* 3 part 4 sub: 0x6000 bytes for 168 tags */
/*
* About 147 bytes per tag. Let's be safe and allocate x2, which makes
* 0x4980 bytes for 64 tags, and round up to 0x6000 bytes for 64 tags.
*
* For 4 GiB of memory we'll have 8192 tags which makes 3 MiB, < 0.1 %.
*/
tag_size = (priv->num_tags / 64) * 0x6000 + tag_margin;
tag_size += tag_align;
tag_size = (tag_size + 0xfff) >> 12; /* round up */
ret = nouveau_mm_tail(&pfb->vram, 0, tag_size, tag_size, 1,
&priv->tag_ram);
if (ret) {
priv->num_tags = 0;
} else {
u64 tag_base = (priv->tag_ram->offset << 12) + tag_margin;
tag_base += tag_align - 1;
tag_base /= tag_align;
nv_wr32(priv, 0x17e8d4, tag_base);
}
ret = nouveau_mm_init(&priv->tags, 0, priv->num_tags, 1);
return ret;
}
static int static int
nvc0_ltcg_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nvc0_ltcg_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size, struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject) struct nouveau_object **pobject)
{ {
struct nvc0_ltcg_priv *priv; struct nvc0_ltcg_priv *priv;
struct nouveau_fb *pfb = nouveau_fb(parent);
int ret; int ret;
ret = nouveau_ltcg_create(parent, engine, oclass, &priv); ret = nouveau_ltcg_create(parent, engine, oclass, &priv);
...@@ -74,19 +174,44 @@ nvc0_ltcg_ctor(struct nouveau_object *parent, struct nouveau_object *engine, ...@@ -74,19 +174,44 @@ nvc0_ltcg_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret) if (ret)
return ret; return ret;
priv->subp_nr = nv_rd32(priv, 0x17e8dc) >> 24; priv->part_nr = nv_rd32(priv, 0x022438);
priv->part_mask = nv_rd32(priv, 0x022554);
priv->subp_nr = nv_rd32(priv, 0x17e8dc) >> 28;
nv_mask(priv, 0x17e820, 0x00100000, 0x00000000); /* INTR_EN &= ~0x10 */ nv_mask(priv, 0x17e820, 0x00100000, 0x00000000); /* INTR_EN &= ~0x10 */
ret = nvc0_ltcg_init_tag_ram(pfb, priv);
if (ret)
return ret;
priv->base.tags_alloc = nvc0_ltcg_tags_alloc;
priv->base.tags_free = nvc0_ltcg_tags_free;
priv->base.tags_clear = nvc0_ltcg_tags_clear;
nv_subdev(priv)->intr = nvc0_ltcg_intr; nv_subdev(priv)->intr = nvc0_ltcg_intr;
return 0; return 0;
} }
static void
nvc0_ltcg_dtor(struct nouveau_object *object)
{
struct nouveau_ltcg *ltcg = (struct nouveau_ltcg *)object;
struct nvc0_ltcg_priv *priv = (struct nvc0_ltcg_priv *)ltcg;
struct nouveau_fb *pfb = nouveau_fb(ltcg->base.base.parent);
nouveau_mm_fini(&priv->tags);
nouveau_mm_free(&pfb->vram, &priv->tag_ram);
nouveau_ltcg_destroy(ltcg);
}
struct nouveau_oclass struct nouveau_oclass
nvc0_ltcg_oclass = { nvc0_ltcg_oclass = {
.handle = NV_SUBDEV(LTCG, 0xc0), .handle = NV_SUBDEV(LTCG, 0xc0),
.ofuncs = &(struct nouveau_ofuncs) { .ofuncs = &(struct nouveau_ofuncs) {
.ctor = nvc0_ltcg_ctor, .ctor = nvc0_ltcg_ctor,
.dtor = _nouveau_ltcg_dtor, .dtor = nvc0_ltcg_dtor,
.init = _nouveau_ltcg_init, .init = _nouveau_ltcg_init,
.fini = _nouveau_ltcg_fini, .fini = _nouveau_ltcg_fini,
}, },
......
...@@ -28,12 +28,54 @@ ...@@ -28,12 +28,54 @@
#include <subdev/timer.h> #include <subdev/timer.h>
#include <subdev/fb.h> #include <subdev/fb.h>
#include <subdev/vm.h> #include <subdev/vm.h>
#include <subdev/ltcg.h>
struct nvc0_vmmgr_priv { struct nvc0_vmmgr_priv {
struct nouveau_vmmgr base; struct nouveau_vmmgr base;
spinlock_t lock; spinlock_t lock;
}; };
/* Map from compressed to corresponding uncompressed storage type.
* The value 0xff represents an invalid storage type.
*/
const u8 nvc0_pte_storage_type_map[256] =
{
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0xff, 0x01, /* 0x00 */
0x01, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x11, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11, /* 0x10 */
0x11, 0x11, 0x11, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x26, 0x27, /* 0x20 */
0x28, 0x29, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30 */
0xff, 0xff, 0x26, 0x27, 0x28, 0x29, 0x26, 0x27,
0x28, 0x29, 0xff, 0xff, 0xff, 0xff, 0x46, 0xff, /* 0x40 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x46, 0x46, 0x46, 0x46, 0xff, 0xff, 0xff, /* 0x50 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70 */
0xff, 0xff, 0xff, 0x7b, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b, 0x7b, /* 0x80 */
0x7b, 0x7b, 0xff, 0x8b, 0x8c, 0x8d, 0x8e, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x8b, 0x8c, 0x8d, 0x8e, 0xa7, /* 0xa0 */
0xa8, 0xa9, 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7,
0xa8, 0xa9, 0xaa, 0xc3, 0xff, 0xff, 0xff, 0xff, /* 0xc0 */
0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xc3, 0xc3,
0xc3, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0 */
0xfe, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe,
0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, /* 0xe0 */
0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xf0 */
0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfd, 0xfe, 0xff
};
static void static void
nvc0_vm_map_pgt(struct nouveau_gpuobj *pgd, u32 index, nvc0_vm_map_pgt(struct nouveau_gpuobj *pgd, u32 index,
struct nouveau_gpuobj *pgt[2]) struct nouveau_gpuobj *pgt[2])
...@@ -68,10 +110,20 @@ static void ...@@ -68,10 +110,20 @@ static void
nvc0_vm_map(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt, nvc0_vm_map(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt,
struct nouveau_mem *mem, u32 pte, u32 cnt, u64 phys, u64 delta) struct nouveau_mem *mem, u32 pte, u32 cnt, u64 phys, u64 delta)
{ {
u32 next = 1 << (vma->node->type - 8); u64 next = 1 << (vma->node->type - 8);
phys = nvc0_vm_addr(vma, phys, mem->memtype, 0); phys = nvc0_vm_addr(vma, phys, mem->memtype, 0);
pte <<= 3; pte <<= 3;
if (mem->tag) {
struct nouveau_ltcg *ltcg =
nouveau_ltcg(vma->vm->vmm->base.base.parent);
u32 tag = mem->tag->offset + (delta >> 17);
phys |= (u64)tag << (32 + 12);
next |= (u64)1 << (32 + 12);
ltcg->tags_clear(ltcg, tag, cnt);
}
while (cnt--) { while (cnt--) {
nv_wo32(pgt, pte + 0, lower_32_bits(phys)); nv_wo32(pgt, pte + 0, lower_32_bits(phys));
nv_wo32(pgt, pte + 4, upper_32_bits(phys)); nv_wo32(pgt, pte + 4, upper_32_bits(phys));
...@@ -85,10 +137,12 @@ nvc0_vm_map_sg(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt, ...@@ -85,10 +137,12 @@ nvc0_vm_map_sg(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt,
struct nouveau_mem *mem, u32 pte, u32 cnt, dma_addr_t *list) struct nouveau_mem *mem, u32 pte, u32 cnt, dma_addr_t *list)
{ {
u32 target = (vma->access & NV_MEM_ACCESS_NOSNOOP) ? 7 : 5; u32 target = (vma->access & NV_MEM_ACCESS_NOSNOOP) ? 7 : 5;
/* compressed storage types are invalid for system memory */
u32 memtype = nvc0_pte_storage_type_map[mem->memtype & 0xff];
pte <<= 3; pte <<= 3;
while (cnt--) { while (cnt--) {
u64 phys = nvc0_vm_addr(vma, *list++, mem->memtype, target); u64 phys = nvc0_vm_addr(vma, *list++, memtype, target);
nv_wo32(pgt, pte + 0, lower_32_bits(phys)); nv_wo32(pgt, pte + 0, lower_32_bits(phys));
nv_wo32(pgt, pte + 4, upper_32_bits(phys)); nv_wo32(pgt, pte + 4, upper_32_bits(phys));
pte += 8; pte += 8;
......
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