Message ID | 1401940898-2825-10-git-send-email-airlied@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
2014-06-05 1:01 GMT-03:00 Dave Airlie <airlied@gmail.com>: > From: Dave Airlie <airlied@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@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@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/intel-gfx
On 23 July 2014 06:02, Paulo Zanoni <przanoni@gmail.com> wrote: > 2014-06-05 1:01 GMT-03:00 Dave Airlie <airlied@gmail.com>: >> From: Dave Airlie <airlied@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: > There are two patches in my drm-i915-next branch They should remove the offending bits for the fbdev and powersave warning. Dave.
On Wed, Jul 23, 2014 at 6:32 AM, Dave Airlie <airlied@gmail.com> wrote: > On 23 July 2014 06:02, Paulo Zanoni <przanoni@gmail.com> wrote: >> 2014-06-05 1:01 GMT-03:00 Dave Airlie <airlied@gmail.com>: >>> From: Dave Airlie <airlied@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: >> > > There are two patches in my drm-i915-next branch > > They should remove the offending bits for the fbdev and powersave warning. Paulo, can you please test these two patches? -Daniel
On 29 July 2014 20:46, Daniel Vetter <daniel@ffwll.ch> wrote: > On Wed, Jul 23, 2014 at 6:32 AM, Dave Airlie <airlied@gmail.com> wrote: >> On 23 July 2014 06:02, Paulo Zanoni <przanoni@gmail.com> wrote: >>> 2014-06-05 1:01 GMT-03:00 Dave Airlie <airlied@gmail.com>: >>>> From: Dave Airlie <airlied@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: >>> >> >> There are two patches in my drm-i915-next branch >> >> They should remove the offending bits for the fbdev and powersave warning. > > Paulo, can you please test these two patches? > Oh he did already, didn't I push them? I must have forgotten. Dave.
On Tue, Jul 29, 2014 at 12:50 PM, Dave Airlie <airlied@gmail.com> wrote: > On 29 July 2014 20:46, Daniel Vetter <daniel@ffwll.ch> wrote: >> On Wed, Jul 23, 2014 at 6:32 AM, Dave Airlie <airlied@gmail.com> wrote: >>> On 23 July 2014 06:02, Paulo Zanoni <przanoni@gmail.com> wrote: >>>> 2014-06-05 1:01 GMT-03:00 Dave Airlie <airlied@gmail.com>: >>>>> From: Dave Airlie <airlied@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: >>>> >>> >>> There are two patches in my drm-i915-next branch >>> >>> They should remove the offending bits for the fbdev and powersave warning. >> >> Paulo, can you please test these two patches? >> > Oh he did already, didn't I push them? I must have forgotten. Oh, I guess I've missed that since it didn't go over the m-l ;-) It's there already. -Daniel
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: