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

drm/i915: Allow DBLSCAN user modes with eDP/LVDS/DSI

When encountering a connector with the scaling mode property both
intel and modesetting ddxs sometimes add tons of DBLSCAN modes
to the output's mode list. The idea presumably being that since the
output will be going through the panel fitter anyway we can pretend
to use any kind of mode.

Sadly that means we can't reject user modes with the DBLSCAN flag
until we know whether we're going to be using the panel's native
mode or the user mode directly. Doing otherwise means X clients using
xf86vidmode/xrandr will get a protocol error (and often self
terminate as a result) when the kernel refuses to use the requested
mode with the DBLSCAN flag.

To undo the regression we'll move the DBLSCAN checks into the
connector->mode_valid() and encoder->compute_config() hooks.

Cc: stable@vger.kernel.org
Cc: Vito Caputo <vcaputo@pengaru.com>
Reported-by: default avatarVito Caputo <vcaputo@pengaru.com>
Fixes: e995ca0b ("drm/i915: Provide a device level .mode_valid() hook")
References: https://lkml.org/lkml/2018/5/21/715Signed-off-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180524125403.23445-1-ville.syrjala@linux.intel.comReviewed-by: default avatarMaarten Lankhorst <maarten.lankhorst@linux.intel.com>
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=106804Tested-by: default avatarArkadiusz Miskiewicz <arekm@maven.pl>
parent bb187e93
...@@ -304,6 +304,9 @@ intel_crt_mode_valid(struct drm_connector *connector, ...@@ -304,6 +304,9 @@ intel_crt_mode_valid(struct drm_connector *connector,
int max_dotclk = dev_priv->max_dotclk_freq; int max_dotclk = dev_priv->max_dotclk_freq;
int max_clock; int max_clock;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
if (mode->clock < 25000) if (mode->clock < 25000)
return MODE_CLOCK_LOW; return MODE_CLOCK_LOW;
...@@ -337,6 +340,12 @@ static bool intel_crt_compute_config(struct intel_encoder *encoder, ...@@ -337,6 +340,12 @@ static bool intel_crt_compute_config(struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config, struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state) struct drm_connector_state *conn_state)
{ {
struct drm_display_mode *adjusted_mode =
&pipe_config->base.adjusted_mode;
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
return false;
return true; return true;
} }
...@@ -344,6 +353,12 @@ static bool pch_crt_compute_config(struct intel_encoder *encoder, ...@@ -344,6 +353,12 @@ static bool pch_crt_compute_config(struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config, struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state) struct drm_connector_state *conn_state)
{ {
struct drm_display_mode *adjusted_mode =
&pipe_config->base.adjusted_mode;
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
return false;
pipe_config->has_pch_encoder = true; pipe_config->has_pch_encoder = true;
return true; return true;
...@@ -354,6 +369,11 @@ static bool hsw_crt_compute_config(struct intel_encoder *encoder, ...@@ -354,6 +369,11 @@ static bool hsw_crt_compute_config(struct intel_encoder *encoder,
struct drm_connector_state *conn_state) struct drm_connector_state *conn_state)
{ {
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
struct drm_display_mode *adjusted_mode =
&pipe_config->base.adjusted_mode;
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
return false;
pipe_config->has_pch_encoder = true; pipe_config->has_pch_encoder = true;
......
...@@ -14529,12 +14529,22 @@ static enum drm_mode_status ...@@ -14529,12 +14529,22 @@ static enum drm_mode_status
intel_mode_valid(struct drm_device *dev, intel_mode_valid(struct drm_device *dev,
const struct drm_display_mode *mode) const struct drm_display_mode *mode)
{ {
/*
* Can't reject DBLSCAN here because Xorg ddxen can add piles
* of DBLSCAN modes to the output's mode list when they detect
* the scaling mode property on the connector. And they don't
* ask the kernel to validate those modes in any way until
* modeset time at which point the client gets a protocol error.
* So in order to not upset those clients we silently ignore the
* DBLSCAN flag on such connectors. For other connectors we will
* reject modes with the DBLSCAN flag in encoder->compute_config().
* And we always reject DBLSCAN modes in connector->mode_valid()
* as we never want such modes on the connector's mode list.
*/
if (mode->vscan > 1) if (mode->vscan > 1)
return MODE_NO_VSCAN; return MODE_NO_VSCAN;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
if (mode->flags & DRM_MODE_FLAG_HSKEW) if (mode->flags & DRM_MODE_FLAG_HSKEW)
return MODE_H_ILLEGAL; return MODE_H_ILLEGAL;
......
...@@ -420,6 +420,9 @@ intel_dp_mode_valid(struct drm_connector *connector, ...@@ -420,6 +420,9 @@ intel_dp_mode_valid(struct drm_connector *connector,
int max_rate, mode_rate, max_lanes, max_link_clock; int max_rate, mode_rate, max_lanes, max_link_clock;
int max_dotclk; int max_dotclk;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
max_dotclk = intel_dp_downstream_max_dotclock(intel_dp); max_dotclk = intel_dp_downstream_max_dotclock(intel_dp);
if (intel_dp_is_edp(intel_dp) && fixed_mode) { if (intel_dp_is_edp(intel_dp) && fixed_mode) {
...@@ -1869,6 +1872,9 @@ intel_dp_compute_config(struct intel_encoder *encoder, ...@@ -1869,6 +1872,9 @@ intel_dp_compute_config(struct intel_encoder *encoder,
conn_state->scaling_mode); conn_state->scaling_mode);
} }
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
return false;
if ((IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) && if ((IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) &&
adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)
return false; return false;
......
...@@ -48,6 +48,9 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, ...@@ -48,6 +48,9 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
bool reduce_m_n = drm_dp_has_quirk(&intel_dp->desc, bool reduce_m_n = drm_dp_has_quirk(&intel_dp->desc,
DP_DPCD_QUIRK_LIMITED_M_N); DP_DPCD_QUIRK_LIMITED_M_N);
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
return false;
pipe_config->has_pch_encoder = false; pipe_config->has_pch_encoder = false;
bpp = 24; bpp = 24;
if (intel_dp->compliance.test_data.bpc) { if (intel_dp->compliance.test_data.bpc) {
...@@ -366,6 +369,9 @@ intel_dp_mst_mode_valid(struct drm_connector *connector, ...@@ -366,6 +369,9 @@ intel_dp_mst_mode_valid(struct drm_connector *connector,
if (!intel_dp) if (!intel_dp)
return MODE_ERROR; return MODE_ERROR;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
max_link_clock = intel_dp_max_link_rate(intel_dp); max_link_clock = intel_dp_max_link_rate(intel_dp);
max_lanes = intel_dp_max_lane_count(intel_dp); max_lanes = intel_dp_max_lane_count(intel_dp);
......
...@@ -326,6 +326,9 @@ static bool intel_dsi_compute_config(struct intel_encoder *encoder, ...@@ -326,6 +326,9 @@ static bool intel_dsi_compute_config(struct intel_encoder *encoder,
conn_state->scaling_mode); conn_state->scaling_mode);
} }
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
return false;
/* DSI uses short packets for sync events, so clear mode flags for DSI */ /* DSI uses short packets for sync events, so clear mode flags for DSI */
adjusted_mode->flags = 0; adjusted_mode->flags = 0;
...@@ -1266,6 +1269,9 @@ intel_dsi_mode_valid(struct drm_connector *connector, ...@@ -1266,6 +1269,9 @@ intel_dsi_mode_valid(struct drm_connector *connector,
DRM_DEBUG_KMS("\n"); DRM_DEBUG_KMS("\n");
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
if (fixed_mode) { if (fixed_mode) {
if (mode->hdisplay > fixed_mode->hdisplay) if (mode->hdisplay > fixed_mode->hdisplay)
return MODE_PANEL; return MODE_PANEL;
......
...@@ -215,6 +215,9 @@ intel_dvo_mode_valid(struct drm_connector *connector, ...@@ -215,6 +215,9 @@ intel_dvo_mode_valid(struct drm_connector *connector,
int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
int target_clock = mode->clock; int target_clock = mode->clock;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
/* XXX: Validate clock range */ /* XXX: Validate clock range */
if (fixed_mode) { if (fixed_mode) {
...@@ -250,6 +253,9 @@ static bool intel_dvo_compute_config(struct intel_encoder *encoder, ...@@ -250,6 +253,9 @@ static bool intel_dvo_compute_config(struct intel_encoder *encoder,
if (fixed_mode) if (fixed_mode)
intel_fixed_panel_mode(fixed_mode, adjusted_mode); intel_fixed_panel_mode(fixed_mode, adjusted_mode);
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
return false;
return true; return true;
} }
......
...@@ -1540,6 +1540,9 @@ intel_hdmi_mode_valid(struct drm_connector *connector, ...@@ -1540,6 +1540,9 @@ intel_hdmi_mode_valid(struct drm_connector *connector,
bool force_dvi = bool force_dvi =
READ_ONCE(to_intel_digital_connector_state(connector->state)->force_audio) == HDMI_AUDIO_OFF_DVI; READ_ONCE(to_intel_digital_connector_state(connector->state)->force_audio) == HDMI_AUDIO_OFF_DVI;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
clock = mode->clock; clock = mode->clock;
if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING)
...@@ -1660,6 +1663,9 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, ...@@ -1660,6 +1663,9 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
int desired_bpp; int desired_bpp;
bool force_dvi = intel_conn_state->force_audio == HDMI_AUDIO_OFF_DVI; bool force_dvi = intel_conn_state->force_audio == HDMI_AUDIO_OFF_DVI;
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
return false;
pipe_config->has_hdmi_sink = !force_dvi && intel_hdmi->has_hdmi_sink; pipe_config->has_hdmi_sink = !force_dvi && intel_hdmi->has_hdmi_sink;
if (pipe_config->has_hdmi_sink) if (pipe_config->has_hdmi_sink)
......
...@@ -378,6 +378,8 @@ intel_lvds_mode_valid(struct drm_connector *connector, ...@@ -378,6 +378,8 @@ intel_lvds_mode_valid(struct drm_connector *connector,
struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode; struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode;
int max_pixclk = to_i915(connector->dev)->max_dotclk_freq; int max_pixclk = to_i915(connector->dev)->max_dotclk_freq;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
if (mode->hdisplay > fixed_mode->hdisplay) if (mode->hdisplay > fixed_mode->hdisplay)
return MODE_PANEL; return MODE_PANEL;
if (mode->vdisplay > fixed_mode->vdisplay) if (mode->vdisplay > fixed_mode->vdisplay)
...@@ -427,6 +429,9 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, ...@@ -427,6 +429,9 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
intel_fixed_panel_mode(intel_connector->panel.fixed_mode, intel_fixed_panel_mode(intel_connector->panel.fixed_mode,
adjusted_mode); adjusted_mode);
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
return false;
if (HAS_PCH_SPLIT(dev_priv)) { if (HAS_PCH_SPLIT(dev_priv)) {
pipe_config->has_pch_encoder = true; pipe_config->has_pch_encoder = true;
......
...@@ -1160,6 +1160,9 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder, ...@@ -1160,6 +1160,9 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder,
adjusted_mode); adjusted_mode);
} }
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
return false;
/* /*
* Make the CRTC code factor in the SDVO pixel multiplier. The * Make the CRTC code factor in the SDVO pixel multiplier. The
* SDVO device will factor out the multiplier during mode_set. * SDVO device will factor out the multiplier during mode_set.
...@@ -1631,6 +1634,9 @@ intel_sdvo_mode_valid(struct drm_connector *connector, ...@@ -1631,6 +1634,9 @@ intel_sdvo_mode_valid(struct drm_connector *connector,
struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector); struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
if (intel_sdvo->pixel_clock_min > mode->clock) if (intel_sdvo->pixel_clock_min > mode->clock)
return MODE_CLOCK_LOW; return MODE_CLOCK_LOW;
......
...@@ -846,6 +846,9 @@ intel_tv_mode_valid(struct drm_connector *connector, ...@@ -846,6 +846,9 @@ intel_tv_mode_valid(struct drm_connector *connector,
const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state); const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state);
int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
if (mode->clock > max_dotclk) if (mode->clock > max_dotclk)
return MODE_CLOCK_HIGH; return MODE_CLOCK_HIGH;
...@@ -873,16 +876,21 @@ intel_tv_compute_config(struct intel_encoder *encoder, ...@@ -873,16 +876,21 @@ intel_tv_compute_config(struct intel_encoder *encoder,
struct drm_connector_state *conn_state) struct drm_connector_state *conn_state)
{ {
const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state); const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state);
struct drm_display_mode *adjusted_mode =
&pipe_config->base.adjusted_mode;
if (!tv_mode) if (!tv_mode)
return false; return false;
pipe_config->base.adjusted_mode.crtc_clock = tv_mode->clock; if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
return false;
adjusted_mode->crtc_clock = tv_mode->clock;
DRM_DEBUG_KMS("forcing bpc to 8 for TV\n"); DRM_DEBUG_KMS("forcing bpc to 8 for TV\n");
pipe_config->pipe_bpp = 8*3; pipe_config->pipe_bpp = 8*3;
/* TV has it's own notion of sync and other mode flags, so clear them. */ /* TV has it's own notion of sync and other mode flags, so clear them. */
pipe_config->base.adjusted_mode.flags = 0; adjusted_mode->flags = 0;
/* /*
* FIXME: We don't check whether the input mode is actually what we want * FIXME: We don't check whether the input mode is actually what we want
......
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