Re: [PATCH 3/7] drm/sun4i: dw-hdmi: Switch to bridge functions

From: Maxime Ripard
Date: Mon Sep 25 2023 - 03:57:30 EST


Hi,

On Sun, Sep 24, 2023 at 09:26:00PM +0200, Jernej Skrabec wrote:
> Since ddc-en property handling was moved from sun8i dw-hdmi driver to
> display connector driver, probe order of drivers determines if EDID is
> properly read at boot time or not.
>
> In order to fix this, let's switch to bridge functions which allows us
> to build proper chain and defer execution until all drivers are probed.
>
> Fixes: 920169041baa ("drm/sun4i: dw-hdmi: Fix ddc-en GPIO consumer conflict")
> Signed-off-by: Jernej Skrabec <jernej.skrabec@xxxxxxxxx>
> ---
> drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c | 114 +++++++++++++++++++++++++-
> drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | 5 ++
> 2 files changed, 117 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
> index 8f8d3bdba5ce..93831cdf1917 100644
> --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
> +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c
> @@ -8,14 +8,82 @@
> #include <linux/of.h>
> #include <linux/platform_device.h>
>
> +#include <drm/drm_atomic_state_helper.h>
> +#include <drm/drm_bridge_connector.h>
> #include <drm/drm_managed.h>
> #include <drm/drm_modeset_helper_vtables.h>
> #include <drm/drm_of.h>
> #include <drm/drm_simple_kms_helper.h>
>
> +#include <media/cec-notifier.h>
> +
> #include "sun8i_dw_hdmi.h"
> #include "sun8i_tcon_top.h"
>
> +#define bridge_to_sun8i_dw_hdmi(x) \
> + container_of(x, struct sun8i_dw_hdmi, enc_bridge)
> +
> +static int sun8i_hdmi_enc_attach(struct drm_bridge *bridge,
> + enum drm_bridge_attach_flags flags)
> +{
> + struct sun8i_dw_hdmi *hdmi = bridge_to_sun8i_dw_hdmi(bridge);
> +
> + return drm_bridge_attach(&hdmi->encoder, hdmi->hdmi_bridge,
> + &hdmi->enc_bridge, flags);
> +}
> +
> +static void sun8i_hdmi_enc_detach(struct drm_bridge *bridge)
> +{
> + struct sun8i_dw_hdmi *hdmi = bridge_to_sun8i_dw_hdmi(bridge);
> +
> + cec_notifier_conn_unregister(hdmi->cec_notifier);
> + hdmi->cec_notifier = NULL;
> +}
> +
> +static void sun8i_hdmi_enc_hpd_notify(struct drm_bridge *bridge,
> + enum drm_connector_status status)
> +{
> + struct sun8i_dw_hdmi *hdmi = bridge_to_sun8i_dw_hdmi(bridge);
> + struct edid *edid;
> +
> + if (!hdmi->cec_notifier)
> + return;
> +
> + if (status == connector_status_connected) {
> + edid = drm_bridge_get_edid(hdmi->hdmi_bridge, hdmi->connector);
> + if (edid)
> + cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier,
> + edid);
> + } else {
> + cec_notifier_phys_addr_invalidate(hdmi->cec_notifier);
> + }
> +}
> +
> +static int sun8i_hdmi_enc_atomic_check(struct drm_bridge *bridge,
> + struct drm_bridge_state *bridge_state,
> + struct drm_crtc_state *crtc_state,
> + struct drm_connector_state *conn_state)
> +{
> + struct drm_connector_state *old_conn_state =
> + drm_atomic_get_old_connector_state(conn_state->state,
> + conn_state->connector);
> +
> + if (!drm_connector_atomic_hdr_metadata_equal(old_conn_state, conn_state))
> + crtc_state->mode_changed = true;
> +
> + return 0;
> +}
> +
> +static const struct drm_bridge_funcs sun8i_hdmi_enc_bridge_funcs = {
> + .attach = sun8i_hdmi_enc_attach,
> + .detach = sun8i_hdmi_enc_detach,
> + .hpd_notify = sun8i_hdmi_enc_hpd_notify,
> + .atomic_check = sun8i_hdmi_enc_atomic_check,
> + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> + .atomic_reset = drm_atomic_helper_bridge_reset,
> +};
> +
> static void sun8i_dw_hdmi_encoder_mode_set(struct drm_encoder *encoder,
> struct drm_display_mode *mode,
> struct drm_display_mode *adj_mode)
> @@ -99,6 +167,8 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
> {
> struct platform_device *pdev = to_platform_device(dev);
> struct dw_hdmi_plat_data *plat_data;
> + struct cec_connector_info conn_info;
> + struct drm_connector *connector;
> struct drm_device *drm = data;
> struct device_node *phy_node;
> struct drm_encoder *encoder;
> @@ -187,18 +257,57 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
>
> plat_data->mode_valid = hdmi->quirks->mode_valid;
> plat_data->use_drm_infoframe = hdmi->quirks->use_drm_infoframe;
> + plat_data->output_port = 1;
> sun8i_hdmi_phy_set_ops(hdmi->phy, plat_data);
>
> platform_set_drvdata(pdev, hdmi);
>
> - hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
> + hdmi->hdmi = dw_hdmi_probe(pdev, plat_data);
> if (IS_ERR(hdmi->hdmi)) {
> ret = PTR_ERR(hdmi->hdmi);
> goto err_deinit_phy;
> }
>
> + hdmi->hdmi_bridge = of_drm_find_bridge(dev->of_node);

So you're also adding child bridge support? This should be mentioned in
the commit log.

> + hdmi->enc_bridge.funcs = &sun8i_hdmi_enc_bridge_funcs;
> + hdmi->enc_bridge.type = DRM_MODE_CONNECTOR_HDMIA;
> + hdmi->enc_bridge.interlace_allowed = true;
> +
> + drm_bridge_add(&hdmi->enc_bridge);
> +
> + ret = drm_bridge_attach(encoder, &hdmi->enc_bridge, NULL,
> + DRM_BRIDGE_ATTACH_NO_CONNECTOR);
> + if (ret)
> + goto err_remove_dw_hdmi;
> +
> + connector = drm_bridge_connector_init(drm, encoder);
> + if (IS_ERR(connector)) {
> + dev_err(dev, "Unable to create HDMI bridge connector\n");
> + ret = PTR_ERR(connector);
> + goto err_remove_dw_hdmi;
> + }
> +
> + hdmi->connector = connector;
> + drm_connector_attach_encoder(connector, encoder);
> +
> + if (hdmi->quirks->use_drm_infoframe)
> + drm_connector_attach_hdr_output_metadata_property(connector);
> +
> + cec_fill_conn_info_from_drm(&conn_info, connector);
> +
> + hdmi->cec_notifier = cec_notifier_conn_register(&pdev->dev, NULL,
> + &conn_info);
> + if (!hdmi->cec_notifier) {
> + ret = -ENOMEM;
> + goto err_remove_dw_hdmi;
> + }
> +
> return 0;

Yeah, I'm not sure. We now have two different yet redundant set of
operations with no clear definition wrt what belongs where. I'm really
not impressed with the use of bridges for things that are clearly not
bridges.

The "nothing happens until all drivers are loaded" argument is also
supposed to be covered by the component framework, so why do we even
have to use a bridge here?

You were saying that it's an issue of probe order going wrong, could you
explain a bit more what goes wrong so we can try to figure something out
that doesn't involve a bridge?

Maxime

Attachment: signature.asc
Description: PGP signature