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);
 
                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;
+
+       /* CHV/BXT DPLL can't generate 216-240 MHz */
+       if ((IS_CHERRYVIEW(dev) || IS_BROXTON(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 < 25000)
-               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)
        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;
 
        if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK) {
                pipe_config->pixel_multiplier = 2;
+               clock_8bpc *= 2;
                clock_12bpc *= 2;
        }
 
         * within limits.
         */
        if (pipe_config->pipe_bpp > 8*3 && pipe_config->has_hdmi_sink &&
-           clock_12bpc <= portclock_limit &&
+           hdmi_port_clock_valid(intel_hdmi, clock_12bpc, false) == MODE_OK &&
            hdmi_12bpc_possible(pipe_config) &&
            0 /* FIXME 12bpc support totally broken */) {
                DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n");
        } 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) {
                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;
        }