Commit b1ba124d authored by Ville Syrjälä's avatar Ville Syrjälä

drm/i915: Respect DP++ adaptor TMDS clock limit

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: default avatarTore Anderson <tore@fud.no>
Fixes: 7a0baa62 ("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: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1462216105-20881-3-git-send-email-ville.syrjala@linux.intel.comReviewed-by: default avatarShashank Sharma <shashank.sharma@intel.com>
parent ede53344
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_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_dp_mst_helper.h>
#include <drm/drm_rect.h> #include <drm/drm_rect.h>
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
...@@ -753,6 +754,10 @@ struct cxsr_latency { ...@@ -753,6 +754,10 @@ struct cxsr_latency {
struct intel_hdmi { struct intel_hdmi {
i915_reg_t hdmi_reg; i915_reg_t hdmi_reg;
int ddc_bus; int ddc_bus;
struct {
enum drm_dp_dual_mode_type type;
int max_tmds_clock;
} dp_dual_mode;
bool limited_color_range; bool limited_color_range;
bool color_range_auto; bool color_range_auto;
bool has_hdmi_sink; bool has_hdmi_sink;
......
...@@ -1167,27 +1167,42 @@ static void pch_post_disable_hdmi(struct intel_encoder *encoder) ...@@ -1167,27 +1167,42 @@ static void pch_post_disable_hdmi(struct intel_encoder *encoder)
intel_disable_hdmi(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 (IS_G4X(dev_priv))
if ((respect_dvi_limit && !hdmi->has_hdmi_sink) || IS_G4X(dev))
return 165000; 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; return 300000;
else else
return 225000; 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 static enum drm_mode_status
hdmi_port_clock_valid(struct intel_hdmi *hdmi, 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); struct drm_device *dev = intel_hdmi_to_dev(hdmi);
if (clock < 25000) if (clock < 25000)
return MODE_CLOCK_LOW; 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; return MODE_CLOCK_HIGH;
/* BXT DPLL can't generate 223-240 MHz */ /* BXT DPLL can't generate 223-240 MHz */
...@@ -1311,7 +1326,7 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, ...@@ -1311,7 +1326,7 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
* within limits. * within limits.
*/ */
if (pipe_config->pipe_bpp > 8*3 && pipe_config->has_hdmi_sink && 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)) { hdmi_12bpc_possible(pipe_config)) {
DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n"); DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n");
desired_bpp = 12*3; desired_bpp = 12*3;
...@@ -1353,10 +1368,35 @@ intel_hdmi_unset_edid(struct drm_connector *connector) ...@@ -1353,10 +1368,35 @@ intel_hdmi_unset_edid(struct drm_connector *connector)
intel_hdmi->has_audio = false; intel_hdmi->has_audio = false;
intel_hdmi->rgb_quant_range_selectable = 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); kfree(to_intel_connector(connector)->detect_edid);
to_intel_connector(connector)->detect_edid = NULL; 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 static bool
intel_hdmi_set_edid(struct drm_connector *connector, bool force) intel_hdmi_set_edid(struct drm_connector *connector, bool force)
{ {
...@@ -1372,6 +1412,8 @@ 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_gmbus_get_adapter(dev_priv,
intel_hdmi->ddc_bus)); intel_hdmi->ddc_bus));
intel_hdmi_dp_dual_mode_detect(connector);
intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS); intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment