]> 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 6d3e4eecf8f87781513515aaf6650c911946784c..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;
        }
 
@@ -227,22 +227,18 @@ static int via_initialize(struct drm_device * dev,
        return 0;
 }
 
-static int via_dma_init(DRM_IOCTL_ARGS)
+static int via_dma_init(struct drm_device *dev, void *data, struct drm_file *file_priv)
 {
-       DRM_DEVICE;
        drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
-       drm_via_dma_init_t init;
+       drm_via_dma_init_t *init = data;
        int retcode = 0;
 
-       DRM_COPY_FROM_USER_IOCTL(init, (drm_via_dma_init_t __user *) data,
-                                sizeof(init));
-
-       switch (init.func) {
+       switch (init->func) {
        case VIA_INIT_DMA:
                if (!DRM_SUSER(DRM_CURPROC))
                        retcode = -EPERM;
                else
-                       retcode = via_initialize(dev, dev_priv, &init);
+                       retcode = via_initialize(dev, dev_priv, init);
                break;
        case VIA_CLEANUP_DMA:
                if (!DRM_SUSER(DRM_CURPROC))
@@ -271,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;
        }
 
@@ -326,29 +321,24 @@ int via_driver_dma_quiescent(struct drm_device * dev)
        return 0;
 }
 
-static int via_flush_ioctl(DRM_IOCTL_ARGS)
+static int via_flush_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
 {
-       DRM_DEVICE;
 
        LOCK_TEST_WITH_RETURN(dev, file_priv);
 
        return via_driver_dma_quiescent(dev);
 }
 
-static int via_cmdbuffer(DRM_IOCTL_ARGS)
+static int via_cmdbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv)
 {
-       DRM_DEVICE;
-       drm_via_cmdbuffer_t cmdbuf;
+       drm_via_cmdbuffer_t *cmdbuf = data;
        int ret;
 
        LOCK_TEST_WITH_RETURN(dev, file_priv);
 
-       DRM_COPY_FROM_USER_IOCTL(cmdbuf, (drm_via_cmdbuffer_t __user *) data,
-                                sizeof(cmdbuf));
-
-       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);
+       ret = via_dispatch_cmdbuffer(dev, cmdbuf);
        if (ret) {
                return ret;
        }
@@ -380,21 +370,16 @@ static int via_dispatch_pci_cmdbuffer(struct drm_device * dev,
        return ret;
 }
 
-static int via_pci_cmdbuffer(DRM_IOCTL_ARGS)
+static int via_pci_cmdbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv)
 {
-       DRM_DEVICE;
-       drm_via_cmdbuffer_t cmdbuf;
+       drm_via_cmdbuffer_t *cmdbuf = data;
        int ret;
 
        LOCK_TEST_WITH_RETURN(dev, file_priv);
 
-       DRM_COPY_FROM_USER_IOCTL(cmdbuf, (drm_via_cmdbuffer_t __user *) data,
-                                sizeof(cmdbuf));
-
-       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);
+       ret = via_dispatch_pci_cmdbuffer(dev, cmdbuf);
        if (ret) {
                return ret;
        }
@@ -412,7 +397,7 @@ static inline uint32_t *via_align_buffer(drm_via_private_t * dev_priv,
 }
 
 /*
- * This function is used internally by ring buffer mangement code.
+ * This function is used internally by ring buffer management code.
  *
  * Returns virtual pointer to ring buffer.
  */
@@ -433,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
@@ -594,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,
@@ -620,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;
+
+       /*
+        * 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.
+        */
 
-       via_hook_segment( dev_priv, jump_addr_hi, jump_addr_lo, 0);
+       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);
 }
 
 
@@ -653,35 +683,30 @@ static void via_cmdbuf_reset(drm_via_private_t * dev_priv)
  * User interface to the space and lag functions.
  */
 
-static int via_cmdbuf_size(DRM_IOCTL_ARGS)
+static int via_cmdbuf_size(struct drm_device *dev, void *data, struct drm_file *file_priv)
 {
-       DRM_DEVICE;
-       drm_via_cmdbuf_size_t d_siz;
+       drm_via_cmdbuf_size_t *d_siz = data;
        int ret = 0;
        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;
        }
 
-       DRM_COPY_FROM_USER_IOCTL(d_siz, (drm_via_cmdbuf_size_t __user *) data,
-                                sizeof(d_siz));
-
        count = 1000000;
