Introduce a DRM interface for DRM clients to further restrict the
VRR Range within the panel supported VRR range on a per-commit
basis.

The goal is to give DRM client the ability to do frame-doubling/
ramping themselves, or to set lower static refresh rates for power
savings.

This is done through 2 new CRTC properties, along with a client
cap. See the docstrings in patch for details.

This RFC doesn't include a driver-side implementation yet; that is
coming soon. Currently, looking to get some comments on whether this
interface makes sense for both compositor and drivers
---
 drivers/gpu/drm/drm_atomic_uapi.c | 30 +++++++++++++++++++++---
 drivers/gpu/drm/drm_connector.c   | 25 ++++++++++++++++++++
 drivers/gpu/drm/drm_file.c        |  2 ++
 drivers/gpu/drm/drm_ioctl.c       |  7 ++++++
 drivers/gpu/drm/drm_mode_config.c | 14 ++++++++++++
 include/drm/drm_connector.h       |  1 +
 include/drm/drm_crtc.h            | 38 +++++++++++++++++++++++++++++++
 include/drm/drm_file.h            | 10 ++++++++
 include/drm/drm_mode_config.h     | 18 +++++++++++++++
 include/uapi/drm/drm.h            | 17 ++++++++++++++
 10 files changed, 159 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_uapi.c 
b/drivers/gpu/drm/drm_atomic_uapi.c
index 85dbdaa4a2e2..73f929cff4e1 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -365,8 +365,8 @@ static s32 __user *get_out_fence_for_connector(struct 
drm_atomic_state *state,
 }
 
 static int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
