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  */
__drm_mode_object_add(struct drm_device * dev,struct drm_mode_object * obj,uint32_t obj_type,bool register_obj,void (* obj_free_cb)(struct kref * kref))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  */
drm_mode_object_add(struct drm_device * dev,struct drm_mode_object * obj,uint32_t obj_type)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 
drm_mode_object_register(struct drm_device * dev,struct drm_mode_object * obj)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  */
drm_mode_object_unregister(struct drm_device * dev,struct drm_mode_object * object)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  */
drm_mode_object_lease_required(uint32_t type)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 
__drm_mode_object_find(struct drm_device * dev,struct drm_file * file_priv,uint32_t id,uint32_t type)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  */
drm_mode_object_find(struct drm_device * dev,struct drm_file * file_priv,uint32_t id,uint32_t type)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  */
drm_mode_object_put(struct drm_mode_object * obj)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  */
drm_mode_object_get(struct drm_mode_object * obj)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  */
drm_object_attach_property(struct drm_mode_object * obj,struct drm_property * property,uint64_t init_val)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  */
drm_object_property_set_value(struct drm_mode_object * obj,struct drm_property * property,uint64_t val)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 
__drm_object_property_get_value(struct drm_mode_object * obj,struct drm_property * property,uint64_t * val)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  */
drm_object_property_get_value(struct drm_mode_object * obj,struct drm_property * property,uint64_t * val)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 */
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)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  */
drm_mode_obj_get_properties_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)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 
drm_mode_obj_find_prop_id(struct drm_mode_object * obj,uint32_t prop_id)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 
set_property_legacy(struct drm_mode_object * obj,struct drm_property * prop,uint64_t prop_value)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 
set_property_atomic(struct drm_mode_object * obj,struct drm_file * file_priv,struct drm_property * prop,uint64_t prop_value)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 
drm_mode_obj_set_property_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)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