[Intel-gfx] [PATCH 09/11] i915: add DP 1.2 MST support (v0.6)

Paulo Zanoni przanoni at gmail.com
Tue Jul 22 22:02:50 CEST 2014


2014-06-05 1:01 GMT-03:00 Dave Airlie <airlied at gmail.com>:
> From: Dave Airlie <airlied at redhat.com>
>
> This adds DP 1.2 MST support on Haswell systems.

Hi

It looks like drm-intel-nightly now includes this patch. It actually
includes v7, but I couldn't find it on my mail dirs.

Just by booting the machine with this patch, I get:

[   11.013593] ------------[ cut here ]------------
[   11.013627] WARNING: CPU: 1 PID: 389 at
drivers/gpu/drm/i915/intel_fbdev.c:383
intel_fb_initial_config+0x3ca/0x660 [i915]()
[   11.013629] Modules linked in: i2c_designware_platform i915(+)
i2c_designware_core i2c_algo_bit drm_kms_helper drm sg sd_mod ahci
ehci_pci libahci ehci_hcd e1000e xhci_hcd sdhci_acpi sdhci
[   11.013651] CPU: 1 PID: 389 Comm: udevd Not tainted
3.16.0-rc6.1407221626+ #673
[   11.013654] Hardware name: Intel Corporation Shark Bay Client
platform/WhiteTip Mountain 1, BIOS HSWLPTU1.86C.0137.R00.1403031632
03/03/2014
[   11.013656]  0000000000000009 ffff88009d14b790 ffffffff816b6a61
0000000000000000
[   11.013662]  ffff88009d14b7c8 ffffffff81075d88 ffff8801483db10b
ffff880148279400
[   11.013667]  ffff88009d0caf00 0000000000000003 0000000000000003
ffff88009d14b7d8
[   11.013672] Call Trace:
[   11.013680]  [<ffffffff816b6a61>] dump_stack+0x4d/0x66
[   11.013686]  [<ffffffff81075d88>] warn_slowpath_common+0x78/0xa0
[   11.013690]  [<ffffffff81075e65>] warn_slowpath_null+0x15/0x20
[   11.013714]  [<ffffffffa01a8e5a>] intel_fb_initial_config+0x3ca/0x660 [i915]
[   11.013724]  [<ffffffffa012ebef>] drm_setup_crtcs+0x17f/0x830
[drm_kms_helper]
[   11.013729]  [<ffffffff810c47fd>] ? trace_hardirqs_on_caller+0x15d/0x200
[   11.013736]  [<ffffffffa012f58a>]
drm_fb_helper_initial_config+0x19a/0x4d0 [drm_kms_helper]
[   11.013740]  [<ffffffff810c47fd>] ? trace_hardirqs_on_caller+0x15d/0x200
[   11.013762]  [<ffffffffa01a9c4a>] intel_fbdev_initial_config+0x1a/0x20 [i915]
[   11.013789]  [<ffffffffa01ce0eb>] i915_driver_load+0x111b/0x1130 [i915]
[   11.013803]  [<ffffffffa00c4615>] drm_dev_register+0xa5/0x100 [drm]
[   11.013815]  [<ffffffffa00c7108>] drm_get_pci_dev+0x88/0x1f0 [drm]
[   11.013834]  [<ffffffffa013e586>] i915_pci_probe+0x36/0x50 [i915]
[   11.013839]  [<ffffffff8133e12d>] local_pci_probe+0x3d/0xa0
[   11.013843]  [<ffffffff8133f385>] ? pci_match_device+0xe5/0x110
[   11.013847]  [<ffffffff8133f4b9>] pci_device_probe+0xc9/0x120
[   11.013853]  [<ffffffff813f65e9>] driver_probe_device+0x89/0x250
[   11.013857]  [<ffffffff813f687b>] __driver_attach+0x8b/0x90
[   11.013879]  [<ffffffff813f67f0>] ? __device_attach+0x40/0x40
[   11.013883]  [<ffffffff813f4723>] bus_for_each_dev+0x63/0xa0
[   11.013888]  [<ffffffff813f6139>] driver_attach+0x19/0x20
[   11.013892]  [<ffffffff813f5db8>] bus_add_driver+0x178/0x230
[   11.013896]  [<ffffffff813f6fcf>] driver_register+0x5f/0xf0
[   11.013899]  [<ffffffff8133da48>] __pci_register_driver+0x58/0x60
[   11.013911]  [<ffffffffa00c736a>] drm_pci_init+0xfa/0x130 [drm]
[   11.013915]  [<ffffffff810c48ad>] ? trace_hardirqs_on+0xd/0x10
[   11.013918]  [<ffffffffa0222000>] ? 0xffffffffa0221fff
[   11.013937]  [<ffffffffa0222089>] i915_init+0x89/0x90 [i915]
[   11.013942]  [<ffffffff810002d8>] do_one_initcall+0x98/0x1f0
[   11.013947]  [<ffffffff811920b2>] ? __vunmap+0xa2/0x100
[   11.013952]  [<ffffffff810f904a>] load_module+0x1bea/0x22d0
[   11.013956]  [<ffffffff810f5ab0>] ? symbol_put_addr+0x40/0x40
[   11.013960]  [<ffffffff810f5df9>] ? copy_module_from_fd.isra.49+0x119/0x170
[   11.013965]  [<ffffffff810f9876>] SyS_finit_module+0x76/0x80
[   11.013970]  [<ffffffff816c0bd2>] system_call_fastpath+0x16/0x1b
[   11.013973] ---[ end trace e4d35cd76b2fc39f ]---

