From patchwork Tue May 21 10:13:46 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Maxime Ripard X-Patchwork-Id: 13669139 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 12CF7C25B75 for ; Tue, 21 May 2024 10:14:36 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 93B0D10E7AF; Tue, 21 May 2024 10:14:34 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="MFja8FzV"; dkim-atps=neutral Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by gabe.freedesktop.org (Postfix) with ESMTPS id E06DE10E645 for ; Tue, 21 May 2024 10:14:29 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id 2BFE062101; Tue, 21 May 2024 10:14:29 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8335FC4AF07; Tue, 21 May 2024 10:14:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1716286468; bh=tjb8VJKKklQTryxYyKvZvdB0KvpfoC5BuBstXRwhh9s=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=MFja8FzV3HRQs6wBu7gmpeY7GC30DXdGVjBxzw14QL+F3bzfGBLGKixSlHCp/1IrB lEl55uckCv7aJweFgf4XwMWw3U8WoakCL8lYxLDukcOi6Fgx5VkDi8z+kMw172S2CE aMdLvQTGvBj9NmUft/FmyCi9FKGAMSN8Ae/C4twqDat7TRlG7CekrD+7CQhTbvei7S 6bZN6lUlsuiFRHUK1hIb0k9FxuQLCm4BjxZBx1mdzi2iyCnm9xZvpJvypblJq/Bt9x xkjE82GssSamvoZONOpA5EUQi1547P68qadWNA7Yl+vTwUk3NR0jUCSawivoHkTH59 IN6/Q1DbPOWjA== From: Maxime Ripard Date: Tue, 21 May 2024 12:13:46 +0200 Subject: [PATCH v14 13/28] drm/connector: hdmi: Add custom hook to filter TMDS character rate MIME-Version: 1.0 Message-Id: <20240521-kms-hdmi-connector-state-v14-13-51950db4fedb@kernel.org> References: <20240521-kms-hdmi-connector-state-v14-0-51950db4fedb@kernel.org> In-Reply-To: <20240521-kms-hdmi-connector-state-v14-0-51950db4fedb@kernel.org> To: Maarten Lankhorst , Thomas Zimmermann , David Airlie , Daniel Vetter , Jonathan Corbet , Sandy Huang , =?utf-8?q?Heiko_St=C3=BCbner?= , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Andy Yan Cc: Hans Verkuil , Sebastian Wick , =?utf-8?b?VmlsbGUgU3lyasOkbMOk?= , dri-devel@lists.freedesktop.org, linux-arm-kernel@lists.infradead.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, linux-rockchip@lists.infradead.org, linux-sunxi@lists.linux.dev, Maxime Ripard , Dave Stevenson X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=12776; i=mripard@kernel.org; h=from:subject:message-id; bh=tjb8VJKKklQTryxYyKvZvdB0KvpfoC5BuBstXRwhh9s=; b=owGbwMvMwCmsHn9OcpHtvjLG02pJDGk+xddEJgbdrjvEwT4ng69BbrPow/+Bxyf/u7Ou5oLC9 EZXW5nQjqksDMKcDLJiiixPZMJOL29fXOVgv/IHzBxWJpAhDFycAjCRpkTGhoVdDFNns+QsiVYP V3MNdCpf0er23vfNj0/z7a901Jobvpr26nRav4KTQWCp0ce1DDuDGWu4509Zk3rKJbduo8ycb39 M+crlX96f3mL4bc/s9UdE33UnpHAkvHV8rCLHk/lWofirIj8A X-Developer-Key: i=mripard@kernel.org; a=openpgp; fpr=BE5675C37E818C8B5764241C254BCFC56BF6CE8D X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" 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 Signed-off-by: Maxime Ripard Reviewed-by: Dmitry Baryshkov --- drivers/gpu/drm/display/drm_hdmi_state_helper.c | 9 +++++++ drivers/gpu/drm/drm_connector.c | 4 +++ drivers/gpu/drm/tests/drm_connector_test.c | 14 ++++++++++ drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c | 4 +++ include/drm/drm_connector.h | 31 ++++++++++++++++++++++ 5 files changed, 62 insertions(+) diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c index 08630561d864..063421835dba 100644 --- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c @@ -51,15 +51,24 @@ connector_state_get_mode(const struct drm_connector_state *conn_state) static enum drm_mode_status 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; } static int hdmi_compute_clock(const struct drm_connector *connector, diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index b629c8e990f4..555eac20e5a4 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -455,10 +455,11 @@ EXPORT_SYMBOL(drmm_connector_init); /** * drmm_connector_hdmi_init - Init a preallocated HDMI connector * @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 * @max_bpc: Maximum bits per char the HDMI connector supports * @@ -474,10 +475,11 @@ EXPORT_SYMBOL(drmm_connector_init); * Zero on success, error code on failure. */ 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, unsigned int max_bpc) { @@ -510,10 +512,12 @@ int drmm_connector_hdmi_init(struct drm_device *dev, connector->max_bpc = max_bpc; 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); /** diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c index 426d974d8d74..34d96f7fbb25 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -22,10 +22,13 @@ struct drm_connector_init_priv { struct drm_device drm; struct drm_connector connector; 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, .reset = drm_atomic_helper_connector_reset, }; @@ -187,10 +190,11 @@ static void drm_test_connector_hdmi_init_valid(struct kunit *test) struct drm_connector_init_priv *priv = test->priv; int ret; ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, &dummy_funcs, + &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), 8); KUNIT_EXPECT_EQ(test, ret, 0); @@ -205,10 +209,11 @@ static void drm_test_connector_hdmi_init_null_ddc(struct kunit *test) struct drm_connector_init_priv *priv = test->priv; int ret; ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, &dummy_funcs, + &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, NULL, BIT(HDMI_COLORSPACE_RGB), 8); KUNIT_EXPECT_EQ(test, ret, 0); @@ -223,10 +228,11 @@ static void drm_test_connector_hdmi_init_bpc_invalid(struct kunit *test) struct drm_connector_init_priv *priv = test->priv; int ret; ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, &dummy_funcs, + &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), 9); KUNIT_EXPECT_LT(test, ret, 0); @@ -241,10 +247,11 @@ static void drm_test_connector_hdmi_init_bpc_null(struct kunit *test) struct drm_connector_init_priv *priv = test->priv; int ret; ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, &dummy_funcs, + &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), 0); KUNIT_EXPECT_LT(test, ret, 0); @@ -263,10 +270,11 @@ static void drm_test_connector_hdmi_init_bpc_8(struct kunit *test) uint64_t val; int ret; ret = drmm_connector_hdmi_init(&priv->drm, connector, &dummy_funcs, + &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), 8); KUNIT_EXPECT_EQ(test, ret, 0); @@ -297,10 +305,11 @@ static void drm_test_connector_hdmi_init_bpc_10(struct kunit *test) uint64_t val; int ret; ret = drmm_connector_hdmi_init(&priv->drm, connector, &dummy_funcs, + &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), 10); KUNIT_EXPECT_EQ(test, ret, 0); @@ -331,10 +340,11 @@ static void drm_test_connector_hdmi_init_bpc_12(struct kunit *test) uint64_t val; int ret; ret = drmm_connector_hdmi_init(&priv->drm, connector, &dummy_funcs, + &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), 12); KUNIT_EXPECT_EQ(test, ret, 0); @@ -361,10 +371,11 @@ static void drm_test_connector_hdmi_init_formats_empty(struct kunit *test) struct drm_connector_init_priv *priv = test->priv; int ret; ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, &dummy_funcs, + &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, 0, 8); KUNIT_EXPECT_LT(test, ret, 0); @@ -379,10 +390,11 @@ static void drm_test_connector_hdmi_init_formats_no_rgb(struct kunit *test) struct drm_connector_init_priv *priv = test->priv; int ret; ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, &dummy_funcs, + &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, BIT(HDMI_COLORSPACE_YUV422), 8); KUNIT_EXPECT_LT(test, ret, 0); @@ -398,10 +410,11 @@ static void drm_test_connector_hdmi_init_type_valid(struct kunit *test) unsigned int connector_type = *(unsigned int *)test->param_value; int ret; ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, &dummy_funcs, + &dummy_hdmi_funcs, connector_type, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), 8); KUNIT_EXPECT_EQ(test, ret, 0); @@ -431,10 +444,11 @@ static void drm_test_connector_hdmi_init_type_invalid(struct kunit *test) unsigned int connector_type = *(unsigned int *)test->param_value; int ret; ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, &dummy_funcs, + &dummy_hdmi_funcs, connector_type, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), 8); KUNIT_EXPECT_LT(test, ret, 0); 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 8ff53ee54e97..7f9a48902db4 100644 --- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c @@ -110,10 +110,13 @@ static int set_connector_edid(struct kunit *test, struct drm_connector *connecto KUNIT_ASSERT_GT(test, ret, 0); 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 = connector_to_priv(connector); const struct drm_edid *edid; @@ -192,10 +195,11 @@ drm_atomic_helper_connector_hdmi_init(struct kunit *test, enc->possible_crtcs = drm_crtc_mask(priv->crtc); conn = &priv->connector; ret = drmm_connector_hdmi_init(drm, conn, &dummy_connector_funcs, + &dummy_connector_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, NULL, formats, max_bpc); KUNIT_ASSERT_EQ(test, ret, 0); diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 54899c030031..3c0b6694074f 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -36,10 +36,11 @@ 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; struct drm_property_blob; struct drm_printer; @@ -1055,10 +1056,34 @@ struct drm_connector_state { */ unsigned long long tmds_char_rate; } 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 * * Each CRTC may have one or more connectors attached to it. The functions * below allow the core DRM code to control connectors, enumerate available modes, @@ -1923,10 +1948,15 @@ struct drm_connector { /** * @supported_formats: Bitmask of @hdmi_colorspace * supported by the controller. */ unsigned long supported_formats; + + /** + * @funcs: HDMI connector Control Functions + */ + const struct drm_connector_hdmi_funcs *funcs; } hdmi; }; #define obj_to_connector(x) container_of(x, struct drm_connector, base) @@ -1945,10 +1975,11 @@ int drmm_connector_init(struct drm_device *dev, int connector_type, struct i2c_adapter *ddc); 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, unsigned int max_bpc); void drm_connector_attach_edid_property(struct drm_connector *connector);