/* i810_dma.c -- DMA support for the i810 -*- 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 * Keith Whitwell * * $XFree86$ * */ #define __NO_VERSION__ #include "drmP.h" #include "i810_drm_public.h" #include "i810_drv.h" #include "i810_dma.h" #include /* For task queue support */ #include /* For do_gettimeofday */ #define I810_REG(reg) 2 #define I810_BASE(reg) ((unsigned long) \ dev->maplist[I810_REG(reg)]->handle) #define I810_ADDR(reg) (I810_BASE(reg) + reg) #define I810_DEREF(reg) *(__volatile__ int *)I810_ADDR(reg) #define I810_READ(reg) I810_DEREF(reg) #define I810_WRITE(reg,val) do { I810_DEREF(reg) = val; } while (0) #define I810_DEREF16(reg) *(__volatile__ u16 *)I810_ADDR(reg) #define I810_READ16(reg) I810_DEREF16(reg) #define I810_WRITE16(reg,val) do { I810_DEREF16(reg) = val; } while (0) #define RING_LOCALS unsigned int outring, ringmask; volatile char *virt; #define BEGIN_LP_RING(n) do { \ if (I810_VERBOSE) \ DRM_DEBUG("BEGIN_LP_RING(%d) in %s\n", n, __FUNCTION__); \ if (dev_priv->ring.space < n*4) i810_wait_ring(dev, n*4, 0); \ dev_priv->ring.space -= n*4; \ outring = dev_priv->ring.tail; \ ringmask = dev_priv->ring.tail_mask; \ virt = dev_priv->ring.virtual_start; \ } while (0) #define ADVANCE_LP_RING() do { \ if (I810_VERBOSE) DRM_DEBUG("ADVANCE_LP_RING\n"); \ dev_priv->ring.tail = outring; \ I810_WRITE(LP_RING + RING_TAIL, outring); \ } while(0) #define OUT_RING(n) do { \ if (I810_VERBOSE) DRM_DEBUG(" OUT_RING %x\n", (int)(n)); \ *(volatile unsigned int *)(virt + outring) = n; \ outring += 4; \ outring &= ringmask; \ } while (0); static unsigned long i810_alloc_page(drm_device_t *dev) { unsigned long address; address = __get_free_page(GFP_KERNEL); if(address == 0UL) { return 0; } atomic_inc(&mem_map[MAP_NR((void *) address)].count); set_bit(PG_locked, &mem_map[MAP_NR((void *) address)].flags); return address; } static void i810_free_page(drm_device_t *dev, unsigned long page) { if(page == 0UL) { return; } atomic_dec(&mem_map[MAP_NR((void *) page)].count); clear_bit(PG_locked, &mem_map[MAP_NR((void *) page)].flags); wake_up(&mem_map[MAP_NR((void *) page)].wait); free_page(page); return; } static int i810_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]; DRM_DEBUG("Kernel queue already allocated\n"); } else { queue = drm_alloc(sizeof(*queue), DRM_MEM_QUEUES); if(!queue) { up(&dev->struct_sem); DRM_DEBUG("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); DRM_DEBUG("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); if (I810_VERBOSE) DRM_DEBUG("%d (new)\n", dev->queue_count - 1); return DRM_KERNEL_CONTEXT; } static int i810_dma_cleanup(drm_device_t *dev) { if (I810_VERBOSE) DRM_DEBUG("i810_dma_cleanup\n"); if(dev->dev_private) { drm_i810_private_t *dev_priv = (drm_i810_private_t *) dev->dev_private; if(dev_priv->ring.virtual_start) { drm_ioremapfree((void *) dev_priv->ring.virtual_start, dev_priv->ring.Size); } if(dev_priv->hw_status_page != 0UL) { i810_free_page(dev, dev_priv->hw_status_page); /* Need to rewrite hardware status page */ I810_WRITE(0x02080, 0x1ffff000); } drm_free(dev->dev_private, sizeof(drm_i810_private_t), DRM_MEM_DRIVER); dev->dev_private = NULL; } return 0; } static int __gettimeinmillis(void) { struct timeval timep; do_gettimeofday(&timep); return(timep.tv_sec * 1000) + (timep.tv_usec / 1000); } static int i810_wait_ring(drm_device_t *dev, int n, int timeout_millis) { drm_i810_private_t *dev_priv = dev->dev_private; drm_i810_ring_buffer_t *ring = &(dev_priv->ring); int iters = 0; int startTime = 0; int curTime = 0; if (timeout_millis == 0) timeout_millis = 3000; if (I810_VERBOSE) DRM_DEBUG( "i810_wait_ring %d\n", n); while (ring->space < n) { int i; ring->head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR; ring->space = ring->head - (ring->tail+8); if (ring->space < 0) ring->space += ring->Size; iters++; curTime = __gettimeinmillis(); if (startTime == 0 || curTime < startTime /*wrap case*/) { startTime = curTime; } else if (curTime - startTime > timeout_millis) { DRM_ERROR("space: %d wanted %d\n", ring->space, n); DRM_ERROR("lockup\n"); goto out_wait_ring; } for (i = 0 ; i < 2000 ; i++) ; } out_wait_ring: return iters; } static void i810_kernel_lost_context(drm_device_t *dev) { drm_i810_private_t *dev_priv = dev->dev_private; drm_i810_ring_buffer_t *ring = &(dev_priv->ring); if (0) DRM_DEBUG("i810_kernel_lost_context, old ring (%x,%x)\n", ring->head, ring->tail); ring->head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR; ring->tail = I810_READ(LP_RING + RING_TAIL); ring->space = ring->head - (ring->tail+8); if (ring->space < 0) ring->space += ring->Size; if (0) DRM_DEBUG("new ring (%x,%x)\n", ring->head, ring->tail); } static inline void i810_ring_write_status(drm_device_t *dev) { drm_i810_private_t *dev_priv = dev->dev_private; RING_LOCALS; i810_kernel_lost_context(dev); dev_priv->counter++; BEGIN_LP_RING(6); OUT_RING(CMD_REPORT_HEAD); OUT_RING(CMD_STORE_DWORD_IDX); OUT_RING(5 * sizeof(unsigned long)); OUT_RING(dev_priv->counter); OUT_RING(GFX_OP_BREAKPOINT_INTERRUPT); OUT_RING(0); ADVANCE_LP_RING(); /* Wait for ring to complete */ i810_wait_ring(dev, dev_priv->ring.Size - 8, 0); } static inline void i810_print_status_page(drm_device_t *dev) { drm_i810_private_t *dev_priv = dev->dev_private; u32 *temp = (u32 *)dev_priv->hw_status_page; DRM_DEBUG( "hw_status: Interrupt Status : %x\n", temp[0]); DRM_DEBUG( "hw_status: LpRing Head ptr : %x\n", temp[1]); DRM_DEBUG( "hw_status: IRing Head ptr : %x\n", temp[2]); DRM_DEBUG( "hw_status: Reserved : %x\n", temp[3]); DRM_DEBUG( "hw_status: Driver Counter : %d\n", temp[5]); } static int i810_dma_initialize(drm_device_t *dev, drm_i810_private_t *dev_priv, drm_i810_init_t *init) { drm_map_t *sarea_map; DRM_DEBUG( "i810_dma_init\n"); dev->dev_private = (void *) dev_priv; memset(dev_priv, 0, sizeof(drm_i810_private_t)); if (init->ring_map_idx >= dev->map_count || init->buffer_map_idx >= dev->map_count) { i810_dma_cleanup(dev); DRM_ERROR("ring_map or buffer_map are invalid\n"); return -EINVAL; } if (i810_alloc_kernel_queue(dev) != DRM_KERNEL_CONTEXT) { i810_dma_cleanup(dev); DRM_ERROR("Kernel context queue not present\n"); return -ENOMEM; } dev_priv->ring_map_idx = init->ring_map_idx; dev_priv->buffer_map_idx = init->buffer_map_idx; sarea_map = dev->maplist[0]; dev_priv->sarea_priv = (drm_i810_sarea_t *) ((u8 *)sarea_map->handle + init->sarea_priv_offset); atomic_set(&dev_priv->pending_bufs, 0); atomic_set(&dev_priv->dispatch_lock, 0); atomic_set(&dev_priv->in_flush, 0); dev_priv->ring.Start = init->ring_start; dev_priv->ring.End = init->ring_end; dev_priv->ring.Size = init->ring_size; dev_priv->ring.virtual_start = drm_ioremap(dev->agp->base + init->ring_start, init->ring_size); dev_priv->ring.tail_mask = dev_priv->ring.Size - 1; if (dev_priv->ring.virtual_start == NULL) { i810_dma_cleanup(dev); DRM_ERROR("can not ioremap virtual address for" " ring buffer\n"); return -ENOMEM; } /* Program Hardware Status Page */ dev_priv->hw_status_page = i810_alloc_page(dev); memset((void *) dev_priv->hw_status_page, 0, PAGE_SIZE); if(dev_priv->hw_status_page == 0UL) { i810_dma_cleanup(dev); DRM_ERROR("Can not allocate hardware status page\n"); return -ENOMEM; } DRM_DEBUG("hw status page @ %lx\n", dev_priv->hw_status_page); I810_WRITE(0x02080, virt_to_bus((void *)dev_priv->hw_status_page)); DRM_DEBUG("Enabled hardware status page\n"); #if 0 DRM_DEBUG("Doing first ring buffer write\n"); i810_ring_write_status(dev); DRM_DEBUG("First ring write succeeded\n"); i810_print_status_page(dev); DRM_DEBUG("Status page dump succeeded\n"); #endif return 0; } int i810_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_i810_private_t *dev_priv; drm_i810_init_t init; int retcode = 0; copy_from_user_ret(&init, (drm_i810_init_t *)arg, sizeof(init), -EFAULT); switch(init.func) { case I810_INIT_DMA: dev_priv = drm_alloc(sizeof(drm_i810_private_t), DRM_MEM_DRIVER); if(dev_priv == NULL) return -ENOMEM; retcode = i810_dma_initialize(dev, dev_priv, &init); break; case I810_CLEANUP_DMA: retcode = i810_dma_cleanup(dev); break; default: retcode = -EINVAL; break; } return retcode; } static void i810_dma_dispatch_general(drm_device_t *dev, drm_buf_t *buf) { drm_i810_buf_priv_t *buf_priv = buf->dev_private; drm_i810_private_t *dev_priv = dev->dev_private; drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv; unsigned long address = (unsigned long)buf->bus_address; unsigned long start = address - dev->agp->base; int length = buf->used; RING_LOCALS; sarea_priv->last_dispatch = buf_priv->age; dev_priv->counter++; DRM_DEBUG( "dispatch counter : %ld\n", dev_priv->counter); DRM_DEBUG( "i810_dma_dispatch\n"); DRM_DEBUG( "start : 0x%lx\n", start); DRM_DEBUG( "length : 0x%x\n", length); DRM_DEBUG( "start + length - 4 : 0x%lx\n", start + length - 4); i810_kernel_lost_context(dev); BEGIN_LP_RING(8); OUT_RING( CMD_OP_BATCH_BUFFER ); OUT_RING( start | BB1_PROTECTED ); OUT_RING( start + length - 4 ); OUT_RING( CMD_STORE_DWORD_IDX ); OUT_RING( 20 ); OUT_RING( dev_priv->counter ); OUT_RING( CMD_REPORT_HEAD ); OUT_RING( GFX_OP_BREAKPOINT_INTERRUPT ); ADVANCE_LP_RING(); /* i810_print_status_page(dev); */ } static void i810_dma_dispatch_vertex(drm_device_t *dev, drm_buf_t *buf) { drm_i810_private_t *dev_priv = dev->dev_private; drm_i810_buf_priv_t *buf_priv = buf->dev_private; drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv; drm_buf_t *real_buf = dev->dma->buflist[ buf_priv->vertex_real_idx ]; unsigned long address = (unsigned long)real_buf->bus_address; unsigned long start = address - dev->agp->base; xf86drmClipRectRec *box = buf_priv->boxes; int length = buf->used; int i = 0; RING_LOCALS; if (I810_VERBOSE) DRM_DEBUG("dispatch vertex addr 0x%lx, length 0x%x nbox %d\n", address, length, buf_priv->nbox); sarea_priv->last_dispatch = buf_priv->age; dev_priv->counter++; DRM_DEBUG( "dispatch counter : %ld\n", dev_priv->counter); DRM_DEBUG( "i810_dma_dispatch\n"); DRM_DEBUG( "start : %lx\n", start); DRM_DEBUG( "length : %d\n", length); DRM_DEBUG( "start + length - 4 : %ld\n", start + length - 4); i810_kernel_lost_context(dev); if (!buf_priv->vertex_discard) { do { if (i < buf_priv->nbox) { BEGIN_LP_RING(4); OUT_RING( GFX_OP_SCISSOR | SC_UPDATE_SCISSOR | SC_ENABLE ); OUT_RING( GFX_OP_SCISSOR_INFO ); OUT_RING( box[i].x1 | (box[i].y1 << 16) ); OUT_RING( box[i].x2 | (box[i].y2 << 16) ); ADVANCE_LP_RING(); } BEGIN_LP_RING(4); OUT_RING( CMD_OP_BATCH_BUFFER ); OUT_RING( start | BB1_PROTECTED ); OUT_RING( start + length - 4 ); OUT_RING( 0 ); ADVANCE_LP_RING(); } while (++i < buf_priv->nbox); } BEGIN_LP_RING( 6 ); OUT_RING( CMD_STORE_DWORD_IDX ); OUT_RING( 20 ); OUT_RING( dev_priv->counter ); OUT_RING( CMD_REPORT_HEAD ); OUT_RING( GFX_OP_BREAKPOINT_INTERRUPT ); OUT_RING( 0 ); ADVANCE_LP_RING(); } static inline void i810_dma_quiescent(drm_device_t *dev) { drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; /* DRM_DEBUG( "i810_dma_quiescent\n"); */ while(1) { atomic_inc(&dev_priv->dispatch_lock); if(atomic_read(&dev_priv->dispatch_lock) == 1) { break; } else { atomic_dec(&dev_priv->dispatch_lock); } } atomic_dec(&dev_priv->dispatch_lock); } static inline void i810_dma_ready(drm_device_t *dev) { i810_dma_quiescent(dev); /* DRM_DEBUG( "i810_dma_ready\n"); */ } static inline int i810_dma_is_ready(drm_device_t *dev) { drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; /* DRM_DEBUG( "i810_dma_is_ready\n"); */ 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 i810_dma_is_ready_no_hold(drm_device_t *dev) { drm_i810_private_t *dev_priv = (drm_i810_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 i810_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_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; u16 temp; atomic_inc(&dev->total_irq); /* DRM_DEBUG("Interrupt Handler\n"); */ /* i810_print_status_page(dev); */ temp = I810_READ16(I810REG_INT_IDENTITY_R); temp = temp & ~(0x6000); if(temp != 0) I810_WRITE16(I810REG_INT_IDENTITY_R, temp); /* Clear all interrupts */ atomic_dec(&dev_priv->dispatch_lock); /* 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 i810_dma_schedule. */ static int i810_do_dma(drm_device_t *dev, int locked) { drm_buf_t *buf; int retcode = 0; drm_device_dma_t *dma = dev->dma; drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; drm_i810_buf_priv_t *buf_priv; DRM_DEBUG("i810_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; DRM_DEBUG("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); atomic_dec(&dev_priv->pending_bufs); DRM_DEBUG("i810_do_dma: pending %d\n",atomic_read( &dev_priv->pending_bufs )); if(!(atomic_read(&dev_priv->pending_bufs))) { wake_up_interruptible(&dev->queuelist[DRM_KERNEL_CONTEXT]->flush_queue); } clear_bit(0, &dev->dma_flag); DRM_DEBUG("RECLIAM\n"); 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); DRM_DEBUG("NOT USED\n"); return 0; } if (i810_dma_is_ready(dev) == 0) { clear_bit(0, &dev->dma_flag); DRM_DEBUG("NOT READY\n"); return -EBUSY; } DRM_DEBUG("--inflush: %d\n", atomic_read(&dev_priv->in_flush)); if ( !locked && !atomic_read(&dev_priv->in_flush) && !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); DRM_DEBUG("NOT LOCKED\n"); 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; DRM_DEBUG("i810_do_dma - type %d\n", buf_priv->dma_type); switch (buf_priv->dma_type) { case I810_DMA_GENERAL: i810_dma_dispatch_general( dev, buf ); break; case I810_DMA_VERTEX: i810_dma_dispatch_vertex( dev, buf ); break; default: DRM_DEBUG("bad buffer type %x in dispatch\n", buf_priv->dma_type); break; } DRM_DEBUG("DONE\n"); atomic_dec(&dev_priv->pending_bufs); if(dma->this_buffer) { drm_free_buffer(dev, dma->this_buffer); } dma->this_buffer = buf; atomic_add(buf->used, &dma->total_bytes); atomic_inc(&dma->total_dmas); DRM_DEBUG("inflush: %d\n", atomic_read(&dev_priv->in_flush)); if (!locked && !atomic_read(&dev_priv->in_flush)) { if (drm_lock_free(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT)) { DRM_ERROR("\n"); } } clear_bit(0, &dev->dma_flag); DRM_DEBUG("i810_do_dma2: pending %d\n",atomic_read( &dev_priv->pending_bufs )); if(!(atomic_read(&dev_priv->pending_bufs))) { wake_up_interruptible(&dev->queuelist[DRM_KERNEL_CONTEXT]->flush_queue); } /* We hold the dispatch lock until the interrupt handler * frees it */ return retcode; } static void i810_dma_schedule_tq_wrapper(void *dev) { drm_i810_private_t *dev_priv = ((drm_device_t *)dev)->dev_private; i810_dma_schedule(dev, atomic_read(&dev_priv->in_flush)); } int i810_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; drm_i810_private_t *dev_priv = dev->dev_private; DRM_DEBUG("i810_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_DEBUG("reclaiming in i810_dma_schedule\n"); drm_clear_next_buffer(dev); drm_free_buffer(dev, buf); atomic_dec(&dev_priv->pending_bufs); DRM_DEBUG("fred pending bufs : %d\n", atomic_read(&dev_priv->pending_bufs)); if(!(atomic_read(&dev_priv->pending_bufs))) { wake_up_interruptible(&dev->queuelist[DRM_KERNEL_CONTEXT]->flush_queue); } dma->next_buffer = NULL; goto again; } } if (dma->next_buffer) { if (!(retcode = i810_do_dma(dev, locked))) ++processed; } DRM_DEBUG("ixxx810_do_dma: pending %d\n",atomic_read( &dev_priv->pending_bufs )); if(!(atomic_read(&dev_priv->pending_bufs))) { wake_up_interruptible(&dev->queuelist[DRM_KERNEL_CONTEXT]->flush_queue); } /* 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 (i810_dma_is_ready_no_hold(dev)) goto again; } if (processed && i810_dma_is_ready_no_hold(dev)) { atomic_inc(&dma->total_lost); processed = 0; goto again; } } clear_bit(0, &dev->interrupt_flag); return retcode; } int i810_irq_install(drm_device_t *dev, int irq) { int retcode; u16 temp; if (!irq) return -EINVAL; down(&dev->struct_sem); if (dev->irq) { up(&dev->struct_sem); return -EBUSY; } dev->irq = irq; up(&dev->struct_sem); DRM_DEBUG( "Interrupt Install : %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 = i810_dma_schedule_tq_wrapper; dev->tq.data = dev; /* Before installing handler */ temp = I810_READ16(I810REG_HWSTAM); temp = temp & 0x6000; I810_WRITE16(I810REG_HWSTAM, temp); temp = I810_READ16(I810REG_INT_MASK_R); temp = temp & 0x6000; I810_WRITE16(I810REG_INT_MASK_R, temp); /* Unmask interrupts */ temp = I810_READ16(I810REG_INT_ENABLE_R); temp = temp & 0x6000; I810_WRITE16(I810REG_INT_ENABLE_R, temp); /* Disable all interrupts */ /* Install handler */ if ((retcode = request_irq(dev->irq, i810_dma_service, 0, dev->devname, dev))) { down(&dev->struct_sem); dev->irq = 0; up(&dev->struct_sem); return retcode; } temp = I810_READ16(I810REG_INT_ENABLE_R); temp = temp & 0x6000; temp = temp | 0x0001; I810_WRITE16(I810REG_INT_ENABLE_R, 0x0001); /* Enable bp interrupts */ return 0; } int i810_irq_uninstall(drm_device_t *dev) { int irq; u16 temp; down(&dev->struct_sem); irq = dev->irq; dev->irq = 0; up(&dev->struct_sem); if (!irq) return -EINVAL; DRM_DEBUG( "Interrupt UnInstall: %d\n", irq); temp = I810_READ16(I810REG_INT_IDENTITY_R); temp = temp & ~(0x6000); if(temp != 0) I810_WRITE16(I810REG_INT_IDENTITY_R, temp); /* Clear all interrupts */ temp = I810_READ16(I810REG_INT_ENABLE_R); temp = temp & 0x6000; I810_WRITE16(I810REG_INT_ENABLE_R, temp); /* Disable all interrupts */ free_irq(irq, dev); return 0; } int i810_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; int retcode; /* DRM_DEBUG( "i810_control\n"); */ copy_from_user_ret(&ctl, (drm_control_t *)arg, sizeof(ctl), -EFAULT); switch (ctl.func) { case DRM_INST_HANDLER: if ((retcode = i810_irq_install(dev, ctl.irq))) return retcode; break; case DRM_UNINST_HANDLER: if ((retcode = i810_irq_uninstall(dev))) return retcode; break; default: return -EINVAL; } return 0; } int i810_flush_queue(drm_device_t *dev) { DECLARE_WAITQUEUE(entry, current); drm_queue_t *q = dev->queuelist[DRM_KERNEL_CONTEXT]; drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; int ret = 0; DRM_DEBUG("i810_flush_queue\n"); DRM_DEBUG("pending_bufs : %d\n", atomic_read(&dev_priv->pending_bufs)); if(atomic_read(&dev_priv->pending_bufs) != 0) { DRM_DEBUG("got to flush\n"); current->state = TASK_INTERRUPTIBLE; add_wait_queue(&q->flush_queue, &entry); for (;;) { if (!atomic_read(&dev_priv->pending_bufs)) break; DRM_DEBUG("Calling schedule from flush_queue : %d\n", atomic_read(&dev_priv->pending_bufs)); i810_dma_schedule(dev, 0); schedule_timeout(DRM_LOCK_SLICE); if (signal_pending(current)) { ret = -EINTR; /* Can't restart */ break; } } DRM_DEBUG("Exited out of schedule from flush_queue\n"); current->state = TASK_RUNNING; remove_wait_queue(&q->flush_queue, &entry); } return ret; } int i810_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; drm_i810_private_t *dev_priv = (drm_i810_private_t *) dev->dev_private; 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; } DRM_DEBUG("%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; DRM_DEBUG("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) { DRM_DEBUG("_DRM_LOCK_QUIESCENT\n"); atomic_set(&dev_priv->in_flush, 1); i810_flush_queue(dev); i810_dma_quiescent(dev); atomic_set(&dev_priv->in_flush, 0); } } DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); return ret; } int i810_flush_ioctl(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_i810_private_t *dev_priv = (drm_i810_private_t *) dev->dev_private; DRM_DEBUG("i810_flush_ioctl\n"); atomic_set(&dev_priv->in_flush, 1); i810_flush_queue(dev); i810_dma_quiescent(dev); atomic_set(&dev_priv->in_flush, 0); return 0; }