On Fri, May 8, 2015 at 6:11 AM, Archit Taneja <architt@xxxxxxxxxxxxxx> wrote: > 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. > > Signed-off-by: Archit Taneja <architt@xxxxxxxxxxxxxx> Looks good. But I guess we probably should have some headerdoc for the new fxns, and somewhere a comment about not calling the bridge vfuncs directly but using the new helper funcs which handle the chaining. I guess it isn't *as* critical as it would be if these were things intended to be called by drivers, rather than just from core, but all the same I think it would be a good idea. With that, Reviewed-by: Rob Clark <robdclark@xxxxxxxxx> > --- > v2: > - Add EXPORT_SYMBOL for the new functions > - Fix logic issue in mode_fixup() > > drivers/gpu/drm/drm_atomic_helper.c | 29 +++++--------- > drivers/gpu/drm/drm_bridge.c | 76 +++++++++++++++++++++++++++++++++++++ > drivers/gpu/drm/drm_crtc_helper.c | 54 ++++++++++---------------- > include/drm/drm_crtc.h | 14 +++++++ > 4 files changed, 120 insertions(+), 53 deletions(-) > > diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c > index 1d2ca52..d6c85c0 100644 > --- a/drivers/gpu/drm/drm_atomic_helper.c > +++ b/drivers/gpu/drm/drm_atomic_helper.c > @@ -281,14 +281,11 @@ mode_fixup(struct drm_atomic_state *state) > encoder = conn_state->best_encoder; > funcs = encoder->helper_private; > > - 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) { > @@ -578,8 +575,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) > @@ -589,8 +585,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) { > @@ -713,9 +708,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); > } > } > > @@ -809,16 +802,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); > diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c > index eaa5790..98c39f6 100644 > --- a/drivers/gpu/drm/drm_bridge.c > +++ b/drivers/gpu/drm/drm_bridge.c > @@ -66,6 +66,82 @@ int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge) > } > EXPORT_SYMBOL(drm_bridge_attach); > > +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); > + > +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); > + > +void drm_bridge_post_disable(struct drm_bridge *bridge) > +{ > + if (!bridge) > + return; > + > + drm_bridge_post_disable(bridge->next); > + > + bridge->funcs->post_disable(bridge); > +} > +EXPORT_SYMBOL(drm_bridge_post_disable); > + > +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); > + > +void drm_bridge_pre_enable(struct drm_bridge *bridge) > +{ > + if (!bridge) > + return; > + > + bridge->funcs->pre_enable(bridge); > + > + drm_bridge_pre_enable(bridge->next); > +} > +EXPORT_SYMBOL(drm_bridge_pre_enable); > + > +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) > { > diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c > index ab00286..ff9b338 100644 > --- a/drivers/gpu/drm/drm_crtc_helper.c > +++ b/drivers/gpu/drm/drm_crtc_helper.c > @@ -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) > diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h > index ca71c03..db830f4 100644 > --- a/include/drm/drm_crtc.h > +++ b/include/drm/drm_crtc.h > @@ -895,6 +895,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 > @@ -904,6 +906,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 > @@ -1230,6 +1233,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, > -- > The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, > hosted by The Linux Foundation > _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel