diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 8dcbc4b76a1a..bf1fed201c66 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -1434,6 +1434,23 @@ bool intel_dp_has_dsc(const struct intel_connector *connector) return true; } +static +bool intel_dp_can_join(struct intel_display *display, + int num_joined_pipes) +{ + switch (num_joined_pipes) { + case 1: + return true; + case 2: + return HAS_BIGJOINER(display) || + HAS_UNCOMPRESSED_JOINER(display); + case 4: + return HAS_ULTRAJOINER(display); + default: + return false; + } +} + static enum drm_mode_status intel_dp_mode_valid(struct drm_connector *_connector, const struct drm_display_mode *mode) @@ -1445,7 +1462,6 @@ intel_dp_mode_valid(struct drm_connector *_connector, const struct drm_display_mode *fixed_mode; int target_clock = mode->clock; int max_rate, mode_rate, max_lanes, max_link_clock; - int max_dotclk = display->cdclk.max_dotclk_freq; u16 dsc_max_compressed_bpp = 0; u8 dsc_slice_count = 0; enum drm_mode_status status; @@ -1488,66 +1504,97 @@ intel_dp_mode_valid(struct drm_connector *_connector, target_clock, mode->hdisplay, link_bpp_x16, 0); - num_joined_pipes = intel_dp_num_joined_pipes(intel_dp, connector, - mode->hdisplay, target_clock); - max_dotclk *= num_joined_pipes; + /* + * We cannot determine the required pipe‑join count before knowing whether + * DSC is needed, nor can we determine DSC need without knowing the pipe + * count. + * Because of this dependency cycle, the only correct approach is to iterate + * over candidate pipe counts and evaluate each combination. + */ + status = MODE_CLOCK_HIGH; + for (num_joined_pipes = 1; num_joined_pipes <= I915_MAX_PIPES; num_joined_pipes++) { + int max_dotclk = display->cdclk.max_dotclk_freq; - if (target_clock > max_dotclk) - return MODE_CLOCK_HIGH; + if (connector->force_joined_pipes && + num_joined_pipes != connector->force_joined_pipes) + continue; - status = intel_pfit_mode_valid(display, mode, output_format, num_joined_pipes); - if (status != MODE_OK) - return status; + if (!intel_dp_can_join(display, num_joined_pipes)) + continue; - if (intel_dp_has_dsc(connector)) { - int pipe_bpp; + if (mode->hdisplay > num_joined_pipes * intel_dp_max_hdisplay_per_pipe(display)) + continue; - /* - * TBD pass the connector BPC, - * for now U8_MAX so that max BPC on that platform would be picked - */ - pipe_bpp = intel_dp_dsc_compute_max_bpp(connector, U8_MAX); + status = intel_pfit_mode_valid(display, mode, output_format, num_joined_pipes); + if (status != MODE_OK) + continue; - /* - * Output bpp is stored in 6.4 format so right shift by 4 to get the - * integer value since we support only integer values of bpp. - */ - if (intel_dp_is_edp(intel_dp)) { - dsc_max_compressed_bpp = - drm_edp_dsc_sink_output_bpp(connector->dp.dsc_dpcd) >> 4; + if (intel_dp_has_dsc(connector)) { + int pipe_bpp; - dsc_slice_count = - intel_dp_dsc_get_slice_count(connector, - target_clock, - mode->hdisplay, - num_joined_pipes); + /* + * TBD pass the connector BPC, + * for now U8_MAX so that max BPC on that platform would be picked + */ + pipe_bpp = intel_dp_dsc_compute_max_bpp(connector, U8_MAX); - dsc = dsc_max_compressed_bpp && dsc_slice_count; - } else if (drm_dp_sink_supports_fec(connector->dp.fec_capability)) { - unsigned long bw_overhead_flags = 0; + /* + * Output bpp is stored in 6.4 format so right shift by 4 to get the + * integer value since we support only integer values of bpp. + */ + if (intel_dp_is_edp(intel_dp)) { + dsc_max_compressed_bpp = + drm_edp_dsc_sink_output_bpp(connector->dp.dsc_dpcd) >> 4; - if (!drm_dp_is_uhbr_rate(max_link_clock)) - bw_overhead_flags |= DRM_DP_BW_OVERHEAD_FEC; + dsc_slice_count = + intel_dp_dsc_get_slice_count(connector, + target_clock, + mode->hdisplay, + num_joined_pipes); - dsc = intel_dp_mode_valid_with_dsc(connector, - max_link_clock, max_lanes, - target_clock, mode->hdisplay, - num_joined_pipes, - output_format, pipe_bpp, - bw_overhead_flags); + dsc = dsc_max_compressed_bpp && dsc_slice_count; + } else if (drm_dp_sink_supports_fec(connector->dp.fec_capability)) { + unsigned long bw_overhead_flags = 0; + + if (!drm_dp_is_uhbr_rate(max_link_clock)) + bw_overhead_flags |= DRM_DP_BW_OVERHEAD_FEC; + + dsc = intel_dp_mode_valid_with_dsc(connector, + max_link_clock, max_lanes, + target_clock, mode->hdisplay, + num_joined_pipes, + output_format, pipe_bpp, + bw_overhead_flags); + } } + + if (intel_dp_joiner_needs_dsc(display, num_joined_pipes) && !dsc) { + status = MODE_CLOCK_HIGH; + continue; + } + + if (mode_rate > max_rate && !dsc) { + status = MODE_CLOCK_HIGH; + continue; + } + + status = intel_mode_valid_max_plane_size(display, mode, num_joined_pipes); + if (status != MODE_OK) + continue; + + max_dotclk *= num_joined_pipes; + + if (target_clock > max_dotclk) { + status = MODE_CLOCK_HIGH; + continue; + } + + break; } - if (intel_dp_joiner_needs_dsc(display, num_joined_pipes) && !dsc) - return MODE_CLOCK_HIGH; - - status = intel_mode_valid_max_plane_size(display, mode, num_joined_pipes); if (status != MODE_OK) return status; - if (mode_rate > max_rate && !dsc) - return MODE_CLOCK_HIGH; - return intel_dp_mode_valid_downstream(connector, mode, target_clock); }