123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
- * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
- */
- #include "msm_prop.h"
- void msm_property_init(struct msm_property_info *info,
- struct drm_mode_object *base,
- struct drm_device *dev,
- struct drm_property **property_array,
- struct msm_property_data *property_data,
- uint32_t property_count,
- uint32_t blob_count,
- uint32_t state_size)
- {
- /* prevent access if any of these are NULL */
- if (!base || !dev || !property_array || !property_data) {
- property_count = 0;
- blob_count = 0;
- DRM_ERROR("invalid arguments, forcing zero properties\n");
- return;
- }
- /* can't have more blob properties than total properties */
- if (blob_count > property_count) {
- blob_count = property_count;
- DBG("Capping number of blob properties to %d", blob_count);
- }
- if (!info) {
- DRM_ERROR("info pointer is NULL\n");
- } else {
- info->base = base;
- info->dev = dev;
- info->property_array = property_array;
- info->property_data = property_data;
- info->property_count = property_count;
- info->blob_count = blob_count;
- info->install_request = 0;
- info->install_count = 0;
- info->recent_idx = 0;
- info->is_active = false;
- info->state_size = state_size;
- info->state_cache_size = 0;
- mutex_init(&info->property_lock);
- memset(property_data,
- 0,
- sizeof(struct msm_property_data) *
- property_count);
- }
- }
- void msm_property_destroy(struct msm_property_info *info)
- {
- if (!info)
- return;
- /* free state cache */
- while (info->state_cache_size > 0)
- kfree(info->state_cache[--(info->state_cache_size)]);
- mutex_destroy(&info->property_lock);
- }
- int msm_property_pop_dirty(struct msm_property_info *info,
- struct msm_property_state *property_state)
- {
- struct list_head *item;
- int rc = 0;
- if (!info || !property_state || !property_state->values) {
- DRM_ERROR("invalid argument(s)\n");
- return -EINVAL;
- }
- WARN_ON(!mutex_is_locked(&info->property_lock));
- if (list_empty(&property_state->dirty_list)) {
- rc = -EAGAIN;
- } else {
- item = property_state->dirty_list.next;
- list_del_init(item);
- rc = container_of(item, struct msm_property_value, dirty_node)
- - property_state->values;
- DRM_DEBUG_KMS("property %d dirty\n", rc);
- }
- return rc;
- }
- /**
- * _msm_property_set_dirty_no_lock - flag given property as being dirty
- * This function doesn't mutex protect the
- * dirty linked list.
- * @info: Pointer to property info container struct
- * @property_state: Pointer to property state container struct
- * @property_idx: Property index
- */
- static void _msm_property_set_dirty_no_lock(
- struct msm_property_info *info,
- struct msm_property_state *property_state,
- uint32_t property_idx)
- {
- if (!info || !property_state || !property_state->values ||
- property_idx >= info->property_count) {
- DRM_ERROR("invalid argument(s), idx %u\n", property_idx);
- return;
- }
- /* avoid re-inserting if already dirty */
- if (!list_empty(&property_state->values[property_idx].dirty_node)) {
- DRM_DEBUG_KMS("property %u already dirty\n", property_idx);
- return;
- }
- list_add_tail(&property_state->values[property_idx].dirty_node,
- &property_state->dirty_list);
- }
- bool msm_property_is_dirty(
- struct msm_property_info *info,
- struct msm_property_state *property_state,
- uint32_t property_idx)
- {
- if (!info || !property_state || !property_state->values ||
- property_idx >= info->property_count) {
- DRM_ERROR("invalid argument(s), idx %u\n", property_idx);
- return false;
- }
- return !list_empty(&property_state->values[property_idx].dirty_node);
- }
- /**
- * _msm_property_install_integer - install standard drm range property
- * @info: Pointer to property info container struct
- * @name: Property name
- * @flags: Other property type flags, e.g. DRM_MODE_PROP_IMMUTABLE
- * @min: Min property value
- * @max: Max property value
- * @init: Default Property value
- * @property_idx: Property index
- * @force_dirty: Whether or not to filter 'dirty' status on unchanged values
- */
- static void _msm_property_install_integer(struct msm_property_info *info,
- const char *name, int flags, uint64_t min, uint64_t max,
- uint64_t init, uint32_t property_idx, bool force_dirty)
- {
- struct drm_property **prop;
- if (!info)
- return;
- ++info->install_request;
- if (!name || (property_idx >= info->property_count)) {
- DRM_ERROR("invalid argument(s), %s\n", name ? name : "null");
- } else {
- prop = &info->property_array[property_idx];
- /*
- * Properties need to be attached to each drm object that
- * uses them, but only need to be created once
- */
- if (!*prop) {
- *prop = drm_property_create_range(info->dev,
- flags, name, min, max);
- if (!*prop)
- DRM_ERROR("create %s property failed\n", name);
- }
- /* save init value for later */
- info->property_data[property_idx].default_value = init;
- info->property_data[property_idx].force_dirty = force_dirty;
- /* always attach property, if created */
- if (*prop) {
- drm_object_attach_property(info->base, *prop, init);
- ++info->install_count;
- }
- }
- }
- void msm_property_install_range(struct msm_property_info *info,
- const char *name, int flags, uint64_t min, uint64_t max,
- uint64_t init, uint32_t property_idx)
- {
- _msm_property_install_integer(info, name, flags,
- min, max, init, property_idx, false);
- }
- void msm_property_install_volatile_range(struct msm_property_info *info,
- const char *name, int flags, uint64_t min, uint64_t max,
- uint64_t init, uint32_t property_idx)
- {
- _msm_property_install_integer(info, name, flags,
- min, max, init, property_idx, true);
- }
- /**
- * msm_property_install_enum_helper - install standard drm enum/bitmask property
- * This function is added as a helper function called within msm_property_install_enum
- * or msm_property_install_volatile_enum depending on whether we want to set the
- * enum property as dirty or not dirty.
- * @info: Pointer to property info container struct
- * @name: Property name
- * @flags: Other property type flags, e.g. DRM_MODE_PROP_IMMUTABLE
- * @is_bitmask: Set to non-zero to create a bitmask property, rather than an
- * enumeration one
- * @values: Array of allowable enumeration/bitmask values
- * @num_values: Size of values array
- * @init_idx: index of the values array entry to initialize the property
- * @property_idx: Property index
- * @force_dirty: Whether or not to filter 'dirty' status on unchanged values
- */
- void msm_property_install_enum_helper(struct msm_property_info *info,
- const char *name, int flags, int is_bitmask,
- const struct drm_prop_enum_list *values, int num_values,
- u32 init_idx, uint32_t property_idx, bool force_dirty)
- {
- struct drm_property **prop;
- if (!info)
- return;
- ++info->install_request;
- if (!name || !values || !num_values ||
- (property_idx >= info->property_count)) {
- DRM_ERROR("invalid argument(s), %s\n", name ? name : "null");
- } else {
- prop = &info->property_array[property_idx];
- /*
- * Properties need to be attached to each drm object that
- * uses them, but only need to be created once
- */
- if (!*prop) {
- /* 'bitmask' is a special type of 'enum' */
- if (is_bitmask)
- *prop = drm_property_create_bitmask(info->dev,
- DRM_MODE_PROP_BITMASK | flags,
- name, values, num_values, -1);
- else
- *prop = drm_property_create_enum(info->dev,
- DRM_MODE_PROP_ENUM | flags,
- name, values, num_values);
- if (!*prop)
- DRM_ERROR("create %s property failed\n", name);
- }
- /* save init value for later */
- info->property_data[property_idx].default_value = 0;
- info->property_data[property_idx].force_dirty = force_dirty;
- /* initialize with the given idx if valid */
- if (!is_bitmask && init_idx && (init_idx < num_values))
- info->property_data[property_idx].default_value =
- values[init_idx].type;
- /* always attach property, if created */
- if (*prop) {
- drm_object_attach_property(info->base, *prop,
- info->property_data
- [property_idx].default_value);
- ++info->install_count;
- }
- }
- }
- void msm_property_install_volatile_enum(struct msm_property_info *info,
- const char *name, int flags, int is_bitmask,
- const struct drm_prop_enum_list *values, int num_values,
- u32 init_idx, uint32_t property_idx)
- {
- msm_property_install_enum_helper(info, name, flags, is_bitmask,
- values, num_values, init_idx, property_idx, true);
- }
- void msm_property_install_enum(struct msm_property_info *info,
- const char *name, int flags, int is_bitmask,
- const struct drm_prop_enum_list *values, int num_values,
- u32 init_idx, uint32_t property_idx)
- {
- msm_property_install_enum_helper(info, name, flags, is_bitmask,
- values, num_values, init_idx, property_idx, false);
- }
- void msm_property_install_blob(struct msm_property_info *info,
- const char *name, int flags, uint32_t property_idx)
- {
- struct drm_property **prop;
- if (!info)
- return;
- ++info->install_request;
- if (!name || (property_idx >= info->blob_count)) {
- DRM_ERROR("invalid argument(s), %s\n", name ? name : "null");
- } else {
- prop = &info->property_array[property_idx];
- /*
- * Properties need to be attached to each drm object that
- * uses them, but only need to be created once
- */
- if (!*prop) {
- /* use 'create' for blob property place holder */
- *prop = drm_property_create(info->dev,
- DRM_MODE_PROP_BLOB | flags, name, 0);
- if (!*prop)
- DRM_ERROR("create %s property failed\n", name);
- }
- /* save init value for later */
- info->property_data[property_idx].default_value = 0;
- info->property_data[property_idx].force_dirty = true;
- /* always attach property, if created */
- if (*prop) {
- drm_object_attach_property(info->base, *prop, -1);
- ++info->install_count;
- }
- }
- }
- int msm_property_install_get_status(struct msm_property_info *info)
- {
- int rc = -ENOMEM;
- if (info && (info->install_request == info->install_count))
- rc = 0;
- return rc;
- }
- int msm_property_index(struct msm_property_info *info,
- struct drm_property *property)
- {
- uint32_t count;
- int32_t idx;
- int rc = -EINVAL;
- if (!info || !property) {
- DRM_ERROR("invalid argument(s)\n");
- } else {
- /*
- * Linear search, but start from last found index. This will
- * help if any single property is accessed multiple times in a
- * row. Ideally, we could keep a list of properties sorted in
- * the order of most recent access, but that may be overkill
- * for now.
- */
- mutex_lock(&info->property_lock);
- idx = info->recent_idx;
- count = info->property_count;
- while (count) {
- --count;
- /* stop searching on match */
- if (info->property_array[idx] == property) {
- info->recent_idx = idx;
- rc = idx;
- break;
- }
- /* move to next valid index */
- if (--idx < 0)
- idx = info->property_count - 1;
- }
- mutex_unlock(&info->property_lock);
- }
- return rc;
- }
- int msm_property_set_dirty(struct msm_property_info *info,
- struct msm_property_state *property_state,
- int property_idx)
- {
- if (!info || !property_state || !property_state->values) {
- DRM_ERROR("invalid argument(s)\n");
- return -EINVAL;
- }
- mutex_lock(&info->property_lock);
- _msm_property_set_dirty_no_lock(info, property_state, property_idx);
- mutex_unlock(&info->property_lock);
- return 0;
- }
- int msm_property_atomic_set(struct msm_property_info *info,
- struct msm_property_state *property_state,
- struct drm_property *property, uint64_t val)
- {
- struct drm_property_blob *blob;
- int property_idx, rc = -EINVAL;
- if (!info || !property_state) {
- DRM_ERROR("invalid argument(s)\n");
- return -EINVAL;
- }
- property_idx = msm_property_index(info, property);
- if ((property_idx == -EINVAL) || !property_state->values) {
- DRM_ERROR("invalid argument(s)\n");
- } else {
- /* extra handling for incoming properties */
- mutex_lock(&info->property_lock);
- if ((property->flags & DRM_MODE_PROP_BLOB) &&
- (property_idx < info->blob_count)) {
- /* need to clear previous ref */
- if (property_state->values[property_idx].blob)
- drm_property_blob_put(
- property_state->values[
- property_idx].blob);
- /* DRM lookup also takes a reference */
- blob = drm_property_lookup_blob(info->dev,
- (uint32_t)val);
- if (val && !blob) {
- DRM_ERROR("prop %d blob id 0x%llx not found\n",
- property_idx, val);
- val = 0;
- } else {
- if (blob) {
- DBG("Blob %u saved", blob->base.id);
- val = blob->base.id;
- }
- /* save the new blob */
- property_state->values[property_idx].blob =
- blob;
- }
- }
- /* update value and flag as dirty */
- if (property_state->values[property_idx].value != val ||
- info->property_data[property_idx].force_dirty) {
- property_state->values[property_idx].value = val;
- _msm_property_set_dirty_no_lock(info, property_state,
- property_idx);
- DBG("%s - %lld", property->name, val);
- }
- mutex_unlock(&info->property_lock);
- rc = 0;
- }
- return rc;
- }
- int msm_property_atomic_get(struct msm_property_info *info,
- struct msm_property_state *property_state,
- struct drm_property *property, uint64_t *val)
- {
- int property_idx, rc = -EINVAL;
- property_idx = msm_property_index(info, property);
- if (!info || (property_idx == -EINVAL) ||
- !property_state->values || !val) {
- DRM_DEBUG("Invalid argument(s)\n");
- } else {
- mutex_lock(&info->property_lock);
- *val = property_state->values[property_idx].value;
- mutex_unlock(&info->property_lock);
- rc = 0;
- }
- return rc;
- }
- void *msm_property_alloc_state(struct msm_property_info *info)
- {
- void *state = NULL;
- if (!info) {
- DRM_ERROR("invalid property info\n");
- return NULL;
- }
- mutex_lock(&info->property_lock);
- if (info->state_cache_size)
- state = info->state_cache[--(info->state_cache_size)];
- mutex_unlock(&info->property_lock);
- if (!state && info->state_size)
- state = kzalloc(info->state_size, GFP_KERNEL);
- if (!state)
- DRM_ERROR("failed to allocate state\n");
- return state;
- }
- /**
- * _msm_property_free_state - helper function for freeing local state objects
- * @info: Pointer to property info container struct
- * @st: Pointer to state object
- */
- static void _msm_property_free_state(struct msm_property_info *info, void *st)
- {
- if (!info || !st)
- return;
- mutex_lock(&info->property_lock);
- if (info->state_cache_size < MSM_PROP_STATE_CACHE_SIZE)
- info->state_cache[(info->state_cache_size)++] = st;
- else
- kfree(st);
- mutex_unlock(&info->property_lock);
- }
- void msm_property_reset_state(struct msm_property_info *info, void *state,
- struct msm_property_state *property_state,
- struct msm_property_value *property_values)
- {
- uint32_t i;
- if (!info) {
- DRM_ERROR("invalid property info\n");
- return;
- }
- if (state)
- memset(state, 0, info->state_size);
- if (property_state) {
- property_state->property_count = info->property_count;
- property_state->values = property_values;
- INIT_LIST_HEAD(&property_state->dirty_list);
- }
- /*
- * Assign default property values. This helper is mostly used
- * to initialize newly created state objects.
- */
- if (property_values)
- for (i = 0; i < info->property_count; ++i) {
- property_values[i].value =
- info->property_data[i].default_value;
- property_values[i].blob = NULL;
- INIT_LIST_HEAD(&property_values[i].dirty_node);
- }
- }
- void msm_property_duplicate_state(struct msm_property_info *info,
- void *old_state, void *state,
- struct msm_property_state *property_state,
- struct msm_property_value *property_values)
- {
- uint32_t i;
- if (!info || !old_state || !state) {
- DRM_ERROR("invalid argument(s)\n");
- return;
- }
- memcpy(state, old_state, info->state_size);
- if (!property_state)
- return;
- INIT_LIST_HEAD(&property_state->dirty_list);
- property_state->values = property_values;
- if (property_state->values)
- /* add ref count for blobs and initialize dirty nodes */
- for (i = 0; i < info->property_count; ++i) {
- if (property_state->values[i].blob)
- drm_property_blob_get(
- property_state->values[i].blob);
- INIT_LIST_HEAD(&property_state->values[i].dirty_node);
- }
- }
- void msm_property_destroy_state(struct msm_property_info *info, void *state,
- struct msm_property_state *property_state)
- {
- uint32_t i;
- if (!info || !state) {
- DRM_ERROR("invalid argument(s)\n");
- return;
- }
- if (property_state && property_state->values) {
- /* remove ref count for blobs */
- for (i = 0; i < info->property_count; ++i)
- if (property_state->values[i].blob) {
- drm_property_blob_put(
- property_state->values[i].blob);
- property_state->values[i].blob = NULL;
- }
- }
- _msm_property_free_state(info, state);
- }
- void *msm_property_get_blob(struct msm_property_info *info,
- struct msm_property_state *property_state,
- size_t *byte_len,
- uint32_t property_idx)
- {
- struct drm_property_blob *blob;
- size_t len = 0;
- void *rc = 0;
- if (!info || !property_state || !property_state->values ||
- (property_idx >= info->blob_count)) {
- DRM_ERROR("invalid argument(s)\n");
- } else {
- blob = property_state->values[property_idx].blob;
- if (blob) {
- len = blob->length;
- rc = blob->data;
- }
- }
- if (byte_len)
- *byte_len = len;
- return rc;
- }
- int msm_property_set_blob(struct msm_property_info *info,
- struct drm_property_blob **blob_reference,
- void *blob_data,
- size_t byte_len,
- uint32_t property_idx)
- {
- struct drm_property_blob *blob = NULL;
- int rc = -EINVAL;
- if (!info || !blob_reference || (property_idx >= info->blob_count)) {
- DRM_ERROR("invalid argument(s)\n");
- } else {
- /* create blob */
- if (blob_data && byte_len) {
- blob = drm_property_create_blob(info->dev,
- byte_len,
- blob_data);
- if (IS_ERR_OR_NULL(blob)) {
- rc = PTR_ERR(blob);
- DRM_ERROR("failed to create blob, %d\n", rc);
- goto exit;
- }
- }
- /* update drm object */
- rc = drm_object_property_set_value(info->base,
- info->property_array[property_idx],
- blob ? blob->base.id : 0);
- if (rc) {
- DRM_ERROR("failed to set blob to property\n");
- if (blob)
- drm_property_blob_put(blob);
- goto exit;
- }
- /* update local reference */
- if (*blob_reference)
- drm_property_blob_put(*blob_reference);
- *blob_reference = blob;
- }
- exit:
- return rc;
- }
- int msm_property_set_property(struct msm_property_info *info,
- struct msm_property_state *property_state,
- uint32_t property_idx,
- uint64_t val)
- {
- int rc = -EINVAL;
- if (!info || (property_idx >= info->property_count) ||
- property_idx < info->blob_count ||
- !property_state || !property_state->values) {
- DRM_ERROR("invalid argument(s)\n");
- } else {
- struct drm_property *drm_prop;
- mutex_lock(&info->property_lock);
- /* update cached value */
- property_state->values[property_idx].value = val;
- /* update the new default value for immutables */
- drm_prop = info->property_array[property_idx];
- if (drm_prop->flags & DRM_MODE_PROP_IMMUTABLE)
- info->property_data[property_idx].default_value = val;
- mutex_unlock(&info->property_lock);
- /* update drm object */
- rc = drm_object_property_set_value(info->base, drm_prop, val);
- if (rc)
- DRM_ERROR("failed set property value, idx %d rc %d\n",
- property_idx, rc);
- }
- return rc;
- }
|