Home | History | Annotate | Line # | Download | only in drm
      1 /*	$NetBSD: drm_mode_object.c,v 1.3 2021/12/19 11:06:54 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2016 Intel Corporation
      5  *
      6  * Permission to use, copy, modify, distribute, and sell this software and its
      7  * documentation for any purpose is hereby granted without fee, provided that
      8  * the above copyright notice appear in all copies and that both that copyright
      9  * notice and this permission notice appear in supporting documentation, and
     10  * that the name of the copyright holders not be used in advertising or
     11  * publicity pertaining to distribution of the software without specific,
     12  * written prior permission.  The copyright holders make no representations
     13  * about the suitability of this software for any purpose.  It is provided "as
     14  * is" without express or implied warranty.
     15  *
     16  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
     17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
     18  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
     19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
     20  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
     21  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
     22  * OF THIS SOFTWARE.
     23  */
     24 
     25 #include <sys/cdefs.h>
     26 __KERNEL_RCSID(0, "$NetBSD: drm_mode_object.c,v 1.3 2021/12/19 11:06:54 riastradh Exp $");
     27 
     28 #include <linux/export.h>
     29 #include <linux/uaccess.h>
     30 
     31 #include <drm/drm_atomic.h>
     32 #include <drm/drm_drv.h>
     33 #include <drm/drm_device.h>
     34 #include <drm/drm_file.h>
     35 #include <drm/drm_mode_object.h>
     36 #include <drm/drm_print.h>
     37 
     38 #include "drm_crtc_internal.h"
     39 
     40 /*
     41  * Internal function to assign a slot in the object idr and optionally
     42  * register the object into the idr.
     43  */
     44 int __drm_mode_object_add(struct drm_device *dev, struct drm_mode_object *obj,
     45 			  uint32_t obj_type, bool register_obj,
     46 			  void (*obj_free_cb)(struct kref *kref))
     47 {
     48 	int ret;
     49 
     50 	WARN_ON(!dev->driver->load && dev->registered && !obj_free_cb);
     51 
     52 	idr_preload(GFP_KERNEL);
     53 	mutex_lock(&dev->mode_config.idr_mutex);
     54 	ret = idr_alloc(&dev->mode_config.object_idr, register_obj ? obj : NULL,
     55 			1, 0, GFP_KERNEL);
     56 	if (ret >= 0) {
     57 		/*
     58 		 * Set up the object linking under the protection of the idr
     59 		 * lock so that other users can't see inconsistent state.
     60 		 */
     61 		obj->id = ret;
     62 		obj->type = obj_type;
     63 		if (obj_free_cb) {
     64 			obj->free_cb = obj_free_cb;
     65 			kref_init(&obj->refcount);
     66 		}
     67 	}
     68 	mutex_unlock(&dev->mode_config.idr_mutex);
     69 	idr_preload_end();
     70 
     71 	return ret < 0 ? ret : 0;
     72 }
     73 
     74 /**
     75  * drm_mode_object_add - allocate a new modeset identifier
     76  * @dev: DRM device
     77  * @obj: object pointer, used to generate unique ID
     78  * @obj_type: object type
     79  *
     80  * Create a unique identifier based on @ptr in @dev's identifier space.  Used
     81  * for tracking modes, CRTCs and connectors.
     82  *
     83  * Returns:
     84  * Zero on success, error code on failure.
     85  */
     86 int drm_mode_object_add(struct drm_device *dev,
     87 			struct drm_mode_object *obj, uint32_t obj_type)
     88 {
     89 	return __drm_mode_object_add(dev, obj, obj_type, true, NULL);
     90 }
     91 
     92 void drm_mode_object_register(struct drm_device *dev,
     93 			      struct drm_mode_object *obj)
     94 {
     95 	mutex_lock(&dev->mode_config.idr_mutex);
     96 	idr_replace(&dev->mode_config.object_idr, obj, obj->id);
     97 	mutex_unlock(&dev->mode_config.idr_mutex);
     98 }
     99 
    100 /**
    101  * drm_mode_object_unregister - free a modeset identifer
    102  * @dev: DRM device
    103  * @object: object to free
    104  *
    105  * Free @id from @dev's unique identifier pool.
    106  * This function can be called multiple times, and guards against
    107  * multiple removals.
    108  * These modeset identifiers are _not_ reference counted. Hence don't use this
    109  * for reference counted modeset objects like framebuffers.
    110  */
    111 void drm_mode_object_unregister(struct drm_device *dev,
    112 				struct drm_mode_object *object)
    113 {
    114 	WARN_ON(!dev->driver->load && dev->registered && !object->free_cb);
    115 
    116 	mutex_lock(&dev->mode_config.idr_mutex);
    117 	if (object->id) {
    118 		idr_remove(&dev->mode_config.object_idr, object->id);
    119 		object->id = 0;
    120 	}
    121 	mutex_unlock(&dev->mode_config.idr_mutex);
    122 }
    123 
    124 /**
    125  * drm_lease_required - check types which must be leased to be used
    126  * @type: type of object
    127  *
    128  * Returns whether the provided type of drm_mode_object must
    129  * be owned or leased to be used by a process.
    130  */
    131 bool drm_mode_object_lease_required(uint32_t type)
    132 {
    133 	switch(type) {
    134 	case DRM_MODE_OBJECT_CRTC:
    135 	case DRM_MODE_OBJECT_CONNECTOR:
    136 	case DRM_MODE_OBJECT_PLANE:
    137 		return true;
    138 	default:
    139 		return false;
    140 	}
    141 }
    142 
    143 struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev,
    144 					       struct drm_file *file_priv,
    145 					       uint32_t id, uint32_t type)
    146 {
    147 	struct drm_mode_object *obj = NULL;
    148 
    149 	mutex_lock(&dev->mode_config.idr_mutex);
    150 	obj = idr_find(&dev->mode_config.object_idr, id);
    151 	if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type)
    152 		obj = NULL;
    153 	if (obj && obj->id != id)
    154 		obj = NULL;
    155 
    156 	if (obj && drm_mode_object_lease_required(obj->type) &&
    157 	    !_drm_lease_held(file_priv, obj->id))
    158 		obj = NULL;
    159 
    160 	if (obj && obj->free_cb) {
    161 		if (!kref_get_unless_zero(&obj->refcount))
    162 			obj = NULL;
    163 	}
    164 	mutex_unlock(&dev->mode_config.idr_mutex);
    165 
    166 	return obj;
    167 }
    168 
    169 /**
    170  * drm_mode_object_find - look up a drm object with static lifetime
    171  * @dev: drm device
    172  * @file_priv: drm file
    173  * @id: id of the mode object
    174  * @type: type of the mode object
    175  *
    176  * This function is used to look up a modeset object. It will acquire a
    177  * reference for reference counted objects. This reference must be dropped again
    178  * by callind drm_mode_object_put().
    179  */
    180 struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
    181 		struct drm_file *file_priv,
    182 		uint32_t id, uint32_t type)
    183 {
    184 	struct drm_mode_object *obj = NULL;
    185 
    186 	obj = __drm_mode_object_find(dev, file_priv, id, type);
    187 	return obj;
    188 }
    189 EXPORT_SYMBOL(drm_mode_object_find);
    190 
    191 /**
    192  * drm_mode_object_put - release a mode object reference
    193  * @obj: DRM mode object
    194  *
    195  * This function decrements the object's refcount if it is a refcounted modeset
    196  * object. It is a no-op on any other object. This is used to drop references
    197  * acquired with drm_mode_object_get().
    198  */
    199 void drm_mode_object_put(struct drm_mode_object *obj)
    200 {
    201 	if (obj->free_cb) {
    202 		DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, kref_read(&obj->refcount));
    203 		kref_put(&obj->refcount, obj->free_cb);
    204 	}
    205 }
    206 EXPORT_SYMBOL(drm_mode_object_put);
    207 
    208 /**
    209  * drm_mode_object_get - acquire a mode object reference
    210  * @obj: DRM mode object
    211  *
    212  * This function increments the object's refcount if it is a refcounted modeset
    213  * object. It is a no-op on any other object. References should be dropped again
    214  * by calling drm_mode_object_put().
    215  */
    216 void drm_mode_object_get(struct drm_mode_object *obj)
    217 {
    218 	if (obj->free_cb) {
    219 		DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, kref_read(&obj->refcount));
    220 		kref_get(&obj->refcount);
    221 	}
    222 }
    223 EXPORT_SYMBOL(drm_mode_object_get);
    224 
    225 /**
    226  * drm_object_attach_property - attach a property to a modeset object
    227  * @obj: drm modeset object
    228  * @property: property to attach
    229  * @init_val: initial value of the property
    230  *
    231  * This attaches the given property to the modeset object with the given initial
    232  * value. Currently this function cannot fail since the properties are stored in
    233  * a statically sized array.
    234  *
    235  * Note that all properties must be attached before the object itself is
    236  * registered and accessible from userspace.
    237  */
    238 void drm_object_attach_property(struct drm_mode_object *obj,
    239 				struct drm_property *property,
    240 				uint64_t init_val)
    241 {
    242 	int count = obj->properties->count;
    243 	struct drm_device *dev = property->dev;
    244 
    245 
    246 	if (obj->type == DRM_MODE_OBJECT_CONNECTOR) {
    247 		struct drm_connector *connector = obj_to_connector(obj);
    248 
    249 		WARN_ON(!dev->driver->load &&
    250 			connector->registration_state == DRM_CONNECTOR_REGISTERED);
    251 	} else {
    252 		WARN_ON(!dev->driver->load && dev->registered);
    253 	}
    254 
    255 	if (count == DRM_OBJECT_MAX_PROPERTY) {
    256 		WARN(1, "Failed to attach object property (type: 0x%x). Please "
    257 			"increase DRM_OBJECT_MAX_PROPERTY by 1 for each time "
    258 			"you see this message on the same object type.\n",
    259 			obj->type);
    260 		return;
    261 	}
    262 
    263 	obj->properties->properties[count] = property;
    264 	obj->properties->values[count] = init_val;
    265 	obj->properties->count++;
    266 }
    267 EXPORT_SYMBOL(drm_object_attach_property);
    268 
    269 /**
    270  * drm_object_property_set_value - set the value of a property
    271  * @obj: drm mode object to set property value for
    272  * @property: property to set
    273  * @val: value the property should be set to
    274  *
    275  * This function sets a given property on a given object. This function only
    276  * changes the software state of the property, it does not call into the
    277  * driver's ->set_property callback.
    278  *
    279  * Note that atomic drivers should not have any need to call this, the core will
    280  * ensure consistency of values reported back to userspace through the
    281  * appropriate ->atomic_get_property callback. Only legacy drivers should call
    282  * this function to update the tracked value (after clamping and other
    283  * restrictions have been applied).
    284  *
    285  * Returns:
    286  * Zero on success, error code on failure.
    287  */
    288 int drm_object_property_set_value(struct drm_mode_object *obj,
    289 				  struct drm_property *property, uint64_t val)
    290 {
    291 	int i;
    292 
    293 	WARN_ON(drm_drv_uses_atomic_modeset(property->dev) &&
    294 		!(property->flags & DRM_MODE_PROP_IMMUTABLE));
    295 
    296 	for (i = 0; i < obj->properties->count; i++) {
    297 		if (obj->properties->properties[i] == property) {
    298 			obj->properties->values[i] = val;
    299 			return 0;
    300 		}
    301 	}
    302 
    303 	return -EINVAL;
    304 }
    305 EXPORT_SYMBOL(drm_object_property_set_value);
    306 
    307 static int __drm_object_property_get_value(struct drm_mode_object *obj,
    308 					   struct drm_property *property,
    309 					   uint64_t *val)
    310 {
    311 	int i;
    312 
    313 	/* read-only properties bypass atomic mechanism and still store
    314 	 * their value in obj->properties->values[].. mostly to avoid
    315 	 * having to deal w/ EDID and similar props in atomic paths:
    316 	 */
    317 	if (drm_drv_uses_atomic_modeset(property->dev) &&
    318 			!(property->flags & DRM_MODE_PROP_IMMUTABLE))
    319 		return drm_atomic_get_property(obj, property, val);
    320 
    321 	for (i = 0; i < obj->properties->count; i++) {
    322 		if (obj->properties->properties[i] == property) {
    323 			*val = obj->properties->values[i];
    324 			return 0;
    325 		}
    326 
    327 	}
    328 
    329 	return -EINVAL;
    330 }
    331 
    332 /**
    333  * drm_object_property_get_value - retrieve the value of a property
    334  * @obj: drm mode object to get property value from
    335  * @property: property to retrieve
    336  * @val: storage for the property value
    337  *
    338  * This function retrieves the softare state of the given property for the given
    339  * property. Since there is no driver callback to retrieve the current property
    340  * value this might be out of sync with the hardware, depending upon the driver
    341  * and property.
    342  *
    343  * Atomic drivers should never call this function directly, the core will read
    344  * out property values through the various ->atomic_get_property callbacks.
    345  *
    346  * Returns:
    347  * Zero on success, error code on failure.
    348  */
    349 int drm_object_property_get_value(struct drm_mode_object *obj,
    350 				  struct drm_property *property, uint64_t *val)
    351 {
    352 	WARN_ON(drm_drv_uses_atomic_modeset(property->dev));
    353 
    354 	return __drm_object_property_get_value(obj, property, val);
    355 }
    356 EXPORT_SYMBOL(drm_object_property_get_value);
    357 
    358 /* helper for getconnector and getproperties ioctls */
    359 int drm_mode_object_get_properties(struct drm_mode_object *obj, bool atomic,
    360 				   uint32_t __user *prop_ptr,
    361 				   uint64_t __user *prop_values,
    362 				   uint32_t *arg_count_props)
    363 {
    364 	int i, ret, count;
    365 
    366 	for (i = 0, count = 0; i < obj->properties->count; i++) {
    367 		struct drm_property *prop = obj->properties->properties[i];
    368 		uint64_t val;
    369 
    370 		if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic)
    371 			continue;
    372 
    373 		if (*arg_count_props > count) {
    374 			ret = __drm_object_property_get_value(obj, prop, &val);
    375 			if (ret)
    376 				return ret;
    377 
    378 			if (put_user(prop->base.id, prop_ptr + count))
    379 				return -EFAULT;
    380 
    381 			if (put_user(val, prop_values + count))
    382 				return -EFAULT;
    383 		}
    384 
    385 		count++;
    386 	}
    387 	*arg_count_props = count;
    388 
    389 	return 0;
    390 }
    391 
    392 /**
    393  * drm_mode_obj_get_properties_ioctl - get the current value of a object's property
    394  * @dev: DRM device
    395  * @data: ioctl data
    396  * @file_priv: DRM file info
    397  *
    398  * This function retrieves the current value for an object's property. Compared
    399  * to the connector specific ioctl this one is extended to also work on crtc and
    400  * plane objects.
    401  *
    402  * Called by the user via ioctl.
    403  *
    404  * Returns:
    405  * Zero on success, negative errno on failure.
    406  */
    407 int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
    408 				      struct drm_file *file_priv)
    409 {
    410 	struct drm_mode_obj_get_properties *arg = data;
    411 	struct drm_mode_object *obj;
    412 	int ret = 0;
    413 
    414 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
    415 		return -EOPNOTSUPP;
    416 
    417 	drm_modeset_lock_all(dev);
    418 
    419 	obj = drm_mode_object_find(dev, file_priv, arg->obj_id, arg->obj_type);
    420 	if (!obj) {
    421 		ret = -ENOENT;
    422 		goto out;
    423 	}
    424 	if (!obj->properties) {
    425 		ret = -EINVAL;
    426 		goto out_unref;
    427 	}
    428 
    429 	ret = drm_mode_object_get_properties(obj, file_priv->atomic,
    430 			(uint32_t __user *)(unsigned long)(arg->props_ptr),
    431 			(uint64_t __user *)(unsigned long)(arg->prop_values_ptr),
    432 			&arg->count_props);
    433 
    434 out_unref:
    435 	drm_mode_object_put(obj);
    436 out:
    437 	drm_modeset_unlock_all(dev);
    438 	return ret;
    439 }
    440 
    441 struct drm_property *drm_mode_obj_find_prop_id(struct drm_mode_object *obj,
    442 					       uint32_t prop_id)
    443 {
    444 	int i;
    445 
    446 	for (i = 0; i < obj->properties->count; i++)
    447 		if (obj->properties->properties[i]->base.id == prop_id)
    448 			return obj->properties->properties[i];
    449 
    450 	return NULL;
    451 }
    452 
    453 static int set_property_legacy(struct drm_mode_object *obj,
    454 			       struct drm_property *prop,
    455 			       uint64_t prop_value)
    456 {
    457 	struct drm_device *dev = prop->dev;
    458 	struct drm_mode_object *ref;
    459 	int ret = -EINVAL;
    460 
    461 	if (!drm_property_change_valid_get(prop, prop_value, &ref))
    462 		return -EINVAL;
    463 
    464 	drm_modeset_lock_all(dev);
    465 	switch (obj->type) {
    466 	case DRM_MODE_OBJECT_CONNECTOR:
    467 		ret = drm_connector_set_obj_prop(obj, prop, prop_value);
    468 		break;
    469 	case DRM_MODE_OBJECT_CRTC:
    470 		ret = drm_mode_crtc_set_obj_prop(obj, prop, prop_value);
    471 		break;
    472 	case DRM_MODE_OBJECT_PLANE:
    473 		ret = drm_mode_plane_set_obj_prop(obj_to_plane(obj),
    474 						  prop, prop_value);
    475 		break;
    476 	}
    477 	drm_property_change_valid_put(prop, ref);
    478 	drm_modeset_unlock_all(dev);
    479 
    480 	return ret;
    481 }
    482 
    483 static int set_property_atomic(struct drm_mode_object *obj,
    484 			       struct drm_file *file_priv,
    485 			       struct drm_property *prop,
    486 			       uint64_t prop_value)
    487 {
    488 	struct drm_device *dev = prop->dev;
    489 	struct drm_atomic_state *state;
    490 	struct drm_modeset_acquire_ctx ctx;
    491 	int ret;
    492 
    493 	state = drm_atomic_state_alloc(dev);
    494 	if (!state)
    495 		return -ENOMEM;
    496 
    497 	drm_modeset_acquire_init(&ctx, 0);
    498 	state->acquire_ctx = &ctx;
    499 
    500 retry:
    501 	if (prop == state->dev->mode_config.dpms_property) {
    502 		if (obj->type != DRM_MODE_OBJECT_CONNECTOR) {
    503 			ret = -EINVAL;
    504 			goto out;
    505 		}
    506 
    507 		ret = drm_atomic_connector_commit_dpms(state,
    508 						       obj_to_connector(obj),
    509 						       prop_value);
    510 	} else {
    511 		ret = drm_atomic_set_property(state, file_priv, obj, prop, prop_value);
    512 		if (ret)
    513 			goto out;
    514 		ret = drm_atomic_commit(state);
    515 	}
    516 out:
    517 	if (ret == -EDEADLK) {
    518 		drm_atomic_state_clear(state);
    519 		drm_modeset_backoff(&ctx);
    520 		goto retry;
    521 	}
    522 
    523 	drm_atomic_state_put(state);
    524 
    525 	drm_modeset_drop_locks(&ctx);
    526 	drm_modeset_acquire_fini(&ctx);
    527 
    528 	return ret;
    529 }
    530 
    531 int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
    532 				    struct drm_file *file_priv)
    533 {
    534 	struct drm_mode_obj_set_property *arg = data;
    535 	struct drm_mode_object *arg_obj;
    536 	struct drm_property *property;
    537 	int ret = -EINVAL;
    538 
    539 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
    540 		return -EOPNOTSUPP;
    541 
    542 	arg_obj = drm_mode_object_find(dev, file_priv, arg->obj_id, arg->obj_type);
    543 	if (!arg_obj)
    544 		return -ENOENT;
    545 
    546 	if (!arg_obj->properties)
    547 		goto out_unref;
    548 
    549 	property = drm_mode_obj_find_prop_id(arg_obj, arg->prop_id);
    550 	if (!property)
    551 		goto out_unref;
    552 
    553 	if (drm_drv_uses_atomic_modeset(property->dev))
    554 		ret = set_property_atomic(arg_obj, file_priv, property, arg->value);
    555 	else
    556 		ret = set_property_legacy(arg_obj, property, arg->value);
    557 
    558 out_unref:
    559 	drm_mode_object_put(arg_obj);
    560 	return ret;
    561 }
    562