drm/connector: hdmi: Add custom hook to filter TMDS character rate
authorMaxime Ripard <mripard@kernel.org>
Mon, 27 May 2024 13:58:02 +0000 (15:58 +0200)
committerMaxime Ripard <mripard@kernel.org>
Tue, 28 May 2024 08:12:59 +0000 (10:12 +0200)
Most of the HDMI controllers have an upper TMDS character rate limit
they can't exceed. On "embedded"-grade display controllers, it will
typically be lower than what high-grade monitors can provide these days,
so drivers will filter the TMDS character rate based on the controller
capabilities.

To make that easier to handle for drivers, let's provide an optional
hook to be implemented by drivers so they can tell the HDMI controller
helpers if a given TMDS character rate is reachable for them or not.

This will then be useful to figure out the best format and bpc count for
a given mode.

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20240527-kms-hdmi-connector-state-v15-13-c5af16c3aae2@kernel.org
Signed-off-by: Maxime Ripard <mripard@kernel.org>
drivers/gpu/drm/display/drm_hdmi_state_helper.c
drivers/gpu/drm/drm_connector.c
drivers/gpu/drm/tests/drm_connector_test.c
drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
include/drm/drm_connector.h

index 08630561d8645f33a451604aee7a942ffc7249a9..063421835dba5e59d9767ca3ead5594f5f3b0cf8 100644 (file)
@@ -53,11 +53,20 @@ hdmi_clock_valid(const struct drm_connector *connector,
                 const struct drm_display_mode *mode,
                 unsigned long long clock)
 {
+       const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs;
        const struct drm_display_info *info = &connector->display_info;
 
        if (info->max_tmds_clock && clock > info->max_tmds_clock * 1000)
                return MODE_CLOCK_HIGH;
 
+       if (funcs && funcs->tmds_char_rate_valid) {
+               enum drm_mode_status status;
+
+               status = funcs->tmds_char_rate_valid(connector, mode, clock);
+               if (status != MODE_OK)
+                       return status;
+       }
+
        return MODE_OK;
 }
 
index f554bf703e13baa0f216e5781f4350c920d89d3a..39e9ff6c4f611ba682f801ba1cc0a7f3cdf21051 100644 (file)
@@ -457,6 +457,7 @@ EXPORT_SYMBOL(drmm_connector_init);
  * @dev: DRM device
  * @connector: A pointer to the HDMI connector to init
  * @funcs: callbacks for this connector
+ * @hdmi_funcs: HDMI-related callbacks for this connector
  * @connector_type: user visible type of the connector
  * @ddc: optional pointer to the associated ddc adapter
  * @supported_formats: Bitmask of @hdmi_colorspace listing supported output formats
@@ -476,6 +477,7 @@ EXPORT_SYMBOL(drmm_connector_init);
 int drmm_connector_hdmi_init(struct drm_device *dev,
                             struct drm_connector *connector,
                             const struct drm_connector_funcs *funcs,
+                            const struct drm_connector_hdmi_funcs *hdmi_funcs,
                             int connector_type,
                             struct i2c_adapter *ddc,
                             unsigned long supported_formats,
@@ -512,6 +514,8 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
        if (max_bpc > 8)
                drm_connector_attach_hdr_output_metadata_property(connector);
 
+       connector->hdmi.funcs = hdmi_funcs;
+
        return 0;
 }
 EXPORT_SYMBOL(drmm_connector_hdmi_init);
index 8557f722080cc2b14659269ed9c60449a9805d56..e44dfe2e9455768e25986c4306df7690e11ac949 100644 (file)
@@ -24,6 +24,9 @@ struct drm_connector_init_priv {
        struct i2c_adapter ddc;
 };
 
