[RFC] [GPU][DRM][PROPERTY] -Added a new ioctl in Linux DRM KMS driver.

From: Satendra Singh Thakur
Date: Fri Jan 20 2017 - 03:13:42 EST


From: satendra singh thakur <satendra.t@xxxxxxxxxxx>

-Added a new ioctl in Linux DRM KMS driver.
This ioctl allows user to set the values of an objectâs multiple
properties in one go.
-In the absence of such ioctl, User would be calling one ioctl to set each
property value;
Thus, user needs to call N ioctls to set values of N properties of an
object, which is a time consuming and costly because each ioctl involves
a system call entering/exiting kernel (context switch).
-This ioctl will set N properties (provided that HW allows it)
in a single context switch
-This ioctl can be used to set multiple properties of ether a plane
or a crtc or a connector (i.e. single object )
-This ioctl can't be used to set one property of a plane and
one property of crtc in one go.

Signed-off-by: Satendra Singh Thakur <satendra.t@xxxxxxxxxxx>
---
drivers/gpu/drm/drm_crtc_internal.h | 9 +-
drivers/gpu/drm/drm_ioctl.c | 1 +
drivers/gpu/drm/drm_mode_object.c | 183 +++++++++++++++++++++++++++++++++++
include/uapi/drm/drm.h | 1 +
include/uapi/drm/drm_mode.h | 8 ++
5 files changed, 201 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h
index cdf6860..4bcae10 100644
--- a/drivers/gpu/drm/drm_crtc_internal.h
+++ b/drivers/gpu/drm/drm_crtc_internal.h
@@ -115,6 +115,12 @@ int drm_mode_object_get_properties(struct drm_mode_object *obj, bool atomic,
uint32_t __user *prop_ptr,
uint64_t __user *prop_values,
uint32_t *arg_count_props);
+
+int drm_mode_object_set_properties(struct drm_device *dev,
+ struct drm_mode_object *obj,
+ bool atomic, uint32_t __user *prop_ptr,
+ uint64_t __user *prop_values, uint32_t *arg_count_props);
+
struct drm_property *drm_mode_obj_find_prop_id(struct drm_mode_object *obj,
uint32_t prop_id);

