summaryrefslogtreecommitdiffstats
path: root/sys/dev/pci/drm/drm_irq.c
diff options
context:
space:
mode:
authoroga <oga@openbsd.org>2008-07-29 22:23:50 +0000
committeroga <oga@openbsd.org>2008-07-29 22:23:50 +0000
commitbdca3b8a71f6f9e5464d6a949abd95390819be4f (patch)
treed8757c0dfae32b312bb088e01efc12e3fbf3ec0f /sys/dev/pci/drm/drm_irq.c
parentUpdate list of supported machines. (diff)
downloadwireguard-openbsd-bdca3b8a71f6f9e5464d6a949abd95390819be4f.tar.xz
wireguard-openbsd-bdca3b8a71f6f9e5464d6a949abd95390819be4f.zip
Update to DRM git.
Some stability fixes for radeon. The most part of this diff is related to fixing up the VBLANK (vertical blank interrupt) handling. Now, if the X driver supports the DRM_IOCTL_MODESET_CTL ioctl, (to be used when changing the video modes), then allow the vblank to be disabled once that ioctl has been called. Otherwise, keep the interrupt enabled at all time, since disabling it otherwise will lead to problems. Tested by a few. "no problem" on API/ABI deraadt@.
Diffstat (limited to 'sys/dev/pci/drm/drm_irq.c')
-rw-r--r--sys/dev/pci/drm/drm_irq.c153
1 files changed, 69 insertions, 84 deletions
diff --git a/sys/dev/pci/drm/drm_irq.c b/sys/dev/pci/drm/drm_irq.c
index e359abaefa7..30ba65057d6 100644
--- a/sys/dev/pci/drm/drm_irq.c
+++ b/sys/dev/pci/drm/drm_irq.c
@@ -37,6 +37,7 @@
irqreturn_t drm_irq_handler_wrap(DRM_IRQ_ARGS);
void vblank_disable(void *);
+void drm_update_vblank_count(struct drm_device *, int);
void drm_locked_task(void *context, void *pending);
int
@@ -183,10 +184,15 @@ vblank_disable(void *arg)
struct drm_device *dev = (struct drm_device*)arg;
int i;
+ if (!dev->vblank_disable_allowed)
+ return;
+
for (i=0; i < dev->num_crtcs; i++){
DRM_SPINLOCK(&dev->vbl_lock);
if (atomic_read(&dev->vblank_refcount[i]) == 0 &&
dev->vblank_enabled[i]) {
+ dev->last_vblank[i] =
+ dev->driver.get_vblank_counter(dev, i);
dev->driver.disable_vblank(dev, i);
dev->vblank_enabled[i] = 0;
}
@@ -204,32 +210,21 @@ drm_vblank_cleanup(struct drm_device *dev)
vblank_disable(dev);
- if (dev->vbl_queue)
- drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) *
- dev->num_crtcs, M_DRM);
+ drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) *
+ dev->num_crtcs, M_DRM);
#if 0 /* disabled for now */
- if (dev-vbl_sigs)
- drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs,
- M_DRM);
+ drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs, M_DRM);
#endif
- if (dev->_vblank_count)
- drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) *
- dev->num_crtcs, M_DRM);
- if (dev->vblank_refcount)
- drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) *
- dev->num_crtcs, M_DRM);
- if (dev->vblank_enabled)
- drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) *
- dev->num_crtcs, M_DRM);
- if (dev->last_vblank)
- drm_free(dev->last_vblank, sizeof(*dev->last_vblank) *
- dev->num_crtcs, M_DRM);
- if (dev->vblank_premodeset)
- drm_free(dev->vblank_premodeset,
- sizeof(*dev->vblank_premodeset) * dev->num_crtcs, M_DRM);
- if (dev->vblank_suspend)
- drm_free(dev->vblank_suspend,
- sizeof(*dev->vblank_suspend) * dev->num_crtcs, M_DRM);
+ drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) *
+ dev->num_crtcs, M_DRM);
+ drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) *
+ dev->num_crtcs, M_DRM);
+ drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) *
+ dev->num_crtcs, M_DRM);
+ drm_free(dev->last_vblank, sizeof(*dev->last_vblank) *
+ dev->num_crtcs, M_DRM);
+ drm_free(dev->vblank_inmodeset, sizeof(*dev->vblank_inmodeset) *
+ dev->num_crtcs, M_DRM);
dev->num_crtcs = 0;
DRM_SPINUNINIT(&dev->vbl_lock);
@@ -259,23 +254,21 @@ drm_vblank_init(struct drm_device *dev, int num_crtcs)
if ((dev->vblank_enabled = drm_calloc(num_crtcs,
sizeof(*dev->vblank_enabled), M_DRM)) == NULL)
goto err;
- if ((dev->last_vblank = drm_calloc(num_crtcs, sizeof(*dev->last_vblank),
- M_DRM)) == NULL)
- goto err;
- if ((dev->vblank_premodeset = drm_calloc(num_crtcs,
- sizeof(*dev->vblank_premodeset), M_DRM)) == NULL)
+ if ((dev->last_vblank = drm_calloc(num_crtcs,
+ sizeof(*dev->last_vblank), M_DRM)) == NULL)
goto err;
- if ((dev->vblank_suspend = drm_calloc(num_crtcs,
- sizeof(*dev->vblank_suspend), M_DRM)) == NULL)
+ if ((dev->vblank_inmodeset = drm_calloc(num_crtcs,
+ sizeof(*dev->vblank_inmodeset), M_DRM)) == NULL)
goto err;
-
/* Zero everything */
for (i = 0; i < num_crtcs; i++) {
atomic_set(&dev->_vblank_count[i], 0);
atomic_set(&dev->vblank_refcount[i], 0);
}
+ dev->vblank_disable_allowed = 0;
+
return (0);
err:
@@ -294,27 +287,15 @@ drm_update_vblank_count(struct drm_device *dev, int crtc)
{
u_int32_t cur_vblank, diff;
- if (dev->vblank_suspend[crtc])
- return;
-
/*
- * Deal with the possibility of lost vblanks due to disabled interrupts
- * counter overflow may have happened.
+ * Interrupt was disabled prior to this call, so deal with counter wrap
+ * note that we may have lost a full dev->max_vblank_count events if
+ * the register is small or the interrupts were off for a long time.
*/
cur_vblank = dev->driver.get_vblank_counter(dev, crtc);
- DRM_SPINLOCK(&dev->vbl_lock);
- if (cur_vblank < dev->last_vblank[crtc]) {
- if (cur_vblank == dev->last_vblank[crtc] -1)
- diff = 0;
- else {
- 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;
- DRM_SPINUNLOCK(&dev->vbl_lock);
+ diff = cur_vblank - dev->last_vblank[crtc];
+ if (cur_vblank < dev->last_vblank[crtc])
+ diff += dev->max_vblank_count;
atomic_add(diff, &dev->_vblank_count[crtc]);
}
@@ -334,11 +315,12 @@ drm_vblank_get(struct drm_device *dev, int crtc)
atomic_dec(&dev->vblank_refcount[crtc]);
} else {
dev->vblank_enabled[crtc] = 1;
+ drm_update_vblank_count(dev, crtc);
}
}
DRM_SPINUNLOCK(&dev->vbl_lock);
- return ret;
+ return (ret);
}
void
@@ -348,7 +330,7 @@ drm_vblank_put(struct drm_device *dev, int crtc)
/* Last user schedules interrupt disable */
atomic_dec(&dev->vblank_refcount[crtc]);
if (dev->vblank_refcount[crtc] == 0)
- timeout_add(&dev->vblank_disable_timer, 5*DRM_HZ);
+ timeout_add_sec(&dev->vblank_disable_timer, 5);
DRM_SPINUNLOCK(&dev->vbl_lock);
}
@@ -358,36 +340,43 @@ drm_modeset_ctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
struct drm_modeset_ctl *modeset = data;
int crtc, ret = 0;
+ /* not initialised yet, just noop */
+ if (dev->num_crtcs == 0)
+ goto out;
+
crtc = modeset->crtc;
if (crtc >= dev->num_crtcs) {
ret = EINVAL;
goto out;
}
+ /* If interrupts are enabled/disabled between calls to this ioctl then
+ * it can get nasty. So just grab a reference so that the interrupts
+ * keep going through the modeset
+ */
switch (modeset->cmd) {
case _DRM_PRE_MODESET:
- dev->vblank_premodeset[crtc] =
- dev->driver.get_vblank_counter(dev, crtc);
- dev->vblank_suspend[crtc] = 1;
+ if (dev->vblank_inmodeset[crtc] == 0) {
+ dev->vblank_inmodeset[crtc] = 1;
+ drm_vblank_get(dev, crtc);
+ }
break;
case _DRM_POST_MODESET:
- if (dev->vblank_suspend[crtc]) {
- uint32_t new =
- dev->driver.get_vblank_counter(dev, crtc);
- /* Compensate for spurious wraparound */
- if (new < dev->vblank_premodeset[crtc])
- atomic_sub(dev->max_vblank_count + new -
- dev->vblank_premodeset[crtc],
- &dev->_vblank_count[crtc]);
+ if (dev->vblank_inmodeset[crtc]) {
+ DRM_SPINLOCK(&dev->vbl_lock);
+ dev->vblank_disable_allowed = 1;
+ dev->vblank_inmodeset[crtc] = 0;
+ DRM_SPINUNLOCK(&dev->vbl_lock);
+ drm_vblank_put(dev, crtc);
}
- dev->vblank_suspend[crtc] = 0;
break;
default:
ret = EINVAL;
+ break;
}
out:
- return ret;
+ return (ret);
}
int
@@ -405,7 +394,9 @@ drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv)
if (crtc >= dev->num_crtcs)
return EINVAL;
- drm_update_vblank_count(dev, crtc);
+ ret = drm_vblank_get(dev, crtc);
+ if (ret)
+ return (ret);
seq = drm_vblank_count(dev,crtc);
if (vblwait->request.type & _DRM_VBLANK_RELATIVE) {
@@ -440,29 +431,22 @@ drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv)
#endif
ret = EINVAL;
} else {
- if (!dev->vblank_suspend[crtc]) {
- unsigned long cur_vblank;
-
- ret = drm_vblank_get(dev, crtc);
- if (ret)
- return ret;
- while (ret == 0) {
- DRM_SPINLOCK(&dev->vbl_lock);
- if (((cur_vblank = drm_vblank_count(dev, crtc))
- - vblwait->request.sequence) <= (1 << 23)) {
- DRM_SPINUNLOCK(&dev->vbl_lock);
- break;
- }
- ret = msleep(&dev->vbl_queue[crtc],
- &dev->vbl_lock, PZERO | PCATCH,
- "drmvblq", 3 * DRM_HZ);
+ while (ret == 0) {
+ DRM_SPINLOCK(&dev->vbl_lock);
+ if ((drm_vblank_count(dev, crtc)
+ - vblwait->request.sequence) <= (1 << 23)) {
DRM_SPINUNLOCK(&dev->vbl_lock);
+ break;
}
- drm_vblank_put(dev, crtc);
+ ret = msleep(&dev->vbl_queue[crtc],
+ &dev->vbl_lock, PZERO | PCATCH,
+ "drmvblq", 3 * DRM_HZ);
+ DRM_SPINUNLOCK(&dev->vbl_lock);
}
if (ret != EINTR) {
struct timeval now;
+
microtime(&now);
vblwait->reply.tval_sec = now.tv_sec;
vblwait->reply.tval_usec = now.tv_usec;
@@ -470,6 +454,7 @@ drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv)
}
}
+ drm_vblank_put(dev, crtc);
return (ret);
}
@@ -506,7 +491,7 @@ drm_vbl_send_signals(struct drm_device *dev, int crtc)
void
drm_handle_vblank(struct drm_device *dev, int crtc)
{
- drm_update_vblank_count(dev, crtc);
+ atomic_inc(&dev->_vblank_count[crtc]);
DRM_WAKEUP(&dev->vbl_queue[crtc]);
drm_vbl_send_signals(dev, crtc);
}