Skip to contentSkip to conversation

DisplayPort MST v0.3

RFC In review Dave Airlie 5 participants 23 messages May 12, 2014 – May 14, 2014

Subsystems (4)
lore
Share & tools
lore

Dave Airlie

author maintainer
#0 lore raw

Hi,

A repost of the current state of the displayport MST support for i915, mainly targetted the Lenovo docks.

Major changes since last posting:
add a path blob property for userspace to use to track topology provide reference counting, locking and lookups for branch and port structs. some DocBook!
fix i915 problems with DPMS - ordering of link bring up.
aux channel locking - this should be pushed down I think into the aux helpers,

TODO:
fbcon support:
fix non 4 lane 5.4Ghz.
fix more of state checker harder.

Dave.

author maintainer
#1 lore raw

PATCH 1/10 drm/dp_helper: add defines for DP 1.2 and MST support.

From: Dave Airlie <[email protected]>

This just adds the defines from the DP 1.2 spec, which we will use later.

---
 include/drm/drm_dp_helper.h | 78 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)
include/drm/drm_dp_helper.h +78 -0
index cfcacec..879836d 100644
@@ -37,6 +37,7 @@
  * eDP: Embedded DisplayPort version 1
  * DPI: DisplayPort Interoperability Guideline v1.1a
  * 1.2: DisplayPort 1.2
+ * MST: Multistream Transport - part of DP 1.2a
  *
  * 1.2 formally includes both eDP and DPI definitions.
  */
@@ -103,9 +104,14 @@
 #define DP_TRAINING_AUX_RD_INTERVAL         0x00e   /* XXX 1.2? */
 
 /* Multiple stream transport */
+#define DP_FAUX_CAP			    0x020   /* 1.2 */
+# define DP_FAUX_CAP_1			    (1 << 0)
+
 #define DP_MSTM_CAP			    0x021   /* 1.2 */
 # define DP_MST_CAP			    (1 << 0)
 
+#define DP_GUID				    0x030   /* 1.2 */
+
 #define DP_PSR_SUPPORT                      0x070   /* XXX 1.2? */
 # define DP_PSR_IS_SUPPORTED                1
 #define DP_PSR_CAPS                         0x071   /* XXX 1.2? */
@@ -221,6 +227,16 @@
 # define DP_PSR_CRC_VERIFICATION	    (1 << 2)
 # define DP_PSR_FRAME_CAPTURE		    (1 << 3)
 
+#define DP_ADAPTER_CTRL			    0x1a0
+# define DP_ADAPTER_CTRL_FORCE_LOAD_SENSE   (1 << 0)
+
+#define DP_BRANCH_DEVICE_CTRL		    0x1a1
+# define DP_BRANCH_DEVICE_IRQ_HPD	    (1 << 0)
+
+#define DP_PAYLOAD_ALLOCATE_SET		    0x1c0
+#define DP_PAYLOAD_ALLOCATE_START_TIME_SLOT 0x1c1
+#define DP_PAYLOAD_ALLOCATE_TIME_SLOT_COUNT 0x1c2
+
 #define DP_SINK_COUNT			    0x200
 /* prior to 1.2 bit 7 was reserved mbz */
 # define DP_GET_SINK_COUNT(x)		    ((((x) & 0x80) >> 1) | ((x) & 0x3f))
@@ -230,6 +246,9 @@
 # define DP_REMOTE_CONTROL_COMMAND_PENDING  (1 << 0)
 # define DP_AUTOMATED_TEST_REQUEST	    (1 << 1)
 # define DP_CP_IRQ			    (1 << 2)
+# define DP_MCCS_IRQ			    (1 << 3)
+# define DP_DOWN_REP_MSG_RDY		    (1 << 4) /* DP MST */
+# define DP_UP_REQ_MSG_RDY		    (1 << 5) /* DP MST */
 # define DP_SINK_SPECIFIC_IRQ		    (1 << 6)
 
 #define DP_LANE0_1_STATUS		    0x202
@@ -294,6 +313,13 @@
 #define DP_TEST_SINK			    0x270
 #define DP_TEST_SINK_START	    (1 << 0)
 
+#define DP_PAYLOAD_TABLE_UPDATE_STATUS      0x2c0   /* 1.2 MST */
+# define DP_PAYLOAD_TABLE_UPDATED           (1 << 0)
+# define DP_PAYLOAD_ACT_HANDLED             (1 << 1)
+
+#define DP_VC_PAYLOAD_ID_SLOT_1             0x2c1   /* 1.2 MST */
+/* up to ID_SLOT_63 at 0x2ff */
+
 #define DP_SOURCE_OUI			    0x300
 #define DP_SINK_OUI			    0x400
 #define DP_BRANCH_OUI			    0x500
@@ -303,6 +329,21 @@
 # define DP_SET_POWER_D3                    0x2
 # define DP_SET_POWER_MASK                  0x3
 
+#define DP_SIDEBAND_MSG_DOWN_REQ_BASE	    0x1000   /* 1.2 MST */
+#define DP_SIDEBAND_MSG_UP_REP_BASE	    0x1200   /* 1.2 MST */
+#define DP_SIDEBAND_MSG_DOWN_REP_BASE	    0x1400   /* 1.2 MST */
+#define DP_SIDEBAND_MSG_UP_REQ_BASE	    0x1600   /* 1.2 MST */
+
+#define DP_SINK_COUNT_ESI		    0x2002   /* 1.2 */
+/* 0-5 sink count */
+# define DP_SINK_COUNT_CP_READY             (1 << 6)
+
+#define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0   0x2003   /* 1.2 */
+
+#define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1   0x2004   /* 1.2 */
+
+#define DP_LINK_SERVICE_IRQ_VECTOR_ESI0     0x2005   /* 1.2 */
+
 #define DP_PSR_ERROR_STATUS                 0x2006  /* XXX 1.2? */
 # define DP_PSR_LINK_CRC_ERROR              (1 << 0)
 # define DP_PSR_RFB_STORAGE_ERROR           (1 << 1)
@@ -319,6 +360,43 @@
 # define DP_PSR_SINK_INTERNAL_ERROR         7
 # define DP_PSR_SINK_STATE_MASK             0x07
 
+/* DP 1.2 Sideband message defines */
+/* peer device type - DP 1.2a Table 2-92 */
+#define DP_PEER_DEVICE_NONE		0x0
+#define DP_PEER_DEVICE_SOURCE_OR_SST	0x1
+#define DP_PEER_DEVICE_MST_BRANCHING	0x2
+#define DP_PEER_DEVICE_SST_SINK		0x3
+#define DP_PEER_DEVICE_DP_LEGACY_CONV	0x4
+
+/* DP 1.2 MST sideband request names DP 1.2a Table 2-80 */
+#define DP_LINK_ADDRESS			0x01
+#define DP_CONNECTION_STATUS_NOTIFY	0x02
+#define DP_ENUM_PATH_RESOURCES		0x10
+#define DP_ALLOCATE_PAYLOAD		0x11
+#define DP_QUERY_PAYLOAD		0x12
+#define DP_RESOURCE_STATUS_NOTIFY	0x13
+#define DP_CLEAR_PAYLOAD_ID_TABLE	0x14
+#define DP_REMOTE_DPCD_READ		0x20
+#define DP_REMOTE_DPCD_WRITE		0x21
+#define DP_REMOTE_I2C_READ		0x22
+#define DP_REMOTE_I2C_WRITE		0x23
+#define DP_POWER_UP_PHY			0x24
+#define DP_POWER_DOWN_PHY		0x25
+#define DP_SINK_EVENT_NOTIFY		0x30
+#define DP_QUERY_STREAM_ENC_STATUS	0x38
+
+/* DP 1.2 MST sideband nak reasons - table 2.84 */
+#define DP_NAK_WRITE_FAILURE		0x01
+#define DP_NAK_INVALID_READ		0x02
+#define DP_NAK_CRC_FAILURE		0x03
+#define DP_NAK_BAD_PARAM		0x04
+#define DP_NAK_DEFER			0x05
+#define DP_NAK_LINK_FAILURE		0x06
+#define DP_NAK_NO_RESOURCES		0x07
+#define DP_NAK_DPCD_FAIL		0x08
+#define DP_NAK_I2C_NAK			0x09
+#define DP_NAK_ALLOCATE_FAIL		0x0a
+
 #define MODE_I2C_START	1
 #define MODE_I2C_WRITE	2
 #define MODE_I2C_READ	4
Signature · 1 line
1.9.0
#2 lore raw

Re: [PATCH 01/10] drm/dp_helper: add defines for DP 1.2 and MST support.

Quoted 23 lines · Dave Airlie

From: Dave Airlie <[email protected]>

This just adds the defines from the DP 1.2 spec, which we will use later.

---
 include/drm/drm_dp_helper.h | 78 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)
include/drm/drm_dp_helper.h +1 -0
index cfcacec..879836d 100644
@@ -37,6 +37,7 @@
  * eDP: Embedded DisplayPort version 1
  * DPI: DisplayPort Interoperability Guideline v1.1a
  * 1.2: DisplayPort 1.2
+ * MST: Multistream Transport - part of DP 1.2a
  *
  * 1.2 formally includes both eDP and DPI definitions.
  */

[.....]

Quoted 3 lines
include/drm/drm_dp_helper.h +3 -0
+# define DP_MCCS_IRQ			    (1 << 3)
+# define DP_DOWN_REP_MSG_RDY		    (1 << 4) /* DP MST */
+# define DP_UP_REQ_MSG_RDY		    (1 << 5) /* DP MST */

[.....]

Quoted 2 lines

+#define DP_PAYLOAD_TABLE_UPDATE_STATUS 0x2c0 /* 1.2 MST */

Hi Dave Airlie,

What is the difference between 'DP MST' and '1.2 MST'? If there is no difference, would you use the same comment as below?

+# define DP_DOWN_REP_MSG_RDY (1 << 4) /* 1.2 MST */
+# define DP_UP_REQ_MSG_RDY (1 << 5) /* 1.2 MST */

Best regards,
Jingoo Han

[.....]

author maintainer
#3 lore raw

PATCH 2/10 drm: add DP MST encoder type

In reply to Dave Airlie

From: Dave Airlie <[email protected]>

This adds an encoder type for DP MST encoders.

---
 drivers/gpu/drm/drm_crtc.c  | 1 +
 include/uapi/drm/drm_mode.h | 1 +
 2 files changed, 2 insertions(+)
drivers/gpu/drm/drm_crtc.c +1 -0
index a3fe324..f1753e6 100644
@@ -227,6 +227,7 @@ static const struct drm_prop_enum_list drm_encoder_enum_list[] =
 	{ DRM_MODE_ENCODER_TVDAC, "TV" },
 	{ DRM_MODE_ENCODER_VIRTUAL, "Virtual" },
 	{ DRM_MODE_ENCODER_DSI, "DSI" },
+	{ DRM_MODE_ENCODER_DPMST, "DP MST" },
 };
 
 static const struct drm_prop_enum_list drm_subpixel_enum_list[] =
