mga_dma.c revision 95746
195584Sanholt/* mga_dma.c -- DMA support for mga g200/g400 -*- linux-c -*-
295584Sanholt * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
395584Sanholt *
495584Sanholt * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
595584Sanholt * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
695584Sanholt * All Rights Reserved.
795584Sanholt *
895584Sanholt * Permission is hereby granted, free of charge, to any person obtaining a
995584Sanholt * copy of this software and associated documentation files (the "Software"),
1095584Sanholt * to deal in the Software without restriction, including without limitation
1195584Sanholt * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1295584Sanholt * and/or sell copies of the Software, and to permit persons to whom the
1395584Sanholt * Software is furnished to do so, subject to the following conditions:
1495584Sanholt *
1595584Sanholt * The above copyright notice and this permission notice (including the next
1695584Sanholt * paragraph) shall be included in all copies or substantial portions of the
1795584Sanholt * Software.
1895584Sanholt *
1995584Sanholt * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2095584Sanholt * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2195584Sanholt * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
2295584Sanholt * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
2395584Sanholt * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2495584Sanholt * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2595584Sanholt * DEALINGS IN THE SOFTWARE.
2695584Sanholt *
2795584Sanholt * Authors:
2895584Sanholt *    Rickard E. (Rik) Faith <faith@valinux.com>
2995584Sanholt *    Jeff Hartmann <jhartmann@valinux.com>
3095584Sanholt *    Keith Whitwell <keithw@valinux.com>
3195584Sanholt *
3295584Sanholt * Rewritten by:
3395584Sanholt *    Gareth Hughes <gareth@valinux.com>
3495584Sanholt *
3595584Sanholt * $FreeBSD: head/sys/dev/drm/mga_dma.c 95746 2002-04-29 18:18:42Z anholt $
3695584Sanholt */
3795584Sanholt
3895584Sanholt#define __NO_VERSION__
3995584Sanholt#include "dev/drm/mga.h"
4095584Sanholt#include "dev/drm/drmP.h"
4195746Sanholt#include "dev/drm/mga_drm.h"
4295584Sanholt#include "dev/drm/mga_drv.h"
4395584Sanholt
4495584Sanholt#ifdef __linux__
4595584Sanholt#include <linux/interrupt.h>	/* For task queue support */
4695584Sanholt#include <linux/delay.h>
4795584Sanholt#endif /* __linux__ */
4895584Sanholt
4995584Sanholt#define MGA_DEFAULT_USEC_TIMEOUT	10000
5095584Sanholt#define MGA_FREELIST_DEBUG		0
5195584Sanholt
5295584Sanholt
5395584Sanholt/* ================================================================
5495584Sanholt * Engine control
5595584Sanholt */
5695584Sanholt
5795584Sanholtint mga_do_wait_for_idle( drm_mga_private_t *dev_priv )
5895584Sanholt{
5995584Sanholt	u32 status = 0;
6095584Sanholt	int i;
6195584Sanholt	DRM_DEBUG( "%s\n", __FUNCTION__ );
6295584Sanholt
6395584Sanholt	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
6495584Sanholt		status = MGA_READ( MGA_STATUS ) & MGA_ENGINE_IDLE_MASK;
6595584Sanholt		if ( status == MGA_ENDPRDMASTS ) {
6695584Sanholt			MGA_WRITE8( MGA_CRTC_INDEX, 0 );
6795584Sanholt			return 0;
6895584Sanholt		}
6995584Sanholt		DRM_OS_DELAY( 1 );
7095584Sanholt	}
7195584Sanholt
7295584Sanholt#if MGA_DMA_DEBUG
7395584Sanholt	DRM_ERROR( "failed!\n" );
7495584Sanholt	DRM_INFO( "   status=0x%08x\n", status );
7595584Sanholt#endif
7695693Sanholt	return DRM_OS_ERR(EBUSY);
7795584Sanholt}
7895584Sanholt
7995584Sanholtint mga_do_dma_idle( drm_mga_private_t *dev_priv )
8095584Sanholt{
8195584Sanholt	u32 status = 0;
8295584Sanholt	int i;
8395584Sanholt	DRM_DEBUG( "%s\n", __FUNCTION__ );
8495584Sanholt
8595584Sanholt	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
8695584Sanholt		status = MGA_READ( MGA_STATUS ) & MGA_DMA_IDLE_MASK;
8795584Sanholt		if ( status == MGA_ENDPRDMASTS ) return 0;
8895584Sanholt		DRM_OS_DELAY( 1 );
8995584Sanholt	}
9095584Sanholt
9195584Sanholt#if MGA_DMA_DEBUG
9295584Sanholt	DRM_ERROR( "failed! status=0x%08x\n", status );
9395584Sanholt#endif
9495693Sanholt	return DRM_OS_ERR(EBUSY);
9595584Sanholt}
9695584Sanholt
9795584Sanholtint mga_do_dma_reset( drm_mga_private_t *dev_priv )
9895584Sanholt{
9995584Sanholt	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
10095584Sanholt	drm_mga_primary_buffer_t *primary = &dev_priv->prim;
10195584Sanholt
10295584Sanholt	DRM_DEBUG( "%s\n", __FUNCTION__ );
10395584Sanholt
10495584Sanholt	/* The primary DMA stream should look like new right about now.
10595584Sanholt	 */
10695584Sanholt	primary->tail = 0;
10795584Sanholt	primary->space = primary->size;
10895584Sanholt	primary->last_flush = 0;
10995584Sanholt
11095584Sanholt	sarea_priv->last_wrap = 0;
11195584Sanholt
11295584Sanholt	/* FIXME: Reset counters, buffer ages etc...
11395584Sanholt	 */
11495584Sanholt
11595584Sanholt	/* FIXME: What else do we need to reinitialize?  WARP stuff?
11695584Sanholt	 */
11795584Sanholt
11895584Sanholt	return 0;
11995584Sanholt}
12095584Sanholt
12195584Sanholtint mga_do_engine_reset( drm_mga_private_t *dev_priv )
12295584Sanholt{
12395584Sanholt	DRM_DEBUG( "%s\n", __FUNCTION__ );
12495584Sanholt
12595584Sanholt	/* Okay, so we've completely screwed up and locked the engine.
12695584Sanholt	 * How about we clean up after ourselves?
12795584Sanholt	 */
12895584Sanholt	MGA_WRITE( MGA_RST, MGA_SOFTRESET );
12995584Sanholt	DRM_OS_DELAY( 15 );		/* Wait at least 10 usecs */
13095584Sanholt	MGA_WRITE( MGA_RST, 0 );
13195584Sanholt
13295584Sanholt	/* Initialize the registers that get clobbered by the soft
13395584Sanholt	 * reset.  Many of the core register values survive a reset,
13495584Sanholt	 * but the drawing registers are basically all gone.
13595584Sanholt	 *
13695584Sanholt	 * 3D clients should probably die after calling this.  The X
13795584Sanholt	 * server should reset the engine state to known values.
13895584Sanholt	 */
13995584Sanholt#if 0
14095584Sanholt	MGA_WRITE( MGA_PRIMPTR,
14195584Sanholt		   virt_to_bus((void *)dev_priv->prim.status_page) |
14295584Sanholt		   MGA_PRIMPTREN0 |
14395584Sanholt		   MGA_PRIMPTREN1 );
14495584Sanholt#endif
14595584Sanholt
14695584Sanholt	MGA_WRITE( MGA_ICLEAR, MGA_SOFTRAPICLR );
14795584Sanholt	MGA_WRITE( MGA_IEN,    MGA_SOFTRAPIEN );
14895584Sanholt
14995584Sanholt	/* The primary DMA stream should look like new right about now.
15095584Sanholt	 */
15195584Sanholt	mga_do_dma_reset( dev_priv );
15295584Sanholt
15395584Sanholt	/* This bad boy will never fail.
15495584Sanholt	 */
15595584Sanholt	return 0;
15695584Sanholt}
15795584Sanholt
15895584Sanholt
15995584Sanholt/* ================================================================
16095584Sanholt * Primary DMA stream
16195584Sanholt */
16295584Sanholt
16395584Sanholtvoid mga_do_dma_flush( drm_mga_private_t *dev_priv )
16495584Sanholt{
16595584Sanholt	drm_mga_primary_buffer_t *primary = &dev_priv->prim;
16695584Sanholt	u32 head, tail;
16795584Sanholt	DMA_LOCALS;
16895584Sanholt	DRM_DEBUG( "%s:\n", __FUNCTION__ );
16995584Sanholt
17095584Sanholt	if ( primary->tail == primary->last_flush ) {
17195584Sanholt		DRM_DEBUG( "   bailing out...\n" );
17295584Sanholt		return;
17395584Sanholt	}
17495584Sanholt
17595584Sanholt	tail = primary->tail + dev_priv->primary->offset;
17695584Sanholt
17795584Sanholt	/* We need to pad the stream between flushes, as the card
17895584Sanholt	 * actually (partially?) reads the first of these commands.
17995584Sanholt	 * See page 4-16 in the G400 manual, middle of the page or so.
18095584Sanholt	 */
18195584Sanholt	BEGIN_DMA( 1 );
18295584Sanholt
18395584Sanholt	DMA_BLOCK( MGA_DMAPAD,  0x00000000,
18495584Sanholt		   MGA_DMAPAD,  0x00000000,
18595584Sanholt		   MGA_DMAPAD,  0x00000000,
18695584Sanholt		   MGA_DMAPAD,	0x00000000 );
18795584Sanholt
18895584Sanholt	ADVANCE_DMA();
18995584Sanholt
19095584Sanholt	primary->last_flush = primary->tail;
19195584Sanholt
19295584Sanholt	head = MGA_READ( MGA_PRIMADDRESS );
19395584Sanholt
19495584Sanholt	if ( head <= tail ) {
19595584Sanholt		primary->space = primary->size - primary->tail;
19695584Sanholt	} else {
19795584Sanholt		primary->space = head - tail;
19895584Sanholt	}
19995584Sanholt
20095584Sanholt	DRM_DEBUG( "   head = 0x%06lx\n", head - dev_priv->primary->offset );
20195584Sanholt	DRM_DEBUG( "   tail = 0x%06lx\n", tail - dev_priv->primary->offset );
20295584Sanholt	DRM_DEBUG( "  space = 0x%06x\n", primary->space );
20395584Sanholt
20495584Sanholt	mga_flush_write_combine();
20595584Sanholt	MGA_WRITE( MGA_PRIMEND, tail | MGA_PAGPXFER );
20695584Sanholt
20795584Sanholt	DRM_DEBUG( "%s: done.\n", __FUNCTION__ );
20895584Sanholt}
20995584Sanholt
21095584Sanholtvoid mga_do_dma_wrap_start( drm_mga_private_t *dev_priv )
21195584Sanholt{
21295584Sanholt	drm_mga_primary_buffer_t *primary = &dev_priv->prim;
21395584Sanholt	u32 head, tail;
21495584Sanholt	DMA_LOCALS;
21595584Sanholt	DRM_DEBUG( "%s:\n", __FUNCTION__ );
21695584Sanholt
21795584Sanholt	BEGIN_DMA_WRAP();
21895584Sanholt
21995584Sanholt	DMA_BLOCK( MGA_DMAPAD,	0x00000000,
22095584Sanholt		   MGA_DMAPAD,	0x00000000,
22195584Sanholt		   MGA_DMAPAD,	0x00000000,
22295584Sanholt		   MGA_DMAPAD,	0x00000000 );
22395584Sanholt
22495584Sanholt	ADVANCE_DMA();
22595584Sanholt
22695584Sanholt	tail = primary->tail + dev_priv->primary->offset;
22795584Sanholt
22895584Sanholt	primary->tail = 0;
22995584Sanholt	primary->last_flush = 0;
23095584Sanholt	primary->last_wrap++;
23195584Sanholt
23295584Sanholt	head = MGA_READ( MGA_PRIMADDRESS );
23395584Sanholt
23495584Sanholt	if ( head == dev_priv->primary->offset ) {
23595584Sanholt		primary->space = primary->size;
23695584Sanholt	} else {
23795584Sanholt		primary->space = head - dev_priv->primary->offset;
23895584Sanholt	}
23995584Sanholt
24095584Sanholt	DRM_DEBUG( "   head = 0x%06lx\n",
24195584Sanholt		  head - dev_priv->primary->offset );
24295584Sanholt	DRM_DEBUG( "   tail = 0x%06x\n", primary->tail );
24395584Sanholt	DRM_DEBUG( "   wrap = %d\n", primary->last_wrap );
24495584Sanholt	DRM_DEBUG( "  space = 0x%06x\n", primary->space );
24595584Sanholt
24695584Sanholt	mga_flush_write_combine();
24795584Sanholt	MGA_WRITE( MGA_PRIMEND, tail | MGA_PAGPXFER );
24895584Sanholt
24995584Sanholt	set_bit( 0, &primary->wrapped );
25095584Sanholt	DRM_DEBUG( "%s: done.\n", __FUNCTION__ );
25195584Sanholt}
25295584Sanholt
25395584Sanholtvoid mga_do_dma_wrap_end( drm_mga_private_t *dev_priv )
25495584Sanholt{
25595584Sanholt	drm_mga_primary_buffer_t *primary = &dev_priv->prim;
25695584Sanholt	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
25795584Sanholt	u32 head = dev_priv->primary->offset;
25895584Sanholt	DRM_DEBUG( "%s:\n", __FUNCTION__ );
25995584Sanholt
26095584Sanholt	sarea_priv->last_wrap++;
26195584Sanholt	DRM_DEBUG( "   wrap = %d\n", sarea_priv->last_wrap );
26295584Sanholt
26395584Sanholt	mga_flush_write_combine();
26495584Sanholt	MGA_WRITE( MGA_PRIMADDRESS, head | MGA_DMA_GENERAL );
26595584Sanholt
26695584Sanholt	clear_bit( 0, &primary->wrapped );
26795584Sanholt	DRM_DEBUG( "%s: done.\n", __FUNCTION__ );
26895584Sanholt}
26995584Sanholt
27095584Sanholt
27195584Sanholt/* ================================================================
27295584Sanholt * Freelist management
27395584Sanholt */
27495584Sanholt
27595584Sanholt#define MGA_BUFFER_USED		~0
27695584Sanholt#define MGA_BUFFER_FREE		0
27795584Sanholt
27895584Sanholt#if MGA_FREELIST_DEBUG
27995584Sanholtstatic void mga_freelist_print( drm_device_t *dev )
28095584Sanholt{
28195584Sanholt	drm_mga_private_t *dev_priv = dev->dev_private;
28295584Sanholt	drm_mga_freelist_t *entry;
28395584Sanholt
28495584Sanholt	DRM_INFO( "\n" );
28595584Sanholt	DRM_INFO( "current dispatch: last=0x%x done=0x%x\n",
28695584Sanholt		  dev_priv->sarea_priv->last_dispatch,
28795584Sanholt		  (unsigned int)(MGA_READ( MGA_PRIMADDRESS ) -
28895584Sanholt				 dev_priv->primary->offset) );
28995584Sanholt	DRM_INFO( "current freelist:\n" );
29095584Sanholt
29195584Sanholt	for ( entry = dev_priv->head->next ; entry ; entry = entry->next ) {
29295584Sanholt		DRM_INFO( "   %p   idx=%2d  age=0x%x 0x%06lx\n",
29395584Sanholt			  entry, entry->buf->idx, entry->age.head,
29495584Sanholt			  entry->age.head - dev_priv->primary->offset );
29595584Sanholt	}
29695584Sanholt	DRM_INFO( "\n" );
29795584Sanholt}
29895584Sanholt#endif
29995584Sanholt
30095584Sanholtstatic int mga_freelist_init( drm_device_t *dev, drm_mga_private_t *dev_priv )
30195584Sanholt{
30295584Sanholt	drm_device_dma_t *dma = dev->dma;
30395584Sanholt	drm_buf_t *buf;
30495584Sanholt	drm_mga_buf_priv_t *buf_priv;
30595584Sanholt	drm_mga_freelist_t *entry;
30695584Sanholt	int i;
30795584Sanholt	DRM_DEBUG( "%s: count=%d\n",
30895584Sanholt		   __FUNCTION__, dma->buf_count );
30995584Sanholt
31095584Sanholt	dev_priv->head = DRM(alloc)( sizeof(drm_mga_freelist_t),
31195584Sanholt				     DRM_MEM_DRIVER );
31295584Sanholt	if ( dev_priv->head == NULL )
31395693Sanholt		return DRM_OS_ERR(ENOMEM);
31495584Sanholt
31595584Sanholt	memset( dev_priv->head, 0, sizeof(drm_mga_freelist_t) );
31695584Sanholt	SET_AGE( &dev_priv->head->age, MGA_BUFFER_USED, 0 );
31795584Sanholt
31895584Sanholt	for ( i = 0 ; i < dma->buf_count ; i++ ) {
31995584Sanholt		buf = dma->buflist[i];
32095584Sanholt	        buf_priv = buf->dev_private;
32195584Sanholt
32295584Sanholt		entry = DRM(alloc)( sizeof(drm_mga_freelist_t),
32395584Sanholt				    DRM_MEM_DRIVER );
32495584Sanholt		if ( entry == NULL )
32595693Sanholt			return DRM_OS_ERR(ENOMEM);
32695584Sanholt
32795584Sanholt		memset( entry, 0, sizeof(drm_mga_freelist_t) );
32895584Sanholt
32995584Sanholt		entry->next = dev_priv->head->next;
33095584Sanholt		entry->prev = dev_priv->head;
33195584Sanholt		SET_AGE( &entry->age, MGA_BUFFER_FREE, 0 );
33295584Sanholt		entry->buf = buf;
33395584Sanholt
33495584Sanholt		if ( dev_priv->head->next != NULL )
33595584Sanholt			dev_priv->head->next->prev = entry;
33695584Sanholt		if ( entry->next == NULL )
33795584Sanholt			dev_priv->tail = entry;
33895584Sanholt
33995584Sanholt		buf_priv->list_entry = entry;
34095584Sanholt		buf_priv->discard = 0;
34195584Sanholt		buf_priv->dispatched = 0;
34295584Sanholt
34395584Sanholt		dev_priv->head->next = entry;
34495584Sanholt	}
34595584Sanholt
34695584Sanholt	return 0;
34795584Sanholt}
34895584Sanholt
34995584Sanholtstatic void mga_freelist_cleanup( drm_device_t *dev )
35095584Sanholt{
35195584Sanholt	drm_mga_private_t *dev_priv = dev->dev_private;
35295584Sanholt	drm_mga_freelist_t *entry;
35395584Sanholt	drm_mga_freelist_t *next;
35495584Sanholt	DRM_DEBUG( "%s\n", __FUNCTION__ );
35595584Sanholt
35695584Sanholt	entry = dev_priv->head;
35795584Sanholt	while ( entry ) {
35895584Sanholt		next = entry->next;
35995584Sanholt		DRM(free)( entry, sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER );
36095584Sanholt		entry = next;
36195584Sanholt	}
36295584Sanholt
36395584Sanholt	dev_priv->head = dev_priv->tail = NULL;
36495584Sanholt}
36595584Sanholt
36695584Sanholt#if 0
36795584Sanholt/* FIXME: Still needed?
36895584Sanholt */
36995584Sanholtstatic void mga_freelist_reset( drm_device_t *dev )
37095584Sanholt{
37195584Sanholt	drm_device_dma_t *dma = dev->dma;
37295584Sanholt	drm_buf_t *buf;
37395584Sanholt	drm_mga_buf_priv_t *buf_priv;
37495584Sanholt	int i;
37595584Sanholt
37695584Sanholt	for ( i = 0 ; i < dma->buf_count ; i++ ) {
37795584Sanholt		buf = dma->buflist[i];
37895584Sanholt	        buf_priv = buf->dev_private;
37995584Sanholt		SET_AGE( &buf_priv->list_entry->age,
38095584Sanholt			 MGA_BUFFER_FREE, 0 );
38195584Sanholt	}
38295584Sanholt}
38395584Sanholt#endif
38495584Sanholt
38595584Sanholtstatic drm_buf_t *mga_freelist_get( drm_device_t *dev )
38695584Sanholt{
38795584Sanholt	drm_mga_private_t *dev_priv = dev->dev_private;
38895584Sanholt	drm_mga_freelist_t *next;
38995584Sanholt	drm_mga_freelist_t *prev;
39095584Sanholt	drm_mga_freelist_t *tail = dev_priv->tail;
39195584Sanholt	u32 head, wrap;
39295584Sanholt	DRM_DEBUG( "%s:\n", __FUNCTION__ );
39395584Sanholt
39495584Sanholt	head = MGA_READ( MGA_PRIMADDRESS );
39595584Sanholt	wrap = dev_priv->sarea_priv->last_wrap;
39695584Sanholt
39795584Sanholt	DRM_DEBUG( "   tail=0x%06lx %d\n",
39895584Sanholt		   tail->age.head ?
39995584Sanholt		   tail->age.head - dev_priv->primary->offset : 0,
40095584Sanholt		   tail->age.wrap );
40195584Sanholt	DRM_DEBUG( "   head=0x%06lx %d\n",
40295584Sanholt		   head - dev_priv->primary->offset, wrap );
40395584Sanholt
40495584Sanholt	if ( TEST_AGE( &tail->age, head, wrap ) ) {
40595584Sanholt		prev = dev_priv->tail->prev;
40695584Sanholt		next = dev_priv->tail;
40795584Sanholt		prev->next = NULL;
40895584Sanholt		next->prev = next->next = NULL;
40995584Sanholt		dev_priv->tail = prev;
41095584Sanholt		SET_AGE( &next->age, MGA_BUFFER_USED, 0 );
41195584Sanholt		return next->buf;
41295584Sanholt	}
41395584Sanholt
41495584Sanholt	DRM_DEBUG( "returning NULL!\n" );
41595584Sanholt	return NULL;
41695584Sanholt}
41795584Sanholt
41895584Sanholtint mga_freelist_put( drm_device_t *dev, drm_buf_t *buf )
41995584Sanholt{
42095584Sanholt	drm_mga_private_t *dev_priv = dev->dev_private;
42195584Sanholt	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
42295584Sanholt	drm_mga_freelist_t *head, *entry, *prev;
42395584Sanholt
42495584Sanholt	DRM_DEBUG( "%s: age=0x%06lx wrap=%d\n",
42595584Sanholt		   __FUNCTION__,
42695584Sanholt		   buf_priv->list_entry->age.head -
42795584Sanholt		   dev_priv->primary->offset,
42895584Sanholt		   buf_priv->list_entry->age.wrap );
42995584Sanholt
43095584Sanholt	entry = buf_priv->list_entry;
43195584Sanholt	head = dev_priv->head;
43295584Sanholt
43395584Sanholt	if ( buf_priv->list_entry->age.head == MGA_BUFFER_USED ) {
43495584Sanholt		SET_AGE( &entry->age, MGA_BUFFER_FREE, 0 );
43595584Sanholt		prev = dev_priv->tail;
43695584Sanholt		prev->next = entry;
43795584Sanholt		entry->prev = prev;
43895584Sanholt		entry->next = NULL;
43995584Sanholt	} else {
44095584Sanholt		prev = head->next;
44195584Sanholt		head->next = entry;
44295584Sanholt		prev->prev = entry;
44395584Sanholt		entry->prev = head;
44495584Sanholt		entry->next = prev;
44595584Sanholt	}
44695584Sanholt
44795584Sanholt	return 0;
44895584Sanholt}
44995584Sanholt
45095584Sanholt
45195584Sanholt/* ================================================================
45295584Sanholt * DMA initialization, cleanup
45395584Sanholt */
45495584Sanholt
45595584Sanholtstatic int mga_do_init_dma( drm_device_t *dev, drm_mga_init_t *init )
45695584Sanholt{
45795584Sanholt	drm_mga_private_t *dev_priv;
45895584Sanholt#ifdef __linux__
45995584Sanholt	struct list_head *list;
46095584Sanholt#endif /* __linux__ */
46195584Sanholt#ifdef __FreeBSD__
46295584Sanholt	drm_map_list_entry_t *listentry;
46395584Sanholt#endif /* __FreeBSD__ */
46495584Sanholt	int ret;
46595584Sanholt	DRM_DEBUG( "%s\n", __FUNCTION__ );
46695584Sanholt
46795584Sanholt	dev_priv = DRM(alloc)( sizeof(drm_mga_private_t), DRM_MEM_DRIVER );
46895584Sanholt	if ( !dev_priv )
46995693Sanholt		return DRM_OS_ERR(ENOMEM);
47095584Sanholt
47195584Sanholt	memset( dev_priv, 0, sizeof(drm_mga_private_t) );
47295584Sanholt
47395584Sanholt	dev_priv->chipset = init->chipset;
47495584Sanholt
47595584Sanholt	dev_priv->usec_timeout = MGA_DEFAULT_USEC_TIMEOUT;
47695584Sanholt
47795584Sanholt	if ( init->sgram ) {
47895584Sanholt		dev_priv->clear_cmd = MGA_DWGCTL_CLEAR | MGA_ATYPE_BLK;
47995584Sanholt	} else {
48095584Sanholt		dev_priv->clear_cmd = MGA_DWGCTL_CLEAR | MGA_ATYPE_RSTR;
48195584Sanholt	}
48295584Sanholt	dev_priv->maccess	= init->maccess;
48395584Sanholt
48495584Sanholt	dev_priv->fb_cpp	= init->fb_cpp;
48595584Sanholt	dev_priv->front_offset	= init->front_offset;
48695584Sanholt	dev_priv->front_pitch	= init->front_pitch;
48795584Sanholt	dev_priv->back_offset	= init->back_offset;
48895584Sanholt	dev_priv->back_pitch	= init->back_pitch;
48995584Sanholt
49095584Sanholt	dev_priv->depth_cpp	= init->depth_cpp;
49195584Sanholt	dev_priv->depth_offset	= init->depth_offset;
49295584Sanholt	dev_priv->depth_pitch	= init->depth_pitch;
49395584Sanholt
49495584Sanholt	/* FIXME: Need to support AGP textures...
49595584Sanholt	 */
49695584Sanholt	dev_priv->texture_offset = init->texture_offset[0];
49795584Sanholt	dev_priv->texture_size = init->texture_size[0];
49895584Sanholt
49995584Sanholt#ifdef __linux__
50095584Sanholt	list_for_each( list, &dev->maplist->head ) {
50195584Sanholt		drm_map_list_t *entry = (drm_map_list_t *)list;
50295584Sanholt		if ( entry->map &&
50395584Sanholt		     entry->map->type == _DRM_SHM &&
50495584Sanholt		     (entry->map->flags & _DRM_CONTAINS_LOCK) ) {
50595584Sanholt			dev_priv->sarea = entry->map;
50695584Sanholt 			break;
50795584Sanholt 		}
50895584Sanholt 	}
50995584Sanholt#endif /* __linux__ */
51095584Sanholt#ifdef __FreeBSD__
51195584Sanholt	TAILQ_FOREACH(listentry, dev->maplist, link) {
51295584Sanholt		drm_map_t *map = listentry->map;
51395584Sanholt		if (map->type == _DRM_SHM &&
51495584Sanholt			map->flags & _DRM_CONTAINS_LOCK) {
51595584Sanholt			dev_priv->sarea = map;
51695584Sanholt			break;
51795584Sanholt		}
51895584Sanholt	}
51995584Sanholt#endif /* __FreeBSD__ */
52095584Sanholt
52195584Sanholt	if(!dev_priv->sarea) {
52295584Sanholt		DRM_ERROR( "failed to find sarea!\n" );
52395584Sanholt		/* Assign dev_private so we can do cleanup. */
52495584Sanholt		dev->dev_private = (void *)dev_priv;
52595584Sanholt		mga_do_cleanup_dma( dev );
52695693Sanholt		return DRM_OS_ERR(EINVAL);
52795584Sanholt	}
52895584Sanholt
52995584Sanholt
53095584Sanholt	DRM_FIND_MAP( dev_priv->fb, init->fb_offset );
53195584Sanholt	if(!dev_priv->fb) {
53295584Sanholt		DRM_ERROR( "failed to find framebuffer!\n" );
53395584Sanholt		/* Assign dev_private so we can do cleanup. */
53495584Sanholt		dev->dev_private = (void *)dev_priv;
53595584Sanholt		mga_do_cleanup_dma( dev );
53695693Sanholt		return DRM_OS_ERR(EINVAL);
53795584Sanholt	}
53895584Sanholt	DRM_FIND_MAP( dev_priv->mmio, init->mmio_offset );
53995584Sanholt	if(!dev_priv->mmio) {
54095584Sanholt		DRM_ERROR( "failed to find mmio region!\n" );
54195584Sanholt		/* Assign dev_private so we can do cleanup. */
54295584Sanholt		dev->dev_private = (void *)dev_priv;
54395584Sanholt		mga_do_cleanup_dma( dev );
54495693Sanholt		return DRM_OS_ERR(EINVAL);
54595584Sanholt	}
54695584Sanholt	DRM_FIND_MAP( dev_priv->status, init->status_offset );
54795584Sanholt	if(!dev_priv->status) {
54895584Sanholt		DRM_ERROR( "failed to find status page!\n" );
54995584Sanholt		/* Assign dev_private so we can do cleanup. */
55095584Sanholt		dev->dev_private = (void *)dev_priv;
55195584Sanholt		mga_do_cleanup_dma( dev );
55295693Sanholt		return DRM_OS_ERR(EINVAL);
55395584Sanholt	}
55495584Sanholt	DRM_FIND_MAP( dev_priv->warp, init->warp_offset );
55595584Sanholt	if(!dev_priv->warp) {
55695584Sanholt		DRM_ERROR( "failed to find warp microcode region!\n" );
55795584Sanholt		/* Assign dev_private so we can do cleanup. */
55895584Sanholt		dev->dev_private = (void *)dev_priv;
55995584Sanholt		mga_do_cleanup_dma( dev );
56095693Sanholt		return DRM_OS_ERR(EINVAL);
56195584Sanholt	}
56295584Sanholt	DRM_FIND_MAP( dev_priv->primary, init->primary_offset );
56395584Sanholt	if(!dev_priv->primary) {
56495584Sanholt		DRM_ERROR( "failed to find primary dma region!\n" );
56595584Sanholt		/* Assign dev_private so we can do cleanup. */
56695584Sanholt		dev->dev_private = (void *)dev_priv;
56795584Sanholt		mga_do_cleanup_dma( dev );
56895693Sanholt		return DRM_OS_ERR(EINVAL);
56995584Sanholt	}
57095584Sanholt	DRM_FIND_MAP( dev_priv->buffers, init->buffers_offset );
57195584Sanholt	if(!dev_priv->buffers) {
57295584Sanholt		DRM_ERROR( "failed to find dma buffer region!\n" );
57395584Sanholt		/* Assign dev_private so we can do cleanup. */
57495584Sanholt		dev->dev_private = (void *)dev_priv;
57595584Sanholt		mga_do_cleanup_dma( dev );
57695693Sanholt		return DRM_OS_ERR(EINVAL);
57795584Sanholt	}
57895584Sanholt
57995584Sanholt	dev_priv->sarea_priv =
58095584Sanholt		(drm_mga_sarea_t *)((u8 *)dev_priv->sarea->handle +
58195584Sanholt				    init->sarea_priv_offset);
58295584Sanholt
58395584Sanholt	DRM_IOREMAP( dev_priv->warp );
58495584Sanholt	DRM_IOREMAP( dev_priv->primary );
58595584Sanholt	DRM_IOREMAP( dev_priv->buffers );
58695584Sanholt
58795584Sanholt	if(!dev_priv->warp->handle ||
58895584Sanholt	   !dev_priv->primary->handle ||
58995584Sanholt	   !dev_priv->buffers->handle ) {
59095584Sanholt		DRM_ERROR( "failed to ioremap agp regions!\n" );
59195584Sanholt		/* Assign dev_private so we can do cleanup. */
59295584Sanholt		dev->dev_private = (void *)dev_priv;
59395584Sanholt		mga_do_cleanup_dma( dev );
59495693Sanholt		return DRM_OS_ERR(ENOMEM);
59595584Sanholt	}
59695584Sanholt
59795584Sanholt	ret = mga_warp_install_microcode( dev_priv );
59895693Sanholt	if ( ret ) {
59995584Sanholt		DRM_ERROR( "failed to install WARP ucode!\n" );
60095584Sanholt		/* Assign dev_private so we can do cleanup. */
60195584Sanholt		dev->dev_private = (void *)dev_priv;
60295584Sanholt		mga_do_cleanup_dma( dev );
60395693Sanholt		return DRM_OS_ERR(ret);
60495584Sanholt	}
60595584Sanholt
60695584Sanholt	ret = mga_warp_init( dev_priv );
60795693Sanholt	if ( ret ) {
60895584Sanholt		DRM_ERROR( "failed to init WARP engine!\n" );
60995584Sanholt		/* Assign dev_private so we can do cleanup. */
61095584Sanholt		dev->dev_private = (void *)dev_priv;
61195584Sanholt		mga_do_cleanup_dma( dev );
61295693Sanholt		return DRM_OS_ERR(ret);
61395584Sanholt	}
61495584Sanholt
61595584Sanholt	dev_priv->prim.status = (u32 *)dev_priv->status->handle;
61695584Sanholt
61795584Sanholt	mga_do_wait_for_idle( dev_priv );
61895584Sanholt
61995584Sanholt	/* Init the primary DMA registers.
62095584Sanholt	 */
62195584Sanholt	MGA_WRITE( MGA_PRIMADDRESS,
62295584Sanholt		   dev_priv->primary->offset | MGA_DMA_GENERAL );
62395584Sanholt#if 0
62495584Sanholt	MGA_WRITE( MGA_PRIMPTR,
62595584Sanholt		   virt_to_bus((void *)dev_priv->prim.status) |
62695584Sanholt		   MGA_PRIMPTREN0 |	/* Soft trap, SECEND, SETUPEND */
62795584Sanholt		   MGA_PRIMPTREN1 );	/* DWGSYNC */
62895584Sanholt#endif
62995584Sanholt
63095584Sanholt	dev_priv->prim.start = (u8 *)dev_priv->primary->handle;
63195584Sanholt	dev_priv->prim.end = ((u8 *)dev_priv->primary->handle
63295584Sanholt			      + dev_priv->primary->size);
63395584Sanholt	dev_priv->prim.size = dev_priv->primary->size;
63495584Sanholt
63595584Sanholt	dev_priv->prim.tail = 0;
63695584Sanholt	dev_priv->prim.space = dev_priv->prim.size;
63795584Sanholt	dev_priv->prim.wrapped = 0;
63895584Sanholt
63995584Sanholt	dev_priv->prim.last_flush = 0;
64095584Sanholt	dev_priv->prim.last_wrap = 0;
64195584Sanholt
64295584Sanholt	dev_priv->prim.high_mark = 256 * DMA_BLOCK_SIZE;
64395584Sanholt
64495584Sanholt#ifdef __linux__
64595584Sanholt	spin_lock_init( &dev_priv->prim.list_lock );
64695584Sanholt#endif /* __linux__ */
64795584Sanholt
64895584Sanholt	dev_priv->prim.status[0] = dev_priv->primary->offset;
64995584Sanholt	dev_priv->prim.status[1] = 0;
65095584Sanholt
65195584Sanholt	dev_priv->sarea_priv->last_wrap = 0;
65295584Sanholt	dev_priv->sarea_priv->last_frame.head = 0;
65395584Sanholt	dev_priv->sarea_priv->last_frame.wrap = 0;
65495584Sanholt
65595584Sanholt	if ( mga_freelist_init( dev, dev_priv ) < 0 ) {
65695584Sanholt		DRM_ERROR( "could not initialize freelist\n" );
65795584Sanholt		/* Assign dev_private so we can do cleanup. */
65895584Sanholt		dev->dev_private = (void *)dev_priv;
65995584Sanholt		mga_do_cleanup_dma( dev );
66095693Sanholt		return DRM_OS_ERR(ENOMEM);
66195584Sanholt	}
66295584Sanholt
66395584Sanholt	/* Make dev_private visable to others. */
66495584Sanholt	dev->dev_private = (void *)dev_priv;
66595584Sanholt	return 0;
66695584Sanholt}
66795584Sanholt
66895584Sanholtint mga_do_cleanup_dma( drm_device_t *dev )
66995584Sanholt{
67095584Sanholt	DRM_DEBUG( "%s\n", __FUNCTION__ );
67195584Sanholt
67295584Sanholt	if ( dev->dev_private ) {
67395584Sanholt		drm_mga_private_t *dev_priv = dev->dev_private;
67495584Sanholt
67595584Sanholt		DRM_IOREMAPFREE( dev_priv->warp );
67695584Sanholt		DRM_IOREMAPFREE( dev_priv->primary );
67795584Sanholt		DRM_IOREMAPFREE( dev_priv->buffers );
67895584Sanholt
67995584Sanholt		if ( dev_priv->head != NULL ) {
68095584Sanholt			mga_freelist_cleanup( dev );
68195584Sanholt		}
68295584Sanholt
68395584Sanholt		DRM(free)( dev->dev_private, sizeof(drm_mga_private_t),
68495584Sanholt			   DRM_MEM_DRIVER );
68595584Sanholt		dev->dev_private = NULL;
68695584Sanholt	}
68795584Sanholt
68895584Sanholt	return 0;
68995584Sanholt}
69095584Sanholt
69195584Sanholtint mga_dma_init( DRM_OS_IOCTL )
69295584Sanholt{
69395584Sanholt	DRM_OS_DEVICE;
69495584Sanholt	drm_mga_init_t init;
69595584Sanholt
69695584Sanholt	DRM_OS_KRNFROMUSR( init, (drm_mga_init_t *) data, sizeof(init) );
69795584Sanholt
69895584Sanholt	switch ( init.func ) {
69995584Sanholt	case MGA_INIT_DMA:
70095584Sanholt		return mga_do_init_dma( dev, &init );
70195584Sanholt	case MGA_CLEANUP_DMA:
70295584Sanholt		return mga_do_cleanup_dma( dev );
70395584Sanholt	}
70495584Sanholt
70595693Sanholt	return DRM_OS_ERR(EINVAL);
70695584Sanholt}
70795584Sanholt
70895584Sanholt
70995584Sanholt/* ================================================================
71095584Sanholt * Primary DMA stream management
71195584Sanholt */
71295584Sanholt
71395584Sanholtint mga_dma_flush( DRM_OS_IOCTL )
71495584Sanholt{
71595584Sanholt	DRM_OS_DEVICE;
71695584Sanholt	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
71795584Sanholt	drm_lock_t lock;
71895584Sanholt
71995584Sanholt	LOCK_TEST_WITH_RETURN( dev );
72095584Sanholt
72195584Sanholt	DRM_OS_KRNFROMUSR( lock, (drm_lock_t *) data, sizeof(lock) );
72295584Sanholt
72395584Sanholt	DRM_DEBUG( "%s: %s%s%s\n",
72495584Sanholt		   __FUNCTION__,
72595584Sanholt		   (lock.flags & _DRM_LOCK_FLUSH) ?	"flush, " : "",
72695584Sanholt		   (lock.flags & _DRM_LOCK_FLUSH_ALL) ?	"flush all, " : "",
72795584Sanholt		   (lock.flags & _DRM_LOCK_QUIESCENT) ?	"idle, " : "" );
72895584Sanholt
72995584Sanholt	WRAP_WAIT_WITH_RETURN( dev_priv );
73095584Sanholt
73195584Sanholt	if ( lock.flags & (_DRM_LOCK_FLUSH | _DRM_LOCK_FLUSH_ALL) ) {
73295584Sanholt		mga_do_dma_flush( dev_priv );
73395584Sanholt	}
73495584Sanholt
73595584Sanholt	if ( lock.flags & _DRM_LOCK_QUIESCENT ) {
73695584Sanholt#if MGA_DMA_DEBUG
73795584Sanholt		int ret = mga_do_wait_for_idle( dev_priv );
73895584Sanholt		if ( ret )
73995584Sanholt			DRM_INFO( __FUNCTION__": -EBUSY\n" );
74095584Sanholt		return ret;
74195584Sanholt#else
74295584Sanholt		return mga_do_wait_for_idle( dev_priv );
74395584Sanholt#endif
74495584Sanholt	} else {
74595584Sanholt		return 0;
74695584Sanholt	}
74795584Sanholt}
74895584Sanholt
74995584Sanholtint mga_dma_reset( DRM_OS_IOCTL )
75095584Sanholt{
75195584Sanholt	DRM_OS_DEVICE;
75295584Sanholt	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
75395584Sanholt
75495584Sanholt	LOCK_TEST_WITH_RETURN( dev );
75595584Sanholt
75695584Sanholt	return mga_do_dma_reset( dev_priv );
75795584Sanholt}
75895584Sanholt
75995584Sanholt
76095584Sanholt/* ================================================================
76195584Sanholt * DMA buffer management
76295584Sanholt */
76395584Sanholt
76495584Sanholt#if 0
76595584Sanholtstatic int mga_dma_get_buffers( drm_device_t *dev, drm_dma_t *d )
76695584Sanholt{
76795584Sanholt	drm_buf_t *buf;
76895584Sanholt	int i;
76995584Sanholt
77095584Sanholt	for ( i = d->granted_count ; i < d->request_count ; i++ ) {
77195584Sanholt		buf = mga_freelist_get( dev );
77295584Sanholt		if ( !buf )
77395693Sanholt			return DRM_OS_ERR(EAGAIN);
77495584Sanholt
77595584Sanholt		buf->pid = current->pid;
77695584Sanholt
77795584Sanholt		if ( DRM_OS_COPYTOUSR( &d->request_indices[i],
77895584Sanholt				   &buf->idx, sizeof(buf->idx) ) )
77995693Sanholt			return DRM_OS_ERR(EFAULT);
78095584Sanholt		if ( DRM_OS_COPYTOUSR( &d->request_sizes[i],
78195584Sanholt				   &buf->total, sizeof(buf->total) ) )
78295693Sanholt			return DRM_OS_ERR(EFAULT);
78395584Sanholt
78495584Sanholt		d->granted_count++;
78595584Sanholt	}
78695584Sanholt	return 0;
78795584Sanholt}
78895584Sanholt#endif /* 0 */
78995584Sanholt
79095584Sanholtint mga_dma_buffers( DRM_OS_IOCTL )
79195584Sanholt{
79295584Sanholt	DRM_OS_DEVICE;
79395584Sanholt	drm_device_dma_t *dma = dev->dma;
79495584Sanholt	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
79595584Sanholt	drm_dma_t d;
79695584Sanholt	drm_buf_t *buf;
79795584Sanholt	int i;
79895584Sanholt	int ret = 0;
79995584Sanholt
80095584Sanholt	LOCK_TEST_WITH_RETURN( dev );
80195584Sanholt
80295584Sanholt	DRM_OS_KRNFROMUSR( d, (drm_dma_t *) data, sizeof(d) );
80395584Sanholt
80495584Sanholt	/* Please don't send us buffers.
80595584Sanholt	 */
80695584Sanholt	if ( d.send_count != 0 ) {
80795584Sanholt		DRM_ERROR( "Process %d trying to send %d buffers via drmDMA\n",
80895584Sanholt			   DRM_OS_CURRENTPID, d.send_count );
80995693Sanholt		return DRM_OS_ERR(EINVAL);
81095584Sanholt	}
81195584Sanholt
81295584Sanholt	/* We'll send you buffers.
81395584Sanholt	 */
81495584Sanholt	if ( d.request_count < 0 || d.request_count > dma->buf_count ) {
81595584Sanholt		DRM_ERROR( "Process %d trying to get %d buffers (of %d max)\n",
81695584Sanholt			   DRM_OS_CURRENTPID, d.request_count, dma->buf_count );
81795693Sanholt		return DRM_OS_ERR(EINVAL);
81895584Sanholt	}
81995584Sanholt
82095584Sanholt	WRAP_TEST_WITH_RETURN( dev_priv );
82195584Sanholt
82295584Sanholt	d.granted_count = 0;
82395584Sanholt
82495584Sanholt	if ( d.request_count ) {
82595584Sanholt		for ( i = d.granted_count ; i < d.request_count ; i++ ) {
82695584Sanholt			buf = mga_freelist_get( dev );
82795584Sanholt			if ( !buf )
82895693Sanholt				return DRM_OS_ERR(EAGAIN);
82995584Sanholt
83095584Sanholt			buf->pid = DRM_OS_CURRENTPID;
83195584Sanholt
83295584Sanholt			if ( DRM_OS_COPYTOUSR( &d.request_indices[i],
83395584Sanholt					   &buf->idx, sizeof(buf->idx) ) )
83495693Sanholt				return DRM_OS_ERR(EFAULT);
83595584Sanholt			if ( DRM_OS_COPYTOUSR( &d.request_sizes[i],
83695584Sanholt					   &buf->total, sizeof(buf->total) ) )
83795693Sanholt				return DRM_OS_ERR(EFAULT);
83895584Sanholt
83995584Sanholt			d.granted_count++;
84095584Sanholt		}
84195584Sanholt		ret = 0;
84295584Sanholt	}
84395584Sanholt
84495584Sanholt	DRM_OS_KRNTOUSR( (drm_dma_t *) data, d, sizeof(d) );
84595584Sanholt
84695584Sanholt	return ret;
84795584Sanholt}
848