[igt-dev] [PATCH i-g-t v3] lib/igt_kms: Fix commits for planes with multiple possible CRTCs
Wentland, Harry
Harry.Wentland at amd.com
Wed Feb 27 20:13:37 UTC 2019
On 2019-02-25 10:42 a.m., Nicholas Kazlauskas wrote:
> An igt_plane_t is defined per igt_pipe_t. It is treated as its
> own independent resource but DRM planes can be exposed to being used on
> a number of possible CRTCs - making it a shared resource.
>
> In IGT planes with multiple possible CRTCs are added to the plane list
> for each pipe that the plane supports. The internal state remains
> independent in IGT so when the same plane is modified for multiple
> pipes in a single commit the last pipe to modify it is the one whose
> state gets fully applied.
>
> This situation happens fairly often in practice - resetting the display
> at the start of the test before a commit will reset the CRTC ID and FB
> ID for each plane.
>
> For an example, consider the
> igt at kms_plane_alpha_blend@pipe-a-constant-alpha-max test.
>
> This test will fail for any overlay plane exposed for multiple CRTCs.
>
> The test tries to set a framebuffer for pipe A but has all the other
> pipes reset the plane state in the same commit. If there are multiple
> pipes on the hardware then the last pipe will be the one to set all the
> plane properties. The driver will receive a commit with no overlay
> plane enabled since the last pipe set CRTC ID and FB ID to 0, disabling
> the plane. The reference CRC capture will be incorrect since not all
> the planes have been enabled and the subsequent CRC captures will
> not match, failing the test.
>
> The simplest (but hacky) fix to this problem is to only set the
> properties for the plane for the pipe that last had it bound.
>
> This patch introduces a global plane list on igt_display_t that keeps
> track of the pipe that pipe that last called igt_plane_set_fb. The
> properties for the plane will only be applied from that single pipe
> when commiting the state to DRM.
>
> No behavioral changes should be introduced by this patch for hardware
> whose planes are only ever exposed one CRTC.
>
> It would likely be best to eventually modify the igt_pipe_t plane list
> to be a list of pointers to planes instead (igt_plane_t**)
> instead of being the actual plane objects, but that can come later.
>
> Many areas of the code like to make use of the backpointer to the pipe
> on the plane which makes refactoring the code in that manner a little
> trickier.
>
> v2: Add igt_plane_set_fb, use igt_plane_t for global plane list (Daniel)
> v3: Leave TODO for filling in all state/props on global planes
>
> Cc: Daniel Vetter <daniel at ffwll.ch>
> Signed-off-by: Nicholas Kazlauskas <nicholas.kazlauskas at amd.com>
Acked-by: Harry Wentland <harry.wentland at amd.com>
Daniel, ping.
Harry
> ---
> lib/igt_kms.c | 103 ++++++++++++++++++++++++++++++++++++--------------
> lib/igt_kms.h | 6 ++-
> 2 files changed, 79 insertions(+), 30 deletions(-)
>
> diff --git a/lib/igt_kms.c b/lib/igt_kms.c
> index 761bb193..a68a9087 100644
> --- a/lib/igt_kms.c
> +++ b/lib/igt_kms.c
> @@ -1905,6 +1905,26 @@ void igt_display_require(igt_display_t *display, int drm_fd)
> plane_resources = drmModeGetPlaneResources(display->drm_fd);
> igt_assert(plane_resources);
>
> + display->n_planes = plane_resources->count_planes;
> + display->planes = calloc(sizeof(igt_plane_t), display->n_planes);
> + igt_assert_f(display->planes, "Failed to allocate memory for %d planes\n", display->n_planes);
> +
> + for (i = 0; i < plane_resources->count_planes; ++i) {
> + igt_plane_t *plane = &display->planes[i];
> + uint32_t id = plane_resources->planes[i];
> +
> + plane->drm_plane = drmModeGetPlane(display->drm_fd, id);
> + igt_assert(plane->drm_plane);
> +
> + plane->type = get_drm_plane_type(display->drm_fd, id);
> +
> + /*
> + * TODO: Fill in the rest of the plane properties here and
> + * move away from the plane per pipe model to align closer
> + * to the DRM KMS model.
> + */
> + }
> +
> for_each_pipe(display, i) {
> igt_pipe_t *pipe = &display->pipes[i];
> igt_plane_t *plane;
> @@ -1922,17 +1942,12 @@ void igt_display_require(igt_display_t *display, int drm_fd)
> igt_fill_pipe_props(display, pipe, IGT_NUM_CRTC_PROPS, igt_crtc_prop_names);
>
> /* count number of valid planes */
> - for (j = 0; j < plane_resources->count_planes; j++) {
> - drmModePlane *drm_plane;
> -
> - drm_plane = drmModeGetPlane(display->drm_fd,
> - plane_resources->planes[j]);
> + for (j = 0; j < display->n_planes; j++) {
> + drmModePlane *drm_plane = display->planes[j].drm_plane;
> igt_assert(drm_plane);
>
> if (drm_plane->possible_crtcs & (1 << i))
> n_planes++;
> -
> - drmModeFreePlane(drm_plane);
> }
>
> igt_assert_lt(0, n_planes);
> @@ -1941,20 +1956,14 @@ void igt_display_require(igt_display_t *display, int drm_fd)
> last_plane = n_planes - 1;
>
> /* add the planes that can be used with that pipe */
> - for (j = 0; j < plane_resources->count_planes; j++) {
> - drmModePlane *drm_plane;
> -
> - drm_plane = drmModeGetPlane(display->drm_fd,
> - plane_resources->planes[j]);
> - igt_assert(drm_plane);
> + for (j = 0; j < display->n_planes; j++) {
> + igt_plane_t *global_plane = &display->planes[j];
> + drmModePlane *drm_plane = global_plane->drm_plane;
>
> - if (!(drm_plane->possible_crtcs & (1 << i))) {
> - drmModeFreePlane(drm_plane);
> + if (!(drm_plane->possible_crtcs & (1 << i)))
> continue;
> - }
>
> - type = get_drm_plane_type(display->drm_fd,
> - plane_resources->planes[j]);
> + type = global_plane->type;
>
> if (type == DRM_PLANE_TYPE_PRIMARY && pipe->plane_primary == -1) {
> plane = &pipe->planes[0];
> @@ -1975,6 +1984,14 @@ void igt_display_require(igt_display_t *display, int drm_fd)
> plane->pipe = pipe;
> plane->drm_plane = drm_plane;
> plane->values[IGT_PLANE_IN_FENCE_FD] = ~0ULL;
> + plane->ref = global_plane;
> +
> + /*
> + * HACK: point the global plane to the first pipe that
> + * it can go on.
> + */
> + if (!global_plane->ref)
> + igt_plane_set_pipe(plane, pipe);
>
> igt_fill_plane_props(display, plane, IGT_NUM_PLANE_PROPS, igt_plane_prop_names);
>
> @@ -2127,17 +2144,6 @@ igt_output_t *igt_output_from_connector(igt_display_t *display,
>
> static void igt_pipe_fini(igt_pipe_t *pipe)
> {
> - int i;
> -
> - for (i = 0; i < pipe->n_planes; i++) {
> - igt_plane_t *plane = &pipe->planes[i];
> -
> - if (plane->drm_plane) {
> - drmModeFreePlane(plane->drm_plane);
> - plane->drm_plane = NULL;
> - }
> - }
> -
> free(pipe->planes);
> pipe->planes = NULL;
>
> @@ -2163,6 +2169,15 @@ void igt_display_fini(igt_display_t *display)
> {
> int i;
>
> + for (i = 0; i < display->n_planes; ++i) {
> + igt_plane_t *plane = &display->planes[i];
> +
> + if (plane->drm_plane) {
> + drmModeFreePlane(plane->drm_plane);
> + plane->drm_plane = NULL;
> + }
> + }
> +
> for (i = 0; i < display->n_pipes; i++)
> igt_pipe_fini(&display->pipes[i]);
>
> @@ -2172,6 +2187,8 @@ void igt_display_fini(igt_display_t *display)
> display->outputs = NULL;
> free(display->pipes);
> display->pipes = NULL;
> + free(display->planes);
> + display->planes = NULL;
> }
>
> static void igt_display_refresh(igt_display_t *display)
> @@ -2789,6 +2806,10 @@ static int igt_pipe_commit(igt_pipe_t *pipe,
> for (i = 0; i < pipe->n_planes; i++) {
> igt_plane_t *plane = &pipe->planes[i];
>
> + /* skip planes that are handled by another pipe */
> + if (plane->ref->pipe != pipe)
> + continue;
> +
> ret = igt_plane_commit(plane, pipe, s, fail_on_error);
> CHECK_RETURN(ret, fail_on_error);
> }
> @@ -3177,6 +3198,10 @@ static int igt_atomic_commit(igt_display_t *display, uint32_t flags, void *user_
> igt_atomic_prepare_crtc_commit(pipe_obj, req);
>
> for_each_plane_on_pipe(display, pipe, plane) {
> + /* skip planes that are handled by another pipe */
> + if (plane->ref->pipe != pipe_obj)
> + continue;
> +
> if (plane->changed)
> igt_atomic_prepare_plane_commit(plane, pipe_obj, req);
> }
> @@ -3709,6 +3734,9 @@ void igt_plane_set_fb(igt_plane_t *plane, struct igt_fb *fb)
> if (igt_plane_has_prop(plane, IGT_PLANE_COLOR_RANGE))
> igt_plane_set_prop_enum(plane, IGT_PLANE_COLOR_RANGE,
> igt_color_range_to_str(fb->color_range));
> +
> + /* Hack to prioritize the plane on the pipe that last set fb */
> + igt_plane_set_pipe(plane, pipe);
> } else {
> igt_plane_set_size(plane, 0, 0);
>
> @@ -3743,6 +3771,23 @@ void igt_plane_set_fence_fd(igt_plane_t *plane, int fence_fd)
> igt_plane_set_prop_value(plane, IGT_PLANE_IN_FENCE_FD, fd);
> }
>
> +/**
> + * igt_plane_set_pipe:
> + * @plane: Target plane pointer
> + * @pipe: The pipe to assign the plane to
> + *
> + */
> +void igt_plane_set_pipe(igt_plane_t *plane, igt_pipe_t *pipe)
> +{
> + /*
> + * HACK: Point the global plane back to the local plane.
> + * This is used to help apply the correct atomic state while
> + * we're moving away from the single pipe per plane model.
> + */
> + plane->ref->ref = plane;
> + plane->ref->pipe = pipe;
> +}
> +
> /**
> * igt_plane_set_position:
> * @plane: Plane pointer for which position is to be set
> diff --git a/lib/igt_kms.h b/lib/igt_kms.h
> index 5755b160..e1c5c967 100644
> --- a/lib/igt_kms.h
> +++ b/lib/igt_kms.h
> @@ -300,9 +300,10 @@ typedef enum {
> #define IGT_ROTATION_MASK \
> (IGT_ROTATION_0 | IGT_ROTATION_90 | IGT_ROTATION_180 | IGT_ROTATION_270)
>
> -typedef struct {
> +typedef struct igt_plane {
> /*< private >*/
> igt_pipe_t *pipe;
> + struct igt_plane *ref;
> int index;
> /* capabilities */
> int type;
> @@ -372,8 +373,10 @@ struct igt_display {
> int drm_fd;
> int log_shift;
> int n_pipes;
> + int n_planes;
> int n_outputs;
> igt_output_t *outputs;
> + igt_plane_t *planes;
> igt_pipe_t *pipes;
> bool has_cursor_plane;
> bool is_atomic;
> @@ -413,6 +416,7 @@ void igt_pipe_request_out_fence(igt_pipe_t *pipe);
>
> void igt_plane_set_fb(igt_plane_t *plane, struct igt_fb *fb);
> void igt_plane_set_fence_fd(igt_plane_t *plane, int fence_fd);
> +void igt_plane_set_pipe(igt_plane_t *plane, igt_pipe_t *pipe);
> void igt_plane_set_position(igt_plane_t *plane, int x, int y);
> void igt_plane_set_size(igt_plane_t *plane, int w, int h);
> void igt_plane_set_rotation(igt_plane_t *plane, igt_rotation_t rotation);
>
More information about the igt-dev
mailing list