diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/g4x_hdmi.c')
| -rw-r--r-- | drivers/gpu/drm/i915/display/g4x_hdmi.c | 403 |
1 files changed, 274 insertions, 129 deletions
diff --git a/drivers/gpu/drm/i915/display/g4x_hdmi.c b/drivers/gpu/drm/i915/display/g4x_hdmi.c index 64c3b3990702..f6e2d1ed5639 100644 --- a/drivers/gpu/drm/i915/display/g4x_hdmi.c +++ b/drivers/gpu/drm/i915/display/g4x_hdmi.c @@ -5,26 +5,30 @@ * HDMI support for G4x,ILK,SNB,IVB,VLV,CHV (HSW+ handled by the DDI code). */ +#include <drm/drm_print.h> + #include "g4x_hdmi.h" #include "i915_reg.h" +#include "intel_atomic.h" #include "intel_audio.h" #include "intel_connector.h" #include "intel_crtc.h" #include "intel_de.h" #include "intel_display_power.h" +#include "intel_display_regs.h" #include "intel_display_types.h" +#include "intel_dp_aux.h" #include "intel_dpio_phy.h" +#include "intel_encoder.h" #include "intel_fifo_underrun.h" #include "intel_hdmi.h" #include "intel_hotplug.h" #include "intel_sdvo.h" -#include "vlv_sideband.h" static void intel_hdmi_prepare(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state) { - struct drm_device *dev = encoder->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_display *display = to_intel_display(encoder); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; @@ -33,7 +37,7 @@ static void intel_hdmi_prepare(struct intel_encoder *encoder, intel_dp_dual_mode_set_tmds_output(intel_hdmi, true); hdmi_val = SDVO_ENCODING_HDMI; - if (!HAS_PCH_SPLIT(dev_priv) && crtc_state->limited_color_range) + if (!HAS_PCH_SPLIT(display) && crtc_state->limited_color_range) hdmi_val |= HDMI_COLOR_RANGE_16_235; if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) hdmi_val |= SDVO_VSYNC_ACTIVE_HIGH; @@ -48,61 +52,112 @@ static void intel_hdmi_prepare(struct intel_encoder *encoder, if (crtc_state->has_hdmi_sink) hdmi_val |= HDMI_MODE_SELECT_HDMI; - if (HAS_PCH_CPT(dev_priv)) + if (HAS_PCH_CPT(display)) hdmi_val |= SDVO_PIPE_SEL_CPT(crtc->pipe); - else if (IS_CHERRYVIEW(dev_priv)) + else if (display->platform.cherryview) hdmi_val |= SDVO_PIPE_SEL_CHV(crtc->pipe); else hdmi_val |= SDVO_PIPE_SEL(crtc->pipe); - intel_de_write(dev_priv, intel_hdmi->hdmi_reg, hdmi_val); - intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); + intel_de_write(display, intel_hdmi->hdmi_reg, hdmi_val); + intel_de_posting_read(display, intel_hdmi->hdmi_reg); } static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder, enum pipe *pipe) { - struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + struct intel_display *display = to_intel_display(encoder); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); intel_wakeref_t wakeref; bool ret; - wakeref = intel_display_power_get_if_enabled(dev_priv, + wakeref = intel_display_power_get_if_enabled(display, encoder->power_domain); if (!wakeref) return false; - ret = intel_sdvo_port_enabled(dev_priv, intel_hdmi->hdmi_reg, pipe); + ret = intel_sdvo_port_enabled(display, intel_hdmi->hdmi_reg, pipe); - intel_display_power_put(dev_priv, encoder->power_domain, wakeref); + intel_display_power_put(display, encoder->power_domain, wakeref); return ret; } +static bool connector_is_hdmi(struct drm_connector *connector) +{ + struct intel_encoder *encoder = + intel_attached_encoder(to_intel_connector(connector)); + + return encoder && encoder->type == INTEL_OUTPUT_HDMI; +} + +static bool g4x_compute_has_hdmi_sink(struct intel_atomic_state *state, + struct intel_crtc *this_crtc) +{ + const struct drm_connector_state *conn_state; + struct drm_connector *connector; + int i; + + /* + * On g4x only one HDMI port can transmit infoframes/audio at + * any given time. Select the first suitable port for this duty. + * + * See also g4x_hdmi_connector_atomic_check(). + */ + for_each_new_connector_in_state(&state->base, connector, conn_state, i) { + struct intel_encoder *encoder = to_intel_encoder(conn_state->best_encoder); + const struct intel_crtc_state *crtc_state; + struct intel_crtc *crtc; + + if (!connector_is_hdmi(connector)) + continue; + + crtc = to_intel_crtc(conn_state->crtc); + if (!crtc) + continue; + + crtc_state = intel_atomic_get_new_crtc_state(state, crtc); + + if (!intel_hdmi_compute_has_hdmi_sink(encoder, crtc_state, conn_state)) + continue; + + return crtc == this_crtc; + } + + return false; +} + static int g4x_hdmi_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { - struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_display *display = to_intel_display(encoder); + struct intel_atomic_state *state = to_intel_atomic_state(crtc_state->uapi.state); + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - if (HAS_PCH_SPLIT(i915)) + if (HAS_PCH_SPLIT(display)) crtc_state->has_pch_encoder = true; + if (display->platform.g4x) + crtc_state->has_hdmi_sink = g4x_compute_has_hdmi_sink(state, crtc); + else + crtc_state->has_hdmi_sink = + intel_hdmi_compute_has_hdmi_sink(encoder, crtc_state, conn_state); + return intel_hdmi_compute_config(encoder, crtc_state, conn_state); } static void intel_hdmi_get_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config) { + struct intel_display *display = to_intel_display(encoder); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); - struct drm_device *dev = encoder->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); u32 tmp, flags = 0; int dotclock; pipe_config->output_types |= BIT(INTEL_OUTPUT_HDMI); - tmp = intel_de_read(dev_priv, intel_hdmi->hdmi_reg); + tmp = intel_de_read(display, intel_hdmi->hdmi_reg); if (tmp & SDVO_HSYNC_ACTIVE_HIGH) flags |= DRM_MODE_FLAG_PHSYNC; @@ -126,7 +181,7 @@ static void intel_hdmi_get_config(struct intel_encoder *encoder, if (tmp & HDMI_AUDIO_ENABLE) pipe_config->has_audio = true; - if (!HAS_PCH_SPLIT(dev_priv) && + if (!HAS_PCH_SPLIT(display) && tmp & HDMI_COLOR_RANGE_16_235) pipe_config->limited_color_range = true; @@ -162,19 +217,50 @@ static void intel_hdmi_get_config(struct intel_encoder *encoder, static void g4x_hdmi_enable_port(struct intel_encoder *encoder, const struct intel_crtc_state *pipe_config) { - struct drm_device *dev = encoder->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_display *display = to_intel_display(encoder); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); u32 temp; - temp = intel_de_read(dev_priv, intel_hdmi->hdmi_reg); + temp = intel_de_read(display, intel_hdmi->hdmi_reg); temp |= SDVO_ENABLE; - if (pipe_config->has_audio) - temp |= HDMI_AUDIO_ENABLE; - intel_de_write(dev_priv, intel_hdmi->hdmi_reg, temp); - intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); + intel_de_write(display, intel_hdmi->hdmi_reg, temp); + intel_de_posting_read(display, intel_hdmi->hdmi_reg); +} + +static void g4x_hdmi_audio_enable(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) +{ + struct intel_display *display = to_intel_display(encoder); + struct intel_hdmi *hdmi = enc_to_intel_hdmi(encoder); + + if (!crtc_state->has_audio) + return; + + drm_WARN_ON(display->drm, !crtc_state->has_hdmi_sink); + + /* Enable audio presence detect */ + intel_de_rmw(display, hdmi->hdmi_reg, 0, HDMI_AUDIO_ENABLE); + + intel_audio_codec_enable(encoder, crtc_state, conn_state); +} + +static void g4x_hdmi_audio_disable(struct intel_encoder *encoder, + const struct intel_crtc_state *old_crtc_state, + const struct drm_connector_state *old_conn_state) +{ + struct intel_display *display = to_intel_display(encoder); + struct intel_hdmi *hdmi = enc_to_intel_hdmi(encoder); + + if (!old_crtc_state->has_audio) + return; + + intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); + + /* Disable audio presence detect */ + intel_de_rmw(display, hdmi->hdmi_reg, HDMI_AUDIO_ENABLE, 0); } static void g4x_enable_hdmi(struct intel_atomic_state *state, @@ -182,13 +268,7 @@ static void g4x_enable_hdmi(struct intel_atomic_state *state, const struct intel_crtc_state *pipe_config, const struct drm_connector_state *conn_state) { - struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); - g4x_hdmi_enable_port(encoder, pipe_config); - - drm_WARN_ON(&dev_priv->drm, pipe_config->has_audio && - !pipe_config->has_hdmi_sink); - intel_audio_codec_enable(encoder, pipe_config, conn_state); } static void ibx_enable_hdmi(struct intel_atomic_state *state, @@ -196,25 +276,22 @@ static void ibx_enable_hdmi(struct intel_atomic_state *state, const struct intel_crtc_state *pipe_config, const struct drm_connector_state *conn_state) { - struct drm_device *dev = encoder->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_display *display = to_intel_display(encoder); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); u32 temp; - temp = intel_de_read(dev_priv, intel_hdmi->hdmi_reg); + temp = intel_de_read(display, intel_hdmi->hdmi_reg); temp |= SDVO_ENABLE; - if (pipe_config->has_audio) - temp |= HDMI_AUDIO_ENABLE; /* * HW workaround, need to write this twice for issue * that may result in first write getting masked. */ - intel_de_write(dev_priv, intel_hdmi->hdmi_reg, temp); - intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); - intel_de_write(dev_priv, intel_hdmi->hdmi_reg, temp); - intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); + intel_de_write(display, intel_hdmi->hdmi_reg, temp); + intel_de_posting_read(display, intel_hdmi->hdmi_reg); + intel_de_write(display, intel_hdmi->hdmi_reg, temp); + intel_de_posting_read(display, intel_hdmi->hdmi_reg); /* * HW workaround, need to toggle enable bit off and on @@ -225,23 +302,19 @@ static void ibx_enable_hdmi(struct intel_atomic_state *state, */ if (pipe_config->pipe_bpp > 24 && pipe_config->pixel_multiplier > 1) { - intel_de_write(dev_priv, intel_hdmi->hdmi_reg, + intel_de_write(display, intel_hdmi->hdmi_reg, temp & ~SDVO_ENABLE); - intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); + intel_de_posting_read(display, intel_hdmi->hdmi_reg); /* * HW workaround, need to write this twice for issue * that may result in first write getting masked. */ - intel_de_write(dev_priv, intel_hdmi->hdmi_reg, temp); - intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); - intel_de_write(dev_priv, intel_hdmi->hdmi_reg, temp); - intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); + intel_de_write(display, intel_hdmi->hdmi_reg, temp); + intel_de_posting_read(display, intel_hdmi->hdmi_reg); + intel_de_write(display, intel_hdmi->hdmi_reg, temp); + intel_de_posting_read(display, intel_hdmi->hdmi_reg); } - - drm_WARN_ON(&dev_priv->drm, pipe_config->has_audio && - !pipe_config->has_hdmi_sink); - intel_audio_codec_enable(encoder, pipe_config, conn_state); } static void cpt_enable_hdmi(struct intel_atomic_state *state, @@ -249,18 +322,15 @@ static void cpt_enable_hdmi(struct intel_atomic_state *state, const struct intel_crtc_state *pipe_config, const struct drm_connector_state *conn_state) { - struct drm_device *dev = encoder->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_display *display = to_intel_display(encoder); struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); enum pipe pipe = crtc->pipe; u32 temp; - temp = intel_de_read(dev_priv, intel_hdmi->hdmi_reg); + temp = intel_de_read(display, intel_hdmi->hdmi_reg); temp |= SDVO_ENABLE; - if (pipe_config->has_audio) - temp |= HDMI_AUDIO_ENABLE; /* * WaEnableHDMI8bpcBefore12bpc:snb,ivb @@ -273,30 +343,26 @@ static void cpt_enable_hdmi(struct intel_atomic_state *state, */ if (pipe_config->pipe_bpp > 24) { - intel_de_write(dev_priv, TRANS_CHICKEN1(pipe), - intel_de_read(dev_priv, TRANS_CHICKEN1(pipe)) | TRANS_CHICKEN1_HDMIUNIT_GC_DISABLE); + intel_de_rmw(display, TRANS_CHICKEN1(pipe), + 0, TRANS_CHICKEN1_HDMIUNIT_GC_DISABLE); temp &= ~SDVO_COLOR_FORMAT_MASK; temp |= SDVO_COLOR_FORMAT_8bpc; } - intel_de_write(dev_priv, intel_hdmi->hdmi_reg, temp); - intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); + intel_de_write(display, intel_hdmi->hdmi_reg, temp); + intel_de_posting_read(display, intel_hdmi->hdmi_reg); if (pipe_config->pipe_bpp > 24) { temp &= ~SDVO_COLOR_FORMAT_MASK; temp |= HDMI_COLOR_FORMAT_12bpc; - intel_de_write(dev_priv, intel_hdmi->hdmi_reg, temp); - intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); + intel_de_write(display, intel_hdmi->hdmi_reg, temp); + intel_de_posting_read(display, intel_hdmi->hdmi_reg); - intel_de_write(dev_priv, TRANS_CHICKEN1(pipe), - intel_de_read(dev_priv, TRANS_CHICKEN1(pipe)) & ~TRANS_CHICKEN1_HDMIUNIT_GC_DISABLE); + intel_de_rmw(display, TRANS_CHICKEN1(pipe), + TRANS_CHICKEN1_HDMIUNIT_GC_DISABLE, 0); } - - drm_WARN_ON(&dev_priv->drm, pipe_config->has_audio && - !pipe_config->has_hdmi_sink); - intel_audio_codec_enable(encoder, pipe_config, conn_state); } static void vlv_enable_hdmi(struct intel_atomic_state *state, @@ -304,11 +370,6 @@ static void vlv_enable_hdmi(struct intel_atomic_state *state, const struct intel_crtc_state *pipe_config, const struct drm_connector_state *conn_state) { - struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); - - drm_WARN_ON(&dev_priv->drm, pipe_config->has_audio && - !pipe_config->has_hdmi_sink); - intel_audio_codec_enable(encoder, pipe_config, conn_state); } static void intel_disable_hdmi(struct intel_atomic_state *state, @@ -316,32 +377,31 @@ static void intel_disable_hdmi(struct intel_atomic_state *state, const struct intel_crtc_state *old_crtc_state, const struct drm_connector_state *old_conn_state) { - struct drm_device *dev = encoder->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_display *display = to_intel_display(encoder); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); struct intel_digital_port *dig_port = hdmi_to_dig_port(intel_hdmi); struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->uapi.crtc); u32 temp; - temp = intel_de_read(dev_priv, intel_hdmi->hdmi_reg); + temp = intel_de_read(display, intel_hdmi->hdmi_reg); - temp &= ~(SDVO_ENABLE | HDMI_AUDIO_ENABLE); - intel_de_write(dev_priv, intel_hdmi->hdmi_reg, temp); - intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); + temp &= ~SDVO_ENABLE; + intel_de_write(display, intel_hdmi->hdmi_reg, temp); + intel_de_posting_read(display, intel_hdmi->hdmi_reg); /* * HW workaround for IBX, we need to move the port * to transcoder A after disabling it to allow the * matching DP port to be enabled on transcoder A. */ - if (HAS_PCH_IBX(dev_priv) && crtc->pipe == PIPE_B) { + if (HAS_PCH_IBX(display) && crtc->pipe == PIPE_B) { /* * We get CPU/PCH FIFO underruns on the other pipe when * doing the workaround. Sweep them under the rug. */ - intel_set_cpu_fifo_underrun_reporting(dev_priv, PIPE_A, false); - intel_set_pch_fifo_underrun_reporting(dev_priv, PIPE_A, false); + intel_set_cpu_fifo_underrun_reporting(display, PIPE_A, false); + intel_set_pch_fifo_underrun_reporting(display, PIPE_A, false); temp &= ~SDVO_PIPE_SEL_MASK; temp |= SDVO_ENABLE | SDVO_PIPE_SEL(PIPE_A); @@ -349,18 +409,18 @@ static void intel_disable_hdmi(struct intel_atomic_state *state, * HW workaround, need to write this twice for issue * that may result in first write getting masked. */ - intel_de_write(dev_priv, intel_hdmi->hdmi_reg, temp); - intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); - intel_de_write(dev_priv, intel_hdmi->hdmi_reg, temp); - intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); + intel_de_write(display, intel_hdmi->hdmi_reg, temp); + intel_de_posting_read(display, intel_hdmi->hdmi_reg); + intel_de_write(display, intel_hdmi->hdmi_reg, temp); + intel_de_posting_read(display, intel_hdmi->hdmi_reg); temp &= ~SDVO_ENABLE; - intel_de_write(dev_priv, intel_hdmi->hdmi_reg, temp); - intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); + intel_de_write(display, intel_hdmi->hdmi_reg, temp); + intel_de_posting_read(display, intel_hdmi->hdmi_reg); - intel_wait_for_vblank_if_active(dev_priv, PIPE_A); - intel_set_cpu_fifo_underrun_reporting(dev_priv, PIPE_A, true); - intel_set_pch_fifo_underrun_reporting(dev_priv, PIPE_A, true); + intel_wait_for_vblank_if_active(display, PIPE_A); + intel_set_cpu_fifo_underrun_reporting(display, PIPE_A, true); + intel_set_pch_fifo_underrun_reporting(display, PIPE_A, true); } dig_port->set_infoframes(encoder, @@ -375,8 +435,6 @@ static void g4x_disable_hdmi(struct intel_atomic_state *state, const struct intel_crtc_state *old_crtc_state, const struct drm_connector_state *old_conn_state) { - intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); - intel_disable_hdmi(state, encoder, old_crtc_state, old_conn_state); } @@ -385,7 +443,6 @@ static void pch_disable_hdmi(struct intel_atomic_state *state, const struct intel_crtc_state *old_crtc_state, const struct drm_connector_state *old_conn_state) { - intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); } static void pch_post_disable_hdmi(struct intel_atomic_state *state, @@ -417,7 +474,6 @@ static void vlv_hdmi_pre_enable(struct intel_atomic_state *state, const struct drm_connector_state *conn_state) { struct intel_digital_port *dig_port = enc_to_dig_port(encoder); - struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); vlv_phy_pre_encoder_enable(encoder, pipe_config); @@ -432,7 +488,7 @@ static void vlv_hdmi_pre_enable(struct intel_atomic_state *state, g4x_hdmi_enable_port(encoder, pipe_config); - vlv_wait_port_ready(dev_priv, dig_port, 0x0); + vlv_wait_port_ready(encoder, 0x0); } static void vlv_hdmi_pre_pll_enable(struct intel_atomic_state *state, @@ -477,15 +533,8 @@ static void chv_hdmi_post_disable(struct intel_atomic_state *state, const struct intel_crtc_state *old_crtc_state, const struct drm_connector_state *old_conn_state) { - struct drm_device *dev = encoder->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - - vlv_dpio_get(dev_priv); - /* Assert data lane reset */ chv_data_lane_soft_reset(encoder, old_crtc_state, true); - - vlv_dpio_put(dev_priv); } static void chv_hdmi_pre_enable(struct intel_atomic_state *state, @@ -494,8 +543,6 @@ static void chv_hdmi_pre_enable(struct intel_atomic_state *state, const struct drm_connector_state *conn_state) { struct intel_digital_port *dig_port = enc_to_dig_port(encoder); - struct drm_device *dev = encoder->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); chv_phy_pre_encoder_enable(encoder, pipe_config); @@ -509,7 +556,7 @@ static void chv_hdmi_pre_enable(struct intel_atomic_state *state, g4x_hdmi_enable_port(encoder, pipe_config); - vlv_wait_port_ready(dev_priv, dig_port, 0x0); + vlv_wait_port_ready(encoder, 0x0); /* Second common lane will stay alive on its own now */ chv_phy_release_cl2_override(encoder); @@ -545,34 +592,121 @@ intel_hdmi_hotplug(struct intel_encoder *encoder, return state; } -void g4x_hdmi_init(struct drm_i915_private *dev_priv, +int g4x_hdmi_connector_atomic_check(struct drm_connector *connector, + struct drm_atomic_state *state) +{ + struct intel_display *display = to_intel_display(connector->dev); + struct drm_connector_list_iter conn_iter; + struct drm_connector *conn; + int ret; + + ret = intel_digital_connector_atomic_check(connector, state); + if (ret) + return ret; + + if (!display->platform.g4x) + return 0; + + if (!intel_connector_needs_modeset(to_intel_atomic_state(state), connector)) + return 0; + + /* + * On g4x only one HDMI port can transmit infoframes/audio + * at any given time. Make sure all enabled HDMI ports are + * included in the state so that it's possible to select + * one of them for this duty. + * + * See also g4x_compute_has_hdmi_sink(). + */ + drm_connector_list_iter_begin(display->drm, &conn_iter); + drm_for_each_connector_iter(conn, &conn_iter) { + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + + if (!connector_is_hdmi(conn)) + continue; + + drm_dbg_kms(display->drm, "Adding [CONNECTOR:%d:%s]\n", + conn->base.id, conn->name); + + conn_state = drm_atomic_get_connector_state(state, conn); + if (IS_ERR(conn_state)) { + ret = PTR_ERR(conn_state); + break; + } + + crtc = conn_state->crtc; + if (!crtc) + continue; + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + crtc_state->mode_changed = true; + + ret = drm_atomic_add_affected_planes(state, crtc); + if (ret) + break; + } + drm_connector_list_iter_end(&conn_iter); + + return ret; +} + +static bool is_hdmi_port_valid(struct intel_display *display, enum port port) +{ + if (display->platform.g4x || display->platform.valleyview) + return port == PORT_B || port == PORT_C; + else + return port == PORT_B || port == PORT_C || port == PORT_D; +} + +static bool assert_hdmi_port_valid(struct intel_display *display, enum port port) +{ + return !drm_WARN(display->drm, !is_hdmi_port_valid(display, port), + "Platform does not support HDMI %c\n", port_name(port)); +} + +bool g4x_hdmi_init(struct intel_display *display, i915_reg_t hdmi_reg, enum port port) { + const struct intel_bios_encoder_data *devdata; struct intel_digital_port *dig_port; struct intel_encoder *intel_encoder; struct intel_connector *intel_connector; - dig_port = kzalloc(sizeof(*dig_port), GFP_KERNEL); + if (!assert_port_valid(display, port)) + return false; + + if (!assert_hdmi_port_valid(display, port)) + return false; + + devdata = intel_bios_encoder_data_lookup(display, port); + + /* FIXME bail? */ + if (!devdata) + drm_dbg_kms(display->drm, "No VBT child device for HDMI-%c\n", + port_name(port)); + + dig_port = intel_dig_port_alloc(); if (!dig_port) - return; + return false; intel_connector = intel_connector_alloc(); - if (!intel_connector) { - kfree(dig_port); - return; - } + if (!intel_connector) + goto err_connector_alloc; intel_encoder = &dig_port->base; - mutex_init(&dig_port->hdcp_mutex); + intel_encoder->devdata = devdata; - drm_encoder_init(&dev_priv->drm, &intel_encoder->base, - &intel_hdmi_enc_funcs, DRM_MODE_ENCODER_TMDS, - "HDMI %c", port_name(port)); + if (drm_encoder_init(display->drm, &intel_encoder->base, + &intel_hdmi_enc_funcs, DRM_MODE_ENCODER_TMDS, + "HDMI %c", port_name(port))) + goto err_encoder_init; intel_encoder->hotplug = intel_hdmi_hotplug; intel_encoder->compute_config = g4x_hdmi_compute_config; - if (HAS_PCH_SPLIT(dev_priv)) { + if (HAS_PCH_SPLIT(display)) { intel_encoder->disable = pch_disable_hdmi; intel_encoder->post_disable = pch_post_disable_hdmi; } else { @@ -580,32 +714,34 @@ void g4x_hdmi_init(struct drm_i915_private *dev_priv, } intel_encoder->get_hw_state = intel_hdmi_get_hw_state; intel_encoder->get_config = intel_hdmi_get_config; - if (IS_CHERRYVIEW(dev_priv)) { + if (display->platform.cherryview) { intel_encoder->pre_pll_enable = chv_hdmi_pre_pll_enable; intel_encoder->pre_enable = chv_hdmi_pre_enable; intel_encoder->enable = vlv_enable_hdmi; intel_encoder->post_disable = chv_hdmi_post_disable; intel_encoder->post_pll_disable = chv_hdmi_post_pll_disable; - } else if (IS_VALLEYVIEW(dev_priv)) { + } else if (display->platform.valleyview) { intel_encoder->pre_pll_enable = vlv_hdmi_pre_pll_enable; intel_encoder->pre_enable = vlv_hdmi_pre_enable; intel_encoder->enable = vlv_enable_hdmi; intel_encoder->post_disable = vlv_hdmi_post_disable; } else { intel_encoder->pre_enable = intel_hdmi_pre_enable; - if (HAS_PCH_CPT(dev_priv)) + if (HAS_PCH_CPT(display)) intel_encoder->enable = cpt_enable_hdmi; - else if (HAS_PCH_IBX(dev_priv)) + else if (HAS_PCH_IBX(display)) intel_encoder->enable = ibx_enable_hdmi; else intel_encoder->enable = g4x_enable_hdmi; } + intel_encoder->audio_enable = g4x_hdmi_audio_enable; + intel_encoder->audio_disable = g4x_hdmi_audio_disable; intel_encoder->shutdown = intel_hdmi_encoder_shutdown; intel_encoder->type = INTEL_OUTPUT_HDMI; - intel_encoder->power_domain = intel_display_power_ddi_lanes_domain(dev_priv, port); + intel_encoder->power_domain = intel_display_power_ddi_lanes_domain(display, port); intel_encoder->port = port; - if (IS_CHERRYVIEW(dev_priv)) { + if (display->platform.cherryview) { if (port == PORT_D) intel_encoder->pipe_mask = BIT(PIPE_C); else @@ -614,21 +750,30 @@ void g4x_hdmi_init(struct drm_i915_private *dev_priv, intel_encoder->pipe_mask = ~0; } intel_encoder->cloneable = BIT(INTEL_OUTPUT_ANALOG); - intel_encoder->hpd_pin = intel_hpd_pin_default(dev_priv, port); + intel_encoder->hpd_pin = intel_hpd_pin_default(port); /* * BSpec is unclear about HDMI+HDMI cloning on g4x, but it seems * to work on real hardware. And since g4x can send infoframes to * only one port anyway, nothing is lost by allowing it. */ - if (IS_G4X(dev_priv)) + if (display->platform.g4x) intel_encoder->cloneable |= BIT(INTEL_OUTPUT_HDMI); dig_port->hdmi.hdmi_reg = hdmi_reg; - dig_port->dp.output_reg = INVALID_MMIO_REG; - dig_port->max_lanes = 4; intel_infoframe_init(dig_port); - dig_port->aux_ch = intel_bios_port_aux_ch(dev_priv, port); - intel_hdmi_init_connector(dig_port, intel_connector); + if (!intel_hdmi_init_connector(dig_port, intel_connector)) + goto err_init_connector; + + return true; + +err_init_connector: + drm_encoder_cleanup(&intel_encoder->base); +err_encoder_init: + kfree(intel_connector); +err_connector_alloc: + kfree(dig_port); + + return false; } |
