From patchwork Mon Dec 20 19:00:54 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Lutomirski X-Patchwork-Id: 423091 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id oBL158SW031378 for ; Tue, 21 Dec 2010 01:05:36 GMT Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 2DB2D9F056 for ; Mon, 20 Dec 2010 11:07:23 -0800 (PST) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Tue, 21 Dec 2010 01:05:36 +0000 (UTC) X-Greylist: delayed 301 seconds by postgrey-1.31 at gabe; Mon, 20 Dec 2010 11:06:11 PST Received: from dmz-mailsec-scanner-3.mit.edu (DMZ-MAILSEC-SCANNER-3.MIT.EDU [18.9.25.14]) by gabe.freedesktop.org (Postfix) with ESMTP id 171829E759; Mon, 20 Dec 2010 11:06:10 -0800 (PST) X-AuditID: 1209190e-b7b3bae000000a71-31-4d0fa7f5456f Received: from mailhub-auth-3.mit.edu ( [18.9.21.43]) by dmz-mailsec-scanner-3.mit.edu (Symantec Brightmail Gateway) with SMTP id 96.9F.02673.5F7AF0D4; Mon, 20 Dec 2010 14:01:09 -0500 (EST) Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103]) by mailhub-auth-3.mit.edu (8.13.8/8.9.2) with ESMTP id oBKJ18KI016160; Mon, 20 Dec 2010 14:01:08 -0500 Received: from localhost ([18.111.50.232]) (authenticated bits=0) (User authenticated as luto@ATHENA.MIT.EDU) by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id oBKJ125Y007715 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES128-SHA bits=128 verify=NOT); Mon, 20 Dec 2010 14:01:06 -0500 (EST) From: Andy Lutomirski To: Jesse Barnes , Chris Wilson , David Airlie Date: Mon, 20 Dec 2010 14:00:54 -0500 Message-Id: <372cb262d2d49bd796fa6636d04d431b50a14f69.1292869603.git.luto@mit.edu> X-Mailer: git-send-email 1.7.3.3 X-Brightmail-Tracker: AAAAAA== Cc: intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org Subject: [Intel-gfx] [PATCH] drm: Aggressively disable vblanks X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.11 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: intel-gfx-bounces+patchwork-intel-gfx=patchwork.kernel.org@lists.freedesktop.org Errors-To: intel-gfx-bounces+patchwork-intel-gfx=patchwork.kernel.org@lists.freedesktop.org diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 9d3a503..2f107c5 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -77,45 +77,59 @@ int drm_irq_by_busid(struct drm_device *dev, void *data, return 0; } -static void vblank_disable_fn(unsigned long arg) +/* After VBLANK_CONSEC_THRESHOLD consecutive non-ignored vblank interrupts, + * vblanks will be left on. */ +#define VBLANK_CONSEC_THRESHOLD 3 + +static void __vblank_disable_now(struct drm_device *dev, int crtc, int force) +{ + if (!dev->vblank_disable_allowed) + return; + + if (atomic_read(&dev->vblank_refcount[crtc]) == 0 && dev->vblank_enabled[crtc] && + (dev->vblank_consecutive[crtc] < VBLANK_CONSEC_THRESHOLD || force)) + { + DRM_DEBUG("disabling vblank on crtc %d\n", crtc); + dev->last_vblank[crtc] = + dev->driver->get_vblank_counter(dev, crtc); + dev->driver->disable_vblank(dev, crtc); + dev->vblank_enabled[crtc] = 0; + if (force) + dev->vblank_consecutive[crtc] = 0; + } +} + +static void vblank_disable_now(struct drm_device *dev, int crtc, int force) { - struct drm_device *dev = (struct drm_device *)arg; unsigned long irqflags; - int i; if (!dev->vblank_disable_allowed) return; - for (i = 0; i < dev->num_crtcs; i++) { - spin_lock_irqsave(&dev->vbl_lock, irqflags); - 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; - } - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); - } + spin_lock_irqsave(&dev->vbl_lock, irqflags); + __vblank_disable_now(dev, crtc, force); + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); } void drm_vblank_cleanup(struct drm_device *dev) { + int i; + /* Bail if the driver didn't call drm_vblank_init() */ if (dev->num_crtcs == 0) return; - del_timer(&dev->vblank_disable_timer); - - vblank_disable_fn((unsigned long)dev); + for (i = 0; i < dev->num_crtcs; i++) + vblank_disable_now(dev, i, 1); kfree(dev->vbl_queue); kfree(dev->_vblank_count); kfree(dev->vblank_refcount); kfree(dev->vblank_enabled); kfree(dev->last_vblank); + kfree(dev->last_vblank_enable); kfree(dev->last_vblank_wait); + kfree(dev->vblank_consecutive); kfree(dev->vblank_inmodeset); dev->num_crtcs = 0; @@ -126,8 +140,6 @@ 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); dev->num_crtcs = num_crtcs; @@ -153,10 +165,18 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) if (!dev->last_vblank) goto err; + dev->last_vblank_enable = kcalloc(num_crtcs, sizeof(u32), GFP_KERNEL); + if (!dev->last_vblank) + goto err; + dev->last_vblank_wait = kcalloc(num_crtcs, sizeof(u32), GFP_KERNEL); if (!dev->last_vblank_wait) goto err; + dev->vblank_consecutive = kcalloc(num_crtcs, sizeof(int), GFP_KERNEL); + if (!dev->vblank_consecutive) + goto err; + dev->vblank_inmodeset = kcalloc(num_crtcs, sizeof(int), GFP_KERNEL); if (!dev->vblank_inmodeset) goto err; @@ -387,10 +407,12 @@ EXPORT_SYMBOL(drm_vblank_count); * Only necessary when going from off->on, to account for frames we * didn't get an interrupt for. * + * Returns the number of vblanks missed. + * * Note: caller must hold dev->vbl_lock since this reads & writes * device vblank fields. */ -static void drm_update_vblank_count(struct drm_device *dev, int crtc) +static u32 drm_update_vblank_count(struct drm_device *dev, int crtc) { u32 cur_vblank, diff; @@ -414,6 +436,17 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc) crtc, diff); atomic_add(diff, &dev->_vblank_count[crtc]); + return diff; +} + +static void vblank_enabled_consecutively(struct drm_device *dev, int crtc) +{ + if (dev->vblank_consecutive[crtc] < VBLANK_CONSEC_THRESHOLD) { + dev->vblank_consecutive[crtc]++; + if (dev->vblank_consecutive[crtc] == VBLANK_CONSEC_THRESHOLD) + DRM_DEBUG("consecutive vblank usage on crtc %d\n", crtc); + } + } /** @@ -433,7 +466,6 @@ int drm_vblank_get(struct drm_device *dev, int crtc) int ret = 0; spin_lock_irqsave(&dev->vbl_lock, irqflags); - /* Going from 0->1 means we have to enable interrupts again */ if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1) { if (!dev->vblank_enabled[crtc]) { ret = dev->driver->enable_vblank(dev, crtc); @@ -442,7 +474,12 @@ int 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); + if (drm_update_vblank_count(dev, crtc) == 0) + vblank_enabled_consecutively(dev, crtc); + else + dev->vblank_consecutive[crtc] = 0; + dev->last_vblank_enable[crtc] = + drm_vblank_count(dev, crtc); } } } else { @@ -467,11 +504,18 @@ EXPORT_SYMBOL(drm_vblank_get); */ void drm_vblank_put(struct drm_device *dev, int crtc) { + unsigned long irqflags; + BUG_ON (atomic_read (&dev->vblank_refcount[crtc]) == 0); - /* Last user schedules interrupt disable */ - if (atomic_dec_and_test(&dev->vblank_refcount[crtc])) - mod_timer(&dev->vblank_disable_timer, jiffies + 5*DRM_HZ); + if (atomic_dec_and_test(&dev->vblank_refcount[crtc])) { + spin_lock_irqsave(&dev->vbl_lock, irqflags); + if (atomic_read(&dev->_vblank_count[crtc]) != + dev->last_vblank_enable[crtc] + && dev->vblank_consecutive[crtc] < VBLANK_CONSEC_THRESHOLD) + __vblank_disable_now(dev, crtc, 0); + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + } } EXPORT_SYMBOL(drm_vblank_put); @@ -780,11 +824,18 @@ void drm_handle_vblank_events(struct drm_device *dev, int crtc) */ void drm_handle_vblank(struct drm_device *dev, int crtc) { + int vblank_refcount; if (!dev->num_crtcs) return; + vblank_refcount = atomic_read(&dev->vblank_refcount[crtc]); atomic_inc(&dev->_vblank_count[crtc]); DRM_WAKEUP(&dev->vbl_queue[crtc]); drm_handle_vblank_events(dev, crtc); + + if (vblank_refcount == 0) { + /* This interrupt was unnecessary. */ + vblank_disable_now(dev, crtc, 1); + } } EXPORT_SYMBOL(drm_handle_vblank); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 4c9461a..c15918b 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -997,11 +997,14 @@ struct drm_device { atomic_t *vblank_refcount; /* number of users of vblank interruptsper crtc */ u32 *last_vblank; /* protected by dev->vbl_lock, used */ /* for wraparound handling */ + u32 *last_vblank_enable; /* protected by dev->vbl_lock, used */ + /* for early disable path */ int *vblank_enabled; /* so we don't call enable more than once per disable */ int *vblank_inmodeset; /* Display driver is setting mode */ u32 *last_vblank_wait; /* Last vblank seqno waited per CRTC */ - struct timer_list vblank_disable_timer; + int *vblank_consecutive; /* protected by dev->vbl_lock, counts + consecutive interesting vblanks */ u32 max_vblank_count; /**< size of vblank counter register */