summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAradhya Bhatia <a-bhatia1@ti.com>2024-03-01 19:43:04 +0530
committerPraneeth Bajjuri <praneeth@ti.com>2024-03-04 10:57:55 -0600
commit7c7a1518438957514ffb17dca4d463b37a6d4f46 (patch)
tree998d873aaa9fee986e858a9248bf666928e08728
parent00dc8b02a75062424d84121c5d5fcd19b3486cb7 (diff)
drm/bridge: Introduce early_enable and late disable
With the existing pre_enable and enable function hooks, the order of enable of display elements looks as follows, crtc_enable -> bridge[n]_pre_enable -> ... -> bridge[1]_pre_enable -> encoder_enable -> bridge[1]_enable -> ... -> bridge[n]_enable and vice versa for the disable. Some bridges need to be up and running before and after their source gets enabled and has run. In some case, that source is a display unit, controlled as part of &drm_crtc. For those bridges, add support for early_enable and late_disable function hooks. Signed-off-by: Aradhya Bhatia <a-bhatia1@ti.com>
-rw-r--r--drivers/gpu/drm/drm_atomic_helper.c66
-rw-r--r--drivers/gpu/drm/drm_bridge.c82
-rw-r--r--include/drm/drm_bridge.h73
3 files changed, 221 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index b097bff1cd18..385af7c95ff4 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1247,6 +1247,48 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
if (ret == 0)
drm_crtc_vblank_put(crtc);
}
+
+ for_each_oldnew_connector_in_state(old_state, connector, old_conn_state, new_conn_state, i) {
+ struct drm_encoder *encoder;
+ struct drm_bridge *bridge;
+
+ /*
+ * Shut down everything that's in the changeset and currently
+ * still on. So need to check the old, saved state.
+ */
+ if (!old_conn_state->crtc)
+ continue;
+
+ old_crtc_state = drm_atomic_get_old_crtc_state(old_state, old_conn_state->crtc);
+
+ if (new_conn_state->crtc)
+ new_crtc_state = drm_atomic_get_new_crtc_state(old_state,
+ new_conn_state->crtc);
+ else
+ new_crtc_state = NULL;
+
+ if (!crtc_needs_disable(old_crtc_state, new_crtc_state) ||
+ !drm_atomic_crtc_needs_modeset(old_conn_state->crtc->state))
+ continue;
+
+ encoder = old_conn_state->best_encoder;
+
+ /* We shouldn't get this far if we didn't previously have
+ * an encoder.. but WARN_ON() rather than explode.
+ */
+ if (WARN_ON(!encoder))
+ continue;
+
+ drm_dbg_atomic(dev, "disabling [ENCODER:%d:%s]\n",
+ encoder->base.id, encoder->name);
+
+ /*
+ * Each encoder has at most one connector (since we always steal
+ * it away), so we won't call disable hooks twice.
+ */
+ bridge = drm_bridge_chain_get_first_bridge(encoder);
+ drm_atomic_bridge_chain_late_disable(bridge, old_state);
+ }
}
/**
@@ -1482,6 +1524,30 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
struct drm_connector_state *new_conn_state;
int i;
+ for_each_new_connector_in_state(old_state, connector, new_conn_state, i) {
+ struct drm_encoder *encoder;
+ struct drm_bridge *bridge;
+
+ if (!new_conn_state->best_encoder)
+ continue;
+
+ if (!new_conn_state->crtc->state->active ||
+ !drm_atomic_crtc_needs_modeset(new_conn_state->crtc->state))
+ continue;
+
+ encoder = new_conn_state->best_encoder;
+
+ drm_dbg_atomic(dev, "enabling [ENCODER:%d:%s]\n",
+ encoder->base.id, encoder->name);
+
+ /*
+ * Each encoder has at most one connector (since we always steal
+ * it away), so we won't call enable hooks twice.
+ */
+ bridge = drm_bridge_chain_get_first_bridge(encoder);
+ drm_atomic_bridge_chain_early_enable(bridge, old_state);
+ }
+
for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
const struct drm_crtc_helper_funcs *funcs;
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 7044e339a82c..9efdf50ab5f8 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -691,6 +691,49 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
}
EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
+/**
+ * drm_atomic_bridge_chain_late_disable - disables all bridges in the encoder chain
+ * after crtc is disabled.
+ * @bridge: bridge control structure
+ * @old_state: old atomic state
+ *
+ * Calls &drm_bridge_funcs.atomic_late_disable op for all the bridges in the
+ * encoder chain, starting from the last bridge to the first. These are called
+ * after calling &drm_crtc_helper_funcs.atomic_late_disable
+ *
+ * Note: the bridge passed should be the one closest to the encoder
+ */
+void drm_atomic_bridge_chain_late_disable(struct drm_bridge *bridge,
+ struct drm_atomic_state *old_state)
+{
+ 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->atomic_late_disable) {
+ 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;
+
+ iter->funcs->atomic_late_disable(iter, old_bridge_state);
+ } else if (iter->funcs->late_disable) {
+ iter->funcs->late_disable(iter);
+ }
+
+ if (iter == bridge)
+ break;
+ }
+}
+EXPORT_SYMBOL(drm_atomic_bridge_chain_late_disable);
+
static void drm_atomic_bridge_call_post_disable(struct drm_bridge *bridge,
struct drm_atomic_state *old_state)
{
@@ -783,6 +826,45 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
}
EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
+/**
+ * drm_atomic_bridge_chain_early_enable - enables all bridges in the encoder
+ * chain before it's crtc is enabled
+ * @bridge: bridge control structure
+ * @old_state: old atomic state
+ *
+ * Calls &drm_bridge_funcs.atomic_early_enable op for all the bridges in the
+ * encoder chain, starting from the first bridge to the last. These are called
+ * before even the &drm_crtc_helper_funcs.atomic_enable is called.
+ *
+ * Note: the bridge passed should be the one closest to the encoder
+ */
+void drm_atomic_bridge_chain_early_enable(struct drm_bridge *bridge,
+ struct drm_atomic_state *old_state)
+{
+ struct drm_encoder *encoder;
+
+ if (!bridge)
+ return;
+
+ encoder = bridge->encoder;
+ list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+ if (bridge->funcs->atomic_early_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_early_enable(bridge, old_bridge_state);
+ } else if (bridge->funcs->early_enable) {
+ bridge->funcs->early_enable(bridge);
+ }
+ }
+}
+EXPORT_SYMBOL(drm_atomic_bridge_chain_early_enable);
+
static void drm_atomic_bridge_call_pre_enable(struct drm_bridge *bridge,
struct drm_atomic_state *old_state)
{
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index a76f4103d48b..ad6c06b960fd 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -204,6 +204,20 @@ struct drm_bridge_funcs {
void (*post_disable)(struct drm_bridge *bridge);
/**
+ * @late_disable:
+ *
+ * This callback should disable the bridge. It is called right after the
+ * preceding element in the display pipe is disabled. If the preceding
+ * element is a bridge this means it's called after that bridge's
+ * @atomic_post_disable. If the preceding element is a &drm_crtc it's
+ * called right after the crtc's &drm_crtc_helper_funcs.atomic_disable
+ * hook.
+ *
+ * The @elate_disable callback is optional.
+ */
+ void (*late_disable)(struct drm_bridge *bridge);
+
+ /**
* @mode_set:
*
* This callback should set the given mode on the bridge. It is called
@@ -232,6 +246,26 @@ struct drm_bridge_funcs {
void (*mode_set)(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode);
+
+ /**
+ * @early_enable:
+ *
+ * This callback should enable the bridge. It is called right before
+ * the preceding element in the display pipe is enabled. If the
+ * preceding element is a bridge this means it's called before that
+ * bridge's @atomic_early_enable. If the preceding element is a
+ * &drm_crtc it's called right before the crtc's
+ * &drm_crtc_helper_funcs.atomic_enable hook.
+ *
+ * The display pipe (i.e. clocks and timing signals) feeding this bridge
+ * will not yet be running when this callback is called. The bridge can
+ * enable the display link feeding the next bridge in the chain (if
+ * there is one) when this callback is called.
+ *
+ * The @early_enable callback is optional.
+ */
+ void (*early_enable)(struct drm_bridge *bridge);
+
/**
* @pre_enable:
*
@@ -283,6 +317,26 @@ struct drm_bridge_funcs {
void (*enable)(struct drm_bridge *bridge);
/**
+ * @early_enable:
+ *
+ * This callback should enable the bridge. It is called right before
+ * the preceding element in the display pipe is enabled. If the
+ * preceding element is a bridge this means it's called before that
+ * bridge's @atomic_early_enable. If the preceding element is a
+ * &drm_crtc it's called right before the crtc's
+ * &drm_crtc_helper_funcs.atomic_enable hook.
+ *
+ * The display pipe (i.e. clocks and timing signals) feeding this bridge
+ * will not yet be running when this callback is called. The bridge can
+ * enable the display link feeding the next bridge in the chain (if
+ * there is one) when this callback is called.
+ *
+ * The @early_enable callback is optional.
+ */
+ void (*atomic_early_enable)(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_bridge_state);
+
+ /**
* @atomic_pre_enable:
*
* This callback should enable the bridge. It is called right before
@@ -383,6 +437,21 @@ struct drm_bridge_funcs {
struct drm_bridge_state *old_bridge_state);
/**
+ * @late_disable:
+ *
+ * This callback should disable the bridge. It is called right after the
+ * preceding element in the display pipe is disabled. If the preceding
+ * element is a bridge this means it's called after that bridge's
+ * @atomic_post_disable. If the preceding element is a &drm_crtc it's
+ * called right after the crtc's &drm_crtc_helper_funcs.atomic_disable
+ * hook.
+ *
+ * The @elate_disable callback is optional.
+ */
+ void (*atomic_late_disable)(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_bridge_state);
+
+ /**
* @atomic_duplicate_state:
*
* Duplicate the current bridge state object (which is guaranteed to be
@@ -899,6 +968,10 @@ void drm_atomic_bridge_chain_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 *state);
+void drm_atomic_bridge_chain_late_disable(struct drm_bridge *bridge,
+ struct drm_atomic_state *state);
+void drm_atomic_bridge_chain_early_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 *state);
void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,