diff options
Diffstat (limited to 'drivers/gpu/drm/drm_atomic_helper.c')
| -rw-r--r-- | drivers/gpu/drm/drm_atomic_helper.c | 139 | 
1 files changed, 94 insertions, 45 deletions
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 5b59d5ad7d1c..aecb5d69bc2d 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -89,7 +89,7 @@ get_current_crtc_for_encoder(struct drm_device *dev,  	WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); -	list_for_each_entry(connector, &config->connector_list, head) { +	drm_for_each_connector(connector, dev) {  		if (connector->state->best_encoder != encoder)  			continue; @@ -124,7 +124,7 @@ steal_encoder(struct drm_atomic_state *state,  	if (IS_ERR(crtc_state))  		return PTR_ERR(crtc_state); -	crtc_state->mode_changed = true; +	crtc_state->connectors_changed = true;  	list_for_each_entry(connector, &config->connector_list, head) {  		if (connector->state->best_encoder != encoder) @@ -174,14 +174,14 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx)  			idx = drm_crtc_index(connector->state->crtc);  			crtc_state = state->crtc_states[idx]; -			crtc_state->mode_changed = true; +			crtc_state->connectors_changed = true;  		}  		if (connector_state->crtc) {  			idx = drm_crtc_index(connector_state->crtc);  			crtc_state = state->crtc_states[idx]; -			crtc_state->mode_changed = true; +			crtc_state->connectors_changed = true;  		}  	} @@ -196,7 +196,12 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx)  	}  	funcs = connector->helper_private; -	new_encoder = funcs->best_encoder(connector); + +	if (funcs->atomic_best_encoder) +		new_encoder = funcs->atomic_best_encoder(connector, +							 connector_state); +	else +		new_encoder = funcs->best_encoder(connector);  	if (!new_encoder) {  		DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n", @@ -229,11 +234,14 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx)  		}  	} +	if (WARN_ON(!connector_state->crtc)) +		return -EINVAL; +  	connector_state->best_encoder = new_encoder;  	idx = drm_crtc_index(connector_state->crtc);  	crtc_state = state->crtc_states[idx]; -	crtc_state->mode_changed = true; +	crtc_state->connectors_changed = true;  	DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d]\n",  			 connector->base.id, @@ -256,7 +264,8 @@ mode_fixup(struct drm_atomic_state *state)  	bool ret;  	for_each_crtc_in_state(state, crtc, crtc_state, i) { -		if (!crtc_state->mode_changed) +		if (!crtc_state->mode_changed && +		    !crtc_state->connectors_changed)  			continue;  		drm_mode_copy(&crtc_state->adjusted_mode, &crtc_state->mode); @@ -298,7 +307,7 @@ mode_fixup(struct drm_atomic_state *state)  						 encoder->base.id, encoder->name);  				return ret;  			} -		} else { +		} else if (funcs->mode_fixup) {  			ret = funcs->mode_fixup(encoder, &crtc_state->mode,  						&crtc_state->adjusted_mode);  			if (!ret) { @@ -312,7 +321,8 @@ mode_fixup(struct drm_atomic_state *state)  	for_each_crtc_in_state(state, crtc, crtc_state, i) {  		const struct drm_crtc_helper_funcs *funcs; -		if (!crtc_state->mode_changed) +		if (!crtc_state->mode_changed && +		    !crtc_state->connectors_changed)  			continue;  		funcs = crtc->helper_private; @@ -338,9 +348,14 @@ mode_fixup(struct drm_atomic_state *state)   *   * Check the state object to see if the requested state is physically possible.   * This does all the crtc and connector related computations for an atomic - * update. It computes and updates crtc_state->mode_changed, adds any additional - * connectors needed for full modesets and calls down into ->mode_fixup - * functions of the driver backend. + * update and adds any additional connectors needed for full modesets and calls + * down into ->mode_fixup functions of the driver backend. + * + * crtc_state->mode_changed is set when the input mode is changed. + * crtc_state->connectors_changed is set when a connector is added or + * removed from the crtc. + * crtc_state->active_changed is set when crtc_state->active changes, + * which is used for dpms.   *   * IMPORTANT:   * @@ -373,7 +388,17 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,  		if (crtc->state->enable != crtc_state->enable) {  			DRM_DEBUG_ATOMIC("[CRTC:%d] enable changed\n",  					 crtc->base.id); + +			/* +			 * For clarity this assignment is done here, but +			 * enable == 0 is only true when there are no +			 * connectors and a NULL mode. +			 * +			 * The other way around is true as well. enable != 0 +			 * iff connectors are attached and a mode is set. +			 */  			crtc_state->mode_changed = true; +			crtc_state->connectors_changed = true;  		}  	} @@ -448,6 +473,9 @@ EXPORT_SYMBOL(drm_atomic_helper_check_modeset);   * This does all the plane update related checks using by calling into the   * ->atomic_check hooks provided by the driver.   * + * It also sets crtc_state->planes_changed to indicate that a crtc has + * updated planes. + *   * RETURNS   * Zero for success or -errno   */ @@ -640,15 +668,29 @@ drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev,  	struct drm_crtc_state *old_crtc_state;  	int i; -	/* clear out existing links */ +	/* clear out existing links and update dpms */  	for_each_connector_in_state(old_state, connector, old_conn_state, i) { -		if (!connector->encoder) -			continue; +		if (connector->encoder) { +			WARN_ON(!connector->encoder->crtc); + +			connector->encoder->crtc = NULL; +			connector->encoder = NULL; +		} + +		crtc = connector->state->crtc; +		if ((!crtc && old_conn_state->crtc) || +		    (crtc && drm_atomic_crtc_needs_modeset(crtc->state))) { +			struct drm_property *dpms_prop = +				dev->mode_config.dpms_property; +			int mode = DRM_MODE_DPMS_OFF; -		WARN_ON(!connector->encoder->crtc); +			if (crtc && crtc->state->active) +				mode = DRM_MODE_DPMS_ON; -		connector->encoder->crtc = NULL; -		connector->encoder = NULL; +			connector->dpms = mode; +			drm_object_property_set_value(&connector->base, +						      dpms_prop, mode); +		}  	}  	/* set new links */ @@ -665,10 +707,16 @@ drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev,  	/* set legacy state in the crtc structure */  	for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { +		struct drm_plane *primary = crtc->primary; +  		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; + +		if (drm_atomic_get_existing_plane_state(old_state, primary) && +		    primary->state->crtc == crtc) { +			crtc->x = primary->state->src_x >> 16; +			crtc->y = primary->state->src_y >> 16; +		}  		if (crtc->state->enable)  			drm_calc_timestamping_constants(crtc, @@ -742,7 +790,7 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)   * This function shuts down all the outputs that need to be shut down and   * prepares them (if required) with the new mode.   * - * For compatability with legacy crtc helpers this should be called before + * For compatibility with legacy crtc helpers this should be called before   * drm_atomic_helper_commit_planes(), which is what the default commit function   * does. But drivers with different needs can group the modeset commits together   * and do the plane commits at the end. This is useful for drivers doing runtime @@ -767,7 +815,7 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_disables);   * This function enables all the outputs with the new configuration which had to   * be turned off for the update.   * - * For compatability with legacy crtc helpers this should be called after + * For compatibility with legacy crtc helpers this should be called after   * drm_atomic_helper_commit_planes(), which is what the default commit function   * does. But drivers with different needs can group the modeset commits together   * and do the plane commits at the end. This is useful for drivers doing runtime @@ -918,7 +966,7 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,  			continue;  		old_crtc_state->enable = true; -		old_crtc_state->last_vblank_count = drm_vblank_count(dev, i); +		old_crtc_state->last_vblank_count = drm_crtc_vblank_count(crtc);  	}  	for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { @@ -927,7 +975,7 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,  		ret = wait_event_timeout(dev->vblank[i].queue,  				old_crtc_state->last_vblank_count != -					drm_vblank_count(dev, i), +					drm_crtc_vblank_count(crtc),  				msecs_to_jiffies(50));  		drm_crtc_vblank_put(crtc); @@ -1138,7 +1186,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,  		if (!funcs || !funcs->atomic_begin)  			continue; -		funcs->atomic_begin(crtc); +		funcs->atomic_begin(crtc, old_crtc_state);  	}  	for_each_plane_in_state(old_state, plane, old_plane_state, i) { @@ -1168,7 +1216,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,  		if (!funcs || !funcs->atomic_flush)  			continue; -		funcs->atomic_flush(crtc); +		funcs->atomic_flush(crtc, old_crtc_state);  	}  }  EXPORT_SYMBOL(drm_atomic_helper_commit_planes); @@ -1204,7 +1252,7 @@ drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_state)  	crtc_funcs = crtc->helper_private;  	if (crtc_funcs && crtc_funcs->atomic_begin) -		crtc_funcs->atomic_begin(crtc); +		crtc_funcs->atomic_begin(crtc, old_crtc_state);  	drm_for_each_plane_mask(plane, crtc->dev, plane_mask) {  		struct drm_plane_state *old_plane_state = @@ -1227,7 +1275,7 @@ drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_state)  	}  	if (crtc_funcs && crtc_funcs->atomic_flush) -		crtc_funcs->atomic_flush(crtc); +		crtc_funcs->atomic_flush(crtc, old_crtc_state);  }  EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc); @@ -1915,10 +1963,6 @@ retry:  	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: @@ -1952,9 +1996,12 @@ EXPORT_SYMBOL(drm_atomic_helper_page_flip);   * implementing the legacy DPMS connector interface. It computes the new desired   * ->active state for the corresponding CRTC (if the connector is enabled) and   *  updates it. + * + * Returns: + * Returns 0 on success, negative errno numbers on failure.   */ -void drm_atomic_helper_connector_dpms(struct drm_connector *connector, -				      int mode) +int drm_atomic_helper_connector_dpms(struct drm_connector *connector, +				     int mode)  {  	struct drm_mode_config *config = &connector->dev->mode_config;  	struct drm_atomic_state *state; @@ -1963,6 +2010,7 @@ void drm_atomic_helper_connector_dpms(struct drm_connector *connector,  	struct drm_connector *tmp_connector;  	int ret;  	bool active = false; +	int old_mode = connector->dpms;  	if (mode != DRM_MODE_DPMS_ON)  		mode = DRM_MODE_DPMS_OFF; @@ -1971,22 +2019,23 @@ void drm_atomic_helper_connector_dpms(struct drm_connector *connector,  	crtc = connector->state->crtc;  	if (!crtc) -		return; +		return 0; -	/* FIXME: ->dpms has no return value so can't forward the -ENOMEM. */  	state = drm_atomic_state_alloc(connector->dev);  	if (!state) -		return; +		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)) -		return; +	if (IS_ERR(crtc_state)) { +		ret = PTR_ERR(crtc_state); +		goto fail; +	}  	WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); -	list_for_each_entry(tmp_connector, &config->connector_list, head) { +	drm_for_each_connector(tmp_connector, connector->dev) {  		if (tmp_connector->state->crtc != crtc)  			continue; @@ -2001,17 +2050,16 @@ retry:  	if (ret != 0)  		goto fail; -	/* Driver takes ownership of state on successful async commit. */ -	return; +	/* Driver takes ownership of state on successful commit. */ +	return 0;  fail:  	if (ret == -EDEADLK)  		goto backoff; +	connector->dpms = old_mode;  	drm_atomic_state_free(state); -	WARN(1, "Driver bug: Changing ->active failed with ret=%i\n", ret); - -	return; +	return ret;  backoff:  	drm_atomic_state_clear(state);  	drm_atomic_legacy_backoff(state); @@ -2072,6 +2120,7 @@ void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc,  	state->mode_changed = false;  	state->active_changed = false;  	state->planes_changed = false; +	state->connectors_changed = false;  	state->event = NULL;  }  EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state);  |