~lkcamp/patches

drm: selftest: convert drm_damage_helper selftest to KUnit v3 PROPOSED

Maíra Canal: 1
 drm: selftest: convert drm_damage_helper selftest to KUnit

 5 files changed, 274 insertions(+), 315 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~lkcamp/patches/patches/28823/mbox | git am -3
Learn more about email & git

[PATCH v3] drm: selftest: convert drm_damage_helper selftest to KUnit Export this patch

Considering the current adoption of the KUnit framework, convert the
DRM damage helper selftest to the KUnit API.

Co-developed-by: Arthur Grillo <arthur.grillo@usp.br>
Signed-off-by: Arthur Grillo <arthur.grillo@usp.br>
Signed-off-by: Maíra Canal <maira.canal@usp.br>
---
V1 -> V2: 
  - Commit in imperative mode
  - Valid SoB-chain
  - Change Kconfig entry to match the pattern
  - Fix indentation
V2 -> V3:
  - As suggested by Daniel, make error messages more similar to the original
  - As suggested by Daniel, turn check_damage_clip into a KUnit function
  - As suggested by Leandro, use KUnit init function to allocate resources
---
 drivers/gpu/drm/Kconfig                       |  13 +
 drivers/gpu/drm/Makefile                      |   2 +-
 drivers/gpu/drm/selftests/Makefile            |   5 +-
 .../gpu/drm/selftests/drm_modeset_selftests.h |  21 -
 .../drm/selftests/test-drm_damage_helper.c    | 548 ++++++++----------
 5 files changed, 274 insertions(+), 315 deletions(-)

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index cea777ae7fb9..56f6ca74ebb6 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -79,6 +79,19 @@ config DRM_DEBUG_SELFTEST

	  If in doubt, say "N".

config DRM_DAMAGE_HELPER_KUNIT_TEST
	tristate "KUnit tests for DRM damage helper"
	depends on DRM && KUNIT
	select DRM_KMS_HELPER
	default KUNIT_ALL_TESTS
	help
	  This option provides a KUnit module that can be used to run
	  an unit test on the DRM damage helper API. This option is not
	  useful for distributions or general kernels, but only for kernel
	  developers working on DRM and associated drivers.

	  If in doubt, say "N".

config DRM_KMS_HELPER
	tristate
	depends on DRM
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index ad1112154898..e632c43c9b7d 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -58,7 +58,7 @@ drm_kms_helper-$(CONFIG_DRM_DP_AUX_CHARDEV) += drm_dp_aux_dev.o
drm_kms_helper-$(CONFIG_DRM_DP_CEC) += drm_dp_cec.o

obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
obj-$(CONFIG_DRM_DEBUG_SELFTEST) += selftests/
obj-y += selftests/

obj-$(CONFIG_DRM)	+= drm.o
obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o
diff --git a/drivers/gpu/drm/selftests/Makefile b/drivers/gpu/drm/selftests/Makefile
index 0856e4b12f70..311f6af2caf1 100644
--- a/drivers/gpu/drm/selftests/Makefile
+++ b/drivers/gpu/drm/selftests/Makefile
@@ -1,7 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
test-drm_modeset-y := test-drm_modeset_common.o test-drm_plane_helper.o \
                      test-drm_format.o test-drm_framebuffer.o \
		      test-drm_damage_helper.o test-drm_dp_mst_helper.o \
		      test-drm_rect.o
		      test-drm_dp_mst_helper.o test-drm_rect.o

obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_modeset.o test-drm_cmdline_parser.o

obj-$(CONFIG_DRM_DAMAGE_HELPER_KUNIT_TEST) += test-drm_damage_helper.o
diff --git a/drivers/gpu/drm/selftests/drm_modeset_selftests.h b/drivers/gpu/drm/selftests/drm_modeset_selftests.h
index 782e285ca383..4787b3b70709 100644
--- a/drivers/gpu/drm/selftests/drm_modeset_selftests.h
+++ b/drivers/gpu/drm/selftests/drm_modeset_selftests.h
@@ -15,26 +15,5 @@ selftest(check_drm_format_block_width, igt_check_drm_format_block_width)
selftest(check_drm_format_block_height, igt_check_drm_format_block_height)
selftest(check_drm_format_min_pitch, igt_check_drm_format_min_pitch)
selftest(check_drm_framebuffer_create, igt_check_drm_framebuffer_create)
selftest(damage_iter_no_damage, igt_damage_iter_no_damage)
selftest(damage_iter_no_damage_fractional_src, igt_damage_iter_no_damage_fractional_src)
selftest(damage_iter_no_damage_src_moved, igt_damage_iter_no_damage_src_moved)
selftest(damage_iter_no_damage_fractional_src_moved, igt_damage_iter_no_damage_fractional_src_moved)
selftest(damage_iter_no_damage_not_visible, igt_damage_iter_no_damage_not_visible)
selftest(damage_iter_no_damage_no_crtc, igt_damage_iter_no_damage_no_crtc)
selftest(damage_iter_no_damage_no_fb, igt_damage_iter_no_damage_no_fb)
selftest(damage_iter_simple_damage, igt_damage_iter_simple_damage)
selftest(damage_iter_single_damage, igt_damage_iter_single_damage)
selftest(damage_iter_single_damage_intersect_src, igt_damage_iter_single_damage_intersect_src)
selftest(damage_iter_single_damage_outside_src, igt_damage_iter_single_damage_outside_src)
selftest(damage_iter_single_damage_fractional_src, igt_damage_iter_single_damage_fractional_src)
selftest(damage_iter_single_damage_intersect_fractional_src, igt_damage_iter_single_damage_intersect_fractional_src)
selftest(damage_iter_single_damage_outside_fractional_src, igt_damage_iter_single_damage_outside_fractional_src)
selftest(damage_iter_single_damage_src_moved, igt_damage_iter_single_damage_src_moved)
selftest(damage_iter_single_damage_fractional_src_moved, igt_damage_iter_single_damage_fractional_src_moved)
selftest(damage_iter_damage, igt_damage_iter_damage)
selftest(damage_iter_damage_one_intersect, igt_damage_iter_damage_one_intersect)
selftest(damage_iter_damage_one_outside, igt_damage_iter_damage_one_outside)
selftest(damage_iter_damage_src_moved, igt_damage_iter_damage_src_moved)
selftest(damage_iter_damage_not_visible, igt_damage_iter_damage_not_visible)
selftest(dp_mst_calc_pbn_mode, igt_dp_mst_calc_pbn_mode)
selftest(dp_mst_sideband_msg_req_decode, igt_dp_mst_sideband_msg_req_decode)
diff --git a/drivers/gpu/drm/selftests/test-drm_damage_helper.c b/drivers/gpu/drm/selftests/test-drm_damage_helper.c
index 1c19a5d3eefb..e57c8fe60d1b 100644
--- a/drivers/gpu/drm/selftests/test-drm_damage_helper.c
+++ b/drivers/gpu/drm/selftests/test-drm_damage_helper.c
@@ -3,38 +3,52 @@
 * Test case for drm_damage_helper functions
 */

#define pr_fmt(fmt) "drm_damage_helper: " fmt

#include <kunit/test.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_plane.h>
#include <drm/drm_drv.h>

#include "test-drm_modeset_common.h"

struct drm_driver mock_driver;
static struct drm_device mock_device;
static struct drm_object_properties mock_obj_props;
static struct drm_plane mock_plane;
static struct drm_property mock_prop;
struct drm_damage_mock {
	struct drm_driver driver;
	struct drm_device device;
	struct drm_object_properties obj_props;
	struct drm_plane plane;
	struct drm_property prop;
	struct drm_framebuffer fb;
	struct drm_plane_state state;
	struct drm_plane_state old_state;
};

