All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC] drm + i915 DP MST code preview
@ 2014-05-02  4:39 Dave Airlie
  2014-05-02  4:39 ` [PATCH 1/8] drm/dp_helper: add defines for DP 1.2 and MST support Dave Airlie
                   ` (9 more replies)
  0 siblings, 10 replies; 22+ messages in thread
From: Dave Airlie @ 2014-05-02  4:39 UTC (permalink / raw)
  To: dri-devel

Hi,

Branch: http://cgit.freedesktop.org/~airlied/linux/log/?h=drm-i915-mst-support

Okay I've gotten this to light up some monitors using -modesetting so it
must be ready for an initial posting,

Tested on Haswell so far with Lenovo Ultradock and with Ancell MST Hub devices,
(checked it can detect the Dell 32" Monitor - but that is another world of pain).

The main problem is documented in the i915 patch, state check backtraces abound.

There is still quite a bit of work todo for a compliant solution, but I don't
have a compliance tester so I can only imagine the fail.

The helper interface can probably be cleaned up, the whole ACT area is bit
of a mess, and I'd probably need an idea from other GPUs how best to do that,

Dave.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH 1/8] drm/dp_helper: add defines for DP 1.2 and MST support.
  2014-05-02  4:39 [RFC] drm + i915 DP MST code preview Dave Airlie
@ 2014-05-02  4:39 ` Dave Airlie
  2014-05-02  4:39 ` [PATCH 2/8] drm: add DP MST encoder type Dave Airlie
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 22+ messages in thread
From: Dave Airlie @ 2014-05-02  4:39 UTC (permalink / raw)
  To: dri-devel

From: Dave Airlie <airlied@redhat.com>

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