-               struct drm_crtc_state *state, struct drm_property *property,
-               uint64_t val)
+               struct drm_crtc_state *state, struct drm_file *file_priv,
+               struct drm_property *property, uint64_t val)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_mode_config *config = &dev->mode_config;
@@ -421,6 +421,26 @@ static int drm_atomic_crtc_set_property(struct drm_crtc 
*crtc,
                state->scaling_filter = val;
        } else if (crtc->funcs->atomic_set_property) {
                return crtc->funcs->atomic_set_property(crtc, state, property, 
val);
+       } else if (property == config->prop_vrr_range_control_min) {
+               if (file_priv->vrr_range_control) {
+                       drm_dbg_atomic(dev, "Setting vrr_range_min crtc 
property not"
+                               "permitted with 
DRM_CLIENT_CAP_VRR_RANGE_CONTROL"
+                               "client cap\n");
+                       return -EINVAL;
+               }
+               if (!val)
+                       return -EINVAL;
+               state->vrr_range_min = val;
+       } else if (property == config->prop_vrr_range_control_max) {
+               if (file_priv->vrr_range_control) {
+                       drm_dbg_atomic(dev,"Setting vrr_range_max crtc property 
not"
+                               "permitted with 
DRM_CLIENT_CAP_VRR_RANGE_CONTROL"
+                               "client cap\n");
+                       return -EINVAL;
+               }
+               if (!val)
+                       return -EINVAL;
+               state->vrr_range_max = val;
        } else {
                drm_dbg_atomic(crtc->dev,
                               "[CRTC:%d:%s] unknown property [PROP:%d:%s]\n",
@@ -446,6 +466,10 @@ drm_atomic_crtc_get_property(struct drm_crtc *crtc,
                *val = (state->mode_blob) ? state->mode_blob->base.id : 0;
        else if (property == config->prop_vrr_enabled)
                *val = state->vrr_enabled;
+       else if (property == config->prop_vrr_range_control_min)
+               *val = state->vrr_range_min;
+       else if (property == config->prop_vrr_range_control_max)
+               *val = state->vrr_range_max;
        else if (property == config->degamma_lut_property)
                *val = (state->degamma_lut) ? state->degamma_lut->base.id : 0;
        else if (property == config->ctm_property)
@@ -1062,7 +1086,7 @@ int drm_atomic_set_property(struct drm_atomic_state 
*state,
                }
 
                ret = drm_atomic_crtc_set_property(crtc,
-                               crtc_state, prop, prop_value);
+                               crtc_state, file_priv, prop, prop_value);
                break;
        }
        case DRM_MODE_OBJECT_PLANE: {
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 272d6254ea47..dc4b50ff5fe0 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -2866,6 +2866,31 @@ int 
drm_connector_attach_hdr_output_metadata_property(struct drm_connector *conn
 }
 EXPORT_SYMBOL(drm_connector_attach_hdr_output_metadata_property);
 
+/**
+ * drm_connector_attach_vrr_range_control_property - attach 
"VRR_RANGE_CONTROL_MIN" and
+ * "VRR_RANGE_CONTROL_MAX" property
+ *
+ * @connector: connector to attach the property on.
+ *
+ * This is used to allow the userspace to send VRR range control min and max 
value to the
+ * driver.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_connector_attach_vrr_range_control_property(struct drm_connector 
*connector)
+{
+       struct drm_device *dev = connector->dev;
+       struct drm_property *prop_min = 
dev->mode_config.prop_vrr_range_control_min;
+       struct drm_property *prop_max = 
dev->mode_config.prop_vrr_range_control_max;
+
+       drm_object_attach_property(&connector->base, prop_min, 0);
+       drm_object_attach_property(&connector->base, prop_max, 0);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_connector_attach_vrr_range_control_property);
+
 /**
  * drm_connector_attach_broadcast_rgb_property - attach "Broadcast RGB" 
property
  * @connector: connector to attach the property on.
diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c
index eebd1a05ee97..7ed28e94544a 100644
--- a/drivers/gpu/drm/drm_file.c
+++ b/drivers/gpu/drm/drm_file.c
@@ -157,6 +157,8 @@ struct drm_file *drm_file_alloc(struct drm_minor *minor)
        init_waitqueue_head(&file->event_wait);
        file->event_space = 4096; /* set aside 4k for event buffer */
 
+       file->vrr_range_control = false; /* set as false for init */
+
        spin_lock_init(&file->master_lookup_lock);
        mutex_init(&file->event_read_lock);
        mutex_init(&file->client_name_lock);
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index d8a24875a7ba..273139688ba1 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -373,6 +373,13 @@ drm_setclientcap(struct drm_device *dev, void *data, 
struct drm_file *file_priv)
                        return -EINVAL;
                file_priv->supports_virtualized_cursor_plane = req->value;
                break;
+       case DRM_CLIENT_CAP_VRR_RANGE_CONTROL:
+               if (!file_priv->atomic)
+                       return -EINVAL;
+               if (req->value == 0)
+                       return -EINVAL;
+               file_priv->vrr_range_control = req->value;
+               break;
        default:
                return -EINVAL;
        }
diff --git a/drivers/gpu/drm/drm_mode_config.c 
b/drivers/gpu/drm/drm_mode_config.c
index 25f376869b3a..1f74284208c6 100644
--- a/drivers/gpu/drm/drm_mode_config.c
+++ b/drivers/gpu/drm/drm_mode_config.c
@@ -340,6 +340,20 @@ static int drm_mode_create_standard_properties(struct 
drm_device *dev)
                return -ENOMEM;
        dev->mode_config.prop_vrr_enabled = prop;
 
+       prop = drm_property_create_range(dev,
+                       DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_RANGE,
+                       "VRR-RANGE-CONTROL-MIN", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_vrr_range_control_min = prop;
+
+       prop = drm_property_create_range(dev,
+                       DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_RANGE,
+                       "VRR-RANGE-CONTROL-MAX", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_vrr_range_control_max = prop;
+
        prop = drm_property_create(dev,
                        DRM_MODE_PROP_BLOB,
                        "DEGAMMA_LUT", 0);
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 8f34f4b8183d..dd2c3337235a 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -2451,6 +2451,7 @@ int drm_connector_attach_vrr_capable_property(
 int drm_connector_attach_broadcast_rgb_property(struct drm_connector 
*connector);
 int drm_connector_attach_colorspace_property(struct drm_connector *connector);
 int drm_connector_attach_hdr_output_metadata_property(struct drm_connector 
*connector);
+int drm_connector_attach_vrr_range_control_property(struct drm_connector 
*connector);
 bool drm_connector_atomic_hdr_metadata_equal(struct drm_connector_state 
*old_state,
                                             struct drm_connector_state 
*new_state);
 int drm_mode_create_aspect_ratio_property(struct drm_device *dev);
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index caa56e039da2..39d1bf66f713 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -299,6 +299,44 @@ struct drm_crtc_state {
         */
        bool vrr_enabled;
 
+       /** @vrr_range_min:
+        *
+        * This is desired minimal FPS number.
+        *
+        * Default state is 'vrr_range_min = 0', (and 'vrr_range_max = 0'),
+        * indicating legacy VRR_ENABLED behavior. if both are set to a non-zeo
+        * value, the new VRR range control behavior will be active.  See
+        * &DRM_CLIENT_CAP_VRR_RANGE_CONTROL.
+        *
+        * If setting a non-zero value, the driver should check that:
+        *
+        * 1. Both vrr_range_min and vrr_range_max are set to a non-zero value.
+        *    This indicates the driver to switch the new VRR range control
+        *    behavior.
+        * 2. Both vrr_rage_min and vrr_range_max are within the panel's 
supported
+        *    FPS range.
+        * 3. vrr_range_min is less-than-or-equal-to vrr_range_max.
+        */
+       uint16_t vrr_range_min;
+
+       /** @vrr_range_max:
+        *
+        * Default state is 'vrr_range_max = 0', (and 'vrr_range_min = 0'),
+        * indicating legacy VRR_ENABLED behavior. if both are set to a non-zeo
+        * value, the new VRR range control behavior will be active.  See
+        * &DRM_CLIENT_CAP_VRR_RANGE_CONTROL.
+        *
+        * If setting a non-zero value, the driver should check that:
+        *
+        * 1. Both vrr_range_min and vrr_range_max are set to a non-zero value.
+        *    This indicates the driver to switch the new VRR range control
+        *    behavior.
+        * 2. Both vrr_rage_min and vrr_range_max are within the panel's 
supported
+        *    FPS range.
+        * 3. vrr_range_min is less-than-or-equal-to vrr_range_max.
+        */
+       uint16_t vrr_range_max;
+
        /**
         * @self_refresh_active:
         *
diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h
index 115763799625..4cb57a503a02 100644
--- a/include/drm/drm_file.h
+++ b/include/drm/drm_file.h
@@ -240,6 +240,16 @@ struct drm_file {
         */
        bool supports_virtualized_cursor_plane;
 
+       /**
+        * @vrr_range_control:
+        *
+        * If set to true, the DRM driver will allow setting of the
+        * &drm_mode_config.prop_vrr_range_control_min_property and
+        * &drm_mode_config.prop_vrr_range_control_max_property CRTC
+        * properties, if the properties are supported by the driver.
+        */
+       bool vrr_range_control;
+
        /**
         * @master:
         *
diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h
index 2e848b816218..e02dd46ca5c2 100644
--- a/include/drm/drm_mode_config.h
+++ b/include/drm/drm_mode_config.h
@@ -680,6 +680,24 @@ struct drm_mode_config {
         */
        struct drm_property *prop_vrr_enabled;
 
+       /**
+        * @prop_vrr_range_control_min_property: Optional CRTC properties to
+        * further limit the minimum allowed refresh rate within the panel's
+        * supported refresh rate range. It's invalid to set unless the
+        * client advertises &DRM_CLIENT_CAP_VRR_RANGE_CONTROL.
+        * See also &drm_ctrc_state.vrr_range_min.
+        */
+       struct drm_property *prop_vrr_range_control_min;
+
+       /**
+        * @prop_vrr_range_control_max_property: Optional CRTC properties to
+        * further limit the maximum allowed refresh rate within the panel's
+        * supported refresh rate range. It's invalid to set unless the
+        * client advertises &DRM_CLIENT_CAP_VRR_RANGE_CONTROL.
+        * See also &drm_ctrc_state.vrr_range_max.
+        */
+       struct drm_property *prop_vrr_range_control_max;
+
        /**
         * @dvi_i_subconnector_property: Optional DVI-I property to
         * differentiate between analog or digital mode.
diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
index 3cd5cf15e3c9..e4d918342e67 100644
--- a/include/uapi/drm/drm.h
+++ b/include/uapi/drm/drm.h
@@ -906,6 +906,23 @@ struct drm_get_cap {
  */
 #define DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT    6
 
+/**
+ * DRM_CLIENT_CAP_VRR_RANGE_CONTROL
+ *
+ * The driver shall not program a refresh rate that is:
+ * - Below the &drm_crtc_state.vrr_range_min, nor
+ * - Above the &drm_crtc_state.vrr_range_max
+ * Even if the panel supports a wider range than the range requested.
+ *
+ * Once set, the driver will allow setting of the
+ *
+ * - &drm_mode_config.prop_vrr_range_control_min and
+ * - &drm_mode_config.prop_vrr_range_control_max properties.
+ *
+ * Otherwise, setting them will be invalid.
+ */
+#define DRM_CLIENT_CAP_VRR_RANGE_CONTROL 7
+
 /* DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */
 struct drm_set_client_cap {
        __u64 capability;
-- 
2.43.0

Reply via email to