Another attempt at adding encoder_mask, with some behavioral fixes.
Maarten Lankhorst (5): drm/core: Add drm_encoder_index. drm/core: Add drm_for_each_encoder_mask, v2. drm/i915: Do not touch best_encoder for load detect. drm/atomic: Do not unset crtc when an encoder is stolen drm/atomic: Add encoder_mask to crtc_state, v2.
drivers/gpu/drm/drm_atomic_helper.c | 46 ++++++++++++++++++++++++++++++------ drivers/gpu/drm/drm_crtc.c | 23 ++++++++++++++++++ drivers/gpu/drm/i915/intel_display.c | 5 ++-- include/drm/drm_crtc.h | 14 +++++++++++ 4 files changed, 79 insertions(+), 9 deletions(-)
This is useful for adding encoder_mask in crtc_state.
Signed-off-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com --- drivers/gpu/drm/drm_crtc.c | 23 +++++++++++++++++++++++ include/drm/drm_crtc.h | 1 + 2 files changed, 24 insertions(+)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 62fa95fa5471..b7c990e0c8b4 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1161,6 +1161,29 @@ out_unlock: EXPORT_SYMBOL(drm_encoder_init);
/** + * drm_encoder_index - find the index of a registered encoder + * @encoder: encoder to find index for + * + * Given a registered encoder, return the index of that encoder within a DRM + * device's list of encoders. + */ +unsigned int drm_encoder_index(struct drm_encoder *encoder) +{ + unsigned int index = 0; + struct drm_encoder *tmp; + + drm_for_each_encoder(tmp, encoder->dev) { + if (tmp == encoder) + return index; + + index++; + } + + BUG(); +} +EXPORT_SYMBOL(drm_encoder_index); + +/** * drm_encoder_cleanup - cleans up an initialised encoder * @encoder: encoder to cleanup * diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index c65a212db77e..fd2ace4a18de 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -2225,6 +2225,7 @@ int drm_encoder_init(struct drm_device *dev, struct drm_encoder *encoder, const struct drm_encoder_funcs *funcs, int encoder_type, const char *name, ...); +extern unsigned int drm_encoder_index(struct drm_encoder *encoder);
/** * drm_encoder_crtc_ok - can a given crtc drive a given encoder?
Hi Maarten,
2016-01-07 Maarten Lankhorst maarten.lankhorst@linux.intel.com:
This is useful for adding encoder_mask in crtc_state.
Signed-off-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
drivers/gpu/drm/drm_crtc.c | 23 +++++++++++++++++++++++ include/drm/drm_crtc.h | 1 + 2 files changed, 24 insertions(+)
Reviewed-by: Gustavo Padovan gustavo.padovan@collabora.co.uk
Gustavo
This is similar to the other drm_for_each_*_mask functions.
Changes since v1: - Use for_each_if
Signed-off-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com --- include/drm/drm_crtc.h | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index fd2ace4a18de..c0226f945d62 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -2153,6 +2153,17 @@ struct drm_mode_config { list_for_each_entry((plane), &(dev)->mode_config.plane_list, head) \ for_each_if ((plane_mask) & (1 << drm_plane_index(plane)))
+/** + * drm_for_each_encoder_mask - iterate over encoders specified by bitmask + * @encoder: the loop cursor + * @dev: the DRM device + * @encoder_mask: bitmask of encoder indices + * + * Iterate over all encoders specified by bitmask. + */ +#define drm_for_each_encoder_mask(encoder, dev, encoder_mask) \ + list_for_each_entry((encoder), &(dev)->mode_config.encoder_list, head) \ + for_each_if ((encoder_mask) & (1 << drm_encoder_index(encoder)))
#define obj_to_crtc(x) container_of(x, struct drm_crtc, base) #define obj_to_connector(x) container_of(x, struct drm_connector, base)
Hi Maarten,
2016-01-07 Maarten Lankhorst maarten.lankhorst@linux.intel.com:
This is similar to the other drm_for_each_*_mask functions.
Changes since v1:
- Use for_each_if
Signed-off-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
include/drm/drm_crtc.h | 11 +++++++++++ 1 file changed, 11 insertions(+)
Reviewed-by: Gustavo Padovan gustavo.padovan@collabora.co.uk
Gustavo
This should only be touched by drm_atomic_helper.
Signed-off-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 2 -- 1 file changed, 2 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 391cc7f000da..d77968092ce4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -10549,7 +10549,6 @@ retry: }
connector_state->crtc = crtc; - connector_state->best_encoder = &intel_encoder->base;
crtc_state = intel_atomic_get_crtc_state(state, intel_crtc); if (IS_ERR(crtc_state)) { @@ -10645,7 +10644,6 @@ void intel_release_load_detect_pipe(struct drm_connector *connector, if (IS_ERR(crtc_state)) goto fail;
- connector_state->best_encoder = NULL; connector_state->crtc = NULL;
crtc_state->base.enable = crtc_state->base.active = false;
Hi Maarten,
2016-01-07 Maarten Lankhorst maarten.lankhorst@linux.intel.com:
This should only be touched by drm_atomic_helper.
Signed-off-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
drivers/gpu/drm/i915/intel_display.c | 2 -- 1 file changed, 2 deletions(-)
Reviewed-by: Gustavo Padovan gustavo.padovan@collabora.co.uk
Gustavo
While we steal the encoder away from the connector the connector may be updated to use a different encoder.
Without this change if 2 connectors swap encoders one of them will end up without a crtc.
Signed-off-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com --- drivers/gpu/drm/drm_atomic_helper.c | 4 ---- 1 file changed, 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 57cccd68ca52..9c84b3b37631 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -134,7 +134,6 @@ steal_encoder(struct drm_atomic_state *state, struct drm_crtc_state *crtc_state; struct drm_connector *connector; struct drm_connector_state *connector_state; - int ret;
/* * We can only steal an encoder coming from a connector, which means we @@ -165,9 +164,6 @@ steal_encoder(struct drm_atomic_state *state, if (IS_ERR(connector_state)) return PTR_ERR(connector_state);
- ret = drm_atomic_set_crtc_for_connector(connector_state, NULL); - if (ret) - return ret; connector_state->best_encoder = NULL; }
Hi Maarten,
2016-01-07 Maarten Lankhorst maarten.lankhorst@linux.intel.com:
While we steal the encoder away from the connector the connector may be updated to use a different encoder.
Without this change if 2 connectors swap encoders one of them will end up without a crtc.
Signed-off-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
drivers/gpu/drm/drm_atomic_helper.c | 4 ---- 1 file changed, 4 deletions(-)
Reviewed-by: Gustavo Padovan gustavo.padovan@collabora.co.uk
Gustavo
This allows iteration over encoders using drm_for_each_encoder_mask without inspecting all connector_states, which requires connection_mutex.
Changes since v1: - Add a set_best_encoder helper function and update encoder_mask inside it.
Signed-off-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com --- drivers/gpu/drm/drm_atomic_helper.c | 42 +++++++++++++++++++++++++++++++++--- drivers/gpu/drm/i915/intel_display.c | 3 +++ include/drm/drm_crtc.h | 2 ++ 3 files changed, 44 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 9c84b3b37631..a33c7a2aaa78 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -125,6 +125,41 @@ get_current_crtc_for_encoder(struct drm_device *dev, return NULL; }
+static void +set_best_encoder(struct drm_atomic_state *state, + struct drm_connector_state *conn_state, + struct drm_encoder *encoder) +{ + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + + if (conn_state->best_encoder) { + /* Unset the encoder_mask in the old crtc state. */ + crtc = conn_state->connector->state->crtc; + + WARN_ON(!crtc); + if (crtc) { + crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); + + crtc_state->encoder_mask &= + ~(1 << drm_encoder_index(conn_state->best_encoder)); + } + } + + if (encoder) { + crtc = conn_state->crtc; + WARN_ON(!crtc); + if (crtc) { + crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); + + crtc_state->encoder_mask |= + 1 << drm_encoder_index(encoder); + } + } + + conn_state->best_encoder = encoder; +} + static int steal_encoder(struct drm_atomic_state *state, struct drm_encoder *encoder, @@ -164,7 +199,7 @@ steal_encoder(struct drm_atomic_state *state, if (IS_ERR(connector_state)) return PTR_ERR(connector_state);
- connector_state->best_encoder = NULL; + set_best_encoder(state, connector_state, NULL); }
return 0; @@ -212,7 +247,7 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx) connector->base.id, connector->name);
- connector_state->best_encoder = NULL; + set_best_encoder(state, connector_state, NULL);
return 0; } @@ -275,7 +310,8 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx) if (WARN_ON(!connector_state->crtc)) return -EINVAL;
- connector_state->best_encoder = new_encoder; + set_best_encoder(state, connector_state, new_encoder); + idx = drm_crtc_index(connector_state->crtc);
crtc_state = state->crtc_states[idx]; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d77968092ce4..2d9cc848f294 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -15604,6 +15604,7 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc) crtc->base.state->active = crtc->active; crtc->base.enabled = crtc->active; crtc->base.state->connector_mask = 0; + crtc->base.state->encoder_mask = 0;
/* Because we only establish the connector -> encoder -> * crtc links if something is active, this means the @@ -15843,6 +15844,8 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) */ encoder->base.crtc->state->connector_mask |= 1 << drm_connector_index(&connector->base); + encoder->base.crtc->state->encoder_mask |= + 1 << drm_encoder_index(&encoder->base); }
} else { diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index c0226f945d62..51287f36b214 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -307,6 +307,7 @@ struct drm_plane_helper_funcs; * @connectors_changed: connectors to this crtc have been updated * @plane_mask: bitmask of (1 << drm_plane_index(plane)) of attached planes * @connector_mask: bitmask of (1 << drm_connector_index(connector)) of attached connectors + * @encoder_mask: bitmask of (1 << drm_encoder_index(encoder)) of attached encoders * @last_vblank_count: for helpers and drivers to capture the vblank of the * update to ensure framebuffer cleanup isn't done too early * @adjusted_mode: for use by helpers and drivers to compute adjusted mode timings @@ -341,6 +342,7 @@ struct drm_crtc_state { u32 plane_mask;
u32 connector_mask; + u32 encoder_mask;
/* last_vblank_count: for vblank waits before cleanup */ u32 last_vblank_count;
A diff to make this v3. Found when running this through IGT bat.
- Keeping an encoder but moving it to a different crtc resulted, in encoder_mask not being updated. - Add some paranoia when best_encoder was already updated to a different encoder in steal_encoder. This could happen in theory. - Relax the WARN_ON(!crtc) when resuming after resume and add an explanation what the WARN_ON is for. --- diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 1ac35072e14f..e5534dc9c4b4 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -137,7 +137,13 @@ set_best_encoder(struct drm_atomic_state *state, /* Unset the encoder_mask in the old crtc state. */ crtc = conn_state->connector->state->crtc;
- WARN_ON(!crtc); + /* A NULL crtc is an error here because we should have + * duplicated a NULL best_encoder when crtc was NULL. + * As an exception restoring duplicated atomic state + * during resume is allowed, so don't warn when + * best_encoder is equal to encoder we intend to set. + */ + WARN_ON(!crtc && encoder != conn_state->best_encoder); if (crtc) { crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
@@ -199,6 +205,9 @@ steal_encoder(struct drm_atomic_state *state, if (IS_ERR(connector_state)) return PTR_ERR(connector_state);
+ if (connector_state->best_encoder != encoder) + continue; + set_best_encoder(state, connector_state, NULL); }
@@ -276,6 +285,8 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx) }
if (new_encoder == connector_state->best_encoder) { + set_best_encoder(state, connector_state, new_encoder); + DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] keeps [ENCODER:%d:%s], now on [CRTC:%d:%s]\n", connector->base.id, connector->name,
This allows iteration over encoders without requiring connection_mutex.
Changes since v1: - Add a set_best_encoder helper function and update encoder_mask inside it. Changes since v2: - Relax the WARN_ON(!crtc), with explanation. - Call set_best_encoder when connector is moved between crtc's. - Add some paranoia to steal_encoder to prevent accidentally setting best_encoder to NULL.
Signed-off-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com --- diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 9c84b3b37631..391b3783e341 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -125,6 +125,47 @@ get_current_crtc_for_encoder(struct drm_device *dev, return NULL; }
+static void +set_best_encoder(struct drm_atomic_state *state, + struct drm_connector_state *conn_state, + struct drm_encoder *encoder) +{ + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + + if (conn_state->best_encoder) { + /* Unset the encoder_mask in the old crtc state. */ + crtc = conn_state->connector->state->crtc; + + /* A NULL crtc is an error here because we should have + * duplicated a NULL best_encoder when crtc was NULL. + * As an exception restoring duplicated atomic state + * during resume is allowed, so don't warn when + * best_encoder is equal to encoder we intend to set. + */ + WARN_ON(!crtc && encoder != conn_state->best_encoder); + if (crtc) { + crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); + + crtc_state->encoder_mask &= + ~(1 << drm_encoder_index(conn_state->best_encoder)); + } + } + + if (encoder) { + crtc = conn_state->crtc; + WARN_ON(!crtc); + if (crtc) { + crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); + + crtc_state->encoder_mask |= + 1 << drm_encoder_index(encoder); + } + } + + conn_state->best_encoder = encoder; +} + static int steal_encoder(struct drm_atomic_state *state, struct drm_encoder *encoder, @@ -164,7 +205,10 @@ steal_encoder(struct drm_atomic_state *state, if (IS_ERR(connector_state)) return PTR_ERR(connector_state);
- connector_state->best_encoder = NULL; + if (connector_state->best_encoder != encoder) + continue; + + set_best_encoder(state, connector_state, NULL); }
return 0; @@ -212,7 +256,7 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx) connector->base.id, connector->name);
- connector_state->best_encoder = NULL; + set_best_encoder(state, connector_state, NULL);
return 0; } @@ -241,6 +285,8 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx) }
if (new_encoder == connector_state->best_encoder) { + set_best_encoder(state, connector_state, new_encoder); + DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] keeps [ENCODER:%d:%s], now on [CRTC:%d:%s]\n", connector->base.id, connector->name, @@ -275,7 +321,8 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx) if (WARN_ON(!connector_state->crtc)) return -EINVAL;
- connector_state->best_encoder = new_encoder; + set_best_encoder(state, connector_state, new_encoder); + idx = drm_crtc_index(connector_state->crtc);
crtc_state = state->crtc_states[idx]; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d77968092ce4..2d9cc848f294 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -15604,6 +15604,7 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc) crtc->base.state->active = crtc->active; crtc->base.enabled = crtc->active; crtc->base.state->connector_mask = 0; + crtc->base.state->encoder_mask = 0;
/* Because we only establish the connector -> encoder -> * crtc links if something is active, this means the @@ -15843,6 +15844,8 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) */ encoder->base.crtc->state->connector_mask |= 1 << drm_connector_index(&connector->base); + encoder->base.crtc->state->encoder_mask |= + 1 << drm_encoder_index(&encoder->base); }
} else { diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index c0226f945d62..51287f36b214 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -307,6 +307,7 @@ struct drm_plane_helper_funcs; * @connectors_changed: connectors to this crtc have been updated * @plane_mask: bitmask of (1 << drm_plane_index(plane)) of attached planes * @connector_mask: bitmask of (1 << drm_connector_index(connector)) of attached connectors + * @encoder_mask: bitmask of (1 << drm_encoder_index(encoder)) of attached encoders * @last_vblank_count: for helpers and drivers to capture the vblank of the * update to ensure framebuffer cleanup isn't done too early * @adjusted_mode: for use by helpers and drivers to compute adjusted mode timings @@ -341,6 +342,7 @@ struct drm_crtc_state { u32 plane_mask;
u32 connector_mask; + u32 encoder_mask;
/* last_vblank_count: for vblank waits before cleanup */ u32 last_vblank_count;
Hi Maarten,
2016-01-28 Maarten Lankhorst maarten.lankhorst@linux.intel.com:
This allows iteration over encoders without requiring connection_mutex.
Changes since v1:
- Add a set_best_encoder helper function and update encoder_mask inside it.
Changes since v2:
- Relax the WARN_ON(!crtc), with explanation.
- Call set_best_encoder when connector is moved between crtc's.
- Add some paranoia to steal_encoder to prevent accidentally setting best_encoder to NULL.
Signed-off-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
Reviewed-by: Gustavo Padovan gustavo.padovan@collabora.co.uk
Gustavo
On Wed, Feb 03, 2016 at 10:45:07AM -0200, Gustavo Padovan wrote:
Hi Maarten,
2016-01-28 Maarten Lankhorst maarten.lankhorst@linux.intel.com:
This allows iteration over encoders without requiring connection_mutex.
Changes since v1:
- Add a set_best_encoder helper function and update encoder_mask inside it.
Changes since v2:
- Relax the WARN_ON(!crtc), with explanation.
- Call set_best_encoder when connector is moved between crtc's.
- Add some paranoia to steal_encoder to prevent accidentally setting best_encoder to NULL.
Signed-off-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
Reviewed-by: Gustavo Padovan gustavo.padovan@collabora.co.uk
All merged to drm-misc, thanks. -Daniel
dri-devel@lists.freedesktop.org