include/uapi/drm/drm_mode.h +1 -0
index f104c26..719add4 100644
@@ -181,6 +181,7 @@ struct drm_mode_get_plane_res {
 #define DRM_MODE_ENCODER_TVDAC	4
 #define DRM_MODE_ENCODER_VIRTUAL 5
 #define DRM_MODE_ENCODER_DSI	6
+#define DRM_MODE_ENCODER_DPMST	7
 
 struct drm_mode_get_encoder {
 	__u32 encoder_id;
Signature · 1 line
1.9.0
author maintainer
#4 lore raw

PATCH 3/10 drm/i915: add some registers need for displayport MST support.

In reply to Dave Airlie

From: Dave Airlie <[email protected]>

These are just from the Haswell spec.

---
 drivers/gpu/drm/i915/i915_reg.h | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
drivers/gpu/drm/i915/i915_reg.h +9 -2
index 8f84555..557b37a 100644
@@ -5386,6 +5386,7 @@ enum punit_power_well {
 #define  TRANS_DDI_EDP_INPUT_A_ONOFF	(4<<12)
 #define  TRANS_DDI_EDP_INPUT_B_ONOFF	(5<<12)
 #define  TRANS_DDI_EDP_INPUT_C_ONOFF	(6<<12)
+#define  TRANS_DDI_DP_VC_PAYLOAD_ALLOC	(1<<8)
 #define  TRANS_DDI_BFI_ENABLE		(1<<4)
 
 /* DisplayPort Transport Control */
@@ -5395,6 +5396,7 @@ enum punit_power_well {
 #define  DP_TP_CTL_ENABLE			(1<<31)
 #define  DP_TP_CTL_MODE_SST			(0<<27)
 #define  DP_TP_CTL_MODE_MST			(1<<27)
+#define  DP_TP_CTL_FORCE_ACT			(1<<25)
 #define  DP_TP_CTL_ENHANCED_FRAME_ENABLE	(1<<18)
 #define  DP_TP_CTL_FDI_AUTOTRAIN		(1<<15)
 #define  DP_TP_CTL_LINK_TRAIN_MASK		(7<<8)
@@ -5409,8 +5411,13 @@ enum punit_power_well {
 #define DP_TP_STATUS_A			0x64044
 #define DP_TP_STATUS_B			0x64144
 #define DP_TP_STATUS(port) _PORT(port, DP_TP_STATUS_A, DP_TP_STATUS_B)
-#define  DP_TP_STATUS_IDLE_DONE		(1<<25)
-#define  DP_TP_STATUS_AUTOTRAIN_DONE	(1<<12)
+#define  DP_TP_STATUS_IDLE_DONE			(1<<25)
+#define  DP_TP_STATUS_ACT_SENT			(1<<24)
+#define  DP_TP_STATUS_MODE_STATUS_MST		(1<<23)
+#define  DP_TP_STATUS_AUTOTRAIN_DONE		(1<<12)
+#define  DP_TP_STATUS_PAYLOAD_MAPPING_VC2	(3 << 8)
+#define  DP_TP_STATUS_PAYLOAD_MAPPING_VC1	(3 << 4)
+#define  DP_TP_STATUS_PAYLOAD_MAPPING_VC0	(3 << 0)
 
 /* DDI Buffer Control */
 #define DDI_BUF_CTL_A				0x64000
Signature · 1 line
1.9.0
author maintainer
#5 lore raw

PATCH 4/10 drm/crtc: add interface to reinitialise the legacy mode group

In reply to Dave Airlie

From: Dave Airlie <[email protected]>

This can be called to update things after dynamic connectors/encoders are created/deleted.

---
 drivers/gpu/drm/drm_crtc.c | 9 +++++++++
 include/drm/drm_crtc.h     | 1 +
 2 files changed, 10 insertions(+)
drivers/gpu/drm/drm_crtc.c +9 -0
index f1753e6..8bf87a6 100644
@@ -1421,6 +1421,15 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
 
+void drm_reinit_primary_mode_group(struct drm_device *dev)
+{
+	drm_modeset_lock_all(dev);
+	drm_mode_group_destroy(&dev->primary->mode_group);
+	drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group);
+	drm_modeset_unlock_all(dev);
+}
+EXPORT_SYMBOL(drm_reinit_primary_mode_group);
+
 /**
  * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo
  * @out: drm_mode_modeinfo struct to return to the user
include/drm/drm_crtc.h +1 -0
index c6b9e8a..55bc523 100644
@@ -916,6 +916,7 @@ extern const char *drm_get_tv_select_name(int val);
 extern void drm_fb_release(struct drm_file *file_priv);
 extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group);
 extern void drm_mode_group_destroy(struct drm_mode_group *group);
+extern void drm_reinit_primary_mode_group(struct drm_device *dev);
 extern bool drm_probe_ddc(struct i2c_adapter *adapter);
 extern struct edid *drm_get_edid(struct drm_connector *connector,
 				 struct i2c_adapter *adapter);
Signature · 1 line
1.9.0
#6 lore raw

Re: [PATCH 04/10] drm/crtc: add interface to reinitialise the legacy mode group

Quoted 36 lines · Dave Airlie

From: Dave Airlie <[email protected]>

This can be called to update things after dynamic connectors/encoders are created/deleted.

---
 drivers/gpu/drm/drm_crtc.c | 9 +++++++++
 include/drm/drm_crtc.h     | 1 +
 2 files changed, 10 insertions(+)
drivers/gpu/drm/drm_crtc.c +9 -0
index f1753e6..8bf87a6 100644
@@ -1421,6 +1421,15 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
 
+void drm_reinit_primary_mode_group(struct drm_device *dev)
+{
+	drm_modeset_lock_all(dev);
+	drm_mode_group_destroy(&dev->primary->mode_group);
+	drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group);
+	drm_modeset_unlock_all(dev);
+}
+EXPORT_SYMBOL(drm_reinit_primary_mode_group);
+
 /**
  * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo
  * @out: drm_mode_modeinfo struct to return to the user
include/drm/drm_crtc.h +0 -0
index c6b9e8a..55bc523 100644

include/drm/drm_crtc.h extern const char *drm_get_tv_select_name(int val); · line 919

Quoted 4 lines
include/drm/drm_crtc.h +1 -0
@@ -916,6 +916,7 @@ extern const char *drm_get_tv_select_name(int val);
 extern void drm_fb_release(struct drm_file *file_priv);
 extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group);
 extern void drm_mode_group_destroy(struct drm_mode_group *group);
+extern void drm_reinit_primary_mode_group(struct drm_device *dev);

There was a patch series once to remove all usage of extern from prototypes. Was there an official decision against that which I missed, or did it just fall off the table?

Thierry

_______________________________________________
dri-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/dri-devel

#7 lore raw

Re: [PATCH 04/10] drm/crtc: add interface to reinitialise the legacy mode group

Quoted 37 lines · Thierry Reding
Quoted 36 lines · Dave Airlie

From: Dave Airlie <[email protected]>

This can be called to update things after dynamic connectors/encoders are created/deleted.

---
 drivers/gpu/drm/drm_crtc.c | 9 +++++++++
 include/drm/drm_crtc.h     | 1 +
 2 files changed, 10 insertions(+)
drivers/gpu/drm/drm_crtc.c +9 -0
index f1753e6..8bf87a6 100644
@@ -1421,6 +1421,15 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
 
+void drm_reinit_primary_mode_group(struct drm_device *dev)
+{
+	drm_modeset_lock_all(dev);
+	drm_mode_group_destroy(&dev->primary->mode_group);
+	drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group);
+	drm_modeset_unlock_all(dev);
+}
+EXPORT_SYMBOL(drm_reinit_primary_mode_group);
+
 /**
  * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo
  * @out: drm_mode_modeinfo struct to return to the user
include/drm/drm_crtc.h +0 -0
index c6b9e8a..55bc523 100644
@@ -916,6 +916,7 @@ extern const char *drm_get_tv_select_name(int val);
Quoted 8 lines

include/drm/drm_crtc.h extern const char *drm_get_tv_select_name(int val); · line 919

Quoted 4 lines
include/drm/drm_crtc.h +1 -0
 extern void drm_fb_release(struct drm_file *file_priv);
 extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group);
 extern void drm_mode_group_destroy(struct drm_mode_group *group);
+extern void drm_reinit_primary_mode_group(struct drm_device *dev);

There was a patch series once to remove all usage of extern from prototypes. Was there an official decision against that which I missed, or did it just fall off the table?

Iirc I've done that for radeon or so and we decided that it's a bikeshed. tbh I don't care, but in i915 I tend to rip them out since redundant. -Daniel

Signature · 3 lines
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
author maintainer
#8 lore raw

PATCH 5/10 drm: add a path blob property

In reply to Dave Airlie

From: Dave Airlie <[email protected]>

This property will be used by the MST code to provide userspace with a path to parse so it can recognise connectors around hotplugs.

---
 drivers/gpu/drm/drm_crtc.c | 26 ++++++++++++++++++++++++++
 include/drm/drm_crtc.h     |  5 +++++
 2 files changed, 31 insertions(+)
drivers/gpu/drm/drm_crtc.c +26 -0
index 8bf87a6..06b9255 100644
@@ -1165,6 +1165,7 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
 {
 	struct drm_property *edid;
 	struct drm_property *dpms;
+	struct drm_property *dev_path;
 
 	/*
 	 * Standard properties (apply to all connectors)
@@ -1179,6 +1180,12 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
 				   ARRAY_SIZE(drm_dpms_enum_list));
 	dev->mode_config.dpms_property = dpms;
 
+	dev_path = drm_property_create(dev,
+				       DRM_MODE_PROP_BLOB |
+				       DRM_MODE_PROP_IMMUTABLE,
+				       "PATH", 0);
+	dev->mode_config.path_property = dev_path;
+
 	return 0;
 }
 
@@ -3637,6 +3644,25 @@ done:
 	return ret;
 }
 
+int drm_mode_connector_set_path_property(struct drm_connector *connector,
+					 char *path)
+{
+	struct drm_device *dev = connector->dev;
+	int ret, size;
+	size = strlen(path) + 1;
+
+	connector->path_blob_ptr = drm_property_create_blob(connector->dev,
+							    size, path);
+	if (!connector->path_blob_ptr)
+		return -EINVAL;
+
+	ret = drm_object_property_set_value(&connector->base,
+					    dev->mode_config.path_property,
+					    connector->path_blob_ptr->base.id);
+	return ret;
+}
+EXPORT_SYMBOL(drm_mode_connector_set_path_property);
+
 /**
  * drm_mode_connector_update_edid_property - update the edid property of a connector
  * @connector: drm connector
include/drm/drm_crtc.h +5 -0
index 55bc523..e33959b 100644
@@ -500,6 +500,8 @@ struct drm_connector {
 	struct drm_property_blob *edid_blob_ptr;
 	struct drm_object_properties properties;
 
+	struct drm_property_blob *path_blob_ptr;
+
 	uint8_t polled; /* DRM_CONNECTOR_POLL_* */
 
 	/* requested DPMS state */
@@ -774,6 +776,7 @@ struct drm_mode_config {
 	struct list_head property_blob_list;
 	struct drm_property *edid_property;
 	struct drm_property *dpms_property;
+	struct drm_property *path_property;
 	struct drm_property *plane_type_property;
 
 	/* DVI-I properties */
@@ -926,6 +929,8 @@ extern void drm_mode_config_init(struct drm_device *dev);
 extern void drm_mode_config_reset(struct drm_device *dev);
 extern void drm_mode_config_cleanup(struct drm_device *dev);
 
+extern int drm_mode_connector_set_path_property(struct drm_connector *connector,
+						char *path);
 extern int drm_mode_connector_update_edid_property(struct drm_connector *connector,
 						struct edid *edid);
 extern int drm_object_property_set_value(struct drm_mode_object *obj,
Signature · 1 line
1.9.0
#9 lore raw

Re: [PATCH 05/10] drm: add a path blob property

Quoted 10 lines · Dave Airlie

From: Dave Airlie <[email protected]>

This property will be used by the MST code to provide userspace with a path to parse so it can recognise connectors around hotplugs.

---
 drivers/gpu/drm/drm_crtc.c | 26 ++++++++++++++++++++++++++
 include/drm/drm_crtc.h     |  5 +++++
 2 files changed, 31 insertions(+)

The documentation for this property is missing. =)

Quoted 1 line
drivers/gpu/drm/drm_crtc.c +0 -0

[...]

drivers/gpu/drm/drm_crtc.c done: · line 3648

Quoted 2 lines
drivers/gpu/drm/drm_crtc.c +2 -0
+int drm_mode_connector_set_path_property(struct drm_connector *connector,
+					 char *path)

"const char *path"?

drivers/gpu/drm/drm_crtc.c done: · line 3652

Quoted 4 lines
drivers/gpu/drm/drm_crtc.c +4 -0
+{
+	struct drm_device *dev = connector->dev;
+	int ret, size;
+	size = strlen(path) + 1;

At the risk of sounding pedantic: strlen() returns size_t, not int. But I see that drm_property_create_blob() uses int as the type for the length argument, so there's an inconsistency either way. I'll go prepare a patch to fix that inconsistency and see if anybody else is equally pedantic and willing to apply it.

Thierry

_______________________________________________
Intel-gfx mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

author maintainer
#10 lore raw

PATCH 6/10 drm/helper: add Displayport multi-stream helper (v0.3)

In reply to Dave Airlie

From: Dave Airlie <[email protected]>

This is the initial import of the helper for displayport multistream.

It consists of a topology manager, init/destroy/set mst state

It supports DP 1.2 MST sideband msg protocol handler - via hpd irqs

connector detect and edid retrieval interface.

It supports i2c device over DP 1.2 sideband msg protocol (EDID reads only)

bandwidth manager API via vcpi allocation and payload updating, along with a helper to check the ACT status.

Objects:
MST topology manager - one per toplevel MST capable GPU port - not sure if this should be higher level again MST branch unit - one instance per plugged branching unit - one at top of hierarchy - others hanging from ports MST port - one port per port reported by branching units, can have MST units hanging from them as well.

Changes since initial posting:
a) add a mutex responsbile for the queues, it locks the sideband and msg slots, and msgs to transmit state
b) add worker to handle connection state change events, for MST device chaining and hotplug
c) add a payload spinlock
d) add path sideband msg support
e) fixup enum path resources transmit
f) reduce max dpcd msg to 16, as per DP1.2 spec.
g) separate tx queue kicking from irq processing and move irq acking back to drivers.

Changes since v0.2:
a) reorganise code,
b) drop ACT forcing code
c) add connector naming interface using path property
d) add topology dumper helper
e) proper reference counting and lookup for ports and mstbs.
f) move tx kicking into a workq
g) add aux locking - this should be redone
h) split teardown into two parts
i) start working on documentation on interface.

TODO:
misc features

---
 Documentation/DocBook/drm.tmpl        |    6 +
 drivers/gpu/drm/Makefile              |    2 +-
 drivers/gpu/drm/drm_dp_mst_topology.c | 2536 +++++++++++++++++++++++++++++++++
 include/drm/drm_dp_mst_helper.h       |  503 +++++++
 4 files changed, 3046 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/drm_dp_mst_topology.c
 create mode 100644 include/drm/drm_dp_mst_helper.h
Documentation/DocBook/drm.tmpl +6 -0
index 83dd0b0..1883976 100644
@@ -2296,6 +2296,12 @@ void intel_crt_init(struct drm_device *dev)
 !Edrivers/gpu/drm/drm_dp_helper.c
     </sect2>
     <sect2>
