OSDN Git Service

Merge commit 'origin/master' into modesetting-gem
[android-x86/external-libdrm.git] / linux-core / drm_irq.c
index d0d6f98..800768a 100644 (file)
@@ -63,7 +63,7 @@ int drm_irq_by_busid(struct drm_device *dev, void *data,
            p->devnum != PCI_SLOT(dev->pdev->devfn) || p->funcnum != PCI_FUNC(dev->pdev->devfn))
                return -EINVAL;
 
-       p->irq = dev->irq;
+       p->irq = dev->pdev->irq;
 
        DRM_DEBUG("%d:%d:%d => IRQ %d\n", p->busnum, p->devnum, p->funcnum,
                  p->irq);
@@ -96,30 +96,37 @@ static void vblank_disable_fn(unsigned long arg)
 
 static void drm_vblank_cleanup(struct drm_device *dev)
 {
-       /* Bail if the driver didn't call drm_vblank_init() */
-       if (dev->num_crtcs == 0)
-               return;
-
        del_timer(&dev->vblank_disable_timer);
 
        vblank_disable_fn((unsigned long)dev);
 
-       drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs,
+       if (dev->vbl_queue)
+           drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs,
                 DRM_MEM_DRIVER);
-       drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs,
+
+       if (dev->vbl_sigs)
+           drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs,
                 DRM_MEM_DRIVER);
-       drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) *
+
+       if (dev->_vblank_count)
+           drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) *
                 dev->num_crtcs, DRM_MEM_DRIVER);
-       drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) *
+
+       if (dev->vblank_refcount)
+           drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) *
                 dev->num_crtcs, DRM_MEM_DRIVER);
-       drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) *
+
+       if (dev->vblank_enabled)
+           drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) *
                 dev->num_crtcs, DRM_MEM_DRIVER);
-       drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs,
+
+       if (dev->last_vblank)
+           drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs,
                 DRM_MEM_DRIVER);
-       drm_free(dev->vblank_inmodeset, sizeof(*dev->vblank_inmodeset) *
-                dev->num_crtcs, DRM_MEM_DRIVER);
 
-       dev->num_crtcs = 0;
+       if (dev->vblank_inmodeset)
+           drm_free(dev->vblank_inmodeset, sizeof(*dev->vblank_inmodeset) *
+                    dev->num_crtcs, DRM_MEM_DRIVER);
 }
 
 int drm_vblank_init(struct drm_device *dev, int num_crtcs)
@@ -128,6 +135,7 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
 
        setup_timer(&dev->vblank_disable_timer, vblank_disable_fn,
                    (unsigned long)dev);
+       init_timer_deferrable(&dev->vblank_disable_timer);
        spin_lock_init(&dev->vbl_lock);
        atomic_set(&dev->vbl_signal_pending, 0);
        dev->num_crtcs = num_crtcs;
@@ -184,6 +192,82 @@ err:
 }
 EXPORT_SYMBOL(drm_vblank_init);
 
