18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/kernel.h>
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <drm/drm_atomic_state_helper.h>
118c2ecf20Sopenharmony_ci#include <drm/drm_bridge.h>
128c2ecf20Sopenharmony_ci#include <drm/drm_bridge_connector.h>
138c2ecf20Sopenharmony_ci#include <drm/drm_connector.h>
148c2ecf20Sopenharmony_ci#include <drm/drm_device.h>
158c2ecf20Sopenharmony_ci#include <drm/drm_edid.h>
168c2ecf20Sopenharmony_ci#include <drm/drm_modeset_helper_vtables.h>
178c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/**
208c2ecf20Sopenharmony_ci * DOC: overview
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * The DRM bridge connector helper object provides a DRM connector
238c2ecf20Sopenharmony_ci * implementation that wraps a chain of &struct drm_bridge. The connector
248c2ecf20Sopenharmony_ci * operations are fully implemented based on the operations of the bridges in
258c2ecf20Sopenharmony_ci * the chain, and don't require any intervention from the display controller
268c2ecf20Sopenharmony_ci * driver at runtime.
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * To use the helper, display controller drivers create a bridge connector with
298c2ecf20Sopenharmony_ci * a call to drm_bridge_connector_init(). This associates the newly created
308c2ecf20Sopenharmony_ci * connector with the chain of bridges passed to the function and registers it
318c2ecf20Sopenharmony_ci * with the DRM device. At that point the connector becomes fully usable, no
328c2ecf20Sopenharmony_ci * further operation is needed.
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci * The DRM bridge connector operations are implemented based on the operations
358c2ecf20Sopenharmony_ci * provided by the bridges in the chain. Each connector operation is delegated
368c2ecf20Sopenharmony_ci * to the bridge closest to the connector (at the end of the chain) that
378c2ecf20Sopenharmony_ci * provides the relevant functionality.
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci * To make use of this helper, all bridges in the chain shall report bridge
408c2ecf20Sopenharmony_ci * operation flags (&drm_bridge->ops) and bridge output type
418c2ecf20Sopenharmony_ci * (&drm_bridge->type), as well as the DRM_BRIDGE_ATTACH_NO_CONNECTOR attach
428c2ecf20Sopenharmony_ci * flag (none of the bridges shall create a DRM connector directly).
438c2ecf20Sopenharmony_ci */
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/**
468c2ecf20Sopenharmony_ci * struct drm_bridge_connector - A connector backed by a chain of bridges
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_cistruct drm_bridge_connector {
498c2ecf20Sopenharmony_ci	/**
508c2ecf20Sopenharmony_ci	 * @base: The base DRM connector
518c2ecf20Sopenharmony_ci	 */
528c2ecf20Sopenharmony_ci	struct drm_connector base;
538c2ecf20Sopenharmony_ci	/**
548c2ecf20Sopenharmony_ci	 * @encoder:
558c2ecf20Sopenharmony_ci	 *
568c2ecf20Sopenharmony_ci	 * The encoder at the start of the bridges chain.
578c2ecf20Sopenharmony_ci	 */
588c2ecf20Sopenharmony_ci	struct drm_encoder *encoder;
598c2ecf20Sopenharmony_ci	/**
608c2ecf20Sopenharmony_ci	 * @bridge_edid:
618c2ecf20Sopenharmony_ci	 *
628c2ecf20Sopenharmony_ci	 * The last bridge in the chain (closest to the connector) that provides
638c2ecf20Sopenharmony_ci	 * EDID read support, if any (see &DRM_BRIDGE_OP_EDID).
648c2ecf20Sopenharmony_ci	 */
658c2ecf20Sopenharmony_ci	struct drm_bridge *bridge_edid;
668c2ecf20Sopenharmony_ci	/**
678c2ecf20Sopenharmony_ci	 * @bridge_hpd:
688c2ecf20Sopenharmony_ci	 *
698c2ecf20Sopenharmony_ci	 * The last bridge in the chain (closest to the connector) that provides
708c2ecf20Sopenharmony_ci	 * hot-plug detection notification, if any (see &DRM_BRIDGE_OP_HPD).
718c2ecf20Sopenharmony_ci	 */
728c2ecf20Sopenharmony_ci	struct drm_bridge *bridge_hpd;
738c2ecf20Sopenharmony_ci	/**
748c2ecf20Sopenharmony_ci	 * @bridge_detect:
758c2ecf20Sopenharmony_ci	 *
768c2ecf20Sopenharmony_ci	 * The last bridge in the chain (closest to the connector) that provides
778c2ecf20Sopenharmony_ci	 * connector detection, if any (see &DRM_BRIDGE_OP_DETECT).
788c2ecf20Sopenharmony_ci	 */
798c2ecf20Sopenharmony_ci	struct drm_bridge *bridge_detect;
808c2ecf20Sopenharmony_ci	/**
818c2ecf20Sopenharmony_ci	 * @bridge_modes:
828c2ecf20Sopenharmony_ci	 *
838c2ecf20Sopenharmony_ci	 * The last bridge in the chain (closest to the connector) that provides
848c2ecf20Sopenharmony_ci	 * connector modes detection, if any (see &DRM_BRIDGE_OP_MODES).
858c2ecf20Sopenharmony_ci	 */
868c2ecf20Sopenharmony_ci	struct drm_bridge *bridge_modes;
878c2ecf20Sopenharmony_ci};
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci#define to_drm_bridge_connector(x) \
908c2ecf20Sopenharmony_ci	container_of(x, struct drm_bridge_connector, base)
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
938c2ecf20Sopenharmony_ci * Bridge Connector Hot-Plug Handling
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic void drm_bridge_connector_hpd_notify(struct drm_connector *connector,
978c2ecf20Sopenharmony_ci					    enum drm_connector_status status)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct drm_bridge_connector *bridge_connector =
1008c2ecf20Sopenharmony_ci		to_drm_bridge_connector(connector);
1018c2ecf20Sopenharmony_ci	struct drm_bridge *bridge;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	/* Notify all bridges in the pipeline of hotplug events. */
1048c2ecf20Sopenharmony_ci	drm_for_each_bridge_in_chain(bridge_connector->encoder, bridge) {
1058c2ecf20Sopenharmony_ci		if (bridge->funcs->hpd_notify)
1068c2ecf20Sopenharmony_ci			bridge->funcs->hpd_notify(bridge, status);
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic void drm_bridge_connector_hpd_cb(void *cb_data,
1118c2ecf20Sopenharmony_ci					enum drm_connector_status status)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct drm_bridge_connector *drm_bridge_connector = cb_data;
1148c2ecf20Sopenharmony_ci	struct drm_connector *connector = &drm_bridge_connector->base;
1158c2ecf20Sopenharmony_ci	struct drm_device *dev = connector->dev;
1168c2ecf20Sopenharmony_ci	enum drm_connector_status old_status;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	mutex_lock(&dev->mode_config.mutex);
1198c2ecf20Sopenharmony_ci	old_status = connector->status;
1208c2ecf20Sopenharmony_ci	connector->status = status;
1218c2ecf20Sopenharmony_ci	mutex_unlock(&dev->mode_config.mutex);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (old_status == status)
1248c2ecf20Sopenharmony_ci		return;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	drm_bridge_connector_hpd_notify(connector, status);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	drm_kms_helper_hotplug_event(dev);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci/**
1328c2ecf20Sopenharmony_ci * drm_bridge_connector_enable_hpd - Enable hot-plug detection for the connector
1338c2ecf20Sopenharmony_ci * @connector: The DRM bridge connector
1348c2ecf20Sopenharmony_ci *
1358c2ecf20Sopenharmony_ci * This function enables hot-plug detection for the given bridge connector.
1368c2ecf20Sopenharmony_ci * This is typically used by display drivers in their resume handler.
1378c2ecf20Sopenharmony_ci */
1388c2ecf20Sopenharmony_civoid drm_bridge_connector_enable_hpd(struct drm_connector *connector)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct drm_bridge_connector *bridge_connector =
1418c2ecf20Sopenharmony_ci		to_drm_bridge_connector(connector);
1428c2ecf20Sopenharmony_ci	struct drm_bridge *hpd = bridge_connector->bridge_hpd;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (hpd)
1458c2ecf20Sopenharmony_ci		drm_bridge_hpd_enable(hpd, drm_bridge_connector_hpd_cb,
1468c2ecf20Sopenharmony_ci				      bridge_connector);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_bridge_connector_enable_hpd);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci/**
1518c2ecf20Sopenharmony_ci * drm_bridge_connector_disable_hpd - Disable hot-plug detection for the
1528c2ecf20Sopenharmony_ci * connector
1538c2ecf20Sopenharmony_ci * @connector: The DRM bridge connector
1548c2ecf20Sopenharmony_ci *
1558c2ecf20Sopenharmony_ci * This function disables hot-plug detection for the given bridge connector.
1568c2ecf20Sopenharmony_ci * This is typically used by display drivers in their suspend handler.
1578c2ecf20Sopenharmony_ci */
1588c2ecf20Sopenharmony_civoid drm_bridge_connector_disable_hpd(struct drm_connector *connector)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	struct drm_bridge_connector *bridge_connector =
1618c2ecf20Sopenharmony_ci		to_drm_bridge_connector(connector);
1628c2ecf20Sopenharmony_ci	struct drm_bridge *hpd = bridge_connector->bridge_hpd;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (hpd)
1658c2ecf20Sopenharmony_ci		drm_bridge_hpd_disable(hpd);
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_bridge_connector_disable_hpd);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
1708c2ecf20Sopenharmony_ci * Bridge Connector Functions
1718c2ecf20Sopenharmony_ci */
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic enum drm_connector_status
1748c2ecf20Sopenharmony_cidrm_bridge_connector_detect(struct drm_connector *connector, bool force)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	struct drm_bridge_connector *bridge_connector =
1778c2ecf20Sopenharmony_ci		to_drm_bridge_connector(connector);
1788c2ecf20Sopenharmony_ci	struct drm_bridge *detect = bridge_connector->bridge_detect;
1798c2ecf20Sopenharmony_ci	enum drm_connector_status status;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (detect) {
1828c2ecf20Sopenharmony_ci		status = detect->funcs->detect(detect);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		drm_bridge_connector_hpd_notify(connector, status);
1858c2ecf20Sopenharmony_ci	} else {
1868c2ecf20Sopenharmony_ci		switch (connector->connector_type) {
1878c2ecf20Sopenharmony_ci		case DRM_MODE_CONNECTOR_DPI:
1888c2ecf20Sopenharmony_ci		case DRM_MODE_CONNECTOR_LVDS:
1898c2ecf20Sopenharmony_ci		case DRM_MODE_CONNECTOR_DSI:
1908c2ecf20Sopenharmony_ci		case DRM_MODE_CONNECTOR_eDP:
1918c2ecf20Sopenharmony_ci			status = connector_status_connected;
1928c2ecf20Sopenharmony_ci			break;
1938c2ecf20Sopenharmony_ci		default:
1948c2ecf20Sopenharmony_ci			status = connector_status_unknown;
1958c2ecf20Sopenharmony_ci			break;
1968c2ecf20Sopenharmony_ci		}
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	return status;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic void drm_bridge_connector_destroy(struct drm_connector *connector)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	struct drm_bridge_connector *bridge_connector =
2058c2ecf20Sopenharmony_ci		to_drm_bridge_connector(connector);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (bridge_connector->bridge_hpd) {
2088c2ecf20Sopenharmony_ci		struct drm_bridge *hpd = bridge_connector->bridge_hpd;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci		drm_bridge_hpd_disable(hpd);
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	drm_connector_unregister(connector);
2148c2ecf20Sopenharmony_ci	drm_connector_cleanup(connector);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	kfree(bridge_connector);
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs drm_bridge_connector_funcs = {
2208c2ecf20Sopenharmony_ci	.reset = drm_atomic_helper_connector_reset,
2218c2ecf20Sopenharmony_ci	.detect = drm_bridge_connector_detect,
2228c2ecf20Sopenharmony_ci	.fill_modes = drm_helper_probe_single_connector_modes,
2238c2ecf20Sopenharmony_ci	.destroy = drm_bridge_connector_destroy,
2248c2ecf20Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
2258c2ecf20Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
2268c2ecf20Sopenharmony_ci};
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
2298c2ecf20Sopenharmony_ci * Bridge Connector Helper Functions
2308c2ecf20Sopenharmony_ci */
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic int drm_bridge_connector_get_modes_edid(struct drm_connector *connector,
2338c2ecf20Sopenharmony_ci					       struct drm_bridge *bridge)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	enum drm_connector_status status;
2368c2ecf20Sopenharmony_ci	struct edid *edid;
2378c2ecf20Sopenharmony_ci	int n;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	status = drm_bridge_connector_detect(connector, false);
2408c2ecf20Sopenharmony_ci	if (status != connector_status_connected)
2418c2ecf20Sopenharmony_ci		goto no_edid;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	edid = bridge->funcs->get_edid(bridge, connector);
2448c2ecf20Sopenharmony_ci	if (!edid || !drm_edid_is_valid(edid)) {
2458c2ecf20Sopenharmony_ci		kfree(edid);
2468c2ecf20Sopenharmony_ci		goto no_edid;
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	drm_connector_update_edid_property(connector, edid);
2508c2ecf20Sopenharmony_ci	n = drm_add_edid_modes(connector, edid);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	kfree(edid);
2538c2ecf20Sopenharmony_ci	return n;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cino_edid:
2568c2ecf20Sopenharmony_ci	drm_connector_update_edid_property(connector, NULL);
2578c2ecf20Sopenharmony_ci	return 0;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic int drm_bridge_connector_get_modes(struct drm_connector *connector)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	struct drm_bridge_connector *bridge_connector =
2638c2ecf20Sopenharmony_ci		to_drm_bridge_connector(connector);
2648c2ecf20Sopenharmony_ci	struct drm_bridge *bridge;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	/*
2678c2ecf20Sopenharmony_ci	 * If display exposes EDID, then we parse that in the normal way to
2688c2ecf20Sopenharmony_ci	 * build table of supported modes.
2698c2ecf20Sopenharmony_ci	 */
2708c2ecf20Sopenharmony_ci	bridge = bridge_connector->bridge_edid;
2718c2ecf20Sopenharmony_ci	if (bridge)
2728c2ecf20Sopenharmony_ci		return drm_bridge_connector_get_modes_edid(connector, bridge);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	/*
2758c2ecf20Sopenharmony_ci	 * Otherwise if the display pipeline reports modes (e.g. with a fixed
2768c2ecf20Sopenharmony_ci	 * resolution panel or an analog TV output), query it.
2778c2ecf20Sopenharmony_ci	 */
2788c2ecf20Sopenharmony_ci	bridge = bridge_connector->bridge_modes;
2798c2ecf20Sopenharmony_ci	if (bridge)
2808c2ecf20Sopenharmony_ci		return bridge->funcs->get_modes(bridge, connector);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	/*
2838c2ecf20Sopenharmony_ci	 * We can't retrieve modes, which can happen for instance for a DVI or
2848c2ecf20Sopenharmony_ci	 * VGA output with the DDC bus unconnected. The KMS core will add the
2858c2ecf20Sopenharmony_ci	 * default modes.
2868c2ecf20Sopenharmony_ci	 */
2878c2ecf20Sopenharmony_ci	return 0;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs drm_bridge_connector_helper_funcs = {
2918c2ecf20Sopenharmony_ci	.get_modes = drm_bridge_connector_get_modes,
2928c2ecf20Sopenharmony_ci	/* No need for .mode_valid(), the bridges are checked by the core. */
2938c2ecf20Sopenharmony_ci};
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
2968c2ecf20Sopenharmony_ci * Bridge Connector Initialisation
2978c2ecf20Sopenharmony_ci */
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci/**
3008c2ecf20Sopenharmony_ci * drm_bridge_connector_init - Initialise a connector for a chain of bridges
3018c2ecf20Sopenharmony_ci * @drm: the DRM device
3028c2ecf20Sopenharmony_ci * @encoder: the encoder where the bridge chain starts
3038c2ecf20Sopenharmony_ci *
3048c2ecf20Sopenharmony_ci * Allocate, initialise and register a &drm_bridge_connector with the @drm
3058c2ecf20Sopenharmony_ci * device. The connector is associated with a chain of bridges that starts at
3068c2ecf20Sopenharmony_ci * the @encoder. All bridges in the chain shall report bridge operation flags
3078c2ecf20Sopenharmony_ci * (&drm_bridge->ops) and bridge output type (&drm_bridge->type), and none of
3088c2ecf20Sopenharmony_ci * them may create a DRM connector directly.
3098c2ecf20Sopenharmony_ci *
3108c2ecf20Sopenharmony_ci * Returns a pointer to the new connector on success, or a negative error
3118c2ecf20Sopenharmony_ci * pointer otherwise.
3128c2ecf20Sopenharmony_ci */
3138c2ecf20Sopenharmony_cistruct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
3148c2ecf20Sopenharmony_ci						struct drm_encoder *encoder)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	struct drm_bridge_connector *bridge_connector;
3178c2ecf20Sopenharmony_ci	struct drm_connector *connector;
3188c2ecf20Sopenharmony_ci	struct i2c_adapter *ddc = NULL;
3198c2ecf20Sopenharmony_ci	struct drm_bridge *bridge;
3208c2ecf20Sopenharmony_ci	int connector_type;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	bridge_connector = kzalloc(sizeof(*bridge_connector), GFP_KERNEL);
3238c2ecf20Sopenharmony_ci	if (!bridge_connector)
3248c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	bridge_connector->encoder = encoder;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	/*
3298c2ecf20Sopenharmony_ci	 * TODO: Handle doublescan_allowed, stereo_allowed and
3308c2ecf20Sopenharmony_ci	 * ycbcr_420_allowed.
3318c2ecf20Sopenharmony_ci	 */
3328c2ecf20Sopenharmony_ci	connector = &bridge_connector->base;
3338c2ecf20Sopenharmony_ci	connector->interlace_allowed = true;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/*
3368c2ecf20Sopenharmony_ci	 * Initialise connector status handling. First locate the furthest
3378c2ecf20Sopenharmony_ci	 * bridges in the pipeline that support HPD and output detection. Then
3388c2ecf20Sopenharmony_ci	 * initialise the connector polling mode, using HPD if available and
3398c2ecf20Sopenharmony_ci	 * falling back to polling if supported. If neither HPD nor output
3408c2ecf20Sopenharmony_ci	 * detection are available, we don't support hotplug detection at all.
3418c2ecf20Sopenharmony_ci	 */
3428c2ecf20Sopenharmony_ci	connector_type = DRM_MODE_CONNECTOR_Unknown;
3438c2ecf20Sopenharmony_ci	drm_for_each_bridge_in_chain(encoder, bridge) {
3448c2ecf20Sopenharmony_ci		if (!bridge->interlace_allowed)
3458c2ecf20Sopenharmony_ci			connector->interlace_allowed = false;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci		if (bridge->ops & DRM_BRIDGE_OP_EDID)
3488c2ecf20Sopenharmony_ci			bridge_connector->bridge_edid = bridge;
3498c2ecf20Sopenharmony_ci		if (bridge->ops & DRM_BRIDGE_OP_HPD)
3508c2ecf20Sopenharmony_ci			bridge_connector->bridge_hpd = bridge;
3518c2ecf20Sopenharmony_ci		if (bridge->ops & DRM_BRIDGE_OP_DETECT)
3528c2ecf20Sopenharmony_ci			bridge_connector->bridge_detect = bridge;
3538c2ecf20Sopenharmony_ci		if (bridge->ops & DRM_BRIDGE_OP_MODES)
3548c2ecf20Sopenharmony_ci			bridge_connector->bridge_modes = bridge;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci		if (!drm_bridge_get_next_bridge(bridge))
3578c2ecf20Sopenharmony_ci			connector_type = bridge->type;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci		if (bridge->ddc)
3608c2ecf20Sopenharmony_ci			ddc = bridge->ddc;
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	if (connector_type == DRM_MODE_CONNECTOR_Unknown) {
3648c2ecf20Sopenharmony_ci		kfree(bridge_connector);
3658c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	drm_connector_init_with_ddc(drm, connector, &drm_bridge_connector_funcs,
3698c2ecf20Sopenharmony_ci				    connector_type, ddc);
3708c2ecf20Sopenharmony_ci	drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (bridge_connector->bridge_hpd)
3738c2ecf20Sopenharmony_ci		connector->polled = DRM_CONNECTOR_POLL_HPD;
3748c2ecf20Sopenharmony_ci	else if (bridge_connector->bridge_detect)
3758c2ecf20Sopenharmony_ci		connector->polled = DRM_CONNECTOR_POLL_CONNECT
3768c2ecf20Sopenharmony_ci				  | DRM_CONNECTOR_POLL_DISCONNECT;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	return connector;
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_bridge_connector_init);
381