Commit | Line | Data |
---|---|---|
59e71ee7 DV |
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> | |
0500c04e SR |
24 | #include <linux/uaccess.h> |
25 | ||
26 | #include <drm/drm_crtc.h> | |
27 | #include <drm/drm_drv.h> | |
28 | #include <drm/drm_file.h> | |
29 | #include <drm/drm_framebuffer.h> | |
59e71ee7 DV |
30 | #include <drm/drm_property.h> |
31 | ||
32 | #include "drm_crtc_internal.h" | |
33 | ||
c8458c7e DV |
34 | /** |
35 | * DOC: overview | |
36 | * | |
37 | * Properties as represented by &drm_property are used to extend the modeset | |
38 | * interface exposed to userspace. For the atomic modeset IOCTL properties are | |
39 | * even the only way to transport metadata about the desired new modeset | |
40 | * configuration from userspace to the kernel. Properties have a well-defined | |
41 | * value range, which is enforced by the drm core. See the documentation of the | |
ea0dd85a | 42 | * flags member of &struct drm_property for an overview of the different |
c8458c7e DV |
43 | * property types and ranges. |
44 | * | |
45 | * Properties don't store the current value directly, but need to be | |
4cf1d871 | 46 | * instantiated by attaching them to a &drm_mode_object with |
c8458c7e DV |
47 | * drm_object_attach_property(). |
48 | * | |
49 | * Property values are only 64bit. To support bigger piles of data (like gamma | |
d574528a DV |
50 | * tables, color correction matrices or large structures) a property can instead |
51 | * point at a &drm_property_blob with that additional data. | |
c8458c7e DV |
52 | * |
53 | * Properties are defined by their symbolic name, userspace must keep a | |
54 | * per-object mapping from those names to the property ID used in the atomic | |
55 | * IOCTL and in the get/set property IOCTL. | |
56 | */ | |
57 | ||
100bc0d9 | 58 | static bool drm_property_flags_valid(u32 flags) |
59e71ee7 | 59 | { |
100bc0d9 VS |
60 | u32 legacy_type = flags & DRM_MODE_PROP_LEGACY_TYPE; |
61 | u32 ext_type = flags & DRM_MODE_PROP_EXTENDED_TYPE; | |
62 | ||
63 | /* Reject undefined/deprecated flags */ | |
64 | if (flags & ~(DRM_MODE_PROP_LEGACY_TYPE | | |
65 | DRM_MODE_PROP_EXTENDED_TYPE | | |
66 | DRM_MODE_PROP_IMMUTABLE | | |
67 | DRM_MODE_PROP_ATOMIC)) | |
68 | return false; | |
69 | ||
70 | /* We want either a legacy type or an extended type, but not both */ | |
71 | if (!legacy_type == !ext_type) | |
72 | return false; | |
73 | ||
74 | /* Only one legacy type at a time please */ | |
75 | if (legacy_type && !is_power_of_2(legacy_type)) | |
76 | return false; | |
77 | ||
78 | return true; | |
59e71ee7 DV |
79 | } |
80 | ||
81 | /** | |
82 | * drm_property_create - create a new property type | |
83 | * @dev: drm device | |
84 | * @flags: flags specifying the property type | |
85 | * @name: name of the property | |
86 | * @num_values: number of pre-defined values | |
87 | * | |
88 | * This creates a new generic drm property which can then be attached to a drm | |
6a8a66ed DV |
89 | * object with drm_object_attach_property(). The returned property object must |
90 | * be freed with drm_property_destroy(), which is done automatically when | |
91 | * calling drm_mode_config_cleanup(). | |
59e71ee7 DV |
92 | * |
93 | * Returns: | |
94 | * A pointer to the newly created property on success, NULL on failure. | |
95 | */ | |
51abc976 VS |
96 | struct drm_property *drm_property_create(struct drm_device *dev, |
97 | u32 flags, const char *name, | |
98 | int num_values) | |
59e71ee7 DV |
99 | { |
100 | struct drm_property *property = NULL; | |
101 | int ret; | |
102 | ||
100bc0d9 VS |
103 | if (WARN_ON(!drm_property_flags_valid(flags))) |
104 | return NULL; | |
105 | ||
5ebbb5b4 VS |
106 | if (WARN_ON(strlen(name) >= DRM_PROP_NAME_LEN)) |
107 | return NULL; | |
108 | ||
59e71ee7 DV |
109 | property = kzalloc(sizeof(struct drm_property), GFP_KERNEL); |
110 | if (!property) | |
111 | return NULL; | |
112 | ||
113 | property->dev = dev; | |
114 | ||
115 | if (num_values) { | |
116 | property->values = kcalloc(num_values, sizeof(uint64_t), | |
117 | GFP_KERNEL); | |
118 | if (!property->values) | |
119 | goto fail; | |
120 | } | |
121 | ||
2135ea7a | 122 | ret = drm_mode_object_add(dev, &property->base, DRM_MODE_OBJECT_PROPERTY); |
59e71ee7 DV |
123 | if (ret) |
124 | goto fail; | |
125 | ||
126 | property->flags = flags; | |
127 | property->num_values = num_values; | |
128 | INIT_LIST_HEAD(&property->enum_list); | |
129 | ||
753f2674 | 130 | strscpy_pad(property->name, name, DRM_PROP_NAME_LEN); |
59e71ee7 DV |
131 | |
132 | list_add_tail(&property->head, &dev->mode_config.property_list); | |
133 | ||
59e71ee7 DV |
134 | return property; |
135 | fail: | |
136 | kfree(property->values); | |
137 | kfree(property); | |
138 | return NULL; | |
139 | } | |
140 | EXPORT_SYMBOL(drm_property_create); | |
141 | ||
142 | /** | |
143 | * drm_property_create_enum - create a new enumeration property type | |
144 | * @dev: drm device | |
145 | * @flags: flags specifying the property type | |
146 | * @name: name of the property | |
147 | * @props: enumeration lists with property values | |
148 | * @num_values: number of pre-defined values | |
149 | * | |
150 | * This creates a new generic drm property which can then be attached to a drm | |
6a8a66ed DV |
151 | * object with drm_object_attach_property(). The returned property object must |
152 | * be freed with drm_property_destroy(), which is done automatically when | |
153 | * calling drm_mode_config_cleanup(). | |
59e71ee7 DV |
154 | * |
155 | * Userspace is only allowed to set one of the predefined values for enumeration | |
156 | * properties. | |
157 | * | |
158 | * Returns: | |
159 | * A pointer to the newly created property on success, NULL on failure. | |
160 | */ | |
51abc976 VS |
161 | struct drm_property *drm_property_create_enum(struct drm_device *dev, |
162 | u32 flags, const char *name, | |
163 | const struct drm_prop_enum_list *props, | |
164 | int num_values) | |
59e71ee7 DV |
165 | { |
166 | struct drm_property *property; | |
167 | int i, ret; | |
168 | ||
169 | flags |= DRM_MODE_PROP_ENUM; | |
170 | ||
171 | property = drm_property_create(dev, flags, name, num_values); | |
172 | if (!property) | |
173 | return NULL; | |
174 | ||
175 | for (i = 0; i < num_values; i++) { | |
30e9db6d VS |
176 | ret = drm_property_add_enum(property, |
177 | props[i].type, | |
178 | props[i].name); | |
59e71ee7 DV |
179 | if (ret) { |
180 | drm_property_destroy(dev, property); | |
181 | return NULL; | |
182 | } | |
183 | } | |
184 | ||
185 | return property; | |
186 | } | |
187 | EXPORT_SYMBOL(drm_property_create_enum); | |
188 | ||
189 | /** | |
190 | * drm_property_create_bitmask - create a new bitmask property type | |
191 | * @dev: drm device | |
192 | * @flags: flags specifying the property type | |
193 | * @name: name of the property | |
194 | * @props: enumeration lists with property bitflags | |
195 | * @num_props: size of the @props array | |
196 | * @supported_bits: bitmask of all supported enumeration values | |
197 | * | |
198 | * This creates a new bitmask drm property which can then be attached to a drm | |
6a8a66ed DV |
199 | * object with drm_object_attach_property(). The returned property object must |
200 | * be freed with drm_property_destroy(), which is done automatically when | |
201 | * calling drm_mode_config_cleanup(). | |
59e71ee7 DV |
202 | * |
203 | * Compared to plain enumeration properties userspace is allowed to set any | |
204 | * or'ed together combination of the predefined property bitflag values | |
205 | * | |
206 | * Returns: | |
207 | * A pointer to the newly created property on success, NULL on failure. | |
208 | */ | |
209 | struct drm_property *drm_property_create_bitmask(struct drm_device *dev, | |
51abc976 VS |
210 | u32 flags, const char *name, |
211 | const struct drm_prop_enum_list *props, | |
212 | int num_props, | |
213 | uint64_t supported_bits) | |
59e71ee7 DV |
214 | { |
215 | struct drm_property *property; | |
30e9db6d | 216 | int i, ret; |
59e71ee7 DV |
217 | int num_values = hweight64(supported_bits); |
218 | ||
219 | flags |= DRM_MODE_PROP_BITMASK; | |
220 | ||
221 | property = drm_property_create(dev, flags, name, num_values); | |
222 | if (!property) | |
223 | return NULL; | |
224 | for (i = 0; i < num_props; i++) { | |
225 | if (!(supported_bits & (1ULL << props[i].type))) | |
226 | continue; | |
227 | ||
30e9db6d VS |
228 | ret = drm_property_add_enum(property, |
229 | props[i].type, | |
230 | props[i].name); | |
59e71ee7 DV |
231 | if (ret) { |
232 | drm_property_destroy(dev, property); | |
233 | return NULL; | |
234 | } | |
235 | } | |
236 | ||
237 | return property; | |
238 | } | |
239 | EXPORT_SYMBOL(drm_property_create_bitmask); | |
240 | ||
241 | static struct drm_property *property_create_range(struct drm_device *dev, | |
51abc976 VS |
242 | u32 flags, const char *name, |
243 | uint64_t min, uint64_t max) | |
59e71ee7 DV |
244 | { |
245 | struct drm_property *property; | |
246 | ||
247 | property = drm_property_create(dev, flags, name, 2); | |
248 | if (!property) | |
249 | return NULL; | |
250 | ||
251 | property->values[0] = min; | |
252 | property->values[1] = max; | |
253 | ||
254 | return property; | |
255 | } | |
256 | ||
257 | /** | |
258 | * drm_property_create_range - create a new unsigned ranged property type | |
259 | * @dev: drm device | |
260 | * @flags: flags specifying the property type | |
261 | * @name: name of the property | |
262 | * @min: minimum value of the property | |
263 | * @max: maximum value of the property | |
264 | * | |
265 | * This creates a new generic drm property which can then be attached to a drm | |
6a8a66ed DV |
266 | * object with drm_object_attach_property(). The returned property object must |
267 | * be freed with drm_property_destroy(), which is done automatically when | |
268 | * calling drm_mode_config_cleanup(). | |
59e71ee7 DV |
269 | * |
270 | * Userspace is allowed to set any unsigned integer value in the (min, max) | |
271 | * range inclusive. | |
272 | * | |
273 | * Returns: | |
274 | * A pointer to the newly created property on success, NULL on failure. | |
275 | */ | |
51abc976 VS |
276 | struct drm_property *drm_property_create_range(struct drm_device *dev, |
277 | u32 flags, const char *name, | |
278 | uint64_t min, uint64_t max) | |
59e71ee7 DV |
279 | { |
280 | return property_create_range(dev, DRM_MODE_PROP_RANGE | flags, | |
281 | name, min, max); | |
282 | } | |
283 | EXPORT_SYMBOL(drm_property_create_range); | |
284 | ||
285 | /** | |
286 | * drm_property_create_signed_range - create a new signed ranged property type | |
287 | * @dev: drm device | |
288 | * @flags: flags specifying the property type | |
289 | * @name: name of the property | |
290 | * @min: minimum value of the property | |
291 | * @max: maximum value of the property | |
292 | * | |
293 | * This creates a new generic drm property which can then be attached to a drm | |
6a8a66ed DV |
294 | * object with drm_object_attach_property(). The returned property object must |
295 | * be freed with drm_property_destroy(), which is done automatically when | |
296 | * calling drm_mode_config_cleanup(). | |
59e71ee7 DV |
297 | * |
298 | * Userspace is allowed to set any signed integer value in the (min, max) | |
299 | * range inclusive. | |
300 | * | |
301 | * Returns: | |
302 | * A pointer to the newly created property on success, NULL on failure. | |
303 | */ | |
304 | struct drm_property *drm_property_create_signed_range(struct drm_device *dev, | |
51abc976 VS |
305 | u32 flags, const char *name, |
306 | int64_t min, int64_t max) | |
59e71ee7 DV |
307 | { |
308 | return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags, | |
309 | name, I642U64(min), I642U64(max)); | |
310 | } | |
311 | EXPORT_SYMBOL(drm_property_create_signed_range); | |
312 | ||
313 | /** | |
314 | * drm_property_create_object - create a new object property type | |
315 | * @dev: drm device | |
316 | * @flags: flags specifying the property type | |
317 | * @name: name of the property | |
318 | * @type: object type from DRM_MODE_OBJECT_* defines | |
319 | * | |
320 | * This creates a new generic drm property which can then be attached to a drm | |
6a8a66ed DV |
321 | * object with drm_object_attach_property(). The returned property object must |
322 | * be freed with drm_property_destroy(), which is done automatically when | |
323 | * calling drm_mode_config_cleanup(). | |
59e71ee7 DV |
324 | * |
325 | * Userspace is only allowed to set this to any property value of the given | |
326 | * @type. Only useful for atomic properties, which is enforced. | |
327 | * | |
328 | * Returns: | |
329 | * A pointer to the newly created property on success, NULL on failure. | |
330 | */ | |
331 | struct drm_property *drm_property_create_object(struct drm_device *dev, | |
51abc976 | 332 | u32 flags, const char *name, |
c8458c7e | 333 | uint32_t type) |
59e71ee7 DV |
334 | { |
335 | struct drm_property *property; | |
336 | ||
337 | flags |= DRM_MODE_PROP_OBJECT; | |
338 | ||
339 | if (WARN_ON(!(flags & DRM_MODE_PROP_ATOMIC))) | |
340 | return NULL; | |
341 | ||
342 | property = drm_property_create(dev, flags, name, 1); | |
343 | if (!property) | |
344 | return NULL; | |
345 | ||
346 | property->values[0] = type; | |
347 | ||
348 | return property; | |
349 | } | |
350 | EXPORT_SYMBOL(drm_property_create_object); | |
351 | ||
352 | /** | |
353 | * drm_property_create_bool - create a new boolean property type | |
354 | * @dev: drm device | |
355 | * @flags: flags specifying the property type | |
356 | * @name: name of the property | |
357 | * | |
358 | * This creates a new generic drm property which can then be attached to a drm | |
6a8a66ed DV |
359 | * object with drm_object_attach_property(). The returned property object must |
360 | * be freed with drm_property_destroy(), which is done automatically when | |
361 | * calling drm_mode_config_cleanup(). | |
59e71ee7 DV |
362 | * |
363 | * This is implemented as a ranged property with only {0, 1} as valid values. | |
364 | * | |
365 | * Returns: | |
366 | * A pointer to the newly created property on success, NULL on failure. | |
367 | */ | |
51abc976 VS |
368 | struct drm_property *drm_property_create_bool(struct drm_device *dev, |
369 | u32 flags, const char *name) | |
59e71ee7 DV |
370 | { |
371 | return drm_property_create_range(dev, flags, name, 0, 1); | |
372 | } | |
373 | EXPORT_SYMBOL(drm_property_create_bool); | |
374 | ||
375 | /** | |
376 | * drm_property_add_enum - add a possible value to an enumeration property | |
377 | * @property: enumeration property to change | |
59e71ee7 DV |
378 | * @value: value of the new enumeration |
379 | * @name: symbolic name of the new enumeration | |
380 | * | |
381 | * This functions adds enumerations to a property. | |
382 | * | |
383 | * It's use is deprecated, drivers should use one of the more specific helpers | |
384 | * to directly create the property with all enumerations already attached. | |
385 | * | |
386 | * Returns: | |
387 | * Zero on success, error code on failure. | |
388 | */ | |
30e9db6d | 389 | int drm_property_add_enum(struct drm_property *property, |
59e71ee7 DV |
390 | uint64_t value, const char *name) |
391 | { | |
392 | struct drm_property_enum *prop_enum; | |
30e9db6d | 393 | int index = 0; |
59e71ee7 | 394 | |
5ebbb5b4 VS |
395 | if (WARN_ON(strlen(name) >= DRM_PROP_NAME_LEN)) |
396 | return -EINVAL; | |
397 | ||
1371f260 VS |
398 | if (WARN_ON(!drm_property_type_is(property, DRM_MODE_PROP_ENUM) && |
399 | !drm_property_type_is(property, DRM_MODE_PROP_BITMASK))) | |
59e71ee7 DV |
400 | return -EINVAL; |
401 | ||
402 | /* | |
403 | * Bitmask enum properties have the additional constraint of values | |
404 | * from 0 to 63 | |
405 | */ | |
8c6c2fe2 VS |
406 | if (WARN_ON(drm_property_type_is(property, DRM_MODE_PROP_BITMASK) && |
407 | value > 63)) | |
59e71ee7 DV |
408 | return -EINVAL; |
409 | ||
6f881d04 VS |
410 | list_for_each_entry(prop_enum, &property->enum_list, head) { |
411 | if (WARN_ON(prop_enum->value == value)) | |
412 | return -EINVAL; | |
30e9db6d | 413 | index++; |
59e71ee7 DV |
414 | } |
415 | ||
30e9db6d VS |
416 | if (WARN_ON(index >= property->num_values)) |
417 | return -EINVAL; | |
418 | ||
59e71ee7 DV |
419 | prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL); |
420 | if (!prop_enum) | |
421 | return -ENOMEM; | |
422 | ||
753f2674 | 423 | strscpy_pad(prop_enum->name, name, DRM_PROP_NAME_LEN); |
59e71ee7 DV |
424 | prop_enum->value = value; |
425 | ||
426 | property->values[index] = value; | |
427 | list_add_tail(&prop_enum->head, &property->enum_list); | |
428 | return 0; | |
429 | } | |
430 | EXPORT_SYMBOL(drm_property_add_enum); | |
431 | ||
432 | /** | |
433 | * drm_property_destroy - destroy a drm property | |
434 | * @dev: drm device | |
0ae865ef | 435 | * @property: property to destroy |
59e71ee7 DV |
436 | * |
437 | * This function frees a property including any attached resources like | |
438 | * enumeration values. | |
439 | */ | |
440 | void drm_property_destroy(struct drm_device *dev, struct drm_property *property) | |
441 | { | |
442 | struct drm_property_enum *prop_enum, *pt; | |
443 | ||
444 | list_for_each_entry_safe(prop_enum, pt, &property->enum_list, head) { | |
445 | list_del(&prop_enum->head); | |
446 | kfree(prop_enum); | |
447 | } | |
448 | ||
449 | if (property->num_values) | |
450 | kfree(property->values); | |
451 | drm_mode_object_unregister(dev, &property->base); | |
452 | list_del(&property->head); | |
453 | kfree(property); | |
454 | } | |
455 | EXPORT_SYMBOL(drm_property_destroy); | |
456 | ||
59e71ee7 DV |
457 | int drm_mode_getproperty_ioctl(struct drm_device *dev, |
458 | void *data, struct drm_file *file_priv) | |
459 | { | |
460 | struct drm_mode_get_property *out_resp = data; | |
461 | struct drm_property *property; | |
462 | int enum_count = 0; | |
463 | int value_count = 0; | |
eb8eb02e | 464 | int i, copied; |
59e71ee7 DV |
465 | struct drm_property_enum *prop_enum; |
466 | struct drm_mode_property_enum __user *enum_ptr; | |
467 | uint64_t __user *values_ptr; | |
468 | ||
469 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) | |
69fdf420 | 470 | return -EOPNOTSUPP; |
59e71ee7 | 471 | |
418da172 | 472 | property = drm_property_find(dev, file_priv, out_resp->prop_id); |
eb8eb02e DV |
473 | if (!property) |
474 | return -ENOENT; | |
59e71ee7 | 475 | |
753f2674 | 476 | strscpy_pad(out_resp->name, property->name, DRM_PROP_NAME_LEN); |
59e71ee7 DV |
477 | out_resp->flags = property->flags; |
478 | ||
eb8eb02e DV |
479 | value_count = property->num_values; |
480 | values_ptr = u64_to_user_ptr(out_resp->values_ptr); | |
481 | ||
482 | for (i = 0; i < value_count; i++) { | |
483 | if (i < out_resp->count_values && | |
484 | put_user(property->values[i], values_ptr + i)) { | |
485 | return -EFAULT; | |
59e71ee7 DV |
486 | } |
487 | } | |
488 | out_resp->count_values = value_count; | |
489 | ||
eb8eb02e DV |
490 | copied = 0; |
491 | enum_ptr = u64_to_user_ptr(out_resp->enum_blob_ptr); | |
492 | ||
59e71ee7 | 493 | if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || |
eb8eb02e DV |
494 | drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { |
495 | list_for_each_entry(prop_enum, &property->enum_list, head) { | |
496 | enum_count++; | |
8cb68c83 | 497 | if (out_resp->count_enum_blobs < enum_count) |
eb8eb02e DV |
498 | continue; |
499 | ||
500 | if (copy_to_user(&enum_ptr[copied].value, | |
501 | &prop_enum->value, sizeof(uint64_t))) | |
502 | return -EFAULT; | |
503 | ||
504 | if (copy_to_user(&enum_ptr[copied].name, | |
505 | &prop_enum->name, DRM_PROP_NAME_LEN)) | |
506 | return -EFAULT; | |
507 | copied++; | |
59e71ee7 DV |
508 | } |
509 | out_resp->count_enum_blobs = enum_count; | |
510 | } | |
511 | ||
512 | /* | |
513 | * NOTE: The idea seems to have been to use this to read all the blob | |
514 | * property values. But nothing ever added them to the corresponding | |
515 | * list, userspace always used the special-purpose get_blob ioctl to | |
516 | * read the value for a blob property. It also doesn't make a lot of | |
517 | * sense to return values here when everything else is just metadata for | |
518 | * the property itself. | |
519 | */ | |
520 | if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) | |
521 | out_resp->count_enum_blobs = 0; | |
eb8eb02e DV |
522 | |
523 | return 0; | |
59e71ee7 DV |
524 | } |
525 | ||
526 | static void drm_property_free_blob(struct kref *kref) | |
527 | { | |
528 | struct drm_property_blob *blob = | |
529 | container_of(kref, struct drm_property_blob, base.refcount); | |
530 | ||
531 | mutex_lock(&blob->dev->mode_config.blob_lock); | |
532 | list_del(&blob->head_global); | |
533 | mutex_unlock(&blob->dev->mode_config.blob_lock); | |
534 | ||
535 | drm_mode_object_unregister(blob->dev, &blob->base); | |
536 | ||
718b5406 | 537 | kvfree(blob); |
59e71ee7 DV |
538 | } |
539 | ||
540 | /** | |
541 | * drm_property_create_blob - Create new blob property | |
59e71ee7 DV |
542 | * @dev: DRM device to create property for |
543 | * @length: Length to allocate for blob data | |
544 | * @data: If specified, copies data into blob | |
545 | * | |
c8458c7e DV |
546 | * Creates a new blob property for a specified DRM device, optionally |
547 | * copying data. Note that blob properties are meant to be invariant, hence the | |
548 | * data must be filled out before the blob is used as the value of any property. | |
549 | * | |
59e71ee7 DV |
550 | * Returns: |
551 | * New blob property with a single reference on success, or an ERR_PTR | |
552 | * value on failure. | |
553 | */ | |
554 | struct drm_property_blob * | |
555 | drm_property_create_blob(struct drm_device *dev, size_t length, | |
556 | const void *data) | |
557 | { | |
558 | struct drm_property_blob *blob; | |
559 | int ret; | |
560 | ||
5bf8bec3 | 561 | if (!length || length > INT_MAX - sizeof(struct drm_property_blob)) |
59e71ee7 DV |
562 | return ERR_PTR(-EINVAL); |
563 | ||
718b5406 | 564 | blob = kvzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL); |
59e71ee7 DV |
565 | if (!blob) |
566 | return ERR_PTR(-ENOMEM); | |
567 | ||
568 | /* This must be explicitly initialised, so we can safely call list_del | |
569 | * on it in the removal handler, even if it isn't in a file list. */ | |
570 | INIT_LIST_HEAD(&blob->head_file); | |
9c60583c | 571 | blob->data = (void *)blob + sizeof(*blob); |
59e71ee7 DV |
572 | blob->length = length; |
573 | blob->dev = dev; | |
574 | ||
575 | if (data) | |
576 | memcpy(blob->data, data, length); | |
577 | ||
2135ea7a TR |
578 | ret = __drm_mode_object_add(dev, &blob->base, DRM_MODE_OBJECT_BLOB, |
579 | true, drm_property_free_blob); | |
59e71ee7 | 580 | if (ret) { |
718b5406 | 581 | kvfree(blob); |
59e71ee7 DV |
582 | return ERR_PTR(-EINVAL); |
583 | } | |
584 | ||
585 | mutex_lock(&dev->mode_config.blob_lock); | |
586 | list_add_tail(&blob->head_global, | |
587 | &dev->mode_config.property_blob_list); | |
588 | mutex_unlock(&dev->mode_config.blob_lock); | |
589 | ||
590 | return blob; | |
591 | } | |
592 | EXPORT_SYMBOL(drm_property_create_blob); | |
593 | ||
594 | /** | |
6472e509 TR |
595 | * drm_property_blob_put - release a blob property reference |
596 | * @blob: DRM blob property | |
59e71ee7 | 597 | * |
6472e509 | 598 | * Releases a reference to a blob property. May free the object. |
59e71ee7 | 599 | */ |
6472e509 | 600 | void drm_property_blob_put(struct drm_property_blob *blob) |
59e71ee7 DV |
601 | { |
602 | if (!blob) | |
603 | return; | |
604 | ||
020a218f | 605 | drm_mode_object_put(&blob->base); |
59e71ee7 | 606 | } |
6472e509 | 607 | EXPORT_SYMBOL(drm_property_blob_put); |
59e71ee7 | 608 | |
59e71ee7 DV |
609 | void drm_property_destroy_user_blobs(struct drm_device *dev, |
610 | struct drm_file *file_priv) | |
611 | { | |
612 | struct drm_property_blob *blob, *bt; | |
613 | ||
614 | /* | |
615 | * When the file gets released that means no one else can access the | |
616 | * blob list any more, so no need to grab dev->blob_lock. | |
617 | */ | |
618 | list_for_each_entry_safe(blob, bt, &file_priv->blobs, head_file) { | |
619 | list_del_init(&blob->head_file); | |
6472e509 | 620 | drm_property_blob_put(blob); |
59e71ee7 DV |
621 | } |
622 | } | |
623 | ||
624 | /** | |
6472e509 TR |
625 | * drm_property_blob_get - acquire blob property reference |
626 | * @blob: DRM blob property | |
c8458c7e | 627 | * |
6472e509 | 628 | * Acquires a reference to an existing blob property. Returns @blob, which |
c8458c7e | 629 | * allows this to be used as a shorthand in assignments. |
59e71ee7 | 630 | */ |
6472e509 | 631 | struct drm_property_blob *drm_property_blob_get(struct drm_property_blob *blob) |
59e71ee7 | 632 | { |
020a218f | 633 | drm_mode_object_get(&blob->base); |
59e71ee7 DV |
634 | return blob; |
635 | } | |
6472e509 | 636 | EXPORT_SYMBOL(drm_property_blob_get); |
59e71ee7 DV |
637 | |
638 | /** | |
639 | * drm_property_lookup_blob - look up a blob property and take a reference | |
640 | * @dev: drm device | |
641 | * @id: id of the blob property | |
642 | * | |
643 | * If successful, this takes an additional reference to the blob property. | |
4cf1d871 | 644 | * callers need to make sure to eventually unreferenced the returned property |
6472e509 | 645 | * again, using drm_property_blob_put(). |
c8458c7e DV |
646 | * |
647 | * Return: | |
648 | * NULL on failure, pointer to the blob on success. | |
59e71ee7 DV |
649 | */ |
650 | struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev, | |
651 | uint32_t id) | |
652 | { | |
653 | struct drm_mode_object *obj; | |
654 | struct drm_property_blob *blob = NULL; | |
655 | ||
418da172 | 656 | obj = __drm_mode_object_find(dev, NULL, id, DRM_MODE_OBJECT_BLOB); |
59e71ee7 DV |
657 | if (obj) |
658 | blob = obj_to_blob(obj); | |
659 | return blob; | |
660 | } | |
661 | EXPORT_SYMBOL(drm_property_lookup_blob); | |
662 | ||
663 | /** | |
c8458c7e | 664 | * drm_property_replace_global_blob - replace existing blob property |
59e71ee7 DV |
665 | * @dev: drm device |
666 | * @replace: location of blob property pointer to be replaced | |
667 | * @length: length of data for new blob, or 0 for no data | |
668 | * @data: content for new blob, or NULL for no data | |
669 | * @obj_holds_id: optional object for property holding blob ID | |
670 | * @prop_holds_id: optional property holding blob ID | |
671 | * @return 0 on success or error on failure | |
672 | * | |
c8458c7e DV |
673 | * This function will replace a global property in the blob list, optionally |
674 | * updating a property which holds the ID of that property. | |
59e71ee7 DV |
675 | * |
676 | * If length is 0 or data is NULL, no new blob will be created, and the holding | |
677 | * property, if specified, will be set to 0. | |
678 | * | |
679 | * Access to the replace pointer is assumed to be protected by the caller, e.g. | |
680 | * by holding the relevant modesetting object lock for its parent. | |
681 | * | |
682 | * For example, a drm_connector has a 'PATH' property, which contains the ID | |
683 | * of a blob property with the value of the MST path information. Calling this | |
684 | * function with replace pointing to the connector's path_blob_ptr, length and | |
685 | * data set for the new path information, obj_holds_id set to the connector's | |
686 | * base object, and prop_holds_id set to the path property name, will perform | |
687 | * a completely atomic update. The access to path_blob_ptr is protected by the | |
688 | * caller holding a lock on the connector. | |
689 | */ | |
690 | int drm_property_replace_global_blob(struct drm_device *dev, | |
691 | struct drm_property_blob **replace, | |
692 | size_t length, | |
693 | const void *data, | |
694 | struct drm_mode_object *obj_holds_id, | |
695 | struct drm_property *prop_holds_id) | |
696 | { | |
697 | struct drm_property_blob *new_blob = NULL; | |
698 | struct drm_property_blob *old_blob = NULL; | |
699 | int ret; | |
700 | ||
701 | WARN_ON(replace == NULL); | |
702 | ||
703 | old_blob = *replace; | |
704 | ||
705 | if (length && data) { | |
706 | new_blob = drm_property_create_blob(dev, length, data); | |
707 | if (IS_ERR(new_blob)) | |
708 | return PTR_ERR(new_blob); | |
709 | } | |
710 | ||
59e71ee7 DV |
711 | if (obj_holds_id) { |
712 | ret = drm_object_property_set_value(obj_holds_id, | |
713 | prop_holds_id, | |
714 | new_blob ? | |
715 | new_blob->base.id : 0); | |
716 | if (ret != 0) | |
717 | goto err_created; | |
718 | } | |
719 | ||
6472e509 | 720 | drm_property_blob_put(old_blob); |
59e71ee7 DV |
721 | *replace = new_blob; |
722 | ||
723 | return 0; | |
724 | ||
725 | err_created: | |
6472e509 | 726 | drm_property_blob_put(new_blob); |
59e71ee7 DV |
727 | return ret; |
728 | } | |
729 | EXPORT_SYMBOL(drm_property_replace_global_blob); | |
730 | ||
5f057ffd PR |
731 | /** |
732 | * drm_property_replace_blob - replace a blob property | |
733 | * @blob: a pointer to the member blob to be replaced | |
734 | * @new_blob: the new blob to replace with | |
735 | * | |
736 | * Return: true if the blob was in fact replaced. | |
737 | */ | |
738 | bool drm_property_replace_blob(struct drm_property_blob **blob, | |
739 | struct drm_property_blob *new_blob) | |
740 | { | |
741 | struct drm_property_blob *old_blob = *blob; | |
742 | ||
743 | if (old_blob == new_blob) | |
744 | return false; | |
745 | ||
746 | drm_property_blob_put(old_blob); | |
747 | if (new_blob) | |
748 | drm_property_blob_get(new_blob); | |
749 | *blob = new_blob; | |
750 | return true; | |
751 | } | |
752 | EXPORT_SYMBOL(drm_property_replace_blob); | |
753 | ||
59e71ee7 DV |
754 | int drm_mode_getblob_ioctl(struct drm_device *dev, |
755 | void *data, struct drm_file *file_priv) | |
756 | { | |
757 | struct drm_mode_get_blob *out_resp = data; | |
758 | struct drm_property_blob *blob; | |
759 | int ret = 0; | |
59e71ee7 DV |
760 | |
761 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) | |
69fdf420 | 762 | return -EOPNOTSUPP; |
59e71ee7 DV |
763 | |
764 | blob = drm_property_lookup_blob(dev, out_resp->blob_id); | |
765 | if (!blob) | |
766 | return -ENOENT; | |
767 | ||
768 | if (out_resp->length == blob->length) { | |
75df6247 CW |
769 | if (copy_to_user(u64_to_user_ptr(out_resp->data), |
770 | blob->data, | |
771 | blob->length)) { | |
59e71ee7 DV |
772 | ret = -EFAULT; |
773 | goto unref; | |
774 | } | |
775 | } | |
776 | out_resp->length = blob->length; | |
777 | unref: | |
6472e509 | 778 | drm_property_blob_put(blob); |
59e71ee7 DV |
779 | |
780 | return ret; | |
781 | } | |
782 | ||
59e71ee7 DV |
783 | int drm_mode_createblob_ioctl(struct drm_device *dev, |
784 | void *data, struct drm_file *file_priv) | |
785 | { | |
786 | struct drm_mode_create_blob *out_resp = data; | |
787 | struct drm_property_blob *blob; | |
59e71ee7 DV |
788 | int ret = 0; |
789 | ||
790 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) | |
69fdf420 | 791 | return -EOPNOTSUPP; |
59e71ee7 DV |
792 | |
793 | blob = drm_property_create_blob(dev, out_resp->length, NULL); | |
794 | if (IS_ERR(blob)) | |
795 | return PTR_ERR(blob); | |
796 | ||
75df6247 CW |
797 | if (copy_from_user(blob->data, |
798 | u64_to_user_ptr(out_resp->data), | |
799 | out_resp->length)) { | |
59e71ee7 DV |
800 | ret = -EFAULT; |
801 | goto out_blob; | |
802 | } | |
803 | ||
804 | /* Dropping the lock between create_blob and our access here is safe | |
805 | * as only the same file_priv can remove the blob; at this point, it is | |
806 | * not associated with any file_priv. */ | |
807 | mutex_lock(&dev->mode_config.blob_lock); | |
808 | out_resp->blob_id = blob->base.id; | |
809 | list_add_tail(&blob->head_file, &file_priv->blobs); | |
810 | mutex_unlock(&dev->mode_config.blob_lock); | |
811 | ||
812 | return 0; | |
813 | ||
814 | out_blob: | |
6472e509 | 815 | drm_property_blob_put(blob); |
59e71ee7 DV |
816 | return ret; |
817 | } | |
818 | ||
59e71ee7 DV |
819 | int drm_mode_destroyblob_ioctl(struct drm_device *dev, |
820 | void *data, struct drm_file *file_priv) | |
821 | { | |
822 | struct drm_mode_destroy_blob *out_resp = data; | |
823 | struct drm_property_blob *blob = NULL, *bt; | |
824 | bool found = false; | |
825 | int ret = 0; | |
826 | ||
827 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) | |
69fdf420 | 828 | return -EOPNOTSUPP; |
59e71ee7 DV |
829 | |
830 | blob = drm_property_lookup_blob(dev, out_resp->blob_id); | |
831 | if (!blob) | |
832 | return -ENOENT; | |
833 | ||
834 | mutex_lock(&dev->mode_config.blob_lock); | |
835 | /* Ensure the property was actually created by this user. */ | |
836 | list_for_each_entry(bt, &file_priv->blobs, head_file) { | |
837 | if (bt == blob) { | |
838 | found = true; | |
839 | break; | |
840 | } | |
841 | } | |
842 | ||
843 | if (!found) { | |
844 | ret = -EPERM; | |
845 | goto err; | |
846 | } | |
847 | ||
848 | /* We must drop head_file here, because we may not be the last | |
849 | * reference on the blob. */ | |
850 | list_del_init(&blob->head_file); | |
851 | mutex_unlock(&dev->mode_config.blob_lock); | |
852 | ||
853 | /* One reference from lookup, and one from the filp. */ | |
6472e509 TR |
854 | drm_property_blob_put(blob); |
855 | drm_property_blob_put(blob); | |
59e71ee7 DV |
856 | |
857 | return 0; | |
858 | ||
859 | err: | |
860 | mutex_unlock(&dev->mode_config.blob_lock); | |
6472e509 | 861 | drm_property_blob_put(blob); |
59e71ee7 DV |
862 | |
863 | return ret; | |
864 | } | |
865 | ||
866 | /* Some properties could refer to dynamic refcnt'd objects, or things that | |
867 | * need special locking to handle lifetime issues (ie. to ensure the prop | |
868 | * value doesn't become invalid part way through the property update due to | |
869 | * race). The value returned by reference via 'obj' should be passed back | |
870 | * to drm_property_change_valid_put() after the property is set (and the | |
1e55a53a | 871 | * object to which the property is attached has a chance to take its own |
59e71ee7 DV |
872 | * reference). |
873 | */ | |
874 | bool drm_property_change_valid_get(struct drm_property *property, | |
c8458c7e | 875 | uint64_t value, struct drm_mode_object **ref) |
59e71ee7 DV |
876 | { |
877 | int i; | |
878 | ||
879 | if (property->flags & DRM_MODE_PROP_IMMUTABLE) | |
880 | return false; | |
881 | ||
882 | *ref = NULL; | |
883 | ||
884 | if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) { | |
885 | if (value < property->values[0] || value > property->values[1]) | |
886 | return false; | |
887 | return true; | |
888 | } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) { | |
889 | int64_t svalue = U642I64(value); | |
890 | ||
891 | if (svalue < U642I64(property->values[0]) || | |
892 | svalue > U642I64(property->values[1])) | |
893 | return false; | |
894 | return true; | |
895 | } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { | |
896 | uint64_t valid_mask = 0; | |
897 | ||
898 | for (i = 0; i < property->num_values; i++) | |
899 | valid_mask |= (1ULL << property->values[i]); | |
900 | return !(value & ~valid_mask); | |
30c06570 ML |
901 | } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { |
902 | struct drm_property_blob *blob; | |
903 | ||
904 | if (value == 0) | |
905 | return true; | |
906 | ||
907 | blob = drm_property_lookup_blob(property->dev, value); | |
908 | if (blob) { | |
909 | *ref = &blob->base; | |
910 | return true; | |
911 | } else { | |
912 | return false; | |
913 | } | |
914 | } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { | |
59e71ee7 DV |
915 | /* a zero value for an object property translates to null: */ |
916 | if (value == 0) | |
917 | return true; | |
918 | ||
418da172 | 919 | *ref = __drm_mode_object_find(property->dev, NULL, value, |
59e71ee7 DV |
920 | property->values[0]); |
921 | return *ref != NULL; | |
922 | } | |
923 | ||
924 | for (i = 0; i < property->num_values; i++) | |
925 | if (property->values[i] == value) | |
926 | return true; | |
927 | return false; | |
928 | } | |
929 | ||
930 | void drm_property_change_valid_put(struct drm_property *property, | |
30c06570 | 931 | struct drm_mode_object *ref) |
59e71ee7 DV |
932 | { |
933 | if (!ref) | |
934 | return; | |
935 | ||
30c06570 | 936 | if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { |
020a218f | 937 | drm_mode_object_put(ref); |
30c06570 | 938 | } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) |
6472e509 | 939 | drm_property_blob_put(obj_to_blob(ref)); |
59e71ee7 | 940 | } |