drm/bridge-connector: move to DRM_DISPLAY_HELPER module
authorDmitry Baryshkov <dmitry.baryshkov@linaro.org>
Tue, 3 Sep 2024 02:01:57 +0000 (05:01 +0300)
committerMaxime Ripard <mripard@kernel.org>
Tue, 3 Sep 2024 08:18:31 +0000 (10:18 +0200)
drm_bridge_connector is a "leaf" driver, belonging to the display
helper, rather than the "CRTC" drm_kms_helper module. Move the driver
to the drm/display and add necessary Kconfig selection clauses.

Suggested-by: Maxime Ripard <mripard@kernel.org>
Acked-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20240903-drm-bridge-connector-fix-hdmi-reset-v5-2-daebde6d9857@linaro.org
Signed-off-by: Maxime Ripard <mripard@kernel.org>
22 files changed:
MAINTAINERS
drivers/gpu/drm/Makefile
drivers/gpu/drm/bridge/Kconfig
drivers/gpu/drm/display/Kconfig
drivers/gpu/drm/display/Makefile
drivers/gpu/drm/display/drm_bridge_connector.c [new file with mode: 0644]
drivers/gpu/drm/drm_bridge_connector.c [deleted file]
drivers/gpu/drm/imx/dcss/Kconfig
drivers/gpu/drm/imx/lcdc/Kconfig
drivers/gpu/drm/ingenic/Kconfig
drivers/gpu/drm/kmb/Kconfig
drivers/gpu/drm/mediatek/Kconfig
drivers/gpu/drm/meson/Kconfig
drivers/gpu/drm/msm/Kconfig
drivers/gpu/drm/omapdrm/Kconfig
drivers/gpu/drm/renesas/rcar-du/Kconfig
drivers/gpu/drm/renesas/rz-du/Kconfig
drivers/gpu/drm/renesas/shmobile/Kconfig
drivers/gpu/drm/rockchip/Kconfig
drivers/gpu/drm/tegra/Kconfig
drivers/gpu/drm/tidss/Kconfig
drivers/gpu/drm/xlnx/Kconfig

index 42decde..515e7f4 100644 (file)
@@ -7452,8 +7452,8 @@ S:        Maintained
 T:     git https://gitlab.freedesktop.org/drm/misc/kernel.git
 F:     Documentation/devicetree/bindings/display/bridge/
 F:     drivers/gpu/drm/bridge/
+F:     drivers/gpu/drm/display/drm_bridge_connector.c
 F:     drivers/gpu/drm/drm_bridge.c
-F:     drivers/gpu/drm/drm_bridge_connector.c
 F:     include/drm/drm_bridge.h
 F:     include/drm/drm_bridge_connector.h
 
index 68cc925..fa432a1 100644 (file)
@@ -128,7 +128,6 @@ obj-$(CONFIG_DRM_TTM_HELPER) += drm_ttm_helper.o
 drm_kms_helper-y := \
        drm_atomic_helper.o \
        drm_atomic_state_helper.o \
-       drm_bridge_connector.o \
        drm_crtc_helper.o \
        drm_damage_helper.o \
        drm_encoder_slave.o \
index c621be1..3eb9553 100644 (file)
@@ -390,6 +390,7 @@ config DRM_TI_SN65DSI86
        depends on OF
        select DRM_DISPLAY_DP_HELPER
        select DRM_DISPLAY_HELPER
+       select DRM_BRIDGE_CONNECTOR
        select DRM_KMS_HELPER
        select REGMAP_I2C
        select DRM_PANEL
index e6fc253..1d73fde 100644 (file)
@@ -13,6 +13,11 @@ config DRM_DISPLAY_HELPER
 
 if DRM_DISPLAY_HELPER
 
+config DRM_BRIDGE_CONNECTOR
+       bool
+       help
+         DRM connector implementation terminating DRM bridge chains.
+
 config DRM_DISPLAY_DP_AUX_CEC
        bool "Enable DisplayPort CEC-Tunneling-over-AUX HDMI support"
        select DRM_DISPLAY_DP_HELPER
index 629df2f..fbb9d2b 100644 (file)
@@ -3,6 +3,8 @@
 obj-$(CONFIG_DRM_DISPLAY_DP_AUX_BUS) += drm_dp_aux_bus.o
 
 drm_display_helper-y := drm_display_helper_mod.o
+drm_display_helper-$(CONFIG_DRM_BRIDGE_CONNECTOR) += \
+       drm_bridge_connector.o
 drm_display_helper-$(CONFIG_DRM_DISPLAY_DP_HELPER) += \
        drm_dp_dual_mode_helper.o \
        drm_dp_helper.o \
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
new file mode 100644 (file)
index 0000000..a4fbf1e
--- /dev/null
@@ -0,0 +1,478 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_device.h>
+#include <drm/drm_edid.h>
+#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
+ *
+ * The DRM bridge connector helper object provides a DRM connector
+ * implementation that wraps a chain of &struct drm_bridge. The connector
+ * operations are fully implemented based on the operations of the bridges in
+ * the chain, and don't require any intervention from the display controller
+ * driver at runtime.
+ *
+ * To use the helper, display controller drivers create a bridge connector with
+ * a call to drm_bridge_connector_init(). This associates the newly created
+ * connector with the chain of bridges passed to the function and registers it
+ * with the DRM device. At that point the connector becomes fully usable, no
+ * further operation is needed.
+ *
+ * The DRM bridge connector operations are implemented based on the operations
+ * provided by the bridges in the chain. Each connector operation is delegated
+ * to the bridge closest to the connector (at the end of the chain) that
+ * provides the relevant functionality.
+ *
+ * To make use of this helper, all bridges in the chain shall report bridge
+ * operation flags (&drm_bridge->ops) and bridge output type
+ * (&drm_bridge->type), as well as the DRM_BRIDGE_ATTACH_NO_CONNECTOR attach
+ * flag (none of the bridges shall create a DRM connector directly).
+ */
+
+/**
+ * struct drm_bridge_connector - A connector backed by a chain of bridges
+ */
+struct drm_bridge_connector {
+       /**
+        * @base: The base DRM connector
+        */
+       struct drm_connector base;
+       /**
+        * @encoder:
+        *
+        * The encoder at the start of the bridges chain.
+        */
+       struct drm_encoder *encoder;
+       /**
+        * @bridge_edid:
+        *
+        * The last bridge in the chain (closest to the connector) that provides
+        * EDID read support, if any (see &DRM_BRIDGE_OP_EDID).
+        */
+       struct drm_bridge *bridge_edid;
+       /**
+        * @bridge_hpd:
+        *
+        * The last bridge in the chain (closest to the connector) that provides
+        * hot-plug detection notification, if any (see &DRM_BRIDGE_OP_HPD).
+        */
+       struct drm_bridge *bridge_hpd;
+       /**
+        * @bridge_detect:
+        *
+        * The last bridge in the chain (closest to the connector) that provides
+        * connector detection, if any (see &DRM_BRIDGE_OP_DETECT).
+        */
+       struct drm_bridge *bridge_detect;
+       /**
+        * @bridge_modes:
+        *
+        * The last bridge in the chain (closest to the connector) that provides
+        * 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) \
+       container_of(x, struct drm_bridge_connector, base)
+
+/* -----------------------------------------------------------------------------
+ * Bridge Connector Hot-Plug Handling
+ */
+
+static void drm_bridge_connector_hpd_notify(struct drm_connector *connector,
+                                           enum drm_connector_status status)
+{
+       struct drm_bridge_connector *bridge_connector =
+               to_drm_bridge_connector(connector);
+       struct drm_bridge *bridge;
+
+       /* Notify all bridges in the pipeline of hotplug events. */
+       drm_for_each_bridge_in_chain(bridge_connector->encoder, bridge) {
+               if (bridge->funcs->hpd_notify)
+                       bridge->funcs->hpd_notify(bridge, status);
+       }
+}
+
+static void drm_bridge_connector_handle_hpd(struct drm_bridge_connector *drm_bridge_connector,
+                                           enum drm_connector_status status)
+{
+       struct drm_connector *connector = &drm_bridge_connector->base;
+       struct drm_device *dev = connector->dev;
+
+       mutex_lock(&dev->mode_config.mutex);
+       connector->status = status;
+       mutex_unlock(&dev->mode_config.mutex);
+
+       drm_bridge_connector_hpd_notify(connector, status);
+
+       drm_kms_helper_connector_hotplug_event(connector);
+}
+
+static void drm_bridge_connector_hpd_cb(void *cb_data,
+                                       enum drm_connector_status status)
+{
+       drm_bridge_connector_handle_hpd(cb_data, status);
+}
+
+static void drm_bridge_connector_oob_hotplug_event(struct drm_connector *connector,
+                                                  enum drm_connector_status status)
+{
+       struct drm_bridge_connector *bridge_connector =
+               to_drm_bridge_connector(connector);
+
+       drm_bridge_connector_handle_hpd(bridge_connector, status);
+}
+
+static void drm_bridge_connector_enable_hpd(struct drm_connector *connector)
+{
+       struct drm_bridge_connector *bridge_connector =
+               to_drm_bridge_connector(connector);
+       struct drm_bridge *hpd = bridge_connector->bridge_hpd;
+
+       if (hpd)
+               drm_bridge_hpd_enable(hpd, drm_bridge_connector_hpd_cb,
+                                     bridge_connector);
+}
+
+static void drm_bridge_connector_disable_hpd(struct drm_connector *connector)
+{
+       struct drm_bridge_connector *bridge_connector =
+               to_drm_bridge_connector(connector);
+       struct drm_bridge *hpd = bridge_connector->bridge_hpd;
+
+       if (hpd)
+               drm_bridge_hpd_disable(hpd);
+}
+
+/* -----------------------------------------------------------------------------
+ * Bridge Connector Functions
+ */
+
+static enum drm_connector_status
+drm_bridge_connector_detect(struct drm_connector *connector, bool force)
+{
+       struct drm_bridge_connector *bridge_connector =
+               to_drm_bridge_connector(connector);
+       struct drm_bridge *detect = bridge_connector->bridge_detect;
+       enum drm_connector_status status;
+
+       if (detect) {
+               status = detect->funcs->detect(detect);
+
+               drm_bridge_connector_hpd_notify(connector, status);
+       } else {
+               switch (connector->connector_type) {
+               case DRM_MODE_CONNECTOR_DPI:
+               case DRM_MODE_CONNECTOR_LVDS:
+               case DRM_MODE_CONNECTOR_DSI:
+               case DRM_MODE_CONNECTOR_eDP:
+                       status = connector_status_connected;
+                       break;
+               default:
+                       status = connector_status_unknown;
+                       break;
+               }
+       }
+
+       return status;
+}
+
+static void drm_bridge_connector_debugfs_init(struct drm_connector *connector,
+                                             struct dentry *root)
+{
+       struct drm_bridge_connector *bridge_connector =
+               to_drm_bridge_connector(connector);
+       struct drm_encoder *encoder = bridge_connector->encoder;
+       struct drm_bridge *bridge;
+
+       list_for_each_entry(bridge, &encoder->bridge_chain, chain_node) {
+               if (bridge->funcs->debugfs_init)
+                       bridge->funcs->debugfs_init(bridge, root);
+       }
+}
+
+static const struct drm_connector_funcs drm_bridge_connector_funcs = {
+       .reset = drm_atomic_helper_connector_reset,
+       .detect = drm_bridge_connector_detect,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+       .debugfs_init = drm_bridge_connector_debugfs_init,
+       .oob_hotplug_event = drm_bridge_connector_oob_hotplug_event,
+};
+
+/* -----------------------------------------------------------------------------
+ * Bridge Connector Helper Functions
+ */
+
+static int drm_bridge_connector_get_modes_edid(struct drm_connector *connector,
+                                              struct drm_bridge *bridge)
+{
+       enum drm_connector_status status;
+       const struct drm_edid *drm_edid;
+       int n;
+
+       status = drm_bridge_connector_detect(connector, false);
+       if (status != connector_status_connected)
+               goto no_edid;
+
+       drm_edid = drm_bridge_edid_read(bridge, connector);
+       if (!drm_edid_valid(drm_edid)) {
+               drm_edid_free(drm_edid);
+               goto no_edid;
+       }
+
+       drm_edid_connector_update(connector, drm_edid);
+       n = drm_edid_connector_add_modes(connector);
+
+       drm_edid_free(drm_edid);
+       return n;
+
+no_edid:
+       drm_edid_connector_update(connector, NULL);
+       return 0;
+}
+
+static int drm_bridge_connector_get_modes(struct drm_connector *connector)
+{
+       struct drm_bridge_connector *bridge_connector =
+               to_drm_bridge_connector(connector);
+       struct drm_bridge *bridge;
+
+       /*
+        * If display exposes EDID, then we parse that in the normal way to
+        * build table of supported modes.
+        */
+       bridge = bridge_connector->bridge_edid;
+       if (bridge)
+               return drm_bridge_connector_get_modes_edid(connector, bridge);
+
+       /*
+        * Otherwise if the display pipeline reports modes (e.g. with a fixed
+        * resolution panel or an analog TV output), query it.
+        */
+       bridge = bridge_connector->bridge_modes;
+       if (bridge)
+               return bridge->funcs->get_modes(bridge, connector);
+
+       /*
+        * We can't retrieve modes, which can happen for instance for a DVI or
+        * VGA output with the DDC bus unconnected. The KMS core will add the
+        * default modes.
+        */
+       return 0;
+}
+
+static const struct drm_connector_helper_funcs drm_bridge_connector_helper_funcs = {
+       .get_modes = drm_bridge_connector_get_modes,
+       /* No need for .mode_valid(), the bridges are checked by the core. */
+       .enable_hpd = drm_bridge_connector_enable_hpd,
+       .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 MODE_ERROR;
+
+       if (bridge->funcs->hdmi_tmds_char_rate_valid)
+               return bridge->funcs->hdmi_tmds_char_rate_valid(bridge, mode, tmds_rate);
+       else
+               return MODE_OK;
+}
+
+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 -EINVAL;
+
+       return bridge->funcs->hdmi_clear_infoframe(bridge, type);
+}
+
+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 -EINVAL;
+
+       return bridge->funcs->hdmi_write_infoframe(bridge, type, buffer, len);
+}
+
+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
+ */
+
+/**
+ * drm_bridge_connector_init - Initialise a connector for a chain of bridges
+ * @drm: the DRM device
+ * @encoder: the encoder where the bridge chain starts
+ *
+ * Allocate, initialise and register a &drm_bridge_connector with the @drm
+ * device. The connector is associated with a chain of bridges that starts at
+ * the @encoder. All bridges in the chain shall report bridge operation flags
+ * (&drm_bridge->ops) and bridge output type (&drm_bridge->type), and none of
+ * them may create a DRM connector directly.
+ *
+ * Returns a pointer to the new connector on success, or a negative error
+ * pointer otherwise.
+ */
+struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
+                                               struct drm_encoder *encoder)
+{
+       struct drm_bridge_connector *bridge_connector;
+       struct drm_connector *connector;
+       struct i2c_adapter *ddc = NULL;
+       struct drm_bridge *bridge, *panel_bridge = NULL;
+       unsigned int supported_formats = BIT(HDMI_COLORSPACE_RGB);
+       unsigned int max_bpc = 8;
+       int connector_type;
+       int ret;
+
+       bridge_connector = drmm_kzalloc(drm, sizeof(*bridge_connector), GFP_KERNEL);
+       if (!bridge_connector)
+               return ERR_PTR(-ENOMEM);
+
+       bridge_connector->encoder = encoder;
+
+       /*
+        * TODO: Handle doublescan_allowed, stereo_allowed and
+        * ycbcr_420_allowed.
+        */
+       connector = &bridge_connector->base;
+       connector->interlace_allowed = true;
+
+       /*
+        * Initialise connector status handling. First locate the furthest
+        * bridges in the pipeline that support HPD and output detection. Then
+        * initialise the connector polling mode, using HPD if available and
+        * falling back to polling if supported. If neither HPD nor output
+        * detection are available, we don't support hotplug detection at all.
+        */
+       connector_type = DRM_MODE_CONNECTOR_Unknown;
+       drm_for_each_bridge_in_chain(encoder, bridge) {
+               if (!bridge->interlace_allowed)
+                       connector->interlace_allowed = false;
+
+               if (bridge->ops & DRM_BRIDGE_OP_EDID)
+                       bridge_connector->bridge_edid = bridge;
+               if (bridge->ops & DRM_BRIDGE_OP_HPD)
+                       bridge_connector->bridge_hpd = bridge;
+               if (bridge->ops & DRM_BRIDGE_OP_DETECT)
+                       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->hdmi_write_infoframe ||
+                           !bridge->funcs->hdmi_clear_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 (!drm_bridge_get_next_bridge(bridge))
+                       connector_type = bridge->type;
+
+#ifdef CONFIG_OF
+               if (!drm_bridge_get_next_bridge(bridge) &&
+                   bridge->of_node)
+                       connector->fwnode = fwnode_handle_get(of_fwnode_handle(bridge->of_node));
+#endif
+
+               if (bridge->ddc)
+                       ddc = bridge->ddc;
+
+               if (drm_bridge_is_panel(bridge))
+                       panel_bridge = bridge;
+       }
+
+       if (connector_type == DRM_MODE_CONNECTOR_Unknown)
+               return ERR_PTR(-EINVAL);
+
+       if (bridge_connector->bridge_hdmi)
+               ret = drmm_connector_hdmi_init(drm, connector,
+                                              bridge_connector->bridge_hdmi->vendor,
+                                              bridge_connector->bridge_hdmi->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)
+               return ERR_PTR(ret);
+
+       drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);
+
+       if (bridge_connector->bridge_hpd)
+               connector->polled = DRM_CONNECTOR_POLL_HPD;
+       else if (bridge_connector->bridge_detect)
+               connector->polled = DRM_CONNECTOR_POLL_CONNECT
+                                 | DRM_CONNECTOR_POLL_DISCONNECT;
+
+       if (panel_bridge)
+               drm_panel_bridge_set_orientation(connector, panel_bridge);
+
+       return connector;
+}
+EXPORT_SYMBOL_GPL(drm_bridge_connector_init);
diff --git a/drivers/gpu/drm/drm_bridge_connector.c b/drivers/gpu/drm/drm_bridge_connector.c
deleted file mode 100644 (file)
index a4fbf1e..0000000
+++ /dev/null
@@ -1,478 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/property.h>
-#include <linux/slab.h>
-
-#include <drm/drm_atomic_state_helper.h>
-#include <drm/drm_bridge.h>
-#include <drm/drm_bridge_connector.h>
-#include <drm/drm_connector.h>
-#include <drm/drm_device.h>
-#include <drm/drm_edid.h>
-#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
- *
- * The DRM bridge connector helper object provides a DRM connector
- * implementation that wraps a chain of &struct drm_bridge. The connector
- * operations are fully implemented based on the operations of the bridges in
- * the chain, and don't require any intervention from the display controller
- * driver at runtime.
- *
- * To use the helper, display controller drivers create a bridge connector with
- * a call to drm_bridge_connector_init(). This associates the newly created
- * connector with the chain of bridges passed to the function and registers it
- * with the DRM device. At that point the connector becomes fully usable, no
- * further operation is needed.
- *
- * The DRM bridge connector operations are implemented based on the operations
- * provided by the bridges in the chain. Each connector operation is delegated
- * to the bridge closest to the connector (at the end of the chain) that
- * provides the relevant functionality.
- *
- * To make use of this helper, all bridges in the chain shall report bridge
- * operation flags (&drm_bridge->ops) and bridge output type
- * (&drm_bridge->type), as well as the DRM_BRIDGE_ATTACH_NO_CONNECTOR attach
- * flag (none of the bridges shall create a DRM connector directly).
- */
-
-/**
- * struct drm_bridge_connector - A connector backed by a chain of bridges
- */
-struct drm_bridge_connector {
-       /**
-        * @base: The base DRM connector
-        */
-       struct drm_connector base;
-       /**
-        * @encoder:
-        *
-        * The encoder at the start of the bridges chain.
-        */
-       struct drm_encoder *encoder;
-       /**
-        * @bridge_edid:
-        *
-        * The last bridge in the chain (closest to the connector) that provides
-        * EDID read support, if any (see &DRM_BRIDGE_OP_EDID).
-        */
-       struct drm_bridge *bridge_edid;
-       /**
-        * @bridge_hpd:
-        *
-        * The last bridge in the chain (closest to the connector) that provides
-        * hot-plug detection notification, if any (see &DRM_BRIDGE_OP_HPD).
-        */
-       struct drm_bridge *bridge_hpd;
-       /**
-        * @bridge_detect:
-        *
-        * The last bridge in the chain (closest to the connector) that provides
-        * connector detection, if any (see &DRM_BRIDGE_OP_DETECT).
-        */
-       struct drm_bridge *bridge_detect;
-       /**
-        * @bridge_modes:
-        *
-        * The last bridge in the chain (closest to the connector) that provides
-        * 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) \
-       container_of(x, struct drm_bridge_connector, base)
-
-/* -----------------------------------------------------------------------------
- * Bridge Connector Hot-Plug Handling
- */
-
-static void drm_bridge_connector_hpd_notify(struct drm_connector *connector,
-                                           enum drm_connector_status status)
-{
-       struct drm_bridge_connector *bridge_connector =
-               to_drm_bridge_connector(connector);
-       struct drm_bridge *bridge;
-
-       /* Notify all bridges in the pipeline of hotplug events. */
-       drm_for_each_bridge_in_chain(bridge_connector->encoder, bridge) {
-               if (bridge->funcs->hpd_notify)
-                       bridge->funcs->hpd_notify(bridge, status);
-       }
-}
-
-static void drm_bridge_connector_handle_hpd(struct drm_bridge_connector *drm_bridge_connector,
-                                           enum drm_connector_status status)
-{
-       struct drm_connector *connector = &drm_bridge_connector->base;
-       struct drm_device *dev = connector->dev;
-
-       mutex_lock(&dev->mode_config.mutex);
-       connector->status = status;
-       mutex_unlock(&dev->mode_config.mutex);
-
-       drm_bridge_connector_hpd_notify(connector, status);
-
-       drm_kms_helper_connector_hotplug_event(connector);
-}
-
-static void drm_bridge_connector_hpd_cb(void *cb_data,
-                                       enum drm_connector_status status)
-{
-       drm_bridge_connector_handle_hpd(cb_data, status);
-}
-
-static void drm_bridge_connector_oob_hotplug_event(struct drm_connector *connector,
-                                                  enum drm_connector_status status)
-{
-       struct drm_bridge_connector *bridge_connector =
-               to_drm_bridge_connector(connector);
-
-       drm_bridge_connector_handle_hpd(bridge_connector, status);
-}
-
-static void drm_bridge_connector_enable_hpd(struct drm_connector *connector)
-{
-       struct drm_bridge_connector *bridge_connector =
-               to_drm_bridge_connector(connector);
-       struct drm_bridge *hpd = bridge_connector->bridge_hpd;
-
-       if (hpd)
-               drm_bridge_hpd_enable(hpd, drm_bridge_connector_hpd_cb,
-                                     bridge_connector);
-}
-
-static void drm_bridge_connector_disable_hpd(struct drm_connector *connector)
-{
-       struct drm_bridge_connector *bridge_connector =
-               to_drm_bridge_connector(connector);
-       struct drm_bridge *hpd = bridge_connector->bridge_hpd;
-
-       if (hpd)
-               drm_bridge_hpd_disable(hpd);
-}
-
-/* -----------------------------------------------------------------------------
- * Bridge Connector Functions
- */
-
-static enum drm_connector_status
-drm_bridge_connector_detect(struct drm_connector *connector, bool force)
-{
-       struct drm_bridge_connector *bridge_connector =
-               to_drm_bridge_connector(connector);
-       struct drm_bridge *detect = bridge_connector->bridge_detect;
-       enum drm_connector_status status;
-
-       if (detect) {
-               status = detect->funcs->detect(detect);
-
-               drm_bridge_connector_hpd_notify(connector, status);
-       } else {
-               switch (connector->connector_type) {
-               case DRM_MODE_CONNECTOR_DPI:
-               case DRM_MODE_CONNECTOR_LVDS:
-               case DRM_MODE_CONNECTOR_DSI:
-               case DRM_MODE_CONNECTOR_eDP:
-                       status = connector_status_connected;
-                       break;
-               default:
-                       status = connector_status_unknown;
-                       break;
-               }
-       }
-
-       return status;
-}
-
-static void drm_bridge_connector_debugfs_init(struct drm_connector *connector,
-                                             struct dentry *root)
-{
-       struct drm_bridge_connector *bridge_connector =
-               to_drm_bridge_connector(connector);
-       struct drm_encoder *encoder = bridge_connector->encoder;
-       struct drm_bridge *bridge;
-
-       list_for_each_entry(bridge, &encoder->bridge_chain, chain_node) {
-               if (bridge->funcs->debugfs_init)
-                       bridge->funcs->debugfs_init(bridge, root);
-       }
-}
-
-static const struct drm_connector_funcs drm_bridge_connector_funcs = {
-       .reset = drm_atomic_helper_connector_reset,
-       .detect = drm_bridge_connector_detect,
-       .fill_modes = drm_helper_probe_single_connector_modes,
-       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
-       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-       .debugfs_init = drm_bridge_connector_debugfs_init,
-       .oob_hotplug_event = drm_bridge_connector_oob_hotplug_event,
-};
-
-/* -----------------------------------------------------------------------------
- * Bridge Connector Helper Functions
- */
-
-static int drm_bridge_connector_get_modes_edid(struct drm_connector *connector,
-                                              struct drm_bridge *bridge)
-{
-       enum drm_connector_status status;
-       const struct drm_edid *drm_edid;
-       int n;
-
-       status = drm_bridge_connector_detect(connector, false);
-       if (status != connector_status_connected)
-               goto no_edid;
-
-       drm_edid = drm_bridge_edid_read(bridge, connector);
-       if (!drm_edid_valid(drm_edid)) {
-               drm_edid_free(drm_edid);
-               goto no_edid;
-       }
-
-       drm_edid_connector_update(connector, drm_edid);
-       n = drm_edid_connector_add_modes(connector);
-
-       drm_edid_free(drm_edid);
-       return n;
-
-no_edid:
-       drm_edid_connector_update(connector, NULL);
-       return 0;
-}
-
-static int drm_bridge_connector_get_modes(struct drm_connector *connector)
-{
-       struct drm_bridge_connector *bridge_connector =
-               to_drm_bridge_connector(connector);
-       struct drm_bridge *bridge;
-
-       /*
-        * If display exposes EDID, then we parse that in the normal way to
-        * build table of supported modes.
-        */
-       bridge = bridge_connector->bridge_edid;
-       if (bridge)
-               return drm_bridge_connector_get_modes_edid(connector, bridge);
-
-       /*
-        * Otherwise if the display pipeline reports modes (e.g. with a fixed
-        * resolution panel or an analog TV output), query it.
-        */
-       bridge = bridge_connector->bridge_modes;
-       if (bridge)
-               return bridge->funcs->get_modes(bridge, connector);
-
-       /*
-        * We can't retrieve modes, which can happen for instance for a DVI or
-        * VGA output with the DDC bus unconnected. The KMS core will add the
-        * default modes.
-        */
-       return 0;
-}
-
-static const struct drm_connector_helper_funcs drm_bridge_connector_helper_funcs = {
-       .get_modes = drm_bridge_connector_get_modes,
-       /* No need for .mode_valid(), the bridges are checked by the core. */
-       .enable_hpd = drm_bridge_connector_enable_hpd,
-       .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 MODE_ERROR;
-
-       if (bridge->funcs->hdmi_tmds_char_rate_valid)
-               return bridge->funcs->hdmi_tmds_char_rate_valid(bridge, mode, tmds_rate);
-       else
-               return MODE_OK;
-}
-
-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 -EINVAL;
-
-       return bridge->funcs->hdmi_clear_infoframe(bridge, type);
-}
-
-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 -EINVAL;
-
-       return bridge->funcs->hdmi_write_infoframe(bridge, type, buffer, len);
-}
-
-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
- */
-
-/**
- * drm_bridge_connector_init - Initialise a connector for a chain of bridges
- * @drm: the DRM device
- * @encoder: the encoder where the bridge chain starts
- *
- * Allocate, initialise and register a &drm_bridge_connector with the @drm
- * device. The connector is associated with a chain of bridges that starts at
- * the @encoder. All bridges in the chain shall report bridge operation flags
- * (&drm_bridge->ops) and bridge output type (&drm_bridge->type), and none of
- * them may create a DRM connector directly.
- *
- * Returns a pointer to the new connector on success, or a negative error
- * pointer otherwise.
- */
-struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
-                                               struct drm_encoder *encoder)
-{
-       struct drm_bridge_connector *bridge_connector;
-       struct drm_connector *connector;
-       struct i2c_adapter *ddc = NULL;
-       struct drm_bridge *bridge, *panel_bridge = NULL;
-       unsigned int supported_formats = BIT(HDMI_COLORSPACE_RGB);
-       unsigned int max_bpc = 8;
-       int connector_type;
-       int ret;
-
-       bridge_connector = drmm_kzalloc(drm, sizeof(*bridge_connector), GFP_KERNEL);
-       if (!bridge_connector)
-               return ERR_PTR(-ENOMEM);
-
-       bridge_connector->encoder = encoder;
-
-       /*
-        * TODO: Handle doublescan_allowed, stereo_allowed and
-        * ycbcr_420_allowed.
-        */
-       connector = &bridge_connector->base;
-       connector->interlace_allowed = true;
-
-       /*
-        * Initialise connector status handling. First locate the furthest
-        * bridges in the pipeline that support HPD and output detection. Then
-        * initialise the connector polling mode, using HPD if available and
-        * falling back to polling if supported. If neither HPD nor output
-        * detection are available, we don't support hotplug detection at all.
-        */
-       connector_type = DRM_MODE_CONNECTOR_Unknown;
-       drm_for_each_bridge_in_chain(encoder, bridge) {
-               if (!bridge->interlace_allowed)
-                       connector->interlace_allowed = false;
-
-               if (bridge->ops & DRM_BRIDGE_OP_EDID)
-                       bridge_connector->bridge_edid = bridge;
-               if (bridge->ops & DRM_BRIDGE_OP_HPD)
-                       bridge_connector->bridge_hpd = bridge;
-               if (bridge->ops & DRM_BRIDGE_OP_DETECT)
-                       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->hdmi_write_infoframe ||
-                           !bridge->funcs->hdmi_clear_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 (!drm_bridge_get_next_bridge(bridge))
-                       connector_type = bridge->type;
-
-#ifdef CONFIG_OF
-               if (!drm_bridge_get_next_bridge(bridge) &&
-                   bridge->of_node)
-                       connector->fwnode = fwnode_handle_get(of_fwnode_handle(bridge->of_node));
-#endif
-
-               if (bridge->ddc)
-                       ddc = bridge->ddc;
-
-               if (drm_bridge_is_panel(bridge))
-                       panel_bridge = bridge;
-       }
-
-       if (connector_type == DRM_MODE_CONNECTOR_Unknown)
-               return ERR_PTR(-EINVAL);
-
-       if (bridge_connector->bridge_hdmi)
-               ret = drmm_connector_hdmi_init(drm, connector,
-                                              bridge_connector->bridge_hdmi->vendor,
-                                              bridge_connector->bridge_hdmi->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)
-               return ERR_PTR(ret);
-
-       drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);
-
-       if (bridge_connector->bridge_hpd)
-               connector->polled = DRM_CONNECTOR_POLL_HPD;
-       else if (bridge_connector->bridge_detect)
-               connector->polled = DRM_CONNECTOR_POLL_CONNECT
-                                 | DRM_CONNECTOR_POLL_DISCONNECT;
-
-       if (panel_bridge)
-               drm_panel_bridge_set_orientation(connector, panel_bridge);
-
-       return connector;
-}
-EXPORT_SYMBOL_GPL(drm_bridge_connector_init);
index 3ffc061..59e3b6a 100644 (file)
@@ -2,6 +2,8 @@ config DRM_IMX_DCSS
        tristate "i.MX8MQ DCSS"
        select IMX_IRQSTEER
        select DRM_KMS_HELPER
