@@ -189,6 +189,32 @@ static const struct drm_connector_hdmi_funcs reject_100mhz_connector_hdmi_funcs
},
};
+static enum drm_mode_status
+accept_any_connector_tmds_char_rate_valid(const struct drm_connector *connector,
+ const struct drm_display_mode *mode,
+ unsigned long long tmds_rate)
+{
+ return MODE_OK;
+}
+
+static const struct drm_connector_hdmi_funcs reject_over_165mhz_connector_hdmi_funcs = {
+ .vendor = "Vendor",
+ .product = "Product",
+ .supported_hdmi_ver = HDMI_VERSION_1_3,
+ .supported_tmds_char_rate = HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ,
+ .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
+ .max_bpc = 8,
+ .tmds_char_rate_valid = accept_any_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)
{
struct drm_atomic_helper_connector_hdmi_priv *priv =
@@ -2751,6 +2777,93 @@ static void drm_test_check_mode_valid_reject_max_clock(struct kunit *test)
KUNIT_EXPECT_EQ(test, preferred->clock, 25200);
}
+/*
+ * Test that the max_tmds_char_rate inferred from caps.supported_hdmi_ver
+ * is applied as a mode-filtering limit: with an HDMI 1.3 connector and no
+ * driver hook, drm_hdmi_connector_mode_valid() will reject modes whose TMDS
+ * character rate exceeds 340 MHz, even though the sink itself advertises
+ * a higher limit (600 MHz).
+ */
+static void
+drm_test_check_mode_valid_reject_inferred_max_tmds_char_rate(struct kunit *test)
+{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_display_mode *preferred;
+
+ hdmi_funcs.supported_hdmi_ver = HDMI_VERSION_1_3;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, &hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ KUNIT_ASSERT_EQ(test, priv->connector.hdmi.max_tmds_char_rate,
+ HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+
+ preferred = find_preferred_mode(&priv->connector);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+ KUNIT_EXPECT_EQ(test, preferred->hdisplay, 2560);
+ KUNIT_EXPECT_EQ(test, preferred->vdisplay, 1600);
+ KUNIT_EXPECT_EQ(test, preferred->clock, 268500);
+}
+
+/*
+ * Test that a connector-provided max_tmds_char_rate limit (165 MHz) takes
+ * precedence over a permissive .tmds_char_rate_valid() driver hook and
+ * drm_hdmi_connector_mode_valid() will reject modes whose computed TMDS
+ * character exceeds the connector limit.
+ */
+static void
+drm_test_check_mode_valid_connector_max_tmds_char_rate_precedes_driver_hook(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_display_mode *preferred;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ &reject_over_165mhz_connector_hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ KUNIT_ASSERT_EQ(test, priv->connector.hdmi.max_tmds_char_rate,
+ HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ);
+
+ preferred = find_preferred_mode(&priv->connector);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+ KUNIT_EXPECT_EQ(test, preferred->hdisplay, 2048);
+ KUNIT_EXPECT_EQ(test, preferred->vdisplay, 1152);
+ KUNIT_EXPECT_EQ(test, preferred->clock, 162000);
+}
+
+/*
+ * Test that a connector-provided max_tmds_char_rate (165 MHz) can be further
+ * limited by a more restrictive (100 MHz) .tmds_char_rate_valid() driver hook
+ * and drm_hdmi_connector_mode_valid() will reject modes whose computed TMDS
+ * character exceeds the hook limit.
+ */
+static void
+drm_test_check_mode_valid_driver_hook_limits_connector_max_tmds_char_rate(struct kunit *test)
+{
+ struct drm_connector_hdmi_funcs hdmi_funcs = reject_100mhz_connector_hdmi_funcs;
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_display_mode *preferred;
+
+ hdmi_funcs.supported_hdmi_ver = HDMI_VERSION_1_3;
+ hdmi_funcs.supported_tmds_char_rate = HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, &hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ KUNIT_ASSERT_EQ(test, priv->connector.hdmi.max_tmds_char_rate,
+ HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ);
+
+ preferred = find_preferred_mode(&priv->connector);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+ KUNIT_EXPECT_EQ(test, preferred->hdisplay, 1440);
+ KUNIT_EXPECT_EQ(test, preferred->vdisplay, 900);
+ KUNIT_EXPECT_EQ(test, preferred->clock, 88750);
+}
+
/*
* Test that drm_hdmi_connector_mode_valid() will accept modes that require a
* 4:2:0 chroma subsampling, even if said mode would violate maximum clock
@@ -2841,6 +2954,9 @@ static struct kunit_case drm_atomic_helper_connector_hdmi_mode_valid_tests[] = {
KUNIT_CASE(drm_test_check_mode_valid_reject),
KUNIT_CASE(drm_test_check_mode_valid_reject_rate),
KUNIT_CASE(drm_test_check_mode_valid_reject_max_clock),
+ KUNIT_CASE(drm_test_check_mode_valid_reject_inferred_max_tmds_char_rate),
+ KUNIT_CASE(drm_test_check_mode_valid_connector_max_tmds_char_rate_precedes_driver_hook),
+ KUNIT_CASE(drm_test_check_mode_valid_driver_hook_limits_connector_max_tmds_char_rate),
KUNIT_CASE(drm_test_check_mode_valid_yuv420_only_max_clock),
KUNIT_CASE(drm_test_check_mode_valid_reject_yuv420_only_connector),
KUNIT_CASE(drm_test_check_mode_valid_accept_yuv420_also_connector_rgb),