diff options
-rw-r--r-- | drivers/gpu/drm/drm_probe_helper.c | 82 | ||||
-rw-r--r-- | drivers/gpu/drm/tests/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/tests/drm_probe_helper_test.c | 205 | ||||
-rw-r--r-- | include/drm/drm_probe_helper.h | 1 |
4 files changed, 289 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index bcd9611dabfd..1ea053cef557 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -1146,3 +1146,85 @@ int drm_connector_helper_get_modes(struct drm_connector *connector) return count; } EXPORT_SYMBOL(drm_connector_helper_get_modes); + +/** + * drm_connector_helper_tv_get_modes - Fills the modes availables to a TV connector + * @connector: The connector + * + * Fills the available modes for a TV connector based on the supported + * TV modes, and the default mode expressed by the kernel command line. + * + * This can be used as the default TV connector helper .get_modes() hook + * if the driver does not need any special processing. + * + * Returns: + * The number of modes added to the connector. + */ +int drm_connector_helper_tv_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_property *tv_mode_property = + dev->mode_config.tv_mode_property; + struct drm_cmdline_mode *cmdline = &connector->cmdline_mode; + unsigned int ntsc_modes = BIT(DRM_MODE_TV_MODE_NTSC) | + BIT(DRM_MODE_TV_MODE_NTSC_443) | + BIT(DRM_MODE_TV_MODE_NTSC_J) | + BIT(DRM_MODE_TV_MODE_PAL_M); + unsigned int pal_modes = BIT(DRM_MODE_TV_MODE_PAL) | + BIT(DRM_MODE_TV_MODE_PAL_N) | + BIT(DRM_MODE_TV_MODE_SECAM); + unsigned int tv_modes[2] = { UINT_MAX, UINT_MAX }; + unsigned int i, supported_tv_modes = 0; + + if (!tv_mode_property) + return 0; + + for (i = 0; i < tv_mode_property->num_values; i++) + supported_tv_modes |= BIT(tv_mode_property->values[i]); + + if ((supported_tv_modes & ntsc_modes) && + (supported_tv_modes & pal_modes)) { + uint64_t default_mode; + + if (drm_object_property_get_default_value(&connector->base, + tv_mode_property, + &default_mode)) + return 0; + + if (cmdline->tv_mode_specified) + default_mode = cmdline->tv_mode; + + if (BIT(default_mode) & ntsc_modes) { + tv_modes[0] = DRM_MODE_TV_MODE_NTSC; + tv_modes[1] = DRM_MODE_TV_MODE_PAL; + } else { + tv_modes[0] = DRM_MODE_TV_MODE_PAL; + tv_modes[1] = DRM_MODE_TV_MODE_NTSC; + } + } else if (supported_tv_modes & ntsc_modes) { + tv_modes[0] = DRM_MODE_TV_MODE_NTSC; + } else if (supported_tv_modes & pal_modes) { + tv_modes[0] = DRM_MODE_TV_MODE_PAL; + } else { + return 0; + } + + for (i = 0; i < ARRAY_SIZE(tv_modes); i++) { + struct drm_display_mode *mode; + + if (tv_modes[i] == DRM_MODE_TV_MODE_NTSC) + mode = drm_mode_analog_ntsc_480i(dev); + else if (tv_modes[i] == DRM_MODE_TV_MODE_PAL) + mode = drm_mode_analog_pal_576i(dev); + else + break; + if (!mode) + return i; + if (!i) + mode->type |= DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(connector, mode); + } + + return i; +} +EXPORT_SYMBOL(drm_connector_helper_tv_get_modes); diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index c7903c112c65..94fe546d937d 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -13,4 +13,5 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \ drm_mm_test.o \ drm_modes_test.o \ drm_plane_helper_test.o \ + drm_probe_helper_test.o \ drm_rect_test.o diff --git a/drivers/gpu/drm/tests/drm_probe_helper_test.c b/drivers/gpu/drm/tests/drm_probe_helper_test.c new file mode 100644 index 000000000000..7e938258c742 --- /dev/null +++ b/drivers/gpu/drm/tests/drm_probe_helper_test.c @@ -0,0 +1,205 @@ +// 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_mode.h> +#include <drm/drm_modes.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_probe_helper.h> + +#include <kunit/test.h> + +#include "drm_kunit_helpers.h" + +struct drm_probe_helper_test_priv { + struct drm_device *drm; + 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->drm = drm_kunit_device_init(test, DRIVER_MODESET | DRIVER_ATOMIC, + "drm-probe-helper-test"); + 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); + + 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"); diff --git a/include/drm/drm_probe_helper.h b/include/drm/drm_probe_helper.h index 5880daa14624..4977e0ab72db 100644 --- a/include/drm/drm_probe_helper.h +++ b/include/drm/drm_probe_helper.h @@ -35,5 +35,6 @@ int drm_connector_helper_get_modes_from_ddc(struct drm_connector *connector); int drm_connector_helper_get_modes_fixed(struct drm_connector *connector, const struct drm_display_mode *fixed_mode); int drm_connector_helper_get_modes(struct drm_connector *connector); +int drm_connector_helper_tv_get_modes(struct drm_connector *connector); #endif |