diff options
Diffstat (limited to 'drivers/gpu/drm/drm_bridge.c')
| -rw-r--r-- | drivers/gpu/drm/drm_bridge.c | 294 | 
1 files changed, 160 insertions, 134 deletions
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 1545c50fd1c8..c3d69af02e79 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -153,6 +153,45 @@   * situation when probing.   */ +/** + * DOC: dsi bridge operations + * + * DSI host interfaces are expected to be implemented as bridges rather than + * encoders, however there are a few aspects of their operation that need to + * be defined in order to provide a consistent interface. + * + * A DSI host should keep the PHY powered down until the pre_enable operation is + * called. All lanes are in an undefined idle state up to this point, and it + * must not be assumed that it is LP-11. + * pre_enable should initialise the PHY, set the data lanes to LP-11, and the + * clock lane to either LP-11 or HS depending on the mode_flag + * %MIPI_DSI_CLOCK_NON_CONTINUOUS. + * + * Ordinarily the downstream bridge DSI peripheral pre_enable will have been + * called before the DSI host. If the DSI peripheral requires LP-11 and/or + * the clock lane to be in HS mode prior to pre_enable, then it can set the + * &pre_enable_prev_first flag to request the pre_enable (and + * post_disable) order to be altered to enable the DSI host first. + * + * Either the CRTC being enabled, or the DSI host enable operation should switch + * the host to actively transmitting video on the data lanes. + * + * The reverse also applies. The DSI host disable operation or stopping the CRTC + * should stop transmitting video, and the data lanes should return to the LP-11 + * state. The DSI host &post_disable operation should disable the PHY. + * If the &pre_enable_prev_first flag is set, then the DSI peripheral's + * bridge &post_disable will be called before the DSI host's post_disable. + * + * Whilst it is valid to call &host_transfer prior to pre_enable or after + * post_disable, the exact state of the lanes is undefined at this point. The + * DSI host should initialise the interface, transmit the data, and then disable + * the interface again. + * + * Ultra Low Power State (ULPS) is not explicitly supported by DRM. If + * implemented, it therefore needs to be handled entirely within the DSI Host + * driver. + */ +  static DEFINE_MUTEX(bridge_lock);  static LIST_HEAD(bridge_list); @@ -510,61 +549,6 @@ drm_bridge_chain_mode_valid(struct drm_bridge *bridge,  EXPORT_SYMBOL(drm_bridge_chain_mode_valid);  /** - * 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 - * chain, starting from the last bridge to the first. These are called before - * calling the encoder's prepare op. - * - * Note: the bridge passed should be the one closest to the encoder - */ -void drm_bridge_chain_disable(struct drm_bridge *bridge) -{ -	struct drm_encoder *encoder; -	struct drm_bridge *iter; - -	if (!bridge) -		return; - -	encoder = bridge->encoder; -	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { -		if (iter->funcs->disable) -			iter->funcs->disable(iter); - -		if (iter == bridge) -			break; -	} -} -EXPORT_SYMBOL(drm_bridge_chain_disable); - -/** - * 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 - * encoder chain, starting from the first bridge to the last. These are called - * after completing the encoder's prepare op. - * - * Note: the bridge passed should be the one closest to the encoder - */ -void drm_bridge_chain_post_disable(struct drm_bridge *bridge) -{ -	struct drm_encoder *encoder; - -	if (!bridge) -		return; - -	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_chain_post_disable); - -/**   * drm_bridge_chain_mode_set - set proposed mode for all bridges in the   *			       encoder chain   * @bridge: bridge control structure @@ -594,61 +578,6 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge,  EXPORT_SYMBOL(drm_bridge_chain_mode_set);  /** - * 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 - * chain, starting from the last bridge to the first. These are called - * before calling the encoder's commit op. - * - * Note: the bridge passed should be the one closest to the encoder - */ -void drm_bridge_chain_pre_enable(struct drm_bridge *bridge) -{ -	struct drm_encoder *encoder; -	struct drm_bridge *iter; - -	if (!bridge) -		return; - -	encoder = bridge->encoder; -	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { -		if (iter->funcs->pre_enable) -			iter->funcs->pre_enable(iter); - -		if (iter == bridge) -			break; -	} -} -EXPORT_SYMBOL(drm_bridge_chain_pre_enable); - -/** - * 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 - * chain, starting from the first bridge to the last. These are called - * after completing the encoder's commit op. - * - * Note that the bridge passed should be the one closest to the encoder - */ -void drm_bridge_chain_enable(struct drm_bridge *bridge) -{ -	struct drm_encoder *encoder; - -	if (!bridge) -		return; - -	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_chain_enable); - -/**   * drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain   * @bridge: bridge control structure   * @old_state: old atomic state @@ -691,6 +620,25 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,  }  EXPORT_SYMBOL(drm_atomic_bridge_chain_disable); +static void drm_atomic_bridge_call_post_disable(struct drm_bridge *bridge, +						struct drm_atomic_state *old_state) +{ +	if (old_state && bridge->funcs->atomic_post_disable) { +		struct drm_bridge_state *old_bridge_state; + +		old_bridge_state = +			drm_atomic_get_old_bridge_state(old_state, +							bridge); +		if (WARN_ON(!old_bridge_state)) +			return; + +		bridge->funcs->atomic_post_disable(bridge, +						   old_bridge_state); +	} else if (bridge->funcs->post_disable) { +		bridge->funcs->post_disable(bridge); +	} +} +  /**   * drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges   *					  in the encoder chain @@ -702,36 +650,86 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);   * starting from the first bridge to the last. These are called after completing   * &drm_encoder_helper_funcs.atomic_disable   * + * If a bridge sets @pre_enable_prev_first, then the @post_disable for that + * bridge will be called before the previous one to reverse the @pre_enable + * calling direction. + *   * Note: the bridge passed should be the one closest to the encoder   */  void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,  					  struct drm_atomic_state *old_state)  {  	struct drm_encoder *encoder; +	struct drm_bridge *next, *limit;  	if (!bridge)  		return;  	encoder = bridge->encoder; +  	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { -		if (bridge->funcs->atomic_post_disable) { -			struct drm_bridge_state *old_bridge_state; +		limit = NULL; + +		if (!list_is_last(&bridge->chain_node, &encoder->bridge_chain)) { +			next = list_next_entry(bridge, chain_node); + +			if (next->pre_enable_prev_first) { +				/* next bridge had requested that prev +				 * was enabled first, so disabled last +				 */ +				limit = next; + +				/* Find the next bridge that has NOT requested +				 * prev to be enabled first / disabled last +				 */ +				list_for_each_entry_from(next, &encoder->bridge_chain, +							 chain_node) { +					if (next->pre_enable_prev_first) { +						next = list_prev_entry(next, chain_node); +						limit = next; +						break; +					} +				} + +				/* Call these bridges in reverse order */ +				list_for_each_entry_from_reverse(next, &encoder->bridge_chain, +								 chain_node) { +					if (next == bridge) +						break; + +					drm_atomic_bridge_call_post_disable(next, +									    old_state); +				} +			} +		} -			old_bridge_state = -				drm_atomic_get_old_bridge_state(old_state, -								bridge); -			if (WARN_ON(!old_bridge_state)) -				return; +		drm_atomic_bridge_call_post_disable(bridge, old_state); -			bridge->funcs->atomic_post_disable(bridge, -							   old_bridge_state); -		} else if (bridge->funcs->post_disable) { -			bridge->funcs->post_disable(bridge); -		} +		if (limit) +			/* Jump all bridges that we have already post_disabled */ +			bridge = limit;  	}  }  EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); +static void drm_atomic_bridge_call_pre_enable(struct drm_bridge *bridge, +					      struct drm_atomic_state *old_state) +{ +	if (old_state && bridge->funcs->atomic_pre_enable) { +		struct drm_bridge_state *old_bridge_state; + +		old_bridge_state = +			drm_atomic_get_old_bridge_state(old_state, +							bridge); +		if (WARN_ON(!old_bridge_state)) +			return; + +		bridge->funcs->atomic_pre_enable(bridge, old_bridge_state); +	} else if (bridge->funcs->pre_enable) { +		bridge->funcs->pre_enable(bridge); +	} +} +  /**   * drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in   *					the encoder chain @@ -743,32 +741,60 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);   * starting from the last bridge to the first. These are called before calling   * &drm_encoder_helper_funcs.atomic_enable   * + * If a bridge sets @pre_enable_prev_first, then the pre_enable for the + * prev bridge will be called before pre_enable of this bridge. + *   * Note: the bridge passed should be the one closest to the encoder   */  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; +	struct drm_bridge *iter, *next, *limit;  	if (!bridge)  		return;  	encoder = bridge->encoder; +  	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { -		if (iter->funcs->atomic_pre_enable) { -			struct drm_bridge_state *old_bridge_state; +		if (iter->pre_enable_prev_first) { +			next = iter; +			limit = bridge; +			list_for_each_entry_from_reverse(next, +							 &encoder->bridge_chain, +							 chain_node) { +				if (next == bridge) +					break; + +				if (!next->pre_enable_prev_first) { +					/* Found first bridge that does NOT +					 * request prev to be enabled first +					 */ +					limit = list_prev_entry(next, chain_node); +					break; +				} +			} + +			list_for_each_entry_from(next, &encoder->bridge_chain, chain_node) { +				/* Call requested prev bridge pre_enable +				 * in order. +				 */ +				if (next == iter) +					/* At the first bridge to request prev +					 * bridges called first. +					 */ +					break; + +				drm_atomic_bridge_call_pre_enable(next, old_state); +			} +		} -			old_bridge_state = -				drm_atomic_get_old_bridge_state(old_state, -								iter); -			if (WARN_ON(!old_bridge_state)) -				return; +		drm_atomic_bridge_call_pre_enable(iter, old_state); -			iter->funcs->atomic_pre_enable(iter, old_bridge_state); -		} else if (iter->funcs->pre_enable) { -			iter->funcs->pre_enable(iter); -		} +		if (iter->pre_enable_prev_first) +			/* Jump all bridges that we have already pre_enabled */ +			iter = limit;  		if (iter == bridge)  			break;  |