[PATCH v2 2/2] drm/amd/display: Move PRIMARY plane zpos higher
From: Leo Li [Why] Compositors have different ways of assigning surfaces to DRM planes for render offloading. It may decide between various strategies: overlay, underlay, or a mix of both (see here for more info: https://gitlab.freedesktop.org/emersion/libliftoff/-/issues/76) One way for compositors to implement the underlay strategy is to assign a higher zpos to the DRM_PRIMARY plane than the DRM_OVERLAY planes, effectively turning the DRM_OVERLAY plane into an underlay plane. Today, amdgpu attaches an immutable zpos of 0 to the DRM_PRIMARY plane. This however, is an arbitrary restriction. DCN pipes are general purpose, and can be arranged in any z-order. To support compositors using this allocation scheme, we can set a non-zero immutable zpos for the PRIMARY, allowing the placement of OVERLAYS (mutable zpos range 0-254) beneath the PRIMARY. [How] Assign a zpos = #no of OVERLAY planes to the PRIMARY plane. Then, clean up any assumptions in the driver of PRIMARY plane having the lowest zpos. Signed-off-by: Leo Li Reviewed-by: Harry Wentland Acked-by: Pekka Paalanen v2: Fix typo s/decending/descending/ --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 34 +-- .../amd/display/amdgpu_dm/amdgpu_dm_plane.c | 18 +++--- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index b4b5b73707c1..6782ca1137d4 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -80,6 +80,7 @@ #include #include #include +#include #include #include @@ -375,6 +376,20 @@ static inline void reverse_planes_order(struct dc_surface_update *array_of_surfa swap(array_of_surface_update[i], array_of_surface_update[j]); } +/* + * DC will program planes with their z-order determined by their ordering + * in the dc_surface_updates array. This comparator is used to sort them + * by descending zpos. + */ +static int dm_plane_layer_index_cmp(const void *a, const void *b) +{ + const struct dc_surface_update *sa = (struct dc_surface_update *)a; + const struct dc_surface_update *sb = (struct dc_surface_update *)b; + + /* Sort by descending dc_plane layer_index (i.e. normalized_zpos) */ + return sb->surface->layer_index - sa->surface->layer_index; +} + /** * update_planes_and_stream_adapter() - Send planes to be updated in DC * @@ -399,7 +414,8 @@ static inline bool update_planes_and_stream_adapter(struct dc *dc, struct dc_stream_update *stream_update, struct dc_surface_update *array_of_surface_update) { - reverse_planes_order(array_of_surface_update, planes_count); + sort(array_of_surface_update, planes_count, +sizeof(*array_of_surface_update), dm_plane_layer_index_cmp, NULL); /* * Previous frame finished and HW is ready for optimization. @@ -9503,6 +9519,8 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) for (j = 0; j < status->plane_count; j++) dummy_updates[j].surface = status->plane_states[0]; + sort(dummy_updates, status->plane_count, +sizeof(*dummy_updates), dm_plane_layer_index_cmp, NULL); mutex_lock(&dm->dc_lock); dc_update_planes_and_stream(dm->dc, @@ -10237,6 +10255,16 @@ static bool should_reset_plane(struct drm_atomic_state *state, if (new_crtc_state->color_mgmt_changed) return true; + /* +* On zpos change, planes need to be reordered by removing and re-adding +* them one by one to the dc state, in order of descending zpos. +* +* TODO: We can likely skip bandwidth validation if the only thing that +* changed about the plane was it'z z-ordering. +*/ + if (new_crtc_state->zpos_changed) + return true; + if (drm_atomic_crtc_needs_modeset(new_crtc_state)) return true; @@ -11076,7 +11104,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, } /* Remove exiting planes if they are modified */ - for_each_oldnew_plane_in_state_reverse(state, plane, old_plane_state, new_plane_state, i) { + for_each_oldnew_plane_in_descending_zpos(state, plane, old_plane_state, new_plane_state) { if (old_plane_state->fb && new_plane_state->fb && get_mem_type(old_plane_state->fb) != get_mem_type(new_plane_state->fb)) @@ -11121,7 +11149,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, } /* Add new/modified planes */ - for_each_oldnew_plane_in_state_reverse(state, plane, old_plane_state, new_plane_state, i) { + for_each_oldnew_plane_in_descending_z
[PATCH v2 1/2] drm/amd/display: Introduce overlay cursor mode
From: Leo Li [Why] DCN is the display hardware for amdgpu. DRM planes are backed by DCN hardware pipes, which carry pixel data from one end (memory), to the other (output encoder). Each DCN pipe has the ability to blend in a cursor early on in the pipeline. In other words, there are no dedicated cursor planes in DCN, which makes cursor behavior somewhat unintuitive for compositors. For example, if the cursor is in RGB format, but the top-most DRM plane is in YUV format, DCN will not be able to blend them. Because of this, amdgpu_dm rejects all configurations where a cursor needs to be enabled on top of a YUV formatted plane. >From a compositor's perspective, when computing an allocation for hardware plane offloading, this cursor-on-yuv configuration result in an atomic test failure. Since the failure reason is not obvious at all, compositors will likely fall back to full rendering, which is not ideal. Instead, amdgpu_dm can try to accommodate the cursor-on-yuv configuration by opportunistically reserving a separate DCN pipe just for the cursor. We can refer to this as "overlay cursor mode". It is contrasted with "native cursor mode", where the native DCN per-pipe cursor is used. [How] On each crtc, compute whether the cursor plane should be enabled in overlay mode. If it is, mark the CRTC as requesting overlay cursor mode. Overlay cursor should be enabled whenever there exists a underlying plane that has YUV format, or is scaled differently than the cursor. It should also be enabled if there is no underlying plane, or if underlying planes do not cover the entire CRTC. During DC validation, attempt to enable a separate DCN pipe for the cursor if it's in overlay mode. If that fails, or if no overlay mode is requested, then fallback to native mode. v2: * Update commit message for when overlay cursor should be enabled * Also consider scale and no-underlying-plane case (cursor on crtc bg) * Consider all underlying planes when determinig overlay/native, not just the plane immediately beneath the cursor, as it may not cover the entire CRTC. * Fix typo s/decending/descending/ * Force native cursor on pre-DCN hardware Signed-off-by: Leo Li Acked-by: Harry Wentland Acked-by: Pekka Paalanen --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 490 +- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 7 + .../amd/display/amdgpu_dm/amdgpu_dm_crtc.c| 1 + .../amd/display/amdgpu_dm/amdgpu_dm_plane.c | 13 +- 4 files changed, 386 insertions(+), 125 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 8245cc63712f..b4b5b73707c1 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -8490,8 +8490,22 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, * Disable the cursor first if we're disabling all the planes. * It'll remain on the screen after the planes are re-enabled * if we don't. +* +* If the cursor is transitioning from native to overlay mode, the +* native cursor needs to be disabled first. */ - if (acrtc_state->active_planes == 0) + if (acrtc_state->cursor_mode == DM_CURSOR_OVERLAY_MODE && + dm_old_crtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE) { + struct dc_cursor_position cursor_position = {0}; + + dc_stream_set_cursor_position(acrtc_state->stream, + &cursor_position); + bundle->stream_update.cursor_position = + &acrtc_state->stream->cursor_position; + } + + if (acrtc_state->active_planes == 0 && + dm_old_crtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE) amdgpu_dm_commit_cursors(state); /* update planes when needed */ @@ -8505,7 +8519,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, struct dm_plane_state *dm_new_plane_state = to_dm_plane_state(new_plane_state); /* Cursor plane is handled after stream updates */ - if (plane->type == DRM_PLANE_TYPE_CURSOR) { + if (plane->type == DRM_PLANE_TYPE_CURSOR && + acrtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE) { if ((fb && crtc == pcrtc) || (old_plane_state->fb && old_plane_state->crtc == pcrtc)) { cursor_update = true; @@ -8863,7 +8878,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, * to be disabling a single plane - those pipes are being disabled. */ if (acrtc_state->active_planes && - (!updated_planes_and_streams || amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0)) + (!updated_planes_and_streams || amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) && +
[PATCH 2/2] drm/amd/display: Move PRIMARY plane zpos higher
From: Leo Li [Why] Compositors have different ways of assigning surfaces to DRM planes for render offloading. It may decide between various strategies: overlay, underlay, or a mix of both One way for compositors to implement the underlay strategy is to assign a higher zpos to the DRM_PRIMARY plane than the DRM_OVERLAY planes, effectively turning the DRM_OVERLAY plane into an underlay plane. Today, amdgpu attaches an immutable zpos of 0 to the DRM_PRIMARY plane. This however, is an arbitrary restriction. DCN pipes are general purpose, and can be arranged in any z-order. To support compositors using this allocation scheme, we can set a non-zero immutable zpos for the PRIMARY, allowing the placement of OVERLAYS (mutable zpos range 0-254) beneath the PRIMARY. [How] Assign a zpos = #no of OVERLAY planes to the PRIMARY plane. Then, clean up any assumptions in the driver of PRIMARY plane having the lowest zpos. Signed-off-by: Leo Li --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 96 ++- .../amd/display/amdgpu_dm/amdgpu_dm_plane.c | 17 +++- 2 files changed, 104 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 09ab330aed17..01b00f587701 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -80,6 +80,7 @@ #include #include #include +#include #include #include @@ -369,6 +370,20 @@ static inline void reverse_planes_order(struct dc_surface_update *array_of_surfa swap(array_of_surface_update[i], array_of_surface_update[j]); } +/* + * DC will program planes with their z-order determined by their ordering + * in the dc_surface_updates array. This comparator is used to sort them + * by descending zpos. + */ +static int dm_plane_layer_index_cmp(const void *a, const void *b) +{ + const struct dc_surface_update *sa = (struct dc_surface_update *)a; + const struct dc_surface_update *sb = (struct dc_surface_update *)b; + + /* Sort by descending dc_plane layer_index (i.e. normalized_zpos) */ + return sb->surface->layer_index - sa->surface->layer_index; +} + /** * update_planes_and_stream_adapter() - Send planes to be updated in DC * @@ -393,7 +408,8 @@ static inline bool update_planes_and_stream_adapter(struct dc *dc, struct dc_stream_update *stream_update, struct dc_surface_update *array_of_surface_update) { - reverse_planes_order(array_of_surface_update, planes_count); + sort(array_of_surface_update, planes_count, +sizeof(*array_of_surface_update), dm_plane_layer_index_cmp, NULL); /* * Previous frame finished and HW is ready for optimization. @@ -9363,6 +9379,8 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) for (j = 0; j < status->plane_count; j++) dummy_updates[j].surface = status->plane_states[0]; + sort(dummy_updates, status->plane_count, +sizeof(*dummy_updates), dm_plane_layer_index_cmp, NULL); mutex_lock(&dm->dc_lock); dc_update_planes_and_stream(dm->dc, @@ -10097,6 +10115,17 @@ static bool should_reset_plane(struct drm_atomic_state *state, if (new_crtc_state->color_mgmt_changed) return true; + /* +* On zpos change, planes need to be reordered by removing and re-adding +* them one by one to the dc state, in order of descending zpos. +* +* TODO: We can likely skip bandwidth validation if the only thing that +* changed about the plane was it'z z-ordering. +*/ + if (new_crtc_state->zpos_changed) { + return true; + } + if (drm_atomic_crtc_needs_modeset(new_crtc_state)) return true; @@ -10509,6 +10538,65 @@ dm_get_plane_scale(struct drm_plane_state *plane_state, *out_plane_scale_h = plane_state->crtc_h * 1000 / plane_src_h; } +/* + * The normalized_zpos value cannot be used by this iterator directly. It's only + * calculated for enabled planes, potentially causing normalized_zpos collisions + * between enabled/disabled planes in the atomic state. We need a unique value + * so that the iterator will not generate the same object twice, or loop + * indefinitely. + */ +static inline struct __drm_planes_state *__get_next_zpos( + struct drm_atomic_state *state, + struct __drm_planes_state *prev) +{ + unsigned int highest_zpos = 0, prev_zpos = 256; + uint32_t highest_id = 0, prev_id = UINT_MAX; + struct drm_plane_state *new_plane_state; + struct drm_plane *plane; + int i, highest_i = -1; + + if (prev != NULL) { + prev_zpos = prev->new_state->zpos; + prev_id = prev->ptr
[PATCH 1/2] drm/amd/display: Introduce overlay cursor mode
From: Leo Li [Why] DCN is the display hardware for amdgpu. DRM planes are backed by DCN hardware pipes, which carry pixel data from one end (memory), to the other (output encoder). Each DCN pipe has the ability to blend in a cursor early on in the pipeline. In other words, there are no dedicated cursor planes in DCN, which makes cursor behavior somewhat unintuitive for compositors. For example, if the cursor is in RGB format, but the top-most DRM plane is in YUV format, DCN will not be able to blend them. Because of this, amdgpu_dm rejects all configurations where a cursor needs to be enabled on top of a YUV formatted plane. >From a compositor's perspective, when computing an allocation for hardware plane offloading, this cursor-on-yuv configuration result in an atomic test failure. Since the failure reason is not obvious at all, compositors will likely fall back to full rendering, which is not ideal. Instead, amdgpu_dm can try to accommodate the cursor-on-yuv configuration by opportunistically reserving a separate DCN pipe just for the cursor. We can refer to this as "overlay cursor mode". It is contrasted with "native cursor mode", where the native DCN per-pipe cursor is used. [How] On each crtc, compute whether the cursor plane should be enabled in overlay mode (which currently, is iff the immediate plane below has a YUV format). If it is, mark the CRTC as requesting overlay cursor mode. During DC validation, attempt to enable a separate DCN pipe for the cursor if it's in overlay mode. If that fails, or if no overlay mode is requested, then fallback to native mode. Signed-off-by: Leo Li --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 309 +++--- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 7 + .../amd/display/amdgpu_dm/amdgpu_dm_crtc.c| 1 + .../amd/display/amdgpu_dm/amdgpu_dm_plane.c | 13 +- 4 files changed, 288 insertions(+), 42 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 21a61454c878..09ab330aed17 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -8359,8 +8359,19 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, * Disable the cursor first if we're disabling all the planes. * It'll remain on the screen after the planes are re-enabled * if we don't. +* +* If the cursor is transitioning from native to overlay mode, the +* native cursor needs to be disabled first. */ - if (acrtc_state->active_planes == 0) + if (acrtc_state->cursor_mode == DM_CURSOR_OVERLAY_MODE && + dm_old_crtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE) { + struct dc_cursor_position cursor_position = {0}; + dc_stream_set_cursor_position(acrtc_state->stream, + &cursor_position); + } + + if (acrtc_state->active_planes == 0 && + dm_old_crtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE) amdgpu_dm_commit_cursors(state); /* update planes when needed */ @@ -8374,7 +8385,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, struct dm_plane_state *dm_new_plane_state = to_dm_plane_state(new_plane_state); /* Cursor plane is handled after stream updates */ - if (plane->type == DRM_PLANE_TYPE_CURSOR) { + if (plane->type == DRM_PLANE_TYPE_CURSOR && + acrtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE) { if ((fb && crtc == pcrtc) || (old_plane_state->fb && old_plane_state->crtc == pcrtc)) cursor_update = true; @@ -8727,7 +8739,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, * This avoids redundant programming in the case where we're going * to be disabling a single plane - those pipes are being disabled. */ - if (acrtc_state->active_planes) + if (acrtc_state->active_planes && + acrtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE) amdgpu_dm_commit_cursors(state); cleanup: @@ -10039,7 +10052,8 @@ static bool should_reset_plane(struct drm_atomic_state *state, { struct drm_plane *other; struct drm_plane_state *old_other_state, *new_other_state; - struct drm_crtc_state *new_crtc_state; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; + struct dm_crtc_state *old_dm_crtc_state, *new_dm_crtc_state; struct amdgpu_device *adev = drm_to_adev(plane->dev); int i; @@ -10061,10 +10075,24 @@ static bool should_reset_plane(struct drm_atomic_state *state, new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc); + old_crtc_state = + drm_atomic_g
[PATCH 0/2] drm/amdgpu/display: Make multi-plane configurations more flexible
From: Leo Li These patches aim to make the amdgpgu KMS driver play nicer with compositors when building multi-plane scanout configurations. They do so by: 1. Making cursor behavior more sensible. 2. Allowing placement of DRM OVERLAY planes underneath the PRIMARY plane for 'underlay' configurations (perhaps more of a RFC, see below). Please see the commit messages for details. For #2, the simplest way to accomplish this was to increase the value of the immutable zpos property for the PRIMARY plane. This allowed OVERLAY planes with a mutable zpos range of (0-254) to be positioned underneath the PRIMARY for an underlay scanout configuration. Technically speaking, DCN hardware does not have a concept of primary or overlay planes - there are simply 4 general purpose hardware pipes that can be maped in any configuration. So the immutable zpos restriction on the PRIMARY plane is kind of arbitrary; it can have a mutable range of (0-254) just like the OVERLAYs. The distinction between PRIMARY and OVERLAY planes is also somewhat arbitrary. We can interpret PRIMARY as the first plane that should be enabled on a CRTC, but beyond that, it doesn't mean much for amdgpu. Therefore, I'm curious about how compositors devs understand KMS planes and their zpos properties, and how we would like to use them. It isn't clear to me how compositors wish to interpret and use the DRM zpos property, or differentiate between OVERLAY and PRIMARY planes, when it comes to setting up multi-plane scanout. Ultimately, what I'd like to answer is "What can we do on the KMS driver and DRM plane API side, that can make building multi-plane scanout configurations easier for compositors?" I'm hoping we can converge on something, whether that be updating the existing documentation to better define the usage, or update the API to provide support for something that is lacking. Thanks, Leo Some links to provide context and details: * What is underlay?: https://gitlab.freedesktop.org/emersion/libliftoff/-/issues/76 * Discussion on how to implement underlay on Weston: https://gitlab.freedesktop.org/wayland/weston/-/merge_requests/1258#note_2325164 Cc: Joshua Ashton Cc: Michel Dänzer Cc: Chao Guo Cc: Xaver Hugl Cc: Vikas Korjani Cc: Robert Mader Cc: Pekka Paalanen Cc: Sean Paul Cc: Simon Ser Cc: Shashank Sharma Cc: Harry Wentland Cc: Sebastian Wick Leo Li (2): drm/amd/display: Introduce overlay cursor mode drm/amd/display: Move PRIMARY plane zpos higher .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 405 -- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 7 + .../amd/display/amdgpu_dm/amdgpu_dm_crtc.c| 1 + .../amd/display/amdgpu_dm/amdgpu_dm_plane.c | 28 +- 4 files changed, 391 insertions(+), 50 deletions(-) -- 2.44.0
[PATCH] drm: Add PSR version 4 macro
From: Leo Li eDP 1.5 specification defines PSR version 4. It defines PSR1 and PSR2 support with selective-update (SU) capabilities, with additional support for Y-coordinate and Early Transport of the selective-update region. This differs from PSR version 3 in that early transport is supported for version 4, but not for version 3. Signed-off-by: Leo Li --- include/drm/drm_dp_helper.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 3f2715eb965f..05268c51acaa 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -360,6 +360,7 @@ struct drm_dp_aux; # define DP_PSR_IS_SUPPORTED1 # define DP_PSR2_IS_SUPPORTED 2 /* eDP 1.4 */ # define DP_PSR2_WITH_Y_COORD_IS_SUPPORTED 3 /* eDP 1.4a */ +# define DP_PSR2_WITH_ET_IS_SUPPORTED 4 /* eDP 1.5 (eDP 1.4b SCR) */ #define DP_PSR_CAPS 0x071 /* XXX 1.2? */ # define DP_PSR_NO_TRAIN_ON_EXIT1 -- 2.34.1
[PATCH v2] drm/amdgpu: Add DC feature mask to disable fractional pwm
From: Leo Li [Why] Some LED panel drivers might not like fractional PWM. In such cases, backlight flickering may be observed. [How] Add a DC feature mask to disable fractional PWM, and associate it with the preexisting dc_config flag. The flag is only plumbed through the dmcu firmware, so plumb it through the driver path as well. To disable, add the following to the linux cmdline: amdgpu.dcfeaturemask=0x4 Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=204957 Signed-off-by: Leo Li --- v2: Add bugzilla link drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 3 +++ drivers/gpu/drm/amd/display/dc/dce/dce_abm.c | 4 drivers/gpu/drm/amd/include/amd_shared.h | 1 + 3 files changed, 8 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 1cf4beb76835..73f917d4d1e1 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -728,6 +728,9 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) if (amdgpu_dc_feature_mask & DC_MULTI_MON_PP_MCLK_SWITCH_MASK) init_data.flags.multi_mon_pp_mclk_switch = true; + if (amdgpu_dc_feature_mask & DC_DISABLE_FRACTIONAL_PWM_MASK) + init_data.flags.disable_fractional_pwm = true; + init_data.flags.power_down_display_on_boot = true; #ifdef CONFIG_DRM_AMD_DC_DCN2_0 diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c b/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c index d759fdca7fdb..b8a3fc505c9b 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c @@ -404,6 +404,10 @@ static bool dce_abm_init_backlight(struct abm *abm) /* Enable the backlight output */ REG_UPDATE(BL_PWM_CNTL, BL_PWM_EN, 1); + /* Disable fractional pwm if configured */ + REG_UPDATE(BL_PWM_CNTL, BL_PWM_FRACTIONAL_EN, + abm->ctx->dc->config.disable_fractional_pwm ? 0 : 1); + /* Unlock group 2 backlight registers */ REG_UPDATE(BL_PWM_GRP1_REG_LOCK, BL_PWM_GRP1_REG_LOCK, 0); diff --git a/drivers/gpu/drm/amd/include/amd_shared.h b/drivers/gpu/drm/amd/include/amd_shared.h index 8889aaceec60..5450ed762b7a 100644 --- a/drivers/gpu/drm/amd/include/amd_shared.h +++ b/drivers/gpu/drm/amd/include/amd_shared.h @@ -143,6 +143,7 @@ enum PP_FEATURE_MASK { enum DC_FEATURE_MASK { DC_FBC_MASK = 0x1, DC_MULTI_MON_PP_MCLK_SWITCH_MASK = 0x2, + DC_DISABLE_FRACTIONAL_PWM_MASK = 0x4, }; enum amd_dpm_forced_level; -- 2.23.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH] drm/amdgpu: Add DC feature mask to disable fractional pwm
From: Leo Li [Why] Some LED panel drivers might not like fractional PWM. In such cases, backlight flickering may be observed. [How] Add a DC feature mask to disable fractional PWM, and associate it with the preexisting dc_config flag. The flag is only plumbed through the dmcu firmware, so plumb it through the driver path as well. To disable, add the following to the linux cmdline: amdgpu.dcfeaturemask=0x4 Signed-off-by: Leo Li --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 3 +++ drivers/gpu/drm/amd/display/dc/dce/dce_abm.c | 4 drivers/gpu/drm/amd/include/amd_shared.h | 1 + 3 files changed, 8 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 1cf4beb76835..73f917d4d1e1 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -728,6 +728,9 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) if (amdgpu_dc_feature_mask & DC_MULTI_MON_PP_MCLK_SWITCH_MASK) init_data.flags.multi_mon_pp_mclk_switch = true; + if (amdgpu_dc_feature_mask & DC_DISABLE_FRACTIONAL_PWM_MASK) + init_data.flags.disable_fractional_pwm = true; + init_data.flags.power_down_display_on_boot = true; #ifdef CONFIG_DRM_AMD_DC_DCN2_0 diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c b/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c index d759fdca7fdb..b8a3fc505c9b 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c @@ -404,6 +404,10 @@ static bool dce_abm_init_backlight(struct abm *abm) /* Enable the backlight output */ REG_UPDATE(BL_PWM_CNTL, BL_PWM_EN, 1); + /* Disable fractional pwm if configured */ + REG_UPDATE(BL_PWM_CNTL, BL_PWM_FRACTIONAL_EN, + abm->ctx->dc->config.disable_fractional_pwm ? 0 : 1); + /* Unlock group 2 backlight registers */ REG_UPDATE(BL_PWM_GRP1_REG_LOCK, BL_PWM_GRP1_REG_LOCK, 0); diff --git a/drivers/gpu/drm/amd/include/amd_shared.h b/drivers/gpu/drm/amd/include/amd_shared.h index 8889aaceec60..5450ed762b7a 100644 --- a/drivers/gpu/drm/amd/include/amd_shared.h +++ b/drivers/gpu/drm/amd/include/amd_shared.h @@ -143,6 +143,7 @@ enum PP_FEATURE_MASK { enum DC_FEATURE_MASK { DC_FBC_MASK = 0x1, DC_MULTI_MON_PP_MCLK_SWITCH_MASK = 0x2, + DC_DISABLE_FRACTIONAL_PWM_MASK = 0x4, }; enum amd_dpm_forced_level; -- 2.23.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 6/9 v2] drm/i915: Implement MST Aux device registration
From: Leo Li Implement late_register and early_unregister hooks for MST connectors. Call drm helpers for MST connector registration, which registers the AUX devices. Signed-off-by: Leo Li v2 changes: Unwind intel_connector_register on mst late register failure. --- drivers/gpu/drm/i915/display/intel_dp_mst.c | 33 +++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index 60652ebbdf61..0633ebf3f1bf 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -400,13 +400,42 @@ intel_dp_mst_detect(struct drm_connector *connector, bool force) intel_connector->port); } +static int +intel_dp_mst_connector_late_register(struct drm_connector *connector) +{ + struct intel_connector *intel_connector = to_intel_connector(connector); + struct drm_dp_mst_port *port = intel_connector->port; + + int ret; + + ret = intel_connector_register(connector); + if (ret) + return ret; + + ret = drm_dp_mst_connector_late_register(connector, port); + if (ret) + intel_connector_unregister(connector); + + return ret; +} + +static void +intel_dp_mst_connector_early_unregister(struct drm_connector *connector) +{ + struct intel_connector *intel_connector = to_intel_connector(connector); + struct drm_dp_mst_port *port = intel_connector->port; + + drm_dp_mst_connector_early_unregister(connector, port); + intel_connector_unregister(connector); +} + static const struct drm_connector_funcs intel_dp_mst_connector_funcs = { .detect = intel_dp_mst_detect, .fill_modes = drm_helper_probe_single_connector_modes, .atomic_get_property = intel_digital_connector_atomic_get_property, .atomic_set_property = intel_digital_connector_atomic_set_property, - .late_register = intel_connector_register, - .early_unregister = intel_connector_unregister, + .late_register = intel_dp_mst_connector_late_register, + .early_unregister = intel_dp_mst_connector_early_unregister, .destroy = intel_connector_destroy, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_duplicate_state = intel_digital_connector_duplicate_state, -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 8/9] drm/radeon: Implement MST Aux device registration
From: Leo Li Implement late_register and early_unregister hooks for MST connectors. Call drm helpers for MST connector registration, which registers the AUX devices. Cc: Alex Deucher Cc: Harry Wentland Signed-off-by: Leo Li --- drivers/gpu/drm/radeon/radeon_dp_mst.c | 22 ++ 1 file changed, 22 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon_dp_mst.c b/drivers/gpu/drm/radeon/radeon_dp_mst.c index 2994f07fbad9..2d53699734fb 100644 --- a/drivers/gpu/drm/radeon/radeon_dp_mst.c +++ b/drivers/gpu/drm/radeon/radeon_dp_mst.c @@ -260,11 +260,33 @@ radeon_dp_mst_connector_destroy(struct drm_connector *connector) kfree(radeon_connector); } +static int +radeon_dp_mst_connector_late_register(struct drm_connector *connector) +{ + struct radeon_connector *radeon_connector = + to_radeon_connector(connector); + struct drm_dp_mst_port *port = radeon_connector->port; + + return drm_dp_mst_connector_late_register(connector, port); +} + +static void +radeon_dp_mst_connector_early_unregister(struct drm_connector *connector) +{ + struct radeon_connector *radeon_connector = + to_radeon_connector(connector); + struct drm_dp_mst_port *port = radeon_connector->port; + + drm_dp_mst_connector_early_unregister(connector, port); +} + static const struct drm_connector_funcs radeon_dp_mst_connector_funcs = { .dpms = drm_helper_connector_dpms, .detect = radeon_dp_mst_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = radeon_dp_mst_connector_destroy, + .late_register = radeon_dp_mst_connector_late_register, + .early_unregister = radeon_dp_mst_connector_early_unregister, }; static struct drm_connector *radeon_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 4/9] drm/bridge/analogix-anx78xx: Use connector kdev as aux device parent
From: Leo Li Set the connector's kernel device as the parent for the aux kernel device. This allows udev rules to access connector attributes when creating symlinks to aux devices. To do so, the connector needs to be registered beforehand. Therefore, shift aux registration to be after connector registration. Cc: Enric Balletbo i Serra Cc: Nicolas Boichat Signed-off-by: Leo Li --- drivers/gpu/drm/bridge/analogix-anx78xx.c | 22 +++--- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/bridge/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix-anx78xx.c index 3c7cc5af735c..c2800cd3e2ee 100644 --- a/drivers/gpu/drm/bridge/analogix-anx78xx.c +++ b/drivers/gpu/drm/bridge/analogix-anx78xx.c @@ -1008,17 +1008,6 @@ static int anx78xx_bridge_attach(struct drm_bridge *bridge) return -ENODEV; } - /* Register aux channel */ - anx78xx->aux.name = "DP-AUX"; - anx78xx->aux.dev = &anx78xx->client->dev; - anx78xx->aux.transfer = anx78xx_aux_transfer; - - err = drm_dp_aux_register(&anx78xx->aux); - if (err < 0) { - DRM_ERROR("Failed to register aux channel: %d\n", err); - return err; - } - err = drm_connector_init(bridge->dev, &anx78xx->connector, &anx78xx_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort); @@ -1038,6 +1027,17 @@ static int anx78xx_bridge_attach(struct drm_bridge *bridge) anx78xx->connector.polled = DRM_CONNECTOR_POLL_HPD; + /* Register aux channel */ + anx78xx->aux.name = "DP-AUX"; + anx78xx->aux.dev = anx78xx->connector.kdev; + anx78xx->aux.transfer = anx78xx_aux_transfer; + + err = drm_dp_aux_register(&anx78xx->aux); + if (err < 0) { + DRM_ERROR("Failed to register aux channel: %d\n", err); + return err; + } + err = drm_connector_attach_encoder(&anx78xx->connector, bridge->encoder); if (err) { -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 9/9] drm/amd/display: Implement MST Aux device registration
From: Leo Li Implement late_register and early_unregister hooks for MST connectors. Call drm helpers for MST connector registration, which registers the AUX devices. Cc: Jerry Zuo Cc: Nicholas Kazlauskas Cc: Harry Wentland Signed-off-by: Leo Li --- .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 24 ++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index 53d2cfe62e13..16218a202b59 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -156,6 +156,26 @@ dm_dp_mst_connector_destroy(struct drm_connector *connector) kfree(amdgpu_dm_connector); } +static int +amdgpu_dm_mst_connector_late_register(struct drm_connector *connector) +{ + struct amdgpu_dm_connector *amdgpu_dm_connector = + to_amdgpu_dm_connector(connector); + struct drm_dp_mst_port *port = amdgpu_dm_connector->port; + + return drm_dp_mst_connector_late_register(connector, port); +} + +static void +amdgpu_dm_mst_connector_early_unregister(struct drm_connector *connector) +{ + struct amdgpu_dm_connector *amdgpu_dm_connector = + to_amdgpu_dm_connector(connector); + struct drm_dp_mst_port *port = amdgpu_dm_connector->port; + + drm_dp_mst_connector_early_unregister(connector, port); +} + static const struct drm_connector_funcs dm_dp_mst_connector_funcs = { .detect = dm_dp_mst_detect, .fill_modes = drm_helper_probe_single_connector_modes, @@ -164,7 +184,9 @@ static const struct drm_connector_funcs dm_dp_mst_connector_funcs = { .atomic_duplicate_state = amdgpu_dm_connector_atomic_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_set_property = amdgpu_dm_connector_atomic_set_property, - .atomic_get_property = amdgpu_dm_connector_atomic_get_property + .atomic_get_property = amdgpu_dm_connector_atomic_get_property, + .late_register = amdgpu_dm_mst_connector_late_register, + .early_unregister = amdgpu_dm_mst_connector_early_unregister, }; static int dm_dp_mst_get_modes(struct drm_connector *connector) -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 5/9] drm/amd/display: Use connector kdev as aux device parent
From: Leo Li Set the connector's kernel device as the parent for the aux kernel device. This allows udev rules to access connector attributes when creating symlinks to aux devices. For example, the following udev rule: SUBSYSTEM=="drm_dp_aux_dev", SUBSYSTEMS=="drm", ATTRS{edid}=="*", SYMLINK+="drm_dp_aux/by-name/$id" Will create the following symlinks using the connector's name: $ ls /dev/drm_dp_aux/by-name/ card0-DP-1 card0-DP-2 card0-DP-3 Cc: Nicholas Kazlauskas Cc: Jerry (Fangzhi) Zuo Cc: Harry Wentland Signed-off-by: Leo Li --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index 6e205ee36ac3..53d2cfe62e13 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -388,7 +388,7 @@ void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm, struct amdgpu_dm_connector *aconnector) { aconnector->dm_dp_aux.aux.name = "dmdc"; - aconnector->dm_dp_aux.aux.dev = dm->adev->dev; + aconnector->dm_dp_aux.aux.dev = aconnector->base.kdev; aconnector->dm_dp_aux.aux.transfer = dm_dp_aux_transfer; aconnector->dm_dp_aux.ddc_service = aconnector->dc_link->ddc; -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 2/9 v3] drm/dp_mst: Enable registration of AUX devices for MST ports
From: Ville Syrjälä All available downstream ports - physical and logical - are exposed for each MST device. They are listed in /dev/, following the same naming scheme as SST devices by appending an incremental ID. Although all downstream ports are exposed, only some will work as expected. Consider the following topology: +-+ | ASIC | +-+ Conn-0| | +v+ +| MST HUB |+ |+-+| | | |Port-1 Port-2| +-v-+ +-v-+ | MST | | SST | | Display | | Display | +---+ +---+ |Port-1 x MST Path | MST Device --+-- sst:0 | MST Hub mst:0-1 | MST Display mst:0-1-1 | MST Display's disconnected DP out mst:0-1-8 | MST Display's internal sink mst:0-2 | SST Display On certain MST displays, the upstream physical port will ACK DPCD reads. However, reads on the local logical port to the internal sink will *NAK*. i.e. reading mst:0-1 ACKs, but mst:0-1-8 NAKs. There may also be duplicates. Some displays will return the same GUID when reading DPCD from both mst:0-1 and mst:0-1-8. There are some device-dependent behavior as well. The MST hub used during testing will actually *ACK* read requests on a disconnected physical port, whereas the MST displays will NAK. In light of these discrepancies, it's simpler to expose all downstream ports - both physical and logical - and let the user decide what to use. v3 changes: * Change WARN_ON_ONCE -> DRM_ERROR on dpcd read errors * Docstring and cosmetic fixes v2 changes: Moved remote aux device (un)registration to new mst connector late register and early unregister helpers. Drivers should call these from their own mst connector function hooks. This is to solve an issue during driver unload, where mst connector devices are unregistered before the remote aux devices are. In a setup where aux devices are created as children of connector devices, the aux device would be removed too early, and uncleanly. Doing so in early_unregister solves this issue, as that is called before connector unregistration. Signed-off-by: Ville Syrjälä Signed-off-by: Leo Li Reviewed-by: Lyude Paul --- drivers/gpu/drm/drm_dp_aux_dev.c | 15 ++- drivers/gpu/drm/drm_dp_mst_topology.c | 144 -- include/drm/drm_dp_helper.h | 4 + include/drm/drm_dp_mst_helper.h | 11 ++ 4 files changed, 162 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_aux_dev.c b/drivers/gpu/drm/drm_dp_aux_dev.c index 26e38dacf654..0cfb386754c3 100644 --- a/drivers/gpu/drm/drm_dp_aux_dev.c +++ b/drivers/gpu/drm/drm_dp_aux_dev.c @@ -37,6 +37,7 @@ #include #include +#include #include #include "drm_crtc_helper_internal.h" @@ -162,7 +163,12 @@ static ssize_t auxdev_read_iter(struct kiocb *iocb, struct iov_iter *to) break; } - res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo); + if (aux_dev->aux->is_remote) + res = drm_dp_mst_dpcd_read(aux_dev->aux, pos, buf, + todo); + else + res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo); + if (res <= 0) break; @@ -209,7 +215,12 @@ static ssize_t auxdev_write_iter(struct kiocb *iocb, struct iov_iter *from) break; } - res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo); + if (aux_dev->aux->is_remote) + res = drm_dp_mst_dpcd_write(aux_dev->aux, pos, buf, + todo); + else + res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo); + if (res <= 0) break; diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 0984b9a34d55..4733d3350ede 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -36,6 +36,8 @@ #include #include +#include "drm_crtc_helper_internal.h" + /** * DOC: dp mst helper * @@ -53,6 +55,9 @@ static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr, int id, struct drm_dp_payload *payload); +static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port, +int offset, int size, u8 *bytes); static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port,
[PATCH 3/9] drm/nouveau: Use connector kdev as aux device parent
From: Leo Li Set the connector's kernel device as the parent for the aux kernel device. This allows udev rules to access connector attributes when creating symlinks to aux devices. Cc: Ben Skeggs Signed-off-by: Leo Li Reviewed-by: Lyude Paul --- drivers/gpu/drm/nouveau/nouveau_connector.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 8f15281faa79..330d7d29a6e3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -1349,7 +1349,7 @@ nouveau_connector_create(struct drm_device *dev, break; case DRM_MODE_CONNECTOR_DisplayPort: case DRM_MODE_CONNECTOR_eDP: - nv_connector->aux.dev = dev->dev; + nv_connector->aux.dev = connector->kdev; nv_connector->aux.transfer = nouveau_connector_aux_xfer; snprintf(aux_name, sizeof(aux_name), "sor-%04x-%04x", dcbe->hasht, dcbe->hashm); -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 7/9] drm/nouveau/kms/nv50: Implement MST Aux device registration
From: Leo Li Implement late_register and early_unregister hooks for MST connectors. Call drm helpers for MST connector registration, which registers the AUX devices. Cc: Ben Skeggs Signed-off-by: Leo Li --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 20 1 file changed, 20 insertions(+) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 8497768f1b41..0d6e9350ba44 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -1024,6 +1024,24 @@ nv50_mstc_destroy(struct drm_connector *connector) kfree(mstc); } +static int +nv50_mstc_late_register(struct drm_connector *connector) +{ + struct nv50_mstc *mstc = nv50_mstc(connector); + struct drm_dp_mst_port *port = mstc->port; + + return drm_dp_mst_connector_late_register(connector, port); +} + +static void +nv50_mstc_early_unregister(struct drm_connector *connector) +{ + struct nv50_mstc *mstc = nv50_mstc(connector); + struct drm_dp_mst_port *port = mstc->port; + + drm_dp_mst_connector_early_unregister(connector, port); +} + static const struct drm_connector_funcs nv50_mstc = { .reset = nouveau_conn_reset, @@ -1034,6 +1052,8 @@ nv50_mstc = { .atomic_destroy_state = nouveau_conn_atomic_destroy_state, .atomic_set_property = nouveau_conn_atomic_set_property, .atomic_get_property = nouveau_conn_atomic_get_property, + .late_register = nv50_mstc_late_register, + .early_unregister = nv50_mstc_early_unregister, }; static int -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 0/9] MST AUX Devices (v3)
From: Leo Li Here's what changed in v3: * Dropped patch to expose the mst-path device attribute, in lieu of ongoing discussion for defining a better connector path property * Change WARN_ON_ONCE -> DRM_ERROR on MST dpcd read errors to avoid tainting the kernel * Unwind intel_connector_register() on drm_dp_mst_connector_late_register() failure. * Docstring and cosmetic fixes Leo Li (8): drm/dp: Use non-cyclic idr drm/nouveau: Use connector kdev as aux device parent drm/bridge/analogix-anx78xx: Use connector kdev as aux device parent drm/amd/display: Use connector kdev as aux device parent drm/i915: Implement MST Aux device registration (v2) drm/nouveau/kms/nv50: Implement MST Aux device registration drm/radeon: Implement MST Aux device registration drm/amd/display: Implement MST Aux device registration Ville Syrjälä (1): drm/dp_mst: Enable registration of AUX devices for MST ports .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 26 +++- drivers/gpu/drm/bridge/analogix-anx78xx.c | 22 +-- drivers/gpu/drm/drm_dp_aux_dev.c | 18 ++- drivers/gpu/drm/drm_dp_mst_topology.c | 144 -- drivers/gpu/drm/i915/display/intel_dp_mst.c | 33 +++- drivers/gpu/drm/nouveau/dispnv50/disp.c | 20 +++ drivers/gpu/drm/nouveau/nouveau_connector.c | 2 +- drivers/gpu/drm/radeon/radeon_dp_mst.c| 22 +++ include/drm/drm_dp_helper.h | 4 + include/drm/drm_dp_mst_helper.h | 11 ++ 10 files changed, 272 insertions(+), 30 deletions(-) -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 1/9] drm/dp: Use non-cyclic idr
From: Leo Li In preparation for adding aux devices for DP MST, make the IDR non-cyclic. That way, hotplug cycling MST devices won't needlessly increment the minor version index. Signed-off-by: Leo Li Reviewed-by: Lyude Paul Reviewed-by: Ville Syrjälä --- drivers/gpu/drm/drm_dp_aux_dev.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_aux_dev.c b/drivers/gpu/drm/drm_dp_aux_dev.c index 5be28e3295f3..26e38dacf654 100644 --- a/drivers/gpu/drm/drm_dp_aux_dev.c +++ b/drivers/gpu/drm/drm_dp_aux_dev.c @@ -82,8 +82,7 @@ static struct drm_dp_aux_dev *alloc_drm_dp_aux_dev(struct drm_dp_aux *aux) kref_init(&aux_dev->refcount); mutex_lock(&aux_idr_mutex); - index = idr_alloc_cyclic(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, -GFP_KERNEL); + index = idr_alloc(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, GFP_KERNEL); mutex_unlock(&aux_idr_mutex); if (index < 0) { kfree(aux_dev); -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2] drm/sysfs: Add mstpath attribute to connector devices
From: Leo Li This can be used to create more descriptive symlinks for MST aux devices. Consider the following udev rule: SUBSYSTEM=="drm_dp_aux_dev", SUBSYSTEMS=="drm", ATTRS{mstpath}=="?*", SYMLINK+="drm_dp_aux/by-path/$attr{mstpath}" The following symlinks will be created (depending on your MST topology): $ ls /dev/drm_dp_aux/by-path/ card0-mst:0-1 card0-mst:0-1-1 card0-mst:0-1-8 card0-mst:0-8 v2: remove unnecessary locking of mode_config.mutex Signed-off-by: Leo Li --- drivers/gpu/drm/drm_sysfs.c | 20 1 file changed, 20 insertions(+) diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index ad10810bc972..7d483ab684a0 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -236,16 +236,36 @@ static ssize_t modes_show(struct device *device, return written; } +static ssize_t mstpath_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = to_drm_connector(device); + ssize_t ret = 0; + char *path; + + if (!connector->path_blob_ptr) + return ret; + + path = connector->path_blob_ptr->data; + ret = snprintf(buf, PAGE_SIZE, "card%d-%s\n", + connector->dev->primary->index, path); + + return ret; +} + static DEVICE_ATTR_RW(status); static DEVICE_ATTR_RO(enabled); static DEVICE_ATTR_RO(dpms); static DEVICE_ATTR_RO(modes); +static DEVICE_ATTR_RO(mstpath); static struct attribute *connector_dev_attrs[] = { &dev_attr_status.attr, &dev_attr_enabled.attr, &dev_attr_dpms.attr, &dev_attr_modes.attr, + &dev_attr_mstpath.attr, NULL }; -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 08/10] drm/nouveau/kms/nv50: Implement MST Aux device registration
From: Leo Li Implement late_register and early_unregister hooks for MST connectors. Call drm helpers for MST connector registration, which registers the AUX devices. Cc: Ben Skeggs Signed-off-by: Leo Li --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 20 1 file changed, 20 insertions(+) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 7ba373f493b2..6d0fbb6036cf 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -1019,6 +1019,24 @@ nv50_mstc_destroy(struct drm_connector *connector) kfree(mstc); } +static int +nv50_mstc_late_register(struct drm_connector *connector) +{ + struct nv50_mstc *mstc = nv50_mstc(connector); + struct drm_dp_mst_port *port = mstc->port; + + return drm_dp_mst_connector_late_register(connector, port); +} + +static void +nv50_mstc_early_unregister(struct drm_connector *connector) +{ + struct nv50_mstc *mstc = nv50_mstc(connector); + struct drm_dp_mst_port *port = mstc->port; + + drm_dp_mst_connector_early_unregister(connector, port); +} + static const struct drm_connector_funcs nv50_mstc = { .reset = nouveau_conn_reset, @@ -1029,6 +1047,8 @@ nv50_mstc = { .atomic_destroy_state = nouveau_conn_atomic_destroy_state, .atomic_set_property = nouveau_conn_atomic_set_property, .atomic_get_property = nouveau_conn_atomic_get_property, + .late_register = nv50_mstc_late_register, + .early_unregister = nv50_mstc_early_unregister, }; static int -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 03/10] drm/sysfs: Add mstpath attribute to connector devices
From: Leo Li This can be used to create more descriptive symlinks for MST aux devices. Consider the following udev rule: SUBSYSTEM=="drm_dp_aux_dev", SUBSYSTEMS=="drm", ATTRS{mstpath}=="?*", SYMLINK+="drm_dp_aux/by-path/$attr{mstpath}" The following symlinks will be created (depending on your MST topology): $ ls /dev/drm_dp_aux/by-path/ card0-mst:0-1 card0-mst:0-1-1 card0-mst:0-1-8 card0-mst:0-8 Signed-off-by: Leo Li --- drivers/gpu/drm/drm_sysfs.c | 23 +++ 1 file changed, 23 insertions(+) diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index ad10810bc972..53fd1f71e900 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -236,16 +236,39 @@ static ssize_t modes_show(struct device *device, return written; } +static ssize_t mstpath_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = to_drm_connector(device); + ssize_t ret = 0; + char *path; + + mutex_lock(&connector->dev->mode_config.mutex); + if (!connector->path_blob_ptr) + goto unlock; + + path = connector->path_blob_ptr->data; + ret = snprintf(buf, PAGE_SIZE, "card%d-%s\n", + connector->dev->primary->index, path); + +unlock: + mutex_unlock(&connector->dev->mode_config.mutex); + return ret; +} + static DEVICE_ATTR_RW(status); static DEVICE_ATTR_RO(enabled); static DEVICE_ATTR_RO(dpms); static DEVICE_ATTR_RO(modes); +static DEVICE_ATTR_RO(mstpath); static struct attribute *connector_dev_attrs[] = { &dev_attr_status.attr, &dev_attr_enabled.attr, &dev_attr_dpms.attr, &dev_attr_modes.attr, + &dev_attr_mstpath.attr, NULL }; -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 04/10] drm/nouveau: Use connector kdev as aux device parent
From: Leo Li Set the connector's kernel device as the parent for the aux kernel device. This allows udev rules to access connector attributes when creating symlinks to aux devices. Cc: Ben Skeggs Signed-off-by: Leo Li --- drivers/gpu/drm/nouveau/nouveau_connector.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 4116ee62adaf..e32def09ef89 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -1346,7 +1346,7 @@ nouveau_connector_create(struct drm_device *dev, break; case DRM_MODE_CONNECTOR_DisplayPort: case DRM_MODE_CONNECTOR_eDP: - nv_connector->aux.dev = dev->dev; + nv_connector->aux.dev = connector->kdev; nv_connector->aux.transfer = nouveau_connector_aux_xfer; snprintf(aux_name, sizeof(aux_name), "sor-%04x-%04x", dcbe->hasht, dcbe->hashm); -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 06/10] drm/amd/display: Use connector kdev as aux device parent
From: Leo Li Set the connector's kernel device as the parent for the aux kernel device. This allows udev rules to access connector attributes when creating symlinks to aux devices. For example, the following udev rule: SUBSYSTEM=="drm_dp_aux_dev", SUBSYSTEMS=="drm", ATTRS{edid}=="*", SYMLINK+="drm_dp_aux/by-name/$id" Will create the following symlinks using the connector's name: $ ls /dev/drm_dp_aux/by-name/ card0-DP-1 card0-DP-2 card0-DP-3 Cc: Nicholas Kazlauskas Cc: Jerry (Fangzhi) Zuo Signed-off-by: Leo Li --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index 6e205ee36ac3..53d2cfe62e13 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -388,7 +388,7 @@ void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm, struct amdgpu_dm_connector *aconnector) { aconnector->dm_dp_aux.aux.name = "dmdc"; - aconnector->dm_dp_aux.aux.dev = dm->adev->dev; + aconnector->dm_dp_aux.aux.dev = aconnector->base.kdev; aconnector->dm_dp_aux.aux.transfer = dm_dp_aux_transfer; aconnector->dm_dp_aux.ddc_service = aconnector->dc_link->ddc; -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 01/10] drm/dp: Use non-cyclic idr
From: Leo Li In preparation for adding aux devices for DP MST, make the IDR non-cyclic. That way, hotplug cycling MST devices won't needlessly increment the minor version index. Signed-off-by: Leo Li Reviewed-by: Lyude Paul Reviewed-by: Ville Syrjälä --- drivers/gpu/drm/drm_dp_aux_dev.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_aux_dev.c b/drivers/gpu/drm/drm_dp_aux_dev.c index 5be28e3295f3..26e38dacf654 100644 --- a/drivers/gpu/drm/drm_dp_aux_dev.c +++ b/drivers/gpu/drm/drm_dp_aux_dev.c @@ -82,8 +82,7 @@ static struct drm_dp_aux_dev *alloc_drm_dp_aux_dev(struct drm_dp_aux *aux) kref_init(&aux_dev->refcount); mutex_lock(&aux_idr_mutex); - index = idr_alloc_cyclic(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, -GFP_KERNEL); + index = idr_alloc(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, GFP_KERNEL); mutex_unlock(&aux_idr_mutex); if (index < 0) { kfree(aux_dev); -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 05/10] drm/bridge/analogix-anx78xx: Use connector kdev as aux device parent
From: Leo Li Set the connector's kernel device as the parent for the aux kernel device. This allows udev rules to access connector attributes when creating symlinks to aux devices. To do so, the connector needs to be registered beforehand. Therefore, shift aux registration to be after connector registration. Cc: Enric Balletbo i Serra Cc: Nicolas Boichat Signed-off-by: Leo Li --- drivers/gpu/drm/bridge/analogix-anx78xx.c | 22 +++--- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/bridge/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix-anx78xx.c index 3c7cc5af735c..c2800cd3e2ee 100644 --- a/drivers/gpu/drm/bridge/analogix-anx78xx.c +++ b/drivers/gpu/drm/bridge/analogix-anx78xx.c @@ -1008,17 +1008,6 @@ static int anx78xx_bridge_attach(struct drm_bridge *bridge) return -ENODEV; } - /* Register aux channel */ - anx78xx->aux.name = "DP-AUX"; - anx78xx->aux.dev = &anx78xx->client->dev; - anx78xx->aux.transfer = anx78xx_aux_transfer; - - err = drm_dp_aux_register(&anx78xx->aux); - if (err < 0) { - DRM_ERROR("Failed to register aux channel: %d\n", err); - return err; - } - err = drm_connector_init(bridge->dev, &anx78xx->connector, &anx78xx_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort); @@ -1038,6 +1027,17 @@ static int anx78xx_bridge_attach(struct drm_bridge *bridge) anx78xx->connector.polled = DRM_CONNECTOR_POLL_HPD; + /* Register aux channel */ + anx78xx->aux.name = "DP-AUX"; + anx78xx->aux.dev = anx78xx->connector.kdev; + anx78xx->aux.transfer = anx78xx_aux_transfer; + + err = drm_dp_aux_register(&anx78xx->aux); + if (err < 0) { + DRM_ERROR("Failed to register aux channel: %d\n", err); + return err; + } + err = drm_connector_attach_encoder(&anx78xx->connector, bridge->encoder); if (err) { -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 00/10] Enable MST Aux devices (v2)
From: Leo Li Hi all, Here's the second revision of patches to enable mst aux devices. v2 fixes an aux device unregistration issue during driver unload. See patch 2/10 for details. Consequently, drivers supporting mst are modified to use the new mst connector late register and early unregister helpers. Thanks, Leo Leo Li (9): drm/dp: Use non-cyclic idr drm/sysfs: Add mstpath attribute to connector devices drm/nouveau: Use connector kdev as aux device parent drm/bridge/analogix-anx78xx: Use connector kdev as aux device parent drm/amd/display: Use connector kdev as aux device parent drm/i915: Implement MST Aux device registration drm/nouveau/kms/nv50: Implement MST Aux device registration drm/radeon: Implement MST Aux device registration drm/amd/display: Implement MST Aux device registration Ville Syrjälä (1): drm/dp_mst: Enable registration of AUX devices for MST ports .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 26 +++- drivers/gpu/drm/bridge/analogix-anx78xx.c | 22 +-- drivers/gpu/drm/drm_dp_aux_dev.c | 19 ++- drivers/gpu/drm/drm_dp_mst_topology.c | 137 -- drivers/gpu/drm/drm_sysfs.c | 23 +++ drivers/gpu/drm/i915/display/intel_dp_mst.c | 29 +++- drivers/gpu/drm/nouveau/dispnv50/disp.c | 20 +++ drivers/gpu/drm/nouveau/nouveau_connector.c | 2 +- drivers/gpu/drm/radeon/radeon_dp_mst.c| 22 +++ include/drm/drm_dp_helper.h | 4 + include/drm/drm_dp_mst_helper.h | 11 ++ 11 files changed, 285 insertions(+), 30 deletions(-) -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 02/10] drm/dp_mst: Enable registration of AUX devices for MST ports (v2)
From: Ville Syrjälä All available downstream ports - physical and logical - are exposed for each MST device. They are listed in /dev/, following the same naming scheme as SST devices by appending an incremental ID. Although all downstream ports are exposed, only some will work as expected. Consider the following topology: +-+ | ASIC | +-+ Conn-0| | +v+ +| MST HUB |+ |+-+| | | |Port-1 Port-2| +-v-+ +-v-+ | MST | | SST | | Display | | Display | +---+ +---+ |Port-1 x MST Path | MST Device --+-- sst:0 | MST Hub mst:0-1 | MST Display mst:0-1-1 | MST Display's disconnected DP out mst:0-1-8 | MST Display's internal sink mst:0-2 | SST Display On certain MST displays, the upstream physical port will ACK DPCD reads. However, reads on the local logical port to the internal sink will *NAK*. i.e. reading mst:0-1 ACKs, but mst:0-1-8 NAKs. There may also be duplicates. Some displays will return the same GUID when reading DPCD from both mst:0-1 and mst:0-1-8. There are some device-dependent behavior as well. The MST hub used during testing will actually *ACK* read requests on a disconnected physical port, whereas the MST displays will NAK. In light of these discrepancies, it's simpler to expose all downstream ports - both physical and logical - and let the user decide what to use. (v2) changes: Moved remote aux device (un)registration to new mst connector late register and early unregister helpers. Drivers should call these from their own mst connector function hooks. This is to solve an issue during driver unload, where mst connector devices are unregistered before the remote aux devices are. In a setup where aux devices are created as children of connector devices, the aux device would be removed too early, and uncleanly. Doing so in early_unregister solves this issue, as that is called before connector unregistration. Signed-off-by: Ville Syrjälä Signed-off-by: Leo Li --- drivers/gpu/drm/drm_dp_aux_dev.c | 16 ++- drivers/gpu/drm/drm_dp_mst_topology.c | 137 -- include/drm/drm_dp_helper.h | 4 + include/drm/drm_dp_mst_helper.h | 11 +++ 4 files changed, 156 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_aux_dev.c b/drivers/gpu/drm/drm_dp_aux_dev.c index 26e38dacf654..4aa5e455e894 100644 --- a/drivers/gpu/drm/drm_dp_aux_dev.c +++ b/drivers/gpu/drm/drm_dp_aux_dev.c @@ -37,6 +37,7 @@ #include #include +#include #include #include "drm_crtc_helper_internal.h" @@ -116,6 +117,7 @@ static ssize_t name_show(struct device *dev, return res; } + static DEVICE_ATTR_RO(name); static struct attribute *drm_dp_aux_attrs[] = { @@ -162,7 +164,12 @@ static ssize_t auxdev_read_iter(struct kiocb *iocb, struct iov_iter *to) break; } - res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo); + if (aux_dev->aux->is_remote) + res = drm_dp_mst_dpcd_read(aux_dev->aux, pos, buf, + todo); + else + res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo); + if (res <= 0) break; @@ -209,7 +216,12 @@ static ssize_t auxdev_write_iter(struct kiocb *iocb, struct iov_iter *from) break; } - res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo); + if (aux_dev->aux->is_remote) + res = drm_dp_mst_dpcd_write(aux_dev->aux, pos, buf, + todo); + else + res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo); + if (res <= 0) break; diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 0984b9a34d55..dde79c44b625 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -36,6 +36,8 @@ #include #include +#include "drm_crtc_helper_internal.h" + /** * DOC: dp mst helper * @@ -53,6 +55,9 @@ static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr, int id, struct drm_dp_payload *payload); +static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port, +int offset, int size, u8 *bytes); static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
[PATCH 10/10] drm/amd/display: Implement MST Aux device registration
From: Leo Li Implement late_register and early_unregister hooks for MST connectors. Call drm helpers for MST connector registration, which registers the AUX devices. Cc: Jerry Zuo Cc: Nicholas Kazlauskas Signed-off-by: Leo Li --- .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 24 ++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index 53d2cfe62e13..16218a202b59 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -156,6 +156,26 @@ dm_dp_mst_connector_destroy(struct drm_connector *connector) kfree(amdgpu_dm_connector); } +static int +amdgpu_dm_mst_connector_late_register(struct drm_connector *connector) +{ + struct amdgpu_dm_connector *amdgpu_dm_connector = + to_amdgpu_dm_connector(connector); + struct drm_dp_mst_port *port = amdgpu_dm_connector->port; + + return drm_dp_mst_connector_late_register(connector, port); +} + +static void +amdgpu_dm_mst_connector_early_unregister(struct drm_connector *connector) +{ + struct amdgpu_dm_connector *amdgpu_dm_connector = + to_amdgpu_dm_connector(connector); + struct drm_dp_mst_port *port = amdgpu_dm_connector->port; + + drm_dp_mst_connector_early_unregister(connector, port); +} + static const struct drm_connector_funcs dm_dp_mst_connector_funcs = { .detect = dm_dp_mst_detect, .fill_modes = drm_helper_probe_single_connector_modes, @@ -164,7 +184,9 @@ static const struct drm_connector_funcs dm_dp_mst_connector_funcs = { .atomic_duplicate_state = amdgpu_dm_connector_atomic_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_set_property = amdgpu_dm_connector_atomic_set_property, - .atomic_get_property = amdgpu_dm_connector_atomic_get_property + .atomic_get_property = amdgpu_dm_connector_atomic_get_property, + .late_register = amdgpu_dm_mst_connector_late_register, + .early_unregister = amdgpu_dm_mst_connector_early_unregister, }; static int dm_dp_mst_get_modes(struct drm_connector *connector) -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 09/10] drm/radeon: Implement MST Aux device registration
From: Leo Li Implement late_register and early_unregister hooks for MST connectors. Call drm helpers for MST connector registration, which registers the AUX devices. Cc: Alex Deucher Signed-off-by: Leo Li --- drivers/gpu/drm/radeon/radeon_dp_mst.c | 22 ++ 1 file changed, 22 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon_dp_mst.c b/drivers/gpu/drm/radeon/radeon_dp_mst.c index 2994f07fbad9..2d53699734fb 100644 --- a/drivers/gpu/drm/radeon/radeon_dp_mst.c +++ b/drivers/gpu/drm/radeon/radeon_dp_mst.c @@ -260,11 +260,33 @@ radeon_dp_mst_connector_destroy(struct drm_connector *connector) kfree(radeon_connector); } +static int +radeon_dp_mst_connector_late_register(struct drm_connector *connector) +{ + struct radeon_connector *radeon_connector = + to_radeon_connector(connector); + struct drm_dp_mst_port *port = radeon_connector->port; + + return drm_dp_mst_connector_late_register(connector, port); +} + +static void +radeon_dp_mst_connector_early_unregister(struct drm_connector *connector) +{ + struct radeon_connector *radeon_connector = + to_radeon_connector(connector); + struct drm_dp_mst_port *port = radeon_connector->port; + + drm_dp_mst_connector_early_unregister(connector, port); +} + static const struct drm_connector_funcs radeon_dp_mst_connector_funcs = { .dpms = drm_helper_connector_dpms, .detect = radeon_dp_mst_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = radeon_dp_mst_connector_destroy, + .late_register = radeon_dp_mst_connector_late_register, + .early_unregister = radeon_dp_mst_connector_early_unregister, }; static struct drm_connector *radeon_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 07/10] drm/i915: Implement MST Aux device registration
From: Leo Li Implement late_register and early_unregister hooks for MST connectors. Call drm helpers for MST connector registration, which registers the AUX devices. Signed-off-by: Leo Li --- drivers/gpu/drm/i915/display/intel_dp_mst.c | 29 +++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index 60652ebbdf61..be309016f746 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -400,13 +400,38 @@ intel_dp_mst_detect(struct drm_connector *connector, bool force) intel_connector->port); } +static int +intel_dp_mst_connector_late_register(struct drm_connector *connector) +{ + struct intel_connector *intel_connector = to_intel_connector(connector); + struct drm_dp_mst_port *port = intel_connector->port; + + int ret; + + ret = intel_connector_register(connector); + if (ret) + return ret; + + return drm_dp_mst_connector_late_register(connector, port); +} + +static void +intel_dp_mst_connector_early_unregister(struct drm_connector *connector) +{ + struct intel_connector *intel_connector = to_intel_connector(connector); + struct drm_dp_mst_port *port = intel_connector->port; + + drm_dp_mst_connector_early_unregister(connector, port); + intel_connector_unregister(connector); +} + static const struct drm_connector_funcs intel_dp_mst_connector_funcs = { .detect = intel_dp_mst_detect, .fill_modes = drm_helper_probe_single_connector_modes, .atomic_get_property = intel_digital_connector_atomic_get_property, .atomic_set_property = intel_digital_connector_atomic_set_property, - .late_register = intel_connector_register, - .early_unregister = intel_connector_unregister, + .late_register = intel_dp_mst_connector_late_register, + .early_unregister = intel_dp_mst_connector_early_unregister, .destroy = intel_connector_destroy, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_duplicate_state = intel_digital_connector_duplicate_state, -- 2.22.0 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 2/7] drm/dp_mst: Register AUX devices for MST ports
From: Ville Syrjälä All available downstream ports - physical and logical - are exposed for each MST device. They are listed in /dev/, following the same naming scheme as SST devices by appending an incremental ID. Although all downstream ports are exposed, only some will work as expected. Consider the following topology: +-+ | ASIC | +-+ Conn-0| | +v+ +| MST HUB |+ |+-+| | | |Port-1 Port-2| +-v-+ +-v-+ | MST | | SST | | Display | | Display | +---+ +---+ |Port-1 x MST Path | MST Device --+-- sst:0 | MST Hub mst:0-1 | MST Display mst:0-1-1 | MST Display's disconnected DP out mst:0-1-8 | MST Display's internal sink mst:0-2 | SST Display On certain MST displays, the upstream physical port will ACK DPCD reads. However, reads on the local logical port to the internal sink will *NAK*. i.e. reading mst:0-1 ACKs, but mst:0-1-8 NAKs. There may also be duplicates. Some displays will return the same GUID when reading DPCD from both mst:0-1 and mst:0-1-8. There are some device-dependent behavior as well. The MST hub used during testing will actually *ACK* read requests on a disconnected physical port, whereas the MST displays will NAK. In light of these discrepancies, it's simpler to expose all downstream ports - both physical and logical - and let the user decide what to use. Cc: Lyude Paul Signed-off-by: Ville Syrjälä Signed-off-by: Leo Li --- drivers/gpu/drm/drm_dp_aux_dev.c | 14 - drivers/gpu/drm/drm_dp_mst_topology.c | 103 +- include/drm/drm_dp_helper.h | 4 ++ include/drm/drm_dp_mst_helper.h | 6 ++ 4 files changed, 112 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_aux_dev.c b/drivers/gpu/drm/drm_dp_aux_dev.c index 6d84611..01c02b9 100644 --- a/drivers/gpu/drm/drm_dp_aux_dev.c +++ b/drivers/gpu/drm/drm_dp_aux_dev.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -114,6 +115,7 @@ static ssize_t name_show(struct device *dev, return res; } + static DEVICE_ATTR_RO(name); static struct attribute *drm_dp_aux_attrs[] = { @@ -160,7 +162,11 @@ static ssize_t auxdev_read_iter(struct kiocb *iocb, struct iov_iter *to) break; } - res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo); + if (aux_dev->aux->is_remote) + res = drm_dp_mst_dpcd_read(aux_dev->aux, pos, buf, todo); + else + res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo); + if (res <= 0) break; @@ -207,7 +213,11 @@ static ssize_t auxdev_write_iter(struct kiocb *iocb, struct iov_iter *from) break; } - res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo); + if (aux_dev->aux->is_remote) + res = drm_dp_mst_dpcd_write(aux_dev->aux, pos, buf, todo); + else + res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo); + if (res <= 0) break; diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 2ab16c9..54da68e 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -35,6 +35,8 @@ #include #include +#include "drm_crtc_helper_internal.h" + /** * DOC: dp mst helper * @@ -52,6 +54,9 @@ static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr, int id, struct drm_dp_payload *payload); +static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port, +int offset, int size, u8 *bytes); static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, int offset, int size, u8 *bytes); @@ -941,6 +946,8 @@ static void drm_dp_destroy_port(struct kref *kref) struct drm_dp_mst_topology_mgr *mgr = port->mgr; if (!port->input) { + drm_dp_aux_unregister_devnode(&port->aux); + port->vcpi.num_slots = 0; kfree(port->cached_edid); @@ -1095,6 +1102,46 @@ static bool drm_dp_port_setup_pdt(struct drm_dp_mst_port *port) return send_link; } +/** + * drm_dp_mst_dpcd_read() - read a series of bytes from the DPCD via sideband + * @aux: Fake sideband AUX CH + * @offset: address of the
[PATCH 1/7] drm/dp: Use non-cyclic idr
From: Leo Li In preparation for adding aux devices for DP MST, make the IDR non-cyclic. That way, hotplug cycling MST devices won't needlessly increment the minor version index. Signed-off-by: Leo Li Reviewed-by: Lyude Paul Reviewed-by: Ville Syrjälä --- drivers/gpu/drm/drm_dp_aux_dev.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_aux_dev.c b/drivers/gpu/drm/drm_dp_aux_dev.c index 0e4f25d..6d84611 100644 --- a/drivers/gpu/drm/drm_dp_aux_dev.c +++ b/drivers/gpu/drm/drm_dp_aux_dev.c @@ -80,8 +80,7 @@ static struct drm_dp_aux_dev *alloc_drm_dp_aux_dev(struct drm_dp_aux *aux) kref_init(&aux_dev->refcount); mutex_lock(&aux_idr_mutex); - index = idr_alloc_cyclic(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, -GFP_KERNEL); + index = idr_alloc(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, GFP_KERNEL); mutex_unlock(&aux_idr_mutex); if (index < 0) { kfree(aux_dev); -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 6/7] drm/bridge/analogix-anx78xx: Use connector kdev as aux device parent
From: Leo Li Set the connector's kernel device as the parent for the aux kernel device. This allows udev rules to access connector attributes when creating symlinks to aux devices. To do so, the connector needs to be registered beforehand. Therefore, shift aux registration to be after connector registration. Cc: Enric Balletbo i Serra Cc: Nicolas Boichat Signed-off-by: Leo Li --- drivers/gpu/drm/bridge/analogix-anx78xx.c | 22 +++--- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/bridge/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix-anx78xx.c index f8433c9..9fc8b4c 100644 --- a/drivers/gpu/drm/bridge/analogix-anx78xx.c +++ b/drivers/gpu/drm/bridge/analogix-anx78xx.c @@ -1018,17 +1018,6 @@ static int anx78xx_bridge_attach(struct drm_bridge *bridge) return -ENODEV; } - /* Register aux channel */ - anx78xx->aux.name = "DP-AUX"; - anx78xx->aux.dev = &anx78xx->client->dev; - anx78xx->aux.transfer = anx78xx_aux_transfer; - - err = drm_dp_aux_register(&anx78xx->aux); - if (err < 0) { - DRM_ERROR("Failed to register aux channel: %d\n", err); - return err; - } - err = drm_connector_init(bridge->dev, &anx78xx->connector, &anx78xx_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort); @@ -1048,6 +1037,17 @@ static int anx78xx_bridge_attach(struct drm_bridge *bridge) anx78xx->connector.polled = DRM_CONNECTOR_POLL_HPD; + /* Register aux channel */ + anx78xx->aux.name = "DP-AUX"; + anx78xx->aux.dev = &anx78xx->connector.kdev; + anx78xx->aux.transfer = anx78xx_aux_transfer; + + err = drm_dp_aux_register(&anx78xx->aux); + if (err < 0) { + DRM_ERROR("Failed to register aux channel: %d\n", err); + return err; + } + err = drm_connector_attach_encoder(&anx78xx->connector, bridge->encoder); if (err) { -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 3/7] drm/dp-mst: Use connector kdev as aux device parent
From: Leo Li Placing the MST aux device as a child of the connector gives udev the ability to access the connector device's attributes. This will come in handy when writing udev rules to create more descriptive symlinks to the MST aux devices. Cc: Ville Syrjälä Cc: Lyude Paul Signed-off-by: Leo Li --- drivers/gpu/drm/drm_dp_mst_topology.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 54da68e..cd2f3c4 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1269,6 +1269,9 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb, } (*mstb->mgr->cbs->register_connector)(port->connector); + if (port->connector->registration_state == DRM_CONNECTOR_REGISTERED) + port->aux.dev = port->connector->kdev; + drm_dp_aux_register_devnode(&port->aux); } -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 4/7] drm/sysfs: Add mstpath attribute to connector devices
From: Leo Li This can be used to create more descriptive symlinks for MST aux devices. Consider the following udev rule: SUBSYSTEM=="drm_dp_aux_dev", SUBSYSTEMS=="drm", ATTRS{mstpath}=="?*", SYMLINK+="drm_dp_aux/by-path/$attr{mstpath}" The following symlinks will be created (depending on your MST topology): $ ls /dev/drm_dp_aux/by-path/ card0-mst:0-1 card0-mst:0-1-1 card0-mst:0-1-8 card0-mst:0-8 Cc: Ville Syrjälä Cc: Lyude Paul Signed-off-by: Leo Li --- drivers/gpu/drm/drm_sysfs.c | 23 +++ 1 file changed, 23 insertions(+) diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index ecb7b33..e000e0c 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -229,16 +229,39 @@ static ssize_t modes_show(struct device *device, return written; } +static ssize_t mstpath_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = to_drm_connector(device); + ssize_t ret = 0; + char *path; + + mutex_lock(&connector->dev->mode_config.mutex); + if (!connector->path_blob_ptr) + goto unlock; + + path = connector->path_blob_ptr->data; + ret = snprintf(buf, PAGE_SIZE, "card%d-%s\n", + connector->dev->primary->index, path); + +unlock: + mutex_unlock(&connector->dev->mode_config.mutex); + return ret; +} + static DEVICE_ATTR_RW(status); static DEVICE_ATTR_RO(enabled); static DEVICE_ATTR_RO(dpms); static DEVICE_ATTR_RO(modes); +static DEVICE_ATTR_RO(mstpath); static struct attribute *connector_dev_attrs[] = { &dev_attr_status.attr, &dev_attr_enabled.attr, &dev_attr_dpms.attr, &dev_attr_modes.attr, + &dev_attr_mstpath.attr, NULL }; -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 5/7] drm/amd/display: Use connector kdev as aux device parent
From: Leo Li Set the connector's kernel device as the parent for the aux kernel device. This allows udev rules to access connector attributes when creating symlinks to aux devices. For example, the following udev rule: SUBSYSTEM=="drm_dp_aux_dev", SUBSYSTEMS=="drm", ATTRS{edid}=="*", SYMLINK+="drm_dp_aux/by-name/$id" Will create the following symlinks using the connector's name: $ ls /dev/drm_dp_aux/by-name/ card0-DP-1 card0-DP-2 card0-DP-3 Cc: Ville Syrjälä Cc: Lyude Paul Cc: Nicholas Kazlauskas Cc: Harry Wentland Cc: Jerry (Fangzhi) Zuo Signed-off-by: Leo Li --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index a6f44a4..083fb97 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -385,7 +385,7 @@ void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm, struct amdgpu_dm_connector *aconnector) { aconnector->dm_dp_aux.aux.name = "dmdc"; - aconnector->dm_dp_aux.aux.dev = dm->adev->dev; + aconnector->dm_dp_aux.aux.dev = aconnector->base.kdev; aconnector->dm_dp_aux.aux.transfer = dm_dp_aux_transfer; aconnector->dm_dp_aux.ddc_service = aconnector->dc_link->ddc; -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 7/7] drm/nouveau: Use connector kdev as aux device parent
From: Leo Li Set the connector's kernel device as the parent for the aux kernel device. This allows udev rules to access connector attributes when creating symlinks to aux devices. Cc: Ben Skeggs Cc: Lyude Paul Signed-off-by: Leo Li --- drivers/gpu/drm/nouveau/nouveau_connector.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 3f463c9..738782a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -1345,7 +1345,7 @@ nouveau_connector_create(struct drm_device *dev, break; case DRM_MODE_CONNECTOR_DisplayPort: case DRM_MODE_CONNECTOR_eDP: - nv_connector->aux.dev = dev->dev; + nv_connector->aux.dev = connector->kdev; nv_connector->aux.transfer = nouveau_connector_aux_xfer; snprintf(aux_name, sizeof(aux_name), "sor-%04x-%04x", dcbe->hasht, dcbe->hashm); -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 0/7] Add DP MST AUX devices
From: Leo Li This series adds support for MST AUX devices. A more descriptive 'mstpath' attribute is also added to MST connector devices. In addition, the DP aux device is made to be a child of the corresponding connector's device where possible (*). This allows udev rules to provide descriptive symlinks to the AUX devices. (*) This can only be done on drivers that register their connector and aux devices via drm_connector_register() and drm_dp_aux_register() respectively. The connector also needs to be registered before the aux device. Leo Li (6): drm/dp: Use non-cyclic idr drm/dp-mst: Use connector kdev as aux device parent drm/sysfs: Add mstpath attribute to connector devices drm/amd/display: Use connector kdev as aux device parent drm/bridge/analogix-anx78xx: Use connector kdev as aux device parent drm/nouveau: Use connector kdev as aux device parent Ville Syrjälä (1): drm/dp_mst: Register AUX devices for MST ports .../amd/display/amdgpu_dm/amdgpu_dm_mst_types.c| 2 +- drivers/gpu/drm/bridge/analogix-anx78xx.c | 22 ++--- drivers/gpu/drm/drm_dp_aux_dev.c | 17 +++- drivers/gpu/drm/drm_dp_mst_topology.c | 106 ++--- drivers/gpu/drm/drm_sysfs.c| 23 + drivers/gpu/drm/nouveau/nouveau_connector.c| 2 +- include/drm/drm_dp_helper.h| 4 + include/drm/drm_dp_mst_helper.h| 6 ++ 8 files changed, 152 insertions(+), 30 deletions(-) -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 3/3] drm/dp_mst: Register AUX devices for MST ports
From: Ville Syrjälä All available downstream ports - physical and logical - are exposed for each MST device. They are listed in /dev/, following the same naming scheme as SST devices by appending an incremental ID. Additionally, a 'path' attribute is attached to the device. This is to allow udev to provide more identifiable symlinks to these devices, similar to /dev/disks. For example, adding the following udev rule: SUBSYSTEMS=="drm_dp_aux_dev", ATTR{path}=="?*", SYMLINK+="drm_dp_aux/by-path/$attr{path}" With a MST topology like so: +-+ | ASIC | +-+ Conn-0| | +v+ +| MST HUB |+ |+-+| | | |Port-1 Port-2| +-v-+ +-v-+ | MST | | SST | | Display | | Display | +---+ +---+ |Port-1 x Will create the following symlinks in 'drm_dp_aux/by-path/': AUX Device Name | MST Device --+-- card0_sst:0 (*) | MST Hub card0_mst:0-1 | MST Display card0_mst:0-1-1 | MST Display's disconnected DP out card0_mst:0-1-8 | MST Display's internal sink card0_mst:0-2 | SST Display (*) Note that the first digit is suppose to mean 'Connector ID'. However, that's only true for 'mst:' paths. A TODO item has been left to address this. Although all downstream ports are exposed, only some will work. On certain MST displays, the upstream physical port will ACK DPCD reads. However, reads on the local logical port to the internal sink will *NAK*. i.e. reading mst:0-1 ACKs, but mst:0-1-8 NAKs. There may also be duplicates. Some displays will return the same GUID when reading DPCD from both mst:0-1 and mst:0-1-8. There are some device-dependent behavior as well. The MST hub used during testing will actually *ACK* read requests on a disconnected physical port, whereas the MST displays will NAK. In light of these discrepancies, it's simpler to expose all downstream ports - both physical and logical - and let the user decide what to use. Signed-off-by: Ville Syrjälä Signed-off-by: Leo Li --- drivers/gpu/drm/drm_dp_aux_dev.c | 53 - drivers/gpu/drm/drm_dp_mst_topology.c | 103 +- include/drm/drm_dp_helper.h | 4 ++ include/drm/drm_dp_mst_helper.h | 6 ++ 4 files changed, 151 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_aux_dev.c b/drivers/gpu/drm/drm_dp_aux_dev.c index 6d84611..4218bbf 100644 --- a/drivers/gpu/drm/drm_dp_aux_dev.c +++ b/drivers/gpu/drm/drm_dp_aux_dev.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -114,10 +115,50 @@ static ssize_t name_show(struct device *dev, return res; } + +static int is_drm_primary_device(struct device *dev, void *data) +{ + return strstr(dev_name(dev), "card") != NULL; +} + +static ssize_t path_show(struct device *dev, +struct device_attribute *attr, char *buf) +{ + struct drm_dp_aux_dev *aux_dev = + drm_dp_aux_dev_get_by_minor(MINOR(dev->devt)); + struct drm_dp_aux *aux = aux_dev->aux; + struct drm_dp_mst_port *port; + struct device *card; + char temp[64]; + ssize_t res; + + card = device_find_child(dev->parent, NULL, +is_drm_primary_device); + + if (!aux->is_remote) { + /* +* TODO: AUX index does not correlate with connector ID. See if +* connector ID can be used instead. +*/ + res = sprintf(buf, "%s_%s:%d\n", + dev_name(card), "sst", aux_dev->index); + return res; + } + + port = container_of(aux, struct drm_dp_mst_port, aux); + drm_dp_build_mst_prop_path(port->parent, port->port_num, + temp, sizeof(temp)); + res = sprintf(buf, "%s_%s\n", dev_name(card), temp); + + return res; +} + static DEVICE_ATTR_RO(name); +static DEVICE_ATTR_RO(path); static struct attribute *drm_dp_aux_attrs[] = { &dev_attr_name.attr, + &dev_attr_path.attr, NULL, }; ATTRIBUTE_GROUPS(drm_dp_aux); @@ -160,7 +201,11 @@ static ssize_t auxdev_read_iter(struct kiocb *iocb, struct iov_iter *to) break; } - res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo); + if (aux_dev->aux->is_remote) + res = drm_dp_mst_dpcd_read(aux_dev->aux, pos, buf, todo); + else + res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo); + if (res <= 0) break; @@ -207,7 +252,11 @@ static ssize_t auxdev_write_iter(str
[PATCH 1/3] drm/dp: Use non-cyclic idr
From: Leo Li In preparation for adding aux devices for DP MST, make the IDR non-cyclic. That way, hotplug cycling MST devices won't needlessly increment the minor version index. Signed-off-by: Leo Li --- drivers/gpu/drm/drm_dp_aux_dev.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_aux_dev.c b/drivers/gpu/drm/drm_dp_aux_dev.c index 0e4f25d..6d84611 100644 --- a/drivers/gpu/drm/drm_dp_aux_dev.c +++ b/drivers/gpu/drm/drm_dp_aux_dev.c @@ -80,8 +80,7 @@ static struct drm_dp_aux_dev *alloc_drm_dp_aux_dev(struct drm_dp_aux *aux) kref_init(&aux_dev->refcount); mutex_lock(&aux_idr_mutex); - index = idr_alloc_cyclic(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, -GFP_KERNEL); + index = idr_alloc(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, GFP_KERNEL); mutex_unlock(&aux_idr_mutex); if (index < 0) { kfree(aux_dev); -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 2/3] drm/dp_mst: Expose build_mst_prop_path()
From: Leo Li To give identifiable attributes to MST DP aux devices, we can use the MST relative address. Expose this function for later use. Signed-off-by: Leo Li --- drivers/gpu/drm/drm_dp_mst_topology.c | 4 ++-- include/drm/drm_dp_mst_helper.h | 4 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 2ab16c9..86ff8e2 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1120,7 +1120,7 @@ static void drm_dp_check_mstb_guid(struct drm_dp_mst_branch *mstb, u8 *guid) } } -static void build_mst_prop_path(const struct drm_dp_mst_branch *mstb, +void drm_dp_build_mst_prop_path(const struct drm_dp_mst_branch *mstb, int pnum, char *proppath, size_t proppath_size) @@ -1202,7 +1202,7 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb, if (created && !port->input) { char proppath[255]; - build_mst_prop_path(mstb, port->port_num, proppath, sizeof(proppath)); + drm_dp_build_mst_prop_path(mstb, port->port_num, proppath, sizeof(proppath)); port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr, port, proppath); if (!port->connector) { /* remove it from the port list */ diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 371cc28..81c8d79 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -602,6 +602,10 @@ void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, int pbn); +void drm_dp_build_mst_prop_path(const struct drm_dp_mst_branch *mstb, + int pnum, + char *proppath, + size_t proppath_size); int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr); -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 1/2] drm/dp_aux: Use non-cyclic idr, add suffix option for aux device
From: Leo Li In preparation for adding aux devices for DP MST: 1. A non-cyclic idr is used for the device minor version. That way, hotplug cycling MST devices won't needlessly increment the minor version index. 2. A suffix option is added to the aux device file name. It can be used to identify the corresponding MST device. Signed-off-by: Leo Li --- drivers/gpu/drm/drm_crtc_helper_internal.h | 5 +++-- drivers/gpu/drm/drm_dp_aux_dev.c | 8 drivers/gpu/drm/drm_dp_helper.c| 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc_helper_internal.h b/drivers/gpu/drm/drm_crtc_helper_internal.h index b5ac158..9f0907b 100644 --- a/drivers/gpu/drm/drm_crtc_helper_internal.h +++ b/drivers/gpu/drm/drm_crtc_helper_internal.h @@ -46,7 +46,7 @@ static inline int drm_fb_helper_modinit(void) #ifdef CONFIG_DRM_DP_AUX_CHARDEV int drm_dp_aux_dev_init(void); void drm_dp_aux_dev_exit(void); -int drm_dp_aux_register_devnode(struct drm_dp_aux *aux); +int drm_dp_aux_register_devnode(struct drm_dp_aux *aux, const char *suffix); void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux); #else static inline int drm_dp_aux_dev_init(void) @@ -58,7 +58,8 @@ static inline void drm_dp_aux_dev_exit(void) { } -static inline int drm_dp_aux_register_devnode(struct drm_dp_aux *aux) +static inline int drm_dp_aux_register_devnode(struct drm_dp_aux *aux, + const char *suffix) { return 0; } diff --git a/drivers/gpu/drm/drm_dp_aux_dev.c b/drivers/gpu/drm/drm_dp_aux_dev.c index 0e4f25d..2310a67 100644 --- a/drivers/gpu/drm/drm_dp_aux_dev.c +++ b/drivers/gpu/drm/drm_dp_aux_dev.c @@ -80,8 +80,7 @@ static struct drm_dp_aux_dev *alloc_drm_dp_aux_dev(struct drm_dp_aux *aux) kref_init(&aux_dev->refcount); mutex_lock(&aux_idr_mutex); - index = idr_alloc_cyclic(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, -GFP_KERNEL); + index = idr_alloc(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, GFP_KERNEL); mutex_unlock(&aux_idr_mutex); if (index < 0) { kfree(aux_dev); @@ -290,7 +289,7 @@ void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux) kref_put(&aux_dev->refcount, release_drm_dp_aux_dev); } -int drm_dp_aux_register_devnode(struct drm_dp_aux *aux) +int drm_dp_aux_register_devnode(struct drm_dp_aux *aux, const char *suffix) { struct drm_dp_aux_dev *aux_dev; int res; @@ -301,7 +300,8 @@ int drm_dp_aux_register_devnode(struct drm_dp_aux *aux) aux_dev->dev = device_create(drm_dp_aux_dev_class, aux->dev, MKDEV(drm_dev_major, aux_dev->index), NULL, -"drm_dp_aux%d", aux_dev->index); +"drm_dp_aux%d%s", aux_dev->index, +suffix ? suffix : ""); if (IS_ERR(aux_dev->dev)) { res = PTR_ERR(aux_dev->dev); aux_dev->dev = NULL; diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index e2266ac..13f1005 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -1143,7 +1143,7 @@ int drm_dp_aux_register(struct drm_dp_aux *aux) strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev), sizeof(aux->ddc.name)); - ret = drm_dp_aux_register_devnode(aux); + ret = drm_dp_aux_register_devnode(aux, NULL); if (ret) return ret; -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 2/2] drm/dp_mst: Register aux-dev nodes for MST ports
From: Ville Syrjälä Expose AUX devices for MST ports, similar to how they are exposed for SST. The registered device will have it's MST port path appended in order to identify it. i.e. /dev/drm_dp_aux4_mst:0-2-1 So for a MST topology like so: +-+ | ASIC | +-+ Conn-0| | +v+ +| MST HUB |+ |+-+| | | |Port-1 Port-2| +-v-+ +-v-+ | MST | | SST | | Display | | Display | +---+ +---+ |Port-1 x The list of AUX device names will look like: AUX Device Name | MST Device --+-- drm_dp_aux0 | MST Hub drm_dp_aux1_mst:0-1-1 | MST Display's disconnected DP out drm_dp_aux2_mst:0-1 | MST Display drm_dp_aux3_mst:0-2 | SST Display Note that aux devices are only created for Physical Ports. Logical Ports are left out, since they are internally connected within the MST device (not connected to a DP RX or TX). Leo Li: * Add missing drm_crtc_helper_internal.h include * Fix hard-coded offset and size in drm_dp_send_dpcd_read() * Only create aux devices for physical ports. Signed-off-by: Ville Syrjälä Signed-off-by: Leo Li --- drivers/gpu/drm/drm_dp_aux_dev.c | 13 +++- drivers/gpu/drm/drm_dp_mst_topology.c | 109 ++ include/drm/drm_dp_helper.h | 4 ++ include/drm/drm_dp_mst_helper.h | 6 ++ 4 files changed, 117 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_aux_dev.c b/drivers/gpu/drm/drm_dp_aux_dev.c index 2310a67..f1241d1 100644 --- a/drivers/gpu/drm/drm_dp_aux_dev.c +++ b/drivers/gpu/drm/drm_dp_aux_dev.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -160,7 +161,11 @@ static ssize_t auxdev_read_iter(struct kiocb *iocb, struct iov_iter *to) break; } - res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo); + if (aux_dev->aux->is_remote) + res = drm_dp_mst_dpcd_read(aux_dev->aux, pos, buf, todo); + else + res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo); + if (res <= 0) break; @@ -207,7 +212,11 @@ static ssize_t auxdev_write_iter(struct kiocb *iocb, struct iov_iter *from) break; } - res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo); + if (aux_dev->aux->is_remote) + res = drm_dp_mst_dpcd_write(aux_dev->aux, pos, buf, todo); + else + res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo); + if (res <= 0) break; diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 2ab16c9..d5282db 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -35,6 +35,8 @@ #include #include +#include "drm_crtc_helper_internal.h" + /** * DOC: dp mst helper * @@ -52,6 +54,9 @@ static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr, int id, struct drm_dp_payload *payload); +static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port, +int offset, int size, u8 *bytes); static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, int offset, int size, u8 *bytes); @@ -941,6 +946,8 @@ static void drm_dp_destroy_port(struct kref *kref) struct drm_dp_mst_topology_mgr *mgr = port->mgr; if (!port->input) { + drm_dp_aux_unregister_devnode(&port->aux); + port->vcpi.num_slots = 0; kfree(port->cached_edid); @@ -1095,6 +1102,46 @@ static bool drm_dp_port_setup_pdt(struct drm_dp_mst_port *port) return send_link; } +/** + * drm_dp_mst_dpcd_read() - read a series of bytes from the DPCD via sideband + * @aux: Fake sideband AUX CH + * @offset: address of the (first) register to read + * @buffer: buffer to store the register values + * @size: number of bytes in @buffer + * + * Performs the same functionality for remote devices via + * sideband messaging as drm_dp_dpcd_read() does for local + * devices via actual AUX CH. + */ +ssize_t drm_dp_mst_dpcd_read(struct drm_dp_aux *aux, +unsigned int offset, void *buffer, size_t size) +{ + struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port, aux); + +
[RFC 0/2] Add AUX device entries for DP MST devices
From: Leo Li Hi all, This is a follup to this change made by Ville to add MST aux nodes: https://github.com/vsyrjala/linux/commit/cac63f799ee2f5fbbe4f0a375383f13e03d640a5 Patch 2/2 describes what I added on top. Sending as an RFC since there are some items I'm not certain on: 1) Only expose aux devices for physical ports. FWICT, only DPTX and DPRX can handle AUX transactions, leaving logical ports out. 2) Naming of exposed AUX devices. I'm not sure if the scheme implemented here is the best approach. Let me know your thoughts, Leo Leo Li (1): drm/dp_mst: Use non-cyclic idr, add suffix option for aux device Ville Syrjälä (1): drm/dp_mst: Register aux-dev nodes for MST ports drivers/gpu/drm/drm_crtc_helper_internal.h | 5 +- drivers/gpu/drm/drm_dp_aux_dev.c | 21 -- drivers/gpu/drm/drm_dp_helper.c| 2 +- drivers/gpu/drm/drm_dp_mst_topology.c | 109 + include/drm/drm_dp_helper.h| 4 ++ include/drm/drm_dp_mst_helper.h| 6 ++ 6 files changed, 125 insertions(+), 22 deletions(-) -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH] drm/amd/display: Fix 64-bit division for 32-bit builds
From: Ken Chalmers [Why] 32-bit builds break when doing 64-bit division directly. [How] Use the div_u64() function instead to perform the division. Fixes: https://lists.freedesktop.org/archives/dri-devel/2018-December/201008.html Signed-off-by: Ken Chalmers Reviewed-by: Leo Li --- drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.c b/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.c index 5c629ae..8b5ce55 100644 --- a/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.c +++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.c @@ -94,7 +94,7 @@ static void program_pix_dur(struct timing_generator *tg, uint32_t pix_clk_100hz) if (pix_clk_100hz == 0) return; - pix_dur = 100ull / pix_clk_100hz; + pix_dur = div_u64(100ull, pix_clk_100hz); set_reg_field_value( value, -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2] drm: Get ref on CRTC commit object when waiting for flip_done
From: Leo Li This fixes a general protection fault, caused by accessing the contents of a flip_done completion object that has already been freed. It occurs due to the preemption of a non-blocking commit worker thread W by another commit thread X. X continues to clear its atomic state at the end, destroying the CRTC commit object that W still needs. Switching back to W and accessing the commit objects then leads to bad results. Worker W becomes preemptable when waiting for flip_done to complete. At this point, a frequently occurring commit thread X can take over. Here's an example where W is a worker thread that flips on both CRTCs, and X does a legacy cursor update on both CRTCs: ... 1. W does flip work 2. W runs commit_hw_done() 3. W waits for flip_done on CRTC 1 4. > flip_done for CRTC 1 completes 5. W finishes waiting for CRTC 1 6. W waits for flip_done on CRTC 2 7. > Preempted by X 8. > flip_done for CRTC 2 completes 9. X atomic_check: hw_done and flip_done are complete on all CRTCs 10. X updates cursor on both CRTCs 11. X destroys atomic state 12. X done 13. > Switch back to W 14. W waits for flip_done on CRTC 2 15. W raises general protection fault The error looks like so: general protection fault: [#1] PREEMPT SMP PTI **snip** Call Trace: lock_acquire+0xa2/0x1b0 _raw_spin_lock_irq+0x39/0x70 wait_for_completion_timeout+0x31/0x130 drm_atomic_helper_wait_for_flip_done+0x64/0x90 [drm_kms_helper] amdgpu_dm_atomic_commit_tail+0xcae/0xdd0 [amdgpu] commit_tail+0x3d/0x70 [drm_kms_helper] process_one_work+0x212/0x650 worker_thread+0x49/0x420 kthread+0xfb/0x130 ret_from_fork+0x3a/0x50 Modules linked in: x86_pkg_temp_thermal amdgpu(O) chash(O) gpu_sched(O) drm_kms_helper(O) syscopyarea sysfillrect sysimgblt fb_sys_fops ttm(O) drm(O) Note that i915 has this issue masked, since hw_done is signaled after waiting for flip_done. Doing so will block the cursor update from happening until hw_done is signaled, preventing the cursor commit from destroying the state. v2: The reference on the commit object needs to be obtained before hw_done() is signaled, since that's the point where another commit is allowed to modify the state. Assuming that the new_crtc_state->commit object still exists within flip_done() is incorrect. Fix by getting a reference in setup_commit(), and releasing it during default_clear(). Signed-off-by: Leo Li --- Sending again, this time to the correct mailing list :) Leo drivers/gpu/drm/drm_atomic.c| 5 + drivers/gpu/drm/drm_atomic_helper.c | 12 include/drm/drm_atomic.h| 11 +++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 3eb061e..12ae917 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -174,6 +174,11 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) state->crtcs[i].state = NULL; state->crtcs[i].old_state = NULL; state->crtcs[i].new_state = NULL; + + if (state->crtcs[i].commit) { + drm_crtc_commit_put(state->crtcs[i].commit); + state->crtcs[i].commit = NULL; + } } for (i = 0; i < config->num_total_plane; i++) { diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 80be74d..1bb4c31 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1408,15 +1408,16 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks); void drm_atomic_helper_wait_for_flip_done(struct drm_device *dev, struct drm_atomic_state *old_state) { - struct drm_crtc_state *new_crtc_state; struct drm_crtc *crtc; int i; - for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { - struct drm_crtc_commit *commit = new_crtc_state->commit; + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct drm_crtc_commit *commit = old_state->crtcs[i].commit; int ret; - if (!commit) + crtc = old_state->crtcs[i].ptr; + + if (!crtc || !commit) continue; ret = wait_for_completion_timeout(&commit->flip_done, 10 * HZ); @@ -1934,6 +1935,9 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, drm_crtc_commit_get(commit); commit->abort_completion = true; + + state->crtcs[i].commit = commit; + drm_crtc_commit_get(commit); } for_each_oldnew_connector_in_state(state, conn, old_conn_state, new_conn_state, i) { diff --git a/include/drm/drm_atomic.h b/include/drm/drm_a
[PATCH] drm: Get ref on CRTC commit object when waiting for flip_done
From: Leo Li This fixes a general protection fault, caused by accessing the contents of a flip_done completion object that has already been freed. It occurs due to the preemption of a non-blocking commit worker thread W by another commit thread X. X continues to clear its atomic state at the end, destroying the CRTC commit object that W still needs. Switching back to W and accessing the commit objects then leads to bad results. Worker W becomes preemptable when waiting for flip_done to complete. At this point, a frequently occurring commit thread X can take over. Here's an example where W is a worker thread that flips on both CRTCs, and X does a legacy cursor update on both CRTCs: ... 1. W does flip work 2. W runs commit_hw_done() 3. W waits for flip_done on CRTC 1 4. > flip_done for CRTC 1 completes 5. W finishes waiting for CRTC 1 6. W waits for flip_done on CRTC 2 7. > Preempted by X 8. > flip_done for CRTC 2 completes 9. X atomic_check: hw_done and flip_done are complete on all CRTCs 10. X updates cursor on both CRTCs 11. X destroys atomic state 12. X done 13. > Switch back to W 14. W waits for flip_done on CRTC 2 15. W raises general protection fault The error looks like so: general protection fault: [#1] PREEMPT SMP PTI **snip** Call Trace: lock_acquire+0xa2/0x1b0 _raw_spin_lock_irq+0x39/0x70 wait_for_completion_timeout+0x31/0x130 drm_atomic_helper_wait_for_flip_done+0x64/0x90 [drm_kms_helper] amdgpu_dm_atomic_commit_tail+0xcae/0xdd0 [amdgpu] commit_tail+0x3d/0x70 [drm_kms_helper] process_one_work+0x212/0x650 worker_thread+0x49/0x420 kthread+0xfb/0x130 ret_from_fork+0x3a/0x50 Modules linked in: x86_pkg_temp_thermal amdgpu(O) chash(O) gpu_sched(O) drm_kms_helper(O) syscopyarea sysfillrect sysimgblt fb_sys_fops ttm(O) drm(O) Note that i915 has this issue masked, since hw_done is signaled after waiting for flip_done. Doing so will block the cursor update from happening until hw_done is signaled, preventing the cursor commit from destroying the state. Signed-off-by: Leo Li --- drivers/gpu/drm/drm_atomic_helper.c | 30 ++ 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 80be74d..efdf043 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1410,20 +1410,42 @@ void drm_atomic_helper_wait_for_flip_done(struct drm_device *dev, { struct drm_crtc_state *new_crtc_state; struct drm_crtc *crtc; + struct drm_crtc_commit **commits; int i; + int num_crtc = dev->mode_config.num_crtc; + commits = kcalloc(num_crtc, sizeof(*commits), GFP_KERNEL); + + /* +* Because we open ourselves to preemption by calling +* wait_for_completion_timeout(), we need to get our own references to +* the commit objects. This is so that a preempting commit won't free +* them. +*/ for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { - struct drm_crtc_commit *commit = new_crtc_state->commit; + commits[i] = new_crtc_state->commit; + if (commits[i]) + drm_crtc_commit_get(commits[i]); + } + + for (i = 0; i < num_crtc; i++) { int ret; - if (!commit) + if (!commits[i]) continue; - ret = wait_for_completion_timeout(&commit->flip_done, 10 * HZ); + ret = wait_for_completion_timeout(&commits[i]->flip_done, + 10 * HZ); if (ret == 0) DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n", - crtc->base.id, crtc->name); + commits[i]->crtc->base.id, + commits[i]->crtc->name); } + + for (i = 0; i < num_crtc; i++) + if (commits[i]) + drm_crtc_commit_put(commits[i]); + kfree(commits); } EXPORT_SYMBOL(drm_atomic_helper_wait_for_flip_done); -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH] drm/amd/display: Remove erroneous if statement in gamma set
From: "Leo (Sunpeng) Li" The lines were removed as part of the original change. They may have been missed during merge. This is a fixup to: committ 265083076187e619aa9176aeb05ad630013429b4 Author: Leo (Sunpeng) Li Date: Fri Feb 2 10:18:56 2018 -0500 drm/amd/display: Hookup color management functions --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index b7265f6..0d4a133 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -2206,11 +2206,9 @@ static int fill_plane_attributes(struct amdgpu_device *adev, dc_plane_state->in_transfer_func = input_tf; - /* In case of gamma set, update gamma value */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0) || \ defined(OS_NAME_RHEL_7_3) || \ defined(OS_NAME_RHEL_7_4_5) - if (crtc_state->gamma_lut) /* * Always set input transfer function, since plane state is refreshed * every time. -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH] drm/atomic: Fix memleak on ERESTARTSYS during non-blocking commits
From: "Leo (Sunpeng) Li" During a non-blocking commit, it is possible to return before the commit_tail work is queued (-ERESTARTSYS, for example). Since a reference on the crtc commit object is obtained for the pending vblank event when preparing the commit, the above situation will leave us with an extra reference. Therefore, if the commit_tail worker has not consumed the event at the end of a commit, release it's reference. Signed-off-by: Leo (Sunpeng) Li --- drivers/gpu/drm/drm_atomic_helper.c | 9 + 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index ab40321..4253f57 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -3421,6 +3421,15 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state); void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state) { if (state->commit) { + /* +* In the event that a non-blocking commit returns +* -ERESTARTSYS before the commit_tail work is queued, we will +* have an extra reference to the commit object. Release it, if +* the event has not been consumed by the worker. +*/ + if (state->event) + drm_crtc_commit_put(state->commit); + kfree(state->commit->event); state->commit->event = NULL; drm_crtc_commit_put(state->commit); -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 1/6] drm/amd/display: Use DRM new-style object iterators.
From: "Leo (Sunpeng) Li" Use the correct for_each_new/old_* iterators instead of for_each_* The following functions were considered: amdgpu_dm_find_first_crtc_matching_connector: use for_each_new - Old from_state_var flag was always choosing the new state amdgpu_dm_display_resume: use for_each_new - drm_atomic_helper_duplicate_state is called during suspend to cache the state - It sets 'state' within the state triplet to 'new_state' amdgpu_dm_commit_planes: use for_each_old - Called after the state was swapped (via atomic commit tail) amdgpu_dm_atomic_commit: use for_each_new - Called before the state is swapped amdgpu_dm_atomic_commit_tail: use for_each_old - Called after the state was swapped dm_update_crtcs_state: use for_each_new - Called before the state is swapped (via atomic check) amdgpu_dm_atomic_check: use for_each_new - Called before the state is swapped v2: Split out typo fixes to a new patch. v3: Say "functions considered" instead of "affected functions". The latter implies that changes are made to each. Signed-off-by: Leo (Sunpeng) Li --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 32 --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 3 +-- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 9bfe1f9..cc024ab 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -570,23 +570,15 @@ static int dm_suspend(void *handle) struct amdgpu_dm_connector *amdgpu_dm_find_first_crct_matching_connector( struct drm_atomic_state *state, - struct drm_crtc *crtc, - bool from_state_var) + struct drm_crtc *crtc) { uint32_t i; struct drm_connector_state *conn_state; struct drm_connector *connector; struct drm_crtc *crtc_from_state; - for_each_new_connector_in_state( - state, - connector, - conn_state, - i) { - crtc_from_state = - from_state_var ? - conn_state->crtc : - connector->state->crtc; + for_each_new_connector_in_state(state, connector, conn_state, i) { + crtc_from_state = conn_state->crtc; if (crtc_from_state == crtc) return to_amdgpu_dm_connector(connector); @@ -3890,7 +3882,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, unsigned long flags; /* update planes when needed */ - for_each_new_plane_in_state(state, plane, old_plane_state, i) { + for_each_old_plane_in_state(state, plane, old_plane_state, i) { struct drm_plane_state *plane_state = plane->state; struct drm_crtc *crtc = plane_state->crtc; struct drm_framebuffer *fb = plane_state->fb; @@ -4024,7 +4016,7 @@ void amdgpu_dm_atomic_commit_tail( dm_state = to_dm_atomic_state(state); /* update changed items */ - for_each_new_crtc_in_state(state, crtc, old_crtc_state, i) { + for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); struct drm_crtc_state *new_state = crtc->state; @@ -4113,11 +4105,9 @@ void amdgpu_dm_atomic_commit_tail( new_acrtc_state = to_dm_crtc_state(new_crtcs[i]->base.state); new_stream = new_acrtc_state->stream; - aconnector = - amdgpu_dm_find_first_crct_matching_connector( + aconnector = amdgpu_dm_find_first_crct_matching_connector( state, - &new_crtcs[i]->base, - false); + &new_crtcs[i]->base); if (!aconnector) { DRM_DEBUG_DRIVER("Atomic commit: Failed to find connector for acrtc id:%d " "skipping freesync init\n", @@ -4151,7 +4141,7 @@ void amdgpu_dm_atomic_commit_tail( } /* Handle scaling and undersacn changes*/ - for_each_new_connector_in_state(state, connector, old_conn_state, i) { + for_each_old_connector_in_state(state, connector, old_conn_state, i) { struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); struct dm_connector_state *con_new_state = to_dm_connector_state(aconnector->base.state); @@ -4205,7 +4195,7 @@ void amdgpu_dm_atomic_commit_tail( } /* update planes when needed per crtc*/ - for_each_new_crtc_in_state(state, pcrtc, old_crtc_stat
[PATCH 3/6] drm/amd/display: Unify DRM state variable namings.
From: "Leo (Sunpeng) Li" Use new_*_state and old_*_state for their respective new/old DRM object states. Signed-off-by: Leo (Sunpeng) Li --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 80 +++ 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index d4426b3..fe0b658 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -573,12 +573,12 @@ struct amdgpu_dm_connector *amdgpu_dm_find_first_crct_matching_connector( struct drm_crtc *crtc) { uint32_t i; - struct drm_connector_state *conn_state; + struct drm_connector_state *new_con_state; struct drm_connector *connector; struct drm_crtc *crtc_from_state; - for_each_new_connector_in_state(state, connector, conn_state, i) { - crtc_from_state = conn_state->crtc; + for_each_new_connector_in_state(state, connector, new_con_state, i) { + crtc_from_state = new_con_state->crtc; if (crtc_from_state == crtc) return to_amdgpu_dm_connector(connector); @@ -608,7 +608,7 @@ int amdgpu_dm_display_resume(struct amdgpu_device *adev) struct amdgpu_dm_connector *aconnector; struct drm_connector *connector; struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; + struct drm_crtc_state *new_crtc_state; int ret = 0; int i; @@ -644,8 +644,8 @@ int amdgpu_dm_display_resume(struct amdgpu_device *adev) } /* Force mode set in atomic comit */ - for_each_new_crtc_in_state(adev->dm.cached_state, crtc, crtc_state, i) - crtc_state->active_changed = true; + for_each_new_crtc_in_state(adev->dm.cached_state, crtc, new_crtc_state, i) + new_crtc_state->active_changed = true; ret = drm_atomic_helper_resume(ddev, adev->dm.cached_state); @@ -3971,7 +3971,7 @@ int amdgpu_dm_atomic_commit( bool nonblock) { struct drm_crtc *crtc; - struct drm_crtc_state *old_crtc_state, *new_state; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; struct amdgpu_device *adev = dev->dev_private; int i; @@ -3982,11 +3982,11 @@ int amdgpu_dm_atomic_commit( * it will update crtc->dm_crtc_state->stream pointer which is used in * the ISRs. */ - for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_state, i) { + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { struct dm_crtc_state *old_acrtc_state = to_dm_crtc_state(old_crtc_state); struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); - if (drm_atomic_crtc_needs_modeset(new_state) && old_acrtc_state->stream) + if (drm_atomic_crtc_needs_modeset(new_crtc_state) && old_acrtc_state->stream) manage_dm_interrupts(adev, acrtc, false); } @@ -4011,7 +4011,7 @@ void amdgpu_dm_atomic_commit_tail( unsigned long flags; bool wait_for_vblank = true; struct drm_connector *connector; - struct drm_connector_state *old_conn_state, *new_con_state; + struct drm_connector_state *old_con_state, *new_con_state; struct dm_crtc_state *old_acrtc_state, *new_acrtc_state; drm_atomic_helper_update_legacy_modeset_state(dev, state); @@ -4145,9 +4145,9 @@ void amdgpu_dm_atomic_commit_tail( } /* Handle scaling and undersacn changes*/ - for_each_oldnew_connector_in_state(state, connector, old_conn_state, new_con_state, i) { + for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) { struct dm_connector_state *con_new_state = to_dm_connector_state(new_con_state); - struct dm_connector_state *con_old_state = to_dm_connector_state(old_conn_state); + struct dm_connector_state *con_old_state = to_dm_connector_state(old_con_state); struct amdgpu_crtc *acrtc = to_amdgpu_crtc(con_new_state->base.crtc); struct dc_stream_status *status = NULL; @@ -4375,7 +4375,7 @@ static int dm_update_crtcs_state( bool *lock_and_validation_needed) { struct drm_crtc *crtc; - struct drm_crtc_state *old_crtc_state, *crtc_state; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; int i; struct dm_crtc_state *old_acrtc_state, *new_acrtc_state; struct dm_atomic_state *dm_state = to_dm_atomic_state(state); @@ -4384,34 +4384,34 @@ static int dm_update_crtcs_state( /*TODO Move this code into dm_crtc_atomic_check once we get rid of dc_validation_set */ /* update changed items */ - for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, crtc_state, i)
[PATCH 6/6] drm/amd/display: Remove useless pcrtc pointer
From: "Leo (Sunpeng) Li" in amdgpu_dm_atomic_commit_tail. Just use crtc instead. Signed-off-by: Leo (Sunpeng) Li --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 67222ff..f9b5769 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -4004,7 +4004,7 @@ void amdgpu_dm_atomic_commit_tail( struct dm_atomic_state *dm_state; uint32_t i, j; uint32_t new_crtcs_count = 0; - struct drm_crtc *crtc, *pcrtc; + struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state, *new_crtc_state; struct amdgpu_crtc *new_crtcs[MAX_STREAMS]; struct dc_stream_state *new_stream = NULL; @@ -4200,11 +4200,11 @@ void amdgpu_dm_atomic_commit_tail( } /* update planes when needed per crtc*/ - for_each_new_crtc_in_state(state, pcrtc, new_crtc_state, j) { + for_each_new_crtc_in_state(state, crtc, new_crtc_state, j) { dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); if (dm_new_crtc_state->stream) - amdgpu_dm_commit_planes(state, dev, dm, pcrtc, &wait_for_vblank); + amdgpu_dm_commit_planes(state, dev, dm, crtc, &wait_for_vblank); } -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 0/6] Use new DRM API where possible, and cleanups.
From: "Leo (Sunpeng) Li" Hi Dave, This series reworks the previous patch. Patch 1 is a v2 of the previous, and additional patches are from the feedback received. They apply on top of your drm-next-amd-dc-staging branch. Thanks, Leo Leo (Sunpeng) Li (6): drm/amd/display: Use DRM new-style object iterators. drm/amd/display: Use new DRM API where possible drm/amd/display: Unify DRM state variable namings. drm/amd/display: Unify amdgpu_dm state variable namings. drm/amd/display: Fix typo drm/amd/display: Remove useless pcrtc pointer drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 320 +++--- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 3 +- 2 files changed, 156 insertions(+), 167 deletions(-) -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 4/6] drm/amd/display: Unify amdgpu_dm state variable namings.
From: "Leo (Sunpeng) Li" Use dm_new_*_state and dm_old_*_state for their respective amdgpu_dm new and old object states. Helps with readability, and enforces use of new DRM api (choose either new, or old). Signed-off-by: Leo (Sunpeng) Li --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 137 +++--- 1 file changed, 68 insertions(+), 69 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index fe0b658..de88ee1 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3890,7 +3890,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, drm_atomic_get_new_crtc_state(state, crtc); struct drm_framebuffer *fb = new_plane_state->fb; bool pflip_needed; - struct dm_plane_state *dm_plane_state = to_dm_plane_state(new_plane_state); + struct dm_plane_state *dm_new_plane_state = to_dm_plane_state(new_plane_state); if (plane->type == DRM_PLANE_TYPE_CURSOR) { handle_cursor_update(plane, old_plane_state); @@ -3914,9 +3914,9 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, spin_unlock_irqrestore(&crtc->dev->event_lock, flags); if (!pflip_needed) { - WARN_ON(!dm_plane_state->dc_state); + WARN_ON(!dm_new_plane_state->dc_state); - plane_states_constructed[planes_count] = dm_plane_state->dc_state; + plane_states_constructed[planes_count] = dm_new_plane_state->dc_state; dc_stream_attach = acrtc_state->stream; planes_count++; @@ -3983,10 +3983,10 @@ int amdgpu_dm_atomic_commit( * the ISRs. */ for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { - struct dm_crtc_state *old_acrtc_state = to_dm_crtc_state(old_crtc_state); + struct dm_crtc_state *dm_old_crtc_state = to_dm_crtc_state(old_crtc_state); struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); - if (drm_atomic_crtc_needs_modeset(new_crtc_state) && old_acrtc_state->stream) + if (drm_atomic_crtc_needs_modeset(new_crtc_state) && dm_old_crtc_state->stream) manage_dm_interrupts(adev, acrtc, false); } @@ -4012,7 +4012,7 @@ void amdgpu_dm_atomic_commit_tail( bool wait_for_vblank = true; struct drm_connector *connector; struct drm_connector_state *old_con_state, *new_con_state; - struct dm_crtc_state *old_acrtc_state, *new_acrtc_state; + struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state; drm_atomic_helper_update_legacy_modeset_state(dev, state); @@ -4022,8 +4022,8 @@ void amdgpu_dm_atomic_commit_tail( for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); - new_acrtc_state = to_dm_crtc_state(new_crtc_state); - old_acrtc_state = to_dm_crtc_state(old_crtc_state); + dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); + dm_old_crtc_state = to_dm_crtc_state(old_crtc_state); DRM_DEBUG_DRIVER( "amdgpu_crtc id:%d crtc_state_flags: enable:%d, active:%d, " @@ -4041,11 +4041,11 @@ void amdgpu_dm_atomic_commit_tail( * aconnector as needed */ - if (modeset_required(new_crtc_state, new_acrtc_state->stream, old_acrtc_state->stream)) { + if (modeset_required(new_crtc_state, dm_new_crtc_state->stream, dm_old_crtc_state->stream)) { DRM_DEBUG_DRIVER("Atomic commit: SET crtc id %d: [%p]\n", acrtc->crtc_id, acrtc); - if (!new_acrtc_state->stream) { + if (!dm_new_crtc_state->stream) { /* * this could happen because of issues with * userspace notifications delivery. @@ -4067,8 +4067,8 @@ void amdgpu_dm_atomic_commit_tail( } - if (old_acrtc_state->stream) - remove_stream(adev, acrtc, old_acrtc_state->stream); + if (dm_old_crtc_state->stream) + remove_stream(adev, acrtc, dm_old_crtc_state->stream); /* @@ -4092,8 +4092,8 @@ void amdgpu_dm_atomic_commit_tail( DRM_DEBUG_DRIVER("Atomic commit: RESET. crtc id %d:[%p]\n", acrtc->crtc_id, acrtc); /* i.e. reset mode */ - if (old_acrtc_state->stream) -
[PATCH 5/6] drm/amd/display: Fix typo
From: "Leo (Sunpeng) Li" undersacn -> underscan Signed-off-by: Leo (Sunpeng) Li --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index de88ee1..67222ff 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -4144,7 +4144,7 @@ void amdgpu_dm_atomic_commit_tail( } } - /* Handle scaling and undersacn changes*/ + /* Handle scaling and underscan changes*/ for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) { struct dm_connector_state *dm_new_con_state = to_dm_connector_state(new_con_state); struct dm_connector_state *dm_old_con_state = to_dm_connector_state(old_con_state); @@ -4707,7 +4707,7 @@ int amdgpu_dm_atomic_check(struct drm_device *dev, if (ret) goto fail; - /* Check scaling and undersacn changes*/ + /* Check scaling and underscan changes*/ /*TODO Removed scaling changes validation due to inability to commit * new stream into context w\o causing full reset. Need to * decide how to handle. -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 2/6] drm/amd/display: Use new DRM API where possible
From: "Leo (Sunpeng) Li" To conform to DRM's new API, we should not be accessing a DRM object's internal state directly. Rather, the DRM for_each_old/new_* iterators, and drm_atomic_get_old/new_* interface should be used. This is an ongoing process. For now, update the DRM-facing atomic functions, where the atomic state object is given. Signed-off-by: Leo (Sunpeng) Li --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 131 +++--- 1 file changed, 66 insertions(+), 65 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index cc024ab..d4426b3 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3873,28 +3873,31 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, { uint32_t i; struct drm_plane *plane; - struct drm_plane_state *old_plane_state; + struct drm_plane_state *old_plane_state, *new_plane_state; struct dc_stream_state *dc_stream_attach; struct dc_plane_state *plane_states_constructed[MAX_SURFACES]; struct amdgpu_crtc *acrtc_attach = to_amdgpu_crtc(pcrtc); - struct dm_crtc_state *acrtc_state = to_dm_crtc_state(pcrtc->state); + struct drm_crtc_state *new_pcrtc_state = + drm_atomic_get_new_crtc_state(state, pcrtc); + struct dm_crtc_state *acrtc_state = to_dm_crtc_state(new_pcrtc_state); int planes_count = 0; unsigned long flags; /* update planes when needed */ - for_each_old_plane_in_state(state, plane, old_plane_state, i) { - struct drm_plane_state *plane_state = plane->state; - struct drm_crtc *crtc = plane_state->crtc; - struct drm_framebuffer *fb = plane_state->fb; + for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) { + struct drm_crtc *crtc = new_plane_state->crtc; + struct drm_crtc_state *new_crtc_state = + drm_atomic_get_new_crtc_state(state, crtc); + struct drm_framebuffer *fb = new_plane_state->fb; bool pflip_needed; - struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state); + struct dm_plane_state *dm_plane_state = to_dm_plane_state(new_plane_state); if (plane->type == DRM_PLANE_TYPE_CURSOR) { handle_cursor_update(plane, old_plane_state); continue; } - if (!fb || !crtc || pcrtc != crtc || !crtc->state->active) + if (!fb || !crtc || pcrtc != crtc || !new_crtc_state->active) continue; pflip_needed = !state->allow_modeset; @@ -3918,13 +3921,13 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, dc_stream_attach = acrtc_state->stream; planes_count++; - } else if (crtc->state->planes_changed) { + } else if (new_crtc_state->planes_changed) { /* Assume even ONE crtc with immediate flip means * entire can't wait for VBLANK * TODO Check if it's correct */ *wait_for_vblank = - pcrtc->state->pageflip_flags & DRM_MODE_PAGE_FLIP_ASYNC ? + new_pcrtc_state->pageflip_flags & DRM_MODE_PAGE_FLIP_ASYNC ? false : true; /* TODO: Needs rework for multiplane flip */ @@ -3942,7 +3945,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, if (planes_count) { unsigned long flags; - if (pcrtc->state->event) { + if (new_pcrtc_state->event) { drm_crtc_vblank_get(pcrtc); @@ -3968,7 +3971,7 @@ int amdgpu_dm_atomic_commit( bool nonblock) { struct drm_crtc *crtc; - struct drm_crtc_state *new_state; + struct drm_crtc_state *old_crtc_state, *new_state; struct amdgpu_device *adev = dev->dev_private; int i; @@ -3979,8 +3982,8 @@ int amdgpu_dm_atomic_commit( * it will update crtc->dm_crtc_state->stream pointer which is used in * the ISRs. */ - for_each_new_crtc_in_state(state, crtc, new_state, i) { - struct dm_crtc_state *old_acrtc_state = to_dm_crtc_state(crtc->state); + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_state, i) { + struct dm_crtc_state *old_acrtc_state = to_dm_crtc_state(old_crtc_state); struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); if (drm_atomic_crtc_needs_modeset(new_state) && old_acrtc_state->stream) @@ -4002,13
[PATCH v2 1/6] drm/amd/display: Use DRM new-style object iterators.
From: "Leo (Sunpeng) Li" Use the correct for_each_new/old_* iterators instead of for_each_* List of affected functions: amdgpu_dm_find_first_crtc_matching_connector: use for_each_new - Old from_state_var flag was always choosing the new state amdgpu_dm_display_resume: use for_each_new - drm_atomic_helper_duplicate_state is called during suspend to cache the state - It sets 'state' within the state triplet to 'new_state' amdgpu_dm_commit_planes: use for_each_old - Called after the state was swapped (via atomic commit tail) amdgpu_dm_atomic_commit: use for_each_new - Called before the state is swapped amdgpu_dm_atomic_commit_tail: use for_each_old - Called after the state was swapped dm_update_crtcs_state: use for_each_new - Called before the state is swapped (via atomic check) amdgpu_dm_atomic_check: use for_each_new - Called before the state is swapped v2: Split out typo fixes to a new patch. Signed-off-by: Leo (Sunpeng) Li --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 32 --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 3 +-- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 9bfe1f9..cc024ab 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -570,23 +570,15 @@ static int dm_suspend(void *handle) struct amdgpu_dm_connector *amdgpu_dm_find_first_crct_matching_connector( struct drm_atomic_state *state, - struct drm_crtc *crtc, - bool from_state_var) + struct drm_crtc *crtc) { uint32_t i; struct drm_connector_state *conn_state; struct drm_connector *connector; struct drm_crtc *crtc_from_state; - for_each_new_connector_in_state( - state, - connector, - conn_state, - i) { - crtc_from_state = - from_state_var ? - conn_state->crtc : - connector->state->crtc; + for_each_new_connector_in_state(state, connector, conn_state, i) { + crtc_from_state = conn_state->crtc; if (crtc_from_state == crtc) return to_amdgpu_dm_connector(connector); @@ -3890,7 +3882,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, unsigned long flags; /* update planes when needed */ - for_each_new_plane_in_state(state, plane, old_plane_state, i) { + for_each_old_plane_in_state(state, plane, old_plane_state, i) { struct drm_plane_state *plane_state = plane->state; struct drm_crtc *crtc = plane_state->crtc; struct drm_framebuffer *fb = plane_state->fb; @@ -4024,7 +4016,7 @@ void amdgpu_dm_atomic_commit_tail( dm_state = to_dm_atomic_state(state); /* update changed items */ - for_each_new_crtc_in_state(state, crtc, old_crtc_state, i) { + for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); struct drm_crtc_state *new_state = crtc->state; @@ -4113,11 +4105,9 @@ void amdgpu_dm_atomic_commit_tail( new_acrtc_state = to_dm_crtc_state(new_crtcs[i]->base.state); new_stream = new_acrtc_state->stream; - aconnector = - amdgpu_dm_find_first_crct_matching_connector( + aconnector = amdgpu_dm_find_first_crct_matching_connector( state, - &new_crtcs[i]->base, - false); + &new_crtcs[i]->base); if (!aconnector) { DRM_DEBUG_DRIVER("Atomic commit: Failed to find connector for acrtc id:%d " "skipping freesync init\n", @@ -4151,7 +4141,7 @@ void amdgpu_dm_atomic_commit_tail( } /* Handle scaling and undersacn changes*/ - for_each_new_connector_in_state(state, connector, old_conn_state, i) { + for_each_old_connector_in_state(state, connector, old_conn_state, i) { struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); struct dm_connector_state *con_new_state = to_dm_connector_state(aconnector->base.state); @@ -4205,7 +4195,7 @@ void amdgpu_dm_atomic_commit_tail( } /* update planes when needed per crtc*/ - for_each_new_crtc_in_state(state, pcrtc, old_crtc_state, j) { + for_each_old_crtc_in_state(state, pcrtc, old_crtc_state, j) { new_acrtc_state = to_dm_crtc_state(pcr
[PATCH] amdgpu/dc: Use DRM new-style object iterators.
From: "Leo (Sunpeng) Li" Use the correct for_each_new/old_* iterators instead of for_each_* List of affected functions: amdgpu_dm_find_first_crtc_matching_connector: use for_each_new - Old from_state_var flag was always choosing the new state amdgpu_dm_display_resume: use for_each_new - drm_atomic_helper_duplicate_state is called during suspend to cache the state - It sets 'state' within the state triplet to 'new_state' amdgpu_dm_commit_planes: use for_each_old - Called after the state was swapped (via atomic commit tail) amdgpu_dm_atomic_commit: use for_each_new - Called before the state is swapped amdgpu_dm_atomic_commit_tail: use for_each_old - Called after the state was swapped dm_update_crtcs_state: use for_each_new - Called before the state is swapped (via atomic check) amdgpu_dm_atomic_check: use for_each_new - Called before the state is swapped Also fix some typos. crct -> crtc undersacn -> underscan Signed-off-by: Leo (Sunpeng) Li --- Hi Dave, This patch goes on top of your fixup for new api's patch. Please feel free to squash them. Thanks, Leo drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 38 +-- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 5 ++- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 9bfe1f9..9394558 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -568,25 +568,17 @@ static int dm_suspend(void *handle) return ret; } -struct amdgpu_dm_connector *amdgpu_dm_find_first_crct_matching_connector( +struct amdgpu_dm_connector *amdgpu_dm_find_first_crtc_matching_connector( struct drm_atomic_state *state, - struct drm_crtc *crtc, - bool from_state_var) + struct drm_crtc *crtc) { uint32_t i; struct drm_connector_state *conn_state; struct drm_connector *connector; struct drm_crtc *crtc_from_state; - for_each_new_connector_in_state( - state, - connector, - conn_state, - i) { - crtc_from_state = - from_state_var ? - conn_state->crtc : - connector->state->crtc; + for_each_new_connector_in_state(state, connector, conn_state, i) { + crtc_from_state = conn_state->crtc; if (crtc_from_state == crtc) return to_amdgpu_dm_connector(connector); @@ -3890,7 +3882,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, unsigned long flags; /* update planes when needed */ - for_each_new_plane_in_state(state, plane, old_plane_state, i) { + for_each_old_plane_in_state(state, plane, old_plane_state, i) { struct drm_plane_state *plane_state = plane->state; struct drm_crtc *crtc = plane_state->crtc; struct drm_framebuffer *fb = plane_state->fb; @@ -4024,7 +4016,7 @@ void amdgpu_dm_atomic_commit_tail( dm_state = to_dm_atomic_state(state); /* update changed items */ - for_each_new_crtc_in_state(state, crtc, old_crtc_state, i) { + for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); struct drm_crtc_state *new_state = crtc->state; @@ -4113,11 +4105,9 @@ void amdgpu_dm_atomic_commit_tail( new_acrtc_state = to_dm_crtc_state(new_crtcs[i]->base.state); new_stream = new_acrtc_state->stream; - aconnector = - amdgpu_dm_find_first_crct_matching_connector( + aconnector = amdgpu_dm_find_first_crtc_matching_connector( state, - &new_crtcs[i]->base, - false); + &new_crtcs[i]->base); if (!aconnector) { DRM_DEBUG_DRIVER("Atomic commit: Failed to find connector for acrtc id:%d " "skipping freesync init\n", @@ -4150,8 +4140,8 @@ void amdgpu_dm_atomic_commit_tail( } } - /* Handle scaling and undersacn changes*/ - for_each_new_connector_in_state(state, connector, old_conn_state, i) { + /* Handle scaling and underscan changes*/ + for_each_old_connector_in_state(state, connector, old_conn_state, i) { struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); struct dm_connector_state *con_new_state = to_dm_connector_state(aconnector->base.state); @@ -4205,7 +