summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/drm_bridge.c
diff options
context:
space:
mode:
authorArchit Taneja <architt@codeaurora.org>2015-05-21 11:03:16 +0530
committerDaniel Vetter <daniel.vetter@ffwll.ch>2015-05-21 13:55:42 +0200
commit862e686ce40299250d21ba425ea5078e947397ff (patch)
treef2abeec2ca0f4bebb2feba01f216608946e6bf22 /drivers/gpu/drm/drm_bridge.c
parent57744aa7cfb5969c5a0621f4cfa45bb83d391064 (diff)
drm: bridge: Allow daisy chaining of bridges
Allow drm_bridge objects to link to each other in order to form an encoder chain. The requirement for creating a chain of bridges comes because the MSM drm driver uses up its encoder and bridge objects for blocks within the SoC itself. There isn't anything left to use if the SoC display output is connected to an external encoder IC. Having an additional bridge connected to the existing bridge helps here. In general, it is possible for platforms to have multiple devices between the encoder and the connector/panel that require some sort of configuration. We create drm bridge helper functions corresponding to each op in 'drm_bridge_funcs'. These helpers call the corresponding 'drm_bridge_funcs' op for the entire chain of bridges. These helpers are used internally by drm_atomic_helper.c and drm_crtc_helper.c. The drm_bridge_enable/pre_enable helpers execute enable/pre_enable ops of the bridge closet to the encoder, and proceed until the last bridge in the chain is enabled. The same holds for drm_bridge_mode_set/mode_fixup helpers. The drm_bridge_disable/post_disable helpers disable the last bridge in the chain first, and proceed until the first bridge in the chain is disabled. drm_bridge_attach() remains the same. As before, the driver calling this function should make sure it has set the links correctly. The order in which the bridges are connected to each other determines the order in which the calls are made. One requirement is that every bridge in the chain should point the parent encoder object. This is required since bridge drivers expect a valid encoder pointer in drm_bridge. For example, consider a chain where an encoder's output is connected to bridge1, and bridge1's output is connected to bridge2: /* Like before, attach bridge to an encoder */ bridge1->encoder = encoder; ret = drm_bridge_attach(dev, bridge1); .. /* * set the first bridge's 'next' bridge to bridge2, set its encoder * as bridge1's encoder */ bridge1->next = bridge2 bridge2->encoder = bridge1->encoder; ret = drm_bridge_attach(dev, bridge2); ... ... This method of bridge chaining isn't intrusive and existing drivers that use drm_bridge will behave the same way as before. The bridge helpers also cleans up the atomic and crtc helper files a bit. Reviewed-by: Jani Nikula <jani.nikula@linux.intel.com> Reviewed-by: Rob Clark <robdclark@gmail.com> Reviewed-by: Daniel Vetter <daniel@ffwll.ch> Signed-off-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'drivers/gpu/drm/drm_bridge.c')
-rw-r--r--drivers/gpu/drm/drm_bridge.c147
1 files changed, 147 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index eaa5790c2a6f..c3a85ce5a7c4 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -66,6 +66,153 @@ int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge)
}
EXPORT_SYMBOL(drm_bridge_attach);
+/**
+ * drm_bridge_mode_fixup - fixup proposed mode for all bridges in the
+ * encoder chain
+ * @bridge: bridge control structure
+ * @mode: desired mode to be set for the bridge
+ * @adjusted_mode: updated mode that works for this bridge
+ *
+ * Calls 'mode_fixup' drm_bridge_funcs op for all the bridges in the
+ * encoder chain, starting from the first bridge to the last.
+ *
+ * Note: the bridge passed should be the one closest to the encoder
+ *
+ * RETURNS:
+ * true on success, false on failure
+ */
+bool drm_bridge_mode_fixup(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ bool ret = true;
+
+ if (!bridge)
+ return true;
+
+ if (bridge->funcs->mode_fixup)
+ ret = bridge->funcs->mode_fixup(bridge, mode, adjusted_mode);
+
+ ret = ret && drm_bridge_mode_fixup(bridge->next, mode, adjusted_mode);
+
+ return ret;
+}
+EXPORT_SYMBOL(drm_bridge_mode_fixup);
+
+/**
+ * drm_bridge_disable - calls 'disable' drm_bridge_funcs op for all
+ * bridges in the encoder chain.
+ * @bridge: bridge control structure
+ *
+ * Calls 'disable' drm_bridge_funcs 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_disable(struct drm_bridge *bridge)
+{
+ if (!bridge)
+ return;
+
+ drm_bridge_disable(bridge->next);
+
+ bridge->funcs->disable(bridge);
+}
+EXPORT_SYMBOL(drm_bridge_disable);
+
+/**
+ * drm_bridge_post_disable - calls 'post_disable' drm_bridge_funcs op for
+ * all bridges in the encoder chain.
+ * @bridge: bridge control structure
+ *
+ * Calls 'post_disable' drm_bridge_funcs 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_post_disable(struct drm_bridge *bridge)
+{
+ if (!bridge)
+ return;
+
+ bridge->funcs->post_disable(bridge);
+
+ drm_bridge_post_disable(bridge->next);
+}
+EXPORT_SYMBOL(drm_bridge_post_disable);
+
+/**
+ * drm_bridge_mode_set - set proposed mode for all bridges in the
+ * encoder chain
+ * @bridge: bridge control structure
+ * @mode: desired mode to be set for the bridge
+ * @adjusted_mode: updated mode that works for this bridge
+ *
+ * Calls 'mode_set' drm_bridge_funcs op for all the bridges in the
+ * encoder chain, starting from the first bridge to the last.
+ *
+ * Note: the bridge passed should be the one closest to the encoder
+ */
+void drm_bridge_mode_set(struct drm_bridge *bridge,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ if (!bridge)
+ return;
+
+ if (bridge->funcs->mode_set)
+ bridge->funcs->mode_set(bridge, mode, adjusted_mode);
+
+ drm_bridge_mode_set(bridge->next, mode, adjusted_mode);
+}
+EXPORT_SYMBOL(drm_bridge_mode_set);
+
+/**
+ * drm_bridge_pre_enable - calls 'pre_enable' drm_bridge_funcs op for all
+ * bridges in the encoder chain.
+ * @bridge: bridge control structure
+ *
+ * Calls 'pre_enable' drm_bridge_funcs 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_pre_enable(struct drm_bridge *bridge)
+{
+ if (!bridge)
+ return;
+
+ drm_bridge_pre_enable(bridge->next);
+
+ bridge->funcs->pre_enable(bridge);
+}
+EXPORT_SYMBOL(drm_bridge_pre_enable);
+
+/**
+ * drm_bridge_enable - calls 'enable' drm_bridge_funcs op for all bridges
+ * in the encoder chain.
+ * @bridge: bridge control structure
+ *
+ * Calls 'enable' drm_bridge_funcs 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_enable(struct drm_bridge *bridge)
+{
+ if (!bridge)
+ return;
+
+ bridge->funcs->enable(bridge);
+
+ drm_bridge_enable(bridge->next);
+}
+EXPORT_SYMBOL(drm_bridge_enable);
+
#ifdef CONFIG_OF
struct drm_bridge *of_drm_find_bridge(struct device_node *np)
{