+int drm_wait_hotplug(struct drm_device *dev, void *data,
+                   struct drm_file *file_priv)
+{
+       union drm_wait_hotplug *hotplugwait = data;
+       struct timeval now;
+       int ret = 0;
+       unsigned int flags;
+
+       if ((!dev->irq) || (!dev->irq_enabled))
+               return -EINVAL;
+
+       flags = hotplugwait->request.type;
+
+       if (flags & _DRM_HOTPLUG_SIGNAL) {
+               unsigned long irqflags;
+               struct list_head *hotplug_sigs = dev->hotplug_sigs;
+               struct drm_hotplug_sig *hotplug_sig;
+
+               hotplug_sig = drm_calloc(1, sizeof(struct drm_hotplug_sig),
+                                    DRM_MEM_DRIVER);
+               if (!hotplug_sig)
+                       return -ENOMEM;
+
+               atomic_inc(&dev->hotplug_signal_pending);
+
+               hotplug_sig->info.si_signo = hotplugwait->request.signal;
+               hotplug_sig->task = current;
+               hotplug_sig->counter = 
+                       hotplugwait->reply.counter = 
+                                       dev->mode_config.hotplug_counter;
+
+               spin_lock_irqsave(&dev->hotplug_lock, irqflags);
+
+               list_add_tail(&hotplug_sig->head, hotplug_sigs);
+
+               spin_unlock_irqrestore(&dev->hotplug_lock, irqflags);
+       } else {
+               int cur_hotplug = dev->mode_config.hotplug_counter;
+
+               DRM_WAIT_ON(ret, dev->hotplug_queue, 3 * DRM_HZ,
+                               dev->mode_config.hotplug_counter > cur_hotplug);
+
+               do_gettimeofday(&now);
+
+               hotplugwait->reply.tval_sec = now.tv_sec;
+               hotplugwait->reply.tval_usec = now.tv_usec;
+               hotplugwait->reply.counter = dev->mode_config.hotplug_counter;
+       }
+
+       return ret;
+}
+
+static void drm_hotplug_cleanup(struct drm_device *dev)
+{
+       if (dev->hotplug_sigs)
+           drm_free(dev->hotplug_sigs, sizeof(*dev->hotplug_sigs),
+                DRM_MEM_DRIVER);
+}
+EXPORT_SYMBOL(drm_hotplug_cleanup);
+
+int drm_hotplug_init(struct drm_device *dev)
+{
+       spin_lock_init(&dev->hotplug_lock);
+       atomic_set(&dev->hotplug_signal_pending, 0);
+
+       dev->hotplug_sigs = drm_alloc(sizeof(struct list_head), DRM_MEM_DRIVER);
+       if (!dev->hotplug_sigs)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(dev->hotplug_sigs);
+       init_waitqueue_head(&dev->hotplug_queue);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_hotplug_init);
+
 /**
  * Install IRQ handler.
  *
@@ -201,7 +285,7 @@ int drm_irq_install(struct drm_device * dev)
        if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
                return -EINVAL;
 
-       if (dev->irq == 0)
+       if (dev->pdev->irq == 0)
                return -EINVAL;
 
        mutex_lock(&dev->struct_mutex);
@@ -214,12 +298,12 @@ int drm_irq_install(struct drm_device * dev)
 
        if (dev->irq_enabled) {
                mutex_unlock(&dev->struct_mutex);
-               return -EBUSY;
+               return 0;
        }
        dev->irq_enabled = 1;
        mutex_unlock(&dev->struct_mutex);
 
-       DRM_DEBUG("irq=%d\n", dev->irq);
+       DRM_DEBUG("irq=%d\n", dev->pdev->irq);
 
        /* Before installing handler */
        dev->driver->irq_preinstall(dev);
@@ -228,7 +312,7 @@ int drm_irq_install(struct drm_device * dev)
        if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED))
                sh_flags = IRQF_SHARED;
 
