diff options
author | Jose Fonseca <jrfonseca@users.sourceforge.net> | 2003-06-22 13:22:24 +0000 |
---|---|---|
committer | Jose Fonseca <jrfonseca@users.sourceforge.net> | 2003-06-22 13:22:24 +0000 |
commit | 577541966bc913fcbb5a41dc07b4be3fe8584052 (patch) | |
tree | 23eaa516c0c43e1b1f27c087058acd1635f62387 | |
parent | d289d2c2d917a2473d24acf74fbba31aaa8e9dcd (diff) |
Beginning of a DMA buffer pool and free list management API.
-rw-r--r-- | linux-core/drm_bufs.c | 124 | ||||
-rw-r--r-- | linux/drm_bufs.h | 124 | ||||
-rw-r--r-- | linux/drm_bufs_tmp.h | 255 |
3 files changed, 503 insertions, 0 deletions
diff --git a/linux-core/drm_bufs.c b/linux-core/drm_bufs.c index 000c7e47..58b7e0e1 100644 --- a/linux-core/drm_bufs.c +++ b/linux-core/drm_bufs.c @@ -4,6 +4,7 @@ * * \author Rickard E. (Rik) Faith <faith@valinux.com> * \author Gareth Hughes <gareth@valinux.com> + * \author José Fonseca <jrfonseca@tungstengraphics.com> * * \todo The current buffer management system assumes too much and is severily * limited: @@ -49,6 +50,127 @@ #define _DRM_BUFS_H +/** \name DMA buffer pool */ +/*@{*/ + +typedef struct drm_pool_buffer drm_pool_buffer_t; +typedef struct drm_pool drm_pool_t; + +/** + * A buffer of the DMA buffer pool. + * + * \sa drm_pool. + */ +struct drm_pool_buffer { + void * cpuaddr; /**< kernel virtual address */ + dma_addr_t busaddr; /**< associated bus address */ +}; + +/** + * DMA buffer pool. + * + * \sa drm_pool_buffer. + */ +struct drm_pool { + size_t count; /**< number of buffers */ + size_t size; /**< size of a buffer */ + drm_pool_buffer_t * buffers; /**< buffers */ + + /** Callback to free the memory associated with this pool */ + void (*free)(drm_device_t *dev, drm_pool_t *pool); +}; + +/*@}*/ + + +/** \name Free-list management */ +/*@{*/ + +typedef struct drm_freelist2_entrys drm_freelist2_entrys_t; +typedef struct drm_freelist2 drm_freelist2_t; + +/** + * An entrys in a freelist. + * + * This structure can be extended by passing to drm_freelist2_init a stride + * value greater than the size of this structure. + * + * \sa drm_freelist2. + * + * \author Based on Leif Delgass's original freelist code for the Mach64 + * driver. + */ +struct drm_freelist2_entrys { + struct list_head list; /**< Linux list */ + drm_pool_buffer_t * buffer; /**< referred DMA buffer */ + + /** + * Used bytes of the buffer. + * + * This is here for convenience to the drivers since this isn't + * used by the free-list management code. + */ + size_t used; + + /** + * Stamp of this buffer. + * + * Whenever drm_freelist2::last_stamp is greater or equal to this value + * then the buffer is considered free. + * + * The actualy quantity used for the stamp and its granularity does not + * matter, but it must be a monotonicaly increasing one. Also + * differences greater than 0x7ffffff are considered the result of + * arithmetic wrap-around and the buffer is freed. + * + * \sa drm_freelist2::last_stamp. + */ + unsigned long stamp; + + /** + * Will it be reused? + * + * If set this flag will override the stamp mechanism above preventing + * the buffer from being considered free, so that its contents can be + * later reused without copying. + */ + int reuse; +} ; + +/** + * A free-list management for DMA buffer pools. + * + * \sa drm_pool_buffer. + */ +struct drm_freelist2 { + drm_pool_t * pool; /**< the pool managed by this free-list */ + + size_t stride; /**< stride of the entries */ + void * entries; /**< array with the freelist entries */ + + struct list_head free; /**< free buffers list */ + struct list_head pending; /**< Buffers pending completion */ + + /** + * Stamp of the last processed buffer. + * + * \sa drm_free_list2_entrys::stamp. + */ + unsigned long last_stamp; + + /** + * Callback to wait for a free buffer. + */ + int (*wait)(drm_device_t *dev, drm_freelist2_t *freelist); +}; + + +/*@}*/ + + +/** \name Deprecated structure */ +/*@{*/ + /** * DMA buffer. */ @@ -130,6 +252,8 @@ typedef struct drm_buf_entry { drm_freelist_t freelist; } drm_buf_entry_t; +/*@}*/ + /** \name Prototypes */ /*@{*/ diff --git a/linux/drm_bufs.h b/linux/drm_bufs.h index 000c7e47..58b7e0e1 100644 --- a/linux/drm_bufs.h +++ b/linux/drm_bufs.h @@ -4,6 +4,7 @@ * * \author Rickard E. (Rik) Faith <faith@valinux.com> * \author Gareth Hughes <gareth@valinux.com> + * \author José Fonseca <jrfonseca@tungstengraphics.com> * * \todo The current buffer management system assumes too much and is severily * limited: @@ -49,6 +50,127 @@ #define _DRM_BUFS_H +/** \name DMA buffer pool */ +/*@{*/ + +typedef struct drm_pool_buffer drm_pool_buffer_t; +typedef struct drm_pool drm_pool_t; + +/** + * A buffer of the DMA buffer pool. + * + * \sa drm_pool. + */ +struct drm_pool_buffer { + void * cpuaddr; /**< kernel virtual address */ + dma_addr_t busaddr; /**< associated bus address */ +}; + +/** + * DMA buffer pool. + * + * \sa drm_pool_buffer. + */ +struct drm_pool { + size_t count; /**< number of buffers */ + size_t size; /**< size of a buffer */ + drm_pool_buffer_t * buffers; /**< buffers */ + + /** Callback to free the memory associated with this pool */ + void (*free)(drm_device_t *dev, drm_pool_t *pool); +}; + +/*@}*/ + + +/** \name Free-list management */ +/*@{*/ + +typedef struct drm_freelist2_entrys drm_freelist2_entrys_t; +typedef struct drm_freelist2 drm_freelist2_t; + +/** + * An entrys in a freelist. + * + * This structure can be extended by passing to drm_freelist2_init a stride + * value greater than the size of this structure. + * + * \sa drm_freelist2. + * + * \author Based on Leif Delgass's original freelist code for the Mach64 + * driver. + */ +struct drm_freelist2_entrys { + struct list_head list; /**< Linux list */ + drm_pool_buffer_t * buffer; /**< referred DMA buffer */ + + /** + * Used bytes of the buffer. + * + * This is here for convenience to the drivers since this isn't + * used by the free-list management code. + */ + size_t used; + + /** + * Stamp of this buffer. + * + * Whenever drm_freelist2::last_stamp is greater or equal to this value + * then the buffer is considered free. + * + * The actualy quantity used for the stamp and its granularity does not + * matter, but it must be a monotonicaly increasing one. Also + * differences greater than 0x7ffffff are considered the result of + * arithmetic wrap-around and the buffer is freed. + * + * \sa drm_freelist2::last_stamp. + */ + unsigned long stamp; + + /** + * Will it be reused? + * + * If set this flag will override the stamp mechanism above preventing + * the buffer from being considered free, so that its contents can be + * later reused without copying. + */ + int reuse; +} ; + +/** + * A free-list management for DMA buffer pools. + * + * \sa drm_pool_buffer. + */ +struct drm_freelist2 { + drm_pool_t * pool; /**< the pool managed by this free-list */ + + size_t stride; /**< stride of the entries */ + void * entries; /**< array with the freelist entries */ + + struct list_head free; /**< free buffers list */ + struct list_head pending; /**< Buffers pending completion */ + + /** + * Stamp of the last processed buffer. + * + * \sa drm_free_list2_entrys::stamp. + */ + unsigned long last_stamp; + + /** + * Callback to wait for a free buffer. + */ + int (*wait)(drm_device_t *dev, drm_freelist2_t *freelist); +}; + + +/*@}*/ + + +/** \name Deprecated structure */ +/*@{*/ + /** * DMA buffer. */ @@ -130,6 +252,8 @@ typedef struct drm_buf_entry { drm_freelist_t freelist; } drm_buf_entry_t; +/*@}*/ + /** \name Prototypes */ /*@{*/ diff --git a/linux/drm_bufs_tmp.h b/linux/drm_bufs_tmp.h index e14af2bb..3e5396e0 100644 --- a/linux/drm_bufs_tmp.h +++ b/linux/drm_bufs_tmp.h @@ -4,6 +4,11 @@ * * \author Rickard E. (Rik) Faith <faith@valinux.com> * \author Gareth Hughes <gareth@valinux.com> + * \author José Fonseca <jrfonseca@tungstengraphics.com> + * + * \todo The new functions here don't use the DRM(...)() convention so they will + * break static kernel builds. The idea is to move them to a seperate library + * in a later time that will be linked agains all modules. */ /* @@ -36,6 +41,256 @@ #include <linux/vmalloc.h> #include "drmP.h" + +/** \name Buffer pools initialization/cleanup */ +/*@{*/ + +/** + * Initialize a buffer pool. + * + * It allocates the drm_pool::buffers but it's the caller's responsability to + * fill those afterwards. + * + * \note Not mean to be called directly by drivers. Use one of the functions + * mentioned below instead. + * + * \sa drm_pci_pool_alloc(), drm_agp_pool_alloc() and drm_sg_pool_alloc(). + */ +int drm_pool_create(drm_pool_t *pool, size_t count, size_t size) +{ + memset(pool, 0, sizeof(drm_pool_t)); + + pool->count = count + pool->size = size; + pool->buffers = (drm_pool_buffer_t *)DRM(alloc)( + count*sizeof(drm_buffer_t), + DRM_MEM_BUFLISTS); + + if (!pool->buffers) + return -ENOMEM; + + return 0; +} + +/** + * Destroy a buffer pool. + * + * \note This function won't actually free the structure and should't be called + * directly by the drivers. Use drm_pool_free() instead. + */ +void drm_pool_destroy(drm_pool_t *pool) +{ + DRM(free)(pool->buffers, pool->count*sizeof(drm_buffer_t), DRM_MEM_BUFLISTS); +} + +/** + * Free a buffer pool. + * + * Calls the free callback + */ +void drm_pool_free(drm_device_t *dev, drm_pool_t *pool) +{ + if (!pool->free) { + DRM_ERROR( "drm_pool_free called but no free callback present\n" ); + return; + } + + pool->free(dev, pool); +} + +/*@}*/ + + +/** \name Free-list management */ +/*@{*/ + +/** + * Initialize a free-list. + * + * \param freelist pointer to the free-list structure to initialize. + * \param pool DMA buffer pool associated with the free-list. + * \param stride stride of the list entries. This can be used to extend the + * information stored in each buffer. + * \code + * struct my_freelist_entry { + * struct drm_freelist2_entry base; + * ... + * } ; + * + * ... + * drm_freelist2_create(my_freelist, my_pool, sizeof(my_freelist_entry)); + * ... + * \endcode + * + * \return zero on success or a negative number on failure. + * + */ +int drm_freelist2_create(drm_freelist2_t *freelist, drm_pool_t *pool, size_t stride) +{ + unsigned i; + drm_freelist2_entry_t *entry; + + memset(freelist, 0, sizeof(drm_freelist2_t)); + + freelist->pool = pool; + freelist->stride = stride; + freelist->entries = DRM(alloc)( + pool->count*stride, + DRM_MEM_BUFLISTS); + + if (!freelist->entries) + return -ENOMEM; + + memset(freelist->entries, 0, pool->count*stride); + + /* Add each buffer to the free list */ + for(i = 0; i < pool->count; ++i) { + entry = (drm_freelist2_entry_t *)((unsigned char *)freelist->entry + i*stride); + entry->buffer = &pool->buffers[i]; + list_add_tail(&entry->list, freelist->free); + } + + return 0; +} + +/** + * Free the resources associated with a free-list. + */ +void drm_freelist2_destroy(drm_freelist2_t *freelist) +{ + DRM(free)(freelist->entries, freelist->pool->count*freelist->stride, DRM_MEM_BUFLISTS); +} + +/** + * Reset the buffers. + * + * Iterates the pending list and move all discardable buffers into the + * free list. + * + * \warning This function should only be called when the engine is idle or + * locked up, as it assumes all buffers in the pending list have been completed + * by the hardware. + */ +void drm_freelist2_reset(drm_freelist2_t *freelist) +{ + struct list_head *ptr, *tmp; + drm_freelist2_entry_t *entry; + + if ( list_empty(&freelist->pending) ) + return; + + list_for_each_safe(ptr, tmp, &dev_priv->pending) + { + entry = list_entry(ptr, drm_freelist2_entry_t, list); + if (entry->discard) { + list_del(ptr); + list_add_tail(ptr, &freelist->free); + } + } +} + +/** + * Walks through the pending list freeing the processed buffers by comparing + * their stamps. + * + * \param freelist pointer to the free-list structure. + * \param bail_out whether to bail out on after so-many freed buffers. + * + * \return number of freed entries. + */ +int drm_freelist2_update(drm_freelist2_t *freelist, int bail_out) +{ + struct list_head *ptr, *tmp; + drm_freelist2_entry_t *entry; + int count = 0; + + list_for_each_safe(ptr, tmp, &freelist->pending) { + entry = list_entry(ptr, drm_freelist2_entry_t, list); + + if ( entry->reuse ) + continue; + + delta = freelist->last_stamp - entry->stamp; + if ( delta >= 0 || delta <= -0x4000000 ) { + /* found a processed buffer */ + list_del(ptr); + list_add_tail(ptr, &freelist->free); + ++count; + if (bail_out && count >= bail_out) + break; + } + } + + return count; +} + + +/** + * Get a free buffer from the free-list. + * + * \return pointer to the buffer entry on success, or NULL on failure. + */ +drm_freelist2_entry_t *drm_freelist2_get(drm_freelist2_t *freelist) +{ + struct list_head *ptr, *tmp; + drm_freelist2_entry_t *entry; + + if ( list_empty(&freelist->free) ) { + + if ( list_empty( &freelist->pending ) ) { + DRM_ERROR( "Couldn't get buffer - pending and free lists empty\n" ); + return NULL; + } + + if (freelist->wait(freelist)) + return NULL; + } + + ptr = freelist->free.next; + list_del(ptr); + entry = list_entry(ptr, drm_freelist2_entry_t, list); + entry->used = 0; + return entry->buf; +} + +/** + * Helper for the drm_freelist2::wait callbacks. + * + */ +int drm_freelist2_wait_helper( + drm_freelist2_t *freelist, + void (*update_stamp)(drm_freelist2_t *freelist), + unsigned long timeout, + int bail_out) +{ + struct list_head *ptr, *tmp; + drm_freelist2_entry_t *entry; + unsigned long t; + + for ( t = 0 ; t < timeout ; t++ ) { + update_stamp(freelist); + + if (drm_freelist2_update(freelist, bail_out)) + return 0; + + DRM_UDELAY( 1 ); + } + + return -1; +} + +/*@}*/ + + + + + + + + +/****************************************************************************/ +/* Deprecated stuff */ + #ifndef __HAVE_PCI_DMA #define __HAVE_PCI_DMA 0 #endif |