+      <title>Display Port MST Helper Functions Reference</title>
+!Pdrivers/gpu/drm/drm_dp_mst_topology.c dp mst helper
+!Iinclude/drm/drm_dp_mst_helper.h
+!Edrivers/gpu/drm/drm_dp_mst_topology.c
+    </sect2>
+    <sect2>
       <title>EDID Helper Functions Reference</title>
 !Edrivers/gpu/drm/drm_edid.c
     </sect2>
drivers/gpu/drm/Makefile +1 -1
index 48e38ba..712b73e 100644
@@ -23,7 +23,7 @@ drm-$(CONFIG_DRM_PANEL) += drm_panel.o
 
 drm-usb-y   := drm_usb.o
 
-drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o
+drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o drm_dp_mst_topology.o
 drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
 drm_kms_helper-$(CONFIG_DRM_KMS_FB_HELPER) += drm_fb_helper.o
 drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o
drivers/gpu/drm/drm_dp_mst_topology.c new file +2536 -0

2,536 lines not on this page · raw message

include/drm/drm_dp_mst_helper.h new file +503 -0

444 lines not on this page · raw message

The rest of this diff is not shown. View the raw message.

Signature · 6 lines
1.9.0

_______________________________________________
dri-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/dri-devel

Part of the diff is not shown here. View the raw message.

author maintainer
#11 lore raw

PATCH 7/10 i915: split some DP modesetting code into a separate function

In reply to Dave Airlie

From: Dave Airlie <[email protected]>

this is just prep work for mst support.

---
 drivers/gpu/drm/i915/intel_ddi.c | 20 +++++++++++++-------
 drivers/gpu/drm/i915/intel_drv.h |  1 +
 2 files changed, 14 insertions(+), 7 deletions(-)
drivers/gpu/drm/i915/intel_ddi.c +13 -7
index 0ad4e96..a5b8b76 100644
@@ -364,6 +364,18 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
 	DRM_ERROR("FDI link training failed!\n");
 }
 
+void intel_ddi_mode_set_dp(struct intel_encoder *encoder)
+{
+	struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+	struct intel_digital_port *intel_dig_port =
+		enc_to_dig_port(&encoder->base);
+
+	intel_dp->DP = intel_dig_port->saved_port_bits |
+		DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW;
+	intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count);
+
+}
+
 static void intel_ddi_mode_set(struct intel_encoder *encoder)
 {
 	struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
@@ -378,13 +390,7 @@ static void intel_ddi_mode_set(struct intel_encoder *encoder)
 	crtc->eld_vld = false;
 	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
 		struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
-		struct intel_digital_port *intel_dig_port =
-			enc_to_dig_port(&encoder->base);
-
-		intel_dp->DP = intel_dig_port->saved_port_bits |
-			       DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW;
-		intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count);
-
+		intel_ddi_mode_set_dp(encoder);
 		if (intel_dp->has_audio) {
 			DRM_DEBUG_DRIVER("DP audio on pipe %c on DDI\n",
 					 pipe_name(crtc->pipe));
drivers/gpu/drm/i915/intel_drv.h +1 -0
index b885df1..8e41cdc 100644
@@ -683,6 +683,7 @@ void intel_ddi_fdi_disable(struct drm_crtc *crtc);
 void intel_ddi_get_config(struct intel_encoder *encoder,
 			  struct intel_crtc_config *pipe_config);
 
+void intel_ddi_mode_set_dp(struct intel_encoder *encoder);
 
 /* intel_display.c */
 const char *intel_output_name(int output);
Signature · 1 line
1.9.0
author maintainer
#12 lore raw

PATCH 8/10 drm/i915: check connector->encoder before using it.

In reply to Dave Airlie

From: Dave Airlie <[email protected]>

DP MST will need connectors that aren't connected to specific encoders, add some checks in advance to avoid oopses.

---
 drivers/gpu/drm/i915/i915_debugfs.c  | 16 +++++++++-------
 drivers/gpu/drm/i915/i915_irq.c      |  4 ++++
 drivers/gpu/drm/i915/intel_display.c | 25 ++++++++++++++-----------
 3 files changed, 27 insertions(+), 18 deletions(-)
drivers/gpu/drm/i915/i915_debugfs.c +9 -7
index 1e83ae4..88e944f 100644
@@ -2279,13 +2279,15 @@ static void intel_connector_info(struct seq_file *m,
 		seq_printf(m, "\tCEA rev: %d\n",
 			   connector->display_info.cea_rev);
 	}
-	if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT ||
-	    intel_encoder->type == INTEL_OUTPUT_EDP)
-		intel_dp_info(m, intel_connector);
-	else if (intel_encoder->type == INTEL_OUTPUT_HDMI)
-		intel_hdmi_info(m, intel_connector);
-	else if (intel_encoder->type == INTEL_OUTPUT_LVDS)
-		intel_lvds_info(m, intel_connector);
+	if (intel_encoder) {
+		if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT ||
+		    intel_encoder->type == INTEL_OUTPUT_EDP)
+			intel_dp_info(m, intel_connector);
+		else if (intel_encoder->type == INTEL_OUTPUT_HDMI)
+			intel_hdmi_info(m, intel_connector);
+		else if (intel_encoder->type == INTEL_OUTPUT_LVDS)
+			intel_lvds_info(m, intel_connector);
+	}
 
 	seq_printf(m, "\tmodes:\n");
 	list_for_each_entry(mode, &connector->modes, head)
drivers/gpu/drm/i915/i915_irq.c +4 -0
index afa5519..5852dee 100644
@@ -1016,6 +1016,8 @@ static void i915_hotplug_work_func(struct work_struct *work)
 	dev_priv->hpd_event_bits = 0;
 	list_for_each_entry(connector, &mode_config->connector_list, head) {
 		intel_connector = to_intel_connector(connector);
+		if (!intel_connector->encoder)
+			continue;
 		intel_encoder = intel_connector->encoder;
 		if (intel_encoder->hpd_pin > HPD_NONE &&
 		    dev_priv->hpd_stats[intel_encoder->hpd_pin].hpd_mark == HPD_MARK_DISABLED &&
@@ -1046,6 +1048,8 @@ static void i915_hotplug_work_func(struct work_struct *work)
 
 	list_for_each_entry(connector, &mode_config->connector_list, head) {
 		intel_connector = to_intel_connector(connector);
+		if (!intel_connector->encoder)
+			continue;
 		intel_encoder = intel_connector->encoder;
 		if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) {
 			if (intel_encoder->hot_plug)
drivers/gpu/drm/i915/intel_display.c +14 -11
index b39d036..75b2aaf 100644
@@ -4600,20 +4600,23 @@ static void intel_connector_check_state(struct intel_connector *connector)
 		     "wrong connector dpms state\n");
 		WARN(connector->base.encoder != &encoder->base,
 		     "active connector not linked to encoder\n");
-		WARN(!encoder->connectors_active,
-		     "encoder->connectors_active not set\n");
 
-		encoder_enabled = encoder->get_hw_state(encoder, &pipe);
-		WARN(!encoder_enabled, "encoder not enabled\n");
-		if (WARN_ON(!encoder->base.crtc))
-			return;
+		if (encoder) {
+			WARN(!encoder->connectors_active,
+			     "encoder->connectors_active not set\n");
+
+			encoder_enabled = encoder->get_hw_state(encoder, &pipe);
+			WARN(!encoder_enabled, "encoder not enabled\n");
+			if (WARN_ON(!encoder->base.crtc))
+				return;
 
-		crtc = encoder->base.crtc;
+			crtc = encoder->base.crtc;
 
-		WARN(!crtc->enabled, "crtc not enabled\n");
-		WARN(!to_intel_crtc(crtc)->active, "crtc not active\n");
-		WARN(pipe != to_intel_crtc(crtc)->pipe,
-		     "encoder active on the wrong pipe\n");
+			WARN(!crtc->enabled, "crtc not enabled\n");
+			WARN(!to_intel_crtc(crtc)->active, "crtc not active\n");
+			WARN(pipe != to_intel_crtc(crtc)->pipe,
+			     "encoder active on the wrong pipe\n");
+		}
 	}
 }
 
Signature · 1 line
1.9.0
author maintainer
#13 lore raw

PATCH 9/10 i915: add DP 1.2 MST support (v0.3)

In reply to Dave Airlie

From: Dave Airlie <[email protected]>

This adds DP 1.2 MST support on Haswell systems.

Notes:
a) this reworks irq handling for DP MST ports, so that we can avoid the mode config locking in the current hpd handlers, as we need to process up/down msgs at a better time.

b) it introduces a new MST output type.

c) it really annoys the state checker, as the connector that is connected at the start, goes into disconnected, and its encoder it active, and things blow up, need to look into how to solve that.

d) the intel userspace driver needs changes to use this, -modesetting should work okay.

e) it might contain a race condition big enough to eat you.

f) its hardcoded to 4 lanes 5.4Ghz. - TODO.

Changes since v0.1:
use PORT_PCH_HOTPLUG to detect short vs long pulses
add a workqueue to deal with digital events as they can get blocked on the main workqueue beyong mode_config mutex
fix a bunch of modeset checker warnings
acks irqs in the driver
cleanup the MST encoders

Changes since v0.3
check irq status again in work handler
move around bring up and tear down to fix DPMS on/off use path properties.

TODO:
further state checker fixes

---
 drivers/gpu/drm/i915/Makefile         |   1 +
 drivers/gpu/drm/i915/i915_dma.c       |  10 +
 drivers/gpu/drm/i915/i915_drv.h       |  14 +
 drivers/gpu/drm/i915/i915_irq.c       | 106 ++++++-
 drivers/gpu/drm/i915/intel_ddi.c      | 111 +++++--
 drivers/gpu/drm/i915/intel_display.c  |  59 +++-
 drivers/gpu/drm/i915/intel_dp.c       | 213 +++++++++++++-
 drivers/gpu/drm/i915/intel_dp_mst.c   | 530 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_drv.h      |  42 ++-
 drivers/gpu/drm/i915/intel_opregion.c |   1 +
 10 files changed, 1041 insertions(+), 46 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/intel_dp_mst.c
drivers/gpu/drm/i915/Makefile +1 -0
index b1445b7..6cf2b95 100644
@@ -52,6 +52,7 @@ i915-y += dvo_ch7017.o \
 	  intel_crt.o \
 	  intel_ddi.o \
 	  intel_dp.o \
+	  intel_dp_mst.o \
 	  intel_dsi_cmd.o \
 	  intel_dsi.o \
 	  intel_dsi_pll.o \
drivers/gpu/drm/i915/i915_dma.c +10 -0
index 58f2c46..3040869 100644
@@ -1677,6 +1677,13 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
 		goto out_mtrrfree;
 	}
 
+	dev_priv->dp_wq = alloc_ordered_workqueue("i915-dp", 0);
+	if (dev_priv->dp_wq == NULL) {
+		DRM_ERROR("Failed to create our dp workqueue.\n");
+		ret = -ENOMEM;
+		goto out_freewq;
+	}
+
 	intel_irq_init(dev);
 	intel_uncore_sanitize(dev);
 
@@ -1752,6 +1759,8 @@ out_gem_unload:
 	intel_teardown_gmbus(dev);
 	intel_teardown_mchbar(dev);
 	pm_qos_remove_request(&dev_priv->pm_qos);
+	destroy_workqueue(dev_priv->dp_wq);
+out_freewq:
 	destroy_workqueue(dev_priv->wq);
 out_mtrrfree:
 	arch_phys_wc_del(dev_priv->gtt.mtrr);
@@ -1856,6 +1865,7 @@ int i915_driver_unload(struct drm_device *dev)
 	intel_teardown_gmbus(dev);
 	intel_teardown_mchbar(dev);
 
+	destroy_workqueue(dev_priv->dp_wq);
 	destroy_workqueue(dev_priv->wq);
 	pm_qos_remove_request(&dev_priv->pm_qos);
 
