/* * Copyright (C) 2014 Red Hat * Copyright (C) 2014 Intel Corp. * Copyright (C) 2018 Intel Corp. * Copyright (c) 2020, The Linux Foundation. All rights reserved. * * 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>
*/
/** * DOC: overview * * This file contains the marshalling and demarshalling glue for the atomic UAPI * in all its forms: The monster ATOMIC IOCTL itself, code for GET_PROPERTY and * SET_PROPERTY IOCTLs. Plus interface functions for compatibility helpers and * drivers which have special needs to construct their own atomic updates, e.g. * for load detect or similar.
*/
/** * drm_atomic_set_mode_for_crtc - set mode for CRTC * @state: the CRTC whose incoming state to update * @mode: kernel-internal mode to use for the CRTC, or NULL to disable * * Set a mode (originating from the kernel) on the desired CRTC state and update * the enable property. * * RETURNS: * Zero on success, error code on failure. Cannot return -EDEADLK.
*/ int drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state, conststruct drm_display_mode *mode)
{ struct drm_crtc *crtc = state->crtc; struct drm_mode_modeinfo umode;
/* Early return for no change. */ if (mode && memcmp(&state->mode, mode, sizeof(*mode)) == 0) return 0;
/** * drm_atomic_set_mode_prop_for_crtc - set mode for CRTC * @state: the CRTC whose incoming state to update * @blob: pointer to blob property to use for mode * * Set a mode (originating from a blob property) on the desired CRTC state. * This function will take a reference on the blob property for the CRTC state, * and release the reference held on the state's existing mode property, if any * was set. * * RETURNS: * Zero on success, error code on failure. Cannot return -EDEADLK.
*/ int drm_atomic_set_mode_prop_for_crtc(struct drm_crtc_state *state, struct drm_property_blob *blob)
{ struct drm_crtc *crtc = state->crtc;
/** * drm_atomic_set_crtc_for_plane - set CRTC for plane * @plane_state: the plane whose incoming state to update * @crtc: CRTC to use for the plane * * Changing the assigned CRTC for a plane requires us to grab the lock and state * for the new CRTC, as needed. This function takes care of all these details * besides updating the pointer in the state object itself. * * Returns: * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK * then the w/w mutex code has detected a deadlock and the entire atomic * sequence must be restarted. All other errors are fatal.
*/ int
drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, struct drm_crtc *crtc)
{ struct drm_plane *plane = plane_state->plane; struct drm_crtc_state *crtc_state; /* Nothing to do for same crtc*/ if (plane_state->crtc == crtc) return 0; if (plane_state->crtc) {
crtc_state = drm_atomic_get_crtc_state(plane_state->state,
plane_state->crtc); if (WARN_ON(IS_ERR(crtc_state))) return PTR_ERR(crtc_state);
if (crtc) {
crtc_state = drm_atomic_get_crtc_state(plane_state->state,
crtc); if (IS_ERR(crtc_state)) return PTR_ERR(crtc_state);
crtc_state->plane_mask |= drm_plane_mask(plane);
}
if (crtc)
drm_dbg_atomic(plane->dev, "Link [PLANE:%d:%s] state %p to [CRTC:%d:%s]\n",
plane->base.id, plane->name, plane_state,
crtc->base.id, crtc->name); else
drm_dbg_atomic(plane->dev, "Link [PLANE:%d:%s] state %p to [NOCRTC]\n",
plane->base.id, plane->name, plane_state);
/** * drm_atomic_set_fb_for_plane - set framebuffer for plane * @plane_state: atomic state object for the plane * @fb: fb to use for the plane * * Changing the assigned framebuffer for a plane requires us to grab a reference * to the new fb and drop the reference to the old fb, if there is one. This * function takes care of all these details besides updating the pointer in the * state object itself.
*/ void
drm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state, struct drm_framebuffer *fb)
{ struct drm_plane *plane = plane_state->plane;
if (fb)
drm_dbg_atomic(plane->dev, "Set [FB:%d] for [PLANE:%d:%s] state %p\n",
fb->base.id, plane->base.id, plane->name,
plane_state); else
drm_dbg_atomic(plane->dev, "Set [NOFB] for [PLANE:%d:%s] state %p\n",
plane->base.id, plane->name, plane_state);
/** * drm_atomic_set_crtc_for_connector - set CRTC for connector * @conn_state: atomic state object for the connector * @crtc: CRTC to use for the connector * * Changing the assigned CRTC for a connector requires us to grab the lock and * state for the new CRTC, as needed. This function takes care of all these * details besides updating the pointer in the state object itself. * * Returns: * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK * then the w/w mutex code has detected a deadlock and the entire atomic * sequence must be restarted. All other errors are fatal.
*/ int
drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state, struct drm_crtc *crtc)
{ struct drm_connector *connector = conn_state->connector; struct drm_crtc_state *crtc_state;
if (conn_state->crtc == crtc) return 0;
if (conn_state->crtc) {
crtc_state = drm_atomic_get_new_crtc_state(conn_state->state,
conn_state->crtc);
ret = drm_writeback_set_fb(conn_state, fb); if (ret < 0) return ret;
if (fb)
drm_dbg_atomic(conn->dev, "Set [FB:%d] for connector state %p\n",
fb->base.id, conn_state); else
drm_dbg_atomic(conn->dev, "Set [NOFB] for connector state %p\n",
conn_state);
plane_state = drm_atomic_get_plane_state(state, plane); if (IS_ERR(plane_state)) {
ret = PTR_ERR(plane_state); break;
}
if (async_flip) { /* no-op changes are always allowed */
ret = drm_atomic_plane_get_property(plane, plane_state,
prop, &old_val);
ret = drm_atomic_check_prop_changes(ret, old_val, prop_value, prop);
/* fail everything that isn't no-op or a pure flip */ if (ret && prop != config->prop_fb_id &&
prop != config->prop_in_fence_fd &&
prop != config->prop_fb_damage_clips) { break;
}
if (ret && plane->type != DRM_PLANE_TYPE_PRIMARY) { /* ask the driver if this non-primary plane is supported */ if (plane_funcs && plane_funcs->atomic_async_check)
ret = plane_funcs->atomic_async_check(plane, state, true);
if (ret) {
drm_dbg_atomic(prop->dev, "[PLANE:%d:%s] does not support async flips\n",
obj->id, plane->name); break;
}
}
}
ret = drm_atomic_plane_set_property(plane,
plane_state, file_priv,
prop, prop_value); break;
} default:
drm_dbg_atomic(prop->dev, "[OBJECT:%d] has no properties\n", obj->id);
ret = -EINVAL; break;
}
/** * DOC: explicit fencing properties * * Explicit fencing allows userspace to control the buffer synchronization * between devices. A Fence or a group of fences are transferred to/from * userspace using Sync File fds and there are two DRM properties for that. * IN_FENCE_FD on each DRM Plane to send fences to the kernel and * OUT_FENCE_PTR on each DRM CRTC to receive fences from the kernel. * * As a contrast, with implicit fencing the kernel keeps track of any * ongoing rendering, and automatically ensures that the atomic update waits * for any pending rendering to complete. This is usually tracked in &struct * dma_resv which can also contain mandatory kernel fences. Implicit syncing * is how Linux traditionally worked (e.g. DRI2/3 on X.org), whereas explicit * fencing is what Android wants. * * "IN_FENCE_FD”: * Use this property to pass a fence that DRM should wait on before * proceeding with the Atomic Commit request and show the framebuffer for * the plane on the screen. The fence can be either a normal fence or a * merged one, the sync_file framework will handle both cases and use a * fence_array if a merged fence is received. Passing -1 here means no * fences to wait on. * * If the Atomic Commit request has the DRM_MODE_ATOMIC_TEST_ONLY flag * it will only check if the Sync File is a valid one. * * On the driver side the fence is stored on the @fence parameter of * &struct drm_plane_state. Drivers which also support implicit fencing * should extract the implicit fence using drm_gem_plane_helper_prepare_fb(), * to make sure there's consistent behaviour between drivers in precedence * of implicit vs. explicit fencing. * * "OUT_FENCE_PTR”: * Use this property to pass a file descriptor pointer to DRM. Once the * Atomic Commit request call returns OUT_FENCE_PTR will be filled with * the file descriptor number of a Sync File. This Sync File contains the * CRTC fence that will be signaled when all framebuffers present on the * Atomic Commit * request for that given CRTC are scanned out on the * screen. * * The Atomic Commit request fails if a invalid pointer is passed. If the * Atomic Commit request fails for any other reason the out fence fd * returned will be -1. On a Atomic Commit with the * DRM_MODE_ATOMIC_TEST_ONLY flag the out fence will also be set to -1. * * Note that out-fences don't have a special interface to drivers and are * internally represented by a &struct drm_pending_vblank_event in struct * &drm_crtc_state, which is also used by the nonblocking atomic commit * helpers and for the DRM event handling for existing userspace.
*/
wb_conn = drm_connector_to_writeback(conn);
fence = drm_writeback_get_out_fence(wb_conn); if (!fence) return -ENOMEM;
ret = setup_out_fence(&f[(*num_fences)++], fence); if (ret) {
dma_fence_put(fence); return ret;
}
conn_state->writeback_job->out_fence = fence;
}
/* * Having this flag means user mode pends on event which will never * reach due to lack of at least one CRTC for signaling
*/ if (c == 0 && (arg->flags & DRM_MODE_PAGE_FLIP_EVENT)) {
drm_dbg_atomic(dev, "need at least one CRTC for DRM_MODE_PAGE_FLIP_EVENT"); return -EINVAL;
}
if (install_fds) { for (i = 0; i < num_fences; i++)
fd_install(fence_state[i].fd,
fence_state[i].sync_file->file);
kfree(fence_state); return;
}
for_each_new_crtc_in_state(state, crtc, crtc_state, i) { struct drm_pending_vblank_event *event = crtc_state->event; /* * Free the allocated event. drm_atomic_helper_setup_commit * can allocate an event too, so only free it if it's ours * to prevent a double free in drm_atomic_state_clear.
*/ if (event && (event->base.fence || event->base.file_priv)) {
drm_event_cancel_free(dev, &event->base);
crtc_state->event = NULL;
}
}
if (!fence_state) return;
for (i = 0; i < num_fences; i++) { if (fence_state[i].sync_file)
fput(fence_state[i].sync_file->file); if (fence_state[i].fd >= 0)
put_unused_fd(fence_state[i].fd);
/* If this fails log error to the user */ if (fence_state[i].out_fence_ptr &&
put_user(-1, fence_state[i].out_fence_ptr))
drm_dbg_atomic(dev, "Couldn't clear out_fence_ptr\n");
}
/* disallow for drivers not supporting atomic: */ if (!drm_core_check_feature(dev, DRIVER_ATOMIC)) return -EOPNOTSUPP;
/* disallow for userspace that has not enabled atomic cap (even * though this may be a bit overkill, since legacy userspace * wouldn't know how to call this ioctl)
*/ if (!file_priv->atomic) {
drm_dbg_atomic(dev, "commit failed: atomic cap not enabled\n"); return -EINVAL;
}
if (arg->reserved) {
drm_dbg_atomic(dev, "commit failed: reserved field set\n"); return -EINVAL;
}
if (arg->flags & DRM_MODE_PAGE_FLIP_ASYNC) { if (!dev->mode_config.async_page_flip) {
drm_dbg_atomic(dev, "commit failed: DRM_MODE_PAGE_FLIP_ASYNC not supported\n"); return -EINVAL;
}
async_flip = true;
}
/* can't test and expect an event at the same time. */ if ((arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) &&
(arg->flags & DRM_MODE_PAGE_FLIP_EVENT)) {
drm_dbg_atomic(dev, "commit failed: page-flip event requested with test-only commit\n"); return -EINVAL;
}
state = drm_atomic_state_alloc(dev); if (!state) return -ENOMEM;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.