is_trans_port_sync_slave(crtc_state);
}
+static enum pipe bigjoiner_master_pipe(const struct intel_crtc_state *crtc_state)
+{
+ return ffs(crtc_state->bigjoiner_pipes) - 1;
+}
+
+u8 intel_crtc_bigjoiner_slave_pipes(const struct intel_crtc_state *crtc_state)
+{
+ return crtc_state->bigjoiner_pipes & ~BIT(bigjoiner_master_pipe(crtc_state));
+}
+
bool intel_crtc_is_bigjoiner_slave(const struct intel_crtc_state *crtc_state)
{
- return crtc_state->bigjoiner_slave;
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ return crtc_state->bigjoiner_pipes &&
+ crtc->pipe != bigjoiner_master_pipe(crtc_state);
}
bool intel_crtc_is_bigjoiner_master(const struct intel_crtc_state *crtc_state)
{
- return crtc_state->bigjoiner && !crtc_state->bigjoiner_slave;
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ return crtc_state->bigjoiner_pipes &&
+ crtc->pipe == bigjoiner_master_pipe(crtc_state);
}
-static struct intel_crtc *intel_master_crtc(const struct intel_crtc_state *crtc_state)
+struct intel_crtc *intel_master_crtc(const struct intel_crtc_state *crtc_state)
{
+ struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev);
+
if (intel_crtc_is_bigjoiner_slave(crtc_state))
- return crtc_state->bigjoiner_linked_crtc;
+ return intel_crtc_for_pipe(i915, bigjoiner_master_pipe(crtc_state));
else
return to_intel_crtc(crtc_state->uapi.crtc);
}
*master_pipes, *slave_pipes);
}
+static enum pipe get_bigjoiner_master_pipe(enum pipe pipe, u8 master_pipes, u8 slave_pipes)
+{
+ if ((slave_pipes & BIT(pipe)) == 0)
+ return pipe;
+
+ /* ignore everything above our pipe */
+ master_pipes &= ~GENMASK(7, pipe);
+
+ /* highest remaining bit should be our master pipe */
+ return fls(master_pipes) - 1;
+}
+
+static u8 get_bigjoiner_slave_pipes(enum pipe pipe, u8 master_pipes, u8 slave_pipes)
+{
+ enum pipe master_pipe, next_master_pipe;
+
+ master_pipe = get_bigjoiner_master_pipe(pipe, master_pipes, slave_pipes);
+
+ if ((master_pipes & BIT(master_pipe)) == 0)
+ return 0;
+
+ /* ignore our master pipe and everything below it */
+ master_pipes &= ~GENMASK(master_pipe, 0);
+ /* make sure a high bit is set for the ffs() */
+ master_pipes |= BIT(7);
+ /* lowest remaining bit should be the next master pipe */
+ next_master_pipe = ffs(master_pipes) - 1;
+
+ return slave_pipes & GENMASK(next_master_pipe - 1, master_pipe);
+}
+
static u8 hsw_panel_transcoders(struct drm_i915_private *i915)
{
u8 panel_transcoder_mask = BIT(TRANSCODER_EDP);
/* bigjoiner slave -> consider the master pipe's transcoder as well */
enabled_bigjoiner_pipes(dev_priv, &master_pipes, &slave_pipes);
if (slave_pipes & BIT(crtc->pipe)) {
- cpu_transcoder = (enum transcoder) crtc->pipe - 1;
+ cpu_transcoder = (enum transcoder)
+ get_bigjoiner_master_pipe(crtc->pipe, master_pipes, slave_pipes);
if (transcoder_ddi_func_is_enabled(dev_priv, cpu_transcoder))
enabled_transcoders |= BIT(cpu_transcoder);
}
return transcoder_is_dsi(pipe_config->cpu_transcoder);
}
+static void intel_bigjoiner_get_config(struct intel_crtc_state *crtc_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ struct drm_i915_private *i915 = to_i915(crtc->base.dev);
+ u8 master_pipes, slave_pipes;
+ enum pipe pipe = crtc->pipe;
+
+ enabled_bigjoiner_pipes(i915, &master_pipes, &slave_pipes);
+
+ if (((master_pipes | slave_pipes) & BIT(pipe)) == 0)
+ return;
+
+ crtc_state->bigjoiner = true;
+ crtc_state->bigjoiner_pipes =
+ BIT(get_bigjoiner_master_pipe(pipe, master_pipes, slave_pipes)) |
+ get_bigjoiner_slave_pipes(pipe, master_pipes, slave_pipes);
+}
+
static bool hsw_get_pipe_config(struct intel_crtc *crtc,
struct intel_crtc_state *pipe_config)
{
goto out;
intel_dsc_get_config(pipe_config);
- if (DISPLAY_VER(dev_priv) >= 13 && !pipe_config->dsc.compression_enable)
- intel_uncompressed_joiner_get_config(pipe_config);
+ intel_bigjoiner_get_config(pipe_config);
if (!transcoder_is_dsi(pipe_config->cpu_transcoder) ||
DISPLAY_VER(dev_priv) >= 11)
transcoder_name(pipe_config->master_transcoder),
pipe_config->sync_mode_slaves_mask);
- drm_dbg_kms(&dev_priv->drm, "bigjoiner: %s\n",
+ drm_dbg_kms(&dev_priv->drm, "bigjoiner: %s, pipes: 0x%x\n",
intel_crtc_is_bigjoiner_slave(pipe_config) ? "slave" :
- intel_crtc_is_bigjoiner_master(pipe_config) ? "master" : "no");
+ intel_crtc_is_bigjoiner_master(pipe_config) ? "master" : "no",
+ pipe_config->bigjoiner_pipes);
drm_dbg_kms(&dev_priv->drm, "splitter: %s, link count %d, overlap %d\n",
enableddisabled(pipe_config->splitter.enable),
PIPE_CONF_CHECK_X(sync_mode_slaves_mask);
PIPE_CONF_CHECK_I(master_transcoder);
PIPE_CONF_CHECK_BOOL(bigjoiner);
- PIPE_CONF_CHECK_BOOL(bigjoiner_slave);
- PIPE_CONF_CHECK_P(bigjoiner_linked_crtc);
+ PIPE_CONF_CHECK_X(bigjoiner_pipes);
PIPE_CONF_CHECK_I(dsc.compression_enable);
PIPE_CONF_CHECK_I(dsc.dsc_split);
static int intel_bigjoiner_add_affected_planes(struct intel_atomic_state *state)
{
+ struct drm_i915_private *i915 = to_i915(state->base.dev);
const struct intel_crtc_state *crtc_state;
struct intel_crtc *crtc;
int i;
for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) {
- int ret;
+ struct intel_crtc *other;
- if (!crtc_state->bigjoiner)
- continue;
+ for_each_intel_crtc_in_pipe_mask(&i915->drm, other,
+ crtc_state->bigjoiner_pipes) {
+ int ret;
- ret = intel_crtc_add_bigjoiner_planes(state, crtc,
- crtc_state->bigjoiner_linked_crtc);
- if (ret)
- return ret;
+ if (crtc == other)
+ continue;
+
+ ret = intel_crtc_add_bigjoiner_planes(state, crtc, other);
+ if (ret)
+ return ret;
+ }
}
return 0;
return false;
}
+static bool intel_pipes_need_modeset(struct intel_atomic_state *state,
+ u8 pipes)
+{
+ const struct intel_crtc_state *new_crtc_state;
+ struct intel_crtc *crtc;
+ int i;
+
+ for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) {
+ if (new_crtc_state->hw.enable &&
+ pipes & BIT(crtc->pipe) &&
+ intel_crtc_needs_modeset(new_crtc_state))
+ return true;
+ }
+
+ return false;
+}
+
static int intel_atomic_check_bigjoiner(struct intel_atomic_state *state,
struct intel_crtc *master_crtc)
{
struct drm_i915_private *i915 = to_i915(state->base.dev);
struct intel_crtc_state *master_crtc_state =
intel_atomic_get_new_crtc_state(state, master_crtc);
- struct intel_crtc_state *slave_crtc_state;
struct intel_crtc *slave_crtc;
+ u8 slave_pipes;
- WARN_ON(master_crtc_state->bigjoiner_linked_crtc);
- WARN_ON(intel_crtc_is_bigjoiner_slave(master_crtc_state));
+ /*
+ * TODO: encoder.compute_config() may be the best
+ * place to populate the bitmask for the master crtc.
+ * For now encoder.compute_config() just flags things
+ * as needing bigjoiner and we populate the bitmask
+ * here.
+ */
+ WARN_ON(master_crtc_state->bigjoiner_pipes);
if (!master_crtc_state->bigjoiner)
return 0;
- slave_crtc = intel_dsc_get_bigjoiner_secondary(master_crtc);
- if (!slave_crtc) {
+ slave_pipes = BIT(master_crtc->pipe + 1);
+
+ if (slave_pipes & ~bigjoiner_pipes(i915)) {
drm_dbg_kms(&i915->drm,
- "[CRTC:%d:%s] Big joiner configuration requires "
- "CRTC + 1 to be used, doesn't exist\n",
- master_crtc->base.base.id, master_crtc->base.name);
+ "[CRTC:%d:%s] Cannot act as big joiner master "
+ "(need 0x%x as slave pipes, only 0x%x possible)\n",
+ master_crtc->base.base.id, master_crtc->base.name,
+ slave_pipes, bigjoiner_pipes(i915));
return -EINVAL;
}
- slave_crtc_state = intel_atomic_get_crtc_state(&state->base, slave_crtc);
- if (IS_ERR(slave_crtc_state))
- return PTR_ERR(slave_crtc_state);
+ for_each_intel_crtc_in_pipe_mask(&i915->drm, slave_crtc, slave_pipes) {
+ struct intel_crtc_state *slave_crtc_state;
+ int ret;
- /* master being enabled, slave was already configured? */
- if (slave_crtc_state->uapi.enable)
- goto claimed;
+ slave_crtc_state = intel_atomic_get_crtc_state(&state->base, slave_crtc);
+ if (IS_ERR(slave_crtc_state))
+ return PTR_ERR(slave_crtc_state);
- /*
- * The state copy logic assumes the master crtc gets processed
- * before the slave crtc during the main compute_config loop.
- * This works because the crtcs are created in pipe order,
- * and the hardware requires master pipe < slave pipe as well.
- * Should that change we need to rethink the logic.
- */
- if (WARN_ON(drm_crtc_index(&master_crtc->base) > drm_crtc_index(&slave_crtc->base)))
- return -EINVAL;
+ /* master being enabled, slave was already configured? */
+ if (slave_crtc_state->uapi.enable) {
+ drm_dbg_kms(&i915->drm,
+ "[CRTC:%d:%s] Slave is enabled as normal CRTC, but "
+ "[CRTC:%d:%s] claiming this CRTC for bigjoiner.\n",
+ slave_crtc->base.base.id, slave_crtc->base.name,
+ master_crtc->base.base.id, master_crtc->base.name);
+ return -EINVAL;
+ }
- drm_dbg_kms(&i915->drm,
- "[CRTC:%d:%s] Used as slave for big joiner master [CRTC:%d:%s]\n",
- slave_crtc->base.base.id, slave_crtc->base.name,
- master_crtc->base.base.id, master_crtc->base.name);
+ /*
+ * The state copy logic assumes the master crtc gets processed
+ * before the slave crtc during the main compute_config loop.
+ * This works because the crtcs are created in pipe order,
+ * and the hardware requires master pipe < slave pipe as well.
+ * Should that change we need to rethink the logic.
+ */
+ if (WARN_ON(drm_crtc_index(&master_crtc->base) >
+ drm_crtc_index(&slave_crtc->base)))
+ return -EINVAL;
- master_crtc_state->bigjoiner_linked_crtc = slave_crtc;
- master_crtc_state->bigjoiner_slave = false;
+ drm_dbg_kms(&i915->drm,
+ "[CRTC:%d:%s] Used as slave for big joiner master [CRTC:%d:%s]\n",
+ slave_crtc->base.base.id, slave_crtc->base.name,
+ master_crtc->base.base.id, master_crtc->base.name);
- slave_crtc_state->bigjoiner_linked_crtc = master_crtc;
- slave_crtc_state->bigjoiner_slave = true;
+ master_crtc_state->bigjoiner_pipes =
+ BIT(master_crtc->pipe) | BIT(slave_crtc->pipe);
+ slave_crtc_state->bigjoiner_pipes =
+ BIT(master_crtc->pipe) | BIT(slave_crtc->pipe);
- return copy_bigjoiner_crtc_state_modeset(state, slave_crtc);
+ ret = copy_bigjoiner_crtc_state_modeset(state, slave_crtc);
+ if (ret)
+ return ret;
+ }
-claimed:
- drm_dbg_kms(&i915->drm,
- "[CRTC:%d:%s] Slave is enabled as normal CRTC, but "
- "[CRTC:%d:%s] claiming this CRTC for bigjoiner.\n",
- slave_crtc->base.base.id, slave_crtc->base.name,
- master_crtc->base.base.id, master_crtc->base.name);
- return -EINVAL;
+ return 0;
}
static void kill_bigjoiner_slave(struct intel_atomic_state *state,
struct intel_crtc *master_crtc)
{
+ struct drm_i915_private *i915 = to_i915(state->base.dev);
struct intel_crtc_state *master_crtc_state =
intel_atomic_get_new_crtc_state(state, master_crtc);
- struct intel_crtc *slave_crtc = master_crtc_state->bigjoiner_linked_crtc;
- struct intel_crtc_state *slave_crtc_state =
- intel_atomic_get_new_crtc_state(state, slave_crtc);
+ struct intel_crtc *slave_crtc;
+
+ for_each_intel_crtc_in_pipe_mask(&i915->drm, slave_crtc,
+ intel_crtc_bigjoiner_slave_pipes(master_crtc_state)) {
+ struct intel_crtc_state *slave_crtc_state =
+ intel_atomic_get_new_crtc_state(state, slave_crtc);
- slave_crtc_state->bigjoiner = master_crtc_state->bigjoiner = false;
- slave_crtc_state->bigjoiner_slave = master_crtc_state->bigjoiner_slave = false;
- slave_crtc_state->bigjoiner_linked_crtc = master_crtc_state->bigjoiner_linked_crtc = NULL;
+ slave_crtc_state->bigjoiner = false;
+ slave_crtc_state->bigjoiner_pipes = 0;
- intel_crtc_copy_uapi_to_hw_state_modeset(state, slave_crtc);
+ intel_crtc_copy_uapi_to_hw_state_modeset(state, slave_crtc);
+ }
+
+ master_crtc_state->bigjoiner = false;
+ master_crtc_state->bigjoiner_pipes = 0;
}
/**
static int intel_bigjoiner_add_affected_crtcs(struct intel_atomic_state *state)
{
+ struct drm_i915_private *i915 = to_i915(state->base.dev);
struct intel_crtc_state *crtc_state;
struct intel_crtc *crtc;
+ u8 affected_pipes = 0;
+ u8 modeset_pipes = 0;
int i;
for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) {
- struct intel_crtc_state *linked_crtc_state;
- struct intel_crtc *linked_crtc;
- int ret;
+ affected_pipes |= crtc_state->bigjoiner_pipes;
+ if (intel_crtc_needs_modeset(crtc_state))
+ modeset_pipes |= crtc_state->bigjoiner_pipes;
+ }
- if (!crtc_state->bigjoiner)
- continue;
+ for_each_intel_crtc_in_pipe_mask(&i915->drm, crtc, affected_pipes) {
+ crtc_state = intel_atomic_get_crtc_state(&state->base, crtc);
+ if (IS_ERR(crtc_state))
+ return PTR_ERR(crtc_state);
+ }
- linked_crtc = crtc_state->bigjoiner_linked_crtc;
- linked_crtc_state = intel_atomic_get_crtc_state(&state->base, linked_crtc);
- if (IS_ERR(linked_crtc_state))
- return PTR_ERR(linked_crtc_state);
+ for_each_intel_crtc_in_pipe_mask(&i915->drm, crtc, modeset_pipes) {
+ int ret;
- if (!intel_crtc_needs_modeset(crtc_state))
- continue;
+ crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
- linked_crtc_state->uapi.mode_changed = true;
+ crtc_state->uapi.mode_changed = true;
- ret = drm_atomic_add_affected_connectors(&state->base,
- &linked_crtc->base);
+ ret = drm_atomic_add_affected_connectors(&state->base, &crtc->base);
if (ret)
return ret;
- ret = intel_atomic_add_affected_planes(state, linked_crtc);
+ ret = intel_atomic_add_affected_planes(state, crtc);
if (ret)
return ret;
}
}
if (new_crtc_state->bigjoiner) {
- struct intel_crtc_state *linked_crtc_state =
- intel_atomic_get_new_crtc_state(state, new_crtc_state->bigjoiner_linked_crtc);
-
- if (intel_crtc_needs_modeset(linked_crtc_state)) {
+ if (intel_pipes_need_modeset(state, new_crtc_state->bigjoiner_pipes)) {
new_crtc_state->uapi.mode_changed = true;
new_crtc_state->update_pipe = false;
}
/* read out to slave crtc as well for bigjoiner */
if (crtc_state->bigjoiner) {
+ struct intel_crtc *slave_crtc;
+
/* encoder should read be linked to bigjoiner master */
WARN_ON(intel_crtc_is_bigjoiner_slave(crtc_state));
- crtc = crtc_state->bigjoiner_linked_crtc;
- crtc_state = to_intel_crtc_state(crtc->base.state);
- intel_encoder_get_config(encoder, crtc_state);
+ for_each_intel_crtc_in_pipe_mask(&dev_priv->drm, slave_crtc,
+ intel_crtc_bigjoiner_slave_pipes(crtc_state)) {
+ struct intel_crtc_state *slave_crtc_state;
+
+ slave_crtc_state = to_intel_crtc_state(slave_crtc->base.state);
+ intel_encoder_get_config(encoder, slave_crtc_state);
+ }
}
} else {
encoder->base.crtc = NULL;