From: Dave Airlie <airl...@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.

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

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

Changes since v0.3:
updates for mst apis
more state checker fixes
irq handling improvements
fbcon handling support
improved reference counting of link - fixes redocking.

Changes since v0.4:
handle gpu reset hpd reinit without oopsing
check link status on HPD irqs
fix suspend/resume

Changes since v0.5:
use proper functions to get max link/lane counts
fix another checker backtrace - due to connectors disappearing.
set output type in more places fro, unknown->displayport
don't talk to devices if no HPD asserted
check mst on short irqs only
check link status properly
rebase onto prepping irq changes.
drop unsued force_act

TODO:
possibly further state checker fixes
HDMI screaming IRQ needs fixing.

Signed-off-by: Dave Airlie <airlied at redhat.com>
---
 drivers/gpu/drm/i915/Makefile         |   1 +
 drivers/gpu/drm/i915/i915_dma.c       |  10 +
 drivers/gpu/drm/i915/i915_drv.c       |  13 +-
 drivers/gpu/drm/i915/i915_drv.h       |   9 +
 drivers/gpu/drm/i915/i915_irq.c       |   6 +-
 drivers/gpu/drm/i915/intel_ddi.c      |  85 +++++-
 drivers/gpu/drm/i915/intel_display.c  |  40 ++-
 drivers/gpu/drm/i915/intel_dp.c       | 234 ++++++++++++++-
 drivers/gpu/drm/i915/intel_dp_mst.c   | 535 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_drv.h      |  45 ++-
 drivers/gpu/drm/i915/intel_fbdev.c    |   5 +
 drivers/gpu/drm/i915/intel_opregion.c |   1 +
 12 files changed, 945 insertions(+), 39 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 7b2f3be..03b7525 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -59,6 +59,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_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 4e70de6..3bf2f1f 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1676,6 +1676,13 @@ int i915_driver_load(struct drm_device *dev, unsigned 
long flags)
                goto out_mtrrfree;
        }

+       dev_priv->dp_wq = alloc_ordered_workqueue("i915-dp", 0);
+       if (dev_priv->dp_wq == NULL) {
+               DRM_ERROR("Failed to create our dp workqueue.\n");
+               ret = -ENOMEM;
+               goto out_freewq;
+       }
+
        intel_irq_init(dev);
        intel_uncore_sanitize(dev);

@@ -1751,6 +1758,8 @@ out_gem_unload:
        intel_teardown_gmbus(dev);
        intel_teardown_mchbar(dev);
        pm_qos_remove_request(&dev_priv->pm_qos);
+       destroy_workqueue(dev_priv->dp_wq);
+out_freewq:
        destroy_workqueue(dev_priv->wq);
 out_mtrrfree:
        arch_phys_wc_del(dev_priv->gtt.mtrr);
@@ -1855,6 +1864,7 @@ int i915_driver_unload(struct drm_device *dev)
        intel_teardown_gmbus(dev);
        intel_teardown_mchbar(dev);

+       destroy_workqueue(dev_priv->dp_wq);
        destroy_workqueue(dev_priv->wq);
        pm_qos_remove_request(&dev_priv->pm_qos);

diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 5b5b82c..051cf97 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -524,7 +524,6 @@ static int i915_drm_freeze(struct drm_device *dev)
                        return error;
                }

-               drm_irq_uninstall(dev);
                dev_priv->enable_hotplug_processing = false;

                intel_disable_gt_powersave(dev);
@@ -539,6 +538,9 @@ static int i915_drm_freeze(struct drm_device *dev)
                }
                drm_modeset_unlock_all(dev);

+               intel_dp_mst_suspend(dev);
+               drm_irq_uninstall(dev);
+
                intel_modeset_suspend_hw(dev);
        }

@@ -642,6 +644,15 @@ static int __i915_drm_thaw(struct drm_device *dev, bool 
restore_gtt_mappings)

                intel_modeset_init_hw(dev);

