X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fchar%2Fdrm%2Fmga_dma.c;h=c2a4bac14521f2b50d9257544ae45066e44620b4;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=bfaee0d837c89bf7bd0bf0764fcc6a4281774c3e;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/drivers/char/drm/mga_dma.c b/drivers/char/drm/mga_dma.c index bfaee0d83..c2a4bac14 100644 --- a/drivers/char/drm/mga_dma.c +++ b/drivers/char/drm/mga_dma.c @@ -23,76 +23,63 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. + */ + +/** + * \file mga_dma.c + * DMA support for MGA G200 / G400. * - * Authors: - * Rickard E. (Rik) Faith - * Jeff Hartmann - * Keith Whitwell - * - * Rewritten by: - * Gareth Hughes + * \author Rickard E. (Rik) Faith + * \author Jeff Hartmann + * \author Keith Whitwell + * \author Gareth Hughes */ -#include "mga.h" #include "drmP.h" #include "drm.h" +#include "drm_sarea.h" #include "mga_drm.h" #include "mga_drv.h" #define MGA_DEFAULT_USEC_TIMEOUT 10000 #define MGA_FREELIST_DEBUG 0 +#define MINIMAL_CLEANUP 0 +#define FULL_CLEANUP 1 +static int mga_do_cleanup_dma(drm_device_t *dev, int full_cleanup); /* ================================================================ * Engine control */ -int mga_do_wait_for_idle( drm_mga_private_t *dev_priv ) +int mga_do_wait_for_idle(drm_mga_private_t * dev_priv) { u32 status = 0; int i; - DRM_DEBUG( "\n" ); + DRM_DEBUG("\n"); - for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { - status = MGA_READ( MGA_STATUS ) & MGA_ENGINE_IDLE_MASK; - if ( status == MGA_ENDPRDMASTS ) { - MGA_WRITE8( MGA_CRTC_INDEX, 0 ); + for (i = 0; i < dev_priv->usec_timeout; i++) { + status = MGA_READ(MGA_STATUS) & MGA_ENGINE_IDLE_MASK; + if (status == MGA_ENDPRDMASTS) { + MGA_WRITE8(MGA_CRTC_INDEX, 0); return 0; } - DRM_UDELAY( 1 ); - } - -#if MGA_DMA_DEBUG - DRM_ERROR( "failed!\n" ); - DRM_INFO( " status=0x%08x\n", status ); -#endif - return DRM_ERR(EBUSY); -} - -int mga_do_dma_idle( drm_mga_private_t *dev_priv ) -{ - u32 status = 0; - int i; - DRM_DEBUG( "\n" ); - - for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { - status = MGA_READ( MGA_STATUS ) & MGA_DMA_IDLE_MASK; - if ( status == MGA_ENDPRDMASTS ) return 0; - DRM_UDELAY( 1 ); + DRM_UDELAY(1); } #if MGA_DMA_DEBUG - DRM_ERROR( "failed! status=0x%08x\n", status ); + DRM_ERROR("failed!\n"); + DRM_INFO(" status=0x%08x\n", status); #endif return DRM_ERR(EBUSY); } -int mga_do_dma_reset( drm_mga_private_t *dev_priv ) +static int mga_do_dma_reset(drm_mga_private_t * dev_priv) { drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; drm_mga_primary_buffer_t *primary = &dev_priv->prim; - DRM_DEBUG( "\n" ); + DRM_DEBUG("\n"); /* The primary DMA stream should look like new right about now. */ @@ -111,66 +98,29 @@ int mga_do_dma_reset( drm_mga_private_t *dev_priv ) return 0; } -int mga_do_engine_reset( drm_mga_private_t *dev_priv ) -{ - DRM_DEBUG( "\n" ); - - /* Okay, so we've completely screwed up and locked the engine. - * How about we clean up after ourselves? - */ - MGA_WRITE( MGA_RST, MGA_SOFTRESET ); - DRM_UDELAY( 15 ); /* Wait at least 10 usecs */ - MGA_WRITE( MGA_RST, 0 ); - - /* Initialize the registers that get clobbered by the soft - * reset. Many of the core register values survive a reset, - * but the drawing registers are basically all gone. - * - * 3D clients should probably die after calling this. The X - * server should reset the engine state to known values. - */ -#if 0 - MGA_WRITE( MGA_PRIMPTR, - virt_to_bus((void *)dev_priv->prim.status_page) | - MGA_PRIMPTREN0 | - MGA_PRIMPTREN1 ); -#endif - - MGA_WRITE( MGA_ICLEAR, MGA_SOFTRAPICLR ); - MGA_WRITE( MGA_IEN, MGA_SOFTRAPIEN ); - - /* The primary DMA stream should look like new right about now. - */ - mga_do_dma_reset( dev_priv ); - - /* This bad boy will never fail. - */ - return 0; -} - - /* ================================================================ * Primary DMA stream */ -void mga_do_dma_flush( drm_mga_private_t *dev_priv ) +void mga_do_dma_flush(drm_mga_private_t * dev_priv) { drm_mga_primary_buffer_t *primary = &dev_priv->prim; u32 head, tail; u32 status = 0; int i; - DMA_LOCALS; - DRM_DEBUG( "\n" ); - - /* We need to wait so that we can do an safe flush */ - for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { - status = MGA_READ( MGA_STATUS ) & MGA_ENGINE_IDLE_MASK; - if ( status == MGA_ENDPRDMASTS ) break; - DRM_UDELAY( 1 ); + DMA_LOCALS; + DRM_DEBUG("\n"); + + /* We need to wait so that we can do an safe flush */ + for (i = 0; i < dev_priv->usec_timeout; i++) { + status = MGA_READ(MGA_STATUS) & MGA_ENGINE_IDLE_MASK; + if (status == MGA_ENDPRDMASTS) + break; + DRM_UDELAY(1); } - if ( primary->tail == primary->last_flush ) { - DRM_DEBUG( " bailing out...\n" ); + if (primary->tail == primary->last_flush) { + DRM_DEBUG(" bailing out...\n"); return; } @@ -180,48 +130,46 @@ void mga_do_dma_flush( drm_mga_private_t *dev_priv ) * actually (partially?) reads the first of these commands. * See page 4-16 in the G400 manual, middle of the page or so. */ - BEGIN_DMA( 1 ); + BEGIN_DMA(1); - DMA_BLOCK( MGA_DMAPAD, 0x00000000, - MGA_DMAPAD, 0x00000000, - MGA_DMAPAD, 0x00000000, - MGA_DMAPAD, 0x00000000 ); + DMA_BLOCK(MGA_DMAPAD, 0x00000000, + MGA_DMAPAD, 0x00000000, + MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000); ADVANCE_DMA(); primary->last_flush = primary->tail; - head = MGA_READ( MGA_PRIMADDRESS ); + head = MGA_READ(MGA_PRIMADDRESS); - if ( head <= tail ) { + if (head <= tail) { primary->space = primary->size - primary->tail; } else { primary->space = head - tail; } - DRM_DEBUG( " head = 0x%06lx\n", head - dev_priv->primary->offset ); - DRM_DEBUG( " tail = 0x%06lx\n", tail - dev_priv->primary->offset ); - DRM_DEBUG( " space = 0x%06x\n", primary->space ); + DRM_DEBUG(" head = 0x%06lx\n", head - dev_priv->primary->offset); + DRM_DEBUG(" tail = 0x%06lx\n", tail - dev_priv->primary->offset); + DRM_DEBUG(" space = 0x%06x\n", primary->space); mga_flush_write_combine(); - MGA_WRITE( MGA_PRIMEND, tail | MGA_PAGPXFER ); + MGA_WRITE(MGA_PRIMEND, tail | dev_priv->dma_access); - DRM_DEBUG( "done.\n" ); + DRM_DEBUG("done.\n"); } -void mga_do_dma_wrap_start( drm_mga_private_t *dev_priv ) +void mga_do_dma_wrap_start(drm_mga_private_t * dev_priv) { drm_mga_primary_buffer_t *primary = &dev_priv->prim; u32 head, tail; DMA_LOCALS; - DRM_DEBUG( "\n" ); + DRM_DEBUG("\n"); BEGIN_DMA_WRAP(); - DMA_BLOCK( MGA_DMAPAD, 0x00000000, - MGA_DMAPAD, 0x00000000, - MGA_DMAPAD, 0x00000000, - MGA_DMAPAD, 0x00000000 ); + DMA_BLOCK(MGA_DMAPAD, 0x00000000, + MGA_DMAPAD, 0x00000000, + MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000); ADVANCE_DMA(); @@ -231,45 +179,43 @@ void mga_do_dma_wrap_start( drm_mga_private_t *dev_priv ) primary->last_flush = 0; primary->last_wrap++; - head = MGA_READ( MGA_PRIMADDRESS ); + head = MGA_READ(MGA_PRIMADDRESS); - if ( head == dev_priv->primary->offset ) { + if (head == dev_priv->primary->offset) { primary->space = primary->size; } else { primary->space = head - dev_priv->primary->offset; } - DRM_DEBUG( " head = 0x%06lx\n", - head - dev_priv->primary->offset ); - DRM_DEBUG( " tail = 0x%06x\n", primary->tail ); - DRM_DEBUG( " wrap = %d\n", primary->last_wrap ); - DRM_DEBUG( " space = 0x%06x\n", primary->space ); + DRM_DEBUG(" head = 0x%06lx\n", head - dev_priv->primary->offset); + DRM_DEBUG(" tail = 0x%06x\n", primary->tail); + DRM_DEBUG(" wrap = %d\n", primary->last_wrap); + DRM_DEBUG(" space = 0x%06x\n", primary->space); mga_flush_write_combine(); - MGA_WRITE( MGA_PRIMEND, tail | MGA_PAGPXFER ); + MGA_WRITE(MGA_PRIMEND, tail | dev_priv->dma_access); - set_bit( 0, &primary->wrapped ); - DRM_DEBUG( "done.\n" ); + set_bit(0, &primary->wrapped); + DRM_DEBUG("done.\n"); } -void mga_do_dma_wrap_end( drm_mga_private_t *dev_priv ) +void mga_do_dma_wrap_end(drm_mga_private_t * dev_priv) { drm_mga_primary_buffer_t *primary = &dev_priv->prim; drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; u32 head = dev_priv->primary->offset; - DRM_DEBUG( "\n" ); + DRM_DEBUG("\n"); sarea_priv->last_wrap++; - DRM_DEBUG( " wrap = %d\n", sarea_priv->last_wrap ); + DRM_DEBUG(" wrap = %d\n", sarea_priv->last_wrap); mga_flush_write_combine(); - MGA_WRITE( MGA_PRIMADDRESS, head | MGA_DMA_GENERAL ); + MGA_WRITE(MGA_PRIMADDRESS, head | MGA_DMA_GENERAL); - clear_bit( 0, &primary->wrapped ); - DRM_DEBUG( "done.\n" ); + clear_bit(0, &primary->wrapped); + DRM_DEBUG("done.\n"); } - /* ================================================================ * Freelist management */ @@ -278,63 +224,61 @@ void mga_do_dma_wrap_end( drm_mga_private_t *dev_priv ) #define MGA_BUFFER_FREE 0 #if MGA_FREELIST_DEBUG -static void mga_freelist_print( drm_device_t *dev ) +static void mga_freelist_print(drm_device_t * dev) { drm_mga_private_t *dev_priv = dev->dev_private; drm_mga_freelist_t *entry; - DRM_INFO( "\n" ); - DRM_INFO( "current dispatch: last=0x%x done=0x%x\n", - dev_priv->sarea_priv->last_dispatch, - (unsigned int)(MGA_READ( MGA_PRIMADDRESS ) - - dev_priv->primary->offset) ); - DRM_INFO( "current freelist:\n" ); - - for ( entry = dev_priv->head->next ; entry ; entry = entry->next ) { - DRM_INFO( " %p idx=%2d age=0x%x 0x%06lx\n", - entry, entry->buf->idx, entry->age.head, - entry->age.head - dev_priv->primary->offset ); + DRM_INFO("\n"); + DRM_INFO("current dispatch: last=0x%x done=0x%x\n", + dev_priv->sarea_priv->last_dispatch, + (unsigned int)(MGA_READ(MGA_PRIMADDRESS) - + dev_priv->primary->offset)); + DRM_INFO("current freelist:\n"); + + for (entry = dev_priv->head->next; entry; entry = entry->next) { + DRM_INFO(" %p idx=%2d age=0x%x 0x%06lx\n", + entry, entry->buf->idx, entry->age.head, + entry->age.head - dev_priv->primary->offset); } - DRM_INFO( "\n" ); + DRM_INFO("\n"); } #endif -static int mga_freelist_init( drm_device_t *dev, drm_mga_private_t *dev_priv ) +static int mga_freelist_init(drm_device_t * dev, drm_mga_private_t * dev_priv) { drm_device_dma_t *dma = dev->dma; drm_buf_t *buf; drm_mga_buf_priv_t *buf_priv; drm_mga_freelist_t *entry; int i; - DRM_DEBUG( "count=%d\n", dma->buf_count ); + DRM_DEBUG("count=%d\n", dma->buf_count); - dev_priv->head = DRM(alloc)( sizeof(drm_mga_freelist_t), - DRM_MEM_DRIVER ); - if ( dev_priv->head == NULL ) + dev_priv->head = drm_alloc(sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER); + if (dev_priv->head == NULL) return DRM_ERR(ENOMEM); - memset( dev_priv->head, 0, sizeof(drm_mga_freelist_t) ); - SET_AGE( &dev_priv->head->age, MGA_BUFFER_USED, 0 ); + memset(dev_priv->head, 0, sizeof(drm_mga_freelist_t)); + SET_AGE(&dev_priv->head->age, MGA_BUFFER_USED, 0); - for ( i = 0 ; i < dma->buf_count ; i++ ) { + for (i = 0; i < dma->buf_count; i++) { buf = dma->buflist[i]; - buf_priv = buf->dev_private; + buf_priv = buf->dev_private; - entry = DRM(alloc)( sizeof(drm_mga_freelist_t), - DRM_MEM_DRIVER ); - if ( entry == NULL ) + entry = drm_alloc(sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER); + if (entry == NULL) return DRM_ERR(ENOMEM); - memset( entry, 0, sizeof(drm_mga_freelist_t) ); + memset(entry, 0, sizeof(drm_mga_freelist_t)); entry->next = dev_priv->head->next; entry->prev = dev_priv->head; - SET_AGE( &entry->age, MGA_BUFFER_FREE, 0 ); + SET_AGE(&entry->age, MGA_BUFFER_FREE, 0); entry->buf = buf; - if ( dev_priv->head->next != NULL ) + if (dev_priv->head->next != NULL) dev_priv->head->next->prev = entry; - if ( entry->next == NULL ) + if (entry->next == NULL) dev_priv->tail = entry; buf_priv->list_entry = entry; @@ -347,17 +291,17 @@ static int mga_freelist_init( drm_device_t *dev, drm_mga_private_t *dev_priv ) return 0; } -static void mga_freelist_cleanup( drm_device_t *dev ) +static void mga_freelist_cleanup(drm_device_t * dev) { drm_mga_private_t *dev_priv = dev->dev_private; drm_mga_freelist_t *entry; drm_mga_freelist_t *next; - DRM_DEBUG( "\n" ); + DRM_DEBUG("\n"); entry = dev_priv->head; - while ( entry ) { + while (entry) { next = entry->next; - DRM(free)( entry, sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER ); + drm_free(entry, sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER); entry = next; } @@ -367,71 +311,69 @@ static void mga_freelist_cleanup( drm_device_t *dev ) #if 0 /* FIXME: Still needed? */ -static void mga_freelist_reset( drm_device_t *dev ) +static void mga_freelist_reset(drm_device_t * dev) { drm_device_dma_t *dma = dev->dma; drm_buf_t *buf; drm_mga_buf_priv_t *buf_priv; int i; - for ( i = 0 ; i < dma->buf_count ; i++ ) { + for (i = 0; i < dma->buf_count; i++) { buf = dma->buflist[i]; - buf_priv = buf->dev_private; - SET_AGE( &buf_priv->list_entry->age, - MGA_BUFFER_FREE, 0 ); + buf_priv = buf->dev_private; + SET_AGE(&buf_priv->list_entry->age, MGA_BUFFER_FREE, 0); } } #endif -static drm_buf_t *mga_freelist_get( drm_device_t *dev ) +static drm_buf_t *mga_freelist_get(drm_device_t * dev) { drm_mga_private_t *dev_priv = dev->dev_private; drm_mga_freelist_t *next; drm_mga_freelist_t *prev; drm_mga_freelist_t *tail = dev_priv->tail; u32 head, wrap; - DRM_DEBUG( "\n" ); + DRM_DEBUG("\n"); - head = MGA_READ( MGA_PRIMADDRESS ); + head = MGA_READ(MGA_PRIMADDRESS); wrap = dev_priv->sarea_priv->last_wrap; - DRM_DEBUG( " tail=0x%06lx %d\n", - tail->age.head ? - tail->age.head - dev_priv->primary->offset : 0, - tail->age.wrap ); - DRM_DEBUG( " head=0x%06lx %d\n", - head - dev_priv->primary->offset, wrap ); + DRM_DEBUG(" tail=0x%06lx %d\n", + tail->age.head ? + tail->age.head - dev_priv->primary->offset : 0, + tail->age.wrap); + DRM_DEBUG(" head=0x%06lx %d\n", + head - dev_priv->primary->offset, wrap); - if ( TEST_AGE( &tail->age, head, wrap ) ) { + if (TEST_AGE(&tail->age, head, wrap)) { prev = dev_priv->tail->prev; next = dev_priv->tail; prev->next = NULL; next->prev = next->next = NULL; dev_priv->tail = prev; - SET_AGE( &next->age, MGA_BUFFER_USED, 0 ); + SET_AGE(&next->age, MGA_BUFFER_USED, 0); return next->buf; } - DRM_DEBUG( "returning NULL!\n" ); + DRM_DEBUG("returning NULL!\n"); return NULL; } -int mga_freelist_put( drm_device_t *dev, drm_buf_t *buf ) +int mga_freelist_put(drm_device_t * dev, drm_buf_t * buf) { drm_mga_private_t *dev_priv = dev->dev_private; drm_mga_buf_priv_t *buf_priv = buf->dev_private; drm_mga_freelist_t *head, *entry, *prev; - DRM_DEBUG( "age=0x%06lx wrap=%d\n", - buf_priv->list_entry->age.head - - dev_priv->primary->offset, - buf_priv->list_entry->age.wrap ); + DRM_DEBUG("age=0x%06lx wrap=%d\n", + buf_priv->list_entry->age.head - + dev_priv->primary->offset, buf_priv->list_entry->age.wrap); entry = buf_priv->list_entry; head = dev_priv->head; - if ( buf_priv->list_entry->age.head == MGA_BUFFER_USED ) { - SET_AGE( &entry->age, MGA_BUFFER_FREE, 0 ); + if (buf_priv->list_entry->age.head == MGA_BUFFER_USED) { + SET_AGE(&entry->age, MGA_BUFFER_FREE, 0); prev = dev_priv->tail; prev->next = entry; entry->prev = prev; @@ -447,43 +389,434 @@ int mga_freelist_put( drm_device_t *dev, drm_buf_t *buf ) return 0; } - /* ================================================================ * DMA initialization, cleanup */ -static int mga_do_init_dma( drm_device_t *dev, drm_mga_init_t *init ) +int mga_driver_load(drm_device_t * dev, unsigned long flags) { drm_mga_private_t *dev_priv; - int ret; - DRM_DEBUG( "\n" ); - dev_priv = DRM(alloc)( sizeof(drm_mga_private_t), DRM_MEM_DRIVER ); - if ( !dev_priv ) + dev_priv = drm_alloc(sizeof(drm_mga_private_t), DRM_MEM_DRIVER); + if (!dev_priv) return DRM_ERR(ENOMEM); - memset( dev_priv, 0, sizeof(drm_mga_private_t) ); - - dev_priv->chipset = init->chipset; + dev->dev_private = (void *)dev_priv; + memset(dev_priv, 0, sizeof(drm_mga_private_t)); dev_priv->usec_timeout = MGA_DEFAULT_USEC_TIMEOUT; + dev_priv->chipset = flags; + + dev_priv->mmio_base = drm_get_resource_start(dev, 1); + dev_priv->mmio_size = drm_get_resource_len(dev, 1); - if ( init->sgram ) { + dev->counters += 3; + dev->types[6] = _DRM_STAT_IRQ; + dev->types[7] = _DRM_STAT_PRIMARY; + dev->types[8] = _DRM_STAT_SECONDARY; + + return 0; +} + +#if __OS_HAS_AGP +/** + * Bootstrap the driver for AGP DMA. + * + * \todo + * Investigate whether there is any benifit to storing the WARP microcode in + * AGP memory. If not, the microcode may as well always be put in PCI + * memory. + * + * \todo + * This routine needs to set dma_bs->agp_mode to the mode actually configured + * in the hardware. Looking just at the Linux AGP driver code, I don't see + * an easy way to determine this. + * + * \sa mga_do_dma_bootstrap, mga_do_pci_dma_bootstrap + */ +static int mga_do_agp_dma_bootstrap(drm_device_t * dev, + drm_mga_dma_bootstrap_t * dma_bs) +{ + drm_mga_private_t *const dev_priv = + (drm_mga_private_t *) dev->dev_private; + unsigned int warp_size = mga_warp_microcode_size(dev_priv); + int err; + unsigned offset; + const unsigned secondary_size = dma_bs->secondary_bin_count + * dma_bs->secondary_bin_size; + const unsigned agp_size = (dma_bs->agp_size << 20); + drm_buf_desc_t req; + drm_agp_mode_t mode; + drm_agp_info_t info; + drm_agp_buffer_t agp_req; + drm_agp_binding_t bind_req; + + /* Acquire AGP. */ + err = drm_agp_acquire(dev); + if (err) { + DRM_ERROR("Unable to acquire AGP: %d\n", err); + return err; + } + + err = drm_agp_info(dev, &info); + if (err) { + DRM_ERROR("Unable to get AGP info: %d\n", err); + return err; + } + + mode.mode = (info.mode & ~0x07) | dma_bs->agp_mode; + err = drm_agp_enable(dev, mode); + if (err) { + DRM_ERROR("Unable to enable AGP (mode = 0x%lx)\n", mode.mode); + return err; + } + + /* In addition to the usual AGP mode configuration, the G200 AGP cards + * need to have the AGP mode "manually" set. + */ + + if (dev_priv->chipset == MGA_CARD_TYPE_G200) { + if (mode.mode & 0x02) { + MGA_WRITE(MGA_AGP_PLL, MGA_AGP2XPLL_ENABLE); + } else { + MGA_WRITE(MGA_AGP_PLL, MGA_AGP2XPLL_DISABLE); + } + } + + /* Allocate and bind AGP memory. */ + agp_req.size = agp_size; + agp_req.type = 0; + err = drm_agp_alloc(dev, &agp_req); + if (err) { + dev_priv->agp_size = 0; + DRM_ERROR("Unable to allocate %uMB AGP memory\n", + dma_bs->agp_size); + return err; + } + + dev_priv->agp_size = agp_size; + dev_priv->agp_handle = agp_req.handle; + + bind_req.handle = agp_req.handle; + bind_req.offset = 0; + err = drm_agp_bind(dev, &bind_req); + if (err) { + DRM_ERROR("Unable to bind AGP memory: %d\n", err); + return err; + } + + /* Make drm_addbufs happy by not trying to create a mapping for less + * than a page. + */ + if (warp_size < PAGE_SIZE) + warp_size = PAGE_SIZE; + + offset = 0; + err = drm_addmap(dev, offset, warp_size, + _DRM_AGP, _DRM_READ_ONLY, &dev_priv->warp); + if (err) { + DRM_ERROR("Unable to map WARP microcode: %d\n", err); + return err; + } + + offset += warp_size; + err = drm_addmap(dev, offset, dma_bs->primary_size, + _DRM_AGP, _DRM_READ_ONLY, &dev_priv->primary); + if (err) { + DRM_ERROR("Unable to map primary DMA region: %d\n", err); + return err; + } + + offset += dma_bs->primary_size; + err = drm_addmap(dev, offset, secondary_size, + _DRM_AGP, 0, &dev->agp_buffer_map); + if (err) { + DRM_ERROR("Unable to map secondary DMA region: %d\n", err); + return err; + } + + (void)memset(&req, 0, sizeof(req)); + req.count = dma_bs->secondary_bin_count; + req.size = dma_bs->secondary_bin_size; + req.flags = _DRM_AGP_BUFFER; + req.agp_start = offset; + + err = drm_addbufs_agp(dev, &req); + if (err) { + DRM_ERROR("Unable to add secondary DMA buffers: %d\n", err); + return err; + } + + { + drm_map_list_t *_entry; + unsigned long agp_token = 0; + + list_for_each_entry(_entry, &dev->maplist->head, head) { + if (_entry->map == dev->agp_buffer_map) + agp_token = _entry->user_token; + } + if (!agp_token) + return -EFAULT; + + dev->agp_buffer_token = agp_token; + } + + offset += secondary_size; + err = drm_addmap(dev, offset, agp_size - offset, + _DRM_AGP, 0, &dev_priv->agp_textures); + if (err) { + DRM_ERROR("Unable to map AGP texture region %d\n", err); + return err; + } + + drm_core_ioremap(dev_priv->warp, dev); + drm_core_ioremap(dev_priv->primary, dev); + drm_core_ioremap(dev->agp_buffer_map, dev); + + if (!dev_priv->warp->handle || + !dev_priv->primary->handle || !dev->agp_buffer_map->handle) { + DRM_ERROR("failed to ioremap agp regions! (%p, %p, %p)\n", + dev_priv->warp->handle, dev_priv->primary->handle, + dev->agp_buffer_map->handle); + return DRM_ERR(ENOMEM); + } + + dev_priv->dma_access = MGA_PAGPXFER; + dev_priv->wagp_enable = MGA_WAGP_ENABLE; + + DRM_INFO("Initialized card for AGP DMA.\n"); + return 0; +} +#else +static int mga_do_agp_dma_bootstrap(drm_device_t * dev, + drm_mga_dma_bootstrap_t * dma_bs) +{ + return -EINVAL; +} +#endif + +/** + * Bootstrap the driver for PCI DMA. + * + * \todo + * The algorithm for decreasing the size of the primary DMA buffer could be + * better. The size should be rounded up to the nearest page size, then + * decrease the request size by a single page each pass through the loop. + * + * \todo + * Determine whether the maximum address passed to drm_pci_alloc is correct. + * The same goes for drm_addbufs_pci. + * + * \sa mga_do_dma_bootstrap, mga_do_agp_dma_bootstrap + */ +static int mga_do_pci_dma_bootstrap(drm_device_t * dev, + drm_mga_dma_bootstrap_t * dma_bs) +{ + drm_mga_private_t *const dev_priv = + (drm_mga_private_t *) dev->dev_private; + unsigned int warp_size = mga_warp_microcode_size(dev_priv); + unsigned int primary_size; + unsigned int bin_count; + int err; + drm_buf_desc_t req; + + if (dev->dma == NULL) { + DRM_ERROR("dev->dma is NULL\n"); + return DRM_ERR(EFAULT); + } + + /* Make drm_addbufs happy by not trying to create a mapping for less + * than a page. + */ + if (warp_size < PAGE_SIZE) + warp_size = PAGE_SIZE; + + /* The proper alignment is 0x100 for this mapping */ + err = drm_addmap(dev, 0, warp_size, _DRM_CONSISTENT, + _DRM_READ_ONLY, &dev_priv->warp); + if (err != 0) { + DRM_ERROR("Unable to create mapping for WARP microcode: %d\n", + err); + return err; + } + + /* Other than the bottom two bits being used to encode other + * information, there don't appear to be any restrictions on the + * alignment of the primary or secondary DMA buffers. + */ + + for (primary_size = dma_bs->primary_size; primary_size != 0; + primary_size >>= 1) { + /* The proper alignment for this mapping is 0x04 */ + err = drm_addmap(dev, 0, primary_size, _DRM_CONSISTENT, + _DRM_READ_ONLY, &dev_priv->primary); + if (!err) + break; + } + + if (err != 0) { + DRM_ERROR("Unable to allocate primary DMA region: %d\n", err); + return DRM_ERR(ENOMEM); + } + + if (dev_priv->primary->size != dma_bs->primary_size) { + DRM_INFO("Primary DMA buffer size reduced from %u to %u.\n", + dma_bs->primary_size, + (unsigned)dev_priv->primary->size); + dma_bs->primary_size = dev_priv->primary->size; + } + + for (bin_count = dma_bs->secondary_bin_count; bin_count > 0; + bin_count--) { + (void)memset(&req, 0, sizeof(req)); + req.count = bin_count; + req.size = dma_bs->secondary_bin_size; + + err = drm_addbufs_pci(dev, &req); + if (!err) { + break; + } + } + + if (bin_count == 0) { + DRM_ERROR("Unable to add secondary DMA buffers: %d\n", err); + return err; + } + + if (bin_count != dma_bs->secondary_bin_count) { + DRM_INFO("Secondary PCI DMA buffer bin count reduced from %u " + "to %u.\n", dma_bs->secondary_bin_count, bin_count); + + dma_bs->secondary_bin_count = bin_count; + } + + dev_priv->dma_access = 0; + dev_priv->wagp_enable = 0; + + dma_bs->agp_mode = 0; + + DRM_INFO("Initialized card for PCI DMA.\n"); + return 0; +} + +static int mga_do_dma_bootstrap(drm_device_t * dev, + drm_mga_dma_bootstrap_t * dma_bs) +{ + const int is_agp = (dma_bs->agp_mode != 0) && drm_device_is_agp(dev); + int err; + drm_mga_private_t *const dev_priv = + (drm_mga_private_t *) dev->dev_private; + + dev_priv->used_new_dma_init = 1; + + /* The first steps are the same for both PCI and AGP based DMA. Map + * the cards MMIO registers and map a status page. + */ + err = drm_addmap(dev, dev_priv->mmio_base, dev_priv->mmio_size, + _DRM_REGISTERS, _DRM_READ_ONLY, &dev_priv->mmio); + if (err) { + DRM_ERROR("Unable to map MMIO region: %d\n", err); + return err; + } + + err = drm_addmap(dev, 0, SAREA_MAX, _DRM_SHM, + _DRM_READ_ONLY | _DRM_LOCKED | _DRM_KERNEL, + &dev_priv->status); + if (err) { + DRM_ERROR("Unable to map status region: %d\n", err); + return err; + } + + /* The DMA initialization procedure is slightly different for PCI and + * AGP cards. AGP cards just allocate a large block of AGP memory and + * carve off portions of it for internal uses. The remaining memory + * is returned to user-mode to be used for AGP textures. + */ + if (is_agp) { + err = mga_do_agp_dma_bootstrap(dev, dma_bs); + } + + /* If we attempted to initialize the card for AGP DMA but failed, + * clean-up any mess that may have been created. + */ + + if (err) { + mga_do_cleanup_dma(dev, MINIMAL_CLEANUP); + } + + /* Not only do we want to try and initialized PCI cards for PCI DMA, + * but we also try to initialized AGP cards that could not be + * initialized for AGP DMA. This covers the case where we have an AGP + * card in a system with an unsupported AGP chipset. In that case the + * card will be detected as AGP, but we won't be able to allocate any + * AGP memory, etc. + */ + + if (!is_agp || err) { + err = mga_do_pci_dma_bootstrap(dev, dma_bs); + } + + return err; +} + +int mga_dma_bootstrap(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_mga_dma_bootstrap_t bootstrap; + int err; + static const int modes[] = { 0, 1, 2, 2, 4, 4, 4, 4 }; + const drm_mga_private_t *const dev_priv = + (drm_mga_private_t *) dev->dev_private; + + DRM_COPY_FROM_USER_IOCTL(bootstrap, + (drm_mga_dma_bootstrap_t __user *) data, + sizeof(bootstrap)); + + err = mga_do_dma_bootstrap(dev, &bootstrap); + if (err) { + mga_do_cleanup_dma(dev, FULL_CLEANUP); + return err; + } + + if (dev_priv->agp_textures != NULL) { + bootstrap.texture_handle = dev_priv->agp_textures->offset; + bootstrap.texture_size = dev_priv->agp_textures->size; + } else { + bootstrap.texture_handle = 0; + bootstrap.texture_size = 0; + } + + bootstrap.agp_mode = modes[bootstrap.agp_mode & 0x07]; + DRM_COPY_TO_USER_IOCTL((drm_mga_dma_bootstrap_t __user *)data, + bootstrap, sizeof(bootstrap)); + + return err; +} + +static int mga_do_init_dma(drm_device_t * dev, drm_mga_init_t * init) +{ + drm_mga_private_t *dev_priv; + int ret; + DRM_DEBUG("\n"); + + dev_priv = dev->dev_private; + + if (init->sgram) { dev_priv->clear_cmd = MGA_DWGCTL_CLEAR | MGA_ATYPE_BLK; } else { dev_priv->clear_cmd = MGA_DWGCTL_CLEAR | MGA_ATYPE_RSTR; } - dev_priv->maccess = init->maccess; + dev_priv->maccess = init->maccess; - dev_priv->fb_cpp = init->fb_cpp; - dev_priv->front_offset = init->front_offset; - dev_priv->front_pitch = init->front_pitch; - dev_priv->back_offset = init->back_offset; - dev_priv->back_pitch = init->back_pitch; + dev_priv->fb_cpp = init->fb_cpp; + dev_priv->front_offset = init->front_offset; + dev_priv->front_pitch = init->front_pitch; + dev_priv->back_offset = init->back_offset; + dev_priv->back_pitch = init->back_pitch; - dev_priv->depth_cpp = init->depth_cpp; - dev_priv->depth_offset = init->depth_offset; - dev_priv->depth_pitch = init->depth_pitch; + dev_priv->depth_cpp = init->depth_cpp; + dev_priv->depth_offset = init->depth_offset; + dev_priv->depth_pitch = init->depth_pitch; /* FIXME: Need to support AGP textures... */ @@ -492,117 +825,88 @@ static int mga_do_init_dma( drm_device_t *dev, drm_mga_init_t *init ) DRM_GETSAREA(); - if(!dev_priv->sarea) { - DRM_ERROR( "failed to find sarea!\n" ); - /* Assign dev_private so we can do cleanup. */ - dev->dev_private = (void *)dev_priv; - mga_do_cleanup_dma( dev ); + if (!dev_priv->sarea) { + DRM_ERROR("failed to find sarea!\n"); return DRM_ERR(EINVAL); } - DRM_FIND_MAP( dev_priv->fb, init->fb_offset ); - if(!dev_priv->fb) { - DRM_ERROR( "failed to find framebuffer!\n" ); - /* Assign dev_private so we can do cleanup. */ - dev->dev_private = (void *)dev_priv; - mga_do_cleanup_dma( dev ); - return DRM_ERR(EINVAL); - } - DRM_FIND_MAP( dev_priv->mmio, init->mmio_offset ); - if(!dev_priv->mmio) { - DRM_ERROR( "failed to find mmio region!\n" ); - /* Assign dev_private so we can do cleanup. */ - dev->dev_private = (void *)dev_priv; - mga_do_cleanup_dma( dev ); - return DRM_ERR(EINVAL); - } - DRM_FIND_MAP( dev_priv->status, init->status_offset ); - if(!dev_priv->status) { - DRM_ERROR( "failed to find status page!\n" ); - /* Assign dev_private so we can do cleanup. */ - dev->dev_private = (void *)dev_priv; - mga_do_cleanup_dma( dev ); - return DRM_ERR(EINVAL); - } + if (!dev_priv->used_new_dma_init) { - DRM_FIND_MAP( dev_priv->warp, init->warp_offset ); - if(!dev_priv->warp) { - DRM_ERROR( "failed to find warp microcode region!\n" ); - /* Assign dev_private so we can do cleanup. */ - dev->dev_private = (void *)dev_priv; - mga_do_cleanup_dma( dev ); - return DRM_ERR(EINVAL); - } - DRM_FIND_MAP( dev_priv->primary, init->primary_offset ); - if(!dev_priv->primary) { - DRM_ERROR( "failed to find primary dma region!\n" ); - /* Assign dev_private so we can do cleanup. */ - dev->dev_private = (void *)dev_priv; - mga_do_cleanup_dma( dev ); - return DRM_ERR(EINVAL); - } - DRM_FIND_MAP( dev_priv->buffers, init->buffers_offset ); - if(!dev_priv->buffers) { - DRM_ERROR( "failed to find dma buffer region!\n" ); - /* Assign dev_private so we can do cleanup. */ - dev->dev_private = (void *)dev_priv; - mga_do_cleanup_dma( dev ); - return DRM_ERR(EINVAL); + dev_priv->dma_access = MGA_PAGPXFER; + dev_priv->wagp_enable = MGA_WAGP_ENABLE; + + dev_priv->status = drm_core_findmap(dev, init->status_offset); + if (!dev_priv->status) { + DRM_ERROR("failed to find status page!\n"); + return DRM_ERR(EINVAL); + } + dev_priv->mmio = drm_core_findmap(dev, init->mmio_offset); + if (!dev_priv->mmio) { + DRM_ERROR("failed to find mmio region!\n"); + return DRM_ERR(EINVAL); + } + dev_priv->warp = drm_core_findmap(dev, init->warp_offset); + if (!dev_priv->warp) { + DRM_ERROR("failed to find warp microcode region!\n"); + return DRM_ERR(EINVAL); + } + dev_priv->primary = drm_core_findmap(dev, init->primary_offset); + if (!dev_priv->primary) { + DRM_ERROR("failed to find primary dma region!\n"); + return DRM_ERR(EINVAL); + } + dev->agp_buffer_token = init->buffers_offset; + dev->agp_buffer_map = + drm_core_findmap(dev, init->buffers_offset); + if (!dev->agp_buffer_map) { + DRM_ERROR("failed to find dma buffer region!\n"); + return DRM_ERR(EINVAL); + } + + drm_core_ioremap(dev_priv->warp, dev); + drm_core_ioremap(dev_priv->primary, dev); + drm_core_ioremap(dev->agp_buffer_map, dev); } dev_priv->sarea_priv = - (drm_mga_sarea_t *)((u8 *)dev_priv->sarea->handle + - init->sarea_priv_offset); - - DRM_IOREMAP( dev_priv->warp, dev ); - DRM_IOREMAP( dev_priv->primary, dev ); - DRM_IOREMAP( dev_priv->buffers, dev ); - - if(!dev_priv->warp->handle || - !dev_priv->primary->handle || - !dev_priv->buffers->handle ) { - DRM_ERROR( "failed to ioremap agp regions!\n" ); - /* Assign dev_private so we can do cleanup. */ - dev->dev_private = (void *)dev_priv; - mga_do_cleanup_dma( dev ); + (drm_mga_sarea_t *) ((u8 *) dev_priv->sarea->handle + + init->sarea_priv_offset); + + if (!dev_priv->warp->handle || + !dev_priv->primary->handle || + ((dev_priv->dma_access != 0) && + ((dev->agp_buffer_map == NULL) || + (dev->agp_buffer_map->handle == NULL)))) { + DRM_ERROR("failed to ioremap agp regions!\n"); return DRM_ERR(ENOMEM); } - ret = mga_warp_install_microcode( dev_priv ); - if ( ret < 0 ) { - DRM_ERROR( "failed to install WARP ucode!\n" ); - /* Assign dev_private so we can do cleanup. */ - dev->dev_private = (void *)dev_priv; - mga_do_cleanup_dma( dev ); + ret = mga_warp_install_microcode(dev_priv); + if (ret < 0) { + DRM_ERROR("failed to install WARP ucode!: %d\n", ret); return ret; } - ret = mga_warp_init( dev_priv ); - if ( ret < 0 ) { - DRM_ERROR( "failed to init WARP engine!\n" ); - /* Assign dev_private so we can do cleanup. */ - dev->dev_private = (void *)dev_priv; - mga_do_cleanup_dma( dev ); + ret = mga_warp_init(dev_priv); + if (ret < 0) { + DRM_ERROR("failed to init WARP engine!: %d\n", ret); return ret; } - dev_priv->prim.status = (u32 *)dev_priv->status->handle; + dev_priv->prim.status = (u32 *) dev_priv->status->handle; - mga_do_wait_for_idle( dev_priv ); + mga_do_wait_for_idle(dev_priv); /* Init the primary DMA registers. */ - MGA_WRITE( MGA_PRIMADDRESS, - dev_priv->primary->offset | MGA_DMA_GENERAL ); + MGA_WRITE(MGA_PRIMADDRESS, dev_priv->primary->offset | MGA_DMA_GENERAL); #if 0 - MGA_WRITE( MGA_PRIMPTR, - virt_to_bus((void *)dev_priv->prim.status) | - MGA_PRIMPTREN0 | /* Soft trap, SECEND, SETUPEND */ - MGA_PRIMPTREN1 ); /* DWGSYNC */ + MGA_WRITE(MGA_PRIMPTR, virt_to_bus((void *)dev_priv->prim.status) | MGA_PRIMPTREN0 | /* Soft trap, SECEND, SETUPEND */ + MGA_PRIMPTREN1); /* DWGSYNC */ #endif - dev_priv->prim.start = (u8 *)dev_priv->primary->handle; - dev_priv->prim.end = ((u8 *)dev_priv->primary->handle + dev_priv->prim.start = (u8 *) dev_priv->primary->handle; + dev_priv->prim.end = ((u8 *) dev_priv->primary->handle + dev_priv->primary->size); dev_priv->prim.size = dev_priv->primary->size; @@ -622,144 +926,184 @@ static int mga_do_init_dma( drm_device_t *dev, drm_mga_init_t *init ) dev_priv->sarea_priv->last_frame.head = 0; dev_priv->sarea_priv->last_frame.wrap = 0; - if ( mga_freelist_init( dev, dev_priv ) < 0 ) { - DRM_ERROR( "could not initialize freelist\n" ); - /* Assign dev_private so we can do cleanup. */ - dev->dev_private = (void *)dev_priv; - mga_do_cleanup_dma( dev ); + if (mga_freelist_init(dev, dev_priv) < 0) { + DRM_ERROR("could not initialize freelist\n"); return DRM_ERR(ENOMEM); } - /* Make dev_private visable to others. */ - dev->dev_private = (void *)dev_priv; return 0; } -int mga_do_cleanup_dma( drm_device_t *dev ) +static int mga_do_cleanup_dma(drm_device_t *dev, int full_cleanup) { - DRM_DEBUG( "\n" ); + int err = 0; + DRM_DEBUG("\n"); -#if _HAVE_DMA_IRQ /* Make sure interrupts are disabled here because the uninstall ioctl * may not have been called from userspace and after dev_private * is freed, it's too late. */ - if ( dev->irq ) DRM(irq_uninstall)(dev); -#endif + if (dev->irq_enabled) + drm_irq_uninstall(dev); - if ( dev->dev_private ) { + if (dev->dev_private) { drm_mga_private_t *dev_priv = dev->dev_private; - if ( dev_priv->warp != NULL ) - DRM_IOREMAPFREE( dev_priv->warp, dev ); - if ( dev_priv->primary != NULL ) - DRM_IOREMAPFREE( dev_priv->primary, dev ); - if ( dev_priv->buffers != NULL ) - DRM_IOREMAPFREE( dev_priv->buffers, dev ); + if ((dev_priv->warp != NULL) + && (dev_priv->warp->type != _DRM_CONSISTENT)) + drm_core_ioremapfree(dev_priv->warp, dev); + + if ((dev_priv->primary != NULL) + && (dev_priv->primary->type != _DRM_CONSISTENT)) + drm_core_ioremapfree(dev_priv->primary, dev); + + if (dev->agp_buffer_map != NULL) + drm_core_ioremapfree(dev->agp_buffer_map, dev); + + if (dev_priv->used_new_dma_init) { +#if __OS_HAS_AGP + if (dev_priv->agp_handle != 0) { + drm_agp_binding_t unbind_req; + drm_agp_buffer_t free_req; + + unbind_req.handle = dev_priv->agp_handle; + drm_agp_unbind(dev, &unbind_req); + + free_req.handle = dev_priv->agp_handle; + drm_agp_free(dev, &free_req); + + dev_priv->agp_textures = NULL; + dev_priv->agp_size = 0; + dev_priv->agp_handle = 0; + } + + if ((dev->agp != NULL) && dev->agp->acquired) { + err = drm_agp_release(dev); + } +#endif + } + + dev_priv->warp = NULL; + dev_priv->primary = NULL; + dev_priv->sarea = NULL; + dev_priv->sarea_priv = NULL; + dev->agp_buffer_map = NULL; - if ( dev_priv->head != NULL ) { - mga_freelist_cleanup( dev ); + if (full_cleanup) { + dev_priv->mmio = NULL; + dev_priv->status = NULL; + dev_priv->used_new_dma_init = 0; } - DRM(free)( dev->dev_private, sizeof(drm_mga_private_t), - DRM_MEM_DRIVER ); - dev->dev_private = NULL; + memset(&dev_priv->prim, 0, sizeof(dev_priv->prim)); + dev_priv->warp_pipe = 0; + memset(dev_priv->warp_pipe_phys, 0, + sizeof(dev_priv->warp_pipe_phys)); + + if (dev_priv->head != NULL) { + mga_freelist_cleanup(dev); + } } return 0; } -int mga_dma_init( DRM_IOCTL_ARGS ) +int mga_dma_init(DRM_IOCTL_ARGS) { DRM_DEVICE; drm_mga_init_t init; + int err; - LOCK_TEST_WITH_RETURN( dev, filp ); + LOCK_TEST_WITH_RETURN(dev, filp); - DRM_COPY_FROM_USER_IOCTL( init, (drm_mga_init_t *)data, sizeof(init) ); + DRM_COPY_FROM_USER_IOCTL(init, (drm_mga_init_t __user *) data, + sizeof(init)); - switch ( init.func ) { + switch (init.func) { case MGA_INIT_DMA: - return mga_do_init_dma( dev, &init ); + err = mga_do_init_dma(dev, &init); + if (err) { + (void)mga_do_cleanup_dma(dev, FULL_CLEANUP); + } + return err; case MGA_CLEANUP_DMA: - return mga_do_cleanup_dma( dev ); + return mga_do_cleanup_dma(dev, FULL_CLEANUP); } return DRM_ERR(EINVAL); } - /* ================================================================ * Primary DMA stream management */ -int mga_dma_flush( DRM_IOCTL_ARGS ) +int mga_dma_flush(DRM_IOCTL_ARGS) { DRM_DEVICE; - drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; drm_lock_t lock; - LOCK_TEST_WITH_RETURN( dev, filp ); + LOCK_TEST_WITH_RETURN(dev, filp); - DRM_COPY_FROM_USER_IOCTL( lock, (drm_lock_t *)data, sizeof(lock) ); + DRM_COPY_FROM_USER_IOCTL(lock, (drm_lock_t __user *) data, + sizeof(lock)); - DRM_DEBUG( "%s%s%s\n", - (lock.flags & _DRM_LOCK_FLUSH) ? "flush, " : "", - (lock.flags & _DRM_LOCK_FLUSH_ALL) ? "flush all, " : "", - (lock.flags & _DRM_LOCK_QUIESCENT) ? "idle, " : "" ); + DRM_DEBUG("%s%s%s\n", + (lock.flags & _DRM_LOCK_FLUSH) ? "flush, " : "", + (lock.flags & _DRM_LOCK_FLUSH_ALL) ? "flush all, " : "", + (lock.flags & _DRM_LOCK_QUIESCENT) ? "idle, " : ""); - WRAP_WAIT_WITH_RETURN( dev_priv ); + WRAP_WAIT_WITH_RETURN(dev_priv); - if ( lock.flags & (_DRM_LOCK_FLUSH | _DRM_LOCK_FLUSH_ALL) ) { - mga_do_dma_flush( dev_priv ); + if (lock.flags & (_DRM_LOCK_FLUSH | _DRM_LOCK_FLUSH_ALL)) { + mga_do_dma_flush(dev_priv); } - if ( lock.flags & _DRM_LOCK_QUIESCENT ) { + if (lock.flags & _DRM_LOCK_QUIESCENT) { #if MGA_DMA_DEBUG - int ret = mga_do_wait_for_idle( dev_priv ); - if ( ret < 0 ) - DRM_INFO( "%s: -EBUSY\n", __FUNCTION__ ); + int ret = mga_do_wait_for_idle(dev_priv); + if (ret < 0) + DRM_INFO("%s: -EBUSY\n", __FUNCTION__); return ret; #else - return mga_do_wait_for_idle( dev_priv ); + return mga_do_wait_for_idle(dev_priv); #endif } else { return 0; } } -int mga_dma_reset( DRM_IOCTL_ARGS ) +int mga_dma_reset(DRM_IOCTL_ARGS) { DRM_DEVICE; - drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; - LOCK_TEST_WITH_RETURN( dev, filp ); + LOCK_TEST_WITH_RETURN(dev, filp); - return mga_do_dma_reset( dev_priv ); + return mga_do_dma_reset(dev_priv); } - /* ================================================================ * DMA buffer management */ -static int mga_dma_get_buffers( DRMFILE filp, - drm_device_t *dev, drm_dma_t *d ) +static int mga_dma_get_buffers(DRMFILE filp, drm_device_t * dev, drm_dma_t * d) { drm_buf_t *buf; int i; - for ( i = d->granted_count ; i < d->request_count ; i++ ) { - buf = mga_freelist_get( dev ); - if ( !buf ) return DRM_ERR(EAGAIN); + for (i = d->granted_count; i < d->request_count; i++) { + buf = mga_freelist_get(dev); + if (!buf) + return DRM_ERR(EAGAIN); buf->filp = filp; - if ( DRM_COPY_TO_USER( &d->request_indices[i], - &buf->idx, sizeof(buf->idx) ) ) + if (DRM_COPY_TO_USER(&d->request_indices[i], + &buf->idx, sizeof(buf->idx))) return DRM_ERR(EFAULT); - if ( DRM_COPY_TO_USER( &d->request_sizes[i], - &buf->total, sizeof(buf->total) ) ) + if (DRM_COPY_TO_USER(&d->request_sizes[i], + &buf->total, sizeof(buf->total))) return DRM_ERR(EFAULT); d->granted_count++; @@ -767,43 +1111,69 @@ static int mga_dma_get_buffers( DRMFILE filp, return 0; } -int mga_dma_buffers( DRM_IOCTL_ARGS ) +int mga_dma_buffers(DRM_IOCTL_ARGS) { DRM_DEVICE; drm_device_dma_t *dma = dev->dma; - drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; + drm_dma_t __user *argp = (void __user *)data; drm_dma_t d; int ret = 0; - LOCK_TEST_WITH_RETURN( dev, filp ); + LOCK_TEST_WITH_RETURN(dev, filp); - DRM_COPY_FROM_USER_IOCTL( d, (drm_dma_t *)data, sizeof(d) ); + DRM_COPY_FROM_USER_IOCTL(d, argp, sizeof(d)); /* Please don't send us buffers. */ - if ( d.send_count != 0 ) { - DRM_ERROR( "Process %d trying to send %d buffers via drmDMA\n", - DRM_CURRENTPID, d.send_count ); + if (d.send_count != 0) { + DRM_ERROR("Process %d trying to send %d buffers via drmDMA\n", + DRM_CURRENTPID, d.send_count); return DRM_ERR(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", - DRM_CURRENTPID, d.request_count, dma->buf_count ); + if (d.request_count < 0 || d.request_count > dma->buf_count) { + DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n", + DRM_CURRENTPID, d.request_count, dma->buf_count); return DRM_ERR(EINVAL); } - WRAP_TEST_WITH_RETURN( dev_priv ); + WRAP_TEST_WITH_RETURN(dev_priv); d.granted_count = 0; - if ( d.request_count ) { - ret = mga_dma_get_buffers( filp, dev, &d ); + if (d.request_count) { + ret = mga_dma_get_buffers(filp, dev, &d); } - DRM_COPY_TO_USER_IOCTL( (drm_dma_t *)data, d, sizeof(d) ); + DRM_COPY_TO_USER_IOCTL(argp, d, sizeof(d)); return ret; } + +/** + * Called just before the module is unloaded. + */ +int mga_driver_unload(drm_device_t * dev) +{ + drm_free(dev->dev_private, sizeof(drm_mga_private_t), DRM_MEM_DRIVER); + dev->dev_private = NULL; + + return 0; +} + +/** + * Called when the last opener of the device is closed. + */ +void mga_driver_lastclose(drm_device_t * dev) +{ + mga_do_cleanup_dma(dev, FULL_CLEANUP); +} + +int mga_driver_dma_quiescent(drm_device_t * dev) +{ + drm_mga_private_t *dev_priv = dev->dev_private; + return mga_do_wait_for_idle(dev_priv); +}