summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c')
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c252
1 files changed, 181 insertions, 71 deletions
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c
index f054331..9f59f2b 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2011 Red Hat Inc.
+ * Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -22,114 +22,224 @@
* Authors: Ben Skeggs
*/
-#include "drmP.h"
-#include "drm.h"
-#include "nouveau_drv.h"
-#include <nouveau_drm.h>
+#include <subdev/fb.h>
+#include <subdev/bios.h>
struct nvc0_fb_priv {
+ struct nouveau_fb base;
struct page *r100c10_page;
dma_addr_t r100c10;
};
-static inline void
-nvc0_mfb_subp_isr(struct drm_device *dev, int unit, int subp)
+/* 0 = unsupported
+ * 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
+nvc0_fb_memtype_valid(struct nouveau_fb *pfb, u32 tile_flags)
{
- u32 subp_base = 0x141000 + (unit * 0x2000) + (subp * 0x400);
- u32 stat = nv_rd32(dev, subp_base + 0x020);
+ u8 memtype = (tile_flags & 0x0000ff00) >> 8;
+ return likely((types[memtype] == 1));
+}
- if (stat) {
- NV_INFO(dev, "PMFB%d_SUBP%d: 0x%08x\n", unit, subp, stat);
- nv_wr32(dev, subp_base + 0x020, stat);
- }
+static int
+nvc0_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
+ u32 memtype, struct nouveau_mem **pmem)
+{
+ struct nouveau_mm *mm = &pfb->vram;
+ struct nouveau_mm_node *r;
+ struct nouveau_mem *mem;
+ int type = (memtype & 0x0ff);
+ int back = (memtype & 0x800);
+ int ret;
+
+ size >>= 12;
+ align >>= 12;
+ ncmin >>= 12;
+ if (!ncmin)
+ ncmin = size;
+
+ mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+ if (!mem)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&mem->regions);
+ mem->memtype = type;
+ mem->size = size;
+
+ mutex_lock(&mm->mutex);
+ do {
+ if (back)
+ ret = nouveau_mm_tail(mm, 1, size, ncmin, align, &r);
+ else
+ ret = nouveau_mm_head(mm, 1, size, ncmin, align, &r);
+ if (ret) {
+ mutex_unlock(&mm->mutex);
+ pfb->ram.put(pfb, &mem);
+ return ret;
+ }
+
+ list_add_tail(&r->rl_entry, &mem->regions);
+ size -= r->length;
+ } while (size);
+ mutex_unlock(&mm->mutex);
+
+ r = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry);
+ mem->offset = (u64)r->offset << 12;
+ *pmem = mem;
+ return 0;
}
-static void
-nvc0_mfb_isr(struct drm_device *dev)
+static int
+nvc0_fb_init(struct nouveau_object *object)
{
- u32 units = nv_rd32(dev, 0x00017c);
- while (units) {
- u32 subp, unit = ffs(units) - 1;
- for (subp = 0; subp < 2; subp++)
- nvc0_mfb_subp_isr(dev, unit, subp);
- units &= ~(1 << unit);
- }
+ struct nvc0_fb_priv *priv = (void *)object;
+ int ret;
+
+ ret = nouveau_fb_init(&priv->base);
+ if (ret)
+ return ret;
- /* we do something horribly wrong and upset PMFB a lot, so mask off
- * interrupts from it after the first one until it's fixed
- */
- nv_mask(dev, 0x000640, 0x02000000, 0x00000000);
+ nv_wr32(priv, 0x100c10, priv->r100c10 >> 8);
+ return 0;
}
static void
-nvc0_fb_destroy(struct drm_device *dev)
+nvc0_fb_dtor(struct nouveau_object *object)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
- struct nvc0_fb_priv *priv = pfb->priv;
-
- nouveau_irq_unregister(dev, 25);
+ struct nouveau_device *device = nv_device(object);
+ struct nvc0_fb_priv *priv = (void *)object;
if (priv->r100c10_page) {
- pci_unmap_page(dev->pdev, priv->r100c10, PAGE_SIZE,
+ pci_unmap_page(device->pdev, priv->r100c10, PAGE_SIZE,
PCI_DMA_BIDIRECTIONAL);
__free_page(priv->r100c10_page);
}
- kfree(priv);
- pfb->priv = NULL;
+ nouveau_fb_destroy(&priv->base);
}
static int
-nvc0_fb_create(struct drm_device *dev)
+nvc0_vram_detect(struct nvc0_fb_priv *priv)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
- struct nvc0_fb_priv *priv;
-
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- pfb->priv = priv;
+ struct nouveau_bios *bios = nouveau_bios(priv);
+ struct nouveau_fb *pfb = &priv->base;
+ const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
+ const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
+ u32 parts = nv_rd32(priv, 0x022438);
+ u32 pmask = nv_rd32(priv, 0x022554);
+ u32 bsize = nv_rd32(priv, 0x10f20c);
+ u32 offset, length;
+ bool uniform = true;
+ int ret, part;
+
+ nv_debug(priv, "0x100800: 0x%08x\n", nv_rd32(priv, 0x100800));
+ nv_debug(priv, "parts 0x%08x mask 0x%08x\n", parts, pmask);
+
+ priv->base.ram.type = nouveau_fb_bios_memtype(bios);
+ priv->base.ram.ranks = (nv_rd32(priv, 0x10f200) & 0x00000004) ? 2 : 1;
+
+ /* read amount of vram attached to each memory controller */
+ for (part = 0; part < parts; part++) {
+ if (!(pmask & (1 << part))) {
+ u32 psize = nv_rd32(priv, 0x11020c + (part * 0x1000));
+ if (psize != bsize) {
+ if (psize < bsize)
+ bsize = psize;
+ uniform = false;
+ }
+
+ nv_debug(priv, "%d: mem_amount 0x%08x\n", part, psize);
+ priv->base.ram.size += (u64)psize << 20;
+ }
+ }
- priv->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
- if (!priv->r100c10_page) {
- nvc0_fb_destroy(dev);
- return -ENOMEM;
+ /* if all controllers have the same amount attached, there's no holes */
+ if (uniform) {
+ offset = rsvd_head;
+ length = (priv->base.ram.size >> 12) - rsvd_head - rsvd_tail;
+ return nouveau_mm_init(&pfb->vram, offset, length, 1);
}
- priv->r100c10 = pci_map_page(dev->pdev, priv->r100c10_page, 0,
- PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
- if (pci_dma_mapping_error(dev->pdev, priv->r100c10)) {
- nvc0_fb_destroy(dev);
- return -EFAULT;
+ /* otherwise, address lowest common amount from 0GiB */
+ ret = nouveau_mm_init(&pfb->vram, rsvd_head, (bsize << 8) * parts, 1);
+ if (ret)
+ return ret;
+
+ /* and the rest starting from (8GiB + common_size) */
+ offset = (0x0200000000ULL >> 12) + (bsize << 8);
+ length = (priv->base.ram.size >> 12) - (bsize << 8) - rsvd_tail;
+
+ ret = nouveau_mm_init(&pfb->vram, offset, length, 0);
+ if (ret) {
+ nouveau_mm_fini(&pfb->vram);
+ return ret;
}
- nouveau_irq_register(dev, 25, nvc0_mfb_isr);
return 0;
}
-int
-nvc0_fb_init(struct drm_device *dev)
+static int
+nvc0_fb_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 nouveau_device *device = nv_device(parent);
struct nvc0_fb_priv *priv;
int ret;
- if (!dev_priv->engine.fb.priv) {
- ret = nvc0_fb_create(dev);
- if (ret)
- return ret;
- }
- priv = dev_priv->engine.fb.priv;
+ ret = nouveau_fb_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
- nv_wr32(dev, 0x100c10, priv->r100c10 >> 8);
- nv_mask(dev, 0x17e820, 0x00100000, 0x00000000); /* NV_PLTCG_INTR_EN */
- return 0;
-}
+ priv->base.memtype_valid = nvc0_fb_memtype_valid;
+ priv->base.ram.get = nvc0_fb_vram_new;
+ priv->base.ram.put = nv50_fb_vram_del;
-void
-nvc0_fb_takedown(struct drm_device *dev)
-{
- nvc0_fb_destroy(dev);
+ ret = nvc0_vram_detect(priv);
+ if (ret)
+ return ret;
+
+ priv->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (!priv->r100c10_page)
+ return -ENOMEM;
+
+ priv->r100c10 = pci_map_page(device->pdev, priv->r100c10_page, 0,
+ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+ if (pci_dma_mapping_error(device->pdev, priv->r100c10))
+ return -EFAULT;
+
+ return nouveau_fb_created(&priv->base);
}
+
+
+struct nouveau_oclass
+nvc0_fb_oclass = {
+ .handle = NV_SUBDEV(FB, 0xc0),
+ .ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = nvc0_fb_ctor,
+ .dtor = nvc0_fb_dtor,
+ .init = nvc0_fb_init,
+ .fini = _nouveau_fb_fini,
+ },
+};
OpenPOWER on IntegriCloud