summaryrefslogtreecommitdiff
path: root/linux-core
diff options
context:
space:
mode:
authorJesse Barnes <jbarnes@hobbes.virtuousgeek.org>2007-06-12 10:44:21 -0700
committerJesse Barnes <jbarnes@hobbes.virtuousgeek.org>2007-06-12 10:44:21 -0700
commitdb689c7b95613237cec904c3f6ee27e8c2bf7ce0 (patch)
tree9e8b5377207f3d2a89759f189b1be611f6e85859 /linux-core
parent280083d4a2a12a1ff6dc1b068553a4ae8960200c (diff)
Initial checkin of vblank rework. Code attempts to reduce the number
of vblank interrupt in order to save power.
Diffstat (limited to 'linux-core')
-rw-r--r--linux-core/drmP.h12
-rw-r--r--linux-core/drm_irq.c69
-rw-r--r--linux-core/i915_drv.c5
3 files changed, 63 insertions, 23 deletions
diff --git a/linux-core/drmP.h b/linux-core/drmP.h
index dd3a69df..c3f20311 100644
--- a/linux-core/drmP.h
+++ b/linux-core/drmP.h
@@ -627,8 +627,9 @@ 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);
+ u32 (*get_vblank_counter) (struct drm_device *dev, int crtc);
+ void (*enable_vblank) (struct drm_device *dev, int crtc);
+ void (*disable_vblank) (struct drm_device *dev, int crtc);
int (*dri_library_name) (struct drm_device * dev, char * buf);
/**
@@ -783,12 +784,13 @@ typedef struct drm_device {
/*@{ */
wait_queue_head_t vbl_queue; /**< VBLANK wait queue */
- atomic_t vbl_received;
- atomic_t vbl_received2; /**< number of secondary VBLANK interrupts */
+ 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;
+ atomic_t *vbl_pending;
+ u32 *last_vblank; /* protected by dev->vbl_lock */
+ 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);
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);
}
}
}
diff --git a/linux-core/i915_drv.c b/linux-core/i915_drv.c
index 7fdb0839..e7a9bfdc 100644
--- a/linux-core/i915_drv.c
+++ b/linux-core/i915_drv.c
@@ -83,8 +83,9 @@ static struct drm_driver driver = {
.lastclose = i915_driver_lastclose,
.preclose = i915_driver_preclose,
.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,