/* mga_dma.c -- DMA support for mga g200/g400 -*- linux-c -*- * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * 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. * * Authors: Rickard E. (Rik) Faith * Jeff Hartmann * * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/mga_dma.c,v 1.1 2000/02/11 17:26:07 dawes Exp $ * */ #define __NO_VERSION__ #include "drmP.h" #include "mga_drv.h" #include "mgareg_flags.h" #include "mga_dma.h" #include "mga_state.h" #include /* For task queue support */ #define MGA_REG(reg) 2 #define MGA_BASE(reg) ((unsigned long) \ ((drm_device_t *)dev)->maplist[MGA_REG(reg)]->handle) #define MGA_ADDR(reg) (MGA_BASE(reg) + reg) #define MGA_DEREF(reg) *(__volatile__ int *)MGA_ADDR(reg) #define MGA_READ(reg) MGA_DEREF(reg) #define MGA_WRITE(reg,val) do { MGA_DEREF(reg) = val; } while (0) #define PDEA_pagpxfer_enable 0x2 #define MGA_SYNC_TAG 0x423f4200 typedef enum { TT_GENERAL, TT_BLIT, TT_VECTOR, TT_VERTEX } transferType_t; static void mga_delay(void) { return; } int mga_dma_cleanup(drm_device_t *dev) { if(dev->dev_private) { drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; if(dev_priv->ioremap) { int temp = (dev_priv->warp_ucode_size + dev_priv->primary_size + PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE; drm_ioremapfree((void *) dev_priv->ioremap, temp); } drm_free(dev->dev_private, sizeof(drm_mga_private_t), DRM_MEM_DRIVER); dev->dev_private = NULL; } return 0; } static int mga_alloc_kernel_queue(drm_device_t *dev) { drm_queue_t *queue = NULL; /* Allocate a new queue */ down(&dev->struct_sem); if(dev->queue_count != 0) { /* Reseting the kernel context here is not * a race, since it can only happen when that * queue is empty. */ queue = dev->queuelist[DRM_KERNEL_CONTEXT]; printk("Kernel queue already allocated\n"); } else { queue = drm_alloc(sizeof(*queue), DRM_MEM_QUEUES); if(!queue) { up(&dev->struct_sem); printk("out of memory\n"); return -ENOMEM; } ++dev->queue_count; dev->queuelist = drm_alloc(sizeof(*dev->queuelist), DRM_MEM_QUEUES); if(!dev->queuelist) { up(&dev->struct_sem); drm_free(queue, sizeof(*queue), DRM_MEM_QUEUES); printk("out of memory\n"); return -ENOMEM; } } memset(queue, 0, sizeof(*queue)); atomic_set(&queue->use_count, 1); atomic_set(&queue->finalization, 0); atomic_set(&queue->block_count, 0); atomic_set(&queue->block_read, 0); atomic_set(&queue->block_write, 0); atomic_set(&queue->total_queued, 0); atomic_set(&queue->total_flushed, 0); atomic_set(&queue->total_locks, 0); init_waitqueue_head(&queue->write_queue); init_waitqueue_head(&queue->read_queue); init_waitqueue_head(&queue->flush_queue); queue->flags = 0; drm_waitlist_create(&queue->waitlist, dev->dma->buf_count); dev->queue_slots = 1; dev->queuelist[DRM_KERNEL_CONTEXT] = queue; dev->queue_count--; up(&dev->struct_sem); printk("%d (new)\n", dev->queue_count - 1); return DRM_KERNEL_CONTEXT; } static int mga_dma_initialize(drm_device_t *dev, drm_mga_init_t *init) { drm_mga_private_t *dev_priv; drm_map_t *prim_map = NULL; drm_map_t *sarea_map = NULL; int temp; dev_priv = drm_alloc(sizeof(drm_mga_private_t), DRM_MEM_DRIVER); if(dev_priv == NULL) return -ENOMEM; dev->dev_private = (void *) dev_priv; printk("dev_private\n"); memset(dev_priv, 0, sizeof(drm_mga_private_t)); atomic_set(&dev_priv->pending_bufs, 0); if((init->reserved_map_idx >= dev->map_count) || (init->buffer_map_idx >= dev->map_count)) { mga_dma_cleanup(dev); printk("reserved_map or buffer_map are invalid\n"); return -EINVAL; } if(mga_alloc_kernel_queue(dev) != DRM_KERNEL_CONTEXT) { mga_dma_cleanup(dev); DRM_ERROR("Kernel context queue not present\n"); } dev_priv->reserved_map_idx = init->reserved_map_idx; dev_priv->buffer_map_idx = init->buffer_map_idx; sarea_map = dev->maplist[0]; dev_priv->sarea_priv = (drm_mga_sarea_t *) ((u8 *)sarea_map->handle + init->sarea_priv_offset); printk("sarea_priv\n"); /* Scale primary size to the next page */ dev_priv->primary_size = ((init->primary_size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE; dev_priv->warp_ucode_size = init->warp_ucode_size; dev_priv->chipset = init->chipset; dev_priv->fbOffset = init->fbOffset; dev_priv->backOffset = init->backOffset; dev_priv->depthOffset = init->depthOffset; dev_priv->textureOffset = init->textureOffset; dev_priv->textureSize = init->textureSize; dev_priv->cpp = init->cpp; dev_priv->sgram = init->sgram; dev_priv->stride = init->stride; dev_priv->frontOrg = init->frontOrg; dev_priv->backOrg = init->backOrg; dev_priv->depthOrg = init->depthOrg; dev_priv->mAccess = init->mAccess; printk("memcpy\n"); memcpy(&dev_priv->WarpIndex, &init->WarpIndex, sizeof(mgaWarpIndex) * MGA_MAX_WARP_PIPES); printk("memcpy done\n"); prim_map = dev->maplist[init->reserved_map_idx]; dev_priv->prim_phys_head = dev->agp->base + init->reserved_map_agpstart; temp = init->warp_ucode_size + dev_priv->primary_size; temp = ((temp + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE; printk("temp : %x\n", temp); printk("dev->agp->base: %lx\n", dev->agp->base); printk("init->reserved_map_agpstart: %x\n", init->reserved_map_agpstart); dev_priv->ioremap = drm_ioremap(dev->agp->base + init->reserved_map_agpstart, temp); if(dev_priv->ioremap == NULL) { printk("Ioremap failed\n"); mga_dma_cleanup(dev); return -ENOMEM; } dev_priv->prim_head = (u32 *)dev_priv->ioremap; printk("dev_priv->prim_head : %p\n", dev_priv->prim_head); dev_priv->current_dma_ptr = dev_priv->prim_head; dev_priv->prim_num_dwords = 0; dev_priv->prim_max_dwords = dev_priv->primary_size / 4; printk("dma initialization\n"); /* Private is now filled in, initialize the hardware */ { PRIMLOCALS; PRIMRESET( dev_priv ); PRIMGETPTR( dev_priv ); PRIMOUTREG(MGAREG_DMAPAD, 0); PRIMOUTREG(MGAREG_DMAPAD, 0); PRIMOUTREG(MGAREG_DWGSYNC, 0); PRIMOUTREG(MGAREG_SOFTRAP, 0); PRIMADVANCE( dev_priv ); /* Poll for the first buffer to insure that * the status register will be correct */ printk("phys_head : %lx\n", phys_head); MGA_WRITE(MGAREG_DWGSYNC, MGA_SYNC_TAG); while(MGA_READ(MGAREG_DWGSYNC) != MGA_SYNC_TAG) { int i; for(i = 0 ; i < 4096; i++) mga_delay(); } MGA_WRITE(MGAREG_PRIMADDRESS, phys_head | TT_GENERAL); MGA_WRITE(MGAREG_PRIMEND, ((phys_head + num_dwords * 4) | PDEA_pagpxfer_enable)); while(MGA_READ(MGAREG_DWGSYNC) == MGA_SYNC_TAG) { int i; for(i = 0; i < 4096; i++) mga_delay(); } } printk("dma init was successful\n"); return 0; } int mga_dma_init(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->dev; drm_mga_init_t init; copy_from_user_ret(&init, (drm_mga_init_t *)arg, sizeof(init), -EFAULT); switch(init.func) { case MGA_INIT_DMA: return mga_dma_initialize(dev, &init); case MGA_CLEANUP_DMA: return mga_dma_cleanup(dev); } return -EINVAL; } #define MGA_ILOAD_CMD (DC_opcod_iload | DC_atype_rpl | \ DC_linear_linear | DC_bltmod_bfcol | \ (0xC << DC_bop_SHIFT) | DC_sgnzero_enable | \ DC_shftzero_enable | DC_clipdis_enable) static void __mga_iload_small(drm_device_t *dev, drm_buf_t *buf, int use_agp) { drm_mga_private_t *dev_priv = dev->dev_private; drm_mga_buf_priv_t *buf_priv = buf->dev_private; drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; unsigned long address = (unsigned long)buf->bus_address; int length = buf->used; int y1 = buf_priv->boxes[0].y1; int x1 = buf_priv->boxes[0].x1; int y2 = buf_priv->boxes[0].y2; int x2 = buf_priv->boxes[0].x2; int dstorg = buf_priv->ContextState[MGA_CTXREG_DSTORG]; int maccess = buf_priv->ContextState[MGA_CTXREG_MACCESS]; PRIMLOCALS; PRIMRESET(dev_priv); PRIMGETPTR(dev_priv); PRIMOUTREG(MGAREG_DSTORG, dstorg | use_agp); PRIMOUTREG(MGAREG_MACCESS, maccess); PRIMOUTREG(MGAREG_PITCH, (1 << 15)); PRIMOUTREG(MGAREG_YDST, y1 * (x2 - x1)); PRIMOUTREG(MGAREG_LEN, 1); PRIMOUTREG(MGAREG_FXBNDRY, ((x2 - x1) * (y2 - y1) - 1) << 16); PRIMOUTREG(MGAREG_AR0, (x2 - x1) * (y2 - y1) - 1); PRIMOUTREG(MGAREG_AR3, 0); PRIMOUTREG(MGAREG_DMAPAD, 0); PRIMOUTREG(MGAREG_DMAPAD, 0); PRIMOUTREG(MGAREG_DMAPAD, 0); PRIMOUTREG(MGAREG_DWGCTL+MGAREG_MGA_EXEC, MGA_ILOAD_CMD); PRIMOUTREG(MGAREG_DMAPAD, 0); PRIMOUTREG(MGAREG_DMAPAD, 0); PRIMOUTREG(MGAREG_SECADDRESS, address | TT_BLIT); PRIMOUTREG(MGAREG_SECEND, (address + length) | use_agp); PRIMOUTREG(MGAREG_DMAPAD, 0); PRIMOUTREG(MGAREG_DMAPAD, 0); PRIMOUTREG(MGAREG_DWGSYNC, 0); PRIMOUTREG(MGAREG_SOFTRAP, 0); PRIMADVANCE(dev_priv); #if 0 /* For now we need to set this in the ioctl */ sarea_priv->dirty |= MGASAREA_NEW_CONTEXT; #endif MGA_WRITE(MGAREG_DWGSYNC, MGA_SYNC_TAG); while(MGA_READ(MGAREG_DWGSYNC) != MGA_SYNC_TAG) ; MGA_WRITE(MGAREG_PRIMADDRESS, dev_priv->prim_phys_head | TT_GENERAL); MGA_WRITE(MGAREG_PRIMEND, (phys_head + num_dwords * 4) | use_agp); } static void __mga_iload_xy(drm_device_t *dev, drm_buf_t *buf, int use_agp) { drm_mga_private_t *dev_priv = dev->dev_private; drm_mga_buf_priv_t *buf_priv = buf->dev_private; drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; unsigned long address = (unsigned long)buf->bus_address; int length = buf->used; int y1 = buf_priv->boxes[0].y1; int x1 = buf_priv->boxes[0].x1; int y2 = buf_priv->boxes[0].y2; int x2 = buf_priv->boxes[0].x2; int dstorg = buf_priv->ContextState[MGA_CTXREG_DSTORG]; int maccess = buf_priv->ContextState[MGA_CTXREG_MACCESS]; int pitch = buf_priv->ServerState[MGA_2DREG_PITCH]; int width, height; int texperdword = 0; PRIMLOCALS; width = (x2 - x1); height = (y2 - y1); switch((maccess & 0x00000003)) { case 0: texperdword = 4; break; case 1: texperdword = 2; break; case 2: texperdword = 1; break; default: DRM_ERROR("Invalid maccess value passed to __mga_iload_xy\n"); return; } x2 = x1 + width; x2 = (x2 + (texperdword - 1)) & ~(texperdword - 1); x1 = (x1 + (texperdword - 1)) & ~(texperdword - 1); width = x2 - x1; PRIMRESET(dev_priv); PRIMGETPTR(dev_priv); PRIMOUTREG(MGAREG_DSTORG, dstorg | use_agp); PRIMOUTREG(MGAREG_MACCESS, maccess); PRIMOUTREG(MGAREG_PITCH, pitch); PRIMOUTREG(MGAREG_YDSTLEN, (y1 << 16) | height); PRIMOUTREG(MGAREG_FXBNDRY, ((x1+width-1) << 16) | x1); PRIMOUTREG(MGAREG_AR0, width * height - 1); PRIMOUTREG(MGAREG_AR3, 0 ); PRIMOUTREG(MGAREG_DWGCTL+MGAREG_MGA_EXEC, MGA_ILOAD_CMD); PRIMOUTREG(MGAREG_DMAPAD, 0); PRIMOUTREG(MGAREG_DMAPAD, 0); PRIMOUTREG(MGAREG_SECADDRESS, address | TT_BLIT); PRIMOUTREG(MGAREG_SECEND, (address + length) | use_agp); PRIMOUTREG(MGAREG_DMAPAD, 0); PRIMOUTREG(MGAREG_DMAPAD, 0); PRIMOUTREG(MGAREG_DWGSYNC, 0); PRIMOUTREG(MGAREG_SOFTRAP, 0); PRIMADVANCE(dev_priv); #if 0 /* For now we need to set this in the ioctl */ sarea_priv->dirty |= MGASAREA_NEW_CONTEXT; #endif MGA_WRITE(MGAREG_DWGSYNC, MGA_SYNC_TAG); while(MGA_READ(MGAREG_DWGSYNC) != MGA_SYNC_TAG) ; MGA_WRITE(MGAREG_PRIMADDRESS, dev_priv->prim_phys_head | TT_GENERAL); MGA_WRITE(MGAREG_PRIMEND, (phys_head + num_dwords * 4) | use_agp); } static void mga_dma_dispatch_iload(drm_device_t *dev, drm_buf_t *buf) { drm_mga_buf_priv_t *buf_priv = buf->dev_private; int use_agp = PDEA_pagpxfer_enable; int x1 = buf_priv->boxes[0].x1; int x2 = buf_priv->boxes[0].x2; if((x2 - x1) < 32) { printk("using iload small\n"); __mga_iload_small(dev, buf, use_agp); } else { printk("using iload xy\n"); __mga_iload_xy(dev, buf, use_agp); } } static void mga_dma_dispatch_vertex(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; unsigned long address = (unsigned long)buf->bus_address; int length = buf->used; int use_agp = PDEA_pagpxfer_enable; int i, count; PRIMLOCALS; PRIMRESET(dev_priv); count = buf_priv->nbox; if (count == 0) count = 1; mgaEmitState( dev_priv, buf_priv ); for (i = 0 ; i < count ; i++) { if (i < buf_priv->nbox) mgaEmitClipRect( dev_priv, &buf_priv->boxes[i] ); PRIMGETPTR(dev_priv); PRIMOUTREG( MGAREG_DMAPAD, 0); PRIMOUTREG( MGAREG_DMAPAD, 0); PRIMOUTREG( MGAREG_SECADDRESS, address | TT_VERTEX); PRIMOUTREG( MGAREG_SECEND, (address + length) | use_agp); PRIMOUTREG( MGAREG_DMAPAD, 0); PRIMOUTREG( MGAREG_DMAPAD, 0); PRIMOUTREG( MGAREG_DWGSYNC, 0); PRIMOUTREG( MGAREG_SOFTRAP, 0); PRIMADVANCE(dev_priv); } PRIMGETPTR( dev_priv ); MGA_WRITE(MGAREG_DWGSYNC, MGA_SYNC_TAG); while(MGA_READ(MGAREG_DWGSYNC) != MGA_SYNC_TAG) ; MGA_WRITE(MGAREG_PRIMADDRESS, dev_priv->prim_phys_head | TT_GENERAL); MGA_WRITE(MGAREG_PRIMEND, (phys_head + num_dwords * 4) | use_agp); } /* Used internally for the small buffers generated from client state * information. */ static void mga_dma_dispatch_general(drm_device_t *dev, drm_buf_t *buf) { drm_mga_private_t *dev_priv = dev->dev_private; unsigned long address = (unsigned long)buf->bus_address; int length = buf->used; int use_agp = PDEA_pagpxfer_enable; PRIMLOCALS; PRIMRESET(dev_priv); PRIMGETPTR(dev_priv); PRIMOUTREG( MGAREG_DMAPAD, 0); PRIMOUTREG( MGAREG_DMAPAD, 0); PRIMOUTREG( MGAREG_SECADDRESS, address | TT_GENERAL); PRIMOUTREG( MGAREG_SECEND, (address + length) | use_agp); PRIMOUTREG( MGAREG_DMAPAD, 0); PRIMOUTREG( MGAREG_DMAPAD, 0); PRIMOUTREG( MGAREG_DWGSYNC, 0); PRIMOUTREG( MGAREG_SOFTRAP, 0); PRIMADVANCE(dev_priv); MGA_WRITE(MGAREG_DWGSYNC, MGA_SYNC_TAG); while(MGA_READ(MGAREG_DWGSYNC) != MGA_SYNC_TAG) ; MGA_WRITE(MGAREG_PRIMADDRESS, dev_priv->prim_phys_head | TT_GENERAL); MGA_WRITE(MGAREG_PRIMEND, (phys_head + num_dwords * 4) | use_agp); } /* Frees dispatch lock */ static inline void mga_dma_quiescent(drm_device_t *dev) { drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; while(1) { atomic_inc(&dev_priv->dispatch_lock); if(atomic_read(&dev_priv->dispatch_lock) == 1) { break; } else { atomic_dec(&dev_priv->dispatch_lock); } } while((MGA_READ(MGAREG_STATUS) & 0x00020001) != 0x00020000) ; #if 0 MGA_WRITE(MGAREG_DWGSYNC, MGA_SYNC_TAG); #endif while(MGA_READ(MGAREG_DWGSYNC) == MGA_SYNC_TAG) ; MGA_WRITE(MGAREG_DWGSYNC, MGA_SYNC_TAG); while(MGA_READ(MGAREG_DWGSYNC) != MGA_SYNC_TAG) ; atomic_dec(&dev_priv->dispatch_lock); } /* Keeps dispatch lock held */ static inline int mga_dma_is_ready(drm_device_t *dev) { drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; atomic_inc(&dev_priv->dispatch_lock); if(atomic_read(&dev_priv->dispatch_lock) == 1) { /* We got the lock */ return 1; } else { atomic_dec(&dev_priv->dispatch_lock); return 0; } } static inline int mga_dma_is_ready_no_hold(drm_device_t *dev) { drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; atomic_inc(&dev_priv->dispatch_lock); if(atomic_read(&dev_priv->dispatch_lock) == 1) { /* We got the lock, but free it */ atomic_dec(&dev_priv->dispatch_lock); return 1; } else { atomic_dec(&dev_priv->dispatch_lock); return 0; } } static void mga_dma_service(int irq, void *device, struct pt_regs *regs) { drm_device_t *dev = (drm_device_t *)device; drm_device_dma_t *dma = dev->dma; drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; atomic_dec(&dev_priv->dispatch_lock); atomic_inc(&dev->total_irq); MGA_WRITE(MGAREG_ICLEAR, 0xfa7); /* Free previous buffer */ if (test_and_set_bit(0, &dev->dma_flag)) { atomic_inc(&dma->total_missed_free); return; } if (dma->this_buffer) { drm_free_buffer(dev, dma->this_buffer); dma->this_buffer = NULL; } clear_bit(0, &dev->dma_flag); /* Dispatch new buffer */ queue_task(&dev->tq, &tq_immediate); mark_bh(IMMEDIATE_BH); } /* Only called by mga_dma_schedule. */ static int mga_do_dma(drm_device_t *dev, int locked) { drm_buf_t *buf; int retcode = 0; drm_device_dma_t *dma = dev->dma; drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; drm_mga_buf_priv_t *buf_priv; printk("mga_do_dma\n"); if (test_and_set_bit(0, &dev->dma_flag)) { atomic_inc(&dma->total_missed_dma); return -EBUSY; } if (!dma->next_buffer) { DRM_ERROR("No next_buffer\n"); clear_bit(0, &dev->dma_flag); return -EINVAL; } buf = dma->next_buffer; printk("context %d, buffer %d\n", buf->context, buf->idx); if (buf->list == DRM_LIST_RECLAIM) { drm_clear_next_buffer(dev); drm_free_buffer(dev, buf); clear_bit(0, &dev->dma_flag); return -EINVAL; } if (!buf->used) { DRM_ERROR("0 length buffer\n"); drm_clear_next_buffer(dev); drm_free_buffer(dev, buf); clear_bit(0, &dev->dma_flag); return 0; } if (mga_dma_is_ready(dev) == 0) { clear_bit(0, &dev->dma_flag); return -EBUSY; } /* Always hold the hardware lock while dispatching. */ if (!locked && !drm_lock_take(&dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT)) { atomic_inc(&dma->total_missed_lock); clear_bit(0, &dev->dma_flag); atomic_dec(&dev_priv->dispatch_lock); return -EBUSY; } dma->next_queue = dev->queuelist[DRM_KERNEL_CONTEXT]; drm_clear_next_buffer(dev); buf->pending = 1; buf->waiting = 0; buf->list = DRM_LIST_PEND; buf_priv = buf->dev_private; printk("dispatch!\n"); switch (buf_priv->dma_type) { case MGA_DMA_GENERAL: mga_dma_dispatch_general(dev, buf); break; case MGA_DMA_VERTEX: mga_dma_dispatch_vertex(dev, buf); break; /* case MGA_DMA_SETUP: */ /* mga_dma_dispatch_setup(dev, address, length); */ /* break; */ case MGA_DMA_ILOAD: mga_dma_dispatch_iload(dev, buf); break; default: printk("bad buffer type %x in dispatch\n", buf_priv->dma_type); break; } atomic_dec(&dev_priv->pending_bufs); drm_free_buffer(dev, dma->this_buffer); dma->this_buffer = buf; atomic_add(buf->used, &dma->total_bytes); atomic_inc(&dma->total_dmas); if (!locked) { if (drm_lock_free(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT)) { DRM_ERROR("\n"); } } clear_bit(0, &dev->dma_flag); if(!atomic_read(&dev_priv->pending_bufs)) { wake_up_interruptible(&dev->queuelist[DRM_KERNEL_CONTEXT]->flush_queue); } #if 0 wake_up_interruptible(&dev->lock.lock_queue); #endif /* We hold the dispatch lock until the interrupt handler * frees it */ return retcode; } static void mga_dma_schedule_timer_wrapper(unsigned long dev) { mga_dma_schedule((drm_device_t *)dev, 0); } static void mga_dma_schedule_tq_wrapper(void *dev) { mga_dma_schedule(dev, 0); } int mga_dma_schedule(drm_device_t *dev, int locked) { drm_queue_t *q; drm_buf_t *buf; int retcode = 0; int processed = 0; int missed; int expire = 20; drm_device_dma_t *dma = dev->dma; printk("mga_dma_schedule\n"); if (test_and_set_bit(0, &dev->interrupt_flag)) { /* Not reentrant */ atomic_inc(&dma->total_missed_sched); return -EBUSY; } missed = atomic_read(&dma->total_missed_sched); again: /* There is only one queue: */ if (!dma->next_buffer && DRM_WAITCOUNT(dev, DRM_KERNEL_CONTEXT)) { q = dev->queuelist[DRM_KERNEL_CONTEXT]; buf = drm_waitlist_get(&q->waitlist); dma->next_buffer = buf; dma->next_queue = q; if (buf && buf->list == DRM_LIST_RECLAIM) { drm_clear_next_buffer(dev); drm_free_buffer(dev, buf); } } if (dma->next_buffer) { if (!(retcode = mga_do_dma(dev, locked))) ++processed; } /* Try again if we succesfully dispatched a buffer, or if someone * tried to schedule while we were working. */ if (--expire) { if (missed != atomic_read(&dma->total_missed_sched)) { atomic_inc(&dma->total_lost); if (mga_dma_is_ready_no_hold(dev)) goto again; } if (processed && mga_dma_is_ready_no_hold(dev)) { atomic_inc(&dma->total_lost); processed = 0; goto again; } } clear_bit(0, &dev->interrupt_flag); return retcode; } int mga_irq_install(drm_device_t *dev, int irq) { int retcode; if (!irq) return -EINVAL; down(&dev->struct_sem); if (dev->irq) { up(&dev->struct_sem); return -EBUSY; } dev->irq = irq; up(&dev->struct_sem); printk("install irq handler %d\n", irq); dev->context_flag = 0; dev->interrupt_flag = 0; dev->dma_flag = 0; dev->dma->next_buffer = NULL; dev->dma->next_queue = NULL; dev->dma->this_buffer = NULL; dev->tq.next = NULL; dev->tq.sync = 0; dev->tq.routine = mga_dma_schedule_tq_wrapper; dev->tq.data = dev; /* Before installing handler */ MGA_WRITE(MGAREG_ICLEAR, 0xfa7); MGA_WRITE(MGAREG_IEN, 0); /* Install handler */ if ((retcode = request_irq(dev->irq, mga_dma_service, 0, dev->devname, dev))) { down(&dev->struct_sem); dev->irq = 0; up(&dev->struct_sem); return retcode; } /* After installing handler */ MGA_WRITE(MGAREG_ICLEAR, 0xfa7); MGA_WRITE(MGAREG_IEN, 0x00000001); return 0; } int mga_irq_uninstall(drm_device_t *dev) { int irq; down(&dev->struct_sem); irq = dev->irq; dev->irq = 0; up(&dev->struct_sem); if (!irq) return -EINVAL; printk("remove irq handler %d\n", irq); MGA_WRITE(MGAREG_ICLEAR, 0xfa7); MGA_WRITE(MGAREG_IEN, 0); MGA_WRITE(MGAREG_ICLEAR, 0xfa7); free_irq(irq, dev); return 0; } int mga_control(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->dev; drm_control_t ctl; copy_from_user_ret(&ctl, (drm_control_t *)arg, sizeof(ctl), -EFAULT); switch (ctl.func) { case DRM_INST_HANDLER: return mga_irq_install(dev, ctl.irq); case DRM_UNINST_HANDLER: return mga_irq_uninstall(dev); default: return -EINVAL; } } int mga_flush_queue(drm_device_t *dev) { DECLARE_WAITQUEUE(entry, current); drm_queue_t *q = dev->queuelist[DRM_KERNEL_CONTEXT]; drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; int ret = 0; printk("mga_flush_queue\n"); if(atomic_read(&dev_priv->pending_bufs) != 0) { current->state = TASK_INTERRUPTIBLE; add_wait_queue(&q->flush_queue, &entry); for (;;) { if (!atomic_read(&dev_priv->pending_bufs)) break; printk("Calling schedule from flush_queue : %d\n", atomic_read(&dev_priv->pending_bufs)); mga_dma_schedule(dev, 1); schedule(); if (signal_pending(current)) { ret = -EINTR; /* Can't restart */ break; } } printk("Exited out of schedule from flush_queue\n"); current->state = TASK_RUNNING; remove_wait_queue(&q->flush_queue, &entry); } return ret; } int mga_lock(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->dev; DECLARE_WAITQUEUE(entry, current); int ret = 0; drm_lock_t lock; copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT); if (lock.context == DRM_KERNEL_CONTEXT) { DRM_ERROR("Process %d using kernel context %d\n", current->pid, lock.context); return -EINVAL; } printk("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", lock.context, current->pid, dev->lock.hw_lock->lock, lock.flags); if (lock.context < 0) { return -EINVAL; } /* Only one queue: */ if (!ret) { add_wait_queue(&dev->lock.lock_queue, &entry); for (;;) { if (!dev->lock.hw_lock) { /* Device has been unregistered */ ret = -EINTR; break; } if (drm_lock_take(&dev->lock.hw_lock->lock, lock.context)) { dev->lock.pid = current->pid; dev->lock.lock_time = jiffies; atomic_inc(&dev->total_locks); break; /* Got lock */ } /* Contention */ atomic_inc(&dev->total_sleeps); current->state = TASK_INTERRUPTIBLE; current->policy |= SCHED_YIELD; printk("Calling lock schedule\n"); schedule(); if (signal_pending(current)) { ret = -ERESTARTSYS; break; } } current->state = TASK_RUNNING; remove_wait_queue(&dev->lock.lock_queue, &entry); } if (!ret) { if (lock.flags & _DRM_LOCK_QUIESCENT) { printk("_DRM_LOCK_QUIESCENT\n"); ret = mga_flush_queue(dev); if(ret != 0) { drm_lock_free(dev, &dev->lock.hw_lock->lock, lock.context); } else { mga_dma_quiescent(dev); } } } printk("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); return ret; }