Message ID | 1462216105-20881-3-git-send-email-ville.syrjala@linux.intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Reviewed-by: Shashank Sharma <shashank.sharma@intel.com> Regards Shashank On 5/3/2016 12:38 AM, ville.syrjala@linux.intel.com wrote: > From: Ville Syrjälä <ville.syrjala@linux.intel.com> > > 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 > > Cc: stable@vger.kernel.org > 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> > --- > drivers/gpu/drm/i915/intel_drv.h | 5 ++++ > drivers/gpu/drm/i915/intel_hdmi.c | 58 +++++++++++++++++++++++++++++++++------ > 2 files changed, 55 insertions(+), 8 deletions(-) > > diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h > index 21dee3f89e84..e94d18fb2ff1 100644 > --- 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> > @@ -753,6 +754,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; > diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c > index e1012d612024..31ca11134294 100644 > --- a/drivers/gpu/drm/i915/intel_hdmi.c > +++ b/drivers/gpu/drm/i915/intel_hdmi.c > @@ -1167,27 +1167,42 @@ static void pch_post_disable_hdmi(struct intel_encoder *encoder) > 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 */ > @@ -1311,7 +1326,7 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, > * 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; > @@ -1353,10 +1368,35 @@ intel_hdmi_unset_edid(struct drm_connector *connector) > 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) > { > @@ -1372,6 +1412,8 @@ intel_hdmi_set_edid(struct drm_connector *connector, bool force) > 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); > } > >
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 21dee3f89e84..e94d18fb2ff1 100644 --- 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> @@ -753,6 +754,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; diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index e1012d612024..31ca11134294 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1167,27 +1167,42 @@ static void pch_post_disable_hdmi(struct intel_encoder *encoder) 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 */ @@ -1311,7 +1326,7 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, * 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; @@ -1353,10 +1368,35 @@ intel_hdmi_unset_edid(struct drm_connector *connector) 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) { @@ -1372,6 +1412,8 @@ intel_hdmi_set_edid(struct drm_connector *connector, bool force) 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); }