From 382d2af64e7d7083eefcc8c7e6c780634174ce87 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 29 May 2019 14:02:03 +0300 Subject: drm/edid: Clean up DRM_EDID_DIGITAL_* flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Give the "DFP 1.x" bit a proper name, and clean up the rest of the bits defines as well. Cc: Mario Kleiner Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20190529110204.2384-1-ville.syrjala@linux.intel.com Reviewed-by: Alex Deucher Reviewed-by: Mario Kleiner --- include/drm/drm_edid.h | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) (limited to 'include/drm') diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 0e21e91c4314..88b63801f9db 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -177,21 +177,23 @@ struct detailed_timing { #define DRM_EDID_INPUT_BLANK_TO_BLACK (1 << 4) #define DRM_EDID_INPUT_VIDEO_LEVEL (3 << 5) #define DRM_EDID_INPUT_DIGITAL (1 << 7) -#define DRM_EDID_DIGITAL_DEPTH_MASK (7 << 4) -#define DRM_EDID_DIGITAL_DEPTH_UNDEF (0 << 4) -#define DRM_EDID_DIGITAL_DEPTH_6 (1 << 4) -#define DRM_EDID_DIGITAL_DEPTH_8 (2 << 4) -#define DRM_EDID_DIGITAL_DEPTH_10 (3 << 4) -#define DRM_EDID_DIGITAL_DEPTH_12 (4 << 4) -#define DRM_EDID_DIGITAL_DEPTH_14 (5 << 4) -#define DRM_EDID_DIGITAL_DEPTH_16 (6 << 4) -#define DRM_EDID_DIGITAL_DEPTH_RSVD (7 << 4) -#define DRM_EDID_DIGITAL_TYPE_UNDEF (0) -#define DRM_EDID_DIGITAL_TYPE_DVI (1) -#define DRM_EDID_DIGITAL_TYPE_HDMI_A (2) -#define DRM_EDID_DIGITAL_TYPE_HDMI_B (3) -#define DRM_EDID_DIGITAL_TYPE_MDDI (4) -#define DRM_EDID_DIGITAL_TYPE_DP (5) +#define DRM_EDID_DIGITAL_DEPTH_MASK (7 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_UNDEF (0 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_6 (1 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_8 (2 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_10 (3 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_12 (4 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_14 (5 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_16 (6 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_RSVD (7 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_MASK (7 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_UNDEF (0 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_DVI (1 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_HDMI_A (2 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_HDMI_B (3 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_MDDI (4 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_DP (5 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_DFP_1_X (1 << 0) /* 1.3 */ #define DRM_EDID_FEATURE_DEFAULT_GTF (1 << 0) #define DRM_EDID_FEATURE_PREFERRED_TIMING (1 << 1) -- cgit v1.2.3 From df73789514554761ebdd87f2426938696a2442a2 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Fri, 31 May 2019 16:01:10 +0200 Subject: drm/atomic: Move __drm_atomic_helper_disable_plane/set_config() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare for moving drm_fb_helper modesetting code to drm_client. drm_client will be linked to drm.ko, so move __drm_atomic_helper_disable_plane() and __drm_atomic_helper_set_config() out of drm_kms_helper.ko. While at it, fix two checkpatch complaints: - WARNING: Block comments use a trailing */ on a separate line - CHECK: Alignment should match open parenthesis v7: Declare drm_mode_set and drm_plane_state Signed-off-by: Noralf Trønnes Reviewed-by: Daniel Vetter Reviewed-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20190531140117.37751-2-noralf@tronnes.org --- drivers/gpu/drm/drm_atomic.c | 168 ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_atomic_helper.c | 164 ----------------------------------- drivers/gpu/drm/drm_crtc_internal.h | 7 ++ include/drm/drm_atomic_helper.h | 4 - 4 files changed, 175 insertions(+), 168 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index b640e77184e5..169f06d7c5ce 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1179,6 +1179,174 @@ int drm_atomic_nonblocking_commit(struct drm_atomic_state *state) } EXPORT_SYMBOL(drm_atomic_nonblocking_commit); +/* just used from drm-client and atomic-helper: */ +int __drm_atomic_helper_disable_plane(struct drm_plane *plane, + struct drm_plane_state *plane_state) +{ + int ret; + + ret = drm_atomic_set_crtc_for_plane(plane_state, NULL); + if (ret != 0) + return ret; + + drm_atomic_set_fb_for_plane(plane_state, NULL); + plane_state->crtc_x = 0; + plane_state->crtc_y = 0; + plane_state->crtc_w = 0; + plane_state->crtc_h = 0; + plane_state->src_x = 0; + plane_state->src_y = 0; + plane_state->src_w = 0; + plane_state->src_h = 0; + + return 0; +} +EXPORT_SYMBOL(__drm_atomic_helper_disable_plane); + +static int update_output_state(struct drm_atomic_state *state, + struct drm_mode_set *set) +{ + struct drm_device *dev = set->crtc->dev; + struct drm_crtc *crtc; + struct drm_crtc_state *new_crtc_state; + struct drm_connector *connector; + struct drm_connector_state *new_conn_state; + int ret, i; + + ret = drm_modeset_lock(&dev->mode_config.connection_mutex, + state->acquire_ctx); + 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; + + for_each_new_connector_in_state(state, connector, new_conn_state, i) { + if (new_conn_state->crtc == set->crtc) { + ret = drm_atomic_set_crtc_for_connector(new_conn_state, + NULL); + if (ret) + return ret; + + /* Make sure legacy setCrtc always re-trains */ + new_conn_state->link_status = DRM_LINK_STATUS_GOOD; + } + } + + /* Then set all connectors from set->connectors on the target crtc */ + for (i = 0; i < set->num_connectors; i++) { + new_conn_state = drm_atomic_get_connector_state(state, + set->connectors[i]); + if (IS_ERR(new_conn_state)) + return PTR_ERR(new_conn_state); + + ret = drm_atomic_set_crtc_for_connector(new_conn_state, + set->crtc); + if (ret) + return ret; + } + + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { + /* + * Don't update ->enable for the CRTC in the set_config request, + * since a mismatch would indicate a bug in the upper layers. + * The actual modeset code later on will catch any + * inconsistencies here. + */ + if (crtc == set->crtc) + continue; + + if (!new_crtc_state->connector_mask) { + ret = drm_atomic_set_mode_prop_for_crtc(new_crtc_state, + NULL); + if (ret < 0) + return ret; + + new_crtc_state->active = false; + } + } + + return 0; +} + +/* just used from drm-client and atomic-helper: */ +int __drm_atomic_helper_set_config(struct drm_mode_set *set, + struct drm_atomic_state *state) +{ + struct drm_crtc_state *crtc_state; + struct drm_plane_state *primary_state; + struct drm_crtc *crtc = set->crtc; + int hdisplay, vdisplay; + int ret; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + primary_state = drm_atomic_get_plane_state(state, crtc->primary); + if (IS_ERR(primary_state)) + return PTR_ERR(primary_state); + + if (!set->mode) { + WARN_ON(set->fb); + WARN_ON(set->num_connectors); + + ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL); + if (ret != 0) + return ret; + + crtc_state->active = false; + + ret = drm_atomic_set_crtc_for_plane(primary_state, NULL); + if (ret != 0) + return ret; + + drm_atomic_set_fb_for_plane(primary_state, NULL); + + goto commit; + } + + WARN_ON(!set->fb); + WARN_ON(!set->num_connectors); + + ret = drm_atomic_set_mode_for_crtc(crtc_state, set->mode); + if (ret != 0) + return ret; + + crtc_state->active = true; + + ret = drm_atomic_set_crtc_for_plane(primary_state, crtc); + if (ret != 0) + return ret; + + drm_mode_get_hv_timing(set->mode, &hdisplay, &vdisplay); + + drm_atomic_set_fb_for_plane(primary_state, set->fb); + primary_state->crtc_x = 0; + primary_state->crtc_y = 0; + primary_state->crtc_w = hdisplay; + primary_state->crtc_h = vdisplay; + primary_state->src_x = set->x << 16; + primary_state->src_y = set->y << 16; + if (drm_rotation_90_or_270(primary_state->rotation)) { + primary_state->src_w = vdisplay << 16; + primary_state->src_h = hdisplay << 16; + } else { + primary_state->src_w = hdisplay << 16; + primary_state->src_h = vdisplay << 16; + } + +commit: + ret = update_output_state(state, set); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(__drm_atomic_helper_set_config); + void drm_atomic_print_state(const struct drm_atomic_state *state) { struct drm_printer p = drm_info_printer(state->dev->dev); diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index acf993cb8e52..0fc63d682245 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -2844,95 +2844,6 @@ fail: } EXPORT_SYMBOL(drm_atomic_helper_disable_plane); -/* just used from fb-helper and atomic-helper: */ -int __drm_atomic_helper_disable_plane(struct drm_plane *plane, - struct drm_plane_state *plane_state) -{ - int ret; - - ret = drm_atomic_set_crtc_for_plane(plane_state, NULL); - if (ret != 0) - return ret; - - drm_atomic_set_fb_for_plane(plane_state, NULL); - plane_state->crtc_x = 0; - plane_state->crtc_y = 0; - plane_state->crtc_w = 0; - plane_state->crtc_h = 0; - plane_state->src_x = 0; - plane_state->src_y = 0; - plane_state->src_w = 0; - plane_state->src_h = 0; - - return 0; -} - -static int update_output_state(struct drm_atomic_state *state, - struct drm_mode_set *set) -{ - struct drm_device *dev = set->crtc->dev; - struct drm_crtc *crtc; - struct drm_crtc_state *new_crtc_state; - struct drm_connector *connector; - struct drm_connector_state *new_conn_state; - int ret, i; - - ret = drm_modeset_lock(&dev->mode_config.connection_mutex, - state->acquire_ctx); - 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; - - for_each_new_connector_in_state(state, connector, new_conn_state, i) { - if (new_conn_state->crtc == set->crtc) { - ret = drm_atomic_set_crtc_for_connector(new_conn_state, - NULL); - if (ret) - return ret; - - /* Make sure legacy setCrtc always re-trains */ - new_conn_state->link_status = DRM_LINK_STATUS_GOOD; - } - } - - /* Then set all connectors from set->connectors on the target crtc */ - for (i = 0; i < set->num_connectors; i++) { - new_conn_state = drm_atomic_get_connector_state(state, - set->connectors[i]); - if (IS_ERR(new_conn_state)) - return PTR_ERR(new_conn_state); - - ret = drm_atomic_set_crtc_for_connector(new_conn_state, - set->crtc); - if (ret) - return ret; - } - - for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { - /* Don't update ->enable for the CRTC in the set_config request, - * since a mismatch would indicate a bug in the upper layers. - * The actual modeset code later on will catch any - * inconsistencies here. */ - if (crtc == set->crtc) - continue; - - if (!new_crtc_state->connector_mask) { - ret = drm_atomic_set_mode_prop_for_crtc(new_crtc_state, - NULL); - if (ret < 0) - return ret; - - new_crtc_state->active = false; - } - } - - return 0; -} - /** * drm_atomic_helper_set_config - set a new config from userspace * @set: mode set configuration @@ -2977,81 +2888,6 @@ fail: } EXPORT_SYMBOL(drm_atomic_helper_set_config); -/* just used from fb-helper and atomic-helper: */ -int __drm_atomic_helper_set_config(struct drm_mode_set *set, - struct drm_atomic_state *state) -{ - struct drm_crtc_state *crtc_state; - struct drm_plane_state *primary_state; - struct drm_crtc *crtc = set->crtc; - int hdisplay, vdisplay; - int ret; - - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) - return PTR_ERR(crtc_state); - - primary_state = drm_atomic_get_plane_state(state, crtc->primary); - if (IS_ERR(primary_state)) - return PTR_ERR(primary_state); - - if (!set->mode) { - WARN_ON(set->fb); - WARN_ON(set->num_connectors); - - ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL); - if (ret != 0) - return ret; - - crtc_state->active = false; - - ret = drm_atomic_set_crtc_for_plane(primary_state, NULL); - if (ret != 0) - return ret; - - drm_atomic_set_fb_for_plane(primary_state, NULL); - - goto commit; - } - - WARN_ON(!set->fb); - WARN_ON(!set->num_connectors); - - ret = drm_atomic_set_mode_for_crtc(crtc_state, set->mode); - if (ret != 0) - return ret; - - crtc_state->active = true; - - ret = drm_atomic_set_crtc_for_plane(primary_state, crtc); - if (ret != 0) - return ret; - - drm_mode_get_hv_timing(set->mode, &hdisplay, &vdisplay); - - drm_atomic_set_fb_for_plane(primary_state, set->fb); - primary_state->crtc_x = 0; - primary_state->crtc_y = 0; - primary_state->crtc_w = hdisplay; - primary_state->crtc_h = vdisplay; - primary_state->src_x = set->x << 16; - primary_state->src_y = set->y << 16; - if (drm_rotation_90_or_270(primary_state->rotation)) { - primary_state->src_w = vdisplay << 16; - primary_state->src_h = hdisplay << 16; - } else { - primary_state->src_w = hdisplay << 16; - primary_state->src_h = vdisplay << 16; - } - -commit: - ret = update_output_state(state, set); - if (ret) - return ret; - - return 0; -} - /** * drm_atomic_helper_disable_all - disable all currently active outputs * @dev: DRM device diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index c78a44fad13d..c7d5e4c21423 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -50,7 +50,9 @@ struct drm_mode_create_dumb; struct drm_mode_fb_cmd2; struct drm_mode_fb_cmd; struct drm_mode_object; +struct drm_mode_set; struct drm_plane; +struct drm_plane_state; struct drm_property; struct edid; struct kref; @@ -223,6 +225,11 @@ struct drm_minor; int drm_atomic_debugfs_init(struct drm_minor *minor); #endif +int __drm_atomic_helper_disable_plane(struct drm_plane *plane, + struct drm_plane_state *plane_state); +int __drm_atomic_helper_set_config(struct drm_mode_set *set, + struct drm_atomic_state *state); + void drm_atomic_print_state(const struct drm_atomic_state *state); /* drm_atomic_uapi.c */ diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 58214be3bf3d..bf4e07141d81 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -117,12 +117,8 @@ int drm_atomic_helper_update_plane(struct drm_plane *plane, struct drm_modeset_acquire_ctx *ctx); int drm_atomic_helper_disable_plane(struct drm_plane *plane, struct drm_modeset_acquire_ctx *ctx); -int __drm_atomic_helper_disable_plane(struct drm_plane *plane, - struct drm_plane_state *plane_state); int drm_atomic_helper_set_config(struct drm_mode_set *set, struct drm_modeset_acquire_ctx *ctx); -int __drm_atomic_helper_set_config(struct drm_mode_set *set, - struct drm_atomic_state *state); int drm_atomic_helper_disable_all(struct drm_device *dev, struct drm_modeset_acquire_ctx *ctx); -- cgit v1.2.3 From aec3925f093d3ac880c53ec03ea28ef8608b0a52 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Fri, 31 May 2019 16:01:13 +0200 Subject: drm/fb-helper: Move out commit code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the modeset commit code to drm_client_modeset. No changes except exporting API. v7: Export drm_client_panel_rotation() (Gerd Hoffmann) v2: Move to drm_client_modeset.c instead of drm_client.c Signed-off-by: Noralf Trønnes Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20190531140117.37751-5-noralf@tronnes.org --- drivers/gpu/drm/drm_client_modeset.c | 288 +++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_fb_helper.c | 282 ---------------------------------- include/drm/drm_client.h | 4 + 3 files changed, 292 insertions(+), 282 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index 66770ed3299e..b1984f901a6d 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -11,9 +11,14 @@ #include #include +#include #include #include #include +#include + +#include "drm_crtc_internal.h" +#include "drm_internal.h" int drm_client_modeset_create(struct drm_client_dev *client) { @@ -102,3 +107,286 @@ struct drm_mode_set *drm_client_find_modeset(struct drm_client_dev *client, stru } /* TODO: Remove export when modeset code has been moved over */ EXPORT_SYMBOL(drm_client_find_modeset); + +/** + * drm_client_panel_rotation() - Check panel orientation + * @modeset: DRM modeset + * @rotation: Returned rotation value + * + * This function checks if the primary plane in @modeset can hw rotate to match + * the panel orientation on its connector. + * + * Note: Currently only 0 and 180 degrees are supported. + * + * Return: + * True if the plane can do the rotation, false otherwise. + */ +bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotation) +{ + struct drm_connector *connector = modeset->connectors[0]; + struct drm_plane *plane = modeset->crtc->primary; + u64 valid_mask = 0; + unsigned int i; + + if (!modeset->num_connectors) + return false; + + switch (connector->display_info.panel_orientation) { + case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP: + *rotation = DRM_MODE_ROTATE_180; + break; + case DRM_MODE_PANEL_ORIENTATION_LEFT_UP: + *rotation = DRM_MODE_ROTATE_90; + break; + case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP: + *rotation = DRM_MODE_ROTATE_270; + break; + default: + *rotation = DRM_MODE_ROTATE_0; + } + + /* + * TODO: support 90 / 270 degree hardware rotation, + * depending on the hardware this may require the framebuffer + * to be in a specific tiling format. + */ + if (*rotation != DRM_MODE_ROTATE_180 || !plane->rotation_property) + return false; + + for (i = 0; i < plane->rotation_property->num_values; i++) + valid_mask |= (1ULL << plane->rotation_property->values[i]); + + if (!(*rotation & valid_mask)) + return false; + + return true; +} +EXPORT_SYMBOL(drm_client_panel_rotation); + +static int drm_client_modeset_commit_atomic(struct drm_client_dev *client, bool active) +{ + struct drm_device *dev = client->dev; + struct drm_plane_state *plane_state; + struct drm_plane *plane; + struct drm_atomic_state *state; + struct drm_modeset_acquire_ctx ctx; + struct drm_mode_set *mode_set; + int ret; + + drm_modeset_acquire_init(&ctx, 0); + + state = drm_atomic_state_alloc(dev); + if (!state) { + ret = -ENOMEM; + goto out_ctx; + } + + state->acquire_ctx = &ctx; +retry: + drm_for_each_plane(plane, dev) { + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) { + ret = PTR_ERR(plane_state); + goto out_state; + } + + plane_state->rotation = DRM_MODE_ROTATE_0; + + /* disable non-primary: */ + if (plane->type == DRM_PLANE_TYPE_PRIMARY) + continue; + + ret = __drm_atomic_helper_disable_plane(plane, plane_state); + if (ret != 0) + goto out_state; + } + + drm_client_for_each_modeset(mode_set, client) { + struct drm_plane *primary = mode_set->crtc->primary; + unsigned int rotation; + + if (drm_client_panel_rotation(mode_set, &rotation)) { + /* Cannot fail as we've already gotten the plane state above */ + plane_state = drm_atomic_get_new_plane_state(state, primary); + plane_state->rotation = rotation; + } + + ret = __drm_atomic_helper_set_config(mode_set, state); + if (ret != 0) + goto out_state; + + /* + * __drm_atomic_helper_set_config() sets active when a + * mode is set, unconditionally clear it if we force DPMS off + */ + if (!active) { + struct drm_crtc *crtc = mode_set->crtc; + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + crtc_state->active = false; + } + } + + ret = drm_atomic_commit(state); + +out_state: + if (ret == -EDEADLK) + goto backoff; + + drm_atomic_state_put(state); +out_ctx: + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + + return ret; + +backoff: + drm_atomic_state_clear(state); + drm_modeset_backoff(&ctx); + + goto retry; +} + +static int drm_client_modeset_commit_legacy(struct drm_client_dev *client) +{ + struct drm_device *dev = client->dev; + struct drm_mode_set *mode_set; + struct drm_plane *plane; + int ret = 0; + + drm_modeset_lock_all(dev); + drm_for_each_plane(plane, dev) { + if (plane->type != DRM_PLANE_TYPE_PRIMARY) + drm_plane_force_disable(plane); + + if (plane->rotation_property) + drm_mode_plane_set_obj_prop(plane, + plane->rotation_property, + DRM_MODE_ROTATE_0); + } + + drm_client_for_each_modeset(mode_set, client) { + struct drm_crtc *crtc = mode_set->crtc; + + if (crtc->funcs->cursor_set2) { + ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0); + if (ret) + goto out; + } else if (crtc->funcs->cursor_set) { + ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0); + if (ret) + goto out; + } + + ret = drm_mode_set_config_internal(mode_set); + if (ret) + goto out; + } +out: + drm_modeset_unlock_all(dev); + + return ret; +} + +/** + * drm_client_modeset_commit_force() - Force commit CRTC configuration + * @client: DRM client + * + * Commit modeset configuration to crtcs without checking if there is a DRM master. + * + * Returns: + * Zero on success or negative error code on failure. + */ +int drm_client_modeset_commit_force(struct drm_client_dev *client) +{ + struct drm_device *dev = client->dev; + int ret; + + mutex_lock(&client->modeset_mutex); + if (drm_drv_uses_atomic_modeset(dev)) + ret = drm_client_modeset_commit_atomic(client, true); + else + ret = drm_client_modeset_commit_legacy(client); + mutex_unlock(&client->modeset_mutex); + + return ret; +} +EXPORT_SYMBOL(drm_client_modeset_commit_force); + +/** + * drm_client_modeset_commit() - Commit CRTC configuration + * @client: DRM client + * + * Commit modeset configuration to crtcs. + * + * Returns: + * Zero on success or negative error code on failure. + */ +int drm_client_modeset_commit(struct drm_client_dev *client) +{ + struct drm_device *dev = client->dev; + int ret; + + if (!drm_master_internal_acquire(dev)) + return -EBUSY; + + ret = drm_client_modeset_commit_force(client); + + drm_master_internal_release(dev); + + return ret; +} +EXPORT_SYMBOL(drm_client_modeset_commit); + +static void drm_client_modeset_dpms_legacy(struct drm_client_dev *client, int dpms_mode) +{ + struct drm_device *dev = client->dev; + struct drm_connector *connector; + struct drm_mode_set *modeset; + int j; + + drm_modeset_lock_all(dev); + drm_client_for_each_modeset(modeset, client) { + if (!modeset->crtc->enabled) + continue; + + for (j = 0; j < modeset->num_connectors; j++) { + connector = modeset->connectors[j]; + connector->funcs->dpms(connector, dpms_mode); + drm_object_property_set_value(&connector->base, + dev->mode_config.dpms_property, dpms_mode); + } + } + drm_modeset_unlock_all(dev); +} + +/** + * drm_client_modeset_dpms() - Set DPMS mode + * @client: DRM client + * @mode: DPMS mode + * + * Note: For atomic drivers @mode is reduced to on/off. + * + * Returns: + * Zero on success or negative error code on failure. + */ +int drm_client_modeset_dpms(struct drm_client_dev *client, int mode) +{ + struct drm_device *dev = client->dev; + int ret = 0; + + if (!drm_master_internal_acquire(dev)) + return -EBUSY; + + mutex_lock(&client->modeset_mutex); + if (drm_drv_uses_atomic_modeset(dev)) + ret = drm_client_modeset_commit_atomic(client, mode == DRM_MODE_DPMS_ON); + else + drm_client_modeset_dpms_legacy(client, mode); + mutex_unlock(&client->modeset_mutex); + + drm_master_internal_release(dev); + + return ret; +} +EXPORT_SYMBOL(drm_client_modeset_dpms); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 64800d43837d..7b388674a456 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -38,7 +38,6 @@ #include #include -#include #include #include #include @@ -47,8 +46,6 @@ #include #include -#include "drm_crtc_helper_internal.h" -#include "drm_crtc_internal.h" #include "drm_internal.h" static bool drm_fbdev_emulation = true; @@ -393,233 +390,6 @@ int drm_fb_helper_debug_leave(struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_debug_leave); -/** - * drm_client_panel_rotation() - Check panel orientation - * @modeset: DRM modeset - * @rotation: Returned rotation value - * - * This function checks if the primary plane in @modeset can hw rotate to match - * the panel orientation on its connector. - * - * Note: Currently only 0 and 180 degrees are supported. - * - * Return: - * True if the plane can do the rotation, false otherwise. - */ -bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotation) -{ - struct drm_connector *connector = modeset->connectors[0]; - struct drm_plane *plane = modeset->crtc->primary; - u64 valid_mask = 0; - unsigned int i; - - if (!modeset->num_connectors) - return false; - - switch (connector->display_info.panel_orientation) { - case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP: - *rotation = DRM_MODE_ROTATE_180; - break; - case DRM_MODE_PANEL_ORIENTATION_LEFT_UP: - *rotation = DRM_MODE_ROTATE_90; - break; - case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP: - *rotation = DRM_MODE_ROTATE_270; - break; - default: - *rotation = DRM_MODE_ROTATE_0; - } - - /* - * TODO: support 90 / 270 degree hardware rotation, - * depending on the hardware this may require the framebuffer - * to be in a specific tiling format. - */ - if (*rotation != DRM_MODE_ROTATE_180 || !plane->rotation_property) - return false; - - for (i = 0; i < plane->rotation_property->num_values; i++) - valid_mask |= (1ULL << plane->rotation_property->values[i]); - - if (!(*rotation & valid_mask)) - return false; - - return true; -} - -static int drm_client_modeset_commit_atomic(struct drm_client_dev *client, bool active) -{ - struct drm_device *dev = client->dev; - struct drm_plane_state *plane_state; - struct drm_plane *plane; - struct drm_atomic_state *state; - struct drm_modeset_acquire_ctx ctx; - struct drm_mode_set *mode_set; - int ret; - - drm_modeset_acquire_init(&ctx, 0); - - state = drm_atomic_state_alloc(dev); - if (!state) { - ret = -ENOMEM; - goto out_ctx; - } - - state->acquire_ctx = &ctx; -retry: - drm_for_each_plane(plane, dev) { - plane_state = drm_atomic_get_plane_state(state, plane); - if (IS_ERR(plane_state)) { - ret = PTR_ERR(plane_state); - goto out_state; - } - - plane_state->rotation = DRM_MODE_ROTATE_0; - - /* disable non-primary: */ - if (plane->type == DRM_PLANE_TYPE_PRIMARY) - continue; - - ret = __drm_atomic_helper_disable_plane(plane, plane_state); - if (ret != 0) - goto out_state; - } - - drm_client_for_each_modeset(mode_set, client) { - struct drm_plane *primary = mode_set->crtc->primary; - unsigned int rotation; - - if (drm_client_panel_rotation(mode_set, &rotation)) { - /* Cannot fail as we've already gotten the plane state above */ - plane_state = drm_atomic_get_new_plane_state(state, primary); - plane_state->rotation = rotation; - } - - ret = __drm_atomic_helper_set_config(mode_set, state); - if (ret != 0) - goto out_state; - - /* - * __drm_atomic_helper_set_config() sets active when a - * mode is set, unconditionally clear it if we force DPMS off - */ - if (!active) { - struct drm_crtc *crtc = mode_set->crtc; - struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); - - crtc_state->active = false; - } - } - - ret = drm_atomic_commit(state); - -out_state: - if (ret == -EDEADLK) - goto backoff; - - drm_atomic_state_put(state); -out_ctx: - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); - - return ret; - -backoff: - drm_atomic_state_clear(state); - drm_modeset_backoff(&ctx); - - goto retry; -} - -static int drm_client_modeset_commit_legacy(struct drm_client_dev *client) -{ - struct drm_device *dev = client->dev; - struct drm_mode_set *mode_set; - struct drm_plane *plane; - int ret = 0; - - drm_modeset_lock_all(dev); - drm_for_each_plane(plane, dev) { - if (plane->type != DRM_PLANE_TYPE_PRIMARY) - drm_plane_force_disable(plane); - - if (plane->rotation_property) - drm_mode_plane_set_obj_prop(plane, - plane->rotation_property, - DRM_MODE_ROTATE_0); - } - - drm_client_for_each_modeset(mode_set, client) { - struct drm_crtc *crtc = mode_set->crtc; - - if (crtc->funcs->cursor_set2) { - ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0); - if (ret) - goto out; - } else if (crtc->funcs->cursor_set) { - ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0); - if (ret) - goto out; - } - - ret = drm_mode_set_config_internal(mode_set); - if (ret) - goto out; - } -out: - drm_modeset_unlock_all(dev); - - return ret; -} - -/** - * drm_client_modeset_commit_force() - Force commit CRTC configuration - * @client: DRM client - * - * Commit modeset configuration to crtcs without checking if there is a DRM master. - * - * Returns: - * Zero on success or negative error code on failure. - */ -int drm_client_modeset_commit_force(struct drm_client_dev *client) -{ - struct drm_device *dev = client->dev; - int ret; - - mutex_lock(&client->modeset_mutex); - if (drm_drv_uses_atomic_modeset(dev)) - ret = drm_client_modeset_commit_atomic(client, true); - else - ret = drm_client_modeset_commit_legacy(client); - mutex_unlock(&client->modeset_mutex); - - return ret; -} - -/** - * drm_client_modeset_commit() - Commit CRTC configuration - * @client: DRM client - * - * Commit modeset configuration to crtcs. - * - * Returns: - * Zero on success or negative error code on failure. - */ -int drm_client_modeset_commit(struct drm_client_dev *client) -{ - struct drm_device *dev = client->dev; - int ret; - - if (!drm_master_internal_acquire(dev)) - return -EBUSY; - - ret = drm_client_modeset_commit_force(client); - - drm_master_internal_release(dev); - - return ret; -} - /** * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration * @fb_helper: driver-allocated fbdev helper, can be NULL @@ -719,58 +489,6 @@ static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { }; #endif -static void drm_client_modeset_dpms_legacy(struct drm_client_dev *client, int dpms_mode) -{ - struct drm_device *dev = client->dev; - struct drm_connector *connector; - struct drm_mode_set *modeset; - int j; - - drm_modeset_lock_all(dev); - drm_client_for_each_modeset(modeset, client) { - if (!modeset->crtc->enabled) - continue; - - for (j = 0; j < modeset->num_connectors; j++) { - connector = modeset->connectors[j]; - connector->funcs->dpms(connector, dpms_mode); - drm_object_property_set_value(&connector->base, - dev->mode_config.dpms_property, dpms_mode); - } - } - drm_modeset_unlock_all(dev); -} - -/** - * drm_client_modeset_dpms() - Set DPMS mode - * @client: DRM client - * @mode: DPMS mode - * - * Note: For atomic drivers @mode is reduced to on/off. - * - * Returns: - * Zero on success or negative error code on failure. - */ -int drm_client_modeset_dpms(struct drm_client_dev *client, int mode) -{ - struct drm_device *dev = client->dev; - int ret = 0; - - if (!drm_master_internal_acquire(dev)) - return -EBUSY; - - mutex_lock(&client->modeset_mutex); - if (drm_drv_uses_atomic_modeset(dev)) - ret = drm_client_modeset_commit_atomic(client, mode == DRM_MODE_DPMS_ON); - else - drm_client_modeset_dpms_legacy(client, mode); - mutex_unlock(&client->modeset_mutex); - - drm_master_internal_release(dev); - - return ret; -} - static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) { struct drm_fb_helper *fb_helper = info->par; diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index 87be9aeb1fe0..6cf48419f77f 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -155,6 +155,10 @@ int drm_client_modeset_create(struct drm_client_dev *client); void drm_client_modeset_free(struct drm_client_dev *client); void drm_client_modeset_release(struct drm_client_dev *client); struct drm_mode_set *drm_client_find_modeset(struct drm_client_dev *client, struct drm_crtc *crtc); +bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotation); +int drm_client_modeset_commit_force(struct drm_client_dev *client); +int drm_client_modeset_commit(struct drm_client_dev *client); +int drm_client_modeset_dpms(struct drm_client_dev *client, int mode); /** * drm_client_for_each_modeset() - Iterate over client modesets -- cgit v1.2.3 From 75f3f70f0462f443283e66d8448bed9330073178 Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sat, 8 Jun 2019 10:02:35 +0200 Subject: drm: drm_crtc.h self-contained While removing drmP.h from drm/radeon a few files ended up including drm_crtc.h as the first file. This failed build due to a missing dependency in drm_crtc.h. Add the missing include file. Signed-off-by: Sam Ravnborg Reviewed-by: Alex Deucher Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Sean Paul Cc: David Airlie Cc: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20190608080241.4958-2-sam@ravnborg.org --- include/drm/drm_crtc.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/drm') diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 58ad983d7cd6..dc42b9e35333 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From a7d469cc99b16cfda51d1c8a5ea250119c3e220a Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sat, 8 Jun 2019 10:02:36 +0200 Subject: drm: drm_debugfs.h self-contained While removing drmP.h from drm/radeon a few files ended up including drm_debugfs.h as the first file. This failed build due to missing dependencies in drm_debugfs.h. Add the missing include files. Signed-off-by: Sam Ravnborg Reviewed-by: Alex Deucher Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Sean Paul Cc: David Airlie Cc: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20190608080241.4958-3-sam@ravnborg.org --- include/drm/drm_debugfs.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/drm') diff --git a/include/drm/drm_debugfs.h b/include/drm/drm_debugfs.h index ac0f75df1ac9..7501e323d383 100644 --- a/include/drm/drm_debugfs.h +++ b/include/drm/drm_debugfs.h @@ -32,6 +32,8 @@ #ifndef _DRM_DEBUGFS_H_ #define _DRM_DEBUGFS_H_ +#include +#include /** * struct drm_info_list - debugfs info list entry * -- cgit v1.2.3 From 656600efd67b9b185bc1c85ec68d9420c2bbb812 Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Mon, 10 Jun 2019 00:07:48 +0200 Subject: drm: fix build errors with drm_print.h drm_print.h requires to fix build when macros are used. Pull in the header file in drm_print.h so users do not have to do it. Signed-off-by: Sam Ravnborg Reviewed-by: Alex Deucher Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Sean Paul Cc: David Airlie Cc: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20190609220757.10862-2-sam@ravnborg.org --- include/drm/drm_print.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/drm') diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h index 3a4247319e63..a5d6f2f3e430 100644 --- a/include/drm/drm_print.h +++ b/include/drm/drm_print.h @@ -32,6 +32,8 @@ #include #include +#include + /** * DOC: print * -- cgit v1.2.3 From e5852bee90d6cb7d9bd2c635c056e7746f137e06 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Sat, 8 Jun 2019 17:26:53 +0200 Subject: drm/fb-helper: Remove drm_fb_helper_connector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All drivers add all their connectors so there's no need to keep around an array of available connectors. Instead we just put the useable (not writeback) connectors in a temporary array using drm_client_for_each_connector_iter() everytime we probe the outputs. Other places where it's necessary to look at the connectors, we just iterate over them using the same iterator function. Rename functions which signature is changed since they will be moved to drm_client in a later patch. v6: Improve commit message (Sam Ravnborg) Signed-off-by: Noralf Trønnes Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20190608152657.36613-2-noralf@tronnes.org --- Documentation/gpu/todo.rst | 4 + drivers/gpu/drm/drm_fb_helper.c | 506 +++++++++++++--------------------------- include/drm/drm_client.h | 15 ++ include/drm/drm_fb_helper.h | 80 ++----- 4 files changed, 197 insertions(+), 408 deletions(-) (limited to 'include/drm') diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index 9d4038c50013..ab96ba0600a9 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -292,6 +292,10 @@ drm_fb_helper tasks - The max connector argument for drm_fb_helper_init() and drm_fb_helper_fbdev_setup() isn't used anymore and can be removed. +- The helper doesn't keep an array of connectors anymore so these can be + removed: drm_fb_helper_single_add_all_connectors(), + drm_fb_helper_add_one_connector() and drm_fb_helper_remove_one_connector(). + Core refactorings ================= diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 7b388674a456..b936db6280ac 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -95,12 +95,6 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); * Setup fbdev emulation by calling drm_fb_helper_fbdev_setup() and tear it * down by calling drm_fb_helper_fbdev_teardown(). * - * Drivers that need to handle connector hotplugging (e.g. dp mst) can't use - * the setup helper and will need to do the whole four-step setup process with - * drm_fb_helper_prepare(), drm_fb_helper_init(), - * drm_fb_helper_single_add_all_connectors(), enable hotplugging and - * drm_fb_helper_initial_config() to avoid a possible race window. - * * At runtime drivers should restore the fbdev console by using * drm_fb_helper_lastclose() as their &drm_driver.lastclose callback. * They should also notify the fb helper code from updates to the output @@ -123,8 +117,7 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); * encoders and connectors. To finish up the fbdev helper initialization, the * drm_fb_helper_init() function is called. To probe for all attached displays * and set up an initial configuration using the detected hardware, drivers - * should call drm_fb_helper_single_add_all_connectors() followed by - * drm_fb_helper_initial_config(). + * should call drm_fb_helper_initial_config(). * * If &drm_framebuffer_funcs.dirty is set, the * drm_fb_helper_{cfb,sys}_{write,fillrect,copyarea,imageblit} functions will @@ -137,165 +130,6 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); * deferred I/O (coupled with drm_fb_helper_fbdev_teardown()). */ -#define drm_fb_helper_for_each_connector(fbh, i__) \ - for (({ lockdep_assert_held(&(fbh)->lock); }), \ - i__ = 0; i__ < (fbh)->connector_count; i__++) - -static int __drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - struct drm_fb_helper_connector *fb_conn; - struct drm_fb_helper_connector **temp; - unsigned int count; - - if (!drm_fbdev_emulation) - return 0; - - lockdep_assert_held(&fb_helper->lock); - - count = fb_helper->connector_count + 1; - - if (count > fb_helper->connector_info_alloc_count) { - size_t size = count * sizeof(fb_conn); - - temp = krealloc(fb_helper->connector_info, size, GFP_KERNEL); - if (!temp) - return -ENOMEM; - - fb_helper->connector_info_alloc_count = count; - fb_helper->connector_info = temp; - } - - fb_conn = kzalloc(sizeof(*fb_conn), GFP_KERNEL); - if (!fb_conn) - return -ENOMEM; - - drm_connector_get(connector); - fb_conn->connector = connector; - fb_helper->connector_info[fb_helper->connector_count++] = fb_conn; - - return 0; -} - -int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - int err; - - if (!fb_helper) - return 0; - - mutex_lock(&fb_helper->lock); - err = __drm_fb_helper_add_one_connector(fb_helper, connector); - mutex_unlock(&fb_helper->lock); - - return err; -} -EXPORT_SYMBOL(drm_fb_helper_add_one_connector); - -/** - * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev - * emulation helper - * @fb_helper: fbdev initialized with drm_fb_helper_init, can be NULL - * - * This functions adds all the available connectors for use with the given - * fb_helper. This is a separate step to allow drivers to freely assign - * connectors to the fbdev, e.g. if some are reserved for special purposes or - * not adequate to be used for the fbcon. - * - * This function is protected against concurrent connector hotadds/removals - * using drm_fb_helper_add_one_connector() and - * drm_fb_helper_remove_one_connector(). - */ -int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) -{ - struct drm_device *dev; - struct drm_connector *connector; - struct drm_connector_list_iter conn_iter; - int i, ret = 0; - - if (!drm_fbdev_emulation || !fb_helper) - return 0; - - dev = fb_helper->dev; - - mutex_lock(&fb_helper->lock); - drm_connector_list_iter_begin(dev, &conn_iter); - drm_for_each_connector_iter(connector, &conn_iter) { - if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) - continue; - - ret = __drm_fb_helper_add_one_connector(fb_helper, connector); - if (ret) - goto fail; - } - goto out; - -fail: - drm_fb_helper_for_each_connector(fb_helper, i) { - struct drm_fb_helper_connector *fb_helper_connector = - fb_helper->connector_info[i]; - - drm_connector_put(fb_helper_connector->connector); - - kfree(fb_helper_connector); - fb_helper->connector_info[i] = NULL; - } - fb_helper->connector_count = 0; -out: - drm_connector_list_iter_end(&conn_iter); - mutex_unlock(&fb_helper->lock); - - return ret; -} -EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors); - -static int __drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - struct drm_fb_helper_connector *fb_helper_connector; - int i, j; - - if (!drm_fbdev_emulation) - return 0; - - lockdep_assert_held(&fb_helper->lock); - - drm_fb_helper_for_each_connector(fb_helper, i) { - if (fb_helper->connector_info[i]->connector == connector) - break; - } - - if (i == fb_helper->connector_count) - return -EINVAL; - fb_helper_connector = fb_helper->connector_info[i]; - drm_connector_put(fb_helper_connector->connector); - - for (j = i + 1; j < fb_helper->connector_count; j++) - fb_helper->connector_info[j - 1] = fb_helper->connector_info[j]; - - fb_helper->connector_count--; - kfree(fb_helper_connector); - - return 0; -} - -int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - int err; - - if (!fb_helper) - return 0; - - mutex_lock(&fb_helper->lock); - err = __drm_fb_helper_remove_one_connector(fb_helper, connector); - mutex_unlock(&fb_helper->lock); - - return err; -} -EXPORT_SYMBOL(drm_fb_helper_remove_one_connector); - static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) { uint16_t *r_base, *g_base, *b_base; @@ -645,20 +479,9 @@ int drm_fb_helper_init(struct drm_device *dev, return ret; } - fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL); - if (!fb_helper->connector_info) - goto out_free; - - fb_helper->connector_info_alloc_count = dev->mode_config.num_connector; - fb_helper->connector_count = 0; - dev->fb_helper = fb_helper; return 0; -out_free: - drm_client_release(&fb_helper->client); - - return -ENOMEM; } EXPORT_SYMBOL(drm_fb_helper_init); @@ -733,7 +556,6 @@ EXPORT_SYMBOL(drm_fb_helper_unregister_fbi); void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) { struct fb_info *info; - int i; if (!fb_helper) return; @@ -766,12 +588,6 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) if (!fb_helper->client.funcs) drm_client_release(&fb_helper->client); - - for (i = 0; i < fb_helper->connector_count; i++) { - drm_connector_put(fb_helper->connector_info[i]->connector); - kfree(fb_helper->connector_info[i]); - } - kfree(fb_helper->connector_info); } EXPORT_SYMBOL(drm_fb_helper_fini); @@ -1650,8 +1466,9 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, struct drm_client_dev *client = &fb_helper->client; int ret = 0; int crtc_count = 0; - int i; + struct drm_connector_list_iter conn_iter; struct drm_fb_helper_surface_size sizes; + struct drm_connector *connector; struct drm_mode_set *mode_set; int best_depth = 0; @@ -1668,11 +1485,11 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, if (preferred_bpp != sizes.surface_bpp) sizes.surface_depth = sizes.surface_bpp = preferred_bpp; - drm_fb_helper_for_each_connector(fb_helper, i) { - struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i]; + drm_connector_list_iter_begin(fb_helper->dev, &conn_iter); + drm_client_for_each_connector_iter(connector, &conn_iter) { struct drm_cmdline_mode *cmdline_mode; - cmdline_mode = &fb_helper_conn->connector->cmdline_mode; + cmdline_mode = &connector->cmdline_mode; if (cmdline_mode->bpp_specified) { switch (cmdline_mode->bpp) { @@ -1697,6 +1514,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, break; } } + drm_connector_list_iter_end(&conn_iter); /* * If we run into a situation where, for example, the primary plane @@ -1881,26 +1699,12 @@ void drm_fb_helper_fill_info(struct fb_info *info, } EXPORT_SYMBOL(drm_fb_helper_fill_info); -static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper, - uint32_t maxX, - uint32_t maxY) -{ - struct drm_connector *connector; - int i, count = 0; - - drm_fb_helper_for_each_connector(fb_helper, i) { - connector = fb_helper->connector_info[i]->connector; - count += connector->funcs->fill_modes(connector, maxX, maxY); - } - - return count; -} - -struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height) +static struct drm_display_mode * +drm_connector_has_preferred_mode(struct drm_connector *connector, int width, int height) { struct drm_display_mode *mode; - list_for_each_entry(mode, &fb_connector->connector->modes, head) { + list_for_each_entry(mode, &connector->modes, head) { if (mode->hdisplay > width || mode->vdisplay > height) continue; @@ -1909,20 +1713,15 @@ struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector * } return NULL; } -EXPORT_SYMBOL(drm_has_preferred_mode); - -static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) -{ - return fb_connector->connector->cmdline_mode.specified; -} -struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn) +static struct drm_display_mode * +drm_connector_pick_cmdline_mode(struct drm_connector *connector) { struct drm_cmdline_mode *cmdline_mode; struct drm_display_mode *mode; bool prefer_non_interlace; - cmdline_mode = &fb_helper_conn->connector->cmdline_mode; + cmdline_mode = &connector->cmdline_mode; if (cmdline_mode->specified == false) return NULL; @@ -1934,7 +1733,7 @@ struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *f prefer_non_interlace = !cmdline_mode->interlace; again: - list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { + list_for_each_entry(mode, &connector->modes, head) { /* check width/height */ if (mode->hdisplay != cmdline_mode->xres || mode->vdisplay != cmdline_mode->yres) @@ -1961,12 +1760,11 @@ again: } create_mode: - mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev, - cmdline_mode); - list_add(&mode->head, &fb_helper_conn->connector->modes); + mode = drm_mode_create_from_cmdline_mode(connector->dev, cmdline_mode); + list_add(&mode->head, &connector->modes); + return mode; } -EXPORT_SYMBOL(drm_pick_cmdline_mode); static bool drm_connector_enabled(struct drm_connector *connector, bool strict) { @@ -1983,15 +1781,16 @@ static bool drm_connector_enabled(struct drm_connector *connector, bool strict) return enable; } -static void drm_enable_connectors(struct drm_fb_helper *fb_helper, - bool *enabled) +static void drm_client_connectors_enabled(struct drm_connector **connectors, + unsigned int connector_count, + bool *enabled) { bool any_enabled = false; struct drm_connector *connector; int i = 0; - drm_fb_helper_for_each_connector(fb_helper, i) { - connector = fb_helper->connector_info[i]->connector; + for (i = 0; i < connector_count; i++) { + connector = connectors[i]; enabled[i] = drm_connector_enabled(connector, true); DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, connector->display_info.non_desktop ? "non desktop" : enabled[i] ? "yes" : "no"); @@ -2002,28 +1801,27 @@ static void drm_enable_connectors(struct drm_fb_helper *fb_helper, if (any_enabled) return; - drm_fb_helper_for_each_connector(fb_helper, i) { - connector = fb_helper->connector_info[i]->connector; - enabled[i] = drm_connector_enabled(connector, false); - } + for (i = 0; i < connector_count; i++) + enabled[i] = drm_connector_enabled(connectors[i], false); } -static bool drm_target_cloned(struct drm_fb_helper *fb_helper, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - bool *enabled, int width, int height) +static bool drm_client_target_cloned(struct drm_device *dev, + struct drm_connector **connectors, + unsigned int connector_count, + struct drm_display_mode **modes, + struct drm_fb_offset *offsets, + bool *enabled, int width, int height) { int count, i, j; bool can_clone = false; - struct drm_fb_helper_connector *fb_helper_conn; struct drm_display_mode *dmt_mode, *mode; /* only contemplate cloning in the single crtc case */ - if (fb_helper->dev->mode_config.num_crtc > 1) + if (dev->mode_config.num_crtc > 1) return false; count = 0; - drm_fb_helper_for_each_connector(fb_helper, i) { + for (i = 0; i < connector_count; i++) { if (enabled[i]) count++; } @@ -2034,11 +1832,10 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper, /* check the command line or if nothing common pick 1024x768 */ can_clone = true; - drm_fb_helper_for_each_connector(fb_helper, i) { + for (i = 0; i < connector_count; i++) { if (!enabled[i]) continue; - fb_helper_conn = fb_helper->connector_info[i]; - modes[i] = drm_pick_cmdline_mode(fb_helper_conn); + modes[i] = drm_connector_pick_cmdline_mode(connectors[i]); if (!modes[i]) { can_clone = false; break; @@ -2062,14 +1859,13 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper, /* try and find a 1024x768 mode on each connector */ can_clone = true; - dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false); + dmt_mode = drm_mode_find_dmt(dev, 1024, 768, 60, false); - drm_fb_helper_for_each_connector(fb_helper, i) { + for (i = 0; i < connector_count; i++) { if (!enabled[i]) continue; - fb_helper_conn = fb_helper->connector_info[i]; - list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { + list_for_each_entry(mode, &connectors[i]->modes, head) { if (drm_mode_match(mode, dmt_mode, DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_CLOCK | @@ -2089,30 +1885,31 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper, return false; } -static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - int idx, - int h_idx, int v_idx) +static int drm_client_get_tile_offsets(struct drm_connector **connectors, + unsigned int connector_count, + struct drm_display_mode **modes, + struct drm_fb_offset *offsets, + int idx, + int h_idx, int v_idx) { - struct drm_fb_helper_connector *fb_helper_conn; + struct drm_connector *connector; int i; int hoffset = 0, voffset = 0; - drm_fb_helper_for_each_connector(fb_helper, i) { - fb_helper_conn = fb_helper->connector_info[i]; - if (!fb_helper_conn->connector->has_tile) + for (i = 0; i < connector_count; i++) { + connector = connectors[i]; + if (!connector->has_tile) continue; if (!modes[i] && (h_idx || v_idx)) { DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i, - fb_helper_conn->connector->base.id); + connector->base.id); continue; } - if (fb_helper_conn->connector->tile_h_loc < h_idx) + if (connector->tile_h_loc < h_idx) hoffset += modes[i]->hdisplay; - if (fb_helper_conn->connector->tile_v_loc < v_idx) + if (connector->tile_v_loc < v_idx) voffset += modes[i]->vdisplay; } offsets[idx].x = hoffset; @@ -2121,20 +1918,21 @@ static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper, return 0; } -static bool drm_target_preferred(struct drm_fb_helper *fb_helper, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - bool *enabled, int width, int height) +static bool drm_client_target_preferred(struct drm_connector **connectors, + unsigned int connector_count, + struct drm_display_mode **modes, + struct drm_fb_offset *offsets, + bool *enabled, int width, int height) { - struct drm_fb_helper_connector *fb_helper_conn; - const u64 mask = BIT_ULL(fb_helper->connector_count) - 1; + const u64 mask = BIT_ULL(connector_count) - 1; + struct drm_connector *connector; u64 conn_configured = 0; int tile_pass = 0; int i; retry: - drm_fb_helper_for_each_connector(fb_helper, i) { - fb_helper_conn = fb_helper->connector_info[i]; + for (i = 0; i < connector_count; i++) { + connector = connectors[i]; if (conn_configured & BIT_ULL(i)) continue; @@ -2145,17 +1943,17 @@ retry: } /* first pass over all the untiled connectors */ - if (tile_pass == 0 && fb_helper_conn->connector->has_tile) + if (tile_pass == 0 && connector->has_tile) continue; if (tile_pass == 1) { - if (fb_helper_conn->connector->tile_h_loc != 0 || - fb_helper_conn->connector->tile_v_loc != 0) + if (connector->tile_h_loc != 0 || + connector->tile_v_loc != 0) continue; } else { - if (fb_helper_conn->connector->tile_h_loc != tile_pass - 1 && - fb_helper_conn->connector->tile_v_loc != tile_pass - 1) + if (connector->tile_h_loc != tile_pass - 1 && + connector->tile_v_loc != tile_pass - 1) /* if this tile_pass doesn't cover any of the tiles - keep going */ continue; @@ -2163,22 +1961,22 @@ retry: * find the tile offsets for this pass - need to find * all tiles left and above */ - drm_get_tile_offsets(fb_helper, modes, offsets, - i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc); + drm_client_get_tile_offsets(connectors, connector_count, modes, offsets, i, + connector->tile_h_loc, connector->tile_v_loc); } DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", - fb_helper_conn->connector->base.id); + connector->base.id); /* got for command line mode first */ - modes[i] = drm_pick_cmdline_mode(fb_helper_conn); + modes[i] = drm_connector_pick_cmdline_mode(connector); if (!modes[i]) { DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n", - fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0); - modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height); + connector->base.id, connector->tile_group ? connector->tile_group->id : 0); + modes[i] = drm_connector_has_preferred_mode(connector, width, height); } /* No preferred modes, pick one off the list */ - if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) { - list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head) + if (!modes[i] && !list_empty(&connector->modes)) { + list_for_each_entry(modes[i], &connector->modes, head) break; } DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : @@ -2207,40 +2005,41 @@ static bool connector_has_possible_crtc(struct drm_connector *connector, return false; } -static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, - struct drm_crtc **best_crtcs, - struct drm_display_mode **modes, - int n, int width, int height) +static int drm_client_pick_crtcs(struct drm_client_dev *client, + struct drm_connector **connectors, + unsigned int connector_count, + struct drm_crtc **best_crtcs, + struct drm_display_mode **modes, + int n, int width, int height) { - struct drm_client_dev *client = &fb_helper->client; + struct drm_device *dev = client->dev; struct drm_connector *connector; int my_score, best_score, score; struct drm_crtc **crtcs, *crtc; struct drm_mode_set *modeset; - struct drm_fb_helper_connector *fb_helper_conn; int o; - if (n == fb_helper->connector_count) + if (n == connector_count) return 0; - fb_helper_conn = fb_helper->connector_info[n]; - connector = fb_helper_conn->connector; + connector = connectors[n]; best_crtcs[n] = NULL; - best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height); + best_score = drm_client_pick_crtcs(client, connectors, connector_count, + best_crtcs, modes, n + 1, width, height); if (modes[n] == NULL) return best_score; - crtcs = kcalloc(fb_helper->connector_count, sizeof(*crtcs), GFP_KERNEL); + crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL); if (!crtcs) return best_score; my_score = 1; if (connector->status == connector_status_connected) my_score++; - if (drm_has_cmdline_mode(fb_helper_conn)) + if (connector->cmdline_mode.specified) my_score++; - if (drm_has_preferred_mode(fb_helper_conn, width, height)) + if (drm_connector_has_preferred_mode(connector, width, height)) my_score++; /* @@ -2259,7 +2058,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, if (o < n) { /* ignore cloning unless only a single crtc */ - if (fb_helper->dev->mode_config.num_crtc > 1) + if (dev->mode_config.num_crtc > 1) continue; if (!drm_mode_equal(modes[o], modes[n])) @@ -2268,12 +2067,11 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, crtcs[n] = crtc; memcpy(crtcs, best_crtcs, n * sizeof(*crtcs)); - score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1, - width, height); + score = my_score + drm_client_pick_crtcs(client, connectors, connector_count, + crtcs, modes, n + 1, width, height); if (score > best_score) { best_score = score; - memcpy(best_crtcs, crtcs, - fb_helper->connector_count * sizeof(*crtcs)); + memcpy(best_crtcs, crtcs, connector_count * sizeof(*crtcs)); } } @@ -2282,15 +2080,17 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, } /* Try to read the BIOS display configuration and use it for the initial config */ -static bool drm_fb_helper_firmware_config(struct drm_fb_helper *fb_helper, - struct drm_crtc **crtcs, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - bool *enabled, int width, int height) -{ - struct drm_device *dev = fb_helper->dev; - unsigned int count = min(fb_helper->connector_count, BITS_PER_LONG); +static bool drm_client_firmware_config(struct drm_client_dev *client, + struct drm_connector **connectors, + unsigned int connector_count, + struct drm_crtc **crtcs, + struct drm_display_mode **modes, + struct drm_fb_offset *offsets, + bool *enabled, int width, int height) +{ + unsigned int count = min_t(unsigned int, connector_count, BITS_PER_LONG); unsigned long conn_configured, conn_seq, mask; + struct drm_device *dev = client->dev; int i, j; bool *save_enabled; bool fallback = true, ret = true; @@ -2316,13 +2116,11 @@ static bool drm_fb_helper_firmware_config(struct drm_fb_helper *fb_helper, retry: conn_seq = conn_configured; for (i = 0; i < count; i++) { - struct drm_fb_helper_connector *fb_conn; struct drm_connector *connector; struct drm_encoder *encoder; struct drm_crtc *new_crtc; - fb_conn = fb_helper->connector_info[i]; - connector = fb_conn->connector; + connector = connectors[i]; if (conn_configured & BIT(i)) continue; @@ -2379,14 +2177,13 @@ retry: connector->name); /* go for command line mode first */ - modes[i] = drm_pick_cmdline_mode(fb_conn); + modes[i] = drm_connector_pick_cmdline_mode(connector); /* try for preferred next */ if (!modes[i]) { DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n", connector->name, connector->has_tile); - modes[i] = drm_has_preferred_mode(fb_conn, width, - height); + modes[i] = drm_connector_has_preferred_mode(connector, width, height); } /* No preferred mode marked by the EDID? Are there any modes? */ @@ -2461,8 +2258,12 @@ bail: static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, u32 width, u32 height) { + struct drm_connector *connector, **connectors = NULL; struct drm_client_dev *client = &fb_helper->client; + struct drm_connector_list_iter conn_iter; struct drm_device *dev = fb_helper->dev; + unsigned int total_modes_count = 0; + unsigned int connector_count = 0; struct drm_display_mode **modes; struct drm_fb_offset *offsets; struct drm_crtc **crtcs; @@ -2470,16 +2271,28 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, int i; DRM_DEBUG_KMS("\n"); - /* prevent concurrent modification of connector_count by hotplug */ - lockdep_assert_held(&fb_helper->lock); - - crtcs = kcalloc(fb_helper->connector_count, sizeof(*crtcs), GFP_KERNEL); - modes = kcalloc(fb_helper->connector_count, - sizeof(struct drm_display_mode *), GFP_KERNEL); - offsets = kcalloc(fb_helper->connector_count, - sizeof(struct drm_fb_offset), GFP_KERNEL); - enabled = kcalloc(fb_helper->connector_count, - sizeof(bool), GFP_KERNEL); + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_client_for_each_connector_iter(connector, &conn_iter) { + struct drm_connector **tmp; + + tmp = krealloc(connectors, (connector_count + 1) * sizeof(*connectors), GFP_KERNEL); + if (!tmp) + goto free_connectors; + + connectors = tmp; + drm_connector_get(connector); + connectors[connector_count++] = connector; + } + drm_connector_list_iter_end(&conn_iter); + + if (!connector_count) + return; + + crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL); + modes = kcalloc(connector_count, sizeof(*modes), GFP_KERNEL); + offsets = kcalloc(connector_count, sizeof(*offsets), GFP_KERNEL); + enabled = kcalloc(connector_count, sizeof(bool), GFP_KERNEL); if (!crtcs || !modes || !enabled || !offsets) { DRM_ERROR("Memory allocation failed\n"); goto out; @@ -2488,40 +2301,42 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, mutex_lock(&client->modeset_mutex); mutex_lock(&fb_helper->dev->mode_config.mutex); - if (drm_fb_helper_probe_connector_modes(fb_helper, width, height) == 0) + for (i = 0; i < connector_count; i++) + total_modes_count += connectors[i]->funcs->fill_modes(connectors[i], width, height); + if (!total_modes_count) DRM_DEBUG_KMS("No connectors reported connected with modes\n"); - drm_enable_connectors(fb_helper, enabled); - - if (!drm_fb_helper_firmware_config(fb_helper, crtcs, modes, offsets, - enabled, width, height)) { - memset(modes, 0, fb_helper->connector_count*sizeof(modes[0])); - memset(crtcs, 0, fb_helper->connector_count*sizeof(crtcs[0])); - memset(offsets, 0, fb_helper->connector_count*sizeof(offsets[0])); - - if (!drm_target_cloned(fb_helper, modes, offsets, - enabled, width, height) && - !drm_target_preferred(fb_helper, modes, offsets, - enabled, width, height)) + drm_client_connectors_enabled(connectors, connector_count, enabled); + + if (!drm_client_firmware_config(client, connectors, connector_count, crtcs, + modes, offsets, enabled, width, height)) { + memset(modes, 0, connector_count * sizeof(*modes)); + memset(crtcs, 0, connector_count * sizeof(*crtcs)); + memset(offsets, 0, connector_count * sizeof(*offsets)); + + if (!drm_client_target_cloned(dev, connectors, connector_count, modes, + offsets, enabled, width, height) && + !drm_client_target_preferred(connectors, connector_count, modes, + offsets, enabled, width, height)) DRM_ERROR("Unable to find initial modes\n"); DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height); - drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height); + drm_client_pick_crtcs(client, connectors, connector_count, + crtcs, modes, 0, width, height); } mutex_unlock(&fb_helper->dev->mode_config.mutex); drm_client_modeset_release(client); - drm_fb_helper_for_each_connector(fb_helper, i) { + for (i = 0; i < connector_count; i++) { struct drm_display_mode *mode = modes[i]; struct drm_crtc *crtc = crtcs[i]; struct drm_fb_offset *offset = &offsets[i]; if (mode && crtc) { struct drm_mode_set *modeset = drm_client_find_modeset(client, crtc); - struct drm_connector *connector = - fb_helper->connector_info[i]->connector; + struct drm_connector *connector = connectors[i]; DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n", mode->name, crtc->base.id, offset->x, offset->y); @@ -2544,6 +2359,10 @@ out: kfree(modes); kfree(offsets); kfree(enabled); +free_connectors: + for (i = 0; i < connector_count; i++) + drm_connector_put(connectors[i]); + kfree(connectors); } /* @@ -2556,10 +2375,11 @@ out: static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) { struct drm_client_dev *client = &fb_helper->client; + struct drm_connector_list_iter conn_iter; struct fb_info *info = fb_helper->fbdev; unsigned int rotation, sw_rotations = 0; + struct drm_connector *connector; struct drm_mode_set *modeset; - int i; mutex_lock(&client->modeset_mutex); drm_client_for_each_modeset(modeset, client) { @@ -2576,10 +2396,8 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) } mutex_unlock(&client->modeset_mutex); - mutex_lock(&fb_helper->dev->mode_config.mutex); - drm_fb_helper_for_each_connector(fb_helper, i) { - struct drm_connector *connector = - fb_helper->connector_info[i]->connector; + drm_connector_list_iter_begin(fb_helper->dev, &conn_iter); + drm_client_for_each_connector_iter(connector, &conn_iter) { /* use first connected connector for the physical dimensions */ if (connector->status == connector_status_connected) { @@ -2588,7 +2406,7 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) break; } } - mutex_unlock(&fb_helper->dev->mode_config.mutex); + drm_connector_list_iter_end(&conn_iter); switch (sw_rotations) { case DRM_MODE_ROTATE_0: @@ -2826,12 +2644,6 @@ int drm_fb_helper_fbdev_setup(struct drm_device *dev, return ret; } - ret = drm_fb_helper_single_add_all_connectors(fb_helper); - if (ret < 0) { - DRM_DEV_ERROR(dev->dev, "fbdev: Failed to add connectors (ret=%d)\n", ret); - goto err_drm_fb_helper_fini; - } - if (!drm_drv_uses_atomic_modeset(dev)) drm_helper_disable_unused_functions(dev); @@ -3137,10 +2949,6 @@ static int drm_fbdev_client_hotplug(struct drm_client_dev *client) if (ret) goto err; - ret = drm_fb_helper_single_add_all_connectors(fb_helper); - if (ret) - goto err_cleanup; - if (!drm_drv_uses_atomic_modeset(dev)) drm_helper_disable_unused_functions(dev); diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index 6cf48419f77f..8d94880bbe25 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -7,6 +7,7 @@ #include #include +#include #include struct drm_client_dev; @@ -169,6 +170,20 @@ int drm_client_modeset_dpms(struct drm_client_dev *client, int mode); for (({ lockdep_assert_held(&(client)->modeset_mutex); }), \ modeset = (client)->modesets; modeset->crtc; modeset++) +/** + * drm_client_for_each_connector_iter - connector_list iterator macro + * @connector: &struct drm_connector pointer used as cursor + * @iter: &struct drm_connector_list_iter + * + * This iterates the connectors that are useable for internal clients (excludes + * writeback connectors). + * + * For more info see drm_for_each_connector_iter(). + */ +#define drm_client_for_each_connector_iter(connector, iter) \ + drm_for_each_connector_iter(connector, iter) \ + if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) + int drm_client_debugfs_init(struct drm_minor *minor); #endif diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 6b334f4d8a22..e14d52ef9638 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -97,16 +97,10 @@ struct drm_fb_helper_funcs { struct drm_fb_helper_surface_size *sizes); }; -struct drm_fb_helper_connector { - struct drm_connector *connector; -}; - /** * struct drm_fb_helper - main structure to emulate fbdev on top of KMS * @fb: Scanout framebuffer object * @dev: DRM device - * @connector_count: number of connected connectors - * @connector_info_alloc_count: size of connector_info * @funcs: driver callbacks for fb helper * @fbdev: emulated fbdev device info struct * @pseudo_palette: fake palette of 16 colors @@ -138,15 +132,6 @@ struct drm_fb_helper { struct drm_framebuffer *fb; struct drm_device *dev; - int connector_count; - int connector_info_alloc_count; - /** - * @connector_info: - * - * Array of per-connector information. Do not iterate directly, but use - * drm_fb_helper_for_each_connector. - */ - struct drm_fb_helper_connector **connector_info; const struct drm_fb_helper_funcs *funcs; struct fb_info *fbdev; u32 pseudo_palette[17]; @@ -286,18 +271,8 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd, int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper); int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel); -int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper); int drm_fb_helper_debug_enter(struct fb_info *info); int drm_fb_helper_debug_leave(struct fb_info *info); -struct drm_display_mode * -drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, - int width, int height); -struct drm_display_mode * -drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn); - -int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector); -int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector); int drm_fb_helper_fbdev_setup(struct drm_device *dev, struct drm_fb_helper *fb_helper, @@ -472,12 +447,6 @@ static inline int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, return 0; } -static inline int -drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) -{ - return 0; -} - static inline int drm_fb_helper_debug_enter(struct fb_info *info) { return 0; @@ -488,34 +457,6 @@ static inline int drm_fb_helper_debug_leave(struct fb_info *info) return 0; } -static inline struct drm_display_mode * -drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, - int width, int height) -{ - return NULL; -} - -static inline struct drm_display_mode * -drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, - int width, int height) -{ - return NULL; -} - -static inline int -drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - return 0; -} - -static inline int -drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - return 0; -} - static inline int drm_fb_helper_fbdev_setup(struct drm_device *dev, struct drm_fb_helper *fb_helper, @@ -557,6 +498,27 @@ drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp) #endif +/* TODO: There's a todo entry to remove these three */ +static inline int +drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) +{ + return 0; +} + +static inline int +drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, + struct drm_connector *connector) +{ + return 0; +} + +static inline int +drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, + struct drm_connector *connector) +{ + return 0; +} + /** * drm_fb_helper_remove_conflicting_framebuffers - remove firmware-configured framebuffers * @a: memory range, users of which are to be removed -- cgit v1.2.3 From aafa9e0668720a663eb47f25d863a84160eead7b Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Sat, 8 Jun 2019 17:26:54 +0200 Subject: drm/fb-helper: Prepare to move out modeset config code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This prepares the modeset code so it can be moved out as-is in the next patch. v3: Remove stray newline Signed-off-by: Noralf Trønnes Reviewed-by: Maxime Ripard Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20190608152657.36613-3-noralf@tronnes.org --- drivers/gpu/drm/drm_fb_helper.c | 62 +++++++++++++++++++++++++++++------------ include/drm/drm_fb_helper.h | 4 --- 2 files changed, 44 insertions(+), 22 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index b936db6280ac..e7f41e5d924c 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -48,6 +48,10 @@ #include "drm_internal.h" +struct drm_client_offset { + int x, y; +}; + static bool drm_fbdev_emulation = true; module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600); MODULE_PARM_DESC(fbdev_emulation, @@ -1809,7 +1813,7 @@ static bool drm_client_target_cloned(struct drm_device *dev, struct drm_connector **connectors, unsigned int connector_count, struct drm_display_mode **modes, - struct drm_fb_offset *offsets, + struct drm_client_offset *offsets, bool *enabled, int width, int height) { int count, i, j; @@ -1888,7 +1892,7 @@ static bool drm_client_target_cloned(struct drm_device *dev, static int drm_client_get_tile_offsets(struct drm_connector **connectors, unsigned int connector_count, struct drm_display_mode **modes, - struct drm_fb_offset *offsets, + struct drm_client_offset *offsets, int idx, int h_idx, int v_idx) { @@ -1921,7 +1925,7 @@ static int drm_client_get_tile_offsets(struct drm_connector **connectors, static bool drm_client_target_preferred(struct drm_connector **connectors, unsigned int connector_count, struct drm_display_mode **modes, - struct drm_fb_offset *offsets, + struct drm_client_offset *offsets, bool *enabled, int width, int height) { const u64 mask = BIT_ULL(connector_count) - 1; @@ -2085,7 +2089,7 @@ static bool drm_client_firmware_config(struct drm_client_dev *client, unsigned int connector_count, struct drm_crtc **crtcs, struct drm_display_mode **modes, - struct drm_fb_offset *offsets, + struct drm_client_offset *offsets, bool *enabled, int width, int height) { unsigned int count = min_t(unsigned int, connector_count, BITS_PER_LONG); @@ -2255,30 +2259,47 @@ bail: return ret; } -static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, - u32 width, u32 height) +/** + * drm_client_modeset_probe() - Probe for displays + * @client: DRM client + * @width: Maximum display mode width (optional) + * @height: Maximum display mode height (optional) + * + * This function sets up display pipelines for enabled connectors and stores the + * config in the client's modeset array. + * + * Returns: + * Zero on success or negative error code on failure. + */ +int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, unsigned int height) { struct drm_connector *connector, **connectors = NULL; - struct drm_client_dev *client = &fb_helper->client; struct drm_connector_list_iter conn_iter; - struct drm_device *dev = fb_helper->dev; + struct drm_device *dev = client->dev; unsigned int total_modes_count = 0; + struct drm_client_offset *offsets; unsigned int connector_count = 0; struct drm_display_mode **modes; - struct drm_fb_offset *offsets; struct drm_crtc **crtcs; + int i, ret = 0; bool *enabled; - int i; DRM_DEBUG_KMS("\n"); + if (!width) + width = dev->mode_config.max_width; + if (!height) + height = dev->mode_config.max_height; + drm_connector_list_iter_begin(dev, &conn_iter); drm_client_for_each_connector_iter(connector, &conn_iter) { struct drm_connector **tmp; tmp = krealloc(connectors, (connector_count + 1) * sizeof(*connectors), GFP_KERNEL); - if (!tmp) + if (!tmp) { + ret = -ENOMEM; goto free_connectors; + } connectors = tmp; drm_connector_get(connector); @@ -2287,7 +2308,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, drm_connector_list_iter_end(&conn_iter); if (!connector_count) - return; + return 0; crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL); modes = kcalloc(connector_count, sizeof(*modes), GFP_KERNEL); @@ -2295,12 +2316,13 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, enabled = kcalloc(connector_count, sizeof(bool), GFP_KERNEL); if (!crtcs || !modes || !enabled || !offsets) { DRM_ERROR("Memory allocation failed\n"); + ret = -ENOMEM; goto out; } mutex_lock(&client->modeset_mutex); - mutex_lock(&fb_helper->dev->mode_config.mutex); + mutex_lock(&dev->mode_config.mutex); for (i = 0; i < connector_count; i++) total_modes_count += connectors[i]->funcs->fill_modes(connectors[i], width, height); if (!total_modes_count) @@ -2325,14 +2347,14 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, drm_client_pick_crtcs(client, connectors, connector_count, crtcs, modes, 0, width, height); } - mutex_unlock(&fb_helper->dev->mode_config.mutex); + mutex_unlock(&dev->mode_config.mutex); drm_client_modeset_release(client); for (i = 0; i < connector_count; i++) { struct drm_display_mode *mode = modes[i]; struct drm_crtc *crtc = crtcs[i]; - struct drm_fb_offset *offset = &offsets[i]; + struct drm_client_offset *offset = &offsets[i]; if (mode && crtc) { struct drm_mode_set *modeset = drm_client_find_modeset(client, crtc); @@ -2342,8 +2364,10 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, mode->name, crtc->base.id, offset->x, offset->y); if (WARN_ON_ONCE(modeset->num_connectors == DRM_CLIENT_MAX_CLONED_CONNECTORS || - (dev->mode_config.num_crtc > 1 && modeset->num_connectors == 1))) + (dev->mode_config.num_crtc > 1 && modeset->num_connectors == 1))) { + ret = -EINVAL; break; + } modeset->mode = drm_mode_duplicate(dev, mode); drm_connector_get(connector); @@ -2363,6 +2387,8 @@ free_connectors: for (i = 0; i < connector_count; i++) drm_connector_put(connectors[i]); kfree(connectors); + + return ret; } /* @@ -2444,7 +2470,7 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper, width = dev->mode_config.max_width; height = dev->mode_config.max_height; - drm_setup_crtcs(fb_helper, width, height); + drm_client_modeset_probe(&fb_helper->client, width, height); ret = drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); if (ret < 0) { if (ret == -EAGAIN) { @@ -2591,7 +2617,7 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) DRM_DEBUG_KMS("\n"); - drm_setup_crtcs(fb_helper, fb_helper->fb->width, fb_helper->fb->height); + drm_client_modeset_probe(&fb_helper->client, fb_helper->fb->width, fb_helper->fb->height); drm_setup_crtcs_fb(fb_helper); mutex_unlock(&fb_helper->lock); diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index e14d52ef9638..c8a8ae2a678a 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -43,10 +43,6 @@ enum mode_set_atomic { ENTER_ATOMIC_MODE_SET, }; -struct drm_fb_offset { - int x, y; -}; - /** * struct drm_fb_helper_surface_size - describes fbdev size and scanout surface size * @fb_width: fbdev width -- cgit v1.2.3 From cf13909aee054f5aa667d4b9da0ac7df4f6c1327 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Sat, 8 Jun 2019 17:26:55 +0200 Subject: drm/fb-helper: Move out modeset config code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No functional changes, just moving code as-is and fixing includes. Signed-off-by: Noralf Trønnes Reviewed-by: Maxime Ripard Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20190608152657.36613-4-noralf@tronnes.org --- drivers/gpu/drm/drm_client_modeset.c | 707 ++++++++++++++++++++++++++++++++++- drivers/gpu/drm/drm_fb_helper.c | 692 ---------------------------------- include/drm/drm_client.h | 5 +- 3 files changed, 702 insertions(+), 702 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index b1984f901a6d..006bf7390e7d 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -13,13 +13,22 @@ #include #include +#include #include #include #include +#include +#include #include "drm_crtc_internal.h" #include "drm_internal.h" +#define DRM_CLIENT_MAX_CLONED_CONNECTORS 8 + +struct drm_client_offset { + int x, y; +}; + int drm_client_modeset_create(struct drm_client_dev *client) { struct drm_device *dev = client->dev; @@ -58,7 +67,7 @@ err_free: return -ENOMEM; } -void drm_client_modeset_release(struct drm_client_dev *client) +static void drm_client_modeset_release(struct drm_client_dev *client) { struct drm_mode_set *modeset; unsigned int i; @@ -75,8 +84,6 @@ void drm_client_modeset_release(struct drm_client_dev *client) modeset->num_connectors = 0; } } -/* TODO: Remove export when modeset code has been moved over */ -EXPORT_SYMBOL(drm_client_modeset_release); void drm_client_modeset_free(struct drm_client_dev *client) { @@ -95,7 +102,8 @@ void drm_client_modeset_free(struct drm_client_dev *client) kfree(client->modesets); } -struct drm_mode_set *drm_client_find_modeset(struct drm_client_dev *client, struct drm_crtc *crtc) +static struct drm_mode_set * +drm_client_find_modeset(struct drm_client_dev *client, struct drm_crtc *crtc) { struct drm_mode_set *modeset; @@ -105,8 +113,695 @@ struct drm_mode_set *drm_client_find_modeset(struct drm_client_dev *client, stru return NULL; } -/* TODO: Remove export when modeset code has been moved over */ -EXPORT_SYMBOL(drm_client_find_modeset); + +static struct drm_display_mode * +drm_connector_has_preferred_mode(struct drm_connector *connector, int width, int height) +{ + struct drm_display_mode *mode; + + list_for_each_entry(mode, &connector->modes, head) { + if (mode->hdisplay > width || + mode->vdisplay > height) + continue; + if (mode->type & DRM_MODE_TYPE_PREFERRED) + return mode; + } + return NULL; +} + +static struct drm_display_mode * +drm_connector_pick_cmdline_mode(struct drm_connector *connector) +{ + struct drm_cmdline_mode *cmdline_mode; + struct drm_display_mode *mode; + bool prefer_non_interlace; + + cmdline_mode = &connector->cmdline_mode; + if (cmdline_mode->specified == false) + return NULL; + + /* attempt to find a matching mode in the list of modes + * we have gotten so far, if not add a CVT mode that conforms + */ + if (cmdline_mode->rb || cmdline_mode->margins) + goto create_mode; + + prefer_non_interlace = !cmdline_mode->interlace; +again: + list_for_each_entry(mode, &connector->modes, head) { + /* check width/height */ + if (mode->hdisplay != cmdline_mode->xres || + mode->vdisplay != cmdline_mode->yres) + continue; + + if (cmdline_mode->refresh_specified) { + if (mode->vrefresh != cmdline_mode->refresh) + continue; + } + + if (cmdline_mode->interlace) { + if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) + continue; + } else if (prefer_non_interlace) { + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + continue; + } + return mode; + } + + if (prefer_non_interlace) { + prefer_non_interlace = false; + goto again; + } + +create_mode: + mode = drm_mode_create_from_cmdline_mode(connector->dev, cmdline_mode); + list_add(&mode->head, &connector->modes); + + return mode; +} + +static bool drm_connector_enabled(struct drm_connector *connector, bool strict) +{ + bool enable; + + if (connector->display_info.non_desktop) + return false; + + if (strict) + enable = connector->status == connector_status_connected; + else + enable = connector->status != connector_status_disconnected; + + return enable; +} + +static void drm_client_connectors_enabled(struct drm_connector **connectors, + unsigned int connector_count, + bool *enabled) +{ + bool any_enabled = false; + struct drm_connector *connector; + int i = 0; + + for (i = 0; i < connector_count; i++) { + connector = connectors[i]; + enabled[i] = drm_connector_enabled(connector, true); + DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, + connector->display_info.non_desktop ? "non desktop" : enabled[i] ? "yes" : "no"); + + any_enabled |= enabled[i]; + } + + if (any_enabled) + return; + + for (i = 0; i < connector_count; i++) + enabled[i] = drm_connector_enabled(connectors[i], false); +} + +static bool drm_client_target_cloned(struct drm_device *dev, + struct drm_connector **connectors, + unsigned int connector_count, + struct drm_display_mode **modes, + struct drm_client_offset *offsets, + bool *enabled, int width, int height) +{ + int count, i, j; + bool can_clone = false; + struct drm_display_mode *dmt_mode, *mode; + + /* only contemplate cloning in the single crtc case */ + if (dev->mode_config.num_crtc > 1) + return false; + + count = 0; + for (i = 0; i < connector_count; i++) { + if (enabled[i]) + count++; + } + + /* only contemplate cloning if more than one connector is enabled */ + if (count <= 1) + return false; + + /* check the command line or if nothing common pick 1024x768 */ + can_clone = true; + for (i = 0; i < connector_count; i++) { + if (!enabled[i]) + continue; + modes[i] = drm_connector_pick_cmdline_mode(connectors[i]); + if (!modes[i]) { + can_clone = false; + break; + } + for (j = 0; j < i; j++) { + if (!enabled[j]) + continue; + if (!drm_mode_match(modes[j], modes[i], + DRM_MODE_MATCH_TIMINGS | + DRM_MODE_MATCH_CLOCK | + DRM_MODE_MATCH_FLAGS | + DRM_MODE_MATCH_3D_FLAGS)) + can_clone = false; + } + } + + if (can_clone) { + DRM_DEBUG_KMS("can clone using command line\n"); + return true; + } + + /* try and find a 1024x768 mode on each connector */ + can_clone = true; + dmt_mode = drm_mode_find_dmt(dev, 1024, 768, 60, false); + + for (i = 0; i < connector_count; i++) { + if (!enabled[i]) + continue; + + list_for_each_entry(mode, &connectors[i]->modes, head) { + if (drm_mode_match(mode, dmt_mode, + DRM_MODE_MATCH_TIMINGS | + DRM_MODE_MATCH_CLOCK | + DRM_MODE_MATCH_FLAGS | + DRM_MODE_MATCH_3D_FLAGS)) + modes[i] = mode; + } + if (!modes[i]) + can_clone = false; + } + + if (can_clone) { + DRM_DEBUG_KMS("can clone using 1024x768\n"); + return true; + } + DRM_INFO("kms: can't enable cloning when we probably wanted to.\n"); + return false; +} + +static int drm_client_get_tile_offsets(struct drm_connector **connectors, + unsigned int connector_count, + struct drm_display_mode **modes, + struct drm_client_offset *offsets, + int idx, + int h_idx, int v_idx) +{ + struct drm_connector *connector; + int i; + int hoffset = 0, voffset = 0; + + for (i = 0; i < connector_count; i++) { + connector = connectors[i]; + if (!connector->has_tile) + continue; + + if (!modes[i] && (h_idx || v_idx)) { + DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i, + connector->base.id); + continue; + } + if (connector->tile_h_loc < h_idx) + hoffset += modes[i]->hdisplay; + + if (connector->tile_v_loc < v_idx) + voffset += modes[i]->vdisplay; + } + offsets[idx].x = hoffset; + offsets[idx].y = voffset; + DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx); + return 0; +} + +static bool drm_client_target_preferred(struct drm_connector **connectors, + unsigned int connector_count, + struct drm_display_mode **modes, + struct drm_client_offset *offsets, + bool *enabled, int width, int height) +{ + const u64 mask = BIT_ULL(connector_count) - 1; + struct drm_connector *connector; + u64 conn_configured = 0; + int tile_pass = 0; + int i; + +retry: + for (i = 0; i < connector_count; i++) { + connector = connectors[i]; + + if (conn_configured & BIT_ULL(i)) + continue; + + if (enabled[i] == false) { + conn_configured |= BIT_ULL(i); + continue; + } + + /* first pass over all the untiled connectors */ + if (tile_pass == 0 && connector->has_tile) + continue; + + if (tile_pass == 1) { + if (connector->tile_h_loc != 0 || + connector->tile_v_loc != 0) + continue; + + } else { + if (connector->tile_h_loc != tile_pass - 1 && + connector->tile_v_loc != tile_pass - 1) + /* if this tile_pass doesn't cover any of the tiles - keep going */ + continue; + + /* + * find the tile offsets for this pass - need to find + * all tiles left and above + */ + drm_client_get_tile_offsets(connectors, connector_count, modes, offsets, i, + connector->tile_h_loc, connector->tile_v_loc); + } + DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", + connector->base.id); + + /* got for command line mode first */ + modes[i] = drm_connector_pick_cmdline_mode(connector); + if (!modes[i]) { + DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n", + connector->base.id, connector->tile_group ? connector->tile_group->id : 0); + modes[i] = drm_connector_has_preferred_mode(connector, width, height); + } + /* No preferred modes, pick one off the list */ + if (!modes[i] && !list_empty(&connector->modes)) { + list_for_each_entry(modes[i], &connector->modes, head) + break; + } + DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : + "none"); + conn_configured |= BIT_ULL(i); + } + + if ((conn_configured & mask) != mask) { + tile_pass++; + goto retry; + } + return true; +} + +static bool connector_has_possible_crtc(struct drm_connector *connector, + struct drm_crtc *crtc) +{ + struct drm_encoder *encoder; + int i; + + drm_connector_for_each_possible_encoder(connector, encoder, i) { + if (encoder->possible_crtcs & drm_crtc_mask(crtc)) + return true; + } + + return false; +} + +static int drm_client_pick_crtcs(struct drm_client_dev *client, + struct drm_connector **connectors, + unsigned int connector_count, + struct drm_crtc **best_crtcs, + struct drm_display_mode **modes, + int n, int width, int height) +{ + struct drm_device *dev = client->dev; + struct drm_connector *connector; + int my_score, best_score, score; + struct drm_crtc **crtcs, *crtc; + struct drm_mode_set *modeset; + int o; + + if (n == connector_count) + return 0; + + connector = connectors[n]; + + best_crtcs[n] = NULL; + best_score = drm_client_pick_crtcs(client, connectors, connector_count, + best_crtcs, modes, n + 1, width, height); + if (modes[n] == NULL) + return best_score; + + crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL); + if (!crtcs) + return best_score; + + my_score = 1; + if (connector->status == connector_status_connected) + my_score++; + if (connector->cmdline_mode.specified) + my_score++; + if (drm_connector_has_preferred_mode(connector, width, height)) + my_score++; + + /* + * select a crtc for this connector and then attempt to configure + * remaining connectors + */ + drm_client_for_each_modeset(modeset, client) { + crtc = modeset->crtc; + + if (!connector_has_possible_crtc(connector, crtc)) + continue; + + for (o = 0; o < n; o++) + if (best_crtcs[o] == crtc) + break; + + if (o < n) { + /* ignore cloning unless only a single crtc */ + if (dev->mode_config.num_crtc > 1) + continue; + + if (!drm_mode_equal(modes[o], modes[n])) + continue; + } + + crtcs[n] = crtc; + memcpy(crtcs, best_crtcs, n * sizeof(*crtcs)); + score = my_score + drm_client_pick_crtcs(client, connectors, connector_count, + crtcs, modes, n + 1, width, height); + if (score > best_score) { + best_score = score; + memcpy(best_crtcs, crtcs, connector_count * sizeof(*crtcs)); + } + } + + kfree(crtcs); + return best_score; +} + +/* Try to read the BIOS display configuration and use it for the initial config */ +static bool drm_client_firmware_config(struct drm_client_dev *client, + struct drm_connector **connectors, + unsigned int connector_count, + struct drm_crtc **crtcs, + struct drm_display_mode **modes, + struct drm_client_offset *offsets, + bool *enabled, int width, int height) +{ + unsigned int count = min_t(unsigned int, connector_count, BITS_PER_LONG); + unsigned long conn_configured, conn_seq, mask; + struct drm_device *dev = client->dev; + int i, j; + bool *save_enabled; + bool fallback = true, ret = true; + int num_connectors_enabled = 0; + int num_connectors_detected = 0; + struct drm_modeset_acquire_ctx ctx; + + if (!drm_drv_uses_atomic_modeset(dev)) + return false; + + save_enabled = kcalloc(count, sizeof(bool), GFP_KERNEL); + if (!save_enabled) + return false; + + drm_modeset_acquire_init(&ctx, 0); + + while (drm_modeset_lock_all_ctx(dev, &ctx) != 0) + drm_modeset_backoff(&ctx); + + memcpy(save_enabled, enabled, count); + mask = GENMASK(count - 1, 0); + conn_configured = 0; +retry: + conn_seq = conn_configured; + for (i = 0; i < count; i++) { + struct drm_connector *connector; + struct drm_encoder *encoder; + struct drm_crtc *new_crtc; + + connector = connectors[i]; + + if (conn_configured & BIT(i)) + continue; + + if (conn_seq == 0 && !connector->has_tile) + continue; + + if (connector->status == connector_status_connected) + num_connectors_detected++; + + if (!enabled[i]) { + DRM_DEBUG_KMS("connector %s not enabled, skipping\n", + connector->name); + conn_configured |= BIT(i); + continue; + } + + if (connector->force == DRM_FORCE_OFF) { + DRM_DEBUG_KMS("connector %s is disabled by user, skipping\n", + connector->name); + enabled[i] = false; + continue; + } + + encoder = connector->state->best_encoder; + if (!encoder || WARN_ON(!connector->state->crtc)) { + if (connector->force > DRM_FORCE_OFF) + goto bail; + + DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n", + connector->name); + enabled[i] = false; + conn_configured |= BIT(i); + continue; + } + + num_connectors_enabled++; + + new_crtc = connector->state->crtc; + + /* + * Make sure we're not trying to drive multiple connectors + * with a single CRTC, since our cloning support may not + * match the BIOS. + */ + for (j = 0; j < count; j++) { + if (crtcs[j] == new_crtc) { + DRM_DEBUG_KMS("fallback: cloned configuration\n"); + goto bail; + } + } + + DRM_DEBUG_KMS("looking for cmdline mode on connector %s\n", + connector->name); + + /* go for command line mode first */ + modes[i] = drm_connector_pick_cmdline_mode(connector); + + /* try for preferred next */ + if (!modes[i]) { + DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n", + connector->name, connector->has_tile); + modes[i] = drm_connector_has_preferred_mode(connector, width, height); + } + + /* No preferred mode marked by the EDID? Are there any modes? */ + if (!modes[i] && !list_empty(&connector->modes)) { + DRM_DEBUG_KMS("using first mode listed on connector %s\n", + connector->name); + modes[i] = list_first_entry(&connector->modes, + struct drm_display_mode, + head); + } + + /* last resort: use current mode */ + if (!modes[i]) { + /* + * IMPORTANT: We want to use the adjusted mode (i.e. + * after the panel fitter upscaling) as the initial + * config, not the input mode, which is what crtc->mode + * usually contains. But since our current + * code puts a mode derived from the post-pfit timings + * into crtc->mode this works out correctly. + * + * This is crtc->mode and not crtc->state->mode for the + * fastboot check to work correctly. + */ + DRM_DEBUG_KMS("looking for current mode on connector %s\n", + connector->name); + modes[i] = &connector->state->crtc->mode; + } + crtcs[i] = new_crtc; + + DRM_DEBUG_KMS("connector %s on [CRTC:%d:%s]: %dx%d%s\n", + connector->name, + connector->state->crtc->base.id, + connector->state->crtc->name, + modes[i]->hdisplay, modes[i]->vdisplay, + modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" : ""); + + fallback = false; + conn_configured |= BIT(i); + } + + if ((conn_configured & mask) != mask && conn_configured != conn_seq) + goto retry; + + /* + * If the BIOS didn't enable everything it could, fall back to have the + * same user experiencing of lighting up as much as possible like the + * fbdev helper library. + */ + if (num_connectors_enabled != num_connectors_detected && + num_connectors_enabled < dev->mode_config.num_crtc) { + DRM_DEBUG_KMS("fallback: Not all outputs enabled\n"); + DRM_DEBUG_KMS("Enabled: %i, detected: %i\n", num_connectors_enabled, + num_connectors_detected); + fallback = true; + } + + if (fallback) { +bail: + DRM_DEBUG_KMS("Not using firmware configuration\n"); + memcpy(enabled, save_enabled, count); + ret = false; + } + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + + kfree(save_enabled); + return ret; +} + +/** + * drm_client_modeset_probe() - Probe for displays + * @client: DRM client + * @width: Maximum display mode width (optional) + * @height: Maximum display mode height (optional) + * + * This function sets up display pipelines for enabled connectors and stores the + * config in the client's modeset array. + * + * Returns: + * Zero on success or negative error code on failure. + */ +int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, unsigned int height) +{ + struct drm_connector *connector, **connectors = NULL; + struct drm_connector_list_iter conn_iter; + struct drm_device *dev = client->dev; + unsigned int total_modes_count = 0; + struct drm_client_offset *offsets; + unsigned int connector_count = 0; + struct drm_display_mode **modes; + struct drm_crtc **crtcs; + int i, ret = 0; + bool *enabled; + + DRM_DEBUG_KMS("\n"); + + if (!width) + width = dev->mode_config.max_width; + if (!height) + height = dev->mode_config.max_height; + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_client_for_each_connector_iter(connector, &conn_iter) { + struct drm_connector **tmp; + + tmp = krealloc(connectors, (connector_count + 1) * sizeof(*connectors), GFP_KERNEL); + if (!tmp) { + ret = -ENOMEM; + goto free_connectors; + } + + connectors = tmp; + drm_connector_get(connector); + connectors[connector_count++] = connector; + } + drm_connector_list_iter_end(&conn_iter); + + if (!connector_count) + return 0; + + crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL); + modes = kcalloc(connector_count, sizeof(*modes), GFP_KERNEL); + offsets = kcalloc(connector_count, sizeof(*offsets), GFP_KERNEL); + enabled = kcalloc(connector_count, sizeof(bool), GFP_KERNEL); + if (!crtcs || !modes || !enabled || !offsets) { + DRM_ERROR("Memory allocation failed\n"); + ret = -ENOMEM; + goto out; + } + + mutex_lock(&client->modeset_mutex); + + mutex_lock(&dev->mode_config.mutex); + for (i = 0; i < connector_count; i++) + total_modes_count += connectors[i]->funcs->fill_modes(connectors[i], width, height); + if (!total_modes_count) + DRM_DEBUG_KMS("No connectors reported connected with modes\n"); + drm_client_connectors_enabled(connectors, connector_count, enabled); + + if (!drm_client_firmware_config(client, connectors, connector_count, crtcs, + modes, offsets, enabled, width, height)) { + memset(modes, 0, connector_count * sizeof(*modes)); + memset(crtcs, 0, connector_count * sizeof(*crtcs)); + memset(offsets, 0, connector_count * sizeof(*offsets)); + + if (!drm_client_target_cloned(dev, connectors, connector_count, modes, + offsets, enabled, width, height) && + !drm_client_target_preferred(connectors, connector_count, modes, + offsets, enabled, width, height)) + DRM_ERROR("Unable to find initial modes\n"); + + DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", + width, height); + + drm_client_pick_crtcs(client, connectors, connector_count, + crtcs, modes, 0, width, height); + } + mutex_unlock(&dev->mode_config.mutex); + + drm_client_modeset_release(client); + + for (i = 0; i < connector_count; i++) { + struct drm_display_mode *mode = modes[i]; + struct drm_crtc *crtc = crtcs[i]; + struct drm_client_offset *offset = &offsets[i]; + + if (mode && crtc) { + struct drm_mode_set *modeset = drm_client_find_modeset(client, crtc); + struct drm_connector *connector = connectors[i]; + + DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n", + mode->name, crtc->base.id, offset->x, offset->y); + + if (WARN_ON_ONCE(modeset->num_connectors == DRM_CLIENT_MAX_CLONED_CONNECTORS || + (dev->mode_config.num_crtc > 1 && modeset->num_connectors == 1))) { + ret = -EINVAL; + break; + } + + modeset->mode = drm_mode_duplicate(dev, mode); + drm_connector_get(connector); + modeset->connectors[modeset->num_connectors++] = connector; + modeset->x = offset->x; + modeset->y = offset->y; + } + } + + mutex_unlock(&client->modeset_mutex); +out: + kfree(crtcs); + kfree(modes); + kfree(offsets); + kfree(enabled); +free_connectors: + for (i = 0; i < connector_count; i++) + drm_connector_put(connectors[i]); + kfree(connectors); + + return ret; +} +EXPORT_SYMBOL(drm_client_modeset_probe); /** * drm_client_panel_rotation() - Check panel orientation diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index e7f41e5d924c..946cae196475 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -48,10 +48,6 @@ #include "drm_internal.h" -struct drm_client_offset { - int x, y; -}; - static bool drm_fbdev_emulation = true; module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600); MODULE_PARM_DESC(fbdev_emulation, @@ -1703,694 +1699,6 @@ void drm_fb_helper_fill_info(struct fb_info *info, } EXPORT_SYMBOL(drm_fb_helper_fill_info); -static struct drm_display_mode * -drm_connector_has_preferred_mode(struct drm_connector *connector, int width, int height) -{ - struct drm_display_mode *mode; - - list_for_each_entry(mode, &connector->modes, head) { - if (mode->hdisplay > width || - mode->vdisplay > height) - continue; - if (mode->type & DRM_MODE_TYPE_PREFERRED) - return mode; - } - return NULL; -} - -static struct drm_display_mode * -drm_connector_pick_cmdline_mode(struct drm_connector *connector) -{ - struct drm_cmdline_mode *cmdline_mode; - struct drm_display_mode *mode; - bool prefer_non_interlace; - - cmdline_mode = &connector->cmdline_mode; - if (cmdline_mode->specified == false) - return NULL; - - /* attempt to find a matching mode in the list of modes - * we have gotten so far, if not add a CVT mode that conforms - */ - if (cmdline_mode->rb || cmdline_mode->margins) - goto create_mode; - - prefer_non_interlace = !cmdline_mode->interlace; -again: - list_for_each_entry(mode, &connector->modes, head) { - /* check width/height */ - if (mode->hdisplay != cmdline_mode->xres || - mode->vdisplay != cmdline_mode->yres) - continue; - - if (cmdline_mode->refresh_specified) { - if (mode->vrefresh != cmdline_mode->refresh) - continue; - } - - if (cmdline_mode->interlace) { - if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) - continue; - } else if (prefer_non_interlace) { - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - continue; - } - return mode; - } - - if (prefer_non_interlace) { - prefer_non_interlace = false; - goto again; - } - -create_mode: - mode = drm_mode_create_from_cmdline_mode(connector->dev, cmdline_mode); - list_add(&mode->head, &connector->modes); - - return mode; -} - -static bool drm_connector_enabled(struct drm_connector *connector, bool strict) -{ - bool enable; - - if (connector->display_info.non_desktop) - return false; - - if (strict) - enable = connector->status == connector_status_connected; - else - enable = connector->status != connector_status_disconnected; - - return enable; -} - -static void drm_client_connectors_enabled(struct drm_connector **connectors, - unsigned int connector_count, - bool *enabled) -{ - bool any_enabled = false; - struct drm_connector *connector; - int i = 0; - - for (i = 0; i < connector_count; i++) { - connector = connectors[i]; - enabled[i] = drm_connector_enabled(connector, true); - DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, - connector->display_info.non_desktop ? "non desktop" : enabled[i] ? "yes" : "no"); - - any_enabled |= enabled[i]; - } - - if (any_enabled) - return; - - for (i = 0; i < connector_count; i++) - enabled[i] = drm_connector_enabled(connectors[i], false); -} - -static bool drm_client_target_cloned(struct drm_device *dev, - struct drm_connector **connectors, - unsigned int connector_count, - struct drm_display_mode **modes, - struct drm_client_offset *offsets, - bool *enabled, int width, int height) -{ - int count, i, j; - bool can_clone = false; - struct drm_display_mode *dmt_mode, *mode; - - /* only contemplate cloning in the single crtc case */ - if (dev->mode_config.num_crtc > 1) - return false; - - count = 0; - for (i = 0; i < connector_count; i++) { - if (enabled[i]) - count++; - } - - /* only contemplate cloning if more than one connector is enabled */ - if (count <= 1) - return false; - - /* check the command line or if nothing common pick 1024x768 */ - can_clone = true; - for (i = 0; i < connector_count; i++) { - if (!enabled[i]) - continue; - modes[i] = drm_connector_pick_cmdline_mode(connectors[i]); - if (!modes[i]) { - can_clone = false; - break; - } - for (j = 0; j < i; j++) { - if (!enabled[j]) - continue; - if (!drm_mode_match(modes[j], modes[i], - DRM_MODE_MATCH_TIMINGS | - DRM_MODE_MATCH_CLOCK | - DRM_MODE_MATCH_FLAGS | - DRM_MODE_MATCH_3D_FLAGS)) - can_clone = false; - } - } - - if (can_clone) { - DRM_DEBUG_KMS("can clone using command line\n"); - return true; - } - - /* try and find a 1024x768 mode on each connector */ - can_clone = true; - dmt_mode = drm_mode_find_dmt(dev, 1024, 768, 60, false); - - for (i = 0; i < connector_count; i++) { - if (!enabled[i]) - continue; - - list_for_each_entry(mode, &connectors[i]->modes, head) { - if (drm_mode_match(mode, dmt_mode, - DRM_MODE_MATCH_TIMINGS | - DRM_MODE_MATCH_CLOCK | - DRM_MODE_MATCH_FLAGS | - DRM_MODE_MATCH_3D_FLAGS)) - modes[i] = mode; - } - if (!modes[i]) - can_clone = false; - } - - if (can_clone) { - DRM_DEBUG_KMS("can clone using 1024x768\n"); - return true; - } - DRM_INFO("kms: can't enable cloning when we probably wanted to.\n"); - return false; -} - -static int drm_client_get_tile_offsets(struct drm_connector **connectors, - unsigned int connector_count, - struct drm_display_mode **modes, - struct drm_client_offset *offsets, - int idx, - int h_idx, int v_idx) -{ - struct drm_connector *connector; - int i; - int hoffset = 0, voffset = 0; - - for (i = 0; i < connector_count; i++) { - connector = connectors[i]; - if (!connector->has_tile) - continue; - - if (!modes[i] && (h_idx || v_idx)) { - DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i, - connector->base.id); - continue; - } - if (connector->tile_h_loc < h_idx) - hoffset += modes[i]->hdisplay; - - if (connector->tile_v_loc < v_idx) - voffset += modes[i]->vdisplay; - } - offsets[idx].x = hoffset; - offsets[idx].y = voffset; - DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx); - return 0; -} - -static bool drm_client_target_preferred(struct drm_connector **connectors, - unsigned int connector_count, - struct drm_display_mode **modes, - struct drm_client_offset *offsets, - bool *enabled, int width, int height) -{ - const u64 mask = BIT_ULL(connector_count) - 1; - struct drm_connector *connector; - u64 conn_configured = 0; - int tile_pass = 0; - int i; - -retry: - for (i = 0; i < connector_count; i++) { - connector = connectors[i]; - - if (conn_configured & BIT_ULL(i)) - continue; - - if (enabled[i] == false) { - conn_configured |= BIT_ULL(i); - continue; - } - - /* first pass over all the untiled connectors */ - if (tile_pass == 0 && connector->has_tile) - continue; - - if (tile_pass == 1) { - if (connector->tile_h_loc != 0 || - connector->tile_v_loc != 0) - continue; - - } else { - if (connector->tile_h_loc != tile_pass - 1 && - connector->tile_v_loc != tile_pass - 1) - /* if this tile_pass doesn't cover any of the tiles - keep going */ - continue; - - /* - * find the tile offsets for this pass - need to find - * all tiles left and above - */ - drm_client_get_tile_offsets(connectors, connector_count, modes, offsets, i, - connector->tile_h_loc, connector->tile_v_loc); - } - DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", - connector->base.id); - - /* got for command line mode first */ - modes[i] = drm_connector_pick_cmdline_mode(connector); - if (!modes[i]) { - DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n", - connector->base.id, connector->tile_group ? connector->tile_group->id : 0); - modes[i] = drm_connector_has_preferred_mode(connector, width, height); - } - /* No preferred modes, pick one off the list */ - if (!modes[i] && !list_empty(&connector->modes)) { - list_for_each_entry(modes[i], &connector->modes, head) - break; - } - DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : - "none"); - conn_configured |= BIT_ULL(i); - } - - if ((conn_configured & mask) != mask) { - tile_pass++; - goto retry; - } - return true; -} - -static bool connector_has_possible_crtc(struct drm_connector *connector, - struct drm_crtc *crtc) -{ - struct drm_encoder *encoder; - int i; - - drm_connector_for_each_possible_encoder(connector, encoder, i) { - if (encoder->possible_crtcs & drm_crtc_mask(crtc)) - return true; - } - - return false; -} - -static int drm_client_pick_crtcs(struct drm_client_dev *client, - struct drm_connector **connectors, - unsigned int connector_count, - struct drm_crtc **best_crtcs, - struct drm_display_mode **modes, - int n, int width, int height) -{ - struct drm_device *dev = client->dev; - struct drm_connector *connector; - int my_score, best_score, score; - struct drm_crtc **crtcs, *crtc; - struct drm_mode_set *modeset; - int o; - - if (n == connector_count) - return 0; - - connector = connectors[n]; - - best_crtcs[n] = NULL; - best_score = drm_client_pick_crtcs(client, connectors, connector_count, - best_crtcs, modes, n + 1, width, height); - if (modes[n] == NULL) - return best_score; - - crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL); - if (!crtcs) - return best_score; - - my_score = 1; - if (connector->status == connector_status_connected) - my_score++; - if (connector->cmdline_mode.specified) - my_score++; - if (drm_connector_has_preferred_mode(connector, width, height)) - my_score++; - - /* - * select a crtc for this connector and then attempt to configure - * remaining connectors - */ - drm_client_for_each_modeset(modeset, client) { - crtc = modeset->crtc; - - if (!connector_has_possible_crtc(connector, crtc)) - continue; - - for (o = 0; o < n; o++) - if (best_crtcs[o] == crtc) - break; - - if (o < n) { - /* ignore cloning unless only a single crtc */ - if (dev->mode_config.num_crtc > 1) - continue; - - if (!drm_mode_equal(modes[o], modes[n])) - continue; - } - - crtcs[n] = crtc; - memcpy(crtcs, best_crtcs, n * sizeof(*crtcs)); - score = my_score + drm_client_pick_crtcs(client, connectors, connector_count, - crtcs, modes, n + 1, width, height); - if (score > best_score) { - best_score = score; - memcpy(best_crtcs, crtcs, connector_count * sizeof(*crtcs)); - } - } - - kfree(crtcs); - return best_score; -} - -/* Try to read the BIOS display configuration and use it for the initial config */ -static bool drm_client_firmware_config(struct drm_client_dev *client, - struct drm_connector **connectors, - unsigned int connector_count, - struct drm_crtc **crtcs, - struct drm_display_mode **modes, - struct drm_client_offset *offsets, - bool *enabled, int width, int height) -{ - unsigned int count = min_t(unsigned int, connector_count, BITS_PER_LONG); - unsigned long conn_configured, conn_seq, mask; - struct drm_device *dev = client->dev; - int i, j; - bool *save_enabled; - bool fallback = true, ret = true; - int num_connectors_enabled = 0; - int num_connectors_detected = 0; - struct drm_modeset_acquire_ctx ctx; - - if (!drm_drv_uses_atomic_modeset(dev)) - return false; - - save_enabled = kcalloc(count, sizeof(bool), GFP_KERNEL); - if (!save_enabled) - return false; - - drm_modeset_acquire_init(&ctx, 0); - - while (drm_modeset_lock_all_ctx(dev, &ctx) != 0) - drm_modeset_backoff(&ctx); - - memcpy(save_enabled, enabled, count); - mask = GENMASK(count - 1, 0); - conn_configured = 0; -retry: - conn_seq = conn_configured; - for (i = 0; i < count; i++) { - struct drm_connector *connector; - struct drm_encoder *encoder; - struct drm_crtc *new_crtc; - - connector = connectors[i]; - - if (conn_configured & BIT(i)) - continue; - - if (conn_seq == 0 && !connector->has_tile) - continue; - - if (connector->status == connector_status_connected) - num_connectors_detected++; - - if (!enabled[i]) { - DRM_DEBUG_KMS("connector %s not enabled, skipping\n", - connector->name); - conn_configured |= BIT(i); - continue; - } - - if (connector->force == DRM_FORCE_OFF) { - DRM_DEBUG_KMS("connector %s is disabled by user, skipping\n", - connector->name); - enabled[i] = false; - continue; - } - - encoder = connector->state->best_encoder; - if (!encoder || WARN_ON(!connector->state->crtc)) { - if (connector->force > DRM_FORCE_OFF) - goto bail; - - DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n", - connector->name); - enabled[i] = false; - conn_configured |= BIT(i); - continue; - } - - num_connectors_enabled++; - - new_crtc = connector->state->crtc; - - /* - * Make sure we're not trying to drive multiple connectors - * with a single CRTC, since our cloning support may not - * match the BIOS. - */ - for (j = 0; j < count; j++) { - if (crtcs[j] == new_crtc) { - DRM_DEBUG_KMS("fallback: cloned configuration\n"); - goto bail; - } - } - - DRM_DEBUG_KMS("looking for cmdline mode on connector %s\n", - connector->name); - - /* go for command line mode first */ - modes[i] = drm_connector_pick_cmdline_mode(connector); - - /* try for preferred next */ - if (!modes[i]) { - DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n", - connector->name, connector->has_tile); - modes[i] = drm_connector_has_preferred_mode(connector, width, height); - } - - /* No preferred mode marked by the EDID? Are there any modes? */ - if (!modes[i] && !list_empty(&connector->modes)) { - DRM_DEBUG_KMS("using first mode listed on connector %s\n", - connector->name); - modes[i] = list_first_entry(&connector->modes, - struct drm_display_mode, - head); - } - - /* last resort: use current mode */ - if (!modes[i]) { - /* - * IMPORTANT: We want to use the adjusted mode (i.e. - * after the panel fitter upscaling) as the initial - * config, not the input mode, which is what crtc->mode - * usually contains. But since our current - * code puts a mode derived from the post-pfit timings - * into crtc->mode this works out correctly. - * - * This is crtc->mode and not crtc->state->mode for the - * fastboot check to work correctly. - */ - DRM_DEBUG_KMS("looking for current mode on connector %s\n", - connector->name); - modes[i] = &connector->state->crtc->mode; - } - crtcs[i] = new_crtc; - - DRM_DEBUG_KMS("connector %s on [CRTC:%d:%s]: %dx%d%s\n", - connector->name, - connector->state->crtc->base.id, - connector->state->crtc->name, - modes[i]->hdisplay, modes[i]->vdisplay, - modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" : ""); - - fallback = false; - conn_configured |= BIT(i); - } - - if ((conn_configured & mask) != mask && conn_configured != conn_seq) - goto retry; - - /* - * If the BIOS didn't enable everything it could, fall back to have the - * same user experiencing of lighting up as much as possible like the - * fbdev helper library. - */ - if (num_connectors_enabled != num_connectors_detected && - num_connectors_enabled < dev->mode_config.num_crtc) { - DRM_DEBUG_KMS("fallback: Not all outputs enabled\n"); - DRM_DEBUG_KMS("Enabled: %i, detected: %i\n", num_connectors_enabled, - num_connectors_detected); - fallback = true; - } - - if (fallback) { -bail: - DRM_DEBUG_KMS("Not using firmware configuration\n"); - memcpy(enabled, save_enabled, count); - ret = false; - } - - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); - - kfree(save_enabled); - return ret; -} - -/** - * drm_client_modeset_probe() - Probe for displays - * @client: DRM client - * @width: Maximum display mode width (optional) - * @height: Maximum display mode height (optional) - * - * This function sets up display pipelines for enabled connectors and stores the - * config in the client's modeset array. - * - * Returns: - * Zero on success or negative error code on failure. - */ -int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, unsigned int height) -{ - struct drm_connector *connector, **connectors = NULL; - struct drm_connector_list_iter conn_iter; - struct drm_device *dev = client->dev; - unsigned int total_modes_count = 0; - struct drm_client_offset *offsets; - unsigned int connector_count = 0; - struct drm_display_mode **modes; - struct drm_crtc **crtcs; - int i, ret = 0; - bool *enabled; - - DRM_DEBUG_KMS("\n"); - - if (!width) - width = dev->mode_config.max_width; - if (!height) - height = dev->mode_config.max_height; - - drm_connector_list_iter_begin(dev, &conn_iter); - drm_client_for_each_connector_iter(connector, &conn_iter) { - struct drm_connector **tmp; - - tmp = krealloc(connectors, (connector_count + 1) * sizeof(*connectors), GFP_KERNEL); - if (!tmp) { - ret = -ENOMEM; - goto free_connectors; - } - - connectors = tmp; - drm_connector_get(connector); - connectors[connector_count++] = connector; - } - drm_connector_list_iter_end(&conn_iter); - - if (!connector_count) - return 0; - - crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL); - modes = kcalloc(connector_count, sizeof(*modes), GFP_KERNEL); - offsets = kcalloc(connector_count, sizeof(*offsets), GFP_KERNEL); - enabled = kcalloc(connector_count, sizeof(bool), GFP_KERNEL); - if (!crtcs || !modes || !enabled || !offsets) { - DRM_ERROR("Memory allocation failed\n"); - ret = -ENOMEM; - goto out; - } - - mutex_lock(&client->modeset_mutex); - - mutex_lock(&dev->mode_config.mutex); - for (i = 0; i < connector_count; i++) - total_modes_count += connectors[i]->funcs->fill_modes(connectors[i], width, height); - if (!total_modes_count) - DRM_DEBUG_KMS("No connectors reported connected with modes\n"); - drm_client_connectors_enabled(connectors, connector_count, enabled); - - if (!drm_client_firmware_config(client, connectors, connector_count, crtcs, - modes, offsets, enabled, width, height)) { - memset(modes, 0, connector_count * sizeof(*modes)); - memset(crtcs, 0, connector_count * sizeof(*crtcs)); - memset(offsets, 0, connector_count * sizeof(*offsets)); - - if (!drm_client_target_cloned(dev, connectors, connector_count, modes, - offsets, enabled, width, height) && - !drm_client_target_preferred(connectors, connector_count, modes, - offsets, enabled, width, height)) - DRM_ERROR("Unable to find initial modes\n"); - - DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", - width, height); - - drm_client_pick_crtcs(client, connectors, connector_count, - crtcs, modes, 0, width, height); - } - mutex_unlock(&dev->mode_config.mutex); - - drm_client_modeset_release(client); - - for (i = 0; i < connector_count; i++) { - struct drm_display_mode *mode = modes[i]; - struct drm_crtc *crtc = crtcs[i]; - struct drm_client_offset *offset = &offsets[i]; - - if (mode && crtc) { - struct drm_mode_set *modeset = drm_client_find_modeset(client, crtc); - struct drm_connector *connector = connectors[i]; - - DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n", - mode->name, crtc->base.id, offset->x, offset->y); - - if (WARN_ON_ONCE(modeset->num_connectors == DRM_CLIENT_MAX_CLONED_CONNECTORS || - (dev->mode_config.num_crtc > 1 && modeset->num_connectors == 1))) { - ret = -EINVAL; - break; - } - - modeset->mode = drm_mode_duplicate(dev, mode); - drm_connector_get(connector); - modeset->connectors[modeset->num_connectors++] = connector; - modeset->x = offset->x; - modeset->y = offset->y; - } - } - - mutex_unlock(&client->modeset_mutex); -out: - kfree(crtcs); - kfree(modes); - kfree(offsets); - kfree(enabled); -free_connectors: - for (i = 0; i < connector_count; i++) - drm_connector_put(connectors[i]); - kfree(connectors); - - return ret; -} - /* * This is a continuation of drm_setup_crtcs() that sets up anything related * to the framebuffer. During initialization, drm_setup_crtcs() is called before diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index 8d94880bbe25..f2d5ed745733 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -18,8 +18,6 @@ struct drm_gem_object; struct drm_minor; struct module; -#define DRM_CLIENT_MAX_CLONED_CONNECTORS 8 - /** * struct drm_client_funcs - DRM client callbacks */ @@ -154,8 +152,7 @@ void drm_client_framebuffer_delete(struct drm_client_buffer *buffer); int drm_client_modeset_create(struct drm_client_dev *client); void drm_client_modeset_free(struct drm_client_dev *client); -void drm_client_modeset_release(struct drm_client_dev *client); -struct drm_mode_set *drm_client_find_modeset(struct drm_client_dev *client, struct drm_crtc *crtc); +int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, unsigned int height); bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotation); int drm_client_modeset_commit_force(struct drm_client_dev *client); int drm_client_modeset_commit(struct drm_client_dev *client); -- cgit v1.2.3 From ecf79e7ca1565e4d36482aa4d2950bc397313425 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Jun 2019 13:28:59 +0200 Subject: drm/fb: document dirty helper better MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently little known fact that there's no need to hand-roll your own anymore. Cc'ing a bunch of driver people who might want to know this too. v2: s/none/known/ (Chris Wilson) Cc: Rob Clark Cc: Sebastian Reichel Cc: Tomi Valkeinen Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Sean Paul Cc: Gerd Hoffmann Cc: David Lechner Cc: Noralf Trønnes Cc: Hans de Goede Reviewed-by: Noralf Trønnes Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20190611112859.16375-1-daniel.vetter@ffwll.ch --- include/drm/drm_framebuffer.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/drm') diff --git a/include/drm/drm_framebuffer.h b/include/drm/drm_framebuffer.h index c23016748e3f..c0e0256e3e98 100644 --- a/include/drm/drm_framebuffer.h +++ b/include/drm/drm_framebuffer.h @@ -87,6 +87,9 @@ struct drm_framebuffer_funcs { * for more information as all the semantics and arguments have a one to * one mapping on this function. * + * Atomic drivers should use drm_atomic_helper_dirtyfb() to implement + * this hook. + * * RETURNS: * * 0 on success or a negative error code on failure. -- cgit v1.2.3 From 99d02ed523dcaf5be0c6a778708df2779cfb3ed6 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Tue, 4 Jun 2019 13:42:06 -0700 Subject: drm: bridge: dw-hdmi: Add hook for resume On Rockchip rk3288-based Chromebooks when you do a suspend/resume cycle: 1. You lose the ability to detect an HDMI device being plugged in. 2. If you're using the i2c bus built in to dw_hdmi then it stops working. Let's add a hook to the core dw-hdmi driver so that we can call it in dw_hdmi-rockchip in the next commit. NOTE: the exact set of steps I've done here in resume come from looking at the normal dw_hdmi init sequence in upstream Linux plus the sequence that we did in downstream Chrome OS 3.14. Testing show that it seems to work, but if an extra step is needed or something here is not needed we could improve it. As part of this change we'll refactor the hardware init bits of dw-hdmi to happen all in one function and all at the same time. Since we need to init the interrupt mutes before we request the IRQ, this means moving the hardware init earlier in the function, but there should be no problems with that. Also as part of this we now unconditionally init the "i2c" parts of dw-hdmi, but again that ought to be fine. Signed-off-by: Douglas Anderson Reviewed-by: Laurent Pinchart Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190604204207.168085-1-dianders@chromium.org --- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 48 +++++++++++++++++++------------ include/drm/bridge/dw_hdmi.h | 2 ++ 2 files changed, 31 insertions(+), 19 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 3a6320108575..f6c89007d3bb 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -233,6 +233,13 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi) { + hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL, + HDMI_PHY_I2CM_INT_ADDR); + + hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL | + HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL, + HDMI_PHY_I2CM_CTLINT_ADDR); + /* Software reset */ hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SOFTRSTZ); @@ -1994,16 +2001,6 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) return 0; } -static void dw_hdmi_setup_i2c(struct dw_hdmi *hdmi) -{ - hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL, - HDMI_PHY_I2CM_INT_ADDR); - - hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL | - HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL, - HDMI_PHY_I2CM_CTLINT_ADDR); -} - static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi) { u8 ih_mute; @@ -2504,6 +2501,21 @@ static const struct regmap_config hdmi_regmap_32bit_config = { .max_register = HDMI_I2CM_FS_SCL_LCNT_0_ADDR << 2, }; +static void dw_hdmi_init_hw(struct dw_hdmi *hdmi) +{ + initialize_hdmi_ih_mutes(hdmi); + + /* + * Reset HDMI DDC I2C master controller and mute I2CM interrupts. + * Even if we are using a separate i2c adapter doing this doesn't + * hurt. + */ + dw_hdmi_i2c_init(hdmi); + + if (hdmi->phy.ops->setup_hpd) + hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); +} + static struct dw_hdmi * __dw_hdmi_probe(struct platform_device *pdev, const struct dw_hdmi_plat_data *plat_data) @@ -2655,7 +2667,7 @@ __dw_hdmi_probe(struct platform_device *pdev, prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without", hdmi->phy.name); - initialize_hdmi_ih_mutes(hdmi); + dw_hdmi_init_hw(hdmi); irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -2712,10 +2724,6 @@ __dw_hdmi_probe(struct platform_device *pdev, hdmi->bridge.of_node = pdev->dev.of_node; #endif - dw_hdmi_setup_i2c(hdmi); - if (hdmi->phy.ops->setup_hpd) - hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); - memset(&pdevinfo, 0, sizeof(pdevinfo)); pdevinfo.parent = dev; pdevinfo.id = PLATFORM_DEVID_AUTO; @@ -2768,10 +2776,6 @@ __dw_hdmi_probe(struct platform_device *pdev, hdmi->cec = platform_device_register_full(&pdevinfo); } - /* Reset HDMI DDC I2C master controller and mute I2CM interrupts */ - if (hdmi->i2c) - dw_hdmi_i2c_init(hdmi); - return hdmi; err_iahb: @@ -2875,6 +2879,12 @@ void dw_hdmi_unbind(struct dw_hdmi *hdmi) } EXPORT_SYMBOL_GPL(dw_hdmi_unbind); +void dw_hdmi_resume(struct dw_hdmi *hdmi) +{ + dw_hdmi_init_hw(hdmi); +} +EXPORT_SYMBOL_GPL(dw_hdmi_resume); + MODULE_AUTHOR("Sascha Hauer "); MODULE_AUTHOR("Andy Yan "); MODULE_AUTHOR("Yakir Yang "); diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index 66e70770cce5..601243b56b69 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -154,6 +154,8 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, const struct dw_hdmi_plat_data *plat_data); +void dw_hdmi_resume(struct dw_hdmi *hdmi); + void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense); void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate); -- cgit v1.2.3 From a3e69b86cf04b7c0b624572c181678ea74e83400 Mon Sep 17 00:00:00 2001 From: Yannick Fertré Date: Mon, 27 May 2019 12:21:38 +0200 Subject: drm/bridge/synopsys: dsi: add power on/off optional phy ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add power on & off optional physical operation functions, helpful to program specific registers of the DSI physical part. Signed-off-by: Yannick Fertré Reviewed-by: Philippe Cornu Tested-by: Philippe Cornu Reviewed-by: Andrzej Hajda Signed-off-by: Andrzej Hajda Link: https://patchwork.freedesktop.org/patch/msgid/1558952499-15418-2-git-send-email-yannick.fertre@st.com --- drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 8 ++++++++ include/drm/bridge/dw_mipi_dsi.h | 2 ++ 2 files changed, 10 insertions(+) (limited to 'include/drm') diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index a79c87bd0147..281c58bab1a1 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c @@ -778,6 +778,10 @@ static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi) static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge) { struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); + const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops; + + if (phy_ops->power_off) + phy_ops->power_off(dsi->plat_data->priv_data); /* * Switch to command mode before panel-bridge post_disable & @@ -877,11 +881,15 @@ static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge, static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge) { struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); + const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops; /* Switch to video mode for panel-bridge enable & panel enable */ dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO); if (dsi->slave) dw_mipi_dsi_set_mode(dsi->slave, MIPI_DSI_MODE_VIDEO); + + if (phy_ops->power_on) + phy_ops->power_on(dsi->plat_data->priv_data); } static enum drm_mode_status diff --git a/include/drm/bridge/dw_mipi_dsi.h b/include/drm/bridge/dw_mipi_dsi.h index 92cf9d1e1655..be8cdc2bf67d 100644 --- a/include/drm/bridge/dw_mipi_dsi.h +++ b/include/drm/bridge/dw_mipi_dsi.h @@ -22,6 +22,8 @@ struct platform_device; struct dw_mipi_dsi_phy_ops { int (*init)(void *priv_data); + void (*power_on)(void *priv_data); + void (*power_off)(void *priv_data); int (*get_lane_mbps)(void *priv_data, const struct drm_display_mode *mode, unsigned long mode_flags, u32 lanes, u32 format, -- cgit v1.2.3 From 09cc560951dbcc74c7f9419f7cf703b57472b7a2 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Wed, 12 Jun 2019 11:00:34 -0400 Subject: drm: Tweak drm_encoder_helper_funcs.enable kerneldoc I copied the kerneldoc for encoder_funcs.atomic_enable from encoder_funcs.enable in a recent patch [1]. Sam rightly pointed out in the review that "for symmetry with" text is awkward [2]. So here's a patch to fix up the source of the awkward language. [1] https://patchwork.freedesktop.org/patch/msgid/20190611160844.257498-2-sean@poorly.run [2] https://patchwork.freedesktop.org/patch/msgid/20190611185352.GA16305@ravnborg.org Suggested-by: Sam Ravnborg Reviewed-by: Sam Ravnborg Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190612150038.194843-1-sean@poorly.run --- include/drm/drm_modeset_helper_vtables.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include/drm') diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index f9c94c2a1364..df80131bb10f 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -719,11 +719,11 @@ struct drm_encoder_helper_funcs { * hooks and call them from CRTC's callback by looping over all encoders * connected to it using for_each_encoder_on_crtc(). * - * This hook is used only by atomic helpers, for symmetry with @disable. - * Atomic drivers don't need to implement it if there's no need to - * enable anything at the encoder level. To ensure that runtime PM handling - * (using either DPMS or the new "ACTIVE" property) works - * @enable must be the inverse of @disable for atomic drivers. + * This hook is only used by atomic helpers, it is the opposite of + * @disable. Atomic drivers don't need to implement it if there's no + * need to enable anything at the encoder level. To ensure that + * runtime PM handling (using either DPMS or the new "ACTIVE" property) + * works @enable must be the inverse of @disable for atomic drivers. */ void (*enable)(struct drm_encoder *encoder); -- cgit v1.2.3 From 57c84d5c9348bda5e9129bc4e4e567546915ad8c Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 13 Jun 2019 09:30:40 +0200 Subject: drm: Remove lock interfaces from GEM VRAM helpers The lock functions and the locked-pin/unpin functions of GEM VRAM are not required any longer. Remove them. Signed-off-by: Thomas Zimmermann Acked-by: Gerd Hoffmann Link: https://patchwork.freedesktop.org/patch/msgid/20190613073041.29350-9-tzimmermann@suse.de --- drivers/gpu/drm/drm_gem_vram_helper.c | 109 ---------------------------------- include/drm/drm_gem_vram_helper.h | 5 -- 2 files changed, 114 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index f3e5803affb0..4cae52054e61 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -151,36 +151,6 @@ void drm_gem_vram_put(struct drm_gem_vram_object *gbo) } EXPORT_SYMBOL(drm_gem_vram_put); -/** - * drm_gem_vram_lock() - Locks a VRAM-backed GEM object - * @gbo: the GEM VRAM object - * @no_wait: don't wait for buffer object to become available - * - * See ttm_bo_reserve() for more information. - * - * Returns: - * 0 on success, or - * a negative error code otherwise - */ -int drm_gem_vram_lock(struct drm_gem_vram_object *gbo, bool no_wait) -{ - return ttm_bo_reserve(&gbo->bo, true, no_wait, NULL); -} -EXPORT_SYMBOL(drm_gem_vram_lock); - -/** - * drm_gem_vram_unlock() - \ - Release a reservation acquired by drm_gem_vram_lock() - * @gbo: the GEM VRAM object - * - * See ttm_bo_unreserve() for more information. - */ -void drm_gem_vram_unlock(struct drm_gem_vram_object *gbo) -{ - ttm_bo_unreserve(&gbo->bo); -} -EXPORT_SYMBOL(drm_gem_vram_unlock); - /** * drm_gem_vram_mmap_offset() - Returns a GEM VRAM object's mmap offset * @gbo: the GEM VRAM object @@ -266,49 +236,6 @@ err_ttm_bo_unreserve: } EXPORT_SYMBOL(drm_gem_vram_pin); -/** - * drm_gem_vram_pin_locked() - Pins a GEM VRAM object in a region. - * @gbo: the GEM VRAM object - * @pl_flag: a bitmask of possible memory regions - * - * Pinning a buffer object ensures that it is not evicted from - * a memory region. A pinned buffer object has to be unpinned before - * it can be pinned to another region. - * - * This function pins a GEM VRAM object that has already been - * locked. Use drm_gem_vram_pin() if possible. - * - * Returns: - * 0 on success, or - * a negative error code otherwise. - */ -int drm_gem_vram_pin_locked(struct drm_gem_vram_object *gbo, - unsigned long pl_flag) -{ - int i, ret; - struct ttm_operation_ctx ctx = { false, false }; - - lockdep_assert_held(&gbo->bo.resv->lock.base); - - if (gbo->pin_count) { - ++gbo->pin_count; - return 0; - } - - drm_gem_vram_placement(gbo, pl_flag); - for (i = 0; i < gbo->placement.num_placement; ++i) - gbo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; - - ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx); - if (ret < 0) - return ret; - - gbo->pin_count = 1; - - return 0; -} -EXPORT_SYMBOL(drm_gem_vram_pin_locked); - /** * drm_gem_vram_unpin() - Unpins a GEM VRAM object * @gbo: the GEM VRAM object @@ -351,42 +278,6 @@ err_ttm_bo_unreserve: } EXPORT_SYMBOL(drm_gem_vram_unpin); -/** - * drm_gem_vram_unpin_locked() - Unpins a GEM VRAM object - * @gbo: the GEM VRAM object - * - * This function unpins a GEM VRAM object that has already been - * locked. Use drm_gem_vram_unpin() if possible. - * - * Returns: - * 0 on success, or - * a negative error code otherwise. - */ -int drm_gem_vram_unpin_locked(struct drm_gem_vram_object *gbo) -{ - int i, ret; - struct ttm_operation_ctx ctx = { false, false }; - - lockdep_assert_held(&gbo->bo.resv->lock.base); - - if (WARN_ON_ONCE(!gbo->pin_count)) - return 0; - - --gbo->pin_count; - if (gbo->pin_count) - return 0; - - for (i = 0; i < gbo->placement.num_placement ; ++i) - gbo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; - - ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx); - if (ret < 0) - return ret; - - return 0; -} -EXPORT_SYMBOL(drm_gem_vram_unpin_locked); - /** * drm_gem_vram_kmap_at() - Maps a GEM VRAM object into kernel address space * @gbo: the GEM VRAM object diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index 4d1d2c1bf32b..1d4aa87f8dfa 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -77,15 +77,10 @@ struct drm_gem_vram_object *drm_gem_vram_create(struct drm_device *dev, unsigned long pg_align, bool interruptible); void drm_gem_vram_put(struct drm_gem_vram_object *gbo); -int drm_gem_vram_lock(struct drm_gem_vram_object *gbo, bool no_wait); -void drm_gem_vram_unlock(struct drm_gem_vram_object *gbo); u64 drm_gem_vram_mmap_offset(struct drm_gem_vram_object *gbo); s64 drm_gem_vram_offset(struct drm_gem_vram_object *gbo); int drm_gem_vram_pin(struct drm_gem_vram_object *gbo, unsigned long pl_flag); -int drm_gem_vram_pin_locked(struct drm_gem_vram_object *gbo, - unsigned long pl_flag); int drm_gem_vram_unpin(struct drm_gem_vram_object *gbo); -int drm_gem_vram_unpin_locked(struct drm_gem_vram_object *gbo); void *drm_gem_vram_kmap_at(struct drm_gem_vram_object *gbo, bool map, bool *is_iomem, struct ttm_bo_kmap_obj *kmap); void *drm_gem_vram_kmap(struct drm_gem_vram_object *gbo, bool map, -- cgit v1.2.3 From 921721738f4994b54f916f9a3bdd5dfdf26f753c Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 13 Jun 2019 09:30:41 +0200 Subject: drm: Remove functions with kmap-object argument from GEM VRAM helpers The GEM VRAM functions with kmap-object argument are not required any longer. Remove them. Signed-off-by: Thomas Zimmermann Acked-by: Gerd Hoffmann Link: https://patchwork.freedesktop.org/patch/msgid/20190613073041.29350-10-tzimmermann@suse.de --- drivers/gpu/drm/drm_gem_vram_helper.c | 50 ++++++----------------------------- include/drm/drm_gem_vram_helper.h | 4 --- 2 files changed, 8 insertions(+), 46 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index 4cae52054e61..4de782ca26b2 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -279,12 +279,11 @@ err_ttm_bo_unreserve: EXPORT_SYMBOL(drm_gem_vram_unpin); /** - * drm_gem_vram_kmap_at() - Maps a GEM VRAM object into kernel address space + * drm_gem_vram_kmap() - Maps a GEM VRAM object into kernel address space * @gbo: the GEM VRAM object * @map: establish a mapping if necessary * @is_iomem: returns true if the mapped memory is I/O memory, or false \ otherwise; can be NULL - * @kmap: the mapping's kmap object * * This function maps the buffer object into the kernel's address space * or returns the current mapping. If the parameter map is false, the @@ -296,10 +295,11 @@ EXPORT_SYMBOL(drm_gem_vram_unpin); * NULL if not mapped, or * an ERR_PTR()-encoded error code otherwise. */ -void *drm_gem_vram_kmap_at(struct drm_gem_vram_object *gbo, bool map, - bool *is_iomem, struct ttm_bo_kmap_obj *kmap) +void *drm_gem_vram_kmap(struct drm_gem_vram_object *gbo, bool map, + bool *is_iomem) { int ret; + struct ttm_bo_kmap_obj *kmap = &gbo->kmap; if (kmap->virtual || !map) goto out; @@ -317,56 +317,22 @@ out: } return ttm_kmap_obj_virtual(kmap, is_iomem); } -EXPORT_SYMBOL(drm_gem_vram_kmap_at); - -/** - * drm_gem_vram_kmap() - Maps a GEM VRAM object into kernel address space - * @gbo: the GEM VRAM object - * @map: establish a mapping if necessary - * @is_iomem: returns true if the mapped memory is I/O memory, or false \ - otherwise; can be NULL - * - * This function maps the buffer object into the kernel's address space - * or returns the current mapping. If the parameter map is false, the - * function only queries the current mapping, but does not establish a - * new one. - * - * Returns: - * The buffers virtual address if mapped, or - * NULL if not mapped, or - * an ERR_PTR()-encoded error code otherwise. - */ -void *drm_gem_vram_kmap(struct drm_gem_vram_object *gbo, bool map, - bool *is_iomem) -{ - return drm_gem_vram_kmap_at(gbo, map, is_iomem, &gbo->kmap); -} EXPORT_SYMBOL(drm_gem_vram_kmap); /** - * drm_gem_vram_kunmap_at() - Unmaps a GEM VRAM object + * drm_gem_vram_kunmap() - Unmaps a GEM VRAM object * @gbo: the GEM VRAM object - * @kmap: the mapping's kmap object */ -void drm_gem_vram_kunmap_at(struct drm_gem_vram_object *gbo, - struct ttm_bo_kmap_obj *kmap) +void drm_gem_vram_kunmap(struct drm_gem_vram_object *gbo) { + struct ttm_bo_kmap_obj *kmap = &gbo->kmap; + if (!kmap->virtual) return; ttm_bo_kunmap(kmap); kmap->virtual = NULL; } -EXPORT_SYMBOL(drm_gem_vram_kunmap_at); - -/** - * drm_gem_vram_kunmap() - Unmaps a GEM VRAM object - * @gbo: the GEM VRAM object - */ -void drm_gem_vram_kunmap(struct drm_gem_vram_object *gbo) -{ - drm_gem_vram_kunmap_at(gbo, &gbo->kmap); -} EXPORT_SYMBOL(drm_gem_vram_kunmap); /** diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index 1d4aa87f8dfa..9581ea0a4f7e 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -81,12 +81,8 @@ u64 drm_gem_vram_mmap_offset(struct drm_gem_vram_object *gbo); s64 drm_gem_vram_offset(struct drm_gem_vram_object *gbo); int drm_gem_vram_pin(struct drm_gem_vram_object *gbo, unsigned long pl_flag); int drm_gem_vram_unpin(struct drm_gem_vram_object *gbo); -void *drm_gem_vram_kmap_at(struct drm_gem_vram_object *gbo, bool map, - bool *is_iomem, struct ttm_bo_kmap_obj *kmap); void *drm_gem_vram_kmap(struct drm_gem_vram_object *gbo, bool map, bool *is_iomem); -void drm_gem_vram_kunmap_at(struct drm_gem_vram_object *gbo, - struct ttm_bo_kmap_obj *kmap); void drm_gem_vram_kunmap(struct drm_gem_vram_object *gbo); int drm_gem_vram_fill_create_dumb(struct drm_file *file, -- cgit v1.2.3 From 43c76d72ea853ccaeb22a497c13d3cd946869ec6 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Tue, 11 Jun 2019 16:49:53 -0400 Subject: drm: Add atomic variants of enable/disable to encoder helper funcs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds atomic_enable and atomic_disable callbacks to the encoder helpers. This will allow encoders to make informed decisions in their start-up/shutdown based on the committed state. Aside from the new hooks, this patch also introduces the new signature for .atomic_* functions going forward. Instead of passing object state (well, encoders don't have atomic state, but let's ignore that), we pass the entire atomic state so the driver can inspect more than what's happening locally. This is particularly important for the upcoming self refresh helpers. Changes in v3: - Added patch to the set Changes in v4: - Move atomic_disable above prepare (Daniel) - Add breadcrumb to .enable() docbook (Daniel) Changes in v5: - None Changes in v6: - Tweak kerneldoc some more (Sam) Link to v3: https://patchwork.freedesktop.org/patch/msgid/20190502194956.218441-2-sean@poorly.run Link to v4: https://patchwork.freedesktop.org/patch/msgid/20190508160920.144739-2-sean@poorly.run Link to v5: https://patchwork.freedesktop.org/patch/msgid/20190611160844.257498-2-sean@poorly.run Cc: Daniel Vetter Cc: Ville Syrjälä Cc: Sam Ravnborg Tested-by: Heiko Stuebner Reviewed-by: Daniel Vetter Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190611204959.180855-1-sean@poorly.run --- drivers/gpu/drm/drm_atomic_helper.c | 8 ++++-- include/drm/drm_modeset_helper_vtables.h | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 0fc63d682245..62e29b5ebb6e 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1002,7 +1002,9 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) /* Right function depends upon target state. */ if (funcs) { - if (new_conn_state->crtc && funcs->prepare) + if (funcs->atomic_disable) + funcs->atomic_disable(encoder, old_state); + else if (new_conn_state->crtc && funcs->prepare) funcs->prepare(encoder); else if (funcs->disable) funcs->disable(encoder); @@ -1311,7 +1313,9 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, drm_bridge_pre_enable(encoder->bridge); if (funcs) { - if (funcs->enable) + if (funcs->atomic_enable) + funcs->atomic_enable(encoder, old_state); + else if (funcs->enable) funcs->enable(encoder); else if (funcs->commit) funcs->commit(encoder); diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index df80131bb10f..13f0e3ca0a29 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -679,6 +679,52 @@ struct drm_encoder_helper_funcs { enum drm_connector_status (*detect)(struct drm_encoder *encoder, struct drm_connector *connector); + /** + * @atomic_disable: + * + * This callback should be used to disable the encoder. With the atomic + * drivers it is called before this encoder's CRTC has been shut off + * using their own &drm_crtc_helper_funcs.atomic_disable hook. If that + * sequence is too simple drivers can just add their own driver private + * encoder hooks and call them from CRTC's callback by looping over all + * encoders connected to it using for_each_encoder_on_crtc(). + * + * This callback is a variant of @disable that provides the atomic state + * to the driver. If @atomic_disable is implemented, @disable is not + * called by the helpers. + * + * This hook is only used by atomic helpers. Atomic drivers don't need + * to implement it if there's no need to disable anything at the encoder + * level. To ensure that runtime PM handling (using either DPMS or the + * new "ACTIVE" property) works @atomic_disable must be the inverse of + * @atomic_enable. + */ + void (*atomic_disable)(struct drm_encoder *encoder, + struct drm_atomic_state *state); + + /** + * @atomic_enable: + * + * This callback should be used to enable the encoder. It is called + * after this encoder's CRTC has been enabled using their own + * &drm_crtc_helper_funcs.atomic_enable hook. If that sequence is + * too simple drivers can just add their own driver private encoder + * hooks and call them from CRTC's callback by looping over all encoders + * connected to it using for_each_encoder_on_crtc(). + * + * This callback is a variant of @enable that provides the atomic state + * to the driver. If @atomic_enable is implemented, @enable is not + * called by the helpers. + * + * This hook is only used by atomic helpers, it is the opposite of + * @atomic_disable. Atomic drivers don't need to implement it if there's + * no need to enable anything at the encoder level. To ensure that + * runtime PM handling works @atomic_enable must be the inverse of + * @atomic_disable. + */ + void (*atomic_enable)(struct drm_encoder *encoder, + struct drm_atomic_state *state); + /** * @disable: * @@ -695,6 +741,9 @@ struct drm_encoder_helper_funcs { * handling (using either DPMS or the new "ACTIVE" property) works * @disable must be the inverse of @enable for atomic drivers. * + * For atomic drivers also consider @atomic_disable and save yourself + * from having to read the NOTE below! + * * NOTE: * * With legacy CRTC helpers there's a big semantic difference between -- cgit v1.2.3 From 1b27fbdde1df172dba604855c45078d741f8c858 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 11 Jun 2019 16:51:43 -0400 Subject: drm: Add drm_atomic_get_(old|new)_connector_for_encoder() helpers Add functions to the atomic core to retrieve the old and new connectors associated with an encoder in a drm_atomic_state. This is useful for encoders and bridges that need to access the connector, for instance for the drm_display_info. The CRTC associated with the encoder can also be retrieved through the connector state, and from it, the old and new CRTC states. Changed in v4: - Added to the set Changed in v5: - Fix up docbook (Daniel & Laurent) Changed in v6: - Updated commit subject (Sam) Link to v4: https://patchwork.freedesktop.org/patch/msgid/20190508160920.144739-3-sean@poorly.run Link to v5: https://patchwork.freedesktop.org/patch/msgid/20190611160844.257498-3-sean@poorly.run Cc: Daniel Vetter Cc: Sam Ravnborg Tested-by: Heiko Stuebner Reviewed-by: Daniel Vetter Signed-off-by: Laurent Pinchart [seanpaul removed WARNs from helpers and added docs to explain why returning NULL might be valid] Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190611205147.181298-1-sean@poorly.run --- drivers/gpu/drm/drm_atomic.c | 69 ++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_atomic.h | 7 +++++ include/drm/drm_connector.h | 5 ++++ 3 files changed, 81 insertions(+) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 169f06d7c5ce..a4e779deab0f 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -846,6 +846,75 @@ drm_atomic_get_new_private_obj_state(struct drm_atomic_state *state, } EXPORT_SYMBOL(drm_atomic_get_new_private_obj_state); +/** + * drm_atomic_get_old_connector_for_encoder - Get old connector for an encoder + * @state: Atomic state + * @encoder: The encoder to fetch the connector state for + * + * This function finds and returns the connector that was connected to @encoder + * as specified by the @state. + * + * If there is no connector in @state which previously had @encoder connected to + * it, this function will return NULL. While this may seem like an invalid use + * case, it is sometimes useful to differentiate commits which had no prior + * connectors attached to @encoder vs ones that did (and to inspect their + * state). This is especially true in enable hooks because the pipeline has + * changed. + * + * Returns: The old connector connected to @encoder, or NULL if the encoder is + * not connected. + */ +struct drm_connector * +drm_atomic_get_old_connector_for_encoder(struct drm_atomic_state *state, + struct drm_encoder *encoder) +{ + struct drm_connector_state *conn_state; + struct drm_connector *connector; + unsigned int i; + + for_each_old_connector_in_state(state, connector, conn_state, i) { + if (conn_state->best_encoder == encoder) + return connector; + } + + return NULL; +} +EXPORT_SYMBOL(drm_atomic_get_old_connector_for_encoder); + +/** + * drm_atomic_get_new_connector_for_encoder - Get new connector for an encoder + * @state: Atomic state + * @encoder: The encoder to fetch the connector state for + * + * This function finds and returns the connector that will be connected to + * @encoder as specified by the @state. + * + * If there is no connector in @state which will have @encoder connected to it, + * this function will return NULL. While this may seem like an invalid use case, + * it is sometimes useful to differentiate commits which have no connectors + * attached to @encoder vs ones that do (and to inspect their state). This is + * especially true in disable hooks because the pipeline will change. + * + * Returns: The new connector connected to @encoder, or NULL if the encoder is + * not connected. + */ +struct drm_connector * +drm_atomic_get_new_connector_for_encoder(struct drm_atomic_state *state, + struct drm_encoder *encoder) +{ + struct drm_connector_state *conn_state; + struct drm_connector *connector; + unsigned int i; + + for_each_new_connector_in_state(state, connector, conn_state, i) { + if (conn_state->best_encoder == encoder) + return connector; + } + + return NULL; +} +EXPORT_SYMBOL(drm_atomic_get_new_connector_for_encoder); + /** * drm_atomic_get_connector_state - get connector state * @state: global atomic state object diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index e937ff2beb04..f12215647801 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -459,6 +459,13 @@ struct drm_private_state * drm_atomic_get_new_private_obj_state(struct drm_atomic_state *state, struct drm_private_obj *obj); +struct drm_connector * +drm_atomic_get_old_connector_for_encoder(struct drm_atomic_state *state, + struct drm_encoder *encoder); +struct drm_connector * +drm_atomic_get_new_connector_for_encoder(struct drm_atomic_state *state, + struct drm_encoder *encoder); + /** * drm_atomic_get_existing_crtc_state - get crtc state, if it exists * @state: global atomic state object diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 47e749b74e5f..071143bc0ebd 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -518,6 +518,11 @@ struct drm_connector_state { * &drm_connector_helper_funcs.atomic_best_encoder or * &drm_connector_helper_funcs.best_encoder callbacks. * + * This is also used in the atomic helpers to map encoders to their + * current and previous connectors, see + * &drm_atomic_get_old_connector_for_encoder() and + * &drm_atomic_get_new_connector_for_encoder(). + * * NOTE: Atomic drivers must fill this out (either themselves or through * helpers), for otherwise the GETCONNECTOR and GETENCODER IOCTLs will * not return correct data to userspace. -- cgit v1.2.3 From 5ade071ba13e3bb24e3a9d5f8d6a3cf145deeb18 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Tue, 11 Jun 2019 12:08:17 -0400 Subject: drm: Add atomic variants for bridge enable/disable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds atomic variants for all of pre_enable/enable/disable/post_disable bridge functions. These will be called from the appropriate atomic helper functions. If the bridge driver doesn't implement the atomic version of the function, we will fall back to the vanilla implementation. Note that some drivers call drm_bridge_disable directly, and these cases are not covered. It's up to the driver to decide whether to implement both atomic_disable and disable, or if it's not necessary. Changes in v3: - Added to the patchset Changes in v4: - Fix up docbook references (Daniel) Changes in v5: - None Link to v3: https://patchwork.freedesktop.org/patch/msgid/20190502194956.218441-4-sean@poorly.run Link to v4: https://patchwork.freedesktop.org/patch/msgid/20190508160920.144739-4-sean@poorly.run Cc: Daniel Vetter Cc: Ville Syrjälä Tested-by: Heiko Stuebner Reviewed-by: Daniel Vetter Reviewed-by: Andrzej Hajda Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190611160844.257498-4-sean@poorly.run --- drivers/gpu/drm/drm_atomic_helper.c | 8 +-- drivers/gpu/drm/drm_bridge.c | 110 ++++++++++++++++++++++++++++++++++++ include/drm/drm_bridge.h | 106 ++++++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 4 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 62e29b5ebb6e..2133f6253917 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -998,7 +998,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) * Each encoder has at most one connector (since we always steal * it away), so we won't call disable hooks twice. */ - drm_bridge_disable(encoder->bridge); + drm_atomic_bridge_disable(encoder->bridge, old_state); /* Right function depends upon target state. */ if (funcs) { @@ -1012,7 +1012,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) funcs->dpms(encoder, DRM_MODE_DPMS_OFF); } - drm_bridge_post_disable(encoder->bridge); + drm_atomic_bridge_post_disable(encoder->bridge, old_state); } for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { @@ -1310,7 +1310,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, * Each encoder has at most one connector (since we always steal * it away), so we won't call enable hooks twice. */ - drm_bridge_pre_enable(encoder->bridge); + drm_atomic_bridge_pre_enable(encoder->bridge, old_state); if (funcs) { if (funcs->atomic_enable) @@ -1321,7 +1321,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, funcs->commit(encoder); } - drm_bridge_enable(encoder->bridge); + drm_atomic_bridge_enable(encoder->bridge, old_state); } drm_atomic_helper_commit_writebacks(dev, old_state); diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 138b2711d389..cba537c99e43 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -352,6 +352,116 @@ void drm_bridge_enable(struct drm_bridge *bridge) } EXPORT_SYMBOL(drm_bridge_enable); +/** + * drm_atomic_bridge_disable - disables all bridges in the encoder chain + * @bridge: bridge control structure + * @state: atomic state being committed + * + * Calls &drm_bridge_funcs.atomic_disable (falls back on + * &drm_bridge_funcs.disable) op for all the bridges in the encoder chain, + * starting from the last bridge to the first. These are called before calling + * &drm_encoder_helper_funcs.atomic_disable + * + * Note: the bridge passed should be the one closest to the encoder + */ +void drm_atomic_bridge_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) +{ + if (!bridge) + return; + + drm_atomic_bridge_disable(bridge->next, state); + + if (bridge->funcs->atomic_disable) + bridge->funcs->atomic_disable(bridge, state); + else if (bridge->funcs->disable) + bridge->funcs->disable(bridge); +} +EXPORT_SYMBOL(drm_atomic_bridge_disable); + +/** + * drm_atomic_bridge_post_disable - cleans up after disabling all bridges in the + * encoder chain + * @bridge: bridge control structure + * @state: atomic state being committed + * + * Calls &drm_bridge_funcs.atomic_post_disable (falls back on + * &drm_bridge_funcs.post_disable) op for all the bridges in the encoder chain, + * starting from the first bridge to the last. These are called after completing + * &drm_encoder_helper_funcs.atomic_disable + * + * Note: the bridge passed should be the one closest to the encoder + */ +void drm_atomic_bridge_post_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) +{ + if (!bridge) + return; + + if (bridge->funcs->atomic_post_disable) + bridge->funcs->atomic_post_disable(bridge, state); + else if (bridge->funcs->post_disable) + bridge->funcs->post_disable(bridge); + + drm_atomic_bridge_post_disable(bridge->next, state); +} +EXPORT_SYMBOL(drm_atomic_bridge_post_disable); + +/** + * drm_atomic_bridge_pre_enable - prepares for enabling all bridges in the + * encoder chain + * @bridge: bridge control structure + * @state: atomic state being committed + * + * Calls &drm_bridge_funcs.atomic_pre_enable (falls back on + * &drm_bridge_funcs.pre_enable) op for all the bridges in the encoder chain, + * starting from the last bridge to the first. These are called before calling + * &drm_encoder_helper_funcs.atomic_enable + * + * Note: the bridge passed should be the one closest to the encoder + */ +void drm_atomic_bridge_pre_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) +{ + if (!bridge) + return; + + drm_atomic_bridge_pre_enable(bridge->next, state); + + if (bridge->funcs->atomic_pre_enable) + bridge->funcs->atomic_pre_enable(bridge, state); + else if (bridge->funcs->pre_enable) + bridge->funcs->pre_enable(bridge); +} +EXPORT_SYMBOL(drm_atomic_bridge_pre_enable); + +/** + * drm_atomic_bridge_enable - enables all bridges in the encoder chain + * @bridge: bridge control structure + * @state: atomic state being committed + * + * Calls &drm_bridge_funcs.atomic_enable (falls back on + * &drm_bridge_funcs.enable) op for all the bridges in the encoder chain, + * starting from the first bridge to the last. These are called after completing + * &drm_encoder_helper_funcs.atomic_enable + * + * Note: the bridge passed should be the one closest to the encoder + */ +void drm_atomic_bridge_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) +{ + if (!bridge) + return; + + if (bridge->funcs->atomic_enable) + bridge->funcs->atomic_enable(bridge, state); + else if (bridge->funcs->enable) + bridge->funcs->enable(bridge); + + drm_atomic_bridge_enable(bridge->next, state); +} +EXPORT_SYMBOL(drm_atomic_bridge_enable); + #ifdef CONFIG_OF /** * of_drm_find_bridge - find the bridge corresponding to the device node in diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index d4428913a4e1..322801884814 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -237,6 +237,103 @@ struct drm_bridge_funcs { * The enable callback is optional. */ void (*enable)(struct drm_bridge *bridge); + + /** + * @atomic_pre_enable: + * + * This callback should enable the bridge. It is called right before + * the preceding element in the display pipe is enabled. If the + * preceding element is a bridge this means it's called before that + * bridge's @atomic_pre_enable or @pre_enable function. If the preceding + * element is a &drm_encoder it's called right before the encoder's + * &drm_encoder_helper_funcs.atomic_enable hook. + * + * The display pipe (i.e. clocks and timing signals) feeding this bridge + * will not yet be running when this callback is called. The bridge must + * not enable the display link feeding the next bridge in the chain (if + * there is one) when this callback is called. + * + * Note that this function will only be invoked in the context of an + * atomic commit. It will not be invoked from &drm_bridge_pre_enable. It + * would be prudent to also provide an implementation of @pre_enable if + * you are expecting driver calls into &drm_bridge_pre_enable. + * + * The @atomic_pre_enable callback is optional. + */ + void (*atomic_pre_enable)(struct drm_bridge *bridge, + struct drm_atomic_state *state); + + /** + * @atomic_enable: + * + * This callback should enable the bridge. It is called right after + * the preceding element in the display pipe is enabled. If the + * preceding element is a bridge this means it's called after that + * bridge's @atomic_enable or @enable function. If the preceding element + * is a &drm_encoder it's called right after the encoder's + * &drm_encoder_helper_funcs.atomic_enable hook. + * + * The bridge can assume that the display pipe (i.e. clocks and timing + * signals) feeding it is running when this callback is called. This + * callback must enable the display link feeding the next bridge in the + * chain if there is one. + * + * Note that this function will only be invoked in the context of an + * atomic commit. It will not be invoked from &drm_bridge_enable. It + * would be prudent to also provide an implementation of @enable if + * you are expecting driver calls into &drm_bridge_enable. + * + * The enable callback is optional. + */ + void (*atomic_enable)(struct drm_bridge *bridge, + struct drm_atomic_state *state); + /** + * @atomic_disable: + * + * This callback should disable the bridge. It is called right before + * the preceding element in the display pipe is disabled. If the + * preceding element is a bridge this means it's called before that + * bridge's @atomic_disable or @disable vfunc. If the preceding element + * is a &drm_encoder it's called right before the + * &drm_encoder_helper_funcs.atomic_disable hook. + * + * The bridge can assume that the display pipe (i.e. clocks and timing + * signals) feeding it is still running when this callback is called. + * + * Note that this function will only be invoked in the context of an + * atomic commit. It will not be invoked from &drm_bridge_disable. It + * would be prudent to also provide an implementation of @disable if + * you are expecting driver calls into &drm_bridge_disable. + * + * The disable callback is optional. + */ + void (*atomic_disable)(struct drm_bridge *bridge, + struct drm_atomic_state *state); + + /** + * @atomic_post_disable: + * + * This callback should disable the bridge. It is called right after the + * preceding element in the display pipe is disabled. If the preceding + * element is a bridge this means it's called after that bridge's + * @atomic_post_disable or @post_disable function. If the preceding + * element is a &drm_encoder it's called right after the encoder's + * &drm_encoder_helper_funcs.atomic_disable hook. + * + * The bridge must assume that the display pipe (i.e. clocks and timing + * signals) feeding it is no longer running when this callback is + * called. + * + * Note that this function will only be invoked in the context of an + * atomic commit. It will not be invoked from &drm_bridge_post_disable. + * It would be prudent to also provide an implementation of + * @post_disable if you are expecting driver calls into + * &drm_bridge_post_disable. + * + * The post_disable callback is optional. + */ + void (*atomic_post_disable)(struct drm_bridge *bridge, + struct drm_atomic_state *state); }; /** @@ -314,6 +411,15 @@ void drm_bridge_mode_set(struct drm_bridge *bridge, void drm_bridge_pre_enable(struct drm_bridge *bridge); void drm_bridge_enable(struct drm_bridge *bridge); +void drm_atomic_bridge_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state); +void drm_atomic_bridge_post_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state); +void drm_atomic_bridge_pre_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state); +void drm_atomic_bridge_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state); + #ifdef CONFIG_DRM_PANEL_BRIDGE struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel, u32 connector_type); -- cgit v1.2.3 From 6f3b62781bbd2670756a4847113d410a827a2593 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Tue, 11 Jun 2019 12:08:18 -0400 Subject: drm: Convert connector_helper_funcs->atomic_check to accept drm_atomic_state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Everyone who implements connector_helper_funcs->atomic_check reaches into the connector state to get the atomic state. Instead of continuing this pattern, change the callback signature to just give atomic state and let the driver determine what it does and does not need from it. Eventually all atomic functions should do this, but that's just too much busy work for me. Changes in v3: - Added to the set Changes in v4: - None Changes in v5: - intel_digital_connector_atomic_check declaration moved to i915_atomic.h Link to v3: https://patchwork.freedesktop.org/patch/msgid/20190502194956.218441-5-sean@poorly.run Link to v4: https://patchwork.freedesktop.org/patch/msgid/20190508160920.144739-5-sean@poorly.run Cc: Daniel Vetter Cc: Ville Syrjälä Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Ben Skeggs Cc: Laurent Pinchart Cc: Kieran Bingham Cc: Eric Anholt Tested-by: Heiko Stuebner Reviewed-by: Laurent Pinchart [for rcar lvds] Acked-by: Daniel Vetter Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190611160844.257498-5-sean@poorly.run --- drivers/gpu/drm/drm_atomic_helper.c | 4 ++-- drivers/gpu/drm/i915/intel_atomic.c | 8 +++++--- drivers/gpu/drm/i915/intel_atomic.h | 2 +- drivers/gpu/drm/i915/intel_dp_mst.c | 7 ++++--- drivers/gpu/drm/i915/intel_sdvo.c | 9 +++++---- drivers/gpu/drm/i915/intel_tv.c | 8 +++++--- drivers/gpu/drm/nouveau/dispnv50/disp.c | 5 +++-- drivers/gpu/drm/rcar-du/rcar_lvds.c | 12 +++++++----- drivers/gpu/drm/vc4/vc4_txp.c | 7 ++++--- include/drm/drm_modeset_helper_vtables.h | 2 +- 10 files changed, 37 insertions(+), 27 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 2133f6253917..e58be6996069 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -686,7 +686,7 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, } if (funcs->atomic_check) - ret = funcs->atomic_check(connector, new_connector_state); + ret = funcs->atomic_check(connector, state); if (ret) return ret; @@ -728,7 +728,7 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, continue; if (funcs->atomic_check) - ret = funcs->atomic_check(connector, new_connector_state); + ret = funcs->atomic_check(connector, state); if (ret) return ret; } diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 58b8049649a0..ab40448a19d5 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -106,12 +106,14 @@ int intel_digital_connector_atomic_set_property(struct drm_connector *connector, } int intel_digital_connector_atomic_check(struct drm_connector *conn, - struct drm_connector_state *new_state) + struct drm_atomic_state *state) { + struct drm_connector_state *new_state = + drm_atomic_get_new_connector_state(state, conn); struct intel_digital_connector_state *new_conn_state = to_intel_digital_connector_state(new_state); struct drm_connector_state *old_state = - drm_atomic_get_old_connector_state(new_state->state, conn); + drm_atomic_get_old_connector_state(state, conn); struct intel_digital_connector_state *old_conn_state = to_intel_digital_connector_state(old_state); struct drm_crtc_state *crtc_state; @@ -121,7 +123,7 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn, if (!new_state->crtc) return 0; - crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc); /* * These properties are handled by fastset, and might not end diff --git a/drivers/gpu/drm/i915/intel_atomic.h b/drivers/gpu/drm/i915/intel_atomic.h index 1c8507da1a69..58065d3161a3 100644 --- a/drivers/gpu/drm/i915/intel_atomic.h +++ b/drivers/gpu/drm/i915/intel_atomic.h @@ -28,7 +28,7 @@ int intel_digital_connector_atomic_set_property(struct drm_connector *connector, struct drm_property *property, u64 val); int intel_digital_connector_atomic_check(struct drm_connector *conn, - struct drm_connector_state *new_state); + struct drm_atomic_state *state); struct drm_connector_state * intel_digital_connector_duplicate_state(struct drm_connector *connector); diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c index 0caf645fbbb8..60652ebbdf61 100644 --- a/drivers/gpu/drm/i915/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/intel_dp_mst.c @@ -151,9 +151,10 @@ static int intel_dp_mst_compute_config(struct intel_encoder *encoder, static int intel_dp_mst_atomic_check(struct drm_connector *connector, - struct drm_connector_state *new_conn_state) + struct drm_atomic_state *state) { - struct drm_atomic_state *state = new_conn_state->state; + struct drm_connector_state *new_conn_state = + drm_atomic_get_new_connector_state(state, connector); struct drm_connector_state *old_conn_state = drm_atomic_get_old_connector_state(state, connector); struct intel_connector *intel_connector = @@ -163,7 +164,7 @@ intel_dp_mst_atomic_check(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr; int ret; - ret = intel_digital_connector_atomic_check(connector, new_conn_state); + ret = intel_digital_connector_atomic_check(connector, state); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 6c98b35790db..368b99f6f7fb 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -2353,9 +2353,10 @@ static const struct drm_connector_funcs intel_sdvo_connector_funcs = { }; static int intel_sdvo_atomic_check(struct drm_connector *conn, - struct drm_connector_state *new_conn_state) + struct drm_atomic_state *state) { - struct drm_atomic_state *state = new_conn_state->state; + struct drm_connector_state *new_conn_state = + drm_atomic_get_new_connector_state(state, conn); struct drm_connector_state *old_conn_state = drm_atomic_get_old_connector_state(state, conn); struct intel_sdvo_connector_state *old_state = @@ -2367,13 +2368,13 @@ static int intel_sdvo_atomic_check(struct drm_connector *conn, (memcmp(&old_state->tv, &new_state->tv, sizeof(old_state->tv)) || memcmp(&old_conn_state->tv, &new_conn_state->tv, sizeof(old_conn_state->tv)))) { struct drm_crtc_state *crtc_state = - drm_atomic_get_new_crtc_state(new_conn_state->state, + drm_atomic_get_new_crtc_state(state, new_conn_state->crtc); crtc_state->connectors_changed = true; } - return intel_digital_connector_atomic_check(conn, new_conn_state); + return intel_digital_connector_atomic_check(conn, state); } static const struct drm_connector_helper_funcs intel_sdvo_connector_helper_funcs = { diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 5dc594eafaf2..0a95df6c6a57 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1821,16 +1821,18 @@ static const struct drm_connector_funcs intel_tv_connector_funcs = { }; static int intel_tv_atomic_check(struct drm_connector *connector, - struct drm_connector_state *new_state) + struct drm_atomic_state *state) { + struct drm_connector_state *new_state; struct drm_crtc_state *new_crtc_state; struct drm_connector_state *old_state; + new_state = drm_atomic_get_new_connector_state(state, connector); if (!new_state->crtc) return 0; - old_state = drm_atomic_get_old_connector_state(new_state->state, connector); - new_crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc); + old_state = drm_atomic_get_old_connector_state(state, connector); + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc); if (old_state->tv.mode != new_state->tv.mode || old_state->tv.margins.left != new_state->tv.margins.left || diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 4b1650f51955..7ba373f493b2 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -948,11 +948,12 @@ nv50_mstc_get_modes(struct drm_connector *connector) static int nv50_mstc_atomic_check(struct drm_connector *connector, - struct drm_connector_state *new_conn_state) + struct drm_atomic_state *state) { - struct drm_atomic_state *state = new_conn_state->state; struct nv50_mstc *mstc = nv50_mstc(connector); struct drm_dp_mst_topology_mgr *mgr = &mstc->mstm->mgr; + struct drm_connector_state *new_conn_state = + drm_atomic_get_new_connector_state(state, connector); struct drm_connector_state *old_conn_state = drm_atomic_get_old_connector_state(state, connector); struct drm_crtc_state *crtc_state; diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c index 620b51aab291..5b81ba2a7f27 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c @@ -92,13 +92,15 @@ static int rcar_lvds_connector_get_modes(struct drm_connector *connector) } static int rcar_lvds_connector_atomic_check(struct drm_connector *connector, - struct drm_connector_state *state) + struct drm_atomic_state *state) { struct rcar_lvds *lvds = connector_to_rcar_lvds(connector); const struct drm_display_mode *panel_mode; + struct drm_connector_state *conn_state; struct drm_crtc_state *crtc_state; - if (!state->crtc) + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (!conn_state->crtc) return 0; if (list_empty(&connector->modes)) { @@ -110,9 +112,9 @@ static int rcar_lvds_connector_atomic_check(struct drm_connector *connector, struct drm_display_mode, head); /* We're not allowed to modify the resolution. */ - crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); - if (IS_ERR(crtc_state)) - return PTR_ERR(crtc_state); + crtc_state = drm_atomic_get_crtc_state(state, conn_state->crtc); + if (!crtc_state) + return -EINVAL; if (crtc_state->mode.hdisplay != panel_mode->hdisplay || crtc_state->mode.vdisplay != panel_mode->vdisplay) diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index c8b89a78f9f4..96f91c1b4b6e 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -221,17 +221,18 @@ static const u32 txp_fmts[] = { }; static int vc4_txp_connector_atomic_check(struct drm_connector *conn, - struct drm_connector_state *conn_state) + struct drm_atomic_state *state) { + struct drm_connector_state *conn_state; struct drm_crtc_state *crtc_state; struct drm_framebuffer *fb; int i; + conn_state = drm_atomic_get_new_connector_state(state, conn); if (!conn_state->writeback_job || !conn_state->writeback_job->fb) return 0; - crtc_state = drm_atomic_get_new_crtc_state(conn_state->state, - conn_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); fb = conn_state->writeback_job->fb; if (fb->width != crtc_state->mode.hdisplay || diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index 13f0e3ca0a29..f36be43bd052 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -1028,7 +1028,7 @@ struct drm_connector_helper_funcs { * deadlock. */ int (*atomic_check)(struct drm_connector *connector, - struct drm_connector_state *state); + struct drm_atomic_state *state); /** * @atomic_commit: -- cgit v1.2.3 From 1452c25b0e60278820f3d2155c65f1bfcce5ee79 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Wed, 12 Jun 2019 10:50:19 -0400 Subject: drm: Add helpers to kick off self refresh mode in drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds a new drm helper library to help drivers implement self refresh. Drivers choosing to use it will register crtcs and will receive callbacks when it's time to enter or exit self refresh mode. In its current form, it has a timer which will trigger after a driver-specified amount of inactivity. When the timer triggers, the helpers will submit a new atomic commit to shut the refreshing pipe off. On the next atomic commit, the drm core will revert the self refresh state and bring everything back up to be actively driven. From the driver's perspective, this works like a regular disable/enable cycle. The driver need only check the 'self_refresh_active' state in crtc_state. It should initiate self refresh mode on the panel and enter an off or low-power state. Changes in v2: - s/psr/self_refresh/ (Daniel) - integrated the psr exit into the commit that wakes it up (Jose/Daniel) - made the psr state per-crtc (Jose/Daniel) Changes in v3: - Remove the self_refresh_(active|changed) from connector state (Daniel) - Simplify loop in drm_self_refresh_helper_alter_state (Daniel) - Improve self_refresh_aware comment (Daniel) - s/self_refresh_state/self_refresh_data/ (Daniel) Changes in v4: - Move docbook location below panel (Daniel) - Improve docbook with references and more detailed explanation (Daniel) - Instead of register/unregister, use init/cleanup (Daniel) Changes in v5: - Resolved conflict in drm_atomic_helper.c #include block - Resolved conflict in rst with HDCP helper docs Changes in v6: - Fix include ordering, clean up forward declarations (Sam) Link to v1: https://patchwork.freedesktop.org/patch/msgid/20190228210939.83386-2-sean@poorly.run Link to v2: https://patchwork.freedesktop.org/patch/msgid/20190326204509.96515-1-sean@poorly.run Link to v3: https://patchwork.freedesktop.org/patch/msgid/20190502194956.218441-6-sean@poorly.run Link to v4: https://patchwork.freedesktop.org/patch/msgid/20190508160920.144739-6-sean@poorly.run Link to v5: https://patchwork.freedesktop.org/patch/msgid/20190611160844.257498-6-sean@poorly.run Cc: Daniel Vetter Cc: Jose Souza Cc: Zain Wang Cc: Tomasz Figa Cc: Ville Syrjälä Cc: Sam Ravnborg Tested-by: Heiko Stuebner Reviewed-by: Daniel Vetter Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190612145026.191846-1-sean@poorly.run --- Documentation/gpu/drm-kms-helpers.rst | 9 ++ drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_atomic.c | 2 + drivers/gpu/drm/drm_atomic_helper.c | 35 ++++- drivers/gpu/drm/drm_atomic_state_helper.c | 4 + drivers/gpu/drm/drm_atomic_uapi.c | 7 +- drivers/gpu/drm/drm_self_refresh_helper.c | 216 ++++++++++++++++++++++++++++++ include/drm/drm_atomic.h | 15 +++ include/drm/drm_connector.h | 14 ++ include/drm/drm_crtc.h | 19 +++ include/drm/drm_self_refresh_helper.h | 20 +++ 11 files changed, 338 insertions(+), 5 deletions(-) create mode 100644 drivers/gpu/drm/drm_self_refresh_helper.c create mode 100644 include/drm/drm_self_refresh_helper.h (limited to 'include/drm') diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 0fe726a6ee67..b327bbc11182 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -181,6 +181,15 @@ Panel Helper Reference .. kernel-doc:: drivers/gpu/drm/drm_panel_orientation_quirks.c :export: +Panel Self Refresh Helper Reference +=================================== + +.. kernel-doc:: drivers/gpu/drm/drm_self_refresh_helper.c + :doc: overview + +.. kernel-doc:: drivers/gpu/drm/drm_self_refresh_helper.c + :export: + HDCP Helper Functions Reference =============================== diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index d36feb4a6233..9d630a28a788 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -43,7 +43,7 @@ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_dsc.o drm_probe_helper drm_simple_kms_helper.o drm_modeset_helper.o \ drm_scdc_helper.o drm_gem_framebuffer_helper.o \ drm_atomic_state_helper.o drm_damage_helper.o \ - drm_format_helper.o + drm_format_helper.o drm_self_refresh_helper.o drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index a4e779deab0f..419381abbdd1 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -384,6 +384,7 @@ static void drm_atomic_crtc_print_state(struct drm_printer *p, drm_printf(p, "crtc[%u]: %s\n", crtc->base.id, crtc->name); drm_printf(p, "\tenable=%d\n", state->enable); drm_printf(p, "\tactive=%d\n", state->active); + drm_printf(p, "\tself_refresh_active=%d\n", state->self_refresh_active); drm_printf(p, "\tplanes_changed=%d\n", state->planes_changed); drm_printf(p, "\tmode_changed=%d\n", state->mode_changed); drm_printf(p, "\tactive_changed=%d\n", state->active_changed); @@ -999,6 +1000,7 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, drm_printf(p, "connector[%u]: %s\n", connector->base.id, connector->name); drm_printf(p, "\tcrtc=%s\n", state->crtc ? state->crtc->name : "(null)"); + drm_printf(p, "\tself_refresh_aware=%d\n", state->self_refresh_aware); if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) if (state->writeback_job && state->writeback_job->fb) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index e58be6996069..d9ea7e51ffdc 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -953,10 +954,33 @@ int drm_atomic_helper_check(struct drm_device *dev, if (state->legacy_cursor_update) state->async_update = !drm_atomic_helper_async_check(dev, state); + drm_self_refresh_helper_alter_state(state); + return ret; } EXPORT_SYMBOL(drm_atomic_helper_check); +static bool +crtc_needs_disable(struct drm_crtc_state *old_state, + struct drm_crtc_state *new_state) +{ + /* + * No new_state means the crtc is off, so the only criteria is whether + * it's currently active or in self refresh mode. + */ + if (!new_state) + return drm_atomic_crtc_effectively_active(old_state); + + /* + * We need to run through the crtc_funcs->disable() function if the crtc + * is currently on, if it's transitioning to self refresh mode, or if + * it's in self refresh mode and needs to be fully disabled. + */ + return old_state->active || + (old_state->self_refresh_active && !new_state->enable) || + new_state->self_refresh_active; +} + static void disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) { @@ -977,7 +1001,14 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) old_crtc_state = drm_atomic_get_old_crtc_state(old_state, old_conn_state->crtc); - if (!old_crtc_state->active || + if (new_conn_state->crtc) + new_crtc_state = drm_atomic_get_new_crtc_state( + old_state, + new_conn_state->crtc); + else + new_crtc_state = NULL; + + if (!crtc_needs_disable(old_crtc_state, new_crtc_state) || !drm_atomic_crtc_needs_modeset(old_conn_state->crtc->state)) continue; @@ -1023,7 +1054,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) continue; - if (!old_crtc_state->active) + if (!crtc_needs_disable(old_crtc_state, new_crtc_state)) continue; funcs = crtc->helper_private; diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 97ab26679b96..7d7347a6f194 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -129,6 +129,10 @@ void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, state->commit = NULL; state->event = NULL; state->pageflip_flags = 0; + + /* Self refresh should be canceled when a new update is available */ + state->active = drm_atomic_crtc_effectively_active(state); + state->self_refresh_active = false; } EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state); diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index eb22e8bdd853..abe38bdf85ae 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -490,7 +490,7 @@ drm_atomic_crtc_get_property(struct drm_crtc *crtc, struct drm_mode_config *config = &dev->mode_config; if (property == config->prop_active) - *val = state->active; + *val = drm_atomic_crtc_effectively_active(state); else if (property == config->prop_mode_id) *val = (state->mode_blob) ? state->mode_blob->base.id : 0; else if (property == config->prop_vrr_enabled) @@ -788,7 +788,10 @@ drm_atomic_connector_get_property(struct drm_connector *connector, if (property == config->prop_crtc_id) { *val = (state->crtc) ? state->crtc->base.id : 0; } else if (property == config->dpms_property) { - *val = connector->dpms; + if (state->crtc && state->crtc->state->self_refresh_active) + *val = DRM_MODE_DPMS_ON; + else + *val = connector->dpms; } else if (property == config->tv_select_subconnector_property) { *val = state->tv.subconnector; } else if (property == config->tv_left_margin_property) { diff --git a/drivers/gpu/drm/drm_self_refresh_helper.c b/drivers/gpu/drm/drm_self_refresh_helper.c new file mode 100644 index 000000000000..2b3daaf77841 --- /dev/null +++ b/drivers/gpu/drm/drm_self_refresh_helper.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (C) 2019 Google, Inc. + * + * Authors: + * Sean Paul + */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * DOC: overview + * + * This helper library provides an easy way for drivers to leverage the atomic + * framework to implement panel self refresh (SR) support. Drivers are + * responsible for initializing and cleaning up the SR helpers on load/unload + * (see &drm_self_refresh_helper_init/&drm_self_refresh_helper_cleanup). + * The connector is responsible for setting + * &drm_connector_state.self_refresh_aware to true at runtime if it is SR-aware + * (meaning it knows how to initiate self refresh on the panel). + * + * Once a crtc has enabled SR using &drm_self_refresh_helper_init, the + * helpers will monitor activity and call back into the driver to enable/disable + * SR as appropriate. The best way to think about this is that it's a DPMS + * on/off request with &drm_crtc_state.self_refresh_active set in crtc state + * that tells you to disable/enable SR on the panel instead of power-cycling it. + * + * During SR, drivers may choose to fully disable their crtc/encoder/bridge + * hardware (in which case no driver changes are necessary), or they can inspect + * &drm_crtc_state.self_refresh_active if they want to enter low power mode + * without full disable (in case full disable/enable is too slow). + * + * SR will be deactivated if there are any atomic updates affecting the + * pipe that is in SR mode. If a crtc is driving multiple connectors, all + * connectors must be SR aware and all will enter/exit SR mode at the same time. + * + * If the crtc and connector are SR aware, but the panel connected does not + * support it (or is otherwise unable to enter SR), the driver should fail + * atomic_check when &drm_crtc_state.self_refresh_active is true. + */ + +struct drm_self_refresh_data { + struct drm_crtc *crtc; + struct delayed_work entry_work; + struct drm_atomic_state *save_state; + unsigned int entry_delay_ms; +}; + +static void drm_self_refresh_helper_entry_work(struct work_struct *work) +{ + struct drm_self_refresh_data *sr_data = container_of( + to_delayed_work(work), + struct drm_self_refresh_data, entry_work); + struct drm_crtc *crtc = sr_data->crtc; + struct drm_device *dev = crtc->dev; + struct drm_modeset_acquire_ctx ctx; + struct drm_atomic_state *state; + struct drm_connector *conn; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + int i, ret; + + drm_modeset_acquire_init(&ctx, 0); + + state = drm_atomic_state_alloc(dev); + if (!state) { + ret = -ENOMEM; + goto out; + } + +retry: + state->acquire_ctx = &ctx; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + goto out; + } + + if (!crtc_state->enable) + goto out; + + ret = drm_atomic_add_affected_connectors(state, crtc); + if (ret) + goto out; + + for_each_new_connector_in_state(state, conn, conn_state, i) { + if (!conn_state->self_refresh_aware) + goto out; + } + + crtc_state->active = false; + crtc_state->self_refresh_active = true; + + ret = drm_atomic_commit(state); + if (ret) + goto out; + +out: + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry; + } + + drm_atomic_state_put(state); + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + +/** + * drm_self_refresh_helper_alter_state - Alters the atomic state for SR exit + * @state: the state currently being checked + * + * Called at the end of atomic check. This function checks the state for flags + * incompatible with self refresh exit and changes them. This is a bit + * disingenuous since userspace is expecting one thing and we're giving it + * another. However in order to keep self refresh entirely hidden from + * userspace, this is required. + * + * At the end, we queue up the self refresh entry work so we can enter PSR after + * the desired delay. + */ +void drm_self_refresh_helper_alter_state(struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + int i; + + if (state->async_update || !state->allow_modeset) { + for_each_old_crtc_in_state(state, crtc, crtc_state, i) { + if (crtc_state->self_refresh_active) { + state->async_update = false; + state->allow_modeset = true; + break; + } + } + } + + for_each_new_crtc_in_state(state, crtc, crtc_state, i) { + struct drm_self_refresh_data *sr_data; + + /* Don't trigger the entry timer when we're already in SR */ + if (crtc_state->self_refresh_active) + continue; + + sr_data = crtc->self_refresh_data; + if (!sr_data) + continue; + + mod_delayed_work(system_wq, &sr_data->entry_work, + msecs_to_jiffies(sr_data->entry_delay_ms)); + } +} +EXPORT_SYMBOL(drm_self_refresh_helper_alter_state); + +/** + * drm_self_refresh_helper_init - Initializes self refresh helpers for a crtc + * @crtc: the crtc which supports self refresh supported displays + * @entry_delay_ms: amount of inactivity to wait before entering self refresh + * + * Returns zero if successful or -errno on failure + */ +int drm_self_refresh_helper_init(struct drm_crtc *crtc, + unsigned int entry_delay_ms) +{ + struct drm_self_refresh_data *sr_data = crtc->self_refresh_data; + + /* Helper is already initialized */ + if (WARN_ON(sr_data)) + return -EINVAL; + + sr_data = kzalloc(sizeof(*sr_data), GFP_KERNEL); + if (!sr_data) + return -ENOMEM; + + INIT_DELAYED_WORK(&sr_data->entry_work, + drm_self_refresh_helper_entry_work); + sr_data->entry_delay_ms = entry_delay_ms; + sr_data->crtc = crtc; + + crtc->self_refresh_data = sr_data; + return 0; +} +EXPORT_SYMBOL(drm_self_refresh_helper_init); + +/** + * drm_self_refresh_helper_cleanup - Cleans up self refresh helpers for a crtc + * @crtc: the crtc to cleanup + */ +void drm_self_refresh_helper_cleanup(struct drm_crtc *crtc) +{ + struct drm_self_refresh_data *sr_data = crtc->self_refresh_data; + + /* Helper is already uninitialized */ + if (sr_data) + return; + + crtc->self_refresh_data = NULL; + + cancel_delayed_work_sync(&sr_data->entry_work); + kfree(sr_data); +} +EXPORT_SYMBOL(drm_self_refresh_helper_cleanup); diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index f12215647801..927e1205d7aa 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -957,4 +957,19 @@ drm_atomic_crtc_needs_modeset(const struct drm_crtc_state *state) state->connectors_changed; } +/** + * drm_atomic_crtc_effectively_active - compute whether crtc is actually active + * @state: &drm_crtc_state for the CRTC + * + * When in self refresh mode, the crtc_state->active value will be false, since + * the crtc is off. However in some cases we're interested in whether the crtc + * is active, or effectively active (ie: it's connected to an active display). + * In these cases, use this function instead of just checking active. + */ +static inline bool +drm_atomic_crtc_effectively_active(const struct drm_crtc_state *state) +{ + return state->active || state->self_refresh_active; +} + #endif /* DRM_ATOMIC_H_ */ diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 071143bc0ebd..c6f8486d8b8f 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -548,6 +548,20 @@ struct drm_connector_state { /** @tv: TV connector state */ struct drm_tv_connector_state tv; + /** + * @self_refresh_aware: + * + * This tracks whether a connector is aware of the self refresh state. + * It should be set to true for those connector implementations which + * understand the self refresh state. This is needed since the crtc + * registers the self refresh helpers and it doesn't know if the + * connectors downstream have implemented self refresh entry/exit. + * + * Drivers should set this to true in atomic_check if they know how to + * handle self_refresh requests. + */ + bool self_refresh_aware; + /** * @picture_aspect_ratio: Connector property to control the * HDMI infoframe aspect ratio setting. diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index dc42b9e35333..128d8b210621 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -54,6 +54,7 @@ struct drm_mode_set; struct drm_file; struct drm_clip_rect; struct drm_printer; +struct drm_self_refresh_data; struct device_node; struct dma_fence; struct edid; @@ -300,6 +301,17 @@ struct drm_crtc_state { */ bool vrr_enabled; + /** + * @self_refresh_active: + * + * Used by the self refresh helpers to denote when a self refresh + * transition is occurring. This will be set on enable/disable callbacks + * when self refresh is being enabled or disabled. In some cases, it may + * not be desirable to fully shut off the crtc during self refresh. + * CRTC's can inspect this flag and determine the best course of action. + */ + bool self_refresh_active; + /** * @event: * @@ -1088,6 +1100,13 @@ struct drm_crtc { * The name of the CRTC's fence timeline. */ char timeline_name[32]; + + /** + * @self_refresh_data: Holds the state for the self refresh helpers + * + * Initialized via drm_self_refresh_helper_register(). + */ + struct drm_self_refresh_data *self_refresh_data; }; /** diff --git a/include/drm/drm_self_refresh_helper.h b/include/drm/drm_self_refresh_helper.h new file mode 100644 index 000000000000..397a583ccca7 --- /dev/null +++ b/include/drm/drm_self_refresh_helper.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (C) 2019 Google, Inc. + * + * Authors: + * Sean Paul + */ +#ifndef DRM_SELF_REFRESH_HELPER_H_ +#define DRM_SELF_REFRESH_HELPER_H_ + +struct drm_atomic_state; +struct drm_crtc; + +void drm_self_refresh_helper_alter_state(struct drm_atomic_state *state); + +int drm_self_refresh_helper_init(struct drm_crtc *crtc, + unsigned int entry_delay_ms); + +void drm_self_refresh_helper_cleanup(struct drm_crtc *crtc); +#endif -- cgit v1.2.3