Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,5 @@
CONFIG_KUNIT=y
CONFIG_DRM=y
CONFIG_DRM_VKMS=y
CONFIG_DRM_FBDEV_EMULATION=y
CONFIG_DRM_KUNIT_TEST=y
@@ -0,0 +1,29 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_DRM_KUNIT_TEST_HELPERS) += \
drm_kunit_helpers.o
obj-$(CONFIG_DRM_KUNIT_TEST) += \
drm_atomic_test.o \
drm_atomic_commit_test.o \
drm_bridge_test.o \
drm_cmdline_parser_test.o \
drm_connector_test.o \
drm_damage_helper_test.o \
drm_dp_mst_helper_test.o \
drm_exec_test.o \
drm_format_helper_test.o \
drm_format_test.o \
drm_framebuffer_test.o \
drm_gem_shmem_test.o \
drm_hdmi_state_helper_test.o \
drm_managed_test.o \
drm_mm_test.o \
drm_modes_test.o \
drm_plane_helper_test.o \
drm_probe_helper_test.o \
drm_rect_test.o \
drm_sysfb_modeset_test.o \
drm_fixp_test.o
CFLAGS_drm_mm_test.o := $(DISABLE_STRUCTLEAK_PLUGIN)
@@ -0,0 +1,409 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Test cases for the drm_atomic_commit helpers
*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic_uapi.h>
#include <drm/drm_kunit_helpers.h>
#include <drm/drm_probe_helper.h>
#define DRM_TEST_ENC_0 BIT(0)
#define DRM_TEST_ENC_1 BIT(1)
#define DRM_TEST_ENC_2 BIT(2)
#define DRM_TEST_CONN_0 BIT(0)
struct drm_clone_mode_test {
const char *name;
u32 encoder_mask;
int expected_result;
};
static const struct drm_display_mode drm_atomic_test_mode = {
DRM_MODE("1024x768", 0, 65000, 1024, 1048,
1184, 1344, 0, 768, 771, 777, 806, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)
};
struct drm_atomic_test_priv {
struct drm_device drm;
struct drm_plane *plane;
struct drm_crtc *crtc;
struct drm_encoder encoders[3];
struct drm_connector connectors[2];
};
static int modeset_counter;
static void drm_test_encoder_mode_set(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
modeset_counter++;
}
static const struct drm_encoder_helper_funcs drm_atomic_test_encoder_funcs = {
.atomic_mode_set = drm_test_encoder_mode_set,
};
static const struct drm_connector_funcs dummy_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_atomic_test_dummy_get_modes(struct drm_connector *connector)
{
return drm_connector_helper_get_modes_fixed(connector,
&drm_atomic_test_mode);
}
static const struct drm_connector_helper_funcs dummy_connector_helper_funcs = {
.get_modes = drm_atomic_test_dummy_get_modes,
};
static struct drm_atomic_test_priv *
drm_atomic_test_init_drm_components(struct kunit *test, bool has_connectors)
{
struct drm_atomic_test_priv *priv;
struct drm_encoder *enc;
struct drm_connector *conn;
struct drm_device *drm;
struct device *dev;
int ret;
dev = drm_kunit_helper_alloc_device(test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
priv = drm_kunit_helper_alloc_drm_device(test, dev,
struct drm_atomic_test_priv,
drm,
DRIVER_MODESET | DRIVER_ATOMIC);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
test->priv = priv;
drm = &priv->drm;
priv->plane = drm_kunit_helper_create_primary_plane(test, drm,
NULL,
NULL,
NULL, 0,
NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->plane);
priv->crtc = drm_kunit_helper_create_crtc(test, drm,
priv->plane, NULL,
NULL,
NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->crtc);
for (int i = 0; i < ARRAY_SIZE(priv->encoders); i++) {
enc = &priv->encoders[i];
ret = drmm_encoder_init(drm, enc, NULL,
DRM_MODE_ENCODER_DSI, NULL);
KUNIT_ASSERT_EQ(test, ret, 0);
enc->possible_crtcs = drm_crtc_mask(priv->crtc);
}
priv->encoders[0].possible_clones = DRM_TEST_ENC_0 | DRM_TEST_ENC_1;
priv->encoders[1].possible_clones = DRM_TEST_ENC_0 | DRM_TEST_ENC_1;
priv->encoders[2].possible_clones = DRM_TEST_ENC_2;
if (!has_connectors)
goto done;
BUILD_BUG_ON(ARRAY_SIZE(priv->connectors) > ARRAY_SIZE(priv->encoders));
for (int i = 0; i < ARRAY_SIZE(priv->connectors); i++) {
conn = &priv->connectors[i];
ret = drmm_connector_init(drm, conn, &dummy_connector_funcs,
DRM_MODE_CONNECTOR_DSI, NULL);
KUNIT_ASSERT_EQ(test, ret, 0);
drm_connector_helper_add(conn, &dummy_connector_helper_funcs);
drm_encoder_helper_add(&priv->encoders[i],
&drm_atomic_test_encoder_funcs);
drm_connector_attach_encoder(conn, &priv->encoders[i]);
}
done:
drm_mode_config_reset(drm);
return priv;
}
static int set_up_atomic_state(struct kunit *test,
struct drm_atomic_test_priv *priv,
struct drm_connector *connector,
struct drm_modeset_acquire_ctx *ctx)
{
struct drm_device *drm = &priv->drm;
struct drm_crtc *crtc = priv->crtc;
struct drm_atomic_commit *state;
struct drm_connector_state *conn_state;
struct drm_crtc_state *crtc_state;
int ret;
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
if (connector) {
conn_state = drm_atomic_get_connector_state(state, connector);
if (IS_ERR(conn_state))
return PTR_ERR(conn_state);
ret = drm_atomic_set_crtc_for_connector(conn_state, crtc);
if (ret)
return ret;
}
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
ret = drm_atomic_set_mode_for_crtc(crtc_state, &drm_atomic_test_mode);
if (ret)
return ret;
crtc_state->enable = true;
crtc_state->active = true;
if (connector) {
ret = drm_atomic_commit(state);
if (ret)
return ret;
} else {
// dummy connector mask
crtc_state->connector_mask = DRM_TEST_CONN_0;
}
return 0;
}
/*
* Test that the DRM encoder mode_set() is called when the atomic state
* connectors are changed but the CRTC mode is not.
*/
static void drm_test_check_connector_changed_modeset(struct kunit *test)
{
struct drm_atomic_test_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector *old_conn, *new_conn;
struct drm_atomic_commit *state;
struct drm_device *drm;
struct drm_connector_state *new_conn_state, *old_conn_state;
int ret, initial_modeset_count;
priv = drm_atomic_test_init_drm_components(test, true);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
old_conn = &priv->connectors[0];
new_conn = &priv->connectors[1];
drm_modeset_acquire_init(&ctx, 0);
// first modeset to enable
retry_set_up:
ret = set_up_atomic_state(test, priv, old_conn, &ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_set_up;
}
KUNIT_ASSERT_EQ(test, ret, 0);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
new_conn_state = drm_atomic_get_connector_state(state, new_conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state);
old_conn_state = drm_atomic_get_connector_state(state, old_conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state);
ret = drm_atomic_set_crtc_for_connector(old_conn_state, NULL);
KUNIT_EXPECT_EQ(test, ret, 0);
ret = drm_atomic_set_crtc_for_connector(new_conn_state, priv->crtc);
KUNIT_EXPECT_EQ(test, ret, 0);
initial_modeset_count = modeset_counter;
// modeset_disables is called as part of the atomic commit tail
ret = drm_atomic_commit(state);
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_ASSERT_EQ(test, modeset_counter, initial_modeset_count + 1);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
/*
* Test that the drm_crtc_in_clone_mode() helper can detect if a given CRTC
* state is in clone mode
*/
static void drm_test_check_in_clone_mode(struct kunit *test)
{
bool ret;
const struct drm_clone_mode_test *param = test->param_value;
struct drm_crtc_state *crtc_state;
crtc_state = kunit_kzalloc(test, sizeof(*crtc_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, crtc_state);
crtc_state->encoder_mask = param->encoder_mask;
ret = drm_crtc_in_clone_mode(crtc_state);
KUNIT_ASSERT_EQ(test, ret, param->expected_result);
}
/*
* Test that the atomic commit path will succeed for valid clones (or non-cloned
* states) and fail for states where the cloned encoders are not possible_clones
* of each other.
*/
static void drm_test_check_valid_clones(struct kunit *test)
{
int ret;
const struct drm_clone_mode_test *param = test->param_value;
struct drm_atomic_test_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_device *drm;
struct drm_atomic_commit *state;
struct drm_crtc_state *crtc_state;
priv = drm_atomic_test_init_drm_components(test, false);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
drm_modeset_acquire_init(&ctx, 0);
retry_set_up:
ret = set_up_atomic_state(test, priv, NULL, &ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_set_up;
}
KUNIT_ASSERT_EQ(test, ret, 0);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
retry:
crtc_state = drm_atomic_get_crtc_state(state, priv->crtc);
if (PTR_ERR(crtc_state) == -EDEADLK) {
drm_atomic_commit_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
crtc_state->encoder_mask = param->encoder_mask;
// force modeset
crtc_state->mode_changed = true;
ret = drm_atomic_helper_check_modeset(drm, state);
if (ret == -EDEADLK) {
drm_atomic_commit_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry;
}
KUNIT_ASSERT_EQ(test, ret, param->expected_result);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_check_in_clone_mode_desc(const struct drm_clone_mode_test *t,
char *desc)
{
sprintf(desc, "%s", t->name);
}
static void drm_check_valid_clones_desc(const struct drm_clone_mode_test *t,
char *desc)
{
sprintf(desc, "%s", t->name);
}
static const struct drm_clone_mode_test drm_clone_mode_tests[] = {
{
.name = "in_clone_mode",
.encoder_mask = DRM_TEST_ENC_0 | DRM_TEST_ENC_1,
.expected_result = true,
},
{
.name = "not_in_clone_mode",
.encoder_mask = DRM_TEST_ENC_0,
.expected_result = false,
},
};
static const struct drm_clone_mode_test drm_valid_clone_mode_tests[] = {
{
.name = "not_in_clone_mode",
.encoder_mask = DRM_TEST_ENC_0,
.expected_result = 0,
},
{
.name = "valid_clone",
.encoder_mask = DRM_TEST_ENC_0 | DRM_TEST_ENC_1,
.expected_result = 0,
},
{
.name = "invalid_clone",
.encoder_mask = DRM_TEST_ENC_0 | DRM_TEST_ENC_2,
.expected_result = -EINVAL,
},
};
KUNIT_ARRAY_PARAM(drm_check_in_clone_mode, drm_clone_mode_tests,
drm_check_in_clone_mode_desc);
KUNIT_ARRAY_PARAM(drm_check_valid_clones, drm_valid_clone_mode_tests,
drm_check_valid_clones_desc);
static struct kunit_case drm_test_check_modeset_test[] = {
KUNIT_CASE(drm_test_check_connector_changed_modeset),
{}
};
static struct kunit_case drm_in_clone_mode_check_test[] = {
KUNIT_CASE_PARAM(drm_test_check_in_clone_mode,
drm_check_in_clone_mode_gen_params),
KUNIT_CASE_PARAM(drm_test_check_valid_clones,
drm_check_valid_clones_gen_params),
{}
};
static struct kunit_suite drm_test_check_modeset_test_suite = {
.name = "drm_validate_modeset",
.test_cases = drm_test_check_modeset_test,
};
static struct kunit_suite drm_in_clone_mode_check_test_suite = {
.name = "drm_validate_clone_mode",
.test_cases = drm_in_clone_mode_check_test,
};
kunit_test_suites(&drm_in_clone_mode_check_test_suite,
&drm_test_check_modeset_test_suite);
MODULE_AUTHOR("Jessica Zhang <quic_jesszhan@quicinc.com");
MODULE_DESCRIPTION("Test cases for the drm_atomic_helper functions");
MODULE_LICENSE("GPL");
@@ -0,0 +1,153 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Kunit test for drm_atomic functions
*/
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_atomic_uapi.h>
#include <drm/drm_encoder.h>
#include <drm/drm_kunit_helpers.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <kunit/test.h>
struct drm_atomic_test_priv {
struct drm_device drm;
struct drm_plane *plane;
struct drm_crtc *crtc;
struct drm_encoder encoder;
struct drm_connector connector;
};
static const struct drm_connector_helper_funcs drm_atomic_init_connector_helper_funcs = {
};
static const struct drm_connector_funcs drm_atomic_init_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 struct drm_atomic_test_priv *create_device(struct kunit *test)
{
struct drm_atomic_test_priv *priv;
struct drm_connector *connector;
struct drm_encoder *enc;
struct drm_device *drm;
struct drm_plane *plane;
struct drm_crtc *crtc;
struct device *dev;
int ret;
dev = drm_kunit_helper_alloc_device(test);
if (IS_ERR(dev))
return ERR_CAST(dev);
priv = drm_kunit_helper_alloc_drm_device(test, dev,
struct drm_atomic_test_priv, drm,
DRIVER_MODESET | DRIVER_ATOMIC);
if (IS_ERR(priv))
return ERR_CAST(priv);
drm = &priv->drm;
plane = drm_kunit_helper_create_primary_plane(test, drm,
NULL,
NULL,
NULL, 0,
NULL);
if (IS_ERR(plane))
return ERR_CAST(plane);
priv->plane = plane;
crtc = drm_kunit_helper_create_crtc(test, drm,
plane, NULL,
NULL,
NULL);
if (IS_ERR(crtc))
return ERR_CAST(crtc);
priv->crtc = crtc;
enc = &priv->encoder;
ret = drmm_encoder_init(drm, enc, NULL, DRM_MODE_ENCODER_TMDS, NULL);
if (ret)
return ERR_PTR(ret);
enc->possible_crtcs = drm_crtc_mask(crtc);
connector = &priv->connector;
ret = drmm_connector_init(drm, connector,
&drm_atomic_init_connector_funcs,
DRM_MODE_CONNECTOR_VIRTUAL,
NULL);
if (ret)
return ERR_PTR(ret);
drm_connector_helper_add(connector, &drm_atomic_init_connector_helper_funcs);
drm_connector_attach_encoder(connector, enc);
drm_mode_config_reset(drm);
return priv;
}
static void drm_test_drm_atomic_get_connector_for_encoder(struct kunit *test)
{
struct drm_modeset_acquire_ctx ctx;
struct drm_atomic_test_priv *priv;
struct drm_display_mode *mode;
struct drm_connector *curr_connector;
int ret;
priv = create_device(test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode);
drm_modeset_acquire_init(&ctx, 0);
retry_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, &priv->drm,
priv->crtc, &priv->connector,
mode, &ctx);
if (ret == -EDEADLK) {
drm_modeset_backoff(&ctx);
goto retry_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
drm_modeset_acquire_init(&ctx, 0);
retry_conn:
curr_connector = drm_atomic_get_connector_for_encoder(&priv->encoder,
&ctx);
if (PTR_ERR(curr_connector) == -EDEADLK) {
drm_modeset_backoff(&ctx);
goto retry_conn;
}
KUNIT_EXPECT_PTR_EQ(test, curr_connector, &priv->connector);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static struct kunit_case drm_atomic_get_connector_for_encoder_tests[] = {
KUNIT_CASE(drm_test_drm_atomic_get_connector_for_encoder),
{ }
};
static struct kunit_suite drm_atomic_get_connector_for_encoder_test_suite = {
.name = "drm_test_atomic_get_connector_for_encoder",
.test_cases = drm_atomic_get_connector_for_encoder_tests,
};
kunit_test_suite(drm_atomic_get_connector_for_encoder_test_suite);
MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
MODULE_DESCRIPTION("Kunit test for drm_atomic functions");
MODULE_LICENSE("GPL");
@@ -0,0 +1,519 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Kunit test for drm_bridge functions
*/
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
#include <drm/drm_bridge_helper.h>
#include <drm/drm_kunit_helpers.h>
#include <kunit/device.h>
#include <kunit/test.h>
/*
* Mimick the typical "private" struct defined by a bridge driver, which
* embeds a bridge plus other fields.
*
* Having at least one member before @bridge ensures we test non-zero
* @bridge offset.
*/
struct drm_bridge_priv {
unsigned int enable_count;
unsigned int disable_count;
struct drm_bridge bridge;
void *data;
};
struct drm_bridge_init_priv {
struct drm_device drm;
/** @dev: device, only for tests not needing a whole drm_device */
struct device *dev;
struct drm_plane *plane;
struct drm_crtc *crtc;
struct drm_encoder encoder;
struct drm_bridge_priv *test_bridge;
struct drm_connector *connector;
bool destroyed;
};
static struct drm_bridge_priv *bridge_to_priv(struct drm_bridge *bridge)
{
return container_of(bridge, struct drm_bridge_priv, bridge);
}
static void drm_test_bridge_priv_destroy(struct drm_bridge *bridge)
{
struct drm_bridge_priv *bridge_priv = bridge_to_priv(bridge);
struct drm_bridge_init_priv *priv = (struct drm_bridge_init_priv *)bridge_priv->data;
priv->destroyed = true;
}
static void drm_test_bridge_enable(struct drm_bridge *bridge)
{
struct drm_bridge_priv *priv = bridge_to_priv(bridge);
priv->enable_count++;
}
static void drm_test_bridge_disable(struct drm_bridge *bridge)
{
struct drm_bridge_priv *priv = bridge_to_priv(bridge);
priv->disable_count++;
}
static const struct drm_bridge_funcs drm_test_bridge_legacy_funcs = {
.destroy = drm_test_bridge_priv_destroy,
.enable = drm_test_bridge_enable,
.disable = drm_test_bridge_disable,
};
static void drm_test_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_atomic_commit *state)
{
struct drm_bridge_priv *priv = bridge_to_priv(bridge);
priv->enable_count++;
}
static void drm_test_bridge_atomic_disable(struct drm_bridge *bridge,
struct drm_atomic_commit *state)
{
struct drm_bridge_priv *priv = bridge_to_priv(bridge);
priv->disable_count++;
}
static const struct drm_bridge_funcs drm_test_bridge_atomic_funcs = {
.destroy = drm_test_bridge_priv_destroy,
.atomic_enable = drm_test_bridge_atomic_enable,
.atomic_disable = drm_test_bridge_atomic_disable,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
};
KUNIT_DEFINE_ACTION_WRAPPER(drm_bridge_remove_wrapper,
drm_bridge_remove,
struct drm_bridge *);
static int drm_kunit_bridge_add(struct kunit *test,
struct drm_bridge *bridge)
{
drm_bridge_add(bridge);
return kunit_add_action_or_reset(test,
drm_bridge_remove_wrapper,
bridge);
}
static struct drm_bridge_init_priv *
drm_test_bridge_init(struct kunit *test, const struct drm_bridge_funcs *funcs)
{
struct drm_bridge_init_priv *priv;
struct drm_encoder *enc;
struct drm_bridge *bridge;
struct drm_device *drm;
struct device *dev;
int ret;
dev = drm_kunit_helper_alloc_device(test);
if (IS_ERR(dev))
return ERR_CAST(dev);
priv = drm_kunit_helper_alloc_drm_device(test, dev,
struct drm_bridge_init_priv, drm,
DRIVER_MODESET | DRIVER_ATOMIC);
if (IS_ERR(priv))
return ERR_CAST(priv);
priv->test_bridge = devm_drm_bridge_alloc(dev, struct drm_bridge_priv, bridge, funcs);
if (IS_ERR(priv->test_bridge))
return ERR_CAST(priv->test_bridge);
priv->test_bridge->data = priv;
drm = &priv->drm;
priv->plane = drm_kunit_helper_create_primary_plane(test, drm,
NULL,
NULL,
NULL, 0,
NULL);
if (IS_ERR(priv->plane))
return ERR_CAST(priv->plane);
priv->crtc = drm_kunit_helper_create_crtc(test, drm,
priv->plane, NULL,
NULL,
NULL);
if (IS_ERR(priv->crtc))
return ERR_CAST(priv->crtc);
enc = &priv->encoder;
ret = drmm_encoder_init(drm, enc, NULL, DRM_MODE_ENCODER_TMDS, NULL);
if (ret)
return ERR_PTR(ret);
enc->possible_crtcs = drm_crtc_mask(priv->crtc);
bridge = &priv->test_bridge->bridge;
bridge->type = DRM_MODE_CONNECTOR_VIRTUAL;
ret = drm_kunit_bridge_add(test, bridge);
if (ret)
return ERR_PTR(ret);
ret = drm_bridge_attach(enc, bridge, NULL, 0);
if (ret)
return ERR_PTR(ret);
priv->connector = drm_bridge_connector_init(drm, enc);
if (IS_ERR(priv->connector))
return ERR_CAST(priv->connector);
drm_mode_config_reset(drm);
return priv;
}
/*
* Test that drm_bridge_get_current_state() returns the last committed
* state for an atomic bridge.
*/
static void drm_test_drm_bridge_get_current_state_atomic(struct kunit *test)
{
struct drm_modeset_acquire_ctx ctx;
struct drm_bridge_init_priv *priv;
struct drm_bridge_state *curr_bridge_state;
struct drm_bridge_state *bridge_state;
struct drm_atomic_commit *state;
struct drm_bridge *bridge;
struct drm_device *drm;
int ret;
priv = drm_test_bridge_init(test, &drm_test_bridge_atomic_funcs);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
drm_modeset_acquire_init(&ctx, 0);
drm = &priv->drm;
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
retry_commit:
bridge = &priv->test_bridge->bridge;
bridge_state = drm_atomic_get_bridge_state(state, bridge);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bridge_state);
ret = drm_atomic_commit(state);
if (ret == -EDEADLK) {
drm_atomic_commit_clear(state);
drm_modeset_backoff(&ctx);
goto retry_commit;
}
KUNIT_ASSERT_EQ(test, ret, 0);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
drm_modeset_acquire_init(&ctx, 0);
retry_state:
ret = drm_modeset_lock(&bridge->base.lock, &ctx);
if (ret == -EDEADLK) {
drm_modeset_backoff(&ctx);
goto retry_state;
}
curr_bridge_state = drm_bridge_get_current_state(bridge);
KUNIT_EXPECT_PTR_EQ(test, curr_bridge_state, bridge_state);
drm_modeset_unlock(&bridge->base.lock);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
/*
* Test that drm_bridge_get_current_state() returns NULL for a
* non-atomic bridge.
*/
static void drm_test_drm_bridge_get_current_state_legacy(struct kunit *test)
{
struct drm_bridge_init_priv *priv;
struct drm_bridge *bridge;
priv = drm_test_bridge_init(test, &drm_test_bridge_legacy_funcs);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
/*
* NOTE: Strictly speaking, we should take the bridge->base.lock
* before calling that function. However, bridge->base is only
* initialized if the bridge is atomic, while we explicitly
* initialize one that isn't there.
*
* In order to avoid unnecessary warnings, let's skip the
* locking. The function would return NULL in all cases anyway,
* so we don't really have any concurrency to worry about.
*/
bridge = &priv->test_bridge->bridge;
KUNIT_EXPECT_NULL(test, drm_bridge_get_current_state(bridge));
}
static struct kunit_case drm_bridge_get_current_state_tests[] = {
KUNIT_CASE(drm_test_drm_bridge_get_current_state_atomic),
KUNIT_CASE(drm_test_drm_bridge_get_current_state_legacy),
{ }
};
static struct kunit_suite drm_bridge_get_current_state_test_suite = {
.name = "drm_test_bridge_get_current_state",
.test_cases = drm_bridge_get_current_state_tests,
};
/*
* Test that an atomic bridge is properly power-cycled when calling
* drm_bridge_helper_reset_crtc().
*/
static void drm_test_drm_bridge_helper_reset_crtc_atomic(struct kunit *test)
{
struct drm_modeset_acquire_ctx ctx;
struct drm_bridge_init_priv *priv;
struct drm_display_mode *mode;
struct drm_bridge_priv *bridge_priv;
int ret;
priv = drm_test_bridge_init(test, &drm_test_bridge_atomic_funcs);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode);
drm_modeset_acquire_init(&ctx, 0);
retry_commit:
ret = drm_kunit_helper_enable_crtc_connector(test,
&priv->drm, priv->crtc,
priv->connector,
mode,
&ctx);
if (ret == -EDEADLK) {
drm_modeset_backoff(&ctx);
goto retry_commit;
}
KUNIT_ASSERT_EQ(test, ret, 0);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
bridge_priv = priv->test_bridge;
KUNIT_ASSERT_EQ(test, bridge_priv->enable_count, 1);
KUNIT_ASSERT_EQ(test, bridge_priv->disable_count, 0);
drm_modeset_acquire_init(&ctx, 0);
retry_reset:
ret = drm_bridge_helper_reset_crtc(&bridge_priv->bridge, &ctx);
if (ret == -EDEADLK) {
drm_modeset_backoff(&ctx);
goto retry_reset;
}
KUNIT_ASSERT_EQ(test, ret, 0);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
KUNIT_EXPECT_EQ(test, bridge_priv->enable_count, 2);
KUNIT_EXPECT_EQ(test, bridge_priv->disable_count, 1);
}
/*
* Test that calling drm_bridge_helper_reset_crtc() on a disabled atomic
* bridge will fail and not call the enable / disable callbacks
*/
static void drm_test_drm_bridge_helper_reset_crtc_atomic_disabled(struct kunit *test)
{
struct drm_modeset_acquire_ctx ctx;
struct drm_bridge_init_priv *priv;
struct drm_display_mode *mode;
struct drm_bridge_priv *bridge_priv;
int ret;
priv = drm_test_bridge_init(test, &drm_test_bridge_atomic_funcs);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode);
bridge_priv = priv->test_bridge;
KUNIT_ASSERT_EQ(test, bridge_priv->enable_count, 0);
KUNIT_ASSERT_EQ(test, bridge_priv->disable_count, 0);
drm_modeset_acquire_init(&ctx, 0);
retry_reset:
ret = drm_bridge_helper_reset_crtc(&bridge_priv->bridge, &ctx);
if (ret == -EDEADLK) {
drm_modeset_backoff(&ctx);
goto retry_reset;
}
KUNIT_EXPECT_LT(test, ret, 0);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
KUNIT_EXPECT_EQ(test, bridge_priv->enable_count, 0);
KUNIT_EXPECT_EQ(test, bridge_priv->disable_count, 0);
}
/*
* Test that a non-atomic bridge is properly power-cycled when calling
* drm_bridge_helper_reset_crtc().
*/
static void drm_test_drm_bridge_helper_reset_crtc_legacy(struct kunit *test)
{
struct drm_modeset_acquire_ctx ctx;
struct drm_bridge_init_priv *priv;
struct drm_display_mode *mode;
struct drm_bridge_priv *bridge_priv;
int ret;
priv = drm_test_bridge_init(test, &drm_test_bridge_legacy_funcs);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode);
drm_modeset_acquire_init(&ctx, 0);
retry_commit:
ret = drm_kunit_helper_enable_crtc_connector(test,
&priv->drm, priv->crtc,
priv->connector,
mode,
&ctx);
if (ret == -EDEADLK) {
drm_modeset_backoff(&ctx);
goto retry_commit;
}
KUNIT_ASSERT_EQ(test, ret, 0);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
bridge_priv = priv->test_bridge;
KUNIT_ASSERT_EQ(test, bridge_priv->enable_count, 1);
KUNIT_ASSERT_EQ(test, bridge_priv->disable_count, 0);
drm_modeset_acquire_init(&ctx, 0);
retry_reset:
ret = drm_bridge_helper_reset_crtc(&bridge_priv->bridge, &ctx);
if (ret == -EDEADLK) {
drm_modeset_backoff(&ctx);
goto retry_reset;
}
KUNIT_ASSERT_EQ(test, ret, 0);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
KUNIT_EXPECT_EQ(test, bridge_priv->enable_count, 2);
KUNIT_EXPECT_EQ(test, bridge_priv->disable_count, 1);
}
static struct kunit_case drm_bridge_helper_reset_crtc_tests[] = {
KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_atomic),
KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_atomic_disabled),
KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_legacy),
{ }
};
static struct kunit_suite drm_bridge_helper_reset_crtc_test_suite = {
.name = "drm_test_bridge_helper_reset_crtc",
.test_cases = drm_bridge_helper_reset_crtc_tests,
};
static int drm_test_bridge_alloc_init(struct kunit *test)
{
struct drm_bridge_init_priv *priv;
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
priv->dev = kunit_device_register(test, "drm-bridge-dev");
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev);
test->priv = priv;
priv->test_bridge = devm_drm_bridge_alloc(priv->dev, struct drm_bridge_priv, bridge,
&drm_test_bridge_atomic_funcs);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->test_bridge);
priv->test_bridge->data = priv;
KUNIT_ASSERT_FALSE(test, priv->destroyed);
return 0;
}
/*
* Test that a bridge is freed when the device is destroyed in lack of
* other drm_bridge_get/put() operations.
*/
static void drm_test_drm_bridge_alloc_basic(struct kunit *test)
{
struct drm_bridge_init_priv *priv = test->priv;
KUNIT_ASSERT_FALSE(test, priv->destroyed);
kunit_device_unregister(test, priv->dev);
KUNIT_EXPECT_TRUE(test, priv->destroyed);
}
/*
* Test that a bridge is not freed when the device is destroyed when there
* is still a reference to it, and freed when that reference is put.
*/
static void drm_test_drm_bridge_alloc_get_put(struct kunit *test)
{
struct drm_bridge_init_priv *priv = test->priv;
KUNIT_ASSERT_FALSE(test, priv->destroyed);
drm_bridge_get(&priv->test_bridge->bridge);
KUNIT_EXPECT_FALSE(test, priv->destroyed);
kunit_device_unregister(test, priv->dev);
KUNIT_EXPECT_FALSE(test, priv->destroyed);
drm_bridge_put(&priv->test_bridge->bridge);
KUNIT_EXPECT_TRUE(test, priv->destroyed);
}
static struct kunit_case drm_bridge_alloc_tests[] = {
KUNIT_CASE(drm_test_drm_bridge_alloc_basic),
KUNIT_CASE(drm_test_drm_bridge_alloc_get_put),
{ }
};
static struct kunit_suite drm_bridge_alloc_test_suite = {
.name = "drm_bridge_alloc",
.init = drm_test_bridge_alloc_init,
.test_cases = drm_bridge_alloc_tests,
};
kunit_test_suites(
&drm_bridge_get_current_state_test_suite,
&drm_bridge_helper_reset_crtc_test_suite,
&drm_bridge_alloc_test_suite,
);
MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
MODULE_DESCRIPTION("Kunit test for drm_bridge functions");
MODULE_LICENSE("GPL");
@@ -0,0 +1,203 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2022 Maxime Ripard <mripard@kernel.org>
*/
#include <kunit/test.h>
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_connector.h>
#include <drm/drm_edid.h>
#include <drm/drm_drv.h>
#include <drm/drm_kunit_helpers.h>
#include <drm/drm_modes.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_probe_helper.h>
struct drm_client_modeset_test_priv {
struct drm_device *drm;
struct device *dev;
struct drm_connector connector;
};
static int drm_client_modeset_connector_get_modes(struct drm_connector *connector)
{
struct drm_display_mode *mode;
int count;
count = drm_add_modes_noedid(connector, 1920, 1200);
mode = drm_mode_analog_ntsc_480i(connector->dev);
if (!mode)
return count;
drm_mode_probed_add(connector, mode);
count += 1;
mode = drm_mode_analog_pal_576i(connector->dev);
if (!mode)
return count;
drm_mode_probed_add(connector, mode);
count += 1;
return count;
}
static const struct drm_connector_helper_funcs drm_client_modeset_connector_helper_funcs = {
.get_modes = drm_client_modeset_connector_get_modes,
};
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
};
static int drm_client_modeset_test_init(struct kunit *test)
{
struct drm_client_modeset_test_priv *priv;
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);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
ret = drmm_connector_init(priv->drm, &priv->connector,
&drm_client_modeset_connector_funcs,
DRM_MODE_CONNECTOR_Unknown,
NULL);
KUNIT_ASSERT_EQ(test, ret, 0);
drm_connector_helper_add(&priv->connector, &drm_client_modeset_connector_helper_funcs);
priv->connector.interlace_allowed = true;
priv->connector.doublescan_allowed = true;
return 0;
}
static void drm_test_pick_cmdline_res_1920_1080_60(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;
struct drm_display_mode *expected_mode;
const struct drm_display_mode *mode;
const char *cmdline = "1920x1080@60";
int ret;
expected_mode = drm_mode_find_dmt(priv->drm, 1920, 1080, 60, false);
KUNIT_ASSERT_NOT_NULL(test, expected_mode);
ret = drm_kunit_add_mode_destroy_action(test, expected_mode);
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_ASSERT_TRUE(test,
drm_mode_parse_command_line_for_connector(cmdline,
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);
KUNIT_ASSERT_NOT_NULL(test, mode);
KUNIT_EXPECT_TRUE(test, drm_mode_equal(expected_mode, mode));
}
struct drm_connector_pick_cmdline_mode_test {
const char *cmdline;
struct drm_display_mode *(*func)(struct drm_device *drm);
};
#define TEST_CMDLINE(_cmdline, _fn) \
{ \
.cmdline = _cmdline, \
.func = _fn, \
}
static void drm_test_pick_cmdline_named(struct kunit *test)
{
const struct drm_connector_pick_cmdline_mode_test *params = test->param_value;
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;
struct drm_display_mode *expected_mode;
const char *cmdline = params->cmdline;
int ret;
KUNIT_ASSERT_TRUE(test,
drm_mode_parse_command_line_for_connector(cmdline,
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);
KUNIT_ASSERT_NOT_NULL(test, mode);
expected_mode = params->func(drm);
KUNIT_ASSERT_NOT_NULL(test, expected_mode);
ret = drm_kunit_add_mode_destroy_action(test, expected_mode);
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_TRUE(test, drm_mode_equal(expected_mode, mode));
}
static const
struct drm_connector_pick_cmdline_mode_test drm_connector_pick_cmdline_mode_tests[] = {
TEST_CMDLINE("NTSC", drm_mode_analog_ntsc_480i),
TEST_CMDLINE("NTSC-J", drm_mode_analog_ntsc_480i),
TEST_CMDLINE("PAL", drm_mode_analog_pal_576i),
TEST_CMDLINE("PAL-M", drm_mode_analog_ntsc_480i),
};
static void
drm_connector_pick_cmdline_mode_desc(const struct drm_connector_pick_cmdline_mode_test *t,
char *desc)
{
sprintf(desc, "%s", t->cmdline);
}
KUNIT_ARRAY_PARAM(drm_connector_pick_cmdline_mode,
drm_connector_pick_cmdline_mode_tests,
drm_connector_pick_cmdline_mode_desc);
static struct kunit_case drm_test_pick_cmdline_tests[] = {
KUNIT_CASE(drm_test_pick_cmdline_res_1920_1080_60),
KUNIT_CASE_PARAM(drm_test_pick_cmdline_named,
drm_connector_pick_cmdline_mode_gen_params),
{}
};
static struct kunit_suite drm_test_pick_cmdline_test_suite = {
.name = "drm_test_pick_cmdline",
.init = drm_client_modeset_test_init,
.test_cases = drm_test_pick_cmdline_tests
};
kunit_test_suite(drm_test_pick_cmdline_test_suite);
/*
* This file is included directly by drm_client_modeset.c so we can't
* use any MODULE_* macro here.
*/
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,640 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Test case for drm_damage_helper functions
*
* Copyright (c) 2022 Maíra Canal <mairacanal@riseup.net>
*/
#include <kunit/test.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_plane.h>
#include <drm/drm_drv.h>
struct drm_damage_mock {
struct drm_driver driver;
struct drm_device device;
struct drm_object_properties obj_props;
struct drm_plane plane;
struct drm_property prop;
struct drm_framebuffer fb;
struct drm_plane_state state;
struct drm_plane_state old_state;
};
static int drm_damage_helper_init(struct kunit *test)
{
struct drm_damage_mock *mock;
mock = kunit_kzalloc(test, sizeof(*mock), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mock);
mock->fb.width = 2048;
mock->fb.height = 2048;
mock->state.crtc = ZERO_SIZE_PTR;
mock->state.fb = &mock->fb;
mock->state.visible = true;
mock->old_state.plane = &mock->plane;
mock->state.plane = &mock->plane;
/* just enough so that drm_plane_enable_fb_damage_clips() works */
mock->device.driver = &mock->driver;
mock->device.mode_config.prop_fb_damage_clips = &mock->prop;
mock->plane.dev = &mock->device;
mock->obj_props.count = 0;
mock->plane.base.properties = &mock->obj_props;
mock->prop.base.id = 1; /* 0 is an invalid id */
mock->prop.dev = &mock->device;
drm_plane_enable_fb_damage_clips(&mock->plane);
test->priv = mock;
return 0;
}
static void set_plane_src(struct drm_plane_state *state, int x1, int y1, int x2,
int y2)
{
state->src_x = x1;
state->src_y = y1;
state->src_w = x2 - x1;
state->src_h = y2 - y1;
state->src.x1 = x1;
state->src.y1 = y1;
state->src.x2 = x2;
state->src.y2 = y2;
}
static void set_damage_clip(struct drm_mode_rect *r, int x1, int y1, int x2,
int y2)
{
r->x1 = x1;
r->y1 = y1;
r->x2 = x2;
r->y2 = y2;
}
static void set_damage_blob(struct drm_property_blob *damage_blob,
struct drm_mode_rect *r, u32 size)
{
damage_blob->length = size;
damage_blob->data = r;
}
static void set_plane_damage(struct drm_plane_state *state,
struct drm_property_blob *damage_blob)
{
state->fb_damage_clips = damage_blob;
}
static void check_damage_clip(struct kunit *test, struct drm_rect *r,
int x1, int y1, int x2, int y2)
{
struct drm_damage_mock *mock = test->priv;
struct drm_plane_state state = mock->state;
/*
* Round down x1/y1 and round up x2/y2. This is because damage is not in
* 16.16 fixed point so to catch all pixels.
*/
int src_x1 = state.src.x1 >> 16;
int src_y1 = state.src.y1 >> 16;
int src_x2 = (state.src.x2 >> 16) + !!(state.src.x2 & 0xFFFF);
int src_y2 = (state.src.y2 >> 16) + !!(state.src.y2 & 0xFFFF);
if (x1 >= x2 || y1 >= y2)
KUNIT_FAIL(test, "Cannot have damage clip with no dimension.");
if (x1 < src_x1 || y1 < src_y1 || x2 > src_x2 || y2 > src_y2)
KUNIT_FAIL(test, "Damage cannot be outside rounded plane src.");
if (r->x1 != x1 || r->y1 != y1 || r->x2 != x2 || r->y2 != y2)
KUNIT_FAIL(test, "Damage = %d %d %d %d, want = %d %d %d %d",
r->x1, r->y1, r->x2, r->y2, x1, y1, x2, y2);
}
static void drm_test_damage_iter_no_damage(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_rect clip;
u32 num_hits = 0;
/* Plane src same as fb size. */
set_plane_src(&mock->old_state, 0, 0, mock->fb.width << 16, mock->fb.height << 16);
set_plane_src(&mock->state, 0, 0, mock->fb.width << 16, mock->fb.height << 16);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return plane src as damage.");
check_damage_clip(test, &clip, 0, 0, 2048, 2048);
}
static void drm_test_damage_iter_no_damage_fractional_src(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_rect clip;
u32 num_hits = 0;
/* Plane src has fractional part. */
set_plane_src(&mock->old_state, 0x3fffe, 0x3fffe,
0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
set_plane_src(&mock->state, 0x3fffe, 0x3fffe,
0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 1,
"Should return rounded off plane src as damage.");
check_damage_clip(test, &clip, 3, 3, 1028, 772);
}
static void drm_test_damage_iter_no_damage_src_moved(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_rect clip;
u32 num_hits = 0;
/* Plane src moved since old plane state. */
set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 10 << 16, 10 << 16,
(10 + 1024) << 16, (10 + 768) << 16);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return plane src as damage.");
check_damage_clip(test, &clip, 10, 10, 1034, 778);
}
static void drm_test_damage_iter_no_damage_fractional_src_moved(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_rect clip;
u32 num_hits = 0;
/* Plane src has fractional part and it moved since old plane state. */
set_plane_src(&mock->old_state, 0x3fffe, 0x3fffe,
0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
set_plane_src(&mock->state, 0x40002, 0x40002,
0x40002 + (1024 << 16), 0x40002 + (768 << 16));
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return plane src as damage.");
check_damage_clip(test, &clip, 4, 4, 1029, 773);
}
static void drm_test_damage_iter_no_damage_not_visible(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_rect clip;
u32 num_hits = 0;
mock->state.visible = false;
set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should have no damage.");
}
static void drm_test_damage_iter_no_damage_no_crtc(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_rect clip;
u32 num_hits = 0;
mock->state.crtc = NULL;
set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should have no damage.");
}
static void drm_test_damage_iter_no_damage_no_fb(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_rect clip;
u32 num_hits = 0;
mock->state.fb = NULL;
set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should have no damage.");
}
static void drm_test_damage_iter_simple_damage(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_property_blob damage_blob;
struct drm_mode_rect damage;
struct drm_rect clip;
u32 num_hits = 0;
set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
/* Damage set to plane src */
set_damage_clip(&damage, 0, 0, 1024, 768);
set_damage_blob(&damage_blob, &damage, sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return damage when set.");
check_damage_clip(test, &clip, 0, 0, 1024, 768);
}
static void drm_test_damage_iter_single_damage(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_property_blob damage_blob;
struct drm_mode_rect damage;
struct drm_rect clip;
u32 num_hits = 0;
set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
set_damage_clip(&damage, 256, 192, 768, 576);
set_damage_blob(&damage_blob, &damage, sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return damage when set.");
check_damage_clip(test, &clip, 256, 192, 768, 576);
}
static void drm_test_damage_iter_single_damage_intersect_src(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_property_blob damage_blob;
struct drm_mode_rect damage;
struct drm_rect clip;
u32 num_hits = 0;
set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
/* Damage intersect with plane src. */
set_damage_clip(&damage, 256, 192, 1360, 768);
set_damage_blob(&damage_blob, &damage, sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return damage clipped to src.");
check_damage_clip(test, &clip, 256, 192, 1024, 768);
}
static void drm_test_damage_iter_single_damage_outside_src(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_property_blob damage_blob;
struct drm_mode_rect damage;
struct drm_rect clip;
u32 num_hits = 0;
set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
/* Damage clip outside plane src */
set_damage_clip(&damage, 1360, 1360, 1380, 1380);
set_damage_blob(&damage_blob, &damage, sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should have no damage.");
}
static void drm_test_damage_iter_single_damage_fractional_src(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_property_blob damage_blob;
struct drm_mode_rect damage;
struct drm_rect clip;
u32 num_hits = 0;
/* Plane src has fractional part. */
set_plane_src(&mock->old_state, 0x40002, 0x40002,
0x40002 + (1024 << 16), 0x40002 + (768 << 16));
set_plane_src(&mock->state, 0x40002, 0x40002,
0x40002 + (1024 << 16), 0x40002 + (768 << 16));
set_damage_clip(&damage, 10, 10, 256, 330);
set_damage_blob(&damage_blob, &damage, sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return damage when set.");
check_damage_clip(test, &clip, 10, 10, 256, 330);
}
static void drm_test_damage_iter_single_damage_intersect_fractional_src(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_property_blob damage_blob;
struct drm_mode_rect damage;
struct drm_rect clip;
u32 num_hits = 0;
/* Plane src has fractional part. */
set_plane_src(&mock->old_state, 0x40002, 0x40002,
0x40002 + (1024 << 16), 0x40002 + (768 << 16));
set_plane_src(&mock->state, 0x40002, 0x40002,
0x40002 + (1024 << 16), 0x40002 + (768 << 16));
/* Damage intersect with plane src. */
set_damage_clip(&damage, 10, 1, 1360, 330);
set_damage_blob(&damage_blob, &damage, sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 1,
"Should return damage clipped to rounded off src.");
check_damage_clip(test, &clip, 10, 4, 1029, 330);
}
static void drm_test_damage_iter_single_damage_outside_fractional_src(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_property_blob damage_blob;
struct drm_mode_rect damage;
struct drm_rect clip;
u32 num_hits = 0;
/* Plane src has fractional part. */
set_plane_src(&mock->old_state, 0x40002, 0x40002,
0x40002 + (1024 << 16), 0x40002 + (768 << 16));
set_plane_src(&mock->state, 0x40002, 0x40002,
0x40002 + (1024 << 16), 0x40002 + (768 << 16));
/* Damage clip outside plane src */
set_damage_clip(&damage, 1360, 1360, 1380, 1380);
set_damage_blob(&damage_blob, &damage, sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should have no damage.");
}
static void drm_test_damage_iter_single_damage_src_moved(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_property_blob damage_blob;
struct drm_mode_rect damage;
struct drm_rect clip;
u32 num_hits = 0;
/* Plane src moved since old plane state. */
set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 10 << 16, 10 << 16,
(10 + 1024) << 16, (10 + 768) << 16);
set_damage_clip(&damage, 20, 30, 256, 256);
set_damage_blob(&damage_blob, &damage, sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 1,
"Should return plane src as damage.");
check_damage_clip(test, &clip, 10, 10, 1034, 778);
}
static void drm_test_damage_iter_single_damage_fractional_src_moved(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_property_blob damage_blob;
struct drm_mode_rect damage;
struct drm_rect clip;
u32 num_hits = 0;
/* Plane src with fractional part moved since old plane state. */
set_plane_src(&mock->old_state, 0x3fffe, 0x3fffe,
0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
set_plane_src(&mock->state, 0x40002, 0x40002,
0x40002 + (1024 << 16), 0x40002 + (768 << 16));
/* Damage intersect with plane src. */
set_damage_clip(&damage, 20, 30, 1360, 256);
set_damage_blob(&damage_blob, &damage, sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 1,
"Should return rounded off plane as damage.");
check_damage_clip(test, &clip, 4, 4, 1029, 773);
}
static void drm_test_damage_iter_damage(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_property_blob damage_blob;
struct drm_mode_rect damage[2];
struct drm_rect clip;
u32 num_hits = 0;
set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
/* 2 damage clips. */
set_damage_clip(&damage[0], 20, 30, 200, 180);
set_damage_clip(&damage[1], 240, 200, 280, 250);
set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip) {
if (num_hits == 0)
check_damage_clip(test, &clip, 20, 30, 200, 180);
if (num_hits == 1)
check_damage_clip(test, &clip, 240, 200, 280, 250);
num_hits++;
}
KUNIT_EXPECT_EQ_MSG(test, num_hits, 2, "Should return damage when set.");
}
static void drm_test_damage_iter_damage_one_intersect(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_property_blob damage_blob;
struct drm_mode_rect damage[2];
struct drm_rect clip;
u32 num_hits = 0;
set_plane_src(&mock->old_state, 0x40002, 0x40002,
0x40002 + (1024 << 16), 0x40002 + (768 << 16));
set_plane_src(&mock->state, 0x40002, 0x40002,
0x40002 + (1024 << 16), 0x40002 + (768 << 16));
/* 2 damage clips, one intersect plane src. */
set_damage_clip(&damage[0], 20, 30, 200, 180);
set_damage_clip(&damage[1], 2, 2, 1360, 1360);
set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip) {
if (num_hits == 0)
check_damage_clip(test, &clip, 20, 30, 200, 180);
if (num_hits == 1)
check_damage_clip(test, &clip, 4, 4, 1029, 773);
num_hits++;
}
KUNIT_EXPECT_EQ_MSG(test, num_hits, 2, "Should return damage when set.");
}
static void drm_test_damage_iter_damage_one_outside(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_property_blob damage_blob;
struct drm_mode_rect damage[2];
struct drm_rect clip;
u32 num_hits = 0;
set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
/* 2 damage clips, one outside plane src. */
set_damage_clip(&damage[0], 1360, 1360, 1380, 1380);
set_damage_clip(&damage[1], 240, 200, 280, 250);
set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return damage when set.");
check_damage_clip(test, &clip, 240, 200, 280, 250);
}
static void drm_test_damage_iter_damage_src_moved(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_property_blob damage_blob;
struct drm_mode_rect damage[2];
struct drm_rect clip;
u32 num_hits = 0;
set_plane_src(&mock->old_state, 0x40002, 0x40002,
0x40002 + (1024 << 16), 0x40002 + (768 << 16));
set_plane_src(&mock->state, 0x3fffe, 0x3fffe,
0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
/* 2 damage clips, one outside plane src. */
set_damage_clip(&damage[0], 1360, 1360, 1380, 1380);
set_damage_clip(&damage[1], 240, 200, 280, 250);
set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 1,
"Should return round off plane src as damage.");
check_damage_clip(test, &clip, 3, 3, 1028, 772);
}
static void drm_test_damage_iter_damage_not_visible(struct kunit *test)
{
struct drm_damage_mock *mock = test->priv;
struct drm_atomic_helper_damage_iter iter;
struct drm_property_blob damage_blob;
struct drm_mode_rect damage[2];
struct drm_rect clip;
u32 num_hits = 0;
mock->state.visible = false;
set_plane_src(&mock->old_state, 0x40002, 0x40002,
0x40002 + (1024 << 16), 0x40002 + (768 << 16));
set_plane_src(&mock->state, 0x3fffe, 0x3fffe,
0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
/* 2 damage clips, one outside plane src. */
set_damage_clip(&damage[0], 1360, 1360, 1380, 1380);
set_damage_clip(&damage[1], 240, 200, 280, 250);
set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should not return any damage.");
}
static struct kunit_case drm_damage_helper_tests[] = {
KUNIT_CASE(drm_test_damage_iter_no_damage),
KUNIT_CASE(drm_test_damage_iter_no_damage_fractional_src),
KUNIT_CASE(drm_test_damage_iter_no_damage_src_moved),
KUNIT_CASE(drm_test_damage_iter_no_damage_fractional_src_moved),
KUNIT_CASE(drm_test_damage_iter_no_damage_not_visible),
KUNIT_CASE(drm_test_damage_iter_no_damage_no_crtc),
KUNIT_CASE(drm_test_damage_iter_no_damage_no_fb),
KUNIT_CASE(drm_test_damage_iter_simple_damage),
KUNIT_CASE(drm_test_damage_iter_single_damage),
KUNIT_CASE(drm_test_damage_iter_single_damage_intersect_src),
KUNIT_CASE(drm_test_damage_iter_single_damage_outside_src),
KUNIT_CASE(drm_test_damage_iter_single_damage_fractional_src),
KUNIT_CASE(drm_test_damage_iter_single_damage_intersect_fractional_src),
KUNIT_CASE(drm_test_damage_iter_single_damage_outside_fractional_src),
KUNIT_CASE(drm_test_damage_iter_single_damage_src_moved),
KUNIT_CASE(drm_test_damage_iter_single_damage_fractional_src_moved),
KUNIT_CASE(drm_test_damage_iter_damage),
KUNIT_CASE(drm_test_damage_iter_damage_one_intersect),
KUNIT_CASE(drm_test_damage_iter_damage_one_outside),
KUNIT_CASE(drm_test_damage_iter_damage_src_moved),
KUNIT_CASE(drm_test_damage_iter_damage_not_visible),
{ }
};
static struct kunit_suite drm_damage_helper_test_suite = {
.name = "drm_damage_helper",
.init = drm_damage_helper_init,
.test_cases = drm_damage_helper_tests,
};
kunit_test_suite(drm_damage_helper_test_suite);
MODULE_DESCRIPTION("Test case for drm_damage_helper functions");
MODULE_LICENSE("GPL");
@@ -0,0 +1,577 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Test cases for the DRM DP MST helpers
*
* Copyright (c) 2022 Maíra Canal <mairacanal@riseup.net>
*/
#include <kunit/test.h>
#include <drm/display/drm_dp_mst_helper.h>
#include <drm/drm_print.h>
#include "../display/drm_dp_mst_topology_internal.h"
struct drm_dp_mst_calc_pbn_mode_test {
const int clock;
const int bpp;
const bool dsc;
const int expected;
};
static const struct drm_dp_mst_calc_pbn_mode_test drm_dp_mst_calc_pbn_mode_cases[] = {
{
.clock = 154000,
.bpp = 30,
.dsc = false,
.expected = 689
},
{
.clock = 234000,
.bpp = 30,
.dsc = false,
.expected = 1047
},
{
.clock = 297000,
.bpp = 24,
.dsc = false,
.expected = 1063
},
{
.clock = 332880,
.bpp = 24,
.dsc = true,
.expected = 1191
},
{
.clock = 324540,
.bpp = 24,
.dsc = true,
.expected = 1161
},
};
static void drm_test_dp_mst_calc_pbn_mode(struct kunit *test)
{
const struct drm_dp_mst_calc_pbn_mode_test *params = test->param_value;
KUNIT_EXPECT_EQ(test, drm_dp_calc_pbn_mode(params->clock, params->bpp << 4),
params->expected);
}
static void dp_mst_calc_pbn_mode_desc(const struct drm_dp_mst_calc_pbn_mode_test *t, char *desc)
{
sprintf(desc, "Clock %d BPP %d DSC %s", t->clock, t->bpp, t->dsc ? "enabled" : "disabled");
}
KUNIT_ARRAY_PARAM(drm_dp_mst_calc_pbn_mode, drm_dp_mst_calc_pbn_mode_cases,
dp_mst_calc_pbn_mode_desc);
struct drm_dp_mst_calc_pbn_div_test {
int link_rate;
int lane_count;
fixed20_12 expected;
};
#define fp_init(__int, __frac) { \
.full = (__int) * (1 << 12) + \
(__frac) * (1 << 12) / 100000 \
}
static const struct drm_dp_mst_calc_pbn_div_test drm_dp_mst_calc_pbn_div_dp1_4_cases[] = {
/*
* UHBR rates (DP Standard v2.1 2.7.6.3, specifying the rounded to
* closest value to 2 decimal places):
* .expected = .link_rate * .lane_count * 0.9671 / 8 / 54 / 100
* DP1.4 rates (DP Standard v2.1 2.6.4.2):
* .expected = .link_rate * .lane_count * 0.8000 / 8 / 54 / 100
*
* truncated to 5 decimal places.
*/
{
.link_rate = 2000000,
.lane_count = 4,
.expected = fp_init(179, 9259), /* 179.09259 */
},
{
.link_rate = 2000000,
.lane_count = 2,
.expected = fp_init(89, 54629),
},
{
.link_rate = 2000000,
.lane_count = 1,
.expected = fp_init(44, 77314),
},
{
.link_rate = 1350000,
.lane_count = 4,
.expected = fp_init(120, 88750),
},
{
.link_rate = 1350000,
.lane_count = 2,
.expected = fp_init(60, 44375),
},
{
.link_rate = 1350000,
.lane_count = 1,
.expected = fp_init(30, 22187),
},
{
.link_rate = 1000000,
.lane_count = 4,
.expected = fp_init(89, 54629),
},
{
.link_rate = 1000000,
.lane_count = 2,
.expected = fp_init(44, 77314),
},
{
.link_rate = 1000000,
.lane_count = 1,
.expected = fp_init(22, 38657),
},
{
.link_rate = 810000,
.lane_count = 4,
.expected = fp_init(60, 0),
},
{
.link_rate = 810000,
.lane_count = 2,
.expected = fp_init(30, 0),
},
{
.link_rate = 810000,
.lane_count = 1,
.expected = fp_init(15, 0),
},
{
.link_rate = 540000,
.lane_count = 4,
.expected = fp_init(40, 0),
},
{
.link_rate = 540000,
.lane_count = 2,
.expected = fp_init(20, 0),
},
{
.link_rate = 540000,
.lane_count = 1,
.expected = fp_init(10, 0),
},
{
.link_rate = 270000,
.lane_count = 4,
.expected = fp_init(20, 0),
},
{
.link_rate = 270000,
.lane_count = 2,
.expected = fp_init(10, 0),
},
{
.link_rate = 270000,
.lane_count = 1,
.expected = fp_init(5, 0),
},
{
.link_rate = 162000,
.lane_count = 4,
.expected = fp_init(12, 0),
},
{
.link_rate = 162000,
.lane_count = 2,
.expected = fp_init(6, 0),
},
{
.link_rate = 162000,
.lane_count = 1,
.expected = fp_init(3, 0),
},
};
static void drm_test_dp_mst_calc_pbn_div(struct kunit *test)
{
const struct drm_dp_mst_calc_pbn_div_test *params = test->param_value;
KUNIT_EXPECT_EQ(test, drm_dp_get_vc_payload_bw(params->link_rate, params->lane_count).full,
params->expected.full);
}
static void dp_mst_calc_pbn_div_desc(const struct drm_dp_mst_calc_pbn_div_test *t, char *desc)
{
sprintf(desc, "Link rate %d lane count %d", t->link_rate, t->lane_count);
}
KUNIT_ARRAY_PARAM(drm_dp_mst_calc_pbn_div, drm_dp_mst_calc_pbn_div_dp1_4_cases,
dp_mst_calc_pbn_div_desc);
static u8 data[] = { 0xff, 0x00, 0xdd };
struct drm_dp_mst_sideband_msg_req_test {
const char *desc;
const struct drm_dp_sideband_msg_req_body in;
};
static const struct drm_dp_mst_sideband_msg_req_test drm_dp_mst_sideband_msg_req_cases[] = {
{
.desc = "DP_ENUM_PATH_RESOURCES with port number",
.in = {
.req_type = DP_ENUM_PATH_RESOURCES,
.u.port_num.port_number = 5,
},
},
{
.desc = "DP_POWER_UP_PHY with port number",
.in = {
.req_type = DP_POWER_UP_PHY,
.u.port_num.port_number = 5,
},
},
{
.desc = "DP_POWER_DOWN_PHY with port number",
.in = {
.req_type = DP_POWER_DOWN_PHY,
.u.port_num.port_number = 5,
},
},
{
.desc = "DP_ALLOCATE_PAYLOAD with SDP stream sinks",
.in = {
.req_type = DP_ALLOCATE_PAYLOAD,
.u.allocate_payload.number_sdp_streams = 3,
.u.allocate_payload.sdp_stream_sink = { 1, 2, 3 },
},
},
{
.desc = "DP_ALLOCATE_PAYLOAD with port number",
.in = {
.req_type = DP_ALLOCATE_PAYLOAD,
.u.allocate_payload.port_number = 0xf,
},
},
{
.desc = "DP_ALLOCATE_PAYLOAD with VCPI",
.in = {
.req_type = DP_ALLOCATE_PAYLOAD,
.u.allocate_payload.vcpi = 0x7f,
},
},
{
.desc = "DP_ALLOCATE_PAYLOAD with PBN",
.in = {
.req_type = DP_ALLOCATE_PAYLOAD,
.u.allocate_payload.pbn = U16_MAX,
},
},
{
.desc = "DP_QUERY_PAYLOAD with port number",
.in = {
.req_type = DP_QUERY_PAYLOAD,
.u.query_payload.port_number = 0xf,
},
},
{
.desc = "DP_QUERY_PAYLOAD with VCPI",
.in = {
.req_type = DP_QUERY_PAYLOAD,
.u.query_payload.vcpi = 0x7f,
},
},
{
.desc = "DP_REMOTE_DPCD_READ with port number",
.in = {
.req_type = DP_REMOTE_DPCD_READ,
.u.dpcd_read.port_number = 0xf,
},
},
{
.desc = "DP_REMOTE_DPCD_READ with DPCD address",
.in = {
.req_type = DP_REMOTE_DPCD_READ,
.u.dpcd_read.dpcd_address = 0xfedcb,
},
},
{
.desc = "DP_REMOTE_DPCD_READ with max number of bytes",
.in = {
.req_type = DP_REMOTE_DPCD_READ,
.u.dpcd_read.num_bytes = U8_MAX,
},
},
{
.desc = "DP_REMOTE_DPCD_WRITE with port number",
.in = {
.req_type = DP_REMOTE_DPCD_WRITE,
.u.dpcd_write.port_number = 0xf,
},
},
{
.desc = "DP_REMOTE_DPCD_WRITE with DPCD address",
.in = {
.req_type = DP_REMOTE_DPCD_WRITE,
.u.dpcd_write.dpcd_address = 0xfedcb,
},
},
{
.desc = "DP_REMOTE_DPCD_WRITE with data array",
.in = {
.req_type = DP_REMOTE_DPCD_WRITE,
.u.dpcd_write.num_bytes = ARRAY_SIZE(data),
.u.dpcd_write.bytes = data,
},
},
{
.desc = "DP_REMOTE_I2C_READ with port number",
.in = {
.req_type = DP_REMOTE_I2C_READ,
.u.i2c_read.port_number = 0xf,
},
},
{
.desc = "DP_REMOTE_I2C_READ with I2C device ID",
.in = {
.req_type = DP_REMOTE_I2C_READ,
.u.i2c_read.read_i2c_device_id = 0x7f,
},
},
{
.desc = "DP_REMOTE_I2C_READ with transactions array",
.in = {
.req_type = DP_REMOTE_I2C_READ,
.u.i2c_read.num_transactions = 3,
.u.i2c_read.num_bytes_read = ARRAY_SIZE(data) * 3,
.u.i2c_read.transactions = {
{ .bytes = data, .num_bytes = ARRAY_SIZE(data), .i2c_dev_id = 0x7f,
.i2c_transaction_delay = 0xf, },
{ .bytes = data, .num_bytes = ARRAY_SIZE(data), .i2c_dev_id = 0x7e,
.i2c_transaction_delay = 0xe, },
{ .bytes = data, .num_bytes = ARRAY_SIZE(data), .i2c_dev_id = 0x7d,
.i2c_transaction_delay = 0xd, },
},
},
},
{
.desc = "DP_REMOTE_I2C_WRITE with port number",
.in = {
.req_type = DP_REMOTE_I2C_WRITE,
.u.i2c_write.port_number = 0xf,
},
},
{
.desc = "DP_REMOTE_I2C_WRITE with I2C device ID",
.in = {
.req_type = DP_REMOTE_I2C_WRITE,
.u.i2c_write.write_i2c_device_id = 0x7f,
},
},
{
.desc = "DP_REMOTE_I2C_WRITE with data array",
.in = {
.req_type = DP_REMOTE_I2C_WRITE,
.u.i2c_write.num_bytes = ARRAY_SIZE(data),
.u.i2c_write.bytes = data,
},
},
{
.desc = "DP_QUERY_STREAM_ENC_STATUS with stream ID",
.in = {
.req_type = DP_QUERY_STREAM_ENC_STATUS,
.u.enc_status.stream_id = 1,
},
},
{
.desc = "DP_QUERY_STREAM_ENC_STATUS with client ID",
.in = {
.req_type = DP_QUERY_STREAM_ENC_STATUS,
.u.enc_status.client_id = { 0x4f, 0x7f, 0xb4, 0x00, 0x8c, 0x0d, 0x67 },
},
},
{
.desc = "DP_QUERY_STREAM_ENC_STATUS with stream event",
.in = {
.req_type = DP_QUERY_STREAM_ENC_STATUS,
.u.enc_status.stream_event = 3,
},
},
{
.desc = "DP_QUERY_STREAM_ENC_STATUS with valid stream event",
.in = {
.req_type = DP_QUERY_STREAM_ENC_STATUS,
.u.enc_status.valid_stream_event = 0,
},
},
{
.desc = "DP_QUERY_STREAM_ENC_STATUS with stream behavior",
.in = {
.req_type = DP_QUERY_STREAM_ENC_STATUS,
.u.enc_status.stream_behavior = 3,
},
},
{
.desc = "DP_QUERY_STREAM_ENC_STATUS with a valid stream behavior",
.in = {
.req_type = DP_QUERY_STREAM_ENC_STATUS,
.u.enc_status.valid_stream_behavior = 1,
}
},
};
static bool
sideband_msg_req_equal(const struct drm_dp_sideband_msg_req_body *in,
const struct drm_dp_sideband_msg_req_body *out)
{
const struct drm_dp_remote_i2c_read_tx *txin, *txout;
int i;
if (in->req_type != out->req_type)
return false;
switch (in->req_type) {
/*
* Compare struct members manually for request types which can't be
* compared simply using memcmp(). This is because said request types
* contain pointers to other allocated structs
*/
case DP_REMOTE_I2C_READ:
#define IN in->u.i2c_read
#define OUT out->u.i2c_read
if (IN.num_bytes_read != OUT.num_bytes_read ||
IN.num_transactions != OUT.num_transactions ||
IN.port_number != OUT.port_number ||
IN.read_i2c_device_id != OUT.read_i2c_device_id)
return false;
for (i = 0; i < IN.num_transactions; i++) {
txin = &IN.transactions[i];
txout = &OUT.transactions[i];
if (txin->i2c_dev_id != txout->i2c_dev_id ||
txin->no_stop_bit != txout->no_stop_bit ||
txin->num_bytes != txout->num_bytes ||
txin->i2c_transaction_delay !=
txout->i2c_transaction_delay)
return false;
if (memcmp(txin->bytes, txout->bytes,
txin->num_bytes) != 0)
return false;
}
break;
#undef IN
#undef OUT
case DP_REMOTE_DPCD_WRITE:
#define IN in->u.dpcd_write
#define OUT out->u.dpcd_write
if (IN.dpcd_address != OUT.dpcd_address ||
IN.num_bytes != OUT.num_bytes ||
IN.port_number != OUT.port_number)
return false;
return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0;
#undef IN
#undef OUT
case DP_REMOTE_I2C_WRITE:
#define IN in->u.i2c_write
#define OUT out->u.i2c_write
if (IN.port_number != OUT.port_number ||
IN.write_i2c_device_id != OUT.write_i2c_device_id ||
IN.num_bytes != OUT.num_bytes)
return false;
return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0;
#undef IN
#undef OUT
default:
return memcmp(in, out, sizeof(*in)) == 0;
}
return true;
}
static void drm_test_dp_mst_msg_printf(struct drm_printer *p, struct va_format *vaf)
{
struct kunit *test = p->arg;
kunit_err(test, "%pV", vaf);
}
static void drm_test_dp_mst_sideband_msg_req_decode(struct kunit *test)
{
const struct drm_dp_mst_sideband_msg_req_test *params = test->param_value;
const struct drm_dp_sideband_msg_req_body *in = &params->in;
struct drm_dp_sideband_msg_req_body *out;
struct drm_dp_sideband_msg_tx *txmsg;
struct drm_printer p = {
.printfn = drm_test_dp_mst_msg_printf,
.arg = test
};
int i;
out = kunit_kzalloc(test, sizeof(*out), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, out);
txmsg = kunit_kzalloc(test, sizeof(*txmsg), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, txmsg);
drm_dp_encode_sideband_req(in, txmsg);
KUNIT_EXPECT_GE_MSG(test, drm_dp_decode_sideband_req(txmsg, out), 0,
"Failed to decode sideband request");
if (!sideband_msg_req_equal(in, out)) {
KUNIT_FAIL(test, "Encode/decode failed");
kunit_err(test, "Expected:");
drm_dp_dump_sideband_msg_req_body(in, 1, &p);
kunit_err(test, "Got:");
drm_dp_dump_sideband_msg_req_body(out, 1, &p);
}
switch (in->req_type) {
case DP_REMOTE_DPCD_WRITE:
kfree(out->u.dpcd_write.bytes);
break;
case DP_REMOTE_I2C_READ:
for (i = 0; i < out->u.i2c_read.num_transactions; i++)
kfree(out->u.i2c_read.transactions[i].bytes);
break;
case DP_REMOTE_I2C_WRITE:
kfree(out->u.i2c_write.bytes);
break;
}
}
static void
drm_dp_mst_sideband_msg_req_desc(const struct drm_dp_mst_sideband_msg_req_test *t, char *desc)
{
strcpy(desc, t->desc);
}
KUNIT_ARRAY_PARAM(drm_dp_mst_sideband_msg_req, drm_dp_mst_sideband_msg_req_cases,
drm_dp_mst_sideband_msg_req_desc);
static struct kunit_case drm_dp_mst_helper_tests[] = {
KUNIT_CASE_PARAM(drm_test_dp_mst_calc_pbn_mode, drm_dp_mst_calc_pbn_mode_gen_params),
KUNIT_CASE_PARAM(drm_test_dp_mst_calc_pbn_div, drm_dp_mst_calc_pbn_div_gen_params),
KUNIT_CASE_PARAM(drm_test_dp_mst_sideband_msg_req_decode,
drm_dp_mst_sideband_msg_req_gen_params),
{ }
};
static struct kunit_suite drm_dp_mst_helper_test_suite = {
.name = "drm_dp_mst_helper",
.test_cases = drm_dp_mst_helper_tests,
};
kunit_test_suite(drm_dp_mst_helper_test_suite);
MODULE_DESCRIPTION("Test cases for the DRM DP MST helpers");
MODULE_LICENSE("GPL");
@@ -0,0 +1,228 @@
// SPDX-License-Identifier: MIT
/*
* Copyright 2022 Advanced Micro Devices, Inc.
*/
#define pr_fmt(fmt) "drm_exec: " fmt
#include <kunit/test.h>
#include <linux/module.h>
#include <linux/prime_numbers.h>
#include <drm/drm_exec.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem.h>
#include <drm/drm_kunit_helpers.h>
struct drm_exec_priv {
struct device *dev;
struct drm_device *drm;
};
static int drm_exec_test_init(struct kunit *test)
{
struct drm_exec_priv *priv;
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_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);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
return 0;
}
static void sanitycheck(struct kunit *test)
{
struct drm_exec exec;
drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
drm_exec_fini(&exec);
KUNIT_SUCCEED(test);
}
static void test_lock(struct kunit *test)
{
struct drm_exec_priv *priv = test->priv;
struct drm_gem_object gobj = { };
struct drm_exec exec;
int ret;
drm_gem_private_object_init(priv->drm, &gobj, PAGE_SIZE);
drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
drm_exec_until_all_locked(&exec) {
ret = drm_exec_lock_obj(&exec, &gobj);
drm_exec_retry_on_contention(&exec);
KUNIT_EXPECT_EQ(test, ret, 0);
if (ret)
break;
}
drm_exec_fini(&exec);
}
static void test_lock_unlock(struct kunit *test)
{
struct drm_exec_priv *priv = test->priv;
struct drm_gem_object gobj = { };
struct drm_exec exec;
int ret;
drm_gem_private_object_init(priv->drm, &gobj, PAGE_SIZE);
drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
drm_exec_until_all_locked(&exec) {
ret = drm_exec_lock_obj(&exec, &gobj);
drm_exec_retry_on_contention(&exec);
KUNIT_EXPECT_EQ(test, ret, 0);
if (ret)
break;
drm_exec_unlock_obj(&exec, &gobj);
ret = drm_exec_lock_obj(&exec, &gobj);
drm_exec_retry_on_contention(&exec);
KUNIT_EXPECT_EQ(test, ret, 0);
if (ret)
break;
}
drm_exec_fini(&exec);
}
static void test_duplicates(struct kunit *test)
{
struct drm_exec_priv *priv = test->priv;
struct drm_gem_object gobj = { };
struct drm_exec exec;
int ret;
drm_gem_private_object_init(priv->drm, &gobj, PAGE_SIZE);
drm_exec_init(&exec, DRM_EXEC_IGNORE_DUPLICATES, 0);
drm_exec_until_all_locked(&exec) {
ret = drm_exec_lock_obj(&exec, &gobj);
drm_exec_retry_on_contention(&exec);
KUNIT_EXPECT_EQ(test, ret, 0);
if (ret)
break;
ret = drm_exec_lock_obj(&exec, &gobj);
drm_exec_retry_on_contention(&exec);
KUNIT_EXPECT_EQ(test, ret, 0);
if (ret)
break;
}
drm_exec_unlock_obj(&exec, &gobj);
drm_exec_fini(&exec);
}
static void test_prepare(struct kunit *test)
{
struct drm_exec_priv *priv = test->priv;
struct drm_gem_object gobj = { };
struct drm_exec exec;
int ret;
drm_gem_private_object_init(priv->drm, &gobj, PAGE_SIZE);
drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
drm_exec_until_all_locked(&exec) {
ret = drm_exec_prepare_obj(&exec, &gobj, 1);
drm_exec_retry_on_contention(&exec);
KUNIT_EXPECT_EQ(test, ret, 0);
if (ret)
break;
}
drm_exec_fini(&exec);
drm_gem_private_object_fini(&gobj);
}
static void test_prepare_array(struct kunit *test)
{
struct drm_exec_priv *priv = test->priv;
struct drm_gem_object *gobj1;
struct drm_gem_object *gobj2;
struct drm_gem_object *array[] = {
(gobj1 = kunit_kzalloc(test, sizeof(*gobj1), GFP_KERNEL)),
(gobj2 = kunit_kzalloc(test, sizeof(*gobj2), GFP_KERNEL)),
};
struct drm_exec exec;
int ret;
if (!gobj1 || !gobj2) {
KUNIT_FAIL(test, "Failed to allocate GEM objects.\n");
return;
}
drm_gem_private_object_init(priv->drm, gobj1, PAGE_SIZE);
drm_gem_private_object_init(priv->drm, gobj2, PAGE_SIZE);
drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
drm_exec_until_all_locked(&exec)
ret = drm_exec_prepare_array(&exec, array, ARRAY_SIZE(array),
1);
KUNIT_EXPECT_EQ(test, ret, 0);
drm_exec_fini(&exec);
drm_gem_private_object_fini(gobj1);
drm_gem_private_object_fini(gobj2);
}
static void test_multiple_loops(struct kunit *test)
{
struct drm_exec exec;
{
__label__ drm_exec_retry;
drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
drm_exec_until_all_locked(&exec)
{
break;
}
drm_exec_fini(&exec);
}
{
__label__ drm_exec_retry;
drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
drm_exec_until_all_locked(&exec)
{
break;
}
drm_exec_fini(&exec);
}
KUNIT_SUCCEED(test);
}
static struct kunit_case drm_exec_tests[] = {
KUNIT_CASE(sanitycheck),
KUNIT_CASE(test_lock),
KUNIT_CASE(test_lock_unlock),
KUNIT_CASE(test_duplicates),
KUNIT_CASE(test_prepare),
KUNIT_CASE(test_prepare_array),
KUNIT_CASE(test_multiple_loops),
{}
};
static struct kunit_suite drm_exec_test_suite = {
.name = "drm_exec",
.init = drm_exec_test_init,
.test_cases = drm_exec_tests,
};
kunit_test_suite(drm_exec_test_suite);
MODULE_AUTHOR("AMD");
MODULE_DESCRIPTION("Kunit test for drm_exec functions");
MODULE_LICENSE("GPL and additional rights");
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: MIT
/*
* Copyright 2022 Advanced Micro Devices, Inc.
*/
#include <kunit/test.h>
#include <drm/drm_fixed.h>
static void drm_test_sm2fixp(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, 0x7fffffffffffffffll, ((1ull << 63) - 1));
/* 1 */
KUNIT_EXPECT_EQ(test, drm_int2fixp(1), drm_sm2fixp(1ull << DRM_FIXED_POINT));
/* -1 */
KUNIT_EXPECT_EQ(test, drm_int2fixp(-1),
drm_sm2fixp((1ull << 63) | (1ull << DRM_FIXED_POINT)));
/* 0.5 */
KUNIT_EXPECT_EQ(test, drm_fixp_from_fraction(1, 2),
drm_sm2fixp(1ull << (DRM_FIXED_POINT - 1)));
/* -0.5 */
KUNIT_EXPECT_EQ(test, drm_fixp_from_fraction(-1, 2),
drm_sm2fixp((1ull << 63) | (1ull << (DRM_FIXED_POINT - 1))));
}
static void drm_test_int2fixp(struct kunit *test)
{
/* 1 */
KUNIT_EXPECT_EQ(test, 1ll << 32, drm_int2fixp(1));
/* -1 */
KUNIT_EXPECT_EQ(test, -(1ll << 32), drm_int2fixp(-1));
/* 1 + (-1) = 0 */
KUNIT_EXPECT_EQ(test, 0, drm_int2fixp(1) + drm_int2fixp(-1));
/* 1 / 2 */
KUNIT_EXPECT_EQ(test, 1ll << 31, drm_fixp_from_fraction(1, 2));
/* -0.5 */
KUNIT_EXPECT_EQ(test, -(1ll << 31), drm_fixp_from_fraction(-1, 2));
/* (1 / 2) + (-1) = 0.5 */
KUNIT_EXPECT_EQ(test, 1ll << 31, drm_fixp_from_fraction(-1, 2) + drm_int2fixp(1));
/* (1 / 2) - 1) = 0.5 */
KUNIT_EXPECT_EQ(test, -(1ll << 31), drm_fixp_from_fraction(1, 2) + drm_int2fixp(-1));
/* (1 / 2) - 1) = 0.5 */
KUNIT_EXPECT_EQ(test, -(1ll << 31), drm_fixp_from_fraction(1, 2) - drm_int2fixp(1));
}
static struct kunit_case drm_fixp_tests[] = {
KUNIT_CASE(drm_test_int2fixp),
KUNIT_CASE(drm_test_sm2fixp),
{ }
};
static struct kunit_suite drm_fixp_test_suite = {
.name = "drm_fixp",
.test_cases = drm_fixp_tests,
};
kunit_test_suite(drm_fixp_test_suite);
MODULE_AUTHOR("AMD");
MODULE_LICENSE("Dual MIT/GPL");
MODULE_DESCRIPTION("Unit tests for drm_fixed.h");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,360 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Test cases for the drm_format functions
*
* Copyright (c) 2022 Maíra Canal <mairacanal@riseup.net>
*/
#include <kunit/test.h>
#include <drm/drm_fourcc.h>
static void drm_test_format_block_width_invalid(struct kunit *test)
{
const struct drm_format_info *info = NULL;
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, -1), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 1), 0);
}
static void drm_test_format_block_width_one_plane(struct kunit *test)
{
const struct drm_format_info *info = drm_format_info(DRM_FORMAT_XRGB4444);
KUNIT_ASSERT_NOT_NULL(test, info);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 1), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, -1), 0);
}
static void drm_test_format_block_width_two_plane(struct kunit *test)
{
const struct drm_format_info *info = drm_format_info(DRM_FORMAT_NV12);
KUNIT_ASSERT_NOT_NULL(test, info);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 1), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 2), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, -1), 0);
}
static void drm_test_format_block_width_three_plane(struct kunit *test)
{
const struct drm_format_info *info = drm_format_info(DRM_FORMAT_YUV422);
KUNIT_ASSERT_NOT_NULL(test, info);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 1), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 2), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 3), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, -1), 0);
}
static void drm_test_format_block_width_tiled(struct kunit *test)
{
const struct drm_format_info *info = drm_format_info(DRM_FORMAT_X0L0);
KUNIT_ASSERT_NOT_NULL(test, info);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 2);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 1), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, -1), 0);
}
static void drm_test_format_block_height_invalid(struct kunit *test)
{
const struct drm_format_info *info = NULL;
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, -1), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, 1), 0);
}
static void drm_test_format_block_height_one_plane(struct kunit *test)
{
const struct drm_format_info *info = drm_format_info(DRM_FORMAT_XRGB4444);
KUNIT_ASSERT_NOT_NULL(test, info);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, 0), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, -1), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, 1), 0);
}
static void drm_test_format_block_height_two_plane(struct kunit *test)
{
const struct drm_format_info *info = drm_format_info(DRM_FORMAT_NV12);
KUNIT_ASSERT_NOT_NULL(test, info);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, 0), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, 1), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, 2), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, -1), 0);
}
static void drm_test_format_block_height_three_plane(struct kunit *test)
{
const struct drm_format_info *info = drm_format_info(DRM_FORMAT_YUV422);
KUNIT_ASSERT_NOT_NULL(test, info);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, 0), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, 1), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, 2), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, 3), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, -1), 0);
}
static void drm_test_format_block_height_tiled(struct kunit *test)
{
const struct drm_format_info *info = drm_format_info(DRM_FORMAT_X0L0);
KUNIT_ASSERT_NOT_NULL(test, info);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, 0), 2);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, 1), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, -1), 0);
}
static void drm_test_format_min_pitch_invalid(struct kunit *test)
{
const struct drm_format_info *info = NULL;
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, -1, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 0), 0);
}
static void drm_test_format_min_pitch_one_plane_8bpp(struct kunit *test)
{
const struct drm_format_info *info = drm_format_info(DRM_FORMAT_RGB332);
KUNIT_ASSERT_NOT_NULL(test, info);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, -1, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 2), 2);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 640), 640);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1024), 1024);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1920), 1920);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 4096), 4096);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 671), 671);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, UINT_MAX),
(uint64_t)UINT_MAX);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, (UINT_MAX - 1)),
(uint64_t)(UINT_MAX - 1));
}
static void drm_test_format_min_pitch_one_plane_16bpp(struct kunit *test)
{
const struct drm_format_info *info = drm_format_info(DRM_FORMAT_XRGB4444);
KUNIT_ASSERT_NOT_NULL(test, info);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, -1, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1), 2);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 2), 4);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 640), 1280);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1024), 2048);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1920), 3840);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 4096), 8192);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 671), 1342);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, UINT_MAX),
(uint64_t)UINT_MAX * 2);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, (UINT_MAX - 1)),
(uint64_t)(UINT_MAX - 1) * 2);
}
static void drm_test_format_min_pitch_one_plane_24bpp(struct kunit *test)
{
const struct drm_format_info *info = drm_format_info(DRM_FORMAT_RGB888);
KUNIT_ASSERT_NOT_NULL(test, info);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, -1, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1), 3);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 2), 6);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 640), 1920);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1024), 3072);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1920), 5760);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 4096), 12288);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 671), 2013);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, UINT_MAX),
(uint64_t)UINT_MAX * 3);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, UINT_MAX - 1),
(uint64_t)(UINT_MAX - 1) * 3);
}
static void drm_test_format_min_pitch_one_plane_32bpp(struct kunit *test)
{
const struct drm_format_info *info = drm_format_info(DRM_FORMAT_ABGR8888);
KUNIT_ASSERT_NOT_NULL(test, info);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, -1, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1), 4);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 2), 8);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 640), 2560);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1024), 4096);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1920), 7680);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 4096), 16384);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 671), 2684);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, UINT_MAX),
(uint64_t)UINT_MAX * 4);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, UINT_MAX - 1),
(uint64_t)(UINT_MAX - 1) * 4);
}
static void drm_test_format_min_pitch_two_plane(struct kunit *test)
{
const struct drm_format_info *info = drm_format_info(DRM_FORMAT_NV12);
KUNIT_ASSERT_NOT_NULL(test, info);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, -1, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 1), 2);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 2), 2);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 1), 2);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 640), 640);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 320), 640);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1024), 1024);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 512), 1024);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1920), 1920);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 960), 1920);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 4096), 4096);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 2048), 4096);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 671), 671);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 336), 672);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, UINT_MAX),
(uint64_t)UINT_MAX);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, UINT_MAX / 2 + 1),
(uint64_t)UINT_MAX + 1);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, (UINT_MAX - 1)),
(uint64_t)(UINT_MAX - 1));
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, (UINT_MAX - 1) / 2),
(uint64_t)(UINT_MAX - 1));
}
static void drm_test_format_min_pitch_three_plane_8bpp(struct kunit *test)
{
const struct drm_format_info *info = drm_format_info(DRM_FORMAT_YUV422);
KUNIT_ASSERT_NOT_NULL(test, info);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, -1, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 3, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 1), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, 1), 1);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 2), 2);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 2), 2);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, 2), 2);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 640), 640);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 320), 320);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, 320), 320);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1024), 1024);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 512), 512);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, 512), 512);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1920), 1920);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 960), 960);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, 960), 960);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 4096), 4096);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 2048), 2048);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, 2048), 2048);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 671), 671);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 336), 336);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, 336), 336);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, UINT_MAX),
(uint64_t)UINT_MAX);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, UINT_MAX / 2 + 1),
(uint64_t)UINT_MAX / 2 + 1);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, UINT_MAX / 2 + 1),
(uint64_t)UINT_MAX / 2 + 1);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, (UINT_MAX - 1) / 2),
(uint64_t)(UINT_MAX - 1) / 2);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, (UINT_MAX - 1) / 2),
(uint64_t)(UINT_MAX - 1) / 2);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, (UINT_MAX - 1) / 2),
(uint64_t)(UINT_MAX - 1) / 2);
}
static void drm_test_format_min_pitch_tiled(struct kunit *test)
{
const struct drm_format_info *info = drm_format_info(DRM_FORMAT_X0L2);
KUNIT_ASSERT_NOT_NULL(test, info);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, -1, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 0), 0);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1), 2);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 2), 4);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 640), 1280);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1024), 2048);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1920), 3840);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 4096), 8192);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 671), 1342);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, UINT_MAX),
(uint64_t)UINT_MAX * 2);
KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, UINT_MAX - 1),
(uint64_t)(UINT_MAX - 1) * 2);
}
static struct kunit_case drm_format_tests[] = {
KUNIT_CASE(drm_test_format_block_width_invalid),
KUNIT_CASE(drm_test_format_block_width_one_plane),
KUNIT_CASE(drm_test_format_block_width_two_plane),
KUNIT_CASE(drm_test_format_block_width_three_plane),
KUNIT_CASE(drm_test_format_block_width_tiled),
KUNIT_CASE(drm_test_format_block_height_invalid),
KUNIT_CASE(drm_test_format_block_height_one_plane),
KUNIT_CASE(drm_test_format_block_height_two_plane),
KUNIT_CASE(drm_test_format_block_height_three_plane),
KUNIT_CASE(drm_test_format_block_height_tiled),
KUNIT_CASE(drm_test_format_min_pitch_invalid),
KUNIT_CASE(drm_test_format_min_pitch_one_plane_8bpp),
KUNIT_CASE(drm_test_format_min_pitch_one_plane_16bpp),
KUNIT_CASE(drm_test_format_min_pitch_one_plane_24bpp),
KUNIT_CASE(drm_test_format_min_pitch_one_plane_32bpp),
KUNIT_CASE(drm_test_format_min_pitch_two_plane),
KUNIT_CASE(drm_test_format_min_pitch_three_plane_8bpp),
KUNIT_CASE(drm_test_format_min_pitch_tiled),
{}
};
static struct kunit_suite drm_format_test_suite = {
.name = "drm_format",
.test_cases = drm_format_tests,
};
kunit_test_suite(drm_format_test_suite);
MODULE_DESCRIPTION("Test cases for the drm_format functions");
MODULE_LICENSE("GPL");
@@ -0,0 +1,725 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Test cases for the drm_framebuffer functions
*
* Copyright (c) 2022 Maíra Canal <mairacanal@riseup.net>
*/
#include <kunit/device.h>
#include <kunit/test.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_mode.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_kunit_helpers.h>
#include <drm/drm_print.h>
#include "../drm_crtc_internal.h"
#define MIN_WIDTH 4
#define MAX_WIDTH 4096
#define MIN_HEIGHT 4
#define MAX_HEIGHT 4096
#define DRM_MODE_FB_INVALID BIT(2)
struct drm_framebuffer_test {
int buffer_created;
struct drm_mode_fb_cmd2 cmd;
const char *name;
};
static const struct drm_framebuffer_test drm_framebuffer_create_cases[] = {
{ .buffer_created = 1, .name = "ABGR8888 normal sizes",
.cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .pitches = { 4 * 600, 0, 0 },
}
},
{ .buffer_created = 1, .name = "ABGR8888 max sizes",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
}
},
{ .buffer_created = 1, .name = "ABGR8888 pitch greater than min required",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH + 1, 0, 0 },
}
},
{ .buffer_created = 0, .name = "ABGR8888 pitch less than min required",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH - 1, 0, 0 },
}
},
{ .buffer_created = 0, .name = "ABGR8888 Invalid width",
.cmd = { .width = MAX_WIDTH + 1, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .pitches = { 4 * (MAX_WIDTH + 1), 0, 0 },
}
},
{ .buffer_created = 0, .name = "ABGR8888 Invalid buffer handle",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 0, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
}
},
{ .buffer_created = 0, .name = "No pixel format",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = 0,
.handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
}
},
{ .buffer_created = 0, .name = "ABGR8888 Width 0",
.cmd = { .width = 0, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
}
},
{ .buffer_created = 0, .name = "ABGR8888 Height 0",
.cmd = { .width = MAX_WIDTH, .height = 0, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
}
},
{ .buffer_created = 0, .name = "ABGR8888 Out of bound height * pitch combination",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .offsets = { UINT_MAX - 1, 0, 0 },
.pitches = { 4 * MAX_WIDTH, 0, 0 },
}
},
{ .buffer_created = 1, .name = "ABGR8888 Large buffer offset",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },
.pitches = { 4 * MAX_WIDTH, 0, 0 },
}
},
/*
* All entries in members that represents per-plane values (@modifier, @handles,
* @pitches and @offsets) must be zero when unused.
*/
{ .buffer_created = 0, .name = "ABGR8888 Buffer offset for inexistent plane",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, UINT_MAX / 2, 0 },
.pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
}
},
{ .buffer_created = 0, .name = "ABGR8888 Invalid flag",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },
.pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_INVALID,
}
},
{ .buffer_created = 1, .name = "ABGR8888 Set DRM_MODE_FB_MODIFIERS without modifiers",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },
.pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
}
},
{ .buffer_created = 1, .name = "ABGR8888 Valid buffer modifier",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },
.pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
.modifier = { AFBC_FORMAT_MOD_YTR, 0, 0 },
}
},
{ .buffer_created = 0,
.name = "ABGR8888 Invalid buffer modifier(DRM_FORMAT_MOD_SAMSUNG_64_32_TILE)",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },
.pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
.modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 },
}
},
{ .buffer_created = 1, .name = "ABGR8888 Extra pitches without DRM_MODE_FB_MODIFIERS",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },
.pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 },
}
},
{ .buffer_created = 0, .name = "ABGR8888 Extra pitches with DRM_MODE_FB_MODIFIERS",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
.pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 },
}
},
{ .buffer_created = 1, .name = "NV12 Normal sizes",
.cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12,
.handles = { 1, 1, 0 }, .pitches = { 600, 600, 0 },
}
},
{ .buffer_created = 1, .name = "NV12 Max sizes",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
.handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
}
},
{ .buffer_created = 0, .name = "NV12 Invalid pitch",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
.handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH - 1, 0 },
}
},
{ .buffer_created = 0, .name = "NV12 Invalid modifier/missing DRM_MODE_FB_MODIFIERS flag",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
.handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 },
.pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
}
},
{ .buffer_created = 0, .name = "NV12 different modifier per-plane",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
.handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
.modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 },
.pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
}
},
{ .buffer_created = 1, .name = "NV12 with DRM_FORMAT_MOD_SAMSUNG_64_32_TILE",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
.handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
.modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE,
DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 },
.pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
}
},
{ .buffer_created = 0, .name = "NV12 Valid modifiers without DRM_MODE_FB_MODIFIERS",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
.handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE,
DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 },
.pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
}
},
{ .buffer_created = 0, .name = "NV12 Modifier for inexistent plane",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
.handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
.modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, DRM_FORMAT_MOD_SAMSUNG_64_32_TILE,
DRM_FORMAT_MOD_SAMSUNG_64_32_TILE },
.pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
}
},
{ .buffer_created = 0, .name = "NV12 Handle for inexistent plane",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
.handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,
.pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
}
},
{ .buffer_created = 1, .name = "NV12 Handle for inexistent plane without DRM_MODE_FB_MODIFIERS",
.cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12,
.handles = { 1, 1, 1 }, .pitches = { 600, 600, 600 },
}
},
{ .buffer_created = 1, .name = "YVU420 DRM_MODE_FB_MODIFIERS set without modifier",
.cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420,
.handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,
.pitches = { 600, 300, 300 },
}
},
{ .buffer_created = 1, .name = "YVU420 Normal sizes",
.cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420,
.handles = { 1, 1, 1 }, .pitches = { 600, 300, 300 },
}
},
{ .buffer_created = 1, .name = "YVU420 Max sizes",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
.handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2),
DIV_ROUND_UP(MAX_WIDTH, 2) },
}
},
{ .buffer_created = 0, .name = "YVU420 Invalid pitch",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
.handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) - 1,
DIV_ROUND_UP(MAX_WIDTH, 2) },
}
},
{ .buffer_created = 1, .name = "YVU420 Different pitches",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
.handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1,
DIV_ROUND_UP(MAX_WIDTH, 2) + 7 },
}
},
{ .buffer_created = 1, .name = "YVU420 Different buffer offsets/pitches",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
.handles = { 1, 1, 1 }, .offsets = { MAX_WIDTH, MAX_WIDTH +
MAX_WIDTH * MAX_HEIGHT, MAX_WIDTH + 2 * MAX_WIDTH * MAX_HEIGHT },
.pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1,
DIV_ROUND_UP(MAX_WIDTH, 2) + 7 },
}
},
{ .buffer_created = 0,
.name = "YVU420 Modifier set just for plane 0, without DRM_MODE_FB_MODIFIERS",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
.handles = { 1, 1, 1 }, .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 },
.pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
}
},
{ .buffer_created = 0,
.name = "YVU420 Modifier set just for planes 0, 1, without DRM_MODE_FB_MODIFIERS",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
.handles = { 1, 1, 1 },
.modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 },
.pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
}
},
{ .buffer_created = 0,
.name = "YVU420 Modifier set just for plane 0, 1, with DRM_MODE_FB_MODIFIERS",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
.handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,
.modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 },
.pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
}
},
{ .buffer_created = 1, .name = "YVU420 Valid modifier",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
.handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,
.modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE,
AFBC_FORMAT_MOD_SPARSE },
.pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
}
},
{ .buffer_created = 0, .name = "YVU420 Different modifiers per plane",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
.handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,
.modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_YTR,
AFBC_FORMAT_MOD_SPARSE },
.pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
}
},
{ .buffer_created = 0, .name = "YVU420 Modifier for inexistent plane",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
.handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,
.modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE,
AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE },
.pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
}
},
{ .buffer_created = 0, .name = "YUV420_10BIT Invalid modifier(DRM_FORMAT_MOD_LINEAR)",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YUV420_10BIT,
.handles = { 1, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
.modifier = { DRM_FORMAT_MOD_LINEAR, 0, 0 },
.pitches = { MAX_WIDTH, 0, 0 },
}
},
{ .buffer_created = 1, .name = "X0L2 Normal sizes",
.cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_X0L2,
.handles = { 1, 0, 0 }, .pitches = { 1200, 0, 0 }
}
},
{ .buffer_created = 1, .name = "X0L2 Max sizes",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
.handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH, 0, 0 }
}
},
{ .buffer_created = 0, .name = "X0L2 Invalid pitch",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
.handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH - 1, 0, 0 }
}
},
{ .buffer_created = 1, .name = "X0L2 Pitch greater than minimum required",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
.handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }
}
},
{ .buffer_created = 0, .name = "X0L2 Handle for inexistent plane",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
.handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
.pitches = { 2 * MAX_WIDTH + 1, 0, 0 }
}
},
{ .buffer_created = 1,
.name = "X0L2 Offset for inexistent plane, without DRM_MODE_FB_MODIFIERS set",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
.handles = { 1, 0, 0 }, .offsets = { 0, 0, 3 },
.pitches = { 2 * MAX_WIDTH + 1, 0, 0 }
}
},
{ .buffer_created = 0, .name = "X0L2 Modifier without DRM_MODE_FB_MODIFIERS set",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
.handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 },
.modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 },
}
},
{ .buffer_created = 1, .name = "X0L2 Valid modifier",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
.handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 },
.modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
}
},
{ .buffer_created = 0, .name = "X0L2 Modifier for inexistent plane",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT,
.pixel_format = DRM_FORMAT_X0L2, .handles = { 1, 0, 0 },
.pitches = { 2 * MAX_WIDTH + 1, 0, 0 },
.modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 },
.flags = DRM_MODE_FB_MODIFIERS,
}
},
};
/*
* This struct is intended to provide a way to mocked functions communicate
* with the outer test when it can't be achieved by using its return value. In
* this way, the functions that receive the mocked drm_device, for example, can
* grab a reference to this and actually return something to be used on some
* expectation.
*/
struct drm_framebuffer_test_priv {
struct drm_device dev;
bool buffer_created;
bool buffer_freed;
};
static struct drm_framebuffer *fb_create_mock(struct drm_device *dev,
struct drm_file *file_priv,
const struct drm_format_info *info,
const struct drm_mode_fb_cmd2 *mode_cmd)
{
struct drm_framebuffer_test_priv *priv = container_of(dev, typeof(*priv), dev);
priv->buffer_created = true;
return ERR_PTR(-EINVAL);
}
static struct drm_mode_config_funcs mock_config_funcs = {
.fb_create = fb_create_mock,
};
static int drm_framebuffer_test_init(struct kunit *test)
{
struct device *parent;
struct drm_framebuffer_test_priv *priv;
struct drm_device *dev;
parent = drm_kunit_helper_alloc_device(test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
priv = drm_kunit_helper_alloc_drm_device(test, parent, typeof(*priv),
dev, 0);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
dev = &priv->dev;
dev->mode_config.min_width = MIN_WIDTH;
dev->mode_config.max_width = MAX_WIDTH;
dev->mode_config.min_height = MIN_HEIGHT;
dev->mode_config.max_height = MAX_HEIGHT;
dev->mode_config.funcs = &mock_config_funcs;
test->priv = priv;
return 0;
}
static void drm_test_framebuffer_create(struct kunit *test)
{
const struct drm_framebuffer_test *params = test->param_value;
struct drm_framebuffer_test_priv *priv = test->priv;
struct drm_device *dev = &priv->dev;
priv->buffer_created = false;
drm_internal_framebuffer_create(dev, &params->cmd, NULL);
KUNIT_EXPECT_EQ(test, params->buffer_created, priv->buffer_created);
}
static void drm_framebuffer_test_to_desc(const struct drm_framebuffer_test *t, char *desc)
{
strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
}
KUNIT_ARRAY_PARAM(drm_framebuffer_create, drm_framebuffer_create_cases,
drm_framebuffer_test_to_desc);
/* Tries to create a framebuffer with modifiers without drm_device supporting it */
static void drm_test_framebuffer_modifiers_not_supported(struct kunit *test)
{
struct drm_framebuffer_test_priv *priv = test->priv;
struct drm_device *dev = &priv->dev;
struct drm_framebuffer *fb;
/* A valid cmd with modifier */
struct drm_mode_fb_cmd2 cmd = {
.width = MAX_WIDTH, .height = MAX_HEIGHT,
.pixel_format = DRM_FORMAT_ABGR8888, .handles = { 1, 0, 0 },
.offsets = { UINT_MAX / 2, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
.flags = DRM_MODE_FB_MODIFIERS,
};
priv->buffer_created = false;
dev->mode_config.fb_modifiers_not_supported = 1;
fb = drm_internal_framebuffer_create(dev, &cmd, NULL);
KUNIT_EXPECT_EQ(test, priv->buffer_created, false);
KUNIT_EXPECT_EQ(test, PTR_ERR(fb), -EINVAL);
}
/* Parameters for testing drm_framebuffer_check_src_coords function */
struct drm_framebuffer_check_src_coords_case {
const char *name;
const int expect;
const unsigned int fb_size;
const uint32_t src_x;
const uint32_t src_y;
/* Deltas to be applied on source */
const uint32_t dsrc_w;
const uint32_t dsrc_h;
};
static const struct drm_framebuffer_check_src_coords_case
drm_framebuffer_check_src_coords_cases[] = {
{ .name = "Success: source fits into fb",
.expect = 0,
},
{ .name = "Fail: overflowing fb with x-axis coordinate",
.expect = -ENOSPC, .src_x = 1, .fb_size = UINT_MAX,
},
{ .name = "Fail: overflowing fb with y-axis coordinate",
.expect = -ENOSPC, .src_y = 1, .fb_size = UINT_MAX,
},
{ .name = "Fail: overflowing fb with source width",
.expect = -ENOSPC, .dsrc_w = 1, .fb_size = UINT_MAX - 1,
},
{ .name = "Fail: overflowing fb with source height",
.expect = -ENOSPC, .dsrc_h = 1, .fb_size = UINT_MAX - 1,
},
};
static void drm_test_framebuffer_check_src_coords(struct kunit *test)
{
const struct drm_framebuffer_check_src_coords_case *params = test->param_value;
const uint32_t src_x = params->src_x;
const uint32_t src_y = params->src_y;
const uint32_t src_w = (params->fb_size << 16) + params->dsrc_w;
const uint32_t src_h = (params->fb_size << 16) + params->dsrc_h;
const struct drm_framebuffer fb = {
.width = params->fb_size,
.height = params->fb_size
};
int ret;
ret = drm_framebuffer_check_src_coords(src_x, src_y, src_w, src_h, &fb);
KUNIT_EXPECT_EQ(test, ret, params->expect);
}
static void
check_src_coords_test_to_desc(const struct drm_framebuffer_check_src_coords_case *t,
char *desc)
{
strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
}
KUNIT_ARRAY_PARAM(check_src_coords, drm_framebuffer_check_src_coords_cases,
check_src_coords_test_to_desc);
/*
* Test if drm_framebuffer_cleanup() really pops out the framebuffer object
* from device's fb_list and decrement the number of framebuffers for that
* device, which is the only things it does.
*/
static void drm_test_framebuffer_cleanup(struct kunit *test)
{
struct drm_framebuffer_test_priv *priv = test->priv;
struct drm_device *dev = &priv->dev;
struct list_head *fb_list = &dev->mode_config.fb_list;
struct drm_format_info format = { };
struct drm_framebuffer fb1 = { .dev = dev, .format = &format };
struct drm_framebuffer fb2 = { .dev = dev, .format = &format };
/* This will result on [fb_list] -> fb2 -> fb1 */
drm_framebuffer_init(dev, &fb1, NULL);
drm_framebuffer_init(dev, &fb2, NULL);
drm_framebuffer_cleanup(&fb1);
/* Now fb2 is the only one element on fb_list */
KUNIT_ASSERT_TRUE(test, list_is_singular(&fb2.head));
KUNIT_ASSERT_EQ(test, dev->mode_config.num_fb, 1);
drm_framebuffer_cleanup(&fb2);
/* Now fb_list is empty */
KUNIT_ASSERT_TRUE(test, list_empty(fb_list));
KUNIT_ASSERT_EQ(test, dev->mode_config.num_fb, 0);
}
/*
* Initialize a framebuffer, lookup its id and test if the returned reference
* matches.
*/
static void drm_test_framebuffer_lookup(struct kunit *test)
{
struct drm_framebuffer_test_priv *priv = test->priv;
struct drm_device *dev = &priv->dev;
struct drm_format_info format = { };
struct drm_framebuffer expected_fb = { .dev = dev, .format = &format };
struct drm_framebuffer *returned_fb;
uint32_t id = 0;
int ret;
ret = drm_framebuffer_init(dev, &expected_fb, NULL);
KUNIT_ASSERT_EQ(test, ret, 0);
id = expected_fb.base.id;
/* Looking for expected_fb */
returned_fb = drm_framebuffer_lookup(dev, NULL, id);
KUNIT_EXPECT_PTR_EQ(test, returned_fb, &expected_fb);
drm_framebuffer_put(returned_fb);
drm_framebuffer_cleanup(&expected_fb);
}
/* Try to lookup an id that is not linked to a framebuffer */
static void drm_test_framebuffer_lookup_inexistent(struct kunit *test)
{
struct drm_framebuffer_test_priv *priv = test->priv;
struct drm_device *dev = &priv->dev;
struct drm_framebuffer *fb;
uint32_t id = 0;
/* Looking for an inexistent framebuffer */
fb = drm_framebuffer_lookup(dev, NULL, id);
KUNIT_EXPECT_NULL(test, fb);
}
/*
* Test if drm_framebuffer_init initializes the framebuffer successfully,
* asserting that its modeset object struct and its refcount are correctly
* set and that strictly one framebuffer is initialized.
*/
static void drm_test_framebuffer_init(struct kunit *test)
{
struct drm_framebuffer_test_priv *priv = test->priv;
struct drm_device *dev = &priv->dev;
struct drm_format_info format = { };
struct drm_framebuffer fb1 = { .dev = dev, .format = &format };
struct drm_framebuffer_funcs funcs = { };
int ret;
ret = drm_framebuffer_init(dev, &fb1, &funcs);
KUNIT_ASSERT_EQ(test, ret, 0);
/* Check if fb->funcs is actually set to the drm_framebuffer_funcs passed on */
KUNIT_EXPECT_PTR_EQ(test, fb1.funcs, &funcs);
/* The fb->comm must be set to the current running process */
KUNIT_EXPECT_STREQ(test, fb1.comm, current->comm);
/* The fb->base must be successfully initialized */
KUNIT_EXPECT_NE(test, fb1.base.id, 0);
KUNIT_EXPECT_EQ(test, fb1.base.type, DRM_MODE_OBJECT_FB);
KUNIT_EXPECT_EQ(test, kref_read(&fb1.base.refcount), 1);
KUNIT_EXPECT_PTR_EQ(test, fb1.base.free_cb, &drm_framebuffer_free);
/* There must be just that one fb initialized */
KUNIT_EXPECT_EQ(test, dev->mode_config.num_fb, 1);
KUNIT_EXPECT_PTR_EQ(test, dev->mode_config.fb_list.prev, &fb1.head);
KUNIT_EXPECT_PTR_EQ(test, dev->mode_config.fb_list.next, &fb1.head);
drm_framebuffer_cleanup(&fb1);
}
/* Try to init a framebuffer without setting its format */
static void drm_test_framebuffer_init_bad_format(struct kunit *test)
{
struct drm_framebuffer_test_priv *priv = test->priv;
struct drm_device *dev = &priv->dev;
struct drm_framebuffer fb1 = { .dev = dev, .format = NULL };
struct drm_framebuffer_funcs funcs = { };
int ret;
/* Fails if fb.format isn't set */
ret = drm_framebuffer_init(dev, &fb1, &funcs);
KUNIT_EXPECT_EQ(test, ret, -EINVAL);
}
/*
* Test calling drm_framebuffer_init() passing a framebuffer linked to a
* different drm_device parent from the one passed on the first argument, which
* must fail.
*/
static void drm_test_framebuffer_init_dev_mismatch(struct kunit *test)
{
struct drm_framebuffer_test_priv *priv = test->priv;
struct drm_device *right_dev = &priv->dev;
struct drm_device *wrong_dev;
struct device *wrong_dev_parent;
struct drm_format_info format = { };
struct drm_framebuffer fb1 = { .dev = right_dev, .format = &format };
struct drm_framebuffer_funcs funcs = { };
int ret;
wrong_dev_parent = kunit_device_register(test, "drm-kunit-wrong-device-mock");
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wrong_dev_parent);
wrong_dev = __drm_kunit_helper_alloc_drm_device(test, wrong_dev_parent,
sizeof(struct drm_device),
0, 0);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wrong_dev);
/* Fails if fb->dev doesn't point to the drm_device passed on first arg */
ret = drm_framebuffer_init(wrong_dev, &fb1, &funcs);
KUNIT_EXPECT_EQ(test, ret, -EINVAL);
}
static void destroy_free_mock(struct drm_framebuffer *fb)
{
struct drm_framebuffer_test_priv *priv = container_of(fb->dev, typeof(*priv), dev);
priv->buffer_freed = true;
}
static struct drm_framebuffer_funcs framebuffer_funcs_free_mock = {
.destroy = destroy_free_mock,
};
/*
* In summary, the drm_framebuffer_free() function must implicitly call
* fb->funcs->destroy() and garantee that the framebufer object is unregistered
* from the drm_device idr pool.
*/
static void drm_test_framebuffer_free(struct kunit *test)
{
struct drm_framebuffer_test_priv *priv = test->priv;
struct drm_device *dev = &priv->dev;
struct drm_mode_object *obj;
struct drm_framebuffer fb = {
.dev = dev,
.funcs = &framebuffer_funcs_free_mock,
};
int id, ret;
priv->buffer_freed = false;
/*
* Mock a framebuffer that was not unregistered at the moment of the
* drm_framebuffer_free() call.
*/
ret = drm_mode_object_add(dev, &fb.base, DRM_MODE_OBJECT_FB);
KUNIT_ASSERT_EQ(test, ret, 0);
id = fb.base.id;
drm_framebuffer_free(&fb.base.refcount);
/* The framebuffer object must be unregistered */
obj = drm_mode_object_find(dev, NULL, id, DRM_MODE_OBJECT_FB);
KUNIT_EXPECT_PTR_EQ(test, obj, NULL);
KUNIT_EXPECT_EQ(test, fb.base.id, 0);
/* Test if fb->funcs->destroy() was called */
KUNIT_EXPECT_EQ(test, priv->buffer_freed, true);
}
static struct kunit_case drm_framebuffer_tests[] = {
KUNIT_CASE_PARAM(drm_test_framebuffer_check_src_coords, check_src_coords_gen_params),
KUNIT_CASE(drm_test_framebuffer_cleanup),
KUNIT_CASE_PARAM(drm_test_framebuffer_create, drm_framebuffer_create_gen_params),
KUNIT_CASE(drm_test_framebuffer_free),
KUNIT_CASE(drm_test_framebuffer_init),
KUNIT_CASE(drm_test_framebuffer_init_bad_format),
KUNIT_CASE(drm_test_framebuffer_init_dev_mismatch),
KUNIT_CASE(drm_test_framebuffer_lookup),
KUNIT_CASE(drm_test_framebuffer_lookup_inexistent),
KUNIT_CASE(drm_test_framebuffer_modifiers_not_supported),
{ }
};
static struct kunit_suite drm_framebuffer_test_suite = {
.name = "drm_framebuffer",
.init = drm_framebuffer_test_init,
.test_cases = drm_framebuffer_tests,
};
kunit_test_suite(drm_framebuffer_test_suite);
MODULE_DESCRIPTION("Test cases for the drm_framebuffer functions");
MODULE_LICENSE("GPL");
@@ -0,0 +1,399 @@
// SPDX-License-Identifier: GPL-2.0
/*
* KUnit test suite for GEM objects backed by shmem buffers
*
* Copyright (C) 2023 Red Hat, Inc.
*
* Author: Marco Pagani <marpagan@redhat.com>
*/
#include <linux/dma-buf.h>
#include <linux/iosys-map.h>
#include <linux/sizes.h>
#include <kunit/test.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_kunit_helpers.h>
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
#define TEST_SIZE SZ_1M
#define TEST_BYTE 0xae
/*
* Wrappers to avoid cast warnings when passing action functions
* directly to kunit_add_action().
*/
KUNIT_DEFINE_ACTION_WRAPPER(kfree_wrapper, kfree, const void *);
KUNIT_DEFINE_ACTION_WRAPPER(sg_free_table_wrapper, sg_free_table,
struct sg_table *);
KUNIT_DEFINE_ACTION_WRAPPER(drm_gem_shmem_free_wrapper, drm_gem_shmem_free,
struct drm_gem_shmem_object *);
KUNIT_DEFINE_ACTION_WRAPPER(drm_gem_shmem_unpin_wrapper, drm_gem_shmem_unpin,
struct drm_gem_shmem_object *);
/*
* Test creating a shmem GEM object backed by shmem buffer. The test
* case succeeds if the GEM object is successfully allocated with the
* shmem file node and object functions attributes set, and the size
* attribute is equal to the correct size.
*/
static void drm_gem_shmem_test_obj_create(struct kunit *test)
{
struct drm_device *drm_dev = test->priv;
struct drm_gem_shmem_object *shmem;
shmem = drm_gem_shmem_create(drm_dev, TEST_SIZE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, shmem);
KUNIT_EXPECT_EQ(test, shmem->base.size, TEST_SIZE);
KUNIT_EXPECT_NOT_NULL(test, shmem->base.filp);
KUNIT_EXPECT_NOT_NULL(test, shmem->base.funcs);
drm_gem_shmem_free(shmem);
}
/*
* Test creating a shmem GEM object from a scatter/gather table exported
* via a DMA-BUF. The test case succeed if the GEM object is successfully
* created with the shmem file node attribute equal to NULL and the sgt
* attribute pointing to the scatter/gather table that has been imported.
*/
static void drm_gem_shmem_test_obj_create_private(struct kunit *test)
{
struct drm_device *drm_dev = test->priv;
struct drm_gem_shmem_object *shmem;
struct drm_gem_object *gem_obj;
struct dma_buf buf_mock;
struct dma_buf_attachment attach_mock;
struct sg_table *sgt;
char *buf;
int ret;
/* Create a mock scatter/gather table */
buf = kunit_kzalloc(test, TEST_SIZE, GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, buf);
sgt = kzalloc_obj(*sgt);
KUNIT_ASSERT_NOT_NULL(test, sgt);
ret = kunit_add_action_or_reset(test, kfree_wrapper, sgt);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = kunit_add_action_or_reset(test, sg_free_table_wrapper, sgt);
KUNIT_ASSERT_EQ(test, ret, 0);
sg_init_one(sgt->sgl, buf, TEST_SIZE);
/*
* Map the sgtables otherwise drm_gem_shmem_free will cause a warning on
* debug kernels.
*/
ret = dma_map_sgtable(drm_dev->dev, sgt, DMA_BIDIRECTIONAL, 0);
KUNIT_ASSERT_EQ(test, ret, 0);
/* Init a mock DMA-BUF */
buf_mock.size = TEST_SIZE;
attach_mock.dmabuf = &buf_mock;
gem_obj = drm_gem_shmem_prime_import_sg_table(drm_dev, &attach_mock, sgt);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, gem_obj);
KUNIT_EXPECT_EQ(test, gem_obj->size, TEST_SIZE);
KUNIT_EXPECT_NULL(test, gem_obj->filp);
KUNIT_EXPECT_NOT_NULL(test, gem_obj->funcs);
/* The scatter/gather table will be freed by drm_gem_shmem_free */
kunit_remove_action(test, sg_free_table_wrapper, sgt);
kunit_remove_action(test, kfree_wrapper, sgt);
shmem = to_drm_gem_shmem_obj(gem_obj);
KUNIT_EXPECT_PTR_EQ(test, shmem->sgt, sgt);
drm_gem_shmem_free(shmem);
}
/*
* Test pinning backing pages for a shmem GEM object. The test case
* succeeds if a suitable number of backing pages are allocated, and
* the pages table counter attribute is increased by one.
*/
static void drm_gem_shmem_test_pin_pages(struct kunit *test)
{
struct drm_device *drm_dev = test->priv;
struct drm_gem_shmem_object *shmem;
int i, ret;
shmem = drm_gem_shmem_create(drm_dev, TEST_SIZE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, shmem);
KUNIT_EXPECT_NULL(test, shmem->pages);
KUNIT_EXPECT_EQ(test, refcount_read(&shmem->pages_use_count), 0);
ret = kunit_add_action_or_reset(test, drm_gem_shmem_free_wrapper, shmem);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = drm_gem_shmem_pin(shmem);
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_ASSERT_NOT_NULL(test, shmem->pages);
KUNIT_EXPECT_EQ(test, refcount_read(&shmem->pages_use_count), 1);
for (i = 0; i < (shmem->base.size >> PAGE_SHIFT); i++)
KUNIT_ASSERT_NOT_NULL(test, shmem->pages[i]);
drm_gem_shmem_unpin(shmem);
KUNIT_EXPECT_NULL(test, shmem->pages);
KUNIT_EXPECT_EQ(test, refcount_read(&shmem->pages_use_count), 0);
}
/*
* Test creating a virtual mapping for a shmem GEM object. The test
* case succeeds if the backing memory is mapped and the reference
* counter for virtual mapping is increased by one. Moreover, the test
* case writes and then reads a test pattern over the mapped memory.
*/
static void drm_gem_shmem_test_vmap(struct kunit *test)
{
struct drm_device *drm_dev = test->priv;
struct drm_gem_shmem_object *shmem;
struct iosys_map map;
int ret, i;
shmem = drm_gem_shmem_create(drm_dev, TEST_SIZE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, shmem);
KUNIT_EXPECT_NULL(test, shmem->vaddr);
KUNIT_EXPECT_EQ(test, refcount_read(&shmem->vmap_use_count), 0);
ret = kunit_add_action_or_reset(test, drm_gem_shmem_free_wrapper, shmem);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = drm_gem_shmem_vmap(shmem, &map);
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_ASSERT_NOT_NULL(test, shmem->vaddr);
KUNIT_ASSERT_FALSE(test, iosys_map_is_null(&map));
KUNIT_EXPECT_EQ(test, refcount_read(&shmem->vmap_use_count), 1);
iosys_map_memset(&map, 0, TEST_BYTE, TEST_SIZE);
for (i = 0; i < TEST_SIZE; i++)
KUNIT_EXPECT_EQ(test, iosys_map_rd(&map, i, u8), TEST_BYTE);
drm_gem_shmem_vunmap(shmem, &map);
KUNIT_EXPECT_NULL(test, shmem->vaddr);
KUNIT_EXPECT_EQ(test, refcount_read(&shmem->vmap_use_count), 0);
}
/*
* Test exporting a scatter/gather table of pinned pages suitable for
* PRIME usage from a shmem GEM object. The test case succeeds if a
* scatter/gather table large enough to accommodate the backing memory
* is successfully exported.
*/
static void drm_gem_shmem_test_get_sg_table(struct kunit *test)
{
struct drm_device *drm_dev = test->priv;
struct drm_gem_shmem_object *shmem;
struct sg_table *sgt;
struct scatterlist *sg;
unsigned int si, len = 0;
int ret;
shmem = drm_gem_shmem_create(drm_dev, TEST_SIZE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, shmem);
ret = kunit_add_action_or_reset(test, drm_gem_shmem_free_wrapper, shmem);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = drm_gem_shmem_pin(shmem);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = kunit_add_action_or_reset(test, drm_gem_shmem_unpin_wrapper, shmem);
KUNIT_ASSERT_EQ(test, ret, 0);
sgt = drm_gem_shmem_get_sg_table(shmem);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sgt);
KUNIT_EXPECT_NULL(test, shmem->sgt);
ret = kunit_add_action_or_reset(test, kfree_wrapper, sgt);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = kunit_add_action_or_reset(test, sg_free_table_wrapper, sgt);
KUNIT_ASSERT_EQ(test, ret, 0);
for_each_sgtable_sg(sgt, sg, si) {
KUNIT_EXPECT_NOT_NULL(test, sg);
len += sg->length;
}
KUNIT_EXPECT_GE(test, len, TEST_SIZE);
}
/*
* Test pinning pages and exporting a scatter/gather table suitable for
* driver usage from a shmem GEM object. The test case succeeds if the
* backing pages are pinned and a scatter/gather table large enough to
* accommodate the backing memory is successfully exported.
*/
static void drm_gem_shmem_test_get_pages_sgt(struct kunit *test)
{
struct drm_device *drm_dev = test->priv;
struct drm_gem_shmem_object *shmem;
struct sg_table *sgt;
struct scatterlist *sg;
unsigned int si, ret, len = 0;
shmem = drm_gem_shmem_create(drm_dev, TEST_SIZE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, shmem);
ret = kunit_add_action_or_reset(test, drm_gem_shmem_free_wrapper, shmem);
KUNIT_ASSERT_EQ(test, ret, 0);
/* The scatter/gather table will be freed by drm_gem_shmem_free */
sgt = drm_gem_shmem_get_pages_sgt(shmem);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sgt);
KUNIT_ASSERT_NOT_NULL(test, shmem->pages);
KUNIT_EXPECT_EQ(test, refcount_read(&shmem->pages_use_count), 1);
KUNIT_EXPECT_PTR_EQ(test, sgt, shmem->sgt);
for_each_sgtable_sg(sgt, sg, si) {
KUNIT_EXPECT_NOT_NULL(test, sg);
len += sg->length;
}
KUNIT_EXPECT_GE(test, len, TEST_SIZE);
}
/*
* Test updating the madvise state of a shmem GEM object. The test
* case checks that the function for setting madv updates it only if
* its current value is greater or equal than zero and returns false
* if it has a negative value.
*/
static void drm_gem_shmem_test_madvise(struct kunit *test)
{
struct drm_device *drm_dev = test->priv;
struct drm_gem_shmem_object *shmem;
int ret;
shmem = drm_gem_shmem_create(drm_dev, TEST_SIZE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, shmem);
KUNIT_ASSERT_EQ(test, shmem->madv, 0);
ret = kunit_add_action_or_reset(test, drm_gem_shmem_free_wrapper, shmem);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = drm_gem_shmem_madvise(shmem, 1);
KUNIT_EXPECT_TRUE(test, ret);
KUNIT_ASSERT_EQ(test, shmem->madv, 1);
/* Set madv to a negative value */
ret = drm_gem_shmem_madvise(shmem, -1);
KUNIT_EXPECT_FALSE(test, ret);
KUNIT_ASSERT_EQ(test, shmem->madv, -1);
/* Check that madv cannot be set back to a positive value */
ret = drm_gem_shmem_madvise(shmem, 0);
KUNIT_EXPECT_FALSE(test, ret);
KUNIT_ASSERT_EQ(test, shmem->madv, -1);
}
/*
* Test purging a shmem GEM object. First, assert that a newly created
* shmem GEM object is not purgeable. Then, set madvise to a positive
* value and call drm_gem_shmem_get_pages_sgt() to pin and dma-map the
* backing pages. Finally, assert that the shmem GEM object is now
* purgeable and purge it.
*/
static void drm_gem_shmem_test_purge(struct kunit *test)
{
struct drm_device *drm_dev = test->priv;
struct drm_gem_shmem_object *shmem;
struct sg_table *sgt;
int ret;
shmem = drm_gem_shmem_create(drm_dev, TEST_SIZE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, shmem);
ret = kunit_add_action_or_reset(test, drm_gem_shmem_free_wrapper, shmem);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = drm_gem_shmem_is_purgeable(shmem);
KUNIT_EXPECT_FALSE(test, ret);
ret = drm_gem_shmem_madvise(shmem, 1);
KUNIT_EXPECT_TRUE(test, ret);
/* The scatter/gather table will be freed by drm_gem_shmem_free */
sgt = drm_gem_shmem_get_pages_sgt(shmem);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sgt);
ret = drm_gem_shmem_is_purgeable(shmem);
KUNIT_EXPECT_TRUE(test, ret);
ret = drm_gem_shmem_purge(shmem);
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_NULL(test, shmem->pages);
KUNIT_EXPECT_NULL(test, shmem->sgt);
KUNIT_EXPECT_EQ(test, shmem->madv, -1);
}
static int drm_gem_shmem_test_init(struct kunit *test)
{
struct device *dev;
struct drm_device *drm_dev;
int ret;
/* Allocate a parent device */
dev = drm_kunit_helper_alloc_device(test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
/*
* Set the DMA mask to 64-bits to avoid intermittent failures calling
* drm_gem_shmem_get_pages_sgt().
*/
ret = dma_set_mask(dev, DMA_BIT_MASK(64));
KUNIT_ASSERT_EQ(test, ret, 0);
/*
* The DRM core will automatically initialize the GEM core and create
* a DRM Memory Manager object which provides an address space pool
* for GEM objects allocation.
*/
drm_dev = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*drm_dev),
0, DRIVER_GEM);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm_dev);
test->priv = drm_dev;
return 0;
}
static struct kunit_case drm_gem_shmem_test_cases[] = {
KUNIT_CASE(drm_gem_shmem_test_obj_create),
KUNIT_CASE(drm_gem_shmem_test_obj_create_private),
KUNIT_CASE(drm_gem_shmem_test_pin_pages),
KUNIT_CASE(drm_gem_shmem_test_vmap),
KUNIT_CASE(drm_gem_shmem_test_get_sg_table),
KUNIT_CASE(drm_gem_shmem_test_get_pages_sgt),
KUNIT_CASE(drm_gem_shmem_test_madvise),
KUNIT_CASE(drm_gem_shmem_test_purge),
{}
};
static struct kunit_suite drm_gem_shmem_suite = {
.name = "drm_gem_shmem",
.init = drm_gem_shmem_test_init,
.test_cases = drm_gem_shmem_test_cases
};
kunit_test_suite(drm_gem_shmem_suite);
MODULE_DESCRIPTION("KUnit test suite for GEM objects backed by shmem buffers");
MODULE_LICENSE("GPL");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,983 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef DRM_KUNIT_EDID_H_
#define DRM_KUNIT_EDID_H_
/*
* edid-decode (hex):
*
* 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
* 00 21 01 03 81 a0 5a 78 0a 00 00 00 00 00 00 00
* 00 00 00 00 00 00 01 01 01 01 01 01 01 01 01 01
* 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
* 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
* 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
* 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ab
*
* ----------------
*
* Block 0, Base EDID:
* EDID Structure Version & Revision: 1.3
* Vendor & Product Identification:
* Manufacturer: LNX
* Model: 42
* Made in: 2023
* Basic Display Parameters & Features:
* Digital display
* DFP 1.x compatible TMDS
* Maximum image size: 160 cm x 90 cm
* Gamma: 2.20
* RGB color display
* First detailed timing is the preferred timing
* Color Characteristics:
* Red : 0.0000, 0.0000
* Green: 0.0000, 0.0000
* Blue : 0.0000, 0.0000
* White: 0.0000, 0.0000
* Established Timings I & II: none
* Standard Timings: none
* Detailed Timing Descriptors:
* DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
* Hfront 88 Hsync 44 Hback 148 Hpol P
* Vfront 4 Vsync 5 Vback 36 Vpol P
* Display Product Name: 'Test EDID'
* Display Range Limits:
* Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
* Dummy Descriptor:
* Checksum: 0xab
*
* ----------------
*
* edid-decode 1.30.0-5367
* edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22
*
* EDID conformity: PASS
*/
static const unsigned char test_edid_dvi_1080p[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab
};
/*
*
* This edid is intentionally broken with the 100MHz limit. It's meant
* to be used only with tests in unusual situations.
*
* edid-decode (hex):
*
* 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
* 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00
* 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
* 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
* 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
* 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
* 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92
*
* 02 03 15 81 e3 05 00 20 41 10 e2 00 4a 67 03 0c
* 00 12 34 00 14 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10
*
* ----------------
*
* Block 0, Base EDID:
* EDID Structure Version & Revision: 1.3
* Vendor & Product Identification:
* Manufacturer: LNX
* Model: 42
* Made in: 2023
* Basic Display Parameters & Features:
* Digital display
* DFP 1.x compatible TMDS
* Maximum image size: 160 cm x 90 cm
* Gamma: 2.20
* Monochrome or grayscale display
* First detailed timing is the preferred timing
* Color Characteristics:
* Red : 0.0000, 0.0000
* Green: 0.0000, 0.0000
* Blue : 0.0000, 0.0000
* White: 0.0000, 0.0000
* Established Timings I & II:
* DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
* Standard Timings: none
* Detailed Timing Descriptors:
* DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
* Hfront 88 Hsync 44 Hback 148 Hpol P
* Vfront 4 Vsync 5 Vback 36 Vpol P
* Display Product Name: 'Test EDID'
* Display Range Limits:
* Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
* Dummy Descriptor:
* Extension blocks: 1
* Checksum: 0x92
*
* ----------------
*
* Block 1, CTA-861 Extension Block:
* Revision: 3
* Underscans IT Video Formats by default
* Native detailed modes: 1
* Colorimetry Data Block:
* sRGB
* Video Data Block:
* VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
* Video Capability Data Block:
* YCbCr quantization: No Data
* RGB quantization: Selectable (via AVI Q)
* PT scan behavior: No Data
* IT scan behavior: Always Underscanned
* CE scan behavior: Always Underscanned
* Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
* Source physical address: 1.2.3.4
* Maximum TMDS clock: 100 MHz
* Checksum: 0x10 Unused space in Extension Block: 106 bytes
*
* ----------------
*
* edid-decode 1.30.0-5367
* edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22
*
* Failures:
*
* EDID:
* CTA-861: The maximum HDMI TMDS clock is 100000 kHz, but one or more video timings go up to 148500 kHz.
*
* EDID conformity: FAIL
*/
static const unsigned char test_edid_hdmi_1080p_rgb_max_100mhz[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x15, 0x81,
0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c,
0x00, 0x12, 0x34, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10
};
/*
* edid-decode (hex):
*
* 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
* 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00
* 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
* 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
* 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
* 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
* 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92
*
* 02 03 15 81 e3 05 00 20 41 10 e2 00 4a 67 03 0c
* 00 12 34 00 28 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fc
*
* ----------------
*
* Block 0, Base EDID:
* EDID Structure Version & Revision: 1.3
* Vendor & Product Identification:
* Manufacturer: LNX
* Model: 42
* Made in: 2023
* Basic Display Parameters & Features:
* Digital display
* DFP 1.x compatible TMDS
* Maximum image size: 160 cm x 90 cm
* Gamma: 2.20
* Monochrome or grayscale display
* First detailed timing is the preferred timing
* Color Characteristics:
* Red : 0.0000, 0.0000
* Green: 0.0000, 0.0000
* Blue : 0.0000, 0.0000
* White: 0.0000, 0.0000
* Established Timings I & II:
* DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
* Standard Timings: none
* Detailed Timing Descriptors:
* DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
* Hfront 88 Hsync 44 Hback 148 Hpol P
* Vfront 4 Vsync 5 Vback 36 Vpol P
* Display Product Name: 'Test EDID'
* Display Range Limits:
* Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
* Dummy Descriptor:
* Extension blocks: 1
* Checksum: 0x92
*
* ----------------
*
* Block 1, CTA-861 Extension Block:
* Revision: 3
* Underscans IT Video Formats by default
* Native detailed modes: 1
* Colorimetry Data Block:
* sRGB
* Video Data Block:
* VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
* Video Capability Data Block:
* YCbCr quantization: No Data
* RGB quantization: Selectable (via AVI Q)
* PT scan behavior: No Data
* IT scan behavior: Always Underscanned
* CE scan behavior: Always Underscanned
* Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
* Source physical address: 1.2.3.4
* Maximum TMDS clock: 200 MHz
* Checksum: 0xfc Unused space in Extension Block: 106 bytes
*
* ----------------
*
* edid-decode 1.30.0-5367
* edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22
*
* EDID conformity: PASS
*/
static const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x15, 0x81,
0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c,
0x00, 0x12, 0x34, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xfc
};
/*
* edid-decode (hex):
*
* 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
* 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00
* 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
* 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
* 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
* 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
* 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92
*
* 02 03 1c 81 e3 05 c0 20 41 10 e2 00 4a 67 03 0c
* 00 12 34 00 28 e6 06 05 01 52 52 51 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4e
*
* ----------------
*
* Block 0, Base EDID:
* EDID Structure Version & Revision: 1.3
* Vendor & Product Identification:
* Manufacturer: LNX
* Model: 42
* Made in: 2023
* Basic Display Parameters & Features:
* Digital display
* DFP 1.x compatible TMDS
* Maximum image size: 160 cm x 90 cm
* Gamma: 2.20
* Monochrome or grayscale display
* First detailed timing is the preferred timing
* Color Characteristics:
* Red : 0.0000, 0.0000
* Green: 0.0000, 0.0000
* Blue : 0.0000, 0.0000
* White: 0.0000, 0.0000
* Established Timings I & II:
* DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
* Standard Timings: none
* Detailed Timing Descriptors:
* DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
* Hfront 88 Hsync 44 Hback 148 Hpol P
* Vfront 4 Vsync 5 Vback 36 Vpol P
* Display Product Name: 'Test EDID'
* Display Range Limits:
* Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
* Dummy Descriptor:
* Extension blocks: 1
* Checksum: 0x92
*
* ----------------
*
* Block 1, CTA-861 Extension Block:
* Revision: 3
* Underscans IT Video Formats by default
* Native detailed modes: 1
* Colorimetry Data Block:
* BT2020YCC
* BT2020RGB
* sRGB
* Video Data Block:
* VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
* Video Capability Data Block:
* YCbCr quantization: No Data
* RGB quantization: Selectable (via AVI Q)
* PT scan behavior: No Data
* IT scan behavior: Always Underscanned
* CE scan behavior: Always Underscanned
* Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
* Source physical address: 1.2.3.4
* Maximum TMDS clock: 200 MHz
* HDR Static Metadata Data Block:
* Electro optical transfer functions:
* Traditional gamma - SDR luminance range
* SMPTE ST2084
* Supported static metadata descriptors:
* Static metadata type 1
* Desired content max luminance: 82 (295.365 cd/m^2)
* Desired content max frame-average luminance: 82 (295.365 cd/m^2)
* Desired content min luminance: 81 (0.298 cd/m^2)
* Checksum: 0x4e Unused space in Extension Block: 99 bytes
*
* ----------------
*
* edid-decode 1.31.0-5387
* edid-decode SHA: 5508bc4301ac 2025-08-25 08:14:22
*
* EDID conformity: PASS
*/
static const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz_hdr[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x1c, 0x81,
0xe3, 0x05, 0xc0, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c,
0x00, 0x12, 0x34, 0x78, 0x28, 0xe6, 0x06, 0x05, 0x01, 0x52, 0x52, 0x51,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xd6,
};
/*
* edid-decode (hex):
*
* 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
* 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00
* 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
* 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
* 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
* 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
* 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92
*
* 02 03 15 81 e3 05 00 20 41 10 e2 00 4a 67 03 0c
* 00 12 34 00 44 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e0
*
* ----------------
*
* Block 0, Base EDID:
* EDID Structure Version & Revision: 1.3
* Vendor & Product Identification:
* Manufacturer: LNX
* Model: 42
* Made in: 2023
* Basic Display Parameters & Features:
* Digital display
* DFP 1.x compatible TMDS
* Maximum image size: 160 cm x 90 cm
* Gamma: 2.20
* Monochrome or grayscale display
* First detailed timing is the preferred timing
* Color Characteristics:
* Red : 0.0000, 0.0000
* Green: 0.0000, 0.0000
* Blue : 0.0000, 0.0000
* White: 0.0000, 0.0000
* Established Timings I & II:
* DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
* Standard Timings: none
* Detailed Timing Descriptors:
* DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
* Hfront 88 Hsync 44 Hback 148 Hpol P
* Vfront 4 Vsync 5 Vback 36 Vpol P
* Display Product Name: 'Test EDID'
* Display Range Limits:
* Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
* Dummy Descriptor:
* Extension blocks: 1
* Checksum: 0x92
*
* ----------------
*
* Block 1, CTA-861 Extension Block:
* Revision: 3
* Underscans IT Video Formats by default
* Native detailed modes: 1
* Colorimetry Data Block:
* sRGB
* Video Data Block:
* VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
* Video Capability Data Block:
* YCbCr quantization: No Data
* RGB quantization: Selectable (via AVI Q)
* PT scan behavior: No Data
* IT scan behavior: Always Underscanned
* CE scan behavior: Always Underscanned
* Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
* Source physical address: 1.2.3.4
* Maximum TMDS clock: 340 MHz
* Checksum: 0xe0 Unused space in Extension Block: 106 bytes
*
* ----------------
*
* edid-decode 1.30.0-5367
* edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22
*
* EDID conformity: PASS
*/
static const unsigned char test_edid_hdmi_1080p_rgb_max_340mhz[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x15, 0x81,
0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c,
0x00, 0x12, 0x34, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xe0
};
/*
* edid-decode (hex):
*
* 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
* 00 21 01 03 81 a0 5a 78 1a 00 00 00 00 00 00 00
* 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
* 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
* 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
* 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
* 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 7a
*
* 02 03 15 b1 e3 05 00 20 41 10 e2 00 ca 67 03 0c
* 00 12 34 78 28 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d4
*
* ----------------
*
* Block 0, Base EDID:
* EDID Structure Version & Revision: 1.3
* Vendor & Product Identification:
* Manufacturer: LNX
* Model: 42
* Made in: 2023
* Basic Display Parameters & Features:
* Digital display
* DFP 1.x compatible TMDS
* Maximum image size: 160 cm x 90 cm
* Gamma: 2.20
* Undefined display color type
* First detailed timing is the preferred timing
* Color Characteristics:
* Red : 0.0000, 0.0000
* Green: 0.0000, 0.0000
* Blue : 0.0000, 0.0000
* White: 0.0000, 0.0000
* Established Timings I & II:
* DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
* Standard Timings: none
* Detailed Timing Descriptors:
* DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
* Hfront 88 Hsync 44 Hback 148 Hpol P
* Vfront 4 Vsync 5 Vback 36 Vpol P
* Display Product Name: 'Test EDID'
* Display Range Limits:
* Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
* Dummy Descriptor:
* Extension blocks: 1
* Checksum: 0x7a
*
* ----------------
*
* Block 1, CTA-861 Extension Block:
* Revision: 3
* Underscans IT Video Formats by default
* Supports YCbCr 4:4:4
* Supports YCbCr 4:2:2
* Native detailed modes: 1
* Colorimetry Data Block:
* sRGB
* Video Data Block:
* VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
* Video Capability Data Block:
* YCbCr quantization: Selectable (via AVI YQ)
* RGB quantization: Selectable (via AVI Q)
* PT scan behavior: No Data
* IT scan behavior: Always Underscanned
* CE scan behavior: Always Underscanned
* Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
* Source physical address: 1.2.3.4
* DC_48bit
* DC_36bit
* DC_30bit
* DC_Y444
* Maximum TMDS clock: 200 MHz
* Checksum: 0xd4 Unused space in Extension Block: 106 bytes
*
* ----------------
*
* edid-decode 1.30.0-5367
* edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22
*
* EDID conformity: PASS
*/
static const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7a, 0x02, 0x03, 0x15, 0xb1,
0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0xca, 0x67, 0x03, 0x0c,
0x00, 0x12, 0x34, 0x78, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xd4
};
/*
* edid-decode (hex):
*
* 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00
* 00 21 01 03 81 a0 5a 78 0a 00 00 00 00 00 00 00
* 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01
* 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
* 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
* 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32
* 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 8a
*
* 02 03 15 b1 e3 05 00 20 41 10 e2 00 ca 67 03 0c
* 00 12 34 78 44 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b8
*
* ----------------
*
* Block 0, Base EDID:
* EDID Structure Version & Revision: 1.3
* Vendor & Product Identification:
* Manufacturer: LNX
* Model: 42
* Made in: 2023
* Basic Display Parameters & Features:
* Digital display
* DFP 1.x compatible TMDS
* Maximum image size: 160 cm x 90 cm
* Gamma: 2.20
* RGB color display
* First detailed timing is the preferred timing
* Color Characteristics:
* Red : 0.0000, 0.0000
* Green: 0.0000, 0.0000
* Blue : 0.0000, 0.0000
* White: 0.0000, 0.0000
* Established Timings I & II:
* DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
* Standard Timings: none
* Detailed Timing Descriptors:
* DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm)
* Hfront 88 Hsync 44 Hback 148 Hpol P
* Vfront 4 Vsync 5 Vback 36 Vpol P
* Display Product Name: 'Test EDID'
* Display Range Limits:
* Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz
* Dummy Descriptor:
* Extension blocks: 1
* Checksum: 0x8a
*
* ----------------
*
* Block 1, CTA-861 Extension Block:
* Revision: 3
* Underscans IT Video Formats by default
* Supports YCbCr 4:4:4
* Supports YCbCr 4:2:2
* Native detailed modes: 1
* Colorimetry Data Block:
* sRGB
* Video Data Block:
* VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
* Video Capability Data Block:
* YCbCr quantization: Selectable (via AVI YQ)
* RGB quantization: Selectable (via AVI Q)
* PT scan behavior: No Data
* IT scan behavior: Always Underscanned
* CE scan behavior: Always Underscanned
* Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
* Source physical address: 1.2.3.4
* DC_48bit
* DC_36bit
* DC_30bit
* DC_Y444
* Maximum TMDS clock: 340 MHz
* Checksum: 0xb8 Unused space in Extension Block: 106 bytes
*
* ----------------
*
* edid-decode 1.30.0-5367
* edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22
*
* EDID conformity: PASS
*/
static const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x8a, 0x02, 0x03, 0x15, 0xb1,
0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0xca, 0x67, 0x03, 0x0c,
0x00, 0x12, 0x34, 0x78, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xb8
};
/*
* Max resolution:
* - 1920x1080@60Hz with RGB, YUV444, YUV422
* - 3840x2160@30Hz with YUV420 only
* Max BPC: 16 for all modes
* Max TMDS clock: 200 MHz
*
* edid-decode (hex):
*
* 00 ff ff ff ff ff ff 00 31 d8 34 00 00 00 00 00
* ff 23 01 03 80 60 36 78 0f ee 91 a3 54 4c 99 26
* 0f 50 54 20 00 00 01 01 01 01 01 01 01 01 01 01
* 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
* 45 00 c0 1c 32 00 00 1e 00 00 00 fc 00 54 65 73
* 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 18
* 55 18 5e 11 00 0a 20 20 20 20 20 20 00 00 00 10
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 bb
*
* 02 03 29 31 42 90 5f 6c 03 0c 00 10 00 78 28 20
* 00 00 01 03 6d d8 5d c4 01 28 80 07 00 00 00 00
* 00 00 e3 0f 00 00 e2 0e 5f 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ca
*
* ----------------
*
* Block 0, Base EDID:
* EDID Structure Version & Revision: 1.3
* Vendor & Product Identification:
* Manufacturer: LNX
* Model: 52
* Model year: 2025
* Basic Display Parameters & Features:
* Digital display
* Maximum image size: 96 cm x 54 cm
* Gamma: 2.20
* RGB color display
* Default (sRGB) color space is primary color space
* First detailed timing is the preferred timing
* Supports GTF timings within operating range
* Color Characteristics:
* Red : 0.6396, 0.3300
* Green: 0.2998, 0.5996
* Blue : 0.1503, 0.0595
* White: 0.3125, 0.3291
* Established Timings I & II:
* DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
* Standard Timings: none
* Detailed Timing Descriptors:
* DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (960 mm x 540 mm)
* Hfront 88 Hsync 44 Hback 148 Hpol P
* Vfront 4 Vsync 5 Vback 36 Vpol P
* Display Product Name: 'Test EDID'
* Display Range Limits:
* Monitor ranges (GTF): 24-85 Hz V, 24-94 kHz H, max dotclock 170 MHz
* Dummy Descriptor:
* Extension blocks: 1
* Checksum: 0xbb
*
* ----------------
*
* Block 1, CTA-861 Extension Block:
* Revision: 3
* Supports YCbCr 4:4:4
* Supports YCbCr 4:2:2
* Native detailed modes: 1
* Video Data Block:
* VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (native)
* VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz
* Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
* Source physical address: 1.0.0.0
* DC_48bit
* DC_36bit
* DC_30bit
* DC_Y444
* Maximum TMDS clock: 200 MHz
* Extended HDMI video details:
* Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8:
* Version: 1
* Maximum TMDS Character Rate: 200 MHz
* SCDC Present
* Supports 16-bits/component Deep Color 4:2:0 Pixel Encoding
* Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding
* Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding
* YCbCr 4:2:0 Capability Map Data Block:
* Empty Capability Map
* YCbCr 4:2:0 Video Data Block:
* VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz
* Checksum: 0xca
*/
static const unsigned char test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x34, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0x23, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,
0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0x20,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e,
0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18,
0x55, 0x18, 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xbb, 0x02, 0x03, 0x29, 0x31,
0x42, 0x90, 0x5f, 0x6c, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x78, 0x28, 0x20,
0x00, 0x00, 0x01, 0x03, 0x6d, 0xd8, 0x5d, 0xc4, 0x01, 0x28, 0x80, 0x07,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x0f, 0x00, 0x00, 0xe2, 0x0e,
0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xca
};
/*
* Max resolution: 3840x2160@30Hz with RGB, YUV444, YUV422, YUV420
* Max BPC: 16 for all modes
* Max TMDS clock: 340 MHz
*
* edid-decode (hex):
*
* 00 ff ff ff ff ff ff 00 31 d8 34 00 00 00 00 00
* ff 23 01 03 80 60 36 78 0f ee 91 a3 54 4c 99 26
* 0f 50 54 20 00 00 01 01 01 01 01 01 01 01 01 01
* 01 01 01 01 01 01 04 74 00 30 f2 70 5a 80 b0 58
* 8a 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
* 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 18
* 55 18 5e 22 00 0a 20 20 20 20 20 20 00 00 00 10
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ce
*
* 02 03 27 31 41 5f 6c 03 0c 00 10 00 78 44 20 00
* 00 01 03 6d d8 5d c4 01 44 80 07 00 00 00 00 00
* 00 e3 0f 01 00 e1 0e 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 84
*
* ----------------
*
* Block 0, Base EDID:
* EDID Structure Version & Revision: 1.3
* Vendor & Product Identification:
* Manufacturer: LNX
* Model: 52
* Model year: 2025
* Basic Display Parameters & Features:
* Digital display
* Maximum image size: 96 cm x 54 cm
* Gamma: 2.20
* RGB color display
* Default (sRGB) color space is primary color space
* First detailed timing is the preferred timing
* Supports GTF timings within operating range
* Color Characteristics:
* Red : 0.6396, 0.3300
* Green: 0.2998, 0.5996
* Blue : 0.1503, 0.0595
* White: 0.3125, 0.3291
* Established Timings I & II:
* DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
* Standard Timings: none
* Detailed Timing Descriptors:
* DTD 1: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz (1600 mm x 900 mm)
* Hfront 176 Hsync 88 Hback 296 Hpol P
* Vfront 8 Vsync 10 Vback 72 Vpol P
* Display Product Name: 'Test EDID'
* Display Range Limits:
* Monitor ranges (GTF): 24-85 Hz V, 24-94 kHz H, max dotclock 340 MHz
* Dummy Descriptor:
* Extension blocks: 1
* Checksum: 0xce
*
* ----------------
*
* Block 1, CTA-861 Extension Block:
* Revision: 3
* Supports YCbCr 4:4:4
* Supports YCbCr 4:2:2
* Native detailed modes: 1
* Video Data Block:
* VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz
* Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
* Source physical address: 1.0.0.0
* DC_48bit
* DC_36bit
* DC_30bit
* DC_Y444
* Maximum TMDS clock: 340 MHz
* Extended HDMI video details:
* Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8:
* Version: 1
* Maximum TMDS Character Rate: 340 MHz
* SCDC Present
* Supports 16-bits/component Deep Color 4:2:0 Pixel Encoding
* Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding
* Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding
* YCbCr 4:2:0 Capability Map Data Block:
* VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz
* YCbCr 4:2:0 Video Data Block:
* Checksum: 0x84
*/
static const unsigned char test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x34, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0x23, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,
0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0x20,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x74, 0x00, 0x30, 0xf2, 0x70,
0x5a, 0x80, 0xb0, 0x58, 0x8a, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18,
0x55, 0x18, 0x5e, 0x22, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xce, 0x02, 0x03, 0x27, 0x31,
0x41, 0x5f, 0x6c, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x78, 0x44, 0x20, 0x00,
0x00, 0x01, 0x03, 0x6d, 0xd8, 0x5d, 0xc4, 0x01, 0x44, 0x80, 0x07, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x0f, 0x01, 0x00, 0xe1, 0x0e, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x84
};
#endif // DRM_KUNIT_EDID_H_
@@ -0,0 +1,400 @@
// SPDX-License-Identifier: GPL-2.0
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic_uapi.h>
#include <drm/drm_drv.h>
#include <drm/drm_edid.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_kunit_helpers.h>
#include <drm/drm_managed.h>
#include <kunit/device.h>
#include <kunit/resource.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/platform_device.h>
#define KUNIT_DEVICE_NAME "drm-kunit-mock-device"
static const struct drm_mode_config_funcs drm_mode_config_funcs = {
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
/**
* drm_kunit_helper_alloc_device - Allocate a mock device for a KUnit test
* @test: The test context object
*
* This allocates a fake struct &device to create a mock for a KUnit
* test. The device will also be bound to a fake driver. It will thus be
* able to leverage the usual infrastructure and most notably the
* device-managed resources just like a "real" device.
*
* Resources will be cleaned up automatically, but the removal can be
* forced using @drm_kunit_helper_free_device.
*
* Returns:
* A pointer to the new device, or an ERR_PTR() otherwise.
*/
struct device *drm_kunit_helper_alloc_device(struct kunit *test)
{
return kunit_device_register(test, KUNIT_DEVICE_NAME);
}
EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_device);
/**
* drm_kunit_helper_free_device - Frees a mock device
* @test: The test context object
* @dev: The device to free
*
* Frees a device allocated with drm_kunit_helper_alloc_device().
*/
void drm_kunit_helper_free_device(struct kunit *test, struct device *dev)
{
kunit_device_unregister(test, dev);
}
EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device);
struct drm_device *
__drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test,
struct device *dev,
size_t size, size_t offset,
const struct drm_driver *driver)
{
struct drm_device *drm;
void *container;
int ret;
container = __devm_drm_dev_alloc(dev, driver, size, offset);
if (IS_ERR(container))
return ERR_CAST(container);
drm = container + offset;
drm->mode_config.funcs = &drm_mode_config_funcs;
ret = drmm_mode_config_init(drm);
if (ret)
return ERR_PTR(ret);
return drm;
}
EXPORT_SYMBOL_GPL(__drm_kunit_helper_alloc_drm_device_with_driver);
static void kunit_action_drm_atomic_commit_put(void *ptr)
{
struct drm_atomic_commit *state = ptr;
drm_atomic_commit_put(state);
}
/**
* drm_kunit_helper_atomic_state_alloc - Allocates an atomic state
* @test: The test context object
* @drm: The device to alloc the state for
* @ctx: Locking context for that atomic update
*
* Allocates a empty atomic state.
*
* The state is tied to the kunit test context, so we must not call
* drm_atomic_commit_put() on it, it will be done so automatically.
*
* Returns:
* An ERR_PTR on error, a pointer to the newly allocated state otherwise
*/
struct drm_atomic_commit *
drm_kunit_helper_atomic_state_alloc(struct kunit *test,
struct drm_device *drm,
struct drm_modeset_acquire_ctx *ctx)
{
struct drm_atomic_commit *state;
int ret;
state = drm_atomic_commit_alloc(drm);
if (!state)
return ERR_PTR(-ENOMEM);
ret = kunit_add_action_or_reset(test,
kunit_action_drm_atomic_commit_put,
state);
if (ret)
return ERR_PTR(ret);
state->acquire_ctx = ctx;
return state;
}
EXPORT_SYMBOL_GPL(drm_kunit_helper_atomic_state_alloc);
static const uint32_t default_plane_formats[] = {
DRM_FORMAT_XRGB8888,
};
static const uint64_t default_plane_modifiers[] = {
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_INVALID
};
static const struct drm_plane_helper_funcs default_plane_helper_funcs = {
};
static const struct drm_plane_funcs default_plane_funcs = {
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.reset = drm_atomic_helper_plane_reset,
};
/**
* drm_kunit_helper_create_primary_plane - Creates a mock primary plane for a KUnit test
* @test: The test context object
* @drm: The device to alloc the plane for
* @funcs: Callbacks for the new plane. Optional.
* @helper_funcs: Helpers callbacks for the new plane. Optional.
* @formats: array of supported formats (DRM_FORMAT\_\*). Optional.
* @num_formats: number of elements in @formats
* @modifiers: array of struct drm_format modifiers terminated by
* DRM_FORMAT_MOD_INVALID. Optional.
*
* This allocates and initializes a mock struct &drm_plane meant to be
* part of a mock device for a KUnit test.
*
* Resources will be cleaned up automatically.
*
* @funcs will default to the default helpers implementations.
* @helper_funcs will default to an empty implementation. @formats will
* default to XRGB8888 only. @modifiers will default to a linear
* modifier only.
*
* Returns:
* A pointer to the new plane, or an ERR_PTR() otherwise.
*/
struct drm_plane *
drm_kunit_helper_create_primary_plane(struct kunit *test,
struct drm_device *drm,
const struct drm_plane_funcs *funcs,
const struct drm_plane_helper_funcs *helper_funcs,
const uint32_t *formats,
unsigned int num_formats,
const uint64_t *modifiers)
{
struct drm_plane *plane;
if (!funcs)
funcs = &default_plane_funcs;
if (!helper_funcs)
helper_funcs = &default_plane_helper_funcs;
if (!formats || !num_formats) {
formats = default_plane_formats;
num_formats = ARRAY_SIZE(default_plane_formats);
}
if (!modifiers)
modifiers = default_plane_modifiers;
plane = __drmm_universal_plane_alloc(drm,
sizeof(struct drm_plane), 0,
0,
funcs,
formats,
num_formats,
default_plane_modifiers,
DRM_PLANE_TYPE_PRIMARY,
NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane);
drm_plane_helper_add(plane, helper_funcs);
return plane;
}
EXPORT_SYMBOL_GPL(drm_kunit_helper_create_primary_plane);
static const struct drm_crtc_helper_funcs default_crtc_helper_funcs = {
};
static const struct drm_crtc_funcs default_crtc_funcs = {
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.reset = drm_atomic_helper_crtc_reset,
};
/**
* drm_kunit_helper_create_crtc - Creates a mock CRTC for a KUnit test
* @test: The test context object
* @drm: The device to alloc the plane for
* @primary: Primary plane for CRTC
* @cursor: Cursor plane for CRTC. Optional.
* @funcs: Callbacks for the new plane. Optional.
* @helper_funcs: Helpers callbacks for the new plane. Optional.
*
* This allocates and initializes a mock struct &drm_crtc meant to be
* part of a mock device for a KUnit test.
*
* Resources will be cleaned up automatically.
*
* @funcs will default to the default helpers implementations.
* @helper_funcs will default to an empty implementation.
*
* Returns:
* A pointer to the new CRTC, or an ERR_PTR() otherwise.
*/
struct drm_crtc *
drm_kunit_helper_create_crtc(struct kunit *test,
struct drm_device *drm,
struct drm_plane *primary,
struct drm_plane *cursor,
const struct drm_crtc_funcs *funcs,
const struct drm_crtc_helper_funcs *helper_funcs)
{
struct drm_crtc *crtc;
int ret;
if (!funcs)
funcs = &default_crtc_funcs;
if (!helper_funcs)
helper_funcs = &default_crtc_helper_funcs;
crtc = drmm_kzalloc(drm, sizeof(*crtc), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, crtc);
ret = drmm_crtc_init_with_planes(drm, crtc,
primary,
cursor,
funcs,
NULL);
KUNIT_ASSERT_EQ(test, ret, 0);
drm_crtc_helper_add(crtc, helper_funcs);
return crtc;
}
EXPORT_SYMBOL_GPL(drm_kunit_helper_create_crtc);
/**
* drm_kunit_helper_enable_crtc_connector - Enables a CRTC -> Connector output
* @test: The test context object
* @drm: The device to alloc the plane for
* @crtc: The CRTC to enable
* @connector: The Connector to enable
* @mode: The display mode to configure the CRTC with
* @ctx: Locking context
*
* This function creates an atomic update to enable the route from @crtc
* to @connector, with the given @mode.
*
* Returns:
*
* A pointer to the new CRTC, or an ERR_PTR() otherwise. If the error
* returned is EDEADLK, the entire atomic sequence must be restarted.
*/
int drm_kunit_helper_enable_crtc_connector(struct kunit *test,
struct drm_device *drm,
struct drm_crtc *crtc,
struct drm_connector *connector,
const struct drm_display_mode *mode,
struct drm_modeset_acquire_ctx *ctx)
{
struct drm_atomic_commit *state;
struct drm_connector_state *conn_state;
struct drm_crtc_state *crtc_state;
int ret;
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
if (IS_ERR(state))
return PTR_ERR(state);
conn_state = drm_atomic_get_connector_state(state, connector);
if (IS_ERR(conn_state))
return PTR_ERR(conn_state);
ret = drm_atomic_set_crtc_for_connector(conn_state, crtc);
if (ret)
return ret;
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
ret = drm_atomic_set_mode_for_crtc(crtc_state, mode);
if (ret)
return ret;
crtc_state->enable = true;
crtc_state->active = true;
ret = drm_atomic_commit(state);
if (ret)
return ret;
return 0;
}
EXPORT_SYMBOL_GPL(drm_kunit_helper_enable_crtc_connector);
static void kunit_action_drm_mode_destroy(void *ptr)
{
struct drm_display_mode *mode = ptr;
drm_mode_destroy(NULL, mode);
}
/**
* drm_kunit_add_mode_destroy_action() - Add a drm_destroy_mode kunit action
* @test: The test context object
* @mode: The drm_display_mode to destroy eventually
*
* Registers a kunit action that will destroy the drm_display_mode at
* the end of the test.
*
* If an error occurs, the drm_display_mode will be destroyed.
*
* Returns:
* 0 on success, an error code otherwise.
*/
int drm_kunit_add_mode_destroy_action(struct kunit *test,
struct drm_display_mode *mode)
{
return kunit_add_action_or_reset(test,
kunit_action_drm_mode_destroy,
mode);
}
EXPORT_SYMBOL_GPL(drm_kunit_add_mode_destroy_action);
/**
* drm_kunit_display_mode_from_cea_vic() - return a mode for CEA VIC for a KUnit test
* @test: The test context object
* @dev: DRM device
* @video_code: CEA VIC of the mode
*
* Creates a new mode matching the specified CEA VIC for a KUnit test.
*
* Resources will be cleaned up automatically.
*
* Returns: A new drm_display_mode on success or NULL on failure
*/
struct drm_display_mode *
drm_kunit_display_mode_from_cea_vic(struct kunit *test, struct drm_device *dev,
u8 video_code)
{
struct drm_display_mode *mode;
int ret;
mode = drm_display_mode_from_cea_vic(dev, video_code);
if (!mode)
return NULL;
ret = kunit_add_action_or_reset(test,
kunit_action_drm_mode_destroy,
mode);
if (ret)
return NULL;
return mode;
}
EXPORT_SYMBOL_GPL(drm_kunit_display_mode_from_cea_vic);
MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>");
MODULE_DESCRIPTION("KUnit test suite helper functions");
MODULE_LICENSE("GPL");
@@ -0,0 +1,117 @@
// SPDX-License-Identifier: GPL-2.0
#include <drm/drm_drv.h>
#include <drm/drm_kunit_helpers.h>
#include <drm/drm_managed.h>
#include <kunit/resource.h>
#include <linux/device.h>
/* Ought to be enough for anybody */
#define TEST_TIMEOUT_MS 100
struct managed_test_priv {
struct drm_device *drm;
bool action_done;
wait_queue_head_t action_wq;
};
static void drm_action(struct drm_device *drm, void *ptr)
{
struct managed_test_priv *priv = ptr;
priv->action_done = true;
wake_up_interruptible(&priv->action_wq);
}
/*
* The test verifies that the release action is called when
* drmm_release_action is called.
*/
static void drm_test_managed_release_action(struct kunit *test)
{
struct managed_test_priv *priv = test->priv;
int ret;
ret = drmm_add_action_or_reset(priv->drm, drm_action, priv);
KUNIT_EXPECT_EQ(test, ret, 0);
ret = drm_dev_register(priv->drm, 0);
KUNIT_ASSERT_EQ(test, ret, 0);
drmm_release_action(priv->drm, drm_action, priv);
ret = wait_event_interruptible_timeout(priv->action_wq, priv->action_done,
msecs_to_jiffies(TEST_TIMEOUT_MS));
KUNIT_EXPECT_GT(test, ret, 0);
drm_dev_unregister(priv->drm);
drm_kunit_helper_free_device(test, priv->drm->dev);
}
/*
* The test verifies that the release action is called automatically when the
* device is released.
*/
static void drm_test_managed_run_action(struct kunit *test)
{
struct managed_test_priv *priv = test->priv;
int ret;
ret = drmm_add_action_or_reset(priv->drm, drm_action, priv);
KUNIT_EXPECT_EQ(test, ret, 0);
ret = drm_dev_register(priv->drm, 0);
KUNIT_ASSERT_EQ(test, ret, 0);
drm_dev_unregister(priv->drm);
drm_kunit_helper_free_device(test, priv->drm->dev);
ret = wait_event_interruptible_timeout(priv->action_wq, priv->action_done,
msecs_to_jiffies(TEST_TIMEOUT_MS));
KUNIT_EXPECT_GT(test, ret, 0);
}
static int drm_managed_test_init(struct kunit *test)
{
struct managed_test_priv *priv;
struct device *dev;
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
init_waitqueue_head(&priv->action_wq);
dev = drm_kunit_helper_alloc_device(test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
/*
* DRM device can't be embedded in priv, since priv->action_done needs
* to remain allocated beyond both parent device and drm_device
* lifetime.
*/
priv->drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*priv->drm), 0,
DRIVER_MODESET);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
test->priv = priv;
return 0;
}
static struct kunit_case drm_managed_tests[] = {
KUNIT_CASE(drm_test_managed_release_action),
KUNIT_CASE(drm_test_managed_run_action),
{}
};
static struct kunit_suite drm_managed_test_suite = {
.name = "drm_managed",
.init = drm_managed_test_init,
.test_cases = drm_managed_tests
};
kunit_test_suite(drm_managed_test_suite);
MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>");
MODULE_DESCRIPTION("KUnit DRM managed test suite");
MODULE_LICENSE("GPL");
@@ -0,0 +1,359 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Test cases for the drm_mm range manager
*
* Copyright (c) 2022 Arthur Grillo <arthur.grillo@usp.br>
*/
#include <kunit/test.h>
#include <linux/prime_numbers.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/vmalloc.h>
#include <linux/ktime.h>
#include <drm/drm_mm.h>
#include <drm/drm_print.h>
enum {
BEST,
BOTTOMUP,
TOPDOWN,
EVICT,
};
static const struct insert_mode {
const char *name;
enum drm_mm_insert_mode mode;
} insert_modes[] = {
[BEST] = { "best", DRM_MM_INSERT_BEST },
[BOTTOMUP] = { "bottom-up", DRM_MM_INSERT_LOW },
[TOPDOWN] = { "top-down", DRM_MM_INSERT_HIGH },
[EVICT] = { "evict", DRM_MM_INSERT_EVICT },
{}
};
static bool assert_no_holes(struct kunit *test, const struct drm_mm *mm)
{
struct drm_mm_node *hole;
u64 hole_start, __always_unused hole_end;
unsigned long count;
count = 0;
drm_mm_for_each_hole(hole, mm, hole_start, hole_end)
count++;
if (count) {
KUNIT_FAIL(test,
"Expected to find no holes (after reserve), found %lu instead\n", count);
return false;
}
drm_mm_for_each_node(hole, mm) {
if (drm_mm_hole_follows(hole)) {
KUNIT_FAIL(test, "Hole follows node, expected none!\n");
return false;
}
}
return true;
}
static bool assert_one_hole(struct kunit *test, const struct drm_mm *mm, u64 start, u64 end)
{
struct drm_mm_node *hole;
u64 hole_start, hole_end;
unsigned long count;
bool ok = true;
if (end <= start)
return true;
count = 0;
drm_mm_for_each_hole(hole, mm, hole_start, hole_end) {
if (start != hole_start || end != hole_end) {
if (ok)
KUNIT_FAIL(test,
"empty mm has incorrect hole, found (%llx, %llx), expect (%llx, %llx)\n",
hole_start, hole_end, start, end);
ok = false;
}
count++;
}
if (count != 1) {
KUNIT_FAIL(test, "Expected to find one hole, found %lu instead\n", count);
ok = false;
}
return ok;
}
static u64 misalignment(struct drm_mm_node *node, u64 alignment)
{
u64 rem;
if (!alignment)
return 0;
div64_u64_rem(node->start, alignment, &rem);
return rem;
}
static bool assert_node(struct kunit *test, struct drm_mm_node *node, struct drm_mm *mm,
u64 size, u64 alignment, unsigned long color)
{
bool ok = true;
if (!drm_mm_node_allocated(node) || node->mm != mm) {
KUNIT_FAIL(test, "node not allocated\n");
ok = false;
}
if (node->size != size) {
KUNIT_FAIL(test, "node has wrong size, found %llu, expected %llu\n",
node->size, size);
ok = false;
}
if (misalignment(node, alignment)) {
KUNIT_FAIL(test,
"node is misaligned, start %llx rem %llu, expected alignment %llu\n",
node->start, misalignment(node, alignment), alignment);
ok = false;
}
if (node->color != color) {
KUNIT_FAIL(test, "node has wrong color, found %lu, expected %lu\n",
node->color, color);
ok = false;
}
return ok;
}
static void drm_test_mm_init(struct kunit *test)
{
const unsigned int size = 4096;
struct drm_mm mm;
struct drm_mm_node tmp;
/* Start with some simple checks on initialising the struct drm_mm */
memset(&mm, 0, sizeof(mm));
KUNIT_ASSERT_FALSE_MSG(test, drm_mm_initialized(&mm),
"zeroed mm claims to be initialized\n");
memset(&mm, 0xff, sizeof(mm));
drm_mm_init(&mm, 0, size);
if (!drm_mm_initialized(&mm)) {
KUNIT_FAIL(test, "mm claims not to be initialized\n");
goto out;
}
if (!drm_mm_clean(&mm)) {
KUNIT_FAIL(test, "mm not empty on creation\n");
goto out;
}
/* After creation, it should all be one massive hole */
if (!assert_one_hole(test, &mm, 0, size)) {
KUNIT_FAIL(test, "mm not one hole on creation");
goto out;
}
memset(&tmp, 0, sizeof(tmp));
tmp.start = 0;
tmp.size = size;
if (drm_mm_reserve_node(&mm, &tmp)) {
KUNIT_FAIL(test, "failed to reserve whole drm_mm\n");
goto out;
}
/* After filling the range entirely, there should be no holes */
if (!assert_no_holes(test, &mm)) {
KUNIT_FAIL(test, "mm has holes when filled");
goto out;
}
/* And then after emptying it again, the massive hole should be back */
drm_mm_remove_node(&tmp);
if (!assert_one_hole(test, &mm, 0, size)) {
KUNIT_FAIL(test, "mm does not have single hole after emptying");
goto out;
}
out:
drm_mm_takedown(&mm);
}
static void drm_test_mm_debug(struct kunit *test)
{
struct drm_printer p = drm_dbg_printer(NULL, DRM_UT_CORE, test->name);
struct drm_mm mm;
struct drm_mm_node nodes[2];
/* Create a small drm_mm with a couple of nodes and a few holes, and
* check that the debug iterator doesn't explode over a trivial drm_mm.
*/
drm_mm_init(&mm, 0, 4096);
memset(nodes, 0, sizeof(nodes));
nodes[0].start = 512;
nodes[0].size = 1024;
KUNIT_ASSERT_FALSE_MSG(test, drm_mm_reserve_node(&mm, &nodes[0]),
"failed to reserve node[0] {start=%lld, size=%lld)\n",
nodes[0].start, nodes[0].size);
nodes[1].size = 1024;
nodes[1].start = 4096 - 512 - nodes[1].size;
KUNIT_ASSERT_FALSE_MSG(test, drm_mm_reserve_node(&mm, &nodes[1]),
"failed to reserve node[0] {start=%lld, size=%lld)\n",
nodes[0].start, nodes[0].size);
drm_mm_print(&mm, &p);
KUNIT_SUCCEED(test);
}
static bool expect_insert(struct kunit *test, struct drm_mm *mm,
struct drm_mm_node *node, u64 size, u64 alignment, unsigned long color,
const struct insert_mode *mode)
{
int err;
err = drm_mm_insert_node_generic(mm, node,
size, alignment, color,
mode->mode);
if (err) {
KUNIT_FAIL(test,
"insert (size=%llu, alignment=%llu, color=%lu, mode=%s) failed with err=%d\n",
size, alignment, color, mode->name, err);
return false;
}
if (!assert_node(test, node, mm, size, alignment, color)) {
drm_mm_remove_node(node);
return false;
}
return true;
}
static void drm_test_mm_align_pot(struct kunit *test, int max)
{
struct drm_mm mm;
struct drm_mm_node *node, *next;
int bit;
/* Check that we can align to the full u64 address space */
drm_mm_init(&mm, 1, U64_MAX - 2);
for (bit = max - 1; bit; bit--) {
u64 align, size;
node = kzalloc_obj(*node);
if (!node) {
KUNIT_FAIL(test, "failed to allocate node");
goto out;
}
align = BIT_ULL(bit);
size = BIT_ULL(bit - 1) + 1;
if (!expect_insert(test, &mm, node, size, align, bit, &insert_modes[0])) {
KUNIT_FAIL(test, "insert failed with alignment=%llx [%d]", align, bit);
goto out;
}
cond_resched();
}
out:
drm_mm_for_each_node_safe(node, next, &mm) {
drm_mm_remove_node(node);
kfree(node);
}
drm_mm_takedown(&mm);
}
static void drm_test_mm_align32(struct kunit *test)
{
drm_test_mm_align_pot(test, 32);
}
static void drm_test_mm_align64(struct kunit *test)
{
drm_test_mm_align_pot(test, 64);
}
static void drm_test_mm_once(struct kunit *test, unsigned int mode)
{
struct drm_mm mm;
struct drm_mm_node rsvd_lo, rsvd_hi, node;
drm_mm_init(&mm, 0, 7);
memset(&rsvd_lo, 0, sizeof(rsvd_lo));
rsvd_lo.start = 1;
rsvd_lo.size = 1;
if (drm_mm_reserve_node(&mm, &rsvd_lo)) {
KUNIT_FAIL(test, "Could not reserve low node\n");
goto err;
}
memset(&rsvd_hi, 0, sizeof(rsvd_hi));
rsvd_hi.start = 5;
rsvd_hi.size = 1;
if (drm_mm_reserve_node(&mm, &rsvd_hi)) {
KUNIT_FAIL(test, "Could not reserve low node\n");
goto err_lo;
}
if (!drm_mm_hole_follows(&rsvd_lo) || !drm_mm_hole_follows(&rsvd_hi)) {
KUNIT_FAIL(test, "Expected a hole after lo and high nodes!\n");
goto err_hi;
}
memset(&node, 0, sizeof(node));
if (drm_mm_insert_node_generic(&mm, &node, 2, 0, 0, mode)) {
KUNIT_FAIL(test, "Could not insert the node into the available hole!\n");
goto err_hi;
}
drm_mm_remove_node(&node);
err_hi:
drm_mm_remove_node(&rsvd_hi);
err_lo:
drm_mm_remove_node(&rsvd_lo);
err:
drm_mm_takedown(&mm);
}
static void drm_test_mm_lowest(struct kunit *test)
{
drm_test_mm_once(test, DRM_MM_INSERT_LOW);
}
static void drm_test_mm_highest(struct kunit *test)
{
drm_test_mm_once(test, DRM_MM_INSERT_HIGH);
}
static struct kunit_case drm_mm_tests[] = {
KUNIT_CASE(drm_test_mm_init),
KUNIT_CASE(drm_test_mm_debug),
KUNIT_CASE(drm_test_mm_align32),
KUNIT_CASE(drm_test_mm_align64),
KUNIT_CASE(drm_test_mm_lowest),
KUNIT_CASE(drm_test_mm_highest),
{}
};
static struct kunit_suite drm_mm_test_suite = {
.name = "drm_mm",
.test_cases = drm_mm_tests,
};
kunit_test_suite(drm_mm_test_suite);
MODULE_AUTHOR("Intel Corporation");
MODULE_DESCRIPTION("Test cases for the drm_mm range manager");
MODULE_LICENSE("GPL");
@@ -0,0 +1,208 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Kunit test for drm_modes functions
*/
#include <drm/drm_drv.h>
#include <drm/drm_kunit_helpers.h>
#include <drm/drm_modes.h>
#include <kunit/test.h>
#include <linux/units.h>
struct drm_test_modes_priv {
struct drm_device *drm;
struct device *dev;
};
static int drm_test_modes_init(struct kunit *test)
{
struct drm_test_modes_priv *priv;
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, 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);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
test->priv = priv;
return 0;
}
static void drm_test_modes_analog_tv_ntsc_480i(struct kunit *test)
{
struct drm_test_modes_priv *priv = test->priv;
struct drm_display_mode *mode;
int ret;
mode = drm_analog_tv_mode(priv->drm,
DRM_MODE_TV_MODE_NTSC,
13500 * HZ_PER_KHZ, 720, 480,
true);
KUNIT_ASSERT_NOT_NULL(test, mode);
ret = drm_kunit_add_mode_destroy_action(test, mode);
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, drm_mode_vrefresh(mode), 60);
KUNIT_EXPECT_EQ(test, mode->hdisplay, 720);
/* BT.601 defines hsync_start at 736 for 480i */
KUNIT_EXPECT_EQ(test, mode->hsync_start, 736);
/*
* The NTSC standard expects a line to take 63.556us. With a
* pixel clock of 13.5 MHz, a pixel takes around 74ns, so we
* need to have 63556ns / 74ns = 858.
*
* This is also mandated by BT.601.
*/
KUNIT_EXPECT_EQ(test, mode->htotal, 858);
KUNIT_EXPECT_EQ(test, mode->vdisplay, 480);
KUNIT_EXPECT_EQ(test, mode->vtotal, 525);
}
static void drm_test_modes_analog_tv_ntsc_480i_inlined(struct kunit *test)
{
struct drm_test_modes_priv *priv = test->priv;
struct drm_display_mode *expected, *mode;
int ret;
expected = drm_analog_tv_mode(priv->drm,
DRM_MODE_TV_MODE_NTSC,
13500 * HZ_PER_KHZ, 720, 480,
true);
KUNIT_ASSERT_NOT_NULL(test, expected);
ret = drm_kunit_add_mode_destroy_action(test, expected);
KUNIT_ASSERT_EQ(test, ret, 0);
mode = drm_mode_analog_ntsc_480i(priv->drm);
KUNIT_ASSERT_NOT_NULL(test, mode);
ret = drm_kunit_add_mode_destroy_action(test, mode);
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_TRUE(test, drm_mode_equal(expected, mode));
}
static void drm_test_modes_analog_tv_pal_576i(struct kunit *test)
{
struct drm_test_modes_priv *priv = test->priv;
struct drm_display_mode *mode;
int ret;
mode = drm_analog_tv_mode(priv->drm,
DRM_MODE_TV_MODE_PAL,
13500 * HZ_PER_KHZ, 720, 576,
true);
KUNIT_ASSERT_NOT_NULL(test, mode);
ret = drm_kunit_add_mode_destroy_action(test, mode);
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, drm_mode_vrefresh(mode), 50);
KUNIT_EXPECT_EQ(test, mode->hdisplay, 720);
/* BT.601 defines hsync_start at 732 for 576i */
KUNIT_EXPECT_EQ(test, mode->hsync_start, 732);
/*
* The PAL standard expects a line to take 64us. With a pixel
* clock of 13.5 MHz, a pixel takes around 74ns, so we need to
* have 64000ns / 74ns = 864.
*
* This is also mandated by BT.601.
*/
KUNIT_EXPECT_EQ(test, mode->htotal, 864);
KUNIT_EXPECT_EQ(test, mode->vdisplay, 576);
KUNIT_EXPECT_EQ(test, mode->vtotal, 625);
}
static void drm_test_modes_analog_tv_pal_576i_inlined(struct kunit *test)
{
struct drm_test_modes_priv *priv = test->priv;
struct drm_display_mode *expected, *mode;
int ret;
expected = drm_analog_tv_mode(priv->drm,
DRM_MODE_TV_MODE_PAL,
13500 * HZ_PER_KHZ, 720, 576,
true);
KUNIT_ASSERT_NOT_NULL(test, expected);
ret = drm_kunit_add_mode_destroy_action(test, expected);
KUNIT_ASSERT_EQ(test, ret, 0);
mode = drm_mode_analog_pal_576i(priv->drm);
KUNIT_ASSERT_NOT_NULL(test, mode);
ret = drm_kunit_add_mode_destroy_action(test, mode);
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_TRUE(test, drm_mode_equal(expected, mode));
}
static void drm_test_modes_analog_tv_mono_576i(struct kunit *test)
{
struct drm_test_modes_priv *priv = test->priv;
struct drm_display_mode *mode;
int ret;
mode = drm_analog_tv_mode(priv->drm,
DRM_MODE_TV_MODE_MONOCHROME,
13500 * HZ_PER_KHZ, 720, 576,
true);
KUNIT_ASSERT_NOT_NULL(test, mode);
ret = drm_kunit_add_mode_destroy_action(test, mode);
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, drm_mode_vrefresh(mode), 50);
KUNIT_EXPECT_EQ(test, mode->hdisplay, 720);
/* BT.601 defines hsync_start at 732 for 576i */
KUNIT_EXPECT_EQ(test, mode->hsync_start, 732);
/*
* The PAL standard expects a line to take 64us. With a pixel
* clock of 13.5 MHz, a pixel takes around 74ns, so we need to
* have 64000ns / 74ns = 864.
*
* This is also mandated by BT.601.
*/
KUNIT_EXPECT_EQ(test, mode->htotal, 864);
KUNIT_EXPECT_EQ(test, mode->vdisplay, 576);
KUNIT_EXPECT_EQ(test, mode->vtotal, 625);
}
static struct kunit_case drm_modes_analog_tv_tests[] = {
KUNIT_CASE(drm_test_modes_analog_tv_mono_576i),
KUNIT_CASE(drm_test_modes_analog_tv_ntsc_480i),
KUNIT_CASE(drm_test_modes_analog_tv_ntsc_480i_inlined),
KUNIT_CASE(drm_test_modes_analog_tv_pal_576i),
KUNIT_CASE(drm_test_modes_analog_tv_pal_576i_inlined),
{ }
};
static struct kunit_suite drm_modes_analog_tv_test_suite = {
.name = "drm_modes_analog_tv",
.init = drm_test_modes_init,
.test_cases = drm_modes_analog_tv_tests,
};
kunit_test_suite(drm_modes_analog_tv_test_suite);
MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>");
MODULE_DESCRIPTION("Kunit test for drm_modes functions");
MODULE_LICENSE("GPL");
@@ -0,0 +1,221 @@
// SPDX-License-Identifier: GPL-2.0 or MIT
/*
* Copyright (c) 2025 Red Hat.
* Author: Jocelyn Falempe <jfalempe@redhat.com>
*
* KUNIT tests for drm panic
*/
#include <drm/drm_fourcc.h>
#include <drm/drm_panic.h>
#include <kunit/test.h>
#include <linux/units.h>
#include <linux/vmalloc.h>
/* Check the framebuffer color only if the panic colors are the default */
#if (CONFIG_DRM_PANIC_BACKGROUND_COLOR == 0 && \
CONFIG_DRM_PANIC_FOREGROUND_COLOR == 0xffffff)
static void drm_panic_check_color_byte(struct kunit *test, u8 b)
{
KUNIT_EXPECT_TRUE(test, (b == 0 || b == 0xff));
}
#else
static void drm_panic_check_color_byte(struct kunit *test, u8 b) {}
#endif
struct drm_test_mode {
const int width;
const int height;
const u32 format;
void (*draw_screen)(struct drm_scanout_buffer *sb);
const char *fname;
};
/*
* Run all tests for the 3 panic screens: user, kmsg and qr_code
*/
#define DRM_TEST_MODE_LIST(func) \
DRM_PANIC_TEST_MODE(1024, 768, DRM_FORMAT_XRGB8888, func) \
DRM_PANIC_TEST_MODE(300, 200, DRM_FORMAT_XRGB8888, func) \
DRM_PANIC_TEST_MODE(1920, 1080, DRM_FORMAT_XRGB8888, func) \
DRM_PANIC_TEST_MODE(1024, 768, DRM_FORMAT_RGB565, func) \
DRM_PANIC_TEST_MODE(1024, 768, DRM_FORMAT_RGB888, func) \
#define DRM_PANIC_TEST_MODE(w, h, f, name) { \
.width = w, \
.height = h, \
.format = f, \
.draw_screen = draw_panic_screen_##name, \
.fname = #name, \
}, \
static const struct drm_test_mode drm_test_modes_cases[] = {
DRM_TEST_MODE_LIST(user)
DRM_TEST_MODE_LIST(kmsg)
#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
DRM_TEST_MODE_LIST(qr_code)
#endif
};
#undef DRM_PANIC_TEST_MODE
static int drm_test_panic_init(struct kunit *test)
{
struct drm_scanout_buffer *priv;
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, priv);
test->priv = priv;
drm_panic_set_description("Kunit testing");
return 0;
}
/*
* Test drawing the panic screen, using a memory mapped framebuffer
* Set the whole buffer to 0xa5, and then check that all pixels have been
* written.
*/
static void drm_test_panic_screen_user_map(struct kunit *test)
{
struct drm_scanout_buffer *sb = test->priv;
const struct drm_test_mode *params = test->param_value;
char *fb;
int fb_size;
int i;
sb->format = drm_format_info(params->format);
fb_size = params->width * params->height * sb->format->cpp[0];
fb = vmalloc(fb_size);
KUNIT_ASSERT_NOT_NULL(test, fb);
memset(fb, 0xa5, fb_size);
iosys_map_set_vaddr(&sb->map[0], fb);
sb->width = params->width;
sb->height = params->height;
sb->pitch[0] = params->width * sb->format->cpp[0];
params->draw_screen(sb);
for (i = 0; i < fb_size; i++)
drm_panic_check_color_byte(test, fb[i]);
vfree(fb);
}
/*
* Test drawing the panic screen, using a list of pages framebuffer
* Set the whole buffer to 0xa5, and then check that all pixels have been
* written.
*/
static void drm_test_panic_screen_user_page(struct kunit *test)
{
struct drm_scanout_buffer *sb = test->priv;
const struct drm_test_mode *params = test->param_value;
int fb_size, p, i, npages;
struct page **pages;
u8 *vaddr;
sb->format = drm_format_info(params->format);
fb_size = params->width * params->height * sb->format->cpp[0];
npages = DIV_ROUND_UP(fb_size, PAGE_SIZE);
pages = kmalloc_objs(struct page *, npages);
KUNIT_ASSERT_NOT_NULL(test, pages);
for (p = 0; p < npages; p++) {
pages[p] = alloc_page(GFP_KERNEL);
if (!pages[p]) {
npages = p - 1;
KUNIT_FAIL(test, "Can't allocate page\n");
goto free_pages;
}
vaddr = kmap_local_page(pages[p]);
memset(vaddr, 0xa5, PAGE_SIZE);
kunmap_local(vaddr);
}
sb->pages = pages;
sb->width = params->width;
sb->height = params->height;
sb->pitch[0] = params->width * sb->format->cpp[0];
params->draw_screen(sb);
for (p = 0; p < npages; p++) {
int bytes_in_page = (p == npages - 1) ? fb_size - p * PAGE_SIZE : PAGE_SIZE;
vaddr = kmap_local_page(pages[p]);
for (i = 0; i < bytes_in_page; i++)
drm_panic_check_color_byte(test, vaddr[i]);
kunmap_local(vaddr);
}
free_pages:
for (p = 0; p < npages; p++)
__free_page(pages[p]);
kfree(pages);
}
static void drm_test_panic_set_pixel(struct drm_scanout_buffer *sb,
unsigned int x,
unsigned int y,
u32 color)
{
struct kunit *test = (struct kunit *)sb->private;
KUNIT_ASSERT_TRUE(test, x < sb->width && y < sb->height);
}
/*
* Test drawing the panic screen, using the set_pixel callback
* Check that all calls to set_pixel() are within the framebuffer
*/
static void drm_test_panic_screen_user_set_pixel(struct kunit *test)
{
struct drm_scanout_buffer *sb = test->priv;
const struct drm_test_mode *params = test->param_value;
sb->format = drm_format_info(params->format);
sb->set_pixel = drm_test_panic_set_pixel;
sb->width = params->width;
sb->height = params->height;
sb->private = test;
params->draw_screen(sb);
}
static void drm_test_panic_desc(const struct drm_test_mode *t, char *desc)
{
sprintf(desc, "Panic screen %s, mode: %d x %d \t%p4cc",
t->fname, t->width, t->height, &t->format);
}
KUNIT_ARRAY_PARAM(drm_test_panic_screen_user_map, drm_test_modes_cases, drm_test_panic_desc);
KUNIT_ARRAY_PARAM(drm_test_panic_screen_user_page, drm_test_modes_cases, drm_test_panic_desc);
KUNIT_ARRAY_PARAM(drm_test_panic_screen_user_set_pixel, drm_test_modes_cases, drm_test_panic_desc);
static struct kunit_case drm_panic_screen_user_test[] = {
KUNIT_CASE_PARAM(drm_test_panic_screen_user_map,
drm_test_panic_screen_user_map_gen_params),
KUNIT_CASE_PARAM(drm_test_panic_screen_user_page,
drm_test_panic_screen_user_page_gen_params),
KUNIT_CASE_PARAM(drm_test_panic_screen_user_set_pixel,
drm_test_panic_screen_user_set_pixel_gen_params),
{ }
};
static struct kunit_suite drm_panic_suite = {
.name = "drm_panic",
.init = drm_test_panic_init,
.test_cases = drm_panic_screen_user_test,
};
kunit_test_suite(drm_panic_suite);
@@ -0,0 +1,319 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Test cases for the drm_plane_helper functions
*
* Copyright (c) 2022 Maíra Canal <mairacanal@riseup.net>
*/
#include <kunit/test.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_modes.h>
#include <drm/drm_rect.h>
static const struct drm_crtc_state crtc_state = {
.crtc = ZERO_SIZE_PTR,
.enable = true,
.active = true,
.mode = {
DRM_MODE("1024x768", 0, 65000, 1024, 1048,
1184, 1344, 0, 768, 771, 777, 806, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)
},
};
struct drm_check_plane_state_test {
const char *name;
const char *msg;
struct {
unsigned int x;
unsigned int y;
unsigned int w;
unsigned int h;
} src, src_expected;
struct {
int x;
int y;
unsigned int w;
unsigned int h;
} crtc, crtc_expected;
unsigned int rotation;
int min_scale;
int max_scale;
bool can_position;
};
static int drm_plane_helper_init(struct kunit *test)
{
const struct drm_check_plane_state_test *params = test->param_value;
struct drm_plane *plane;
struct drm_framebuffer *fb;
struct drm_plane_state *mock;
plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, plane);
fb = kunit_kzalloc(test, sizeof(*fb), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, fb);
fb->width = 2048;
fb->height = 2048;
mock = kunit_kzalloc(test, sizeof(*mock), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, mock);
mock->plane = plane;
mock->crtc = ZERO_SIZE_PTR;
mock->fb = fb;
mock->rotation = params->rotation;
mock->src_x = params->src.x;
mock->src_y = params->src.y;
mock->src_w = params->src.w;
mock->src_h = params->src.h;
mock->crtc_x = params->crtc.x;
mock->crtc_y = params->crtc.y;
mock->crtc_w = params->crtc.w;
mock->crtc_h = params->crtc.h;
test->priv = mock;
return 0;
}
static void check_src_eq(struct kunit *test, struct drm_plane_state *plane_state,
unsigned int src_x, unsigned int src_y,
unsigned int src_w, unsigned int src_h)
{
struct drm_rect expected = DRM_RECT_INIT(src_x, src_y, src_w, src_h);
KUNIT_ASSERT_GE_MSG(test, plane_state->src.x1, 0,
"src x coordinate %x should never be below 0, src: " DRM_RECT_FP_FMT,
plane_state->src.x1, DRM_RECT_FP_ARG(&plane_state->src));
KUNIT_ASSERT_GE_MSG(test, plane_state->src.y1, 0,
"src y coordinate %x should never be below 0, src: " DRM_RECT_FP_FMT,
plane_state->src.y1, DRM_RECT_FP_ARG(&plane_state->src));
KUNIT_EXPECT_TRUE_MSG(test, drm_rect_equals(&plane_state->src, &expected),
"dst: " DRM_RECT_FP_FMT ", expected: " DRM_RECT_FP_FMT,
DRM_RECT_FP_ARG(&plane_state->src), DRM_RECT_FP_ARG(&expected));
}
static void check_crtc_eq(struct kunit *test, struct drm_plane_state *plane_state,
int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h)
{
struct drm_rect expected = DRM_RECT_INIT(crtc_x, crtc_y, crtc_w, crtc_h);
KUNIT_EXPECT_TRUE_MSG(test, drm_rect_equals(&plane_state->dst, &expected),
"dst: " DRM_RECT_FMT ", expected: " DRM_RECT_FMT,
DRM_RECT_ARG(&plane_state->dst), DRM_RECT_ARG(&expected));
}
static void drm_test_check_plane_state(struct kunit *test)
{
const struct drm_check_plane_state_test *params = test->param_value;
struct drm_plane_state *plane_state = test->priv;
KUNIT_ASSERT_EQ_MSG(test,
drm_atomic_helper_check_plane_state(plane_state, &crtc_state,
params->min_scale,
params->max_scale,
params->can_position, false),
0, params->msg);
KUNIT_EXPECT_TRUE(test, plane_state->visible);
check_src_eq(test, plane_state, params->src_expected.x, params->src_expected.y,
params->src_expected.w, params->src_expected.h);
check_crtc_eq(test, plane_state, params->crtc_expected.x, params->crtc_expected.y,
params->crtc_expected.w, params->crtc_expected.h);
}
static void drm_check_plane_state_desc(const struct drm_check_plane_state_test *t,
char *desc)
{
sprintf(desc, "%s", t->name);
}
static const struct drm_check_plane_state_test drm_check_plane_state_tests[] = {
{
.name = "clipping_simple",
.msg = "Simple clipping check should pass",
.src = { 0, 0,
2048 << 16,
2048 << 16 },
.crtc = { 0, 0, 2048, 2048 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = DRM_PLANE_NO_SCALING,
.can_position = false,
.src_expected = { 0, 0, 1024 << 16, 768 << 16 },
.crtc_expected = { 0, 0, 1024, 768 },
},
{
.name = "clipping_rotate_reflect",
.msg = "Rotated clipping check should pass",
.src = { 0, 0,
2048 << 16,
2048 << 16 },
.crtc = { 0, 0, 2048, 2048 },
.rotation = DRM_MODE_ROTATE_90 | DRM_MODE_REFLECT_X,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = DRM_PLANE_NO_SCALING,
.can_position = false,
.src_expected = { 0, 0, 768 << 16, 1024 << 16 },
.crtc_expected = { 0, 0, 1024, 768 },
},
{
.name = "positioning_simple",
.msg = "Simple positioning should work",
.src = { 0, 0, 1023 << 16, 767 << 16 },
.crtc = { 0, 0, 1023, 767 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = DRM_PLANE_NO_SCALING,
.can_position = true,
.src_expected = { 0, 0, 1023 << 16, 767 << 16 },
.crtc_expected = { 0, 0, 1023, 767 },
},
{
.name = "upscaling",
.msg = "Upscaling exactly 2x should work",
.src = { 0, 0, 512 << 16, 384 << 16 },
.crtc = { 0, 0, 1024, 768 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = 0x8000,
.max_scale = DRM_PLANE_NO_SCALING,
.can_position = false,
.src_expected = { 0, 0, 512 << 16, 384 << 16 },
.crtc_expected = { 0, 0, 1024, 768 },
},
{
.name = "downscaling",
.msg = "Should succeed with exact scaling limit",
.src = { 0, 0, 2048 << 16, 1536 << 16 },
.crtc = { 0, 0, 1024, 768 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = 0x20000,
.can_position = false,
.src_expected = { 0, 0, 2048 << 16, 1536 << 16 },
.crtc_expected = { 0, 0, 1024, 768 },
},
{
.name = "rounding1",
.msg = "Should succeed by clipping to exact multiple",
.src = { 0, 0, 0x40001, 0x40001 },
.crtc = { 1022, 766, 4, 4 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = 0x10001,
.can_position = true,
.src_expected = { 0, 0, 2 << 16, 2 << 16 },
.crtc_expected = { 1022, 766, 2, 2 },
},
{
.name = "rounding2",
.msg = "Should succeed by clipping to exact multiple",
.src = { 0x20001, 0x20001, 0x4040001, 0x3040001 },
.crtc = { -2, -2, 1028, 772 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = 0x10001,
.can_position = false,
.src_expected = { 0x40002, 0x40002, 1024 << 16, 768 << 16 },
.crtc_expected = { 0, 0, 1024, 768 },
},
{
.name = "rounding3",
.msg = "Should succeed by clipping to exact multiple",
.src = { 0, 0, 0x3ffff, 0x3ffff },
.crtc = { 1022, 766, 4, 4 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = 0xffff,
.max_scale = DRM_PLANE_NO_SCALING,
.can_position = true,
/* Should not be rounded to 0x20001, which would be upscaling. */
.src_expected = { 0, 0, 2 << 16, 2 << 16 },
.crtc_expected = { 1022, 766, 2, 2 },
},
{
.name = "rounding4",
.msg = "Should succeed by clipping to exact multiple",
.src = { 0x1ffff, 0x1ffff, 0x403ffff, 0x303ffff },
.crtc = { -2, -2, 1028, 772 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = 0xffff,
.max_scale = DRM_PLANE_NO_SCALING,
.can_position = false,
.src_expected = { 0x3fffe, 0x3fffe, 1024 << 16, 768 << 16 },
.crtc_expected = { 0, 0, 1024, 768 },
},
};
KUNIT_ARRAY_PARAM(drm_check_plane_state, drm_check_plane_state_tests, drm_check_plane_state_desc);
static void drm_test_check_invalid_plane_state(struct kunit *test)
{
const struct drm_check_plane_state_test *params = test->param_value;
struct drm_plane_state *plane_state = test->priv;
KUNIT_ASSERT_LT_MSG(test,
drm_atomic_helper_check_plane_state(plane_state, &crtc_state,
params->min_scale,
params->max_scale,
params->can_position, false),
0, params->msg);
}
static const struct drm_check_plane_state_test drm_check_invalid_plane_state_tests[] = {
{
.name = "positioning_invalid",
.msg = "Should not be able to position on the crtc with can_position=false",
.src = { 0, 0, 1023 << 16, 767 << 16 },
.crtc = { 0, 0, 1023, 767 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = DRM_PLANE_NO_SCALING,
.can_position = false,
},
{
.name = "upscaling_invalid",
.msg = "Upscaling out of range should fail",
.src = { 0, 0, 512 << 16, 384 << 16 },
.crtc = { 0, 0, 1024, 768 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = 0x8001,
.max_scale = DRM_PLANE_NO_SCALING,
.can_position = false,
},
{
.name = "downscaling_invalid",
.msg = "Downscaling out of range should fail",
.src = { 0, 0, 2048 << 16, 1536 << 16 },
.crtc = { 0, 0, 1024, 768 },
.rotation = DRM_MODE_ROTATE_0,
.min_scale = DRM_PLANE_NO_SCALING,
.max_scale = 0x1ffff,
.can_position = false,
},
};
KUNIT_ARRAY_PARAM(drm_check_invalid_plane_state, drm_check_invalid_plane_state_tests,
drm_check_plane_state_desc);
static struct kunit_case drm_plane_helper_test[] = {
KUNIT_CASE_PARAM(drm_test_check_plane_state, drm_check_plane_state_gen_params),
KUNIT_CASE_PARAM(drm_test_check_invalid_plane_state,
drm_check_invalid_plane_state_gen_params),
{}
};
static struct kunit_suite drm_plane_helper_test_suite = {
.name = "drm_plane_helper",
.init = drm_plane_helper_init,
.test_cases = drm_plane_helper_test,
};
kunit_test_suite(drm_plane_helper_test_suite);
MODULE_DESCRIPTION("Test cases for the drm_plane_helper functions");
MODULE_LICENSE("GPL");
@@ -0,0 +1,217 @@
// 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;
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);
ret = drm_kunit_add_mode_destroy_action(test, expected);
KUNIT_ASSERT_EQ(test, ret, 0);
}
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);
ret = drm_kunit_add_mode_destroy_action(test, expected);
KUNIT_ASSERT_EQ(test, ret, 0);
}
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_DESCRIPTION("Kunit test for drm_probe_helper functions");
MODULE_LICENSE("GPL");
@@ -0,0 +1,564 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Test cases for the drm_rect functions
*
* Copyright (c) 2022 Maíra Canal <mairacanal@riseup.net>
*/
#include <kunit/test.h>
#include <drm/drm_rect.h>
#include <drm/drm_mode.h>
#include <linux/limits.h>
#include <linux/string_helpers.h>
#include <linux/errno.h>
static void drm_rect_compare(struct kunit *test, const struct drm_rect *r,
const struct drm_rect *expected)
{
KUNIT_EXPECT_EQ(test, r->x1, expected->x1);
KUNIT_EXPECT_EQ(test, r->y1, expected->y1);
KUNIT_EXPECT_EQ(test, drm_rect_width(r), drm_rect_width(expected));
KUNIT_EXPECT_EQ(test, drm_rect_height(r), drm_rect_height(expected));
}
static void drm_test_rect_clip_scaled_div_by_zero(struct kunit *test)
{
struct drm_rect src, dst, clip;
bool visible;
/*
* Make sure we don't divide by zero when dst
* width/height is zero and dst and clip do not intersect.
*/
drm_rect_init(&src, 0, 0, 0, 0);
drm_rect_init(&dst, 0, 0, 0, 0);
drm_rect_init(&clip, 1, 1, 1, 1);
visible = drm_rect_clip_scaled(&src, &dst, &clip);
KUNIT_EXPECT_FALSE_MSG(test, visible, "Destination not be visible\n");
KUNIT_EXPECT_FALSE_MSG(test, drm_rect_visible(&src), "Source should not be visible\n");
drm_rect_init(&src, 0, 0, 0, 0);
drm_rect_init(&dst, 3, 3, 0, 0);
drm_rect_init(&clip, 1, 1, 1, 1);
visible = drm_rect_clip_scaled(&src, &dst, &clip);
KUNIT_EXPECT_FALSE_MSG(test, visible, "Destination not be visible\n");
KUNIT_EXPECT_FALSE_MSG(test, drm_rect_visible(&src), "Source should not be visible\n");
}
static void drm_test_rect_clip_scaled_not_clipped(struct kunit *test)
{
struct drm_rect src, dst, clip;
bool visible;
/* 1:1 scaling */
drm_rect_init(&src, 0, 0, 1 << 16, 1 << 16);
drm_rect_init(&dst, 0, 0, 1, 1);
drm_rect_init(&clip, 0, 0, 1, 1);
visible = drm_rect_clip_scaled(&src, &dst, &clip);
KUNIT_EXPECT_FALSE_MSG(test, src.x1 != 0 || src.x2 != 1 << 16 ||
src.y1 != 0 || src.y2 != 1 << 16, "Source badly clipped\n");
KUNIT_EXPECT_FALSE_MSG(test, dst.x1 != 0 || dst.x2 != 1 ||
dst.y1 != 0 || dst.y2 != 1, "Destination badly clipped\n");
KUNIT_EXPECT_TRUE_MSG(test, visible, "Destination should be visible\n");
KUNIT_EXPECT_TRUE_MSG(test, drm_rect_visible(&src), "Source should be visible\n");
/* 2:1 scaling */
drm_rect_init(&src, 0, 0, 2 << 16, 2 << 16);
drm_rect_init(&dst, 0, 0, 1, 1);
drm_rect_init(&clip, 0, 0, 1, 1);
visible = drm_rect_clip_scaled(&src, &dst, &clip);
KUNIT_EXPECT_FALSE_MSG(test, src.x1 != 0 || src.x2 != 2 << 16 ||
src.y1 != 0 || src.y2 != 2 << 16, "Source badly clipped\n");
KUNIT_EXPECT_FALSE_MSG(test, dst.x1 != 0 || dst.x2 != 1 ||
dst.y1 != 0 || dst.y2 != 1, "Destination badly clipped\n");
KUNIT_EXPECT_TRUE_MSG(test, visible, "Destination should be visible\n");
KUNIT_EXPECT_TRUE_MSG(test, drm_rect_visible(&src), "Source should be visible\n");
/* 1:2 scaling */
drm_rect_init(&src, 0, 0, 1 << 16, 1 << 16);
drm_rect_init(&dst, 0, 0, 2, 2);
drm_rect_init(&clip, 0, 0, 2, 2);
visible = drm_rect_clip_scaled(&src, &dst, &clip);
KUNIT_EXPECT_FALSE_MSG(test, src.x1 != 0 || src.x2 != 1 << 16 ||
src.y1 != 0 || src.y2 != 1 << 16, "Source badly clipped\n");
KUNIT_EXPECT_FALSE_MSG(test, dst.x1 != 0 || dst.x2 != 2 ||
dst.y1 != 0 || dst.y2 != 2, "Destination badly clipped\n");
KUNIT_EXPECT_TRUE_MSG(test, visible, "Destination should be visible\n");
KUNIT_EXPECT_TRUE_MSG(test, drm_rect_visible(&src), "Source should be visible\n");
}
static void drm_test_rect_clip_scaled_clipped(struct kunit *test)
{
struct drm_rect src, dst, clip;
bool visible;
/* 1:1 scaling top/left clip */
drm_rect_init(&src, 0, 0, 2 << 16, 2 << 16);
drm_rect_init(&dst, 0, 0, 2, 2);
drm_rect_init(&clip, 0, 0, 1, 1);
visible = drm_rect_clip_scaled(&src, &dst, &clip);
KUNIT_EXPECT_FALSE_MSG(test, src.x1 != 0 || src.x2 != 1 << 16 ||
src.y1 != 0 || src.y2 != 1 << 16, "Source badly clipped\n");
KUNIT_EXPECT_FALSE_MSG(test, dst.x1 != 0 || dst.x2 != 1 ||
dst.y1 != 0 || dst.y2 != 1, "Destination badly clipped\n");
KUNIT_EXPECT_TRUE_MSG(test, visible, "Destination should be visible\n");
KUNIT_EXPECT_TRUE_MSG(test, drm_rect_visible(&src), "Source should be visible\n");
/* 1:1 scaling bottom/right clip */
drm_rect_init(&src, 0, 0, 2 << 16, 2 << 16);
drm_rect_init(&dst, 0, 0, 2, 2);
drm_rect_init(&clip, 1, 1, 1, 1);
visible = drm_rect_clip_scaled(&src, &dst, &clip);
KUNIT_EXPECT_FALSE_MSG(test, src.x1 != 1 << 16 || src.x2 != 2 << 16 ||
src.y1 != 1 << 16 || src.y2 != 2 << 16, "Source badly clipped\n");
KUNIT_EXPECT_FALSE_MSG(test, dst.x1 != 1 || dst.x2 != 2 || dst.y1 != 1 ||
dst.y2 != 2, "Destination badly clipped\n");
KUNIT_EXPECT_TRUE_MSG(test, visible, "Destination should be visible\n");
KUNIT_EXPECT_TRUE_MSG(test, drm_rect_visible(&src), "Source should be visible\n");
/* 2:1 scaling top/left clip */
drm_rect_init(&src, 0, 0, 4 << 16, 4 << 16);
drm_rect_init(&dst, 0, 0, 2, 2);
drm_rect_init(&clip, 0, 0, 1, 1);
visible = drm_rect_clip_scaled(&src, &dst, &clip);
KUNIT_EXPECT_FALSE_MSG(test, src.x1 != 0 || src.x2 != 2 << 16 ||
src.y1 != 0 || src.y2 != 2 << 16, "Source badly clipped\n");
KUNIT_EXPECT_FALSE_MSG(test, dst.x1 != 0 || dst.x2 != 1 || dst.y1 != 0 ||
dst.y2 != 1, "Destination badly clipped\n");
KUNIT_EXPECT_TRUE_MSG(test, visible, "Destination should be visible\n");
KUNIT_EXPECT_TRUE_MSG(test, drm_rect_visible(&src), "Source should be visible\n");
/* 2:1 scaling bottom/right clip */
drm_rect_init(&src, 0, 0, 4 << 16, 4 << 16);
drm_rect_init(&dst, 0, 0, 2, 2);
drm_rect_init(&clip, 1, 1, 1, 1);
visible = drm_rect_clip_scaled(&src, &dst, &clip);
KUNIT_EXPECT_FALSE_MSG(test, src.x1 != 2 << 16 || src.x2 != 4 << 16 ||
src.y1 != 2 << 16 || src.y2 != 4 << 16, "Source badly clipped\n");
KUNIT_EXPECT_FALSE_MSG(test, dst.x1 != 1 || dst.x2 != 2 || dst.y1 != 1 ||
dst.y2 != 2, "Destination badly clipped\n");
KUNIT_EXPECT_TRUE_MSG(test, visible, "Destination should be visible\n");
KUNIT_EXPECT_TRUE_MSG(test, drm_rect_visible(&src), "Source should be visible\n");
/* 1:2 scaling top/left clip */
drm_rect_init(&src, 0, 0, 2 << 16, 2 << 16);
drm_rect_init(&dst, 0, 0, 4, 4);
drm_rect_init(&clip, 0, 0, 2, 2);
visible = drm_rect_clip_scaled(&src, &dst, &clip);
KUNIT_EXPECT_FALSE_MSG(test, src.x1 != 0 || src.x2 != 1 << 16 ||
src.y1 != 0 || src.y2 != 1 << 16, "Source badly clipped\n");
KUNIT_EXPECT_FALSE_MSG(test, dst.x1 != 0 || dst.x2 != 2 || dst.y1 != 0 ||
dst.y2 != 2, "Destination badly clipped\n");
KUNIT_EXPECT_TRUE_MSG(test, visible, "Destination should be visible\n");
KUNIT_EXPECT_TRUE_MSG(test, drm_rect_visible(&src), "Source should be visible\n");
/* 1:2 scaling bottom/right clip */
drm_rect_init(&src, 0, 0, 2 << 16, 2 << 16);
drm_rect_init(&dst, 0, 0, 4, 4);
drm_rect_init(&clip, 2, 2, 2, 2);
visible = drm_rect_clip_scaled(&src, &dst, &clip);
KUNIT_EXPECT_FALSE_MSG(test, src.x1 != 1 << 16 || src.x2 != 2 << 16 ||
src.y1 != 1 << 16 || src.y2 != 2 << 16, "Source badly clipped\n");
KUNIT_EXPECT_FALSE_MSG(test, dst.x1 != 2 || dst.x2 != 4 || dst.y1 != 2 ||
dst.y2 != 4, "Destination badly clipped\n");
KUNIT_EXPECT_TRUE_MSG(test, visible, "Destination should be visible\n");
KUNIT_EXPECT_TRUE_MSG(test, drm_rect_visible(&src), "Source should be visible\n");
}
static void drm_test_rect_clip_scaled_signed_vs_unsigned(struct kunit *test)
{
struct drm_rect src, dst, clip;
bool visible;
/*
* 'clip.x2 - dst.x1 >= dst width' could result a negative
* src rectangle width which is no longer expected by the
* code as it's using unsigned types. This could lead to
* the clipped source rectangle appering visible when it
* should have been fully clipped. Make sure both rectangles
* end up invisible.
*/
drm_rect_init(&src, 0, 0, INT_MAX, INT_MAX);
drm_rect_init(&dst, 0, 0, 2, 2);
drm_rect_init(&clip, 3, 3, 1, 1);
visible = drm_rect_clip_scaled(&src, &dst, &clip);
KUNIT_EXPECT_FALSE_MSG(test, visible, "Destination should not be visible\n");
KUNIT_EXPECT_FALSE_MSG(test, drm_rect_visible(&src), "Source should not be visible\n");
}
struct drm_rect_intersect_case {
const char *description;
struct drm_rect r1, r2;
bool should_be_visible;
struct drm_rect expected_intersection;
};
static const struct drm_rect_intersect_case drm_rect_intersect_cases[] = {
{
.description = "top-left x bottom-right",
.r1 = DRM_RECT_INIT(1, 1, 2, 2),
.r2 = DRM_RECT_INIT(0, 0, 2, 2),
.should_be_visible = true,
.expected_intersection = DRM_RECT_INIT(1, 1, 1, 1),
},
{
.description = "top-right x bottom-left",
.r1 = DRM_RECT_INIT(0, 0, 2, 2),
.r2 = DRM_RECT_INIT(1, -1, 2, 2),
.should_be_visible = true,
.expected_intersection = DRM_RECT_INIT(1, 0, 1, 1),
},
{
.description = "bottom-left x top-right",
.r1 = DRM_RECT_INIT(1, -1, 2, 2),
.r2 = DRM_RECT_INIT(0, 0, 2, 2),
.should_be_visible = true,
.expected_intersection = DRM_RECT_INIT(1, 0, 1, 1),
},
{
.description = "bottom-right x top-left",
.r1 = DRM_RECT_INIT(0, 0, 2, 2),
.r2 = DRM_RECT_INIT(1, 1, 2, 2),
.should_be_visible = true,
.expected_intersection = DRM_RECT_INIT(1, 1, 1, 1),
},
{
.description = "right x left",
.r1 = DRM_RECT_INIT(0, 0, 2, 1),
.r2 = DRM_RECT_INIT(1, 0, 3, 1),
.should_be_visible = true,
.expected_intersection = DRM_RECT_INIT(1, 0, 1, 1),
},
{
.description = "left x right",
.r1 = DRM_RECT_INIT(1, 0, 3, 1),
.r2 = DRM_RECT_INIT(0, 0, 2, 1),
.should_be_visible = true,
.expected_intersection = DRM_RECT_INIT(1, 0, 1, 1),
},
{
.description = "up x bottom",
.r1 = DRM_RECT_INIT(0, 0, 1, 2),
.r2 = DRM_RECT_INIT(0, -1, 1, 3),
.should_be_visible = true,
.expected_intersection = DRM_RECT_INIT(0, 0, 1, 2),
},
{
.description = "bottom x up",
.r1 = DRM_RECT_INIT(0, -1, 1, 3),
.r2 = DRM_RECT_INIT(0, 0, 1, 2),
.should_be_visible = true,
.expected_intersection = DRM_RECT_INIT(0, 0, 1, 2),
},
{
.description = "touching corner",
.r1 = DRM_RECT_INIT(0, 0, 1, 1),
.r2 = DRM_RECT_INIT(1, 1, 2, 2),
.should_be_visible = false,
.expected_intersection = DRM_RECT_INIT(1, 1, 0, 0),
},
{
.description = "touching side",
.r1 = DRM_RECT_INIT(0, 0, 1, 1),
.r2 = DRM_RECT_INIT(1, 0, 1, 1),
.should_be_visible = false,
.expected_intersection = DRM_RECT_INIT(1, 0, 0, 1),
},
{
.description = "equal rects",
.r1 = DRM_RECT_INIT(0, 0, 2, 2),
.r2 = DRM_RECT_INIT(0, 0, 2, 2),
.should_be_visible = true,
.expected_intersection = DRM_RECT_INIT(0, 0, 2, 2),
},
{
.description = "inside another",
.r1 = DRM_RECT_INIT(0, 0, 2, 2),
.r2 = DRM_RECT_INIT(1, 1, 1, 1),
.should_be_visible = true,
.expected_intersection = DRM_RECT_INIT(1, 1, 1, 1),
},
{
.description = "far away",
.r1 = DRM_RECT_INIT(0, 0, 1, 1),
.r2 = DRM_RECT_INIT(3, 6, 1, 1),
.should_be_visible = false,
.expected_intersection = DRM_RECT_INIT(3, 6, -2, -5),
},
{
.description = "points intersecting",
.r1 = DRM_RECT_INIT(5, 10, 0, 0),
.r2 = DRM_RECT_INIT(5, 10, 0, 0),
.should_be_visible = false,
.expected_intersection = DRM_RECT_INIT(5, 10, 0, 0),
},
{
.description = "points not intersecting",
.r1 = DRM_RECT_INIT(0, 0, 0, 0),
.r2 = DRM_RECT_INIT(5, 10, 0, 0),
.should_be_visible = false,
.expected_intersection = DRM_RECT_INIT(5, 10, -5, -10),
},
};
static void drm_rect_intersect_case_desc(const struct drm_rect_intersect_case *t, char *desc)
{
snprintf(desc, KUNIT_PARAM_DESC_SIZE,
"%s: " DRM_RECT_FMT " x " DRM_RECT_FMT,
t->description, DRM_RECT_ARG(&t->r1), DRM_RECT_ARG(&t->r2));
}
KUNIT_ARRAY_PARAM(drm_rect_intersect, drm_rect_intersect_cases, drm_rect_intersect_case_desc);
static void drm_test_rect_intersect(struct kunit *test)
{
const struct drm_rect_intersect_case *params = test->param_value;
struct drm_rect r1_aux = params->r1;
bool visible;
visible = drm_rect_intersect(&r1_aux, &params->r2);
KUNIT_EXPECT_EQ(test, visible, params->should_be_visible);
drm_rect_compare(test, &r1_aux, &params->expected_intersection);
}
struct drm_rect_scale_case {
const char *name;
struct drm_rect src, dst;
int min_range, max_range;
int expected_scaling_factor;
};
static const struct drm_rect_scale_case drm_rect_scale_cases[] = {
{
.name = "normal use",
.src = DRM_RECT_INIT(0, 0, 2 << 16, 2 << 16),
.dst = DRM_RECT_INIT(0, 0, 1 << 16, 1 << 16),
.min_range = 0, .max_range = INT_MAX,
.expected_scaling_factor = 2,
},
{
.name = "out of max range",
.src = DRM_RECT_INIT(0, 0, 10 << 16, 10 << 16),
.dst = DRM_RECT_INIT(0, 0, 1 << 16, 1 << 16),
.min_range = 3, .max_range = 5,
.expected_scaling_factor = -ERANGE,
},
{
.name = "out of min range",
.src = DRM_RECT_INIT(0, 0, 2 << 16, 2 << 16),
.dst = DRM_RECT_INIT(0, 0, 1 << 16, 1 << 16),
.min_range = 3, .max_range = 5,
.expected_scaling_factor = -ERANGE,
},
{
.name = "zero dst",
.src = DRM_RECT_INIT(0, 0, 2 << 16, 2 << 16),
.dst = DRM_RECT_INIT(0, 0, 0 << 16, 0 << 16),
.min_range = 0, .max_range = INT_MAX,
.expected_scaling_factor = 0,
},
{
.name = "negative src",
.src = DRM_RECT_INIT(0, 0, -(1 << 16), -(1 << 16)),
.dst = DRM_RECT_INIT(0, 0, 1 << 16, 1 << 16),
.min_range = 0, .max_range = INT_MAX,
.expected_scaling_factor = -EINVAL,
},
{
.name = "negative dst",
.src = DRM_RECT_INIT(0, 0, 1 << 16, 1 << 16),
.dst = DRM_RECT_INIT(0, 0, -(1 << 16), -(1 << 16)),
.min_range = 0, .max_range = INT_MAX,
.expected_scaling_factor = -EINVAL,
},
};
static void drm_rect_scale_case_desc(const struct drm_rect_scale_case *t, char *desc)
{
strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
}
KUNIT_ARRAY_PARAM(drm_rect_scale, drm_rect_scale_cases, drm_rect_scale_case_desc);
static void drm_test_rect_calc_hscale(struct kunit *test)
{
const struct drm_rect_scale_case *params = test->param_value;
int expected_warnings = params->expected_scaling_factor == -EINVAL;
int scaling_factor = INT_MIN;
/*
* Without CONFIG_BUG, WARN_ON() is a no-op and the suppressed warning
* count stays zero, failing the assertion.
*/
if (expected_warnings && !IS_ENABLED(CONFIG_BUG))
kunit_skip(test, "requires CONFIG_BUG");
/*
* drm_rect_calc_hscale() generates a warning backtrace whenever bad
* parameters are passed to it. This affects unit tests with -EINVAL
* error code in expected_scaling_factor.
*/
kunit_warning_suppress(test) {
scaling_factor = drm_rect_calc_hscale(&params->src, &params->dst,
params->min_range,
params->max_range);
KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, expected_warnings);
}
KUNIT_EXPECT_EQ(test, scaling_factor, params->expected_scaling_factor);
}
static void drm_test_rect_calc_vscale(struct kunit *test)
{
const struct drm_rect_scale_case *params = test->param_value;
int expected_warnings = params->expected_scaling_factor == -EINVAL;
int scaling_factor = INT_MIN;
/*
* Without CONFIG_BUG, WARN_ON() is a no-op and the suppressed warning
* count stays zero, failing the assertion.
*/
if (expected_warnings && !IS_ENABLED(CONFIG_BUG))
kunit_skip(test, "requires CONFIG_BUG");
/*
* drm_rect_calc_vscale() generates a warning backtrace whenever bad
* parameters are passed to it. This affects unit tests with -EINVAL
* error code in expected_scaling_factor.
*/
kunit_warning_suppress(test) {
scaling_factor = drm_rect_calc_vscale(&params->src, &params->dst,
params->min_range, params->max_range);
KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, expected_warnings);
}
KUNIT_EXPECT_EQ(test, scaling_factor, params->expected_scaling_factor);
}
struct drm_rect_rotate_case {
const char *name;
unsigned int rotation;
struct drm_rect rect;
int width, height;
struct drm_rect expected;
};
static const struct drm_rect_rotate_case drm_rect_rotate_cases[] = {
{
.name = "reflect-x",
.rotation = DRM_MODE_REFLECT_X,
.rect = DRM_RECT_INIT(0, 0, 5, 5),
.width = 5, .height = 10,
.expected = DRM_RECT_INIT(0, 0, 5, 5),
},
{
.name = "reflect-y",
.rotation = DRM_MODE_REFLECT_Y,
.rect = DRM_RECT_INIT(2, 0, 5, 5),
.width = 5, .height = 10,
.expected = DRM_RECT_INIT(2, 5, 5, 5),
},
{
.name = "rotate-0",
.rotation = DRM_MODE_ROTATE_0,
.rect = DRM_RECT_INIT(0, 2, 5, 5),
.width = 5, .height = 10,
.expected = DRM_RECT_INIT(0, 2, 5, 5),
},
{
.name = "rotate-90",
.rotation = DRM_MODE_ROTATE_90,
.rect = DRM_RECT_INIT(0, 0, 5, 10),
.width = 5, .height = 10,
.expected = DRM_RECT_INIT(0, 0, 10, 5),
},
{
.name = "rotate-180",
.rotation = DRM_MODE_ROTATE_180,
.rect = DRM_RECT_INIT(11, 3, 5, 10),
.width = 5, .height = 10,
.expected = DRM_RECT_INIT(-11, -3, 5, 10),
},
{
.name = "rotate-270",
.rotation = DRM_MODE_ROTATE_270,
.rect = DRM_RECT_INIT(6, 3, 5, 10),
.width = 5, .height = 10,
.expected = DRM_RECT_INIT(-3, 6, 10, 5),
},
};
static void drm_rect_rotate_case_desc(const struct drm_rect_rotate_case *t, char *desc)
{
strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
}
KUNIT_ARRAY_PARAM(drm_rect_rotate, drm_rect_rotate_cases, drm_rect_rotate_case_desc);
static void drm_test_rect_rotate(struct kunit *test)
{
const struct drm_rect_rotate_case *params = test->param_value;
struct drm_rect r = params->rect;
drm_rect_rotate(&r, params->width, params->height, params->rotation);
drm_rect_compare(test, &r, &params->expected);
}
static void drm_test_rect_rotate_inv(struct kunit *test)
{
const struct drm_rect_rotate_case *params = test->param_value;
struct drm_rect r = params->expected;
drm_rect_rotate_inv(&r, params->width, params->height, params->rotation);
drm_rect_compare(test, &r, &params->rect);
}
static struct kunit_case drm_rect_tests[] = {
KUNIT_CASE(drm_test_rect_clip_scaled_div_by_zero),
KUNIT_CASE(drm_test_rect_clip_scaled_not_clipped),
KUNIT_CASE(drm_test_rect_clip_scaled_clipped),
KUNIT_CASE(drm_test_rect_clip_scaled_signed_vs_unsigned),
KUNIT_CASE_PARAM(drm_test_rect_intersect, drm_rect_intersect_gen_params),
KUNIT_CASE_PARAM(drm_test_rect_calc_hscale, drm_rect_scale_gen_params),
KUNIT_CASE_PARAM(drm_test_rect_calc_vscale, drm_rect_scale_gen_params),
KUNIT_CASE_PARAM(drm_test_rect_rotate, drm_rect_rotate_gen_params),
KUNIT_CASE_PARAM(drm_test_rect_rotate_inv, drm_rect_rotate_gen_params),
{ }
};
static struct kunit_suite drm_rect_test_suite = {
.name = "drm_rect",
.test_cases = drm_rect_tests,
};
kunit_test_suite(drm_rect_test_suite);
MODULE_DESCRIPTION("Test cases for the drm_rect functions");
MODULE_LICENSE("GPL");
@@ -0,0 +1,168 @@
// SPDX-License-Identifier: GPL-2.0+
#include <kunit/test.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_kunit_helpers.h>
#include "../sysfb/drm_sysfb_helper.h"
#define TEST_BUF_SIZE 50
struct sysfb_build_fourcc_list_case {
const char *name;
u32 native_fourccs[TEST_BUF_SIZE];
size_t native_fourccs_size;
u32 expected[TEST_BUF_SIZE];
size_t expected_fourccs_size;
};
static struct sysfb_build_fourcc_list_case sysfb_build_fourcc_list_cases[] = {
{
.name = "no native formats",
.native_fourccs = { },
.native_fourccs_size = 0,
.expected = { DRM_FORMAT_XRGB8888 },
.expected_fourccs_size = 1,
},
{
.name = "XRGB8888 as native format",
.native_fourccs = { DRM_FORMAT_XRGB8888 },
.native_fourccs_size = 1,
.expected = { DRM_FORMAT_XRGB8888 },
.expected_fourccs_size = 1,
},
{
.name = "remove duplicates",
.native_fourccs = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_RGB888,
DRM_FORMAT_RGB888,
DRM_FORMAT_RGB888,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_RGB888,
DRM_FORMAT_RGB565,
DRM_FORMAT_RGB888,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_RGB565,
DRM_FORMAT_RGB565,
DRM_FORMAT_XRGB8888,
},
.native_fourccs_size = 11,
.expected = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_RGB888,
DRM_FORMAT_RGB565,
},
.expected_fourccs_size = 3,
},
{
.name = "convert alpha formats",
.native_fourccs = {
DRM_FORMAT_ARGB1555,
DRM_FORMAT_ABGR1555,
DRM_FORMAT_RGBA5551,
DRM_FORMAT_BGRA5551,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_RGBA8888,
DRM_FORMAT_BGRA8888,
DRM_FORMAT_ARGB2101010,
DRM_FORMAT_ABGR2101010,
DRM_FORMAT_RGBA1010102,
DRM_FORMAT_BGRA1010102,
},
.native_fourccs_size = 12,
.expected = {
DRM_FORMAT_XRGB1555,
DRM_FORMAT_XBGR1555,
DRM_FORMAT_RGBX5551,
DRM_FORMAT_BGRX5551,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_XBGR8888,
DRM_FORMAT_RGBX8888,
DRM_FORMAT_BGRX8888,
DRM_FORMAT_XRGB2101010,
DRM_FORMAT_XBGR2101010,
DRM_FORMAT_RGBX1010102,
DRM_FORMAT_BGRX1010102,
},
.expected_fourccs_size = 12,
},
{
.name = "random formats",
.native_fourccs = {
DRM_FORMAT_Y212,
DRM_FORMAT_ARGB1555,
DRM_FORMAT_ABGR16161616F,
DRM_FORMAT_C8,
DRM_FORMAT_BGR888,
DRM_FORMAT_XRGB1555,
DRM_FORMAT_RGBA5551,
DRM_FORMAT_BGR565_A8,
DRM_FORMAT_R10,
DRM_FORMAT_XYUV8888,
},
.native_fourccs_size = 10,
.expected = {
DRM_FORMAT_Y212,
DRM_FORMAT_XRGB1555,
DRM_FORMAT_ABGR16161616F,
DRM_FORMAT_C8,
DRM_FORMAT_BGR888,
DRM_FORMAT_RGBX5551,
DRM_FORMAT_BGR565_A8,
DRM_FORMAT_R10,
DRM_FORMAT_XYUV8888,
DRM_FORMAT_XRGB8888,
},
.expected_fourccs_size = 10,
},
};
static void sysfb_build_fourcc_list_case_desc(struct sysfb_build_fourcc_list_case *t, char *desc)
{
strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
}
KUNIT_ARRAY_PARAM(sysfb_build_fourcc_list, sysfb_build_fourcc_list_cases,
sysfb_build_fourcc_list_case_desc);
static void drm_test_sysfb_build_fourcc_list(struct kunit *test)
{
const struct sysfb_build_fourcc_list_case *params = test->param_value;
u32 fourccs_out[TEST_BUF_SIZE] = {0};
size_t nfourccs_out;
struct drm_device *drm;
struct device *dev;
dev = drm_kunit_helper_alloc_device(test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*drm), 0, DRIVER_MODESET);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
nfourccs_out = drm_sysfb_build_fourcc_list(drm, params->native_fourccs,
params->native_fourccs_size,
fourccs_out, TEST_BUF_SIZE);
KUNIT_EXPECT_EQ(test, nfourccs_out, params->expected_fourccs_size);
KUNIT_EXPECT_MEMEQ(test, fourccs_out, params->expected, TEST_BUF_SIZE);
}
static struct kunit_case drm_sysfb_modeset_test_cases[] = {
KUNIT_CASE_PARAM(drm_test_sysfb_build_fourcc_list, sysfb_build_fourcc_list_gen_params),
{}
};
static struct kunit_suite drm_sysfb_modeset_test_suite = {
.name = "drm_sysfb_modeset_test",
.test_cases = drm_sysfb_modeset_test_cases,
};
kunit_test_suite(drm_sysfb_modeset_test_suite);
MODULE_DESCRIPTION("KUnit tests for the drm_sysfb_modeset APIs");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("José Expósito <jose.exposito89@gmail.com>");