123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2017-2018, 2020 The Linux Foundation. All rights reserved.
- */
- #include "cam_sync_util.h"
- #include "cam_req_mgr_workq.h"
- int cam_sync_util_find_and_set_empty_row(struct sync_device *sync_dev,
- long *idx)
- {
- int rc = 0;
- mutex_lock(&sync_dev->table_lock);
- *idx = find_first_zero_bit(sync_dev->bitmap, CAM_SYNC_MAX_OBJS);
- if (*idx < CAM_SYNC_MAX_OBJS)
- set_bit(*idx, sync_dev->bitmap);
- else
- rc = -1;
- mutex_unlock(&sync_dev->table_lock);
- return rc;
- }
- int cam_sync_init_row(struct sync_table_row *table,
- uint32_t idx, const char *name, uint32_t type)
- {
- struct sync_table_row *row = table + idx;
- if (!table || idx <= 0 || idx >= CAM_SYNC_MAX_OBJS)
- return -EINVAL;
- memset(row, 0, sizeof(*row));
- if (name)
- strlcpy(row->name, name, SYNC_DEBUG_NAME_LEN);
- INIT_LIST_HEAD(&row->parents_list);
- INIT_LIST_HEAD(&row->children_list);
- row->type = type;
- row->sync_id = idx;
- row->state = CAM_SYNC_STATE_ACTIVE;
- row->remaining = 0;
- atomic_set(&row->ref_cnt, 0);
- init_completion(&row->signaled);
- INIT_LIST_HEAD(&row->callback_list);
- INIT_LIST_HEAD(&row->user_payload_list);
- CAM_DBG(CAM_SYNC,
- "row name:%s sync_id:%i [idx:%u] row_state:%u ",
- row->name, row->sync_id, idx, row->state);
- return 0;
- }
- int cam_sync_init_group_object(struct sync_table_row *table,
- uint32_t idx,
- uint32_t *sync_objs,
- uint32_t num_objs)
- {
- int i, rc = 0;
- struct sync_child_info *child_info;
- struct sync_parent_info *parent_info;
- struct sync_table_row *row = table + idx;
- struct sync_table_row *child_row = NULL;
- cam_sync_init_row(table, idx, "merged_fence", CAM_SYNC_TYPE_GROUP);
- /*
- * While traversing for children, parent's row list is updated with
- * child info and each child's row is updated with parent info.
- * If any child state is ERROR or SUCCESS, it will not be added to list.
- */
- for (i = 0; i < num_objs; i++) {
- child_row = table + sync_objs[i];
- spin_lock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
- /* validate child */
- if ((child_row->type == CAM_SYNC_TYPE_GROUP) ||
- (child_row->state == CAM_SYNC_STATE_INVALID)) {
- spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
- CAM_ERR(CAM_SYNC,
- "Invalid child fence:%i state:%u type:%u",
- child_row->sync_id, child_row->state,
- child_row->type);
- rc = -EINVAL;
- goto clean_children_info;
- }
- /* check for child's state */
- if ((child_row->state == CAM_SYNC_STATE_SIGNALED_ERROR) ||
- (child_row->state == CAM_SYNC_STATE_SIGNALED_CANCEL)) {
- row->state = child_row->state;
- spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
- continue;
- }
- if (child_row->state != CAM_SYNC_STATE_ACTIVE) {
- spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
- continue;
- }
- row->remaining++;
- /* Add child info */
- child_info = kzalloc(sizeof(*child_info), GFP_ATOMIC);
- if (!child_info) {
- spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
- rc = -ENOMEM;
- goto clean_children_info;
- }
- child_info->sync_id = sync_objs[i];
- list_add_tail(&child_info->list, &row->children_list);
- /* Add parent info */
- parent_info = kzalloc(sizeof(*parent_info), GFP_ATOMIC);
- if (!parent_info) {
- spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
- rc = -ENOMEM;
- goto clean_children_info;
- }
- parent_info->sync_id = idx;
- list_add_tail(&parent_info->list, &child_row->parents_list);
- spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
- }
- if (!row->remaining) {
- if ((row->state != CAM_SYNC_STATE_SIGNALED_ERROR) &&
- (row->state != CAM_SYNC_STATE_SIGNALED_CANCEL))
- row->state = CAM_SYNC_STATE_SIGNALED_SUCCESS;
- complete_all(&row->signaled);
- }
- return 0;
- clean_children_info:
- row->state = CAM_SYNC_STATE_INVALID;
- for (i = i-1; i >= 0; i--) {
- spin_lock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
- child_row = table + sync_objs[i];
- cam_sync_util_cleanup_parents_list(child_row,
- SYNC_LIST_CLEAN_ONE, idx);
- spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
- }
- cam_sync_util_cleanup_children_list(row, SYNC_LIST_CLEAN_ALL, 0);
- return rc;
- }
- int cam_sync_deinit_object(struct sync_table_row *table, uint32_t idx)
- {
- struct sync_table_row *row = table + idx;
- struct sync_child_info *child_info, *temp_child;
- struct sync_callback_info *sync_cb, *temp_cb;
- struct sync_parent_info *parent_info, *temp_parent;
- struct sync_user_payload *upayload_info, *temp_upayload;
- struct sync_table_row *child_row = NULL, *parent_row = NULL;
- struct list_head temp_child_list, temp_parent_list;
- if (!table || idx <= 0 || idx >= CAM_SYNC_MAX_OBJS)
- return -EINVAL;
- CAM_DBG(CAM_SYNC,
- "row name:%s sync_id:%i [idx:%u] row_state:%u",
- row->name, row->sync_id, idx, row->state);
- spin_lock_bh(&sync_dev->row_spinlocks[idx]);
- if (row->state == CAM_SYNC_STATE_INVALID) {
- spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
- CAM_ERR(CAM_SYNC,
- "Error: accessing an uninitialized sync obj: idx = %d",
- idx);
- return -EINVAL;
- }
- if (row->state == CAM_SYNC_STATE_ACTIVE)
- CAM_DBG(CAM_SYNC,
- "Destroying an active sync object name:%s id:%i",
- row->name, row->sync_id);
- row->state = CAM_SYNC_STATE_INVALID;
- /* Object's child and parent objects will be added into this list */
- INIT_LIST_HEAD(&temp_child_list);
- INIT_LIST_HEAD(&temp_parent_list);
- list_for_each_entry_safe(child_info, temp_child, &row->children_list,
- list) {
- if (child_info->sync_id <= 0)
- continue;
- list_del_init(&child_info->list);
- list_add_tail(&child_info->list, &temp_child_list);
- }
- list_for_each_entry_safe(parent_info, temp_parent, &row->parents_list,
- list) {
- if (parent_info->sync_id <= 0)
- continue;
- list_del_init(&parent_info->list);
- list_add_tail(&parent_info->list, &temp_parent_list);
- }
- spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
- /* Cleanup the child to parent link from child list */
- while (!list_empty(&temp_child_list)) {
- child_info = list_first_entry(&temp_child_list,
- struct sync_child_info, list);
- child_row = sync_dev->sync_table + child_info->sync_id;
- spin_lock_bh(&sync_dev->row_spinlocks[child_info->sync_id]);
- if (child_row->state == CAM_SYNC_STATE_INVALID) {
- list_del_init(&child_info->list);
- spin_unlock_bh(&sync_dev->row_spinlocks[
- child_info->sync_id]);
- kfree(child_info);
- continue;
- }
- if (child_row->state == CAM_SYNC_STATE_ACTIVE)
- CAM_DBG(CAM_SYNC,
- "Warning: destroying active child sync obj = %d",
- child_info->sync_id);
- cam_sync_util_cleanup_parents_list(child_row,
- SYNC_LIST_CLEAN_ONE, idx);
- list_del_init(&child_info->list);
- spin_unlock_bh(&sync_dev->row_spinlocks[child_info->sync_id]);
- kfree(child_info);
- }
- /* Cleanup the parent to child link */
- while (!list_empty(&temp_parent_list)) {
- parent_info = list_first_entry(&temp_parent_list,
- struct sync_parent_info, list);
- parent_row = sync_dev->sync_table + parent_info->sync_id;
- spin_lock_bh(&sync_dev->row_spinlocks[parent_info->sync_id]);
- if (parent_row->state == CAM_SYNC_STATE_INVALID) {
- list_del_init(&parent_info->list);
- spin_unlock_bh(&sync_dev->row_spinlocks[
- parent_info->sync_id]);
- kfree(parent_info);
- continue;
- }
- if (parent_row->state == CAM_SYNC_STATE_ACTIVE)
- CAM_DBG(CAM_SYNC,
- "Warning: destroying active parent sync obj = %d",
- parent_info->sync_id);
- cam_sync_util_cleanup_children_list(parent_row,
- SYNC_LIST_CLEAN_ONE, idx);
- list_del_init(&parent_info->list);
- spin_unlock_bh(&sync_dev->row_spinlocks[parent_info->sync_id]);
- kfree(parent_info);
- }
- spin_lock_bh(&sync_dev->row_spinlocks[idx]);
- list_for_each_entry_safe(upayload_info, temp_upayload,
- &row->user_payload_list, list) {
- list_del_init(&upayload_info->list);
- kfree(upayload_info);
- }
- list_for_each_entry_safe(sync_cb, temp_cb,
- &row->callback_list, list) {
- list_del_init(&sync_cb->list);
- kfree(sync_cb);
- }
- memset(row, 0, sizeof(*row));
- clear_bit(idx, sync_dev->bitmap);
- INIT_LIST_HEAD(&row->callback_list);
- INIT_LIST_HEAD(&row->parents_list);
- INIT_LIST_HEAD(&row->children_list);
- INIT_LIST_HEAD(&row->user_payload_list);
- spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
- CAM_DBG(CAM_SYNC, "Destroying sync obj:%d successful", idx);
- return 0;
- }
- void cam_sync_util_cb_dispatch(struct work_struct *cb_dispatch_work)
- {
- struct sync_callback_info *cb_info = container_of(cb_dispatch_work,
- struct sync_callback_info,
- cb_dispatch_work);
- sync_callback sync_data = cb_info->callback_func;
- cam_req_mgr_thread_switch_delay_detect(
- cb_info->workq_scheduled_ts);
- sync_data(cb_info->sync_obj, cb_info->status, cb_info->cb_data);
- kfree(cb_info);
- }
- void cam_sync_util_dispatch_signaled_cb(int32_t sync_obj,
- uint32_t status, uint32_t event_cause)
- {
- struct sync_callback_info *sync_cb;
- struct sync_user_payload *payload_info;
- struct sync_callback_info *temp_sync_cb;
- struct sync_table_row *signalable_row;
- struct sync_user_payload *temp_payload_info;
- signalable_row = sync_dev->sync_table + sync_obj;
- if (signalable_row->state == CAM_SYNC_STATE_INVALID) {
- CAM_DBG(CAM_SYNC,
- "Accessing invalid sync object:%i", sync_obj);
- return;
- }
- /* Dispatch kernel callbacks if any were registered earlier */
- list_for_each_entry_safe(sync_cb,
- temp_sync_cb, &signalable_row->callback_list, list) {
- sync_cb->status = status;
- list_del_init(&sync_cb->list);
- queue_work(sync_dev->work_queue,
- &sync_cb->cb_dispatch_work);
- }
- /* Dispatch user payloads if any were registered earlier */
- list_for_each_entry_safe(payload_info, temp_payload_info,
- &signalable_row->user_payload_list, list) {
- spin_lock_bh(&sync_dev->cam_sync_eventq_lock);
- if (!sync_dev->cam_sync_eventq) {
- spin_unlock_bh(
- &sync_dev->cam_sync_eventq_lock);
- break;
- }
- spin_unlock_bh(&sync_dev->cam_sync_eventq_lock);
- cam_sync_util_send_v4l2_event(
- CAM_SYNC_V4L_EVENT_ID_CB_TRIG,
- sync_obj,
- status,
- payload_info->payload_data,
- CAM_SYNC_PAYLOAD_WORDS * sizeof(__u64),
- event_cause);
- list_del_init(&payload_info->list);
- /*
- * We can free the list node here because
- * sending V4L event will make a deep copy
- * anyway
- */
- kfree(payload_info);
- }
- /*
- * This needs to be done because we want to unblock anyone
- * who might be blocked and waiting on this sync object
- */
- complete_all(&signalable_row->signaled);
- }
- void cam_sync_util_send_v4l2_event(uint32_t id,
- uint32_t sync_obj,
- int status,
- void *payload,
- int len, uint32_t event_cause)
- {
- struct v4l2_event event;
- __u64 *payload_data = NULL;
- if (sync_dev->version == CAM_SYNC_V4L_EVENT_V2) {
- struct cam_sync_ev_header_v2 *ev_header = NULL;
- event.id = id;
- event.type = CAM_SYNC_V4L_EVENT_V2;
- ev_header = CAM_SYNC_GET_HEADER_PTR_V2(event);
- ev_header->sync_obj = sync_obj;
- ev_header->status = status;
- ev_header->version = sync_dev->version;
- ev_header->evt_param[CAM_SYNC_EVENT_REASON_CODE_INDEX] =
- event_cause;
- payload_data = CAM_SYNC_GET_PAYLOAD_PTR_V2(event, __u64);
- } else {
- struct cam_sync_ev_header *ev_header = NULL;
- event.id = id;
- event.type = CAM_SYNC_V4L_EVENT;
- ev_header = CAM_SYNC_GET_HEADER_PTR(event);
- ev_header->sync_obj = sync_obj;
- ev_header->status = status;
- payload_data = CAM_SYNC_GET_PAYLOAD_PTR(event, __u64);
- }
- memcpy(payload_data, payload, len);
- v4l2_event_queue(sync_dev->vdev, &event);
- CAM_DBG(CAM_SYNC, "send v4l2 event version %d for sync_obj :%d",
- sync_dev->version,
- sync_obj);
- }
- int cam_sync_util_update_parent_state(struct sync_table_row *parent_row,
- int new_state)
- {
- int rc = 0;
- switch (parent_row->state) {
- case CAM_SYNC_STATE_ACTIVE:
- case CAM_SYNC_STATE_SIGNALED_SUCCESS:
- parent_row->state = new_state;
- break;
- case CAM_SYNC_STATE_SIGNALED_ERROR:
- case CAM_SYNC_STATE_SIGNALED_CANCEL:
- break;
- case CAM_SYNC_STATE_INVALID:
- default:
- rc = -EINVAL;
- break;
- }
- return rc;
- }
- void cam_sync_util_cleanup_children_list(struct sync_table_row *row,
- uint32_t list_clean_type, uint32_t sync_obj)
- {
- struct sync_child_info *child_info = NULL;
- struct sync_child_info *temp_child_info = NULL;
- uint32_t curr_sync_obj;
- list_for_each_entry_safe(child_info,
- temp_child_info, &row->children_list, list) {
- if ((list_clean_type == SYNC_LIST_CLEAN_ONE) &&
- (child_info->sync_id != sync_obj))
- continue;
- curr_sync_obj = child_info->sync_id;
- list_del_init(&child_info->list);
- kfree(child_info);
- if ((list_clean_type == SYNC_LIST_CLEAN_ONE) &&
- (curr_sync_obj == sync_obj))
- break;
- }
- }
- void cam_sync_util_cleanup_parents_list(struct sync_table_row *row,
- uint32_t list_clean_type, uint32_t sync_obj)
- {
- struct sync_parent_info *parent_info = NULL;
- struct sync_parent_info *temp_parent_info = NULL;
- uint32_t curr_sync_obj;
- list_for_each_entry_safe(parent_info,
- temp_parent_info, &row->parents_list, list) {
- if ((list_clean_type == SYNC_LIST_CLEAN_ONE) &&
- (parent_info->sync_id != sync_obj))
- continue;
- curr_sync_obj = parent_info->sync_id;
- list_del_init(&parent_info->list);
- kfree(parent_info);
- if ((list_clean_type == SYNC_LIST_CLEAN_ONE) &&
- (curr_sync_obj == sync_obj))
- break;
- }
- }
|