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