diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 81fb349ece5f..2b8401b6d4d6 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -67,21 +67,43 @@ static bool is_best_fixed_mode(struct intel_connector *connector, if (!best_mode) return true; - /* - * With VRR always pick a mode with equal/higher than requested - * vrefresh, which we can then reduce to match the requested - * vrefresh by extending the vblank length. - */ - if (intel_vrr_is_in_range(connector, vrefresh) && - intel_vrr_is_in_range(connector, fixed_mode_vrefresh) && - fixed_mode_vrefresh < vrefresh) - return false; - /* pick the fixed_mode that is closest in terms of vrefresh */ return abs(fixed_mode_vrefresh - vrefresh) < abs(drm_mode_vrefresh(best_mode) - vrefresh); } +static const struct drm_display_mode * +intel_panel_fixed_mode_vrr(struct intel_connector *connector, + const struct drm_display_mode *mode) +{ + const struct drm_display_mode *fixed_mode, *best_mode = NULL; + int vrefresh = drm_mode_vrefresh(mode); + + if (!intel_vrr_is_in_range(connector, vrefresh)) + return NULL; + + list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) { + int fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode); + + if (!intel_vrr_is_in_range(connector, fixed_mode_vrefresh)) + continue; + + /* + * With VRR always pick a mode with equal/higher than requested + * vrefresh, which we can then reduce to match the requested + * vrefresh by extending the vblank length. + */ + if (fixed_mode_vrefresh < vrefresh) + continue; + + if (is_best_fixed_mode(connector, vrefresh, + fixed_mode_vrefresh, best_mode)) + best_mode = fixed_mode; + } + + return best_mode; +} + const struct drm_display_mode * intel_panel_fixed_mode(struct intel_connector *connector, const struct drm_display_mode *mode) @@ -197,47 +219,22 @@ enum drrs_type intel_panel_drrs_type(struct intel_connector *connector) return connector->panel.vbt.drrs_type; } -int intel_panel_compute_config(struct intel_connector *connector, - struct drm_display_mode *adjusted_mode) +static int intel_panel_compute_config_vrr(struct intel_connector *connector, + struct drm_display_mode *adjusted_mode) { - const struct drm_display_mode *fixed_mode = - intel_panel_fixed_mode(connector, adjusted_mode); + const struct drm_display_mode *fixed_mode; int vrefresh, fixed_mode_vrefresh; - bool is_vrr; + fixed_mode = intel_panel_fixed_mode_vrr(connector, adjusted_mode); if (!fixed_mode) - return 0; + return -EINVAL; vrefresh = drm_mode_vrefresh(adjusted_mode); fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode); - /* - * Assume that we shouldn't muck about with the - * timings if they don't land in the VRR range. - */ - is_vrr = intel_vrr_is_in_range(connector, vrefresh) && - intel_vrr_is_in_range(connector, fixed_mode_vrefresh); - - if (!is_vrr) { - /* - * We don't want to lie too much to the user about the refresh - * rate they're going to get. But we have to allow a bit of latitude - * for Xorg since it likes to automagically cook up modes with slightly - * off refresh rates. - */ - if (abs(vrefresh - fixed_mode_vrefresh) > 1) { - drm_dbg_kms(connector->base.dev, - "[CONNECTOR:%d:%s] Requested mode vrefresh (%d Hz) does not match fixed mode vrefresh (%d Hz)\n", - connector->base.base.id, connector->base.name, - vrefresh, fixed_mode_vrefresh); - - return -EINVAL; - } - } - drm_mode_copy(adjusted_mode, fixed_mode); - if (is_vrr && fixed_mode_vrefresh != vrefresh) { + if (fixed_mode_vrefresh != vrefresh) { int vsync_start_offset = adjusted_mode->vtotal - adjusted_mode->vsync_start; int vsync_end_offset = adjusted_mode->vtotal - adjusted_mode->vsync_end; @@ -254,6 +251,53 @@ int intel_panel_compute_config(struct intel_connector *connector, return 0; } +static int intel_panel_compute_config_fixed_rr(struct intel_connector *connector, + struct drm_display_mode *adjusted_mode) +{ + const struct drm_display_mode *fixed_mode; + int vrefresh, fixed_mode_vrefresh; + + fixed_mode = intel_panel_fixed_mode(connector, adjusted_mode); + if (!fixed_mode) + return 0; + + vrefresh = drm_mode_vrefresh(adjusted_mode); + fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode); + + /* + * We don't want to lie too much to the user about the refresh + * rate they're going to get. But we have to allow a bit of latitude + * for Xorg since it likes to automagically cook up modes with slightly + * off refresh rates. + */ + if (abs(vrefresh - fixed_mode_vrefresh) > 1) { + drm_dbg_kms(connector->base.dev, + "[CONNECTOR:%d:%s] Requested mode vrefresh (%d Hz) does not match fixed mode vrefresh (%d Hz)\n", + connector->base.base.id, connector->base.name, + vrefresh, fixed_mode_vrefresh); + + return -EINVAL; + } + + drm_mode_copy(adjusted_mode, fixed_mode); + + drm_mode_set_crtcinfo(adjusted_mode, 0); + + return 0; +} + +int intel_panel_compute_config(struct intel_connector *connector, + struct drm_display_mode *adjusted_mode) +{ + int ret; + + ret = intel_panel_compute_config_vrr(connector, adjusted_mode); + if (ret) + ret = intel_panel_compute_config_fixed_rr(connector, adjusted_mode); + + return ret; +} + static void intel_panel_add_edid_alt_fixed_modes(struct intel_connector *connector) { struct intel_display *display = to_intel_display(connector);