|
|
@@ -1,308 +0,0 @@
|
|
|
-From 5365030a003a6cb0c336202256341e4bc9d65d52 Mon Sep 17 00:00:00 2001
|
|
|
-From: Dave Stevenson <[email protected]>
|
|
|
-Date: Thu, 16 Dec 2021 15:25:35 +0000
|
|
|
-Subject: [PATCH] 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 <[email protected]>
|
|
|
----
|
|
|
- drivers/gpu/drm/drm_bridge.c | 177 +++++++++++++++++++++++++----------
|
|
|
- include/drm/drm_bridge.h | 8 ++
|
|
|
- 2 files changed, 137 insertions(+), 48 deletions(-)
|
|
|
-
|
|
|
---- 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(str
|
|
|
- }
|
|
|
- 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_di
|
|
|
- * &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_disabl
|
|
|
- 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;
|
|
|
-
|
|
|
-- old_bridge_state =
|
|
|
-- drm_atomic_get_old_bridge_state(old_state,
|
|
|
-- bridge);
|
|
|
-- if (WARN_ON(!old_bridge_state))
|
|
|
-- return;
|
|
|
-+ if (!list_is_last(&bridge->chain_node, &encoder->bridge_chain)) {
|
|
|
-+ next = list_next_entry(bridge, chain_node);
|
|
|
-
|
|
|
-- bridge->funcs->atomic_post_disable(bridge,
|
|
|
-- old_bridge_state);
|
|
|
-- } else if (bridge->funcs->post_disable) {
|
|
|
-- bridge->funcs->post_disable(bridge);
|
|
|
-+ 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);
|
|
|
-+ }
|
|
|
-+ }
|
|
|
- }
|
|
|
-+
|
|
|
-+ drm_atomic_bridge_call_post_disable(bridge, old_state);
|
|
|
-+
|
|
|
-+ 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,33 +795,62 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_po
|
|
|
- * 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;
|
|
|
-
|
|
|
-- old_bridge_state =
|
|
|
-- drm_atomic_get_old_bridge_state(old_state,
|
|
|
-- iter);
|
|
|
-- if (WARN_ON(!old_bridge_state))
|
|
|
-- return;
|
|
|
-+ list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
|
|
|
-+ 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;
|
|
|
-
|
|
|
-- 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_upstream_first)
|
|
|
-+ /* Jump all bridges that we have already pre_enabled
|
|
|
-+ */
|
|
|
-+ iter = limit;
|
|
|
-+
|
|
|
- if (iter == bridge)
|
|
|
- break;
|
|
|
- }
|
|
|
---- a/include/drm/drm_bridge.h
|
|
|
-+++ b/include/drm/drm_bridge.h
|
|
|
-@@ -769,6 +769,14 @@ struct drm_bridge {
|
|
|
- */
|
|
|
- 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.
|
|
|
- */
|
|
|
- struct i2c_adapter *ddc;
|