Act on the "min bpc" property's min_requested_bpc state in the common HDMI state helpers, and register the property on HDMI connectors.
Also add two KUnit tests to verify that the property behaves as expected. Signed-off-by: Nicolas Frattaroli <[email protected]> --- drivers/gpu/drm/display/drm_hdmi_state_helper.c | 10 +- drivers/gpu/drm/drm_connector.c | 4 + drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c | 146 +++++++++++++++++++++ 3 files changed, 156 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c index ce17eeefc2da..fa015d218b41 100644 --- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c @@ -636,10 +636,12 @@ hdmi_compute_format_bpc(const struct drm_connector *connector, unsigned int max_bpc, enum drm_output_color_format fmt) { struct drm_device *dev = connector->dev; - unsigned int bpc; + unsigned int bpc, min_bpc; int ret; - for (bpc = max_bpc; bpc >= 8; bpc -= 2) { + min_bpc = max(conn_state->min_requested_bpc, 8); + + for (bpc = max_bpc; bpc >= min_bpc; bpc -= 2) { ret = hdmi_try_format_bpc(connector, conn_state, mode, bpc, fmt); if (!ret) continue; @@ -657,8 +659,8 @@ hdmi_compute_format_bpc(const struct drm_connector *connector, return 0; } - drm_dbg_kms(dev, "Failed. %s output format not supported for any bpc count.\n", - drm_hdmi_connector_get_output_format_name(fmt)); + drm_dbg_kms(dev, "Failed. %s output format not supported for any bpc <= %u and >= %u.\n", + drm_hdmi_connector_get_output_format_name(fmt), max_bpc, min_bpc); return -EINVAL; } diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index dcf53cc113b0..5cf512add4ef 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -640,6 +640,10 @@ int drmm_connector_hdmi_init(struct drm_device *dev, if (ret) return ret; + ret = drm_connector_attach_min_bpc_property(connector, max_bpc); + if (ret) + return ret; + connector->hdmi.funcs = hdmi_funcs; return 0; diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c index 353a261d42da..8f8f7a528e08 100644 --- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c @@ -1361,6 +1361,150 @@ static void drm_test_check_tmds_char_rate_rgb_12bpc(struct kunit *test) drm_modeset_acquire_fini(&ctx); } +/* + * Test that an atomic check with a min bpc of 10 fails on a connector that + * maxes out at 8bpc even if the EDID could go higher. + */ +static void drm_test_check_min_bpc_10bit_connector_fail(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_atomic_commit *state; + struct drm_connector *conn; + struct drm_device *drm; + int ret; + + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444), + 8, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz); + KUNIT_ASSERT_NOT_NULL(test, priv); + + drm = &priv->drm; + conn = &priv->connector; + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + + drm_modeset_acquire_init(&ctx, 0); + +retry_conn_enable: + ret = drm_kunit_helper_enable_crtc_connector(test, drm, priv->crtc, + conn, preferred, &ctx); + if (ret == -EDEADLK) { + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_enable; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + +retry_conn_state: + conn_state = drm_atomic_get_connector_state(state, conn); + if (PTR_ERR(conn_state) == -EDEADLK) { + drm_atomic_commit_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_state; + } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + conn_state->min_requested_bpc = 10; + + ret = drm_atomic_check_only(state); + if (ret == -EDEADLK) { + drm_atomic_commit_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_state; + } + KUNIT_EXPECT_LT(test, ret, 0); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + +/* + * Test that an atomic check with a min bpc of 10 succeeds on a connector that + * can do 10bpc if the EDID can also do 10bpc. + */ +static void drm_test_check_min_bpc_10bit_success(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_atomic_commit *state; + struct drm_connector *conn; + struct drm_device *drm; + int ret; + + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444), + 12, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz); + KUNIT_ASSERT_NOT_NULL(test, priv); + + drm = &priv->drm; + conn = &priv->connector; + preferred = find_preferred_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, preferred); + KUNIT_ASSERT_TRUE(test, conn->display_info.edid_hdmi_rgb444_dc_modes & + DRM_EDID_HDMI_DC_30); + + drm_modeset_acquire_init(&ctx, 0); + +retry_conn_enable: + ret = drm_kunit_helper_enable_crtc_connector(test, drm, priv->crtc, + conn, preferred, &ctx); + if (ret == -EDEADLK) { + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_enable; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + +retry_conn_state: + conn_state = drm_atomic_get_connector_state(state, conn); + if (PTR_ERR(conn_state) == -EDEADLK) { + drm_atomic_commit_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_state; + } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + conn_state->min_requested_bpc = 10; + + ret = drm_atomic_check_only(state); + if (ret == -EDEADLK) { + drm_atomic_commit_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry_conn_state; + } + KUNIT_EXPECT_EQ(test, ret, 0); + + conn_state = drm_atomic_get_new_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + KUNIT_EXPECT_GE(test, conn_state->hdmi.output_bpc, 10); + KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, DRM_OUTPUT_COLOR_FORMAT_RGB444); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + /* * Test that if we filter a rate through our hook, it's indeed rejected * by the whole atomic_check logic. @@ -2476,6 +2620,8 @@ static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = { 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), + KUNIT_CASE(drm_test_check_min_bpc_10bit_connector_fail), + KUNIT_CASE(drm_test_check_min_bpc_10bit_success), KUNIT_CASE_PARAM(drm_test_check_hdmi_color_format, check_hdmi_color_format_gen_params), KUNIT_CASE_PARAM(drm_test_check_hdmi_color_format_420_only, -- 2.54.0
