diff --git a/linux/mga_dma.c b/linux/mga_dma.c index b12da7f0..72e096a5 100644 --- a/linux/mga_dma.c +++ b/linux/mga_dma.c @@ -467,48 +467,49 @@ out_prim_wait: int mga_advance_primary(drm_device_t *dev) { - DECLARE_WAITQUEUE(entry, current); - drm_mga_private_t *dev_priv = dev->dev_private; - drm_mga_prim_buf_t *prim_buffer; - drm_device_dma_t *dma = dev->dma; - int next_prim_idx; - int ret = 0; + DECLARE_WAITQUEUE(entry, current); + drm_mga_private_t *dev_priv = dev->dev_private; + drm_mga_prim_buf_t *prim_buffer; + drm_device_dma_t *dma = dev->dma; + int next_prim_idx; + int ret = 0; - /* This needs to reset the primary buffer if available, - * we should collect stats on how many times it bites - * it's tail */ - next_prim_idx = dev_priv->current_prim_idx + 1; - if(next_prim_idx >= MGA_NUM_PRIM_BUFS) - next_prim_idx = 0; - prim_buffer = dev_priv->prim_bufs[next_prim_idx]; - /* In use is cleared in interrupt handler */ - atomic_set(&dev_priv->in_wait, 1); - if(test_and_set_bit(0, &prim_buffer->in_use)) { - add_wait_queue(&dev_priv->wait_queue, &entry); - for (;;) { - current->state = TASK_INTERRUPTIBLE; - mga_dma_schedule(dev, 0); - if(!test_and_set_bit(0, &prim_buffer->in_use)) break; - atomic_inc(&dev->total_sleeps); - atomic_inc(&dma->total_missed_sched); - schedule_timeout(HZ/60); - if (signal_pending(current)) { - ret = -ERESTARTSYS; - break; - } - } - current->state = TASK_RUNNING; - remove_wait_queue(&dev_priv->wait_queue, &entry); - if(ret) return ret; - } - atomic_set(&dev_priv->in_wait, 0); - /* This primary buffer is now free to use */ - prim_buffer->current_dma_ptr = prim_buffer->head; - prim_buffer->num_dwords = 0; - prim_buffer->sec_used = 0; - dev_priv->current_prim = prim_buffer; - dev_priv->current_prim_idx = next_prim_idx; - return 0; + /* This needs to reset the primary buffer if available, + * we should collect stats on how many times it bites + * it's tail */ + next_prim_idx = dev_priv->current_prim_idx + 1; + if(next_prim_idx >= MGA_NUM_PRIM_BUFS) + next_prim_idx = 0; + prim_buffer = dev_priv->prim_bufs[next_prim_idx]; + /* In use is cleared in interrupt handler */ + atomic_set(&dev_priv->in_wait, 1); + if(test_and_set_bit(0, &prim_buffer->in_use)) { + add_wait_queue(&dev_priv->wait_queue, &entry); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + mga_dma_schedule(dev, 0); + if(!test_and_set_bit(0, &prim_buffer->in_use)) break; + atomic_inc(&dev->total_sleeps); + atomic_inc(&dma->total_missed_sched); + schedule_timeout(HZ/60); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev_priv->wait_queue, &entry); + if(ret) return ret; + } + atomic_set(&dev_priv->in_wait, 0); + /* This primary buffer is now free to use */ + prim_buffer->current_dma_ptr = prim_buffer->head; + prim_buffer->num_dwords = 0; + prim_buffer->sec_used = 0; + atomic_set(&prim_buffer->needs_overflow, 0); + dev_priv->current_prim = prim_buffer; + dev_priv->current_prim_idx = next_prim_idx; + return 0; } /* More dynamic performance decisions */ @@ -558,12 +559,15 @@ int mga_dma_schedule(drm_device_t *dev, int locked) /* Fire dma buffer */ if(mga_decide_to_fire(dev)) { DRM_DEBUG("mga_fire_primary\n"); - atomic_set(&dev_priv->next_prim->force_fire, 0); - if(dev_priv->current_prim == dev_priv->next_prim) { - mga_advance_primary(dev); - } - mga_fire_primary(dev, + mga_fire_primary(dev, dev_priv->next_prim); + if(dev_priv->current_prim == dev_priv->next_prim) { + /* Schedule overflow for a later time */ + atomic_set(&dev_priv->current_prim->needs_overflow, + 1); + } + atomic_set(&dev_priv->next_prim->force_fire, 0); + DRM_DEBUG("idx :%d\n", dev_priv->next_prim->idx); } else { clear_bit(0, &dev_priv->dispatch_lock); diff --git a/linux/mga_dma.h b/linux/mga_dma.h index b5b73f99..47588dfe 100644 --- a/linux/mga_dma.h +++ b/linux/mga_dma.h @@ -33,7 +33,7 @@ typedef struct { #define MGA_VERBOSE 0 -#define MGA_NUM_PRIM_BUFS 8 +#define MGA_NUM_PRIM_BUFS 2 /* Primary buffer versions of above -- pretty similar really. */ @@ -44,7 +44,8 @@ typedef struct { drm_mga_prim_buf_t *tmp_buf = \ dev_priv->prim_bufs[dev_priv->current_prim_idx]; \ if( (tmp_buf->max_dwords - tmp_buf->num_dwords) < length || \ - tmp_buf->sec_used > (MGA_DMA_BUF_NR / 2)) { \ + tmp_buf->sec_used > (MGA_DMA_BUF_NR / 2) || \ +atomic_read(&tmp_buf->needs_overflow)) { \ atomic_set(&tmp_buf->force_fire, 1); \ mga_advance_primary(dev); \ mga_dma_schedule(dev, 1); \ diff --git a/linux/mga_drv.h b/linux/mga_drv.h index 4031ee14..eeb4f0d9 100644 --- a/linux/mga_drv.h +++ b/linux/mga_drv.h @@ -44,6 +44,7 @@ typedef struct { int swap_pending; u32 in_use; atomic_t force_fire; + atomic_t needs_overflow; } drm_mga_prim_buf_t; typedef struct _drm_mga_freelist {