aboutsummaryrefslogtreecommitdiff
path: root/linux-core
diff options
context:
space:
mode:
authorJesse Barnes <jesse.barnes@intel.com>2007-06-12 13:35:41 -0700
committerJesse Barnes <jesse.barnes@intel.com>2007-06-12 13:35:41 -0700
commitca47fa90b73d0eac73fb7d1ba712d81e180eae7d (patch)
tree586ab4ad1306c572405617f56f7aaabb0e79116f /linux-core
parentdb689c7b95613237cec904c3f6ee27e8c2bf7ce0 (diff)
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
Diffstat (limited to 'linux-core')
-rw-r--r--linux-core/drmP.h22
-rw-r--r--linux-core/drm_drv.c1
-rw-r--r--linux-core/drm_irq.c145
3 files changed, 137 insertions, 31 deletions
diff --git a/linux-core/drmP.h b/linux-core/drmP.h
index c3f20311..c8b72257 100644
--- a/linux-core/drmP.h
+++ b/linux-core/drmP.h
@@ -648,7 +648,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 file * filp);
void (*reclaim_buffers_locked) (struct drm_device *dev,
@@ -786,10 +786,14 @@ typedef struct drm_device {
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 */
- atomic_t *vbl_pending;
- u32 *last_vblank; /* protected by dev->vbl_lock */
+ struct list_head *vbl_sigs; /**< signal list to send on VBLANK */
+ atomic_t vbl_pending; /* number of signals pending on all crtcs*/
+ atomic_t *vblank_usage; /* 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 */
+
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);
@@ -810,6 +814,7 @@ typedef struct drm_device {
#ifdef __alpha__
struct pci_controller *hose;
#endif
+ int num_crtcs; /**< Number of CRTCs on this device */
drm_sg_mem_t *sg; /**< Scatter gather memory */
void *dev_private; /**< device private data */
drm_sigdata_t sigdata; /**< For block_all_signals */
@@ -1074,11 +1079,18 @@ extern void drm_driver_irq_preinstall(drm_device_t * dev);
extern void drm_driver_irq_postinstall(drm_device_t * dev);
extern void drm_driver_irq_uninstall(drm_device_t * dev);
+extern int drm_vblank_init(drm_device_t *dev, int num_crtcs);
extern int drm_wait_vblank(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg);
extern int drm_vblank_wait(drm_device_t * dev, unsigned int *vbl_seq);
extern void drm_vbl_send_signals(drm_device_t * dev);
extern void drm_locked_tasklet(drm_device_t *dev, void(*func)(drm_device_t*));
+extern void drm_vblank_get(drm_device_t *dev, int crtc);
+extern void drm_vblank_put(drm_device_t *dev, int crtc);
+
+ /* Modesetting support */
+extern int drm_modeset_ctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
/* AGP/GART support (drm_agpsupport.h) */
extern drm_agp_head_t *drm_agp_init(drm_device_t *dev);
diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c
index d5eb9713..1b37ee4a 100644
--- a/linux-core/drm_drv.c
+++ b/linux-core/drm_drv.c
@@ -123,6 +123,7 @@ static drm_ioctl_desc_t drm_ioctls[] = {
DRM_AUTH },
[DRM_IOCTL_NR(DRM_IOCTL_UPDATE_DRAW)] = {drm_update_drawable_info, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY},
+ [DRM_IOCTL_NR(DRM_IOCTL_MODESET_CTL)] = {drm_modeset_ctl, 0},
};
#define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls )
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);
}
}