drm: Consolidate connector arrays in drm_atomic_state
[cascardo/linux.git] / drivers / gpu / drm / drm_atomic_helper.c
index 4befe25..b4e2e98 100644 (file)
@@ -110,8 +110,10 @@ static int handle_conflicting_encoders(struct drm_atomic_state *state,
 
                if (funcs->atomic_best_encoder)
                        new_encoder = funcs->atomic_best_encoder(connector, conn_state);
-               else
+               else if (funcs->best_encoder)
                        new_encoder = funcs->best_encoder(connector);
+               else
+                       new_encoder = drm_atomic_helper_best_encoder(connector);
 
                if (new_encoder) {
                        if (encoder_mask & (1 << drm_encoder_index(new_encoder))) {
@@ -384,8 +386,6 @@ mode_fixup(struct drm_atomic_state *state)
                 */
                encoder = conn_state->best_encoder;
                funcs = encoder->helper_private;
-               if (!funcs)
-                       continue;
 
                ret = drm_bridge_mode_fixup(encoder->bridge, &crtc_state->mode,
                                &crtc_state->adjusted_mode);
@@ -394,7 +394,7 @@ mode_fixup(struct drm_atomic_state *state)
                        return -EINVAL;
                }
 
-               if (funcs->atomic_check) {
+               if (funcs && funcs->atomic_check) {
                        ret = funcs->atomic_check(encoder, crtc_state,
                                                  conn_state);
                        if (ret) {
@@ -402,7 +402,7 @@ mode_fixup(struct drm_atomic_state *state)
                                                 encoder->base.id, encoder->name);
                                return ret;
                        }
-               } else if (funcs->mode_fixup) {
+               } else if (funcs && funcs->mode_fixup) {
                        ret = funcs->mode_fixup(encoder, &crtc_state->mode,
                                                &crtc_state->adjusted_mode);
                        if (!ret) {
@@ -416,6 +416,9 @@ mode_fixup(struct drm_atomic_state *state)
        for_each_crtc_in_state(state, crtc, crtc_state, i) {
                const struct drm_crtc_helper_funcs *funcs;
 
+               if (!crtc_state->enable)
+                       continue;
+
                if (!crtc_state->mode_changed &&
                    !crtc_state->connectors_changed)
                        continue;
@@ -613,7 +616,7 @@ drm_atomic_helper_check_planes(struct drm_device *dev,
                if (!funcs || !funcs->atomic_check)
                        continue;
 
-               ret = funcs->atomic_check(crtc, state->crtc_states[i]);
+               ret = funcs->atomic_check(crtc, crtc_state);
                if (ret) {
                        DRM_DEBUG_ATOMIC("[CRTC:%d:%s] atomic driver check failed\n",
                                         crtc->base.id, crtc->name);
@@ -707,12 +710,14 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
                drm_bridge_disable(encoder->bridge);
 
                /* Right function depends upon target state. */
-               if (connector->state->crtc && funcs->prepare)
-                       funcs->prepare(encoder);
-               else if (funcs->disable)
-                       funcs->disable(encoder);
-               else
-                       funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
+               if (funcs) {
+                       if (connector->state->crtc && funcs->prepare)
+                               funcs->prepare(encoder);
+                       else if (funcs->disable)
+                               funcs->disable(encoder);
+                       else if (funcs->dpms)
+                               funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
+               }
 
                drm_bridge_post_disable(encoder->bridge);
        }
@@ -873,7 +878,7 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
                 * Each encoder has at most one connector (since we always steal
                 * it away), so we won't call mode_set hooks twice.
                 */
-               if (funcs->mode_set)
+               if (funcs && funcs->mode_set)
                        funcs->mode_set(encoder, mode, adjusted_mode);
 
                drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode);
@@ -974,17 +979,29 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
                 */
                drm_bridge_pre_enable(encoder->bridge);
 
-               if (funcs->enable)
-                       funcs->enable(encoder);
-               else
-                       funcs->commit(encoder);
+               if (funcs) {
+                       if (funcs->enable)
+                               funcs->enable(encoder);
+                       else if (funcs->commit)
+                               funcs->commit(encoder);
+               }
 
                drm_bridge_enable(encoder->bridge);
        }
 }
 EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables);
 
-static void wait_for_fences(struct drm_device *dev,
+/**
+ * drm_atomic_helper_wait_for_fences - wait for fences stashed in plane state
+ * @dev: DRM device
+ * @state: atomic state object with old state structures
+ *
+ * For implicit sync, driver should fish the exclusive fence out from the
+ * incoming fb's and stash it in the drm_plane_state.  This is called after
+ * drm_atomic_helper_swap_state() so it uses the current plane state (and
+ * just uses the atomic state to find the changed planes)
+ */
+void drm_atomic_helper_wait_for_fences(struct drm_device *dev,
                            struct drm_atomic_state *state)
 {
        struct drm_plane *plane;
@@ -1002,6 +1019,7 @@ static void wait_for_fences(struct drm_device *dev,
                plane->state->fence = NULL;
        }
 }
+EXPORT_SYMBOL(drm_atomic_helper_wait_for_fences);
 
 /**
  * drm_atomic_helper_framebuffer_changed - check if framebuffer has changed
@@ -1092,6 +1110,8 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
                                        drm_crtc_vblank_count(crtc),
                                msecs_to_jiffies(50));
 
+               WARN(!ret, "[CRTC:%d] vblank wait timed out\n", crtc->base.id);
+
                drm_crtc_vblank_put(crtc);
        }
 }
@@ -1101,13 +1121,13 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks);
  * drm_atomic_helper_commit - commit validated state object
  * @dev: DRM device
  * @state: the driver state object
- * @async: asynchronous commit
+ * @nonblocking: whether nonblocking behavior is requested.
  *
  * This function commits a with drm_atomic_helper_check() pre-validated state
  * object. This can still fail when e.g. the framebuffer reservation fails. For
- * now this doesn't implement asynchronous commits.
+ * now this doesn't implement nonblocking commits.
  *
- * Note that right now this function does not support async commits, and hence
+ * Note that right now this function does not support nonblocking commits, hence
  * driver writers must implement their own version for now. Also note that the
  * default ordering of how the various stages are called is to match the legacy
  * modeset helper library closest. One peculiarity of that is that it doesn't
@@ -1128,11 +1148,11 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks);
  */
 int drm_atomic_helper_commit(struct drm_device *dev,
                             struct drm_atomic_state *state,
-                            bool async)
+                            bool nonblock)
 {
        int ret;
 
-       if (async)
+       if (nonblock)
                return -EBUSY;
 
        ret = drm_atomic_helper_prepare_planes(dev, state);
@@ -1163,7 +1183,7 @@ int drm_atomic_helper_commit(struct drm_device *dev,
         * current layout.
         */
 
-       wait_for_fences(dev, state);
+       drm_atomic_helper_wait_for_fences(dev, state);
 
        drm_atomic_helper_commit_modeset_disables(dev, state);
 
@@ -1182,20 +1202,20 @@ int drm_atomic_helper_commit(struct drm_device *dev,
 EXPORT_SYMBOL(drm_atomic_helper_commit);
 
 /**
- * DOC: implementing async commit
+ * DOC: implementing nonblocking commit
  *
- * For now the atomic helpers don't support async commit directly. If there is
- * real need it could be added though, using the dma-buf fence infrastructure
- * for generic synchronization with outstanding rendering.
+ * For now the atomic helpers don't support nonblocking commit directly. If
+ * there is real need it could be added though, using the dma-buf fence
+ * infrastructure for generic synchronization with outstanding rendering.
  *
- * For now drivers have to implement async commit themselves, with the following
- * sequence being the recommended one:
+ * For now drivers have to implement nonblocking commit themselves, with the
+ * following sequence being the recommended one:
  *
  * 1. Run drm_atomic_helper_prepare_planes() first. This is the only function
  * which commit needs to call which can fail, so we want to run it first and
  * synchronously.
  *
- * 2. Synchronize with any outstanding asynchronous commit worker threads which
+ * 2. Synchronize with any outstanding nonblocking commit worker threads which
  * might be affected the new state update. This can be done by either cancelling
  * or flushing the work items, depending upon whether the driver can deal with
  * cancelled updates. Note that it is important to ensure that the framebuffer
@@ -1209,9 +1229,9 @@ EXPORT_SYMBOL(drm_atomic_helper_commit);
  * 3. The software state is updated synchronously with
  * drm_atomic_helper_swap_state(). Doing this under the protection of all modeset
  * locks means concurrent callers never see inconsistent state. And doing this
- * while it's guaranteed that no relevant async worker runs means that async
- * workers do not need grab any locks. Actually they must not grab locks, for
- * otherwise the work flushing will deadlock.
+ * while it's guaranteed that no relevant nonblocking worker runs means that
+ * nonblocking workers do not need grab any locks. Actually they must not grab
+ * locks, for otherwise the work flushing will deadlock.
  *
  * 4. Schedule a work item to do all subsequent steps, using the split-out
  * commit helpers: a) pre-plane commit b) plane commit c) post-plane commit and
@@ -1234,16 +1254,12 @@ EXPORT_SYMBOL(drm_atomic_helper_commit);
 int drm_atomic_helper_prepare_planes(struct drm_device *dev,
                                     struct drm_atomic_state *state)
 {
-       int nplanes = dev->mode_config.num_total_plane;
-       int ret, i;
+       struct drm_plane *plane;
+       struct drm_plane_state *plane_state;
+       int ret, i, j;
 
-       for (i = 0; i < nplanes; i++) {
+       for_each_plane_in_state(state, plane, plane_state, i) {
                const struct drm_plane_helper_funcs *funcs;
-               struct drm_plane *plane = state->planes[i];
-               struct drm_plane_state *plane_state = state->plane_states[i];
-
-               if (!plane)
-                       continue;
 
                funcs = plane->helper_private;
 
@@ -1257,12 +1273,10 @@ int drm_atomic_helper_prepare_planes(struct drm_device *dev,
        return 0;
 
 fail:
-       for (i--; i >= 0; i--) {
+       for_each_plane_in_state(state, plane, plane_state, j) {
                const struct drm_plane_helper_funcs *funcs;
-               struct drm_plane *plane = state->planes[i];
-               struct drm_plane_state *plane_state = state->plane_states[i];
 
-               if (!plane)
+               if (j >= i)
                        continue;
 
                funcs = plane->helper_private;
@@ -1549,35 +1563,26 @@ void drm_atomic_helper_swap_state(struct drm_device *dev,
                                  struct drm_atomic_state *state)
 {
        int i;
+       struct drm_connector *connector;
+       struct drm_connector_state *conn_state;
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *crtc_state;
+       struct drm_plane *plane;
+       struct drm_plane_state *plane_state;
 
-       for (i = 0; i < state->num_connector; i++) {
-               struct drm_connector *connector = state->connectors[i];
-
-               if (!connector)
-                       continue;
-
+       for_each_connector_in_state(state, connector, conn_state, i) {
                connector->state->state = state;
-               swap(state->connector_states[i], connector->state);
+               swap(state->connectors[i].state, connector->state);
                connector->state->state = NULL;
        }
 
-       for (i = 0; i < dev->mode_config.num_crtc; i++) {
-               struct drm_crtc *crtc = state->crtcs[i];
-
-               if (!crtc)
-                       continue;
-
+       for_each_crtc_in_state(state, crtc, crtc_state, i) {
                crtc->state->state = state;
                swap(state->crtc_states[i], crtc->state);
                crtc->state->state = NULL;
        }
 
-       for (i = 0; i < dev->mode_config.num_total_plane; i++) {
-               struct drm_plane *plane = state->planes[i];
-
-               if (!plane)
-                       continue;
-
+       for_each_plane_in_state(state, plane, plane_state, i) {
                plane->state->state = state;
                swap(state->plane_states[i], plane->state);
                plane->state->state = NULL;
@@ -2358,11 +2363,11 @@ retry:
                goto fail;
        }
 
-       ret = drm_atomic_async_commit(state);
+       ret = drm_atomic_nonblocking_commit(state);
        if (ret != 0)
                goto fail;
 
-       /* Driver takes ownership of state on successful async commit. */
+       /* Driver takes ownership of state on successful commit. */
        return 0;
 fail:
        if (ret == -EDEADLK)
@@ -2394,7 +2399,7 @@ EXPORT_SYMBOL(drm_atomic_helper_page_flip);
  * This is the main helper function provided by the atomic helper framework for
  * implementing the legacy DPMS connector interface. It computes the new desired
  * ->active state for the corresponding CRTC (if the connector is enabled) and
- *  updates it.
+ * updates it.
  *
  * Returns:
  * Returns 0 on success, negative errno numbers on failure.
@@ -2467,6 +2472,23 @@ backoff:
 }
 EXPORT_SYMBOL(drm_atomic_helper_connector_dpms);
 
+/**
+ * drm_atomic_helper_best_encoder - Helper for &drm_connector_helper_funcs
+ *                                  ->best_encoder callback
+ * @connector: Connector control structure
+ *
+ * This is a &drm_connector_helper_funcs ->best_encoder callback helper for
+ * connectors that support exactly 1 encoder, statically determined at driver
+ * init time.
+ */
+struct drm_encoder *
+drm_atomic_helper_best_encoder(struct drm_connector *connector)
+{
+       WARN_ON(connector->encoder_ids[1]);
+       return drm_encoder_find(connector->dev, connector->encoder_ids[0]);
+}
+EXPORT_SYMBOL(drm_atomic_helper_best_encoder);
+
 /**
  * DOC: atomic state reset and initialization
  *
@@ -2497,12 +2519,9 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_dpms);
  */
 void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc)
 {
-       if (crtc->state) {
-               drm_property_unreference_blob(crtc->state->mode_blob);
-               drm_property_unreference_blob(crtc->state->degamma_lut);
-               drm_property_unreference_blob(crtc->state->ctm);
-               drm_property_unreference_blob(crtc->state->gamma_lut);
-       }
+       if (crtc->state)
+               __drm_atomic_helper_crtc_destroy_state(crtc->state);
+
        kfree(crtc->state);
        crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL);
 
@@ -2566,15 +2585,13 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state);
 
 /**
  * __drm_atomic_helper_crtc_destroy_state - release CRTC state
- * @crtc: CRTC object
  * @state: CRTC state object to release
  *
  * Releases all resources stored in the CRTC state without actually freeing
  * the memory of the CRTC state. This is useful for drivers that subclass the
  * CRTC state.
  */
-void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc,
-                                           struct drm_crtc_state *state)
+void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state)
 {
        drm_property_unreference_blob(state->mode_blob);
        drm_property_unreference_blob(state->degamma_lut);
@@ -2594,7 +2611,7 @@ EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state);
 void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc,
                                          struct drm_crtc_state *state)
 {
-       __drm_atomic_helper_crtc_destroy_state(crtc, state);
+       __drm_atomic_helper_crtc_destroy_state(state);
        kfree(state);
 }
 EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state);
