Merge tag 'drm-intel-fixes-2015-07-15' into drm-intel-next-queued
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / i915 / intel_hdmi.c
index e97731aab6dcfee2a380d09b95cefdff57e193e1..70bad5bf1d48b9cc1c72afa360475487bf9b152e 100644 (file)
@@ -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;
+
+       if ((val & VIDEO_DIP_PORT_MASK) != VIDEO_DIP_PORT(intel_dig_port->port))
+               return false;
 
-       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 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);
 
-       /* 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 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 (HAS_PCH_SPLIT(dev)) {
+       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);
 }
@@ -1827,7 +2083,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;