drivers/gpu/drm/i915/i915_drv.h +14 -0
index 7d6acb4..b3b5864c 100644
@@ -1468,6 +1468,18 @@ struct drm_i915_private {
 
 	struct i915_runtime_pm pm;
 
+	struct intel_digital_port *hpd_irq_port[I915_MAX_PORTS];
+	u32 hpd_port;
+	struct work_struct dig_port_work;
+	/*
+	 * if we get a HPD irq from DP and a HPD irq from non-DP
+	 * the non-DP HPD could block the workqueue on a mode config
+	 * mutex getting, that userspace may have taken. However
+	 * userspace is waiting on the DP workqueue to run which is
+	 * blocked behind the non-DP one.
+	 */
+	struct workqueue_struct *dp_wq;
+
 	/* Old dri1 support infrastructure, beware the dragons ya fools entering
 	 * here! */
 	struct i915_dri1_state dri1;
@@ -1999,6 +2011,8 @@ i915_disable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
 
 void valleyview_enable_display_irqs(struct drm_i915_private *dev_priv);
 void valleyview_disable_display_irqs(struct drm_i915_private *dev_priv);
+bool intel_hpd_irq_event(struct drm_device *dev,
+			 struct drm_connector *connector);
 
 /* i915_gem.c */
 int i915_gem_init_ioctl(struct drm_device *dev, void *data,
drivers/gpu/drm/i915/i915_irq.c +102 -4

155 lines not on this page · raw message

drivers/gpu/drm/i915/intel_ddi.c +89 -22

186 lines not on this page · raw message

drivers/gpu/drm/i915/intel_display.c +43 -16

107 lines not on this page · raw message

drivers/gpu/drm/i915/intel_dp.c +210 -3

288 lines not on this page · raw message

drivers/gpu/drm/i915/intel_dp_mst.c new file +530 -0

530 lines not on this page · raw message

drivers/gpu/drm/i915/intel_drv.h +41 -1

104 lines not on this page · raw message

drivers/gpu/drm/i915/intel_opregion.c +1 -0
index acde294..bce66b2 100644
@@ -352,6 +352,7 @@ int intel_opregion_notify_encoder(struct intel_encoder *intel_encoder,
 	case INTEL_OUTPUT_UNKNOWN:
 	case INTEL_OUTPUT_DISPLAYPORT:
 	case INTEL_OUTPUT_HDMI:
+	case INTEL_OUTPUT_DP_MST:
 		type = DISPLAY_TYPE_EXTERNAL_FLAT_PANEL;
 		break;
 	case INTEL_OUTPUT_EDP:
Signature · 6 lines
1.9.0

_______________________________________________
dri-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/dri-devel
#14 lore raw

Re: [PATCH 09/10] i915: add DP 1.2 MST support (v0.3)

Hi Dave

Quoted 1,363 lines · Dave Airlie

From: Dave Airlie <[email protected]>

This adds DP 1.2 MST support on Haswell systems.

Notes:
a) this reworks irq handling for DP MST ports, so that we can avoid the mode config locking in the current hpd handlers, as we need to process up/down msgs at a better time.

b) it introduces a new MST output type.

c) it really annoys the state checker, as the connector that is connected at the start, goes into disconnected, and its encoder it active, and things blow up, need to look into how to solve that.

d) the intel userspace driver needs changes to use this, -modesetting should work okay.

e) it might contain a race condition big enough to eat you.

f) its hardcoded to 4 lanes 5.4Ghz. - TODO.

Changes since v0.1:
use PORT_PCH_HOTPLUG to detect short vs long pulses
add a workqueue to deal with digital events as they can get blocked on the main workqueue beyong mode_config mutex
fix a bunch of modeset checker warnings
acks irqs in the driver
cleanup the MST encoders

Changes since v0.3
check irq status again in work handler
move around bring up and tear down to fix DPMS on/off use path properties.

TODO:
further state checker fixes

---
 drivers/gpu/drm/i915/Makefile         |   1 +
 drivers/gpu/drm/i915/i915_dma.c       |  10 +
 drivers/gpu/drm/i915/i915_drv.h       |  14 +
 drivers/gpu/drm/i915/i915_irq.c       | 106 ++++++-
 drivers/gpu/drm/i915/intel_ddi.c      | 111 +++++--
 drivers/gpu/drm/i915/intel_display.c  |  59 +++-
 drivers/gpu/drm/i915/intel_dp.c       | 213 +++++++++++++-
 drivers/gpu/drm/i915/intel_dp_mst.c   | 530 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_drv.h      |  42 ++-
 drivers/gpu/drm/i915/intel_opregion.c |   1 +
 10 files changed, 1041 insertions(+), 46 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/intel_dp_mst.c
drivers/gpu/drm/i915/Makefile +1 -0
index b1445b7..6cf2b95 100644
@@ -52,6 +52,7 @@ i915-y += dvo_ch7017.o \
          intel_crt.o \
          intel_ddi.o \
          intel_dp.o \
+         intel_dp_mst.o \
          intel_dsi_cmd.o \
          intel_dsi.o \
          intel_dsi_pll.o \
drivers/gpu/drm/i915/i915_dma.c +10 -0
index 58f2c46..3040869 100644
@@ -1677,6 +1677,13 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
                goto out_mtrrfree;
        }
 
+       dev_priv->dp_wq = alloc_ordered_workqueue("i915-dp", 0);
+       if (dev_priv->dp_wq == NULL) {
+               DRM_ERROR("Failed to create our dp workqueue.\n");
+               ret = -ENOMEM;
+               goto out_freewq;
+       }
+
        intel_irq_init(dev);
        intel_uncore_sanitize(dev);
 
@@ -1752,6 +1759,8 @@ out_gem_unload:
        intel_teardown_gmbus(dev);
        intel_teardown_mchbar(dev);
        pm_qos_remove_request(&dev_priv->pm_qos);
+       destroy_workqueue(dev_priv->dp_wq);
+out_freewq:
        destroy_workqueue(dev_priv->wq);
 out_mtrrfree:
        arch_phys_wc_del(dev_priv->gtt.mtrr);
@@ -1856,6 +1865,7 @@ int i915_driver_unload(struct drm_device *dev)
        intel_teardown_gmbus(dev);
        intel_teardown_mchbar(dev);
 
+       destroy_workqueue(dev_priv->dp_wq);
        destroy_workqueue(dev_priv->wq);
        pm_qos_remove_request(&dev_priv->pm_qos);
 
