struct drm_crtc_state *state, struct drm_property *property,
                uint64_t val)
 {
-       if (crtc->funcs->atomic_set_property)
+       struct drm_device *dev = crtc->dev;
+       struct drm_mode_config *config = &dev->mode_config;
+
+       /* FIXME: Mode prop is missing, which also controls ->enable. */
+       if (property == config->prop_active) {
+               state->active = val;
+       } else if (crtc->funcs->atomic_set_property)
                return crtc->funcs->atomic_set_property(crtc, state, property, val);
        return -EINVAL;
 }
         *
         * TODO: Add generic modeset state checks once we support those.
         */
+
+       if (state->active && !state->enable) {
+               DRM_DEBUG_KMS("[CRTC:%d] active without enabled\n",
+                             crtc->base.id);
+               return -EINVAL;
+       }
+
        return 0;
 }
 
                        if (!crtc)
                                continue;
 
-                       if (crtc_state->mode_changed) {
+                       if (crtc_state->mode_changed ||
+                           crtc_state->active_changed) {
                                DRM_DEBUG_KMS("[CRTC:%d] requires full modeset\n",
                                              crtc->base.id);
                                return -EINVAL;
 
        return 0;
 }
 
+static bool
+needs_modeset(struct drm_crtc_state *state)
+{
+       return state->mode_changed || state->active_changed;
+}
+
 /**
  * drm_atomic_helper_check - validate state object for modeset changes
  * @dev: DRM device
                crtc = state->crtcs[i];
                crtc_state = state->crtc_states[i];
 
-               if (!crtc || !crtc_state->mode_changed)
+               if (!crtc)
                        continue;
 
-               DRM_DEBUG_KMS("[CRTC:%d] needs full modeset, enable: %c\n",
+               /*
+                * We must set ->active_changed after walking connectors for
+                * otherwise an update that only changes active would result in
+                * a full modeset because update_connector_routing force that.
+                */
+               if (crtc->state->active != crtc_state->active) {
+                       DRM_DEBUG_KMS("[CRTC:%d] active changed\n",
+                                     crtc->base.id);
+                       crtc_state->active_changed = true;
+               }
+
+               if (!needs_modeset(crtc_state))
+                       continue;
+
+               DRM_DEBUG_KMS("[CRTC:%d] needs all connectors, enable: %c, active: %c\n",
                              crtc->base.id,
-                             crtc_state->enable ? 'y' : 'n');
+                             crtc_state->enable ? 'y' : 'n',
+                             crtc_state->active ? 'y' : 'n');
 
                ret = drm_atomic_add_affected_connectors(state, crtc);
                if (ret != 0)
                struct drm_connector *connector;
                struct drm_encoder_helper_funcs *funcs;
                struct drm_encoder *encoder;
+               struct drm_crtc_state *old_crtc_state;
 
                old_conn_state = old_state->connector_states[i];
                connector = old_state->connectors[i];
                if (!old_conn_state || !old_conn_state->crtc)
                        continue;
 
+               old_crtc_state = old_state->crtc_states[drm_crtc_index(old_conn_state->crtc)];
+
+               if (!old_crtc_state->active)
+                       continue;
+
                encoder = old_conn_state->best_encoder;
 
                /* We shouldn't get this far if we didn't previously have
        for (i = 0; i < ncrtcs; i++) {
                struct drm_crtc_helper_funcs *funcs;
                struct drm_crtc *crtc;
+               struct drm_crtc_state *old_crtc_state;
 
                crtc = old_state->crtcs[i];
+               old_crtc_state = old_state->crtc_states[i];
 
                /* Shut down everything that needs a full modeset. */
-               if (!crtc || !crtc->state->mode_changed)
+               if (!crtc || !needs_modeset(crtc->state))
+                       continue;
+
+               if (!old_crtc_state->active)
                        continue;
 
                funcs = crtc->helper_private;
                mode = &new_crtc_state->mode;
                adjusted_mode = &new_crtc_state->adjusted_mode;
 
+               if (!new_crtc_state->mode_changed)
+                       continue;
+
                /*
                 * Each encoder has at most one connector (since we always steal
                 * it away), so we won't call call mode_set hooks twice.
                crtc = old_state->crtcs[i];
 
                /* Need to filter out CRTCs where only planes change. */
-               if (!crtc || !crtc->state->mode_changed)
+               if (!crtc || !needs_modeset(crtc->state))
+                       continue;
+
+               if (!crtc->state->active)
                        continue;
 
                funcs = crtc->helper_private;
                if (!connector || !connector->state->best_encoder)
                        continue;
 
+               if (!connector->state->crtc->state->active)
+                       continue;
+
                encoder = connector->state->best_encoder;
                funcs = encoder->helper_private;
 
                WARN_ON(set->num_connectors);
 
                crtc_state->enable = false;
+               crtc_state->active = false;
 
                ret = drm_atomic_set_crtc_for_plane(primary_state, NULL);
                if (ret != 0)
        WARN_ON(!set->num_connectors);
 
        crtc_state->enable = true;
+       crtc_state->active = true;
        drm_mode_copy(&crtc_state->mode, set->mode);
 
        ret = drm_atomic_set_crtc_for_plane(primary_state, crtc);
 
        if (state) {
                state->mode_changed = false;
+               state->active_changed = false;
                state->planes_changed = false;
                state->event = NULL;
        }
 
  * @enable: whether the CRTC should be enabled, gates all other state
  * @active: whether the CRTC is actively displaying (used for DPMS)
  * @mode_changed: for use by helpers and drivers when computing state updates
+ * @active_changed: for use by helpers and drivers when computing state updates
  * @plane_mask: bitmask of (1 << drm_plane_index(plane)) of attached planes
  * @last_vblank_count: for helpers and drivers to capture the vblank of the
  *     update to ensure framebuffer cleanup isn't done too early
        /* computed state bits used by helpers and drivers */
        bool planes_changed : 1;
        bool mode_changed : 1;
+       bool active_changed : 1;
 
        /* attached planes bitmask:
         * WARNING: transitional helpers do not maintain plane_mask so
        struct drm_property *prop_crtc_h;
        struct drm_property *prop_fb_id;
        struct drm_property *prop_crtc_id;
+       struct drm_property *prop_active;
 
        /* DVI-I properties */
        struct drm_property *dvi_i_subconnector_property;