+       select DRM_DISPLAY_HELPER
+       select DRM_BRIDGE_CONNECTOR
        select DRM_GEM_DMA_HELPER
        select VIDEOMODE_HELPERS
        depends on DRM && ARCH_MXC && ARM64
index 7e57922..9c28bb0 100644 (file)
@@ -3,5 +3,7 @@ config DRM_IMX_LCDC
       depends on DRM && (ARCH_MXC || COMPILE_TEST)
       select DRM_GEM_DMA_HELPER
       select DRM_KMS_HELPER
+      select DRM_DISPLAY_HELPER
+      select DRM_BRIDGE_CONNECTOR
       help
         Found on i.MX1, i.MX21, i.MX25 and i.MX27.
index 3db117c..8cd7b75 100644 (file)
@@ -8,6 +8,8 @@ config DRM_INGENIC
        select DRM_BRIDGE
        select DRM_PANEL_BRIDGE
        select DRM_KMS_HELPER
+       select DRM_DISPLAY_HELPER
+       select DRM_BRIDGE_CONNECTOR
        select DRM_GEM_DMA_HELPER
        select REGMAP
        select REGMAP_MMIO
index fd01136..e5ae3ec 100644 (file)
@@ -3,6 +3,8 @@ config DRM_KMB_DISPLAY
        depends on DRM
        depends on ARCH_KEEMBAY || COMPILE_TEST
        select DRM_KMS_HELPER