Signed-off-by: Dave Airlie <airlied@redhat.com>
---
 include/drm/drm_dp_helper.h | 78 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index cfcacec..879836d 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -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
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 2/8] drm: add DP MST encoder type
  2014-05-02  4:39 [RFC] drm + i915 DP MST code preview Dave Airlie
  2014-05-02  4:39 ` [PATCH 1/8] drm/dp_helper: add defines for DP 1.2 and MST support Dave Airlie
@ 2014-05-02  4:39 ` Dave Airlie
  2014-05-02  4:39 ` [PATCH 3/8] drm/i915: add some registers need for displayport MST support Dave Airlie
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 22+ messages in thread
From: Dave Airlie @ 2014-05-02  4:39 UTC (permalink / raw)
  To: dri-devel

From: Dave Airlie <airlied@redhat.com>

This adds an encoder type for DP MST encoders.

Signed-off-by: Dave Airlie <airlied@redhat.com>
---
 drivers/gpu/drm/drm_crtc.c  | 1 +
 include/uapi/drm/drm_mode.h | 1 +
 2 files changed, 2 insertions(+)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index a3fe324..f1753e6 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -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[] =
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index f104c26..719add4 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -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;
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 3/8] drm/i915: add some registers need for displayport MST support.
  2014-05-02  4:39 [RFC] drm + i915 DP MST code preview Dave Airlie
  2014-05-02  4:39 ` [PATCH 1/8] drm/dp_helper: add defines for DP 1.2 and MST support Dave Airlie
  2014-05-02  4:39 ` [PATCH 2/8] drm: add DP MST encoder type Dave Airlie
@ 2014-05-02  4:39 ` Dave Airlie
  2014-05-02  4:39 ` [PATCH 4/8] drm/crtc: add interface to reinitialise the legacy mode group Dave Airlie
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 22+ messages in thread
From: Dave Airlie @ 2014-05-02  4:39 UTC (permalink / raw)
  To: dri-devel

From: Dave Airlie <airlied@redhat.com>

These are just from the Haswell spec.

Signed-off-by: Dave Airlie <airlied@redhat.com>
---
 drivers/gpu/drm/i915/i915_reg.h | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 8f84555..557b37a 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -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
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 4/8] drm/crtc: add interface to reinitialise the legacy mode group
  2014-05-02  4:39 [RFC] drm + i915 DP MST code preview Dave Airlie
                   ` (2 preceding siblings ...)
  2014-05-02  4:39 ` [PATCH 3/8] drm/i915: add some registers need for displayport MST support Dave Airlie
@ 2014-05-02  4:39 ` Dave Airlie
  2014-05-02  4:39 ` [PATCH 5/8] drm/helper: add Displayport multi-stream helper Dave Airlie
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 22+ messages in thread
From: Dave Airlie @ 2014-05-02  4:39 UTC (permalink / raw)
  To: dri-devel

From: Dave Airlie <airlied@redhat.com>

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

Signed-off-by: Dave Airlie <airlied@redhat.com>
---
 drivers/gpu/drm/drm_crtc.c | 9 +++++++++
 include/drm/drm_crtc.h     | 1 +
 2 files changed, 10 insertions(+)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index f1753e6..8bf87a6 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -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
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index c6b9e8a..55bc523 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -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);
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 5/8] drm/helper: add Displayport multi-stream helper
  2014-05-02  4:39 [RFC] drm + i915 DP MST code preview Dave Airlie
                   ` (3 preceding siblings ...)
  2014-05-02  4:39 ` [PATCH 4/8] drm/crtc: add interface to reinitialise the legacy mode group Dave Airlie
@ 2014-05-02  4:39 ` Dave Airlie
  2014-05-02  4:39 ` [PATCH 6/8] i915: split some DP modesetting code into a separate function Dave Airlie
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 22+ messages in thread
From: Dave Airlie @ 2014-05-02  4:39 UTC (permalink / raw)
  To: dri-devel

From: Dave Airlie <airlied@redhat.com>

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.

TODO:
cleanup
locking review
fix known bug with UP message when an MST device is connected to an MST device.

Signed-off-by: Dave Airlie <airlied@redhat.com>
---
 drivers/gpu/drm/Makefile              |    2 +-
 drivers/gpu/drm/drm_dp_mst_topology.c | 2081 +++++++++++++++++++++++++++++++++
 include/drm/drm_dp_mst_helper.h       |  403 +++++++
 3 files changed, 2485 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/drm_dp_mst_topology.c
 create mode 100644 include/drm/drm_dp_mst_helper.h

diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 48e38ba..712b73e 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -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
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
new file mode 100644
index 0000000..c6a0881
--- /dev/null
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -0,0 +1,2081 @@
+/*
+ * Copyright © 2014 Red Hat
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/i2c.h>
+#include <drm/drm_dp_mst_helper.h>
+#include <drm/drmP.h>
+
+#include <drm/drm_fixed.h>
+
+static int test_calc_pbn_mode(void);
+
+static void drm_dp_put_port(struct drm_dp_mst_port *port);
+
+static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req,
+				       struct drm_dp_sideband_msg_tx *raw);
+static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr,
+				 struct drm_dp_sideband_msg_tx *txmsg);
+static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
+					   struct drm_dp_mst_branch *mstb,
+					   int port_num);
+
+static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
+				     int id,
+				     struct drm_dp_payload *payload);
+
+#if 0
+static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
+				 struct drm_dp_mst_port *port,
+				 int offset, int size);
+#endif
+
+static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
+				  struct drm_dp_mst_port *port,
+				  int offset, int size, u8 *bytes);
+
+static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
+				    struct drm_dp_mst_branch *mstb);
+static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
+				 u8 *guid);
+
+static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,
+				    struct drm_dp_sideband_msg_tx *txmsg)
+{
+	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
+	int ret;
+
+	ret = wait_event_timeout(mgr->tx_waitq,
+				 (txmsg->state == DRM_DP_SIDEBAND_TX_RX ||
+				  txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT),
+				 (4 * HZ));
+	if (ret > 0) {
+		if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) {
+			ret = -EIO;
+			goto out;
+		}
+	} else {
+		DRM_DEBUG_KMS("timedout msg send %p %d\n", txmsg, txmsg->state);
+		/* remove from q */
+		if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED ||
+		    txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND) {
+			mutex_lock(&mgr->lock);
+			list_del(&txmsg->next);
+			mutex_unlock(&mgr->lock);
+		}
+
+		if (txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND ||
+		    txmsg->state == DRM_DP_SIDEBAND_TX_SENT) {
+			spin_lock(&mstb->slots_lock);
+			mstb->tx_slots[txmsg->seqno] = NULL;
+			spin_unlock(&mstb->slots_lock);
+		}
+	}
+out:
+	return ret;
+}
+static int drm_dp_mst_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
+			       int num)
+{
+	struct drm_dp_aux *aux = adapter->algo_data;
+	struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port, aux);
+	struct drm_dp_mst_branch *mstb = port->parent;
+	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
+	unsigned int i;
+	bool reading = false;
+	struct drm_dp_sideband_msg_req_body msg;
+	struct drm_dp_sideband_msg_tx *txmsg = NULL;
+	int ret;
+	/* construct i2c msg */
+	/* see if last msg is a read */
+	if (msgs[num - 1].flags & I2C_M_RD)
+		reading = true;
+
+	if (!reading) {
+		DRM_DEBUG_KMS("Unsupported I2C transaction for MST device\n");
+		ret = -EIO;
+		goto out;
+	}
+
+	msg.req_type = DP_REMOTE_I2C_READ;
+	msg.u.i2c_read.num_transactions = num - 1;
+	msg.u.i2c_read.port_number = port->port_num;
+	for (i = 0; i < num - 1; i++) {
+		msg.u.i2c_read.transactions[i].i2c_dev_id = msgs[i].addr;
+		msg.u.i2c_read.transactions[i].num_bytes = msgs[i].len;
+		memcpy(&msg.u.i2c_read.transactions[i].bytes, msgs[i].buf, msgs[i].len);
+	}
+	msg.u.i2c_read.read_i2c_device_id = msgs[num - 1].addr;
+	msg.u.i2c_read.num_bytes_read = msgs[num - 1].len;
+
+	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+	if (!txmsg) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	txmsg->dst = mstb;
+	drm_dp_encode_sideband_req(&msg, txmsg);
+
+	mutex_lock(&mgr->lock);
+	drm_dp_queue_down_tx(mgr, txmsg);
+	mutex_unlock(&mgr->lock);
+
+	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+	if (ret > 0) {
+
+		if (txmsg->reply.reply_type == 1) { /* got a NAK back */
+			ret = -EIO;
+			goto out;
+		}
+		if (txmsg->reply.u.remote_i2c_read_ack.num_bytes != msgs[num - 1].len) {
+			ret = -EIO;
+			goto out;
+		}
+		memcpy(msgs[num - 1].buf, txmsg->reply.u.remote_i2c_read_ack.bytes, msgs[num - 1].len);
+		ret = num;
+	}
+out:
+	kfree(txmsg);
+	return ret;
+}
+
+static u32 drm_dp_mst_i2c_functionality(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+	       I2C_FUNC_SMBUS_READ_BLOCK_DATA |
+	       I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+	       I2C_FUNC_10BIT_ADDR;
+}
+
+static const struct i2c_algorithm drm_dp_mst_i2c_algo = {
+	.functionality = drm_dp_mst_i2c_functionality,
+	.master_xfer = drm_dp_mst_i2c_xfer,
+};
+
+/**
+ * drm_dp_mst_register_i2c_bus() - register an I2C adapter for I2C-over-AUX
+ * @aux: DisplayPort AUX channel
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux)
+{
+	aux->ddc.algo = &drm_dp_mst_i2c_algo;
+	aux->ddc.algo_data = aux;
+	aux->ddc.retries = 3;
+
+	aux->ddc.class = I2C_CLASS_DDC;
+	aux->ddc.owner = THIS_MODULE;
+	aux->ddc.dev.parent = aux->dev;
+	aux->ddc.dev.of_node = aux->dev->of_node;
+
+	strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev),
+		sizeof(aux->ddc.name));
+
+	return i2c_add_adapter(&aux->ddc);
+}
+
+/**
+ * drm_dp_mst_unregister_i2c_bus() - unregister an I2C-over-AUX adapter
+ * @aux: DisplayPort AUX channel
+ */
+void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux)
+{
+	i2c_del_adapter(&aux->ddc);
+}
+
+static struct drm_dp_mst_branch *drm_dp_add_mst_branch_device(u8 lct, u8 *rad)
+{
+	struct drm_dp_mst_branch *mstb;
+
+	mstb = kzalloc(sizeof(*mstb), GFP_KERNEL);
+	if (!mstb)
+		return NULL;
+
+	mstb->lct = lct;
+	if (lct > 1)
+		memcpy(mstb->rad, rad, lct / 2);
+	INIT_LIST_HEAD(&mstb->ports);
+	spin_lock_init(&mstb->slots_lock);
+	kref_init(&mstb->kref);
+	return mstb;
+}
+
+static void drm_dp_destroy_mst_branch_device(struct kref *kref)
+{
+	struct drm_dp_mst_branch *mstb = container_of(kref, struct drm_dp_mst_branch, kref);
+	struct drm_dp_mst_port *port, *tmp;
+	bool wake_tx = false;
+	/* destroy all ports */
+	list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
+		drm_dp_put_port(port);
+	}
+
+	/* drop any tx slots msg */
+	spin_lock(&mstb->slots_lock);
+	if (mstb->tx_slots[0]) {
+		mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
+		mstb->tx_slots[0] = NULL;
+		wake_tx = true;
+	}
+	if (mstb->tx_slots[1]) {
+		mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
+		mstb->tx_slots[1] = NULL;
+		wake_tx = true;
+	}
+	spin_unlock(&mstb->slots_lock);
+
+	if (wake_tx)
+		wake_up(&mstb->mgr->tx_waitq);
+	kfree(mstb);
+}
+
+static void drm_dp_put_mst_branch_device(struct drm_dp_mst_branch *mstb)
+{
+	kref_put(&mstb->kref, drm_dp_destroy_mst_branch_device);
+}
+
+static int drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr *mgr, int force)
+{
+	int ret;
+
+	if (force == -1) {
+		ret = find_first_zero_bit(&mgr->payload_mask, DP_MAX_PAYLOAD);
+		if (ret > mgr->max_payloads) {
+			DRM_DEBUG_KMS("out of payload ids %d\n", ret);
+			return -EINVAL;
+		}
+	} else
+		ret = force;
+
+	set_bit(ret, &mgr->payload_mask);
+	return ret;
+}
+
+static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr,
+				      int id)
+{
+	clear_bit(id, &mgr->payload_mask);
+	mgr->proposed_vcpis[id] = NULL;
+}
+
+static void drm_dp_destroy_port(struct kref *kref)
+{
+	struct drm_dp_mst_port *port = container_of(kref, struct drm_dp_mst_port, kref);
+
+	if (port->connector)
+		(*port->parent->mgr->cbs->destroy_connector)(port->parent->mgr, port->connector);
+	drm_dp_mst_unregister_i2c_bus(&port->aux);
+	if (port->mstb)
+		drm_dp_put_mst_branch_device(port->mstb);
+	list_del(&port->next);
+	kfree(port);
+}
+
+static void drm_dp_put_port(struct drm_dp_mst_port *port)
+{
+	kref_put(&port->kref, drm_dp_destroy_port);
+}
+
+static struct drm_dp_mst_port *drm_dp_get_port(struct drm_dp_mst_branch *mstb, u8 port_num)
+{
+	struct drm_dp_mst_port *port;
+
+	list_for_each_entry(port, &mstb->ports, next) {
+		if (port->port_num == port_num) {
+			kref_get(&port->kref);
+			return port;
+		}
+	}
+
+	return NULL;
+}
+
+static void drm_dp_port_teardown_pdt(struct drm_dp_mst_port *port, int old_pdt)
+{
+	switch (old_pdt) {
+	case DP_PEER_DEVICE_DP_LEGACY_CONV:
+	case DP_PEER_DEVICE_SST_SINK:
+		/* remove i2c over sideband */
+		drm_dp_mst_unregister_i2c_bus(&port->aux);
+		break;
+	case DP_PEER_DEVICE_MST_BRANCHING:
+		drm_dp_put_mst_branch_device(port->mstb);
+		port->mstb = NULL;
+		break;
+	}
+}
+
+/*
+ * calculate a new RAD for this MST branch device
+ * if parent has an LCT of 2 then it has 1 nibble of RAD,
+ * if parent has an LCT of 3 then it has 2 nibbles of RAD,
+ */
+static u8 drm_dp_calculate_rad(struct drm_dp_mst_port *port,
+				 u8 *rad)
+{
+	int lct = port->parent->lct;
+	int shift = 4;
+	int idx = lct / 2;
+	if (lct > 1) {
+		memcpy(rad, port->parent->rad, idx);
+		shift = (lct % 2) ? 4 : 0;
+	} else
+		rad[0] = 0;
+
+	rad[idx] |= port->port_num << shift;
+	return lct + 1;
+}
+
+static void drm_dp_port_setup_pdt(struct drm_dp_mst_port *port, u8 *guid)
+{
+	int ret;
+	u8 rad[6], lct;
+
+	switch (port->pdt) {
+	case DP_PEER_DEVICE_DP_LEGACY_CONV:
+	case DP_PEER_DEVICE_SST_SINK:
+		/* add i2c over sideband */
+		ret = drm_dp_mst_register_i2c_bus(&port->aux);
+		break;
+	case DP_PEER_DEVICE_MST_BRANCHING:
+		lct = drm_dp_calculate_rad(port, rad);
+
+		port->mstb = drm_dp_add_mst_branch_device(lct, rad);
+		port->mstb->mgr = port->parent->mgr;
+		port->mstb->parent_is_port = true;
+		port->mstb->port_parent = port;
+
+		drm_dp_send_link_address(port->parent->mgr, port->mstb);
+
+		break;
+	}
+}
+
+static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
+			    struct device *dev,
+			    struct drm_dp_link_addr_reply_port *port_msg)
+{
+	struct drm_dp_mst_port *port;
+	int ret;
+	port = kzalloc(sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return;
+
+	kref_init(&port->kref);
+	port->parent = mstb;
+	port->port_num = port_msg->port_number;
+	port->pdt = port_msg->peer_device_type;
+	port->input = port_msg->input_port;
+	port->mcs = port_msg->mcs;
+	port->ddps = port_msg->ddps;
+	port->ldps = port_msg->legacy_device_plug_status;
+	port->dpcd_rev = port_msg->dpcd_revision;
+	memcpy(port->guid, port_msg->peer_guid, 16);
+	list_add(&port->next, &mstb->ports);
+
+	port->aux.name = "DPMST";
+	port->aux.dev = dev;
+
+	if (port->ddps) {
+		if (port->dpcd_rev >= 0x12) {
+			port->guid_valid = drm_dp_validate_guid(mstb->mgr, port->guid);
+			if (!port->guid_valid) {
+				ret = drm_dp_send_dpcd_write(mstb->mgr,
+							     port,
+							     DP_GUID,
+							     16, port->guid);
+				port->guid_valid = true;
+			}
+		}
+	}
+	if (!port->input && port->ddps)
+		drm_dp_port_setup_pdt(port, port_msg->peer_guid);
+
+	if (!port->input && port->ddps)
+		drm_dp_send_enum_path_resources(mstb->mgr, mstb,
+						port->port_num);
+
+	if (!port->input)
+		port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr, port);
+
+}
+
+static void drm_dp_update_port(struct drm_dp_mst_branch *mstb,
+			       struct drm_dp_connection_status_notify *conn_stat)
+{
+	struct drm_dp_mst_port *port;
+	int old_pdt;
+	port = drm_dp_get_port(mstb, conn_stat->port_number);
+	if (!port)
+		return;
+
+	old_pdt = port->pdt;
+	port->pdt = conn_stat->peer_device_type;
+	port->mcs = conn_stat->message_capability_status;
+	port->ldps = conn_stat->legacy_device_plug_status;
+	port->ddps = conn_stat->displayport_device_plug_status;
+
+	if (old_pdt != port->pdt) {
+		drm_dp_port_teardown_pdt(port, old_pdt);
+		drm_dp_port_setup_pdt(port, conn_stat->guid);
+	}
+
+	if (!port->input && port->ddps)
+		drm_dp_send_enum_path_resources(mstb->mgr, mstb,
+						port->port_num);
+
+	drm_dp_put_port(port);
+}
+
+static void drm_dp_update_port_pbn(struct drm_dp_mst_branch *mstb,
+				   uint8_t port_number,
+				   uint16_t available_pbn)
+{
+	struct drm_dp_mst_port *port;
+
+	port = drm_dp_get_port(mstb, port_number);
+	if (!port)
+		return;
+
+	port->available_pbn = available_pbn;
+	drm_dp_put_port(port);
+}
+
+static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_topology_mgr *mgr,
+							       u8 lct, u8 *rad)
+{
+	struct drm_dp_mst_branch *mstb;
+	struct drm_dp_mst_port *port;
+	int i;
+	/* find the port by iterating down */
+	mstb = mgr->mst_primary;
+
+	for (i = 0; i < lct - 1; i++) {
+		int shift = (i % 2) ? 0 : 4;
+		int port_num = rad[i / 2] >> shift;
+
+		list_for_each_entry(port, &mstb->ports, next) {
+			if (port->port_num == port_num) {
+				if (!port->mstb) {
+					DRM_ERROR("failed to lookup MSTB with lct %d, rad %02x\n", lct, rad[0]);
+					return NULL;
+				}
+
+				mstb = port->mstb;
+				break;
+			}
+		}
+	}
+	kref_get(&mstb->kref);
+	return mstb;
+}
+
+static u8 drm_dp_msg_header_crc4(const uint8_t *data, size_t num_nibbles)
+{
+	u8 bitmask = 0x80;
+	u8 bitshift = 7;
+	u8 array_index = 0;
+	int number_of_bits = num_nibbles * 4;
+	u8 remainder = 0;
+
+	while (number_of_bits != 0) {
+		number_of_bits--;
+		remainder <<= 1;
+		remainder |= (data[array_index] & bitmask) >> bitshift;
+		bitmask >>= 1;
+		bitshift--;
+		if (bitmask == 0) {
+			bitmask = 0x80;
+			bitshift = 7;
+			array_index++;
+		}
+		if ((remainder & 0x10) == 0x10)
+			remainder ^= 0x13;
+	}
+
+	number_of_bits = 4;
+	while (number_of_bits != 0) {
+		number_of_bits--;
+		remainder <<= 1;
+		if ((remainder & 0x10) != 0)
+			remainder ^= 0x13;
+	}
+
+	return remainder;
+}
+
+static u8 drm_dp_msg_data_crc4(const uint8_t *data, u8 number_of_bytes)
+{
+	u8 bitmask = 0x80;
+	u8 bitshift = 7;
+	u8 array_index = 0;
+	int number_of_bits = number_of_bytes * 8;
+	u16 remainder = 0;
+
+	while (number_of_bits != 0) {
+		number_of_bits--;
+		remainder <<= 1;
+		remainder |= (data[array_index] & bitmask) >> bitshift;
+		bitmask >>= 1;
+		bitshift--;
+		if (bitmask == 0) {
+			bitmask = 0x80;
+			bitshift = 7;
+			array_index++;
+		}
+		if ((remainder & 0x100) == 0x100)
+			remainder ^= 0xd5;
+	}
+
+	number_of_bits = 8;
+	while (number_of_bits != 0) {
+		number_of_bits--;
+		remainder <<= 1;
+		if ((remainder & 0x100) != 0)
+			remainder ^= 0xd5;
+	}
+
+	return remainder & 0xff;
+}
+
+static inline u8 drm_dp_calc_sb_hdr_size(struct drm_dp_sideband_msg_hdr *hdr)
+{
+	u8 size = 3;
+	size += (hdr->lct / 2);
+	return size;
+}
+
+static void drm_dp_encode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr,
+					   u8 *buf, int *len)
+{
+	int idx = 0;
+	int i;
+	u8 crc4;
+	buf[idx++] = ((hdr->lct & 0xf) << 4) | (hdr->lcr & 0xf);
+	for (i = 0; i < (hdr->lct / 2); i++)
+		buf[idx++] = hdr->rad[i];
+	buf[idx++] = (hdr->broadcast << 7) | (hdr->path_msg << 6) |
+		(hdr->msg_len & 0x3f);
+	buf[idx++] = (hdr->somt << 7) | (hdr->eomt << 6) | (hdr->seqno << 4);
+
+	crc4 = drm_dp_msg_header_crc4(buf, (idx * 2) - 1);
+	buf[idx - 1] |= (crc4 & 0xf);
+
+	*len = idx;
+}
+
+static bool drm_dp_decode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr,
+					   u8 *buf, int buflen, u8 *hdrlen)
+{
+	u8 crc4;
+	u8 len;
+	int i;
+	u8 idx;
+	if (buf[0] == 0)
+		return false;
+	len = 3;
+	len += ((buf[0] & 0xf0) >> 4) / 2;
+	if (len > buflen)
+		return false;
+	crc4 = drm_dp_msg_header_crc4(buf, (len * 2) - 1);
+
+	if ((crc4 & 0xf) != (buf[len - 1] & 0xf)) {
+		DRM_DEBUG_KMS("crc4 mismatch 0x%x 0x%x\n", crc4, buf[len - 1]);
+		return false;
+	}
+
+	hdr->lct = (buf[0] & 0xf0) >> 4;
+	hdr->lcr = (buf[0] & 0xf);
+	idx = 1;
+	for (i = 0; i < (hdr->lct / 2); i++)
+		hdr->rad[i] = buf[idx++];
+	hdr->broadcast = (buf[idx] >> 7) & 0x1;
+	hdr->path_msg = (buf[idx] >> 6) & 0x1;
+	hdr->msg_len = buf[idx] & 0x3f;
+	idx++;
+	hdr->somt = (buf[idx] >> 7) & 0x1;
+	hdr->eomt = (buf[idx] >> 6) & 0x1;
+	hdr->seqno = (buf[idx] >> 4) & 0x1;
+	idx++;
+	*hdrlen = idx;
+	return true;
+}
+
+static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req,
+				       struct drm_dp_sideband_msg_tx *raw)
+{
+	int idx = 0;
+	int i;
+	u8 *buf = raw->msg;
+	buf[idx++] = req->req_type & 0x7f;
+
+	switch (req->req_type) {
+	case DP_ENUM_PATH_RESOURCES:
+		buf[idx] = (req->u.port_num.port_number & 0xf) << 4;
+		idx++;
+		break;
+	case DP_ALLOCATE_PAYLOAD:
+		buf[idx] = (req->u.allocate_payload.port_number & 0xf) << 4 |
+			(req->u.allocate_payload.number_sdp_streams & 0xf);
+		idx++;
+		buf[idx] = (req->u.allocate_payload.vcpi & 0x7f);
+		idx++;
+		buf[idx] = (req->u.allocate_payload.pbn >> 8);
+		idx++;
+		buf[idx] = (req->u.allocate_payload.pbn & 0xff);
+		idx++;
+		for (i = 0; i < req->u.allocate_payload.number_sdp_streams / 2; i++) {
+			buf[idx] = ((req->u.allocate_payload.sdp_stream_sink[i * 2] & 0xf) << 4) |
+				(req->u.allocate_payload.sdp_stream_sink[i * 2 + 1] & 0xf);
+			idx++;
+		}
+		if (req->u.allocate_payload.number_sdp_streams & 1) {
+			i = req->u.allocate_payload.number_sdp_streams - 1;
+			buf[idx] = (req->u.allocate_payload.sdp_stream_sink[i] & 0xf) << 4;
+			idx++;
+		}
+		break;
+	case DP_QUERY_PAYLOAD:
+		buf[idx] = (req->u.query_payload.port_number & 0xf) << 4;
+		idx++;
+		buf[idx] = (req->u.query_payload.vcpi & 0x7f);
+		idx++;
+		break;
+	case DP_REMOTE_DPCD_READ:
+		buf[idx] = (req->u.dpcd_read.port_number & 0xf) << 4;
+		buf[idx] |= ((req->u.dpcd_read.dpcd_address & 0xf0000) >> 16) & 0xf;
+		idx++;
+		buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff00) >> 8;
+		idx++;
+		buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff);
+		idx++;
+		buf[idx] = (req->u.dpcd_read.num_bytes);
+		idx++;
+		break;
+
+	case DP_REMOTE_DPCD_WRITE:
+		buf[idx] = (req->u.dpcd_write.port_number & 0xf) << 4;
+		buf[idx] |= ((req->u.dpcd_write.dpcd_address & 0xf0000) >> 16) & 0xf;
+		idx++;
+		buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff00) >> 8;
+		idx++;
+		buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff);
+		idx++;
+		buf[idx] = (req->u.dpcd_write.num_bytes);
+		idx++;
+		memcpy(&buf[idx], req->u.dpcd_write.bytes, req->u.dpcd_write.num_bytes);
+		idx += req->u.dpcd_write.num_bytes;
+		break;
+	case DP_REMOTE_I2C_READ:
+		buf[idx] = (req->u.i2c_read.port_number & 0xf) << 4;
+		buf[idx] |= (req->u.i2c_read.num_transactions & 0x3);
+		idx++;
+		for (i = 0; i < (req->u.i2c_read.num_transactions & 0x3); i++) {
+			buf[idx] = req->u.i2c_read.transactions[i].i2c_dev_id & 0x7f;
+			idx++;
+			buf[idx] = req->u.i2c_read.transactions[i].num_bytes;
+			idx++;
+			memcpy(&buf[idx], req->u.i2c_read.transactions[i].bytes, req->u.i2c_read.transactions[i].num_bytes);
+			idx += req->u.i2c_read.transactions[i].num_bytes;
+
+			buf[idx] = (req->u.i2c_read.transactions[i].no_stop_bit & 0x1) << 5;
+			buf[idx] |= (req->u.i2c_read.transactions[i].i2c_transaction_delay & 0xf);
+			idx++;
+		}
+		buf[idx] = (req->u.i2c_read.read_i2c_device_id) & 0x7f;
+		idx++;
+		buf[idx] = (req->u.i2c_read.num_bytes_read);
+		idx++;
+		break;
+
+	case DP_REMOTE_I2C_WRITE:
+		buf[idx] = (req->u.i2c_write.port_number & 0xf) << 4;
+		idx++;
+		buf[idx] = (req->u.i2c_write.write_i2c_device_id) & 0x7f;
+		idx++;
+		buf[idx] = (req->u.i2c_write.num_bytes);
+		idx++;
+		memcpy(&buf[idx], req->u.i2c_write.bytes, req->u.i2c_write.num_bytes);
+		idx += req->u.i2c_write.num_bytes;
+		break;
+	}
+	raw->cur_len = idx;
+}
+
+static void drm_dp_crc_sideband_chunk_req(u8 *msg, u8 len)
+{
+	u8 crc4;
+	crc4 = drm_dp_msg_data_crc4(msg, len);
+	msg[len] = crc4;
+}
+
+static void drm_dp_encode_sideband_reply(struct drm_dp_sideband_msg_reply_body *rep,
+					 struct drm_dp_sideband_msg_tx *raw)
+{
+	int idx = 0;
+	u8 *buf = raw->msg;
+
+	buf[idx++] = (rep->reply_type & 0x1) << 7 | (rep->req_type & 0x7f);
+
+	raw->cur_len = idx;
+}
+
+/* this adds a chunk of msg to the builder to get the final msg */
+static bool drm_dp_sideband_msg_build(struct drm_dp_sideband_msg_rx *msg,
+				      u8 *replybuf, u8 replybuflen, bool hdr)
+{
+	int ret;
+	u8 crc4;
+
+	if (hdr) {
+		u8 hdrlen;
+		struct drm_dp_sideband_msg_hdr recv_hdr;
+		ret = drm_dp_decode_sideband_msg_hdr(&recv_hdr, replybuf, replybuflen, &hdrlen);
+		if (ret == false)
+			return false;
+
+		/* get length contained in this portion */
+		msg->curchunk_len = recv_hdr.msg_len;
+		msg->curchunk_hdrlen = hdrlen;
+
+		/* we have already gotten an somt - don't bother parsing */
+		if (recv_hdr.somt && msg->have_somt)
+			return false;
+
+		if (recv_hdr.somt) {
+			memcpy(&msg->initial_hdr, &recv_hdr, sizeof(struct drm_dp_sideband_msg_hdr));
+			msg->have_somt = true;
+		}
+		if (recv_hdr.eomt)
+			msg->have_eomt = true;
+
+		/* copy the bytes for the remainder of this header chunk */
+		msg->curchunk_idx = min(msg->curchunk_len, (u8)(replybuflen - hdrlen));
+		memcpy(&msg->chunk[0], replybuf + hdrlen, msg->curchunk_idx);
+	} else {
+		memcpy(&msg->chunk[msg->curchunk_idx], replybuf, replybuflen);
+		msg->curchunk_idx += replybuflen;
+	}
+
+	if (msg->curchunk_idx >= msg->curchunk_len) {
+		/* do CRC */
+		crc4 = drm_dp_msg_data_crc4(msg->chunk, msg->curchunk_len - 1);
+		/* copy chunk into bigger msg */
+		memcpy(&msg->msg[msg->curlen], msg->chunk, msg->curchunk_len - 1);
+		msg->curlen += msg->curchunk_len - 1;
+	}
+	return true;
+}
+
+static bool drm_dp_sideband_parse_link_address(struct drm_dp_sideband_msg_rx *raw,
+					       struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+	int idx = 1;
+	int i;
+	memcpy(repmsg->u.link_addr.guid, &raw->msg[idx], 16);
+	idx += 16;
+	repmsg->u.link_addr.nports = raw->msg[idx] & 0xf;
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+	for (i = 0; i < repmsg->u.link_addr.nports; i++) {
+		if (raw->msg[idx] & 0x80)
+			repmsg->u.link_addr.ports[i].input_port = 1;
+
+		repmsg->u.link_addr.ports[i].peer_device_type = (raw->msg[idx] >> 4) & 0x7;
+		repmsg->u.link_addr.ports[i].port_number = (raw->msg[idx] & 0xf);
+
+		idx++;
+		if (idx > raw->curlen)
+			goto fail_len;
+		repmsg->u.link_addr.ports[i].mcs = (raw->msg[idx] >> 7) & 0x1;
+		repmsg->u.link_addr.ports[i].ddps = (raw->msg[idx] >> 6) & 0x1;
+		if (repmsg->u.link_addr.ports[i].input_port == 0)
+			repmsg->u.link_addr.ports[i].legacy_device_plug_status = (raw->msg[idx] >> 5) & 0x1;
+		idx++;
+		if (idx > raw->curlen)
+			goto fail_len;
+		if (repmsg->u.link_addr.ports[i].input_port == 0) {
+			repmsg->u.link_addr.ports[i].dpcd_revision = (raw->msg[idx]);
+			idx++;
+			if (idx > raw->curlen)
+				goto fail_len;
+			memcpy(repmsg->u.link_addr.ports[i].peer_guid, &raw->msg[idx], 16);
+			idx += 16;
+			if (idx > raw->curlen)
+				goto fail_len;
+			repmsg->u.link_addr.ports[i].num_sdp_streams = (raw->msg[idx] >> 4) & 0xf;
+			repmsg->u.link_addr.ports[i].num_sdp_stream_sinks = (raw->msg[idx] & 0xf);
+			idx++;
+
+		}
+		if (idx > raw->curlen)
+			goto fail_len;
+	}
+
+	return true;
+fail_len:
+	DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen);
+	return false;
+}
+
+static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx *raw,
+						   struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+	int idx = 1;
+	repmsg->u.remote_dpcd_read_ack.port_number = raw->msg[idx] & 0xf;
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+	repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx];
+	if (idx > raw->curlen)
+		goto fail_len;
+
+	memcpy(repmsg->u.remote_dpcd_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_dpcd_read_ack.num_bytes);
+	return true;
+fail_len:
+	DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen);
+	return false;
+}
+
+static bool drm_dp_sideband_parse_remote_dpcd_write(struct drm_dp_sideband_msg_rx *raw,
+						      struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+	int idx = 1;
+	repmsg->u.remote_dpcd_write_ack.port_number = raw->msg[idx] & 0xf;
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+	return true;
+fail_len:
+	DRM_DEBUG_KMS("parse length fail %d %d\n", idx, raw->curlen);
+	return false;
+}
+
+static bool drm_dp_sideband_parse_remote_i2c_read_ack(struct drm_dp_sideband_msg_rx *raw,
+						      struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+	int idx = 1;
+
+	repmsg->u.remote_i2c_read_ack.port_number = (raw->msg[idx] & 0xf);
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+	repmsg->u.remote_i2c_read_ack.num_bytes = raw->msg[idx];
+	idx++;
+	/* TODO check */
+	memcpy(repmsg->u.remote_i2c_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_i2c_read_ack.num_bytes);
+	return true;
+fail_len:
+	DRM_DEBUG_KMS("remote i2c reply parse length fail %d %d\n", idx, raw->curlen);
+	return false;
+}
+
+static bool drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband_msg_rx *raw,
+							  struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+	int idx = 1;
+	repmsg->u.path_resources.port_number = (raw->msg[idx] >> 4) & 0xf;
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+	repmsg->u.path_resources.full_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
+	idx += 2;
+	if (idx > raw->curlen)
+		goto fail_len;
+	repmsg->u.path_resources.avail_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
+	idx += 2;
+	if (idx > raw->curlen)
+		goto fail_len;
+	return true;
+fail_len:
+	DRM_DEBUG_KMS("enum resource parse length fail %d %d\n", idx, raw->curlen);
+	return false;
+}
+
+static bool drm_dp_sideband_parse_allocate_payload_ack(struct drm_dp_sideband_msg_rx *raw,
+							  struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+	int idx = 1;
+	repmsg->u.allocate_payload.port_number = (raw->msg[idx] >> 4) & 0xf;
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+	repmsg->u.allocate_payload.vcpi = raw->msg[idx];
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+	repmsg->u.allocate_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
+	idx += 2;
+	if (idx > raw->curlen)
+		goto fail_len;
+	return true;
+fail_len:
+	DRM_DEBUG_KMS("allocate payload parse length fail %d %d\n", idx, raw->curlen);
+	return false;
+}
+
+static bool drm_dp_sideband_parse_query_payload_ack(struct drm_dp_sideband_msg_rx *raw,
+						    struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+	int idx = 1;
+	repmsg->u.query_payload.port_number = (raw->msg[idx] >> 4) & 0xf;
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+	repmsg->u.query_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
+	idx += 2;
+	if (idx > raw->curlen)
+		goto fail_len;
+	return true;
+fail_len:
+	DRM_DEBUG_KMS("query payload parse length fail %d %d\n", idx, raw->curlen);
+	return false;
+}
+
+static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw,
+					struct drm_dp_sideband_msg_reply_body *msg)
+{
+	memset(msg, 0, sizeof(*msg));
+	msg->reply_type = (raw->msg[0] & 0x80) >> 7;
+	msg->req_type = (raw->msg[0] & 0x7f);
+
+	if (msg->reply_type) {
+		memcpy(msg->u.nak.guid, &raw->msg[1], 16);
+		msg->u.nak.reason = raw->msg[17];
+		msg->u.nak.nak_data = raw->msg[18];
+		return false;
+	}
+
+	switch (msg->req_type) {
+	case DP_LINK_ADDRESS:
+		return drm_dp_sideband_parse_link_address(raw, msg);
+	case DP_QUERY_PAYLOAD:
+		return drm_dp_sideband_parse_query_payload_ack(raw, msg);
+	case DP_REMOTE_DPCD_READ:
+		return drm_dp_sideband_parse_remote_dpcd_read(raw, msg);
+	case DP_REMOTE_DPCD_WRITE:
+		return drm_dp_sideband_parse_remote_dpcd_write(raw, msg);
+	case DP_REMOTE_I2C_READ:
+		return drm_dp_sideband_parse_remote_i2c_read_ack(raw, msg);
+	case DP_ENUM_PATH_RESOURCES:
+		return drm_dp_sideband_parse_enum_path_resources_ack(raw, msg);
+	case DP_ALLOCATE_PAYLOAD:
+		return drm_dp_sideband_parse_allocate_payload_ack(raw, msg);
+	default:
+		DRM_ERROR("Got unknown reply 0x%02x\n", msg->req_type);
+		return false;
+	}
+}
+
+static bool drm_dp_sideband_parse_connection_status_notify(struct drm_dp_sideband_msg_rx *raw,
+							   struct drm_dp_sideband_msg_req_body *msg)
+{
+	int idx = 1;
+
+	msg->u.conn_stat.port_number = (raw->msg[idx] & 0xf0) >> 4;
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+
+	memcpy(msg->u.conn_stat.guid, &raw->msg[idx], 16);
+	idx += 16;
+	if (idx > raw->curlen)
+		goto fail_len;
+
+	msg->u.conn_stat.legacy_device_plug_status = (raw->msg[idx] >> 6) & 0x1;
+	msg->u.conn_stat.displayport_device_plug_status = (raw->msg[idx] >> 5) & 0x1;
+	msg->u.conn_stat.message_capability_status = (raw->msg[idx] >> 4) & 0x1;
+	msg->u.conn_stat.input_port = (raw->msg[idx] >> 3) & 0x1;
+	msg->u.conn_stat.peer_device_type = (raw->msg[idx] & 0x7);
+	idx++;
+	return true;
+fail_len:
+	DRM_DEBUG_KMS("connection status reply parse length fail %d %d\n", idx, raw->curlen);
+	return false;
+}
+
+static bool drm_dp_sideband_parse_resource_status_notify(struct drm_dp_sideband_msg_rx *raw,
+							   struct drm_dp_sideband_msg_req_body *msg)
+{
+	int idx = 1;
+
+	msg->u.resource_stat.port_number = (raw->msg[idx] & 0xf0) >> 4;
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+
+	memcpy(msg->u.resource_stat.guid, &raw->msg[idx], 16);
+	idx += 16;
+	if (idx > raw->curlen)
+		goto fail_len;
+
+	msg->u.resource_stat.available_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
+	idx++;
+	return true;
+fail_len:
+	DRM_DEBUG_KMS("resource status reply parse length fail %d %d\n", idx, raw->curlen);
+	return false;
+}
+static bool drm_dp_sideband_parse_req(struct drm_dp_sideband_msg_rx *raw,
+				      struct drm_dp_sideband_msg_req_body *msg)
+{
+	memset(msg, 0, sizeof(*msg));
+	msg->req_type = (raw->msg[0] & 0x7f);
+
+	switch (msg->req_type) {
+	case DP_CONNECTION_STATUS_NOTIFY:
+		return drm_dp_sideband_parse_connection_status_notify(raw, msg);
+	case DP_RESOURCE_STATUS_NOTIFY:
+		return drm_dp_sideband_parse_resource_status_notify(raw, msg);
+	default:
+		DRM_ERROR("Got unknown request 0x%02x\n", msg->req_type);
+		return false;
+	}
+}
+
+static void drm_dp_mst_link_probe_work(struct work_struct *work)
+{
+	struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, work);
+
+	drm_dp_send_link_address(mgr, mgr->mst_primary);
+}
+
+int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
+				 struct device *dev, struct drm_dp_aux *aux,
+				 int max_dpcd_transaction_bytes,
+				 int max_payloads)
+{
+	mutex_init(&mgr->lock);
+	spin_lock_init(&mgr->qlock);
+	INIT_LIST_HEAD(&mgr->tx_msg_upq);
+	INIT_LIST_HEAD(&mgr->tx_msg_downq);
+	INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work);
+	init_waitqueue_head(&mgr->tx_waitq);
+	mgr->dev = dev;
+	mgr->aux = aux;
+	mgr->max_dpcd_transaction_bytes = max_dpcd_transaction_bytes;
+	mgr->max_payloads = max_payloads;
+	mgr->payloads = kcalloc(max_payloads, sizeof(struct drm_dp_payload), GFP_KERNEL);
+	if (!mgr->payloads)
+		return -ENOMEM;
+	mgr->proposed_vcpis = kcalloc(max_payloads, sizeof(struct drm_dp_vcpi *), GFP_KERNEL);
+	if (!mgr->proposed_vcpis)
+		return -ENOMEM;
+	set_bit(0, &mgr->payload_mask);
+	test_calc_pbn_mode();
+	return 0;
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init);
+
+void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
+{
+	kfree(mgr->payloads);
+	kfree(mgr->proposed_vcpis);
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);
+
+/* this is pretty bogus */
+static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
+				 u8 *guid)
+{
+	static u8 zero_guid[16];
+
+	if (!memcmp(guid, zero_guid, 16)) {
+		u64 salt = get_jiffies_64();
+		memcpy(&guid[0], &salt, sizeof(u64));
+		memcpy(&guid[8], &salt, sizeof(u64));
+		return false;
+	}
+	return true;
+}
+
+#if 0
+static int build_dpcd_read(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes)
+{
+	struct drm_dp_sideband_msg_req_body req;
+
+	req.req_type = DP_REMOTE_DPCD_READ;
+	req.u.dpcd_read.port_number = port_num;
+	req.u.dpcd_read.dpcd_address = offset;
+	req.u.dpcd_read.num_bytes = num_bytes;
+	drm_dp_encode_sideband_req(&req, msg);
+
+	return 0;
+}
+#endif
+
+static int build_dpcd_write(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes, u8 *bytes)
+{
+	struct drm_dp_sideband_msg_req_body req;
+
+	req.req_type = DP_REMOTE_DPCD_WRITE;
+	req.u.dpcd_write.port_number = port_num;
+	req.u.dpcd_write.dpcd_address = offset;
+	req.u.dpcd_write.num_bytes = num_bytes;
+	memcpy(req.u.dpcd_write.bytes, bytes, num_bytes);
+	drm_dp_encode_sideband_req(&req, msg);
+
+	return 0;
+}
+
+static int build_link_address(struct drm_dp_sideband_msg_tx *msg)
+{
+	struct drm_dp_sideband_msg_req_body req;
+
+	req.req_type = DP_LINK_ADDRESS;
+	drm_dp_encode_sideband_req(&req, msg);
+	return 0;
+}
+
+static int build_enum_path_resources(struct drm_dp_sideband_msg_tx *msg, int port_num)
+{
+	struct drm_dp_sideband_msg_req_body req;
+
+	req.req_type = DP_ENUM_PATH_RESOURCES;
+	req.u.port_num.port_number = port_num;
+	drm_dp_encode_sideband_req(&req, msg);
+	return 0;
+}
+
+static int build_allocate_payload(struct drm_dp_sideband_msg_tx *msg, int port_num,
+				  u8 vcpi, uint16_t pbn)
+{
+	struct drm_dp_sideband_msg_req_body req;
+	memset(&req, 0, sizeof(req));
+	req.req_type = DP_ALLOCATE_PAYLOAD;
+	req.u.allocate_payload.port_number = port_num;
+	req.u.allocate_payload.vcpi = vcpi;
+	req.u.allocate_payload.pbn = pbn;
+	drm_dp_encode_sideband_req(&req, msg);
+	return 0;
+}
+
+static int drm_dp_send_sideband_msg(struct drm_dp_mst_topology_mgr *mgr,
+				    bool up, u8 *msg, int len)
+{
+	int ret;
+	int regbase = up ? DP_SIDEBAND_MSG_UP_REP_BASE : DP_SIDEBAND_MSG_DOWN_REQ_BASE;
+	int tosend, total, offset;
+
+	total = len;
+	offset = 0;
+	do {
+		tosend = min3(mgr->max_dpcd_transaction_bytes, 32, total);
+
+		ret = drm_dp_dpcd_write(mgr->aux, regbase + offset,
+					&msg[offset],
+					tosend);
+		offset += tosend;
+		total -= tosend;
+	} while (total > 0);
+	return ret;
+}
+
+static int set_hdr_from_dst(struct drm_dp_sideband_msg_hdr *hdr,
+			    struct drm_dp_sideband_msg_tx *txmsg)
+{
+	struct drm_dp_mst_branch *mstb = txmsg->dst;
+
+	/* both msg slots are full */
+	if (txmsg->seqno == -1) {
+		spin_lock(&mstb->slots_lock);
+		if (mstb->tx_slots[0] && mstb->tx_slots[1]) {
+			DRM_DEBUG_KMS("%s: failed to find slot\n", __func__);
+			spin_unlock(&mstb->slots_lock);
+			return -EAGAIN;
+		}
+		if (mstb->tx_slots[0] == NULL)
+			txmsg->seqno = 0;
+		else
+			txmsg->seqno = 1;
+		mstb->tx_slots[txmsg->seqno] = txmsg;
+		spin_unlock(&mstb->slots_lock);
+	}
+	hdr->broadcast = 0;
+	hdr->path_msg = 0;
+	hdr->lct = mstb->lct;
+	hdr->lcr = mstb->lct - 1;
+	if (mstb->lct > 1)
+		memcpy(hdr->rad, mstb->rad, mstb->lct / 2);
+	hdr->seqno = txmsg->seqno;
+	return 0;
+}
+/*
+ * process a single block of the next message in the sideband queue
+ */
+static int process_single_tx(struct drm_dp_mst_topology_mgr *mgr,
+			      struct drm_dp_sideband_msg_tx *txmsg,
+			      bool up)
+{
+	u8 chunk[48];
+	struct drm_dp_sideband_msg_hdr hdr;
+	int len, space, idx, tosend;
+	int ret;
+
+	/* make hdr from dst mst - for replies use seqno
+	   otherwise assign one */
+	ret = set_hdr_from_dst(&hdr, txmsg);
+	if (ret < 0)
+		return ret;
+
+	txmsg->state = DRM_DP_SIDEBAND_TX_START_SEND;
+
+	/* amount left to send in this message */
+	len = txmsg->cur_len - txmsg->cur_offset;
+
+	/* 48 - sideband msg size - 1 byte for data CRC, x header bytes */
+	space = 48 - 1 - drm_dp_calc_sb_hdr_size(&hdr);
+
+	tosend = min(len, space);
+	if (len == txmsg->cur_len)
+		hdr.somt = 1;
+	if (space >= len)
+		hdr.eomt = 1;
+
+	hdr.msg_len = tosend + 1;
+	drm_dp_encode_sideband_msg_hdr(&hdr, chunk, &idx);
+	memcpy(&chunk[idx], &txmsg->msg[txmsg->cur_offset], tosend);
+	/* add crc at end */
+	drm_dp_crc_sideband_chunk_req(&chunk[idx], tosend);
+	idx += tosend + 1;
+	drm_dp_send_sideband_msg(mgr, up, chunk, idx);
+
+	txmsg->cur_offset += tosend;
+	if (txmsg->cur_offset == txmsg->cur_len) {
+		txmsg->state = DRM_DP_SIDEBAND_TX_SENT;
+		return 1;
+	}
+	return 0;
+}
+
+static void process_single_down_tx(struct drm_dp_mst_topology_mgr *mgr)
+{
+	struct drm_dp_sideband_msg_tx *txmsg;
+	int ret;
+
+	/* construct a chunk from the first msg in the tx_msg queue */
+	if (list_empty(&mgr->tx_msg_downq)) {
+		mgr->tx_down_in_progress = false;
+		return;
+	}
+
+	txmsg = list_first_entry(&mgr->tx_msg_downq, struct drm_dp_sideband_msg_tx, next);
+	txmsg->seqno = -1;
+	ret = process_single_tx(mgr, txmsg, false);
+	if (ret == 1) {
+		/* txmsg is sent it should be in the slots now */
+		list_del(&txmsg->next);
+	}
+	mgr->tx_down_in_progress = true;
+}
+
+static void process_single_up_tx(struct drm_dp_mst_topology_mgr *mgr)
+{
+	struct drm_dp_sideband_msg_tx *txmsg;
+	int ret;
+	/* construct a chunk from the first msg in the tx_msg queue */
+	if (list_empty(&mgr->tx_msg_upq)) {
+		mgr->tx_up_in_progress = false;
+		return;
+	}
+
+	txmsg = list_first_entry(&mgr->tx_msg_upq, struct drm_dp_sideband_msg_tx, next);
+	ret = process_single_tx(mgr, txmsg, true);
+	if (ret == 1) {
+		/* up txmsgs aren't put in slots - so free after we send it */
+		list_del(&txmsg->next);
+		kfree(txmsg);
+	}
+	mgr->tx_up_in_progress = true;
+}
+
+static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr,
+				 struct drm_dp_sideband_msg_tx *txmsg)
+{
+	list_add_tail(&txmsg->next, &mgr->tx_msg_downq);
+	if (!mgr->tx_down_in_progress)
+		process_single_down_tx(mgr);
+
+}
+
+static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
+				    struct drm_dp_mst_branch *mstb)
+{
+	int len;
+	struct drm_dp_sideband_msg_tx *txmsg;
+	int ret;
+	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+	if (!txmsg)
+		return -ENOMEM;
+
+	txmsg->dst = mstb;
+	len = build_link_address(txmsg);
+
+	drm_dp_queue_down_tx(mgr, txmsg);
+
+	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+	if (ret > 0) {
+		int i;
+
+		if (txmsg->reply.reply_type == 1)
+			DRM_DEBUG_KMS("link address nak received\n");
+		else {
+			DRM_DEBUG_KMS("link address reply: %d\n", txmsg->reply.u.link_addr.nports);
+			for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) {
+				DRM_DEBUG_KMS("port %d: input %d, pdt: %d, pn: %d, dpcd_rev: %02x, mcs: %d, ddps: %d, ldps %d\n", i,
+				       txmsg->reply.u.link_addr.ports[i].input_port,
+				       txmsg->reply.u.link_addr.ports[i].peer_device_type,
+				       txmsg->reply.u.link_addr.ports[i].port_number,
+				       txmsg->reply.u.link_addr.ports[i].dpcd_revision,
+				       txmsg->reply.u.link_addr.ports[i].mcs,
+				       txmsg->reply.u.link_addr.ports[i].ddps,
+				       txmsg->reply.u.link_addr.ports[i].legacy_device_plug_status);
+			}
+			for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) {
+				drm_dp_add_port(mstb, mgr->dev, &txmsg->reply.u.link_addr.ports[i]);
+			}
+		}
+	} else
+		DRM_DEBUG_KMS("link address failed %d\n", ret);
+
+	kfree(txmsg);
+	return 0;
+}
+
+static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
+					   struct drm_dp_mst_branch *mstb,
+					   int port_num)
+{
+	int len;
+	struct drm_dp_sideband_msg_tx *txmsg;
+
+	/* TODO redo this properly */
+	return 0;
+	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+	if (!txmsg)
+		return -ENOMEM;
+
+	txmsg->dst = mstb;
+	len = build_enum_path_resources(txmsg, port_num);
+
+	drm_dp_queue_down_tx(mgr, txmsg);
+
+	return 0;
+}
+
+int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr,
+			    struct drm_dp_mst_port *port,
+			    int id,
+			    int pbn)
+{
+	struct drm_dp_sideband_msg_tx *txmsg;
+	int len, ret;
+
+	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+	if (!txmsg)
+		return -ENOMEM;
+
+	txmsg->dst = port->parent;
+	len = build_allocate_payload(txmsg, port->port_num,
+				     id,
+				     pbn);
+
+	drm_dp_queue_down_tx(mgr, txmsg);
+
+	ret = drm_dp_mst_wait_tx_reply(port->parent, txmsg);
+
+	kfree(txmsg);
+	return 0;
+}
+
+static int drm_dp_create_payload_step1(struct drm_dp_mst_topology_mgr *mgr,
+				       int id,
+				       struct drm_dp_payload *payload)
+{
+	int ret;
+
+	ret = drm_dp_dpcd_write_payload(mgr, id, payload);
+	if (ret < 0)
+		return ret;
+	payload->payload_state = DP_PAYLOAD_LOCAL;
+	return 0;
+}
+
+int drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr *mgr,
+				struct drm_dp_mst_port *port,
+				int id,
+				struct drm_dp_payload *payload)
+{
+	int ret;
+	ret = drm_dp_payload_send_msg(mgr, port, id, port->vcpi.pbn);
+	if (ret < 0)
+		return ret;
+	payload->payload_state = DP_PAYLOAD_REMOTE;
+	return ret;
+}
+
+int drm_dp_destroy_payload(struct drm_dp_mst_topology_mgr *mgr,
+			   struct drm_dp_mst_port *port,
+			   int id,
+			   struct drm_dp_payload *payload)
+{
+	int ret;
+	if (port)
+		ret = drm_dp_payload_send_msg(mgr, port, id, 0);
+
+	ret = drm_dp_dpcd_write_payload(mgr, id, payload);
+
+	(*mgr->cbs->generate_act_event)(mgr);
+
+	ret = drm_dp_check_act_status(mgr);
+	payload->payload_state = 0;
+	return ret;
+}
+
+/* see if we need to change and of the payload allocations */
+/* it appears for intel we should set the whole mode up between
+   sending the remote payload setup and configuring the local dpcd table */
+/* return 1 if things changed so ACT can be sent */
+int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr)
+{
+	int i;
+	int cur_slots = 1;
+	struct drm_dp_payload req_payload;
+
+	for (i = 0; i < mgr->max_payloads; i++) {
+		/* solve the current payloads - compare to the hw ones
+		   - update the hw view */
+		req_payload.start_slot = cur_slots;
+		if (mgr->proposed_vcpis[i])
+			req_payload.num_slots = mgr->proposed_vcpis[i]->num_slots;
+		else
+			req_payload.num_slots = 0;
+
+		/* work out what is required to happen with this payload */
+		if (mgr->payloads[i].start_slot != req_payload.start_slot ||
+		    mgr->payloads[i].num_slots != req_payload.num_slots) {
+			struct drm_dp_mst_port *port = NULL;
+
+			if (mgr->proposed_vcpis[i])
+				port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
+
+			/* need to push an update for this payload */
+			if (req_payload.num_slots)
+				drm_dp_create_payload_step1(mgr, i, &req_payload);
+			else if (mgr->payloads[i].num_slots) {
+				mgr->payloads[i].num_slots = 0;
+				drm_dp_destroy_payload(mgr, port, i, &mgr->payloads[i]);
+			}
+			mgr->payloads[i].start_slot = req_payload.start_slot;
+			mgr->payloads[i].num_slots = req_payload.num_slots;
+			mgr->payloads[i].payload_state = req_payload.payload_state;
+		}
+		cur_slots += req_payload.num_slots;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_dp_update_payload_part1);
+
+int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr)
+{
+	struct drm_dp_mst_port *port;
+	int i;
+	int ret;
+	for (i = 0; i < mgr->max_payloads; i++) {
+		if (!mgr->proposed_vcpis[i])
+			continue;
+
+		if (mgr->payloads[i].payload_state != DP_PAYLOAD_LOCAL)
+			continue;
+
+		port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
+		ret = drm_dp_create_payload_step2(mgr, port, i, &mgr->payloads[i]);
+	}
+	return 0;
+}
+EXPORT_SYMBOL(drm_dp_update_payload_part2);
+
+#if 0 /* unused as of yet */
+static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
+				 struct drm_dp_mst_port *port,
+				 int offset, int size)
+{
+	int len;
+	struct drm_dp_sideband_msg_tx *txmsg;
+
+	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+	if (!txmsg)
+		return -ENOMEM;
+
+	len = build_dpcd_read(txmsg, port->port_num, 0, 8);
+	txmsg->dst = port->parent;
+
+	drm_dp_queue_down_tx(mgr, txmsg);
+
+	return 0;
+}
+#endif
+
+static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
+				  struct drm_dp_mst_port *port,
+				  int offset, int size, u8 *bytes)
+{
+	int len;
+	int ret;
+	struct drm_dp_sideband_msg_tx *txmsg;
+	struct drm_dp_mst_branch *mstb = port->parent;
+
+	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+	if (!txmsg)
+		return -ENOMEM;
+
+	len = build_dpcd_write(txmsg, port->port_num, offset, size, bytes);
+	txmsg->dst = port->parent;
+
+	drm_dp_queue_down_tx(mgr, txmsg);
+
+	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+	if (ret > 0) {
+		if (txmsg->reply.reply_type == 1) {
+			return -EINVAL;
+		} else
+			return 0;
+	}
+	kfree(txmsg);
+	return 0;
+}
+
+static int drm_dp_encode_up_ack_reply(struct drm_dp_sideband_msg_tx *msg, u8 req_type)
+{
+	struct drm_dp_sideband_msg_reply_body reply;
+
+	reply.reply_type = 1;
+	reply.req_type = req_type;
+	drm_dp_encode_sideband_reply(&reply, msg);
+	return 0;
+}
+
+static int drm_dp_send_up_ack_reply(struct drm_dp_mst_topology_mgr *mgr,
+				    struct drm_dp_mst_branch *mstb,
+				    int req_type, int seqno, bool broadcast)
+{
+	struct drm_dp_sideband_msg_tx *txmsg;
+
+	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+	if (!txmsg)
+		return -ENOMEM;
+
+	txmsg->dst = mstb;
+	txmsg->seqno = seqno;
+	drm_dp_encode_up_ack_reply(txmsg, req_type);
+
+	list_add_tail(&txmsg->next, &mgr->tx_msg_upq);
+	if (!mgr->tx_up_in_progress) {
+		process_single_up_tx(mgr);
+	}
+	return 0;
+}
+
+static int drm_dp_get_vc_payload_bw(int dp_link_bw, int dp_link_count)
+{
+	switch (dp_link_bw) {
+	case DP_LINK_BW_1_62:
+		return 3 * dp_link_count;
+	case DP_LINK_BW_2_7:
+		return 5 * dp_link_count;
+	case DP_LINK_BW_5_4:
+		return 10 * dp_link_count;
+	}
+	return 0;
+}
+
+int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state)
+{
+	int ret = 0;
+
+	mutex_lock(&mgr->lock);
+	if (mst_state == mgr->mst_state)
+		goto out_unlock;
+
+	mgr->mst_state = mst_state;
+	/* set the device into MST mode */
+	if (mst_state) {
+		struct drm_dp_mst_branch *mstb;
+		WARN_ON(mgr->mst_primary);
+
+		/* get dpcd info */
+		ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE);
+		if (ret != DP_RECEIVER_CAP_SIZE) {
+			DRM_DEBUG_KMS("failed to read DPCD\n");
+			goto out_unlock;
+		}
+
+		mgr->pbn_div = drm_dp_get_vc_payload_bw(mgr->dpcd[1], mgr->dpcd[2] & DP_MAX_LANE_COUNT_MASK);
+		mgr->total_pbn = 2560;
+		mgr->total_slots = DIV_ROUND_UP(mgr->total_pbn, mgr->pbn_div);
+		mgr->avail_slots = mgr->total_slots;
+
+		/* add initial branch device at LCT 1 */
+		mstb = drm_dp_add_mst_branch_device(1, NULL);
+		if (mstb == NULL) {
+			ret = -ENOMEM;
+			goto out_unlock;
+		}
+		mstb->mgr = mgr;
+		/* give this the main reference */
+		mgr->mst_primary = mstb;
+		kref_get(&mgr->mst_primary->kref);
+
+		{
+			struct drm_dp_payload reset_pay;
+			reset_pay.start_slot = 0;
+			reset_pay.num_slots = 0x3f;
+			drm_dp_dpcd_write_payload(mgr, 0, &reset_pay);
+		}
+
+		ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
+					 DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC);
+		if (ret < 0)
+			goto out_unlock;
+
+		/* sort out guid */
+		ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, mgr->guid, 16);
+		if (ret != 16) {
+			DRM_DEBUG_KMS("failed to read DP GUID %d\n", ret);
+			goto out_unlock;
+		}
+
+		mgr->guid_valid = drm_dp_validate_guid(mgr, mgr->guid);
+		if (!mgr->guid_valid) {
+			ret = drm_dp_dpcd_write(mgr->aux, DP_GUID, mgr->guid, 16);
+			mgr->guid_valid = true;
+		}
+
+		mutex_unlock(&mgr->lock);
+
+		schedule_work(&mgr->work);
+		/* we need to send the link address in a work queue
+		   so we can avoid recusing the modeset locks if we get
+		   new connectors */
+		drm_dp_put_mst_branch_device(mgr->mst_primary);
+
+		ret = 0;
+	} else {
+		if (mgr->mst_primary) {
+			cancel_work_sync(&mgr->work);
+			drm_dp_put_mst_branch_device(mgr->mst_primary);
+			mgr->mst_primary = NULL;
+		}
+		/* disable MST on the device */
+		ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0);
+		if (ret < 0)
+			goto out_unlock;
+		ret = 0;
+		mutex_unlock(&mgr->lock);
+	}
+
+	return 0;
+out_unlock:
+	mutex_unlock(&mgr->lock);
+	return ret;
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_set_mst);
+
+static void drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up)
+{
+	int len;
+	u8 replyblock[32];
+	int replylen, origlen, curreply;
+	int ret;
+	struct drm_dp_sideband_msg_rx *msg;
+	int basereg = up ? DP_SIDEBAND_MSG_UP_REQ_BASE : DP_SIDEBAND_MSG_DOWN_REP_BASE;
+	if (up)
+		msg = &mgr->up_req_recv;
+	else
+		msg = &mgr->down_rep_recv;
+
+	len = min(mgr->max_dpcd_transaction_bytes, 32);
+	ret = drm_dp_dpcd_read(mgr->aux, basereg,
+			       replyblock, len);
+	if (ret != len) {
+		DRM_DEBUG_KMS("failed to read DPCD down rep %d %d\n", len, ret);
+		return;
+	}
+	ret = drm_dp_sideband_msg_build(msg, replyblock, len, true);
+	if (!ret) {
+		DRM_DEBUG_KMS("sideband msg build failed %d\n", replyblock[0]);
+		return;
+	}
+	replylen = msg->curchunk_len + msg->curchunk_hdrlen;
+
+	origlen = replylen;
+	replylen -= len;
+	curreply = len;
+	while (replylen > 0) {
+		len = min3(replylen, mgr->max_dpcd_transaction_bytes, 32);
+		ret = drm_dp_dpcd_read(mgr->aux, basereg + curreply,
+				    replyblock, len);
+
+		drm_dp_sideband_msg_build(msg, replyblock, len, false);
+		curreply += len;
+		replylen -= len;
+	}
+}
+
+static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
+{
+	int ret = 0;
+	drm_dp_get_one_sb_msg(mgr, false);
+
+	if (mgr->down_rep_recv.have_eomt) {
+		struct drm_dp_sideband_msg_tx *txmsg;
+		struct drm_dp_mst_branch *mstb;
+
+		mstb = drm_dp_get_mst_branch_device(mgr,
+						    mgr->down_rep_recv.initial_hdr.lct,
+						    mgr->down_rep_recv.initial_hdr.rad);
+
+		if (!mstb) {
+			DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->down_rep_recv.initial_hdr.lct);
+			memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+			return 0;
+		}
+
+		/* find the message */
+		spin_lock(&mstb->slots_lock);
+		txmsg = mstb->tx_slots[mgr->down_rep_recv.initial_hdr.seqno];
+		/* remove from slots */
+		mstb->tx_slots[mgr->down_rep_recv.initial_hdr.seqno] = NULL;
+		spin_unlock(&mstb->slots_lock);
+
+		if (!txmsg) {
+			DRM_DEBUG_KMS("Got MST reply with no msg %p %d %d %02x\n",
+			       mstb,
+			       mgr->down_rep_recv.initial_hdr.seqno,
+			       mgr->down_rep_recv.initial_hdr.lct,
+			       mgr->down_rep_recv.initial_hdr.rad[0]);
+			drm_dp_put_mst_branch_device(mstb);
+			memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+			return 0;
+		}
+
+		drm_dp_sideband_parse_reply(&mgr->down_rep_recv, &txmsg->reply);
+		if (txmsg->reply.reply_type == 1) {
+			DRM_DEBUG_KMS("Got NAK reply: req 0x%02x, reason 0x%02x, nak data 0x%02x\n", txmsg->reply.req_type, txmsg->reply.u.nak.reason, txmsg->reply.u.nak.nak_data);
+		} else if (txmsg->reply.req_type == DP_ENUM_PATH_RESOURCES) {
+			drm_dp_update_port_pbn(mstb, txmsg->reply.u.path_resources.port_number, txmsg->reply.u.path_resources.avail_payload_bw_number);
+		}
+
+		drm_dp_put_mst_branch_device(mstb);
+
+		memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+		txmsg->state = DRM_DP_SIDEBAND_TX_RX;
+
+		process_single_down_tx(mgr);
+
+		wake_up(&mgr->tx_waitq);
+	}
+	return ret;
+}
+
+static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
+{
+	int ret = 0;
+	drm_dp_get_one_sb_msg(mgr, true);
+
+	if (mgr->up_req_recv.have_eomt) {
+		struct drm_dp_sideband_msg_req_body msg;
+		struct drm_dp_mst_branch *mstb;
+		bool seqno;
+		mstb = drm_dp_get_mst_branch_device(mgr,
+						    mgr->up_req_recv.initial_hdr.lct,
+						    mgr->up_req_recv.initial_hdr.rad);
+		if (!mstb) {
+			DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
+			memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+			return 0;
+		}
+
+		seqno = mgr->up_req_recv.initial_hdr.seqno;
+		drm_dp_sideband_parse_req(&mgr->up_req_recv, &msg);
+
+		if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
+			drm_dp_send_up_ack_reply(mgr, mstb, msg.req_type, seqno, false);
+			drm_dp_update_port(mstb, &msg.u.conn_stat);
+			DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n", msg.u.conn_stat.port_number, msg.u.conn_stat.legacy_device_plug_status, msg.u.conn_stat.displayport_device_plug_status, msg.u.conn_stat.message_capability_status, msg.u.conn_stat.input_port, msg.u.conn_stat.peer_device_type);
+			ret = 1;
+		} else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
+			drm_dp_send_up_ack_reply(mgr, mstb, msg.req_type, seqno, false);
+			DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n", msg.u.resource_stat.port_number, msg.u.resource_stat.available_pbn);
+		}
+
+		drm_dp_put_mst_branch_device(mstb);
+		memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+	}
+	return ret;
+}
+
+int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, int irq_vector)
+{
+	int ack_value = 0;
+	int ret = 0;
+	mutex_lock(&mgr->lock);
+
+	if (irq_vector & DP_DOWN_REP_MSG_RDY) {
+		ret = drm_dp_mst_handle_down_rep(mgr);
+		ack_value |= DP_DOWN_REP_MSG_RDY;
+	}
+
+	if (irq_vector & DP_UP_REQ_MSG_RDY) {
+		ret |= drm_dp_mst_handle_up_req(mgr);
+		ack_value |= DP_UP_REQ_MSG_RDY;
+	}
+
+	drm_dp_dpcd_writeb(mgr->aux, DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
+			   ack_value);
+
+	mutex_unlock(&mgr->lock);
+	return ret;
+}
+EXPORT_SYMBOL(drm_dp_mst_hpd_irq);
+
+/* HACKS */
+enum drm_connector_status drm_dp_mst_detect_port(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+	enum drm_connector_status status = connector_status_disconnected;
+
+	if (!port->ddps)
+		goto out;
+
+	switch (port->pdt) {
+	case DP_PEER_DEVICE_NONE:
+	case DP_PEER_DEVICE_MST_BRANCHING:
+		break;
+
+	case DP_PEER_DEVICE_SST_SINK:
+		status = connector_status_connected;
+		break;
+	case DP_PEER_DEVICE_DP_LEGACY_CONV:
+		if (port->ldps)
+			status = connector_status_connected;
+		break;
+	}
+out:
+	return status;
+}
+EXPORT_SYMBOL(drm_dp_mst_detect_port);
+
+struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+	struct edid *edid = NULL;
+
+	edid = drm_get_edid(connector, &port->aux.ddc);
+	return edid;
+}
+EXPORT_SYMBOL(drm_dp_mst_get_edid);
+
+static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr,
+			    struct drm_dp_vcpi *vcpi, int pbn, int force)
+{
+	int num_slots;
+	int ret;
+
+	num_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
+
+	if (num_slots > mgr->avail_slots)
+		return -ENOSPC;
+
+	ret = drm_dp_mst_assign_payload_id(mgr, force);
+	if (ret < 0)
+		return ret;
+
+	vcpi->vcpi = ret;
+	vcpi->pbn = pbn;
+	vcpi->aligned_pbn = num_slots * mgr->pbn_div;
+	vcpi->num_slots = num_slots;
+
+	mgr->proposed_vcpis[ret] = vcpi;
+	return 0;
+}
+
+bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, int pbn, int *slots, int force_override)
+{
+	int ret;
+
+	if (pbn == port->vcpi.pbn) {
+		DRM_DEBUG_KMS("already have vcpi allocated for pbn %d\n", pbn);
+		*slots = port->vcpi.num_slots;
+		return true;
+	}
+
+	ret = drm_dp_init_vcpi(mgr, &port->vcpi, pbn, force_override);
+	if (ret) {
+		DRM_DEBUG_KMS("failed to init vcpi %d %d %d\n", DIV_ROUND_UP(pbn, mgr->pbn_div), mgr->avail_slots, ret);
+		goto out;
+	}
+	DRM_DEBUG_KMS("initing vcpi for %d %d\n", pbn, port->vcpi.num_slots);
+	*slots = port->vcpi.num_slots;
+
+	return true;
+out:
+	return false;
+}
+EXPORT_SYMBOL(drm_dp_mst_allocate_vcpi);
+
+static void drm_dp_destroy_vcpi(struct drm_dp_mst_topology_mgr *mgr,
+				struct drm_dp_vcpi *vcpi)
+{
+
+	drm_dp_mst_put_payload_id(mgr, vcpi->vcpi);
+	vcpi->num_slots = vcpi->pbn = vcpi->aligned_pbn = 0;
+}
+
+void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+	mgr->proposed_vcpis[port->vcpi.vcpi] = NULL;
+	drm_dp_destroy_vcpi(mgr, &port->vcpi);
+	port->vcpi.vcpi = -1;
+}
+EXPORT_SYMBOL(drm_dp_mst_deallocate_vcpi);
+
+static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
+				     int id, struct drm_dp_payload *payload)
+{
+	u8 payload_alloc[3], status;
+	int ret;
+	int retries = 0;
+
+	drm_dp_dpcd_writeb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS,
+			   DP_PAYLOAD_TABLE_UPDATED);
+
+	payload_alloc[0] = id;
+	payload_alloc[1] = payload->start_slot;
+	payload_alloc[2] = payload->num_slots;
+
+	ret = drm_dp_dpcd_write(mgr->aux, DP_PAYLOAD_ALLOCATE_SET, payload_alloc, 3);
+	if (ret != 3) {
+		DRM_DEBUG_KMS("failed to write payload allocation %d\n", ret);
+		goto fail;
+	}
+
+ retry:
+	ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status);
+	if (ret < 0) {
+		DRM_DEBUG_KMS("failed to read payload table status %d\n", ret);
+		goto fail;
+	}
+
+	if (!(status & DP_PAYLOAD_TABLE_UPDATED)) {
+		if (retries++ < 5) {
+			udelay(10);
+			goto retry;
+		}
+		DRM_DEBUG_KMS("status not set after read payload table status %d\n", status);
+		ret = -EINVAL;
+		goto fail;
+	}
+	ret = 0;
+fail:
+	return ret;
+}
+
+int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr)
+{
+	u8 status;
+	int ret;
+	int count = 0;
+
+	do {
+		ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status);
+		if (ret < 0) {
+			DRM_DEBUG_KMS("failed to read payload table status %d\n", ret);
+			goto fail;
+		}
+
+		if (status & DP_PAYLOAD_ACT_HANDLED)
+			break;
+		count++;
+		udelay(100);
+
+	} while (count < 30);
+
+	if (!(status & DP_PAYLOAD_ACT_HANDLED)) {
+		DRM_DEBUG_KMS("failed to get ACT bit %d after %d retries\n", status, count);
+		ret = -EINVAL;
+		goto fail;
+	}
+	return 0;
+fail:
+	return ret;
+}
+EXPORT_SYMBOL(drm_dp_check_act_status);
+
+int drm_dp_calc_pbn_mode(int clock, int bpp)
+{
+	fixed20_12 pix_bw;
+	fixed20_12 fbpp;
+	fixed20_12 result;
+	fixed20_12 margin, tmp;
+	u32 res;
+
+	pix_bw.full = dfixed_const(clock);
+	fbpp.full = dfixed_const(bpp);
+	tmp.full = dfixed_const(8);
+	fbpp.full = dfixed_div(fbpp, tmp);
+
+	result.full = dfixed_mul(pix_bw, fbpp);
+	margin.full = dfixed_const(54);
+	tmp.full = dfixed_const(64);
+	margin.full = dfixed_div(margin, tmp);
+	result.full = dfixed_div(result, margin);
+
+	margin.full = dfixed_const(1006);
+	tmp.full = dfixed_const(1000);
+	margin.full = dfixed_div(margin, tmp);
+	result.full = dfixed_mul(result, margin);
+
+	result.full = dfixed_div(result, tmp);
+	result.full = dfixed_ceil(result);
+	res = dfixed_trunc(result);
+	return res;
+}
+EXPORT_SYMBOL(drm_dp_calc_pbn_mode);
+
+static int test_calc_pbn_mode(void)
+{
+	int ret;
+	ret = drm_dp_calc_pbn_mode(154000, 30);
+	if (ret != 689)
+		return -EINVAL;
+	ret = drm_dp_calc_pbn_mode(234000, 30);
+	if (ret != 1047)
+		return -EINVAL;
+	return 0;
+}
diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
new file mode 100644
index 0000000..290f3cd
--- /dev/null
+++ b/include/drm/drm_dp_mst_helper.h
@@ -0,0 +1,403 @@
+/*
+ * Copyright © 2014 Red Hat.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+#ifndef _DRM_DP_MST_HELPER_H_
+#define _DRM_DP_MST_HELPER_H_
+
+#include <linux/types.h>
+#include <drm/drm_dp_helper.h>
+
+struct drm_dp_mst_branch;
+
+/* vpci channel info */
+struct drm_dp_vcpi {
+	int vcpi;
+	int pbn;
+	int aligned_pbn;
+	int num_slots;
+	struct list_head next;
+};
+
+/* display port MST port */
+struct drm_dp_mst_port {
+	struct kref kref;
+
+	/* if dpcd 1.2 device is on this port - its GUID info */
+	bool guid_valid;
+	u8 guid[16];
+
+	u8 port_num;
+	bool input;
+	bool mcs;
+	bool ddps;
+	u8 pdt;
+	bool ldps;
+	u8 dpcd_rev;
+	uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
+	uint16_t available_pbn;
+	struct list_head next;
+	struct drm_dp_mst_branch *mstb; /* pointer to an mstb if this port has one */
+	struct drm_dp_aux aux; /* i2c bus for this port? */
+	struct drm_dp_mst_branch *parent;
+
+	struct drm_dp_vcpi vcpi;
+	struct drm_connector *connector;
+};
+
+/* list of MST branch devices we know about */
+/* we can only have two outstanding un ack msgs for
+   each MST device */
+struct drm_dp_mst_branch {
+	struct kref kref;
+	u8 rad[8];
+	u8 lct;
+	int num_ports;
+
+	int msg_slots;
+	struct list_head ports;
+	u8 fail;
+	/* list of tx ops queue for this port */
+	bool parent_is_port;
+	struct drm_dp_mst_port *port_parent;
+	struct drm_dp_mst_topology_mgr *mgr;
+
+	spinlock_t slots_lock;
+	struct drm_dp_sideband_msg_tx *tx_slots[2];
+};
+
+
+/* sideband msg header - not bit struct */
+struct drm_dp_sideband_msg_hdr {
+	u8 lct;
+	u8 lcr;
+	u8 rad[8];
+	bool broadcast;
+	bool path_msg;
+	u8 msg_len;
+	bool somt;
+	bool eomt;
+	bool seqno;
+};
+
+struct drm_dp_nak_reply {
+	u8 guid[16];
+	u8 reason;
+	u8 nak_data;
+};
+
+struct drm_dp_link_address_ack_reply {
+	u8 guid[16];
+	u8 nports;
+	struct drm_dp_link_addr_reply_port {
+		bool input_port;
+		u8 peer_device_type;
+		u8 port_number;
+		bool mcs;
+		bool ddps;
+		bool legacy_device_plug_status;
+		u8 dpcd_revision;
+		u8 peer_guid[16];
+		bool num_sdp_streams;
+		bool num_sdp_stream_sinks;
+	} ports[16];
+};
+
+struct drm_dp_remote_dpcd_read_ack_reply {
+	u8 port_number;
+	u8 num_bytes;
+	u8 bytes[255];
+};
+
+struct drm_dp_remote_dpcd_write_ack_reply {
+	u8 port_number;
+};
+
+struct drm_dp_remote_dpcd_write_nak_reply {
+	u8 port_number;
+	u8 reason;
+	u8 bytes_written_before_failure;
+};
+
+struct drm_dp_remote_i2c_read_ack_reply {
+	u8 port_number;
+	u8 num_bytes;
+	u8 bytes[255];
+};
+
+struct drm_dp_remote_i2c_read_nak_reply {
+	u8 port_number;
+	u8 nak_reason;
+	u8 i2c_nak_transaction;
+};
+
+struct drm_dp_remote_i2c_write_ack_reply {
+	u8 port_number;
+};
+
+
+struct drm_dp_sideband_msg_rx {
+	u8 chunk[48];
+	u8 msg[256];
+	u8 curchunk_len;
+	u8 curchunk_idx; /* chunk we are parsing now */
+	u8 curchunk_hdrlen;
+	u8 curlen; /* total length of the msg */
+	bool have_somt;
+	bool have_eomt;
+	struct drm_dp_sideband_msg_hdr initial_hdr;
+};
+
+
+struct drm_dp_allocate_payload {
+	u8 port_number;
+	u8 number_sdp_streams;
+	u8 vcpi;
+	u16 pbn;
+	u8 sdp_stream_sink[8];
+};
+
+struct drm_dp_allocate_payload_ack_reply {
+	u8 port_number;
+	u8 vcpi;
+	u16 allocated_pbn;
+};
+
+struct drm_dp_connection_status_notify {
+	u8 guid[16];
+	u8 port_number;
+	bool legacy_device_plug_status;
+	bool displayport_device_plug_status;
+	bool message_capability_status;
+	bool input_port;
+	u8 peer_device_type;
+};
+
+struct drm_dp_remote_dpcd_read {
+	u8 port_number;
+	u32 dpcd_address;
+	u8 num_bytes;
+};
+
+struct drm_dp_remote_dpcd_write {
+	u8 port_number;
+	u32 dpcd_address;
+	u8 num_bytes;
+	u8 bytes[255];
+};
+
+struct drm_dp_remote_i2c_read {
+	u8 num_transactions;
+	u8 port_number;
+	struct {
+		u8 i2c_dev_id;
+		u8 num_bytes;
+		u8 bytes[255];
+		u8 no_stop_bit;
+		u8 i2c_transaction_delay;
+	} transactions[4];
+	u8 read_i2c_device_id;
+	u8 num_bytes_read;
+};
+
+struct drm_dp_remote_i2c_write {
+	u8 port_number;
+	u8 write_i2c_device_id;
+	u8 num_bytes;
+	u8 bytes[255];
+};
+
+/* this covers ENUM_RESOURCES, POWER_DOWN_PHY, POWER_UP_PHY */
+struct drm_dp_port_number_req {
+	u8 port_number;
+};
+
+struct drm_dp_enum_path_resources_ack_reply {
+	u8 port_number;
+	u16 full_payload_bw_number;
+	u16 avail_payload_bw_number;
+};
+
+/* covers POWER_DOWN_PHY, POWER_UP_PHY */
+struct drm_dp_port_number_rep {
+	u8 port_number;
+};
+
+struct drm_dp_query_payload {
+	u8 port_number;
+	u8 vcpi;
+};
+
+struct drm_dp_resource_status_notify {
+	u8 port_number;
+	u8 guid[16];
+	u16 available_pbn;
+};
+
+struct drm_dp_query_payload_ack_reply {
+	u8 port_number;
+	u8 allocated_pbn;
+};
+
+struct drm_dp_sideband_msg_req_body {
+	u8 req_type;
+	union ack_req {
+		struct drm_dp_connection_status_notify conn_stat;
+		struct drm_dp_port_number_req port_num;
+		struct drm_dp_resource_status_notify resource_stat;
+
+		struct drm_dp_query_payload query_payload;
+		struct drm_dp_allocate_payload allocate_payload;
+
+		struct drm_dp_remote_dpcd_read dpcd_read;
+		struct drm_dp_remote_dpcd_write dpcd_write;
+
+		struct drm_dp_remote_i2c_read i2c_read;
+		struct drm_dp_remote_i2c_write i2c_write;
+	} u;
+};
+
+struct drm_dp_sideband_msg_reply_body {
+	u8 reply_type;
+	u8 req_type;
+	union ack_replies {
+		struct drm_dp_nak_reply nak;
+		struct drm_dp_link_address_ack_reply link_addr;
+		struct drm_dp_port_number_rep port_number;
+
+		struct drm_dp_enum_path_resources_ack_reply path_resources;
+		struct drm_dp_allocate_payload_ack_reply allocate_payload;
+		struct drm_dp_query_payload_ack_reply query_payload;
+
+		struct drm_dp_remote_dpcd_read_ack_reply remote_dpcd_read_ack;
+		struct drm_dp_remote_dpcd_write_ack_reply remote_dpcd_write_ack;
+		struct drm_dp_remote_dpcd_write_nak_reply remote_dpcd_write_nack;
+
+		struct drm_dp_remote_i2c_read_ack_reply remote_i2c_read_ack;
+		struct drm_dp_remote_i2c_read_nak_reply remote_i2c_read_nack;
+		struct drm_dp_remote_i2c_write_ack_reply remote_i2c_write_ack;
+	} u;
+};
+
+/* msg is queued to be put into a slot */
+#define DRM_DP_SIDEBAND_TX_QUEUED 0
+/* msg has started transmitting on a slot - still on msgq */
+#define DRM_DP_SIDEBAND_TX_START_SEND 1
+/* msg has finished transmitting on a slot - removed from msgq only in slot */
+#define DRM_DP_SIDEBAND_TX_SENT 2
+/* msg has received a response - removed from slot */
+#define DRM_DP_SIDEBAND_TX_RX 3
+#define DRM_DP_SIDEBAND_TX_TIMEOUT 4
+
+struct drm_dp_sideband_msg_tx {
+	u8 msg[256];
+	u8 chunk[48];
+	u8 cur_offset;
+	u8 cur_len;
+	struct drm_dp_mst_branch *dst;
+	struct list_head next;
+	int seqno;
+	int state;
+	struct drm_dp_sideband_msg_reply_body reply;
+};
+
+/* sideband msg handler */
+struct drm_dp_mst_topology_mgr;
+struct drm_dp_mst_topology_cbs {
+	/* create a connector for a port */
+	struct drm_connector *(*add_connector)(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port);
+	void (*destroy_connector)(struct drm_dp_mst_topology_mgr *mgr,
+				  struct drm_connector *connector);
+	void (*generate_act_event)(struct drm_dp_mst_topology_mgr *mgr);
+};
+
+#define DP_MAX_PAYLOAD (sizeof(unsigned long) * 8)
+
+#define DP_PAYLOAD_LOCAL 1
+#define DP_PAYLOAD_REMOTE 2
+
+struct drm_dp_payload {
+	int payload_state;
+	int start_slot;
+	int num_slots;
+	struct drm_dp_mst_port *port;
+};
+
+struct drm_dp_mst_topology_mgr {
+	struct device *dev;
+	struct drm_dp_mst_topology_cbs *cbs;
+	bool mst_state;
+	int max_dpcd_transaction_bytes;
+	struct drm_dp_aux *aux; /* auxch for this topology mgr to use */
+
+	struct mutex lock;
+	struct drm_dp_sideband_msg_rx down_rep_recv;
+	struct drm_dp_sideband_msg_rx up_req_recv;
+
+	/* pointer to info about the initial MST device */
+	struct drm_dp_mst_branch *mst_primary;
+
+	/* primary MST device GUID */
+	bool guid_valid;
+	u8 guid[16];
+
+	/* messages to be transmitted */
+	struct list_head tx_msg_downq;
+	struct list_head tx_msg_upq;
+	bool tx_down_in_progress;
+	bool tx_up_in_progress;
+
+	struct drm_dp_vcpi **proposed_vcpis;
+	struct drm_dp_payload *payloads;
+
+	int max_payloads;
+	unsigned long payload_mask;
+	/* protect the upq/downq and in_progress */
+	spinlock_t qlock;
+
+	wait_queue_head_t tx_waitq;
+	struct work_struct work;
+
+	u8 dpcd[DP_RECEIVER_CAP_SIZE];
+	int pbn_div;
+	int total_pbn;
+	int total_slots;
+	int avail_slots;
+};
+
+int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, struct device *dev, struct drm_dp_aux *aux, int max_dpcd_transaction_bytes, int max_payloads);
+void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr);
+int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state);
+int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, int irq_vector);
+
+/* hacky interface for now */
+enum drm_connector_status drm_dp_mst_detect_port(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port);
+struct drm_connector;
+struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port);
+int drm_dp_calc_pbn_mode(int clock, int bpp);
+
+bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, int pbn, int *slots, int force_override);
+void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr,
+				struct drm_dp_mst_port *port);
+int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr);
+int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr);
+int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr);
+
+#endif
-- 
1.9.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 6/8] i915: split some DP modesetting code into a separate function
  2014-05-02  4:39 [RFC] drm + i915 DP MST code preview Dave Airlie
                   ` (4 preceding siblings ...)
  2014-05-02  4:39 ` [PATCH 5/8] drm/helper: add Displayport multi-stream helper Dave Airlie
@ 2014-05-02  4:39 ` Dave Airlie
  2014-05-02  4:39 ` [PATCH 7/8] drm/i915: check connector->encoder before using it Dave Airlie
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 22+ messages in thread
From: Dave Airlie @ 2014-05-02  4:39 UTC (permalink / raw)
  To: dri-devel

From: Dave Airlie <airlied@redhat.com>

this is just prep work for mst support.

Signed-off-by: Dave Airlie <airlied@redhat.com>
---
 drivers/gpu/drm/i915/intel_ddi.c | 20 +++++++++++++-------
 drivers/gpu/drm/i915/intel_drv.h |  1 +
 2 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index 0ad4e96..a5b8b76 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -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));
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index b885df1..8e41cdc 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -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);
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 7/8] drm/i915: check connector->encoder before using it.
  2014-05-02  4:39 [RFC] drm + i915 DP MST code preview Dave Airlie
                   ` (5 preceding siblings ...)
  2014-05-02  4:39 ` [PATCH 6/8] i915: split some DP modesetting code into a separate function Dave Airlie
@ 2014-05-02  4:39 ` Dave Airlie
  2014-05-02  4:39 ` [PATCH 8/8] i915: add DP 1.2 MST support (v0.1) Dave Airlie
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 22+ messages in thread
From: Dave Airlie @ 2014-05-02  4:39 UTC (permalink / raw)
  To: dri-devel

From: Dave Airlie <airlied@redhat.com>

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

Signed-off-by: Dave Airlie <airlied@redhat.com>
---
 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(-)

diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 1e83ae4..88e944f 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -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)
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index afa5519..5852dee 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -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)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index b39d036..75b2aaf 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -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");
+		}
 	}
 }
 
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 8/8] i915: add DP 1.2 MST support (v0.1)
  2014-05-02  4:39 [RFC] drm + i915 DP MST code preview Dave Airlie
                   ` (6 preceding siblings ...)
  2014-05-02  4:39 ` [PATCH 7/8] drm/i915: check connector->encoder before using it Dave Airlie
@ 2014-05-02  4:39 ` Dave Airlie
  2014-05-02  4:51 ` [RFC] drm + i915 DP MST code preview Dave Airlie
  2014-05-02  8:52 ` Chris Wilson
  9 siblings, 0 replies; 22+ messages in thread
