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