[v3,3/7] drm: report dp downstream port type as a subconnector property
diff mbox series

Message ID 20190826132216.2823-4-oleg.vasilev@intel.com
State New
Headers show
Series
  • [v3,1/7] drm: move DP_MAX_DOWNSTREAM_PORTS from i915 to drm core
Related show

Commit Message

Oleg Vasilev Aug. 26, 2019, 1:22 p.m. UTC
Currently, downstream port type is only reported in debugfs. This
information should be considered important since it reflects the actual
physical connector type. Some userspace (e.g. window compositors)
may want to show this info to a user.

The 'subconnector' property is already utilized for DVI-I and TV-out for
reporting connector subtype.

The initial motivation for this feature came from i2c test [1].
It is supposed to be skipped on VGA connectors, but it cannot
detect VGA over DP and fails instead.

v2:
 - Ville: utilized drm_dp_is_branch()
 - Ville: implement DP 1.0 downstream type info
 - Replaced create_dp_properties with add_dp_subconnector_property
 - Added dp_set_subconnector_property helper

[1]: https://bugs.freedesktop.org/show_bug.cgi?id=104097

Reviewed-by: Emil Velikov <emil.velikov@collabora.com>
Signed-off-by: Oleg Vasilev <oleg.vasilev@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: intel-gfx@lists.freedesktop.org
---
 drivers/gpu/drm/drm_connector.c | 49 +++++++++++++++++++++++-
 drivers/gpu/drm/drm_dp_helper.c | 68 +++++++++++++++++++++++++++++++++
 include/drm/drm_connector.h     |  3 ++
 include/drm/drm_dp_helper.h     |  8 ++++
 include/drm/drm_mode_config.h   |  6 +++
 include/uapi/drm/drm_mode.h     | 22 +++++++----
 6 files changed, 146 insertions(+), 10 deletions(-)

Comments

