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 <[email protected]>
---
 .../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

Reply via email to