diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 69338ea3..8f50d90b 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -315,6 +315,7 @@ typedef struct drm_freelist { int low_mark; /* Low water mark */ int high_mark; /* High water mark */ atomic_t wfh; /* If waiting for high mark */ + atomic_t wait_count; /* Waiting counter */ } drm_freelist_t; typedef struct drm_buf_entry { diff --git a/linux-core/i810_dma.c b/linux-core/i810_dma.c index c1038fa1..22c43ba3 100644 --- a/linux-core/i810_dma.c +++ b/linux-core/i810_dma.c @@ -32,9 +32,11 @@ #define __NO_VERSION__ #include "drmP.h" +#include "i810_drm_public.h" #include "i810_drv.h" #include /* For task queue support */ +#include /* For do_gettimeofday */ #define I810_REG(reg) 2 #define I810_BASE(reg) ((unsigned long) \ @@ -43,88 +45,481 @@ #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) -void i810_dma_init(drm_device_t *dev) -{ - printk(KERN_INFO "i810_dma_init\n"); +#define RING_LOCALS() unsigned int outring, ringmask; volatile char *virt; +#define BEGIN_LP_RING(n) \ + 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; + +#define ADVANCE_LP_RING() { \ + dev_priv->ring.tail = outring; \ + I810_WRITE(LP_RING + RING_TAIL, outring); \ } -void i810_dma_cleanup(drm_device_t *dev) +#define OUT_RING(n) { \ + *(volatile unsigned int *)(virt + outring) = n; \ + outring += 4; \ + outring &= ringmask; \ +} + +static unsigned long i810_alloc_page(drm_device_t *dev) { - printk(KERN_INFO "i810_dma_cleanup\n"); + 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]; + 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 i810_dma_cleanup(drm_device_t *dev) +{ + 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 *ring = &(dev_priv->ring); + int iters = 0; + int startTime = 0; + int curTime = 0; + + if (timeout_millis == 0) timeout_millis = 3000; + + 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"); + } + + for (i = 0 ; i < 2000 ; i++) ; + } + + 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 *ring = &(dev_priv->ring); + + 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; + + 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++; +#if 1 + BEGIN_LP_RING(8); +#else + BEGIN_LP_RING(16); +#endif + OUT_RING(CMD_REPORT_HEAD); + OUT_RING(0); +#if 1 + OUT_RING(CMD_STORE_DWORD_IDX); + OUT_RING(5 * sizeof(unsigned long)); + OUT_RING(dev_priv->counter); + OUT_RING(0); + +#endif + /* Add a breakpoint interrupt at the end */ +#if 1 + OUT_RING(0); + OUT_RING(GFX_OP_BREAKPOINT_INTERRUPT); +#endif + /* Add a blit here just to see if my commands work */ +#if 0 + OUT_RING(0x02000000); + OUT_RING(0); + OUT_RING(0x50c00004); + OUT_RING(0xcc0800); + OUT_RING(0x1900320); + OUT_RING(0); + OUT_RING(0x800); + OUT_RING(0xb80000); + OUT_RING(0x02000000); + OUT_RING(0); +#endif + 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 : %lx\n", temp[0]); + DRM_DEBUG( "hw_status: LpRing Head ptr : %lx\n", temp[1]); + DRM_DEBUG( "hw_status: IRing Head ptr : %lx\n", temp[2]); + DRM_DEBUG( "hw_status: Reserved : %lx\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_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; + + 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 inline void i810_dma_dispatch(drm_device_t *dev, unsigned long address, unsigned long length) { - printk(KERN_INFO "i810_dma_dispatch\n"); + drm_i810_private_t *dev_priv = dev->dev_private; + unsigned long start = address - dev->agp->base; + RING_LOCALS(); + + dev_priv->counter++; + DRM_DEBUG( "dispatch counter : %d\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 : %d\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(5 * sizeof(unsigned long)); + OUT_RING(dev_priv->counter); + /* Add a breakpoint interrupt at the end */ + OUT_RING(CMD_REPORT_HEAD); + OUT_RING(GFX_OP_BREAKPOINT_INTERRUPT); + ADVANCE_LP_RING(); + + /* Wait for ring to flush */ +#if 0 + i810_wait_ring(dev, dev_priv->ring.Size - 8, 0); + i810_ring_write_status(dev); + DRM_DEBUG("ring write succeeded\n"); + i810_print_status_page(dev); + DRM_DEBUG("Status page dump succeeded\n"); +#endif + i810_print_status_page(dev); } 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); - printk(KERN_INFO "i810_dma_ready\n"); + i810_dma_quiescent(dev); + DRM_DEBUG( "i810_dma_ready\n"); } static inline int i810_dma_is_ready(drm_device_t *dev) { - - i810_dma_quiescent(dev); - - printk(KERN_INFO "i810_dma_is_ready\n"); - return 1; + 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); - if (i810_dma_is_ready(dev)) { + 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); + 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) { - unsigned long address; - unsigned long length; drm_buf_t *buf; int retcode = 0; drm_device_dma_t *dma = dev->dma; -#if DRM_DMA_HISTOGRAM - cycles_t dma_start, dma_stop; -#endif + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + unsigned long address; + unsigned long length; + printk("i810_do_dma\n"); if (test_and_set_bit(0, &dev->dma_flag)) { atomic_inc(&dma->total_missed_dma); return -EBUSY; } -#if DRM_DMA_HISTOGRAM - dma_start = get_cycles(); -#endif - if (!dma->next_buffer) { DRM_ERROR("No next_buffer\n"); clear_bit(0, &dev->dma_flag); @@ -132,21 +527,21 @@ static int i810_do_dma(drm_device_t *dev, int locked) } buf = dma->next_buffer; - address = (unsigned long)buf->bus_address; - length = buf->used; - - DRM_DEBUG("context %d, buffer %d (%ld bytes)\n", - buf->context, buf->idx, length); + 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); + atomic_dec(&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); return -EINVAL; } - if (!length) { + if (!buf->used) { DRM_ERROR("0 length buffer\n"); drm_clear_next_buffer(dev); drm_free_buffer(dev, buf); @@ -154,82 +549,63 @@ static int i810_do_dma(drm_device_t *dev, int locked) return 0; } - if (!i810_dma_is_ready(dev)) { + if (i810_dma_is_ready(dev) == 0) { clear_bit(0, &dev->dma_flag); return -EBUSY; } + + /* Always hold the hardware lock while dispatching. + */ - if (buf->while_locked) { - if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { - DRM_ERROR("Dispatching buffer %d from pid %d" - " \"while locked\", but no lock held\n", - buf->idx, buf->pid); - } - } else { - 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); - return -EBUSY; - } - } - - if (dev->last_context != buf->context - && !(dev->queuelist[buf->context]->flags - & _DRM_CONTEXT_PRESERVED)) { - /* PRE: dev->last_context != buf->context */ - if (drm_context_switch(dev, dev->last_context, buf->context)) { - drm_clear_next_buffer(dev); - drm_free_buffer(dev, buf); - } - retcode = -EBUSY; - goto cleanup; - - /* POST: we will wait for the context - switch and will dispatch on a later call - when dev->last_context == buf->context. - NOTE WE HOLD THE LOCK THROUGHOUT THIS - TIME! */ + 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; -#if DRM_DMA_HISTOGRAM - buf->time_dispatched = get_cycles(); -#endif + address = buf->bus_address; + length = buf->used; - i810_dma_dispatch(dev, address, length); - drm_free_buffer(dev, dma->this_buffer); + printk("dispatch!\n"); + i810_dma_dispatch(dev, address, length); + + atomic_dec(&dev_priv->pending_bufs); + + if(dma->this_buffer) { + drm_free_buffer(dev, dma->this_buffer); + } + dma->this_buffer = buf; - atomic_add(length, &dma->total_bytes); + atomic_add(buf->used, &dma->total_bytes); atomic_inc(&dma->total_dmas); - if (!buf->while_locked && !dev->context_flag && !locked) { + if (!locked) { if (drm_lock_free(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT)) { DRM_ERROR("\n"); } } -cleanup: clear_bit(0, &dev->dma_flag); - -#if DRM_DMA_HISTOGRAM - dma_stop = get_cycles(); - atomic_inc(&dev->histo.dma[drm_histogram_slot(dma_stop - dma_start)]); -#endif - + + 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_timer_wrapper(unsigned long dev) -{ - i810_dma_schedule((drm_device_t *)dev, 0); -} - static void i810_dma_schedule_tq_wrapper(void *dev) { i810_dma_schedule(dev, 0); @@ -237,7 +613,6 @@ static void i810_dma_schedule_tq_wrapper(void *dev) int i810_dma_schedule(drm_device_t *dev, int locked) { - int next; drm_queue_t *q; drm_buf_t *buf; int retcode = 0; @@ -245,9 +620,10 @@ int i810_dma_schedule(drm_device_t *dev, int locked) int missed; int expire = 20; drm_device_dma_t *dma = dev->dma; -#if DRM_DMA_HISTOGRAM - cycles_t schedule_start; -#endif + drm_i810_private_t *dev_priv = dev->dev_private; + + + printk("i810_dma_schedule\n"); if (test_and_set_bit(0, &dev->interrupt_flag)) { /* Not reentrant */ @@ -256,54 +632,48 @@ int i810_dma_schedule(drm_device_t *dev, int locked) } missed = atomic_read(&dma->total_missed_sched); -#if DRM_DMA_HISTOGRAM - schedule_start = get_cycles(); -#endif - again: - if (dev->context_flag) { - clear_bit(0, &dev->interrupt_flag); - return -EBUSY; - } - if (dma->next_buffer) { - /* Unsent buffer that was previously - selected, but that couldn't be sent - because the lock could not be obtained - or the DMA engine wasn't ready. Try - again. */ - atomic_inc(&dma->total_tried); - if (!(retcode = i810_do_dma(dev, locked))) { - atomic_inc(&dma->total_hit); - ++processed; - } - } else { - do { - next = drm_select_queue(dev, - i810_dma_schedule_timer_wrapper); - if (next >= 0) { - q = dev->queuelist[next]; - 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); - } - } - } while (next >= 0 && !dma->next_buffer); - if (dma->next_buffer) { - if (!(retcode = i810_do_dma(dev, locked))) { - ++processed; + /* 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) { + printk("reclaiming in i810_dma_schedule\n"); + drm_clear_next_buffer(dev); + drm_free_buffer(dev, buf); + atomic_dec(&dev_priv->pending_bufs); + printk("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; + } + 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(dev)) goto again; + if (i810_dma_is_ready_no_hold(dev)) + goto again; } - if (processed && i810_dma_is_ready(dev)) { + + if (processed && i810_dma_is_ready_no_hold(dev)) { atomic_inc(&dma->total_lost); processed = 0; goto again; @@ -312,154 +682,6 @@ again: clear_bit(0, &dev->interrupt_flag); -#if DRM_DMA_HISTOGRAM - atomic_inc(&dev->histo.schedule[drm_histogram_slot(get_cycles() - - schedule_start)]); -#endif - return retcode; -} - -static int i810_dma_priority(drm_device_t *dev, drm_dma_t *d) -{ - unsigned long address; - unsigned long length; - int must_free = 0; - int retcode = 0; - int i; - int idx; - drm_buf_t *buf; - drm_buf_t *last_buf = NULL; - drm_device_dma_t *dma = dev->dma; - DECLARE_WAITQUEUE(entry, current); - - /* Turn off interrupt handling */ - while (test_and_set_bit(0, &dev->interrupt_flag)) { - schedule(); - if (signal_pending(current)) return -EINTR; - } - if (!(d->flags & _DRM_DMA_WHILE_LOCKED)) { - while (!drm_lock_take(&dev->lock.hw_lock->lock, - DRM_KERNEL_CONTEXT)) { - schedule(); - if (signal_pending(current)) { - clear_bit(0, &dev->interrupt_flag); - return -EINTR; - } - } - ++must_free; - } - atomic_inc(&dma->total_prio); - - for (i = 0; i < d->send_count; i++) { - idx = d->send_indices[i]; - if (idx < 0 || idx >= dma->buf_count) { - DRM_ERROR("Index %d (of %d max)\n", - d->send_indices[i], dma->buf_count - 1); - continue; - } - buf = dma->buflist[ idx ]; - if (buf->pid != current->pid) { - DRM_ERROR("Process %d using buffer owned by %d\n", - current->pid, buf->pid); - retcode = -EINVAL; - goto cleanup; - } - if (buf->list != DRM_LIST_NONE) { - DRM_ERROR("Process %d using %d's buffer on list %d\n", - current->pid, buf->pid, buf->list); - retcode = -EINVAL; - goto cleanup; - } - /* This isn't a race condition on - buf->list, since our concern is the - buffer reclaim during the time the - process closes the /dev/drm? handle, so - it can't also be doing DMA. */ - buf->list = DRM_LIST_PRIO; - buf->used = d->send_sizes[i]; - buf->context = d->context; - buf->while_locked = d->flags & _DRM_DMA_WHILE_LOCKED; - address = (unsigned long)buf->address; - length = buf->used; - if (!length) { - DRM_ERROR("0 length buffer\n"); - } - if (buf->pending) { - DRM_ERROR("Sending pending buffer:" - " buffer %d, offset %d\n", - d->send_indices[i], i); - retcode = -EINVAL; - goto cleanup; - } - if (buf->waiting) { - DRM_ERROR("Sending waiting buffer:" - " buffer %d, offset %d\n", - d->send_indices[i], i); - retcode = -EINVAL; - goto cleanup; - } - buf->pending = 1; - - if (dev->last_context != buf->context - && !(dev->queuelist[buf->context]->flags - & _DRM_CONTEXT_PRESERVED)) { - add_wait_queue(&dev->context_wait, &entry); - current->state = TASK_INTERRUPTIBLE; - /* PRE: dev->last_context != buf->context */ - drm_context_switch(dev, dev->last_context, - buf->context); - /* POST: we will wait for the context - switch and will dispatch on a later call - when dev->last_context == buf->context. - NOTE WE HOLD THE LOCK THROUGHOUT THIS - TIME! */ - schedule(); - current->state = TASK_RUNNING; - remove_wait_queue(&dev->context_wait, &entry); - if (signal_pending(current)) { - retcode = -EINTR; - goto cleanup; - } - if (dev->last_context != buf->context) { - DRM_ERROR("Context mismatch: %d %d\n", - dev->last_context, - buf->context); - } - } - -#if DRM_DMA_HISTOGRAM - buf->time_queued = get_cycles(); - buf->time_dispatched = buf->time_queued; -#endif - i810_dma_dispatch(dev, address, length); - if (drm_lock_free(dev, &dev->lock.hw_lock->lock, - DRM_KERNEL_CONTEXT)) { - DRM_ERROR("\n"); - } - - atomic_add(length, &dma->total_bytes); - atomic_inc(&dma->total_dmas); - - if (last_buf) { - drm_free_buffer(dev, last_buf); - } - last_buf = buf; - } - - -cleanup: - if (last_buf) { - i810_dma_ready(dev); - drm_free_buffer(dev, last_buf); - } - - if (must_free && !dev->context_flag) { - if (drm_lock_free(dev, &dev->lock.hw_lock->lock, - DRM_KERNEL_CONTEXT)) { - DRM_ERROR("\n"); - } - } - clear_bit(0, &dev->interrupt_flag); return retcode; } @@ -469,55 +691,17 @@ static int i810_dma_send_buffers(drm_device_t *dev, drm_dma_t *d) drm_buf_t *last_buf = NULL; int retcode = 0; drm_device_dma_t *dma = dev->dma; + drm_i810_private_t *dev_priv = dev->dev_private; - if (d->flags & _DRM_DMA_BLOCK) { - last_buf = dma->buflist[d->send_indices[d->send_count-1]]; - add_wait_queue(&last_buf->dma_wait, &entry); - } + d->context = DRM_KERNEL_CONTEXT; if ((retcode = drm_dma_enqueue(dev, d))) { - if (d->flags & _DRM_DMA_BLOCK) - remove_wait_queue(&last_buf->dma_wait, &entry); return retcode; } - - i810_dma_schedule(dev, 0); - - if (d->flags & _DRM_DMA_BLOCK) { - DRM_DEBUG("%d waiting\n", current->pid); - current->state = TASK_INTERRUPTIBLE; - for (;;) { - if (!last_buf->waiting - && !last_buf->pending) - break; /* finished */ - schedule(); - if (signal_pending(current)) { - retcode = -EINTR; /* Can't restart */ - break; - } - } - current->state = TASK_RUNNING; - DRM_DEBUG("%d running\n", current->pid); - remove_wait_queue(&last_buf->dma_wait, &entry); - if (!retcode - || (last_buf->list==DRM_LIST_PEND && !last_buf->pending)) { - if (!waitqueue_active(&last_buf->dma_wait)) { - drm_free_buffer(dev, last_buf); - } - } - if (retcode) { - DRM_ERROR("ctx%d w%d p%d c%d i%d l%d %d/%d\n", - d->context, - last_buf->waiting, - last_buf->pending, - DRM_WAITCOUNT(dev, d->context), - last_buf->idx, - last_buf->list, - last_buf->pid, - current->pid); - } - } - return retcode; + + atomic_inc(&dev_priv->pending_bufs); + i810_dma_schedule(dev, 1); + return retcode; } int i810_dma(struct inode *inode, struct file *filp, unsigned int cmd, @@ -529,17 +713,11 @@ int i810_dma(struct inode *inode, struct file *filp, unsigned int cmd, int retcode = 0; drm_dma_t d; - printk("i810_dma start\n"); + DRM_DEBUG("i810_dma start\n"); copy_from_user_ret(&d, (drm_dma_t *)arg, sizeof(d), -EFAULT); DRM_DEBUG("%d %d: %d send, %d req\n", current->pid, d.context, d.send_count, d.request_count); - if (d.context == DRM_KERNEL_CONTEXT || d.context >= dev->queue_slots) { - DRM_ERROR("Process %d using context %d\n", - current->pid, d.context); - return -EINVAL; - } - if (d.send_count < 0 || d.send_count > dma->buf_count) { DRM_ERROR("Process %d trying to send %d buffers (of %d max)\n", current->pid, d.send_count, dma->buf_count); @@ -552,15 +730,7 @@ int i810_dma(struct inode *inode, struct file *filp, unsigned int cmd, } if (d.send_count) { -#if 0 - if (d.flags & _DRM_DMA_PRIORITY) - retcode = i810_dma_priority(dev, &d); - else - retcode = i810_dma_send_buffers(dev, &d); -#endif - printk("i810_dma priority\n"); - - retcode = i810_dma_priority(dev, &d); + retcode = i810_dma_send_buffers(dev, &d); } d.granted_count = 0; @@ -573,14 +743,15 @@ int i810_dma(struct inode *inode, struct file *filp, unsigned int cmd, current->pid, d.granted_count); copy_to_user_ret((drm_dma_t *)arg, &d, sizeof(d), -EFAULT); - printk("i810_dma end (granted)\n"); + DRM_DEBUG("i810_dma end (granted)\n"); return retcode; } int i810_irq_install(drm_device_t *dev, int irq) { int retcode; - + u16 temp; + if (!irq) return -EINVAL; down(&dev->struct_sem); @@ -591,6 +762,7 @@ int i810_irq_install(drm_device_t *dev, int irq) dev->irq = irq; up(&dev->struct_sem); + DRM_DEBUG( "Interrupt Install : %d\n", irq); DRM_DEBUG("%d\n", irq); dev->context_flag = 0; @@ -606,9 +778,18 @@ int i810_irq_install(drm_device_t *dev, int irq) dev->tq.routine = i810_dma_schedule_tq_wrapper; dev->tq.data = dev; - /* Before installing handler */ - /* TODO */ + 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, @@ -620,15 +801,18 @@ int i810_irq_install(drm_device_t *dev, int irq) up(&dev->struct_sem); return retcode; } - - /* After installing handler */ - /* TODO */ + 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; @@ -636,16 +820,27 @@ int i810_irq_uninstall(drm_device_t *dev) up(&dev->struct_sem); if (!irq) return -EINVAL; + + DRM_DEBUG( "Interrupt UnInstall: %d\n", irq); + DRM_DEBUG("%d\n", irq); - - /* TODO : Disable interrupts */ + + 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) { @@ -654,8 +849,7 @@ int i810_control(struct inode *inode, struct file *filp, unsigned int cmd, drm_control_t ctl; int retcode; - printk(KERN_INFO "i810_control\n"); - i810_dma_init(dev); + DRM_DEBUG( "i810_control\n"); copy_from_user_ret(&ctl, (drm_control_t *)arg, sizeof(ctl), -EFAULT); @@ -674,20 +868,48 @@ int i810_control(struct inode *inode, struct file *filp, unsigned int cmd, 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; + + printk("i810_flush_queue\n"); + printk("pending_bufs : %d\n", atomic_read(&dev_priv->pending_bufs)); + if(atomic_read(&dev_priv->pending_bufs) != 0) { + printk("got to flush\n"); + 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)); + i810_dma_schedule(dev, 0); + 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 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; - drm_queue_t *q; -#if DRM_DMA_HISTOGRAM - cycles_t start; - - dev->lck_start = start = get_cycles(); -#endif copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT); @@ -696,26 +918,58 @@ int i810_lock(struct inode *inode, struct file *filp, unsigned int cmd, 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 || lock.context >= dev->queue_count) { + if (lock.context < 0) { return -EINVAL; } - q = dev->queuelist[lock.context]; - - ret = drm_flush_block_and_flush(dev, lock.context, lock.flags); + + atomic_inc(&dev_priv->in_flush); + printk("in_flush : %d\n", atomic_read(&dev_priv->in_flush)); + if(atomic_read(&dev_priv->in_flush) != 1) { + atomic_dec(&dev_priv->in_flush); + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + /* Contention */ + atomic_inc(&dev->total_sleeps); + current->state = TASK_INTERRUPTIBLE; + current->policy |= SCHED_YIELD; + atomic_inc(&dev_priv->in_flush); + + printk("in_flush_loop : %d\n", atomic_read(&dev_priv->in_flush)); + if(atomic_read(&dev_priv->in_flush) == 1) { + break; + } + atomic_dec(&dev_priv->in_flush); + 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 (lock.flags & _DRM_LOCK_QUIESCENT) { + ret = i810_flush_queue(dev); + if(ret != 0) { + atomic_dec(&dev_priv->in_flush); + wake_up_interruptible(&dev->lock.lock_queue); + } + } else if (ret == 0) { + atomic_dec(&dev_priv->in_flush); + wake_up_interruptible(&dev->lock.lock_queue); + } + + /* Only one queue: + */ if (!ret) { - if (_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) - != lock.context) { - long j = jiffies - dev->lock.lock_time; - - if (j > 0 && j <= DRM_LOCK_SLICE) { - /* Can't take lock if we just had it and - there is contention. */ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(j); - } - } add_wait_queue(&dev->lock.lock_queue, &entry); for (;;) { if (!dev->lock.hw_lock) { @@ -728,13 +982,14 @@ int i810_lock(struct inode *inode, struct file *filp, unsigned int cmd, dev->lock.pid = current->pid; dev->lock.lock_time = jiffies; atomic_inc(&dev->total_locks); - atomic_inc(&q->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; @@ -744,19 +999,15 @@ int i810_lock(struct inode *inode, struct file *filp, unsigned int cmd, current->state = TASK_RUNNING; remove_wait_queue(&dev->lock.lock_queue, &entry); } - - drm_flush_unblock(dev, lock.context, lock.flags); /* cleanup phase */ if (!ret) { - if (lock.flags & _DRM_LOCK_READY) - i810_dma_ready(dev); - if (lock.flags & _DRM_LOCK_QUIESCENT) - i810_dma_quiescent(dev); + if (lock.flags & _DRM_LOCK_QUIESCENT) { + printk("_DRM_LOCK_QUIESCENT\n"); + i810_dma_quiescent(dev); + atomic_dec(&dev_priv->in_flush); + wake_up_interruptible(&dev->lock.lock_queue); + } } - -#if DRM_DMA_HISTOGRAM - atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]); -#endif - + printk("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); return ret; } diff --git a/linux-core/i810_drv.c b/linux-core/i810_drv.c index 70293575..15c68ddf 100644 --- a/linux-core/i810_drv.c +++ b/linux-core/i810_drv.c @@ -37,7 +37,7 @@ EXPORT_SYMBOL(i810_init); EXPORT_SYMBOL(i810_cleanup); #define I810_NAME "i810" -#define I810_DESC "Matrox g200/g400" +#define I810_DESC "Intel I810" #define I810_DATE "19991213" #define I810_MAJOR 0 #define I810_MINOR 0 @@ -80,13 +80,13 @@ static drm_ioctl_desc_t i810_ioctls[] = { [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)] = { i810_mapbufs, 1, 0 }, [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)] = { i810_freebufs, 1, 0 }, - [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { drm_addctx, 1, 1 }, - [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { drm_rmctx, 1, 1 }, - [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { drm_modctx, 1, 1 }, - [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { drm_getctx, 1, 0 }, - [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { drm_switchctx, 1, 1 }, - [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { drm_newctx, 1, 1 }, - [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { drm_resctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { i810_addctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { i810_rmctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { i810_modctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { i810_getctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { i810_switchctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { i810_newctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { i810_resctx, 1, 0 }, [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 }, [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 }, @@ -104,6 +104,7 @@ static drm_ioctl_desc_t i810_ioctls[] = { [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)] = { drm_agp_free, 1, 1 }, [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)] = { drm_agp_bind, 1, 1 }, [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = { drm_agp_unbind, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_INIT)] = { i810_dma_init, 1, 1 }, }; #define I810_IOCTL_COUNT DRM_ARRAY_SIZE(i810_ioctls) @@ -386,10 +387,6 @@ int i810_init(void) i810_takedown(dev); return retcode; } -#if 0 - printk("doing i810_dma_init\n"); - i810_dma_init(dev); -#endif DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", I810_NAME, @@ -417,7 +414,9 @@ void i810_cleanup(void) DRM_INFO("Module unloaded\n"); } drm_ctxbitmap_cleanup(dev); +#if 0 i810_dma_cleanup(dev); +#endif i810_takedown(dev); if (dev->agp) { drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS); diff --git a/linux-core/i810_drv.h b/linux-core/i810_drv.h index 1072b39a..82546976 100644 --- a/linux-core/i810_drv.h +++ b/linux-core/i810_drv.h @@ -1,4 +1,4 @@ -/* i810_drv.h -- Private header for the Matrox g200/g400 driver -*- linux-c -*- +/* i810_drv.h -- Private header for the i810 driver -*- linux-c -*- * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. @@ -31,6 +31,29 @@ #ifndef _I810_DRV_H_ #define _I810_DRV_H_ +#include "i810_drm_public.h" + +typedef struct _drm_i810_ring_buffer{ + int tail_mask; + unsigned long Start; + unsigned long End; + unsigned long Size; + u8 *virtual_start; + int head; + int tail; + int space; +} drm_i810_ring_buffer; + +typedef struct drm_i810_private { + int ring_map_idx; + int buffer_map_idx; + drm_i810_ring_buffer ring; + unsigned long hw_status_page; + unsigned long counter; + atomic_t dispatch_lock; + atomic_t pending_bufs; + atomic_t in_flush; +} drm_i810_private_t; /* i810_drv.c */ extern int i810_init(void); @@ -54,8 +77,8 @@ extern int i810_control(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); extern int i810_lock(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); -extern void i810_dma_init(drm_device_t *dev); -extern void i810_dma_cleanup(drm_device_t *dev); +extern int i810_dma_init(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); /* i810_bufs.c */ @@ -72,5 +95,61 @@ extern int i810_mapbufs(struct inode *inode, struct file *filp, extern int i810_addmap(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); + /* i810_context.c */ +extern int i810_resctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_addctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_modctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_getctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_switchctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_newctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_rmctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +extern int i810_context_switch(drm_device_t *dev, int old, int new); +extern int i810_context_switch_complete(drm_device_t *dev, int new); + + +#define GFX_OP_USER_INTERRUPT ((0<<29)|(2<<23)) +#define GFX_OP_BREAKPOINT_INTERRUPT ((0<<29)|(1<<23)) +#define CMD_REPORT_HEAD (7<<23) +#define CMD_STORE_DWORD_IDX ((0x21<<23) | 0x1) +#define CMD_OP_BATCH_BUFFER ((0x0<<29)|(0x30<<23)|0x1) + + +#define BB1_START_ADDR_MASK (~0x7) +#define BB1_PROTECTED (1<<0) +#define BB1_UNPROTECTED (0<<0) +#define BB2_END_ADDR_MASK (~0x7) + +#define I810REG_HWSTAM 0x02098 +#define I810REG_INT_IDENTITY_R 0x020a4 +#define I810REG_INT_MASK_R 0x020a8 +#define I810REG_INT_ENABLE_R 0x020a0 + +#define LP_RING 0x2030 +#define HP_RING 0x2040 +#define RING_TAIL 0x00 +#define TAIL_ADDR 0x000FFFF8 +#define RING_HEAD 0x04 +#define HEAD_WRAP_COUNT 0xFFE00000 +#define HEAD_WRAP_ONE 0x00200000 +#define HEAD_ADDR 0x001FFFFC +#define RING_START 0x08 +#define START_ADDR 0x00FFFFF8 +#define RING_LEN 0x0C +#define RING_NR_PAGES 0x000FF000 +#define RING_REPORT_MASK 0x00000006 +#define RING_REPORT_64K 0x00000002 +#define RING_REPORT_128K 0x00000004 +#define RING_NO_REPORT 0x00000000 +#define RING_VALID_MASK 0x00000001 +#define RING_VALID 0x00000001 +#define RING_INVALID 0x00000000 #endif diff --git a/linux/Makefile.linux b/linux/Makefile.linux index 5cc5334f..15556caf 100644 --- a/linux/Makefile.linux +++ b/linux/Makefile.linux @@ -78,7 +78,7 @@ MGAHEADERS= mga_drv.h mga_drm_public.h $(DRMHEADERS) R128OBJS= r128_drv.o r128_context.o R128HEADERS= r128_drv.h $(DRMHEADERS) -I810OBJS= i810_drv.o i810_dma.o i810_bufs.o +I810OBJS= i810_drv.o i810_dma.o i810_bufs.o i810_context.o I810HEADERS= i810_drv.h $(DRMHEADERS) PROGOBJS= drmstat.po xf86drm.po xf86drmHash.po xf86drmRandom.po sigio.po diff --git a/linux/dma.c b/linux/dma.c index 099311c2..121db00f 100644 --- a/linux/dma.c +++ b/linux/dma.c @@ -435,10 +435,6 @@ int drm_dma_enqueue(drm_device_t *dev, drm_dma_t *d) current->pid, buf->idx, buf->list); } buf->used = d->send_sizes[i]; -#if 0 - buf->bus_address = dev->agp->base + 65536 + - (idx * 524288); -#endif buf->while_locked = while_locked; buf->context = d->context; if (!buf->used) { diff --git a/linux/drmP.h b/linux/drmP.h index 69338ea3..8f50d90b 100644 --- a/linux/drmP.h +++ b/linux/drmP.h @@ -315,6 +315,7 @@ typedef struct drm_freelist { int low_mark; /* Low water mark */ int high_mark; /* High water mark */ atomic_t wfh; /* If waiting for high mark */ + atomic_t wait_count; /* Waiting counter */ } drm_freelist_t; typedef struct drm_buf_entry { diff --git a/linux/i810_bufs.c b/linux/i810_bufs.c index 8b0fc1da..a46cf3f1 100644 --- a/linux/i810_bufs.c +++ b/linux/i810_bufs.c @@ -32,6 +32,7 @@ #define __NO_VERSION__ #include "drmP.h" +#include "i810_drv.h" #include "linux/un.h" int i810_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd, @@ -55,6 +56,7 @@ int i810_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd, int i; if (!dma) return -EINVAL; + printk("dma : %p\n", dma); copy_from_user_ret(&request, (drm_buf_desc_t *)arg, @@ -69,7 +71,16 @@ int i810_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd, page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; total = PAGE_SIZE << page_order; byte_count = 0; - +#if 0 + printk("count: %d\n", count); + printk("order: %d\n", order); + printk("size: %d\n", size); + printk("agp_offset: %d\n", agp_offset); + printk("alignment: %d\n", alignment); + printk("page_order: %d\n", page_order); + printk("total: %d\n", total); + printk("byte_count: %d\n", byte_count); +#endif if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; if (dev->queue_count) return -EBUSY; /* Not while in use */ spin_lock(&dev->count_lock); @@ -99,33 +110,37 @@ int i810_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd, entry->buf_size = size; entry->page_order = page_order; + offset = 0; while(entry->buf_count < count) { - for(offset = 0; offset + size <= total && entry->buf_count < count; - offset += alignment, ++entry->buf_count) { - buf = &entry->buflist[entry->buf_count]; - buf->idx = dma->buf_count + entry->buf_count; - buf->total = alignment; - buf->order = order; - buf->used = 0; - buf->offset = agp_offset - dev->agp->base + offset;/* ?? */ - buf->bus_address = agp_offset + offset; - buf->address = agp_offset + offset + dev->agp->base; - buf->next = NULL; - buf->waiting = 0; - buf->pending = 0; - init_waitqueue_head(&buf->dma_wait); - buf->pid = 0; -#if DRM_DMA_HISTOGRAM - buf->time_queued = 0; - buf->time_dispatched = 0; - buf->time_completed = 0; - buf->time_freed = 0; + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; +#if 0 + printk("offset : %d\n", offset); #endif - DRM_DEBUG("buffer %d @ %p\n", - entry->buf_count, buf->address); - } + buf->offset = offset; /* Hrm */ + buf->bus_address = dev->agp->base + agp_offset + offset; + buf->address = (void *)(agp_offset + offset + dev->agp->base); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head(&buf->dma_wait); + buf->pid = 0; +#if DRM_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + offset = offset + alignment; + entry->buf_count++; byte_count += PAGE_SIZE << page_order; + + printk(KERN_INFO "buffer %d @ %p\n", + entry->buf_count, buf->address); } dma->buflist = drm_realloc(dma->buflist, @@ -137,8 +152,13 @@ int i810_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd, dma->buflist[i] = &entry->buflist[i - dma->buf_count]; dma->buf_count += entry->buf_count; - dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order); - +#if 0 + printk("dma->buf_count : %d\n", dma->buf_count); +#endif + dma->byte_count += byte_count; +#if 0 + printk("entry->buf_count : %d\n", entry->buf_count); +#endif drm_freelist_create(&entry->freelist, entry->buf_count); for (i = 0; i < entry->buf_count; i++) { drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]); @@ -155,6 +175,20 @@ int i810_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd, -EFAULT); atomic_dec(&dev->buf_alloc); +#if 0 + printk("count: %d\n", count); + printk("order: %d\n", order); + printk("size: %d\n", size); + printk("agp_offset: %d\n", agp_offset); + printk("alignment: %d\n", alignment); + printk("page_order: %d\n", page_order); + printk("total: %d\n", total); + printk("byte_count: %d\n", byte_count); +#endif + dma->flags = _DRM_DMA_USE_AGP; +#if 0 + printk("dma->flags : %lx\n", dma->flags); +#endif return 0; } @@ -506,6 +540,7 @@ int i810_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd, spin_lock(&dev->count_lock); if (atomic_read(&dev->buf_alloc)) { spin_unlock(&dev->count_lock); + printk("Buzy\n"); return -EBUSY; } ++dev->buf_use; /* Can't allocate more after this call */ @@ -515,70 +550,81 @@ int i810_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd, (drm_buf_map_t *)arg, sizeof(request), -EFAULT); - - if (request.count >= dma->buf_count) { - if(dma->flags & _DRM_DMA_USE_AGP) { - /* This is an ugly vicious hack */ - drm_map_t *map = NULL; - for(i = 0; i < dev->map_count; i++) { - map = dev->maplist[i]; - if(map->type == _DRM_AGP) break; - } - if (i >= dev->map_count || !map) { - retcode = -EINVAL; - goto done; - } - - virtual = do_mmap(filp, 0, map->size, PROT_READ|PROT_WRITE, - MAP_SHARED, (unsigned long)map->handle); - } - else { +#if 0 + printk("i810_mapbufs\n"); + printk("dma->flags : %lx\n", dma->flags); +#endif + if (request.count >= dma->buf_count) { + if(dma->flags & _DRM_DMA_USE_AGP) { + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + drm_map_t *map = NULL; + + map = dev->maplist[dev_priv->buffer_map_idx]; + if (!map) { + printk("map is null\n"); + retcode = -EINVAL; + goto done; + } +#if 0 + printk("map->offset : %lx\n", map->offset); + printk("map->size : %lx\n", map->size); + printk("map->type : %d\n", map->type); + printk("map->flags : %x\n", map->flags); + printk("map->handle : %lx\n", map->handle); + printk("map->mtrr : %d\n", map->mtrr); +#endif + virtual = do_mmap(filp, 0, map->size, PROT_READ|PROT_WRITE, + MAP_SHARED, (unsigned long)map->offset); + } else { virtual = do_mmap(filp, 0, dma->byte_count, PROT_READ|PROT_WRITE, MAP_SHARED, 0); - } - if (virtual > -1024UL) { - /* Real error */ - retcode = (signed long)virtual; - goto done; - } - request.virtual = (void *)virtual; - - for (i = 0; i < dma->buf_count; i++) { - if (copy_to_user(&request.list[i].idx, - &dma->buflist[i]->idx, - sizeof(request.list[0].idx))) { - retcode = -EFAULT; - goto done; - } - if (copy_to_user(&request.list[i].total, - &dma->buflist[i]->total, - sizeof(request.list[0].total))) { - retcode = -EFAULT; - goto done; - } - if (copy_to_user(&request.list[i].used, - &zero, - sizeof(zero))) { - retcode = -EFAULT; - goto done; - } - address = virtual + dma->buflist[i]->offset; - if (copy_to_user(&request.list[i].address, - &address, - sizeof(address))) { - retcode = -EFAULT; - goto done; - } - } - } -done: - request.count = dma->buf_count; - DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode); - - copy_to_user_ret((drm_buf_map_t *)arg, - &request, - sizeof(request), - -EFAULT); - - return retcode; + } + if (virtual > -1024UL) { + /* Real error */ + printk("mmap error\n"); + retcode = (signed long)virtual; + goto done; + } + request.virtual = (void *)virtual; + + for (i = 0; i < dma->buf_count; i++) { + if (copy_to_user(&request.list[i].idx, + &dma->buflist[i]->idx, + sizeof(request.list[0].idx))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].total, + &dma->buflist[i]->total, + sizeof(request.list[0].total))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].used, + &zero, + sizeof(zero))) { + retcode = -EFAULT; + goto done; + } + address = virtual + dma->buflist[i]->offset; + if (copy_to_user(&request.list[i].address, + &address, + sizeof(address))) { + retcode = -EFAULT; + goto done; + } + } + } + done: + request.count = dma->buf_count; + DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode); + + copy_to_user_ret((drm_buf_map_t *)arg, + &request, + sizeof(request), + -EFAULT); +#if 0 + printk("retcode : %d\n", retcode); +#endif + return retcode; } diff --git a/linux/i810_context.c b/linux/i810_context.c new file mode 100644 index 00000000..fca7b29a --- /dev/null +++ b/linux/i810_context.c @@ -0,0 +1,245 @@ +/* i810_context.c -- IOCTLs for i810 contexts -*- linux-c -*- + * Created: Mon Dec 13 09:51:35 1999 by faith@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. + * + * Author: Rickard E. (Rik) Faith + * + * $XFree86$ + * + */ + +#include + +#define __NO_VERSION__ +#include "drmP.h" +#include "i810_drv.h" + +static int i810_alloc_queue(drm_device_t *dev) +{ + int temp = drm_ctxbitmap_next(dev); + printk("i810_alloc_queue: %d\n", temp); + return temp; +} + +int i810_context_switch(drm_device_t *dev, int old, int new) +{ + char buf[64]; + + atomic_inc(&dev->total_ctx); + + if (test_and_set_bit(0, &dev->context_flag)) { + DRM_ERROR("Reentering -- FIXME\n"); + return -EBUSY; + } + +#if DRM_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + printk("Context switch from %d to %d\n", old, new); + + if (new == dev->last_context) { + clear_bit(0, &dev->context_flag); + return 0; + } + + if (drm_flags & DRM_FLAG_NOCTX) { + i810_context_switch_complete(dev, new); + } else { + sprintf(buf, "C %d %d\n", old, new); + drm_write_string(dev, buf); + } + + return 0; +} + +int i810_context_switch_complete(drm_device_t *dev, int new) +{ + dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ + dev->last_switch = jiffies; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("Lock isn't held after context switch\n"); + } + + /* If a context switch is ever initiated + when the kernel holds the lock, release + that lock here. */ +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles() + - dev->ctx_start)]); + +#endif + clear_bit(0, &dev->context_flag); + wake_up(&dev->context_wait); + + return 0; +} + +int i810_resctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + printk("%d\n", DRM_RESERVED_CONTEXTS); + copy_from_user_ret(&res, (drm_ctx_res_t *)arg, sizeof(res), -EFAULT); + if (res.count >= DRM_RESERVED_CONTEXTS) { + memset(&ctx, 0, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + copy_to_user_ret(&res.contexts[i], + &i, + sizeof(i), + -EFAULT); + } + } + res.count = DRM_RESERVED_CONTEXTS; + copy_to_user_ret((drm_ctx_res_t *)arg, &res, sizeof(res), -EFAULT); + return 0; +} + +int i810_addctx(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_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + if ((ctx.handle = i810_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) { + /* Skip kernel's context and get a new one. */ + ctx.handle = i810_alloc_queue(dev); + } + if (ctx.handle == -1) { + printk("Not enough free contexts.\n"); + /* Should this return -EBUSY instead? */ + return -ENOMEM; + } + printk("%d\n", ctx.handle); + copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT); + return 0; +} + +int i810_modctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + /* This does nothing for the i810 */ + return 0; +} + +int i810_getctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT); + /* This is 0, because we don't hanlde any context flags */ + ctx.flags = 0; + copy_to_user_ret((drm_ctx_t*)arg, &ctx, sizeof(ctx), -EFAULT); + return 0; +} + +int i810_switchctx(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_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + printk("%d\n", ctx.handle); + return i810_context_switch(dev, dev->last_context, ctx.handle); +} + +int i810_newctx(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_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + printk("%d\n", ctx.handle); + i810_context_switch_complete(dev, ctx.handle); + + return 0; +} + +int i810_rmctx(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_ctx_t ctx; + drm_queue_t *q; + drm_buf_t *buf; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + printk("%d\n", ctx.handle); + if(ctx.handle == DRM_KERNEL_CONTEXT) { + q = dev->queuelist[ctx.handle]; + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + /* No longer in use */ + atomic_dec(&q->use_count); + return -EINVAL; + } + atomic_inc(&q->finalization); /* Mark queue in finalization state */ + atomic_sub(2, &q->use_count); + /* Mark queue as unused (pending finalization) */ + + while (test_and_set_bit(0, &dev->interrupt_flag)) { + printk("Calling schedule from rmctx\n"); + schedule(); + if (signal_pending(current)) { + clear_bit(0, &dev->interrupt_flag); + return -EINTR; + } + } + + /* Remove queued buffers */ + while ((buf = drm_waitlist_get(&q->waitlist))) { + drm_free_buffer(dev, buf); + } + clear_bit(0, &dev->interrupt_flag); + + /* Wakeup blocked processes */ + wake_up_interruptible(&q->read_queue); + wake_up_interruptible(&q->write_queue); + wake_up_interruptible(&q->flush_queue); + + /* Finalization over. Queue is made + available when both use_count and + finalization become 0, which won't + happen until all the waiting processes + stop waiting. */ + atomic_dec(&q->finalization); + } else { + drm_ctxbitmap_free(dev, ctx.handle); + } + + return 0; +} diff --git a/linux/i810_dma.c b/linux/i810_dma.c index c1038fa1..22c43ba3 100644 --- a/linux/i810_dma.c +++ b/linux/i810_dma.c @@ -32,9 +32,11 @@ #define __NO_VERSION__ #include "drmP.h" +#include "i810_drm_public.h" #include "i810_drv.h" #include /* For task queue support */ +#include /* For do_gettimeofday */ #define I810_REG(reg) 2 #define I810_BASE(reg) ((unsigned long) \ @@ -43,88 +45,481 @@ #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) -void i810_dma_init(drm_device_t *dev) -{ - printk(KERN_INFO "i810_dma_init\n"); +#define RING_LOCALS() unsigned int outring, ringmask; volatile char *virt; +#define BEGIN_LP_RING(n) \ + 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; + +#define ADVANCE_LP_RING() { \ + dev_priv->ring.tail = outring; \ + I810_WRITE(LP_RING + RING_TAIL, outring); \ } -void i810_dma_cleanup(drm_device_t *dev) +#define OUT_RING(n) { \ + *(volatile unsigned int *)(virt + outring) = n; \ + outring += 4; \ + outring &= ringmask; \ +} + +static unsigned long i810_alloc_page(drm_device_t *dev) { - printk(KERN_INFO "i810_dma_cleanup\n"); + 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]; + 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 i810_dma_cleanup(drm_device_t *dev) +{ + 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 *ring = &(dev_priv->ring); + int iters = 0; + int startTime = 0; + int curTime = 0; + + if (timeout_millis == 0) timeout_millis = 3000; + + 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"); + } + + for (i = 0 ; i < 2000 ; i++) ; + } + + 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 *ring = &(dev_priv->ring); + + 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; + + 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++; +#if 1 + BEGIN_LP_RING(8); +#else + BEGIN_LP_RING(16); +#endif + OUT_RING(CMD_REPORT_HEAD); + OUT_RING(0); +#if 1 + OUT_RING(CMD_STORE_DWORD_IDX); + OUT_RING(5 * sizeof(unsigned long)); + OUT_RING(dev_priv->counter); + OUT_RING(0); + +#endif + /* Add a breakpoint interrupt at the end */ +#if 1 + OUT_RING(0); + OUT_RING(GFX_OP_BREAKPOINT_INTERRUPT); +#endif + /* Add a blit here just to see if my commands work */ +#if 0 + OUT_RING(0x02000000); + OUT_RING(0); + OUT_RING(0x50c00004); + OUT_RING(0xcc0800); + OUT_RING(0x1900320); + OUT_RING(0); + OUT_RING(0x800); + OUT_RING(0xb80000); + OUT_RING(0x02000000); + OUT_RING(0); +#endif + 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 : %lx\n", temp[0]); + DRM_DEBUG( "hw_status: LpRing Head ptr : %lx\n", temp[1]); + DRM_DEBUG( "hw_status: IRing Head ptr : %lx\n", temp[2]); + DRM_DEBUG( "hw_status: Reserved : %lx\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_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; + + 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 inline void i810_dma_dispatch(drm_device_t *dev, unsigned long address, unsigned long length) { - printk(KERN_INFO "i810_dma_dispatch\n"); + drm_i810_private_t *dev_priv = dev->dev_private; + unsigned long start = address - dev->agp->base; + RING_LOCALS(); + + dev_priv->counter++; + DRM_DEBUG( "dispatch counter : %d\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 : %d\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(5 * sizeof(unsigned long)); + OUT_RING(dev_priv->counter); + /* Add a breakpoint interrupt at the end */ + OUT_RING(CMD_REPORT_HEAD); + OUT_RING(GFX_OP_BREAKPOINT_INTERRUPT); + ADVANCE_LP_RING(); + + /* Wait for ring to flush */ +#if 0 + i810_wait_ring(dev, dev_priv->ring.Size - 8, 0); + i810_ring_write_status(dev); + DRM_DEBUG("ring write succeeded\n"); + i810_print_status_page(dev); + DRM_DEBUG("Status page dump succeeded\n"); +#endif + i810_print_status_page(dev); } 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); - printk(KERN_INFO "i810_dma_ready\n"); + i810_dma_quiescent(dev); + DRM_DEBUG( "i810_dma_ready\n"); } static inline int i810_dma_is_ready(drm_device_t *dev) { - - i810_dma_quiescent(dev); - - printk(KERN_INFO "i810_dma_is_ready\n"); - return 1; + 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); - if (i810_dma_is_ready(dev)) { + 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); + 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) { - unsigned long address; - unsigned long length; drm_buf_t *buf; int retcode = 0; drm_device_dma_t *dma = dev->dma; -#if DRM_DMA_HISTOGRAM - cycles_t dma_start, dma_stop; -#endif + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + unsigned long address; + unsigned long length; + printk("i810_do_dma\n"); if (test_and_set_bit(0, &dev->dma_flag)) { atomic_inc(&dma->total_missed_dma); return -EBUSY; } -#if DRM_DMA_HISTOGRAM - dma_start = get_cycles(); -#endif - if (!dma->next_buffer) { DRM_ERROR("No next_buffer\n"); clear_bit(0, &dev->dma_flag); @@ -132,21 +527,21 @@ static int i810_do_dma(drm_device_t *dev, int locked) } buf = dma->next_buffer; - address = (unsigned long)buf->bus_address; - length = buf->used; - - DRM_DEBUG("context %d, buffer %d (%ld bytes)\n", - buf->context, buf->idx, length); + 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); + atomic_dec(&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); return -EINVAL; } - if (!length) { + if (!buf->used) { DRM_ERROR("0 length buffer\n"); drm_clear_next_buffer(dev); drm_free_buffer(dev, buf); @@ -154,82 +549,63 @@ static int i810_do_dma(drm_device_t *dev, int locked) return 0; } - if (!i810_dma_is_ready(dev)) { + if (i810_dma_is_ready(dev) == 0) { clear_bit(0, &dev->dma_flag); return -EBUSY; } + + /* Always hold the hardware lock while dispatching. + */ - if (buf->while_locked) { - if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { - DRM_ERROR("Dispatching buffer %d from pid %d" - " \"while locked\", but no lock held\n", - buf->idx, buf->pid); - } - } else { - 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); - return -EBUSY; - } - } - - if (dev->last_context != buf->context - && !(dev->queuelist[buf->context]->flags - & _DRM_CONTEXT_PRESERVED)) { - /* PRE: dev->last_context != buf->context */ - if (drm_context_switch(dev, dev->last_context, buf->context)) { - drm_clear_next_buffer(dev); - drm_free_buffer(dev, buf); - } - retcode = -EBUSY; - goto cleanup; - - /* POST: we will wait for the context - switch and will dispatch on a later call - when dev->last_context == buf->context. - NOTE WE HOLD THE LOCK THROUGHOUT THIS - TIME! */ + 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; -#if DRM_DMA_HISTOGRAM - buf->time_dispatched = get_cycles(); -#endif + address = buf->bus_address; + length = buf->used; - i810_dma_dispatch(dev, address, length); - drm_free_buffer(dev, dma->this_buffer); + printk("dispatch!\n"); + i810_dma_dispatch(dev, address, length); + + atomic_dec(&dev_priv->pending_bufs); + + if(dma->this_buffer) { + drm_free_buffer(dev, dma->this_buffer); + } + dma->this_buffer = buf; - atomic_add(length, &dma->total_bytes); + atomic_add(buf->used, &dma->total_bytes); atomic_inc(&dma->total_dmas); - if (!buf->while_locked && !dev->context_flag && !locked) { + if (!locked) { if (drm_lock_free(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT)) { DRM_ERROR("\n"); } } -cleanup: clear_bit(0, &dev->dma_flag); - -#if DRM_DMA_HISTOGRAM - dma_stop = get_cycles(); - atomic_inc(&dev->histo.dma[drm_histogram_slot(dma_stop - dma_start)]); -#endif - + + 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_timer_wrapper(unsigned long dev) -{ - i810_dma_schedule((drm_device_t *)dev, 0); -} - static void i810_dma_schedule_tq_wrapper(void *dev) { i810_dma_schedule(dev, 0); @@ -237,7 +613,6 @@ static void i810_dma_schedule_tq_wrapper(void *dev) int i810_dma_schedule(drm_device_t *dev, int locked) { - int next; drm_queue_t *q; drm_buf_t *buf; int retcode = 0; @@ -245,9 +620,10 @@ int i810_dma_schedule(drm_device_t *dev, int locked) int missed; int expire = 20; drm_device_dma_t *dma = dev->dma; -#if DRM_DMA_HISTOGRAM - cycles_t schedule_start; -#endif + drm_i810_private_t *dev_priv = dev->dev_private; + + + printk("i810_dma_schedule\n"); if (test_and_set_bit(0, &dev->interrupt_flag)) { /* Not reentrant */ @@ -256,54 +632,48 @@ int i810_dma_schedule(drm_device_t *dev, int locked) } missed = atomic_read(&dma->total_missed_sched); -#if DRM_DMA_HISTOGRAM - schedule_start = get_cycles(); -#endif - again: - if (dev->context_flag) { - clear_bit(0, &dev->interrupt_flag); - return -EBUSY; - } - if (dma->next_buffer) { - /* Unsent buffer that was previously - selected, but that couldn't be sent - because the lock could not be obtained - or the DMA engine wasn't ready. Try - again. */ - atomic_inc(&dma->total_tried); - if (!(retcode = i810_do_dma(dev, locked))) { - atomic_inc(&dma->total_hit); - ++processed; - } - } else { - do { - next = drm_select_queue(dev, - i810_dma_schedule_timer_wrapper); - if (next >= 0) { - q = dev->queuelist[next]; - 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); - } - } - } while (next >= 0 && !dma->next_buffer); - if (dma->next_buffer) { - if (!(retcode = i810_do_dma(dev, locked))) { - ++processed; + /* 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) { + printk("reclaiming in i810_dma_schedule\n"); + drm_clear_next_buffer(dev); + drm_free_buffer(dev, buf); + atomic_dec(&dev_priv->pending_bufs); + printk("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; + } + 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(dev)) goto again; + if (i810_dma_is_ready_no_hold(dev)) + goto again; } - if (processed && i810_dma_is_ready(dev)) { + + if (processed && i810_dma_is_ready_no_hold(dev)) { atomic_inc(&dma->total_lost); processed = 0; goto again; @@ -312,154 +682,6 @@ again: clear_bit(0, &dev->interrupt_flag); -#if DRM_DMA_HISTOGRAM - atomic_inc(&dev->histo.schedule[drm_histogram_slot(get_cycles() - - schedule_start)]); -#endif - return retcode; -} - -static int i810_dma_priority(drm_device_t *dev, drm_dma_t *d) -{ - unsigned long address; - unsigned long length; - int must_free = 0; - int retcode = 0; - int i; - int idx; - drm_buf_t *buf; - drm_buf_t *last_buf = NULL; - drm_device_dma_t *dma = dev->dma; - DECLARE_WAITQUEUE(entry, current); - - /* Turn off interrupt handling */ - while (test_and_set_bit(0, &dev->interrupt_flag)) { - schedule(); - if (signal_pending(current)) return -EINTR; - } - if (!(d->flags & _DRM_DMA_WHILE_LOCKED)) { - while (!drm_lock_take(&dev->lock.hw_lock->lock, - DRM_KERNEL_CONTEXT)) { - schedule(); - if (signal_pending(current)) { - clear_bit(0, &dev->interrupt_flag); - return -EINTR; - } - } - ++must_free; - } - atomic_inc(&dma->total_prio); - - for (i = 0; i < d->send_count; i++) { - idx = d->send_indices[i]; - if (idx < 0 || idx >= dma->buf_count) { - DRM_ERROR("Index %d (of %d max)\n", - d->send_indices[i], dma->buf_count - 1); - continue; - } - buf = dma->buflist[ idx ]; - if (buf->pid != current->pid) { - DRM_ERROR("Process %d using buffer owned by %d\n", - current->pid, buf->pid); - retcode = -EINVAL; - goto cleanup; - } - if (buf->list != DRM_LIST_NONE) { - DRM_ERROR("Process %d using %d's buffer on list %d\n", - current->pid, buf->pid, buf->list); - retcode = -EINVAL; - goto cleanup; - } - /* This isn't a race condition on - buf->list, since our concern is the - buffer reclaim during the time the - process closes the /dev/drm? handle, so - it can't also be doing DMA. */ - buf->list = DRM_LIST_PRIO; - buf->used = d->send_sizes[i]; - buf->context = d->context; - buf->while_locked = d->flags & _DRM_DMA_WHILE_LOCKED; - address = (unsigned long)buf->address; - length = buf->used; - if (!length) { - DRM_ERROR("0 length buffer\n"); - } - if (buf->pending) { - DRM_ERROR("Sending pending buffer:" - " buffer %d, offset %d\n", - d->send_indices[i], i); - retcode = -EINVAL; - goto cleanup; - } - if (buf->waiting) { - DRM_ERROR("Sending waiting buffer:" - " buffer %d, offset %d\n", - d->send_indices[i], i); - retcode = -EINVAL; - goto cleanup; - } - buf->pending = 1; - - if (dev->last_context != buf->context - && !(dev->queuelist[buf->context]->flags - & _DRM_CONTEXT_PRESERVED)) { - add_wait_queue(&dev->context_wait, &entry); - current->state = TASK_INTERRUPTIBLE; - /* PRE: dev->last_context != buf->context */ - drm_context_switch(dev, dev->last_context, - buf->context); - /* POST: we will wait for the context - switch and will dispatch on a later call - when dev->last_context == buf->context. - NOTE WE HOLD THE LOCK THROUGHOUT THIS - TIME! */ - schedule(); - current->state = TASK_RUNNING; - remove_wait_queue(&dev->context_wait, &entry); - if (signal_pending(current)) { - retcode = -EINTR; - goto cleanup; - } - if (dev->last_context != buf->context) { - DRM_ERROR("Context mismatch: %d %d\n", - dev->last_context, - buf->context); - } - } - -#if DRM_DMA_HISTOGRAM - buf->time_queued = get_cycles(); - buf->time_dispatched = buf->time_queued; -#endif - i810_dma_dispatch(dev, address, length); - if (drm_lock_free(dev, &dev->lock.hw_lock->lock, - DRM_KERNEL_CONTEXT)) { - DRM_ERROR("\n"); - } - - atomic_add(length, &dma->total_bytes); - atomic_inc(&dma->total_dmas); - - if (last_buf) { - drm_free_buffer(dev, last_buf); - } - last_buf = buf; - } - - -cleanup: - if (last_buf) { - i810_dma_ready(dev); - drm_free_buffer(dev, last_buf); - } - - if (must_free && !dev->context_flag) { - if (drm_lock_free(dev, &dev->lock.hw_lock->lock, - DRM_KERNEL_CONTEXT)) { - DRM_ERROR("\n"); - } - } - clear_bit(0, &dev->interrupt_flag); return retcode; } @@ -469,55 +691,17 @@ static int i810_dma_send_buffers(drm_device_t *dev, drm_dma_t *d) drm_buf_t *last_buf = NULL; int retcode = 0; drm_device_dma_t *dma = dev->dma; + drm_i810_private_t *dev_priv = dev->dev_private; - if (d->flags & _DRM_DMA_BLOCK) { - last_buf = dma->buflist[d->send_indices[d->send_count-1]]; - add_wait_queue(&last_buf->dma_wait, &entry); - } + d->context = DRM_KERNEL_CONTEXT; if ((retcode = drm_dma_enqueue(dev, d))) { - if (d->flags & _DRM_DMA_BLOCK) - remove_wait_queue(&last_buf->dma_wait, &entry); return retcode; } - - i810_dma_schedule(dev, 0); - - if (d->flags & _DRM_DMA_BLOCK) { - DRM_DEBUG("%d waiting\n", current->pid); - current->state = TASK_INTERRUPTIBLE; - for (;;) { - if (!last_buf->waiting - && !last_buf->pending) - break; /* finished */ - schedule(); - if (signal_pending(current)) { - retcode = -EINTR; /* Can't restart */ - break; - } - } - current->state = TASK_RUNNING; - DRM_DEBUG("%d running\n", current->pid); - remove_wait_queue(&last_buf->dma_wait, &entry); - if (!retcode - || (last_buf->list==DRM_LIST_PEND && !last_buf->pending)) { - if (!waitqueue_active(&last_buf->dma_wait)) { - drm_free_buffer(dev, last_buf); - } - } - if (retcode) { - DRM_ERROR("ctx%d w%d p%d c%d i%d l%d %d/%d\n", - d->context, - last_buf->waiting, - last_buf->pending, - DRM_WAITCOUNT(dev, d->context), - last_buf->idx, - last_buf->list, - last_buf->pid, - current->pid); - } - } - return retcode; + + atomic_inc(&dev_priv->pending_bufs); + i810_dma_schedule(dev, 1); + return retcode; } int i810_dma(struct inode *inode, struct file *filp, unsigned int cmd, @@ -529,17 +713,11 @@ int i810_dma(struct inode *inode, struct file *filp, unsigned int cmd, int retcode = 0; drm_dma_t d; - printk("i810_dma start\n"); + DRM_DEBUG("i810_dma start\n"); copy_from_user_ret(&d, (drm_dma_t *)arg, sizeof(d), -EFAULT); DRM_DEBUG("%d %d: %d send, %d req\n", current->pid, d.context, d.send_count, d.request_count); - if (d.context == DRM_KERNEL_CONTEXT || d.context >= dev->queue_slots) { - DRM_ERROR("Process %d using context %d\n", - current->pid, d.context); - return -EINVAL; - } - if (d.send_count < 0 || d.send_count > dma->buf_count) { DRM_ERROR("Process %d trying to send %d buffers (of %d max)\n", current->pid, d.send_count, dma->buf_count); @@ -552,15 +730,7 @@ int i810_dma(struct inode *inode, struct file *filp, unsigned int cmd, } if (d.send_count) { -#if 0 - if (d.flags & _DRM_DMA_PRIORITY) - retcode = i810_dma_priority(dev, &d); - else - retcode = i810_dma_send_buffers(dev, &d); -#endif - printk("i810_dma priority\n"); - - retcode = i810_dma_priority(dev, &d); + retcode = i810_dma_send_buffers(dev, &d); } d.granted_count = 0; @@ -573,14 +743,15 @@ int i810_dma(struct inode *inode, struct file *filp, unsigned int cmd, current->pid, d.granted_count); copy_to_user_ret((drm_dma_t *)arg, &d, sizeof(d), -EFAULT); - printk("i810_dma end (granted)\n"); + DRM_DEBUG("i810_dma end (granted)\n"); return retcode; } int i810_irq_install(drm_device_t *dev, int irq) { int retcode; - + u16 temp; + if (!irq) return -EINVAL; down(&dev->struct_sem); @@ -591,6 +762,7 @@ int i810_irq_install(drm_device_t *dev, int irq) dev->irq = irq; up(&dev->struct_sem); + DRM_DEBUG( "Interrupt Install : %d\n", irq); DRM_DEBUG("%d\n", irq); dev->context_flag = 0; @@ -606,9 +778,18 @@ int i810_irq_install(drm_device_t *dev, int irq) dev->tq.routine = i810_dma_schedule_tq_wrapper; dev->tq.data = dev; - /* Before installing handler */ - /* TODO */ + 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, @@ -620,15 +801,18 @@ int i810_irq_install(drm_device_t *dev, int irq) up(&dev->struct_sem); return retcode; } - - /* After installing handler */ - /* TODO */ + 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; @@ -636,16 +820,27 @@ int i810_irq_uninstall(drm_device_t *dev) up(&dev->struct_sem); if (!irq) return -EINVAL; + + DRM_DEBUG( "Interrupt UnInstall: %d\n", irq); + DRM_DEBUG("%d\n", irq); - - /* TODO : Disable interrupts */ + + 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) { @@ -654,8 +849,7 @@ int i810_control(struct inode *inode, struct file *filp, unsigned int cmd, drm_control_t ctl; int retcode; - printk(KERN_INFO "i810_control\n"); - i810_dma_init(dev); + DRM_DEBUG( "i810_control\n"); copy_from_user_ret(&ctl, (drm_control_t *)arg, sizeof(ctl), -EFAULT); @@ -674,20 +868,48 @@ int i810_control(struct inode *inode, struct file *filp, unsigned int cmd, 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; + + printk("i810_flush_queue\n"); + printk("pending_bufs : %d\n", atomic_read(&dev_priv->pending_bufs)); + if(atomic_read(&dev_priv->pending_bufs) != 0) { + printk("got to flush\n"); + 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)); + i810_dma_schedule(dev, 0); + 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 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; - drm_queue_t *q; -#if DRM_DMA_HISTOGRAM - cycles_t start; - - dev->lck_start = start = get_cycles(); -#endif copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT); @@ -696,26 +918,58 @@ int i810_lock(struct inode *inode, struct file *filp, unsigned int cmd, 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 || lock.context >= dev->queue_count) { + if (lock.context < 0) { return -EINVAL; } - q = dev->queuelist[lock.context]; - - ret = drm_flush_block_and_flush(dev, lock.context, lock.flags); + + atomic_inc(&dev_priv->in_flush); + printk("in_flush : %d\n", atomic_read(&dev_priv->in_flush)); + if(atomic_read(&dev_priv->in_flush) != 1) { + atomic_dec(&dev_priv->in_flush); + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + /* Contention */ + atomic_inc(&dev->total_sleeps); + current->state = TASK_INTERRUPTIBLE; + current->policy |= SCHED_YIELD; + atomic_inc(&dev_priv->in_flush); + + printk("in_flush_loop : %d\n", atomic_read(&dev_priv->in_flush)); + if(atomic_read(&dev_priv->in_flush) == 1) { + break; + } + atomic_dec(&dev_priv->in_flush); + 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 (lock.flags & _DRM_LOCK_QUIESCENT) { + ret = i810_flush_queue(dev); + if(ret != 0) { + atomic_dec(&dev_priv->in_flush); + wake_up_interruptible(&dev->lock.lock_queue); + } + } else if (ret == 0) { + atomic_dec(&dev_priv->in_flush); + wake_up_interruptible(&dev->lock.lock_queue); + } + + /* Only one queue: + */ if (!ret) { - if (_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) - != lock.context) { - long j = jiffies - dev->lock.lock_time; - - if (j > 0 && j <= DRM_LOCK_SLICE) { - /* Can't take lock if we just had it and - there is contention. */ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(j); - } - } add_wait_queue(&dev->lock.lock_queue, &entry); for (;;) { if (!dev->lock.hw_lock) { @@ -728,13 +982,14 @@ int i810_lock(struct inode *inode, struct file *filp, unsigned int cmd, dev->lock.pid = current->pid; dev->lock.lock_time = jiffies; atomic_inc(&dev->total_locks); - atomic_inc(&q->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; @@ -744,19 +999,15 @@ int i810_lock(struct inode *inode, struct file *filp, unsigned int cmd, current->state = TASK_RUNNING; remove_wait_queue(&dev->lock.lock_queue, &entry); } - - drm_flush_unblock(dev, lock.context, lock.flags); /* cleanup phase */ if (!ret) { - if (lock.flags & _DRM_LOCK_READY) - i810_dma_ready(dev); - if (lock.flags & _DRM_LOCK_QUIESCENT) - i810_dma_quiescent(dev); + if (lock.flags & _DRM_LOCK_QUIESCENT) { + printk("_DRM_LOCK_QUIESCENT\n"); + i810_dma_quiescent(dev); + atomic_dec(&dev_priv->in_flush); + wake_up_interruptible(&dev->lock.lock_queue); + } } - -#if DRM_DMA_HISTOGRAM - atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]); -#endif - + printk("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); return ret; } diff --git a/linux/i810_drm_public.h b/linux/i810_drm_public.h new file mode 100644 index 00000000..e91d0fd9 --- /dev/null +++ b/linux/i810_drm_public.h @@ -0,0 +1,48 @@ +/* i810_drm_public.h -- Public header for the i810 driver -*- 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: Jeff Hartmann + * + * $XFree86$ + */ + +#ifndef _I810_DRM_H_ +#define _I810_DRM_H_ + +typedef struct drm_i810_init { + enum { + I810_INIT_DMA = 0x01, + I810_CLEANUP_DMA = 0x02 + } func; + int ring_map_idx; + int buffer_map_idx; + unsigned long ring_start; + unsigned long ring_end; + unsigned long ring_size; +} drm_i810_init_t; + +#define DRM_IOCTL_I810_INIT DRM_IOW( 0x40, drm_i810_init_t) + +#endif /* _I810_DRM_H_ */ diff --git a/linux/i810_drv.c b/linux/i810_drv.c index 70293575..15c68ddf 100644 --- a/linux/i810_drv.c +++ b/linux/i810_drv.c @@ -37,7 +37,7 @@ EXPORT_SYMBOL(i810_init); EXPORT_SYMBOL(i810_cleanup); #define I810_NAME "i810" -#define I810_DESC "Matrox g200/g400" +#define I810_DESC "Intel I810" #define I810_DATE "19991213" #define I810_MAJOR 0 #define I810_MINOR 0 @@ -80,13 +80,13 @@ static drm_ioctl_desc_t i810_ioctls[] = { [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)] = { i810_mapbufs, 1, 0 }, [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)] = { i810_freebufs, 1, 0 }, - [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { drm_addctx, 1, 1 }, - [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { drm_rmctx, 1, 1 }, - [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { drm_modctx, 1, 1 }, - [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { drm_getctx, 1, 0 }, - [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { drm_switchctx, 1, 1 }, - [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { drm_newctx, 1, 1 }, - [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { drm_resctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { i810_addctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { i810_rmctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { i810_modctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { i810_getctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { i810_switchctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { i810_newctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { i810_resctx, 1, 0 }, [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 }, [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 }, @@ -104,6 +104,7 @@ static drm_ioctl_desc_t i810_ioctls[] = { [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)] = { drm_agp_free, 1, 1 }, [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)] = { drm_agp_bind, 1, 1 }, [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = { drm_agp_unbind, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_INIT)] = { i810_dma_init, 1, 1 }, }; #define I810_IOCTL_COUNT DRM_ARRAY_SIZE(i810_ioctls) @@ -386,10 +387,6 @@ int i810_init(void) i810_takedown(dev); return retcode; } -#if 0 - printk("doing i810_dma_init\n"); - i810_dma_init(dev); -#endif DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", I810_NAME, @@ -417,7 +414,9 @@ void i810_cleanup(void) DRM_INFO("Module unloaded\n"); } drm_ctxbitmap_cleanup(dev); +#if 0 i810_dma_cleanup(dev); +#endif i810_takedown(dev); if (dev->agp) { drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS); diff --git a/linux/i810_drv.h b/linux/i810_drv.h index 1072b39a..82546976 100644 --- a/linux/i810_drv.h +++ b/linux/i810_drv.h @@ -1,4 +1,4 @@ -/* i810_drv.h -- Private header for the Matrox g200/g400 driver -*- linux-c -*- +/* i810_drv.h -- Private header for the i810 driver -*- linux-c -*- * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. @@ -31,6 +31,29 @@ #ifndef _I810_DRV_H_ #define _I810_DRV_H_ +#include "i810_drm_public.h" + +typedef struct _drm_i810_ring_buffer{ + int tail_mask; + unsigned long Start; + unsigned long End; + unsigned long Size; + u8 *virtual_start; + int head; + int tail; + int space; +} drm_i810_ring_buffer; + +typedef struct drm_i810_private { + int ring_map_idx; + int buffer_map_idx; + drm_i810_ring_buffer ring; + unsigned long hw_status_page; + unsigned long counter; + atomic_t dispatch_lock; + atomic_t pending_bufs; + atomic_t in_flush; +} drm_i810_private_t; /* i810_drv.c */ extern int i810_init(void); @@ -54,8 +77,8 @@ extern int i810_control(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); extern int i810_lock(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); -extern void i810_dma_init(drm_device_t *dev); -extern void i810_dma_cleanup(drm_device_t *dev); +extern int i810_dma_init(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); /* i810_bufs.c */ @@ -72,5 +95,61 @@ extern int i810_mapbufs(struct inode *inode, struct file *filp, extern int i810_addmap(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); + /* i810_context.c */ +extern int i810_resctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_addctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_modctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_getctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_switchctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_newctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_rmctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +extern int i810_context_switch(drm_device_t *dev, int old, int new); +extern int i810_context_switch_complete(drm_device_t *dev, int new); + + +#define GFX_OP_USER_INTERRUPT ((0<<29)|(2<<23)) +#define GFX_OP_BREAKPOINT_INTERRUPT ((0<<29)|(1<<23)) +#define CMD_REPORT_HEAD (7<<23) +#define CMD_STORE_DWORD_IDX ((0x21<<23) | 0x1) +#define CMD_OP_BATCH_BUFFER ((0x0<<29)|(0x30<<23)|0x1) + + +#define BB1_START_ADDR_MASK (~0x7) +#define BB1_PROTECTED (1<<0) +#define BB1_UNPROTECTED (0<<0) +#define BB2_END_ADDR_MASK (~0x7) + +#define I810REG_HWSTAM 0x02098 +#define I810REG_INT_IDENTITY_R 0x020a4 +#define I810REG_INT_MASK_R 0x020a8 +#define I810REG_INT_ENABLE_R 0x020a0 + +#define LP_RING 0x2030 +#define HP_RING 0x2040 +#define RING_TAIL 0x00 +#define TAIL_ADDR 0x000FFFF8 +#define RING_HEAD 0x04 +#define HEAD_WRAP_COUNT 0xFFE00000 +#define HEAD_WRAP_ONE 0x00200000 +#define HEAD_ADDR 0x001FFFFC +#define RING_START 0x08 +#define START_ADDR 0x00FFFFF8 +#define RING_LEN 0x0C +#define RING_NR_PAGES 0x000FF000 +#define RING_REPORT_MASK 0x00000006 +#define RING_REPORT_64K 0x00000002 +#define RING_REPORT_128K 0x00000004 +#define RING_NO_REPORT 0x00000000 +#define RING_VALID_MASK 0x00000001 +#define RING_VALID 0x00000001 +#define RING_INVALID 0x00000000 #endif