From patchwork Tue Nov 28 10:24:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Maxime Ripard X-Patchwork-Id: 170689 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:ce62:0:b0:403:3b70:6f57 with SMTP id o2csp3813857vqx; Tue, 28 Nov 2023 02:26:35 -0800 (PST) X-Google-Smtp-Source: AGHT+IG1zvYiOJpRyoosPMJgOlc1y+kOTOfgWVccX0jWC0ScCzGaG5GrsaEOVRKVc2rWABsH2g2P X-Received: by 2002:a05:6808:190c:b0:3b8:5bee:6ba4 with SMTP id bf12-20020a056808190c00b003b85bee6ba4mr14712412oib.5.1701167195358; Tue, 28 Nov 2023 02:26:35 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1701167195; cv=none; d=google.com; s=arc-20160816; b=rDvgayPyWswzFeHGRbhDGlbqrNK1/eU6MFlwyUjw4d4RhwmQWDFnAT6JcG9Bs+P8km jytyvF8PPndbM1U/jIWdawPUlGw/r2LZXPzVlo8OTqq6JN7+qGkUF/AM4b3LSKP/viNQ GSpvt+Vez7g1mbdxNkELDQy+2IVqSIpBGSgl20ctDpFtgOUTO9LysmbmzOxjy5SP05Bi qu+4f9ZoTH0EyzX16/Y6tzpeG3sUbzNQFY54saR/JjcMsYzR9wDCM0WEeZLIVyPPgFCD 6OFPw2UKVGspeogXJ/rMN5nvDrSnUGnWGsTKsw4DdfInROHog9B5D/iQYiF6oDDaCQT0 s75g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:in-reply-to:references:message-id :content-transfer-encoding:mime-version:subject:date:from :dkim-signature; bh=cEcVC9wB/8DthxA/hf5gr+SU0O/+/5LtGZViffU4k6E=; fh=e6/uyeFf2k7v8JUtB6fPyaAS5iN3eSA1hjtg35V2Rzw=; b=XfYtxXX8pjvhifiTRARe4YogOparckzoudH8VU572kVVcksQsVcJ9VuLxVHJKyl7zr S3sAjFIyqY5clBUQoB63TYOomXzC9v74Pw6nAOsVf9m6GKQQkDc6g8CCeD06v3Za5J5I 0RI4blrfGW9xzvw7lzN8iIDHd1f9SPuLeYMyiHq5fyPTllSM8Uy2yjH3zXJdAQvc9/Wv z8827YRx5+WWKtovxng+IQqDwGaOx3yrjdXd3XhUfXaFQWT+JU5XAJZNa7WmpjGJMpu0 Vk5Gjmoqelidd+OWBSPFTCOwtWndapyFDSru4Z1+yw3Y7SZL1068qrrCwRAI3YrOY7PY H5HQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=oTI6Dv3M; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.38 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: from fry.vger.email (fry.vger.email. [23.128.96.38]) by mx.google.com with ESMTPS id a21-20020a63e855000000b005be232472e6si11838351pgk.474.2023.11.28.02.26.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 28 Nov 2023 02:26:35 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.38 as permitted sender) client-ip=23.128.96.38; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=oTI6Dv3M; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.38 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by fry.vger.email (Postfix) with ESMTP id 6DD7E8077836; Tue, 28 Nov 2023 02:26:30 -0800 (PST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.11 at fry.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344367AbjK1K0E (ORCPT + 99 others); Tue, 28 Nov 2023 05:26:04 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39856 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234820AbjK1KZ0 (ORCPT ); Tue, 28 Nov 2023 05:25:26 -0500 Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A94F61FE6 for ; Tue, 28 Nov 2023 02:25:05 -0800 (PST) Received: by smtp.kernel.org (Postfix) with ESMTPSA id C0DF1C433D9; Tue, 28 Nov 2023 10:25:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1701167105; bh=RnFOphfoMJ84xnAA58VjL8L6GJ2hZMYhdb+qbqsc+KA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=oTI6Dv3MEYLXU51CchZ7TH/rypdy2PZsp33yNfSkHvmeCq5LFOdKum7O82Ux/NmQ6 F4jScktmuUEAKN841HrLmjAwz2jqq+LgNFE31KB73f7V4aX6Hee9ZBozkh8dW1XfCM j9ibhJHFlv7NxAVEa87HmsJ3pqg+3GAyOKYwRHXh3GghiHM/w1hcwloAhH+bI8PRzp KrOTMi4+AtimAezrUMiPHKTWnyV+Yj3Z7p2F5Q8M40FPMQ9Od3TNpA45WO18xoJM6J RD23omnqP9Oq2j+gxkPBYWnkFY7XkIzMH7ib+0ZKRD3/jeVwEfw/+kne80Gc2H7VjO ULhY+LXEiaLqA== From: Maxime Ripard Date: Tue, 28 Nov 2023 11:24:26 +0100 Subject: [PATCH v4 15/45] drm/connector: hdmi: Add custom hook to filter TMDS character rate MIME-Version: 1.0 Message-Id: <20231128-kms-hdmi-connector-state-v4-15-c7602158306e@kernel.org> References: <20231128-kms-hdmi-connector-state-v4-0-c7602158306e@kernel.org> In-Reply-To: <20231128-kms-hdmi-connector-state-v4-0-c7602158306e@kernel.org> To: Maarten Lankhorst , Thomas Zimmermann , David Airlie , Daniel Vetter , Emma Anholt , Jonathan Corbet , Sandy Huang , =?utf-8?q?Heiko_St=C3=BCbner?= , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland Cc: Hans Verkuil , 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 X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=15402; i=mripard@kernel.org; h=from:subject:message-id; bh=RnFOphfoMJ84xnAA58VjL8L6GJ2hZMYhdb+qbqsc+KA=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKmp+y/ySq68UKl/ItEv1e3Y49YHj3KiGyJTXx5n3Xniz qoF2ganO0pZGMS4GGTFFFlihM2XxJ2a9bqTjW8ezBxWJpAhDFycAjCRX3MY/vt2Jj7tWnK9yfxt 9vuo7Rzee2XFt9x3/LTjbuajSUqJgZ8YGa4vTGP28JCwXB3WKVmTHaL5urL1kPceRf5r+zZ+zOm fzgMA X-Developer-Key: i=mripard@kernel.org; a=openpgp; fpr=BE5675C37E818C8B5764241C254BCFC56BF6CE8D X-Spam-Status: No, score=-1.2 required=5.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,MAILING_LIST_MULTI, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on fry.vger.email Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (fry.vger.email [0.0.0.0]); Tue, 28 Nov 2023 02:26:30 -0800 (PST) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1783803092835060688 X-GMAIL-MSGID: 1783803092835060688 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. Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic_state_helper.c | 9 +++ drivers/gpu/drm/drm_connector.c | 7 +++ .../gpu/drm/tests/drm_atomic_state_helper_test.c | 69 ++++++++++++++++++++++ drivers/gpu/drm/tests/drm_connector_test.c | 38 ++++++++++++ include/drm/drm_connector.h | 30 ++++++++++ 5 files changed, 153 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 63069432c061..af3ece136040 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -673,11 +673,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; } diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 5eefeb868dbb..1073ab2f78bc 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -460,6 +460,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 @@ -479,6 +480,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, @@ -490,6 +492,9 @@ int drmm_connector_hdmi_init(struct drm_device *dev, connector_type == DRM_MODE_CONNECTOR_HDMIB)) return -EINVAL; + if (!hdmi_funcs) + return -EINVAL; + if (!supported_formats || !(supported_formats & BIT(HDMI_COLORSPACE_RGB))) return -EINVAL; @@ -515,6 +520,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); diff --git a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c index d76fafb91025..e7dbdd4a4e7f 100644 --- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c @@ -110,6 +110,21 @@ 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 enum drm_mode_status +reject_connector_tmds_char_rate_valid(const struct drm_connector *connector, + const struct drm_display_mode *mode, + unsigned long long tmds_rate) +{ + return MODE_BAD; +} + +static const struct drm_connector_hdmi_funcs reject_connector_hdmi_funcs = { + .tmds_char_rate_valid = reject_connector_tmds_char_rate_valid, +}; + static int dummy_connector_get_modes(struct drm_connector *connector) { struct drm_atomic_helper_connector_hdmi_priv *priv = @@ -192,6 +207,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, @@ -956,6 +972,58 @@ static void drm_test_check_tmds_char_rate_rgb_12bpc(struct kunit *test) KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1500); } +/* + * Test that if we filter a rate through our hook, it's indeed rejected + * by the whole atomic_check logic. + * + * We do so by first doing a commit on the pipeline to make sure that it + * works, change the HDMI helpers pointer, and then try the same commit + * again to see if it fails as it should. + */ +static void drm_test_check_hdmi_funcs_reject_rate(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_atomic_state *state; + struct drm_display_mode *preferred; + struct drm_crtc_state *crtc_state; + struct drm_connector *conn; + struct drm_device *drm; + struct drm_crtc *crtc; + int ret; + + priv = drm_atomic_helper_connector_hdmi_init(test, + BIT(HDMI_COLORSPACE_RGB), + 8); + KUNIT_ASSERT_NOT_NULL(test, priv); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + conn = &priv->connector; + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + KUNIT_ASSERT_EQ(test, ret, 0); + + /* You shouldn't be doing that at home. */ + conn->hdmi.funcs = &reject_connector_hdmi_funcs; + + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + crtc_state->connectors_changed = true; + + ret = drm_atomic_check_only(state); + KUNIT_EXPECT_LT(test, ret, 0); +} + static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = { KUNIT_CASE(drm_test_check_broadcast_rgb_auto_cea_mode), KUNIT_CASE(drm_test_check_broadcast_rgb_auto_cea_mode_vic_1), @@ -965,6 +1033,7 @@ static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = { KUNIT_CASE(drm_test_check_broadcast_rgb_limited_cea_mode_vic_1), KUNIT_CASE(drm_test_check_broadcast_rgb_crtc_mode_changed), KUNIT_CASE(drm_test_check_broadcast_rgb_crtc_mode_not_changed), + KUNIT_CASE(drm_test_check_hdmi_funcs_reject_rate), KUNIT_CASE(drm_test_check_output_bpc_crtc_mode_changed), KUNIT_CASE(drm_test_check_output_bpc_crtc_mode_not_changed), KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_8bpc), diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c index 5262118cde85..493cbda9d5db 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -22,6 +22,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, @@ -236,6 +239,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), @@ -254,6 +258,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), @@ -271,6 +276,7 @@ static void drm_test_connector_hdmi_init_null_device(struct kunit *test) ret = drmm_connector_hdmi_init(NULL, &priv->connector, &dummy_funcs, + &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), @@ -288,6 +294,7 @@ static void drm_test_connector_hdmi_init_null_connector(struct kunit *test) ret = drmm_connector_hdmi_init(&priv->drm, NULL, &dummy_funcs, + &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), @@ -305,6 +312,26 @@ static void drm_test_connector_hdmi_init_null_funcs(struct kunit *test) int ret; ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + NULL, + &dummy_hdmi_funcs, + DRM_MODE_CONNECTOR_HDMIA, + &priv->ddc, + BIT(HDMI_COLORSPACE_RGB), + 8); + KUNIT_EXPECT_LT(test, ret, 0); +} + +/* + * Test that the registration of a connector without any HDMI callbacks + * fails. + */ +static void drm_test_connector_hdmi_init_null_hdmi_funcs(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + int ret; + + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + &dummy_funcs, NULL, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, @@ -324,6 +351,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), @@ -342,6 +370,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), @@ -364,6 +393,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), @@ -398,6 +428,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), @@ -432,6 +463,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), @@ -462,6 +494,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, @@ -480,6 +513,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), @@ -499,6 +533,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), @@ -532,6 +567,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), @@ -578,6 +614,7 @@ static struct kunit_case drmm_connector_hdmi_init_tests[] = { KUNIT_CASE(drm_test_connector_hdmi_init_null_ddc), KUNIT_CASE(drm_test_connector_hdmi_init_null_device), KUNIT_CASE(drm_test_connector_hdmi_init_null_funcs), + KUNIT_CASE(drm_test_connector_hdmi_init_null_hdmi_funcs), KUNIT_CASE_PARAM(drm_test_connector_hdmi_init_type_valid, drm_connector_hdmi_init_type_valid_gen_params), KUNIT_CASE_PARAM(drm_test_connector_hdmi_init_type_invalid, @@ -799,6 +836,7 @@ static void drm_test_drm_connector_attach_broadcast_rgb_property_hdmi_connector( ret = drmm_connector_hdmi_init(&priv->drm, connector, &dummy_funcs, + &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 59016d9c17f5..3eaf4d54364d 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1093,6 +1093,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 * @@ -1967,6 +1991,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; }; @@ -1989,6 +2018,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,