diff options
author | Thomas Hellstrom <thomas@tungstengraphics.com> | 2006-05-28 16:20:29 +0000 |
---|---|---|
committer | Thomas Hellstrom <thomas@tungstengraphics.com> | 2006-05-28 16:20:29 +0000 |
commit | fa3ac534a8ebbe2cbf012b38636faabdcc107699 (patch) | |
tree | f11b8840aec7241f3aedf661c563eda8b5b0e337 | |
parent | f4e6e4499c1b7a26de2bbf36568f75315faec212 (diff) |
Replacing SiS and VIA memory managers.
-rw-r--r-- | linux-core/Makefile | 14 | ||||
-rw-r--r-- | linux-core/Makefile.kernel | 9 | ||||
-rw-r--r-- | linux-core/drmP.h | 35 | ||||
-rw-r--r-- | linux-core/drm_hashtab.c | 185 | ||||
-rw-r--r-- | linux-core/drm_hashtab.h | 63 | ||||
-rw-r--r-- | linux-core/drm_mm.c | 201 | ||||
-rw-r--r-- | linux-core/drm_sman.c | 352 | ||||
-rw-r--r-- | linux-core/drm_sman.h | 176 | ||||
-rw-r--r-- | linux-core/sis_drv.c | 36 | ||||
-rw-r--r-- | linux-core/sis_mm.c | 286 | ||||
-rw-r--r-- | linux-core/via_mm.c | 203 | ||||
-rw-r--r-- | shared-core/sis_drv.h | 29 | ||||
-rw-r--r-- | shared-core/via_drm.h | 6 | ||||
-rw-r--r-- | shared-core/via_drv.c | 5 | ||||
-rw-r--r-- | shared-core/via_drv.h | 19 | ||||
-rw-r--r-- | shared-core/via_map.c | 12 |
16 files changed, 1609 insertions, 22 deletions
diff --git a/linux-core/Makefile b/linux-core/Makefile index 9bb578cd..32828d28 100644 --- a/linux-core/Makefile +++ b/linux-core/Makefile @@ -77,7 +77,7 @@ DRM_MODULES ?= $(MODULE_LIST) DRMSHARED = drm.h drm_sarea.h DRMHEADERS = drmP.h drm_compat.h drm_os_linux.h $(DRMSHARED) -COREHEADERS = drm_core.h +COREHEADERS = drm_core.h drm_sman.h drm_hashtab.h TDFXHEADERS = tdfx_drv.h $(DRMHEADERS) TDFXSHARED = tdfx_drv.h @@ -93,15 +93,13 @@ I810HEADERS = i810_drv.h i810_drm.h $(DRMHEADERS) I830HEADERS = i830_drv.h i830_drm.h $(DRMHEADERS) I915HEADERS = i915_drv.h i915_drm.h $(DRMHEADERS) I915SHARED = i915_drv.h i915_drm.h i915_irq.c i915_mem.c i915_dma.c -SISHEADERS= sis_drv.h sis_drm.h $(DRMHEADERS) -SISSHARED= sis_drv.h sis_drm.h sis_ds.c sis_ds.h sis_mm.c +SISHEADERS= sis_drv.h sis_drm.h drm_hashtab.h drm_sman.h $(DRMHEADERS) +SISSHARED= sis_drv.h sis_drm.h SAVAGEHEADERS= savage_drv.h savage_drm.h $(DRMHEADERS) SAVAGESHARED= savage_drv.h savage_drm.h savage_bci.c savage_state.c -VIAHEADERS = via_drm.h via_drv.h via_mm.h via_ds.h \ - via_3d_reg.h via_verifier.h $(DRMHEADERS) -VIASHARED = via_drm.h via_drv.h via_mm.h via_ds.h \ - via_3d_reg.h via_drv.c via_ds.c via_irq.c via_map.c \ - via_mm.c via_dma.c via_verifier.c via_verifier.h via_video.c +VIAHEADERS = via_drm.h via_drv.h via_3d_reg.h via_verifier.h $(DRMHEADERS) +VIASHARED = via_drm.h via_drv.h via_3d_reg.h via_drv.c via_irq.c via_map.c \ + via_dma.c via_verifier.c via_verifier.h via_video.c MACH64HEADERS = mach64_drv.h mach64_drm.h $(DRMHEADERS) MACH64SHARED = mach64_drv.h mach64_drm.h mach64_dma.c \ mach64_irq.c mach64_state.c diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel index d63aabb6..211e5b05 100644 --- a/linux-core/Makefile.kernel +++ b/linux-core/Makefile.kernel @@ -11,7 +11,8 @@ drm-objs := drm_auth.o drm_bufs.o drm_context.o drm_dma.o drm_drawable.o \ drm_drv.o drm_fops.o drm_ioctl.o drm_irq.o \ drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \ drm_sysfs.o drm_pci.o drm_agpsupport.o drm_scatter.o \ - drm_memory_debug.o ati_pcigart.o + drm_memory_debug.o ati_pcigart.o drm_sman.o \ + drm_hashtab.o drm_mm.o tdfx-objs := tdfx_drv.o r128-objs := r128_drv.o r128_cce.o r128_state.o r128_irq.o mga-objs := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o @@ -19,11 +20,11 @@ i810-objs := i810_drv.o i810_dma.o i830-objs := i830_drv.o i830_dma.o i830_irq.o i915-objs := i915_drv.o i915_dma.o i915_irq.o i915_mem.o radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o r300_cmdbuf.o -sis-objs := sis_drv.o sis_ds.o sis_mm.o +sis-objs := sis_drv.o sis_mm.o ffb-objs := ffb_drv.o ffb_context.o savage-objs := savage_drv.o savage_bci.o savage_state.o -via-objs := via_irq.o via_drv.o via_ds.o via_map.o via_mm.o via_dma.o via_verifier.o \ - via_video.o via_dmablit.o +via-objs := via_irq.o via_drv.o via_map.o via_mm.o via_dma.o via_verifier.o \ + via_video.o via_dmablit.o mach64-objs := mach64_drv.o mach64_dma.o mach64_irq.o mach64_state.o nv-objs := nv_drv.o diff --git a/linux-core/drmP.h b/linux-core/drmP.h index b2295b99..ec72ebca 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -147,7 +147,9 @@ #define DRM_MEM_CTXBITMAP 18 #define DRM_MEM_STUB 19 #define DRM_MEM_SGLISTS 20 -#define DRM_MEM_CTXLIST 21 +#define DRM_MEM_CTXLIST 21 +#define DRM_MEM_MM 22 +#define DRM_MEM_HASHTAB 23 #define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) @@ -533,6 +535,24 @@ typedef struct ati_pcigart_info { drm_local_map_t mapping; } drm_ati_pcigart_info; +/* + * Generic memory manager structs + */ + +typedef struct drm_mm_node { + struct list_head fl_entry; + struct list_head ml_entry; + int free; + unsigned long start; + unsigned long size; + void *private; +} drm_mm_node_t; + +typedef struct drm_mm { + drm_mm_node_t root_node; +} drm_mm_t; + + /** * DRM driver structure. This structure represent the common code for * a family of cards. There will one drm_device for each card present @@ -1017,6 +1037,19 @@ extern struct class_device *drm_sysfs_device_add(struct drm_sysfs_class *cs, drm_head_t * head); extern void drm_sysfs_device_remove(struct class_device *class_dev); +/* + * Basic memory manager support (drm_mm.c) + */ + +extern drm_mm_node_t * drm_mm_get_block(drm_mm_node_t * parent, unsigned long size, + unsigned alignment); +extern void drm_mm_put_block(drm_mm_t *mm, drm_mm_node_t *cur); +extern drm_mm_node_t *drm_mm_search_free(const drm_mm_t *mm, unsigned long size, + unsigned alignment, int best_match); +extern int drm_mm_init(drm_mm_t *mm, unsigned long start, unsigned long size); +extern void drm_mm_takedown(drm_mm_t *mm); + + /* Inline replacements for DRM_IOREMAP macros */ static __inline__ void drm_core_ioremap(struct drm_map *map, struct drm_device *dev) diff --git a/linux-core/drm_hashtab.c b/linux-core/drm_hashtab.c new file mode 100644 index 00000000..b61d79ba --- /dev/null +++ b/linux-core/drm_hashtab.c @@ -0,0 +1,185 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. + * 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 + * THE COPYRIGHT HOLDERS, AUTHORS 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. + * + * + **************************************************************************/ +/* + * Simple open hash tab implementation. + * + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#include "drmP.h" +#include "drm_hashtab.h" +#include <linux/hash.h> + +int drm_ht_create(drm_open_hash_t * ht, unsigned int order) +{ + unsigned int i; + + ht->size = 1 << order; + ht->order = order; + ht->fill = 0; + ht->table = vmalloc(ht->size * sizeof(*ht->table)); + if (!ht->table) { + DRM_ERROR("Out of memory for hash table\n"); + return -ENOMEM; + } + for (i = 0; i < ht->size; ++i) { + INIT_LIST_HEAD(&ht->table[i]); + } + return 0; +} + +void drm_ht_verbose_list(drm_open_hash_t * ht, unsigned long key) +{ + drm_hash_item_t *entry; + struct list_head *list, *h_list; + unsigned int hashed_key; + int count = 0; + + hashed_key = hash_long(key, ht->order); + DRM_DEBUG("Key is 0x%08lx, Hashed key is 0x%08x\n", key, hashed_key); + h_list = &ht->table[hashed_key]; + list_for_each(list, h_list) { + entry = list_entry(list, drm_hash_item_t, head); + DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key); + } +} + +static struct list_head +*drm_ht_find_key(drm_open_hash_t * ht, unsigned long key, int *found) +{ + drm_hash_item_t *entry; + struct list_head *list, *h_list, *ret; + unsigned int hashed_key; + + hashed_key = hash_long(key, ht->order); + + *found = FALSE; + h_list = &ht->table[hashed_key]; + ret = h_list; + list_for_each(list, h_list) { + entry = list_entry(list, drm_hash_item_t, head); + if (entry->key == key) { + ret = list; + *found = TRUE; + break; + } + if (entry->key > key) { + ret = list; + break; + } + } + return ret; +} + +int drm_ht_insert_item(drm_open_hash_t * ht, drm_hash_item_t * item) +{ + int found; + struct list_head *list; + + list = drm_ht_find_key(ht, item->key, &found); + if (found) { + return -EINVAL; + } else { + list_add_tail(&item->head, list); + ht->fill++; + } + return 0; +} + +/* + * Just insert an item and return any "bits" bit key that hasn't been used before. + */ + +int +drm_ht_just_insert_please(drm_open_hash_t * ht, drm_hash_item_t * item, + unsigned long seed, int bits) +{ + int ret; + unsigned long mask = (1 << bits) - 1; + unsigned long first; + + item->key = hash_long(seed, bits); + first = item->key; + do { + ret = drm_ht_insert_item(ht, item); + if (ret) + item->key = item->key++ & mask; + } while (ret && (item->key != first)); + + if (ret) { + DRM_ERROR("Available key bit space exhausted\n"); + return -EINVAL; + } + return 0; +} + +int +drm_ht_find_item(drm_open_hash_t * ht, unsigned long key, + drm_hash_item_t ** item) +{ + int found; + struct list_head *list; + + list = drm_ht_find_key(ht, key, &found); + if (!found) { + return -1; + } else { + *item = list_entry(list, drm_hash_item_t, head); + return 0; + } +} + +int drm_ht_remove_key(drm_open_hash_t * ht, unsigned long key) +{ + int found; + struct list_head *list; + + list = drm_ht_find_key(ht, key, &found); + if (found) { + list_del_init(list); + ht->fill--; + return 0; + } + return -1; +} + +int drm_ht_remove_item(drm_open_hash_t * ht, drm_hash_item_t * item) +{ + list_del_init(&item->head); + ht->fill--; + return 0; +} + +void drm_ht_remove(drm_open_hash_t * ht) +{ + if (ht->table) { + vfree(ht->table); + ht->table = NULL; + } +} diff --git a/linux-core/drm_hashtab.h b/linux-core/drm_hashtab.h new file mode 100644 index 00000000..3154b1a8 --- /dev/null +++ b/linux-core/drm_hashtab.h @@ -0,0 +1,63 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. + * 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 + * THE COPYRIGHT HOLDERS, AUTHORS 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. + * + * + **************************************************************************/ +/* + * Simple open hash tab implementation. + * + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#ifndef DRM_HASHTAB_H +#define DRM_HASHTAB_H + +typedef struct drm_hash_item { + struct list_head head; + unsigned long key; +} drm_hash_item_t; + +typedef struct drm_open_hash { + unsigned int size; + unsigned int order; + unsigned int fill; + struct list_head *table; +} drm_open_hash_t; + +extern int drm_ht_create(drm_open_hash_t * ht, unsigned int order); +extern int drm_ht_insert_item(drm_open_hash_t * ht, drm_hash_item_t * item); +extern int drm_ht_just_insert_please(drm_open_hash_t * ht, + drm_hash_item_t * item, unsigned long seed, + int bits); +extern int drm_ht_find_item(drm_open_hash_t * ht, unsigned long key, + drm_hash_item_t ** item); + +extern void drm_ht_verbose_list(drm_open_hash_t * ht, unsigned long key); +extern int drm_ht_remove_key(drm_open_hash_t * ht, unsigned long key); +extern int drm_ht_remove_item(drm_open_hash_t * ht, drm_hash_item_t * item); +extern void drm_ht_remove(drm_open_hash_t * ht); + +#endif diff --git a/linux-core/drm_mm.c b/linux-core/drm_mm.c new file mode 100644 index 00000000..3063c9eb --- /dev/null +++ b/linux-core/drm_mm.c @@ -0,0 +1,201 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., 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 + * THE COPYRIGHT HOLDERS, AUTHORS 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. + * + * + **************************************************************************/ + +/* + * Generic simple memory manager implementation. Intended to be used as a base + * class implementation for more advanced memory managers. + * + * Note that the algorithm used is quite simple and there might be substantial + * performance gains if a smarter free list is implemented. Currently it is just an + * unordered stack of free regions. This could easily be improved if an RB-tree + * is used instead. At least if we expect heavy fragmentation. + * + * Aligned allocations can also see improvement. + * + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#include "drmP.h" + +drm_mm_node_t *drm_mm_get_block(drm_mm_node_t * parent, + unsigned long size, unsigned alignment) +{ + + drm_mm_node_t *child; + + if (alignment) + size += alignment - 1; + + if (parent->size == size) { + list_del_init(&parent->fl_entry); + parent->free = FALSE; + return parent; + } else { + child = (drm_mm_node_t *) drm_alloc(sizeof(*child), DRM_MEM_MM); + if (!child) + return NULL; + + INIT_LIST_HEAD(&child->ml_entry); + INIT_LIST_HEAD(&child->fl_entry); + + child->free = FALSE; + child->size = size; + child->start = parent->start; + + list_add_tail(&child->ml_entry, &parent->ml_entry); + parent->size -= size; + parent->start += size; + } + return child; +} + +/* + * Put a block. Merge with the previous and / or next block if they are free. + * Otherwise add to the free stack. + */ + +void drm_mm_put_block(drm_mm_t * mm, drm_mm_node_t * cur) +{ + + drm_mm_node_t *list_root = &mm->root_node; + struct list_head *cur_head = &cur->ml_entry; + struct list_head *root_head = &list_root->ml_entry; + drm_mm_node_t *prev_node = NULL; + drm_mm_node_t *next_node; + + int merged = FALSE; + + if (cur_head->prev != root_head) { + prev_node = list_entry(cur_head->prev, drm_mm_node_t, ml_entry); + if (prev_node->free) { + prev_node->size += cur->size; + merged = TRUE; + } + } + if (cur_head->next != root_head) { + next_node = list_entry(cur_head->next, drm_mm_node_t, ml_entry); + if (next_node->free) { + if (merged) { + prev_node->size += next_node->size; + list_del(&next_node->ml_entry); + list_del(&next_node->fl_entry); + drm_free(next_node, sizeof(*next_node), + DRM_MEM_MM); + } else { + next_node->size += cur->size; + next_node->start = cur->start; + merged = TRUE; + } + } + } + if (!merged) { + cur->free = TRUE; + list_add(&cur->fl_entry, &list_root->fl_entry); + } else { + list_del(&cur->ml_entry); + drm_free(cur, sizeof(*cur), DRM_MEM_MM); + } +} + +drm_mm_node_t *drm_mm_search_free(const drm_mm_t * mm, + unsigned long size, + unsigned alignment, int best_match) +{ + struct list_head *list; + const struct list_head *free_stack = &mm->root_node.fl_entry; + drm_mm_node_t *entry; + drm_mm_node_t *best; + unsigned long best_size; + + best = NULL; + best_size = ~0UL; + + if (alignment) + size += alignment - 1; + + list_for_each(list, free_stack) { + entry = list_entry(list, drm_mm_node_t, fl_entry); + if (entry->size >= size) { + if (!best_match) + return entry; + if (size < best_size) { + best = entry; + best_size = entry->size; + } + } + } + + return best; +} + +int drm_mm_init(drm_mm_t * mm, unsigned long start, unsigned long size) +{ + drm_mm_node_t *child; + + INIT_LIST_HEAD(&mm->root_node.ml_entry); + INIT_LIST_HEAD(&mm->root_node.fl_entry); + child = (drm_mm_node_t *) drm_alloc(sizeof(*child), DRM_MEM_MM); + if (!child) + return -ENOMEM; + + INIT_LIST_HEAD(&child->ml_entry); + INIT_LIST_HEAD(&child->fl_entry); + + child->start = start; + child->size = size; + child->free = TRUE; + + list_add(&child->fl_entry, &mm->root_node.fl_entry); + list_add(&child->ml_entry, &mm->root_node.ml_entry); + + return 0; +} + +EXPORT_SYMBOL(drm_mm_init); + +void drm_mm_takedown(drm_mm_t * mm) +{ + struct list_head *bnode = mm->root_node.fl_entry.next; + drm_mm_node_t *entry; + + entry = list_entry(bnode, drm_mm_node_t, fl_entry); + + if (entry->ml_entry.next != &mm->root_node.ml_entry || + entry->fl_entry.next != &mm->root_node.fl_entry) { + DRM_ERROR("Memory manager not clean. Delaying takedown\n"); + return; + } + + list_del(&entry->fl_entry); + list_del(&entry->ml_entry); + + drm_free(entry, sizeof(*entry), DRM_MEM_MM); +} + +EXPORT_SYMBOL(drm_mm_takedown); diff --git a/linux-core/drm_sman.c b/linux-core/drm_sman.c new file mode 100644 index 00000000..96557932 --- /dev/null +++ b/linux-core/drm_sman.c @@ -0,0 +1,352 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck., ND., USA. + * 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 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 + * THE COPYRIGHT HOLDERS, AUTHORS 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * + **************************************************************************/ +/* + * Simple memory manager interface that keeps track on allocate regions on a + * per "owner" basis. All regions associated with an "owner" can be released + * with a simple call. Typically if the "owner" exists. The owner is any + * "unsigned long" identifier. Can typically be a pointer to a file private + * struct or a context identifier. + * + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#include "drm_sman.h" + +typedef struct drm_owner_item { + drm_hash_item_t owner_hash; + struct list_head sman_list; + struct list_head mem_blocks; +} drm_owner_item_t; + +void drm_sman_takedown(drm_sman_t * sman) +{ + drm_ht_remove(&sman->user_hash_tab); + drm_ht_remove(&sman->owner_hash_tab); + if (sman->mm) + drm_free(sman->mm, sman->num_managers * sizeof(*sman->mm), + DRM_MEM_MM); +} + +EXPORT_SYMBOL(drm_sman_takedown); + +int +drm_sman_init(drm_sman_t * sman, unsigned int num_managers, + unsigned int user_order, unsigned int owner_order) +{ + int ret = 0; + + sman->mm = (drm_sman_mm_t *) drm_calloc(num_managers, sizeof(*sman->mm), + DRM_MEM_MM); + if (!sman->mm) { + ret = -ENOMEM; + goto out; + } + sman->num_managers = num_managers; + INIT_LIST_HEAD(&sman->owner_items); + ret = drm_ht_create(&sman->owner_hash_tab, owner_order); + if (ret) + goto out1; + ret = drm_ht_create(&sman->user_hash_tab, user_order); + if (!ret) + goto out; + + drm_ht_remove(&sman->owner_hash_tab); + out1: + drm_free(sman->mm, num_managers * sizeof(*sman->mm), DRM_MEM_MM); + out: + return ret; +} + +EXPORT_SYMBOL(drm_sman_init); + +static void *drm_sman_mm_allocate(void *private, unsigned long size, + unsigned alignment) +{ + drm_mm_t *mm = (drm_mm_t *) private; + drm_mm_node_t *tmp; + + tmp = drm_mm_search_free(mm, size, alignment, TRUE); + if (!tmp) { + return NULL; + } + tmp = drm_mm_get_block(tmp, size, alignment); + return tmp; +} + +static void drm_sman_mm_free(void *private, void *ref) +{ + drm_mm_t *mm = (drm_mm_t *) private; + drm_mm_node_t *node = (drm_mm_node_t *) ref; + + drm_mm_put_block(mm, node); +} + +static void drm_sman_mm_destroy(void *private) +{ + drm_mm_t *mm = (drm_mm_t *) private; + drm_mm_takedown(mm); + drm_free(mm, sizeof(*mm), DRM_MEM_MM); +} + +unsigned long drm_sman_mm_offset(void *private, void *ref) +{ + drm_mm_node_t *node = (drm_mm_node_t *) ref; + return node->start; +} + +int +drm_sman_set_range(drm_sman_t * sman, unsigned int manager, + unsigned long start, unsigned long size) +{ + drm_sman_mm_t *sman_mm; + drm_mm_t *mm; + int ret; + + BUG_ON(manager >= sman->num_managers); + + sman_mm = &sman->mm[manager]; + mm = drm_calloc(1, sizeof(*mm), DRM_MEM_MM); + if (!mm) { + return -ENOMEM; + } + sman_mm->private = mm; + ret = drm_mm_init(mm, start, size); + + if (ret) { + drm_free(mm, sizeof(*mm), DRM_MEM_MM); + return ret; + } + + sman_mm->allocate = drm_sman_mm_allocate; + sman_mm->free = drm_sman_mm_free; + sman_mm->destroy = drm_sman_mm_destroy; + sman_mm->offset = drm_sman_mm_offset; + + return 0; +} + +EXPORT_SYMBOL(drm_sman_set_range); + +int +drm_sman_set_manager(drm_sman_t * sman, unsigned int manager, + drm_sman_mm_t * allocator) +{ + BUG_ON(manager >= sman->num_managers); + sman->mm[manager] = *allocator; + + return 0; +} + +static drm_owner_item_t + * drm_sman_get_owner_item(drm_sman_t * sman, unsigned long owner) +{ + int ret; + drm_hash_item_t *owner_hash_item; + drm_owner_item_t *owner_item; + + ret = drm_ht_find_item(&sman->owner_hash_tab, owner, &owner_hash_item); + if (!ret) { + return list_entry(owner_hash_item, drm_owner_item_t, + owner_hash); + } + + owner_item = drm_calloc(1, sizeof(*owner_item), DRM_MEM_MM); + if (!owner_item) + goto out; + + INIT_LIST_HEAD(&owner_item->mem_blocks); + owner_item->owner_hash.key = owner; + if (drm_ht_insert_item(&sman->owner_hash_tab, &owner_item->owner_hash)) + goto out1; + + list_add_tail(&owner_item->sman_list, &sman->owner_items); + return owner_item; + + out1: + drm_free(owner_item, sizeof(*owner_item), DRM_MEM_MM); + out: + return NULL; +} + +drm_memblock_item_t *drm_sman_alloc(drm_sman_t * sman, unsigned int manager, + unsigned long size, unsigned alignment, + unsigned long owner) +{ + void *tmp; + drm_sman_mm_t *sman_mm; + drm_owner_item_t *owner_item; + drm_memblock_item_t *memblock; + + BUG_ON(manager >= sman->num_managers); + + sman_mm = &sman->mm[manager]; + tmp = sman_mm->allocate(sman_mm->private, size, alignment); + + if (!tmp) { + return NULL; + } + + memblock = drm_calloc(1, sizeof(*memblock), DRM_MEM_MM); + + if (!memblock) + goto out; + + memblock->mm_info = tmp; + memblock->mm = sman_mm; + memblock->sman = sman; + + if (drm_ht_just_insert_please + (&sman->user_hash_tab, &memblock->user_hash, + (unsigned long)memblock, 32)) + goto out1; + + owner_item = drm_sman_get_owner_item(sman, owner); + if (!owner_item) + goto out2; + + list_add_tail(&memblock->owner_list, &owner_item->mem_blocks); + + return memblock; + + out2: + drm_ht_remove_item(&sman->user_hash_tab, &memblock->user_hash); + out1: + drm_free(memblock, sizeof(*memblock), DRM_MEM_MM); + out: + sman_mm->free(sman_mm->private, tmp); + + return NULL; +} + +EXPORT_SYMBOL(drm_sman_alloc); + +static void drm_sman_free(drm_memblock_item_t * item) +{ + drm_sman_t *sman = item->sman; + + list_del(&item->owner_list); + drm_ht_remove_item(&sman->user_hash_tab, &item->user_hash); + item->mm->free(item->mm->private, item->mm_info); + drm_free(item, sizeof(*item), DRM_MEM_MM); +} + +int drm_sman_free_key(drm_sman_t * sman, unsigned int key) +{ + drm_hash_item_t *hash_item; + drm_memblock_item_t *memblock_item; + + if (drm_ht_find_item(&sman->user_hash_tab, key, &hash_item)) + return -EINVAL; + + memblock_item = list_entry(hash_item, drm_memblock_item_t, user_hash); + drm_sman_free(memblock_item); + return 0; +} + +EXPORT_SYMBOL(drm_sman_free_key); + +static void +drm_sman_remove_owner(drm_sman_t * sman, drm_owner_item_t * owner_item) +{ + list_del(&owner_item->sman_list); + drm_ht_remove_item(&sman->owner_hash_tab, &owner_item->owner_hash); + drm_free(owner_item, sizeof(*owner_item), DRM_MEM_MM); +} + +int drm_sman_owner_clean(drm_sman_t * sman, unsigned long owner) +{ + + drm_hash_item_t *hash_item; + drm_owner_item_t *owner_item; + + if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) { + return -1; + } + + owner_item = list_entry(hash_item, drm_owner_item_t, owner_hash); + if (owner_item->mem_blocks.next == &owner_item->mem_blocks) { + drm_sman_remove_owner(sman, owner_item); + return -1; + } + + return 0; +} + +EXPORT_SYMBOL(drm_sman_owner_clean); + +static void +drm_sman_do_owner_cleanup(drm_sman_t * sman, drm_owner_item_t * owner_item) +{ + drm_memblock_item_t *entry, *next; + + list_for_each_entry_safe(entry, next, &owner_item->mem_blocks, + owner_list) { + drm_sman_free(entry); + } + drm_sman_remove_owner(sman, owner_item); +} + +void drm_sman_owner_cleanup(drm_sman_t * sman, unsigned long owner) +{ + + drm_hash_item_t *hash_item; + drm_owner_item_t *owner_item; + + if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) { + + return; + } + + owner_item = list_entry(hash_item, drm_owner_item_t, owner_hash); + drm_sman_do_owner_cleanup(sman, owner_item); +} + +EXPORT_SYMBOL(drm_sman_owner_cleanup); + +void drm_sman_cleanup(drm_sman_t * sman) +{ + drm_owner_item_t *entry, *next; + unsigned int i; + drm_sman_mm_t *sman_mm; + + list_for_each_entry_safe(entry, next, &sman->owner_items, sman_list) { + drm_sman_do_owner_cleanup(sman, entry); + } + if (sman->mm) { + for (i = 0; i < sman->num_managers; ++i) { + sman_mm = &sman->mm[i]; + if (sman_mm->private) { + sman_mm->destroy(sman_mm->private); + sman_mm->private = NULL; + } + } + } +} + +EXPORT_SYMBOL(drm_sman_cleanup); diff --git a/linux-core/drm_sman.h b/linux-core/drm_sman.h new file mode 100644 index 00000000..7e537d94 --- /dev/null +++ b/linux-core/drm_sman.h @@ -0,0 +1,176 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. + * 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 + * THE COPYRIGHT HOLDERS, AUTHORS 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. + * + * + **************************************************************************/ +/* + * Simple memory MANager interface that keeps track on allocate regions on a + * per "owner" basis. All regions associated with an "owner" can be released + * with a simple call. Typically if the "owner" exists. The owner is any + * "unsigned long" identifier. Can typically be a pointer to a file private + * struct or a context identifier. + * + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#ifndef DRM_SMAN_H +#define DRM_SMAN_H + +#include "drmP.h" +#include "drm_hashtab.h" + +/* + * A class that is an abstration of a simple memory allocator. + * The sman implementation provides a default such allocator + * using the drm_mm.c implementation. But the user can replace it. + * See the SiS implementation, which may use the SiS FB kernel module + * for memory management. + */ + +typedef struct drm_sman_mm { + /* private info. If allocated, needs to be destroyed by the destroy + function */ + void *private; + + /* Allocate a memory block with given size and alignment. + Return an opaque reference to the memory block */ + + void *(*allocate) (void *private, unsigned long size, + unsigned alignment); + + /* Free a memory block. "ref" is the opaque reference that we got from + the "alloc" function */ + + void (*free) (void *private, void *ref); + + /* Free all resources associated with this allocator */ + + void (*destroy) (void *private); + + /* Return a memory offset from the opaque reference returned from the + "alloc" function */ + + unsigned long (*offset) (void *private, void *ref); +} drm_sman_mm_t; + +typedef struct drm_memblock_item { + struct list_head owner_list; + drm_hash_item_t user_hash; + void *mm_info; + drm_sman_mm_t *mm; + struct drm_sman *sman; +} drm_memblock_item_t; + +typedef struct drm_sman { + drm_sman_mm_t *mm; + int num_managers; + drm_open_hash_t owner_hash_tab; + drm_open_hash_t user_hash_tab; + struct list_head owner_items; +} drm_sman_t; + +/* + * Take down a memory manager. This function should only be called after a + * successful init and after a call to drm_sman_cleanup. + */ + +extern void drm_sman_takedown(drm_sman_t * sman); + +/* + * Allocate structures for a manager. + * num_managers are the number of memory pools to manage. (VRAM, AGP, ....) + * user_order is the log2 of the number of buckets in the user hash table. + * set this to approximately log2 of the max number of memory regions + * that will be allocated for _all_ pools together. + * owner_order is the log2 of the number of buckets in the owner hash table. + * set this to approximately log2 of + * the number of client file connections that will + * be using the manager. + * + */ + +extern int drm_sman_init(drm_sman_t * sman, unsigned int num_managers, + unsigned int user_order, unsigned int owner_order); + +/* + * Initialize a drm_mm.c allocator. Should be called only once for each + * manager unless a customized allogator is used. + */ + +extern int drm_sman_set_range(drm_sman_t * sman, unsigned int manager, + unsigned long start, unsigned long size); + +/* + * Initialize a customized allocator for one of the managers. + * (See the SiS module). The object pointed to by "allocator" is copied, + * so it can be destroyed after this call. + */ + +extern int drm_sman_set_manager(drm_sman_t * sman, unsigned int mananger, + drm_sman_mm_t * allocator); + +/* + * Allocate a memory block. Aligment is not implemented yet. + */ + +extern drm_memblock_item_t *drm_sman_alloc(drm_sman_t * sman, + unsigned int manager, + unsigned long size, + unsigned alignment, + unsigned long owner); +/* + * Free a memory block identified by its user hash key. + */ + +extern int drm_sman_free_key(drm_sman_t * sman, unsigned int key); + +/* + * returns TRUE iff there are no stale memory blocks associated with this owner. + * Typically called to determine if we need to idle the hardware and call + * drm_sman_owner_cleanup. If there are no stale memory blocks, it removes all + * resources associated with owner. + */ + +extern int drm_sman_owner_clean(drm_sman_t * sman, unsigned long owner); + +/* + * Frees all stale memory blocks associated with this owner. Note that this + * requires that the hardware is finished with all blocks, so the graphics engine + * should be idled before this call is made. This function also frees + * any resources associated with "owner" and should be called when owner + * is not going to be referenced anymore. + */ + +extern void drm_sman_owner_cleanup(drm_sman_t * sman, unsigned long owner); + +/* + * Frees all stale memory blocks associated with the memory manager. + * See idling above. + */ + +extern void drm_sman_cleanup(drm_sman_t * sman); + +#endif diff --git a/linux-core/sis_drv.c b/linux-core/sis_drv.c index 96c143a5..5d55357c 100644 --- a/linux-core/sis_drv.c +++ b/linux-core/sis_drv.c @@ -36,12 +36,44 @@ static struct pci_device_id pciidlist[] = { sis_PCI_IDS }; + +static int sis_driver_load(drm_device_t *dev, unsigned long chipset) +{ + drm_sis_private_t *dev_priv; + int ret; + + dev_priv = drm_calloc(1, sizeof(drm_sis_private_t), DRM_MEM_DRIVER); + if (dev_priv == NULL) + return DRM_ERR(ENOMEM); + + dev->dev_private = (void *)dev_priv; + dev_priv->chipset = chipset; + ret = drm_sman_init(&dev_priv->sman, 2, 12, 8); + if (ret) { + drm_free(dev_priv, sizeof(dev_priv), DRM_MEM_DRIVER); + } + + return ret; +} + +static int sis_driver_unload(drm_device_t *dev) +{ + drm_sis_private_t *dev_priv = dev->dev_private; + + drm_sman_takedown(&dev_priv->sman); + drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); + + return 0; +} + + static int probe(struct pci_dev *pdev, const struct pci_device_id *ent); static struct drm_driver driver = { .driver_features = DRIVER_USE_AGP | DRIVER_USE_MTRR, - .context_ctor = sis_init_context, + .load = sis_driver_load, + .unload = sis_driver_unload, .context_dtor = sis_final_context, - .reclaim_buffers = drm_core_reclaim_buffers, + .reclaim_buffers = NULL, .get_map_ofs = drm_core_get_map_ofs, .get_reg_ofs = drm_core_get_reg_ofs, .ioctls = sis_ioctls, diff --git a/linux-core/sis_mm.c b/linux-core/sis_mm.c new file mode 100644 index 00000000..ee924535 --- /dev/null +++ b/linux-core/sis_mm.c @@ -0,0 +1,286 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. + * 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 + * THE COPYRIGHT HOLDERS, AUTHORS 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. + * + * + **************************************************************************/ + +/* + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#include "drmP.h" +#include "sis_drm.h" +#include "sis_drv.h" + +#if defined(__linux__) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +#include <video/sisfb.h> +#else +#include <linux/sisfb.h> +#endif +#endif + +#define VIDEO_TYPE 0 +#define AGP_TYPE 1 + +#define SIS_MM_ALIGN_SHIFT 4 +#define SIS_MM_ALIGN_MASK ( (1 << SIS_MM_ALIGN_SHIFT) - 1) + +#if defined(__linux__) && defined(CONFIG_FB_SIS) +/* fb management via fb device */ + +#define SIS_MM_ALIGN_SHIFT 0 +#define SIS_MM_ALIGN_MASK 0 + +static void *sis_sman_mm_allocate(void *private, unsigned long size, + unsigned alignment) +{ + struct sis_memreq req; + + req.size = size; + sis_malloc(&req); + if (req.size == 0) + return NULL; + else + return (void *)~req.offset; +} + +static void sis_sman_mm_free(void *private, void *ref) +{ + sis_free(~((unsigned long)ref)); +} + +static void sis_sman_mm_destroy(void *private) +{ + ; +} + +unsigned long sis_sman_mm_offset(void *private, void *ref) +{ + return ~((unsigned long)ref); +} + +#endif + +static int sis_fb_init(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_sis_private_t *dev_priv = dev->dev_private; + drm_sis_fb_t fb; + int ret; + + DRM_COPY_FROM_USER_IOCTL(fb, (drm_sis_fb_t __user *) data, sizeof(fb)); + + down(&dev->struct_sem); +#if defined(__linux__) && defined(CONFIG_FB_SIS) + { + drm_sman_mm_t sman_mm; + sman_mm.private = (void *)0xFFFFFFFF; + sman_mm.allocate = sis_sman_mm_allocate; + sman_mm.free = sis_sman_mm_free; + sman_mm.destroy = sis_sman_mm_destroy; + sman_mm.offset = sis_sman_mm_offset; + ret = + drm_sman_set_manager(&dev_priv->sman, VIDEO_TYPE, &sman_mm); + } +#else + ret = drm_sman_set_range(&dev_priv->sman, VIDEO_TYPE, 0, + fb.size >> SIS_MM_ALIGN_SHIFT); +#endif + + if (ret) { + DRM_ERROR("VRAM memory manager initialisation error\n"); + up(&dev->struct_sem); + return ret; + } + + dev_priv->vram_initialized = TRUE; + dev_priv->vram_offset = fb.offset; + + up(&dev->struct_sem); + DRM_DEBUG("offset = %u, size = %u", fb.offset, fb.size); + + return 0; +} + +static int sis_drm_alloc(drm_device_t * dev, drm_file_t * priv, + unsigned long data, int pool) +{ + drm_sis_private_t *dev_priv = dev->dev_private; + drm_sis_mem_t __user *argp = (drm_sis_mem_t __user *) data; + drm_sis_mem_t mem; + int retval = 0; + drm_memblock_item_t *item; + + DRM_COPY_FROM_USER_IOCTL(mem, argp, sizeof(mem)); + + down(&dev->struct_sem); + + if (FALSE == ((pool == 0) ? dev_priv->vram_initialized : + dev_priv->agp_initialized)) { + DRM_ERROR + ("Attempt to allocate from uninitialized memory manager.\n"); + return DRM_ERR(EINVAL); + } + + mem.size = (mem.size + SIS_MM_ALIGN_MASK) >> SIS_MM_ALIGN_SHIFT; + item = drm_sman_alloc(&dev_priv->sman, pool, mem.size, 0, + (unsigned long)priv); + + up(&dev->struct_sem); + if (item) { + mem.offset = ((pool == 0) ? + dev_priv->vram_offset : dev_priv->agp_offset) + + (item->mm-> + offset(item->mm, item->mm_info) << SIS_MM_ALIGN_SHIFT); + mem.free = item->user_hash.key; + mem.size = mem.size << SIS_MM_ALIGN_SHIFT; + } else { + mem.offset = 0; + mem.size = 0; + mem.free = 0; + retval = DRM_ERR(ENOMEM); + } + + DRM_COPY_TO_USER_IOCTL(argp, mem, sizeof(mem)); + + DRM_DEBUG("alloc %d, size = %d, offset = %d\n", pool, mem.size, + mem.offset); + + return retval; +} + +static int sis_drm_free(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_sis_private_t *dev_priv = dev->dev_private; + drm_sis_mem_t mem; + int ret; + + DRM_COPY_FROM_USER_IOCTL(mem, (drm_sis_mem_t __user *) data, + sizeof(mem)); + + down(&dev->struct_sem); + ret = drm_sman_free_key(&dev_priv->sman, mem.free); + up(&dev->struct_sem); + DRM_DEBUG("free = 0x%lx\n", mem.free); + + return ret; +} + +static int sis_fb_alloc(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + return sis_drm_alloc(dev, priv, data, VIDEO_TYPE); +} + +static int sis_ioctl_agp_init(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_sis_private_t *dev_priv = dev->dev_private; + drm_sis_agp_t agp; + int ret; + dev_priv = dev->dev_private; + + DRM_COPY_FROM_USER_IOCTL(agp, (drm_sis_agp_t __user *) data, + sizeof(agp)); + down(&dev->struct_sem); + ret = drm_sman_set_range(&dev_priv->sman, AGP_TYPE, 0, + agp.size >> SIS_MM_ALIGN_SHIFT); + + if (ret) { + DRM_ERROR("AGP memory manager initialisation error\n"); + up(&dev->struct_sem); + return ret; + } + + dev_priv->agp_initialized = TRUE; + dev_priv->agp_offset = agp.offset; + up(&dev->struct_sem); + + DRM_DEBUG("offset = %u, size = %u", agp.offset, agp.size); + return 0; +} + +static int sis_ioctl_agp_alloc(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + + return sis_drm_alloc(dev, priv, data, AGP_TYPE); +} + +int sis_final_context(struct drm_device *dev, int context) +{ + if (dev->ctx_count == 1 && dev->dev_private) { + drm_sis_private_t *dev_priv = dev->dev_private; + + DRM_DEBUG("Last Context\n"); + down(&dev->struct_sem); + drm_sman_cleanup(&dev_priv->sman); + dev_priv->vram_initialized = FALSE; + dev_priv->agp_initialized = FALSE; + up(&dev->struct_sem); + } + return 1; +} + +void sis_reclaim_buffers_locked(drm_device_t * dev, struct file *filp) +{ + drm_sis_private_t *dev_priv = dev->dev_private; + drm_file_t *priv = filp->private_data; + + down(&dev->struct_sem); + if (drm_sman_owner_clean(&dev_priv->sman, (unsigned long)priv)) { + up(&dev->struct_sem); + return; + } + + if (dev->driver->dma_quiescent) { + dev->driver->dma_quiescent(dev); + } + + drm_sman_owner_cleanup(&dev_priv->sman, (unsigned long)priv); + up(&dev->struct_sem); + return; +} + +drm_ioctl_desc_t sis_ioctls[] = { + [DRM_IOCTL_NR(DRM_SIS_FB_ALLOC)] = {sis_fb_alloc, DRM_AUTH} + , + [DRM_IOCTL_NR(DRM_SIS_FB_FREE)] = {sis_drm_free, DRM_AUTH} + , + [DRM_IOCTL_NR(DRM_SIS_AGP_INIT)] = + {sis_ioctl_agp_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY} + , + [DRM_IOCTL_NR(DRM_SIS_AGP_ALLOC)] = {sis_ioctl_agp_alloc, DRM_AUTH} + , + [DRM_IOCTL_NR(DRM_SIS_AGP_FREE)] = {sis_drm_free, DRM_AUTH} + , + [DRM_IOCTL_NR(DRM_SIS_FB_INIT)] = + {sis_fb_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY} +}; + +int sis_max_ioctl = DRM_ARRAY_SIZE(sis_ioctls); diff --git a/linux-core/via_mm.c b/linux-core/via_mm.c new file mode 100644 index 00000000..2881a1d1 --- /dev/null +++ b/linux-core/via_mm.c @@ -0,0 +1,203 @@ +/* + * Copyright 2006 Tungsten Graphics Inc., Bismarck, ND., USA. + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/* + * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#include "drmP.h" +#include "via_drm.h" +#include "via_drv.h" +#include "drm_sman.h" + +#define VIA_MM_ALIGN_SHIFT 4 +#define VIA_MM_ALIGN_MASK ( (1 << VIA_MM_ALIGN_SHIFT) - 1) + +int via_agp_init(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_via_agp_t agp; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + int ret; + + DRM_COPY_FROM_USER_IOCTL(agp, (drm_via_agp_t __user *) data, + sizeof(agp)); + down(&dev->struct_sem); + ret = drm_sman_set_range(&dev_priv->sman, VIA_MEM_AGP, 0, + agp.size >> VIA_MM_ALIGN_SHIFT); + + if (ret) { + DRM_ERROR("AGP memory manager initialisation error\n"); + up(&dev->struct_sem); + return ret; + } + + dev_priv->agp_initialized = TRUE; + dev_priv->agp_offset = agp.offset; + up(&dev->struct_sem); + + DRM_DEBUG("offset = %u, size = %u", agp.offset, agp.size); + return 0; +} + +int via_fb_init(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_via_fb_t fb; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + int ret; + + DRM_COPY_FROM_USER_IOCTL(fb, (drm_via_fb_t __user *) data, sizeof(fb)); + + down(&dev->struct_sem); + ret = drm_sman_set_range(&dev_priv->sman, VIA_MEM_VIDEO, 0, + fb.size >> VIA_MM_ALIGN_SHIFT); + + if (ret) { + DRM_ERROR("VRAM memory manager initialisation error\n"); + up(&dev->struct_sem); + return ret; + } + + dev_priv->vram_initialized = TRUE; + dev_priv->vram_offset = fb.offset; + + up(&dev->struct_sem); + DRM_DEBUG("offset = %u, size = %u", fb.offset, fb.size); + + return 0; + +} + +int via_final_context(struct drm_device *dev, int context) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + + via_release_futex(dev_priv, context); + +#if defined(__linux__) + /* Linux specific until context tracking code gets ported to BSD */ + /* Last context, perform cleanup */ + if (dev->ctx_count == 1 && dev->dev_private) { + DRM_DEBUG("Last Context\n"); + if (dev->irq) + drm_irq_uninstall(dev); + via_cleanup_futex(dev_priv); + via_do_cleanup_map(dev); + + down(&dev->struct_sem); + drm_sman_cleanup(&dev_priv->sman); + dev_priv->vram_initialized = FALSE; + dev_priv->agp_initialized = FALSE; + up(&dev->struct_sem); + } +#endif + + return 1; +} + +int via_mem_alloc(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + + drm_via_mem_t mem; + int retval = 0; + drm_memblock_item_t *item; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + + DRM_COPY_FROM_USER_IOCTL(mem, (drm_via_mem_t __user *) data, + sizeof(mem)); + + if (mem.type > VIA_MEM_AGP) { + DRM_ERROR("Unknown memory type allocation\n"); + return DRM_ERR(EINVAL); + } + down(&dev->struct_sem); + if (FALSE == ((mem.type == VIA_MEM_VIDEO) ? dev_priv->vram_initialized : + dev_priv->agp_initialized)) { + DRM_ERROR + ("Attempt to allocate from uninitialized memory manager.\n"); + up(&dev->struct_sem); + return DRM_ERR(EINVAL); + } + + mem.size = (mem.size + VIA_MM_ALIGN_MASK) >> VIA_MM_ALIGN_SHIFT; + item = drm_sman_alloc(&dev_priv->sman, mem.type, mem.size, 0, + (unsigned long)priv); + up(&dev->struct_sem); + if (item) { + mem.offset = ((mem.type == VIA_MEM_VIDEO) ? + dev_priv->vram_offset : dev_priv->agp_offset) + + (item->mm-> + offset(item->mm, item->mm_info) << VIA_MM_ALIGN_SHIFT); + mem.index = item->user_hash.key; + mem.size = mem.size << VIA_MM_ALIGN_SHIFT; + } else { + mem.offset = 0; + mem.size = 0; + mem.index = 0; + DRM_ERROR("Video memory allocation failed\n"); + retval = DRM_ERR(ENOMEM); + } + DRM_COPY_TO_USER_IOCTL((drm_via_mem_t __user *) data, mem, sizeof(mem)); + + return retval; +} + +int via_mem_free(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_via_private_t *dev_priv = dev->dev_private; + drm_via_mem_t mem; + int ret; + + DRM_COPY_FROM_USER_IOCTL(mem, (drm_via_mem_t __user *) data, + sizeof(mem)); + + down(&dev->struct_sem); + ret = drm_sman_free_key(&dev_priv->sman, mem.index); + up(&dev->struct_sem); + DRM_DEBUG("free = 0x%lx\n", mem.index); + + return ret; +} + +void via_reclaim_buffers_locked(drm_device_t * dev, struct file *filp) +{ + drm_via_private_t *dev_priv = dev->dev_private; + drm_file_t *priv = filp->private_data; + + down(&dev->struct_sem); + if (drm_sman_owner_clean(&dev_priv->sman, (unsigned long)priv)) { + up(&dev->struct_sem); + return; + } + + if (dev->driver->dma_quiescent) { + dev->driver->dma_quiescent(dev); + } + + drm_sman_owner_cleanup(&dev_priv->sman, (unsigned long)priv); + up(&dev->struct_sem); + return; +} diff --git a/shared-core/sis_drv.h b/shared-core/sis_drv.h index e218e526..037bc49d 100644 --- a/shared-core/sis_drv.h +++ b/shared-core/sis_drv.h @@ -34,11 +34,32 @@ #define DRIVER_AUTHOR "SIS" #define DRIVER_NAME "sis" #define DRIVER_DESC "SIS 300/630/540" -#define DRIVER_DATE "20030826" +#define DRIVER_DATE "20060529" #define DRIVER_MAJOR 1 -#define DRIVER_MINOR 1 +#define DRIVER_MINOR 2 #define DRIVER_PATCHLEVEL 0 +#if defined(__linux__) +#define SIS_HAVE_CORE_MM +#endif + +#ifdef SIS_HAVE_CORE_MM + +#include "drm_sman.h" +typedef struct drm_sis_private { + drm_local_map_t *mmio; + unsigned idle_fault; + drm_sman_t sman; + unsigned long chipset; + int vram_initialized; + int agp_initialized; + unsigned long vram_offset; + unsigned long agp_offset; +} drm_sis_private_t; + +extern void sis_reclaim_buffers_locked(drm_device_t *dev, struct file *filp); + +#else #include "sis_ds.h" typedef struct drm_sis_private { @@ -47,6 +68,10 @@ typedef struct drm_sis_private { } drm_sis_private_t; extern int sis_init_context(drm_device_t * dev, int context); + +#endif + + extern int sis_final_context(drm_device_t * dev, int context); extern drm_ioctl_desc_t sis_ioctls[]; diff --git a/shared-core/via_drm.h b/shared-core/via_drm.h index dbaf857b..4be7e249 100644 --- a/shared-core/via_drm.h +++ b/shared-core/via_drm.h @@ -42,11 +42,11 @@ * backwards incompatibilities, (which should be avoided whenever possible). */ -#define VIA_DRM_DRIVER_DATE "20060111" +#define VIA_DRM_DRIVER_DATE "20060528" #define VIA_DRM_DRIVER_MAJOR 2 -#define VIA_DRM_DRIVER_MINOR 9 -#define VIA_DRM_DRIVER_PATCHLEVEL 1 +#define VIA_DRM_DRIVER_MINOR 10 +#define VIA_DRM_DRIVER_PATCHLEVEL 0 #define VIA_DRM_DRIVER_VERSION (((VIA_DRM_DRIVER_MAJOR) << 16) | (VIA_DRM_DRIVER_MINOR)) #define VIA_NR_SAREA_CLIPRECTS 8 diff --git a/shared-core/via_drv.c b/shared-core/via_drv.c index 2dc72ec1..a7509cff 100644 --- a/shared-core/via_drv.c +++ b/shared-core/via_drv.c @@ -46,7 +46,9 @@ static struct drm_driver driver = { DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL, .load = via_driver_load, .unload = via_driver_unload, +#ifndef VIA_HAVE_CORE_MM .context_ctor = via_init_context, +#endif .context_dtor = via_final_context, .vblank_wait = via_driver_vblank_wait, .irq_preinstall = via_driver_irq_preinstall, @@ -56,6 +58,9 @@ static struct drm_driver driver = { .dma_quiescent = via_driver_dma_quiescent, .dri_library_name = dri_library_name, .reclaim_buffers = drm_core_reclaim_buffers, +#ifdef VIA_HAVE_CORE_MM + .reclaim_buffers_locked = via_reclaim_buffers_locked, +#endif .get_map_ofs = drm_core_get_map_ofs, .get_reg_ofs = drm_core_get_reg_ofs, .ioctls = via_ioctls, diff --git a/shared-core/via_drv.h b/shared-core/via_drv.h index 0e29815b..2e9e10c3 100644 --- a/shared-core/via_drv.h +++ b/shared-core/via_drv.h @@ -24,6 +24,7 @@ #ifndef _VIA_DRV_H_ #define _VIA_DRV_H_ +#include "drm_sman.h" #define DRIVER_AUTHOR "Various" #define DRIVER_NAME "via" @@ -39,6 +40,7 @@ * the DMA blit code has been implemented for FreeBSD. */ #define VIA_HAVE_DMABLIT 1 +#define VIA_HAVE_CORE_MM 1 #endif #define VIA_PCI_BUF_SIZE 60000 @@ -88,6 +90,15 @@ typedef struct drm_via_private { uint32_t irq_enable_mask; uint32_t irq_pending_mask; int *irq_map; + /* Memory manager stuff */ +#ifdef VIA_HAVE_CORE_MM + unsigned idle_fault; + drm_sman_t sman; + int vram_initialized; + int agp_initialized; + unsigned long vram_offset; + unsigned long agp_offset; +#endif #ifdef VIA_HAVE_DMABLIT drm_via_blitq_t blit_queues[VIA_NUM_BLIT_ENGINES]; #endif @@ -121,7 +132,6 @@ extern int via_dma_blit( DRM_IOCTL_ARGS ); extern int via_driver_load(drm_device_t *dev, unsigned long chipset); extern int via_driver_unload(drm_device_t *dev); -extern int via_init_context(drm_device_t * dev, int context); extern int via_final_context(drm_device_t * dev, int context); extern int via_do_cleanup_map(drm_device_t * dev); @@ -140,6 +150,13 @@ extern void via_cleanup_futex(drm_via_private_t *dev_priv); extern void via_release_futex(drm_via_private_t *dev_priv, int context); extern int via_driver_irq_wait(drm_device_t * dev, unsigned int irq, int force_sequence, unsigned int *sequence); + +#ifdef VIA_HAVE_CORE_MM +extern void via_reclaim_buffers_locked(drm_device_t *dev, struct file *filp); +#else +extern int via_init_context(drm_device_t * dev, int context); +#endif + #ifdef VIA_HAVE_DMABLIT extern void via_dmablit_handler(drm_device_t *dev, int engine, int from_irq); extern void via_init_dmablit(drm_device_t *dev); diff --git a/shared-core/via_map.c b/shared-core/via_map.c index 2b653d75..71967d6c 100644 --- a/shared-core/via_map.c +++ b/shared-core/via_map.c @@ -99,6 +99,7 @@ int via_map_init(DRM_IOCTL_ARGS) int via_driver_load(drm_device_t *dev, unsigned long chipset) { drm_via_private_t *dev_priv; + int ret = 0; dev_priv = drm_calloc(1, sizeof(drm_via_private_t), DRM_MEM_DRIVER); if (dev_priv == NULL) @@ -109,13 +110,22 @@ int via_driver_load(drm_device_t *dev, unsigned long chipset) if (chipset == VIA_PRO_GROUP_A) dev_priv->pro_group_a = 1; - return 0; +#ifdef VIA_HAVE_CORE_MM + ret = drm_sman_init(&dev_priv->sman, 2, 12, 8); + if (ret) { + drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); + } +#endif + return ret; } int via_driver_unload(drm_device_t *dev) { drm_via_private_t *dev_priv = dev->dev_private; +#ifdef VIA_HAVE_CORE_MM + drm_sman_takedown(&dev_priv->sman); +#endif drm_free(dev_priv, sizeof(drm_via_private_t), DRM_MEM_DRIVER); return 0; |