From patchwork Mon Mar 11 14:49:29 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588830 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 D4F36C5475B for ; Mon, 11 Mar 2024 14:50:10 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id A47B510F870; Mon, 11 Mar 2024 14:50:09 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="moX67BX/"; dkim-atps=neutral Received: from sin.source.kernel.org (sin.source.kernel.org [145.40.73.55]) by gabe.freedesktop.org (Postfix) with ESMTPS id 65B8310F870 for ; Mon, 11 Mar 2024 14:50:07 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sin.source.kernel.org (Postfix) with ESMTP id AAD04CE1105; Mon, 11 Mar 2024 14:50:04 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 94002C43394; Mon, 11 Mar 2024 14:50:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168603; bh=uITNeO8iu5HQmM+XePNnDpEdGi4EM3JrAF8vMV6INqk=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=moX67BX/RU8gfnyaIS/Z7bIbGxYt5vU6SN9IsTT4D+neOqyIc/IzMabBBxRhlqfHE OSL0oh+2QMYTeTqIlInO+xgAFyUymB+Fss9vDf0GLEDgse/ThPQJvo5C4MJQ6wVuwc tJzrfwfnQCbHiMWTNYe6alU3dXP3osvJb+gvk7JRgtkguwcmAKx9Ro0OMkuPJQ/lch ojnphbjBs12ZYAo16lbAXVYgl/dXKNCFpAPXNyRAPgxWB04+2UV55ulMsUScb45WlN NL0w9ldIeYVnvwi1xUgxNzlvbl8JCIA3gvcjRHLQDShHJKH+xQjqV/a8sG0SFTLA31 yc7bZvqHFafsA== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:29 +0100 Subject: [PATCH v9 01/27] drm/connector: Introduce an HDMI connector initialization function MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-1-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=3792; i=mripard@kernel.org; h=from:subject:message-id; bh=uITNeO8iu5HQmM+XePNnDpEdGi4EM3JrAF8vMV6INqk=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpViYZN6tfvotds+GhBv+cq8tnKW4z1cUb9AV5zl1N /HKeVOXjlIWBjEuBlkxRZYYYfMlcadmve5k45sHM4eVCWQIAxenAEzkZAbDP90vD7k5vumc4lh/ /+8aLuGQ5vcdTvu+dy3hlJvs8/cY5x1GhiPVlbq3982sTo+bf2qnp3e1DHNW49SSIJWXDCw59Y+ usgIA 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" A lot of the various HDMI drivers duplicate some logic that depends on the HDMI spec itself and not really a particular hardware implementation. Output BPC or format selection, infoframe generation are good examples of such areas. This creates a lot of boilerplate, with a lot of variations, which makes it hard for userspace to rely on, and makes it difficult to get it right for drivers. In the next patches, we'll add a lot of infrastructure around the drm_connector and drm_connector_state structures, which will allow to abstract away the duplicated logic. This infrastructure comes with a few requirements though, and thus we need a new initialization function. Hopefully, this will make drivers simpler to handle, and their behaviour more consistent. Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Reviewed-by: Sui Jingfeng --- drivers/gpu/drm/drm_connector.c | 39 +++++++++++++++++++++++++++++++++++++++ include/drm/drm_connector.h | 5 +++++ 2 files changed, 44 insertions(+) diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index b0516505f7ae..d9961cce8245 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -450,10 +450,49 @@ int drmm_connector_init(struct drm_device *dev, return 0; } 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 + * @connector_type: user visible type of the connector + * @ddc: optional pointer to the associated ddc adapter + * + * Initialises a preallocated HDMI connector. Connectors can be + * subclassed as part of driver connector objects. + * + * Cleanup is automatically handled with a call to + * drm_connector_cleanup() in a DRM-managed action. + * + * The connector structure should be allocated with drmm_kzalloc(). + * + * Returns: + * 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, + int connector_type, + struct i2c_adapter *ddc) +{ + int ret; + + if (!(connector_type == DRM_MODE_CONNECTOR_HDMIA || + connector_type == DRM_MODE_CONNECTOR_HDMIB)) + return -EINVAL; + + ret = drmm_connector_init(dev, connector, funcs, connector_type, ddc); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(drmm_connector_hdmi_init); + /** * drm_connector_attach_edid_property - attach edid property. * @connector: the connector * * Some connector types like DRM_MODE_CONNECTOR_VIRTUAL do not get a diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index fe88d7fc6b8f..4491c4c2fb6e 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1902,10 +1902,15 @@ int drm_connector_init_with_ddc(struct drm_device *dev, int drmm_connector_init(struct drm_device *dev, struct drm_connector *connector, const struct drm_connector_funcs *funcs, 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, + int connector_type, + struct i2c_adapter *ddc); void drm_connector_attach_edid_property(struct drm_connector *connector); int drm_connector_register(struct drm_connector *connector); void drm_connector_unregister(struct drm_connector *connector); int drm_connector_attach_encoder(struct drm_connector *connector, struct drm_encoder *encoder); From patchwork Mon Mar 11 14:49:30 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588831 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 BE942C54E58 for ; Mon, 11 Mar 2024 14:50:14 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 0B5DF10FC68; Mon, 11 Mar 2024 14:50:14 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="g5/oMlIu"; 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 1780510F138 for ; Mon, 11 Mar 2024 14:50:07 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id D829F60EBC; Mon, 11 Mar 2024 14:50:06 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 562AAC43399; Mon, 11 Mar 2024 14:50:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168606; bh=CjIGYTbMXVCqb3XcBIX9A123T+vivJfkGlKpICpmC2k=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=g5/oMlIubhSeW1B2smF0/CI/QbzSzy8nDue4iZqfCebGwJxg14bY1ZH5JKRwaNKHc uVN5MPSENnqg+8BH0bf+iPWp7O6+vX02BruS5df+oPKnpARtXuvB1TxylceYabXe0N uFLHa0ILkKHMRLA8a3ATYtzxzINtcfu/Q7qItRm83i/yNSNHiCSxzakyhHKEWgm34h S7Zh8CH6sMp6PK+uwsawuIMpHU8TA8Zirdf4cDGHOFG40+qbg8nIz5Y4DF3sJsEZhx gfslY4KA0DCIvDCQc1wd06EPsheQqwOdYXmcnwH6KSNOpouIPxqzMGT25B4mqlTz2D Iw2P8e5c30a1g== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:30 +0100 Subject: [PATCH v9 02/27] drm/tests: connector: Add tests for drmm_connector_hdmi_init MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-2-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=5120; i=mripard@kernel.org; h=from:subject:message-id; bh=CjIGYTbMXVCqb3XcBIX9A123T+vivJfkGlKpICpmC2k=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpVhf7L0052D+ouA5+8S8bF+veO3akh8pJ/3EVrAlx OplIo9+RykLgxgXg6yYIkuMsPmSuFOzXney8c2DmcPKBDKEgYtTACayfQLD/7S3OulSB7+2LfVc /31dSHy7wOvfx5j5Jhg9d3N5zDHnNj/D/xyNHiExpuyQXS/MxV0Pp0TFlbLJ7v/UZ2zPnhl9zHg 9OwA= 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" We just introduced a new initialization function for our connectors, so let's build a kunit test suite for it as well. Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard --- drivers/gpu/drm/tests/drm_connector_test.c | 123 +++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c index 44f82ed2a958..261d4109946d 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -170,10 +170,132 @@ static struct kunit_suite drmm_connector_init_test_suite = { .name = "drmm_connector_init", .init = drm_test_connector_init, .test_cases = drmm_connector_init_tests, }; +/* + * Test that the registration of a bog standard connector works as + * expected and doesn't report any error. + */ +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, + DRM_MODE_CONNECTOR_HDMIA, + &priv->ddc); + KUNIT_EXPECT_EQ(test, ret, 0); +} + +/* + * Test that the registration of a connector without a DDC adapter + * doesn't report any error. + */ +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, + DRM_MODE_CONNECTOR_HDMIA, + NULL); + KUNIT_EXPECT_EQ(test, ret, 0); +} + +/* + * Test that the registration of an HDMI connector with an HDMI + * connector type succeeds. + */ +static void drm_test_connector_hdmi_init_type_valid(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + unsigned int connector_type = *(unsigned int *)test->param_value; + int ret; + + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + &dummy_funcs, + connector_type, + &priv->ddc); + KUNIT_EXPECT_EQ(test, ret, 0); +} + +static const unsigned int drm_connector_hdmi_init_type_valid_tests[] = { + DRM_MODE_CONNECTOR_HDMIA, + DRM_MODE_CONNECTOR_HDMIB, +}; + +static void drm_connector_hdmi_init_type_desc(const unsigned int *type, char *desc) +{ + sprintf(desc, "%s", drm_get_connector_type_name(*type)); +} + +KUNIT_ARRAY_PARAM(drm_connector_hdmi_init_type_valid, + drm_connector_hdmi_init_type_valid_tests, + drm_connector_hdmi_init_type_desc); + +/* + * Test that the registration of an HDMI connector with an !HDMI + * connector type fails. + */ +static void drm_test_connector_hdmi_init_type_invalid(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + unsigned int connector_type = *(unsigned int *)test->param_value; + int ret; + + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + &dummy_funcs, + connector_type, + &priv->ddc); + KUNIT_EXPECT_LT(test, ret, 0); +} + +static const unsigned int drm_connector_hdmi_init_type_invalid_tests[] = { + DRM_MODE_CONNECTOR_Unknown, + DRM_MODE_CONNECTOR_VGA, + DRM_MODE_CONNECTOR_DVII, + DRM_MODE_CONNECTOR_DVID, + DRM_MODE_CONNECTOR_DVIA, + DRM_MODE_CONNECTOR_Composite, + DRM_MODE_CONNECTOR_SVIDEO, + DRM_MODE_CONNECTOR_LVDS, + DRM_MODE_CONNECTOR_Component, + DRM_MODE_CONNECTOR_9PinDIN, + DRM_MODE_CONNECTOR_DisplayPort, + DRM_MODE_CONNECTOR_TV, + DRM_MODE_CONNECTOR_eDP, + DRM_MODE_CONNECTOR_VIRTUAL, + DRM_MODE_CONNECTOR_DSI, + DRM_MODE_CONNECTOR_DPI, + DRM_MODE_CONNECTOR_WRITEBACK, + DRM_MODE_CONNECTOR_SPI, + DRM_MODE_CONNECTOR_USB, +}; + +KUNIT_ARRAY_PARAM(drm_connector_hdmi_init_type_invalid, + drm_connector_hdmi_init_type_invalid_tests, + drm_connector_hdmi_init_type_desc); + +static struct kunit_case drmm_connector_hdmi_init_tests[] = { + KUNIT_CASE(drm_test_connector_hdmi_init_valid), + KUNIT_CASE(drm_test_connector_hdmi_init_null_ddc), + 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, + drm_connector_hdmi_init_type_invalid_gen_params), + { } +}; + +static struct kunit_suite drmm_connector_hdmi_init_test_suite = { + .name = "drmm_connector_hdmi_init", + .init = drm_test_connector_init, + .test_cases = drmm_connector_hdmi_init_tests, +}; + struct drm_get_tv_mode_from_name_test { const char *name; enum drm_connector_tv_mode expected_mode; }; @@ -234,10 +356,11 @@ static struct kunit_suite drm_get_tv_mode_from_name_test_suite = { .name = "drm_get_tv_mode_from_name", .test_cases = drm_get_tv_mode_from_name_tests, }; kunit_test_suites( + &drmm_connector_hdmi_init_test_suite, &drmm_connector_init_test_suite, &drm_get_tv_mode_from_name_test_suite ); MODULE_AUTHOR("Maxime Ripard "); From patchwork Mon Mar 11 14:49:31 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588834 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 CFE79C5475B for ; Mon, 11 Mar 2024 14:50:19 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id D39FF112ACA; Mon, 11 Mar 2024 14:50:18 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="hm7D8VZA"; 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 C6EF910FCF7 for ; Mon, 11 Mar 2024 14:50:10 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id 13FE160DD7; Mon, 11 Mar 2024 14:50:10 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8218FC433F1; Mon, 11 Mar 2024 14:50:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168609; bh=eZw9702RagXmh2VG3XD3rGSR1PJvIZ11Csaq049mV+s=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=hm7D8VZAEom/JUmAnSjS5TDa2YkBoSnexgnrR8TQjG9JHM8hivo5YeUD+zp9pmSG3 mYMgyjbdcjkPA6yfjQwgTu5oS524+Tj3QiEmWKswHks1Xc8j04SbZpia7Gf/ki+61G 2YYcSbRM2PUSMXKPpnyzCsnlnWzWWS/Sv87UOwrRU3LjB188uxokpleWTuKBzcPydf RME+E2tNoBvsUSkDuG/6Nb7bWauqLoYl2/rsXOF2LIlH8erRfZMrwwzsBBY8PoZnol lm0GiAud8lDcZTgl3gtzNvgoW9U2yDf//2TYnU0kdQ22gKctyNPRRrEGx+k9SXwd7K nylDaybiWoFHg== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:31 +0100 Subject: [PATCH v9 03/27] drm/connector: hdmi: Create an HDMI sub-state MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-3-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=4742; i=mripard@kernel.org; h=from:subject:message-id; bh=eZw9702RagXmh2VG3XD3rGSR1PJvIZ11Csaq049mV+s=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpdjiPswM849pTKtzUjbf+TfB/3do5DHeK5Ed/+tNS 93envPvKGVhEONikBVTZIkRNl8Sd2rW6042vnkwc1iZQIYwcHEKwEQidzIyXD9fUVU1u+7MO5af m6O33py37Y7CmdX2V27edPietibuviYjw4KfPWrvE+UmKsy6ufZcrijTFeOCrsrrtnPkbz/4vin YmAkA 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" The next features we will need to share across drivers will need to store some parameters for drivers to use, such as the selected output format. Let's create a new connector sub-state dedicated to HDMI controllers, that will eventually store everything we need. Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic_state_helper.c | 35 +++++++++++++++++++++++++++++++ include/drm/drm_atomic_state_helper.h | 4 ++++ include/drm/drm_connector.h | 7 +++++++ 3 files changed, 46 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 519228eb1095..7ad1dffe66d9 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -569,10 +569,26 @@ void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector) drm_atomic_helper_connector_tv_margins_reset(connector); } EXPORT_SYMBOL(drm_atomic_helper_connector_tv_reset); +/** + * __drm_atomic_helper_connector_hdmi_reset() - Initializes all HDMI @drm_connector_state resources + * @connector: DRM connector + * @new_state: connector state to reset + * + * Initializes all HDMI resources from a @drm_connector_state without + * actually allocating it. This is useful for HDMI drivers, in + * combination with __drm_atomic_helper_connector_reset() or + * drm_atomic_helper_connector_reset(). + */ +void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector, + struct drm_connector_state *new_state) +{ +} +EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_reset); + /** * drm_atomic_helper_connector_tv_check - Validate an analog TV connector state * @connector: DRM Connector * @state: the DRM State object * @@ -618,10 +634,29 @@ int drm_atomic_helper_connector_tv_check(struct drm_connector *connector, return 0; } EXPORT_SYMBOL(drm_atomic_helper_connector_tv_check); +/** + * drm_atomic_helper_connector_hdmi_check() - Helper to check HDMI connector atomic state + * @connector: DRM Connector + * @state: the DRM State object + * + * Provides a default connector state check handler for HDMI connectors. + * Checks that a desired connector update is valid, and updates various + * fields of derived state. + * + * RETURNS: + * Zero on success, or an errno code otherwise. + */ +int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector, + struct drm_atomic_state *state) +{ + return 0; +} +EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_check); + /** * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state * @connector: connector object * @state: atomic connector state * diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index b9740edb2658..d59d2b3aef9a 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -69,11 +69,15 @@ void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, void __drm_atomic_helper_connector_state_reset(struct drm_connector_state *conn_state, struct drm_connector *connector); void __drm_atomic_helper_connector_reset(struct drm_connector *connector, struct drm_connector_state *conn_state); void drm_atomic_helper_connector_reset(struct drm_connector *connector); +void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector, + struct drm_connector_state *new_state); void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector); +int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector, + struct drm_atomic_state *state); int drm_atomic_helper_connector_tv_check(struct drm_connector *connector, struct drm_atomic_state *state); void drm_atomic_helper_connector_tv_margins_reset(struct drm_connector *connector); void __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 4491c4c2fb6e..000a2a156619 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1029,10 +1029,17 @@ struct drm_connector_state { /** * @hdr_output_metadata: * DRM blob property for HDR output metadata */ struct drm_property_blob *hdr_output_metadata; + + /** + * @hdmi: HDMI-related variable and properties. Filled by + * @drm_atomic_helper_connector_hdmi_check(). + */ + struct { + } hdmi; }; /** * struct drm_connector_funcs - control connectors on a given device * From patchwork Mon Mar 11 14:49:32 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588832 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 02994C5475B for ; Mon, 11 Mar 2024 14:50:15 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 90E6710FE57; Mon, 11 Mar 2024 14:50:14 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="kOe9qD2+"; 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 9574510FC68 for ; Mon, 11 Mar 2024 14:50:13 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id F0ACC60EC9; Mon, 11 Mar 2024 14:50:12 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6B8E3C433C7; Mon, 11 Mar 2024 14:50:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168612; bh=DCTqBIluyTbkgVZ3/5iiIdIvkYJXGSwt0l8P8XxAPEU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=kOe9qD2+e9393O/jrAN/AXFFWvOjhH79sUfNVuK0hlzNSSr7rieTDQXjsdV78c6FU GUYVKjOExq/MB0/VEs1nsDPhTzWo4W85WV3JJRIVUwt3h/FEo4HY5rK7fKddgPES4j qxZ3F2tYsmkOR/n1XtSBUBSIyXvFAZnJ57WGOt8SESrhDa2HwB72WX0f232kyoZpL0 0lJCaI9C7FoAPpmTfgQWbOae1JOoI9fGbdxhmwWlgSoKjCIpuV6CcxnLEkmZThE0yJ 8tXXGTZi5ix/JjB0QElmEeJoMjVnN5FWi9Cg8TZqJsuXfYguYLBUnE3TkqDtFThYsR vabMI3dgPFiTQ== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:32 +0100 Subject: [PATCH v9 04/27] drm/connector: hdmi: Add output BPC to the connector state MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-4-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=8922; i=mripard@kernel.org; h=from:subject:message-id; bh=DCTqBIluyTbkgVZ3/5iiIdIvkYJXGSwt0l8P8XxAPEU=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpdg/C/GkLVy9sHR2c+i0Qw7r+Z45Zk/sapux9tHN0 rmCu9fydpSyMIhxMciKKbLECJsviTs163UnG988mDmsTCBDGLg4BWAiF/0YGVZPKH2iv+jvpxe+ Wy8lHnxfuunbPpkotpV87AoSTwsjwu4yMtySVeELzp71+nf8pucFzdF7dlxSX7PTQFPQ+5v1ZIV wQXYA 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" We'll add automatic selection of the output BPC in a following patch, but let's add it to the HDMI connector state already. Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic.c | 5 +++++ drivers/gpu/drm/drm_atomic_state_helper.c | 20 ++++++++++++++++++++ drivers/gpu/drm/drm_connector.c | 20 +++++++++++++++++++- drivers/gpu/drm/tests/drm_connector_test.c | 12 ++++++++---- include/drm/drm_connector.h | 12 +++++++++++- 5 files changed, 63 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index a91737adf8e7..4e11cfb4518b 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1141,10 +1141,15 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, drm_printf(p, "\tcrtc=%s\n", state->crtc ? state->crtc->name : "(null)"); drm_printf(p, "\tself_refresh_aware=%d\n", state->self_refresh_aware); drm_printf(p, "\tmax_requested_bpc=%d\n", state->max_requested_bpc); drm_printf(p, "\tcolorspace=%s\n", drm_get_colorspace_name(state->colorspace)); + if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || + connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) { + drm_printf(p, "\toutput_bpc=%u\n", state->hdmi.output_bpc); + } + if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) if (state->writeback_job && state->writeback_job->fb) drm_printf(p, "\tfb=%d\n", state->writeback_job->fb->base.id); if (connector->funcs->atomic_print_state) diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 7ad1dffe66d9..460454904fe3 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -582,10 +582,14 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_tv_reset); * drm_atomic_helper_connector_reset(). */ void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector, struct drm_connector_state *new_state) { + unsigned int max_bpc = connector->max_bpc; + + new_state->max_bpc = max_bpc; + new_state->max_requested_bpc = max_bpc; } EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_reset); /** * drm_atomic_helper_connector_tv_check - Validate an analog TV connector state @@ -649,10 +653,26 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_tv_check); * Zero on success, or an errno code otherwise. */ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector, struct drm_atomic_state *state) { + struct drm_connector_state *old_state = + drm_atomic_get_old_connector_state(state, connector); + struct drm_connector_state *new_state = + drm_atomic_get_new_connector_state(state, connector); + + if (old_state->hdmi.output_bpc != new_state->hdmi.output_bpc) { + struct drm_crtc *crtc = new_state->crtc; + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + crtc_state->mode_changed = true; + } + return 0; } EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_check); /** diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index d9961cce8245..da51a2bcb978 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -457,10 +457,11 @@ EXPORT_SYMBOL(drmm_connector_init); * @dev: DRM device * @connector: A pointer to the HDMI connector to init * @funcs: callbacks for this connector * @connector_type: user visible type of the connector * @ddc: optional pointer to the associated ddc adapter + * @max_bpc: Maximum bits per char the HDMI connector supports * * Initialises a preallocated HDMI connector. Connectors can be * subclassed as part of driver connector objects. * * Cleanup is automatically handled with a call to @@ -473,22 +474,39 @@ EXPORT_SYMBOL(drmm_connector_init); */ int drmm_connector_hdmi_init(struct drm_device *dev, struct drm_connector *connector, const struct drm_connector_funcs *funcs, int connector_type, - struct i2c_adapter *ddc) + struct i2c_adapter *ddc, + unsigned int max_bpc) { int ret; if (!(connector_type == DRM_MODE_CONNECTOR_HDMIA || connector_type == DRM_MODE_CONNECTOR_HDMIB)) return -EINVAL; + if (!(max_bpc == 8 || max_bpc == 10 || max_bpc == 12)) + return -EINVAL; + ret = drmm_connector_init(dev, connector, funcs, connector_type, ddc); if (ret) return ret; + /* + * drm_connector_attach_max_bpc_property() requires the + * connector to have a state. + */ + if (connector->funcs->reset) + connector->funcs->reset(connector); + + drm_connector_attach_max_bpc_property(connector, 8, max_bpc); + connector->max_bpc = max_bpc; + + if (max_bpc > 8) + drm_connector_attach_hdr_output_metadata_property(connector); + 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 261d4109946d..2661eb64a5cd 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -182,11 +182,12 @@ static void drm_test_connector_hdmi_init_valid(struct kunit *test) int ret; ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, &dummy_funcs, DRM_MODE_CONNECTOR_HDMIA, - &priv->ddc); + &priv->ddc, + 8); KUNIT_EXPECT_EQ(test, ret, 0); } /* * Test that the registration of a connector without a DDC adapter @@ -198,11 +199,12 @@ static void drm_test_connector_hdmi_init_null_ddc(struct kunit *test) int ret; ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, &dummy_funcs, DRM_MODE_CONNECTOR_HDMIA, - NULL); + NULL, + 8); KUNIT_EXPECT_EQ(test, ret, 0); } /* * Test that the registration of an HDMI connector with an HDMI @@ -215,11 +217,12 @@ static void drm_test_connector_hdmi_init_type_valid(struct kunit *test) int ret; ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, &dummy_funcs, connector_type, - &priv->ddc); + &priv->ddc, + 8); KUNIT_EXPECT_EQ(test, ret, 0); } static const unsigned int drm_connector_hdmi_init_type_valid_tests[] = { DRM_MODE_CONNECTOR_HDMIA, @@ -246,11 +249,12 @@ static void drm_test_connector_hdmi_init_type_invalid(struct kunit *test) int ret; ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, &dummy_funcs, connector_type, - &priv->ddc); + &priv->ddc, + 8); KUNIT_EXPECT_LT(test, ret, 0); } static const unsigned int drm_connector_hdmi_init_type_invalid_tests[] = { DRM_MODE_CONNECTOR_Unknown, diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 000a2a156619..d4d2ae15bc1e 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1035,10 +1035,14 @@ struct drm_connector_state { /** * @hdmi: HDMI-related variable and properties. Filled by * @drm_atomic_helper_connector_hdmi_check(). */ struct { + /** + * @output_bpc: Bits per color channel to output. + */ + unsigned int output_bpc; } hdmi; }; /** * struct drm_connector_funcs - control connectors on a given device @@ -1680,10 +1684,15 @@ struct drm_connector { * DRM blob property data for the DP MST path property. This should only * be updated by calling drm_connector_set_path_property(). */ struct drm_property_blob *path_blob_ptr; + /** + * @max_bpc: Maximum bits per color channel the connector supports. + */ + unsigned int max_bpc; + /** * @max_bpc_property: Default connector property for the max bpc to be * driven out of the connector. */ struct drm_property *max_bpc_property; @@ -1913,11 +1922,12 @@ int drmm_connector_init(struct drm_device *dev, struct i2c_adapter *ddc); int drmm_connector_hdmi_init(struct drm_device *dev, struct drm_connector *connector, const struct drm_connector_funcs *funcs, int connector_type, - struct i2c_adapter *ddc); + struct i2c_adapter *ddc, + unsigned int max_bpc); void drm_connector_attach_edid_property(struct drm_connector *connector); int drm_connector_register(struct drm_connector *connector); void drm_connector_unregister(struct drm_connector *connector); int drm_connector_attach_encoder(struct drm_connector *connector, struct drm_encoder *encoder); From patchwork Mon Mar 11 14:49:33 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588833 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 85CFEC54E58 for ; Mon, 11 Mar 2024 14:50:18 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id DF9EF112AC2; Mon, 11 Mar 2024 14:50:17 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="CKy0k/5x"; 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 D05E4112720 for ; Mon, 11 Mar 2024 14:50:16 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id 3072260EC2; Mon, 11 Mar 2024 14:50:16 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4E9BFC43399; Mon, 11 Mar 2024 14:50:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168616; bh=xqWVlsbHOsm95if4S8KC73qtl4XxHLkRaNosbEV3xqs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=CKy0k/5xLbTtyLgWYZe80Ao0vtR812EBbSRWYn4kyRFddgD2q4crsWRfI1Z3NotcD GhTN7SblCS8ViOWlT0ujNk2uLtaFeJnGbg9x5a/PGn74y1jY4WKJ9ah+oPzROetgjR rIh6vExZ3PJvUXGaMMp6Ct5WuOM/o/xb/u/7WZ25318dAzLbHBaCgFkiSjlRxgGp5e T243rXSUCOa41YzYBGei3VVSF7nu0o4L/Nlq7X5aLOpjuxKoPmOHvQbJ/mnp9LCwAV XneOuwMVxUhl/jpgfIpX71YWTb0MvpM8karwOO4/jd7i1COY0WPyB20NK7OeNYohqR KgndPrkxUMsQA== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:33 +0100 Subject: [PATCH v9 05/27] drm/tests: Add output bpc tests MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-5-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=26460; i=mripard@kernel.org; h=from:subject:message-id; bh=xqWVlsbHOsm95if4S8KC73qtl4XxHLkRaNosbEV3xqs=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpdgDlzj8O36BN+LFKfkNfww4a13aqj85nstuiHMID Pz2ZuevjlIWBjEuBlkxRZYYYfMlcadmve5k45sHM4eVCWQIAxenAEzkiygjw1T7fw8u377u/vFl Qc4k9hhXoZfCh+/4T3TMfei0Z9a+hTGMDHuyu562uN1cwymyL+WnYtMSvj9d1hXSze+TVxkndPL zcgAA 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" Now that we're tracking the output bpc count in the connector state, let's add a few tests to make sure it works as expected. Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard --- drivers/gpu/drm/tests/Makefile | 1 + .../gpu/drm/tests/drm_atomic_state_helper_test.c | 436 +++++++++++++++++++++ drivers/gpu/drm/tests/drm_connector_test.c | 140 +++++++ drivers/gpu/drm/tests/drm_kunit_edid.h | 106 +++++ 4 files changed, 683 insertions(+) diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index d6183b3d7688..b29ddfd90596 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -2,10 +2,11 @@ obj-$(CONFIG_DRM_KUNIT_TEST_HELPERS) += \ drm_kunit_helpers.o obj-$(CONFIG_DRM_KUNIT_TEST) += \ + drm_atomic_state_helper_test.o \ drm_buddy_test.o \ drm_cmdline_parser_test.o \ drm_connector_test.o \ drm_damage_helper_test.o \ drm_dp_mst_helper_test.o \ diff --git a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c new file mode 100644 index 000000000000..5d1a1dbff433 --- /dev/null +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Kunit test for drm_atomic_state_helper functions + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "../drm_crtc_internal.h" + +#include + +#include "drm_kunit_edid.h" + +struct drm_atomic_helper_connector_hdmi_priv { + struct drm_device drm; + struct drm_plane *plane; + struct drm_crtc *crtc; + struct drm_encoder encoder; + struct drm_connector connector; + + const char *current_edid; + size_t current_edid_len; +}; + +#define connector_to_priv(c) \ + container_of_const(c, struct drm_atomic_helper_connector_hdmi_priv, connector) + +static struct drm_display_mode *find_preferred_mode(struct drm_connector *connector) +{ + struct drm_device *drm = connector->dev; + struct drm_display_mode *mode, *preferred; + + mutex_lock(&drm->mode_config.mutex); + preferred = list_first_entry(&connector->modes, struct drm_display_mode, head); + list_for_each_entry(mode, &connector->modes, head) + if (mode->type & DRM_MODE_TYPE_PREFERRED) + preferred = mode; + mutex_unlock(&drm->mode_config.mutex); + + return preferred; +} + +static int light_up_connector(struct kunit *test, + struct drm_device *drm, + struct drm_crtc *crtc, + struct drm_connector *connector, + struct drm_display_mode *mode, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_atomic_state *state; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + int ret; + + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + conn_state = drm_atomic_get_connector_state(state, connector); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + ret = drm_atomic_set_crtc_for_connector(conn_state, crtc); + KUNIT_EXPECT_EQ(test, ret, 0); + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + ret = drm_atomic_set_mode_for_crtc(crtc_state, mode); + KUNIT_EXPECT_EQ(test, ret, 0); + + crtc_state->enable = true; + crtc_state->active = true; + + ret = drm_atomic_commit(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + return 0; +} + +static int set_connector_edid(struct kunit *test, struct drm_connector *connector, + const char *edid, size_t edid_len) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv = + connector_to_priv(connector); + struct drm_device *drm = connector->dev; + int ret; + + priv->current_edid = edid; + priv->current_edid_len = edid_len; + + mutex_lock(&drm->mode_config.mutex); + ret = connector->funcs->fill_modes(connector, 4096, 4096); + mutex_unlock(&drm->mode_config.mutex); + KUNIT_ASSERT_GT(test, ret, 0); + + return 0; +} + +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; + unsigned int num_modes; + + edid = drm_edid_alloc(priv->current_edid, priv->current_edid_len); + if (!edid) + return -EINVAL; + + drm_edid_connector_update(connector, edid); + num_modes = drm_edid_connector_add_modes(connector); + + drm_edid_free(edid); + + return num_modes; +} + +static const struct drm_connector_helper_funcs dummy_connector_helper_funcs = { + .atomic_check = drm_atomic_helper_connector_hdmi_check, + .get_modes = dummy_connector_get_modes, +}; + +static void dummy_hdmi_connector_reset(struct drm_connector *connector) +{ + drm_atomic_helper_connector_reset(connector); + __drm_atomic_helper_connector_hdmi_reset(connector, connector->state); +} + +static const struct drm_connector_funcs dummy_connector_funcs = { + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .fill_modes = drm_helper_probe_single_connector_modes, + .reset = dummy_hdmi_connector_reset, +}; + +static +struct drm_atomic_helper_connector_hdmi_priv * +drm_atomic_helper_connector_hdmi_init(struct kunit *test, + unsigned int max_bpc) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_connector *conn; + struct drm_encoder *enc; + struct drm_device *drm; + struct device *dev; + int ret; + + dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + priv = drm_kunit_helper_alloc_drm_device(test, dev, + struct drm_atomic_helper_connector_hdmi_priv, drm, + DRIVER_MODESET | DRIVER_ATOMIC); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); + test->priv = priv; + + drm = &priv->drm; + priv->plane = drm_kunit_helper_create_primary_plane(test, drm, + NULL, + NULL, + NULL, 0, + NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->plane); + + priv->crtc = drm_kunit_helper_create_crtc(test, drm, + priv->plane, NULL, + NULL, + NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->crtc); + + enc = &priv->encoder; + ret = drmm_encoder_init(drm, enc, NULL, DRM_MODE_ENCODER_TMDS, NULL); + KUNIT_ASSERT_EQ(test, ret, 0); + + enc->possible_crtcs = drm_crtc_mask(priv->crtc); + + conn = &priv->connector; + ret = drmm_connector_hdmi_init(drm, conn, + &dummy_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA, + NULL, + max_bpc); + KUNIT_ASSERT_EQ(test, ret, 0); + + drm_connector_helper_add(conn, &dummy_connector_helper_funcs); + drm_connector_attach_encoder(conn, enc); + + drm_mode_config_reset(drm); + + ret = set_connector_edid(test, conn, + test_edid_hdmi_1080p_rgb_max_200mhz, + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_200mhz)); + KUNIT_ASSERT_EQ(test, ret, 0); + + return priv; +} + +/* + * Test that if we change the maximum bpc property to a different value, + * we trigger a mode change on the connector's CRTC, which will in turn + * disable/enable the connector. + */ +static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *old_conn_state; + struct drm_connector_state *new_conn_state; + struct drm_crtc_state *crtc_state; + struct drm_atomic_state *state; + struct drm_display_mode *preferred; + struct drm_connector *conn; + struct drm_device *drm; + struct drm_crtc *crtc; + int ret; + + priv = drm_atomic_helper_connector_hdmi_init(test, 10); + 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); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + new_conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state); + + old_conn_state = drm_atomic_get_old_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state); + + new_conn_state->hdmi.output_bpc = 8; + + KUNIT_ASSERT_NE(test, + old_conn_state->hdmi.output_bpc, + new_conn_state->hdmi.output_bpc); + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + old_conn_state = drm_atomic_get_old_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state); + + new_conn_state = drm_atomic_get_new_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state); + + KUNIT_ASSERT_NE(test, + old_conn_state->hdmi.output_bpc, + new_conn_state->hdmi.output_bpc); + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + KUNIT_EXPECT_TRUE(test, crtc_state->mode_changed); +} + +/* + * Test that if we set the output bpc property to the same value, we + * don't trigger a mode change on the connector's CRTC and leave the + * connector unaffected. + */ +static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *old_conn_state; + struct drm_connector_state *new_conn_state; + struct drm_crtc_state *crtc_state; + struct drm_atomic_state *state; + struct drm_display_mode *preferred; + struct drm_connector *conn; + struct drm_device *drm; + struct drm_crtc *crtc; + int ret; + + priv = drm_atomic_helper_connector_hdmi_init(test, 10); + 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); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + new_conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state); + + old_conn_state = drm_atomic_get_old_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state); + + KUNIT_ASSERT_EQ(test, + new_conn_state->hdmi.output_bpc, + old_conn_state->hdmi.output_bpc); + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + old_conn_state = drm_atomic_get_old_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state); + + new_conn_state = drm_atomic_get_new_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state); + + KUNIT_EXPECT_EQ(test, + old_conn_state->hdmi.output_bpc, + new_conn_state->hdmi.output_bpc); + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + KUNIT_EXPECT_FALSE(test, crtc_state->mode_changed); +} + +static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = { + KUNIT_CASE(drm_test_check_output_bpc_crtc_mode_changed), + KUNIT_CASE(drm_test_check_output_bpc_crtc_mode_not_changed), + { } +}; + +static struct kunit_suite drm_atomic_helper_connector_hdmi_check_test_suite = { + .name = "drm_atomic_helper_connector_hdmi_check", + .test_cases = drm_atomic_helper_connector_hdmi_check_tests, +}; + +/* + * Test that if the connector was initialised with a maximum bpc of 8, + * the value of the max_bpc and max_requested_bpc properties out of + * reset are also set to 8, and output_bpc is set to 0 and will be + * filled at atomic_check time. + */ +static void drm_test_check_bpc_8_value(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_connector_state *conn_state; + struct drm_connector *conn; + + priv = drm_atomic_helper_connector_hdmi_init(test, 8); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + conn_state = conn->state; + KUNIT_EXPECT_EQ(test, conn_state->max_bpc, 8); + KUNIT_EXPECT_EQ(test, conn_state->max_requested_bpc, 8); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 0); +} + +/* + * Test that if the connector was initialised with a maximum bpc of 10, + * the value of the max_bpc and max_requested_bpc properties out of + * reset are also set to 10, and output_bpc is set to 0 and will be + * filled at atomic_check time. + */ +static void drm_test_check_bpc_10_value(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_connector_state *conn_state; + struct drm_connector *conn; + + priv = drm_atomic_helper_connector_hdmi_init(test, 10); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + conn_state = conn->state; + KUNIT_EXPECT_EQ(test, conn_state->max_bpc, 10); + KUNIT_EXPECT_EQ(test, conn_state->max_requested_bpc, 10); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 0); +} + +/* + * Test that if the connector was initialised with a maximum bpc of 12, + * the value of the max_bpc and max_requested_bpc properties out of + * reset are also set to 12, and output_bpc is set to 0 and will be + * filled at atomic_check time. + */ +static void drm_test_check_bpc_12_value(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_connector_state *conn_state; + struct drm_connector *conn; + + priv = drm_atomic_helper_connector_hdmi_init(test, 12); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + conn_state = conn->state; + KUNIT_EXPECT_EQ(test, conn_state->max_bpc, 12); + KUNIT_EXPECT_EQ(test, conn_state->max_requested_bpc, 12); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 0); +} + +static struct kunit_case drm_atomic_helper_connector_hdmi_reset_tests[] = { + KUNIT_CASE(drm_test_check_bpc_8_value), + KUNIT_CASE(drm_test_check_bpc_10_value), + KUNIT_CASE(drm_test_check_bpc_12_value), + { } +}; + +static struct kunit_suite drm_atomic_helper_connector_hdmi_reset_test_suite = { + .name = "drm_atomic_helper_connector_hdmi_reset", + .test_cases = drm_atomic_helper_connector_hdmi_reset_tests, +}; + +kunit_test_suites( + &drm_atomic_helper_connector_hdmi_check_test_suite, + &drm_atomic_helper_connector_hdmi_reset_test_suite, +); + +MODULE_AUTHOR("Maxime Ripard "); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c index 2661eb64a5cd..2519b91de95e 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -10,10 +10,12 @@ #include #include #include +#include "../drm_crtc_internal.h" + struct drm_connector_init_priv { struct drm_device drm; struct drm_connector connector; struct i2c_adapter ddc; }; @@ -204,10 +206,143 @@ static void drm_test_connector_hdmi_init_null_ddc(struct kunit *test) NULL, 8); KUNIT_EXPECT_EQ(test, ret, 0); } +/* + * Test that the registration of a connector with an invalid maximum bpc + * count fails. + */ +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, + DRM_MODE_CONNECTOR_HDMIA, + &priv->ddc, + 9); + KUNIT_EXPECT_LT(test, ret, 0); +} + +/* + * Test that the registration of a connector with a null maximum bpc + * count fails. + */ +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, + DRM_MODE_CONNECTOR_HDMIA, + &priv->ddc, + 0); + KUNIT_EXPECT_LT(test, ret, 0); +} + +/* + * Test that the registration of a connector with a maximum bpc count of + * 8 succeeds, registers the max bpc property, but doesn't register the + * HDR output metadata one. + */ +static void drm_test_connector_hdmi_init_bpc_8(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + struct drm_connector *connector = &priv->connector; + struct drm_property *prop; + uint64_t val; + int ret; + + ret = drmm_connector_hdmi_init(&priv->drm, connector, + &dummy_funcs, + DRM_MODE_CONNECTOR_HDMIA, + &priv->ddc, + 8); + KUNIT_EXPECT_EQ(test, ret, 0); + + prop = connector->max_bpc_property; + KUNIT_ASSERT_NOT_NULL(test, prop); + KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id)); + + ret = drm_object_property_get_value(&connector->base, prop, &val); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, val, 8); + + prop = priv->drm.mode_config.hdr_output_metadata_property; + KUNIT_ASSERT_NOT_NULL(test, prop); + KUNIT_EXPECT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id)); +} + +/* + * Test that the registration of a connector with a maximum bpc count of + * 10 succeeds and registers the max bpc and HDR output metadata + * properties. + */ +static void drm_test_connector_hdmi_init_bpc_10(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + struct drm_connector *connector = &priv->connector; + struct drm_property *prop; + uint64_t val; + int ret; + + ret = drmm_connector_hdmi_init(&priv->drm, connector, + &dummy_funcs, + DRM_MODE_CONNECTOR_HDMIA, + &priv->ddc, + 10); + KUNIT_EXPECT_EQ(test, ret, 0); + + prop = connector->max_bpc_property; + KUNIT_ASSERT_NOT_NULL(test, prop); + KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id)); + + ret = drm_object_property_get_value(&connector->base, prop, &val); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, val, 10); + + prop = priv->drm.mode_config.hdr_output_metadata_property; + KUNIT_ASSERT_NOT_NULL(test, prop); + KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id)); +} + +/* + * Test that the registration of a connector with a maximum bpc count of + * 12 succeeds and registers the max bpc and HDR output metadata + * properties. + */ +static void drm_test_connector_hdmi_init_bpc_12(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + struct drm_connector *connector = &priv->connector; + struct drm_property *prop; + uint64_t val; + int ret; + + ret = drmm_connector_hdmi_init(&priv->drm, connector, + &dummy_funcs, + DRM_MODE_CONNECTOR_HDMIA, + &priv->ddc, + 12); + KUNIT_EXPECT_EQ(test, ret, 0); + + prop = connector->max_bpc_property; + KUNIT_ASSERT_NOT_NULL(test, prop); + KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id)); + + ret = drm_object_property_get_value(&connector->base, prop, &val); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, val, 12); + + prop = priv->drm.mode_config.hdr_output_metadata_property; + KUNIT_ASSERT_NOT_NULL(test, prop); + KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id)); +} + /* * Test that the registration of an HDMI connector with an HDMI * connector type succeeds. */ static void drm_test_connector_hdmi_init_type_valid(struct kunit *test) @@ -282,10 +417,15 @@ KUNIT_ARRAY_PARAM(drm_connector_hdmi_init_type_invalid, drm_connector_hdmi_init_type_invalid_tests, drm_connector_hdmi_init_type_desc); static struct kunit_case drmm_connector_hdmi_init_tests[] = { KUNIT_CASE(drm_test_connector_hdmi_init_valid), + KUNIT_CASE(drm_test_connector_hdmi_init_bpc_8), + KUNIT_CASE(drm_test_connector_hdmi_init_bpc_10), + KUNIT_CASE(drm_test_connector_hdmi_init_bpc_12), + KUNIT_CASE(drm_test_connector_hdmi_init_bpc_invalid), + KUNIT_CASE(drm_test_connector_hdmi_init_bpc_null), KUNIT_CASE(drm_test_connector_hdmi_init_null_ddc), 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, drm_connector_hdmi_init_type_invalid_gen_params), diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.h b/drivers/gpu/drm/tests/drm_kunit_edid.h new file mode 100644 index 000000000000..0366dd29c820 --- /dev/null +++ b/drivers/gpu/drm/tests/drm_kunit_edid.h @@ -0,0 +1,106 @@ +#ifndef DRM_KUNIT_EDID_H_ +#define DRM_KUNIT_EDID_H_ + +/* + * edid-decode (hex): + * + * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 + * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00 + * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01 + * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c + * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 + * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 + * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92 + * + * 02 03 1b 81 e3 05 00 20 41 10 e2 00 4a 6d 03 0c + * 00 12 34 00 28 20 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d0 + * + * ---------------- + * + * Block 0, Base EDID: + * EDID Structure Version & Revision: 1.3 + * Vendor & Product Identification: + * Manufacturer: LNX + * Model: 42 + * Made in: 2023 + * Basic Display Parameters & Features: + * Digital display + * DFP 1.x compatible TMDS + * Maximum image size: 160 cm x 90 cm + * Gamma: 2.20 + * Monochrome or grayscale display + * First detailed timing is the preferred timing + * Color Characteristics: + * Red : 0.0000, 0.0000 + * Green: 0.0000, 0.0000 + * Blue : 0.0000, 0.0000 + * White: 0.0000, 0.0000 + * Established Timings I & II: + * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz + * Standard Timings: none + * Detailed Timing Descriptors: + * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) + * Hfront 88 Hsync 44 Hback 148 Hpol P + * Vfront 4 Vsync 5 Vback 36 Vpol P + * Display Product Name: 'Test EDID' + * Display Range Limits: + * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz + * Dummy Descriptor: + * Extension blocks: 1 + * Checksum: 0x92 + * + * ---------------- + * + * Block 1, CTA-861 Extension Block: + * Revision: 3 + * Underscans IT Video Formats by default + * Native detailed modes: 1 + * Colorimetry Data Block: + * sRGB + * Video Data Block: + * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz + * Video Capability Data Block: + * YCbCr quantization: No Data + * RGB quantization: Selectable (via AVI Q) + * PT scan behavior: No Data + * IT scan behavior: Always Underscanned + * CE scan behavior: Always Underscanned + * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: + * Source physical address: 1.2.3.4 + * Maximum TMDS clock: 200 MHz + * Extended HDMI video details: + * Checksum: 0xd0 Unused space in Extension Block: 100 bytes + */ +static const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, + 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, + 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, + 0x46, 0x00, 0x00, 0xc4, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x02, 0x03, 0x1b, 0x81, + 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x6d, 0x03, 0x0c, + 0x00, 0x12, 0x34, 0x00, 0x28, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xd0 +}; + +#endif // DRM_KUNIT_EDID_H_ From patchwork Mon Mar 11 14:49:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588835 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 5B7A8C54E58 for ; Mon, 11 Mar 2024 14:50:23 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 8DB0010FCF7; Mon, 11 Mar 2024 14:50:22 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="Hk9I7jTw"; 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 EE62A112470 for ; Mon, 11 Mar 2024 14:50:19 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id D963B60EB9; Mon, 11 Mar 2024 14:50:18 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 57169C433F1; Mon, 11 Mar 2024 14:50:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168618; bh=0CzX1jg40qqIJLC46w8/k2Ft/Q55zd3GeVatG2t1zbM=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Hk9I7jTwaZkvRWL6olsgzmu7TqvZbu3lgUCf9Mc8b+wsPbXzn6mYJN3l2885SCzet U3f9Oi+7Jh4Lkq4tJCVhBnPPLParmwjB75fMs2v6lgfYvAoCCnHCbEU0f4Uc/1HOZ0 BVxbrj7diK6AT9iLkUERw76U2BRH9a2/Uq8nuArP3zwJl2W44Dz8bpA036lXCtQl/O M7IkyftCIUcWBTYwxtJsyknFKSsp0a8GZF5oSMxFOC6IIAxlFknbYrHHBiPcXHTNCs DPVwlypVnP05jeYOo2RPbO0nvoCHP6rLA6YJ541u5kTyIkVOdFlLQ2sFvD+T+sQ7bw bRkT34yM75DDw== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:34 +0100 Subject: [PATCH v9 06/27] drm/connector: hdmi: Add support for output format MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-6-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=15064; i=mripard@kernel.org; h=from:subject:message-id; bh=0CzX1jg40qqIJLC46w8/k2Ft/Q55zd3GeVatG2t1zbM=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpTicrt66toTZbPZ9+T3X1y34sq3EkDP/isSfkD25u 3+f57B73FHKwiDGxSArpsgSI2y+JO7UrNedbHzzYOawMoEMYeDiFICJ2Bgy/PdJnbrwnw275B5V /es95+PT+oLMbB6Fsf20vLjTMm62YQrDf5fNM6rs6xXW8vkv+OYY+OP9aabo/gMvkk/8k/yzV+P ZJAYA 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" Just like BPC, we'll add support for automatic selection of the output format for HDMI connectors. Let's add the needed defaults and fields for now. Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic.c | 2 ++ drivers/gpu/drm/drm_atomic_state_helper.c | 3 ++- drivers/gpu/drm/drm_connector.c | 31 ++++++++++++++++++++++ .../gpu/drm/tests/drm_atomic_state_helper_test.c | 22 +++++++++++---- drivers/gpu/drm/tests/drm_connector_test.c | 9 +++++++ include/drm/drm_connector.h | 20 ++++++++++++++ 6 files changed, 81 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 4e11cfb4518b..8730137baa86 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1144,10 +1144,12 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, drm_printf(p, "\tcolorspace=%s\n", drm_get_colorspace_name(state->colorspace)); if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) { drm_printf(p, "\toutput_bpc=%u\n", state->hdmi.output_bpc); + drm_printf(p, "\toutput_format=%s\n", + drm_hdmi_connector_get_output_format_name(state->hdmi.output_format)); } if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) if (state->writeback_job && state->writeback_job->fb) drm_printf(p, "\tfb=%d\n", state->writeback_job->fb->base.id); diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 460454904fe3..ae99765c45de 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -658,11 +658,12 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector, struct drm_connector_state *old_state = drm_atomic_get_old_connector_state(state, connector); struct drm_connector_state *new_state = drm_atomic_get_new_connector_state(state, connector); - if (old_state->hdmi.output_bpc != new_state->hdmi.output_bpc) { + if (old_state->hdmi.output_bpc != new_state->hdmi.output_bpc || + old_state->hdmi.output_format != new_state->hdmi.output_format) { struct drm_crtc *crtc = new_state->crtc; struct drm_crtc_state *crtc_state; crtc_state = drm_atomic_get_crtc_state(state, crtc); if (IS_ERR(crtc_state)) diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index da51a2bcb978..b629c8e990f4 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -457,10 +457,11 @@ EXPORT_SYMBOL(drmm_connector_init); * @dev: DRM device * @connector: A pointer to the HDMI connector to init * @funcs: 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 * * Initialises a preallocated HDMI connector. Connectors can be * subclassed as part of driver connector objects. * @@ -475,25 +476,31 @@ EXPORT_SYMBOL(drmm_connector_init); int drmm_connector_hdmi_init(struct drm_device *dev, struct drm_connector *connector, const struct drm_connector_funcs *funcs, int connector_type, struct i2c_adapter *ddc, + unsigned long supported_formats, unsigned int max_bpc) { int ret; if (!(connector_type == DRM_MODE_CONNECTOR_HDMIA || connector_type == DRM_MODE_CONNECTOR_HDMIB)) return -EINVAL; + if (!supported_formats || !(supported_formats & BIT(HDMI_COLORSPACE_RGB))) + return -EINVAL; + if (!(max_bpc == 8 || max_bpc == 10 || max_bpc == 12)) return -EINVAL; ret = drmm_connector_init(dev, connector, funcs, connector_type, ddc); if (ret) return ret; + connector->hdmi.supported_formats = supported_formats; + /* * drm_connector_attach_max_bpc_property() requires the * connector to have a state. */ if (connector->funcs->reset) @@ -1199,10 +1206,34 @@ static const u32 dp_colorspaces = BIT(DRM_MODE_COLORIMETRY_SYCC_601) | BIT(DRM_MODE_COLORIMETRY_OPYCC_601) | BIT(DRM_MODE_COLORIMETRY_BT2020_CYCC) | BIT(DRM_MODE_COLORIMETRY_BT2020_YCC); +static const char * const output_format_str[] = { + [HDMI_COLORSPACE_RGB] = "RGB", + [HDMI_COLORSPACE_YUV420] = "YUV 4:2:0", + [HDMI_COLORSPACE_YUV422] = "YUV 4:2:2", + [HDMI_COLORSPACE_YUV444] = "YUV 4:4:4", +}; + +/* + * drm_hdmi_connector_get_output_format_name() - Return a string for HDMI connector output format + * @fmt: Output format to compute name of + * + * Returns: the name of the output format, or NULL if the type is not + * valid. + */ +const char * +drm_hdmi_connector_get_output_format_name(enum hdmi_colorspace fmt) +{ + if (fmt >= ARRAY_SIZE(output_format_str)) + return NULL; + + return output_format_str[fmt]; +} +EXPORT_SYMBOL(drm_hdmi_connector_get_output_format_name); + /** * DOC: standard connector properties * * DRM connectors have a few standardized properties: * 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 5d1a1dbff433..a129ea5f014c 100644 --- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c @@ -145,10 +145,11 @@ static const struct drm_connector_funcs dummy_connector_funcs = { }; static struct drm_atomic_helper_connector_hdmi_priv * drm_atomic_helper_connector_hdmi_init(struct kunit *test, + unsigned int formats, unsigned int max_bpc) { struct drm_atomic_helper_connector_hdmi_priv *priv; struct drm_connector *conn; struct drm_encoder *enc; @@ -188,10 +189,11 @@ drm_atomic_helper_connector_hdmi_init(struct kunit *test, conn = &priv->connector; ret = drmm_connector_hdmi_init(drm, conn, &dummy_connector_funcs, DRM_MODE_CONNECTOR_HDMIA, NULL, + formats, max_bpc); KUNIT_ASSERT_EQ(test, ret, 0); drm_connector_helper_add(conn, &dummy_connector_helper_funcs); drm_connector_attach_encoder(conn, enc); @@ -223,11 +225,13 @@ static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test) struct drm_connector *conn; struct drm_device *drm; struct drm_crtc *crtc; int ret; - priv = drm_atomic_helper_connector_hdmi_init(test, 10); + priv = drm_atomic_helper_connector_hdmi_init(test, + BIT(HDMI_COLORSPACE_RGB), + 10); KUNIT_ASSERT_NOT_NULL(test, priv); ctx = drm_kunit_helper_acquire_ctx_alloc(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); @@ -290,11 +294,13 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test) struct drm_connector *conn; struct drm_device *drm; struct drm_crtc *crtc; int ret; - priv = drm_atomic_helper_connector_hdmi_init(test, 10); + priv = drm_atomic_helper_connector_hdmi_init(test, + BIT(HDMI_COLORSPACE_RGB), + 10); KUNIT_ASSERT_NOT_NULL(test, priv); ctx = drm_kunit_helper_acquire_ctx_alloc(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); @@ -359,11 +365,13 @@ static void drm_test_check_bpc_8_value(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; struct drm_connector_state *conn_state; struct drm_connector *conn; - priv = drm_atomic_helper_connector_hdmi_init(test, 8); + priv = drm_atomic_helper_connector_hdmi_init(test, + BIT(HDMI_COLORSPACE_RGB), + 8); KUNIT_ASSERT_NOT_NULL(test, priv); conn = &priv->connector; conn_state = conn->state; KUNIT_EXPECT_EQ(test, conn_state->max_bpc, 8); @@ -381,11 +389,13 @@ static void drm_test_check_bpc_10_value(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; struct drm_connector_state *conn_state; struct drm_connector *conn; - priv = drm_atomic_helper_connector_hdmi_init(test, 10); + priv = drm_atomic_helper_connector_hdmi_init(test, + BIT(HDMI_COLORSPACE_RGB), + 10); KUNIT_ASSERT_NOT_NULL(test, priv); conn = &priv->connector; conn_state = conn->state; KUNIT_EXPECT_EQ(test, conn_state->max_bpc, 10); @@ -403,11 +413,13 @@ static void drm_test_check_bpc_12_value(struct kunit *test) { struct drm_atomic_helper_connector_hdmi_priv *priv; struct drm_connector_state *conn_state; struct drm_connector *conn; - priv = drm_atomic_helper_connector_hdmi_init(test, 12); + priv = drm_atomic_helper_connector_hdmi_init(test, + BIT(HDMI_COLORSPACE_RGB), + 12); KUNIT_ASSERT_NOT_NULL(test, priv); conn = &priv->connector; conn_state = conn->state; KUNIT_EXPECT_EQ(test, conn_state->max_bpc, 12); diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c index 2519b91de95e..9589867bdf7c 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -185,10 +185,11 @@ static void drm_test_connector_hdmi_init_valid(struct kunit *test) ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, &dummy_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, + BIT(HDMI_COLORSPACE_RGB), 8); KUNIT_EXPECT_EQ(test, ret, 0); } /* @@ -202,10 +203,11 @@ static void drm_test_connector_hdmi_init_null_ddc(struct kunit *test) ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, &dummy_funcs, DRM_MODE_CONNECTOR_HDMIA, NULL, + BIT(HDMI_COLORSPACE_RGB), 8); KUNIT_EXPECT_EQ(test, ret, 0); } /* @@ -219,10 +221,11 @@ static void drm_test_connector_hdmi_init_bpc_invalid(struct kunit *test) ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, &dummy_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, + BIT(HDMI_COLORSPACE_RGB), 9); KUNIT_EXPECT_LT(test, ret, 0); } /* @@ -236,10 +239,11 @@ static void drm_test_connector_hdmi_init_bpc_null(struct kunit *test) ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, &dummy_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, + BIT(HDMI_COLORSPACE_RGB), 0); KUNIT_EXPECT_LT(test, ret, 0); } /* @@ -257,10 +261,11 @@ static void drm_test_connector_hdmi_init_bpc_8(struct kunit *test) ret = drmm_connector_hdmi_init(&priv->drm, connector, &dummy_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, + BIT(HDMI_COLORSPACE_RGB), 8); KUNIT_EXPECT_EQ(test, ret, 0); prop = connector->max_bpc_property; KUNIT_ASSERT_NOT_NULL(test, prop); @@ -290,10 +295,11 @@ static void drm_test_connector_hdmi_init_bpc_10(struct kunit *test) ret = drmm_connector_hdmi_init(&priv->drm, connector, &dummy_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, + BIT(HDMI_COLORSPACE_RGB), 10); KUNIT_EXPECT_EQ(test, ret, 0); prop = connector->max_bpc_property; KUNIT_ASSERT_NOT_NULL(test, prop); @@ -323,10 +329,11 @@ static void drm_test_connector_hdmi_init_bpc_12(struct kunit *test) ret = drmm_connector_hdmi_init(&priv->drm, connector, &dummy_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, + BIT(HDMI_COLORSPACE_RGB), 12); KUNIT_EXPECT_EQ(test, ret, 0); prop = connector->max_bpc_property; KUNIT_ASSERT_NOT_NULL(test, prop); @@ -353,10 +360,11 @@ static void drm_test_connector_hdmi_init_type_valid(struct kunit *test) ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, &dummy_funcs, connector_type, &priv->ddc, + BIT(HDMI_COLORSPACE_RGB), 8); KUNIT_EXPECT_EQ(test, ret, 0); } static const unsigned int drm_connector_hdmi_init_type_valid_tests[] = { @@ -385,10 +393,11 @@ static void drm_test_connector_hdmi_init_type_invalid(struct kunit *test) ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, &dummy_funcs, connector_type, &priv->ddc, + BIT(HDMI_COLORSPACE_RGB), 8); KUNIT_EXPECT_LT(test, ret, 0); } static const unsigned int drm_connector_hdmi_init_type_invalid_tests[] = { diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index d4d2ae15bc1e..29883e6f8e50 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -366,10 +366,13 @@ enum drm_panel_orientation { DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP, DRM_MODE_PANEL_ORIENTATION_LEFT_UP, DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, }; +const char * +drm_hdmi_connector_get_output_format_name(enum hdmi_colorspace fmt); + /** * struct drm_monitor_range_info - Panel's Monitor range in EDID for * &drm_display_info * * This struct is used to store a frequency range supported by panel @@ -1039,10 +1042,15 @@ struct drm_connector_state { struct { /** * @output_bpc: Bits per color channel to output. */ unsigned int output_bpc; + + /** + * @output_format: Pixel format to output in. + */ + enum hdmi_colorspace output_format; } hdmi; }; /** * struct drm_connector_funcs - control connectors on a given device @@ -1900,10 +1908,21 @@ struct drm_connector { */ struct llist_node free_node; /** @hdr_sink_metadata: HDR Metadata Information read from sink */ struct hdr_sink_metadata hdr_sink_metadata; + + /** + * @hdmi: HDMI-related variable and properties. + */ + struct { + /** + * @supported_formats: Bitmask of @hdmi_colorspace + * supported by the controller. + */ + unsigned long supported_formats; + } hdmi; }; #define obj_to_connector(x) container_of(x, struct drm_connector, base) int drm_connector_init(struct drm_device *dev, @@ -1923,10 +1942,11 @@ 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, 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); int drm_connector_register(struct drm_connector *connector); void drm_connector_unregister(struct drm_connector *connector); int drm_connector_attach_encoder(struct drm_connector *connector, From patchwork Mon Mar 11 14:49:35 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588836 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 AD7DBC5475B for ; Mon, 11 Mar 2024 14:50:26 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 8258F112216; Mon, 11 Mar 2024 14:50:25 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="trmy+GbF"; 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 3FBEE10FCF7 for ; Mon, 11 Mar 2024 14:50:22 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id B0CD660DD7; Mon, 11 Mar 2024 14:50:21 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0FC80C43394; Mon, 11 Mar 2024 14:50:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168621; bh=Wd4dBu3fwcD8y9aL4WdMeBIO3hDwDnCR78xMD9gMtIA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=trmy+GbF184H1SWVQktCTjdZG73tifU7aG7Rl2DAjFCmP0XqGD5sZa5w0uEtAjf4M P+t0XBjnAVcP2eOvXfC2Jr86w3cOVNIrrgBLo74LyStkdHSENlYA008NtPj2sm4fos 2eoPey+g+Df8Q1yNsH+InNCRo9uCU1mqyOR6QD9ZPXv2uQzOBWTT4f7P2+NB5hPsiR i2mssdNSkNNL8GNdqp4ycZnkEUgpURR93kHhW7k+FwQjxvB638BRJssKom1o8/k2yE 0RkgVm9LP5tEaJg6ZIg5MG3eyb6lsux6rjVH7GCwCKPAC/v+gnCwP53hoIytTIBatF KqL6b3dCJhi2A== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:35 +0100 Subject: [PATCH v9 07/27] drm/tests: Add output formats tests MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-7-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=8032; i=mripard@kernel.org; h=from:subject:message-id; bh=Wd4dBu3fwcD8y9aL4WdMeBIO3hDwDnCR78xMD9gMtIA=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpTgezPixwvOP4vZqrT8tNmfV/xj9axFfMMXsd0PL2 pUTCxbN6ShlYRDjYpAVU2SJETZfEndq1utONr55MHNYmUCGMHBxCsBEnJcy/Gb9JXFtkp2bgsaj piVWBYJB6TtK9E+rb5+7nbtpgt+i/v0M/3ReNsxapjurZk6PeHD6gwcHxdlX8e4XuXeiJbp2Qfa lOFYA 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" Now that we track the HDMI output format as part of the connector state, let's add a few tests to make sure it works as expected. Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard --- .../gpu/drm/tests/drm_atomic_state_helper_test.c | 32 +++++++ drivers/gpu/drm/tests/drm_connector_test.c | 99 +++++++++++++++++++++- 2 files changed, 130 insertions(+), 1 deletion(-) 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 a129ea5f014c..1c3dd8a98fb0 100644 --- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c @@ -345,10 +345,19 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test) } static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = { KUNIT_CASE(drm_test_check_output_bpc_crtc_mode_changed), KUNIT_CASE(drm_test_check_output_bpc_crtc_mode_not_changed), + /* + * TODO: We should have tests to check that a change in the + * format triggers a CRTC mode change just like we do for the + * RGB Quantization and BPC. + * + * However, we don't have any way to control which format gets + * picked up aside from changing the BPC or mode which would + * already trigger a mode change. + */ { } }; static struct kunit_suite drm_atomic_helper_connector_hdmi_check_test_suite = { .name = "drm_atomic_helper_connector_hdmi_check", @@ -425,14 +434,37 @@ static void drm_test_check_bpc_12_value(struct kunit *test) KUNIT_EXPECT_EQ(test, conn_state->max_bpc, 12); KUNIT_EXPECT_EQ(test, conn_state->max_requested_bpc, 12); KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 0); } +/* + * Test that the value of the output format property out of reset is set + * to RGB, even if the driver supports more than that. + */ +static void drm_test_check_format_value(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_connector_state *conn_state; + struct drm_connector *conn; + + priv = drm_atomic_helper_connector_hdmi_init(test, + BIT(HDMI_COLORSPACE_RGB) | + BIT(HDMI_COLORSPACE_YUV422) | + BIT(HDMI_COLORSPACE_YUV444), + 8); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + conn_state = conn->state; + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); +} + static struct kunit_case drm_atomic_helper_connector_hdmi_reset_tests[] = { KUNIT_CASE(drm_test_check_bpc_8_value), KUNIT_CASE(drm_test_check_bpc_10_value), KUNIT_CASE(drm_test_check_bpc_12_value), + KUNIT_CASE(drm_test_check_format_value), { } }; static struct kunit_suite drm_atomic_helper_connector_hdmi_reset_test_suite = { .name = "drm_atomic_helper_connector_hdmi_reset", diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c index 9589867bdf7c..72f22ec951d6 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -346,10 +346,46 @@ static void drm_test_connector_hdmi_init_bpc_12(struct kunit *test) prop = priv->drm.mode_config.hdr_output_metadata_property; KUNIT_ASSERT_NOT_NULL(test, prop); KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id)); } +/* + * Test that the registration of an HDMI connector with no supported + * format fails. + */ +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, + DRM_MODE_CONNECTOR_HDMIA, + &priv->ddc, + 0, + 8); + KUNIT_EXPECT_LT(test, ret, 0); +} + +/* + * Test that the registration of an HDMI connector not listing RGB as a + * supported format fails. + */ +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, + DRM_MODE_CONNECTOR_HDMIA, + &priv->ddc, + BIT(HDMI_COLORSPACE_YUV422), + 8); + KUNIT_EXPECT_LT(test, ret, 0); +} + /* * Test that the registration of an HDMI connector with an HDMI * connector type succeeds. */ static void drm_test_connector_hdmi_init_type_valid(struct kunit *test) @@ -431,10 +467,12 @@ static struct kunit_case drmm_connector_hdmi_init_tests[] = { KUNIT_CASE(drm_test_connector_hdmi_init_bpc_8), KUNIT_CASE(drm_test_connector_hdmi_init_bpc_10), KUNIT_CASE(drm_test_connector_hdmi_init_bpc_12), KUNIT_CASE(drm_test_connector_hdmi_init_bpc_invalid), KUNIT_CASE(drm_test_connector_hdmi_init_bpc_null), + KUNIT_CASE(drm_test_connector_hdmi_init_formats_empty), + KUNIT_CASE(drm_test_connector_hdmi_init_formats_no_rgb), KUNIT_CASE(drm_test_connector_hdmi_init_null_ddc), 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, drm_connector_hdmi_init_type_invalid_gen_params), @@ -508,13 +546,72 @@ static struct kunit_case drm_get_tv_mode_from_name_tests[] = { static struct kunit_suite drm_get_tv_mode_from_name_test_suite = { .name = "drm_get_tv_mode_from_name", .test_cases = drm_get_tv_mode_from_name_tests, }; +struct drm_hdmi_connector_get_output_format_name_test { + unsigned int kind; + const char *expected_name; +}; + +#define OUTPUT_FORMAT_TEST(_kind, _name) \ + { \ + .kind = _kind, \ + .expected_name = _name, \ + } + +static void drm_test_drm_hdmi_connector_get_output_format_name(struct kunit *test) +{ + const struct drm_hdmi_connector_get_output_format_name_test *params = + test->param_value; + + KUNIT_EXPECT_STREQ(test, + drm_hdmi_connector_get_output_format_name(params->kind), + params->expected_name); +} + +static const +struct drm_hdmi_connector_get_output_format_name_test +drm_hdmi_connector_get_output_format_name_valid_tests[] = { + OUTPUT_FORMAT_TEST(HDMI_COLORSPACE_RGB, "RGB"), + OUTPUT_FORMAT_TEST(HDMI_COLORSPACE_YUV420, "YUV 4:2:0"), + OUTPUT_FORMAT_TEST(HDMI_COLORSPACE_YUV422, "YUV 4:2:2"), + OUTPUT_FORMAT_TEST(HDMI_COLORSPACE_YUV444, "YUV 4:4:4"), +}; + +static void +drm_hdmi_connector_get_output_format_name_valid_desc(const struct drm_hdmi_connector_get_output_format_name_test *t, + char *desc) +{ + sprintf(desc, "%s", t->expected_name); +} + +KUNIT_ARRAY_PARAM(drm_hdmi_connector_get_output_format_name_valid, + drm_hdmi_connector_get_output_format_name_valid_tests, + drm_hdmi_connector_get_output_format_name_valid_desc); + +static void drm_test_drm_hdmi_connector_get_output_format_name_invalid(struct kunit *test) +{ + KUNIT_EXPECT_NULL(test, drm_hdmi_connector_get_output_format_name(4)); +}; + +static struct kunit_case drm_hdmi_connector_get_output_format_name_tests[] = { + KUNIT_CASE_PARAM(drm_test_drm_hdmi_connector_get_output_format_name, + drm_hdmi_connector_get_output_format_name_valid_gen_params), + KUNIT_CASE(drm_test_drm_hdmi_connector_get_output_format_name_invalid), + { } +}; + +static struct kunit_suite drm_hdmi_connector_get_output_format_name_test_suite = { + .name = "drm_hdmi_connector_get_output_format_name", + .test_cases = drm_hdmi_connector_get_output_format_name_tests, +}; + kunit_test_suites( &drmm_connector_hdmi_init_test_suite, &drmm_connector_init_test_suite, - &drm_get_tv_mode_from_name_test_suite + &drm_get_tv_mode_from_name_test_suite, + &drm_hdmi_connector_get_output_format_name_test_suite ); MODULE_AUTHOR("Maxime Ripard "); MODULE_LICENSE("GPL"); From patchwork Mon Mar 11 14:49:36 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588837 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 77E5FC54E58 for ; Mon, 11 Mar 2024 14:50:28 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 8AA6F10FF0D; Mon, 11 Mar 2024 14:50:27 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="u6o8XtWK"; 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 5E00A10FF0D for ; Mon, 11 Mar 2024 14:50:25 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id AF1CD60EB9; Mon, 11 Mar 2024 14:50:24 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 11348C433C7; Mon, 11 Mar 2024 14:50:23 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168624; bh=knNLcwVqg8jv1l/rgrHhTRsj59b8/zn3xoeOIeIyJfk=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=u6o8XtWKsw+awRKIEGn4RU6qA/u4W7Qtp9DPdd4EZuGIpVzmSBgOPComxSwR2ujt7 pypiavm6b26rRrWiRXGW9gKKVtpAhDRaFPuTsvikjzxjVbKERmv13AgOG8iVF0DX9V Ewc/CpxkoXX4C8o/mA9GMfXBqXhDvbr+12iopqGfyRKRGlKQwyc3YOHK9SpQ65hjX8 nUElpAbXgl8ijL8QdPxVgC/cq0epUCtvmDbEo9nVBxHRtXFssiX0+qmzHPb1qLRCPV DDrOaEgjHN4272xEEukvQw9baYvcUP/jEcd9+Jtwf9KnXC8LNpI1g7c+T+e+deHlRS zLSm3bTcSr/Sw== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:36 +0100 Subject: [PATCH v9 08/27] drm/connector: hdmi: Add HDMI compute clock helper MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-8-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=4451; i=mripard@kernel.org; h=from:subject:message-id; bh=knNLcwVqg8jv1l/rgrHhTRsj59b8/zn3xoeOIeIyJfk=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpTgZSz80LG8xWlZ2Yu9yF33WgL1LiheocXbmc03I2 ye/spOxo5SFQYyLQVZMkSVG2HxJ3KlZrzvZ+ObBzGFlAhnCwMUpABNJO8DI0HKEN1tujYrPE9HD 3ewSz3fvt70z8+q2tIDF6yK8PSLXmTH8Fd5X6n6SKW2zfUjGhVCOmNJTzw9wdbFcfzr/d5/pli+ fOQE= 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" A lot of HDMI drivers have some variation of the formula to calculate the TMDS character rate from a mode, but few of them actually take all parameters into account. Let's create a helper to provide that rate taking all parameters into account. Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_connector.c | 71 +++++++++++++++++++++++++++++++++++++++++ include/drm/drm_connector.h | 5 +++ 2 files changed, 76 insertions(+) diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index b629c8e990f4..8cc1332f11c2 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -2895,10 +2895,81 @@ void drm_connector_update_privacy_screen(const struct drm_connector_state *conne /* The hw_state property value may have changed, update it. */ drm_connector_update_privacy_screen_properties(connector, false); } EXPORT_SYMBOL(drm_connector_update_privacy_screen); +/** + * drm_connector_hdmi_compute_mode_clock() - Computes the TMDS Character Rate + * @mode: Display mode to compute the clock for + * @bpc: Bits per character + * @fmt: Output Pixel Format used + * + * Returns the TMDS Character Rate for a given mode, bpc count and output format. + * + * RETURNS: + * The TMDS Character Rate, in Hertz, or 0 on error. + */ +unsigned long long +drm_connector_hdmi_compute_mode_clock(const struct drm_display_mode *mode, + unsigned int bpc, + enum hdmi_colorspace fmt) +{ + unsigned long long clock = mode->clock * 1000ULL; + unsigned int vic = drm_match_cea_mode(mode); + + /* + * CTA-861-G Spec, section 5.4 - Color Coding and Quantization + * mandates that VIC 1 always uses 8 bpc. + */ + if (vic == 1 && bpc != 8) + return 0; + + /* + * HDMI 2.0 Spec, section 7.1 - YCbCr 4:2:0 Pixel Encoding + * specifies that YUV420 encoding is only available for those + * VICs. + */ + if (fmt == HDMI_COLORSPACE_YUV420 && + !(vic == 96 || vic == 97 || vic == 101 || + vic == 102 || vic == 106 || vic == 107)) + return 0; + + if (fmt == HDMI_COLORSPACE_YUV422) { + /* + * HDMI 1.4b Spec, section 6.2.3 - Pixel Encoding Requirements + * specifies that YUV422 is 36-bit only. + */ + if (bpc != 12) + return 0; + + /* + * HDMI 1.0 Spec, section 6.5 - Pixel Encoding + * specifies that YUV422 requires two 12-bits components per + * pixel clock, which is equivalent in our calculation to three + * 8-bits components + */ + bpc = 8; + } + + /* + * HDMI 2.0 Spec, Section 7.1 - YCbCr 4:2:0 Pixel Encoding + * specifies that YUV420 encoding is carried at a TMDS Character Rate + * equal to half the pixel clock rate. + */ + if (fmt == HDMI_COLORSPACE_YUV420) + clock = clock / 2; + + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + clock = clock * 2; + + clock = clock * bpc; + do_div(clock, 8); + + return clock; +} +EXPORT_SYMBOL(drm_connector_hdmi_compute_mode_clock); + int drm_connector_set_obj_prop(struct drm_mode_object *obj, struct drm_property *property, uint64_t value) { int ret = -EINVAL; diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 29883e6f8e50..a859ad7ee04b 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; @@ -2092,10 +2093,14 @@ int drm_connector_attach_max_bpc_property(struct drm_connector *connector, void drm_connector_create_privacy_screen_properties(struct drm_connector *conn); void drm_connector_attach_privacy_screen_properties(struct drm_connector *conn); void drm_connector_attach_privacy_screen_provider( struct drm_connector *connector, struct drm_privacy_screen *priv); void drm_connector_update_privacy_screen(const struct drm_connector_state *connector_state); +unsigned long long +drm_connector_hdmi_compute_mode_clock(const struct drm_display_mode *mode, + unsigned int bpc, + enum hdmi_colorspace fmt); /** * struct drm_tile_group - Tile group metadata * @refcount: reference count * @dev: DRM device From patchwork Mon Mar 11 14:49:37 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588840 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 2AA6AC5475B for ; Mon, 11 Mar 2024 14:50:39 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 092D2112AA7; Mon, 11 Mar 2024 14:50:38 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="CrrdwVX4"; dkim-atps=neutral Received: from sin.source.kernel.org (sin.source.kernel.org [145.40.73.55]) by gabe.freedesktop.org (Postfix) with ESMTPS id 11E49112720 for ; Mon, 11 Mar 2024 14:50:33 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sin.source.kernel.org (Postfix) with ESMTP id D7594CE1116; Mon, 11 Mar 2024 14:50:27 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id B3002C433F1; Mon, 11 Mar 2024 14:50:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168627; bh=mDmu05P60hTHdrx0GPadTWyad21c+S0aaMLwlQPO8EU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=CrrdwVX4EGy7pR69YD/jIRf3w0gPfiV7wZ18Et4rla9RzE20G9KXwhE7wTz/GNsQ+ jF6rDe6js46rTQSBBDr3tpy0VJZFJ29RGQwqJ1TM4QnF9RC9BhBztQt3kyuiV8QPdz j60Po4saoiasHFbNjxaCPLGlh86LXl47KfSbJrxzGWlinpXhpbYDx3c7i9LTMYxrX7 foxgjB6r114QYkYDFetRtn8JNQFOM8TqH5xZTfdcaj5GSQWSOU408lmnzA3wGvG4Qz I0Zv6y045+ag2IF1q+VN9aMm9C0Q3Pr3afRdjEP2MJa0+hrzHo/SKQXrLBpa71Dnom Ss6ooxoTbVY1Q== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:37 +0100 Subject: [PATCH v9 09/27] drm/tests: Add HDMI TDMS character rate tests MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-9-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=13448; i=mripard@kernel.org; h=from:subject:message-id; bh=mDmu05P60hTHdrx0GPadTWyad21c+S0aaMLwlQPO8EU=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpbhW7o1pv3HP+NLMST46CW+2vLo+N9jf5mkEUwdnR OuRiH3TOkpZGMS4GGTFFFlihM2XxJ2a9bqTjW8ezBxWJpAhDFycAjAR0QxGhvOzIvrmb/t1tDHd 18lsf0zsLaPMV+u/zIxQniJkvvnFr0RGhlbjaA6+nmXSG+PudUmqvpdJXD21/pv/5DVrtVP2eui U8wIA 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" The previous patch added an helper to compute the TMDS character rate on an HDMI connector. Let's add a few tests to make sure it works as expected. Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard --- drivers/gpu/drm/tests/drm_connector_test.c | 323 +++++++++++++++++++++++++++++ 1 file changed, 323 insertions(+) diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c index 72f22ec951d6..4d1ac744a844 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -6,11 +6,13 @@ #include #include #include #include +#include #include +#include #include #include "../drm_crtc_internal.h" @@ -604,13 +606,334 @@ static struct kunit_case drm_hdmi_connector_get_output_format_name_tests[] = { static struct kunit_suite drm_hdmi_connector_get_output_format_name_test_suite = { .name = "drm_hdmi_connector_get_output_format_name", .test_cases = drm_hdmi_connector_get_output_format_name_tests, }; +/* + * Test that for a given mode, with 8bpc and an RGB output the TMDS + * character rate is equal to the mode pixel clock. + */ +static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const struct drm_display_mode *mode; + unsigned long long rate; + struct drm_device *drm = &priv->drm; + + mode = drm_display_mode_from_cea_vic(drm, 16); + KUNIT_ASSERT_NOT_NULL(test, mode); + + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); + + rate = drm_connector_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB); + KUNIT_ASSERT_GT(test, rate, 0); + KUNIT_EXPECT_EQ(test, mode->clock * 1000ULL, rate); +} + +/* + * Test that for a given mode, with 10bpc and an RGB output the TMDS + * character rate is equal to 1.25 times the mode pixel clock. + */ +static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb_10bpc(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const struct drm_display_mode *mode; + unsigned long long rate; + struct drm_device *drm = &priv->drm; + + mode = drm_display_mode_from_cea_vic(drm, 16); + KUNIT_ASSERT_NOT_NULL(test, mode); + + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); + + rate = drm_connector_hdmi_compute_mode_clock(mode, 10, HDMI_COLORSPACE_RGB); + KUNIT_ASSERT_GT(test, rate, 0); + KUNIT_EXPECT_EQ(test, mode->clock * 1250, rate); +} + +/* + * Test that for the VIC-1 mode, with 10bpc and an RGB output the TMDS + * character rate computation fails. + */ +static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb_10bpc_vic_1(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const struct drm_display_mode *mode; + unsigned long long rate; + struct drm_device *drm = &priv->drm; + + mode = drm_display_mode_from_cea_vic(drm, 1); + KUNIT_ASSERT_NOT_NULL(test, mode); + + rate = drm_connector_hdmi_compute_mode_clock(mode, 10, HDMI_COLORSPACE_RGB); + KUNIT_EXPECT_EQ(test, rate, 0); +} + +/* + * Test that for a given mode, with 12bpc and an RGB output the TMDS + * character rate is equal to 1.5 times the mode pixel clock. + */ +static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb_12bpc(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const struct drm_display_mode *mode; + unsigned long long rate; + struct drm_device *drm = &priv->drm; + + mode = drm_display_mode_from_cea_vic(drm, 16); + KUNIT_ASSERT_NOT_NULL(test, mode); + + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); + + rate = drm_connector_hdmi_compute_mode_clock(mode, 12, HDMI_COLORSPACE_RGB); + KUNIT_ASSERT_GT(test, rate, 0); + KUNIT_EXPECT_EQ(test, mode->clock * 1500, rate); +} + +/* + * Test that for the VIC-1 mode, with 12bpc and an RGB output the TMDS + * character rate computation fails. + */ +static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb_12bpc_vic_1(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const struct drm_display_mode *mode; + unsigned long long rate; + struct drm_device *drm = &priv->drm; + + mode = drm_display_mode_from_cea_vic(drm, 1); + KUNIT_ASSERT_NOT_NULL(test, mode); + + rate = drm_connector_hdmi_compute_mode_clock(mode, 12, HDMI_COLORSPACE_RGB); + KUNIT_EXPECT_EQ(test, rate, 0); +} + +/* + * Test that for a mode with the pixel repetition flag, the TMDS + * character rate is indeed double the mode pixel clock. + */ +static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb_double(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const struct drm_display_mode *mode; + unsigned long long rate; + struct drm_device *drm = &priv->drm; + + mode = drm_display_mode_from_cea_vic(drm, 6); + KUNIT_ASSERT_NOT_NULL(test, mode); + + KUNIT_ASSERT_TRUE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); + + rate = drm_connector_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB); + KUNIT_ASSERT_GT(test, rate, 0); + KUNIT_EXPECT_EQ(test, (mode->clock * 1000ULL) * 2, rate); +} + +/* + * Test that the TMDS character rate computation for the VIC modes + * explicitly listed in the spec as supporting YUV420 succeed and return + * half the mode pixel clock. + */ +static void drm_test_connector_hdmi_compute_mode_clock_yuv420_valid(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const struct drm_display_mode *mode; + struct drm_device *drm = &priv->drm; + unsigned long long rate; + unsigned int vic = *(unsigned int *)test->param_value; + + mode = drm_display_mode_from_cea_vic(drm, vic); + KUNIT_ASSERT_NOT_NULL(test, mode); + + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); + + rate = drm_connector_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_YUV420); + KUNIT_ASSERT_GT(test, rate, 0); + KUNIT_EXPECT_EQ(test, (mode->clock * 1000ULL) / 2, rate); +} + +static const unsigned int drm_connector_hdmi_compute_mode_clock_yuv420_vic_valid_tests[] = { + 96, 97, 101, 102, 106, 107, +}; + +static void drm_connector_hdmi_compute_mode_clock_yuv420_vic_desc(const unsigned int *vic, char *desc) +{ + sprintf(desc, "VIC %u", *vic); +} + +KUNIT_ARRAY_PARAM(drm_connector_hdmi_compute_mode_clock_yuv420_valid, + drm_connector_hdmi_compute_mode_clock_yuv420_vic_valid_tests, + drm_connector_hdmi_compute_mode_clock_yuv420_vic_desc); + +/* + * Test that trying to compute the TMDS char rate with the YUV420 format + * for a mode that doesn't support the YUV420 encoding returns an error. + * + * TODO: We should probably test this with all the VIC but the + * explicitly supported ones. Since the list of VIC is quite long and + * not linear, the best way to support it at the moment would be to + * create a custom gen_params function that would only return valid + * VICs. At the moment, that function expects to get a pointer back + * however, and compilers don't really like casting between integer and + * pointers. + */ +static void drm_test_connector_hdmi_compute_mode_clock_yuv420_invalid(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const struct drm_display_mode *mode; + struct drm_device *drm = &priv->drm; + unsigned long long rate; + + mode = drm_display_mode_from_cea_vic(drm, 42); + KUNIT_ASSERT_NOT_NULL(test, mode); + + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); + + rate = drm_connector_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_YUV420); + KUNIT_EXPECT_EQ(test, rate, 0); +} + +/* + * Test that for a given mode listed supporting it and an YUV420 output + * with 10bpc, the TMDS character rate is equal to 0.625 times the mode + * pixel clock. + */ +static void drm_test_connector_hdmi_compute_mode_clock_yuv420_10_bpc(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const struct drm_display_mode *mode; + struct drm_device *drm = &priv->drm; + unsigned int vic = + drm_connector_hdmi_compute_mode_clock_yuv420_vic_valid_tests[0]; + unsigned long long rate; + + mode = drm_display_mode_from_cea_vic(drm, vic); + KUNIT_ASSERT_NOT_NULL(test, mode); + + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); + + rate = drm_connector_hdmi_compute_mode_clock(mode, 10, HDMI_COLORSPACE_YUV420); + KUNIT_ASSERT_GT(test, rate, 0); + + KUNIT_EXPECT_EQ(test, mode->clock * 625, rate); +} + +/* + * Test that for a given mode listed supporting it and an YUV420 output + * with 12bpc, the TMDS character rate is equal to 0.75 times the mode + * pixel clock. + */ +static void drm_test_connector_hdmi_compute_mode_clock_yuv420_12_bpc(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const struct drm_display_mode *mode; + struct drm_device *drm = &priv->drm; + unsigned int vic = + drm_connector_hdmi_compute_mode_clock_yuv420_vic_valid_tests[0]; + unsigned long long rate; + + mode = drm_display_mode_from_cea_vic(drm, vic); + KUNIT_ASSERT_NOT_NULL(test, mode); + + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); + + rate = drm_connector_hdmi_compute_mode_clock(mode, 12, HDMI_COLORSPACE_YUV420); + KUNIT_ASSERT_GT(test, rate, 0); + + KUNIT_EXPECT_EQ(test, mode->clock * 750, rate); +} + +/* + * Test that for a given mode, the computation of the TMDS character + * rate with 8bpc and a YUV422 output fails. + */ +static void drm_test_connector_hdmi_compute_mode_clock_yuv422_8_bpc(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const struct drm_display_mode *mode; + struct drm_device *drm = &priv->drm; + unsigned long long rate; + + mode = drm_display_mode_from_cea_vic(drm, 16); + KUNIT_ASSERT_NOT_NULL(test, mode); + + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); + + rate = drm_connector_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_YUV422); + KUNIT_EXPECT_EQ(test, rate, 0); +} + +/* + * Test that for a given mode, the computation of the TMDS character + * rate with 10bpc and a YUV422 output fails. + */ +static void drm_test_connector_hdmi_compute_mode_clock_yuv422_10_bpc(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const struct drm_display_mode *mode; + struct drm_device *drm = &priv->drm; + unsigned long long rate; + + mode = drm_display_mode_from_cea_vic(drm, 16); + KUNIT_ASSERT_NOT_NULL(test, mode); + + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); + + rate = drm_connector_hdmi_compute_mode_clock(mode, 10, HDMI_COLORSPACE_YUV422); + KUNIT_EXPECT_EQ(test, rate, 0); +} + +/* + * Test that for a given mode, the computation of the TMDS character + * rate with 12bpc and a YUV422 output succeeds and returns a rate equal + * to the mode pixel clock. + */ +static void drm_test_connector_hdmi_compute_mode_clock_yuv422_12_bpc(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const struct drm_display_mode *mode; + struct drm_device *drm = &priv->drm; + unsigned long long rate; + + mode = drm_display_mode_from_cea_vic(drm, 16); + KUNIT_ASSERT_NOT_NULL(test, mode); + + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); + + rate = drm_connector_hdmi_compute_mode_clock(mode, 12, HDMI_COLORSPACE_YUV422); + KUNIT_ASSERT_GT(test, rate, 0); + KUNIT_EXPECT_EQ(test, mode->clock * 1000, rate); +} + +static struct kunit_case drm_connector_hdmi_compute_mode_clock_tests[] = { + KUNIT_CASE(drm_test_drm_connector_hdmi_compute_mode_clock_rgb), + KUNIT_CASE(drm_test_drm_connector_hdmi_compute_mode_clock_rgb_10bpc), + KUNIT_CASE(drm_test_drm_connector_hdmi_compute_mode_clock_rgb_10bpc_vic_1), + KUNIT_CASE(drm_test_drm_connector_hdmi_compute_mode_clock_rgb_12bpc), + KUNIT_CASE(drm_test_drm_connector_hdmi_compute_mode_clock_rgb_12bpc_vic_1), + KUNIT_CASE(drm_test_drm_connector_hdmi_compute_mode_clock_rgb_double), + KUNIT_CASE_PARAM(drm_test_connector_hdmi_compute_mode_clock_yuv420_valid, + drm_connector_hdmi_compute_mode_clock_yuv420_valid_gen_params), + KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv420_invalid), + KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv420_10_bpc), + KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv420_12_bpc), + KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv422_8_bpc), + KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv422_10_bpc), + KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv422_12_bpc), + { } +}; + +static struct kunit_suite drm_connector_hdmi_compute_mode_clock_test_suite = { + .name = "drm_test_connector_hdmi_compute_mode_clock", + .init = drm_test_connector_init, + .test_cases = drm_connector_hdmi_compute_mode_clock_tests, +}; + kunit_test_suites( &drmm_connector_hdmi_init_test_suite, &drmm_connector_init_test_suite, + &drm_connector_hdmi_compute_mode_clock_test_suite, &drm_get_tv_mode_from_name_test_suite, &drm_hdmi_connector_get_output_format_name_test_suite ); MODULE_AUTHOR("Maxime Ripard "); From patchwork Mon Mar 11 14:49:38 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588838 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 DEAC6C54E58 for ; Mon, 11 Mar 2024 14:50:35 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 1CB541122DF; Mon, 11 Mar 2024 14:50:35 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="qMqTI5m6"; dkim-atps=neutral Received: from sin.source.kernel.org (sin.source.kernel.org [145.40.73.55]) by gabe.freedesktop.org (Postfix) with ESMTPS id 448C6112AA7 for ; Mon, 11 Mar 2024 14:50:33 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sin.source.kernel.org (Postfix) with ESMTP id F384DCE1122; Mon, 11 Mar 2024 14:50:30 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id B19F8C433C7; Mon, 11 Mar 2024 14:50:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168630; bh=m0lVuFZx7PIWgVWuOqTSo7KUYnJKghF2lT9iIlcwgRM=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=qMqTI5m6eXvGiRD1jQVtt8nRvoZk7PkDDXjZUG4X6gP7yOb3PKaLW9YbOdXVbjfSn VE4mjMCV1kvshaXljIllToNRmwvYPo+skIKMmtmYifTaBpmD/ykk3aUgjhIA0KYHaN 7LC9/TUgn728hX1/HQIsYqsyIhnF2x6cib0U1OdEjR6glqgvmHNGGKvoEIFnbB5Ezq sbzxHF2hR8ZgWHFbexdpl2cAMe3Oe0l65lroxqVTSLmOwaGSNAMEa0Y0027P5FuNzn SYAbJZWkiKTTw1TwOtbeZFqAny+ueAm2wqfmnV7t7danx3hX7EiGoxvCHJ1GEp5Sa5 v1g202yCHtpUg== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:38 +0100 Subject: [PATCH v9 10/27] drm/connector: hdmi: Calculate TMDS character rate MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-10-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=5859; i=mripard@kernel.org; h=from:subject:message-id; bh=m0lVuFZx7PIWgVWuOqTSo7KUYnJKghF2lT9iIlcwgRM=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpbi+VZ/RNmeQEa3tKG0yTpx7ve2o1fdr+/Zte92Q4 i7wyGxeRykLgxgXg6yYIkuMsPmSuFOzXney8c2DmcPKBDKEgYtTACZydBcjw0x2q81vYl7r/Ofy mrF39fLsxNWJs4OmZ0kuFTF3nLHsaSTDP8sbbm2K8ieniWrvLdqS39fx7aH6hetWd27HZEx9K1S 2nBkA 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 HDMI drivers have some code to calculate the TMDS character rate, usually to adjust an internal clock to match what the mode requires. Since the TMDS character rates mostly depends on the resolution, whether we need to repeat pixels or not, the bpc count and the format, we can now derive it from the HDMI connector state that stores all those infos and remove the duplication from drivers. Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic.c | 1 + drivers/gpu/drm/drm_atomic_state_helper.c | 66 ++++++++++++++++++++++ .../gpu/drm/tests/drm_atomic_state_helper_test.c | 3 + include/drm/drm_connector.h | 5 ++ 4 files changed, 75 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 8730137baa86..26f9e525c0a0 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1146,10 +1146,11 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) { drm_printf(p, "\toutput_bpc=%u\n", state->hdmi.output_bpc); drm_printf(p, "\toutput_format=%s\n", drm_hdmi_connector_get_output_format_name(state->hdmi.output_format)); + drm_printf(p, "\ttmds_char_rate=%llu\n", state->hdmi.tmds_char_rate); } if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) if (state->writeback_job && state->writeback_job->fb) drm_printf(p, "\tfb=%d\n", state->writeback_job->fb->base.id); diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index ae99765c45de..63a96c691460 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -638,10 +638,67 @@ int drm_atomic_helper_connector_tv_check(struct drm_connector *connector, return 0; } EXPORT_SYMBOL(drm_atomic_helper_connector_tv_check); +static const struct drm_display_mode * +connector_state_get_mode(const struct drm_connector_state *conn_state) +{ + struct drm_atomic_state *state; + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + + state = conn_state->state; + if (!state) + return NULL; + + crtc = conn_state->crtc; + if (!crtc) + return NULL; + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + if (!crtc_state) + return NULL; + + return &crtc_state->mode; +} + +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_display_info *info = &connector->display_info; + + if (info->max_tmds_clock && clock > info->max_tmds_clock * 1000) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static int +hdmi_compute_clock(const struct drm_connector *connector, + struct drm_connector_state *state, + const struct drm_display_mode *mode, + unsigned int bpc, enum hdmi_colorspace fmt) +{ + enum drm_mode_status status; + unsigned long long clock; + + clock = drm_connector_hdmi_compute_mode_clock(mode, bpc, fmt); + if (!clock) + return -EINVAL; + + status = hdmi_clock_valid(connector, mode, clock); + if (status != MODE_OK) + return -EINVAL; + + state->hdmi.tmds_char_rate = clock; + + return 0; +} + /** * drm_atomic_helper_connector_hdmi_check() - Helper to check HDMI connector atomic state * @connector: DRM Connector * @state: the DRM State object * @@ -657,10 +714,19 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector, { struct drm_connector_state *old_state = drm_atomic_get_old_connector_state(state, connector); struct drm_connector_state *new_state = drm_atomic_get_new_connector_state(state, connector); + const struct drm_display_mode *mode = + connector_state_get_mode(new_state); + int ret; + + ret = hdmi_compute_clock(connector, new_state, mode, + new_state->hdmi.output_bpc, + new_state->hdmi.output_format); + if (ret) + return ret; if (old_state->hdmi.output_bpc != new_state->hdmi.output_bpc || old_state->hdmi.output_format != new_state->hdmi.output_format) { struct drm_crtc *crtc = new_state->crtc; struct drm_crtc_state *crtc_state; 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 1c3dd8a98fb0..f2acbd4c216c 100644 --- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c @@ -68,10 +68,13 @@ static int light_up_connector(struct kunit *test, KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); conn_state = drm_atomic_get_connector_state(state, connector); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + conn_state->hdmi.output_bpc = connector->max_bpc; + conn_state->hdmi.output_format = HDMI_COLORSPACE_RGB; + ret = drm_atomic_set_crtc_for_connector(conn_state, crtc); KUNIT_EXPECT_EQ(test, ret, 0); crtc_state = drm_atomic_get_crtc_state(state, crtc); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index a859ad7ee04b..e3917ea93986 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1048,10 +1048,15 @@ struct drm_connector_state { /** * @output_format: Pixel format to output in. */ enum hdmi_colorspace output_format; + + /** + * @tmds_char_rate: TMDS Character Rate, in Hz. + */ + unsigned long long tmds_char_rate; } hdmi; }; /** * struct drm_connector_funcs - control connectors on a given device From patchwork Mon Mar 11 14:49:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588839 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 303A8C54E60 for ; Mon, 11 Mar 2024 14:50:38 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 7E664112720; Mon, 11 Mar 2024 14:50:37 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="d8qtx+EI"; dkim-atps=neutral Received: from sin.source.kernel.org (sin.source.kernel.org [145.40.73.55]) by gabe.freedesktop.org (Postfix) with ESMTPS id E4C72112720 for ; Mon, 11 Mar 2024 14:50:35 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sin.source.kernel.org (Postfix) with ESMTP id 8B99ACE0D55; Mon, 11 Mar 2024 14:50:33 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6670BC433F1; Mon, 11 Mar 2024 14:50:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168632; bh=Yj5r0webazNPn0wQGj9WKcYn3nt93Xj1D3pk/60CoKc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=d8qtx+EIpEimta9AHBzxLuWjd++DgIJch3vJ/rKsIQS645gjEwtyrtOLsZrUtltzl Doa4SCpGwCAs5fjAmbXoozb7d8P8H2Y/+X98ccQWA6g/WIJHL8uj8XOE+W+SQ1E2tb KMI3JScNuxh+fb4n6L/wPHC9oENOsZBdQTpP4vrRoT+buZxIteO1HvJ8WKFDQ9n4c/ cQss6MBfAXcGONc9X4UAp0W86y7QATalQg14JwzBdu4rXG2YF0pxHZyj2gSjb0NtvB GpNOK3SkLMgMe2mAdD3XuuxfvC5XxvotcGP6QY8+ROVtFIMlxCEW2RlpRS+Dyyd7xw xjB2DfagNgcsw== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:39 +0100 Subject: [PATCH v9 11/27] drm/tests: Add TDMS character rate connector state tests MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-11-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=17960; i=mripard@kernel.org; h=from:subject:message-id; bh=Yj5r0webazNPn0wQGj9WKcYn3nt93Xj1D3pk/60CoKc=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpbiZshfamtmW/a+tk92jI9mnyyvi8yxGwOnTxC07b s1lMunoKGVhEONikBVTZIkRNl8Sd2rW6042vnkwc1iZQIYwcHEKwEQEGhj+B8i/m7HRO7LcKfL6 TosfT82T13WuPcNz/+sR28rVW9VX5zEyLMiymCAX+CdZMepKetu825w5C0t3PpKKldSOf8W6iPE YPwA= 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" The previous patch stores in the connector state the expected TMDS character rate matching the configuration of the HDMI connector. Let's add a few tests to make sure it works as expected. Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard --- .../gpu/drm/tests/drm_atomic_state_helper_test.c | 166 ++++++++++++++++ drivers/gpu/drm/tests/drm_kunit_edid.h | 216 +++++++++++++++++++++ 2 files changed, 382 insertions(+) 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 f2acbd4c216c..dfed45b250a5 100644 --- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c @@ -345,13 +345,156 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test) crtc_state = drm_atomic_get_new_crtc_state(state, crtc); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); KUNIT_EXPECT_FALSE(test, crtc_state->mode_changed); } +/* + * Test that when doing a commit which would use RGB 8bpc, the TMDS + * clock rate stored in the connector state is equal to the mode clock + */ +static void drm_test_check_tmds_char_rate_rgb_8bpc(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_display_mode *preferred; + 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); + + conn = &priv->connector; + ret = set_connector_edid(test, conn, + test_edid_hdmi_1080p_rgb_max_200mhz, + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_200mhz)); + KUNIT_ASSERT_EQ(test, ret, 0); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + KUNIT_ASSERT_EQ(test, ret, 0); + + conn_state = conn->state; + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_bpc, 8); + KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1000); +} + +/* + * Test that when doing a commit which would use RGB 10bpc, the TMDS + * clock rate stored in the connector state is equal to 1.25 times the + * mode pixel clock + */ +static void drm_test_check_tmds_char_rate_rgb_10bpc(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_display_mode *preferred; + 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), + 10); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + ret = set_connector_edid(test, conn, + test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz, + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz)); + KUNIT_ASSERT_EQ(test, ret, 0); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + KUNIT_ASSERT_EQ(test, ret, 0); + + conn_state = conn->state; + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_bpc, 10); + KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1250); +} + +/* + * Test that when doing a commit which would use RGB 12bpc, the TMDS + * clock rate stored in the connector state is equal to 1.5 times the + * mode pixel clock + */ +static void drm_test_check_tmds_char_rate_rgb_12bpc(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_display_mode *preferred; + 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), + 12); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + ret = set_connector_edid(test, conn, + test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz, + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz)); + KUNIT_ASSERT_EQ(test, ret, 0); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + KUNIT_ASSERT_EQ(test, ret, 0); + + conn_state = conn->state; + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_bpc, 12); + KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1500); +} + static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = { 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), + KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_10bpc), + KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_12bpc), /* * TODO: We should have tests to check that a change in the * format triggers a CRTC mode change just like we do for the * RGB Quantization and BPC. * @@ -459,15 +602,38 @@ static void drm_test_check_format_value(struct kunit *test) conn = &priv->connector; conn_state = conn->state; KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); } +/* + * Test that the value of the output format property out of reset is set + * to 0, and will be computed at atomic_check time. + */ +static void drm_test_check_tmds_char_value(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_connector_state *conn_state; + struct drm_connector *conn; + + priv = drm_atomic_helper_connector_hdmi_init(test, + BIT(HDMI_COLORSPACE_RGB) | + BIT(HDMI_COLORSPACE_YUV422) | + BIT(HDMI_COLORSPACE_YUV444), + 12); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + conn_state = conn->state; + KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, 0); +} + static struct kunit_case drm_atomic_helper_connector_hdmi_reset_tests[] = { KUNIT_CASE(drm_test_check_bpc_8_value), KUNIT_CASE(drm_test_check_bpc_10_value), KUNIT_CASE(drm_test_check_bpc_12_value), KUNIT_CASE(drm_test_check_format_value), + KUNIT_CASE(drm_test_check_tmds_char_value), { } }; static struct kunit_suite drm_atomic_helper_connector_hdmi_reset_test_suite = { .name = "drm_atomic_helper_connector_hdmi_reset", diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.h b/drivers/gpu/drm/tests/drm_kunit_edid.h index 0366dd29c820..ed051d356d5e 100644 --- a/drivers/gpu/drm/tests/drm_kunit_edid.h +++ b/drivers/gpu/drm/tests/drm_kunit_edid.h @@ -101,6 +101,222 @@ static const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0 }; +/* + * edid-decode (hex): + * + * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 + * 00 21 01 03 81 a0 5a 78 1a 00 00 00 00 00 00 00 + * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01 + * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c + * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 + * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 + * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 7a + * + * 02 03 1b b1 e3 05 00 20 41 10 e2 00 ca 6d 03 0c + * 00 12 34 78 28 20 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 a8 + * + * ---------------- + * + * Block 0, Base EDID: + * EDID Structure Version & Revision: 1.3 + * Vendor & Product Identification: + * Manufacturer: LNX + * Model: 42 + * Made in: 2023 + * Basic Display Parameters & Features: + * Digital display + * DFP 1.x compatible TMDS + * Maximum image size: 160 cm x 90 cm + * Gamma: 2.20 + * Undefined display color type + * First detailed timing is the preferred timing + * Color Characteristics: + * Red : 0.0000, 0.0000 + * Green: 0.0000, 0.0000 + * Blue : 0.0000, 0.0000 + * White: 0.0000, 0.0000 + * Established Timings I & II: + * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz + * Standard Timings: none + * Detailed Timing Descriptors: + * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) + * Hfront 88 Hsync 44 Hback 148 Hpol P + * Vfront 4 Vsync 5 Vback 36 Vpol P + * Display Product Name: 'Test EDID' + * Display Range Limits: + * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz + * Dummy Descriptor: + * Extension blocks: 1 + * Checksum: 0x7a + * + * ---------------- + * + * Block 1, CTA-861 Extension Block: + * Revision: 3 + * Underscans IT Video Formats by default + * Supports YCbCr 4:4:4 + * Supports YCbCr 4:2:2 + * Native detailed modes: 1 + * Colorimetry Data Block: + * sRGB + * Video Data Block: + * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz + * Video Capability Data Block: + * YCbCr quantization: Selectable (via AVI YQ) + * RGB quantization: Selectable (via AVI Q) + * PT scan behavior: No Data + * IT scan behavior: Always Underscanned + * CE scan behavior: Always Underscanned + * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: + * Source physical address: 1.2.3.4 + * DC_48bit + * DC_36bit + * DC_30bit + * DC_Y444 + * Maximum TMDS clock: 200 MHz + * Extended HDMI video details: + * Checksum: 0xa8 Unused space in Extension Block: 100 bytes + */ +static const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, + 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, + 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, + 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, + 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7a, 0x02, 0x03, 0x1b, 0xb1, + 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0xca, 0x6d, 0x03, 0x0c, + 0x00, 0x12, 0x34, 0x78, 0x28, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xa8 +}; + +/* + * edid-decode (hex): + * + * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 + * 00 21 01 03 81 a0 5a 78 0a 00 00 00 00 00 00 00 + * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01 + * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c + * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 + * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 + * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 8a + * + * 02 03 1b b1 e3 05 00 20 41 10 e2 00 ca 6d 03 0c + * 00 12 34 78 44 20 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 8c + * + * ---------------- + * + * Block 0, Base EDID: + * EDID Structure Version & Revision: 1.3 + * Vendor & Product Identification: + * Manufacturer: LNX + * Model: 42 + * Made in: 2023 + * Basic Display Parameters & Features: + * Digital display + * DFP 1.x compatible TMDS + * Maximum image size: 160 cm x 90 cm + * Gamma: 2.20 + * RGB color display + * First detailed timing is the preferred timing + * Color Characteristics: + * Red : 0.0000, 0.0000 + * Green: 0.0000, 0.0000 + * Blue : 0.0000, 0.0000 + * White: 0.0000, 0.0000 + * Established Timings I & II: + * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz + * Standard Timings: none + * Detailed Timing Descriptors: + * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) + * Hfront 88 Hsync 44 Hback 148 Hpol P + * Vfront 4 Vsync 5 Vback 36 Vpol P + * Display Product Name: 'Test EDID' + * Display Range Limits: + * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz + * Dummy Descriptor: + * Extension blocks: 1 + * Checksum: 0x8a + * + * ---------------- + * + * Block 1, CTA-861 Extension Block: + * Revision: 3 + * Underscans IT Video Formats by default + * Supports YCbCr 4:4:4 + * Supports YCbCr 4:2:2 + * Native detailed modes: 1 + * Colorimetry Data Block: + * sRGB + * Video Data Block: + * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz + * Video Capability Data Block: + * YCbCr quantization: Selectable (via AVI YQ) + * RGB quantization: Selectable (via AVI Q) + * PT scan behavior: No Data + * IT scan behavior: Always Underscanned + * CE scan behavior: Always Underscanned + * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: + * Source physical address: 1.2.3.4 + * DC_48bit + * DC_36bit + * DC_30bit + * DC_Y444 + * Maximum TMDS clock: 340 MHz + * Extended HDMI video details: + * Checksum: 0x8c Unused space in Extension Block: 100 bytes + */ +static const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, + 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, + 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, + 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x8a, 0x02, 0x03, 0x1b, 0xb1, + 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0xca, 0x6d, 0x03, 0x0c, + 0x00, 0x12, 0x34, 0x78, 0x44, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x8c +}; + #endif // DRM_KUNIT_EDID_H_ From patchwork Mon Mar 11 14:49:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588842 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 3DB88C5475B for ; Mon, 11 Mar 2024 14:50:46 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 21DA8112AC1; Mon, 11 Mar 2024 14:50:45 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="BGvxM+h6"; dkim-atps=neutral Received: from sin.source.kernel.org (sin.source.kernel.org [145.40.73.55]) by gabe.freedesktop.org (Postfix) with ESMTPS id 9FF0F112ABC for ; Mon, 11 Mar 2024 14:50:41 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sin.source.kernel.org (Postfix) with ESMTP id 69E17CE10FF; Mon, 11 Mar 2024 14:50:36 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 41901C433F1; Mon, 11 Mar 2024 14:50:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168635; bh=2vPwPO8kvi7SoILxLke9m0YI6xFI1AJM4npOguVc2BY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=BGvxM+h6ShklFtRT+UmCyAcH3rIh2e/3i+xr27U1pbYT93bNEIlgOInkvEFfHVtL1 PZo++MzBWMRHGYvMzOu+FVTrhJ36SAD8Pr+3w5i+j+UvUxdoX9o+FyQ3CBqRbgRBLh jB/1P61L/PvIfQ4mzLOjfMLP1TouoM2bpxPka7D2WaQvN/MSZg1Q22tPZvWi/ZTfGL dBIyxVmIuHM8letCCeLGIkGSnxijNabCaMQ3+/VXL0zFN25jDd98MYD8hwSqnrRPVS n0nfdaO1EtKtf/TJOK1K0np3HnjrmUf/OTYrUzGEBDImow5W6rnfmSfsRfsn08ukIl 3kueEziGVzQJw== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:40 +0100 Subject: [PATCH v9 12/27] drm/connector: hdmi: Add custom hook to filter TMDS character rate MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-12-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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.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+D1vfrpE4FHG02pJDKnvpXguZkrUsUu68ExmlWPr314d/Kng+/oVd2t0nkXK2 OVKf33VUcrCIMbFICumyBIjbL4k7tSs151sfPNg5rAygQxh4OIUgImYyDL897HNkmfKMr7UsmvR IsEZLB1HWI8zmNW4B75fE/mA6/RLS0aGU6Gp12c1c1w7o2LvdDjS0Pesw6VS98xbhg/jzH5dmxT JCAA= 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 --- 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); From patchwork Mon Mar 11 14:49:41 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588841 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 73AACC54E58 for ; Mon, 11 Mar 2024 14:50:43 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id B6706112AAF; Mon, 11 Mar 2024 14:50:42 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="hy6ynmx8"; dkim-atps=neutral Received: from sin.source.kernel.org (sin.source.kernel.org [145.40.73.55]) by gabe.freedesktop.org (Postfix) with ESMTPS id 0B0AF112AAF for ; Mon, 11 Mar 2024 14:50:41 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sin.source.kernel.org (Postfix) with ESMTP id 20C78CE1102; Mon, 11 Mar 2024 14:50:39 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id EFBA5C433F1; Mon, 11 Mar 2024 14:50:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168638; bh=FYtMepddDx3HCIUl7lef+T8X++8b141JfngL4arDBvg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=hy6ynmx8940Oh31udQVQIzWpHC1P8RVpjL1gSSCt2gFRtxppGXQ1k6pvnXxphTfB+ 3pDeRibqVaH54cbIj3Bjy3km2B8EnbI3tMDCSDrWLxPRmXLlfetFzMypB3I2AoNrpT Ak9GIWMi6pjrk/Ru4vBsLKPIBH1HY2A5lksCGJFVRkEuzisLdxr4atG6Ve7UHfGbCZ U5AckXr0cJFwLhkex78wDl/bTmcH/5YYdUwSpwsRdxV5w8efKdzA6INIB1uCl3hp5D y26iZTJn1nYCKiZxDvv4fzxkjjLKaMzCPRbZtjnQNAqjB0k0/oCHHeEOoAgivIjdNY 2MRPkdhrZ9hzw== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:41 +0100 Subject: [PATCH v9 13/27] drm/tests: Add HDMI connector rate filter hook tests MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-13-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=3906; i=mripard@kernel.org; h=from:subject:message-id; bh=FYtMepddDx3HCIUl7lef+T8X++8b141JfngL4arDBvg=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpXhOzQ88r6Gq/HXbtMty+1fnl37MPnLrzdllcaveZ LZ/rNs9vaOUhUGMi0FWTJElRth8SdypWa872fjmwcxhZQIZwsDFKQATWdLH8D/se+USMZ7nD2fo z6k7bbmIaVbL1u9WG8LZFvpMmVTc2LmDkeGNftSzeG2lm+InG65cdLuyVJf54n+ZQK0zL3f+COj ezssAAA== 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" The previous patch adds a new hook for HDMI connectors to filter out configurations based on the TMDS character rate. Let's add some tests to make sure it works as expected. Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard --- .../gpu/drm/tests/drm_atomic_state_helper_test.c | 65 ++++++++++++++++++++++ 1 file changed, 65 insertions(+) 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 989661ad3aee..5a8750153510 100644 --- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c @@ -111,10 +111,22 @@ static int set_connector_edid(struct kunit *test, struct drm_connector *connecto } 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 = connector_to_priv(connector); const struct drm_edid *edid; @@ -489,11 +501,64 @@ static void drm_test_check_tmds_char_rate_rgb_12bpc(struct kunit *test) KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_bpc, 12); KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); 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_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), KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_10bpc), KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_12bpc), From patchwork Mon Mar 11 14:49:42 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588843 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 203D7C54E58 for ; Mon, 11 Mar 2024 14:50:46 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 5B1DA112AC4; Mon, 11 Mar 2024 14:50:45 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="jlJ4n5BJ"; 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 2B8FE112AAF for ; Mon, 11 Mar 2024 14:50:42 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id 804A660DD7; Mon, 11 Mar 2024 14:50:41 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id AAA7CC43390; Mon, 11 Mar 2024 14:50:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168641; bh=Ki2+kLnqSWfFwJnoudwShZ/oB+/zItTMcLPTcxLszd8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=jlJ4n5BJnnNaXoKUtJbIqU1JXYLUWktkkBIdzbKhM67wTYObwhk6XEriLaSjknSoK Dlj6D9SUAdjR8Xry3FB05Ok/gbLmIg8i51StVOO3t9P326SxJF81nAs1OrHL99t8x5 LnFAh2sqd75dUhy1LQJqW5DSKgAZvsXtP0RnW2Cbt32fD/g064SpNDtC255THKNpBg G44qK2zP8wUYKdjKcfryd5nqFAHXhvIGPMMMjEDTyXSW4rUMQhnBWCUvwqioVLugyI 3PiD7GZoIbkKhImUzkbfeYbq9sWFHFylJjUrnSaKULtTo30SMgPhZ1n2xv23m5++4s SX+EYI1ql6mJw== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:42 +0100 Subject: [PATCH v9 14/27] drm/connector: hdmi: Compute bpc and format automatically MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-14-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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 X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=11119; i=mripard@kernel.org; h=from:subject:message-id; bh=Ki2+kLnqSWfFwJnoudwShZ/oB+/zItTMcLPTcxLszd8=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpXgZOKQerZx79OQ3zv0b1vSENa71+bJh/bSKrNtp5 r0zJCbc6ihlYRDjYpAVU2SJETZfEndq1utONr55MHNYmcCGcHEKwESMihgZJh54eHmblmSQ4hQL h47q+fptloebWKRsUmXr8vcmOje2MzLcnyV0zVvUyPp/plvE3sncz4MfqfDPutHHnZQbveEK21t uAA== 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" Now that we have all the infrastructure needed, we can add some code that will, for a given connector state and mode, compute the best output format and bpc. The algorithm is equivalent to the one already found in i915 and vc4. Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic_state_helper.c | 184 ++++++++++++++++++++- .../gpu/drm/tests/drm_atomic_state_helper_test.c | 25 ++- 2 files changed, 197 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 448b4a73d1c8..9f517599f117 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -29,10 +29,11 @@ #include #include #include #include #include +#include #include #include #include #include #include @@ -660,10 +661,100 @@ connector_state_get_mode(const struct drm_connector_state *conn_state) return NULL; return &crtc_state->mode; } +static bool +sink_supports_format_bpc(const struct drm_connector *connector, + const struct drm_display_info *info, + const struct drm_display_mode *mode, + unsigned int format, unsigned int bpc) +{ + struct drm_device *dev = connector->dev; + u8 vic = drm_match_cea_mode(mode); + + if (vic == 1 && bpc != 8) { + drm_dbg(dev, "VIC1 requires a bpc of 8, got %u\n", bpc); + return false; + } + + if (!info->is_hdmi && + (format != HDMI_COLORSPACE_RGB || bpc != 8)) { + drm_dbg(dev, "DVI Monitors require an RGB output at 8 bpc\n"); + return false; + } + + if (!(connector->hdmi.supported_formats & BIT(format))) { + drm_dbg(dev, "%s format unsupported by the connector.\n", + drm_hdmi_connector_get_output_format_name(format)); + return false; + } + + switch (format) { + case HDMI_COLORSPACE_RGB: + drm_dbg(dev, "RGB Format, checking the constraints.\n"); + + if (!(info->color_formats & DRM_COLOR_FORMAT_RGB444)) + return false; + + if (bpc == 10 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30)) { + drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n"); + return false; + } + + if (bpc == 12 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36)) { + drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n"); + return false; + } + + drm_dbg(dev, "RGB format supported in that configuration.\n"); + + return true; + + case HDMI_COLORSPACE_YUV422: + drm_dbg(dev, "YUV422 format, checking the constraints.\n"); + + if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR422)) { + drm_dbg(dev, "Sink doesn't support YUV422.\n"); + return false; + } + + if (bpc != 12) { + drm_dbg(dev, "YUV422 only supports 12 bpc.\n"); + return false; + } + + drm_dbg(dev, "YUV422 format supported in that configuration.\n"); + + return true; + + case HDMI_COLORSPACE_YUV444: + drm_dbg(dev, "YUV444 format, checking the constraints.\n"); + + if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR444)) { + drm_dbg(dev, "Sink doesn't support YUV444.\n"); + return false; + } + + if (bpc == 10 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_30)) { + drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n"); + return false; + } + + if (bpc == 12 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_36)) { + drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n"); + return false; + } + + drm_dbg(dev, "YUV444 format supported in that configuration.\n"); + + return true; + } + + return false; +} + static enum drm_mode_status hdmi_clock_valid(const struct drm_connector *connector, const struct drm_display_mode *mode, unsigned long long clock) { @@ -704,10 +795,99 @@ hdmi_compute_clock(const struct drm_connector *connector, state->hdmi.tmds_char_rate = clock; return 0; } +static bool +hdmi_try_format_bpc(const struct drm_connector *connector, + struct drm_connector_state *state, + const struct drm_display_mode *mode, + unsigned int bpc, enum hdmi_colorspace fmt) +{ + const struct drm_display_info *info = &connector->display_info; + struct drm_device *dev = connector->dev; + int ret; + + drm_dbg(dev, "Trying %s output format\n", + drm_hdmi_connector_get_output_format_name(fmt)); + + if (!sink_supports_format_bpc(connector, info, mode, fmt, bpc)) { + drm_dbg(dev, "%s output format not supported with %u bpc\n", + drm_hdmi_connector_get_output_format_name(fmt), bpc); + return false; + } + + ret = hdmi_compute_clock(connector, state, mode, bpc, fmt); + if (ret) { + drm_dbg(dev, "Couldn't compute clock for %s output format and %u bpc\n", + drm_hdmi_connector_get_output_format_name(fmt), bpc); + return false; + } + + drm_dbg(dev, "%s output format supported with %u (TMDS char rate: %llu Hz)\n", + drm_hdmi_connector_get_output_format_name(fmt), bpc, state->hdmi.tmds_char_rate); + + return true; +} + +static int +hdmi_compute_format(const struct drm_connector *connector, + struct drm_connector_state *state, + const struct drm_display_mode *mode, + unsigned int bpc) +{ + struct drm_device *dev = connector->dev; + + if (hdmi_try_format_bpc(connector, state, mode, bpc, HDMI_COLORSPACE_RGB)) { + state->hdmi.output_format = HDMI_COLORSPACE_RGB; + return 0; + } + + if (hdmi_try_format_bpc(connector, state, mode, bpc, HDMI_COLORSPACE_YUV422)) { + state->hdmi.output_format = HDMI_COLORSPACE_YUV422; + return 0; + } + + drm_dbg(dev, "Failed. No Format Supported for that bpc count.\n"); + + return -EINVAL; +} + +static int +hdmi_compute_config(const struct drm_connector *connector, + struct drm_connector_state *state, + const struct drm_display_mode *mode) +{ + struct drm_device *dev = connector->dev; + unsigned int max_bpc = clamp_t(unsigned int, + state->max_bpc, + 8, connector->max_bpc); + unsigned int bpc; + int ret; + + for (bpc = max_bpc; bpc >= 8; bpc -= 2) { + drm_dbg(dev, "Trying with a %d bpc output\n", bpc); + + ret = hdmi_compute_format(connector, state, mode, bpc); + if (ret) + continue; + + state->hdmi.output_bpc = bpc; + + drm_dbg(dev, + "Mode %ux%u @ %uHz: Found configuration: bpc: %u, fmt: %s, clock: %llu\n", + mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode), + state->hdmi.output_bpc, + drm_hdmi_connector_get_output_format_name(state->hdmi.output_format), + state->hdmi.tmds_char_rate); + + return 0; + } + + return -EINVAL; +} + /** * drm_atomic_helper_connector_hdmi_check() - Helper to check HDMI connector atomic state * @connector: DRM Connector * @state: the DRM State object * @@ -727,13 +907,11 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector, drm_atomic_get_new_connector_state(state, connector); const struct drm_display_mode *mode = connector_state_get_mode(new_state); int ret; - ret = hdmi_compute_clock(connector, new_state, mode, - new_state->hdmi.output_bpc, - new_state->hdmi.output_format); + ret = hdmi_compute_config(connector, new_state, mode); if (ret) return ret; if (old_state->hdmi.output_bpc != new_state->hdmi.output_bpc || old_state->hdmi.output_format != new_state->hdmi.output_format) { 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 5a8750153510..f010fde0eb69 100644 --- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c @@ -68,13 +68,10 @@ static int light_up_connector(struct kunit *test, KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); conn_state = drm_atomic_get_connector_state(state, connector); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); - conn_state->hdmi.output_bpc = connector->max_bpc; - conn_state->hdmi.output_format = HDMI_COLORSPACE_RGB; - ret = drm_atomic_set_crtc_for_connector(conn_state, crtc); KUNIT_EXPECT_EQ(test, ret, 0); crtc_state = drm_atomic_get_crtc_state(state, crtc); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); @@ -249,14 +246,19 @@ static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test) priv = drm_atomic_helper_connector_hdmi_init(test, BIT(HDMI_COLORSPACE_RGB), 10); KUNIT_ASSERT_NOT_NULL(test, priv); + conn = &priv->connector; + ret = set_connector_edid(test, conn, + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); + KUNIT_ASSERT_EQ(test, ret, 0); + 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; @@ -270,15 +272,15 @@ static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state); old_conn_state = drm_atomic_get_old_connector_state(state, conn); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state); - new_conn_state->hdmi.output_bpc = 8; + new_conn_state->max_requested_bpc = 8; KUNIT_ASSERT_NE(test, - old_conn_state->hdmi.output_bpc, - new_conn_state->hdmi.output_bpc); + old_conn_state->max_requested_bpc, + new_conn_state->max_requested_bpc); ret = drm_atomic_check_only(state); KUNIT_ASSERT_EQ(test, ret, 0); old_conn_state = drm_atomic_get_old_connector_state(state, conn); @@ -318,14 +320,19 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test) priv = drm_atomic_helper_connector_hdmi_init(test, BIT(HDMI_COLORSPACE_RGB), 10); KUNIT_ASSERT_NOT_NULL(test, priv); + conn = &priv->connector; + ret = set_connector_edid(test, conn, + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); + KUNIT_ASSERT_EQ(test, ret, 0); + 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; @@ -668,11 +675,11 @@ static void drm_test_check_format_value(struct kunit *test) 8); KUNIT_ASSERT_NOT_NULL(test, priv); conn = &priv->connector; conn_state = conn->state; - KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, 0); } /* * Test that the value of the output format property out of reset is set * to 0, and will be computed at atomic_check time. From patchwork Mon Mar 11 14:49:43 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588844 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 0F5C4C54E58 for ; Mon, 11 Mar 2024 14:50:50 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 21D4C112AD1; Mon, 11 Mar 2024 14:50:50 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="Rd/sTv4z"; dkim-atps=neutral Received: from sin.source.kernel.org (sin.source.kernel.org [145.40.73.55]) by gabe.freedesktop.org (Postfix) with ESMTPS id C118C112ABC for ; Mon, 11 Mar 2024 14:50:47 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sin.source.kernel.org (Postfix) with ESMTP id 22237CE1101; Mon, 11 Mar 2024 14:50:45 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id C8076C433C7; Mon, 11 Mar 2024 14:50:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168644; bh=KkZqZSJpWLgxai2zI6UpP/qpD0ZHinCZbUrZYwmTyzw=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Rd/sTv4z6bmVCotFB7cVHv58CtN8ckwYSIOE+fmbDV3xOo5YParQWQ90NBKYrrFtM bbaYj4GHtGC9MRpjUEMb1BS24bj5DfXfvjj8OvNoc4AEeRcshJnD3UnyUL8ZXg/FNU zloWpdFnBo6qCBadawOiFq+QnrWkSx8pKbaFMUFQtzwowC3eKYogWBQPc7PBllAwiw gMUFKTrroN/91EFevtK3juF3MTIiwoF/W9/ET5VdOwgM29RNSAp/PrJH5DTfWLMDjd MuVljA2OcZtxhOSnAkEtJwVnRh0Rwxn/+Fx1yjO6rAFNxlerVvFLEF3AnjFgaNo4Oj YWnDLbdxXn+XQ== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:43 +0100 Subject: [PATCH v9 15/27] drm/tests: Add HDMI connector bpc and format tests MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-15-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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 X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=27343; i=mripard@kernel.org; h=from:subject:message-id; bh=KkZqZSJpWLgxai2zI6UpP/qpD0ZHinCZbUrZYwmTyzw=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpXhzMzrOeha9u9HKvM/3zJq3/Tv4E8wzFzrUrMjk/ yW6M7Sho5SFQYyLQVZMkSVG2HxJ3KlZrzvZ+ObBzGFlAhnCwMUpABOxnMTwP+/FxJccicuOCEi9 mOoyR1Xd4sDDtogz7ZOS/zHefhIepsPwz9wkMHZXSn6FtfPU57UCczUa5K1+bw5/tWuJcOiMwBR DZgA= 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" The previous patch added the bpc and format an HDMI connector needs to be set up with for a given connector state. Let's add a few tests to make sure it works as expected. Signed-off-by: Maxime Ripard --- .../gpu/drm/tests/drm_atomic_state_helper_test.c | 504 +++++++++++++++++++++ drivers/gpu/drm/tests/drm_kunit_edid.h | 160 +++++++ 2 files changed, 664 insertions(+) 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 f010fde0eb69..c0f26422e925 100644 --- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c @@ -368,10 +368,60 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test) crtc_state = drm_atomic_get_new_crtc_state(state, crtc); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); KUNIT_EXPECT_FALSE(test, crtc_state->mode_changed); } +/* + * Test that if we have an HDMI connector but a !HDMI display, we always + * output RGB with 8 bpc. + */ +static void drm_test_check_output_bpc_dvi(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_display_info *info; + struct drm_display_mode *preferred; + 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) | + BIT(HDMI_COLORSPACE_YUV422) | + BIT(HDMI_COLORSPACE_YUV444), + 12); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + ret = set_connector_edid(test, conn, + test_edid_dvi_1080p, + ARRAY_SIZE(test_edid_dvi_1080p)); + KUNIT_ASSERT_EQ(test, ret, 0); + + info = &conn->display_info; + KUNIT_ASSERT_FALSE(test, info->is_hdmi); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + 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); + + conn_state = conn->state; + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); +} + /* * Test that when doing a commit which would use RGB 8bpc, the TMDS * clock rate stored in the connector state is equal to the mode clock */ static void drm_test_check_tmds_char_rate_rgb_8bpc(struct kunit *test) @@ -560,14 +610,468 @@ static void drm_test_check_hdmi_funcs_reject_rate(struct kunit *test) ret = drm_atomic_check_only(state); KUNIT_EXPECT_LT(test, ret, 0); } +/* + * Test that if: + * - We have an HDMI connector supporting RGB only + * - The chosen mode has a TMDS character rate higher than the display + * supports in RGB/12bpc + * - The chosen mode has a TMDS character rate lower than the display + * supports in RGB/10bpc. + * + * Then we will pick the latter, and the computed TMDS character rate + * will be equal to 1.25 times the mode pixel clock. + */ +static void drm_test_check_max_tmds_rate_bpc_fallback(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_display_info *info; + struct drm_display_mode *preferred; + unsigned long long rate; + 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), + 12); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + ret = set_connector_edid(test, conn, + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); + KUNIT_ASSERT_EQ(test, ret, 0); + + info = &conn->display_info; + KUNIT_ASSERT_TRUE(test, info->is_hdmi); + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK); + + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB); + KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000); + + rate = drm_connector_hdmi_compute_mode_clock(preferred, 10, HDMI_COLORSPACE_RGB); + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + KUNIT_EXPECT_EQ(test, ret, 0); + + conn_state = conn->state; + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 10); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1250); +} + +/* + * Test that if: + * - We have an HDMI connector supporting both RGB and YUV422 and up to + * 12 bpc + * - The chosen mode has a TMDS character rate higher than the display + * supports in RGB/12bpc + * - The chosen mode has a TMDS character rate lower than the display + * supports in YUV422/12bpc. + * + * Then we will pick the latter, and the computed TMDS character rate + * will be equal to the mode pixel clock. + */ +static void drm_test_check_max_tmds_rate_format_fallback(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_display_info *info; + struct drm_display_mode *preferred; + unsigned long long rate; + 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) | + BIT(HDMI_COLORSPACE_YUV422) | + BIT(HDMI_COLORSPACE_YUV444), + 12); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + ret = set_connector_edid(test, conn, + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); + KUNIT_ASSERT_EQ(test, ret, 0); + + info = &conn->display_info; + KUNIT_ASSERT_TRUE(test, info->is_hdmi); + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK); + + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB); + KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000); + + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422); + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + KUNIT_EXPECT_EQ(test, ret, 0); + + conn_state = conn->state; + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 12); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_YUV422); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1000); +} + +/* + * Test that if a driver and screen supports RGB and YUV formats, and we + * try to set the VIC 1 mode, we end up with 8bpc RGB even if we could + * have had a higher bpc. + */ +static void drm_test_check_output_bpc_format_vic_1(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_display_info *info; + struct drm_display_mode *mode; + unsigned long long rate; + 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) | + BIT(HDMI_COLORSPACE_YUV422) | + BIT(HDMI_COLORSPACE_YUV444), + 12); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + ret = set_connector_edid(test, conn, + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); + KUNIT_ASSERT_EQ(test, ret, 0); + + info = &conn->display_info; + KUNIT_ASSERT_TRUE(test, info->is_hdmi); + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + mode = drm_display_mode_from_cea_vic(drm, 1); + KUNIT_ASSERT_NOT_NULL(test, mode); + + /* + * NOTE: We can't use drm_connector_hdmi_compute_mode_clock() + * here because we're trying to get the rate of an invalid + * configuration. + * + * Thus, we have to calculate the rate by hand. + */ + rate = mode->clock * 1500; + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, mode, ctx); + KUNIT_EXPECT_EQ(test, ret, 0); + + conn_state = conn->state; + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); +} + +/* + * Test that if a driver supports only RGB but the screen also supports + * YUV formats, we only end up with an RGB format. + */ +static void drm_test_check_output_bpc_format_driver_rgb_only(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_display_info *info; + struct drm_display_mode *preferred; + unsigned long long rate; + 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), + 12); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + ret = set_connector_edid(test, conn, + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); + KUNIT_ASSERT_EQ(test, ret, 0); + + info = &conn->display_info; + KUNIT_ASSERT_TRUE(test, info->is_hdmi); + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + + /* + * We're making sure that YUV422 would be the preferred option + * here: we're always favouring higher bpc, we can't have RGB + * because the TMDS character rate exceeds the maximum supported + * by the display, and YUV422 works for that display. + * + * But since the driver only supports RGB, we should fallback to + * a lower bpc with RGB. + */ + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB); + KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000); + + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422); + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + KUNIT_EXPECT_EQ(test, ret, 0); + + conn_state = conn->state; + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + KUNIT_EXPECT_LT(test, conn_state->hdmi.output_bpc, 12); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); +} + +/* + * Test that if a screen supports only RGB but the driver also supports + * YUV formats, we only end up with an RGB format. + */ +static void drm_test_check_output_bpc_format_display_rgb_only(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_display_info *info; + struct drm_display_mode *preferred; + unsigned long long rate; + 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) | + BIT(HDMI_COLORSPACE_YUV422) | + BIT(HDMI_COLORSPACE_YUV444), + 12); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + ret = set_connector_edid(test, conn, + test_edid_hdmi_1080p_rgb_max_200mhz, + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_200mhz)); + KUNIT_ASSERT_EQ(test, ret, 0); + + info = &conn->display_info; + KUNIT_ASSERT_TRUE(test, info->is_hdmi); + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + + /* + * We're making sure that YUV422 would be the preferred option + * here: we're always favouring higher bpc, we can't have RGB + * because the TMDS character rate exceeds the maximum supported + * by the display, and YUV422 works for that display. + * + * But since the display only supports RGB, we should fallback to + * a lower bpc with RGB. + */ + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB); + KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000); + + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422); + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + KUNIT_EXPECT_EQ(test, ret, 0); + + conn_state = conn->state; + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + KUNIT_EXPECT_LT(test, conn_state->hdmi.output_bpc, 12); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); +} + +/* + * Test that if a display supports higher bpc but the driver only + * supports 8 bpc, we only end up with 8 bpc even if we could have had a + * higher bpc. + */ +static void drm_test_check_output_bpc_format_driver_8bpc_only(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_display_info *info; + struct drm_display_mode *preferred; + unsigned long long rate; + 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); + + conn = &priv->connector; + ret = set_connector_edid(test, conn, + test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz, + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz)); + KUNIT_ASSERT_EQ(test, ret, 0); + + info = &conn->display_info; + KUNIT_ASSERT_TRUE(test, info->is_hdmi); + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + + /* + * We're making sure that we have headroom on the TMDS character + * clock to actually use 12bpc. + */ + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB); + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + KUNIT_EXPECT_EQ(test, ret, 0); + + conn_state = conn->state; + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); +} + +/* + * Test that if a driver supports higher bpc but the display only + * supports 8 bpc, we only end up with 8 bpc even if we could have had a + * higher bpc. + */ +static void drm_test_check_output_bpc_format_display_8bpc_only(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_display_info *info; + struct drm_display_mode *preferred; + unsigned long long rate; + 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) | + BIT(HDMI_COLORSPACE_YUV422) | + BIT(HDMI_COLORSPACE_YUV444), + 12); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + ret = set_connector_edid(test, conn, + test_edid_hdmi_1080p_rgb_max_340mhz, + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_340mhz)); + KUNIT_ASSERT_EQ(test, ret, 0); + + info = &conn->display_info; + KUNIT_ASSERT_TRUE(test, info->is_hdmi); + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + + /* + * We're making sure that we have headroom on the TMDS character + * clock to actually use 12bpc. + */ + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB); + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + KUNIT_EXPECT_EQ(test, ret, 0); + + conn_state = conn->state; + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); +} + static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = { KUNIT_CASE(drm_test_check_hdmi_funcs_reject_rate), + KUNIT_CASE(drm_test_check_max_tmds_rate_bpc_fallback), + KUNIT_CASE(drm_test_check_max_tmds_rate_format_fallback), 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_output_bpc_dvi), + KUNIT_CASE(drm_test_check_output_bpc_format_vic_1), + KUNIT_CASE(drm_test_check_output_bpc_format_display_8bpc_only), + KUNIT_CASE(drm_test_check_output_bpc_format_display_rgb_only), + KUNIT_CASE(drm_test_check_output_bpc_format_driver_8bpc_only), + KUNIT_CASE(drm_test_check_output_bpc_format_driver_rgb_only), KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_8bpc), KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_10bpc), KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_12bpc), /* * TODO: We should have tests to check that a change in the diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.h b/drivers/gpu/drm/tests/drm_kunit_edid.h index ed051d356d5e..cda410440aca 100644 --- a/drivers/gpu/drm/tests/drm_kunit_edid.h +++ b/drivers/gpu/drm/tests/drm_kunit_edid.h @@ -1,8 +1,66 @@ #ifndef DRM_KUNIT_EDID_H_ #define DRM_KUNIT_EDID_H_ +/* + * edid-decode (hex): + * + * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 + * 00 21 01 03 81 a0 5a 78 0a 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 01 01 01 01 01 01 01 01 01 01 + * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c + * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 + * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 + * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ab + * + * ---------------- + * + * Block 0, Base EDID: + * EDID Structure Version & Revision: 1.3 + * Vendor & Product Identification: + * Manufacturer: LNX + * Model: 42 + * Made in: 2023 + * Basic Display Parameters & Features: + * Digital display + * DFP 1.x compatible TMDS + * Maximum image size: 160 cm x 90 cm + * Gamma: 2.20 + * RGB color display + * First detailed timing is the preferred timing + * Color Characteristics: + * Red : 0.0000, 0.0000 + * Green: 0.0000, 0.0000 + * Blue : 0.0000, 0.0000 + * White: 0.0000, 0.0000 + * Established Timings I & II: none + * Standard Timings: none + * Detailed Timing Descriptors: + * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) + * Hfront 88 Hsync 44 Hback 148 Hpol P + * Vfront 4 Vsync 5 Vback 36 Vpol P + * Display Product Name: 'Test EDID' + * Display Range Limits: + * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz + * Dummy Descriptor: + * Checksum: 0xab + */ +static const unsigned char test_edid_dvi_1080p[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, + 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, + 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, + 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab +}; + /* * edid-decode (hex): * * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00 @@ -101,10 +159,112 @@ static const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0 }; +/* + * edid-decode (hex): + * + * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 + * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00 + * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01 + * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c + * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 + * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 + * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92 + * + * 02 03 1b 81 e3 05 00 20 41 10 e2 00 4a 6d 03 0c + * 00 12 34 00 28 20 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d0 + * + * ---------------- + * + * Block 0, Base EDID: + * EDID Structure Version & Revision: 1.3 + * Vendor & Product Identification: + * Manufacturer: LNX + * Model: 42 + * Made in: 2023 + * Basic Display Parameters & Features: + * Digital display + * DFP 1.x compatible TMDS + * Maximum image size: 160 cm x 90 cm + * Gamma: 2.20 + * Monochrome or grayscale display + * First detailed timing is the preferred timing + * Color Characteristics: + * Red : 0.0000, 0.0000 + * Green: 0.0000, 0.0000 + * Blue : 0.0000, 0.0000 + * White: 0.0000, 0.0000 + * Established Timings I & II: + * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz + * Standard Timings: none + * Detailed Timing Descriptors: + * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) + * Hfront 88 Hsync 44 Hback 148 Hpol P + * Vfront 4 Vsync 5 Vback 36 Vpol P + * Display Product Name: 'Test EDID' + * Display Range Limits: + * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz + * Dummy Descriptor: + * Extension blocks: 1 + * Checksum: 0x92 + * + * ---------------- + * + * Block 1, CTA-861 Extension Block: + * Revision: 3 + * Underscans IT Video Formats by default + * Native detailed modes: 1 + * Colorimetry Data Block: + * sRGB + * Video Data Block: + * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz + * Video Capability Data Block: + * YCbCr quantization: No Data + * RGB quantization: Selectable (via AVI Q) + * PT scan behavior: No Data + * IT scan behavior: Always Underscanned + * CE scan behavior: Always Underscanned + * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: + * Source physical address: 1.2.3.4 + * Maximum TMDS clock: 340 MHz + * Extended HDMI video details: + * Checksum: 0xd0 Unused space in Extension Block: 100 bytes + */ +static const unsigned char test_edid_hdmi_1080p_rgb_max_340mhz[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, + 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, + 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, + 0x46, 0x00, 0x00, 0xc4, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x02, 0x03, 0x1b, 0x81, + 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x6d, 0x03, 0x0c, + 0x00, 0x12, 0x34, 0x00, 0x44, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xd0 +}; + /* * edid-decode (hex): * * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 * 00 21 01 03 81 a0 5a 78 1a 00 00 00 00 00 00 00 From patchwork Mon Mar 11 14:49:44 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588845 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 7C349C54E60 for ; Mon, 11 Mar 2024 14:50:53 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 9D539112ABC; Mon, 11 Mar 2024 14:50:52 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="ImWpwA6R"; dkim-atps=neutral Received: from sin.source.kernel.org (sin.source.kernel.org [145.40.73.55]) by gabe.freedesktop.org (Postfix) with ESMTPS id 8C4E5112AD4 for ; Mon, 11 Mar 2024 14:50:50 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sin.source.kernel.org (Postfix) with ESMTP id DD518CE1103; Mon, 11 Mar 2024 14:50:47 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id B6692C43394; Mon, 11 Mar 2024 14:50:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168647; bh=VGIhkwPyv0WFsgHXtqY5x4Uuc5dl2wKjO2BmaSTWiQ0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=ImWpwA6Rbc2iHkYw9WwtJCvKcOGAQXEsgarfC9FMDOkcDiJ2/65jTwR/OGl1oaHX1 OTThV5ZDtzWBNMPJCO2pCmUwOdRTFMz/NP5B4PEsyvF8+iLMGv9ru8q5LGuhfV+/wk RRtwWIieBx21bdHaCdmt/GjV2XxpWYHSXXnBzTDphnRaOnchy6tdHrm2rRiLxR6ymq 5t6eAjbhpanDsG+ERf5hJ8zPqUlXQ41NPSWIl8Yqbe9n7ayFn66KTyxz6ObWjDU9sj zR8HZ5yfmjKNHadHuJXtbdaa+dgMr8pKycGNUGr3UbrVJ6L7tTcTsSOPKbzInAG6Lg C8nl9XhaZ7Pww== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:44 +0100 Subject: [PATCH v9 16/27] drm/connector: hdmi: Add Broadcast RGB property MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-16-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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 , Pekka Paalanen X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=14048; i=mripard@kernel.org; h=from:subject:message-id; bh=VGIhkwPyv0WFsgHXtqY5x4Uuc5dl2wKjO2BmaSTWiQ0=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpfguT/RP3rzw4tGOAIVWY/lNEk6XYiZ9dPw08UNcD AOf64N3HaUsDGJcDLJiiiwxwuZL4k7Net3JxjcPZg4rE8gQBi5OAZjIzFhGht9H74XqHmaaoeWv eKBqjUqzyZH2+7MuXLi28JBOdY1wHFDFZbYzZT2+uffUVfq3/WGS0jBUnO4fvoghb1vNRpVeFh1 WAA== 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" The i915 driver has a property to force the RGB range of an HDMI output. The vc4 driver then implemented the same property with the same semantics. KWin has support for it, and a PR for mutter is also there to support it. Both drivers implementing the same property with the same semantics, plus the userspace having support for it, is proof enough that it's pretty much a de-facto standard now and we can provide helpers for it. Let's plumb it into the newly created HDMI connector. Reviewed-by: Dave Stevenson Acked-by: Pekka Paalanen Reviewed-by: Sebastian Wick Signed-off-by: Maxime Ripard --- Documentation/gpu/kms-properties.csv | 1 - drivers/gpu/drm/drm_atomic.c | 2 + drivers/gpu/drm/drm_atomic_state_helper.c | 4 +- drivers/gpu/drm/drm_atomic_uapi.c | 4 ++ drivers/gpu/drm/drm_connector.c | 88 +++++++++++++++++++++++++++++++ include/drm/drm_connector.h | 36 +++++++++++++ 6 files changed, 133 insertions(+), 2 deletions(-) diff --git a/Documentation/gpu/kms-properties.csv b/Documentation/gpu/kms-properties.csv index 0f9590834829..caef14c532d4 100644 --- a/Documentation/gpu/kms-properties.csv +++ b/Documentation/gpu/kms-properties.csv @@ -15,11 +15,10 @@ Owner Module/Drivers,Group,Property Name,Type,Property Values,Object attached,De ,,“saturation”,RANGE,"Min=0, Max=100",Connector,TBD ,,“hue”,RANGE,"Min=0, Max=100",Connector,TBD ,Virtual GPU,“suggested X”,RANGE,"Min=0, Max=0xffffffff",Connector,property to suggest an X offset for a connector ,,“suggested Y”,RANGE,"Min=0, Max=0xffffffff",Connector,property to suggest an Y offset for a connector ,Optional,"""aspect ratio""",ENUM,"{ ""None"", ""4:3"", ""16:9"" }",Connector,TDB -i915,Generic,"""Broadcast RGB""",ENUM,"{ ""Automatic"", ""Full"", ""Limited 16:235"" }",Connector,"When this property is set to Limited 16:235 and CTM is set, the hardware will be programmed with the result of the multiplication of CTM by the limited range matrix to ensure the pixels normally in the range 0..1.0 are remapped to the range 16/255..235/255." ,,“audio”,ENUM,"{ ""force-dvi"", ""off"", ""auto"", ""on"" }",Connector,TBD ,SDVO-TV,“mode”,ENUM,"{ ""NTSC_M"", ""NTSC_J"", ""NTSC_443"", ""PAL_B"" } etc.",Connector,TBD ,,"""left_margin""",RANGE,"Min=0, Max= SDVO dependent",Connector,TBD ,,"""right_margin""",RANGE,"Min=0, Max= SDVO dependent",Connector,TBD ,,"""top_margin""",RANGE,"Min=0, Max= SDVO dependent",Connector,TBD diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 26f9e525c0a0..3e57d98d8418 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1143,10 +1143,12 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, drm_printf(p, "\tmax_requested_bpc=%d\n", state->max_requested_bpc); drm_printf(p, "\tcolorspace=%s\n", drm_get_colorspace_name(state->colorspace)); if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) { + drm_printf(p, "\tbroadcast_rgb=%s\n", + drm_hdmi_connector_get_broadcast_rgb_name(state->hdmi.broadcast_rgb)); drm_printf(p, "\toutput_bpc=%u\n", state->hdmi.output_bpc); drm_printf(p, "\toutput_format=%s\n", drm_hdmi_connector_get_output_format_name(state->hdmi.output_format)); drm_printf(p, "\ttmds_char_rate=%llu\n", state->hdmi.tmds_char_rate); } diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 9f517599f117..0e8fb653965a 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -587,10 +587,11 @@ void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector, { unsigned int max_bpc = connector->max_bpc; new_state->max_bpc = max_bpc; new_state->max_requested_bpc = max_bpc; + new_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO; } EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_reset); /** * drm_atomic_helper_connector_tv_check - Validate an analog TV connector state @@ -911,11 +912,12 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector, ret = hdmi_compute_config(connector, new_state, mode); if (ret) return ret; - if (old_state->hdmi.output_bpc != new_state->hdmi.output_bpc || + if (old_state->hdmi.broadcast_rgb != new_state->hdmi.broadcast_rgb || + old_state->hdmi.output_bpc != new_state->hdmi.output_bpc || old_state->hdmi.output_format != new_state->hdmi.output_format) { struct drm_crtc *crtc = new_state->crtc; struct drm_crtc_state *crtc_state; crtc_state = drm_atomic_get_crtc_state(state, crtc); diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 29d4940188d4..2b415b4ed506 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -774,10 +774,12 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, fence_ptr); } else if (property == connector->max_bpc_property) { state->max_requested_bpc = val; } else if (property == connector->privacy_screen_sw_state_property) { state->privacy_screen_sw_state = val; + } else if (property == connector->broadcast_rgb_property) { + state->hdmi.broadcast_rgb = val; } else if (connector->funcs->atomic_set_property) { return connector->funcs->atomic_set_property(connector, state, property, val); } else { drm_dbg_atomic(connector->dev, @@ -857,10 +859,12 @@ drm_atomic_connector_get_property(struct drm_connector *connector, *val = 0; } else if (property == connector->max_bpc_property) { *val = state->max_requested_bpc; } else if (property == connector->privacy_screen_sw_state_property) { *val = state->privacy_screen_sw_state; + } else if (property == connector->broadcast_rgb_property) { + *val = state->hdmi.broadcast_rgb; } else if (connector->funcs->atomic_get_property) { return connector->funcs->atomic_get_property(connector, state, property, val); } else { drm_dbg_atomic(dev, diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 591d2d500f61..088e6ba286bf 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -1210,10 +1210,33 @@ static const u32 dp_colorspaces = BIT(DRM_MODE_COLORIMETRY_SYCC_601) | BIT(DRM_MODE_COLORIMETRY_OPYCC_601) | BIT(DRM_MODE_COLORIMETRY_BT2020_CYCC) | BIT(DRM_MODE_COLORIMETRY_BT2020_YCC); +static const struct drm_prop_enum_list broadcast_rgb_names[] = { + { DRM_HDMI_BROADCAST_RGB_AUTO, "Automatic" }, + { DRM_HDMI_BROADCAST_RGB_FULL, "Full" }, + { DRM_HDMI_BROADCAST_RGB_LIMITED, "Limited 16:235" }, +}; + +/* + * drm_hdmi_connector_get_broadcast_rgb_name - Return a string for HDMI connector RGB broadcast selection + * @broadcast_rgb: Broadcast RGB selection to compute name of + * + * Returns: the name of the Broadcast RGB selection, or NULL if the type + * is not valid. + */ +const char * +drm_hdmi_connector_get_broadcast_rgb_name(enum drm_hdmi_broadcast_rgb broadcast_rgb) +{ + if (broadcast_rgb >= ARRAY_SIZE(broadcast_rgb_names)) + return NULL; + + return broadcast_rgb_names[broadcast_rgb].name; +} +EXPORT_SYMBOL(drm_hdmi_connector_get_broadcast_rgb_name); + static const char * const output_format_str[] = { [HDMI_COLORSPACE_RGB] = "RGB", [HDMI_COLORSPACE_YUV420] = "YUV 4:2:0", [HDMI_COLORSPACE_YUV422] = "YUV 4:2:2", [HDMI_COLORSPACE_YUV444] = "YUV 4:4:4", @@ -1706,10 +1729,42 @@ void drm_connector_attach_dp_subconnector_property(struct drm_connector *connect EXPORT_SYMBOL(drm_connector_attach_dp_subconnector_property); /** * DOC: HDMI connector properties * + * Broadcast RGB (HDMI specific) + * Indicates the Quantization Range (Full vs Limited) used. The color + * processing pipeline will be adjusted to match the value of the + * property, and the Infoframes will be generated and sent accordingly. + * + * This property is only relevant if the HDMI output format is RGB. If + * it's one of the YCbCr variant, it will be ignored. + * + * The CRTC attached to the connector must be configured by user-space to + * always produce full-range pixels. + * + * The value of this property can be one of the following: + * + * Automatic: + * The quantization range is selected automatically based on the + * mode according to the HDMI specifications (HDMI 1.4b - Section + * 6.6 - Video Quantization Ranges). + * + * Full: + * Full quantization range is forced. + * + * Limited 16:235: + * Limited quantization range is forced. Unlike the name suggests, + * this works for any number of bits-per-component. + * + * Property values other than Automatic can result in colors being off (if + * limited is selected but the display expects full), or a black screen + * (if full is selected but the display expects limited). + * + * Drivers can set up this property by calling + * drm_connector_attach_broadcast_rgb_property(). + * * content type (HDMI specific): * Indicates content type setting to be used in HDMI infoframes to indicate * content type for the external device, so that it adjusts its display * settings accordingly. * @@ -2568,10 +2623,43 @@ int drm_connector_attach_hdr_output_metadata_property(struct drm_connector *conn return 0; } EXPORT_SYMBOL(drm_connector_attach_hdr_output_metadata_property); +/** + * drm_connector_attach_broadcast_rgb_property - attach "Broadcast RGB" property + * @connector: connector to attach the property on. + * + * This is used to add support for forcing the RGB range on a connector + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_connector_attach_broadcast_rgb_property(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_property *prop; + + prop = connector->broadcast_rgb_property; + if (!prop) { + prop = drm_property_create_enum(dev, DRM_MODE_PROP_ENUM, + "Broadcast RGB", + broadcast_rgb_names, + ARRAY_SIZE(broadcast_rgb_names)); + if (!prop) + return -EINVAL; + + connector->broadcast_rgb_property = prop; + } + + drm_object_attach_property(&connector->base, prop, + DRM_HDMI_BROADCAST_RGB_AUTO); + + return 0; +} +EXPORT_SYMBOL(drm_connector_attach_broadcast_rgb_property); + /** * drm_connector_attach_colorspace_property - attach "Colorspace" property * @connector: connector to attach the property on. * * This is used to allow the userspace to signal the output colorspace diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 8cda902934cd..bb6b6a36ade3 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -367,10 +367,33 @@ enum drm_panel_orientation { DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP, DRM_MODE_PANEL_ORIENTATION_LEFT_UP, DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, }; +/** + * enum drm_hdmi_broadcast_rgb - Broadcast RGB Selection for an HDMI @drm_connector + */ +enum drm_hdmi_broadcast_rgb { + /** + * @DRM_HDMI_BROADCAST_RGB_AUTO: The RGB range is selected + * automatically based on the mode. + */ + DRM_HDMI_BROADCAST_RGB_AUTO, + + /** + * @DRM_HDMI_BROADCAST_RGB_FULL: Full range RGB is forced. + */ + DRM_HDMI_BROADCAST_RGB_FULL, + + /** + * @DRM_HDMI_BROADCAST_RGB_LIMITED: Limited range RGB is forced. + */ + DRM_HDMI_BROADCAST_RGB_LIMITED, +}; + +const char * +drm_hdmi_connector_get_broadcast_rgb_name(enum drm_hdmi_broadcast_rgb broadcast_rgb); const char * drm_hdmi_connector_get_output_format_name(enum hdmi_colorspace fmt); /** * struct drm_monitor_range_info - Panel's Monitor range in EDID for @@ -1039,10 +1062,16 @@ struct drm_connector_state { /** * @hdmi: HDMI-related variable and properties. Filled by * @drm_atomic_helper_connector_hdmi_check(). */ struct { + /** + * @broadcast_rgb: Connector property to pass the + * Broadcast RGB selection value. + */ + enum drm_hdmi_broadcast_rgb broadcast_rgb; + /** * @output_bpc: Bits per color channel to output. */ unsigned int output_bpc; @@ -1751,10 +1780,16 @@ struct drm_connector { * @privacy_screen_hw_state_property: Optional atomic property for the * connector to report the actual integrated privacy screen state. */ struct drm_property *privacy_screen_hw_state_property; + /** + * @broadcast_rgb_property: Connector property to set the + * Broadcast RGB selection to output with. + */ + struct drm_property *broadcast_rgb_property; + #define DRM_CONNECTOR_POLL_HPD (1 << 0) #define DRM_CONNECTOR_POLL_CONNECT (1 << 1) #define DRM_CONNECTOR_POLL_DISCONNECT (1 << 2) /** @@ -2090,10 +2125,11 @@ int drm_mode_create_scaling_mode_property(struct drm_device *dev); int drm_connector_attach_content_type_property(struct drm_connector *dev); int drm_connector_attach_scaling_mode_property(struct drm_connector *connector, u32 scaling_mode_mask); int drm_connector_attach_vrr_capable_property( struct drm_connector *connector); +int drm_connector_attach_broadcast_rgb_property(struct drm_connector *connector); int drm_connector_attach_colorspace_property(struct drm_connector *connector); int drm_connector_attach_hdr_output_metadata_property(struct drm_connector *connector); bool drm_connector_atomic_hdr_metadata_equal(struct drm_connector_state *old_state, struct drm_connector_state *new_state); int drm_mode_create_aspect_ratio_property(struct drm_device *dev); From patchwork Mon Mar 11 14:49:45 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588846 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 0A8ECC5475B for ; Mon, 11 Mar 2024 14:50:54 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 06430112ACD; Mon, 11 Mar 2024 14:50:53 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="stuHJK4k"; 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 C244D112ACD for ; Mon, 11 Mar 2024 14:50:50 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id 22DD760DD7; Mon, 11 Mar 2024 14:50:50 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6C14AC433F1; Mon, 11 Mar 2024 14:50:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168649; bh=ETz9nlR0atHV46UY5p7KYA1JJn6mH150Vspr8N/BJpw=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=stuHJK4kAMKnpLGvqQ3XSbW18gDPnLc9EyjAm4Hq2C5L0CcDcFVUPJmFV7k7eIGP8 AWBpOMihYQ/94G7Sk/oKyE/Hf35syciEHzqOBl95lk512KSBk1Z76/FocH3Vgdl++V jDc33TvI9V2y3OdFH0AOyRU9ov04l8x6ikvEjUmVZ5DZ7+nt52wp5/oNn/e7+dD8/A tK958yF6v+jWYAzdkMf93z9gC7/R2DOzUBlKM4WQLI2GrYIqvvqpr8wizlHDDywwf1 HZOfk7mqFViXHfr94lW2+/YMapvXBioapqOziK9NXpmmTcMhDgedpXsWdKweODE9vT +Lp/GRc6kMyqw== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:45 +0100 Subject: [PATCH v9 17/27] drm/tests: Add tests for Broadcast RGB property MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-17-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=13818; i=mripard@kernel.org; h=from:subject:message-id; bh=ETz9nlR0atHV46UY5p7KYA1JJn6mH150Vspr8N/BJpw=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpfi11rpdWv29mCnqcGPLnj9OjJqy/4oL8hqSGRt4n 3eJZZt1lLIwiHExyIopssQImy+JOzXrdScb3zyYOaxMIEMYuDgFYCIv9Rj+cJsfU/q/YOEOFz7p N/P5p5e//XpyScmJy/WNZyT/bDr23orhr8BJrYhsmzBNTT2RCwxhjhaTszZn9R9gZ5lQYVbhn/m eEwA= 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" This had a bunch of kunit tests to make sure our code to handle the Broadcast RGB property behaves properly. This requires bringing a bit of infrastructure to create mock HDMI connectors, with custom EDIDs. Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard --- .../gpu/drm/tests/drm_atomic_state_helper_test.c | 151 +++++++++++++++++++++ drivers/gpu/drm/tests/drm_connector_test.c | 116 ++++++++++++++++ 2 files changed, 267 insertions(+) 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 c0f26422e925..3de15cec2f5f 100644 --- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c @@ -222,10 +222,138 @@ drm_atomic_helper_connector_hdmi_init(struct kunit *test, KUNIT_ASSERT_EQ(test, ret, 0); return priv; } +/* + * Test that if we change the RGB quantization property to a different + * value, we trigger a mode change on the connector's CRTC, which will + * in turn disable/enable the connector. + */ +static void drm_test_check_broadcast_rgb_crtc_mode_changed(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *old_conn_state; + struct drm_connector_state *new_conn_state; + struct drm_crtc_state *crtc_state; + struct drm_atomic_state *state; + struct drm_display_mode *preferred; + 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); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + new_conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state); + + old_conn_state = drm_atomic_get_old_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state); + + new_conn_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_FULL; + + KUNIT_ASSERT_NE(test, + old_conn_state->hdmi.broadcast_rgb, + new_conn_state->hdmi.broadcast_rgb); + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + new_conn_state = drm_atomic_get_new_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state); + KUNIT_EXPECT_EQ(test, new_conn_state->hdmi.broadcast_rgb, DRM_HDMI_BROADCAST_RGB_FULL); + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + KUNIT_EXPECT_TRUE(test, crtc_state->mode_changed); +} + +/* + * Test that if we set the RGB quantization property to the same value, + * we don't trigger a mode change on the connector's CRTC and leave the + * connector unaffected. + */ +static void drm_test_check_broadcast_rgb_crtc_mode_not_changed(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *old_conn_state; + struct drm_connector_state *new_conn_state; + struct drm_crtc_state *crtc_state; + struct drm_atomic_state *state; + struct drm_display_mode *preferred; + 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); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + new_conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state); + + old_conn_state = drm_atomic_get_old_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state); + + new_conn_state->hdmi.broadcast_rgb = old_conn_state->hdmi.broadcast_rgb; + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + old_conn_state = drm_atomic_get_old_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state); + + new_conn_state = drm_atomic_get_new_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state); + + KUNIT_EXPECT_EQ(test, + old_conn_state->hdmi.broadcast_rgb, + new_conn_state->hdmi.broadcast_rgb); + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + KUNIT_EXPECT_FALSE(test, crtc_state->mode_changed); +} + /* * Test that if we change the maximum bpc property to a different value, * we trigger a mode change on the connector's CRTC, which will in turn * disable/enable the connector. */ @@ -1057,10 +1185,12 @@ static void drm_test_check_output_bpc_format_display_8bpc_only(struct kunit *tes KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8); KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); } static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = { + 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_max_tmds_rate_bpc_fallback), KUNIT_CASE(drm_test_check_max_tmds_rate_format_fallback), KUNIT_CASE(drm_test_check_output_bpc_crtc_mode_changed), KUNIT_CASE(drm_test_check_output_bpc_crtc_mode_not_changed), @@ -1088,10 +1218,30 @@ static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = { static struct kunit_suite drm_atomic_helper_connector_hdmi_check_test_suite = { .name = "drm_atomic_helper_connector_hdmi_check", .test_cases = drm_atomic_helper_connector_hdmi_check_tests, }; +/* + * Test that the value of the Broadcast RGB property out of reset is set + * to auto. + */ +static void drm_test_check_broadcast_rgb_value(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_connector_state *conn_state; + struct drm_connector *conn; + + priv = drm_atomic_helper_connector_hdmi_init(test, + BIT(HDMI_COLORSPACE_RGB), + 8); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + conn_state = conn->state; + KUNIT_EXPECT_EQ(test, conn_state->hdmi.broadcast_rgb, DRM_HDMI_BROADCAST_RGB_AUTO); +} + /* * Test that if the connector was initialised with a maximum bpc of 8, * the value of the max_bpc and max_requested_bpc properties out of * reset are also set to 8, and output_bpc is set to 0 and will be * filled at atomic_check time. @@ -1205,10 +1355,11 @@ static void drm_test_check_tmds_char_value(struct kunit *test) conn_state = conn->state; KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, 0); } static struct kunit_case drm_atomic_helper_connector_hdmi_reset_tests[] = { + KUNIT_CASE(drm_test_check_broadcast_rgb_value), KUNIT_CASE(drm_test_check_bpc_8_value), KUNIT_CASE(drm_test_check_bpc_10_value), KUNIT_CASE(drm_test_check_bpc_12_value), KUNIT_CASE(drm_test_check_format_value), KUNIT_CASE(drm_test_check_tmds_char_value), diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c index ac4b8d00e992..d5320a089887 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -562,10 +562,67 @@ static struct kunit_case drm_get_tv_mode_from_name_tests[] = { static struct kunit_suite drm_get_tv_mode_from_name_test_suite = { .name = "drm_get_tv_mode_from_name", .test_cases = drm_get_tv_mode_from_name_tests, }; +struct drm_hdmi_connector_get_broadcast_rgb_name_test { + unsigned int kind; + const char *expected_name; +}; + +#define BROADCAST_RGB_TEST(_kind, _name) \ + { \ + .kind = _kind, \ + .expected_name = _name, \ + } + +static void drm_test_drm_hdmi_connector_get_broadcast_rgb_name(struct kunit *test) +{ + const struct drm_hdmi_connector_get_broadcast_rgb_name_test *params = + test->param_value; + + KUNIT_EXPECT_STREQ(test, + drm_hdmi_connector_get_broadcast_rgb_name(params->kind), + params->expected_name); +} + +static const +struct drm_hdmi_connector_get_broadcast_rgb_name_test +drm_hdmi_connector_get_broadcast_rgb_name_valid_tests[] = { + BROADCAST_RGB_TEST(DRM_HDMI_BROADCAST_RGB_AUTO, "Automatic"), + BROADCAST_RGB_TEST(DRM_HDMI_BROADCAST_RGB_FULL, "Full"), + BROADCAST_RGB_TEST(DRM_HDMI_BROADCAST_RGB_LIMITED, "Limited 16:235"), +}; + +static void +drm_hdmi_connector_get_broadcast_rgb_name_valid_desc(const struct drm_hdmi_connector_get_broadcast_rgb_name_test *t, + char *desc) +{ + sprintf(desc, "%s", t->expected_name); +} + +KUNIT_ARRAY_PARAM(drm_hdmi_connector_get_broadcast_rgb_name_valid, + drm_hdmi_connector_get_broadcast_rgb_name_valid_tests, + drm_hdmi_connector_get_broadcast_rgb_name_valid_desc); + +static void drm_test_drm_hdmi_connector_get_broadcast_rgb_name_invalid(struct kunit *test) +{ + KUNIT_EXPECT_NULL(test, drm_hdmi_connector_get_broadcast_rgb_name(3)); +}; + +static struct kunit_case drm_hdmi_connector_get_broadcast_rgb_name_tests[] = { + KUNIT_CASE_PARAM(drm_test_drm_hdmi_connector_get_broadcast_rgb_name, + drm_hdmi_connector_get_broadcast_rgb_name_valid_gen_params), + KUNIT_CASE(drm_test_drm_hdmi_connector_get_broadcast_rgb_name_invalid), + { } +}; + +static struct kunit_suite drm_hdmi_connector_get_broadcast_rgb_name_test_suite = { + .name = "drm_hdmi_connector_get_broadcast_rgb_name", + .test_cases = drm_hdmi_connector_get_broadcast_rgb_name_tests, +}; + struct drm_hdmi_connector_get_output_format_name_test { unsigned int kind; const char *expected_name; }; @@ -620,10 +677,67 @@ static struct kunit_case drm_hdmi_connector_get_output_format_name_tests[] = { static struct kunit_suite drm_hdmi_connector_get_output_format_name_test_suite = { .name = "drm_hdmi_connector_get_output_format_name", .test_cases = drm_hdmi_connector_get_output_format_name_tests, }; +static void drm_test_drm_connector_attach_broadcast_rgb_property(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + struct drm_connector *connector = &priv->connector; + struct drm_property *prop; + int ret; + + ret = drmm_connector_init(&priv->drm, connector, + &dummy_funcs, + DRM_MODE_CONNECTOR_HDMIA, + &priv->ddc); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = drm_connector_attach_broadcast_rgb_property(connector); + KUNIT_ASSERT_EQ(test, ret, 0); + + prop = connector->broadcast_rgb_property; + KUNIT_ASSERT_NOT_NULL(test, prop); + KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id)); +} + +static void drm_test_drm_connector_attach_broadcast_rgb_property_hdmi_connector(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + struct drm_connector *connector = &priv->connector; + struct drm_property *prop; + 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); + + ret = drm_connector_attach_broadcast_rgb_property(connector); + KUNIT_ASSERT_EQ(test, ret, 0); + + prop = connector->broadcast_rgb_property; + KUNIT_ASSERT_NOT_NULL(test, prop); + KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id)); +} + +static struct kunit_case drm_connector_attach_broadcast_rgb_property_tests[] = { + KUNIT_CASE(drm_test_drm_connector_attach_broadcast_rgb_property), + KUNIT_CASE(drm_test_drm_connector_attach_broadcast_rgb_property_hdmi_connector), + { } +}; + +static struct kunit_suite drm_connector_attach_broadcast_rgb_property_test_suite = { + .name = "drm_connector_attach_broadcast_rgb_property", + .init = drm_test_connector_init, + .test_cases = drm_connector_attach_broadcast_rgb_property_tests, +}; + /* * Test that for a given mode, with 8bpc and an RGB output the TMDS * character rate is equal to the mode pixel clock. */ static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb(struct kunit *test) @@ -943,12 +1057,14 @@ static struct kunit_suite drm_connector_hdmi_compute_mode_clock_test_suite = { }; kunit_test_suites( &drmm_connector_hdmi_init_test_suite, &drmm_connector_init_test_suite, + &drm_connector_attach_broadcast_rgb_property_test_suite, &drm_connector_hdmi_compute_mode_clock_test_suite, &drm_get_tv_mode_from_name_test_suite, + &drm_hdmi_connector_get_broadcast_rgb_name_test_suite, &drm_hdmi_connector_get_output_format_name_test_suite ); MODULE_AUTHOR("Maxime Ripard "); MODULE_LICENSE("GPL"); From patchwork Mon Mar 11 14:49:46 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588847 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 04149C54E58 for ; Mon, 11 Mar 2024 14:50:57 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 309C3112ACE; Mon, 11 Mar 2024 14:50:56 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="HHLhhgL0"; 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 68A3B112ACE for ; Mon, 11 Mar 2024 14:50:53 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id D1B5960C73; Mon, 11 Mar 2024 14:50:52 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 27697C43390; Mon, 11 Mar 2024 14:50:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168652; bh=PB5uZMP81Svihtcjmn3oN1XmVQnFwiL8nPkDTykrcLI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=HHLhhgL0z6zZ58+62ouYTmjVoG++B2jJO91V/QTUckdDVZKE2TTJOI0ix5eU+IcMy WlO5iQDfKEzjPrLxjzCirmoPJvpZyNZLQOgevwX1jY3xQT6NsIJaKB+rh3M9/BqNTi 2YaAN9jJI2yDBaLFM9DrTkBYLdAQjyzp7PJFov/rv0Q8gH2xxKVUBsenFWOifXYo/3 Dn1X/SgElh3m1AiZ1s+ux0+Pn5Jc07Bv0OoU1ZqzKv/1CPFVF43acATdA+tgdbVy8i bx4dhP9B0ZwtEJTGZFTql9b35XontsPz/WV9CPbpI9Ds5og5FRHLP8R78PitMKQtu3 Dox4sli7kYk8Q== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:46 +0100 Subject: [PATCH v9 18/27] drm/connector: hdmi: Add RGB Quantization Range to the connector state MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-18-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=4014; i=mripard@kernel.org; h=from:subject:message-id; bh=PB5uZMP81Svihtcjmn3oN1XmVQnFwiL8nPkDTykrcLI=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpQSOr9k4Kyap8/PUucy/P5et4puWyvJxd6dl7xOt/ 4LpwqnhHaUsDGJcDLJiiiwxwuZL4k7Net3JxjcPZg4rE8gQBi5OAZiIWxIjQ4dIyLcV6u7sm8L4 Yj00TqhKteitDzAV/3o5qvLjhg+8QQz/nbjDdhUvVlT7xuHcvlZd+ObjDVa8Bx/PM9+/4PyV/Mp CTgA= 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" HDMI controller drivers will need to figure out the RGB range they need to configure based on a mode and property values. Let's expose that in the HDMI connector state so drivers can just use that value. Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic.c | 1 + drivers/gpu/drm/drm_atomic_state_helper.c | 29 +++++++++++++++++++++++++++++ include/drm/drm_connector.h | 6 ++++++ 3 files changed, 36 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 3e57d98d8418..ec6c6beda5c9 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1145,10 +1145,11 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) { drm_printf(p, "\tbroadcast_rgb=%s\n", drm_hdmi_connector_get_broadcast_rgb_name(state->hdmi.broadcast_rgb)); + drm_printf(p, "\tis_full_range=%c\n", state->hdmi.is_full_range ? 'y' : 'n'); drm_printf(p, "\toutput_bpc=%u\n", state->hdmi.output_bpc); drm_printf(p, "\toutput_format=%s\n", drm_hdmi_connector_get_output_format_name(state->hdmi.output_format)); drm_printf(p, "\ttmds_char_rate=%llu\n", state->hdmi.tmds_char_rate); } diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 0e8fb653965a..e66272c0d006 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -662,10 +662,37 @@ connector_state_get_mode(const struct drm_connector_state *conn_state) return NULL; return &crtc_state->mode; } +static bool hdmi_is_full_range(const struct drm_connector *connector, + const struct drm_connector_state *state) +{ + const struct drm_display_info *display = &connector->display_info; + const struct drm_display_mode *mode = + connector_state_get_mode(state); + + /* + * The Broadcast RGB property only applies to RGB format, and + * i915 just assumes limited range for YCbCr output, so let's + * just do the same. + */ + if (state->hdmi.output_format != HDMI_COLORSPACE_RGB) + return false; + + if (state->hdmi.broadcast_rgb == DRM_HDMI_BROADCAST_RGB_FULL) + return true; + + if (state->hdmi.broadcast_rgb == DRM_HDMI_BROADCAST_RGB_LIMITED) + return false; + + if (!display->is_hdmi) + return true; + + return drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_FULL ? true : false; +} + static bool sink_supports_format_bpc(const struct drm_connector *connector, const struct drm_display_info *info, const struct drm_display_mode *mode, unsigned int format, unsigned int bpc) @@ -908,10 +935,12 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector, drm_atomic_get_new_connector_state(state, connector); const struct drm_display_mode *mode = connector_state_get_mode(new_state); int ret; + new_state->hdmi.is_full_range = hdmi_is_full_range(connector, new_state); + ret = hdmi_compute_config(connector, new_state, mode); if (ret) return ret; if (old_state->hdmi.broadcast_rgb != new_state->hdmi.broadcast_rgb || diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index bb6b6a36ade3..3eaf4d54364d 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1068,10 +1068,16 @@ struct drm_connector_state { * @broadcast_rgb: Connector property to pass the * Broadcast RGB selection value. */ enum drm_hdmi_broadcast_rgb broadcast_rgb; + /** + * @is_full_range: Is the output supposed to use a full + * RGB Quantization Range or not? + */ + bool is_full_range; + /** * @output_bpc: Bits per color channel to output. */ unsigned int output_bpc; From patchwork Mon Mar 11 14:49:47 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588848 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 EB41AC54E68 for ; Mon, 11 Mar 2024 14:51:00 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id D96DB112AD4; Mon, 11 Mar 2024 14:50:59 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="BLUXWSrN"; 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 55657112ACF for ; Mon, 11 Mar 2024 14:50:56 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id 8C39260EAD; Mon, 11 Mar 2024 14:50:55 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id E0F81C43394; Mon, 11 Mar 2024 14:50:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168655; bh=hdQFimYEoL1NKCgBJcqXUYcz0R/w6fqejvd5drDOq3w=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=BLUXWSrNEgGAhMyooRP8XizI3B6TeWhaYcK6UcjrPIuh6RFQ4vZbleS46e0OKdJen uKu1/IbmY6QHXuC6tWNkIJ4czVqcn6Fr7O6of49inM9tqJhNvcOvgT4w/kvxhPF/9R 40w3uRukPc3gj3B4KyX6YXvvp/qSmnD9qA9BWHJehBVzh7U+sC8luAnsn9PWdPOV3Z cv3f78IkQPC507WUlWc20VNzRQZpPROaOD/ZXwxJa8yiTKwXRsM566Xi6cZMXcJdzj OqHmB3ARm6DlwHZltLRtEgehM+uYrbii9dmhFHyv5NkprmZbzmeUV3aBBClN2NDwyI XLKYmUeuKtSrg== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:47 +0100 Subject: [PATCH v9 19/27] drm/tests: Add RGB Quantization tests MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-19-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=20952; i=mripard@kernel.org; h=from:subject:message-id; bh=hdQFimYEoL1NKCgBJcqXUYcz0R/w6fqejvd5drDOq3w=; b=kA0DAAoW4+8Nb2cYUcUByyZiAGXvGhCjUGWnqbF0JDPdbQhFZPdtW8ODmyuvIqhaYUAC5ihoY 4h1BAAWCgAdFiEEXBM3pF7KmuuJBg6e4+8Nb2cYUcUFAmXvGhAACgkQ4+8Nb2cYUcXb1AD9E/wS s81XFGERBRuv8T1/HpNWIgkG1ngv5JVduPo7VhQBAPt+Ju1kvGpJxD9oIQSnSuvi3FEabpD261M iwgBHYM8M 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" The previous commit added the infrastructure to the connector state to track what RGB Quantization should be used in a given state for an HDMI connector. Let's add some kunit tests to make sure it works as expected. Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard --- .../gpu/drm/tests/drm_atomic_state_helper_test.c | 563 +++++++++++++++++++++ 1 file changed, 563 insertions(+) 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 3de15cec2f5f..dcc0f7486f49 100644 --- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c @@ -350,10 +350,564 @@ static void drm_test_check_broadcast_rgb_crtc_mode_not_changed(struct kunit *tes crtc_state = drm_atomic_get_new_crtc_state(state, crtc); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); KUNIT_EXPECT_FALSE(test, crtc_state->mode_changed); } +/* + * Test that for an HDMI connector, with an HDMI monitor, if the + * Broadcast RGB property is set to auto with a mode that isn't the + * VIC-1 mode, we will get a limited RGB Quantization Range. + */ +static void drm_test_check_broadcast_rgb_auto_cea_mode(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_atomic_state *state; + struct drm_display_mode *preferred; + 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); + + conn = &priv->connector; + KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + KUNIT_ASSERT_NE(test, drm_match_cea_mode(preferred), 1); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + KUNIT_ASSERT_EQ(test, ret, 0); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + KUNIT_ASSERT_EQ(test, + conn_state->hdmi.broadcast_rgb, + DRM_HDMI_BROADCAST_RGB_AUTO); + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_full_range); +} + +/* + * Test that for an HDMI connector, with an HDMI monitor, if the + * Broadcast RGB property is set to auto with a VIC-1 mode, we will get + * a full RGB Quantization Range. + */ +static void drm_test_check_broadcast_rgb_auto_cea_mode_vic_1(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_atomic_state *state; + struct drm_display_mode *mode; + 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); + + conn = &priv->connector; + KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + mode = drm_display_mode_from_cea_vic(drm, 1); + KUNIT_ASSERT_NOT_NULL(test, mode); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, mode, ctx); + KUNIT_ASSERT_EQ(test, ret, 0); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + KUNIT_ASSERT_EQ(test, + conn_state->hdmi.broadcast_rgb, + DRM_HDMI_BROADCAST_RGB_AUTO); + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + KUNIT_EXPECT_TRUE(test, conn_state->hdmi.is_full_range); +} + +/* + * Test that for an HDMI connector, with an HDMI monitor, if the + * Broadcast RGB property is set to full with a mode that isn't the + * VIC-1 mode, we will get a full RGB Quantization Range. + */ +static void drm_test_check_broadcast_rgb_full_cea_mode(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_atomic_state *state; + struct drm_display_mode *preferred; + 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); + + conn = &priv->connector; + KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + KUNIT_ASSERT_NE(test, drm_match_cea_mode(preferred), 1); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + KUNIT_ASSERT_EQ(test, ret, 0); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + conn_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_FULL; + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + KUNIT_ASSERT_EQ(test, + conn_state->hdmi.broadcast_rgb, + DRM_HDMI_BROADCAST_RGB_FULL); + + KUNIT_EXPECT_TRUE(test, conn_state->hdmi.is_full_range); +} + +/* + * Test that for an HDMI connector, with an HDMI monitor, if the + * Broadcast RGB property is set to full with a VIC-1 mode, we will get + * a full RGB Quantization Range. + */ +static void drm_test_check_broadcast_rgb_full_cea_mode_vic_1(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_atomic_state *state; + struct drm_display_mode *mode; + 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); + + conn = &priv->connector; + KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + mode = drm_display_mode_from_cea_vic(drm, 1); + KUNIT_ASSERT_NOT_NULL(test, mode); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, mode, ctx); + KUNIT_ASSERT_EQ(test, ret, 0); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + conn_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_FULL; + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + KUNIT_ASSERT_EQ(test, + conn_state->hdmi.broadcast_rgb, + DRM_HDMI_BROADCAST_RGB_FULL); + + KUNIT_EXPECT_TRUE(test, conn_state->hdmi.is_full_range); +} + +/* + * Test that for an HDMI connector, with an HDMI monitor, if the + * Broadcast RGB property is set to limited with a mode that isn't the + * VIC-1 mode, we will get a limited RGB Quantization Range. + */ +static void drm_test_check_broadcast_rgb_limited_cea_mode(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_atomic_state *state; + struct drm_display_mode *preferred; + 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); + + conn = &priv->connector; + KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + KUNIT_ASSERT_NE(test, drm_match_cea_mode(preferred), 1); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + KUNIT_ASSERT_EQ(test, ret, 0); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + conn_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_LIMITED; + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + KUNIT_ASSERT_EQ(test, + conn_state->hdmi.broadcast_rgb, + DRM_HDMI_BROADCAST_RGB_LIMITED); + + KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_full_range); +} + +/* + * Test that for an HDMI connector, with an HDMI monitor, if the + * Broadcast RGB property is set to limited with a VIC-1 mode, we will + * get a limited RGB Quantization Range. + */ +static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_atomic_state *state; + struct drm_display_mode *mode; + 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); + + conn = &priv->connector; + KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + mode = drm_display_mode_from_cea_vic(drm, 1); + KUNIT_ASSERT_NOT_NULL(test, mode); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, mode, ctx); + KUNIT_ASSERT_EQ(test, ret, 0); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + conn_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_LIMITED; + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + KUNIT_ASSERT_EQ(test, + conn_state->hdmi.broadcast_rgb, + DRM_HDMI_BROADCAST_RGB_LIMITED); + + KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_full_range); +} + +/* + * Test that if we have an HDMI connector that would use a YUV output, + * we always get a limited quantization range with the Broadcast RGB + * property set to auto. + */ +static void drm_test_check_broadcast_rgb_auto_yuv(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_atomic_state *state; + struct drm_display_info *info; + struct drm_display_mode *preferred; + unsigned long long rate; + 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) | + BIT(HDMI_COLORSPACE_YUV422), + 12); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + ret = set_connector_edid(test, conn, + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); + KUNIT_ASSERT_EQ(test, ret, 0); + + info = &conn->display_info; + KUNIT_ASSERT_TRUE(test, info->is_hdmi); + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK); + + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB); + KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000); + + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422); + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + KUNIT_EXPECT_EQ(test, ret, 0); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + conn_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO; + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_YUV422); + KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_full_range); +} + +/* + * Test that if we have an HDMI connector that would use a YUV output, + * we always get a limited quantization range even if we have the + * Broadcast RGB property set to full. + */ +static void drm_test_check_broadcast_rgb_full_yuv(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_atomic_state *state; + struct drm_display_info *info; + struct drm_display_mode *preferred; + unsigned long long rate; + 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) | + BIT(HDMI_COLORSPACE_YUV422), + 12); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + ret = set_connector_edid(test, conn, + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); + KUNIT_ASSERT_EQ(test, ret, 0); + + info = &conn->display_info; + KUNIT_ASSERT_TRUE(test, info->is_hdmi); + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK); + + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB); + KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000); + + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422); + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + KUNIT_EXPECT_EQ(test, ret, 0); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + conn_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_FULL; + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_YUV422); + KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_full_range); +} + +/* + * Test that if we have an HDMI connector that would use a YUV output, + * we always get a limited quantization range even if we have the + * Broadcast RGB property set to full. + */ +static void drm_test_check_broadcast_rgb_limited_yuv(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_modeset_acquire_ctx *ctx; + struct drm_connector_state *conn_state; + struct drm_atomic_state *state; + struct drm_display_info *info; + struct drm_display_mode *preferred; + unsigned long long rate; + 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) | + BIT(HDMI_COLORSPACE_YUV422), + 12); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + ret = set_connector_edid(test, conn, + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); + KUNIT_ASSERT_EQ(test, ret, 0); + + info = &conn->display_info; + KUNIT_ASSERT_TRUE(test, info->is_hdmi); + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0); + + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK); + + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB); + KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000); + + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422); + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000); + + drm = &priv->drm; + crtc = priv->crtc; + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); + KUNIT_EXPECT_EQ(test, ret, 0); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + conn_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_LIMITED; + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_YUV422); + KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_full_range); +} + /* * Test that if we change the maximum bpc property to a different value, * we trigger a mode change on the connector's CRTC, which will in turn * disable/enable the connector. */ @@ -1185,10 +1739,19 @@ static void drm_test_check_output_bpc_format_display_8bpc_only(struct kunit *tes KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8); KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); } 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), + KUNIT_CASE(drm_test_check_broadcast_rgb_auto_yuv), + KUNIT_CASE(drm_test_check_broadcast_rgb_full_cea_mode), + KUNIT_CASE(drm_test_check_broadcast_rgb_full_cea_mode_vic_1), + KUNIT_CASE(drm_test_check_broadcast_rgb_full_yuv), + KUNIT_CASE(drm_test_check_broadcast_rgb_limited_cea_mode), + KUNIT_CASE(drm_test_check_broadcast_rgb_limited_cea_mode_vic_1), + KUNIT_CASE(drm_test_check_broadcast_rgb_limited_yuv), 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_max_tmds_rate_bpc_fallback), KUNIT_CASE(drm_test_check_max_tmds_rate_format_fallback), From patchwork Mon Mar 11 14:49:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588849 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 66B0AC5475B for ; Mon, 11 Mar 2024 14:51:03 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 893D5112AD6; Mon, 11 Mar 2024 14:51:02 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="YRGdDkpY"; dkim-atps=neutral Received: from sin.source.kernel.org (sin.source.kernel.org [145.40.73.55]) by gabe.freedesktop.org (Postfix) with ESMTPS id 85447112AD5 for ; Mon, 11 Mar 2024 14:51:01 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sin.source.kernel.org (Postfix) with ESMTP id CD63BCE10FF; Mon, 11 Mar 2024 14:50:58 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9F200C433F1; Mon, 11 Mar 2024 14:50:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168658; bh=4sxJ9PrbNIwJi9uCvWzSbxMmqOCcxjT6GHGf1mVHocs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=YRGdDkpYQ4ImftOhLr7MYnI2+dHVCiqkyq5epKcgYes0JsKQ6OZKTCZKNBySiDcjV CxKMiBKUdVJeTLoP1+rdE9WoOb4//z7xI3EFZZ7eXrh7kCfeuUw9ceziDwm4B5F2Fw WIdoBeU/7IrFexpA91YryRrFvGOZ0eFcM9yJGkgyGQSTbHO/SLgdcVRq3cY8qmTNbZ 0N7OyKEDW2w3rdHLDwJDm29qvm5lRoiXcWkicF1OULmpZQf/NnTlCjDwY6QfMCe/4Y UuAZj6VHDjNiVGzObxVz8629P+wi8LCaIpnpwoY/cP6opXsLt6+9yf3AlzVvEZCGQF umFss3LdqMG2A== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:48 +0100 Subject: [PATCH v9 20/27] drm/connector: hdmi: Add Infoframes generation MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-20-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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 X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=28416; i=mripard@kernel.org; h=from:subject:message-id; bh=4sxJ9PrbNIwJi9uCvWzSbxMmqOCcxjT6GHGf1mVHocs=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpQQn1lyP6tkTmZgWtXNBXIzG4ooJ00pWX5G6fVzxS NLjul9MHaUsDGJcDLJiiiwxwuZL4k7Net3JxjcPZg4rE8gQBi5OAZiIph3Db/bTVz7uLN7LG72j gslLvPPiAs5rDYX3/0gURMw88mx/xQSG/36KlZ37Tjj8nLg4k//7zd0pp0tez+Xfspr3zubP8nv 1m1kA 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" Infoframes in KMS is usually handled by a bunch of low-level helpers that require quite some boilerplate for drivers. This leads to discrepancies with how drivers generate them, and which are actually sent. Now that we have everything needed to generate them in the HDMI connector state, we can generate them in our common logic so that drivers can simply reuse what we precomputed. Signed-off-by: Maxime Ripard --- drivers/gpu/drm/Kconfig | 1 + drivers/gpu/drm/drm_atomic_state_helper.c | 323 +++++++++++++++++++++ drivers/gpu/drm/drm_connector.c | 14 + .../gpu/drm/tests/drm_atomic_state_helper_test.c | 1 + drivers/gpu/drm/tests/drm_connector_test.c | 12 + include/drm/drm_atomic_state_helper.h | 8 + include/drm/drm_connector.h | 133 +++++++++ 7 files changed, 492 insertions(+) diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 872edb47bb53..ad9c467e20ce 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -97,10 +97,11 @@ config DRM_KUNIT_TEST If in doubt, say "N". config DRM_KMS_HELPER tristate depends on DRM + select DRM_DISPLAY_HDMI_HELPER help CRTC helpers for KMS drivers. config DRM_DEBUG_DP_MST_TOPOLOGY_REFS bool "Enable refcount backtrace history in the DP MST helpers" diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index e66272c0d006..2bf53666fc9d 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -36,10 +36,12 @@ #include #include #include #include +#include + #include #include /** * DOC: atomic state reset and initialization @@ -912,10 +914,143 @@ hdmi_compute_config(const struct drm_connector *connector, } return -EINVAL; } +static int hdmi_generate_avi_infoframe(const struct drm_connector *connector, + struct drm_connector_state *state) +{ + const struct drm_display_mode *mode = + connector_state_get_mode(state); + struct drm_connector_hdmi_infoframe *infoframe = + &state->hdmi.infoframes.avi; + struct hdmi_avi_infoframe *frame = + &infoframe->data.avi; + bool is_full_range = state->hdmi.is_full_range; + enum hdmi_quantization_range rgb_quant_range = + is_full_range ? HDMI_QUANTIZATION_RANGE_FULL : HDMI_QUANTIZATION_RANGE_LIMITED; + int ret; + + ret = drm_hdmi_avi_infoframe_from_display_mode(frame, connector, mode); + if (ret) + return ret; + + frame->colorspace = state->hdmi.output_format; + + drm_hdmi_avi_infoframe_quant_range(frame, connector, mode, rgb_quant_range); + drm_hdmi_avi_infoframe_colorimetry(frame, state); + drm_hdmi_avi_infoframe_bars(frame, state); + + infoframe->set = true; + + return 0; +} + +static int hdmi_generate_spd_infoframe(const struct drm_connector *connector, + struct drm_connector_state *state) +{ + struct drm_connector_hdmi_infoframe *infoframe = + &state->hdmi.infoframes.spd; + struct hdmi_spd_infoframe *frame = + &infoframe->data.spd; + int ret; + + ret = hdmi_spd_infoframe_init(frame, + connector->hdmi.vendor, + connector->hdmi.product); + if (ret) + return ret; + + frame->sdi = HDMI_SPD_SDI_PC; + + infoframe->set = true; + + return 0; +} + +static int hdmi_generate_hdr_infoframe(const struct drm_connector *connector, + struct drm_connector_state *state) +{ + struct drm_connector_hdmi_infoframe *infoframe = + &state->hdmi.infoframes.hdr_drm; + struct hdmi_drm_infoframe *frame = + &infoframe->data.drm; + int ret; + + if (connector->max_bpc < 10) + return 0; + + if (!state->hdr_output_metadata) + return 0; + + ret = drm_hdmi_infoframe_set_hdr_metadata(frame, state); + if (ret) + return ret; + + infoframe->set = true; + + return 0; +} + +static int hdmi_generate_hdmi_vendor_infoframe(const struct drm_connector *connector, + struct drm_connector_state *state) +{ + const struct drm_display_info *info = &connector->display_info; + const struct drm_display_mode *mode = + connector_state_get_mode(state); + struct drm_connector_hdmi_infoframe *infoframe = + &state->hdmi.infoframes.hdmi; + struct hdmi_vendor_infoframe *frame = + &infoframe->data.vendor.hdmi; + int ret; + + if (!info->has_hdmi_infoframe) + return 0; + + ret = drm_hdmi_vendor_infoframe_from_display_mode(frame, connector, mode); + if (ret) + return ret; + + infoframe->set = true; + + return 0; +} + +static int +hdmi_generate_infoframes(const struct drm_connector *connector, + struct drm_connector_state *state) +{ + const struct drm_display_info *info = &connector->display_info; + int ret; + + if (!info->is_hdmi) + return 0; + + ret = hdmi_generate_avi_infoframe(connector, state); + if (ret) + return ret; + + ret = hdmi_generate_spd_infoframe(connector, state); + if (ret) + return ret; + + /* + * Audio Infoframes will be generated by ALSA, and updated by + * drm_atomic_helper_connector_hdmi_update_audio_infoframe(). + */ + + ret = hdmi_generate_hdr_infoframe(connector, state); + if (ret) + return ret; + + ret = hdmi_generate_hdmi_vendor_infoframe(connector, state); + if (ret) + return ret; + + return 0; +} + /** * drm_atomic_helper_connector_hdmi_check() - Helper to check HDMI connector atomic state * @connector: DRM Connector * @state: the DRM State object * @@ -941,10 +1076,14 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector, ret = hdmi_compute_config(connector, new_state, mode); if (ret) return ret; + ret = hdmi_generate_infoframes(connector, new_state); + if (ret) + return ret; + if (old_state->hdmi.broadcast_rgb != new_state->hdmi.broadcast_rgb || old_state->hdmi.output_bpc != new_state->hdmi.output_bpc || old_state->hdmi.output_format != new_state->hdmi.output_format) { struct drm_crtc *crtc = new_state->crtc; struct drm_crtc_state *crtc_state; @@ -958,10 +1097,194 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector, return 0; } EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_check); +#define HDMI_MAX_INFOFRAME_SIZE 29 + +static int clear_device_infoframe(struct drm_connector *connector, + enum hdmi_infoframe_type type) +{ + const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs; + + if (!funcs || !funcs->clear_infoframe) + return 0; + + return funcs->clear_infoframe(connector, type); +} + +static int clear_infoframe(struct drm_connector *connector, + struct drm_connector_hdmi_infoframe *conn_frame, + struct drm_connector_hdmi_infoframe *old_frame) +{ + int ret; + + ret = clear_device_infoframe(connector, old_frame->data.any.type); + if (ret) + return ret; + + memset(old_frame, 0, sizeof(*old_frame)); + + return 0; +} + +static int write_device_infoframe(struct drm_connector *connector, + union hdmi_infoframe *frame) +{ + const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs; + u8 buffer[HDMI_MAX_INFOFRAME_SIZE]; + int len; + + if (!funcs || !funcs->write_infoframe) + return -ENOSYS; + + len = hdmi_infoframe_pack(frame, buffer, sizeof(buffer)); + if (len < 0) + return len; + + return funcs->write_infoframe(connector, frame->any.type, buffer, len); +} + +static int write_infoframe(struct drm_connector *connector, + struct drm_connector_hdmi_infoframe *conn_frame, + struct drm_connector_hdmi_infoframe *new_frame) +{ + int ret; + + ret = write_device_infoframe(connector, &new_frame->data); + if (ret) + return ret; + + if (conn_frame) + memcpy(conn_frame, new_frame, sizeof(*conn_frame)); + + return 0; +} + +static int write_or_clear_infoframe(struct drm_connector *connector, + struct drm_connector_hdmi_infoframe *conn_frame, + struct drm_connector_hdmi_infoframe *old_frame, + struct drm_connector_hdmi_infoframe *new_frame) +{ + if (new_frame->set) + return write_infoframe(connector, conn_frame, new_frame); + + if (old_frame->set && !new_frame->set) + return clear_infoframe(connector, conn_frame, old_frame); + + return 0; +} + +#define UPDATE_INFOFRAME(c, os, ns, i) \ + write_or_clear_infoframe(c, \ + &(c)->hdmi.infoframes.i, \ + &(os)->hdmi.infoframes.i, \ + &(ns)->hdmi.infoframes.i) + +/** + * drm_atomic_helper_connector_hdmi_update_infoframes - Update the Infoframes + * @connector: A pointer to the HDMI connector + * @state: The HDMI connector state to generate the infoframe from + * + * This function is meant for HDMI connector drivers to write their + * infoframes. It will typically be used in a + * @drm_connector_helper_funcs.atomic_enable implementation. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *connector, + struct drm_atomic_state *state) +{ + struct drm_connector_state *old_state = + drm_atomic_get_old_connector_state(state, connector); + struct drm_connector_state *new_state = + drm_atomic_get_new_connector_state(state, connector); + struct drm_display_info *info = &connector->display_info; + int ret; + + if (!info->is_hdmi) + return 0; + + if (!info->has_hdmi_infoframe) + return 0; + + mutex_lock(&connector->hdmi.infoframes.lock); + + ret = UPDATE_INFOFRAME(connector, old_state, new_state, avi); + if (ret) + goto out; + + if (connector->hdmi.infoframes.audio.set) { + ret = write_infoframe(connector, + NULL, + &connector->hdmi.infoframes.audio); + if (ret) + goto out; + } + + ret = UPDATE_INFOFRAME(connector, old_state, new_state, hdr_drm); + if (ret) + goto out; + + ret = UPDATE_INFOFRAME(connector, old_state, new_state, spd); + if (ret) + goto out; + + ret = UPDATE_INFOFRAME(connector, old_state, new_state, hdmi); + if (ret) + goto out; + +out: + mutex_unlock(&connector->hdmi.infoframes.lock); + return ret; +} +EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_update_infoframes); + +#undef UPDATE_INFOFRAME + +/** + * drm_atomic_helper_connector_hdmi_update_audio_infoframe - Update the Audio Infoframe + * @connector: A pointer to the HDMI connector + * @frame: A pointer to the audio infoframe to write + * + * This function is meant for HDMI connector drivers to update their + * audio infoframe. It will typically be used in one of the ALSA hooks + * (most likely prepare). + * + * Returns: + * Zero on success, error code on failure. + */ +int +drm_atomic_helper_connector_hdmi_update_audio_infoframe(struct drm_connector *connector, + struct hdmi_audio_infoframe *frame) +{ + struct drm_connector_hdmi_infoframe infoframe = {}; + struct drm_display_info *info = &connector->display_info; + int ret; + + if (!info->is_hdmi) + return 0; + + if (!info->has_hdmi_infoframe) + return 0; + + memcpy(&infoframe.data, frame, sizeof(infoframe.data)); + infoframe.set = true; + + mutex_lock(&connector->hdmi.infoframes.lock); + + ret = write_infoframe(connector, + &connector->hdmi.infoframes.audio, + &infoframe); + + mutex_unlock(&connector->hdmi.infoframes.lock); + + return ret; +} +EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_update_audio_infoframe); + /** * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state * @connector: connector object * @state: atomic connector state * diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 088e6ba286bf..140728b5d2a5 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -276,10 +276,11 @@ static int __drm_connector_init(struct drm_device *dev, INIT_LIST_HEAD(&connector->global_connector_list_entry); INIT_LIST_HEAD(&connector->probed_modes); INIT_LIST_HEAD(&connector->modes); mutex_init(&connector->mutex); mutex_init(&connector->edid_override_mutex); + mutex_init(&connector->hdmi.infoframes.lock); connector->edid_blob_ptr = NULL; connector->epoch_counter = 0; connector->tile_blob_ptr = NULL; connector->status = connector_status_unknown; connector->display_info.panel_orientation = @@ -454,10 +455,12 @@ 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 + * @vendor: HDMI Controller Vendor name + * @product: HDMI Controller Product name * @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 @@ -474,19 +477,27 @@ EXPORT_SYMBOL(drmm_connector_init); * Returns: * Zero on success, error code on failure. */ int drmm_connector_hdmi_init(struct drm_device *dev, struct drm_connector *connector, + const char *vendor, const char *product, 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) { int ret; + if (!vendor || !product) + return -EINVAL; + + if ((strlen(vendor) > DRM_CONNECTOR_HDMI_VENDOR_LEN) || + (strlen(product) > DRM_CONNECTOR_HDMI_PRODUCT_LEN)) + return -EINVAL; + if (!(connector_type == DRM_MODE_CONNECTOR_HDMIA || connector_type == DRM_MODE_CONNECTOR_HDMIB)) return -EINVAL; if (!supported_formats || !(supported_formats & BIT(HDMI_COLORSPACE_RGB))) @@ -498,10 +509,12 @@ int drmm_connector_hdmi_init(struct drm_device *dev, ret = drmm_connector_init(dev, connector, funcs, connector_type, ddc); if (ret) return ret; connector->hdmi.supported_formats = supported_formats; + strtomem_pad(connector->hdmi.vendor, vendor, 0); + strtomem_pad(connector->hdmi.product, product, 0); /* * drm_connector_attach_max_bpc_property() requires the * connector to have a state. */ @@ -650,10 +663,11 @@ void drm_connector_cleanup(struct drm_connector *connector) WARN_ON(connector->state && !connector->funcs->atomic_destroy_state); if (connector->state && connector->funcs->atomic_destroy_state) connector->funcs->atomic_destroy_state(connector, connector->state); + mutex_destroy(&connector->hdmi.infoframes.lock); mutex_destroy(&connector->mutex); memset(connector, 0, sizeof(*connector)); if (dev->registered) 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 dcc0f7486f49..b28409fe65dd 100644 --- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c @@ -201,10 +201,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, + "Vendor", "Product", &dummy_connector_funcs, &dummy_connector_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, NULL, formats, diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c index d5320a089887..c3f7a3ba6808 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -187,10 +187,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, + "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), @@ -206,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, + "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, NULL, BIT(HDMI_COLORSPACE_RGB), @@ -225,10 +227,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, + "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), @@ -244,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, + "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), @@ -267,10 +271,11 @@ static void drm_test_connector_hdmi_init_bpc_8(struct kunit *test) struct drm_property *prop; uint64_t val; int ret; ret = drmm_connector_hdmi_init(&priv->drm, connector, + "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), @@ -302,10 +307,11 @@ static void drm_test_connector_hdmi_init_bpc_10(struct kunit *test) struct drm_property *prop; uint64_t val; int ret; ret = drmm_connector_hdmi_init(&priv->drm, connector, + "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), @@ -337,10 +343,11 @@ static void drm_test_connector_hdmi_init_bpc_12(struct kunit *test) struct drm_property *prop; uint64_t val; int ret; ret = drmm_connector_hdmi_init(&priv->drm, connector, + "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), @@ -368,10 +375,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, + "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, 0, @@ -387,10 +395,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, + "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, BIT(HDMI_COLORSPACE_YUV422), @@ -407,10 +416,11 @@ static void drm_test_connector_hdmi_init_type_valid(struct kunit *test) struct drm_connector_init_priv *priv = test->priv; unsigned int connector_type = *(unsigned int *)test->param_value; int ret; ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, connector_type, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), @@ -441,10 +451,11 @@ static void drm_test_connector_hdmi_init_type_invalid(struct kunit *test) struct drm_connector_init_priv *priv = test->priv; unsigned int connector_type = *(unsigned int *)test->param_value; int ret; ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, connector_type, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), @@ -706,10 +717,11 @@ static void drm_test_drm_connector_attach_broadcast_rgb_property_hdmi_connector( struct drm_connector *connector = &priv->connector; struct drm_property *prop; int ret; ret = drmm_connector_hdmi_init(&priv->drm, connector, + "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, DRM_MODE_CONNECTOR_HDMIA, &priv->ddc, BIT(HDMI_COLORSPACE_RGB), diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index d59d2b3aef9a..22f083968aa8 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -38,10 +38,12 @@ struct drm_connector_state; struct drm_private_obj; struct drm_private_state; struct drm_modeset_acquire_ctx; struct drm_device; +struct hdmi_audio_infoframe; + void __drm_atomic_helper_crtc_state_reset(struct drm_crtc_state *state, struct drm_crtc *crtc); void __drm_atomic_helper_crtc_reset(struct drm_crtc *crtc, struct drm_crtc_state *state); void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc); @@ -86,10 +88,16 @@ struct drm_connector_state * drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector); void __drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state); void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, struct drm_connector_state *state); + +int drm_atomic_helper_connector_hdmi_update_audio_infoframe(struct drm_connector *connector, + struct hdmi_audio_infoframe *frame); +int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *connector, + struct drm_atomic_state *state); + void __drm_atomic_helper_private_obj_duplicate_state(struct drm_private_obj *obj, struct drm_private_state *state); void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge, struct drm_bridge_state *state); diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 3eaf4d54364d..09d383fa8973 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -912,10 +912,25 @@ struct drm_tv_connector_state { unsigned int overscan; unsigned int saturation; unsigned int hue; }; +/** + * struct drm_connector_hdmi_infoframe - HDMI Infoframe container + */ +struct drm_connector_hdmi_infoframe { + /** + * @data: HDMI Infoframe structure + */ + union hdmi_infoframe data; + + /** + * @set: Is the content of @data valid? + */ + bool set; +}; + /** * struct drm_connector_state - mutable connector state */ struct drm_connector_state { /** @connector: backpointer to the connector */ @@ -1068,10 +1083,39 @@ struct drm_connector_state { * @broadcast_rgb: Connector property to pass the * Broadcast RGB selection value. */ enum drm_hdmi_broadcast_rgb broadcast_rgb; + /** + * @infoframes: HDMI Infoframes matching that state + */ + struct { + /** + * @avi: AVI Infoframes structure matching our + * state. + */ + struct drm_connector_hdmi_infoframe avi; + + /** + * @hdr_drm: DRM (Dynamic Range and Mastering) + * Infoframes structure matching our state. + */ + struct drm_connector_hdmi_infoframe hdr_drm; + + /** + * @spd: SPD Infoframes structure matching our + * state. + */ + struct drm_connector_hdmi_infoframe spd; + + /** + * @vendor: HDMI Vendor Infoframes structure + * matching our state. + */ + struct drm_connector_hdmi_infoframe hdmi; + } infoframes; + /** * @is_full_range: Is the output supposed to use a full * RGB Quantization Range or not? */ bool is_full_range; @@ -1113,10 +1157,45 @@ struct drm_connector_hdmi_funcs { */ enum drm_mode_status (*tmds_char_rate_valid)(const struct drm_connector *connector, const struct drm_display_mode *mode, unsigned long long tmds_rate); + + /** + * @clear_infoframe: + * + * This callback is invoked through + * @drm_atomic_helper_connector_hdmi_update_infoframes during a + * commit to clear the infoframes into the hardware. It will be + * called multiple times, once for every disabled infoframe + * type. + * + * The @clear_infoframe callback is optional. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*clear_infoframe)(struct drm_connector *connector, + enum hdmi_infoframe_type type); + + /** + * @write_infoframe: + * + * This callback is invoked through + * @drm_atomic_helper_connector_hdmi_update_infoframes during a + * commit to program the infoframes into the hardware. It will + * be called multiple times, once for every updated infoframe + * type. + * + * The @write_infoframe callback is mandatory. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*write_infoframe)(struct drm_connector *connector, + enum hdmi_infoframe_type type, + const u8 *buffer, size_t len); }; /** * struct drm_connector_funcs - control connectors on a given device * @@ -1984,20 +2063,73 @@ struct drm_connector { /** * @hdmi: HDMI-related variable and properties. */ struct { +#define DRM_CONNECTOR_HDMI_VENDOR_LEN 8 + /** + * @vendor: HDMI Controller Vendor Name + */ + unsigned char vendor[DRM_CONNECTOR_HDMI_VENDOR_LEN] __nonstring; + +#define DRM_CONNECTOR_HDMI_PRODUCT_LEN 16 + /** + * @product: HDMI Controller Product Name + */ + unsigned char product[DRM_CONNECTOR_HDMI_PRODUCT_LEN] __nonstring; + /** * @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; + + /** + * @infoframes: Current Infoframes output by the connector + */ + struct { + /** + * @lock: Mutex protecting against concurrent access to + * the infoframes, most notably between KMS and ALSA. + */ + struct mutex lock; + + /** + * @audio: Current Audio Infoframes structure. Protected + * by @lock. + */ + struct drm_connector_hdmi_infoframe audio; + + /** + * @avi: Current AVI Infoframes structure. Protected by + * @lock. + */ + struct drm_connector_hdmi_infoframe avi; + + /** + * @hdr_drm: Current DRM (Dynamic Range and Mastering) + * Infoframes structure. Protected by @lock. + */ + struct drm_connector_hdmi_infoframe hdr_drm; + + /** + * @spd: Current SPD Infoframes structure. Protected by + * @lock. + */ + struct drm_connector_hdmi_infoframe spd; + + /** + * @vendor: Current HDMI Vendor Infoframes structure. + * Protected by @lock. + */ + struct drm_connector_hdmi_infoframe hdmi; + } infoframes; } hdmi; }; #define obj_to_connector(x) container_of(x, struct drm_connector, base) @@ -2015,10 +2147,11 @@ int drmm_connector_init(struct drm_device *dev, const struct drm_connector_funcs *funcs, int connector_type, struct i2c_adapter *ddc); int drmm_connector_hdmi_init(struct drm_device *dev, struct drm_connector *connector, + const char *vendor, const char *product, 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, From patchwork Mon Mar 11 14:49:49 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588850 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 3E9ABC54E58 for ; Mon, 11 Mar 2024 14:51:04 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id F2AA9112ABE; Mon, 11 Mar 2024 14:51:02 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="hckSBHhE"; 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 D9A38112AD6 for ; Mon, 11 Mar 2024 14:51:01 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id 3AEC660C73; Mon, 11 Mar 2024 14:51:01 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8F010C433F1; Mon, 11 Mar 2024 14:51:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168660; bh=wuvkj6Z9EdEp0Xg/aV0m4CQIRW41OvrOKoTXm5nw22I=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=hckSBHhEWCF8IDKQoFFJ4oIbrhhFFW/dSrvxgBB3Eh4syspYnUA6Tv1JJHv3xzxNU 8ZTeQ7NP2nZu94uuFDlMukV78w/rikx89RFKMsFW8ex78wPhvzhARVWQmGZchIzh2I 3jmAZAmxS4QmIwbH09NQ0aZ5MMi3rZSXahrLsLKEZkwzI+kUoYrMQvrISrnSlka3rL 5ioC2anobozLu5GPZEwBHPaPKz+pqY+stCLMUgQJK4abDxHd91L4ke8gib7FJ2R8CL L087zW37NatpOR6mwNNnuKE12x90tlfccK2iMRJ+aDSkFvGNCmBjlRwI6+jNe/PyoW ac4o5FzT0WWfQ== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:49 +0100 Subject: [PATCH v9 21/27] drm/tests: Add infoframes test MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-21-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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 X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=8730; i=mripard@kernel.org; h=from:subject:message-id; bh=wuvkj6Z9EdEp0Xg/aV0m4CQIRW41OvrOKoTXm5nw22I=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpYTSJzs/PLZ7rt1n0Uj7z+lyEUc3NTS/6rtbuncFm 46ycXBIRykLgxgXg6yYIkuMsPmSuFOzXney8c2DmcPKBDKEgYtTACbCsY+RYbf7u868DNnAmbWR Mt9b8g8seX1xoWGiT33m/caF77UvRTH8U1+cH7Omkvn29Wccb6I1olfPbeiPjvt+iT2hJE2qwiu bHwA= 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" The previous patch added the generation of the infoframes matching an HDMI connector state. Let's add a few tests to make sure it works as expected. Signed-off-by: Maxime Ripard --- drivers/gpu/drm/tests/drm_connector_test.c | 219 +++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c index c3f7a3ba6808..fd64b2d2a60e 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -217,10 +217,221 @@ static void drm_test_connector_hdmi_init_null_ddc(struct kunit *test) BIT(HDMI_COLORSPACE_RGB), 8); KUNIT_EXPECT_EQ(test, ret, 0); } +/* + * Test that the registration of an HDMI connector with a NULL vendor + * fails. + */ +static void drm_test_connector_hdmi_init_null_vendor(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + int ret; + + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + NULL, "Product", + &dummy_funcs, + &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 an HDMI connector with a NULL product + * fails. + */ +static void drm_test_connector_hdmi_init_null_product(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + int ret; + + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + "Vendor", NULL, + &dummy_funcs, + &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 with a valid, shorter than + * the max length, product name succeeds, and is stored padded with 0. + */ +static void drm_test_connector_hdmi_init_product_valid(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const unsigned char expected_product[DRM_CONNECTOR_HDMI_PRODUCT_LEN] = { + 'P', 'r', 'o', 'd', + }; + const char *product_name = "Prod"; + int ret; + + KUNIT_ASSERT_LT(test, strlen(product_name), DRM_CONNECTOR_HDMI_PRODUCT_LEN); + + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + "Vendor", product_name, + &dummy_funcs, + &dummy_hdmi_funcs, + DRM_MODE_CONNECTOR_HDMIA, + &priv->ddc, + BIT(HDMI_COLORSPACE_RGB), + 8); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_MEMEQ(test, + priv->connector.hdmi.product, + expected_product, + sizeof(priv->connector.hdmi.product)); +} + +/* + * Test that the registration of a connector with a valid, at max + * length, product name succeeds, and is stored padded without any + * trailing \0. + */ +static void drm_test_connector_hdmi_init_product_length_exact(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const unsigned char expected_product[DRM_CONNECTOR_HDMI_PRODUCT_LEN] = { + 'P', 'r', 'o', 'd', 'u', 'c', 't', + 'P', 'r', 'o', 'd', 'u', 'c', 't', + 'P', 'r', + }; + const char *product_name = "ProductProductPr"; + int ret; + + KUNIT_ASSERT_EQ(test, strlen(product_name), DRM_CONNECTOR_HDMI_PRODUCT_LEN); + + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + "Vendor", product_name, + &dummy_funcs, + &dummy_hdmi_funcs, + DRM_MODE_CONNECTOR_HDMIA, + &priv->ddc, + BIT(HDMI_COLORSPACE_RGB), + 8); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_MEMEQ(test, + priv->connector.hdmi.product, + expected_product, + sizeof(priv->connector.hdmi.product)); +} + +/* + * Test that the registration of a connector with a product name larger + * than the maximum length fails. + */ +static void drm_test_connector_hdmi_init_product_length_too_long(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const char *product_name = "ProductProductProduct"; + int ret; + + KUNIT_ASSERT_GT(test, strlen(product_name), DRM_CONNECTOR_HDMI_PRODUCT_LEN); + + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + "Vendor", product_name, + &dummy_funcs, + &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 with a vendor name smaller + * than the maximum length succeeds, and is stored padded with zeros. + */ +static void drm_test_connector_hdmi_init_vendor_valid(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const char expected_vendor[DRM_CONNECTOR_HDMI_VENDOR_LEN] = { + 'V', 'e', 'n', 'd', + }; + const char *vendor_name = "Vend"; + int ret; + + KUNIT_ASSERT_LT(test, strlen(vendor_name), DRM_CONNECTOR_HDMI_VENDOR_LEN); + + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + vendor_name, "Product", + &dummy_funcs, + &dummy_hdmi_funcs, + DRM_MODE_CONNECTOR_HDMIA, + &priv->ddc, + BIT(HDMI_COLORSPACE_RGB), + 8); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_MEMEQ(test, + priv->connector.hdmi.vendor, + expected_vendor, + sizeof(priv->connector.hdmi.vendor)); +} + +/* + * Test that the registration of a connector with a vendor name at the + * maximum length succeeds, and is stored padded without the trailing + * zero. + */ +static void drm_test_connector_hdmi_init_vendor_length_exact(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const char expected_vendor[DRM_CONNECTOR_HDMI_VENDOR_LEN] = { + 'V', 'e', 'n', 'd', 'o', 'r', + 'V', 'e', + }; + const char *vendor_name = "VendorVe"; + int ret; + + KUNIT_ASSERT_EQ(test, strlen(vendor_name), DRM_CONNECTOR_HDMI_VENDOR_LEN); + + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + vendor_name, "Product", + &dummy_funcs, + &dummy_hdmi_funcs, + DRM_MODE_CONNECTOR_HDMIA, + &priv->ddc, + BIT(HDMI_COLORSPACE_RGB), + 8); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_MEMEQ(test, + priv->connector.hdmi.vendor, + expected_vendor, + sizeof(priv->connector.hdmi.vendor)); +} + +/* + * Test that the registration of a connector with a vendor name larger + * than the maximum length fails. + */ +static void drm_test_connector_hdmi_init_vendor_length_too_long(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + const char *vendor_name = "VendorVendor"; + int ret; + + KUNIT_ASSERT_GT(test, strlen(vendor_name), DRM_CONNECTOR_HDMI_VENDOR_LEN); + + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + vendor_name, "Product", + &dummy_funcs, + &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 with an invalid maximum bpc * count fails. */ static void drm_test_connector_hdmi_init_bpc_invalid(struct kunit *test) @@ -497,10 +708,18 @@ static struct kunit_case drmm_connector_hdmi_init_tests[] = { KUNIT_CASE(drm_test_connector_hdmi_init_bpc_invalid), KUNIT_CASE(drm_test_connector_hdmi_init_bpc_null), KUNIT_CASE(drm_test_connector_hdmi_init_formats_empty), KUNIT_CASE(drm_test_connector_hdmi_init_formats_no_rgb), KUNIT_CASE(drm_test_connector_hdmi_init_null_ddc), + KUNIT_CASE(drm_test_connector_hdmi_init_null_product), + KUNIT_CASE(drm_test_connector_hdmi_init_null_vendor), + KUNIT_CASE(drm_test_connector_hdmi_init_product_length_exact), + KUNIT_CASE(drm_test_connector_hdmi_init_product_length_too_long), + KUNIT_CASE(drm_test_connector_hdmi_init_product_valid), + KUNIT_CASE(drm_test_connector_hdmi_init_vendor_length_exact), + KUNIT_CASE(drm_test_connector_hdmi_init_vendor_length_too_long), + KUNIT_CASE(drm_test_connector_hdmi_init_vendor_valid), 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, drm_connector_hdmi_init_type_invalid_gen_params), { } From patchwork Mon Mar 11 14:49:50 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588851 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 379C1C5475B for ; Mon, 11 Mar 2024 14:51:08 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 7D0DA112ACF; Mon, 11 Mar 2024 14:51:07 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="S6zY0etK"; 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 559EE112AD9 for ; Mon, 11 Mar 2024 14:51:04 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id C404060EAC; Mon, 11 Mar 2024 14:51:03 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 44321C433F1; Mon, 11 Mar 2024 14:51:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168663; bh=8/opTRplB5Ku39o1OEl1lg2hR6YKBkFWnOlEJPXB6sg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=S6zY0etKtkPf6wHL+91qGftBwWEN1RWDX69XX61l+SduvSeEa8CFbqY8j8xW9+baK vi53OeVv3WDDCCjvOEpoMSQH5mr3iiljUAw2Dm0Xm3UL7Id2F9nbnO2gTN7yCekC+/ wOcRA4wBb4pPamfaKTQx+YI0mgvW3ahtiwFIWoQW+lGo4k39GaPetiThOhLYyvzgZs +Z5nZ1ZYCNKxW3ra8E38Yc0Cgrma08dXFm0NMwANdyacmw7GNyd49QpjfyuTnZ3z4k X2WpqPYFkp1rDIP2SgLhI5UqPeeYv9t4B0xFO9mNjoSoYl/WELIFEAPQcV4uWIAzhI 0RAUiIvNDEXIw== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:50 +0100 Subject: [PATCH v9 22/27] drm/connector: hdmi: Create Infoframe DebugFS entries MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-22-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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 X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=4334; i=mripard@kernel.org; h=from:subject:message-id; bh=8/opTRplB5Ku39o1OEl1lg2hR6YKBkFWnOlEJPXB6sg=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpYSEzv/evPRD6+GFc9oZmtcbir38K1DE563mUCt15 Ns5jneXO0pZGMS4GGTFFFlihM2XxJ2a9bqTjW8ezBxWJpAhDFycAjCR44WMDLe/6CmauRYoMSg/ e55cHSy63mzHueuTXey5YnY9nX+nip+R4fAKTyOTyWdWlxx7OrVxZ/OZ7MKAy9d9L1dsYk6JjNP oZwYA 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" There has been some discussions recently about the infoframes sent by drivers and if they were properly generated. In parallel, there's been some interest in creating an infoframe-decode tool similar to edid-decode. Both would be much easier if we were to expose the infoframes programmed in the hardware. It won't be perfect since we have no guarantee that it's actually what goes through the wire, but it's the best we can do. Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_debugfs.c | 110 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 08fcefd804bc..160389f3d84b 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -518,10 +518,118 @@ static const struct file_operations drm_connector_fops = { .llseek = seq_lseek, .release = single_release, .write = connector_write }; +struct debugfs_wrapper { + struct drm_connector *connector; + struct drm_connector_hdmi_infoframe *frame; +}; + +#define HDMI_MAX_INFOFRAME_SIZE 29 + +static ssize_t +infoframe_read(struct file *filp, char __user *ubuf, size_t count, loff_t *ppos) +{ + const struct debugfs_wrapper *wrapper = filp->private_data; + struct drm_connector *connector = wrapper->connector; + struct drm_connector_hdmi_infoframe *infoframe = wrapper->frame; + union hdmi_infoframe *frame = &infoframe->data; + u8 buf[HDMI_MAX_INFOFRAME_SIZE]; + ssize_t len = 0; + + mutex_lock(&connector->hdmi.infoframes.lock); + + if (!infoframe->set) + goto out; + + len = hdmi_infoframe_pack(frame, buf, sizeof(buf)); + if (len < 0) + goto out; + + len = simple_read_from_buffer(ubuf, count, ppos, buf, len); + +out: + mutex_unlock(&connector->hdmi.infoframes.lock); + return len; +} + +static const struct file_operations infoframe_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = infoframe_read, +}; + +static int create_hdmi_infoframe_file(struct drm_connector *connector, + struct dentry *parent, + const char *filename, + struct drm_connector_hdmi_infoframe *frame) +{ + struct drm_device *dev = connector->dev; + struct debugfs_wrapper *wrapper; + struct dentry *file; + + wrapper = drmm_kzalloc(dev, sizeof(*wrapper), GFP_KERNEL); + if (!wrapper) + return -ENOMEM; + + wrapper->connector = connector; + wrapper->frame = frame; + + file = debugfs_create_file(filename, 0400, parent, wrapper, &infoframe_fops); + if (IS_ERR(file)) + return PTR_ERR(file); + + return 0; +} + +#define CREATE_HDMI_INFOFRAME_FILE(c, p, i) \ + create_hdmi_infoframe_file(c, p, #i, &(c)->hdmi.infoframes.i) + +static int create_hdmi_infoframe_files(struct drm_connector *connector, + struct dentry *parent) +{ + int ret; + + ret = CREATE_HDMI_INFOFRAME_FILE(connector, parent, audio); + if (ret) + return ret; + + ret = CREATE_HDMI_INFOFRAME_FILE(connector, parent, avi); + if (ret) + return ret; + + ret = CREATE_HDMI_INFOFRAME_FILE(connector, parent, hdr_drm); + if (ret) + return ret; + + ret = CREATE_HDMI_INFOFRAME_FILE(connector, parent, spd); + if (ret) + return ret; + + ret = CREATE_HDMI_INFOFRAME_FILE(connector, parent, hdmi); + if (ret) + return ret; + + return 0; +} + +static void hdmi_debugfs_add(struct drm_connector *connector) +{ + struct dentry *dir; + + if (!(connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || + connector->connector_type == DRM_MODE_CONNECTOR_HDMIB)) + return; + + dir = debugfs_create_dir("infoframes", connector->debugfs_entry); + if (IS_ERR(dir)) + return; + + create_hdmi_infoframe_files(connector, dir); +} + void drm_debugfs_connector_add(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct dentry *root; @@ -545,10 +653,12 @@ void drm_debugfs_connector_add(struct drm_connector *connector) /* max bpc */ debugfs_create_file("output_bpc", 0444, root, connector, &output_bpc_fops); + hdmi_debugfs_add(connector); + if (connector->funcs->debugfs_init) connector->funcs->debugfs_init(connector, root); } void drm_debugfs_connector_remove(struct drm_connector *connector) From patchwork Mon Mar 11 14:49:51 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588852 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 77482C54E58 for ; Mon, 11 Mar 2024 14:51:10 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 96EF9112AD5; Mon, 11 Mar 2024 14:51:09 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="qu9svqPN"; 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 9474E112AD5 for ; Mon, 11 Mar 2024 14:51:07 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id C5E2760EBA; Mon, 11 Mar 2024 14:51:06 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1B33DC433B1; Mon, 11 Mar 2024 14:51:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168666; bh=v3yx9ONGkMObmgCTmfOc6SwbMwyae5YqWJXvVoOZ0Xc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=qu9svqPNZGDydtqGObbpbm2rCgJmvay/NDs33yO8j/r8H/Q6y7E8VSL/GaQlF5xIi gVz1mGlyaoK2PcveLeDqsh9ASc+cwjIhnu8zVRY9fd9eno+diSPkuy6EtNB8Io9z0s /t9knVLRjrbKJQCA3blMaswshxewZf9uBtuUXVydL/RTuWpo79ELiVl4xJNMb7/irJ yrx8zfuxsUKoURBBz5y6prFNxf+DmnVrWQOe3BzJOmrU1liriKFaXpiBQSoZKSQLQ0 NmHceSZkykHJCRj4jAaWaPs2D3Z7lLq1lDJC/sFzPmVS0tWY1J11sGL5W/1AwpvKuY izNsglEzCZY9w== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:51 +0100 Subject: [PATCH v9 23/27] drm/vc4: hdmi: Switch to HDMI connector MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-23-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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 X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=41761; i=mripard@kernel.org; h=from:subject:message-id; bh=v3yx9ONGkMObmgCTmfOc6SwbMwyae5YqWJXvVoOZ0Xc=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpYRXntw1dVvdTafgLP6Qp3uqwwreXGN21Czp3HVEW 3p77wmJjlIWBjEuBlkxRZYYYfMlcadmve5k45sHM4eVCWQIAxenAEzk7mqGPzwPD7XwXLzks7gu 2/C/hd2XXJ5HUy923hXPYlH+OOvK09mMDE0SfU8ljf51ljJP+t/NHnKv/FBCS1TcjIqe281Vh8T ecQAA 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" The new HDMI connector infrastructure allows us to remove a lot of boilerplate, so let's switch to it. Signed-off-by: Maxime Ripard Acked-by: Sui Jingfeng --- drivers/gpu/drm/vc4/vc4_hdmi.c | 638 +++++-------------------------------- drivers/gpu/drm/vc4/vc4_hdmi.h | 44 +-- drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 6 +- 3 files changed, 86 insertions(+), 602 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 34f807ed1c31..1f489cbe0c90 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -108,29 +108,10 @@ #define HSM_MIN_CLOCK_FREQ 120000000 #define CEC_CLOCK_FREQ 40000 #define HDMI_14_MAX_TMDS_CLK (340 * 1000 * 1000) -static const char * const output_format_str[] = { - [VC4_HDMI_OUTPUT_RGB] = "RGB", - [VC4_HDMI_OUTPUT_YUV420] = "YUV 4:2:0", - [VC4_HDMI_OUTPUT_YUV422] = "YUV 4:2:2", - [VC4_HDMI_OUTPUT_YUV444] = "YUV 4:4:4", -}; - -static const char *vc4_hdmi_output_fmt_str(enum vc4_hdmi_output_format fmt) -{ - if (fmt >= ARRAY_SIZE(output_format_str)) - return "invalid"; - - return output_format_str[fmt]; -} - -static unsigned long long -vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode, - unsigned int bpc, enum vc4_hdmi_output_format fmt); - static bool vc4_hdmi_supports_scrambling(struct vc4_hdmi *vc4_hdmi) { struct drm_display_info *display = &vc4_hdmi->connector.display_info; lockdep_assert_held(&vc4_hdmi->mutex); @@ -145,32 +126,17 @@ static bool vc4_hdmi_supports_scrambling(struct vc4_hdmi *vc4_hdmi) return true; } static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode, unsigned int bpc, - enum vc4_hdmi_output_format fmt) + enum hdmi_colorspace fmt) { - unsigned long long clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc, fmt); + unsigned long long clock = drm_connector_hdmi_compute_mode_clock(mode, bpc, fmt); return clock > HDMI_14_MAX_TMDS_CLK; } -static bool vc4_hdmi_is_full_range(struct vc4_hdmi *vc4_hdmi, - struct vc4_hdmi_connector_state *vc4_state) -{ - const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; - struct drm_display_info *display = &vc4_hdmi->connector.display_info; - - if (vc4_state->broadcast_rgb == VC4_HDMI_BROADCAST_RGB_LIMITED) - return false; - else if (vc4_state->broadcast_rgb == VC4_HDMI_BROADCAST_RGB_FULL) - return true; - - return !display->is_hdmi || - drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_FULL; -} - static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) { struct drm_debugfs_entry *entry = m->private; struct vc4_hdmi *vc4_hdmi = entry->file.data; struct drm_device *drm = vc4_hdmi->connector.dev; @@ -518,11 +484,11 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) if (!vc4->hvs->vc5_hdmi_enable_hdmi_20) { struct drm_device *drm = connector->dev; const struct drm_display_mode *mode; list_for_each_entry(mode, &connector->probed_modes, head) { - if (vc4_hdmi_mode_needs_scrambling(mode, 8, VC4_HDMI_OUTPUT_RGB)) { + if (vc4_hdmi_mode_needs_scrambling(mode, 8, HDMI_COLORSPACE_RGB)) { drm_warn_once(drm, "The core clock cannot reach frequencies high enough to support 4k @ 60Hz."); drm_warn_once(drm, "Please change your config.txt file to add hdmi_enable_4kp60."); } } } @@ -533,16 +499,12 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector, struct drm_atomic_state *state) { struct drm_connector_state *old_state = drm_atomic_get_old_connector_state(state, connector); - struct vc4_hdmi_connector_state *old_vc4_state = - conn_state_to_vc4_hdmi_conn_state(old_state); struct drm_connector_state *new_state = drm_atomic_get_new_connector_state(state, connector); - struct vc4_hdmi_connector_state *new_vc4_state = - conn_state_to_vc4_hdmi_conn_state(new_state); struct drm_crtc *crtc = new_state->crtc; if (!crtc) return 0; @@ -570,174 +532,62 @@ static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector, ret = drm_atomic_add_affected_planes(state, crtc); if (ret) return ret; } - if (old_state->colorspace != new_state->colorspace || - old_vc4_state->broadcast_rgb != new_vc4_state->broadcast_rgb || - !drm_connector_atomic_hdr_metadata_equal(old_state, new_state)) { + if (old_state->colorspace != new_state->colorspace) { struct drm_crtc_state *crtc_state; crtc_state = drm_atomic_get_crtc_state(state, crtc); if (IS_ERR(crtc_state)) return PTR_ERR(crtc_state); crtc_state->mode_changed = true; } - return 0; -} - -static int vc4_hdmi_connector_get_property(struct drm_connector *connector, - const struct drm_connector_state *state, - struct drm_property *property, - uint64_t *val) -{ - struct drm_device *drm = connector->dev; - struct vc4_hdmi *vc4_hdmi = - connector_to_vc4_hdmi(connector); - const struct vc4_hdmi_connector_state *vc4_conn_state = - conn_state_to_vc4_hdmi_conn_state(state); - - if (property == vc4_hdmi->broadcast_rgb_property) { - *val = vc4_conn_state->broadcast_rgb; - } else { - drm_dbg(drm, "Unknown property [PROP:%d:%s]\n", - property->base.id, property->name); - return -EINVAL; - } - - return 0; -} - -static int vc4_hdmi_connector_set_property(struct drm_connector *connector, - struct drm_connector_state *state, - struct drm_property *property, - uint64_t val) -{ - struct drm_device *drm = connector->dev; - struct vc4_hdmi *vc4_hdmi = - connector_to_vc4_hdmi(connector); - struct vc4_hdmi_connector_state *vc4_conn_state = - conn_state_to_vc4_hdmi_conn_state(state); - - if (property == vc4_hdmi->broadcast_rgb_property) { - vc4_conn_state->broadcast_rgb = val; - return 0; - } - - drm_dbg(drm, "Unknown property [PROP:%d:%s]\n", - property->base.id, property->name); - return -EINVAL; + return drm_atomic_helper_connector_hdmi_check(connector, state); } static void vc4_hdmi_connector_reset(struct drm_connector *connector) { - struct vc4_hdmi_connector_state *old_state = - conn_state_to_vc4_hdmi_conn_state(connector->state); - struct vc4_hdmi_connector_state *new_state = - kzalloc(sizeof(*new_state), GFP_KERNEL); - - if (connector->state) - __drm_atomic_helper_connector_destroy_state(connector->state); - - kfree(old_state); - __drm_atomic_helper_connector_reset(connector, &new_state->base); - - if (!new_state) - return; - - new_state->base.max_bpc = 8; - new_state->base.max_requested_bpc = 8; - new_state->output_format = VC4_HDMI_OUTPUT_RGB; - new_state->broadcast_rgb = VC4_HDMI_BROADCAST_RGB_AUTO; + drm_atomic_helper_connector_reset(connector); + __drm_atomic_helper_connector_hdmi_reset(connector, connector->state); drm_atomic_helper_connector_tv_margins_reset(connector); } -static struct drm_connector_state * -vc4_hdmi_connector_duplicate_state(struct drm_connector *connector) -{ - struct drm_connector_state *conn_state = connector->state; - struct vc4_hdmi_connector_state *vc4_state = conn_state_to_vc4_hdmi_conn_state(conn_state); - struct vc4_hdmi_connector_state *new_state; - - new_state = kzalloc(sizeof(*new_state), GFP_KERNEL); - if (!new_state) - return NULL; - - new_state->tmds_char_rate = vc4_state->tmds_char_rate; - new_state->output_bpc = vc4_state->output_bpc; - new_state->output_format = vc4_state->output_format; - new_state->broadcast_rgb = vc4_state->broadcast_rgb; - __drm_atomic_helper_connector_duplicate_state(connector, &new_state->base); - - return &new_state->base; -} - -static void vc4_hdmi_connector_destroy_state(struct drm_connector *connector, - struct drm_connector_state *state) -{ - struct vc4_hdmi_connector_state *vc4_state = - conn_state_to_vc4_hdmi_conn_state(state); - - __drm_atomic_helper_connector_destroy_state(state); - kfree(vc4_state); -} - static const struct drm_connector_funcs vc4_hdmi_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .reset = vc4_hdmi_connector_reset, - .atomic_duplicate_state = vc4_hdmi_connector_duplicate_state, - .atomic_destroy_state = vc4_hdmi_connector_destroy_state, - .atomic_get_property = vc4_hdmi_connector_get_property, - .atomic_set_property = vc4_hdmi_connector_set_property, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = { .detect_ctx = vc4_hdmi_connector_detect_ctx, .get_modes = vc4_hdmi_connector_get_modes, .atomic_check = vc4_hdmi_connector_atomic_check, }; -static const struct drm_prop_enum_list broadcast_rgb_names[] = { - { VC4_HDMI_BROADCAST_RGB_AUTO, "Automatic" }, - { VC4_HDMI_BROADCAST_RGB_FULL, "Full" }, - { VC4_HDMI_BROADCAST_RGB_LIMITED, "Limited 16:235" }, -}; - -static void -vc4_hdmi_attach_broadcast_rgb_property(struct drm_device *dev, - struct vc4_hdmi *vc4_hdmi) -{ - struct drm_property *prop = vc4_hdmi->broadcast_rgb_property; - - if (!prop) { - prop = drm_property_create_enum(dev, DRM_MODE_PROP_ENUM, - "Broadcast RGB", - broadcast_rgb_names, - ARRAY_SIZE(broadcast_rgb_names)); - if (!prop) - return; - - vc4_hdmi->broadcast_rgb_property = prop; - } - - drm_object_attach_property(&vc4_hdmi->connector.base, prop, - VC4_HDMI_BROADCAST_RGB_AUTO); -} +static const struct drm_connector_hdmi_funcs vc4_hdmi_hdmi_connector_funcs; static int vc4_hdmi_connector_init(struct drm_device *dev, struct vc4_hdmi *vc4_hdmi) { struct drm_connector *connector = &vc4_hdmi->connector; struct drm_encoder *encoder = &vc4_hdmi->encoder.base; int ret; - ret = drmm_connector_init(dev, connector, - &vc4_hdmi_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA, - vc4_hdmi->ddc); + ret = drmm_connector_hdmi_init(dev, connector, + "Broadcom", "Videocore", + &vc4_hdmi_connector_funcs, + &vc4_hdmi_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA, + vc4_hdmi->ddc, + BIT(HDMI_COLORSPACE_RGB) | + BIT(HDMI_COLORSPACE_YUV422) | + BIT(HDMI_COLORSPACE_YUV444), + 12); if (ret) return ret; drm_connector_helper_add(connector, &vc4_hdmi_connector_helper_funcs); @@ -757,34 +607,31 @@ static int vc4_hdmi_connector_init(struct drm_device *dev, if (ret) return ret; drm_connector_attach_colorspace_property(connector); drm_connector_attach_tv_margin_properties(connector); - drm_connector_attach_max_bpc_property(connector, 8, 12); connector->polled = (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT); connector->interlace_allowed = 1; connector->doublescan_allowed = 0; connector->stereo_allowed = 1; - if (vc4_hdmi->variant->supports_hdr) - drm_connector_attach_hdr_output_metadata_property(connector); - - vc4_hdmi_attach_broadcast_rgb_property(dev, vc4_hdmi); + ret = drm_connector_attach_broadcast_rgb_property(connector); + if (ret) + return ret; drm_connector_attach_encoder(connector, encoder); return 0; } -static int vc4_hdmi_stop_packet(struct drm_encoder *encoder, +static int vc4_hdmi_stop_packet(struct vc4_hdmi *vc4_hdmi, enum hdmi_infoframe_type type, bool poll) { - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); struct drm_device *drm = vc4_hdmi->connector.dev; u32 packet_id = type - 0x80; unsigned long flags; int ret = 0; int idx; @@ -804,41 +651,45 @@ static int vc4_hdmi_stop_packet(struct drm_encoder *encoder, drm_dev_exit(idx); return ret; } -static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, - union hdmi_infoframe *frame) +static int vc4_hdmi_write_infoframe(struct drm_connector *connector, + enum hdmi_infoframe_type type, + const u8 *infoframe, size_t len) { - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); - struct drm_device *drm = vc4_hdmi->connector.dev; - u32 packet_id = frame->any.type - 0x80; + struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector); + struct drm_device *drm = connector->dev; + u32 packet_id = type - 0x80; const struct vc4_hdmi_register *ram_packet_start = &vc4_hdmi->variant->registers[HDMI_RAM_PACKET_START]; u32 packet_reg = ram_packet_start->offset + VC4_HDMI_PACKET_STRIDE * packet_id; u32 packet_reg_next = ram_packet_start->offset + VC4_HDMI_PACKET_STRIDE * (packet_id + 1); void __iomem *base = __vc4_hdmi_get_field_base(vc4_hdmi, ram_packet_start->reg); uint8_t buffer[VC4_HDMI_PACKET_STRIDE] = {}; unsigned long flags; - ssize_t len, i; + ssize_t i; int ret; int idx; if (!drm_dev_enter(drm, &idx)) - return; + return 0; + + if (len > sizeof(buffer)) { + ret = -ENOMEM; + goto out; + } + + memcpy(buffer, infoframe, len); WARN_ONCE(!(HDMI_READ(HDMI_RAM_PACKET_CONFIG) & VC4_HDMI_RAM_PACKET_ENABLE), "Packet RAM has to be on to store the packet."); - len = hdmi_infoframe_pack(frame, buffer, sizeof(buffer)); - if (len < 0) - goto out; - - ret = vc4_hdmi_stop_packet(encoder, frame->any.type, true); + ret = vc4_hdmi_stop_packet(vc4_hdmi, type, true); if (ret) { DRM_ERROR("Failed to wait for infoframe to go idle: %d\n", ret); goto out; } @@ -876,134 +727,11 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, if (ret) DRM_ERROR("Failed to wait for infoframe to start: %d\n", ret); out: drm_dev_exit(idx); -} - -static void vc4_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame, - enum vc4_hdmi_output_format fmt) -{ - switch (fmt) { - case VC4_HDMI_OUTPUT_RGB: - frame->colorspace = HDMI_COLORSPACE_RGB; - break; - - case VC4_HDMI_OUTPUT_YUV420: - frame->colorspace = HDMI_COLORSPACE_YUV420; - break; - - case VC4_HDMI_OUTPUT_YUV422: - frame->colorspace = HDMI_COLORSPACE_YUV422; - break; - - case VC4_HDMI_OUTPUT_YUV444: - frame->colorspace = HDMI_COLORSPACE_YUV444; - break; - - default: - break; - } -} - -static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder) -{ - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); - struct drm_connector *connector = &vc4_hdmi->connector; - struct drm_connector_state *cstate = connector->state; - struct vc4_hdmi_connector_state *vc4_state = - conn_state_to_vc4_hdmi_conn_state(cstate); - const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; - union hdmi_infoframe frame; - int ret; - - lockdep_assert_held(&vc4_hdmi->mutex); - - ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, - connector, mode); - if (ret < 0) { - DRM_ERROR("couldn't fill AVI infoframe\n"); - return; - } - - drm_hdmi_avi_infoframe_quant_range(&frame.avi, - connector, mode, - vc4_hdmi_is_full_range(vc4_hdmi, vc4_state) ? - HDMI_QUANTIZATION_RANGE_FULL : - HDMI_QUANTIZATION_RANGE_LIMITED); - drm_hdmi_avi_infoframe_colorimetry(&frame.avi, cstate); - vc4_hdmi_avi_infoframe_colorspace(&frame.avi, vc4_state->output_format); - drm_hdmi_avi_infoframe_bars(&frame.avi, cstate); - - vc4_hdmi_write_infoframe(encoder, &frame); -} - -static void vc4_hdmi_set_spd_infoframe(struct drm_encoder *encoder) -{ - union hdmi_infoframe frame; - int ret; - - ret = hdmi_spd_infoframe_init(&frame.spd, "Broadcom", "Videocore"); - if (ret < 0) { - DRM_ERROR("couldn't fill SPD infoframe\n"); - return; - } - - frame.spd.sdi = HDMI_SPD_SDI_PC; - - vc4_hdmi_write_infoframe(encoder, &frame); -} - -static void vc4_hdmi_set_audio_infoframe(struct drm_encoder *encoder) -{ - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); - struct hdmi_audio_infoframe *audio = &vc4_hdmi->audio.infoframe; - union hdmi_infoframe frame; - - memcpy(&frame.audio, audio, sizeof(*audio)); - - if (vc4_hdmi->packet_ram_enabled) - vc4_hdmi_write_infoframe(encoder, &frame); -} - -static void vc4_hdmi_set_hdr_infoframe(struct drm_encoder *encoder) -{ - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); - struct drm_connector *connector = &vc4_hdmi->connector; - struct drm_connector_state *conn_state = connector->state; - union hdmi_infoframe frame; - - lockdep_assert_held(&vc4_hdmi->mutex); - - if (!vc4_hdmi->variant->supports_hdr) - return; - - if (!conn_state->hdr_output_metadata) - return; - - if (drm_hdmi_infoframe_set_hdr_metadata(&frame.drm, conn_state)) - return; - - vc4_hdmi_write_infoframe(encoder, &frame); -} - -static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder) -{ - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); - - lockdep_assert_held(&vc4_hdmi->mutex); - - vc4_hdmi_set_avi_infoframe(encoder); - vc4_hdmi_set_spd_infoframe(encoder); - /* - * If audio was streaming, then we need to reenabled the audio - * infoframe here during encoder_enable. - */ - if (vc4_hdmi->audio.streaming) - vc4_hdmi_set_audio_infoframe(encoder); - - vc4_hdmi_set_hdr_infoframe(encoder); + return ret; } #define SCRAMBLING_POLLING_DELAY_MS 1000 static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder) @@ -1168,13 +896,12 @@ static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder, static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, struct drm_connector_state *state, const struct drm_display_mode *mode) { - struct vc4_hdmi_connector_state *vc4_state = - conn_state_to_vc4_hdmi_conn_state(state); struct drm_device *drm = vc4_hdmi->connector.dev; + bool is_lim_range = !state->hdmi.is_full_range; unsigned long flags; u32 csc_ctl; int idx; if (!drm_dev_enter(drm, &idx)) @@ -1183,11 +910,11 @@ static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); csc_ctl = VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR, VC4_HD_CSC_CTL_ORDER); - if (!vc4_hdmi_is_full_range(vc4_hdmi, vc4_state)) { + if (is_lim_range) { /* CEA VICs other than #1 requre limited range RGB * output unless overridden by an AVI infoframe. * Apply a colorspace conversion to squash 0-255 down * to 16-235. The matrix here is: * @@ -1406,13 +1133,11 @@ static const u16 static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, struct drm_connector_state *state, const struct drm_display_mode *mode) { struct drm_device *drm = vc4_hdmi->connector.dev; - struct vc4_hdmi_connector_state *vc4_state = - conn_state_to_vc4_hdmi_conn_state(state); - unsigned int lim_range = vc4_hdmi_is_full_range(vc4_hdmi, vc4_state) ? 0 : 1; + unsigned int lim_range = state->hdmi.is_full_range ? 0 : 1; unsigned long flags; const u16 (*csc)[4]; u32 if_cfg = 0; u32 if_xbar = 0x543210; u32 csc_chan_ctl = 0; @@ -1423,18 +1148,18 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, if (!drm_dev_enter(drm, &idx)) return; spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); - switch (vc4_state->output_format) { - case VC4_HDMI_OUTPUT_YUV444: + switch (state->hdmi.output_format) { + case HDMI_COLORSPACE_YUV444: csc = vc5_hdmi_find_yuv_csc_coeffs(vc4_hdmi, state->colorspace, !!lim_range); vc5_hdmi_set_csc_coeffs_swap(vc4_hdmi, csc); break; - case VC4_HDMI_OUTPUT_YUV422: + case HDMI_COLORSPACE_YUV422: csc = vc5_hdmi_find_yuv_csc_coeffs(vc4_hdmi, state->colorspace, !!lim_range); csc_ctl |= VC4_SET_FIELD(VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_STANDARD, VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422) | VC5_MT_CP_CSC_CTL_USE_444_TO_422 | @@ -1447,11 +1172,11 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422); vc5_hdmi_set_csc_coeffs(vc4_hdmi, csc); break; - case VC4_HDMI_OUTPUT_RGB: + case HDMI_COLORSPACE_RGB: if_xbar = 0x354021; vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_rgb[lim_range]); break; @@ -1536,12 +1261,10 @@ static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, struct drm_connector_state *state, const struct drm_display_mode *mode) { struct drm_device *drm = vc4_hdmi->connector.dev; - const struct vc4_hdmi_connector_state *vc4_state = - conn_state_to_vc4_hdmi_conn_state(state); bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC; bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; u32 pixel_rep = (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1; u32 verta = (VC4_SET_FIELD(mode->crtc_vsync_end - mode->crtc_vsync_start, @@ -1589,11 +1312,11 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, HDMI_WRITE(HDMI_VERTA1, verta); HDMI_WRITE(HDMI_VERTB0, vertb_even); HDMI_WRITE(HDMI_VERTB1, vertb); - switch (vc4_state->output_bpc) { + switch (state->hdmi.output_bpc) { case 12: gcp = 6; break; case 10: gcp = 5; @@ -1606,11 +1329,11 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, /* * YCC422 is always 36-bit and not considered deep colour so * doesn't signal in GCP. */ - if (vc4_state->output_format == VC4_HDMI_OUTPUT_YUV422) { + if (state->hdmi.output_format == HDMI_COLORSPACE_YUV422) { gcp = 0; } reg = HDMI_READ(HDMI_DEEP_COLOR_CONFIG_1); reg &= ~(VC5_HDMI_DEEP_COLOR_CONFIG_1_INIT_PACK_PHASE_MASK | @@ -1690,14 +1413,12 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); struct drm_device *drm = vc4_hdmi->connector.dev; struct drm_connector *connector = &vc4_hdmi->connector; struct drm_connector_state *conn_state = drm_atomic_get_new_connector_state(state, connector); - struct vc4_hdmi_connector_state *vc4_conn_state = - conn_state_to_vc4_hdmi_conn_state(conn_state); const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; - unsigned long tmds_char_rate = vc4_conn_state->tmds_char_rate; + unsigned long tmds_char_rate = conn_state->hdmi.tmds_char_rate; unsigned long bvb_rate, hsm_rate; unsigned long flags; int ret; int idx; @@ -1770,11 +1491,11 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, DRM_ERROR("Failed to turn on pixel bvb clock: %d\n", ret); goto err_disable_pixel_clock; } if (vc4_hdmi->variant->phy_init) - vc4_hdmi->variant->phy_init(vc4_hdmi, vc4_conn_state); + vc4_hdmi->variant->phy_init(vc4_hdmi, conn_state); spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_SCHEDULER_CONTROL, HDMI_READ(HDMI_SCHEDULER_CONTROL) | @@ -1835,11 +1556,12 @@ static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder, static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); - struct drm_device *drm = vc4_hdmi->connector.dev; + struct drm_connector *connector = &vc4_hdmi->connector; + struct drm_device *drm = connector->dev; const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; struct drm_display_info *display = &vc4_hdmi->connector.display_info; bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC; unsigned long flags; @@ -1901,11 +1623,11 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, VC4_HDMI_RAM_PACKET_ENABLE); spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); vc4_hdmi->packet_ram_enabled = true; - vc4_hdmi_set_infoframes(encoder); + drm_atomic_helper_connector_hdmi_update_infoframes(connector, state); } vc4_hdmi_recenter_fifo(vc4_hdmi); vc4_hdmi_enable_scrambling(encoder); @@ -1918,112 +1640,25 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, static void vc4_hdmi_encoder_atomic_mode_set(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); - struct vc4_hdmi_connector_state *vc4_state = - conn_state_to_vc4_hdmi_conn_state(conn_state); mutex_lock(&vc4_hdmi->mutex); drm_mode_copy(&vc4_hdmi->saved_adjusted_mode, &crtc_state->adjusted_mode); - vc4_hdmi->output_bpc = vc4_state->output_bpc; - vc4_hdmi->output_format = vc4_state->output_format; + vc4_hdmi->output_bpc = conn_state->hdmi.output_bpc; + vc4_hdmi->output_format = conn_state->hdmi.output_format; mutex_unlock(&vc4_hdmi->mutex); } -static bool -vc4_hdmi_sink_supports_format_bpc(const struct vc4_hdmi *vc4_hdmi, - const struct drm_display_info *info, - const struct drm_display_mode *mode, - unsigned int format, unsigned int bpc) -{ - struct drm_device *dev = vc4_hdmi->connector.dev; - u8 vic = drm_match_cea_mode(mode); - - if (vic == 1 && bpc != 8) { - drm_dbg(dev, "VIC1 requires a bpc of 8, got %u\n", bpc); - return false; - } - - if (!info->is_hdmi && - (format != VC4_HDMI_OUTPUT_RGB || bpc != 8)) { - drm_dbg(dev, "DVI Monitors require an RGB output at 8 bpc\n"); - return false; - } - - switch (format) { - case VC4_HDMI_OUTPUT_RGB: - drm_dbg(dev, "RGB Format, checking the constraints.\n"); - - if (!(info->color_formats & DRM_COLOR_FORMAT_RGB444)) - return false; - - if (bpc == 10 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30)) { - drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n"); - return false; - } - - if (bpc == 12 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36)) { - drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n"); - return false; - } - - drm_dbg(dev, "RGB format supported in that configuration.\n"); - - return true; - - case VC4_HDMI_OUTPUT_YUV422: - drm_dbg(dev, "YUV422 format, checking the constraints.\n"); - - if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR422)) { - drm_dbg(dev, "Sink doesn't support YUV422.\n"); - return false; - } - - if (bpc != 12) { - drm_dbg(dev, "YUV422 only supports 12 bpc.\n"); - return false; - } - - drm_dbg(dev, "YUV422 format supported in that configuration.\n"); - - return true; - - case VC4_HDMI_OUTPUT_YUV444: - drm_dbg(dev, "YUV444 format, checking the constraints.\n"); - - if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR444)) { - drm_dbg(dev, "Sink doesn't support YUV444.\n"); - return false; - } - - if (bpc == 10 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_30)) { - drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n"); - return false; - } - - if (bpc == 12 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_36)) { - drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n"); - return false; - } - - drm_dbg(dev, "YUV444 format supported in that configuration.\n"); - - return true; - } - - return false; -} - static enum drm_mode_status -vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi, - const struct drm_display_mode *mode, - unsigned long long clock) +vc4_hdmi_connector_clock_valid(const struct drm_connector *connector, + const struct drm_display_mode *mode, + unsigned long long clock) { - const struct drm_connector *connector = &vc4_hdmi->connector; - const struct drm_display_info *info = &connector->display_info; + const struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector); struct vc4_dev *vc4 = to_vc4_dev(connector->dev); if (clock > vc4_hdmi->variant->max_pixel_clock) return MODE_CLOCK_HIGH; @@ -2034,148 +1669,29 @@ vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi, if (!vc4->hvs->vc5_hdmi_enable_4096by2160 && mode->hdisplay > 3840 && mode->vdisplay >= 2160 && drm_mode_vrefresh(mode) >= 50) return MODE_CLOCK_HIGH; - if (info->max_tmds_clock && clock > (info->max_tmds_clock * 1000)) - return MODE_CLOCK_HIGH; - return MODE_OK; } -static unsigned long long -vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode, - unsigned int bpc, - enum vc4_hdmi_output_format fmt) -{ - unsigned long long clock = mode->clock * 1000ULL; - - if (mode->flags & DRM_MODE_FLAG_DBLCLK) - clock = clock * 2; - - if (fmt == VC4_HDMI_OUTPUT_YUV422) - bpc = 8; - - clock = clock * bpc; - do_div(clock, 8); - - return clock; -} - -static int -vc4_hdmi_encoder_compute_clock(const struct vc4_hdmi *vc4_hdmi, - struct vc4_hdmi_connector_state *vc4_state, - const struct drm_display_mode *mode, - unsigned int bpc, unsigned int fmt) -{ - unsigned long long clock; - - clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc, fmt); - if (vc4_hdmi_encoder_clock_valid(vc4_hdmi, mode, clock) != MODE_OK) - return -EINVAL; - - vc4_state->tmds_char_rate = clock; - - return 0; -} - -static int -vc4_hdmi_encoder_compute_format(const struct vc4_hdmi *vc4_hdmi, - struct vc4_hdmi_connector_state *vc4_state, - const struct drm_display_mode *mode, - unsigned int bpc) -{ - struct drm_device *dev = vc4_hdmi->connector.dev; - const struct drm_connector *connector = &vc4_hdmi->connector; - const struct drm_display_info *info = &connector->display_info; - unsigned int format; - - drm_dbg(dev, "Trying with an RGB output\n"); - - format = VC4_HDMI_OUTPUT_RGB; - if (vc4_hdmi_sink_supports_format_bpc(vc4_hdmi, info, mode, format, bpc)) { - int ret; - - ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state, - mode, bpc, format); - if (!ret) { - vc4_state->output_format = format; - return 0; - } - } - - drm_dbg(dev, "Failed, Trying with an YUV422 output\n"); - - format = VC4_HDMI_OUTPUT_YUV422; - if (vc4_hdmi_sink_supports_format_bpc(vc4_hdmi, info, mode, format, bpc)) { - int ret; - - ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state, - mode, bpc, format); - if (!ret) { - vc4_state->output_format = format; - return 0; - } - } - - drm_dbg(dev, "Failed. No Format Supported for that bpc count.\n"); - - return -EINVAL; -} - -static int -vc4_hdmi_encoder_compute_config(const struct vc4_hdmi *vc4_hdmi, - struct vc4_hdmi_connector_state *vc4_state, - const struct drm_display_mode *mode) -{ - struct drm_device *dev = vc4_hdmi->connector.dev; - struct drm_connector_state *conn_state = &vc4_state->base; - unsigned int max_bpc = clamp_t(unsigned int, conn_state->max_bpc, 8, 12); - unsigned int bpc; - int ret; - - for (bpc = max_bpc; bpc >= 8; bpc -= 2) { - drm_dbg(dev, "Trying with a %d bpc output\n", bpc); - - ret = vc4_hdmi_encoder_compute_format(vc4_hdmi, vc4_state, - mode, bpc); - if (ret) - continue; - - vc4_state->output_bpc = bpc; - - drm_dbg(dev, - "Mode %ux%u @ %uHz: Found configuration: bpc: %u, fmt: %s, clock: %llu\n", - mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode), - vc4_state->output_bpc, - vc4_hdmi_output_fmt_str(vc4_state->output_format), - vc4_state->tmds_char_rate); - - break; - } - - return ret; -} +static const struct drm_connector_hdmi_funcs vc4_hdmi_hdmi_connector_funcs = { + .tmds_char_rate_valid = vc4_hdmi_connector_clock_valid, + .write_infoframe = vc4_hdmi_write_infoframe, +}; #define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL #define WIFI_2_4GHz_CH1_MAX_FREQ 2422000000ULL static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); - struct drm_connector *connector = &vc4_hdmi->connector; - struct drm_connector_state *old_conn_state = - drm_atomic_get_old_connector_state(conn_state->state, connector); - struct vc4_hdmi_connector_state *old_vc4_state = - conn_state_to_vc4_hdmi_conn_state(old_conn_state); - struct vc4_hdmi_connector_state *vc4_state = conn_state_to_vc4_hdmi_conn_state(conn_state); struct drm_display_mode *mode = &crtc_state->adjusted_mode; unsigned long long tmds_char_rate = mode->clock * 1000; unsigned long long tmds_bit_rate; - int ret; if (vc4_hdmi->variant->unsupported_odd_h_timings) { if (mode->flags & DRM_MODE_FLAG_DBLCLK) { /* Only try to fixup DBLCLK modes to get 480i and 576i * working. @@ -2207,35 +1723,28 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, tmds_bit_rate <= WIFI_2_4GHz_CH1_MAX_FREQ)) { mode->clock = 238560; tmds_char_rate = mode->clock * 1000; } - ret = vc4_hdmi_encoder_compute_config(vc4_hdmi, vc4_state, mode); - if (ret) - return ret; - - /* vc4_hdmi_encoder_compute_config may have changed output_bpc and/or output_format */ - if (vc4_state->output_bpc != old_vc4_state->output_bpc || - vc4_state->output_format != old_vc4_state->output_format) - crtc_state->mode_changed = true; - return 0; } static enum drm_mode_status vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder, const struct drm_display_mode *mode) { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + unsigned long long rate; if (vc4_hdmi->variant->unsupported_odd_h_timings && !(mode->flags & DRM_MODE_FLAG_DBLCLK) && ((mode->hdisplay % 2) || (mode->hsync_start % 2) || (mode->hsync_end % 2) || (mode->htotal % 2))) return MODE_H_ILLEGAL; - return vc4_hdmi_encoder_clock_valid(vc4_hdmi, mode, mode->clock * 1000); + rate = drm_connector_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB); + return vc4_hdmi_connector_clock_valid(&vc4_hdmi->connector, mode, rate); } static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = { .atomic_check = vc4_hdmi_encoder_atomic_check, .atomic_mode_set = vc4_hdmi_encoder_atomic_mode_set, @@ -2423,19 +1932,18 @@ static int vc4_hdmi_audio_startup(struct device *dev, void *data) return ret; } static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi) { - struct drm_encoder *encoder = &vc4_hdmi->encoder.base; struct device *dev = &vc4_hdmi->pdev->dev; unsigned long flags; int ret; lockdep_assert_held(&vc4_hdmi->mutex); vc4_hdmi->audio.streaming = false; - ret = vc4_hdmi_stop_packet(encoder, HDMI_INFOFRAME_TYPE_AUDIO, false); + ret = vc4_hdmi_stop_packet(vc4_hdmi, HDMI_INFOFRAME_TYPE_AUDIO, false); if (ret) dev_err(dev, "Failed to stop audio infoframe: %d\n", ret); spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); @@ -2522,11 +2030,11 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data, struct hdmi_codec_daifmt *daifmt, struct hdmi_codec_params *params) { struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); struct drm_device *drm = vc4_hdmi->connector.dev; - struct drm_encoder *encoder = &vc4_hdmi->encoder.base; + struct drm_connector *connector = &vc4_hdmi->connector; unsigned int sample_rate = params->sample_rate; unsigned int channels = params->channels; unsigned long flags; u32 audio_packet_config, channel_mask; u32 channel_map; @@ -2599,12 +2107,14 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data, vc4_hdmi_set_n_cts(vc4_hdmi, sample_rate); spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); - memcpy(&vc4_hdmi->audio.infoframe, ¶ms->cea, sizeof(params->cea)); - vc4_hdmi_set_audio_infoframe(encoder); + ret = drm_atomic_helper_connector_hdmi_update_audio_infoframe(connector, + ¶ms->cea); + if (ret) + goto out_dev_exit; out_dev_exit: drm_dev_exit(idx); out: mutex_unlock(&vc4_hdmi->mutex); diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h index 934d5d61485a..b37f1d2c3fe5 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h @@ -8,11 +8,10 @@ #include "vc4_drv.h" struct vc4_hdmi; struct vc4_hdmi_register; -struct vc4_hdmi_connector_state; enum vc4_hdmi_phy_channel { PHY_LANE_0 = 0, PHY_LANE_1, PHY_LANE_2, @@ -74,11 +73,11 @@ struct vc4_hdmi_variant { struct drm_connector_state *state, const struct drm_display_mode *mode); /* Callback to initialize the PHY according to the connector state */ void (*phy_init)(struct vc4_hdmi *vc4_hdmi, - struct vc4_hdmi_connector_state *vc4_conn_state); + struct drm_connector_state *conn_state); /* Callback to disable the PHY */ void (*phy_disable)(struct vc4_hdmi *vc4_hdmi); /* Callback to enable the RNG in the PHY */ @@ -108,23 +107,10 @@ struct vc4_hdmi_audio { struct hdmi_audio_infoframe infoframe; struct platform_device *codec_pdev; bool streaming; }; -enum vc4_hdmi_output_format { - VC4_HDMI_OUTPUT_RGB, - VC4_HDMI_OUTPUT_YUV422, - VC4_HDMI_OUTPUT_YUV444, - VC4_HDMI_OUTPUT_YUV420, -}; - -enum vc4_hdmi_broadcast_rgb { - VC4_HDMI_BROADCAST_RGB_AUTO, - VC4_HDMI_BROADCAST_RGB_FULL, - VC4_HDMI_BROADCAST_RGB_LIMITED, -}; - /* General HDMI hardware state. */ struct vc4_hdmi { struct vc4_hdmi_audio audio; struct platform_device *pdev; @@ -133,12 +119,10 @@ struct vc4_hdmi { struct vc4_encoder encoder; struct drm_connector connector; struct delayed_work scrambling_work; - struct drm_property *broadcast_rgb_property; - struct i2c_adapter *ddc; void __iomem *hdmicore_regs; void __iomem *hd_regs; /* VC5 Only */ @@ -216,20 +200,21 @@ struct vc4_hdmi { * the scrambler on? Protected by @mutex. */ bool scdc_enabled; /** - * @output_bpc: Copy of @vc4_connector_state.output_bpc for use - * outside of KMS hooks. Protected by @mutex. + * @output_bpc: Copy of @drm_connector_state.hdmi.output_bpc for + * use outside of KMS hooks. Protected by @mutex. */ unsigned int output_bpc; /** - * @output_format: Copy of @vc4_connector_state.output_format - * for use outside of KMS hooks. Protected by @mutex. + * @output_format: Copy of + * @drm_connector_state.hdmi.output_format for use outside of + * KMS hooks. Protected by @mutex. */ - enum vc4_hdmi_output_format output_format; + enum hdmi_colorspace output_format; }; #define connector_to_vc4_hdmi(_connector) \ container_of_const(_connector, struct vc4_hdmi, connector) @@ -238,29 +223,18 @@ encoder_to_vc4_hdmi(struct drm_encoder *encoder) { struct vc4_encoder *_encoder = to_vc4_encoder(encoder); return container_of_const(_encoder, struct vc4_hdmi, encoder); } -struct vc4_hdmi_connector_state { - struct drm_connector_state base; - unsigned long long tmds_char_rate; - unsigned int output_bpc; - enum vc4_hdmi_output_format output_format; - enum vc4_hdmi_broadcast_rgb broadcast_rgb; -}; - -#define conn_state_to_vc4_hdmi_conn_state(_state) \ - container_of_const(_state, struct vc4_hdmi_connector_state, base) - void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, - struct vc4_hdmi_connector_state *vc4_conn_state); + struct drm_connector_state *conn_state); void vc4_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi); void vc4_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi); void vc4_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi); void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, - struct vc4_hdmi_connector_state *vc4_conn_state); + struct drm_connector_state *conn_state); void vc5_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi); void vc5_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi); void vc5_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi); #endif /* _VC4_HDMI_H_ */ diff --git a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c index ec24999bf96d..1f5507fc7a03 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c @@ -126,11 +126,11 @@ #define VC4_HDMI_RM_FORMAT_SHIFT_MASK VC4_MASK(25, 24) #define OSCILLATOR_FREQUENCY 54000000 void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, - struct vc4_hdmi_connector_state *conn_state) + struct drm_connector_state *conn_state) { unsigned long flags; /* PHY should be in reset, like * vc4_hdmi_encoder_disable() does. @@ -359,15 +359,15 @@ static void vc5_hdmi_reset_phy(struct vc4_hdmi *vc4_hdmi) HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0x0f); HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL, BIT(10)); } void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, - struct vc4_hdmi_connector_state *conn_state) + struct drm_connector_state *conn_state) { const struct phy_lane_settings *chan0_settings, *chan1_settings, *chan2_settings, *clock_settings; const struct vc4_hdmi_variant *variant = vc4_hdmi->variant; - unsigned long long pixel_freq = conn_state->tmds_char_rate; + unsigned long long pixel_freq = conn_state->hdmi.tmds_char_rate; unsigned long long vco_freq; unsigned char word_sel; unsigned long flags; u8 vco_sel, vco_div; From patchwork Mon Mar 11 14:49:52 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588853 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 10CE5C54E58 for ; Mon, 11 Mar 2024 14:51:15 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 48B43112AD8; Mon, 11 Mar 2024 14:51:14 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="ab9m5ZdU"; 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 509F5112AD8 for ; Mon, 11 Mar 2024 14:51:10 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id C90C960EB8; Mon, 11 Mar 2024 14:51:09 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 033BBC433C7; Mon, 11 Mar 2024 14:51:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168669; bh=xekVGAfWfJvLfOV1ebNOPUR3oLtTkCap7YpzVZIPKAQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=ab9m5ZdUTeyK0n1ff1p2rUSRKYm3M/1iqGq0A/0h6eMl0YofjlQnqV9ECWQxTn31Q Nt7aDjreB6VJFGMt6fgNvbMFrwty4k/uewuNIv/6bxE3fR2fdJhdTFMJYXxJaWMle9 vQy4T7tXdNTHMM+8COboH7XsmBWExBWUyeQTKM7NM9BhuOrKK6GZUOnh17JpypoDcm lcInmhIRFvjj7z5uJGPABEBaEDNXeqm/FpgwuEaNWdHgCu7ZzLeM6kn9X4WDLehODN DaD9TmorLwHtqxJJi6zTu9asp3C7s1oiPpusJ6GSvZIATw5NNPxbMITow2+cZ8kR4j XsQaWYiDnk2wg== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:52 +0100 Subject: [PATCH v9 24/27] drm/vc4: tests: Remove vc4_dummy_plane structure MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-24-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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 , =?utf-8?q?Ma=C3=ADra_Canal?= X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=3744; i=mripard@kernel.org; h=from:subject:message-id; bh=xekVGAfWfJvLfOV1ebNOPUR3oLtTkCap7YpzVZIPKAQ=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpYRP6fQf1+taZ5jpyDf9SuexCT+SQ/hSQnZ9k75yO of/VNuijlIWBjEuBlkxRZYYYfMlcadmve5k45sHM4eVCWQIAxenAEyk8jzDPx3xSf+mbOL9pfV8 ogTjMuGKlpIfx32/y7lG6do+8Hvt+oqRYelKWxXhuH/NXWsNQrhrxMM2vUi6FzRv1prbJoWHsxK X8wAA 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" The vc4_dummy_plane structure was introduced as a mean to add mock-specific fields. However, we never really used it and it's still strictly equivalent to vc4_plane (which is in the same situation vs drm_plane), so we can simply remove the vc4_dummy_plane structure and make the mock code cleaner. Reviewed-by: Maíra Canal Signed-off-by: Maxime Ripard --- drivers/gpu/drm/vc4/tests/vc4_mock.c | 6 ++---- drivers/gpu/drm/vc4/tests/vc4_mock.h | 9 ++------- drivers/gpu/drm/vc4/tests/vc4_mock_plane.c | 14 +++++--------- 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock.c b/drivers/gpu/drm/vc4/tests/vc4_mock.c index becb3dbaa548..0731a7d85d7a 100644 --- a/drivers/gpu/drm/vc4/tests/vc4_mock.c +++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c @@ -107,20 +107,18 @@ static const struct vc4_mock_desc vc5_mock = ); static int __build_one_pipe(struct kunit *test, struct drm_device *drm, const struct vc4_mock_pipe_desc *pipe) { - struct vc4_dummy_plane *dummy_plane; struct drm_plane *plane; struct vc4_dummy_crtc *dummy_crtc; struct drm_crtc *crtc; unsigned int i; - dummy_plane = vc4_dummy_plane(test, drm, DRM_PLANE_TYPE_PRIMARY); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_plane); + plane = vc4_dummy_plane(test, drm, DRM_PLANE_TYPE_PRIMARY); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane); - plane = &dummy_plane->plane.base; dummy_crtc = vc4_mock_pv(test, drm, plane, pipe->data); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_crtc); crtc = &dummy_crtc->crtc.base; for (i = 0; i < pipe->noutputs; i++) { diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock.h b/drivers/gpu/drm/vc4/tests/vc4_mock.h index 2d0b339bd9f3..002b6218960c 100644 --- a/drivers/gpu/drm/vc4/tests/vc4_mock.h +++ b/drivers/gpu/drm/vc4/tests/vc4_mock.h @@ -19,17 +19,12 @@ struct drm_crtc *vc4_find_crtc_for_encoder(struct kunit *test, return crtc; return NULL; } -struct vc4_dummy_plane { - struct vc4_plane plane; -}; - -struct vc4_dummy_plane *vc4_dummy_plane(struct kunit *test, - struct drm_device *drm, - enum drm_plane_type type); +struct drm_plane *vc4_dummy_plane(struct kunit *test, struct drm_device *drm, + enum drm_plane_type type); struct vc4_dummy_crtc { struct vc4_crtc crtc; }; diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c index 62b18f5f41db..973f5f929097 100644 --- a/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c +++ b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c @@ -20,28 +20,24 @@ static const struct drm_plane_funcs vc4_dummy_plane_funcs = { static const uint32_t vc4_dummy_plane_formats[] = { DRM_FORMAT_XRGB8888, }; -struct vc4_dummy_plane *vc4_dummy_plane(struct kunit *test, - struct drm_device *drm, - enum drm_plane_type type) +struct drm_plane *vc4_dummy_plane(struct kunit *test, struct drm_device *drm, + enum drm_plane_type type) { - struct vc4_dummy_plane *dummy_plane; struct drm_plane *plane; - dummy_plane = drmm_universal_plane_alloc(drm, - struct vc4_dummy_plane, plane.base, + plane = __drmm_universal_plane_alloc(drm, sizeof(struct drm_plane), 0, 0, &vc4_dummy_plane_funcs, vc4_dummy_plane_formats, ARRAY_SIZE(vc4_dummy_plane_formats), NULL, DRM_PLANE_TYPE_PRIMARY, NULL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_plane); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane); - plane = &dummy_plane->plane.base; drm_plane_helper_add(plane, &vc4_dummy_plane_helper_funcs); - return dummy_plane; + return plane; } From patchwork Mon Mar 11 14:49:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588855 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 C49EBC54E58 for ; Mon, 11 Mar 2024 14:51:19 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 004CC112AD7; Mon, 11 Mar 2024 14:51:18 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="OhJXotx9"; dkim-atps=neutral Received: from sin.source.kernel.org (sin.source.kernel.org [145.40.73.55]) by gabe.freedesktop.org (Postfix) with ESMTPS id 01EAD112AD9 for ; Mon, 11 Mar 2024 14:51:14 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sin.source.kernel.org (Postfix) with ESMTP id D435BCE1105; Mon, 11 Mar 2024 14:51:12 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id ADD50C43390; Mon, 11 Mar 2024 14:51:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168672; bh=EewgHdae7h/0M9r/RvBrwLIDoK5b4L1MDAyj2KDv0e0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=OhJXotx9POXNaKE/5f2ldD6bg+nqQSgqEBtYYmYUtzfGzOnFtqyDC9vlFfU2Rf2Mp D+5V5o2TbMmeauVbI7JVr5TbDwVd7bsiZmmg1glAOVkDkhoXlrGa9gG2Fy9PeZobqx BpdepJCS6DkQxO9aLUPqGfxqdw9bd3dzzwSmKgP9aObjjmmRVPY41f1F4t16Radyls EIPTG2Sbcf9AK/dmeTNYCEiYGzS0gRlbOKw5biUtEsEfDpDBFHm+L6yet8MHaJyYaU ORBMeb75M9wZfacxmitIdZT6ED7diBYx+vR+r8gLK/8cdrh+abo3O5W/4VdnNoWxYS n5cI5dTRugU/A== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:53 +0100 Subject: [PATCH v9 25/27] drm/vc4: tests: Convert to plane creation helper MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-25-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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 , =?utf-8?q?Ma=C3=ADra_Canal?= X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=2115; i=mripard@kernel.org; h=from:subject:message-id; bh=EewgHdae7h/0M9r/RvBrwLIDoK5b4L1MDAyj2KDv0e0=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpURm7plven/23Ecvv6ntOl971cLWWPvEURf7C7Jmh xb7z9k0raOUhUGMi0FWTJElRth8SdypWa872fjmwcxhZQIZwsDFKQATieBgZDibfHFeIE/LI6vD v/5N2DO/7HWyqmCHHy8P9ynrpC9ntI4x/NP89/T5nS+W7yRLLOOKzmvvOv/kmkr/Q40Q40q1I+V fX/ADAA== 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" Now that we have a plane create helper for kunit mocked drivers, let's convert to it in vc4. Reviewed-by: Maíra Canal Signed-off-by: Maxime Ripard --- drivers/gpu/drm/vc4/tests/vc4_mock_plane.c | 34 +++++++----------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c index 973f5f929097..14357db82238 100644 --- a/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c +++ b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c @@ -1,43 +1,25 @@ // SPDX-License-Identifier: GPL-2.0 -#include -#include -#include +#include #include #include #include "vc4_mock.h" -static const struct drm_plane_helper_funcs vc4_dummy_plane_helper_funcs = { -}; - -static const struct drm_plane_funcs vc4_dummy_plane_funcs = { - .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, - .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, - .reset = drm_atomic_helper_plane_reset, -}; - -static const uint32_t vc4_dummy_plane_formats[] = { - DRM_FORMAT_XRGB8888, -}; - struct drm_plane *vc4_dummy_plane(struct kunit *test, struct drm_device *drm, enum drm_plane_type type) { struct drm_plane *plane; - plane = __drmm_universal_plane_alloc(drm, sizeof(struct drm_plane), 0, - 0, - &vc4_dummy_plane_funcs, - vc4_dummy_plane_formats, - ARRAY_SIZE(vc4_dummy_plane_formats), - NULL, - DRM_PLANE_TYPE_PRIMARY, - NULL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane); + KUNIT_ASSERT_EQ(test, type, DRM_PLANE_TYPE_PRIMARY); - drm_plane_helper_add(plane, &vc4_dummy_plane_helper_funcs); + plane = drm_kunit_helper_create_primary_plane(test, drm, + NULL, + NULL, + NULL, 0, + NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane); return plane; } From patchwork Mon Mar 11 14:49:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588854 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 9D5FAC54E60 for ; Mon, 11 Mar 2024 14:51:17 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id C03D2112AD3; Mon, 11 Mar 2024 14:51:16 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="CH4Ls6oe"; 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 8E970112AD3 for ; Mon, 11 Mar 2024 14:51:15 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id EDDC660DD7; Mon, 11 Mar 2024 14:51:14 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 695D8C433F1; Mon, 11 Mar 2024 14:51:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168674; bh=OZcorIJO1o+v2FQ5fprPdjhHAZXbQyuEG8TtVbtTpnM=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=CH4Ls6oeP+zOJV/xtHtP6HW4KLtJ6GrIAQDktTwTEG/IQwRu6n64TvYuxxTls8bEC Y2S5J3kh4y9/jRYSBOc8HLvtTFj5QJX8VXz86weAxS1YvONCNNlUcz42FPX7i6Au6i HTlVM0f9kDeC5sv9elg8KiqF5wOa1e74Rd440eynPJ6pILiI/Vflcct6K+3IfMZzw6 9yDGWOENybfwQG9oukArunUySKZfbfXtgNP6wGddhgjpcsKBQSeemYilnMIDus2m2L 7REIcm5MRcDnafGCzvFxVVatguz+LDc8BURErYcwIHhTHisKBw2tCgSYdRb3Zh5KKt 5a1Y/JUBHPiPA== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:54 +0100 Subject: [PATCH v9 26/27] drm/rockchip: inno_hdmi: Switch to HDMI connector MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-26-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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 X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=11744; i=mripard@kernel.org; h=from:subject:message-id; bh=OZcorIJO1o+v2FQ5fprPdjhHAZXbQyuEG8TtVbtTpnM=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpURdvykFpV08Zu74hYOxP8eoZP7GA2dWCq422PyMR +lvZNL6jlIWBjEuBlkxRZYYYfMlcadmve5k45sHM4eVCWQIAxenAEzk+zeG3+xLxNhPblle826r qOzslb/fnn63Q/Nmn/uXu7ylPO9emHcx/E/zkj7psKaY2cpgAu8Kzo0bREobrkS7CSX9UX6RJ3x OjAcA 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" The new HDMI connector infrastructure allows to remove some boilerplate, especially to generate infoframes. Let's switch to it. Reviewed-by: Heiko Stuebner Acked-by: Heiko Stuebner Signed-off-by: Maxime Ripard --- drivers/gpu/drm/rockchip/inno_hdmi.c | 143 +++++++++++++---------------------- 1 file changed, 52 insertions(+), 91 deletions(-) diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c index 1d2261643743..d59947679042 100644 --- a/drivers/gpu/drm/rockchip/inno_hdmi.c +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c @@ -65,13 +65,11 @@ struct inno_hdmi { const struct inno_hdmi_variant *variant; }; struct inno_hdmi_connector_state { struct drm_connector_state base; - unsigned int enc_out_format; unsigned int colorimetry; - bool rgb_limited_range; }; static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder) { struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); @@ -255,90 +253,53 @@ static void inno_hdmi_reset(struct inno_hdmi *hdmi) hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val); inno_hdmi_standby(hdmi); } -static void inno_hdmi_disable_frame(struct inno_hdmi *hdmi, - enum hdmi_infoframe_type type) +static int inno_hdmi_disable_frame(struct drm_connector *connector, + enum hdmi_infoframe_type type) { - struct drm_connector *connector = &hdmi->connector; - - if (type != HDMI_INFOFRAME_TYPE_AVI) { - drm_err(connector->dev, - "Unsupported infoframe type: %u\n", type); - return; - } - - hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI); -} - -static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, - union hdmi_infoframe *frame, enum hdmi_infoframe_type type) -{ - struct drm_connector *connector = &hdmi->connector; - u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE]; - ssize_t rc, i; + struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector); if (type != HDMI_INFOFRAME_TYPE_AVI) { drm_err(connector->dev, "Unsupported infoframe type: %u\n", type); return 0; } - inno_hdmi_disable_frame(hdmi, type); + hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI); - rc = hdmi_infoframe_pack(frame, packed_frame, - sizeof(packed_frame)); - if (rc < 0) - return rc; + return 0; +} - for (i = 0; i < rc; i++) +static int inno_hdmi_upload_frame(struct drm_connector *connector, + enum hdmi_infoframe_type type, + const u8 *buffer, size_t len) +{ + struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector); + u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE]; + ssize_t i; + + if (type != HDMI_INFOFRAME_TYPE_AVI) { + drm_err(connector->dev, + "Unsupported infoframe type: %u\n", type); + return 0; + } + + inno_hdmi_disable_frame(connector, type); + + for (i = 0; i < len; i++) hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, packed_frame[i]); return 0; } -static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi, - struct drm_display_mode *mode) -{ - struct drm_connector *connector = &hdmi->connector; - struct drm_connector_state *conn_state = connector->state; - struct inno_hdmi_connector_state *inno_conn_state = - to_inno_hdmi_conn_state(conn_state); - union hdmi_infoframe frame; - int rc; - - rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, - &hdmi->connector, - mode); - if (rc) { - inno_hdmi_disable_frame(hdmi, HDMI_INFOFRAME_TYPE_AVI); - return rc; - } - - if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444) - frame.avi.colorspace = HDMI_COLORSPACE_YUV444; - else if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV422) - frame.avi.colorspace = HDMI_COLORSPACE_YUV422; - else - frame.avi.colorspace = HDMI_COLORSPACE_RGB; - - if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_RGB) { - drm_hdmi_avi_infoframe_quant_range(&frame.avi, - connector, mode, - inno_conn_state->rgb_limited_range ? - HDMI_QUANTIZATION_RANGE_LIMITED : - HDMI_QUANTIZATION_RANGE_FULL); - } else { - frame.avi.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT; - frame.avi.ycc_quantization_range = - HDMI_YCC_QUANTIZATION_RANGE_LIMITED; - } - - return inno_hdmi_upload_frame(hdmi, &frame, HDMI_INFOFRAME_TYPE_AVI); -} +static const struct drm_connector_hdmi_funcs inno_hdmi_hdmi_connector_funcs = { + .clear_infoframe = inno_hdmi_disable_frame, + .write_infoframe = inno_hdmi_upload_frame, +}; static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi) { struct drm_connector *connector = &hdmi->connector; struct drm_connector_state *conn_state = connector->state; @@ -359,12 +320,12 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi) value = v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) | v_VIDEO_OUTPUT_COLOR(0) | v_VIDEO_INPUT_CSP(0); hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value); - if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_RGB) { - if (inno_conn_state->rgb_limited_range) { + if (conn_state->hdmi.output_format == HDMI_COLORSPACE_RGB) { + if (!conn_state->hdmi.is_full_range) { csc_mode = CSC_RGB_0_255_TO_RGB_16_235_8BIT; auto_csc = AUTO_CSC_DISABLE; c0_c2_change = C0_C2_CHANGE_DISABLE; csc_enable = v_CSC_ENABLE; @@ -378,18 +339,18 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi) v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE)); return 0; } } else { if (inno_conn_state->colorimetry == HDMI_COLORIMETRY_ITU_601) { - if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444) { + if (conn_state->hdmi.output_format == HDMI_COLORSPACE_YUV444) { csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT; auto_csc = AUTO_CSC_DISABLE; c0_c2_change = C0_C2_CHANGE_DISABLE; csc_enable = v_CSC_ENABLE; } } else { - if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444) { + if (conn_state->hdmi.output_format == HDMI_COLORSPACE_YUV444) { csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT; auto_csc = AUTO_CSC_DISABLE; c0_c2_change = C0_C2_CHANGE_DISABLE; csc_enable = v_CSC_ENABLE; } @@ -460,14 +421,16 @@ static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi, return 0; } static int inno_hdmi_setup(struct inno_hdmi *hdmi, - struct drm_display_mode *mode) + struct drm_crtc_state *new_crtc_state, + struct drm_connector_state *new_conn_state) { - struct drm_display_info *display = &hdmi->connector.display_info; - unsigned long mpixelclock = mode->clock * 1000; + struct drm_connector *connector = &hdmi->connector; + struct drm_display_info *display = &connector->display_info; + struct drm_display_mode *mode = &new_crtc_state->adjusted_mode; /* Mute video and audio output */ hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK, v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1)); @@ -477,26 +440,26 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi, inno_hdmi_config_video_timing(hdmi, mode); inno_hdmi_config_video_csc(hdmi); - if (display->is_hdmi) - inno_hdmi_config_video_avi(hdmi, mode); + drm_atomic_helper_connector_hdmi_update_infoframes(connector, + new_conn_state->state); /* * When IP controller have configured to an accurate video * timing, then the TMDS clock source would be switched to * DCLK_LCDC, so we need to init the TMDS rate to mode pixel * clock rate, and reconfigure the DDC clock. */ - inno_hdmi_i2c_init(hdmi, mpixelclock); + inno_hdmi_i2c_init(hdmi, new_conn_state->hdmi.tmds_char_rate); /* Unmute video and audio output */ hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK, v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0)); - inno_hdmi_power_up(hdmi, mpixelclock); + inno_hdmi_power_up(hdmi, new_conn_state->hdmi.tmds_char_rate); return 0; } static enum drm_mode_status inno_hdmi_display_mode_valid(struct inno_hdmi *hdmi, @@ -544,11 +507,11 @@ static void inno_hdmi_encoder_enable(struct drm_encoder *encoder, crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); if (WARN_ON(!crtc_state)) return; - inno_hdmi_setup(hdmi, &crtc_state->adjusted_mode); + inno_hdmi_setup(hdmi, crtc_state, conn_state); } static void inno_hdmi_encoder_disable(struct drm_encoder *encoder, struct drm_atomic_state *state) { @@ -561,11 +524,10 @@ static int inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); - struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder); struct drm_display_mode *mode = &crtc_state->adjusted_mode; u8 vic = drm_match_cea_mode(mode); struct inno_hdmi_connector_state *inno_conn_state = to_inno_hdmi_conn_state(conn_state); @@ -578,16 +540,11 @@ inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder, vic == 17 || vic == 18) inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_601; else inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709; - inno_conn_state->enc_out_format = HDMI_COLORSPACE_RGB; - inno_conn_state->rgb_limited_range = - drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED; - - return inno_hdmi_display_mode_valid(hdmi, - &crtc_state->adjusted_mode) == MODE_OK ? 0 : -EINVAL; + return 0; } static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = { .atomic_check = inno_hdmi_encoder_atomic_check, .atomic_enable = inno_hdmi_encoder_enable, @@ -660,14 +617,13 @@ static void inno_hdmi_connector_reset(struct drm_connector *connector) inno_conn_state = kzalloc(sizeof(*inno_conn_state), GFP_KERNEL); if (!inno_conn_state) return; __drm_atomic_helper_connector_reset(connector, &inno_conn_state->base); + __drm_atomic_helper_connector_hdmi_reset(connector, connector->state); inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709; - inno_conn_state->enc_out_format = HDMI_COLORSPACE_RGB; - inno_conn_state->rgb_limited_range = false; } static struct drm_connector_state * inno_hdmi_connector_duplicate_state(struct drm_connector *connector) { @@ -696,10 +652,11 @@ static const struct drm_connector_funcs inno_hdmi_connector_funcs = { .atomic_duplicate_state = inno_hdmi_connector_duplicate_state, .atomic_destroy_state = inno_hdmi_connector_destroy_state, }; static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = { + .atomic_check = drm_atomic_helper_connector_hdmi_check, .get_modes = inno_hdmi_connector_get_modes, .mode_valid = inno_hdmi_connector_mode_valid, }; static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi) @@ -723,14 +680,18 @@ static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi) hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; drm_connector_helper_add(&hdmi->connector, &inno_hdmi_connector_helper_funcs); - drm_connector_init_with_ddc(drm, &hdmi->connector, - &inno_hdmi_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA, - hdmi->ddc); + drmm_connector_hdmi_init(drm, &hdmi->connector, + "Rockchip", "Inno HDMI", + &inno_hdmi_connector_funcs, + &inno_hdmi_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA, + hdmi->ddc, + BIT(HDMI_COLORSPACE_RGB), + 8); drm_connector_attach_encoder(&hdmi->connector, encoder); return 0; } From patchwork Mon Mar 11 14:49:55 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "mripard@kernel.org" X-Patchwork-Id: 13588856 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 A7D5BC54E58 for ; Mon, 11 Mar 2024 14:51:23 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id CE669112ADA; Mon, 11 Mar 2024 14:51:22 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.b="eqUQjXV0"; 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 48FDF112AD7 for ; Mon, 11 Mar 2024 14:51:18 +0000 (UTC) Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id 9BADC60E88; Mon, 11 Mar 2024 14:51:17 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1B5DFC433C7; Mon, 11 Mar 2024 14:51:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1710168677; bh=ynm2eUkg4770MXlhVxVBYTFOvcdjKNlePExaCrKIcX0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=eqUQjXV0IugkqwEhFj8QGYP23P6JbSkGYcye3j9cFt+X4JutlKZFOm2FHnjEKrdGC QhvG9acVs0pMBxBofVjyq66CTl4horVTiJ6O/jTe0bVncjpoHAWpwTVJqxP1YTC9Y5 9e4Fly8Fiy+okJCi60UK6cAtp6JLib4JX1Fntk6Y4lrmJPXuHJiI7mYEv2Sf5Z/9X/ BrhCZuWUKoJeyvt+BlzB30HOh+8Vg2KbP5WxuN8+RZppAFWU2mawhMhi9Q14DR2N/R euNs3C6dbnkNHK4BzvmDxpGqYRGLfok+UMLsX/mxJjwZdrJnaGWdSItn/dxhGzf4qu 6ntPpt9MzPQnw== From: Maxime Ripard Date: Mon, 11 Mar 2024 15:49:55 +0100 Subject: [PATCH v9 27/27] drm/sun4i: hdmi: Switch to HDMI connector MIME-Version: 1.0 Message-Id: <20240311-kms-hdmi-connector-state-v9-27-d45890323344@kernel.org> References: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@kernel.org> In-Reply-To: <20240311-kms-hdmi-connector-state-v9-0-d45890323344@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?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 , Sui Jingfeng X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=7059; i=mripard@kernel.org; h=from:subject:message-id; bh=ynm2eUkg4770MXlhVxVBYTFOvcdjKNlePExaCrKIcX0=; b=owGbwMvMwCX2+D1vfrpE4FHG02pJDKnvpUQ/if/P3bnG7tLBZce1Hh5s3VUgcUp3wZyrcW1fs xdUCgZZdpSyMIhxMciKKbLECJsviTs163UnG988mDmsTCBDGLg4BWAiNy0Y/tfWZkdce95wqu+B bYBh8O1O3d8nWt1l7h/NN+YplOpy6GH4X5EVd/ot/4L40Av7rHlXbKt/r823ePLzLw8boqL2rD7 lyAUA 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" The new HDMI connector infrastructure allows to remove some boilerplate, especially to generate infoframes. Let's switch to it. Reviewed-by: Jernej Skrabec Acked-by: Sui Jingfeng Signed-off-by: Maxime Ripard --- drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c | 80 ++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c index b7cf369b1906..8a9106a39f23 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c @@ -34,34 +34,28 @@ container_of_const(e, struct sun4i_hdmi, encoder) #define drm_connector_to_sun4i_hdmi(c) \ container_of_const(c, struct sun4i_hdmi, connector) -static int sun4i_hdmi_setup_avi_infoframes(struct sun4i_hdmi *hdmi, - struct drm_display_mode *mode) +static int sun4i_hdmi_write_infoframe(struct drm_connector *connector, + enum hdmi_infoframe_type type, + const u8 *buffer, size_t len) { - struct hdmi_avi_infoframe frame; - u8 buffer[17]; - int i, ret; + struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector); + int i; - ret = drm_hdmi_avi_infoframe_from_display_mode(&frame, - &hdmi->connector, mode); - if (ret < 0) { - DRM_ERROR("Failed to get infoframes from mode\n"); - return ret; + if (type != HDMI_INFOFRAME_TYPE_AVI) { + drm_err(connector->dev, + "Unsupported infoframe type: %u\n", type); + return 0; } - ret = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer)); - if (ret < 0) { - DRM_ERROR("Failed to pack infoframes\n"); - return ret; - } - - for (i = 0; i < sizeof(buffer); i++) + for (i = 0; i < len; i++) writeb(buffer[i], hdmi->base + SUN4I_HDMI_AVI_INFOFRAME_REG(i)); return 0; + } static void sun4i_hdmi_disable(struct drm_encoder *encoder, struct drm_atomic_state *state) { @@ -80,18 +74,22 @@ static void sun4i_hdmi_disable(struct drm_encoder *encoder, static void sun4i_hdmi_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) { struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder); - struct drm_display_info *display = &hdmi->connector.display_info; + struct drm_connector *connector = &hdmi->connector; + struct drm_display_info *display = &connector->display_info; + struct drm_connector_state *conn_state = + drm_atomic_get_new_connector_state(state, connector); + unsigned long long tmds_rate = conn_state->hdmi.tmds_char_rate; unsigned int x, y; u32 val = 0; DRM_DEBUG_DRIVER("Enabling the HDMI Output\n"); - clk_set_rate(hdmi->mod_clk, mode->crtc_clock * 1000); - clk_set_rate(hdmi->tmds_clk, mode->crtc_clock * 1000); + clk_set_rate(hdmi->mod_clk, tmds_rate); + clk_set_rate(hdmi->tmds_clk, tmds_rate); /* Set input sync enable */ writel(SUN4I_HDMI_UNKNOWN_INPUT_SYNC, hdmi->base + SUN4I_HDMI_UNKNOWN_REG); @@ -140,11 +138,12 @@ static void sun4i_hdmi_enable(struct drm_encoder *encoder, writel(val, hdmi->base + SUN4I_HDMI_VID_TIMING_POL_REG); clk_prepare_enable(hdmi->tmds_clk); - sun4i_hdmi_setup_avi_infoframes(hdmi, mode); + drm_atomic_helper_connector_hdmi_update_infoframes(connector, state); + val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI); val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END); writel(val, hdmi->base + SUN4I_HDMI_PKT_CTRL_REG(0)); val = SUN4I_HDMI_VID_CTRL_ENABLE; @@ -193,23 +192,26 @@ static int sun4i_hdmi_connector_atomic_check(struct drm_connector *connector, struct drm_crtc_state *crtc_state = crtc->state; struct drm_display_mode *mode = &crtc_state->adjusted_mode; enum drm_mode_status status; status = sun4i_hdmi_connector_clock_valid(connector, mode, - mode->clock * 1000); + conn_state->hdmi.tmds_char_rate); if (status != MODE_OK) return -EINVAL; return 0; } static enum drm_mode_status sun4i_hdmi_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - return sun4i_hdmi_connector_clock_valid(connector, mode, - mode->clock * 1000); + unsigned long long rate = + drm_connector_hdmi_compute_mode_clock(mode, 8, + HDMI_COLORSPACE_RGB); + + return sun4i_hdmi_connector_clock_valid(connector, mode, rate); } static int sun4i_hdmi_get_modes(struct drm_connector *connector) { struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector); @@ -251,10 +253,15 @@ static struct i2c_adapter *sun4i_hdmi_get_ddc(struct device *dev) return ERR_PTR(-EPROBE_DEFER); return ddc; } +static const struct drm_connector_hdmi_funcs sun4i_hdmi_hdmi_connector_funcs = { + .tmds_char_rate_valid = sun4i_hdmi_connector_clock_valid, + .write_infoframe = sun4i_hdmi_write_infoframe, +}; + static const struct drm_connector_helper_funcs sun4i_hdmi_connector_helper_funcs = { .atomic_check = sun4i_hdmi_connector_atomic_check, .mode_valid = sun4i_hdmi_connector_mode_valid, .get_modes = sun4i_hdmi_get_modes, }; @@ -272,15 +279,21 @@ sun4i_hdmi_connector_detect(struct drm_connector *connector, bool force) } return connector_status_connected; } +static void sun4i_hdmi_connector_reset(struct drm_connector *connector) +{ + drm_atomic_helper_connector_reset(connector); + __drm_atomic_helper_connector_hdmi_reset(connector, connector->state); +} + static const struct drm_connector_funcs sun4i_hdmi_connector_funcs = { .detect = sun4i_hdmi_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = drm_connector_cleanup, - .reset = drm_atomic_helper_connector_reset, + .reset = sun4i_hdmi_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; #ifdef CONFIG_DRM_SUN4I_HDMI_CEC @@ -635,14 +648,23 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master, hdmi->base + SUN4I_HDMI_CEC); #endif drm_connector_helper_add(&hdmi->connector, &sun4i_hdmi_connector_helper_funcs); - ret = drm_connector_init_with_ddc(drm, &hdmi->connector, - &sun4i_hdmi_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA, - hdmi->ddc_i2c); + ret = drmm_connector_hdmi_init(drm, &hdmi->connector, + /* + * NOTE: Those are likely to be + * wrong, but I couldn't find the + * actual ones in the BSP. + */ + "AW", "HDMI", + &sun4i_hdmi_connector_funcs, + &sun4i_hdmi_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA, + hdmi->ddc_i2c, + BIT(HDMI_COLORSPACE_RGB), + 8); if (ret) { dev_err(dev, "Couldn't initialise the HDMI connector\n"); goto err_cleanup_connector; }