+               {
+                       unsigned long irqflags;
+                       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+                       if (dev_priv->display.hpd_irq_setup)
+                               dev_priv->display.hpd_irq_setup(dev);
+                       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+               }
+
+               intel_dp_mst_resume(dev);
                drm_modeset_lock_all(dev);
                intel_modeset_setup_hw_state(dev, true);
                drm_modeset_unlock_all(dev);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 5fd5bf3..1f3e929 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1556,6 +1556,15 @@ struct drm_i915_private {
        u32 short_hpd_port_mask;
        struct work_struct dig_port_work;

+       /*
+        * if we get a HPD irq from DP and a HPD irq from non-DP
+        * the non-DP HPD could block the workqueue on a mode config
+        * mutex getting, that userspace may have taken. However
+        * userspace is waiting on the DP workqueue to run which is
+        * blocked behind the non-DP one.
+        */
+       struct workqueue_struct *dp_wq;
+
        /* Old dri1 support infrastructure, beware the dragons ya fools entering
         * here! */
        struct i915_dri1_state dri1;
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index e3554d9..4e8f826 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -1700,7 +1700,7 @@ static inline void intel_hpd_irq_handler(struct 
drm_device *dev,
         * deadlock.
         */
        if (queue_dig)
-               schedule_work(&dev_priv->dig_port_work);
+               queue_work(dev_priv->dp_wq, &dev_priv->dig_port_work);
        if (queue_hp)
                schedule_work(&dev_priv->hotplug_work);
 }
@@ -4566,7 +4566,9 @@ void intel_hpd_init(struct drm_device *dev)
        list_for_each_entry(connector, &mode_config->connector_list, head) {
                struct intel_connector *intel_connector = 
to_intel_connector(connector);
                connector->polled = intel_connector->polled;
-               if (!connector->polled && I915_HAS_HOTPLUG(dev) && 
intel_connector->encoder->hpd_pin > HPD_NONE)
+               if (connector->encoder && !connector->polled && 
I915_HAS_HOTPLUG(dev) && intel_connector->encoder->hpd_pin > HPD_NONE)
+                       connector->polled = DRM_CONNECTOR_POLL_HPD;
+               if (intel_connector->mst_port)
                        connector->polled = DRM_CONNECTOR_POLL_HPD;
        }

diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index 2733a3d..2d8192a 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);
@@ -630,8 +633,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);
@@ -785,7 +788,15 @@ 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);

                intel_crtc->ddi_pll_sel = link_bw_to_pll_sel(intel_dp->link_bw);
@@ -943,8 +954,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:
@@ -966,6 +976,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);
@@ -1043,7 +1068,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 {
@@ -1060,7 +1097,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);
 }
@@ -1099,8 +1136,11 @@ bool intel_ddi_connector_get_hw_state(struct 
intel_connector *intel_connector)
        case TRANS_DDI_MODE_SELECT_DP_SST:
                if (type == DRM_MODE_CONNECTOR_eDP)
                        return true;
-       case TRANS_DDI_MODE_SELECT_DP_MST:
                return (type == DRM_MODE_CONNECTOR_DisplayPort);
+       case TRANS_DDI_MODE_SELECT_DP_MST:
+               /* if the transcoder is in MST state then
+                * connector isn't connected */
+               return false;

        case TRANS_DDI_MODE_SELECT_FDI:
                return (type == DRM_MODE_CONNECTOR_VGA);
@@ -1152,6 +1192,9 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder,

                        if ((tmp & TRANS_DDI_PORT_MASK)
                            == TRANS_DDI_SELECT_PORT(port)) {
+                               if ((tmp & TRANS_DDI_MODE_SELECT_MASK) == 
TRANS_DDI_MODE_SELECT_DP_MST)
+                                       return false;
+
                                *pipe = i;
                                return true;
                        }
@@ -1478,10 +1521,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));

@@ -1520,11 +1568,16 @@ void intel_ddi_fdi_disable(struct drm_crtc *crtc)

 static void intel_ddi_hot_plug(struct intel_encoder *intel_encoder)
 {
-       struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base);
-       int type = intel_encoder->type;
+       struct intel_digital_port *intel_dig_port = 
enc_to_dig_port(&intel_encoder->base);
+       int type = intel_dig_port->base.type;
+
+       if (type != INTEL_OUTPUT_DISPLAYPORT &&
+           type != INTEL_OUTPUT_EDP &&
+           type != INTEL_OUTPUT_UNKNOWN) {
+               return;
+       }

