From: Ville Syrjälä <ville.syrj...@linux.intel.com>

In order to do the FBC tracking properly for execbuffer, we need to
figure out if the object being rendered to is the current front buffer.
However in order to do that purely based on the crtc, we'd need to
grab crtc->mutex, but we can't since we're already holding struct_mutex.

So let's keep the current fb around in dev_priv->fbc.fb and consult that
one. We're holding a reference to it for the entire time FBC is enabled,
so there's no danger of it disappearing while we're looking at it.
Assuming FBC updates always happens while holding struct_mutex that is.

Also change the delayed enable mechanism to grab a reference to the fb.

Signed-off-by: Ville Syrjälä <ville.syrj...@linux.intel.com>
---
 drivers/gpu/drm/i915/i915_drv.h |  6 ++--
 drivers/gpu/drm/i915/intel_pm.c | 63 ++++++++++++++++++++++++++++-------------
 2 files changed, 47 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index c842928..42a4375 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -352,7 +352,9 @@ struct dpll;
 
 struct drm_i915_display_funcs {
        bool (*fbc_enabled)(struct drm_device *dev);
-       void (*enable_fbc)(struct drm_crtc *crtc, unsigned long interval);
+       void (*enable_fbc)(struct drm_crtc *crtc,
+                          struct drm_framebuffer *fb,
+                          unsigned long interval);
        void (*disable_fbc)(struct drm_device *dev);
        int (*get_display_clock_speed)(struct drm_device *dev);
        int (*get_fifo_size)(struct drm_device *dev, int plane);
@@ -652,7 +654,7 @@ struct i915_hw_context {
 
 struct i915_fbc {
        unsigned long size;
-       unsigned int fb_id;
+       struct drm_framebuffer *fb;
        enum plane plane;
        int y;
 
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index dc306e1..8780b87 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -86,11 +86,12 @@ static void i8xx_disable_fbc(struct drm_device *dev)
        DRM_DEBUG_KMS("disabled FBC\n");
 }
 
-static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
+static void i8xx_enable_fbc(struct drm_crtc *crtc,
+                           struct drm_framebuffer *fb,
+                           unsigned long interval)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct drm_framebuffer *fb = crtc->fb;
        struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
        struct drm_i915_gem_object *obj = intel_fb->obj;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
@@ -136,11 +137,12 @@ static bool i8xx_fbc_enabled(struct drm_device *dev)
        return I915_READ(FBC_CONTROL) & FBC_CTL_EN;
 }
 
-static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
+static void g4x_enable_fbc(struct drm_crtc *crtc,
+                          struct drm_framebuffer *fb,
+                          unsigned long interval)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct drm_framebuffer *fb = crtc->fb;
        struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
        struct drm_i915_gem_object *obj = intel_fb->obj;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
@@ -205,11 +207,12 @@ static void sandybridge_blit_fbc_update(struct drm_device 
*dev)
        gen6_gt_force_wake_put(dev_priv);
 }
 
-static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
+static void ironlake_enable_fbc(struct drm_crtc *crtc,
+                               struct drm_framebuffer *fb,
+                               unsigned long interval)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct drm_framebuffer *fb = crtc->fb;
        struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
        struct drm_i915_gem_object *obj = intel_fb->obj;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
@@ -265,11 +268,12 @@ static bool ironlake_fbc_enabled(struct drm_device *dev)
        return I915_READ(ILK_DPFC_CONTROL) & DPFC_CTL_EN;
 }
 
-static void gen7_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
+static void gen7_enable_fbc(struct drm_crtc *crtc,
+                           struct drm_framebuffer *fb,
+                           unsigned long interval)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct drm_framebuffer *fb = crtc->fb;
        struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
        struct drm_i915_gem_object *obj = intel_fb->obj;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
@@ -308,6 +312,22 @@ bool intel_fbc_enabled(struct drm_device *dev)
        return dev_priv->display.fbc_enabled(dev);
 }
 
