[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