+       select DRM_DISPLAY_HELPER
+       select DRM_BRIDGE_CONNECTOR
        select DRM_GEM_DMA_HELPER
        select DRM_MIPI_DSI
        help
index d6449eb..417ac8c 100644 (file)
@@ -9,6 +9,8 @@ config DRM_MEDIATEK
        depends on MTK_MMSYS
        select DRM_GEM_DMA_HELPER if DRM_FBDEV_EMULATION
        select DRM_KMS_HELPER
+       select DRM_DISPLAY_HELPER
+       select DRM_BRIDGE_CONNECTOR
        select DRM_MIPI_DSI
        select DRM_PANEL
        select MEMORY
index 615fdd0..2544756 100644 (file)
@@ -4,6 +4,8 @@ config DRM_MESON
        depends on DRM && OF && (ARM || ARM64)
        depends on ARCH_MESON || COMPILE_TEST
        select DRM_KMS_HELPER
+       select DRM_DISPLAY_HELPER
+       select DRM_BRIDGE_CONNECTOR
        select DRM_GEM_DMA_HELPER
        select DRM_DISPLAY_CONNECTOR
        select VIDEOMODE_HELPERS
index 26a4c71..90c6810 100644 (file)
@@ -17,6 +17,7 @@ config DRM_MSM
        select DRM_DISPLAY_DP_AUX_BUS
        select DRM_DISPLAY_DP_HELPER
        select DRM_DISPLAY_HELPER
