diff options
| author | Dave Airlie <airlied@redhat.com> | 2026-01-23 12:44:59 +1000 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2026-01-23 12:45:15 +1000 |
| commit | b2c6627e406d00bc53903ff6204aabb0c6a22b1a (patch) | |
| tree | 2169a920f444166e707d96689b800f6581edad72 /drivers/gpu/drm/tests | |
| parent | d2f618b8fe76b565f6bc0071b5eeee07a9012c6d (diff) | |
| parent | 68b271a3a94cfd6c7695a96b6398b52feb89e2c2 (diff) | |
Merge tag 'drm-misc-next-2026-01-22' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for 6.20:
Core Changes:
- buddy: Fix free_trees memory leak, prevent a BUG_ON
- dma-buf: Start to introduce cgroup memory accounting in heaps, Remove
sysfs stats, add new tracepoints
- hdmi: Limit infoframes exposure to userspace based on driver
capabilities
- property: Account for property blobs in memcg
Driver Changes:
- atmel-hlcdc: Switch to drmm resources, Support nomodeset parameter,
various patches to use newish helpers and fix memory safety bugs
- hisilicon: Fix various DisplayPort related bugs
- imagination: Introduce hardware version checks
- renesas: Fix kernel panic on reboot
- rockchip: Fix RK3576 HPD interrupt handling, Improve RK3588 HPD
interrupt handling
- v3d: Convert to drm logging helpers
- bridge:
- Continuation of the refcounting effort
- new bridge: Algoltek AG6311
- panel:
- new panel: Anbernic RG-DS
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maxime Ripard <mripard@redhat.com>
Link: https://patch.msgid.link/20260122-antique-sexy-junglefowl-1bc5a8@houat
Diffstat (limited to 'drivers/gpu/drm/tests')
| -rw-r--r-- | drivers/gpu/drm/tests/drm_buddy_test.c | 35 | ||||
| -rw-r--r-- | drivers/gpu/drm/tests/drm_client_modeset_test.c | 3 | ||||
| -rw-r--r-- | drivers/gpu/drm/tests/drm_connector_test.c | 19 | ||||
| -rw-r--r-- | drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c | 667 | ||||
| -rw-r--r-- | drivers/gpu/drm/tests/drm_kunit_edid.h | 119 |
5 files changed, 843 insertions, 0 deletions
diff --git a/drivers/gpu/drm/tests/drm_buddy_test.c b/drivers/gpu/drm/tests/drm_buddy_test.c index 5f40b5343bd8..e6f8459c6c54 100644 --- a/drivers/gpu/drm/tests/drm_buddy_test.c +++ b/drivers/gpu/drm/tests/drm_buddy_test.c @@ -857,6 +857,40 @@ static void drm_test_buddy_alloc_limit(struct kunit *test) drm_buddy_fini(&mm); } +static void drm_test_buddy_alloc_exceeds_max_order(struct kunit *test) +{ + u64 mm_size = SZ_8G + SZ_2G, size = SZ_8G + SZ_1G, min_block_size = SZ_8G; + struct drm_buddy mm; + LIST_HEAD(blocks); + int err; + + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, SZ_4K), + "buddy_init failed\n"); + + /* CONTIGUOUS allocation should succeed via try_harder fallback */ + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, size, + SZ_4K, &blocks, + DRM_BUDDY_CONTIGUOUS_ALLOCATION), + "buddy_alloc hit an error size=%llu\n", size); + drm_buddy_free_list(&mm, &blocks, 0); + + /* Non-CONTIGUOUS with large min_block_size should return -EINVAL */ + err = drm_buddy_alloc_blocks(&mm, 0, mm_size, size, min_block_size, &blocks, 0); + KUNIT_EXPECT_EQ(test, err, -EINVAL); + + /* Non-CONTIGUOUS + RANGE with large min_block_size should return -EINVAL */ + err = drm_buddy_alloc_blocks(&mm, 0, mm_size, size, min_block_size, &blocks, + DRM_BUDDY_RANGE_ALLOCATION); + KUNIT_EXPECT_EQ(test, err, -EINVAL); + + /* CONTIGUOUS + RANGE should return -EINVAL (no try_harder for RANGE) */ + err = drm_buddy_alloc_blocks(&mm, 0, mm_size, size, SZ_4K, &blocks, + DRM_BUDDY_CONTIGUOUS_ALLOCATION | DRM_BUDDY_RANGE_ALLOCATION); + KUNIT_EXPECT_EQ(test, err, -EINVAL); + + drm_buddy_fini(&mm); +} + static int drm_buddy_suite_init(struct kunit_suite *suite) { while (!random_seed) @@ -877,6 +911,7 @@ static struct kunit_case drm_buddy_tests[] = { KUNIT_CASE(drm_test_buddy_alloc_clear), KUNIT_CASE(drm_test_buddy_alloc_range_bias), KUNIT_CASE(drm_test_buddy_fragmentation_performance), + KUNIT_CASE(drm_test_buddy_alloc_exceeds_max_order), {} }; diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c b/drivers/gpu/drm/tests/drm_client_modeset_test.c index 3f44fe5e92e4..ec58fe064d86 100644 --- a/drivers/gpu/drm/tests/drm_client_modeset_test.c +++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c @@ -5,6 +5,7 @@ #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> @@ -48,6 +49,8 @@ static const struct drm_connector_helper_funcs drm_client_modeset_connector_help }; 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) diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c index 22e2d959eb31..86860ad0861c 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -25,7 +25,26 @@ struct drm_connector_init_priv { struct i2c_adapter ddc; }; +static int accept_infoframe_clear_infoframe(struct drm_connector *connector) +{ + return 0; +} + +static int accept_infoframe_write_infoframe(struct drm_connector *connector, + const u8 *buffer, size_t len) +{ + return 0; +} + static const struct drm_connector_hdmi_funcs dummy_hdmi_funcs = { + .avi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, + .hdmi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, }; static const struct drm_connector_funcs dummy_funcs = { diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c index 70f9aa702143..4bdcea3c7435 100644 --- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c @@ -35,11 +35,16 @@ struct drm_atomic_helper_connector_hdmi_priv { const void *current_edid; size_t current_edid_len; + + int hdmi_update_failures; }; #define connector_to_priv(c) \ container_of_const(c, struct drm_atomic_helper_connector_hdmi_priv, connector) +#define encoder_to_priv(e) \ + container_of_const(e, struct drm_atomic_helper_connector_hdmi_priv, encoder) + static struct drm_display_mode *find_preferred_mode(struct drm_connector *connector) { struct drm_device *drm = connector->dev; @@ -73,7 +78,26 @@ static int set_connector_edid(struct kunit *test, struct drm_connector *connecto return ret; } +static int accept_infoframe_clear_infoframe(struct drm_connector *connector) +{ + return 0; +} + +static int accept_infoframe_write_infoframe(struct drm_connector *connector, + const u8 *buffer, size_t len) +{ + return 0; +} + static const struct drm_connector_hdmi_funcs dummy_connector_hdmi_funcs = { + .avi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, + .hdmi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, }; static enum drm_mode_status @@ -86,6 +110,14 @@ reject_connector_tmds_char_rate_valid(const struct drm_connector *connector, static const struct drm_connector_hdmi_funcs reject_connector_hdmi_funcs = { .tmds_char_rate_valid = reject_connector_tmds_char_rate_valid, + .avi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, + .hdmi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, }; static enum drm_mode_status @@ -98,6 +130,14 @@ reject_100mhz_connector_tmds_char_rate_valid(const struct drm_connector *connect static const struct drm_connector_hdmi_funcs reject_100mhz_connector_hdmi_funcs = { .tmds_char_rate_valid = reject_100mhz_connector_tmds_char_rate_valid, + .avi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, + .hdmi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, }; static int dummy_connector_get_modes(struct drm_connector *connector) @@ -138,6 +178,22 @@ static const struct drm_connector_funcs dummy_connector_funcs = { .reset = dummy_hdmi_connector_reset, }; +static void test_encoder_atomic_enable(struct drm_encoder *encoder, + struct drm_atomic_state *state) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv = + encoder_to_priv(encoder); + int ret; + + ret = drm_atomic_helper_connector_hdmi_update_infoframes(&priv->connector, state); + if (ret) + priv->hdmi_update_failures++; +} + +static const struct drm_encoder_helper_funcs test_encoder_helper_funcs = { + .atomic_enable = test_encoder_atomic_enable, +}; + static struct drm_atomic_helper_connector_hdmi_priv * __connector_hdmi_init(struct kunit *test, @@ -2466,10 +2522,621 @@ static struct kunit_suite drm_atomic_helper_connector_hdmi_mode_valid_test_suite .test_cases = drm_atomic_helper_connector_hdmi_mode_valid_tests, }; +/* + * Test that the default behaviour works without errors. We expect that + * infoframe-related hooks are called and there are no errors raised. + */ +static void drm_test_check_infoframes(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx ctx; + struct drm_crtc_state *crtc_state; + struct drm_atomic_state *state; + struct drm_display_mode *preferred; + struct drm_connector *conn; + struct drm_device *drm; + struct drm_crtc *crtc; + int old_hdmi_update_failures; + int ret; + + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 8, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_max_200mhz); + KUNIT_ASSERT_NOT_NULL(test, priv); + + drm = &priv->drm; + crtc = priv->crtc; + conn = &priv->connector; + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + + drm_modeset_acquire_init(&ctx, 0); + +retry_conn_enable: + ret = drm_kunit_helper_enable_crtc_connector(test, drm, + crtc, conn, + preferred, + &ctx); + if (ret == -EDEADLK) { + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_enable; + } + 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: + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (PTR_ERR(crtc_state) == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_crtc_state; + } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + crtc_state->mode_changed = true; + + old_hdmi_update_failures = priv->hdmi_update_failures; + + ret = drm_atomic_check_only(state); + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_crtc_state; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = drm_atomic_commit(state); + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_crtc_state; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + KUNIT_EXPECT_GE(test, old_hdmi_update_failures, priv->hdmi_update_failures); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + +static int reject_infoframe_write_infoframe(struct drm_connector *connector, + const u8 *buffer, size_t len) +{ + return -EOPNOTSUPP; +} + +static const struct drm_connector_hdmi_funcs reject_avi_infoframe_hdmi_funcs = { + .avi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = reject_infoframe_write_infoframe, + }, + .hdmi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, +}; + +/* + * Test that the rejection of AVI InfoFrame results in the failure of + * drm_atomic_helper_connector_hdmi_update_infoframes(). + */ +static void drm_test_check_reject_avi_infoframe(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx ctx; + struct drm_atomic_state *state; + struct drm_crtc_state *crtc_state; + struct drm_display_mode *preferred; + struct drm_connector *conn; + struct drm_device *drm; + struct drm_crtc *crtc; + int old_hdmi_update_failures; + int ret; + + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 8, + &reject_avi_infoframe_hdmi_funcs, + test_edid_hdmi_1080p_rgb_max_200mhz); + KUNIT_ASSERT_NOT_NULL(test, priv); + + drm = &priv->drm; + crtc = priv->crtc; + conn = &priv->connector; + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + + drm_modeset_acquire_init(&ctx, 0); + +retry_conn_enable: + ret = drm_kunit_helper_enable_crtc_connector(test, drm, + crtc, conn, + preferred, + &ctx); + if (ret == -EDEADLK) { + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_enable; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + drm_encoder_helper_add(&priv->encoder, &test_encoder_helper_funcs); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + +retry_crtc_state: + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (PTR_ERR(crtc_state) == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_crtc_state; + } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + crtc_state->mode_changed = true; + + old_hdmi_update_failures = priv->hdmi_update_failures; + + ret = drm_atomic_check_only(state); + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_crtc_state; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = drm_atomic_commit(state); + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_crtc_state; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + KUNIT_EXPECT_NE(test, old_hdmi_update_failures, priv->hdmi_update_failures); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + +static const struct drm_connector_hdmi_funcs reject_hdr_infoframe_hdmi_funcs = { + .avi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, + .hdmi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, + .hdr_drm = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = reject_infoframe_write_infoframe, + }, +}; + +/* + * Test that the HDR InfoFrame isn't programmed in + * drm_atomic_helper_connector_hdmi_update_infoframes() if the max_bpc is 8. + */ +static void drm_test_check_reject_hdr_infoframe_bpc_8(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx ctx; + struct drm_atomic_state *state; + struct drm_connector_state *new_conn_state; + struct drm_crtc_state *crtc_state; + struct drm_display_mode *preferred; + struct drm_connector *conn; + struct drm_device *drm; + struct drm_crtc *crtc; + int old_hdmi_update_failures; + int ret; + + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 8, + &reject_hdr_infoframe_hdmi_funcs, + test_edid_hdmi_1080p_rgb_max_200mhz_hdr); + KUNIT_ASSERT_NOT_NULL(test, priv); + + drm = &priv->drm; + crtc = priv->crtc; + conn = &priv->connector; + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + + drm_modeset_acquire_init(&ctx, 0); + +retry_conn_enable: + ret = drm_kunit_helper_enable_crtc_connector(test, drm, + crtc, conn, + preferred, + &ctx); + if (ret == -EDEADLK) { + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_enable; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + drm_encoder_helper_add(&priv->encoder, &test_encoder_helper_funcs); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + +retry_conn_state: + new_conn_state = drm_atomic_get_connector_state(state, conn); + if (PTR_ERR(new_conn_state) == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_state; + } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state); + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (PTR_ERR(crtc_state) == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_state; + } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + /* Verify that there is no HDR property, so "userspace" can't set it. */ + for (int i = 0; i < conn->base.properties->count; i++) + KUNIT_ASSERT_PTR_NE(test, + drm->mode_config.hdr_output_metadata_property, + conn->base.properties->properties[i]); + + crtc_state->mode_changed = true; + + old_hdmi_update_failures = priv->hdmi_update_failures; + + ret = drm_atomic_check_only(state); + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_state; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = drm_atomic_commit(state); + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_state; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + KUNIT_EXPECT_EQ(test, old_hdmi_update_failures, priv->hdmi_update_failures); + + new_conn_state = conn->state; + KUNIT_ASSERT_NOT_NULL(test, new_conn_state); + + KUNIT_ASSERT_EQ(test, new_conn_state->hdmi.output_bpc, 8); + KUNIT_ASSERT_EQ(test, new_conn_state->hdmi.infoframes.hdr_drm.set, false); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + +/* + * Test that the rejection of HDR InfoFrame results in the failure of + * drm_atomic_helper_connector_hdmi_update_infoframes() in the high bpc is + * supported. + */ +static void drm_test_check_reject_hdr_infoframe_bpc_10(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx ctx; + struct drm_atomic_state *state; + struct drm_connector_state *new_conn_state; + struct drm_crtc_state *crtc_state; + struct drm_display_mode *preferred; + struct drm_connector *conn; + struct drm_device *drm; + struct drm_crtc *crtc; + int old_hdmi_update_failures; + struct hdr_output_metadata hdr_data; + struct drm_property_blob *hdr_blob; + bool replaced; + int ret; + + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 10, + &reject_hdr_infoframe_hdmi_funcs, + test_edid_hdmi_1080p_rgb_max_200mhz_hdr); + KUNIT_ASSERT_NOT_NULL(test, priv); + + drm = &priv->drm; + crtc = priv->crtc; + conn = &priv->connector; + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + + drm_modeset_acquire_init(&ctx, 0); + +retry_conn_enable: + ret = drm_kunit_helper_enable_crtc_connector(test, drm, + crtc, conn, + preferred, + &ctx); + if (ret == -EDEADLK) { + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_enable; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + drm_encoder_helper_add(&priv->encoder, &test_encoder_helper_funcs); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + +retry_conn_state: + new_conn_state = drm_atomic_get_connector_state(state, conn); + if (PTR_ERR(new_conn_state) == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_state; + } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state); + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (PTR_ERR(crtc_state) == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_state; + } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + hdr_data.metadata_type = HDMI_STATIC_METADATA_TYPE1; + hdr_data.hdmi_metadata_type1.eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR; + hdr_data.hdmi_metadata_type1.metadata_type = HDMI_STATIC_METADATA_TYPE1; + + hdr_blob = drm_property_create_blob(drm, sizeof(hdr_data), &hdr_data); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hdr_blob); + + ret = drm_property_replace_blob_from_id(drm, + &new_conn_state->hdr_output_metadata, + hdr_blob->base.id, + -1, sizeof(struct hdr_output_metadata), -1, + &replaced); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_ASSERT_EQ(test, replaced, true); + + crtc_state->mode_changed = true; + + old_hdmi_update_failures = priv->hdmi_update_failures; + + ret = drm_atomic_check_only(state); + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_state; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = drm_atomic_commit(state); + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_state; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + KUNIT_EXPECT_LE(test, old_hdmi_update_failures, priv->hdmi_update_failures); + + new_conn_state = conn->state; + KUNIT_ASSERT_NOT_NULL(test, new_conn_state); + + KUNIT_ASSERT_EQ(test, new_conn_state->hdmi.output_bpc, 10); + KUNIT_ASSERT_EQ(test, new_conn_state->hdmi.infoframes.hdr_drm.set, true); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + +static const struct drm_connector_hdmi_funcs reject_audio_infoframe_hdmi_funcs = { + .avi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, + .hdmi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, + .audio = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = reject_infoframe_write_infoframe, + }, +}; + +/* + * Test that Audio InfoFrame is only programmed if we call a corresponding API, + * thus the drivers can safely assume that they won't get Audio InfoFrames if + * they don't call it. + */ +static void drm_test_check_reject_audio_infoframe(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx ctx; + struct drm_atomic_state *state; + struct drm_crtc_state *crtc_state; + struct drm_display_mode *preferred; + struct drm_connector *conn; + struct drm_device *drm; + struct drm_crtc *crtc; + int old_hdmi_update_failures; + struct hdmi_audio_infoframe cea; + int ret; + + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 8, + &reject_audio_infoframe_hdmi_funcs, + test_edid_hdmi_1080p_rgb_max_200mhz); + KUNIT_ASSERT_NOT_NULL(test, priv); + + drm = &priv->drm; + crtc = priv->crtc; + conn = &priv->connector; + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + + drm_modeset_acquire_init(&ctx, 0); + +retry_conn_enable: + ret = drm_kunit_helper_enable_crtc_connector(test, drm, + crtc, conn, + preferred, + &ctx); + if (ret == -EDEADLK) { + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_enable; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + drm_encoder_helper_add(&priv->encoder, &test_encoder_helper_funcs); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + +retry_crtc_state: + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (PTR_ERR(crtc_state) == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_crtc_state; + } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + crtc_state->mode_changed = true; + + old_hdmi_update_failures = priv->hdmi_update_failures; + + ret = drm_atomic_check_only(state); + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_crtc_state; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = drm_atomic_commit(state); + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_crtc_state; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + KUNIT_EXPECT_EQ(test, old_hdmi_update_failures, priv->hdmi_update_failures); + + /* + * So, it works without Audio InfoFrame, let's fail with it in place, + * checking that writing the infofraem actually gets triggered. + */ + + hdmi_audio_infoframe_init(&cea); + cea.channels = 2; + cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; + cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; + cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; + + ret = drm_atomic_helper_connector_hdmi_update_audio_infoframe(conn, &cea); + KUNIT_ASSERT_EQ(test, ret, -EOPNOTSUPP); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + +retry_crtc_state_2: + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (PTR_ERR(crtc_state) == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_crtc_state_2; + } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + crtc_state->mode_changed = true; + + old_hdmi_update_failures = priv->hdmi_update_failures; + + ret = drm_atomic_check_only(state); + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_crtc_state_2; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = drm_atomic_commit(state); + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_crtc_state_2; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + KUNIT_EXPECT_LE(test, old_hdmi_update_failures, priv->hdmi_update_failures); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + + +static struct kunit_case drm_atomic_helper_connector_hdmi_infoframes_tests[] = { + KUNIT_CASE(drm_test_check_infoframes), + KUNIT_CASE(drm_test_check_reject_avi_infoframe), + KUNIT_CASE(drm_test_check_reject_hdr_infoframe_bpc_8), + KUNIT_CASE(drm_test_check_reject_hdr_infoframe_bpc_10), + KUNIT_CASE(drm_test_check_reject_audio_infoframe), + { } +}; + +static struct kunit_suite drm_atomic_helper_connector_hdmi_infoframes_test_suite = { + .name = "drm_atomic_helper_connector_hdmi_infoframes", + .test_cases = drm_atomic_helper_connector_hdmi_infoframes_tests, +}; + kunit_test_suites( &drm_atomic_helper_connector_hdmi_check_test_suite, &drm_atomic_helper_connector_hdmi_reset_test_suite, &drm_atomic_helper_connector_hdmi_mode_valid_test_suite, + &drm_atomic_helper_connector_hdmi_infoframes_test_suite, ); MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>"); diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.h b/drivers/gpu/drm/tests/drm_kunit_edid.h index c59c8528a3f7..f4923157f5bf 100644 --- a/drivers/gpu/drm/tests/drm_kunit_edid.h +++ b/drivers/gpu/drm/tests/drm_kunit_edid.h @@ -305,6 +305,125 @@ static const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz[] = { * 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 |