-       ret = request_irq(dev->irq, dev->driver->irq_handler,
+       ret = request_irq(dev->pdev->irq, dev->driver->irq_handler,
                          sh_flags, dev->devname, dev);
        if (ret < 0) {
                mutex_lock(&dev->struct_mutex);
@@ -236,6 +320,10 @@ int drm_irq_install(struct drm_device * dev)
                mutex_unlock(&dev->struct_mutex);
                return ret;
        }
+       /* Expose the device irq to device drivers that want to export it for
+        * whatever reason.
+        */
+       dev->irq = dev->pdev->irq;
 
        /* After installing handler */
        ret = dev->driver->irq_postinstall(dev);
@@ -271,14 +359,16 @@ int drm_irq_uninstall(struct drm_device * dev)
        if (!irq_enabled)
                return -EINVAL;
 
-       DRM_DEBUG("irq=%d\n", dev->irq);
+       DRM_DEBUG("irq=%d\n", dev->pdev->irq);
 
        dev->driver->irq_uninstall(dev);
 
-       free_irq(dev->irq, dev);
+       free_irq(dev->pdev->irq, dev);
 
        drm_vblank_cleanup(dev);
 
+       drm_hotplug_cleanup(dev);
+
        dev->locked_tasklet_func = NULL;
 
        return 0;
@@ -308,13 +398,17 @@ int drm_control(struct drm_device *dev, void *data,
        case DRM_INST_HANDLER:
                if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
                        return 0;
+               if (drm_core_check_feature(dev, DRIVER_MODESET))
+                       return 0;
                if (dev->if_version < DRM_IF_VERSION(1, 2) &&
-                   ctl->irq != dev->irq)
+                   ctl->irq != dev->pdev->irq)
                        return -EINVAL;
                return drm_irq_install(dev);
        case DRM_UNINST_HANDLER:
                if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
                        return 0;
+               if (drm_core_check_feature(dev, DRIVER_MODESET))
+                       return 0;
                return drm_irq_uninstall(dev);
        default:
                return -EINVAL;
@@ -514,7 +608,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
        int ret = 0;
        unsigned int flags, seq, crtc;
 
-       if ((!dev->irq) || (!dev->irq_enabled))
+       if ((!dev->pdev->irq) || (!dev->irq_enabled))
                return -EINVAL;
 
        if (vblwait->request.type &
@@ -555,7 +649,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
        if (flags & _DRM_VBLANK_SIGNAL) {
                unsigned long irqflags;
                struct list_head *vbl_sigs = &dev->vbl_sigs[crtc];
-               struct drm_vbl_sig *vbl_sig;
+               struct drm_vbl_sig *vbl_sig, *tmp;
 
                spin_lock_irqsave(&dev->vbl_lock, irqflags);
 
@@ -563,7 +657,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
                 * for the same vblank sequence number; nothing to be done in
                 * that case
                 */
-               list_for_each_entry(vbl_sig, vbl_sigs, head) {
+               list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) {
                        if (vbl_sig->sequence == vblwait->request.sequence
                            && vbl_sig->info.si_signo ==
                            vblwait->request.signal
@@ -688,6 +782,53 @@ void drm_handle_vblank(struct drm_device *dev, int crtc)
 EXPORT_SYMBOL(drm_handle_vblank);
 
 /**
+ * Send the HOTPLUG signals.
+ *
+ * \param dev DRM device.
+ *
+ * Sends a signal for each task in drm_device::hotplug_sigs and empties the list.
+ */
+static void drm_hotplug_send_signals(struct drm_device * dev)
+{
+       struct drm_hotplug_sig *hotplug_sig, *tmp;
+       struct list_head *hotplug_sigs;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->hotplug_lock, flags);
+
+       hotplug_sigs = dev->hotplug_sigs;
+
+       list_for_each_entry_safe(hotplug_sig, tmp, hotplug_sigs, head) {
+           hotplug_sig->info.si_code = hotplug_sig->counter;
+
+           send_sig_info(hotplug_sig->info.si_signo,
+                             &hotplug_sig->info, hotplug_sig->task);
+
+           list_del(&hotplug_sig->head);
+
+           drm_free(hotplug_sig, sizeof(*hotplug_sig),
+                        DRM_MEM_DRIVER);
+           atomic_dec(&dev->hotplug_signal_pending);
+       }
+
+       spin_unlock_irqrestore(&dev->hotplug_lock, flags);
+}
+
+/**
+ * drm_handle_hotplug - handle a hotplug event
+ * @dev: DRM device
+ * @crtc: where this event occurred
+ *
+ * Drivers should call this routine in their hotplug interrupt handlers.
+ */
+void drm_handle_hotplug(struct drm_device *dev)
+{
+       DRM_WAKEUP(&dev->hotplug_queue);
+       drm_hotplug_send_signals(dev);
+}
+EXPORT_SYMBOL(drm_handle_hotplug);
+
+/**
  * Tasklet wrapper function.
  *
  * \param data DRM device in disguise.
@@ -704,18 +845,18 @@ static void drm_locked_tasklet_func(unsigned long data)
        spin_lock_irqsave(&dev->tasklet_lock, irqflags);
 
        if (!dev->locked_tasklet_func ||
-           !drm_lock_take(&dev->lock,
+           !drm_lock_take(&dev->primary->master->lock,
                           DRM_KERNEL_CONTEXT)) {
                spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
                return;
        }
 
-       dev->lock.lock_time = jiffies;
+       dev->primary->master->lock.lock_time = jiffies;
        atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
 
        dev->locked_tasklet_func(dev);
 
-       drm_lock_free(&dev->lock,
+       drm_lock_free(&dev->primary->master->lock,
                      DRM_KERNEL_CONTEXT);
 
        dev->locked_tasklet_func = NULL;