+static const struct drm_connector_hdmi_funcs dummy_hdmi_funcs = {
+};
+
 static const struct drm_connector_funcs dummy_funcs = {
        .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
@@ -189,6 +192,7 @@ static void drm_test_connector_hdmi_init_valid(struct kunit *test)
 
        ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
                                       &dummy_funcs,
+                                      &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
                                       &priv->ddc,
                                       BIT(HDMI_COLORSPACE_RGB),
@@ -207,6 +211,7 @@ static void drm_test_connector_hdmi_init_null_ddc(struct kunit *test)
 
        ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
                                       &dummy_funcs,
+                                      &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
                                       NULL,
                                       BIT(HDMI_COLORSPACE_RGB),
@@ -225,6 +230,7 @@ static void drm_test_connector_hdmi_init_bpc_invalid(struct kunit *test)
 
        ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
                                       &dummy_funcs,
+                                      &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
                                       &priv->ddc,
                                       BIT(HDMI_COLORSPACE_RGB),
@@ -243,6 +249,7 @@ static void drm_test_connector_hdmi_init_bpc_null(struct kunit *test)
 
        ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
                                       &dummy_funcs,
+                                      &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
                                       &priv->ddc,
                                       BIT(HDMI_COLORSPACE_RGB),
@@ -266,6 +273,7 @@ static void drm_test_connector_hdmi_init_bpc_8(struct kunit *test)
 
        ret = drmm_connector_hdmi_init(&priv->drm, connector,
                                       &dummy_funcs,
+                                      &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
                                       &priv->ddc,
                                       BIT(HDMI_COLORSPACE_RGB),
@@ -305,6 +313,7 @@ static void drm_test_connector_hdmi_init_bpc_10(struct kunit *test)
 
        ret = drmm_connector_hdmi_init(&priv->drm, connector,
                                       &dummy_funcs,
+                                      &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
                                       &priv->ddc,
                                       BIT(HDMI_COLORSPACE_RGB),
@@ -344,6 +353,7 @@ static void drm_test_connector_hdmi_init_bpc_12(struct kunit *test)
 
        ret = drmm_connector_hdmi_init(&priv->drm, connector,
                                       &dummy_funcs,
+                                      &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
                                       &priv->ddc,
                                       BIT(HDMI_COLORSPACE_RGB),
@@ -378,6 +388,7 @@ static void drm_test_connector_hdmi_init_formats_empty(struct kunit *test)
 
        ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
                                       &dummy_funcs,
+                                      &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
                                       &priv->ddc,
                                       0,
@@ -396,6 +407,7 @@ static void drm_test_connector_hdmi_init_formats_no_rgb(struct kunit *test)
 
        ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
                                       &dummy_funcs,
+                                      &dummy_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
                                       &priv->ddc,
                                       BIT(HDMI_COLORSPACE_YUV422),
@@ -415,6 +427,7 @@ static void drm_test_connector_hdmi_init_type_valid(struct kunit *test)
 
        ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
                                       &dummy_funcs,
+                                      &dummy_hdmi_funcs,
                                       connector_type,
                                       &priv->ddc,
                                       BIT(HDMI_COLORSPACE_RGB),
@@ -448,6 +461,7 @@ static void drm_test_connector_hdmi_init_type_invalid(struct kunit *test)
 
        ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
                                       &dummy_funcs,
+                                      &dummy_hdmi_funcs,
                                       connector_type,
                                       &priv->ddc,
                                       BIT(HDMI_COLORSPACE_RGB),
index 0731f244f9774da8bcd29781e5c40086d22a19f4..23449a4786ce2630a86bfb0988a6c44679a98189 100644 (file)
@@ -112,6 +112,9 @@ static int set_connector_edid(struct kunit *test, struct drm_connector *connecto
        return 0;
 }
 
+static const struct drm_connector_hdmi_funcs dummy_connector_hdmi_funcs = {
+};
+
 static int dummy_connector_get_modes(struct drm_connector *connector)
 {
        struct drm_atomic_helper_connector_hdmi_priv *priv =
@@ -194,6 +197,7 @@ drm_atomic_helper_connector_hdmi_init(struct kunit *test,
        conn = &priv->connector;
        ret = drmm_connector_hdmi_init(drm, conn,
                                       &dummy_connector_funcs,
+                                      &dummy_connector_hdmi_funcs,
                                       DRM_MODE_CONNECTOR_HDMIA,
                                       NULL,
                                       formats,
index 3bdcf904a11b5d67eae576d797e6006205ff74fa..ffef967869d9960e9a60036d03018f8f9d4ff471 100644 (file)
@@ -38,6 +38,7 @@ struct drm_connector_helper_funcs;
 struct drm_modeset_acquire_ctx;
 struct drm_device;
 struct drm_crtc;
+struct drm_display_mode;
 struct drm_encoder;
 struct drm_panel;
 struct drm_property;
@@ -1057,6 +1058,30 @@ struct drm_connector_state {
        } hdmi;
 };
 
+/**
+ * struct drm_connector_hdmi_funcs - drm_hdmi_connector control functions
+ */
+struct drm_connector_hdmi_funcs {
+       /**
+        * @tmds_char_rate_valid:
+        *
+        * This callback is invoked at atomic_check time to figure out
+        * whether a particular TMDS character rate is supported by the
+        * driver.
+        *
+        * The @tmds_char_rate_valid callback is optional.
+        *
+        * Returns:
+        *
+        * Either &drm_mode_status.MODE_OK or one of the failure reasons
+        * in &enum drm_mode_status.
+        */
+       enum drm_mode_status
+       (*tmds_char_rate_valid)(const struct drm_connector *connector,
+                               const struct drm_display_mode *mode,
+                               unsigned long long tmds_rate);
+};
+
 /**
  * struct drm_connector_funcs - control connectors on a given device
  *
@@ -1929,6 +1954,11 @@ struct drm_connector {
                 * supported by the controller.
                 */
                unsigned long supported_formats;
+
+               /**
+                * @funcs: HDMI connector Control Functions
+                */
+               const struct drm_connector_hdmi_funcs *funcs;
        } hdmi;
 };
 
@@ -1951,6 +1981,7 @@ int drmm_connector_init(struct drm_device *dev,
 int drmm_connector_hdmi_init(struct drm_device *dev,
                             struct drm_connector *connector,
                             const struct drm_connector_funcs *funcs,
+                            const struct drm_connector_hdmi_funcs *hdmi_funcs,
                             int connector_type,
                             struct i2c_adapter *ddc,
                             unsigned long supported_formats,