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