Unverified Commit 4fb912e5 authored by Dave Stevenson's avatar Dave Stevenson Committed by Maxime Ripard
Browse files

drm/bridge: Introduce pre_enable_prev_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_prev_first that any bridge can set
to swap the order of pre_enable (and post_disable) for that and the
immediately previous bridge.
Should the immediately previous bridge also set the
pre_enable_prev_first flag, the previous bridge to that will be called
before either of those which requested pre_enable_prev_first.

eg:
- Panel
- Bridge 1
- Bridge 2 pre_enable_prev_first
- Bridge 3
- Bridge 4 pre_enable_prev_first
- Bridge 5 pre_enable_prev_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: default avatarDave Stevenson <dave.stevenson@raspberrypi.com>
Tested-by: default avatarFrieder Schrempf <frieder.schrempf@kontron.de>
Reviewed-by: default avatarFrieder Schrempf <frieder.schrempf@kontron.de>
Link: https://lore.kernel.org/r/20221205173328.1395350-5-dave.stevenson@raspberrypi.com


Signed-off-by: default avatarMaxime Ripard <maxime@cerno.tech>
parent 4e910d9d
Loading
Loading
Loading
Loading
+121 −24
Original line number Diff line number Diff line
@@ -581,6 +581,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
@@ -592,19 +611,72 @@ 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) {
		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);
				}
			}
		}

		drm_atomic_bridge_call_post_disable(bridge, old_state);

		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 =
@@ -613,14 +685,11 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *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);
		bridge->funcs->atomic_pre_enable(bridge, old_bridge_state);
	} else if (bridge->funcs->pre_enable) {
		bridge->funcs->pre_enable(bridge);
	}
}
}
EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);

/**
 * drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in
@@ -633,33 +702,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_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;

			old_bridge_state =
				drm_atomic_get_old_bridge_state(old_state,
								iter);
			if (WARN_ON(!old_bridge_state))
				return;
				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;

			iter->funcs->atomic_pre_enable(iter, old_bridge_state);
		} else if (iter->funcs->pre_enable) {
			iter->funcs->pre_enable(iter);
				drm_atomic_bridge_call_pre_enable(next, old_state);
			}
		}

		drm_atomic_bridge_call_pre_enable(iter, old_state);

		if (iter->pre_enable_prev_first)
			/* Jump all bridges that we have already pre_enabled */
			iter = limit;

		if (iter == bridge)
			break;
	}
+8 −0
Original line number Diff line number Diff line
@@ -744,6 +744,14 @@ struct drm_bridge {
	 * modes.
	 */
	bool interlace_allowed;
	/**
	 * @pre_enable_prev_first: The bridge requires that the prev
	 * 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_prev_first;
	/**
	 * @ddc: Associated I2C adapter for DDC access, if any.
	 */