diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_sprite.c')
| -rw-r--r-- | drivers/gpu/drm/i915/intel_sprite.c | 488 |
1 files changed, 349 insertions, 139 deletions
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index ad6ec4b3900..9a17b4e92ef 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -37,6 +37,106 @@ #include <drm/i915_drm.h> #include "i915_drv.h" +static int usecs_to_scanlines(const struct drm_display_mode *mode, int usecs) +{ + /* paranoia */ + if (!mode->crtc_htotal) + return 1; + + return DIV_ROUND_UP(usecs * mode->crtc_clock, 1000 * mode->crtc_htotal); +} + +static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl_count) +{ + struct drm_device *dev = crtc->base.dev; + const struct drm_display_mode *mode = &crtc->config.adjusted_mode; + enum pipe pipe = crtc->pipe; + long timeout = msecs_to_jiffies_timeout(1); + int scanline, min, max, vblank_start; + DEFINE_WAIT(wait); + + WARN_ON(!drm_modeset_is_locked(&crtc->base.mutex)); + + vblank_start = mode->crtc_vblank_start; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + vblank_start = DIV_ROUND_UP(vblank_start, 2); + + /* FIXME needs to be calibrated sensibly */ + min = vblank_start - usecs_to_scanlines(mode, 100); + max = vblank_start - 1; + + if (min <= 0 || max <= 0) + return false; + + if (WARN_ON(drm_vblank_get(dev, pipe))) + return false; + + local_irq_disable(); + + trace_i915_pipe_update_start(crtc, min, max); + + for (;;) { + /* + * prepare_to_wait() has a memory barrier, which guarantees + * other CPUs can see the task state update by the time we + * read the scanline. + */ + prepare_to_wait(&crtc->vbl_wait, &wait, TASK_UNINTERRUPTIBLE); + + scanline = intel_get_crtc_scanline(crtc); + if (scanline < min || scanline > max) + break; + + if (timeout <= 0) { + DRM_ERROR("Potential atomic update failure on pipe %c\n", + pipe_name(crtc->pipe)); + break; + } + + local_irq_enable(); + + timeout = schedule_timeout(timeout); + + local_irq_disable(); + } + + finish_wait(&crtc->vbl_wait, &wait); + + drm_vblank_put(dev, pipe); + + *start_vbl_count = dev->driver->get_vblank_counter(dev, pipe); + + trace_i915_pipe_update_vblank_evaded(crtc, min, max, *start_vbl_count); + + return true; +} + +static void intel_pipe_update_end(struct intel_crtc *crtc, u32 start_vbl_count) +{ + struct drm_device *dev = crtc->base.dev; + enum pipe pipe = crtc->pipe; + u32 end_vbl_count = dev->driver->get_vblank_counter(dev, pipe); + + trace_i915_pipe_update_end(crtc, end_vbl_count); + + local_irq_enable(); + + if (start_vbl_count != end_vbl_count) + DRM_ERROR("Atomic update failure on pipe %c (start=%u end=%u)\n", + pipe_name(pipe), start_vbl_count, end_vbl_count); +} + +static void intel_update_primary_plane(struct intel_crtc *crtc) +{ + struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + int reg = DSPCNTR(crtc->plane); + + if (crtc->primary_enabled) + I915_WRITE(reg, I915_READ(reg) | DISPLAY_PLANE_ENABLE); + else + I915_WRITE(reg, I915_READ(reg) & ~DISPLAY_PLANE_ENABLE); +} + static void vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc, struct drm_framebuffer *fb, @@ -48,11 +148,14 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc, struct drm_device *dev = dplane->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane *intel_plane = to_intel_plane(dplane); + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_plane->pipe; int plane = intel_plane->plane; u32 sprctl; unsigned long sprsurf_offset, linear_offset; int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + u32 start_vbl_count; + bool atomic_update; sprctl = I915_READ(SPCNTR(pipe, plane)); @@ -104,6 +207,12 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc, break; } + /* + * Enable gamma to match primary/cursor plane behaviour. + * FIXME should be user controllable via propertiesa. + */ + sprctl |= SP_GAMMA_ENABLE; + if (obj->tiling_mode != I915_TILING_NONE) sprctl |= SP_TILED; @@ -118,9 +227,6 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc, crtc_w--; crtc_h--; - I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]); - I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x); - linear_offset = y * fb->pitches[0] + x * pixel_size; sprsurf_offset = intel_gen4_compute_page_offset(&x, &y, obj->tiling_mode, @@ -128,6 +234,13 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc, fb->pitches[0]); linear_offset -= sprsurf_offset; + atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count); + + intel_update_primary_plane(intel_crtc); + + I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]); + I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x); + if (obj->tiling_mode != I915_TILING_NONE) I915_WRITE(SPTILEOFF(pipe, plane), (y << 16) | x); else @@ -135,9 +248,13 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc, I915_WRITE(SPSIZE(pipe, plane), (crtc_h << 16) | crtc_w); I915_WRITE(SPCNTR(pipe, plane), sprctl); - I915_MODIFY_DISPBASE(SPSURF(pipe, plane), i915_gem_obj_ggtt_offset(obj) + - sprsurf_offset); - POSTING_READ(SPSURF(pipe, plane)); + I915_WRITE(SPSURF(pipe, plane), i915_gem_obj_ggtt_offset(obj) + + sprsurf_offset); + + intel_flush_primary_plane(dev_priv, intel_crtc->plane); + + if (atomic_update) + intel_pipe_update_end(intel_crtc, start_vbl_count); } static void @@ -146,14 +263,25 @@ vlv_disable_plane(struct drm_plane *dplane, struct drm_crtc *crtc) struct drm_device *dev = dplane->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane *intel_plane = to_intel_plane(dplane); + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_plane->pipe; int plane = intel_plane->plane; + u32 start_vbl_count; + bool atomic_update; + + atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count); + + intel_update_primary_plane(intel_crtc); I915_WRITE(SPCNTR(pipe, plane), I915_READ(SPCNTR(pipe, plane)) & ~SP_ENABLE); /* Activate double buffered register update */ - I915_MODIFY_DISPBASE(SPSURF(pipe, plane), 0); - POSTING_READ(SPSURF(pipe, plane)); + I915_WRITE(SPSURF(pipe, plane), 0); + + intel_flush_primary_plane(dev_priv, intel_crtc->plane); + + if (atomic_update) + intel_pipe_update_end(intel_crtc, start_vbl_count); intel_update_sprite_watermarks(dplane, crtc, 0, 0, false, false); } @@ -220,11 +348,13 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_device *dev = plane->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane *intel_plane = to_intel_plane(plane); + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_plane->pipe; u32 sprctl, sprscale = 0; unsigned long sprsurf_offset, linear_offset; int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); - bool scaling_was_enabled = dev_priv->sprite_scaling_enabled; + u32 start_vbl_count; + bool atomic_update; sprctl = I915_READ(SPRCTL(pipe)); @@ -257,17 +387,23 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, BUG(); } + /* + * Enable gamma to match primary/cursor plane behaviour. + * FIXME should be user controllable via propertiesa. + */ + sprctl |= SPRITE_GAMMA_ENABLE; + if (obj->tiling_mode != I915_TILING_NONE) sprctl |= SPRITE_TILED; - if (IS_HASWELL(dev)) + if (IS_HASWELL(dev) || IS_BROADWELL(dev)) sprctl &= ~SPRITE_TRICKLE_FEED_DISABLE; else sprctl |= SPRITE_TRICKLE_FEED_DISABLE; sprctl |= SPRITE_ENABLE; - if (IS_HASWELL(dev)) + if (IS_HASWELL(dev) || IS_BROADWELL(dev)) sprctl |= SPRITE_PIPE_CSC_ENABLE; intel_update_sprite_watermarks(plane, crtc, src_w, pixel_size, true, @@ -279,24 +415,8 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, crtc_w--; crtc_h--; - /* - * IVB workaround: must disable low power watermarks for at least - * one frame before enabling scaling. LP watermarks can be re-enabled - * when scaling is disabled. - */ - if (crtc_w != src_w || crtc_h != src_h) { - dev_priv->sprite_scaling_enabled |= 1 << pipe; - - if (!scaling_was_enabled) { - intel_update_watermarks(dev); - intel_wait_for_vblank(dev, pipe); - } + if (crtc_w != src_w || crtc_h != src_h) sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h; - } else - dev_priv->sprite_scaling_enabled &= ~(1 << pipe); - - I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]); - I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x); linear_offset = y * fb->pitches[0] + x * pixel_size; sprsurf_offset = @@ -304,9 +424,16 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, pixel_size, fb->pitches[0]); linear_offset -= sprsurf_offset; + atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count); + + intel_update_primary_plane(intel_crtc); + + I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]); + I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x); + /* HSW consolidates SPRTILEOFF and SPRLINOFF into a single SPROFFSET * register */ - if (IS_HASWELL(dev)) + if (IS_HASWELL(dev) || IS_BROADWELL(dev)) I915_WRITE(SPROFFSET(pipe), (y << 16) | x); else if (obj->tiling_mode != I915_TILING_NONE) I915_WRITE(SPRTILEOFF(pipe), (y << 16) | x); @@ -317,13 +444,13 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, if (intel_plane->can_scale) I915_WRITE(SPRSCALE(pipe), sprscale); I915_WRITE(SPRCTL(pipe), sprctl); - I915_MODIFY_DISPBASE(SPRSURF(pipe), - i915_gem_obj_ggtt_offset(obj) + sprsurf_offset); - POSTING_READ(SPRSURF(pipe)); + I915_WRITE(SPRSURF(pipe), + i915_gem_obj_ggtt_offset(obj) + sprsurf_offset); - /* potentially re-enable LP watermarks */ - if (scaling_was_enabled && !dev_priv->sprite_scaling_enabled) - intel_update_watermarks(dev); + intel_flush_primary_plane(dev_priv, intel_crtc->plane); + + if (atomic_update) + intel_pipe_update_end(intel_crtc, start_vbl_count); } static void @@ -332,24 +459,34 @@ ivb_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc) struct drm_device *dev = plane->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane *intel_plane = to_intel_plane(plane); + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_plane->pipe; - bool scaling_was_enabled = dev_priv->sprite_scaling_enabled; + u32 start_vbl_count; + bool atomic_update; + + atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count); + + intel_update_primary_plane(intel_crtc); I915_WRITE(SPRCTL(pipe), I915_READ(SPRCTL(pipe)) & ~SPRITE_ENABLE); /* Can't leave the scaler enabled... */ if (intel_plane->can_scale) I915_WRITE(SPRSCALE(pipe), 0); /* Activate double buffered register update */ - I915_MODIFY_DISPBASE(SPRSURF(pipe), 0); - POSTING_READ(SPRSURF(pipe)); + I915_WRITE(SPRSURF(pipe), 0); - dev_priv->sprite_scaling_enabled &= ~(1 << pipe); + intel_flush_primary_plane(dev_priv, intel_crtc->plane); - intel_update_sprite_watermarks(plane, crtc, 0, 0, false, false); + if (atomic_update) + intel_pipe_update_end(intel_crtc, start_vbl_count); + + /* + * Avoid underruns when disabling the sprite. + * FIXME remove once watermark updates are done properly. + */ + intel_wait_for_vblank(dev, pipe); - /* potentially re-enable LP watermarks */ - if (scaling_was_enabled && !dev_priv->sprite_scaling_enabled) - intel_update_watermarks(dev); + intel_update_sprite_watermarks(plane, crtc, 0, 0, false, false); } static int @@ -417,10 +554,13 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_device *dev = plane->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane *intel_plane = to_intel_plane(plane); + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_plane->pipe; unsigned long dvssurf_offset, linear_offset; u32 dvscntr, dvsscale; int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + u32 start_vbl_count; + bool atomic_update; dvscntr = I915_READ(DVSCNTR(pipe)); @@ -453,6 +593,12 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, BUG(); } + /* + * Enable gamma to match primary/cursor plane behaviour. + * FIXME should be user controllable via propertiesa. + */ + dvscntr |= DVS_GAMMA_ENABLE; + if (obj->tiling_mode != I915_TILING_NONE) dvscntr |= DVS_TILED; @@ -470,18 +616,22 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, crtc_h--; dvsscale = 0; - if (IS_GEN5(dev) || crtc_w != src_w || crtc_h != src_h) + if (crtc_w != src_w || crtc_h != src_h) dvsscale = DVS_SCALE_ENABLE | (src_w << 16) | src_h; - I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]); - I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x); - linear_offset = y * fb->pitches[0] + x * pixel_size; dvssurf_offset = intel_gen4_compute_page_offset(&x, &y, obj->tiling_mode, pixel_size, fb->pitches[0]); linear_offset -= dvssurf_offset; + atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count); + + intel_update_primary_plane(intel_crtc); + + I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]); + I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x); + if (obj->tiling_mode != I915_TILING_NONE) I915_WRITE(DVSTILEOFF(pipe), (y << 16) | x); else @@ -490,9 +640,13 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, I915_WRITE(DVSSIZE(pipe), (crtc_h << 16) | crtc_w); I915_WRITE(DVSSCALE(pipe), dvsscale); I915_WRITE(DVSCNTR(pipe), dvscntr); - I915_MODIFY_DISPBASE(DVSSURF(pipe), - i915_gem_obj_ggtt_offset(obj) + dvssurf_offset); - POSTING_READ(DVSSURF(pipe)); + I915_WRITE(DVSSURF(pipe), + i915_gem_obj_ggtt_offset(obj) + dvssurf_offset); + + intel_flush_primary_plane(dev_priv, intel_crtc->plane); + + if (atomic_update) + intel_pipe_update_end(intel_crtc, start_vbl_count); } static void @@ -501,50 +655,81 @@ ilk_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc) struct drm_device *dev = plane->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane *intel_plane = to_intel_plane(plane); + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_plane->pipe; + u32 start_vbl_count; + bool atomic_update; + + atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count); + + intel_update_primary_plane(intel_crtc); I915_WRITE(DVSCNTR(pipe), I915_READ(DVSCNTR(pipe)) & ~DVS_ENABLE); /* Disable the scaler */ I915_WRITE(DVSSCALE(pipe), 0); /* Flush double buffered register updates */ - I915_MODIFY_DISPBASE(DVSSURF(pipe), 0); - POSTING_READ(DVSSURF(pipe)); + I915_WRITE(DVSSURF(pipe), 0); + + intel_flush_primary_plane(dev_priv, intel_crtc->plane); + + if (atomic_update) + intel_pipe_update_end(intel_crtc, start_vbl_count); + + /* + * Avoid underruns when disabling the sprite. + * FIXME remove once watermark updates are done properly. + */ + intel_wait_for_vblank(dev, pipe); intel_update_sprite_watermarks(plane, crtc, 0, 0, false, false); } static void -intel_enable_primary(struct drm_crtc *crtc) +intel_post_enable_primary(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int reg = DSPCNTR(intel_crtc->plane); - if (!intel_crtc->primary_disabled) - return; + /* + * BDW signals flip done immediately if the plane + * is disabled, even if the plane enable is already + * armed to occur at the next vblank :( + */ + if (IS_BROADWELL(dev)) + intel_wait_for_vblank(dev, intel_crtc->pipe); - intel_crtc->primary_disabled = false; - intel_update_fbc(dev); + /* + * FIXME IPS should be fine as long as one plane is + * enabled, but in practice it seems to have problems + * when going from primary only to sprite only and vice + * versa. + */ + hsw_enable_ips(intel_crtc); - I915_WRITE(reg, I915_READ(reg) | DISPLAY_PLANE_ENABLE); + mutex_lock(&dev->struct_mutex); + intel_update_fbc(dev); + mutex_unlock(&dev->struct_mutex); } static void -intel_disable_primary(struct drm_crtc *crtc) +intel_pre_disable_primary(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int reg = DSPCNTR(intel_crtc->plane); - - if (intel_crtc->primary_disabled) - return; - I915_WRITE(reg, I915_READ(reg) & ~DISPLAY_PLANE_ENABLE); + mutex_lock(&dev->struct_mutex); + if (dev_priv->fbc.plane == intel_crtc->plane) + intel_disable_fbc(dev); + mutex_unlock(&dev->struct_mutex); - intel_crtc->primary_disabled = true; - intel_update_fbc(dev); + /* + * FIXME IPS should be fine as long as one plane is + * enabled, but in practice it seems to have problems + * when going from primary only to sprite only and vice + * versa. + */ + hsw_disable_ips(intel_crtc); } static int @@ -615,6 +800,15 @@ format_is_yuv(uint32_t format) } } +static bool colorkey_enabled(struct intel_plane *intel_plane) +{ + struct drm_intel_sprite_colorkey key; + + intel_plane->get_colorkey(&intel_plane->base, &key); + + return key.flags != I915_SET_COLORKEY_NONE; +} + static int intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, @@ -623,16 +817,13 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, uint32_t src_w, uint32_t src_h) { struct drm_device *dev = plane->dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_plane *intel_plane = to_intel_plane(plane); - struct intel_framebuffer *intel_fb; - struct drm_i915_gem_object *obj, *old_obj; - int pipe = intel_plane->pipe; - enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, - pipe); - int ret = 0; - bool disable_primary = false; + struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); + struct drm_i915_gem_object *obj = intel_fb->obj; + struct drm_i915_gem_object *old_obj = intel_plane->obj; + int ret; + bool primary_enabled; bool visible; int hscale, vscale; int max_scale, min_scale; @@ -652,29 +843,23 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, .y2 = crtc_y + crtc_h, }; const struct drm_rect clip = { - .x2 = crtc->mode.hdisplay, - .y2 = crtc->mode.vdisplay, + .x2 = intel_crtc->active ? intel_crtc->config.pipe_src_w : 0, + .y2 = intel_crtc->active ? intel_crtc->config.pipe_src_h : 0, + }; + const struct { + int crtc_x, crtc_y; + unsigned int crtc_w, crtc_h; + uint32_t src_x, src_y, src_w, src_h; + } orig = { + .crtc_x = crtc_x, + .crtc_y = crtc_y, + .crtc_w = crtc_w, + .crtc_h = crtc_h, + .src_x = src_x, + .src_y = src_y, + .src_w = src_w, + .src_h = src_h, }; - - intel_fb = to_intel_framebuffer(fb); - obj = intel_fb->obj; - - old_obj = intel_plane->obj; - - intel_plane->crtc_x = crtc_x; - intel_plane->crtc_y = crtc_y; - intel_plane->crtc_w = crtc_w; - intel_plane->crtc_h = crtc_h; - intel_plane->src_x = src_x; - intel_plane->src_y = src_y; - intel_plane->src_w = src_w; - intel_plane->src_h = src_h; - - /* Pipe must be running... */ - if (!(I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_ENABLE)) { - DRM_DEBUG_KMS("Pipe disabled\n"); - return -EINVAL; - } /* Don't modify another pipe's plane */ if (intel_plane->pipe != intel_crtc->pipe) { @@ -809,8 +994,8 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, * If the sprite is completely covering the primary plane, * we can disable the primary and save power. */ - disable_primary = drm_rect_equals(&dst, &clip); - WARN_ON(disable_primary && !visible); + primary_enabled = !drm_rect_equals(&dst, &clip) || colorkey_enabled(intel_plane); + WARN_ON(!primary_enabled && !visible && intel_crtc->active); mutex_lock(&dev->struct_mutex); @@ -820,27 +1005,43 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, * the sprite planes only require 128KiB alignment and 32 PTE padding. */ ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); - if (ret) - goto out_unlock; + mutex_unlock(&dev->struct_mutex); + + if (ret) + return ret; + + intel_plane->crtc_x = orig.crtc_x; + intel_plane->crtc_y = orig.crtc_y; + intel_plane->crtc_w = orig.crtc_w; + intel_plane->crtc_h = orig.crtc_h; + intel_plane->src_x = orig.src_x; + intel_plane->src_y = orig.src_y; + intel_plane->src_w = orig.src_w; + intel_plane->src_h = orig.src_h; intel_plane->obj = obj; - /* - * Be sure to re-enable the primary before the sprite is no longer - * covering it fully. - */ - if (!disable_primary) - intel_enable_primary(crtc); + if (intel_crtc->active) { + bool primary_was_enabled = intel_crtc->primary_enabled; - if (visible) - intel_plane->update_plane(plane, crtc, fb, obj, - crtc_x, crtc_y, crtc_w, crtc_h, - src_x, src_y, src_w, src_h); - else - intel_plane->disable_plane(plane, crtc); + intel_crtc->primary_enabled = primary_enabled; - if (disable_primary) - intel_disable_primary(crtc); + if (primary_was_enabled != primary_enabled) + intel_crtc_wait_for_pending_flips(crtc); + + if (primary_was_enabled && !primary_enabled) + intel_pre_disable_primary(crtc); + + if (visible) + intel_plane->update_plane(plane, crtc, fb, obj, + crtc_x, crtc_y, crtc_w, crtc_h, + src_x, src_y, src_w, src_h); + else + intel_plane->disable_plane(plane, crtc); + + if (!primary_was_enabled && primary_enabled) + intel_post_enable_primary(crtc); + } /* Unpin old obj after new one is active to avoid ugliness */ if (old_obj) { @@ -850,17 +1051,15 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, * wait for vblank to avoid ugliness, we only need to * do the pin & ref bookkeeping. */ - if (old_obj != obj) { - mutex_unlock(&dev->struct_mutex); - intel_wait_for_vblank(dev, to_intel_crtc(crtc)->pipe); - mutex_lock(&dev->struct_mutex); - } + if (old_obj != obj && intel_crtc->active) + intel_wait_for_vblank(dev, intel_crtc->pipe); + + mutex_lock(&dev->struct_mutex); intel_unpin_fb_obj(old_obj); + mutex_unlock(&dev->struct_mutex); } -out_unlock: - mutex_unlock(&dev->struct_mutex); - return ret; + return 0; } static int @@ -868,7 +1067,7 @@ intel_disable_plane(struct drm_plane *plane) { struct drm_device *dev = plane->dev; struct intel_plane *intel_plane = to_intel_plane(plane); - int ret = 0; + struct intel_crtc *intel_crtc; if (!plane->fb) return 0; @@ -876,21 +1075,31 @@ intel_disable_plane(struct drm_plane *plane) if (WARN_ON(!plane->crtc)) return -EINVAL; - intel_enable_primary(plane->crtc); - intel_plane->disable_plane(plane, plane->crtc); + intel_crtc = to_intel_crtc(plane->crtc); - if (!intel_plane->obj) - goto out; + if (intel_crtc->active) { + bool primary_was_enabled = intel_crtc->primary_enabled; - intel_wait_for_vblank(dev, intel_plane->pipe); + intel_crtc->primary_enabled = true; - mutex_lock(&dev->struct_mutex); - intel_unpin_fb_obj(intel_plane->obj); - intel_plane->obj = NULL; - mutex_unlock(&dev->struct_mutex); -out: + intel_plane->disable_plane(plane, plane->crtc); - return ret; + if (!primary_was_enabled && intel_crtc->primary_enabled) + intel_post_enable_primary(plane->crtc); + } + + if (intel_plane->obj) { + if (intel_crtc->active) + intel_wait_for_vblank(dev, intel_plane->pipe); + + mutex_lock(&dev->struct_mutex); + intel_unpin_fb_obj(intel_plane->obj); + mutex_unlock(&dev->struct_mutex); + + intel_plane->obj = NULL; + } + + return 0; } static void intel_destroy_plane(struct drm_plane *plane) @@ -921,7 +1130,7 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data, obj = drm_mode_object_find(dev, set->plane_id, DRM_MODE_OBJECT_PLANE); if (!obj) { - ret = -EINVAL; + ret = -ENOENT; goto out_unlock; } @@ -950,7 +1159,7 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data, obj = drm_mode_object_find(dev, get->plane_id, DRM_MODE_OBJECT_PLANE); if (!obj) { - ret = -EINVAL; + ret = -ENOENT; goto out_unlock; } @@ -1034,7 +1243,7 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane) if (INTEL_INFO(dev)->gen < 5) return -ENODEV; - intel_plane = kzalloc(sizeof(struct intel_plane), GFP_KERNEL); + intel_plane = kzalloc(sizeof(*intel_plane), GFP_KERNEL); if (!intel_plane) return -ENOMEM; @@ -1058,6 +1267,7 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane) break; case 7: + case 8: if (IS_IVYBRIDGE(dev)) { intel_plane->can_scale = true; intel_plane->max_downscale = 2; |
