diff options
author | David Dawes <dawes@xfree86.org> | 2002-01-27 18:23:04 +0000 |
---|---|---|
committer | David Dawes <dawes@xfree86.org> | 2002-01-27 18:23:04 +0000 |
commit | 16bd14926e02e4dbc6e74689bdb3eb90f30a0233 (patch) | |
tree | 9d7cb6b98165c9e2a9091c41a39acca11d6fa162 /bsd-core | |
parent | f18a6d836b5e0081dff9217b44e88e74c421c576 (diff) |
Initial revision
Diffstat (limited to 'bsd-core')
-rw-r--r-- | bsd-core/ati_pcigart.c | 197 | ||||
-rw-r--r-- | bsd-core/drm_auth.c | 166 | ||||
-rw-r--r-- | bsd-core/drm_bufs.c | 1092 | ||||
-rw-r--r-- | bsd-core/drm_context.c | 730 | ||||
-rw-r--r-- | bsd-core/drm_dma.c | 605 | ||||
-rw-r--r-- | bsd-core/drm_drawable.c | 50 | ||||
-rw-r--r-- | bsd-core/drm_drv.c | 1160 | ||||
-rw-r--r-- | bsd-core/drm_fops.c | 223 | ||||
-rw-r--r-- | bsd-core/drm_ioctl.c | 237 | ||||
-rw-r--r-- | bsd-core/drm_lock.c | 244 | ||||
-rw-r--r-- | bsd-core/drm_memory.c | 433 | ||||
-rw-r--r-- | bsd-core/drm_scatter.c | 237 |
12 files changed, 5374 insertions, 0 deletions
diff --git a/bsd-core/ati_pcigart.c b/bsd-core/ati_pcigart.c new file mode 100644 index 00000000..8b486c10 --- /dev/null +++ b/bsd-core/ati_pcigart.c @@ -0,0 +1,197 @@ +/* ati_pcigart.h -- ATI PCI GART support -*- linux-c -*- + * Created: Wed Dec 13 21:52:19 2000 by gareth@valinux.com + * + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * 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, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (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 NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT 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: + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" + +#if PAGE_SIZE == 8192 +# define ATI_PCIGART_TABLE_ORDER 2 +# define ATI_PCIGART_TABLE_PAGES (1 << 2) +#elif PAGE_SIZE == 4096 +# define ATI_PCIGART_TABLE_ORDER 3 +# define ATI_PCIGART_TABLE_PAGES (1 << 3) +#elif +# error - PAGE_SIZE not 8K or 4K +#endif + +# define ATI_MAX_PCIGART_PAGES 8192 /* 32 MB aperture, 4K pages */ +# define ATI_PCIGART_PAGE_SIZE 4096 /* PCI GART page size */ + +static unsigned long DRM(ati_alloc_pcigart_table)( void ) +{ + unsigned long address; + struct page *page; + int i; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + address = __get_free_pages( GFP_KERNEL, ATI_PCIGART_TABLE_ORDER ); + if ( address == 0UL ) { + return 0; + } + + page = virt_to_page( address ); + + for ( i = 0 ; i <= ATI_PCIGART_TABLE_PAGES ; i++, page++ ) { + atomic_inc( &page->count ); + SetPageReserved( page ); + } + + DRM_DEBUG( "%s: returning 0x%08lx\n", __FUNCTION__, address ); + return address; +} + +static void DRM(ati_free_pcigart_table)( unsigned long address ) +{ + struct page *page; + int i; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + page = virt_to_page( address ); + + for ( i = 0 ; i <= ATI_PCIGART_TABLE_PAGES ; i++, page++ ) { + atomic_dec( &page->count ); + ClearPageReserved( page ); + } + + free_pages( address, ATI_PCIGART_TABLE_ORDER ); +} + +int DRM(ati_pcigart_init)( drm_device_t *dev, + unsigned long *addr, + dma_addr_t *bus_addr) +{ + drm_sg_mem_t *entry = dev->sg; + unsigned long address = 0; + unsigned long pages; + u32 *pci_gart, page_base, bus_address = 0; + int i, j, ret = 0; + + if ( !entry ) { + DRM_ERROR( "no scatter/gather memory!\n" ); + goto done; + } + + address = DRM(ati_alloc_pcigart_table)(); + if ( !address ) { + DRM_ERROR( "cannot allocate PCI GART page!\n" ); + goto done; + } + + if ( !dev->pdev ) { + DRM_ERROR( "PCI device unknown!\n" ); + goto done; + } + + bus_address = pci_map_single(dev->pdev, (void *)address, + ATI_PCIGART_TABLE_PAGES * PAGE_SIZE, + PCI_DMA_TODEVICE); + if (bus_address == 0) { + DRM_ERROR( "unable to map PCIGART pages!\n" ); + DRM(ati_free_pcigart_table)( address ); + address = 0; + goto done; + } + + pci_gart = (u32 *)address; + + pages = ( entry->pages <= ATI_MAX_PCIGART_PAGES ) + ? entry->pages : ATI_MAX_PCIGART_PAGES; + + memset( pci_gart, 0, ATI_MAX_PCIGART_PAGES * sizeof(u32) ); + + for ( i = 0 ; i < pages ; i++ ) { + /* we need to support large memory configurations */ + entry->busaddr[i] = pci_map_single(dev->pdev, + page_address( entry->pagelist[i] ), + PAGE_SIZE, + PCI_DMA_TODEVICE); + if (entry->busaddr[i] == 0) { + DRM_ERROR( "unable to map PCIGART pages!\n" ); + DRM(ati_pcigart_cleanup)( dev, address, bus_address ); + address = 0; + bus_address = 0; + goto done; + } + page_base = (u32) entry->busaddr[i]; + + for (j = 0; j < (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); j++) { + *pci_gart++ = cpu_to_le32( page_base ); + page_base += ATI_PCIGART_PAGE_SIZE; + } + } + + ret = 1; + +#if defined(__i386__) || defined(__x86_64__) + asm volatile ( "wbinvd" ::: "memory" ); +#else + mb(); +#endif + +done: + *addr = address; + *bus_addr = bus_address; + return ret; +} + +int DRM(ati_pcigart_cleanup)( drm_device_t *dev, + unsigned long addr, + dma_addr_t bus_addr) +{ + drm_sg_mem_t *entry = dev->sg; + unsigned long pages; + int i; + + /* we need to support large memory configurations */ + if ( !entry ) { + DRM_ERROR( "no scatter/gather memory!\n" ); + return 0; + } + + if ( bus_addr ) { + pci_unmap_single(dev->pdev, bus_addr, + ATI_PCIGART_TABLE_PAGES * PAGE_SIZE, + PCI_DMA_TODEVICE); + + pages = ( entry->pages <= ATI_MAX_PCIGART_PAGES ) + ? entry->pages : ATI_MAX_PCIGART_PAGES; + + for ( i = 0 ; i < pages ; i++ ) { + if ( !entry->busaddr[i] ) break; + pci_unmap_single(dev->pdev, entry->busaddr[i], + PAGE_SIZE, PCI_DMA_TODEVICE); + } + } + + if ( addr ) { + DRM(ati_free_pcigart_table)( addr ); + } + + return 1; +} diff --git a/bsd-core/drm_auth.c b/bsd-core/drm_auth.c new file mode 100644 index 00000000..f2c2d8da --- /dev/null +++ b/bsd-core/drm_auth.c @@ -0,0 +1,166 @@ +/* drm_auth.h -- IOCTLs for authentication -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@valinux.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * 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, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (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 NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS 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: + * Rickard E. (Rik) Faith <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" + +static int DRM(hash_magic)(drm_magic_t magic) +{ + return magic & (DRM_HASH_SIZE-1); +} + +static drm_file_t *DRM(find_file)(drm_device_t *dev, drm_magic_t magic) +{ + drm_file_t *retval = NULL; + drm_magic_entry_t *pt; + int hash = DRM(hash_magic)(magic); + + DRM_OS_LOCK; + for (pt = dev->magiclist[hash].head; pt; pt = pt->next) { + if (pt->magic == magic) { + retval = pt->priv; + break; + } + } + DRM_OS_UNLOCK; + return retval; +} + +int DRM(add_magic)(drm_device_t *dev, drm_file_t *priv, drm_magic_t magic) +{ + int hash; + drm_magic_entry_t *entry; + + DRM_DEBUG("%d\n", magic); + + hash = DRM(hash_magic)(magic); + entry = (drm_magic_entry_t*) DRM(alloc)(sizeof(*entry), DRM_MEM_MAGIC); + if (!entry) DRM_OS_RETURN(ENOMEM); + entry->magic = magic; + entry->priv = priv; + entry->next = NULL; + + DRM_OS_LOCK; + if (dev->magiclist[hash].tail) { + dev->magiclist[hash].tail->next = entry; + dev->magiclist[hash].tail = entry; + } else { + dev->magiclist[hash].head = entry; + dev->magiclist[hash].tail = entry; + } + DRM_OS_UNLOCK; + + return 0; +} + +int DRM(remove_magic)(drm_device_t *dev, drm_magic_t magic) +{ + drm_magic_entry_t *prev = NULL; + drm_magic_entry_t *pt; + int hash; + + DRM_DEBUG("%d\n", magic); + hash = DRM(hash_magic)(magic); + + DRM_OS_LOCK; + for (pt = dev->magiclist[hash].head; pt; prev = pt, pt = pt->next) { + if (pt->magic == magic) { + if (dev->magiclist[hash].head == pt) { + dev->magiclist[hash].head = pt->next; + } + if (dev->magiclist[hash].tail == pt) { + dev->magiclist[hash].tail = prev; + } + if (prev) { + prev->next = pt->next; + } + DRM_OS_UNLOCK; + DRM(free)(pt, sizeof(*pt), DRM_MEM_MAGIC); + return 0; + } + } + DRM_OS_UNLOCK; + + DRM(free)(pt, sizeof(*pt), DRM_MEM_MAGIC); + DRM_OS_RETURN(EINVAL); +} + +int DRM(getmagic)(DRM_OS_IOCTL) +{ + static drm_magic_t sequence = 0; + drm_auth_t auth; + static DRM_OS_SPINTYPE lock; + static int first = 1; + DRM_OS_DEVICE; + DRM_OS_PRIV; + + if (first) { + DRM_OS_SPININIT(lock, "drm getmagic"); + first = 0; + } + + /* Find unique magic */ + if (priv->magic) { + auth.magic = priv->magic; + } else { + do { + DRM_OS_SPINLOCK(&lock); + if (!sequence) ++sequence; /* reserve 0 */ + auth.magic = sequence++; + DRM_OS_SPINUNLOCK(&lock); + } while (DRM(find_file)(dev, auth.magic)); + priv->magic = auth.magic; + DRM(add_magic)(dev, priv, auth.magic); + } + + DRM_DEBUG("%u\n", auth.magic); + + DRM_OS_KRNTOUSR((drm_auth_t *)data, auth, sizeof(auth)); + + return 0; +} + +int DRM(authmagic)(DRM_OS_IOCTL) +{ + drm_auth_t auth; + drm_file_t *file; + DRM_OS_DEVICE; + + DRM_OS_KRNFROMUSR(auth, (drm_auth_t *)data, sizeof(auth)); + + DRM_DEBUG("%u\n", auth.magic); + if ((file = DRM(find_file)(dev, auth.magic))) { + file->authenticated = 1; + DRM(remove_magic)(dev, auth.magic); + return 0; + } + DRM_OS_RETURN(EINVAL); +} diff --git a/bsd-core/drm_bufs.c b/bsd-core/drm_bufs.c new file mode 100644 index 00000000..d55b36d8 --- /dev/null +++ b/bsd-core/drm_bufs.c @@ -0,0 +1,1092 @@ +/* drm_bufs.h -- Generic buffer template -*- linux-c -*- + * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com + * + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * 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, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (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 NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS 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: + * Rickard E. (Rik) Faith <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include <machine/param.h> +#include <sys/mman.h> +#include <vm/vm.h> +#include <vm/pmap.h> +#include <vm/vm_extern.h> +#include <vm/vm_map.h> +#include <vm/vm_param.h> +#include "drmP.h" + +#ifndef __HAVE_PCI_DMA +#define __HAVE_PCI_DMA 0 +#endif + +#ifndef __HAVE_SG +#define __HAVE_SG 0 +#endif + +#ifndef DRIVER_BUF_PRIV_T +#define DRIVER_BUF_PRIV_T u32 +#endif +#ifndef DRIVER_AGP_BUFFERS_MAP +#if __HAVE_AGP && __HAVE_DMA +#error "You must define DRIVER_AGP_BUFFERS_MAP()" +#else +#define DRIVER_AGP_BUFFERS_MAP( dev ) NULL +#endif +#endif + +/* + * Compute order. Can be made faster. + */ +int DRM(order)( unsigned long size ) +{ + int order; + unsigned long tmp; + + for ( order = 0, tmp = size ; tmp >>= 1 ; ++order ); + + if ( size & ~(1 << order) ) + ++order; + + return order; +} + +int DRM(addmap)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_map_t *map; + drm_map_list_entry_t *list; + + if (!(dev->flags & (FREAD|FWRITE))) + DRM_OS_RETURN(EACCES); /* Require read/write */ + + map = (drm_map_t *) DRM(alloc)( sizeof(*map), DRM_MEM_MAPS ); + if ( !map ) + DRM_OS_RETURN(ENOMEM); + + *map = *(drm_map_t *)data; + + /* Only allow shared memory to be removable since we only keep enough + * book keeping information about shared memory to allow for removal + * when processes fork. + */ + if ( (map->flags & _DRM_REMOVABLE) && map->type != _DRM_SHM ) { + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + DRM_OS_RETURN(EINVAL); + } + DRM_DEBUG( "offset = 0x%08lx, size = 0x%08lx, type = %d\n", + map->offset, map->size, map->type ); + if ( (map->offset & PAGE_MASK) || (map->size & PAGE_MASK) ) { + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + DRM_OS_RETURN(EINVAL); + } + map->mtrr = -1; + map->handle = 0; + + TAILQ_FOREACH(list, dev->maplist, link) { + drm_map_t *entry = list->map; + if ( (entry->offset >= map->offset + && (entry->offset) < (map->offset + map->size) ) + || ((entry->offset + entry->size) >= map->offset + && (entry->offset + entry->size) < (map->offset + map->size) ) + || ((entry->offset < map->offset) + && (entry->offset + entry->size) >= (map->offset + map->size) ) ) + DRM_DEBUG("map collission: add(0x%lx-0x%lx), current(0x%lx-0x%lx)\n", + entry->offset, entry->offset + entry->size - 1, + map->offset, map->offset + map->size - 1); + } + + switch ( map->type ) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#if !defined(__sparc__) && !defined(__alpha__) + if ( map->offset + map->size < map->offset + ) { + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + DRM_OS_RETURN(EINVAL); + } +#endif +#ifdef __alpha__ + map->offset += dev->hose->mem_space->start; +#endif +#if __REALLY_HAVE_MTRR + if ( map->type == _DRM_FRAME_BUFFER || + (map->flags & _DRM_WRITE_COMBINING) ) { + map->mtrr = mtrr_add( map->offset, map->size, + MTRR_TYPE_WRCOMB, 1 ); + } +#endif + map->handle = DRM(ioremap)( map->offset, map->size ); + break; + + case _DRM_SHM: + DRM_INFO( "%ld %d %d\n", + map->size, DRM(order)( map->size ), PAGE_SHIFT); + map->handle = (void *)DRM(alloc_pages) + (DRM(order)(map->size) - PAGE_SHIFT, DRM_MEM_SAREA); + DRM_DEBUG( "%ld %d %p\n", + map->size, DRM(order)( map->size ), map->handle ); + if ( !map->handle ) { + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + DRM_OS_RETURN(ENOMEM); + } + map->offset = (unsigned long)map->handle; + if ( map->flags & _DRM_CONTAINS_LOCK ) { + dev->lock.hw_lock = map->handle; /* Pointer to lock */ + } + break; +#if __REALLY_HAVE_AGP + case _DRM_AGP: +#ifdef __alpha__ + map->offset += dev->hose->mem_space->start; +#endif + map->offset += dev->agp->base; + map->mtrr = dev->agp->agp_mtrr; /* for getmap */ + break; +#endif + case _DRM_SCATTER_GATHER: + if (!dev->sg) { + DRM(free)(map, sizeof(*map), DRM_MEM_MAPS); + DRM_OS_RETURN(EINVAL); + } + map->offset = map->offset + dev->sg->handle; + break; + + default: + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + DRM_OS_RETURN(EINVAL); + } + + list = DRM(alloc)(sizeof(*list), DRM_MEM_MAPS); + if(!list) { + DRM(free)(map, sizeof(*map), DRM_MEM_MAPS); + DRM_OS_RETURN(EINVAL); + } + memset(list, 0, sizeof(*list)); + list->map = map; + + DRM_OS_LOCK; + TAILQ_INSERT_TAIL(dev->maplist, list, link); + DRM_OS_UNLOCK; + + *(drm_map_t *)data = *map; + + if ( map->type != _DRM_SHM ) { + ((drm_map_t *)data)->handle = (void *)map->offset; + } + return 0; +} + + +/* Remove a map private from list and deallocate resources if the mapping + * isn't in use. + */ + +int DRM(rmmap)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_map_list_entry_t *list; + drm_map_t *map; + drm_map_t request; + int found_maps = 0; + + DRM_OS_KRNFROMUSR( request, (drm_map_t *)data, sizeof(request) ); + + DRM_OS_LOCK; + TAILQ_FOREACH(list, dev->maplist, link) { + map = list->map; + if(map->handle == request.handle && + map->flags & _DRM_REMOVABLE) break; + } + + /* List has wrapped around to the head pointer, or its empty we didn't + * find anything. + */ + if(list == NULL) { + DRM_OS_UNLOCK; + DRM_OS_RETURN(EINVAL); + } + TAILQ_REMOVE(dev->maplist, list, link); + DRM(free)(list, sizeof(*list), DRM_MEM_MAPS); + + + if(!found_maps) { + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#if __REALLY_HAVE_MTRR + if (map->mtrr >= 0) { + int retcode; + retcode = mtrr_del(map->mtrr, + map->offset, + map->size); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } +#endif + DRM(ioremapfree)(map->handle, map->size); + break; + case _DRM_SHM: + DRM(free_pages)( (unsigned long)map->handle, DRM(order)(map->size), DRM_MEM_SAREA ); + break; + case _DRM_AGP: + case _DRM_SCATTER_GATHER: + break; + } + DRM(free)(map, sizeof(*map), DRM_MEM_MAPS); + } + DRM_OS_UNLOCK; + return 0; +} + +#if __HAVE_DMA + + +static void DRM(cleanup_buf_error)(drm_buf_entry_t *entry) +{ + int i; + + if (entry->seg_count) { + for (i = 0; i < entry->seg_count; i++) { + DRM(free_pages)(entry->seglist[i], + entry->page_order, + DRM_MEM_DMA); + } + DRM(free)(entry->seglist, + entry->seg_count * + sizeof(*entry->seglist), + DRM_MEM_SEGS); + + entry->seg_count = 0; + } + + if(entry->buf_count) { + for(i = 0; i < entry->buf_count; i++) { + if(entry->buflist[i].dev_private) { + DRM(free)(entry->buflist[i].dev_private, + entry->buflist[i].dev_priv_size, + DRM_MEM_BUFS); + } + } + DRM(free)(entry->buflist, + entry->buf_count * + sizeof(*entry->buflist), + DRM_MEM_BUFS); + +#if __HAVE_DMA_FREELIST + DRM(freelist_destroy)(&entry->freelist); +#endif + + entry->buf_count = 0; + } +} + +#if __REALLY_HAVE_AGP +int DRM(addbufs_agp)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + drm_buf_entry_t *entry; + drm_buf_t *buf; + unsigned long offset; + unsigned long agp_offset; + int count; + int order; + int size; + int alignment; + int page_order; + int total; + int byte_count; + int i; + drm_buf_t **temp_buflist; + + if ( !dma ) DRM_OS_RETURN(EINVAL); + + DRM_OS_KRNFROMUSR( request, (drm_buf_desc_t *)data, sizeof(request) ); + + count = request.count; + order = DRM(order)( request.size ); + size = 1 << order; + + alignment = (request.flags & _DRM_PAGE_ALIGN) + ? round_page(size) : size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + byte_count = 0; + agp_offset = dev->agp->base + request.agp_start; + + DRM_DEBUG( "count: %d\n", count ); + DRM_DEBUG( "order: %d\n", order ); + DRM_DEBUG( "size: %d\n", size ); + DRM_DEBUG( "agp_offset: 0x%lx\n", agp_offset ); + DRM_DEBUG( "alignment: %d\n", alignment ); + DRM_DEBUG( "page_order: %d\n", page_order ); + DRM_DEBUG( "total: %d\n", total ); + + if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) + DRM_OS_RETURN(EINVAL); + if ( dev->queue_count ) + DRM_OS_RETURN(EBUSY); /* Not while in use */ + + DRM_OS_SPINLOCK( &dev->count_lock ); + if ( dev->buf_use ) { + DRM_OS_SPINUNLOCK( &dev->count_lock ); + DRM_OS_RETURN(EBUSY); + } + atomic_inc( &dev->buf_alloc ); + DRM_OS_SPINUNLOCK( &dev->count_lock ); + + DRM_OS_LOCK; + entry = &dma->bufs[order]; + if ( entry->buf_count ) { + DRM_OS_UNLOCK; + atomic_dec( &dev->buf_alloc ); + DRM_OS_RETURN(ENOMEM); /* May only call once for each order */ + } + + if (count < 0 || count > 4096) { + DRM_OS_UNLOCK; + atomic_dec( &dev->buf_alloc ); + DRM_OS_RETURN(EINVAL); + } + + entry->buflist = DRM(alloc)( count * sizeof(*entry->buflist), + DRM_MEM_BUFS ); + if ( !entry->buflist ) { + DRM_OS_UNLOCK; + atomic_dec( &dev->buf_alloc ); + DRM_OS_RETURN(ENOMEM); + } + memset( entry->buflist, 0, count * sizeof(*entry->buflist) ); + + entry->buf_size = size; + entry->page_order = page_order; + + offset = 0; + + while ( entry->buf_count < count ) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + + buf->offset = (dma->byte_count + offset); + buf->bus_address = agp_offset + offset; + buf->address = (void *)(agp_offset + offset); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + buf->dma_wait = 0; + buf->pid = 0; + + buf->dev_priv_size = sizeof(DRIVER_BUF_PRIV_T); + buf->dev_private = DRM(alloc)( sizeof(DRIVER_BUF_PRIV_T), + DRM_MEM_BUFS ); + if(!buf->dev_private) { + /* Set count correctly so we free the proper amount. */ + entry->buf_count = count; + DRM(cleanup_buf_error)(entry); + } + memset( buf->dev_private, 0, buf->dev_priv_size ); + +#if __HAVE_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + + offset += alignment; + entry->buf_count++; + byte_count += PAGE_SIZE << page_order; + } + + DRM_DEBUG( "byte_count: %d\n", byte_count ); + + temp_buflist = DRM(realloc)( dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS ); + if(!temp_buflist) { + /* Free the entry because it isn't valid */ + DRM(cleanup_buf_error)(entry); + DRM_OS_UNLOCK; + atomic_dec( &dev->buf_alloc ); + DRM_OS_RETURN(ENOMEM); + } + dma->buflist = temp_buflist; + + for ( i = 0 ; i < entry->buf_count ; i++ ) { + dma->buflist[i + dma->buf_count] = &entry->buflist[i]; + } + + dma->buf_count += entry->buf_count; + dma->byte_count += byte_count; + + DRM_DEBUG( "dma->buf_count : %d\n", dma->buf_count ); + DRM_DEBUG( "entry->buf_count : %d\n", entry->buf_count ); + +#if __HAVE_DMA_FREELIST + DRM(freelist_create)( &entry->freelist, entry->buf_count ); + for ( i = 0 ; i < entry->buf_count ; i++ ) { + DRM(freelist_put)( dev, &entry->freelist, &entry->buflist[i] ); + } +#endif + DRM_OS_UNLOCK; + + request.count = entry->buf_count; + request.size = size; + + DRM_OS_KRNTOUSR( (drm_buf_desc_t *)data, request, sizeof(request) ); + + dma->flags = _DRM_DMA_USE_AGP; + + atomic_dec( &dev->buf_alloc ); + return 0; +} +#endif /* __REALLY_HAVE_AGP */ + +#if __HAVE_PCI_DMA +int DRM(addbufs_pci)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int count; + int order; + int size; + int total; + int page_order; + drm_buf_entry_t *entry; + unsigned long page; + drm_buf_t *buf; + int alignment; + unsigned long offset; + int i; + int byte_count; + int page_count; + unsigned long *temp_pagelist; + drm_buf_t **temp_buflist; + + if ( !dma ) DRM_OS_RETURN(EINVAL); + + DRM_OS_KRNFROMUSR( request, (drm_buf_desc_t *)data, sizeof(request) ); + + count = request.count; + order = DRM(order)( request.size ); + size = 1 << order; + + DRM_DEBUG( "count=%d, size=%d (%d), order=%d, queue_count=%d\n", + request.count, request.size, size, + order, dev->queue_count ); + + if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) + DRM_OS_RETURN(EINVAL); + if ( dev->queue_count ) + DRM_OS_RETURN(EBUSY); /* Not while in use */ + + alignment = (request.flags & _DRM_PAGE_ALIGN) + ? round_page(size) : size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + DRM_OS_SPINLOCK( &dev->count_lock ); + if ( dev->buf_use ) { + DRM_OS_SPINUNLOCK( &dev->count_lock ); + DRM_OS_RETURN(EBUSY); + } + atomic_inc( &dev->buf_alloc ); + DRM_OS_SPINUNLOCK( &dev->count_lock ); + + DRM_OS_LOCK; + entry = &dma->bufs[order]; + if ( entry->buf_count ) { + DRM_OS_UNLOCK; + atomic_dec( &dev->buf_alloc ); + DRM_OS_RETURN(ENOMEM); /* May only call once for each order */ + } + + if (count < 0 || count > 4096) { + DRM_OS_UNLOCK; + atomic_dec( &dev->buf_alloc ); + DRM_OS_RETURN(EINVAL); + } + + entry->buflist = DRM(alloc)( count * sizeof(*entry->buflist), + DRM_MEM_BUFS ); + if ( !entry->buflist ) { + DRM_OS_UNLOCK; + atomic_dec( &dev->buf_alloc ); + DRM_OS_RETURN(ENOMEM); + } + memset( entry->buflist, 0, count * sizeof(*entry->buflist) ); + + entry->seglist = DRM(alloc)( count * sizeof(*entry->seglist), + DRM_MEM_SEGS ); + if ( !entry->seglist ) { + DRM(free)( entry->buflist, + count * sizeof(*entry->buflist), + DRM_MEM_BUFS ); + DRM_OS_UNLOCK; + atomic_dec( &dev->buf_alloc ); + DRM_OS_RETURN(ENOMEM); + } + memset( entry->seglist, 0, count * sizeof(*entry->seglist) ); + + temp_pagelist = DRM(realloc)( dma->pagelist, + dma->page_count * sizeof(*dma->pagelist), + (dma->page_count + (count << page_order)) + * sizeof(*dma->pagelist), + DRM_MEM_PAGES ); + if(!temp_pagelist) { + DRM(free)( entry->buflist, + count * sizeof(*entry->buflist), + DRM_MEM_BUFS ); + DRM(free)( entry->seglist, + count * sizeof(*entry->seglist), + DRM_MEM_SEGS ); + DRM_OS_UNLOCK; + atomic_dec( &dev->buf_alloc ); + DRM_OS_RETURN(ENOMEM); + } + + dma->pagelist = temp_pagelist; + DRM_DEBUG( "pagelist: %d entries\n", + dma->page_count + (count << page_order) ); + + entry->buf_size = size; + entry->page_order = page_order; + byte_count = 0; + page_count = 0; + + while ( entry->buf_count < count ) { + page = DRM(alloc_pages)( page_order, DRM_MEM_DMA ); + if ( !page ) break; + entry->seglist[entry->seg_count++] = page; + for ( i = 0 ; i < (1 << page_order) ; i++ ) { + DRM_DEBUG( "page %d @ 0x%08lx\n", + dma->page_count + page_count, + page + PAGE_SIZE * i ); + dma->pagelist[dma->page_count + page_count++] + = page + PAGE_SIZE * i; + } + for ( offset = 0 ; + offset + size <= total && entry->buf_count < count ; + offset += alignment, ++entry->buf_count ) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + buf->offset = (dma->byte_count + byte_count + offset); + buf->address = (void *)(page + offset); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + buf->dma_wait = 0; + buf->pid = 0; +#if __HAVE_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + DRM_DEBUG( "buffer %d @ %p\n", + entry->buf_count, buf->address ); + } + byte_count += PAGE_SIZE << page_order; + } + + temp_buflist = DRM(realloc)( dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS ); + if(!temp_buflist) { + /* Free the entry because it isn't valid */ + DRM(cleanup_buf_error)(entry); + DRM_OS_UNLOCK; + atomic_dec( &dev->buf_alloc ); + DRM_OS_RETURN(ENOMEM); + } + dma->buflist = temp_buflist; + + for ( i = 0 ; i < entry->buf_count ; i++ ) { + dma->buflist[i + dma->buf_count] = &entry->buflist[i]; + } + + dma->buf_count += entry->buf_count; + dma->seg_count += entry->seg_count; + dma->page_count += entry->seg_count << page_order; + dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order); + +#if __HAVE_DMA_FREELIST + DRM(freelist_create)( &entry->freelist, entry->buf_count ); + for ( i = 0 ; i < entry->buf_count ; i++ ) { + DRM(freelist_put)( dev, &entry->freelist, &entry->buflist[i] ); + } +#endif + DRM_OS_UNLOCK; + + request.count = entry->buf_count; + request.size = size; + + DRM_OS_KRNTOUSR( (drm_buf_desc_t *)data, request, sizeof(request) ); + + atomic_dec( &dev->buf_alloc ); + return 0; + +} +#endif /* __HAVE_PCI_DMA */ + +#if __REALLY_HAVE_SG +int DRM(addbufs_sg)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + drm_buf_entry_t *entry; + drm_buf_t *buf; + unsigned long offset; + unsigned long agp_offset; + int count; + int order; + int size; + int alignment; + int page_order; + int total; + int byte_count; + int i; + drm_buf_t **temp_buflist; + + if ( !dma ) DRM_OS_RETURN(EINVAL); + + DRM_OS_KRNFROMUSR( request, (drm_buf_desc_t *)data, sizeof(request) ); + + count = request.count; + order = DRM(order)( request.size ); + size = 1 << order; + + alignment = (request.flags & _DRM_PAGE_ALIGN) + ? round_page(size) : size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + byte_count = 0; + agp_offset = request.agp_start; + + DRM_DEBUG( "count: %d\n", count ); + DRM_DEBUG( "order: %d\n", order ); + DRM_DEBUG( "size: %d\n", size ); + DRM_DEBUG( "agp_offset: %ld\n", agp_offset ); + DRM_DEBUG( "alignment: %d\n", alignment ); + DRM_DEBUG( "page_order: %d\n", page_order ); + DRM_DEBUG( "total: %d\n", total ); + + if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) + DRM_OS_RETURN(EINVAL); + if ( dev->queue_count ) DRM_OS_RETURN(EBUSY); /* Not while in use */ + + DRM_OS_SPINLOCK( &dev->count_lock ); + if ( dev->buf_use ) { + DRM_OS_SPINUNLOCK( &dev->count_lock ); + DRM_OS_RETURN(EBUSY); + } + atomic_inc( &dev->buf_alloc ); + DRM_OS_SPINUNLOCK( &dev->count_lock ); + + DRM_OS_LOCK; + entry = &dma->bufs[order]; + if ( entry->buf_count ) { + DRM_OS_UNLOCK; + atomic_dec( &dev->buf_alloc ); + DRM_OS_RETURN(ENOMEM); /* May only call once for each order */ + } + + if (count < 0 || count > 4096) { + DRM_OS_UNLOCK; + atomic_dec( &dev->buf_alloc ); + DRM_OS_RETURN(EINVAL); + } + + entry->buflist = DRM(alloc)( count * sizeof(*entry->buflist), + DRM_MEM_BUFS ); + if ( !entry->buflist ) { + DRM_OS_UNLOCK; + atomic_dec( &dev->buf_alloc ); + DRM_OS_RETURN(ENOMEM); + } + memset( entry->buflist, 0, count * sizeof(*entry->buflist) ); + + entry->buf_size = size; + entry->page_order = page_order; + + offset = 0; + + while ( entry->buf_count < count ) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + + buf->offset = (dma->byte_count + offset); + buf->bus_address = agp_offset + offset; + buf->address = (void *)(agp_offset + offset + dev->sg->handle); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + buf->dma_wait = 0; + buf->pid = 0; + + buf->dev_priv_size = sizeof(DRIVER_BUF_PRIV_T); + buf->dev_private = DRM(alloc)( sizeof(DRIVER_BUF_PRIV_T), + DRM_MEM_BUFS ); + if(!buf->dev_private) { + /* Set count correctly so we free the proper amount. */ + entry->buf_count = count; + DRM(cleanup_buf_error)(entry); + DRM_OS_UNLOCK; + atomic_dec( &dev->buf_alloc ); + DRM_OS_RETURN(ENOMEM); + } + + memset( buf->dev_private, 0, buf->dev_priv_size ); + +# if __HAVE_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +# endif + DRM_DEBUG( "buffer %d @ %p\n", + entry->buf_count, buf->address ); + + offset += alignment; + entry->buf_count++; + byte_count += PAGE_SIZE << page_order; + } + + DRM_DEBUG( "byte_count: %d\n", byte_count ); + + temp_buflist = DRM(realloc)( dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS ); + if(!temp_buflist) { + /* Free the entry because it isn't valid */ + DRM(cleanup_buf_error)(entry); + DRM_OS_UNLOCK; + atomic_dec( &dev->buf_alloc ); + DRM_OS_RETURN(ENOMEM); + } + dma->buflist = temp_buflist; + + for ( i = 0 ; i < entry->buf_count ; i++ ) { + dma->buflist[i + dma->buf_count] = &entry->buflist[i]; + } + + dma->buf_count += entry->buf_count; + dma->byte_count += byte_count; + + DRM_DEBUG( "dma->buf_count : %d\n", dma->buf_count ); + DRM_DEBUG( "entry->buf_count : %d\n", entry->buf_count ); + +#if __HAVE_DMA_FREELIST + DRM(freelist_create)( &entry->freelist, entry->buf_count ); + for ( i = 0 ; i < entry->buf_count ; i++ ) { + DRM(freelist_put)( dev, &entry->freelist, &entry->buflist[i] ); + } +#endif + DRM_OS_UNLOCK; + + request.count = entry->buf_count; + request.size = size; + + DRM_OS_KRNTOUSR( (drm_buf_desc_t *)data, request, sizeof(request) ); + + dma->flags = _DRM_DMA_USE_SG; + + atomic_dec( &dev->buf_alloc ); + return 0; +} +#endif /* __REALLY_HAVE_SG */ + +int DRM(addbufs)( DRM_OS_IOCTL ) +{ + drm_buf_desc_t request; + + DRM_OS_KRNFROMUSR( request, (drm_buf_desc_t *)data, sizeof(request) ); + +#if __REALLY_HAVE_AGP + if ( request.flags & _DRM_AGP_BUFFER ) + return DRM(addbufs_agp)( kdev, cmd, data, flags, p ); + else +#endif +#if __REALLY_HAVE_SG + if ( request.flags & _DRM_SG_BUFFER ) + return DRM(addbufs_sg)( kdev, cmd, data, flags, p ); + else +#endif +#if __HAVE_PCI_DMA + return DRM(addbufs_pci)( kdev, cmd, data, flags, p ); +#else + DRM_OS_RETURN(EINVAL); +#endif +} + +int DRM(infobufs)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_device_dma_t *dma = dev->dma; + drm_buf_info_t request; + int i; + int count; + + if ( !dma ) DRM_OS_RETURN(EINVAL); + + DRM_OS_SPINLOCK( &dev->count_lock ); + if ( atomic_read( &dev->buf_alloc ) ) { + DRM_OS_SPINUNLOCK( &dev->count_lock ); + DRM_OS_RETURN(EBUSY); + } + ++dev->buf_use; /* Can't allocate more after this call */ + DRM_OS_SPINUNLOCK( &dev->count_lock ); + + DRM_OS_KRNFROMUSR( request, (drm_buf_info_t *)data, sizeof(request) ); + + for ( i = 0, count = 0 ; i < DRM_MAX_ORDER + 1 ; i++ ) { + if ( dma->bufs[i].buf_count ) ++count; + } + + DRM_DEBUG( "count = %d\n", count ); + + if ( request.count >= count ) { + for ( i = 0, count = 0 ; i < DRM_MAX_ORDER + 1 ; i++ ) { + if ( dma->bufs[i].buf_count ) { + drm_buf_desc_t *to = &request.list[count]; + drm_buf_entry_t *from = &dma->bufs[i]; + drm_freelist_t *list = &dma->bufs[i].freelist; + if ( DRM_OS_COPYTOUSR( &to->count, + &from->buf_count, + sizeof(from->buf_count) ) || + DRM_OS_COPYTOUSR( &to->size, + &from->buf_size, + sizeof(from->buf_size) ) || + DRM_OS_COPYTOUSR( &to->low_mark, + &list->low_mark, + sizeof(list->low_mark) ) || + DRM_OS_COPYTOUSR( &to->high_mark, + &list->high_mark, + sizeof(list->high_mark) ) ) + DRM_OS_RETURN(EFAULT); + + DRM_DEBUG( "%d %d %d %d %d\n", + i, + dma->bufs[i].buf_count, + dma->bufs[i].buf_size, + dma->bufs[i].freelist.low_mark, + dma->bufs[i].freelist.high_mark ); + ++count; + } + } + } + request.count = count; + + DRM_OS_KRNTOUSR( (drm_buf_info_t *)data, request, sizeof(request) ); + + return 0; +} + +int DRM(markbufs)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int order; + drm_buf_entry_t *entry; + + if ( !dma ) DRM_OS_RETURN(EINVAL); + + DRM_OS_KRNFROMUSR( request, (drm_buf_desc_t *)data, sizeof(request) ); + + DRM_DEBUG( "%d, %d, %d\n", + request.size, request.low_mark, request.high_mark ); + order = DRM(order)( request.size ); + if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) + DRM_OS_RETURN(EINVAL); + entry = &dma->bufs[order]; + + if ( request.low_mark < 0 || request.low_mark > entry->buf_count ) + DRM_OS_RETURN(EINVAL); + if ( request.high_mark < 0 || request.high_mark > entry->buf_count ) + DRM_OS_RETURN(EINVAL); + + entry->freelist.low_mark = request.low_mark; + entry->freelist.high_mark = request.high_mark; + + return 0; +} + +int DRM(freebufs)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_device_dma_t *dma = dev->dma; + drm_buf_free_t request; + int i; + int idx; + drm_buf_t *buf; + + if ( !dma ) DRM_OS_RETURN(EINVAL); + + DRM_OS_KRNFROMUSR( request, (drm_buf_free_t *)data, sizeof(request) ); + + DRM_DEBUG( "%d\n", request.count ); + for ( i = 0 ; i < request.count ; i++ ) { + if ( DRM_OS_COPYFROMUSR( &idx, + &request.list[i], + sizeof(idx) ) ) + DRM_OS_RETURN(EFAULT); + if ( idx < 0 || idx >= dma->buf_count ) { + DRM_ERROR( "Index %d (of %d max)\n", + idx, dma->buf_count - 1 ); + DRM_OS_RETURN(EINVAL); + } + buf = dma->buflist[idx]; + if ( buf->pid != DRM_OS_CURRENTPID ) { + DRM_ERROR( "Process %d freeing buffer owned by %d\n", + DRM_OS_CURRENTPID, buf->pid ); + DRM_OS_RETURN(EINVAL); + } + DRM(free_buffer)( dev, buf ); + } + + return 0; +} + +int DRM(mapbufs)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_device_dma_t *dma = dev->dma; + int retcode = 0; + const int zero = 0; + vm_offset_t virtual, address; +#if __FreeBSD_version >= 500000 + struct vmspace *vms = p->td_proc->p_vmspace; +#else + struct vmspace *vms = p->p_vmspace; +#endif + drm_buf_map_t request; + int i; + + if ( !dma ) DRM_OS_RETURN(EINVAL); + + DRM_OS_SPINLOCK( &dev->count_lock ); + if ( atomic_read( &dev->buf_alloc ) ) { + DRM_OS_SPINUNLOCK( &dev->count_lock ); + DRM_OS_RETURN(EBUSY); + } + dev->buf_use++; /* Can't allocate more after this call */ + DRM_OS_SPINUNLOCK( &dev->count_lock ); + + DRM_OS_KRNFROMUSR( request, (drm_buf_map_t *)data, sizeof(request) ); + + if ( request.count >= dma->buf_count ) { + if ( (__HAVE_AGP && (dma->flags & _DRM_DMA_USE_AGP)) || + (__HAVE_SG && (dma->flags & _DRM_DMA_USE_SG)) ) { + drm_map_t *map = DRIVER_AGP_BUFFERS_MAP( dev ); + + if ( !map ) { + retcode = EINVAL; + goto done; + } + + virtual = round_page((vm_offset_t)vms->vm_daddr + MAXDSIZ); + retcode = vm_mmap(&vms->vm_map, + &virtual, + round_page(map->size), + PROT_READ|PROT_WRITE, VM_PROT_ALL, + MAP_SHARED, + SLIST_FIRST(&kdev->si_hlist), + (unsigned long)map->offset ); + } else { + virtual = round_page((vm_offset_t)vms->vm_daddr + MAXDSIZ); + retcode = vm_mmap(&vms->vm_map, + &virtual, + round_page(dma->byte_count), + PROT_READ|PROT_WRITE, VM_PROT_ALL, + MAP_SHARED, + SLIST_FIRST(&kdev->si_hlist), + 0); + } + if (retcode) + goto done; + request.virtual = (void *)virtual; + + for ( i = 0 ; i < dma->buf_count ; i++ ) { + if ( DRM_OS_COPYTOUSR( &request.list[i].idx, + &dma->buflist[i]->idx, + sizeof(request.list[0].idx) ) ) { + retcode = EFAULT; + goto done; + } + if ( DRM_OS_COPYTOUSR( &request.list[i].total, + &dma->buflist[i]->total, + sizeof(request.list[0].total) ) ) { + retcode = EFAULT; + goto done; + } + if ( DRM_OS_COPYTOUSR( &request.list[i].used, + &zero, + sizeof(zero) ) ) { + retcode = EFAULT; + goto done; + } + address = virtual + dma->buflist[i]->offset; /* *** */ + if ( DRM_OS_COPYTOUSR( &request.list[i].address, + &address, + sizeof(address) ) ) { + retcode = EFAULT; + goto done; + } + } + } + done: + request.count = dma->buf_count; + + DRM_DEBUG( "%d buffers, retcode = %d\n", request.count, retcode ); + + DRM_OS_KRNTOUSR( (drm_buf_map_t *)data, request, sizeof(request) ); + + DRM_OS_RETURN(retcode); +} + +#endif /* __HAVE_DMA */ + diff --git a/bsd-core/drm_context.c b/bsd-core/drm_context.c new file mode 100644 index 00000000..8d676a23 --- /dev/null +++ b/bsd-core/drm_context.c @@ -0,0 +1,730 @@ +/* drm_context.h -- IOCTLs for generic contexts -*- linux-c -*- + * Created: Fri Nov 24 18:31:37 2000 by gareth@valinux.com + * + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * 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, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (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 NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS 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: + * Rickard E. (Rik) Faith <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" + +#if __HAVE_CTX_BITMAP + +/* ================================================================ + * Context bitmap support + */ + +void DRM(ctxbitmap_free)( drm_device_t *dev, int ctx_handle ) +{ + if ( ctx_handle < 0 ) goto failed; + if ( !dev->ctx_bitmap ) goto failed; + + if ( ctx_handle < DRM_MAX_CTXBITMAP ) { + DRM_OS_LOCK; + clear_bit( ctx_handle, dev->ctx_bitmap ); + dev->context_sareas[ctx_handle] = NULL; + DRM_OS_UNLOCK; + return; + } +failed: + DRM_ERROR( "Attempt to free invalid context handle: %d\n", + ctx_handle ); + return; +} + +int DRM(ctxbitmap_next)( drm_device_t *dev ) +{ + int bit; + + if(!dev->ctx_bitmap) return -1; + + DRM_OS_LOCK; + bit = find_first_zero_bit( dev->ctx_bitmap, DRM_MAX_CTXBITMAP ); + if ( bit < DRM_MAX_CTXBITMAP ) { + set_bit( bit, dev->ctx_bitmap ); + DRM_DEBUG( "drm_ctxbitmap_next bit : %d\n", bit ); + if((bit+1) > dev->max_context) { + dev->max_context = (bit+1); + if(dev->context_sareas) { + drm_map_t **ctx_sareas; + + ctx_sareas = DRM(realloc)(dev->context_sareas, + (dev->max_context - 1) * + sizeof(*dev->context_sareas), + dev->max_context * + sizeof(*dev->context_sareas), + DRM_MEM_MAPS); + if(!ctx_sareas) { + clear_bit(bit, dev->ctx_bitmap); + DRM_OS_UNLOCK; + return -1; + } + dev->context_sareas = ctx_sareas; + dev->context_sareas[bit] = NULL; + } else { + /* max_context == 1 at this point */ + dev->context_sareas = DRM(alloc)( + dev->max_context * + sizeof(*dev->context_sareas), + DRM_MEM_MAPS); + if(!dev->context_sareas) { + clear_bit(bit, dev->ctx_bitmap); + DRM_OS_UNLOCK; + return -1; + } + dev->context_sareas[bit] = NULL; + } + } + DRM_OS_UNLOCK; + return bit; + } + DRM_OS_UNLOCK; + return -1; +} + +int DRM(ctxbitmap_init)( drm_device_t *dev ) +{ + int i; + int temp; + + DRM_OS_LOCK; + dev->ctx_bitmap = (unsigned long *) DRM(alloc)( PAGE_SIZE, + DRM_MEM_CTXBITMAP ); + if ( dev->ctx_bitmap == NULL ) { + DRM_OS_UNLOCK; + DRM_OS_RETURN(ENOMEM); + } + memset( (void *)dev->ctx_bitmap, 0, PAGE_SIZE ); + dev->context_sareas = NULL; + dev->max_context = -1; + DRM_OS_UNLOCK; + + for ( i = 0 ; i < DRM_RESERVED_CONTEXTS ; i++ ) { + temp = DRM(ctxbitmap_next)( dev ); + DRM_DEBUG( "drm_ctxbitmap_init : %d\n", temp ); + } + + return 0; +} + +void DRM(ctxbitmap_cleanup)( drm_device_t *dev ) +{ + DRM_OS_LOCK; + if( dev->context_sareas ) DRM(free)( dev->context_sareas, + sizeof(*dev->context_sareas) * + dev->max_context, + DRM_MEM_MAPS ); + DRM(free)( (void *)dev->ctx_bitmap, PAGE_SIZE, DRM_MEM_CTXBITMAP ); + DRM_OS_UNLOCK; +} + +/* ================================================================ + * Per Context SAREA Support + */ + +int DRM(getsareactx)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_ctx_priv_map_t request; + drm_map_t *map; + + DRM_OS_KRNFROMUSR( request, (drm_ctx_priv_map_t *)data, + sizeof(request) ); + + DRM_OS_LOCK; + if (dev->max_context < 0 || request.ctx_id >= (unsigned) dev->max_context) { + DRM_OS_UNLOCK; + DRM_OS_RETURN(EINVAL); + } + + map = dev->context_sareas[request.ctx_id]; + DRM_OS_UNLOCK; + + request.handle = map->handle; + + DRM_OS_KRNTOUSR( (drm_ctx_priv_map_t *)data, request, sizeof(request) ); + + return 0; +} + +int DRM(setsareactx)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_ctx_priv_map_t request; + drm_map_t *map = NULL; + drm_map_list_entry_t *list; + + DRM_OS_KRNFROMUSR( request, (drm_ctx_priv_map_t *)data, + sizeof(request) ); + + DRM_OS_LOCK; + TAILQ_FOREACH(list, dev->maplist, link) { + map=list->map; + if(map->handle == request.handle) + goto found; + } + +bad: + DRM_OS_UNLOCK; + return -EINVAL; + +found: + map = list->map; + if (!map) goto bad; + if (dev->max_context < 0) + goto bad; + if (request.ctx_id >= (unsigned) dev->max_context) + goto bad; + dev->context_sareas[request.ctx_id] = map; + DRM_OS_UNLOCK; + return 0; +} + +/* ================================================================ + * The actual DRM context handling routines + */ + +int DRM(context_switch)( drm_device_t *dev, int old, int new ) +{ + char buf[64]; + + if ( test_and_set_bit( 0, &dev->context_flag ) ) { + DRM_ERROR( "Reentering -- FIXME\n" ); + DRM_OS_RETURN(EBUSY); + } + +#if __HAVE_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + DRM_DEBUG( "Context switch from %d to %d\n", old, new ); + + if ( new == dev->last_context ) { + clear_bit( 0, &dev->context_flag ); + return 0; + } + + if ( DRM(flags) & DRM_FLAG_NOCTX ) { + DRM(context_switch_complete)( dev, new ); + } else { + sprintf( buf, "C %d %d\n", old, new ); + DRM(write_string)( dev, buf ); + } + + return 0; +} + +int DRM(context_switch_complete)( drm_device_t *dev, int new ) +{ + dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ + dev->last_switch = jiffies; + + if ( !_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) ) { + DRM_ERROR( "Lock isn't held after context switch\n" ); + } + + /* If a context switch is ever initiated + when the kernel holds the lock, release + that lock here. */ +#if __HAVE_DMA_HISTOGRAM + atomic_inc( &dev->histo.ctx[DRM(histogram_slot)(get_cycles() + - dev->ctx_start)] ); + +#endif + clear_bit( 0, &dev->context_flag ); + DRM_OS_WAKEUP( &dev->context_wait ); + + return 0; +} + +int DRM(resctx)( DRM_OS_IOCTL ) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + DRM_OS_KRNFROMUSR( res, (drm_ctx_res_t *)data, sizeof(res) ); + + if ( res.count >= DRM_RESERVED_CONTEXTS ) { + memset( &ctx, 0, sizeof(ctx) ); + for ( i = 0 ; i < DRM_RESERVED_CONTEXTS ; i++ ) { + ctx.handle = i; + if ( DRM_OS_COPYTOUSR( &res.contexts[i], + &i, sizeof(i) ) ) + DRM_OS_RETURN(EFAULT); + } + } + res.count = DRM_RESERVED_CONTEXTS; + + DRM_OS_KRNTOUSR( (drm_ctx_res_t *)data, res, sizeof(res) ); + + return 0; +} + +int DRM(addctx)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_ctx_t ctx; + + DRM_OS_KRNFROMUSR( ctx, (drm_ctx_t *)data, sizeof(ctx) ); + + ctx.handle = DRM(ctxbitmap_next)( dev ); + if ( ctx.handle == DRM_KERNEL_CONTEXT ) { + /* Skip kernel's context and get a new one. */ + ctx.handle = DRM(ctxbitmap_next)( dev ); + } + DRM_DEBUG( "%d\n", ctx.handle ); + if ( ctx.handle == -1 ) { + DRM_DEBUG( "Not enough free contexts.\n" ); + /* Should this return -EBUSY instead? */ + DRM_OS_RETURN(ENOMEM); + } + + DRM_OS_KRNTOUSR( (drm_ctx_t *)data, ctx, sizeof(ctx) ); + + return 0; +} + +int DRM(modctx)( DRM_OS_IOCTL ) +{ + /* This does nothing */ + return 0; +} + +int DRM(getctx)( DRM_OS_IOCTL ) +{ + drm_ctx_t ctx; + + DRM_OS_KRNFROMUSR( ctx, (drm_ctx_t *)data, sizeof(ctx) ); + + /* This is 0, because we don't handle any context flags */ + ctx.flags = 0; + + DRM_OS_KRNTOUSR( (drm_ctx_t *)data, ctx, sizeof(ctx) ); + + return 0; +} + +int DRM(switchctx)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_ctx_t ctx; + + DRM_OS_KRNFROMUSR( ctx, (drm_ctx_t *)data, sizeof(ctx) ); + + DRM_DEBUG( "%d\n", ctx.handle ); + return DRM(context_switch)( dev, dev->last_context, ctx.handle ); +} + +int DRM(newctx)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_ctx_t ctx; + + DRM_OS_KRNFROMUSR( ctx, (drm_ctx_t *)data, sizeof(ctx) ); + + DRM_DEBUG( "%d\n", ctx.handle ); + DRM(context_switch_complete)( dev, ctx.handle ); + + return 0; +} + +int DRM(rmctx)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_ctx_t ctx; + + DRM_OS_KRNFROMUSR( ctx, (drm_ctx_t *)data, sizeof(ctx) ); + + DRM_DEBUG( "%d\n", ctx.handle ); + if ( ctx.handle != DRM_KERNEL_CONTEXT ) { + DRM(ctxbitmap_free)( dev, ctx.handle ); + } + + return 0; +} + + +#else /* __HAVE_CTX_BITMAP */ + +/* ================================================================ + * Old-style context support + */ + + +int DRM(context_switch)(drm_device_t *dev, int old, int new) +{ + char buf[64]; + drm_queue_t *q; + +#if 0 + atomic_inc(&dev->total_ctx); +#endif + + if (test_and_set_bit(0, &dev->context_flag)) { + DRM_ERROR("Reentering -- FIXME\n"); + DRM_OS_RETURN(EBUSY); + } + +#if __HAVE_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + DRM_DEBUG("Context switch from %d to %d\n", old, new); + + if (new >= dev->queue_count) { + clear_bit(0, &dev->context_flag); + DRM_OS_RETURN(EINVAL); + } + + if (new == dev->last_context) { + clear_bit(0, &dev->context_flag); + return 0; + } + + q = dev->queuelist[new]; + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + atomic_dec(&q->use_count); + clear_bit(0, &dev->context_flag); + DRM_OS_RETURN(EINVAL); + } + + if (DRM(flags) & DRM_FLAG_NOCTX) { + DRM(context_switch_complete)(dev, new); + } else { + sprintf(buf, "C %d %d\n", old, new); + DRM(write_string)(dev, buf); + } + + atomic_dec(&q->use_count); + + return 0; +} + +int DRM(context_switch_complete)(drm_device_t *dev, int new) +{ + drm_device_dma_t *dma = dev->dma; + + dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ + dev->last_switch = jiffies; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("Lock isn't held after context switch\n"); + } + + if (!dma || !(dma->next_buffer && dma->next_buffer->while_locked)) { + if (DRM(lock_free)(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("Cannot free lock\n"); + } + } + +#if __HAVE_DMA_HISTOGRAM + atomic_inc(&dev->histo.ctx[DRM(histogram_slot)(get_cycles() + - dev->ctx_start)]); + +#endif + clear_bit(0, &dev->context_flag); + DRM_OS_WAKEUP_INT(&dev->context_wait); + + return 0; +} + +static int DRM(init_queue)(drm_device_t *dev, drm_queue_t *q, drm_ctx_t *ctx) +{ + DRM_DEBUG("\n"); + + if (atomic_read(&q->use_count) != 1 + || atomic_read(&q->finalization) + || atomic_read(&q->block_count)) { + DRM_ERROR("New queue is already in use: u%ld f%ld b%ld\n", + (unsigned long)atomic_read(&q->use_count), + (unsigned long)atomic_read(&q->finalization), + (unsigned long)atomic_read(&q->block_count)); + } + + atomic_set(&q->finalization, 0); + atomic_set(&q->block_count, 0); + atomic_set(&q->block_read, 0); + atomic_set(&q->block_write, 0); + atomic_set(&q->total_queued, 0); + atomic_set(&q->total_flushed, 0); + atomic_set(&q->total_locks, 0); + + q->write_queue = 0; + q->read_queue = 0; + q->flush_queue = 0; + + q->flags = ctx->flags; + + DRM(waitlist_create)(&q->waitlist, dev->dma->buf_count); + + return 0; +} + + +/* drm_alloc_queue: +PRE: 1) dev->queuelist[0..dev->queue_count] is allocated and will not + disappear (so all deallocation must be done after IOCTLs are off) + 2) dev->queue_count < dev->queue_slots + 3) dev->queuelist[i].use_count == 0 and + dev->queuelist[i].finalization == 0 if i not in use +POST: 1) dev->queuelist[i].use_count == 1 + 2) dev->queue_count < dev->queue_slots */ + +static int DRM(alloc_queue)(drm_device_t *dev) +{ + int i; + drm_queue_t *queue; + int oldslots; + int newslots; + /* Check for a free queue */ + for (i = 0; i < dev->queue_count; i++) { + atomic_inc(&dev->queuelist[i]->use_count); + if (atomic_read(&dev->queuelist[i]->use_count) == 1 + && !atomic_read(&dev->queuelist[i]->finalization)) { + DRM_DEBUG("%d (free)\n", i); + return i; + } + atomic_dec(&dev->queuelist[i]->use_count); + } + /* Allocate a new queue */ + DRM_OS_LOCK; + + queue = gamma_alloc(sizeof(*queue), DRM_MEM_QUEUES); + memset(queue, 0, sizeof(*queue)); + atomic_set(&queue->use_count, 1); + + ++dev->queue_count; + if (dev->queue_count >= dev->queue_slots) { + oldslots = dev->queue_slots * sizeof(*dev->queuelist); + if (!dev->queue_slots) dev->queue_slots = 1; + dev->queue_slots *= 2; + newslots = dev->queue_slots * sizeof(*dev->queuelist); + + dev->queuelist = DRM(realloc)(dev->queuelist, + oldslots, + newslots, + DRM_MEM_QUEUES); + if (!dev->queuelist) { + DRM_OS_UNLOCK; + DRM_DEBUG("out of memory\n"); + DRM_OS_RETURN(ENOMEM); + } + } + dev->queuelist[dev->queue_count-1] = queue; + + DRM_OS_UNLOCK; + DRM_DEBUG("%d (new)\n", dev->queue_count - 1); + return dev->queue_count - 1; +} + +int DRM(resctx)( DRM_OS_IOCTL ) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS); + + DRM_OS_KRNFROMUSR( res, (drm_ctx_res_t *)data, sizeof(res) ); + + if (res.count >= DRM_RESERVED_CONTEXTS) { + memset(&ctx, 0, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + if (DRM_OS_COPYTOUSR(&res.contexts[i], + &i, + sizeof(i))) + DRM_OS_RETURN(EFAULT); + } + } + res.count = DRM_RESERVED_CONTEXTS; + + DRM_OS_KRNTOUSR( (drm_ctx_res_t *)data, res, sizeof(res) ); + + return 0; +} + +int DRM(addctx)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_ctx_t ctx; + + DRM_OS_KRNFROMUSR( ctx, (drm_ctx_t *)data, sizeof(ctx) ); + + if ((ctx.handle = DRM(alloc_queue)(dev)) == DRM_KERNEL_CONTEXT) { + /* Init kernel's context and get a new one. */ + DRM(init_queue)(dev, dev->queuelist[ctx.handle], &ctx); + ctx.handle = DRM(alloc_queue)(dev); + } + DRM(init_queue)(dev, dev->queuelist[ctx.handle], &ctx); + DRM_DEBUG("%d\n", ctx.handle); + + DRM_OS_KRNTOUSR( (drm_ctx_t *)data, ctx, sizeof(ctx) ); + + return 0; +} + +int DRM(modctx)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_ctx_t ctx; + drm_queue_t *q; + + DRM_OS_KRNFROMUSR( ctx, (drm_ctx_t *)data, sizeof(ctx) ); + + DRM_DEBUG("%d\n", ctx.handle); + + if (ctx.handle < 0 || ctx.handle >= dev->queue_count) + DRM_OS_RETURN(EINVAL); + q = dev->queuelist[ctx.handle]; + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + /* No longer in use */ + atomic_dec(&q->use_count); + DRM_OS_RETURN(EINVAL); + } + + if (DRM_BUFCOUNT(&q->waitlist)) { + atomic_dec(&q->use_count); + DRM_OS_RETURN(EBUSY); + } + + q->flags = ctx.flags; + + atomic_dec(&q->use_count); + return 0; +} + +int DRM(getctx)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_ctx_t ctx; + drm_queue_t *q; + + DRM_OS_KRNFROMUSR( ctx, (drm_ctx_t *)data, sizeof(ctx) ); + + DRM_DEBUG("%d\n", ctx.handle); + + if (ctx.handle >= dev->queue_count) + DRM_OS_RETURN(EINVAL); + q = dev->queuelist[ctx.handle]; + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + /* No longer in use */ + atomic_dec(&q->use_count); + DRM_OS_RETURN(EINVAL); + } + + ctx.flags = q->flags; + atomic_dec(&q->use_count); + + DRM_OS_KRNTOUSR( (drm_ctx_t *)data, ctx, sizeof(ctx) ); + + return 0; +} + +int DRM(switchctx)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_ctx_t ctx; + + DRM_OS_KRNFROMUSR( ctx, (drm_ctx_t *)data, sizeof(ctx) ); + + DRM_DEBUG("%d\n", ctx.handle); + return DRM(context_switch)(dev, dev->last_context, ctx.handle); +} + +int DRM(newctx)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_ctx_t ctx; + + DRM_OS_KRNFROMUSR( ctx, (drm_ctx_t *)data, sizeof(ctx) ); + + DRM_DEBUG("%d\n", ctx.handle); + DRM(context_switch_complete)(dev, ctx.handle); + + return 0; +} + +int DRM(rmctx)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_ctx_t ctx; + drm_queue_t *q; + drm_buf_t *buf; + + DRM_OS_KRNFROMUSR( ctx, (drm_ctx_t *)data, sizeof(ctx) ); + + DRM_DEBUG("%d\n", ctx.handle); + + if (ctx.handle >= dev->queue_count) DRM_OS_RETURN(EINVAL); + q = dev->queuelist[ctx.handle]; + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + /* No longer in use */ + atomic_dec(&q->use_count); + DRM_OS_RETURN(EINVAL); + } + + atomic_inc(&q->finalization); /* Mark queue in finalization state */ + atomic_sub(2, &q->use_count); /* Mark queue as unused (pending + finalization) */ + + while (test_and_set_bit(0, &dev->interrupt_flag)) { + static int never; + int retcode; + retcode = tsleep(&never, PZERO|PCATCH, "never", 1); + if (retcode) + return retcode; + } + /* Remove queued buffers */ + while ((buf = DRM(waitlist_get)(&q->waitlist))) { + DRM(free_buffer)(dev, buf); + } + clear_bit(0, &dev->interrupt_flag); + + /* Wakeup blocked processes */ + wakeup( &q->block_read ); + wakeup( &q->block_write ); + DRM_OS_WAKEUP_INT( &q->flush_queue ); + /* Finalization over. Queue is made + available when both use_count and + finalization become 0, which won't + happen until all the waiting processes + stop waiting. */ + atomic_dec(&q->finalization); + return 0; +} + +#endif /* __HAVE_CTX_BITMAP */ diff --git a/bsd-core/drm_dma.c b/bsd-core/drm_dma.c new file mode 100644 index 00000000..e5aef241 --- /dev/null +++ b/bsd-core/drm_dma.c @@ -0,0 +1,605 @@ +/* drm_dma.c -- DMA IOCTL and function support -*- linux-c -*- + * Created: Fri Mar 19 14:30:16 1999 by faith@valinux.com + * + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * 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, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (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 NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS 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: + * Rickard E. (Rik) Faith <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include "drmP.h" + +#ifndef __HAVE_DMA_WAITQUEUE +#define __HAVE_DMA_WAITQUEUE 0 +#endif +#ifndef __HAVE_DMA_RECLAIM +#define __HAVE_DMA_RECLAIM 0 +#endif +#ifndef __HAVE_SHARED_IRQ +#define __HAVE_SHARED_IRQ 0 +#endif + +#if __HAVE_SHARED_IRQ +#define DRM_IRQ_TYPE SA_SHIRQ +#else +#define DRM_IRQ_TYPE 0 +#endif + +#if __HAVE_DMA + +int DRM(dma_setup)( drm_device_t *dev ) +{ + int i; + + dev->dma = DRM(alloc)( sizeof(*dev->dma), DRM_MEM_DRIVER ); + if ( !dev->dma ) + DRM_OS_RETURN(ENOMEM); + + memset( dev->dma, 0, sizeof(*dev->dma) ); + + for ( i = 0 ; i <= DRM_MAX_ORDER ; i++ ) + memset(&dev->dma->bufs[i], 0, sizeof(dev->dma->bufs[0])); + + return 0; +} + +void DRM(dma_takedown)(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + int i, j; + + if (!dma) return; + + /* Clear dma buffers */ + for (i = 0; i <= DRM_MAX_ORDER; i++) { + if (dma->bufs[i].seg_count) { + DRM_DEBUG("order %d: buf_count = %d," + " seg_count = %d\n", + i, + dma->bufs[i].buf_count, + dma->bufs[i].seg_count); + for (j = 0; j < dma->bufs[i].seg_count; j++) { + DRM(free_pages)(dma->bufs[i].seglist[j], + dma->bufs[i].page_order, + DRM_MEM_DMA); + } + DRM(free)(dma->bufs[i].seglist, + dma->bufs[i].seg_count + * sizeof(*dma->bufs[0].seglist), + DRM_MEM_SEGS); + } + if(dma->bufs[i].buf_count) { + for(j = 0; j < dma->bufs[i].buf_count; j++) { + if(dma->bufs[i].buflist[j].dev_private) { + DRM(free)(dma->bufs[i].buflist[j].dev_private, + dma->bufs[i].buflist[j].dev_priv_size, + DRM_MEM_BUFS); + } + } + DRM(free)(dma->bufs[i].buflist, + dma->bufs[i].buf_count * + sizeof(*dma->bufs[0].buflist), + DRM_MEM_BUFS); +#if __HAVE_DMA_FREELIST + DRM(freelist_destroy)(&dma->bufs[i].freelist); +#endif + } + } + + if (dma->buflist) { + DRM(free)(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + DRM_MEM_BUFS); + } + + if (dma->pagelist) { + DRM(free)(dma->pagelist, + dma->page_count * sizeof(*dma->pagelist), + DRM_MEM_PAGES); + } + DRM(free)(dev->dma, sizeof(*dev->dma), DRM_MEM_DRIVER); + dev->dma = NULL; +} + + +#if __HAVE_DMA_HISTOGRAM +/* This is slow, but is useful for debugging. */ +int DRM(histogram_slot)(unsigned long count) +{ + int value = DRM_DMA_HISTOGRAM_INITIAL; + int slot; + + for (slot = 0; + slot < DRM_DMA_HISTOGRAM_SLOTS; + ++slot, value = DRM_DMA_HISTOGRAM_NEXT(value)) { + if (count < value) return slot; + } + return DRM_DMA_HISTOGRAM_SLOTS - 1; +} + +void DRM(histogram_compute)(drm_device_t *dev, drm_buf_t *buf) +{ + cycles_t queued_to_dispatched; + cycles_t dispatched_to_completed; + cycles_t completed_to_freed; + int q2d, d2c, c2f, q2c, q2f; + + if (buf->time_queued) { + queued_to_dispatched = (buf->time_dispatched + - buf->time_queued); + dispatched_to_completed = (buf->time_completed + - buf->time_dispatched); + completed_to_freed = (buf->time_freed + - buf->time_completed); + + q2d = DRM(histogram_slot)(queued_to_dispatched); + d2c = DRM(histogram_slot)(dispatched_to_completed); + c2f = DRM(histogram_slot)(completed_to_freed); + + q2c = DRM(histogram_slot)(queued_to_dispatched + + dispatched_to_completed); + q2f = DRM(histogram_slot)(queued_to_dispatched + + dispatched_to_completed + + completed_to_freed); + + atomic_inc(&dev->histo.total); + atomic_inc(&dev->histo.queued_to_dispatched[q2d]); + atomic_inc(&dev->histo.dispatched_to_completed[d2c]); + atomic_inc(&dev->histo.completed_to_freed[c2f]); + + atomic_inc(&dev->histo.queued_to_completed[q2c]); + atomic_inc(&dev->histo.queued_to_freed[q2f]); + + } + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +} +#endif + +void DRM(free_buffer)(drm_device_t *dev, drm_buf_t *buf) +{ + if (!buf) return; + + buf->waiting = 0; + buf->pending = 0; + buf->pid = 0; + buf->used = 0; +#if __HAVE_DMA_HISTOGRAM + buf->time_completed = get_cycles(); +#endif + + if ( buf->dma_wait ) { + wakeup( &buf->dma_wait ); + buf->dma_wait = 0; + } +#if __HAVE_DMA_FREELIST + else { + drm_device_dma_t *dma = dev->dma; + /* If processes are waiting, the last one + to wake will put the buffer on the free + list. If no processes are waiting, we + put the buffer on the freelist here. */ + DRM(freelist_put)(dev, &dma->bufs[buf->order].freelist, buf); + } +#endif +} + +#if !__HAVE_DMA_RECLAIM +void DRM(reclaim_buffers)(drm_device_t *dev, pid_t pid) +{ + drm_device_dma_t *dma = dev->dma; + int i; + + if (!dma) return; + for (i = 0; i < dma->buf_count; i++) { + if (dma->buflist[i]->pid == pid) { + switch (dma->buflist[i]->list) { + case DRM_LIST_NONE: + DRM(free_buffer)(dev, dma->buflist[i]); + break; + case DRM_LIST_WAIT: + dma->buflist[i]->list = DRM_LIST_RECLAIM; + break; + default: + /* Buffer already on hardware. */ + break; + } + } + } +} +#endif + + +/* GH: This is a big hack for now... + */ +#if __HAVE_OLD_DMA + +void DRM(clear_next_buffer)(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + + dma->next_buffer = NULL; + if (dma->next_queue && !DRM_BUFCOUNT(&dma->next_queue->waitlist)) { + DRM_OS_WAKEUP_INT(&dma->next_queue->flush_queue); + } + dma->next_queue = NULL; +} + +int DRM(select_queue)(drm_device_t *dev, void (*wrapper)(unsigned long)) +{ + int i; + int candidate = -1; + int j = jiffies; + + if (!dev) { + DRM_ERROR("No device\n"); + return -1; + } + if (!dev->queuelist || !dev->queuelist[DRM_KERNEL_CONTEXT]) { + /* This only happens between the time the + interrupt is initialized and the time + the queues are initialized. */ + return -1; + } + + /* Doing "while locked" DMA? */ + if (DRM_WAITCOUNT(dev, DRM_KERNEL_CONTEXT)) { + return DRM_KERNEL_CONTEXT; + } + + /* If there are buffers on the last_context + queue, and we have not been executing + this context very long, continue to + execute this context. */ + if (dev->last_switch <= j + && dev->last_switch + DRM_TIME_SLICE > j + && DRM_WAITCOUNT(dev, dev->last_context)) { + return dev->last_context; + } + + /* Otherwise, find a candidate */ + for (i = dev->last_checked + 1; i < dev->queue_count; i++) { + if (DRM_WAITCOUNT(dev, i)) { + candidate = dev->last_checked = i; + break; + } + } + + if (candidate < 0) { + for (i = 0; i < dev->queue_count; i++) { + if (DRM_WAITCOUNT(dev, i)) { + candidate = dev->last_checked = i; + break; + } + } + } + + if (wrapper + && candidate >= 0 + && candidate != dev->last_context + && dev->last_switch <= j + && dev->last_switch + DRM_TIME_SLICE > j) { + int s = splclock(); + if (dev->timer.c_time != dev->last_switch + DRM_TIME_SLICE) { + callout_reset(&dev->timer, + dev->last_switch + DRM_TIME_SLICE - j, + (void (*)(void *))wrapper, + dev); + } + splx(s); + return -1; + } + + return candidate; +} + + +int DRM(dma_enqueue)(drm_device_t *dev, drm_dma_t *d) +{ + int i; + drm_queue_t *q; + drm_buf_t *buf; + int idx; + int while_locked = 0; + drm_device_dma_t *dma = dev->dma; + int error; + + DRM_DEBUG("%d\n", d->send_count); + + if (d->flags & _DRM_DMA_WHILE_LOCKED) { + int context = dev->lock.hw_lock->lock; + + if (!_DRM_LOCK_IS_HELD(context)) { + DRM_ERROR("No lock held during \"while locked\"" + " request\n"); + DRM_OS_RETURN(EINVAL); + } + if (d->context != _DRM_LOCKING_CONTEXT(context) + && _DRM_LOCKING_CONTEXT(context) != DRM_KERNEL_CONTEXT) { + DRM_ERROR("Lock held by %d while %d makes" + " \"while locked\" request\n", + _DRM_LOCKING_CONTEXT(context), + d->context); + DRM_OS_RETURN(EINVAL); + } + q = dev->queuelist[DRM_KERNEL_CONTEXT]; + while_locked = 1; + } else { + q = dev->queuelist[d->context]; + } + + + atomic_inc(&q->use_count); + if (atomic_read(&q->block_write)) { + atomic_inc(&q->block_count); + for (;;) { + if (!atomic_read(&q->block_write)) break; + error = tsleep(&q->block_write, PZERO|PCATCH, + "dmawr", 0); + if (error) { + atomic_dec(&q->use_count); + return error; + } + } + atomic_dec(&q->block_count); + } + + for (i = 0; i < d->send_count; i++) { + idx = d->send_indices[i]; + if (idx < 0 || idx >= dma->buf_count) { + atomic_dec(&q->use_count); + DRM_ERROR("Index %d (of %d max)\n", + d->send_indices[i], dma->buf_count - 1); + DRM_OS_RETURN(EINVAL); + } + buf = dma->buflist[ idx ]; + if (buf->pid != DRM_OS_CURRENTPID) { + atomic_dec(&q->use_count); + DRM_ERROR("Process %d using buffer owned by %d\n", + DRM_OS_CURRENTPID, buf->pid); + DRM_OS_RETURN(EINVAL); + } + if (buf->list != DRM_LIST_NONE) { + atomic_dec(&q->use_count); + DRM_ERROR("Process %d using buffer %d on list %d\n", + DRM_OS_CURRENTPID, buf->idx, buf->list); + } + buf->used = d->send_sizes[i]; + buf->while_locked = while_locked; + buf->context = d->context; + if (!buf->used) { + DRM_ERROR("Queueing 0 length buffer\n"); + } + if (buf->pending) { + atomic_dec(&q->use_count); + DRM_ERROR("Queueing pending buffer:" + " buffer %d, offset %d\n", + d->send_indices[i], i); + DRM_OS_RETURN(EINVAL); + } + if (buf->waiting) { + atomic_dec(&q->use_count); + DRM_ERROR("Queueing waiting buffer:" + " buffer %d, offset %d\n", + d->send_indices[i], i); + DRM_OS_RETURN(EINVAL); + } + buf->waiting = 1; + if (atomic_read(&q->use_count) == 1 + || atomic_read(&q->finalization)) { + DRM(free_buffer)(dev, buf); + } else { + DRM(waitlist_put)(&q->waitlist, buf); + atomic_inc(&q->total_queued); + } + } + atomic_dec(&q->use_count); + + return 0; +} + +static int DRM(dma_get_buffers_of_order)(drm_device_t *dev, drm_dma_t *d, + int order) +{ + int i; + drm_buf_t *buf; + drm_device_dma_t *dma = dev->dma; + + for (i = d->granted_count; i < d->request_count; i++) { + buf = DRM(freelist_get)(&dma->bufs[order].freelist, + d->flags & _DRM_DMA_WAIT); + if (!buf) break; + if (buf->pending || buf->waiting) { + DRM_ERROR("Free buffer %d in use by %d (w%d, p%d)\n", + buf->idx, + buf->pid, + buf->waiting, + buf->pending); + } + buf->pid = DRM_OS_CURRENTPID; + if (DRM_OS_COPYTOUSR(&d->request_indices[i], + &buf->idx, + sizeof(buf->idx))) + DRM_OS_RETURN(EFAULT); + + if (DRM_OS_COPYTOUSR(&d->request_sizes[i], + &buf->total, + sizeof(buf->total))) + DRM_OS_RETURN(EFAULT); + + ++d->granted_count; + } + return 0; +} + + +int DRM(dma_get_buffers)(drm_device_t *dev, drm_dma_t *dma) +{ + int order; + int retcode = 0; + int tmp_order; + + order = DRM(order)(dma->request_size); + + dma->granted_count = 0; + retcode = DRM(dma_get_buffers_of_order)(dev, dma, order); + + if (dma->granted_count < dma->request_count + && (dma->flags & _DRM_DMA_SMALLER_OK)) { + for (tmp_order = order - 1; + !retcode + && dma->granted_count < dma->request_count + && tmp_order >= DRM_MIN_ORDER; + --tmp_order) { + + retcode = DRM(dma_get_buffers_of_order)(dev, dma, + tmp_order); + } + } + + if (dma->granted_count < dma->request_count + && (dma->flags & _DRM_DMA_LARGER_OK)) { + for (tmp_order = order + 1; + !retcode + && dma->granted_count < dma->request_count + && tmp_order <= DRM_MAX_ORDER; + ++tmp_order) { + + retcode = DRM(dma_get_buffers_of_order)(dev, dma, + tmp_order); + } + } + return 0; +} + +#endif /* __HAVE_OLD_DMA */ + + +#if __HAVE_DMA_IRQ + +int DRM(irq_install)( drm_device_t *dev, int irq ) +{ + int rid; + int retcode; + + if ( !irq ) + DRM_OS_RETURN(EINVAL); + + DRM_OS_LOCK; + if ( dev->irq ) { + DRM_OS_UNLOCK; + DRM_OS_RETURN(EBUSY); + } + dev->irq = irq; + DRM_OS_UNLOCK; + + DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, irq ); + + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + + dev->dma->next_buffer = NULL; + dev->dma->next_queue = NULL; + dev->dma->this_buffer = NULL; + +#if __HAVE_DMA_IRQ_BH + TASK_INIT(&dev->task, 0, DRM(dma_immediate_bh), dev); +#endif + + /* Before installing handler */ + DRIVER_PREINSTALL(); + + /* Install handler */ + rid = 0; + dev->irqr = bus_alloc_resource(dev->device, SYS_RES_IRQ, &rid, + 0, ~0, 1, RF_SHAREABLE); + if (!dev->irqr) + return ENOENT; + + retcode = bus_setup_intr(dev->device, dev->irqr, INTR_TYPE_TTY, + DRM(dma_service), dev, &dev->irqh); + if ( retcode ) { + DRM_OS_LOCK; + bus_release_resource(dev->device, SYS_RES_IRQ, 0, dev->irqr); + dev->irq = 0; + DRM_OS_UNLOCK; + return retcode; + } + + /* After installing handler */ + DRIVER_POSTINSTALL(); + + return 0; +} + +int DRM(irq_uninstall)( drm_device_t *dev ) +{ + int irq; + + DRM_OS_LOCK; + irq = dev->irq; + dev->irq = 0; + DRM_OS_UNLOCK; + + if ( !irq ) + DRM_OS_RETURN(EINVAL); + + DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, irq ); + + DRIVER_UNINSTALL(); + + bus_teardown_intr(dev->device, dev->irqr, dev->irqh); + bus_release_resource(dev->device, SYS_RES_IRQ, 0, dev->irqr); + + return 0; +} + +int DRM(control)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_control_t ctl; + + DRM_OS_KRNFROMUSR( ctl, (drm_control_t *) data, sizeof(ctl) ); + + switch ( ctl.func ) { + case DRM_INST_HANDLER: + return DRM(irq_install)( dev, ctl.irq ); + case DRM_UNINST_HANDLER: + return DRM(irq_uninstall)( dev ); + default: + DRM_OS_RETURN(EINVAL); + } +} + +#endif /* __HAVE_DMA_IRQ */ + +#endif /* __HAVE_DMA */ diff --git a/bsd-core/drm_drawable.c b/bsd-core/drm_drawable.c new file mode 100644 index 00000000..f57d8628 --- /dev/null +++ b/bsd-core/drm_drawable.c @@ -0,0 +1,50 @@ +/* drm_drawable.h -- IOCTLs for drawables -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@valinux.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * 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, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (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 NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS 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: + * Rickard E. (Rik) Faith <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int DRM(adddraw)( DRM_OS_IOCTL ) +{ + drm_draw_t draw; + + draw.handle = 0; /* NOOP */ + DRM_DEBUG("%d\n", draw.handle); + + DRM_OS_KRNTOUSR( (drm_draw_t *)data, draw, sizeof(draw) ); + + return 0; +} + +int DRM(rmdraw)( DRM_OS_IOCTL ) +{ + return 0; /* NOOP */ +} diff --git a/bsd-core/drm_drv.c b/bsd-core/drm_drv.c new file mode 100644 index 00000000..4e5d76fb --- /dev/null +++ b/bsd-core/drm_drv.c @@ -0,0 +1,1160 @@ +/* drm_drv.h -- Generic driver template -*- linux-c -*- + * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com + * + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * 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, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (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 NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS 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: + * Rickard E. (Rik) Faith <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +/* + * To use this template, you must at least define the following (samples + * given for the MGA driver): + * + * #define DRIVER_AUTHOR "VA Linux Systems, Inc." + * + * #define DRIVER_NAME "mga" + * #define DRIVER_DESC "Matrox G200/G400" + * #define DRIVER_DATE "20001127" + * + * #define DRIVER_MAJOR 2 + * #define DRIVER_MINOR 0 + * #define DRIVER_PATCHLEVEL 2 + * + * #define DRIVER_IOCTL_COUNT DRM_ARRAY_SIZE( mga_ioctls ) + * + * #define DRM(x) mga_##x + */ + +#ifndef __MUST_HAVE_AGP +#define __MUST_HAVE_AGP 0 +#endif +#ifndef __HAVE_CTX_BITMAP +#define __HAVE_CTX_BITMAP 0 +#endif +#ifndef __HAVE_DMA_IRQ +#define __HAVE_DMA_IRQ 0 +#endif +#ifndef __HAVE_DMA_QUEUE +#define __HAVE_DMA_QUEUE 0 +#endif +#ifndef __HAVE_MULTIPLE_DMA_QUEUES +#define __HAVE_MULTIPLE_DMA_QUEUES 0 +#endif +#ifndef __HAVE_DMA_SCHEDULE +#define __HAVE_DMA_SCHEDULE 0 +#endif +#ifndef __HAVE_DMA_FLUSH +#define __HAVE_DMA_FLUSH 0 +#endif +#ifndef __HAVE_DMA_READY +#define __HAVE_DMA_READY 0 +#endif +#ifndef __HAVE_DMA_QUIESCENT +#define __HAVE_DMA_QUIESCENT 0 +#endif +#ifndef __HAVE_RELEASE +#define __HAVE_RELEASE 0 +#endif +#ifndef __HAVE_COUNTERS +#define __HAVE_COUNTERS 0 +#endif +#ifndef __HAVE_SG +#define __HAVE_SG 0 +#endif +#ifndef __HAVE_KERNEL_CTX_SWITCH +#define __HAVE_KERNEL_CTX_SWITCH 0 +#endif +#ifndef PCI_ANY_ID +#define PCI_ANY_ID ~0 +#endif + +#ifndef DRIVER_PREINIT +#define DRIVER_PREINIT() +#endif +#ifndef DRIVER_POSTINIT +#define DRIVER_POSTINIT() +#endif +#ifndef DRIVER_PRERELEASE +#define DRIVER_PRERELEASE() +#endif +#ifndef DRIVER_PRETAKEDOWN +#define DRIVER_PRETAKEDOWN() +#endif +#ifndef DRIVER_POSTCLEANUP +#define DRIVER_POSTCLEANUP() +#endif +#ifndef DRIVER_PRESETUP +#define DRIVER_PRESETUP() +#endif +#ifndef DRIVER_POSTSETUP +#define DRIVER_POSTSETUP() +#endif +#ifndef DRIVER_IOCTLS +#define DRIVER_IOCTLS +#endif +#ifndef DRIVER_FOPS +#if DRM_LINUX +#include <sys/file.h> +#include <sys/proc.h> +#include <machine/../linux/linux.h> +#include <machine/../linux/linux_proto.h> +#include "drm_linux.h" +#endif +#endif + + +/* + * The default number of instances (minor numbers) to initialize. + */ +#ifndef DRIVER_NUM_CARDS +#define DRIVER_NUM_CARDS 1 +#endif + +static int DRM(init)(device_t nbdev); +static void DRM(cleanup)(device_t nbdev); + +#define CDEV_MAJOR 145 +#define DRIVER_SOFTC(unit) \ + ((drm_device_t *) devclass_get_softc(DRM(devclass), unit)) + +#if __REALLY_HAVE_AGP +MODULE_DEPEND(DRIVER_NAME, agp, 1, 1, 1); +#endif +#if DRM_LINUX +MODULE_DEPEND(DRIVER_NAME, linux, 1, 1, 1); +#endif + +static drm_device_t *DRM(device); +static int *DRM(minor); +static int DRM(numdevs) = 0; + + +static drm_ioctl_desc_t DRM(ioctls)[] = { + [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { DRM(version), 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { DRM(getunique), 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { DRM(getmagic), 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { DRM(irq_busid), 0, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_MAP)] = { DRM(getmap), 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CLIENT)] = { DRM(getclient), 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_STATS)] = { DRM(getstats), 0, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { DRM(setunique), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { DRM(block), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { DRM(unblock), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { DRM(authmagic), 1, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { DRM(addmap), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_MAP)] = { DRM(rmmap), 1, 0 }, + +#if __HAVE_CTX_BITMAP + [DRM_IOCTL_NR(DRM_IOCTL_SET_SAREA_CTX)] = { DRM(setsareactx), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_SAREA_CTX)] = { DRM(getsareactx), 1, 0 }, +#endif + + [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { DRM(addctx), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { DRM(rmctx), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { DRM(modctx), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { DRM(getctx), 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { DRM(switchctx), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { DRM(newctx), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { DRM(resctx), 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { DRM(adddraw), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { DRM(rmdraw), 1, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { DRM(lock), 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { DRM(unlock), 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { DRM(finish), 1, 0 }, + +#if __HAVE_DMA + [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)] = { DRM(addbufs), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)] = { DRM(markbufs), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)] = { DRM(infobufs), 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)] = { DRM(mapbufs), 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)] = { DRM(freebufs), 1, 0 }, + + /* The DRM_IOCTL_DMA ioctl should be defined by the driver. + */ +#if __HAVE_DMA_IRQ + [DRM_IOCTL_NR(DRM_IOCTL_CONTROL)] = { DRM(control), 1, 1 }, +#endif +#endif + +#if __REALLY_HAVE_AGP + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { DRM(agp_acquire), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { DRM(agp_release), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)] = { DRM(agp_enable), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)] = { DRM(agp_info), 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)] = { DRM(agp_alloc), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)] = { DRM(agp_free), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)] = { DRM(agp_bind), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = { DRM(agp_unbind), 1, 1 }, +#endif + +#if __REALLY_HAVE_SG + [DRM_IOCTL_NR(DRM_IOCTL_SG_ALLOC)] = { DRM(sg_alloc), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_SG_FREE)] = { DRM(sg_free), 1, 1 }, +#endif + + DRIVER_IOCTLS +}; + +#define DRIVER_IOCTL_COUNT DRM_ARRAY_SIZE( DRM(ioctls) ) + + +static int DRM(probe)(device_t dev) +{ + const char *s = 0; + + int pciid=pci_get_devid(dev); + int vendor = (pciid & 0x0000ffff); + int device = (pciid & 0xffff0000) >> 16; + int i=0, done=0; + DRM_INFO("Checking PCI vendor=%d, device=%d\n", vendor, device); + while ( !done && (DRM(devicelist)[i].vendor != 0 ) ) { + if ( (DRM(devicelist)[i].vendor == vendor) && + (DRM(devicelist)[i].device == device) ) { + done=1; + if ( DRM(devicelist)[i].supported ) + s = DRM(devicelist)[i].name; + else + DRM_INFO("%s not supported\n", DRM(devicelist)[i].name); + } + i++; + } + + if (s) { + device_set_desc(dev, s); + return 0; + } + + return ENXIO; +} + +static int DRM(attach)(device_t dev) +{ + return DRM(init)(dev); +} + +static int DRM(detach)(device_t dev) +{ + DRM(cleanup)(dev); + return 0; +} + +static device_method_t DRM(methods)[] = { + /* Device interface */ + DEVMETHOD(device_probe, DRM( probe)), + DEVMETHOD(device_attach, DRM( attach)), + DEVMETHOD(device_detach, DRM( detach)), + + { 0, 0 } +}; + +static driver_t DRM(driver) = { + "drm", + DRM(methods), + sizeof(drm_device_t), +}; + +static devclass_t DRM( devclass); + +static struct cdevsw DRM( cdevsw) = { + /* open */ DRM( open ), + /* close */ DRM( close ), + /* read */ DRM( read ), + /* write */ DRM( write ), + /* ioctl */ DRM( ioctl ), + /* poll */ DRM( poll ), + /* mmap */ DRM( mmap ), + /* strategy */ nostrategy, + /* name */ DRIVER_NAME, + /* maj */ CDEV_MAJOR, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ D_TTY | D_TRACKCLOSE, +#if __FreeBSD_version >= 500000 + /* kqfilter */ 0 +#else + /* bmaj */ -1 +#endif +}; + +static int DRM(setup)( drm_device_t *dev ) +{ + int i; + + DRIVER_PRESETUP(); + atomic_set( &dev->ioctl_count, 0 ); + atomic_set( &dev->vma_count, 0 ); + dev->buf_use = 0; + atomic_set( &dev->buf_alloc, 0 ); + +#if __HAVE_DMA + i = DRM(dma_setup)( dev ); + if ( i < 0 ) + return i; +#endif + + dev->counters = 6 + __HAVE_COUNTERS; + dev->types[0] = _DRM_STAT_LOCK; + dev->types[1] = _DRM_STAT_OPENS; + dev->types[2] = _DRM_STAT_CLOSES; + dev->types[3] = _DRM_STAT_IOCTLS; + dev->types[4] = _DRM_STAT_LOCKS; + dev->types[5] = _DRM_STAT_UNLOCKS; +#ifdef __HAVE_COUNTER6 + dev->types[6] = __HAVE_COUNTER6; +#endif +#ifdef __HAVE_COUNTER7 + dev->types[7] = __HAVE_COUNTER7; +#endif +#ifdef __HAVE_COUNTER8 + dev->types[8] = __HAVE_COUNTER8; +#endif +#ifdef __HAVE_COUNTER9 + dev->types[9] = __HAVE_COUNTER9; +#endif +#ifdef __HAVE_COUNTER10 + dev->types[10] = __HAVE_COUNTER10; +#endif +#ifdef __HAVE_COUNTER11 + dev->types[11] = __HAVE_COUNTER11; +#endif +#ifdef __HAVE_COUNTER12 + dev->types[12] = __HAVE_COUNTER12; +#endif +#ifdef __HAVE_COUNTER13 + dev->types[13] = __HAVE_COUNTER13; +#endif +#ifdef __HAVE_COUNTER14 + dev->types[14] = __HAVE_COUNTER14; +#endif +#ifdef __HAVE_COUNTER15 + dev->types[14] = __HAVE_COUNTER14; +#endif + + for ( i = 0 ; i < DRM_ARRAY_SIZE(dev->counts) ; i++ ) + atomic_set( &dev->counts[i], 0 ); + + for ( i = 0 ; i < DRM_HASH_SIZE ; i++ ) { + dev->magiclist[i].head = NULL; + dev->magiclist[i].tail = NULL; + } + + dev->maplist = DRM(alloc)(sizeof(*dev->maplist), + DRM_MEM_MAPS); + if(dev->maplist == NULL) DRM_OS_RETURN(ENOMEM); + memset(dev->maplist, 0, sizeof(*dev->maplist)); + TAILQ_INIT(dev->maplist); + dev->map_count = 0; + + dev->vmalist = NULL; + dev->lock.hw_lock = NULL; + dev->lock.lock_queue = 0; + dev->queue_count = 0; + dev->queue_reserved = 0; + dev->queue_slots = 0; + dev->queuelist = NULL; + dev->irq = 0; + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + dev->last_context = 0; + dev->last_switch = 0; + dev->last_checked = 0; +#if __FreeBSD_version >= 500000 + callout_init( &dev->timer, 1 ); +#else + callout_init( &dev->timer ); +#endif + dev->context_wait = 0; + + dev->ctx_start = 0; + dev->lck_start = 0; + + dev->buf_rp = dev->buf; + dev->buf_wp = dev->buf; + dev->buf_end = dev->buf + DRM_BSZ; + dev->buf_sigio = NULL; + dev->buf_readers = 0; + dev->buf_writers = 0; + dev->buf_selecting = 0; + + DRM_DEBUG( "\n" ); + + /* The kernel's context could be created here, but is now created + * in drm_dma_enqueue. This is more resource-efficient for + * hardware that does not do DMA, but may mean that + * drm_select_queue fails between the time the interrupt is + * initialized and the time the queues are initialized. + */ + DRIVER_POSTSETUP(); + return 0; +} + + +static int DRM(takedown)( drm_device_t *dev ) +{ + drm_magic_entry_t *pt, *next; + drm_map_t *map; + drm_map_list_entry_t *list; + drm_vma_entry_t *vma, *vma_next; + int i; + + DRM_DEBUG( "\n" ); + + DRIVER_PRETAKEDOWN(); +#if __HAVE_DMA_IRQ + if ( dev->irq ) DRM(irq_uninstall)( dev ); +#endif + + DRM_OS_LOCK; + callout_stop( &dev->timer ); + + if ( dev->devname ) { + DRM(free)( dev->devname, strlen( dev->devname ) + 1, + DRM_MEM_DRIVER ); + dev->devname = NULL; + } + + if ( dev->unique ) { + DRM(free)( dev->unique, strlen( dev->unique ) + 1, + DRM_MEM_DRIVER ); + dev->unique = NULL; + dev->unique_len = 0; + } + /* Clear pid list */ + for ( i = 0 ; i < DRM_HASH_SIZE ; i++ ) { + for ( pt = dev->magiclist[i].head ; pt ; pt = next ) { + next = pt->next; + DRM(free)( pt, sizeof(*pt), DRM_MEM_MAGIC ); + } + dev->magiclist[i].head = dev->magiclist[i].tail = NULL; + } + +#if __REALLY_HAVE_AGP + /* Clear AGP information */ + if ( dev->agp ) { + drm_agp_mem_t *entry; + drm_agp_mem_t *nexte; + + /* Remove AGP resources, but leave dev->agp + intact until drv_cleanup is called. */ + for ( entry = dev->agp->memory ; entry ; entry = nexte ) { + nexte = entry->next; + if ( entry->bound ) DRM(unbind_agp)( entry->handle ); + DRM(free_agp)( entry->handle, entry->pages ); + DRM(free)( entry, sizeof(*entry), DRM_MEM_AGPLISTS ); + } + dev->agp->memory = NULL; + + if ( dev->agp->acquired ) DRM(agp_do_release)(); + + dev->agp->acquired = 0; + dev->agp->enabled = 0; + } +#endif + + /* Clear vma list (only built for debugging) */ + if ( dev->vmalist ) { + for ( vma = dev->vmalist ; vma ; vma = vma_next ) { + vma_next = vma->next; + DRM(free)( vma, sizeof(*vma), DRM_MEM_VMAS ); + } + dev->vmalist = NULL; + } + + if( dev->maplist ) { + while ((list=TAILQ_FIRST(dev->maplist))) { + map = list->map; + switch ( map->type ) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#if __REALLY_HAVE_MTRR + if ( map->mtrr >= 0 ) { + int retcode; + retcode = mtrr_del( map->mtrr, + map->offset, + map->size ); + DRM_DEBUG( "mtrr_del=%d\n", retcode ); + } +#endif + DRM(ioremapfree)( map->handle, map->size ); + break; + case _DRM_SHM: + DRM(free_pages)((unsigned long)map->handle, + DRM(order)(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + break; + + case _DRM_AGP: + /* Do nothing here, because this is all + * handled in the AGP/GART driver. + */ + break; + case _DRM_SCATTER_GATHER: + /* Handle it, but do nothing, if REALLY_HAVE_SG + * isn't defined. + */ +#if __REALLY_HAVE_SG + if(dev->sg) { + DRM(sg_cleanup)(dev->sg); + dev->sg = NULL; + } +#endif + break; + } + TAILQ_REMOVE(dev->maplist, list, link); + DRM(free)(list, sizeof(*list), DRM_MEM_MAPS); + DRM(free)(map, sizeof(*map), DRM_MEM_MAPS); + } + DRM(free)(dev->maplist, sizeof(*dev->maplist), DRM_MEM_MAPS); + dev->maplist = NULL; + } + +#if __HAVE_DMA_QUEUE || __HAVE_MULTIPLE_DMA_QUEUES + if ( dev->queuelist ) { + for ( i = 0 ; i < dev->queue_count ; i++ ) { + DRM(waitlist_destroy)( &dev->queuelist[i]->waitlist ); + if ( dev->queuelist[i] ) { + DRM(free)( dev->queuelist[i], + sizeof(*dev->queuelist[0]), + DRM_MEM_QUEUES ); + dev->queuelist[i] = NULL; + } + } + DRM(free)( dev->queuelist, + dev->queue_slots * sizeof(*dev->queuelist), + DRM_MEM_QUEUES ); + dev->queuelist = NULL; + } + dev->queue_count = 0; +#endif + +#if __HAVE_DMA + DRM(dma_takedown)( dev ); +#endif + if ( dev->lock.hw_lock ) { + dev->lock.hw_lock = NULL; /* SHM removed */ + dev->lock.pid = 0; + DRM_OS_WAKEUP_INT(&dev->lock.lock_queue); + } + DRM_OS_UNLOCK; + + return 0; +} + +/* + * Figure out how many instances to initialize. + */ +static int drm_count_cards(void) +{ + int num = 0; +#if defined(DRIVER_CARD_LIST) + int i; + drm_pci_list_t *l; + u16 device, vendor; + struct pci_dev *pdev = NULL; +#endif + + DRM_DEBUG( "\n" ); + +#if defined(DRIVER_COUNT_CARDS) + num = DRIVER_COUNT_CARDS(); +#elif defined(DRIVER_CARD_LIST) + for (i = 0, l = DRIVER_CARD_LIST; l[i].vendor != 0; i++) { + pdev = NULL; + vendor = l[i].vendor; + device = l[i].device; + if(device == 0xffff) device = PCI_ANY_ID; + if(vendor == 0xffff) vendor = PCI_ANY_ID; + while ((pdev = pci_find_device(vendor, device, pdev))) { + num++; /* FIXME: What about two cards of the same device id? */ + } + } +#else + num = DRIVER_NUM_CARDS; +#endif + DRM_DEBUG("numdevs = %d\n", num); + return num; +} + +/* drm_init is called via init_module at module load time, or via + * linux/init/main.c (this is not currently supported). + */ +static int DRM(init)( device_t nbdev ) +{ + + drm_device_t *dev; + int i; +#if __HAVE_CTX_BITMAP + int retcode; +#endif + DRM_DEBUG( "\n" ); + +#ifdef MODULE + DRM(parse_options)( drm_opts ); +#endif + + DRM(numdevs) = drm_count_cards(); + /* Force at least one instance. */ + if (DRM(numdevs) <= 0) + DRM(numdevs) = 1; + + DRM(device) = DRM_OS_MALLOC(sizeof(*DRM(device)) * DRM(numdevs)); + if (!DRM(device)) { + DRM_OS_RETURN(ENOMEM); + } + DRM(minor) = DRM_OS_MALLOC(sizeof(*(DRM(minor))) * DRM(numdevs)); + if (!DRM(minor)) { + DRM_OS_FREE(DRM(device)); + DRM_OS_RETURN(ENOMEM); + } + + DRIVER_PREINIT(); + + + for (i = 0; i < DRM(numdevs); i++) { + int unit = device_get_unit(nbdev); + /* FIXME??? - multihead !!! */ + dev = device_get_softc(nbdev); + memset( (void *)dev, 0, sizeof(*dev) ); + DRM(minor)[i]=unit; + DRM_OS_SPININIT(dev->count_lock, "drm device"); + lockinit(&dev->dev_lock, PZERO, "drmlk", 0, 0); + dev->device = nbdev; + dev->devnode = make_dev( &DRM(cdevsw), + unit, + DRM_DEV_UID, + DRM_DEV_GID, + DRM_DEV_MODE, + "dri/card%d", unit ); + dev->name = DRIVER_NAME; + DRM(mem_init)(); + DRM(sysctl_init)(dev); + TAILQ_INIT(&dev->files); + +#if __REALLY_HAVE_AGP + dev->agp = DRM(agp_init)(); +#if __MUST_HAVE_AGP + if ( dev->agp == NULL ) { + DRM_ERROR( "Cannot initialize the agpgart module.\n" ); + DRM(sysctl_cleanup)( dev ); + destroy_dev(dev->devnode); + DRM(takedown)( dev ); + DRM_OS_RETURN(ENOMEM); + } +#endif +#if __REALLY_HAVE_MTRR + if (dev->agp) + dev->agp->agp_mtrr = mtrr_add( dev->agp->agp_info.aper_base, + dev->agp->agp_info.aper_size*1024*1024, + MTRR_TYPE_WRCOMB, + 1 ); +#endif +#endif + +#if __HAVE_CTX_BITMAP + retcode = DRM(ctxbitmap_init)( dev ); + if( retcode ) { + DRM_ERROR( "Cannot allocate memory for context bitmap.\n" ); + DRM(sysctl_cleanup)( dev ); + destroy_dev(dev->devnode); + DRM(takedown)( dev ); + return retcode; + } +#endif + DRM_INFO( "Initialized %s %d.%d.%d %s on minor %d\n", + DRIVER_NAME, + DRIVER_MAJOR, + DRIVER_MINOR, + DRIVER_PATCHLEVEL, + DRIVER_DATE, + DRM(minor)[i] ); + } + + DRIVER_POSTINIT(); + + return 0; +} + +/* drm_cleanup is called via cleanup_module at module unload time. + */ +static void DRM(cleanup)(device_t nbdev) +{ + drm_device_t *dev; + int i; + + DRM_DEBUG( "\n" ); + + for (i = DRM(numdevs) - 1; i >= 0; i--) { + /* FIXME??? - multihead */ + dev = device_get_softc(nbdev); + DRM(sysctl_cleanup)( dev ); + destroy_dev(dev->devnode); +#if __HAVE_CTX_BITMAP + DRM(ctxbitmap_cleanup)( dev ); +#endif + +#if __REALLY_HAVE_AGP && __REALLY_HAVE_MTRR + if ( dev->agp && dev->agp->agp_mtrr >= 0) { + int retval; + retval = mtrr_del( dev->agp->agp_mtrr, + dev->agp->agp_info.aper_base, + dev->agp->agp_info.aper_size*1024*1024 ); + DRM_DEBUG( "mtrr_del=%d\n", retval ); + } +#endif + + DRM(takedown)( dev ); + +#if __REALLY_HAVE_AGP + if ( dev->agp ) { + DRM(agp_uninit)(); + DRM(free)( dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS ); + dev->agp = NULL; + } +#endif + } + DRIVER_POSTCLEANUP(); + DRM_OS_FREE(DRM(minor)); + DRM_OS_FREE(DRM(device)); + DRM(numdevs) = 0; +} + + +int DRM(version)( DRM_OS_IOCTL ) +{ + drm_version_t version; + int len; + + DRM_OS_KRNFROMUSR( version, (drm_version_t *)data, sizeof(version) ); + +#define DRM_COPY( name, value ) \ + len = strlen( value ); \ + if ( len > name##_len ) len = name##_len; \ + name##_len = strlen( value ); \ + if ( len && name ) { \ + if ( DRM_OS_COPYTOUSR( name, value, len ) ) \ + DRM_OS_RETURN(EFAULT); \ + } + + version.version_major = DRIVER_MAJOR; + version.version_minor = DRIVER_MINOR; + version.version_patchlevel = DRIVER_PATCHLEVEL; + + DRM_COPY( version.name, DRIVER_NAME ); + DRM_COPY( version.date, DRIVER_DATE ); + DRM_COPY( version.desc, DRIVER_DESC ); + + DRM_OS_KRNTOUSR( (drm_version_t *)data, version, sizeof(version) ); + + return 0; +} + +int DRM( open)(dev_t kdev, int flags, int fmt, DRM_OS_STRUCTPROC *p) +{ + drm_device_t *dev = NULL; + int retcode = 0; + int i; + + for (i = 0; i < DRM(numdevs); i++) { + /* FIXME ??? - multihead */ + dev = DRIVER_SOFTC(minor(kdev)); + } + if (!dev) { + DRM_OS_RETURN(ENODEV); + } + + DRM_DEBUG( "open_count = %d\n", dev->open_count ); + + device_busy(dev->device); + retcode = DRM(open_helper)(kdev, flags, fmt, p, dev); + + if ( !retcode ) { + atomic_inc( &dev->counts[_DRM_STAT_OPENS] ); + DRM_OS_SPINLOCK( &dev->count_lock ); + if ( !dev->open_count++ ) { + DRM_OS_SPINUNLOCK( &dev->count_lock ); + return DRM(setup)( dev ); + } + DRM_OS_SPINUNLOCK( &dev->count_lock ); + } + device_unbusy(dev->device); + + return retcode; +} + +int DRM( close)(dev_t kdev, int flags, int fmt, DRM_OS_STRUCTPROC *p) +{ + drm_file_t *priv; + drm_device_t *dev = kdev->si_drv1; + int retcode = 0; + + DRM_DEBUG( "open_count = %d\n", dev->open_count ); + priv = DRM(find_file_by_proc)(dev, p); + if (!priv) { + DRM_DEBUG("can't find authenticator\n"); + return EINVAL; + } + + DRIVER_PRERELEASE(); + + /* ======================================================== + * Begin inline drm_release + */ + + DRM_DEBUG( "pid = %d, device = 0x%lx, open_count = %d\n", + DRM_OS_CURRENTPID, (long)dev->device, dev->open_count ); + + if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) + && dev->lock.pid == DRM_OS_CURRENTPID) { + DRM_DEBUG("Process %d dead, freeing lock for context %d\n", + DRM_OS_CURRENTPID, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); +#if HAVE_DRIVER_RELEASE + DRIVER_RELEASE(); +#endif + DRM(lock_free)(dev, + &dev->lock.hw_lock->lock, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + + /* FIXME: may require heavy-handed reset of + hardware at this point, possibly + processed via a callback to the X + server. */ + } +#if __HAVE_RELEASE + else if ( dev->lock.hw_lock ) { + /* The lock is required to reclaim buffers */ + for (;;) { + if ( !dev->lock.hw_lock ) { + /* Device has been unregistered */ + retcode = EINTR; + break; + } + if ( DRM(lock_take)( &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT ) ) { + dev->lock.pid = p->p_pid; + dev->lock.lock_time = jiffies; + atomic_inc( &dev->counts[_DRM_STAT_LOCKS] ); + break; /* Got lock */ + } + /* Contention */ +#if 0 + atomic_inc( &dev->total_sleeps ); +#endif + retcode = tsleep(&dev->lock.lock_queue, + PZERO|PCATCH, + "drmlk2", + 0); + if (retcode) + break; + } + if( !retcode ) { + DRIVER_RELEASE(); + DRM(lock_free)( dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT ); + } + } +#elif __HAVE_DMA + DRM(reclaim_buffers)( dev, priv->pid ); +#endif + + funsetown(dev->buf_sigio); + + DRM_OS_LOCK; + priv = DRM(find_file_by_proc)(dev, p); + if (priv) { + priv->refs--; + if (!priv->refs) { + TAILQ_REMOVE(&dev->files, priv, link); + } + } + DRM_OS_UNLOCK; + + DRM(free)( priv, sizeof(*priv), DRM_MEM_FILES ); + + /* ======================================================== + * End inline drm_release + */ + + atomic_inc( &dev->counts[_DRM_STAT_CLOSES] ); + DRM_OS_SPINLOCK( &dev->count_lock ); + if ( !--dev->open_count ) { + if ( atomic_read( &dev->ioctl_count ) || dev->blocked ) { + DRM_ERROR( "Device busy: %ld %d\n", + (unsigned long)atomic_read( &dev->ioctl_count ), + dev->blocked ); + DRM_OS_SPINUNLOCK( &dev->count_lock ); + DRM_OS_RETURN(EBUSY); + } + DRM_OS_SPINUNLOCK( &dev->count_lock ); + device_unbusy(dev->device); + return DRM(takedown)( dev ); + } + DRM_OS_SPINUNLOCK( &dev->count_lock ); + + + DRM_OS_RETURN(retcode); +} + +/* DRM(ioctl) is called whenever a process performs an ioctl on /dev/drm. + */ +int DRM(ioctl)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + int retcode = 0; + drm_ioctl_desc_t *ioctl; + d_ioctl_t *func; + int nr = DRM_IOCTL_NR(cmd); + DRM_OS_PRIV; + + atomic_inc( &dev->ioctl_count ); + atomic_inc( &dev->counts[_DRM_STAT_IOCTLS] ); + ++priv->ioctl_count; + + DRM_DEBUG( "pid=%d, cmd=0x%02lx, nr=0x%02x, dev 0x%lx, auth=%d\n", + DRM_OS_CURRENTPID, cmd, nr, (long)dev->device, priv->authenticated ); + + switch (cmd) { + case FIONBIO: + atomic_dec(&dev->ioctl_count); + return 0; + + case FIOASYNC: + atomic_dec(&dev->ioctl_count); + dev->flags |= FASYNC; + return 0; + + case FIOSETOWN: + atomic_dec(&dev->ioctl_count); + return fsetown(*(int *)data, &dev->buf_sigio); + + case FIOGETOWN: + atomic_dec(&dev->ioctl_count); + *(int *) data = fgetown(dev->buf_sigio); + return 0; + } + + if ( nr >= DRIVER_IOCTL_COUNT ) { + retcode = EINVAL; + } else { + ioctl = &DRM(ioctls)[nr]; + func = ioctl->func; + + if ( !func ) { + DRM_DEBUG( "no function\n" ); + retcode = EINVAL; + } else if ( ( ioctl->root_only && DRM_OS_CHECKSUSER ) + || ( ioctl->auth_needed && !priv->authenticated ) ) { + retcode = EACCES; + } else { + retcode = func( kdev, cmd, data, flags, p ); + } + } + + atomic_dec( &dev->ioctl_count ); + DRM_OS_RETURN(retcode); +} + +int DRM(lock)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_lock_t lock; + int ret = 0; +#if __HAVE_MULTIPLE_DMA_QUEUES + drm_queue_t *q; +#endif +#if __HAVE_DMA_HISTOGRAM + cycles_t start; + + dev->lck_start = start = get_cycles(); +#endif + + DRM_OS_KRNFROMUSR( lock, (drm_lock_t *)data, sizeof(lock) ); + + if ( lock.context == DRM_KERNEL_CONTEXT ) { + DRM_ERROR( "Process %d using kernel context %d\n", + DRM_OS_CURRENTPID, lock.context ); + DRM_OS_RETURN(EINVAL); + } + + DRM_DEBUG( "%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", + lock.context, DRM_OS_CURRENTPID, + dev->lock.hw_lock->lock, lock.flags ); + +#if __HAVE_DMA_QUEUE + if ( lock.context < 0 ) + DRM_OS_RETURN(EINVAL); +#elif __HAVE_MULTIPLE_DMA_QUEUES + if ( lock.context < 0 || lock.context >= dev->queue_count ) + DRM_OS_RETURN(EINVAL); + q = dev->queuelist[lock.context]; +#endif + +#if __HAVE_DMA_FLUSH + ret = DRM(flush_block_and_flush)( dev, lock.context, lock.flags ); +#endif + if ( !ret ) { + for (;;) { + if ( !dev->lock.hw_lock ) { + /* Device has been unregistered */ + ret = EINTR; + break; + } + if ( DRM(lock_take)( &dev->lock.hw_lock->lock, + lock.context ) ) { + dev->lock.pid = DRM_OS_CURRENTPID; + dev->lock.lock_time = jiffies; + atomic_inc( &dev->counts[_DRM_STAT_LOCKS] ); + break; /* Got lock */ + } + + /* Contention */ + ret = tsleep(&dev->lock.lock_queue, + PZERO|PCATCH, + "drmlk2", + 0); + if (ret) + break; + } + } + +#if __HAVE_DMA_FLUSH + DRM(flush_unblock)( dev, lock.context, lock.flags ); /* cleanup phase */ +#endif + + if ( !ret ) { + +#if __HAVE_DMA_READY + if ( lock.flags & _DRM_LOCK_READY ) { + DRIVER_DMA_READY(); + } +#endif +#if __HAVE_DMA_QUIESCENT + if ( lock.flags & _DRM_LOCK_QUIESCENT ) { + DRIVER_DMA_QUIESCENT(); + } +#endif +#if __HAVE_KERNEL_CTX_SWITCH + if ( dev->last_context != lock.context ) { + DRM(context_switch)(dev, dev->last_context, + lock.context); + } +#endif + } + + DRM_DEBUG( "%d %s\n", lock.context, ret ? "interrupted" : "has lock" ); + +#if __HAVE_DMA_HISTOGRAM + atomic_inc(&dev->histo.lacq[DRM(histogram_slot)(get_cycles()-start)]); +#endif + + DRM_OS_RETURN(ret); +} + + +int DRM(unlock)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_lock_t lock; + + DRM_OS_KRNFROMUSR( lock, (drm_lock_t *)data, sizeof(lock) ) ; + + if ( lock.context == DRM_KERNEL_CONTEXT ) { + DRM_ERROR( "Process %d using kernel context %d\n", + DRM_OS_CURRENTPID, lock.context ); + DRM_OS_RETURN(EINVAL); + } + + atomic_inc( &dev->counts[_DRM_STAT_UNLOCKS] ); + +#if __HAVE_KERNEL_CTX_SWITCH + /* We no longer really hold it, but if we are the next + * agent to request it then we should just be able to + * take it immediately and not eat the ioctl. + */ + dev->lock.pid = 0; + { + __volatile__ unsigned int *plock = &dev->lock.hw_lock->lock; + unsigned int old, new, prev, ctx; + + ctx = lock.context; + do { + old = *plock; + new = ctx; + prev = cmpxchg(plock, old, new); + } while (prev != old); + } + wake_up_interruptible(&dev->lock.lock_queue); +#else + DRM(lock_transfer)( dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT ); +#if __HAVE_DMA_SCHEDULE + DRM(dma_schedule)( dev, 1 ); +#endif + + /* FIXME: Do we ever really need to check this??? + */ + if ( 1 /* !dev->context_flag */ ) { + if ( DRM(lock_free)( dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT ) ) { + DRM_ERROR( "\n" ); + } + } +#endif /* !__HAVE_KERNEL_CTX_SWITCH */ + + return 0; +} + +#if DRM_LINUX +static linux_ioctl_function_t DRM( linux_ioctl); +static struct linux_ioctl_handler DRM( handler) = {DRM( linux_ioctl), LINUX_IOCTL_DRM_MIN, LINUX_IOCTL_DRM_MAX}; +SYSINIT (DRM( register), SI_SUB_KLD, SI_ORDER_MIDDLE, linux_ioctl_register_handler, &DRM( handler)); +SYSUNINIT(DRM( unregister), SI_SUB_KLD, SI_ORDER_MIDDLE, linux_ioctl_unregister_handler, &DRM( handler)); + +/* + * Linux emulation IOCTL + */ +static int +DRM(linux_ioctl)(DRM_OS_STRUCTPROC *p, struct linux_ioctl_args* args) +{ +#if (__FreeBSD_version >= 500000) + struct file *fp = p->td_proc->p_fd->fd_ofiles[args->fd]; +#else + struct file *fp = p->p_fd->fd_ofiles[args->fd]; +#endif + u_long cmd = args->cmd; + caddr_t data = (caddr_t) args->arg; + /* + * Pass the ioctl off to our standard handler. + */ + return(fo_ioctl(fp, cmd, data, p)); +} +#endif /* DRM_LINUX */ diff --git a/bsd-core/drm_fops.c b/bsd-core/drm_fops.c new file mode 100644 index 00000000..53af39cd --- /dev/null +++ b/bsd-core/drm_fops.c @@ -0,0 +1,223 @@ +/* drm_fops.h -- File operations for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@valinux.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * 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, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (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 NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS 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: + * Rickard E. (Rik) Faith <faith@valinux.com> + * Daryll Strauss <daryll@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" + + +#include <sys/signalvar.h> +#include <sys/poll.h> + +drm_file_t *DRM(find_file_by_proc)(drm_device_t *dev, DRM_OS_STRUCTPROC *p) +{ +#if __FreeBSD_version >= 500021 + uid_t uid = p->td_proc->p_ucred->cr_svuid; + pid_t pid = p->td_proc->p_pid; +#else + uid_t uid = p->p_cred->p_svuid; + pid_t pid = p->p_pid; +#endif + drm_file_t *priv; + + TAILQ_FOREACH(priv, &dev->files, link) + if (priv->pid == pid && priv->uid == uid) + return priv; + return NULL; +} + +/* DRM(open) is called whenever a process opens /dev/drm. */ + +int DRM(open_helper)(dev_t kdev, int flags, int fmt, DRM_OS_STRUCTPROC *p, + drm_device_t *dev) +{ + int m = minor(kdev); + drm_file_t *priv; + + if (flags & O_EXCL) + return EBUSY; /* No exclusive opens */ + dev->flags = flags; + if (!DRM(cpu_valid)()) + DRM_OS_RETURN(EINVAL); + + DRM_DEBUG("pid = %d, minor = %d\n", DRM_OS_CURRENTPID, m); + + /* FIXME: linux mallocs and bzeros here */ + priv = (drm_file_t *) DRM(find_file_by_proc)(dev, p); + if (priv) { + priv->refs++; + } else { + priv = (drm_file_t *) DRM(alloc)(sizeof(*priv), DRM_MEM_FILES); + bzero(priv, sizeof(*priv)); +#if __FreeBSD_version >= 500000 + priv->uid = p->td_proc->p_ucred->cr_svuid; + priv->pid = p->td_proc->p_pid; +#else + priv->uid = p->p_cred->p_svuid; + priv->pid = p->p_pid; +#endif + + priv->refs = 1; + priv->minor = m; + priv->devXX = dev; + priv->ioctl_count = 0; + priv->authenticated = !DRM_OS_CHECKSUSER; + lockmgr(&dev->dev_lock, LK_EXCLUSIVE, 0, p); + TAILQ_INSERT_TAIL(&dev->files, priv, link); + lockmgr(&dev->dev_lock, LK_RELEASE, 0, p); + } + + kdev->si_drv1 = dev; + + + return 0; +} + + +/* The drm_read and drm_write_string code (especially that which manages + the circular buffer), is based on Alessandro Rubini's LINUX DEVICE + DRIVERS (Cambridge: O'Reilly, 1998), pages 111-113. */ + +ssize_t DRM(read)(dev_t kdev, struct uio *uio, int ioflag) +{ + DRM_OS_DEVICE; + int left; + int avail; + int send; + int cur; + int error = 0; + + DRM_DEBUG("%p, %p\n", dev->buf_rp, dev->buf_wp); + + while (dev->buf_rp == dev->buf_wp) { + DRM_DEBUG(" sleeping\n"); + if (dev->flags & FASYNC) + return EWOULDBLOCK; + error = tsleep(&dev->buf_rp, PZERO|PCATCH, "drmrd", 0); + if (error) { + DRM_DEBUG(" interrupted\n"); + return error; + } + DRM_DEBUG(" awake\n"); + } + + left = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ; + avail = DRM_BSZ - left; + send = DRM_MIN(avail, uio->uio_resid); + + while (send) { + if (dev->buf_wp > dev->buf_rp) { + cur = DRM_MIN(send, dev->buf_wp - dev->buf_rp); + } else { + cur = DRM_MIN(send, dev->buf_end - dev->buf_rp); + } + error = uiomove(dev->buf_rp, cur, uio); + if (error) + break; + dev->buf_rp += cur; + if (dev->buf_rp == dev->buf_end) dev->buf_rp = dev->buf; + send -= cur; + } + + wakeup(&dev->buf_wp); + return error; +} + +int DRM(write_string)(drm_device_t *dev, const char *s) +{ + int left = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ; + int send = strlen(s); + int count; + + DRM_DEBUG("%d left, %d to send (%p, %p)\n", + left, send, dev->buf_rp, dev->buf_wp); + + if (left == 1 || dev->buf_wp != dev->buf_rp) { + DRM_ERROR("Buffer not empty (%d left, wp = %p, rp = %p)\n", + left, + dev->buf_wp, + dev->buf_rp); + } + + while (send) { + if (dev->buf_wp >= dev->buf_rp) { + count = DRM_MIN(send, dev->buf_end - dev->buf_wp); + if (count == left) --count; /* Leave a hole */ + } else { + count = DRM_MIN(send, dev->buf_rp - dev->buf_wp - 1); + } + strncpy(dev->buf_wp, s, count); + dev->buf_wp += count; + if (dev->buf_wp == dev->buf_end) dev->buf_wp = dev->buf; + send -= count; + } + + if (dev->buf_selecting) { + dev->buf_selecting = 0; + selwakeup(&dev->buf_sel); + } + + DRM_DEBUG("dev->buf_sigio=%p\n", dev->buf_sigio); + if (dev->buf_sigio) { + DRM_DEBUG("dev->buf_sigio->sio_pgid=%d\n", dev->buf_sigio->sio_pgid); + pgsigio(dev->buf_sigio, SIGIO, 0); + } + DRM_DEBUG("waking\n"); + wakeup(&dev->buf_rp); + + return 0; +} + +int DRM(poll)(dev_t kdev, int events, DRM_OS_STRUCTPROC *p) +{ + drm_device_t *dev = kdev->si_drv1; + int s; + int revents = 0; + + s = spldrm(); + if (events & (POLLIN | POLLRDNORM)) { + int left = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ; + if (left > 0) + revents |= events & (POLLIN | POLLRDNORM); + else + selrecord(p, &dev->buf_sel); + } + splx(s); + + return revents; +} + +int DRM(write)(dev_t kdev, struct uio *uio, int ioflag) +{ + DRM_DEBUG("pid = %d, device = %p, open_count = %d\n", + curproc->p_pid, ((drm_device_t *)kdev->si_drv1)->device, ((drm_device_t *)kdev->si_drv1)->open_count); + return 0; +} diff --git a/bsd-core/drm_ioctl.c b/bsd-core/drm_ioctl.c new file mode 100644 index 00000000..1e8281e6 --- /dev/null +++ b/bsd-core/drm_ioctl.c @@ -0,0 +1,237 @@ +/* drm_ioctl.h -- IOCTL processing for DRM -*- linux-c -*- + * Created: Fri Jan 8 09:01:26 1999 by faith@valinux.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * 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, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (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 NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS 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: + * Rickard E. (Rik) Faith <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include <sys/bus.h> +#include <pci/pcivar.h> + +int DRM(irq_busid)( DRM_OS_IOCTL ) +{ + drm_irq_busid_t id; + devclass_t pci; + device_t bus, dev; + device_t *kids; + int error, i, num_kids; + + DRM_OS_KRNFROMUSR( id, (drm_irq_busid_t *)data, sizeof(id) ); + + pci = devclass_find("pci"); + if (!pci) + return ENOENT; + bus = devclass_get_device(pci, id.busnum); + if (!bus) + return ENOENT; + error = device_get_children(bus, &kids, &num_kids); + if (error) + return error; + + dev = 0; + for (i = 0; i < num_kids; i++) { + dev = kids[i]; + if (pci_get_slot(dev) == id.devnum + && pci_get_function(dev) == id.funcnum) + break; + } + + free(kids, M_TEMP); + + if (i != num_kids) + id.irq = pci_get_irq(dev); + else + id.irq = 0; + DRM_DEBUG("%d:%d:%d => IRQ %d\n", + id.busnum, id.devnum, id.funcnum, id.irq); + + DRM_OS_KRNTOUSR( (drm_irq_busid_t *)data, id, sizeof(id) ); + + return 0; +} + +int DRM(getunique)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_unique_t u; + + DRM_OS_KRNFROMUSR( u, (drm_unique_t *)data, sizeof(u) ); + + if (u.unique_len >= dev->unique_len) { + if (DRM_OS_COPYTOUSR(u.unique, dev->unique, dev->unique_len)) + DRM_OS_RETURN(EFAULT); + } + u.unique_len = dev->unique_len; + + DRM_OS_KRNTOUSR( (drm_unique_t *)data, u, sizeof(u) ); + + return 0; +} + +int DRM(setunique)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_unique_t u; + + if (dev->unique_len || dev->unique) + DRM_OS_RETURN(EBUSY); + + DRM_OS_KRNFROMUSR( u, (drm_unique_t *)data, sizeof(u) ); + + if (!u.unique_len || u.unique_len > 1024) + DRM_OS_RETURN(EINVAL); + + dev->unique_len = u.unique_len; + dev->unique = DRM(alloc)(u.unique_len + 1, DRM_MEM_DRIVER); + + if(!dev->unique) DRM_OS_RETURN(ENOMEM); + + if (DRM_OS_COPYFROMUSR(dev->unique, u.unique, dev->unique_len)) + DRM_OS_RETURN(EFAULT); + + dev->unique[dev->unique_len] = '\0'; + + dev->devname = DRM(alloc)(strlen(dev->name) + strlen(dev->unique) + 2, + DRM_MEM_DRIVER); + if(!dev->devname) { + DRM(free)(dev->devname, sizeof(*dev->devname), DRM_MEM_DRIVER); + DRM_OS_RETURN(ENOMEM); + } + sprintf(dev->devname, "%s@%s", dev->name, dev->unique); + + + return 0; +} + + +int DRM(getmap)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_map_t map; + drm_map_t *mapinlist; + drm_map_list_entry_t *list; + int idx; + int i = 0; + + DRM_OS_KRNFROMUSR( map, (drm_map_t *)data, sizeof(map) ); + + idx = map.offset; + + DRM_OS_LOCK; + if (idx < 0 || idx >= dev->map_count) { + DRM_OS_UNLOCK; + DRM_OS_RETURN(EINVAL); + } + + TAILQ_FOREACH(list, dev->maplist, link) { + mapinlist = list->map; + if (i==idx) { + map.offset = mapinlist->offset; + map.size = mapinlist->size; + map.type = mapinlist->type; + map.flags = mapinlist->flags; + map.handle = mapinlist->handle; + map.mtrr = mapinlist->mtrr; + break; + } + i++; + } + + DRM_OS_UNLOCK; + + if (!list) + return EINVAL; + + DRM_OS_KRNTOUSR( (drm_map_t *)data, map, sizeof(map) ); + + return 0; +} + +int DRM(getclient)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_client_t client; + drm_file_t *pt; + int idx; + int i = 0; + + DRM_OS_KRNFROMUSR( client, (drm_client_t *)data, sizeof(client) ); + + idx = client.idx; + DRM_OS_LOCK; + TAILQ_FOREACH(pt, &dev->files, link) { + if (i==idx) + { + client.auth = pt->authenticated; + client.pid = pt->pid; + client.uid = pt->uid; + client.magic = pt->magic; + client.iocs = pt->ioctl_count; + DRM_OS_UNLOCK; + + *(drm_client_t *)data = client; + return 0; + } + i++; + } + DRM_OS_UNLOCK; + + DRM_OS_KRNTOUSR( (drm_client_t *)data, client, sizeof(client) ); + + return 0; +} + +int DRM(getstats)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + drm_stats_t stats; + int i; + + memset(&stats, 0, sizeof(stats)); + + DRM_OS_LOCK; + + for (i = 0; i < dev->counters; i++) { + if (dev->types[i] == _DRM_STAT_LOCK) + stats.data[i].value + = (dev->lock.hw_lock + ? dev->lock.hw_lock->lock : 0); + else + stats.data[i].value = atomic_read(&dev->counts[i]); + stats.data[i].type = dev->types[i]; + } + + stats.count = dev->counters; + + DRM_OS_UNLOCK; + + DRM_OS_KRNTOUSR( (drm_stats_t *)data, stats, sizeof(stats) ); + + return 0; +} diff --git a/bsd-core/drm_lock.c b/bsd-core/drm_lock.c new file mode 100644 index 00000000..863a228c --- /dev/null +++ b/bsd-core/drm_lock.c @@ -0,0 +1,244 @@ +/* lock.c -- IOCTLs for locking -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@valinux.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * 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, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (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 NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS 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: + * Rickard E. (Rik) Faith <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int DRM(block)( DRM_OS_IOCTL ) +{ + DRM_DEBUG("\n"); + return 0; +} + +int DRM(unblock)( DRM_OS_IOCTL ) +{ + DRM_DEBUG("\n"); + return 0; +} + +int DRM(lock_take)(__volatile__ unsigned int *lock, unsigned int context) +{ + unsigned int old, new; + + char failed; + + do { + old = *lock; + if (old & _DRM_LOCK_HELD) new = old | _DRM_LOCK_CONT; + else new = context | _DRM_LOCK_HELD; + _DRM_CAS(lock, old, new, failed); + } while (failed); + if (_DRM_LOCKING_CONTEXT(old) == context) { + if (old & _DRM_LOCK_HELD) { + if (context != DRM_KERNEL_CONTEXT) { + DRM_ERROR("%d holds heavyweight lock\n", + context); + } + return 0; + } + } + if (new == (context | _DRM_LOCK_HELD)) { + /* Have lock */ + return 1; + } + return 0; +} + +/* This takes a lock forcibly and hands it to context. Should ONLY be used + inside *_unlock to give lock to kernel before calling *_dma_schedule. */ +int DRM(lock_transfer)(drm_device_t *dev, + __volatile__ unsigned int *lock, unsigned int context) +{ + unsigned int old, new; + char failed; + + dev->lock.pid = 0; + do { + old = *lock; + new = context | _DRM_LOCK_HELD; + _DRM_CAS(lock, old, new, failed); + } while (failed); + return 1; +} + +int DRM(lock_free)(drm_device_t *dev, + __volatile__ unsigned int *lock, unsigned int context) +{ + unsigned int old, new; + pid_t pid = dev->lock.pid; + char failed; + + dev->lock.pid = 0; + do { + old = *lock; + new = 0; + _DRM_CAS(lock, old, new, failed); + } while (failed); + if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) { + DRM_ERROR("%d freed heavyweight lock held by %d (pid %d)\n", + context, + _DRM_LOCKING_CONTEXT(old), + pid); + return 1; + } + DRM_OS_WAKEUP_INT(&dev->lock.lock_queue); + return 0; +} + +static int DRM(flush_queue)(drm_device_t *dev, int context) +{ + int error; + int ret = 0; + drm_queue_t *q = dev->queuelist[context]; + + DRM_DEBUG("\n"); + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) > 1) { + atomic_inc(&q->block_write); + atomic_inc(&q->block_count); + error = tsleep(&q->flush_queue, PZERO|PCATCH, "drmfq", 0); + if (error) + return error; + atomic_dec(&q->block_count); + } + atomic_dec(&q->use_count); + + /* NOTE: block_write is still incremented! + Use drm_flush_unlock_queue to decrement. */ + return ret; +} + +static int DRM(flush_unblock_queue)(drm_device_t *dev, int context) +{ + drm_queue_t *q = dev->queuelist[context]; + + DRM_DEBUG("\n"); + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) > 1) { + if (atomic_read(&q->block_write)) { + atomic_dec(&q->block_write); + DRM_OS_WAKEUP_INT(&q->write_queue); + } + } + atomic_dec(&q->use_count); + return 0; +} + +int DRM(flush_block_and_flush)(drm_device_t *dev, int context, + drm_lock_flags_t flags) +{ + int ret = 0; + int i; + + DRM_DEBUG("\n"); + + if (flags & _DRM_LOCK_FLUSH) { + ret = DRM(flush_queue)(dev, DRM_KERNEL_CONTEXT); + if (!ret) ret = DRM(flush_queue)(dev, context); + } + if (flags & _DRM_LOCK_FLUSH_ALL) { + for (i = 0; !ret && i < dev->queue_count; i++) { + ret = DRM(flush_queue)(dev, i); + } + } + return ret; +} + +int DRM(flush_unblock)(drm_device_t *dev, int context, drm_lock_flags_t flags) +{ + int ret = 0; + int i; + + DRM_DEBUG("\n"); + + if (flags & _DRM_LOCK_FLUSH) { + ret = DRM(flush_unblock_queue)(dev, DRM_KERNEL_CONTEXT); + if (!ret) ret = DRM(flush_unblock_queue)(dev, context); + } + if (flags & _DRM_LOCK_FLUSH_ALL) { + for (i = 0; !ret && i < dev->queue_count; i++) { + ret = DRM(flush_unblock_queue)(dev, i); + } + } + + return ret; +} + +int DRM(finish)( DRM_OS_IOCTL ) +{ + DRM_OS_DEVICE; + int ret = 0; + drm_lock_t lock; + + DRM_DEBUG("\n"); + + DRM_OS_KRNFROMUSR( lock, (drm_lock_t *)data, sizeof(lock) ); + + ret = DRM(flush_block_and_flush)(dev, lock.context, lock.flags); + DRM(flush_unblock)(dev, lock.context, lock.flags); + return ret; +} + +/* If we get here, it means that the process has called DRM_IOCTL_LOCK + without calling DRM_IOCTL_UNLOCK. + + If the lock is not held, then let the signal proceed as usual. + + If the lock is held, then set the contended flag and keep the signal + blocked. + + + Return 1 if the signal should be delivered normally. + Return 0 if the signal should be blocked. */ + +int DRM(notifier)(void *priv) +{ + drm_sigdata_t *s = (drm_sigdata_t *)priv; + unsigned int old, new; + char failed; + + + /* Allow signal delivery if lock isn't held */ + if (!_DRM_LOCK_IS_HELD(s->lock->lock) + || _DRM_LOCKING_CONTEXT(s->lock->lock) != s->context) return 1; + + /* Otherwise, set flag to force call to + drmUnlock */ + do { + old = s->lock->lock; + new = old | _DRM_LOCK_CONT; + _DRM_CAS(&s->lock->lock, old, new, failed); + } while (failed); + return 0; +} + diff --git a/bsd-core/drm_memory.c b/bsd-core/drm_memory.c new file mode 100644 index 00000000..605b42ae --- /dev/null +++ b/bsd-core/drm_memory.c @@ -0,0 +1,433 @@ +/* drm_memory.h -- Memory management wrappers for DRM -*- linux-c -*- + * Created: Thu Feb 4 14:00:34 1999 by faith@valinux.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * 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, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (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 NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS 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: + * Rickard E. (Rik) Faith <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include <vm/vm.h> +#include <vm/pmap.h> +#if __REALLY_HAVE_AGP +#include <sys/agpio.h> +#endif + +#define malloctype DRM(M_DRM) +/* The macros confliced in the MALLOC_DEFINE */ + +MALLOC_DEFINE(malloctype, "drm", "DRM Data Structures"); + +#undef malloctype + +typedef struct drm_mem_stats { + const char *name; + int succeed_count; + int free_count; + int fail_count; + unsigned long bytes_allocated; + unsigned long bytes_freed; +} drm_mem_stats_t; + +static DRM_OS_SPINTYPE DRM(mem_lock); +static unsigned long DRM(ram_available) = 0; /* In pages */ +static unsigned long DRM(ram_used) = 0; +static drm_mem_stats_t DRM(mem_stats)[] = { + [DRM_MEM_DMA] = { "dmabufs" }, + [DRM_MEM_SAREA] = { "sareas" }, + [DRM_MEM_DRIVER] = { "driver" }, + [DRM_MEM_MAGIC] = { "magic" }, + [DRM_MEM_IOCTLS] = { "ioctltab" }, + [DRM_MEM_MAPS] = { "maplist" }, + [DRM_MEM_VMAS] = { "vmalist" }, + [DRM_MEM_BUFS] = { "buflist" }, + [DRM_MEM_SEGS] = { "seglist" }, + [DRM_MEM_PAGES] = { "pagelist" }, + [DRM_MEM_FILES] = { "files" }, + [DRM_MEM_QUEUES] = { "queues" }, + [DRM_MEM_CMDS] = { "commands" }, + [DRM_MEM_MAPPINGS] = { "mappings" }, + [DRM_MEM_BUFLISTS] = { "buflists" }, + [DRM_MEM_AGPLISTS] = { "agplist" }, + [DRM_MEM_SGLISTS] = { "sglist" }, + [DRM_MEM_TOTALAGP] = { "totalagp" }, + [DRM_MEM_BOUNDAGP] = { "boundagp" }, + [DRM_MEM_CTXBITMAP] = { "ctxbitmap"}, + [DRM_MEM_STUB] = { "stub" }, + { NULL, 0, } /* Last entry must be null */ +}; + +void DRM(mem_init)(void) +{ + drm_mem_stats_t *mem; + + DRM_OS_SPININIT(DRM(mem_lock), "drm memory"); + + for (mem = DRM(mem_stats); mem->name; ++mem) { + mem->succeed_count = 0; + mem->free_count = 0; + mem->fail_count = 0; + mem->bytes_allocated = 0; + mem->bytes_freed = 0; + } + + DRM(ram_available) = 0; /* si.totalram */ + DRM(ram_used) = 0; +} + +/* drm_mem_info is called whenever a process reads /dev/drm/mem. */ + +static int DRM(_mem_info) DRM_SYSCTL_HANDLER_ARGS +{ + drm_mem_stats_t *pt; + char buf[128]; + int error; + + DRM_SYSCTL_PRINT(" total counts " + " | outstanding \n"); + DRM_SYSCTL_PRINT("type alloc freed fail bytes freed" + " | allocs bytes\n\n"); + DRM_SYSCTL_PRINT("%-9.9s %5d %5d %4d %10lu |\n", + "system", 0, 0, 0, DRM(ram_available)); + DRM_SYSCTL_PRINT("%-9.9s %5d %5d %4d %10lu |\n", + "locked", 0, 0, 0, DRM(ram_used)); + DRM_SYSCTL_PRINT("\n"); + for (pt = DRM(mem_stats); pt->name; pt++) { + DRM_SYSCTL_PRINT("%-9.9s %5d %5d %4d %10lu %10lu | %6d %10ld\n", + pt->name, + pt->succeed_count, + pt->free_count, + pt->fail_count, + pt->bytes_allocated, + pt->bytes_freed, + pt->succeed_count - pt->free_count, + (long)pt->bytes_allocated + - (long)pt->bytes_freed); + } + SYSCTL_OUT(req, "", 1); + + return 0; +} + +int DRM(mem_info) DRM_SYSCTL_HANDLER_ARGS +{ + int ret; + + DRM_OS_SPINLOCK(&DRM(mem_lock)); + ret = DRM(_mem_info)(oidp, arg1, arg2, req); + DRM_OS_SPINUNLOCK(&DRM(mem_lock)); + return ret; +} + +void *DRM(alloc)(size_t size, int area) +{ + void *pt; + + if (!size) { + DRM_MEM_ERROR(area, "Allocating 0 bytes\n"); + return NULL; + } + + if (!(pt = malloc(size, DRM(M_DRM), M_NOWAIT))) { + DRM_OS_SPINLOCK(&DRM(mem_lock)); + ++DRM(mem_stats)[area].fail_count; + DRM_OS_SPINUNLOCK(&DRM(mem_lock)); + return NULL; + } + DRM_OS_SPINLOCK(&DRM(mem_lock)); + ++DRM(mem_stats)[area].succeed_count; + DRM(mem_stats)[area].bytes_allocated += size; + DRM_OS_SPINUNLOCK(&DRM(mem_lock)); + return pt; +} + +void *DRM(realloc)(void *oldpt, size_t oldsize, size_t size, int area) +{ + void *pt; + + if (!(pt = DRM(alloc)(size, area))) return NULL; + if (oldpt && oldsize) { + memcpy(pt, oldpt, oldsize); + DRM(free)(oldpt, oldsize, area); + } + return pt; +} + +char *DRM(strdup)(const char *s, int area) +{ + char *pt; + int length = s ? strlen(s) : 0; + + if (!(pt = DRM(alloc)(length+1, area))) return NULL; + strcpy(pt, s); + return pt; +} + +void DRM(strfree)(char *s, int area) +{ + unsigned int size; + + if (!s) return; + + size = 1 + strlen(s); + DRM(free)((void *)s, size, area); +} + +void DRM(free)(void *pt, size_t size, int area) +{ + int alloc_count; + int free_count; + + if (!pt) DRM_MEM_ERROR(area, "Attempt to free NULL pointer\n"); + else free(pt, DRM(M_DRM)); + DRM_OS_SPINLOCK(&DRM(mem_lock)); + DRM(mem_stats)[area].bytes_freed += size; + free_count = ++DRM(mem_stats)[area].free_count; + alloc_count = DRM(mem_stats)[area].succeed_count; + DRM_OS_SPINUNLOCK(&DRM(mem_lock)); + if (free_count > alloc_count) { + DRM_MEM_ERROR(area, "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +unsigned long DRM(alloc_pages)(int order, int area) +{ + vm_offset_t address; + unsigned long bytes = PAGE_SIZE << order; + + + address = (vm_offset_t) contigmalloc(bytes, DRM(M_DRM), M_WAITOK, 0, ~0, 1, 0); + if (!address) { + DRM_OS_SPINLOCK(&DRM(mem_lock)); + ++DRM(mem_stats)[area].fail_count; + DRM_OS_SPINUNLOCK(&DRM(mem_lock)); + return 0; + } + DRM_OS_SPINLOCK(&DRM(mem_lock)); + ++DRM(mem_stats)[area].succeed_count; + DRM(mem_stats)[area].bytes_allocated += bytes; + DRM(ram_used) += bytes; + DRM_OS_SPINUNLOCK(&DRM(mem_lock)); + + + /* Zero outside the lock */ + memset((void *)address, 0, bytes); + + + return address; +} + +void DRM(free_pages)(unsigned long address, int order, int area) +{ + unsigned long bytes = PAGE_SIZE << order; + int alloc_count; + int free_count; + + if (!address) { + DRM_MEM_ERROR(area, "Attempt to free address 0\n"); + } else { + contigfree((void *) address, bytes, DRM(M_DRM)); + } + + DRM_OS_SPINLOCK(&DRM(mem_lock)); + free_count = ++DRM(mem_stats)[area].free_count; + alloc_count = DRM(mem_stats)[area].succeed_count; + DRM(mem_stats)[area].bytes_freed += bytes; + DRM(ram_used) -= bytes; + DRM_OS_SPINUNLOCK(&DRM(mem_lock)); + if (free_count > alloc_count) { + DRM_MEM_ERROR(area, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +void *DRM(ioremap)(unsigned long offset, unsigned long size) +{ + void *pt; + + if (!size) { + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Mapping 0 bytes at 0x%08lx\n", offset); + return NULL; + } + + if (!(pt = pmap_mapdev(offset, size))) { + DRM_OS_SPINLOCK(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_MAPPINGS].fail_count; + DRM_OS_SPINUNLOCK(&DRM(mem_lock)); + return NULL; + } + DRM_OS_SPINLOCK(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_MAPPINGS].succeed_count; + DRM(mem_stats)[DRM_MEM_MAPPINGS].bytes_allocated += size; + DRM_OS_SPINUNLOCK(&DRM(mem_lock)); + return pt; +} + +void DRM(ioremapfree)(void *pt, unsigned long size) +{ + int alloc_count; + int free_count; + + if (!pt) + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Attempt to free NULL pointer\n"); + else + pmap_unmapdev((vm_offset_t) pt, size); + + DRM_OS_SPINLOCK(&DRM(mem_lock)); + DRM(mem_stats)[DRM_MEM_MAPPINGS].bytes_freed += size; + free_count = ++DRM(mem_stats)[DRM_MEM_MAPPINGS].free_count; + alloc_count = DRM(mem_stats)[DRM_MEM_MAPPINGS].succeed_count; + DRM_OS_SPINUNLOCK(&DRM(mem_lock)); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +#if __REALLY_HAVE_AGP +agp_memory *DRM(alloc_agp)(int pages, u32 type) +{ + agp_memory *handle; + + if (!pages) { + DRM_MEM_ERROR(DRM_MEM_TOTALAGP, "Allocating 0 pages\n"); + return NULL; + } + + if ((handle = DRM(agp_allocate_memory)(pages, type))) { + DRM_OS_SPINLOCK(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_TOTALAGP].succeed_count; + DRM(mem_stats)[DRM_MEM_TOTALAGP].bytes_allocated + += pages << PAGE_SHIFT; + DRM_OS_SPINUNLOCK(&DRM(mem_lock)); + return handle; + } + DRM_OS_SPINLOCK(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_TOTALAGP].fail_count; + DRM_OS_SPINUNLOCK(&DRM(mem_lock)); + return NULL; +} + +int DRM(free_agp)(agp_memory *handle, int pages) +{ + int alloc_count; + int free_count; + + if (!handle) { + DRM_MEM_ERROR(DRM_MEM_TOTALAGP, + "Attempt to free NULL AGP handle\n"); + DRM_OS_RETURN(EINVAL); + } + + if (DRM(agp_free_memory)(handle)) { + DRM_OS_SPINLOCK(&DRM(mem_lock)); + free_count = ++DRM(mem_stats)[DRM_MEM_TOTALAGP].free_count; + alloc_count = DRM(mem_stats)[DRM_MEM_TOTALAGP].succeed_count; + DRM(mem_stats)[DRM_MEM_TOTALAGP].bytes_freed + += pages << PAGE_SHIFT; + DRM_OS_SPINUNLOCK(&DRM(mem_lock)); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_TOTALAGP, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } + return 0; + } + DRM_OS_RETURN(EINVAL); +} + +int DRM(bind_agp)(agp_memory *handle, unsigned int start) +{ + int retcode; + device_t dev = agp_find_device(); + struct agp_memory_info info; + + if (!dev) + return EINVAL; + + if (!handle) { + DRM_MEM_ERROR(DRM_MEM_BOUNDAGP, + "Attempt to bind NULL AGP handle\n"); + DRM_OS_RETURN(EINVAL); + } + + if (!(retcode = DRM(agp_bind_memory)(handle, start))) { + DRM_OS_SPINLOCK(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_BOUNDAGP].succeed_count; + agp_memory_info(dev, handle, &info); + DRM(mem_stats)[DRM_MEM_BOUNDAGP].bytes_allocated + += info.ami_size; + DRM_OS_SPINUNLOCK(&DRM(mem_lock)); + DRM_OS_RETURN(0); + } + DRM_OS_SPINLOCK(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_BOUNDAGP].fail_count; + DRM_OS_SPINUNLOCK(&DRM(mem_lock)); + DRM_OS_RETURN(retcode); +} + +int DRM(unbind_agp)(agp_memory *handle) +{ + int alloc_count; + int free_count; + int retcode = EINVAL; + device_t dev = agp_find_device(); + struct agp_memory_info info; + + if (!dev) + return EINVAL; + + if (!handle) { + DRM_MEM_ERROR(DRM_MEM_BOUNDAGP, + "Attempt to unbind NULL AGP handle\n"); + DRM_OS_RETURN(retcode); + } + + agp_memory_info(dev, handle, &info); + + if ((retcode = DRM(agp_unbind_memory)(handle))) + DRM_OS_RETURN(retcode); + + DRM_OS_SPINLOCK(&DRM(mem_lock)); + free_count = ++DRM(mem_stats)[DRM_MEM_BOUNDAGP].free_count; + alloc_count = DRM(mem_stats)[DRM_MEM_BOUNDAGP].succeed_count; + DRM(mem_stats)[DRM_MEM_BOUNDAGP].bytes_freed + += info.ami_size; + DRM_OS_SPINUNLOCK(&DRM(mem_lock)); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_BOUNDAGP, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } + DRM_OS_RETURN(retcode); +} +#endif diff --git a/bsd-core/drm_scatter.c b/bsd-core/drm_scatter.c new file mode 100644 index 00000000..a6b8275f --- /dev/null +++ b/bsd-core/drm_scatter.c @@ -0,0 +1,237 @@ +/* drm_scatter.h -- IOCTLs to manage scatter/gather memory -*- linux-c -*- + * Created: Mon Dec 18 23:20:54 2000 by gareth@valinux.com + * + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * 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, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (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 NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT 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: + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include <linux/config.h> +#include <linux/vmalloc.h> +#include "drmP.h" + +#define DEBUG_SCATTER 0 + +void DRM(sg_cleanup)( drm_sg_mem_t *entry ) +{ + struct page *page; + int i; + + for ( i = 0 ; i < entry->pages ; i++ ) { + page = entry->pagelist[i]; + if ( page ) + ClearPageReserved( page ); + } + + vfree( entry->virtual ); + + DRM(free)( entry->busaddr, + entry->pages * sizeof(*entry->busaddr), + DRM_MEM_PAGES ); + DRM(free)( entry->pagelist, + entry->pages * sizeof(*entry->pagelist), + DRM_MEM_PAGES ); + DRM(free)( entry, + sizeof(*entry), + DRM_MEM_SGLISTS ); +} + +int DRM(sg_alloc)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_scatter_gather_t request; + drm_sg_mem_t *entry; + unsigned long pages, i, j; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( dev->sg ) + return -EINVAL; + + if ( copy_from_user( &request, + (drm_scatter_gather_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + entry = DRM(alloc)( sizeof(*entry), DRM_MEM_SGLISTS ); + if ( !entry ) + return -ENOMEM; + + memset( entry, 0, sizeof(*entry) ); + + pages = (request.size + PAGE_SIZE - 1) / PAGE_SIZE; + DRM_DEBUG( "sg size=%ld pages=%ld\n", request.size, pages ); + + entry->pages = pages; + entry->pagelist = DRM(alloc)( pages * sizeof(*entry->pagelist), + DRM_MEM_PAGES ); + if ( !entry->pagelist ) { + DRM(free)( entry, sizeof(*entry), DRM_MEM_SGLISTS ); + return -ENOMEM; + } + + entry->busaddr = DRM(alloc)( pages * sizeof(*entry->busaddr), + DRM_MEM_PAGES ); + if ( !entry->busaddr ) { + DRM(free)( entry->pagelist, + entry->pages * sizeof(*entry->pagelist), + DRM_MEM_PAGES ); + DRM(free)( entry, + sizeof(*entry), + DRM_MEM_SGLISTS ); + return -ENOMEM; + } + memset( (void *)entry->busaddr, 0, pages * sizeof(*entry->busaddr) ); + + entry->virtual = vmalloc_32( pages << PAGE_SHIFT ); + if ( !entry->virtual ) { + DRM(free)( entry->busaddr, + entry->pages * sizeof(*entry->busaddr), + DRM_MEM_PAGES ); + DRM(free)( entry->pagelist, + entry->pages * sizeof(*entry->pagelist), + DRM_MEM_PAGES ); + DRM(free)( entry, + sizeof(*entry), + DRM_MEM_SGLISTS ); + return -ENOMEM; + } + + /* This also forces the mapping of COW pages, so our page list + * will be valid. Please don't remove it... + */ + memset( entry->virtual, 0, pages << PAGE_SHIFT ); + + entry->handle = (unsigned long)entry->virtual; + + DRM_DEBUG( "sg alloc handle = %08lx\n", entry->handle ); + DRM_DEBUG( "sg alloc virtual = %p\n", entry->virtual ); + + for ( i = entry->handle, j = 0 ; j < pages ; i += PAGE_SIZE, j++ ) { + pgd = pgd_offset_k( i ); + if ( !pgd_present( *pgd ) ) + goto failed; + + pmd = pmd_offset( pgd, i ); + if ( !pmd_present( *pmd ) ) + goto failed; + + pte = pte_offset( pmd, i ); + if ( !pte_present( *pte ) ) + goto failed; + + entry->pagelist[j] = pte_page( *pte ); + + SetPageReserved( entry->pagelist[j] ); + } + + request.handle = entry->handle; + + if ( copy_to_user( (drm_scatter_gather_t *)arg, + &request, + sizeof(request) ) ) { + DRM(sg_cleanup)( entry ); + return -EFAULT; + } + + dev->sg = entry; + +#if DEBUG_SCATTER + /* Verify that each page points to its virtual address, and vice + * versa. + */ + { + int error = 0; + + for ( i = 0 ; i < pages ; i++ ) { + unsigned long *tmp; + + tmp = page_address( entry->pagelist[i] ); + for ( j = 0 ; + j < PAGE_SIZE / sizeof(unsigned long) ; + j++, tmp++ ) { + *tmp = 0xcafebabe; + } + tmp = (unsigned long *)((u8 *)entry->virtual + + (PAGE_SIZE * i)); + for( j = 0 ; + j < PAGE_SIZE / sizeof(unsigned long) ; + j++, tmp++ ) { + if ( *tmp != 0xcafebabe && error == 0 ) { + error = 1; + DRM_ERROR( "Scatter allocation error, " + "pagelist does not match " + "virtual mapping\n" ); + } + } + tmp = page_address( entry->pagelist[i] ); + for(j = 0 ; + j < PAGE_SIZE / sizeof(unsigned long) ; + j++, tmp++) { + *tmp = 0; + } + } + if (error == 0) + DRM_ERROR( "Scatter allocation matches pagelist\n" ); + } +#endif + + return 0; + + failed: + DRM(sg_cleanup)( entry ); + return -ENOMEM; +} + +int DRM(sg_free)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_scatter_gather_t request; + drm_sg_mem_t *entry; + + if ( copy_from_user( &request, + (drm_scatter_gather_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + entry = dev->sg; + dev->sg = NULL; + + if ( !entry || entry->handle != request.handle ) + return -EINVAL; + + DRM_DEBUG( "sg free virtual = %p\n", entry->virtual ); + + DRM(sg_cleanup)( entry ); + + return 0; +} |