-       if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP)
-               intel_dp_check_link_status(intel_dp);
+       intel_dp_hot_plug(intel_encoder);
 }

 void intel_ddi_get_config(struct intel_encoder *encoder,
diff --git a/drivers/gpu/drm/i915/intel_display.c 
b/drivers/gpu/drm/i915/intel_display.c
index cef64b8..85db1c2 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -68,6 +68,14 @@ static void haswell_set_pipeconf(struct drm_crtc *crtc);
 static void intel_set_pipe_csc(struct drm_crtc *crtc);
 static void vlv_prepare_pll(struct intel_crtc *crtc);

+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;
@@ -4105,6 +4113,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);
@@ -4155,6 +4166,9 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)

        intel_disable_pipe(dev_priv, pipe);

+       if (intel_crtc->config.dp_encoder_is_mst)
+               intel_ddi_set_vc_payload_alloc(crtc, false);
+
        ironlake_pfit_disable(intel_crtc);

        for_each_encoder_on_crtc(dev, crtc, encoder)
@@ -4316,6 +4330,9 @@ intel_display_port_power_domain(struct intel_encoder 
*intel_encoder)
        case INTEL_OUTPUT_EDP:
                intel_dig_port = enc_to_dig_port(&intel_encoder->base);
                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:
@@ -4936,6 +4953,10 @@ static void intel_connector_check_state(struct 
intel_connector *connector)
                              connector->base.base.id,
                              connector->base.name);

+               /* there is no real hw state for MST connectors */
+               if (connector->mst_port)
+                       return;
+
                WARN(connector->base.dpms == DRM_MODE_DPMS_OFF,
                     "wrong connector dpms state\n");
                WARN(connector->base.encoder != &encoder->base,
@@ -10080,6 +10101,14 @@ check_encoder_state(struct drm_device *dev)
                        if (connector->base.dpms != DRM_MODE_DPMS_OFF)
                                active = true;
                }
+               /*
+                * for MST connectors if we unplug the connector is gone
+                * away but the encoder is still connected to a crtc
+                * until a modeset happens in response to the hotplug.
+                */
+               if (!enabled && encoder->base.encoder_type == 
DRM_MODE_ENCODER_DPMST)
+                       continue;
+
                WARN(!!encoder->base.crtc != enabled,
                     "encoder's enabled state mismatch "
                     "(expected %i, found %i)\n",
@@ -10617,7 +10646,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;
                        }
                }
@@ -10663,7 +10692,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,
@@ -10697,7 +10726,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;
+       }
        for_each_intel_crtc(dev, crtc) {
                crtc->new_enabled = false;

diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 4603e5f..c500f63 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -112,7 +112,7 @@ static void intel_dp_link_down(struct intel_dp *intel_dp);
 static bool _edp_panel_vdd_on(struct intel_dp *intel_dp);
 static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync);

-static int
+int
 intel_dp_max_link_bw(struct intel_dp *intel_dp)
 {
        int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE];
@@ -723,8 +723,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);
 }

@@ -3175,6 +3176,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);
@@ -3212,6 +3240,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, 14);
+       if (ret != 14)
+               return false;
+
+       return true;
+}
+
 static void
 intel_dp_handle_test_request(struct intel_dp *intel_dp)
 {
@@ -3219,6 +3261,63 @@ intel_dp_handle_test_request(struct intel_dp *intel_dp)
        drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_RESPONSE, DP_TEST_NAK);
 }