drivers/gpu/drm/i915/i915_drv.h +14 -0
index 7d6acb4..b3b5864c 100644
@@ -1468,6 +1468,18 @@ struct drm_i915_private {
 
        struct i915_runtime_pm pm;
 
+       struct intel_digital_port *hpd_irq_port[I915_MAX_PORTS];
+       u32 hpd_port;
+       struct work_struct dig_port_work;
+       /*
+        * if we get a HPD irq from DP and a HPD irq from non-DP
+        * the non-DP HPD could block the workqueue on a mode config
+        * mutex getting, that userspace may have taken. However
+        * userspace is waiting on the DP workqueue to run which is
+        * blocked behind the non-DP one.
+        */
+       struct workqueue_struct *dp_wq;
+
        /* Old dri1 support infrastructure, beware the dragons ya fools entering
         * here! */
        struct i915_dri1_state dri1;
@@ -1999,6 +2011,8 @@ i915_disable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
 
 void valleyview_enable_display_irqs(struct drm_i915_private *dev_priv);
 void valleyview_disable_display_irqs(struct drm_i915_private *dev_priv);
+bool intel_hpd_irq_event(struct drm_device *dev,
+                        struct drm_connector *connector);
 
 /* i915_gem.c */
 int i915_gem_init_ioctl(struct drm_device *dev, void *data,
drivers/gpu/drm/i915/i915_irq.c +102 -4
index 5852dee..0b16d41 100644
@@ -963,8 +963,8 @@ static int i915_get_vblank_timestamp(struct drm_device *dev, int pipe,
                                                     &to_intel_crtc(crtc)->config.adjusted_mode);
 }
 
-static bool intel_hpd_irq_event(struct drm_device *dev,
-                               struct drm_connector *connector)
+bool intel_hpd_irq_event(struct drm_device *dev,
+                        struct drm_connector *connector)
 {
        enum drm_connector_status old_status;
 
@@ -984,6 +984,71 @@ static bool intel_hpd_irq_event(struct drm_device *dev,
        return true;
 }
 
+static int port_to_hotplug_shift(enum port port)
+{
+       switch (port) {
+       case PORT_A:
+       case PORT_E:
+       default:
+               return -1;
+       case PORT_B:
+               return 0;
+       case PORT_C:
+               return 8;
+       case PORT_D:
+               return 16;
+       }
+}
+static void i915_digport_work_func(struct work_struct *work)
+{
+       struct drm_i915_private *dev_priv =
+               container_of(work, struct drm_i915_private, dig_port_work);
+       unsigned long irqflags;
+       u32 port_bits;
+       struct intel_digital_port *digport;
+       int i, ret;
+       u32 old_bits = 0;
+       u32 hotplug_reg, hpshift;
+       bool long_hpd = false;
+       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+       port_bits = dev_priv->hpd_port;
+       dev_priv->hpd_port = 0;
+
+       hotplug_reg = I915_READ(PCH_PORT_HOTPLUG);
+       I915_WRITE(PCH_PORT_HOTPLUG, hotplug_reg);
+
+       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+
+       for (i = 0; i < I915_MAX_PORTS; i++) {
+               if (port_bits & (1 << i)) {
+                       digport = dev_priv->hpd_irq_port[i];
+
+                       hpshift = port_to_hotplug_shift(i);
+
+                       /* if we have no HPD bits for this */
+                       if (!((hotplug_reg >> hpshift) & PORTB_HOTPLUG_STATUS_MASK)) {
+                               DRM_DEBUG_KMS("got not regs for port %d %08x\n", i, hotplug_reg);
+                               continue;
+                       }
+
+                       long_hpd = (hotplug_reg >> hpshift) & PORTB_HOTPLUG_LONG_DETECT;
+
+                       ret = intel_dp_handle_hpd_irq(digport, long_hpd);
+                       if (ret == 1) {
+                               /* if we get 1 fallback to old school hpd */
+                               old_bits |= (1 << digport->base.hpd_pin);
+                       }
+               }
+       }
+
+       if (old_bits) {
+               spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+               dev_priv->hpd_event_bits = old_bits;
+               spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+               schedule_work(&dev_priv->hotplug_work);
+       }
+}
+
 /*
  * Handle hotplug events outside the interrupt handler proper.
  */
@@ -1381,14 +1446,29 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev,
 #define HPD_STORM_DETECT_PERIOD 1000
 #define HPD_STORM_THRESHOLD 5
 
+static inline enum port get_port_from_pin(enum hpd_pin pin)
+{
+       switch (pin) {
+       case HPD_PORT_B:
+               return PORT_B;
+       case HPD_PORT_C:
+               return PORT_C;
+       case HPD_PORT_D:
+               return PORT_D;
+       default:
+               return PORT_A; /* no hpd */
+       }
+}
+
 static inline void intel_hpd_irq_handler(struct drm_device *dev,
                                         u32 hotplug_trigger,
                                         const u32 *hpd)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        int i;
+       enum port port;
        bool storm_detected = false;
-
+       bool queue_dig = false, queue_hp = false;
        if (!hotplug_trigger)
                return;
 
@@ -1397,6 +1477,19 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev,
 
        spin_lock(&dev_priv->irq_lock);
        for (i = 1; i < HPD_NUM_PINS; i++) {
+               if (!(hpd[i] & hotplug_trigger))
+                       continue;
+
+               port = get_port_from_pin(i);
+               if (port && dev_priv->hpd_irq_port[port]) {
+                       DRM_DEBUG_DRIVER("digital hpd port %d\n", port);
+                       dev_priv->hpd_port |= (1 << port);
+                       hotplug_trigger &= ~hpd[i];
+                       queue_dig = true;
+               }
+       }
+
+       for (i = 1; i < HPD_NUM_PINS; i++) {
 
                if (hpd[i] & hotplug_trigger &&
                    dev_priv->hpd_stats[i].hpd_mark == HPD_DISABLED) {
@@ -1418,6 +1511,7 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev,
                        continue;
 
                dev_priv->hpd_event_bits |= (1 << i);
+               queue_hp = true;
                if (!time_in_range(jiffies, dev_priv->hpd_stats[i].hpd_last_jiffies,
                                   dev_priv->hpd_stats[i].hpd_last_jiffies
                                   + msecs_to_jiffies(HPD_STORM_DETECT_PERIOD))) {
@@ -1446,7 +1540,10 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev,
         * queue for otherwise the flush_work in the pageflip code will
         * deadlock.
         */
-       schedule_work(&dev_priv->hotplug_work);
+       if (queue_dig)
+               queue_work(dev_priv->dp_wq, &dev_priv->dig_port_work);
+       if (queue_hp)
+               schedule_work(&dev_priv->hotplug_work);
 }
 
 static void gmbus_irq_handler(struct drm_device *dev)
@@ -4014,6 +4111,7 @@ void intel_irq_init(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
+       INIT_WORK(&dev_priv->dig_port_work, i915_digport_work_func);
        INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
        INIT_WORK(&dev_priv->gpu_error.work, i915_error_work_func);
        INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work);
drivers/gpu/drm/i915/intel_ddi.c +89 -22
index a5b8b76..a8728d1 100644
@@ -116,7 +116,10 @@ enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder)
        struct drm_encoder *encoder = &intel_encoder->base;
        int type = intel_encoder->type;
 
-       if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP ||
+       if (type == INTEL_OUTPUT_DP_MST) {
+               struct intel_digital_port *intel_dig_port = enc_to_mst(encoder)->primary;
+               return intel_dig_port->port;
+       } else if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP ||
            type == INTEL_OUTPUT_HDMI || type == INTEL_OUTPUT_UNKNOWN) {
                struct intel_digital_port *intel_dig_port =
                        enc_to_dig_port(encoder);
@@ -211,6 +214,19 @@ void intel_prepare_ddi(struct drm_device *dev)
                intel_prepare_ddi_buffers(dev, port);
 }
 
+void intel_ddi_force_act(struct intel_encoder *encoder, bool state)
+{
+       struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+       enum port port = intel_ddi_get_encoder_port(encoder);
+       u32 val;
+       val = I915_READ(DP_TP_CTL(port));
+       if (state == true)
+               val |= DP_TP_CTL_FORCE_ACT;
+       else
+               val &= ~DP_TP_CTL_FORCE_ACT;
+       I915_WRITE(DP_TP_CTL(port), val);
+}
+
 static const long hsw_ddi_buf_ctl_values[] = {
        DDI_BUF_EMP_400MV_0DB_HSW,
        DDI_BUF_EMP_400MV_3_5DB_HSW,
@@ -673,8 +689,8 @@ static int intel_ddi_calc_wrpll_link(struct drm_i915_private *dev_priv,
        return (refclk * n * 100) / (p * r);
 }
 
-static void intel_ddi_clock_get(struct intel_encoder *encoder,
-                               struct intel_crtc_config *pipe_config)
+void intel_ddi_clock_get(struct intel_encoder *encoder,
+                        struct intel_crtc_config *pipe_config)
 {
        struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
        enum port port = intel_ddi_get_encoder_port(encoder);
@@ -795,6 +811,20 @@ intel_ddi_calculate_wrpll(int clock /* in Hz */,
        *r2_out = best.r2;
 }
 
+static int link_bw_to_pll_sel(int link_bw)
+{
+       switch (link_bw) {
+       case DP_LINK_BW_1_62:
+               return PORT_CLK_SEL_LCPLL_810;
+       case DP_LINK_BW_2_7:
+               return PORT_CLK_SEL_LCPLL_1350;
+       case DP_LINK_BW_5_4:
+               return PORT_CLK_SEL_LCPLL_2700;
+       default:
+               return -1;
+       }
+}
+
 /*
  * Tries to find a PLL for the CRTC. If it finds, it increases the refcount and
  * stores it in intel_crtc->ddi_pll_sel, so other mode sets won't be able to
@@ -814,20 +844,19 @@ bool intel_ddi_pll_select(struct intel_crtc *intel_crtc)
 
        intel_ddi_put_crtc_pll(crtc);
 
-       if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
+       if (type == INTEL_OUTPUT_DP_MST) {
+               struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
+               intel_crtc->ddi_pll_sel = link_bw_to_pll_sel(intel_mst->primary->dp.link_bw);
+               if (intel_crtc->ddi_pll_sel == -1) {
+                       DRM_ERROR("Link bandwidth %d unsupported\n",
+                                 intel_mst->primary->dp.link_bw);
+                       return false;
+               }
+       } else if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
                struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
 
-               switch (intel_dp->link_bw) {
-               case DP_LINK_BW_1_62:
-                       intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_810;
-                       break;
-               case DP_LINK_BW_2_7:
-                       intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_1350;
-                       break;
-               case DP_LINK_BW_5_4:
-                       intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_2700;
-                       break;
-               default:
+               intel_crtc->ddi_pll_sel = link_bw_to_pll_sel(intel_dp->link_bw);
+               if (intel_crtc->ddi_pll_sel == -1) {
                        DRM_ERROR("Link bandwidth %d unsupported\n",
                                  intel_dp->link_bw);
                        return false;
@@ -981,8 +1010,7 @@ void intel_ddi_set_pipe_settings(struct drm_crtc *crtc)
        int type = intel_encoder->type;
        uint32_t temp;
 
-       if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
-
+       if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP || type == INTEL_OUTPUT_DP_MST) {
                temp = TRANS_MSA_SYNC_CLK;
                switch (intel_crtc->config.pipe_bpp) {
                case 18:
@@ -1004,6 +1032,21 @@ void intel_ddi_set_pipe_settings(struct drm_crtc *crtc)
        }
 }
 
+void intel_ddi_set_vc_payload_alloc(struct drm_crtc *crtc, bool state)
+{
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder;
+       uint32_t temp;
+       temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder));
+       if (state == true)
+               temp |= TRANS_DDI_DP_VC_PAYLOAD_ALLOC;
+       else
+               temp &= ~TRANS_DDI_DP_VC_PAYLOAD_ALLOC;
+       I915_WRITE(TRANS_DDI_FUNC_CTL(cpu_transcoder), temp);
+}
+
 void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
 {
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
@@ -1083,7 +1126,19 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
                   type == INTEL_OUTPUT_EDP) {
                struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
 
-               temp |= TRANS_DDI_MODE_SELECT_DP_SST;
+               if (intel_dp->is_mst) {
+                       temp |= TRANS_DDI_MODE_SELECT_DP_MST;
+               } else
+                       temp |= TRANS_DDI_MODE_SELECT_DP_SST;
+
+               temp |= DDI_PORT_WIDTH(intel_dp->lane_count);
+       } else if (type == INTEL_OUTPUT_DP_MST) {
+               struct intel_dp *intel_dp = &enc_to_mst(encoder)->primary->dp;
+
+               if (intel_dp->is_mst) {
+                       temp |= TRANS_DDI_MODE_SELECT_DP_MST;
+               } else
+                       temp |= TRANS_DDI_MODE_SELECT_DP_SST;
 
                temp |= DDI_PORT_WIDTH(intel_dp->lane_count);
        } else {
@@ -1100,7 +1155,7 @@ void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv,
        uint32_t reg = TRANS_DDI_FUNC_CTL(cpu_transcoder);
        uint32_t val = I915_READ(reg);
 
-       val &= ~(TRANS_DDI_FUNC_ENABLE | TRANS_DDI_PORT_MASK);
+       val &= ~(TRANS_DDI_FUNC_ENABLE | TRANS_DDI_PORT_MASK | TRANS_DDI_DP_VC_PAYLOAD_ALLOC);
        val |= TRANS_DDI_PORT_NONE;
        I915_WRITE(reg, val);
 }
@@ -1160,6 +1215,11 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
        u32 tmp;
        int i;
 
+       if (encoder->type == INTEL_OUTPUT_DISPLAYPORT) {
+               if (enc_to_intel_dp(&encoder->base)->is_mst)
+                       return false;
+       }
+
        power_domain = intel_display_port_power_domain(encoder);
        if (!intel_display_power_enabled(dev_priv, power_domain))
                return false;
@@ -1498,10 +1558,15 @@ void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder)
                        intel_wait_ddi_buf_idle(dev_priv, port);
        }
 
-       val = DP_TP_CTL_ENABLE | DP_TP_CTL_MODE_SST |
+       val = DP_TP_CTL_ENABLE |
              DP_TP_CTL_LINK_TRAIN_PAT1 | DP_TP_CTL_SCRAMBLE_DISABLE;
-       if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
-               val |= DP_TP_CTL_ENHANCED_FRAME_ENABLE;
+       if (intel_dp->is_mst)
+               val |= DP_TP_CTL_MODE_MST;
+       else {
+               val |= DP_TP_CTL_MODE_SST;
+               if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
+                       val |= DP_TP_CTL_ENHANCED_FRAME_ENABLE;
+       }
        I915_WRITE(DP_TP_CTL(port), val);
        POSTING_READ(DP_TP_CTL(port));
 
@@ -1732,6 +1797,8 @@ void intel_ddi_init(struct drm_device *dev, enum port port)
        intel_encoder->cloneable = 0;
        intel_encoder->hot_plug = intel_ddi_hot_plug;
 
+       dev_priv->hpd_irq_port[port] = intel_dig_port;
+
        if (init_dp)
                dp_connector = intel_ddi_init_dp_connector(intel_dig_port);
 
drivers/gpu/drm/i915/intel_display.c +43 -16
index 75b2aaf..dfcc78e 100644
@@ -56,6 +56,14 @@ static int intel_framebuffer_init(struct drm_device *dev,
                                  struct drm_mode_fb_cmd2 *mode_cmd,
                                  struct drm_i915_gem_object *obj);
 
+static struct intel_encoder *intel_find_encoder(struct intel_connector *connector, int pipe)
+{
+       if (!connector->mst_port)
+               return connector->encoder;
+       else
+               return &connector->mst_port->mst_encoders[pipe]->base;
+}
+
 typedef struct {
        int     min, max;
 } intel_range_t;
@@ -3821,6 +3829,9 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
        if (intel_crtc->config.has_pch_encoder)
                lpt_pch_enable(crtc);
 
+       if (intel_crtc->config.dp_encoder_is_mst)
+               intel_ddi_set_vc_payload_alloc(crtc, true);
+
        for_each_encoder_on_crtc(dev, crtc, encoder) {
                encoder->enable(encoder);
                intel_opregion_notify_encoder(encoder, true);
@@ -3869,6 +3880,9 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
 
        intel_disable_pipe(dev_priv, pipe);
 
+       if (intel_crtc->config.dp_encoder_is_mst)
+               intel_ddi_set_vc_payload_alloc(crtc, false);
+
        ironlake_pfit_disable(intel_crtc);
 
        for_each_encoder_on_crtc(dev, crtc, encoder)
@@ -4038,6 +4052,23 @@ static void i9xx_pfit_enable(struct intel_crtc *crtc)
        for ((domain) = 0; (domain) < POWER_DOMAIN_NUM; (domain)++)     \
                if ((1 << (domain)) & (mask))
 
+enum intel_display_power_domain port_to_power_domain(enum port port)
+{
+       switch (port) {
+       case PORT_A:
+               return POWER_DOMAIN_PORT_DDI_A_4_LANES;
+       case PORT_B:
+               return POWER_DOMAIN_PORT_DDI_B_4_LANES;
+       case PORT_C:
+               return POWER_DOMAIN_PORT_DDI_C_4_LANES;
+       case PORT_D:
+               return POWER_DOMAIN_PORT_DDI_D_4_LANES;
+       default:
+               WARN_ON_ONCE(1);
+               return POWER_DOMAIN_PORT_OTHER;
+       }
+}
+
 enum intel_display_power_domain
 intel_display_port_power_domain(struct intel_encoder *intel_encoder)
 {
@@ -4052,19 +4083,10 @@ intel_display_port_power_domain(struct intel_encoder *intel_encoder)
        case INTEL_OUTPUT_HDMI:
        case INTEL_OUTPUT_EDP:
                intel_dig_port = enc_to_dig_port(&intel_encoder->base);
-               switch (intel_dig_port->port) {
-               case PORT_A:
-                       return POWER_DOMAIN_PORT_DDI_A_4_LANES;
-               case PORT_B:
-                       return POWER_DOMAIN_PORT_DDI_B_4_LANES;
-               case PORT_C:
-                       return POWER_DOMAIN_PORT_DDI_C_4_LANES;
-               case PORT_D:
-                       return POWER_DOMAIN_PORT_DDI_D_4_LANES;
-               default:
-                       WARN_ON_ONCE(1);
-                       return POWER_DOMAIN_PORT_OTHER;
-               }
+               return port_to_power_domain(intel_dig_port->port);
+       case INTEL_OUTPUT_DP_MST:
+               intel_dig_port = enc_to_mst(&intel_encoder->base)->primary;
+               return port_to_power_domain(intel_dig_port->port);
        case INTEL_OUTPUT_ANALOG:
                return POWER_DOMAIN_PORT_CRT;
        case INTEL_OUTPUT_DSI:
@@ -10203,7 +10225,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
                 * for them. */
                for (ro = 0; ro < set->num_connectors; ro++) {
                        if (set->connectors[ro] == &connector->base) {
-                               connector->new_encoder = connector->encoder;
+                               connector->new_encoder = intel_find_encoder(connector, to_intel_crtc(set->crtc)->pipe);
                                break;
                        }
                }
@@ -10249,7 +10271,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
                                         new_crtc)) {
                        return -EINVAL;
                }
-               connector->encoder->new_crtc = to_intel_crtc(new_crtc);
+               connector->new_encoder->new_crtc = to_intel_crtc(new_crtc);
 
                DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
                        connector->base.base.id,
@@ -10283,7 +10305,12 @@ intel_modeset_stage_output_state(struct drm_device *dev,
                }
        }
        /* Now we've also updated encoder->new_crtc for all encoders. */
-
+       list_for_each_entry(connector, &dev->mode_config.connector_list,
+                           base.head) {
+               if (connector->new_encoder)
+                       if (connector->new_encoder != connector->encoder)
+                               connector->encoder = connector->new_encoder;
+       }
        list_for_each_entry(crtc, &dev->mode_config.crtc_list,
                            base.head) {
                crtc->new_enabled = false;
drivers/gpu/drm/i915/intel_dp.c +210 -3
index 44df493..e1bd017 100644
@@ -701,8 +701,9 @@ intel_dp_connector_unregister(struct intel_connector *intel_connector)
 {
        struct intel_dp *intel_dp = intel_attached_dp(&intel_connector->base);
 
-       sysfs_remove_link(&intel_connector->base.kdev->kobj,
-                         intel_dp->aux.ddc.dev.kobj.name);
+       if (!intel_connector->mst_port)
+               sysfs_remove_link(&intel_connector->base.kdev->kobj,
+                                 intel_dp->aux.ddc.dev.kobj.name);
        intel_connector_unregister(intel_connector);
 }
 
@@ -2892,6 +2893,33 @@ intel_dp_probe_oui(struct intel_dp *intel_dp)
        edp_panel_vdd_off(intel_dp, false);
 }
 
+static bool
+intel_dp_probe_mst(struct intel_dp *intel_dp)
+{
+       u8 buf[1];
+
+       if (!intel_dp->can_mst)
+               return false;
+
+       if (intel_dp->dpcd[DP_DPCD_REV] < 0x12)
+               return false;
+
+       _edp_panel_vdd_on(intel_dp);
+       if (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_MSTM_CAP, buf, 1)) {
+               if (buf[0] & DP_MST_CAP) {
+                       DRM_DEBUG_KMS("Sink is MST capable\n");
+                       intel_dp->is_mst = true;
+               } else {
+                       DRM_DEBUG_KMS("Sink is not MST capable\n");
+                       intel_dp->is_mst = false;
+               }
+       }
+       edp_panel_vdd_off(intel_dp, false);
+
+       drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst);
+       return intel_dp->is_mst;
+}
+
 int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc)
 {
        struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
@@ -2929,6 +2957,20 @@ intel_dp_get_sink_irq(struct intel_dp *intel_dp, u8 *sink_irq_vector)
                                       sink_irq_vector, 1) == 1;
 }
 
