drivers/gpu/drm/display/Kconfig | 6 + drivers/gpu/drm/display/Makefile | 2 + drivers/gpu/drm/display/drm_dp_connector_helper.c | 184 ++++++++++++++++++++++ include/drm/display/drm_dp_connector_helper.h | 33 ++++ include/drm/drm_connector.h | 102 +++++++++++- 5 files changed, 323 insertions(+), 4 deletions(-)
Add common structure to drm_connector and a set of high-level helpers to
be used by DRM drivers to implement DisplayPort support.
Note: this is currently early WIP patch, sent in order to kick off the
discussion and the hackaton at the XDC 2025.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/display/Kconfig | 6 +
drivers/gpu/drm/display/Makefile | 2 +
drivers/gpu/drm/display/drm_dp_connector_helper.c | 184 ++++++++++++++++++++++
include/drm/display/drm_dp_connector_helper.h | 33 ++++
include/drm/drm_connector.h | 102 +++++++++++-
5 files changed, 323 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/display/Kconfig b/drivers/gpu/drm/display/Kconfig
index df09cf9a8ca19ea894d6f2fad68c0b191e81e3d0..e042a2b7a666e71f5eac748dee8bc506e725533b 100644
--- a/drivers/gpu/drm/display/Kconfig
+++ b/drivers/gpu/drm/display/Kconfig
@@ -40,6 +40,12 @@ config DRM_DISPLAY_DP_AUX_CHARDEV
read and write values to arbitrary DPCD registers on the DP aux
channel.
+config DRM_DISPLAY_DP_CONNECTOR_HELPER
+ bool "test dp_conn"
+ select DRM_DISPLAY_DP_HELPER
+ help
+ DRM display helpers for DisplayPort.
+
config DRM_DISPLAY_DP_HELPER
bool
help
diff --git a/drivers/gpu/drm/display/Makefile b/drivers/gpu/drm/display/Makefile
index 0ff4a1ad0222078bf495175915007f1b1f903296..ee7a09adf3baacbbd7ad251ba92dc78458881192 100644
--- a/drivers/gpu/drm/display/Makefile
+++ b/drivers/gpu/drm/display/Makefile
@@ -5,6 +5,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_CONNECTOR_HELPER) += \
+ drm_dp_connector_helper.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_dp_connector_helper.c b/drivers/gpu/drm/display/drm_dp_connector_helper.c
new file mode 100644
index 0000000000000000000000000000000000000000..1251a25af1a65d05255e763385eeca24c0b49292
--- /dev/null
+++ b/drivers/gpu/drm/display/drm_dp_connector_helper.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * Based on Nouveau DP code:
+ * Copyright 2009 Red Hat Inc.
+ */
+
+#include <drm/drm_connector.h>
+#include <drm/drm_print.h>
+#include <drm/display/drm_dp_connector_helper.h>
+#include <drm/display/drm_dp_helper.h>
+
+static void drm_connector_dp_init_lttpr_caps(struct drm_connector *connector)
+{
+ struct drm_dp_aux *aux = connector->dp.aux;
+ u8 *lttpr_caps = connector->dp.lttpr_caps;
+ u8 dpcd[DP_RECEIVER_CAP_SIZE];
+ int ret, nr;
+
+ if (connector->dp.caps.forbid_lttpr_init)
+ return;
+
+ /*
+ * First access should be to the
+ * DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV,
+ * otherwise LTTPRs might be not initialized correctly.
+ */
+ ret = drm_dp_dpcd_probe(aux, DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV);
+ if (ret)
+ goto err;
+
+ ret = drm_dp_read_dpcd_caps(aux, dpcd);
+ if (ret)
+ goto err;
+
+ ret = drm_dp_read_lttpr_common_caps(aux, dpcd, lttpr_caps);
+ if (ret)
+ goto err;
+
+ /* FIXME: don't attempt switching LTTPR mode on active link */
+ nr = drm_dp_lttpr_count(lttpr_caps);
+ ret = drm_dp_lttpr_init(aux, nr);
+ if (ret)
+ goto err;
+
+ connector->dp.lttpr_count = nr;
+
+ return;
+
+err:
+ memset(lttpr_caps, 0, DP_LTTPR_COMMON_CAP_SIZE);
+ connector->dp.lttpr_count = 0;
+}
+
+enum drm_connector_status drm_atomic_helper_connector_dp_detect(struct drm_connector *connector)
+{
+ struct drm_dp_aux *aux = connector->dp.aux;
+ u8 *dpcd = connector->dp.dpcd;
+ struct drm_dp_desc desc;
+ int ret;
+
+ drm_connector_dp_init_lttpr_caps(connector);
+
+ ret = drm_dp_read_dpcd_caps(aux, dpcd);
+ if (ret)
+ return connector_status_disconnected;
+
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
+ u8 value;
+
+ ret = drm_dp_dpcd_read_byte(aux, DP_EDP_DPCD_REV, &value);
+ if (ret < 0)
+ return connector_status_disconnected;
+
+ connector->dp.edp = value;
+ }
+
+ ret = drm_dp_read_desc(aux, &desc, drm_dp_is_branch(dpcd));
+ if (ret < 0)
+ return connector_status_disconnected;
+
+ if (drm_dp_read_sink_count_cap(connector, dpcd, &desc)) {
+ ret = drm_dp_read_sink_count(aux);
+ if (ret < 0)
+ return connector_status_disconnected;
+
+ /* No sink devices */
+ if (!ret)
+ return connector_status_disconnected;
+ }
+
+ return connector_status_connected;
+}
+EXPORT_SYMBOL(drm_atomic_helper_connector_dp_detect);
+
+static bool drm_connector_dp_check_rate(struct drm_connector *connector,
+ u32 rate)
+{
+ for (int j = 0; j < connector->dp.caps.num_supported_rates; j++)
+ if (connector->dp.caps.supported_rates[j] == rate)
+ return true;
+
+ return false;
+}
+
+void drm_atomic_helper_connector_dp_hotplug(struct drm_connector *connector,
+ enum drm_connector_status status)
+{
+ struct drm_dp_aux *aux = connector->dp.aux;
+ u8 *lttpr_caps = connector->dp.lttpr_caps;
+ u8 *dpcd = connector->dp.dpcd;
+ u32 lane_count;
+ int ret;
+
+ connector->dp.rate_count = 0;
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP &&
+ connector->dp.edp >= DP_EDP_14) {
+ __le16 rates[DP_MAX_SUPPORTED_RATES];
+ int num_rates;
+
+ ret = drm_dp_dpcd_read_data(aux, DP_SUPPORTED_LINK_RATES,
+ rates, sizeof(rates));
+ if (ret)
+ rates[0] = 0;
+
+ for (num_rates = 0;
+ num_rates < DP_MAX_SUPPORTED_RATES && rates[num_rates] != 0;
+ num_rates++)
+ ;
+
+ for (int i = num_rates; i > 0; i--) {
+ u32 rate = (le16_to_cpu(rates[i - 1]) * 200) / 10;
+
+ if (!rate)
+ break;
+
+ if (!drm_connector_dp_check_rate(connector, rate))
+ continue;
+
+ connector->dp.rate[connector->dp.rate_count].dpcd = i - 1;
+ connector->dp.rate[connector->dp.rate_count].rate = rate;
+ connector->dp.rate_count++;
+ }
+ }
+
+ if (!connector->dp.rate_count) {
+ const u32 rates[] = { 810000, 540000, 270000, 162000 };
+ u32 max_rate = dpcd[DP_MAX_LINK_RATE] * 27000;
+
+ if (connector->dp.lttpr_count) {
+ int rate = drm_dp_lttpr_max_link_rate(connector->dp.lttpr_caps);
+
+ if (rate && rate < max_rate)
+ max_rate = rate;
+ }
+
+ for (int i = 0; i < ARRAY_SIZE(rates); i++) {
+ u32 rate = rates[i];
+
+ if (rate > max_rate)
+ continue;
+
+ if (!drm_connector_dp_check_rate(connector, rate))
+ continue;
+
+ connector->dp.rate[connector->dp.rate_count].dpcd = -1;
+ connector->dp.rate[connector->dp.rate_count].rate = rate;
+ connector->dp.rate_count++;
+ }
+ }
+
+ lane_count = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
+ if (connector->dp.lttpr_count) {
+ unsigned int lttpr_lane_count = drm_dp_lttpr_max_lane_count(lttpr_caps);
+
+ if (lttpr_lane_count)
+ lane_count = min(lane_count, lttpr_lane_count);
+ }
+
+ connector->dp.dprx_lanes = lane_count;
+
+}
+EXPORT_SYMBOL(drm_atomic_helper_connector_dp_hotplug);
diff --git a/include/drm/display/drm_dp_connector_helper.h b/include/drm/display/drm_dp_connector_helper.h
new file mode 100644
index 0000000000000000000000000000000000000000..a15ae1a4b1a5ef038e2cca3966f88ea1dafa9c13
--- /dev/null
+++ b/include/drm/display/drm_dp_connector_helper.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef __DRM_DP_CONNECTOR_HELPER_H__
+#define __DRM_DP_CONNECTOR_HELPER_H__
+
+enum drm_connector_status;
+
+enum drm_connector_status drm_atomic_helper_connector_dp_detect(struct drm_connector *connector);
+
+void drm_atomic_helper_connector_dp_hotplug(struct drm_connector *connector,
+ enum drm_connector_status status);
+
+#endif
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 8f34f4b8183d83dccd3e820a444fbf74fb6c16f2..953affeff0d6b7ca574d2b70fc1117dc435a97e8 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -31,6 +31,7 @@
#include <drm/drm_mode_object.h>
#include <drm/drm_util.h>
#include <drm/drm_property.h>
+#include <drm/display/drm_dp.h>
#include <uapi/drm/drm_mode.h>
@@ -46,6 +47,7 @@ struct drm_property_blob;
struct drm_printer;
struct drm_privacy_screen;
struct drm_edid;
+struct drm_dp_aux;
struct edid;
struct hdmi_codec_daifmt;
struct hdmi_codec_params;
@@ -1882,6 +1884,91 @@ struct drm_connector_cec {
void *data;
};
+struct drm_connector_dp_rate_entry {
+ int dpcd;
+ u32 rate;
+};
+
+/**
+ * struct drm_connector_dp - DRM Connector DisplayPort capabilities
+ */
+struct drm_connector_dp_caps {
+ /**
+ * @forbid_lttpr_init - forbid LTTPR init and access, e.g. on platforms
+ * with AUX timeout < 3.2 ms.
+ */
+ bool forbid_lttpr_init;
+
+ /**
+ * @supported_rates - array of supported link rates, sorted in
+ * ascending order
+ */
+ u32 supported_rates[DP_MAX_SUPPORTED_RATES + 1];
+
+ /**
+ * @num_supported_rates - number of valied entries in the
+ * @supported_rates array
+ */
+ u32 num_supported_rates;
+
+ /**
+ * @dptx_lanes - number of lanes provided by DPTX
+ */
+ u32 dptx_lanes;
+};
+
+/**
+ * struct drm_connector_dp - DRM Connector DisplayPort-related structure
+ */
+struct drm_connector_dp {
+ /* field set by the driver before registering the connector */
+
+ struct drm_connector_dp_caps caps;
+
+ /**
+ * @aux - pointer to the DP AUX instance
+ */
+ struct drm_dp_aux *aux;
+
+ /*
+ * fields set by the drm_atomic_helper_connector_dp_detect(),
+ * protected by connection_mutex
+ */
+
+ /**
+ * @dpcd - cached DPCD registers
+ */
+ u8 dpcd[DP_RECEIVER_CAP_SIZE];
+
+ /**
+ * @edp - cached eDP panel version
+ */
+ u8 edp;
+
+ /**
+ * @lttpr_caps - LTTPR capabilities read from DPRX
+ */
+ u8 lttpr_caps[DP_LTTPR_COMMON_CAP_SIZE];
+
+ /**
+ * @lttpr_count - the count of LTTPRs that are present and initialized
+ * in a non-transparent mode
+ */
+ u32 lttpr_count;
+
+ /*
+ * fields set in drm_atomic_helper_connector_dp_hotplug, protected by FIXME
+ */
+
+ /**
+ * @dprx_lanes - number of lanes reported by DPRX
+ */
+ u32 dprx_lanes;
+
+ struct drm_connector_dp_rate_entry rate[DP_MAX_SUPPORTED_RATES];
+ int rate_count;
+};
+
/**
* struct drm_connector - central DRM connector control structure
*
@@ -2291,10 +2378,17 @@ struct drm_connector {
*/
struct llist_node free_node;
- /**
- * @hdmi: HDMI-related variable and properties.
- */
- struct drm_connector_hdmi hdmi;
+ union {
+ /**
+ * @hdmi: HDMI-related variable and properties.
+ */
+ struct drm_connector_hdmi hdmi;
+
+ /**
+ * @dp: DisplayPort-related variable and properties.
+ */
+ struct drm_connector_dp dp;
+ };
/**
* @hdmi_audio: HDMI codec properties and non-DRM state.
---
base-commit: 457f4393d02fdb612a93912fb09cef70e6e545c9
change-id: 20251001-drm-dp-connector-a82238bd5b81
Best regards,
--
With best wishes
Dmitry
© 2016 - 2025 Red Hat, Inc.