diff options
| -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 | 194 | ||||
| -rw-r--r-- | linux-core/drm_hashtab.h | 64 | ||||
| -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, 1619 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..3be781df --- /dev/null +++ b/linux-core/drm_hashtab.c @@ -0,0 +1,194 @@ +/************************************************************************** + *  + * 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_HLIST_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 hlist_head *h_list; +        struct hlist_node *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]; +        hlist_for_each(list, h_list) { +                entry = hlist_entry(list, drm_hash_item_t, head); +                DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key); +        } +} + +static struct hlist_node +*drm_ht_find_key(drm_open_hash_t *ht, unsigned long key) +{ +        drm_hash_item_t *entry; +        struct hlist_head *h_list; +        struct hlist_node *list; +        unsigned int hashed_key; + +        hashed_key = hash_long(key, ht->order); +        h_list = &ht->table[hashed_key]; +        hlist_for_each(list, h_list) { +                entry = hlist_entry(list, drm_hash_item_t, head); +                if (entry->key == key) +                        return list; +                if (entry->key > key) +                        break; +        } +        return NULL; +} +         + +int +drm_ht_insert_item(drm_open_hash_t *ht, drm_hash_item_t *item) +{ +        drm_hash_item_t *entry; +        struct hlist_head *h_list; +        struct hlist_node *list, *parent; +        unsigned int hashed_key; +        unsigned long key = item->key; + +        hashed_key = hash_long(key, ht->order); +        h_list = &ht->table[hashed_key]; +        parent = NULL; +        hlist_for_each(list, h_list) { +                entry = hlist_entry(list, drm_hash_item_t, head); +                if (entry->key == key)  +                        return -1; +                if (entry->key > key) +                        break; +                parent = list; +        } +        if (parent) { +                hlist_add_after(parent, &item->head); +        } else { +                hlist_add_head(&item->head, h_list); +        } +        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 + 1) & 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)  +{ +        struct hlist_node *list; + +        list = drm_ht_find_key(ht, key); +        if (!list)  +                return -1; + +        *item = hlist_entry(list, drm_hash_item_t, head); +        return 0; +} +              +int  +drm_ht_remove_key(drm_open_hash_t *ht, unsigned long key) +{ +        struct hlist_node *list; +         +        list = drm_ht_find_key(ht, key); +        if (list) { +                hlist_del_init(list); +                ht->fill--; +                return 0; +        } +        return -1; +} + +int  +drm_ht_remove_item(drm_open_hash_t *ht, drm_hash_item_t *item) +{ +        hlist_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..7f457e74 --- /dev/null +++ b/linux-core/drm_hashtab.h @@ -0,0 +1,64 @@ +/************************************************************************** + *  + * Copyright 2006 Tungsten Graphics, Inc., Bismack, 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 hlist_node head; +        unsigned long key; +} drm_hash_item_t; + +typedef struct drm_open_hash{ +        unsigned int size; +        unsigned int order; +        unsigned int fill; +        struct hlist_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..a93760f9 --- /dev/null +++ b/linux-core/drm_mm.c @@ -0,0 +1,201 @@ +/************************************************************************** + *  + * 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. + * + *  + **************************************************************************/ + +/* + * 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; | 
