[PATCH v2 RFC 4/5] drm/i915: make plane helpers fully atomic

Maarten Lankhorst
2015-04-23T06:19:49Z

This kills off most of the transitional sers and uses atomic plane updates
in the modeset path to update everything.

Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
---
Changes since v1:
- Add atomic and sprite planes during a modeset too so they
will be restored.
- Drop a WARN_ON(!crtc_state->enabled) in atomic_plane_check for
cursor and sprite planes. Keep it for primary as this probably
indicates we messed up somewhere.

drivers/gpu/drm/i915/intel_atomic_plane.c | 58 ++--
drivers/gpu/drm/i915/intel_display.c | 485 +++++++++++++++++++++---------
drivers/gpu/drm/i915/intel_sprite.c | 33 +-
3 files changed, 366 insertions(+), 210 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_atomic_plane.c b/drivers/gpu/drm/i915/intel_atomic_plane.c
index f7bd3b8fa245..ba4ab392b6b0 100644
--- a/drivers/gpu/drm/i915/intel_atomic_plane.c
+++ b/drivers/gpu/drm/i915/intel_atomic_plane.c
@@ -110,32 +110,40 @@ static int intel_plane_atomic_check(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct drm_crtc *crtc = state->crtc;
- struct intel_crtc *intel_crtc;
- struct intel_crtc_state *crtc_state;
+ struct drm_crtc_state *crtc_state;
struct intel_plane *intel_plane = to_intel_plane(plane);
struct intel_plane_state *intel_state = to_intel_plane_state(state);

- crtc = crtc ? crtc : plane->crtc;
- intel_crtc = to_intel_crtc(crtc);
-
+ intel_state->visible = false;
/*
* Both crtc and plane->crtc could be NULL if we're updating a
* property while the plane is disabled. We don't actually have
* anything driver-specific we need to test in that case, so
* just return success.
*/
- if (!crtc)
+ if (!crtc) {
+ DRM_DEBUG_ATOMIC("Invisible: no crtc\n");
return 0;
+ }
+
+ crtc_state = state->state->crtc_states[drm_crtc_index(crtc)];
+ if (WARN_ON(!crtc_state))
+ return 0;
+
+ if (!crtc_state->enable) {
+ DRM_DEBUG_ATOMIC("Invisible: crtc off\n");

- /* FIXME: temporary hack necessary while we still use the plane update
- * helper. */
- if (state->state) {
- crtc_state =
- intel_atomic_get_crtc_state(state->state, intel_crtc);
- if (IS_ERR(crtc_state))
- return PTR_ERR(crtc_state);
- } else {
- crtc_state = intel_crtc->config;
+ /*
+ * Probably allowed after converting to atomic. Right
+ * now it probably means we have the state confused.
+ */
+ I915_STATE_WARN_ON(plane->type == DRM_PLANE_TYPE_PRIMARY);
+ return 0;
+ }
+
+ if (!crtc_state->active) {
+ DRM_DEBUG_ATOMIC("Invisible: dpms off\n");
+ return 0;
}

/*
@@ -155,24 +163,8 @@ static int intel_plane_atomic_check(struct drm_plane *plane,
/* Clip all planes to CRTC size, or 0x0 if CRTC is disabled */
intel_state->clip.x1 = 0;
intel_state->clip.y1 = 0;
- intel_state->clip.x2 =
- crtc_state->base.active ? crtc_state->pipe_src_w : 0;
- intel_state->clip.y2 =
- crtc_state->base.active ? crtc_state->pipe_src_h : 0;
-
- /*
- * Disabling a plane is always okay; we just need to update
- * fb tracking in a special way since cleanup_fb() won't
- * get called by the plane helpers.
- */
- if (state->fb == NULL && plane->state->fb != NULL) {
- /*
- * 'prepare' is never called when plane is being disabled, so
- * we need to handle frontbuffer tracking as a special case
- */
- intel_crtc->atomic.disabled_planes |=
- (1 << drm_plane_index(plane));
- }
+ intel_state->clip.x2 = to_intel_crtc_state(crtc_state)->pipe_src_w;
+ intel_state->clip.y2 = to_intel_crtc_state(crtc_state)->pipe_src_h;

if (state->fb && intel_rotation_90_or_270(state->rotation)) {
if (!(state->fb->modifier[0] == I915_FORMAT_MOD_Y_TILED ||
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 2ffacb4c3a12..acb5c5bea428 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -100,12 +100,16 @@ static void vlv_prepare_pll(struct intel_crtc *crtc,
const struct intel_crtc_state *pipe_config);
static void chv_prepare_pll(struct intel_crtc *crtc,
const struct intel_crtc_state *pipe_config);
+static int intel_atomic_check_crtc(struct drm_crtc *crtc,
+ struct drm_crtc_state *crtc_state);
static void intel_begin_crtc_commit(struct drm_crtc *crtc);
static void intel_finish_crtc_commit(struct drm_crtc *crtc);
static void skl_init_scalers(struct drm_device *dev, struct intel_crtc *intel_crtc,
struct intel_crtc_state *crtc_state);
static void intel_crtc_enable_planes(struct drm_crtc *crtc);
static void intel_crtc_disable_planes(struct drm_crtc *crtc);
+static void intel_pre_disable_primary(struct drm_crtc *crtc);
+static void intel_post_enable_primary(struct drm_crtc *crtc);

static struct intel_encoder *intel_find_encoder(struct intel_connector *connector, int pipe)
{
@@ -3056,11 +3060,20 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_plane_state *plane_state =
+ to_intel_plane_state(crtc->primary->state);
+ bool was_visible = plane_state->visible;

- if (dev_priv->display.disable_fbc)
+ /* Not supported right now by the helper, but lets be thorough. */
+ if (was_visible && !fb)
+ intel_pre_disable_primary(crtc);
+ else if (was_visible && dev_priv->display.disable_fbc)
dev_priv->display.disable_fbc(dev);

+ plane_state->visible = !!fb;
dev_priv->display.update_primary_plane(crtc, fb, x, y);
+ if (!was_visible && fb)
+ intel_post_enable_primary(crtc);

return 0;
}
@@ -3087,16 +3100,17 @@ static void intel_update_primary_planes(struct drm_device *dev)
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);

drm_modeset_lock(&crtc->mutex, NULL);
- /*
- * FIXME: Once we have proper support for primary planes (and
- * disabling them without disabling the entire crtc) allow again
- * a NULL crtc->primary->fb.
- */
- if (intel_crtc->active && crtc->primary->fb)
+
+ if (intel_crtc->active) {
+ const struct intel_plane_state *state =
+ to_intel_plane_state(crtc->primary->state);
+
dev_priv->display.update_primary_plane(crtc,
- crtc->primary->fb,
- crtc->x,
- crtc->y);
+ state->base.fb,
+ state->src.x1 >> 16,
+ state->src.y1 >> 16);
+ }
+
drm_modeset_unlock(&crtc->mutex);
}
}
@@ -10659,6 +10673,7 @@ static struct drm_crtc_helper_funcs intel_helper_funcs = {
.load_lut = intel_crtc_load_lut,
.atomic_begin = intel_begin_crtc_commit,
.atomic_flush = intel_finish_crtc_commit,
+ .atomic_check = intel_atomic_check_crtc,
};

/**
@@ -10713,7 +10728,6 @@ static void intel_modeset_update_connector_atomic_state(struct drm_device *dev)
*/
static void intel_modeset_fixup_state(struct drm_atomic_state *state)
{
- struct intel_crtc *crtc;
struct intel_encoder *encoder;
struct intel_connector *connector;

@@ -10736,11 +10750,6 @@ static void intel_modeset_fixup_state(struct drm_atomic_state *state)
encoder->base.crtc = NULL;
}

- for_each_intel_crtc(state->dev, crtc) {
- crtc->base.enabled = crtc->base.state->enable;
- crtc->config = to_intel_crtc_state(crtc->base.state);
- }
-
/* Copy the new configuration to the staged state, to keep the few
* pieces of code that haven't been converted yet happy */
intel_modeset_update_staged_output_state(state->dev);
@@ -11170,6 +11179,8 @@ intel_modeset_update_state(struct drm_atomic_state *state)
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state;
struct drm_connector *connector;
+ struct drm_connector_state *connector_state;
+ int i;

intel_shared_dpll_commit(dev_priv);

@@ -11185,7 +11196,26 @@ intel_modeset_update_state(struct drm_atomic_state *state)
intel_encoder->connectors_active = false;
}

- drm_atomic_helper_swap_state(state->dev, state);
+ /*
+ * swap crtc and connector state, plane state is already swapped in
+ * __intel_set_mode_update_planes. Once .crtc_disable is fixed
+ * all state should be swapped before disabling crtc's.
+ */
+ for_each_crtc_in_state(state, crtc, crtc_state, i) {
+ crtc->enabled = crtc_state->enable;
+ to_intel_crtc(crtc)->config = to_intel_crtc_state(crtc_state);
+
+ crtc->state->state = state;
+ swap(state->crtc_states[i], crtc->state);
+ crtc->state->state = NULL;
+ }
+
+ for_each_connector_in_state(state, connector, connector_state, i) {
+ connector->state->state = state;
+ swap(state->connector_states[i], connector->state);
+ connector->state->state = NULL;
+ }
+
intel_modeset_fixup_state(state);

/* Double check state. */
@@ -11740,6 +11770,30 @@ static void update_scanline_offset(struct intel_crtc *crtc)
crtc->scanline_offset = 1;
}

+static int intel_modeset_compute_planes(struct drm_atomic_state *state,
+ struct drm_crtc *crtc)
+{
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ struct drm_plane_state *plane_state;
+ struct drm_plane *plane;
+
+ plane_state = drm_atomic_get_plane_state(state, crtc->cursor);
+ if (IS_ERR(plane_state))
+ return PTR_ERR(plane_state);
+
+ /* Add all overlay planes */
+ drm_for_each_legacy_plane(plane, &crtc->dev->mode_config.plane_list) {
+ if (to_intel_plane(plane)->pipe == pipe) {
+ plane_state = drm_atomic_get_plane_state(state, plane);
+ if (IS_ERR(plane_state))
+ return PTR_ERR(plane_state);
+ }
+ }
+
+ return 0;
+}
+
static struct intel_crtc_state *
intel_modeset_compute_config(struct drm_crtc *crtc,
struct drm_atomic_state *state)
@@ -11765,8 +11819,14 @@ intel_modeset_compute_config(struct drm_crtc *crtc,
if (IS_ERR(pipe_config))
return pipe_config;

+ if (needs_modeset(&pipe_config->base)) {
+ ret = intel_modeset_compute_planes(state, crtc);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
if (!pipe_config->base.enable)
- return pipe_config;
+ goto done;

ret = intel_modeset_pipe_config(crtc, state, pipe_config);
if (ret)
@@ -11784,8 +11844,8 @@ intel_modeset_compute_config(struct drm_crtc *crtc,
* required changes and forcing a mode set.
*/

- intel_dump_pipe_config(to_intel_crtc(crtc), pipe_config,"[modeset]");
-
+ intel_dump_pipe_config(to_intel_crtc(crtc), pipe_config, "[modeset]");
+done:
ret = drm_atomic_helper_check_planes(state->dev, state);
if (ret)
return ERR_PTR(ret);
@@ -11869,6 +11929,113 @@ static int __intel_set_mode_checks(struct drm_atomic_state *state)
return 0;
}

+static void __intel_set_mode_update_planes(struct drm_device *dev,
+ struct drm_atomic_state *state)
+{
+ int i;
+ struct drm_plane_state *old_plane_state;
+ struct drm_crtc_state *crtc_state;
+ struct drm_plane *plane;
+ struct drm_crtc *crtc;
+
+ /*
+ * For now only swap plane state, should be replaced with a
+ * call to drm_atomic_helper_swap_state
+ */
+ for_each_plane_in_state(state, plane, old_plane_state, i) {
+ struct drm_plane *plane = state->planes[i];
+
+ if (!plane)
+ continue;
+
+ plane->state->state = state;
+ swap(state->plane_states[i], plane->state);
+ plane->state->state = NULL;
+ }
+
+ for_each_crtc_in_state(state, crtc, crtc_state, i) {
+ const struct drm_crtc_helper_funcs *funcs;
+
+ funcs = crtc->helper_private;
+
+ if (!funcs || !funcs->atomic_begin)
+ continue;
+
+ /* XXX: Hack because crtc state is not swapped */
+ crtc->state->mode_changed = crtc_state->mode_changed;
+ crtc->state->active_changed = crtc_state->active_changed;
+
+ DRM_DEBUG_ATOMIC("Calling atomic_begin on crtc %i\n", i);
+ funcs->atomic_begin(crtc);
+ }
+
+ for_each_plane_in_state(state, plane, old_plane_state, i) {
+ bool visible = to_intel_plane_state(plane->state)->visible;
+ struct intel_plane *intel_plane = to_intel_plane(plane);
+ const struct drm_plane_helper_funcs *funcs;
+
+ crtc = plane->state->crtc;
+ funcs = plane->helper_private;
+
+ if (!funcs)
+ continue;
+
+ DRM_DEBUG_ATOMIC("Plane %i is visible: %i\n", i, visible);
+
+ if (!visible)
+ funcs->atomic_update(plane, old_plane_state);
+ else if (crtc->state->mode_changed)
+ intel_plane->disable_plane(plane, crtc, true);
+ }
+}
+
+static void __intel_set_mode_cleanup_planes(struct drm_device *dev,
+ struct drm_atomic_state *old_state)
+{
+ int nplanes = dev->mode_config.num_total_plane;
+ int ncrtcs = dev->mode_config.num_crtc;
+ int i;
+
+ for (i = 0; i < nplanes; i++) {
+ const struct drm_plane_helper_funcs *funcs;
+ struct drm_plane *plane = old_state->planes[i];
+ struct drm_plane_state *old_plane_state;
+
+ if (!plane)
+ continue;
+
+ funcs = plane->helper_private;
+
+ if (!funcs)
+ continue;
+
+ old_plane_state = old_state->plane_states[i];
+
+ if (to_intel_plane_state(plane->state)->visible) {
+ DRM_DEBUG_ATOMIC("Plane %i is updated\n", i);
+ funcs->atomic_update(plane, old_plane_state);
+ } else
+ DRM_DEBUG_ATOMIC("Plane %i is left alone\n", i);
+ }
+
+ for (i = 0; i < ncrtcs; i++) {
+ const struct drm_crtc_helper_funcs *funcs;
+ struct drm_crtc *crtc = old_state->crtcs[i];
+
+ if (!crtc)
+ continue;
+
+ funcs = crtc->helper_private;
+
+ if (!funcs || !funcs->atomic_flush)
+ continue;
+
+ DRM_DEBUG_ATOMIC("Calling atomic_flush on crtc %i\n", i);
+ funcs->atomic_flush(crtc);
+ }
+ drm_atomic_helper_cleanup_planes(dev, old_state);
+}
+
static int __intel_set_mode(struct drm_crtc *modeset_crtc,
struct intel_crtc_state *pipe_config)
{
@@ -11877,7 +12044,7 @@ static int __intel_set_mode(struct drm_crtc *modeset_crtc,
struct drm_atomic_state *state = pipe_config->base.state;
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state;
- int ret = 0;
+ int ret;
int i;

ret = __intel_set_mode_checks(state);
@@ -11888,14 +12055,14 @@ static int __intel_set_mode(struct drm_crtc *modeset_crtc,
if (ret)
return ret;

+ __intel_set_mode_update_planes(dev, state);
+
for_each_crtc_in_state(state, crtc, crtc_state, i) {
if (!needs_modeset(crtc_state))
continue;

- intel_crtc_disable_planes(crtc);
+ intel_crtc_dpms_overlay_disable(to_intel_crtc(crtc));
dev_priv->display.crtc_disable(crtc);
- if (!crtc_state->enable)
- drm_plane_helper_disable(crtc->primary);
}

/* crtc->mode is already used by the ->mode_set callbacks, hence we need
@@ -11926,8 +12093,6 @@ static int __intel_set_mode(struct drm_crtc *modeset_crtc,

modeset_update_crtc_power_domains(state);

- drm_atomic_helper_commit_planes(dev, state);
-
/* Now enable the clocks, plane, pipe, and connectors that we set up. */
for_each_crtc_in_state(state, crtc, crtc_state, i) {
if (!crtc->state->enable)
@@ -11935,13 +12100,13 @@ static int __intel_set_mode(struct drm_crtc *modeset_crtc,

update_scanline_offset(to_intel_crtc(crtc));

- dev_priv->display.crtc_enable(crtc);
- intel_crtc_enable_planes(crtc);
+ if (needs_modeset(crtc->state))
+ dev_priv->display.crtc_enable(crtc);
}

- /* FIXME: add subpixel order */
+ __intel_set_mode_cleanup_planes(dev, state);

- drm_atomic_helper_cleanup_planes(dev, state);
+ /* FIXME: add subpixel order */

drm_atomic_state_free(state);

@@ -12170,6 +12335,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
return ret;

crtc_state->enable = drm_atomic_connectors_for_crtc(state, crtc);
+ crtc_state->active = crtc_state->enable;
}

ret = intel_modeset_setup_plane_state(state, set->crtc, set->mode,
@@ -12190,20 +12356,11 @@ intel_modeset_stage_output_state(struct drm_device *dev,
return 0;
}

-static bool primary_plane_visible(struct drm_crtc *crtc)
-{
- struct intel_plane_state *plane_state =
- to_intel_plane_state(crtc->primary->state);
-
- return plane_state->visible;
-}
-
static int intel_crtc_set_config(struct drm_mode_set *set)
{
struct drm_device *dev;
struct drm_atomic_state *state = NULL;
struct intel_crtc_state *pipe_config;
- bool primary_plane_was_visible;
int ret;

BUG_ON(!set);
@@ -12242,38 +12399,7 @@ static int intel_crtc_set_config(struct drm_mode_set *set)

intel_update_pipe_size(to_intel_crtc(set->crtc));

- primary_plane_was_visible = primary_plane_visible(set->crtc);
-
ret = intel_set_mode_with_config(set->crtc, pipe_config);
-
- if (ret == 0 &&
- pipe_config->base.enable &&
- pipe_config->base.planes_changed &&
- !needs_modeset(&pipe_config->base)) {
- struct intel_crtc *intel_crtc = to_intel_crtc(set->crtc);
-
- /*
- * We need to make sure the primary plane is re-enabled if it
- * has previously been turned off.
- */
- if (ret == 0 && !primary_plane_was_visible &&
- primary_plane_visible(set->crtc)) {
- WARN_ON(!intel_crtc->active);
- intel_post_enable_primary(set->crtc);
- }
-
- /*
- * In the fastboot case this may be our only check of the
- * state after boot. It would be better to only do it on
- * the first update, but we don't have a nice way of doing that
- * (and really, set_config isn't used much for high freq page
- * flipping, so increasing its cost here shouldn't be a big
- * deal).
- */
- if (i915.fastboot && ret == 0)
- intel_modeset_check_state(set->crtc->dev);
- }
-
if (ret) {
DRM_DEBUG_KMS("failed to set mode on [CRTC:%d], err = %d\n",
set->crtc->base.id, ret);
@@ -12413,6 +12539,9 @@ bool intel_wm_need_update(struct drm_plane *plane,
plane->state->rotation != state->rotation)
return true;

+ if (plane->state->crtc_w != state->crtc_w)
+ return true;
+
return false;
}

@@ -12507,74 +12636,22 @@ intel_check_primary_plane(struct drm_plane *plane,
struct intel_plane_state *state)
{
struct drm_device *dev = plane->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc = state->base.crtc;
- struct intel_crtc *intel_crtc;
struct drm_framebuffer *fb = state->base.fb;
struct drm_rect *dest = &state->dst;
struct drm_rect *src = &state->src;
const struct drm_rect *clip = &state->clip;
bool can_position = false;
- int ret;
-
- crtc = crtc ? crtc : plane->crtc;
- intel_crtc = to_intel_crtc(crtc);

if (INTEL_INFO(dev)->gen >= 9)
can_position = true;

- ret = drm_plane_helper_check_update(plane, crtc, fb,
- src, dest, clip,
- DRM_PLANE_HELPER_NO_SCALING,
- DRM_PLANE_HELPER_NO_SCALING,
- can_position, true,
- &state->visible);
- if (ret)
- return ret;
-
- if (intel_crtc->active) {
- struct intel_plane_state *old_state =
- to_intel_plane_state(plane->state);
-
- intel_crtc->atomic.wait_for_flips = true;
-
- /*
- * FBC does not work on some platforms for rotated
- * planes, so disable it when rotation is not 0 and
- * update it when rotation is set back to 0.
- *
- * FIXME: This is redundant with the fbc update done in
- * the primary plane enable function except that that
- * one is done too late. We eventually need to unify
- * this.
- */
- if (state->visible &&
- INTEL_INFO(dev)->gen <= 4 && !IS_G4X(dev) &&
- dev_priv->fbc.crtc == intel_crtc &&
- state->base.rotation != BIT(DRM_ROTATE_0)) {
- intel_crtc->atomic.disable_fbc = true;
- }
-
- if (state->visible && !old_state->visible) {
- /*
- * 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_crtc->atomic.wait_vblank = true;
- }
-
- intel_crtc->atomic.fb_bits |=
- INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe);
-
- intel_crtc->atomic.update_fbc = true;
-
- if (intel_wm_need_update(plane, &state->base))
- intel_crtc->atomic.update_wm = true;
- }
-
- return 0;
+ return drm_plane_helper_check_update(plane, crtc, fb,
+ src, dest, clip,
+ DRM_PLANE_HELPER_NO_SCALING,
+ DRM_PLANE_HELPER_NO_SCALING,
+ can_position, true,
+ &state->visible);
}

static void
@@ -12600,8 +12677,10 @@ intel_commit_primary_plane(struct drm_plane *plane,
/* FIXME: kill this fastboot hack */
intel_update_pipe_size(intel_crtc);

- dev_priv->display.update_primary_plane(crtc, plane->fb,
- crtc->x, crtc->y);
+ dev_priv->display.update_primary_plane(crtc,
+ state->base.fb,
+ state->src.x1 >> 16,
+ state->src.y1 >> 16);
}
}

@@ -12616,6 +12695,123 @@ intel_disable_primary_plane(struct drm_plane *plane,
dev_priv->display.update_primary_plane(crtc, NULL, 0, 0);
}

+/* Transitional checking here, mostly for plane updates */
+static int intel_atomic_check_crtc(struct drm_crtc *crtc,
+ struct drm_crtc_state *crtc_state)
+{
+ struct intel_crtc_state *pipe_config = to_intel_crtc_state(crtc_state);
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct drm_atomic_state *state = crtc_state->state;
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ unsigned plane_mask;
+ int i, nplanes = dev->mode_config.num_total_plane, idx;
+ bool mode_changed = needs_modeset(crtc_state);
+ bool is_crtc_enabled = crtc_state->active;
+ bool was_crtc_enabled = crtc->state->active && intel_crtc->active;
+
+ memset(&intel_crtc->atomic, 0, sizeof(intel_crtc->atomic));
+ intel_crtc->atomic.update_wm = mode_changed;
+
+ idx = drm_crtc_index(crtc);
+ DRM_DEBUG_ATOMIC("Crtc %i was enabled %i now enabled: %i\n",
+ idx, was_crtc_enabled, is_crtc_enabled);
+
+ plane_mask = crtc_state->plane_mask | crtc->state->plane_mask;
+ for (i = 0; i < nplanes; i++) {
+ struct intel_plane_state *plane_state, *old_plane_state;
+ struct intel_plane *plane = to_intel_plane(state->planes[i]);
+ bool turn_off, turn_on, visible, was_visible;
+ struct drm_framebuffer *fb;
+
+ if (!plane)
+ continue;
+
+ plane_state = to_intel_plane_state(state->plane_states[i]);
+ old_plane_state = to_intel_plane_state(plane->base.state);
+
+ was_visible = old_plane_state->visible && was_crtc_enabled;
+ visible = plane_state->visible && is_crtc_enabled;
+
+ turn_off = was_visible && (!visible || mode_changed);
+ turn_on = visible && (!was_visible || mode_changed);
+ fb = plane_state->base.fb;
+
+ DRM_DEBUG_ATOMIC("Crtc %i has plane %i with fb %i\n", idx,
+ drm_plane_index(&plane->base), fb ? fb->base.id : -1);
+ DRM_DEBUG_ATOMIC("\tvisible %i -> %i, off %i, on %i, ms %i\n",
+ was_visible, visible, turn_off, turn_on, mode_changed);
+
+ /* plane being turned off as part of modeset or changes? */
+ if (intel_wm_need_update(&plane->base, &plane_state->base))
+ intel_crtc->atomic.update_wm = true;
+
+ /*
+ * 'prepare' is never called when plane is being disabled, so
+ * we need to handle frontbuffer tracking as a special case
+ */
+ if (old_plane_state->base.fb && !plane_state->base.fb)
+ intel_crtc->atomic.disabled_planes |=
+ (1 << drm_plane_index(&plane->base));
+
+ switch (plane->base.type) {
+ case DRM_PLANE_TYPE_PRIMARY:
+ intel_crtc->atomic.fb_bits |=
+ INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe);
+
+ intel_crtc->atomic.wait_for_flips = true;
+ intel_crtc->atomic.pre_disable_primary = turn_off;
+ intel_crtc->atomic.post_enable_primary = turn_on;
+
+ if (turn_off)
+ intel_crtc->atomic.disable_fbc = true;
+
+ /*
+ * FBC does not work on some platforms for rotated
+ * planes, so disable it when rotation is not 0 and
+ * update it when rotation is set back to 0.
+ *
+ * FIXME: This is redundant with the fbc update done in
+ * the primary plane enable function except that that
+ * one is done too late. We eventually need to unify
+ * this.
+ */
+
+ if (visible &&
+ INTEL_INFO(dev)->gen <= 4 && !IS_G4X(dev) &&
+ dev_priv->fbc.crtc == intel_crtc &&
+ plane_state->base.rotation != BIT(DRM_ROTATE_0))
+ intel_crtc->atomic.disable_fbc = true;
+
+ /*
+ * 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 (turn_on && IS_BROADWELL(dev))
+ intel_crtc->atomic.wait_vblank = true;
+
+ intel_crtc->atomic.update_fbc = true;
+ break;
+ case DRM_PLANE_TYPE_CURSOR:
+ intel_crtc->atomic.fb_bits |=
+ INTEL_FRONTBUFFER_CURSOR(intel_crtc->pipe);
+ break;
+ case DRM_PLANE_TYPE_OVERLAY:
+ intel_crtc->atomic.fb_bits |=
+ INTEL_FRONTBUFFER_SPRITE(intel_crtc->pipe);
+
+ if (turn_off) {
+ intel_crtc->atomic.wait_vblank = true;
+ intel_crtc->atomic.update_sprite_watermarks |=
+ (1 << drm_plane_index(&plane->base));
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
static void intel_begin_crtc_commit(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
@@ -12664,10 +12860,13 @@ static void intel_begin_crtc_commit(struct drm_crtc *crtc)
intel_runtime_pm_get(dev_priv);

/* Perform vblank evasion around commit operation */
- if (intel_crtc->active)
+ if (intel_crtc->active && !needs_modeset(crtc->state) &&
+ !dev_priv->power_domains.init_power_on)
intel_crtc->atomic.evade =
intel_pipe_update_start(intel_crtc,
&intel_crtc->atomic.start_vbl_count);
+ else
+ intel_crtc->atomic.evade = false;
}

static void intel_finish_crtc_commit(struct drm_crtc *crtc)
@@ -12703,6 +12902,8 @@ static void intel_finish_crtc_commit(struct drm_crtc *crtc)
false, false);

memset(&intel_crtc->atomic, 0, sizeof(intel_crtc->atomic));
+ crtc->state->mode_changed = false;
+ crtc->state->active_changed = false;
}

/**
@@ -12812,13 +13013,9 @@ intel_check_cursor_plane(struct drm_plane *plane,
struct drm_rect *src = &state->src;
const struct drm_rect *clip = &state->clip;
struct drm_i915_gem_object *obj = intel_fb_obj(fb);
- struct intel_crtc *intel_crtc;
unsigned stride;
int ret;

- crtc = crtc ? crtc : plane->crtc;
- intel_crtc = to_intel_crtc(crtc);
-
ret = drm_plane_helper_check_update(plane, crtc, fb,
src, dest, clip,
DRM_PLANE_HELPER_NO_SCALING,
@@ -12830,7 +13027,7 @@ intel_check_cursor_plane(struct drm_plane *plane,

/* if we want to turn off the cursor ignore width and height */
if (!obj)
- goto finish;
+ return 0;

/* Check for which cursor types we support */
if (!cursor_size_ok(dev, state->base.crtc_w, state->base.crtc_h)) {
@@ -12847,19 +13044,10 @@ intel_check_cursor_plane(struct drm_plane *plane,

if (fb->modifier[0] != DRM_FORMAT_MOD_NONE) {
DRM_DEBUG_KMS("cursor cannot be tiled\n");
- ret = -EINVAL;
- }
-
-finish:
- if (intel_crtc->active) {
- if (plane->state->crtc_w != state->base.crtc_w)
- intel_crtc->atomic.update_wm = true;
-
- intel_crtc->atomic.fb_bits |=
- INTEL_FRONTBUFFER_CURSOR(intel_crtc->pipe);
+ return -EINVAL;
}

- return ret;
+ return 0;
}

static void
@@ -14089,6 +14277,7 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc)

WARN_ON(crtc->active);
crtc->base.state->enable = false;
+ crtc->base.state->active = false;
crtc->base.enabled = false;
}

@@ -14117,6 +14306,7 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc)
crtc->active ? "enabled" : "disabled");

crtc->base.state->enable = crtc->active;
+ crtc->base.state->active = crtc->active;
crtc->base.enabled = crtc->active;

/* Because we only establish the connector -> encoder ->
@@ -14255,6 +14445,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
crtc->config);

crtc->base.state->enable = crtc->active;
+ crtc->base.state->active = crtc->active;
crtc->base.enabled = crtc->active;

plane_state = to_intel_plane_state(primary->state);
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index 7419e04b113f..5a277757ac2d 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -811,12 +811,8 @@ intel_check_sprite_plane(struct drm_plane *plane,
int max_scale, min_scale;
int pixel_size;

- intel_crtc = intel_crtc ? intel_crtc : to_intel_crtc(plane->crtc);
-
- if (!fb) {
- state->visible = false;
- goto finish;
- }
+ if (!fb)
+ return 0;

/* Don't modify another pipe's plane */
if (intel_plane->pipe != intel_crtc->pipe) {
@@ -847,7 +843,7 @@ intel_check_sprite_plane(struct drm_plane *plane,
vscale = drm_rect_calc_vscale_relaxed(src, dst, min_scale, max_scale);
BUG_ON(vscale < 0);

- state->visible = drm_rect_clip_scaled(src, dst, clip, hscale, vscale);
+ state->visible = drm_rect_clip_scaled(src, dst, clip, hscale, vscale);

crtc_x = dst->x1;
crtc_y = dst->y1;
@@ -952,29 +948,6 @@ intel_check_sprite_plane(struct drm_plane *plane,
dst->y1 = crtc_y;
dst->y2 = crtc_y + crtc_h;

-finish:
- /*
- * If the sprite is completely covering the primary plane,
- * we can disable the primary and save power.
- */
- if (intel_crtc->active) {
- intel_crtc->atomic.fb_bits |=
- INTEL_FRONTBUFFER_SPRITE(intel_crtc->pipe);
-
- if (intel_wm_need_update(plane, &state->base))
- intel_crtc->atomic.update_wm = true;
-
- if (!state->visible) {
- /*
- * Avoid underruns when disabling the sprite.
- * FIXME remove once watermark updates are done properly.
- */
- intel_crtc->atomic.wait_vblank = true;
- intel_crtc->atomic.update_sprite_watermarks |=
- (1 << drm_plane_index(plane));
- }
- }
-
return 0;