From: Dave Airlie @ 2014-05-02  4:39 UTC (permalink / raw)
  To: dri-devel

From: Dave Airlie <airlied@redhat.com>

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.

TODO:
 write TODOs.

Signed-off-by: Dave Airlie <airlied@redhat.com>
---
 drivers/gpu/drm/i915/Makefile        |   1 +
 drivers/gpu/drm/i915/i915_drv.h      |   5 +
 drivers/gpu/drm/i915/i915_irq.c      | 105 +++++++-
 drivers/gpu/drm/i915/intel_ddi.c     | 106 ++++++--
 drivers/gpu/drm/i915/intel_display.c |  56 ++--
 drivers/gpu/drm/i915/intel_dp.c      | 174 +++++++++++-
 drivers/gpu/drm/i915/intel_dp_mst.c  | 502 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_drv.h     |  39 ++-
 8 files changed, 942 insertions(+), 46 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/intel_dp_mst.c

diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index b1445b7..6cf2b95 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -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 \
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 7d6acb4..36a5c78 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1468,6 +1468,9 @@ 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;
 	/* Old dri1 support infrastructure, beware the dragons ya fools entering
 	 * here! */
 	struct i915_dri1_state dri1;
@@ -1999,6 +2002,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,
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 5852dee..ddd4d84 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -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,70 @@ 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;
+	spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+
+	hotplug_reg = I915_READ(PCH_PORT_HOTPLUG);
+
+	for (i = 0; i < I915_MAX_PORTS; i++) {
+		if (port_bits & (1 << i)) {
+
+			hpshift = port_to_hotplug_shift(i);
+
+			/* if we have no HPD bits for this */
+			if (!((hotplug_reg >> hpshift) & PORTB_HOTPLUG_STATUS_MASK)) {
+				ret = 1;
+				continue;
+			}
+
+			long_hpd = (hotplug_reg >> hpshift) & PORTB_HOTPLUG_LONG_DETECT;
+			digport = dev_priv->hpd_irq_port[i];
+
+			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);
+			}
+		}
+	}
+	I915_WRITE(PCH_PORT_HOTPLUG, hotplug_reg);
+
+	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 +1445,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 +1476,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 +1510,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 +1539,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)
+		schedule_work(&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 +4110,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);
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index a5b8b76..61d594f 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -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);
 }