+static bool
+intel_dp_get_sink_irq_esi(struct intel_dp *intel_dp, u8 *sink_irq_vector)
+{
+       int ret;
+
+       ret = intel_dp_dpcd_read_wake(&intel_dp->aux,
+                                            DP_SINK_COUNT_ESI,
+                                            sink_irq_vector, 4);
+       if (ret != 4)
+               return false;
+
+       return true;
+}
+
 static void
 intel_dp_handle_test_request(struct intel_dp *intel_dp)
 {
@@ -2944,6 +2986,65 @@ intel_dp_handle_test_request(struct intel_dp *intel_dp)
  *  3. Use Link Training from 2.5.3.3 and 3.5.1.3
  *  4. Check link status on receipt of hot-plug interrupt
  */
+static int
+intel_dp_check_mst_status(struct intel_dp *intel_dp)
+{
+       int send_event = 0;
+       bool bret;
+       if (intel_dp->is_mst) {
+               u8 esi[4] = { 0 };
+               int ret = 0;
+               int retry;
+
+               mutex_lock(&intel_dp->mst_mgr.aux_lock);
+               bret = intel_dp_get_sink_irq_esi(intel_dp, esi);
+               mutex_unlock(&intel_dp->mst_mgr.aux_lock);
+       go_again:
+               if (bret == true) {
+                       DRM_DEBUG_KMS("got esi %02x %02x %02x\n", esi[0], esi[1], esi[2]);
+                       if (esi[1] & (DP_UP_REQ_MSG_RDY | DP_DOWN_REP_MSG_RDY))  {
+                               ret = drm_dp_mst_hpd_irq(&intel_dp->mst_mgr, esi[1]);
+                               if (ret == 1)
+                                       send_event = 1;
+                       } else
+                               ret = 0;
+                       send_event |= ret;
+                       mutex_lock(&intel_dp->mst_mgr.aux_lock);
+                       for (retry = 0; retry < 3; retry++) {
+                               int wret;
+                               wret = drm_dp_dpcd_write(&intel_dp->aux,
+                                                       DP_SINK_COUNT_ESI+1,
+                                                       &esi[1], 3);
+                               if (wret == 3) {
+                                       break;
+                               }
+                       }
+                       mutex_unlock(&intel_dp->mst_mgr.aux_lock);
+
+                       mutex_lock(&intel_dp->mst_mgr.aux_lock);
+                       bret = intel_dp_get_sink_irq_esi(intel_dp, esi);
+                       mutex_unlock(&intel_dp->mst_mgr.aux_lock);
+                       if (bret == true) {
+                               DRM_DEBUG_KMS("got esi2 %02x %02x %02x\n", esi[0], esi[1], esi[2]);
+                               if (esi[1] & (DP_UP_REQ_MSG_RDY | DP_DOWN_REP_MSG_RDY))  {
+                                       goto go_again;
+                               }
+                       }
+
+                       if (ret == 0 && send_event)
+                               return 1;
+                       return ret;
+               } else {
+                       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+                       DRM_DEBUG_KMS("failed to get ESI - device may have failed\n");
+                       intel_dp->is_mst = false;
+                       drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst);
+                       /* send a hotplug event */
+                       drm_sysfs_hotplug_event(intel_dig_port->base.base.dev);
+               }
+       }
+       return -EINVAL;
+}
 
 void
 intel_dp_check_link_status(struct intel_dp *intel_dp)
@@ -3163,6 +3264,7 @@ intel_dp_detect(struct drm_connector *connector, bool force)
        enum drm_connector_status status;
        enum intel_display_power_domain power_domain;
        struct edid *edid = NULL;
+       bool ret;
 
        intel_runtime_pm_get(dev_priv);
 
@@ -3172,6 +3274,12 @@ intel_dp_detect(struct drm_connector *connector, bool force)
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
                      connector->base.id, drm_get_connector_name(connector));
 
+       if (intel_dp->is_mst) {
+               /* MST devices are disconnected from a monitor POV */
+               status = connector_status_disconnected;
+               goto out;
+       }
+
        intel_dp->has_audio = false;
 
        if (HAS_PCH_SPLIT(dev))
@@ -3184,6 +3292,16 @@ intel_dp_detect(struct drm_connector *connector, bool force)
 
        intel_dp_probe_oui(intel_dp);
 
+       ret = intel_dp_probe_mst(intel_dp);
+       if (ret) {
+               /* if we are in MST mode then this connector
+                  won't appear connected or have anything with EDID on it */
+               if (intel_encoder->type != INTEL_OUTPUT_EDP)
+                       intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
+               status = connector_status_disconnected;
+               goto out;
+       }
+
        if (intel_dp->force_audio != HDMI_AUDIO_AUTO) {
                intel_dp->has_audio = (intel_dp->force_audio == HDMI_AUDIO_ON);
        } else {
@@ -3379,6 +3497,7 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder)
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
 
        drm_dp_aux_unregister_i2c_bus(&intel_dp->aux);
+       intel_dp_mst_encoder_cleanup(intel_dig_port);
        drm_encoder_cleanup(encoder);
        if (is_edp(intel_dp)) {
                cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
@@ -3463,7 +3582,7 @@ bool intel_dp_is_edp(struct drm_device *dev, enum port port)
        return false;
 }
 