-       tmp_size = d_siz.size;
-       switch (d_siz.func) {
+       tmp_size = d_siz->size;
+       switch (d_siz->func) {
        case VIA_CMDBUF_SPACE:
-               while (((tmp_size = via_cmdbuf_space(dev_priv)) < d_siz.size)
+               while (((tmp_size = via_cmdbuf_space(dev_priv)) < d_siz->size)
                       && count--) {
-                       if (!d_siz.wait) {
+                       if (!d_siz->wait) {
                                break;
                        }
                }
@@ -691,9 +716,9 @@ static int via_cmdbuf_size(DRM_IOCTL_ARGS)
                }
                break;
        case VIA_CMDBUF_LAG:
-               while (((tmp_size = via_cmdbuf_lag(dev_priv)) > d_siz.size)
+               while (((tmp_size = via_cmdbuf_lag(dev_priv)) > d_siz->size)
                       && count--) {
-                       if (!d_siz.wait) {
+                       if (!d_siz->wait) {
                                break;
                        }
                }
@@ -705,28 +730,26 @@ static int via_cmdbuf_size(DRM_IOCTL_ARGS)
        default:
                ret = -EFAULT;
        }
-       d_siz.size = tmp_size;
+       d_siz->size = tmp_size;
 
-       DRM_COPY_TO_USER_IOCTL((drm_via_cmdbuf_size_t __user *) data, d_siz,
-                              sizeof(d_siz));
        return ret;
 }
 
-drm_ioctl_desc_t via_ioctls[] = {
-       [DRM_IOCTL_NR(DRM_VIA_ALLOCMEM)] = {via_mem_alloc, DRM_AUTH},
-       [DRM_IOCTL_NR(DRM_VIA_FREEMEM)] = {via_mem_free, DRM_AUTH},
-       [DRM_IOCTL_NR(DRM_VIA_AGP_INIT)] = {via_agp_init, DRM_AUTH|DRM_MASTER},
-       [DRM_IOCTL_NR(DRM_VIA_FB_INIT)] = {via_fb_init, DRM_AUTH|DRM_MASTER},
-       [DRM_IOCTL_NR(DRM_VIA_MAP_INIT)] = {via_map_init, DRM_AUTH|DRM_MASTER},
-       [DRM_IOCTL_NR(DRM_VIA_DEC_FUTEX)] = {via_decoder_futex, DRM_AUTH},
-       [DRM_IOCTL_NR(DRM_VIA_DMA_INIT)] = {via_dma_init, DRM_AUTH},
-       [DRM_IOCTL_NR(DRM_VIA_CMDBUFFER)] = {via_cmdbuffer, DRM_AUTH},
-       [DRM_IOCTL_NR(DRM_VIA_FLUSH)] = {via_flush_ioctl, DRM_AUTH},
-       [DRM_IOCTL_NR(DRM_VIA_PCICMD)] = {via_pci_cmdbuffer, DRM_AUTH},
-       [DRM_IOCTL_NR(DRM_VIA_CMDBUF_SIZE)] = {via_cmdbuf_size, DRM_AUTH},
-       [DRM_IOCTL_NR(DRM_VIA_WAIT_IRQ)] = {via_wait_irq, DRM_AUTH},
-       [DRM_IOCTL_NR(DRM_VIA_DMA_BLIT)] = {via_dma_blit, DRM_AUTH},
-       [DRM_IOCTL_NR(DRM_VIA_BLIT_SYNC)] = {via_dma_blit_sync, DRM_AUTH}
+struct drm_ioctl_desc via_ioctls[] = {
+       DRM_IOCTL_DEF(DRM_VIA_ALLOCMEM, via_mem_alloc, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_VIA_FREEMEM, via_mem_free, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_VIA_AGP_INIT, via_agp_init, DRM_AUTH|DRM_MASTER),
+       DRM_IOCTL_DEF(DRM_VIA_FB_INIT, via_fb_init, DRM_AUTH|DRM_MASTER),
+       DRM_IOCTL_DEF(DRM_VIA_MAP_INIT, via_map_init, DRM_AUTH|DRM_MASTER),
+       DRM_IOCTL_DEF(DRM_VIA_DEC_FUTEX, via_decoder_futex, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_VIA_DMA_INIT, via_dma_init, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_VIA_CMDBUFFER, via_cmdbuffer, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_VIA_FLUSH, via_flush_ioctl, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_VIA_PCICMD, via_pci_cmdbuffer, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_VIA_CMDBUF_SIZE, via_cmdbuf_size, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_VIA_WAIT_IRQ, via_wait_irq, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_VIA_DMA_BLIT, via_dma_blit, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_VIA_BLIT_SYNC, via_dma_blit_sync, DRM_AUTH)
 };
 
 int via_max_ioctl = DRM_ARRAY_SIZE(via_ioctls);