@@ -1498,10 +1553,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 +1792,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);
 
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 75b2aaf..648d5a8 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -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);
@@ -4038,6 +4049,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 +4080,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 +10222,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 +10268,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 +10302,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;
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 44df493..6aaadbb 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -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,28 @@ 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)
+{
+	if (intel_dp->is_mst) {
+		u8 esi[4] = { 0 };
+		int ret;
+		ret = intel_dp_get_sink_irq_esi(intel_dp, esi);
+		if (ret == true) {
+			DRM_DEBUG_KMS("got esi %02x\n", esi[1]);
+			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]);
+				return ret;
+			}
+			return 0;
+		} else {
+			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);
+		}
+	}
+	return -EINVAL;
+}
 
 void
 intel_dp_check_link_status(struct intel_dp *intel_dp)
@@ -3163,6 +3227,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 +3237,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 +3255,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 {
@@ -3463,7 +3544,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 +4040,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_init_encoder(intel_dig_port);
+		}
+	}
+
 	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 +4077,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 +4118,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 +4133,78 @@ 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);
+	}
+	return 1;
+}
diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c
new file mode 100644
index 0000000..f6e7615
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_dp_mst.c
@@ -0,0 +1,502 @@
+/*
+ * 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 ret;
+	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);
+
+	/* create a VCPI for this */
+	ret = drm_dp_mst_allocate_vcpi(&intel_dp->mst_mgr,
+				       found->port, mst_pbn, &slots, -1);
+	if (ret == false) {
+		DRM_ERROR("failed to allocate vcpi\n");
+		return false;
+	}
+
+	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;
+	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;
+	DRM_DEBUG_KMS("\n");
+	if (intel_dp->active_mst_links == 0) {
+		struct drm_crtc *crtc = encoder->base.crtc;
+		struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+		enum port port = intel_ddi_get_encoder_port(encoder);
+
+		intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+
+		I915_WRITE(PORT_CLK_SEL(port), intel_crtc->ddi_pll_sel);
+
+		intel_dp_start_link_train(intel_dp);
+		intel_ddi_mode_set_dp(&intel_dig_port->base);
+		intel_dp_complete_link_train(intel_dp);
+		intel_dp_stop_link_train(intel_dp);
+	}
+	intel_dp->active_mst_links++;
+
+}
+
+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;
+
+	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;
+
+	found->port->vcpi.num_slots = 0;
+	ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
+
+	drm_dp_mst_deallocate_vcpi(&intel_dp->mst_mgr, found->port);
+
+	DRM_DEBUG_KMS("\n");
+}
+
+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;
+
+	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;
+	DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
+#if 0
+	if (intel_dp->active_mst_links == 0) {
+		struct drm_crtc *crtc = encoder->base.crtc;
+		struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+		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);
+	}
+
+	intel_dp->active_mst_links++;
+#endif
+	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)
+{
+	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)
+{
+	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_DEBUG_KMS("Adding DP connector on MST\n");
+	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_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,
+					   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_DEBUG_KMS("\n");
+}
+
+static void intel_dp_mst_generate_act_event(struct drm_dp_mst_topology_mgr *mgr)
+{
+	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 drm_i915_private *dev_priv = dev->dev_private;
+	enum port port = intel_dig_port->port;
+
+	intel_ddi_force_act(&intel_dig_port->base, true);
+
+	if (wait_for((I915_READ(DP_TP_STATUS(port)) & DP_TP_STATUS_ACT_SENT),
+		     1))
+		DRM_ERROR("Timed out waiting for ACT sent\n");
+
+	intel_ddi_force_act(&intel_dig_port->base, false);
+}
+
+struct drm_dp_mst_topology_cbs mst_cbs = {
+	.add_connector = intel_dp_add_mst_connector,
+	.destroy_connector = intel_dp_destroy_mst_connector,
+	.generate_act_event = intel_dp_mst_generate_act_event,
+};
+
+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;
+}
+
+bool
+intel_dp_mst_init_encoder(struct intel_digital_port *intel_dig_port)
+{
+	struct intel_dp *intel_dp = &intel_dig_port->dp;
+	struct drm_device *dev = intel_dig_port->base.base.dev;
+
+	intel_dp->can_mst = true;
+	intel_dp->mst_mgr.cbs = &mst_cbs;
+
+	/* create encoders */
+	intel_dp_create_fake_mst_encoders(intel_dig_port);
+	drm_dp_mst_topology_mgr_init(&intel_dp->mst_mgr, dev->dev, &intel_dp->aux, 16, 3);
+	return true;
+}
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 8e41cdc..98bb133 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -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,9 @@ 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;
+
+	struct drm_dp_mst_port *port;
+	struct intel_dp *mst_port;
 };
 
 typedef struct dpll {
@@ -340,6 +344,8 @@ struct intel_crtc_config {
 	bool ips_enabled;
 
 	bool double_wide;
+
+	bool dp_encoder_is_mst;
 };
 
 struct intel_pipe_wm {
@@ -487,6 +493,7 @@ struct intel_hdmi {
 			       struct drm_display_mode *adjusted_mode);
 };
 
+struct intel_dp_mst_encoder;
 #define DP_MAX_DOWNSTREAM_PORTS		0x10
 
 /**
@@ -527,8 +534,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 +570,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 +643,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 +712,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 +832,11 @@ 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 */
+bool intel_dp_mst_init_encoder(struct intel_digital_port *intel_dig_port);
 /* intel_dsi.c */
 bool intel_dsi_init(struct drm_device *dev);
 
-- 
1.9.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* Re: [RFC] drm + i915 DP MST code preview
  2014-05-02  4:39 [RFC] drm + i915 DP MST code preview Dave Airlie
                   ` (7 preceding siblings ...)
  2014-05-02  4:39 ` [PATCH 8/8] i915: add DP 1.2 MST support (v0.1) Dave Airlie
@ 2014-05-02  4:51 ` Dave Airlie
  2014-05-02  8:52 ` Chris Wilson
  9 siblings, 0 replies; 22+ messages in thread
From: Dave Airlie @ 2014-05-02  4:51 UTC (permalink / raw)
  To: dri-devel

TODO: fix fbcon

I forgot to mention no fbcon yet :-)

Dave.

On 2 May 2014 14:39, Dave Airlie <airlied@gmail.com> wrote:
> Hi,
>
> Branch: http://cgit.freedesktop.org/~airlied/linux/log/?h=drm-i915-mst-support
>
> Okay I've gotten this to light up some monitors using -modesetting so it
> must be ready for an initial posting,
>
> Tested on Haswell so far with Lenovo Ultradock and with Ancell MST Hub devices,
> (checked it can detect the Dell 32" Monitor - but that is another world of pain).
>
> The main problem is documented in the i915 patch, state check backtraces abound.
>
> There is still quite a bit of work todo for a compliant solution, but I don't
> have a compliance tester so I can only imagine the fail.
>
> The helper interface can probably be cleaned up, the whole ACT area is bit
> of a mess, and I'd probably need an idea from other GPUs how best to do that,
>
> Dave.
>
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC] drm + i915 DP MST code preview
  2014-05-02  4:39 [RFC] drm + i915 DP MST code preview Dave Airlie
                   ` (8 preceding siblings ...)
  2014-05-02  4:51 ` [RFC] drm + i915 DP MST code preview Dave Airlie
@ 2014-05-02  8:52 ` Chris Wilson
  2014-05-02 21:08   ` Dave Airlie
  9 siblings, 1 reply; 22+ messages in thread