-static void
+void
 intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector)
 {
        struct intel_connector *intel_connector = to_intel_connector(connector);
@@ -3959,6 +4078,13 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
 
        intel_dp->psr_setup_done = false;
 
+       /* init MST on ports that can support it */
+       if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
+               if (port == PORT_B || port == PORT_C || port == PORT_D) {
+                       intel_dp_mst_encoder_init(intel_dig_port, intel_connector->base.base.id);
+               }
+       }
+
        if (!intel_edp_init_connector(intel_dp, intel_connector, &power_seq)) {
                drm_dp_aux_unregister_i2c_bus(&intel_dp->aux);
                if (is_edp(intel_dp)) {
@@ -3989,6 +4115,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
 void
 intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
 {
+       struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_digital_port *intel_dig_port;
        struct intel_encoder *intel_encoder;
        struct drm_encoder *encoder;
@@ -4029,6 +4156,10 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
        intel_dig_port->port = port;
        intel_dig_port->dp.output_reg = output_reg;
 
+       /* for now only use new IRQ handling for MST capable ports */
+       if (intel_dig_port->dp.can_mst)
+               dev_priv->hpd_irq_port[port] = intel_dig_port;
+
        intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
        intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
        intel_encoder->cloneable = 0;
@@ -4040,3 +4171,79 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
                kfree(intel_connector);
        }
 }
+
+/*
+ * Handle DP IRQ close to spec recommendations
+ * Use hw to detect long vs short pulses
+ *
+ * If we get a long pulse - we need to read DPCD first
+ * If we get a short pulse - we need to check link irq/status
+ * returning 1 causes the old detect to happen for HDMI
+ */
+int intel_dp_handle_hpd_irq(struct intel_digital_port *intel_dig_port,
+                           bool hpd_long)
+{
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct drm_device *dev = intel_dig_port->base.base.dev;
+       struct drm_mode_config *mode_config = &dev->mode_config;
+       struct drm_connector *connector;
+       struct intel_connector *intel_connector;
+       bool changed = false;
+       int ret;
+
+       if (!intel_dp->output_reg)
+               return 1;
+
+       DRM_DEBUG_KMS("got hpd irq on port %d - %s\n", intel_dig_port->port,
+                     hpd_long ? "long" : "short");
+       /* we have to re-read DPCD only on a long irq */
+       if (hpd_long) {
+               if (!intel_dp_get_dpcd(intel_dp))
+                       goto mst_fail;
+
+               intel_dp_probe_oui(intel_dp);
+
+               if (!intel_dp_probe_mst(intel_dp))
+                       goto mst_fail;
+       }
+
+       if (intel_dp->is_mst) {
+               ret = intel_dp_check_mst_status(intel_dp);
+               if (ret == -EINVAL)
+                       return 1;
+       } else {
+               intel_dp_check_link_status(intel_dp);
+               return 1;
+       }
+
+       if (ret == 1) {
+               mutex_lock(&mode_config->mutex);
+
+               /* send the something changed event for the correct port */
+               list_for_each_entry(connector, &mode_config->connector_list, head) {
+                       intel_connector = to_intel_connector(connector);
+                       if (!intel_connector->mst_port)
+                               continue;
+
+                       if (intel_connector->mst_port != intel_dp)
+                               continue;
+
+                       if (intel_hpd_irq_event(dev, connector))
+                               changed = true;
+               }
+               mutex_unlock(&mode_config->mutex);
+               if (changed)
+                       drm_kms_helper_hotplug_event(dev);
+       }
+       return 0;
+
+mst_fail:
+       /* if we were in MST mode, and device is not there get out of MST mode */
+       if (intel_dp->is_mst) {
+               DRM_DEBUG_KMS("failed to get ESI - device may have failed\n");
+               intel_dp->is_mst = false;
+               drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst);
+               drm_sysfs_hotplug_event(dev);
+       }
+       return 1;
+}
drivers/gpu/drm/i915/intel_dp_mst.c new file +433 -0
new file mode 100644
index 0000000..a656c22
@@ -0,0 +1,530 @@
+/*
+ * Copyright © 2008 Intel Corporation
+ *             2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#include <drm/drmP.h>
+#include "i915_drv.h"
+#include "intel_drv.h"
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+
+bool
+intel_dp_mst_compute_config(struct intel_encoder *encoder,
+                           struct intel_crtc_config *pipe_config)
+{
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_digital_port *intel_dig_port = intel_mst->primary;
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct drm_device *dev = encoder->base.dev;
+       int bpp;
+       int lane_count, slots;
+       struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
+       struct intel_connector *found = NULL, *intel_connector;
+       int mst_pbn;
+
+       pipe_config->dp_encoder_is_mst = true;
+       pipe_config->has_pch_encoder = false;
+       pipe_config->has_dp_encoder = true;
+       bpp = 24;
+       lane_count = 4;
+       intel_dp->link_bw = DP_LINK_BW_5_4;
+       intel_dp->lane_count = lane_count;
+
+       pipe_config->pipe_bpp = 24;
+       pipe_config->port_clock = drm_dp_bw_code_to_link_rate(intel_dp->link_bw);
+
+       list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head) {
+               if (intel_connector->new_encoder == encoder) {
+                       found = intel_connector;
+                       break;
+               }
+       }
+
+       if (!found) {
+               DRM_ERROR("can't find connector\n");
+               return false;
+       }
+
+       mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->clock, bpp);
+
+       pipe_config->pbn = mst_pbn;
+       slots = drm_dp_find_vcpi_slots(&intel_dp->mst_mgr, mst_pbn);
+
+       intel_link_compute_m_n(bpp, lane_count,
+                              adjusted_mode->crtc_clock,
+                              pipe_config->port_clock,
+                              &pipe_config->dp_m_n);
+
+       pipe_config->dp_m_n.tu = slots;
+       return true;
+
+}
+
+static void intel_dp_mst_mode_set(struct intel_encoder *encoder)
+{
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_digital_port *intel_dig_port = intel_mst->primary;
+
+       intel_ddi_mode_set_dp(&intel_dig_port->base);
+}
+
+static void intel_mst_disable_dp(struct intel_encoder *encoder)
+{
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_connector *found = NULL, *intel_connector;
+       struct intel_digital_port *intel_dig_port = intel_mst->primary;
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct drm_device *dev = encoder->base.dev;
+       int ret;
+
+       DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
+       list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head) {
+               if (intel_connector->encoder == encoder) {
+                       found = intel_connector;
+                       break;
+               }
+       }
+
+       if (!found)
+               return;
+
+       drm_dp_mst_reset_vcpi_slots(&intel_dp->mst_mgr, found->port);
+
+       ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
+       if (ret) {
+               DRM_ERROR("failed to update payload %d\n", ret);
+       }
+}
+
+static void intel_mst_post_disable_dp(struct intel_encoder *encoder)
+{
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_digital_port *intel_dig_port = intel_mst->primary;
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct intel_connector *found = NULL, *intel_connector;
+       struct drm_device *dev = encoder->base.dev;
+       int ret;
+       DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
+
+       list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head) {
+               if (intel_connector->encoder == encoder) {
+                       found = intel_connector;
+                       break;
+               }
+       }
+
+       if (!found)
+               return;
+
+       ret = drm_dp_check_act_status(&intel_dp->mst_mgr);
+       if (ret)
+               DRM_ERROR("failed to check ACT status\n");
+       ret = drm_dp_update_payload_part2(&intel_dp->mst_mgr);
+       if (ret)
+               DRM_ERROR("failed to update payloads.\n");
+
+       drm_dp_mst_deallocate_vcpi(&intel_dp->mst_mgr, found->port);
+
+       intel_dp->active_mst_links--;
+       if (intel_dp->active_mst_links == 0) {
+               intel_dig_port->base.post_disable(&intel_dig_port->base);
+               intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF);
+       }
+}
+
+static void intel_mst_pre_enable_dp(struct intel_encoder *encoder)
+{
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_digital_port *intel_dig_port = intel_mst->primary;
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum port port = intel_dig_port->port;
+       int ret;
+       uint32_t temp;
+       struct intel_connector *found = NULL, *intel_connector;
+       int slots;
+       struct drm_crtc *crtc = encoder->base.crtc;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+       list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head) {
+               if (intel_connector->new_encoder == encoder) {
+                       found = intel_connector;
+                       break;
+               }
+       }
+
+       if (!found) {
+               DRM_ERROR("can't find connector\n");
+               return;
+       }
+
+       DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
+       if (intel_dp->active_mst_links == 0) {
+               enum port port = intel_ddi_get_encoder_port(encoder);
+
+               I915_WRITE(PORT_CLK_SEL(port), intel_crtc->ddi_pll_sel);
+
+               intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+
+
+               intel_dp_start_link_train(intel_dp);
+               intel_dp_complete_link_train(intel_dp);
+               intel_dp_stop_link_train(intel_dp);
+       }
+
+       ret = drm_dp_mst_allocate_vcpi(&intel_dp->mst_mgr,
+                                      found->port, intel_crtc->config.pbn, &slots);
+       if (ret == false) {
+               DRM_ERROR("failed to allocate vcpi\n");
+               return;
+       }
+
+
+       intel_dp->active_mst_links++;
+       temp = I915_READ(DP_TP_STATUS(port));
+       I915_WRITE(DP_TP_STATUS(port), temp);
+
+       ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
+}
+
+static void intel_mst_enable_dp(struct intel_encoder *encoder)
+{
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_digital_port *intel_dig_port = intel_mst->primary;
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct drm_device *dev = intel_dig_port->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum port port = intel_dig_port->port;
+       int ret;
+
+       DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
+
+       if (wait_for((I915_READ(DP_TP_STATUS(port)) & DP_TP_STATUS_ACT_SENT),
+                    1))
+               DRM_ERROR("Timed out waiting for ACT sent\n");
+
+       ret = drm_dp_check_act_status(&intel_dp->mst_mgr);
+
+       ret = drm_dp_update_payload_part2(&intel_dp->mst_mgr);
+}
+
+static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder,
+                                     enum pipe *pipe)
+{
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       *pipe = intel_mst->pipe;
+       return encoder->connectors_active;
+}
+
+static void intel_dp_mst_enc_get_config(struct intel_encoder *encoder,
+                                       struct intel_crtc_config *pipe_config)
+{
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_digital_port *intel_dig_port = intel_mst->primary;
+       struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum transcoder cpu_transcoder = crtc->config.cpu_transcoder;
+       u32 temp, flags = 0;
+
+       pipe_config->has_dp_encoder = true;
+
+       temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder));
+       if (temp & TRANS_DDI_PHSYNC)
+               flags |= DRM_MODE_FLAG_PHSYNC;
+       else
+               flags |= DRM_MODE_FLAG_NHSYNC;
+       if (temp & TRANS_DDI_PVSYNC)
+               flags |= DRM_MODE_FLAG_PVSYNC;
+       else
+               flags |= DRM_MODE_FLAG_NVSYNC;
+
+       switch (temp & TRANS_DDI_BPC_MASK) {
+       case TRANS_DDI_BPC_6:
+               pipe_config->pipe_bpp = 18;
+               break;
+       case TRANS_DDI_BPC_8:
+               pipe_config->pipe_bpp = 24;
+               break;
+       case TRANS_DDI_BPC_10:
+               pipe_config->pipe_bpp = 30;
+               break;
+       case TRANS_DDI_BPC_12:
+               pipe_config->pipe_bpp = 36;
+               break;
+       default:
+               break;
+       }
+       pipe_config->adjusted_mode.flags |= flags;
+       intel_dp_get_m_n(crtc, pipe_config);
+
+       intel_ddi_clock_get(&intel_dig_port->base, pipe_config);
+}
+
+static int intel_dp_mst_get_ddc_modes(struct drm_connector *connector)
+{
+       struct intel_connector *intel_connector = to_intel_connector(connector);
+       struct intel_dp *intel_dp = intel_connector->mst_port;
+       struct edid *edid;
+       int ret;
+
+       edid = drm_dp_mst_get_edid(connector, &intel_dp->mst_mgr, intel_connector->port);
+       if (!edid)
+               return 0;
+
+       ret = intel_connector_update_modes(connector, edid);
+       kfree(edid);
+
+       return ret;
+}
+
+static enum drm_connector_status
+intel_mst_port_dp_detect(struct drm_connector *connector)
+{
+       struct intel_connector *intel_connector = to_intel_connector(connector);
+       struct intel_dp *intel_dp = intel_connector->mst_port;
+
+       return drm_dp_mst_detect_port(&intel_dp->mst_mgr, intel_connector->port);
+}
+
+static enum drm_connector_status
+intel_dp_mst_detect(struct drm_connector *connector, bool force)
+{
+       enum drm_connector_status status;
+       status = intel_mst_port_dp_detect(connector);
+       return status;
+}
+
+static int
+intel_dp_mst_set_property(struct drm_connector *connector,
+                         struct drm_property *property,
+                         uint64_t val)
+{
+       return 0;
+}
+
+static void
+intel_dp_mst_connector_destroy(struct drm_connector *connector)
+{
+       struct intel_connector *intel_connector = to_intel_connector(connector);
+
+       if (!IS_ERR_OR_NULL(intel_connector->edid))
+               kfree(intel_connector->edid);
+
+       drm_connector_cleanup(connector);
+       kfree(connector);
+}
+
+static const struct drm_connector_funcs intel_dp_mst_connector_funcs = {
+       .dpms = intel_connector_dpms,
+       .detect = intel_dp_mst_detect,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .set_property = intel_dp_mst_set_property,
+       .destroy = intel_dp_mst_connector_destroy,
+};
+
+static int intel_dp_mst_get_modes(struct drm_connector *connector)
+{
+       return intel_dp_mst_get_ddc_modes(connector);
+}
+
+static enum drm_mode_status
+intel_dp_mst_mode_valid(struct drm_connector *connector,
+                       struct drm_display_mode *mode)
+{
+       /* TODO - validate mode against available PBN for link */
+       if (mode->clock < 10000)
+               return MODE_CLOCK_LOW;
+
+       if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+               return MODE_H_ILLEGAL;
+
+       return MODE_OK;
+}
+
+struct drm_encoder *intel_mst_best_encoder(struct drm_connector *connector)
+{
+       WARN_ON(1);
+       return NULL;
+}
+
+static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_funcs = {
+       .get_modes = intel_dp_mst_get_modes,
+       .mode_valid = intel_dp_mst_mode_valid,
+       .best_encoder = intel_mst_best_encoder,
+};
+
+void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder)
+{
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
+
+       drm_encoder_cleanup(encoder);
+       kfree(intel_mst);
+}
+
+static const struct drm_encoder_funcs intel_dp_mst_enc_funcs = {
+       .destroy = intel_dp_mst_encoder_destroy,
+};
+
+static bool intel_dp_mst_get_hw_state(struct intel_connector *connector)
+{
+       if (connector->encoder)
+               return true;
+       return false;
+}
+
+static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, char *pathprop)
+{
+       struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
+       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+       struct drm_device *dev = intel_dig_port->base.base.dev;
+       struct intel_connector *intel_connector;
+       struct drm_connector *connector;
+       int i;
+
+       intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL);
+       if (!intel_connector)
+               return NULL;
+
+       connector = &intel_connector->base;
+       drm_connector_init(dev, connector, &intel_dp_mst_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort);
+       drm_connector_helper_add(connector, &intel_dp_mst_connector_helper_funcs);
+
+       intel_connector->unregister = intel_connector_unregister;
+       intel_connector->get_hw_state = intel_dp_mst_get_hw_state;
+       intel_connector->mst_port = intel_dp;
+       intel_connector->port = port;
+
+       for (i = PIPE_A; i <= PIPE_C; i++) {
+               drm_mode_connector_attach_encoder(&intel_connector->base,
+                                                 &intel_dp->mst_encoders[i]->base.base);
+       }
+       intel_dp_add_properties(intel_dp, connector);
+
+       drm_object_attach_property(&connector->base, dev->mode_config.path_property, 0);
+       drm_mode_connector_set_path_property(connector, pathprop);
+       drm_reinit_primary_mode_group(dev);
+       drm_sysfs_connector_add(&intel_connector->base);
+       return connector;
+}
+
+static void intel_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr,

drivers/gpu/drm/i915/intel_dp_mst.c line 443

Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c new file +10 -0
+                                          struct drm_connector *connector)
+{
+       struct intel_connector *intel_connector = to_intel_connector(connector);
+       struct drm_device *dev = connector->dev;
+       /* need to nuke the connector */
+       mutex_lock(&dev->mode_config.mutex);
+       intel_connector->unregister(intel_connector);
+       drm_connector_cleanup(connector);
+       mutex_unlock(&dev->mode_config.mutex);
+       drm_reinit_primary_mode_group(dev);

So we leak all dynamically created objects? There's no kfree(intel_connector) here and we cannot add it because drm_mode_object_find() is not ref-counted. So we keep the connector in the mode_group and wait until the final drm_mode_config_cleanup()? But drm_connector_cleanup() already removes the connector from the mode-group, so we eventually leak the object.

I guess you already know that dynamic DRM objects are fragile. Adding new objects is fine, but removing them is troublesome. A quick and easy fix would be to retain destroyed objects in the mode-group but mark them dead. When adding new objects, we can simply reuse them in case we have some left. Not sure, whether that's better than leaking the object, though.

Thanks
David

Quoted 220 lines
drivers/gpu/drm/i915/intel_dp_mst.c new file +87 -0
+
+       DRM_DEBUG_KMS("\n");
+}
+
+struct drm_dp_mst_topology_cbs mst_cbs = {
+       .add_connector = intel_dp_add_mst_connector,
+       .destroy_connector = intel_dp_destroy_mst_connector,
+};
+
+static struct intel_dp_mst_encoder *
+intel_dp_create_fake_mst_encoder(struct intel_digital_port *intel_dig_port, enum pipe pipe)
+{
+       struct intel_dp_mst_encoder *intel_mst;
+       struct intel_encoder *intel_encoder;
+       struct drm_device *dev = intel_dig_port->base.base.dev;
+
+       intel_mst = kzalloc(sizeof(*intel_mst), GFP_KERNEL);
+
+       if (!intel_mst)
+               return NULL;
+
+       intel_mst->pipe = pipe;
+       intel_encoder = &intel_mst->base;
+       intel_mst->primary = intel_dig_port;
+
+       drm_encoder_init(dev, &intel_encoder->base, &intel_dp_mst_enc_funcs,
+                        DRM_MODE_ENCODER_DPMST);
+
+       intel_encoder->type = INTEL_OUTPUT_DP_MST;
+       intel_encoder->crtc_mask = 0x7;
+       intel_encoder->cloneable = 0;
+
+       intel_encoder->compute_config = intel_dp_mst_compute_config;
+       intel_encoder->mode_set = intel_dp_mst_mode_set;
+       intel_encoder->disable = intel_mst_disable_dp;
+       intel_encoder->post_disable = intel_mst_post_disable_dp;
+       intel_encoder->pre_enable = intel_mst_pre_enable_dp;
+       intel_encoder->enable = intel_mst_enable_dp;
+       intel_encoder->get_hw_state = intel_dp_mst_enc_get_hw_state;
+       intel_encoder->get_config = intel_dp_mst_enc_get_config;
+
+       return intel_mst;
+
+}
+
+static bool
+intel_dp_create_fake_mst_encoders(struct intel_digital_port *intel_dig_port)
+{
+       int i;
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+
+       for (i = PIPE_A; i <= PIPE_C; i++)
+               intel_dp->mst_encoders[i] = intel_dp_create_fake_mst_encoder(intel_dig_port, i);
+       return true;
+}
+
+int
+intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_base_id)
+{
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct drm_device *dev = intel_dig_port->base.base.dev;
+       int ret;
+
+       intel_dp->can_mst = true;
+       intel_dp->mst_mgr.cbs = &mst_cbs;
+
+       /* create encoders */
+       intel_dp_create_fake_mst_encoders(intel_dig_port);
+       ret = drm_dp_mst_topology_mgr_init(&intel_dp->mst_mgr, dev->dev, &intel_dp->aux, 16, 3, conn_base_id);
+       if (ret) {
+               intel_dp->can_mst = false;
+               return ret;
+       }
+       return 0;
+}
+
+void
+intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port)
+{
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+
+       if (!intel_dp->can_mst)
+               return;
+
+       drm_dp_mst_topology_mgr_destroy(&intel_dp->mst_mgr);
+       /* encoders will get killed by normal cleanup */
+}
drivers/gpu/drm/i915/intel_drv.h +41 -1
index 8e41cdc..eda5131 100644
@@ -32,7 +32,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_helper.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/drm_dp_mst_helper.h>
 
 /**
  * _wait_for - magic (register) wait macro
@@ -100,6 +100,7 @@
 #define INTEL_OUTPUT_EDP 8
 #define INTEL_OUTPUT_DSI 9
 #define INTEL_OUTPUT_UNKNOWN 10
+#define INTEL_OUTPUT_DP_MST 11
 
 #define INTEL_DVO_CHIP_NONE 0
 #define INTEL_DVO_CHIP_LVDS 1
@@ -207,6 +208,10 @@ struct intel_connector {
        /* since POLL and HPD connectors may use the same HPD line keep the native
           state of connector->polled in case hotplug storm detection changes it */
        u8 polled;
