[Intel-gfx] [PATCH] drm/i915: Solve IVB FDI bifurcation problems using atomic
ville.syrjala at linux.intel.com
ville.syrjala at linux.intel.com
Tue Nov 24 04:59:24 PST 2015
From: Ville Syrjälä <ville.syrjala at linux.intel.com>
Now that we have atomic modesetting, we can use it to solve the IVB
pipe B/C FDI lane sharing issue. That is, we can force a modeset on
pipe B if pipe C requires the FDI lanes currently used by pipe B.
To achieve this we do the following:
First stage:
- first compute the initial pipe config for each modeset crtc
- initial max FDI estimate will be 4 for pipe B, 2 for pipe C
- also need to run through the encoder .compute_config()
hooks once to update has_pch_encoder
- and we need to compute an initial number of required FDI lanes.
Note that we only need something that makes fdi_lanes > 0 speak
the truth, so there's no need to check against the max here
Second stage:
- Check if pipe C requires any FDI lanes, and based on that
update the max_fdi_lanes for both pipe B and pipe C
- Check if pipe B conflicts with the new requirements,
and if so, add it to the state forcing a modeset (in case
it already wasn't included)
Third stage:
- run through the rest of the pipe config computation, incuding
recomputing the FDI lane requirement and checking it against the
max
- this will signal that we should recompute things with a lower
bpp by returning -EAGAIN
- we keep doing this in a loop as long -EAGAIN is returned
If some other error occurs we bail out as usual
Signed-off-by: Ville Syrjälä <ville.syrjala at linux.intel.com>
---
drivers/gpu/drm/i915/intel_display.c | 353 ++++++++++++++++++++++-------------
drivers/gpu/drm/i915/intel_drv.h | 1 +
2 files changed, 222 insertions(+), 132 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 68fb449ded77..082323c2aa8c 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -6492,87 +6492,120 @@ static int pipe_required_fdi_lanes(struct intel_crtc_state *crtc_state)
return 0;
}
-static int ironlake_check_fdi_lanes(struct drm_device *dev, enum pipe pipe,
- struct intel_crtc_state *pipe_config)
+static void
+intel_modeset_pipe_config_start(struct intel_crtc *crtc,
+ struct intel_crtc_state *pipe_config);
+static int
+intel_modeset_pipe_config_continue(struct intel_crtc *crtc,
+ struct intel_crtc_state *pipe_config);
+
+static int intel_add_crtc_modeset_to_state(struct drm_atomic_state *state,
+ struct intel_crtc *crtc)
{
- struct drm_atomic_state *state = pipe_config->base.state;
- struct intel_crtc *other_crtc;
- struct intel_crtc_state *other_crtc_state;
+ struct intel_crtc_state *crtc_state;
+ int ret;
- DRM_DEBUG_KMS("checking fdi config on pipe %c, lanes %i\n",
- pipe_name(pipe), pipe_config->fdi_lanes);
- if (pipe_config->fdi_lanes > 4) {
- DRM_DEBUG_KMS("invalid fdi lane config on pipe %c: %i lanes\n",
- pipe_name(pipe), pipe_config->fdi_lanes);
- return -EINVAL;
- }
+ crtc_state = intel_atomic_get_crtc_state(state, crtc);
+ if (IS_ERR(crtc_state))
+ return PTR_ERR(crtc_state);
- if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
- if (pipe_config->fdi_lanes > 2) {
- DRM_DEBUG_KMS("only 2 lanes on haswell, required: %i lanes\n",
- pipe_config->fdi_lanes);
- return -EINVAL;
- } else {
- return 0;
- }
- }
+ crtc_state->base.mode_changed = true;
- if (INTEL_INFO(dev)->num_pipes == 2)
- return 0;
+ ret = drm_atomic_add_affected_connectors(state, &crtc->base);
+ if (ret)
+ return ret;
- /* Ivybridge 3 pipe is really complicated */
- switch (pipe) {
- case PIPE_A:
- return 0;
- case PIPE_B:
- if (pipe_config->fdi_lanes <= 2)
- return 0;
+ intel_modeset_pipe_config_start(crtc, crtc_state);
- other_crtc = to_intel_crtc(intel_get_crtc_for_pipe(dev, PIPE_C));
- other_crtc_state =
- intel_atomic_get_crtc_state(state, other_crtc);
- if (IS_ERR(other_crtc_state))
- return PTR_ERR(other_crtc_state);
+ /*
+ * Must run through this at least once for every new
+ * pipe to make sure has_pch_encoder and fdi_lanes are
+ * set up to something half decent.
+ */
+ ret = intel_modeset_pipe_config_continue(crtc, crtc_state);
+ if (ret)
+ return ret;
- if (pipe_required_fdi_lanes(other_crtc_state) > 0) {
- DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %c: %i lanes\n",
- pipe_name(pipe), pipe_config->fdi_lanes);
- return -EINVAL;
- }
- return 0;
- case PIPE_C:
- if (pipe_config->fdi_lanes > 2) {
- DRM_DEBUG_KMS("only 2 lanes on pipe %c: required %i lanes\n",
- pipe_name(pipe), pipe_config->fdi_lanes);
- return -EINVAL;
- }
-
- other_crtc = to_intel_crtc(intel_get_crtc_for_pipe(dev, PIPE_B));
- other_crtc_state =
- intel_atomic_get_crtc_state(state, other_crtc);
- if (IS_ERR(other_crtc_state))
- return PTR_ERR(other_crtc_state);
-
- if (pipe_required_fdi_lanes(other_crtc_state) > 2) {
- DRM_DEBUG_KMS("fdi link B uses too many lanes to enable link C\n");
- return -EINVAL;
- }
- return 0;
- default:
- BUG();
- }
+ return 0;
}
-#define RETRY 1
-static int ironlake_fdi_compute_config(struct intel_crtc *intel_crtc,
- struct intel_crtc_state *pipe_config)
+static bool intel_crtc_needs_modeset(struct drm_atomic_state *state,
+ struct intel_crtc *crtc)
{
- struct drm_device *dev = intel_crtc->base.dev;
- const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
- int lane, link_bw, fdi_dotclock, ret;
- bool needs_recompute = false;
+ struct drm_crtc_state *crtc_state =
+ drm_atomic_get_existing_crtc_state(state, &crtc->base);
+
+ return crtc_state && needs_modeset(crtc_state);
+}
+
+static int ivybridge_compute_fdi_bc_bifurcation(struct drm_atomic_state *state)
+{
+ struct drm_device *dev = state->dev;
+ struct intel_crtc *crtc_b, *crtc_c;
+ struct intel_crtc_state *crtc_state_b, *crtc_state_c;
+ int ret = 0;
+
+ crtc_b = to_intel_crtc(intel_get_crtc_for_pipe(dev, PIPE_B));
+ crtc_c = to_intel_crtc(intel_get_crtc_for_pipe(dev, PIPE_C));
+
+ /* Nothing to do if we're not modesetting pipe B or C */
+ if (!intel_crtc_needs_modeset(state, crtc_b) &&
+ !intel_crtc_needs_modeset(state, crtc_c))
+ return 0;
+
+ crtc_state_b = intel_atomic_get_crtc_state(state, crtc_b);
+ if (IS_ERR(crtc_state_b))
+ return PTR_ERR(crtc_state_b);
+
+ crtc_state_c = intel_atomic_get_crtc_state(state, crtc_c);
+ if (IS_ERR(crtc_state_c))
+ return PTR_ERR(crtc_state_c);
+
+ if (pipe_required_fdi_lanes(crtc_state_c) > 0) {
+ DRM_DEBUG_KMS("pipe C requires FDI lanes\n");
+
+ /* need to modeset pipe B to make room for pipe C */
+ if (pipe_required_fdi_lanes(crtc_state_b) > 2) {
+ DRM_DEBUG_KMS("forcing a modeset in pipe B to free up FDI lanes\n");
+ ret = intel_add_crtc_modeset_to_state(state, crtc_b);
+ }
+
+ /*
+ * must do this _after_ intel_add_crtc_modeset_to_state() since
+ * otherwise we end up with the defaults
+ */
+ crtc_state_b->max_fdi_lanes = 2;
+ crtc_state_c->max_fdi_lanes = 2;
+ } else {
+ DRM_DEBUG_KMS("pipe C doesn't require FDI lanes\n");
+
+ /* pipe C no longer needs FDI lanes, can give them to pipe B */
+ if (pipe_required_fdi_lanes(crtc_state_b) > 0 &&
+ pipe_required_fdi_lanes(crtc_state_b) <= 2 &&
+ crtc_state_b->bw_constrained) {
+ DRM_DEBUG_KMS("forcing a modeset in pipe B to use up FDI lanes\n");
+ ret = intel_add_crtc_modeset_to_state(state, crtc_b);
+ }
+
+ /*
+ * must do this _after_ intel_add_crtc_modeset_to_state() since
+ * otherwise we end up with the defaults
+ */
+ crtc_state_b->max_fdi_lanes = 4;
+ crtc_state_c->max_fdi_lanes = 0;
+ }
+
+ return ret;
+}
+
+static void ironlake_fdi_compute_config(struct intel_crtc *crtc,
+ struct intel_crtc_state *pipe_config)
+{
+ struct drm_device *dev = crtc->base.dev;
+ const struct drm_display_mode *adjusted_mode =
+ &pipe_config->base.adjusted_mode;
+ int link_bw, fdi_dotclock;
-retry:
/* FDI is a binary signal running at ~2.7GHz, encoding
* each output octet as 10 bits. The actual frequency
* is stored as a divider into a 100MHz clock, and the
@@ -6584,30 +6617,36 @@ retry:
fdi_dotclock = adjusted_mode->crtc_clock;
- lane = ironlake_get_lanes_required(fdi_dotclock, link_bw,
- pipe_config->pipe_bpp);
+ pipe_config->fdi_lanes =
+ ironlake_get_lanes_required(fdi_dotclock, link_bw,
+ pipe_config->pipe_bpp);
- pipe_config->fdi_lanes = lane;
+ intel_link_compute_m_n(pipe_config->pipe_bpp, pipe_config->fdi_lanes,
+ fdi_dotclock, link_bw, &pipe_config->fdi_m_n);
+}
- intel_link_compute_m_n(pipe_config->pipe_bpp, lane, fdi_dotclock,
- link_bw, &pipe_config->fdi_m_n);
+static int ironlake_fdi_check_config(struct intel_crtc *crtc,
+ struct intel_crtc_state *pipe_config)
+{
+ DRM_DEBUG_KMS("fdi lane config on pipe %c: %i lanes (max: %i)\n",
+ pipe_name(crtc->pipe), pipe_config->fdi_lanes,
+ pipe_config->max_fdi_lanes);
- ret = ironlake_check_fdi_lanes(intel_crtc->base.dev,
- intel_crtc->pipe, pipe_config);
- if (ret == -EINVAL && pipe_config->pipe_bpp > 6*3) {
+ if (pipe_config->fdi_lanes <= pipe_config->max_fdi_lanes)
+ return 0;
+
+ DRM_DEBUG_KMS("invalid fdi lane config on pipe %c: %i lanes (max: %i)\n",
+ pipe_name(crtc->pipe), pipe_config->fdi_lanes,
+ pipe_config->max_fdi_lanes);
+
+ if (pipe_config->pipe_bpp > 6*3) {
pipe_config->pipe_bpp -= 2*3;
- DRM_DEBUG_KMS("fdi link bw constraint, reducing pipe bpp to %i\n",
- pipe_config->pipe_bpp);
- needs_recompute = true;
pipe_config->bw_constrained = true;
- goto retry;
+ return -EAGAIN;
}
- if (needs_recompute)
- return RETRY;
-
- return ret;
+ return -EINVAL;
}
static bool pipe_config_supports_ips(struct drm_i915_private *dev_priv,
@@ -6701,7 +6740,7 @@ static int intel_crtc_compute_config(struct intel_crtc *crtc,
hsw_compute_ips_config(crtc, pipe_config);
if (pipe_config->has_pch_encoder)
- return ironlake_fdi_compute_config(crtc, pipe_config);
+ return ironlake_fdi_check_config(crtc, pipe_config);
return 0;
}
@@ -11986,7 +12025,7 @@ connected_sink_compute_bpp(struct intel_connector *connector,
}
}
-static int
+static void
compute_baseline_pipe_bpp(struct intel_crtc *crtc,
struct intel_crtc_state *pipe_config)
{
@@ -12016,8 +12055,6 @@ compute_baseline_pipe_bpp(struct intel_crtc *crtc,
connected_sink_compute_bpp(to_intel_connector(connector),
pipe_config);
}
-
- return bpp;
}
static void intel_dump_crtc_timings(const struct drm_display_mode *mode)
@@ -12047,9 +12084,10 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc,
DRM_DEBUG_KMS("cpu_transcoder: %c\n", transcoder_name(pipe_config->cpu_transcoder));
DRM_DEBUG_KMS("pipe bpp: %i, dithering: %i\n",
pipe_config->pipe_bpp, pipe_config->dither);
- DRM_DEBUG_KMS("fdi/pch: %i, lanes: %i, gmch_m: %u, gmch_n: %u, link_m: %u, link_n: %u, tu: %u\n",
+ DRM_DEBUG_KMS("fdi/pch: %i, lanes: %i, max_lanes: %i, gmch_m: %u, gmch_n: %u, link_m: %u, link_n: %u, tu: %u\n",
pipe_config->has_pch_encoder,
pipe_config->fdi_lanes,
+ pipe_config->max_fdi_lanes,
pipe_config->fdi_m_n.gmch_m, pipe_config->fdi_m_n.gmch_n,
pipe_config->fdi_m_n.link_m, pipe_config->fdi_m_n.link_n,
pipe_config->fdi_m_n.tu);
@@ -12245,22 +12283,14 @@ clear_intel_crtc_state(struct intel_crtc_state *crtc_state)
crtc_state->pch_pfit.force_thru = force_thru;
}
-static int
-intel_modeset_pipe_config(struct drm_crtc *crtc,
- struct intel_crtc_state *pipe_config)
+static void
+intel_modeset_pipe_config_start(struct intel_crtc *crtc,
+ struct intel_crtc_state *pipe_config)
{
- struct drm_atomic_state *state = pipe_config->base.state;
- struct intel_encoder *encoder;
- struct drm_connector *connector;
- struct drm_connector_state *connector_state;
- int base_bpp, ret = -EINVAL;
- int i;
- bool retry = true;
-
clear_intel_crtc_state(pipe_config);
pipe_config->cpu_transcoder =
- (enum transcoder) to_intel_crtc(crtc)->pipe;
+ (enum transcoder) crtc->pipe;
/*
* Sanitize sync polarity flags based on requested ones. If neither
@@ -12275,10 +12305,7 @@ intel_modeset_pipe_config(struct drm_crtc *crtc,
(DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC)))
pipe_config->base.adjusted_mode.flags |= DRM_MODE_FLAG_NVSYNC;
- base_bpp = compute_baseline_pipe_bpp(to_intel_crtc(crtc),
- pipe_config);
- if (base_bpp < 0)
- goto fail;
+ compute_baseline_pipe_bpp(crtc, pipe_config);
/*
* Determine the real pipe dimensions. Note that stereo modes can
@@ -12292,7 +12319,23 @@ intel_modeset_pipe_config(struct drm_crtc *crtc,
&pipe_config->pipe_src_w,
&pipe_config->pipe_src_h);
-encoder_retry:
+ /* initial assumption is no shared lanes */
+ if (HAS_DDI(crtc->base.dev) || crtc->pipe == PIPE_C)
+ pipe_config->max_fdi_lanes = 2;
+ else
+ pipe_config->max_fdi_lanes = 4;
+}
+
+static int
+intel_modeset_pipe_config_continue(struct intel_crtc *crtc,
+ struct intel_crtc_state *pipe_config)
+{
+ struct drm_atomic_state *state = pipe_config->base.state;
+ struct intel_encoder *encoder;
+ struct drm_connector *connector;
+ struct drm_connector_state *connector_state;
+ int i;
+
/* Ensure the port clock defaults are reset when retrying. */
pipe_config->port_clock = 0;
pipe_config->pixel_multiplier = 1;
@@ -12306,14 +12349,14 @@ encoder_retry:
* a chance to reject the mode entirely.
*/
for_each_connector_in_state(state, connector, connector_state, i) {
- if (connector_state->crtc != crtc)
+ if (connector_state->crtc != &crtc->base)
continue;
encoder = to_intel_encoder(connector_state->best_encoder);
if (!(encoder->compute_config(encoder, pipe_config))) {
DRM_DEBUG_KMS("Encoder config failure\n");
- goto fail;
+ return -EINVAL;
}
}
@@ -12323,31 +12366,37 @@ encoder_retry:
pipe_config->port_clock = pipe_config->base.adjusted_mode.crtc_clock
* pipe_config->pixel_multiplier;
- ret = intel_crtc_compute_config(to_intel_crtc(crtc), pipe_config);
- if (ret < 0) {
- DRM_DEBUG_KMS("CRTC fixup failed\n");
- goto fail;
+ if (pipe_config->has_pch_encoder)
+ ironlake_fdi_compute_config(crtc, pipe_config);
+
+ return 0;
+}
+
+static int
+intel_modeset_pipe_config_finish(struct intel_crtc *crtc,
+ struct intel_crtc_state *pipe_config)
+{
+ int ret;
+
+ ret = intel_crtc_compute_config(crtc, pipe_config);
+
+ if (ret == -EAGAIN) {
+ DRM_DEBUG_KMS("CRTC bw constrained, retrying\n");
+ return ret;
}
- if (ret == RETRY) {
- if (WARN(!retry, "loop in pipe configuration computation\n")) {
- ret = -EINVAL;
- goto fail;
- }
-
- DRM_DEBUG_KMS("CRTC bw constrained, retrying\n");
- retry = false;
- goto encoder_retry;
+ if (ret) {
+ DRM_DEBUG_KMS("CRTC fixup failed\n");
+ return ret;
}
/* Dithering seems to not pass-through bits correctly when it should, so
* only enable it on 6bpc panels. */
pipe_config->dither = pipe_config->pipe_bpp == 6*3;
- DRM_DEBUG_KMS("hw max bpp: %i, pipe bpp: %i, dithering: %i\n",
- base_bpp, pipe_config->pipe_bpp, pipe_config->dither);
+ DRM_DEBUG_KMS("pipe bpp: %i, dithering: %i\n",
+ pipe_config->pipe_bpp, pipe_config->dither);
-fail:
- return ret;
+ return 0;
}
static void
@@ -13194,15 +13243,13 @@ static int intel_atomic_check(struct drm_device *dev,
struct drm_crtc_state *crtc_state;
int ret, i;
bool any_ms = false;
+ bool retry;
ret = drm_atomic_helper_check_modeset(dev, state);
if (ret)
return ret;
for_each_crtc_in_state(state, crtc, crtc_state, i) {
- struct intel_crtc_state *pipe_config =
- to_intel_crtc_state(crtc_state);
-
memset(&to_intel_crtc(crtc)->atomic, 0,
sizeof(struct intel_crtc_atomic_commit));
@@ -13222,13 +13269,55 @@ static int intel_atomic_check(struct drm_device *dev,
/* FIXME: For only active_changed we shouldn't need to do any
* state recomputation at all. */
- ret = drm_atomic_add_affected_connectors(state, crtc);
+ ret = intel_add_crtc_modeset_to_state(state,
+ to_intel_crtc(crtc));
if (ret)
return ret;
+ }
- ret = intel_modeset_pipe_config(crtc, pipe_config);
+ if (IS_IVYBRIDGE(dev)) {
+ ret = ivybridge_compute_fdi_bc_bifurcation(state);
if (ret)
return ret;
+ }
+
+ /*
+ * Keep at it until we run out of ways
+ * to reduce FDI bandwidth utilization.
+ */
+ do {
+ retry = false;
+
+ for_each_crtc_in_state(state, crtc, crtc_state, i) {
+ struct intel_crtc_state *pipe_config =
+ to_intel_crtc_state(crtc_state);
+
+ if (!crtc_state->enable) {
+ if (needs_modeset(crtc_state))
+ any_ms = true;
+ continue;
+ }
+
+ if (!needs_modeset(crtc_state))
+ continue;
+
+ ret = intel_modeset_pipe_config_continue(to_intel_crtc(crtc),
+ pipe_config);
+ if (ret)
+ return ret;
+
+ ret = intel_modeset_pipe_config_finish(to_intel_crtc(crtc),
+ pipe_config);
+ if (ret == -EAGAIN)
+ retry = true;
+ else if (ret)
+ return ret;
+ }
+ } while (retry);
+
+ for_each_crtc_in_state(state, crtc, crtc_state, i) {
+ struct intel_crtc_state *pipe_config =
+ to_intel_crtc_state(crtc_state);
if (i915.fastboot &&
intel_pipe_config_compare(state->dev,
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index ab5c147fa9e9..631a9be89fdf 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -471,6 +471,7 @@ struct intel_crtc_state {
} pch_pfit;
/* FDI configuration, only valid if has_pch_encoder is set. */
+ int max_fdi_lanes;
int fdi_lanes;
struct intel_link_m_n fdi_m_n;
--
2.4.10
More information about the Intel-gfx
mailing list