From: Chris Wilson @ 2014-05-02  8:52 UTC (permalink / raw)
  To: Dave Airlie; +Cc: dri-devel

On Fri, May 02, 2014 at 02:39:37PM +1000, Dave Airlie wrote:
> Hi,
> 
> Branch: http://cgit.freedesktop.org/~airlied/linux/log/?h=drm-i915-mst-support
> 
> Okay I've gotten this to light up some monitors using -modesetting so it
> must be ready for an initial posting,

I see that connectors are added/removed dynamically based on branch
discovery, but I haven't spotted how that is communicated to userspace.
I was expecting to see a new uevent. Or do you simply want userspace to
update it lists of connectors on every hotplug event? Is there anyway to
retrieve the guid so that we can associate the same XID to each branch
device for the server lifetime? (Is that even a sane idea?) Can we
detect when a DP dongle is present splitting DVI/VGA using MST but since
they use different encoders they already falsely appear as unique
connectors to i915.ko?
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC] drm + i915 DP MST code preview
  2014-05-02  8:52 ` Chris Wilson
@ 2014-05-02 21:08   ` Dave Airlie
  2014-05-03  9:00     ` Chris Wilson
  0 siblings, 1 reply; 22+ messages in thread
From: Dave Airlie @ 2014-05-02 21:08 UTC (permalink / raw)
  To: Chris Wilson, Dave Airlie, dri-devel

On 2 May 2014 18:52, Chris Wilson <chris@chris-wilson.co.uk> wrote:
> On Fri, May 02, 2014 at 02:39:37PM +1000, Dave Airlie wrote:
>> Hi,
>>
>> Branch: http://cgit.freedesktop.org/~airlied/linux/log/?h=drm-i915-mst-support
>>
>> Okay I've gotten this to light up some monitors using -modesetting so it
>> must be ready for an initial posting,
>
> I see that connectors are added/removed dynamically based on branch
> discovery, but I haven't spotted how that is communicated to userspace.
> I was expecting to see a new uevent. Or do you simply want userspace to
> update it lists of connectors on every hotplug event? Is there anyway to
> retrieve the guid so that we can associate the same XID to each branch
> device for the server lifetime? (Is that even a sane idea?) Can we
> detect when a DP dongle is present splitting DVI/VGA using MST but since
> they use different encoders they already falsely appear as unique
> connectors to i915.ko?

I hadn't considered a new hotplug event but I suppose one makes sense
for this actually,
however userspace will still get old school hotplug events anyways as
the connectors transition to connected state.

I'm not sure how much overhead userspace would have calling
drmGetResources on every hotplug it certainly shouldn't be a common
event,

the GUID is only on DP 1.2 devices, so you don't get one for ever
port, also GUIDs are wiped on powerdown on most devices, default GUID
is 0 except where devices have USB hubs as well, so it probably
doesn't make much sense to bother exposing them directly.

I'm not sure about the last question, MST is always active DP
conversion, each port the protocol advertises will appear as a
connector, the protocol doesn't offer much more detail, like my dock
has a DP port and a HDMI port shared on one DP port, depending on what
is plugged in the port is configured different, but it appears as the
same connector.

Dave.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC] drm + i915 DP MST code preview
  2014-05-02 21:08   ` Dave Airlie
@ 2014-05-03  9:00     ` Chris Wilson
  2014-05-07  7:16       ` Aaron Plattner
  0 siblings, 1 reply; 22+ messages in thread
From: Chris Wilson @ 2014-05-03  9:00 UTC (permalink / raw)
  To: Dave Airlie; +Cc: dri-devel

On Sat, May 03, 2014 at 07:08:02AM +1000, Dave Airlie wrote:
> On 2 May 2014 18:52, Chris Wilson <chris@chris-wilson.co.uk> wrote:
> > On Fri, May 02, 2014 at 02:39:37PM +1000, Dave Airlie wrote:
> >> Hi,
> >>
> >> Branch: http://cgit.freedesktop.org/~airlied/linux/log/?h=drm-i915-mst-support
> >>
> >> Okay I've gotten this to light up some monitors using -modesetting so it
> >> must be ready for an initial posting,
> >
> > I see that connectors are added/removed dynamically based on branch
> > discovery, but I haven't spotted how that is communicated to userspace.
> > I was expecting to see a new uevent. Or do you simply want userspace to
> > update it lists of connectors on every hotplug event? Is there anyway to
> > retrieve the guid so that we can associate the same XID to each branch
> > device for the server lifetime? (Is that even a sane idea?) Can we
> > detect when a DP dongle is present splitting DVI/VGA using MST but since
> > they use different encoders they already falsely appear as unique
> > connectors to i915.ko?
> 
> I hadn't considered a new hotplug event but I suppose one makes sense
> for this actually,
> however userspace will still get old school hotplug events anyways as
> the connectors transition to connected state.
> 
> I'm not sure how much overhead userspace would have calling
> drmGetResources on every hotplug it certainly shouldn't be a common
> event,

I realised that GETRESOURCES is not too heavy later. I was thinking that
the maintainance of the X state would be more troublesome, but it
doesn't appear so. I still think having a separate event type for
branch changes will be useful, if only for the purposes of diagnostics.
 
> the GUID is only on DP 1.2 devices, so you don't get one for ever
> port, also GUIDs are wiped on powerdown on most devices, default GUID
> is 0 except where devices have USB hubs as well, so it probably
> doesn't make much sense to bother exposing them directly.

Ok. It looks like if we do attempt to maintain persistent naming, we need
to do it in the kernel anyway. That is to make sure that a downstream
device always has the same type-id upon reconnection - at least for the
lifetime of module. Or maybe the output name is irrelevant for
preserving extended desktop configurations?
 
> I'm not sure about the last question, MST is always active DP
> conversion, each port the protocol advertises will appear as a
> connector, the protocol doesn't offer much more detail, like my dock
> has a DP port and a HDMI port shared on one DP port, depending on what
> is plugged in the port is configured different, but it appears as the
> same connector.

Hmm. I think I am confused. I thought we had a recent report of a 
DP-to-VGA/DVI splitter that on Windows you could drive the VGA + DVI
separately, but on Linux they could only be cloned. That sounds like MST.
However, I am pretty sure the xrandr was reporting VGA and HDMI outputs
(behind the splitter) that the user had some control over. I am not too
sure if I have the details right though.
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC] drm + i915 DP MST code preview
  2014-05-03  9:00     ` Chris Wilson
@ 2014-05-07  7:16       ` Aaron Plattner
  2014-05-07  8:07         ` Daniel Vetter
  2014-05-07  9:00         ` Dave Airlie
  0 siblings, 2 replies; 22+ messages in thread
From: Aaron Plattner @ 2014-05-07  7:16 UTC (permalink / raw)
  To: Chris Wilson, Dave Airlie, dri-devel

On 05/03/2014 02:00 AM, Chris Wilson wrote:
> On Sat, May 03, 2014 at 07:08:02AM +1000, Dave Airlie wrote:
>> On 2 May 2014 18:52, Chris Wilson <chris@chris-wilson.co.uk> wrote:
>>> On Fri, May 02, 2014 at 02:39:37PM +1000, Dave Airlie wrote:
>> the GUID is only on DP 1.2 devices, so you don't get one for ever
>> port, also GUIDs are wiped on powerdown on most devices, default GUID
>> is 0 except where devices have USB hubs as well, so it probably
>> doesn't make much sense to bother exposing them directly.
>
> Ok. It looks like if we do attempt to maintain persistent naming, we need
> to do it in the kernel anyway. That is to make sure that a downstream
> device always has the same type-id upon reconnection - at least for the
> lifetime of module. Or maybe the output name is irrelevant for
> preserving extended desktop configurations?

Dunno if it helps, but for roughly similar reasons we ended up naming 
the outputs based on their topology paths in the NVIDIA driver.  So for 
example a port named DP-3 that has a Dell UP2414Q attached will show up 
as two outputs named DP-3.1 and DP-3.8 since its internal bridge uses 
downstream ports 1 and 8.  This has worked out fairly well in practice.

Here's how I described it in the README:

     When DisplayPort 1.2 branch devices are present, display
     devices will be created with type- and connector-based names
     that are based on how they are connected to the branch device
     tree. For example, if a connector named DP-2 has a branch
     device attached and a DisplayPort device is connected to the
     branch device's first downstream port, a display device named
     DP-2.1 might be created. If another branch device is
     connected between the first branch device and the display
     device, the name might be DP-2.1.1.

     To avoid cluttering the output list, DisplayPort 1.2 devices
     can be deleted when they are no longer connected and are not
     named in any MetaModes. This behavior can be enabled with the
     DeleteUnusedDP12Displays option.

http://us.download.nvidia.com/XFree86/Linux-x86/337.19/README/displaydevicenames.html

-- 
Aaron

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC] drm + i915 DP MST code preview
  2014-05-07  7:16       ` Aaron Plattner
@ 2014-05-07  8:07         ` Daniel Vetter
  2014-05-07 16:46           ` Aaron Plattner
  2014-05-07 22:24           ` Dave Airlie
  2014-05-07  9:00         ` Dave Airlie
  1 sibling, 2 replies; 22+ messages in thread
From: Daniel Vetter @ 2014-05-07  8:07 UTC (permalink / raw)
  To: Aaron Plattner; +Cc: dri-devel

On Wed, May 07, 2014 at 12:16:37AM -0700, Aaron Plattner wrote:
> On 05/03/2014 02:00 AM, Chris Wilson wrote:
> >On Sat, May 03, 2014 at 07:08:02AM +1000, Dave Airlie wrote:
> >>On 2 May 2014 18:52, Chris Wilson <chris@chris-wilson.co.uk> wrote:
> >>>On Fri, May 02, 2014 at 02:39:37PM +1000, Dave Airlie wrote:
> >>the GUID is only on DP 1.2 devices, so you don't get one for ever
> >>port, also GUIDs are wiped on powerdown on most devices, default GUID
> >>is 0 except where devices have USB hubs as well, so it probably
> >>doesn't make much sense to bother exposing them directly.
> >
> >Ok. It looks like if we do attempt to maintain persistent naming, we need
> >to do it in the kernel anyway. That is to make sure that a downstream
> >device always has the same type-id upon reconnection - at least for the
> >lifetime of module. Or maybe the output name is irrelevant for
> >preserving extended desktop configurations?
> 
> Dunno if it helps, but for roughly similar reasons we ended up naming the
> outputs based on their topology paths in the NVIDIA driver.  So for example
> a port named DP-3 that has a Dell UP2414Q attached will show up as two
> outputs named DP-3.1 and DP-3.8 since its internal bridge uses downstream
> ports 1 and 8.  This has worked out fairly well in practice.
> 
> Here's how I described it in the README:
> 
>     When DisplayPort 1.2 branch devices are present, display
>     devices will be created with type- and connector-based names
>     that are based on how they are connected to the branch device
>     tree. For example, if a connector named DP-2 has a branch
>     device attached and a DisplayPort device is connected to the
>     branch device's first downstream port, a display device named
>     DP-2.1 might be created. If another branch device is
>     connected between the first branch device and the display
>     device, the name might be DP-2.1.1.
> 
>     To avoid cluttering the output list, DisplayPort 1.2 devices
>     can be deleted when they are no longer connected and are not
>     named in any MetaModes. This behavior can be enabled with the
>     DeleteUnusedDP12Displays option.
> 
> http://us.download.nvidia.com/XFree86/Linux-x86/337.19/README/displaydevicenames.html

I'm unclear how you name non-DP branch devices. Do they show up as
DP-2.1-HDMI or something similar? Or do you just keep the DP-2.1 name but
set the connector type to HDMI?

In general I think your prosoal of adding branch downstream ports to name
MST connectors sounds really good. The kms object ids will still be
random, but at least if users connect the same topology to the same ports
we should have stable names.

And of course userspace can still check the EDID serial.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC] drm + i915 DP MST code preview
  2014-05-07  7:16       ` Aaron Plattner
  2014-05-07  8:07         ` Daniel Vetter
@ 2014-05-07  9:00         ` Dave Airlie
  2014-05-07 16:42           ` Aaron Plattner
  1 sibling, 1 reply; 22+ messages in thread
From: Dave Airlie @ 2014-05-07  9:00 UTC (permalink / raw)
  To: Aaron Plattner; +Cc: dri-devel

On 7 May 2014 17:16, Aaron Plattner <aplattner@nvidia.com> wrote:
> On 05/03/2014 02:00 AM, Chris Wilson wrote:
>>
>> On Sat, May 03, 2014 at 07:08:02AM +1000, Dave Airlie wrote:
>>>
>>> On 2 May 2014 18:52, Chris Wilson <chris@chris-wilson.co.uk> wrote:
>>>>
>>>> On Fri, May 02, 2014 at 02:39:37PM +1000, Dave Airlie wrote:
>>>
>>> the GUID is only on DP 1.2 devices, so you don't get one for ever
>>> port, also GUIDs are wiped on powerdown on most devices, default GUID
>>> is 0 except where devices have USB hubs as well, so it probably
>>> doesn't make much sense to bother exposing them directly.
>>
>>
>> Ok. It looks like if we do attempt to maintain persistent naming, we need
>> to do it in the kernel anyway. That is to make sure that a downstream
>> device always has the same type-id upon reconnection - at least for the
>> lifetime of module. Or maybe the output name is irrelevant for
>> preserving extended desktop configurations?
>
>
> Dunno if it helps, but for roughly similar reasons we ended up naming the
> outputs based on their topology paths in the NVIDIA driver.  So for example
> a port named DP-3 that has a Dell UP2414Q attached will show up as two
> outputs named DP-3.1 and DP-3.8 since its internal bridge uses downstream
> ports 1 and 8.  This has worked out fairly well in practice.
>
> Here's how I described it in the README:
>
>     When DisplayPort 1.2 branch devices are present, display
>     devices will be created with type- and connector-based names
>     that are based on how they are connected to the branch device
>     tree. For example, if a connector named DP-2 has a branch
>     device attached and a DisplayPort device is connected to the
>     branch device's first downstream port, a display device named
>     DP-2.1 might be created. If another branch device is
>     connected between the first branch device and the display
>     device, the name might be DP-2.1.1.
>
>     To avoid cluttering the output list, DisplayPort 1.2 devices
>     can be deleted when they are no longer connected and are not
>     named in any MetaModes. This behavior can be enabled with the
>     DeleteUnusedDP12Displays option.
>
> http://us.download.nvidia.com/XFree86/Linux-x86/337.19/README/displaydevicenames.html

Is the cleaning up an option because it caused some problems?

I'm seeing some gnome-settings-daemon, gnome-shell crash because the
XIDs are gone away and they get X errors, just wondering if this is
what you were seeing,

Dave.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC] drm + i915 DP MST code preview
  2014-05-07  9:00         ` Dave Airlie
