diff options
-rw-r--r-- | linux-core/drmP.h | 89 | ||||
-rw-r--r-- | linux-core/drm_drv.c | 4 | ||||
-rw-r--r-- | linux-core/drm_irq.c | 353 | ||||
-rw-r--r-- | linux-core/i915_drv.c | 8 | ||||
-rw-r--r-- | linux-core/mga_drv.c | 7 | ||||
-rw-r--r-- | linux-core/r128_drv.c | 7 | ||||
-rw-r--r-- | linux-core/radeon_drv.c | 8 | ||||
-rw-r--r-- | shared-core/drm.h | 17 | ||||
-rw-r--r-- | shared-core/i915_drv.h | 41 | ||||
-rw-r--r-- | shared-core/i915_irq.c | 174 | ||||
-rw-r--r-- | shared-core/mga_drv.h | 10 | ||||
-rw-r--r-- | shared-core/mga_irq.c | 76 | ||||
-rw-r--r-- | shared-core/nouveau_drv.h | 2 | ||||
-rw-r--r-- | shared-core/nouveau_irq.c | 4 | ||||
-rw-r--r-- | shared-core/r128_drv.h | 9 | ||||
-rw-r--r-- | shared-core/r128_irq.c | 55 | ||||
-rw-r--r-- | shared-core/radeon_drv.h | 19 | ||||
-rw-r--r-- | shared-core/radeon_irq.c | 172 | ||||
-rw-r--r-- | shared-core/via_drv.c | 6 | ||||
-rw-r--r-- | shared-core/via_drv.h | 7 | ||||
-rw-r--r-- | shared-core/via_irq.c | 74 |
21 files changed, 797 insertions, 345 deletions
diff --git a/linux-core/drmP.h b/linux-core/drmP.h index ac3ca4d2..332ee1cd 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -102,10 +102,8 @@ struct drm_file; #define DRIVER_HAVE_DMA 0x20 #define DRIVER_HAVE_IRQ 0x40 #define DRIVER_IRQ_SHARED 0x80 -#define DRIVER_IRQ_VBL 0x100 -#define DRIVER_DMA_QUEUE 0x200 -#define DRIVER_FB_DMA 0x400 -#define DRIVER_IRQ_VBL2 0x800 +#define DRIVER_DMA_QUEUE 0x100 +#define DRIVER_FB_DMA 0x200 /*@}*/ @@ -638,8 +636,50 @@ struct drm_driver { int (*kernel_context_switch) (struct drm_device * dev, int old, int new); void (*kernel_context_switch_unlock) (struct drm_device * dev); - int (*vblank_wait) (struct drm_device * dev, unsigned int *sequence); - int (*vblank_wait2) (struct drm_device * dev, unsigned int *sequence); + /** + * get_vblank_counter - get raw hardware vblank counter + * @dev: DRM device + * @crtc: counter to fetch + * + * Driver callback for fetching a raw hardware vblank counter + * for @crtc. If a device doesn't have a hardware counter, the + * driver can simply return the value of drm_vblank_count and + * make the enable_vblank() and disable_vblank() hooks into no-ops, + * leaving interrupts enabled at all times. + * + * Wraparound handling and loss of events due to modesetting is dealt + * with in the DRM core code. + * + * RETURNS + * Raw vblank counter value. + */ + u32 (*get_vblank_counter) (struct drm_device *dev, int crtc); + + /** + * enable_vblank - enable vblank interrupt events + * @dev: DRM device + * @crtc: which irq to enable + * + * Enable vblank interrupts for @crtc. If the device doesn't have + * a hardware vblank counter, this routine should be a no-op, since + * interrupts will have to stay on to keep the count accurate. + * + * RETURNS + * Zero on success, appropriate errno if the given @crtc's vblank + * interrupt cannot be enabled. + */ + int (*enable_vblank) (struct drm_device *dev, int crtc); + + /** + * disable_vblank - disable vblank interrupt events + * @dev: DRM device + * @crtc: which irq to enable + * + * Disable vblank interrupts for @crtc. If the device doesn't have + * a hardware vblank counter, this routine should be a no-op, since + * interrupts will have to stay on to keep the count accurate. + */ + void (*disable_vblank) (struct drm_device *dev, int crtc); int (*dri_library_name) (struct drm_device * dev, char * buf); /** @@ -658,7 +698,7 @@ struct drm_driver { /* these have to be filled in */ irqreturn_t(*irq_handler) (DRM_IRQ_ARGS); void (*irq_preinstall) (struct drm_device * dev); - void (*irq_postinstall) (struct drm_device * dev); + int (*irq_postinstall) (struct drm_device * dev); void (*irq_uninstall) (struct drm_device * dev); void (*reclaim_buffers) (struct drm_device *dev, struct drm_file *file_priv); @@ -795,13 +835,19 @@ struct drm_device { /** \name VBLANK IRQ support */ /*@{ */ - wait_queue_head_t vbl_queue; /**< VBLANK wait queue */ - atomic_t vbl_received; - atomic_t vbl_received2; /**< number of secondary VBLANK interrupts */ + wait_queue_head_t *vbl_queue; /**< VBLANK wait queue */ + atomic_t *_vblank_count; /**< number of VBLANK interrupts (driver must alloc the right number of counters) */ spinlock_t vbl_lock; - struct list_head vbl_sigs; /**< signal list to send on VBLANK */ - struct list_head vbl_sigs2; /**< signals to send on secondary VBLANK */ - unsigned int vbl_pending; + struct list_head *vbl_sigs; /**< signal list to send on VBLANK */ + atomic_t vbl_signal_pending; /* number of signals pending on all crtcs*/ + atomic_t *vblank_refcount; /* number of users of vblank interrupts per crtc */ + u32 *last_vblank; /* protected by dev->vbl_lock, used */ + /* for wraparound handling */ + u32 *vblank_offset; /* used to track how many vblanks */ + u32 *vblank_premodeset; /* were lost during modeset */ + struct timer_list vblank_disable_timer; + + unsigned long max_vblank_count; /**< size of vblank counter register */ spinlock_t tasklet_lock; /**< For drm_locked_tasklet */ void (*locked_tasklet_func)(struct drm_device *dev); @@ -821,6 +867,7 @@ struct drm_device { #ifdef __alpha__ struct pci_controller *hose; #endif + int num_crtcs; /**< Number of CRTCs on this device */ struct drm_sg_mem *sg; /**< Scatter gather memory */ void *dev_private; /**< device private data */ struct drm_sigdata sigdata; /**< For block_all_signals */ @@ -1092,11 +1139,19 @@ extern void drm_driver_irq_preinstall(struct drm_device *dev); extern void drm_driver_irq_postinstall(struct drm_device *dev); extern void drm_driver_irq_uninstall(struct drm_device *dev); -extern int drm_wait_vblank(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern int drm_vblank_wait(struct drm_device *dev, unsigned int *vbl_seq); -extern void drm_vbl_send_signals(struct drm_device *dev); +extern int drm_vblank_init(struct drm_device *dev, int num_crtcs); +extern int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *filp); +extern int drm_vblank_wait(struct drm_device * dev, unsigned int *vbl_seq); extern void drm_locked_tasklet(struct drm_device *dev, void(*func)(struct drm_device*)); +extern u32 drm_vblank_count(struct drm_device *dev, int crtc); +extern void drm_update_vblank_count(struct drm_device *dev, int crtc); +extern void drm_handle_vblank(struct drm_device *dev, int crtc); +extern int drm_vblank_get(struct drm_device *dev, int crtc); +extern void drm_vblank_put(struct drm_device *dev, int crtc); + + /* Modesetting support */ +extern int drm_modeset_ctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); /* AGP/GART support (drm_agpsupport.h) */ extern struct drm_agp_head *drm_agp_init(struct drm_device *dev); diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index 47d17651..296a3268 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -115,14 +115,12 @@ static struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_SG_ALLOC, drm_sg_alloc_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_SG_FREE, drm_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, 0), - // DRM_IOCTL_DEF(DRM_IOCTL_BUFOBJ, drm_bo_ioctl, DRM_AUTH), + DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_modeset_ctl, 0), DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_update_drawable_info, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_MM_INIT, drm_mm_init_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_MM_TAKEDOWN, drm_mm_takedown_ioctl, diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index 25166b6f..e917e7eb 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -71,18 +71,102 @@ int drm_irq_by_busid(struct drm_device *dev, void *data, return 0; } +static void vblank_disable_fn(unsigned long arg) +{ + struct drm_device *dev = (struct drm_device *)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(struct drm_device *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; + + 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) + goto err; + + dev->_vblank_count = drm_alloc(sizeof(atomic_t) * num_crtcs, + DRM_MEM_DRIVER); + if (!dev->_vblank_count) + goto err; + + dev->vblank_refcount = drm_alloc(sizeof(atomic_t) * num_crtcs, + DRM_MEM_DRIVER); + if (!dev->vblank_refcount) + goto err; + + dev->last_vblank = drm_calloc(1, sizeof(u32) * num_crtcs, + DRM_MEM_DRIVER); + if (!dev->last_vblank) + goto err; + + dev->vblank_premodeset = drm_calloc(1, sizeof(u32) * num_crtcs, + DRM_MEM_DRIVER); + if (!dev->vblank_premodeset) + goto err; + + dev->vblank_offset = drm_calloc(1, 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_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); + } + + 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, + 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); + /** * Install IRQ handler. * * \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. */ int drm_irq_install(struct drm_device * dev) { - int ret; + int ret = 0; unsigned long sh_flags = 0; if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) @@ -108,17 +192,6 @@ int drm_irq_install(struct drm_device * 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); @@ -136,9 +209,9 @@ int drm_irq_install(struct drm_device * dev) } /* After installing handler */ - dev->driver->irq_postinstall(dev); + ret = dev->driver->irq_postinstall(dev); - return 0; + return ret; } EXPORT_SYMBOL(drm_irq_install); @@ -213,6 +286,149 @@ int drm_control(struct drm_device *dev, void *data, } /** + * 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(struct drm_device *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(struct drm_device *dev, int crtc) +{ + unsigned long irqflags; + u32 cur_vblank, diff; + + /* + * 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 - dev->last_vblank[crtc]; + } + dev->last_vblank[crtc] = cur_vblank; + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + + 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(struct drm_device *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(struct drm_device *dev, int crtc) +{ + /* Last user schedules interrupt disable */ + if (atomic_dec_and_test(&dev->vblank_refcount[crtc])) + mod_timer(&dev->vblank_disable_timer, + round_jiffies_relative(DRM_HZ)); +} +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(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; + 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. * * \param inode device inode. @@ -231,12 +447,13 @@ int drm_control(struct drm_device *dev, void *data, * * If a signal is not requested, then calls vblank_wait(). */ -int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv) +int drm_wait_vblank(struct drm_device *dev, void *data, + struct drm_file *file_priv) { union drm_wait_vblank *vblwait = data; struct timeval now; int ret = 0; - unsigned int flags, seq; + unsigned int flags, seq, crtc; if ((!dev->irq) || (!dev->irq_enabled)) return -EINVAL; @@ -250,13 +467,13 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr } 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; - seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_received2 - : &dev->vbl_received); + drm_update_vblank_count(dev, crtc); + seq = drm_vblank_count(dev, crtc); switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) { case _DRM_VBLANK_RELATIVE: @@ -275,8 +492,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr 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]; struct drm_vbl_sig *vbl_sig; spin_lock_irqsave(&dev->vbl_lock, irqflags); @@ -297,22 +513,26 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr } } - if (dev->vbl_pending >= 100) { + if (atomic_read(&dev->vbl_signal_pending) >= 100) { spin_unlock_irqrestore(&dev->vbl_lock, irqflags); return -EBUSY; } - dev->vbl_pending++; - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); - if (! - (vbl_sig = - drm_alloc(sizeof(struct drm_vbl_sig), DRM_MEM_DRIVER))) { + vbl_sig = drm_calloc(1, sizeof(struct drm_vbl_sig), + DRM_MEM_DRIVER); + if (!vbl_sig) return -ENOMEM; + + ret = drm_vblank_get(dev, crtc); + if (ret) { + drm_free(vbl_sig, sizeof(struct drm_vbl_sig), + DRM_MEM_DRIVER); + return ret; } - 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; @@ -326,17 +546,20 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr 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; + + ret = drm_vblank_get(dev, crtc); + if (ret) + return ret; + DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ, + (((cur_vblank = drm_vblank_count(dev, crtc)) + - vblwait->request.sequence) <= (1 << 23))); + drm_vblank_put(dev, crtc); do_gettimeofday(&now); + vblwait->reply.tval_sec = now.tv_sec; vblwait->reply.tval_usec = now.tv_usec; + vblwait->reply.sequence = cur_vblank; } done: @@ -347,43 +570,57 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr * 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(struct drm_device * dev) +static void drm_vbl_send_signals(struct drm_device * dev, int crtc) { + struct drm_vbl_sig *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 < 2; i++) { - struct drm_vbl_sig *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); - - 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); + vbl_sigs = &dev->vbl_sigs[crtc]; + vbl_seq = drm_vblank_count(dev, crtc); - list_del(&vbl_sig->head); + 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); - drm_free(vbl_sig, sizeof(*vbl_sig), - DRM_MEM_DRIVER); + list_del(&vbl_sig->head); - dev->vbl_pending--; - } - } + 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(struct drm_device *dev, int crtc) +{ + drm_update_vblank_count(dev, crtc); + DRM_WAKEUP(&dev->vbl_queue[crtc]); + drm_vbl_send_signals(dev, crtc); +} +EXPORT_SYMBOL(drm_handle_vblank); /** * Tasklet wrapper function. diff --git a/linux-core/i915_drv.c b/linux-core/i915_drv.c index 124db68f..84df64a7 100644 --- a/linux-core/i915_drv.c +++ b/linux-core/i915_drv.c @@ -529,8 +529,7 @@ static struct drm_driver driver = { */ .driver_features = DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | /* DRIVER_USE_MTRR | */ - DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL | - DRIVER_IRQ_VBL2, + DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, .load = i915_driver_load, .unload = i915_driver_unload, .firstopen = i915_driver_firstopen, @@ -539,8 +538,9 @@ static struct drm_driver driver = { .suspend = i915_suspend, .resume = i915_resume, .device_is_agp = i915_driver_device_is_agp, - .vblank_wait = i915_driver_vblank_wait, - .vblank_wait2 = i915_driver_vblank_wait2, + .get_vblank_counter = i915_get_vblank_counter, + .enable_vblank = i915_enable_vblank, + .disable_vblank = i915_disable_vblank, .irq_preinstall = i915_driver_irq_preinstall, .irq_postinstall = i915_driver_irq_postinstall, .irq_uninstall = i915_driver_irq_uninstall, diff --git a/linux-core/mga_drv.c b/linux-core/mga_drv.c index 1eb6d9e6..11796b01 100644 --- a/linux-core/mga_drv.c +++ b/linux-core/mga_drv.c @@ -46,15 +46,16 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *ent); static struct drm_driver driver = { .driver_features = DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | - DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | - DRIVER_IRQ_VBL, + DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, .dev_priv_size = sizeof (drm_mga_buf_priv_t), .load = mga_driver_load, .unload = mga_driver_unload, .lastclose = mga_driver_lastclose, .dma_quiescent = mga_driver_dma_quiescent, .device_is_agp = mga_driver_device_is_agp, - .vblank_wait = mga_driver_vblank_wait, + .get_vblank_counter = mga_get_vblank_counter, + .enable_vblank = mga_enable_vblank, + .disable_vblank = mga_disable_vblank, .irq_preinstall = mga_driver_irq_preinstall, .irq_postinstall = mga_driver_irq_postinstall, .irq_uninstall = mga_driver_irq_uninstall, diff --git a/linux-core/r128_drv.c b/linux-core/r128_drv.c index ef4a5cbd..7b6159b9 100644 --- a/linux-core/r128_drv.c +++ b/linux-core/r128_drv.c @@ -44,12 +44,13 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *ent); static struct drm_driver driver = { .driver_features = DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | - DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | - DRIVER_IRQ_VBL, + DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, .dev_priv_size = sizeof(drm_r128_buf_priv_t), .preclose = r128_driver_preclose, .lastclose = r128_driver_lastclose, - .vblank_wait = r128_driver_vblank_wait, + .get_vblank_counter = r128_get_vblank_counter, + .enable_vblank = r128_enable_vblank, + .disable_vblank = r128_disable_vblank, .irq_preinstall = r128_driver_irq_preinstall, .irq_postinstall = r128_driver_irq_postinstall, .irq_uninstall = r128_driver_irq_uninstall, diff --git a/linux-core/radeon_drv.c b/linux-core/radeon_drv.c index 327a6a97..39c35134 100644 --- a/linux-core/radeon_drv.c +++ b/linux-core/radeon_drv.c @@ -60,8 +60,7 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *ent); static struct drm_driver driver = { .driver_features = DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | - DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED | - DRIVER_IRQ_VBL | DRIVER_IRQ_VBL2, + DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED, .dev_priv_size = sizeof(drm_radeon_buf_priv_t), .load = radeon_driver_load, .firstopen = radeon_driver_firstopen, @@ -70,8 +69,9 @@ static struct drm_driver driver = { .postclose = radeon_driver_postclose, .lastclose = radeon_driver_lastclose, .unload = radeon_driver_unload, - .vblank_wait = radeon_driver_vblank_wait, - .vblank_wait2 = radeon_driver_vblank_wait2, + .get_vblank_counter = radeon_get_vblank_counter, + .enable_vblank = radeon_enable_vblank, + .disable_vblank = radeon_disable_vblank, .dri_library_name = dri_library_name, .irq_preinstall = radeon_driver_irq_preinstall, .irq_postinstall = radeon_driver_irq_postinstall, diff --git a/shared-core/drm.h b/shared-core/drm.h index 3a102735..3092e536 100644 --- a/shared-core/drm.h +++ b/shared-core/drm.h @@ -554,6 +554,21 @@ union drm_wait_vblank { struct drm_wait_vblank_reply reply; }; +typedef enum { + _DRM_PRE_MODESET = 1, + _DRM_POST_MODESET = 2, +} drm_modeset_ctl_cmd_t; + +/** + * DRM_IOCTL_MODESET_CTL ioctl argument type + * + * \sa drmModesetCtl(). + */ +typedef struct drm_modeset_ctl { + u64 arg; + drm_modeset_ctl_cmd_t cmd; +} drm_modeset_ctl_t; + /** * DRM_IOCTL_AGP_ENABLE ioctl argument type. * @@ -978,6 +993,8 @@ struct drm_mm_init_arg { #define DRM_IOCTL_BO_VERSION DRM_IOR(0xd6, struct drm_bo_version_arg) +#define DRM_IOCTL_MODESET_CTL DRM_IOW(0xa0, drm_modeset_ctl_t) + /*@}*/ /** diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index 9f69d841..d34621e5 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -257,21 +257,22 @@ extern int i915_irq_emit(struct drm_device *dev, void *data, extern int i915_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int i915_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence); -extern int i915_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence); extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS); extern void i915_driver_irq_preinstall(struct drm_device * dev); -extern void i915_driver_irq_postinstall(struct drm_device * dev); +extern int i915_driver_irq_postinstall(struct drm_device * dev); extern void i915_driver_irq_uninstall(struct drm_device * dev); extern int i915_vblank_pipe_set(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int i915_vblank_pipe_get(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int i915_emit_irq(struct drm_device * dev); -extern void i915_user_irq_on(drm_i915_private_t *dev_priv); -extern void i915_user_irq_off(drm_i915_private_t *dev_priv); +extern int i915_enable_vblank(struct drm_device *dev, int crtc); +extern void i915_disable_vblank(struct drm_device *dev, int crtc); +extern u32 i915_get_vblank_counter(struct drm_device *dev, int crtc); extern int i915_vblank_swap(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern void i915_user_irq_on(drm_i915_private_t *dev_priv); +extern void i915_user_irq_off(drm_i915_private_t *dev_priv); /* i915_mem.c */ extern int i915_mem_alloc(struct drm_device *dev, void *data, @@ -464,6 +465,36 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); #define I915REG_PIPEASTAT 0x70024 #define I915REG_PIPEBSTAT 0x71024 +/* + * The two pipe frame counter registers are not synchronized, so + * reading a stable value is somewhat tricky. The following code + * should work: + * + * do { + * high1 = ((INREG(PIPEAFRAMEHIGH) & PIPE_FRAME_HIGH_MASK) >> + * PIPE_FRAME_HIGH_SHIFT; + * low1 = ((INREG(PIPEAFRAMEPIXEL) & PIPE_FRAME_LOW_MASK) >> + * PIPE_FRAME_LOW_SHIFT); + * high2 = ((INREG(PIPEAFRAMEHIGH) & PIPE_FRAME_HIGH_MASK) >> + * PIPE_FRAME_HIGH_SHIFT); + * } while (high1 != high2); + * frame = (high1 << 8) | low1; + */ +#define PIPEAFRAMEHIGH 0x70040 +#define PIPEBFRAMEHIGH 0x71040 +#define PIPE_FRAME_HIGH_MASK 0x0000ffff +#define PIPE_FRAME_HIGH_SHIFT 0 +#define PIPEAFRAMEPIXEL 0x70044 +#define PIPEBFRAMEPIXEL 0x71044 + +#define PIPE_FRAME_LOW_MASK 0xff000000 +#define PIPE_FRAME_LOW_SHIFT 24 +/* + * Pixel within the current frame is counted in the PIPEAFRAMEPIXEL register + * and is 24 bits wide. + */ +#define PIPE_PIXEL_MASK 0x00ffffff +#define PIPE_PIXEL_SHIFT 0 #define I915_VBLANK_INTERRUPT_ENABLE (1UL<<17) #define I915_VBLANK_CLEAR (1UL<<1) diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index db18a895..46d09663 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -114,8 +114,7 @@ static void i915_vblank_tasklet(struct drm_device *dev) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; struct list_head *list, *tmp, hits, *hit; int nhits, nrects, slice[2], upper[2], lower[2], i, num_pages; - unsigned counter[2] = { atomic_read(&dev->vbl_received), - atomic_read(&dev->vbl_received2) }; + unsigned counter[2]; struct drm_drawable_info *drw; drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv; u32 cpp = dev_priv->cpp, offsets[3]; @@ -127,6 +126,9 @@ static void i915_vblank_tasklet(struct drm_device *dev) (cpp << 23) | (1 << 24); RING_LOCALS; + counter[0] = drm_vblank_count(dev, 0); + counter[1] = drm_vblank_count(dev, 1); + DRM_DEBUG("\n"); INIT_LIST_HEAD(&hits); @@ -144,13 +146,14 @@ static void i915_vblank_tasklet(struct drm_device *dev) list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) { drm_i915_vbl_swap_t *vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head); - int pipe = i915_get_pipe(dev, vbl_swap->plane); + int crtc = i915_get_pipe(dev, vbl_swap->plane); - if ((counter[pipe] - vbl_swap->sequence) > (1<<23)) + if ((counter[crtc] - vbl_swap->sequence) > (1<<23)) continue; list_del(list); dev_priv->swaps_pending--; + drm_vblank_put(dev, crtc); DRM_SPINUNLOCK(&dev_priv->swaps_lock); DRM_SPINLOCK(&dev->drw_lock); @@ -301,6 +304,32 @@ static void i915_vblank_tasklet(struct drm_device *dev) } } +u32 i915_get_vblank_counter(struct drm_device *dev, int crtc) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + unsigned long high_frame = crtc ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH; + unsigned long low_frame = crtc ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL; + u32 high1, high2, low, count; + + /* + * High & low register fields aren't synchronized, so make sure + * we get a low value that's stable across two reads of the high + * register. + */ + do { + high1 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> + PIPE_FRAME_HIGH_SHIFT); + low = ((I915_READ(low_frame) & PIPE_FRAME_LOW_MASK) >> + PIPE_FRAME_LOW_SHIFT); + high2 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> + PIPE_FRAME_HIGH_SHIFT); + } while (high1 != high2); + + count = (high1 << 8) | low; + + return count; +} + irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; @@ -312,7 +341,6 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) pipeb_stats = I915_READ(I915REG_PIPEBSTAT); temp = I915_READ16(I915REG_INT_IDENTITY_R); - temp &= (dev_priv->irq_enable_reg | USER_INT_FLAG); #if 0 DRM_DEBUG("%s flag=%08x\n", __FUNCTION__, temp); @@ -321,8 +349,10 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) return IRQ_NONE; I915_WRITE16(I915REG_INT_IDENTITY_R, temp); - (void) I915_READ16(I915REG_INT_IDENTITY_R); - DRM_READMEMORYBARRIER(); + (void) I915_READ16(I915REG_INT_IDENTITY_R); /* Flush posted write */ + + temp &= (dev_priv->irq_enable_reg | USER_INT_FLAG | VSYNC_PIPEA_FLAG | + VSYNC_PIPEB_FLAG); dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); @@ -333,25 +363,16 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) #endif } - if (temp & (VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG)) { - int vblank_pipe = dev_priv->vblank_pipe; - - if ((vblank_pipe & - (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) - == (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) { - if (temp & VSYNC_PIPEA_FLAG) - atomic_inc(&dev->vbl_received); - if (temp & VSYNC_PIPEB_FLAG) - atomic_inc(&dev->vbl_received2); - } else if (((temp & VSYNC_PIPEA_FLAG) && - (vblank_pipe & DRM_I915_VBLANK_PIPE_A)) || - ((temp & VSYNC_PIPEB_FLAG) && - (vblank_pipe & DRM_I915_VBLANK_PIPE_B))) - atomic_inc(&dev->vbl_received); - - DRM_WAKEUP(&dev->vbl_queue); - drm_vbl_send_signals(dev); + /* + * Use drm_update_vblank_counter here to deal with potential lost + * interrupts + */ + if (temp & VSYNC_PIPEA_FLAG) + drm_handle_vblank(dev, 0); + if (temp & VSYNC_PIPEB_FLAG) + drm_handle_vblank(dev, 1); + if (temp & (VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG)) { if (dev_priv->swaps_pending > 0) drm_locked_tasklet(dev, i915_vblank_tasklet); I915_WRITE(I915REG_PIPEASTAT, @@ -437,38 +458,6 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr) return ret; } -static int i915_driver_vblank_do_wait(struct drm_device *dev, - unsigned int *sequence, - atomic_t *counter) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - unsigned int cur_vblank; - int ret = 0; - - if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __FUNCTION__); - return -EINVAL; - } - - DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, - (((cur_vblank = atomic_read(counter)) - - *sequence) <= (1<<23))); - - *sequence = cur_vblank; - - return ret; -} - -int i915_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence) -{ - return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received); -} - -int i915_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence) -{ - return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received2); -} - /* Needs the lock as it touches the ring. */ int i915_irq_emit(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -510,15 +499,53 @@ int i915_irq_wait(struct drm_device *dev, void *data, return i915_wait_irq(dev, irqwait->irq_seq); } -static void i915_enable_interrupt (struct drm_device *dev) +int i915_enable_vblank(struct drm_device *dev, int crtc) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - - dev_priv->irq_enable_reg = USER_INT_FLAG; - if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A) + + switch (crtc) { + case 0: dev_priv->irq_enable_reg |= VSYNC_PIPEA_FLAG; - if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B) + break; + case 1: dev_priv->irq_enable_reg |= VSYNC_PIPEB_FLAG; + break; + default: + DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", + crtc); + break; + } + + I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + + return 0; +} + +void i915_disable_vblank(struct drm_device *dev, int crtc) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + + switch (crtc) { + case 0: + dev_priv->irq_enable_reg &= ~VSYNC_PIPEA_FLAG; + break; + case 1: + dev_priv->irq_enable_reg &= ~VSYNC_PIPEB_FLAG; + break; + default: + DRM_ERROR("tried to disable vblank on non-existent crtc %d\n", + crtc); + break; + } + + I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); +} + +static void i915_enable_interrupt (struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + + dev_priv->irq_enable_reg |= USER_INT_FLAG; I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); dev_priv->irq_enabled = 1; @@ -545,8 +572,6 @@ int i915_vblank_pipe_set(struct drm_device *dev, void *data, dev_priv->vblank_pipe = pipe->pipe; - i915_enable_interrupt (dev); - return 0; } @@ -584,6 +609,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data, unsigned int pipe, seqtype, curseq, plane; unsigned long irqflags; struct list_head *list; + int ret; if (!dev_priv) { DRM_ERROR("%s called with no initialization\n", __func__); @@ -627,7 +653,8 @@ int i915_vblank_swap(struct drm_device *dev, void *data, DRM_SPINUNLOCK_IRQRESTORE(&dev->drw_lock, irqflags); - curseq = atomic_read(pipe ? &dev->vbl_received2 : &dev->vbl_received); + drm_update_vblank_count(dev, pipe); + curseq = drm_vblank_count(dev, pipe); if (seqtype == _DRM_VBLANK_RELATIVE) swap->sequence += curseq; @@ -700,6 +727,12 @@ int i915_vblank_swap(struct drm_device *dev, void *data, DRM_DEBUG("\n"); + ret = drm_vblank_get(dev, pipe); + if (ret) { + drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER); + return ret; + } + vbl_swap->drw_id = swap->drawable; vbl_swap->plane = plane; vbl_swap->sequence = swap->sequence; @@ -729,9 +762,10 @@ void i915_driver_irq_preinstall(struct drm_device * dev) I915_WRITE16(I915REG_INT_ENABLE_R, 0x0); } -void i915_driver_irq_postinstall(struct drm_device * dev) +int i915_driver_irq_postinstall(struct drm_device * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int ret, num_pipes = 2; DRM_SPININIT(&dev_priv->swaps_lock, "swap"); INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); @@ -739,6 +773,13 @@ void i915_driver_irq_postinstall(struct drm_device * dev) DRM_SPININIT(&dev_priv->user_irq_lock, "userirq"); dev_priv->user_irq_refcount = 0; + dev_priv->irq_enable_reg = 0; + + ret = drm_vblank_init(dev, num_pipes); + if (ret) + return ret; + + dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ i915_enable_interrupt(dev); DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); @@ -748,6 +789,7 @@ void i915_driver_irq_postinstall(struct drm_device * dev) */ I915_WRITE(I915REG_INSTPM, ( 1 << 5) | ( 1 << 21)); + return 0; } void i915_driver_irq_uninstall(struct drm_device * dev) diff --git a/shared-core/mga_drv.h b/shared-core/mga_drv.h index 8254c3f1..b961155a 100644 --- a/shared-core/mga_drv.h +++ b/shared-core/mga_drv.h @@ -113,13 +113,14 @@ typedef struct drm_mga_private { * \sa drm_mga_private_t::mmio */ /*@{*/ - u32 mmio_base; /**< Bus address of base of MMIO. */ - u32 mmio_size; /**< Size of the MMIO region. */ + u32 mmio_base; /**< Bus address of base of MMIO. */ + u32 mmio_size; /**< Size of the MMIO region. */ /*@}*/ u32 clear_cmd; u32 maccess; + atomic_t vbl_received; /**< Number of vblanks received. */ wait_queue_head_t fence_queue; atomic_t last_fence_retired; u32 next_fence_to_post; @@ -181,11 +182,14 @@ extern int mga_warp_install_microcode(drm_mga_private_t * dev_priv); extern int mga_warp_init(drm_mga_private_t * dev_priv); /* mga_irq.c */ +extern int mga_enable_vblank(struct drm_device *dev, int crtc); +extern void mga_disable_vblank(struct drm_device *dev, int crtc); +extern u32 mga_get_vblank_counter(struct drm_device *dev, int crtc); extern int mga_driver_fence_wait(struct drm_device * dev, unsigned int *sequence); extern int mga_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence); extern irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS); extern void mga_driver_irq_preinstall(struct drm_device * dev); -extern void mga_driver_irq_postinstall(struct drm_device * dev); +extern int mga_driver_irq_postinstall(struct drm_device * dev); extern void mga_driver_irq_uninstall(struct drm_device * dev); extern long mga_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); diff --git a/shared-core/mga_irq.c b/shared-core/mga_irq.c index 8b555e2e..0d4b473c 100644 --- a/shared-core/mga_irq.c +++ b/shared-core/mga_irq.c @@ -36,6 +36,20 @@ #include "mga_drm.h" #include "mga_drv.h" +u32 mga_get_vblank_counter(struct drm_device *dev, int crtc) +{ + const drm_mga_private_t *const dev_priv = + (drm_mga_private_t *) dev->dev_private; + + if (crtc != 0) { + return 0; + } + + + return atomic_read(&dev_priv->vbl_received); +} + + irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; @@ -48,9 +62,8 @@ irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS) /* VBLANK interrupt */ if (status & MGA_VLINEPEN) { MGA_WRITE(MGA_ICLEAR, MGA_VLINEICLR); - atomic_inc(&dev->vbl_received); - DRM_WAKEUP(&dev->vbl_queue); - drm_vbl_send_signals(dev); + atomic_inc(&dev_priv->vbl_received); + drm_handle_vblank(dev, 0); handled = 1; } @@ -74,28 +87,37 @@ irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS) handled = 1; } - if ( handled ) { - return IRQ_HANDLED; - } - return IRQ_NONE; + return (handled) ? IRQ_HANDLED : IRQ_NONE; } -int mga_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence) +int mga_enable_vblank(struct drm_device *dev, int crtc) { - unsigned int cur_vblank; - int ret = 0; + drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; - /* Assume that the user has missed the current sequence number - * by about a day rather than she wants to wait for years - * using vertical blanks... - */ - DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, - (((cur_vblank = atomic_read(&dev->vbl_received)) - - *sequence) <= (1 << 23))); + if (crtc != 0) { + DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", + crtc); + return 0; + } - *sequence = cur_vblank; + MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN); + return 0; +} - return ret; + +void mga_disable_vblank(struct drm_device *dev, int crtc) +{ + if (crtc != 0) { + DRM_ERROR("tried to disable vblank on non-existent crtc %d\n", + crtc); + } + + /* Do *NOT* disable the vertical refresh interrupt. MGA doesn't have + * a nice hardware counter that tracks the number of refreshes when + * the interrupt is disabled, and the kernel doesn't know the refresh + * rate to calculate an estimate. + */ + /* MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN); */ } int mga_driver_fence_wait(struct drm_device * dev, unsigned int *sequence) @@ -127,14 +149,22 @@ void mga_driver_irq_preinstall(struct drm_device * dev) MGA_WRITE(MGA_ICLEAR, ~0); } -void mga_driver_irq_postinstall(struct drm_device * dev) +int mga_driver_irq_postinstall(struct drm_device * dev) { drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; + int ret; - DRM_INIT_WAITQUEUE( &dev_priv->fence_queue ); + ret = drm_vblank_init(dev, 1); + if (ret) + return ret; - /* Turn on vertical blank interrupt and soft trap interrupt. */ - MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN); + DRM_INIT_WAITQUEUE(&dev_priv->fence_queue); + + /* Turn on soft trap interrupt. Vertical blank interrupts are enabled + * in mga_enable_vblank. + */ + MGA_WRITE(MGA_IEN, MGA_SOFTRAPEN); + return 0; } void mga_driver_irq_uninstall(struct drm_device * dev) diff --git a/shared-core/nouveau_drv.h b/shared-core/nouveau_drv.h index e5cef075..95878345 100644 --- a/shared-core/nouveau_drv.h +++ b/shared-core/nouveau_drv.h @@ -416,7 +416,7 @@ extern int nouveau_ioctl_gpuobj_free(struct drm_device *, void *data, /* nouveau_irq.c */ extern irqreturn_t nouveau_irq_handler(DRM_IRQ_ARGS); extern void nouveau_irq_preinstall(struct drm_device *); -extern void nouveau_irq_postinstall(struct drm_device *); +extern int nouveau_irq_postinstall(struct drm_device *); extern void nouveau_irq_uninstall(struct drm_device *); /* nouveau_sgdma.c */ diff --git a/shared-core/nouveau_irq.c b/shared-core/nouveau_irq.c index ac507299..e4b76766 100644 --- a/shared-core/nouveau_irq.c +++ b/shared-core/nouveau_irq.c @@ -46,13 +46,15 @@ nouveau_irq_preinstall(struct drm_device *dev) NV_WRITE(NV03_PMC_INTR_EN_0, 0); } -void +int nouveau_irq_postinstall(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; /* Master enable */ NV_WRITE(NV03_PMC_INTR_EN_0, NV_PMC_INTR_EN_0_MASTER_ENABLE); + + return 0; } void diff --git a/shared-core/r128_drv.h b/shared-core/r128_drv.h index abb99898..cb0c41cb 100644 --- a/shared-core/r128_drv.h +++ b/shared-core/r128_drv.h @@ -97,6 +97,8 @@ typedef struct drm_r128_private { u32 crtc_offset; u32 crtc_offset_cntl; + atomic_t vbl_received; + u32 color_fmt; unsigned int front_offset; unsigned int front_pitch; @@ -149,11 +151,12 @@ extern int r128_wait_ring(drm_r128_private_t * dev_priv, int n); extern int r128_do_cce_idle(drm_r128_private_t * dev_priv); extern int r128_do_cleanup_cce(struct drm_device * dev); -extern int r128_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence); - +extern int r128_enable_vblank(struct drm_device *dev, int crtc); +extern void r128_disable_vblank(struct drm_device *dev, int crtc); +extern u32 r128_get_vblank_counter(struct drm_device *dev, int crtc); extern irqreturn_t r128_driver_irq_handler(DRM_IRQ_ARGS); extern void r128_driver_irq_preinstall(struct drm_device * dev); -extern void r128_driver_irq_postinstall(struct drm_device * dev); +extern int r128_driver_irq_postinstall(struct drm_device * dev); extern void r128_driver_irq_uninstall(struct drm_device * dev); extern void r128_driver_lastclose(struct drm_device * dev); extern void r128_driver_preclose(struct drm_device * dev, diff --git a/shared-core/r128_irq.c b/shared-core/r128_irq.c index c76fdca7..5b95bd89 100644 --- a/shared-core/r128_irq.c +++ b/shared-core/r128_irq.c @@ -35,6 +35,16 @@ #include "r128_drm.h" #include "r128_drv.h" +u32 r128_get_vblank_counter(struct drm_device *dev, int crtc) +{ + const drm_r128_private_t *dev_priv = dev->dev_private; + + if (crtc != 0) + return 0; + + return atomic_read(&dev_priv->vbl_received); +} + irqreturn_t r128_driver_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; @@ -46,30 +56,38 @@ irqreturn_t r128_driver_irq_handler(DRM_IRQ_ARGS) /* VBLANK interrupt */ if (status & R128_CRTC_VBLANK_INT) { R128_WRITE(R128_GEN_INT_STATUS, R128_CRTC_VBLANK_INT_AK); - atomic_inc(&dev->vbl_received); - DRM_WAKEUP(&dev->vbl_queue); - drm_vbl_send_signals(dev); + atomic_inc(&dev_priv->vbl_received); + drm_handle_vblank(dev, 0); return IRQ_HANDLED; } return IRQ_NONE; } -int r128_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence) +int r128_enable_vblank(struct drm_device *dev, int crtc) { - unsigned int cur_vblank; - int ret = 0; + drm_r128_private_t *dev_priv = dev->dev_private; - /* Assume that the user has missed the current sequence number - * by about a day rather than she wants to wait for years - * using vertical blanks... - */ - DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, - (((cur_vblank = atomic_read(&dev->vbl_received)) - - *sequence) <= (1 << 23))); + if (crtc != 0) { + DRM_ERROR("%s: bad crtc %d\n", __FUNCTION__, crtc); + return -EINVAL; + } - *sequence = cur_vblank; + R128_WRITE(R128_GEN_INT_CNTL, R128_CRTC_VBLANK_INT_EN); + return 0; +} + +void r128_disable_vblank(struct drm_device *dev, int crtc) +{ + if (crtc != 0) + DRM_ERROR("%s: bad crtc %d\n", __FUNCTION__, crtc); - return ret; + /* + * FIXME: implement proper interrupt disable by using the vblank + * counter register (if available) + * + * R128_WRITE(R128_GEN_INT_CNTL, + * R128_READ(R128_GEN_INT_CNTL) & ~R128_CRTC_VBLANK_INT_EN); + */ } void r128_driver_irq_preinstall(struct drm_device * dev) @@ -82,12 +100,9 @@ void r128_driver_irq_preinstall(struct drm_device * dev) R128_WRITE(R128_GEN_INT_STATUS, R128_CRTC_VBLANK_INT_AK); } -void r128_driver_irq_postinstall(struct drm_device * dev) +int r128_driver_irq_postinstall(struct drm_device * dev) { - drm_r128_private_t *dev_priv = (drm_r128_private_t *) dev->dev_private; - - /* Turn on VBL interrupt */ - R128_WRITE(R128_GEN_INT_CNTL, R128_CRTC_VBLANK_INT_EN); + return drm_vblank_init(dev, 1); } void r128_driver_irq_uninstall(struct drm_device * dev) diff --git a/shared-core/radeon_drv.h b/shared-core/radeon_drv.h index 006559df..a71b0bee 100644 --- a/shared-core/radeon_drv.h +++ b/shared-core/radeon_drv.h @@ -299,6 +299,9 @@ typedef struct drm_radeon_private { u32 scratch_ages[5]; + unsigned int crtc_last_cnt; + unsigned int crtc2_last_cnt; + /* starting from here on, data is preserved accross an open */ uint32_t flags; /* see radeon_chip_flags */ @@ -364,13 +367,13 @@ extern int radeon_irq_emit(struct drm_device *dev, void *data, struct drm_file * extern int radeon_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_priv); extern void radeon_do_release(struct drm_device * dev); -extern int radeon_driver_vblank_wait(struct drm_device * dev, - unsigned int *sequence); -extern int radeon_driver_vblank_wait2(struct drm_device * dev, - unsigned int *sequence); +extern u32 radeon_get_vblank_counter(struct drm_device *dev, int crtc); +extern int radeon_enable_vblank(struct drm_device *dev, int crtc); +extern void radeon_disable_vblank(struct drm_device *dev, int crtc); +extern void radeon_do_release(struct drm_device * dev); extern irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS); extern void radeon_driver_irq_preinstall(struct drm_device * dev); -extern void radeon_driver_irq_postinstall(struct drm_device * dev); +extern int radeon_driver_irq_postinstall(struct drm_device * dev); extern void radeon_driver_irq_uninstall(struct drm_device * dev); extern int radeon_vblank_crtc_get(struct drm_device *dev); extern int radeon_vblank_crtc_set(struct drm_device *dev, int64_t value); @@ -511,6 +514,12 @@ extern int r300_do_cp_cmdbuf(struct drm_device *dev, ? DRM_READ32( dev_priv->ring_rptr, RADEON_SCRATCHOFF(x) ) \ : RADEON_READ( RADEON_SCRATCH_REG0 + 4*(x) ) ) +#define RADEON_CRTC_CRNT_FRAME 0x0214 +#define RADEON_CRTC2_CRNT_FRAME 0x0314 + +#define RADEON_CRTC_STATUS 0x005c +#define RADEON_CRTC2_STATUS 0x03fc + #define RADEON_GEN_INT_CNTL 0x0040 # define RADEON_CRTC_VBLANK_MASK (1 << 0) # define RADEON_CRTC2_VBLANK_MASK (1 << 9) diff --git a/shared-core/radeon_irq.c b/shared-core/radeon_irq.c index 1ece6399..6ba3c147 100644 --- a/shared-core/radeon_irq.c +++ b/shared-core/radeon_irq.c @@ -35,12 +35,61 @@ #include "radeon_drm.h" #include "radeon_drv.h" -static __inline__ u32 radeon_acknowledge_irqs(drm_radeon_private_t * dev_priv, - u32 mask) +static void radeon_irq_set_state(struct drm_device *dev, u32 mask, int state) { - u32 irqs = RADEON_READ(RADEON_GEN_INT_STATUS) & mask; + drm_radeon_private_t *dev_priv = dev->dev_private; + + if (state) + dev_priv->irq_enable_reg |= mask; + else + dev_priv->irq_enable_reg &= ~mask; + + RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg); +} + +int radeon_enable_vblank(struct drm_device *dev, int crtc) +{ + switch (crtc) { + case 0: + radeon_irq_set_state(dev, RADEON_CRTC_VBLANK_MASK, 1); + break; + case 1: + radeon_irq_set_state(dev, RADEON_CRTC2_VBLANK_MASK, 1); + break; + default: + DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", + crtc); + return EINVAL; + } + + return 0; +} + +void radeon_disable_vblank(struct drm_device *dev, int crtc) +{ + switch (crtc) { + case 0: + radeon_irq_set_state(dev, RADEON_CRTC_VBLANK_MASK, 0); + break; + case 1: + radeon_irq_set_state(dev, RADEON_CRTC2_VBLANK_MASK, 0); + break; + default: + DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", + crtc); + break; + } +} + +static __inline__ u32 radeon_acknowledge_irqs(drm_radeon_private_t * dev_priv) +{ + u32 irqs = RADEON_READ(RADEON_GEN_INT_STATUS) & + (RADEON_SW_INT_TEST | RADEON_CRTC_VBLANK_STAT | + RADEON_CRTC2_VBLANK_STAT); + if (irqs) RADEON_WRITE(RADEON_GEN_INT_STATUS, irqs); + return irqs; } @@ -72,39 +121,21 @@ irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS) /* Only consider the bits we're interested in - others could be used * outside the DRM */ - stat = radeon_acknowledge_irqs(dev_priv, (RADEON_SW_INT_TEST_ACK | - RADEON_CRTC_VBLANK_STAT | - RADEON_CRTC2_VBLANK_STAT)); + stat = radeon_acknowledge_irqs(dev_priv); if (!stat) return IRQ_NONE; stat &= dev_priv->irq_enable_reg; /* SW interrupt */ - if (stat & RADEON_SW_INT_TEST) { + if (stat & RADEON_SW_INT_TEST) DRM_WAKEUP(&dev_priv->swi_queue); - } /* VBLANK interrupt */ - if (stat & (RADEON_CRTC_VBLANK_STAT|RADEON_CRTC2_VBLANK_STAT)) { - int vblank_crtc = dev_priv->vblank_crtc; - - if ((vblank_crtc & - (DRM_RADEON_VBLANK_CRTC1 | DRM_RADEON_VBLANK_CRTC2)) == - (DRM_RADEON_VBLANK_CRTC1 | DRM_RADEON_VBLANK_CRTC2)) { - if (stat & RADEON_CRTC_VBLANK_STAT) - atomic_inc(&dev->vbl_received); - if (stat & RADEON_CRTC2_VBLANK_STAT) - atomic_inc(&dev->vbl_received2); - } else if (((stat & RADEON_CRTC_VBLANK_STAT) && - (vblank_crtc & DRM_RADEON_VBLANK_CRTC1)) || - ((stat & RADEON_CRTC2_VBLANK_STAT) && - (vblank_crtc & DRM_RADEON_VBLANK_CRTC2))) - atomic_inc(&dev->vbl_received); - - DRM_WAKEUP(&dev->vbl_queue); - drm_vbl_send_signals(dev); - } + if (stat & RADEON_CRTC_VBLANK_STAT) + drm_handle_vblank(dev, 0); + if (stat & RADEON_CRTC2_VBLANK_STAT) + drm_handle_vblank(dev, 1); return IRQ_HANDLED; } @@ -144,55 +175,27 @@ static int radeon_wait_irq(struct drm_device * dev, int swi_nr) return ret; } -static int radeon_driver_vblank_do_wait(struct drm_device * dev, - unsigned int *sequence, - int crtc) +u32 radeon_get_vblank_counter(struct drm_device *dev, int crtc) { - drm_radeon_private_t *dev_priv = - (drm_radeon_private_t *) dev->dev_private; - unsigned int cur_vblank; - int ret = 0; - int ack = 0; - atomic_t *counter; + drm_radeon_private_t *dev_priv = dev->dev_private; + u32 crtc_cnt_reg, crtc_status_reg; + if (!dev_priv) { DRM_ERROR("%s called with no initialization\n", __FUNCTION__); return -EINVAL; } - if (crtc == DRM_RADEON_VBLANK_CRTC1) { - counter = &dev->vbl_received; - ack |= RADEON_CRTC_VBLANK_STAT; - } else if (crtc == DRM_RADEON_VBLANK_CRTC2) { - counter = &dev->vbl_received2; - ack |= RADEON_CRTC2_VBLANK_STAT; - } else + if (crtc == 0) { + crtc_cnt_reg = RADEON_CRTC_CRNT_FRAME; + crtc_status_reg = RADEON_CRTC_STATUS; + } else if (crtc == 1) { + crtc_cnt_reg = RADEON_CRTC2_CRNT_FRAME; + crtc_status_reg = RADEON_CRTC2_STATUS; + } else { return -EINVAL; + } - radeon_acknowledge_irqs(dev_priv, ack); - - dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; - - /* Assume that the user has missed the current sequence number - * by about a day rather than she wants to wait for years - * using vertical blanks... - */ - DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, - (((cur_vblank = atomic_read(counter)) - - *sequence) <= (1 << 23))); - - *sequence = cur_vblank; - - return ret; -} - -int radeon_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence) -{ - return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC1); -} - -int radeon_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence) -{ - return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC2); + return RADEON_READ(crtc_cnt_reg) + (RADEON_READ(crtc_status_reg) & 1); } /* Needs the lock as it touches the ring. @@ -235,21 +238,6 @@ int radeon_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_pr return radeon_wait_irq(dev, irqwait->irq_seq); } -static void radeon_enable_interrupt(struct drm_device *dev) -{ - drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private; - - dev_priv->irq_enable_reg = RADEON_SW_INT_ENABLE; - if (dev_priv->vblank_crtc & DRM_RADEON_VBLANK_CRTC1) - dev_priv->irq_enable_reg |= RADEON_CRTC_VBLANK_MASK; - - if (dev_priv->vblank_crtc & DRM_RADEON_VBLANK_CRTC2) - dev_priv->irq_enable_reg |= RADEON_CRTC2_VBLANK_MASK; - - RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg); - dev_priv->irq_enabled = 1; -} - /* drm_dma.h hooks */ void radeon_driver_irq_preinstall(struct drm_device * dev) @@ -261,20 +249,27 @@ void radeon_driver_irq_preinstall(struct drm_device * dev) RADEON_WRITE(RADEON_GEN_INT_CNTL, 0); /* Clear bits if they're already high */ - radeon_acknowledge_irqs(dev_priv, (RADEON_SW_INT_TEST_ACK | - RADEON_CRTC_VBLANK_STAT | - RADEON_CRTC2_VBLANK_STAT)); + radeon_acknowledge_irqs(dev_priv); } -void radeon_driver_irq_postinstall(struct drm_device * dev) +int radeon_driver_irq_postinstall(struct drm_device * dev) { drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private; + int ret; atomic_set(&dev_priv->swi_emitted, 0); DRM_INIT_WAITQUEUE(&dev_priv->swi_queue); - radeon_enable_interrupt(dev); + ret = drm_vblank_init(dev, 2); + if (ret) + return ret; + + dev->max_vblank_count = 0x001fffff; + + radeon_irq_set_state(dev, RADEON_SW_INT_ENABLE, 1); + + return 0; } void radeon_driver_irq_uninstall(struct drm_device * dev) @@ -316,6 +311,5 @@ int radeon_vblank_crtc_set(struct drm_device *dev, int64_t value) return -EINVAL; } dev_priv->vblank_crtc = (unsigned int)value; - radeon_enable_interrupt(dev); return 0; } diff --git a/shared-core/via_drv.c b/shared-core/via_drv.c index 9f099555..3a6f27fb 100644 --- a/shared-core/via_drv.c +++ b/shared-core/via_drv.c @@ -83,14 +83,16 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *ent); static struct drm_driver driver = { .driver_features = DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_HAVE_IRQ | - DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL, + DRIVER_IRQ_SHARED, .load = via_driver_load, .unload = via_driver_unload, #ifndef VIA_HAVE_CORE_MM .context_ctor = via_init_context, #endif .context_dtor = via_final_context, - .vblank_wait = via_driver_vblank_wait, + .get_vblank_counter = via_get_vblank_counter, + .enable_vblank = via_enable_vblank, + .disable_vblank = via_disable_vblank, .irq_preinstall = via_driver_irq_preinstall, .irq_postinstall = via_driver_irq_postinstall, .irq_uninstall = via_driver_irq_uninstall, diff --git a/shared-core/via_drv.h b/shared-core/via_drv.h index 0b474844..bb1c4857 100644 --- a/shared-core/via_drv.h +++ b/shared-core/via_drv.h @@ -102,6 +102,7 @@ typedef struct drm_via_private { struct timeval last_vblank; int last_vblank_valid; unsigned usec_per_vblank; + atomic_t vbl_received; drm_via_state_t hc_state; char pci_buf[VIA_PCI_BUF_SIZE]; const uint32_t *fire_offsets[VIA_FIRE_BUF_SIZE]; @@ -166,11 +167,13 @@ extern int via_driver_unload(struct drm_device *dev); extern int via_final_context(struct drm_device * dev, int context); extern int via_do_cleanup_map(struct drm_device * dev); -extern int via_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence); +extern u32 via_get_vblank_counter(struct drm_device *dev, int crtc); +extern int via_enable_vblank(struct drm_device *dev, int crtc); +extern void via_disable_vblank(struct drm_device *dev, int crtc); extern irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS); extern void via_driver_irq_preinstall(struct drm_device * dev); -extern void via_driver_irq_postinstall(struct drm_device * dev); +extern int via_driver_irq_postinstall(struct drm_device * dev); extern void via_driver_irq_uninstall(struct drm_device * dev); extern int via_dma_cleanup(struct drm_device * dev); diff --git a/shared-core/via_irq.c b/shared-core/via_irq.c index 475b6461..a1d33248 100644 --- a/shared-core/via_irq.c +++ b/shared-core/via_irq.c @@ -97,6 +97,15 @@ static unsigned time_diff(struct timeval *now,struct timeval *then) 1000000 - (then->tv_usec - now->tv_usec); } +u32 via_get_vblank_counter(struct drm_device *dev, int crtc) +{ + drm_via_private_t *dev_priv = dev->dev_private; + if (crtc != 0) + return 0; + + return atomic_read(&dev_priv->vbl_received); +} + irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; @@ -109,8 +118,8 @@ irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS) status = VIA_READ(VIA_REG_INTERRUPT); if (status & VIA_IRQ_VBLANK_PENDING) { - atomic_inc(&dev->vbl_received); - if (!(atomic_read(&dev->vbl_received) & 0x0F)) { + atomic_inc(&dev_priv->vbl_received); + if (!(atomic_read(&dev_priv->vbl_received) & 0x0F)) { #ifdef __linux__ do_gettimeofday(&cur_vblank); #else @@ -124,12 +133,11 @@ irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS) dev_priv->last_vblank = cur_vblank; dev_priv->last_vblank_valid = 1; } - if (!(atomic_read(&dev->vbl_received) & 0xFF)) { + if (!(atomic_read(&dev_priv->vbl_received) & 0xFF)) { DRM_DEBUG("US per vblank is: %u\n", dev_priv->usec_per_vblank); } - DRM_WAKEUP(&dev->vbl_queue); - drm_vbl_send_signals(dev); + drm_handle_vblank(dev, 0); handled = 1; } @@ -171,31 +179,30 @@ static __inline__ void viadrv_acknowledge_irqs(drm_via_private_t * dev_priv) } } -int via_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence) +int via_enable_vblank(struct drm_device *dev, int crtc) { - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - unsigned int cur_vblank; - int ret = 0; + drm_via_private_t *dev_priv = dev->dev_private; + u32 status; - DRM_DEBUG("viadrv_vblank_wait\n"); - if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __FUNCTION__); + if (crtc != 0) { + DRM_ERROR("%s: bad crtc %d\n", __FUNCTION__, crtc); return -EINVAL; } - viadrv_acknowledge_irqs(dev_priv); + status = VIA_READ(VIA_REG_INTERRUPT); + VIA_WRITE(VIA_REG_INTERRUPT, status & VIA_IRQ_VBLANK_ENABLE); + return 0; +} - /* Assume that the user has missed the current sequence number - * by about a day rather than she wants to wait for years - * using vertical blanks... - */ +void via_disable_vblank(struct drm_device *dev, int crtc) +{ + if (crtc != 0) + DRM_ERROR("%s: bad crtc %d\n", __FUNCTION__, crtc); - DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, - (((cur_vblank = atomic_read(&dev->vbl_received)) - - *sequence) <= (1 << 23))); - - *sequence = cur_vblank; - return ret; + /* + * FIXME: implement proper interrupt disable by using the vblank + * counter register (if available). + */ } static int @@ -302,23 +309,24 @@ void via_driver_irq_preinstall(struct drm_device * dev) } } -void via_driver_irq_postinstall(struct drm_device * dev) +int via_driver_irq_postinstall(struct drm_device * dev) { drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; u32 status; DRM_DEBUG("via_driver_irq_postinstall\n"); - if (dev_priv) { - status = VIA_READ(VIA_REG_INTERRUPT); - VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL - | dev_priv->irq_enable_mask); + if (!dev_priv) + return -EINVAL; - /* Some magic, oh for some data sheets ! */ + status = VIA_READ(VIA_REG_INTERRUPT); + VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL + | dev_priv->irq_enable_mask); - VIA_WRITE8(0x83d4, 0x11); - VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30); - - } + /* Some magic, oh for some data sheets ! */ + VIA_WRITE8(0x83d4, 0x11); + VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30); + + return 0; } void via_driver_irq_uninstall(struct drm_device * dev) |