2326 lines · 68.8 KB · C
RawDownload
/* * 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_mode.h>#include <drm/drm_plane_helper.h>#include <drm/drm_print.h>#include <linux/sync_file.h> #include "drm_crtc_internal.h" void __drm_crtc_commit_free(struct kref *kref){	struct drm_crtc_commit *commit =		container_of(kref, struct drm_crtc_commit, ref); 	kfree(commit);}EXPORT_SYMBOL(__drm_crtc_commit_free); /** * drm_atomic_state_default_release - * release memory initialized by drm_atomic_state_init * @state: atomic state * * Free all the memory allocated by drm_atomic_state_init. * This is useful for drivers that subclass the atomic state. */void drm_atomic_state_default_release(struct drm_atomic_state *state){	kfree(state->connectors);	kfree(state->crtcs);	kfree(state->planes);	kfree(state->private_objs);}EXPORT_SYMBOL(drm_atomic_state_default_release); /** * drm_atomic_state_init - init new atomic state * @dev: DRM device * @state: atomic state * * Default implementation for filling in a new atomic state. * This is useful for drivers that subclass the atomic state. */intdrm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state){	kref_init(&state->ref); 	/* TODO legacy paths should maybe do a better job about	 * setting this appropriately?	 */	state->allow_modeset = true; 	state->crtcs = kcalloc(dev->mode_config.num_crtc,			       sizeof(*state->crtcs), GFP_KERNEL);	if (!state->crtcs)		goto fail;	state->planes = kcalloc(dev->mode_config.num_total_plane,				sizeof(*state->planes), GFP_KERNEL);	if (!state->planes)		goto fail; 	state->dev = dev; 	DRM_DEBUG_ATOMIC("Allocated atomic state %p\n", state); 	return 0;fail:	drm_atomic_state_default_release(state);	return -ENOMEM;}EXPORT_SYMBOL(drm_atomic_state_init); /** * 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_mode_config *config = &dev->mode_config; 	if (!config->funcs->atomic_state_alloc) {		struct drm_atomic_state *state; 		state = kzalloc(sizeof(*state), GFP_KERNEL);		if (!state)			return NULL;		if (drm_atomic_state_init(dev, state) < 0) {			kfree(state);			return NULL;		}		return state;	} 	return config->funcs->atomic_state_alloc(dev);}EXPORT_SYMBOL(drm_atomic_state_alloc); /** * drm_atomic_state_default_clear - clear base atomic state * @state: atomic state * * Default implementation for clearing atomic state. * This is useful for drivers that subclass the atomic state. */void drm_atomic_state_default_clear(struct drm_atomic_state *state){	struct drm_device *dev = state->dev;	struct drm_mode_config *config = &dev->mode_config;	int i; 	DRM_DEBUG_ATOMIC("Clearing atomic state %p\n", state); 	for (i = 0; i < state->num_connector; i++) {		struct drm_connector *connector = state->connectors[i].ptr; 		if (!connector)			continue; 		connector->funcs->atomic_destroy_state(connector,						       state->connectors[i].state);		state->connectors[i].ptr = NULL;		state->connectors[i].state = NULL;		drm_connector_put(connector);	} 	for (i = 0; i < config->num_crtc; i++) {		struct drm_crtc *crtc = state->crtcs[i].ptr; 		if (!crtc)			continue; 		crtc->funcs->atomic_destroy_state(crtc,						  state->crtcs[i].state); 		if (state->crtcs[i].commit) {			kfree(state->crtcs[i].commit->event);			state->crtcs[i].commit->event = NULL;			drm_crtc_commit_put(state->crtcs[i].commit);		} 		state->crtcs[i].commit = NULL;		state->crtcs[i].ptr = NULL;		state->crtcs[i].state = NULL;	} 	for (i = 0; i < config->num_total_plane; i++) {		struct drm_plane *plane = state->planes[i].ptr; 		if (!plane)			continue; 		plane->funcs->atomic_destroy_state(plane,						   state->planes[i].state);		state->planes[i].ptr = NULL;		state->planes[i].state = NULL;	} 	for (i = 0; i < state->num_private_objs; i++) {		void *obj_state = state->private_objs[i].obj_state; 		state->private_objs[i].funcs->destroy_state(obj_state);		state->private_objs[i].obj = NULL;		state->private_objs[i].obj_state = NULL;		state->private_objs[i].funcs = NULL;	}	state->num_private_objs = 0; }EXPORT_SYMBOL(drm_atomic_state_default_clear); /** * 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 * &drm_mode_config_funcs.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;	struct drm_mode_config *config = &dev->mode_config; 	if (config->funcs->atomic_state_clear)		config->funcs->atomic_state_clear(state);	else		drm_atomic_state_default_clear(state);}EXPORT_SYMBOL(drm_atomic_state_clear); /** * __drm_atomic_state_free - free all memory for an atomic state * @ref: This 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 kref *ref){	struct drm_atomic_state *state = container_of(ref, typeof(*state), ref);	struct drm_mode_config *config = &state->dev->mode_config; 	drm_atomic_state_clear(state); 	DRM_DEBUG_ATOMIC("Freeing atomic state %p\n", state); 	if (config->funcs->atomic_state_free) {		config->funcs->atomic_state_free(state);	} else {		drm_atomic_state_default_release(state);		kfree(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 = drm_crtc_index(crtc);	struct drm_crtc_state *crtc_state; 	WARN_ON(!state->acquire_ctx); 	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);	if (crtc_state)		return crtc_state; 	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->crtcs[index].state = crtc_state;	state->crtcs[index].old_state = crtc->state;	state->crtcs[index].new_state = crtc_state;	state->crtcs[index].ptr = crtc;	crtc_state->state = state; 	DRM_DEBUG_ATOMIC("Added [CRTC:%d:%s] %p state to %p\n",			 crtc->base.id, crtc->name, crtc_state, state); 	return crtc_state;}EXPORT_SYMBOL(drm_atomic_get_crtc_state); static void set_out_fence_for_crtc(struct drm_atomic_state *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_state *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;} /** * 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_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) {		drm_mode_convert_to_umode(&umode, mode);		state->mode_blob =			drm_property_create_blob(state->crtc->dev,		                                 sizeof(umode),		                                 &umode);		if (IS_ERR(state->mode_blob))			return PTR_ERR(state->mode_blob); 		drm_mode_copy(&state->mode, mode);		state->enable = true;		DRM_DEBUG_ATOMIC("Set [MODE:%s] for CRTC state %p\n",				 mode->name, state);	} else {		memset(&state->mode, 0, sizeof(state->mode));		state->enable = false;		DRM_DEBUG_ATOMIC("Set [NOMODE] for CRTC state %p\n",				 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){	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) {		if (blob->length != sizeof(struct drm_mode_modeinfo) ||		    drm_mode_convert_umode(&state->mode,		                           (const struct drm_mode_modeinfo *)		                            blob->data))			return -EINVAL; 		state->mode_blob = drm_property_blob_get(blob);		state->enable = true;		DRM_DEBUG_ATOMIC("Set [MODE:%s] for CRTC state %p\n",				 state->mode.name, state);	} else {		state->enable = false;		DRM_DEBUG_ATOMIC("Set [NOMODE] for CRTC state %p\n",				 state);	} 	return 0;}EXPORT_SYMBOL(drm_atomic_set_mode_prop_for_crtc); /** * drm_atomic_replace_property_blob - replace a blob property * @blob: a pointer to the member blob to be replaced * @new_blob: the new blob to replace with * @replaced: whether the blob has been replaced * * RETURNS: * Zero on success, error code on failure */static voiddrm_atomic_replace_property_blob(struct drm_property_blob **blob,				 struct drm_property_blob *new_blob,				 bool *replaced){	struct drm_property_blob *old_blob = *blob; 	if (old_blob == new_blob)		return; 	drm_property_blob_put(old_blob);	if (new_blob)		drm_property_blob_get(new_blob);	*blob = new_blob;	*replaced = true; 	return;} static intdrm_atomic_replace_property_blob_from_id(struct drm_device *dev,					 struct drm_property_blob **blob,					 uint64_t blob_id,					 ssize_t expected_size,					 bool *replaced){	struct drm_property_blob *new_blob = NULL; 	if (blob_id != 0) {		new_blob = drm_property_lookup_blob(dev, blob_id);		if (new_blob == NULL)			return -EINVAL; 		if (expected_size > 0 && expected_size != new_blob->length) {			drm_property_blob_put(new_blob);			return -EINVAL;		}	} 	drm_atomic_replace_property_blob(blob, new_blob, replaced);	drm_property_blob_put(new_blob); 	return 0;} /** * drm_atomic_crtc_set_property - set property on CRTC * @crtc: the drm CRTC to set a property on * @state: the state object to update with the new property value * @property: the property to set * @val: the new property value * * This function handles generic/core properties and calls out to driver's * &drm_crtc_funcs.atomic_set_property for driver properties. To ensure * consistent behavior you must call this function rather than the driver hook * directly. * * RETURNS: * Zero on success, error code on failure */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->degamma_lut_property) {		ret = drm_atomic_replace_property_blob_from_id(dev,					&state->degamma_lut,

Lines 1–500 of 2326.