diff options
Diffstat (limited to 'drivers/gpu/drm/drm_bridge.c')
| -rw-r--r-- | drivers/gpu/drm/drm_bridge.c | 280 | 
1 files changed, 167 insertions, 113 deletions
| diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index cba537c99e43..c2cf0c90fa26 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -55,7 +55,7 @@   * just provide additional hooks to get the desired output at the end of the   * encoder chain.   * - * Bridges can also be chained up using the &drm_bridge.next pointer. + * Bridges can also be chained up using the &drm_bridge.chain_node field.   *   * Both legacy CRTC helpers and the new atomic modeset helpers support bridges.   */ @@ -128,20 +128,21 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,  	bridge->dev = encoder->dev;  	bridge->encoder = encoder; +	if (previous) +		list_add(&bridge->chain_node, &previous->chain_node); +	else +		list_add(&bridge->chain_node, &encoder->bridge_chain); +  	if (bridge->funcs->attach) {  		ret = bridge->funcs->attach(bridge);  		if (ret < 0) { +			list_del(&bridge->chain_node);  			bridge->dev = NULL;  			bridge->encoder = NULL;  			return ret;  		}  	} -	if (previous) -		previous->next = bridge; -	else -		encoder->bridge = bridge; -  	return 0;  }  EXPORT_SYMBOL(drm_bridge_attach); @@ -157,6 +158,7 @@ void drm_bridge_detach(struct drm_bridge *bridge)  	if (bridge->funcs->detach)  		bridge->funcs->detach(bridge); +	list_del(&bridge->chain_node);  	bridge->dev = NULL;  } @@ -172,8 +174,8 @@ void drm_bridge_detach(struct drm_bridge *bridge)   */  /** - * drm_bridge_mode_fixup - fixup proposed mode for all bridges in the - *			   encoder chain + * drm_bridge_chain_mode_fixup - fixup proposed mode for all bridges in the + *				 encoder chain   * @bridge: bridge control structure   * @mode: desired mode to be set for the bridge   * @adjusted_mode: updated mode that works for this bridge @@ -186,27 +188,31 @@ void drm_bridge_detach(struct drm_bridge *bridge)   * RETURNS:   * true on success, false on failure   */ -bool drm_bridge_mode_fixup(struct drm_bridge *bridge, -			const struct drm_display_mode *mode, -			struct drm_display_mode *adjusted_mode) +bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge, +				 const struct drm_display_mode *mode, +				 struct drm_display_mode *adjusted_mode)  { -	bool ret = true; +	struct drm_encoder *encoder;  	if (!bridge)  		return true; -	if (bridge->funcs->mode_fixup) -		ret = bridge->funcs->mode_fixup(bridge, mode, adjusted_mode); +	encoder = bridge->encoder; +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { +		if (!bridge->funcs->mode_fixup) +			continue; -	ret = ret && drm_bridge_mode_fixup(bridge->next, mode, adjusted_mode); +		if (!bridge->funcs->mode_fixup(bridge, mode, adjusted_mode)) +			return false; +	} -	return ret; +	return true;  } -EXPORT_SYMBOL(drm_bridge_mode_fixup); +EXPORT_SYMBOL(drm_bridge_chain_mode_fixup);  /** - * drm_bridge_mode_valid - validate the mode against all bridges in the - * 			   encoder chain. + * drm_bridge_chain_mode_valid - validate the mode against all bridges in the + *				 encoder chain.   * @bridge: bridge control structure   * @mode: desired mode to be validated   * @@ -219,26 +225,33 @@ EXPORT_SYMBOL(drm_bridge_mode_fixup);   * RETURNS:   * MODE_OK on success, drm_mode_status Enum error code on failure   */ -enum drm_mode_status drm_bridge_mode_valid(struct drm_bridge *bridge, -					   const struct drm_display_mode *mode) +enum drm_mode_status +drm_bridge_chain_mode_valid(struct drm_bridge *bridge, +			    const struct drm_display_mode *mode)  { -	enum drm_mode_status ret = MODE_OK; +	struct drm_encoder *encoder;  	if (!bridge) -		return ret; +		return MODE_OK; -	if (bridge->funcs->mode_valid) -		ret = bridge->funcs->mode_valid(bridge, mode); +	encoder = bridge->encoder; +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { +		enum drm_mode_status ret; -	if (ret != MODE_OK) -		return ret; +		if (!bridge->funcs->mode_valid) +			continue; -	return drm_bridge_mode_valid(bridge->next, mode); +		ret = bridge->funcs->mode_valid(bridge, mode); +		if (ret != MODE_OK) +			return ret; +	} + +	return MODE_OK;  } -EXPORT_SYMBOL(drm_bridge_mode_valid); +EXPORT_SYMBOL(drm_bridge_chain_mode_valid);  /** - * drm_bridge_disable - disables all bridges in the encoder chain + * drm_bridge_chain_disable - disables all bridges in the encoder chain   * @bridge: bridge control structure   *   * Calls &drm_bridge_funcs.disable op for all the bridges in the encoder @@ -247,20 +260,28 @@ EXPORT_SYMBOL(drm_bridge_mode_valid);   *   * Note: the bridge passed should be the one closest to the encoder   */ -void drm_bridge_disable(struct drm_bridge *bridge) +void drm_bridge_chain_disable(struct drm_bridge *bridge)  { +	struct drm_encoder *encoder; +	struct drm_bridge *iter; +  	if (!bridge)  		return; -	drm_bridge_disable(bridge->next); +	encoder = bridge->encoder; +	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { +		if (iter->funcs->disable) +			iter->funcs->disable(iter); -	if (bridge->funcs->disable) -		bridge->funcs->disable(bridge); +		if (iter == bridge) +			break; +	}  } -EXPORT_SYMBOL(drm_bridge_disable); +EXPORT_SYMBOL(drm_bridge_chain_disable);  /** - * drm_bridge_post_disable - cleans up after disabling all bridges in the encoder chain + * drm_bridge_chain_post_disable - cleans up after disabling all bridges in the + *				   encoder chain   * @bridge: bridge control structure   *   * Calls &drm_bridge_funcs.post_disable op for all the bridges in the @@ -269,47 +290,53 @@ EXPORT_SYMBOL(drm_bridge_disable);   *   * Note: the bridge passed should be the one closest to the encoder   */ -void drm_bridge_post_disable(struct drm_bridge *bridge) +void drm_bridge_chain_post_disable(struct drm_bridge *bridge)  { +	struct drm_encoder *encoder; +  	if (!bridge)  		return; -	if (bridge->funcs->post_disable) -		bridge->funcs->post_disable(bridge); - -	drm_bridge_post_disable(bridge->next); +	encoder = bridge->encoder; +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { +		if (bridge->funcs->post_disable) +			bridge->funcs->post_disable(bridge); +	}  } -EXPORT_SYMBOL(drm_bridge_post_disable); +EXPORT_SYMBOL(drm_bridge_chain_post_disable);  /** - * drm_bridge_mode_set - set proposed mode for all bridges in the - *			 encoder chain + * drm_bridge_chain_mode_set - set proposed mode for all bridges in the + *			       encoder chain   * @bridge: bridge control structure - * @mode: desired mode to be set for the bridge - * @adjusted_mode: updated mode that works for this bridge + * @mode: desired mode to be set for the encoder chain + * @adjusted_mode: updated mode that works for this encoder chain   *   * Calls &drm_bridge_funcs.mode_set op for all the bridges in the   * encoder chain, starting from the first bridge to the last.   *   * Note: the bridge passed should be the one closest to the encoder   */ -void drm_bridge_mode_set(struct drm_bridge *bridge, -			 const struct drm_display_mode *mode, -			 const struct drm_display_mode *adjusted_mode) +void drm_bridge_chain_mode_set(struct drm_bridge *bridge, +			       const struct drm_display_mode *mode, +			       const struct drm_display_mode *adjusted_mode)  { +	struct drm_encoder *encoder; +  	if (!bridge)  		return; -	if (bridge->funcs->mode_set) -		bridge->funcs->mode_set(bridge, mode, adjusted_mode); - -	drm_bridge_mode_set(bridge->next, mode, adjusted_mode); +	encoder = bridge->encoder; +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { +		if (bridge->funcs->mode_set) +			bridge->funcs->mode_set(bridge, mode, adjusted_mode); +	}  } -EXPORT_SYMBOL(drm_bridge_mode_set); +EXPORT_SYMBOL(drm_bridge_chain_mode_set);  /** - * drm_bridge_pre_enable - prepares for enabling all - *			   bridges in the encoder chain + * drm_bridge_chain_pre_enable - prepares for enabling all bridges in the + *				 encoder chain   * @bridge: bridge control structure   *   * Calls &drm_bridge_funcs.pre_enable op for all the bridges in the encoder @@ -318,20 +345,24 @@ EXPORT_SYMBOL(drm_bridge_mode_set);   *   * Note: the bridge passed should be the one closest to the encoder   */ -void drm_bridge_pre_enable(struct drm_bridge *bridge) +void drm_bridge_chain_pre_enable(struct drm_bridge *bridge)  { +	struct drm_encoder *encoder; +	struct drm_bridge *iter; +  	if (!bridge)  		return; -	drm_bridge_pre_enable(bridge->next); - -	if (bridge->funcs->pre_enable) -		bridge->funcs->pre_enable(bridge); +	encoder = bridge->encoder; +	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { +		if (iter->funcs->pre_enable) +			iter->funcs->pre_enable(iter); +	}  } -EXPORT_SYMBOL(drm_bridge_pre_enable); +EXPORT_SYMBOL(drm_bridge_chain_pre_enable);  /** - * drm_bridge_enable - enables all bridges in the encoder chain + * drm_bridge_chain_enable - enables all bridges in the encoder chain   * @bridge: bridge control structure   *   * Calls &drm_bridge_funcs.enable op for all the bridges in the encoder @@ -340,22 +371,25 @@ EXPORT_SYMBOL(drm_bridge_pre_enable);   *   * Note that the bridge passed should be the one closest to the encoder   */ -void drm_bridge_enable(struct drm_bridge *bridge) +void drm_bridge_chain_enable(struct drm_bridge *bridge)  { +	struct drm_encoder *encoder; +  	if (!bridge)  		return; -	if (bridge->funcs->enable) -		bridge->funcs->enable(bridge); - -	drm_bridge_enable(bridge->next); +	encoder = bridge->encoder; +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { +		if (bridge->funcs->enable) +			bridge->funcs->enable(bridge); +	}  } -EXPORT_SYMBOL(drm_bridge_enable); +EXPORT_SYMBOL(drm_bridge_chain_enable);  /** - * drm_atomic_bridge_disable - disables all bridges in the encoder chain + * drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain   * @bridge: bridge control structure - * @state: atomic state being committed + * @old_state: old atomic state   *   * Calls &drm_bridge_funcs.atomic_disable (falls back on   * &drm_bridge_funcs.disable) op for all the bridges in the encoder chain, @@ -364,26 +398,33 @@ EXPORT_SYMBOL(drm_bridge_enable);   *   * Note: the bridge passed should be the one closest to the encoder   */ -void drm_atomic_bridge_disable(struct drm_bridge *bridge, -			       struct drm_atomic_state *state) +void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, +				     struct drm_atomic_state *old_state)  { +	struct drm_encoder *encoder; +	struct drm_bridge *iter; +  	if (!bridge)  		return; -	drm_atomic_bridge_disable(bridge->next, state); +	encoder = bridge->encoder; +	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { +		if (iter->funcs->atomic_disable) +			iter->funcs->atomic_disable(iter, old_state); +		else if (iter->funcs->disable) +			iter->funcs->disable(iter); -	if (bridge->funcs->atomic_disable) -		bridge->funcs->atomic_disable(bridge, state); -	else if (bridge->funcs->disable) -		bridge->funcs->disable(bridge); +		if (iter == bridge) +			break; +	}  } -EXPORT_SYMBOL(drm_atomic_bridge_disable); +EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);  /** - * drm_atomic_bridge_post_disable - cleans up after disabling all bridges in the - *				    encoder chain + * drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges + *					  in the encoder chain   * @bridge: bridge control structure - * @state: atomic state being committed + * @old_state: old atomic state   *   * Calls &drm_bridge_funcs.atomic_post_disable (falls back on   * &drm_bridge_funcs.post_disable) op for all the bridges in the encoder chain, @@ -392,26 +433,29 @@ EXPORT_SYMBOL(drm_atomic_bridge_disable);   *   * Note: the bridge passed should be the one closest to the encoder   */ -void drm_atomic_bridge_post_disable(struct drm_bridge *bridge, -				    struct drm_atomic_state *state) +void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, +					  struct drm_atomic_state *old_state)  { +	struct drm_encoder *encoder; +  	if (!bridge)  		return; -	if (bridge->funcs->atomic_post_disable) -		bridge->funcs->atomic_post_disable(bridge, state); -	else if (bridge->funcs->post_disable) -		bridge->funcs->post_disable(bridge); - -	drm_atomic_bridge_post_disable(bridge->next, state); +	encoder = bridge->encoder; +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { +		if (bridge->funcs->atomic_post_disable) +			bridge->funcs->atomic_post_disable(bridge, old_state); +		else if (bridge->funcs->post_disable) +			bridge->funcs->post_disable(bridge); +	}  } -EXPORT_SYMBOL(drm_atomic_bridge_post_disable); +EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);  /** - * drm_atomic_bridge_pre_enable - prepares for enabling all bridges in the - *				  encoder chain + * drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in + *					the encoder chain   * @bridge: bridge control structure - * @state: atomic state being committed + * @old_state: old atomic state   *   * Calls &drm_bridge_funcs.atomic_pre_enable (falls back on   * &drm_bridge_funcs.pre_enable) op for all the bridges in the encoder chain, @@ -420,25 +464,32 @@ EXPORT_SYMBOL(drm_atomic_bridge_post_disable);   *   * Note: the bridge passed should be the one closest to the encoder   */ -void drm_atomic_bridge_pre_enable(struct drm_bridge *bridge, -				  struct drm_atomic_state *state) +void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, +					struct drm_atomic_state *old_state)  { +	struct drm_encoder *encoder; +	struct drm_bridge *iter; +  	if (!bridge)  		return; -	drm_atomic_bridge_pre_enable(bridge->next, state); +	encoder = bridge->encoder; +	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { +		if (iter->funcs->atomic_pre_enable) +			iter->funcs->atomic_pre_enable(iter, old_state); +		else if (iter->funcs->pre_enable) +			iter->funcs->pre_enable(iter); -	if (bridge->funcs->atomic_pre_enable) -		bridge->funcs->atomic_pre_enable(bridge, state); -	else if (bridge->funcs->pre_enable) -		bridge->funcs->pre_enable(bridge); +		if (iter == bridge) +			break; +	}  } -EXPORT_SYMBOL(drm_atomic_bridge_pre_enable); +EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);  /** - * drm_atomic_bridge_enable - enables all bridges in the encoder chain + * drm_atomic_bridge_chain_enable - enables all bridges in the encoder chain   * @bridge: bridge control structure - * @state: atomic state being committed + * @old_state: old atomic state   *   * Calls &drm_bridge_funcs.atomic_enable (falls back on   * &drm_bridge_funcs.enable) op for all the bridges in the encoder chain, @@ -447,20 +498,23 @@ EXPORT_SYMBOL(drm_atomic_bridge_pre_enable);   *   * Note: the bridge passed should be the one closest to the encoder   */ -void drm_atomic_bridge_enable(struct drm_bridge *bridge, -			      struct drm_atomic_state *state) +void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, +				    struct drm_atomic_state *old_state)  { +	struct drm_encoder *encoder; +  	if (!bridge)  		return; -	if (bridge->funcs->atomic_enable) -		bridge->funcs->atomic_enable(bridge, state); -	else if (bridge->funcs->enable) -		bridge->funcs->enable(bridge); - -	drm_atomic_bridge_enable(bridge->next, state); +	encoder = bridge->encoder; +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { +		if (bridge->funcs->atomic_enable) +			bridge->funcs->atomic_enable(bridge, old_state); +		else if (bridge->funcs->enable) +			bridge->funcs->enable(bridge); +	}  } -EXPORT_SYMBOL(drm_atomic_bridge_enable); +EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);  #ifdef CONFIG_OF  /** |