1782 lines · 52.7 KB · C
RawDownload
/* * Copyright (C) 2014 Red Hat * Copyright (C) 2014 Intel Corp. * Copyright (C) 2018 Intel Corp. * Copyright (c) 2020, The Linux Foundation. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: * Rob Clark <robdclark@gmail.com> * Daniel Vetter <daniel.vetter@ffwll.ch> */ #include <drm/drm_atomic.h>#include <drm/drm_atomic_helper.h>#include <drm/drm_atomic_uapi.h>#include <drm/drm_framebuffer.h>#include <drm/drm_print.h>#include <drm/drm_drv.h>#include <drm/drm_writeback.h>#include <drm/drm_vblank.h>#include <drm/drm_colorop.h> #include <linux/export.h>#include <linux/dma-fence.h>#include <linux/uaccess.h>#include <linux/sync_file.h>#include <linux/file.h> #include "drm_crtc_internal.h" /** * DOC: overview * * This file contains the marshalling and demarshalling glue for the atomic UAPI * in all its forms: The monster ATOMIC IOCTL itself, code for GET_PROPERTY and * SET_PROPERTY IOCTLs. Plus interface functions for compatibility helpers and * drivers which have special needs to construct their own atomic updates, e.g. * for load detect or similar. */ /** * drm_atomic_set_mode_for_crtc - set mode for CRTC * @state: the CRTC whose incoming state to update * @mode: kernel-internal mode to use for the CRTC, or NULL to disable * * Set a mode (originating from the kernel) on the desired CRTC state and update * the enable property. * * RETURNS: * Zero on success, error code on failure. Cannot return -EDEADLK. */int drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state,				 const struct drm_display_mode *mode){	struct drm_crtc *crtc = state->crtc;	struct drm_mode_modeinfo umode; 	/* Early return for no change. */	if (mode && memcmp(&state->mode, mode, sizeof(*mode)) == 0)		return 0; 	drm_property_blob_put(state->mode_blob);	state->mode_blob = NULL; 	if (mode) {		struct drm_property_blob *blob; 		drm_mode_convert_to_umode(&umode, mode);		blob = drm_property_create_blob(crtc->dev,						sizeof(umode), &umode);		if (IS_ERR(blob))			return PTR_ERR(blob); 		drm_mode_copy(&state->mode, mode); 		state->mode_blob = blob;		state->enable = true;		drm_dbg_atomic(crtc->dev,			       "Set [MODE:%s] for [CRTC:%d:%s] state %p\n",			       mode->name, crtc->base.id, crtc->name, state);	} else {		memset(&state->mode, 0, sizeof(state->mode));		state->enable = false;		drm_dbg_atomic(crtc->dev,			       "Set [NOMODE] for [CRTC:%d:%s] state %p\n",			       crtc->base.id, crtc->name, state);	} 	return 0;}EXPORT_SYMBOL(drm_atomic_set_mode_for_crtc); /** * drm_atomic_set_mode_prop_for_crtc - set mode for CRTC * @state: the CRTC whose incoming state to update * @blob: pointer to blob property to use for mode * * Set a mode (originating from a blob property) on the desired CRTC state. * This function will take a reference on the blob property for the CRTC state, * and release the reference held on the state's existing mode property, if any * was set. * * RETURNS: * Zero on success, error code on failure. Cannot return -EDEADLK. */int drm_atomic_set_mode_prop_for_crtc(struct drm_crtc_state *state,				      struct drm_property_blob *blob){	struct drm_crtc *crtc = state->crtc; 	if (blob == state->mode_blob)		return 0; 	drm_property_blob_put(state->mode_blob);	state->mode_blob = NULL; 	memset(&state->mode, 0, sizeof(state->mode)); 	if (blob) {		int ret; 		if (blob->length != sizeof(struct drm_mode_modeinfo)) {			drm_dbg_atomic(crtc->dev,				       "[CRTC:%d:%s] bad mode blob length: %zu\n",				       crtc->base.id, crtc->name,				       blob->length);			return -EINVAL;		} 		ret = drm_mode_convert_umode(crtc->dev,					     &state->mode, blob->data);		if (ret) {			drm_dbg_atomic(crtc->dev,				       "[CRTC:%d:%s] invalid mode (%s, %pe): " DRM_MODE_FMT "\n",				       crtc->base.id, crtc->name,				       drm_get_mode_status_name(state->mode.status),				       ERR_PTR(ret), DRM_MODE_ARG(&state->mode));			return -EINVAL;		} 		state->mode_blob = drm_property_blob_get(blob);		state->enable = true;		drm_dbg_atomic(crtc->dev,			       "Set [MODE:%s] for [CRTC:%d:%s] state %p\n",			       state->mode.name, crtc->base.id, crtc->name,			       state);	} else {		state->enable = false;		drm_dbg_atomic(crtc->dev,			       "Set [NOMODE] for [CRTC:%d:%s] state %p\n",			       crtc->base.id, crtc->name, state);	} 	return 0;}EXPORT_SYMBOL(drm_atomic_set_mode_prop_for_crtc); /** * drm_atomic_set_crtc_for_plane - set CRTC for plane * @plane_state: the plane whose incoming state to update * @crtc: CRTC to use for the plane * * Changing the assigned CRTC for a plane requires us to grab the lock and state * for the new CRTC, as needed. This function takes care of all these details * besides updating the pointer in the state object itself. * * Returns: * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK * then the w/w mutex code has detected a deadlock and the entire atomic * sequence must be restarted. All other errors are fatal. */intdrm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state,			      struct drm_crtc *crtc){	struct drm_plane *plane = plane_state->plane;	struct drm_crtc_state *crtc_state;	/* Nothing to do for same crtc*/	if (plane_state->crtc == crtc)		return 0;	if (plane_state->crtc) {		crtc_state = drm_atomic_get_crtc_state(plane_state->state,						       plane_state->crtc);		if (WARN_ON(IS_ERR(crtc_state)))			return PTR_ERR(crtc_state); 		crtc_state->plane_mask &= ~drm_plane_mask(plane);	} 	plane_state->crtc = crtc; 	if (crtc) {		crtc_state = drm_atomic_get_crtc_state(plane_state->state,						       crtc);		if (IS_ERR(crtc_state))			return PTR_ERR(crtc_state);		crtc_state->plane_mask |= drm_plane_mask(plane);	} 	if (crtc)		drm_dbg_atomic(plane->dev,			       "Link [PLANE:%d:%s] state %p to [CRTC:%d:%s]\n",			       plane->base.id, plane->name, plane_state,			       crtc->base.id, crtc->name);	else		drm_dbg_atomic(plane->dev,			       "Link [PLANE:%d:%s] state %p to [NOCRTC]\n",			       plane->base.id, plane->name, plane_state); 	return 0;}EXPORT_SYMBOL(drm_atomic_set_crtc_for_plane); /** * drm_atomic_set_fb_for_plane - set framebuffer for plane * @plane_state: atomic state object for the plane * @fb: fb to use for the plane * * Changing the assigned framebuffer for a plane requires us to grab a reference * to the new fb and drop the reference to the old fb, if there is one. This * function takes care of all these details besides updating the pointer in the * state object itself. */voiddrm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state,			    struct drm_framebuffer *fb){	struct drm_plane *plane = plane_state->plane; 	if (fb)		drm_dbg_atomic(plane->dev,			       "Set [FB:%d] for [PLANE:%d:%s] state %p\n",			       fb->base.id, plane->base.id, plane->name,			       plane_state);	else		drm_dbg_atomic(plane->dev,			       "Set [NOFB] for [PLANE:%d:%s] state %p\n",			       plane->base.id, plane->name, plane_state); 	drm_framebuffer_assign(&plane_state->fb, fb);}EXPORT_SYMBOL(drm_atomic_set_fb_for_plane); /** * drm_atomic_set_colorop_for_plane - set colorop for plane * @plane_state: atomic state object for the plane * @colorop: colorop to use for the plane * * Helper function to select the color pipeline on a plane by setting * it to the first drm_colorop element of the pipeline. * * Return: true if plane color pipeline value changed, false otherwise. */booldrm_atomic_set_colorop_for_plane(struct drm_plane_state *plane_state,				 struct drm_colorop *colorop){	struct drm_plane *plane = plane_state->plane; 	/* Color pipeline didn't change */	if (plane_state->color_pipeline == colorop)		return false; 	if (colorop)		drm_dbg_atomic(plane->dev,			       "Set [COLOROP:%d] for [PLANE:%d:%s] state %p\n",			       colorop->base.id, plane->base.id, plane->name,			       plane_state);	else		drm_dbg_atomic(plane->dev,			       "Set [NOCOLOROP] for [PLANE:%d:%s] state %p\n",			       plane->base.id, plane->name, plane_state); 	plane_state->color_pipeline = colorop; 	return true;}EXPORT_SYMBOL(drm_atomic_set_colorop_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. */intdrm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state,				  struct drm_crtc *crtc){	struct drm_connector *connector = conn_state->connector;	struct drm_crtc_state *crtc_state; 	if (conn_state->crtc == crtc)		return 0; 	if (conn_state->crtc) {		crtc_state = drm_atomic_get_new_crtc_state(conn_state->state,							   conn_state->crtc); 		crtc_state->connector_mask &=			~drm_connector_mask(conn_state->connector); 		drm_connector_put(conn_state->connector);		conn_state->crtc = NULL;	} 	if (crtc) {		crtc_state = drm_atomic_get_crtc_state(conn_state->state, crtc);		if (IS_ERR(crtc_state))			return PTR_ERR(crtc_state); 		crtc_state->connector_mask |=			drm_connector_mask(conn_state->connector); 		drm_connector_get(conn_state->connector);		conn_state->crtc = crtc; 		drm_dbg_atomic(connector->dev,			       "Link [CONNECTOR:%d:%s] state %p to [CRTC:%d:%s]\n",			       connector->base.id, connector->name,			       conn_state, crtc->base.id, crtc->name);	} else {		drm_dbg_atomic(connector->dev,			       "Link [CONNECTOR:%d:%s] state %p to [NOCRTC]\n",			       connector->base.id, connector->name,			       conn_state);	} 	return 0;}EXPORT_SYMBOL(drm_atomic_set_crtc_for_connector); static void set_out_fence_for_crtc(struct drm_atomic_commit *state,				   struct drm_crtc *crtc, s32 __user *fence_ptr){	state->crtcs[drm_crtc_index(crtc)].out_fence_ptr = fence_ptr;} static s32 __user *get_out_fence_for_crtc(struct drm_atomic_commit *state,					  struct drm_crtc *crtc){	s32 __user *fence_ptr; 	fence_ptr = state->crtcs[drm_crtc_index(crtc)].out_fence_ptr;	state->crtcs[drm_crtc_index(crtc)].out_fence_ptr = NULL; 	return fence_ptr;} static int set_out_fence_for_connector(struct drm_atomic_commit *state,					struct drm_connector *connector,					s32 __user *fence_ptr){	unsigned int index = drm_connector_index(connector); 	if (!fence_ptr)		return 0; 	if (put_user(-1, fence_ptr))		return -EFAULT; 	state->connectors[index].out_fence_ptr = fence_ptr; 	return 0;} static s32 __user *get_out_fence_for_connector(struct drm_atomic_commit *state,					       struct drm_connector *connector){	unsigned int index = drm_connector_index(connector);	s32 __user *fence_ptr; 	fence_ptr = state->connectors[index].out_fence_ptr;	state->connectors[index].out_fence_ptr = NULL; 	return fence_ptr;} static int drm_atomic_crtc_set_property(struct drm_crtc *crtc,		struct drm_crtc_state *state, struct drm_property *property,		uint64_t val){	struct drm_device *dev = crtc->dev;	struct drm_mode_config *config = &dev->mode_config;	bool replaced = false;	int ret; 	if (property == config->prop_active)		state->active = val;	else if (property == config->prop_mode_id) {		struct drm_property_blob *mode =			drm_property_lookup_blob(dev, val);		ret = drm_atomic_set_mode_prop_for_crtc(state, mode);		drm_property_blob_put(mode);		return ret;	} else if (property == config->prop_vrr_enabled) {		state->vrr_enabled = val;	} else if (property == config->degamma_lut_property) {		const size_t elem_size = sizeof(struct drm_color_lut);		u64 lut_size; 		ret = drm_object_immutable_property_get_value(&crtc->base,							      config->degamma_lut_size_property,							      &lut_size);		if (ret)			return ret; 		ret = drm_property_replace_blob_from_id(dev,					&state->degamma_lut,					val,					elem_size * lut_size, -1, elem_size,					&replaced);		state->color_mgmt_changed |= replaced;		return ret;	} else if (property == config->ctm_property) {		ret = drm_property_replace_blob_from_id(dev,					&state->ctm,					val,					-1, sizeof(struct drm_color_ctm), -1,					&replaced);		state->color_mgmt_changed |= replaced;		return ret;	} else if (property == config->gamma_lut_property) {		const size_t elem_size = sizeof(struct drm_color_lut);		u64 lut_size; 		ret = drm_object_immutable_property_get_value(&crtc->base,							      config->gamma_lut_size_property,							      &lut_size);		if (ret)			return ret; 		ret = drm_property_replace_blob_from_id(dev,					&state->gamma_lut,					val,					elem_size * lut_size, -1, elem_size,					&replaced);		state->color_mgmt_changed |= replaced;		return ret;	} else if (property == config->background_color_property) {		state->background_color = val;	} else if (property == config->prop_out_fence_ptr) {		s32 __user *fence_ptr = u64_to_user_ptr(val); 		if (!fence_ptr)			return 0; 		if (put_user(-1, fence_ptr))			return -EFAULT; 		set_out_fence_for_crtc(state->state, crtc, fence_ptr);	} else if (property == crtc->scaling_filter_property) {		state->scaling_filter = val;	} else if (property == crtc->sharpness_strength_property) {		state->sharpness_strength = val;	} else if (crtc->funcs->atomic_set_property) {		return crtc->funcs->atomic_set_property(crtc, state, property, val);	} else {		drm_dbg_atomic(crtc->dev,			       "[CRTC:%d:%s] unknown property [PROP:%d:%s]\n",			       crtc->base.id, crtc->name,			       property->base.id, property->name);		return -EINVAL;	} 	return 0;} static intdrm_atomic_crtc_get_property(struct drm_crtc *crtc,		const struct drm_crtc_state *state,		struct drm_property *property, uint64_t *val){	struct drm_device *dev = crtc->dev;	struct drm_mode_config *config = &dev->mode_config;

Lines 1–500 of 1782.