aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2010-08-25 01:26:04 -0400
committerBen Skeggs <bskeggs@redhat.com>2010-12-07 22:48:07 -0500
commit573a2a37e8648a3249426c816f51e7ef50f6f73e (patch)
treec8d9ea9071026f469511e380417532f60733fd14 /drivers/gpu/drm
parent937c3471cc8b7ef8f9e382d9e4ec232db151ea7b (diff)
drm/nv50: implement custom vram mm
This is required on nv50 as we need to be able to have more precise control over physical VRAM allocations to avoid buffer corruption when using buffers of mixed memory types. This removes some nasty overallocation/alignment that we were previously using to "control" this problem. Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r--drivers/gpu/drm/nouveau/Makefile5
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo.c43
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h10
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mem.c201
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mm.c271
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mm.h61
-rw-r--r--drivers/gpu/drm/nouveau/nv50_instmem.c1
-rw-r--r--drivers/gpu/drm/nouveau/nv50_vram.c180
8 files changed, 650 insertions, 122 deletions
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index 7ea9a1154ca8..26fdd12561b6 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -9,7 +9,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
9 nouveau_bo.o nouveau_fence.o nouveau_gem.o nouveau_ttm.o \ 9 nouveau_bo.o nouveau_fence.o nouveau_gem.o nouveau_ttm.o \
10 nouveau_hw.o nouveau_calc.o nouveau_bios.o nouveau_i2c.o \ 10 nouveau_hw.o nouveau_calc.o nouveau_bios.o nouveau_i2c.o \
11 nouveau_display.o nouveau_connector.o nouveau_fbcon.o \ 11 nouveau_display.o nouveau_connector.o nouveau_fbcon.o \
12 nouveau_dp.o nouveau_ramht.o \ 12 nouveau_dp.o nouveau_ramht.o nouveau_mm.o \
13 nouveau_pm.o nouveau_volt.o nouveau_perf.o nouveau_temp.o \ 13 nouveau_pm.o nouveau_volt.o nouveau_perf.o nouveau_temp.o \
14 nv04_timer.o \ 14 nv04_timer.o \
15 nv04_mc.o nv40_mc.o nv50_mc.o \ 15 nv04_mc.o nv40_mc.o nv50_mc.o \
@@ -26,7 +26,8 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
26 nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \ 26 nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \
27 nv10_gpio.o nv50_gpio.o \ 27 nv10_gpio.o nv50_gpio.o \
28 nv50_calc.o \ 28 nv50_calc.o \
29 nv04_pm.o nv50_pm.o nva3_pm.o 29 nv04_pm.o nv50_pm.o nva3_pm.o \
30 nv50_vram.o
30 31
31nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o 32nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o
32nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o 33nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
index a7883e7db344..5a71ca4346c8 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
@@ -57,42 +57,7 @@ nouveau_bo_fixup_align(struct drm_device *dev,
57{ 57{
58 struct drm_nouveau_private *dev_priv = dev->dev_private; 58 struct drm_nouveau_private *dev_priv = dev->dev_private;
59 59
60 /* 60 if (dev_priv->card_type < NV_50) {
61 * Some of the tile_flags have a periodic structure of N*4096 bytes,
62 * align to to that as well as the page size. Align the size to the
63 * appropriate boundaries. This does imply that sizes are rounded up
64 * 3-7 pages, so be aware of this and do not waste memory by allocating
65 * many small buffers.
66 */
67 if (dev_priv->card_type == NV_50) {
68 uint32_t block_size = dev_priv->vram_size >> 15;
69 int i;
70
71 switch (tile_flags) {
72 case 0x1800:
73 case 0x2800:
74 case 0x4800:
75 case 0x7a00:
76 if (is_power_of_2(block_size)) {
77 for (i = 1; i < 10; i++) {
78 *align = 12 * i * block_size;
79 if (!(*align % 65536))
80 break;
81 }
82 } else {
83 for (i = 1; i < 10; i++) {
84 *align = 8 * i * block_size;
85 if (!(*align % 65536))
86 break;
87 }
88 }
89 *size = roundup(*size, *align);
90 break;
91 default:
92 break;
93 }
94
95 } else {
96 if (tile_mode) { 61 if (tile_mode) {
97 if (dev_priv->chipset >= 0x40) { 62 if (dev_priv->chipset >= 0x40) {
98 *align = 65536; 63 *align = 65536;
@@ -115,7 +80,6 @@ nouveau_bo_fixup_align(struct drm_device *dev,
115 80
116 /* ALIGN works only on powers of two. */ 81 /* ALIGN works only on powers of two. */
117 *size = roundup(*size, PAGE_SIZE); 82 *size = roundup(*size, PAGE_SIZE);
118
119 if (dev_priv->card_type == NV_50) { 83 if (dev_priv->card_type == NV_50) {
120 *size = roundup(*size, 65536); 84 *size = roundup(*size, 65536);
121 *align = max(65536, *align); 85 *align = max(65536, *align);
@@ -422,7 +386,10 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
422 man->default_caching = TTM_PL_FLAG_CACHED; 386 man->default_caching = TTM_PL_FLAG_CACHED;
423 break; 387 break;
424 case TTM_PL_VRAM: 388 case TTM_PL_VRAM:
425 man->func = &ttm_bo_manager_func; 389 if (dev_priv->card_type == NV_50)
390 man->func = &nouveau_vram_manager;
391 else
392 man->func = &ttm_bo_manager_func;
426 man->flags = TTM_MEMTYPE_FLAG_FIXED | 393 man->flags = TTM_MEMTYPE_FLAG_FIXED |
427 TTM_MEMTYPE_FLAG_MAPPABLE; 394 TTM_MEMTYPE_FLAG_MAPPABLE;
428 man->available_caching = TTM_PL_FLAG_UNCACHED | 395 man->available_caching = TTM_PL_FLAG_UNCACHED |
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 22abe8579912..1305e2c94201 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -66,6 +66,15 @@ struct nouveau_grctx;
66#define NV50_VM_BLOCK (512*1024*1024ULL) 66#define NV50_VM_BLOCK (512*1024*1024ULL)
67#define NV50_VM_VRAM_NR (NV50_VM_MAX_VRAM / NV50_VM_BLOCK) 67#define NV50_VM_VRAM_NR (NV50_VM_MAX_VRAM / NV50_VM_BLOCK)
68 68
69struct nouveau_vram {
70 struct drm_device *dev;
71
72 struct list_head regions;
73 u32 memtype;
74 u64 offset;
75 u64 size;
76};
77
69struct nouveau_tile_reg { 78struct nouveau_tile_reg {
70 bool used; 79 bool used;
71 uint32_t addr; 80 uint32_t addr;
@@ -821,6 +830,7 @@ extern int nv50_mem_vm_bind_linear(struct drm_device *, uint64_t virt,
821 uint64_t phys); 830 uint64_t phys);
822extern void nv50_mem_vm_unbind(struct drm_device *, uint64_t virt, 831extern void nv50_mem_vm_unbind(struct drm_device *, uint64_t virt,
823 uint32_t size); 832 uint32_t size);
833extern const struct ttm_mem_type_manager_func nouveau_vram_manager;
824 834
825/* nouveau_notifier.c */ 835/* nouveau_notifier.c */
826extern int nouveau_notifier_init_channel(struct nouveau_channel *); 836extern int nouveau_notifier_init_channel(struct nouveau_channel *);
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
index 549f59052881..dbeb9e5f6b22 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
@@ -36,6 +36,7 @@
36 36
37#include "nouveau_drv.h" 37#include "nouveau_drv.h"
38#include "nouveau_pm.h" 38#include "nouveau_pm.h"
39#include "nouveau_mm.h"
39 40
40/* 41/*
41 * NV10-NV40 tiling helpers 42 * NV10-NV40 tiling helpers
@@ -333,61 +334,6 @@ nouveau_mem_detect_nforce(struct drm_device *dev)
333 return 0; 334 return 0;
334} 335}
335 336
336static void
337nv50_vram_preinit(struct drm_device *dev)
338{
339 struct drm_nouveau_private *dev_priv = dev->dev_private;
340 int i, parts, colbits, rowbitsa, rowbitsb, banks;
341 u64 rowsize, predicted;
342 u32 r0, r4, rt, ru;
343
344 r0 = nv_rd32(dev, 0x100200);
345 r4 = nv_rd32(dev, 0x100204);
346 rt = nv_rd32(dev, 0x100250);
347 ru = nv_rd32(dev, 0x001540);
348 NV_DEBUG(dev, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", r0, r4, rt, ru);
349
350 for (i = 0, parts = 0; i < 8; i++) {
351 if (ru & (0x00010000 << i))
352 parts++;
353 }
354
355 colbits = (r4 & 0x0000f000) >> 12;
356 rowbitsa = ((r4 & 0x000f0000) >> 16) + 8;
357 rowbitsb = ((r4 & 0x00f00000) >> 20) + 8;
358 banks = ((r4 & 0x01000000) ? 8 : 4);
359
360 rowsize = parts * banks * (1 << colbits) * 8;
361 predicted = rowsize << rowbitsa;
362 if (r0 & 0x00000004)
363 predicted += rowsize << rowbitsb;
364
365 if (predicted != dev_priv->vram_size) {
366 NV_WARN(dev, "memory controller reports %dMiB VRAM\n",
367 (u32)(dev_priv->vram_size >> 20));
368 NV_WARN(dev, "we calculated %dMiB VRAM\n",
369 (u32)(predicted >> 20));
370 }
371
372 dev_priv->vram_rblock_size = rowsize >> 12;
373 if (rt & 1)
374 dev_priv->vram_rblock_size *= 3;
375
376 NV_DEBUG(dev, "rblock %lld bytes\n",
377 (u64)dev_priv->vram_rblock_size << 12);
378}
379
380static void
381nvaa_vram_preinit(struct drm_device *dev)
382{
383 struct drm_nouveau_private *dev_priv = dev->dev_private;
384
385 /* To our knowledge, there's no large scale reordering of pages
386 * that occurs on IGP chipsets.
387 */
388 dev_priv->vram_rblock_size = 1;
389}
390
391static int 337static int
392nouveau_mem_detect(struct drm_device *dev) 338nouveau_mem_detect(struct drm_device *dev)
393{ 339{
@@ -404,22 +350,8 @@ nouveau_mem_detect(struct drm_device *dev)
404 dev_priv->vram_size &= NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_MASK; 350 dev_priv->vram_size &= NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_MASK;
405 } else 351 } else
406 if (dev_priv->card_type < NV_C0) { 352 if (dev_priv->card_type < NV_C0) {
407 dev_priv->vram_size = nv_rd32(dev, NV04_PFB_FIFO_DATA); 353 if (nv50_vram_init(dev))
408 dev_priv->vram_size |= (dev_priv->vram_size & 0xff) << 32; 354 return -ENOMEM;
409 dev_priv->vram_size &= 0xffffffff00ll;
410
411 switch (dev_priv->chipset) {
412 case 0xaa:
413 case 0xac:
414 case 0xaf:
415 dev_priv->vram_sys_base = nv_rd32(dev, 0x100e10);
416 dev_priv->vram_sys_base <<= 12;
417 nvaa_vram_preinit(dev);
418 break;
419 default:
420 nv50_vram_preinit(dev);
421 break;
422 }
423 } else { 355 } else {
424 dev_priv->vram_size = nv_rd32(dev, 0x10f20c) << 20; 356 dev_priv->vram_size = nv_rd32(dev, 0x10f20c) << 20;
425 dev_priv->vram_size *= nv_rd32(dev, 0x121c74); 357 dev_priv->vram_size *= nv_rd32(dev, 0x121c74);
@@ -568,10 +500,6 @@ nouveau_mem_vram_init(struct drm_device *dev)
568 if (ret) 500 if (ret)
569 return ret; 501 return ret;
570 502
571 ret = nouveau_mem_detect(dev);
572 if (ret)
573 return ret;
574
575 dev_priv->fb_phys = pci_resource_start(dev->pdev, 1); 503 dev_priv->fb_phys = pci_resource_start(dev->pdev, 1);
576 504
577 ret = nouveau_ttm_global_init(dev_priv); 505 ret = nouveau_ttm_global_init(dev_priv);
@@ -587,13 +515,6 @@ nouveau_mem_vram_init(struct drm_device *dev)
587 return ret; 515 return ret;
588 } 516 }
589 517
590 dev_priv->fb_available_size = dev_priv->vram_size;
591 dev_priv->fb_mappable_pages = dev_priv->fb_available_size;
592 if (dev_priv->fb_mappable_pages > pci_resource_len(dev->pdev, 1))
593 dev_priv->fb_mappable_pages =
594 pci_resource_len(dev->pdev, 1);
595 dev_priv->fb_mappable_pages >>= PAGE_SHIFT;
596
597 /* reserve space at end of VRAM for PRAMIN */ 518 /* reserve space at end of VRAM for PRAMIN */
598 if (dev_priv->chipset == 0x40 || dev_priv->chipset == 0x47 || 519 if (dev_priv->chipset == 0x40 || dev_priv->chipset == 0x47 ||
599 dev_priv->chipset == 0x49 || dev_priv->chipset == 0x4b) 520 dev_priv->chipset == 0x49 || dev_priv->chipset == 0x4b)
@@ -604,6 +525,17 @@ nouveau_mem_vram_init(struct drm_device *dev)
604 else 525 else
605 dev_priv->ramin_rsvd_vram = (512 * 1024); 526 dev_priv->ramin_rsvd_vram = (512 * 1024);
606 527
528 /* initialise gpu-specific vram backend */
529 ret = nouveau_mem_detect(dev);
530 if (ret)
531 return ret;
532
533 dev_priv->fb_available_size = dev_priv->vram_size;
534 dev_priv->fb_mappable_pages = dev_priv->fb_available_size;
535 if (dev_priv->fb_mappable_pages > pci_resource_len(dev->pdev, 1))
536 dev_priv->fb_mappable_pages = pci_resource_len(dev->pdev, 1);
537 dev_priv->fb_mappable_pages >>= PAGE_SHIFT;
538
607 dev_priv->fb_available_size -= dev_priv->ramin_rsvd_vram; 539 dev_priv->fb_available_size -= dev_priv->ramin_rsvd_vram;
608 dev_priv->fb_aper_free = dev_priv->fb_available_size; 540 dev_priv->fb_aper_free = dev_priv->fb_available_size;
609 541
@@ -820,3 +752,108 @@ nouveau_mem_timing_fini(struct drm_device *dev)
820 752
821 kfree(mem->timing); 753 kfree(mem->timing);
822} 754}
755
756static int
757nouveau_vram_manager_init(struct ttm_mem_type_manager *man, unsigned long p_size)
758{
759 struct drm_nouveau_private *dev_priv = nouveau_bdev(man->bdev);
760 struct nouveau_mm *mm;
761 u32 b_size;
762 int ret;
763
764 p_size = (p_size << PAGE_SHIFT) >> 12;
765 b_size = dev_priv->vram_rblock_size >> 12;
766
767 ret = nouveau_mm_init(&mm, 0, p_size, b_size);
768 if (ret)
769 return ret;
770
771 man->priv = mm;
772 return 0;
773}
774
775static int
776nouveau_vram_manager_fini(struct ttm_mem_type_manager *man)
777{
778 struct nouveau_mm *mm = man->priv;
779 int ret;
780
781 ret = nouveau_mm_fini(&mm);
782 if (ret)
783 return ret;
784
785 man->priv = NULL;
786 return 0;
787}
788
789static void
790nouveau_vram_manager_del(struct ttm_mem_type_manager *man,
791 struct ttm_mem_reg *mem)
792{
793 struct drm_nouveau_private *dev_priv = nouveau_bdev(man->bdev);
794 struct drm_device *dev = dev_priv->dev;
795
796 nv50_vram_del(dev, (struct nouveau_vram **)&mem->mm_node);
797}
798
799static int
800nouveau_vram_manager_new(struct ttm_mem_type_manager *man,
801 struct ttm_buffer_object *bo,
802 struct ttm_placement *placement,
803 struct ttm_mem_reg *mem)
804{
805 struct drm_nouveau_private *dev_priv = nouveau_bdev(man->bdev);
806 struct drm_device *dev = dev_priv->dev;
807 struct nouveau_bo *nvbo = nouveau_bo(bo);
808 struct nouveau_vram *vram;
809 int ret;
810
811 ret = nv50_vram_new(dev, mem->num_pages << PAGE_SHIFT, 65536, 0,
812 (nvbo->tile_flags >> 8) & 0x7f, &vram);
813 if (ret)
814 return ret;
815
816 mem->mm_node = vram;
817 mem->start = vram->offset >> PAGE_SHIFT;
818 return 0;
819}
820
821void
822nouveau_vram_manager_debug(struct ttm_mem_type_manager *man, const char *prefix)
823{
824 struct ttm_bo_global *glob = man->bdev->glob;
825 struct nouveau_mm *mm = man->priv;
826 struct nouveau_mm_node *r;
827 u64 total = 0, ttotal[3] = {}, tused[3] = {}, tfree[3] = {};
828 int i;
829
830 mutex_lock(&mm->mutex);
831 list_for_each_entry(r, &mm->nodes, nl_entry) {
832 printk(KERN_DEBUG "%s %s-%d: 0x%010llx 0x%010llx\n",
833 prefix, r->free ? "free" : "used", r->type,
834 ((u64)r->offset << 12),
835 (((u64)r->offset + r->length) << 12));
836 total += r->length;
837 ttotal[r->type] += r->length;
838 if (r->free)
839 tfree[r->type] += r->length;
840 else
841 tused[r->type] += r->length;
842 }
843 mutex_unlock(&mm->mutex);
844
845 printk(KERN_DEBUG "%s total: 0x%010llx\n", prefix, total << 12);
846 for (i = 0; i < 3; i++) {
847 printk(KERN_DEBUG "%s type %d: 0x%010llx, "
848 "used 0x%010llx, free 0x%010llx\n", prefix,
849 i, ttotal[i] << 12, tused[i] << 12, tfree[i] << 12);
850 }
851}
852
853const struct ttm_mem_type_manager_func nouveau_vram_manager = {
854 nouveau_vram_manager_init,
855 nouveau_vram_manager_fini,
856 nouveau_vram_manager_new,
857 nouveau_vram_manager_del,
858 nouveau_vram_manager_debug
859};
diff --git a/drivers/gpu/drm/nouveau/nouveau_mm.c b/drivers/gpu/drm/nouveau/nouveau_mm.c
new file mode 100644
index 000000000000..cdbb11eb701b
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_mm.c
@@ -0,0 +1,271 @@
1/*
2 * Copyright 2010 Red Hat Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Authors: Ben Skeggs
23 */
24
25#include "drmP.h"
26#include "nouveau_drv.h"
27#include "nouveau_mm.h"
28
29static inline void
30region_put(struct nouveau_mm *rmm, struct nouveau_mm_node *a)
31{
32 list_del(&a->nl_entry);
33 list_del(&a->fl_entry);
34 kfree(a);
35}
36
37static struct nouveau_mm_node *
38region_split(struct nouveau_mm *rmm, struct nouveau_mm_node *a, u32 size)
39{
40 struct nouveau_mm_node *b;
41
42 if (a->length == size)
43 return a;
44
45 b = kmalloc(sizeof(*b), GFP_KERNEL);
46 if (unlikely(b == NULL))
47 return NULL;
48
49 b->offset = a->offset;
50 b->length = size;
51 b->free = a->free;
52 b->type = a->type;
53 a->offset += size;
54 a->length -= size;
55 list_add_tail(&b->nl_entry, &a->nl_entry);
56 if (b->free)
57 list_add_tail(&b->fl_entry, &a->fl_entry);
58 return b;
59}
60
61static struct nouveau_mm_node *
62nouveau_mm_merge(struct nouveau_mm *rmm, struct nouveau_mm_node *this)
63{
64 struct nouveau_mm_node *prev, *next;
65
66 /* try to merge with free adjacent entries of same type */
67 prev = list_entry(this->nl_entry.prev, struct nouveau_mm_node, nl_entry);
68 if (this->nl_entry.prev != &rmm->nodes) {
69 if (prev->free && prev->type == this->type) {
70 prev->length += this->length;
71 region_put(rmm, this);
72 this = prev;
73 }
74 }
75
76 next = list_entry(this->nl_entry.next, struct nouveau_mm_node, nl_entry);
77 if (this->nl_entry.next != &rmm->nodes) {
78 if (next->free && next->type == this->type) {
79 next->offset = this->offset;
80 next->length += this->length;
81 region_put(rmm, this);
82 this = next;
83 }
84 }
85
86 return this;
87}
88
89void
90nouveau_mm_put(struct nouveau_mm *rmm, struct nouveau_mm_node *this)
91{
92 u32 block_s, block_l;
93
94 this->free = true;
95 list_add(&this->fl_entry, &rmm->free);
96 this = nouveau_mm_merge(rmm, this);
97
98 /* any entirely free blocks now? we'll want to remove typing
99 * on them now so they can be use for any memory allocation
100 */
101 block_s = roundup(this->offset, rmm->block_size);
102 if (block_s + rmm->block_size > this->offset + this->length)
103 return;
104
105 /* split off any still-typed region at the start */
106 if (block_s != this->offset) {
107 if (!region_split(rmm, this, block_s - this->offset))
108 return;
109 }
110
111 /* split off the soon-to-be-untyped block(s) */
112 block_l = rounddown(this->length, rmm->block_size);
113 if (block_l != this->length) {
114 this = region_split(rmm, this, block_l);
115 if (!this)
116 return;
117 }
118
119 /* mark as having no type, and retry merge with any adjacent
120 * untyped blocks
121 */
122 this->type = 0;
123 nouveau_mm_merge(rmm, this);
124}
125
126int
127nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc,
128 u32 align, struct nouveau_mm_node **pnode)
129{
130 struct nouveau_mm_node *this, *tmp, *next;
131 u32 splitoff, avail, alloc;
132
133 list_for_each_entry_safe(this, tmp, &rmm->free, fl_entry) {
134 next = list_entry(this->nl_entry.next, struct nouveau_mm_node, nl_entry);
135 if (this->nl_entry.next == &rmm->nodes)
136 next = NULL;
137
138 /* skip wrongly typed blocks */
139 if (this->type && this->type != type)
140 continue;
141
142 /* account for alignment */
143 splitoff = this->offset & (align - 1);
144 if (splitoff)
145 splitoff = align - splitoff;
146
147 if (this->length <= splitoff)
148 continue;
149
150 /* determine total memory available from this, and
151 * the next block (if appropriate)
152 */
153 avail = this->length;
154 if (next && next->free && (!next->type || next->type == type))
155 avail += next->length;
156
157 avail -= splitoff;
158
159 /* determine allocation size */
160 if (size_nc) {
161 alloc = min(avail, size);
162 alloc = rounddown(alloc, size_nc);
163 if (alloc == 0)
164 continue;
165 } else {
166 alloc = size;
167 if (avail < alloc)
168 continue;
169 }
170
171 /* untyped block, split off a chunk that's a multiple
172 * of block_size and type it
173 */
174 if (!this->type) {
175 u32 block = roundup(alloc + splitoff, rmm->block_size);
176 if (this->length < block)
177 continue;
178
179 this = region_split(rmm, this, block);
180 if (!this)
181 return -ENOMEM;
182
183 this->type = type;
184 }
185
186 /* stealing memory from adjacent block */
187 if (alloc > this->length) {
188 u32 amount = alloc - (this->length - splitoff);
189
190 if (!next->type) {
191 amount = roundup(amount, rmm->block_size);
192
193 next = region_split(rmm, next, amount);
194 if (!next)
195 return -ENOMEM;
196
197 next->type = type;
198 }
199
200 this->length += amount;
201 next->offset += amount;
202 next->length -= amount;
203 if (!next->length) {
204 list_del(&next->nl_entry);
205 list_del(&next->fl_entry);
206 kfree(next);
207 }
208 }
209
210 if (splitoff) {
211 if (!region_split(rmm, this, splitoff))
212 return -ENOMEM;
213 }
214
215 this = region_split(rmm, this, alloc);
216 if (this == NULL)
217 return -ENOMEM;
218
219 this->free = false;
220 list_del(&this->fl_entry);
221 *pnode = this;
222 return 0;
223 }
224
225 return -ENOMEM;
226}
227
228int
229nouveau_mm_init(struct nouveau_mm **prmm, u32 offset, u32 length, u32 block)
230{
231 struct nouveau_mm *rmm;
232 struct nouveau_mm_node *heap;
233
234 heap = kzalloc(sizeof(*heap), GFP_KERNEL);
235 if (!heap)
236 return -ENOMEM;
237 heap->free = true;
238 heap->offset = roundup(offset, block);
239 heap->length = rounddown(offset + length, block) - heap->offset;
240
241 rmm = kzalloc(sizeof(*rmm), GFP_KERNEL);
242 if (!rmm) {
243 kfree(heap);
244 return -ENOMEM;
245 }
246 rmm->block_size = block;
247 mutex_init(&rmm->mutex);
248 INIT_LIST_HEAD(&rmm->nodes);
249 INIT_LIST_HEAD(&rmm->free);
250 list_add(&heap->nl_entry, &rmm->nodes);
251 list_add(&heap->fl_entry, &rmm->free);
252
253 *prmm = rmm;
254 return 0;
255}
256
257int
258nouveau_mm_fini(struct nouveau_mm **prmm)
259{
260 struct nouveau_mm *rmm = *prmm;
261 struct nouveau_mm_node *heap =
262 list_first_entry(&rmm->nodes, struct nouveau_mm_node, nl_entry);
263
264 if (!list_is_singular(&rmm->nodes))
265 return -EBUSY;
266
267 kfree(heap);
268 kfree(rmm);
269 *prmm = NULL;
270 return 0;
271}
diff --git a/drivers/gpu/drm/nouveau/nouveau_mm.h b/drivers/gpu/drm/nouveau/nouveau_mm.h
new file mode 100644
index 000000000000..7e8f8bd86d47
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_mm.h
@@ -0,0 +1,61 @@
1/*
2 * Copyright 2010 Red Hat Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Authors: Ben Skeggs
23 */
24
25#ifndef __NOUVEAU_REGION_H__
26#define __NOUVEAU_REGION_H__
27
28struct nouveau_mm_node {
29 struct list_head nl_entry;
30 struct list_head fl_entry;
31 struct list_head rl_entry;
32
33 bool free;
34 int type;
35
36 u32 offset;
37 u32 length;
38};
39
40struct nouveau_mm {
41 struct list_head nodes;
42 struct list_head free;
43
44 struct mutex mutex;
45
46 u32 block_size;
47};
48
49int nouveau_mm_init(struct nouveau_mm **, u32 offset, u32 length, u32 block);
50int nouveau_mm_fini(struct nouveau_mm **);
51int nouveau_mm_pre(struct nouveau_mm *);
52int nouveau_mm_get(struct nouveau_mm *, int type, u32 size, u32 size_nc,
53 u32 align, struct nouveau_mm_node **);
54void nouveau_mm_put(struct nouveau_mm *, struct nouveau_mm_node *);
55
56int nv50_vram_init(struct drm_device *);
57int nv50_vram_new(struct drm_device *, u64 size, u32 align, u32 size_nc,
58 u32 memtype, struct nouveau_vram **);
59void nv50_vram_del(struct drm_device *, struct nouveau_vram **);
60
61#endif
diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c
index 87160952a30b..1e7d50397e4a 100644
--- a/drivers/gpu/drm/nouveau/nv50_instmem.c
+++ b/drivers/gpu/drm/nouveau/nv50_instmem.c
@@ -325,6 +325,7 @@ nv50_instmem_get(struct nouveau_gpuobj *gpuobj, u32 size, u32 align)
325 0, 0x0000, true, false, &node->vram); 325 0, 0x0000, true, false, &node->vram);
326 if (ret) { 326 if (ret) {
327 NV_ERROR(dev, "error getting PRAMIN backing pages: %d\n", ret); 327 NV_ERROR(dev, "error getting PRAMIN backing pages: %d\n", ret);
328 WARN_ON(1);
328 return ret; 329 return ret;
329 } 330 }
330 331
diff --git a/drivers/gpu/drm/nouveau/nv50_vram.c b/drivers/gpu/drm/nouveau/nv50_vram.c
new file mode 100644
index 000000000000..6e753356cd94
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv50_vram.c
@@ -0,0 +1,180 @@
1/*
2 * Copyright 2010 Red Hat Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Authors: Ben Skeggs
23 */
24
25#include "drmP.h"
26#include "nouveau_drv.h"
27#include "nouveau_mm.h"
28
29static int types[0x80] = {
30 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
31 1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0,
32 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 0,
33 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
34 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 0, 0,
35 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
36 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 2, 2, 2, 2,
37 1, 0, 2, 0, 1, 0, 2, 0, 1, 1, 2, 2, 1, 1, 0, 0
38};
39
40void
41nv50_vram_del(struct drm_device *dev, struct nouveau_vram **pvram)
42{
43 struct drm_nouveau_private *dev_priv = dev->dev_private;
44 struct ttm_bo_device *bdev = &dev_priv->ttm.bdev;
45 struct ttm_mem_type_manager *man = &bdev->man[TTM_PL_VRAM];
46 struct nouveau_mm *mm = man->priv;
47 struct nouveau_mm_node *this;
48 struct nouveau_vram *vram;
49
50 vram = *pvram;
51 *pvram = NULL;
52 if (unlikely(vram == NULL))
53 return;
54
55 mutex_lock(&mm->mutex);
56 while (!list_empty(&vram->regions)) {
57 this = list_first_entry(&vram->regions, struct nouveau_mm_node, rl_entry);
58
59 list_del(&this->rl_entry);
60 nouveau_mm_put(mm, this);
61 }
62 mutex_unlock(&mm->mutex);
63
64 kfree(vram);
65}
66
67int
68nv50_vram_new(struct drm_device *dev, u64 size, u32 align, u32 size_nc,
69 u32 type, struct nouveau_vram **pvram)
70{
71 struct drm_nouveau_private *dev_priv = dev->dev_private;
72 struct ttm_bo_device *bdev = &dev_priv->ttm.bdev;
73 struct ttm_mem_type_manager *man = &bdev->man[TTM_PL_VRAM];
74 struct nouveau_mm *mm = man->priv;
75 struct nouveau_mm_node *r;
76 struct nouveau_vram *vram;
77 int ret;
78
79 if (!types[type])
80 return -EINVAL;
81 size >>= 12;
82 align >>= 12;
83 size_nc >>= 12;
84
85 vram = kzalloc(sizeof(*vram), GFP_KERNEL);
86 if (!vram)
87 return -ENOMEM;
88
89 INIT_LIST_HEAD(&vram->regions);
90 vram->dev = dev_priv->dev;
91 vram->memtype = type;
92 vram->size = size;
93
94 mutex_lock(&mm->mutex);
95 do {
96 ret = nouveau_mm_get(mm, types[type], size, size_nc, align, &r);
97 if (ret) {
98 mutex_unlock(&mm->mutex);
99 nv50_vram_del(dev, &vram);
100 return ret;
101 }
102
103 list_add_tail(&r->rl_entry, &vram->regions);
104 size -= r->length;
105 } while (size);
106 mutex_unlock(&mm->mutex);
107
108 r = list_first_entry(&vram->regions, struct nouveau_mm_node, rl_entry);
109 vram->offset = (u64)r->offset << 12;
110 *pvram = vram;
111 return 0;
112}
113
114static u32
115nv50_vram_rblock(struct drm_device *dev)
116{
117 struct drm_nouveau_private *dev_priv = dev->dev_private;
118 int i, parts, colbits, rowbitsa, rowbitsb, banks;
119 u64 rowsize, predicted;
120 u32 r0, r4, rt, ru, rblock_size;
121
122 r0 = nv_rd32(dev, 0x100200);
123 r4 = nv_rd32(dev, 0x100204);
124 rt = nv_rd32(dev, 0x100250);
125 ru = nv_rd32(dev, 0x001540);
126 NV_DEBUG(dev, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", r0, r4, rt, ru);
127
128 for (i = 0, parts = 0; i < 8; i++) {
129 if (ru & (0x00010000 << i))
130 parts++;
131 }
132
133 colbits = (r4 & 0x0000f000) >> 12;
134 rowbitsa = ((r4 & 0x000f0000) >> 16) + 8;
135 rowbitsb = ((r4 & 0x00f00000) >> 20) + 8;
136 banks = ((r4 & 0x01000000) ? 8 : 4);
137
138 rowsize = parts * banks * (1 << colbits) * 8;
139 predicted = rowsize << rowbitsa;
140 if (r0 & 0x00000004)
141 predicted += rowsize << rowbitsb;
142
143 if (predicted != dev_priv->vram_size) {
144 NV_WARN(dev, "memory controller reports %dMiB VRAM\n",
145 (u32)(dev_priv->vram_size >> 20));
146 NV_WARN(dev, "we calculated %dMiB VRAM\n",
147 (u32)(predicted >> 20));
148 }
149
150 rblock_size = rowsize;
151 if (rt & 1)
152 rblock_size *= 3;
153
154 NV_DEBUG(dev, "rblock %d bytes\n", rblock_size);
155 return rblock_size;
156}
157
158int
159nv50_vram_init(struct drm_device *dev)
160{
161 struct drm_nouveau_private *dev_priv = dev->dev_private;
162
163 dev_priv->vram_size = nv_rd32(dev, 0x10020c);
164 dev_priv->vram_size |= (dev_priv->vram_size & 0xff) << 32;
165 dev_priv->vram_size &= 0xffffffff00ULL;
166
167 switch (dev_priv->chipset) {
168 case 0xaa:
169 case 0xac:
170 case 0xaf:
171 dev_priv->vram_sys_base = (u64)nv_rd32(dev, 0x100e10) << 12;
172 dev_priv->vram_rblock_size = 4096;
173 break;
174 default:
175 dev_priv->vram_rblock_size = nv50_vram_rblock(dev);
176 break;
177 }
178
179 return 0;
180}