diff options
author | Leif Delgass <ldelgass@users.sourceforge.net> | 2002-05-08 05:32:52 +0000 |
---|---|---|
committer | Leif Delgass <ldelgass@users.sourceforge.net> | 2002-05-08 05:32:52 +0000 |
commit | c3e3c95731905f973995e3bb1c7afb9f7e5d14df (patch) | |
tree | 6f0369f279de59ccf356bc0d6587907802546cd1 | |
parent | b53b0e0a040ce9a2f28263615cf70395808a8c68 (diff) |
- 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)
-rw-r--r-- | linux/drm.h | 4 | ||||
-rw-r--r-- | linux/mach64.h | 72 | ||||
-rw-r--r-- | linux/mach64_dma.c | 665 | ||||
-rw-r--r-- | linux/mach64_drm.h | 31 | ||||
-rw-r--r-- | linux/mach64_drv.h | 56 | ||||
-rw-r--r-- | linux/mach64_state.c | 2 | ||||
-rw-r--r-- | shared-core/drm.h | 4 | ||||
-rw-r--r-- | shared/drm.h | 4 |
8 files changed, 671 insertions, 167 deletions
diff --git a/linux/drm.h b/linux/drm.h index 6fc2c259..a37870a2 100644 --- a/linux/drm.h +++ b/linux/drm.h @@ -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 diff --git a/linux/mach64.h b/linux/mach64.h index 2d494703..244667f4 100644 --- a/linux/mach64.h +++ b/linux/mach64.h @@ -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: */ diff --git a/linux/mach64_dma.c b/linux/mach64_dma.c index c06be286..56b2126a 100644 --- a/linux/mach64_dma.c +++ b/linux/mach64_dma.c @@ -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; + } + 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; + } + } - /* 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; + /* 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; } - 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; + + /* 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 ); } - } - /* 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 MMIO mode\n"); - dev->dev_private = (void *) dev_priv; - return 0; - } + DRM_INFO( "descriptor table: cpu addr: 0x%08x, bus addr: 0x%08x\n", + (u32) dev_priv->cpu_addr_table, dev_priv->table_addr ); - /* 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"); + /* 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; + 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->table_addr = (u32) dev_priv->table_handle; - memset( dev_priv->cpu_addr_table, 0x0, 0x4000 ); + dev_priv->driver_mode = MACH64_MODE_MMIO; + DRM_INFO( "Forcing pseudo-DMA 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 ); + /* 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); - /* setup physical address and size of descriptor table */ - MACH64_WRITE( MACH64_BM_GUI_TABLE_CMD, - ( dev_priv->table_addr | MACH64_CIRCULAR_BUF_SIZE_16KB ) ); + 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); - /* 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"); - } else { - dev_priv->driver_mode = MACH64_MODE_MMIO; - DRM_INFO( "DMA test failed (ret=%d), using MMIO mode\n", ret ); + + 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; + } } -#else - dev_priv->driver_mode = MACH64_MODE_MMIO; - DRM_INFO( "Using MMIO mode\n"); #endif + return 0; +} - dev->dev_private = (void *) dev_priv; + +/* + 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; - return 0; + /* 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 ); - - 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; - } - - /* 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; - } - - 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; + LOCK_TEST_WITH_RETURN( dev ); + + if ( copy_from_user( &d, (drm_dma_t *)arg, sizeof(d) ) ) + { + return -EFAULT; + } + + /* 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; + + if ( d.request_count ) + { + ret = mach64_dma_get_buffers( dev, &d ); + } + + if ( copy_to_user( (drm_dma_t *)arg, &d, sizeof(d) ) ) + { + ret = -EFAULT; + } + } + } + + return ret; } + diff --git a/linux/mach64_drm.h b/linux/mach64_drm.h index a0aaee36..83b5c6ee 100644 --- a/linux/mach64_drm.h +++ b/linux/mach64_drm.h @@ -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 diff --git a/linux/mach64_drv.h b/linux/mach64_drv.h index f891e848..09a89ecc 100644 --- a/linux/mach64_drv.h +++ b/linux/mach64_drv.h @@ -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 diff --git a/linux/mach64_state.c b/linux/mach64_state.c index fe7c5362..13cfbc86 100644 --- a/linux/mach64_state.c +++ b/linux/mach64_state.c @@ -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 { diff --git a/shared-core/drm.h b/shared-core/drm.h index 6fc2c259..a37870a2 100644 --- a/shared-core/drm.h +++ b/shared-core/drm.h @@ -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 diff --git a/shared/drm.h b/shared/drm.h index 6fc2c259..a37870a2 100644 --- a/shared/drm.h +++ b/shared/drm.h @@ -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 |