This is the WARN that makes the code print
[drm:intel_fb_initial_config] connector HDMI-A-2 has no encoder or
crtc, skipping


Then, if I do a normal S3 suspend/resume cycle with the desktop
enabled and everything running, I get:

[   32.431306] ------------[ cut here ]------------
[   32.431341] WARNING: CPU: 2 PID: 3200 at
drivers/gpu/drm/i915/intel_pm.c:4979
intel_suspend_gt_powersave+0x49/0x50 [i915]()
[   32.431370] Modules linked in: fuse intel_rapl x86_pkg_temp_thermal
intel_powerclamp serio_raw i2c_i801 i2c_designware_platform
i2c_designware_core i915 i2c_algo_bit drm_kms_helper drm mei_me mei sg
sd_mod ehci_pci ehci_hcd ahci libahci e1000e xhci_hcd sdhci_acpi sdhci
[   32.431373] CPU: 2 PID: 3200 Comm: kworker/u16:14 Tainted: G
W     3.16.0-rc6.1407221626+ #673
[   32.431375] Hardware name: Intel Corporation Shark Bay Client
platform/WhiteTip Mountain 1, BIOS HSWLPTU1.86C.0137.R00.1403031632
03/03/2014
[   32.431379] Workqueue: events_unbound async_run_entry_fn
[   32.431383]  0000000000000009 ffff88009d1f3be8 ffffffff816b6a61
0000000000000000
[   32.431386]  ffff88009d1f3c20 ffffffff81075d88 ffff880148610000
0000000000000000
[   32.431390]  ffff880148610000 ffff88009d86e758 ffffffff81a5d47a
ffff88009d1f3c30
[   32.431390] Call Trace:
[   32.431396]  [<ffffffff816b6a61>] dump_stack+0x4d/0x66
[   32.431399]  [<ffffffff81075d88>] warn_slowpath_common+0x78/0xa0
[   32.431402]  [<ffffffff81075e65>] warn_slowpath_null+0x15/0x20
[   32.431413]  [<ffffffffa015a359>] intel_suspend_gt_powersave+0x49/0x50 [i915]
[   32.431422]  [<ffffffffa014c096>] i915_drm_freeze+0x96/0x190 [i915]
[   32.431430]  [<ffffffffa014c21a>] i915_pm_suspend+0x2a/0x50 [i915]
[   32.431434]  [<ffffffff8133ee01>] pci_pm_suspend+0x71/0x160
[   32.431437]  [<ffffffff8133ed90>] ? pci_pm_freeze+0xe0/0xe0
[   32.431440]  [<ffffffff8140071a>] dpm_run_callback+0x3a/0x130
[   32.431443]  [<ffffffff81401e00>] __device_suspend+0xf0/0x2f0
[   32.431445]  [<ffffffff8140201a>] async_suspend+0x1a/0xa0
[   32.431448]  [<ffffffff810a0224>] async_run_entry_fn+0x34/0x120
[   32.431451]  [<ffffffff8109306f>] process_one_work+0x1cf/0x550
[   32.431454]  [<ffffffff8109300f>] ? process_one_work+0x16f/0x550
[   32.431457]  [<ffffffff81093923>] worker_thread+0x63/0x540
[   32.431461]  [<ffffffff810938c0>] ? create_and_start_worker+0x50/0x50
[   32.431464]  [<ffffffff81099ef0>] kthread+0x100/0x120
[   32.431467]  [<ffffffff81099df0>] ? kthread_create_on_node+0x230/0x230
[   32.431470]  [<ffffffff816c0b2c>] ret_from_fork+0x7c/0xb0
[   32.431472]  [<ffffffff81099df0>] ? kthread_create_on_node+0x230/0x230
[   32.431474] ---[ end trace 466388920de060e0 ]---

This is the warn caused by "WARN_ON(intel_irqs_enabled(dev_priv));".


Also, if I run intel-gpu-tools/tests/pm-rpm, which disables all the
screens before trying to suspend, I still get the following backtrace:

[  548.769561] [drm:intel_runtime_resume] Device resumed
[  548.769716] [drm:intel_display_power_get] enabling always-on
[  548.769718] [drm:intel_display_power_get] enabling display
[  548.769735] [drm:hsw_set_power_well] Enabling power well
[  548.770519] [drm] Enabling RC6 states: RC6 on, RC6p off, RC6pp off
[  548.770540] [drm:gen6_enable_rps] Overclocking supported. Max:
1000MHz, Overclock max: 1000MHz
[  548.770670] ------------[ cut here ]------------
[  548.770696] WARNING: CPU: 2 PID: 3186 at
drivers/gpu/drm/i915/intel_pm.c:4979
intel_suspend_gt_powersave+0x49/0x50 [i915]()
[  548.770729] Modules linked in: fuse intel_rapl x86_pkg_temp_thermal
intel_powerclamp serio_raw i2c_i801 mei_me mei i2c_designware_platform
i915 i2c_designware_core i2c_algo_bit drm_kms_helper drm sg sd_mod
ahci ehci_pci libahci ehci_hcd e1000e xhci_hcd sdhci_acpi sdhci
[  548.770734] CPU: 2 PID: 3186 Comm: kworker/u16:13 Tainted: G
W     3.16.0-rc6.1407221626+ #673
[  548.770736] Hardware name: Intel Corporation Shark Bay Client
platform/WhiteTip Mountain 1, BIOS HSWLPTU1.86C.0137.R00.1403031632
03/03/2014
[  548.770743] Workqueue: events_unbound async_run_entry_fn
[  548.770751]  0000000000000009 ffff88009b5c7be8 ffffffff816b6a61
0000000000000000
[  548.770757]  ffff88009b5c7c20 ffffffff81075d88 ffff88009d470000
0000000000000000
[  548.770762]  ffff88009d470000 ffff88009d6bc758 ffffffff81a5d47a
ffff88009b5c7c30
[  548.770764] Call Trace:
[  548.770773]  [<ffffffff816b6a61>] dump_stack+0x4d/0x66
[  548.770778]  [<ffffffff81075d88>] warn_slowpath_common+0x78/0xa0
[  548.770781]  [<ffffffff81075e65>] warn_slowpath_null+0x15/0x20
[  548.770795]  [<ffffffffa014c359>] intel_suspend_gt_powersave+0x49/0x50 [i915]
[  548.770804]  [<ffffffffa013e096>] i915_drm_freeze+0x96/0x190 [i915]
[  548.770814]  [<ffffffffa013e21a>] i915_pm_suspend+0x2a/0x50 [i915]
[  548.770818]  [<ffffffff8133ee01>] pci_pm_suspend+0x71/0x160
[  548.770821]  [<ffffffff8133ed90>] ? pci_pm_freeze+0xe0/0xe0
[  548.770825]  [<ffffffff8140071a>] dpm_run_callback+0x3a/0x130
[  548.770828]  [<ffffffff81401e00>] __device_suspend+0xf0/0x2f0
[  548.770831]  [<ffffffff8140201a>] async_suspend+0x1a/0xa0
[  548.770833]  [<ffffffff810a0224>] async_run_entry_fn+0x34/0x120
[  548.770837]  [<ffffffff8109306f>] process_one_work+0x1cf/0x550
[  548.770841]  [<ffffffff8109300f>] ? process_one_work+0x16f/0x550
[  548.770844]  [<ffffffff81093923>] worker_thread+0x63/0x540
[  548.770848]  [<ffffffff810938c0>] ? create_and_start_worker+0x50/0x50
[  548.770852]  [<ffffffff81099ef0>] kthread+0x100/0x120
[  548.770855]  [<ffffffff81099df0>] ? kthread_create_on_node+0x230/0x230
[  548.770859]  [<ffffffff816c0b2c>] ret_from_fork+0x7c/0xb0
[  548.770861]  [<ffffffff81099df0>] ? kthread_create_on_node+0x230/0x230
[  548.770863] ---[ end trace e4d35cd76b2fc3a0 ]---
[  548.774490] [drm:intel_display_power_put] disabling display
[  548.774493] [drm:hsw_set_power_well] Requesting to disable the power well
[  548.774494] [drm:intel_display_power_put] disabling always-on
[  548.796952] PM: suspend of devices complete after 54.240 msecs

If I revert both "drm/i915: mst topology dumper in debugfs (v0.2)" and
"drm/i915: add DP 1.2 MST support (v0.7)", then the WARNs go away.

My machine is a HSW ULT, and it has an eDP panel and an HDMI monitor attached.

Thanks,
Paulo

>
> 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
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx



-- 
Paulo Zanoni



More information about the Intel-gfx mailing list