+       select DRM_BRIDGE_CONNECTOR
        select DRM_EXEC
        select DRM_KMS_HELPER
        select DRM_PANEL
index 64e440a..fbd9af7 100644 (file)
@@ -5,6 +5,8 @@ config DRM_OMAP
        depends on DRM && OF
        depends on ARCH_OMAP2PLUS || (COMPILE_TEST && PAGE_SIZE_LESS_THAN_64KB)
        select DRM_KMS_HELPER
+       select DRM_DISPLAY_HELPER
+       select DRM_BRIDGE_CONNECTOR
        select FB_DMAMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION
        select VIDEOMODE_HELPERS
        select HDMI
index c17e7c5..025677f 100644 (file)
@@ -5,6 +5,8 @@ config DRM_RCAR_DU
        depends on ARM || ARM64 || COMPILE_TEST
        depends on ARCH_RENESAS || COMPILE_TEST
        select DRM_KMS_HELPER
+       select DRM_DISPLAY_HELPER
+       select DRM_BRIDGE_CONNECTOR
        select DRM_GEM_DMA_HELPER
        select VIDEOMODE_HELPERS
        help
index 5f0db2c..e1a6dd3 100644 (file)
@@ -6,6 +6,8 @@ config DRM_RZG2L_DU
        depends on VIDEO_RENESAS_VSP1
        select DRM_GEM_DMA_HELPER
        select DRM_KMS_HELPER
