409 lines · 10.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Test cases for the drm_atomic_commit helpers * * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <drm/drm_atomic.h>#include <drm/drm_atomic_helper.h>#include <drm/drm_atomic_uapi.h>#include <drm/drm_kunit_helpers.h>#include <drm/drm_probe_helper.h> #define DRM_TEST_ENC_0 BIT(0)#define DRM_TEST_ENC_1 BIT(1)#define DRM_TEST_ENC_2 BIT(2) #define DRM_TEST_CONN_0 BIT(0) struct drm_clone_mode_test {	const char *name;	u32 encoder_mask;	int expected_result;}; static const struct drm_display_mode drm_atomic_test_mode = {	DRM_MODE("1024x768", 0, 65000, 1024, 1048,		 1184, 1344, 0, 768, 771, 777, 806, 0,		 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)}; struct drm_atomic_test_priv {	struct drm_device drm;	struct drm_plane *plane;	struct drm_crtc *crtc;	struct drm_encoder encoders[3];	struct drm_connector connectors[2];}; static int modeset_counter; static void drm_test_encoder_mode_set(struct drm_encoder *encoder,				      struct drm_crtc_state *crtc_state,				      struct drm_connector_state *conn_state){	modeset_counter++;} static const struct drm_encoder_helper_funcs drm_atomic_test_encoder_funcs = {	.atomic_mode_set	= drm_test_encoder_mode_set,}; static const struct drm_connector_funcs dummy_connector_funcs = {	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,	.atomic_duplicate_state	= drm_atomic_helper_connector_duplicate_state,	.reset			= drm_atomic_helper_connector_reset,}; static int drm_atomic_test_dummy_get_modes(struct drm_connector *connector){	return drm_connector_helper_get_modes_fixed(connector,						    &drm_atomic_test_mode);} static const struct drm_connector_helper_funcs dummy_connector_helper_funcs = {	.get_modes	= drm_atomic_test_dummy_get_modes,}; static struct drm_atomic_test_priv *drm_atomic_test_init_drm_components(struct kunit *test, bool has_connectors){	struct drm_atomic_test_priv *priv;	struct drm_encoder *enc;	struct drm_connector *conn;	struct drm_device *drm;	struct device *dev;	int ret; 	dev = drm_kunit_helper_alloc_device(test);	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); 	priv = drm_kunit_helper_alloc_drm_device(test, dev,						 struct drm_atomic_test_priv,						 drm,						 DRIVER_MODESET | DRIVER_ATOMIC);	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);	test->priv = priv; 	drm = &priv->drm;	priv->plane = drm_kunit_helper_create_primary_plane(test, drm,							    NULL,							    NULL,							    NULL, 0,							    NULL);	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->plane); 	priv->crtc = drm_kunit_helper_create_crtc(test, drm,						  priv->plane, NULL,						  NULL,						  NULL);	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->crtc); 	for (int i = 0; i < ARRAY_SIZE(priv->encoders); i++) {		enc = &priv->encoders[i]; 		ret = drmm_encoder_init(drm, enc, NULL,					DRM_MODE_ENCODER_DSI, NULL);		KUNIT_ASSERT_EQ(test, ret, 0); 		enc->possible_crtcs = drm_crtc_mask(priv->crtc);	} 	priv->encoders[0].possible_clones = DRM_TEST_ENC_0 | DRM_TEST_ENC_1;	priv->encoders[1].possible_clones = DRM_TEST_ENC_0 | DRM_TEST_ENC_1;	priv->encoders[2].possible_clones = DRM_TEST_ENC_2; 	if (!has_connectors)		goto done; 	BUILD_BUG_ON(ARRAY_SIZE(priv->connectors) > ARRAY_SIZE(priv->encoders)); 	for (int i = 0; i < ARRAY_SIZE(priv->connectors); i++) {		conn = &priv->connectors[i]; 		ret = drmm_connector_init(drm, conn, &dummy_connector_funcs,					  DRM_MODE_CONNECTOR_DSI, NULL);		KUNIT_ASSERT_EQ(test, ret, 0); 		drm_connector_helper_add(conn, &dummy_connector_helper_funcs);		drm_encoder_helper_add(&priv->encoders[i],				       &drm_atomic_test_encoder_funcs); 		drm_connector_attach_encoder(conn, &priv->encoders[i]);	} done:	drm_mode_config_reset(drm); 	return priv;} static int set_up_atomic_state(struct kunit *test,			       struct drm_atomic_test_priv *priv,			       struct drm_connector *connector,			       struct drm_modeset_acquire_ctx *ctx){	struct drm_device *drm = &priv->drm;	struct drm_crtc *crtc = priv->crtc;	struct drm_atomic_commit *state;	struct drm_connector_state *conn_state;	struct drm_crtc_state *crtc_state;	int ret; 	state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); 	if (connector) {		conn_state = drm_atomic_get_connector_state(state, connector);		if (IS_ERR(conn_state))			return PTR_ERR(conn_state); 		ret = drm_atomic_set_crtc_for_connector(conn_state, crtc);		if (ret)			return ret;	} 	crtc_state = drm_atomic_get_crtc_state(state, crtc);	if (IS_ERR(crtc_state))		return PTR_ERR(crtc_state); 	ret = drm_atomic_set_mode_for_crtc(crtc_state, &drm_atomic_test_mode);	if (ret)		return ret; 	crtc_state->enable = true;	crtc_state->active = true; 	if (connector) {		ret = drm_atomic_commit(state);		if (ret)			return ret;	} else {		// dummy connector mask		crtc_state->connector_mask = DRM_TEST_CONN_0;	} 	return 0;} /* * Test that the DRM encoder mode_set() is called when the atomic state * connectors are changed but the CRTC mode is not. */static void drm_test_check_connector_changed_modeset(struct kunit *test){	struct drm_atomic_test_priv *priv;	struct drm_modeset_acquire_ctx ctx;	struct drm_connector *old_conn, *new_conn;	struct drm_atomic_commit *state;	struct drm_device *drm;	struct drm_connector_state *new_conn_state, *old_conn_state;	int ret, initial_modeset_count; 	priv = drm_atomic_test_init_drm_components(test, true);	KUNIT_ASSERT_NOT_NULL(test, priv); 	drm = &priv->drm;	old_conn = &priv->connectors[0];	new_conn = &priv->connectors[1]; 	drm_modeset_acquire_init(&ctx, 0); 	// first modeset to enableretry_set_up:	ret = set_up_atomic_state(test, priv, old_conn, &ctx);	if (ret == -EDEADLK) {		ret = drm_modeset_backoff(&ctx);		if (!ret)			goto retry_set_up;	}	KUNIT_ASSERT_EQ(test, ret, 0); 	state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); 	new_conn_state = drm_atomic_get_connector_state(state, new_conn);	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state); 	old_conn_state = drm_atomic_get_connector_state(state, old_conn);	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state); 	ret = drm_atomic_set_crtc_for_connector(old_conn_state, NULL);	KUNIT_EXPECT_EQ(test, ret, 0); 	ret = drm_atomic_set_crtc_for_connector(new_conn_state, priv->crtc);	KUNIT_EXPECT_EQ(test, ret, 0); 	initial_modeset_count = modeset_counter; 	// modeset_disables is called as part of the atomic commit tail	ret = drm_atomic_commit(state);	KUNIT_ASSERT_EQ(test, ret, 0);	KUNIT_ASSERT_EQ(test, modeset_counter, initial_modeset_count + 1); 	drm_modeset_drop_locks(&ctx);	drm_modeset_acquire_fini(&ctx);} /* * Test that the drm_crtc_in_clone_mode() helper can detect if a given CRTC * state is in clone mode */static void drm_test_check_in_clone_mode(struct kunit *test){	bool ret;	const struct drm_clone_mode_test *param = test->param_value;	struct drm_crtc_state *crtc_state; 	crtc_state = kunit_kzalloc(test, sizeof(*crtc_state), GFP_KERNEL);	KUNIT_ASSERT_NOT_NULL(test, crtc_state); 	crtc_state->encoder_mask = param->encoder_mask; 	ret = drm_crtc_in_clone_mode(crtc_state); 	KUNIT_ASSERT_EQ(test, ret, param->expected_result);} /* * Test that the atomic commit path will succeed for valid clones (or non-cloned * states) and fail for states where the cloned encoders are not possible_clones * of each other. */static void drm_test_check_valid_clones(struct kunit *test){	int ret;	const struct drm_clone_mode_test *param = test->param_value;	struct drm_atomic_test_priv *priv;	struct drm_modeset_acquire_ctx ctx;	struct drm_device *drm;	struct drm_atomic_commit *state;	struct drm_crtc_state *crtc_state; 	priv = drm_atomic_test_init_drm_components(test, false);	KUNIT_ASSERT_NOT_NULL(test, priv); 	drm = &priv->drm; 	drm_modeset_acquire_init(&ctx, 0); retry_set_up:	ret = set_up_atomic_state(test, priv, NULL, &ctx);	if (ret == -EDEADLK) {		ret = drm_modeset_backoff(&ctx);		if (!ret)			goto retry_set_up;	}	KUNIT_ASSERT_EQ(test, ret, 0); 	state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); retry:	crtc_state = drm_atomic_get_crtc_state(state, priv->crtc);	if (PTR_ERR(crtc_state) == -EDEADLK) {		drm_atomic_commit_clear(state);		ret = drm_modeset_backoff(&ctx);		if (!ret)			goto retry;	}	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); 	crtc_state->encoder_mask = param->encoder_mask; 	// force modeset	crtc_state->mode_changed = true; 	ret = drm_atomic_helper_check_modeset(drm, state);	if (ret == -EDEADLK) {		drm_atomic_commit_clear(state);		ret = drm_modeset_backoff(&ctx);		if (!ret)			goto retry;	}	KUNIT_ASSERT_EQ(test, ret, param->expected_result); 	drm_modeset_drop_locks(&ctx);	drm_modeset_acquire_fini(&ctx);} static void drm_check_in_clone_mode_desc(const struct drm_clone_mode_test *t,				      char *desc){	sprintf(desc, "%s", t->name);} static void drm_check_valid_clones_desc(const struct drm_clone_mode_test *t,				      char *desc){	sprintf(desc, "%s", t->name);} static const struct drm_clone_mode_test drm_clone_mode_tests[] = {	{		.name = "in_clone_mode",		.encoder_mask = DRM_TEST_ENC_0 | DRM_TEST_ENC_1,		.expected_result = true,	},	{		.name = "not_in_clone_mode",		.encoder_mask = DRM_TEST_ENC_0,		.expected_result = false,	},}; static const struct drm_clone_mode_test drm_valid_clone_mode_tests[] = {	{		.name = "not_in_clone_mode",		.encoder_mask = DRM_TEST_ENC_0,		.expected_result = 0,	}, 	{		.name = "valid_clone",		.encoder_mask = DRM_TEST_ENC_0 | DRM_TEST_ENC_1,		.expected_result = 0,	},	{		.name = "invalid_clone",		.encoder_mask = DRM_TEST_ENC_0 | DRM_TEST_ENC_2,		.expected_result = -EINVAL,	},}; KUNIT_ARRAY_PARAM(drm_check_in_clone_mode, drm_clone_mode_tests,		  drm_check_in_clone_mode_desc); KUNIT_ARRAY_PARAM(drm_check_valid_clones, drm_valid_clone_mode_tests,		  drm_check_valid_clones_desc); static struct kunit_case drm_test_check_modeset_test[] = {	KUNIT_CASE(drm_test_check_connector_changed_modeset),	{}}; static struct kunit_case drm_in_clone_mode_check_test[] = {	KUNIT_CASE_PARAM(drm_test_check_in_clone_mode,			 drm_check_in_clone_mode_gen_params),	KUNIT_CASE_PARAM(drm_test_check_valid_clones,			 drm_check_valid_clones_gen_params),	{}}; static struct kunit_suite drm_test_check_modeset_test_suite = {	.name = "drm_validate_modeset",	.test_cases = drm_test_check_modeset_test,}; static struct kunit_suite drm_in_clone_mode_check_test_suite = {	.name = "drm_validate_clone_mode",	.test_cases = drm_in_clone_mode_check_test,}; kunit_test_suites(&drm_in_clone_mode_check_test_suite,		  &drm_test_check_modeset_test_suite); MODULE_AUTHOR("Jessica Zhang <quic_jesszhan@quicinc.com");MODULE_DESCRIPTION("Test cases for the drm_atomic_helper functions");MODULE_LICENSE("GPL");