Ville Syrjälä Aug. 28, 2019, 2:25 p.m. UTC | #1
On Mon, Aug 26, 2019 at 04:22:12PM +0300, Oleg Vasilev wrote:
> Currently, downstream port type is only reported in debugfs. This
> information should be considered important since it reflects the actual
> physical connector type. Some userspace (e.g. window compositors)
> may want to show this info to a user.
> 
> The 'subconnector' property is already utilized for DVI-I and TV-out for
> reporting connector subtype.
> 
> The initial motivation for this feature came from i2c test [1].
> It is supposed to be skipped on VGA connectors, but it cannot
> detect VGA over DP and fails instead.
> 
> v2:
>  - Ville: utilized drm_dp_is_branch()
>  - Ville: implement DP 1.0 downstream type info
>  - Replaced create_dp_properties with add_dp_subconnector_property
>  - Added dp_set_subconnector_property helper
> 
> [1]: https://bugs.freedesktop.org/show_bug.cgi?id=104097
> 
> Reviewed-by: Emil Velikov <emil.velikov@collabora.com>
> Signed-off-by: Oleg Vasilev <oleg.vasilev@intel.com>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Cc: intel-gfx@lists.freedesktop.org
> ---
>  drivers/gpu/drm/drm_connector.c | 49 +++++++++++++++++++++++-
>  drivers/gpu/drm/drm_dp_helper.c | 68 +++++++++++++++++++++++++++++++++
>  include/drm/drm_connector.h     |  3 ++
>  include/drm/drm_dp_helper.h     |  8 ++++
>  include/drm/drm_mode_config.h   |  6 +++
>  include/uapi/drm/drm_mode.h     | 22 +++++++----
>  6 files changed, 146 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
> index 4c766624b20d..47e3466891db 100644
> --- a/drivers/gpu/drm/drm_connector.c
> +++ b/drivers/gpu/drm/drm_connector.c
> @@ -829,7 +829,7 @@ static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = {
>  DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list)
>  
>  static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = {
> -	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
> +	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I, TV-out and DP */
>  	{ DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
>  	{ DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
>  };
> @@ -846,7 +846,7 @@ static const struct drm_prop_enum_list drm_tv_select_enum_list[] = {
>  DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list)
>  
>  static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = {
> -	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
> +	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I, TV-out and DP */
>  	{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
>  	{ DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
>  	{ DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
> @@ -855,6 +855,19 @@ static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = {
>  DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
>  		 drm_tv_subconnector_enum_list)
>  
> +static const struct drm_prop_enum_list drm_dp_subconnector_enum_list[] = {
> +	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I, TV-out and DP */
> +	{ DRM_MODE_SUBCONNECTOR_VGA,	   "VGA"       }, /* DP */
> +	{ DRM_MODE_SUBCONNECTOR_DVI,	   "DVI"       }, /* DP */
> +	{ DRM_MODE_SUBCONNECTOR_HDMI,	   "HDMI"      }, /* DP */
> +	{ DRM_MODE_SUBCONNECTOR_DP,        "DP"        }, /* DP */
> +	{ DRM_MODE_SUBCONNECTOR_Wireless,  "Wireless"  }, /* DP */
> +	{ DRM_MODE_SUBCONNECTOR_Native,	   "Native"    }, /* DP */
> +};
> +
> +DRM_ENUM_NAME_FN(drm_get_dp_subconnector_name,
> +		 drm_dp_subconnector_enum_list)
> +
>  static const struct drm_prop_enum_list hdmi_colorspaces[] = {
>  	/* For Default case, driver will set the colorspace */
>  	{ DRM_MODE_COLORIMETRY_DEFAULT, "Default" },
> @@ -1138,6 +1151,14 @@ static const struct drm_prop_enum_list hdmi_colorspaces[] = {
>   *	can also expose this property to external outputs, in which case they
>   *	must support "None", which should be the default (since external screens
>   *	have a built-in scaler).
> + *
> + * subconnector:
> + *	This property is used by DVI-I, TVout and DisplayPort to indicate different
> + *	connector subtypes. Enum values more or less match with those from main
> + *	connector types.
> + *	For DVI-I and TVout there is also a matching property "select subconnector"
> + *	allowing to switch between signal types.
> + *	DP subconnector corresponds to a downstream port.
>   */
>  
>  int drm_connector_create_standard_properties(struct drm_device *dev)
> @@ -1226,6 +1247,30 @@ int drm_mode_create_dvi_i_properties(struct drm_device *dev)
>  }
>  EXPORT_SYMBOL(drm_mode_create_dvi_i_properties);
>  
> +/**
> + * drm_mode_add_dp_subconnector_property - create subconnector property for DP
> + * @connector: drm_connector to attach property
> + *
> + * Called by a driver when DP connector is created.
> + */
> +void drm_mode_add_dp_subconnector_property(struct drm_connector *connector)
> +{
> +	struct drm_mode_config *mode_config = &connector->dev->mode_config;
> +
> +	if (!mode_config->dp_subconnector_property)
> +		mode_config->dp_subconnector_property =
> +			drm_property_create_enum(connector->dev,
> +				DRM_MODE_PROP_IMMUTABLE,
> +				"subconnector",
> +				drm_dp_subconnector_enum_list,
> +				ARRAY_SIZE(drm_dp_subconnector_enum_list));
> +
> +	drm_object_attach_property(&connector->base,
> +				   mode_config->dp_subconnector_property,
> +				   DRM_MODE_SUBCONNECTOR_Unknown);
> +}
> +EXPORT_SYMBOL(drm_mode_add_dp_subconnector_property);
> +
>  /**
>   * DOC: HDMI connector properties
>   *
> diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
> index 14320930091b..bca116cbc58f 100644
> --- a/drivers/gpu/drm/drm_dp_helper.c
> +++ b/drivers/gpu/drm/drm_dp_helper.c
> @@ -638,6 +638,74 @@ void drm_dp_downstream_debug(struct seq_file *m,
>  }
>  EXPORT_SYMBOL(drm_dp_downstream_debug);
>  
> +/**
> + * drm_dp_subconnector_type() - get DP branch device type
> + * @dpcd: DisplayPort configuration data
> + * @port_cap: port capabilities
> + *
> + */
> +enum drm_mode_subconnector
> +drm_dp_subconnector_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
> +			 const u8 port_cap[4])
> +{
> +	int type;
> +	bool branch_device_present = drm_dp_is_branch(dpcd);

Having a variable for this seems kinda pointless.

> +
> +	if (!branch_device_present)
> +		return DRM_MODE_SUBCONNECTOR_Native;
> +	/* DP 1.0 approach */
> +	if (dpcd[DP_DPCD_REV] == DP_DPCD_REV_10) {
> +		type = dpcd[DP_DOWNSTREAMPORT_PRESENT] &
> +		       DP_DWN_STRM_PORT_TYPE_MASK;
> +		if (type == DP_DWN_STRM_PORT_TYPE_DP)
> +			return DRM_MODE_SUBCONNECTOR_DP;

TMDS -> DVI maybe?
analog -> VGA maybe?

> +		return DRM_MODE_SUBCONNECTOR_Unknown;
> +	}
> +	type = port_cap[0] & DP_DS_PORT_TYPE_MASK;
> +
> +	switch (type) {
> +	case DP_DS_PORT_TYPE_DP:
> +	case DP_DS_PORT_TYPE_DP_DUALMODE:
> +		return DRM_MODE_SUBCONNECTOR_DP;
> +	case DP_DS_PORT_TYPE_VGA:
> +		return DRM_MODE_SUBCONNECTOR_VGA;
> +	case DP_DS_PORT_TYPE_DVI:
> +		return DRM_MODE_SUBCONNECTOR_DVI;
> +	case DP_DS_PORT_TYPE_HDMI:
> +		return DRM_MODE_SUBCONNECTOR_HDMI;
> +	case DP_DS_PORT_TYPE_WIRELESS:
> +		return DRM_MODE_SUBCONNECTOR_Wireless;
> +	case DP_DS_PORT_TYPE_NON_EDID:
> +	default:
> +		return DRM_MODE_SUBCONNECTOR_Unknown;
> +	}
> +}
> +EXPORT_SYMBOL(drm_dp_subconnector_type);
> +
> +/**
> + * drm_mode_set_dp_subconnector_property - set subconnector for DP connector
> + * @connector: dp connector to attach property
> + * @status: connector status which is about to be set
> + * @dpcd: DisplayPort configuration data
> + * @port_cap: port capabilities
> + *
> + * Called by a driver on every detect event.
> + */
> +void drm_dp_set_subconnector_property(struct drm_connector *connector,
> +				      enum drm_connector_status status,
> +				      const u8 *dpcd,
> +				      const u8 port_cap[4])
> +{
> +	enum drm_mode_subconnector subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
> +
> +	if (status == connector_status_connected)
> +		subconnector = drm_dp_subconnector_type(dpcd, port_cap);
> +	drm_object_property_set_value(&connector->base,
> +			connector->dev->mode_config.dp_subconnector_property,
> +			subconnector);
> +}
> +EXPORT_SYMBOL(drm_dp_set_subconnector_property);
> +
>  /*
>   * I2C-over-AUX implementation
>   */
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index 681cb590f952..119606fcd59f 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -1507,10 +1507,13 @@ const char *drm_get_dvi_i_subconnector_name(int val);
>  const char *drm_get_dvi_i_select_name(int val);
>  const char *drm_get_tv_subconnector_name(int val);
>  const char *drm_get_tv_select_name(int val);
> +const char *drm_get_dp_subconnector_name(int val);
>  const char *drm_get_content_protection_name(int val);
>  const char *drm_get_hdcp_content_type_name(int val);
>  
>  int drm_mode_create_dvi_i_properties(struct drm_device *dev);
> +void drm_mode_add_dp_subconnector_property(struct drm_connector *connector);
> +
>  int drm_mode_create_tv_margin_properties(struct drm_device *dev);
>  int drm_mode_create_tv_properties(struct drm_device *dev,
>  				  unsigned int num_modes,
> diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
> index 1aea3e0810db..1fec3c731017 100644
> --- a/include/drm/drm_dp_helper.h
> +++ b/include/drm/drm_dp_helper.h
> @@ -26,6 +26,7 @@
>  #include <linux/types.h>
>  #include <linux/i2c.h>
>  #include <linux/delay.h>
> +#include <drm/drm_connector.h>
>  
>  /*
>   * Unless otherwise noted, all values are from the DP 1.1a spec.  Note that
> @@ -1378,6 +1379,13 @@ int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
>  int drm_dp_downstream_id(struct drm_dp_aux *aux, char id[6]);
>  void drm_dp_downstream_debug(struct seq_file *m, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
>  			     const u8 port_cap[4], struct drm_dp_aux *aux);
> +enum drm_mode_subconnector
> +drm_dp_subconnector_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
> +			 const u8 port_cap[4]);
> +void drm_dp_set_subconnector_property(struct drm_connector *connector,
> +				      enum drm_connector_status status,
> +				      const u8 *dpcd,
> +				      const u8 port_cap[4]);
>  
>  void drm_dp_aux_init(struct drm_dp_aux *aux);
>  int drm_dp_aux_register(struct drm_dp_aux *aux);
> diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h
> index 3bcbe30339f0..732d51951bce 100644
> --- a/include/drm/drm_mode_config.h
> +++ b/include/drm/drm_mode_config.h
> @@ -680,6 +680,12 @@ struct drm_mode_config {
>  	 */
>  	struct drm_property *dvi_i_select_subconnector_property;
>  
> +	/**
> +	 * @dp_subconnector_property: Optional DP property to differentiate
> +	 * between different DP downstream port types.
> +	 */
> +	struct drm_property *dp_subconnector_property;
> +
>  	/**
>  	 * @tv_subconnector_property: Optional TV property to differentiate
>  	 * between different TV connector types.
> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
> index 735c8cfdaaa1..80e3118fee13 100644
> --- a/include/uapi/drm/drm_mode.h
> +++ b/include/uapi/drm/drm_mode.h
> @@ -332,14 +332,20 @@ struct drm_mode_get_encoder {
>  /* This is for connectors with multiple signal types. */
>  /* Try to match DRM_MODE_CONNECTOR_X as closely as possible. */
>  enum drm_mode_subconnector {
> -	DRM_MODE_SUBCONNECTOR_Automatic = 0,
> -	DRM_MODE_SUBCONNECTOR_Unknown = 0,
> -	DRM_MODE_SUBCONNECTOR_DVID = 3,
> -	DRM_MODE_SUBCONNECTOR_DVIA = 4,
> -	DRM_MODE_SUBCONNECTOR_Composite = 5,
> -	DRM_MODE_SUBCONNECTOR_SVIDEO = 6,
> -	DRM_MODE_SUBCONNECTOR_Component = 8,
> -	DRM_MODE_SUBCONNECTOR_SCART = 9,
> +	DRM_MODE_SUBCONNECTOR_Automatic  = 0,  /* DVI-I, TV     */
> +	DRM_MODE_SUBCONNECTOR_Unknown    = 0,  /* DVI-I, TV, DP */
> +	DRM_MODE_SUBCONNECTOR_VGA	 = 1,  /*            DP */
> +	DRM_MODE_SUBCONNECTOR_DVI	 = 2,  /*            DP */

I suspect the DP DFP TMDS/DVI stuff is always DVI-D. So maybe we
should just reuse that one?

> +	DRM_MODE_SUBCONNECTOR_DVID	 = 3,  /* DVI-I         */
> +	DRM_MODE_SUBCONNECTOR_DVIA	 = 4,  /* DVI-I         */
> +	DRM_MODE_SUBCONNECTOR_Composite  = 5,  /*        TV     */
> +	DRM_MODE_SUBCONNECTOR_SVIDEO	 = 6,  /*        TV     */
> +	DRM_MODE_SUBCONNECTOR_Component  = 8,  /*        TV     */
> +	DRM_MODE_SUBCONNECTOR_SCART	 = 9,  /*        TV     */
> +	DRM_MODE_SUBCONNECTOR_DP	 = 10, /*            DP */
> +	DRM_MODE_SUBCONNECTOR_HDMI       = 11, /*            DP */

Maybe we want to call it HDMIA just to match the connector counterpart?

> +	DRM_MODE_SUBCONNECTOR_Native     = 15, /*            DP */
> +	DRM_MODE_SUBCONNECTOR_Wireless   = 19, /*            DP */

Not sure what numbers we should assign these. The comment says
we're trying to match DRM_MODE_CONNECTOR_ here. Not sure why that's
needed though.

Reusing the WRITEBACK value for Wireless seems like a safe choice?

>  };
>  
>  #define DRM_MODE_CONNECTOR_Unknown	0
> -- 
> 2.23.0

Patch
diff mbox series

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 4c766624b20d..47e3466891db 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -829,7 +829,7 @@  static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = {
 DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list)
 
 static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = {
-	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
+	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I, TV-out and DP */
 	{ DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
 	{ DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
 };
@@ -846,7 +846,7 @@  static const struct drm_prop_enum_list drm_tv_select_enum_list[] = {
 DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list)
 
 static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = {
-	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
+	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I, TV-out and DP */
 	{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
 	{ DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
 	{ DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
@@ -855,6 +855,19 @@  static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = {
 DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
 		 drm_tv_subconnector_enum_list)
 
+static const struct drm_prop_enum_list drm_dp_subconnector_enum_list[] = {
+	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I, TV-out and DP */
+	{ DRM_MODE_SUBCONNECTOR_VGA,	   "VGA"       }, /* DP */
+	{ DRM_MODE_SUBCONNECTOR_DVI,	   "DVI"       }, /* DP */
+	{ DRM_MODE_SUBCONNECTOR_HDMI,	   "HDMI"      }, /* DP */
+	{ DRM_MODE_SUBCONNECTOR_DP,        "DP"        }, /* DP */
+	{ DRM_MODE_SUBCONNECTOR_Wireless,  "Wireless"  }, /* DP */
+	{ DRM_MODE_SUBCONNECTOR_Native,	   "Native"    }, /* DP */
+};
+
+DRM_ENUM_NAME_FN(drm_get_dp_subconnector_name,
+		 drm_dp_subconnector_enum_list)
+
 static const struct drm_prop_enum_list hdmi_colorspaces[] = {
 	/* For Default case, driver will set the colorspace */
 	{ DRM_MODE_COLORIMETRY_DEFAULT, "Default" },
@@ -1138,6 +1151,14 @@  static const struct drm_prop_enum_list hdmi_colorspaces[] = {
  *	can also expose this property to external outputs, in which case they
  *	must support "None", which should be the default (since external screens
  *	have a built-in scaler).
+ *
+ * subconnector:
+ *	This property is used by DVI-I, TVout and DisplayPort to indicate different
+ *	connector subtypes. Enum values more or less match with those from main
+ *	connector types.
+ *	For DVI-I and TVout there is also a matching property "select subconnector"
+ *	allowing to switch between signal types.
+ *	DP subconnector corresponds to a downstream port.
  */
 
 int drm_connector_create_standard_properties(struct drm_device *dev)
@@ -1226,6 +1247,30 @@  int drm_mode_create_dvi_i_properties(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_mode_create_dvi_i_properties);
 
+/**
+ * drm_mode_add_dp_subconnector_property - create subconnector property for DP
+ * @connector: drm_connector to attach property
+ *
+ * Called by a driver when DP connector is created.
+ */
+void drm_mode_add_dp_subconnector_property(struct drm_connector *connector)
+{
+	struct drm_mode_config *mode_config = &connector->dev->mode_config;
+
+	if (!mode_config->dp_subconnector_property)
+		mode_config->dp_subconnector_property =
+			drm_property_create_enum(connector->dev,
+				DRM_MODE_PROP_IMMUTABLE,
+				"subconnector",
+				drm_dp_subconnector_enum_list,
+				ARRAY_SIZE(drm_dp_subconnector_enum_list));
+
+	drm_object_attach_property(&connector->base,
+				   mode_config->dp_subconnector_property,
+				   DRM_MODE_SUBCONNECTOR_Unknown);
+}
+EXPORT_SYMBOL(drm_mode_add_dp_subconnector_property);
+
 /**
  * DOC: HDMI connector properties
  *
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
index 14320930091b..bca116cbc58f 100644
--- a/drivers/gpu/drm/drm_dp_helper.c
+++ b/drivers/gpu/drm/drm_dp_helper.c
@@ -638,6 +638,74 @@  void drm_dp_downstream_debug(struct seq_file *m,
 }
 EXPORT_SYMBOL(drm_dp_downstream_debug);
 
+/**
+ * drm_dp_subconnector_type() - get DP branch device type
+ * @dpcd: DisplayPort configuration data
+ * @port_cap: port capabilities
+ *
+ */
+enum drm_mode_subconnector
+drm_dp_subconnector_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+			 const u8 port_cap[4])
+{
+	int type;
+	bool branch_device_present = drm_dp_is_branch(dpcd);
+
+	if (!branch_device_present)
+		return DRM_MODE_SUBCONNECTOR_Native;
+	/* DP 1.0 approach */
+	if (dpcd[DP_DPCD_REV] == DP_DPCD_REV_10) {
+		type = dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+		       DP_DWN_STRM_PORT_TYPE_MASK;
+		if (type == DP_DWN_STRM_PORT_TYPE_DP)
+			return DRM_MODE_SUBCONNECTOR_DP;
+		return DRM_MODE_SUBCONNECTOR_Unknown;
+	}
+	type = port_cap[0] & DP_DS_PORT_TYPE_MASK;
+
+	switch (type) {
+	case DP_DS_PORT_TYPE_DP:
+	case DP_DS_PORT_TYPE_DP_DUALMODE:
+		return DRM_MODE_SUBCONNECTOR_DP;
+	case DP_DS_PORT_TYPE_VGA:
+		return DRM_MODE_SUBCONNECTOR_VGA;
+	case DP_DS_PORT_TYPE_DVI:
+		return DRM_MODE_SUBCONNECTOR_DVI;
+	case DP_DS_PORT_TYPE_HDMI:
+		return DRM_MODE_SUBCONNECTOR_HDMI;
+	case DP_DS_PORT_TYPE_WIRELESS:
+		return DRM_MODE_SUBCONNECTOR_Wireless;
+	case DP_DS_PORT_TYPE_NON_EDID:
+	default:
+		return DRM_MODE_SUBCONNECTOR_Unknown;
+	}
+}
+EXPORT_SYMBOL(drm_dp_subconnector_type);
+
+/**
+ * drm_mode_set_dp_subconnector_property - set subconnector for DP connector
+ * @connector: dp connector to attach property
+ * @status: connector status which is about to be set
+ * @dpcd: DisplayPort configuration data
+ * @port_cap: port capabilities
+ *
+ * Called by a driver on every detect event.
+ */
+void drm_dp_set_subconnector_property(struct drm_connector *connector,
+				      enum drm_connector_status status,
+				      const u8 *dpcd,
+				      const u8 port_cap[4])
+{
+	enum drm_mode_subconnector subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
+
+	if (status == connector_status_connected)
+		subconnector = drm_dp_subconnector_type(dpcd, port_cap);
+	drm_object_property_set_value(&connector->base,
+			connector->dev->mode_config.dp_subconnector_property,
+			subconnector);
+}
+EXPORT_SYMBOL(drm_dp_set_subconnector_property);
+
 /*
  * I2C-over-AUX implementation
  */
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 681cb590f952..119606fcd59f 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1507,10 +1507,13 @@  const char *drm_get_dvi_i_subconnector_name(int val);
 const char *drm_get_dvi_i_select_name(int val);
 const char *drm_get_tv_subconnector_name(int val);
 const char *drm_get_tv_select_name(int val);
+const char *drm_get_dp_subconnector_name(int val);
 const char *drm_get_content_protection_name(int val);
 const char *drm_get_hdcp_content_type_name(int val);
 
 int drm_mode_create_dvi_i_properties(struct drm_device *dev);
+void drm_mode_add_dp_subconnector_property(struct drm_connector *connector);
+
 int drm_mode_create_tv_margin_properties(struct drm_device *dev);
 int drm_mode_create_tv_properties(struct drm_device *dev,
 				  unsigned int num_modes,
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index 1aea3e0810db..1fec3c731017 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -26,6 +26,7 @@ 
 #include <linux/types.h>
 #include <linux/i2c.h>
 #include <linux/delay.h>
+#include <drm/drm_connector.h>
 
 /*
  * Unless otherwise noted, all values are from the DP 1.1a spec.  Note that
@@ -1378,6 +1379,13 @@  int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
 int drm_dp_downstream_id(struct drm_dp_aux *aux, char id[6]);
 void drm_dp_downstream_debug(struct seq_file *m, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
 			     const u8 port_cap[4], struct drm_dp_aux *aux);
+enum drm_mode_subconnector
+drm_dp_subconnector_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+			 const u8 port_cap[4]);
+void drm_dp_set_subconnector_property(struct drm_connector *connector,
+				      enum drm_connector_status status,
+				      const u8 *dpcd,
+				      const u8 port_cap[4]);
 
 void drm_dp_aux_init(struct drm_dp_aux *aux);
 int drm_dp_aux_register(struct drm_dp_aux *aux);
diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h
index 3bcbe30339f0..732d51951bce 100644
--- a/include/drm/drm_mode_config.h
+++ b/include/drm/drm_mode_config.h
@@ -680,6 +680,12 @@  struct drm_mode_config {
 	 */
 	struct drm_property *dvi_i_select_subconnector_property;
 
+	/**
+	 * @dp_subconnector_property: Optional DP property to differentiate
+	 * between different DP downstream port types.
+	 */
+	struct drm_property *dp_subconnector_property;
+
 	/**
 	 * @tv_subconnector_property: Optional TV property to differentiate
 	 * between different TV connector types.
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index 735c8cfdaaa1..80e3118fee13 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -332,14 +332,20 @@  struct drm_mode_get_encoder {
 /* This is for connectors with multiple signal types. */
 /* Try to match DRM_MODE_CONNECTOR_X as closely as possible. */
 enum drm_mode_subconnector {
-	DRM_MODE_SUBCONNECTOR_Automatic = 0,
-	DRM_MODE_SUBCONNECTOR_Unknown = 0,
-	DRM_MODE_SUBCONNECTOR_DVID = 3,
-	DRM_MODE_SUBCONNECTOR_DVIA = 4,
-	DRM_MODE_SUBCONNECTOR_Composite = 5,
-	DRM_MODE_SUBCONNECTOR_SVIDEO = 6,
-	DRM_MODE_SUBCONNECTOR_Component = 8,
-	DRM_MODE_SUBCONNECTOR_SCART = 9,
+	DRM_MODE_SUBCONNECTOR_Automatic  = 0,  /* DVI-I, TV     */
+	DRM_MODE_SUBCONNECTOR_Unknown    = 0,  /* DVI-I, TV, DP */
+	DRM_MODE_SUBCONNECTOR_VGA	 = 1,  /*            DP */
+	DRM_MODE_SUBCONNECTOR_DVI	 = 2,  /*            DP */
+	DRM_MODE_SUBCONNECTOR_DVID	 = 3,  /* DVI-I         */
+	DRM_MODE_SUBCONNECTOR_DVIA	 = 4,  /* DVI-I         */
+	DRM_MODE_SUBCONNECTOR_Composite  = 5,  /*        TV     */
+	DRM_MODE_SUBCONNECTOR_SVIDEO	 = 6,  /*        TV     */
+	DRM_MODE_SUBCONNECTOR_Component  = 8,  /*        TV     */
+	DRM_MODE_SUBCONNECTOR_SCART	 = 9,  /*        TV     */
+	DRM_MODE_SUBCONNECTOR_DP	 = 10, /*            DP */
+	DRM_MODE_SUBCONNECTOR_HDMI       = 11, /*            DP */
+	DRM_MODE_SUBCONNECTOR_Native     = 15, /*            DP */
+	DRM_MODE_SUBCONNECTOR_Wireless   = 19, /*            DP */
 };
 
 #define DRM_MODE_CONNECTOR_Unknown	0