+       select DRM_DISPLAY_HELPER
+       select DRM_BRIDGE_CONNECTOR
        select VIDEOMODE_HELPERS
        help
          Choose this option if you have an RZ/G2L alike chipset.
index 027220b..c329ab8 100644 (file)
@@ -5,6 +5,8 @@ config DRM_SHMOBILE
        depends on ARCH_RENESAS || ARCH_SHMOBILE || COMPILE_TEST
        select BACKLIGHT_CLASS_DEVICE
        select DRM_KMS_HELPER
+       select DRM_DISPLAY_HELPER
+       select DRM_BRIDGE_CONNECTOR
        select DRM_GEM_DMA_HELPER
        select VIDEOMODE_HELPERS
        help
index 7df875e..23c49e9 100644 (file)
@@ -86,6 +86,8 @@ config ROCKCHIP_LVDS
        bool "Rockchip LVDS support"
        depends on DRM_ROCKCHIP
        depends on PINCTRL && OF
+       select DRM_DISPLAY_HELPER
+       select DRM_BRIDGE_CONNECTOR
        help
          Choose this option to enable support for Rockchip LVDS controllers.
          Rockchip rk3288 SoC has LVDS TX Controller can be used, and it
@@ -96,6 +98,8 @@ config ROCKCHIP_RGB
        bool "Rockchip RGB support"
        depends on DRM_ROCKCHIP
        depends on PINCTRL
