From 2186f9f6eff4b3b4f605d35a030c0910646865ab Mon Sep 17 00:00:00 2001 From: Robert Noland Date: Sun, 1 Jun 2008 12:49:20 -0400 Subject: [FreeBSD] Call drm_vblank_cleanup during irq uninstall I needed to re-arrange some functions for this. Also needed to call DRM_SPINUNINIT on the vbl_lock during cleanup. --- bsd-core/drm_irq.c | 287 +++++++++++++++++++++++++++-------------------------- 1 file changed, 145 insertions(+), 142 deletions(-) (limited to 'bsd-core/drm_irq.c') diff --git a/bsd-core/drm_irq.c b/bsd-core/drm_irq.c index 592e2ea8..4b10ba4f 100644 --- a/bsd-core/drm_irq.c +++ b/bsd-core/drm_irq.c @@ -66,6 +66,116 @@ drm_irq_handler_wrap(DRM_IRQ_ARGS) } #endif +static void vblank_disable_fn(void *arg) +{ + struct drm_device *dev = (struct drm_device *)arg; + unsigned long irqflags; + int i; + + for (i = 0; i < dev->num_crtcs; i++) { + DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); + if (atomic_read(&dev->vblank_refcount[i]) == 0 && + dev->vblank_enabled[i]) { + dev->driver.disable_vblank(dev, i); + dev->vblank_enabled[i] = 0; + } + DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); + } +} + +static void drm_vblank_cleanup(struct drm_device *dev) +{ + /* Bail if the driver didn't call drm_vblank_init() */ + if (dev->num_crtcs == 0) + return; + + callout_drain(&dev->vblank_disable_timer); + + vblank_disable_fn((void *)dev); + DRM_SPINUNINIT(&dev->vbl_lock); + + drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs, + DRM_MEM_DRIVER); + drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs, + DRM_MEM_DRIVER); + drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) * + dev->num_crtcs, DRM_MEM_DRIVER); + drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) * + dev->num_crtcs, DRM_MEM_DRIVER); + drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) * + dev->num_crtcs, DRM_MEM_DRIVER); + drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs, + DRM_MEM_DRIVER); + drm_free(dev->vblank_premodeset, sizeof(*dev->vblank_premodeset) * + dev->num_crtcs, DRM_MEM_DRIVER); + drm_free(dev->vblank_offset, sizeof(*dev->vblank_offset) * dev->num_crtcs, + DRM_MEM_DRIVER); + + dev->num_crtcs = 0; +} + +int drm_vblank_init(struct drm_device *dev, int num_crtcs) +{ + int i, ret = -ENOMEM; + + callout_init(&dev->vblank_disable_timer, 0); + DRM_SPININIT(&dev->vbl_lock, "drm_vblk"); + 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 drm_vbl_sig) * 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->vblank_enabled = drm_calloc(num_crtcs, sizeof(int), + DRM_MEM_DRIVER); + if (!dev->vblank_enabled) + goto err; + + dev->last_vblank = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER); + if (!dev->last_vblank) + goto err; + + dev->vblank_premodeset = drm_calloc(num_crtcs, sizeof(u32), + DRM_MEM_DRIVER); + if (!dev->vblank_premodeset) + goto err; + + dev->vblank_offset = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER); + if (!dev->vblank_offset) + goto err; + + /* Zero per-crtc vblank stuff */ + for (i = 0; i < num_crtcs; i++) { + DRM_INIT_WAITQUEUE(&dev->vbl_queue[i]); + TAILQ_INIT(&dev->vbl_sigs[i]); + atomic_set(&dev->_vblank_count[i], 0); + atomic_set(&dev->vblank_refcount[i], 0); + } + + return 0; + +err: + drm_vblank_cleanup(dev); + return ret; +} + int drm_irq_install(struct drm_device *dev) { int retcode; @@ -175,6 +285,8 @@ int drm_irq_uninstall(struct drm_device *dev) #elif defined(__NetBSD__) || defined(__OpenBSD__) pci_intr_disestablish(&dev->pa.pa_pc, dev->irqh); #endif + drm_vblank_cleanup(dev); + DRM_SPINUNINIT(&dev->irq_lock); return 0; @@ -208,27 +320,37 @@ int drm_control(struct drm_device *dev, void *data, struct drm_file *file_priv) } } -static void vblank_disable_fn(void *arg) +u32 drm_vblank_count(struct drm_device *dev, int crtc) +{ + return atomic_read(&dev->_vblank_count[crtc]) + + dev->vblank_offset[crtc]; +} + +void drm_update_vblank_count(struct drm_device *dev, int crtc) { - struct drm_device *dev = (struct drm_device *)arg; unsigned long irqflags; - int i; + u32 cur_vblank, diff; - for (i = 0; i < dev->num_crtcs; i++) { - DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); - if (atomic_read(&dev->vblank_refcount[i]) == 0 && - dev->vblank_enabled[i]) { - dev->driver.disable_vblank(dev, i); - dev->vblank_enabled[i] = 0; - } - DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); + /* + * 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); + DRM_SPINLOCK_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; + DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); -u32 drm_vblank_count(struct drm_device *dev, int crtc) -{ - return atomic_read(&dev->_vblank_count[crtc]) + - dev->vblank_offset[crtc]; + atomic_add(diff, &dev->_vblank_count[crtc]); } int drm_vblank_get(struct drm_device *dev, int crtc) @@ -261,40 +383,6 @@ void drm_vblank_put(struct drm_device *dev, int crtc) (timeout_t *)vblank_disable_fn, (void *)dev); } -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); -} - -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); - DRM_SPINLOCK_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; - DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); - - atomic_add(diff, &dev->_vblank_count[crtc]); -} - int drm_modeset_ctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -410,98 +498,6 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr return ret; } -static void drm_vblank_cleanup(struct drm_device *dev) -{ - /* Bail if the driver didn't call drm_vblank_init() */ - if (dev->num_crtcs == 0) - return; - - callout_stop(&dev->vblank_disable_timer); - - vblank_disable_fn((void *)dev); - - drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs, - DRM_MEM_DRIVER); - drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs, - DRM_MEM_DRIVER); - drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) * - dev->num_crtcs, DRM_MEM_DRIVER); - drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) * - dev->num_crtcs, DRM_MEM_DRIVER); - drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) * - dev->num_crtcs, DRM_MEM_DRIVER); - drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs, - DRM_MEM_DRIVER); - drm_free(dev->vblank_premodeset, sizeof(*dev->vblank_premodeset) * - dev->num_crtcs, DRM_MEM_DRIVER); - drm_free(dev->vblank_offset, sizeof(*dev->vblank_offset) * dev->num_crtcs, - DRM_MEM_DRIVER); - - dev->num_crtcs = 0; -} - -int drm_vblank_init(struct drm_device *dev, int num_crtcs) -{ - int i, ret = -ENOMEM; - - callout_init(&dev->vblank_disable_timer, 0); - DRM_SPININIT(&dev->vbl_lock, "drm_vblk"); - 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 drm_vbl_sig) * 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->vblank_enabled = drm_calloc(num_crtcs, sizeof(int), - DRM_MEM_DRIVER); - if (!dev->vblank_enabled) - goto err; - - dev->last_vblank = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER); - if (!dev->last_vblank) - goto err; - - dev->vblank_premodeset = drm_calloc(num_crtcs, sizeof(u32), - DRM_MEM_DRIVER); - if (!dev->vblank_premodeset) - goto err; - - dev->vblank_offset = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER); - if (!dev->vblank_offset) - goto err; - - /* Zero per-crtc vblank stuff */ - for (i = 0; i < num_crtcs; i++) { - DRM_INIT_WAITQUEUE(&dev->vbl_queue[i]); - TAILQ_INIT(&dev->vbl_sigs[i]); - atomic_set(&dev->_vblank_count[i], 0); - atomic_set(&dev->vblank_refcount[i], 0); - } - - return 0; - -err: - drm_vblank_cleanup(dev); - return ret; -} - void drm_vbl_send_signals(struct drm_device *dev, int crtc) { } @@ -530,6 +526,13 @@ void drm_vbl_send_signals(struct drm_device *dev, int crtc ) } #endif +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); +} + static void drm_locked_task(void *context, int pending __unused) { struct drm_device *dev = context; -- cgit v1.2.3 From 4ce47fd328cd885d66abdd42db1f7c054bd44498 Mon Sep 17 00:00:00 2001 From: Robert Noland Date: Sun, 1 Jun 2008 16:17:31 -0400 Subject: [FreeBSD] Get rid of vbl_lock and re-use irq_lock. --- bsd-core/drm_irq.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'bsd-core/drm_irq.c') diff --git a/bsd-core/drm_irq.c b/bsd-core/drm_irq.c index 4b10ba4f..57a70263 100644 --- a/bsd-core/drm_irq.c +++ b/bsd-core/drm_irq.c @@ -73,13 +73,13 @@ static void vblank_disable_fn(void *arg) int i; for (i = 0; i < dev->num_crtcs; i++) { - DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); + DRM_SPINLOCK_IRQSAVE(&dev->irq_lock, irqflags); if (atomic_read(&dev->vblank_refcount[i]) == 0 && dev->vblank_enabled[i]) { dev->driver.disable_vblank(dev, i); dev->vblank_enabled[i] = 0; } - DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); + DRM_SPINUNLOCK_IRQRESTORE(&dev->irq_lock, irqflags); } } @@ -92,7 +92,6 @@ static void drm_vblank_cleanup(struct drm_device *dev) callout_drain(&dev->vblank_disable_timer); vblank_disable_fn((void *)dev); - DRM_SPINUNINIT(&dev->vbl_lock); drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs, DRM_MEM_DRIVER); @@ -119,7 +118,6 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) int i, ret = -ENOMEM; callout_init(&dev->vblank_disable_timer, 0); - DRM_SPININIT(&dev->vbl_lock, "drm_vblk"); atomic_set(&dev->vbl_signal_pending, 0); dev->num_crtcs = num_crtcs; @@ -339,7 +337,7 @@ void drm_update_vblank_count(struct drm_device *dev, int crtc) * a long time. */ cur_vblank = dev->driver.get_vblank_counter(dev, crtc); - DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); + DRM_SPINLOCK_IRQSAVE(&dev->irq_lock, irqflags); if (cur_vblank < dev->last_vblank[crtc]) { diff = dev->max_vblank_count - dev->last_vblank[crtc]; @@ -348,7 +346,7 @@ void drm_update_vblank_count(struct drm_device *dev, int crtc) diff = cur_vblank - dev->last_vblank[crtc]; } dev->last_vblank[crtc] = cur_vblank; - DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); + DRM_SPINUNLOCK_IRQRESTORE(&dev->irq_lock, irqflags); atomic_add(diff, &dev->_vblank_count[crtc]); } @@ -358,7 +356,7 @@ int drm_vblank_get(struct drm_device *dev, int crtc) unsigned long irqflags; int ret = 0; - DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); + DRM_SPINLOCK_IRQSAVE(&dev->irq_lock, irqflags); /* Going from 0->1 means we have to enable interrupts again */ atomic_add_acq_int(&dev->vblank_refcount[crtc], 1); if (dev->vblank_refcount[crtc] == 1 && @@ -369,7 +367,7 @@ int drm_vblank_get(struct drm_device *dev, int crtc) else dev->vblank_enabled[crtc] = 1; } - DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); + DRM_SPINUNLOCK_IRQRESTORE(&dev->irq_lock, irqflags); return ret; } -- cgit v1.2.3 From 416754f1cc5a55b1c6b2d2fa2f501b18462d62e6 Mon Sep 17 00:00:00 2001 From: Robert Noland Date: Sun, 1 Jun 2008 19:34:29 -0400 Subject: [FreeBSD] Declare vblank_disable_fn callout MPSAFE. --- bsd-core/drm_irq.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'bsd-core/drm_irq.c') diff --git a/bsd-core/drm_irq.c b/bsd-core/drm_irq.c index 57a70263..b76e96dd 100644 --- a/bsd-core/drm_irq.c +++ b/bsd-core/drm_irq.c @@ -72,23 +72,40 @@ static void vblank_disable_fn(void *arg) unsigned long irqflags; int i; + DRM_SPINLOCK_IRQSAVE(&dev->irq_lock, irqflags); + if (callout_pending(&dev->vblank_disable_timer)) { + /* callout was reset */ + DRM_SPINUNLOCK_IRQRESTORE(&dev->irq_lock, irqflags); + return; + } + if (!callout_active(&dev->vblank_disable_timer)) { + /* callout was stopped */ + DRM_SPINUNLOCK_IRQRESTORE(&dev->irq_lock, irqflags); + return; + } + callout_deactivate(&dev->vblank_disable_timer); + for (i = 0; i < dev->num_crtcs; i++) { - DRM_SPINLOCK_IRQSAVE(&dev->irq_lock, irqflags); if (atomic_read(&dev->vblank_refcount[i]) == 0 && dev->vblank_enabled[i]) { dev->driver.disable_vblank(dev, i); dev->vblank_enabled[i] = 0; } - DRM_SPINUNLOCK_IRQRESTORE(&dev->irq_lock, irqflags); } + DRM_SPINUNLOCK_IRQRESTORE(&dev->irq_lock, irqflags); } static void drm_vblank_cleanup(struct drm_device *dev) { + unsigned long irqflags; + /* Bail if the driver didn't call drm_vblank_init() */ if (dev->num_crtcs == 0) return; + DRM_SPINLOCK_IRQSAVE(&dev->irq_lock, irqflags); + callout_stop(&dev->vblank_disable_timer); + DRM_SPINUNLOCK_IRQRESTORE(&dev->irq_lock, irqflags); callout_drain(&dev->vblank_disable_timer); vblank_disable_fn((void *)dev); @@ -117,7 +134,7 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) { int i, ret = -ENOMEM; - callout_init(&dev->vblank_disable_timer, 0); + callout_init_mtx(&dev->vblank_disable_timer, &dev->irq_lock, 1); atomic_set(&dev->vbl_signal_pending, 0); dev->num_crtcs = num_crtcs; @@ -374,11 +391,15 @@ int drm_vblank_get(struct drm_device *dev, int crtc) void drm_vblank_put(struct drm_device *dev, int crtc) { + unsigned long irqflags; + + DRM_SPINLOCK_IRQSAVE(&dev->irq_lock, irqflags); /* Last user schedules interrupt disable */ atomic_subtract_acq_int(&dev->vblank_refcount[crtc], 1); if (dev->vblank_refcount[crtc] == 0) callout_reset(&dev->vblank_disable_timer, jiffies + 5*DRM_HZ, (timeout_t *)vblank_disable_fn, (void *)dev); + DRM_SPINUNLOCK_IRQRESTORE(&dev->irq_lock, irqflags); } int drm_modeset_ctl(struct drm_device *dev, void *data, -- cgit v1.2.3 From fc74c2e9d65dbd0c611e1610886df098c6e3273b Mon Sep 17 00:00:00 2001 From: Robert Noland Date: Mon, 2 Jun 2008 13:12:59 -0400 Subject: [FreeBSD] Go back to using vbl_lock and move init/destroy to load/unload. --- bsd-core/drm_irq.c | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) (limited to 'bsd-core/drm_irq.c') diff --git a/bsd-core/drm_irq.c b/bsd-core/drm_irq.c index b76e96dd..79f8f9ff 100644 --- a/bsd-core/drm_irq.c +++ b/bsd-core/drm_irq.c @@ -72,15 +72,15 @@ static void vblank_disable_fn(void *arg) unsigned long irqflags; int i; - DRM_SPINLOCK_IRQSAVE(&dev->irq_lock, irqflags); + DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); if (callout_pending(&dev->vblank_disable_timer)) { /* callout was reset */ - DRM_SPINUNLOCK_IRQRESTORE(&dev->irq_lock, irqflags); + DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); return; } if (!callout_active(&dev->vblank_disable_timer)) { /* callout was stopped */ - DRM_SPINUNLOCK_IRQRESTORE(&dev->irq_lock, irqflags); + DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); return; } callout_deactivate(&dev->vblank_disable_timer); @@ -92,7 +92,7 @@ static void vblank_disable_fn(void *arg) dev->vblank_enabled[i] = 0; } } - DRM_SPINUNLOCK_IRQRESTORE(&dev->irq_lock, irqflags); + DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); } static void drm_vblank_cleanup(struct drm_device *dev) @@ -103,9 +103,9 @@ static void drm_vblank_cleanup(struct drm_device *dev) if (dev->num_crtcs == 0) return; - DRM_SPINLOCK_IRQSAVE(&dev->irq_lock, irqflags); + DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); callout_stop(&dev->vblank_disable_timer); - DRM_SPINUNLOCK_IRQRESTORE(&dev->irq_lock, irqflags); + DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); callout_drain(&dev->vblank_disable_timer); vblank_disable_fn((void *)dev); @@ -134,7 +134,7 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) { int i, ret = -ENOMEM; - callout_init_mtx(&dev->vblank_disable_timer, &dev->irq_lock, 1); + callout_init_mtx(&dev->vblank_disable_timer, &dev->vbl_lock, 0); atomic_set(&dev->vbl_signal_pending, 0); dev->num_crtcs = num_crtcs; @@ -212,8 +212,6 @@ int drm_irq_install(struct drm_device *dev) dev->context_flag = 0; - DRM_SPININIT(&dev->irq_lock, "DRM IRQ lock"); - /* Before installing handler */ dev->driver.irq_preinstall(dev); DRM_UNLOCK(); @@ -268,7 +266,6 @@ err: dev->irqrid = 0; } #endif - DRM_SPINUNINIT(&dev->irq_lock); DRM_UNLOCK(); return retcode; } @@ -302,8 +299,6 @@ int drm_irq_uninstall(struct drm_device *dev) #endif drm_vblank_cleanup(dev); - DRM_SPINUNINIT(&dev->irq_lock); - return 0; } @@ -354,7 +349,7 @@ void drm_update_vblank_count(struct drm_device *dev, int crtc) * a long time. */ cur_vblank = dev->driver.get_vblank_counter(dev, crtc); - DRM_SPINLOCK_IRQSAVE(&dev->irq_lock, irqflags); + DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); if (cur_vblank < dev->last_vblank[crtc]) { diff = dev->max_vblank_count - dev->last_vblank[crtc]; @@ -363,7 +358,7 @@ void drm_update_vblank_count(struct drm_device *dev, int crtc) diff = cur_vblank - dev->last_vblank[crtc]; } dev->last_vblank[crtc] = cur_vblank; - DRM_SPINUNLOCK_IRQRESTORE(&dev->irq_lock, irqflags); + DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); atomic_add(diff, &dev->_vblank_count[crtc]); } @@ -373,7 +368,7 @@ int drm_vblank_get(struct drm_device *dev, int crtc) unsigned long irqflags; int ret = 0; - DRM_SPINLOCK_IRQSAVE(&dev->irq_lock, irqflags); + DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); /* Going from 0->1 means we have to enable interrupts again */ atomic_add_acq_int(&dev->vblank_refcount[crtc], 1); if (dev->vblank_refcount[crtc] == 1 && @@ -384,7 +379,7 @@ int drm_vblank_get(struct drm_device *dev, int crtc) else dev->vblank_enabled[crtc] = 1; } - DRM_SPINUNLOCK_IRQRESTORE(&dev->irq_lock, irqflags); + DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); return ret; } @@ -393,13 +388,13 @@ void drm_vblank_put(struct drm_device *dev, int crtc) { unsigned long irqflags; - DRM_SPINLOCK_IRQSAVE(&dev->irq_lock, irqflags); + DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); /* Last user schedules interrupt disable */ atomic_subtract_acq_int(&dev->vblank_refcount[crtc], 1); if (dev->vblank_refcount[crtc] == 0) callout_reset(&dev->vblank_disable_timer, jiffies + 5*DRM_HZ, (timeout_t *)vblank_disable_fn, (void *)dev); - DRM_SPINUNLOCK_IRQRESTORE(&dev->irq_lock, irqflags); + DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); } int drm_modeset_ctl(struct drm_device *dev, void *data, @@ -488,9 +483,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr vblwait->reply.sequence = atomic_read(&dev->vbl_received); - DRM_SPINLOCK(&dev->irq_lock); + DRM_SPINLOCK(&dev->vbl_lock); TAILQ_INSERT_HEAD(&dev->vbl_sig_list, vbl_sig, link); - DRM_SPINUNLOCK(&dev->irq_lock); + DRM_SPINUNLOCK(&dev->vbl_lock); ret = 0; #endif ret = EINVAL; -- cgit v1.2.3 From 93c57ff4e5d9f62be0a353222fef564dd9e59e39 Mon Sep 17 00:00:00 2001 From: Robert Noland Date: Mon, 2 Jun 2008 19:35:00 -0400 Subject: [FreeBSD] Remove the locks in the vblank_disable_fn They are recursive and causing panics with witness enabled. --- bsd-core/drm_irq.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'bsd-core/drm_irq.c') diff --git a/bsd-core/drm_irq.c b/bsd-core/drm_irq.c index 79f8f9ff..132d01bb 100644 --- a/bsd-core/drm_irq.c +++ b/bsd-core/drm_irq.c @@ -69,18 +69,14 @@ drm_irq_handler_wrap(DRM_IRQ_ARGS) static void vblank_disable_fn(void *arg) { struct drm_device *dev = (struct drm_device *)arg; - unsigned long irqflags; int i; - DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); if (callout_pending(&dev->vblank_disable_timer)) { /* callout was reset */ - DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); return; } if (!callout_active(&dev->vblank_disable_timer)) { /* callout was stopped */ - DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); return; } callout_deactivate(&dev->vblank_disable_timer); @@ -92,7 +88,6 @@ static void vblank_disable_fn(void *arg) dev->vblank_enabled[i] = 0; } } - DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); } static void drm_vblank_cleanup(struct drm_device *dev) @@ -106,6 +101,7 @@ static void drm_vblank_cleanup(struct drm_device *dev) DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); callout_stop(&dev->vblank_disable_timer); DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); + callout_drain(&dev->vblank_disable_timer); vblank_disable_fn((void *)dev); -- cgit v1.2.3 From 6d6921719c7d475856199ddbe88bbe11fc882ba6 Mon Sep 17 00:00:00 2001 From: Robert Noland Date: Tue, 3 Jun 2008 16:21:13 -0400 Subject: [FreeBSD] Incorporate vblank fixes for bsd. --- bsd-core/drm_irq.c | 93 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 31 deletions(-) (limited to 'bsd-core/drm_irq.c') diff --git a/bsd-core/drm_irq.c b/bsd-core/drm_irq.c index 132d01bb..c3ecd28b 100644 --- a/bsd-core/drm_irq.c +++ b/bsd-core/drm_irq.c @@ -120,15 +120,15 @@ static void drm_vblank_cleanup(struct drm_device *dev) DRM_MEM_DRIVER); drm_free(dev->vblank_premodeset, sizeof(*dev->vblank_premodeset) * dev->num_crtcs, DRM_MEM_DRIVER); - drm_free(dev->vblank_offset, sizeof(*dev->vblank_offset) * dev->num_crtcs, - DRM_MEM_DRIVER); + drm_free(dev->vblank_suspend, sizeof(*dev->vblank_suspend) * + dev->num_crtcs, DRM_MEM_DRIVER); dev->num_crtcs = 0; } int drm_vblank_init(struct drm_device *dev, int num_crtcs) { - int i, ret = -ENOMEM; + int i, ret = ENOMEM; callout_init_mtx(&dev->vblank_disable_timer, &dev->vbl_lock, 0); atomic_set(&dev->vbl_signal_pending, 0); @@ -168,8 +168,9 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) if (!dev->vblank_premodeset) goto err; - dev->vblank_offset = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER); - if (!dev->vblank_offset) + dev->vblank_suspend = drm_calloc(num_crtcs, sizeof(int), + DRM_MEM_DRIVER); + if (!dev->vblank_suspend) goto err; /* Zero per-crtc vblank stuff */ @@ -328,8 +329,7 @@ int drm_control(struct drm_device *dev, void *data, struct drm_file *file_priv) u32 drm_vblank_count(struct drm_device *dev, int crtc) { - return atomic_read(&dev->_vblank_count[crtc]) + - dev->vblank_offset[crtc]; + return atomic_read(&dev->_vblank_count[crtc]); } void drm_update_vblank_count(struct drm_device *dev, int crtc) @@ -337,6 +337,9 @@ void drm_update_vblank_count(struct drm_device *dev, int crtc) unsigned long irqflags; u32 cur_vblank, diff; + if (dev->vblank_suspend[crtc]) + return; + /* * Interrupts were disabled prior to this call, so deal with counter * wrap if needed. @@ -347,9 +350,16 @@ void drm_update_vblank_count(struct drm_device *dev, int crtc) cur_vblank = dev->driver.get_vblank_counter(dev, crtc); DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); if (cur_vblank < dev->last_vblank[crtc]) { - diff = dev->max_vblank_count - - dev->last_vblank[crtc]; - diff += cur_vblank; + if (cur_vblank == dev->last_vblank[crtc] - 1) { + diff = 0; + } else { + diff = dev->max_vblank_count - + dev->last_vblank[crtc]; + diff += cur_vblank; + } + + DRM_DEBUG("last_vblank[%d]=0x%x, cur_vblank=0x%x => diff=0x%x\n", + crtc, dev->last_vblank[crtc], cur_vblank, diff); } else { diff = cur_vblank - dev->last_vblank[crtc]; } @@ -398,11 +408,10 @@ int drm_modeset_ctl(struct drm_device *dev, void *data, { struct drm_modeset_ctl *modeset = data; int crtc, ret = 0; - u32 new; crtc = modeset->crtc; if (crtc >= dev->num_crtcs) { - ret = -EINVAL; + ret = EINVAL; goto out; } @@ -410,13 +419,28 @@ int drm_modeset_ctl(struct drm_device *dev, void *data, case _DRM_PRE_MODESET: dev->vblank_premodeset[crtc] = dev->driver.get_vblank_counter(dev, crtc); + dev->vblank_suspend[crtc] = 1; break; case _DRM_POST_MODESET: - new = dev->driver.get_vblank_counter(dev, crtc); - dev->vblank_offset[crtc] = dev->vblank_premodeset[crtc] - new; + if (dev->vblank_suspend[crtc]) { + u32 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]); + DRM_DEBUG("vblank_premodeset[%d]=0x%x, new=0x%x" + " => _vblank_count[%d]-=0x%x\n", crtc, + dev->vblank_premodeset[crtc], new, + crtc, dev->max_vblank_count + new - + dev->vblank_premodeset[crtc]); + } + } + dev->vblank_suspend[crtc] = 0; break; default: - ret = -EINVAL; + ret = EINVAL; break; } @@ -427,7 +451,6 @@ out: int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_wait_vblank_t *vblwait = data; - struct timeval now; int ret = 0; int flags, seq, crtc; @@ -473,6 +496,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr if (vbl_sig == NULL) return ENOMEM; + if (dev->vblank_suspend[crtc]) + return EBUSY; + vbl_sig->sequence = vblwait->request.sequence; vbl_sig->signo = vblwait->request.signal; vbl_sig->pid = DRM_CURRENTPID; @@ -486,23 +512,28 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr #endif ret = EINVAL; } else { - unsigned long cur_vblank; - - DRM_LOCK(); - /* shared code returns -errno */ - - 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)) + if (!dev->vblank_suspend[crtc]) { + DRM_LOCK(); + /* shared code returns -errno */ + + ret = drm_vblank_get(dev, crtc); + if (ret) + return ret; + DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ, + ((drm_vblank_count(dev, crtc) - vblwait->request.sequence) <= (1 << 23))); - drm_vblank_put(dev, crtc); - DRM_UNLOCK(); + drm_vblank_put(dev, crtc); + DRM_UNLOCK(); + } - microtime(&now); - vblwait->reply.tval_sec = now.tv_sec; - vblwait->reply.tval_usec = now.tv_usec; + if (ret != EINTR) { + struct timeval now; + + microtime(&now); + vblwait->reply.tval_sec = now.tv_sec; + vblwait->reply.tval_usec = now.tv_usec; + vblwait->reply.sequence = drm_vblank_count(dev, crtc); + } } return ret; -- cgit v1.2.3 From 74cf1f91be7f4139601624af0343e3d411190dec Mon Sep 17 00:00:00 2001 From: Owain Gordon Ainsworth Date: Mon, 7 Jul 2008 17:23:48 +0100 Subject: BSD: change drm_locked_task*() to use the same scheme as linux. The current code can sleep in an interrupt handler, that is bad. So instead if we can't grab the lock, flag it and run the tasklet on unlock. Signed-off-by: Robert Noland --- bsd-core/drm_irq.c | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) (limited to 'bsd-core/drm_irq.c') diff --git a/bsd-core/drm_irq.c b/bsd-core/drm_irq.c index c3ecd28b..a066cfc9 100644 --- a/bsd-core/drm_irq.c +++ b/bsd-core/drm_irq.c @@ -578,41 +578,42 @@ static void drm_locked_task(void *context, int pending __unused) { struct drm_device *dev = context; - DRM_LOCK(); - for (;;) { - int ret; - - if (drm_lock_take(&dev->lock.hw_lock->lock, - DRM_KERNEL_CONTEXT)) - { - dev->lock.file_priv = NULL; /* kernel owned */ - dev->lock.lock_time = jiffies; - atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); - break; /* Got lock */ - } + DRM_SPINLOCK(&dev->tsk_lock); - /* Contention */ -#if defined(__FreeBSD__) && __FreeBSD_version > 500000 - ret = mtx_sleep((void *)&dev->lock.lock_queue, &dev->dev_lock, - PZERO | PCATCH, "drmlk2", 0); -#else - ret = tsleep((void *)&dev->lock.lock_queue, PZERO | PCATCH, - "drmlk2", 0); -#endif - if (ret != 0) - return; + DRM_LOCK(); /* XXX drm_lock_take() should do it's own locking */ + if (dev->locked_task_call == NULL || + drm_lock_take(&dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT) == 0) { + DRM_UNLOCK(); + DRM_SPINUNLOCK(&dev->tsk_lock); + return; } + + dev->lock.file_priv = NULL; /* kernel owned */ + dev->lock.lock_time = jiffies; + atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); + DRM_UNLOCK(); dev->locked_task_call(dev); drm_lock_free(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT); + + dev->locked_task_call = NULL; + + DRM_SPINUNLOCK(&dev->tsk_lock); } void drm_locked_tasklet(struct drm_device *dev, void (*tasklet)(struct drm_device *dev)) { + DRM_SPINLOCK(&dev->tsk_lock); + if (dev->locked_task_call != NULL) { + DRM_SPINUNLOCK(&dev->tsk_lock); + return; + } + dev->locked_task_call = tasklet; + DRM_SPINUNLOCK(&dev->tsk_lock); taskqueue_enqueue(taskqueue_swi, &dev->locked_task); } -- cgit v1.2.3 From 480c317a6ae634d777931eee54fadd2a50a2f650 Mon Sep 17 00:00:00 2001 From: Robert Noland Date: Thu, 17 Jul 2008 14:01:04 -0400 Subject: [FreeBSD] drm_irq.c updates for vblank fixes. --- bsd-core/drm_irq.c | 58 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 23 deletions(-) (limited to 'bsd-core/drm_irq.c') diff --git a/bsd-core/drm_irq.c b/bsd-core/drm_irq.c index a066cfc9..4b515ad8 100644 --- a/bsd-core/drm_irq.c +++ b/bsd-core/drm_irq.c @@ -81,9 +81,15 @@ static void vblank_disable_fn(void *arg) } callout_deactivate(&dev->vblank_disable_timer); + if (!dev->vblank_disable_allowed) + return; + for (i = 0; i < dev->num_crtcs; i++) { if (atomic_read(&dev->vblank_refcount[i]) == 0 && dev->vblank_enabled[i]) { + DRM_DEBUG("disabling vblank on crtc %d\n", i); + dev->last_vblank[i] = + dev->driver.get_vblank_counter(dev, i); dev->driver.disable_vblank(dev, i); dev->vblank_enabled[i] = 0; } @@ -181,6 +187,8 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) atomic_set(&dev->vblank_refcount[i], 0); } + dev->vblank_disable_allowed = 0; + return 0; err: @@ -334,7 +342,6 @@ u32 drm_vblank_count(struct drm_device *dev, int crtc) void drm_update_vblank_count(struct drm_device *dev, int crtc) { - unsigned long irqflags; u32 cur_vblank, diff; if (dev->vblank_suspend[crtc]) @@ -348,7 +355,6 @@ void drm_update_vblank_count(struct drm_device *dev, int crtc) * a long time. */ cur_vblank = dev->driver.get_vblank_counter(dev, crtc); - DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); if (cur_vblank < dev->last_vblank[crtc]) { if (cur_vblank == dev->last_vblank[crtc] - 1) { diff = 0; @@ -364,7 +370,9 @@ void drm_update_vblank_count(struct drm_device *dev, int crtc) diff = cur_vblank - dev->last_vblank[crtc]; } dev->last_vblank[crtc] = cur_vblank; - DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); + + DRM_DEBUG("enabling vblank interrupts on crtc %d, missed %d\n", + crtc, diff); atomic_add(diff, &dev->_vblank_count[crtc]); } @@ -382,8 +390,10 @@ int drm_vblank_get(struct drm_device *dev, int crtc) ret = dev->driver.enable_vblank(dev, crtc); if (ret) atomic_dec(&dev->vblank_refcount[crtc]); - else + else { dev->vblank_enabled[crtc] = 1; + drm_update_vblank_count(dev, crtc); + } } DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); @@ -407,6 +417,8 @@ int drm_modeset_ctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_modeset_ctl *modeset = data; + unsigned long irqflags; + u32 new, diff; int crtc, ret = 0; crtc = modeset->crtc; @@ -417,27 +429,28 @@ int drm_modeset_ctl(struct drm_device *dev, void *data, switch (modeset->cmd) { case _DRM_PRE_MODESET: - dev->vblank_premodeset[crtc] = - dev->driver.get_vblank_counter(dev, crtc); - dev->vblank_suspend[crtc] = 1; + DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); + dev->vblank_disable_allowed = 1; + if (!dev->vblank_enabled[crtc]) { + dev->vblank_premodeset[crtc] = + dev->driver.get_vblank_counter(dev, crtc); + dev->vblank_suspend[crtc] = 1; + } + DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); break; case _DRM_POST_MODESET: - if (dev->vblank_suspend[crtc]) { - u32 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]); - DRM_DEBUG("vblank_premodeset[%d]=0x%x, new=0x%x" - " => _vblank_count[%d]-=0x%x\n", crtc, - dev->vblank_premodeset[crtc], new, - crtc, dev->max_vblank_count + new - - dev->vblank_premodeset[crtc]); - } + DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); + dev->vblank_disable_allowed = 1; + new = dev->driver.get_vblank_counter(dev, crtc); + if (dev->vblank_suspend[crtc] && !dev->vblank_enabled[crtc]) { + if (new > dev->vblank_premodeset[crtc]) + diff = dev->vblank_premodeset[crtc] - new; + else + diff = new; + atomic_add(diff, &dev->_vblank_count[crtc]); } dev->vblank_suspend[crtc] = 0; + DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); break; default: ret = EINVAL; @@ -471,7 +484,6 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr if (crtc >= dev->num_crtcs) return EINVAL; - drm_update_vblank_count(dev, crtc); seq = drm_vblank_count(dev, crtc); switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) { @@ -569,7 +581,7 @@ void 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); } -- cgit v1.2.3 From 2580a065d81be645a14af1e91b8441f7e72fcbe4 Mon Sep 17 00:00:00 2001 From: Robert Noland Date: Thu, 24 Jul 2008 00:21:00 -0400 Subject: [FreeBSD] Catch up to linux on vblank-rework --- bsd-core/drm_irq.c | 102 +++++++++++++++++++++-------------------------------- 1 file changed, 41 insertions(+), 61 deletions(-) (limited to 'bsd-core/drm_irq.c') diff --git a/bsd-core/drm_irq.c b/bsd-core/drm_irq.c index 4b515ad8..6e21d41c 100644 --- a/bsd-core/drm_irq.c +++ b/bsd-core/drm_irq.c @@ -124,9 +124,7 @@ static void drm_vblank_cleanup(struct drm_device *dev) dev->num_crtcs, DRM_MEM_DRIVER); drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs, DRM_MEM_DRIVER); - drm_free(dev->vblank_premodeset, sizeof(*dev->vblank_premodeset) * - dev->num_crtcs, DRM_MEM_DRIVER); - drm_free(dev->vblank_suspend, sizeof(*dev->vblank_suspend) * + drm_free(dev->vblank_inmodeset, sizeof(*dev->vblank_inmodeset) * dev->num_crtcs, DRM_MEM_DRIVER); dev->num_crtcs = 0; @@ -169,14 +167,9 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) if (!dev->last_vblank) goto err; - dev->vblank_premodeset = drm_calloc(num_crtcs, sizeof(u32), + dev->vblank_inmodeset = drm_calloc(num_crtcs, sizeof(int), DRM_MEM_DRIVER); - if (!dev->vblank_premodeset) - goto err; - - dev->vblank_suspend = drm_calloc(num_crtcs, sizeof(int), - DRM_MEM_DRIVER); - if (!dev->vblank_suspend) + if (!dev->vblank_inmodeset) goto err; /* Zero per-crtc vblank stuff */ @@ -340,13 +333,10 @@ u32 drm_vblank_count(struct drm_device *dev, int crtc) return atomic_read(&dev->_vblank_count[crtc]); } -void drm_update_vblank_count(struct drm_device *dev, int crtc) +static void drm_update_vblank_count(struct drm_device *dev, int crtc) { u32 cur_vblank, diff; - if (dev->vblank_suspend[crtc]) - return; - /* * Interrupts were disabled prior to this call, so deal with counter * wrap if needed. @@ -355,21 +345,13 @@ void drm_update_vblank_count(struct drm_device *dev, int crtc) * a long time. */ cur_vblank = dev->driver.get_vblank_counter(dev, crtc); + diff = cur_vblank - dev->last_vblank[crtc]; 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; - } + diff += dev->max_vblank_count; DRM_DEBUG("last_vblank[%d]=0x%x, cur_vblank=0x%x => diff=0x%x\n", crtc, dev->last_vblank[crtc], cur_vblank, diff); - } else { - diff = cur_vblank - dev->last_vblank[crtc]; } - dev->last_vblank[crtc] = cur_vblank; DRM_DEBUG("enabling vblank interrupts on crtc %d, missed %d\n", crtc, diff); @@ -408,7 +390,7 @@ void drm_vblank_put(struct drm_device *dev, int crtc) /* Last user schedules interrupt disable */ atomic_subtract_acq_int(&dev->vblank_refcount[crtc], 1); if (dev->vblank_refcount[crtc] == 0) - callout_reset(&dev->vblank_disable_timer, jiffies + 5*DRM_HZ, + callout_reset(&dev->vblank_disable_timer, 5 * DRM_HZ, (timeout_t *)vblank_disable_fn, (void *)dev); DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); } @@ -418,39 +400,40 @@ int drm_modeset_ctl(struct drm_device *dev, void *data, { struct drm_modeset_ctl *modeset = data; unsigned long irqflags; - u32 new, diff; int crtc, ret = 0; + /* If drm_vblank_init() hasn't been called yet, just no-op */ + if (!dev->num_crtcs) + goto out; + crtc = modeset->crtc; if (crtc >= dev->num_crtcs) { ret = EINVAL; goto out; } + /* + * To avoid all the problems that might happen if interrupts + * were enabled/disabled around or between these calls, we just + * have the kernel take a reference on the CRTC (just once though + * to avoid corrupting the count if multiple, mismatch calls occur), + * so that interrupts remain enabled in the interim. + */ switch (modeset->cmd) { case _DRM_PRE_MODESET: - DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); - dev->vblank_disable_allowed = 1; - if (!dev->vblank_enabled[crtc]) { - dev->vblank_premodeset[crtc] = - dev->driver.get_vblank_counter(dev, crtc); - dev->vblank_suspend[crtc] = 1; + if (!dev->vblank_inmodeset[crtc]) { + dev->vblank_inmodeset[crtc] = 1; + drm_vblank_get(dev, crtc); } - DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); break; case _DRM_POST_MODESET: - DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); - dev->vblank_disable_allowed = 1; - new = dev->driver.get_vblank_counter(dev, crtc); - if (dev->vblank_suspend[crtc] && !dev->vblank_enabled[crtc]) { - if (new > dev->vblank_premodeset[crtc]) - diff = dev->vblank_premodeset[crtc] - new; - else - diff = new; - atomic_add(diff, &dev->_vblank_count[crtc]); + if (dev->vblank_inmodeset[crtc]) { + DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); + dev->vblank_disable_allowed = 1; + dev->vblank_inmodeset[crtc] = 0; + DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); + drm_vblank_put(dev, crtc); } - dev->vblank_suspend[crtc] = 0; - DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); break; default: ret = EINVAL; @@ -484,6 +467,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr if (crtc >= dev->num_crtcs) return EINVAL; + ret = drm_vblank_get(dev, crtc); + if (ret) + return ret; seq = drm_vblank_count(dev, crtc); switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) { @@ -493,7 +479,8 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr case _DRM_VBLANK_ABSOLUTE: break; default: - return EINVAL; + ret = EINVAL; + goto done; } if ((flags & _DRM_VBLANK_NEXTONMISS) && @@ -508,9 +495,6 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr if (vbl_sig == NULL) return ENOMEM; - if (dev->vblank_suspend[crtc]) - return EBUSY; - vbl_sig->sequence = vblwait->request.sequence; vbl_sig->signo = vblwait->request.signal; vbl_sig->pid = DRM_CURRENTPID; @@ -524,19 +508,13 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr #endif ret = EINVAL; } else { - if (!dev->vblank_suspend[crtc]) { - DRM_LOCK(); - /* shared code returns -errno */ - - ret = drm_vblank_get(dev, crtc); - if (ret) - return ret; - DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ, - ((drm_vblank_count(dev, crtc) - - vblwait->request.sequence) <= (1 << 23))); - drm_vblank_put(dev, crtc); - DRM_UNLOCK(); - } + DRM_LOCK(); + /* shared code returns -errno */ + + DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ, + ((drm_vblank_count(dev, crtc) + - vblwait->request.sequence) <= (1 << 23))); + DRM_UNLOCK(); if (ret != EINTR) { struct timeval now; @@ -548,6 +526,8 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr } } +done: + drm_vblank_put(dev, crtc); return ret; } -- cgit v1.2.3