From nobody Fri Sep 25 16:02:52 2026 Received: from smtp-8fae.mail.infomaniak.ch (smtp-8fae.mail.infomaniak.ch [83.166.143.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0E94F58E2C8 for ; Thu, 10 Sep 2026 18:56:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=83.166.143.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789066623; cv=none; b=n+aYTFsIyUgNCEtDq+MsFOEf/Pmz921QSc0vtBi2wGPZ7iYEQveXzHv4hCC+VzRc2cU9eQvH4GbIthnrfTsAGZ6beYz46a7chuGGvsasqCZXvZw8+aDMVoNpE2SfDOYCZCYGaVSIjbU+c3H2xCMFE1WOff02Mp8vxYVbBZTsXq8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789066623; c=relaxed/simple; bh=bAG76qlu9oaRJYAAdIIzEDQtqG/bBgx4rgMWo3RtEDg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EYGVqavPQXcP4yg6PqETrfWgvmmIFndqhdq2LB1dqbRZS0TzlMBW3MWLnxpRU7+sHgpPaWgcDwZYeS8f58R4NwboPH/REWeyhwrl6ueFeY3BEo7aLXB+eczcAdZoQguwPJ9ljhq1FkyX13ZKK5vtdn8QceTJFEs5CLfyzigkq6Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cinemaone.ch; spf=pass smtp.mailfrom=cinemaone.ch; dkim=pass (2048-bit key) header.d=cinemaone.ch header.i=@cinemaone.ch header.b=vaeAadkt; arc=none smtp.client-ip=83.166.143.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cinemaone.ch Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cinemaone.ch Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cinemaone.ch header.i=@cinemaone.ch header.b="vaeAadkt" Received: from smtp-3-0000.mail.infomaniak.ch (smtp-3-0000.mail.infomaniak.ch [10.4.36.107]) by smtp-3-3000.mail.infomaniak.ch (Postfix) with ESMTPS id 4hgn363twqzHqd; Thu, 10 Sep 2026 20:56:50 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cinemaone.ch; s=20250113; t=1789066610; bh=79untcTBsMIh0N42cp6pQROVUMP4JU0DTMgmss7VSJQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=vaeAadktJowWe+S26ivlP6ydI3jCWGFQTkyc3tM9VdKDAJmcphttPR3bj6AwXTxoN vh84MZtGZxMziRHJ7MHdUmbKv4h598k5xsRr1rxelMWiidvdHPXrjYPnZ3m173hh4b SCST2iUwlMz0mMR628DxPZSi2XrjSTngu8fk4b0O4AfgCSeySoF3T1oKH7whFIj919 utNNUwQ9atIyRjCRh1zeLxv3JrzB6+tVuLSD/yh+5IPe2E/t1DbxUFLYKW8fckVVKv GgDavr38ZGkVIoFJ9Huq6vrZmLzezX9J/h2ahVwYWSwsb1omnqSSdafmsPlm36Zo89 7lbjihjYx9sEg== Received: from unknown by smtp-3-0000.mail.infomaniak.ch (Postfix) with ESMTPA id 4hgn344h4gzngl; Thu, 10 Sep 2026 20:56:48 +0200 (CEST) From: Adrian Betschart To: amd-gfx@lists.freedesktop.org Cc: dri-devel@lists.freedesktop.org, Harry Wentland , Leo Li , Rodrigo Siqueira , Alex Deucher , =?UTF-8?q?Christian=20K=C3=B6nig?= , Satyajit Roy , Alex Hung , linux-kernel@vger.kernel.org Subject: [PATCH v2] drm/amd/display: Default HDMI RGB output to limited range on CTA modes Date: Thu, 10 Sep 2026 20:56:48 +0200 Message-ID: <20260910185648.75182-1-adrian.betschart@cinemaone.ch> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260910181517.68781-1-adrian.betschart@cinemaone.ch> References: <20260910181517.68781-1-adrian.betschart@cinemaone.ch> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Infomaniak-Routing: alpha Content-Type: text/plain; charset="utf-8" With the Broadcast RGB property at Automatic, amdgpu_dm_get_output_color_sp= ace() selects COLOR_SPACE_SRGB for RGB output, so DC neither compresses the pixels nor, without a QS-capable sink, signals full range in the AVI InfoFrame. A = sink that follows CTA-861 treats the default quantization of a CTA video format = as limited and expands 16-235 to 0-255: everything below 16 is crushed to blac= k, everything above 235 clips. Follow the CTA-861 default for Automatic instead, as the DRM HDMI state hel= per (hdmi_is_limited_range()) and i915 do: limited range on an HDMI sink for CTA modes other than 640x480, full range elsewhere. Full and Limited keep their explicit meaning. The same rule applies to the BT.2020 RGB branch. With commit a56a50074 ("drm/amd/display: Propagate HDMI RGB quantization selectability") the AVI InfoFrame then carries the matching Q value on sinks that support selection. Measured on a Radeon RX 7600 (DCN 3.2.1) driving a JVC DLA-RS4100 at 1920x1080p24 RGB 12 bpc: the sink reports the signal as limited range while the picture shows crushed shadows; rendering limited range in the client ma= kes it match a reference source. The KUnit fixtures that reach amdgpu_dm_get_output_color_space() now carry a connector, since Automatic reads its display_info, and five cases cover the new rule. Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/5796 Signed-off-by: Adrian Betschart Assisted-by: Claude Code:claude-fable-5-1 --- Changes in v2: - KUnit: give the AUTO test states a connector (the new rule reads connector->display_info.is_hdmi) and add cases for Automatic on an HDMI CTA mode, on VIC 1, on a non-HDMI sink, for Full on a CTA mode, and for BT.2020 RGB. Reported by the Sashiko review. .../display/amdgpu_dm/amdgpu_dm_connector.c | 22 +++- .../tests/amdgpu_dm_connector_test.c | 120 ++++++++++++++++++ 2 files changed, 140 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/= drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c index 0b446133e..13cbe099c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c @@ -734,6 +734,24 @@ get_aspect_ratio(const struct drm_display_mode *mode_i= n) } EXPORT_IF_KUNIT(get_aspect_ratio); =20 +/* + * CTA-861 5.1: RGB video formats default to limited range except 640x480 = (VIC 1). + * With Broadcast RGB left at Automatic follow that default, as i915 does. + */ +static bool rgb_output_is_limited_range(const struct dc_crtc_timing *dc_cr= tc_timing, + const struct drm_connector_state *connector_state) +{ + switch (connector_state->hdmi.broadcast_rgb) { + case DRM_HDMI_BROADCAST_RGB_FULL: + return false; + case DRM_HDMI_BROADCAST_RGB_LIMITED: + return true; + default: + return connector_state->connector->display_info.is_hdmi && + dc_crtc_timing->vic > 1; + } +} + enum dc_color_space amdgpu_dm_get_output_color_space(const struct dc_crtc_timing *dc_crtc_timi= ng, const struct drm_connector_state *connector_state) @@ -759,7 +777,7 @@ amdgpu_dm_get_output_color_space(const struct dc_crtc_t= iming *dc_crtc_timing, case DRM_MODE_COLORIMETRY_BT2020_RGB: case DRM_MODE_COLORIMETRY_BT2020_YCC: if (dc_crtc_timing->pixel_encoding =3D=3D PIXEL_ENCODING_RGB) { - if (connector_state->hdmi.broadcast_rgb =3D=3D DRM_HDMI_BROADCAST_RGB_L= IMITED) + if (rgb_output_is_limited_range(dc_crtc_timing, connector_state)) color_space =3D COLOR_SPACE_2020_RGB_LIMITEDRANGE; else color_space =3D COLOR_SPACE_2020_RGB_FULLRANGE; @@ -771,7 +789,7 @@ amdgpu_dm_get_output_color_space(const struct dc_crtc_t= iming *dc_crtc_timing, default: if (dc_crtc_timing->pixel_encoding =3D=3D PIXEL_ENCODING_RGB) { color_space =3D COLOR_SPACE_SRGB; - if (connector_state->hdmi.broadcast_rgb =3D=3D DRM_HDMI_BROADCAST_RGB_L= IMITED) + if (rgb_output_is_limited_range(dc_crtc_timing, connector_state)) color_space =3D COLOR_SPACE_SRGB_LIMITED; /* * 27030khz is the separation point between HDTV and SDTV diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connecto= r_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_= test.c index 6e70d7213..64ad47403 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c @@ -412,7 +412,9 @@ static void dm_test_output_color_space_default_rgb_full= (struct kunit *test) { struct dc_crtc_timing timing =3D {}; struct drm_connector_state state =3D {}; + struct drm_connector *connector =3D kunit_kzalloc(test, sizeof(*connector= ), GFP_KERNEL); =20 + state.connector =3D connector; timing.pixel_encoding =3D PIXEL_ENCODING_RGB; state.colorspace =3D DRM_MODE_COLORIMETRY_DEFAULT; state.hdmi.broadcast_rgb =3D DRM_HDMI_BROADCAST_RGB_AUTO; @@ -562,7 +564,9 @@ static void dm_test_output_color_space_bt2020_rgb(struc= t kunit *test) { struct dc_crtc_timing timing =3D {}; struct drm_connector_state state =3D {}; + struct drm_connector *connector =3D kunit_kzalloc(test, sizeof(*connector= ), GFP_KERNEL); =20 + state.connector =3D connector; timing.pixel_encoding =3D PIXEL_ENCODING_RGB; state.colorspace =3D DRM_MODE_COLORIMETRY_BT2020_RGB; =20 @@ -587,6 +591,110 @@ static void dm_test_output_color_space_bt2020_rgb_lim= ited(struct kunit *test) (int)COLOR_SPACE_2020_RGB_LIMITEDRANGE); } =20 +/** + * dm_test_output_color_space_default_rgb_auto_hdmi_cta - Automatic follow= s the CTA-861 default + * @test: The KUnit test context + */ +static void dm_test_output_color_space_default_rgb_auto_hdmi_cta(struct ku= nit *test) +{ + struct dc_crtc_timing timing =3D {}; + struct drm_connector_state state =3D {}; + struct drm_connector *connector =3D kunit_kzalloc(test, sizeof(*connector= ), GFP_KERNEL); + + connector->display_info.is_hdmi =3D true; + timing.pixel_encoding =3D PIXEL_ENCODING_RGB; + timing.vic =3D 32; + state.connector =3D connector; + state.colorspace =3D DRM_MODE_COLORIMETRY_DEFAULT; + state.hdmi.broadcast_rgb =3D DRM_HDMI_BROADCAST_RGB_AUTO; + + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &sta= te), + (int)COLOR_SPACE_SRGB_LIMITED); +} + +/** + * dm_test_output_color_space_default_rgb_auto_hdmi_vga - 640x480 (VIC 1) = stays full range + * @test: The KUnit test context + */ +static void dm_test_output_color_space_default_rgb_auto_hdmi_vga(struct ku= nit *test) +{ + struct dc_crtc_timing timing =3D {}; + struct drm_connector_state state =3D {}; + struct drm_connector *connector =3D kunit_kzalloc(test, sizeof(*connector= ), GFP_KERNEL); + + connector->display_info.is_hdmi =3D true; + timing.pixel_encoding =3D PIXEL_ENCODING_RGB; + timing.vic =3D 1; + state.connector =3D connector; + state.colorspace =3D DRM_MODE_COLORIMETRY_DEFAULT; + state.hdmi.broadcast_rgb =3D DRM_HDMI_BROADCAST_RGB_AUTO; + + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &sta= te), + (int)COLOR_SPACE_SRGB); +} + +/** + * dm_test_output_color_space_default_rgb_auto_dp_cta - Automatic on a non= -HDMI sink is full range + * @test: The KUnit test context + */ +static void dm_test_output_color_space_default_rgb_auto_dp_cta(struct kuni= t *test) +{ + struct dc_crtc_timing timing =3D {}; + struct drm_connector_state state =3D {}; + struct drm_connector *connector =3D kunit_kzalloc(test, sizeof(*connector= ), GFP_KERNEL); + + timing.pixel_encoding =3D PIXEL_ENCODING_RGB; + timing.vic =3D 32; + state.connector =3D connector; + state.colorspace =3D DRM_MODE_COLORIMETRY_DEFAULT; + state.hdmi.broadcast_rgb =3D DRM_HDMI_BROADCAST_RGB_AUTO; + + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &sta= te), + (int)COLOR_SPACE_SRGB); +} + +/** + * dm_test_output_color_space_default_rgb_full_hdmi_cta - Full overrides t= he CTA-861 default + * @test: The KUnit test context + */ +static void dm_test_output_color_space_default_rgb_full_hdmi_cta(struct ku= nit *test) +{ + struct dc_crtc_timing timing =3D {}; + struct drm_connector_state state =3D {}; + struct drm_connector *connector =3D kunit_kzalloc(test, sizeof(*connector= ), GFP_KERNEL); + + connector->display_info.is_hdmi =3D true; + timing.pixel_encoding =3D PIXEL_ENCODING_RGB; + timing.vic =3D 32; + state.connector =3D connector; + state.colorspace =3D DRM_MODE_COLORIMETRY_DEFAULT; + state.hdmi.broadcast_rgb =3D DRM_HDMI_BROADCAST_RGB_FULL; + + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &sta= te), + (int)COLOR_SPACE_SRGB); +} + +/** + * dm_test_output_color_space_bt2020_rgb_auto_hdmi_cta - BT.2020 RGB follo= ws the same default + * @test: The KUnit test context + */ +static void dm_test_output_color_space_bt2020_rgb_auto_hdmi_cta(struct kun= it *test) +{ + struct dc_crtc_timing timing =3D {}; + struct drm_connector_state state =3D {}; + struct drm_connector *connector =3D kunit_kzalloc(test, sizeof(*connector= ), GFP_KERNEL); + + connector->display_info.is_hdmi =3D true; + timing.pixel_encoding =3D PIXEL_ENCODING_RGB; + timing.vic =3D 32; + state.connector =3D connector; + state.colorspace =3D DRM_MODE_COLORIMETRY_BT2020_RGB; + state.hdmi.broadcast_rgb =3D DRM_HDMI_BROADCAST_RGB_AUTO; + + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &sta= te), + (int)COLOR_SPACE_2020_RGB_LIMITEDRANGE); +} + /** * dm_test_output_color_space_bt2020_ycc - Test Output color space bt2020 = ycc * @test: The KUnit test context @@ -650,7 +758,9 @@ static void dm_test_output_color_space_bt2020_ycc_rgb_e= ncoding(struct kunit *tes { struct dc_crtc_timing timing =3D {}; struct drm_connector_state state =3D {}; + struct drm_connector *connector =3D kunit_kzalloc(test, sizeof(*connector= ), GFP_KERNEL); =20 + state.connector =3D connector; timing.pixel_encoding =3D PIXEL_ENCODING_RGB; state.colorspace =3D DRM_MODE_COLORIMETRY_BT2020_YCC; =20 @@ -3594,6 +3704,7 @@ static struct dm_test_fill_ctx *dm_test_fill_ctx_allo= c(struct kunit *test) =20 ctx->conn_state =3D drmm_kzalloc(ctx->drm, sizeof(*ctx->conn_state), GFP_= KERNEL); KUNIT_ASSERT_NOT_NULL(test, ctx->conn_state); + ctx->conn_state->connector =3D &ctx->aconnector->base; ctx->stream =3D kunit_kzalloc(test, sizeof(*ctx->stream), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, ctx->stream); ctx->mode =3D kunit_kzalloc(test, sizeof(*ctx->mode), GFP_KERNEL); @@ -4063,6 +4174,7 @@ static struct dm_test_stream_ctx *dm_test_stream_ctx_= alloc(struct kunit *test) =20 ctx->dm_state =3D kunit_kzalloc(test, sizeof(*ctx->dm_state), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, ctx->dm_state); + ctx->dm_state->base.connector =3D &ctx->aconnector->base; ctx->dm_state->scaling =3D RMX_OFF; =20 ctx->mode =3D kunit_kzalloc(test, sizeof(*ctx->mode), GFP_KERNEL); @@ -6818,6 +6930,7 @@ dm_test_cvs_ctx_alloc(struct kunit *test, int connect= or_type) =20 ctx->dm_state =3D kunit_kzalloc(test, sizeof(*ctx->dm_state), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, ctx->dm_state); + ctx->dm_state->base.connector =3D &ctx->aconnector->base; ctx->dm_state->base.max_requested_bpc =3D 4; =20 ctx->mode =3D kunit_kzalloc(test, sizeof(*ctx->mode), GFP_KERNEL); @@ -6872,6 +6985,7 @@ static void dm_test_create_validate_stream_writeback(= struct kunit *test) =20 dm_state =3D kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, dm_state); + dm_state->base.connector =3D &wbcon->base.base; dm_state->scaling =3D RMX_OFF; =20 mode =3D kunit_kzalloc(test, sizeof(*mode), GFP_KERNEL); @@ -7120,6 +7234,7 @@ static struct dm_test_cvs_dc *dm_test_cvs_dc_alloc(st= ruct kunit *test) =20 c->dm_state =3D kunit_kzalloc(test, sizeof(*c->dm_state), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, c->dm_state); + c->dm_state->base.connector =3D &c->aconnector->base; c->dm_state->base.max_requested_bpc =3D 8; c->dm_state->scaling =3D RMX_OFF; =20 @@ -8940,6 +9055,11 @@ static struct kunit_case amdgpu_dm_connector_tests[]= =3D { KUNIT_CASE(dm_test_output_color_space_oprgb), KUNIT_CASE(dm_test_output_color_space_bt2020_rgb), KUNIT_CASE(dm_test_output_color_space_bt2020_rgb_limited), + KUNIT_CASE(dm_test_output_color_space_default_rgb_auto_hdmi_cta), + KUNIT_CASE(dm_test_output_color_space_default_rgb_auto_hdmi_vga), + KUNIT_CASE(dm_test_output_color_space_default_rgb_auto_dp_cta), + KUNIT_CASE(dm_test_output_color_space_default_rgb_full_hdmi_cta), + KUNIT_CASE(dm_test_output_color_space_bt2020_rgb_auto_hdmi_cta), KUNIT_CASE(dm_test_output_color_space_bt2020_ycc), KUNIT_CASE(dm_test_output_color_space_default_ycbcr709_y_only), KUNIT_CASE(dm_test_output_color_space_default_ycbcr601), --=20 2.55.0