drm/nv50: fix iommu errors caused by device reading from address 0
[safe/jmp/linux-2.6] / drivers / gpu / drm / drm_irq.c
index 6b3ce6d..a263b70 100644 (file)
@@ -36,6 +36,7 @@
 #include "drmP.h"
 
 #include <linux/interrupt.h>   /* For task queue support */
+#include <linux/slab.h>
 
 #include <linux/vgaarb.h>
 /**
@@ -115,6 +116,7 @@ void drm_vblank_cleanup(struct drm_device *dev)
 
        dev->num_crtcs = 0;
 }
+EXPORT_SYMBOL(drm_vblank_cleanup);
 
 int drm_vblank_init(struct drm_device *dev, int num_crtcs)
 {
@@ -163,7 +165,6 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
        }
 
        dev->vblank_disable_allowed = 0;
-
        return 0;
 
 err:
@@ -429,15 +430,21 @@ int drm_vblank_get(struct drm_device *dev, int crtc)
 
        spin_lock_irqsave(&dev->vbl_lock, irqflags);
        /* Going from 0->1 means we have to enable interrupts again */
-       if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1 &&
-           !dev->vblank_enabled[crtc]) {
-               ret = dev->driver->enable_vblank(dev, crtc);
-               DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", crtc, ret);
-               if (ret)
+       if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1) {
+               if (!dev->vblank_enabled[crtc]) {
+                       ret = dev->driver->enable_vblank(dev, crtc);
+                       DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", crtc, ret);
+                       if (ret)
+                               atomic_dec(&dev->vblank_refcount[crtc]);
+                       else {
+                               dev->vblank_enabled[crtc] = 1;
+                               drm_update_vblank_count(dev, crtc);
+                       }
+               }
+       } else {
+               if (!dev->vblank_enabled[crtc]) {
                        atomic_dec(&dev->vblank_refcount[crtc]);
-               else {
-                       dev->vblank_enabled[crtc] = 1;
-                       drm_update_vblank_count(dev, crtc);
+                       ret = -EINVAL;
                }
        }
        spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
@@ -464,6 +471,19 @@ void drm_vblank_put(struct drm_device *dev, int crtc)
 }
 EXPORT_SYMBOL(drm_vblank_put);
 
+void drm_vblank_off(struct drm_device *dev, int crtc)
+{
+       unsigned long irqflags;
+
+       spin_lock_irqsave(&dev->vbl_lock, irqflags);
+       dev->driver->disable_vblank(dev, crtc);
+       DRM_WAKEUP(&dev->vbl_queue[crtc]);
+       dev->vblank_enabled[crtc] = 0;
+       dev->last_vblank[crtc] = dev->driver->get_vblank_counter(dev, crtc);
+       spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+}
+EXPORT_SYMBOL(drm_vblank_off);
+
 /**
  * drm_vblank_pre_modeset - account for vblanks across mode sets
  * @dev: DRM device
@@ -475,6 +495,9 @@ EXPORT_SYMBOL(drm_vblank_put);
  */
 void drm_vblank_pre_modeset(struct drm_device *dev, int crtc)
 {
+       /* vblank is not initialized (IRQ not installed ?) */
+       if (!dev->num_crtcs)
+               return;
        /*
         * To avoid all the problems that might happen if interrupts
         * were enabled/disabled around or between these calls, we just