From db689c7b95613237cec904c3f6ee27e8c2bf7ce0 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 12 Jun 2007 10:44:21 -0700 Subject: Initial checkin of vblank rework. Code attempts to reduce the number of vblank interrupt in order to save power. --- linux-core/drm_irq.c | 69 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 16 deletions(-) (limited to 'linux-core/drm_irq.c') diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index 88716712..f229f778 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -221,6 +221,44 @@ int drm_control(struct inode *inode, struct file *filp, } } +static void drm_vblank_get(drm_device_t *dev, int crtc) +{ + unsigned long irqflags; + u32 cur_vblank, diff; + + if (atomic_add_return(1, &dev->vbl_pending[crtc]) != 1) + return; + + /* + * Interrupts were disabled prior to this call, so deal with counter + * wrap if needed. + * NOTE! It's possible we lost a full dev->max_vblank_count events + * here if the register is small or we had vblank interrupts off for + * a long time. + */ + cur_vblank = dev->driver->get_vblank_counter(dev, crtc); + spin_lock_irqsave(&dev->vbl_lock, irqflags); + if (cur_vblank < dev->last_vblank[crtc]) { + diff = dev->max_vblank_count - + dev->last_vblank[crtc]; + diff += cur_vblank; + } else { + diff = cur_vblank - last_vblank; + } + dev->last_vblank[crtc] = cur_vblank; + spin_lock_irqrestore(&dev->vbl_lock, irqflags); + + atomic_add(diff, &dev->vblank_count[crtc]); + dev->driver->enable_vblank(dev, crtc); +} + +static void drm_vblank_put(drm_device_t *dev, int crtc) +{ + if (atomic_dec_and_test(&dev->vbl_pending[crtc])) + dev->driver->disable_vblank(dev, crtc); +} + + /** * Wait for VBLANK. * @@ -248,7 +286,7 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) drm_wait_vblank_t vblwait; struct timeval now; int ret = 0; - unsigned int flags, seq; + unsigned int flags, seq, crtc; if ((!dev->irq) || (!dev->irq_enabled)) return -EINVAL; @@ -265,13 +303,14 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) } flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK; + crtc = flags & _DRM_VBLANK_SECONDARY ? 1 : 0; if (!drm_core_check_feature(dev, (flags & _DRM_VBLANK_SECONDARY) ? DRIVER_IRQ_VBL2 : DRIVER_IRQ_VBL)) return -EINVAL; - seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_received2 - : &dev->vbl_received); + drm_vblank_get(dev, crtc); + seq = atomic_read(&dev->vblank_count[crtc]); switch (vblwait.request.type & _DRM_VBLANK_TYPES_MASK) { case _DRM_VBLANK_RELATIVE: @@ -307,22 +346,23 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) spin_unlock_irqrestore(&dev->vbl_lock, irqflags); vblwait.reply.sequence = seq; + drm_vblank_put(dev, crtc); goto done; } } - if (dev->vbl_pending >= 100) { + if (atomic_read(&dev->vbl_pending[crtc]) >= 100) { spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + drm_vblank_put(dev, crtc); return -EBUSY; } - dev->vbl_pending++; - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); if (! (vbl_sig = drm_alloc(sizeof(drm_vbl_sig_t), DRM_MEM_DRIVER))) { + drm_vblank_put(dev, crtc); return -ENOMEM; } @@ -340,14 +380,12 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) vblwait.reply.sequence = seq; } else { - if (flags & _DRM_VBLANK_SECONDARY) { - if (dev->driver->vblank_wait2) - ret = dev->driver->vblank_wait2(dev, &vblwait.request.sequence); - } else if (dev->driver->vblank_wait) - ret = - dev->driver->vblank_wait(dev, - &vblwait.request.sequence); + unsigned long cur_vblank; + DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, + (((cur_vblank = atomic_read(&dev->vblank_count[crtc])) + - seq) <= (1 << 23))); + drm_vblank_put(dev, crtc); do_gettimeofday(&now); vblwait.reply.tval_sec = now.tv_sec; vblwait.reply.tval_usec = now.tv_usec; @@ -379,8 +417,7 @@ void drm_vbl_send_signals(drm_device_t * dev) for (i = 0; i < 2; i++) { drm_vbl_sig_t *vbl_sig, *tmp; struct list_head *vbl_sigs = i ? &dev->vbl_sigs2 : &dev->vbl_sigs; - unsigned int vbl_seq = atomic_read(i ? &dev->vbl_received2 : - &dev->vbl_received); + unsigned int vbl_seq = atomic_read(&dev->vblank_count[i]); list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) { if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { @@ -393,7 +430,7 @@ void drm_vbl_send_signals(drm_device_t * dev) drm_free(vbl_sig, sizeof(*vbl_sig), DRM_MEM_DRIVER); - dev->vbl_pending--; + drm_vblank_put(dev, i); } } } -- cgit v1.2.3 From ca47fa90b73d0eac73fb7d1ba712d81e180eae7d Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 12 Jun 2007 13:35:41 -0700 Subject: Update vblank code: - move pre/post modeset ioctl to core - fixup i915 buffer swap - fix outstanding signal count code - create new core vblank init routine - test (works with glxgears) - simplify i915 interrupt handler --- linux-core/drm_irq.c | 145 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 119 insertions(+), 26 deletions(-) (limited to 'linux-core/drm_irq.c') diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index f229f778..8125b75c 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -77,6 +77,70 @@ int drm_irq_by_busid(struct inode *inode, struct file *filp, return 0; } +int drm_vblank_init(drm_device_t *dev, int num_crtcs) +{ + int i, ret = -ENOMEM; + + init_waitqueue_head(&dev->vbl_queue); + spin_lock_init(&dev->vbl_lock); + atomic_set(&dev->vbl_pending, 0); + dev->num_crtcs = num_crtcs; + + dev->vbl_sigs = drm_alloc(sizeof(struct list_head) * num_crtcs, + DRM_MEM_DRIVER); + if (!dev->vbl_sigs) + goto err; + + dev->vblank_count = drm_alloc(sizeof(atomic_t) * num_crtcs, + DRM_MEM_DRIVER); + if (!dev->vblank_count) + goto err; + + dev->vblank_usage = drm_alloc(sizeof(atomic_t) * num_crtcs, + DRM_MEM_DRIVER); + if (!dev->vblank_count) + goto err; + + dev->last_vblank = drm_alloc(sizeof(u32) * num_crtcs, + DRM_MEM_DRIVER); + if (!dev->last_vblank) + goto err; + + dev->vblank_premodeset = drm_alloc(sizeof(u32) * num_crtcs, + DRM_MEM_DRIVER); + if (!dev->vblank_premodeset) + goto err; + + dev->vblank_offset = drm_alloc(sizeof(u32) * num_crtcs, + DRM_MEM_DRIVER); + if (!dev->vblank_offset) + goto err; + + /* Zero per-crtc vblank stuff */ + for (i = 0; i < num_crtcs; i++) { + INIT_LIST_HEAD(&dev->vbl_sigs[i]); + atomic_set(&dev->vblank_count[i], 0); + atomic_set(&dev->vblank_usage[i], 0); + dev->last_vblank[i] = 0; + dev->vblank_premodeset[i] = 0; + dev->vblank_offset[i] = 0; + } + + ret = 0; + goto out; + +err: + kfree(dev->vbl_sigs); + kfree(dev->vblank_count); + kfree(dev->vblank_usage); + kfree(dev->last_vblank); + kfree(dev->vblank_premodeset); + kfree(dev->vblank_offset); +out: + return ret; +} +EXPORT_SYMBOL(drm_vblank_init); + /** * Install IRQ handler. * @@ -88,7 +152,7 @@ int drm_irq_by_busid(struct inode *inode, struct file *filp, */ static int drm_irq_install(drm_device_t * dev) { - int ret; + int ret = 0; unsigned long sh_flags = 0; if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) @@ -114,17 +178,6 @@ static int drm_irq_install(drm_device_t * dev) DRM_DEBUG("%s: irq=%d\n", __FUNCTION__, dev->irq); - if (drm_core_check_feature(dev, DRIVER_IRQ_VBL)) { - init_waitqueue_head(&dev->vbl_queue); - - spin_lock_init(&dev->vbl_lock); - - INIT_LIST_HEAD(&dev->vbl_sigs); - INIT_LIST_HEAD(&dev->vbl_sigs2); - - dev->vbl_pending = 0; - } - /* Before installing handler */ dev->driver->irq_preinstall(dev); @@ -142,9 +195,9 @@ static int drm_irq_install(drm_device_t * dev) } /* After installing handler */ - dev->driver->irq_postinstall(dev); + ret = dev->driver->irq_postinstall(dev); - return 0; + return ret; } /** @@ -221,12 +274,12 @@ int drm_control(struct inode *inode, struct file *filp, } } -static void drm_vblank_get(drm_device_t *dev, int crtc) +void drm_vblank_get(drm_device_t *dev, int crtc) { unsigned long irqflags; u32 cur_vblank, diff; - if (atomic_add_return(1, &dev->vbl_pending[crtc]) != 1) + if (atomic_add_return(1, &dev->vblank_count[crtc]) != 1) return; /* @@ -243,21 +296,60 @@ static void drm_vblank_get(drm_device_t *dev, int crtc) dev->last_vblank[crtc]; diff += cur_vblank; } else { - diff = cur_vblank - last_vblank; + diff = cur_vblank - dev->last_vblank[crtc]; } dev->last_vblank[crtc] = cur_vblank; - spin_lock_irqrestore(&dev->vbl_lock, irqflags); + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); atomic_add(diff, &dev->vblank_count[crtc]); dev->driver->enable_vblank(dev, crtc); } +EXPORT_SYMBOL(drm_vblank_get); -static void drm_vblank_put(drm_device_t *dev, int crtc) +void drm_vblank_put(drm_device_t *dev, int crtc) { - if (atomic_dec_and_test(&dev->vbl_pending[crtc])) + if (atomic_dec_and_test(&dev->vblank_count[crtc])) dev->driver->disable_vblank(dev, crtc); } +EXPORT_SYMBOL(drm_vblank_put); +int drm_modeset_ctl(DRM_IOCTL_ARGS) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->head->dev; + drm_modeset_ctl_t __user *argp = (void __user *)data; + drm_modeset_ctl_t modeset; + int crtc, ret = 0; + u32 new; + + if (copy_from_user(&modeset, argp, sizeof(modeset))) { + ret = -EFAULT; + goto out; + } + + crtc = modeset.arg; + if (crtc > dev->num_crtcs) { + ret = -EINVAL; + goto out; + } + + switch (modeset.cmd) { + case _DRM_PRE_MODESET: + dev->vblank_premodeset[crtc] = + dev->driver->get_vblank_counter(dev, crtc); + break; + case _DRM_POST_MODESET: + new = dev->driver->get_vblank_counter(dev, crtc); + dev->vblank_offset[crtc] = dev->vblank_premodeset[crtc] - new; + break; + default: + ret = -EINVAL; + break; + } + +out: + return ret; +} /** * Wait for VBLANK. @@ -329,8 +421,7 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) if (flags & _DRM_VBLANK_SIGNAL) { unsigned long irqflags; - struct list_head *vbl_sigs = (flags & _DRM_VBLANK_SECONDARY) - ? &dev->vbl_sigs2 : &dev->vbl_sigs; + struct list_head *vbl_sigs = &dev->vbl_sigs[crtc]; drm_vbl_sig_t *vbl_sig; spin_lock_irqsave(&dev->vbl_lock, irqflags); @@ -351,7 +442,7 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) } } - if (atomic_read(&dev->vbl_pending[crtc]) >= 100) { + if (atomic_read(&dev->vbl_pending) >= 100) { spin_unlock_irqrestore(&dev->vbl_lock, irqflags); drm_vblank_put(dev, crtc); return -EBUSY; @@ -359,6 +450,8 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + atomic_inc(&dev->vbl_pending); + if (! (vbl_sig = drm_alloc(sizeof(drm_vbl_sig_t), DRM_MEM_DRIVER))) { @@ -414,9 +507,9 @@ void drm_vbl_send_signals(drm_device_t * dev) spin_lock_irqsave(&dev->vbl_lock, flags); - for (i = 0; i < 2; i++) { + for (i = 0; i < dev->num_crtcs; i++) { drm_vbl_sig_t *vbl_sig, *tmp; - struct list_head *vbl_sigs = i ? &dev->vbl_sigs2 : &dev->vbl_sigs; + struct list_head *vbl_sigs = &dev->vbl_sigs[i]; unsigned int vbl_seq = atomic_read(&dev->vblank_count[i]); list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) { @@ -429,7 +522,7 @@ void drm_vbl_send_signals(drm_device_t * dev) drm_free(vbl_sig, sizeof(*vbl_sig), DRM_MEM_DRIVER); - + atomic_dec(&dev->vbl_pending); drm_vblank_put(dev, i); } } -- cgit v1.2.3 From b06268294afb47e62949984d73905344dd160262 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 14 Jun 2007 11:32:31 -0700 Subject: Comment new vblank routines and fixup several issues: - use correct refcount variable in get/put routines - extract counter update from drm_vblank_get - make signal handling callback per-crtc - update interrupt handling logic, drivers should use drm_handle_vblank - move wakeup and counter update logic to new drm_handle_vblank routine - fixup usage of get/put in light of counter update extraction - fix longstanding bug in signal code, update pending counter only *after* we're sure we'll setup signal handling --- linux-core/drm_irq.c | 191 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 139 insertions(+), 52 deletions(-) (limited to 'linux-core/drm_irq.c') diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index 8125b75c..7bdb01b2 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -83,7 +83,7 @@ int drm_vblank_init(drm_device_t *dev, int num_crtcs) init_waitqueue_head(&dev->vbl_queue); spin_lock_init(&dev->vbl_lock); - atomic_set(&dev->vbl_pending, 0); + atomic_set(&dev->vbl_signal_pending, 0); dev->num_crtcs = num_crtcs; dev->vbl_sigs = drm_alloc(sizeof(struct list_head) * num_crtcs, @@ -91,14 +91,14 @@ int drm_vblank_init(drm_device_t *dev, int num_crtcs) if (!dev->vbl_sigs) goto err; - dev->vblank_count = drm_alloc(sizeof(atomic_t) * num_crtcs, + dev->_vblank_count = drm_alloc(sizeof(atomic_t) * num_crtcs, DRM_MEM_DRIVER); - if (!dev->vblank_count) + if (!dev->_vblank_count) goto err; - dev->vblank_usage = drm_alloc(sizeof(atomic_t) * num_crtcs, - DRM_MEM_DRIVER); - if (!dev->vblank_count) + dev->vblank_refcount = drm_alloc(sizeof(atomic_t) * num_crtcs, + DRM_MEM_DRIVER); + if (!dev->vblank_refcount) goto err; dev->last_vblank = drm_alloc(sizeof(u32) * num_crtcs, @@ -119,24 +119,28 @@ int drm_vblank_init(drm_device_t *dev, int num_crtcs) /* Zero per-crtc vblank stuff */ for (i = 0; i < num_crtcs; i++) { INIT_LIST_HEAD(&dev->vbl_sigs[i]); - atomic_set(&dev->vblank_count[i], 0); - atomic_set(&dev->vblank_usage[i], 0); + atomic_set(&dev->_vblank_count[i], 0); + atomic_set(&dev->vblank_refcount[i], 0); dev->last_vblank[i] = 0; dev->vblank_premodeset[i] = 0; dev->vblank_offset[i] = 0; } - ret = 0; - goto out; + return 0; err: - kfree(dev->vbl_sigs); - kfree(dev->vblank_count); - kfree(dev->vblank_usage); - kfree(dev->last_vblank); - kfree(dev->vblank_premodeset); - kfree(dev->vblank_offset); -out: + drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * num_crtcs, + DRM_MEM_DRIVER); + drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) * num_crtcs, + DRM_MEM_DRIVER); + drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) * + num_crtcs, DRM_MEM_DRIVER); + drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * num_crtcs, + DRM_MEM_DRIVER); + drm_free(dev->vblank_premodeset, sizeof(*dev->vblank_premodeset) * + num_crtcs, DRM_MEM_DRIVER); + drm_free(dev->vblank_offset, sizeof(*dev->vblank_offset) * num_crtcs, + DRM_MEM_DRIVER); return ret; } EXPORT_SYMBOL(drm_vblank_init); @@ -274,14 +278,37 @@ int drm_control(struct inode *inode, struct file *filp, } } -void drm_vblank_get(drm_device_t *dev, int crtc) +/** + * drm_vblank_count - retrieve "cooked" vblank counter value + * @dev: DRM device + * @crtc: which counter to retrieve + * + * Fetches the "cooked" vblank count value that represents the number of + * vblank events since the system was booted, including lost events due to + * modesetting activity. + */ +u32 drm_vblank_count(drm_device_t *dev, int crtc) +{ + return atomic_read(&dev->_vblank_count[crtc]) + + dev->vblank_offset[crtc]; +} +EXPORT_SYMBOL(drm_vblank_count); + +/** + * drm_update_vblank_count - update the master vblank counter + * @dev: DRM device + * @crtc: counter to update + * + * Call back into the driver to update the appropriate vblank counter + * (specified by @crtc). Deal with wraparound, if it occurred, and + * update the last read value so we can deal with wraparound on the next + * call if necessary. + */ +void drm_update_vblank_count(drm_device_t *dev, int crtc) { unsigned long irqflags; u32 cur_vblank, diff; - if (atomic_add_return(1, &dev->vblank_count[crtc]) != 1) - return; - /* * Interrupts were disabled prior to this call, so deal with counter * wrap if needed. @@ -301,18 +328,61 @@ void drm_vblank_get(drm_device_t *dev, int crtc) dev->last_vblank[crtc] = cur_vblank; spin_unlock_irqrestore(&dev->vbl_lock, irqflags); - atomic_add(diff, &dev->vblank_count[crtc]); - dev->driver->enable_vblank(dev, crtc); + atomic_add(diff, &dev->_vblank_count[crtc]); +} +EXPORT_SYMBOL(drm_update_vblank_count); + +/** + * drm_vblank_get - get a reference count on vblank events + * @dev: DRM device + * @crtc: which CRTC to own + * + * Acquire a reference count on vblank events to avoid having them disabled + * while in use. Note callers will probably want to update the master counter + * using drm_update_vblank_count() above before calling this routine so that + * wakeups occur on the right vblank event. + * + * RETURNS + * Zero on success, nonzero on failure. + */ +int drm_vblank_get(drm_device_t *dev, int crtc) +{ + int ret = 0; + + /* Going from 0->1 means we have to enable interrupts again */ + if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1) { + ret = dev->driver->enable_vblank(dev, crtc); + if (ret) + atomic_dec(&dev->vblank_refcount[crtc]); + } + + return ret; } EXPORT_SYMBOL(drm_vblank_get); +/** + * drm_vblank_put - give up ownership of vblank events + * @dev: DRM device + * @crtc: which counter to give up + * + * Release ownership of a given vblank counter, turning off interrupts + * if possible. + */ void drm_vblank_put(drm_device_t *dev, int crtc) { - if (atomic_dec_and_test(&dev->vblank_count[crtc])) + /* Last user can disable interrupts */ + if (atomic_dec_and_test(&dev->vblank_refcount[crtc])) dev->driver->disable_vblank(dev, crtc); } EXPORT_SYMBOL(drm_vblank_put); +/** + * drm_modeset_ctl - handle vblank event counter changes across mode switch + * @DRM_IOCTL_ARGS: standard ioctl arguments + * + * Applications should call the %_DRM_PRE_MODESET and %_DRM_POST_MODESET + * ioctls around modesetting so that any lost vblank events are accounted for. + */ int drm_modeset_ctl(DRM_IOCTL_ARGS) { drm_file_t *priv = filp->private_data; @@ -401,8 +471,8 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) DRIVER_IRQ_VBL2 : DRIVER_IRQ_VBL)) return -EINVAL; - drm_vblank_get(dev, crtc); - seq = atomic_read(&dev->vblank_count[crtc]); + drm_update_vblank_count(dev, crtc); + seq = drm_vblank_count(dev, crtc); switch (vblwait.request.type & _DRM_VBLANK_TYPES_MASK) { case _DRM_VBLANK_RELATIVE: @@ -437,28 +507,28 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) spin_unlock_irqrestore(&dev->vbl_lock, irqflags); vblwait.reply.sequence = seq; - drm_vblank_put(dev, crtc); goto done; } } - if (atomic_read(&dev->vbl_pending) >= 100) { + if (atomic_read(&dev->vbl_signal_pending) >= 100) { spin_unlock_irqrestore(&dev->vbl_lock, irqflags); - drm_vblank_put(dev, crtc); return -EBUSY; } spin_unlock_irqrestore(&dev->vbl_lock, irqflags); - atomic_inc(&dev->vbl_pending); - if (! (vbl_sig = drm_alloc(sizeof(drm_vbl_sig_t), DRM_MEM_DRIVER))) { - drm_vblank_put(dev, crtc); return -ENOMEM; } + ret = drm_vblank_get(dev, crtc); + if (ret) + return ret; + atomic_inc(&dev->vbl_signal_pending); + memset((void *)vbl_sig, 0, sizeof(*vbl_sig)); vbl_sig->sequence = vblwait.request.sequence; @@ -475,8 +545,11 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) } else { unsigned long cur_vblank; + ret = drm_vblank_get(dev, crtc); + if (ret) + return ret; DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, - (((cur_vblank = atomic_read(&dev->vblank_count[crtc])) + (((cur_vblank = drm_vblank_count(dev, crtc)) - seq) <= (1 << 23))); drm_vblank_put(dev, crtc); do_gettimeofday(&now); @@ -495,42 +568,56 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) * Send the VBLANK signals. * * \param dev DRM device. + * \param crtc CRTC where the vblank event occurred * * Sends a signal for each task in drm_device::vbl_sigs and empties the list. * * If a signal is not requested, then calls vblank_wait(). */ -void drm_vbl_send_signals(drm_device_t * dev) +static void drm_vbl_send_signals(drm_device_t * dev, int crtc) { + drm_vbl_sig_t *vbl_sig, *tmp; + struct list_head *vbl_sigs; + unsigned int vbl_seq; unsigned long flags; - int i; spin_lock_irqsave(&dev->vbl_lock, flags); - for (i = 0; i < dev->num_crtcs; i++) { - drm_vbl_sig_t *vbl_sig, *tmp; - struct list_head *vbl_sigs = &dev->vbl_sigs[i]; - unsigned int vbl_seq = atomic_read(&dev->vblank_count[i]); + vbl_sigs = &dev->vbl_sigs[crtc]; + vbl_seq = drm_vblank_count(dev, crtc); - list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) { - if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { - vbl_sig->info.si_code = vbl_seq; - send_sig_info(vbl_sig->info.si_signo, - &vbl_sig->info, vbl_sig->task); + list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) { + if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { + vbl_sig->info.si_code = vbl_seq; + send_sig_info(vbl_sig->info.si_signo, + &vbl_sig->info, vbl_sig->task); - list_del(&vbl_sig->head); + list_del(&vbl_sig->head); - drm_free(vbl_sig, sizeof(*vbl_sig), - DRM_MEM_DRIVER); - atomic_dec(&dev->vbl_pending); - drm_vblank_put(dev, i); - } - } + drm_free(vbl_sig, sizeof(*vbl_sig), + DRM_MEM_DRIVER); + atomic_dec(&dev->vbl_signal_pending); + drm_vblank_put(dev, crtc); + } } spin_unlock_irqrestore(&dev->vbl_lock, flags); } -EXPORT_SYMBOL(drm_vbl_send_signals); + +/** + * drm_handle_vblank - handle a vblank event + * @dev: DRM device + * @crtc: where this event occurred + * + * Drivers should call this routine in their vblank interrupt handlers to + * update the vblank counter and send any signals that may be pending. + */ +void drm_handle_vblank(drm_device_t *dev, int crtc) +{ + drm_update_vblank_count(dev, crtc); + drm_vbl_send_signals(dev, crtc); +} +EXPORT_SYMBOL(drm_handle_vblank); /** * Tasklet wrapper function. -- cgit v1.2.3 From 1000d88ddfcd0ae769125db37d4e78643a430caf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Fri, 15 Jun 2007 10:10:33 +0200 Subject: Fix memory leaks in vblank error paths. Also use drm_calloc instead of drm_alloc and memset, and use the size of the struct instead of the size of the pointer for allocation... --- linux-core/drm_irq.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'linux-core/drm_irq.c') diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index 7bdb01b2..f73d067f 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -518,18 +518,19 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) spin_unlock_irqrestore(&dev->vbl_lock, irqflags); - if (! - (vbl_sig = - drm_alloc(sizeof(drm_vbl_sig_t), DRM_MEM_DRIVER))) { + vbl_sig = drm_calloc(1, sizeof(drm_vbl_sig_t), DRM_MEM_DRIVER); + if (!vbl_sig) { return -ENOMEM; } ret = drm_vblank_get(dev, crtc); - if (ret) + if (ret) { + drm_free(vbl_sig, sizeof(drm_vbl_sig_t), + DRM_MEM_DRIVER); return ret; - atomic_inc(&dev->vbl_signal_pending); + } - memset((void *)vbl_sig, 0, sizeof(*vbl_sig)); + atomic_inc(&dev->vbl_signal_pending); vbl_sig->sequence = vblwait.request.sequence; vbl_sig->info.si_signo = vblwait.request.signal; -- cgit v1.2.3 From 7f95a06c61f585cbc4b5fefc833432178550fe31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Fri, 15 Jun 2007 10:12:23 +0200 Subject: Return current sequence number to userspace after blocking wait for vblank. --- linux-core/drm_irq.c | 1 + 1 file changed, 1 insertion(+) (limited to 'linux-core/drm_irq.c') diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index f73d067f..b4e3c10f 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -556,6 +556,7 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) do_gettimeofday(&now); vblwait.reply.tval_sec = now.tv_sec; vblwait.reply.tval_usec = now.tv_usec; + vblwait.reply.sequence = cur_vblank; } done: -- cgit v1.2.3 From 82e2c3304d3f1697537b73a2c888c8c6b1b6cdc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Fri, 15 Jun 2007 10:25:50 +0200 Subject: Wake up vblank waitqueue in drm_handle_vblank(). --- linux-core/drm_irq.c | 1 + 1 file changed, 1 insertion(+) (limited to 'linux-core/drm_irq.c') diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index b4e3c10f..3dcde9a5 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -617,6 +617,7 @@ static void drm_vbl_send_signals(drm_device_t * dev, int crtc) void drm_handle_vblank(drm_device_t *dev, int crtc) { drm_update_vblank_count(dev, crtc); + DRM_WAKEUP(&dev->vbl_queue); drm_vbl_send_signals(dev, crtc); } EXPORT_SYMBOL(drm_handle_vblank); -- cgit v1.2.3 From fbee089aca727c92e0aa5d7a2ae7a8c5cf9c3076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Fri, 15 Jun 2007 10:49:16 +0200 Subject: Make vblank waitqueue per CRTC. --- linux-core/drm_irq.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'linux-core/drm_irq.c') diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index 3dcde9a5..f673a97c 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -81,11 +81,15 @@ int drm_vblank_init(drm_device_t *dev, int num_crtcs) { int i, ret = -ENOMEM; - init_waitqueue_head(&dev->vbl_queue); spin_lock_init(&dev->vbl_lock); atomic_set(&dev->vbl_signal_pending, 0); dev->num_crtcs = num_crtcs; + dev->vbl_queue = drm_alloc(sizeof(wait_queue_head_t) * num_crtcs, + DRM_MEM_DRIVER); + if (!dev->vbl_queue) + goto err; + dev->vbl_sigs = drm_alloc(sizeof(struct list_head) * num_crtcs, DRM_MEM_DRIVER); if (!dev->vbl_sigs) @@ -118,6 +122,7 @@ int drm_vblank_init(drm_device_t *dev, int num_crtcs) /* Zero per-crtc vblank stuff */ for (i = 0; i < num_crtcs; i++) { + init_waitqueue_head(&dev->vbl_queue[i]); INIT_LIST_HEAD(&dev->vbl_sigs[i]); atomic_set(&dev->_vblank_count[i], 0); atomic_set(&dev->vblank_refcount[i], 0); @@ -129,6 +134,8 @@ int drm_vblank_init(drm_device_t *dev, int num_crtcs) return 0; err: + drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * num_crtcs, + DRM_MEM_DRIVER); drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * num_crtcs, DRM_MEM_DRIVER); drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) * num_crtcs, @@ -150,7 +157,7 @@ EXPORT_SYMBOL(drm_vblank_init); * * \param dev DRM device. * - * Initializes the IRQ related data, and setups drm_device::vbl_queue. Installs the handler, calling the driver + * Initializes the IRQ related data. Installs the handler, calling the driver * \c drm_driver_irq_preinstall() and \c drm_driver_irq_postinstall() functions * before and after the installation. */ @@ -549,7 +556,7 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) ret = drm_vblank_get(dev, crtc); if (ret) return ret; - DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, + DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ, (((cur_vblank = drm_vblank_count(dev, crtc)) - seq) <= (1 << 23))); drm_vblank_put(dev, crtc); @@ -617,7 +624,7 @@ static void drm_vbl_send_signals(drm_device_t * dev, int crtc) void drm_handle_vblank(drm_device_t *dev, int crtc) { drm_update_vblank_count(dev, crtc); - DRM_WAKEUP(&dev->vbl_queue); + DRM_WAKEUP(&dev->vbl_queue[crtc]); drm_vbl_send_signals(dev, crtc); } EXPORT_SYMBOL(drm_handle_vblank); -- cgit v1.2.3 From 0f5334be2bc6ceca971a7a6ab3ca1c23a707867c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Fri, 15 Jun 2007 11:01:51 +0200 Subject: Remove DRIVER_IRQ_VBL(2). If the driver doesn't support vertical blank interrupts, it won't call drm_vblank_init(), and dev->num_crtcs will be 0. Also fix an off-by-one test against dev->num_crtcs. --- linux-core/drm_irq.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'linux-core/drm_irq.c') diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index f673a97c..c9d1c0d2 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -405,7 +405,7 @@ int drm_modeset_ctl(DRM_IOCTL_ARGS) } crtc = modeset.arg; - if (crtc > dev->num_crtcs) { + if (crtc >= dev->num_crtcs) { ret = -EINVAL; goto out; } @@ -474,8 +474,7 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK; crtc = flags & _DRM_VBLANK_SECONDARY ? 1 : 0; - if (!drm_core_check_feature(dev, (flags & _DRM_VBLANK_SECONDARY) ? - DRIVER_IRQ_VBL2 : DRIVER_IRQ_VBL)) + if (crtc >= dev->num_crtcs) return -EINVAL; drm_update_vblank_count(dev, crtc); -- cgit v1.2.3 From 2738bca6f52e236a2d9a0e456a78b10442ededdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Fri, 22 Jun 2007 11:44:38 +0200 Subject: Use drm_calloc instead of assigning 0. --- linux-core/drm_irq.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'linux-core/drm_irq.c') diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index c9d1c0d2..01e970f9 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -105,18 +105,18 @@ int drm_vblank_init(drm_device_t *dev, int num_crtcs) if (!dev->vblank_refcount) goto err; - dev->last_vblank = drm_alloc(sizeof(u32) * num_crtcs, - DRM_MEM_DRIVER); + dev->last_vblank = drm_calloc(1, sizeof(u32) * num_crtcs, + DRM_MEM_DRIVER); if (!dev->last_vblank) goto err; - dev->vblank_premodeset = drm_alloc(sizeof(u32) * num_crtcs, - DRM_MEM_DRIVER); + dev->vblank_premodeset = drm_calloc(1, sizeof(u32) * num_crtcs, + DRM_MEM_DRIVER); if (!dev->vblank_premodeset) goto err; - dev->vblank_offset = drm_alloc(sizeof(u32) * num_crtcs, - DRM_MEM_DRIVER); + dev->vblank_offset = drm_calloc(1, sizeof(u32) * num_crtcs, + DRM_MEM_DRIVER); if (!dev->vblank_offset) goto err; @@ -126,9 +126,6 @@ int drm_vblank_init(drm_device_t *dev, int num_crtcs) INIT_LIST_HEAD(&dev->vbl_sigs[i]); atomic_set(&dev->_vblank_count[i], 0); atomic_set(&dev->vblank_refcount[i], 0); - dev->last_vblank[i] = 0; - dev->vblank_premodeset[i] = 0; - dev->vblank_offset[i] = 0; } return 0; -- cgit v1.2.3 From d2d53024fb4003a6b86a3ea1ea33c76ac20bebc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Fri, 22 Jun 2007 11:45:23 +0200 Subject: Fix vblank wait condition. Sync-to-vblank actually works again for me with radeon. --- linux-core/drm_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux-core/drm_irq.c') diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index 01e970f9..5d8d71be 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -554,7 +554,7 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) return ret; DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ, (((cur_vblank = drm_vblank_count(dev, crtc)) - - seq) <= (1 << 23))); + - vblwait.request.sequence) <= (1 << 23))); drm_vblank_put(dev, crtc); do_gettimeofday(&now); vblwait.reply.tval_sec = now.tv_sec; -- cgit v1.2.3 From 97dcd7fd25c18d5148619254229f8d94efb55b44 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 22 Jun 2007 11:06:51 -0700 Subject: more vblank rework - use a timer for disabling vblank events to avoid enable/disable calls too often - make i915 work with pre-965 chips again (would like to structure this better, but this hack works on my test system) --- linux-core/drm_irq.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'linux-core/drm_irq.c') diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index c9d1c0d2..1e1b7f4d 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -77,10 +77,22 @@ int drm_irq_by_busid(struct inode *inode, struct file *filp, return 0; } +static void vblank_disable_fn(unsigned long arg) +{ + drm_device_t *dev = (drm_device_t *)arg; + int i; + + for (i = 0; i < dev->num_crtcs; i++) + if (atomic_read(&dev->vblank_refcount[i]) == 0) + dev->driver->disable_vblank(dev, i); +} + int drm_vblank_init(drm_device_t *dev, int num_crtcs) { int i, ret = -ENOMEM; + setup_timer(&dev->vblank_disable_timer, vblank_disable_fn,\ + (unsigned long)dev); spin_lock_init(&dev->vbl_lock); atomic_set(&dev->vbl_signal_pending, 0); dev->num_crtcs = num_crtcs; @@ -377,9 +389,10 @@ EXPORT_SYMBOL(drm_vblank_get); */ void drm_vblank_put(drm_device_t *dev, int crtc) { - /* Last user can disable interrupts */ + /* Last user schedules interrupt disable */ if (atomic_dec_and_test(&dev->vblank_refcount[crtc])) - dev->driver->disable_vblank(dev, crtc); + mod_timer(&dev->vblank_disable_timer, + round_jiffies_relative(DRM_HZ)); } EXPORT_SYMBOL(drm_vblank_put); -- cgit v1.2.3 From 00d60265570c866261c09fd3397d5853a1ce196a Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 1 Nov 2007 12:50:03 -0700 Subject: Cleanup vblank_init and fix drm_irq_install The vblank_init function wanted a couple of cleanups. Also, drm_irq_install wasn't checking the new return value of irq_postinstall. If it returns a failure, assume IRQs didn't get set up and take appropriate action. --- linux-core/drm_irq.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'linux-core/drm_irq.c') diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index e917e7eb..4aa58d77 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -85,7 +85,7 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) { int i, ret = -ENOMEM; - setup_timer(&dev->vblank_disable_timer, vblank_disable_fn,\ + setup_timer(&dev->vblank_disable_timer, vblank_disable_fn, (unsigned long)dev); spin_lock_init(&dev->vbl_lock); atomic_set(&dev->vbl_signal_pending, 0); @@ -111,18 +111,16 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) if (!dev->vblank_refcount) goto err; - dev->last_vblank = drm_calloc(1, sizeof(u32) * num_crtcs, - DRM_MEM_DRIVER); + dev->last_vblank = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER); if (!dev->last_vblank) goto err; - dev->vblank_premodeset = drm_calloc(1, sizeof(u32) * num_crtcs, + dev->vblank_premodeset = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER); if (!dev->vblank_premodeset) goto err; - dev->vblank_offset = drm_calloc(1, sizeof(u32) * num_crtcs, - DRM_MEM_DRIVER); + dev->vblank_offset = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER); if (!dev->vblank_offset) goto err; @@ -210,6 +208,11 @@ int drm_irq_install(struct drm_device * dev) /* After installing handler */ ret = dev->driver->irq_postinstall(dev); + if (ret < 0) { + mutex_lock(&dev->struct_mutex); + dev->irq_enabled = 0; + mutex_unlock(&dev->struct_mutex); + } return ret; } -- cgit v1.2.3 From 9ab620d661253f9b08f683a2a6f9ddee002015bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20N=C3=A9meth?= Date: Thu, 3 Jan 2008 16:56:04 +1000 Subject: drm: cleanup DRM_DEBUG() parameters As DRM_DEBUG macro already prints out the __FUNCTION__ string (see drivers/char/drm/drmP.h), it is not worth doing this again. At some other places the ending "\n" was added. airlied:- I cleaned up a few that this patch missed also --- linux-core/drm_irq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'linux-core/drm_irq.c') diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index 314c2329..866878aa 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -106,7 +106,7 @@ int drm_irq_install(struct drm_device * dev) dev->irq_enabled = 1; mutex_unlock(&dev->struct_mutex); - DRM_DEBUG("%s: irq=%d\n", __FUNCTION__, dev->irq); + DRM_DEBUG("irq=%d\n", dev->irq); if (drm_core_check_feature(dev, DRIVER_IRQ_VBL)) { init_waitqueue_head(&dev->vbl_queue); @@ -164,7 +164,7 @@ int drm_irq_uninstall(struct drm_device * dev) if (!irq_enabled) return -EINVAL; - DRM_DEBUG("%s: irq=%d\n", __FUNCTION__, dev->irq); + DRM_DEBUG("irq=%d\n", dev->irq); dev->driver->irq_uninstall(dev); -- cgit v1.2.3 From bfdddd218ec3e7ce3f8e765b93af35661a7bf0fd Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 24 Jan 2008 20:59:51 -0800 Subject: Fixup modeset ioctl number & typedef usage Should be 0x08 rather than 0xa0, and shouldn't use typedefs. --- linux-core/drm_irq.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'linux-core/drm_irq.c') diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index 2a5a4539..367d2dd8 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -397,23 +397,17 @@ EXPORT_SYMBOL(drm_vblank_put); int drm_modeset_ctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - drm_modeset_ctl_t __user *argp = (void __user *)data; - drm_modeset_ctl_t modeset; + struct drm_modeset_ctl *modeset = data; int crtc, ret = 0; u32 new; - if (copy_from_user(&modeset, argp, sizeof(modeset))) { - ret = -EFAULT; - goto out; - } - - crtc = modeset.arg; + crtc = modeset->arg; if (crtc >= dev->num_crtcs) { ret = -EINVAL; goto out; } - switch (modeset.cmd) { + switch (modeset->cmd) { case _DRM_PRE_MODESET: dev->vblank_premodeset[crtc] = dev->driver->get_vblank_counter(dev, crtc); -- cgit v1.2.3