@ 2014-05-07 16:42           ` Aaron Plattner
  0 siblings, 0 replies; 22+ messages in thread
From: Aaron Plattner @ 2014-05-07 16:42 UTC (permalink / raw)
  To: Dave Airlie; +Cc: dri-devel

On 05/07/2014 02:00 AM, Dave Airlie wrote:
> On 7 May 2014 17:16, Aaron Plattner <aplattner@nvidia.com> wrote:
>> On 05/03/2014 02:00 AM, Chris Wilson wrote:
>>>
>>> On Sat, May 03, 2014 at 07:08:02AM +1000, Dave Airlie wrote:
>>>>
>>>> On 2 May 2014 18:52, Chris Wilson <chris@chris-wilson.co.uk> wrote:
>>>>>
>>>>> On Fri, May 02, 2014 at 02:39:37PM +1000, Dave Airlie wrote:
>>>>
>>>> the GUID is only on DP 1.2 devices, so you don't get one for ever
>>>> port, also GUIDs are wiped on powerdown on most devices, default GUID
>>>> is 0 except where devices have USB hubs as well, so it probably
>>>> doesn't make much sense to bother exposing them directly.
>>>
>>>
>>> Ok. It looks like if we do attempt to maintain persistent naming, we need
>>> to do it in the kernel anyway. That is to make sure that a downstream
>>> device always has the same type-id upon reconnection - at least for the
>>> lifetime of module. Or maybe the output name is irrelevant for
>>> preserving extended desktop configurations?
>>
>>
>> Dunno if it helps, but for roughly similar reasons we ended up naming the
>> outputs based on their topology paths in the NVIDIA driver.  So for example
>> a port named DP-3 that has a Dell UP2414Q attached will show up as two
>> outputs named DP-3.1 and DP-3.8 since its internal bridge uses downstream
>> ports 1 and 8.  This has worked out fairly well in practice.
>>
>> Here's how I described it in the README:
>>
>>      When DisplayPort 1.2 branch devices are present, display
>>      devices will be created with type- and connector-based names
>>      that are based on how they are connected to the branch device
>>      tree. For example, if a connector named DP-2 has a branch
>>      device attached and a DisplayPort device is connected to the
>>      branch device's first downstream port, a display device named
>>      DP-2.1 might be created. If another branch device is
>>      connected between the first branch device and the display
>>      device, the name might be DP-2.1.1.
>>
>>      To avoid cluttering the output list, DisplayPort 1.2 devices
>>      can be deleted when they are no longer connected and are not
>>      named in any MetaModes. This behavior can be enabled with the
>>      DeleteUnusedDP12Displays option.
>>
>> http://us.download.nvidia.com/XFree86/Linux-x86/337.19/README/displaydevicenames.html
>
> Is the cleaning up an option because it caused some problems?

> I'm seeing some gnome-settings-daemon, gnome-shell crash because the
> XIDs are gone away and they get X errors, just wondering if this is
> what you were seeing,

Yeah, this exactly.  RandR's timestamp mechanism doesn't help you avoid 
BadOutput errors, although we could probably extend the protocol to add 
that, if we decide it's necessary.

Since the topology-based IDs are relatively stable unless you get a 
whole pile of branch devices and swap them around in weird ways all day, 
it was easier to just leave them around.  They'll get reused if a device 
with that topology path ever comes back.

> Dave.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC] drm + i915 DP MST code preview
  2014-05-07  8:07         ` Daniel Vetter
@ 2014-05-07 16:46           ` Aaron Plattner
  2014-05-07 22:24           ` Dave Airlie
  1 sibling, 0 replies; 22+ messages in thread
From: Aaron Plattner @ 2014-05-07 16:46 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel

On 05/07/2014 01:07 AM, Daniel Vetter wrote:
> On Wed, May 07, 2014 at 12:16:37AM -0700, Aaron Plattner wrote:
>> On 05/03/2014 02:00 AM, Chris Wilson wrote:
>>> On Sat, May 03, 2014 at 07:08:02AM +1000, Dave Airlie wrote:
>>>> On 2 May 2014 18:52, Chris Wilson <chris@chris-wilson.co.uk> wrote:
>>>>> On Fri, May 02, 2014 at 02:39:37PM +1000, Dave Airlie wrote:
>>>> the GUID is only on DP 1.2 devices, so you don't get one for ever
>>>> port, also GUIDs are wiped on powerdown on most devices, default GUID
>>>> is 0 except where devices have USB hubs as well, so it probably
>>>> doesn't make much sense to bother exposing them directly.
>>>
>>> Ok. It looks like if we do attempt to maintain persistent naming, we need
>>> to do it in the kernel anyway. That is to make sure that a downstream
>>> device always has the same type-id upon reconnection - at least for the
>>> lifetime of module. Or maybe the output name is irrelevant for
>>> preserving extended desktop configurations?
>>
>> Dunno if it helps, but for roughly similar reasons we ended up naming the
>> outputs based on their topology paths in the NVIDIA driver.  So for example
>> a port named DP-3 that has a Dell UP2414Q attached will show up as two
>> outputs named DP-3.1 and DP-3.8 since its internal bridge uses downstream
>> ports 1 and 8.  This has worked out fairly well in practice.
>>
>> Here's how I described it in the README:
>>
>>      When DisplayPort 1.2 branch devices are present, display
>>      devices will be created with type- and connector-based names
>>      that are based on how they are connected to the branch device
>>      tree. For example, if a connector named DP-2 has a branch
>>      device attached and a DisplayPort device is connected to the
>>      branch device's first downstream port, a display device named
>>      DP-2.1 might be created. If another branch device is
>>      connected between the first branch device and the display
>>      device, the name might be DP-2.1.1.
>>
>>      To avoid cluttering the output list, DisplayPort 1.2 devices
>>      can be deleted when they are no longer connected and are not
>>      named in any MetaModes. This behavior can be enabled with the
>>      DeleteUnusedDP12Displays option.
>>
>> http://us.download.nvidia.com/XFree86/Linux-x86/337.19/README/displaydevicenames.html
>
> I'm unclear how you name non-DP branch devices. Do they show up as
> DP-2.1-HDMI or something similar? Or do you just keep the DP-2.1 name but
> set the connector type to HDMI?

I don't have any branches that support passive adapters on downstream 
ports, so I'm not sure what happens with those.

> In general I think your prosoal of adding branch downstream ports to name
> MST connectors sounds really good. The kms object ids will still be
> random, but at least if users connect the same topology to the same ports
> we should have stable names.

Yeah, the RandR output XIDs work the same way.

> And of course userspace can still check the EDID serial.
> -Daniel

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC] drm + i915 DP MST code preview
  2014-05-07  8:07         ` Daniel Vetter
  2014-05-07 16:46           ` Aaron Plattner
@ 2014-05-07 22:24           ` Dave Airlie
  2014-05-08  1:56             ` Dave Airlie
  1 sibling, 1 reply; 22+ messages in thread
From: Dave Airlie @ 2014-05-07 22:24 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel

On 7 May 2014 18:07, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Wed, May 07, 2014 at 12:16:37AM -0700, Aaron Plattner wrote:
>> On 05/03/2014 02:00 AM, Chris Wilson wrote:
>> >On Sat, May 03, 2014 at 07:08:02AM +1000, Dave Airlie wrote:
>> >>On 2 May 2014 18:52, Chris Wilson <chris@chris-wilson.co.uk> wrote:
>> >>>On Fri, May 02, 2014 at 02:39:37PM +1000, Dave Airlie wrote:
>> >>the GUID is only on DP 1.2 devices, so you don't get one for ever
>> >>port, also GUIDs are wiped on powerdown on most devices, default GUID
>> >>is 0 except where devices have USB hubs as well, so it probably
>> >>doesn't make much sense to bother exposing them directly.
>> >
>> >Ok. It looks like if we do attempt to maintain persistent naming, we need
>> >to do it in the kernel anyway. That is to make sure that a downstream
>> >device always has the same type-id upon reconnection - at least for the
>> >lifetime of module. Or maybe the output name is irrelevant for
>> >preserving extended desktop configurations?
>>
>> Dunno if it helps, but for roughly similar reasons we ended up naming the
>> outputs based on their topology paths in the NVIDIA driver.  So for example
>> a port named DP-3 that has a Dell UP2414Q attached will show up as two
>> outputs named DP-3.1 and DP-3.8 since its internal bridge uses downstream
>> ports 1 and 8.  This has worked out fairly well in practice.
>>
>> Here's how I described it in the README:
>>
>>     When DisplayPort 1.2 branch devices are present, display
>>     devices will be created with type- and connector-based names
>>     that are based on how they are connected to the branch device
>>     tree. For example, if a connector named DP-2 has a branch
>>     device attached and a DisplayPort device is connected to the
>>     branch device's first downstream port, a display device named
>>     DP-2.1 might be created. If another branch device is
>>     connected between the first branch device and the display
>>     device, the name might be DP-2.1.1.
>>
>>     To avoid cluttering the output list, DisplayPort 1.2 devices
>>     can be deleted when they are no longer connected and are not
>>     named in any MetaModes. This behavior can be enabled with the
>>     DeleteUnusedDP12Displays option.
>>
>> http://us.download.nvidia.com/XFree86/Linux-x86/337.19/README/displaydevicenames.html
>
> I'm unclear how you name non-DP branch devices. Do they show up as
> DP-2.1-HDMI or something similar? Or do you just keep the DP-2.1 name but
> set the connector type to HDMI?
>
> In general I think your prosoal of adding branch downstream ports to name
> MST connectors sounds really good. The kms object ids will still be
> random, but at least if users connect the same topology to the same ports
> we should have stable names.
>
> And of course userspace can still check the EDID serial.

I'm going to just add a property with the path and the userspace
driver can reconstruct a name from that
property, seems like the best plan,

Also for non-DP outputs, you don't know what they are, at least on
dock I have I can tell if I have
a legacy or a DP device plugged in to a port, but nothing else about
it. When nothing is plugged in
the port isn't connected.

So on the dock there is a VGA port, HDMI and DP shared, DVI-D and DP
shared, I see 3 ports
in the MST protocol, until something gets plugged in to something I
get no more info.

Dave.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC] drm + i915 DP MST code preview
  2014-05-07 22:24           ` Dave Airlie
@ 2014-05-08  1:56             ` Dave Airlie
  2014-05-08  7:24               ` Chris Wilson
  0 siblings, 1 reply; 22+ messages in thread
From: Dave Airlie @ 2014-05-08  1:56 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel

I've pushed a branch of -modesetting to

http://cgit.freedesktop.org/~airlied/xf86-video-modesetting/log/?h=dp-mst

that in theory supports this then,

it uses the same option name the nvidia driver does, and seems to work
as well as we can expect.

I'll probably port that code to intel, ati and nouveau as they get support.

Dave.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC] drm + i915 DP MST code preview
  2014-05-08  1:56             ` Dave Airlie
@ 2014-05-08  7:24               ` Chris Wilson
  2014-05-08  7:32                 ` Dave Airlie
  0 siblings, 1 reply; 22+ messages in thread
From: Chris Wilson @ 2014-05-08  7:24 UTC (permalink / raw)
  To: Dave Airlie; +Cc: dri-devel

On Thu, May 08, 2014 at 11:56:02AM +1000, Dave Airlie wrote:
> I've pushed a branch of -modesetting to
> 
> http://cgit.freedesktop.org/~airlied/xf86-video-modesetting/log/?h=dp-mst

In parse_path_blob():
char conn_id[5];

len = conn - (blob_data + 4);
if (len + 1> 5) return -1;

memcpy(conn_id, blob_data + 4, len);
conn_id[len + 1] = '\0';
        ^^^^^^^ should just be len.
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC] drm + i915 DP MST code preview
  2014-05-08  7:24               ` Chris Wilson
@ 2014-05-08  7:32                 ` Dave Airlie
  0 siblings, 0 replies; 22+ messages in thread
From: Dave Airlie @ 2014-05-08  7:32 UTC (permalink / raw)
  To: Chris Wilson, Dave Airlie, Daniel Vetter, Aaron Plattner, dri-devel

On 8 May 2014 17:24, Chris Wilson <chris@chris-wilson.co.uk> wrote:
> On Thu, May 08, 2014 at 11:56:02AM +1000, Dave Airlie wrote:
>> I've pushed a branch of -modesetting to
>>
>> http://cgit.freedesktop.org/~airlied/xf86-video-modesetting/log/?h=dp-mst
>
> In parse_path_blob():
> char conn_id[5];
>
> len = conn - (blob_data + 4);
> if (len + 1> 5) return -1;
>
> memcpy(conn_id, blob_data + 4, len);
> conn_id[len + 1] = '\0';
>         ^^^^^^^ should just be len.

Nice catch, thanks, I'll fix that,

The kernel side for this along with even more locking and refcounting
fixes is in
http://cgit.freedesktop.org/~airlied/linux/log/?h=drm-i915-mst-support

Dave.

^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2014-05-08  7:32 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-05-02  4:39 [RFC] drm + i915 DP MST code preview Dave Airlie
2014-05-02  4:39 ` [PATCH 1/8] drm/dp_helper: add defines for DP 1.2 and MST support Dave Airlie
2014-05-02  4:39 ` [PATCH 2/8] drm: add DP MST encoder type Dave Airlie
2014-05-02  4:39 ` [PATCH 3/8] drm/i915: add some registers need for displayport MST support Dave Airlie
2014-05-02  4:39 ` [PATCH 4/8] drm/crtc: add interface to reinitialise the legacy mode group Dave Airlie
2014-05-02  4:39 ` [PATCH 5/8] drm/helper: add Displayport multi-stream helper Dave Airlie
2014-05-02  4:39 ` [PATCH 6/8] i915: split some DP modesetting code into a separate function Dave Airlie
2014-05-02  4:39 ` [PATCH 7/8] drm/i915: check connector->encoder before using it Dave Airlie
2014-05-02  4:39 ` [PATCH 8/8] i915: add DP 1.2 MST support (v0.1) Dave Airlie
2014-05-02  4:51 ` [RFC] drm + i915 DP MST code preview Dave Airlie
2014-05-02  8:52 ` Chris Wilson
2014-05-02 21:08   ` Dave Airlie
2014-05-03  9:00     ` Chris Wilson
2014-05-07  7:16       ` Aaron Plattner
2014-05-07  8:07         ` Daniel Vetter
2014-05-07 16:46           ` Aaron Plattner
2014-05-07 22:24           ` Dave Airlie
2014-05-08  1:56             ` Dave Airlie
2014-05-08  7:24               ` Chris Wilson
2014-05-08  7:32                 ` Dave Airlie
2014-05-07  9:00         ` Dave Airlie
2014-05-07 16:42           ` Aaron Plattner

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.