diff options
Diffstat (limited to 'bsd/mga/mga_dma.c')
| -rw-r--r-- | bsd/mga/mga_dma.c | 1544 | 
1 files changed, 643 insertions, 901 deletions
diff --git a/bsd/mga/mga_dma.c b/bsd/mga/mga_dma.c index 85c29df7..9ed5d095 100644 --- a/bsd/mga/mga_dma.c +++ b/bsd/mga/mga_dma.c @@ -1,4 +1,4 @@ -/* mga_dma.c -- DMA support for mga g200/g400 +/* mga_dma.c -- DMA support for mga g200/g400 -*- linux-c -*-   * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com   *   * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. @@ -11,11 +11,11 @@   * 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 @@ -24,1054 +24,796 @@   * 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> - *	    Jeff Hartmann <jhartmann@valinux.com> - *	    Keith Whitwell <keithw@valinux.com> + * Authors: + *    Rickard E. (Rik) Faith <faith@valinux.com> + *    Jeff Hartmann <jhartmann@valinux.com> + *    Keith Whitwell <keithw@valinux.com>   * + * Rewritten by: + *    Gareth Hughes <gareth@valinux.com>   */  #define __NO_VERSION__ +#include "mga.h"  #include "drmP.h"  #include "mga_drv.h" -#include <machine/bus.h> -#include <machine/resource.h> -#include <sys/rman.h> -#include <vm/vm.h> -#include <vm/pmap.h> -#define MGA_REG(reg)		2 -#define MGA_BASE(reg)		((unsigned long) \ -				((drm_device_t *)dev)->maplist[MGA_REG(reg)]->handle) -#define MGA_ADDR(reg)		(MGA_BASE(reg) + reg) -#define MGA_DEREF(reg)		*(__volatile__ int *)MGA_ADDR(reg) -#define MGA_READ(reg)		MGA_DEREF(reg) -#define MGA_WRITE(reg,val) 	do { MGA_DEREF(reg) = val; } while (0) +#define MGA_DEFAULT_USEC_TIMEOUT	10000 +#define MGA_FREELIST_DEBUG		0 -#define PDEA_pagpxfer_enable 	     0x2 -static int mga_flush_queue(drm_device_t *dev); +/* ================================================================ + * Engine control + */ -static unsigned long mga_alloc_page(drm_device_t *dev) +int mga_do_wait_for_idle( drm_mga_private_t *dev_priv )  { -	unsigned long address; -    -	DRM_DEBUG("%s\n", __FUNCTION__); +	u32 status = 0; +	int i; +	DRM_DEBUG( "%s\n", __FUNCTION__ ); -	address = (unsigned long) drm_alloc(PAGE_SIZE, DRM_MEM_DMA); -	if(address == 0UL) { -		return 0; +	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { +		status = MGA_READ( MGA_STATUS ) & MGA_ENGINE_IDLE_MASK; +		if ( status == MGA_ENDPRDMASTS ) { +			MGA_WRITE8( MGA_CRTC_INDEX, 0 ); +			return 0; +		} +		DRM_OS_DELAY( 1 );  	} -    -	return address; + +#if MGA_DMA_DEBUG +	DRM_ERROR( "failed!\n" ); +	DRM_INFO( "   status=0x%08x\n", status ); +#endif +	DRM_OS_RETURN(EBUSY);  } -static void mga_free_page(drm_device_t *dev, unsigned long page) +int mga_do_dma_idle( drm_mga_private_t *dev_priv )  { -	DRM_DEBUG("%s\n", __FUNCTION__); +	u32 status = 0; +	int i; +	DRM_DEBUG( "%s\n", __FUNCTION__ ); -	if(page == 0UL) { -		return; +	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { +		status = MGA_READ( MGA_STATUS ) & MGA_DMA_IDLE_MASK; +		if ( status == MGA_ENDPRDMASTS ) return 0; +		DRM_OS_DELAY( 1 );  	} -	drm_free((void *) page, PAGE_SIZE, DRM_MEM_DMA); -	return; -} -static void mga_delay(void) -{ -   	return; +#if MGA_DMA_DEBUG +	DRM_ERROR( "failed! status=0x%08x\n", status ); +#endif +	DRM_OS_RETURN(EBUSY);  } -void mga_flush_write_combine(void) +int mga_do_dma_reset( drm_mga_private_t *dev_priv )  { -   	int xchangeDummy; -	DRM_DEBUG("%s\n", __FUNCTION__); +	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; +	drm_mga_primary_buffer_t *primary = &dev_priv->prim; -   	__asm__ volatile(" push %%eax ; xchg %%eax, %0 ; pop %%eax" : : "m" (xchangeDummy)); -   	__asm__ volatile(" push %%eax ; push %%ebx ; push %%ecx ; push %%edx ;" -			 " movl $0,%%eax ; cpuid ; pop %%edx ; pop %%ecx ; pop %%ebx ;" -			 " pop %%eax" : /* no outputs */ :  /* no inputs */ ); -} +	DRM_DEBUG( "%s\n", __FUNCTION__ ); -/* These are two age tags that will never be sent to - * the hardware */ -#define MGA_BUF_USED 	0xffffffff -#define MGA_BUF_FREE	0 +	/* The primary DMA stream should look like new right about now. +	 */ +	primary->tail = 0; +	primary->space = primary->size; +	primary->last_flush = 0; -static int mga_freelist_init(drm_device_t *dev) -{ -      	drm_device_dma_t *dma = dev->dma; -   	drm_buf_t *buf; -   	drm_mga_buf_priv_t *buf_priv; -      	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; -   	drm_mga_freelist_t *item; -   	int i; - -	DRM_DEBUG("%s\n", __FUNCTION__); - -   	dev_priv->head = drm_alloc(sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER); -	if(dev_priv->head == NULL) return ENOMEM; -   	memset(dev_priv->head, 0, sizeof(drm_mga_freelist_t)); -   	dev_priv->head->age = MGA_BUF_USED; -    -   	for (i = 0; i < dma->buf_count; i++) { -	   	buf = dma->buflist[ i ]; -	        buf_priv = buf->dev_private; -		item = drm_alloc(sizeof(drm_mga_freelist_t), -				 DRM_MEM_DRIVER); -	   	if(item == NULL) return ENOMEM; -	   	memset(item, 0, sizeof(drm_mga_freelist_t)); -	  	item->age = MGA_BUF_FREE; -	   	item->prev = dev_priv->head; -	   	item->next = dev_priv->head->next; -	   	if(dev_priv->head->next != NULL) -			dev_priv->head->next->prev = item; -	   	if(item->next == NULL) dev_priv->tail = item; -	   	item->buf = buf; -	   	buf_priv->my_freelist = item; -		buf_priv->discard = 0; -	   	dev_priv->head->next = item; -	} -    -   	return 0; -} +	sarea_priv->last_wrap = 0; -static void mga_freelist_cleanup(drm_device_t *dev) -{ -      	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; -   	drm_mga_freelist_t *item; -   	drm_mga_freelist_t *prev; +	/* FIXME: Reset counters, buffer ages etc... +	 */ -	DRM_DEBUG("%s\n", __FUNCTION__); +	/* FIXME: What else do we need to reinitialize?  WARP stuff? +	 */ -   	item = dev_priv->head; -   	while(item) { -	   	prev = item; -	   	item = item->next; -	   	drm_free(prev, sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER); -	} -    -   	dev_priv->head = dev_priv->tail = NULL; +	return 0;  } -/* Frees dispatch lock */ -static __inline void mga_dma_quiescent(drm_device_t *dev) +int mga_do_engine_reset( drm_mga_private_t *dev_priv )  { -	drm_device_dma_t  *dma      = dev->dma; -	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; -	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; -   	unsigned long end; -	int i; +	DRM_DEBUG( "%s\n", __FUNCTION__ ); -	DRM_DEBUG("%s\n", __FUNCTION__); -	end = ticks + (hz*3); -    	while(1) { -		if(!test_and_set_bit(MGA_IN_DISPATCH,  -				     &dev_priv->dispatch_status)) { -			break; -		} -	   	if((signed)(end - ticks) <= 0) { -			DRM_ERROR("irqs: %d wanted %d\n",  -				  atomic_read(&dev->total_irq),  -				  atomic_read(&dma->total_lost)); -			DRM_ERROR("lockup\n");  -			return; -		} -		for (i = 0 ; i < 2000 ; i++) mga_delay(); -	} -	end = ticks + (hz*3); -    	DRM_DEBUG("quiescent status : %x\n", MGA_READ(MGAREG_STATUS)); -    	while((MGA_READ(MGAREG_STATUS) & 0x00030001) != 0x00020000) { -		if((signed)(end - ticks) <= 0) { -			DRM_ERROR("irqs: %d wanted %d\n",  -				  atomic_read(&dev->total_irq),  -				  atomic_read(&dma->total_lost)); -			DRM_ERROR("lockup\n");  -			clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status); -			return; -		} -		for (i = 0 ; i < 2000 ; i++) mga_delay();	   -	} -    	sarea_priv->dirty |= MGA_DMA_FLUSH; +	/* Okay, so we've completely screwed up and locked the engine. +	 * How about we clean up after ourselves? +	 */ +	MGA_WRITE( MGA_RST, MGA_SOFTRESET ); +	DRM_OS_DELAY( 15 );		/* Wait at least 10 usecs */ +	MGA_WRITE( MGA_RST, 0 ); + +	/* Initialize the registers that get clobbered by the soft +	 * reset.  Many of the core register values survive a reset, +	 * but the drawing registers are basically all gone. +	 * +	 * 3D clients should probably die after calling this.  The X +	 * server should reset the engine state to known values. +	 */ +#if 0 +	MGA_WRITE( MGA_PRIMPTR, +		   virt_to_bus((void *)dev_priv->prim.status_page) | +		   MGA_PRIMPTREN0 | +		   MGA_PRIMPTREN1 ); +#endif -    	clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status); -	DRM_DEBUG("exit, dispatch_status = 0x%02x\n",dev_priv->dispatch_status); -} +	MGA_WRITE( MGA_ICLEAR, MGA_SOFTRAPICLR ); +	MGA_WRITE( MGA_IEN,    MGA_SOFTRAPIEN ); -static void mga_reset_freelist(drm_device_t *dev) -{ -   	drm_device_dma_t  *dma      = dev->dma; -   	drm_buf_t *buf; -   	drm_mga_buf_priv_t *buf_priv; -	int i; +	/* The primary DMA stream should look like new right about now. +	 */ +	mga_do_dma_reset( dev_priv ); -   	for (i = 0; i < dma->buf_count; i++) { -	   	buf = dma->buflist[ i ]; -	        buf_priv = buf->dev_private; -		buf_priv->my_freelist->age = MGA_BUF_FREE; -	} +	/* This bad boy will never fail. +	 */ +	return 0;  } -/* Least recently used : - * These operations are not atomic b/c they are protected by the  - * hardware lock */ -drm_buf_t *mga_freelist_get(drm_device_t *dev) +/* ================================================================ + * Primary DMA stream + */ + +void mga_do_dma_flush( drm_mga_private_t *dev_priv )  { -   	drm_mga_private_t *dev_priv =  -     		(drm_mga_private_t *) dev->dev_private; -	drm_mga_freelist_t *prev; -   	drm_mga_freelist_t *next; -	static int failed = 0; -	int ret, s; - -	DRM_DEBUG("%s : tail->age : %d last_prim_age : %d\n", __FUNCTION__, -	       dev_priv->tail->age, dev_priv->last_prim_age); -    -	if(failed >= 1000 && dev_priv->tail->age >= dev_priv->last_prim_age) { -		DRM_DEBUG("I'm waiting on the freelist!!! %d\n",  -		       dev_priv->last_prim_age); -		s = splsofttq(); -	   	set_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status); -	   	for (;;) { -		   	mga_dma_schedule(dev, 0); -/*		   	if(!test_bit(MGA_IN_GETBUF,  -				     &dev_priv->dispatch_status)) */ -			if(dev_priv->tail->age < dev_priv->last_prim_age) -				break; -		   	atomic_inc(&dev->total_sleeps); -			ret = tsleep(&dev_priv->buf_queue, PZERO|PCATCH, -				     "mgafg", 0); -			if (ret == EINTR) { -				clear_bit(MGA_IN_GETBUF, -					  &dev_priv->dispatch_status); -				break; -			} -		} -		splx(s); -		clear_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status); -		if (ret) return NULL; -	} -    -   	if(dev_priv->tail->age < dev_priv->last_prim_age) { -		prev = dev_priv->tail->prev; -	   	next = dev_priv->tail; -	   	prev->next = NULL; -	   	next->prev = next->next = NULL; -	   	dev_priv->tail = prev; -	   	next->age = MGA_BUF_USED; -		failed = 0; -	   	return next->buf; +	drm_mga_primary_buffer_t *primary = &dev_priv->prim; +	u32 head, tail; +	DMA_LOCALS; +	DRM_DEBUG( "%s:\n", __FUNCTION__ ); + +	if ( primary->tail == primary->last_flush ) { +		DRM_DEBUG( "   bailing out...\n" ); +		return;  	} -	failed++; -   	return NULL; -} +	tail = primary->tail + dev_priv->primary->offset; -int mga_freelist_put(drm_device_t *dev, drm_buf_t *buf) -{ -      	drm_mga_private_t *dev_priv =  -     		(drm_mga_private_t *) dev->dev_private; -   	drm_mga_buf_priv_t *buf_priv = buf->dev_private; -	drm_mga_freelist_t *prev; -   	drm_mga_freelist_t *head; -   	drm_mga_freelist_t *next; +	/* We need to pad the stream between flushes, as the card +	 * actually (partially?) reads the first of these commands. +	 * See page 4-16 in the G400 manual, middle of the page or so. +	 */ +	BEGIN_DMA( 1 ); -	DRM_DEBUG("%s\n", __FUNCTION__); +	DMA_BLOCK( MGA_DMAPAD,  0x00000000, +		   MGA_DMAPAD,  0x00000000, +		   MGA_DMAPAD,  0x00000000, +		   MGA_DMAPAD,	0x00000000 ); -   	if(buf_priv->my_freelist->age == MGA_BUF_USED) { -		/* Discarded buffer, put it on the tail */ -		next = buf_priv->my_freelist; -		next->age = MGA_BUF_FREE; -		prev = dev_priv->tail; -		prev->next = next; -		next->prev = prev; -		next->next = NULL; -		dev_priv->tail = next; -		DRM_DEBUG("Discarded\n"); +	ADVANCE_DMA(); + +	primary->last_flush = primary->tail; + +	head = MGA_READ( MGA_PRIMADDRESS ); + +	if ( head <= tail ) { +		primary->space = primary->size - primary->tail;  	} else { -		/* Normally aged buffer, put it on the head + 1, -		 * as the real head is a sentinal element -		 */ -		next = buf_priv->my_freelist; -		head = dev_priv->head; -		prev = head->next; -		head->next = next; -		prev->prev = next; -		next->prev = head; -		next->next = prev; +		primary->space = head - tail;  	} -    -   	return 0; -} -static int mga_init_primary_bufs(drm_device_t *dev, drm_mga_init_t *init) -{ -   	drm_mga_private_t *dev_priv = dev->dev_private; -	drm_mga_prim_buf_t *prim_buffer; -   	int i, temp, size_of_buf; -   	int offset = init->reserved_map_agpstart; - -	DRM_DEBUG("%s\n", __FUNCTION__); -   	dev_priv->primary_size = ((init->primary_size + PAGE_SIZE - 1) /  -				  PAGE_SIZE) * PAGE_SIZE; -   	size_of_buf = dev_priv->primary_size / MGA_NUM_PRIM_BUFS; -	dev_priv->warp_ucode_size = init->warp_ucode_size; -   	dev_priv->prim_bufs = drm_alloc(sizeof(drm_mga_prim_buf_t *) *  -					(MGA_NUM_PRIM_BUFS + 1),  -					DRM_MEM_DRIVER); -   	if(dev_priv->prim_bufs == NULL) { -		DRM_ERROR("Unable to allocate memory for prim_buf\n"); -		return ENOMEM; -	} -   	memset(dev_priv->prim_bufs,  -	       0, sizeof(drm_mga_prim_buf_t *) * (MGA_NUM_PRIM_BUFS + 1)); -    -   	temp = init->warp_ucode_size + dev_priv->primary_size; -	temp = ((temp + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE; -	    -	dev_priv->ioremap = drm_ioremap(dev->agp->base + offset,  -					temp); -	if(dev_priv->ioremap == NULL) { -		DRM_DEBUG("Ioremap failed\n"); -		return ENOMEM; -	} -   	dev_priv->wait_queue = 0; -    -   	for(i = 0; i < MGA_NUM_PRIM_BUFS; i++) { -	   	prim_buffer = drm_alloc(sizeof(drm_mga_prim_buf_t),  -					DRM_MEM_DRIVER); -	   	if(prim_buffer == NULL) return ENOMEM; -	   	memset(prim_buffer, 0, sizeof(drm_mga_prim_buf_t)); -	   	prim_buffer->phys_head = offset + dev->agp->base; -	   	prim_buffer->current_dma_ptr =  -			prim_buffer->head =  -			(u_int32_t *) (dev_priv->ioremap +  -				       offset -  -				       init->reserved_map_agpstart); -	   	prim_buffer->num_dwords = 0; -	   	prim_buffer->max_dwords = size_of_buf / sizeof(u_int32_t); -	   	prim_buffer->max_dwords -= 5; /* Leave room for the softrap */ -	   	prim_buffer->sec_used = 0; -	   	prim_buffer->idx = i; -		prim_buffer->prim_age = i + 1; -	   	offset = offset + size_of_buf; -	   	dev_priv->prim_bufs[i] = prim_buffer; -	} -	dev_priv->current_prim_idx = 0; -        dev_priv->next_prim =  -		dev_priv->last_prim =  -		dev_priv->current_prim = -        	dev_priv->prim_bufs[0]; -	dev_priv->next_prim_age = 2;	 -	dev_priv->last_prim_age = 1; -   	set_bit(MGA_BUF_IN_USE, &dev_priv->current_prim->buffer_status); -   	return 0; +	DRM_DEBUG( "   head = 0x%06lx\n", head - dev_priv->primary->offset ); +	DRM_DEBUG( "   tail = 0x%06lx\n", tail - dev_priv->primary->offset ); +	DRM_DEBUG( "  space = 0x%06x\n", primary->space ); + +	mga_flush_write_combine(); +	MGA_WRITE( MGA_PRIMEND, tail | MGA_PAGPXFER ); + +	DRM_DEBUG( "%s: done.\n", __FUNCTION__ );  } -static void mga_fire_primary(drm_device_t *dev, drm_mga_prim_buf_t *prim) +void mga_do_dma_wrap_start( drm_mga_private_t *dev_priv )  { -       	drm_mga_private_t *dev_priv = dev->dev_private; -      	drm_device_dma_t  *dma	    = dev->dma; -       	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; - 	int use_agp = PDEA_pagpxfer_enable; -	unsigned long end; -   	int i; -   	int next_idx; -       	PRIMLOCALS; - -   	DRM_DEBUG("%s\n", __FUNCTION__); -   	dev_priv->last_prim = prim; -    - 	/* We never check for overflow, b/c there is always room */ -    	PRIMPTR(prim); -   	if(num_dwords <= 0) { -		DRM_DEBUG("num_dwords == 0 when dispatched\n"); -		goto out_prim_wait; -	} - 	PRIMOUTREG( MGAREG_DMAPAD, 0); - 	PRIMOUTREG( MGAREG_DMAPAD, 0); -       	PRIMOUTREG( MGAREG_DMAPAD, 0); -   	PRIMOUTREG( MGAREG_SOFTRAP, 0); -    	PRIMFINISH(prim); - -	end = ticks + (hz*3); -    	if(sarea_priv->dirty & MGA_DMA_FLUSH) { -		DRM_DEBUG("Dma top flush\n");	    -		while((MGA_READ(MGAREG_STATUS) & 0x00030001) != 0x00020000) { -			if((signed)(end - ticks) <= 0) { -				DRM_ERROR("irqs: %d wanted %d\n",  -					  atomic_read(&dev->total_irq),  -					  atomic_read(&dma->total_lost)); -				DRM_ERROR("lockup in fire primary " -					  "(Dma Top Flush)\n"); -				goto out_prim_wait; -			} -	       -			for (i = 0 ; i < 4096 ; i++) mga_delay(); -		} -		sarea_priv->dirty &= ~(MGA_DMA_FLUSH); +	drm_mga_primary_buffer_t *primary = &dev_priv->prim; +	u32 head, tail; +	DMA_LOCALS; +	DRM_DEBUG( "%s:\n", __FUNCTION__ ); + +	BEGIN_DMA_WRAP(); + +	DMA_BLOCK( MGA_DMAPAD,	0x00000000, +		   MGA_DMAPAD,	0x00000000, +		   MGA_DMAPAD,	0x00000000, +		   MGA_DMAPAD,	0x00000000 ); + +	ADVANCE_DMA(); + +	tail = primary->tail + dev_priv->primary->offset; + +	primary->tail = 0; +	primary->last_flush = 0; +	primary->last_wrap++; + +	head = MGA_READ( MGA_PRIMADDRESS ); + +	if ( head == dev_priv->primary->offset ) { +		primary->space = primary->size;  	} else { -		DRM_DEBUG("Status wait\n"); -		while((MGA_READ(MGAREG_STATUS) & 0x00020001) != 0x00020000) { -			if((signed)(end - ticks) <= 0) { -				DRM_ERROR("irqs: %d wanted %d\n",  -					  atomic_read(&dev->total_irq),  -					  atomic_read(&dma->total_lost)); -				DRM_ERROR("lockup in fire primary " -					  "(Status Wait)\n"); -				goto out_prim_wait; -			} -	    -			for (i = 0 ; i < 4096 ; i++) mga_delay(); -		} +		primary->space = head - dev_priv->primary->offset;  	} -   	mga_flush_write_combine(); -    	atomic_inc(&dev_priv->pending_bufs); -       	MGA_WRITE(MGAREG_PRIMADDRESS, phys_head | TT_GENERAL); - 	MGA_WRITE(MGAREG_PRIMEND, (phys_head + num_dwords * 4) | use_agp); -   	prim->num_dwords = 0; -	sarea_priv->last_enqueue = prim->prim_age; -     -   	next_idx = prim->idx + 1; -    	if(next_idx >= MGA_NUM_PRIM_BUFS)  -		next_idx = 0; - -    	dev_priv->next_prim = dev_priv->prim_bufs[next_idx]; -	return; - - out_prim_wait: -	prim->num_dwords = 0; -	prim->sec_used = 0; -	clear_bit(MGA_BUF_IN_USE, &prim->buffer_status); -   	wakeup(&dev_priv->wait_queue); -	clear_bit(MGA_BUF_SWAP_PENDING, &prim->buffer_status); -	clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status); +	DRM_DEBUG( "   head = 0x%06lx\n", +		  head - dev_priv->primary->offset ); +	DRM_DEBUG( "   tail = 0x%06x\n", primary->tail ); +	DRM_DEBUG( "   wrap = %d\n", primary->last_wrap ); +	DRM_DEBUG( "  space = 0x%06x\n", primary->space ); + +	mga_flush_write_combine(); +	MGA_WRITE( MGA_PRIMEND, tail | MGA_PAGPXFER ); + +	set_bit( 0, &primary->wrapped ); +	DRM_DEBUG( "%s: done.\n", __FUNCTION__ );  } -int mga_advance_primary(drm_device_t *dev) +void mga_do_dma_wrap_end( drm_mga_private_t *dev_priv )  { -   	drm_mga_private_t *dev_priv = dev->dev_private; -   	drm_mga_prim_buf_t *prim_buffer; -   	drm_device_dma_t  *dma      = dev->dma; -   	int		  next_prim_idx; -   	int		  ret = 0; -	int		  s; -    -   	/* This needs to reset the primary buffer if available, -	 * we should collect stats on how many times it bites -	 * it's tail */ -	DRM_DEBUG("%s\n", __FUNCTION__); -    -   	next_prim_idx = dev_priv->current_prim_idx + 1; -   	if(next_prim_idx >= MGA_NUM_PRIM_BUFS) -     		next_prim_idx = 0; -   	prim_buffer = dev_priv->prim_bufs[next_prim_idx]; -	set_bit(MGA_IN_WAIT, &dev_priv->dispatch_status); -    -      	/* In use is cleared in interrupt handler */ -    -	s = splsofttq(); -   	if(test_and_set_bit(MGA_BUF_IN_USE, &prim_buffer->buffer_status)) { -	   	for (;;) { -		   	mga_dma_schedule(dev, 0); -		   	if(!test_and_set_bit(MGA_BUF_IN_USE,  -					     &prim_buffer->buffer_status))  -				break; -		   	atomic_inc(&dev->total_sleeps); -		   	atomic_inc(&dma->total_missed_sched); -			ret = tsleep(&dev_priv->wait_queue, PZERO|PCATCH, -				     "mgaap", 0); -			if (ret) -				break; -		} -	   	if(ret) { -			splx(s); -			return ret; -		} -	} -	clear_bit(MGA_IN_WAIT, &dev_priv->dispatch_status); -	splx(s); - -   	/* This primary buffer is now free to use */ -   	prim_buffer->current_dma_ptr = prim_buffer->head; -   	prim_buffer->num_dwords = 0; -   	prim_buffer->sec_used = 0; -	prim_buffer->prim_age = dev_priv->next_prim_age++; -	if(prim_buffer->prim_age == 0 || prim_buffer->prim_age == 0xffffffff) { -	   mga_flush_queue(dev); -	   mga_dma_quiescent(dev); -	   mga_reset_freelist(dev); -	   prim_buffer->prim_age = (dev_priv->next_prim_age += 2); -	} +	drm_mga_primary_buffer_t *primary = &dev_priv->prim; +	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; +	u32 head = dev_priv->primary->offset; +	DRM_DEBUG( "%s:\n", __FUNCTION__ ); + +	sarea_priv->last_wrap++; +	DRM_DEBUG( "   wrap = %d\n", sarea_priv->last_wrap ); -	/* Reset all buffer status stuff */ -	clear_bit(MGA_BUF_NEEDS_OVERFLOW, &prim_buffer->buffer_status); -	clear_bit(MGA_BUF_FORCE_FIRE, &prim_buffer->buffer_status); -	clear_bit(MGA_BUF_SWAP_PENDING, &prim_buffer->buffer_status); +	mga_flush_write_combine(); +	MGA_WRITE( MGA_PRIMADDRESS, head | MGA_DMA_GENERAL ); -   	dev_priv->current_prim = prim_buffer; -   	dev_priv->current_prim_idx = next_prim_idx; -   	return 0; +	clear_bit( 0, &primary->wrapped ); +	DRM_DEBUG( "%s: done.\n", __FUNCTION__ );  } -/* More dynamic performance decisions */ -static __inline int mga_decide_to_fire(drm_device_t *dev) + +/* ================================================================ + * Freelist management + */ + +#define MGA_BUFFER_USED		~0 +#define MGA_BUFFER_FREE		0 + +#if MGA_FREELIST_DEBUG +static void mga_freelist_print( drm_device_t *dev )  { -   	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; +	drm_mga_private_t *dev_priv = dev->dev_private; +	drm_mga_freelist_t *entry; + +	DRM_INFO( "\n" ); +	DRM_INFO( "current dispatch: last=0x%x done=0x%x\n", +		  dev_priv->sarea_priv->last_dispatch, +		  (unsigned int)(MGA_READ( MGA_PRIMADDRESS ) - +				 dev_priv->primary->offset) ); +	DRM_INFO( "current freelist:\n" ); + +	for ( entry = dev_priv->head->next ; entry ; entry = entry->next ) { +		DRM_INFO( "   %p   idx=%2d  age=0x%x 0x%06lx\n", +			  entry, entry->buf->idx, entry->age.head, +			  entry->age.head - dev_priv->primary->offset ); +	} +	DRM_INFO( "\n" ); +} +#endif -   	DRM_DEBUG("%s\n", __FUNCTION__); +static int mga_freelist_init( drm_device_t *dev, drm_mga_private_t *dev_priv ) +{ +	drm_device_dma_t *dma = dev->dma; +	drm_buf_t *buf; +	drm_mga_buf_priv_t *buf_priv; +	drm_mga_freelist_t *entry; +	int i; +	DRM_DEBUG( "%s: count=%d\n", +		   __FUNCTION__, dma->buf_count ); -   	if(test_bit(MGA_BUF_FORCE_FIRE, &dev_priv->next_prim->buffer_status)) { -	   	return 1; -	} +	dev_priv->head = DRM(alloc)( sizeof(drm_mga_freelist_t), +				     DRM_MEM_DRIVER ); +	if ( dev_priv->head == NULL ) +		DRM_OS_RETURN(ENOMEM); -	if (test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status) && -	    dev_priv->next_prim->num_dwords) { -	   	return 1; -	} +	memset( dev_priv->head, 0, sizeof(drm_mga_freelist_t) ); +	SET_AGE( &dev_priv->head->age, MGA_BUFFER_USED, 0 ); -	if (test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) && -	    dev_priv->next_prim->num_dwords) { -	   	return 1; -	} -    -   	if(atomic_read(&dev_priv->pending_bufs) <= MGA_NUM_PRIM_BUFS - 1) { -		if(test_bit(MGA_BUF_SWAP_PENDING,  -			    &dev_priv->next_prim->buffer_status)) { -			return 1; -		} -	} +	for ( i = 0 ; i < dma->buf_count ; i++ ) { +		buf = dma->buflist[i]; +	        buf_priv = buf->dev_private; -   	if(atomic_read(&dev_priv->pending_bufs) <= MGA_NUM_PRIM_BUFS / 2) { -		if(dev_priv->next_prim->sec_used >= MGA_DMA_BUF_NR / 8) { -			return 1; -		} -	} +		entry = DRM(alloc)( sizeof(drm_mga_freelist_t), +				    DRM_MEM_DRIVER ); +		if ( entry == NULL ) +			DRM_OS_RETURN(ENOMEM); -   	if(atomic_read(&dev_priv->pending_bufs) >= MGA_NUM_PRIM_BUFS / 2) { -		if(dev_priv->next_prim->sec_used >= MGA_DMA_BUF_NR / 4) { -			return 1; -		} -	} +		memset( entry, 0, sizeof(drm_mga_freelist_t) ); -   	return 0; -} +		entry->next = dev_priv->head->next; +		entry->prev = dev_priv->head; +		SET_AGE( &entry->age, MGA_BUFFER_FREE, 0 ); +		entry->buf = buf; -int mga_dma_schedule(drm_device_t *dev, int locked) -{ -      	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; -	int	retval =0 ; +		if ( dev_priv->head->next != NULL ) +			dev_priv->head->next->prev = entry; +		if ( entry->next == NULL ) +			dev_priv->tail = entry; -	if (!dev_priv) return EBUSY; +		buf_priv->list_entry = entry; +		buf_priv->discard = 0; +		buf_priv->dispatched = 0; -   	if (test_and_set_bit(0, &dev->dma_flag)) { -		retval = EBUSY; -		goto sch_out_wakeup; -	} -    -	DRM_DEBUG("%s\n", __FUNCTION__); -	if (!dev_priv) { -		DRM_DEBUG("dev_priv is not set\n"); -		return (0); +		dev_priv->head->next = entry;  	} -   	if(test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) ||  -	   test_bit(MGA_IN_WAIT, &dev_priv->dispatch_status) || -	   test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status)) { -		locked = 1; -	} -    -   	if (!locked &&  -	    !drm_lock_take(&dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT)) { -	   	clear_bit(0, &dev->dma_flag); -		DRM_DEBUG("Not locked\n"); -		retval = EBUSY; -		goto sch_out_wakeup; -	} +	return 0; +} -   	if(!test_and_set_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status)) { -	   	/* Fire dma buffer */ -	   	if(mga_decide_to_fire(dev)) { -			clear_bit(MGA_BUF_FORCE_FIRE,  -				  &dev_priv->next_prim->buffer_status); -		   	if(dev_priv->current_prim == dev_priv->next_prim) { -				/* Schedule overflow for a later time */ -				set_bit(MGA_BUF_NEEDS_OVERFLOW, -					&dev_priv->next_prim->buffer_status); -			} -		   	mga_fire_primary(dev, dev_priv->next_prim); -		} else { -			clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status); -		} -	} -   	 -	if (!locked) { -		if (drm_lock_free(dev, &dev->lock.hw_lock->lock, -				  DRM_KERNEL_CONTEXT)) { -			DRM_ERROR("\n"); -		} -	} -	clear_bit(0, &dev->dma_flag); -sch_out_wakeup: -      	if(test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) && -	   atomic_read(&dev_priv->pending_bufs) == 0) { -	   	/* Everything has been processed by the hardware */ -		clear_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status); -	   	wakeup(&dev_priv->flush_queue); -	} +static void mga_freelist_cleanup( drm_device_t *dev ) +{ +	drm_mga_private_t *dev_priv = dev->dev_private; +	drm_mga_freelist_t *entry; +	drm_mga_freelist_t *next; +	DRM_DEBUG( "%s\n", __FUNCTION__ ); -	if(test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status) && -	   dev_priv->tail->age < dev_priv->last_prim_age)  -		wakeup(&dev_priv->buf_queue); +	entry = dev_priv->head; +	while ( entry ) { +		next = entry->next; +		DRM(free)( entry, sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER ); +		entry = next; +	} -	return retval; +	dev_priv->head = dev_priv->tail = NULL;  } -static void mga_dma_service(void *arg) +#if 0 +/* FIXME: Still needed? + */ +static void mga_freelist_reset( drm_device_t *dev )  { -    	drm_device_t	 *dev = (drm_device_t *)arg; -    	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; -    	drm_mga_prim_buf_t *last_prim_buffer; - -    	atomic_inc(&dev->total_irq); -	if((MGA_READ(MGAREG_STATUS) & 0x00000001) != 0x00000001) return; -      	MGA_WRITE(MGAREG_ICLEAR, 0x00000001); -   	last_prim_buffer = dev_priv->last_prim; -    	last_prim_buffer->num_dwords = 0; -    	last_prim_buffer->sec_used = 0; -	dev_priv->sarea_priv->last_dispatch =  -		dev_priv->last_prim_age = last_prim_buffer->prim_age; -      	clear_bit(MGA_BUF_IN_USE, &last_prim_buffer->buffer_status); -      	clear_bit(MGA_BUF_SWAP_PENDING, &last_prim_buffer->buffer_status); -      	clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status); -      	atomic_dec(&dev_priv->pending_bufs); -	taskqueue_enqueue(taskqueue_swi, &dev->task); -   	wakeup(&dev_priv->wait_queue); +	drm_device_dma_t *dma = dev->dma; +	drm_buf_t *buf; +	drm_mga_buf_priv_t *buf_priv; +	int i; + +	for ( i = 0 ; i < dma->buf_count ; i++ ) { +		buf = dma->buflist[i]; +	        buf_priv = buf->dev_private; +		SET_AGE( &buf_priv->list_entry->age, +			 MGA_BUFFER_FREE, 0 ); +	}  } +#endif -static void mga_dma_task_queue(void *device, int pending) +static drm_buf_t *mga_freelist_get( drm_device_t *dev )  { -	DRM_DEBUG("%s\n", __FUNCTION__); -	mga_dma_schedule((drm_device_t *)device, 0); +	drm_mga_private_t *dev_priv = dev->dev_private; +	drm_mga_freelist_t *next; +	drm_mga_freelist_t *prev; +	drm_mga_freelist_t *tail = dev_priv->tail; +	u32 head, wrap; +	DRM_DEBUG( "%s:\n", __FUNCTION__ ); + +	head = MGA_READ( MGA_PRIMADDRESS ); +	wrap = dev_priv->sarea_priv->last_wrap; + +	DRM_DEBUG( "   tail=0x%06lx %d\n", +		   tail->age.head ? +		   tail->age.head - dev_priv->primary->offset : 0, +		   tail->age.wrap ); +	DRM_DEBUG( "   head=0x%06lx %d\n", +		   head - dev_priv->primary->offset, wrap ); + +	if ( TEST_AGE( &tail->age, head, wrap ) ) { +		prev = dev_priv->tail->prev; +		next = dev_priv->tail; +		prev->next = NULL; +		next->prev = next->next = NULL; +		dev_priv->tail = prev; +		SET_AGE( &next->age, MGA_BUFFER_USED, 0 ); +		return next->buf; +	} + +	DRM_DEBUG( "returning NULL!\n" ); +	return NULL;  } -int mga_dma_cleanup(drm_device_t *dev) +int mga_freelist_put( drm_device_t *dev, drm_buf_t *buf )  { -	DRM_DEBUG("%s\n", __FUNCTION__); - -	if(dev->dev_private) { -		drm_mga_private_t *dev_priv =  -			(drm_mga_private_t *) dev->dev_private; -       -		if (dev->irq) mga_flush_queue(dev); -		mga_dma_quiescent(dev); -		if(dev_priv->ioremap) { -			int temp = (dev_priv->warp_ucode_size +  -				    dev_priv->primary_size +  -				    PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE; - -			drm_ioremapfree((void *) dev_priv->ioremap, temp); -		} -	   	if(dev_priv->real_status_page != 0UL) { -		   	mga_free_page(dev, dev_priv->real_status_page); -		} -	   	if(dev_priv->prim_bufs != NULL) { -		   	int i; -		   	for(i = 0; i < MGA_NUM_PRIM_BUFS; i++) { -			   	if(dev_priv->prim_bufs[i] != NULL) { -			     		drm_free(dev_priv->prim_bufs[i], -						 sizeof(drm_mga_prim_buf_t), -						 DRM_MEM_DRIVER); -				} -			} -		   	drm_free(dev_priv->prim_bufs, sizeof(void *) * -				 (MGA_NUM_PRIM_BUFS + 1),  -				 DRM_MEM_DRIVER); -		} -		if(dev_priv->head != NULL) { -		   	mga_freelist_cleanup(dev); -		} +	drm_mga_private_t *dev_priv = dev->dev_private; +	drm_mga_buf_priv_t *buf_priv = buf->dev_private; +	drm_mga_freelist_t *head, *entry, *prev; +	DRM_DEBUG( "%s: age=0x%06lx wrap=%d\n", +		   __FUNCTION__, +		   buf_priv->list_entry->age.head - +		   dev_priv->primary->offset, +		   buf_priv->list_entry->age.wrap ); -		drm_free(dev->dev_private, sizeof(drm_mga_private_t),  -			 DRM_MEM_DRIVER); -		dev->dev_private = NULL; +	entry = buf_priv->list_entry; +	head = dev_priv->head; + +	if ( buf_priv->list_entry->age.head == MGA_BUFFER_USED ) { +		SET_AGE( &entry->age, MGA_BUFFER_FREE, 0 ); +		prev = dev_priv->tail; +		prev->next = entry; +		entry->prev = prev; +		entry->next = NULL; +	} else { +		prev = head->next; +		head->next = entry; +		prev->prev = entry; +		entry->prev = head; +		entry->next = prev;  	}  	return 0;  } -static int mga_dma_initialize(drm_device_t *dev, drm_mga_init_t *init) { + +/* ================================================================ + * DMA initialization, cleanup + */ + +static int mga_do_init_dma( drm_device_t *dev, drm_mga_init_t *init ) +{  	drm_mga_private_t *dev_priv; -	drm_map_t *sarea_map = NULL; +	drm_map_list_entry_t *listentry; +	int ret; +	DRM_DEBUG( "%s\n", __FUNCTION__ ); -	dev_priv = drm_alloc(sizeof(drm_mga_private_t), DRM_MEM_DRIVER); -	if(dev_priv == NULL) return ENOMEM; -	dev->dev_private = (void *) dev_priv; +	dev_priv = DRM(alloc)( sizeof(drm_mga_private_t), DRM_MEM_DRIVER ); +	if ( !dev_priv ) +		DRM_OS_RETURN(ENOMEM); -	memset(dev_priv, 0, sizeof(drm_mga_private_t)); +	memset( dev_priv, 0, sizeof(drm_mga_private_t) ); -	if((init->reserved_map_idx >= dev->map_count) || -	   (init->buffer_map_idx >= dev->map_count)) { -		mga_dma_cleanup(dev); -		DRM_DEBUG("reserved_map or buffer_map are invalid\n"); -		return EINVAL; -	} -    -	dev_priv->reserved_map_idx = init->reserved_map_idx; -	dev_priv->buffer_map_idx = init->buffer_map_idx; -	sarea_map = dev->maplist[0]; -	dev_priv->sarea_priv = (drm_mga_sarea_t *)  -		((u_int8_t *)sarea_map->handle +  -		 init->sarea_priv_offset); - -	/* Scale primary size to the next page */  	dev_priv->chipset = init->chipset; -	dev_priv->frontOffset = init->frontOffset; -	dev_priv->backOffset = init->backOffset; -	dev_priv->depthOffset = init->depthOffset; -	dev_priv->textureOffset = init->textureOffset; -	dev_priv->textureSize = init->textureSize; -	dev_priv->cpp = init->cpp; -	dev_priv->sgram = init->sgram; -	dev_priv->stride = init->stride; - -	dev_priv->mAccess = init->mAccess; -   	dev_priv->flush_queue = 0; -	dev_priv->WarpPipe = 0xff000000; -	dev_priv->vertexsize = 0; - -   	DRM_DEBUG("chipset: %d ucode_size: %d backOffset: %x depthOffset: %x\n", -		  dev_priv->chipset, dev_priv->warp_ucode_size,  -		  dev_priv->backOffset, dev_priv->depthOffset); -   	DRM_DEBUG("cpp: %d sgram: %d stride: %d maccess: %x\n", -		  dev_priv->cpp, dev_priv->sgram, dev_priv->stride,  -		  dev_priv->mAccess); -    -	memcpy(&dev_priv->WarpIndex, &init->WarpIndex,  -	       sizeof(drm_mga_warp_index_t) * MGA_MAX_WARP_PIPES); - -   	if(mga_init_primary_bufs(dev, init) != 0) { -		DRM_ERROR("Can not initialize primary buffers\n"); -		mga_dma_cleanup(dev); -		return ENOMEM; + +	dev_priv->usec_timeout = MGA_DEFAULT_USEC_TIMEOUT; + +	if ( init->sgram ) { +		dev_priv->clear_cmd = MGA_DWGCTL_CLEAR | MGA_ATYPE_BLK; +	} else { +		dev_priv->clear_cmd = MGA_DWGCTL_CLEAR | MGA_ATYPE_RSTR;  	} -   	dev_priv->real_status_page = mga_alloc_page(dev); -      	if(dev_priv->real_status_page == 0UL) { -		mga_dma_cleanup(dev); -		DRM_ERROR("Can not allocate status page\n"); -		return ENOMEM; +	dev_priv->maccess	= init->maccess; + +	dev_priv->fb_cpp	= init->fb_cpp; +	dev_priv->front_offset	= init->front_offset; +	dev_priv->front_pitch	= init->front_pitch; +	dev_priv->back_offset	= init->back_offset; +	dev_priv->back_pitch	= init->back_pitch; + +	dev_priv->depth_cpp	= init->depth_cpp; +	dev_priv->depth_offset	= init->depth_offset; +	dev_priv->depth_pitch	= init->depth_pitch; + +	/* FIXME: Need to support AGP textures... +	 */ +	dev_priv->texture_offset = init->texture_offset[0]; +	dev_priv->texture_size = init->texture_size[0]; + +	TAILQ_FOREACH(listentry, dev->maplist, link) { +		drm_map_t *map = listentry->map; +		if (map->type == _DRM_SHM && +			map->flags & _DRM_CONTAINS_LOCK) { +			dev_priv->sarea = map; +			break; +		}  	} -   	dev_priv->status_page = (void*)dev_priv->real_status_page; /* XXX wants nocache */ +	if(!dev_priv->sarea) { +		DRM_ERROR( "failed to find sarea!\n" ); +		/* Assign dev_private so we can do cleanup. */ +		dev->dev_private = (void *)dev_priv; +		mga_do_cleanup_dma( dev ); +		DRM_OS_RETURN(EINVAL); +	} + + +	DRM_FIND_MAP( dev_priv->fb, init->fb_offset ); +	if(!dev_priv->fb) { +		DRM_ERROR( "failed to find framebuffer!\n" ); +		/* Assign dev_private so we can do cleanup. */ +		dev->dev_private = (void *)dev_priv; +		mga_do_cleanup_dma( dev ); +		DRM_OS_RETURN(EINVAL); +	} +	DRM_FIND_MAP( dev_priv->mmio, init->mmio_offset ); +	if(!dev_priv->mmio) { +		DRM_ERROR( "failed to find mmio region!\n" ); +		/* Assign dev_private so we can do cleanup. */ +		dev->dev_private = (void *)dev_priv; +		mga_do_cleanup_dma( dev ); +		DRM_OS_RETURN(EINVAL); +	} +	DRM_FIND_MAP( dev_priv->status, init->status_offset ); +	if(!dev_priv->status) { +		DRM_ERROR( "failed to find status page!\n" ); +		/* Assign dev_private so we can do cleanup. */ +		dev->dev_private = (void *)dev_priv; +		mga_do_cleanup_dma( dev ); +		DRM_OS_RETURN(EINVAL); +	} +	DRM_FIND_MAP( dev_priv->warp, init->warp_offset ); +	if(!dev_priv->warp) { +		DRM_ERROR( "failed to find warp microcode region!\n" ); +		/* Assign dev_private so we can do cleanup. */ +		dev->dev_private = (void *)dev_priv; +		mga_do_cleanup_dma( dev ); +		DRM_OS_RETURN(EINVAL); +	} +	DRM_FIND_MAP( dev_priv->primary, init->primary_offset ); +	if(!dev_priv->primary) { +		DRM_ERROR( "failed to find primary dma region!\n" ); +		/* Assign dev_private so we can do cleanup. */ +		dev->dev_private = (void *)dev_priv; +		mga_do_cleanup_dma( dev ); +		DRM_OS_RETURN(EINVAL); +	} +	DRM_FIND_MAP( dev_priv->buffers, init->buffers_offset ); +	if(!dev_priv->buffers) { +		DRM_ERROR( "failed to find dma buffer region!\n" ); +		/* Assign dev_private so we can do cleanup. */ +		dev->dev_private = (void *)dev_priv; +		mga_do_cleanup_dma( dev ); +		DRM_OS_RETURN(EINVAL); +	} + +	dev_priv->sarea_priv = +		(drm_mga_sarea_t *)((u8 *)dev_priv->sarea->handle + +				    init->sarea_priv_offset); + +	DRM_IOREMAP( dev_priv->warp ); +	DRM_IOREMAP( dev_priv->primary ); +	DRM_IOREMAP( dev_priv->buffers ); + +	if(!dev_priv->warp->handle || +	   !dev_priv->primary->handle || +	   !dev_priv->buffers->handle ) { +		DRM_ERROR( "failed to ioremap agp regions!\n" ); +		/* Assign dev_private so we can do cleanup. */ +		dev->dev_private = (void *)dev_priv; +		mga_do_cleanup_dma( dev ); +		DRM_OS_RETURN(ENOMEM); +	} + +	ret = mga_warp_install_microcode( dev_priv ); +	if ( ret < 0 ) { +		DRM_ERROR( "failed to install WARP ucode!\n" ); +		/* Assign dev_private so we can do cleanup. */ +		dev->dev_private = (void *)dev_priv; +		mga_do_cleanup_dma( dev ); +		DRM_OS_RETURN(ret); +	} + +	ret = mga_warp_init( dev_priv ); +	if ( ret < 0 ) { +		DRM_ERROR( "failed to init WARP engine!\n" ); +		/* Assign dev_private so we can do cleanup. */ +		dev->dev_private = (void *)dev_priv; +		mga_do_cleanup_dma( dev ); +		DRM_OS_RETURN(ret); +	} + +	dev_priv->prim.status = (u32 *)dev_priv->status->handle; + +	mga_do_wait_for_idle( dev_priv ); + +	/* Init the primary DMA registers. +	 */ +	MGA_WRITE( MGA_PRIMADDRESS, +		   dev_priv->primary->offset | MGA_DMA_GENERAL );  #if 0 -   	dev_priv->status_page =  -		ioremap_nocache(virt_to_bus((void *)dev_priv->real_status_page), -				PAGE_SIZE); - -   	if(dev_priv->status_page == NULL) { -		mga_dma_cleanup(dev); -		DRM_ERROR("Can not remap status page\n"); -		return ENOMEM; -	} +	MGA_WRITE( MGA_PRIMPTR, +		   virt_to_bus((void *)dev_priv->prim.status) | +		   MGA_PRIMPTREN0 |	/* Soft trap, SECEND, SETUPEND */ +		   MGA_PRIMPTREN1 );	/* DWGSYNC */  #endif -   	/* Write status page when secend or softrap occurs */ -   	MGA_WRITE(MGAREG_PRIMPTR,  -		  vtophys((void *)dev_priv->real_status_page) | 0x00000003); -       - -	/* Private is now filled in, initialize the hardware */ -	{ -		PRIMLOCALS; -		PRIMGETPTR( dev_priv ); -	   	    -		PRIMOUTREG(MGAREG_DMAPAD, 0); -		PRIMOUTREG(MGAREG_DMAPAD, 0); -		PRIMOUTREG(MGAREG_DWGSYNC, 0x0100); -		PRIMOUTREG(MGAREG_SOFTRAP, 0); -		/* Poll for the first buffer to insure that -		 * the status register will be correct -		 */ -	    -		mga_flush_write_combine(); -	   	MGA_WRITE(MGAREG_PRIMADDRESS, phys_head | TT_GENERAL); - -		MGA_WRITE(MGAREG_PRIMEND, ((phys_head + num_dwords * 4) |  -					   PDEA_pagpxfer_enable)); -	    -	   	while(MGA_READ(MGAREG_DWGSYNC) != 0x0100) ; +	dev_priv->prim.start = (u8 *)dev_priv->primary->handle; +	dev_priv->prim.end = ((u8 *)dev_priv->primary->handle +			      + dev_priv->primary->size); +	dev_priv->prim.size = dev_priv->primary->size; + +	dev_priv->prim.tail = 0; +	dev_priv->prim.space = dev_priv->prim.size; +	dev_priv->prim.wrapped = 0; + +	dev_priv->prim.last_flush = 0; +	dev_priv->prim.last_wrap = 0; + +	dev_priv->prim.high_mark = 256 * DMA_BLOCK_SIZE; + + +	dev_priv->prim.status[0] = dev_priv->primary->offset; +	dev_priv->prim.status[1] = 0; + +	dev_priv->sarea_priv->last_wrap = 0; +	dev_priv->sarea_priv->last_frame.head = 0; +	dev_priv->sarea_priv->last_frame.wrap = 0; + +	if ( mga_freelist_init( dev, dev_priv ) < 0 ) { +		DRM_ERROR( "could not initialize freelist\n" ); +		/* Assign dev_private so we can do cleanup. */ +		dev->dev_private = (void *)dev_priv; +		mga_do_cleanup_dma( dev ); +		DRM_OS_RETURN(ENOMEM);  	} -	if(mga_freelist_init(dev) != 0) { -	   	DRM_ERROR("Could not initialize freelist\n"); -	   	mga_dma_cleanup(dev); -	   	return ENOMEM; +	/* Make dev_private visable to others. */ +	dev->dev_private = (void *)dev_priv; +	return 0; +} + +int mga_do_cleanup_dma( drm_device_t *dev ) +{ +	DRM_DEBUG( "%s\n", __FUNCTION__ ); + +	if ( dev->dev_private ) { +		drm_mga_private_t *dev_priv = dev->dev_private; + +		DRM_IOREMAPFREE( dev_priv->warp ); +		DRM_IOREMAPFREE( dev_priv->primary ); +		DRM_IOREMAPFREE( dev_priv->buffers ); + +		if ( dev_priv->head != NULL ) { +			mga_freelist_cleanup( dev ); +		} + +		DRM(free)( dev->dev_private, sizeof(drm_mga_private_t), +			   DRM_MEM_DRIVER ); +		dev->dev_private = NULL;  	} +  	return 0;  } -int -mga_dma_init(dev_t kdev, u_long cmd, caddr_t data, int flags, struct proc *p) +int mga_dma_init( DRM_OS_IOCTL )  { -        drm_device_t *dev = kdev->si_drv1; +	DRM_OS_DEVICE;  	drm_mga_init_t init; -    -   	DRM_DEBUG("%s\n", __FUNCTION__); -	init = *(drm_mga_init_t *) data; -    -	switch(init.func) { +	DRM_OS_KRNFROMUSR( init, (drm_mga_init_t *) data, sizeof(init) ); + +	switch ( init.func ) {  	case MGA_INIT_DMA: -		return mga_dma_initialize(dev, &init); +		return mga_do_init_dma( dev, &init );  	case MGA_CLEANUP_DMA: -		return mga_dma_cleanup(dev); +		return mga_do_cleanup_dma( dev );  	} -	return EINVAL; +	DRM_OS_RETURN( EINVAL );  } -int mga_irq_install(drm_device_t *dev, int irq) + +/* ================================================================ + * Primary DMA stream management + */ + +int mga_dma_flush( DRM_OS_IOCTL )  { -	int rid; -	int retcode; +	DRM_OS_DEVICE; +	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; +	drm_lock_t lock; -	if (!irq)     return EINVAL; -	 -	lockmgr(&dev->dev_lock, LK_EXCLUSIVE, 0, curproc); -	if (dev->irq) { -		lockmgr(&dev->dev_lock, LK_RELEASE, 0, curproc); -		return EBUSY; -	} -	lockmgr(&dev->dev_lock, LK_RELEASE, 0, curproc); -	 -	DRM_DEBUG("install irq handler %d\n", 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; -	TASK_INIT(&dev->task, 0, mga_dma_task_queue, dev); - -				/* Before installing handler */ -	MGA_WRITE(MGAREG_IEN, 0); -   				/* Install handler */ -	rid = 0; -	dev->irq = bus_alloc_resource(dev->device, SYS_RES_IRQ, &rid, -				      0, ~0, 1, RF_SHAREABLE); -	if (!dev->irq) -		return ENOENT; - -	retcode = bus_setup_intr(dev->device, dev->irq, INTR_TYPE_TTY, -				 mga_dma_service, dev, &dev->irqh); -	if (retcode) { -		bus_release_resource(dev->device, SYS_RES_IRQ, 0, dev->irq); -		dev->irq = 0; -		return retcode; -	} +	LOCK_TEST_WITH_RETURN( dev ); -				/* After installing handler */ -   	MGA_WRITE(MGAREG_ICLEAR, 0x00000001); -	MGA_WRITE(MGAREG_IEN, 0x00000001); -	return 0; -} +	DRM_OS_KRNFROMUSR( lock, (drm_lock_t *) data, sizeof(lock) ); -int mga_irq_uninstall(drm_device_t *dev) -{ -	if (!dev->irq) -		return EINVAL; -	 -   	DRM_DEBUG("remove irq handler %ld\n", rman_get_start(dev->irq)); -      	MGA_WRITE(MGAREG_ICLEAR, 0x00000001); -	MGA_WRITE(MGAREG_IEN, 0); +	DRM_DEBUG( "%s: %s%s%s\n", +		   __FUNCTION__, +		   (lock.flags & _DRM_LOCK_FLUSH) ?	"flush, " : "", +		   (lock.flags & _DRM_LOCK_FLUSH_ALL) ?	"flush all, " : "", +		   (lock.flags & _DRM_LOCK_QUIESCENT) ?	"idle, " : "" ); -	bus_teardown_intr(dev->device, dev->irq, dev->irqh); -	bus_release_resource(dev->device, SYS_RES_IRQ, 0, dev->irq); -	dev->irq = 0; +	WRAP_WAIT_WITH_RETURN( dev_priv ); -	return 0; -} +	if ( lock.flags & (_DRM_LOCK_FLUSH | _DRM_LOCK_FLUSH_ALL) ) { +		mga_do_dma_flush( dev_priv ); +	} -int mga_control(dev_t kdev, u_long cmd, caddr_t data, -		int flags, struct proc *p) -{ -	drm_device_t	*dev	= kdev->si_drv1; -	drm_control_t	ctl; -    -	ctl = *(drm_control_t *) data; - -   	DRM_DEBUG("%s\n", __FUNCTION__); - -	switch (ctl.func) { -	case DRM_INST_HANDLER: -		return mga_irq_install(dev, ctl.irq); -	case DRM_UNINST_HANDLER: -		return mga_irq_uninstall(dev); -	default: -		return EINVAL; +	if ( lock.flags & _DRM_LOCK_QUIESCENT ) { +#if MGA_DMA_DEBUG +		int ret = mga_do_wait_for_idle( dev_priv ); +		if ( ret ) +			DRM_INFO( __FUNCTION__": -EBUSY\n" ); +		return ret; +#else +		return mga_do_wait_for_idle( dev_priv ); +#endif +	} else { +		return 0;  	}  } -static int mga_flush_queue(drm_device_t *dev) +int mga_dma_reset( DRM_OS_IOCTL )  { -  	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; -   	int ret = 0; -	int s; - -   	DRM_DEBUG("%s\n", __FUNCTION__); - -   	if(!dev_priv) return 0; -    -   	if(dev_priv->next_prim->num_dwords != 0) { -		s = splsofttq(); -		set_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status); -   		for (;;) { -		   	mga_dma_schedule(dev, 0); -	   		if (!test_bit(MGA_IN_FLUSH,  -				      &dev_priv->dispatch_status))  -				break; -		   	atomic_inc(&dev->total_sleeps); -			ret = tsleep(&dev_priv->flush_queue, PZERO|PCATCH, -				     "mgafq", 0); -			if (ret) { -				clear_bit(MGA_IN_FLUSH,  -					  &dev_priv->dispatch_status); -		   		break; -			} -		} -		splx(s); -	} -   	return ret; +	DRM_OS_DEVICE; +	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + +	LOCK_TEST_WITH_RETURN( dev ); + +	return mga_do_dma_reset( dev_priv );  } -/* Must be called with the lock held */ -void mga_reclaim_buffers(drm_device_t *dev, pid_t pid) + +/* ================================================================ + * DMA buffer management + */ + +#if 0 +static int mga_dma_get_buffers( drm_device_t *dev, drm_dma_t *d )  { -	drm_device_dma_t *dma = dev->dma; -	int		 i; +	drm_buf_t *buf; +	int i; -	if (!dma) return; -      	if(dev->dev_private == NULL) return; -	if(dma->buflist == NULL) return; +	for ( i = d->granted_count ; i < d->request_count ; i++ ) { +		buf = mga_freelist_get( dev ); +		if ( !buf ) +			DRM_OS_RETURN( EAGAIN ); -	DRM_DEBUG("%s\n", __FUNCTION__); -        mga_flush_queue(dev); +		buf->pid = current->pid; -	for (i = 0; i < dma->buf_count; i++) { -	   	drm_buf_t *buf = dma->buflist[ i ]; -	   	drm_mga_buf_priv_t *buf_priv = buf->dev_private; +		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 ); -		/* Only buffers that need to get reclaimed ever  -		 * get set to free  -		 */ -		if (buf->pid == pid  && buf_priv) { -			if(buf_priv->my_freelist->age == MGA_BUF_USED)  -		     		buf_priv->my_freelist->age = MGA_BUF_FREE; -		} +		d->granted_count++;  	} +	return 0;  } +#endif /* 0 */ -int mga_lock(dev_t kdev, u_long cmd, caddr_t data, -	     int flags, struct proc *p) +int mga_dma_buffers( DRM_OS_IOCTL )  { -	drm_device_t	  *dev	  = kdev->si_drv1; -	int		  ret	= 0; -	drm_lock_t	  lock; +	DRM_OS_DEVICE; +	drm_device_dma_t *dma = dev->dma; +	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; +	drm_dma_t d; +	drm_buf_t *buf; +	int i; +	int ret = 0; -	DRM_DEBUG("%s\n", __FUNCTION__); -	lock = *(drm_lock_t *) data; +	LOCK_TEST_WITH_RETURN( dev ); -	if (lock.context == DRM_KERNEL_CONTEXT) { -		DRM_ERROR("Process %d using kernel context %d\n", -			  p->p_pid, lock.context); -		return EINVAL; -	} -    -   	DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", -	       lock.context, p->p_pid, dev->lock.hw_lock->lock, -	       lock.flags); +	DRM_OS_KRNFROMUSR( d, (drm_dma_t *) data, sizeof(d) ); -	if (lock.context < 0) { -		return EINVAL; -	} -    -	/* Only one queue: +	/* Please don't send us buffers.  	 */ - -	if (!ret) { -		atomic_inc(&dev->lock.lock_queue); -		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	    = p->p_pid; -				dev->lock.lock_time = ticks; -				atomic_inc(&dev->total_locks); -				break;	/* Got lock */ -			} -			 -				/* Contention */ -			atomic_inc(&dev->total_sleeps); -			ret = tsleep(&dev->lock.lock_queue, PZERO|PCATCH, -					 "mgal2", 0); -			if (ret) -				break; -		} -		atomic_dec(&dev->lock.lock_queue); +	if ( d.send_count != 0 ) { +		DRM_ERROR( "Process %d trying to send %d buffers via drmDMA\n", +			   DRM_OS_CURRENTPID, d.send_count ); +		DRM_OS_RETURN( EINVAL );  	} -	 -	if (!ret) { -		if (lock.flags & _DRM_LOCK_QUIESCENT) { -		   DRM_DEBUG("_DRM_LOCK_QUIESCENT\n"); -		   mga_flush_queue(dev); -		   mga_dma_quiescent(dev); -		} + +	/* We'll send you buffers. +	 */ +	if ( d.request_count < 0 || d.request_count > dma->buf_count ) { +		DRM_ERROR( "Process %d trying to get %d buffers (of %d max)\n", +			   DRM_OS_CURRENTPID, d.request_count, dma->buf_count ); +		DRM_OS_RETURN( EINVAL );  	} -    -	if (ret) DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); -	return ret; -} -		 -int mga_flush_ioctl(dev_t kdev, u_long cmd, caddr_t data, -		    int flags, struct proc *p) -{ -	drm_device_t	  *dev	  = kdev->si_drv1; -	drm_lock_t	  lock; -      	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; -	int		  s; -   	DRM_DEBUG("%s\n", __FUNCTION__); -	lock = *(drm_lock_t *) data; +	WRAP_TEST_WITH_RETURN( dev_priv ); -	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { -		DRM_ERROR("mga_flush_ioctl called without lock held\n"); -		return EINVAL; -	} +	d.granted_count = 0; -   	if(lock.flags & _DRM_LOCK_FLUSH || lock.flags & _DRM_LOCK_FLUSH_ALL) { -		drm_mga_prim_buf_t *temp_buf;  -		temp_buf = dev_priv->current_prim; - -			s = splsofttq(); -		if(temp_buf && temp_buf->num_dwords) { -			set_bit(MGA_BUF_FORCE_FIRE, &temp_buf->buffer_status); -			mga_advance_primary(dev); - 		} -			mga_dma_schedule(dev, 1); -			splx(s); -	} -   	if(lock.flags & _DRM_LOCK_QUIESCENT) { -		mga_flush_queue(dev); -		mga_dma_quiescent(dev); +	if ( d.request_count ) { +		for ( i = d.granted_count ; i < d.request_count ; i++ ) { +			buf = mga_freelist_get( dev ); +			if ( !buf ) +				DRM_OS_RETURN( EAGAIN ); + +			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++; +		} +		ret = 0;  	} -    	return 0; +	DRM_OS_KRNTOUSR( (drm_dma_t *) data, d, sizeof(d) ); + +	return ret;  }  | 
