Commit 0e32b39c authored by Dave Airlie's avatar Dave Airlie

drm/i915: add DP 1.2 MST support (v0.7)

This adds DP 1.2 MST support on Haswell systems.

Notes:
a) this reworks irq handling for DP MST ports, so that we can
avoid the mode config locking in the current hpd handlers, as
we need to process up/down msgs at a better time.

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

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

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

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

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

Changes since v0.6:
cleanup unused struct entry.

[airlied: fix some sparse warnings].
Reviewed-by: default avatarTodd Previte <tprevite@gmail.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent d05410f9
...@@ -59,6 +59,7 @@ i915-y += dvo_ch7017.o \ ...@@ -59,6 +59,7 @@ i915-y += dvo_ch7017.o \
intel_crt.o \ intel_crt.o \
intel_ddi.o \ intel_ddi.o \
intel_dp.o \ intel_dp.o \
intel_dp_mst.o \
intel_dsi_cmd.o \ intel_dsi_cmd.o \
intel_dsi.o \ intel_dsi.o \
intel_dsi_pll.o \ intel_dsi_pll.o \
......
...@@ -1717,6 +1717,13 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) ...@@ -1717,6 +1717,13 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
goto out_mtrrfree; 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_irq_init(dev);
intel_uncore_sanitize(dev); intel_uncore_sanitize(dev);
...@@ -1792,6 +1799,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) ...@@ -1792,6 +1799,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
intel_teardown_gmbus(dev); intel_teardown_gmbus(dev);
intel_teardown_mchbar(dev); intel_teardown_mchbar(dev);
pm_qos_remove_request(&dev_priv->pm_qos); pm_qos_remove_request(&dev_priv->pm_qos);
destroy_workqueue(dev_priv->dp_wq);
out_freewq:
destroy_workqueue(dev_priv->wq); destroy_workqueue(dev_priv->wq);
out_mtrrfree: out_mtrrfree:
arch_phys_wc_del(dev_priv->gtt.mtrr); arch_phys_wc_del(dev_priv->gtt.mtrr);
...@@ -1892,6 +1901,7 @@ int i915_driver_unload(struct drm_device *dev) ...@@ -1892,6 +1901,7 @@ int i915_driver_unload(struct drm_device *dev)
intel_teardown_gmbus(dev); intel_teardown_gmbus(dev);
intel_teardown_mchbar(dev); intel_teardown_mchbar(dev);
destroy_workqueue(dev_priv->dp_wq);
destroy_workqueue(dev_priv->wq); destroy_workqueue(dev_priv->wq);
pm_qos_remove_request(&dev_priv->pm_qos); pm_qos_remove_request(&dev_priv->pm_qos);
......
...@@ -518,7 +518,6 @@ static int i915_drm_freeze(struct drm_device *dev) ...@@ -518,7 +518,6 @@ static int i915_drm_freeze(struct drm_device *dev)
flush_delayed_work(&dev_priv->rps.delayed_resume_work); flush_delayed_work(&dev_priv->rps.delayed_resume_work);
intel_runtime_pm_disable_interrupts(dev);
intel_suspend_gt_powersave(dev); intel_suspend_gt_powersave(dev);
...@@ -532,6 +531,9 @@ static int i915_drm_freeze(struct drm_device *dev) ...@@ -532,6 +531,9 @@ static int i915_drm_freeze(struct drm_device *dev)
} }
drm_modeset_unlock_all(dev); drm_modeset_unlock_all(dev);
intel_dp_mst_suspend(dev);
intel_runtime_pm_disable_interrupts(dev);
intel_modeset_suspend_hw(dev); intel_modeset_suspend_hw(dev);
} }
...@@ -646,6 +648,15 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings) ...@@ -646,6 +648,15 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
intel_modeset_init_hw(dev); 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); drm_modeset_lock_all(dev);
intel_modeset_setup_hw_state(dev, true); intel_modeset_setup_hw_state(dev, true);
drm_modeset_unlock_all(dev); drm_modeset_unlock_all(dev);
......
...@@ -1595,6 +1595,15 @@ struct drm_i915_private { ...@@ -1595,6 +1595,15 @@ struct drm_i915_private {
u32 short_hpd_port_mask; u32 short_hpd_port_mask;
struct work_struct dig_port_work; 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 /* Old dri1 support infrastructure, beware the dragons ya fools entering
* here! */ * here! */
struct i915_dri1_state dri1; struct i915_dri1_state dri1;
......
...@@ -1846,7 +1846,7 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev, ...@@ -1846,7 +1846,7 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev,
* deadlock. * deadlock.
*/ */
if (queue_dig) if (queue_dig)
schedule_work(&dev_priv->dig_port_work); queue_work(dev_priv->dp_wq, &dev_priv->dig_port_work);
if (queue_hp) if (queue_hp)
schedule_work(&dev_priv->hotplug_work); schedule_work(&dev_priv->hotplug_work);
} }
...@@ -4739,7 +4739,9 @@ void intel_hpd_init(struct drm_device *dev) ...@@ -4739,7 +4739,9 @@ void intel_hpd_init(struct drm_device *dev)
list_for_each_entry(connector, &mode_config->connector_list, head) { list_for_each_entry(connector, &mode_config->connector_list, head) {
struct intel_connector *intel_connector = to_intel_connector(connector); struct intel_connector *intel_connector = to_intel_connector(connector);
connector->polled = intel_connector->polled; 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; connector->polled = DRM_CONNECTOR_POLL_HPD;
} }
......
...@@ -116,7 +116,10 @@ enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder) ...@@ -116,7 +116,10 @@ enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder)
struct drm_encoder *encoder = &intel_encoder->base; struct drm_encoder *encoder = &intel_encoder->base;
int type = intel_encoder->type; 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) { type == INTEL_OUTPUT_HDMI || type == INTEL_OUTPUT_UNKNOWN) {
struct intel_digital_port *intel_dig_port = struct intel_digital_port *intel_dig_port =
enc_to_dig_port(encoder); enc_to_dig_port(encoder);
...@@ -584,8 +587,8 @@ static int intel_ddi_calc_wrpll_link(struct drm_i915_private *dev_priv, ...@@ -584,8 +587,8 @@ static int intel_ddi_calc_wrpll_link(struct drm_i915_private *dev_priv,
return (refclk * n * 100) / (p * r); return (refclk * n * 100) / (p * r);
} }
static void intel_ddi_clock_get(struct intel_encoder *encoder, void intel_ddi_clock_get(struct intel_encoder *encoder,
struct intel_crtc_config *pipe_config) struct intel_crtc_config *pipe_config)
{ {
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
int link_clock = 0; int link_clock = 0;
...@@ -755,8 +758,7 @@ void intel_ddi_set_pipe_settings(struct drm_crtc *crtc) ...@@ -755,8 +758,7 @@ void intel_ddi_set_pipe_settings(struct drm_crtc *crtc)
int type = intel_encoder->type; int type = intel_encoder->type;
uint32_t temp; 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; temp = TRANS_MSA_SYNC_CLK;
switch (intel_crtc->config.pipe_bpp) { switch (intel_crtc->config.pipe_bpp) {
case 18: case 18:
...@@ -778,6 +780,21 @@ void intel_ddi_set_pipe_settings(struct drm_crtc *crtc) ...@@ -778,6 +780,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) void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
{ {
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
...@@ -857,7 +874,19 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc) ...@@ -857,7 +874,19 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
type == INTEL_OUTPUT_EDP) { type == INTEL_OUTPUT_EDP) {
struct intel_dp *intel_dp = enc_to_intel_dp(encoder); 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); temp |= DDI_PORT_WIDTH(intel_dp->lane_count);
} else { } else {
...@@ -874,7 +903,7 @@ void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv, ...@@ -874,7 +903,7 @@ void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv,
uint32_t reg = TRANS_DDI_FUNC_CTL(cpu_transcoder); uint32_t reg = TRANS_DDI_FUNC_CTL(cpu_transcoder);
uint32_t val = I915_READ(reg); 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; val |= TRANS_DDI_PORT_NONE;
I915_WRITE(reg, val); I915_WRITE(reg, val);
} }
...@@ -913,8 +942,11 @@ bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector) ...@@ -913,8 +942,11 @@ bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector)
case TRANS_DDI_MODE_SELECT_DP_SST: case TRANS_DDI_MODE_SELECT_DP_SST:
if (type == DRM_MODE_CONNECTOR_eDP) if (type == DRM_MODE_CONNECTOR_eDP)
return true; return true;
case TRANS_DDI_MODE_SELECT_DP_MST:
return (type == DRM_MODE_CONNECTOR_DisplayPort); 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: case TRANS_DDI_MODE_SELECT_FDI:
return (type == DRM_MODE_CONNECTOR_VGA); return (type == DRM_MODE_CONNECTOR_VGA);
...@@ -966,6 +998,9 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder, ...@@ -966,6 +998,9 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
if ((tmp & TRANS_DDI_PORT_MASK) if ((tmp & TRANS_DDI_PORT_MASK)
== TRANS_DDI_SELECT_PORT(port)) { == TRANS_DDI_SELECT_PORT(port)) {
if ((tmp & TRANS_DDI_MODE_SELECT_MASK) == TRANS_DDI_MODE_SELECT_DP_MST)
return false;
*pipe = i; *pipe = i;
return true; return true;
} }
...@@ -1272,10 +1307,15 @@ void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder) ...@@ -1272,10 +1307,15 @@ void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder)
intel_wait_ddi_buf_idle(dev_priv, port); 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; DP_TP_CTL_LINK_TRAIN_PAT1 | DP_TP_CTL_SCRAMBLE_DISABLE;
if (drm_dp_enhanced_frame_cap(intel_dp->dpcd)) if (intel_dp->is_mst)
val |= DP_TP_CTL_ENHANCED_FRAME_ENABLE; 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); I915_WRITE(DP_TP_CTL(port), val);
POSTING_READ(DP_TP_CTL(port)); POSTING_READ(DP_TP_CTL(port));
...@@ -1314,11 +1354,16 @@ void intel_ddi_fdi_disable(struct drm_crtc *crtc) ...@@ -1314,11 +1354,16 @@ void intel_ddi_fdi_disable(struct drm_crtc *crtc)
static void intel_ddi_hot_plug(struct intel_encoder *intel_encoder) static void intel_ddi_hot_plug(struct intel_encoder *intel_encoder)
{ {
struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base); struct intel_digital_port *intel_dig_port = enc_to_dig_port(&intel_encoder->base);
int type = intel_encoder->type; 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_hot_plug(intel_encoder);
intel_dp_check_link_status(intel_dp);
} }
void intel_ddi_get_config(struct intel_encoder *encoder, void intel_ddi_get_config(struct intel_encoder *encoder,
......
...@@ -101,6 +101,14 @@ static void haswell_set_pipeconf(struct drm_crtc *crtc); ...@@ -101,6 +101,14 @@ static void haswell_set_pipeconf(struct drm_crtc *crtc);
static void intel_set_pipe_csc(struct drm_crtc *crtc); static void intel_set_pipe_csc(struct drm_crtc *crtc);
static void vlv_prepare_pll(struct intel_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 { typedef struct {
int min, max; int min, max;
} intel_range_t; } intel_range_t;
...@@ -4130,6 +4138,9 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) ...@@ -4130,6 +4138,9 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
if (intel_crtc->config.has_pch_encoder) if (intel_crtc->config.has_pch_encoder)
lpt_pch_enable(crtc); 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) { for_each_encoder_on_crtc(dev, crtc, encoder) {
encoder->enable(encoder); encoder->enable(encoder);
intel_opregion_notify_encoder(encoder, true); intel_opregion_notify_encoder(encoder, true);
...@@ -4178,6 +4189,9 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) ...@@ -4178,6 +4189,9 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
intel_disable_pipe(dev_priv, pipe); 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); ironlake_pfit_disable(intel_crtc);
for_each_encoder_on_crtc(dev, crtc, encoder) for_each_encoder_on_crtc(dev, crtc, encoder)
...@@ -4336,6 +4350,9 @@ intel_display_port_power_domain(struct intel_encoder *intel_encoder) ...@@ -4336,6 +4350,9 @@ intel_display_port_power_domain(struct intel_encoder *intel_encoder)
case INTEL_OUTPUT_EDP: case INTEL_OUTPUT_EDP:
intel_dig_port = enc_to_dig_port(&intel_encoder->base); intel_dig_port = enc_to_dig_port(&intel_encoder->base);
return port_to_power_domain(intel_dig_port->port); 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: case INTEL_OUTPUT_ANALOG:
return POWER_DOMAIN_PORT_CRT; return POWER_DOMAIN_PORT_CRT;
case INTEL_OUTPUT_DSI: case INTEL_OUTPUT_DSI:
...@@ -5004,6 +5021,10 @@ static void intel_connector_check_state(struct intel_connector *connector) ...@@ -5004,6 +5021,10 @@ static void intel_connector_check_state(struct intel_connector *connector)
connector->base.base.id, connector->base.base.id,
connector->base.name); 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, WARN(connector->base.dpms == DRM_MODE_DPMS_OFF,
"wrong connector dpms state\n"); "wrong connector dpms state\n");
WARN(connector->base.encoder != &encoder->base, WARN(connector->base.encoder != &encoder->base,
...@@ -10524,6 +10545,14 @@ check_encoder_state(struct drm_device *dev) ...@@ -10524,6 +10545,14 @@ check_encoder_state(struct drm_device *dev)
if (connector->base.dpms != DRM_MODE_DPMS_OFF) if (connector->base.dpms != DRM_MODE_DPMS_OFF)
active = true; 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, WARN(!!encoder->base.crtc != enabled,
"encoder's enabled state mismatch " "encoder's enabled state mismatch "
"(expected %i, found %i)\n", "(expected %i, found %i)\n",
...@@ -11069,7 +11098,7 @@ intel_modeset_stage_output_state(struct drm_device *dev, ...@@ -11069,7 +11098,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
* for them. */ * for them. */
for (ro = 0; ro < set->num_connectors; ro++) { for (ro = 0; ro < set->num_connectors; ro++) {
if (set->connectors[ro] == &connector->base) { 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; break;
} }
} }
...@@ -11115,7 +11144,7 @@ intel_modeset_stage_output_state(struct drm_device *dev, ...@@ -11115,7 +11144,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
new_crtc)) { new_crtc)) {
return -EINVAL; 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", DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
connector->base.base.id, connector->base.base.id,
...@@ -11149,7 +11178,12 @@ intel_modeset_stage_output_state(struct drm_device *dev, ...@@ -11149,7 +11178,12 @@ intel_modeset_stage_output_state(struct drm_device *dev,
} }
} }
/* Now we've also updated encoder->new_crtc for all encoders. */ /* 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) { for_each_intel_crtc(dev, crtc) {
crtc->new_enabled = false; crtc->new_enabled = false;
......
...@@ -112,7 +112,7 @@ static void intel_dp_link_down(struct intel_dp *intel_dp); ...@@ -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 bool _edp_panel_vdd_on(struct intel_dp *intel_dp);
static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync); 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) intel_dp_max_link_bw(struct intel_dp *intel_dp)
{ {
int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE]; int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE];
...@@ -740,8 +740,9 @@ intel_dp_connector_unregister(struct intel_connector *intel_connector) ...@@ -740,8 +740,9 @@ intel_dp_connector_unregister(struct intel_connector *intel_connector)
{ {
struct intel_dp *intel_dp = intel_attached_dp(&intel_connector->base); struct intel_dp *intel_dp = intel_attached_dp(&intel_connector->base);
sysfs_remove_link(&intel_connector->base.kdev->kobj, if (!intel_connector->mst_port)
intel_dp->aux.ddc.dev.kobj.name); sysfs_remove_link(&intel_connector->base.kdev->kobj,
intel_dp->aux.ddc.dev.kobj.name);
intel_connector_unregister(intel_connector); intel_connector_unregister(intel_connector);
} }
...@@ -3309,6 +3310,33 @@ intel_dp_probe_oui(struct intel_dp *intel_dp) ...@@ -3309,6 +3310,33 @@ intel_dp_probe_oui(struct intel_dp *intel_dp)
edp_panel_vdd_off(intel_dp, false); 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) 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); struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
...@@ -3346,6 +3374,20 @@ intel_dp_get_sink_irq(struct intel_dp *intel_dp, u8 *sink_irq_vector) ...@@ -3346,6 +3374,20 @@ intel_dp_get_sink_irq(struct intel_dp *intel_dp, u8 *sink_irq_vector)
sink_irq_vector, 1) == 1; 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 static void
intel_dp_handle_test_request(struct intel_dp *intel_dp) intel_dp_handle_test_request(struct intel_dp *intel_dp)
{ {
...@@ -3353,6 +3395,63 @@ intel_dp_handle_test_request(struct intel_dp *intel_dp) ...@@ -3353,6 +3395,63 @@ intel_dp_handle_test_request(struct intel_dp *intel_dp)
drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_RESPONSE, DP_TEST_NAK); 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 * According to DP spec
* 5.1.2: * 5.1.2:
...@@ -3361,7 +3460,6 @@ intel_dp_handle_test_request(struct intel_dp *intel_dp) ...@@ -3361,7 +3460,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 * 3. Use Link Training from 2.5.3.3 and 3.5.1.3
* 4. Check link status on receipt of hot-plug interrupt * 4. Check link status on receipt of hot-plug interrupt
*/ */
void void
intel_dp_check_link_status(struct intel_dp *intel_dp) intel_dp_check_link_status(struct intel_dp *intel_dp)
{ {
...@@ -3581,6 +3679,7 @@ intel_dp_detect(struct drm_connector *connector, bool force) ...@@ -3581,6 +3679,7 @@ intel_dp_detect(struct drm_connector *connector, bool force)
enum drm_connector_status status; enum drm_connector_status status;
enum intel_display_power_domain power_domain; enum intel_display_power_domain power_domain;
struct edid *edid = NULL; struct edid *edid = NULL;
bool ret;
intel_runtime_pm_get(dev_priv); intel_runtime_pm_get(dev_priv);
...@@ -3590,6 +3689,14 @@ intel_dp_detect(struct drm_connector *connector, bool force) ...@@ -3590,6 +3689,14 @@ intel_dp_detect(struct drm_connector *connector, bool force)
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector->base.id, connector->name); 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; intel_dp->has_audio = false;
if (HAS_PCH_SPLIT(dev)) if (HAS_PCH_SPLIT(dev))
...@@ -3602,6 +3709,16 @@ intel_dp_detect(struct drm_connector *connector, bool force) ...@@ -3602,6 +3709,16 @@ intel_dp_detect(struct drm_connector *connector, bool force)
intel_dp_probe_oui(intel_dp); 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) { if (intel_dp->force_audio != HDMI_AUDIO_AUTO) {
intel_dp->has_audio = (intel_dp->force_audio == HDMI_AUDIO_ON); intel_dp->has_audio = (intel_dp->force_audio == HDMI_AUDIO_ON);
} else { } else {
...@@ -3797,6 +3914,7 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder) ...@@ -3797,6 +3914,7 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder)
struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_device *dev = intel_dp_to_dev(intel_dp);
drm_dp_aux_unregister(&intel_dp->aux); drm_dp_aux_unregister(&intel_dp->aux);
intel_dp_mst_encoder_cleanup(intel_dig_port);
drm_encoder_cleanup(encoder); drm_encoder_cleanup(encoder);
if (is_edp(intel_dp)) { if (is_edp(intel_dp)) {
cancel_delayed_work_sync(&intel_dp->panel_vdd_work); cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
...@@ -3825,28 +3943,62 @@ static const struct drm_encoder_funcs intel_dp_enc_funcs = { ...@@ -3825,28 +3943,62 @@ static const struct drm_encoder_funcs intel_dp_enc_funcs = {
.destroy = intel_dp_encoder_destroy, .destroy = intel_dp_encoder_destroy,
}; };
static void void
intel_dp_hot_plug(struct intel_encoder *intel_encoder) intel_dp_hot_plug(struct intel_encoder *intel_encoder)
{ {
struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base); return;
intel_dp_check_link_status(intel_dp);
} }
bool bool
intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd) intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd)
{ {
struct intel_dp *intel_dp = &intel_dig_port->dp; 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) DRM_DEBUG_KMS("got hpd irq on port %d - %s\n", intel_dig_port->port,
return true; long_hpd ? "long" : "short");
/* if (long_hpd) {
* we'll check the link status via the normal hot plug path later - if (!ibx_digital_port_connected(dev_priv, intel_dig_port))
* but for short hpds we should check it now goto mst_fail;
*/
intel_dp_check_link_status(intel_dp); 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; 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 */ /* Return which DP Port should be selected for Transcoder DP control */
...@@ -3897,7 +4049,7 @@ bool intel_dp_is_edp(struct drm_device *dev, enum port port) ...@@ -3897,7 +4049,7 @@ bool intel_dp_is_edp(struct drm_device *dev, enum port port)
return false; return false;
} }
static void void
intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector) intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector)
{ {
struct intel_connector *intel_connector = to_intel_connector(connector); struct intel_connector *intel_connector = to_intel_connector(connector);
...@@ -4391,6 +4543,13 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, ...@@ -4391,6 +4543,13 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
intel_dp_aux_init(intel_dp, intel_connector); intel_dp_aux_init(intel_dp, intel_connector);
/* 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)) { if (!intel_edp_init_connector(intel_dp, intel_connector, &power_seq)) {
drm_dp_aux_unregister(&intel_dp->aux); drm_dp_aux_unregister(&intel_dp->aux);
if (is_edp(intel_dp)) { if (is_edp(intel_dp)) {
...@@ -4487,3 +4646,46 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) ...@@ -4487,3 +4646,46 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
kfree(intel_connector); 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);
}
}
}
}
/*
* 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>
static 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->config.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;
}
static 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,
};
static 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_connector_register(&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);
}
static 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 */
}
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_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 * _wait_for - magic (register) wait macro
...@@ -100,6 +100,7 @@ ...@@ -100,6 +100,7 @@
#define INTEL_OUTPUT_EDP 8 #define INTEL_OUTPUT_EDP 8
#define INTEL_OUTPUT_DSI 9 #define INTEL_OUTPUT_DSI 9
#define INTEL_OUTPUT_UNKNOWN 10 #define INTEL_OUTPUT_UNKNOWN 10
#define INTEL_OUTPUT_DP_MST 11
#define INTEL_DVO_CHIP_NONE 0 #define INTEL_DVO_CHIP_NONE 0
#define INTEL_DVO_CHIP_LVDS 1 #define INTEL_DVO_CHIP_LVDS 1
...@@ -207,6 +208,10 @@ struct intel_connector { ...@@ -207,6 +208,10 @@ struct intel_connector {
/* since POLL and HPD connectors may use the same HPD line keep the native /* 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 */ state of connector->polled in case hotplug storm detection changes it */
u8 polled; u8 polled;
void *port; /* store this opaque as its illegal to dereference it */
struct intel_dp *mst_port;
}; };
typedef struct dpll { typedef struct dpll {
...@@ -351,6 +356,9 @@ struct intel_crtc_config { ...@@ -351,6 +356,9 @@ struct intel_crtc_config {
bool ips_enabled; bool ips_enabled;
bool double_wide; bool double_wide;
bool dp_encoder_is_mst;
int pbn;
}; };
struct intel_pipe_wm { struct intel_pipe_wm {
...@@ -506,6 +514,7 @@ struct intel_hdmi { ...@@ -506,6 +514,7 @@ struct intel_hdmi {
struct drm_display_mode *adjusted_mode); struct drm_display_mode *adjusted_mode);
}; };
struct intel_dp_mst_encoder;
#define DP_MAX_DOWNSTREAM_PORTS 0x10 #define DP_MAX_DOWNSTREAM_PORTS 0x10
/** /**
...@@ -545,8 +554,16 @@ struct intel_dp { ...@@ -545,8 +554,16 @@ struct intel_dp {
unsigned long last_power_on; unsigned long last_power_on;
unsigned long last_backlight_off; unsigned long last_backlight_off;
bool use_tps3; 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; struct intel_connector *attached_connector;
/* mst connector list */
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); uint32_t (*get_aux_clock_divider)(struct intel_dp *dp, int index);
/* /*
* This function returns the value we have to program the AUX_CTL * This function returns the value we have to program the AUX_CTL
...@@ -573,6 +590,13 @@ struct intel_digital_port { ...@@ -573,6 +590,13 @@ struct intel_digital_port {
bool (*hpd_pulse)(struct intel_digital_port *, bool); 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 static inline int
vlv_dport_to_channel(struct intel_digital_port *dport) vlv_dport_to_channel(struct intel_digital_port *dport)
{ {
...@@ -657,6 +681,12 @@ enc_to_dig_port(struct drm_encoder *encoder) ...@@ -657,6 +681,12 @@ enc_to_dig_port(struct drm_encoder *encoder)
return container_of(encoder, struct intel_digital_port, base.base); 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) static inline struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder)
{ {
return &enc_to_dig_port(encoder)->dp; return &enc_to_dig_port(encoder)->dp;
...@@ -719,6 +749,9 @@ void intel_ddi_get_config(struct intel_encoder *encoder, ...@@ -719,6 +749,9 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
struct intel_crtc_config *pipe_config); struct intel_crtc_config *pipe_config);
void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder); 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 */ /* intel_display.c */
const char *intel_output_name(int output); const char *intel_output_name(int output);
...@@ -871,6 +904,15 @@ void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate); ...@@ -871,6 +904,15 @@ void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate);
void intel_edp_psr_exit(struct drm_device *dev); void intel_edp_psr_exit(struct drm_device *dev);
void intel_edp_psr_init(struct drm_device *dev); void intel_edp_psr_init(struct drm_device *dev);
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 */ /* intel_dsi.c */
void intel_dsi_init(struct drm_device *dev); void intel_dsi_init(struct drm_device *dev);
......
...@@ -375,6 +375,11 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, ...@@ -375,6 +375,11 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
} }
encoder = connector->encoder; 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)) { if (!encoder || WARN_ON(!encoder->crtc)) {
DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n", DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n",
connector->name); connector->name);
......
...@@ -352,6 +352,7 @@ int intel_opregion_notify_encoder(struct intel_encoder *intel_encoder, ...@@ -352,6 +352,7 @@ int intel_opregion_notify_encoder(struct intel_encoder *intel_encoder,
case INTEL_OUTPUT_UNKNOWN: case INTEL_OUTPUT_UNKNOWN:
case INTEL_OUTPUT_DISPLAYPORT: case INTEL_OUTPUT_DISPLAYPORT:
case INTEL_OUTPUT_HDMI: case INTEL_OUTPUT_HDMI:
case INTEL_OUTPUT_DP_MST:
type = DISPLAY_TYPE_EXTERNAL_FLAT_PANEL; type = DISPLAY_TYPE_EXTERNAL_FLAT_PANEL;
break; break;
case INTEL_OUTPUT_EDP: case INTEL_OUTPUT_EDP:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment