From c6c5c7fa7f3ec7d35541e9e4c6feb4ccd6cca7c9 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Mon, 18 Jan 2016 11:36:35 +0800 Subject: drm/atomic-helper: Remove redundant local var old_crtc_state in disable_outputs One of the two local variables old_crtc_state is redundantly defined in the function disable_outputs(). It has only a scope partway through the block for_each_connector_in_state. So, let's remove it and use the one which has the scope within the function disable_outputs(). Signed-off-by: Liu Ying Link: http://patchwork.freedesktop.org/patch/msgid/1453088195-2564-1-git-send-email-gnuiyl@gmail.com Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic_helper.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/gpu/drm/drm_atomic_helper.c') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 57cccd68ca52..b9d8b5393955 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -617,7 +617,6 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) for_each_connector_in_state(old_state, connector, old_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder; - struct drm_crtc_state *old_crtc_state; /* Shut down everything that's in the changeset and currently * still on. So need to check the old, saved state. */ -- cgit v1.2.3 From 25aaa3a1e5dba6256fd4a548f088ee1ebbc4b5f8 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 19 Jan 2016 09:26:48 +0100 Subject: drm: initialize default rotation value to DRM_ROTATE_0 When no console framebuffer is enabled, the default plane state is defined by plane reset function. If driver uses generic helper, then rotation property is set to zero. This is not a valid value for that enum. This patch sets default rotation value to DRM_ROTATE_0. Signed-off-by: Marek Szyprowski Link: http://patchwork.freedesktop.org/patch/msgid/1453192008-13283-1-git-send-email-m.szyprowski@samsung.com Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic_helper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/drm_atomic_helper.c') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index b9d8b5393955..f0c39840bacf 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -2532,8 +2532,10 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane) kfree(plane->state); plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL); - if (plane->state) + if (plane->state) { plane->state->plane = plane; + plane->state->rotation = BIT(DRM_ROTATE_0); + } } EXPORT_SYMBOL(drm_atomic_helper_plane_reset); -- cgit v1.2.3 From 97a8df90875f72ba3b4c3320759fd93cea743261 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 7 Jan 2016 10:59:21 +0100 Subject: drm/atomic: Do not unset crtc when an encoder is stolen 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 Reviewed-by: Gustavo Padovan Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1452160762-30487-5-git-send-email-maarten.lankhorst@linux.intel.com --- drivers/gpu/drm/drm_atomic_helper.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/gpu/drm/drm_atomic_helper.c') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 0ab7c24cd7d6..254f6d0fad7c 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; } -- cgit v1.2.3 From e87a52b38943608531ff63ea397b1ae5dd48e341 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 28 Jan 2016 15:04:58 +0100 Subject: drm/atomic: Add encoder_mask to crtc_state, v3. 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 Reviewed-by: Gustavo Padovan Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/56AA200A.6070501@linux.intel.com --- drivers/gpu/drm/drm_atomic_helper.c | 53 ++++++++++++++++++++++++++++++++++-- drivers/gpu/drm/i915/intel_display.c | 3 ++ include/drm/drm_crtc.h | 2 ++ 3 files changed, 55 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/drm_atomic_helper.c') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 254f6d0fad7c..2b430b05f35d 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 0ea5a4e08b49..e1e7cdee3bbd 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -15591,6 +15591,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 @@ -15830,6 +15831,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; -- cgit v1.2.3 From 6ab520a2a105abbd275c4814ac0d61d36b1db4fe Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Wed, 24 Feb 2016 09:37:28 +0100 Subject: drm/atomic: Clean up update_output_state. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the addition of crtc_state->connector_mask other connectors from different crtc's aren't needed any more to determine if a crtc has connectors, so only call add_affected_connectors on the target crtc. This allows a cleanup to first remove all current connectors, then add all set->connectors to the target crtc. Signed-off-by: Maarten Lankhorst Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1456303053-28806-2-git-send-email-maarten.lankhorst@linux.intel.com --- drivers/gpu/drm/drm_atomic_helper.c | 41 +++++++++++++++---------------------- 1 file changed, 17 insertions(+), 24 deletions(-) (limited to 'drivers/gpu/drm/drm_atomic_helper.c') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 2b430b05f35d..ddcfc8efe59e 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1761,28 +1761,18 @@ static int update_output_state(struct drm_atomic_state *state, struct drm_crtc_state *crtc_state; struct drm_connector *connector; struct drm_connector_state *conn_state; - int ret, i, j; + int ret, i; ret = drm_modeset_lock(&dev->mode_config.connection_mutex, state->acquire_ctx); if (ret) return ret; - /* First grab all affected connector/crtc states. */ - for (i = 0; i < set->num_connectors; i++) { - conn_state = drm_atomic_get_connector_state(state, - set->connectors[i]); - if (IS_ERR(conn_state)) - return PTR_ERR(conn_state); - } - - for_each_crtc_in_state(state, crtc, crtc_state, i) { - ret = drm_atomic_add_affected_connectors(state, crtc); - if (ret) - return ret; - } + /* First disable all connectors on the target crtc. */ + ret = drm_atomic_add_affected_connectors(state, set->crtc); + if (ret) + return ret; - /* Then recompute connector->crtc links and crtc enabling state. */ for_each_connector_in_state(state, connector, conn_state, i) { if (conn_state->crtc == set->crtc) { ret = drm_atomic_set_crtc_for_connector(conn_state, @@ -1790,16 +1780,19 @@ static int update_output_state(struct drm_atomic_state *state, if (ret) return ret; } + } - for (j = 0; j < set->num_connectors; j++) { - if (set->connectors[j] == connector) { - ret = drm_atomic_set_crtc_for_connector(conn_state, - set->crtc); - if (ret) - return ret; - break; - } - } + /* Then set all connectors from set->connectors on the target crtc */ + for (i = 0; i < set->num_connectors; i++) { + conn_state = drm_atomic_get_connector_state(state, + set->connectors[i]); + if (IS_ERR(conn_state)) + return PTR_ERR(conn_state); + + ret = drm_atomic_set_crtc_for_connector(conn_state, + set->crtc); + if (ret) + return ret; } for_each_crtc_in_state(state, crtc, crtc_state, i) { -- cgit v1.2.3 From 9459545b9c966bd221e6e3129b635a4b0a9cf6c2 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Wed, 24 Feb 2016 09:37:29 +0100 Subject: drm/atomic: Pass connector and state to update_connector_routing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Minor cleanup, connector and connector_state are always non-NULL here. Signed-off-by: Maarten Lankhorst Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1456303053-28806-3-git-send-email-maarten.lankhorst@linux.intel.com --- drivers/gpu/drm/drm_atomic_helper.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers/gpu/drm/drm_atomic_helper.c') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index ddcfc8efe59e..ed28ca1fc494 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -215,22 +215,16 @@ steal_encoder(struct drm_atomic_state *state, } static int -update_connector_routing(struct drm_atomic_state *state, int conn_idx) +update_connector_routing(struct drm_atomic_state *state, + struct drm_connector *connector, + struct drm_connector_state *connector_state) { const struct drm_connector_helper_funcs *funcs; struct drm_encoder *new_encoder; struct drm_crtc *encoder_crtc; - struct drm_connector *connector; - struct drm_connector_state *connector_state; struct drm_crtc_state *crtc_state; int idx, ret; - connector = state->connectors[conn_idx]; - connector_state = state->connector_states[conn_idx]; - - if (!connector) - return 0; - DRM_DEBUG_ATOMIC("Updating routing for [CONNECTOR:%d:%s]\n", connector->base.id, connector->name); @@ -494,7 +488,8 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, * drivers must set crtc->mode_changed themselves when connector * properties need to be updated. */ - ret = update_connector_routing(state, i); + ret = update_connector_routing(state, connector, + connector_state); if (ret) return ret; } -- cgit v1.2.3 From ff19b7867fcaa00638018e116c63aba75c4b07ba Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 3 Mar 2016 10:17:38 +0100 Subject: drm/atomic: Always call steal_encoder, v2. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's no need to have a separate function to get the crtc which is stolen, this can already be found when actually stealing the encoder. drm_for_each_connector already checks for connection_mutex, so use that macro now. Changes since v1: - Do not check for NULL crtc in connector_state, this may happen when a crtc is disabled and its encoder stolen. - Because of this, use connector->state->crtc instead of conn_state->crtc. Signed-off-by: Maarten Lankhorst Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1456996662-8704-4-git-send-email-maarten.lankhorst@linux.intel.com --- drivers/gpu/drm/drm_atomic_helper.c | 74 ++++++++++--------------------------- 1 file changed, 20 insertions(+), 54 deletions(-) (limited to 'drivers/gpu/drm/drm_atomic_helper.c') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index ed28ca1fc494..32f6f57fc66e 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -106,25 +106,6 @@ check_pending_encoder_assignment(struct drm_atomic_state *state, return true; } -static struct drm_crtc * -get_current_crtc_for_encoder(struct drm_device *dev, - struct drm_encoder *encoder) -{ - struct drm_mode_config *config = &dev->mode_config; - struct drm_connector *connector; - - WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); - - drm_for_each_connector(connector, dev) { - if (connector->state->best_encoder != encoder) - continue; - - return connector->state->crtc; - } - - return NULL; -} - static void set_best_encoder(struct drm_atomic_state *state, struct drm_connector_state *conn_state, @@ -168,38 +149,18 @@ set_best_encoder(struct drm_atomic_state *state, static int steal_encoder(struct drm_atomic_state *state, - struct drm_encoder *encoder, - struct drm_crtc *encoder_crtc) + struct drm_encoder *encoder) { - struct drm_mode_config *config = &state->dev->mode_config; struct drm_crtc_state *crtc_state; struct drm_connector *connector; struct drm_connector_state *connector_state; - /* - * We can only steal an encoder coming from a connector, which means we - * must already hold the connection_mutex. - */ - WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); - - DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d:%s], stealing it\n", - encoder->base.id, encoder->name, - encoder_crtc->base.id, encoder_crtc->name); + drm_for_each_connector(connector, state->dev) { + struct drm_crtc *encoder_crtc; - crtc_state = drm_atomic_get_crtc_state(state, encoder_crtc); - if (IS_ERR(crtc_state)) - return PTR_ERR(crtc_state); - - crtc_state->connectors_changed = true; - - list_for_each_entry(connector, &config->connector_list, head) { if (connector->state->best_encoder != encoder) continue; - DRM_DEBUG_ATOMIC("Stealing encoder from [CONNECTOR:%d:%s]\n", - connector->base.id, - connector->name); - connector_state = drm_atomic_get_connector_state(state, connector); if (IS_ERR(connector_state)) @@ -208,7 +169,18 @@ steal_encoder(struct drm_atomic_state *state, if (connector_state->best_encoder != encoder) continue; + encoder_crtc = connector->state->crtc; + + DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d:%s], stealing it\n", + encoder->base.id, encoder->name, + encoder_crtc->base.id, encoder_crtc->name); + set_best_encoder(state, connector_state, NULL); + + crtc_state = drm_atomic_get_existing_crtc_state(state, encoder_crtc); + crtc_state->connectors_changed = true; + + return 0; } return 0; @@ -221,7 +193,6 @@ update_connector_routing(struct drm_atomic_state *state, { const struct drm_connector_helper_funcs *funcs; struct drm_encoder *new_encoder; - struct drm_crtc *encoder_crtc; struct drm_crtc_state *crtc_state; int idx, ret; @@ -299,17 +270,12 @@ update_connector_routing(struct drm_atomic_state *state, return -EINVAL; } - encoder_crtc = get_current_crtc_for_encoder(state->dev, - new_encoder); - - if (encoder_crtc) { - ret = steal_encoder(state, new_encoder, encoder_crtc); - if (ret) { - DRM_DEBUG_ATOMIC("Encoder stealing failed for [CONNECTOR:%d:%s]\n", - connector->base.id, - connector->name); - return ret; - } + ret = steal_encoder(state, new_encoder); + if (ret) { + DRM_DEBUG_ATOMIC("Encoder stealing failed for [CONNECTOR:%d:%s]\n", + connector->base.id, + connector->name); + return ret; } if (WARN_ON(!connector_state->crtc)) -- cgit v1.2.3 From 40616a26d1c68e4c80e2358a02297ba492f4cc17 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 3 Mar 2016 10:17:39 +0100 Subject: drm/atomic: Handle encoder stealing from set_config better. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of failing with -EINVAL when conflicting encoders are found, the legacy set_config will disable other connectors when encoders conflict. With the previous commit this becomes a lot easier to implement. set_config only adds connectors to the state that are modified, and because of the previous commit that calls add_affected_connectors only on set->crtc it means any connector not part of the modeset can be stolen from. We disable the connector in that case, and possibly the crtc if required. Atomic modeset itself still doesn't allow encoder stealing, the results would be too unpredictable. Signed-off-by: Maarten Lankhorst Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1456996662-8704-5-git-send-email-maarten.lankhorst@linux.intel.com --- drivers/gpu/drm/drm_atomic_helper.c | 69 +++++++++++++++++++++++++++++++++++++ include/drm/drm_crtc.h | 2 ++ 2 files changed, 71 insertions(+) (limited to 'drivers/gpu/drm/drm_atomic_helper.c') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 32f6f57fc66e..9fab9860999b 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -86,6 +86,68 @@ drm_atomic_helper_plane_changed(struct drm_atomic_state *state, } } +static int disable_conflicting_connectors(struct drm_atomic_state *state) +{ + struct drm_connector_state *conn_state; + struct drm_connector *connector; + struct drm_encoder *encoder; + unsigned encoder_mask = 0; + int i, ret; + + for_each_connector_in_state(state, connector, conn_state, i) { + const struct drm_connector_helper_funcs *funcs = connector->helper_private; + struct drm_encoder *new_encoder; + + if (!conn_state->crtc) + continue; + + if (funcs->atomic_best_encoder) + new_encoder = funcs->atomic_best_encoder(connector, conn_state); + else + new_encoder = funcs->best_encoder(connector); + + if (new_encoder) + encoder_mask |= 1 << drm_encoder_index(new_encoder); + } + + drm_for_each_connector(connector, state->dev) { + struct drm_crtc_state *crtc_state; + + if (drm_atomic_get_existing_connector_state(state, connector)) + continue; + + encoder = connector->state->best_encoder; + if (!encoder || !(encoder_mask & (1 << drm_encoder_index(encoder)))) + continue; + + conn_state = drm_atomic_get_connector_state(state, connector); + if (IS_ERR(conn_state)) + return PTR_ERR(conn_state); + + DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d:%s], disabling [CONNECTOR:%d:%s]\n", + encoder->base.id, encoder->name, + conn_state->crtc->base.id, conn_state->crtc->name, + connector->base.id, connector->name); + + crtc_state = drm_atomic_get_existing_crtc_state(state, conn_state->crtc); + + ret = drm_atomic_set_crtc_for_connector(conn_state, NULL); + if (ret) + return ret; + + if (!crtc_state->connector_mask) { + ret = drm_atomic_set_mode_prop_for_crtc(crtc_state, + NULL); + if (ret < 0) + return ret; + + crtc_state->active = false; + } + } + + return 0; +} + static bool check_pending_encoder_assignment(struct drm_atomic_state *state, struct drm_encoder *new_encoder) @@ -448,6 +510,12 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, } } + if (state->legacy_set_config) { + ret = disable_conflicting_connectors(state); + if (ret) + return ret; + } + for_each_connector_in_state(state, connector, connector_state, i) { /* * This only sets crtc->mode_changed for routing changes, @@ -1796,6 +1864,7 @@ int drm_atomic_helper_set_config(struct drm_mode_set *set) if (!state) return -ENOMEM; + state->legacy_set_config = true; state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); retry: ret = __drm_atomic_helper_set_config(set, state); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 8c7fb3d0f9d0..75b28deeaf24 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1676,6 +1676,7 @@ struct drm_bridge { * @dev: parent DRM device * @allow_modeset: allow full modeset * @legacy_cursor_update: hint to enforce legacy cursor IOCTL semantics + * @legacy_set_config: Disable conflicting encoders instead of failing with -EINVAL. * @planes: pointer to array of plane pointers * @plane_states: pointer to array of plane states pointers * @crtcs: pointer to array of CRTC pointers @@ -1689,6 +1690,7 @@ struct drm_atomic_state { struct drm_device *dev; bool allow_modeset : 1; bool legacy_cursor_update : 1; + bool legacy_set_config : 1; struct drm_plane **planes; struct drm_plane_state **plane_states; struct drm_crtc **crtcs; -- cgit v1.2.3 From 8248b65df653c0a536bdd64f8a6c8bb150bfdd60 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 3 Mar 2016 10:17:40 +0100 Subject: drm/atomic: Handle encoder assignment conflicts in a separate check, v3. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current check doesn't handle the case where we don't steal an encoder, but keep it on the current connector. If we repurpose disable_conflicting_encoders to do the checking, we just have to reject the ones that conflict. Changes since v1: - Return early with empty encoder_mask, drm_for_each_connector requires connection_mutex held. Changes since v2: - Add comments for the loops. Signed-off-by: Maarten Lankhorst Testcase: kms_setmode.invalid-clone-single-crtc-stealing Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1456996662-8704-6-git-send-email-maarten.lankhorst@linux.intel.com --- drivers/gpu/drm/drm_atomic_helper.c | 77 +++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 34 deletions(-) (limited to 'drivers/gpu/drm/drm_atomic_helper.c') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 9fab9860999b..52c03cea6cf9 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -86,7 +86,8 @@ drm_atomic_helper_plane_changed(struct drm_atomic_state *state, } } -static int disable_conflicting_connectors(struct drm_atomic_state *state) +static int handle_conflicting_encoders(struct drm_atomic_state *state, + bool disable_conflicting_encoders) { struct drm_connector_state *conn_state; struct drm_connector *connector; @@ -94,6 +95,11 @@ static int disable_conflicting_connectors(struct drm_atomic_state *state) unsigned encoder_mask = 0; int i, ret; + /* + * First loop, find all newly assigned encoders from the connectors + * part of the state. If the same encoder is assigned to multiple + * connectors bail out. + */ for_each_connector_in_state(state, connector, conn_state, i) { const struct drm_connector_helper_funcs *funcs = connector->helper_private; struct drm_encoder *new_encoder; @@ -106,10 +112,33 @@ static int disable_conflicting_connectors(struct drm_atomic_state *state) else new_encoder = funcs->best_encoder(connector); - if (new_encoder) + if (new_encoder) { + if (encoder_mask & (1 << drm_encoder_index(new_encoder))) { + DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] on [CONNECTOR:%d:%s] already assigned\n", + new_encoder->base.id, new_encoder->name, + connector->base.id, connector->name); + + return -EINVAL; + } + encoder_mask |= 1 << drm_encoder_index(new_encoder); + } } + if (!encoder_mask) + return 0; + + /* + * Second loop, iterate over all connectors not part of the state. + * + * If a conflicting encoder is found and disable_conflicting_encoders + * is not set, an error is returned. Userspace can provide a solution + * through the atomic ioctl. + * + * If the flag is set conflicting connectors are removed from the crtc + * and the crtc is disabled if no encoder is left. This preserves + * compatibility with the legacy set_config behavior. + */ drm_for_each_connector(connector, state->dev) { struct drm_crtc_state *crtc_state; @@ -120,6 +149,15 @@ static int disable_conflicting_connectors(struct drm_atomic_state *state) if (!encoder || !(encoder_mask & (1 << drm_encoder_index(encoder)))) continue; + if (!disable_conflicting_encoders) { + DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d:%s] by [CONNECTOR:%d:%s]\n", + encoder->base.id, encoder->name, + connector->state->crtc->base.id, + connector->state->crtc->name, + connector->base.id, connector->name); + return -EINVAL; + } + conn_state = drm_atomic_get_connector_state(state, connector); if (IS_ERR(conn_state)) return PTR_ERR(conn_state); @@ -148,26 +186,6 @@ static int disable_conflicting_connectors(struct drm_atomic_state *state) return 0; } -static bool -check_pending_encoder_assignment(struct drm_atomic_state *state, - struct drm_encoder *new_encoder) -{ - struct drm_connector *connector; - struct drm_connector_state *conn_state; - int i; - - for_each_connector_in_state(state, connector, conn_state, i) { - if (conn_state->best_encoder != new_encoder) - continue; - - /* encoder already assigned and we're trying to re-steal it! */ - if (connector->state->best_encoder != conn_state->best_encoder) - return false; - } - - return true; -} - static void set_best_encoder(struct drm_atomic_state *state, struct drm_connector_state *conn_state, @@ -325,13 +343,6 @@ update_connector_routing(struct drm_atomic_state *state, return 0; } - if (!check_pending_encoder_assignment(state, new_encoder)) { - DRM_DEBUG_ATOMIC("Encoder for [CONNECTOR:%d:%s] already assigned\n", - connector->base.id, - connector->name); - return -EINVAL; - } - ret = steal_encoder(state, new_encoder); if (ret) { DRM_DEBUG_ATOMIC("Encoder stealing failed for [CONNECTOR:%d:%s]\n", @@ -510,11 +521,9 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, } } - if (state->legacy_set_config) { - ret = disable_conflicting_connectors(state); - if (ret) - return ret; - } + ret = handle_conflicting_encoders(state, state->legacy_set_config); + if (ret) + return ret; for_each_connector_in_state(state, connector, connector_state, i) { /* -- cgit v1.2.3 From ec5aaa586fe0d826b6e65a60756b9bbf4d93e011 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 3 Mar 2016 10:17:41 +0100 Subject: drm/atomic: Clean up steal_encoder, v2. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that only encoders can be stolen that are part of the state steal_encoder no longer needs to inspect all connectors, just those that are part of the atomic state. Changes since v1: - Change return value to void, can no longer fail. Signed-off-by: Maarten Lankhorst Reviewed-by: Ville Syrjälä [danvet: Appease gcc, reported by kbuild.] Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1456996662-8704-7-git-send-email-maarten.lankhorst@linux.intel.com --- drivers/gpu/drm/drm_atomic_helper.c | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) (limited to 'drivers/gpu/drm/drm_atomic_helper.c') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 52c03cea6cf9..baa0fc652f43 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -227,25 +227,18 @@ set_best_encoder(struct drm_atomic_state *state, conn_state->best_encoder = encoder; } -static int +static void steal_encoder(struct drm_atomic_state *state, struct drm_encoder *encoder) { struct drm_crtc_state *crtc_state; struct drm_connector *connector; struct drm_connector_state *connector_state; + int i; - drm_for_each_connector(connector, state->dev) { + for_each_connector_in_state(state, connector, connector_state, i) { struct drm_crtc *encoder_crtc; - if (connector->state->best_encoder != encoder) - continue; - - connector_state = drm_atomic_get_connector_state(state, - connector); - if (IS_ERR(connector_state)) - return PTR_ERR(connector_state); - if (connector_state->best_encoder != encoder) continue; @@ -260,10 +253,8 @@ steal_encoder(struct drm_atomic_state *state, crtc_state = drm_atomic_get_existing_crtc_state(state, encoder_crtc); crtc_state->connectors_changed = true; - return 0; + return; } - - return 0; } static int @@ -274,7 +265,7 @@ update_connector_routing(struct drm_atomic_state *state, const struct drm_connector_helper_funcs *funcs; struct drm_encoder *new_encoder; struct drm_crtc_state *crtc_state; - int idx, ret; + int idx; DRM_DEBUG_ATOMIC("Updating routing for [CONNECTOR:%d:%s]\n", connector->base.id, @@ -343,13 +334,7 @@ update_connector_routing(struct drm_atomic_state *state, return 0; } - ret = steal_encoder(state, new_encoder); - if (ret) { - DRM_DEBUG_ATOMIC("Encoder stealing failed for [CONNECTOR:%d:%s]\n", - connector->base.id, - connector->name); - return ret; - } + steal_encoder(state, new_encoder); if (WARN_ON(!connector_state->crtc)) return -EINVAL; -- cgit v1.2.3 From 9b8d1e53f6da63920ba569405b8d57ddbb42a988 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 3 Mar 2016 10:17:42 +0100 Subject: drm/atomic: Clean up update_connector_routing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit connector_state->crtc can no longer be unset by accident, so that check can be removed. The other code open-codes drm_atomic_get_existing_crtc_state, so use that. Signed-off-by: Maarten Lankhorst Reviewed-by: Ville Syrjälä [danvet: Resolve tiny conflict due to gcc warning fix on previous patch.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic_helper.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) (limited to 'drivers/gpu/drm/drm_atomic_helper.c') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index baa0fc652f43..7a034b231792 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -265,7 +265,6 @@ update_connector_routing(struct drm_atomic_state *state, const struct drm_connector_helper_funcs *funcs; struct drm_encoder *new_encoder; struct drm_crtc_state *crtc_state; - int idx; DRM_DEBUG_ATOMIC("Updating routing for [CONNECTOR:%d:%s]\n", connector->base.id, @@ -273,16 +272,12 @@ update_connector_routing(struct drm_atomic_state *state, if (connector->state->crtc != connector_state->crtc) { if (connector->state->crtc) { - idx = drm_crtc_index(connector->state->crtc); - - crtc_state = state->crtc_states[idx]; + crtc_state = drm_atomic_get_existing_crtc_state(state, connector->state->crtc); crtc_state->connectors_changed = true; } if (connector_state->crtc) { - idx = drm_crtc_index(connector_state->crtc); - - crtc_state = state->crtc_states[idx]; + crtc_state = drm_atomic_get_existing_crtc_state(state, connector_state->crtc); crtc_state->connectors_changed = true; } } @@ -336,14 +331,9 @@ update_connector_routing(struct drm_atomic_state *state, steal_encoder(state, new_encoder); - if (WARN_ON(!connector_state->crtc)) - return -EINVAL; - set_best_encoder(state, connector_state, new_encoder); - idx = drm_crtc_index(connector_state->crtc); - - crtc_state = state->crtc_states[idx]; + crtc_state = drm_atomic_get_existing_crtc_state(state, connector_state->crtc); crtc_state->connectors_changed = true; DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d:%s]\n", -- cgit v1.2.3 From 5488dc16fde74595a40c5d20ae52d978313f0b4e Mon Sep 17 00:00:00 2001 From: Lionel Landwerlin Date: Fri, 26 Feb 2016 17:05:00 +0000 Subject: drm: introduce pipe color correction properties Patch based on a previous series by Shashank Sharma. This introduces optional properties to enable color correction at the pipe level. It relies on 3 transformations applied to every pixels displayed. First a lookup into a degamma table, then a multiplication of the rgb components by a 3x3 matrix and finally another lookup into a gamma table. The following properties can be added to a pipe : - DEGAMMA_LUT : blob containing degamma LUT - DEGAMMA_LUT_SIZE : number of elements in DEGAMMA_LUT - CTM : transformation matrix applied after the degamma LUT - GAMMA_LUT : blob containing gamma LUT - GAMMA_LUT_SIZE : number of elements in GAMMA_LUT DEGAMMA_LUT_SIZE and GAMMA_LUT_SIZE are read only properties, set by the driver to tell userspace applications what sizes should be the lookup tables in DEGAMMA_LUT and GAMMA_LUT. A helper is also provided so legacy gamma correction is redirected through these new properties. v2: Register LUT size properties as range v3: Fix round in drm_color_lut_get_value() helper More docs on how degamma/gamma properties are used v4: Update contributors v5: Rename CTM_MATRIX property to CTM (Doh!) Add legacy gamma_set atomic helper Describe CTM/LUT acronyms in the kernel doc v6: Fix missing blob unref in drm_atomic_helper_crtc_reset Signed-off-by: Shashank Sharma Signed-off-by: Kumar, Kiran S Signed-off-by: Kausal Malladi Signed-off-by: Lionel Landwerlin Reviewed-by: Matt Roper Acked-by: Rob Bradford [danvet: CrOS maintainers are also happy with the userspacde side: https://codereview.chromium.org/1182063002/ ] Reviewed-by: Daniel Stone Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1456506302-640-4-git-send-email-lionel.g.landwerlin@intel.com --- Documentation/DocBook/gpu.tmpl | 59 ++++++++++++++++++- drivers/gpu/drm/drm_atomic.c | 88 ++++++++++++++++++++++++++++- drivers/gpu/drm/drm_atomic_helper.c | 110 +++++++++++++++++++++++++++++++++++- drivers/gpu/drm/drm_crtc.c | 35 ++++++++++++ drivers/gpu/drm/drm_crtc_helper.c | 33 +++++++++++ include/drm/drm_atomic_helper.h | 3 + include/drm/drm_crtc.h | 46 ++++++++++++++- include/drm/drm_crtc_helper.h | 3 + include/uapi/drm/drm_mode.h | 15 +++++ 9 files changed, 386 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/drm_atomic_helper.c') diff --git a/Documentation/DocBook/gpu.tmpl b/Documentation/DocBook/gpu.tmpl index fe6b36a2fd98..1692c4dd5487 100644 --- a/Documentation/DocBook/gpu.tmpl +++ b/Documentation/DocBook/gpu.tmpl @@ -1816,7 +1816,7 @@ void intel_crt_init(struct drm_device *dev) Description/Restrictions - DRM + DRM Generic “rotation” BITMASK @@ -2068,7 +2068,7 @@ void intel_crt_init(struct drm_device *dev) property to suggest an Y offset for a connector - Optional + Optional “scaling mode” ENUM { "None", "Full", "Center", "Full aspect" } @@ -2092,6 +2092,61 @@ void intel_crt_init(struct drm_device *dev) TBD + “DEGAMMA_LUT” + BLOB + 0 + CRTC + DRM property to set the degamma lookup table + (LUT) mapping pixel data from the framebuffer before it is + given to the transformation matrix. The data is an interpreted + as an array of struct drm_color_lut elements. Hardware might + choose not to use the full precision of the LUT elements nor + use all the elements of the LUT (for example the hardware + might choose to interpolate between LUT[0] and LUT[4]). + + + “DEGAMMA_LUT_SIZE” + RANGE | IMMUTABLE + Min=0, Max=UINT_MAX + CRTC + DRM property to gives the size of the lookup + table to be set on the DEGAMMA_LUT property (the size depends + on the underlying hardware). + + + “CTM” + BLOB + 0 + CRTC + DRM property to set the current + transformation matrix (CTM) apply to pixel data after the + lookup through the degamma LUT and before the lookup through + the gamma LUT. The data is an interpreted as a struct + drm_color_ctm. + + + “GAMMA_LUT” + BLOB + 0 + CRTC + DRM property to set the gamma lookup table + (LUT) mapping pixel data after to the transformation matrix to + data sent to the connector. The data is an interpreted as an + array of struct drm_color_lut elements. Hardware might choose + not to use the full precision of the LUT elements nor use all + the elements of the LUT (for example the hardware might choose + to interpolate between LUT[0] and LUT[4]). + + + “GAMMA_LUT_SIZE” + RANGE | IMMUTABLE + Min=0, Max=UINT_MAX + CRTC + DRM property to gives the size of the lookup + table to be set on the GAMMA_LUT property (the size depends on + the underlying hardware). + + i915 Generic "Broadcast RGB" diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 8fb469c4e4b8..c7e7a955bb1e 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -28,6 +28,7 @@ #include #include +#include #include /** @@ -387,6 +388,59 @@ int drm_atomic_set_mode_prop_for_crtc(struct drm_crtc_state *state, } EXPORT_SYMBOL(drm_atomic_set_mode_prop_for_crtc); +/** + * drm_atomic_replace_property_blob - replace a blob property + * @blob: a pointer to the member blob to be replaced + * @new_blob: the new blob to replace with + * @expected_size: the expected size of the new blob + * @replaced: whether the blob has been replaced + * + * RETURNS: + * Zero on success, error code on failure + */ +static void +drm_atomic_replace_property_blob(struct drm_property_blob **blob, + struct drm_property_blob *new_blob, + bool *replaced) +{ + struct drm_property_blob *old_blob = *blob; + + if (old_blob == new_blob) + return; + + if (old_blob) + drm_property_unreference_blob(old_blob); + if (new_blob) + drm_property_reference_blob(new_blob); + *blob = new_blob; + *replaced = true; + + return; +} + +static int +drm_atomic_replace_property_blob_from_id(struct drm_crtc *crtc, + struct drm_property_blob **blob, + uint64_t blob_id, + ssize_t expected_size, + bool *replaced) +{ + struct drm_device *dev = crtc->dev; + struct drm_property_blob *new_blob = NULL; + + if (blob_id != 0) { + new_blob = drm_property_lookup_blob(dev, blob_id); + if (new_blob == NULL) + return -EINVAL; + if (expected_size > 0 && expected_size != new_blob->length) + return -EINVAL; + } + + drm_atomic_replace_property_blob(blob, new_blob, replaced); + + return 0; +} + /** * drm_atomic_crtc_set_property - set property on CRTC * @crtc: the drm CRTC to set a property on @@ -409,6 +463,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; struct drm_mode_config *config = &dev->mode_config; + bool replaced = false; int ret; if (property == config->prop_active) @@ -419,8 +474,31 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc, ret = drm_atomic_set_mode_prop_for_crtc(state, mode); drm_property_unreference_blob(mode); return ret; - } - else if (crtc->funcs->atomic_set_property) + } else if (property == config->degamma_lut_property) { + ret = drm_atomic_replace_property_blob_from_id(crtc, + &state->degamma_lut, + val, + -1, + &replaced); + state->color_mgmt_changed = replaced; + return ret; + } else if (property == config->ctm_property) { + ret = drm_atomic_replace_property_blob_from_id(crtc, + &state->ctm, + val, + sizeof(struct drm_color_ctm), + &replaced); + state->color_mgmt_changed = replaced; + return ret; + } else if (property == config->gamma_lut_property) { + ret = drm_atomic_replace_property_blob_from_id(crtc, + &state->gamma_lut, + val, + -1, + &replaced); + state->color_mgmt_changed = replaced; + return ret; + } else if (crtc->funcs->atomic_set_property) return crtc->funcs->atomic_set_property(crtc, state, property, val); else return -EINVAL; @@ -456,6 +534,12 @@ drm_atomic_crtc_get_property(struct drm_crtc *crtc, *val = state->active; else if (property == config->prop_mode_id) *val = (state->mode_blob) ? state->mode_blob->base.id : 0; + else if (property == config->degamma_lut_property) + *val = (state->degamma_lut) ? state->degamma_lut->base.id : 0; + else if (property == config->ctm_property) + *val = (state->ctm) ? state->ctm->base.id : 0; + else if (property == config->gamma_lut_property) + *val = (state->gamma_lut) ? state->gamma_lut->base.id : 0; else if (crtc->funcs->atomic_get_property) return crtc->funcs->atomic_get_property(crtc, state, property, val); else diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 7a034b231792..3e49a0993191 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -2495,8 +2495,12 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_dpms); */ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) { - if (crtc->state) + if (crtc->state) { drm_property_unreference_blob(crtc->state->mode_blob); + drm_property_unreference_blob(crtc->state->degamma_lut); + drm_property_unreference_blob(crtc->state->ctm); + drm_property_unreference_blob(crtc->state->gamma_lut); + } kfree(crtc->state); crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL); @@ -2520,10 +2524,17 @@ void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, if (state->mode_blob) drm_property_reference_blob(state->mode_blob); + if (state->degamma_lut) + drm_property_reference_blob(state->degamma_lut); + if (state->ctm) + drm_property_reference_blob(state->ctm); + if (state->gamma_lut) + drm_property_reference_blob(state->gamma_lut); state->mode_changed = false; state->active_changed = false; state->planes_changed = false; state->connectors_changed = false; + state->color_mgmt_changed = false; state->event = NULL; } EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state); @@ -2564,6 +2575,9 @@ void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state) { drm_property_unreference_blob(state->mode_blob); + drm_property_unreference_blob(state->degamma_lut); + drm_property_unreference_blob(state->ctm); + drm_property_unreference_blob(state->gamma_lut); } EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state); @@ -2877,3 +2891,97 @@ void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, kfree(state); } EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state); + +/** + * drm_atomic_helper_legacy_gamma_set - set the legacy gamma correction table + * @crtc: CRTC object + * @red: red correction table + * @green: green correction table + * @blue: green correction table + * @start: + * @size: size of the tables + * + * Implements support for legacy gamma correction table for drivers + * that support color management through the DEGAMMA_LUT/GAMMA_LUT + * properties. + */ +void drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, + u16 *red, u16 *green, u16 *blue, + uint32_t start, uint32_t size) +{ + struct drm_device *dev = crtc->dev; + struct drm_mode_config *config = &dev->mode_config; + struct drm_atomic_state *state; + struct drm_crtc_state *crtc_state; + struct drm_property_blob *blob = NULL; + struct drm_color_lut *blob_data; + int i, ret = 0; + + state = drm_atomic_state_alloc(crtc->dev); + if (!state) + return; + + blob = drm_property_create_blob(dev, + sizeof(struct drm_color_lut) * size, + NULL); + if (!blob) { + ret = -ENOMEM; + goto fail; + } + + /* Prepare GAMMA_LUT with the legacy values. */ + blob_data = (struct drm_color_lut *) blob->data; + for (i = 0; i < size; i++) { + blob_data[i].red = red[i]; + blob_data[i].green = green[i]; + blob_data[i].blue = blue[i]; + } + + state->acquire_ctx = crtc->dev->mode_config.acquire_ctx; +retry: + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + goto fail; + } + + /* Reset DEGAMMA_LUT and CTM properties. */ + ret = drm_atomic_crtc_set_property(crtc, crtc_state, + config->degamma_lut_property, 0); + if (ret) + goto fail; + + ret = drm_atomic_crtc_set_property(crtc, crtc_state, + config->ctm_property, 0); + if (ret) + goto fail; + + ret = drm_atomic_crtc_set_property(crtc, crtc_state, + config->gamma_lut_property, blob->base.id); + if (ret) + goto fail; + + ret = drm_atomic_commit(state); + if (ret) + goto fail; + + /* Driver takes ownership of state on successful commit. */ + + drm_property_unreference_blob(blob); + + return; +fail: + if (ret == -EDEADLK) + goto backoff; + + drm_atomic_state_free(state); + drm_property_unreference_blob(blob); + + return; +backoff: + drm_atomic_state_clear(state); + drm_atomic_legacy_backoff(state); + + goto retry; +} +EXPORT_SYMBOL(drm_atomic_helper_legacy_gamma_set); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 579505c0a498..49781a9f8a94 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1563,6 +1563,41 @@ static int drm_mode_create_standard_properties(struct drm_device *dev) return -ENOMEM; dev->mode_config.prop_mode_id = prop; + prop = drm_property_create(dev, + DRM_MODE_PROP_BLOB, + "DEGAMMA_LUT", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.degamma_lut_property = prop; + + prop = drm_property_create_range(dev, + DRM_MODE_PROP_IMMUTABLE, + "DEGAMMA_LUT_SIZE", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.degamma_lut_size_property = prop; + + prop = drm_property_create(dev, + DRM_MODE_PROP_BLOB, + "CTM", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.ctm_property = prop; + + prop = drm_property_create(dev, + DRM_MODE_PROP_BLOB, + "GAMMA_LUT", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.gamma_lut_property = prop; + + prop = drm_property_create_range(dev, + DRM_MODE_PROP_IMMUTABLE, + "GAMMA_LUT_SIZE", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.gamma_lut_size_property = prop; + return 0; } diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 7539eea4ccbc..79555d2b1b87 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -1075,3 +1075,36 @@ int drm_helper_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, return drm_plane_helper_commit(plane, plane_state, old_fb); } EXPORT_SYMBOL(drm_helper_crtc_mode_set_base); + +/** + * drm_helper_crtc_enable_color_mgmt - enable color management properties + * @crtc: DRM CRTC + * @degamma_lut_size: the size of the degamma lut (before CSC) + * @gamma_lut_size: the size of the gamma lut (after CSC) + * + * This function lets the driver enable the color correction properties on a + * CRTC. This includes 3 degamma, csc and gamma properties that userspace can + * set and 2 size properties to inform the userspace of the lut sizes. + */ +void drm_helper_crtc_enable_color_mgmt(struct drm_crtc *crtc, + int degamma_lut_size, + int gamma_lut_size) +{ + struct drm_device *dev = crtc->dev; + struct drm_mode_config *config = &dev->mode_config; + + drm_object_attach_property(&crtc->base, + config->degamma_lut_property, 0); + drm_object_attach_property(&crtc->base, + config->ctm_property, 0); + drm_object_attach_property(&crtc->base, + config->gamma_lut_property, 0); + + drm_object_attach_property(&crtc->base, + config->degamma_lut_size_property, + degamma_lut_size); + drm_object_attach_property(&crtc->base, + config->gamma_lut_size_property, + gamma_lut_size); +} +EXPORT_SYMBOL(drm_helper_crtc_enable_color_mgmt); diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index fe5efada9d68..9054598c9a7a 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -146,6 +146,9 @@ __drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, struct drm_connector_state *state); void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, struct drm_connector_state *state); +void drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, + u16 *red, u16 *green, u16 *blue, + uint32_t start, uint32_t size); /** * drm_atomic_crtc_for_each_plane - iterate over planes currently attached to CRTC diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 75b28deeaf24..b10eba23a744 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -305,6 +305,8 @@ struct drm_plane_helper_funcs; * @mode_changed: crtc_state->mode or crtc_state->enable has been changed * @active_changed: crtc_state->active has been toggled. * @connectors_changed: connectors to this crtc have been updated + * @color_mgmt_changed: color management properties have changed (degamma or + * gamma LUT or CSC matrix) * @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 @@ -312,6 +314,11 @@ struct drm_plane_helper_funcs; * update to ensure framebuffer cleanup isn't done too early * @adjusted_mode: for use by helpers and drivers to compute adjusted mode timings * @mode: current mode timings + * @degamma_lut: Lookup table for converting framebuffer pixel data + * before apply the conversion matrix + * @ctm: Transformation matrix + * @gamma_lut: Lookup table for converting pixel data after the + * conversion matrix * @event: optional pointer to a DRM event to signal upon completion of the * state update * @state: backpointer to global drm_atomic_state @@ -333,6 +340,7 @@ struct drm_crtc_state { bool mode_changed : 1; bool active_changed : 1; bool connectors_changed : 1; + bool color_mgmt_changed : 1; /* attached planes bitmask: * WARNING: transitional helpers do not maintain plane_mask so @@ -355,6 +363,11 @@ struct drm_crtc_state { /* blob property to expose current mode to atomic userspace */ struct drm_property_blob *mode_blob; + /* blob property to expose color management to userspace */ + struct drm_property_blob *degamma_lut; + struct drm_property_blob *ctm; + struct drm_property_blob *gamma_lut; + struct drm_pending_vblank_event *event; struct drm_atomic_state *state; @@ -757,7 +770,7 @@ struct drm_crtc { int x, y; const struct drm_crtc_funcs *funcs; - /* CRTC gamma size for reporting to userspace */ + /* Legacy FB CRTC gamma size for reporting to userspace */ uint32_t gamma_size; uint16_t *gamma_store; @@ -2027,6 +2040,15 @@ struct drm_mode_config_funcs { * @property_blob_list: list of all the blob property objects * @blob_lock: mutex for blob property allocation and management * @*_property: core property tracking + * @degamma_lut_property: LUT used to convert the framebuffer's colors to linear + * gamma + * @degamma_lut_size_property: size of the degamma LUT as supported by the + * driver (read-only) + * @ctm_property: Matrix used to convert colors after the lookup in the + * degamma LUT + * @gamma_lut_property: LUT used to convert the colors, after the CSC matrix, to + * the gamma space of the connected screen (read-only) + * @gamma_lut_size_property: size of the gamma LUT as supported by the driver * @preferred_depth: preferred RBG pixel depth, used by fb helpers * @prefer_shadow: hint to userspace to prefer shadow-fb rendering * @async_page_flip: does this device support async flips on the primary plane? @@ -2128,6 +2150,13 @@ struct drm_mode_config { struct drm_property *aspect_ratio_property; struct drm_property *dirty_info_property; + /* Optional color correction properties */ + struct drm_property *degamma_lut_property; + struct drm_property *degamma_lut_size_property; + struct drm_property *ctm_property; + struct drm_property *gamma_lut_property; + struct drm_property *gamma_lut_size_property; + /* properties for virtual machine layout */ struct drm_property *suggested_x_property; struct drm_property *suggested_y_property; @@ -2550,6 +2579,21 @@ static inline struct drm_property *drm_property_find(struct drm_device *dev, return mo ? obj_to_property(mo) : NULL; } +/* + * Extract a degamma/gamma LUT value provided by user and round it to the + * precision supported by the hardware. + */ +static inline uint32_t drm_color_lut_extract(uint32_t user_input, + uint32_t bit_precision) +{ + uint32_t val = user_input + (1 << (16 - bit_precision - 1)); + uint32_t max = 0xffff >> (16 - bit_precision); + + val >>= 16 - bit_precision; + + return clamp_val(val, 0, max); +} + /* Plane list iterator for legacy (overlay only) planes. */ #define drm_for_each_legacy_plane(plane, dev) \ list_for_each_entry(plane, &(dev)->mode_config.plane_list, head) \ diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index 4b37afa2b73b..97fa894d4ee2 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -48,6 +48,9 @@ extern bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, int x, int y, struct drm_framebuffer *old_fb); +extern void drm_helper_crtc_enable_color_mgmt(struct drm_crtc *crtc, + int degamma_lut_size, + int gamma_lut_size); extern bool drm_helper_crtc_in_use(struct drm_crtc *crtc); extern bool drm_helper_encoder_in_use(struct drm_encoder *encoder); diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 50adb46204c2..c0217434d28d 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -487,6 +487,21 @@ struct drm_mode_crtc_lut { __u64 blue; }; +struct drm_color_ctm { + /* Conversion matrix in S31.32 format. */ + __s64 matrix[9]; +}; + +struct drm_color_lut { + /* + * Data is U0.16 fixed point format. + */ + __u16 red; + __u16 green; + __u16 blue; + __u16 reserved; +}; + #define DRM_MODE_PAGE_FLIP_EVENT 0x01 #define DRM_MODE_PAGE_FLIP_ASYNC 0x02 #define DRM_MODE_PAGE_FLIP_FLAGS (DRM_MODE_PAGE_FLIP_EVENT|DRM_MODE_PAGE_FLIP_ASYNC) -- cgit v1.2.3 From 562c5b4d898613bec97f482098e7507f52c914cb Mon Sep 17 00:00:00 2001 From: Lionel Landwerlin Date: Thu, 10 Mar 2016 12:04:21 +0000 Subject: drm: fix blob pointer check Check properly that the allocated blob's pointer is valid. Signed-off-by: Lionel Landwerlin Reported-by: Dan Carpenter Cc: Daniel Stone Cc: Daniel Vetter Cc: Matt Roper Cc: dri-devel@lists.freedesktop.org Reviewed-by: Daniel Stone Fixes: 5488dc16fde7 ("drm: introduce pipe color correction properties") Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1457611461-9116-1-git-send-email-lionel.g.landwerlin@intel.com --- drivers/gpu/drm/drm_atomic_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/drm_atomic_helper.c') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 3e49a0993191..67261ca03fbf 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -2924,8 +2924,8 @@ void drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, blob = drm_property_create_blob(dev, sizeof(struct drm_color_lut) * size, NULL); - if (!blob) { - ret = -ENOMEM; + if (IS_ERR(blob)) { + ret = PTR_ERR(blob); goto fail; } -- cgit v1.2.3 From c1f415c9acb5877c408d9311eb837d7d50636a68 Mon Sep 17 00:00:00 2001 From: Lionel Landwerlin Date: Fri, 11 Mar 2016 12:17:26 +0000 Subject: drm: atomic helper: do not unreference error pointer 562c5b4d8986 didn't quite fix the issue of dealing with an error pointer. We can't free/unref an error pointer so reset it to NULL. Many thanks to Dan Carpenter for pointing this out again. Signed-off-by: Lionel Landwerlin Cc: Dan Carpenter Cc: Daniel Stone Cc: Daniel Vetter Cc: Matt Roper Cc: dri-devel@lists.freedesktop.org Fixes: 562c5b4d8986 ("drm: fix blob pointer check") Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1457698646-22231-1-git-send-email-lionel.g.landwerlin@intel.com --- drivers/gpu/drm/drm_atomic_helper.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpu/drm/drm_atomic_helper.c') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 67261ca03fbf..e93bb0bbdd82 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -2926,6 +2926,7 @@ void drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, NULL); if (IS_ERR(blob)) { ret = PTR_ERR(blob); + blob = NULL; goto fail; } -- cgit v1.2.3 From 2943ef32c97666a6556c98496722c4b72420ad37 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Tue, 15 Mar 2016 13:46:00 +0100 Subject: drm/atomic: use helper to get crtc state DRM core provide helper to access crtc state. Signed-off-by: Andrzej Hajda Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1458045960-25193-1-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/drm_atomic_helper.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/drm_atomic_helper.c') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index e93bb0bbdd82..91351111d0f9 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -67,7 +67,8 @@ drm_atomic_helper_plane_changed(struct drm_atomic_state *state, struct drm_crtc_state *crtc_state; if (plane->state->crtc) { - crtc_state = state->crtc_states[drm_crtc_index(plane->state->crtc)]; + crtc_state = drm_atomic_get_existing_crtc_state(state, + plane->state->crtc); if (WARN_ON(!crtc_state)) return; @@ -76,8 +77,8 @@ drm_atomic_helper_plane_changed(struct drm_atomic_state *state, } if (plane_state->crtc) { - crtc_state = - state->crtc_states[drm_crtc_index(plane_state->crtc)]; + crtc_state = drm_atomic_get_existing_crtc_state(state, + plane_state->crtc); if (WARN_ON(!crtc_state)) return; @@ -374,8 +375,8 @@ mode_fixup(struct drm_atomic_state *state) if (!conn_state->crtc || !conn_state->best_encoder) continue; - crtc_state = - state->crtc_states[drm_crtc_index(conn_state->crtc)]; + crtc_state = drm_atomic_get_existing_crtc_state(state, + conn_state->crtc); /* * Each encoder has at most one connector (since we always steal @@ -679,7 +680,8 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) if (!old_conn_state->crtc) continue; - old_crtc_state = old_state->crtc_states[drm_crtc_index(old_conn_state->crtc)]; + old_crtc_state = drm_atomic_get_existing_crtc_state(old_state, + old_conn_state->crtc); if (!old_crtc_state->active || !drm_atomic_crtc_needs_modeset(old_conn_state->crtc->state)) -- cgit v1.2.3