- interrupt-driven DMA framework written by Frank C. Earl (merged from

mach64-0-0-3-dma-branch)
- I've partly filled in the dma_dispatch implementation from the vertex
    dispatch code. We still need to deal with adding a register reset
    buffer to the end of the dma pass. The freelist and blits are also
    still to be filled in.
- I've added XF86Config options for the driver: ForcePCIMode - Don't use
    AGP for buffers/textures, even if agpgart is present PseudoDMAMode -
    Dispatch DMA buffers with MMIO, one register at a time. AgpMode - 1 or
    2 AgpSize - Size of AGP aperture to use for allocations BufferSize -
    Size of vertex buffers in MB (1 or 2)
This commit is contained in:
Leif Delgass 2002-05-08 05:32:52 +00:00
parent b53b0e0a04
commit c3e3c95731
8 changed files with 670 additions and 166 deletions

View file

@ -523,5 +523,7 @@ typedef struct drm_scatter_gather {
#define DRM_IOCTL_MACH64_SWAP DRM_IO( 0x43)
#define DRM_IOCTL_MACH64_CLEAR DRM_IOW( 0x44, drm_mach64_clear_t)
#define DRM_IOCTL_MACH64_VERTEX DRM_IOW( 0x45, drm_mach64_vertex_t)
#if 0
#define DRM_IOCTL_MACH64_BLIT DRM_IOW( 0x46, drm_mach64_blit_t)
#endif
#endif

View file

@ -25,6 +25,7 @@
*
* Authors:
* Gareth Hughes <gareth@valinux.com>
* Leif Delgass <ldelgass@retinalburn.net>
*/
#ifndef __MACH64_H__
@ -45,6 +46,77 @@
/* DMA customization:
*/
#define __HAVE_DMA 1
#define __HAVE_DMA_IRQ 1
#define __HAVE_DMA_IRQ_BH 1
#define __HAVE_SHARED_IRQ 1
/* called before installing service routine in _irq_install */
#define DRIVER_PREINSTALL() \
do { \
u32 tmp; \
drm_mach64_private_t *dev_priv = dev->dev_private; \
\
tmp = MACH64_READ(MACH64_CRTC_INT_CNTL); \
DRM_DEBUG("Before PREINSTALL: CRTC_INT_CNTL = 0x%08x\n", tmp); \
/* clear active interrupts */ \
if ( tmp & (MACH64_VBLANK_INT | MACH64_BUSMASTER_EOL_INT) ) { \
/* ack bits are the same as active interrupt bits, */ \
/* so write back tmp to clear active interrupts */ \
MACH64_WRITE( MACH64_CRTC_INT_CNTL, tmp ); \
} \
\
/* disable interrupts */ \
tmp &= ~(MACH64_VBLANK_INT_EN | MACH64_BUSMASTER_EOL_INT_EN); \
MACH64_WRITE( MACH64_CRTC_INT_CNTL, tmp ); \
DRM_DEBUG("After PREINSTALL: CRTC_INT_CNTL = 0x%08x\n", tmp); \
\
} while(0)
/* called after installing service routine in _irq_install */
#define DRIVER_POSTINSTALL() \
do { \
/* clear and enable interrupts */ \
u32 tmp; \
drm_mach64_private_t *dev_priv = dev->dev_private; \
\
tmp = MACH64_READ(MACH64_CRTC_INT_CNTL); \
DRM_DEBUG("Before POSTINSTALL: CRTC_INT_CNTL = 0x%08x\n", tmp); \
/* clear active interrupts */ \
if ( tmp & (MACH64_VBLANK_INT | MACH64_BUSMASTER_EOL_INT) ) { \
/* ack bits are the same as active interrupt bits, */ \
/* so write back tmp to clear active interrupts */ \
MACH64_WRITE( MACH64_CRTC_INT_CNTL, tmp ); \
} \
\
/* enable interrupts */ \
tmp |= MACH64_VBLANK_INT_EN | MACH64_BUSMASTER_EOL_INT_EN; \
MACH64_WRITE( MACH64_CRTC_INT_CNTL, tmp ); \
DRM_DEBUG("After POSTINSTALL: CRTC_INT_CNTL = 0x%08x\n", tmp); \
\
} while(0)
/* called before freeing irq in _irq_uninstall */
#define DRIVER_UNINSTALL() \
do { \
u32 tmp; \
drm_mach64_private_t *dev_priv = dev->dev_private; \
if (dev_priv) { \
tmp = MACH64_READ(MACH64_CRTC_INT_CNTL); \
DRM_DEBUG("Before UNINSTALL: CRTC_INT_CNTL = 0x%08x\n", tmp); \
/* clear active interrupts */ \
if ( tmp & (MACH64_VBLANK_INT | MACH64_BUSMASTER_EOL_INT) ) { \
/* ack bits are the same as active interrupt bits, */ \
/* so write back tmp to clear active interrupts */ \
MACH64_WRITE( MACH64_CRTC_INT_CNTL, tmp ); \
} \
\
/* disable interrupts */ \
tmp &= ~(MACH64_VBLANK_INT_EN | MACH64_BUSMASTER_EOL_INT_EN); \
MACH64_WRITE( MACH64_CRTC_INT_CNTL, tmp ); \
DRM_DEBUG("After UNINSTALL: CRTC_INT_CNTL = 0x%08x\n", tmp); \
} \
} while(0)
/* Buffer customization:
*/

View file

@ -2,6 +2,7 @@
* Created: Sun Dec 03 19:20:26 2000 by gareth@valinux.com
*
* Copyright 2000 Gareth Hughes
* Copyright 2002 Frank C. Earl
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@ -24,6 +25,8 @@
*
* Authors:
* Gareth Hughes <gareth@valinux.com>
* Frank C. Earl <fearl@airmail.net>
* Leif Delgass <ldelgass@retinalburn.net>
*/
#include "mach64.h"
@ -32,6 +35,94 @@
#include <linux/interrupt.h> /* For task queue support */
#include <linux/delay.h>
#include <linux/list.h>
int mach64_do_cleanup_dma( drm_device_t *dev );
int mach64_handle_dma( drm_mach64_private_t *dev_priv );
int mach64_do_dispatch_dma( drm_mach64_private_t *dev_priv );
int mach64_do_complete_blit( drm_mach64_private_t *dev_priv );
int mach64_do_wait_for_dma( drm_mach64_private_t *dev_priv );
int mach64_do_release_used_buffers( drm_mach64_private_t *dev_priv );
int mach64_init_freelist( drm_device_t *dev );
static DECLARE_WAIT_QUEUE_HEAD(read_wait);
/* ================================================================
* Interrupt handler
*/
void mach64_dma_service(int irq, void *device, struct pt_regs *regs)
{
drm_device_t *dev = (drm_device_t *) device;
drm_mach64_private_t *dev_priv = (drm_mach64_private_t *)dev->dev_private;
unsigned int flags;
/* Check to see if we've been interrupted for VBLANK or the BLIT completion
and ack the interrupt accordingly... Set flags for the handler to
know that it needs to process accordingly... */
flags = MACH64_READ(MACH64_CRTC_INT_CNTL);
if (flags & MACH64_VBLANK_INT)
{
/* VBLANK -- GUI-master dispatch and polling... */
MACH64_WRITE(MACH64_CRTC_INT_CNTL, flags | MACH64_VBLANK_INT_AK);
atomic_inc(&dev_priv->do_gui);
}
if (flags & MACH64_BUSMASTER_EOL_INT)
{
/* Completion of BLIT op */
MACH64_WRITE(MACH64_CRTC_INT_CNTL, flags | MACH64_BUSMASTER_EOL_INT_AK);
atomic_inc(&dev_priv->do_blit);
}
/* Check for an error condition in the engine... */
if (MACH64_READ(MACH64_FIFO_STAT) & 0x80000000)
{
/* This would be a failure to maintain FIFO discipline
per the SDK sources. Need to reset... */
mach64_do_engine_reset(dev_priv);
}
#if 0
/* According to reg. ref this bit is BUS_MSTR_RD_LINE and on my
* card (LT Pro), it's set by default (LLD)
*/
if (MACH64_READ(MACH64_BUS_CNTL) & 0x00200000)
{
/* This would be a host data error, per information from
Vernon Chiang @ ATI (Thanks, Vernon!). Need to reset... */
mach64_do_engine_reset(dev_priv);
}
#endif
/* Ok, now that we've gotten that out of the way, schedule the bottom half accordingly... */
queue_task(&dev->tq, &tq_immediate);
mark_bh(IMMEDIATE_BH);
return;
}
/* Handle the DMA dispatch/completion */
void mach64_dma_immediate_bh(void *device)
{
drm_device_t *dev = (drm_device_t *) device;
drm_mach64_private_t *dev_priv = (drm_mach64_private_t *)dev->dev_private;
/* Handle the completion of a blit pass... */
if (atomic_read(&dev_priv->do_blit) > 0)
{
atomic_set(&dev_priv->do_blit, 0);
/* mach64_do_complete_blit(dev_priv); */
}
/* Check to see if we've been told to handle gui-mastering... */
if (atomic_read(&dev_priv->do_gui) > 0)
{
atomic_set(&dev_priv->do_gui, 0);
/* mach64_handle_dma(dev_priv); */
}
wake_up_interruptible(&read_wait);
return;
}
/* ================================================================
@ -72,44 +163,60 @@ int mach64_do_wait_for_idle( drm_mach64_private_t *dev_priv )
return -EBUSY;
}
/* Wait until all DMA requests have been processed... */
int mach64_do_wait_for_dma( drm_mach64_private_t *dev_priv )
{
int i, ret;
/* ================================================================
* DMA initialization, cleanup
*/
/* Assume we timeout... */
ret = -EBUSY;
for ( i = 0 ; i < dev_priv->usec_timeout; i++ )
{
if ( list_empty(&dev_priv->dma_queue) )
{
ret = mach64_do_wait_for_idle( dev_priv );
break;
}
udelay( 1 );
}
if (ret != 0)
DRM_INFO( "do_wait_for_dma failed! GUI_STAT=0x%08x\n", MACH64_READ( MACH64_GUI_STAT ) );
return ret;
}
/* Reset the engine. This will stop the DMA if it is running.
*/
int mach64_do_engine_reset( drm_device_t *dev )
int mach64_do_engine_reset( drm_mach64_private_t *dev_priv )
{
drm_mach64_private_t *dev_priv = dev->dev_private;
u32 bus_cntl, gen_test_cntl;
u32 tmp;
DRM_DEBUG( "%s\n", __FUNCTION__ );
/* Kill off any outstanding DMA transfers.
*/
bus_cntl = MACH64_READ( MACH64_BUS_CNTL );
tmp = MACH64_READ( MACH64_BUS_CNTL );
MACH64_WRITE( MACH64_BUS_CNTL,
bus_cntl | MACH64_BUS_MASTER_DIS );
tmp | MACH64_BUS_MASTER_DIS );
/* Reset the GUI engine (high to low transition).
*/
gen_test_cntl = MACH64_READ( MACH64_GEN_TEST_CNTL );
tmp = MACH64_READ( MACH64_GEN_TEST_CNTL );
MACH64_WRITE( MACH64_GEN_TEST_CNTL,
gen_test_cntl & ~MACH64_GUI_ENGINE_ENABLE );
tmp & ~MACH64_GUI_ENGINE_ENABLE );
/* Enable the GUI engine
*/
gen_test_cntl = MACH64_READ( MACH64_GEN_TEST_CNTL );
tmp = MACH64_READ( MACH64_GEN_TEST_CNTL );
MACH64_WRITE( MACH64_GEN_TEST_CNTL,
gen_test_cntl | MACH64_GUI_ENGINE_ENABLE );
tmp | MACH64_GUI_ENGINE_ENABLE );
/* ensure engine is not locked up by clearing any FIFO or HOST errors
*/
bus_cntl = MACH64_READ( MACH64_BUS_CNTL );
MACH64_WRITE( MACH64_BUS_CNTL, bus_cntl | 0x00a00000 );
tmp = MACH64_READ( MACH64_BUS_CNTL );
MACH64_WRITE( MACH64_BUS_CNTL, tmp | 0x00a00000 );
return 0;
}
@ -190,6 +297,9 @@ void mach64_dump_engine_info( drm_mach64_private_t *dev_priv )
DRM_INFO( "\n" );
}
/* ================================================================
* DMA test and initialization
*/
static int mach64_bm_dma_test( drm_device_t *dev )
{
@ -199,6 +309,7 @@ static int mach64_bm_dma_test( drm_device_t *dev )
u32 data_addr;
u32 *table, *data;
u32 regs[3], expected[3];
u32 src_cntl;
int i;
DRM_DEBUG( "%s\n", __FUNCTION__ );
@ -216,7 +327,9 @@ static int mach64_bm_dma_test( drm_device_t *dev )
data_addr = (u32) data_handle;
}
MACH64_WRITE( MACH64_SRC_CNTL, 0x00000000 );
src_cntl = MACH64_READ( MACH64_SRC_CNTL );
src_cntl &= ~MACH64_SRC_BM_ENABLE;
MACH64_WRITE( MACH64_SRC_CNTL, src_cntl );
MACH64_WRITE( MACH64_VERTEX_1_S, 0x00000000 );
MACH64_WRITE( MACH64_VERTEX_1_T, 0x00000000 );
@ -234,7 +347,7 @@ static int mach64_bm_dma_test( drm_device_t *dev )
data[2] = expected[1] = 0x22222222;
data[3] = expected[2] = 0x33333333;
data[4] = cpu_to_le32(0x0000006d); /* SRC_CNTL */
data[5] = 0x00000000;
data[5] = cpu_to_le32(src_cntl);
DRM_DEBUG( "Preparing table ...\n" );
table[0] = cpu_to_le32(MACH64_BM_ADDR + APERTURE_OFFSET);
@ -257,7 +370,7 @@ static int mach64_bm_dma_test( drm_device_t *dev )
if ( ( i = mach64_do_wait_for_idle( dev_priv ) ) ) {
DRM_INFO( "mach64_do_wait_for_idle failed (result=%d)\n", i);
DRM_INFO( "resetting engine ...\n");
mach64_do_engine_reset( dev );
mach64_do_engine_reset( dev_priv );
DRM_INFO( "freeing data buffer memory.\n" );
pci_pool_free( dev_priv->pool, cpu_addr_data, data_handle );
DRM_INFO( "returning ...\n" );
@ -290,7 +403,7 @@ static int mach64_bm_dma_test( drm_device_t *dev )
DRM_INFO( "mach64_do_wait_for_idle failed (result=%d)\n", i);
mach64_dump_engine_info( dev_priv );
DRM_INFO( "resetting engine ...\n");
mach64_do_engine_reset( dev );
mach64_do_engine_reset( dev_priv );
DRM_INFO( "freeing data buffer memory.\n" );
pci_pool_free( dev_priv->pool, cpu_addr_data, data_handle );
DRM_INFO( "returning ...\n" );
@ -410,94 +523,329 @@ static int mach64_do_dma_init( drm_device_t *dev, drm_mach64_init_t *init )
}
}
#if MACH64_USE_DMA
/* enable block 1 registers and bus mastering */
MACH64_WRITE( MACH64_BUS_CNTL,
( ( MACH64_READ(MACH64_BUS_CNTL)
| MACH64_BUS_EXT_REG_EN )
& ~MACH64_BUS_MASTER_DIS ) );
if ( !init->pseudo_dma ) {
/* enable block 1 registers and bus mastering */
MACH64_WRITE( MACH64_BUS_CNTL,
( ( MACH64_READ(MACH64_BUS_CNTL)
| MACH64_BUS_EXT_REG_EN )
& ~MACH64_BUS_MASTER_DIS ) );
/* changing the FIFO size from the default seems to cause problems with DMA */
tmp = MACH64_READ( MACH64_GUI_CNTL );
if ( (tmp & MACH64_CMDFIFO_SIZE_MASK) != MACH64_CMDFIFO_SIZE_128 ) {
DRM_INFO( "Setting FIFO size to 128 entries\n");
/* FIFO must be empty to change the FIFO depth */
if ((ret=mach64_do_wait_for_idle( dev_priv ))) {
dev->dev_private = (void *)dev_priv;
mach64_do_cleanup_dma( dev );
DRM_ERROR("wait for idle failed before changing FIFO depth!\n");
return ret;
/* changing the FIFO size from the default seems to cause problems with DMA */
tmp = MACH64_READ( MACH64_GUI_CNTL );
if ( (tmp & MACH64_CMDFIFO_SIZE_MASK) != MACH64_CMDFIFO_SIZE_128 ) {
DRM_INFO( "Setting FIFO size to 128 entries\n");
/* FIFO must be empty to change the FIFO depth */
if ((ret=mach64_do_wait_for_idle( dev_priv ))) {
dev->dev_private = (void *)dev_priv;
mach64_do_cleanup_dma( dev );
DRM_ERROR("wait for idle failed before changing FIFO depth!\n");
return ret;
}
MACH64_WRITE( MACH64_GUI_CNTL, ( ( tmp & ~MACH64_CMDFIFO_SIZE_MASK ) \
| MACH64_CMDFIFO_SIZE_128 ) );
/* need to read GUI_STAT for proper sync according to register reference */
if ((ret=mach64_do_wait_for_idle( dev_priv ))) {
dev->dev_private = (void *)dev_priv;
mach64_do_cleanup_dma( dev );
DRM_ERROR("wait for idle failed when changing FIFO depth!\n");
return ret;
}
}
MACH64_WRITE( MACH64_GUI_CNTL, ( ( tmp & ~MACH64_CMDFIFO_SIZE_MASK ) \
| MACH64_CMDFIFO_SIZE_128 ) );
/* need to read GUI_STAT for proper sync according to register reference */
if ((ret=mach64_do_wait_for_idle( dev_priv ))) {
dev->dev_private = (void *)dev_priv;
mach64_do_cleanup_dma( dev );
DRM_ERROR("wait for idle failed when changing FIFO depth!\n");
return ret;
/* create pci pool for descriptor memory */
DRM_INFO( "Creating pci pool\n");
dev_priv->pool = pci_pool_create( "mach64", /* name */
NULL, /* dev */
0x4000, /* size - 16KB */
0x4000, /* align - 16KB */
0x4000, /* alloc - 16KB */
SLAB_ATOMIC /* flags */
);
if (!dev_priv->pool) {
dev_priv->driver_mode = MACH64_MODE_MMIO;
DRM_INFO( "pci_pool_create failed, using pseudo-DMA mode\n");
dev->dev_private = (void *) dev_priv;
return 0;
}
}
/* create pci pool for descriptor memory */
DRM_INFO( "Creating pci pool\n");
dev_priv->pool = pci_pool_create( "mach64", /* name */
NULL, /* dev */
0x4000, /* size - 16KB */
0x4000, /* align - 16KB */
0x4000, /* alloc - 16KB */
SLAB_ATOMIC /* flags */
);
/* allocate descriptor memory from pci pool */
DRM_INFO( "Allocating descriptor table memory\n" );
dev_priv->cpu_addr_table = pci_pool_alloc( dev_priv->pool, SLAB_ATOMIC,
&dev_priv->table_handle );
if (!dev_priv->cpu_addr_table || !dev_priv->table_handle) {
pci_pool_destroy( dev_priv->pool );
dev_priv->driver_mode = MACH64_MODE_MMIO;
DRM_INFO( "pci_pool_alloc failed, using pseudo-DMA mode\n");
dev->dev_private = (void *) dev_priv;
return 0;
} else {
dev_priv->table_addr = (u32) dev_priv->table_handle;
memset( dev_priv->cpu_addr_table, 0x0, 0x4000 );
}
if (!dev_priv->pool) {
dev_priv->driver_mode = MACH64_MODE_MMIO;
DRM_INFO( "pci_pool_create failed, using MMIO mode\n");
DRM_INFO( "descriptor table: cpu addr: 0x%08x, bus addr: 0x%08x\n",
(u32) dev_priv->cpu_addr_table, dev_priv->table_addr );
/* setup physical address and size of descriptor table */
MACH64_WRITE( MACH64_BM_GUI_TABLE_CMD,
( dev_priv->table_addr | MACH64_CIRCULAR_BUF_SIZE_16KB ) );
/* try a DMA GUI-mastering pass and fall back to MMIO if it fails */
dev->dev_private = (void *) dev_priv;
return 0;
}
/* allocate descriptor memory from pci pool */
DRM_INFO( "Allocating descriptor table memory\n" );
dev_priv->cpu_addr_table = pci_pool_alloc( dev_priv->pool, SLAB_ATOMIC,
&dev_priv->table_handle );
if (!dev_priv->cpu_addr_table || !dev_priv->table_handle) {
pci_pool_destroy( dev_priv->pool );
dev_priv->driver_mode = MACH64_MODE_MMIO;
DRM_INFO( "pci_pool_alloc failed, using MMIO mode\n");
dev->dev_private = (void *) dev_priv;
return 0;
} else {
dev_priv->table_addr = (u32) dev_priv->table_handle;
memset( dev_priv->cpu_addr_table, 0x0, 0x4000 );
}
DRM_INFO( "descriptor table: cpu addr: 0x%08x, bus addr: 0x%08x\n",
(u32) dev_priv->cpu_addr_table, dev_priv->table_addr );
/* setup physical address and size of descriptor table */
MACH64_WRITE( MACH64_BM_GUI_TABLE_CMD,
( dev_priv->table_addr | MACH64_CIRCULAR_BUF_SIZE_16KB ) );
/* try a DMA GUI-mastering pass and fall back to MMIO if it fails */
dev->dev_private = (void *) dev_priv;
DRM_INFO( "Starting DMA test...\n");
if ( (ret=mach64_bm_dma_test( dev )) == 0 ) {
dev_priv->driver_mode = MACH64_MODE_DMA_SYNC;
DRM_INFO( "DMA test succeeded, using synchronous DMA mode\n");
DRM_INFO( "Starting DMA test...\n");
if ( (ret=mach64_bm_dma_test( dev )) == 0 ) {
dev_priv->driver_mode = MACH64_MODE_DMA_SYNC;
DRM_INFO( "DMA test succeeded, using synchronous DMA mode\n");
} else {
dev_priv->driver_mode = MACH64_MODE_MMIO;
DRM_INFO( "DMA test failed (ret=%d), using pseudo-DMA mode\n", ret );
}
} else {
dev_priv->driver_mode = MACH64_MODE_MMIO;
DRM_INFO( "DMA test failed (ret=%d), using MMIO mode\n", ret );
DRM_INFO( "Forcing pseudo-DMA mode\n");
}
#else
dev_priv->driver_mode = MACH64_MODE_MMIO;
DRM_INFO( "Using MMIO mode\n");
#endif
/* Set up the freelist, empty (placeholder), pending, and DMA request queues... */
INIT_LIST_HEAD(&dev_priv->free_list);
INIT_LIST_HEAD(&dev_priv->empty_list);
INIT_LIST_HEAD(&dev_priv->pending);
INIT_LIST_HEAD(&dev_priv->dma_queue);
mach64_init_freelist( dev );
/* Set up for interrupt handling proper- clear state on the handler
* The handler is enabled by the DDX via the DRM(control) ioctl once we return */
atomic_set(&dev_priv->do_gui, 0);
atomic_set(&dev_priv->do_blit, 0);
atomic_set(&dev_priv->dma_timeout, -1);
dev->dev_private = (void *) dev_priv;
return 0;
}
/* ================================================================
* Primary DMA stream management
*/
/*
Manage the GUI-Mastering operations of the chip. Since the GUI-Master
operation is slightly less intelligent than the BLIT operation (no interrupt
for completion), we have to provide the completion detection, etc. in
a state engine.
*/
int mach64_handle_dma( drm_mach64_private_t *dev_priv )
{
struct list_head *ptr;
int i;
int timeout;
timeout = atomic_read(&dev_priv->dma_timeout);
/* Check for engine idle... */
if (!(MACH64_READ(MACH64_GUI_STAT) & MACH64_GUI_ACTIVE))
{
/* Check to see if we had a DMA pass going... */
if ( timeout > -1)
{
/* Ok, do the clean up for the previous pass... */
mach64_do_release_used_buffers(dev_priv);
atomic_set(&dev_priv->dma_timeout, -1);
}
/* Now, check for queued buffers... */
if (!list_empty(&dev_priv->dma_queue))
{
ptr = dev_priv->dma_queue.next;
for(i = 0; i < MACH64_DMA_SIZE && !list_empty(&dev_priv->dma_queue); i++)
{
/* FIXME -- We REALLY need to be doing this based off of not just
a DMA-able size that's tolerable, but also rounding up/down by
what was submitted to us- if the client's submitting 3 buffer
submits, we really want to push all three at the same time to
the DMA channel. */
list_del(ptr);
list_add_tail(ptr, &dev_priv->pending);
}
atomic_set(&dev_priv->dma_timeout, 0);
}
/* Check to see if we've got a DMA pass set up */
if (atomic_read(&dev_priv->dma_timeout) == 0)
{
/* Make sure we're locked and fire off the prepped pass */
mach64_do_dispatch_dma(dev_priv);
}
}
else
{
/* Check to see if we've got a GUI-Master going... */
if ((timeout > -1) && !(MACH64_READ( MACH64_BUS_CNTL ) & MACH64_BUS_MASTER_DIS))
{
/* Check for DMA timeout */
if (timeout > MACH64_DMA_TIMEOUT)
{
/* Assume the engine's hung bigtime... */
mach64_do_engine_reset(dev_priv);
mach64_do_release_used_buffers(dev_priv);
atomic_set(&dev_priv->dma_timeout, -1);
}
else
{
atomic_inc(&dev_priv->dma_timeout);
}
}
}
return 0;
}
/*
Perform the clean-up for the blit operation- turn off DMA
operation (not support) and unlock the DRM.
*/
int mach64_do_complete_blit( drm_mach64_private_t *dev_priv )
{
/* Turn off DMA mode -- we don't have anything going because the chip
tells us that it completed in this case (Why didn't they do this for
GUI Master operation?!) */
MACH64_WRITE( MACH64_BUS_CNTL, MACH64_READ( MACH64_BUS_CNTL ) | MACH64_BUS_MASTER_DIS );
return 0;
}
/*
Take the pending list and build up a descriptor table for
GUI-Master use, then fire off the DMA engine with the list.
(The list includes a register reset buffer that the DRM
only controls)
*/
/* FIXME: need to add commands to terminate DMA at the end of the stream */
int mach64_do_dispatch_dma( drm_mach64_private_t *dev_priv )
{
u32 *table_ptr = (u32 *) dev_priv->cpu_addr_table;
struct list_head *ptr;
drm_mach64_freelist_t *entry;
drm_buf_t *buf;
int size, i, pages, remainder, tableDwords;
u32 address, page, end_flag;
u32 *p;
tableDwords = 0;
/* Iterate the pending list build a descriptor table accordingly... */
list_for_each(ptr, &dev_priv->pending)
{
entry = list_entry(ptr, drm_mach64_freelist_t, list);
buf = entry->buf;
size = buf->used;
if (dev_priv->is_pci) {
address = (u32) virt_to_bus((void *)buf->address);
p = (u32 *) buf->address;
} else {
address = (u32) buf->bus_address;
p = (u32 *)((char *)dev_priv->buffers->handle +
buf->offset);
}
pages = (size + DMA_CHUNKSIZE - 1) / DMA_CHUNKSIZE;
for ( i = 0 ; i < pages-1 ; i++ ) {
page = address + i * DMA_CHUNKSIZE;
table_ptr[DMA_FRAME_BUF_OFFSET] = cpu_to_le32(MACH64_BM_ADDR +
APERTURE_OFFSET);
table_ptr[DMA_SYS_MEM_ADDR] = cpu_to_le32(page);
table_ptr[DMA_COMMAND] = cpu_to_le32(DMA_CHUNKSIZE | 0x40000000);
table_ptr[DMA_RESERVED] = 0;
tableDwords += 4;
table_ptr += 4;
}
/* if this is the last buffer, we need to set the final descriptor flag */
end_flag = (ptr->next == &dev_priv->pending) ? 0x80000000 : 0;
/* generate the final descriptor for any remaining commands in this buffer */
page = address + i * DMA_CHUNKSIZE;
remainder = size - i * DMA_CHUNKSIZE;
table_ptr[DMA_FRAME_BUF_OFFSET] = cpu_to_le32(MACH64_BM_ADDR + APERTURE_OFFSET);
table_ptr[DMA_SYS_MEM_ADDR] = cpu_to_le32(page);
table_ptr[DMA_COMMAND] = cpu_to_le32(remainder | end_flag | 0x40000000);
table_ptr[DMA_RESERVED] = 0;
tableDwords += 4;
table_ptr += 4;
}
/* Now, dispatch the whole lot to the gui-master engine */
/* flush write combining */
mach64_flush_write_combine();
mach64_do_wait_for_idle( dev_priv );
/* enable bus mastering */
MACH64_WRITE( MACH64_BUS_CNTL,
( MACH64_READ(MACH64_BUS_CNTL)
& ~MACH64_BUS_MASTER_DIS ) );
/* enable VBLANK interrupt */
MACH64_WRITE( MACH64_CRTC_INT_CNTL, MACH64_READ(MACH64_CRTC_INT_CNTL) |
MACH64_VBLANK_INT_EN);
/* reset descriptor table head */
MACH64_WRITE( MACH64_BM_GUI_TABLE_CMD, ( dev_priv->table_addr
| MACH64_CIRCULAR_BUF_SIZE_16KB ) );
/* enable GUI-master operation */
MACH64_WRITE( MACH64_SRC_CNTL,
MACH64_SRC_BM_ENABLE | MACH64_SRC_BM_SYNC |
MACH64_SRC_BM_OP_SYSTEM_TO_REG );
/* kick off the transfer */
MACH64_WRITE( MACH64_DST_HEIGHT_WIDTH, 0 );
#if 0
if ( dev_priv->driver_mode == MACH64_MODE_DMA_SYNC )
if ( mach64_do_wait_for_idle( dev_priv ) ) {
DRM_INFO( "mach64_do_wait_for_idle failed\n" );
DRM_INFO( "resetting engine ...\n" );
mach64_dump_engine_info( dev_priv );
mach64_do_engine_reset( dev_priv );
return -EBUSY;
}
}
#endif
return 0;
}
/*
Release completed, releaseable buffers to the freelist, currently
ignore flags for buffers that aren't flagged for release (shouldn't
be any, but you never know what someone's going to do to us...).
*/
int mach64_do_release_used_buffers( drm_mach64_private_t *dev_priv )
{
struct list_head *ptr;
struct list_head *tmp;
/* Iterate the pending list and shove the whole lot into the freelist... */
list_for_each_safe(ptr, tmp, &dev_priv->pending)
{
list_del(ptr);
list_add_tail(ptr, &dev_priv->free_list);
}
return 0;
}
/* ================================================================
* DMA cleanup
*/
int mach64_do_cleanup_dma( drm_device_t *dev )
{
DRM_DEBUG( "%s\n", __FUNCTION__ );
@ -505,13 +853,14 @@ int mach64_do_cleanup_dma( drm_device_t *dev )
if ( dev->dev_private ) {
drm_mach64_private_t *dev_priv = dev->dev_private;
/* Discard the allocations for the descriptor table... */
if ( (dev_priv->pool != NULL) &&
(dev_priv->cpu_addr_table != NULL) && dev_priv->table_handle ) {
DRM_INFO( "freeing descriptor table from pci pool\n" );
pci_pool_free( dev_priv->pool, dev_priv->cpu_addr_table,
dev_priv->table_handle );
}
if ( dev_priv->pool ) {
if ( dev_priv->pool != NULL ) {
DRM_INFO( "destroying pci pool\n" );
pci_pool_destroy( dev_priv->pool );
}
@ -528,6 +877,10 @@ int mach64_do_cleanup_dma( drm_device_t *dev )
return 0;
}
/* ================================================================
* IOCTL handlers
*/
int mach64_dma_init( struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg )
{
@ -569,25 +922,27 @@ int mach64_engine_reset( struct inode *inode, struct file *filp,
{
drm_file_t *priv = filp->private_data;
drm_device_t *dev = priv->dev;
drm_mach64_private_t *dev_priv = dev->dev_private;
DRM_DEBUG( "%s\n", __FUNCTION__ );
LOCK_TEST_WITH_RETURN( dev );
return mach64_do_engine_reset( dev );
return mach64_do_engine_reset( dev_priv );
}
/* ================================================================
* Primary DMA stream management
*/
/* ================================================================
* Freelist management
*/
#define MACH64_BUFFER_USED 0xffffffff
#define MACH64_BUFFER_FREE 0
int mach64_init_freelist( drm_device_t *dev )
{
return 0;
}
drm_buf_t *mach64_freelist_get( drm_device_t *dev )
{
drm_device_dma_t *dma = dev->dma;
@ -642,10 +997,9 @@ void mach64_freelist_reset( drm_device_t *dev )
/* ================================================================
* DMA command submission
* DMA buffer request and submission IOCTL handler
*/
static int mach64_dma_get_buffers( drm_device_t *dev, drm_dma_t *d )
{
int i;
@ -669,44 +1023,85 @@ static int mach64_dma_get_buffers( drm_device_t *dev, drm_dma_t *d )
return 0;
}
/*
Through some pretty thorough testing, it has been found that the
RagePRO engine will pretty much ignore any "commands" sent
via the gui-master pathway that aren't gui operations (the register
gets set, but the actions that are normally associated with the setting
of those said registers doesn't happen.). So, it's safe to send us
buffers of gui commands from userspace (altering the buffer in mid-
execution will at worst scribble all over the screen and pushing
bogus commands will have no apparent effect...)
FCE (03-08-2002)
*/
int mach64_dma_buffers( struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg )
unsigned int cmd, unsigned long arg )
{
drm_file_t *priv = filp->private_data;
drm_device_t *dev = priv->dev;
drm_device_dma_t *dma = dev->dma;
int ret = 0;
drm_file_t *priv = filp->private_data;
drm_device_t *dev = priv->dev;
drm_mach64_private_t *dev_priv = dev->dev_private;
drm_device_dma_t *dma = dev->dma;
struct list_head *ptr;
drm_mach64_freelist_t *entry;
drm_dma_t d;
int ret = 0;
int i;
LOCK_TEST_WITH_RETURN( dev );
LOCK_TEST_WITH_RETURN( dev );
if ( copy_from_user( &d, (drm_dma_t *) arg, sizeof(d) ) )
return -EFAULT;
if ( copy_from_user( &d, (drm_dma_t *)arg, sizeof(d) ) )
{
return -EFAULT;
}
/* Please don't send us buffers.
*/
if ( d.send_count != 0 ) {
DRM_ERROR( "Process %d trying to send %d buffers via drmDMA\n",
current->pid, d.send_count );
return -EINVAL;
}
/* Queue up buffers sent to us...
*/
if ( d.send_count > 0 )
{
for (i = 0; i < d.send_count ; i++)
{
if (!list_empty(&dev_priv->empty_list))
{
ptr = dev_priv->empty_list.next;
list_del(ptr);
entry = list_entry(ptr, drm_mach64_freelist_t, list);
entry->buf = dma->buflist[d.send_indices[i]];
list_add_tail(ptr, &dev_priv->dma_queue);
}
else
{
return -EFAULT;
}
}
}
else
{
/* Send the caller as many as they ask for, so long as we
have them in hand to give...
*/
if ( d.request_count < 0 || d.request_count > dma->buf_count )
{
DRM_ERROR( "Process %d trying to get %d buffers (of %d max)\n",
current->pid, d.request_count, dma->buf_count );
ret = -EINVAL;
}
else
{
d.granted_count = 0;
/* 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",
current->pid, d.request_count, dma->buf_count );
return -EINVAL;
}
if ( d.request_count )
{
ret = mach64_dma_get_buffers( dev, &d );
}
d.granted_count = 0;
if ( d.request_count ) {
ret = mach64_dma_get_buffers( dev, &d );
}
if ( copy_to_user( (drm_dma_t *) arg, &d, sizeof(d) ) )
return -EFAULT;
return ret;
if ( copy_to_user( (drm_dma_t *)arg, &d, sizeof(d) ) )
{
ret = -EFAULT;
}
}
}
return ret;
}

View file

@ -2,6 +2,7 @@
* Created: Thu Nov 30 20:04:32 2000 by gareth@valinux.com
*
* Copyright 2000 Gareth Hughes
* Copyright 2002 Frank C. Earl
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@ -24,6 +25,8 @@
*
* Authors:
* Gareth Hughes <gareth@valinux.com>
* Frank C. Earl <fearl@airmail.net>
* Leif Delgass <ldelgass@retinalburn.net>
*/
#ifndef __MACH64_DRM_H__
@ -61,13 +64,20 @@
#define MACH64_BACK 0x2
#define MACH64_DEPTH 0x4
/* Keep these small for testing.
*/
#define MACH64_NR_SAREA_CLIPRECTS 8
/* WARNING: If you change any of these defines, make sure to change the
* defines in the Xserver file (mach64_sarea.h)
*/
/* DMA buffer size
*/
#define MACH64_BUFFER_SIZE 16384
/* Keep these small for testing.
*/
#define MACH64_NR_SAREA_CLIPRECTS 8
#define MACH64_CARD_HEAP 0
#define MACH64_AGP_HEAP 1
#define MACH64_NR_TEX_HEAPS 2
@ -76,6 +86,9 @@
#define MACH64_TEX_MAXLEVELS 1
#define MACH64_NR_CONTEXT_REGS 15
#define MACH64_NR_TEXTURE_REGS 4
#endif /* __MACH64_SAREA_DEFINES__ */
typedef struct {
@ -149,6 +162,7 @@ typedef struct drm_mach64_init {
unsigned int sarea_priv_offset;
int is_pci;
int pseudo_dma;
unsigned int fb_bpp;
unsigned int front_offset, front_pitch;
@ -177,5 +191,14 @@ typedef struct drm_mach64_vertex {
int count; /* Number of vertices in buffer */
int discard; /* Client finished with buffer? */
} drm_mach64_vertex_t;
#if 0
typedef struct drm_mach64_blit {
int idx;
int pitch;
int offset;
int format;
unsigned short x, y;
unsigned short width, height;
} drm_mach64_blit_t;
#endif
#endif

View file

@ -2,6 +2,7 @@
* Created: Fri Nov 24 22:07:58 2000 by gareth@valinux.com
*
* Copyright 2000 Gareth Hughes
* Copyright 2002 Frank C. Earl
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@ -24,25 +25,23 @@
*
* Authors:
* Gareth Hughes <gareth@valinux.com>
* Frank C. Earl <fearl@airmail.net>
* Leif Delgass <ldelgass@retinalburn.net>
*/
#ifndef __MACH64_DRV_H__
#define __MACH64_DRV_H__
#include <linux/list.h>
typedef struct drm_mach64_freelist {
unsigned int age;
struct list_head list; /* Linux LIST structure... */
drm_buf_t *buf;
struct drm_mach64_freelist *next;
struct drm_mach64_freelist *prev;
} drm_mach64_freelist_t;
typedef struct drm_mach64_private {
drm_mach64_sarea_t *sarea_priv;
drm_mach64_freelist_t *head;
drm_mach64_freelist_t *tail;
int usec_timeout;
int is_pci;
enum {
MACH64_MODE_MMIO,
@ -50,11 +49,6 @@ typedef struct drm_mach64_private {
MACH64_MODE_DMA_ASYNC
} driver_mode;
struct pci_pool *pool;
dma_addr_t table_handle;
void *cpu_addr_table;
u32 table_addr;
unsigned int fb_bpp;
unsigned int front_offset, front_pitch;
unsigned int back_offset, back_pitch;
@ -66,6 +60,21 @@ typedef struct drm_mach64_private {
u32 back_offset_pitch;
u32 depth_offset_pitch;
int usec_timeout; /* Number of microseconds to wait for a timeout on the idle functions */
atomic_t dma_timeout; /* Number of interrupt dispatches since last DMA dispatch... */
atomic_t do_gui; /* Flag for the bottom half to know what to do... */
atomic_t do_blit; /* Flag for the bottom half to know what to do... */
struct pci_pool *pool;
dma_addr_t table_handle;
void *cpu_addr_table;
u32 table_addr;
struct list_head free_list; /* Free-list head */
struct list_head empty_list; /* Free-list placeholder list */
struct list_head pending; /* Pending submission placeholder */
struct list_head dma_queue; /* Submission queue head */
drm_map_t *sarea;
drm_map_t *fb;
drm_map_t *mmio;
@ -98,8 +107,7 @@ extern int mach64_do_wait_for_fifo( drm_mach64_private_t *dev_priv,
int entries );
extern int mach64_do_wait_for_idle( drm_mach64_private_t *dev_priv );
extern void mach64_dump_engine_info( drm_mach64_private_t *dev_priv );
extern int mach64_do_engine_reset( drm_device_t *dev );
extern int mach64_do_cleanup_dma( drm_device_t *dev );
extern int mach64_do_engine_reset( drm_mach64_private_t *dev_priv );
/* mach64_state.c */
extern int mach64_dma_clear( struct inode *inode, struct file *filp,
@ -338,7 +346,13 @@ extern int mach64_dma_vertex( struct inode *inode, struct file *filp,
#define MACH64_Z_CNTL 0x054c
#define MACH64_Z_OFF_PITCH 0x0548
#define MACH64_CRTC_INT_CNTL 0x0418
# define MACH64_VBLANK_INT_EN (1 << 1)
# define MACH64_VBLANK_INT (1 << 2)
# define MACH64_VBLANK_INT_AK (1 << 2)
# define MACH64_BUSMASTER_EOL_INT_EN (1 << 24)
# define MACH64_BUSMASTER_EOL_INT (1 << 25)
# define MACH64_BUSMASTER_EOL_INT_AK (1 << 25)
#define MACH64_DATATYPE_CI8 2
#define MACH64_DATATYPE_ARGB1555 3
@ -348,12 +362,6 @@ extern int mach64_dma_vertex( struct inode *inode, struct file *filp,
#define MACH64_DATATYPE_RGB8 9
#define MACH64_DATATYPE_ARGB4444 15
#define MACH64_CRTC_INT_CNTL 0x0418
/* Constants */
#define MACH64_LAST_FRAME_REG MACH64_SCRATCH_REG0
#define MACH64_LAST_DISPATCH_REG MACH64_SCRATCH_REG1
#define MACH64_BASE(reg) ((u32)(dev_priv->mmio->handle))
@ -411,14 +419,14 @@ do { \
* DMA macros
*/
#define MACH64_USE_DMA 1
#define DMA_FRAME_BUF_OFFSET 0
#define DMA_SYS_MEM_ADDR 1
#define DMA_COMMAND 2
#define DMA_RESERVED 3
#define DMA_CHUNKSIZE 0x1000
#define MACH64_DMA_TIMEOUT 10 /* 10 vertical retraces should be enough */
#define MACH64_DMA_SIZE 64 /* 1 MB (64*16kB) should be enough */
#define DMA_CHUNKSIZE 0x1000 /* 4kB per DMA descriptor */
#define APERTURE_OFFSET 0x7ff800

View file

@ -508,7 +508,7 @@ static int mach64_dma_dispatch_vertex( drm_device_t *dev,
DRM_INFO( "mach64_do_wait_for_idle failed\n" );
DRM_INFO( "resetting engine ...\n" );
mach64_dump_engine_info( dev_priv );
mach64_do_engine_reset( dev );
mach64_do_engine_reset( dev_priv );
return -EBUSY;
}
} else {

View file

@ -523,5 +523,7 @@ typedef struct drm_scatter_gather {
#define DRM_IOCTL_MACH64_SWAP DRM_IO( 0x43)
#define DRM_IOCTL_MACH64_CLEAR DRM_IOW( 0x44, drm_mach64_clear_t)
#define DRM_IOCTL_MACH64_VERTEX DRM_IOW( 0x45, drm_mach64_vertex_t)
#if 0
#define DRM_IOCTL_MACH64_BLIT DRM_IOW( 0x46, drm_mach64_blit_t)
#endif
#endif

View file

@ -523,5 +523,7 @@ typedef struct drm_scatter_gather {
#define DRM_IOCTL_MACH64_SWAP DRM_IO( 0x43)
#define DRM_IOCTL_MACH64_CLEAR DRM_IOW( 0x44, drm_mach64_clear_t)
#define DRM_IOCTL_MACH64_VERTEX DRM_IOW( 0x45, drm_mach64_vertex_t)
#if 0
#define DRM_IOCTL_MACH64_BLIT DRM_IOW( 0x46, drm_mach64_blit_t)
#endif
#endif