[PATCH] drm: Add drm_bridge

Rob Clark robdclark at gmail.com
Thu Aug 8 18:15:34 PDT 2013


On Thu, Aug 8, 2013 at 8:36 PM, Daniel Vetter <daniel at ffwll.ch> wrote:
> On Thu, Aug 8, 2013 at 11:03 PM, Sean Paul <seanpaul at chromium.org> wrote:
>> This patch adds the notion of a drm_bridge. A bridge is a chained
>> device which hangs off an encoder. The drm driver using the bridge
>> should provide the association between encoder and bridge. Once a
>> bridge is associated with an encoder, it will participate in mode
>> set, dpms, and optionally connection detection.
>>
>> Since a connector may not be able to determine the connection state
>> downstream of the bridge, bridges may implement detect() which will take
>> precedence over connector detect() if the bridge is already attached
>> to an encoder and that encoder is already attached to the connector.
>> In practical terms, this requires the drm driver to make these
>> associations at init time, which is fine for SoC applications where
>> this link is static.
>>
>> Signed-off-by: Sean Paul <seanpaul at chromium.org>
>
> A few comments below. I think overall any such infrastructure simply
> needs to demonstrate viability on a few drivers, that makes it much
> simpler to check whether the interfaces make sense.

Just a quick note.. I have a branch of msm where I've been using a
slightly earlier version of this (and in next day or so will be
rebased on this version).

and I guess Sean is using this on a driver or two.  So that's at least
two drivers ;-)

BR,
-R

> Generally I'd make
> more sense for me if the bridge is just an implementation detail of a
> given encoder and if the bridge would not be exposed to the crtc
> helpers. With that abstraction chaining would be more natural imo and
> we'd also have a much higher chance that bridge drivers work accross
> different drm drivers: Atm you can run some encoder stuff in the crtc
> callbacks and the other way round (and pretty much every driver for a
> bit more complicated hw does that), and every driver can differ in
> those tricks a bit. If we bake the bridge callbacks into the crtc
> helpers I expect that for bridges with tricky ordering constraints
> this will become a giant mess. So I'd prefer much if this would work
> like drm i2c slave encoders.
>
> Cheers, Daniel
>
>> ---
>>  drivers/gpu/drm/drm_crtc.c        |  50 +++++++++++++++++
>>  drivers/gpu/drm/drm_crtc_helper.c | 112 ++++++++++++++++++++++++++++++++++----
>>  drivers/gpu/drm/drm_sysfs.c       |   8 ++-
>>  include/drm/drm_crtc.h            |  48 ++++++++++++++++
>>  include/drm/drm_crtc_helper.h     |  34 ++++++++++++
>>  5 files changed, 241 insertions(+), 11 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
>> index fc83bb9..0311e2b 100644
>> --- a/drivers/gpu/drm/drm_crtc.c
>> +++ b/drivers/gpu/drm/drm_crtc.c
>> @@ -781,6 +781,41 @@ void drm_connector_unplug_all(struct drm_device *dev)
>>  }
>>  EXPORT_SYMBOL(drm_connector_unplug_all);
>>
>> +int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge,
>> +               const struct drm_bridge_funcs *funcs)
>> +{
>> +       int ret;
>> +
>> +       drm_modeset_lock_all(dev);
>> +
>> +       ret = drm_mode_object_get(dev, &bridge->base, DRM_MODE_OBJECT_BRIDGE);
>> +       if (ret)
>> +               goto out;
>> +
>> +       bridge->dev = dev;
>> +       bridge->funcs = funcs;
>> +
>> +       list_add_tail(&bridge->head, &dev->mode_config.bridge_list);
>> +       dev->mode_config.num_bridge++;
>> +
>> + out:
>> +       drm_modeset_unlock_all(dev);
>> +       return ret;
>> +}
>> +EXPORT_SYMBOL(drm_bridge_init);
>> +
>> +void drm_bridge_cleanup(struct drm_bridge *bridge)
>> +{
>> +       struct drm_device *dev = bridge->dev;
>> +
>> +       drm_modeset_lock_all(dev);
>> +       drm_mode_object_put(dev, &bridge->base);
>> +       list_del(&bridge->head);
>> +       dev->mode_config.num_bridge--;
>> +       drm_modeset_unlock_all(dev);
>> +}
>> +EXPORT_SYMBOL(drm_bridge_cleanup);
>> +
>>  int drm_encoder_init(struct drm_device *dev,
>>                       struct drm_encoder *encoder,
>>                       const struct drm_encoder_funcs *funcs,
>> @@ -1190,6 +1225,7 @@ static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *gr
>>         total_objects += dev->mode_config.num_crtc;
>>         total_objects += dev->mode_config.num_connector;
>>         total_objects += dev->mode_config.num_encoder;
>> +       total_objects += dev->mode_config.num_bridge;
>>
>>         group->id_list = kzalloc(total_objects * sizeof(uint32_t), GFP_KERNEL);
>>         if (!group->id_list)
>> @@ -1198,6 +1234,7 @@ static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *gr
>>         group->num_crtcs = 0;
>>         group->num_connectors = 0;
>>         group->num_encoders = 0;
>> +       group->num_bridges = 0;
>>         return 0;
>>  }
>>
>> @@ -1207,6 +1244,7 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev,
>>         struct drm_crtc *crtc;
>>         struct drm_encoder *encoder;
>>         struct drm_connector *connector;
>> +       struct drm_bridge *bridge;
>>         int ret;
>>
>>         if ((ret = drm_mode_group_init(dev, group)))
>> @@ -1223,6 +1261,11 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev,
>>                 group->id_list[group->num_crtcs + group->num_encoders +
>>                                group->num_connectors++] = connector->base.id;
>>
>> +       list_for_each_entry(bridge, &dev->mode_config.bridge_list, head)
>> +               group->id_list[group->num_crtcs + group->num_encoders +
>> +                              group->num_connectors + group->num_bridges++] =
>> +                                       bridge->base.id;
>> +
>>         return 0;
>>  }
>>  EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
>> @@ -3905,6 +3948,7 @@ void drm_mode_config_init(struct drm_device *dev)
>>         INIT_LIST_HEAD(&dev->mode_config.fb_list);
>>         INIT_LIST_HEAD(&dev->mode_config.crtc_list);
>>         INIT_LIST_HEAD(&dev->mode_config.connector_list);
>> +       INIT_LIST_HEAD(&dev->mode_config.bridge_list);
>>         INIT_LIST_HEAD(&dev->mode_config.encoder_list);
>>         INIT_LIST_HEAD(&dev->mode_config.property_list);
>>         INIT_LIST_HEAD(&dev->mode_config.property_blob_list);
>> @@ -3941,6 +3985,7 @@ void drm_mode_config_cleanup(struct drm_device *dev)
>>         struct drm_connector *connector, *ot;
>>         struct drm_crtc *crtc, *ct;
>>         struct drm_encoder *encoder, *enct;
>> +       struct drm_bridge *bridge, *brt;
>>         struct drm_framebuffer *fb, *fbt;
>>         struct drm_property *property, *pt;
>>         struct drm_property_blob *blob, *bt;
>> @@ -3951,6 +3996,11 @@ void drm_mode_config_cleanup(struct drm_device *dev)
>>                 encoder->funcs->destroy(encoder);
>>         }
>>
>> +       list_for_each_entry_safe(bridge, brt,
>> +                                &dev->mode_config.bridge_list, head) {
>> +               bridge->funcs->destroy(bridge);
>> +       }
>> +
>>         list_for_each_entry_safe(connector, ot,
>>                                  &dev->mode_config.connector_list, head) {
>>                 connector->funcs->destroy(connector);
>> diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
>> index 6a64749..30139fe 100644
>> --- a/drivers/gpu/drm/drm_crtc_helper.c
>> +++ b/drivers/gpu/drm/drm_crtc_helper.c
>> @@ -71,6 +71,23 @@ EXPORT_SYMBOL(drm_helper_move_panel_connectors_to_head);
>>  static bool drm_kms_helper_poll = true;
>>  module_param_named(poll, drm_kms_helper_poll, bool, 0600);
>>
>> +static enum drm_connector_status detect_connection(
>> +               struct drm_connector *connector, bool force)
>> +{
>> +       struct drm_encoder *encoder = connector->encoder;
>> +       struct drm_bridge *bridge = encoder ? encoder->bridge : NULL;
>
> That won't work since the connector->encoder link is only set up at
> runtime when a mode is set. Furthermore it's possible for connectors
> to use different encoders.
>
> So if a connector can't figure out whether anything is connected
> itself but needs to poke the bridge that needs to be forward in a
> different fashion. One way would be to allow embedding both the
> drm_bridge an the drm encoder into an over structure and the let a
> bridge specific callback do the casting.
>
>> +
>> +       /*
>> +        * If there's a bridge attached to the encoder (attached to
>> +        * the connector) and it can detect an active connection,
>> +        * re-route the detect call to the bridge.
>> +        */
>> +       if (bridge && bridge->funcs->detect)
>> +               return bridge->funcs->detect(bridge, force);
>> +
>> +       return connector->funcs->detect(connector, force);
>> +}
>> +
>>  static void drm_mode_validate_flag(struct drm_connector *connector,
>>                                    int flags)
>>  {
>> @@ -137,7 +154,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
>>                 if (connector->funcs->force)
>>                         connector->funcs->force(connector);
>>         } else {
>> -               connector->status = connector->funcs->detect(connector, true);
>> +               connector->status = detect_connection(connector, true);
>>         }
>>
>>         /* Re-enable polling in case the global poll config changed. */
>> @@ -256,11 +273,22 @@ static void
>>  drm_encoder_disable(struct drm_encoder *encoder)
>>  {
>>         struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
>> +       struct drm_bridge_helper_funcs *bridge_funcs;
>> +
>> +       if (encoder->bridge) {
>> +               bridge_funcs = encoder->bridge->helper_private;
>> +               bridge_funcs->disable(encoder->bridge);
>> +       }
>>
>>         if (encoder_funcs->disable)
>>                 (*encoder_funcs->disable)(encoder);
>>         else
>>                 (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
>> +
>> +       if (encoder->bridge) {
>> +               bridge_funcs = encoder->bridge->helper_private;
>> +               bridge_funcs->post_disable(encoder->bridge);
>> +       }
>>  }
>>
>>  /**
>> @@ -392,6 +420,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
>>         struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode;
>>         struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
>>         struct drm_encoder_helper_funcs *encoder_funcs;
>> +       struct drm_bridge_helper_funcs *bridge_funcs;
>>         int saved_x, saved_y;
>>         struct drm_encoder *encoder;
>>         bool ret = true;
>> @@ -424,6 +453,17 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
>>
>>                 if (encoder->crtc != crtc)
>>                         continue;
>> +
>> +               if (encoder->bridge) {
>> +                       bridge_funcs = encoder->bridge->helper_private;
>> +                       ret = bridge_funcs->mode_fixup(encoder->bridge, mode,
>> +                                                      adjusted_mode);
>> +                       if (!ret) {
>> +                               DRM_DEBUG_KMS("Bridge fixup failed\n");
>> +                               goto done;
>> +                       }
>> +               }
>
> I strongly believe that this isn't good enough for many cases and that
> often the bridge and encoder want to more closely discuss how to set
> up the link between them (e.g. all the nonsense dsi panels might
> want).
>
>> +
>>                 encoder_funcs = encoder->helper_private;
>>                 if (!(ret = encoder_funcs->mode_fixup(encoder, mode,
>>                                                       adjusted_mode))) {
>> @@ -443,9 +483,20 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
>>
>>                 if (encoder->crtc != crtc)
>>                         continue;
>> +
>> +               if (encoder->bridge) {
>> +                       bridge_funcs = encoder->bridge->helper_private;
>> +                       bridge_funcs->disable(encoder->bridge);
>> +               }
>> +
>>                 encoder_funcs = encoder->helper_private;
>>                 /* Disable the encoders as the first thing we do. */
>>                 encoder_funcs->prepare(encoder);
>> +
>> +               if (encoder->bridge) {
>> +                       bridge_funcs = encoder->bridge->helper_private;
>> +                       bridge_funcs->post_disable(encoder->bridge);
>> +               }
>>         }
>>
>>         drm_crtc_prepare_encoders(dev);
>> @@ -469,6 +520,12 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
>>                         mode->base.id, mode->name);
>>                 encoder_funcs = encoder->helper_private;
>>                 encoder_funcs->mode_set(encoder, mode, adjusted_mode);
>> +
>> +               if (encoder->bridge) {
>> +                       bridge_funcs = encoder->bridge->helper_private;
>> +                       bridge_funcs->mode_set(encoder->bridge, mode,
>> +                                              adjusted_mode);
>> +               }
>>         }
>>
>>         /* Now enable the clocks, plane, pipe, and connectors that we set up. */
>> @@ -479,9 +536,18 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
>>                 if (encoder->crtc != crtc)
>>                         continue;
>>
>> +               if (encoder->bridge) {
>> +                       bridge_funcs = encoder->bridge->helper_private;
>> +                       bridge_funcs->pre_enable(encoder->bridge);
>> +               }
>> +
>>                 encoder_funcs = encoder->helper_private;
>>                 encoder_funcs->commit(encoder);
>>
>> +               if (encoder->bridge) {
>> +                       bridge_funcs = encoder->bridge->helper_private;
>> +                       bridge_funcs->enable(encoder->bridge);
>> +               }
>>         }
>>
>>         /* Store real post-adjustment hardware mode. */
>> @@ -856,8 +922,9 @@ static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc)
>>  void drm_helper_connector_dpms(struct drm_connector *connector, int mode)
>>  {
>>         struct drm_encoder *encoder = connector->encoder;
>> +       struct drm_bridge *bridge = encoder ? encoder->bridge : NULL;
>>         struct drm_crtc *crtc = encoder ? encoder->crtc : NULL;
>> -       int old_dpms;
>> +       int old_dpms, encoder_dpms = DRM_MODE_DPMS_OFF;
>>
>>         if (mode == connector->dpms)
>>                 return;
>> @@ -865,6 +932,9 @@ void drm_helper_connector_dpms(struct drm_connector *connector, int mode)
>>         old_dpms = connector->dpms;
>>         connector->dpms = mode;
>>
>> +       if (encoder)
>> +               encoder_dpms = drm_helper_choose_encoder_dpms(encoder);
>> +
>>         /* from off to on, do crtc then encoder */
>>         if (mode < old_dpms) {
>>                 if (crtc) {
>> @@ -876,18 +946,28 @@ void drm_helper_connector_dpms(struct drm_connector *connector, int mode)
>>                 if (encoder) {
>>                         struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
>>                         if (encoder_funcs->dpms)
>> -                               (*encoder_funcs->dpms) (encoder,
>> -                                                       drm_helper_choose_encoder_dpms(encoder));
>> +                               (*encoder_funcs->dpms) (encoder, encoder_dpms);
>> +               }
>> +               if (bridge) {
>> +                       struct drm_bridge_helper_funcs *bridge_funcs;
>> +
>> +                       bridge_funcs = bridge->helper_private;
>> +                       bridge_funcs->dpms(bridge, encoder_dpms);
>
> Here we only have one dpms callback, no post_disable/pre_enable.
> Presuming you have a bridge that really needs the former this will
> fall over when doing dpms transitions.
>
>>                 }
>>         }
>>
>>         /* from on to off, do encoder then crtc */
>>         if (mode > old_dpms) {
>> +               if (bridge) {
>> +                       struct drm_bridge_helper_funcs *bridge_funcs;
>> +
>> +                       bridge_funcs = bridge->helper_private;
>> +                       bridge_funcs->dpms(bridge, encoder_dpms);
>> +               }
>>                 if (encoder) {
>>                         struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
>>                         if (encoder_funcs->dpms)
>> -                               (*encoder_funcs->dpms) (encoder,
>> -                                                       drm_helper_choose_encoder_dpms(encoder));
>> +                               (*encoder_funcs->dpms) (encoder, encoder_dpms);
>>                 }
>>                 if (crtc) {
>>                         struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
>> @@ -924,9 +1004,11 @@ int drm_helper_resume_force_mode(struct drm_device *dev)
>>  {
>>         struct drm_crtc *crtc;
>>         struct drm_encoder *encoder;
>> +       struct drm_bridge *bridge;
>> +       struct drm_bridge_helper_funcs *bridge_funcs;
>>         struct drm_encoder_helper_funcs *encoder_funcs;
>>         struct drm_crtc_helper_funcs *crtc_funcs;
>> -       int ret;
>> +       int ret, encoder_dpms;
>>
>>         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
>>
>> @@ -946,10 +1028,20 @@ int drm_helper_resume_force_mode(struct drm_device *dev)
>>                                 if(encoder->crtc != crtc)
>>                                         continue;
>>
>> +                               encoder_dpms = drm_helper_choose_encoder_dpms(
>> +                                                       encoder);
>> +
>> +                               bridge = encoder->bridge;
>> +                               if (bridge) {
>> +                                       bridge_funcs = bridge->helper_private;
>> +                                       bridge_funcs->dpms(bridge,
>> +                                               encoder_dpms);
>> +                               }
>> +
>>                                 encoder_funcs = encoder->helper_private;
>>                                 if (encoder_funcs->dpms)
>>                                         (*encoder_funcs->dpms) (encoder,
>> -                                                               drm_helper_choose_encoder_dpms(encoder));
>> +                                                               encoder_dpms);
>>                         }
>>
>>                         crtc_funcs = crtc->helper_private;
>> @@ -1006,7 +1098,7 @@ static void output_poll_execute(struct work_struct *work)
>>                     !(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT))
>>                         continue;
>>
>> -               connector->status = connector->funcs->detect(connector, false);
>> +               connector->status = detect_connection(connector, false);
>>                 if (old_status != connector->status) {
>>                         const char *old, *new;
>>
>> @@ -1092,7 +1184,7 @@ void drm_helper_hpd_irq_event(struct drm_device *dev)
>>
>>                 old_status = connector->status;
>>
>> -               connector->status = connector->funcs->detect(connector, false);
>> +               connector->status = detect_connection(connector, false);
>>                 DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n",
>>                               connector->base.id,
>>                               drm_get_connector_name(connector),
>> diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
>> index 2290b3b..a912d90 100644
>> --- a/drivers/gpu/drm/drm_sysfs.c
>> +++ b/drivers/gpu/drm/drm_sysfs.c
>> @@ -184,6 +184,8 @@ static ssize_t status_show(struct device *device,
>>                            char *buf)
>>  {
>>         struct drm_connector *connector = to_drm_connector(device);
>> +       struct drm_encoder *encoder = connector->encoder;
>> +       struct drm_bridge *bridge = encoder ? encoder->bridge : NULL;
>>         enum drm_connector_status status;
>>         int ret;
>>
>> @@ -191,7 +193,11 @@ static ssize_t status_show(struct device *device,
>>         if (ret)
>>                 return ret;
>>
>> -       status = connector->funcs->detect(connector, true);
>> +       if (bridge && bridge->funcs->detect)
>> +               status = bridge->funcs->detect(bridge, true);
>> +       else
>> +               status = connector->funcs->detect(connector, true);
>> +
>>         mutex_unlock(&connector->dev->mode_config.mutex);
>>
>>         return snprintf(buf, PAGE_SIZE, "%s\n",
>> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
>> index fa12a2f..f020957 100644
>> --- a/include/drm/drm_crtc.h
>> +++ b/include/drm/drm_crtc.h
>> @@ -49,6 +49,7 @@ struct drm_clip_rect;
>>  #define DRM_MODE_OBJECT_FB 0xfbfbfbfb
>>  #define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb
>>  #define DRM_MODE_OBJECT_PLANE 0xeeeeeeee
>> +#define DRM_MODE_OBJECT_BRIDGE 0xbdbdbdbd
>>
>>  struct drm_mode_object {
>>         uint32_t id;
>> @@ -305,6 +306,7 @@ struct drm_connector;
>>  struct drm_encoder;
>>  struct drm_pending_vblank_event;
>>  struct drm_plane;
>> +struct drm_bridge;
>>
>>  /**
>>   * drm_crtc_funcs - control CRTCs for a given device
>> @@ -507,6 +509,7 @@ struct drm_encoder_funcs {
>>   * @possible_crtcs: bitmask of potential CRTC bindings
>>   * @possible_clones: bitmask of potential sibling encoders for cloning
>>   * @crtc: currently bound CRTC
>> + * @bridge: bridge associated to the encoder
>>   * @funcs: control functions
>>   * @helper_private: mid-layer private data
>>   *
>> @@ -523,6 +526,7 @@ struct drm_encoder {
>>         uint32_t possible_clones;
>>
>>         struct drm_crtc *crtc;
>> +       struct drm_bridge *bridge;
>>         const struct drm_encoder_funcs *funcs;
>>         void *helper_private;
>>  };
>> @@ -683,6 +687,41 @@ struct drm_plane {
>>  };
>>
>>  /**
>> + * drm_bridge_funcs - drm_bridge control functions
>> + * @detect: Checks if something is connected to the bridge. If this is
>> + *         implemented, it should take precedence over connector->detect()
>> + *         since the connector may not be able to deduce whether something is
>> + *         connected downstream of the bridge.
>> + * @destroy: make object go away
>> + */
>> +struct drm_bridge_funcs {
>> +       enum drm_connector_status (*detect)(struct drm_bridge *bridge,
>> +                                           bool force);
>> +       void (*destroy)(struct drm_bridge *bridge);
>> +};
>> +
>> +/**
>> + * drm_bridge - central DRM bridge control structure
>> + * @dev: DRM device this bridge belongs to
>> + * @head: list management
>> + * @base: base mode object
>> + * @connector_type: the type of connector this bridge can associate with
>> + * @funcs: control functions
>> + * @helper_private: mid-layer private data
>> + */
>> +struct drm_bridge {
>> +       struct drm_device *dev;
>> +       struct list_head head;
>> +
>> +       struct drm_mode_object base;
>> +
>> +       int connector_type;
>> +
>> +       const struct drm_bridge_funcs *funcs;
>> +       void *helper_private;
>> +};
>> +
>> +/**
>>   * drm_mode_set - new values for a CRTC config change
>>   * @head: list management
>>   * @fb: framebuffer to use for new config
>> @@ -742,6 +781,7 @@ struct drm_mode_group {
>>         uint32_t num_crtcs;
>>         uint32_t num_encoders;
>>         uint32_t num_connectors;
>> +       uint32_t num_bridges;
>>
>>         /* list of object IDs for this group */
>>         uint32_t *id_list;
>> @@ -756,6 +796,8 @@ struct drm_mode_group {
>>   * @fb_list: list of framebuffers available
>>   * @num_connector: number of connectors on this device
>>   * @connector_list: list of connector objects
>> + * @num_bridge: number of bridges on this device
>> + * @bridge_list: list of bridge objects
>>   * @num_encoder: number of encoders on this device
>>   * @encoder_list: list of encoder objects
>>   * @num_crtc: number of CRTCs on this device
>> @@ -793,6 +835,8 @@ struct drm_mode_config {
>>
>>         int num_connector;
>>         struct list_head connector_list;
>> +       int num_bridge;
>> +       struct list_head bridge_list;
>>         int num_encoder;
>>         struct list_head encoder_list;
>>         int num_plane;
>> @@ -878,6 +922,10 @@ extern void drm_connector_cleanup(struct drm_connector *connector);
>>  /* helper to unplug all connectors from sysfs for device */
>>  extern void drm_connector_unplug_all(struct drm_device *dev);
>>
>> +extern int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge,
>> +                          const struct drm_bridge_funcs *funcs);
>> +extern void drm_bridge_cleanup(struct drm_bridge *bridge);
>> +
>>  extern int drm_encoder_init(struct drm_device *dev,
>>                             struct drm_encoder *encoder,
>>                             const struct drm_encoder_funcs *funcs,
>> diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h
>> index f43d556..89a11f9 100644
>> --- a/include/drm/drm_crtc_helper.h
>> +++ b/include/drm/drm_crtc_helper.h
>> @@ -125,6 +125,34 @@ struct drm_connector_helper_funcs {
>>         struct drm_encoder *(*best_encoder)(struct drm_connector *connector);
>>  };
>>
>> +/**
>> + * drm_bridge_helper_funcs - helper operations for bridges (all required)
>> + * @dpms: Control power levels on the bridge.  If the mode passed in is
>> + *       unsupported, the provider must use the next lowest power level.
>> + * @mode_fixup: Try to fixup (or reject entirely) proposed mode for this bridge
>> + * @disable: Called right before encoder prepare, disables the bridge
>> + * @post_disable: Called right after encoder prepare, for lockstepped disable
>> + * @mode_set: Set this mode to the bridge
>> + * @pre_enable: Called right before encoder commit, for lockstepped commit
>> + * @enable: Called right after encoder commit, enables the bridge
>> + *
>> + * The helper operations are called by the mid-layer CRTC helper, however they
>> + * can also be called directly by drivers that don't use the helper functions.
>> + */
>> +struct drm_bridge_helper_funcs {
>> +       void (*dpms)(struct drm_bridge *bridge, int mode);
>> +       bool (*mode_fixup)(struct drm_bridge *bridge,
>> +                          const struct drm_display_mode *mode,
>> +                          struct drm_display_mode *adjusted_mode);
>> +       void (*disable)(struct drm_bridge *bridge);
>> +       void (*post_disable)(struct drm_bridge *bridge);
>> +       void (*mode_set)(struct drm_bridge *bridge,
>> +                        struct drm_display_mode *mode,
>> +                        struct drm_display_mode *adjusted_mode);
>> +       void (*pre_enable)(struct drm_bridge *bridge);
>> +       void (*enable)(struct drm_bridge *bridge);
>> +};
>> +
>>  extern int drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY);
>>  extern void drm_helper_disable_unused_functions(struct drm_device *dev);
>>  extern int drm_crtc_helper_set_config(struct drm_mode_set *set);
>> @@ -160,6 +188,12 @@ static inline void drm_connector_helper_add(struct drm_connector *connector,
>>         connector->helper_private = (void *)funcs;
>>  }
>>
>> +static inline void drm_bridge_helper_add(struct drm_bridge *bridge,
>> +               const struct drm_bridge_helper_funcs *funcs)
>> +{
>> +       bridge->helper_private = (void *)funcs;
>> +}
>> +
>>  extern int drm_helper_resume_force_mode(struct drm_device *dev);
>>  extern void drm_kms_helper_poll_init(struct drm_device *dev);
>>  extern void drm_kms_helper_poll_fini(struct drm_device *dev);
>> --
>> 1.8.3
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel at lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>
>
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel


More information about the dri-devel mailing list