]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/char/drm/via_dma.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc
[linux-2.6-omap-h63xx.git] / drivers / char / drm / via_dma.c
index 7009dbddac436fc86fbd1fe20757382d6228541f..7a339dba6a69c2fb4d961a73851c048a0807f823 100644 (file)
@@ -126,6 +126,8 @@ via_cmdbuf_wait(drm_via_private_t * dev_priv, unsigned int size)
                             hw_addr, cur_addr, next_addr);
                        return -1;
                }
+               if  ((cur_addr < hw_addr) && (next_addr >= hw_addr))
+                       msleep(1);
        } while ((cur_addr < hw_addr) && (next_addr >= hw_addr));
        return 0;
 }
@@ -179,14 +181,12 @@ static int via_initialize(struct drm_device * dev,
        }
 
        if (dev_priv->ring.virtual_start != NULL) {
-               DRM_ERROR("%s called again without calling cleanup\n",
-                         __FUNCTION__);
+               DRM_ERROR("called again without calling cleanup\n");
                return -EFAULT;
        }
 
        if (!dev->agp || !dev->agp->base) {
-               DRM_ERROR("%s called with no agp memory available\n",
-                         __FUNCTION__);
+               DRM_ERROR("called with no agp memory available\n");
                return -EFAULT;
        }
 
@@ -267,8 +267,7 @@ static int via_dispatch_cmdbuffer(struct drm_device * dev, drm_via_cmdbuffer_t *
        dev_priv = (drm_via_private_t *) dev->dev_private;
 
        if (dev_priv->ring.virtual_start == NULL) {
-               DRM_ERROR("%s called without initializing AGP ring buffer.\n",
-                         __FUNCTION__);
+               DRM_ERROR("called without initializing AGP ring buffer.\n");
                return -EFAULT;
        }
 
@@ -337,8 +336,7 @@ static int via_cmdbuffer(struct drm_device *dev, void *data, struct drm_file *fi
 
        LOCK_TEST_WITH_RETURN(dev, file_priv);
 
-       DRM_DEBUG("via cmdbuffer, buf %p size %lu\n", cmdbuf->buf,
-                 cmdbuf->size);
+       DRM_DEBUG("buf %p size %lu\n", cmdbuf->buf, cmdbuf->size);
 
        ret = via_dispatch_cmdbuffer(dev, cmdbuf);
        if (ret) {
@@ -379,8 +377,7 @@ static int via_pci_cmdbuffer(struct drm_device *dev, void *data, struct drm_file
 
        LOCK_TEST_WITH_RETURN(dev, file_priv);
 
-       DRM_DEBUG("via_pci_cmdbuffer, buf %p size %lu\n", cmdbuf->buf,
-                 cmdbuf->size);
+       DRM_DEBUG("buf %p size %lu\n", cmdbuf->buf, cmdbuf->size);
 
        ret = via_dispatch_pci_cmdbuffer(dev, cmdbuf);
        if (ret) {
@@ -421,27 +418,50 @@ static int via_hook_segment(drm_via_private_t * dev_priv,
        int paused, count;
        volatile uint32_t *paused_at = dev_priv->last_pause_ptr;
        uint32_t reader,ptr;
+       uint32_t diff;
 
        paused = 0;
        via_flush_write_combine();
        (void) *(volatile uint32_t *)(via_get_dma(dev_priv) -1);
+
        *paused_at = pause_addr_lo;
        via_flush_write_combine();
        (void) *paused_at;
+
        reader = *(dev_priv->hw_addr_ptr);
        ptr = ((volatile char *)paused_at - dev_priv->dma_ptr) +
                dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr + 4;
+
        dev_priv->last_pause_ptr = via_get_dma(dev_priv) - 1;
 
-       if ((ptr - reader) <= dev_priv->dma_diff ) {
-               count = 10000000;
-               while (!(paused = (VIA_READ(0x41c) & 0x80000000)) && count--);
+       /*
+        * If there is a possibility that the command reader will 
+        * miss the new pause address and pause on the old one,
+        * In that case we need to program the new start address
+        * using PCI.
+        */
+
+       diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff;
+       count = 10000000;
+       while(diff == 0 && count--) {
+               paused = (VIA_READ(0x41c) & 0x80000000);
+               if (paused) 
+                       break;
+               reader = *(dev_priv->hw_addr_ptr);
+               diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff;
        }
 
+       paused = VIA_READ(0x41c) & 0x80000000;
+
        if (paused && !no_pci_fire) {
                reader = *(dev_priv->hw_addr_ptr);
-               if ((ptr - reader) == dev_priv->dma_diff) {
-
+               diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff;
+               diff &= (dev_priv->dma_high - 1);
+               if (diff != 0 && diff < (dev_priv->dma_high >> 1)) {
+                       DRM_ERROR("Paused at incorrect address. "
+                                 "0x%08x, 0x%08x 0x%08x\n",
+                                 ptr, reader, dev_priv->dma_diff);
+               } else if (diff == 0) {
                        /*
                         * There is a concern that these writes may stall the PCI bus
                         * if the GPU is not idle. However, idling the GPU first
@@ -582,6 +602,7 @@ static void via_cmdbuf_jump(drm_via_private_t * dev_priv)
        uint32_t pause_addr_lo, pause_addr_hi;
        uint32_t jump_addr_lo, jump_addr_hi;
        volatile uint32_t *last_pause_ptr;
+       uint32_t dma_low_save1, dma_low_save2;
 
        agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr;
        via_align_cmd(dev_priv, HC_HAGPBpID_JUMP, 0, &jump_addr_hi,
@@ -608,8 +629,29 @@ static void via_cmdbuf_jump(drm_via_private_t * dev_priv)
                      &pause_addr_lo, 0);
 
        *last_pause_ptr = pause_addr_lo;
+       dma_low_save1 = dev_priv->dma_low;
 
-       via_hook_segment( dev_priv, jump_addr_hi, jump_addr_lo, 0);
+       /*
+        * Now, set a trap that will pause the regulator if it tries to rerun the old
+        * command buffer. (Which may happen if via_hook_segment detecs a command regulator pause
+        * and reissues the jump command over PCI, while the regulator has already taken the jump
+        * and actually paused at the current buffer end).
+        * There appears to be no other way to detect this condition, since the hw_addr_pointer
+        * does not seem to get updated immediately when a jump occurs.
+        */
+
+       last_pause_ptr =
+               via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi,
+                             &pause_addr_lo, 0) - 1;
+       via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi,
+                     &pause_addr_lo, 0);
+       *last_pause_ptr = pause_addr_lo;
+
+       dma_low_save2 = dev_priv->dma_low;
+       dev_priv->dma_low = dma_low_save1;
+       via_hook_segment(dev_priv, jump_addr_hi, jump_addr_lo, 0);
+       dev_priv->dma_low = dma_low_save2;
+       via_hook_segment(dev_priv, pause_addr_hi, pause_addr_lo, 0);
 }
 
 
@@ -648,14 +690,13 @@ static int via_cmdbuf_size(struct drm_device *dev, void *data, struct drm_file *
        uint32_t tmp_size, count;
        drm_via_private_t *dev_priv;
 
-       DRM_DEBUG("via cmdbuf_size\n");
+       DRM_DEBUG("\n");
        LOCK_TEST_WITH_RETURN(dev, file_priv);
 
        dev_priv = (drm_via_private_t *) dev->dev_private;
 
        if (dev_priv->ring.virtual_start == NULL) {
-               DRM_ERROR("%s called without initializing AGP ring buffer.\n",
-                         __FUNCTION__);
+               DRM_ERROR("called without initializing AGP ring buffer.\n");
                return -EFAULT;
        }