diff options
Diffstat (limited to 'drivers/gpu/drm/drm_atomic.c')
| -rw-r--r-- | drivers/gpu/drm/drm_atomic.c | 299 | 
1 files changed, 258 insertions, 41 deletions
| diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 6e3b78ee7d16..f6f2fb58eb37 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -30,7 +30,15 @@  #include <drm/drm_atomic.h>  #include <drm/drm_plane_helper.h> -static void kfree_state(struct drm_atomic_state *state) +/** + * 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->connector_states); @@ -38,24 +46,20 @@ static void kfree_state(struct drm_atomic_state *state)  	kfree(state->crtc_states);  	kfree(state->planes);  	kfree(state->plane_states); -	kfree(state);  } +EXPORT_SYMBOL(drm_atomic_state_default_release);  /** - * drm_atomic_state_alloc - allocate atomic state + * drm_atomic_state_init - init new atomic state   * @dev: DRM device + * @state: atomic state   * - * This allocates an empty atomic state to track updates. + * Default implementation for filling in a new atomic state. + * This is useful for drivers that subclass the atomic state.   */ -struct drm_atomic_state * -drm_atomic_state_alloc(struct drm_device *dev) +int +drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state)  { -	struct drm_atomic_state *state; - -	state = kzalloc(sizeof(*state), GFP_KERNEL); -	if (!state) -		return NULL; -  	/* TODO legacy paths should maybe do a better job about  	 * setting this appropriately?  	 */ @@ -92,31 +96,50 @@ drm_atomic_state_alloc(struct drm_device *dev)  	state->dev = dev; -	DRM_DEBUG_ATOMIC("Allocate atomic state %p\n", state); +	DRM_DEBUG_ATOMIC("Allocated atomic state %p\n", state); -	return state; +	return 0;  fail: -	kfree_state(state); +	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; +	struct drm_atomic_state *state; + +	if (!config->funcs->atomic_state_alloc) { +		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 NULL; +	return config->funcs->atomic_state_alloc(dev);  }  EXPORT_SYMBOL(drm_atomic_state_alloc);  /** - * drm_atomic_state_clear - clear state object + * drm_atomic_state_default_clear - clear base atomic state   * @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. + * Default implementation for clearing atomic state. + * This is useful for drivers that subclass the atomic state.   */ -void drm_atomic_state_clear(struct drm_atomic_state *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; @@ -162,6 +185,32 @@ void drm_atomic_state_clear(struct drm_atomic_state *state)  		state->plane_states[i] = NULL;  	}  } +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 ->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);  /** @@ -173,14 +222,25 @@ EXPORT_SYMBOL(drm_atomic_state_clear);   */  void drm_atomic_state_free(struct drm_atomic_state *state)  { +	struct drm_device *dev; +	struct drm_mode_config *config; +  	if (!state)  		return; +	dev = state->dev; +	config = &dev->mode_config; +  	drm_atomic_state_clear(state);  	DRM_DEBUG_ATOMIC("Freeing atomic state %p\n", state); -	kfree_state(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); @@ -203,13 +263,12 @@ struct drm_crtc_state *  drm_atomic_get_crtc_state(struct drm_atomic_state *state,  			  struct drm_crtc *crtc)  { -	int ret, index; +	int ret, index = drm_crtc_index(crtc);  	struct drm_crtc_state *crtc_state; -	index = drm_crtc_index(crtc); - -	if (state->crtc_states[index]) -		return state->crtc_states[index]; +	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) @@ -231,6 +290,100 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state,  EXPORT_SYMBOL(drm_atomic_get_crtc_state);  /** + * 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. Does + * not change any other state properties, including enable, active, or + * mode_changed. + * + * RETURNS: + * Zero on success, error code on failure. Cannot return -EDEADLK. + */ +int drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state, +				 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; + +	if (state->mode_blob) +		drm_property_unreference_blob(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; + +	if (state->mode_blob) +		drm_property_unreference_blob(state->mode_blob); +	state->mode_blob = NULL; + +	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_reference_blob(blob); +		state->enable = true; +		DRM_DEBUG_ATOMIC("Set [MODE:%s] for CRTC state %p\n", +				 state->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_prop_for_crtc); + +/**   * 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 @@ -252,10 +405,18 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,  {  	struct drm_device *dev = crtc->dev;  	struct drm_mode_config *config = &dev->mode_config; +	int ret; -	/* FIXME: Mode prop is missing, which also controls ->enable. */  	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); +		if (mode) +			drm_property_unreference_blob(mode); +		return ret; +	}  	else if (crtc->funcs->atomic_set_property)  		return crtc->funcs->atomic_set_property(crtc, state, property, val);  	else @@ -280,6 +441,8 @@ int drm_atomic_crtc_get_property(struct drm_crtc *crtc,  	if (property == config->prop_active)  		*val = state->active; +	else if (property == config->prop_mode_id) +		*val = (state->mode_blob) ? state->mode_blob->base.id : 0;  	else if (crtc->funcs->atomic_get_property)  		return crtc->funcs->atomic_get_property(crtc, state, property, val);  	else @@ -315,6 +478,23 @@ static int drm_atomic_crtc_check(struct drm_crtc *crtc,  		return -EINVAL;  	} +	/* The state->enable vs. state->mode_blob checks can be WARN_ON, +	 * as this is a kernel-internal detail that userspace should never +	 * be able to trigger. */ +	if (drm_core_check_feature(crtc->dev, DRIVER_ATOMIC) && +	    WARN_ON(state->enable && !state->mode_blob)) { +		DRM_DEBUG_ATOMIC("[CRTC:%d] enabled without mode blob\n", +				 crtc->base.id); +		return -EINVAL; +	} + +	if (drm_core_check_feature(crtc->dev, DRIVER_ATOMIC) && +	    WARN_ON(!state->enable && state->mode_blob)) { +		DRM_DEBUG_ATOMIC("[CRTC:%d] disabled with mode blob\n", +				 crtc->base.id); +		return -EINVAL; +	} +  	return 0;  } @@ -337,13 +517,12 @@ struct drm_plane_state *  drm_atomic_get_plane_state(struct drm_atomic_state *state,  			  struct drm_plane *plane)  { -	int ret, index; +	int ret, index = drm_plane_index(plane);  	struct drm_plane_state *plane_state; -	index = drm_plane_index(plane); - -	if (state->plane_states[index]) -		return state->plane_states[index]; +	plane_state = drm_atomic_get_existing_plane_state(state, plane); +	if (plane_state) +		return plane_state;  	ret = drm_modeset_lock(&plane->mutex, state->acquire_ctx);  	if (ret) @@ -898,6 +1077,45 @@ drm_atomic_add_affected_connectors(struct drm_atomic_state *state,  EXPORT_SYMBOL(drm_atomic_add_affected_connectors);  /** + * drm_atomic_add_affected_planes - add planes for crtc + * @state: atomic state + * @crtc: DRM crtc + * + * This function walks the current configuration and adds all planes + * currently used by @crtc to the atomic configuration @state. This is useful + * when an atomic commit also needs to check all currently enabled plane on + * @crtc, e.g. when changing the mode. It's also useful when re-enabling a CRTC + * to avoid special code to force-enable all planes. + * + * Since acquiring a plane state will always also acquire the w/w mutex of the + * current CRTC for that plane (if there is any) adding all the plane states for + * a CRTC will not reduce parallism of atomic updates. + * + * 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_planes(struct drm_atomic_state *state, +			       struct drm_crtc *crtc) +{ +	struct drm_plane *plane; + +	WARN_ON(!drm_atomic_get_existing_crtc_state(state, crtc)); + +	drm_for_each_plane_mask(plane, state->dev, crtc->state->plane_mask) { +		struct drm_plane_state *plane_state = +			drm_atomic_get_plane_state(state, plane); + +		if (IS_ERR(plane_state)) +			return PTR_ERR(plane_state); +	} +	return 0; +} +EXPORT_SYMBOL(drm_atomic_add_affected_planes); + +/**   * drm_atomic_connectors_for_crtc - count number of connected outputs   * @state: atomic state   * @crtc: DRM crtc @@ -998,8 +1216,7 @@ int drm_atomic_check_only(struct drm_atomic_state *state)  	if (!state->allow_modeset) {  		for_each_crtc_in_state(state, crtc, crtc_state, i) { -			if (crtc_state->mode_changed || -			    crtc_state->active_changed) { +			if (drm_atomic_crtc_needs_modeset(crtc_state)) {  				DRM_DEBUG_ATOMIC("[CRTC:%d] requires full modeset\n",  						 crtc->base.id);  				return -EINVAL; | 
