summaryrefslogtreecommitdiff
path: root/linux-core/radeon_gem.c
diff options
context:
space:
mode:
authorJesse Barnes <jbarnes@virtuousgeek.org>2008-12-09 10:23:43 -0800
committerJesse Barnes <jbarnes@virtuousgeek.org>2008-12-09 10:23:43 -0800
commit6656db10551bbb8770dd945b6d81d5138521f208 (patch)
tree68e457660c174ee2e16353691126ddd754c79398 /linux-core/radeon_gem.c
parentc99566fb810c9d8cae5e9cd39d1772b55e2f514c (diff)
parent12e68f8059485fb4f02a15f74ab2fa3bdff38c81 (diff)
Merge branch 'modesetting-gem'
Diffstat (limited to 'linux-core/radeon_gem.c')
-rw-r--r--linux-core/radeon_gem.c1608
1 files changed, 1608 insertions, 0 deletions
diff --git a/linux-core/radeon_gem.c b/linux-core/radeon_gem.c
new file mode 100644
index 00000000..b2e1d7fe
--- /dev/null
+++ b/linux-core/radeon_gem.c
@@ -0,0 +1,1608 @@
+/*
+ * Copyright 2008 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"),
+ * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Author: Dave Airlie
+ */
+#include "drmP.h"
+#include "drm.h"
+
+#include "radeon_drm.h"
+#include "radeon_drv.h"
+
+static int radeon_gem_ib_init(struct drm_device *dev);
+static int radeon_gem_ib_destroy(struct drm_device *dev);
+static int radeon_gem_dma_bufs_init(struct drm_device *dev);
+static void radeon_gem_dma_bufs_destroy(struct drm_device *dev);
+
+int radeon_gem_init_object(struct drm_gem_object *obj)
+{
+ struct drm_radeon_gem_object *obj_priv;
+
+ obj_priv = drm_calloc(1, sizeof(*obj_priv), DRM_MEM_DRIVER);
+ if (!obj_priv) {
+ return -ENOMEM;
+ }
+
+ obj->driver_private = obj_priv;
+ obj_priv->obj = obj;
+ return 0;
+}
+
+void radeon_gem_free_object(struct drm_gem_object *obj)
+{
+
+ struct drm_radeon_gem_object *obj_priv = obj->driver_private;
+
+ /* tear down the buffer object - gem holds struct mutex */
+ drm_bo_takedown_vm_locked(obj_priv->bo);
+ drm_bo_usage_deref_locked(&obj_priv->bo);
+ drm_free(obj->driver_private, 1, DRM_MEM_DRIVER);
+}
+
+int radeon_gem_info_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_radeon_private *dev_priv = dev->dev_private;
+ struct drm_radeon_gem_info *args = data;
+
+ args->vram_start = dev_priv->mm.vram_offset;
+ args->vram_size = dev_priv->mm.vram_size;
+ args->vram_visible = dev_priv->mm.vram_visible;
+
+ args->gart_start = dev_priv->mm.gart_start;
+ args->gart_size = dev_priv->mm.gart_useable;
+
+ return 0;
+}
+
+struct drm_gem_object *radeon_gem_object_alloc(struct drm_device *dev, int size, int alignment,
+ int initial_domain, bool discardable)
+{
+ struct drm_gem_object *obj;
+ struct drm_radeon_gem_object *obj_priv;
+ int ret;
+ uint32_t flags;
+ uint32_t page_align;
+
+ obj = drm_gem_object_alloc(dev, size);
+ if (!obj)
+ return NULL;
+
+ obj_priv = obj->driver_private;
+ flags = DRM_BO_FLAG_MAPPABLE;
+ if (initial_domain == RADEON_GEM_DOMAIN_VRAM)
+ flags |= DRM_BO_FLAG_MEM_VRAM;
+ else if (initial_domain == RADEON_GEM_DOMAIN_GTT)
+ flags |= DRM_BO_FLAG_MEM_TT;
+ else
+ flags |= DRM_BO_FLAG_MEM_LOCAL | DRM_BO_FLAG_CACHED;
+
+ flags |= DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE | DRM_BO_FLAG_EXE;
+
+ if (discardable)
+ flags |= DRM_BO_FLAG_DISCARDABLE;
+
+ if (alignment == 0)
+ alignment = PAGE_SIZE;
+
+ page_align = alignment >> PAGE_SHIFT;
+ /* create a TTM BO */
+ ret = drm_buffer_object_create(dev,
+ size, drm_bo_type_device,
+ flags, 0, page_align,
+ 0, &obj_priv->bo);
+ if (ret)
+ goto fail;
+
+ DRM_DEBUG("%p : size 0x%x, alignment %d, initial_domain %d\n", obj_priv->bo, size, alignment, initial_domain);
+ return obj;
+fail:
+
+ return NULL;
+}
+
+int radeon_gem_create_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_radeon_gem_create *args = data;
+ struct drm_radeon_gem_object *obj_priv;
+ struct drm_gem_object *obj;
+ int ret = 0;
+ int handle;
+
+ /* create a gem object to contain this object in */
+ args->size = roundup(args->size, PAGE_SIZE);
+
+ obj = radeon_gem_object_alloc(dev, args->size, args->alignment, args->initial_domain, args->no_backing_store);
+ if (!obj)
+ return -EINVAL;
+
+ obj_priv = obj->driver_private;
+ DRM_DEBUG("obj is %p bo is %p, %d\n", obj, obj_priv->bo, obj_priv->bo->num_pages);
+ ret = drm_gem_handle_create(file_priv, obj, &handle);
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_handle_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+
+ if (ret)
+ goto fail;
+
+ args->handle = handle;
+
+ return 0;
+fail:
+ drm_gem_object_unreference(obj);
+
+ return ret;
+}
+
+int radeon_gem_set_domain(struct drm_gem_object *obj, uint32_t read_domains, uint32_t write_domain, uint32_t *flags_p, bool unfenced)
+{
+ struct drm_radeon_gem_object *obj_priv;
+ uint32_t flags = 0;
+ int ret;
+
+ obj_priv = obj->driver_private;
+
+ /* work out where to validate the buffer to */
+ if (write_domain) { /* write domains always win */
+ if (write_domain == RADEON_GEM_DOMAIN_VRAM)
+ flags = DRM_BO_FLAG_MEM_VRAM;
+ else if (write_domain == RADEON_GEM_DOMAIN_GTT)
+ flags = DRM_BO_FLAG_MEM_TT; // need a can write gart check
+ else
+ return -EINVAL; // we can't write to system RAM
+ } else {
+ /* okay for a read domain - prefer wherever the object is now or close enough */
+ if (read_domains == 0)
+ return -EINVAL;
+
+ /* if its already a local memory and CPU is valid do nothing */
+ if (read_domains & RADEON_GEM_DOMAIN_CPU) {
+ if (obj_priv->bo->mem.mem_type == DRM_BO_MEM_LOCAL)
+ return 0;
+ if (read_domains == RADEON_GEM_DOMAIN_CPU)
+ return -EINVAL;
+ }
+
+ /* simple case no choice in domains */
+ if (read_domains == RADEON_GEM_DOMAIN_VRAM)
+ flags = DRM_BO_FLAG_MEM_VRAM;
+ else if (read_domains == RADEON_GEM_DOMAIN_GTT)
+ flags = DRM_BO_FLAG_MEM_TT;
+ else if ((obj_priv->bo->mem.mem_type == DRM_BO_MEM_VRAM) && (read_domains & RADEON_GEM_DOMAIN_VRAM))
+ flags = DRM_BO_FLAG_MEM_VRAM;
+ else if ((obj_priv->bo->mem.mem_type == DRM_BO_MEM_TT) && (read_domains & RADEON_GEM_DOMAIN_GTT))
+ flags = DRM_BO_FLAG_MEM_TT;
+ else if ((obj_priv->bo->mem.mem_type == DRM_BO_MEM_LOCAL) && (read_domains & RADEON_GEM_DOMAIN_GTT))
+ flags = DRM_BO_FLAG_MEM_TT;
+
+ /* no idea here just set whatever we are input */
+ if (flags == 0) {
+ if (read_domains & RADEON_GEM_DOMAIN_VRAM)
+ flags |= DRM_BO_FLAG_MEM_VRAM;
+ if (read_domains & RADEON_GEM_DOMAIN_GTT)
+ flags |= DRM_BO_FLAG_MEM_TT;
+ }
+ }
+
+ /* if this BO is pinned then we ain't moving it anywhere */
+ if (obj_priv->bo->pinned_mem_type && unfenced)
+ return 0;
+
+ DRM_DEBUG("validating %p from %d into %x %d %d\n", obj_priv->bo, obj_priv->bo->mem.mem_type, flags, read_domains, write_domain);
+ ret = drm_bo_do_validate(obj_priv->bo, flags, DRM_BO_MASK_MEM | DRM_BO_FLAG_CACHED,
+ unfenced ? DRM_BO_HINT_DONT_FENCE : 0, 0);
+ if (ret)
+ return ret;
+
+ if (flags_p)
+ *flags_p = flags;
+ return 0;
+
+}
+
+int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ /* transition the BO to a domain - just validate the BO into a certain domain */
+ struct drm_radeon_gem_set_domain *args = data;
+ struct drm_gem_object *obj;
+ struct drm_radeon_gem_object *obj_priv;
+ int ret;
+
+ /* for now if someone requests domain CPU - just make sure the buffer is finished with */
+
+ /* just do a BO wait for now */
+ obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+ if (obj == NULL)
+ return -EINVAL;
+
+ obj_priv = obj->driver_private;
+
+ ret = radeon_gem_set_domain(obj, args->read_domains, args->write_domain, NULL, true);
+
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+int radeon_gem_pread_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ return -ENOSYS;
+}
+
+int radeon_gem_pwrite_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_radeon_gem_pwrite *args = data;
+ struct drm_gem_object *obj;
+ struct drm_radeon_gem_object *obj_priv;
+ int ret;
+
+ obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+ if (obj == NULL)
+ return -EINVAL;
+
+ obj_priv = obj->driver_private;
+
+ /* check where the buffer is first - if not in VRAM
+ fallback to userspace copying for now */
+ mutex_lock(&obj_priv->bo->mutex);
+ if (obj_priv->bo->mem.mem_type != DRM_BO_MEM_VRAM) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ DRM_ERROR("pwriting data->size %lld %llx\n", args->size, args->offset);
+ ret = -EINVAL;
+
+#if 0
+ /* so need to grab an IB, copy the data into it in a loop
+ and send them to VRAM using HDB */
+ while ((buf = radeon_host_data_blit(dev, cpp, w, dst_pitch_off, &buf_pitch,
+ x, &y, (unsigned int*)&h, &hpass)) != 0) {
+ radeon_host_data_blit_copy_pass(dev, cpp, buf, (uint8_t *)src,
+ hpass, buf_pitch, src_pitch);
+ src += hpass * src_pitch;
+ }
+#endif
+out_unlock:
+ mutex_unlock(&obj_priv->bo->mutex);
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_radeon_gem_mmap *args = data;
+ struct drm_gem_object *obj;
+ struct drm_radeon_gem_object *obj_priv;
+ loff_t offset;
+ unsigned long addr;
+
+ obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+ if (obj == NULL)
+ return -EINVAL;
+
+ offset = args->offset;
+
+ DRM_DEBUG("got here %p\n", obj);
+ obj_priv = obj->driver_private;
+
+ DRM_DEBUG("got here %p %p %lld %ld\n", obj, obj_priv->bo, args->size, obj_priv->bo->num_pages);
+ if (!obj_priv->bo) {
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+ return -EINVAL;
+ }
+
+ down_write(&current->mm->mmap_sem);
+ addr = do_mmap_pgoff(file_priv->filp, 0, args->size,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ obj_priv->bo->map_list.hash.key);
+ up_write(&current->mm->mmap_sem);
+
+ DRM_DEBUG("got here %p %d\n", obj, obj_priv->bo->mem.mem_type);
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+ if (IS_ERR((void *)addr))
+ return addr;
+
+ args->addr_ptr = (uint64_t) addr;
+
+ return 0;
+
+}
+
+int radeon_gem_pin_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_radeon_gem_pin *args = data;
+ struct drm_gem_object *obj;
+ struct drm_radeon_gem_object *obj_priv;
+ int ret;
+ int flags = DRM_BO_FLAG_NO_EVICT;
+ int mask = DRM_BO_FLAG_NO_EVICT;
+
+ /* check for valid args */
+ if (args->pin_domain) {
+ mask |= DRM_BO_MASK_MEM;
+ if (args->pin_domain == RADEON_GEM_DOMAIN_GTT)
+ flags |= DRM_BO_FLAG_MEM_TT;
+ else if (args->pin_domain == RADEON_GEM_DOMAIN_VRAM)
+ flags |= DRM_BO_FLAG_MEM_VRAM;
+ else /* hand back the offset we currently have if no args supplied
+ - this is to allow old mesa to work - its a hack */
+ flags = 0;
+ }
+
+ obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+ if (obj == NULL)
+ return -EINVAL;
+
+ obj_priv = obj->driver_private;
+
+ /* validate into a pin with no fence */
+ DRM_DEBUG("got here %p %p %d\n", obj, obj_priv->bo, atomic_read(&obj_priv->bo->usage));
+ if (flags && !(obj_priv->bo->type != drm_bo_type_kernel && !DRM_SUSER(DRM_CURPROC))) {
+ ret = drm_bo_do_validate(obj_priv->bo, flags, mask,
+ DRM_BO_HINT_DONT_FENCE, 0);
+ } else
+ ret = 0;
+
+ args->offset = obj_priv->bo->offset;
+ DRM_DEBUG("got here %p %p %x\n", obj, obj_priv->bo, obj_priv->bo->offset);
+
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+int radeon_gem_unpin_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_radeon_gem_unpin *args = data;
+ struct drm_gem_object *obj;
+ struct drm_radeon_gem_object *obj_priv;
+ int ret;
+
+ obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+ if (obj == NULL)
+ return -EINVAL;
+
+ obj_priv = obj->driver_private;
+
+ /* validate into a pin with no fence */
+
+ ret = drm_bo_do_validate(obj_priv->bo, 0, DRM_BO_FLAG_NO_EVICT,
+ DRM_BO_HINT_DONT_FENCE, 0);
+
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+int radeon_gem_busy(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ return 0;
+}
+
+int radeon_gem_wait_rendering(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_radeon_gem_wait_rendering *args = data;
+ struct drm_gem_object *obj;
+ struct drm_radeon_gem_object *obj_priv;
+ int ret;
+
+
+ obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+ if (obj == NULL)
+ return -EINVAL;
+
+ obj_priv = obj->driver_private;
+
+ mutex_lock(&obj_priv->bo->mutex);
+ ret = drm_bo_wait(obj_priv->bo, 0, 1, 1, 0);
+ mutex_unlock(&obj_priv->bo->mutex);
+
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+
+
+/*
+ * Depending on card genertation, chipset bugs, etc... the amount of vram
+ * accessible to the CPU can vary. This function is our best shot at figuring
+ * it out. Returns a value in KB.
+ */
+static uint32_t radeon_get_accessible_vram(struct drm_device *dev)
+{
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+ uint32_t aper_size;
+ u8 byte;
+
+ if (dev_priv->chip_family >= CHIP_R600)
+ aper_size = RADEON_READ(R600_CONFIG_APER_SIZE) / 1024;
+ else
+ aper_size = RADEON_READ(RADEON_CONFIG_APER_SIZE) / 1024;
+
+ /* Set HDP_APER_CNTL only on cards that are known not to be broken,
+ * that is has the 2nd generation multifunction PCI interface
+ */
+ if (dev_priv->chip_family == CHIP_RV280 ||
+ dev_priv->chip_family == CHIP_RV350 ||
+ dev_priv->chip_family == CHIP_RV380 ||
+ dev_priv->chip_family == CHIP_R420 ||
+ dev_priv->chip_family == CHIP_R423 ||
+ dev_priv->chip_family == CHIP_RV410 ||
+ radeon_is_avivo(dev_priv)) {
+ uint32_t temp = RADEON_READ(RADEON_HOST_PATH_CNTL);
+ temp |= RADEON_HDP_APER_CNTL;
+ RADEON_WRITE(RADEON_HOST_PATH_CNTL, temp);
+ return aper_size * 2;
+ }
+
+ /* Older cards have all sorts of funny issues to deal with. First
+ * check if it's a multifunction card by reading the PCI config
+ * header type... Limit those to one aperture size
+ */
+ pci_read_config_byte(dev->pdev, 0xe, &byte);
+ if (byte & 0x80)
+ return aper_size;
+
+ /* Single function older card. We read HDP_APER_CNTL to see how the BIOS
+ * have set it up. We don't write this as it's broken on some ASICs but
+ * we expect the BIOS to have done the right thing (might be too optimistic...)
+ */
+ if (RADEON_READ(RADEON_HOST_PATH_CNTL) & RADEON_HDP_APER_CNTL)
+ return aper_size * 2;
+
+ return aper_size;
+}
+
+/* code from the DDX - do memory sizing */
+void radeon_vram_setup(struct drm_device *dev)
+{
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+ uint32_t vram;
+ uint32_t accessible, bar_size;
+
+ if (!radeon_is_avivo(dev_priv) && (dev_priv->flags & RADEON_IS_IGP)) {
+ uint32_t tom = RADEON_READ(RADEON_NB_TOM);
+
+ vram = (((tom >> 16) - (tom & 0xffff) + 1) << 6);
+ RADEON_WRITE(RADEON_CONFIG_MEMSIZE, vram * 1024);
+ } else {
+ if (dev_priv->chip_family >= CHIP_R600)
+ vram = RADEON_READ(R600_CONFIG_MEMSIZE) / 1024;
+ else {
+ vram = RADEON_READ(RADEON_CONFIG_MEMSIZE) / 1024;
+
+ /* Some production boards of m6 will return 0 if it's 8 MB */
+ if (vram == 0) {
+ vram = 8192;
+ RADEON_WRITE(RADEON_CONFIG_MEMSIZE, 0x800000);
+ }
+ }
+ }
+
+ accessible = radeon_get_accessible_vram(dev);
+
+ bar_size = drm_get_resource_len(dev, 0) / 1024;
+ if (bar_size == 0)
+ bar_size = 0x20000;
+ if (accessible > bar_size)
+ accessible = bar_size;
+
+ if (accessible > vram)
+ accessible = vram;
+
+ DRM_INFO("Detected VRAM RAM=%dK, accessible=%uK, BAR=%uK\n",
+ vram, accessible, bar_size);
+
+ dev_priv->mm.vram_offset = dev_priv->fb_aper_offset;
+ dev_priv->mm.vram_size = vram * 1024;
+ dev_priv->mm.vram_visible = accessible * 1024;
+
+
+}
+
+static int radeon_gart_init(struct drm_device *dev)
+{
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+ int ret;
+ u32 base = 0;
+
+ /* setup a 32MB GART */
+ dev_priv->gart_size = dev_priv->mm.gart_size;
+
+ dev_priv->gart_info.table_size = RADEON_PCIGART_TABLE_SIZE;
+
+#if __OS_HAS_AGP
+ /* setup VRAM vs GART here */
+ if (dev_priv->flags & RADEON_IS_AGP) {
+ base = dev->agp->base;
+ if ((base + dev_priv->gart_size - 1) >= dev_priv->fb_location &&
+ base < (dev_priv->fb_location + dev_priv->fb_size - 1)) {
+ DRM_INFO("Can't use agp base @0x%08lx, won't fit\n",
+ dev->agp->base);
+ base = 0;
+ }
+ }
+#endif
+
+ if (base == 0) {
+ base = dev_priv->fb_location + dev_priv->fb_size;
+ if (base < dev_priv->fb_location ||
+ ((base + dev_priv->gart_size) & 0xfffffffful) < base)
+ base = dev_priv->fb_location
+ - dev_priv->gart_size;
+ }
+ /* start on the card */
+ dev_priv->gart_vm_start = base & 0xffc00000u;
+ if (dev_priv->gart_vm_start != base)
+ DRM_INFO("GART aligned down from 0x%08x to 0x%08x\n",
+ base, dev_priv->gart_vm_start);
+
+ /* if on PCIE we need to allocate an fb object for the PCIE GART table */
+ if (dev_priv->flags & RADEON_IS_PCIE) {
+ ret = drm_buffer_object_create(dev, RADEON_PCIGART_TABLE_SIZE,
+ drm_bo_type_kernel,
+ DRM_BO_FLAG_READ | DRM_BO_FLAG_MEM_VRAM | DRM_BO_FLAG_MAPPABLE | DRM_BO_FLAG_NO_EVICT,
+ 0, 1, 0, &dev_priv->mm.pcie_table.bo);
+ if (ret)
+ return -EINVAL;
+
+ /* subtract from VRAM value reporting to userspace */
+ dev_priv->mm.vram_visible -= RADEON_PCIGART_TABLE_SIZE;
+
+ dev_priv->mm.pcie_table_backup = kzalloc(RADEON_PCIGART_TABLE_SIZE, GFP_KERNEL);
+ if (!dev_priv->mm.pcie_table_backup)
+ return -EINVAL;
+
+ ret = drm_bo_kmap(dev_priv->mm.pcie_table.bo, 0, RADEON_PCIGART_TABLE_SIZE >> PAGE_SHIFT,
+ &dev_priv->mm.pcie_table.kmap);
+ if (ret)
+ return -EINVAL;
+
+ dev_priv->pcigart_offset_set = 2;
+ dev_priv->gart_info.bus_addr = dev_priv->fb_location + dev_priv->mm.pcie_table.bo->offset;
+ dev_priv->gart_info.addr = dev_priv->mm.pcie_table.kmap.virtual;
+ dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_PCIE;
+ dev_priv->gart_info.gart_table_location = DRM_ATI_GART_FB;
+ memset(dev_priv->gart_info.addr, 0, RADEON_PCIGART_TABLE_SIZE);
+ } else if (!(dev_priv->flags & RADEON_IS_AGP)) {
+ /* allocate PCI GART table */
+ dev_priv->gart_info.table_mask = DMA_BIT_MASK(32);
+ dev_priv->gart_info.gart_table_location = DRM_ATI_GART_MAIN;
+ if (dev_priv->flags & RADEON_IS_IGPGART)
+ dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_IGP;
+ else
+ dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_PCI;
+
+ ret = drm_ati_alloc_pcigart_table(dev, &dev_priv->gart_info);
+ if (ret) {
+ DRM_ERROR("cannot allocate PCI GART page!\n");
+ return -EINVAL;
+ }
+
+ dev_priv->gart_info.addr = dev_priv->gart_info.table_handle->vaddr;
+ dev_priv->gart_info.bus_addr = dev_priv->gart_info.table_handle->busaddr;
+ }
+
+ /* gart values setup - start the GART */
+ if (dev_priv->flags & RADEON_IS_AGP) {
+ radeon_set_pcigart(dev_priv, 0);
+ } else {
+ radeon_set_pcigart(dev_priv, 1);
+ }
+
+ return 0;
+}
+
+int radeon_alloc_gart_objects(struct drm_device *dev)
+{
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+ int ret;
+
+ ret = drm_buffer_object_create(dev, RADEON_DEFAULT_RING_SIZE,
+ drm_bo_type_kernel,
+ DRM_BO_FLAG_READ | DRM_BO_FLAG_MEM_TT |
+ DRM_BO_FLAG_MAPPABLE | DRM_BO_FLAG_NO_EVICT,
+ 0, 1, 0, &dev_priv->mm.ring.bo);
+ if (ret) {
+ if (dev_priv->flags & RADEON_IS_AGP)
+ DRM_ERROR("failed to allocate ring - most likely an AGP driver bug\n");
+ else
+ DRM_ERROR("failed to allocate ring\n");
+ return -EINVAL;
+ }
+
+ ret = drm_bo_kmap(dev_priv->mm.ring.bo, 0, RADEON_DEFAULT_RING_SIZE >> PAGE_SHIFT,
+ &dev_priv->mm.ring.kmap);
+ if (ret) {
+ DRM_ERROR("failed to map ring\n");
+ return -EINVAL;
+ }
+
+ ret = drm_buffer_object_create(dev, PAGE_SIZE,
+ drm_bo_type_kernel,
+ DRM_BO_FLAG_WRITE |DRM_BO_FLAG_READ | DRM_BO_FLAG_MEM_TT |
+ DRM_BO_FLAG_MAPPABLE | DRM_BO_FLAG_NO_EVICT,
+ 0, 1, 0, &dev_priv->mm.ring_read.bo);
+ if (ret) {
+ DRM_ERROR("failed to allocate ring read\n");
+ return -EINVAL;
+ }
+
+ ret = drm_bo_kmap(dev_priv->mm.ring_read.bo, 0,
+ PAGE_SIZE >> PAGE_SHIFT,
+ &dev_priv->mm.ring_read.kmap);
+ if (ret) {
+ DRM_ERROR("failed to map ring read\n");
+ return -EINVAL;
+ }
+
+ DRM_DEBUG("Ring ptr %p mapped at %ld %p, read ptr %p maped at %ld %p\n",
+ dev_priv->mm.ring.bo, dev_priv->mm.ring.bo->offset, dev_priv->mm.ring.kmap.virtual,
+ dev_priv->mm.ring_read.bo, dev_priv->mm.ring_read.bo->offset, dev_priv->mm.ring_read.kmap.virtual);
+
+ dev_priv->mm.gart_useable -= RADEON_DEFAULT_RING_SIZE + PAGE_SIZE;
+
+ /* init the indirect buffers */
+ radeon_gem_ib_init(dev);
+ radeon_gem_dma_bufs_init(dev);
+ return 0;
+
+}
+
+static bool avivo_get_mc_idle(struct drm_device *dev)
+{
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+
+ if (dev_priv->chip_family >= CHIP_R600) {
+ /* no idea where this is on r600 yet */
+ return true;
+ } else if (dev_priv->chip_family == CHIP_RV515) {
+ if (radeon_read_mc_reg(dev_priv, RV515_MC_STATUS) & RV515_MC_STATUS_IDLE)
+ return true;
+ else
+ return false;
+ } else if (dev_priv->chip_family == CHIP_RS600) {
+ if (radeon_read_mc_reg(dev_priv, RS600_MC_STATUS) & RS600_MC_STATUS_IDLE)
+ return true;
+ else
+ return false;
+ } else if ((dev_priv->chip_family == CHIP_RS690) ||
+ (dev_priv->chip_family == CHIP_RS740)) {
+ if (radeon_read_mc_reg(dev_priv, RS690_MC_STATUS) & RS690_MC_STATUS_IDLE)
+ return true;
+ else
+ return false;
+ } else {
+ if (radeon_read_mc_reg(dev_priv, R520_MC_STATUS) & R520_MC_STATUS_IDLE)
+ return true;
+ else
+ return false;
+ }
+}
+
+
+static void avivo_disable_mc_clients(struct drm_device *dev)
+{
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+ uint32_t tmp;
+ int timeout;
+
+ radeon_do_wait_for_idle(dev_priv);
+
+ RADEON_WRITE(AVIVO_D1VGA_CONTROL, RADEON_READ(AVIVO_D1VGA_CONTROL) & ~AVIVO_DVGA_CONTROL_MODE_ENABLE);
+ RADEON_WRITE(AVIVO_D2VGA_CONTROL, RADEON_READ(AVIVO_D2VGA_CONTROL) & ~AVIVO_DVGA_CONTROL_MODE_ENABLE);
+
+ tmp = RADEON_READ(AVIVO_D1CRTC_CONTROL);
+ RADEON_WRITE(AVIVO_D1CRTC_CONTROL, tmp & ~AVIVO_CRTC_EN);
+
+ tmp = RADEON_READ(AVIVO_D2CRTC_CONTROL);
+ RADEON_WRITE(AVIVO_D2CRTC_CONTROL, tmp & ~AVIVO_CRTC_EN);
+
+ tmp = RADEON_READ(AVIVO_D2CRTC_CONTROL);
+
+ udelay(1000);
+
+ timeout = 0;
+ while (!(avivo_get_mc_idle(dev))) {
+ if (++timeout > 100000) {
+ DRM_ERROR("Timeout waiting for memory controller to update settings\n");
+ DRM_ERROR("Bad things may or may not happen\n");
+ }
+ udelay(10);
+ }
+}
+
+static inline u32 radeon_busy_wait(struct drm_device *dev, uint32_t reg, uint32_t bits,
+ unsigned int timeout)
+{
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+ u32 status;
+
+ do {
+ udelay(10);
+ status = RADEON_READ(reg);
+ timeout--;
+ } while(status != 0xffffffff && (status & bits) && (timeout > 0));
+
+ if (timeout == 0)
+ status = 0xffffffff;
+
+ return status;
+}
+
+/* Wait for vertical sync on primary CRTC */
+static void radeon_wait_for_vsync(struct drm_device *dev)
+{
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+ uint32_t crtc_gen_cntl;
+
+ crtc_gen_cntl = RADEON_READ(RADEON_CRTC_GEN_CNTL);
+ if ((crtc_gen_cntl & RADEON_CRTC_DISP_REQ_EN_B) ||
+ !(crtc_gen_cntl & RADEON_CRTC_EN))
+ return;
+
+ /* Clear the CRTC_VBLANK_SAVE bit */
+ RADEON_WRITE(RADEON_CRTC_STATUS, RADEON_CRTC_VBLANK_SAVE_CLEAR);
+
+ radeon_busy_wait(dev, RADEON_CRTC_STATUS, RADEON_CRTC_VBLANK_SAVE, 2000);
+
+}
+
+/* Wait for vertical sync on primary CRTC */
+static void radeon_wait_for_vsync2(struct drm_device *dev)
+{
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+ uint32_t crtc2_gen_cntl;
+
+ crtc2_gen_cntl = RADEON_READ(RADEON_CRTC2_GEN_CNTL);
+ if ((crtc2_gen_cntl & RADEON_CRTC2_DISP_REQ_EN_B) ||
+ !(crtc2_gen_cntl & RADEON_CRTC2_EN))
+ return;
+
+ /* Clear the CRTC_VBLANK_SAVE bit */
+ RADEON_WRITE(RADEON_CRTC2_STATUS, RADEON_CRTC2_VBLANK_SAVE_CLEAR);
+
+ radeon_busy_wait(dev, RADEON_CRTC2_STATUS, RADEON_CRTC2_VBLANK_SAVE, 2000);
+}
+
+static void legacy_disable_mc_clients(struct drm_device *dev)
+{
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+ uint32_t old_mc_status, status_idle;
+ uint32_t ov0_scale_cntl, crtc_ext_cntl, crtc_gen_cntl, crtc2_gen_cntl;
+ uint32_t status;
+
+ radeon_do_wait_for_idle(dev_priv);
+
+ if (dev_priv->flags & RADEON_IS_IGP)
+ return;
+
+ old_mc_status = RADEON_READ(RADEON_MC_STATUS);
+
+ /* stop display and memory access */
+ ov0_scale_cntl = RADEON_READ(RADEON_OV0_SCALE_CNTL);
+ RADEON_WRITE(RADEON_OV0_SCALE_CNTL, ov0_scale_cntl & ~RADEON_SCALER_ENABLE);
+ crtc_ext_cntl = RADEON_READ(RADEON_CRTC_EXT_CNTL);
+ RADEON_WRITE(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl | RADEON_CRTC_DISPLAY_DIS);
+ crtc_gen_cntl = RADEON_READ(RADEON_CRTC_GEN_CNTL);
+
+ radeon_wait_for_vsync(dev);
+
+ RADEON_WRITE(RADEON_CRTC_GEN_CNTL,
+ (crtc_gen_cntl & ~(RADEON_CRTC_CUR_EN | RADEON_CRTC_ICON_EN)) |
+ RADEON_CRTC_DISP_REQ_EN_B | RADEON_CRTC_EXT_DISP_EN);
+
+ if (!(dev_priv->flags & RADEON_SINGLE_CRTC)) {
+ crtc2_gen_cntl = RADEON_READ(RADEON_CRTC2_GEN_CNTL);
+
+ radeon_wait_for_vsync2(dev);
+ RADEON_WRITE(RADEON_CRTC2_GEN_CNTL,
+ (crtc2_gen_cntl &
+ ~(RADEON_CRTC2_CUR_EN | RADEON_CRTC2_ICON_EN)) |
+ RADEON_CRTC2_DISP_REQ_EN_B);
+ }
+
+ udelay(500);
+
+ if (radeon_is_r300(dev_priv))
+ status_idle = R300_MC_IDLE;
+ else
+ status_idle = RADEON_MC_IDLE;
+
+ status = radeon_busy_wait(dev, RADEON_MC_STATUS, status_idle, 200000);
+ if (status == 0xffffffff) {
+ DRM_ERROR("Timeout waiting for memory controller to update settings\n");
+ DRM_ERROR("Bad things may or may not happen\n");
+ }
+}
+
+
+void radeon_init_memory_map(struct drm_device *dev)
+{
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+ u32 mem_size, aper_size;
+
+ dev_priv->mc_fb_location = radeon_read_fb_location(dev_priv);
+ radeon_read_agp_location(dev_priv, &dev_priv->mc_agp_loc_lo, &dev_priv->mc_agp_loc_hi);
+
+ if (dev_priv->chip_family >= CHIP_R600) {
+ mem_size = RADEON_READ(R600_CONFIG_MEMSIZE);
+ aper_size = RADEON_READ(R600_CONFIG_APER_SIZE);
+ } else {
+ mem_size = RADEON_READ(RADEON_CONFIG_MEMSIZE);
+ aper_size = RADEON_READ(RADEON_CONFIG_APER_SIZE);
+ }
+
+ /* M6s report illegal memory size */
+ if (mem_size == 0)
+ mem_size = 8 * 1024 * 1024;
+
+ /* for RN50/M6/M7 - Novell bug 204882 */
+ if (aper_size > mem_size)
+ mem_size = aper_size;
+
+ if ((dev_priv->chip_family != CHIP_RS600) &&
+ (dev_priv->chip_family != CHIP_RS690) &&
+ (dev_priv->chip_family != CHIP_RS740)) {
+ if (dev_priv->flags & RADEON_IS_IGP)
+ dev_priv->mc_fb_location = RADEON_READ(RADEON_NB_TOM);
+ else {
+ uint32_t aper0_base;
+
+ if (dev_priv->chip_family >= CHIP_R600)
+ aper0_base = RADEON_READ(R600_CONFIG_F0_BASE);
+ else
+ aper0_base = RADEON_READ(RADEON_CONFIG_APER_0_BASE);
+
+
+ /* Some chips have an "issue" with the memory controller, the
+ * location must be aligned to the size. We just align it down,
+ * too bad if we walk over the top of system memory, we don't
+ * use DMA without a remapped anyway.
+ * Affected chips are rv280, all r3xx, and all r4xx, but not IGP
+ */
+ if (dev_priv->chip_family == CHIP_RV280 ||
+ dev_priv->chip_family == CHIP_R300 ||
+ dev_priv->chip_family == CHIP_R350 ||
+ dev_priv->chip_family == CHIP_RV350 ||
+ dev_priv->chip_family == CHIP_RV380 ||
+ dev_priv->chip_family == CHIP_R420 ||
+ dev_priv->chip_family == CHIP_R423 ||
+ dev_priv->chip_family == CHIP_RV410)
+ aper0_base &= ~(mem_size - 1);
+
+ if (dev_priv->chip_family >= CHIP_R600) {
+ dev_priv->mc_fb_location = (aper0_base >> 24) |
+ (((aper0_base + mem_size - 1) & 0xff000000U) >> 8);
+ } else {
+ dev_priv->mc_fb_location = (aper0_base >> 16) |
+ ((aper0_base + mem_size - 1) & 0xffff0000U);
+ }
+ }
+ }
+
+ if (dev_priv->chip_family >= CHIP_R600)
+ dev_priv->fb_location = (dev_priv->mc_fb_location & 0xffff) << 24;
+ else
+ dev_priv->fb_location = (dev_priv->mc_fb_location & 0xffff) << 16;
+
+ /* updating mc regs here */
+ if (radeon_is_avivo(dev_priv))
+ avivo_disable_mc_clients(dev);
+ else
+ legacy_disable_mc_clients(dev);
+
+ radeon_write_fb_location(dev_priv, dev_priv->mc_fb_location);
+
+ if (radeon_is_avivo(dev_priv)) {
+ if (dev_priv->chip_family >= CHIP_R600)
+ RADEON_WRITE(R600_HDP_NONSURFACE_BASE, (dev_priv->mc_fb_location << 16) & 0xff0000);
+ else
+ RADEON_WRITE(AVIVO_HDP_FB_LOCATION, dev_priv->mc_fb_location);
+ }
+
+ if (dev_priv->chip_family >= CHIP_R600) {
+ dev_priv->fb_location = (radeon_read_fb_location(dev_priv) & 0xffffff) << 24;
+ dev_priv->fb_size = ((radeon_read_fb_location(dev_priv) & 0xff000000u) + 0x1000000)
+ - dev_priv->fb_location;
+ } else {
+ dev_priv->fb_location = (radeon_read_fb_location(dev_priv) & 0xffff) << 16;
+ dev_priv->fb_size =
+ ((radeon_read_fb_location(dev_priv) & 0xffff0000u) + 0x10000)
+ - dev_priv->fb_location;
+ }
+
+ /* add an MTRR for the VRAM */
+ dev_priv->aper_size = aper_size;
+ dev_priv->vram_mtrr = mtrr_add(dev_priv->fb_aper_offset, dev_priv->aper_size, MTRR_TYPE_WRCOMB, 1);
+
+}
+
+/* init memory manager - start with all of VRAM and a 32MB GART aperture for now */
+int radeon_gem_mm_init(struct drm_device *dev)
+{
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+ int ret;
+ u32 pg_offset;
+
+ /* init TTM underneath */
+ drm_bo_driver_init(dev);
+
+ /* use the uncached allocator */
+ dev->bm.allocator_type = _DRM_BM_ALLOCATOR_UNCACHED;
+
+ /* size the mappable VRAM memory for now */
+ radeon_vram_setup(dev);
+
+ radeon_init_memory_map(dev);
+
+#define VRAM_RESERVE_TEXT (256*1024) /* need to reserve 256 for text mode for now */
+ dev_priv->mm.vram_visible -= VRAM_RESERVE_TEXT;
+ pg_offset = VRAM_RESERVE_TEXT >> PAGE_SHIFT;
+ drm_bo_init_mm(dev, DRM_BO_MEM_VRAM, pg_offset, /*dev_priv->mm.vram_offset >> PAGE_SHIFT,*/
+ ((dev_priv->mm.vram_visible) >> PAGE_SHIFT) - 16,
+ 0);
+
+
+ if (dev_priv->chip_family > CHIP_R600) {
+ dev_priv->mm_enabled = true;
+ return 0;
+ }
+
+ dev_priv->mm.gart_size = (32 * 1024 * 1024);
+ dev_priv->mm.gart_start = 0;
+ dev_priv->mm.gart_useable = dev_priv->mm.gart_size;
+ ret = radeon_gart_init(dev);
+ if (ret)
+ return -EINVAL;
+
+ drm_bo_init_mm(dev, DRM_BO_MEM_TT, 0,
+ dev_priv->mm.gart_size >> PAGE_SHIFT,
+ 0);
+
+ /* need to allocate some objects in the GART */
+ /* ring + ring read ptr */
+ ret = radeon_alloc_gart_objects(dev);
+ if (ret) {
+ radeon_gem_mm_fini(dev);
+ return -EINVAL;
+ }
+
+ dev_priv->mm_enabled = true;
+ return 0;
+}
+
+void radeon_gem_mm_fini(struct drm_device *dev)
+{
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+
+ radeon_gem_dma_bufs_destroy(dev);
+ radeon_gem_ib_destroy(dev);
+
+ mutex_lock(&dev->struct_mutex);
+
+ if (dev_priv->mm.ring_read.bo) {
+ drm_bo_kunmap(&dev_priv->mm.ring_read.kmap);
+ drm_bo_usage_deref_locked(&dev_priv->mm.ring_read.bo);
+ }
+
+ if (dev_priv->mm.ring.bo) {
+ drm_bo_kunmap(&dev_priv->mm.ring.kmap);
+ drm_bo_usage_deref_locked(&dev_priv->mm.ring.bo);
+ }
+
+ if (drm_bo_clean_mm(dev, DRM_BO_MEM_TT, 1)) {
+ DRM_DEBUG("delaying takedown of TTM memory\n");
+ }
+
+ if (dev_priv->flags & RADEON_IS_PCIE) {
+ if (dev_priv->mm.pcie_table_backup) {
+ kfree(dev_priv->mm.pcie_table_backup);
+ dev_priv->mm.pcie_table_backup = NULL;
+ }
+ if (dev_priv->mm.pcie_table.bo) {
+ drm_bo_kunmap(&dev_priv->mm.pcie_table.kmap);
+ drm_bo_usage_deref_locked(&dev_priv->mm.pcie_table.bo);
+ }
+ }
+
+ if (drm_bo_clean_mm(dev, DRM_BO_MEM_VRAM, 1)) {
+ DRM_DEBUG("delaying takedown of VRAM memory\n");
+ }
+
+ if (dev_priv->vram_mtrr)
+ mtrr_del(dev_priv->vram_mtrr, dev_priv->fb_aper_offset, dev_priv->aper_size);
+ mutex_unlock(&dev->struct_mutex);
+
+ drm_bo_driver_finish(dev);
+ dev_priv->mm_enabled = false;
+}
+
+int radeon_gem_object_pin(struct drm_gem_object *obj,
+ uint32_t alignment, uint32_t pin_domain)
+{
+ struct drm_radeon_gem_object *obj_priv;
+ int ret;
+ uint32_t flags = DRM_BO_FLAG_NO_EVICT;
+ uint32_t mask = DRM_BO_FLAG_NO_EVICT;
+
+ obj_priv = obj->driver_private;
+
+ if (pin_domain) {
+ mask |= DRM_BO_MASK_MEM;
+ if (pin_domain == RADEON_GEM_DOMAIN_GTT)
+ flags |= DRM_BO_FLAG_MEM_TT;
+ else if (pin_domain == RADEON_GEM_DOMAIN_VRAM)
+ flags |= DRM_BO_FLAG_MEM_VRAM;
+ else
+ return -EINVAL;
+ }
+ ret = drm_bo_do_validate(obj_priv->bo, flags, mask,
+ DRM_BO_HINT_DONT_FENCE, 0);
+
+ return ret;
+}
+
+int radeon_gem_object_unpin(struct drm_gem_object *obj)
+{
+ struct drm_radeon_gem_object *obj_priv;
+ int ret;
+
+ obj_priv = obj->driver_private;
+
+ ret = drm_bo_do_validate(obj_priv->bo, 0, DRM_BO_FLAG_NO_EVICT,
+ DRM_BO_HINT_DONT_FENCE, 0);
+
+ return ret;
+}
+
+#define RADEON_IB_MEMORY (1*1024*1024)
+#define RADEON_IB_SIZE (65536)
+
+#define RADEON_NUM_IB (RADEON_IB_MEMORY / RADEON_IB_SIZE)
+
+int radeon_gem_ib_get(struct drm_radeon_cs_parser *parser)
+{
+ int i, index = -1;
+ int ret;
+ drm_radeon_private_t *dev_priv = parser->dev->dev_private;
+
+ for (i = 0; i < RADEON_NUM_IB; i++) {
+ if (!(dev_priv->ib_alloc_bitmap & (1 << i))){
+ index = i;
+ break;
+ }
+ }
+
+ /* if all in use we need to wait */
+ if (index == -1) {
+ for (i = 0; i < RADEON_NUM_IB; i++) {
+ if (dev_priv->ib_alloc_bitmap & (1 << i)) {
+ mutex_lock(&dev_priv->ib_objs[i]->bo->mutex);
+ ret = drm_bo_wait(dev_priv->ib_objs[i]->bo, 0, 1, 0, 0);
+ mutex_unlock(&dev_priv->ib_objs[i]->bo->mutex);
+ if (ret)
+ continue;
+ dev_priv->ib_alloc_bitmap &= ~(1 << i);
+ index = i;
+ break;
+ }
+ }
+ }
+
+ if (index == -1) {
+ DRM_ERROR("Major case fail to allocate IB from freelist %llx\n", dev_priv->ib_alloc_bitmap);
+ return -EINVAL;
+ }
+
+
+ if (parser->chunks[parser->ib_index].length_dw > RADEON_IB_SIZE / sizeof(uint32_t))
+ return -EINVAL;
+
+ ret = drm_bo_do_validate(dev_priv->ib_objs[index]->bo, 0,
+ DRM_BO_FLAG_NO_EVICT,
+ 0, 0);
+ if (ret) {
+ DRM_ERROR("Failed to validate IB %d\n", index);
+ return -EINVAL;
+ }
+
+ parser->ib = dev_priv->ib_objs[index]->kmap.virtual;
+ parser->card_offset = dev_priv->gart_vm_start + dev_priv->ib_objs[index]->bo->offset;
+ dev_priv->ib_alloc_bitmap |= (1 << i);
+ return 0;
+}
+
+static void radeon_gem_ib_free(struct drm_radeon_cs_parser *parser)
+{
+ struct drm_device *dev = parser->dev;
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+ struct drm_fence_object *fence;
+ int ret;
+ int i;
+
+ for (i = 0; i < RADEON_NUM_IB; i++) {
+ if (dev_priv->ib_objs[i]->kmap.virtual == parser->ib) {
+ /* emit a fence object */
+ ret = drm_fence_buffer_objects(dev, NULL, 0, NULL, &fence);
+ dev_priv->irq_emitted = 0;
+ if (ret) {
+ drm_putback_buffer_objects(dev);
+ }
+ /* dereference the fence object */
+ if (fence)
+ drm_fence_usage_deref_unlocked(&fence);
+ }
+ }
+
+}
+
+static int radeon_gem_ib_destroy(struct drm_device *dev)
+{
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+ int i;
+
+ if (dev_priv->ib_objs) {
+ for (i = 0; i < RADEON_NUM_IB; i++) {
+ if (dev_priv->ib_objs[i]) {
+ drm_bo_kunmap(&dev_priv->ib_objs[i]->kmap);
+ drm_bo_usage_deref_unlocked(&dev_priv->ib_objs[i]->bo);
+ }
+ drm_free(dev_priv->ib_objs[i], sizeof(struct radeon_mm_obj), DRM_MEM_DRIVER);
+ }
+ drm_free(dev_priv->ib_objs, RADEON_NUM_IB*sizeof(struct radeon_mm_obj *), DRM_MEM_DRIVER);
+ }
+ dev_priv->ib_objs = NULL;
+ return 0;
+}
+
+static int radeon_gem_find_reloc(struct drm_radeon_cs_parser *parser,
+ uint32_t offset, uint32_t *handle,
+ uint32_t *read_domains, uint32_t *write_domain)
+{
+ struct drm_device *dev = parser->dev;
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+ struct drm_radeon_kernel_chunk *reloc_chunk = &parser->chunks[parser->reloc_index];
+
+ if (!reloc_chunk->kdata)
+ return -EINVAL;
+
+ if (offset > reloc_chunk->length_dw){
+ DRM_ERROR("Offset larger than chunk %d %d\n", offset, reloc_chunk->length_dw);
+ return -EINVAL;
+ }
+
+ *handle = reloc_chunk->kdata[offset];
+ *read_domains = reloc_chunk->kdata[offset + 3];
+ *write_domain = reloc_chunk->kdata[offset + 4];
+ return 0;
+}
+
+static int radeon_gem_relocate(struct drm_radeon_cs_parser *parser,
+ uint32_t *reloc, uint32_t *offset)
+{
+ struct drm_device *dev = parser->dev;
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+ /* relocate the handle */
+ uint32_t read_domains, write_domain;
+ struct drm_gem_object *obj;
+ int flags = 0;
+ int ret;
+ struct drm_radeon_gem_object *obj_priv;
+
+ if (parser->reloc_index == -1) {
+ obj = drm_gem_object_lookup(dev, parser->file_priv, reloc[1]);
+ if (!obj)
+ return -EINVAL;
+ read_domains = reloc[2];
+ write_domain = reloc[3];
+ } else {
+ uint32_t handle;
+
+ /* have to lookup handle in other chunk */
+ ret = radeon_gem_find_reloc(parser, reloc[1], &handle, &read_domains, &write_domain);
+ if (ret < 0)
+ return ret;
+
+ obj = drm_gem_object_lookup(dev, parser->file_priv, handle);
+ if (!obj)
+ return -EINVAL;
+ }
+
+ obj_priv = obj->driver_private;
+ radeon_gem_set_domain(obj, read_domains, write_domain, &flags, false);
+
+ obj_priv->bo->mem.flags &= ~DRM_BO_FLAG_CLEAN;
+ obj_priv->bo->mem.proposed_flags &= ~DRM_BO_FLAG_CLEAN;
+
+ if (flags == DRM_BO_FLAG_MEM_VRAM)
+ *offset = obj_priv->bo->offset + dev_priv->fb_location;
+ else if (flags == DRM_BO_FLAG_MEM_TT)
+ *offset = obj_priv->bo->offset + dev_priv->gart_vm_start;
+
+ /* BAD BAD BAD - LINKED LIST THE OBJS and UNREF ONCE IB is SUBMITTED */
+ drm_gem_object_unreference(obj);
+ return 0;
+}
+
+/* allocate 1MB of 64k IBs the the kernel can keep mapped */
+static int radeon_gem_ib_init(struct drm_device *dev)
+{
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+ int i;
+ int ret;
+
+ dev_priv->ib_objs = drm_calloc(RADEON_NUM_IB, sizeof(struct radeon_mm_obj *), DRM_MEM_DRIVER);
+ if (!dev_priv->ib_objs)
+ goto free_all;
+
+ for (i = 0; i < RADEON_NUM_IB; i++) {
+ dev_priv->ib_objs[i] = drm_calloc(1, sizeof(struct radeon_mm_obj), DRM_MEM_DRIVER);
+ if (!dev_priv->ib_objs[i])
+ goto free_all;
+
+ ret = drm_buffer_object_create(dev, RADEON_IB_SIZE,
+ drm_bo_type_kernel,
+ DRM_BO_FLAG_READ | DRM_BO_FLAG_MEM_TT |
+ DRM_BO_FLAG_MAPPABLE, 0,
+ 0, 0, &dev_priv->ib_objs[i]->bo);
+ if (ret)
+ goto free_all;
+
+ ret = drm_bo_kmap(dev_priv->ib_objs[i]->bo, 0, RADEON_IB_SIZE >> PAGE_SHIFT,
+ &dev_priv->ib_objs[i]->kmap);
+
+ if (ret)
+ goto free_all;
+ }
+
+ dev_priv->mm.gart_useable -= RADEON_IB_SIZE * RADEON_NUM_IB;
+ dev_priv->ib_alloc_bitmap = 0;
+
+ dev_priv->cs.ib_get = radeon_gem_ib_get;
+ dev_priv->cs.ib_free = radeon_gem_ib_free;
+
+ radeon_cs_init(dev);
+ dev_priv->cs.relocate = radeon_gem_relocate;
+ return 0;
+
+free_all:
+ radeon_gem_ib_destroy(dev);
+ return -ENOMEM;
+}
+
+#define RADEON_DMA_BUFFER_SIZE (64 * 1024)
+#define RADEON_DMA_BUFFER_COUNT (16)
+
+
+/**
+ * Cleanup after an error on one of the addbufs() functions.
+ *
+ * \param dev DRM device.
+ * \param entry buffer entry where the error occurred.
+ *
+ * Frees any pages and buffers associated with the given entry.
+ */
+static void drm_cleanup_buf_error(struct drm_device * dev,
+ struct drm_buf_entry * entry)
+{
+ int i;
+
+ if (entry->seg_count) {
+ for (i = 0; i < entry->seg_count; i++) {
+ if (entry->seglist[i]) {
+ drm_pci_free(dev, entry->seglist[i]);
+ }
+ }
+ drm_free(entry->seglist,
+ entry->seg_count *
+ sizeof(*entry->seglist), DRM_MEM_SEGS);
+
+ entry->seg_count = 0;
+ }
+
+ if (entry->buf_count) {
+ for (i = 0; i < entry->buf_count; i++) {
+ if (entry->buflist[i].dev_private) {
+ drm_free(entry->buflist[i].dev_private,
+ entry->buflist[i].dev_priv_size,
+ DRM_MEM_BUFS);
+ }
+ }
+ drm_free(entry->buflist,
+ entry->buf_count *
+ sizeof(*entry->buflist), DRM_MEM_BUFS);
+
+ entry->buf_count = 0;
+ }
+}
+
+static int radeon_gem_addbufs(struct drm_device *dev)
+{
+ struct drm_radeon_private *dev_priv = dev->dev_private;
+ struct drm_device_dma *dma = dev->dma;
+ struct drm_buf_entry *entry;
+ struct drm_buf *buf;
+ unsigned long offset;
+ unsigned long agp_offset;
+ int count;
+ int order;
+ int size;
+ int alignment;
+ int page_order;
+ int total;
+ int byte_count;
+ int i;
+ struct drm_buf **temp_buflist;
+
+ if (!dma)
+ return -EINVAL;
+
+ count = RADEON_DMA_BUFFER_COUNT;
+ order = drm_order(RADEON_DMA_BUFFER_SIZE);
+ size = 1 << order;
+
+ alignment = PAGE_ALIGN(size);
+ page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+ total = PAGE_SIZE << page_order;
+
+ byte_count = 0;
+ agp_offset = dev_priv->mm.dma_bufs.bo->offset;
+
+ DRM_DEBUG("count: %d\n", count);
+ DRM_DEBUG("order: %d\n", order);
+ DRM_DEBUG("size: %d\n", size);
+ DRM_DEBUG("agp_offset: %lu\n", agp_offset);
+ DRM_DEBUG("alignment: %d\n", alignment);
+ DRM_DEBUG("page_order: %d\n", page_order);
+ DRM_DEBUG("total: %d\n", total);
+
+ if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
+ return -EINVAL;
+ if (dev->queue_count)
+ return -EBUSY; /* Not while in use */
+
+ spin_lock(&dev->count_lock);
+ if (dev->buf_use) {
+ spin_unlock(&dev->count_lock);
+ return -EBUSY;
+ }
+ atomic_inc(&dev->buf_alloc);
+ spin_unlock(&dev->count_lock);
+
+ mutex_lock(&dev->struct_mutex);
+ entry = &dma->bufs[order];
+ if (entry->buf_count) {
+ mutex_unlock(&dev->struct_mutex);
+ atomic_dec(&dev->buf_alloc);
+ return -ENOMEM; /* May only call once for each order */
+ }
+
+ if (count < 0 || count > 4096) {
+ mutex_unlock(&dev->struct_mutex);
+ atomic_dec(&dev->buf_alloc);
+ return -EINVAL;
+ }
+
+ entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+ DRM_MEM_BUFS);
+ if (!entry->buflist) {
+ mutex_unlock(&dev->struct_mutex);
+ atomic_dec(&dev->buf_alloc);
+ return -ENOMEM;
+ }
+ memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+
+ entry->buf_size = size;
+ entry->page_order = page_order;
+
+ offset = 0;
+
+ while (entry->buf_count < count) {
+ buf = &entry->buflist[entry->buf_count];
+ buf->idx = dma->buf_count + entry->buf_count;
+ buf->total = alignment;
+ buf->order = order;
+ buf->used = 0;
+
+ buf->offset = (dma->byte_count + offset);
+ buf->bus_address = dev_priv->gart_vm_start + agp_offset + offset;
+ buf->address = (void *)(agp_offset + offset);
+ buf->next = NULL;
+ buf->waiting = 0;
+ buf->pending = 0;
+ init_waitqueue_head(&buf->dma_wait);
+ buf->file_priv = NULL;
+
+ buf->dev_priv_size = dev->driver->dev_priv_size;
+ buf->dev_private = drm_alloc(buf->dev_priv_size, DRM_MEM_BUFS);
+ if (!buf->dev_private) {
+ /* Set count correctly so we free the proper amount. */
+ entry->buf_count = count;
+ drm_cleanup_buf_error(dev, entry);
+ mutex_unlock(&dev->struct_mutex);
+ atomic_dec(&dev->buf_alloc);
+ return -ENOMEM;
+ }
+
+ memset(buf->dev_private, 0, buf->dev_priv_size);
+
+ DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address);
+
+ offset += alignment;
+ entry->buf_count++;
+ byte_count += PAGE_SIZE << page_order;
+ }
+
+ DRM_DEBUG("byte_count: %d\n", byte_count);
+
+ temp_buflist = drm_realloc(dma->buflist,
+ dma->buf_count * sizeof(*dma->buflist),
+ (dma->buf_count + entry->buf_count)
+ * sizeof(*dma->buflist), DRM_MEM_BUFS);
+ if (!temp_buflist) {
+ /* Free the entry because it isn't valid */
+ drm_cleanup_buf_error(dev, entry);
+ mutex_unlock(&dev->struct_mutex);
+ atomic_dec(&dev->buf_alloc);
+ return -ENOMEM;
+ }
+ dma->buflist = temp_buflist;
+
+ for (i = 0; i < entry->buf_count; i++) {
+ dma->buflist[i + dma->buf_count] = &entry->buflist[i];
+ }
+
+ dma->buf_count += entry->buf_count;
+ dma->seg_count += entry->seg_count;
+ dma->page_count += byte_count >> PAGE_SHIFT;
+ dma->byte_count += byte_count;
+
+ DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
+ DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
+
+ mutex_unlock(&dev->struct_mutex);
+
+ dma->flags = _DRM_DMA_USE_SG;
+ atomic_dec(&dev->buf_alloc);
+ return 0;
+}
+
+static int radeon_gem_dma_bufs_init(struct drm_device *dev)
+{
+ struct drm_radeon_private *dev_priv = dev->dev_private;
+ int size = RADEON_DMA_BUFFER_SIZE * RADEON_DMA_BUFFER_COUNT;
+ int ret;
+
+ ret = drm_dma_setup(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = drm_buffer_object_create(dev, size, drm_bo_type_kernel,
+ DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE | DRM_BO_FLAG_NO_EVICT |
+ DRM_BO_FLAG_MEM_TT | DRM_BO_FLAG_MAPPABLE, 0,
+ 0, 0, &dev_priv->mm.dma_bufs.bo);
+ if (ret) {
+ DRM_ERROR("Failed to create DMA bufs\n");
+ return -ENOMEM;
+ }
+
+ ret = drm_bo_kmap(dev_priv->mm.dma_bufs.bo, 0, size >> PAGE_SHIFT,
+ &dev_priv->mm.dma_bufs.kmap);
+ if (ret) {
+ DRM_ERROR("Failed to mmap DMA buffers\n");
+ return -ENOMEM;
+ }
+ dev_priv->mm.gart_useable -= size;
+ DRM_DEBUG("\n");
+ radeon_gem_addbufs(dev);
+
+ DRM_DEBUG("%lx %d\n", dev_priv->mm.dma_bufs.bo->map_list.hash.key, size);
+ dev->agp_buffer_token = dev_priv->mm.dma_bufs.bo->map_list.hash.key << PAGE_SHIFT;
+ dev_priv->mm.fake_agp_map.handle = dev_priv->mm.dma_bufs.kmap.virtual;
+ dev_priv->mm.fake_agp_map.size = size;
+
+ dev->agp_buffer_map = &dev_priv->mm.fake_agp_map;
+ dev_priv->gart_buffers_offset = dev_priv->mm.dma_bufs.bo->offset + dev_priv->gart_vm_start;
+ return 0;
+}
+
+static void radeon_gem_dma_bufs_destroy(struct drm_device *dev)
+{
+
+ struct drm_radeon_private *dev_priv = dev->dev_private;
+ drm_dma_takedown(dev);
+
+ if (dev_priv->mm.dma_bufs.bo) {
+ drm_bo_kunmap(&dev_priv->mm.dma_bufs.kmap);
+ drm_bo_usage_deref_unlocked(&dev_priv->mm.dma_bufs.bo);
+ }
+}
+
+
+static struct drm_gem_object *gem_object_get(struct drm_device *dev, uint32_t name)
+{
+ struct drm_gem_object *obj;
+
+ spin_lock(&dev->object_name_lock);
+ obj = idr_find(&dev->object_name_idr, name);
+ if (obj)
+ drm_gem_object_reference(obj);
+ spin_unlock(&dev->object_name_lock);
+ return obj;
+}
+
+void radeon_gem_update_offsets(struct drm_device *dev, struct drm_master *master)
+{
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+ struct drm_radeon_master_private *master_priv = master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv;
+ struct drm_gem_object *obj;
+ struct drm_radeon_gem_object *obj_priv;
+
+ /* update front_pitch_offset and back_pitch_offset */
+ obj = gem_object_get(dev, sarea_priv->front_handle);
+ if (obj) {
+ obj_priv = obj->driver_private;
+
+ dev_priv->front_offset = obj_priv->bo->offset;
+ dev_priv->front_pitch_offset = (((sarea_priv->front_pitch / 64) << 22) |
+ ((obj_priv->bo->offset
+ + dev_priv->fb_location) >> 10));
+ drm_gem_object_unreference(obj);
+ }
+
+ obj = gem_object_get(dev, sarea_priv->back_handle);
+ if (obj) {
+ obj_priv = obj->driver_private;
+ dev_priv->back_offset = obj_priv->bo->offset;
+ dev_priv->back_pitch_offset = (((sarea_priv->back_pitch / 64) << 22) |
+ ((obj_priv->bo->offset
+ + dev_priv->fb_location) >> 10));
+ drm_gem_object_unreference(obj);
+ }
+ dev_priv->color_fmt = RADEON_COLOR_FORMAT_ARGB8888;
+
+}
+
+