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 <[email protected]> --- 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
