static void via_dmablit_engine_off(drm_device_t *dev, int engine) { drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; VIA_WRITE(VIA_PCI_DMA_CSR0 + engine*0x04, VIA_DMA_CSR_TD | VIA_DMA_CSR_DD); }
static void via_abort_dmablit(drm_device_t *dev, int engine) { drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; VIA_WRITE(VIA_PCI_DMA_CSR0 + engine*0x04, VIA_DMA_CSR_TA); }
static void via_fire_dmablit(drm_device_t *dev, drm_via_sg_info_t *vsg, int engine) { drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; VIA_WRITE(VIA_PCI_DMA_MAR0 + engine*0x10, 0); VIA_WRITE(VIA_PCI_DMA_DAR0 + engine*0x10, 0); VIA_WRITE(VIA_PCI_DMA_CSR0 + engine*0x04, VIA_DMA_CSR_DD | VIA_DMA_CSR_TD | VIA_DMA_CSR_DE); VIA_WRITE(VIA_PCI_DMA_MR0 + engine*0x04, VIA_DMA_MR_CM | VIA_DMA_MR_TDIE); VIA_WRITE(VIA_PCI_DMA_BCR0 + engine*0x10, 0); VIA_WRITE(VIA_PCI_DMA_DPR0 + engine*0x10, vsg->chain_start); VIA_WRITE(VIA_PCI_DMA_CSR0 + engine*0x04, VIA_DMA_CSR_DE | VIA_DMA_CSR_TS); }
void via_dmablit_handler(drm_device_t *dev, int engine, int from_irq) { drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; drm_via_blitq_t *blitq = dev_priv->blit_queues + engine; int cur; int done_transfer; unsigned long irqsave=0; uint32_t status = 0; DRM_DEBUG("DMA blit handler called. engine = %d, from_irq = %d, blitq = 0x%lx\n", engine, from_irq, (unsigned long) blitq); if (from_irq) { spin_lock(&blitq->blit_lock); } else { spin_lock_irqsave(&blitq->blit_lock, irqsave); } done_transfer = blitq->is_active && (( status = VIA_READ(VIA_PCI_DMA_CSR0 + engine*0x04)) & VIA_DMA_CSR_TD); done_transfer = done_transfer || ( blitq->aborting && !(status & VIA_DMA_CSR_DE)); cur = blitq->cur; if (done_transfer) { blitq->blits[cur]->aborted = blitq->aborting; blitq->done_blit_handle++; DRM_WAKEUP(blitq->blit_queue + cur); cur++; if (cur >= VIA_NUM_BLIT_SLOTS) cur = 0; blitq->cur = cur; /* * Clear transfer done flag. */ VIA_WRITE(VIA_PCI_DMA_CSR0 + engine*0x04, VIA_DMA_CSR_TD); blitq->is_active = 0; blitq->aborting = 0; schedule_work(&blitq->wq); } else if (blitq->is_active && time_after_eq(jiffies, blitq->end)) { /* * Abort transfer after one second. */ via_abort_dmablit(dev, engine); blitq->aborting = 1; blitq->end = jiffies + DRM_HZ; } if (!blitq->is_active) { if (blitq->num_outstanding) { via_fire_dmablit(dev, blitq->blits[cur], engine); blitq->is_active = 1; blitq->cur = cur; blitq->num_outstanding--; blitq->end = jiffies + DRM_HZ; if (!timer_pending(&blitq->poll_timer)) { blitq->poll_timer.expires = jiffies+1; add_timer(&blitq->poll_timer); } } else { if (timer_pending(&blitq->poll_timer)) { del_timer(&blitq->poll_timer); } via_dmablit_engine_off(dev, engine); } } if (from_irq) { spin_unlock(&blitq->blit_lock); } else { spin_unlock_irqrestore(&blitq->blit_lock, irqsave); } }
/* * The dmablit part of the IRQ handler. Trying to do only reasonably fast things here. * The rest, like unmapping and freeing memory for done blits is done in a separate workqueue * task. Basically the task of the interrupt handler is to submit a new blit to the engine, while * the workqueue task takes care of processing associated with the old blit. */ void via_dmablit_handler(struct drm_device *dev, int engine, int from_irq) { drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; drm_via_blitq_t *blitq = dev_priv->blit_queues + engine; int cur; int done_transfer; uint32_t status = 0; DRM_DEBUG("DMA blit handler called. engine = %d, from_irq = %d, blitq = 0x%lx\n", engine, from_irq, (unsigned long) blitq); mtx_lock(&blitq->blit_lock); done_transfer = blitq->is_active && (( status = VIA_READ(VIA_PCI_DMA_CSR0 + engine*0x04)) & VIA_DMA_CSR_TD); done_transfer = done_transfer || ( blitq->aborting && !(status & VIA_DMA_CSR_DE)); cur = blitq->cur; if (done_transfer) { blitq->blits[cur]->aborted = blitq->aborting; blitq->done_blit_handle++; DRM_WAKEUP(&blitq->blit_queue[cur]); cur++; if (cur >= VIA_NUM_BLIT_SLOTS) cur = 0; blitq->cur = cur; /* * Clear transfer done flag. */ VIA_WRITE(VIA_PCI_DMA_CSR0 + engine*0x04, VIA_DMA_CSR_TD); blitq->is_active = 0; blitq->aborting = 0; taskqueue_enqueue(taskqueue_swi, &blitq->wq); } else if (blitq->is_active && (ticks >= blitq->end)) { /* * Abort transfer after one second. */ via_abort_dmablit(dev, engine); blitq->aborting = 1; blitq->end = ticks + DRM_HZ; } if (!blitq->is_active) { if (blitq->num_outstanding) { via_fire_dmablit(dev, blitq->blits[cur], engine); blitq->is_active = 1; blitq->cur = cur; blitq->num_outstanding--; blitq->end = ticks + DRM_HZ; if (!callout_pending(&blitq->poll_timer)) callout_reset(&blitq->poll_timer, 1, (timeout_t *)via_dmablit_timer, (void *)blitq); } else { if (callout_pending(&blitq->poll_timer)) { callout_stop(&blitq->poll_timer); } via_dmablit_engine_off(dev, engine); } } mtx_unlock(&blitq->blit_lock); }