drm: bridge: Allow daisy chaining of bridges
authorArchit Taneja <architt@codeaurora.org>
Thu, 21 May 2015 05:33:16 +0000 (11:03 +0530)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Thu, 21 May 2015 11:55:42 +0000 (13:55 +0200)
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>
drivers/gpu/drm/drm_atomic_helper.c
drivers/gpu/drm/drm_bridge.c
drivers/gpu/drm/drm_crtc_helper.c
include/drm/drm_crtc.h

index 21259e70adee7c2fad945bbba685e6994950f392..a64bacdcf2630fdf6b8e8b00a7cf14df6696ac12 100644 (file)
@@ -283,14 +283,11 @@ mode_fixup(struct drm_atomic_state *state)
                if (!funcs)
                        continue;
 
-               if (encoder->bridge && encoder->bridge->funcs->mode_fixup) {
-                       ret = encoder->bridge->funcs->mode_fixup(
-                                       encoder->bridge, &crtc_state->mode,
-                                       &crtc_state->adjusted_mode);
-                       if (!ret) {
-                               DRM_DEBUG_ATOMIC("Bridge fixup failed\n");
-                               return -EINVAL;
-                       }
+               ret = drm_bridge_mode_fixup(encoder->bridge, &crtc_state->mode,
+                               &crtc_state->adjusted_mode);
+               if (!ret) {
+                       DRM_DEBUG_ATOMIC("Bridge fixup failed\n");
+                       return -EINVAL;
                }
 
                if (funcs->atomic_check) {
@@ -587,8 +584,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
                 * Each encoder has at most one connector (since we always steal
                 * it away), so we won't call disable hooks twice.
                 */
-               if (encoder->bridge)
-                       encoder->bridge->funcs->disable(encoder->bridge);
+               drm_bridge_disable(encoder->bridge);
 
                /* Right function depends upon target state. */
                if (connector->state->crtc && funcs->prepare)
@@ -598,8 +594,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
                else
                        funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
 
-               if (encoder->bridge)
-                       encoder->bridge->funcs->post_disable(encoder->bridge);
+               drm_bridge_post_disable(encoder->bridge);
        }
 
        for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
@@ -741,9 +736,7 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
                if (funcs->mode_set)
                        funcs->mode_set(encoder, mode, adjusted_mode);
 
-               if (encoder->bridge && encoder->bridge->funcs->mode_set)
-                       encoder->bridge->funcs->mode_set(encoder->bridge,
-                                                        mode, adjusted_mode);
+               drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode);
        }
 }
 
@@ -839,16 +832,14 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
                 * Each encoder has at most one connector (since we always steal
                 * it away), so we won't call enable hooks twice.
                 */
-               if (encoder->bridge)
-                       encoder->bridge->funcs->pre_enable(encoder->bridge);
+               drm_bridge_pre_enable(encoder->bridge);
 
                if (funcs->enable)
                        funcs->enable(encoder);
                else
                        funcs->commit(encoder);
 
-               if (encoder->bridge)
-                       encoder->bridge->funcs->enable(encoder->bridge);
+               drm_bridge_enable(encoder->bridge);
        }
 }
 EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables);
index eaa5790c2a6f89a384f01d398bef81ef74cacb48..c3a85ce5a7c497846cce541e8dee25f13d67a18d 100644 (file)
@@ -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)
 {
index d727b73fba3a43e95bb5d08bef3ea5133b33f70e..f2f42b17967a7dff88a2914d8fff9df2e230797d 100644 (file)
@@ -163,16 +163,14 @@ drm_encoder_disable(struct drm_encoder *encoder)
 {
        const struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
 
-       if (encoder->bridge)
-               encoder->bridge->funcs->disable(encoder->bridge);
+       drm_bridge_disable(encoder->bridge);
 
        if (encoder_funcs->disable)
                (*encoder_funcs->disable)(encoder);
        else
                (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
 
-       if (encoder->bridge)
-               encoder->bridge->funcs->post_disable(encoder->bridge);
+       drm_bridge_post_disable(encoder->bridge);
 }
 
 static void __drm_helper_disable_unused_functions(struct drm_device *dev)
@@ -312,13 +310,11 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
                if (encoder->crtc != crtc)
                        continue;
 
-               if (encoder->bridge && encoder->bridge->funcs->mode_fixup) {
-                       ret = encoder->bridge->funcs->mode_fixup(
-                                       encoder->bridge, mode, adjusted_mode);
-                       if (!ret) {
-                               DRM_DEBUG_KMS("Bridge fixup failed\n");
-                               goto done;
-                       }
+               ret = drm_bridge_mode_fixup(encoder->bridge,
+                       mode, adjusted_mode);
+               if (!ret) {
+                       DRM_DEBUG_KMS("Bridge fixup failed\n");
+                       goto done;
                }
 
                encoder_funcs = encoder->helper_private;
@@ -343,15 +339,13 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
                if (encoder->crtc != crtc)
                        continue;
 
-               if (encoder->bridge)
-                       encoder->bridge->funcs->disable(encoder->bridge);
+               drm_bridge_disable(encoder->bridge);
 
                encoder_funcs = encoder->helper_private;
                /* Disable the encoders as the first thing we do. */
                encoder_funcs->prepare(encoder);
 
-               if (encoder->bridge)
-                       encoder->bridge->funcs->post_disable(encoder->bridge);
+               drm_bridge_post_disable(encoder->bridge);
        }
 
        drm_crtc_prepare_encoders(dev);
@@ -376,9 +370,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
                encoder_funcs = encoder->helper_private;
                encoder_funcs->mode_set(encoder, mode, adjusted_mode);
 
-               if (encoder->bridge && encoder->bridge->funcs->mode_set)
-                       encoder->bridge->funcs->mode_set(encoder->bridge, mode,
-                                       adjusted_mode);
+               drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode);
        }
 
        /* Now enable the clocks, plane, pipe, and connectors that we set up. */
