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 2b0ffa20d98e..ba41ae10c4cd 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 @@ -855,16 +855,15 @@ STATIC_IFN_KUNIT void fill_stream_properties_from_drm_display_mode( const struct drm_connector *connector, const struct drm_connector_state *connector_state, const struct dc_stream_state *old_stream, - int requested_bpc) + int requested_bpc, + enum dc_pixel_encoding requested_encoding, + bool is_hdmi_ep) { - bool is_dp_or_hdmi = dc_is_hdmi_signal(stream->signal) || dc_is_dp_signal(stream->signal); struct dc_crtc_timing *timing_out = &stream->timing; const struct drm_display_info *info = &connector->display_info; struct amdgpu_dm_connector *aconnector = NULL; struct hdmi_vendor_infoframe hv_frame; struct hdmi_avi_infoframe avi_frame; - bool want_420; - bool want_422; ssize_t err; if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) @@ -878,40 +877,13 @@ STATIC_IFN_KUNIT void fill_stream_properties_from_drm_display_mode( timing_out->v_border_top = 0; timing_out->v_border_bottom = 0; - want_420 = (aconnector && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420) || - (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR420); - want_422 = (aconnector && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR422) || - (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR422); - - if (drm_mode_is_420_only(info, mode_in) && - (want_420 || connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_AUTO)) { - timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; - } else if (drm_mode_is_420_also(info, mode_in) && want_420) { - timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; - } else if ((info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) && - want_422 && is_dp_or_hdmi) { - timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR422; - } else if (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR444 && - (info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) && - is_dp_or_hdmi) { - timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444; - } else if (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_RGB444 || - connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_AUTO) { - timing_out->pixel_encoding = PIXEL_ENCODING_RGB; - } else { - /* - * If a format was explicitly requested but the requested format - * can't be satisfied, set it to an invalid value so that an - * error bubbles up to userspace. This way, userspace knows it - * needs to make a better choice. - */ - if (connector_state->color_format != DRM_CONNECTOR_COLOR_FORMAT_AUTO) - timing_out->pixel_encoding = PIXEL_ENCODING_UNDEFINED; - else if (drm_mode_is_420_only(info, mode_in)) - timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; - else - timing_out->pixel_encoding = PIXEL_ENCODING_RGB; - } + /* + * The pixel encoding to use is decided entirely by the caller (see + * amdgpu_dm_create_validate_stream_for_sink()), which enumerates only + * the encodings the sink actually advertises. This helper must not + * second-guess that choice; it simply applies it. + */ + timing_out->pixel_encoding = requested_encoding; timing_out->timing_3d_format = TIMING_3D_FORMAT_NONE; timing_out->display_color_depth = amdgpu_dm_convert_color_depth_from_display_info( @@ -977,14 +949,15 @@ STATIC_IFN_KUNIT void fill_stream_properties_from_drm_display_mode( stream->out_transfer_func.type = TF_TYPE_PREDEFINED; stream->out_transfer_func.tf = TRANSFER_FUNCTION_SRGB; - if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A) { - if (!adjust_colour_depth_from_display_info(timing_out, info) && - drm_mode_is_420_also(info, mode_in) && - timing_out->pixel_encoding != PIXEL_ENCODING_YCBCR420) { - timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; - adjust_colour_depth_from_display_info(timing_out, info); - } - } + + /* Clamp the HDMI colour depth to what the sink's max TMDS clock + * allows. The pixel encoding is fixed by the caller and must not be + * changed here: the caller already enumerates YCbCr420 as its own + * candidate, so silently switching to it would hide an enumeration + * bug and produce a stream the caller never asked to validate. + */ + if (is_hdmi_ep) + adjust_colour_depth_from_display_info(timing_out, info); stream->output_color_space = amdgpu_dm_get_output_color_space(timing_out, connector_state); stream->content_type = get_output_content_type(connector_state); @@ -1451,7 +1424,9 @@ create_stream_for_sink(struct drm_connector *connector, const struct drm_display_mode *drm_mode, const struct dm_connector_state *dm_state, const struct dc_stream_state *old_stream, - int requested_bpc) + int requested_bpc, + enum dc_pixel_encoding requested_encoding, + bool is_hdmi_ep) { struct drm_device *dev = connector->dev; struct amdgpu_dm_connector *aconnector = NULL; @@ -1562,11 +1537,11 @@ create_stream_for_sink(struct drm_connector *connector, if (!scale || mode_refresh != preferred_refresh) fill_stream_properties_from_drm_display_mode( stream, &mode, connector, con_state, NULL, - requested_bpc); + requested_bpc, requested_encoding, is_hdmi_ep); else fill_stream_properties_from_drm_display_mode( stream, &mode, connector, con_state, old_stream, - requested_bpc); + requested_bpc, requested_encoding, is_hdmi_ep); /* The rest isn't needed for writeback connectors */ if (!aconnector) @@ -2271,13 +2246,32 @@ amdgpu_dm_create_validate_stream_for_sink(struct drm_connector *connector, const struct dm_connector_state *dm_state, const struct dc_stream_state *old_stream) { + /* + * Ordered lists of the encodings and bit depths we are willing to + * validate, best quality/bandwidth first. The per-sink masks built + * below gate which of these entries are actually attempted. + */ + static const enum dc_pixel_encoding encoding_order[] = { + PIXEL_ENCODING_YCBCR444, + PIXEL_ENCODING_RGB, + PIXEL_ENCODING_YCBCR422, + PIXEL_ENCODING_YCBCR420, + }; + static const u8 bpc_order[] = { 16, 12, 10, 8, 6 }; + struct amdgpu_dm_connector *aconnector = NULL; struct amdgpu_device *adev = drm_to_adev(connector->dev); - struct dc_stream_state *stream; + const struct drm_display_info *info = &connector->display_info; + struct dc_stream_state *stream = NULL; const struct drm_connector_state *drm_state = dm_state ? &dm_state->base : NULL; int requested_bpc = drm_state ? drm_state->max_requested_bpc : 8; enum dc_status dc_result = DC_OK; - uint8_t bpc_limit = 6; + enum signal_type signal = SIGNAL_TYPE_NONE; + bool want_420, want_422, is_dp_or_hdmi; + u32 encoding_mask = 0; + u32 bpc_mask = 0; + bool is_hdmi_ep = false; + unsigned int i, j; if (!dm_state) return NULL; @@ -2285,90 +2279,179 @@ amdgpu_dm_create_validate_stream_for_sink(struct drm_connector *connector, if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) aconnector = to_amdgpu_dm_connector(connector); - if (aconnector && - (aconnector->dc_link->connector_signal == SIGNAL_TYPE_HDMI_TYPE_A || - aconnector->dc_link->connector_signal == SIGNAL_TYPE_HDMI_FRL || - aconnector->dc_link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER)) - bpc_limit = 8; - - do { - drm_dbg_kms(connector->dev, "Trying with %d bpc\n", requested_bpc); - stream = create_stream_for_sink(connector, drm_mode, - dm_state, old_stream, - requested_bpc); - if (stream == NULL) { + /* + * Writeback connectors have no sink EDID to enumerate against. They + * only ever use RGB at the requested depth, so build and validate a + * single stream directly and return it (dc_validate_stream() is not + * meaningful for the writeback path). + */ + if (!aconnector) { + stream = create_stream_for_sink(connector, drm_mode, dm_state, + old_stream, requested_bpc, + PIXEL_ENCODING_RGB, is_hdmi_ep); + if (!stream) drm_err(adev_to_drm(adev), "Failed to create stream for sink!\n"); - break; - } + return stream; + } - dc_result = dc_validate_stream(adev->dm.dc, stream); + signal = aconnector->dc_link->connector_signal; - if (!aconnector) /* writeback connector */ - return stream; + /* + * Determine whether this is a native HDMI sink or a DP->HDMI dongle. + * Since we check for it a few times below, cache the result. + */ + is_hdmi_ep = (signal == SIGNAL_TYPE_HDMI_TYPE_A || + signal == SIGNAL_TYPE_HDMI_FRL || + aconnector->dc_link->dpcd_caps.dongle_type == + DISPLAY_DONGLE_DP_HDMI_CONVERTER); + is_dp_or_hdmi = dc_is_hdmi_signal(signal) || dc_is_dp_signal(signal); - if (dc_result == DC_OK && stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) - dc_result = dm_dp_mst_is_port_support_mode(aconnector, stream); + /* + * Build the set of pixel encodings this sink advertises, so we only + * ever validate combinations the display can actually accept. Ordered + * best-first: RGB / YCbCr444 (full bandwidth) -> YCbCr422 -> YCbCr420. + * + * - RGB is the mandatory baseline and always available. + * - YCbCr444 is only meaningful for native HDMI sinks. + * - A 420-only mode collapses the mask to YCbCr420 alone. + * - The debugfs force_yuv420_output / force_yuv422_output overrides + * pin the encoding to a single value when set. An explicit YCbCr420 + * force is honoured even on modes the sink only lists as RGB/4:4:4 + * capable (drm_mode_is_420_also() clear), as required for HDMI + * compliance testing; dc_validate_stream() still rejects anything + * the link genuinely cannot carry. The YCbCr422 force stays gated on + * the sink's advertised caps. + */ + want_420 = (aconnector && aconnector->force_yuv420_output) || + (drm_state && drm_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR420); + want_422 = (aconnector && aconnector->force_yuv422_output) || + (drm_state && drm_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR422); - if (dc_result == DC_OK) - dc_result = dm_validate_stream_and_context(adev->dm.dc, stream); + if (drm_mode_is_420_only(info, drm_mode) && + (want_420 || (drm_state && drm_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_AUTO))) { + encoding_mask = BIT(PIXEL_ENCODING_YCBCR420); + } else if (drm_mode_is_420_also(info, drm_mode) && want_420) { + encoding_mask = BIT(PIXEL_ENCODING_YCBCR420); + } else if ((info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) && + want_422 && is_dp_or_hdmi) { + encoding_mask = BIT(PIXEL_ENCODING_YCBCR422); + } else if ((drm_state && drm_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR444) && + (info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) && + is_dp_or_hdmi) { + encoding_mask = BIT(PIXEL_ENCODING_YCBCR444); + } else if (drm_state && drm_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_RGB444) { + encoding_mask = BIT(PIXEL_ENCODING_RGB); + } else { + encoding_mask = BIT(PIXEL_ENCODING_RGB); - if (dc_result == DC_OK) - dc_result = dm_validate_stream_color_format(drm_state, stream); + if ((info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) && + is_hdmi_ep) + encoding_mask |= BIT(PIXEL_ENCODING_YCBCR444); - if (dc_result != DC_OK) { - drm_dbg_kms(connector->dev, "Pruned mode %d x %d (clk %d) %s %s -- %s\n", + if (info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) + encoding_mask |= BIT(PIXEL_ENCODING_YCBCR422); + + if (drm_mode_is_420_also(info, drm_mode)) + encoding_mask |= BIT(PIXEL_ENCODING_YCBCR420); + } + + /* + * Build the set of bit depths to try, high-to-low. Start from the + * atomic-requested cap and clear anything the sink cannot do: + * + * - Never exceed the requested bpc. + * - HDMI/FRL and DP->HDMI dongles have no defined 6 bpc mode. + * - Any depth above the EDID-reported bpc is dropped. + * + * amdgpu_dm_convert_color_depth_from_display_info() applies the final + * per-encoding cap (e.g. YCbCr420 deep-colour limits) when the stream + * is built, so this mask only needs the coarse sink limits. + */ + for (j = 0; j < ARRAY_SIZE(bpc_order); j++) { + u8 bpc = bpc_order[j]; + + if (requested_bpc > 0 && bpc > requested_bpc) + continue; + + if (bpc == 6 && is_hdmi_ep) + continue; + + if (info->bpc && bpc > info->bpc) + continue; + + bpc_mask |= BIT(bpc); + } + + /* + * Enumerate the supported (encoding, bpc) combinations in priority + * order and return the first that validates. Because the masks only + * contain sink-supported entries, this is generic across connector + * types and needs no encoding-specific fallback afterwards. + * + * The selection lives entirely on the stack and is passed by value to + * create_stream_for_sink(); no shared connector state is mutated. This + * matters because this helper runs concurrently from the connector + * probe worker (->mode_valid) and from a compositor's atomic check on + * the same connector. + */ + for (i = 0; i < ARRAY_SIZE(encoding_order); i++) { + enum dc_pixel_encoding enc = encoding_order[i]; + + if (!(encoding_mask & BIT(enc))) + continue; + + for (j = 0; j < ARRAY_SIZE(bpc_order); j++) { + u8 bpc = bpc_order[j]; + + if (!(bpc_mask & BIT(bpc))) + continue; + + drm_dbg_kms(connector->dev, + "Trying %s with %d bpc (encoding_mask=0x%x bpc_mask=0x%x requested_bpc=%d drm max_bpc)\n", + dc_pixel_encoding_to_str(enc), bpc, + encoding_mask, bpc_mask, requested_bpc); + + stream = create_stream_for_sink(connector, drm_mode, + dm_state, old_stream, + bpc, enc, is_hdmi_ep); + if (stream == NULL) { + drm_err(adev_to_drm(adev), "Failed to create stream for sink!\n"); + return NULL; + } + + dc_result = dc_validate_stream(adev->dm.dc, stream); + + if (dc_result == DC_OK && + stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) + dc_result = dm_dp_mst_is_port_support_mode(aconnector, stream); + + if (dc_result == DC_OK) + dc_result = dm_validate_stream_and_context(adev->dm.dc, stream); + + if (dc_result == DC_OK) + dc_result = dm_validate_stream_color_format(drm_state, stream); + + if (dc_result == DC_OK) + return stream; + + drm_dbg_kms(connector->dev, "Pruned mode %d x %d (refresh rate %d) %s %s -- %s\n", drm_mode->hdisplay, drm_mode->vdisplay, - drm_mode->clock, + drm_mode_vrefresh(drm_mode), dc_pixel_encoding_to_str(stream->timing.pixel_encoding), dc_color_depth_to_str(stream->timing.display_color_depth), dc_status_to_str(dc_result)); dc_stream_release(stream); stream = NULL; - requested_bpc -= 2; /* lower bpc to retry validation */ } - - } while (stream == NULL && requested_bpc >= bpc_limit); - - switch (dc_result) { - /* - * If we failed to validate DP bandwidth stream with the requested RGB color depth, - * we try to fallback and configure in order: - * YUV422 (8bpc, 6bpc) - * YUV420 (8bpc, 6bpc) - */ - case DC_FAIL_ENC_VALIDATE: - case DC_EXCEED_DONGLE_CAP: - case DC_NO_DP_LINK_BANDWIDTH: - /* recursively entered twice and already tried both YUV422 and YUV420 */ - if (aconnector->force_yuv422_output && aconnector->force_yuv420_output) - break; - /* first failure; try YUV422 */ - if (!aconnector->force_yuv422_output) { - drm_dbg_kms(connector->dev, "%s:%d Validation failed with %d, retrying w/ YUV422\n", - __func__, __LINE__, dc_result); - aconnector->force_yuv422_output = true; - /* recursively entered and YUV422 failed, try YUV420 */ - } else if (!aconnector->force_yuv420_output) { - drm_dbg_kms(connector->dev, "%s:%d Validation failed with %d, retrying w/ YUV420\n", - __func__, __LINE__, dc_result); - aconnector->force_yuv420_output = true; - } - stream = amdgpu_dm_create_validate_stream_for_sink(connector, drm_mode, - dm_state, old_stream); - aconnector->force_yuv422_output = false; - aconnector->force_yuv420_output = false; - break; - case DC_OK: - break; - default: - drm_dbg_kms(connector->dev, "%s:%d Unhandled validation failure %d\n", - __func__, __LINE__, dc_result); - break; } - return stream; + /* + * Every sink-supported combination was exhausted without validating; + * the mode is genuinely unsupported on this link. + */ + return NULL; } EXPORT_IF_KUNIT(amdgpu_dm_create_validate_stream_for_sink); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h index 13c54229d72c..4e9eb3ff2c90 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h @@ -189,13 +189,17 @@ void fill_stream_properties_from_drm_display_mode( const struct drm_connector *connector, const struct drm_connector_state *connector_state, const struct dc_stream_state *old_stream, - int requested_bpc); + int requested_bpc, + enum dc_pixel_encoding requested_encoding, + bool is_hdmi_ep); struct dc_stream_state * create_stream_for_sink(struct drm_connector *connector, const struct drm_display_mode *drm_mode, const struct dm_connector_state *dm_state, const struct dc_stream_state *old_stream, - int requested_bpc); + int requested_bpc, + enum dc_pixel_encoding requested_encoding, + bool is_hdmi_ep); enum drm_connector_status amdgpu_dm_connector_poll(struct amdgpu_dm_connector *aconnector, bool force); enum drm_connector_status diff --git 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 index 1a8f02c78c4d..1658d4d5997a 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 @@ -3416,7 +3416,8 @@ static void dm_test_fill_stream_borders_zeroed(struct kunit *test) timing->v_border_bottom = 8; fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode, - &ctx->aconnector->base, ctx->conn_state, NULL, 8); + &ctx->aconnector->base, ctx->conn_state, NULL, 8, + PIXEL_ENCODING_RGB, false); KUNIT_EXPECT_EQ(test, (int)timing->h_border_left, 0); KUNIT_EXPECT_EQ(test, (int)timing->h_border_right, 0); @@ -3437,7 +3438,8 @@ static void dm_test_fill_stream_rgb_defaults(struct kunit *test) struct dc_crtc_timing *timing = &ctx->stream->timing; fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode, - &ctx->aconnector->base, ctx->conn_state, NULL, 8); + &ctx->aconnector->base, ctx->conn_state, NULL, 8, + PIXEL_ENCODING_RGB, false); KUNIT_EXPECT_EQ(test, (int)timing->pixel_encoding, (int)PIXEL_ENCODING_RGB); KUNIT_EXPECT_EQ(test, (int)timing->timing_3d_format, @@ -3463,7 +3465,8 @@ static void dm_test_fill_stream_sync_polarity_positive(struct kunit *test) ctx->mode->flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC; fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode, - &ctx->aconnector->base, ctx->conn_state, NULL, 8); + &ctx->aconnector->base, ctx->conn_state, NULL, 8, + PIXEL_ENCODING_RGB, false); KUNIT_EXPECT_EQ(test, (int)timing->flags.HSYNC_POSITIVE_POLARITY, 1); KUNIT_EXPECT_EQ(test, (int)timing->flags.VSYNC_POSITIVE_POLARITY, 1); @@ -3482,7 +3485,8 @@ static void dm_test_fill_stream_sync_polarity_negative(struct kunit *test) ctx->mode->flags = 0; fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode, - &ctx->aconnector->base, ctx->conn_state, NULL, 8); + &ctx->aconnector->base, ctx->conn_state, NULL, 8, + PIXEL_ENCODING_RGB, false); KUNIT_EXPECT_EQ(test, (int)timing->flags.HSYNC_POSITIVE_POLARITY, 0); KUNIT_EXPECT_EQ(test, (int)timing->flags.VSYNC_POSITIVE_POLARITY, 0); @@ -3511,7 +3515,8 @@ static void dm_test_fill_stream_inherits_old_stream(struct kunit *test) ctx->mode->flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC; fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode, - &ctx->aconnector->base, ctx->conn_state, old_stream, 8); + &ctx->aconnector->base, ctx->conn_state, old_stream, 8, + PIXEL_ENCODING_RGB, false); KUNIT_EXPECT_EQ(test, (int)timing->vic, 16); KUNIT_EXPECT_EQ(test, (int)timing->flags.HSYNC_POSITIVE_POLARITY, 1); @@ -3541,7 +3546,8 @@ static void dm_test_fill_stream_timing_from_crtc(struct kunit *test) ctx->mode->crtc_clock = 148500; fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode, - &ctx->aconnector->base, ctx->conn_state, NULL, 8); + &ctx->aconnector->base, ctx->conn_state, NULL, 8, + PIXEL_ENCODING_RGB, false); KUNIT_EXPECT_EQ(test, (int)timing->h_addressable, 1920); KUNIT_EXPECT_EQ(test, (int)timing->h_total, 2200); @@ -3568,7 +3574,8 @@ static void dm_test_fill_stream_color_depth_requested_bpc(struct kunit *test) ctx->aconnector->base.display_info.bpc = 12; fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode, - &ctx->aconnector->base, ctx->conn_state, NULL, 10); + &ctx->aconnector->base, ctx->conn_state, NULL, 10, + PIXEL_ENCODING_RGB, false); KUNIT_EXPECT_EQ(test, (int)timing->display_color_depth, (int)COLOR_DEPTH_101010); @@ -3585,7 +3592,8 @@ static void dm_test_fill_stream_content_type(struct kunit *test) ctx->conn_state->content_type = DRM_MODE_CONTENT_TYPE_GRAPHICS; fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode, - &ctx->aconnector->base, ctx->conn_state, NULL, 8); + &ctx->aconnector->base, ctx->conn_state, NULL, 8, + PIXEL_ENCODING_RGB, false); KUNIT_EXPECT_EQ(test, (int)ctx->stream->content_type, (int)DISPLAY_CONTENT_TYPE_GRAPHICS); @@ -3603,12 +3611,121 @@ static void dm_test_fill_stream_aspect_ratio(struct kunit *test) ctx->mode->picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9; fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode, - &ctx->aconnector->base, ctx->conn_state, NULL, 8); + &ctx->aconnector->base, ctx->conn_state, NULL, 8, + PIXEL_ENCODING_RGB, false); KUNIT_EXPECT_EQ(test, (int)timing->aspect_ratio, (int)ASPECT_RATIO_16_9); } +/** + * dm_test_fill_stream_encoding_from_caller_ycbcr420 - Test caller-selected 420 + * @test: The KUnit test context + * + * The helper no longer derives the pixel encoding from the display info; it + * applies whatever the caller selected. Passing YCbCr420 must be honoured even + * though the DisplayPort sink advertises no YCbCr color formats. + */ +static void dm_test_fill_stream_encoding_from_caller_ycbcr420(struct kunit *test) +{ + struct dm_test_fill_ctx *ctx = dm_test_fill_ctx_alloc(test); + struct dc_crtc_timing *timing = &ctx->stream->timing; + + fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode, + &ctx->aconnector->base, ctx->conn_state, NULL, 8, + PIXEL_ENCODING_YCBCR420, false); + + KUNIT_EXPECT_EQ(test, (int)timing->pixel_encoding, + (int)PIXEL_ENCODING_YCBCR420); +} + +/** + * dm_test_fill_stream_encoding_from_caller_ycbcr422 - Test caller-selected 422 + * @test: The KUnit test context + * + * A caller-selected YCbCr422 encoding is applied verbatim. + */ +static void dm_test_fill_stream_encoding_from_caller_ycbcr422(struct kunit *test) +{ + struct dm_test_fill_ctx *ctx = dm_test_fill_ctx_alloc(test); + struct dc_crtc_timing *timing = &ctx->stream->timing; + + fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode, + &ctx->aconnector->base, ctx->conn_state, NULL, 8, + PIXEL_ENCODING_YCBCR422, false); + + KUNIT_EXPECT_EQ(test, (int)timing->pixel_encoding, + (int)PIXEL_ENCODING_YCBCR422); +} + +/** + * dm_test_fill_stream_encoding_from_caller_ycbcr444 - Test caller-selected 444 + * @test: The KUnit test context + * + * A caller-selected YCbCr444 encoding is applied verbatim. + */ +static void dm_test_fill_stream_encoding_from_caller_ycbcr444(struct kunit *test) +{ + struct dm_test_fill_ctx *ctx = dm_test_fill_ctx_alloc(test); + struct dc_crtc_timing *timing = &ctx->stream->timing; + + fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode, + &ctx->aconnector->base, ctx->conn_state, NULL, 8, + PIXEL_ENCODING_YCBCR444, false); + + KUNIT_EXPECT_EQ(test, (int)timing->pixel_encoding, + (int)PIXEL_ENCODING_YCBCR444); +} + +/** + * dm_test_fill_stream_hdmi_ep_clamps_depth - Test HDMI TMDS depth clamp applied + * @test: The KUnit test context + * + * With is_hdmi_ep set the colour depth is clamped to what the sink's max TMDS + * clock allows: a 10bpc request that exceeds the limit is reduced to 8bpc. + */ +static void dm_test_fill_stream_hdmi_ep_clamps_depth(struct kunit *test) +{ + struct dm_test_fill_ctx *ctx = dm_test_fill_ctx_alloc(test); + struct dc_crtc_timing *timing = &ctx->stream->timing; + + ctx->aconnector->base.display_info.bpc = 10; + /* 10bpc RGB needs 185625 KHz, over the sink's 160 MHz TMDS limit. */ + ctx->aconnector->base.display_info.max_tmds_clock = 160000; + ctx->mode->crtc_clock = 148500; + + fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode, + &ctx->aconnector->base, ctx->conn_state, NULL, 10, + PIXEL_ENCODING_RGB, true); + + KUNIT_EXPECT_EQ(test, (int)timing->display_color_depth, + (int)COLOR_DEPTH_888); +} + +/** + * dm_test_fill_stream_non_hdmi_ep_keeps_depth - Test no TMDS clamp off HDMI + * @test: The KUnit test context + * + * With is_hdmi_ep clear the TMDS clamp is skipped, so the same over-limit + * 10bpc request is left untouched. The clamp is HDMI-specific. + */ +static void dm_test_fill_stream_non_hdmi_ep_keeps_depth(struct kunit *test) +{ + struct dm_test_fill_ctx *ctx = dm_test_fill_ctx_alloc(test); + struct dc_crtc_timing *timing = &ctx->stream->timing; + + ctx->aconnector->base.display_info.bpc = 10; + ctx->aconnector->base.display_info.max_tmds_clock = 160000; + ctx->mode->crtc_clock = 148500; + + fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode, + &ctx->aconnector->base, ctx->conn_state, NULL, 10, + PIXEL_ENCODING_RGB, false); + + KUNIT_EXPECT_EQ(test, (int)timing->display_color_depth, + (int)COLOR_DEPTH_101010); +} + /* Tests for create_stream_for_sink() */ /* @@ -3687,7 +3804,8 @@ static void dm_test_create_stream_fake_sink_success(struct kunit *test) struct dc_stream_state *stream; stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode, - ctx->dm_state, NULL, 8); + ctx->dm_state, NULL, 8, + PIXEL_ENCODING_RGB, false); KUNIT_ASSERT_NOT_NULL(test, stream); dc_stream_release(stream); @@ -3703,7 +3821,8 @@ static void dm_test_create_stream_sets_dm_context(struct kunit *test) struct dc_stream_state *stream; stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode, - ctx->dm_state, NULL, 8); + ctx->dm_state, NULL, 8, + PIXEL_ENCODING_RGB, false); KUNIT_ASSERT_NOT_NULL(test, stream); KUNIT_EXPECT_PTR_EQ(test, stream->dm_stream_context, ctx->aconnector); @@ -3720,7 +3839,8 @@ static void dm_test_create_stream_virtual_signal(struct kunit *test) struct dc_stream_state *stream; stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode, - ctx->dm_state, NULL, 8); + ctx->dm_state, NULL, 8, + PIXEL_ENCODING_RGB, false); KUNIT_ASSERT_NOT_NULL(test, stream); KUNIT_EXPECT_EQ(test, (int)stream->signal, (int)SIGNAL_TYPE_VIRTUAL); @@ -3739,7 +3859,8 @@ static void dm_test_create_stream_scaling_src(struct kunit *test) struct dc_stream_state *stream; stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode, - ctx->dm_state, NULL, 8); + ctx->dm_state, NULL, 8, + PIXEL_ENCODING_RGB, false); KUNIT_ASSERT_NOT_NULL(test, stream); KUNIT_EXPECT_EQ(test, (int)stream->src.width, 1920); @@ -3770,7 +3891,8 @@ static void dm_test_create_stream_existing_sink(struct kunit *test) ctx->aconnector->dc_sink = sink; stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode, - ctx->dm_state, NULL, 8); + ctx->dm_state, NULL, 8, + PIXEL_ENCODING_RGB, false); KUNIT_ASSERT_NOT_NULL(test, stream); KUNIT_EXPECT_PTR_EQ(test, stream->sink, sink); @@ -5456,6 +5578,11 @@ static struct kunit_case amdgpu_dm_connector_tests[] = { KUNIT_CASE(dm_test_fill_stream_color_depth_requested_bpc), KUNIT_CASE(dm_test_fill_stream_content_type), KUNIT_CASE(dm_test_fill_stream_aspect_ratio), + KUNIT_CASE(dm_test_fill_stream_encoding_from_caller_ycbcr420), + KUNIT_CASE(dm_test_fill_stream_encoding_from_caller_ycbcr422), + KUNIT_CASE(dm_test_fill_stream_encoding_from_caller_ycbcr444), + KUNIT_CASE(dm_test_fill_stream_hdmi_ep_clamps_depth), + KUNIT_CASE(dm_test_fill_stream_non_hdmi_ep_keeps_depth), /* create_stream_for_sink */ KUNIT_CASE(dm_test_create_stream_fake_sink_success), KUNIT_CASE(dm_test_create_stream_sets_dm_context),