+static void intel_setup_fbc(struct drm_crtc *crtc,
+                           struct drm_framebuffer *fb,
+                           unsigned long interval)
+{
+       struct drm_i915_private *dev_priv = crtc->dev->dev_private;
+
+       dev_priv->display.enable_fbc(crtc, fb, interval);
+
+       dev_priv->fbc.plane = to_intel_crtc(crtc)->plane;
+       drm_framebuffer_reference(fb);
+       if (dev_priv->fbc.fb)
+               drm_framebuffer_unreference(dev_priv->fbc.fb);
+       dev_priv->fbc.fb = fb;
+       dev_priv->fbc.y = crtc->y;
+}
+
 static void intel_fbc_work_fn(struct work_struct *__work)
 {
        struct intel_fbc_work *work =
@@ -321,19 +341,14 @@ static void intel_fbc_work_fn(struct work_struct *__work)
                /* Double check that we haven't switched fb without cancelling
                 * the prior work.
                 */
-               if (work->crtc->fb == work->fb) {
-                       dev_priv->display.enable_fbc(work->crtc,
-                                                    work->interval);
-
-                       dev_priv->fbc.plane = to_intel_crtc(work->crtc)->plane;
-                       dev_priv->fbc.fb_id = work->crtc->fb->base.id;
-                       dev_priv->fbc.y = work->crtc->y;
-               }
-
+               if (work->crtc->fb == work->fb)
+                       intel_setup_fbc(work->crtc, work->fb, work->interval);
                dev_priv->fbc.fbc_work = NULL;
        }
        mutex_unlock(&dev->struct_mutex);
 
+       drm_framebuffer_reference(work->fb);
+
        kfree(work);
 }
 
@@ -348,9 +363,11 @@ static void intel_cancel_fbc_work(struct drm_i915_private 
*dev_priv)
         * dev_priv->fbc.fbc_work, so we can perform the cancellation
         * entirely asynchronously.
         */
-       if (cancel_delayed_work(&dev_priv->fbc.fbc_work->work))
+       if (cancel_delayed_work(&dev_priv->fbc.fbc_work->work)) {
                /* tasklet was killed before being run, clean up */
+               drm_framebuffer_unreference(dev_priv->fbc.fbc_work->fb);
                kfree(dev_priv->fbc.fbc_work);
+       }
 
        /* Mark the work as no longer wanted so that if it does
         * wake-up (because the work was already running and waiting
@@ -374,7 +391,7 @@ static void intel_enable_fbc(struct drm_crtc *crtc, 
unsigned long interval)
        work = kzalloc(sizeof(*work), GFP_KERNEL);
        if (work == NULL) {
                DRM_ERROR("Failed to allocate FBC work structure\n");
-               dev_priv->display.enable_fbc(crtc, interval);
+               intel_setup_fbc(crtc, crtc->fb, interval);
                return;
        }
 
@@ -383,6 +400,8 @@ static void intel_enable_fbc(struct drm_crtc *crtc, 
unsigned long interval)
        work->interval = interval;
        INIT_DELAYED_WORK(&work->work, intel_fbc_work_fn);
 
+       drm_framebuffer_reference(work->fb);
+
        dev_priv->fbc.fbc_work = work;
 
        /* Delay the actual enabling to let pageflipping cease and the
@@ -412,6 +431,10 @@ void intel_disable_fbc(struct drm_device *dev)
 
        dev_priv->display.disable_fbc(dev);
        dev_priv->fbc.plane = -1;
+       if (dev_priv->fbc.fb) {
+               drm_framebuffer_unreference(dev_priv->fbc.fb);
+               dev_priv->fbc.fb = NULL;
+       }
 }
 
 static bool set_no_fbc_reason(struct drm_i915_private *dev_priv,
@@ -563,7 +586,7 @@ void intel_update_fbc(struct drm_device *dev)
         * without first being decoupled from the scanout and FBC disabled.
         */
        if (dev_priv->fbc.plane == intel_crtc->plane &&
-           dev_priv->fbc.fb_id == fb->base.id &&
+           dev_priv->fbc.fb == fb &&
            dev_priv->fbc.y == crtc->y)
                return;
 
-- 
1.8.1.5

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to