/************************************************************************** * * Copyright 2006 Tungsten Graphics, Inc., Cedar Park, Texas. * All Rights Reserved. * * 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, sub license, 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 NON-INFRINGEMENT. * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS 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. * **************************************************************************/ /* Originally a fake version of the buffer manager so that we can * prototype the changes in a driver fairly quickly, has been fleshed * out to a fully functional interim solution. * * Basically wraps the old style memory management in the new * programming interface, but is more expressive and avoids many of * the bugs in the old texture manager. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "intel_bufmgr.h" #include "intel_bufmgr_priv.h" #include "drm.h" #include "i915_drm.h" #include "mm.h" #include "libdrm_lists.h" #define ALIGN(value, alignment) ((value + alignment - 1) & ~(alignment - 1)) #define DBG(...) do { \ if (bufmgr_fake->bufmgr.debug) \ drmMsg(__VA_ARGS__); \ } while (0) /* Internal flags: */ #define BM_NO_BACKING_STORE 0x00000001 #define BM_NO_FENCE_SUBDATA 0x00000002 #define BM_PINNED 0x00000004 /* Wrapper around mm.c's mem_block, which understands that you must * wait for fences to expire before memory can be freed. This is * specific to our use of memcpy for uploads - an upload that was * processed through the command queue wouldn't need to care about * fences. */ #define MAX_RELOCS 4096 struct fake_buffer_reloc { /** Buffer object that the relocation points at. */ drm_intel_bo *target_buf; /** Offset of the relocation entry within reloc_buf. */ uint32_t offset; /** Cached value of the offset when we last performed this relocation. */ uint32_t last_target_offset; /** Value added to target_buf's offset to get the relocation entry. */ uint32_t delta; /** Cache domains the target buffer is read into. */ uint32_t read_domains; /** Cache domain the target buffer will have dirty cachelines in. */ uint32_t write_domain; }; struct block { struct block *next, *prev; struct mem_block *mem; /* BM_MEM_AGP */ /** * Marks that the block is currently in the aperture and has yet to be * fenced. */ unsigned on_hardware:1; /** * Marks that the block is currently fenced (being used by rendering) and * can't be freed until @fence is passed. */ unsigned fenced:1; /** Fence cookie for the block. */ unsigned fence; /* Split to read_fence, write_fence */ drm_intel_bo *bo; void *virtual; }; typedef struct _bufmgr_fake { drm_intel_bufmgr bufmgr; pthread_mutex_t lock; unsigned long low_offset; unsigned long size; void *virtual; struct mem_block *heap; unsigned buf_nr; /* for generating ids */ /** * List of blocks which are currently in the GART but haven't been * fenced yet. */ struct block on_hardware; /** * List of blocks which are in the GART and have an active fence on them. */ struct block fenced; /** * List of blocks which have an expired fence and are ready to be evicted. */ struct block lru; unsigned int last_fence; unsigned fail:1; unsigned need_fence:1; int thrashing; /** * Driver callback to emit a fence, returning the cookie. * * This allows the driver to hook in a replacement for the DRM usage in * bufmgr_fake. * * Currently, this also requires that a write flush be emitted before * emitting the fence, but this should change. */ unsigned int (*fence_emit)(void *private); /** Driver callback to wait for a fence cookie to have passed. */ void (*fence_wait)(unsigned int fence, void *private); void *fence_priv; /** * Driver callback to execute a buffer. * * This allows the driver to hook in a replacement for the DRM usage in * bufmgr_fake. */ int (*exec)(drm_intel_bo *bo, unsigned int used, void *priv); void *exec_priv; /** Driver-supplied argument to driver callbacks */ void *driver_priv; /* Pointer to kernel-updated sarea data for the last completed user irq */ volatile int *last_dispatch; int fd; int debug; int performed_rendering; } drm_intel_bufmgr_fake; typedef struct _drm_intel_bo_fake { drm_intel_bo bo; unsigned id; /* debug only */ const char *name; unsigned dirty:1; /** has the card written to this buffer - we make need to copy it back */ unsigned card_dirty:1; unsigned int refcount; /* Flags may consist of any of the DRM_BO flags, plus * DRM_BO_NO_BACKING_STORE and BM_NO_FENCE_SUBDATA, which are the first two * driver private flags. */ uint64_t flags; /** Cache domains the target buffer is read into. */ uint32_t read_domains; /** Cache domain the target buffer will have dirty cachelines in. */ uint32_t write_domain; unsigned int alignment; int is_static, validated; unsigned int map_count; /** relocation list */ struct fake_buffer_reloc *relocs; int nr_relocs; /** * Total size of the target_bos of this buffer. * * Used for estimation in check_aperture. */ unsigned int child_size; struct block *block; void *backing_store; void (*invalidate_cb)(drm_intel_bo *bo, void *ptr); void *invalidate_ptr; } drm_intel_bo_fake; static int clear_fenced(drm_intel_bufmgr_fake *bufmgr_fake, unsigned int fence_cookie); #define MAXFENCE 0x7fffffff static int FENCE_LTE( unsigned a, unsigned b ) { if (a == b) return 1; if (a < b && b - a < (1<<24)) return 1; if (a > b && MAXFENCE - a + b < (1<<24)) return 1; return 0; } void drm_intel_bufmgr_fake_set_fence_callback(drm_intel_bufmgr *bufmgr, unsigned int (*emit)(void *priv), void (*wait)(unsigned int fence, void *priv), void *priv) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bufmgr; bufmgr_fake->fence_emit = emit; bufmgr_fake->fence_wait = wait; bufmgr_fake->fence_priv = priv; } static unsigned int _fence_emit_internal(drm_intel_bufmgr_fake *bufmgr_fake) { struct drm_i915_irq_emit ie; int ret, seq = 1; if (bufmgr_fake->fence_emit != NULL) { seq = bufmgr_fake->fence_emit(bufmgr_fake->fence_priv); return seq; } ie.irq_seq = &seq; ret = drmCommandWriteRead(bufmgr_fake->fd, DRM_I915_IRQ_EMIT, &ie, sizeof(ie)); if (ret) { drmMsg("%s: drm_i915_irq_emit: %d\n", __FUNCTION__, ret); abort(); } DBG("emit 0x%08x\n", seq); return seq; } static void _fence_wait_internal(drm_intel_bufmgr_fake *bufmgr_fake, int seq) { struct drm_i915_irq_wait iw; int hw_seq, busy_count = 0; int ret; int kernel_lied; if (bufmgr_fake->fence_wait != NULL) { bufmgr_fake->fence_wait(seq, bufmgr_fake->fence_priv); clear_fenced(bufmgr_fake, seq); return; } DBG("wait 0x%08x\n", iw.irq_seq); iw.irq_seq = seq; /* The kernel IRQ_WAIT implementation is all sorts of broken. * 1) It returns 1 to 0x7fffffff instead of using the full 32-bit unsigned * range. * 2) It returns 0 if hw_seq >= seq, not seq - hw_seq < 0 on the 32-bit * signed range. * 3) It waits if seq < hw_seq, not seq - hw_seq > 0 on the 32-bit * signed range. * 4) It returns -EBUSY in 3 seconds even if the hardware is still * successfully chewing through buffers. * * Assume that in userland we treat sequence numbers as ints, which makes * some of the comparisons convenient, since the sequence numbers are * all postive signed integers. * * From this we get several cases we need to handle. Here's a timeline. * 0x2 0x7 0x7ffffff8 0x7ffffffd * | | | | * ------------------------------------------------------------------- * * A) Normal wait for hw to catch up * hw_seq seq * | | * ------------------------------------------------------------------- * seq - hw_seq = 5. If we call IRQ_WAIT, it will wait for hw to catch up. * * B) Normal wait for a sequence number that's already passed. * seq hw_seq * | | * ------------------------------------------------------------------- * seq - hw_seq = -5. If we call IRQ_WAIT, it returns 0 quickly. * * C) Hardware has already wrapped around ahead of us * hw_seq seq * | | * ------------------------------------------------------------------- * seq - hw_seq = 0x80000000 - 5. If we called IRQ_WAIT, it would wait * for hw_seq >= seq, which may never occur. Thus, we want to catch this * in userland and return 0. * * D) We've wrapped around ahead of the hardware. * seq hw_seq * | | * ------------------------------------------------------------------- * seq - hw_seq = -(0x80000000 - 5). If we called IRQ_WAIT, it would return * 0 quickly because hw_seq >= seq, even though the hardware isn't caught up. * Thus, we need to catch this early return in userland and bother the * kernel until the hardware really does catch up. * * E) Hardware might wrap after we test in userland. * hw_seq seq * | | * ------------------------------------------------------------------- * seq - hw_seq = 5. If we call IRQ_WAIT, it will likely see seq >= hw_seq * and wait. However, suppose hw_seq wraps before we make it into the * kernel. The kernel sees hw_seq >= seq and waits for 3 seconds then * returns -EBUSY. This is case C). We should catch this and then return * successfully. * * F) Hardware might take a long time on a buffer. * hw_seq seq * | | * ------------------------------------------------------------------- * seq - hw_seq = 5. If we call IRQ_WAIT, if sequence 2 through 5 take too * long, it will return -EBUSY. Batchbuffers in the gltestperf demo were * seen to take up to 7 seconds. We should catch early -EBUSY return * and keep trying. */ do { /* Keep a copy of last_dispatch so that if the wait -EBUSYs because the * hardware didn't catch up in 3 seconds, we can see if it at least made * progress and retry. */ hw_seq = *bufmgr_fake->last_dispatch; /* Catch case C */ if (seq - hw_seq > 0x40000000) return; ret = drmCommandWrite(bufmgr_fake->fd, DRM_I915_IRQ_WAIT, &iw, sizeof(iw)); /* Catch case D */ kernel_lied = (ret == 0) && (seq - *bufmgr_fake->last_dispatch < -0x40000000); /* Catch case E */ if (ret == -EBUSY && (seq - *bufmgr_fake->last_dispatch > 0x40000000)) ret = 0; /* Catch case F: Allow up to 15 seconds chewing on one buffer. */ if ((ret == -EBUSY) && (hw_seq != *bufmgr_fake->last_dispatch)) busy_count = 0; else busy_count++; } while (kernel_lied || ret == -EAGAIN || ret == -EINTR || (ret == -EBUSY && busy_count < 5)); if (ret != 0) { drmMsg("%s:%d: Error waiting for fence: %s.\n", __FILE__, __LINE__, strerror(-ret)); abort(); } clear_fenced(bufmgr_fake, seq); } static int _fence_test(drm_intel_bufmgr_fake *bufmgr_fake, unsigned fence) { /* Slight problem with wrap-around: */ return fence == 0 || FENCE_LTE(fence, bufmgr_fake->last_fence); } /** * Allocate a memory manager block for the buffer. */ static int alloc_block(drm_intel_bo *bo) { drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo; drm_intel_bufmgr_fake *bufmgr_fake= (drm_intel_bufmgr_fake *)bo->bufmgr; struct block *block = (struct block *)calloc(sizeof *block, 1); unsigned int align_log2 = ffs(bo_fake->alignment) - 1; unsigned int sz; if (!block) return 1; sz = (bo->size + bo_fake->alignment - 1) & ~(bo_fake->alignment - 1); block->mem = mmAllocMem(bufmgr_fake->heap, sz, align_log2, 0); if (!block->mem) { free(block); return 0; } DRMINITLISTHEAD(block); /* Insert at head or at tail??? */ DRMLISTADDTAIL(block, &bufmgr_fake->lru); block->virtual = (uint8_t *)bufmgr_fake->virtual + block->mem->ofs - bufmgr_fake->low_offset; block->bo = bo; bo_fake->block = block; return 1; } /* Release the card storage associated with buf: */ static void free_block(drm_intel_bufmgr_fake *bufmgr_fake, struct block *block, int skip_dirty_copy) { drm_intel_bo_fake *bo_fake; DBG("free block %p %08x %d %d\n", block, block->mem->ofs, block->on_hardware, block->fenced); if (!block) return; bo_fake = (drm_intel_bo_fake *)block->bo; if (bo_fake->flags & (BM_PINNED | BM_NO_BACKING_STORE)) skip_dirty_copy = 1; if (!skip_dirty_copy && (bo_fake->card_dirty == 1)) { memcpy(bo_fake->backing_store, block->virtual, block->bo->size); bo_fake->card_dirty = 0; bo_fake->dirty = 1; } if (block->on_hardware) { block->bo = NULL; } else if (block->fenced) { block->bo = NULL; } else { DBG(" - free immediately\n"); DRMLISTDEL(block); mmFreeMem(block->mem); free(block); } } static void alloc_backing_store(drm_intel_bo *bo) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr; drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo; assert(!bo_fake->backing_store); assert(!(bo_fake->flags & (BM_PINNED|BM_NO_BACKING_STORE))); bo_fake->backing_store = malloc(bo->size); DBG("alloc_backing - buf %d %p %d\n", bo_fake->id, bo_fake->backing_store, bo->size); assert(bo_fake->backing_store); } static void free_backing_store(drm_intel_bo *bo) { drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo; if (bo_fake->backing_store) { assert(!(bo_fake->flags & (BM_PINNED|BM_NO_BACKING_STORE))); free(bo_fake->backing_store); bo_fake->backing_store = NULL; } } static void set_dirty(drm_intel_bo *bo) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr; drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo; if (bo_fake->flags & BM_NO_BACKING_STORE && bo_fake->invalidate_cb != NULL) bo_fake->invalidate_cb(bo, bo_fake->invalidate_ptr); assert(!(bo_fake->flags & BM_PINNED)); DBG("set_dirty - buf %d\n", bo_fake->id); bo_fake->dirty = 1; } static int evict_lru(drm_intel_bufmgr_fake *bufmgr_fake, unsigned int max_fence) { struct block *block, *tmp; DBG("%s\n", __FUNCTION__); DRMLISTFOREACHSAFE(block, tmp, &bufmgr_fake->lru) { drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)block->bo; if (bo_fake != NULL && (bo_fake->flags & BM_NO_FENCE_SUBDATA)) continue; if (block->fence && max_fence && !FENCE_LTE(block->fence, max_fence)) return 0; set_dirty(&bo_fake->bo); bo_fake->block = NULL; free_block(bufmgr_fake, block, 0); return 1; } return 0; } static int evict_mru(drm_intel_bufmgr_fake *bufmgr_fake) { struct block *block, *tmp; DBG("%s\n", __FUNCTION__); DRMLISTFOREACHSAFEREVERSE(block, tmp, &bufmgr_fake->lru) { drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)block->bo; if (bo_fake && (bo_fake->flags & BM_NO_FENCE_SUBDATA)) continue; set_dirty(&bo_fake->bo); bo_fake->block = NULL; free_block(bufmgr_fake, block, 0); return 1; } return 0; } /** * Removes all objects from the fenced list older than the given fence. */ static int clear_fenced(drm_intel_bufmgr_fake *bufmgr_fake, unsigned int fence_cookie) { struct block *block, *tmp; int ret = 0; bufmgr_fake->last_fence = fence_cookie; DRMLISTFOREACHSAFE(block, tmp, &bufmgr_fake->fenced) { assert(block->fenced); if (_fence_test(bufmgr_fake, block->fence)) { block->fenced = 0; if (!block->bo) { DBG("delayed free: offset %x sz %x\n", block->mem->ofs, block->mem->size); DRMLISTDEL(block); mmFreeMem(block->mem); free(block); } else { DBG("return to lru: offset %x sz %x\n", block->mem->ofs, block->mem->size); DRMLISTDEL(block); DRMLISTADDTAIL(block, &bufmgr_fake->lru); } ret = 1; } else { /* Blocks are ordered by fence, so if one fails, all from * here will fail also: */ DBG("fence not passed: offset %x sz %x %d %d \n", block->mem->ofs, block->mem->size, block->fence, bufmgr_fake->last_fence); break; } } DBG("%s: %d\n", __FUNCTION__, ret); return ret; } static void fence_blocks(drm_intel_bufmgr_fake *bufmgr_fake, unsigned fence) { struct block *block, *tmp; DRMLISTFOREACHSAFE(block, tmp, &bufmgr_fake->on_hardware) { DBG("Fence block %p (sz 0x%x ofs %x buf %p) with fence %d\n", block, block->mem->size, block->mem->ofs, block->bo, fence); block->fence = fence; block->on_hardware = 0; block->fenced = 1; /* Move to tail of pending list here */ DRMLISTDEL(block); DRMLISTADDTAIL(block, &bufmgr_fake->fenced); } assert(DRMLISTEMPTY(&bufmgr_fake->on_hardware)); } static int evict_and_alloc_block(drm_intel_bo *bo) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr; drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo; assert(bo_fake->block == NULL); /* Search for already free memory: */ if (alloc_block(bo)) return 1; /* If we're not thrashing, allow lru eviction to dig deeper into * recently used textures. We'll probably be thrashing soon: */ if (!bufmgr_fake->thrashing) { while (evict_lru(bufmgr_fake, 0)) if (alloc_block(bo)) return 1; } /* Keep thrashing counter alive? */ if (bufmgr_fake->thrashing) bufmgr_fake->thrashing = 20; /* Wait on any already pending fences - here we are waiting for any * freed memory that has been submitted to hardware and fenced to * become available: */ while (!DRMLISTEMPTY(&bufmgr_fake->fenced)) { uint32_t fence = bufmgr_fake->fenced.next->fence; _fence_wait_internal(bufmgr_fake, fence); if (alloc_block(bo)) return 1; } if (!DRMLISTEMPTY(&bufmgr_fake->on_hardware)) { while (!DRMLISTEMPTY(&bufmgr_fake->fenced)) { uint32_t fence = bufmgr_fake->fenced.next->fence; _fence_wait_internal(bufmgr_fake, fence); } if (!bufmgr_fake->thrashing) { DBG("thrashing\n"); } bufmgr_fake->thrashing = 20; if (alloc_block(bo)) return 1; } while (evict_mru(bufmgr_fake)) if (alloc_block(bo)) return 1; DBG("%s 0x%x bytes failed\n", __FUNCTION__, bo->size); return 0; } /*********************************************************************** * Public functions */ /** * Wait for hardware idle by emitting a fence and waiting for it. */ static void drm_intel_bufmgr_fake_wait_idle(drm_intel_bufmgr_fake *bufmgr_fake) { unsigned int cookie; cookie = _fence_emit_internal(bufmgr_fake); _fence_wait_internal(bufmgr_fake, cookie); } /** * Wait for rendering to a buffer to complete. * * It is assumed that the bathcbuffer which performed the rendering included * the necessary flushing. */ static void drm_intel_fake_bo_wait_rendering_locked(drm_intel_bo *bo) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr; drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo; if (bo_fake->block == NULL || !bo_fake->block->fenced) return; _fence_wait_internal(bufmgr_fake, bo_fake->block->fence); } static void drm_intel_fake_bo_wait_rendering(drm_intel_bo *bo) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr; pthread_mutex_lock(&bufmgr_fake->lock); drm_intel_fake_bo_wait_rendering_locked(bo); pthread_mutex_unlock(&bufmgr_fake->lock); } /* Specifically ignore texture memory sharing. * -- just evict everything * -- and wait for idle */ void drm_intel_bufmgr_fake_contended_lock_take(drm_intel_bufmgr *bufmgr) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bufmgr; struct block *block, *tmp; pthread_mutex_lock(&bufmgr_fake->lock); bufmgr_fake->need_fence = 1; bufmgr_fake->fail = 0; /* Wait for hardware idle. We don't know where acceleration has been * happening, so we'll need to wait anyway before letting anything get * put on the card again. */ drm_intel_bufmgr_fake_wait_idle(bufmgr_fake); /* Check that we hadn't released the lock without having fenced the last * set of buffers. */ assert(DRMLISTEMPTY(&bufmgr_fake->fenced)); assert(DRMLISTEMPTY(&bufmgr_fake->on_hardware)); DRMLISTFOREACHSAFE(block, tmp, &bufmgr_fake->lru) { assert(_fence_test(bufmgr_fake, block->fence)); set_dirty(block->bo); } pthread_mutex_unlock(&bufmgr_fake->lock); } static drm_intel_bo * drm_intel_fake_bo_alloc(drm_intel_bufmgr *bufmgr, const char *name, unsigned long size, unsigned int alignment) { drm_intel_bufmgr_fake *bufmgr_fake; drm_intel_bo_fake *bo_fake; bufmgr_fake = (drm_intel_bufmgr_fake *)bufmgr; assert(size != 0); bo_fake = calloc(1, sizeof(*bo_fake)); if (!bo_fake) return NULL; bo_fake->bo.size = size; bo_fake->bo.offset = -1; bo_fake->bo.virtual = NULL; bo_fake->bo.bufmgr = bufmgr; bo_fake->refcount = 1; /* Alignment must be a power of two */ assert((alignment & (alignment - 1)) == 0); if (alignment == 0) alignment = 1; bo_fake->alignment = alignment; bo_fake->id = ++bufmgr_fake->buf_nr; bo_fake->name = name; bo_fake->flags = 0; bo_fake->is_static = 0; DBG("drm_bo_alloc: (buf %d: %s, %d kb)\n", bo_fake->id, bo_fake->name, bo_fake->bo.size / 1024); return &bo_fake->bo; } drm_intel_bo * drm_intel_bo_fake_alloc_static(drm_intel_bufmgr *bufmgr, const char *name, unsigned long offset, unsigned long size, void *virtual) { drm_intel_bufmgr_fake *bufmgr_fake; drm_intel_bo_fake *bo_fake; bufmgr_fake = (drm_intel_bufmgr_fake *)bufmgr; assert(size != 0); bo_fake = calloc(1, sizeof(*bo_fake)); if (!bo_fake) return NULL; bo_fake->bo.size = size; bo_fake->bo.offset = offset; bo_fake->bo.virtual = virtual; bo_fake->bo.bufmgr = bufmgr; bo_fake->refcount = 1; bo_fake->id = ++bufmgr_fake->buf_nr; bo_fake->name = name; bo_fake->flags = BM_PINNED; bo_fake->is_static = 1; DBG("drm_bo_alloc_static: (buf %d: %s, %d kb)\n", bo_fake->id, bo_fake->name, bo_fake->bo.size / 1024); return &bo_fake->bo; } static void drm_intel_fake_bo_reference(drm_intel_bo *bo) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr; drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo; pthread_mutex_lock(&bufmgr_fake->lock); bo_fake->refcount++; pthread_mutex_unlock(&bufmgr_fake->lock); } static void drm_intel_fake_bo_reference_locked(drm_intel_bo *bo) { drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo; bo_fake->refcount++; } static void drm_intel_fake_bo_unreference_locked(drm_intel_bo *bo) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr; drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo; int i; if (--bo_fake->refcount == 0) { assert(bo_fake->map_count == 0); /* No remaining references, so free it */ if (bo_fake->block) free_block(bufmgr_fake, bo_fake->block, 1); free_backing_store(bo); for (i = 0; i < bo_fake->nr_relocs; i++) drm_intel_fake_bo_unreference_locked(bo_fake->relocs[i].target_buf); DBG("drm_bo_unreference: free buf %d %s\n", bo_fake->id, bo_fake->name); free(bo_fake->relocs); free(bo); } } static void drm_intel_fake_bo_unreference(drm_intel_bo *bo) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr; pthread_mutex_lock(&bufmgr_fake->lock); drm_intel_fake_bo_unreference_locked(bo); pthread_mutex_unlock(&bufmgr_fake->lock); } /** * Set the buffer as not requiring backing store, and instead get the callback * invoked whenever it would be set dirty. */ void drm_intel_bo_fake_disable_backing_store(drm_intel_bo *bo, void (*invalidate_cb)(drm_intel_bo *bo, void *ptr), void *ptr) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr; drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo; pthread_mutex_lock(&bufmgr_fake->lock); if (bo_fake->backing_store) free_backing_store(bo); bo_fake->flags |= BM_NO_BACKING_STORE; DBG("disable_backing_store set buf %d dirty\n", bo_fake->id); bo_fake->dirty = 1; bo_fake->invalidate_cb = invalidate_cb; bo_fake->invalidate_ptr = ptr; /* Note that it is invalid right from the start. Also note * invalidate_cb is called with the bufmgr locked, so cannot * itself make bufmgr calls. */ if (invalidate_cb != NULL) invalidate_cb(bo, ptr); pthread_mutex_unlock(&bufmgr_fake->lock); } /** * Map a buffer into bo->virtual, allocating either card memory space (If * BM_NO_BACKING_STORE or BM_PINNED) or backing store, as necessary. */ static int drm_intel_fake_bo_map_locked(drm_intel_bo *bo, int write_enable) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr; drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo; /* Static buffers are always mapped. */ if (bo_fake->is_static) { if (bo_fake->card_dirty) { drm_intel_bufmgr_fake_wait_idle(bufmgr_fake); bo_fake->card_dirty = 0; } return 0; } /* Allow recursive mapping. Mesa may recursively map buffers with * nested display loops, and it is used internally in bufmgr_fake * for relocation. */ if (bo_fake->map_count++ != 0) return 0; { DBG("drm_bo_map: (buf %d: %s, %d kb)\n", bo_fake->id, bo_fake->name, bo_fake->bo.size / 1024); if (bo->virtual != NULL) { drmMsg("%s: already mapped\n", __FUNCTION__); abort(); } else if (bo_fake->flags & (BM_NO_BACKING_STORE|BM_PINNED)) { if (!bo_fake->block && !evict_and_alloc_block(bo)) { DBG("%s: alloc failed\n", __FUNCTION__); bufmgr_fake->fail = 1; return 1; } else { assert(bo_fake->block); bo_fake->dirty = 0; if (!(bo_fake->flags & BM_NO_FENCE_SUBDATA) && bo_fake->block->fenced) { drm_intel_fake_bo_wait_rendering_locked(bo); } bo->virtual = bo_fake->block->virtual; } } else { if (write_enable) set_dirty(bo); if (bo_fake->backing_store == 0) alloc_backing_store(bo); if ((bo_fake->card_dirty == 1) && bo_fake->block) { if (bo_fake->block->fenced) drm_intel_fake_bo_wait_rendering_locked(bo); memcpy(bo_fake->backing_store, bo_fake->block->virtual, bo_fake->block->bo->size); bo_fake->card_dirty = 0; } bo->virtual = bo_fake->backing_store; } } return 0; } static int drm_intel_fake_bo_map(drm_intel_bo *bo, int write_enable) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr; int ret; pthread_mutex_lock(&bufmgr_fake->lock); ret = drm_intel_fake_bo_map_locked(bo, write_enable); pthread_mutex_unlock(&bufmgr_fake->lock); return ret; } static int drm_intel_fake_bo_unmap_locked(drm_intel_bo *bo) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr; drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo; /* Static buffers are always mapped. */ if (bo_fake->is_static) return 0; assert(bo_fake->map_count != 0); if (--bo_fake->map_count != 0) return 0; DBG("drm_bo_unmap: (buf %d: %s, %d kb)\n", bo_fake->id, bo_fake->name, bo_fake->bo.size / 1024); bo->virtual = NULL; return 0; } static int drm_intel_fake_bo_unmap(drm_intel_bo *bo) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr; int ret; pthread_mutex_lock(&bufmgr_fake->lock); ret = drm_intel_fake_bo_unmap_locked(bo); pthread_mutex_unlock(&bufmgr_fake->lock); return ret; } static void drm_intel_fake_kick_all_locked(drm_intel_bufmgr_fake *bufmgr_fake) { struct block *block, *tmp; bufmgr_fake->performed_rendering = 0; /* okay for ever BO that is on the HW kick it off. seriously not afraid of the POLICE right now */ DRMLISTFOREACHSAFE(block, tmp, &bufmgr_fake->on_hardware) { drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)block->bo; block->on_hardware = 0; free_block(bufmgr_fake, block, 0); bo_fake->block = NULL; bo_fake->validated = 0; if (!(bo_fake->flags & BM_NO_BACKING_STORE)) bo_fake->dirty = 1; } } static int drm_intel_fake_bo_validate(drm_intel_bo *bo) { drm_intel_bufmgr_fake *bufmgr_fake; drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo; bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr; DBG("drm_bo_validate: (buf %d: %s, %d kb)\n", bo_fake->id, bo_fake->name, bo_fake->bo.size / 1024); /* Sanity check: Buffers should be unmapped before being validated. * This is not so much of a problem for bufmgr_fake, but TTM refuses, * and the problem is harder to debug there. */ assert(bo_fake->map_count == 0); if (bo_fake->is_static) { /* Add it to the needs-fence list */ bufmgr_fake->need_fence = 1; return 0; } /* Allocate the card memory */ if (!bo_fake->block && !evict_and_alloc_block(bo)) { bufmgr_fake->fail = 1; DBG("Failed to validate buf %d:%s\n", bo_fake->id, bo_fake->name); return -1; } assert(bo_fake->block); assert(bo_fake->block->bo == &bo_fake->bo); bo->offset = bo_fake->block->mem->ofs; /* Upload the buffer contents if necessary */ if (bo_fake->dirty) { DBG("Upload dirty buf %d:%s, sz %d offset 0x%x\n", bo_fake->id, bo_fake->name, bo->size, bo_fake->block->mem->ofs); assert(!(bo_fake->flags & (BM_NO_BACKING_STORE|BM_PINNED))); /* Actually, should be able to just wait for a fence on the memory, * which we would be tracking when we free it. Waiting for idle is * a sufficiently large hammer for now. */ drm_intel_bufmgr_fake_wait_idle(bufmgr_fake); /* we may never have mapped this BO so it might not have any backing * store if this happens it should be rare, but 0 the card memory * in any case */ if (bo_fake->backing_store) memcpy(bo_fake->block->virtual, bo_fake->backing_store, bo->size); else memset(bo_fake->block->virtual, 0, bo->size); bo_fake->dirty = 0; } bo_fake->block->fenced = 0; bo_fake->block->on_hardware = 1; DRMLISTDEL(bo_fake->block); DRMLISTADDTAIL(bo_fake->block, &bufmgr_fake->on_hardware); bo_fake->validated = 1; bufmgr_fake->need_fence = 1; return 0; } static void drm_intel_fake_fence_validated(drm_intel_bufmgr *bufmgr) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bufmgr; unsigned int cookie; cookie = _fence_emit_internal(bufmgr_fake); fence_blocks(bufmgr_fake, cookie); DBG("drm_fence_validated: 0x%08x cookie\n", cookie); } static void drm_intel_fake_destroy(drm_intel_bufmgr *bufmgr) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bufmgr; pthread_mutex_destroy(&bufmgr_fake->lock); mmDestroy(bufmgr_fake->heap); free(bufmgr); } static int drm_intel_fake_emit_reloc(drm_intel_bo *bo, uint32_t offset, drm_intel_bo *target_bo, uint32_t target_offset, uint32_t read_domains, uint32_t write_domain) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr; struct fake_buffer_reloc *r; drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo; drm_intel_bo_fake *target_fake = (drm_intel_bo_fake *)target_bo; int i; pthread_mutex_lock(&bufmgr_fake->lock); assert(bo); assert(target_bo); if (bo_fake->relocs == NULL) { bo_fake->relocs = malloc(sizeof(struct fake_buffer_reloc) * MAX_RELOCS); } r = &bo_fake->relocs[bo_fake->nr_relocs++]; assert(bo_fake->nr_relocs <= MAX_RELOCS); drm_intel_fake_bo_reference_locked(target_bo); if (!target_fake->is_static) { bo_fake->child_size += ALIGN(target_bo->size, target_fake->alignment); bo_fake->child_size += target_fake->child_size; } r->target_buf = target_bo; r->offset = offset; r->last_target_offset = target_bo->offset; r->delta = target_offset; r->read_domains = read_domains; r->write_domain = write_domain; if (bufmgr_fake->debug) { /* Check that a conflicting relocation hasn't already been emitted. */ for (i = 0; i < bo_fake->nr_relocs - 1; i++) { struct fake_buffer_reloc *r2 = &bo_fake->relocs[i]; assert(r->offset != r2->offset); } } pthread_mutex_unlock(&bufmgr_fake->lock); return 0; } /** * Incorporates the validation flags associated with each relocation into * the combined validation flags for the buffer on this batchbuffer submission. */ static void drm_intel_fake_calculate_domains(drm_intel_bo *bo) { drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo; int i; for (i = 0; i < bo_fake->nr_relocs; i++) { struct fake_buffer_reloc *r = &bo_fake->relocs[i]; drm_intel_bo_fake *target_fake = (drm_intel_bo_fake *)r->target_buf; /* Do the same for the tree of buffers we depend on */ drm_intel_fake_calculate_domains(r->target_buf); target_fake->read_domains |= r->read_domains; target_fake->write_domain |= r->write_domain; } } static int drm_intel_fake_reloc_and_validate_buffer(drm_intel_bo *bo) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr; drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo; int i, ret; assert(bo_fake->map_count == 0); for (i = 0; i < bo_fake->nr_relocs; i++) { struct fake_buffer_reloc *r = &bo_fake->relocs[i]; drm_intel_bo_fake *target_fake = (drm_intel_bo_fake *)r->target_buf; uint32_t reloc_data; /* Validate the target buffer if that hasn't been done. */ if (!target_fake->validated) { ret = drm_intel_fake_reloc_and_validate_buffer(r->target_buf); if (ret != 0) { if (bo->virtual != NULL) drm_intel_fake_bo_unmap_locked(bo); return ret; } } /* Calculate the value of the relocation entry. */ if (r->target_buf->offset != r->last_target_offset) { reloc_data = r->target_buf->offset + r->delta; if (bo->virtual == NULL) drm_intel_fake_bo_map_locked(bo, 1); *(uint32_t *)((uint8_t *)bo->virtual + r->offset) = reloc_data; r->last_target_offset = r->target_buf->offset; } } if (bo->virtual != NULL) drm_intel_fake_bo_unmap_locked(bo); if (bo_fake->write_domain != 0) { if (!(bo_fake->flags & (BM_NO_BACKING_STORE|BM_PINNED))) { if (bo_fake->backing_store == 0) alloc_backing_store(bo); } bo_fake->card_dirty = 1; bufmgr_fake->performed_rendering = 1; } return drm_intel_fake_bo_validate(bo); } static void drm_intel_bo_fake_post_submit(drm_intel_bo *bo) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr; drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo; int i; for (i = 0; i < bo_fake->nr_relocs; i++) { struct fake_buffer_reloc *r = &bo_fake->relocs[i]; drm_intel_bo_fake *target_fake = (drm_intel_bo_fake *)r->target_buf; if (target_fake->validated) drm_intel_bo_fake_post_submit(r->target_buf); DBG("%s@0x%08x + 0x%08x -> %s@0x%08x + 0x%08x\n", bo_fake->name, (uint32_t)bo->offset, r->offset, target_fake->name, (uint32_t)r->target_buf->offset, r->delta); } assert(bo_fake->map_count == 0); bo_fake->validated = 0; bo_fake->read_domains = 0; bo_fake->write_domain = 0; } void drm_intel_bufmgr_fake_set_exec_callback(drm_intel_bufmgr *bufmgr, int (*exec)(drm_intel_bo *bo, unsigned int used, void *priv), void *priv) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bufmgr; bufmgr_fake->exec = exec; bufmgr_fake->exec_priv = priv; } static int drm_intel_fake_bo_exec(drm_intel_bo *bo, int used, drm_clip_rect_t *cliprects, int num_cliprects, int DR4) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr; drm_intel_bo_fake *batch_fake = (drm_intel_bo_fake *)bo; struct drm_i915_batchbuffer batch; int ret; int retry_count = 0; pthread_mutex_lock(&bufmgr_fake->lock); bufmgr_fake->performed_rendering = 0; drm_intel_fake_calculate_domains(bo); batch_fake->read_domains = I915_GEM_DOMAIN_COMMAND; /* we've ran out of RAM so blow the whole lot away and retry */ restart: ret = drm_intel_fake_reloc_and_validate_buffer(bo); if (bufmgr_fake->fail == 1) { if (retry_count == 0) { retry_count++; drm_intel_fake_kick_all_locked(bufmgr_fake); bufmgr_fake->fail = 0; goto restart; } else /* dump out the memory here */ mmDumpMemInfo(bufmgr_fake->heap); } assert(ret == 0); if (bufmgr_fake->exec != NULL) { int ret = bufmgr_fake->exec(bo, used, bufmgr_fake->exec_priv); if (ret != 0) { pthread_mutex_unlock(&bufmgr_fake->lock); return ret; } } else { batch.start = bo->offset; batch.used = used; batch.cliprects = cliprects; batch.num_cliprects = num_cliprects; batch.DR1 = 0; batch.DR4 = DR4; if (drmCommandWrite(bufmgr_fake->fd, DRM_I915_BATCHBUFFER, &batch, sizeof(batch))) { drmMsg("DRM_I915_BATCHBUFFER: %d\n", -errno); pthread_mutex_unlock(&bufmgr_fake->lock); return -errno; } } drm_intel_fake_fence_validated(bo->bufmgr); drm_intel_bo_fake_post_submit(bo); pthread_mutex_unlock(&bufmgr_fake->lock); return 0; } /** * Return an error if the list of BOs will exceed the aperture size. * * This is a rough guess and likely to fail, as during the validate sequence we * may place a buffer in an inopportune spot early on and then fail to fit * a set smaller than the aperture. */ static int drm_intel_fake_check_aperture_space(drm_intel_bo **bo_array, int count) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo_array[0]->bufmgr; unsigned int sz = 0; int i; for (i = 0; i < count; i++) { drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo_array[i]; if (bo_fake == NULL) continue; if (!bo_fake->is_static) sz += ALIGN(bo_array[i]->size, bo_fake->alignment); sz += bo_fake->child_size; } if (sz > bufmgr_fake->size) { DBG("check_space: overflowed bufmgr size, %dkb vs %dkb\n", sz / 1024, bufmgr_fake->size / 1024); return -1; } DBG("drm_check_space: sz %dkb vs bufgr %dkb\n", sz / 1024 , bufmgr_fake->size / 1024); return 0; } /** * Evicts all buffers, waiting for fences to pass and copying contents out * as necessary. * * Used by the X Server on LeaveVT, when the card memory is no longer our * own. */ void drm_intel_bufmgr_fake_evict_all(drm_intel_bufmgr *bufmgr) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bufmgr; struct block *block, *tmp; pthread_mutex_lock(&bufmgr_fake->lock); bufmgr_fake->need_fence = 1; bufmgr_fake->fail = 0; /* Wait for hardware idle. We don't know where acceleration has been * happening, so we'll need to wait anyway before letting anything get * put on the card again. */ drm_intel_bufmgr_fake_wait_idle(bufmgr_fake); /* Check that we hadn't released the lock without having fenced the last * set of buffers. */ assert(DRMLISTEMPTY(&bufmgr_fake->fenced)); assert(DRMLISTEMPTY(&bufmgr_fake->on_hardware)); DRMLISTFOREACHSAFE(block, tmp, &bufmgr_fake->lru) { drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)block->bo; /* Releases the memory, and memcpys dirty contents out if necessary. */ free_block(bufmgr_fake, block, 0); bo_fake->block = NULL; } pthread_mutex_unlock(&bufmgr_fake->lock); } void drm_intel_bufmgr_fake_set_last_dispatch(drm_intel_bufmgr *bufmgr, volatile unsigned int *last_dispatch) { drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bufmgr; bufmgr_fake->last_dispatch = (volatile int *)last_dispatch; } drm_intel_bufmgr * drm_intel_bufmgr_fake_init(int fd, unsigned long low_offset, void *low_virtual, unsigned long size, volatile unsigned int *last_dispatch) { drm_intel_bufmgr_fake *bufmgr_fake; bufmgr_fake = calloc(1, sizeof(*bufmgr_fake)); if (pthread_mutex_init(&bufmgr_fake->lock, NULL) != 0) { free(bufmgr_fake); return NULL; } /* Initialize allocator */ DRMINITLISTHEAD(&bufmgr_fake->fenced); DRMINITLISTHEAD(&bufmgr_fake->on_hardware); DRMINITLISTHEAD(&bufmgr_fake->lru); bufmgr_fake->low_offset = low_offset; bufmgr_fake->virtual = low_virtual; bufmgr_fake->size = size; bufmgr_fake->heap = mmInit(low_offset, size); /* Hook in methods */ bufmgr_fake->bufmgr.bo_alloc = drm_intel_fake_bo_alloc; bufmgr_fake->bufmgr.bo_alloc_for_render = drm_intel_fake_bo_alloc; bufmgr_fake->bufmgr.bo_reference = drm_intel_fake_bo_reference; bufmgr_fake->bufmgr.bo_unreference = drm_intel_fake_bo_unreference; bufmgr_fake->bufmgr.bo_map = drm_intel_fake_bo_map; bufmgr_fake->bufmgr.bo_unmap = drm_intel_fake_bo_unmap; bufmgr_fake->bufmgr.bo_wait_rendering = drm_intel_fake_bo_wait_rendering; bufmgr_fake->bufmgr.bo_emit_reloc = drm_intel_fake_emit_reloc; bufmgr_fake->bufmgr.destroy = drm_intel_fake_destroy; bufmgr_fake->bufmgr.bo_exec = drm_intel_fake_bo_exec; bufmgr_fake->bufmgr.check_aperture_space = drm_intel_fake_check_aperture_space; bufmgr_fake->bufmgr.debug = 0; bufmgr_fake->fd = fd; bufmgr_fake->last_dispatch = (volatile int *)last_dispatch; return &bufmgr_fake->bufmgr; } /* tf/7 */ #define R200_EMIT_VTX_FMT_0 31 /* vtx/5 */ #define R200_EMIT_VAP_CTL 32 /* vap/1 */ #define R200_EMIT_MATRIX_SELECT_0 33 /* msl/5 */ #define R200_EMIT_TEX_PROC_CTL_2 34 /* tcg/5 */ #define R200_EMIT_TCL_UCP_VERT_BLEND_CTL 35 /* tcl/1 */ #define R200_EMIT_PP_TXFILTER_0 36 /* tex0/6 */ #define R200_EMIT_PP_TXFILTER_1 37 /* tex1/6 */ #define R200_EMIT_PP_TXFILTER_2 38 /* tex2/6 */ #define R200_EMIT_PP_TXFILTER_3 39 /* tex3/6 */ #define R200_EMIT_PP_TXFILTER_4 40 /* tex4/6 */ #define R200_EMIT_PP_TXFILTER_5 41 /* tex5/6 */ #define R200_EMIT_PP_TXOFFSET_0 42 /* tex0/1 */ #define R200_EMIT_PP_TXOFFSET_1 43 /* tex1/1 */ #define R200_EMIT_PP_TXOFFSET_2 44 /* tex2/1 */ #define R200_EMIT_PP_TXOFFSET_3 45 /* tex3/1 */ #define R200_EMIT_PP_TXOFFSET_4 46 /* tex4/1 */ #define R200_EMIT_PP_TXOFFSET_5 47 /* tex5/1 */ #define R200_EMIT_VTE_CNTL 48 /* vte/1 */ #define R200_EMIT_OUTPUT_VTX_COMP_SEL 49 /* vtx/1 */ #define R200_EMIT_PP_TAM_DEBUG3 50 /* tam/1 */ #define R200_EMIT_PP_CNTL_X 51 /* cst/1 */ #define R200_EMIT_RB3D_DEPTHXY_OFFSET 52 /* cst/1 */ #define R200_EMIT_RE_AUX_SCISSOR_CNTL 53 /* cst/1 */ #define R200_EMIT_RE_SCISSOR_TL_0 54 /* cst/2 */ #define R200_EMIT_RE_SCISSOR_TL_1 55 /* cst/2 */ #define R200_EMIT_RE_SCISSOR_TL_2 56 /* cst/2 */ #define R200_EMIT_SE_VAP_CNTL_STATUS 57 /* cst/1 */ #define R200_EMIT_SE_VTX_STATE_CNTL 58 /* cst/1 */ #define R200_EMIT_RE_POINTSIZE 59 /* cst/1 */ #define R200_EMIT_TCL_INPUT_VTX_VECTOR_ADDR_0 60 /* cst/4 */ #define R200_EMIT_PP_CUBIC_FACES_0 61 #define R200_EMIT_PP_CUBIC_OFFSETS_0 62 #define R200_EMIT_PP_CUBIC_FACES_1 63 #define R200_EMIT_PP_CUBIC_OFFSETS_1 64 #define R200_EMIT_PP_CUBIC_FACES_2 65 #define R200_EMIT_PP_CUBIC_OFFSETS_2 66 #define R200_EMIT_PP_CUBIC_FACES_3 67 #define R200_EMIT_PP_CUBIC_OFFSETS_3 68 #define R200_EMIT_PP_CUBIC_FACES_4 69 #define R200_EMIT_PP_CUBIC_OFFSETS_4 70 #define R200_EMIT_PP_CUBIC_FACES_5 71 #define R200_EMIT_PP_CUBIC_OFFSETS_5 72 #define RADEON_EMIT_PP_TEX_SIZE_0 73 #define RADEON_EMIT_PP_TEX_SIZE_1 74 #define RADEON_EMIT_PP_TEX_SIZE_2 75 #define R200_EMIT_RB3D_BLENDCOLOR 76 #define R200_EMIT_TCL_POINT_SPRITE_CNTL 77 #define RADEON_EMIT_PP_CUBIC_FACES_0 78 #define RADEON_EMIT_PP_CUBIC_OFFSETS_T0 79 #define RADEON_EMIT_PP_CUBIC_FACES_1 80 #define RADEON_EMIT_PP_CUBIC_OFFSETS_T1 81 #define RADEON_EMIT_PP_CUBIC_FACES_2 82 #define RADEON_EMIT_PP_CUBIC_OFFSETS_T2 83 #define R200_EMIT_PP_TRI_PERF_CNTL 84 #define R200_EMIT_PP_AFS_0 85 #define R200_EMIT_PP_AFS_1 86 #define R200_EMIT_ATF_TFACTOR 87 #define R200_EMIT_PP_TXCTLALL_0 88 #define R200_EMIT_PP_TXCTLALL_1 89 #define R200_EMIT_PP_TXCTLALL_2 90 #define R200_EMIT_PP_TXCTLALL_3 91 #define R200_EMIT_PP_TXCTLALL_4 92 #define R200_EMIT_PP_TXCTLALL_5 93 #define R200_EMIT_VAP_PVS_CNTL 94 #define RADEON_MAX_STATE_PACKETS 95 /* Commands understood by cmd_buffer ioctl. More can be added but * obviously these can't be removed or changed: */ #define RADEON_CMD_PACKET 1 /* emit one of the register packets above */ #define RADEON_CMD_SCALARS 2 /* emit scalar data */ #define RADEON_CMD_VECTORS 3 /* emit vector data */ #define RADEON_CMD_DMA_DISCARD 4 /* discard current dma buf */ #define RADEON_CMD_PACKET3 5 /* emit hw packet */ #define RADEON_CMD_PACKET3_CLIP 6 /* emit hw packet wrapped in cliprects */ #define RADEON_CMD_SCALARS2 7 /* r200 stopgap */ #define RADEON_CMD_WAIT 8 /* emit hw wait commands -- note: * doesn't make the cpu wait, just * the graphics hardware */ #define RADEON_CMD_VECLINEAR 9 /* another r200 stopgap */ typedef union { int i; struct { unsigned char cmd_type, pad0, pad1, pad2; } header; struct { unsigned char cmd_type, packet_id, pad0, pad1; } packet; struct { unsigned char cmd_type, offset, stride, count; } scalars; struct { unsigned char cmd_type, offset, stride, count; } vectors; struct { unsigned char cmd_type, addr_lo, addr_hi, count; } veclinear; struct { unsigned char cmd_type, buf_idx, pad0, pad1; } dma; struct { unsigned char cmd_type, flags, pad0, pad1; } wait; } drm_radeon_cmd_header_t; #define RADEON_WAIT_2D 0x1 #define RADEON_WAIT_3D 0x2 /* Allowed parameters for R300_CMD_PACKET3 */ #define R300_CMD_PACKET3_CLEAR 0 #define R300_CMD_PACKET3_RAW 1 /* Commands understood by cmd_buffer ioctl for R300. * The interface has not been stabilized, so some of these may be removed * and eventually reordered before stabilization. */ #define R300_CMD_PACKET0 1 #define R300_CMD_VPU 2 /* emit vertex program upload */ #define R300_CMD_PACKET3 3 /* emit a packet3 */ #define R300_CMD_END3D 4 /* emit sequence ending 3d rendering */ #define R300_CMD_CP_DELAY 5 #define R300_CMD_DMA_DISCARD 6 #define R300_CMD_WAIT 7 # define R300_WAIT_2D 0x1 # define R300_WAIT_3D 0x2 /* these two defines are DOING IT WRONG - however * we have userspace which relies on using these. * The wait interface is backwards compat new * code should use the NEW_WAIT defines below * THESE ARE NOT BIT FIELDS */ # define R300_WAIT_2D_CLEAN 0x3 # define R300_WAIT_3D_CLEAN 0x4 # define R300_NEW_WAIT_2D_3D 0x3 # define R300_NEW_WAIT_2D_2D_CLEAN 0x4 # define R300_NEW_WAIT_3D_3D_CLEAN 0x6 # define R300_NEW_WAIT_2D_2D_CLEAN_3D_3D_CLEAN 0x8 #define R300_CMD_SCRATCH 8 #define R300_CMD_R500FP 9 typedef union { unsigned int u; struct { unsigned char cmd_type, pad0, pad1, pad2; } header; struct { unsigned char cmd_type, count, reglo, reghi; } packet0; struct { unsigned char cmd_type, count, adrlo, adrhi; } vpu; struct { unsigned char cmd_type, packet, pad0, pad1; } packet3; struct { unsigned char cmd_type, packet; unsigned short count; /* amount of packet2 to emit */ } delay; struct { unsigned char cmd_type, buf_idx, pad0, pad1; } dma; struct { unsigned char cmd_type, flags, pad0, pad1; } wait; struct { unsigned char cmd_type, reg, n_bufs, flags; } scratch; struct { unsigned char cmd_type, count, adrlo, adrhi_flags; } r500fp; } drm_r300_cmd_header_t; #define RADEON_FRONT 0x1 #define RADEON_BACK 0x2 #define RADEON_DEPTH 0x4 #define RADEON_STENCIL 0x8 #define RADEON_CLEAR_FASTZ 0x80000000 #define RADEON_USE_HIERZ 0x40000000 #define RADEON_USE_COMP_ZBUF 0x20000000 #define R500FP_CONSTANT_TYPE (1 << 1) #define R500FP_CONSTANT_CLAMP (1 << 2) /* Primitive types */ #define RADEON_POINTS 0x1 #define RADEON_LINES 0x2 #define RADEON_LINE_STRIP 0x3 #define RADEON_TRIANGLES 0x4 #define RADEON_TRIANGLE_FAN 0x5 #define RADEON_TRIANGLE_STRIP 0x6 /* Vertex/indirect buffer size */ #define RADEON_BUFFER_SIZE 65536 /* Byte offsets for indirect buffer data */ #define RADEON_INDEX_PRIM_OFFSET 20 #define RADEON_SCRATCH_REG_OFFSET 32 #define R600_SCRATCH_REG_OFFSET 256 #define RADEON_NR_SAREA_CLIPRECTS 12 /* There are 2 heaps (local/GART). Each region within a heap is a * minimum of 64k, and there are at most 64 of them per heap. */ #define RADEON_LOCAL_TEX_HEAP 0 #define RADEON_GART_TEX_HEAP 1 #define RADEON_NR_TEX_HEAPS 2 #define RADEON_NR_TEX_REGIONS 64 #define RADEON_LOG_TEX_GRANULARITY 16 #define RADEON_MAX_TEXTURE_LEVELS 12 #define RADEON_MAX_TEXTURE_UNITS 3 #define RADEON_MAX_SURFACES 8 /* Blits have strict offset rules. All blit offset must be aligned on * a 1K-byte boundary. */ #define RADEON_OFFSET_SHIFT 10 #define RADEON_OFFSET_ALIGN (1 << RADEON_OFFSET_SHIFT) #define RADEON_OFFSET_MASK (RADEON_OFFSET_ALIGN - 1) #endif /* __RADEON_SAREA_DEFINES__ */ typedef struct { unsigned int red; unsigned int green; unsigned int blue; unsigned int alpha; } radeon_color_regs_t; typedef struct { /* Context state */ unsigned int pp_misc; /* 0x1c14 */ unsigned int pp_fog_color; unsigned int re_solid_color; unsigned int rb3d_blendcntl; unsigned int rb3d_depthoffset; unsigned int rb3d_depthpitch; unsigned int rb3d_zstencilcntl; unsigned int pp_cntl; /* 0x1c38 */ unsigned int rb3d_cntl; unsigned int rb3d_coloroffset; unsigned int re_width_height; unsigned int rb3d_colorpitch; unsigned int se_cntl; /* Vertex format state */ unsigned int se_coord_fmt; /* 0x1c50 */ /* Line state */ unsigned int re_line_pattern; /* 0x1cd0 */ unsigned int re_line_state; unsigned int se_line_width; /* 0x1db8 */ /* Bumpmap state */ unsigned int pp_lum_matrix; /* 0x1d00 */ unsigned int pp_rot_matrix_0; /* 0x1d58 */ unsigned int pp_rot_matrix_1; /* Mask state */ unsigned int rb3d_stencilrefmask; /* 0x1d7c */ unsigned int rb3d_ropcntl; unsigned int rb3d_planemask; /* Viewport state */ unsigned int se_vport_xscale; /* 0x1d98 */ unsigned int se_vport_xoffset; unsigned int se_vport_yscale; unsigned int se_vport_yoffset; unsigned int se_vport_zscale; unsigned int se_vport_zoffset; /* Setup state */ unsigned int se_cntl_status; /* 0x2140 */ /* Misc state */ unsigned int re_top_left; /* 0x26c0 */ unsigned int re_misc; } drm_radeon_context_regs_t; typedef struct { /* Zbias state */ unsigned int se_zbias_factor; /* 0x1dac */ unsigned int se_zbias_constant; } drm_radeon_context2_regs_t; /* Setup registers for each texture unit */ typedef struct { unsigned int pp_txfilter; unsigned int pp_txformat; unsigned int pp_txoffset; unsigned int pp_txcblend; unsigned int pp_txablend; unsigned int pp_tfactor; unsigned int pp_border_color; } drm_radeon_texture_regs_t; typedef struct { unsigned int start; unsigned int finish; unsigned int prim:8; unsigned int stateidx:8; unsigned int numverts:16; /* overloaded as offset/64 for elt prims */ unsigned int vc_format; /* vertex format */ } drm_radeon_prim_t; typedef struct { drm_radeon_context_regs_t context; drm_radeon_texture_regs_t tex[RADEON_MAX_TEXTURE_UNITS]; drm_radeon_context2_regs_t context2; unsigned int dirty; } drm_radeon_state_t; typedef struct { /* The channel for communication of state information to the * kernel on firing a vertex buffer with either of the * obsoleted vertex/index ioctls. */ drm_radeon_context_regs_t context_state; drm_radeon_texture_regs_t tex_state[RADEON_MAX_TEXTURE_UNITS]; unsigned int dirty; unsigned int vertsize; unsigned int vc_format; /* The current cliprects, or a subset thereof. */ struct drm_clip_rect boxes[RADEON_NR_SAREA_CLIPRECTS]; unsigned int nbox; /* Counters for client-side throttling of rendering clients. */ unsigned int last_frame; unsigned int last_dispatch; unsigned int last_clear; struct drm_tex_region tex_list[RADEON_NR_TEX_HEAPS][RADEON_NR_TEX_REGIONS + 1]; unsigned int tex_age[RADEON_NR_TEX_HEAPS]; int ctx_owner; int pfState; /* number of 3d windows (0,1,2ormore) */ int pfCurrentPage; /* which buffer is being displayed? */ int crtc2_base; /* CRTC2 frame offset */ int tiling_enabled; /* set by drm, read by 2d + 3d clients */ } drm_radeon_sarea_t; /* WARNING: If you change any of these defines, make sure to change the * defines in the Xserver file (xf86drmRadeon.h) * * KW: actually it's illegal to change any of this (backwards compatibility). */ /* Radeon specific ioctls * The device specific ioctl range is 0x40 to 0x79. */ #define DRM_RADEON_CP_INIT 0x00 #define DRM_RADEON_CP_START 0x01 #define DRM_RADEON_CP_STOP 0x02 #define DRM_RADEON_CP_RESET 0x03 #define DRM_RADEON_CP_IDLE 0x04 #define DRM_RADEON_RESET 0x05 #define DRM_RADEON_FULLSCREEN 0x06 #define DRM_RADEON_SWAP 0x07 #define DRM_RADEON_CLEAR 0x08 #define DRM_RADEON_VERTEX 0x09 #define DRM_RADEON_INDICES 0x0A #define DRM_RADEON_NOT_USED #define DRM_RADEON_STIPPLE 0x0C #define DRM_RADEON_INDIRECT 0x0D #define DRM_RADEON_TEXTURE 0x0E #define DRM_RADEON_VERTEX2 0x0F #define DRM_RADEON_CMDBUF 0x10 #define DRM_RADEON_GETPARAM 0x11 #define DRM_RADEON_FLIP 0x12 #define DRM_RADEON_ALLOC 0x13 #define DRM_RADEON_FREE 0x14 #define DRM_RADEON_INIT_HEAP 0x15 #define DRM_RADEON_IRQ_EMIT 0x16 #define DRM_RADEON_IRQ_WAIT 0x17 #define DRM_RADEON_CP_RESUME 0x18 #define DRM_RADEON_SETPARAM 0x19 #define DRM_RADEON_SURF_ALLOC 0x1a #define DRM_RADEON_SURF_FREE 0x1b /* KMS ioctl */ #define DRM_RADEON_GEM_INFO 0x1c #define DRM_RADEON_GEM_CREATE 0x1d #define DRM_RADEON_GEM_MMAP 0x1e #define DRM_RADEON_GEM_PREAD 0x21 #define DRM_RADEON_GEM_PWRITE 0x22 #define DRM_RADEON_GEM_SET_DOMAIN 0x23 #define DRM_RADEON_GEM_WAIT_IDLE 0x24 #define DRM_RADEON_CS 0x26 #define DRM_RADEON_INFO 0x27 #define DRM_RADEON_GEM_SET_TILING 0x28 #define DRM_RADEON_GEM_GET_TILING 0x29 #define DRM_RADEON_GEM_BUSY 0x2a #define DRM_IOCTL_RADEON_CP_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CP_INIT, drm_radeon_init_t) #define DRM_IOCTL_RADEON_CP_START DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_START) #define DRM_IOCTL_RADEON_CP_STOP DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CP_STOP, drm_radeon_cp_stop_t) #define DRM_IOCTL_RADEON_CP_RESET DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_RESET) #define DRM_IOCTL_RADEON_CP_IDLE DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_IDLE) #define DRM_IOCTL_RADEON_RESET DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_RESET) #define DRM_IOCTL_RADEON_FULLSCREEN DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_FULLSCREEN, drm_radeon_fullscreen_t) #define DRM_IOCTL_RADEON_SWAP DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_SWAP) #define DRM_IOCTL_RADEON_CLEAR DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CLEAR, drm_radeon_clear_t) #define DRM_IOCTL_RADEON_VERTEX DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_VERTEX, drm_radeon_vertex_t) #define DRM_IOCTL_RADEON_INDICES DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_INDICES, drm_radeon_indices_t) #define DRM_IOCTL_RADEON_STIPPLE DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_STIPPLE, drm_radeon_stipple_t) #define DRM_IOCTL_RADEON_INDIRECT DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_INDIRECT, drm_radeon_indirect_t) #define DRM_IOCTL_RADEON_TEXTURE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_TEXTURE, drm_radeon_texture_t) #define DRM_IOCTL_RADEON_VERTEX2 DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_VERTEX2, drm_radeon_vertex2_t) #define DRM_IOCTL_RADEON_CMDBUF DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CMDBUF, drm_radeon_cmd_buffer_t) #define DRM_IOCTL_RADEON_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GETPARAM, drm_radeon_getparam_t) #define DRM_IOCTL_RADEON_FLIP DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_FLIP) #define DRM_IOCTL_RADEON_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_ALLOC, drm_radeon_mem_alloc_t) #define DRM_IOCTL_RADEON_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_FREE, drm_radeon_mem_free_t) #define DRM_IOCTL_RADEON_INIT_HEAP DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_INIT_HEAP, drm_radeon_mem_init_heap_t) #define DRM_IOCTL_RADEON_IRQ_EMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_IRQ_EMIT, drm_radeon_irq_emit_t) #define DRM_IOCTL_RADEON_IRQ_WAIT DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_IRQ_WAIT, drm_radeon_irq_wait_t) #define DRM_IOCTL_RADEON_CP_RESUME DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_RESUME) #define DRM_IOCTL_RADEON_SETPARAM DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SETPARAM, drm_radeon_setparam_t) #define DRM_IOCTL_RADEON_SURF_ALLOC DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SURF_ALLOC, drm_radeon_surface_alloc_t) #define DRM_IOCTL_RADEON_SURF_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SURF_FREE, drm_radeon_surface_free_t) /* KMS */ #define DRM_IOCTL_RADEON_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_INFO, struct drm_radeon_gem_info) #define DRM_IOCTL_RADEON_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_CREATE, struct drm_radeon_gem_create) #define DRM_IOCTL_RADEON_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_MMAP, struct drm_radeon_gem_mmap) #define DRM_IOCTL_RADEON_GEM_PREAD DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PREAD, struct drm_radeon_gem_pread) #define DRM_IOCTL_RADEON_GEM_PWRITE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PWRITE, struct drm_radeon_gem_pwrite) #define DRM_IOCTL_RADEON_GEM_SET_DOMAIN DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_SET_DOMAIN, struct drm_radeon_gem_set_domain) #define DRM_IOCTL_RADEON_GEM_WAIT_IDLE DRM_IOW(DRM_COMMAND_BASE + DRM_RADEON_GEM_WAIT_IDLE, struct drm_radeon_gem_wait_idle) #define DRM_IOCTL_RADEON_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_CS, struct drm_radeon_cs) #define DRM_IOCTL_RADEON_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_INFO, struct drm_radeon_info) #define DRM_IOCTL_RADEON_SET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_SET_TILING, struct drm_radeon_gem_set_tiling) #define DRM_IOCTL_RADEON_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_GET_TILING, struct drm_radeon_gem_get_tiling) #define DRM_IOCTL_RADEON_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_BUSY, struct drm_radeon_gem_busy) typedef struct drm_radeon_init { enum { RADEON_INIT_CP = 0x01, RADEON_CLEANUP_CP = 0x02, RADEON_INIT_R200_CP = 0x03, RADEON_INIT_R300_CP = 0x04, RADEON_INIT_R600_CP = 0x05 } func; unsigned long sarea_priv_offset; int is_pci; int cp_mode; int gart_size; int ring_size; int usec_timeout; unsigned int fb_bpp; unsigned int front_offset, front_pitch; unsigned int back_offset, back_pitch; unsigned int depth_bpp; unsigned int depth_offset, depth_pitch; unsigned long fb_offset; unsigned long mmio_offset; unsigned long ring_offset; unsigned long ring_rptr_offset; unsigned long buffers_offset; unsigned long gart_textures_offset; } drm_radeon_init_t; typedef struct drm_radeon_cp_stop { int flush; int idle; } drm_radeon_cp_stop_t; typedef struct drm_radeon_fullscreen { enum { RADEON_INIT_FULLSCREEN = 0x01, RADEON_CLEANUP_FULLSCREEN = 0x02 } func; } drm_radeon_fullscreen_t; #define CLEAR_X1 0 #define CLEAR_Y1 1 #define CLEAR_X2 2 #define CLEAR_Y2 3 #define CLEAR_DEPTH 4 typedef union drm_radeon_clear_rect { float f[5]; unsigned int ui[5]; } drm_radeon_clear_rect_t; typedef struct drm_radeon_clear { unsigned int flags; unsigned int clear_color; unsigned int clear_depth; unsigned int color_mask; unsigned int depth_mask; /* misnamed field: should be stencil */ drm_radeon_clear_rect_t *depth_boxes; } drm_radeon_clear_t; typedef struct drm_radeon_vertex { int prim; int idx; /* Index of vertex buffer */ int count; /* Number of vertices in buffer */ int discard; /* Client finished with buffer? */ } drm_radeon_vertex_t; typedef struct drm_radeon_indices { int prim; int idx; int start; int end; int discard; /* Client finished with buffer? */ } drm_radeon_indices_t; /* v1.2 - obsoletes drm_radeon_vertex and drm_radeon_indices * - allows multiple primitives and state changes in a single ioctl * - supports driver change to emit native primitives */ typedef struct drm_radeon_vertex2 { int idx; /* Index of vertex buffer */ int discard; /* Client finished with buffer? */ int nr_states; drm_radeon_state_t *state; int nr_prims; drm_radeon_prim_t *prim; } drm_radeon_vertex2_t; /* v1.3 - obsoletes drm_radeon_vertex2 * - allows arbitarily large cliprect list * - allows updating of tcl packet, vector and scalar state * - allows memory-efficient description of state updates * - allows state to be emitted without a primitive * (for clears, ctx switches) * - allows more than one dma buffer to be referenced per ioctl * - supports tcl driver * - may be extended in future versions with new cmd types, packets */ typedef struct drm_radeon_cmd_buffer { int bufsz; char *buf; int nbox; struct drm_clip_rect *boxes; } drm_radeon_cmd_buffer_t; typedef struct drm_radeon_tex_image { unsigned int x, y; /* Blit coordinates */ unsigned int width, height; const void *data; } drm_radeon_tex_image_t; typedef struct drm_radeon_texture { unsigned int offset; int pitch; int format; int width; /* Texture image coordinates */ int height; drm_radeon_tex_image_t *image; } drm_radeon_texture_t; typedef struct drm_radeon_stipple { unsigned int *mask; } drm_radeon_stipple_t; typedef struct drm_radeon_indirect { int idx; int start; int end; int discard; } drm_radeon_indirect_t; /* enum for card type parameters */ #define RADEON_CARD_PCI 0 #define RADEON_CARD_AGP 1 #define RADEON_CARD_PCIE 2 /* 1.3: An ioctl to get parameters that aren't available to the 3d * client any other way. */ #define RADEON_PARAM_GART_BUFFER_OFFSET 1 /* card offset of 1st GART buffer */ #define RADEON_PARAM_LAST_FRAME 2 #define RADEON_PARAM_LAST_DISPATCH 3 #define RADEON_PARAM_LAST_CLEAR 4 /* Added with DRM version 1.6. */ #define RADEON_PARAM_IRQ_NR 5 #define RADEON_PARAM_GART_BASE 6 /* card offset of GART base */ /* Added with DRM version 1.8. */ #define RADEON_PARAM_REGISTER_HANDLE 7 /* for drmMap() */ #define RADEON_PARAM_STATUS_HANDLE 8 #define RADEON_PARAM_SAREA_HANDLE 9 #define RADEON_PARAM_GART_TEX_HANDLE 10 #define RADEON_PARAM_SCRATCH_OFFSET 11 #define RADEON_PARAM_CARD_TYPE 12 #define RADEON_PARAM_VBLANK_CRTC 13 /* VBLANK CRTC */ #define RADEON_PARAM_FB_LOCATION 14 /* FB location */ #define RADEON_PARAM_NUM_GB_PIPES 15 /* num GB pipes */ #define RADEON_PARAM_DEVICE_ID 16 #define RADEON_PARAM_NUM_Z_PIPES 17 /* num Z pipes */ typedef struct drm_radeon_getparam { int param; void *value; } drm_radeon_getparam_t; /* 1.6: Set up a memory manager for regions of shared memory: */ #define RADEON_MEM_REGION_GART 1 #define RADEON_MEM_REGION_FB 2 typedef struct drm_radeon_mem_alloc { int region; int alignment; int size; int *region_offset; /* offset from start of fb or GART */ } drm_radeon_mem_alloc_t; typedef struct drm_radeon_mem_free { int region; int region_offset; } drm_radeon_mem_free_t; typedef struct drm_radeon_mem_init_heap { int region; int size; int start; } drm_radeon_mem_init_heap_t; /* 1.6: Userspace can request & wait on irq's: */ typedef struct drm_radeon_irq_emit { int *irq_seq; } drm_radeon_irq_emit_t; typedef struct drm_radeon_irq_wait { int irq_seq; } drm_radeon_irq_wait_t; /* 1.10: Clients tell the DRM where they think the framebuffer is located in * the card's address space, via a new generic ioctl to set parameters */ typedef struct drm_radeon_setparam { unsigned int param; __s64 value; } drm_radeon_setparam_t; #define RADEON_SETPARAM_FB_LOCATION 1 /* determined framebuffer location */ #define RADEON_SETPARAM_SWITCH_TILING 2 /* enable/disable color tiling */ #define RADEON_SETPARAM_PCIGART_LOCATION 3 /* PCI Gart Location */ #define RADEON_SETPARAM_NEW_MEMMAP 4 /* Use new memory map */ #define RADEON_SETPARAM_PCIGART_TABLE_SIZE 5 /* PCI GART Table Size */ #define RADEON_SETPARAM_VBLANK_CRTC 6 /* VBLANK CRTC */ /* 1.14: Clients can allocate/free a surface */ typedef struct drm_radeon_surface_alloc { unsigned int address; unsigned int size; unsigned int flags; } drm_radeon_surface_alloc_t; typedef struct drm_radeon_surface_free { unsigned int address; } drm_radeon_surface_free_t; #define DRM_RADEON_VBLANK_CRTC1 1 #define DRM_RADEON_VBLANK_CRTC2 2 /* * Kernel modesetting world below. */ #define RADEON_GEM_DOMAIN_CPU 0x1 #define RADEON_GEM_DOMAIN_GTT 0x2 #define RADEON_GEM_DOMAIN_VRAM 0x4 struct drm_radeon_gem_info { uint64_t gart_size; uint64_t vram_size; uint64_t vram_visible; }; #define RADEON_GEM_NO_BACKING_STORE 1 struct drm_radeon_gem_create { uint64_t size; uint64_t alignment; uint32_t handle; uint32_t initial_domain; uint32_t flags; }; #define RADEON_TILING_MACRO 0x1 #define RADEON_TILING_MICRO 0x2 #define RADEON_TILING_SWAP_16BIT 0x4 #define RADEON_TILING_SWAP_32BIT 0x8 #define RADEON_TILING_SURFACE 0x10 /* this object requires a surface * when mapped - i.e. front buffer */ #define RADEON_TILING_MICRO_SQUARE 0x20 struct drm_radeon_gem_set_tiling { uint32_t handle; uint32_t tiling_flags; uint32_t pitch; }; struct drm_radeon_gem_get_tiling { uint32_t handle; uint32_t tiling_flags; uint32_t pitch; }; struct drm_radeon_gem_mmap { uint32_t handle; uint32_t pad; uint64_t offset; uint64_t size; uint64_t addr_ptr; }; struct drm_radeon_gem_set_domain { uint32_t handle; uint32_t read_domains; uint32_t write_domain; }; struct drm_radeon_gem_wait_idle { uint32_t handle; uint32_t pad; }; struct drm_radeon_gem_busy { uint32_t handle; uint32_t domain; }; struct drm_radeon_gem_pread { /** Handle for the object being read. */ uint32_t handle; uint32_t pad; /** Offset into the object to read from */ uint64_t offset; /** Length of data to read */ uint64_t size; /** Pointer to write the data into. */ /* void *, but pointers are not 32/64 compatible */ uint64_t data_ptr; }; struct drm_radeon_gem_pwrite { /** Handle for the object being written to. */ uint32_t handle; uint32_t pad; /** Offset into the object to write to */ uint64_t offset; /** Length of data to write */ uint64_t size; /** Pointer to read the data from. */ /* void *, but pointers are not 32/64 compatible */ uint64_t data_ptr; }; #define RADEON_CHUNK_ID_RELOCS 0x01 #define RADEON_CHUNK_ID_IB 0x02 struct drm_radeon_cs_chunk { uint32_t chunk_id; uint32_t length_dw; uint64_t chunk_data; }; struct drm_radeon_cs_reloc { uint32_t handle; uint32_t read_domains; uint32_t write_domain; uint32_t flags; }; struct drm_radeon_cs {