+
+       void *port; /* store this opaque as its illegal to dereference it */
+
+       struct intel_dp *mst_port;
 };
 
 typedef struct dpll {
@@ -340,6 +345,9 @@ struct intel_crtc_config {
        bool ips_enabled;
 
        bool double_wide;
+
+       bool dp_encoder_is_mst;
+       int pbn;
 };
 
 struct intel_pipe_wm {
@@ -487,6 +495,7 @@ struct intel_hdmi {
                               struct drm_display_mode *adjusted_mode);
 };
 
+struct intel_dp_mst_encoder;
 #define DP_MAX_DOWNSTREAM_PORTS                0x10
 
 /**
@@ -527,8 +536,17 @@ struct intel_dp {
        unsigned long last_backlight_off;
        bool psr_setup_done;
        bool use_tps3;
+       bool can_mst; /* this port supports mst */
+       bool is_mst;
+       int active_mst_links;
+       /* connector directly attached - won't be use for modeset in mst world */
        struct intel_connector *attached_connector;
 
+       /* mst connector list */
+       struct intel_connector *mst_connectors;
+       struct intel_dp_mst_encoder *mst_encoders[I915_MAX_PIPES];
+       struct drm_dp_mst_topology_mgr mst_mgr;
+
        uint32_t (*get_aux_clock_divider)(struct intel_dp *dp, int index);
        /*
         * This function returns the value we have to program the AUX_CTL
@@ -554,6 +572,12 @@ struct intel_digital_port {
        struct intel_hdmi hdmi;
 };
 
+struct intel_dp_mst_encoder {
+       struct intel_encoder base;
+       enum pipe pipe;
+       struct intel_digital_port *primary;
+};
+
 static inline int
 vlv_dport_to_channel(struct intel_digital_port *dport)
 {
@@ -621,6 +645,12 @@ enc_to_dig_port(struct drm_encoder *encoder)
        return container_of(encoder, struct intel_digital_port, base.base);
 }
 
+static inline struct intel_dp_mst_encoder *
+enc_to_mst(struct drm_encoder *encoder)
+{
+       return container_of(encoder, struct intel_dp_mst_encoder, base.base);
+}
+
 static inline struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder)
 {
        return &enc_to_dig_port(encoder)->dp;
@@ -684,6 +714,10 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
                          struct intel_crtc_config *pipe_config);
 
 void intel_ddi_mode_set_dp(struct intel_encoder *encoder);
+void intel_ddi_clock_get(struct intel_encoder *encoder,
+                        struct intel_crtc_config *pipe_config);
+void intel_ddi_force_act(struct intel_encoder *encoder, bool state);
+void intel_ddi_set_vc_payload_alloc(struct drm_crtc *crtc, bool state);
 
 /* intel_display.c */
 const char *intel_output_name(int output);
@@ -800,6 +834,12 @@ void intel_edp_psr_disable(struct intel_dp *intel_dp);
 void intel_edp_psr_update(struct drm_device *dev);
 void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate);
 
+int intel_dp_handle_hpd_irq(struct intel_digital_port *digport, bool long_hpd);
+void intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector);
+
+/* intel_dp_mst.c */
+int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id);
+void intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port);
 /* intel_dsi.c */
 bool intel_dsi_init(struct drm_device *dev);
 
drivers/gpu/drm/i915/intel_opregion.c +1 -1
index acde294..bce66b2 100644
@@ -352,6 +352,7 @@ int intel_opregion_notify_encoder(struct intel_encoder *intel_encoder,
        case INTEL_OUTPUT_UNKNOWN:
        case INTEL_OUTPUT_DISPLAYPORT:
        case INTEL_OUTPUT_HDMI:
+       case INTEL_OUTPUT_DP_MST:
                type = DISPLAY_TYPE_EXTERNAL_FLAT_PANEL;
                break;
        case INTEL_OUTPUT_EDP:
--

1.9.0

Quoted 4 lines

_______________________________________________
dri-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/dri-devel

_______________________________________________
dri-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/dri-devel

author maintainer
#15 lore raw

Re: [PATCH 09/10] i915: add DP 1.2 MST support (v0.3)

Quoted 8 lines

So we leak all dynamically created objects? There's no kfree(intel_connector) here and we cannot add it because drm_mode_object_find() is not ref-counted. So we keep the connector in the mode_group and wait until the final drm_mode_config_cleanup()? But drm_connector_cleanup() already removes the connector from the mode-group, so we eventually leak the object.

Seems like freeing it should be fine, it was just an oversight,

Since any objects you get with find are only valid under mode_config.mutex, yes some drivers mess this up, but they should be fixed.

Dave.

#16 lore raw

Re: [PATCH 09/10] i915: add DP 1.2 MST support (v0.3)

Hi

Quoted 2 lines · Dave Airlie

Since any objects you get with find are only valid under mode_config.mutex, yes some drivers mess this up, but they should be fixed.

Didn't know that we have such a rule. Then it's fine, of course. The page-flip code is what worried me, but drivers ought to cancel that when destroying connectors, anyway.

Thanks
David

#17 lore raw

Re: [PATCH 09/10] i915: add DP 1.2 MST support (v0.3)

Quoted 9 lines · David Herrmann

Hi

Quoted 2 lines · Dave Airlie

Since any objects you get with find are only valid under mode_config.mutex, yes some drivers mess this up, but they should be fixed.

Didn't know that we have such a rule. Then it's fine, of course. The page-flip code is what worried me, but drivers ought to cancel that when destroying connectors, anyway.

i915 cancels/stalls for any pageflips when we switch off the crtc, so I think we should be fine.

And the above rule is for all modeset objects except framebuffers, since those are separately protected with refcounting and you don't even need the big mode_config mutex to do the idr lookup.
-Daniel

Signature · 3 lines
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
author maintainer
#18 lore raw

PATCH 10/10 i915: mst topology dumper in debugfs

In reply to Dave Airlie

From: Dave Airlie <[email protected]>

use the mst helper code to dump the topology in debugfs.

---
 drivers/gpu/drm/i915/i915_debugfs.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)
drivers/gpu/drm/i915/i915_debugfs.c +26 -0
index 88e944f..b8a9a51 100644
@@ -2378,6 +2378,31 @@ struct pipe_crc_info {
 	enum pipe pipe;
 };
 
+static int i915_dp_mst_info(struct seq_file *m, void *unused)
+{
+	struct drm_info_node *node = (struct drm_info_node *) m->private;
+	struct drm_device *dev = node->minor->dev;
+	struct drm_encoder *encoder;
+	struct intel_encoder *intel_encoder;
+	struct intel_digital_port *intel_dig_port;
+	drm_modeset_lock_all(dev);
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		intel_encoder = to_intel_encoder(encoder);
+		if (intel_encoder->type != INTEL_OUTPUT_DISPLAYPORT)
+			continue;
+		intel_dig_port = enc_to_dig_port(encoder);
+		if (!intel_dig_port->dp.can_mst)
+			continue;
+
+		if (!intel_dig_port->dp.is_mst)
+			continue;
+
+		drm_dp_mst_dump_topology(m, &intel_dig_port->dp.mst_mgr);
+	}
+	drm_modeset_unlock_all(dev);
+	return 0;
+}
+
 static int i915_pipe_crc_open(struct inode *inode, struct file *filep)
 {
 	struct pipe_crc_info *info = inode->i_private;
@@ -3813,6 +3838,7 @@ static const struct drm_info_list i915_debugfs_list[] = {
 	{"i915_pc8_status", i915_pc8_status, 0},
 	{"i915_power_domain_info", i915_power_domain_info, 0},
 	{"i915_display_info", i915_display_info, 0},
+	{"i915_dp_mst_info", i915_dp_mst_info, 0},
 };
 #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
 
Signature · 1 line
1.9.0
#19 lore raw

Re: [PATCH 10/10] i915: mst topology dumper in debugfs

On Mon, May 12, 2014 at 04:46:47PM +1000, Dave Airlie wrote:
[...]

drivers/gpu/drm/i915/i915_debugfs.c static const struct drm_info_list i915_debugfs_list[] = { · line 3841

Quoted 5 lines
drivers/gpu/drm/i915/i915_debugfs.c +1 -0
@@ -3813,6 +3838,7 @@ static const struct drm_info_list i915_debugfs_list[] = {
 	{"i915_pc8_status", i915_pc8_status, 0},
 	{"i915_power_domain_info", i915_power_domain_info, 0},
 	{"i915_display_info", i915_display_info, 0},
+	{"i915_dp_mst_info", i915_dp_mst_info, 0},

This isn't really specific to this patch, but I've been thinking for a while if perhaps it would be a good idea to try to unify debugfs across drivers to some degree. What I mean by that is try to use common names for files with similar functionality. Off the top of my head I think a couple of drivers expose a list of framebuffers via debugfs, mostly using duplicated code and different file names. Sharing the code would of course be easy, but I think there may be some advantage to keeping the names consistent as well.

Given its generic nature, MST sounds like a good candidate as well.

Thierry

_______________________________________________
dri-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/dri-devel

#20 lore raw

Re: [PATCH 10/10] i915: mst topology dumper in debugfs

Quoted 16 lines · Thierry Reding

On Mon, May 12, 2014 at 04:46:47PM +1000, Dave Airlie wrote:
[...]

drivers/gpu/drm/i915/i915_debugfs.c static const struct drm_info_list i915_debugfs_list[] = { · line 3841

Quoted 5 lines
drivers/gpu/drm/i915/i915_debugfs.c +1 -0
@@ -3813,6 +3838,7 @@ static const struct drm_info_list i915_debugfs_list[] = {
 	{"i915_pc8_status", i915_pc8_status, 0},
 	{"i915_power_domain_info", i915_power_domain_info, 0},
 	{"i915_display_info", i915_display_info, 0},
+	{"i915_dp_mst_info", i915_dp_mst_info, 0},

This isn't really specific to this patch, but I've been thinking for a while if perhaps it would be a good idea to try to unify debugfs across drivers to some degree. What I mean by that is try to use common names for files with similar functionality. Off the top of my head I think a couple of drivers expose a list of framebuffers via debugfs, mostly using duplicated code and different file names. Sharing the code would of course be easy, but I think there may be some advantage to keeping the names consistent as well.

Imo our current approach of having seq_file helpers in libraries that drivers can use works well. At least with i915 we often want to add some more driver-private state to dumps (e.g. for framebuffers), so extracting more than what's already extracted is probably hard to do.

But I guess we could add some common files with e.g. just the core framebuffer stuff to all drivers. Not terribly motivated myself though since i915 has it all already ;-)
-Daniel

Signature · 3 lines
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
#21 lore raw

Re: [PATCH 10/10] i915: mst topology dumper in debugfs

Quoted 18 lines · Daniel Vetter
Quoted 16 lines · Thierry Reding

On Mon, May 12, 2014 at 04:46:47PM +1000, Dave Airlie wrote:
[...]

drivers/gpu/drm/i915/i915_debugfs.c static const struct drm_info_list i915_debugfs_list[] = { · line 3841

Quoted 5 lines
drivers/gpu/drm/i915/i915_debugfs.c +1 -0
@@ -3813,6 +3838,7 @@ static const struct drm_info_list i915_debugfs_list[] = {
 	{"i915_pc8_status", i915_pc8_status, 0},
 	{"i915_power_domain_info", i915_power_domain_info, 0},
 	{"i915_display_info", i915_display_info, 0},
+	{"i915_dp_mst_info", i915_dp_mst_info, 0},

This isn't really specific to this patch, but I've been thinking for a while if perhaps it would be a good idea to try to unify debugfs across drivers to some degree. What I mean by that is try to use common names for files with similar functionality. Off the top of my head I think a couple of drivers expose a list of framebuffers via debugfs, mostly using duplicated code and different file names. Sharing the code would of course be easy, but I think there may be some advantage to keeping the names consistent as well.

Quoted 4 lines

Imo our current approach of having seq_file helpers in libraries that drivers can use works well. At least with i915 we often want to add some more driver-private state to dumps (e.g. for framebuffers), so extracting more than what's already extracted is probably hard to do.

Good point.

Quoted 3 lines

But I guess we could add some common files with e.g. just the core framebuffer stuff to all drivers. Not terribly motivated myself though since i915 has it all already ;-)

I was thinking it would be convenient if there was some consistency between the debugfs interfaces of the various drivers to make it easier for people to find equivalent information sources. On second thought maybe that's not such a useful idea after all since some of the files may expose similar, but not exactly the same, information, given that some of it may be driver-specific.

Thierry

_______________________________________________
Intel-gfx mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/intel-gfx