|
@@ -0,0 +1,355 @@
|
|
|
+/*
|
|
|
+ * Copyright (C) 2014 Red Hat
|
|
|
+ * Copyright (C) 2014 Intel Corp.
|
|
|
+ *
|
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
|
+ *
|
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
|
+ * all copies or substantial portions of the Software.
|
|
|
+ *
|
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
|
+ * OTHER DEALINGS IN THE SOFTWARE.
|
|
|
+ *
|
|
|
+ * Authors:
|
|
|
+ * Rob Clark <robdclark@gmail.com>
|
|
|
+ * Daniel Vetter <daniel.vetter@ffwll.ch>
|
|
|
+ */
|
|
|
+
|
|
|
+#include <drm/drmP.h>
|
|
|
+#include <drm/drm_atomic.h>
|
|
|
+#include <drm/drm_plane_helper.h>
|
|
|
+#include <drm/drm_crtc_helper.h>
|
|
|
+
|
|
|
+static void
|
|
|
+drm_atomic_helper_plane_changed(struct drm_atomic_state *state,
|
|
|
+ struct drm_plane_state *plane_state,
|
|
|
+ struct drm_plane *plane)
|
|
|
+{
|
|
|
+ struct drm_crtc_state *crtc_state;
|
|
|
+
|
|
|
+ if (plane->state->crtc) {
|
|
|
+ crtc_state = state->crtc_states[drm_crtc_index(plane->crtc)];
|
|
|
+
|
|
|
+ if (WARN_ON(!crtc_state))
|
|
|
+ return;
|
|
|
+
|
|
|
+ crtc_state->planes_changed = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (plane_state->crtc) {
|
|
|
+ crtc_state =
|
|
|
+ state->crtc_states[drm_crtc_index(plane_state->crtc)];
|
|
|
+
|
|
|
+ if (WARN_ON(!crtc_state))
|
|
|
+ return;
|
|
|
+
|
|
|
+ crtc_state->planes_changed = true;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * drm_atomic_helper_check - validate state object
|
|
|
+ * @dev: DRM device
|
|
|
+ * @state: the driver state object
|
|
|
+ *
|
|
|
+ * Check the state object to see if the requested state is physically possible.
|
|
|
+ * Only crtcs and planes have check callbacks, so for any additional (global)
|
|
|
+ * checking that a driver needs it can simply wrap that around this function.
|
|
|
+ * Drivers without such needs can directly use this as their ->atomic_check()
|
|
|
+ * callback.
|
|
|
+ *
|
|
|
+ * RETURNS
|
|
|
+ * Zero for success or -errno
|
|
|
+ */
|
|
|
+int drm_atomic_helper_check(struct drm_device *dev,
|
|
|
+ struct drm_atomic_state *state)
|
|
|
+{
|
|
|
+ int nplanes = dev->mode_config.num_total_plane;
|
|
|
+ int ncrtcs = dev->mode_config.num_crtc;
|
|
|
+ int i, ret = 0;
|
|
|
+
|
|
|
+ for (i = 0; i < nplanes; i++) {
|
|
|
+ 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;
|
|
|
+
|
|
|
+ drm_atomic_helper_plane_changed(state, plane_state, plane);
|
|
|
+
|
|
|
+ if (!funcs || !funcs->atomic_check)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ ret = funcs->atomic_check(plane, plane_state);
|
|
|
+ if (ret) {
|
|
|
+ DRM_DEBUG_KMS("[PLANE:%d] atomic check failed\n",
|
|
|
+ plane->base.id);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < ncrtcs; i++) {
|
|
|
+ struct drm_crtc_helper_funcs *funcs;
|
|
|
+ struct drm_crtc *crtc = state->crtcs[i];
|
|
|
+
|
|
|
+ if (!crtc)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ funcs = crtc->helper_private;
|
|
|
+
|
|
|
+ if (!funcs || !funcs->atomic_check)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ ret = funcs->atomic_check(crtc, state->crtc_states[i]);
|
|
|
+ if (ret) {
|
|
|
+ DRM_DEBUG_KMS("[CRTC:%d] atomic check failed\n",
|
|
|
+ crtc->base.id);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(drm_atomic_helper_check);
|
|
|
+
|
|
|
+/**
|
|
|
+ * drm_atomic_helper_prepare_planes - prepare plane resources after commit
|
|
|
+ * @dev: DRM device
|
|
|
+ * @state: atomic state object with old state structures
|
|
|
+ *
|
|
|
+ * This function prepares plane state, specifically framebuffers, for the new
|
|
|
+ * configuration. If any failure is encountered this function will call
|
|
|
+ * ->cleanup_fb on any already successfully prepared framebuffer.
|
|
|
+ *
|
|
|
+ * Returns:
|
|
|
+ * 0 on success, negative error code on failure.
|
|
|
+ */
|
|
|
+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;
|
|
|
+
|
|
|
+ for (i = 0; i < nplanes; i++) {
|
|
|
+ struct drm_plane_helper_funcs *funcs;
|
|
|
+ struct drm_plane *plane = state->planes[i];
|
|
|
+ struct drm_framebuffer *fb;
|
|
|
+
|
|
|
+ if (!plane)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ funcs = plane->helper_private;
|
|
|
+
|
|
|
+ fb = state->plane_states[i]->fb;
|
|
|
+
|
|
|
+ if (fb && funcs->prepare_fb) {
|
|
|
+ ret = funcs->prepare_fb(plane, fb);
|
|
|
+ if (ret)
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+fail:
|
|
|
+ for (i--; i >= 0; i--) {
|
|
|
+ struct drm_plane_helper_funcs *funcs;
|
|
|
+ struct drm_plane *plane = state->planes[i];
|
|
|
+ struct drm_framebuffer *fb;
|
|
|
+
|
|
|
+ if (!plane)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ funcs = plane->helper_private;
|
|
|
+
|
|
|
+ fb = state->plane_states[i]->fb;
|
|
|
+
|
|
|
+ if (fb && funcs->cleanup_fb)
|
|
|
+ funcs->cleanup_fb(plane, fb);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(drm_atomic_helper_prepare_planes);
|
|
|
+
|
|
|
+/**
|
|
|
+ * drm_atomic_helper_commit_planes - commit plane state
|
|
|
+ * @dev: DRM device
|
|
|
+ * @state: atomic state
|
|
|
+ *
|
|
|
+ * This function commits the new plane state using the plane and atomic helper
|
|
|
+ * functions for planes and crtcs. It assumes that the atomic state has already
|
|
|
+ * been pushed into the relevant object state pointers, since this step can no
|
|
|
+ * longer fail.
|
|
|
+ *
|
|
|
+ * It still requires the global state object @state to know which planes and
|
|
|
+ * crtcs need to be updated though.
|
|
|
+ */
|
|
|
+void drm_atomic_helper_commit_planes(struct drm_device *dev,
|
|
|
+ struct drm_atomic_state *state)
|
|
|
+{
|
|
|
+ int nplanes = dev->mode_config.num_total_plane;
|
|
|
+ int ncrtcs = dev->mode_config.num_crtc;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < ncrtcs; i++) {
|
|
|
+ struct drm_crtc_helper_funcs *funcs;
|
|
|
+ struct drm_crtc *crtc = state->crtcs[i];
|
|
|
+
|
|
|
+ if (!crtc)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ funcs = crtc->helper_private;
|
|
|
+
|
|
|
+ if (!funcs || !funcs->atomic_begin)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ funcs->atomic_begin(crtc);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < nplanes; i++) {
|
|
|
+ struct drm_plane_helper_funcs *funcs;
|
|
|
+ struct drm_plane *plane = state->planes[i];
|
|
|
+
|
|
|
+ if (!plane)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ funcs = plane->helper_private;
|
|
|
+
|
|
|
+ if (!funcs || !funcs->atomic_update)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ funcs->atomic_update(plane);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < ncrtcs; i++) {
|
|
|
+ struct drm_crtc_helper_funcs *funcs;
|
|
|
+ struct drm_crtc *crtc = state->crtcs[i];
|
|
|
+
|
|
|
+ if (!crtc)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ funcs = crtc->helper_private;
|
|
|
+
|
|
|
+ if (!funcs || !funcs->atomic_flush)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ funcs->atomic_flush(crtc);
|
|
|
+ }
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(drm_atomic_helper_commit_planes);
|
|
|
+
|
|
|
+/**
|
|
|
+ * drm_atomic_helper_cleanup_planes - cleanup plane resources after commit
|
|
|
+ * @dev: DRM device
|
|
|
+ * @old_state: atomic state object with old state structures
|
|
|
+ *
|
|
|
+ * This function cleans up plane state, specifically framebuffers, from the old
|
|
|
+ * configuration. Hence the old configuration must be perserved in @old_state to
|
|
|
+ * be able to call this function.
|
|
|
+ *
|
|
|
+ * This function must also be called on the new state when the atomic update
|
|
|
+ * fails at any point after calling drm_atomic_helper_prepare_planes().
|
|
|
+ */
|
|
|
+void drm_atomic_helper_cleanup_planes(struct drm_device *dev,
|
|
|
+ struct drm_atomic_state *old_state)
|
|
|
+{
|
|
|
+ int nplanes = dev->mode_config.num_total_plane;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < nplanes; i++) {
|
|
|
+ struct drm_plane_helper_funcs *funcs;
|
|
|
+ struct drm_plane *plane = old_state->planes[i];
|
|
|
+ struct drm_framebuffer *old_fb;
|
|
|
+
|
|
|
+ if (!plane)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ funcs = plane->helper_private;
|
|
|
+
|
|
|
+ old_fb = old_state->plane_states[i]->fb;
|
|
|
+
|
|
|
+ if (old_fb && funcs->cleanup_fb)
|
|
|
+ funcs->cleanup_fb(plane, old_fb);
|
|
|
+ }
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
|
|
|
+
|
|
|
+/**
|
|
|
+ * drm_atomic_helper_swap_state - store atomic state into current sw state
|
|
|
+ * @dev: DRM device
|
|
|
+ * @state: atomic state
|
|
|
+ *
|
|
|
+ * This function stores the atomic state into the current state pointers in all
|
|
|
+ * driver objects. It should be called after all failing steps have been done
|
|
|
+ * and succeeded, but before the actual hardware state is committed.
|
|
|
+ *
|
|
|
+ * For cleanup and error recovery the current state for all changed objects will
|
|
|
+ * be swaped into @state.
|
|
|
+ *
|
|
|
+ * With that sequence it fits perfectly into the plane prepare/cleanup sequence:
|
|
|
+ *
|
|
|
+ * 1. Call drm_atomic_helper_prepare_planes() with the staged atomic state.
|
|
|
+ *
|
|
|
+ * 2. Do any other steps that might fail.
|
|
|
+ *
|
|
|
+ * 3. Put the staged state into the current state pointers with this function.
|
|
|
+ *
|
|
|
+ * 4. Actually commit the hardware state.
|
|
|
+ *
|
|
|
+ * 5. Call drm_atomic_helper_cleanup_planes with @state, which since step 3
|
|
|
+ * contains the old state. Also do any other cleanup required with that state.
|
|
|
+ */
|
|
|
+void drm_atomic_helper_swap_state(struct drm_device *dev,
|
|
|
+ struct drm_atomic_state *state)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < dev->mode_config.num_connector; i++) {
|
|
|
+ struct drm_connector *connector = state->connectors[i];
|
|
|
+
|
|
|
+ if (!connector)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ connector->state->state = state;
|
|
|
+ swap(state->connector_states[i], 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;
|
|
|
+
|
|
|
+ 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;
|
|
|
+
|
|
|
+ plane->state->state = state;
|
|
|
+ swap(state->plane_states[i], plane->state);
|
|
|
+ plane->state->state = NULL;
|
|
|
+ }
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(drm_atomic_helper_swap_state);
|