drm/meson: encoder_hdmi: switch to bridge DRM_BRIDGE_ATTACH_NO_CONNECTOR
authorNeil Armstrong <narmstrong@baylibre.com>
Wed, 20 Oct 2021 12:39:45 +0000 (14:39 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 17 Aug 2022 12:23:17 +0000 (14:23 +0200)
[ Upstream commit 0af5e0b41110e2da872030395231ab19c45be931 ]

This implements the necessary change to no more use the embedded
connector in dw-hdmi and use the dedicated bridge connector driver
by passing DRM_BRIDGE_ATTACH_NO_CONNECTOR to the bridge attach call.

The necessary connector properties are added to handle the same
functionalities as the embedded dw-hdmi connector, i.e. the HDR
metadata, the CEC notifier & other flags.

The dw-hdmi output_port is set to 1 in order to look for a connector
next bridge in order to get DRM_BRIDGE_ATTACH_NO_CONNECTOR working.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Acked-by: Sam Ravnborg <sam@ravnborg.org>
Acked-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20211020123947.2585572-5-narmstrong@baylibre.com
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/gpu/drm/meson/Kconfig
drivers/gpu/drm/meson/meson_dw_hdmi.c
drivers/gpu/drm/meson/meson_encoder_hdmi.c

index 9f9281d..a4e1ed9 100644 (file)
@@ -6,9 +6,11 @@ config DRM_MESON
        select DRM_KMS_HELPER
        select DRM_KMS_CMA_HELPER
        select DRM_GEM_CMA_HELPER
+       select DRM_DISPLAY_CONNECTOR
        select VIDEOMODE_HELPERS
        select REGMAP_MMIO
        select MESON_CANVAS
+       select CEC_CORE if CEC_NOTIFIER
 
 config DRM_MESON_DW_HDMI
        tristate "HDMI Synopsys Controller support for Amlogic Meson Display"
index fb540a5..5cd2b2e 100644 (file)
@@ -803,6 +803,7 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
        dw_plat_data->input_bus_encoding = V4L2_YCBCR_ENC_709;
        dw_plat_data->ycbcr_420_allowed = true;
        dw_plat_data->disable_cec = true;
+       dw_plat_data->output_port = 1;
 
        if (dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
            dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxm-dw-hdmi") ||
index db332fa..5e306de 100644 (file)
 #include <linux/regulator/consumer.h>
 #include <linux/reset.h>
 
+#include <media/cec-notifier.h>
+
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
+#include <drm/drm_bridge_connector.h>
 #include <drm/drm_device.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_probe_helper.h>
@@ -34,8 +37,10 @@ struct meson_encoder_hdmi {
        struct drm_encoder encoder;
        struct drm_bridge bridge;
        struct drm_bridge *next_bridge;
+       struct drm_connector *connector;
        struct meson_drm *priv;
        unsigned long output_bus_fmt;
+       struct cec_notifier *cec_notifier;
 };
 
 #define bridge_to_meson_encoder_hdmi(x) \
@@ -50,6 +55,14 @@ static int meson_encoder_hdmi_attach(struct drm_bridge *bridge,
                                 &encoder_hdmi->bridge, flags);
 }
 
+static void meson_encoder_hdmi_detach(struct drm_bridge *bridge)
+{
+       struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
+
+       cec_notifier_conn_unregister(encoder_hdmi->cec_notifier);
+       encoder_hdmi->cec_notifier = NULL;
+}
+
 static void meson_encoder_hdmi_set_vclk(struct meson_encoder_hdmi *encoder_hdmi,
                                        const struct drm_display_mode *mode)
 {
@@ -298,9 +311,30 @@ static int meson_encoder_hdmi_atomic_check(struct drm_bridge *bridge,
        return 0;
 }
 
+static void meson_encoder_hdmi_hpd_notify(struct drm_bridge *bridge,
+                                         enum drm_connector_status status)
+{
+       struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
+       struct edid *edid;
+
+       if (!encoder_hdmi->cec_notifier)
+               return;
+
+       if (status == connector_status_connected) {
+               edid = drm_bridge_get_edid(encoder_hdmi->next_bridge, encoder_hdmi->connector);
+               if (!edid)
+                       return;
+
+               cec_notifier_set_phys_addr_from_edid(encoder_hdmi->cec_notifier, edid);
+       } else
+               cec_notifier_phys_addr_invalidate(encoder_hdmi->cec_notifier);
+}
+
 static const struct drm_bridge_funcs meson_encoder_hdmi_bridge_funcs = {
        .attach = meson_encoder_hdmi_attach,
+       .detach = meson_encoder_hdmi_detach,
        .mode_valid = meson_encoder_hdmi_mode_valid,
+       .hpd_notify = meson_encoder_hdmi_hpd_notify,
        .atomic_enable = meson_encoder_hdmi_atomic_enable,
        .atomic_disable = meson_encoder_hdmi_atomic_disable,
        .atomic_get_input_bus_fmts = meson_encoder_hdmi_get_inp_bus_fmts,
@@ -313,6 +347,7 @@ static const struct drm_bridge_funcs meson_encoder_hdmi_bridge_funcs = {
 int meson_encoder_hdmi_init(struct meson_drm *priv)
 {
        struct meson_encoder_hdmi *meson_encoder_hdmi;
+       struct platform_device *pdev;
        struct device_node *remote;
        int ret;
 
@@ -337,6 +372,7 @@ int meson_encoder_hdmi_init(struct meson_drm *priv)
        meson_encoder_hdmi->bridge.funcs = &meson_encoder_hdmi_bridge_funcs;
        meson_encoder_hdmi->bridge.of_node = priv->dev->of_node;
        meson_encoder_hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
+       meson_encoder_hdmi->bridge.interlace_allowed = true;
 
        drm_bridge_add(&meson_encoder_hdmi->bridge);
 
@@ -353,17 +389,58 @@ int meson_encoder_hdmi_init(struct meson_drm *priv)
        meson_encoder_hdmi->encoder.possible_crtcs = BIT(0);
 
        /* Attach HDMI Encoder Bridge to Encoder */
-       ret = drm_bridge_attach(&meson_encoder_hdmi->encoder, &meson_encoder_hdmi->bridge, NULL, 0);
+       ret = drm_bridge_attach(&meson_encoder_hdmi->encoder, &meson_encoder_hdmi->bridge, NULL,
+                               DRM_BRIDGE_ATTACH_NO_CONNECTOR);
        if (ret) {
                dev_err(priv->dev, "Failed to attach bridge: %d\n", ret);
                return ret;
        }
 
+       /* Initialize & attach Bridge Connector */
+       meson_encoder_hdmi->connector = drm_bridge_connector_init(priv->drm,
+                                                       &meson_encoder_hdmi->encoder);
+       if (IS_ERR(meson_encoder_hdmi->connector)) {
+               dev_err(priv->dev, "Unable to create HDMI bridge connector\n");
+               return PTR_ERR(meson_encoder_hdmi->connector);
+       }
+       drm_connector_attach_encoder(meson_encoder_hdmi->connector,
+                                    &meson_encoder_hdmi->encoder);
+
        /*
         * We should have now in place:
-        * encoder->[hdmi encoder bridge]->[dw-hdmi bridge]->[dw-hdmi connector]
+        * encoder->[hdmi encoder bridge]->[dw-hdmi bridge]->[display connector bridge]->[display connector]
         */
 
+       /*
+        * drm_connector_attach_max_bpc_property() requires the
+        * connector to have a state.
+        */
+       drm_atomic_helper_connector_reset(meson_encoder_hdmi->connector);
+
+       if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL) ||
+           meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
+           meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
+               drm_connector_attach_hdr_output_metadata_property(meson_encoder_hdmi->connector);
+
+       drm_connector_attach_max_bpc_property(meson_encoder_hdmi->connector, 8, 8);
+
+       /* Handle this here until handled by drm_bridge_connector_init() */
+       meson_encoder_hdmi->connector->ycbcr_420_allowed = true;
+
+       pdev = of_find_device_by_node(remote);
+       if (pdev) {
+               struct cec_connector_info conn_info;
+               struct cec_notifier *notifier;
+
+               cec_fill_conn_info_from_drm(&conn_info, meson_encoder_hdmi->connector);
+
+               notifier = cec_notifier_conn_register(&pdev->dev, NULL, &conn_info);
+               if (!notifier)
+                       return -ENOMEM;
+
+               meson_encoder_hdmi->cec_notifier = notifier;
+       }
+
        dev_dbg(priv->dev, "HDMI encoder initialized\n");
 
        return 0;