mirror of
				https://github.com/Ysurac/openmptcprouter.git
				synced 2025-03-09 15:40:20 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			315 lines
		
	
	
	
		
			9.7 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			315 lines
		
	
	
	
		
			9.7 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 52fa9a4c3b78b5fd6fbe8c8e32c95f161cc116b2 Mon Sep 17 00:00:00 2001
 | |
| From: Dave Stevenson <dave.stevenson@raspberrypi.com>
 | |
| Date: Thu, 16 Dec 2021 15:25:35 +0000
 | |
| Subject: [PATCH 034/726] drm/bridge: Introduce pre_enable_upstream_first to
 | |
|  alter bridge init order
 | |
| 
 | |
| DSI sink devices typically want the DSI host powered up and configured
 | |
| before they are powered up. pre_enable is the place this would normally
 | |
| happen, but they are called in reverse order from panel/connector towards
 | |
| the encoder, which is the "wrong" order.
 | |
| 
 | |
| Add a new flag pre_enable_upstream_first that any bridge can set
 | |
| to swap the order of pre_enable (and post_disable) for that and the
 | |
| immediately upstream bridge.
 | |
| Should the immediately upstream bridge also set the
 | |
| pre_enable_upstream_first flag, the bridge upstream of that will be called
 | |
| before either of those which requested pre_enable_upstream_first.
 | |
| 
 | |
| eg:
 | |
| - Panel
 | |
| - Bridge 1
 | |
| - Bridge 2 pre_enable_upstream_first
 | |
| - Bridge 3
 | |
| - Bridge 4 pre_enable_upstream_first
 | |
| - Bridge 5 pre_enable_upstream_first
 | |
| - Bridge 6
 | |
| - Encoder
 | |
| Would result in pre_enable's being called as Panel, Bridge 1, Bridge 3,
 | |
| Bridge 2, Bridge 6, Bridge 5, Bridge 4, Encoder.
 | |
| 
 | |
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
 | |
| ---
 | |
|  drivers/gpu/drm/drm_bridge.c | 177 +++++++++++++++++++++++++----------
 | |
|  include/drm/drm_bridge.h     |   8 ++
 | |
|  2 files changed, 137 insertions(+), 48 deletions(-)
 | |
| 
 | |
| diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
 | |
| index 1545c50fd1c8..441d781c3da1 100644
 | |
| --- a/drivers/gpu/drm/drm_bridge.c
 | |
| +++ b/drivers/gpu/drm/drm_bridge.c
 | |
| @@ -547,20 +547,15 @@ EXPORT_SYMBOL(drm_bridge_chain_disable);
 | |
|   * encoder chain, starting from the first bridge to the last. These are called
 | |
|   * after completing the encoder's prepare op.
 | |
|   *
 | |
| + * If a bridge sets @pre_enable_upstream_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_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);
 | |
| -	}
 | |
| +	drm_atomic_bridge_chain_post_disable(bridge, NULL);
 | |
|  }
 | |
|  EXPORT_SYMBOL(drm_bridge_chain_post_disable);
 | |
|  
 | |
| @@ -602,24 +597,14 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_set);
 | |
|   * chain, starting from the last bridge to the first. These are called
 | |
|   * before calling the encoder's commit op.
 | |
|   *
 | |
| + * If a bridge sets @pre_enable_upstream_first, then the @pre_enable for the
 | |
| + * previous bridge will be called before @pre_enable of this bridge.
 | |
| + *
 | |
|   * 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;
 | |
| -	}
 | |
| +	drm_atomic_bridge_chain_pre_enable(bridge, NULL);
 | |
|  }
 | |
|  EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
 | |
|  
 | |
| @@ -691,6 +676,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
 | |
| @@ -701,6 +705,9 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
 | |
|   * &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
 | |
|   * &drm_encoder_helper_funcs.atomic_disable
 | |
| + * If a bridge sets @pre_enable_upstream_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
 | |
|   */
 | |
| @@ -708,30 +715,75 @@ 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_upstream_first) {
 | |
| +				/* Downstream bridge had requested that upstream
 | |
| +				 * was enabled first, so disabled last
 | |
| +				 */
 | |
| +				limit = next;
 | |
| +
 | |
| +				/* Find the next bridge that has NOT requested
 | |
| +				 * upstream to be enabled first / disabled last
 | |
| +				 */
 | |
| +				list_for_each_entry_from(next, &encoder->bridge_chain,
 | |
| +							 chain_node) {
 | |
| +					if (next->pre_enable_upstream_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)
 | |
| +			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 +795,61 @@ 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_upstream_first, then the pre_enable for the
 | |
| + * upstream 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_upstream_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_upstream_first) {
 | |
| +					/* Found first bridge that does NOT
 | |
| +					 * request upstream 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 upstream bridge pre_enable
 | |
| +				 * in order.
 | |
| +				 */
 | |
| +				if (next == iter)
 | |
| +					/* At the first bridgge to request upstream
 | |
| +					 * 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_upstream_first)
 | |
| +			/* Jump all bridges that we have already pre_enabled
 | |
| +			 */
 | |
| +			iter = limit;
 | |
|  
 | |
|  		if (iter == bridge)
 | |
|  			break;
 | |
| diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
 | |
| index 6b65b0dfb4fb..8a5e03e4c264 100644
 | |
| --- a/include/drm/drm_bridge.h
 | |
| +++ b/include/drm/drm_bridge.h
 | |
| @@ -768,6 +768,14 @@ struct drm_bridge {
 | |
|  	 * modes.
 | |
|  	 */
 | |
|  	bool interlace_allowed;
 | |
| +	/**
 | |
| +	 * @pre_enable_upstream_first: The bridge requires that the upstream
 | |
| +	 * bridge @pre_enable function is called before its @pre_enable,
 | |
| +	 * and conversely for post_disable. This is most frequently a
 | |
| +	 * requirement for DSI devices which need the host to be initialised
 | |
| +	 * before the peripheral.
 | |
| +	 */
 | |
| +	bool pre_enable_upstream_first;
 | |
|  	/**
 | |
|  	 * @ddc: Associated I2C adapter for DDC access, if any.
 | |
|  	 */
 | |
| -- 
 | |
| 2.33.1
 | |
| 
 |