diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_hdmi.c')
-rw-r--r-- | drivers/gpu/drm/i915/display/intel_hdmi.c | 202 |
1 files changed, 142 insertions, 60 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index a690a5616506..94a7e1537f42 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -40,12 +40,13 @@ #include <drm/drm_edid.h> #include <drm/intel_lpe_audio.h> -#include "i915_debugfs.h" +#include "g4x_hdmi.h" #include "i915_drv.h" #include "i915_reg.h" #include "intel_atomic.h" #include "intel_audio.h" #include "intel_connector.h" +#include "intel_cx0_phy.h" #include "intel_ddi.h" #include "intel_de.h" #include "intel_display_types.h" @@ -58,7 +59,7 @@ #include "intel_panel.h" #include "intel_snps_phy.h" -static struct drm_i915_private *intel_hdmi_to_i915(struct intel_hdmi *intel_hdmi) +inline struct drm_i915_private *intel_hdmi_to_i915(struct intel_hdmi *intel_hdmi) { return to_i915(hdmi_to_dig_port(intel_hdmi)->base.base.dev); } @@ -1789,7 +1790,9 @@ static int intel_hdmi_source_max_tmds_clock(struct intel_encoder *encoder) static bool intel_has_hdmi_sink(struct intel_hdmi *hdmi, const struct drm_connector_state *conn_state) { - return hdmi->has_hdmi_sink && + struct intel_connector *connector = hdmi->attached_connector; + + return connector->base.display_info.is_hdmi && READ_ONCE(to_intel_digital_connector_state(conn_state)->force_audio) != HDMI_AUDIO_OFF_DVI; } @@ -1865,16 +1868,19 @@ hdmi_port_clock_valid(struct intel_hdmi *hdmi, * FIXME: We will hopefully get an algorithmic way of programming * the MPLLB for HDMI in the future. */ - if (IS_DG2(dev_priv)) + if (DISPLAY_VER(dev_priv) >= 14) + return intel_cx0_phy_check_hdmi_link_rate(hdmi, clock); + else if (IS_DG2(dev_priv)) return intel_snps_phy_check_hdmi_link_rate(clock); return MODE_OK; } -int intel_hdmi_tmds_clock(int clock, int bpc, bool ycbcr420_output) +int intel_hdmi_tmds_clock(int clock, int bpc, + enum intel_output_format sink_format) { /* YCBCR420 TMDS rate requirement is half the pixel clock */ - if (ycbcr420_output) + if (sink_format == INTEL_OUTPUT_FORMAT_YCBCR420) clock /= 2; /* @@ -1901,7 +1907,8 @@ static bool intel_hdmi_source_bpc_possible(struct drm_i915_private *i915, int bp } static bool intel_hdmi_sink_bpc_possible(struct drm_connector *connector, - int bpc, bool has_hdmi_sink, bool ycbcr420_output) + int bpc, bool has_hdmi_sink, + enum intel_output_format sink_format) { const struct drm_display_info *info = &connector->display_info; const struct drm_hdmi_info *hdmi = &info->hdmi; @@ -1911,7 +1918,7 @@ static bool intel_hdmi_sink_bpc_possible(struct drm_connector *connector, if (!has_hdmi_sink) return false; - if (ycbcr420_output) + if (sink_format == INTEL_OUTPUT_FORMAT_YCBCR420) return hdmi->y420_dc_modes & DRM_EDID_YCBCR420_DC_36; else return info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36; @@ -1919,7 +1926,7 @@ static bool intel_hdmi_sink_bpc_possible(struct drm_connector *connector, if (!has_hdmi_sink) return false; - if (ycbcr420_output) + if (sink_format == INTEL_OUTPUT_FORMAT_YCBCR420) return hdmi->y420_dc_modes & DRM_EDID_YCBCR420_DC_30; else return info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30; @@ -1933,7 +1940,8 @@ static bool intel_hdmi_sink_bpc_possible(struct drm_connector *connector, static enum drm_mode_status intel_hdmi_mode_clock_valid(struct drm_connector *connector, int clock, - bool has_hdmi_sink, bool ycbcr420_output) + bool has_hdmi_sink, + enum intel_output_format sink_format) { struct drm_i915_private *i915 = to_i915(connector->dev); struct intel_hdmi *hdmi = intel_attached_hdmi(to_intel_connector(connector)); @@ -1946,12 +1954,12 @@ intel_hdmi_mode_clock_valid(struct drm_connector *connector, int clock, * least one color depth is accepted. */ for (bpc = 12; bpc >= 8; bpc -= 2) { - int tmds_clock = intel_hdmi_tmds_clock(clock, bpc, ycbcr420_output); + int tmds_clock = intel_hdmi_tmds_clock(clock, bpc, sink_format); if (!intel_hdmi_source_bpc_possible(i915, bpc)) continue; - if (!intel_hdmi_sink_bpc_possible(connector, bpc, has_hdmi_sink, ycbcr420_output)) + if (!intel_hdmi_sink_bpc_possible(connector, bpc, has_hdmi_sink, sink_format)) continue; status = hdmi_port_clock_valid(hdmi, tmds_clock, true, has_hdmi_sink); @@ -1976,6 +1984,7 @@ intel_hdmi_mode_valid(struct drm_connector *connector, int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; bool has_hdmi_sink = intel_has_hdmi_sink(hdmi, connector->state); bool ycbcr_420_only; + enum intel_output_format sink_format; if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) clock *= 2; @@ -2000,14 +2009,20 @@ intel_hdmi_mode_valid(struct drm_connector *connector, ycbcr_420_only = drm_mode_is_420_only(&connector->display_info, mode); - status = intel_hdmi_mode_clock_valid(connector, clock, has_hdmi_sink, ycbcr_420_only); + if (ycbcr_420_only) + sink_format = INTEL_OUTPUT_FORMAT_YCBCR420; + else + sink_format = INTEL_OUTPUT_FORMAT_RGB; + + status = intel_hdmi_mode_clock_valid(connector, clock, has_hdmi_sink, sink_format); if (status != MODE_OK) { if (ycbcr_420_only || !connector->ycbcr_420_allowed || !drm_mode_is_420_also(&connector->display_info, mode)) return status; - status = intel_hdmi_mode_clock_valid(connector, clock, has_hdmi_sink, true); + sink_format = INTEL_OUTPUT_FORMAT_YCBCR420; + status = intel_hdmi_mode_clock_valid(connector, clock, has_hdmi_sink, sink_format); if (status != MODE_OK) return status; } @@ -2016,7 +2031,7 @@ intel_hdmi_mode_valid(struct drm_connector *connector, } bool intel_hdmi_bpc_possible(const struct intel_crtc_state *crtc_state, - int bpc, bool has_hdmi_sink, bool ycbcr420_output) + int bpc, bool has_hdmi_sink) { struct drm_atomic_state *state = crtc_state->uapi.state; struct drm_connector_state *connector_state; @@ -2027,7 +2042,8 @@ bool intel_hdmi_bpc_possible(const struct intel_crtc_state *crtc_state, if (connector_state->crtc != crtc_state->uapi.crtc) continue; - if (!intel_hdmi_sink_bpc_possible(connector, bpc, has_hdmi_sink, ycbcr420_output)) + if (!intel_hdmi_sink_bpc_possible(connector, bpc, has_hdmi_sink, + crtc_state->sink_format)) return false; } @@ -2051,8 +2067,7 @@ static bool hdmi_bpc_possible(const struct intel_crtc_state *crtc_state, int bpc adjusted_mode->crtc_hblank_start) % 8 == 2) return false; - return intel_hdmi_bpc_possible(crtc_state, bpc, crtc_state->has_hdmi_sink, - intel_hdmi_is_ycbcr420(crtc_state)); + return intel_hdmi_bpc_possible(crtc_state, bpc, crtc_state->has_hdmi_sink); } static int intel_hdmi_compute_bpc(struct intel_encoder *encoder, @@ -2060,7 +2075,6 @@ static int intel_hdmi_compute_bpc(struct intel_encoder *encoder, int clock, bool respect_downstream_limits) { struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); - bool ycbcr420_output = intel_hdmi_is_ycbcr420(crtc_state); int bpc; /* @@ -2078,7 +2092,8 @@ static int intel_hdmi_compute_bpc(struct intel_encoder *encoder, bpc = 8; for (; bpc >= 8; bpc -= 2) { - int tmds_clock = intel_hdmi_tmds_clock(clock, bpc, ycbcr420_output); + int tmds_clock = intel_hdmi_tmds_clock(clock, bpc, + crtc_state->sink_format); if (hdmi_bpc_possible(crtc_state, bpc) && hdmi_port_clock_valid(intel_hdmi, tmds_clock, @@ -2108,7 +2123,7 @@ static int intel_hdmi_compute_clock(struct intel_encoder *encoder, return bpc; crtc_state->port_clock = - intel_hdmi_tmds_clock(clock, bpc, intel_hdmi_is_ycbcr420(crtc_state)); + intel_hdmi_tmds_clock(clock, bpc, crtc_state->sink_format); /* * pipe_bpp could already be below 8bpc due to @@ -2156,7 +2171,7 @@ static bool intel_hdmi_has_audio(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state) { - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + struct drm_connector *connector = conn_state->connector; const struct intel_digital_connector_state *intel_conn_state = to_intel_digital_connector_state(conn_state); @@ -2164,15 +2179,15 @@ static bool intel_hdmi_has_audio(struct intel_encoder *encoder, return false; if (intel_conn_state->force_audio == HDMI_AUDIO_AUTO) - return intel_hdmi->has_audio; + return connector->display_info.has_audio; else return intel_conn_state->force_audio == HDMI_AUDIO_ON; } static enum intel_output_format -intel_hdmi_output_format(const struct intel_crtc_state *crtc_state, - struct intel_connector *connector, - bool ycbcr_420_output) +intel_hdmi_sink_format(const struct intel_crtc_state *crtc_state, + struct intel_connector *connector, + bool ycbcr_420_output) { if (!crtc_state->has_hdmi_sink) return INTEL_OUTPUT_FORMAT_RGB; @@ -2183,6 +2198,12 @@ intel_hdmi_output_format(const struct intel_crtc_state *crtc_state, return INTEL_OUTPUT_FORMAT_RGB; } +static enum intel_output_format +intel_hdmi_output_format(const struct intel_crtc_state *crtc_state) +{ + return crtc_state->sink_format; +} + static int intel_hdmi_compute_output_format(struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state, @@ -2195,23 +2216,26 @@ static int intel_hdmi_compute_output_format(struct intel_encoder *encoder, bool ycbcr_420_only = drm_mode_is_420_only(info, adjusted_mode); int ret; - crtc_state->output_format = - intel_hdmi_output_format(crtc_state, connector, ycbcr_420_only); + crtc_state->sink_format = + intel_hdmi_sink_format(crtc_state, connector, ycbcr_420_only); - if (ycbcr_420_only && !intel_hdmi_is_ycbcr420(crtc_state)) { + if (ycbcr_420_only && crtc_state->sink_format != INTEL_OUTPUT_FORMAT_YCBCR420) { drm_dbg_kms(&i915->drm, "YCbCr 4:2:0 mode but YCbCr 4:2:0 output not possible. Falling back to RGB.\n"); - crtc_state->output_format = INTEL_OUTPUT_FORMAT_RGB; + crtc_state->sink_format = INTEL_OUTPUT_FORMAT_RGB; } + crtc_state->output_format = intel_hdmi_output_format(crtc_state); ret = intel_hdmi_compute_clock(encoder, crtc_state, respect_downstream_limits); if (ret) { - if (intel_hdmi_is_ycbcr420(crtc_state) || + if (crtc_state->sink_format == INTEL_OUTPUT_FORMAT_YCBCR420 || + !crtc_state->has_hdmi_sink || !connector->base.ycbcr_420_allowed || !drm_mode_is_420_also(info, adjusted_mode)) return ret; - crtc_state->output_format = intel_hdmi_output_format(crtc_state, connector, true); + crtc_state->sink_format = INTEL_OUTPUT_FORMAT_YCBCR420; + crtc_state->output_format = intel_hdmi_output_format(crtc_state); ret = intel_hdmi_compute_clock(encoder, crtc_state, respect_downstream_limits); } @@ -2243,11 +2267,20 @@ static bool source_supports_scrambling(struct intel_encoder *encoder) return intel_hdmi_source_max_tmds_clock(encoder) > 340000; } +bool intel_hdmi_compute_has_hdmi_sink(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) +{ + struct intel_hdmi *hdmi = enc_to_intel_hdmi(encoder); + + return intel_has_hdmi_sink(hdmi, conn_state) && + !intel_hdmi_is_cloned(crtc_state); +} + int intel_hdmi_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; struct drm_connector *connector = conn_state->connector; @@ -2262,9 +2295,6 @@ int intel_hdmi_compute_config(struct intel_encoder *encoder, return -EINVAL; pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB; - pipe_config->has_hdmi_sink = - intel_has_hdmi_sink(intel_hdmi, conn_state) && - !intel_hdmi_is_cloned(pipe_config); if (pipe_config->has_hdmi_sink) pipe_config->has_infoframe = true; @@ -2357,9 +2387,6 @@ intel_hdmi_unset_edid(struct drm_connector *connector) { struct intel_hdmi *intel_hdmi = intel_attached_hdmi(to_intel_connector(connector)); - intel_hdmi->has_hdmi_sink = false; - intel_hdmi->has_audio = false; - intel_hdmi->dp_dual_mode.type = DRM_DP_DUAL_MODE_NONE; intel_hdmi->dp_dual_mode.max_tmds_clock = 0; @@ -2372,7 +2399,7 @@ intel_hdmi_dp_dual_mode_detect(struct drm_connector *connector) { struct drm_i915_private *dev_priv = to_i915(connector->dev); struct intel_hdmi *hdmi = intel_attached_hdmi(to_intel_connector(connector)); - enum port port = hdmi_to_dig_port(hdmi)->base.port; + struct intel_encoder *encoder = &hdmi_to_dig_port(hdmi)->base; struct i2c_adapter *adapter = intel_gmbus_get_adapter(dev_priv, hdmi->ddc_bus); enum drm_dp_dual_mode_type type = drm_dp_dual_mode_detect(&dev_priv->drm, adapter); @@ -2388,7 +2415,7 @@ intel_hdmi_dp_dual_mode_detect(struct drm_connector *connector) */ if (type == DRM_DP_DUAL_MODE_UNKNOWN) { if (!connector->force && - intel_bios_is_port_dp_dual_mode(dev_priv, port)) { + intel_bios_encoder_supports_dp_dual_mode(encoder->devdata)) { drm_dbg_kms(&dev_priv->drm, "Assuming DP dual mode adaptor presence based on VBT\n"); type = DRM_DP_DUAL_MODE_TYPE1_DVI; @@ -2411,7 +2438,7 @@ intel_hdmi_dp_dual_mode_detect(struct drm_connector *connector) /* Older VBTs are often buggy and can't be trusted :( Play it safe. */ if ((DISPLAY_VER(dev_priv) >= 8 || IS_HASWELL(dev_priv)) && - !intel_bios_is_port_dp_dual_mode(dev_priv, port)) { + !intel_bios_encoder_supports_dp_dual_mode(encoder->devdata)) { drm_dbg_kms(&dev_priv->drm, "Ignoring DP dual mode adaptor max TMDS clock for native HDMI port\n"); hdmi->dp_dual_mode.max_tmds_clock = 0; @@ -2451,9 +2478,6 @@ intel_hdmi_set_edid(struct drm_connector *connector) /* FIXME: Get rid of drm_edid_raw() */ edid = drm_edid_raw(drm_edid); if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) { - intel_hdmi->has_audio = drm_detect_monitor_audio(edid); - intel_hdmi->has_hdmi_sink = drm_detect_hdmi_monitor(edid); - intel_hdmi_dp_dual_mode_detect(connector); connected = true; @@ -2597,10 +2621,21 @@ static const struct drm_connector_funcs intel_hdmi_connector_funcs = { .atomic_duplicate_state = intel_digital_connector_duplicate_state, }; +static int intel_hdmi_connector_atomic_check(struct drm_connector *connector, + struct drm_atomic_state *state) +{ + struct drm_i915_private *i915 = to_i915(state->dev); + + if (HAS_DDI(i915)) + return intel_digital_connector_atomic_check(connector, state); + else + return g4x_hdmi_connector_atomic_check(connector, state); +} + static const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = { .get_modes = intel_hdmi_get_modes, .mode_valid = intel_hdmi_mode_valid, - .atomic_check = intel_digital_connector_atomic_check, + .atomic_check = intel_hdmi_connector_atomic_check, }; static void @@ -2845,21 +2880,12 @@ static u8 g4x_port_to_ddc_pin(struct drm_i915_private *dev_priv, return ddc_pin; } -static u8 intel_hdmi_ddc_pin(struct intel_encoder *encoder) +static u8 intel_hdmi_default_ddc_pin(struct intel_encoder *encoder) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); enum port port = encoder->port; u8 ddc_pin; - ddc_pin = intel_bios_hdmi_ddc_pin(encoder->devdata); - if (ddc_pin) { - drm_dbg_kms(&dev_priv->drm, - "[ENCODER:%d:%s] Using DDC pin 0x%x (VBT)\n", - encoder->base.base.id, encoder->base.name, - ddc_pin); - return ddc_pin; - } - if (IS_ALDERLAKE_S(dev_priv)) ddc_pin = adls_port_to_ddc_pin(dev_priv, port); else if (INTEL_PCH_TYPE(dev_priv) >= PCH_DG1) @@ -2868,7 +2894,8 @@ static u8 intel_hdmi_ddc_pin(struct intel_encoder *encoder) ddc_pin = rkl_port_to_ddc_pin(dev_priv, port); else if (DISPLAY_VER(dev_priv) == 9 && HAS_PCH_TGP(dev_priv)) ddc_pin = gen9bc_tgp_port_to_ddc_pin(dev_priv, port); - else if (IS_JSL_EHL(dev_priv) && HAS_PCH_TGP(dev_priv)) + else if ((IS_JASPERLAKE(dev_priv) || IS_ELKHARTLAKE(dev_priv)) && + HAS_PCH_TGP(dev_priv)) ddc_pin = mcc_port_to_ddc_pin(dev_priv, port); else if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP) ddc_pin = icl_port_to_ddc_pin(dev_priv, port); @@ -2881,10 +2908,62 @@ static u8 intel_hdmi_ddc_pin(struct intel_encoder *encoder) else ddc_pin = g4x_port_to_ddc_pin(dev_priv, port); - drm_dbg_kms(&dev_priv->drm, - "[ENCODER:%d:%s] Using DDC pin 0x%x (platform default)\n", + return ddc_pin; +} + +static struct intel_encoder * +get_encoder_by_ddc_pin(struct intel_encoder *encoder, u8 ddc_pin) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_encoder *other; + + for_each_intel_encoder(&i915->drm, other) { + if (other == encoder) + continue; + + if (!intel_encoder_is_dig_port(other)) + continue; + + if (enc_to_dig_port(other)->hdmi.ddc_bus == ddc_pin) + return other; + } + + return NULL; +} + +static u8 intel_hdmi_ddc_pin(struct intel_encoder *encoder) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_encoder *other; + const char *source; + u8 ddc_pin; + + ddc_pin = intel_bios_hdmi_ddc_pin(encoder->devdata); + source = "VBT"; + + if (!ddc_pin) { + ddc_pin = intel_hdmi_default_ddc_pin(encoder); + source = "platform default"; + } + + if (!intel_gmbus_is_valid_pin(i915, ddc_pin)) { + drm_dbg_kms(&i915->drm, "[ENCODER:%d:%s] Invalid DDC pin %d\n", + encoder->base.base.id, encoder->base.name, ddc_pin); + return 0; + } + + other = get_encoder_by_ddc_pin(encoder, ddc_pin); + if (other) { + drm_dbg_kms(&i915->drm, "[ENCODER:%d:%s] DDC pin %d already claimed by [ENCODER:%d:%s]\n", + encoder->base.base.id, encoder->base.name, ddc_pin, + other->base.base.id, other->base.name); + return 0; + } + + drm_dbg_kms(&i915->drm, + "[ENCODER:%d:%s] Using DDC pin 0x%x (%s)\n", encoder->base.base.id, encoder->base.name, - ddc_pin); + ddc_pin, source); return ddc_pin; } @@ -2955,6 +3034,9 @@ void intel_hdmi_init_connector(struct intel_digital_port *dig_port, return; intel_hdmi->ddc_bus = intel_hdmi_ddc_pin(intel_encoder); + if (!intel_hdmi->ddc_bus) + return; + ddc = intel_gmbus_get_adapter(dev_priv, intel_hdmi->ddc_bus); drm_connector_init_with_ddc(dev, connector, |