@@ -2608,8 +2625,8 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state);
  */
 void drm_atomic_helper_plane_reset(struct drm_plane *plane)
 {
-       if (plane->state && plane->state->fb)
-               drm_framebuffer_unreference(plane->state->fb);
+       if (plane->state)
+               __drm_atomic_helper_plane_destroy_state(plane->state);
 
        kfree(plane->state);
        plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL);
@@ -2664,15 +2681,13 @@ EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state);
 
 /**
  * __drm_atomic_helper_plane_destroy_state - release plane state
- * @plane: plane object
  * @state: plane state object to release
  *
  * Releases all resources stored in the plane state without actually freeing
  * the memory of the plane state. This is useful for drivers that subclass the
  * plane state.
  */
-void __drm_atomic_helper_plane_destroy_state(struct drm_plane *plane,
-                                            struct drm_plane_state *state)
+void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state)
 {
        if (state->fb)
                drm_framebuffer_unreference(state->fb);
@@ -2690,7 +2705,7 @@ EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state);
 void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane,
                                           struct drm_plane_state *state)
 {
-       __drm_atomic_helper_plane_destroy_state(plane, state);
+       __drm_atomic_helper_plane_destroy_state(state);
        kfree(state);
 }
 EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state);
@@ -2730,6 +2745,9 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector)
        struct drm_connector_state *conn_state =
                kzalloc(sizeof(*conn_state), GFP_KERNEL);
 
