Commit a2da6702 authored by Ville Syrjälä's avatar Ville Syrjälä

drm/i915: Allow arbitrary refresh rates with VRR eDP panels

If the panel supports VRR it must be capable of accepting
timings with arbitrary vblank length, within the valid VRR
range. Use that fact to allow the user to request any refresh
rate they like. We simply pick the next highest fixed mode
from our list, and adjust the vblank to get the desired refresh
rate in the end.

Of course currently everything to do with the vrefresh is
using 1Hz precision, so might not be exact. But we can improve
that in the future by just upping our vrefresh precision.
Signed-off-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20230404175431.23064-1-ville.syrjala@linux.intel.comReviewed-by: default avatarMitul Golani <mitulkumar.ajitkumar.golani@intel.com>
parent e39c76b2
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include "intel_lvds_regs.h" #include "intel_lvds_regs.h"
#include "intel_panel.h" #include "intel_panel.h"
#include "intel_quirks.h" #include "intel_quirks.h"
#include "intel_vrr.h"
bool intel_panel_use_ssc(struct drm_i915_private *i915) bool intel_panel_use_ssc(struct drm_i915_private *i915)
{ {
...@@ -58,6 +59,38 @@ intel_panel_preferred_fixed_mode(struct intel_connector *connector) ...@@ -58,6 +59,38 @@ intel_panel_preferred_fixed_mode(struct intel_connector *connector)
struct drm_display_mode, head); struct drm_display_mode, head);
} }
static bool is_in_vrr_range(struct intel_connector *connector, int vrefresh)
{
const struct drm_display_info *info = &connector->base.display_info;
return intel_vrr_is_capable(connector) &&
vrefresh >= info->monitor_range.min_vfreq &&
vrefresh <= info->monitor_range.max_vfreq;
}
static bool is_best_fixed_mode(struct intel_connector *connector,
int vrefresh, int fixed_mode_vrefresh,
const struct drm_display_mode *best_mode)
{
/* we want to always return something */
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 (is_in_vrr_range(connector, vrefresh) &&
is_in_vrr_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);
}
const struct drm_display_mode * const struct drm_display_mode *
intel_panel_fixed_mode(struct intel_connector *connector, intel_panel_fixed_mode(struct intel_connector *connector,
const struct drm_display_mode *mode) const struct drm_display_mode *mode)
...@@ -65,11 +98,11 @@ intel_panel_fixed_mode(struct intel_connector *connector, ...@@ -65,11 +98,11 @@ intel_panel_fixed_mode(struct intel_connector *connector,
const struct drm_display_mode *fixed_mode, *best_mode = NULL; const struct drm_display_mode *fixed_mode, *best_mode = NULL;
int vrefresh = drm_mode_vrefresh(mode); int vrefresh = drm_mode_vrefresh(mode);
/* pick the fixed_mode that is closest in terms of vrefresh */
list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) { list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) {
if (!best_mode || int fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode);
abs(drm_mode_vrefresh(fixed_mode) - vrefresh) <
abs(drm_mode_vrefresh(best_mode) - vrefresh)) if (is_best_fixed_mode(connector, vrefresh,
fixed_mode_vrefresh, best_mode))
best_mode = fixed_mode; best_mode = fixed_mode;
} }
...@@ -178,27 +211,46 @@ int intel_panel_compute_config(struct intel_connector *connector, ...@@ -178,27 +211,46 @@ int intel_panel_compute_config(struct intel_connector *connector,
{ {
const struct drm_display_mode *fixed_mode = const struct drm_display_mode *fixed_mode =
intel_panel_fixed_mode(connector, adjusted_mode); intel_panel_fixed_mode(connector, adjusted_mode);
int vrefresh, fixed_mode_vrefresh;
bool is_vrr;
if (!fixed_mode) if (!fixed_mode)
return 0; 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 * Assume that we shouldn't muck about with the
* rate they're going to get. But we have to allow a bit of latitude * timings if they don't land in the VRR range.
* for Xorg since it likes to automagically cook up modes with slightly
* off refresh rates.
*/ */
if (abs(drm_mode_vrefresh(adjusted_mode) - drm_mode_vrefresh(fixed_mode)) > 1) { is_vrr = is_in_vrr_range(connector, vrefresh) &&
drm_dbg_kms(connector->base.dev, is_in_vrr_range(connector, fixed_mode_vrefresh);
"[CONNECTOR:%d:%s] Requested mode vrefresh (%d Hz) does not match fixed mode vrefresh (%d Hz)\n",
connector->base.base.id, connector->base.name,
drm_mode_vrefresh(adjusted_mode), drm_mode_vrefresh(fixed_mode));
return -EINVAL; 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); drm_mode_copy(adjusted_mode, fixed_mode);
if (is_vrr && fixed_mode_vrefresh != vrefresh)
adjusted_mode->vtotal =
DIV_ROUND_CLOSEST(adjusted_mode->clock * 1000,
adjusted_mode->htotal * vrefresh);
drm_mode_set_crtcinfo(adjusted_mode, 0); drm_mode_set_crtcinfo(adjusted_mode, 0);
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment