| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Kunit test for drm_probe_helper functions |
| */ |
| |
| #include <drm/drm_atomic_state_helper.h> |
| #include <drm/drm_connector.h> |
| #include <drm/drm_device.h> |
| #include <drm/drm_drv.h> |
| #include <drm/drm_kunit_helpers.h> |
| #include <drm/drm_mode.h> |
| #include <drm/drm_modes.h> |
| #include <drm/drm_modeset_helper_vtables.h> |
| #include <drm/drm_probe_helper.h> |
| |
| #include <kunit/test.h> |
| |
| struct drm_probe_helper_test_priv { |
| struct drm_device *drm; |
| struct device *dev; |
| struct drm_connector connector; |
| }; |
| |
| static const struct drm_connector_helper_funcs drm_probe_helper_connector_helper_funcs = { |
| }; |
| |
| static const struct drm_connector_funcs drm_probe_helper_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_probe_helper_test_init(struct kunit *test) |
| { |
| struct drm_probe_helper_test_priv *priv; |
| struct drm_connector *connector; |
| int ret; |
| |
| priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); |
| KUNIT_ASSERT_NOT_NULL(test, priv); |
| test->priv = priv; |
| |
| priv->dev = drm_kunit_helper_alloc_device(test); |
| KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev); |
| |
| priv->drm = __drm_kunit_helper_alloc_drm_device(test, priv->dev, |
| sizeof(*priv->drm), 0, |
| DRIVER_MODESET | DRIVER_ATOMIC); |
| KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm); |
| |
| connector = &priv->connector; |
| ret = drmm_connector_init(priv->drm, connector, |
| &drm_probe_helper_connector_funcs, |
| DRM_MODE_CONNECTOR_Unknown, |
| NULL); |
| KUNIT_ASSERT_EQ(test, ret, 0); |
| |
| drm_connector_helper_add(connector, &drm_probe_helper_connector_helper_funcs); |
| |
| return 0; |
| } |
| |
| typedef struct drm_display_mode *(*expected_mode_func_t)(struct drm_device *); |
| |
| struct drm_connector_helper_tv_get_modes_test { |
| const char *name; |
| unsigned int supported_tv_modes; |
| enum drm_connector_tv_mode default_mode; |
| bool cmdline; |
| enum drm_connector_tv_mode cmdline_mode; |
| expected_mode_func_t *expected_modes; |
| unsigned int num_expected_modes; |
| }; |
| |
| #define _TV_MODE_TEST(_name, _supported, _default, _cmdline, _cmdline_mode, ...) \ |
| { \ |
| .name = _name, \ |
| .supported_tv_modes = _supported, \ |
| .default_mode = _default, \ |
| .cmdline = _cmdline, \ |
| .cmdline_mode = _cmdline_mode, \ |
| .expected_modes = (expected_mode_func_t[]) { __VA_ARGS__ }, \ |
| .num_expected_modes = sizeof((expected_mode_func_t[]) { __VA_ARGS__ }) / \ |
| (sizeof(expected_mode_func_t)), \ |
| } |
| |
| #define TV_MODE_TEST(_name, _supported, _default, ...) \ |
| _TV_MODE_TEST(_name, _supported, _default, false, 0, __VA_ARGS__) |
| |
| #define TV_MODE_TEST_CMDLINE(_name, _supported, _default, _cmdline, ...) \ |
| _TV_MODE_TEST(_name, _supported, _default, true, _cmdline, __VA_ARGS__) |
| |
| static void |
| drm_test_connector_helper_tv_get_modes_check(struct kunit *test) |
| { |
| const struct drm_connector_helper_tv_get_modes_test *params = test->param_value; |
| struct drm_probe_helper_test_priv *priv = test->priv; |
| struct drm_connector *connector = &priv->connector; |
| struct drm_cmdline_mode *cmdline = &connector->cmdline_mode; |
| struct drm_display_mode *mode; |
| const struct drm_display_mode *expected; |
| size_t len; |
| int ret; |
| |
| if (params->cmdline) { |
| cmdline->tv_mode_specified = true; |
| cmdline->tv_mode = params->cmdline_mode; |
| } |
| |
| ret = drm_mode_create_tv_properties(priv->drm, params->supported_tv_modes); |
| KUNIT_ASSERT_EQ(test, ret, 0); |
| |
| drm_object_attach_property(&connector->base, |
| priv->drm->mode_config.tv_mode_property, |
| params->default_mode); |
| |
| mutex_lock(&priv->drm->mode_config.mutex); |
| |
| ret = drm_connector_helper_tv_get_modes(connector); |
| KUNIT_EXPECT_EQ(test, ret, params->num_expected_modes); |
| |
| len = 0; |
| list_for_each_entry(mode, &connector->probed_modes, head) |
| len++; |
| KUNIT_EXPECT_EQ(test, len, params->num_expected_modes); |
| |
| if (params->num_expected_modes >= 1) { |
| mode = list_first_entry_or_null(&connector->probed_modes, |
| struct drm_display_mode, head); |
| KUNIT_ASSERT_NOT_NULL(test, mode); |
| |
| expected = params->expected_modes[0](priv->drm); |
| KUNIT_ASSERT_NOT_NULL(test, expected); |
| |
| KUNIT_EXPECT_TRUE(test, drm_mode_equal(mode, expected)); |
| KUNIT_EXPECT_TRUE(test, mode->type & DRM_MODE_TYPE_PREFERRED); |
| } |
| |
| if (params->num_expected_modes >= 2) { |
| mode = list_next_entry(mode, head); |
| KUNIT_ASSERT_NOT_NULL(test, mode); |
| |
| expected = params->expected_modes[1](priv->drm); |
| KUNIT_ASSERT_NOT_NULL(test, expected); |
| |
| KUNIT_EXPECT_TRUE(test, drm_mode_equal(mode, expected)); |
| KUNIT_EXPECT_FALSE(test, mode->type & DRM_MODE_TYPE_PREFERRED); |
| } |
| |
| mutex_unlock(&priv->drm->mode_config.mutex); |
| } |
| |
| static const |
| struct drm_connector_helper_tv_get_modes_test drm_connector_helper_tv_get_modes_tests[] = { |
| { .name = "None" }, |
| TV_MODE_TEST("PAL", |
| BIT(DRM_MODE_TV_MODE_PAL), |
| DRM_MODE_TV_MODE_PAL, |
| drm_mode_analog_pal_576i), |
| TV_MODE_TEST("NTSC", |
| BIT(DRM_MODE_TV_MODE_NTSC), |
| DRM_MODE_TV_MODE_NTSC, |
| drm_mode_analog_ntsc_480i), |
| TV_MODE_TEST("Both, NTSC Default", |
| BIT(DRM_MODE_TV_MODE_NTSC) | BIT(DRM_MODE_TV_MODE_PAL), |
| DRM_MODE_TV_MODE_NTSC, |
| drm_mode_analog_ntsc_480i, drm_mode_analog_pal_576i), |
| TV_MODE_TEST("Both, PAL Default", |
| BIT(DRM_MODE_TV_MODE_NTSC) | BIT(DRM_MODE_TV_MODE_PAL), |
| DRM_MODE_TV_MODE_PAL, |
| drm_mode_analog_pal_576i, drm_mode_analog_ntsc_480i), |
| TV_MODE_TEST_CMDLINE("Both, NTSC Default, with PAL on command-line", |
| BIT(DRM_MODE_TV_MODE_NTSC) | BIT(DRM_MODE_TV_MODE_PAL), |
| DRM_MODE_TV_MODE_NTSC, |
| DRM_MODE_TV_MODE_PAL, |
| drm_mode_analog_pal_576i, drm_mode_analog_ntsc_480i), |
| TV_MODE_TEST_CMDLINE("Both, PAL Default, with NTSC on command-line", |
| BIT(DRM_MODE_TV_MODE_NTSC) | BIT(DRM_MODE_TV_MODE_PAL), |
| DRM_MODE_TV_MODE_PAL, |
| DRM_MODE_TV_MODE_NTSC, |
| drm_mode_analog_ntsc_480i, drm_mode_analog_pal_576i), |
| }; |
| |
| static void |
| drm_connector_helper_tv_get_modes_desc(const struct drm_connector_helper_tv_get_modes_test *t, |
| char *desc) |
| { |
| sprintf(desc, "%s", t->name); |
| } |
| |
| KUNIT_ARRAY_PARAM(drm_connector_helper_tv_get_modes, |
| drm_connector_helper_tv_get_modes_tests, |
| drm_connector_helper_tv_get_modes_desc); |
| |
| static struct kunit_case drm_test_connector_helper_tv_get_modes_tests[] = { |
| KUNIT_CASE_PARAM(drm_test_connector_helper_tv_get_modes_check, |
| drm_connector_helper_tv_get_modes_gen_params), |
| { } |
| }; |
| |
| static struct kunit_suite drm_test_connector_helper_tv_get_modes_suite = { |
| .name = "drm_connector_helper_tv_get_modes", |
| .init = drm_probe_helper_test_init, |
| .test_cases = drm_test_connector_helper_tv_get_modes_tests, |
| }; |
| |
| kunit_test_suite(drm_test_connector_helper_tv_get_modes_suite); |
| |
| MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>"); |
| MODULE_LICENSE("GPL"); |