Received: by 2002:ab2:788f:0:b0:1ee:8f2e:70ae with SMTP id b15csp565275lqi; Thu, 7 Mar 2024 05:47:05 -0800 (PST) X-Forwarded-Encrypted: i=3; AJvYcCWGvUs3s96iS2Me2HeqPusZBp4MmDOduczL25+kQui7Dy/Q6llY1lgW0y0TfCMbakmBVQ4jBhYhnqD9fysbAjR7U1wOntrw6PXJ1eAztw== X-Google-Smtp-Source: AGHT+IFcqPbD7U+D2odA08+3tcc3PHcpWzo1FzfdsDywlhc5E0T/l0p7NlvWdueJZ7o2L3hbsEsA X-Received: by 2002:a17:90a:8a0a:b0:299:3f5d:b5e8 with SMTP id w10-20020a17090a8a0a00b002993f5db5e8mr15330958pjn.20.1709819224991; Thu, 07 Mar 2024 05:47:04 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1709819224; cv=pass; d=google.com; s=arc-20160816; b=tAWlChcrPZe5eBdudlSdnG2tT9SfQhEzjmkA/qaoBGDMBOIFqY/pya0y1r7i38ukqR v8c1/C++qlOU8paLeE4IazpoPub9JaRI5DEgAVwSduXXgPPfXe2YJd1Pp+LtYj9ggznf a0UvXSo+7CVSq5GsHb4ObGA/3qPbUOyUXjmG82+RtdRXmfZ+FXVUUgWU6wCxQHezWMsG MlOT1lG6BJaJWeg+O8Ueqskb6E851KScDQ5nmGHVgkmI/JAXqpoAUNedtF9H9x7uQPSP vLFo4FqEHalOqJ99947raIwhRYDzm3LuPxsQX7FH29vWMLQCV/ByyJ7xTpGWH7YPYmSi 5QMA== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:list-unsubscribe:list-subscribe:list-id:precedence :subject:date:from:dkim-signature; bh=RBNhvgN5fqMeLYwM42qco//H0nq7oDoPW0KmzJ8fm04=; fh=8wcoxKk21hICD+6obVirG13fsSSMhKa/pufykSf87d0=; b=VhwFFIn1HTZpJbBrtgehjrKgswZs2yeobz2NcPWqutBmY7m8CZl3pnMCWzrciOJeQa 2TyNjBt9qXyOlHBFBpXkv2t28RG733dx2FjTU7DZhLpUcRTauUONCYiFm0SFUk2zJBgs 76xi2c9LdGggkRotow7xgJfl7yB2yRQYMT92sADM2HftBVAV6z/7n6b0XqUDGJNuPH1V 4l7A7+ouwcStWrJYIW1m1hWVyt56TLYrUh8JVe5V7YNiJ6Dx5W2d9E8XMaW/ttwtv5St qAzV45rSVF7cZtJM883Umyi5DWUe1BIHjMOOz/6NxC2i5HeVhAEXoDTa7RDMO+Qn5r6N soRQ==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=jZ1BCvMN; arc=pass (i=1 dkim=pass dkdomain=kernel.org); spf=pass (google.com: domain of linux-kernel+bounces-95635-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) smtp.mailfrom="linux-kernel+bounces-95635-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Return-Path: Received: from sy.mirrors.kernel.org (sy.mirrors.kernel.org. [147.75.48.161]) by mx.google.com with ESMTPS id s19-20020a17090ad49300b0029b85203a6dsi1664785pju.136.2024.03.07.05.47.04 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 Mar 2024 05:47:04 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-95635-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) client-ip=147.75.48.161; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=jZ1BCvMN; arc=pass (i=1 dkim=pass dkdomain=kernel.org); spf=pass (google.com: domain of linux-kernel+bounces-95635-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) smtp.mailfrom="linux-kernel+bounces-95635-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sy.mirrors.kernel.org (Postfix) with ESMTPS id EFB1CB23DFC for ; Thu, 7 Mar 2024 13:44:19 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 8939C135A6E; Thu, 7 Mar 2024 13:39:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="jZ1BCvMN" Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 220771353EC; Thu, 7 Mar 2024 13:39:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1709818770; cv=none; b=ZJufYGw3bdM2fRWl6dmWeudfcJjLWzdZ0a7uFM7QEPvyYOtNK7tX/ng4/T11H/kJFqwZksYi9D5wXzj95MotI+Nes3iutBrUzZ0uT8FkbXCh2adFYvrM+qUYl11eFkSisvQdJhUEXu5iYQ/ewBZjt9m7h26uYIh3L7QKU93Ajyg= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1709818770; c=relaxed/simple; bh=2vPwPO8kvi7SoILxLke9m0YI6xFI1AJM4npOguVc2BY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=W0jqKx7kCWhcOqc0DhJfM/+c3u49v+TuyFAOOt17puC5IUNkEUbV1T5mOgotNF5kqACoV7zatA8h2R7+ia6zbohdUJ2Kbg9xERQ7ti6CLDbThR/TwCQaBjTfB2tleHod973UhK1WzGnE+Hc2WtJToJdLwNt8Tuz6Yi7TkMjOvHw= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=jZ1BCvMN; arc=none smtp.client-ip=10.30.226.201 Received: by smtp.kernel.org (Postfix) with ESMTPSA id A5562C433C7; Thu, 7 Mar 2024 13:39:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1709818770; bh=2vPwPO8kvi7SoILxLke9m0YI6xFI1AJM4npOguVc2BY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=jZ1BCvMNGyycQnVlGyEDNYTTonebraysNRCjSS0yz8fUjScC0P/olWpxzJKT9wdKS rMDGe0qTRxLU9y6xWghkz2kcxAFrXVbn0afWnVjBsZ+GBn2Dn9ToZWxFWw1v1wmcZt 8pi4UJlWa4nhL/oLy3mnbYhvjLczot/s5a+PLc9HW2yKGJ6OZY0bhUkUrc8YeJ7hKL Amxijh9wFvmwxHIP7ZGKpzdg1cef5VGWGWLn7cApSiHoLKR25Q2hEV38Itwbs7HPPb sr7Ddi9aMfcVWLBN93L+j7nf+0sfzLfD2tFUbhtQ6qYFbJZzH8DhDD4lJaU34pSkr8 fUpMi5D/c7Eeg== From: Maxime Ripard Date: Thu, 07 Mar 2024 14:38:39 +0100 Subject: [PATCH v8 12/27] drm/connector: hdmi: Add custom hook to filter TMDS character rate Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20240307-kms-hdmi-connector-state-v8-12-ef6a6f31964b@kernel.org> References: <20240307-kms-hdmi-connector-state-v8-0-ef6a6f31964b@kernel.org> In-Reply-To: <20240307-kms-hdmi-connector-state-v8-0-ef6a6f31964b@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 Cc: Hans Verkuil , Sebastian Wick , =?utf-8?q?Ville_Syrj=C3=A4l=C3=A4?= , 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.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=12485; i=mripard@kernel.org; h=from:subject:message-id; bh=2vPwPO8kvi7SoILxLke9m0YI6xFI1AJM4npOguVc2BY=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKkvDydN3WXik9TA8mZVoRXHyS3JD/3T5LWOpWfcvHC7b LrLjhd5HaUsDGJcDLJiiiwxwuZL4k7Net3JxjcPZg4rE8gQBi5OAZhIKgsjw0J2/w9euxasvCJY tZnn4bE3rqfXsZltYtquJ3UjSCF2Kj8jQ9PU6qmrHv08emTWqhDWn0FH1E+Ffbg4e8etv5arVWT u9TEAAA== X-Developer-Key: i=mripard@kernel.org; a=openpgp; fpr=BE5675C37E818C8B5764241C254BCFC56BF6CE8D 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 --- drivers/gpu/drm/drm_atomic_state_helper.c | 9 +++++++ drivers/gpu/drm/drm_connector.c | 4 +++ .../gpu/drm/tests/drm_atomic_state_helper_test.c | 4 +++ drivers/gpu/drm/tests/drm_connector_test.c | 14 ++++++++++ include/drm/drm_connector.h | 30 ++++++++++++++++++++++ 5 files changed, 61 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 63a96c691460..448b4a73d1c8 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -665,15 +665,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 8cc1332f11c2..591d2d500f61 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_atomic_state_helper_test.c b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c index dfed45b250a5..989661ad3aee 100644 --- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c @@ -108,10 +108,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; @@ -190,10 +193,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/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c index 4d1ac744a844..ac4b8d00e992 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -20,10 +20,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, }; @@ -185,10 +188,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); @@ -203,10 +207,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); @@ -221,10 +226,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); @@ -239,10 +245,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); @@ -261,10 +268,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); @@ -295,10 +303,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); @@ -329,10 +338,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); @@ -359,10 +369,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); @@ -377,10 +388,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); @@ -396,10 +408,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); @@ -429,10 +442,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/include/drm/drm_connector.h b/include/drm/drm_connector.h index e3917ea93986..8cda902934cd 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1056,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, @@ -1924,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) @@ -1946,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); -- 2.43.2