+       if (connector->state)
+               __drm_atomic_helper_connector_destroy_state(connector->state);
+
        kfree(connector->state);
        __drm_atomic_helper_connector_reset(connector, conn_state);
 }
@@ -2748,6 +2766,8 @@ __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector,
                                            struct drm_connector_state *state)
 {
        memcpy(state, connector->state, sizeof(*state));
+       if (state->crtc)
+               drm_connector_reference(connector);
 }
 EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state);
 
@@ -2859,7 +2879,6 @@ EXPORT_SYMBOL(drm_atomic_helper_duplicate_state);
 
 /**
  * __drm_atomic_helper_connector_destroy_state - release connector state
- * @connector: connector object
  * @state: connector state object to release
  *
  * Releases all resources stored in the connector state without actually
@@ -2867,14 +2886,15 @@ EXPORT_SYMBOL(drm_atomic_helper_duplicate_state);
  * subclass the connector state.
  */
 void
-__drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
-                                           struct drm_connector_state *state)
+__drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state)
 {
        /*
         * This is currently a placeholder so that drivers that subclass the
         * state will automatically do the right thing if code is ever added
         * to this function.
         */
+       if (state->crtc)
+               drm_connector_unreference(state->connector);
 }
 EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state);
 
@@ -2889,7 +2909,7 @@ EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state);
 void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
                                          struct drm_connector_state *state)
 {
-       __drm_atomic_helper_connector_destroy_state(connector, state);
+       __drm_atomic_helper_connector_destroy_state(state);
        kfree(state);
 }
 EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state);