+       select DRM_DISPLAY_HELPER
+       select DRM_BRIDGE_CONNECTOR
        help
          Choose this option to enable support for Rockchip RGB output.
          Some Rockchip CRTCs, like rv1108, can directly output parallel
index 782f51d..e688d81 100644 (file)
@@ -8,6 +8,7 @@ config DRM_TEGRA
        select DRM_DISPLAY_DP_HELPER
        select DRM_DISPLAY_HDMI_HELPER
        select DRM_DISPLAY_HELPER
+       select DRM_BRIDGE_CONNECTOR
        select DRM_DISPLAY_DP_AUX_BUS
        select DRM_KMS_HELPER
        select DRM_MIPI_DSI
index 3786008..2385c56 100644 (file)
@@ -3,6 +3,8 @@ config DRM_TIDSS
        depends on DRM && OF
        depends on ARM || ARM64 || COMPILE_TEST
        select DRM_KMS_HELPER
+       select DRM_DISPLAY_HELPER
+       select DRM_BRIDGE_CONNECTOR
        select DRM_GEM_DMA_HELPER
        help
          The TI Keystone family SoCs introduced a new generation of
index 68ee897..626e5ac 100644 (file)
@@ -8,6 +8,7 @@ config DRM_ZYNQMP_DPSUB
        select DMA_ENGINE
        select DRM_DISPLAY_DP_HELPER
        select DRM_DISPLAY_HELPER
+       select DRM_BRIDGE_CONNECTOR
        select DRM_GEM_DMA_HELPER
        select DRM_KMS_HELPER
        select GENERIC_PHY