+static int
+intel_dp_check_mst_status(struct intel_dp *intel_dp)
+{
+       bool bret;
+
+       if (intel_dp->is_mst) {
+               u8 esi[16] = { 0 };
+               int ret = 0;
+               int retry;
+               bool handled;
+               bret = intel_dp_get_sink_irq_esi(intel_dp, esi);
+       go_again:
+               if (bret == true) {
+
+                       /* check link status - esi[10] = 0x200c */
+                       if (intel_dp->active_mst_links && 
!drm_dp_channel_eq_ok(&esi[10], intel_dp->lane_count)) {
+                               DRM_DEBUG_KMS("channel EQ not ok, 
retraining\n");
+                               intel_dp_start_link_train(intel_dp);
+                               intel_dp_complete_link_train(intel_dp);
+                               intel_dp_stop_link_train(intel_dp);
+                       }
+
+                       DRM_DEBUG_KMS("got esi %02x %02x %02x\n", esi[0], 
esi[1], esi[2]);
+                       ret = drm_dp_mst_hpd_irq(&intel_dp->mst_mgr, esi, 
&handled);
+
+                       if (handled) {
+                               for (retry = 0; retry < 3; retry++) {
+                                       int wret;
+                                       wret = drm_dp_dpcd_write(&intel_dp->aux,
+                                                                
DP_SINK_COUNT_ESI+1,
+                                                                &esi[1], 3);
+                                       if (wret == 3) {
+                                               break;
+                                       }
+                               }
+
+                               bret = intel_dp_get_sink_irq_esi(intel_dp, esi);
+                               if (bret == true) {
+                                       DRM_DEBUG_KMS("got esi2 %02x %02x 
%02x\n", esi[0], esi[1], esi[2]);
+                                       goto go_again;
+                               }
+                       } else
+                               ret = 0;
+
+                       return ret;
+               } else {
+                       struct intel_digital_port *intel_dig_port = 
dp_to_dig_port(intel_dp);
+                       DRM_DEBUG_KMS("failed to get ESI - device may have 
failed\n");
+                       intel_dp->is_mst = false;
+                       drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, 
intel_dp->is_mst);
+                       /* send a hotplug event */
+                       
drm_kms_helper_hotplug_event(intel_dig_port->base.base.dev);
+               }
+       }
+       return -EINVAL;
+}
+
 /*
  * According to DP spec
  * 5.1.2:
@@ -3227,7 +3326,6 @@ 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
  */
-
 void
 intel_dp_check_link_status(struct intel_dp *intel_dp)
 {
@@ -3447,6 +3545,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);

@@ -3456,6 +3555,14 @@ intel_dp_detect(struct drm_connector *connector, bool 
force)
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
                      connector->base.id, connector->name);

+       if (intel_dp->is_mst) {
+               /* MST devices are disconnected from a monitor POV */
+               if (intel_encoder->type != INTEL_OUTPUT_EDP)
+                       intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
+               status = connector_status_disconnected;
+               goto out;
+       }
+
        intel_dp->has_audio = false;

        if (HAS_PCH_SPLIT(dev))
@@ -3468,6 +3575,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 {
@@ -3663,6 +3780,7 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder)
        struct drm_device *dev = intel_dp_to_dev(intel_dp);

        drm_dp_aux_unregister(&intel_dp->aux);
+       intel_dp_mst_encoder_cleanup(intel_dig_port);
        drm_encoder_cleanup(encoder);
        if (is_edp(intel_dp)) {
                cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
@@ -3691,28 +3809,62 @@ static const struct drm_encoder_funcs 
intel_dp_enc_funcs = {
        .destroy = intel_dp_encoder_destroy,
 };

-static void
+void
 intel_dp_hot_plug(struct intel_encoder *intel_encoder)
 {
-       struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base);
-
-       intel_dp_check_link_status(intel_dp);
+       return;
 }

 bool
 intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd)
 {
        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;
+       int ret;
+       if (intel_dig_port->base.type != INTEL_OUTPUT_EDP)
+               intel_dig_port->base.type = INTEL_OUTPUT_DISPLAYPORT;

-       if (long_hpd)
-               return true;
+       DRM_DEBUG_KMS("got hpd irq on port %d - %s\n", intel_dig_port->port,
+                     long_hpd ? "long" : "short");

-       /*
-        * we'll check the link status via the normal hot plug path later -
-        * but for short hpds we should check it now
-        */
-       intel_dp_check_link_status(intel_dp);
+       if (long_hpd) {
+               if (!ibx_digital_port_connected(dev_priv, intel_dig_port))
+                       goto mst_fail;
+
+               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;
+
+       } else {
+               if (intel_dp->is_mst) {
+                       ret = intel_dp_check_mst_status(intel_dp);
+                       if (ret == -EINVAL)
+                               goto mst_fail;
+               }
+
+               if (!intel_dp->is_mst) {
+                       /*
+                        * we'll check the link status via the normal hot plug 
path later -
+                        * but for short hpds we should check it now
+                        */
+                       intel_dp_check_link_status(intel_dp);
+               }
+       }
        return false;
+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("MST device may have disappeared %d vs %d\n", 
intel_dp->is_mst, intel_dp->mst_mgr.mst_state);
+               intel_dp->is_mst = false;
+               drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, 
intel_dp->is_mst);
+       }
+       return true;
 }

 /* Return which DP Port should be selected for Transcoder DP control */
@@ -3763,7 +3915,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);
@@ -4259,6 +4411,13 @@ intel_dp_init_connector(struct intel_digital_port 
*intel_dig_port,

        intel_dp->psr_setup_done = false;

+       /* init MST on ports that can support it */
+       if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
+               if (port == PORT_B || port == PORT_C || port == PORT_D) {
+                       intel_dp_mst_encoder_init(intel_dig_port, 
intel_connector->base.base.id);
+               }
+       }
+
        if (!intel_edp_init_connector(intel_dp, intel_connector, &power_seq)) {
                drm_dp_aux_unregister(&intel_dp->aux);
                if (is_edp(intel_dp)) {
@@ -4356,3 +4515,46 @@ intel_dp_init(struct drm_device *dev, int output_reg, 
enum port port)
                kfree(intel_connector);
        }
 }
+
+void intel_dp_mst_suspend(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int i;
+
+       /* disable MST */
+       for (i = 0; i < I915_MAX_PORTS; i++) {
+               struct intel_digital_port *intel_dig_port = 
dev_priv->hpd_irq_port[i];
+               if (!intel_dig_port)
+                       continue;
+
+               if (intel_dig_port->base.type == INTEL_OUTPUT_DISPLAYPORT) {
+                       if (!intel_dig_port->dp.can_mst)
+                               continue;
+                       if (intel_dig_port->dp.is_mst)
+                               
drm_dp_mst_topology_mgr_suspend(&intel_dig_port->dp.mst_mgr);
+               }
+       }
+}
+
+void intel_dp_mst_resume(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int i;
+
+       for (i = 0; i < I915_MAX_PORTS; i++) {
+               struct intel_digital_port *intel_dig_port = 
dev_priv->hpd_irq_port[i];
+               if (!intel_dig_port)
+                       continue;
+               if (intel_dig_port->base.type == INTEL_OUTPUT_DISPLAYPORT) {
+                       int ret;
+
+                       if (!intel_dig_port->dp.can_mst)
+                               continue;
+
+                       ret = 
drm_dp_mst_topology_mgr_resume(&intel_dig_port->dp.mst_mgr);
+                       if (ret != 0) {
+                               intel_dp_check_mst_status(&intel_dig_port->dp);
+                       }
+               }
+       }
+}
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..a7db741
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_dp_mst.c
@@ -0,0 +1,535 @@
+/*
+ * Copyright ? 2008 Intel Corporation
+ *             2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#include <drm/drmP.h>
+#include "i915_drv.h"
+#include "intel_drv.h"
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+
+bool
+intel_dp_mst_compute_config(struct intel_encoder *encoder,
+                           struct intel_crtc_config *pipe_config)
+{
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_digital_port *intel_dig_port = intel_mst->primary;
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct drm_device *dev = encoder->base.dev;
+       int bpp;
+       int lane_count, slots;
+       struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
+       struct intel_connector *found = NULL, *intel_connector;
+       int mst_pbn;
+
+       pipe_config->dp_encoder_is_mst = true;
+       pipe_config->has_pch_encoder = false;
+       pipe_config->has_dp_encoder = true;
+       bpp = 24;
+       /*
+        * for MST we always configure max link bw - the spec doesn't
+        * seem to suggest we should do otherwise.
+        */
+       lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
+       intel_dp->link_bw = intel_dp_max_link_bw(intel_dp);
+       intel_dp->lane_count = lane_count;
+
+       pipe_config->pipe_bpp = 24;
+       pipe_config->port_clock = 
drm_dp_bw_code_to_link_rate(intel_dp->link_bw);
+
+       list_for_each_entry(intel_connector, &dev->mode_config.connector_list, 
base.head) {
+               if (intel_connector->new_encoder == encoder) {
+                       found = intel_connector;
+                       break;
+               }
+       }
+
+       if (!found) {
+               DRM_ERROR("can't find connector\n");
+               return false;
+       }
+
+       mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->clock, bpp);
+
+       pipe_config->pbn = mst_pbn;
+       slots = drm_dp_find_vcpi_slots(&intel_dp->mst_mgr, mst_pbn);
+
+       intel_link_compute_m_n(bpp, lane_count,
+                              adjusted_mode->crtc_clock,
+                              pipe_config->port_clock,
+                              &pipe_config->dp_m_n);
+
+       pipe_config->dp_m_n.tu = slots;
+       return true;
+
+}
+
+static void intel_mst_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;
+       int ret;
+
+       DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
+
+       drm_dp_mst_reset_vcpi_slots(&intel_dp->mst_mgr, intel_mst->port);
+
+       ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
+       if (ret) {
+               DRM_ERROR("failed to update payload %d\n", ret);
+       }
+}
+
+static void intel_mst_post_disable_dp(struct intel_encoder *encoder)
+{
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_digital_port *intel_dig_port = intel_mst->primary;
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+
+       DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
+
+       /* this can fail */
+       drm_dp_check_act_status(&intel_dp->mst_mgr);
+       /* and this can also fail */
+       drm_dp_update_payload_part2(&intel_dp->mst_mgr);
+
+       drm_dp_mst_deallocate_vcpi(&intel_dp->mst_mgr, intel_mst->port);
+
+       intel_dp->active_mst_links--;
+       intel_mst->port = NULL;
+       if (intel_dp->active_mst_links == 0) {
+               intel_dig_port->base.post_disable(&intel_dig_port->base);
+               intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF);
+       }
+}
+
+static void intel_mst_pre_enable_dp(struct intel_encoder *encoder)
+{
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_digital_port *intel_dig_port = intel_mst->primary;
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum port port = intel_dig_port->port;
+       int ret;
+       uint32_t temp;
+       struct intel_connector *found = NULL, *intel_connector;
+       int slots;
+       struct drm_crtc *crtc = encoder->base.crtc;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+       list_for_each_entry(intel_connector, &dev->mode_config.connector_list, 
base.head) {
+               if (intel_connector->new_encoder == encoder) {
+                       found = intel_connector;
+                       break;
+               }
+       }
+
+       if (!found) {
+               DRM_ERROR("can't find connector\n");
+               return;
+       }
+
+       DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
+       intel_mst->port = found->port;
+
+       if (intel_dp->active_mst_links == 0) {
+               enum port port = intel_ddi_get_encoder_port(encoder);
+
+               I915_WRITE(PORT_CLK_SEL(port), intel_crtc->ddi_pll_sel);
+
+               intel_ddi_init_dp_buf_reg(&intel_dig_port->base);
+
+               intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+
+
+               intel_dp_start_link_train(intel_dp);
+               intel_dp_complete_link_train(intel_dp);
+               intel_dp_stop_link_train(intel_dp);
+       }
+
+       ret = drm_dp_mst_allocate_vcpi(&intel_dp->mst_mgr,
+                                      intel_mst->port, intel_crtc->config.pbn, 
&slots);
+       if (ret == false) {
+               DRM_ERROR("failed to allocate vcpi\n");
+               return;
+       }
+
+
+       intel_dp->active_mst_links++;
+       temp = I915_READ(DP_TP_STATUS(port));
+       I915_WRITE(DP_TP_STATUS(port), temp);
+
+       ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
+}
+
+static void intel_mst_enable_dp(struct intel_encoder *encoder)
+{
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       struct intel_digital_port *intel_dig_port = intel_mst->primary;
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct drm_device *dev = intel_dig_port->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum port port = intel_dig_port->port;
+       int ret;
+
+       DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
+
+       if (wait_for((I915_READ(DP_TP_STATUS(port)) & DP_TP_STATUS_ACT_SENT),
+                    1))
+               DRM_ERROR("Timed out waiting for ACT sent\n");
+
+       ret = drm_dp_check_act_status(&intel_dp->mst_mgr);
+
+       ret = drm_dp_update_payload_part2(&intel_dp->mst_mgr);
+}
+
+static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder,
+                                     enum pipe *pipe)
+{
+       struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+       *pipe = intel_mst->pipe;
+       if (intel_mst->port)
+               return true;
+       return false;
+}
+
+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)
+{
+       struct intel_connector *intel_connector = to_intel_connector(connector);
+       struct intel_dp *intel_dp = intel_connector->mst_port;
+       return &intel_dp->mst_encoders[0]->base.base;
+}
+
+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) {
+               enum pipe pipe;
+               if (!connector->encoder->get_hw_state(connector->encoder, 
&pipe))
+                       return false;
+               return true;
+       }
+       return false;
+}
+
+static struct drm_connector *intel_dp_add_mst_connector(struct 
drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, char *pathprop)
+{
+       struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
+       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+       struct drm_device *dev = intel_dig_port->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_connector *intel_connector;
+       struct drm_connector *connector;
+       int i;
+
+       intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL);
+       if (!intel_connector)
+               return NULL;
+
+       connector = &intel_connector->base;
+       drm_connector_init(dev, connector, &intel_dp_mst_connector_funcs, 
DRM_MODE_CONNECTOR_DisplayPort);
+       drm_connector_helper_add(connector, 
&intel_dp_mst_connector_helper_funcs);
+
+       intel_connector->unregister = intel_connector_unregister;
+       intel_connector->get_hw_state = intel_dp_mst_get_hw_state;
+       intel_connector->mst_port = intel_dp;
+       intel_connector->port = port;
+
+       for (i = PIPE_A; i <= PIPE_C; i++) {
+               drm_mode_connector_attach_encoder(&intel_connector->base,
+                                                 
&intel_dp->mst_encoders[i]->base.base);
+       }
+       intel_dp_add_properties(intel_dp, connector);
+
+       drm_object_attach_property(&connector->base, 
dev->mode_config.path_property, 0);
+       drm_mode_connector_set_path_property(connector, pathprop);
+       drm_reinit_primary_mode_group(dev);
+       mutex_lock(&dev->mode_config.mutex);
+       drm_fb_helper_add_one_connector(&dev_priv->fbdev->helper, connector);
+       mutex_unlock(&dev->mode_config.mutex);
+       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;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       /* need to nuke the connector */
+       mutex_lock(&dev->mode_config.mutex);
+       intel_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+       mutex_unlock(&dev->mode_config.mutex);
+
+       intel_connector->unregister(intel_connector);
+
+       mutex_lock(&dev->mode_config.mutex);
+       drm_fb_helper_remove_one_connector(&dev_priv->fbdev->helper, connector);
+       drm_connector_cleanup(connector);
+       mutex_unlock(&dev->mode_config.mutex);
+
+       drm_reinit_primary_mode_group(dev);
+
+       kfree(intel_connector);
+       DRM_DEBUG_KMS("\n");
+}
+
+static void intel_dp_mst_hotplug(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;
+
+       drm_kms_helper_hotplug_event(dev);
+}
+
+struct drm_dp_mst_topology_cbs mst_cbs = {
+       .add_connector = intel_dp_add_mst_connector,
+       .destroy_connector = intel_dp_destroy_mst_connector,
+       .hotplug = intel_dp_mst_hotplug,
+};
+
+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->disable = intel_mst_disable_dp;
+       intel_encoder->post_disable = intel_mst_post_disable_dp;
+       intel_encoder->pre_enable = intel_mst_pre_enable_dp;
+       intel_encoder->enable = intel_mst_enable_dp;
+       intel_encoder->get_hw_state = intel_dp_mst_enc_get_hw_state;
+       intel_encoder->get_config = intel_dp_mst_enc_get_config;
+
+       return intel_mst;
+
+}
+
+static bool
+intel_dp_create_fake_mst_encoders(struct intel_digital_port *intel_dig_port)
+{
+       int i;
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+
+       for (i = PIPE_A; i <= PIPE_C; i++)
+               intel_dp->mst_encoders[i] = 
intel_dp_create_fake_mst_encoder(intel_dig_port, i);
+       return true;
+}
+
+int
+intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int 
conn_base_id)
+{
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct drm_device *dev = intel_dig_port->base.base.dev;
+       int ret;
+
+       intel_dp->can_mst = true;
+       intel_dp->mst_mgr.cbs = &mst_cbs;
+
+       /* create encoders */
+       intel_dp_create_fake_mst_encoders(intel_dig_port);
+       ret = drm_dp_mst_topology_mgr_init(&intel_dp->mst_mgr, dev->dev, 
&intel_dp->aux, 16, 3, conn_base_id);
+       if (ret) {
+               intel_dp->can_mst = false;
+               return ret;
+       }
+       return 0;
+}
+
+void
+intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port)
+{
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+
+       if (!intel_dp->can_mst)
+               return;
+
+       drm_dp_mst_topology_mgr_destroy(&intel_dp->mst_mgr);
+       /* encoders will get killed by normal cleanup */
+}
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index da6c440..1edb38a 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,10 @@ struct intel_connector {
        /* since POLL and HPD connectors may use the same HPD line keep the 
native
           state of connector->polled in case hotplug storm detection changes 
it */
        u8 polled;
+
+       void *port; /* store this opaque as its illegal to dereference it */
+
+       struct intel_dp *mst_port;
 };

 typedef struct dpll {
@@ -347,6 +352,9 @@ struct intel_crtc_config {
        bool ips_enabled;

        bool double_wide;
+
+       bool dp_encoder_is_mst;
+       int pbn;
 };

 struct intel_pipe_wm {
@@ -498,6 +506,7 @@ struct intel_hdmi {
                               struct drm_display_mode *adjusted_mode);
 };

+struct intel_dp_mst_encoder;
 #define DP_MAX_DOWNSTREAM_PORTS                0x10

 /**
@@ -538,8 +547,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
@@ -566,6 +584,13 @@ struct intel_digital_port {
        bool (*hpd_pulse)(struct intel_digital_port *, bool);
 };

+struct intel_dp_mst_encoder {
+       struct intel_encoder base;
+       enum pipe pipe;
+       struct intel_digital_port *primary;
+       void *port; /* store this opaque as its illegal to dereference it */
+};
+
 static inline int
 vlv_dport_to_channel(struct intel_digital_port *dport)
 {
@@ -650,6 +675,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;
@@ -715,6 +746,9 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
                          struct intel_crtc_config *pipe_config);

 void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder);
+void intel_ddi_clock_get(struct intel_encoder *encoder,
+                        struct intel_crtc_config *pipe_config);
+void intel_ddi_set_vc_payload_alloc(struct drm_crtc *crtc, bool state);

 /* intel_display.c */
 const char *intel_output_name(int output);
@@ -834,6 +868,15 @@ void intel_edp_psr_update(struct drm_device *dev);
 void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate);
 bool intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool 
long_hpd);

+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);
+void intel_dp_mst_suspend(struct drm_device *dev);
+void intel_dp_mst_resume(struct drm_device *dev);
+int intel_dp_max_link_bw(struct intel_dp *intel_dp);
+void intel_dp_hot_plug(struct intel_encoder *intel_encoder);
+/* intel_dp_mst.c */
+int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int 
conn_id);
+void intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port);
 /* intel_dsi.c */
 bool intel_dsi_init(struct drm_device *dev);

diff --git a/drivers/gpu/drm/i915/intel_fbdev.c 
b/drivers/gpu/drm/i915/intel_fbdev.c
index e2d4161..1949350 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -349,6 +349,11 @@ static bool intel_fb_initial_config(struct drm_fb_helper 
*fb_helper,
                }

                encoder = connector->encoder;
+               if (!encoder) {
+                       struct drm_connector_helper_funcs *connector_funcs;
+                       connector_funcs = connector->helper_private;
+                       encoder = connector_funcs->best_encoder(connector);
+               }
                if (!encoder || WARN_ON(!encoder->crtc)) {
                        DRM_DEBUG_KMS("connector %s has no encoder or crtc, 
skipping\n",
                                      connector->name);
diff --git a/drivers/gpu/drm/i915/intel_opregion.c 
b/drivers/gpu/drm/i915/intel_opregion.c
index b812e9d..27d4570 100644
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -352,6 +352,7 @@ int intel_opregion_notify_encoder(struct intel_encoder 
*intel_encoder,
        case INTEL_OUTPUT_UNKNOWN:
        case INTEL_OUTPUT_DISPLAYPORT:
        case INTEL_OUTPUT_HDMI:
+       case INTEL_OUTPUT_DP_MST:
                type = DISPLAY_TYPE_EXTERNAL_FLAT_PANEL;
                break;
        case INTEL_OUTPUT_EDP:
-- 
1.9.3

Reply via email to