On Thu, May 30, 2024 at 11:01:26AM +0200, Maxime Ripard wrote:
> Hi,
> 
> On Thu, May 30, 2024 at 02:12:26AM GMT, Dmitry Baryshkov wrote:
> > In order to let bridge chains implement HDMI connector infrastructure,
> > add necessary glue code to the drm_bridge_connector. In case there is a
> > bridge that sets DRM_BRIDGE_OP_HDMI, drm_bridge_connector will register
> > itself as a HDMI connector and provide proxy drm_connector_hdmi_funcs
> > implementation.
> > 
> > Note, to simplify implementation, there can be only one bridge in a
> > chain that sets DRM_BRIDGE_OP_HDMI. Setting more than one is considered
> > an error. This limitation can be lifted later, if the need arises.
> > 
> > Signed-off-by: Dmitry Baryshkov <dmitry.barysh...@linaro.org>
> > ---
> >  drivers/gpu/drm/drm_bridge_connector.c | 101 
> > ++++++++++++++++++++++++++++++++-
> >  drivers/gpu/drm/drm_debugfs.c          |   2 +
> >  include/drm/drm_bridge.h               |  82 ++++++++++++++++++++++++++
> >  3 files changed, 182 insertions(+), 3 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/drm_bridge_connector.c 
> > b/drivers/gpu/drm/drm_bridge_connector.c
> > index e093fc8928dc..8dca3eda5381 100644
> > --- a/drivers/gpu/drm/drm_bridge_connector.c
> > +++ b/drivers/gpu/drm/drm_bridge_connector.c
> > @@ -18,6 +18,7 @@
> >  #include <drm/drm_managed.h>
> >  #include <drm/drm_modeset_helper_vtables.h>
> >  #include <drm/drm_probe_helper.h>
> > +#include <drm/display/drm_hdmi_state_helper.h>
> >  
> >  /**
> >   * DOC: overview
> > @@ -87,6 +88,13 @@ struct drm_bridge_connector {
> >      * connector modes detection, if any (see &DRM_BRIDGE_OP_MODES).
> >      */
> >     struct drm_bridge *bridge_modes;
> > +   /**
> > +    * @bridge_hdmi:
> > +    *
> > +    * The bridge in the chain that implements necessary support for the
> > +    * HDMI connector infrastructure, if any (see &DRM_BRIDGE_OP_HDMI).
> > +    */
> > +   struct drm_bridge *bridge_hdmi;
> >  };
> >  
> >  #define to_drm_bridge_connector(x) \
> > @@ -287,6 +295,61 @@ static const struct drm_connector_helper_funcs 
> > drm_bridge_connector_helper_funcs
> >     .disable_hpd = drm_bridge_connector_disable_hpd,
> >  };
> >  
> > +static enum drm_mode_status
> > +drm_bridge_connector_tmds_char_rate_valid(const struct drm_connector 
> > *connector,
> > +                                     const struct drm_display_mode *mode,
> > +                                     unsigned long long tmds_rate)
> > +{
> > +   struct drm_bridge_connector *bridge_connector =
> > +           to_drm_bridge_connector(connector);
> > +   struct drm_bridge *bridge;
> > +
> > +   bridge = bridge_connector->bridge_hdmi;
> > +   if (bridge)
> > +           return bridge->funcs->tmds_char_rate_valid ?
> > +                   bridge->funcs->tmds_char_rate_valid(bridge, mode, 
> > tmds_rate) :
> > +                   MODE_OK;
> > +
> > +   return MODE_ERROR;
> > +}
> 
> It's kind of a nitpick, but I think the following syntax is clearer:
> 
> if (bridge)
>       if (bridge->funcs->tmds_char_rate_valid)
>               return bridge->funcs->tmds_char_rate_valid(bridge, mode, 
> tmds_rate);
>       else
>               return MODE_OK;
> 
> return MODE_ERROR;

Ack

