diff options
Diffstat (limited to 'drivers/gpu/drm')
41 files changed, 3137 insertions, 386 deletions
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 9292a761ea6d..c3cf64ce2891 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -14,7 +14,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ drm_info.o drm_debugfs.o drm_encoder_slave.o \ drm_trace_points.o drm_global.o drm_prime.o \ drm_rect.o drm_vma_manager.o drm_flip_work.o \ - drm_modeset_lock.o + drm_modeset_lock.o drm_atomic.o drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o @@ -23,7 +23,7 @@ drm-$(CONFIG_DRM_PANEL) += drm_panel.o drm-$(CONFIG_OF) += drm_of.o drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ - drm_plane_helper.o drm_dp_mst_topology.o + drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o drm_kms_helper-$(CONFIG_DRM_KMS_FB_HELPER) += drm_fb_helper.o drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o diff --git a/drivers/gpu/drm/README.drm b/drivers/gpu/drm/README.drm deleted file mode 100644 index b5b332722581..000000000000 --- a/drivers/gpu/drm/README.drm +++ /dev/null @@ -1,43 +0,0 @@ -************************************************************ -* For the very latest on DRI development, please see: * -* http://dri.freedesktop.org/ * -************************************************************ - -The Direct Rendering Manager (drm) is a device-independent kernel-level -device driver that provides support for the XFree86 Direct Rendering -Infrastructure (DRI). - -The DRM supports the Direct Rendering Infrastructure (DRI) in four major -ways: - - 1. The DRM provides synchronized access to the graphics hardware via - the use of an optimized two-tiered lock. - - 2. The DRM enforces the DRI security policy for access to the graphics - hardware by only allowing authenticated X11 clients access to - restricted regions of memory. - - 3. The DRM provides a generic DMA engine, complete with multiple - queues and the ability to detect the need for an OpenGL context - switch. - - 4. The DRM is extensible via the use of small device-specific modules - that rely extensively on the API exported by the DRM module. - - -Documentation on the DRI is available from: - http://dri.freedesktop.org/wiki/Documentation - http://sourceforge.net/project/showfiles.php?group_id=387 - http://dri.sourceforge.net/doc/ - -For specific information about kernel-level support, see: - - The Direct Rendering Manager, Kernel Support for the Direct Rendering - Infrastructure - http://dri.sourceforge.net/doc/drm_low_level.html - - Hardware Locking for the Direct Rendering Infrastructure - http://dri.sourceforge.net/doc/hardware_locking_low_level.html - - A Security Analysis of the Direct Rendering Infrastructure - http://dri.sourceforge.net/doc/security_low_level.html diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 9a0cc09e6653..0b164fb1c107 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -12,6 +12,7 @@ #include <linux/platform_device.h> #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_plane_helper.h> #include "armada_crtc.h" #include "armada_drm.h" #include "armada_fb.h" diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 9dc0fd5c1ea4..b7ee2634e47c 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -31,6 +31,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_plane_helper.h> #include "ast_drv.h" #include "ast_tables.h" diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c index 6b7efcf363d6..5ffd4895d040 100644 --- a/drivers/gpu/drm/bochs/bochs_kms.c +++ b/drivers/gpu/drm/bochs/bochs_kms.c @@ -6,6 +6,7 @@ */ #include "bochs.h" +#include <drm/drm_plane_helper.h> static int defx = 1024; static int defy = 768; diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index c7c5a9d91fa0..99d4a74ffeaf 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -16,6 +16,7 @@ */ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_plane_helper.h> #include <video/cirrus.h> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c new file mode 100644 index 000000000000..ed991ba66e21 --- /dev/null +++ b/drivers/gpu/drm/drm_atomic.c @@ -0,0 +1,628 @@ +/* + * 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> + +static void kfree_state(struct drm_atomic_state *state) +{ + kfree(state->connectors); + kfree(state->connector_states); + kfree(state->crtcs); + kfree(state->crtc_states); + kfree(state->planes); + kfree(state->plane_states); + kfree(state); +} + +/** + * drm_atomic_state_alloc - allocate atomic state + * @dev: DRM device + * + * This allocates an empty atomic state to track updates. + */ +struct drm_atomic_state * +drm_atomic_state_alloc(struct drm_device *dev) +{ + struct drm_atomic_state *state; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + state->crtcs = kcalloc(dev->mode_config.num_crtc, + sizeof(*state->crtcs), GFP_KERNEL); + if (!state->crtcs) + goto fail; + state->crtc_states = kcalloc(dev->mode_config.num_crtc, + sizeof(*state->crtc_states), GFP_KERNEL); + if (!state->crtc_states) + goto fail; + state->planes = kcalloc(dev->mode_config.num_total_plane, + sizeof(*state->planes), GFP_KERNEL); + if (!state->planes) + goto fail; + state->plane_states = kcalloc(dev->mode_config.num_total_plane, + sizeof(*state->plane_states), GFP_KERNEL); + if (!state->plane_states) + goto fail; + state->connectors = kcalloc(dev->mode_config.num_connector, + sizeof(*state->connectors), + GFP_KERNEL); + if (!state->connectors) + goto fail; + state->connector_states = kcalloc(dev->mode_config.num_connector, + sizeof(*state->connector_states), + GFP_KERNEL); + if (!state->connector_states) + goto fail; + + state->dev = dev; + + DRM_DEBUG_KMS("Allocate atomic state %p\n", state); + + return state; +fail: + kfree_state(state); + + return NULL; +} +EXPORT_SYMBOL(drm_atomic_state_alloc); + +/** + * drm_atomic_state_clear - clear state object + * @state: atomic state + * + * When the w/w mutex algorithm detects a deadlock we need to back off and drop + * all locks. So someone else could sneak in and change the current modeset + * configuration. Which means that all the state assembled in @state is no + * longer an atomic update to the current state, but to some arbitrary earlier + * state. Which could break assumptions the driver's ->atomic_check likely + * relies on. + * + * Hence we must clear all cached state and completely start over, using this + * function. + */ +void drm_atomic_state_clear(struct drm_atomic_state *state) +{ + struct drm_device *dev = state->dev; + int i; + + DRM_DEBUG_KMS("Clearing atomic state %p\n", state); + + for (i = 0; i < dev->mode_config.num_connector; i++) { + struct drm_connector *connector = state->connectors[i]; + + if (!connector) + continue; + + connector->funcs->atomic_destroy_state(connector, + state->connector_states[i]); + } + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct drm_crtc *crtc = state->crtcs[i]; + + if (!crtc) + continue; + + crtc->funcs->atomic_destroy_state(crtc, + state->crtc_states[i]); + } + + for (i = 0; i < dev->mode_config.num_total_plane; i++) { + struct drm_plane *plane = state->planes[i]; + + if (!plane) + continue; + + plane->funcs->atomic_destroy_state(plane, + state->plane_states[i]); + } +} +EXPORT_SYMBOL(drm_atomic_state_clear); + +/** + * drm_atomic_state_free - free all memory for an atomic state + * @state: atomic state to deallocate + * + * This frees all memory associated with an atomic state, including all the + * per-object state for planes, crtcs and connectors. + */ +void drm_atomic_state_free(struct drm_atomic_state *state) +{ + drm_atomic_state_clear(state); + + DRM_DEBUG_KMS("Freeing atomic state %p\n", state); + + kfree_state(state); +} +EXPORT_SYMBOL(drm_atomic_state_free); + +/** + * drm_atomic_get_crtc_state - get crtc state + * @state: global atomic state object + * @crtc: crtc to get state object for + * + * This function returns the crtc state for the given crtc, allocating it if + * needed. It will also grab the relevant crtc lock to make sure that the state + * is consistent. + * + * Returns: + * + * Either the allocated state or the error code encoded into the pointer. 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. + */ +struct drm_crtc_state * +drm_atomic_get_crtc_state(struct drm_atomic_state *state, + struct drm_crtc *crtc) +{ + int ret, index; + struct drm_crtc_state *crtc_state; + + index = drm_crtc_index(crtc); + + if (state->crtc_states[index]) + return state->crtc_states[index]; + + ret = drm_modeset_lock(&crtc->mutex, state->acquire_ctx); + if (ret) + return ERR_PTR(ret); + + crtc_state = crtc->funcs->atomic_duplicate_state(crtc); + if (!crtc_state) + return ERR_PTR(-ENOMEM); + + state->crtc_states[index] = crtc_state; + state->crtcs[index] = crtc; + crtc_state->state = state; + + DRM_DEBUG_KMS("Added [CRTC:%d] %p state to %p\n", + crtc->base.id, crtc_state, state); + + return crtc_state; +} +EXPORT_SYMBOL(drm_atomic_get_crtc_state); + +/** + * drm_atomic_get_plane_state - get plane state + * @state: global atomic state object + * @plane: plane to get state object for + * + * This function returns the plane state for the given plane, allocating it if + * needed. It will also grab the relevant plane lock to make sure that the state + * is consistent. + * + * Returns: + * + * Either the allocated state or the error code encoded into the pointer. 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. + */ +struct drm_plane_state * +drm_atomic_get_plane_state(struct drm_atomic_state *state, + struct drm_plane *plane) +{ + int ret, index; + struct drm_plane_state *plane_state; + + index = drm_plane_index(plane); + + if (state->plane_states[index]) + return state->plane_states[index]; + + /* + * TODO: We currently don't have per-plane mutexes. So instead of trying + * crazy tricks with deferring plane->crtc and hoping for the best just + * grab all crtc locks. Once we have per-plane locks we must update this + * to only take the plane mutex. + */ + ret = drm_modeset_lock_all_crtcs(state->dev, state->acquire_ctx); + if (ret) + return ERR_PTR(ret); + + plane_state = plane->funcs->atomic_duplicate_state(plane); + if (!plane_state) + return ERR_PTR(-ENOMEM); + + state->plane_states[index] = plane_state; + state->planes[index] = plane; + plane_state->state = state; + + DRM_DEBUG_KMS("Added [PLANE:%d] %p state to %p\n", + plane->base.id, plane_state, state); + + if (plane_state->crtc) { + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_crtc_state(state, + plane_state->crtc); + if (IS_ERR(crtc_state)) + return ERR_CAST(crtc_state); + } + + return plane_state; +} +EXPORT_SYMBOL(drm_atomic_get_plane_state); + +/** + * drm_atomic_get_connector_state - get connector state + * @state: global atomic state object + * @connector: connector to get state object for + * + * This function returns the connector state for the given connector, + * allocating it if needed. It will also grab the relevant connector lock to + * make sure that the state is consistent. + * + * Returns: + * + * Either the allocated state or the error code encoded into the pointer. 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. + */ +struct drm_connector_state * +drm_atomic_get_connector_state(struct drm_atomic_state *state, + struct drm_connector *connector) +{ + int ret, index; + struct drm_mode_config *config = &connector->dev->mode_config; + struct drm_connector_state *connector_state; + + index = drm_connector_index(connector); + + if (state->connector_states[index]) + return state->connector_states[index]; + + ret = drm_modeset_lock(&config->connection_mutex, state->acquire_ctx); + if (ret) + return ERR_PTR(ret); + + connector_state = connector->funcs->atomic_duplicate_state(connector); + if (!connector_state) + return ERR_PTR(-ENOMEM); + + state->connector_states[index] = connector_state; + state->connectors[index] = connector; + connector_state->state = state; + + DRM_DEBUG_KMS("Added [CONNECTOR:%d] %p state to %p\n", + connector->base.id, connector_state, state); + + if (connector_state->crtc) { + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_crtc_state(state, + connector_state->crtc); + if (IS_ERR(crtc_state)) + return ERR_CAST(crtc_state); + } + + return connector_state; +} +EXPORT_SYMBOL(drm_atomic_get_connector_state); + +/** + * drm_atomic_set_crtc_for_plane - set crtc for plane + * @plane_state: atomic state object for the plane + * @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_crtc_state *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); + } + + plane_state->crtc = crtc; + + if (crtc) + DRM_DEBUG_KMS("Link plane state %p to [CRTC:%d]\n", + plane_state, crtc->base.id); + else + DRM_DEBUG_KMS("Link plane state %p to [NOCRTC]\n", plane_state); + + return 0; +} +EXPORT_SYMBOL(drm_atomic_set_crtc_for_plane); + +/** + * drm_atomic_set_fb_for_plane - set crtc 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) +{ + if (plane_state->fb) + drm_framebuffer_unreference(plane_state->fb); + if (fb) + drm_framebuffer_reference(fb); + plane_state->fb = fb; + + if (fb) + DRM_DEBUG_KMS("Set [FB:%d] for plane state %p\n", + fb->base.id, plane_state); + else + DRM_DEBUG_KMS("Set [NOFB] for plane state %p\n", plane_state); +} +EXPORT_SYMBOL(drm_atomic_set_fb_for_plane); + +/** + * 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_crtc_state *crtc_state; + + if (crtc) { + crtc_state = drm_atomic_get_crtc_state(conn_state->state, crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + } + + conn_state->crtc = crtc; + + if (crtc) + DRM_DEBUG_KMS("Link connector state %p to [CRTC:%d]\n", + conn_state, crtc->base.id); + else + DRM_DEBUG_KMS("Link connector state %p to [NOCRTC]\n", + conn_state); + + return 0; +} +EXPORT_SYMBOL(drm_atomic_set_crtc_for_connector); + +/** + * drm_atomic_add_affected_connectors - add connectors for crtc + * @state: atomic state + * @crtc: DRM crtc + * + * This function walks the current configuration and adds all connectors + * currently using @crtc to the atomic configuration @state. Note that this + * function must acquire the connection mutex. This can potentially cause + * unneeded seralization if the update is just for the planes on one crtc. Hence + * drivers and helpers should only call this when really needed (e.g. when a + * full modeset needs to happen due to some change). + * + * 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_add_affected_connectors(struct drm_atomic_state *state, + struct drm_crtc *crtc) +{ + struct drm_mode_config *config = &state->dev->mode_config; + struct drm_connector *connector; + struct drm_connector_state *conn_state; + int ret; + + ret = drm_modeset_lock(&config->connection_mutex, state->acquire_ctx); + if (ret) + return ret; + + DRM_DEBUG_KMS("Adding all current connectors for [CRTC:%d] to %p\n", + crtc->base.id, state); + + /* + * Changed connectors are already in @state, so only need to look at the + * current configuration. + */ + list_for_each_entry(connector, &config->connector_list, head) { + if (connector->state->crtc != crtc) + continue; + + conn_state = drm_atomic_get_connector_state(state, connector); + if (IS_ERR(conn_state)) + return PTR_ERR(conn_state); + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_add_affected_connectors); + +/** + * drm_atomic_connectors_for_crtc - count number of connected outputs + * @state: atomic state + * @crtc: DRM crtc + * + * This function counts all connectors which will be connected to @crtc + * according to @state. Useful to recompute the enable state for @crtc. + */ +int +drm_atomic_connectors_for_crtc(struct drm_atomic_state *state, + struct drm_crtc *crtc) +{ + int nconnectors = state->dev->mode_config.num_connector; + int i, num_connected_connectors = 0; + + for (i = 0; i < nconnectors; i++) { + struct drm_connector_state *conn_state; + + conn_state = state->connector_states[i]; + + if (conn_state && conn_state->crtc == crtc) + num_connected_connectors++; + } + + DRM_DEBUG_KMS("State %p has %i connectors for [CRTC:%d]\n", + state, num_connected_connectors, crtc->base.id); + + return num_connected_connectors; +} +EXPORT_SYMBOL(drm_atomic_connectors_for_crtc); + +/** + * drm_atomic_legacy_backoff - locking backoff for legacy ioctls + * @state: atomic state + * + * This function should be used by legacy entry points which don't understand + * -EDEADLK semantics. For simplicity this one will grab all modeset locks after + * the slowpath completed. + */ +void drm_atomic_legacy_backoff(struct drm_atomic_state *state) +{ + int ret; + +retry: + drm_modeset_backoff(state->acquire_ctx); + + ret = drm_modeset_lock(&state->dev->mode_config.connection_mutex, + state->acquire_ctx); + if (ret) + goto retry; + ret = drm_modeset_lock_all_crtcs(state->dev, + state->acquire_ctx); + if (ret) + goto retry; +} +EXPORT_SYMBOL(drm_atomic_legacy_backoff); + +/** + * drm_atomic_check_only - check whether a given config would work + * @state: atomic configuration to check + * + * Note that this function can return -EDEADLK if the driver needed to acquire + * more locks but encountered a deadlock. The caller must then do the usual w/w + * backoff dance and restart. All other errors are fatal. + * + * Returns: + * 0 on success, negative error code on failure. + */ +int drm_atomic_check_only(struct drm_atomic_state *state) +{ + struct drm_mode_config *config = &state->dev->mode_config; + + DRM_DEBUG_KMS("checking %p\n", state); + + if (config->funcs->atomic_check) + return config->funcs->atomic_check(state->dev, state); + else + return 0; +} +EXPORT_SYMBOL(drm_atomic_check_only); + +/** + * drm_atomic_commit - commit configuration atomically + * @state: atomic configuration to check + * + * Note that this function can return -EDEADLK if the driver needed to acquire + * more locks but encountered a deadlock. The caller must then do the usual w/w + * backoff dance and restart. All other errors are fatal. + * + * Also note that on successful execution ownership of @state is transferred + * from the caller of this function to the function itself. The caller must not + * free or in any other way access @state. If the function fails then the caller + * must clean up @state itself. + * + * Returns: + * 0 on success, negative error code on failure. + */ +int drm_atomic_commit(struct drm_atomic_state *state) +{ + struct drm_mode_config *config = &state->dev->mode_config; + int ret; + + ret = drm_atomic_check_only(state); + if (ret) + return ret; + + DRM_DEBUG_KMS("commiting %p\n", state); + + return config->funcs->atomic_commit(state->dev, state, false); +} +EXPORT_SYMBOL(drm_atomic_commit); + +/** + * drm_atomic_async_commit - atomic&async configuration commit + * @state: atomic configuration to check + * + * Note that this function can return -EDEADLK if the driver needed to acquire + * more locks but encountered a deadlock. The caller must then do the usual w/w + * backoff dance and restart. All other errors are fatal. + * + * Also note that on successful execution ownership of @state is transferred + * from the caller of this function to the function itself. The caller must not + * free or in any other way access @state. If the function fails then the caller + * must clean up @state itself. + * + * Returns: + * 0 on success, negative error code on failure. + */ +int drm_atomic_async_commit(struct drm_atomic_state *state) +{ + struct drm_mode_config *config = &state->dev->mode_config; + int ret; + + ret = drm_atomic_check_only(state); + if (ret) + return ret; + + DRM_DEBUG_KMS("commiting %p asynchronously\n", state); + + return config->funcs->atomic_commit(state->dev, state, true); +} +EXPORT_SYMBOL(drm_atomic_async_commit); diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c new file mode 100644 index 000000000000..ca839bd9bb0d --- /dev/null +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -0,0 +1,1906 @@ +/* + * 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> +#include <drm/drm_atomic_helper.h> +#include <linux/fence.h> + +/** + * DOC: overview + * + * This helper library provides implementations of check and commit functions on + * top of the CRTC modeset helper callbacks and the plane helper callbacks. It + * also provides convenience implementations for the atomic state handling + * callbacks for drivers which don't need to subclass the drm core structures to + * add their own additional internal state. + * + * This library also provides default implementations for the check callback in + * drm_atomic_helper_check and for the commit callback with + * drm_atomic_helper_commit. But the individual stages and callbacks are expose + * to allow drivers to mix and match and e.g. use the plane helpers only + * together with a driver private modeset implementation. + * + * This library also provides implementations for all the legacy driver + * interfaces on top of the atomic interface. See drm_atomic_helper_set_config, + * drm_atomic_helper_disable_plane, drm_atomic_helper_disable_plane and the + * various functions to implement set_property callbacks. New drivers must not + * implement these functions themselves but must use the provided helpers. + */ +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; + } +} + +static struct drm_crtc * +get_current_crtc_for_encoder(struct drm_device *dev, + struct drm_encoder *encoder) +{ + struct drm_mode_config *config = &dev->mode_config; + struct drm_connector *connector; + + WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); + + list_for_each_entry(connector, &config->connector_list, head) { + if (connector->state->best_encoder != encoder) + continue; + + return connector->state->crtc; + } + + return NULL; +} + +static int +steal_encoder(struct drm_atomic_state *state, + struct drm_encoder *encoder, + struct drm_crtc *encoder_crtc) +{ + struct drm_mode_config *config = &state->dev->mode_config; + struct drm_crtc_state *crtc_state; + struct drm_connector *connector; + struct drm_connector_state *connector_state; + int ret; + + /* + * We can only steal an encoder coming from a connector, which means we + * must already hold the connection_mutex. + */ + WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); + + DRM_DEBUG_KMS("[ENCODER:%d:%s] in use on [CRTC:%d], stealing it\n", + encoder->base.id, encoder->name, + encoder_crtc->base.id); + + crtc_state = drm_atomic_get_crtc_state(state, encoder_crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + crtc_state->mode_changed = true; + + list_for_each_entry(connector, &config->connector_list, head) { + if (connector->state->best_encoder != encoder) + continue; + + DRM_DEBUG_KMS("Stealing encoder from [CONNECTOR:%d:%s]\n", + connector->base.id, + connector->name); + + connector_state = drm_atomic_get_connector_state(state, + connector); + if (IS_ERR(connector_state)) + return PTR_ERR(connector_state); + + ret = drm_atomic_set_crtc_for_connector(connector_state, NULL); + if (ret) + return ret; + connector_state->best_encoder = NULL; + } + + return 0; +} + +static int +update_connector_routing(struct drm_atomic_state *state, int conn_idx) +{ + struct drm_connector_helper_funcs *funcs; + struct drm_encoder *new_encoder; + struct drm_crtc *encoder_crtc; + struct drm_connector *connector; + struct drm_connector_state *connector_state; + struct drm_crtc_state *crtc_state; + int idx, ret; + + connector = state->connectors[conn_idx]; + connector_state = state->connector_states[conn_idx]; + + if (!connector) + return 0; + + DRM_DEBUG_KMS("Updating routing for [CONNECTOR:%d:%s]\n", + connector->base.id, + connector->name); + + if (connector->state->crtc != connector_state->crtc) { + if (connector->state->crtc) { + idx = drm_crtc_index(connector->state->crtc); + + crtc_state = state->crtc_states[idx]; + crtc_state->mode_changed = true; + } + + if (connector_state->crtc) { + idx = drm_crtc_index(connector_state->crtc); + + crtc_state = state->crtc_states[idx]; + crtc_state->mode_changed = true; + } + } + + if (!connector_state->crtc) { + DRM_DEBUG_KMS("Disabling [CONNECTOR:%d:%s]\n", + connector->base.id, + connector->name); + + connector_state->best_encoder = NULL; + + return 0; + } + + funcs = connector->helper_private; + new_encoder = funcs->best_encoder(connector); + + if (!new_encoder) { + DRM_DEBUG_KMS("No suitable encoder found for [CONNECTOR:%d:%s]\n", + connector->base.id, + connector->name); + return -EINVAL; + } + + if (new_encoder == connector_state->best_encoder) { + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] keeps [ENCODER:%d:%s], now on [CRTC:%d]\n", + connector->base.id, + connector->name, + new_encoder->base.id, + new_encoder->name, + connector_state->crtc->base.id); + + return 0; + } + + encoder_crtc = get_current_crtc_for_encoder(state->dev, + new_encoder); + + if (encoder_crtc) { + ret = steal_encoder(state, new_encoder, encoder_crtc); + if (ret) { + DRM_DEBUG_KMS("Encoder stealing failed for [CONNECTOR:%d:%s]\n", + connector->base.id, + connector->name); + return ret; + } + } + + connector_state->best_encoder = new_encoder; + idx = drm_crtc_index(connector_state->crtc); + + crtc_state = state->crtc_states[idx]; + crtc_state->mode_changed = true; + + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d]\n", + connector->base.id, + connector->name, + new_encoder->base.id, + new_encoder->name, + connector_state->crtc->base.id); + + return 0; +} + +static int +mode_fixup(struct drm_atomic_state *state) +{ + int ncrtcs = state->dev->mode_config.num_crtc; + int nconnectors = state->dev->mode_config.num_connector; + struct drm_crtc_state *crtc_state; + struct drm_connector_state *conn_state; + int i; + bool ret; + + for (i = 0; i < ncrtcs; i++) { + crtc_state = state->crtc_states[i]; + + if (!crtc_state || !crtc_state->mode_changed) + continue; + + drm_mode_copy(&crtc_state->adjusted_mode, &crtc_state->mode); + } + + for (i = 0; i < nconnectors; i++) { + struct drm_encoder_helper_funcs *funcs; + struct drm_encoder *encoder; + + conn_state = state->connector_states[i]; + + if (!conn_state) + continue; + + WARN_ON(!!conn_state->best_encoder != !!conn_state->crtc); + + if (!conn_state->crtc || !conn_state->best_encoder) + continue; + + crtc_state = + state->crtc_states[drm_crtc_index(conn_state->crtc)]; + + /* + * Each encoder has at most one connector (since we always steal + * it away), so we won't call ->mode_fixup twice. + */ + encoder = conn_state->best_encoder; + funcs = encoder->helper_private; + + if (encoder->bridge && encoder->bridge->funcs->mode_fixup) { + ret = encoder->bridge->funcs->mode_fixup( + encoder->bridge, &crtc_state->mode, + &crtc_state->adjusted_mode); + if (!ret) { + DRM_DEBUG_KMS("Bridge fixup failed\n"); + return -EINVAL; + } + } + + + ret = funcs->mode_fixup(encoder, &crtc_state->mode, + &crtc_state->adjusted_mode); + if (!ret) { + DRM_DEBUG_KMS("[ENCODER:%d:%s] fixup failed\n", + encoder->base.id, encoder->name); + return -EINVAL; + } + } + + for (i = 0; i < ncrtcs; i++) { + struct drm_crtc_helper_funcs *funcs; + struct drm_crtc *crtc; + + crtc_state = state->crtc_states[i]; + crtc = state->crtcs[i]; + + if (!crtc_state || !crtc_state->mode_changed) + continue; + + funcs = crtc->helper_private; + ret = funcs->mode_fixup(crtc, &crtc_state->mode, + &crtc_state->adjusted_mode); + if (!ret) { + DRM_DEBUG_KMS("[CRTC:%d] fixup failed\n", + crtc->base.id); + return -EINVAL; + } + } + + return 0; +} + +static int +drm_atomic_helper_check_prepare(struct drm_device *dev, + struct drm_atomic_state *state) +{ + int ncrtcs = dev->mode_config.num_crtc; + int nconnectors = dev->mode_config.num_connector; + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + int i, ret; + + for (i = 0; i < ncrtcs; i++) { + crtc = state->crtcs[i]; + crtc_state = state->crtc_states[i]; + + if (!crtc) + continue; + + if (!drm_mode_equal(&crtc->state->mode, &crtc_state->mode)) { + DRM_DEBUG_KMS("[CRTC:%d] mode changed\n", + crtc->base.id); + crtc_state->mode_changed = true; + } + + if (crtc->state->enable != crtc_state->enable) { + DRM_DEBUG_KMS("[CRTC:%d] enable changed\n", + crtc->base.id); + crtc_state->mode_changed = true; + } + } + + for (i = 0; i < nconnectors; i++) { + /* + * This only sets crtc->mode_changed for routing changes, + * drivers must set crtc->mode_changed themselves when connector + * properties need to be updated. + */ + ret = update_connector_routing(state, i); + if (ret) + return ret; + } + + /* + * After all the routing has been prepared we need to add in any + * connector which is itself unchanged, but who's crtc changes it's + * configuration. This must be done before calling mode_fixup in case a + * crtc only changed its mode but has the same set of connectors. + */ + for (i = 0; i < ncrtcs; i++) { + int num_connectors; + + crtc = state->crtcs[i]; + crtc_state = state->crtc_states[i]; + + if (!crtc || !crtc_state->mode_changed) + continue; + + DRM_DEBUG_KMS("[CRTC:%d] needs full modeset, enable: %c\n", + crtc->base.id, + crtc_state->enable ? 'y' : 'n'); + + ret = drm_atomic_add_affected_connectors(state, crtc); + if (ret != 0) + return ret; + + num_connectors = drm_atomic_connectors_for_crtc(state, + crtc); + + if (crtc_state->enable != !!num_connectors) { + DRM_DEBUG_KMS("[CRTC:%d] enabled/connectors mismatch\n", + crtc->base.id); + + return -EINVAL; + } + } + + return mode_fixup(state); +} + +/** + * 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; + + ret = drm_atomic_helper_check_prepare(dev, state); + if (ret) + return ret; + + 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); + +static void +disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) +{ + int ncrtcs = old_state->dev->mode_config.num_crtc; + int nconnectors = old_state->dev->mode_config.num_connector; + int i; + + for (i = 0; i < nconnectors; i++) { + struct drm_connector_state *old_conn_state; + struct drm_connector *connector; + struct drm_encoder_helper_funcs *funcs; + struct drm_encoder *encoder; + + old_conn_state = old_state->connector_states[i]; + connector = old_state->connectors[i]; + + /* Shut down everything that's in the changeset and currently + * still on. So need to check the old, saved state. */ + if (!old_conn_state || !old_conn_state->crtc) + continue; + + encoder = connector->state->best_encoder; + + if (!encoder) + continue; + + funcs = encoder->helper_private; + + /* + * Each encoder has at most one connector (since we always steal + * it away), so we won't call call disable hooks twice. + */ + if (encoder->bridge) + encoder->bridge->funcs->disable(encoder->bridge); + + /* Right function depends upon target state. */ + if (connector->state->crtc) + funcs->prepare(encoder); + else if (funcs->disable) + funcs->disable(encoder); + else + funcs->dpms(encoder, DRM_MODE_DPMS_OFF); + + if (encoder->bridge) + encoder->bridge->funcs->post_disable(encoder->bridge); + } + + for (i = 0; i < ncrtcs; i++) { + struct drm_crtc_helper_funcs *funcs; + struct drm_crtc *crtc; + + crtc = old_state->crtcs[i]; + + /* Shut down everything that needs a full modeset. */ + if (!crtc || !crtc->state->mode_changed) + continue; + + funcs = crtc->helper_private; + + /* Right function depends upon target state. */ + if (crtc->state->enable) + funcs->prepare(crtc); + else if (funcs->disable) + funcs->disable(crtc); + else + funcs->dpms(crtc, DRM_MODE_DPMS_OFF); + } +} + +static void +set_routing_links(struct drm_device *dev, struct drm_atomic_state *old_state) +{ + int nconnectors = dev->mode_config.num_connector; + int ncrtcs = old_state->dev->mode_config.num_crtc; + int i; + + /* clear out existing links */ + for (i = 0; i < nconnectors; i++) { + struct drm_connector *connector; + + connector = old_state->connectors[i]; + + if (!connector || !connector->encoder) + continue; + + WARN_ON(!connector->encoder->crtc); + + connector->encoder->crtc = NULL; + connector->encoder = NULL; + } + + /* set new links */ + for (i = 0; i < nconnectors; i++) { + struct drm_connector *connector; + + connector = old_state->connectors[i]; + + if (!connector || !connector->state->crtc) + continue; + + if (WARN_ON(!connector->state->best_encoder)) + continue; + + connector->encoder = connector->state->best_encoder; + connector->encoder->crtc = connector->state->crtc; + } + + /* set legacy state in the crtc structure */ + for (i = 0; i < ncrtcs; i++) { + struct drm_crtc *crtc; + + crtc = old_state->crtcs[i]; + + if (!crtc) + continue; + + crtc->mode = crtc->state->mode; + crtc->enabled = crtc->state->enable; + crtc->x = crtc->primary->state->src_x >> 16; + crtc->y = crtc->primary->state->src_y >> 16; + } +} + +static void +crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) +{ + int ncrtcs = old_state->dev->mode_config.num_crtc; + int nconnectors = old_state->dev->mode_config.num_connector; + int i; + + for (i = 0; i < ncrtcs; i++) { + struct drm_crtc_helper_funcs *funcs; + struct drm_crtc *crtc; + + crtc = old_state->crtcs[i]; + + if (!crtc || !crtc->state->mode_changed) + continue; + + funcs = crtc->helper_private; + + if (crtc->state->enable) + funcs->mode_set_nofb(crtc); + } + + for (i = 0; i < nconnectors; i++) { + struct drm_connector *connector; + struct drm_crtc_state *new_crtc_state; + struct drm_encoder_helper_funcs *funcs; + struct drm_encoder *encoder; + struct drm_display_mode *mode, *adjusted_mode; + + connector = old_state->connectors[i]; + + if (!connector || !connector->state->best_encoder) + continue; + + encoder = connector->state->best_encoder; + funcs = encoder->helper_private; + new_crtc_state = connector->state->crtc->state; + mode = &new_crtc_state->mode; + adjusted_mode = &new_crtc_state->adjusted_mode; + + /* + * Each encoder has at most one connector (since we always steal + * it away), so we won't call call mode_set hooks twice. + */ + funcs->mode_set(encoder, mode, adjusted_mode); + + if (encoder->bridge && encoder->bridge->funcs->mode_set) + encoder->bridge->funcs->mode_set(encoder->bridge, + mode, adjusted_mode); + } +} + +/** + * drm_atomic_helper_commit_pre_planes - modeset commit before plane updates + * @dev: DRM device + * @state: atomic state + * + * This function commits the modeset changes that need to be committed before + * updating planes. It shuts down all the outputs that need to be shut down and + * prepares them (if required) with the new mode. + */ +void drm_atomic_helper_commit_pre_planes(struct drm_device *dev, + struct drm_atomic_state *state) +{ + disable_outputs(dev, state); + set_routing_links(dev, state); + crtc_set_mode(dev, state); +} +EXPORT_SYMBOL(drm_atomic_helper_commit_pre_planes); + +/** + * drm_atomic_helper_commit_post_planes - modeset commit after plane updates + * @dev: DRM device + * @old_state: atomic state object with old state structures + * + * This function commits the modeset changes that need to be committed after + * updating planes: It enables all the outputs with the new configuration which + * had to be turned off for the update. + */ +void drm_atomic_helper_commit_post_planes(struct drm_device *dev, + struct drm_atomic_state *old_state) +{ + int ncrtcs = old_state->dev->mode_config.num_crtc; + int nconnectors = old_state->dev->mode_config.num_connector; + int i; + + for (i = 0; i < ncrtcs; i++) { + struct drm_crtc_helper_funcs *funcs; + struct drm_crtc *crtc; + + crtc = old_state->crtcs[i]; + + /* Need to filter out CRTCs where only planes change. */ + if (!crtc || !crtc->state->mode_changed) + continue; + + funcs = crtc->helper_private; + + if (crtc->state->enable) + funcs->commit(crtc); + } + + for (i = 0; i < nconnectors; i++) { + struct drm_connector *connector; + struct drm_encoder_helper_funcs *funcs; + struct drm_encoder *encoder; + + connector = old_state->connectors[i]; + + if (!connector || !connector->state->best_encoder) + continue; + + encoder = connector->state->best_encoder; + funcs = encoder->helper_private; + + /* + * Each encoder has at most one connector (since we always steal + * it away), so we won't call call enable hooks twice. + */ + if (encoder->bridge) + encoder->bridge->funcs->pre_enable(encoder->bridge); + + funcs->commit(encoder); + + if (encoder->bridge) + encoder->bridge->funcs->enable(encoder->bridge); + } +} +EXPORT_SYMBOL(drm_atomic_helper_commit_post_planes); + +static void wait_for_fences(struct drm_device *dev, + struct drm_atomic_state *state) +{ + int nplanes = dev->mode_config.num_total_plane; + int i; + + for (i = 0; i < nplanes; i++) { + struct drm_plane *plane = state->planes[i]; + + if (!plane || !plane->state->fence) + continue; + + WARN_ON(!plane->state->fb); + + fence_wait(plane->state->fence, false); + fence_put(plane->state->fence); + plane->state->fence = NULL; + } +} + +static void +wait_for_vblanks(struct drm_device *dev, struct drm_atomic_state *old_state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state; + int ncrtcs = old_state->dev->mode_config.num_crtc; + int i, ret; + + for (i = 0; i < ncrtcs; i++) { + crtc = old_state->crtcs[i]; + old_crtc_state = old_state->crtc_states[i]; + + if (!crtc) + continue; + + /* No one cares about the old state, so abuse it for tracking + * and store whether we hold a vblank reference (and should do a + * vblank wait) in the ->enable boolean. */ + old_crtc_state->enable = false; + + if (!crtc->state->enable) + continue; + + ret = drm_crtc_vblank_get(crtc); + if (ret != 0) + continue; + + old_crtc_state->enable = true; + old_crtc_state->last_vblank_count = drm_vblank_count(dev, i); + } + + for (i = 0; i < ncrtcs; i++) { + crtc = old_state->crtcs[i]; + old_crtc_state = old_state->crtc_states[i]; + + if (!crtc || !old_crtc_state->enable) + continue; + + ret = wait_event_timeout(dev->vblank[i].queue, + old_crtc_state->last_vblank_count != + drm_vblank_count(dev, i), + msecs_to_jiffies(50)); + + drm_crtc_vblank_put(crtc); + } +} + +/** + * drm_atomic_helper_commit - commit validated state object + * @dev: DRM device + * @state: the driver state object + * @async: asynchronous commit + * + * 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. + * + * RETURNS + * Zero for success or -errno. + */ +int drm_atomic_helper_commit(struct drm_device *dev, + struct drm_atomic_state *state, + bool async) +{ + int ret; + + if (async) + return -EBUSY; + + ret = drm_atomic_helper_prepare_planes(dev, state); + if (ret) + return ret; + + /* + * This is the point of no return - everything below never fails except + * when the hw goes bonghits. Which means we can commit the new state on + * the software side now. + */ + + drm_atomic_helper_swap_state(dev, state); + + /* + * Everything below can be run asynchronously without the need to grab + * any modeset locks at all under one conditions: It must be guaranteed + * that the asynchronous work has either been cancelled (if the driver + * supports it, which at least requires that the framebuffers get + * cleaned up with drm_atomic_helper_cleanup_planes()) or completed + * before the new state gets committed on the software side with + * drm_atomic_helper_swap_state(). + * + * This scheme allows new atomic state updates to be prepared and + * checked in parallel to the asynchronous completion of the previous + * update. Which is important since compositors need to figure out the + * composition of the next frame right after having submitted the + * current layout. + */ + + wait_for_fences(dev, state); + + drm_atomic_helper_commit_pre_planes(dev, state); + + drm_atomic_helper_commit_planes(dev, state); + + drm_atomic_helper_commit_post_planes(dev, state); + + wait_for_vblanks(dev, state); + + drm_atomic_helper_cleanup_planes(dev, state); + + drm_atomic_state_free(state); + + return 0; +} +EXPORT_SYMBOL(drm_atomic_helper_commit); + +/** + * DOC: implementing async 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 drivers have to implement async 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 + * 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 + * cleanup is still done when cancelling. + * + * For sufficient parallelism it is recommended to have a work item per crtc + * (for updates which don't touch global state) and a global one. Then we only + * need to synchronize with the crtc work items for changed crtcs and the global + * work item, which allows nice concurrent updates on disjoint sets of crtcs. + * + * 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. + * + * 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 + * then cleaning up the framebuffers after the old framebuffer is no longer + * being displayed. + */ + +/** + * 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); + +/** + * drm_atomic_helper_update_plane - Helper for primary plane update using atomic + * @plane: plane object to update + * @crtc: owning CRTC of owning plane + * @fb: framebuffer to flip onto plane + * @crtc_x: x offset of primary plane on crtc + * @crtc_y: y offset of primary plane on crtc + * @crtc_w: width of primary plane rectangle on crtc + * @crtc_h: height of primary plane rectangle on crtc + * @src_x: x offset of @fb for panning + * @src_y: y offset of @fb for panning + * @src_w: width of source rectangle in @fb + * @src_h: height of source rectangle in @fb + * + * Provides a default plane update handler using the atomic driver interface. + * + * RETURNS: + * Zero on success, error code on failure + */ +int drm_atomic_helper_update_plane(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + struct drm_atomic_state *state; + struct drm_plane_state *plane_state; + int ret = 0; + + state = drm_atomic_state_alloc(plane->dev); + if (!state) + return -ENOMEM; + + state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); +retry: + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) { + ret = PTR_ERR(plane_state); + goto fail; + } + + ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); + if (ret != 0) + goto fail; + drm_atomic_set_fb_for_plane(plane_state, fb); + plane_state->crtc_x = crtc_x; + plane_state->crtc_y = crtc_y; + plane_state->crtc_h = crtc_h; + plane_state->crtc_w = crtc_w; + plane_state->src_x = src_x; + plane_state->src_y = src_y; + plane_state->src_h = src_h; + plane_state->src_w = src_w; + + ret = drm_atomic_commit(state); + if (ret != 0) + goto fail; + + /* Driver takes ownership of state on successful commit. */ + return 0; +fail: + if (ret == -EDEADLK) + goto backoff; + + drm_atomic_state_free(state); + + return ret; +backoff: + drm_atomic_legacy_backoff(state); + drm_atomic_state_clear(state); + + /* + * Someone might have exchanged the framebuffer while we dropped locks + * in the backoff code. We need to fix up the fb refcount tracking the + * core does for us. + */ + plane->old_fb = plane->fb; + + goto retry; +} +EXPORT_SYMBOL(drm_atomic_helper_update_plane); + +/** + * drm_atomic_helper_disable_plane - Helper for primary plane disable using * atomic + * @plane: plane to disable + * + * Provides a default plane disable handler using the atomic driver interface. + * + * RETURNS: + * Zero on success, error code on failure + */ +int drm_atomic_helper_disable_plane(struct drm_plane *plane) +{ + struct drm_atomic_state *state; + struct drm_plane_state *plane_state; + int ret = 0; + + state = drm_atomic_state_alloc(plane->dev); + if (!state) + return -ENOMEM; + + state->acquire_ctx = drm_modeset_legacy_acquire_ctx(plane->crtc); +retry: + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) { + ret = PTR_ERR(plane_state); + goto fail; + } + + ret = drm_atomic_set_crtc_for_plane(plane_state, NULL); + if (ret != 0) + goto fail; + drm_atomic_set_fb_for_plane(plane_state, NULL); + plane_state->crtc_x = 0; + plane_state->crtc_y = 0; + plane_state->crtc_h = 0; + plane_state->crtc_w = 0; + plane_state->src_x = 0; + plane_state->src_y = 0; + plane_state->src_h = 0; + plane_state->src_w = 0; + + ret = drm_atomic_commit(state); + if (ret != 0) + goto fail; + + /* Driver takes ownership of state on successful commit. */ + return 0; +fail: + if (ret == -EDEADLK) + goto backoff; + + drm_atomic_state_free(state); + + return ret; +backoff: + drm_atomic_legacy_backoff(state); + drm_atomic_state_clear(state); + + /* + * Someone might have exchanged the framebuffer while we dropped locks + * in the backoff code. We need to fix up the fb refcount tracking the + * core does for us. + */ + plane->old_fb = plane->fb; + + goto retry; +} +EXPORT_SYMBOL(drm_atomic_helper_disable_plane); + +static int update_output_state(struct drm_atomic_state *state, + struct drm_mode_set *set) +{ + struct drm_device *dev = set->crtc->dev; + struct drm_connector_state *conn_state; + int nconnectors = state->dev->mode_config.num_connector; + int ncrtcs = state->dev->mode_config.num_crtc; + int ret, i, j; + + ret = drm_modeset_lock(&dev->mode_config.connection_mutex, + state->acquire_ctx); + if (ret) + return ret; + + /* First grab all affected connector/crtc states. */ + for (i = 0; i < set->num_connectors; i++) { + conn_state = drm_atomic_get_connector_state(state, + set->connectors[i]); + if (IS_ERR(conn_state)) + return PTR_ERR(conn_state); + } + + for (i = 0; i < ncrtcs; i++) { + struct drm_crtc *crtc = state->crtcs[i]; + + if (!crtc) + continue; + + ret = drm_atomic_add_affected_connectors(state, crtc); + if (ret) + return ret; + } + + /* Then recompute connector->crtc links and crtc enabling state. */ + for (i = 0; i < nconnectors; i++) { + struct drm_connector *connector; + + connector = state->connectors[i]; + conn_state = state->connector_states[i]; + + if (!connector) + continue; + + if (conn_state->crtc == set->crtc) { + ret = drm_atomic_set_crtc_for_connector(conn_state, + NULL); + if (ret) + return ret; + } + + for (j = 0; j < set->num_connectors; j++) { + if (set->connectors[j] == connector) { + ret = drm_atomic_set_crtc_for_connector(conn_state, + set->crtc); + if (ret) + return ret; + break; + } + } + } + + for (i = 0; i < ncrtcs; i++) { + struct drm_crtc *crtc = state->crtcs[i]; + struct drm_crtc_state *crtc_state = state->crtc_states[i]; + + if (!crtc) + continue; + + /* Don't update ->enable for the CRTC in the set_config request, + * since a mismatch would indicate a bug in the upper layers. + * The actual modeset code later on will catch any + * inconsistencies here. */ + if (crtc == set->crtc) + continue; + + crtc_state->enable = + drm_atomic_connectors_for_crtc(state, crtc); + } + + return 0; +} + +/** + * drm_atomic_helper_set_config - set a new config from userspace + * @set: mode set configuration + * + * Provides a default crtc set_config handler using the atomic driver interface. + * + * Returns: + * Returns 0 on success, negative errno numbers on failure. + */ +int drm_atomic_helper_set_config(struct drm_mode_set *set) +{ + struct drm_atomic_state *state; + struct drm_crtc *crtc = set->crtc; + struct drm_crtc_state *crtc_state; + struct drm_plane_state *primary_state; + int ret = 0; + + state = drm_atomic_state_alloc(crtc->dev); + if (!state) + return -ENOMEM; + + state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); +retry: + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + goto fail; + } + + if (!set->mode) { + WARN_ON(set->fb); + WARN_ON(set->num_connectors); + + crtc_state->enable = false; + goto commit; + } + + WARN_ON(!set->fb); + WARN_ON(!set->num_connectors); + + crtc_state->enable = true; + drm_mode_copy(&crtc_state->mode, set->mode); + + primary_state = drm_atomic_get_plane_state(state, crtc->primary); + if (IS_ERR(primary_state)) { + ret = PTR_ERR(primary_state); + goto fail; + } + + ret = drm_atomic_set_crtc_for_plane(primary_state, crtc); + if (ret != 0) + goto fail; + drm_atomic_set_fb_for_plane(primary_state, set->fb); + primary_state->crtc_x = 0; + primary_state->crtc_y = 0; + primary_state->crtc_h = set->mode->vdisplay; + primary_state->crtc_w = set->mode->hdisplay; + primary_state->src_x = set->x << 16; + primary_state->src_y = set->y << 16; + primary_state->src_h = set->mode->vdisplay << 16; + primary_state->src_w = set->mode->hdisplay << 16; + +commit: + ret = update_output_state(state, set); + if (ret) + goto fail; + + ret = drm_atomic_commit(state); + if (ret != 0) + goto fail; + + /* Driver takes ownership of state on successful commit. */ + return 0; +fail: + if (ret == -EDEADLK) + goto backoff; + + drm_atomic_state_free(state); + + return ret; +backoff: + drm_atomic_legacy_backoff(state); + drm_atomic_state_clear(state); + + /* + * Someone might have exchanged the framebuffer while we dropped locks + * in the backoff code. We need to fix up the fb refcount tracking the + * core does for us. + */ + crtc->primary->old_fb = crtc->primary->fb; + + goto retry; +} +EXPORT_SYMBOL(drm_atomic_helper_set_config); + +/** + * drm_atomic_helper_crtc_set_property - helper for crtc prorties + * @crtc: DRM crtc + * @property: DRM property + * @val: value of property + * + * Provides a default plane disablle handler using the atomic driver interface. + * + * RETURNS: + * Zero on success, error code on failure + */ +int +drm_atomic_helper_crtc_set_property(struct drm_crtc *crtc, + struct drm_property *property, + uint64_t val) +{ + struct drm_atomic_state *state; + struct drm_crtc_state *crtc_state; + int ret = 0; + + state = drm_atomic_state_alloc(crtc->dev); + if (!state) + return -ENOMEM; + + /* ->set_property is always called with all locks held. */ + state->acquire_ctx = crtc->dev->mode_config.acquire_ctx; +retry: + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + goto fail; + } + + ret = crtc->funcs->atomic_set_property(crtc, crtc_state, + property, val); + if (ret) + goto fail; + + ret = drm_atomic_commit(state); + if (ret != 0) + goto fail; + + /* Driver takes ownership of state on successful commit. */ + return 0; +fail: + if (ret == -EDEADLK) + goto backoff; + + drm_atomic_state_free(state); + + return ret; +backoff: + drm_atomic_legacy_backoff(state); + drm_atomic_state_clear(state); + + goto retry; +} +EXPORT_SYMBOL(drm_atomic_helper_crtc_set_property); + +/** + * drm_atomic_helper_plane_set_property - helper for plane prorties + * @plane: DRM plane + * @property: DRM property + * @val: value of property + * + * Provides a default plane disable handler using the atomic driver interface. + * + * RETURNS: + * Zero on success, error code on failure + */ +int +drm_atomic_helper_plane_set_property(struct drm_plane *plane, + struct drm_property *property, + uint64_t val) +{ + struct drm_atomic_state *state; + struct drm_plane_state *plane_state; + int ret = 0; + + state = drm_atomic_state_alloc(plane->dev); + if (!state) + return -ENOMEM; + + /* ->set_property is always called with all locks held. */ + state->acquire_ctx = plane->dev->mode_config.acquire_ctx; +retry: + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) { + ret = PTR_ERR(plane_state); + goto fail; + } + + ret = plane->funcs->atomic_set_property(plane, plane_state, + property, val); + if (ret) + goto fail; + + ret = drm_atomic_commit(state); + if (ret != 0) + goto fail; + + /* Driver takes ownership of state on successful commit. */ + return 0; +fail: + if (ret == -EDEADLK) + goto backoff; + + drm_atomic_state_free(state); + + return ret; +backoff: + drm_atomic_legacy_backoff(state); + drm_atomic_state_clear(state); + + goto retry; +} +EXPORT_SYMBOL(drm_atomic_helper_plane_set_property); + +/** + * drm_atomic_helper_connector_set_property - helper for connector prorties + * @connector: DRM connector + * @property: DRM property + * @val: value of property + * + * Provides a default plane disablle handler using the atomic driver interface. + * + * RETURNS: + * Zero on success, error code on failure + */ +int +drm_atomic_helper_connector_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t val) +{ + struct drm_atomic_state *state; + struct drm_connector_state *connector_state; + int ret = 0; + + state = drm_atomic_state_alloc(connector->dev); + if (!state) + return -ENOMEM; + + /* ->set_property is always called with all locks held. */ + state->acquire_ctx = connector->dev->mode_config.acquire_ctx; +retry: + connector_state = drm_atomic_get_connector_state(state, connector); + if (IS_ERR(connector_state)) { + ret = PTR_ERR(connector_state); + goto fail; + } + + ret = connector->funcs->atomic_set_property(connector, connector_state, + property, val); + if (ret) + goto fail; + + ret = drm_atomic_commit(state); + if (ret != 0) + goto fail; + + /* Driver takes ownership of state on successful commit. */ + return 0; +fail: + if (ret == -EDEADLK) + goto backoff; + + drm_atomic_state_free(state); + + return ret; +backoff: + drm_atomic_legacy_backoff(state); + drm_atomic_state_clear(state); + + goto retry; +} +EXPORT_SYMBOL(drm_atomic_helper_connector_set_property); + +/** + * drm_atomic_helper_page_flip - execute a legacy page flip + * @crtc: DRM crtc + * @fb: DRM framebuffer + * @event: optional DRM event to signal upon completion + * @flags: flip flags for non-vblank sync'ed updates + * + * Provides a default page flip implementation using the atomic driver interface. + * + * Note that for now so called async page flips (i.e. updates which are not + * synchronized to vblank) are not supported, since the atomic interfaces have + * no provisions for this yet. + * + * Returns: + * Returns 0 on success, negative errno numbers on failure. + */ +int drm_atomic_helper_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t flags) +{ + struct drm_plane *plane = crtc->primary; + struct drm_atomic_state *state; + struct drm_plane_state *plane_state; + struct drm_crtc_state *crtc_state; + int ret = 0; + + if (flags & DRM_MODE_PAGE_FLIP_ASYNC) + return -EINVAL; + + state = drm_atomic_state_alloc(plane->dev); + if (!state) + return -ENOMEM; + + state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); +retry: + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + goto fail; + } + crtc_state->event = event; + + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) { + ret = PTR_ERR(plane_state); + goto fail; + } + + ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); + if (ret != 0) + goto fail; + drm_atomic_set_fb_for_plane(plane_state, fb); + + ret = drm_atomic_async_commit(state); + if (ret != 0) + goto fail; + + /* TODO: ->page_flip is the only driver callback where the core + * doesn't update plane->fb. For now patch it up here. */ + plane->fb = plane->state->fb; + + /* Driver takes ownership of state on successful async commit. */ + return 0; +fail: + if (ret == -EDEADLK) + goto backoff; + + drm_atomic_state_free(state); + + return ret; +backoff: + drm_atomic_legacy_backoff(state); + drm_atomic_state_clear(state); + + /* + * Someone might have exchanged the framebuffer while we dropped locks + * in the backoff code. We need to fix up the fb refcount tracking the + * core does for us. + */ + plane->old_fb = plane->fb; + + goto retry; +} +EXPORT_SYMBOL(drm_atomic_helper_page_flip); + +/** + * DOC: atomic state reset and initialization + * + * Both the drm core and the atomic helpers assume that there is always the full + * and correct atomic software state for all connectors, CRTCs and planes + * available. Which is a bit a problem on driver load and also after system + * suspend. One way to solve this is to have a hardware state read-out + * infrastructure which reconstructs the full software state (e.g. the i915 + * driver). + * + * The simpler solution is to just reset the software state to everything off, + * which is easiest to do by calling drm_mode_config_reset(). To facilitate this + * the atomic helpers provide default reset implementations for all hooks. + */ + +/** + * drm_atomic_helper_crtc_reset - default ->reset hook for CRTCs + * @crtc: drm CRTC + * + * Resets the atomic state for @crtc by freeing the state pointer (which might + * be NULL, e.g. at driver load time) and allocating a new empty state object. + */ +void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) +{ + kfree(crtc->state); + crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL); +} +EXPORT_SYMBOL(drm_atomic_helper_crtc_reset); + +/** + * drm_atomic_helper_crtc_duplicate_state - default state duplicate hook + * @crtc: drm CRTC + * + * Default CRTC state duplicate hook for drivers which don't have their own + * subclassed CRTC state structure. + */ +struct drm_crtc_state * +drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc) +{ + struct drm_crtc_state *state; + + if (WARN_ON(!crtc->state)) + return NULL; + + state = kmemdup(crtc->state, sizeof(*crtc->state), GFP_KERNEL); + + if (state) { + state->mode_changed = false; + state->planes_changed = false; + state->event = NULL; + } + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state); + +/** + * drm_atomic_helper_crtc_destroy_state - default state destroy hook + * @crtc: drm CRTC + * @state: CRTC state object to release + * + * Default CRTC state destroy hook for drivers which don't have their own + * subclassed CRTC state structure. + */ +void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + kfree(state); +} +EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state); + +/** + * drm_atomic_helper_plane_reset - default ->reset hook for planes + * @plane: drm plane + * + * Resets the atomic state for @plane by freeing the state pointer (which might + * be NULL, e.g. at driver load time) and allocating a new empty state object. + */ +void drm_atomic_helper_plane_reset(struct drm_plane *plane) +{ + if (plane->state && plane->state->fb) + drm_framebuffer_unreference(plane->state->fb); + + kfree(plane->state); + plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL); +} +EXPORT_SYMBOL(drm_atomic_helper_plane_reset); + +/** + * drm_atomic_helper_plane_duplicate_state - default state duplicate hook + * @plane: drm plane + * + * Default plane state duplicate hook for drivers which don't have their own + * subclassed plane state structure. + */ +struct drm_plane_state * +drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane) +{ + struct drm_plane_state *state; + + if (WARN_ON(!plane->state)) + return NULL; + + state = kmemdup(plane->state, sizeof(*plane->state), GFP_KERNEL); + + if (state && state->fb) + drm_framebuffer_reference(state->fb); + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state); + +/** + * drm_atomic_helper_plane_destroy_state - default state destroy hook + * @plane: drm plane + * @state: plane state object to release + * + * Default plane state destroy hook for drivers which don't have their own + * subclassed plane state structure. + */ +void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + if (state->fb) + drm_framebuffer_unreference(state->fb); + + kfree(state); +} +EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state); + +/** + * drm_atomic_helper_connector_reset - default ->reset hook for connectors + * @connector: drm connector + * + * Resets the atomic state for @connector by freeing the state pointer (which + * might be NULL, e.g. at driver load time) and allocating a new empty state + * object. + */ +void drm_atomic_helper_connector_reset(struct drm_connector *connector) +{ + kfree(connector->state); + connector->state = kzalloc(sizeof(*connector->state), GFP_KERNEL); +} +EXPORT_SYMBOL(drm_atomic_helper_connector_reset); + +/** + * drm_atomic_helper_connector_duplicate_state - default state duplicate hook + * @connector: drm connector + * + * Default connector state duplicate hook for drivers which don't have their own + * subclassed connector state structure. + */ +struct drm_connector_state * +drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector) +{ + if (WARN_ON(!connector->state)) + return NULL; + + return kmemdup(connector->state, sizeof(*connector->state), GFP_KERNEL); +} +EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state); + +/** + * drm_atomic_helper_connector_destroy_state - default state destroy hook + * @connector: drm connector + * @state: connector state object to release + * + * Default connector state destroy hook for drivers which don't have their own + * subclassed connector state structure. + */ +void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, + struct drm_connector_state *state) +{ + kfree(state); +} +EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index e79c8d3700d8..e6c169152bf1 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -766,7 +766,6 @@ static void drm_mode_remove(struct drm_connector *connector, /** * drm_connector_get_cmdline_mode - reads the user's cmdline mode * @connector: connector to quwery - * @mode: returned mode * * The kernel supports per-connector configration of its consoles through * use of the video= parameter. This function parses that option and @@ -2943,7 +2942,7 @@ EXPORT_SYMBOL(drm_mode_legacy_fb_format); * @file_priv: drm file for the ioctl call * * Add a new FB to the specified CRTC, given a user request. This is the - * original addfb ioclt which only supported RGB formats. + * original addfb ioctl which only supported RGB formats. * * Called by the user via ioctl. * @@ -2955,11 +2954,9 @@ int drm_mode_addfb(struct drm_device *dev, { struct drm_mode_fb_cmd *or = data; struct drm_mode_fb_cmd2 r = {}; - struct drm_mode_config *config = &dev->mode_config; - struct drm_framebuffer *fb; - int ret = 0; + int ret; - /* Use new struct with format internally */ + /* convert to new format and call new ioctl */ r.fb_id = or->fb_id; r.width = or->width; r.height = or->height; @@ -2967,26 +2964,11 @@ int drm_mode_addfb(struct drm_device *dev, r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth); r.handles[0] = or->handle; - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - if ((config->min_width > r.width) || (r.width > config->max_width)) - return -EINVAL; - - if ((config->min_height > r.height) || (r.height > config->max_height)) - return -EINVAL; - - fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r); - if (IS_ERR(fb)) { - DRM_DEBUG_KMS("could not create framebuffer\n"); - return PTR_ERR(fb); - } + ret = drm_mode_addfb2(dev, &r, file_priv); + if (ret) + return ret; - mutex_lock(&file_priv->fbs_lock); - or->fb_id = fb->base.id; - list_add(&fb->filp_head, &file_priv->fbs); - DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); - mutex_unlock(&file_priv->fbs_lock); + or->fb_id = r.fb_id; return ret; } @@ -3080,7 +3062,7 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) num_planes = drm_format_num_planes(r->pixel_format); if (r->width == 0 || r->width % hsub) { - DRM_DEBUG_KMS("bad framebuffer width %u\n", r->height); + DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width); return -EINVAL; } @@ -3435,6 +3417,10 @@ void drm_fb_release(struct drm_file *priv) * object with drm_object_attach_property. The returned property object must be * freed with drm_property_destroy. * + * Note that the DRM core keeps a per-device list of properties and that, if + * drm_mode_config_cleanup() is called, it will destroy all properties created + * by the driver. + * * Returns: * A pointer to the newly created property on success, NULL on failure. */ @@ -3611,7 +3597,7 @@ static struct drm_property *property_create_range(struct drm_device *dev, * object with drm_object_attach_property. The returned property object must be * freed with drm_property_destroy. * - * Userspace is allowed to set any interger value in the (min, max) range + * Userspace is allowed to set any integer value in the (min, max) range * inclusive. * * Returns: @@ -4019,6 +4005,19 @@ done: return ret; } +/** + * drm_mode_connector_set_path_property - set tile property on connector + * @connector: connector to set property on. + * @path: path to use for property. + * + * This creates a property to expose to userspace to specify a + * connector path. This is mainly used for DisplayPort MST where + * connectors have a topology and we want to allow userspace to give + * them more meaningful names. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_connector_set_path_property(struct drm_connector *connector, char *path) { diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 6c65a0a28fbd..d552708409de 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -34,12 +34,35 @@ #include <linux/moduleparam.h> #include <drm/drmP.h> +#include <drm/drm_atomic.h> #include <drm/drm_crtc.h> #include <drm/drm_fourcc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_edid.h> +/** + * DOC: overview + * + * The CRTC modeset helper library provides a default set_config implementation + * in drm_crtc_helper_set_config(). Plus a few other convenience functions using + * the same callbacks which drivers can use to e.g. restore the modeset + * configuration on resume with drm_helper_resume_force_mode(). + * + * The driver callbacks are mostly compatible with the atomic modeset helpers, + * except for the handling of the primary plane: Atomic helpers require that the + * primary plane is implemented as a real standalone plane and not directly tied + * to the CRTC state. For easier transition this library provides functions to + * implement the old semantics required by the CRTC helpers using the new plane + * and atomic helper callbacks. + * + * Drivers are strongly urged to convert to the atomic helpers (by way of first + * converting to the plane helpers). New drivers must not use these functions + * but need to implement the atomic interface instead, potentially using the + * atomic helpers for that. + */ MODULE_AUTHOR("David Airlie, Jesse Barnes"); MODULE_DESCRIPTION("DRM KMS helper"); MODULE_LICENSE("GPL and additional rights"); @@ -888,3 +911,112 @@ void drm_helper_resume_force_mode(struct drm_device *dev) drm_modeset_unlock_all(dev); } EXPORT_SYMBOL(drm_helper_resume_force_mode); + +/** + * drm_helper_crtc_mode_set - mode_set implementation for atomic plane helpers + * @crtc: DRM CRTC + * @mode: DRM display mode which userspace requested + * @adjusted_mode: DRM display mode adjusted by ->mode_fixup callbacks + * @x: x offset of the CRTC scanout area on the underlying framebuffer + * @y: y offset of the CRTC scanout area on the underlying framebuffer + * @old_fb: previous framebuffer + * + * This function implements a callback useable as the ->mode_set callback + * required by the crtc helpers. Besides the atomic plane helper functions for + * the primary plane the driver must also provide the ->mode_set_nofb callback + * to set up the crtc. + * + * This is a transitional helper useful for converting drivers to the atomic + * interfaces. + */ +int drm_helper_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct drm_crtc_state *crtc_state; + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + int ret; + + if (crtc->funcs->atomic_duplicate_state) + crtc_state = crtc->funcs->atomic_duplicate_state(crtc); + else if (crtc->state) + crtc_state = kmemdup(crtc->state, sizeof(*crtc_state), + GFP_KERNEL); + else + crtc_state = kzalloc(sizeof(*crtc_state), GFP_KERNEL); + if (!crtc_state) + return -ENOMEM; + + crtc_state->enable = true; + crtc_state->planes_changed = true; + crtc_state->mode_changed = true; + drm_mode_copy(&crtc_state->mode, mode); + drm_mode_copy(&crtc_state->adjusted_mode, adjusted_mode); + + if (crtc_funcs->atomic_check) { + ret = crtc_funcs->atomic_check(crtc, crtc_state); + if (ret) { + kfree(crtc_state); + + return ret; + } + } + + swap(crtc->state, crtc_state); + + crtc_funcs->mode_set_nofb(crtc); + + if (crtc_state) { + if (crtc->funcs->atomic_destroy_state) + crtc->funcs->atomic_destroy_state(crtc, crtc_state); + else + kfree(crtc_state); + } + + return drm_helper_crtc_mode_set_base(crtc, x, y, old_fb); +} +EXPORT_SYMBOL(drm_helper_crtc_mode_set); + +/** + * drm_helper_crtc_mode_set_base - mode_set_base implementation for atomic plane helpers + * @crtc: DRM CRTC + * @x: x offset of the CRTC scanout area on the underlying framebuffer + * @y: y offset of the CRTC scanout area on the underlying framebuffer + * @old_fb: previous framebuffer + * + * This function implements a callback useable as the ->mode_set_base used + * required by the crtc helpers. The driver must provide the atomic plane helper + * functions for the primary plane. + * + * This is a transitional helper useful for converting drivers to the atomic + * interfaces. + */ +int drm_helper_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct drm_plane_state *plane_state; + struct drm_plane *plane = crtc->primary; + + if (plane->funcs->atomic_duplicate_state) + plane_state = plane->funcs->atomic_duplicate_state(plane); + else if (plane->state) + plane_state = drm_atomic_helper_plane_duplicate_state(plane); + else + plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL); + if (!plane_state) + return -ENOMEM; + + plane_state->crtc = crtc; + drm_atomic_set_fb_for_plane(plane_state, crtc->primary->fb); + plane_state->crtc_x = 0; + plane_state->crtc_y = 0; + plane_state->crtc_h = crtc->mode.vdisplay; + plane_state->crtc_w = crtc->mode.hdisplay; + plane_state->src_x = x << 16; + plane_state->src_y = y << 16; + plane_state->src_h = crtc->mode.vdisplay << 16; + plane_state->src_w = crtc->mode.hdisplay << 16; + + return drm_plane_helper_commit(plane, plane_state, old_fb); +} +EXPORT_SYMBOL(drm_helper_crtc_mode_set_base); diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 08e33b8b13a4..959e2074b0d4 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -39,198 +39,6 @@ * blocks, ... */ -/* Run a single AUX_CH I2C transaction, writing/reading data as necessary */ -static int -i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode, - uint8_t write_byte, uint8_t *read_byte) -{ - struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; - int ret; - - ret = (*algo_data->aux_ch)(adapter, mode, - write_byte, read_byte); - return ret; -} - -/* - * I2C over AUX CH - */ - -/* - * Send the address. If the I2C link is running, this 'restarts' - * the connection with the new address, this is used for doing - * a write followed by a read (as needed for DDC) - */ -static int -i2c_algo_dp_aux_address(struct i2c_adapter *adapter, u16 address, bool reading) -{ - struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; - int mode = MODE_I2C_START; - int ret; - - if (reading) - mode |= MODE_I2C_READ; - else - mode |= MODE_I2C_WRITE; - algo_data->address = address; - algo_data->running = true; - ret = i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL); - return ret; -} - -/* - * Stop the I2C transaction. This closes out the link, sending - * a bare address packet with the MOT bit turned off - */ -static void -i2c_algo_dp_aux_stop(struct i2c_adapter *adapter, bool reading) -{ - struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; - int mode = MODE_I2C_STOP; - - if (reading) - mode |= MODE_I2C_READ; - else - mode |= MODE_I2C_WRITE; - if (algo_data->running) { - (void) i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL); - algo_data->running = false; - } -} - -/* - * Write a single byte to the current I2C address, the - * the I2C link must be running or this returns -EIO - */ -static int -i2c_algo_dp_aux_put_byte(struct i2c_adapter *adapter, u8 byte) -{ - struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; - int ret; - - if (!algo_data->running) - return -EIO; - - ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_WRITE, byte, NULL); - return ret; -} - -/* - * Read a single byte from the current I2C address, the - * I2C link must be running or this returns -EIO - */ -static int -i2c_algo_dp_aux_get_byte(struct i2c_adapter *adapter, u8 *byte_ret) -{ - struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; - int ret; - - if (!algo_data->running) - return -EIO; - - ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_READ, 0, byte_ret); - return ret; -} - -static int -i2c_algo_dp_aux_xfer(struct i2c_adapter *adapter, - struct i2c_msg *msgs, - int num) -{ - int ret = 0; - bool reading = false; - int m; - int b; - - for (m = 0; m < num; m++) { - u16 len = msgs[m].len; - u8 *buf = msgs[m].buf; - reading = (msgs[m].flags & I2C_M_RD) != 0; - ret = i2c_algo_dp_aux_address(adapter, msgs[m].addr, reading); - if (ret < 0) - break; - if (reading) { - for (b = 0; b < len; b++) { - ret = i2c_algo_dp_aux_get_byte(adapter, &buf[b]); - if (ret < 0) - break; - } - } else { - for (b = 0; b < len; b++) { - ret = i2c_algo_dp_aux_put_byte(adapter, buf[b]); - if (ret < 0) - break; - } - } - if (ret < 0) - break; - } - if (ret >= 0) - ret = num; - i2c_algo_dp_aux_stop(adapter, reading); - DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret); - return ret; -} - -static u32 -i2c_algo_dp_aux_functionality(struct i2c_adapter *adapter) -{ - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | - I2C_FUNC_SMBUS_READ_BLOCK_DATA | - I2C_FUNC_SMBUS_BLOCK_PROC_CALL | - I2C_FUNC_10BIT_ADDR; -} - -static const struct i2c_algorithm i2c_dp_aux_algo = { - .master_xfer = i2c_algo_dp_aux_xfer, - .functionality = i2c_algo_dp_aux_functionality, -}; - -static void -i2c_dp_aux_reset_bus(struct i2c_adapter *adapter) -{ - (void) i2c_algo_dp_aux_address(adapter, 0, false); - (void) i2c_algo_dp_aux_stop(adapter, false); -} - -static int -i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter) -{ - adapter->algo = &i2c_dp_aux_algo; - adapter->retries = 3; - i2c_dp_aux_reset_bus(adapter); - return 0; -} - -/** - * i2c_dp_aux_add_bus() - register an i2c adapter using the aux ch helper - * @adapter: i2c adapter to register - * - * This registers an i2c adapter that uses dp aux channel as it's underlaying - * transport. The driver needs to fill out the &i2c_algo_dp_aux_data structure - * and store it in the algo_data member of the @adapter argument. This will be - * used by the i2c over dp aux algorithm to drive the hardware. - * - * RETURNS: - * 0 on success, -ERRNO on failure. - * - * IMPORTANT: - * This interface is deprecated, please switch to the new dp aux helpers and - * drm_dp_aux_register(). - */ -int -i2c_dp_aux_add_bus(struct i2c_adapter *adapter) -{ - int error; - - error = i2c_dp_aux_prepare_bus(adapter); - if (error) - return error; - error = i2c_add_adapter(adapter); - return error; -} -EXPORT_SYMBOL(i2c_dp_aux_add_bus); - /* Helpers for DP link training */ static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r) { @@ -654,10 +462,12 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) case DP_AUX_I2C_REPLY_NACK: DRM_DEBUG_KMS("I2C nack\n"); + aux->i2c_nack_count++; return -EREMOTEIO; case DP_AUX_I2C_REPLY_DEFER: DRM_DEBUG_KMS("I2C defer\n"); + aux->i2c_defer_count++; usleep_range(400, 500); continue; diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 070f913d2dba..dc98b8f78168 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1011,19 +1011,20 @@ static void drm_dp_check_port_guid(struct drm_dp_mst_branch *mstb, static void build_mst_prop_path(struct drm_dp_mst_port *port, struct drm_dp_mst_branch *mstb, - char *proppath) + char *proppath, + size_t proppath_size) { int i; char temp[8]; - snprintf(proppath, 255, "mst:%d", mstb->mgr->conn_base_id); + snprintf(proppath, proppath_size, "mst:%d", mstb->mgr->conn_base_id); for (i = 0; i < (mstb->lct - 1); i++) { int shift = (i % 2) ? 0 : 4; int port_num = mstb->rad[i / 2] >> shift; - snprintf(temp, 8, "-%d", port_num); - strncat(proppath, temp, 255); + snprintf(temp, sizeof(temp), "-%d", port_num); + strlcat(proppath, temp, proppath_size); } - snprintf(temp, 8, "-%d", port->port_num); - strncat(proppath, temp, 255); + snprintf(temp, sizeof(temp), "-%d", port->port_num); + strlcat(proppath, temp, proppath_size); } static void drm_dp_add_port(struct drm_dp_mst_branch *mstb, @@ -1094,7 +1095,7 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb, if (created && !port->input) { char proppath[255]; - build_mst_prop_path(port, mstb, proppath); + build_mst_prop_path(port, mstb, proppath, sizeof(proppath)); port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr, port, proppath); } diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index bc3da32d4585..2e5c7d941313 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -56,7 +56,7 @@ static struct idr drm_minors_idr; struct class *drm_class; static struct dentry *drm_debugfs_root; -void drm_err(const char *func, const char *format, ...) +void drm_err(const char *format, ...) { struct va_format vaf; va_list args; @@ -66,7 +66,8 @@ void drm_err(const char *func, const char *format, ...) vaf.fmt = format; vaf.va = &args; - printk(KERN_ERR "[" DRM_NAME ":%s] *ERROR* %pV", func, &vaf); + printk(KERN_ERR "[" DRM_NAME ":%pf] *ERROR* %pV", + __builtin_return_address(0), &vaf); va_end(args); } diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 0c0c39bac23d..09d47e9ba026 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1570,7 +1570,6 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) modeset = &fb_helper->crtc_info[i].mode_set; if (modeset->num_connectors == 0) { BUG_ON(modeset->fb); - BUG_ON(modeset->num_connectors); if (modeset->mode) drm_mode_destroy(dev, modeset->mode); modeset->mode = NULL; diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index ed7bc68f7e87..91e1105f2800 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -515,10 +515,12 @@ ssize_t drm_read(struct file *filp, char __user *buffer, size_t total; ssize_t ret; - ret = wait_event_interruptible(file_priv->event_wait, - !list_empty(&file_priv->event_list)); - if (ret < 0) - return ret; + if ((filp->f_flags & O_NONBLOCK) == 0) { + ret = wait_event_interruptible(file_priv->event_wait, + !list_empty(&file_priv->event_list)); + if (ret < 0) + return ret; + } total = 0; while (drm_dequeue_event(file_priv, total, count, &e)) { @@ -532,7 +534,7 @@ ssize_t drm_read(struct file *filp, char __user *buffer, e->destroy(e); } - return total; + return total ?: -EAGAIN; } EXPORT_SYMBOL(drm_read); diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 5ef03c216a27..3e6b582f60dd 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -1190,7 +1190,7 @@ EXPORT_SYMBOL(drm_crtc_vblank_off); * * This functions restores the vblank interrupt state captured with * drm_vblank_off() again. Note that calls to drm_vblank_on() and - * drm_vblank_off() can be unbalanced and so can also be unconditionaly called + * drm_vblank_off() can be unbalanced and so can also be unconditionally called * in driver load code to reflect the current hardware state of the crtc. * * This is the legacy version of drm_crtc_vblank_on(). @@ -1237,7 +1237,7 @@ EXPORT_SYMBOL(drm_vblank_on); * * This functions restores the vblank interrupt state captured with * drm_vblank_off() again. Note that calls to drm_vblank_on() and - * drm_vblank_off() can be unbalanced and so can also be unconditionaly called + * drm_vblank_off() can be unbalanced and so can also be unconditionally called * in driver load code to reflect the current hardware state of the crtc. * * This is the native kms version of drm_vblank_on(). diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index d1b7d2006529..6d8b941c8200 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -914,7 +914,7 @@ EXPORT_SYMBOL(drm_mode_equal_no_clocks_no_stereo); * * This function is a helper which can be used to validate modes against size * limitations of the DRM device/connector. If a mode is too big its status - * memeber is updated with the appropriate validation failure code. The list + * member is updated with the appropriate validation failure code. The list * itself is not changed. */ void drm_mode_validate_size(struct drm_device *dev, diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index fe4d1fb2376c..93c6533c25da 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -27,10 +27,38 @@ #include <drm/drmP.h> #include <drm/drm_plane_helper.h> #include <drm/drm_rect.h> -#include <drm/drm_plane_helper.h> +#include <drm/drm_atomic.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic_helper.h> #define SUBPIXEL_MASK 0xffff +/** + * DOC: overview + * + * This helper library has two parts. The first part has support to implement + * primary plane support on top of the normal CRTC configuration interface. + * Since the legacy ->set_config interface ties the primary plane together with + * the CRTC state this does not allow userspace to disable the primary plane + * itself. To avoid too much duplicated code use + * drm_plane_helper_check_update() which can be used to enforce the same + * restrictions as primary planes had thus. The default primary plane only + * expose XRBG8888 and ARGB8888 as valid pixel formats for the attached + * framebuffer. + * + * Drivers are highly recommended to implement proper support for primary + * planes, and newly merged drivers must not rely upon these transitional + * helpers. + * + * The second part also implements transitional helpers which allow drivers to + * gradually switch to the atomic helper infrastructure for plane updates. Once + * that switch is complete drivers shouldn't use these any longer, instead using + * the proper legacy implementations for update and disable plane hooks provided + * by the atomic helpers. + * + * Again drivers are strongly urged to switch to the new interfaces. + */ + /* * This is the minimal list of formats that seem to be safe for modeset use * with all current DRM drivers. Most hardware can actually support more @@ -374,3 +402,171 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, return drm_crtc_init_with_planes(dev, crtc, primary, NULL, funcs); } EXPORT_SYMBOL(drm_crtc_init); + +int drm_plane_helper_commit(struct drm_plane *plane, + struct drm_plane_state *plane_state, + struct drm_framebuffer *old_fb) +{ + struct drm_plane_helper_funcs *plane_funcs; + struct drm_crtc *crtc[2]; + struct drm_crtc_helper_funcs *crtc_funcs[2]; + int i, ret = 0; + + plane_funcs = plane->helper_private; + + /* Since this is a transitional helper we can't assume that plane->state + * is always valid. Hence we need to use plane->crtc instead of + * plane->state->crtc as the old crtc. */ + crtc[0] = plane->crtc; + crtc[1] = crtc[0] != plane_state->crtc ? plane_state->crtc : NULL; + + for (i = 0; i < 2; i++) + crtc_funcs[i] = crtc[i] ? crtc[i]->helper_private : NULL; + + if (plane_funcs->atomic_check) { + ret = plane_funcs->atomic_check(plane, plane_state); + if (ret) + goto out; + } + + if (plane_funcs->prepare_fb && plane_state->fb) { + ret = plane_funcs->prepare_fb(plane, plane_state->fb); + if (ret) + goto out; + } + + /* Point of no return, commit sw state. */ + swap(plane->state, plane_state); + + for (i = 0; i < 2; i++) { + if (crtc_funcs[i] && crtc_funcs[i]->atomic_begin) + crtc_funcs[i]->atomic_begin(crtc[i]); + } + + plane_funcs->atomic_update(plane); + + for (i = 0; i < 2; i++) { + if (crtc_funcs[i] && crtc_funcs[i]->atomic_flush) + crtc_funcs[i]->atomic_flush(crtc[i]); + } + + for (i = 0; i < 2; i++) { + if (!crtc[i]) + continue; + + /* There's no other way to figure out whether the crtc is running. */ + ret = drm_crtc_vblank_get(crtc[i]); + if (ret == 0) { + drm_crtc_wait_one_vblank(crtc[i]); + drm_crtc_vblank_put(crtc[i]); + } + + ret = 0; + } + + if (plane_funcs->cleanup_fb && old_fb) + plane_funcs->cleanup_fb(plane, old_fb); +out: + if (plane_state) { + if (plane->funcs->atomic_destroy_state) + plane->funcs->atomic_destroy_state(plane, plane_state); + else + drm_atomic_helper_plane_destroy_state(plane, plane_state); + } + + return ret; +} + +/** + * drm_plane_helper_update() - Helper for primary plane update + * @plane: plane object to update + * @crtc: owning CRTC of owning plane + * @fb: framebuffer to flip onto plane + * @crtc_x: x offset of primary plane on crtc + * @crtc_y: y offset of primary plane on crtc + * @crtc_w: width of primary plane rectangle on crtc + * @crtc_h: height of primary plane rectangle on crtc + * @src_x: x offset of @fb for panning + * @src_y: y offset of @fb for panning + * @src_w: width of source rectangle in @fb + * @src_h: height of source rectangle in @fb + * + * Provides a default plane update handler using the atomic plane update + * functions. It is fully left to the driver to check plane constraints and + * handle corner-cases like a fully occluded or otherwise invisible plane. + * + * This is useful for piecewise transitioning of a driver to the atomic helpers. + * + * RETURNS: + * Zero on success, error code on failure + */ +int drm_plane_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + struct drm_plane_state *plane_state; + + if (plane->funcs->atomic_duplicate_state) + plane_state = plane->funcs->atomic_duplicate_state(plane); + else if (plane->state) + plane_state = drm_atomic_helper_plane_duplicate_state(plane); + else + plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL); + if (!plane_state) + return -ENOMEM; + + plane_state->crtc = crtc; + drm_atomic_set_fb_for_plane(plane_state, fb); + plane_state->crtc_x = crtc_x; + plane_state->crtc_y = crtc_y; + plane_state->crtc_h = crtc_h; + plane_state->crtc_w = crtc_w; + plane_state->src_x = src_x; + plane_state->src_y = src_y; + plane_state->src_h = src_h; + plane_state->src_w = src_w; + + return drm_plane_helper_commit(plane, plane_state, plane->fb); +} +EXPORT_SYMBOL(drm_plane_helper_update); + +/** + * drm_plane_helper_disable() - Helper for primary plane disable + * @plane: plane to disable + * + * Provides a default plane disable handler using the atomic plane update + * functions. It is fully left to the driver to check plane constraints and + * handle corner-cases like a fully occluded or otherwise invisible plane. + * + * This is useful for piecewise transitioning of a driver to the atomic helpers. + * + * RETURNS: + * Zero on success, error code on failure + */ +int drm_plane_helper_disable(struct drm_plane *plane) +{ + struct drm_plane_state *plane_state; + + /* crtc helpers love to call disable functions for already disabled hw + * functions. So cope with that. */ + if (!plane->crtc) + return 0; + + if (plane->funcs->atomic_duplicate_state) + plane_state = plane->funcs->atomic_duplicate_state(plane); + else if (plane->state) + plane_state = drm_atomic_helper_plane_duplicate_state(plane); + else + plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL); + if (!plane_state) + return -ENOMEM; + + plane_state->crtc = NULL; + drm_atomic_set_fb_for_plane(plane_state, NULL); + + return drm_plane_helper_commit(plane, plane_state, plane->fb); +} +EXPORT_SYMBOL(drm_plane_helper_disable); diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 78ca30808422..6872eca6555e 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -328,7 +328,7 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = { */ /** - * drm_gem_prime_export - helper library implemention of the export callback + * drm_gem_prime_export - helper library implementation of the export callback * @dev: drm_device to export from * @obj: GEM object to export * @flags: flags like DRM_CLOEXEC @@ -483,7 +483,7 @@ out_unlock: EXPORT_SYMBOL(drm_gem_prime_handle_to_fd); /** - * drm_gem_prime_import - helper library implemention of the import callback + * drm_gem_prime_import - helper library implementation of the import callback * @dev: drm_device to import into * @dma_buf: dma-buf object to import * diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 6857e9ad6339..7483a47de8e4 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -118,7 +118,8 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect mode->status = MODE_UNVERIFIED; if (connector->force) { - if (connector->force == DRM_FORCE_ON) + if (connector->force == DRM_FORCE_ON || + connector->force == DRM_FORCE_ON_DIGITAL) connector->status = connector_status_connected; else connector->status = connector_status_disconnected; diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index 9f158eab517a..0fafb8e2483a 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -37,6 +37,201 @@ #include "gma_display.h" #include <drm/drm_dp_helper.h> +/** + * struct i2c_algo_dp_aux_data - driver interface structure for i2c over dp + * aux algorithm + * @running: set by the algo indicating whether an i2c is ongoing or whether + * the i2c bus is quiescent + * @address: i2c target address for the currently ongoing transfer + * @aux_ch: driver callback to transfer a single byte of the i2c payload + */ +struct i2c_algo_dp_aux_data { + bool running; + u16 address; + int (*aux_ch) (struct i2c_adapter *adapter, + int mode, uint8_t write_byte, + uint8_t *read_byte); +}; + +/* Run a single AUX_CH I2C transaction, writing/reading data as necessary */ +static int +i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode, + uint8_t write_byte, uint8_t *read_byte) +{ + struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; + int ret; + + ret = (*algo_data->aux_ch)(adapter, mode, + write_byte, read_byte); + return ret; +} + +/* + * I2C over AUX CH + */ + +/* + * Send the address. If the I2C link is running, this 'restarts' + * the connection with the new address, this is used for doing + * a write followed by a read (as needed for DDC) + */ +static int +i2c_algo_dp_aux_address(struct i2c_adapter *adapter, u16 address, bool reading) +{ + struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; + int mode = MODE_I2C_START; + int ret; + + if (reading) + mode |= MODE_I2C_READ; + else + mode |= MODE_I2C_WRITE; + algo_data->address = address; + algo_data->running = true; + ret = i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL); + return ret; +} + +/* + * Stop the I2C transaction. This closes out the link, sending + * a bare address packet with the MOT bit turned off + */ +static void +i2c_algo_dp_aux_stop(struct i2c_adapter *adapter, bool reading) +{ + struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; + int mode = MODE_I2C_STOP; + + if (reading) + mode |= MODE_I2C_READ; + else + mode |= MODE_I2C_WRITE; + if (algo_data->running) { + (void) i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL); + algo_data->running = false; + } +} + +/* + * Write a single byte to the current I2C address, the + * the I2C link must be running or this returns -EIO + */ +static int +i2c_algo_dp_aux_put_byte(struct i2c_adapter *adapter, u8 byte) +{ + struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; + int ret; + + if (!algo_data->running) + return -EIO; + + ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_WRITE, byte, NULL); + return ret; +} + +/* + * Read a single byte from the current I2C address, the + * I2C link must be running or this returns -EIO + */ +static int +i2c_algo_dp_aux_get_byte(struct i2c_adapter *adapter, u8 *byte_ret) +{ + struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; + int ret; + + if (!algo_data->running) + return -EIO; + + ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_READ, 0, byte_ret); + return ret; +} + +static int +i2c_algo_dp_aux_xfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, + int num) +{ + int ret = 0; + bool reading = false; + int m; + int b; + + for (m = 0; m < num; m++) { + u16 len = msgs[m].len; + u8 *buf = msgs[m].buf; + reading = (msgs[m].flags & I2C_M_RD) != 0; + ret = i2c_algo_dp_aux_address(adapter, msgs[m].addr, reading); + if (ret < 0) + break; + if (reading) { + for (b = 0; b < len; b++) { + ret = i2c_algo_dp_aux_get_byte(adapter, &buf[b]); + if (ret < 0) + break; + } + } else { + for (b = 0; b < len; b++) { + ret = i2c_algo_dp_aux_put_byte(adapter, buf[b]); + if (ret < 0) + break; + } + } + if (ret < 0) + break; + } + if (ret >= 0) + ret = num; + i2c_algo_dp_aux_stop(adapter, reading); + DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret); + return ret; +} + +static u32 +i2c_algo_dp_aux_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_SMBUS_READ_BLOCK_DATA | + I2C_FUNC_SMBUS_BLOCK_PROC_CALL | + I2C_FUNC_10BIT_ADDR; +} + +static const struct i2c_algorithm i2c_dp_aux_algo = { + .master_xfer = i2c_algo_dp_aux_xfer, + .functionality = i2c_algo_dp_aux_functionality, +}; + +static void +i2c_dp_aux_reset_bus(struct i2c_adapter *adapter) +{ + (void) i2c_algo_dp_aux_address(adapter, 0, false); + (void) i2c_algo_dp_aux_stop(adapter, false); +} + +static int +i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter) +{ + adapter->algo = &i2c_dp_aux_algo; + adapter->retries = 3; + i2c_dp_aux_reset_bus(adapter); + return 0; +} + +/* + * FIXME: This is the old dp aux helper, gma500 is the last driver that needs to + * be ported over to the new helper code in drm_dp_helper.c like i915 or radeon. + */ +static int __deprecated +i2c_dp_aux_add_bus(struct i2c_adapter *adapter) +{ + int error; + + error = i2c_dp_aux_prepare_bus(adapter); + if (error) + return error; + error = i2c_add_adapter(adapter); + return error; +} + #define _wait_for(COND, MS, W) ({ \ unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \ int ret__ = 0; \ diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c index 87b50ba64ed4..b21a09451d1d 100644 --- a/drivers/gpu/drm/gma500/psb_intel_display.c +++ b/drivers/gpu/drm/gma500/psb_intel_display.c @@ -21,6 +21,7 @@ #include <linux/i2c.h> #include <drm/drmP.h> +#include <drm/drm_plane_helper.h> #include "framebuffer.h" #include "psb_drv.h" #include "psb_intel_drv.h" diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c index 0be96fdb5e28..58529cea575d 100644 --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c @@ -1631,57 +1631,8 @@ static int psb_intel_sdvo_get_modes(struct drm_connector *connector) return !list_empty(&connector->probed_modes); } -static void -psb_intel_sdvo_destroy_enhance_property(struct drm_connector *connector) -{ - struct psb_intel_sdvo_connector *psb_intel_sdvo_connector = to_psb_intel_sdvo_connector(connector); - struct drm_device *dev = connector->dev; - - if (psb_intel_sdvo_connector->left) - drm_property_destroy(dev, psb_intel_sdvo_connector->left); - if (psb_intel_sdvo_connector->right) - drm_property_destroy(dev, psb_intel_sdvo_connector->right); - if (psb_intel_sdvo_connector->top) - drm_property_destroy(dev, psb_intel_sdvo_connector->top); - if (psb_intel_sdvo_connector->bottom) - drm_property_destroy(dev, psb_intel_sdvo_connector->bottom); - if (psb_intel_sdvo_connector->hpos) - drm_property_destroy(dev, psb_intel_sdvo_connector->hpos); - if (psb_intel_sdvo_connector->vpos) - drm_property_destroy(dev, psb_intel_sdvo_connector->vpos); - if (psb_intel_sdvo_connector->saturation) - drm_property_destroy(dev, psb_intel_sdvo_connector->saturation); - if (psb_intel_sdvo_connector->contrast) - drm_property_destroy(dev, psb_intel_sdvo_connector->contrast); - if (psb_intel_sdvo_connector->hue) - drm_property_destroy(dev, psb_intel_sdvo_connector->hue); - if (psb_intel_sdvo_connector->sharpness) - drm_property_destroy(dev, psb_intel_sdvo_connector->sharpness); - if (psb_intel_sdvo_connector->flicker_filter) - drm_property_destroy(dev, psb_intel_sdvo_connector->flicker_filter); - if (psb_intel_sdvo_connector->flicker_filter_2d) - drm_property_destroy(dev, psb_intel_sdvo_connector->flicker_filter_2d); - if (psb_intel_sdvo_connector->flicker_filter_adaptive) - drm_property_destroy(dev, psb_intel_sdvo_connector->flicker_filter_adaptive); - if (psb_intel_sdvo_connector->tv_luma_filter) - drm_property_destroy(dev, psb_intel_sdvo_connector->tv_luma_filter); - if (psb_intel_sdvo_connector->tv_chroma_filter) - drm_property_destroy(dev, psb_intel_sdvo_connector->tv_chroma_filter); - if (psb_intel_sdvo_connector->dot_crawl) - drm_property_destroy(dev, psb_intel_sdvo_connector->dot_crawl); - if (psb_intel_sdvo_connector->brightness) - drm_property_destroy(dev, psb_intel_sdvo_connector->brightness); -} - static void psb_intel_sdvo_destroy(struct drm_connector *connector) { - struct psb_intel_sdvo_connector *psb_intel_sdvo_connector = to_psb_intel_sdvo_connector(connector); - - if (psb_intel_sdvo_connector->tv_format) - drm_property_destroy(connector->dev, - psb_intel_sdvo_connector->tv_format); - - psb_intel_sdvo_destroy_enhance_property(connector); drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c index ef38915075a1..809bb957b452 100644 --- a/drivers/gpu/drm/i915/i915_cmd_parser.c +++ b/drivers/gpu/drm/i915/i915_cmd_parser.c @@ -73,7 +73,7 @@ * those commands required by the parser. This generally works because command * opcode ranges have standard command length encodings. So for commands that * the parser does not need to check, it can easily skip them. This is - * implementated via a per-ring length decoding vfunc. + * implemented via a per-ring length decoding vfunc. * * Unfortunately, there are a number of commands that do not follow the standard * length encoding for their opcode range, primarily amongst the MI_* commands. @@ -843,7 +843,7 @@ finish: * @ring: the ring in question * * Only certain platforms require software batch buffer command parsing, and - * only when enabled via module paramter. + * only when enabled via module parameter. * * Return: true if the ring requires software command parsing */ diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index b250d96ab7a1..d43fa0e627f8 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -672,7 +672,7 @@ enum punit_power_well { * need to be accessed during AUX communication, * * Generally the common lane corresponds to the pipe and - * the spline (PCS/TX) correponds to the port. + * the spline (PCS/TX) corresponds to the port. * * For dual channel PHY (VLV/CHV): * diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 292beb0fa1dc..6025ac754c37 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -1681,7 +1681,7 @@ static int lrc_setup_hardware_status_page(struct intel_engine_cs *ring, * the creation is a deferred call: it's better to make sure first that we need to use * a given ring with the context. * - * Return: non-zero on eror. + * Return: non-zero on error. */ int intel_lr_context_deferred_create(struct intel_context *ctx, struct intel_engine_cs *ring) diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 9350edd6728d..6d7a277458b5 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1991,57 +1991,10 @@ static int intel_sdvo_get_modes(struct drm_connector *connector) return !list_empty(&connector->probed_modes); } -static void -intel_sdvo_destroy_enhance_property(struct drm_connector *connector) -{ - struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector); - struct drm_device *dev = connector->dev; - - if (intel_sdvo_connector->left) - drm_property_destroy(dev, intel_sdvo_connector->left); - if (intel_sdvo_connector->right) - drm_property_destroy(dev, intel_sdvo_connector->right); - if (intel_sdvo_connector->top) - drm_property_destroy(dev, intel_sdvo_connector->top); - if (intel_sdvo_connector->bottom) - drm_property_destroy(dev, intel_sdvo_connector->bottom); - if (intel_sdvo_connector->hpos) - drm_property_destroy(dev, intel_sdvo_connector->hpos); - if (intel_sdvo_connector->vpos) - drm_property_destroy(dev, intel_sdvo_connector->vpos); - if (intel_sdvo_connector->saturation) - drm_property_destroy(dev, intel_sdvo_connector->saturation); - if (intel_sdvo_connector->contrast) - drm_property_destroy(dev, intel_sdvo_connector->contrast); - if (intel_sdvo_connector->hue) - drm_property_destroy(dev, intel_sdvo_connector->hue); - if (intel_sdvo_connector->sharpness) - drm_property_destroy(dev, intel_sdvo_connector->sharpness); - if (intel_sdvo_connector->flicker_filter) - drm_property_destroy(dev, intel_sdvo_connector->flicker_filter); - if (intel_sdvo_connector->flicker_filter_2d) - drm_property_destroy(dev, intel_sdvo_connector->flicker_filter_2d); - if (intel_sdvo_connector->flicker_filter_adaptive) - drm_property_destroy(dev, intel_sdvo_connector->flicker_filter_adaptive); - if (intel_sdvo_connector->tv_luma_filter) - drm_property_destroy(dev, intel_sdvo_connector->tv_luma_filter); - if (intel_sdvo_connector->tv_chroma_filter) - drm_property_destroy(dev, intel_sdvo_connector->tv_chroma_filter); - if (intel_sdvo_connector->dot_crawl) - drm_property_destroy(dev, intel_sdvo_connector->dot_crawl); - if (intel_sdvo_connector->brightness) - drm_property_destroy(dev, intel_sdvo_connector->brightness); -} - static void intel_sdvo_destroy(struct drm_connector *connector) { struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector); - if (intel_sdvo_connector->tv_format) - drm_property_destroy(connector->dev, - intel_sdvo_connector->tv_format); - - intel_sdvo_destroy_enhance_property(connector); drm_connector_cleanup(connector); kfree(intel_sdvo_connector); } diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index 83485ab81ce8..9872ba9abf1a 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -15,6 +15,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_plane_helper.h> #include "mgag200_drv.h" diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c index fca6a1f9c20c..2a03e77abef4 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c @@ -26,6 +26,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_plane_helper.h> #include "nouveau_drm.h" #include "nouveau_reg.h" diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index ae873d1a8d46..76b8c4f980ea 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -26,6 +26,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_plane_helper.h> #include <drm/drm_dp_helper.h> #include <nvif/class.h> diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index 2d28dc337cfb..b0566a1ca28f 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -20,6 +20,7 @@ #include "omap_drv.h" #include <drm/drm_mode.h> +#include <drm/drm_plane_helper.h> #include "drm_crtc.h" #include "drm_crtc_helper.h" diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 0d1396266857..8b7892880ad2 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -29,6 +29,7 @@ #include "qxl_drv.h" #include "qxl_object.h" #include "drm_crtc_helper.h" +#include <drm/drm_plane_helper.h> static bool qxl_head_enabled(struct qxl_head *head) { diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 00ead8c2758a..f1b0fa1285bb 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -32,6 +32,7 @@ #include <linux/pm_runtime.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_plane_helper.h> #include <drm/drm_edid.h> #include <linux/gcd.h> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 148b50589181..088bfd875d29 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -19,6 +19,7 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> +#include <drm/drm_plane_helper.h> #include "rcar_du_crtc.h" #include "rcar_du_drv.h" diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c index 0ddce4d046d9..859ccb658601 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c @@ -19,6 +19,7 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> +#include <drm/drm_plane_helper.h> #include <video/sh_mobile_meram.h> diff --git a/drivers/gpu/drm/sti/sti_drm_crtc.c b/drivers/gpu/drm/sti/sti_drm_crtc.c index d2ae0c0e13be..36a1ad3c4823 100644 --- a/drivers/gpu/drm/sti/sti_drm_crtc.c +++ b/drivers/gpu/drm/sti/sti_drm_crtc.c @@ -10,6 +10,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_plane_helper.h> #include "sti_compositor.h" #include "sti_drm_drv.h" diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 6553fd238685..cdfa126a4725 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -15,6 +15,8 @@ #include "drm.h" #include "gem.h" +#include <drm/drm_plane_helper.h> + struct tegra_dc_soc_info { bool supports_interlacing; bool supports_cursor; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index d642d4a02134..29ec98baffd1 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -16,6 +16,7 @@ */ #include "drm_flip_work.h" +#include <drm/drm_plane_helper.h> #include "tilcdc_drv.h" #include "tilcdc_regs.h" diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c index dc145d320b25..1701f1dfb23f 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -14,6 +14,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_plane_helper.h> #include "udl_drv.h" /* diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 15e185ae4c99..5c289f748ab4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -26,6 +26,7 @@ **************************************************************************/ #include "vmwgfx_kms.h" +#include <drm/drm_plane_helper.h> #define vmw_crtc_to_ldu(x) \ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index b295463a60b3..7dc591d04d9a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -26,6 +26,7 @@ **************************************************************************/ #include "vmwgfx_kms.h" +#include <drm/drm_plane_helper.h> #define vmw_crtc_to_sou(x) \ |