blob: 663781ff6bdb65374430ed4674a833b316cabe54 [file] [log] [blame]
Gopikrishnaiah Anandane0e5e0c2016-05-25 11:05:33 -07001/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
Clarence Ip7aa390f2016-05-26 21:06:54 -04002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include "msm_prop.h"
14
15void msm_property_init(struct msm_property_info *info,
16 struct drm_mode_object *base,
17 struct drm_device *dev,
18 struct drm_property **property_array,
19 struct msm_property_data *property_data,
20 uint32_t property_count,
21 uint32_t blob_count,
22 uint32_t state_size)
23{
Clarence Ip913624e2016-06-23 14:56:32 -040024 int i;
25
Clarence Ip7aa390f2016-05-26 21:06:54 -040026 /* prevent access if any of these are NULL */
27 if (!base || !dev || !property_array || !property_data) {
28 property_count = 0;
29 blob_count = 0;
30
31 DRM_ERROR("invalid arguments, forcing zero properties\n");
32 return;
33 }
34
35 /* can't have more blob properties than total properties */
36 if (blob_count > property_count) {
37 blob_count = property_count;
38
39 DBG("Capping number of blob properties to %d", blob_count);
40 }
41
42 if (!info) {
43 DRM_ERROR("info pointer is NULL\n");
44 } else {
45 info->base = base;
46 info->dev = dev;
47 info->property_array = property_array;
48 info->property_data = property_data;
49 info->property_count = property_count;
50 info->blob_count = blob_count;
51 info->install_request = 0;
52 info->install_count = 0;
53 info->recent_idx = 0;
Clarence Ip913624e2016-06-23 14:56:32 -040054 info->is_active = false;
Clarence Ip7aa390f2016-05-26 21:06:54 -040055 info->state_size = state_size;
56 info->state_cache_size = 0;
57 mutex_init(&info->property_lock);
58
59 memset(property_data,
60 0,
61 sizeof(struct msm_property_data) *
62 property_count);
Clarence Ip913624e2016-06-23 14:56:32 -040063 INIT_LIST_HEAD(&info->dirty_list);
64
65 for (i = 0; i < property_count; ++i)
66 INIT_LIST_HEAD(&property_data[i].dirty_node);
Clarence Ip7aa390f2016-05-26 21:06:54 -040067 }
68}
69
70void msm_property_destroy(struct msm_property_info *info)
71{
72 if (!info)
73 return;
74
Clarence Ip913624e2016-06-23 14:56:32 -040075 /* reset dirty list */
76 INIT_LIST_HEAD(&info->dirty_list);
77
Clarence Ip7aa390f2016-05-26 21:06:54 -040078 /* free state cache */
79 while (info->state_cache_size > 0)
80 kfree(info->state_cache[--(info->state_cache_size)]);
81
82 mutex_destroy(&info->property_lock);
83}
84
Clarence Ip913624e2016-06-23 14:56:32 -040085int msm_property_pop_dirty(struct msm_property_info *info)
86{
87 struct list_head *item;
88 int rc = 0;
89
90 if (!info) {
91 DRM_ERROR("invalid info\n");
92 return -EINVAL;
93 }
94
95 mutex_lock(&info->property_lock);
96 if (list_empty(&info->dirty_list)) {
97 rc = -EAGAIN;
98 } else {
99 item = info->dirty_list.next;
100 list_del_init(item);
101 rc = container_of(item, struct msm_property_data, dirty_node)
102 - info->property_data;
103 DRM_DEBUG_KMS("property %d dirty\n", rc);
104 }
105 mutex_unlock(&info->property_lock);
106
107 return rc;
108}
109
110/**
111 * _msm_property_set_dirty_no_lock - flag given property as being dirty
112 * This function doesn't mutex protect the
113 * dirty linked list.
114 * @info: Pointer to property info container struct
115 * @property_idx: Property index
116 */
117static void _msm_property_set_dirty_no_lock(
118 struct msm_property_info *info,
119 uint32_t property_idx)
120{
121 if (!info || property_idx >= info->property_count) {
122 DRM_ERROR("invalid argument(s), info %pK, idx %u\n",
123 info, property_idx);
124 return;
125 }
126
127 /* avoid re-inserting if already dirty */
128 if (!list_empty(&info->property_data[property_idx].dirty_node)) {
129 DRM_DEBUG_KMS("property %u already dirty\n", property_idx);
130 return;
131 }
132
133 list_add_tail(&info->property_data[property_idx].dirty_node,
134 &info->dirty_list);
135}
136
Clarence Ip5fc00c52016-09-23 15:03:34 -0400137/**
138 * _msm_property_install_integer - install standard drm range property
139 * @info: Pointer to property info container struct
140 * @name: Property name
141 * @flags: Other property type flags, e.g. DRM_MODE_PROP_IMMUTABLE
142 * @min: Min property value
143 * @max: Max property value
144 * @init: Default Property value
145 * @property_idx: Property index
146 * @force_dirty: Whether or not to filter 'dirty' status on unchanged values
147 */
148static void _msm_property_install_integer(struct msm_property_info *info,
Lloyd Atkinson38ad8c92016-07-06 10:39:32 -0400149 const char *name, int flags, uint64_t min, uint64_t max,
Clarence Ip5fc00c52016-09-23 15:03:34 -0400150 uint64_t init, uint32_t property_idx, bool force_dirty)
Clarence Ip7aa390f2016-05-26 21:06:54 -0400151{
152 struct drm_property **prop;
153
154 if (!info)
155 return;
156
157 ++info->install_request;
158
159 if (!name || (property_idx >= info->property_count)) {
160 DRM_ERROR("invalid argument(s), %s\n", name ? name : "null");
161 } else {
162 prop = &info->property_array[property_idx];
163 /*
164 * Properties need to be attached to each drm object that
165 * uses them, but only need to be created once
166 */
167 if (*prop == 0) {
168 *prop = drm_property_create_range(info->dev,
Lloyd Atkinson38ad8c92016-07-06 10:39:32 -0400169 flags, name, min, max);
Clarence Ip7aa390f2016-05-26 21:06:54 -0400170 if (*prop == 0)
171 DRM_ERROR("create %s property failed\n", name);
172 }
173
174 /* save init value for later */
175 info->property_data[property_idx].default_value = init;
Clarence Ip5fc00c52016-09-23 15:03:34 -0400176 info->property_data[property_idx].force_dirty = force_dirty;
Clarence Ip7aa390f2016-05-26 21:06:54 -0400177
178 /* always attach property, if created */
179 if (*prop) {
180 drm_object_attach_property(info->base, *prop, init);
181 ++info->install_count;
182 }
183 }
184}
185
Clarence Ip5fc00c52016-09-23 15:03:34 -0400186void msm_property_install_range(struct msm_property_info *info,
187 const char *name, int flags, uint64_t min, uint64_t max,
188 uint64_t init, uint32_t property_idx)
189{
190 _msm_property_install_integer(info, name, flags,
191 min, max, init, property_idx, false);
192}
193
194void msm_property_install_volatile_range(struct msm_property_info *info,
195 const char *name, int flags, uint64_t min, uint64_t max,
196 uint64_t init, uint32_t property_idx)
197{
198 _msm_property_install_integer(info, name, flags,
199 min, max, init, property_idx, true);
200}
201
Clarence Ip7aa390f2016-05-26 21:06:54 -0400202void msm_property_install_rotation(struct msm_property_info *info,
203 unsigned int supported_rotations, uint32_t property_idx)
204{
205 struct drm_property **prop;
206
207 if (!info)
208 return;
209
210 ++info->install_request;
211
212 if (property_idx >= info->property_count) {
213 DRM_ERROR("invalid property index %d\n", property_idx);
214 } else {
215 prop = &info->property_array[property_idx];
216 /*
217 * Properties need to be attached to each drm object that
218 * uses them, but only need to be created once
219 */
220 if (*prop == 0) {
221 *prop = drm_mode_create_rotation_property(info->dev,
222 supported_rotations);
223 if (*prop == 0)
224 DRM_ERROR("create rotation property failed\n");
225 }
226
227 /* save init value for later */
228 info->property_data[property_idx].default_value = 0;
Clarence Ip5fc00c52016-09-23 15:03:34 -0400229 info->property_data[property_idx].force_dirty = false;
Clarence Ip7aa390f2016-05-26 21:06:54 -0400230
231 /* always attach property, if created */
232 if (*prop) {
233 drm_object_attach_property(info->base, *prop, 0);
234 ++info->install_count;
235 }
236 }
237}
238
239void msm_property_install_enum(struct msm_property_info *info,
Lloyd Atkinson38ad8c92016-07-06 10:39:32 -0400240 const char *name, int flags, int is_bitmask,
Clarence Ip7aa390f2016-05-26 21:06:54 -0400241 const struct drm_prop_enum_list *values, int num_values,
242 uint32_t property_idx)
243{
244 struct drm_property **prop;
245
246 if (!info)
247 return;
248
249 ++info->install_request;
250
251 if (!name || !values || !num_values ||
252 (property_idx >= info->property_count)) {
253 DRM_ERROR("invalid argument(s), %s\n", name ? name : "null");
254 } else {
255 prop = &info->property_array[property_idx];
256 /*
257 * Properties need to be attached to each drm object that
258 * uses them, but only need to be created once
259 */
260 if (*prop == 0) {
261 /* 'bitmask' is a special type of 'enum' */
262 if (is_bitmask)
263 *prop = drm_property_create_bitmask(info->dev,
Lloyd Atkinson38ad8c92016-07-06 10:39:32 -0400264 DRM_MODE_PROP_BITMASK | flags,
265 name, values, num_values, -1);
Clarence Ip7aa390f2016-05-26 21:06:54 -0400266 else
267 *prop = drm_property_create_enum(info->dev,
Lloyd Atkinson38ad8c92016-07-06 10:39:32 -0400268 DRM_MODE_PROP_ENUM | flags,
269 name, values, num_values);
Clarence Ip7aa390f2016-05-26 21:06:54 -0400270 if (*prop == 0)
271 DRM_ERROR("create %s property failed\n", name);
272 }
273
274 /* save init value for later */
275 info->property_data[property_idx].default_value = 0;
Clarence Ip5fc00c52016-09-23 15:03:34 -0400276 info->property_data[property_idx].force_dirty = false;
Clarence Ip7aa390f2016-05-26 21:06:54 -0400277
278 /* always attach property, if created */
279 if (*prop) {
280 drm_object_attach_property(info->base, *prop, 0);
281 ++info->install_count;
282 }
283 }
284}
285
286void msm_property_install_blob(struct msm_property_info *info,
287 const char *name, int flags, uint32_t property_idx)
288{
289 struct drm_property **prop;
290
291 if (!info)
292 return;
293
294 ++info->install_request;
295
296 if (!name || (property_idx >= info->blob_count)) {
297 DRM_ERROR("invalid argument(s), %s\n", name ? name : "null");
298 } else {
299 prop = &info->property_array[property_idx];
300 /*
301 * Properties need to be attached to each drm object that
302 * uses them, but only need to be created once
303 */
304 if (*prop == 0) {
305 /* use 'create' for blob property place holder */
306 *prop = drm_property_create(info->dev,
307 DRM_MODE_PROP_BLOB | flags, name, 0);
308 if (*prop == 0)
309 DRM_ERROR("create %s property failed\n", name);
310 }
311
312 /* save init value for later */
313 info->property_data[property_idx].default_value = 0;
Clarence Ip5fc00c52016-09-23 15:03:34 -0400314 info->property_data[property_idx].force_dirty = true;
Clarence Ip7aa390f2016-05-26 21:06:54 -0400315
316 /* always attach property, if created */
317 if (*prop) {
318 drm_object_attach_property(info->base, *prop, -1);
319 ++info->install_count;
320 }
321 }
322}
323
324int msm_property_install_get_status(struct msm_property_info *info)
325{
326 int rc = -ENOMEM;
327
328 if (info && (info->install_request == info->install_count))
329 rc = 0;
330
331 return rc;
332}
333
334int msm_property_index(struct msm_property_info *info,
335 struct drm_property *property)
336{
337 uint32_t count;
338 int32_t idx;
339 int rc = -EINVAL;
340
341 if (!info || !property) {
342 DRM_ERROR("invalid argument(s)\n");
343 } else {
344 /*
345 * Linear search, but start from last found index. This will
346 * help if any single property is accessed multiple times in a
347 * row. Ideally, we could keep a list of properties sorted in
348 * the order of most recent access, but that may be overkill
349 * for now.
350 */
351 mutex_lock(&info->property_lock);
352 idx = info->recent_idx;
353 count = info->property_count;
354 while (count) {
355 --count;
356
357 /* stop searching on match */
358 if (info->property_array[idx] == property) {
359 info->recent_idx = idx;
360 rc = idx;
361 break;
362 }
363
364 /* move to next valid index */
365 if (--idx < 0)
366 idx = info->property_count - 1;
367 }
368 mutex_unlock(&info->property_lock);
369 }
370
371 return rc;
372}
373
374int msm_property_atomic_set(struct msm_property_info *info,
375 uint64_t *property_values,
376 struct drm_property_blob **property_blobs,
377 struct drm_property *property, uint64_t val)
378{
379 struct drm_property_blob *blob;
380 int property_idx, rc = -EINVAL;
381
382 property_idx = msm_property_index(info, property);
383 if (!info || (property_idx == -EINVAL) || !property_values) {
Gopikrishnaiah Anandane0e5e0c2016-05-25 11:05:33 -0700384 DRM_DEBUG("Invalid argument(s)\n");
Clarence Ip7aa390f2016-05-26 21:06:54 -0400385 } else {
386 /* extra handling for incoming properties */
387 mutex_lock(&info->property_lock);
388 if ((property->flags & DRM_MODE_PROP_BLOB) &&
389 (property_idx < info->blob_count) &&
390 property_blobs) {
391 /* DRM lookup also takes a reference */
392 blob = drm_property_lookup_blob(info->dev,
393 (uint32_t)val);
394 if (!blob) {
395 DRM_ERROR("blob not found\n");
396 val = 0;
397 } else {
398 DBG("Blob %u saved", blob->base.id);
399 val = blob->base.id;
400
401 /* save blob - need to clear previous ref */
402 if (property_blobs[property_idx])
403 drm_property_unreference_blob(
404 property_blobs[property_idx]);
405 property_blobs[property_idx] = blob;
406 }
407 }
Clarence Ip913624e2016-06-23 14:56:32 -0400408
409 /* update value and flag as dirty */
Clarence Ip282dad62016-09-27 17:07:35 -0400410 if (property_values[property_idx] != val ||
Clarence Ip5fc00c52016-09-23 15:03:34 -0400411 info->property_data[property_idx].force_dirty) {
Clarence Ip282dad62016-09-27 17:07:35 -0400412 property_values[property_idx] = val;
413 _msm_property_set_dirty_no_lock(info, property_idx);
Clarence Ip913624e2016-06-23 14:56:32 -0400414
Clarence Ip282dad62016-09-27 17:07:35 -0400415 DBG("%s - %lld", property->name, val);
416 }
417 mutex_unlock(&info->property_lock);
Clarence Ip7aa390f2016-05-26 21:06:54 -0400418 rc = 0;
419 }
420
421 return rc;
422}
423
424int msm_property_atomic_get(struct msm_property_info *info,
425 uint64_t *property_values,
426 struct drm_property_blob **property_blobs,
427 struct drm_property *property, uint64_t *val)
428{
429 int property_idx, rc = -EINVAL;
430
431 property_idx = msm_property_index(info, property);
432 if (!info || (property_idx == -EINVAL) || !property_values || !val) {
Gopikrishnaiah Anandane0e5e0c2016-05-25 11:05:33 -0700433 DRM_DEBUG("Invalid argument(s)\n");
Clarence Ip7aa390f2016-05-26 21:06:54 -0400434 } else {
435 mutex_lock(&info->property_lock);
436 *val = property_values[property_idx];
437 mutex_unlock(&info->property_lock);
438 rc = 0;
439 }
440
441 return rc;
442}
443
444void *msm_property_alloc_state(struct msm_property_info *info)
445{
446 void *state = NULL;
447
448 if (!info) {
449 DRM_ERROR("invalid property info\n");
450 return NULL;
451 }
452
453 mutex_lock(&info->property_lock);
454 if (info->state_cache_size)
455 state = info->state_cache[--(info->state_cache_size)];
456 mutex_unlock(&info->property_lock);
457
458 if (!state && info->state_size)
459 state = kmalloc(info->state_size, GFP_KERNEL);
460
461 if (!state)
462 DRM_ERROR("failed to allocate state\n");
463
464 return state;
465}
466
467/**
468 * _msm_property_free_state - helper function for freeing local state objects
469 * @info: Pointer to property info container struct
470 * @st: Pointer to state object
471 */
472static void _msm_property_free_state(struct msm_property_info *info, void *st)
473{
474 if (!info || !st)
475 return;
476
477 mutex_lock(&info->property_lock);
478 if (info->state_cache_size < MSM_PROP_STATE_CACHE_SIZE)
479 info->state_cache[(info->state_cache_size)++] = st;
480 else
481 kfree(st);
482 mutex_unlock(&info->property_lock);
483}
484
485void msm_property_reset_state(struct msm_property_info *info, void *state,
486 uint64_t *property_values,
487 struct drm_property_blob **property_blobs)
488{
489 uint32_t i;
490
491 if (!info) {
492 DRM_ERROR("invalid property info\n");
493 return;
494 }
495
496 if (state)
497 memset(state, 0, info->state_size);
498
499 /*
500 * Assign default property values. This helper is mostly used
501 * to initialize newly created state objects.
502 */
503 if (property_values)
504 for (i = 0; i < info->property_count; ++i)
505 property_values[i] =
506 info->property_data[i].default_value;
507
508 if (property_blobs)
509 for (i = 0; i < info->blob_count; ++i)
510 property_blobs[i] = 0;
511}
512
513void msm_property_duplicate_state(struct msm_property_info *info,
514 void *old_state, void *state,
515 uint64_t *property_values,
516 struct drm_property_blob **property_blobs)
517{
518 uint32_t i;
519
520 if (!info || !old_state || !state) {
521 DRM_ERROR("invalid argument(s)\n");
522 return;
523 }
524
525 memcpy(state, old_state, info->state_size);
526
527 if (property_blobs) {
528 /* add ref count for blobs */
529 for (i = 0; i < info->blob_count; ++i)
530 if (property_blobs[i])
531 drm_property_reference_blob(property_blobs[i]);
532 }
533}
534
535void msm_property_destroy_state(struct msm_property_info *info, void *state,
536 uint64_t *property_values,
537 struct drm_property_blob **property_blobs)
538{
539 uint32_t i;
540
541 if (!info || !state) {
542 DRM_ERROR("invalid argument(s)\n");
543 return;
544 }
545 if (property_blobs) {
546 /* remove ref count for blobs */
547 for (i = 0; i < info->blob_count; ++i)
548 if (property_blobs[i])
549 drm_property_unreference_blob(
550 property_blobs[i]);
551 }
552
553 _msm_property_free_state(info, state);
554}
555
556void *msm_property_get_blob(struct msm_property_info *info,
557 struct drm_property_blob **property_blobs,
558 size_t *byte_len,
559 uint32_t property_idx)
560{
561 struct drm_property_blob *blob;
562 size_t len = 0;
563 void *rc = 0;
564
Clarence Ip075c2572016-07-18 19:06:10 -0400565 if (!info || !property_blobs || (property_idx >= info->blob_count)) {
Clarence Ip7aa390f2016-05-26 21:06:54 -0400566 DRM_ERROR("invalid argument(s)\n");
567 } else {
568 blob = property_blobs[property_idx];
569 if (blob) {
570 len = blob->length;
571 rc = &blob->data;
572 }
573 }
574
575 if (byte_len)
576 *byte_len = len;
577
578 return rc;
579}
580
581int msm_property_set_blob(struct msm_property_info *info,
582 struct drm_property_blob **blob_reference,
583 void *blob_data,
584 size_t byte_len,
585 uint32_t property_idx)
586{
587 struct drm_property_blob *blob = NULL;
588 int rc = -EINVAL;
589
590 if (!info || !blob_reference || (property_idx >= info->blob_count)) {
591 DRM_ERROR("invalid argument(s)\n");
592 } else {
593 /* create blob */
594 if (blob_data && byte_len) {
595 blob = drm_property_create_blob(info->dev,
596 byte_len,
597 blob_data);
598 if (IS_ERR_OR_NULL(blob)) {
599 rc = PTR_ERR(blob);
600 DRM_ERROR("failed to create blob, %d\n", rc);
601 goto exit;
602 }
603 }
604
605 /* update drm object */
606 rc = drm_object_property_set_value(info->base,
607 info->property_array[property_idx],
608 blob ? blob->base.id : 0);
609 if (rc) {
610 DRM_ERROR("failed to set blob to property\n");
611 if (blob)
612 drm_property_unreference_blob(blob);
613 goto exit;
614 }
615
616 /* update local reference */
617 if (*blob_reference)
618 drm_property_unreference_blob(*blob_reference);
619 *blob_reference = blob;
620 }
621
622exit:
623 return rc;
624}
625
Lloyd Atkinsonb6191972016-08-10 18:31:46 -0400626int msm_property_set_property(struct msm_property_info *info,
Lloyd Atkinson13cee812016-08-16 16:10:31 -0400627 uint64_t *property_values,
Lloyd Atkinsonb6191972016-08-10 18:31:46 -0400628 uint32_t property_idx,
629 uint64_t val)
630{
631 int rc = -EINVAL;
632
633 if (!info || (property_idx >= info->property_count) ||
Lloyd Atkinson13cee812016-08-16 16:10:31 -0400634 property_idx < info->blob_count || !property_values) {
Lloyd Atkinsonb6191972016-08-10 18:31:46 -0400635 DRM_ERROR("invalid argument(s)\n");
636 } else {
Lloyd Atkinson13cee812016-08-16 16:10:31 -0400637 struct drm_property *drm_prop;
638
639 mutex_lock(&info->property_lock);
640
641 /* update cached value */
642 if (property_values)
643 property_values[property_idx] = val;
644
645 /* update the new default value for immutables */
646 drm_prop = info->property_array[property_idx];
647 if (drm_prop->flags & DRM_MODE_PROP_IMMUTABLE)
648 info->property_data[property_idx].default_value = val;
649
650 mutex_unlock(&info->property_lock);
651
Lloyd Atkinsonb6191972016-08-10 18:31:46 -0400652 /* update drm object */
Lloyd Atkinson13cee812016-08-16 16:10:31 -0400653 rc = drm_object_property_set_value(info->base, drm_prop, val);
Lloyd Atkinsonb6191972016-08-10 18:31:46 -0400654 if (rc)
655 DRM_ERROR("failed set property value, idx %d rc %d\n",
656 property_idx, rc);
Lloyd Atkinson13cee812016-08-16 16:10:31 -0400657
Lloyd Atkinsonb6191972016-08-10 18:31:46 -0400658 }
659
660 return rc;
661}
662