mirror of
https://github.com/ukui/kernel.git
synced 2026-03-09 10:07:04 -07:00
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: Todd Previte <tprevite@gmail.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -1717,6 +1717,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);
|
||||
|
||||
@@ -1792,6 +1799,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);
|
||||
@@ -1892,6 +1901,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);
|
||||
|
||||
|
||||
@@ -518,7 +518,6 @@ static int i915_drm_freeze(struct drm_device *dev)
|
||||
|
||||
flush_delayed_work(&dev_priv->rps.delayed_resume_work);
|
||||
|
||||
intel_runtime_pm_disable_interrupts(dev);
|
||||
|
||||
intel_suspend_gt_powersave(dev);
|
||||
|
||||
@@ -532,6 +531,9 @@ static int i915_drm_freeze(struct drm_device *dev)
|
||||
}
|
||||
drm_modeset_unlock_all(dev);
|
||||
|
||||
intel_dp_mst_suspend(dev);
|
||||
intel_runtime_pm_disable_interrupts(dev);
|
||||
|
||||
intel_modeset_suspend_hw(dev);
|
||||
}
|
||||
|
||||
@@ -646,6 +648,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);
|
||||
|
||||
@@ -1595,6 +1595,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;
|
||||
|
||||
@@ -1846,7 +1846,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);
|
||||
}
|
||||
@@ -4739,7 +4739,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -584,8 +587,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;
|
||||
int link_clock = 0;
|
||||
@@ -755,8 +758,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:
|
||||
@@ -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)
|
||||
{
|
||||
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
||||
@@ -857,7 +874,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 {
|
||||
@@ -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 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);
|
||||
}
|
||||
@@ -913,8 +942,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);
|
||||
@@ -966,6 +998,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;
|
||||
}
|
||||
@@ -1272,10 +1307,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));
|
||||
|
||||
@@ -1314,11 +1354,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)
|
||||
intel_dp_check_link_status(intel_dp);
|
||||
if (type != INTEL_OUTPUT_DISPLAYPORT &&
|
||||
type != INTEL_OUTPUT_EDP &&
|
||||
type != INTEL_OUTPUT_UNKNOWN) {
|
||||
return;
|
||||
}
|
||||
|
||||
intel_dp_hot_plug(intel_encoder);
|
||||
}
|
||||
|
||||
void intel_ddi_get_config(struct intel_encoder *encoder,
|
||||
|
||||
@@ -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 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;
|
||||
@@ -4130,6 +4138,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);
|
||||
@@ -4178,6 +4189,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)
|
||||
@@ -4336,6 +4350,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:
|
||||
@@ -5004,6 +5021,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,
|
||||
@@ -10524,6 +10545,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",
|
||||
@@ -11069,7 +11098,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;
|
||||
}
|
||||
}
|
||||
@@ -11115,7 +11144,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,
|
||||
@@ -11149,7 +11178,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;
|
||||
|
||||
|
||||
+218
-16
@@ -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];
|
||||
@@ -740,8 +740,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);
|
||||
}
|
||||
|
||||
@@ -3309,6 +3310,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);
|
||||
@@ -3346,6 +3374,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)
|
||||
{
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
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:
|
||||
@@ -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
|
||||
* 4. Check link status on receipt of hot-plug interrupt
|
||||
*/
|
||||
|
||||
void
|
||||
intel_dp_check_link_status(struct intel_dp *intel_dp)
|
||||
{
|
||||
@@ -3581,6 +3679,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);
|
||||
|
||||
@@ -3590,6 +3689,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))
|
||||
@@ -3602,6 +3709,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 {
|
||||
@@ -3797,6 +3914,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);
|
||||
@@ -3825,28 +3943,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 */
|
||||
@@ -3897,7 +4049,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);
|
||||
@@ -4391,6 +4543,13 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
|
||||
|
||||
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)) {
|
||||
drm_dp_aux_unregister(&intel_dp->aux);
|
||||
if (is_edp(intel_dp)) {
|
||||
@@ -4487,3 +4646,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 {
|
||||
@@ -351,6 +356,9 @@ struct intel_crtc_config {
|
||||
bool ips_enabled;
|
||||
|
||||
bool double_wide;
|
||||
|
||||
bool dp_encoder_is_mst;
|
||||
int pbn;
|
||||
};
|
||||
|
||||
struct intel_pipe_wm {
|
||||
@@ -506,6 +514,7 @@ struct intel_hdmi {
|
||||
struct drm_display_mode *adjusted_mode);
|
||||
};
|
||||
|
||||
struct intel_dp_mst_encoder;
|
||||
#define DP_MAX_DOWNSTREAM_PORTS 0x10
|
||||
|
||||
/**
|
||||
@@ -545,8 +554,16 @@ struct intel_dp {
|
||||
unsigned long last_power_on;
|
||||
unsigned long last_backlight_off;
|
||||
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_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
|
||||
@@ -573,6 +590,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)
|
||||
{
|
||||
@@ -657,6 +681,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;
|
||||
@@ -719,6 +749,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);
|
||||
@@ -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_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 */
|
||||
void intel_dsi_init(struct drm_device *dev);
|
||||
|
||||
|
||||
@@ -375,6 +375,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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user