drm/radeon/kms/pm: voltage fixes
[safe/jmp/linux-2.6] / drivers / gpu / drm / drm_irq.c
index b4a3dbc..a263b70 100644 (file)
@@ -36,7 +36,9 @@
 #include "drmP.h"
 
 #include <linux/interrupt.h>   /* For task queue support */
+#include <linux/slab.h>
 
+#include <linux/vgaarb.h>
 /**
  * Get interrupt from bus id.
  *
@@ -114,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)
 {
@@ -162,7 +165,6 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
        }
 
        dev->vblank_disable_allowed = 0;
-
        return 0;
 
 err:
@@ -171,6 +173,26 @@ err:
 }
 EXPORT_SYMBOL(drm_vblank_init);
 
+static void drm_irq_vgaarb_nokms(void *cookie, bool state)
+{
+       struct drm_device *dev = cookie;
+
+       if (dev->driver->vgaarb_irq) {
+               dev->driver->vgaarb_irq(dev, state);
+               return;
+       }
+
+       if (!dev->irq_enabled)
+               return;
+
+       if (state)
+               dev->driver->irq_uninstall(dev);
+       else {
+               dev->driver->irq_preinstall(dev);
+               dev->driver->irq_postinstall(dev);
+       }
+}
+
 /**
  * Install IRQ handler.
  *
@@ -231,6 +253,9 @@ int drm_irq_install(struct drm_device *dev)
                return ret;
        }
 
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               vga_client_register(dev->pdev, (void *)dev, drm_irq_vgaarb_nokms, NULL);
+
        /* After installing handler */
        ret = dev->driver->irq_postinstall(dev);
        if (ret < 0) {
@@ -279,6 +304,9 @@ int drm_irq_uninstall(struct drm_device * dev)
 
        DRM_DEBUG("irq=%d\n", dev->pdev->irq);
 
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               vga_client_register(dev->pdev, NULL, NULL, NULL);
+
        dev->driver->irq_uninstall(dev);
 
        free_irq(dev->pdev->irq, dev);
@@ -402,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);
@@ -437,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
@@ -448,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
@@ -523,6 +573,63 @@ out:
        return ret;
 }
 
+static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
+                                 union drm_wait_vblank *vblwait,
+                                 struct drm_file *file_priv)
+{
+       struct drm_pending_vblank_event *e;
+       struct timeval now;
+       unsigned long flags;
+       unsigned int seq;
+
+       e = kzalloc(sizeof *e, GFP_KERNEL);
+       if (e == NULL)
+               return -ENOMEM;
+
+       e->pipe = pipe;
+       e->event.base.type = DRM_EVENT_VBLANK;
+       e->event.base.length = sizeof e->event;
+       e->event.user_data = vblwait->request.signal;
+       e->base.event = &e->event.base;
+       e->base.file_priv = file_priv;
+       e->base.destroy = (void (*) (struct drm_pending_event *)) kfree;
+
+       do_gettimeofday(&now);
+       spin_lock_irqsave(&dev->event_lock, flags);
+
+       if (file_priv->event_space < sizeof e->event) {
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+               kfree(e);
+               return -ENOMEM;
+       }
+
+       file_priv->event_space -= sizeof e->event;
+       seq = drm_vblank_count(dev, pipe);
+       if ((vblwait->request.type & _DRM_VBLANK_NEXTONMISS) &&
+           (seq - vblwait->request.sequence) <= (1 << 23)) {
+               vblwait->request.sequence = seq + 1;
+               vblwait->reply.sequence = vblwait->request.sequence;
+       }
+
+       DRM_DEBUG("event on vblank count %d, current %d, crtc %d\n",
+                 vblwait->request.sequence, seq, pipe);
+
+       e->event.sequence = vblwait->request.sequence;
+       if ((seq - vblwait->request.sequence) <= (1 << 23)) {
+               e->event.tv_sec = now.tv_sec;
+               e->event.tv_usec = now.tv_usec;
+               drm_vblank_put(dev, e->pipe);
+               list_add_tail(&e->base.link, &e->base.file_priv->event_list);
+               wake_up_interruptible(&e->base.file_priv->event_wait);
+       } else {
+               list_add_tail(&e->base.link, &dev->vblank_event_list);
+       }
+
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       return 0;
+}
+
 /**
  * Wait for VBLANK.
  *
@@ -566,7 +673,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
 
        ret = drm_vblank_get(dev, crtc);
        if (ret) {
-               DRM_ERROR("failed to acquire vblank counter, %d\n", ret);
+               DRM_DEBUG("failed to acquire vblank counter, %d\n", ret);
                return ret;
        }
        seq = drm_vblank_count(dev, crtc);
@@ -582,6 +689,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
                goto done;
        }
 
+       if (flags & _DRM_VBLANK_EVENT)
+               return drm_queue_vblank_event(dev, crtc, vblwait, file_priv);
+
        if ((flags & _DRM_VBLANK_NEXTONMISS) &&
            (seq - vblwait->request.sequence) <= (1<<23)) {
                vblwait->request.sequence = seq + 1;
@@ -614,6 +724,38 @@ done:
        return ret;
 }
 
+void drm_handle_vblank_events(struct drm_device *dev, int crtc)
+{
+       struct drm_pending_vblank_event *e, *t;
+       struct timeval now;
+       unsigned long flags;
+       unsigned int seq;
+
+       do_gettimeofday(&now);
+       seq = drm_vblank_count(dev, crtc);
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+
+       list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
+               if (e->pipe != crtc)
+                       continue;
+               if ((seq - e->event.sequence) > (1<<23))
+                       continue;
+
+               DRM_DEBUG("vblank event on %d, current %d\n",
+                         e->event.sequence, seq);
+
+               e->event.sequence = seq;
+               e->event.tv_sec = now.tv_sec;
+               e->event.tv_usec = now.tv_usec;
+               drm_vblank_put(dev, e->pipe);
+               list_move_tail(&e->base.link, &e->base.file_priv->event_list);
+               wake_up_interruptible(&e->base.file_priv->event_wait);
+       }
+
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
 /**
  * drm_handle_vblank - handle a vblank event
  * @dev: DRM device
@@ -624,7 +766,11 @@ done:
  */
 void drm_handle_vblank(struct drm_device *dev, int crtc)
 {
+       if (!dev->num_crtcs)
+               return;
+
        atomic_inc(&dev->_vblank_count[crtc]);
        DRM_WAKEUP(&dev->vbl_queue[crtc]);
+       drm_handle_vblank_events(dev, crtc);
 }
 EXPORT_SYMBOL(drm_handle_vblank);