@@ -124,7 +130,8 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
-
+int drm_mode_obj_set_properties_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
/* drm_encoder.c */
int drm_encoder_register_all(struct drm_device *dev);
void drm_encoder_unregister_all(struct drm_device *dev);
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index fed22c2..bbad577 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -631,6 +631,7 @@ int drm_ioctl_permit(u32 flags, struct drm_file *file_priv)
DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTIES, drm_mode_obj_set_properties_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
diff --git a/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c
index 9f17085..685500d 100644
--- a/drivers/gpu/drm/drm_mode_object.c
+++ b/drivers/gpu/drm/drm_mode_object.c
@@ -436,3 +436,186 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
drm_modeset_unlock_all(dev);
return ret;
}
+
+/**
+ * drm_mode_object_set_properties
+ * helper for drm_mode_obj_set_properties_ioctl which can be used to set
+ * values of an object's numerous properties
+ * @dev: DRM device
+ * @obj: Object pointer
+ * @atomic: Flag that indicates atomocity
+ * @prop_ptr: pointer to array of prop IDs
+ * @prop_values: pointer to array of prop values
+ * @arg_count_props: pointer to total num of properties
+ *
+ * This function helps to set the values for an object's several properties
+ * in one go.
+ * Called by drm_mode_obj_set_properties_ioctl
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ * Fills prop_values with -1 for failed prop IDs.
+ * Fills arg_count_props with total successful props
+ */
+int drm_mode_object_set_properties(struct drm_device *dev,
+ struct drm_mode_object *obj, bool atomic,
+ uint32_t __user *prop_ptr,
+ uint64_t __user *prop_values, uint32_t *arg_count_props)
+{
+ struct drm_mode_object *ref = NULL;
+ int props_count;
+ int i, j, ret = 0, set = 0;
+ /* user requested property id, value, count*/
+ uint32_t prop_id_req;
+ uint64_t prop_val_req;
+ uint32_t props_count_req = *arg_count_props;
+
+ props_count = obj->properties->count;
+ if (!props_count) {
+ DRM_DEBUG_KMS("0 props_count\n");
+ return -EINVAL;
+ }
+ /*
+ * if user sends arg_count_props = 0
+ * user will get *arg_count_props = props_count
+ */
+ if (!props_count_req) {
+ *arg_count_props = props_count;
+ return 0;
+ }
+ /* if user wish to set number of props <= props_count, its allowed*/
+ if (props_count_req > props_count) {
+ DRM_DEBUG_KMS("Invalid props_count\
+ %u\n", props_count_req);
+ return -EINVAL;
+ }
+ for (j = 0; j < props_count_req; j++) {
+ if (get_user(prop_id_req, prop_ptr + j)) {
+ DRM_DEBUG_KMS("get_user failed\
+ for prop_id %u\n", prop_id_req);
+ return -EFAULT;
+ }
+ if (get_user(prop_val_req, prop_values + j)) {
+ DRM_DEBUG_KMS("get_user failed\
+ for prop_val %llu\n", prop_val_req);
+ return -EFAULT;
+ }
+ for (i = 0; i < props_count; i++) {
+ struct drm_property *prop =
+ obj->properties->properties[i];
+ /*in case prop inside the array
+ *obj->properties->properties[]
+ *are less than props_count
+ */
+ if (!prop)
+ continue;
+ if (prop->base.id != prop_id_req)
+ continue;
+ if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic)
+ continue;
+ if (!drm_property_change_valid_get(prop,
+ prop_val_req, &ref)) {
+ DRM_DEBUG_KMS("change_valid_get failed\
+ for prop id %u, prop val %llu\n",\
+ prop_id_req, prop_val_req);
+ /*copy -1 to prop_values array
+ * to let the user know that this prop id
+ * failed
+ */
+ if (put_user((uint64_t) -1, prop_values + j)) {
+ DRM_DEBUG_KMS("put_user failed\
+ for prop_val %d\n", -1);
+ return -EFAULT;
+ }
+ continue;
+ }
+ drm_modeset_lock_all(dev);
+ switch (obj->type) {
+ case DRM_MODE_OBJECT_CONNECTOR:
+ ret = drm_mode_connector_set_obj_prop(obj,
+ prop, prop_val_req);
+ break;
+ case DRM_MODE_OBJECT_CRTC:
+ ret = drm_mode_crtc_set_obj_prop(obj,
+ prop, prop_val_req);
+ break;
+ case DRM_MODE_OBJECT_PLANE:
+ ret = drm_mode_plane_set_obj_prop(
+ obj_to_plane(obj), prop, prop_val_req);
+ break;
+ default:
+ DRM_DEBUG_KMS("Invalid object type %u\n",\
+ obj->type);
+ drm_modeset_unlock_all(dev);
+ drm_property_change_valid_put(prop, ref);
+ return -EINVAL;
+ }
+ drm_modeset_unlock_all(dev);
+ drm_property_change_valid_put(prop, ref);
+ if (ret) {
+ /*Even if one prop failed to set
+ *continue setting others , so that
+ *max props can be set and this ioctl/feature
+ *is utilized to max extent
+ */
+ DRM_DEBUG_KMS("drm_mode_<connector|crtc|plane>\
+ _set_obj_prop failed for obj id %u and obj type\
+ %u, prop id %u, prop val %llu\n", obj->id,\
+ obj->type, prop_id_req, prop_val_req);
+ /*The properties which failed
+ *user gets -1 in prop_values
+ *for those prop IDs,
+ *if ret is passed, it may coincide with
+ *some prop val, therefore -1 is passed
+ */
+ if (put_user((uint64_t) -1, prop_values + j)) {
+ DRM_DEBUG_KMS("put_user failed\
+ for prop_val %d\n", -1);
+ return -EFAULT;
+ }
+ } else {
+ set++;
+ }
+ }
+ }
+ DRM_DEBUG_KMS("Total %u props set successfully\n", set);
+ *arg_count_props = set;
+ return ret;
+}
+
+/**
+ * drm_mode_obj_set_properties_ioctl - sets values of an object's multiple
+ * properties
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ * This function sets the values for an object's several properties in one go.
+ * It internally utilizes the helper function set_properties
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_obj_set_properties_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_mode_obj_set_properties *arg = data;
+ struct drm_mode_object *obj;
+ int ret = 0;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return -EINVAL;
+
+
+ obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
+ if (!obj)
+ return -ENOENT;
+ if (!obj->properties)
+ return -EINVAL;
+ ret = drm_mode_object_set_properties(dev, obj, file_priv->atomic,
+ (uint32_t __user *)(unsigned long)(arg->props_ptr),
+ (uint64_t __user *)(unsigned long)(arg->prop_values_ptr),
+ &arg->count_props);
+
+ return ret;
+}
diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
index b2c5284..a43838d 100644
--- a/include/uapi/drm/drm.h
+++ b/include/uapi/drm/drm.h
@@ -813,6 +813,7 @@ struct drm_prime_handle {
#define DRM_IOCTL_MODE_ATOMIC DRM_IOWR(0xBC, struct drm_mode_atomic)
#define DRM_IOCTL_MODE_CREATEPROPBLOB DRM_IOWR(0xBD, struct drm_mode_create_blob)
#define DRM_IOCTL_MODE_DESTROYPROPBLOB DRM_IOWR(0xBE, struct drm_mode_destroy_blob)
+#define DRM_IOCTL_MODE_OBJ_SETPROPERTIES DRM_IOWR(0xBF, struct drm_mode_obj_set_properties)

/**
* Device specific ioctls should only be in their respective headers
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index ce7efe2..05df931 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -362,6 +362,14 @@ struct drm_mode_obj_get_properties {
__u32 obj_type;
};

+struct drm_mode_obj_set_properties {
+ __u64 props_ptr;
+ __u64 prop_values_ptr;
+ __u32 count_props;
+ __u32 obj_id;
+ __u32 obj_type;
+};
+
struct drm_mode_obj_set_property {
__u64 value;
__u32 prop_id;
--
1.7.9.5