@@ -3205,6 +3205,167 @@ static void drm_test_check_infoframes(struct kunit *test)
drm_modeset_acquire_fini(&ctx);
}
+struct drm_hdmi_spd_infoframe_test {
+ const char *name;
+ const char *vendor;
+ const char *product;
+ unsigned char expected_vendor[HDMI_SPD_INFOFRAME_VENDOR_LEN];
+ unsigned char expected_product[HDMI_SPD_INFOFRAME_PRODUCT_LEN];
+};
+
+static const struct drm_hdmi_spd_infoframe_test drm_hdmi_spd_infoframe_tests[] = {
+ {
+ .name = "shorter-than-field",
+ .vendor = "Vend",
+ .product = "Prod",
+ .expected_vendor = { 'V', 'e', 'n', 'd' },
+ .expected_product = { 'P', 'r', 'o', 'd' },
+ },
+ {
+ .name = "whole-field",
+ .vendor = "VendorVe",
+ .product = "ProductProductPr",
+ .expected_vendor = {
+ 'V', 'e', 'n', 'd', 'o', 'r', 'V', 'e',
+ },
+ .expected_product = {
+ 'P', 'r', 'o', 'd', 'u', 'c', 't',
+ 'P', 'r', 'o', 'd', 'u', 'c', 't',
+ 'P', 'r',
+ },
+ },
+};
+
+static void drm_hdmi_spd_infoframe_desc(const struct drm_hdmi_spd_infoframe_test *t,
+ char *desc)
+{
+ strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
+}
+
+KUNIT_ARRAY_PARAM(drm_hdmi_spd_infoframe, drm_hdmi_spd_infoframe_tests,
+ drm_hdmi_spd_infoframe_desc);
+
+/*
+ * The SPD InfoFrame is only generated for connectors implementing the
+ * related hooks, hence the SPD tests cannot rely on the plain dummy funcs.
+ */
+static const struct drm_connector_hdmi_funcs dummy_connector_hdmi_spd_funcs = {
+ .vendor = "Vendor",
+ .product = "Product",
+ .supported_hdmi_ver = HDMI_VERSION_1_4,
+ .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
+ .max_bpc = 8,
+ .avi = {
+ .clear_infoframe = accept_infoframe_clear_infoframe,
+ .write_infoframe = accept_infoframe_write_infoframe,
+ },
+ .spd = {
+ .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,
+ },
+};
+
+/*
+ * Test that the vendor and product names end up in the SPD InfoFrame padded
+ * with zeros, and without any trailing NUL when spanning the whole field.
+ */
+static void drm_test_check_spd_infoframe(struct kunit *test)
+{
+ const struct drm_hdmi_spd_infoframe_test *param = test->param_value;
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_spd_funcs;
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_connector_state *conn_state;
+ struct drm_crtc_state *crtc_state;
+ struct drm_atomic_commit *state;
+ struct drm_display_mode *preferred;
+ struct hdmi_spd_infoframe *frame;
+ struct drm_connector *conn;
+ struct drm_device *drm;
+ struct drm_crtc *crtc;
+ int ret;
+
+ hdmi_funcs.vendor = param->vendor;
+ hdmi_funcs.product = param->product;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ &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_commit_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;
+
+ ret = drm_atomic_check_only(state);
+ if (ret == -EDEADLK) {
+ drm_atomic_commit_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_commit_clear(state);
+ ret = drm_modeset_backoff(&ctx);
+ if (!ret)
+ goto retry_crtc_state;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ conn_state = conn->state;
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+ KUNIT_ASSERT_TRUE(test, conn_state->hdmi.infoframes.spd.set);
+
+ frame = &conn_state->hdmi.infoframes.spd.data.spd;
+
+ KUNIT_EXPECT_MEMEQ(test, frame->vendor, param->expected_vendor,
+ sizeof(frame->vendor));
+ KUNIT_EXPECT_MEMEQ(test, frame->product, param->expected_product,
+ sizeof(frame->product));
+
+ 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)
{
@@ -3723,9 +3884,10 @@ static void drm_test_check_reject_audio_infoframe(struct kunit *test)
drm_modeset_acquire_fini(&ctx);
}
-
static struct kunit_case drm_atomic_helper_connector_hdmi_infoframes_tests[] = {
KUNIT_CASE(drm_test_check_infoframes),
+ KUNIT_CASE_PARAM(drm_test_check_spd_infoframe,
+ drm_hdmi_spd_infoframe_gen_params),
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),