static void mock_setup(struct drm_plane_state *state)
static int drm_damage_helper_init(struct kunit *test)
{
	static bool setup_done = false;
	struct drm_damage_mock *mock;

	mock = kunit_kzalloc(test, sizeof(*mock), GFP_KERNEL);
	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mock);

	mock->fb.width = 2048;
	mock->fb.height = 2048;

	state->plane = &mock_plane;
	mock->state.crtc = ZERO_SIZE_PTR;
	mock->state.fb = &mock->fb;
	mock->state.visible = true;

	if (setup_done)
		return;
	mock->old_state.plane = &mock->plane;
	mock->state.plane = &mock->plane;

	/* just enough so that drm_plane_enable_fb_damage_clips() works */
	mock_device.driver = &mock_driver;
	mock_device.mode_config.prop_fb_damage_clips = &mock_prop;
	mock_plane.dev = &mock_device;
	mock_plane.base.properties = &mock_obj_props;
	mock_prop.base.id = 1; /* 0 is an invalid id */
	mock_prop.dev = &mock_device;

	drm_plane_enable_fb_damage_clips(&mock_plane);
	mock->device.driver = &mock->driver;
	mock->device.mode_config.prop_fb_damage_clips = &mock->prop;
	mock->plane.dev = &mock->device;
	mock->plane.base.properties = &mock->obj_props;
	mock->prop.base.id = 1; /* 0 is an invalid id */
	mock->prop.dev = &mock->device;

	drm_plane_enable_fb_damage_clips(&mock->plane);

	test->priv = mock;

	return 0;
}

static void set_plane_src(struct drm_plane_state *state, int x1, int y1, int x2,
@@ -68,599 +82,551 @@ static void set_plane_damage(struct drm_plane_state *state,
	state->fb_damage_clips = damage_blob;
}

static bool check_damage_clip(struct drm_plane_state *state, struct drm_rect *r,
			      int x1, int y1, int x2, int y2)
static void check_damage_clip(struct kunit *test, struct drm_rect *r,
		int x1, int y1, int x2, int y2)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_plane_state state = mock->state;

	/*
	 * Round down x1/y1 and round up x2/y2. This is because damage is not in
	 * 16.16 fixed point so to catch all pixels.
	 */
	int src_x1 = state->src.x1 >> 16;
	int src_y1 = state->src.y1 >> 16;
	int src_x2 = (state->src.x2 >> 16) + !!(state->src.x2 & 0xFFFF);
	int src_y2 = (state->src.y2 >> 16) + !!(state->src.y2 & 0xFFFF);

	if (x1 >= x2 || y1 >= y2) {
		pr_err("Cannot have damage clip with no dimension.\n");
		return false;
	}

	if (x1 < src_x1 || y1 < src_y1 || x2 > src_x2 || y2 > src_y2) {
		pr_err("Damage cannot be outside rounded plane src.\n");
		return false;
	}

	if (r->x1 != x1 || r->y1 != y1 || r->x2 != x2 || r->y2 != y2) {
		pr_err("Damage = %d %d %d %d\n", r->x1, r->y1, r->x2, r->y2);
		return false;
	}

	return true;
	int src_x1 = state.src.x1 >> 16;
	int src_y1 = state.src.y1 >> 16;
	int src_x2 = (state.src.x2 >> 16) + !!(state.src.x2 & 0xFFFF);
	int src_y2 = (state.src.y2 >> 16) + !!(state.src.y2 & 0xFFFF);

	if (x1 >= x2 || y1 >= y2)
		KUNIT_FAIL(test, "Cannot have damage clip with no dimension.");
	if (x1 < src_x1 || y1 < src_y1 || x2 > src_x2 || y2 > src_y2)
		KUNIT_FAIL(test, "Damage cannot be outside rounded plane src.");
	if (r->x1 != x1 || r->y1 != y1 || r->x2 != x2 || r->y2 != y2)
		KUNIT_FAIL(test, "Damage = %d %d %d %d, want = %d %d %d %d",
				r->x1, r->y1, r->x2, r->y2, x1, y1, x2, y2);
}

const struct drm_framebuffer fb = {
	.width = 2048,
	.height = 2048
};

/* common mocked structs many tests need */
#define MOCK_VARIABLES() \
	struct drm_plane_state old_state; \
	struct drm_plane_state state = { \
		.crtc = ZERO_SIZE_PTR, \
		.fb = (struct drm_framebuffer *) &fb, \
		.visible = true, \
	}; \
	mock_setup(&old_state); \
	mock_setup(&state);

int igt_damage_iter_no_damage(void *ignored)
static void igt_damage_iter_no_damage(struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	/* Plane src same as fb size. */
	set_plane_src(&old_state, 0, 0, fb.width << 16, fb.height << 16);
	set_plane_src(&state, 0, 0, fb.width << 16, fb.height << 16);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	set_plane_src(&mock->old_state, 0, 0, mock->fb.width << 16, mock->fb.height << 16);
	set_plane_src(&mock->state, 0, 0, mock->fb.width << 16, mock->fb.height << 16);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 1, "Should return plane src as damage.");
	FAIL_ON(!check_damage_clip(&state, &clip, 0, 0, 2048, 2048));

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return plane src as damage.");
	check_damage_clip(test, &clip, 0, 0, 2048, 2048);
}

int igt_damage_iter_no_damage_fractional_src(void *ignored)
static void igt_damage_iter_no_damage_fractional_src(struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	/* Plane src has fractional part. */
	set_plane_src(&old_state, 0x3fffe, 0x3fffe,
	set_plane_src(&mock->old_state, 0x3fffe, 0x3fffe,
		      0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
	set_plane_src(&state, 0x3fffe, 0x3fffe,
	set_plane_src(&mock->state, 0x3fffe, 0x3fffe,
		      0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 1, "Should return rounded off plane src as damage.");
	FAIL_ON(!check_damage_clip(&state, &clip, 3, 3, 1028, 772));

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 1,
			"Should return rounded off plane src as damage.");
	check_damage_clip(test, &clip, 3, 3, 1028, 772);
}

int igt_damage_iter_no_damage_src_moved(void *ignored)
static void igt_damage_iter_no_damage_src_moved(struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	/* Plane src moved since old plane state. */
	set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&state, 10 << 16, 10 << 16,
	set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&mock->state, 10 << 16, 10 << 16,
		      (10 + 1024) << 16, (10 + 768) << 16);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 1, "Should return plane src as damage.");
	FAIL_ON(!check_damage_clip(&state, &clip, 10, 10, 1034, 778));

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return plane src as damage.");
	check_damage_clip(test, &clip, 10, 10, 1034, 778);
}

int igt_damage_iter_no_damage_fractional_src_moved(void *ignored)
static void igt_damage_iter_no_damage_fractional_src_moved(struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	/* Plane src has fractional part and it moved since old plane state. */
	set_plane_src(&old_state, 0x3fffe, 0x3fffe,
	set_plane_src(&mock->old_state, 0x3fffe, 0x3fffe,
		      0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
	set_plane_src(&state, 0x40002, 0x40002,
	set_plane_src(&mock->state, 0x40002, 0x40002,
		      0x40002 + (1024 << 16), 0x40002 + (768 << 16));
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 1, "Should return plane src as damage.");
	FAIL_ON(!check_damage_clip(&state, &clip, 4, 4, 1029, 773));

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return plane src as damage.");
	check_damage_clip(test, &clip, 4, 4, 1029, 773);
}

int igt_damage_iter_no_damage_not_visible(void *ignored)
static void igt_damage_iter_no_damage_not_visible(struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	state.visible = false;
	mock->state.visible = false;

	mock_setup(&old_state);

	set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 0, "Should have no damage.");

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should have no damage.");
}

int igt_damage_iter_no_damage_no_crtc(void *ignored)
static void igt_damage_iter_no_damage_no_crtc(struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	state.crtc = NULL;
	mock->state.crtc = NULL;

	set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 0, "Should have no damage.");

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should have no damage.");
}

int igt_damage_iter_no_damage_no_fb(void *ignored)
static void igt_damage_iter_no_damage_no_fb(struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_plane_state old_state;
	struct drm_rect clip;
	uint32_t num_hits = 0;

	struct drm_plane_state state = {
		.crtc = ZERO_SIZE_PTR,
		.fb = 0,
	};

	mock_setup(&old_state);
	mock->state.fb = NULL;

	set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 0, "Should have no damage.");

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should have no damage.");
}

int igt_damage_iter_simple_damage(void *ignored)
static void igt_damage_iter_simple_damage(struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_property_blob damage_blob;
	struct drm_mode_rect damage;
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
	/* Damage set to plane src */
	set_damage_clip(&damage, 0, 0, 1024, 768);
	set_damage_blob(&damage_blob, &damage, sizeof(damage));
	set_plane_damage(&state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	set_plane_damage(&mock->state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 1, "Should return damage when set.");
	FAIL_ON(!check_damage_clip(&state, &clip, 0, 0, 1024, 768));

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return damage when set.");
	check_damage_clip(test, &clip, 0, 0, 1024, 768);
}

int igt_damage_iter_single_damage(void *ignored)
static void igt_damage_iter_single_damage(struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_property_blob damage_blob;
	struct drm_mode_rect damage;
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
	set_damage_clip(&damage, 256, 192, 768, 576);
	set_damage_blob(&damage_blob, &damage, sizeof(damage));
	set_plane_damage(&state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	set_plane_damage(&mock->state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 1, "Should return damage when set.");
	FAIL_ON(!check_damage_clip(&state, &clip, 256, 192, 768, 576));

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return damage when set.");
	check_damage_clip(test, &clip, 256, 192, 768, 576);
}

int igt_damage_iter_single_damage_intersect_src(void *ignored)
static void igt_damage_iter_single_damage_intersect_src(struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_property_blob damage_blob;
	struct drm_mode_rect damage;
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
	/* Damage intersect with plane src. */
	set_damage_clip(&damage, 256, 192, 1360, 768);
	set_damage_blob(&damage_blob, &damage, sizeof(damage));
	set_plane_damage(&state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	set_plane_damage(&mock->state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 1, "Should return damage clipped to src.");
	FAIL_ON(!check_damage_clip(&state, &clip, 256, 192, 1024, 768));

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return damage clipped to src.");
	check_damage_clip(test, &clip, 256, 192, 1024, 768);
}

int igt_damage_iter_single_damage_outside_src(void *ignored)
static void igt_damage_iter_single_damage_outside_src(struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_property_blob damage_blob;
	struct drm_mode_rect damage;
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
	/* Damage clip outside plane src */
	set_damage_clip(&damage, 1360, 1360, 1380, 1380);
	set_damage_blob(&damage_blob, &damage, sizeof(damage));
	set_plane_damage(&state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	set_plane_damage(&mock->state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 0, "Should have no damage.");

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should have no damage.");
}

int igt_damage_iter_single_damage_fractional_src(void *ignored)
static void igt_damage_iter_single_damage_fractional_src(struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_property_blob damage_blob;
	struct drm_mode_rect damage;
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	/* Plane src has fractional part. */
	set_plane_src(&old_state, 0x40002, 0x40002,
	set_plane_src(&mock->old_state, 0x40002, 0x40002,
		      0x40002 + (1024 << 16), 0x40002 + (768 << 16));
	set_plane_src(&state, 0x40002, 0x40002,
	set_plane_src(&mock->state, 0x40002, 0x40002,
		      0x40002 + (1024 << 16), 0x40002 + (768 << 16));
	set_damage_clip(&damage, 10, 10, 256, 330);
	set_damage_blob(&damage_blob, &damage, sizeof(damage));
	set_plane_damage(&state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	set_plane_damage(&mock->state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 1, "Should return damage when set.");
	FAIL_ON(!check_damage_clip(&state, &clip, 10, 10, 256, 330));

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return damage when set.");
	check_damage_clip(test, &clip, 10, 10, 256, 330);
}

int igt_damage_iter_single_damage_intersect_fractional_src(void *ignored)
static void igt_damage_iter_single_damage_intersect_fractional_src(
		struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_property_blob damage_blob;
	struct drm_mode_rect damage;
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	/* Plane src has fractional part. */
	set_plane_src(&old_state, 0x40002, 0x40002,
	set_plane_src(&mock->old_state, 0x40002, 0x40002,
		      0x40002 + (1024 << 16), 0x40002 + (768 << 16));
	set_plane_src(&state, 0x40002, 0x40002,
	set_plane_src(&mock->state, 0x40002, 0x40002,
		      0x40002 + (1024 << 16), 0x40002 + (768 << 16));
	/* Damage intersect with plane src. */
	set_damage_clip(&damage, 10, 1, 1360, 330);
	set_damage_blob(&damage_blob, &damage, sizeof(damage));
	set_plane_damage(&state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	set_plane_damage(&mock->state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 1, "Should return damage clipped to rounded off src.");
	FAIL_ON(!check_damage_clip(&state, &clip, 10, 4, 1029, 330));

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 1,
			"Should return damage clipped to rounded off src.");
	check_damage_clip(test, &clip, 10, 4, 1029, 330);
}

int igt_damage_iter_single_damage_outside_fractional_src(void *ignored)
static void igt_damage_iter_single_damage_outside_fractional_src(
		struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_property_blob damage_blob;
	struct drm_mode_rect damage;
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	/* Plane src has fractional part. */
	set_plane_src(&old_state, 0x40002, 0x40002,
	set_plane_src(&mock->old_state, 0x40002, 0x40002,
		      0x40002 + (1024 << 16), 0x40002 + (768 << 16));
	set_plane_src(&state, 0x40002, 0x40002,
	set_plane_src(&mock->state, 0x40002, 0x40002,
		      0x40002 + (1024 << 16), 0x40002 + (768 << 16));
	/* Damage clip outside plane src */
	set_damage_clip(&damage, 1360, 1360, 1380, 1380);
	set_damage_blob(&damage_blob, &damage, sizeof(damage));
	set_plane_damage(&state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	set_plane_damage(&mock->state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 0, "Should have no damage.");

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should have no damage.");
}

int igt_damage_iter_single_damage_src_moved(void *ignored)
static void igt_damage_iter_single_damage_src_moved(struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_property_blob damage_blob;
	struct drm_mode_rect damage;
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	/* Plane src moved since old plane state. */
	set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&state, 10 << 16, 10 << 16,
	set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&mock->state, 10 << 16, 10 << 16,
		      (10 + 1024) << 16, (10 + 768) << 16);
	set_damage_clip(&damage, 20, 30, 256, 256);
	set_damage_blob(&damage_blob, &damage, sizeof(damage));
	set_plane_damage(&state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	set_plane_damage(&mock->state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 1, "Should return plane src as damage.");
	FAIL_ON(!check_damage_clip(&state, &clip, 10, 10, 1034, 778));

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 1,
			"Should return plane src as damage.");
	check_damage_clip(test, &clip, 10, 10, 1034, 778);
}

int igt_damage_iter_single_damage_fractional_src_moved(void *ignored)
static void igt_damage_iter_single_damage_fractional_src_moved(
		struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_property_blob damage_blob;
	struct drm_mode_rect damage;
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	/* Plane src with fractional part moved since old plane state. */
	set_plane_src(&old_state, 0x3fffe, 0x3fffe,
	set_plane_src(&mock->old_state, 0x3fffe, 0x3fffe,
		      0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
	set_plane_src(&state, 0x40002, 0x40002,
	set_plane_src(&mock->state, 0x40002, 0x40002,
		      0x40002 + (1024 << 16), 0x40002 + (768 << 16));
	/* Damage intersect with plane src. */
	set_damage_clip(&damage, 20, 30, 1360, 256);
	set_damage_blob(&damage_blob, &damage, sizeof(damage));
	set_plane_damage(&state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	set_plane_damage(&mock->state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 1, "Should return rounded off plane src as damage.");
	FAIL_ON(!check_damage_clip(&state, &clip, 4, 4, 1029, 773));

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 1,
			"Should return rounded off plane as damage.");
	check_damage_clip(test, &clip, 4, 4, 1029, 773);
}

int igt_damage_iter_damage(void *ignored)
static void igt_damage_iter_damage(struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_property_blob damage_blob;
	struct drm_mode_rect damage[2];
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
	/* 2 damage clips. */
	set_damage_clip(&damage[0], 20, 30, 200, 180);
	set_damage_clip(&damage[1], 240, 200, 280, 250);
	set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
	set_plane_damage(&state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	set_plane_damage(&mock->state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip) {
		if (num_hits == 0)
			FAIL_ON(!check_damage_clip(&state, &clip, 20, 30, 200, 180));
			check_damage_clip(test, &clip, 20, 30, 200, 180);
		if (num_hits == 1)
			FAIL_ON(!check_damage_clip(&state, &clip, 240, 200, 280, 250));
			check_damage_clip(test, &clip, 240, 200, 280, 250);
		num_hits++;
	}

	FAIL(num_hits != 2, "Should return damage when set.");

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 2, "Should return damage when set.");
}

int igt_damage_iter_damage_one_intersect(void *ignored)
static void igt_damage_iter_damage_one_intersect(struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_property_blob damage_blob;
	struct drm_mode_rect damage[2];
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	set_plane_src(&old_state, 0x40002, 0x40002,
	set_plane_src(&mock->old_state, 0x40002, 0x40002,
		      0x40002 + (1024 << 16), 0x40002 + (768 << 16));
	set_plane_src(&state, 0x40002, 0x40002,
	set_plane_src(&mock->state, 0x40002, 0x40002,
		      0x40002 + (1024 << 16), 0x40002 + (768 << 16));
	/* 2 damage clips, one intersect plane src. */
	set_damage_clip(&damage[0], 20, 30, 200, 180);
	set_damage_clip(&damage[1], 2, 2, 1360, 1360);
	set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
	set_plane_damage(&state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	set_plane_damage(&mock->state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip) {
		if (num_hits == 0)
			FAIL_ON(!check_damage_clip(&state, &clip, 20, 30, 200, 180));
			check_damage_clip(test, &clip, 20, 30, 200, 180);
		if (num_hits == 1)
			FAIL_ON(!check_damage_clip(&state, &clip, 4, 4, 1029, 773));
			check_damage_clip(test, &clip, 4, 4, 1029, 773);
		num_hits++;
	}

	FAIL(num_hits != 2, "Should return damage when set.");

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 2, "Should return damage when set.");
}

int igt_damage_iter_damage_one_outside(void *ignored)
static void igt_damage_iter_damage_one_outside(struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_property_blob damage_blob;
	struct drm_mode_rect damage[2];
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
	set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
	/* 2 damage clips, one outside plane src. */
	set_damage_clip(&damage[0], 1360, 1360, 1380, 1380);
	set_damage_clip(&damage[1], 240, 200, 280, 250);
	set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
	set_plane_damage(&state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	set_plane_damage(&mock->state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 1, "Should return damage when set.");
	FAIL_ON(!check_damage_clip(&state, &clip, 240, 200, 280, 250));

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return damage when set.");
	check_damage_clip(test, &clip, 240, 200, 280, 250);
}

int igt_damage_iter_damage_src_moved(void *ignored)
static void igt_damage_iter_damage_src_moved(struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_property_blob damage_blob;
	struct drm_mode_rect damage[2];
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	set_plane_src(&old_state, 0x40002, 0x40002,
	set_plane_src(&mock->old_state, 0x40002, 0x40002,
		      0x40002 + (1024 << 16), 0x40002 + (768 << 16));
	set_plane_src(&state, 0x3fffe, 0x3fffe,
	set_plane_src(&mock->state, 0x3fffe, 0x3fffe,
		      0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
	/* 2 damage clips, one outside plane src. */
	set_damage_clip(&damage[0], 1360, 1360, 1380, 1380);
	set_damage_clip(&damage[1], 240, 200, 280, 250);
	set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
	set_plane_damage(&state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	set_plane_damage(&mock->state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 1, "Should return round off plane src as damage.");
	FAIL_ON(!check_damage_clip(&state, &clip, 3, 3, 1028, 772));

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 1,
			"Should return round off plane src as damage.");
	check_damage_clip(test, &clip, 3, 3, 1028, 772);
}

int igt_damage_iter_damage_not_visible(void *ignored)
static void igt_damage_iter_damage_not_visible(struct kunit *test)
{
	struct drm_damage_mock *mock = test->priv;
	struct drm_atomic_helper_damage_iter iter;
	struct drm_property_blob damage_blob;
	struct drm_mode_rect damage[2];
	struct drm_rect clip;
	uint32_t num_hits = 0;

	MOCK_VARIABLES();

	state.visible = false;
	mock->state.visible = false;

	set_plane_src(&old_state, 0x40002, 0x40002,
	set_plane_src(&mock->old_state, 0x40002, 0x40002,
		      0x40002 + (1024 << 16), 0x40002 + (768 << 16));
	set_plane_src(&state, 0x3fffe, 0x3fffe,
	set_plane_src(&mock->state, 0x3fffe, 0x3fffe,
		      0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
	/* 2 damage clips, one outside plane src. */
	set_damage_clip(&damage[0], 1360, 1360, 1380, 1380);
	set_damage_clip(&damage[1], 240, 200, 280, 250);
	set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
	set_plane_damage(&state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &old_state, &state);
	set_plane_damage(&mock->state, &damage_blob);
	drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
	drm_atomic_for_each_plane_damage(&iter, &clip)
		num_hits++;

	FAIL(num_hits != 0, "Should not return any damage.");

	return 0;
	KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should not return any damage.");
}

static struct kunit_case drm_damage_helper_tests[] = {
	KUNIT_CASE(igt_damage_iter_no_damage),
	KUNIT_CASE(igt_damage_iter_no_damage_fractional_src),
	KUNIT_CASE(igt_damage_iter_no_damage_src_moved),
	KUNIT_CASE(igt_damage_iter_no_damage_fractional_src_moved),
	KUNIT_CASE(igt_damage_iter_no_damage_not_visible),
	KUNIT_CASE(igt_damage_iter_no_damage_no_crtc),
	KUNIT_CASE(igt_damage_iter_no_damage_no_fb),
	KUNIT_CASE(igt_damage_iter_simple_damage),
	KUNIT_CASE(igt_damage_iter_single_damage),
	KUNIT_CASE(igt_damage_iter_single_damage_intersect_src),
	KUNIT_CASE(igt_damage_iter_single_damage_outside_src),
	KUNIT_CASE(igt_damage_iter_single_damage_fractional_src),
	KUNIT_CASE(igt_damage_iter_single_damage_intersect_fractional_src),
	KUNIT_CASE(igt_damage_iter_single_damage_outside_fractional_src),
	KUNIT_CASE(igt_damage_iter_single_damage_src_moved),
	KUNIT_CASE(igt_damage_iter_single_damage_fractional_src_moved),
	KUNIT_CASE(igt_damage_iter_damage),
	KUNIT_CASE(igt_damage_iter_damage_one_intersect),
	KUNIT_CASE(igt_damage_iter_damage_one_outside),
	KUNIT_CASE(igt_damage_iter_damage_src_moved),
	KUNIT_CASE(igt_damage_iter_damage_not_visible),
	{ }
};

static struct kunit_suite drm_damage_helper_test_suite = {
	.name = "drm_damage_helper_tests",
	.init = drm_damage_helper_init,
	.test_cases = drm_damage_helper_tests,
};

kunit_test_suite(drm_damage_helper_test_suite);

MODULE_LICENSE("GPL");
-- 
2.34.1