| From c578d15226c99f0566d5d022f81af6b7d69928db Mon Sep 17 00:00:00 2001 |
| From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= <ville.syrjala@linux.intel.com> |
| Date: Mon, 2 May 2016 22:08:23 +0300 |
| Subject: drm/i915: Respect DP++ adaptor TMDS clock limit |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Ville Syrjälä <ville.syrjala@linux.intel.com> |
| |
| commit c578d15226c99f0566d5d022f81af6b7d69928db upstream. |
| |
| Try to detect the max TMDS clock limit for the DP++ adaptor (if any) |
| and take it into account when checking the port clock. |
| |
| Note that as with the sink (HDMI vs. DVI) TMDS clock limit we'll ignore |
| the adaptor TMDS clock limit in the modeset path, in case users are |
| already "overclocking" their TMDS links. One subtle change here is that |
| we'll have to respect the adaptor TMDS clock limit when we decide whether |
| to do 12bpc or 8bpc, otherwise we might end up picking 12bpc and |
| accidentally driving the TMDS link out of spec even when the user chose |
| a mode that fits wihting the limits at 8bpc. This means you can't |
| "overclock" your DP++ dongle at 12bpc anymore, but you can continue to |
| do so at 8bpc. |
| |
| Note that for simplicity we'll use the I2C access method for all dual |
| mode adaptors including type 2. Otherwise we'd have to start mixing |
| DP AUX and HDMI together. In the future we may need to do that if we |
| come across any board designs that don't hook up the DDC pins to the |
| DP++ connectors. Such boards would obviously only work with type 2 |
| dual mode adaptors, and not type 1. |
| |
| v2: Store adaptor type under indel_hdmi->dp_dual_mode |
| Deal with DRM_DP_DUAL_MODE_UNKNOWN |
| Pass adaptor type to drm_dp_dual_mode_max_tmds_clock(), |
| and use it for type1 adaptors as well |
| |
| Reported-by: Tore Anderson <tore@fud.no> |
| Fixes: 7a0baa623446 ("Revert "drm/i915: Disable 12bpc hdmi for now"") |
| Cc: Paulo Zanoni <paulo.r.zanoni@intel.com> |
| Cc: Shashank Sharma <shashank.sharma@intel.com> |
| Cc: Daniel Vetter <daniel.vetter@ffwll.ch> |
| Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> |
| Link: http://patchwork.freedesktop.org/patch/msgid/1462216105-20881-3-git-send-email-ville.syrjala@linux.intel.com |
| Reviewed-by: Shashank Sharma <shashank.sharma@intel.com> |
| (cherry picked from commit b1ba124d8e95cca48d33502a4a76b1ed09d213ce) |
| Signed-off-by: Jani Nikula <jani.nikula@intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/gpu/drm/i915/intel_drv.h | 5 +++ |
| drivers/gpu/drm/i915/intel_hdmi.c | 58 ++++++++++++++++++++++++++++++++------ |
| 2 files changed, 55 insertions(+), 8 deletions(-) |
| |
| --- a/drivers/gpu/drm/i915/intel_drv.h |
| +++ b/drivers/gpu/drm/i915/intel_drv.h |
| @@ -33,6 +33,7 @@ |
| #include <drm/drm_crtc.h> |
| #include <drm/drm_crtc_helper.h> |
| #include <drm/drm_fb_helper.h> |
| +#include <drm/drm_dp_dual_mode_helper.h> |
| #include <drm/drm_dp_mst_helper.h> |
| #include <drm/drm_rect.h> |
| #include <drm/drm_atomic.h> |
| @@ -703,6 +704,10 @@ struct cxsr_latency { |
| struct intel_hdmi { |
| i915_reg_t hdmi_reg; |
| int ddc_bus; |
| + struct { |
| + enum drm_dp_dual_mode_type type; |
| + int max_tmds_clock; |
| + } dp_dual_mode; |
| bool limited_color_range; |
| bool color_range_auto; |
| bool has_hdmi_sink; |
| --- a/drivers/gpu/drm/i915/intel_hdmi.c |
| +++ b/drivers/gpu/drm/i915/intel_hdmi.c |
| @@ -1168,27 +1168,42 @@ static void pch_post_disable_hdmi(struct |
| intel_disable_hdmi(encoder); |
| } |
| |
| -static int hdmi_port_clock_limit(struct intel_hdmi *hdmi, bool respect_dvi_limit) |
| +static int intel_hdmi_source_max_tmds_clock(struct drm_i915_private *dev_priv) |
| { |
| - struct drm_device *dev = intel_hdmi_to_dev(hdmi); |
| - |
| - if ((respect_dvi_limit && !hdmi->has_hdmi_sink) || IS_G4X(dev)) |
| + if (IS_G4X(dev_priv)) |
| return 165000; |
| - else if (IS_HASWELL(dev) || INTEL_INFO(dev)->gen >= 8) |
| + else if (IS_HASWELL(dev_priv) || INTEL_INFO(dev_priv)->gen >= 8) |
| return 300000; |
| else |
| return 225000; |
| } |
| |
| +static int hdmi_port_clock_limit(struct intel_hdmi *hdmi, |
| + bool respect_downstream_limits) |
| +{ |
| + struct drm_device *dev = intel_hdmi_to_dev(hdmi); |
| + int max_tmds_clock = intel_hdmi_source_max_tmds_clock(to_i915(dev)); |
| + |
| + if (respect_downstream_limits) { |
| + if (hdmi->dp_dual_mode.max_tmds_clock) |
| + max_tmds_clock = min(max_tmds_clock, |
| + hdmi->dp_dual_mode.max_tmds_clock); |
| + if (!hdmi->has_hdmi_sink) |
| + max_tmds_clock = min(max_tmds_clock, 165000); |
| + } |
| + |
| + return max_tmds_clock; |
| +} |
| + |
| static enum drm_mode_status |
| hdmi_port_clock_valid(struct intel_hdmi *hdmi, |
| - int clock, bool respect_dvi_limit) |
| + int clock, bool respect_downstream_limits) |
| { |
| 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)) |
| + if (clock > hdmi_port_clock_limit(hdmi, respect_downstream_limits)) |
| return MODE_CLOCK_HIGH; |
| |
| /* BXT DPLL can't generate 223-240 MHz */ |
| @@ -1312,7 +1327,7 @@ bool intel_hdmi_compute_config(struct in |
| * within limits. |
| */ |
| if (pipe_config->pipe_bpp > 8*3 && pipe_config->has_hdmi_sink && |
| - hdmi_port_clock_valid(intel_hdmi, clock_12bpc, false) == MODE_OK && |
| + hdmi_port_clock_valid(intel_hdmi, clock_12bpc, true) == MODE_OK && |
| hdmi_12bpc_possible(pipe_config)) { |
| DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n"); |
| desired_bpp = 12*3; |
| @@ -1352,10 +1367,35 @@ intel_hdmi_unset_edid(struct drm_connect |
| intel_hdmi->has_audio = false; |
| intel_hdmi->rgb_quant_range_selectable = false; |
| |
| + intel_hdmi->dp_dual_mode.type = DRM_DP_DUAL_MODE_NONE; |
| + intel_hdmi->dp_dual_mode.max_tmds_clock = 0; |
| + |
| kfree(to_intel_connector(connector)->detect_edid); |
| to_intel_connector(connector)->detect_edid = NULL; |
| } |
| |
| +static void |
| +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(connector); |
| + 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(adapter); |
| + |
| + if (type == DRM_DP_DUAL_MODE_NONE || |
| + type == DRM_DP_DUAL_MODE_UNKNOWN) |
| + return; |
| + |
| + hdmi->dp_dual_mode.type = type; |
| + hdmi->dp_dual_mode.max_tmds_clock = |
| + drm_dp_dual_mode_max_tmds_clock(type, adapter); |
| + |
| + DRM_DEBUG_KMS("DP dual mode adaptor (%s) detected (max TMDS clock: %d kHz)\n", |
| + drm_dp_get_dual_mode_type_name(type), |
| + hdmi->dp_dual_mode.max_tmds_clock); |
| +} |
| + |
| static bool |
| intel_hdmi_set_edid(struct drm_connector *connector, bool force) |
| { |
| @@ -1371,6 +1411,8 @@ intel_hdmi_set_edid(struct drm_connector |
| intel_gmbus_get_adapter(dev_priv, |
| intel_hdmi->ddc_bus)); |
| |
| + intel_hdmi_dp_dual_mode_detect(connector); |
| + |
| intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS); |
| } |
| |