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:
parent
d05410f9a4
commit
0e32b39cee
|
@ -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,7 +587,7 @@ 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,
|
||||
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;
|
||||
|
@ -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,6 +874,18 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
|
|||
type == INTEL_OUTPUT_EDP) {
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
|
||||
|
||||
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);
|
||||
|
@ -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 (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;
|
||||
|
||||
|
|
|
@ -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,6 +740,7 @@ intel_dp_connector_unregister(struct intel_connector *intel_connector)
|
|||
{
|
||||
struct intel_dp *intel_dp = intel_attached_dp(&intel_connector->base);
|
||||
|
||||
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");
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,534 @@
|
|||
/*
|
||||
* 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 @@
|
|||
#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:
|
||||
|
|
Loading…
Reference in New Issue