> 
> > +static int drm_bridge_connector_clear_infoframe(struct drm_connector 
> > *connector,
> > +                                           enum hdmi_infoframe_type type)
> > +{
> > +   struct drm_bridge_connector *bridge_connector =
> > +           to_drm_bridge_connector(connector);
> > +   struct drm_bridge *bridge;
> > +
> > +   bridge = bridge_connector->bridge_hdmi;
> > +   if (bridge)
> > +           return bridge->funcs->clear_infoframe ?
> > +                   bridge->funcs->clear_infoframe(bridge, type) :
> > +                   0;
> > +
> > +   return -EINVAL;
> > +}
> > +
> > +static int drm_bridge_connector_write_infoframe(struct drm_connector 
> > *connector,
> > +                                           enum hdmi_infoframe_type type,
> > +                                           const u8 *buffer, size_t len)
> > +{
> > +   struct drm_bridge_connector *bridge_connector =
> > +           to_drm_bridge_connector(connector);
> > +   struct drm_bridge *bridge;
> > +
> > +   bridge = bridge_connector->bridge_hdmi;
> > +   if (bridge)
> > +           return bridge->funcs->write_infoframe(bridge, type, buffer, 
> > len);
> > +
> > +   return -EINVAL;
> > +}
> > +
> > +static const struct drm_connector_hdmi_funcs 
> > drm_bridge_connector_hdmi_funcs = {
> > +   .tmds_char_rate_valid = drm_bridge_connector_tmds_char_rate_valid,
> > +   .clear_infoframe = drm_bridge_connector_clear_infoframe,
> > +   .write_infoframe = drm_bridge_connector_write_infoframe,
> > +};
> > +
> >  /* 
> > -----------------------------------------------------------------------------
> >   * Bridge Connector Initialisation
> >   */
> > @@ -312,6 +375,10 @@ struct drm_connector *drm_bridge_connector_init(struct 
> > drm_device *drm,
> >     struct drm_connector *connector;
> >     struct i2c_adapter *ddc = NULL;
> >     struct drm_bridge *bridge, *panel_bridge = NULL;
> > +   const char *vendor = "Unknown";
> > +   const char *product = "Unknown";
> > +   unsigned int supported_formats = BIT(HDMI_COLORSPACE_RGB);
> > +   unsigned int max_bpc = 8;
> >     int connector_type;
> >     int ret;
> >  
> > @@ -348,6 +415,25 @@ struct drm_connector *drm_bridge_connector_init(struct 
> > drm_device *drm,
> >                     bridge_connector->bridge_detect = bridge;
> >             if (bridge->ops & DRM_BRIDGE_OP_MODES)
> >                     bridge_connector->bridge_modes = bridge;
> > +           if (bridge->ops & DRM_BRIDGE_OP_HDMI) {
> > +                   if (bridge_connector->bridge_hdmi)
> > +                           return ERR_PTR(-EBUSY);
> > +                   if (!bridge->funcs->write_infoframe)
> > +                           return ERR_PTR(-EINVAL);
> > +
> > +                   bridge_connector->bridge_hdmi = bridge;
> > +
> > +                   if (bridge->supported_formats)
> > +                           supported_formats = bridge->supported_formats;
> > +                   if (bridge->max_bpc)
> > +                           max_bpc = bridge->max_bpc;
> > +           }
> > +
> > +           if (bridge->vendor)
> > +                   vendor = bridge->vendor;
> > +
> > +           if (bridge->product)
> > +                   product = bridge->product;
> 
> I don't think we should allow HDMI bridges without a product or vendor.
> We should at the very least warn or return an error there, preferably
> the latter to start with. We can always relax the rule if it turns out
> to be too strict later on.

My goal was to be able to override the vendor/product after the HDMI
bridge. Something like setting 'Qualcomm / Snapdragon' in HDMI driver,
but then e.g. display-connector overriding it to e.g. '96board /
DB820c'. Maybe it's an overkill and we should just set vendor / product
from the HDMI bridge.

> 
> >             if (!drm_bridge_get_next_bridge(bridge))
> >                     connector_type = bridge->type;
> > @@ -370,9 +456,18 @@ struct drm_connector *drm_bridge_connector_init(struct 
> > drm_device *drm,
> >             return ERR_PTR(-EINVAL);
> >     }
> >  
> > -   ret = drmm_connector_init(drm, connector,
> > -                             &drm_bridge_connector_funcs,
> > -                             connector_type, ddc);
> > +   if (bridge_connector->bridge_hdmi)
> > +           ret = drmm_connector_hdmi_init(drm, connector,
> > +                                          vendor, product,
> > +                                          &drm_bridge_connector_funcs,
> > +                                          &drm_bridge_connector_hdmi_funcs,
> > +                                          connector_type, ddc,
> > +                                          supported_formats,
> > +                                          max_bpc);
> > +   else
> > +           ret = drmm_connector_init(drm, connector,
> > +                                     &drm_bridge_connector_funcs,
> > +                                     connector_type, ddc);
> >     if (ret) {
> >             kfree(bridge_connector);
> >             return ERR_PTR(ret);
> > diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
> > index dd39a5b7a711..e385d90ef893 100644
> > --- a/drivers/gpu/drm/drm_debugfs.c
> > +++ b/drivers/gpu/drm/drm_debugfs.c
> > @@ -762,6 +762,8 @@ static int bridges_show(struct seq_file *m, void *data)
> >                     drm_puts(&p, " hpd");
> >             if (bridge->ops & DRM_BRIDGE_OP_MODES)
> >                     drm_puts(&p, " modes");
> > +           if (bridge->ops & DRM_BRIDGE_OP_HDMI)
> > +                   drm_puts(&p, " hdmi");
> >             drm_puts(&p, "\n");
> >     }
> >  
> > diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> > index 4baca0d9107b..c45e539fe276 100644
> > --- a/include/drm/drm_bridge.h
> > +++ b/include/drm/drm_bridge.h
> > @@ -630,6 +630,54 @@ struct drm_bridge_funcs {
> >      */
> >     void (*hpd_disable)(struct drm_bridge *bridge);
> >  
> > +   /**
> > +    * @tmds_char_rate_valid:
> > +    *
> > +    * Check whether a particular TMDS character rate is supported by the
> > +    * driver.
> > +    *
> > +    * This callback is optional and should only be implemented by the
> > +    * bridges that take part in the HDMI connector implementation. Bridges
> > +    * that implement it shall set set the DRM_BRIDGE_OP_HDMI flag in their
> > +    * &drm_bridge->ops.
> > +    *
> > +    * Returns:
> > +    *
> > +    * Either &drm_mode_status.MODE_OK or one of the failure reasons
> > +    * in &enum drm_mode_status.
> > +    */
> > +   enum drm_mode_status
> > +   (*tmds_char_rate_valid)(const struct drm_bridge *bridge,
> > +                           const struct drm_display_mode *mode,
> > +                           unsigned long long tmds_rate);
> 
> Would an HDMI prefix make sense here?

Yes, and in other callbacks too.

> 
> > +   /**
> > +    * @clear_infoframe:
> > +    *
> > +    * This callback clears the infoframes in the hardware during commit.
> > +    * It will be called multiple times, once for every disabled infoframe
> > +    * type.
> > +    *
> > +    * This callback is optional and should only be implemented by the
> > +    * bridges that take part in the HDMI connector implementation. Bridges
> > +    * that implement it shall set set the DRM_BRIDGE_OP_HDMI flag in their
> > +    * &drm_bridge->ops.
> > +    */
> > +   int (*clear_infoframe)(struct drm_bridge *bridge,
> > +                          enum hdmi_infoframe_type type);
> 
> Missing newline there.
> 
> > +   /**
> > +    * @write_infoframe:
> > +    *
> > +    * Program the infoframe into the hardware. It will be called multiple
> > +    * times, once for every updated infoframe type.
> > +    *
> > +    * This callback is optional but it must be implemented by bridges that
> > +    * set the DRM_BRIDGE_OP_HDMI flag in their &drm_bridge->ops.
> > +    */
> > +   int (*write_infoframe)(struct drm_bridge *bridge,
> > +                          enum hdmi_infoframe_type type,
> > +                          const u8 *buffer, size_t len);
> > +
> >     /**
> >      * @debugfs_init:
> >      *
> > @@ -705,6 +753,16 @@ enum drm_bridge_ops {
> >      * this flag shall implement the &drm_bridge_funcs->get_modes callback.
> >      */
> >     DRM_BRIDGE_OP_MODES = BIT(3),
> > +   /**
> > +    * @DRM_BRIDGE_OP_HDMI: The bridge provides HDMI connector operations,
> > +    * including infoframes support. Bridges that set this flag must
> > +    * implement the &drm_bridge_funcs->write_infoframe callback.
> > +    *
> > +    * Note: currently there can be at most one bridge in a chain that sets
> > +    * this bit. This is to simplify corresponding glue code in connector
> > +    * drivers.
> > +    */
> > +   DRM_BRIDGE_OP_HDMI = BIT(4),
> >  };
> >  
> >  /**
> > @@ -773,6 +831,30 @@ struct drm_bridge {
> >      * @hpd_cb.
> >      */
> >     void *hpd_data;
> > +
> > +   /**
> > +    * @vendor: Vendor of the product to be used for the SPD InfoFrame
> > +    * generation.
> > +    */
> > +   const char *vendor;
> > +
> > +   /**
> > +    * @product: Name of the product to be used for the SPD InfoFrame
> > +    * generation.
> > +    */
> > +   const char *product;
> > +
> > +   /**
> > +    * @supported_formats: Bitmask of @hdmi_colorspace listing supported
> > +    * output formats. This is only relevant if @DRM_BRIDGE_OP_HDMI is set.
> > +    */
> > +   unsigned int supported_formats;
> > +
> > +   /**
> > +    * @max_bpc: Maximum bits per char the HDMI bridge supports. This is
> > +    * only relevant if @DRM_BRIDGE_OP_HDMI is set.
> > +    */
> 
> We could probably document that the only allowed values are 8, 10 or 12.
> 
> Thanks!
> Maxime



-- 
With best wishes
Dmitry

Reply via email to