From 8d91c1e411f55d7ea91b1183a2e9f8088fb4d5be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Fabian=20Silva=20Delgado?= Date: Tue, 15 Dec 2015 14:52:16 -0300 Subject: Linux-libre 4.3.2-gnu --- drivers/gpu/drm/i915/intel_hdmi.c | 446 +++++++++++++++++++++++++++++++------- 1 file changed, 364 insertions(+), 82 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_hdmi.c') diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index e97731aab..dcd336bcd 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -174,10 +174,14 @@ static bool g4x_infoframe_enabled(struct drm_encoder *encoder) struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); u32 val = I915_READ(VIDEO_DIP_CTL); - if (VIDEO_DIP_PORT(intel_dig_port->port) == (val & VIDEO_DIP_PORT_MASK)) - return val & VIDEO_DIP_ENABLE; + if ((val & VIDEO_DIP_ENABLE) == 0) + return false; - return false; + if ((val & VIDEO_DIP_PORT_MASK) != VIDEO_DIP_PORT(intel_dig_port->port)) + return false; + + return val & (VIDEO_DIP_ENABLE_AVI | + VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_SPD); } static void ibx_write_infoframe(struct drm_encoder *encoder, @@ -227,10 +231,15 @@ static bool ibx_infoframe_enabled(struct drm_encoder *encoder) int reg = TVIDEO_DIP_CTL(intel_crtc->pipe); u32 val = I915_READ(reg); - if (VIDEO_DIP_PORT(intel_dig_port->port) == (val & VIDEO_DIP_PORT_MASK)) - return val & VIDEO_DIP_ENABLE; + if ((val & VIDEO_DIP_ENABLE) == 0) + return false; - return false; + if ((val & VIDEO_DIP_PORT_MASK) != VIDEO_DIP_PORT(intel_dig_port->port)) + return false; + + return val & (VIDEO_DIP_ENABLE_AVI | + VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT | + VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP); } static void cpt_write_infoframe(struct drm_encoder *encoder, @@ -282,7 +291,12 @@ static bool cpt_infoframe_enabled(struct drm_encoder *encoder) int reg = TVIDEO_DIP_CTL(intel_crtc->pipe); u32 val = I915_READ(reg); - return val & VIDEO_DIP_ENABLE; + if ((val & VIDEO_DIP_ENABLE) == 0) + return false; + + return val & (VIDEO_DIP_ENABLE_AVI | + VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT | + VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP); } static void vlv_write_infoframe(struct drm_encoder *encoder, @@ -332,10 +346,15 @@ static bool vlv_infoframe_enabled(struct drm_encoder *encoder) int reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe); u32 val = I915_READ(reg); - if (VIDEO_DIP_PORT(intel_dig_port->port) == (val & VIDEO_DIP_PORT_MASK)) - return val & VIDEO_DIP_ENABLE; + if ((val & VIDEO_DIP_ENABLE) == 0) + return false; - return false; + if ((val & VIDEO_DIP_PORT_MASK) != VIDEO_DIP_PORT(intel_dig_port->port)) + return false; + + return val & (VIDEO_DIP_ENABLE_AVI | + VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT | + VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP); } static void hsw_write_infoframe(struct drm_encoder *encoder, @@ -383,8 +402,9 @@ static bool hsw_infoframe_enabled(struct drm_encoder *encoder) u32 ctl_reg = HSW_TVIDEO_DIP_CTL(intel_crtc->config->cpu_transcoder); u32 val = I915_READ(ctl_reg); - return val & (VIDEO_DIP_ENABLE_AVI_HSW | VIDEO_DIP_ENABLE_SPD_HSW | - VIDEO_DIP_ENABLE_VS_HSW); + return val & (VIDEO_DIP_ENABLE_VSC_HSW | VIDEO_DIP_ENABLE_AVI_HSW | + VIDEO_DIP_ENABLE_GCP_HSW | VIDEO_DIP_ENABLE_VS_HSW | + VIDEO_DIP_ENABLE_GMP_HSW | VIDEO_DIP_ENABLE_SPD_HSW); } /* @@ -514,7 +534,13 @@ static void g4x_set_infoframes(struct drm_encoder *encoder, if (!enable) { if (!(val & VIDEO_DIP_ENABLE)) return; - val &= ~VIDEO_DIP_ENABLE; + if (port != (val & VIDEO_DIP_PORT_MASK)) { + DRM_DEBUG_KMS("video DIP still enabled on port %c\n", + (val & VIDEO_DIP_PORT_MASK) >> 29); + return; + } + val &= ~(VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI | + VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_SPD); I915_WRITE(reg, val); POSTING_READ(reg); return; @@ -522,16 +548,17 @@ static void g4x_set_infoframes(struct drm_encoder *encoder, if (port != (val & VIDEO_DIP_PORT_MASK)) { if (val & VIDEO_DIP_ENABLE) { - val &= ~VIDEO_DIP_ENABLE; - I915_WRITE(reg, val); - POSTING_READ(reg); + DRM_DEBUG_KMS("video DIP already enabled on port %c\n", + (val & VIDEO_DIP_PORT_MASK) >> 29); + return; } val &= ~VIDEO_DIP_PORT_MASK; val |= port; } val |= VIDEO_DIP_ENABLE; - val &= ~VIDEO_DIP_ENABLE_VENDOR; + val &= ~(VIDEO_DIP_ENABLE_AVI | + VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_SPD); I915_WRITE(reg, val); POSTING_READ(reg); @@ -541,6 +568,97 @@ static void g4x_set_infoframes(struct drm_encoder *encoder, intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode); } +static bool hdmi_sink_is_deep_color(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct drm_connector *connector; + + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); + + /* + * HDMI cloning is only supported on g4x which doesn't + * support deep color or GCP infoframes anyway so no + * need to worry about multiple HDMI sinks here. + */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) + if (connector->encoder == encoder) + return connector->display_info.bpc > 8; + + return false; +} + +/* + * Determine if default_phase=1 can be indicated in the GCP infoframe. + * + * From HDMI specification 1.4a: + * - The first pixel of each Video Data Period shall always have a pixel packing phase of 0 + * - The first pixel following each Video Data Period shall have a pixel packing phase of 0 + * - The PP bits shall be constant for all GCPs and will be equal to the last packing phase + * - The first pixel following every transition of HSYNC or VSYNC shall have a pixel packing + * phase of 0 + */ +static bool gcp_default_phase_possible(int pipe_bpp, + const struct drm_display_mode *mode) +{ + unsigned int pixels_per_group; + + switch (pipe_bpp) { + case 30: + /* 4 pixels in 5 clocks */ + pixels_per_group = 4; + break; + case 36: + /* 2 pixels in 3 clocks */ + pixels_per_group = 2; + break; + case 48: + /* 1 pixel in 2 clocks */ + pixels_per_group = 1; + break; + default: + /* phase information not relevant for 8bpc */ + return false; + } + + return mode->crtc_hdisplay % pixels_per_group == 0 && + mode->crtc_htotal % pixels_per_group == 0 && + mode->crtc_hblank_start % pixels_per_group == 0 && + mode->crtc_hblank_end % pixels_per_group == 0 && + mode->crtc_hsync_start % pixels_per_group == 0 && + mode->crtc_hsync_end % pixels_per_group == 0 && + ((mode->flags & DRM_MODE_FLAG_INTERLACE) == 0 || + mode->crtc_htotal/2 % pixels_per_group == 0); +} + +static bool intel_hdmi_set_gcp_infoframe(struct drm_encoder *encoder) +{ + struct drm_i915_private *dev_priv = encoder->dev->dev_private; + struct intel_crtc *crtc = to_intel_crtc(encoder->crtc); + u32 reg, val = 0; + + if (HAS_DDI(dev_priv)) + reg = HSW_TVIDEO_DIP_GCP(crtc->config->cpu_transcoder); + else if (IS_VALLEYVIEW(dev_priv)) + reg = VLV_TVIDEO_DIP_GCP(crtc->pipe); + else if (HAS_PCH_SPLIT(dev_priv->dev)) + reg = TVIDEO_DIP_GCP(crtc->pipe); + else + return false; + + /* Indicate color depth whenever the sink supports deep color */ + if (hdmi_sink_is_deep_color(encoder)) + val |= GCP_COLOR_INDICATION; + + /* Enable default_phase whenever the display mode is suitably aligned */ + if (gcp_default_phase_possible(crtc->config->pipe_bpp, + &crtc->config->base.adjusted_mode)) + val |= GCP_DEFAULT_PHASE_ENABLE; + + I915_WRITE(reg, val); + + return val != 0; +} + static void ibx_set_infoframes(struct drm_encoder *encoder, bool enable, struct drm_display_mode *adjusted_mode) @@ -561,25 +679,29 @@ static void ibx_set_infoframes(struct drm_encoder *encoder, if (!enable) { if (!(val & VIDEO_DIP_ENABLE)) return; - val &= ~VIDEO_DIP_ENABLE; + val &= ~(VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI | + VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT | + VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP); I915_WRITE(reg, val); POSTING_READ(reg); return; } if (port != (val & VIDEO_DIP_PORT_MASK)) { - if (val & VIDEO_DIP_ENABLE) { - val &= ~VIDEO_DIP_ENABLE; - I915_WRITE(reg, val); - POSTING_READ(reg); - } + WARN(val & VIDEO_DIP_ENABLE, + "DIP already enabled on port %c\n", + (val & VIDEO_DIP_PORT_MASK) >> 29); val &= ~VIDEO_DIP_PORT_MASK; val |= port; } val |= VIDEO_DIP_ENABLE; - val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT | - VIDEO_DIP_ENABLE_GCP); + val &= ~(VIDEO_DIP_ENABLE_AVI | + VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT | + VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP); + + if (intel_hdmi_set_gcp_infoframe(encoder)) + val |= VIDEO_DIP_ENABLE_GCP; I915_WRITE(reg, val); POSTING_READ(reg); @@ -607,7 +729,9 @@ static void cpt_set_infoframes(struct drm_encoder *encoder, if (!enable) { if (!(val & VIDEO_DIP_ENABLE)) return; - val &= ~(VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI); + val &= ~(VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI | + VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT | + VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP); I915_WRITE(reg, val); POSTING_READ(reg); return; @@ -616,7 +740,10 @@ static void cpt_set_infoframes(struct drm_encoder *encoder, /* Set both together, unset both together: see the spec. */ val |= VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI; val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT | - VIDEO_DIP_ENABLE_GCP); + VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP); + + if (intel_hdmi_set_gcp_infoframe(encoder)) + val |= VIDEO_DIP_ENABLE_GCP; I915_WRITE(reg, val); POSTING_READ(reg); @@ -646,25 +773,29 @@ static void vlv_set_infoframes(struct drm_encoder *encoder, if (!enable) { if (!(val & VIDEO_DIP_ENABLE)) return; - val &= ~VIDEO_DIP_ENABLE; + val &= ~(VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI | + VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT | + VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP); I915_WRITE(reg, val); POSTING_READ(reg); return; } if (port != (val & VIDEO_DIP_PORT_MASK)) { - if (val & VIDEO_DIP_ENABLE) { - val &= ~VIDEO_DIP_ENABLE; - I915_WRITE(reg, val); - POSTING_READ(reg); - } + WARN(val & VIDEO_DIP_ENABLE, + "DIP already enabled on port %c\n", + (val & VIDEO_DIP_PORT_MASK) >> 29); val &= ~VIDEO_DIP_PORT_MASK; val |= port; } val |= VIDEO_DIP_ENABLE; - val &= ~(VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_ENABLE_VENDOR | - VIDEO_DIP_ENABLE_GAMUT | VIDEO_DIP_ENABLE_GCP); + val &= ~(VIDEO_DIP_ENABLE_AVI | + VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT | + VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP); + + if (intel_hdmi_set_gcp_infoframe(encoder)) + val |= VIDEO_DIP_ENABLE_GCP; I915_WRITE(reg, val); POSTING_READ(reg); @@ -686,14 +817,18 @@ static void hsw_set_infoframes(struct drm_encoder *encoder, assert_hdmi_port_disabled(intel_hdmi); + val &= ~(VIDEO_DIP_ENABLE_VSC_HSW | VIDEO_DIP_ENABLE_AVI_HSW | + VIDEO_DIP_ENABLE_GCP_HSW | VIDEO_DIP_ENABLE_VS_HSW | + VIDEO_DIP_ENABLE_GMP_HSW | VIDEO_DIP_ENABLE_SPD_HSW); + if (!enable) { - I915_WRITE(reg, 0); + I915_WRITE(reg, val); POSTING_READ(reg); return; } - val &= ~(VIDEO_DIP_ENABLE_VSC_HSW | VIDEO_DIP_ENABLE_GCP_HSW | - VIDEO_DIP_ENABLE_VS_HSW | VIDEO_DIP_ENABLE_GMP_HSW); + if (intel_hdmi_set_gcp_infoframe(encoder)) + val |= VIDEO_DIP_ENABLE_GCP_HSW; I915_WRITE(reg, val); POSTING_READ(reg); @@ -808,58 +943,146 @@ static void intel_hdmi_get_config(struct intel_encoder *encoder, else dotclock = pipe_config->port_clock; + if (pipe_config->pixel_multiplier) + dotclock /= pipe_config->pixel_multiplier; + if (HAS_PCH_SPLIT(dev_priv->dev)) ironlake_check_encoder_dotclock(pipe_config, dotclock); pipe_config->base.adjusted_mode.crtc_clock = dotclock; } -static void intel_enable_hdmi(struct intel_encoder *encoder) +static void intel_enable_hdmi_audio(struct intel_encoder *encoder) +{ + struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); + + WARN_ON(!crtc->config->has_hdmi_sink); + DRM_DEBUG_DRIVER("Enabling HDMI audio on pipe %c\n", + pipe_name(crtc->pipe)); + intel_audio_codec_enable(encoder); +} + +static void g4x_enable_hdmi(struct intel_encoder *encoder) { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); + struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); u32 temp; - u32 enable_bits = SDVO_ENABLE; - if (intel_crtc->config->has_audio) - enable_bits |= SDVO_AUDIO_ENABLE; + temp = I915_READ(intel_hdmi->hdmi_reg); + + temp |= SDVO_ENABLE; + if (crtc->config->has_audio) + temp |= SDVO_AUDIO_ENABLE; + + I915_WRITE(intel_hdmi->hdmi_reg, temp); + POSTING_READ(intel_hdmi->hdmi_reg); + + if (crtc->config->has_audio) + intel_enable_hdmi_audio(encoder); +} + +static void ibx_enable_hdmi(struct intel_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); + u32 temp; temp = I915_READ(intel_hdmi->hdmi_reg); - /* HW workaround for IBX, we need to move the port to transcoder A - * before disabling it, so restore the transcoder select bit here. */ - if (HAS_PCH_IBX(dev)) - enable_bits |= SDVO_PIPE_SEL(intel_crtc->pipe); + temp |= SDVO_ENABLE; + if (crtc->config->has_audio) + temp |= SDVO_AUDIO_ENABLE; - /* HW workaround, need to toggle enable bit off and on for 12bpc, but - * we do this anyway which shows more stable in testing. + /* + * HW workaround, need to write this twice for issue + * that may result in first write getting masked. */ - if (HAS_PCH_SPLIT(dev)) { + I915_WRITE(intel_hdmi->hdmi_reg, temp); + POSTING_READ(intel_hdmi->hdmi_reg); + I915_WRITE(intel_hdmi->hdmi_reg, temp); + POSTING_READ(intel_hdmi->hdmi_reg); + + /* + * HW workaround, need to toggle enable bit off and on + * for 12bpc with pixel repeat. + * + * FIXME: BSpec says this should be done at the end of + * of the modeset sequence, so not sure if this isn't too soon. + */ + if (crtc->config->pipe_bpp > 24 && + crtc->config->pixel_multiplier > 1) { I915_WRITE(intel_hdmi->hdmi_reg, temp & ~SDVO_ENABLE); POSTING_READ(intel_hdmi->hdmi_reg); + + /* + * HW workaround, need to write this twice for issue + * that may result in first write getting masked. + */ + I915_WRITE(intel_hdmi->hdmi_reg, temp); + POSTING_READ(intel_hdmi->hdmi_reg); + I915_WRITE(intel_hdmi->hdmi_reg, temp); + POSTING_READ(intel_hdmi->hdmi_reg); } - temp |= enable_bits; + if (crtc->config->has_audio) + intel_enable_hdmi_audio(encoder); +} + +static void cpt_enable_hdmi(struct intel_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); + enum pipe pipe = crtc->pipe; + u32 temp; + + temp = I915_READ(intel_hdmi->hdmi_reg); + + temp |= SDVO_ENABLE; + if (crtc->config->has_audio) + temp |= SDVO_AUDIO_ENABLE; + + /* + * WaEnableHDMI8bpcBefore12bpc:snb,ivb + * + * The procedure for 12bpc is as follows: + * 1. disable HDMI clock gating + * 2. enable HDMI with 8bpc + * 3. enable HDMI with 12bpc + * 4. enable HDMI clock gating + */ + + if (crtc->config->pipe_bpp > 24) { + I915_WRITE(TRANS_CHICKEN1(pipe), + I915_READ(TRANS_CHICKEN1(pipe)) | + TRANS_CHICKEN1_HDMIUNIT_GC_DISABLE); + + temp &= ~SDVO_COLOR_FORMAT_MASK; + temp |= SDVO_COLOR_FORMAT_8bpc; + } I915_WRITE(intel_hdmi->hdmi_reg, temp); POSTING_READ(intel_hdmi->hdmi_reg); - /* HW workaround, need to write this twice for issue that may result - * in first write getting masked. - */ - if (HAS_PCH_SPLIT(dev)) { + if (crtc->config->pipe_bpp > 24) { + temp &= ~SDVO_COLOR_FORMAT_MASK; + temp |= HDMI_COLOR_FORMAT_12bpc; + I915_WRITE(intel_hdmi->hdmi_reg, temp); POSTING_READ(intel_hdmi->hdmi_reg); - } - if (intel_crtc->config->has_audio) { - WARN_ON(!intel_crtc->config->has_hdmi_sink); - DRM_DEBUG_DRIVER("Enabling HDMI audio on pipe %c\n", - pipe_name(intel_crtc->pipe)); - intel_audio_codec_enable(encoder); + I915_WRITE(TRANS_CHICKEN1(pipe), + I915_READ(TRANS_CHICKEN1(pipe)) & + ~TRANS_CHICKEN1_HDMIUNIT_GC_DISABLE); } + + if (crtc->config->has_audio) + intel_enable_hdmi_audio(encoder); } static void vlv_enable_hdmi(struct intel_encoder *encoder) @@ -901,6 +1124,8 @@ static void intel_disable_hdmi(struct intel_encoder *encoder) I915_WRITE(intel_hdmi->hdmi_reg, temp); POSTING_READ(intel_hdmi->hdmi_reg); } + + intel_hdmi->set_infoframes(&encoder->base, false, NULL); } static void g4x_disable_hdmi(struct intel_encoder *encoder) @@ -926,7 +1151,7 @@ static void pch_post_disable_hdmi(struct intel_encoder *encoder) intel_disable_hdmi(encoder); } -static int hdmi_portclock_limit(struct intel_hdmi *hdmi, bool respect_dvi_limit) +static int hdmi_port_clock_limit(struct intel_hdmi *hdmi, bool respect_dvi_limit) { struct drm_device *dev = intel_hdmi_to_dev(hdmi); @@ -938,25 +1163,52 @@ static int hdmi_portclock_limit(struct intel_hdmi *hdmi, bool respect_dvi_limit) return 225000; } +static enum drm_mode_status +hdmi_port_clock_valid(struct intel_hdmi *hdmi, + int clock, bool respect_dvi_limit) +{ + struct drm_device *dev = intel_hdmi_to_dev(hdmi); + + if (clock < 25000) + return MODE_CLOCK_LOW; + if (clock > hdmi_port_clock_limit(hdmi, respect_dvi_limit)) + return MODE_CLOCK_HIGH; + + /* BXT DPLL can't generate 223-240 MHz */ + if (IS_BROXTON(dev) && clock > 223333 && clock < 240000) + return MODE_CLOCK_RANGE; + + /* CHV DPLL can't generate 216-240 MHz */ + if (IS_CHERRYVIEW(dev) && clock > 216000 && clock < 240000) + return MODE_CLOCK_RANGE; + + return MODE_OK; +} + static enum drm_mode_status intel_hdmi_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - int clock = mode->clock; + struct intel_hdmi *hdmi = intel_attached_hdmi(connector); + struct drm_device *dev = intel_hdmi_to_dev(hdmi); + enum drm_mode_status status; + int clock; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + + clock = mode->clock; if (mode->flags & DRM_MODE_FLAG_DBLCLK) clock *= 2; - if (clock > hdmi_portclock_limit(intel_attached_hdmi(connector), - true)) - return MODE_CLOCK_HIGH; - if (clock < 20000) - return MODE_CLOCK_LOW; + /* check if we can do 8bpc */ + status = hdmi_port_clock_valid(hdmi, clock, true); - if (mode->flags & DRM_MODE_FLAG_DBLSCAN) - return MODE_NO_DBLESCAN; + /* if we can't do 8bpc we may still be able to do 12bpc */ + if (!HAS_GMCH_DISPLAY(dev) && status != MODE_OK) + status = hdmi_port_clock_valid(hdmi, clock * 3 / 2, true); - return MODE_OK; + return status; } static bool hdmi_12bpc_possible(struct intel_crtc_state *crtc_state) @@ -997,8 +1249,8 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); struct drm_device *dev = encoder->base.dev; struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode; - int clock_12bpc = pipe_config->base.adjusted_mode.crtc_clock * 3 / 2; - int portclock_limit = hdmi_portclock_limit(intel_hdmi, false); + int clock_8bpc = pipe_config->base.adjusted_mode.crtc_clock; + int clock_12bpc = clock_8bpc * 3 / 2; int desired_bpp; pipe_config->has_hdmi_sink = intel_hdmi->has_hdmi_sink; @@ -1017,6 +1269,8 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK) { pipe_config->pixel_multiplier = 2; + clock_8bpc *= 2; + clock_12bpc *= 2; } if (intel_hdmi->color_range) @@ -1035,9 +1289,8 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, * within limits. */ if (pipe_config->pipe_bpp > 8*3 && pipe_config->has_hdmi_sink && - clock_12bpc <= portclock_limit && - hdmi_12bpc_possible(pipe_config) && - 0 /* FIXME 12bpc support totally broken */) { + hdmi_port_clock_valid(intel_hdmi, clock_12bpc, false) == MODE_OK && + hdmi_12bpc_possible(pipe_config)) { DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n"); desired_bpp = 12*3; @@ -1046,6 +1299,8 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, } else { DRM_DEBUG_KMS("picking bpc to 8 for HDMI output\n"); desired_bpp = 8*3; + + pipe_config->port_clock = clock_8bpc; } if (!pipe_config->bw_constrained) { @@ -1053,8 +1308,9 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, pipe_config->pipe_bpp = desired_bpp; } - if (adjusted_mode->crtc_clock > portclock_limit) { - DRM_DEBUG_KMS("too high HDMI clock, rejecting mode\n"); + if (hdmi_port_clock_valid(intel_hdmi, pipe_config->port_clock, + false) != MODE_OK) { + DRM_DEBUG_KMS("unsupported HDMI clock, rejecting mode\n"); return false; } @@ -1323,7 +1579,7 @@ static void vlv_hdmi_pre_enable(struct intel_encoder *encoder) intel_crtc->config->has_hdmi_sink, adjusted_mode); - intel_enable_hdmi(encoder); + g4x_enable_hdmi(encoder); vlv_wait_port_ready(dev_priv, dport, 0x0); } @@ -1640,7 +1896,7 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder) intel_crtc->config->has_hdmi_sink, adjusted_mode); - intel_enable_hdmi(encoder); + g4x_enable_hdmi(encoder); vlv_wait_port_ready(dev_priv, dport, 0x0); } @@ -1653,7 +1909,7 @@ static void intel_hdmi_destroy(struct drm_connector *connector) } static const struct drm_connector_funcs intel_hdmi_connector_funcs = { - .dpms = intel_connector_dpms, + .dpms = drm_atomic_helper_connector_dpms, .detect = intel_hdmi_detect, .force = intel_hdmi_force, .fill_modes = drm_helper_probe_single_connector_modes, @@ -1702,6 +1958,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, struct drm_device *dev = intel_encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; enum port port = intel_dig_port->port; + uint8_t alternate_ddc_pin; drm_connector_init(dev, connector, &intel_hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); @@ -1735,6 +1992,26 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, intel_hdmi->ddc_bus = GMBUS_PIN_DPD; intel_encoder->hpd_pin = HPD_PORT_D; break; + case PORT_E: + /* On SKL PORT E doesn't have seperate GMBUS pin + * We rely on VBT to set a proper alternate GMBUS pin. */ + alternate_ddc_pin = + dev_priv->vbt.ddi_port_info[PORT_E].alternate_ddc_pin; + switch (alternate_ddc_pin) { + case DDC_PIN_B: + intel_hdmi->ddc_bus = GMBUS_PIN_DPB; + break; + case DDC_PIN_C: + intel_hdmi->ddc_bus = GMBUS_PIN_DPC; + break; + case DDC_PIN_D: + intel_hdmi->ddc_bus = GMBUS_PIN_DPD; + break; + default: + MISSING_CASE(alternate_ddc_pin); + } + intel_encoder->hpd_pin = HPD_PORT_E; + break; case PORT_A: intel_encoder->hpd_pin = HPD_PORT_A; /* Internal port only for eDP. */ @@ -1827,7 +2104,12 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port) intel_encoder->post_disable = vlv_hdmi_post_disable; } else { intel_encoder->pre_enable = intel_hdmi_pre_enable; - intel_encoder->enable = intel_enable_hdmi; + if (HAS_PCH_CPT(dev)) + intel_encoder->enable = cpt_enable_hdmi; + else if (HAS_PCH_IBX(dev)) + intel_encoder->enable = ibx_enable_hdmi; + else + intel_encoder->enable = g4x_enable_hdmi; } intel_encoder->type = INTEL_OUTPUT_HDMI; -- cgit v1.2.3