drm/probe-helper: Provide a TV get_modes helper
Most of the TV connectors will need a similar get_modes implementation that will, depending on the drivers' capabilities, register the 480i and 576i modes. That implementation will also need to set the preferred flag and order the modes based on the driver and users preferrence. This is especially important to guarantee that a userspace stack such as Xorg can start and pick up the preferred mode while maintaining a working output. Signed-off-by: Noralf Trønnes <noralf@tronnes.org> Tested-by: Mateusz Kwiatkowski <kfyatek+publicgit@gmail.com> Acked-in-principle-or-something-like-that-by: Daniel Vetter <daniel.vetter@ffwll.ch> Link: https://lore.kernel.org/r/20220728-rpi-analog-tv-properties-v10-12-256dad125326@cerno.tech Signed-off-by: Maxime Ripard <maxime@cerno.tech>
This commit is contained in:
parent
0740ac381b
commit
1e4a91db10
@ -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);
|
||||
|
@ -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
|
||||
|
205
drivers/gpu/drm/tests/drm_probe_helper_test.c
Normal file
205
drivers/gpu/drm/tests/drm_probe_helper_test.c
Normal file
@ -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");
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user