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