@@ -389,14 +381,12 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
                if (encoder->crtc != crtc)
                        continue;
 
-               if (encoder->bridge)
-                       encoder->bridge->funcs->pre_enable(encoder->bridge);
+               drm_bridge_pre_enable(encoder->bridge);
 
                encoder_funcs = encoder->helper_private;
                encoder_funcs->commit(encoder);
 
-               if (encoder->bridge)
-                       encoder->bridge->funcs->enable(encoder->bridge);
+               drm_bridge_enable(encoder->bridge);
        }
 
        /* Calculate and store various constants which
@@ -735,23 +725,19 @@ static void drm_helper_encoder_dpms(struct drm_encoder *encoder, int mode)
        struct drm_bridge *bridge = encoder->bridge;
        const struct drm_encoder_helper_funcs *encoder_funcs;
 
-       if (bridge) {
-               if (mode == DRM_MODE_DPMS_ON)
-                       bridge->funcs->pre_enable(bridge);
-               else
-                       bridge->funcs->disable(bridge);
-       }
+       if (mode == DRM_MODE_DPMS_ON)
+               drm_bridge_pre_enable(bridge);
+       else
+               drm_bridge_disable(bridge);
 
        encoder_funcs = encoder->helper_private;
        if (encoder_funcs->dpms)
                encoder_funcs->dpms(encoder, mode);
 
-       if (bridge) {
-               if (mode == DRM_MODE_DPMS_ON)
-                       bridge->funcs->enable(bridge);
-               else
-                       bridge->funcs->post_disable(bridge);
-       }
+       if (mode == DRM_MODE_DPMS_ON)
+               drm_bridge_enable(bridge);
+       else
+               drm_bridge_post_disable(bridge);
 }
 
 static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc)
index bff25b0cada90b4e96d53776b447a41b062d4b88..dace1b6356855046c9af16f9673c0e6c4647e5ce 100644 (file)
@@ -903,6 +903,8 @@ struct drm_bridge_funcs {
 /**
  * struct drm_bridge - central DRM bridge control structure
  * @dev: DRM device this bridge belongs to
+ * @encoder: encoder to which this bridge is connected
+ * @next: the next bridge in the encoder chain
  * @of_node: device node pointer to the bridge
  * @list: to keep track of all added bridges
  * @base: base mode object
@@ -912,6 +914,7 @@ struct drm_bridge_funcs {
 struct drm_bridge {
        struct drm_device *dev;
        struct drm_encoder *encoder;
+       struct drm_bridge *next;
 #ifdef CONFIG_OF
        struct device_node *of_node;
 #endif
@@ -1247,6 +1250,17 @@ extern void drm_bridge_remove(struct drm_bridge *bridge);
 extern struct drm_bridge *of_drm_find_bridge(struct device_node *np);
 extern int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge);
 
+bool drm_bridge_mode_fixup(struct drm_bridge *bridge,
+                       const struct drm_display_mode *mode,
+                       struct drm_display_mode *adjusted_mode);
+void drm_bridge_disable(struct drm_bridge *bridge);
+void drm_bridge_post_disable(struct drm_bridge *bridge);
+void drm_bridge_mode_set(struct drm_bridge *bridge,
+                       struct drm_display_mode *mode,
+                       struct drm_display_mode *adjusted_mode);
+void drm_bridge_pre_enable(struct drm_bridge *bridge);
+void drm_bridge_enable(struct drm_bridge *bridge);
+
 extern int drm_encoder_init(struct drm_device *dev,
                            struct drm_encoder *encoder,
                            const struct drm_encoder_funcs *funcs,