[PATCH 2/2] drm/tests: Add KUnit tests for the client mode size limits
From: Hugo Sales
Date: Thu Oct 15 2026 - 17:26:30 EST
Test that the first-mode, cmdline, tiled and clone pickers skip modes
larger than the client's size, and that drm_client_modeset_probe() keeps
the preferred mode in connector->modes when the client is smaller than
it
The probe test calls drm_client_modeset_create() directly, because
drm_client_init() needs a dumb_create callback the test driver doesn't
have
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Hugo Sales <hugo@xxxxxxx>
---
.../gpu/drm/tests/drm_client_modeset_test.c | 218 +++++++++++++++++-
1 file changed, 217 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c b/drivers/gpu/drm/tests/drm_client_modeset_test.c
index 00f43fca9795..93aa919d4f31 100644
--- a/drivers/gpu/drm/tests/drm_client_modeset_test.c
+++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c
@@ -18,6 +18,8 @@ struct drm_client_modeset_test_priv {
struct drm_device *drm;
struct device *dev;
struct drm_connector connector;
+ struct drm_connector second_connector;
+ struct drm_encoder encoder;
};
static int drm_client_modeset_connector_get_modes(struct drm_connector *connector)
@@ -48,9 +50,23 @@ static const struct drm_connector_helper_funcs drm_client_modeset_connector_help
.get_modes = drm_client_modeset_connector_get_modes,
};
+static int drm_client_modeset_connector_get_modes_preferred(struct drm_connector *connector)
+{
+ int count = drm_add_modes_noedid(connector, 1920, 1080);
+
+ drm_set_preferred_mode(connector, 1920, 1080);
+
+ return count;
+}
+
+static const struct drm_connector_helper_funcs drm_client_modeset_preferred_helper_funcs = {
+ .get_modes = drm_client_modeset_connector_get_modes_preferred,
+};
+
static const struct drm_connector_funcs drm_client_modeset_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .fill_modes = drm_helper_probe_single_connector_modes,
};
static int drm_client_modeset_test_init(struct kunit *test)
@@ -197,6 +213,206 @@ static struct kunit_suite drm_test_pick_cmdline_test_suite = {
kunit_test_suite(drm_test_pick_cmdline_test_suite);
+static void drm_test_pick_first_mode_within_limits(struct kunit *test)
+{
+ struct drm_client_modeset_test_priv *priv = test->priv;
+ struct drm_device *drm = priv->drm;
+ struct drm_connector *connector = &priv->connector;
+ const struct drm_display_mode *mode;
+ int ret;
+
+ drm_connector_helper_add(connector, &drm_client_modeset_preferred_helper_funcs);
+
+ mutex_lock(&drm->mode_config.mutex);
+ ret = drm_helper_probe_single_connector_modes(connector, 1920, 1080);
+ mutex_unlock(&drm->mode_config.mutex);
+ KUNIT_ASSERT_GT(test, ret, 0);
+
+ mode = drm_connector_first_mode(connector, 1920, 1080);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+ KUNIT_EXPECT_EQ(test, mode->hdisplay, 1920);
+ KUNIT_EXPECT_EQ(test, mode->vdisplay, 1080);
+
+ KUNIT_ASSERT_NULL(test, drm_connector_preferred_mode(connector, 1024, 768));
+
+ mode = drm_connector_first_mode(connector, 1024, 768);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+ KUNIT_EXPECT_EQ(test, mode->hdisplay, 1024);
+ KUNIT_EXPECT_EQ(test, mode->vdisplay, 768);
+
+ KUNIT_EXPECT_NULL(test, drm_connector_first_mode(connector, 320, 240));
+}
+
+static void drm_test_pick_cmdline_mode_within_limits(struct kunit *test)
+{
+ struct drm_client_modeset_test_priv *priv = test->priv;
+ struct drm_device *drm = priv->drm;
+ struct drm_connector *connector = &priv->connector;
+ struct drm_cmdline_mode *cmdline_mode = &connector->cmdline_mode;
+ const struct drm_display_mode *mode;
+ int ret;
+
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector("1920x1080", connector,
+ cmdline_mode));
+
+ mutex_lock(&drm->mode_config.mutex);
+ ret = drm_helper_probe_single_connector_modes(connector, 1920, 1080);
+ mutex_unlock(&drm->mode_config.mutex);
+ KUNIT_ASSERT_GT(test, ret, 0);
+
+ mode = drm_connector_pick_cmdline_mode(connector, 1920, 1080);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+ KUNIT_EXPECT_EQ(test, mode->hdisplay, 1920);
+ KUNIT_EXPECT_EQ(test, mode->vdisplay, 1080);
+
+ KUNIT_EXPECT_NULL(test, drm_connector_pick_cmdline_mode(connector, 1024, 768));
+}
+
+static void drm_test_pick_tiled_mode_within_limits(struct kunit *test)
+{
+ struct drm_client_modeset_test_priv *priv = test->priv;
+ struct drm_device *drm = priv->drm;
+ struct drm_connector *connector = &priv->connector;
+ const struct drm_display_mode *mode;
+ int ret;
+
+ drm_connector_helper_add(connector, &drm_client_modeset_preferred_helper_funcs);
+ connector->tile_h_size = 1920;
+ connector->tile_v_size = 1080;
+
+ mutex_lock(&drm->mode_config.mutex);
+ ret = drm_helper_probe_single_connector_modes(connector, 1920, 1080);
+ mutex_unlock(&drm->mode_config.mutex);
+ KUNIT_ASSERT_GT(test, ret, 0);
+
+ mode = drm_connector_get_tiled_mode(connector, 1920, 1080);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+ KUNIT_EXPECT_EQ(test, mode->hdisplay, 1920);
+ KUNIT_EXPECT_EQ(test, mode->vdisplay, 1080);
+
+ KUNIT_EXPECT_NULL(test, drm_connector_get_tiled_mode(connector, 1024, 768));
+
+ mode = drm_connector_fallback_non_tiled_mode(connector, 1024, 768);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+ KUNIT_EXPECT_EQ(test, mode->hdisplay, 1024);
+ KUNIT_EXPECT_EQ(test, mode->vdisplay, 768);
+}
+
+static void drm_test_pick_clone_mode_within_limits(struct kunit *test)
+{
+ struct drm_client_modeset_test_priv *priv = test->priv;
+ struct drm_device *drm = priv->drm;
+ struct drm_connector *connectors[] = { &priv->connector, &priv->second_connector };
+ const struct drm_display_mode *modes[ARRAY_SIZE(connectors)] = {};
+ struct drm_client_offset offsets[ARRAY_SIZE(connectors)] = {};
+ bool enabled[ARRAY_SIZE(connectors)] = { true, true };
+ unsigned int i;
+ int ret;
+
+ ret = drmm_connector_init(drm, &priv->second_connector,
+ &drm_client_modeset_connector_funcs,
+ DRM_MODE_CONNECTOR_Unknown,
+ NULL);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ drm_connector_helper_add(&priv->second_connector,
+ &drm_client_modeset_connector_helper_funcs);
+
+ mutex_lock(&drm->mode_config.mutex);
+ for (i = 0; i < ARRAY_SIZE(connectors); i++) {
+ ret = drm_helper_probe_single_connector_modes(connectors[i], 1920, 1080);
+ KUNIT_EXPECT_GT(test, ret, 0);
+ }
+ mutex_unlock(&drm->mode_config.mutex);
+
+ KUNIT_EXPECT_TRUE(test, drm_client_target_cloned(drm, connectors, ARRAY_SIZE(connectors),
+ modes, offsets, enabled, 1024, 768));
+ for (i = 0; i < ARRAY_SIZE(connectors); i++) {
+ KUNIT_ASSERT_NOT_NULL(test, modes[i]);
+ KUNIT_EXPECT_EQ(test, modes[i]->hdisplay, 1024);
+ KUNIT_EXPECT_EQ(test, modes[i]->vdisplay, 768);
+ }
+ modes_destroy(drm, modes, ARRAY_SIZE(connectors));
+
+ KUNIT_EXPECT_FALSE(test, drm_client_target_cloned(drm, connectors, ARRAY_SIZE(connectors),
+ modes, offsets, enabled, 800, 600));
+ modes_destroy(drm, modes, ARRAY_SIZE(connectors));
+}
+
+KUNIT_DEFINE_ACTION_WRAPPER(drm_client_modeset_free_wrapper, drm_client_modeset_free,
+ struct drm_client_dev *);
+
+static void drm_test_probe_keeps_modes_above_limits(struct kunit *test)
+{
+ struct drm_client_modeset_test_priv *priv = test->priv;
+ struct drm_device *drm = priv->drm;
+ struct drm_connector *connector = &priv->connector;
+ const struct drm_display_mode *mode;
+ struct drm_client_dev *client;
+ struct drm_encoder *encoder = &priv->encoder;
+ struct drm_plane *plane;
+ struct drm_crtc *crtc;
+ int ret;
+
+ drm->mode_config.max_width = 4096;
+ drm->mode_config.max_height = 4096;
+
+ plane = drm_kunit_helper_create_primary_plane(test, drm, NULL, NULL, NULL, 0, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane);
+
+ crtc = drm_kunit_helper_create_crtc(test, drm, plane, NULL, NULL, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc);
+
+ ret = drmm_encoder_init(drm, encoder, NULL, DRM_MODE_ENCODER_NONE, NULL);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ encoder->possible_crtcs = drm_crtc_mask(crtc);
+
+ ret = drm_connector_attach_encoder(connector, encoder);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ drm_connector_helper_add(connector, &drm_client_modeset_preferred_helper_funcs);
+
+ client = kunit_kzalloc(test, sizeof(*client), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, client);
+ client->dev = drm;
+
+ ret = drm_client_modeset_create(client);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = kunit_add_action_or_reset(test, drm_client_modeset_free_wrapper, client);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = drm_client_modeset_probe(client, 1024, 768);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ mode = client->modesets[0].mode;
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+ KUNIT_EXPECT_EQ(test, mode->hdisplay, 1024);
+ KUNIT_EXPECT_EQ(test, mode->vdisplay, 768);
+
+ mode = drm_connector_preferred_mode(connector, 1920, 1080);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+ KUNIT_EXPECT_EQ(test, mode->hdisplay, 1920);
+ KUNIT_EXPECT_EQ(test, mode->vdisplay, 1080);
+}
+
+static struct kunit_case drm_test_pick_mode_limits_tests[] = {
+ KUNIT_CASE(drm_test_pick_first_mode_within_limits),
+ KUNIT_CASE(drm_test_pick_cmdline_mode_within_limits),
+ KUNIT_CASE(drm_test_pick_tiled_mode_within_limits),
+ KUNIT_CASE(drm_test_pick_clone_mode_within_limits),
+ KUNIT_CASE(drm_test_probe_keeps_modes_above_limits),
+ {}
+};
+
+static struct kunit_suite drm_test_pick_mode_limits_test_suite = {
+ .name = "drm_test_pick_mode_limits",
+ .init = drm_client_modeset_test_init,
+ .test_cases = drm_test_pick_mode_limits_tests
+};
+
+kunit_test_suite(drm_test_pick_mode_limits_test_suite);
+
/*
* This file is included directly by drm_client_modeset.c so we can't
* use any MODULE_* macro here.
--
2.55.0