2006-08-26 16:55:02 -06:00
|
|
|
/*
|
|
|
|
* Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved.
|
|
|
|
* Copyright 2005 Stephane Marchesin
|
|
|
|
*
|
|
|
|
* The Weather Channel (TM) funded Tungsten Graphics to develop the
|
|
|
|
* initial release of the Radeon 8500 driver under the XFree86 license.
|
|
|
|
* This notice must be preserved.
|
|
|
|
*
|
|
|
|
* 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 (including the next
|
|
|
|
* paragraph) 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 AUTHORS AND/OR THEIR SUPPLIERS 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:
|
|
|
|
* Keith Whitwell <keith@tungstengraphics.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "drmP.h"
|
|
|
|
#include "drm.h"
|
|
|
|
#include "drm_sarea.h"
|
|
|
|
#include "nouveau_drv.h"
|
2008-06-25 07:16:38 -06:00
|
|
|
|
2008-06-22 09:00:42 -06:00
|
|
|
static struct mem_block *
|
|
|
|
split_block(struct mem_block *p, uint64_t start, uint64_t size,
|
|
|
|
struct drm_file *file_priv)
|
2006-08-26 16:55:02 -06:00
|
|
|
{
|
|
|
|
/* Maybe cut off the start of an existing block */
|
|
|
|
if (start > p->start) {
|
|
|
|
struct mem_block *newblock =
|
|
|
|
drm_alloc(sizeof(*newblock), DRM_MEM_BUFS);
|
|
|
|
if (!newblock)
|
|
|
|
goto out;
|
|
|
|
newblock->start = start;
|
|
|
|
newblock->size = p->size - (start - p->start);
|
2007-07-20 07:39:25 -06:00
|
|
|
newblock->file_priv = NULL;
|
2006-08-26 16:55:02 -06:00
|
|
|
newblock->next = p->next;
|
|
|
|
newblock->prev = p;
|
|
|
|
p->next->prev = newblock;
|
|
|
|
p->next = newblock;
|
|
|
|
p->size -= newblock->size;
|
|
|
|
p = newblock;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Maybe cut off the end of an existing block */
|
|
|
|
if (size < p->size) {
|
|
|
|
struct mem_block *newblock =
|
|
|
|
drm_alloc(sizeof(*newblock), DRM_MEM_BUFS);
|
|
|
|
if (!newblock)
|
|
|
|
goto out;
|
|
|
|
newblock->start = start + size;
|
|
|
|
newblock->size = p->size - size;
|
2007-07-20 07:39:25 -06:00
|
|
|
newblock->file_priv = NULL;
|
2006-08-26 16:55:02 -06:00
|
|
|
newblock->next = p->next;
|
|
|
|
newblock->prev = p;
|
|
|
|
p->next->prev = newblock;
|
|
|
|
p->next = newblock;
|
|
|
|
p->size = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
/* Our block is in the middle */
|
2007-07-20 07:39:25 -06:00
|
|
|
p->file_priv = file_priv;
|
2006-08-26 16:55:02 -06:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2008-06-22 09:00:42 -06:00
|
|
|
struct mem_block *
|
|
|
|
nouveau_mem_alloc_block(struct mem_block *heap, uint64_t size,
|
|
|
|
int align2, struct drm_file *file_priv, int tail)
|
2006-08-26 16:55:02 -06:00
|
|
|
{
|
|
|
|
struct mem_block *p;
|
|
|
|
uint64_t mask = (1 << align2) - 1;
|
|
|
|
|
|
|
|
if (!heap)
|
|
|
|
return NULL;
|
|
|
|
|
2008-06-22 09:00:42 -06:00
|
|
|
if (tail) {
|
|
|
|
list_for_each_prev(p, heap) {
|
|
|
|
uint64_t start = ((p->start + p->size) - size) & ~mask;
|
|
|
|
|
|
|
|
if (p->file_priv == 0 && start >= p->start &&
|
|
|
|
start + size <= p->start + p->size)
|
|
|
|
return split_block(p, start, size, file_priv);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
list_for_each(p, heap) {
|
|
|
|
uint64_t start = (p->start + mask) & ~mask;
|
|
|
|
|
|
|
|
if (p->file_priv == 0 &&
|
|
|
|
start + size <= p->start + p->size)
|
|
|
|
return split_block(p, start, size, file_priv);
|
|
|
|
}
|
2006-08-26 16:55:02 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct mem_block *find_block(struct mem_block *heap, uint64_t start)
|
|
|
|
{
|
|
|
|
struct mem_block *p;
|
|
|
|
|
|
|
|
list_for_each(p, heap)
|
|
|
|
if (p->start == start)
|
|
|
|
return p;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2007-06-24 03:03:35 -06:00
|
|
|
void nouveau_mem_free_block(struct mem_block *p)
|
2006-08-26 16:55:02 -06:00
|
|
|
{
|
2007-07-20 07:39:25 -06:00
|
|
|
p->file_priv = NULL;
|
2006-08-26 16:55:02 -06:00
|
|
|
|
2007-07-20 07:39:25 -06:00
|
|
|
/* Assumes a single contiguous range. Needs a special file_priv in
|
2006-08-26 16:55:02 -06:00
|
|
|
* 'heap' to stop it being subsumed.
|
|
|
|
*/
|
2007-07-20 07:39:25 -06:00
|
|
|
if (p->next->file_priv == 0) {
|
2006-08-26 16:55:02 -06:00
|
|
|
struct mem_block *q = p->next;
|
|
|
|
p->size += q->size;
|
|
|
|
p->next = q->next;
|
|
|
|
p->next->prev = p;
|
|
|
|
drm_free(q, sizeof(*q), DRM_MEM_BUFS);
|
|
|
|
}
|
|
|
|
|
2007-07-20 07:39:25 -06:00
|
|
|
if (p->prev->file_priv == 0) {
|
2006-08-26 16:55:02 -06:00
|
|
|
struct mem_block *q = p->prev;
|
|
|
|
q->size += p->size;
|
|
|
|
q->next = p->next;
|
|
|
|
q->next->prev = q;
|
|
|
|
drm_free(p, sizeof(*q), DRM_MEM_BUFS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize. How to check for an uninitialized heap?
|
|
|
|
*/
|
2007-06-24 03:03:35 -06:00
|
|
|
int nouveau_mem_init_heap(struct mem_block **heap, uint64_t start,
|
|
|
|
uint64_t size)
|
2006-08-26 16:55:02 -06:00
|
|
|
{
|
|
|
|
struct mem_block *blocks = drm_alloc(sizeof(*blocks), DRM_MEM_BUFS);
|
|
|
|
|
|
|
|
if (!blocks)
|
2007-07-19 18:00:17 -06:00
|
|
|
return -ENOMEM;
|
2006-08-26 16:55:02 -06:00
|
|
|
|
|
|
|
*heap = drm_alloc(sizeof(**heap), DRM_MEM_BUFS);
|
|
|
|
if (!*heap) {
|
|
|
|
drm_free(blocks, sizeof(*blocks), DRM_MEM_BUFS);
|
2007-07-19 18:00:17 -06:00
|
|
|
return -ENOMEM;
|
2006-08-26 16:55:02 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
blocks->start = start;
|
|
|
|
blocks->size = size;
|
2007-07-20 07:39:25 -06:00
|
|
|
blocks->file_priv = NULL;
|
2006-08-26 16:55:02 -06:00
|
|
|
blocks->next = blocks->prev = *heap;
|
|
|
|
|
|
|
|
memset(*heap, 0, sizeof(**heap));
|
2007-07-20 07:39:25 -06:00
|
|
|
(*heap)->file_priv = (struct drm_file *) - 1;
|
2006-08-26 16:55:02 -06:00
|
|
|
(*heap)->next = (*heap)->prev = blocks;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-11-04 19:42:22 -07:00
|
|
|
/*
|
2007-07-20 07:39:25 -06:00
|
|
|
* Free all blocks associated with the releasing file_priv
|
2006-08-26 16:55:02 -06:00
|
|
|
*/
|
2007-07-20 07:39:25 -06:00
|
|
|
void nouveau_mem_release(struct drm_file *file_priv, struct mem_block *heap)
|
2006-08-26 16:55:02 -06:00
|
|
|
{
|
|
|
|
struct mem_block *p;
|
|
|
|
|
|
|
|
if (!heap || !heap->next)
|
|
|
|
return;
|
|
|
|
|
|
|
|
list_for_each(p, heap) {
|
2007-07-20 07:39:25 -06:00
|
|
|
if (p->file_priv == file_priv)
|
|
|
|
p->file_priv = NULL;
|
2006-08-26 16:55:02 -06:00
|
|
|
}
|
|
|
|
|
2007-07-20 07:39:25 -06:00
|
|
|
/* Assumes a single contiguous range. Needs a special file_priv in
|
2006-08-26 16:55:02 -06:00
|
|
|
* 'heap' to stop it being subsumed.
|
|
|
|
*/
|
|
|
|
list_for_each(p, heap) {
|
2007-07-20 07:39:25 -06:00
|
|
|
while ((p->file_priv == 0) && (p->next->file_priv == 0) &&
|
|
|
|
(p->next!=heap)) {
|
2006-08-26 16:55:02 -06:00
|
|
|
struct mem_block *q = p->next;
|
|
|
|
p->size += q->size;
|
|
|
|
p->next = q->next;
|
|
|
|
p->next->prev = p;
|
|
|
|
drm_free(q, sizeof(*q), DRM_MEM_DRIVER);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-04 19:42:22 -07:00
|
|
|
/*
|
2006-08-26 16:55:02 -06:00
|
|
|
* Cleanup everything
|
|
|
|
*/
|
2007-07-02 03:31:18 -06:00
|
|
|
void nouveau_mem_takedown(struct mem_block **heap)
|
2006-08-26 16:55:02 -06:00
|
|
|
{
|
|
|
|
struct mem_block *p;
|
|
|
|
|
|
|
|
if (!*heap)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (p = (*heap)->next; p != *heap;) {
|
|
|
|
struct mem_block *q = p;
|
|
|
|
p = p->next;
|
|
|
|
drm_free(q, sizeof(*q), DRM_MEM_DRIVER);
|
|
|
|
}
|
|
|
|
|
|
|
|
drm_free(*heap, sizeof(**heap), DRM_MEM_DRIVER);
|
|
|
|
*heap = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nouveau_mem_close(struct drm_device *dev)
|
|
|
|
{
|
2007-07-12 23:09:31 -06:00
|
|
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
2007-07-15 01:18:15 -06:00
|
|
|
|
2006-08-26 16:55:02 -06:00
|
|
|
nouveau_mem_takedown(&dev_priv->agp_heap);
|
|
|
|
nouveau_mem_takedown(&dev_priv->fb_heap);
|
2007-07-15 01:18:15 -06:00
|
|
|
if (dev_priv->pci_heap)
|
2007-07-10 18:35:10 -06:00
|
|
|
nouveau_mem_takedown(&dev_priv->pci_heap);
|
2006-08-26 16:55:02 -06:00
|
|
|
}
|
|
|
|
|
2007-08-16 09:12:46 -06:00
|
|
|
/*XXX won't work on BSD because of pci_read_config_dword */
|
|
|
|
static uint32_t
|
|
|
|
nouveau_mem_fb_amount_igp(struct drm_device *dev)
|
|
|
|
{
|
2009-03-02 14:14:45 -07:00
|
|
|
#if defined(__linux__)
|
2007-08-16 09:12:46 -06:00
|
|
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
|
|
struct pci_dev *bridge;
|
|
|
|
uint32_t mem;
|
|
|
|
|
|
|
|
bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0,1));
|
|
|
|
if (!bridge) {
|
|
|
|
DRM_ERROR("no bridge device\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dev_priv->flags&NV_NFORCE) {
|
|
|
|
pci_read_config_dword(bridge, 0x7C, &mem);
|
|
|
|
return (uint64_t)(((mem >> 6) & 31) + 1)*1024*1024;
|
|
|
|
} else
|
|
|
|
if(dev_priv->flags&NV_NFORCE2) {
|
|
|
|
pci_read_config_dword(bridge, 0x84, &mem);
|
|
|
|
return (uint64_t)(((mem >> 4) & 127) + 1)*1024*1024;
|
|
|
|
}
|
|
|
|
|
|
|
|
DRM_ERROR("impossible!\n");
|
2007-08-19 10:41:18 -06:00
|
|
|
#else
|
|
|
|
DRM_ERROR("Linux kernel >= 2.6.19 required to check for igp memory amount\n");
|
|
|
|
#endif
|
|
|
|
|
2007-08-16 09:12:46 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-08-26 16:55:02 -06:00
|
|
|
/* returns the amount of FB ram in bytes */
|
|
|
|
uint64_t nouveau_mem_fb_amount(struct drm_device *dev)
|
|
|
|
{
|
2007-07-12 23:09:31 -06:00
|
|
|
struct drm_nouveau_private *dev_priv=dev->dev_private;
|
2006-08-26 16:55:02 -06:00
|
|
|
switch(dev_priv->card_type)
|
|
|
|
{
|
|
|
|
case NV_04:
|
|
|
|
case NV_05:
|
2006-11-09 18:18:38 -07:00
|
|
|
if (NV_READ(NV03_BOOT_0) & 0x00000100) {
|
|
|
|
return (((NV_READ(NV03_BOOT_0) >> 12) & 0xf)*2+2)*1024*1024;
|
|
|
|
} else
|
2006-08-26 16:55:02 -06:00
|
|
|
switch(NV_READ(NV03_BOOT_0)&NV03_BOOT_0_RAM_AMOUNT)
|
|
|
|
{
|
|
|
|
case NV04_BOOT_0_RAM_AMOUNT_32MB:
|
|
|
|
return 32*1024*1024;
|
|
|
|
case NV04_BOOT_0_RAM_AMOUNT_16MB:
|
|
|
|
return 16*1024*1024;
|
|
|
|
case NV04_BOOT_0_RAM_AMOUNT_8MB:
|
|
|
|
return 8*1024*1024;
|
|
|
|
case NV04_BOOT_0_RAM_AMOUNT_4MB:
|
|
|
|
return 4*1024*1024;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NV_10:
|
2007-07-14 10:32:11 -06:00
|
|
|
case NV_11:
|
2007-05-08 13:18:02 -06:00
|
|
|
case NV_17:
|
2006-08-26 16:55:02 -06:00
|
|
|
case NV_20:
|
|
|
|
case NV_30:
|
|
|
|
case NV_40:
|
2006-12-03 02:02:54 -07:00
|
|
|
case NV_44:
|
|
|
|
case NV_50:
|
2006-08-26 16:55:02 -06:00
|
|
|
default:
|
2007-08-16 09:12:46 -06:00
|
|
|
if (dev_priv->flags & (NV_NFORCE | NV_NFORCE2)) {
|
|
|
|
return nouveau_mem_fb_amount_igp(dev);
|
2006-08-26 16:55:02 -06:00
|
|
|
} else {
|
|
|
|
uint64_t mem;
|
2007-08-16 09:12:46 -06:00
|
|
|
|
2009-03-24 10:42:36 -06:00
|
|
|
mem = (NV_READ(NV10_PFB_CSTATUS) &
|
|
|
|
NV10_PFB_CSTATUS_RAM_AMOUNT_MB_MASK) >>
|
|
|
|
NV10_PFB_CSTATUS_RAM_AMOUNT_MB_SHIFT;
|
2006-08-26 16:55:02 -06:00
|
|
|
return mem*1024*1024;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
DRM_ERROR("Unable to detect video ram size. Please report your setup to " DRIVER_EMAIL "\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-03-10 18:33:58 -06:00
|
|
|
static void nouveau_mem_reset_agp(struct drm_device *dev)
|
|
|
|
{
|
|
|
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
|
|
uint32_t saved_pci_nv_1, saved_pci_nv_19, pmc_enable;
|
|
|
|
|
|
|
|
saved_pci_nv_1 = NV_READ(NV04_PBUS_PCI_NV_1);
|
|
|
|
saved_pci_nv_19 = NV_READ(NV04_PBUS_PCI_NV_19);
|
|
|
|
|
|
|
|
/* clear busmaster bit */
|
|
|
|
NV_WRITE(NV04_PBUS_PCI_NV_1, saved_pci_nv_1 & ~0x4);
|
|
|
|
/* clear SBA and AGP bits */
|
|
|
|
NV_WRITE(NV04_PBUS_PCI_NV_19, saved_pci_nv_19 & 0xfffff0ff);
|
|
|
|
|
|
|
|
/* power cycle pgraph, if enabled */
|
|
|
|
pmc_enable = NV_READ(NV03_PMC_ENABLE);
|
|
|
|
if (pmc_enable & NV_PMC_ENABLE_PGRAPH) {
|
|
|
|
NV_WRITE(NV03_PMC_ENABLE, pmc_enable & ~NV_PMC_ENABLE_PGRAPH);
|
|
|
|
NV_WRITE(NV03_PMC_ENABLE, NV_READ(NV03_PMC_ENABLE) |
|
|
|
|
NV_PMC_ENABLE_PGRAPH);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* and restore (gives effect of resetting AGP) */
|
|
|
|
NV_WRITE(NV04_PBUS_PCI_NV_19, saved_pci_nv_19);
|
|
|
|
NV_WRITE(NV04_PBUS_PCI_NV_1, saved_pci_nv_1);
|
|
|
|
}
|
|
|
|
|
2007-07-15 01:18:15 -06:00
|
|
|
static int
|
2007-11-04 06:01:38 -07:00
|
|
|
nouveau_mem_init_agp(struct drm_device *dev, int ttm)
|
2006-08-26 16:55:02 -06:00
|
|
|
{
|
2007-07-12 23:09:31 -06:00
|
|
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
2007-07-15 01:18:15 -06:00
|
|
|
struct drm_agp_info info;
|
|
|
|
struct drm_agp_mode mode;
|
|
|
|
int ret;
|
|
|
|
|
2008-03-10 18:33:58 -06:00
|
|
|
nouveau_mem_reset_agp(dev);
|
|
|
|
|
2007-07-15 01:18:15 -06:00
|
|
|
ret = drm_agp_acquire(dev);
|
|
|
|
if (ret) {
|
|
|
|
DRM_ERROR("Unable to acquire AGP: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
2006-08-26 16:55:02 -06:00
|
|
|
|
2007-07-15 01:18:15 -06:00
|
|
|
ret = drm_agp_info(dev, &info);
|
|
|
|
if (ret) {
|
|
|
|
DRM_ERROR("Unable to get AGP info: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
2006-08-26 16:55:02 -06:00
|
|
|
|
2007-07-15 01:18:15 -06:00
|
|
|
/* see agp.h for the AGPSTAT_* modes available */
|
|
|
|
mode.mode = info.mode;
|
|
|
|
ret = drm_agp_enable(dev, mode);
|
|
|
|
if (ret) {
|
|
|
|
DRM_ERROR("Unable to enable AGP: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
2006-12-03 02:02:54 -07:00
|
|
|
|
2007-11-04 06:01:38 -07:00
|
|
|
if (!ttm) {
|
|
|
|
struct drm_agp_buffer agp_req;
|
|
|
|
struct drm_agp_binding bind_req;
|
2007-07-10 18:35:10 -06:00
|
|
|
|
2007-11-04 06:01:38 -07:00
|
|
|
agp_req.size = info.aperture_size;
|
|
|
|
agp_req.type = 0;
|
|
|
|
ret = drm_agp_alloc(dev, &agp_req);
|
|
|
|
if (ret) {
|
|
|
|
DRM_ERROR("Unable to alloc AGP: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bind_req.handle = agp_req.handle;
|
|
|
|
bind_req.offset = 0;
|
|
|
|
ret = drm_agp_bind(dev, &bind_req);
|
|
|
|
if (ret) {
|
|
|
|
DRM_ERROR("Unable to bind AGP: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
2007-07-15 01:18:15 -06:00
|
|
|
}
|
2007-07-11 07:01:37 -06:00
|
|
|
|
2007-07-15 01:18:15 -06:00
|
|
|
dev_priv->gart_info.type = NOUVEAU_GART_AGP;
|
|
|
|
dev_priv->gart_info.aper_base = info.aperture_base;
|
|
|
|
dev_priv->gart_info.aper_size = info.aperture_size;
|
|
|
|
return 0;
|
|
|
|
}
|
2007-07-11 07:01:37 -06:00
|
|
|
|
2007-11-04 06:01:38 -07:00
|
|
|
#define HACK_OLD_MM
|
|
|
|
int
|
|
|
|
nouveau_mem_init_ttm(struct drm_device *dev)
|
|
|
|
{
|
|
|
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
|
|
uint32_t vram_size, bar1_size;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
dev_priv->agp_heap = dev_priv->pci_heap = dev_priv->fb_heap = NULL;
|
|
|
|
dev_priv->fb_phys = drm_get_resource_start(dev,1);
|
|
|
|
dev_priv->gart_info.type = NOUVEAU_GART_NONE;
|
|
|
|
|
|
|
|
drm_bo_driver_init(dev);
|
|
|
|
|
|
|
|
/* non-mappable vram */
|
|
|
|
dev_priv->fb_available_size = nouveau_mem_fb_amount(dev);
|
|
|
|
dev_priv->fb_available_size -= dev_priv->ramin_rsvd_vram;
|
|
|
|
vram_size = dev_priv->fb_available_size >> PAGE_SHIFT;
|
|
|
|
bar1_size = drm_get_resource_len(dev, 1) >> PAGE_SHIFT;
|
|
|
|
if (bar1_size < vram_size) {
|
|
|
|
if ((ret = drm_bo_init_mm(dev, DRM_BO_MEM_PRIV0,
|
2008-02-17 17:39:21 -07:00
|
|
|
bar1_size, vram_size - bar1_size, 1))) {
|
2007-11-04 06:01:38 -07:00
|
|
|
DRM_ERROR("Failed PRIV0 mm init: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
vram_size = bar1_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* mappable vram */
|
|
|
|
#ifdef HACK_OLD_MM
|
|
|
|
vram_size /= 4;
|
|
|
|
#endif
|
2008-02-17 17:39:21 -07:00
|
|
|
if ((ret = drm_bo_init_mm(dev, DRM_BO_MEM_VRAM, 0, vram_size, 1))) {
|
2007-11-04 06:01:38 -07:00
|
|
|
DRM_ERROR("Failed VRAM mm init: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* GART */
|
2008-02-15 19:50:10 -07:00
|
|
|
#if !defined(__powerpc__) && !defined(__ia64__)
|
2007-11-04 06:01:38 -07:00
|
|
|
if (drm_device_is_agp(dev) && dev->agp) {
|
|
|
|
if ((ret = nouveau_mem_init_agp(dev, 1)))
|
|
|
|
DRM_ERROR("Error initialising AGP: %d\n", ret);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (dev_priv->gart_info.type == NOUVEAU_GART_NONE) {
|
|
|
|
if ((ret = nouveau_sgdma_init(dev)))
|
|
|
|
DRM_ERROR("Error initialising PCI SGDMA: %d\n", ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = drm_bo_init_mm(dev, DRM_BO_MEM_TT, 0,
|
|
|
|
dev_priv->gart_info.aper_size >>
|
2008-02-17 17:39:21 -07:00
|
|
|
PAGE_SHIFT, 1))) {
|
2007-11-04 06:01:38 -07:00
|
|
|
DRM_ERROR("Failed TT mm init: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HACK_OLD_MM
|
|
|
|
vram_size <<= PAGE_SHIFT;
|
|
|
|
DRM_INFO("Old MM using %dKiB VRAM\n", (vram_size * 3) >> 10);
|
|
|
|
if (nouveau_mem_init_heap(&dev_priv->fb_heap, vram_size, vram_size * 3))
|
|
|
|
return -ENOMEM;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-07-15 01:18:15 -06:00
|
|
|
int nouveau_mem_init(struct drm_device *dev)
|
|
|
|
{
|
|
|
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
|
|
uint32_t fb_size;
|
|
|
|
int ret = 0;
|
2006-08-26 16:55:02 -06:00
|
|
|
|
2007-07-15 01:18:15 -06:00
|
|
|
dev_priv->agp_heap = dev_priv->pci_heap = dev_priv->fb_heap = NULL;
|
|
|
|
dev_priv->fb_phys = 0;
|
|
|
|
dev_priv->gart_info.type = NOUVEAU_GART_NONE;
|
2007-07-10 18:35:10 -06:00
|
|
|
|
2007-03-26 03:43:48 -06:00
|
|
|
/* setup a mtrr over the FB */
|
|
|
|
dev_priv->fb_mtrr = drm_mtrr_add(drm_get_resource_start(dev, 1),
|
|
|
|
nouveau_mem_fb_amount(dev),
|
|
|
|
DRM_MTRR_WC);
|
|
|
|
|
2006-08-26 16:55:02 -06:00
|
|
|
/* Init FB */
|
2006-12-03 02:02:54 -07:00
|
|
|
dev_priv->fb_phys=drm_get_resource_start(dev,1);
|
2007-01-07 06:37:39 -07:00
|
|
|
fb_size = nouveau_mem_fb_amount(dev);
|
2008-03-12 06:37:29 -06:00
|
|
|
/* On G80, limit VRAM to 512MiB temporarily due to limits in how
|
|
|
|
* we handle VRAM page tables.
|
|
|
|
*/
|
|
|
|
if (dev_priv->card_type >= NV_50 && fb_size > (512 * 1024 * 1024))
|
|
|
|
fb_size = (512 * 1024 * 1024);
|
2009-03-05 16:04:44 -07:00
|
|
|
fb_size -= dev_priv->ramin_rsvd_vram;
|
2007-02-27 21:14:08 -07:00
|
|
|
dev_priv->fb_available_size = fb_size;
|
2007-01-07 06:37:39 -07:00
|
|
|
DRM_DEBUG("Available VRAM: %dKiB\n", fb_size>>10);
|
|
|
|
|
|
|
|
if (fb_size>256*1024*1024) {
|
2007-11-04 19:42:22 -07:00
|
|
|
/* On cards with > 256Mb, you can't map everything.
|
2006-08-26 16:55:02 -06:00
|
|
|
* So we create a second FB heap for that type of memory */
|
2007-06-24 03:03:35 -06:00
|
|
|
if (nouveau_mem_init_heap(&dev_priv->fb_heap,
|
2007-07-11 18:15:16 -06:00
|
|
|
0, 256*1024*1024))
|
2007-07-19 18:00:17 -06:00
|
|
|
return -ENOMEM;
|
2007-06-24 03:03:35 -06:00
|
|
|
if (nouveau_mem_init_heap(&dev_priv->fb_nomap_heap,
|
2007-07-11 18:15:16 -06:00
|
|
|
256*1024*1024, fb_size-256*1024*1024))
|
2007-07-19 18:00:17 -06:00
|
|
|
return -ENOMEM;
|
2006-08-26 16:55:02 -06:00
|
|
|
} else {
|
2007-07-11 18:15:16 -06:00
|
|
|
if (nouveau_mem_init_heap(&dev_priv->fb_heap, 0, fb_size))
|
2007-07-19 18:00:17 -06:00
|
|
|
return -ENOMEM;
|
2006-08-26 16:55:02 -06:00
|
|
|
dev_priv->fb_nomap_heap=NULL;
|
|
|
|
}
|
|
|
|
|
2008-02-15 19:50:10 -07:00
|
|
|
#if !defined(__powerpc__) && !defined(__ia64__)
|
2007-07-15 01:18:15 -06:00
|
|
|
/* Init AGP / NV50 PCIEGART */
|
|
|
|
if (drm_device_is_agp(dev) && dev->agp) {
|
2007-11-04 06:01:38 -07:00
|
|
|
if ((ret = nouveau_mem_init_agp(dev, 0)))
|
2007-07-15 01:18:15 -06:00
|
|
|
DRM_ERROR("Error initialising AGP: %d\n", ret);
|
|
|
|
}
|
2007-11-01 08:48:46 -06:00
|
|
|
#endif
|
2007-07-15 01:18:15 -06:00
|
|
|
|
|
|
|
/*Note: this is *not* just NV50 code, but only used on NV50 for now */
|
2007-10-15 21:43:57 -06:00
|
|
|
if (dev_priv->gart_info.type == NOUVEAU_GART_NONE &&
|
|
|
|
dev_priv->card_type >= NV_50) {
|
2007-07-15 01:18:15 -06:00
|
|
|
ret = nouveau_sgdma_init(dev);
|
|
|
|
if (!ret) {
|
|
|
|
ret = nouveau_sgdma_nottm_hack_init(dev);
|
|
|
|
if (ret)
|
2007-11-04 19:42:22 -07:00
|
|
|
nouveau_sgdma_takedown(dev);
|
2007-07-15 01:18:15 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
DRM_ERROR("Error initialising SG DMA: %d\n", ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dev_priv->gart_info.type != NOUVEAU_GART_NONE) {
|
|
|
|
if (nouveau_mem_init_heap(&dev_priv->agp_heap,
|
|
|
|
0, dev_priv->gart_info.aper_size)) {
|
|
|
|
if (dev_priv->gart_info.type == NOUVEAU_GART_SGDMA) {
|
|
|
|
nouveau_sgdma_nottm_hack_takedown(dev);
|
2007-11-04 19:42:22 -07:00
|
|
|
nouveau_sgdma_takedown(dev);
|
2007-07-15 01:18:15 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* NV04-NV40 PCIEGART */
|
|
|
|
if (!dev_priv->agp_heap && dev_priv->card_type < NV_50) {
|
|
|
|
struct drm_scatter_gather sgreq;
|
|
|
|
|
|
|
|
DRM_DEBUG("Allocating sg memory for PCI DMA\n");
|
2007-08-06 09:42:31 -06:00
|
|
|
sgreq.size = 16 << 20; //16MB of PCI scatter-gather zone
|
2007-07-15 01:18:15 -06:00
|
|
|
|
|
|
|
if (drm_sg_alloc(dev, &sgreq)) {
|
2007-10-14 08:56:17 -06:00
|
|
|
DRM_ERROR("Unable to allocate %ldMB of scatter-gather"
|
2007-08-06 09:42:31 -06:00
|
|
|
" pages for PCI DMA!",sgreq.size>>20);
|
2007-07-15 01:18:15 -06:00
|
|
|
} else {
|
|
|
|
if (nouveau_mem_init_heap(&dev_priv->pci_heap, 0,
|
|
|
|
dev->sg->pages * PAGE_SIZE)) {
|
2007-11-04 19:42:22 -07:00
|
|
|
DRM_ERROR("Unable to initialize pci_heap!");
|
2007-07-15 01:18:15 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-12 06:37:29 -06:00
|
|
|
/* G8x: Allocate shared page table to map real VRAM pages into */
|
|
|
|
if (dev_priv->card_type >= NV_50) {
|
|
|
|
unsigned size = ((512 * 1024 * 1024) / 65536) * 8;
|
|
|
|
|
|
|
|
ret = nouveau_gpuobj_new(dev, NULL, size, 0,
|
|
|
|
NVOBJ_FLAG_ZERO_ALLOC |
|
|
|
|
NVOBJ_FLAG_ALLOW_NO_REFS,
|
|
|
|
&dev_priv->vm_vram_pt);
|
|
|
|
if (ret) {
|
|
|
|
DRM_ERROR("Error creating VRAM page table: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-08-26 16:55:02 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-06-22 09:00:42 -06:00
|
|
|
struct mem_block *
|
|
|
|
nouveau_mem_alloc(struct drm_device *dev, int alignment, uint64_t size,
|
|
|
|
int flags, struct drm_file *file_priv)
|
2006-08-26 16:55:02 -06:00
|
|
|
{
|
2007-07-12 23:09:31 -06:00
|
|
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
2008-06-22 09:00:42 -06:00
|
|
|
struct mem_block *block;
|
|
|
|
int type, tail = !(flags & NOUVEAU_MEM_USER);
|
2006-08-26 16:55:02 -06:00
|
|
|
|
2007-11-04 19:42:22 -07:00
|
|
|
/*
|
|
|
|
* Make things easier on ourselves: all allocations are page-aligned.
|
2006-08-26 16:55:02 -06:00
|
|
|
* We need that to map allocated regions into the user space
|
|
|
|
*/
|
2009-01-29 18:18:54 -07:00
|
|
|
if (alignment < PAGE_SIZE)
|
|
|
|
alignment = PAGE_SIZE;
|
2006-08-26 16:55:02 -06:00
|
|
|
|
2008-03-12 06:37:29 -06:00
|
|
|
/* Align allocation sizes to 64KiB blocks on G8x. We use a 64KiB
|
|
|
|
* page size in the GPU VM.
|
|
|
|
*/
|
2008-06-24 12:39:32 -06:00
|
|
|
if (flags & NOUVEAU_MEM_FB && dev_priv->card_type >= NV_50) {
|
|
|
|
size = (size + 65535) & ~65535;
|
2009-01-29 18:18:54 -07:00
|
|
|
if (alignment < 65536)
|
|
|
|
alignment = 65536;
|
2008-06-24 12:39:32 -06:00
|
|
|
}
|
2008-03-12 06:37:29 -06:00
|
|
|
|
2009-01-29 18:18:54 -07:00
|
|
|
/* Further down wants alignment in pages, not bytes */
|
|
|
|
alignment >>= PAGE_SHIFT;
|
|
|
|
|
2006-08-26 16:55:02 -06:00
|
|
|
/*
|
|
|
|
* Warn about 0 sized allocations, but let it go through. It'll return 1 page
|
|
|
|
*/
|
|
|
|
if (size == 0)
|
|
|
|
DRM_INFO("warning : 0 byte allocation\n");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Keep alloc size a multiple of the page size to keep drm_addmap() happy
|
|
|
|
*/
|
|
|
|
if (size & (~PAGE_MASK))
|
|
|
|
size = ((size/PAGE_SIZE) + 1) * PAGE_SIZE;
|
|
|
|
|
2007-07-10 18:35:10 -06:00
|
|
|
|
|
|
|
#define NOUVEAU_MEM_ALLOC_AGP {\
|
|
|
|
type=NOUVEAU_MEM_AGP;\
|
|
|
|
block = nouveau_mem_alloc_block(dev_priv->agp_heap, size,\
|
2008-06-22 09:00:42 -06:00
|
|
|
alignment, file_priv, tail); \
|
2007-07-10 18:35:10 -06:00
|
|
|
if (block) goto alloc_ok;\
|
|
|
|
}
|
|
|
|
|
|
|
|
#define NOUVEAU_MEM_ALLOC_PCI {\
|
|
|
|
type = NOUVEAU_MEM_PCI;\
|
2007-07-20 07:39:25 -06:00
|
|
|
block = nouveau_mem_alloc_block(dev_priv->pci_heap, size, \
|
2008-06-22 09:00:42 -06:00
|
|
|
alignment, file_priv, tail); \
|
2007-07-10 18:35:10 -06:00
|
|
|
if ( block ) goto alloc_ok;\
|
|
|
|
}
|
|
|
|
|
|
|
|
#define NOUVEAU_MEM_ALLOC_FB {\
|
|
|
|
type=NOUVEAU_MEM_FB;\
|
|
|
|
if (!(flags&NOUVEAU_MEM_MAPPED)) {\
|
|
|
|
block = nouveau_mem_alloc_block(dev_priv->fb_nomap_heap,\
|
2007-07-20 07:39:25 -06:00
|
|
|
size, alignment, \
|
2008-06-22 09:00:42 -06:00
|
|
|
file_priv, tail); \
|
2007-07-10 18:35:10 -06:00
|
|
|
if (block) goto alloc_ok;\
|
|
|
|
}\
|
|
|
|
block = nouveau_mem_alloc_block(dev_priv->fb_heap, size,\
|
2008-06-22 09:00:42 -06:00
|
|
|
alignment, file_priv, tail);\
|
2007-07-10 18:35:10 -06:00
|
|
|
if (block) goto alloc_ok;\
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (flags&NOUVEAU_MEM_FB) NOUVEAU_MEM_ALLOC_FB
|
|
|
|
if (flags&NOUVEAU_MEM_AGP) NOUVEAU_MEM_ALLOC_AGP
|
|
|
|
if (flags&NOUVEAU_MEM_PCI) NOUVEAU_MEM_ALLOC_PCI
|
|
|
|
if (flags&NOUVEAU_MEM_FB_ACCEPTABLE) NOUVEAU_MEM_ALLOC_FB
|
|
|
|
if (flags&NOUVEAU_MEM_AGP_ACCEPTABLE) NOUVEAU_MEM_ALLOC_AGP
|
|
|
|
if (flags&NOUVEAU_MEM_PCI_ACCEPTABLE) NOUVEAU_MEM_ALLOC_PCI
|
|
|
|
|
2006-08-26 16:55:02 -06:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
alloc_ok:
|
|
|
|
block->flags=type;
|
|
|
|
|
2008-03-12 06:37:29 -06:00
|
|
|
/* On G8x, map memory into VM */
|
|
|
|
if (block->flags & NOUVEAU_MEM_FB && dev_priv->card_type >= NV_50 &&
|
|
|
|
!(flags & NOUVEAU_MEM_NOVM)) {
|
|
|
|
struct nouveau_gpuobj *pt = dev_priv->vm_vram_pt;
|
|
|
|
unsigned offset = block->start;
|
|
|
|
unsigned count = block->size / 65536;
|
2008-06-24 12:39:32 -06:00
|
|
|
unsigned tile = 0;
|
2008-03-12 06:37:29 -06:00
|
|
|
|
|
|
|
if (!pt) {
|
|
|
|
DRM_ERROR("vm alloc without vm pt\n");
|
|
|
|
nouveau_mem_free_block(block);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-06-24 12:39:32 -06:00
|
|
|
/* The tiling stuff is *not* what NVIDIA does - but both the
|
|
|
|
* 2D and 3D engines seem happy with this simpler method.
|
|
|
|
* Should look into why NVIDIA do what they do at some point.
|
|
|
|
*/
|
|
|
|
if (flags & NOUVEAU_MEM_TILE) {
|
|
|
|
if (flags & NOUVEAU_MEM_TILE_ZETA)
|
|
|
|
tile = 0x00002800;
|
|
|
|
else
|
|
|
|
tile = 0x00007000;
|
|
|
|
}
|
|
|
|
|
2008-03-12 06:37:29 -06:00
|
|
|
while (count--) {
|
|
|
|
unsigned pte = offset / 65536;
|
|
|
|
|
|
|
|
INSTANCE_WR(pt, (pte * 2) + 0, offset | 1);
|
2008-06-24 12:39:32 -06:00
|
|
|
INSTANCE_WR(pt, (pte * 2) + 1, 0x00000000 | tile);
|
2008-03-12 06:37:29 -06:00
|
|
|
offset += 65536;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
block->flags |= NOUVEAU_MEM_NOVM;
|
|
|
|
}
|
|
|
|
|
2006-08-26 16:55:02 -06:00
|
|
|
if (flags&NOUVEAU_MEM_MAPPED)
|
|
|
|
{
|
2007-07-15 20:32:51 -06:00
|
|
|
struct drm_map_list *entry;
|
2007-07-10 18:35:10 -06:00
|
|
|
int ret = 0;
|
2006-08-26 16:55:02 -06:00
|
|
|
block->flags|=NOUVEAU_MEM_MAPPED;
|
|
|
|
|
2007-07-15 01:18:15 -06:00
|
|
|
if (type == NOUVEAU_MEM_AGP) {
|
|
|
|
if (dev_priv->gart_info.type != NOUVEAU_GART_SGDMA)
|
2007-07-12 10:18:59 -06:00
|
|
|
ret = drm_addmap(dev, block->start, block->size,
|
|
|
|
_DRM_AGP, 0, &block->map);
|
2007-07-15 01:18:15 -06:00
|
|
|
else
|
|
|
|
ret = drm_addmap(dev, block->start, block->size,
|
|
|
|
_DRM_SCATTER_GATHER, 0, &block->map);
|
|
|
|
}
|
2007-07-10 18:35:10 -06:00
|
|
|
else if (type == NOUVEAU_MEM_FB)
|
2007-07-11 18:15:16 -06:00
|
|
|
ret = drm_addmap(dev, block->start + dev_priv->fb_phys,
|
|
|
|
block->size, _DRM_FRAME_BUFFER,
|
|
|
|
0, &block->map);
|
2007-07-10 18:35:10 -06:00
|
|
|
else if (type == NOUVEAU_MEM_PCI)
|
2007-07-11 18:15:16 -06:00
|
|
|
ret = drm_addmap(dev, block->start, block->size,
|
|
|
|
_DRM_SCATTER_GATHER, 0, &block->map);
|
2007-07-10 18:35:10 -06:00
|
|
|
|
2007-11-04 19:42:22 -07:00
|
|
|
if (ret) {
|
2007-06-24 03:03:35 -06:00
|
|
|
nouveau_mem_free_block(block);
|
2006-08-26 16:55:02 -06:00
|
|
|
return NULL;
|
|
|
|
}
|
2007-07-11 18:15:16 -06:00
|
|
|
|
|
|
|
entry = drm_find_matching_map(dev, block->map);
|
|
|
|
if (!entry) {
|
|
|
|
nouveau_mem_free_block(block);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
block->map_handle = entry->user_token;
|
2006-08-26 16:55:02 -06:00
|
|
|
}
|
|
|
|
|
2008-01-23 08:40:19 -07:00
|
|
|
DRM_DEBUG("allocated %lld bytes at 0x%llx type=0x%08x\n", block->size, block->start, block->flags);
|
2006-08-26 16:55:02 -06:00
|
|
|
return block;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nouveau_mem_free(struct drm_device* dev, struct mem_block* block)
|
|
|
|
{
|
2008-03-12 06:37:29 -06:00
|
|
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
|
|
|
2007-08-14 21:53:58 -06:00
|
|
|
DRM_DEBUG("freeing 0x%llx type=0x%08x\n", block->start, block->flags);
|
2008-03-12 06:37:29 -06:00
|
|
|
|
2006-08-26 16:55:02 -06:00
|
|
|
if (block->flags&NOUVEAU_MEM_MAPPED)
|
|
|
|
drm_rmmap(dev, block->map);
|
2008-03-12 06:37:29 -06:00
|
|
|
|
|
|
|
/* G8x: Remove pages from vm */
|
|
|
|
if (block->flags & NOUVEAU_MEM_FB && dev_priv->card_type >= NV_50 &&
|
|
|
|
!(block->flags & NOUVEAU_MEM_NOVM)) {
|
|
|
|
struct nouveau_gpuobj *pt = dev_priv->vm_vram_pt;
|
|
|
|
unsigned offset = block->start;
|
|
|
|
unsigned count = block->size / 65536;
|
|
|
|
|
|
|
|
if (!pt) {
|
|
|
|
DRM_ERROR("vm free without vm pt\n");
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (count--) {
|
|
|
|
unsigned pte = offset / 65536;
|
|
|
|
INSTANCE_WR(pt, (pte * 2) + 0, 0);
|
|
|
|
INSTANCE_WR(pt, (pte * 2) + 1, 0);
|
|
|
|
offset += 65536;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out_free:
|
2007-06-24 03:03:35 -06:00
|
|
|
nouveau_mem_free_block(block);
|
2006-08-26 16:55:02 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ioctls
|
|
|
|
*/
|
|
|
|
|
2008-06-22 09:00:42 -06:00
|
|
|
int
|
|
|
|
nouveau_ioctl_mem_alloc(struct drm_device *dev, void *data,
|
|
|
|
struct drm_file *file_priv)
|
2006-08-26 16:55:02 -06:00
|
|
|
{
|
2008-06-22 09:24:11 -06:00
|
|
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
2007-07-19 18:11:11 -06:00
|
|
|
struct drm_nouveau_mem_alloc *alloc = data;
|
2006-08-26 16:55:02 -06:00
|
|
|
struct mem_block *block;
|
|
|
|
|
2007-08-06 05:45:18 -06:00
|
|
|
NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
2006-08-26 16:55:02 -06:00
|
|
|
|
2008-03-12 06:37:29 -06:00
|
|
|
if (alloc->flags & NOUVEAU_MEM_INTERNAL)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2008-06-22 09:00:42 -06:00
|
|
|
block = nouveau_mem_alloc(dev, alloc->alignment, alloc->size,
|
|
|
|
alloc->flags | NOUVEAU_MEM_USER, file_priv);
|
2006-08-26 16:55:02 -06:00
|
|
|
if (!block)
|
2007-07-19 18:00:17 -06:00
|
|
|
return -ENOMEM;
|
2007-07-19 18:11:11 -06:00
|
|
|
alloc->map_handle=block->map_handle;
|
|
|
|
alloc->offset=block->start;
|
|
|
|
alloc->flags=block->flags;
|
2006-08-26 16:55:02 -06:00
|
|
|
|
2008-06-22 09:24:11 -06:00
|
|
|
if (dev_priv->card_type >= NV_50 && alloc->flags & NOUVEAU_MEM_FB)
|
|
|
|
alloc->offset += 512*1024*1024;
|
|
|
|
|
2006-08-26 16:55:02 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-06-22 09:24:11 -06:00
|
|
|
int
|
|
|
|
nouveau_ioctl_mem_free(struct drm_device *dev, void *data,
|
|
|
|
struct drm_file *file_priv)
|
2006-08-26 16:55:02 -06:00
|
|
|
{
|
2007-07-12 23:09:31 -06:00
|
|
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
2007-07-19 18:11:11 -06:00
|
|
|
struct drm_nouveau_mem_free *memfree = data;
|
2006-08-26 16:55:02 -06:00
|
|
|
struct mem_block *block;
|
|
|
|
|
2007-08-06 05:45:18 -06:00
|
|
|
NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
|
|
|
|
2008-06-22 09:24:11 -06:00
|
|
|
if (dev_priv->card_type >= NV_50 && memfree->flags & NOUVEAU_MEM_FB)
|
|
|
|
memfree->offset -= 512*1024*1024;
|
|
|
|
|
2006-08-26 16:55:02 -06:00
|
|
|
block=NULL;
|
2009-02-07 14:20:17 -07:00
|
|
|
if (dev_priv->fb_heap && memfree->flags & NOUVEAU_MEM_FB)
|
2007-07-19 18:11:11 -06:00
|
|
|
block = find_block(dev_priv->fb_heap, memfree->offset);
|
2009-02-07 14:20:17 -07:00
|
|
|
else if (dev_priv->agp_heap && memfree->flags & NOUVEAU_MEM_AGP)
|
2007-07-19 18:11:11 -06:00
|
|
|
block = find_block(dev_priv->agp_heap, memfree->offset);
|
2009-02-07 14:20:17 -07:00
|
|
|
else if (dev_priv->pci_heap && memfree->flags & NOUVEAU_MEM_PCI)
|
2007-07-19 18:11:11 -06:00
|
|
|
block = find_block(dev_priv->pci_heap, memfree->offset);
|
2006-08-26 16:55:02 -06:00
|
|
|
if (!block)
|
2007-07-19 18:00:17 -06:00
|
|
|
return -EFAULT;
|
2007-07-20 07:39:25 -06:00
|
|
|
if (block->file_priv != file_priv)
|
2007-07-19 18:00:17 -06:00
|
|
|
return -EPERM;
|
2006-08-26 16:55:02 -06:00
|
|
|
|
|
|
|
nouveau_mem_free(dev, block);
|
|
|
|
return 0;
|
|
|
|
}
|
2008-06-24 12:39:32 -06:00
|
|
|
|
|
|
|
int
|
|
|
|
nouveau_ioctl_mem_tile(struct drm_device *dev, void *data,
|
|
|
|
struct drm_file *file_priv)
|
|
|
|
{
|
|
|
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
|
|
struct drm_nouveau_mem_tile *memtile = data;
|
|
|
|
struct mem_block *block = NULL;
|
|
|
|
|
|
|
|
NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
|
|
|
|
|
|
|
if (dev_priv->card_type < NV_50)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (memtile->flags & NOUVEAU_MEM_FB) {
|
|
|
|
memtile->offset -= 512*1024*1024;
|
|
|
|
block = find_block(dev_priv->fb_heap, memtile->offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!block)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (block->file_priv != file_priv)
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
{
|
|
|
|
struct nouveau_gpuobj *pt = dev_priv->vm_vram_pt;
|
|
|
|
unsigned offset = block->start + memtile->delta;
|
|
|
|
unsigned count = memtile->size / 65536;
|
|
|
|
unsigned tile = 0;
|
|
|
|
|
|
|
|
if (memtile->flags & NOUVEAU_MEM_TILE) {
|
|
|
|
if (memtile->flags & NOUVEAU_MEM_TILE_ZETA)
|
|
|
|
tile = 0x00002800;
|
|
|
|
else
|
|
|
|
tile = 0x00007000;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (count--) {
|
|
|
|
unsigned pte = offset / 65536;
|
|
|
|
|
|
|
|
INSTANCE_WR(pt, (pte * 2) + 0, offset | 1);
|
|
|
|
INSTANCE_WR(pt, (pte * 2) + 1, 0x00000000 | tile);
|
|
|
|
offset += 65536;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|