diff options
Diffstat (limited to 'linux-core/drm_irq.c')
-rw-r--r-- | linux-core/drm_irq.c | 195 |
1 files changed, 168 insertions, 27 deletions
diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index d0d6f987..800768ae 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -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; |