mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-08 12:58:12 -04:00
Merge "msm: synx: Porting synx driver to 5.x kernel"
This commit is contained in:
commit
c1640b59aa
13 changed files with 4041 additions and 0 deletions
|
|
@ -686,3 +686,5 @@ config VIDEO_RCAR_DRIF
|
|||
will be called rcar_drif.
|
||||
|
||||
endif # SDR_PLATFORM_DRIVERS
|
||||
|
||||
source "drivers/media/platform/msm/Kconfig"
|
||||
|
|
|
|||
|
|
@ -101,3 +101,5 @@ obj-y += meson/
|
|||
obj-y += cros-ec-cec/
|
||||
|
||||
obj-y += sunxi/
|
||||
|
||||
obj-y += msm/
|
||||
|
|
|
|||
3
drivers/media/platform/msm/Kconfig
Normal file
3
drivers/media/platform/msm/Kconfig
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
source "drivers/media/platform/msm/synx/Kconfig"
|
||||
3
drivers/media/platform/msm/Makefile
Normal file
3
drivers/media/platform/msm/Makefile
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
obj-$(CONFIG_MSM_GLOBAL_SYNX) += synx/
|
||||
11
drivers/media/platform/msm/synx/Kconfig
Normal file
11
drivers/media/platform/msm/synx/Kconfig
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
menuconfig MSM_GLOBAL_SYNX
|
||||
tristate "Qualcomm Technologies, Inc. Global Synchronization Framework"
|
||||
depends on ARCH_QCOM
|
||||
help
|
||||
Say M here to enable global synchronization module for
|
||||
Qualcomm Technologies, Inc. chipsets.
|
||||
Enabling this adds support for global synchronization across
|
||||
heterogeneous cores.
|
||||
|
||||
5
drivers/media/platform/msm/synx/Makefile
Normal file
5
drivers/media/platform/msm/synx/Makefile
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
obj-$(CONFIG_MSM_GLOBAL_SYNX) += synx-driver.o
|
||||
|
||||
synx-driver-y := synx.o synx_util.o synx_debugfs.o
|
||||
1771
drivers/media/platform/msm/synx/synx.c
Normal file
1771
drivers/media/platform/msm/synx/synx.c
Normal file
File diff suppressed because it is too large
Load diff
344
drivers/media/platform/msm/synx/synx_api.h
Normal file
344
drivers/media/platform/msm/synx/synx_api.h
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __SYNX_API_H__
|
||||
#define __SYNX_API_H__
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <uapi/media/synx.h>
|
||||
|
||||
typedef void (*synx_callback)(s32 sync_obj, int status, void *data);
|
||||
|
||||
/**
|
||||
* struct bind_operations - Function pointers that need to be defined
|
||||
* to achieve bind functionality for external fence with synx obj
|
||||
*
|
||||
* @register_callback : Function to register with external sync object
|
||||
* @deregister_callback : Function to deregister with external sync object
|
||||
* @enable_signaling : Function to enable the signaling on the external
|
||||
* sync object (optional)
|
||||
* @signal : Function to signal the external sync object
|
||||
*/
|
||||
struct bind_operations {
|
||||
int (*register_callback)(synx_callback cb_func,
|
||||
void *userdata, s32 sync_obj);
|
||||
int (*deregister_callback)(synx_callback cb_func,
|
||||
void *userdata, s32 sync_obj);
|
||||
int (*enable_signaling)(s32 sync_obj);
|
||||
int (*signal)(s32 sync_obj, u32 status);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct synx_register_params - External registration parameters
|
||||
*
|
||||
* @ops : Bind operations struct
|
||||
* @name : External client name
|
||||
* Only first 64 bytes are accepted, rest will be ignored
|
||||
* @type : Synx external client type
|
||||
*/
|
||||
struct synx_register_params {
|
||||
struct bind_operations ops;
|
||||
char *name;
|
||||
u32 type;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct synx_session - Client session handle
|
||||
*
|
||||
* @client_id : Client id
|
||||
*/
|
||||
struct synx_session {
|
||||
u32 client_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct synx_initialization_params - Session params
|
||||
*
|
||||
* @name : Client session name
|
||||
* Only first 64 bytes are accepted, rest will be ignored
|
||||
*/
|
||||
struct synx_initialization_params {
|
||||
const char *name;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct synx_create_params - Synx creation parameters
|
||||
*
|
||||
* @h_synx : Pointer to synx object handle (filled by function)
|
||||
* @name : Optional parameter associating a name with the synx
|
||||
* object for debug purposes
|
||||
* Only first 64 bytes are accepted,
|
||||
* rest will be ignored
|
||||
*/
|
||||
struct synx_create_params {
|
||||
s32 *h_synx;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct synx_export_params - Synx export parameters
|
||||
*
|
||||
* @h_synx : Synx object handle to export
|
||||
* @secure_key : Pointer to Key generated for authentication
|
||||
* (filled by the function)
|
||||
*/
|
||||
struct synx_export_params {
|
||||
s32 h_synx;
|
||||
u32 *secure_key;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct synx_import_params - Synx import parameters
|
||||
*
|
||||
* @h_synx : Synx object handle to import
|
||||
* @secure_key : Key to verify authenticity
|
||||
* @new_h_synx : Pointer to newly created synx object
|
||||
* (filled by the function)
|
||||
* This handle should be used by importing
|
||||
* process for all synx api operations
|
||||
*/
|
||||
struct synx_import_params {
|
||||
s32 h_synx;
|
||||
u32 secure_key;
|
||||
s32 *new_h_synx;
|
||||
};
|
||||
|
||||
/* Kernel APIs */
|
||||
|
||||
/* @brief: Register operations for external synchronization
|
||||
*
|
||||
* Register with synx for enabling external synchronization through bind
|
||||
*
|
||||
* @param params : Pointer to register params
|
||||
*
|
||||
* @return Status of operation. Zero in case of success.
|
||||
* -EINVAL will be returned if params are invalid.
|
||||
* -ENOMEM will be returned if bind ops cannot be registered due to
|
||||
* insufficient memory.
|
||||
* -EALREADY will be returned if type already in use
|
||||
*/
|
||||
int synx_register_ops(const struct synx_register_params *params);
|
||||
|
||||
/**
|
||||
* @brief: De-register external synchronization operations
|
||||
*
|
||||
* @param params : Pointer to register params
|
||||
*
|
||||
* @return Status of operation. Zero in case of success.
|
||||
* -EINVAL will be returned if record not found.
|
||||
*/
|
||||
int synx_deregister_ops(const struct synx_register_params *params);
|
||||
|
||||
/**
|
||||
* @brief: Initializes a new client session
|
||||
*
|
||||
* Create a new client and provides access to the synx framework
|
||||
*
|
||||
* @param session_id : Pointer to client session id (filled by function)
|
||||
* @param params : Pointer to session init params
|
||||
*
|
||||
* @return Status of operation. Zero in case of success.
|
||||
* -EINVAL will be returned if params is not valid.
|
||||
* -ENOMEM will be returned if the kernel can't allocate space for
|
||||
* new client
|
||||
*/
|
||||
int synx_initialize(struct synx_session *session_id,
|
||||
struct synx_initialization_params *params);
|
||||
|
||||
/**
|
||||
* @brief: Destroys the client session
|
||||
*
|
||||
* The client session is destroyed and all synx objects owned
|
||||
* by the client are cleaned up.
|
||||
*
|
||||
* @param session_id : Client session id
|
||||
*
|
||||
* @return Status of operation. Zero in case of success.
|
||||
*/
|
||||
int synx_uninitialize(struct synx_session session_id);
|
||||
|
||||
/**
|
||||
* @brief: Creates a synx object
|
||||
*
|
||||
* Creates a new synx obj and returns the handle to client
|
||||
*
|
||||
* @param session_id : Client session id
|
||||
* @param params : Pointer to create params
|
||||
*
|
||||
* @return Status of operation. Zero in case of success.
|
||||
* -EINVAL will be returned if params were invalid.
|
||||
* -ENOMEM will be returned if the kernel can't allocate space for
|
||||
* synx object.
|
||||
*/
|
||||
int synx_create(struct synx_session session_id,
|
||||
struct synx_create_params *params);
|
||||
|
||||
/**
|
||||
* @brief: Registers a callback with a synx object
|
||||
*
|
||||
* @param session_id : Client session id
|
||||
* @param h_synx : Synx object handle
|
||||
* @param cb_func : Pointer to callback to be registered
|
||||
* @param userdata : Opaque pointer passed back with callback
|
||||
*
|
||||
* @return Status of operation. Zero in case of success.
|
||||
* -EINVAL will be returned if userdata is invalid.
|
||||
* -ENOMEM will be returned if cb_func is invalid.
|
||||
*
|
||||
*/
|
||||
int synx_register_callback(struct synx_session session_id,
|
||||
s32 h_synx, synx_callback cb_func, void *userdata);
|
||||
|
||||
/**
|
||||
* @brief: De-registers a callback with a synx object
|
||||
*
|
||||
* @param session_id : Client session id
|
||||
* @param h_synx : Synx object handle
|
||||
* @param cb_func : Pointer to callback to be de-registered
|
||||
* @param userdata : Opaque pointer passed back with callback
|
||||
* @param cancel_cb_func : Pointer to callback to ack de-registration
|
||||
*
|
||||
* @return Status of operation. Zero in case of success.
|
||||
* -EINVAL will be returned if userdata is invalid.
|
||||
* -ENOMEM will be returned if cb_func is invalid.
|
||||
*/
|
||||
int synx_deregister_callback(struct synx_session session_id,
|
||||
s32 h_synx,
|
||||
synx_callback cb_func,
|
||||
void *userdata,
|
||||
synx_callback cancel_cb_func);
|
||||
|
||||
/**
|
||||
* @brief: Signals a synx object with the status argument.
|
||||
*
|
||||
* This function will signal the synx object referenced by h_synx
|
||||
* and invoke any external binding synx objs.
|
||||
* The status parameter will indicate whether the entity
|
||||
* performing the signaling wants to convey an error case or a success case.
|
||||
*
|
||||
* @param session_id : Client session id
|
||||
* @param h_synx : Synx object handle
|
||||
* @param status : Status of signaling.
|
||||
* Value : SYNX_STATE_SIGNALED_SUCCESS or
|
||||
* SYNX_STATE_SIGNALED_ERROR.
|
||||
*
|
||||
* @return Status of operation. Negative in case of error. Zero otherwise.
|
||||
*/
|
||||
int synx_signal(struct synx_session session_id, s32 h_synx, u32 status);
|
||||
|
||||
/**
|
||||
* @brief: Merges multiple synx objects
|
||||
*
|
||||
* This function will merge multiple synx objects into a synx group.
|
||||
*
|
||||
* @param session_id : Client session id
|
||||
* @param h_synxs : Pointer to a block of synx handles to be merged
|
||||
* @param num_objs : Number of synx objs in the block
|
||||
* @param h_merged_obj : Merged synx object handle (filled by function)
|
||||
*
|
||||
* @return Status of operation. Negative in case of error. Zero otherwise.
|
||||
*/
|
||||
int synx_merge(struct synx_session session_id,
|
||||
s32 *h_synxs, u32 num_objs, s32 *h_merged_obj);
|
||||
|
||||
/**
|
||||
* @brief: Waits for a synx object synchronously
|
||||
*
|
||||
* Does a wait on the synx object identified by h_synx for a maximum
|
||||
* of timeout_ms milliseconds. Must not be called from interrupt context as
|
||||
* this API can sleep. Should be called from process context only.
|
||||
*
|
||||
* @param session_id : Client session id
|
||||
* @param h_synx : Synx object handle to be waited upon
|
||||
* @param timeout_ms : Timeout in ms
|
||||
*
|
||||
* @return 0 upon success, -EINVAL if synx object is in bad state or arguments
|
||||
* are invalid, -ETIMEDOUT if wait times out.
|
||||
*/
|
||||
int synx_wait(struct synx_session session_id, s32 h_synx, u64 timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief: Binds synx object with external sync
|
||||
*
|
||||
* Binding synx objects with supported external sync object will unify them
|
||||
* in a way that if one of them signals, the other ones is signaled as well
|
||||
*
|
||||
* @param session_id : Client session id
|
||||
* @param h_synx : Synx object handle
|
||||
* @param external_sync : External synx descriptor to bind to object
|
||||
*
|
||||
* @return 0 upon success, -EINVAL if synx object is in bad state or arguments
|
||||
* are invalid.
|
||||
*/
|
||||
int synx_bind(struct synx_session session_id, s32 h_synx,
|
||||
struct synx_external_desc external_sync);
|
||||
|
||||
/**
|
||||
* @brief: Returns the status of the synx object
|
||||
*
|
||||
* @param session_id : Client session id
|
||||
* @param h_synx : Synx object handle
|
||||
*
|
||||
* @return Status of the synx object
|
||||
*/
|
||||
int synx_get_status(struct synx_session session_id, s32 h_synx);
|
||||
|
||||
/**
|
||||
* @brief: Adds to the reference count of a synx object
|
||||
*
|
||||
* When a synx object is created, the refcount will be 1. Every
|
||||
* additional refcount requires separate release of object handle.
|
||||
*
|
||||
* @param session_id : Client session id
|
||||
* @param h_synx : Synx object handle
|
||||
* @param count : Count to add to the refcount
|
||||
*
|
||||
* @return 0 upon success, -EINVAL if synx object is in bad state
|
||||
*/
|
||||
int synx_addrefcount(struct synx_session session_id,
|
||||
s32 h_synx, s32 count);
|
||||
|
||||
/**
|
||||
* @brief: Imports (looks up) synx object from given handle and key
|
||||
*
|
||||
* The given ID should have been exported by another client
|
||||
* and provided with key.
|
||||
*
|
||||
* @param session_id : Client session id
|
||||
* @param params : Pointer to import params
|
||||
*
|
||||
* @return 0 upon success, -EINVAL if synx object is bad state
|
||||
*/
|
||||
int synx_import(struct synx_session session_id,
|
||||
struct synx_import_params *params);
|
||||
|
||||
/**
|
||||
* @brief: Exports a synx object and returns a key
|
||||
*
|
||||
* The synx object handle and given key may be passed to other
|
||||
* clients to be imported.
|
||||
*
|
||||
* @param session_id : Client session id
|
||||
* @param params : Pointer to export params
|
||||
*
|
||||
* @return 0 upon success, -EINVAL if the ID is bad
|
||||
*/
|
||||
int synx_export(struct synx_session session_id,
|
||||
struct synx_export_params *params);
|
||||
|
||||
/**
|
||||
* @brief: Release the synx object
|
||||
|
||||
* Decrements refcount of a synx object by 1, and destroys it
|
||||
* if becomes 0
|
||||
*
|
||||
* @param session_id : Client session id
|
||||
* @param h_synx : Synx object handle to be destroyed
|
||||
*
|
||||
* @return Status of operation. Negative in case of error. Zero otherwise.
|
||||
*/
|
||||
int synx_release(struct synx_session session_id, s32 h_synx);
|
||||
|
||||
#endif /* __SYNX_API_H__ */
|
||||
211
drivers/media/platform/msm/synx/synx_debugfs.c
Normal file
211
drivers/media/platform/msm/synx/synx_debugfs.c
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "synx_api.h"
|
||||
#include "synx_debugfs.h"
|
||||
#include "synx_util.h"
|
||||
|
||||
#define MAX_DBG_BUF_SIZE (36 * SYNX_MAX_OBJS)
|
||||
|
||||
struct dentry *my_direc;
|
||||
static const char delim[] = ",";
|
||||
int columns = NAME_COLUMN | ID_COLUMN |
|
||||
BOUND_COLUMN | STATE_COLUMN | ERROR_CODES;
|
||||
|
||||
void populate_bound_rows(
|
||||
struct synx_coredata *row,
|
||||
char *cur,
|
||||
char *end)
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j = 0; j < row->num_bound_synxs;
|
||||
j++) {
|
||||
cur += scnprintf(cur, end - cur,
|
||||
"\n\tID: %d",
|
||||
row->bound_synxs[j].external_desc.id[0]);
|
||||
}
|
||||
}
|
||||
static ssize_t synx_table_read(struct file *file,
|
||||
char *buf,
|
||||
size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
|
||||
struct synx_device *dev = file->private_data;
|
||||
struct error_node *err_node, *err_node_tmp;
|
||||
struct synx_client *client;
|
||||
struct synx_coredata *row;
|
||||
char *dbuf, *cur, *end;
|
||||
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int status = SYNX_STATE_INVALID;
|
||||
ssize_t len = 0;
|
||||
|
||||
dbuf = kzalloc(MAX_DBG_BUF_SIZE, GFP_KERNEL);
|
||||
if (!dbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
cur = dbuf;
|
||||
end = cur + MAX_DBG_BUF_SIZE;
|
||||
if (columns & NAME_COLUMN)
|
||||
cur += scnprintf(cur, end - cur, "| Name |");
|
||||
if (columns & ID_COLUMN)
|
||||
cur += scnprintf(cur, end - cur, "| ID |");
|
||||
if (columns & BOUND_COLUMN)
|
||||
cur += scnprintf(cur, end - cur, "| Bound |");
|
||||
if (columns & STATE_COLUMN)
|
||||
cur += scnprintf(cur, end - cur, "| Status |");
|
||||
cur += scnprintf(cur, end - cur, "\n");
|
||||
|
||||
for (j = 0; j < SYNX_MAX_CLIENTS; j++) {
|
||||
struct synx_session session;
|
||||
|
||||
session.client_id = j;
|
||||
client = synx_get_client(session);
|
||||
if (!client)
|
||||
continue;
|
||||
|
||||
cur += scnprintf(cur, end - cur,
|
||||
"=============== session %08u ===============\n",
|
||||
client->id);
|
||||
for (i = 0; i < SYNX_MAX_OBJS; i++) {
|
||||
row = synx_util_acquire_object(client, i);
|
||||
if (!row)
|
||||
continue;
|
||||
|
||||
spin_lock_bh(&row->lock);
|
||||
if (columns & NAME_COLUMN)
|
||||
cur += scnprintf(cur, end - cur,
|
||||
"|%10s|", row->name);
|
||||
if (columns & ID_COLUMN)
|
||||
cur += scnprintf(cur, end - cur,
|
||||
"|%10d|", i);
|
||||
if (columns & BOUND_COLUMN)
|
||||
cur += scnprintf(cur, end - cur,
|
||||
"|%11d|", row->num_bound_synxs);
|
||||
if (columns & STATE_COLUMN) {
|
||||
status =
|
||||
synx_util_get_object_status_locked(row);
|
||||
cur += scnprintf(cur, end - cur,
|
||||
"|%10d|", status);
|
||||
}
|
||||
if ((columns & BOUND_COLUMN) &&
|
||||
(row->num_bound_synxs > 0)) {
|
||||
cur += scnprintf(
|
||||
cur, end - cur, "\nBound synx: ");
|
||||
populate_bound_rows(row,
|
||||
cur,
|
||||
end);
|
||||
}
|
||||
spin_unlock_bh(&row->lock);
|
||||
cur += scnprintf(cur, end - cur, "\n");
|
||||
synx_util_release_object(client, i);
|
||||
}
|
||||
cur += scnprintf(cur, end - cur,
|
||||
"\n================================================\n\n");
|
||||
synx_put_client(client);
|
||||
}
|
||||
|
||||
if (columns & ERROR_CODES && !list_empty(
|
||||
&dev->error_list)) {
|
||||
cur += scnprintf(
|
||||
cur, end - cur, "\nError(s): ");
|
||||
|
||||
mutex_lock(&dev->error_lock);
|
||||
list_for_each_entry_safe(
|
||||
err_node, err_node_tmp,
|
||||
&dev->error_list,
|
||||
node) {
|
||||
if (err_node->timestamp != NULL) {
|
||||
cur += scnprintf(cur, end - cur,
|
||||
"\n\tTime: %s - ID: %d - Code: %d",
|
||||
err_node->timestamp,
|
||||
err_node->h_synx,
|
||||
err_node->error_code);
|
||||
}
|
||||
list_del(&err_node->node);
|
||||
kfree(err_node);
|
||||
}
|
||||
mutex_unlock(&dev->error_lock);
|
||||
}
|
||||
|
||||
len = simple_read_from_buffer(buf, count, ppos,
|
||||
dbuf, cur - dbuf);
|
||||
kfree(dbuf);
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t synx_table_write(struct file *file,
|
||||
const char __user *buf,
|
||||
size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
char *ptr;
|
||||
char *kbuffer = kzalloc(48, GFP_KERNEL);
|
||||
int stat = -1;
|
||||
|
||||
if (!kbuffer)
|
||||
return -ENOMEM;
|
||||
stat = copy_from_user(kbuffer, buf, 48);
|
||||
if (stat != 0) {
|
||||
kfree(kbuffer);
|
||||
return -EFAULT;
|
||||
}
|
||||
while ((ptr = strsep(&kbuffer, delim)) != NULL) {
|
||||
ptr += '\0';
|
||||
if (strcmp(ptr, "bound\n") == 0)
|
||||
columns = columns ^ BOUND_COLUMN;
|
||||
else if (strcmp(ptr, "name\n") == 0)
|
||||
columns = columns ^ NAME_COLUMN;
|
||||
else if (strcmp(ptr, "synxid\n") == 0)
|
||||
columns = columns ^ ID_COLUMN;
|
||||
else if (strcmp(ptr, "status\n") == 0)
|
||||
columns = columns ^ STATE_COLUMN;
|
||||
else if (strcmp(ptr, "errors\n") == 0)
|
||||
columns = columns ^ ERROR_CODES;
|
||||
}
|
||||
kfree(kbuffer);
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations synx_table_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = synx_table_read,
|
||||
.write = synx_table_write,
|
||||
.open = simple_open,
|
||||
};
|
||||
|
||||
struct dentry *synx_init_debugfs_dir(struct synx_device *dev)
|
||||
{
|
||||
struct dentry *dir = NULL;
|
||||
|
||||
dir = debugfs_create_dir("synx_debug", NULL);
|
||||
|
||||
if (!dir) {
|
||||
pr_debug("Failed to create debugfs for synx\n");
|
||||
return NULL;
|
||||
}
|
||||
if (!debugfs_create_file("synx_table",
|
||||
0644, dir, dev, &synx_table_fops)) {
|
||||
pr_debug("Failed to create debugfs file for synx\n");
|
||||
return NULL;
|
||||
}
|
||||
mutex_init(&dev->error_lock);
|
||||
INIT_LIST_HEAD(&dev->error_list);
|
||||
return dir;
|
||||
}
|
||||
|
||||
void synx_remove_debugfs_dir(struct synx_device *dev)
|
||||
{
|
||||
debugfs_remove_recursive(dev->debugfs_root);
|
||||
}
|
||||
29
drivers/media/platform/msm/synx/synx_debugfs.h
Normal file
29
drivers/media/platform/msm/synx/synx_debugfs.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "synx_private.h"
|
||||
|
||||
/**
|
||||
* @brief: Initializes debugfs
|
||||
*
|
||||
* @param dev : Pointer to synx device structure
|
||||
*/
|
||||
struct dentry *synx_init_debugfs_dir(struct synx_device *dev);
|
||||
|
||||
/**
|
||||
* @brief: Removes debugfs
|
||||
*
|
||||
* @param dev : Pointer to synx device structure
|
||||
*/
|
||||
void synx_remove_debugfs_dir(struct synx_device *dev);
|
||||
|
||||
#define NAME_COLUMN 0x0001
|
||||
#define ID_COLUMN 0x0002
|
||||
#define BOUND_COLUMN 0x0004
|
||||
#define STATE_COLUMN 0x0008
|
||||
#define ERROR_CODES 0x8000
|
||||
303
drivers/media/platform/msm/synx/synx_private.h
Normal file
303
drivers/media/platform/msm/synx/synx_private.h
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __SYNX_PRIVATE_H__
|
||||
#define __SYNX_PRIVATE_H__
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/dma-fence.h>
|
||||
#include <linux/dma-fence-array.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#define SYNX_NAME "synx"
|
||||
#define SYNX_WORKQUEUE_NAME "hiprio_synx_work_queue"
|
||||
#define SYNX_WORKQUEUE_THREADS 1
|
||||
#define SYNX_MAX_NUM_BINDINGS 8
|
||||
#define SYNX_DEVICE_NAME "synx_device"
|
||||
|
||||
#define SYNX_CLIENT_HANDLE_SHIFT 8
|
||||
#define SYNX_SECURE_KEY_SHIFT 16
|
||||
|
||||
#define MAX_TIMESTAMP_SIZE 32
|
||||
#define SYNX_OBJ_NAME_LEN 64
|
||||
#define SYNX_MAX_CLIENTS (1UL<<SYNX_CLIENT_HANDLE_SHIFT)
|
||||
#define SYNX_MAX_OBJS 1024
|
||||
#define SYNX_MAX_REF_COUNTS 2048
|
||||
#define SYNX_PAYLOAD_WORDS 4
|
||||
|
||||
#define SYNX_SECURE_KEY_SIZE (1UL<<SYNX_SECURE_KEY_SHIFT)
|
||||
#define SYNX_SECURE_KEY_MASK (SYNX_SECURE_KEY_SIZE-1)
|
||||
#define SYNX_CLIENT_HANDLE_MASK (SYNX_MAX_CLIENTS-1)
|
||||
|
||||
#define SYNX_OBJ_TYPE_LOCAL 0x1
|
||||
#define SYNX_OBJ_TYPE_MERGED 0x2
|
||||
|
||||
#define SYNX_STATE_FORCED_RELEASE 5
|
||||
|
||||
/**
|
||||
* struct synx_external_data - data passed over to external sync objects
|
||||
* to pass on callback
|
||||
*
|
||||
* @session_id : Synx client id
|
||||
* @h_synx : Synx object handle
|
||||
*/
|
||||
struct synx_external_data {
|
||||
struct synx_session session_id;
|
||||
s32 h_synx;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct synx_bind_desc - bind payload descriptor
|
||||
*
|
||||
* @external_desc : External bind information
|
||||
* @external_data : Pointer to data passed over
|
||||
*/
|
||||
struct synx_bind_desc {
|
||||
struct synx_external_desc external_desc;
|
||||
struct synx_external_data *external_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct error_node - Single error node related to a table_row
|
||||
*
|
||||
* @timestamp : Time that the error occurred
|
||||
* @client_id : Synx client id
|
||||
* @h_synx : Synx object handle
|
||||
* @error_code : Code related to the error
|
||||
* @node : List member used to append this node to error list
|
||||
*/
|
||||
struct error_node {
|
||||
char timestamp[MAX_TIMESTAMP_SIZE];
|
||||
u32 client_id;
|
||||
s32 h_synx;
|
||||
s32 error_code;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct synx_kernel_payload - Single node of information about a kernel
|
||||
* callback registered on a synx object
|
||||
*
|
||||
* @h_synx : Synx object handle
|
||||
* @data : Callback data, passed by client driver
|
||||
* @cb_func : Callback function, registered by client driver
|
||||
* @cancel_cb_func : Cancellation callback function
|
||||
*/
|
||||
struct synx_kernel_payload {
|
||||
s32 h_synx;
|
||||
void *data;
|
||||
synx_callback cb_func;
|
||||
synx_callback cancel_cb_func;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct synx_user_payload - Single node of information about a callback
|
||||
* registered from user space on a synx object
|
||||
*
|
||||
* @client : Synx client structure
|
||||
* @h_synx : Synx object handle
|
||||
* @status : Synx obj status or callback failure
|
||||
* @data : Payload data, opaque to kernel
|
||||
*/
|
||||
struct synx_user_payload {
|
||||
struct synx_client *client;
|
||||
s32 h_synx;
|
||||
u32 status;
|
||||
u64 data[SYNX_PAYLOAD_WORDS];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct synx_cb_data - Single node of information about callback
|
||||
* registered by client for the synx object
|
||||
*
|
||||
* @session_id : Synx client id
|
||||
* @idx : Client payload table index
|
||||
* @status : Synx obj status or callback failure
|
||||
* @cb_dispatch : Work representing the callback dispatch
|
||||
* @node : List member used to append this node to reg cb list
|
||||
*/
|
||||
struct synx_cb_data {
|
||||
struct synx_session session_id;
|
||||
u32 idx;
|
||||
u32 status;
|
||||
struct work_struct cb_dispatch;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct synx_client_cb - Single node of information about cb payload
|
||||
* registered by client
|
||||
*
|
||||
* @is_valid : True if this is a valid entry
|
||||
* @is_kernel_cb : True for kernel callback, false for user callback
|
||||
* @idx : Index of the client callback table
|
||||
* @kernel_cb : Kernel callback payload
|
||||
* @user_cb : Userspace callback payload
|
||||
* @node : List member used to append this node to event list
|
||||
*/
|
||||
struct synx_client_cb {
|
||||
bool is_valid;
|
||||
u32 idx;
|
||||
struct synx_kernel_payload kernel_cb;
|
||||
struct synx_user_payload user_cb;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct synx_registered_ops - External sync clients registered for bind
|
||||
* operations with synx driver
|
||||
*
|
||||
* @name : Name of the external sync client
|
||||
* @ops : Bind operations struct for the client
|
||||
* @type : External client type
|
||||
* @valid : Validity of the client registered bind ops
|
||||
*/
|
||||
struct synx_registered_ops {
|
||||
char name[SYNX_OBJ_NAME_LEN];
|
||||
struct bind_operations ops;
|
||||
u32 type;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct synx_coredata - Synx object, used for internal book keeping
|
||||
* of all metadata associated with each individual synx object
|
||||
*
|
||||
* @name : Optional string representation of the synx object
|
||||
* @fence : Dma fence backing the synx object
|
||||
* @merged_fence : Pointer to dma fence backing merged synx object
|
||||
* @lock : Spinlock
|
||||
* @type : Synx object type
|
||||
* @num_bound_synxs : Number of external bound synx objects
|
||||
* @bound_synxs : Array of bound external sync objects
|
||||
* @reg_cbs_list : List of all registered callbacks
|
||||
*/
|
||||
struct synx_coredata {
|
||||
char name[SYNX_OBJ_NAME_LEN];
|
||||
struct dma_fence fence;
|
||||
struct dma_fence *merged_fence;
|
||||
spinlock_t lock;
|
||||
u32 type;
|
||||
u32 num_bound_synxs;
|
||||
struct synx_bind_desc bound_synxs[SYNX_MAX_NUM_BINDINGS];
|
||||
struct list_head reg_cbs_list;
|
||||
};
|
||||
|
||||
struct synx_client;
|
||||
struct synx_device;
|
||||
|
||||
/**
|
||||
* struct synx_handle_coredata - Internal struct to manage synx handle to
|
||||
* synx object mapping along with tracking synx object usage by the client
|
||||
* through reference counting
|
||||
*
|
||||
* @client : Pointer to client owning the synx object
|
||||
* @synx_obj : Pointer to synx object
|
||||
* @internal_refcount : References by the client
|
||||
* @import_refcount : References for external clients for import
|
||||
* @id : Synx object handle for the client
|
||||
* @key : Key for import authentication
|
||||
*/
|
||||
struct synx_handle_coredata {
|
||||
struct synx_client *client;
|
||||
struct synx_coredata *synx_obj;
|
||||
struct kref internal_refcount;
|
||||
struct kref import_refcount;
|
||||
u32 id;
|
||||
u16 key;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct synx_client - Internal struct to book keep each client
|
||||
* details
|
||||
*
|
||||
* @device : Pointer to synx device structure
|
||||
* @name : Optional string representation of the client
|
||||
* @id : Unique client session id
|
||||
* @event_q_lock : Mutex for the event queue
|
||||
* @event_q : All the user callback payloads
|
||||
* @event_wq : Wait queue for the polling process
|
||||
* @cb_bitmap : Bitmap representation of all cb table entries
|
||||
* @cb_table : Table of all registered callbacks by client
|
||||
* @bitmap : Bitmap representation of all synx object handles
|
||||
* @synx_table : Table of all synx objects
|
||||
* @synx_table_lock : Mutex array, one for each row in the synx table
|
||||
*/
|
||||
struct synx_client {
|
||||
struct synx_device *device;
|
||||
char name[SYNX_OBJ_NAME_LEN];
|
||||
u32 id;
|
||||
struct mutex event_q_lock;
|
||||
struct list_head event_q;
|
||||
wait_queue_head_t event_wq;
|
||||
DECLARE_BITMAP(cb_bitmap, SYNX_MAX_OBJS);
|
||||
struct synx_client_cb cb_table[SYNX_MAX_OBJS];
|
||||
DECLARE_BITMAP(bitmap, SYNX_MAX_OBJS);
|
||||
struct synx_handle_coredata synx_table[SYNX_MAX_OBJS];
|
||||
struct mutex synx_table_lock[SYNX_MAX_OBJS];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct synx_client_metadata - Internal struct to map client id with
|
||||
* client data along with tracking usage through reference counting
|
||||
*
|
||||
* @client : Pointer to client data
|
||||
* @refcount : Outstanding references to client data
|
||||
*/
|
||||
struct synx_client_metadata {
|
||||
struct synx_client *client;
|
||||
struct kref refcount;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct synx_device - Internal struct to book keep synx driver details
|
||||
*
|
||||
* @cdev : Character device
|
||||
* @dev : Device type
|
||||
* @class : Device class
|
||||
* @table_lock : Mutex used to lock the table
|
||||
* @bitmap : Bitmap representation of all synx clients
|
||||
* @client_table : Table of all synx clients
|
||||
* @work_queue : Work queue used for dispatching callbacks
|
||||
* @dma_context : dma context id
|
||||
* @vtbl_lock : Mutex used to lock the bind table
|
||||
* @bind_vtbl : Table with registered bind ops for external sync
|
||||
* @debugfs_root : Root directory for debugfs
|
||||
* @error_list : List of all errors occurred
|
||||
* @error_lock : Mutex used to modify the error list
|
||||
*/
|
||||
struct synx_device {
|
||||
struct cdev cdev;
|
||||
dev_t dev;
|
||||
struct class *class;
|
||||
struct mutex dev_table_lock;
|
||||
DECLARE_BITMAP(bitmap, SYNX_MAX_CLIENTS);
|
||||
struct synx_client_metadata client_table[SYNX_MAX_CLIENTS];
|
||||
struct workqueue_struct *work_queue;
|
||||
u64 dma_context;
|
||||
struct mutex vtbl_lock;
|
||||
struct synx_registered_ops bind_vtbl[SYNX_MAX_BIND_TYPES];
|
||||
struct dentry *debugfs_root;
|
||||
struct list_head error_list;
|
||||
struct mutex error_lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief: Internal function to signal the synx object
|
||||
*
|
||||
* @param synx_obj : Pointer to the synx object to signal
|
||||
* @param status : Signaling status
|
||||
* @param cb_signal : If signaling invoked from external cb
|
||||
* @param ext_sync_id : External sync id
|
||||
*
|
||||
* @return Status of operation. Negative in case of error. Zero otherwise.
|
||||
*/
|
||||
int synx_signal_core(struct synx_coredata *synx_obj,
|
||||
u32 status,
|
||||
bool cb_signal,
|
||||
s32 ext_sync_id);
|
||||
|
||||
#endif /* __SYNX_PRIVATE_H__ */
|
||||
992
drivers/media/platform/msm/synx/synx_util.c
Normal file
992
drivers/media/platform/msm/synx/synx_util.c
Normal file
|
|
@ -0,0 +1,992 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
#define pr_fmt(fmt) "synx: " fmt
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/random.h>
|
||||
|
||||
#include "synx_api.h"
|
||||
#include "synx_util.h"
|
||||
|
||||
extern void synx_external_callback(s32 sync_obj, int status, void *data);
|
||||
|
||||
bool synx_util_is_valid_bind_type(u32 type)
|
||||
{
|
||||
if (type < SYNX_MAX_BIND_TYPES)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 synx_util_get_object_type(struct synx_coredata *synx_obj)
|
||||
{
|
||||
if (!synx_obj)
|
||||
return 0;
|
||||
|
||||
return synx_obj->type;
|
||||
}
|
||||
|
||||
bool synx_util_is_merged_object(struct synx_coredata *synx_obj)
|
||||
{
|
||||
if (synx_util_get_object_type(synx_obj)
|
||||
& SYNX_OBJ_TYPE_MERGED)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct dma_fence *synx_util_get_fence(struct synx_coredata *synx_obj)
|
||||
{
|
||||
if (!synx_obj)
|
||||
return NULL;
|
||||
|
||||
if (synx_util_is_merged_object(synx_obj))
|
||||
return synx_obj->merged_fence;
|
||||
|
||||
return &synx_obj->fence;
|
||||
}
|
||||
|
||||
void synx_util_get_object(struct synx_coredata *synx_obj)
|
||||
{
|
||||
struct dma_fence *fence;
|
||||
|
||||
if (!synx_obj)
|
||||
return;
|
||||
|
||||
fence = synx_util_get_fence(synx_obj);
|
||||
dma_fence_get(fence);
|
||||
}
|
||||
|
||||
void synx_util_put_object(struct synx_coredata *synx_obj)
|
||||
{
|
||||
struct dma_fence *fence;
|
||||
|
||||
if (!synx_obj)
|
||||
return;
|
||||
|
||||
fence = synx_util_get_fence(synx_obj);
|
||||
dma_fence_put(fence);
|
||||
}
|
||||
|
||||
int synx_util_init_coredata(struct synx_coredata *synx_obj,
|
||||
struct synx_create_params *params,
|
||||
struct dma_fence_ops *ops)
|
||||
{
|
||||
if (!synx_obj || !params || !ops) {
|
||||
pr_err("invalid arguments\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_init(&synx_obj->lock);
|
||||
dma_fence_init(&synx_obj->fence, ops,
|
||||
&synx_obj->lock, synx_dev->dma_context, 1);
|
||||
|
||||
synx_obj->type = SYNX_OBJ_TYPE_LOCAL;
|
||||
INIT_LIST_HEAD(&synx_obj->reg_cbs_list);
|
||||
if (params->name)
|
||||
strlcpy(synx_obj->name, params->name, sizeof(synx_obj->name));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int synx_util_init_group_coredata(struct synx_coredata *synx_obj,
|
||||
struct dma_fence **fences,
|
||||
u32 num_objs)
|
||||
{
|
||||
struct dma_fence_array *array;
|
||||
|
||||
if (!synx_obj)
|
||||
return -EINVAL;
|
||||
|
||||
array = dma_fence_array_create(num_objs, fences,
|
||||
synx_dev->dma_context, 1, false);
|
||||
if (!array)
|
||||
return -EINVAL;
|
||||
|
||||
synx_obj->merged_fence = &array->base;
|
||||
synx_obj->type = SYNX_OBJ_TYPE_LOCAL | SYNX_OBJ_TYPE_MERGED;
|
||||
INIT_LIST_HEAD(&synx_obj->reg_cbs_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void synx_util_object_destroy(struct synx_coredata *synx_obj)
|
||||
{
|
||||
int rc;
|
||||
u32 i;
|
||||
s32 sync_id;
|
||||
u32 type;
|
||||
struct synx_cb_data *synx_cb, *synx_cb_temp;
|
||||
struct synx_bind_desc *bind_desc;
|
||||
struct bind_operations *bind_ops;
|
||||
struct synx_external_data *data;
|
||||
|
||||
/* clear all the undispatched callbacks */
|
||||
list_for_each_entry_safe(synx_cb,
|
||||
synx_cb_temp, &synx_obj->reg_cbs_list, node) {
|
||||
pr_err("[sess: %u] cleaning up callback\n",
|
||||
synx_cb->session_id.client_id);
|
||||
list_del_init(&synx_cb->node);
|
||||
kfree(synx_cb);
|
||||
}
|
||||
|
||||
for (i = 0; i < synx_obj->num_bound_synxs; i++) {
|
||||
bind_desc = &synx_obj->bound_synxs[i];
|
||||
sync_id = bind_desc->external_desc.id[0];
|
||||
type = bind_desc->external_desc.type;
|
||||
data = bind_desc->external_data;
|
||||
bind_ops = synx_util_get_bind_ops(type);
|
||||
rc = bind_ops->deregister_callback(
|
||||
synx_external_callback, data, sync_id);
|
||||
if (rc < 0)
|
||||
pr_err("de-registration fail id: %d, type: %u, err: %d\n",
|
||||
sync_id, type, rc);
|
||||
/*
|
||||
* release the memory allocated for external data.
|
||||
* It is safe to release this memory as external cb
|
||||
* has been already deregistered before this.
|
||||
* even if deregistration fails, it implies the
|
||||
* external fence might have been cleaned up already
|
||||
* or in some bad state. Have to release cb data
|
||||
* at this point or else will lead to memory leak.
|
||||
*/
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
kfree(synx_obj);
|
||||
pr_debug("cleaned up synx object fence\n");
|
||||
}
|
||||
|
||||
int synx_util_init_handle(struct synx_client *client,
|
||||
struct synx_coredata *synx_obj,
|
||||
long *h_synx)
|
||||
{
|
||||
bool bit;
|
||||
long idx;
|
||||
struct synx_handle_coredata *synx_data;
|
||||
|
||||
if (!client || !synx_obj)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
idx = find_first_zero_bit(client->bitmap, SYNX_MAX_OBJS);
|
||||
if (idx >= SYNX_MAX_OBJS) {
|
||||
pr_err("[sess: %u] free index not available\n",
|
||||
client->id);
|
||||
return -ENOMEM;
|
||||
}
|
||||
bit = test_and_set_bit(idx, client->bitmap);
|
||||
} while (bit);
|
||||
|
||||
synx_data = &client->synx_table[idx];
|
||||
synx_data->client = client;
|
||||
synx_data->id = idx;
|
||||
synx_data->synx_obj = synx_obj;
|
||||
kref_init(&synx_data->internal_refcount);
|
||||
|
||||
*h_synx = idx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int synx_util_activate(struct synx_coredata *synx_obj)
|
||||
{
|
||||
struct dma_fence *fence;
|
||||
|
||||
if (!synx_obj)
|
||||
return -EINVAL;
|
||||
|
||||
fence = synx_util_get_fence(synx_obj);
|
||||
/* move synx to ACTIVE state and register cb for merged object */
|
||||
dma_fence_enable_sw_signaling(fence);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 synx_util_get_references(struct synx_coredata *synx_obj)
|
||||
{
|
||||
u32 count = 0;
|
||||
u32 i = 0;
|
||||
struct dma_fence *fence;
|
||||
struct dma_fence_array *array = NULL;
|
||||
|
||||
fence = synx_util_get_fence(synx_obj);
|
||||
/* obtain dma fence reference */
|
||||
if (dma_fence_is_array(fence)) {
|
||||
array = to_dma_fence_array(fence);
|
||||
if (!array)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < array->num_fences; i++)
|
||||
dma_fence_get(array->fences[i]);
|
||||
|
||||
count = array->num_fences;
|
||||
} else {
|
||||
dma_fence_get(fence);
|
||||
count = 1;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void synx_util_put_references(struct synx_coredata *synx_obj)
|
||||
{
|
||||
u32 i = 0;
|
||||
struct dma_fence *fence;
|
||||
struct dma_fence_array *array = NULL;
|
||||
|
||||
fence = synx_util_get_fence(synx_obj);
|
||||
if (dma_fence_is_array(fence)) {
|
||||
array = to_dma_fence_array(fence);
|
||||
if (!array)
|
||||
return;
|
||||
|
||||
for (i = 0; i < array->num_fences; i++)
|
||||
dma_fence_put(array->fences[i]);
|
||||
} else {
|
||||
dma_fence_put(fence);
|
||||
}
|
||||
}
|
||||
|
||||
static u32 synx_util_add_fence(struct synx_coredata *synx_obj,
|
||||
struct dma_fence **fences,
|
||||
u32 idx)
|
||||
{
|
||||
struct dma_fence *fence;
|
||||
struct dma_fence_array *array = NULL;
|
||||
u32 i = 0;
|
||||
|
||||
fence = synx_util_get_fence(synx_obj);
|
||||
if (dma_fence_is_array(fence)) {
|
||||
array = to_dma_fence_array(fence);
|
||||
if (!array)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < array->num_fences; i++)
|
||||
fences[idx+i] = array->fences[i];
|
||||
|
||||
return array->num_fences;
|
||||
}
|
||||
|
||||
fences[idx] = fence;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static u32 synx_util_remove_duplicates(struct dma_fence **arr, u32 num)
|
||||
{
|
||||
int i, j;
|
||||
u32 wr_idx = 1;
|
||||
|
||||
if (!arr) {
|
||||
pr_err("invalid input array\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 1; i < num; i++) {
|
||||
for (j = 0; j < wr_idx ; j++) {
|
||||
if (arr[i] == arr[j]) {
|
||||
/* release reference obtained for duplicate */
|
||||
pr_debug("releasing duplicate reference\n");
|
||||
dma_fence_put(arr[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == wr_idx)
|
||||
arr[wr_idx++] = arr[i];
|
||||
}
|
||||
|
||||
return wr_idx;
|
||||
}
|
||||
|
||||
s32 synx_util_merge_error(struct synx_client *client,
|
||||
s32 *h_synxs,
|
||||
u32 num_objs)
|
||||
{
|
||||
u32 i = 0;
|
||||
struct synx_coredata *synx_obj;
|
||||
|
||||
if (!client || !h_synxs)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < num_objs; i++) {
|
||||
synx_obj = synx_util_acquire_object(client, h_synxs[i]);
|
||||
if (!synx_obj) {
|
||||
pr_err("[sess: %u] invalid handle %d in merge cleanup\n",
|
||||
client->id, h_synxs[i]);
|
||||
continue;
|
||||
}
|
||||
/* release all references obtained during merge validatation */
|
||||
synx_util_put_references(synx_obj);
|
||||
synx_util_release_object(client, h_synxs[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int synx_util_validate_merge(struct synx_client *client,
|
||||
s32 *h_synxs,
|
||||
u32 num_objs,
|
||||
struct dma_fence ***fence_list,
|
||||
u32 *fence_cnt)
|
||||
{
|
||||
u32 count = 0;
|
||||
u32 i = 0;
|
||||
struct synx_coredata **synx_objs = NULL;
|
||||
struct dma_fence **fences = NULL;
|
||||
|
||||
if (num_objs <= 1) {
|
||||
pr_err("single object merge is not allowed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
synx_objs = kcalloc(num_objs, sizeof(*synx_objs), GFP_KERNEL);
|
||||
if (!synx_objs)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num_objs; i++) {
|
||||
synx_objs[i] = synx_util_acquire_object(client, h_synxs[i]);
|
||||
if (!synx_objs) {
|
||||
pr_err("[sess: %u] invalid handle %d in merge list\n",
|
||||
client->id, h_synxs[i]);
|
||||
*fence_cnt = i;
|
||||
return -EINVAL;
|
||||
}
|
||||
count += synx_util_get_references(synx_objs[i]);
|
||||
}
|
||||
|
||||
fences = kcalloc(count, sizeof(*fences), GFP_KERNEL);
|
||||
if (!fences) {
|
||||
*fence_cnt = num_objs;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* memory will be released later in the invoking function */
|
||||
*fence_list = fences;
|
||||
count = 0;
|
||||
|
||||
for (i = 0; i < num_objs; i++) {
|
||||
count += synx_util_add_fence(synx_objs[i], fences, count);
|
||||
/* release the reference obtained earlier in the function */
|
||||
synx_util_release_object(client, h_synxs[i]);
|
||||
}
|
||||
|
||||
*fence_cnt = synx_util_remove_duplicates(fences, count);
|
||||
kfree(synx_objs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 __fence_state(struct dma_fence *fence, bool locked)
|
||||
{
|
||||
s32 status;
|
||||
u32 state = SYNX_STATE_INVALID;
|
||||
|
||||
if (!fence) {
|
||||
pr_err("invalid dma fence addr\n");
|
||||
return SYNX_STATE_INVALID;
|
||||
}
|
||||
|
||||
if (locked)
|
||||
status = dma_fence_get_status_locked(fence);
|
||||
else
|
||||
status = dma_fence_get_status(fence);
|
||||
|
||||
/* convert fence status to synx state */
|
||||
switch (status) {
|
||||
case 0:
|
||||
state = SYNX_STATE_ACTIVE;
|
||||
break;
|
||||
case 1:
|
||||
state = SYNX_STATE_SIGNALED_SUCCESS;
|
||||
break;
|
||||
case -SYNX_STATE_SIGNALED_CANCEL:
|
||||
state = SYNX_STATE_SIGNALED_CANCEL;
|
||||
break;
|
||||
case -SYNX_STATE_FORCED_RELEASE:
|
||||
state = SYNX_STATE_FORCED_RELEASE;
|
||||
break;
|
||||
case -SYNX_STATE_SIGNALED_ERROR:
|
||||
default:
|
||||
state = SYNX_STATE_SIGNALED_ERROR;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static u32 __fence_group_state(struct dma_fence *fence, bool locked)
|
||||
{
|
||||
u32 i = 0;
|
||||
u32 state = SYNX_STATE_INVALID;
|
||||
struct dma_fence_array *array = NULL;
|
||||
u32 intr, actv_cnt, sig_cnt, err_cnt;
|
||||
|
||||
if (!fence) {
|
||||
pr_err("invalid dma fence addr\n");
|
||||
return SYNX_STATE_INVALID;
|
||||
}
|
||||
|
||||
actv_cnt = sig_cnt = err_cnt = 0;
|
||||
array = to_dma_fence_array(fence);
|
||||
if (!array)
|
||||
return SYNX_STATE_INVALID;
|
||||
|
||||
for (i = 0; i < array->num_fences; i++) {
|
||||
intr = __fence_state(array->fences[i], locked);
|
||||
switch (intr) {
|
||||
case SYNX_STATE_ACTIVE:
|
||||
actv_cnt++;
|
||||
break;
|
||||
case SYNX_STATE_SIGNALED_SUCCESS:
|
||||
sig_cnt++;
|
||||
break;
|
||||
default:
|
||||
err_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
pr_debug("group cnt stats act:%u, sig: %u, err: %u\n",
|
||||
actv_cnt, sig_cnt, err_cnt);
|
||||
|
||||
if (err_cnt)
|
||||
state = SYNX_STATE_SIGNALED_ERROR;
|
||||
else if (actv_cnt)
|
||||
state = SYNX_STATE_ACTIVE;
|
||||
else if (sig_cnt == array->num_fences)
|
||||
state = SYNX_STATE_SIGNALED_SUCCESS;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/*
|
||||
* WARN: Should not hold the synx spinlock when invoking
|
||||
* this function. Use synx_fence_state_locked instead
|
||||
*/
|
||||
u32 synx_util_get_object_status(struct synx_coredata *synx_obj)
|
||||
{
|
||||
u32 state;
|
||||
struct dma_fence *fence;
|
||||
|
||||
if (!synx_obj)
|
||||
return SYNX_STATE_INVALID;
|
||||
|
||||
fence = synx_util_get_fence(synx_obj);
|
||||
if (synx_util_is_merged_object(synx_obj))
|
||||
state = __fence_group_state(fence, false);
|
||||
else
|
||||
state = __fence_state(fence, false);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/* use this for status check when holding on to metadata spinlock */
|
||||
u32 synx_util_get_object_status_locked(struct synx_coredata *synx_obj)
|
||||
{
|
||||
u32 state;
|
||||
struct dma_fence *fence;
|
||||
|
||||
if (!synx_obj)
|
||||
return SYNX_STATE_INVALID;
|
||||
|
||||
fence = synx_util_get_fence(synx_obj);
|
||||
if (synx_util_is_merged_object(synx_obj))
|
||||
state = __fence_group_state(fence, true);
|
||||
else
|
||||
state = __fence_state(fence, true);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
struct synx_handle_coredata *synx_util_obtain_handle(
|
||||
struct synx_client *client,
|
||||
s32 h_synx)
|
||||
{
|
||||
if (!client) {
|
||||
pr_err("invalid session argument\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (h_synx < 0 || h_synx >= SYNX_MAX_OBJS) {
|
||||
pr_err("[sess: %u] invalid handle %d access\n",
|
||||
client->id, h_synx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &client->synx_table[h_synx];
|
||||
}
|
||||
|
||||
struct synx_coredata *synx_util_acquire_object(
|
||||
struct synx_client *client, s32 h_synx)
|
||||
{
|
||||
struct synx_coredata *synx_obj = NULL;
|
||||
struct synx_handle_coredata *synx_data =
|
||||
synx_util_obtain_handle(client, h_synx);
|
||||
|
||||
if (!synx_data)
|
||||
return NULL;
|
||||
|
||||
mutex_lock(&client->synx_table_lock[h_synx]);
|
||||
synx_obj = synx_data->synx_obj;
|
||||
if (synx_obj) {
|
||||
synx_util_get_object(synx_obj);
|
||||
kref_get(&synx_data->internal_refcount);
|
||||
pr_debug("[sess: %u] acquired synx object for handle %d\n",
|
||||
client->id, h_synx);
|
||||
}
|
||||
mutex_unlock(&client->synx_table_lock[h_synx]);
|
||||
|
||||
return synx_obj;
|
||||
}
|
||||
|
||||
static void synx_util_destroy_handle(struct synx_handle_coredata *synx_data)
|
||||
{
|
||||
long idx = synx_data->id;
|
||||
struct synx_client *client = synx_data->client;
|
||||
|
||||
if (synx_util_is_merged_object(synx_data->synx_obj)) {
|
||||
pr_debug("cleaned up synx object fence\n");
|
||||
kfree(synx_data->synx_obj);
|
||||
}
|
||||
|
||||
memset(synx_data, 0, sizeof(*synx_data));
|
||||
clear_bit(idx, client->bitmap);
|
||||
pr_debug("[sess: %u] handle %d destroyed\n",
|
||||
client->id, idx);
|
||||
}
|
||||
|
||||
void synx_util_destroy_import_handle(struct kref *kref)
|
||||
{
|
||||
struct synx_handle_coredata *synx_data =
|
||||
container_of(kref, struct synx_handle_coredata,
|
||||
import_refcount);
|
||||
|
||||
pr_debug("[sess: %u] import handle cleanup for %d\n",
|
||||
synx_data->client->id, synx_data->id);
|
||||
|
||||
/* in case of pending internal references, abort clean up */
|
||||
if (kref_read(&synx_data->internal_refcount))
|
||||
return;
|
||||
|
||||
synx_util_destroy_handle(synx_data);
|
||||
}
|
||||
|
||||
void synx_util_destroy_internal_handle(struct kref *kref)
|
||||
{
|
||||
struct synx_handle_coredata *synx_data =
|
||||
container_of(kref, struct synx_handle_coredata,
|
||||
internal_refcount);
|
||||
|
||||
pr_debug("[sess: %u] internal handle cleanup for %d\n",
|
||||
synx_data->client->id, synx_data->id);
|
||||
|
||||
/* in case of pending imports, abort clean up */
|
||||
if (kref_read(&synx_data->import_refcount))
|
||||
return;
|
||||
|
||||
synx_util_destroy_handle(synx_data);
|
||||
}
|
||||
|
||||
void synx_util_release_object(struct synx_client *client, s32 h_synx)
|
||||
{
|
||||
struct synx_handle_coredata *synx_data =
|
||||
synx_util_obtain_handle(client, h_synx);
|
||||
struct synx_coredata *synx_obj;
|
||||
struct dma_fence *fence;
|
||||
|
||||
if (!synx_data)
|
||||
return;
|
||||
|
||||
mutex_lock(&client->synx_table_lock[h_synx]);
|
||||
synx_obj = synx_data->synx_obj;
|
||||
if (synx_obj) {
|
||||
fence = synx_util_get_fence(synx_obj);
|
||||
kref_put(&synx_data->internal_refcount,
|
||||
synx_util_destroy_internal_handle);
|
||||
dma_fence_put(fence);
|
||||
}
|
||||
mutex_unlock(&client->synx_table_lock[h_synx]);
|
||||
}
|
||||
|
||||
struct bind_operations *synx_util_get_bind_ops(u32 type)
|
||||
{
|
||||
struct synx_registered_ops *client_ops;
|
||||
|
||||
if (!synx_util_is_valid_bind_type(type))
|
||||
return NULL;
|
||||
|
||||
mutex_lock(&synx_dev->vtbl_lock);
|
||||
client_ops = &synx_dev->bind_vtbl[type];
|
||||
if (!client_ops->valid) {
|
||||
mutex_unlock(&synx_dev->vtbl_lock);
|
||||
return NULL;
|
||||
}
|
||||
mutex_unlock(&synx_dev->vtbl_lock);
|
||||
|
||||
return &client_ops->ops;
|
||||
}
|
||||
|
||||
int synx_util_alloc_cb_entry(struct synx_client *client,
|
||||
u32 *cb_idx)
|
||||
{
|
||||
bool bit;
|
||||
long idx;
|
||||
struct synx_client_cb *cb;
|
||||
|
||||
if (!client || !cb_idx)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
idx = find_first_zero_bit(client->cb_bitmap, SYNX_MAX_OBJS);
|
||||
if (idx >= SYNX_MAX_OBJS) {
|
||||
pr_err("[sess: %u] free cb index not available\n",
|
||||
client->id);
|
||||
return -ENOMEM;
|
||||
}
|
||||
bit = test_and_set_bit(idx, client->cb_bitmap);
|
||||
} while (bit);
|
||||
|
||||
cb = &client->cb_table[idx];
|
||||
memset(cb, 0, sizeof(*cb));
|
||||
cb->idx = idx;
|
||||
*cb_idx = idx;
|
||||
pr_debug("[sess: %u] allocated cb index %u\n", client->id, idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int synx_util_update_cb_entry(struct synx_client *client,
|
||||
void *data,
|
||||
u32 cb_idx)
|
||||
{
|
||||
struct synx_client_cb *cb;
|
||||
|
||||
if (!client || !data || (cb_idx >= SYNX_MAX_OBJS))
|
||||
return -EINVAL;
|
||||
|
||||
cb = &client->cb_table[cb_idx];
|
||||
cb->is_valid = true;
|
||||
memcpy(&cb->kernel_cb, data,
|
||||
sizeof(cb->kernel_cb));
|
||||
|
||||
pr_debug("[sess: %u] updated cb index %u\n", client->id, cb_idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void synx_util_callback_dispatch(struct synx_coredata *synx_obj, u32 status)
|
||||
{
|
||||
struct synx_cb_data *synx_cb, *synx_cb_temp;
|
||||
|
||||
if (!synx_obj) {
|
||||
pr_err("invalid arguments\n");
|
||||
return;
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(synx_cb,
|
||||
synx_cb_temp, &synx_obj->reg_cbs_list, node) {
|
||||
synx_cb->status = status;
|
||||
list_del_init(&synx_cb->node);
|
||||
queue_work(synx_dev->work_queue,
|
||||
&synx_cb->cb_dispatch);
|
||||
pr_debug("dispatched callback\n");
|
||||
}
|
||||
}
|
||||
|
||||
void synx_util_cb_dispatch(struct work_struct *cb_dispatch)
|
||||
{
|
||||
struct synx_cb_data *synx_cb =
|
||||
container_of(cb_dispatch, struct synx_cb_data, cb_dispatch);
|
||||
struct synx_client *client;
|
||||
struct synx_client_cb *cb;
|
||||
struct synx_kernel_payload *payload;
|
||||
|
||||
client = synx_get_client(synx_cb->session_id);
|
||||
if (!client) {
|
||||
pr_err("invalid session data %u in cb payload\n",
|
||||
synx_cb->session_id);
|
||||
goto free;
|
||||
}
|
||||
|
||||
if (synx_cb->idx >= SYNX_MAX_OBJS) {
|
||||
pr_err("[sess: %u] invalid cb index\n",
|
||||
client->id);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cb = &client->cb_table[synx_cb->idx];
|
||||
if (!cb->is_valid) {
|
||||
pr_err("invalid cb payload\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
payload = &cb->kernel_cb;
|
||||
pr_debug("[sess: %u] kernel cb dispatch for handle %d\n",
|
||||
client->id, payload->h_synx);
|
||||
|
||||
/* dispatch kernel callback */
|
||||
payload->cb_func(payload->h_synx,
|
||||
synx_cb->status,
|
||||
payload->data);
|
||||
|
||||
fail:
|
||||
synx_put_client(client);
|
||||
free:
|
||||
pr_debug("released synx_cb_data memory\n");
|
||||
kfree(synx_cb);
|
||||
}
|
||||
|
||||
struct synx_coredata *synx_util_import_object(struct synx_import_params *params)
|
||||
{
|
||||
u16 key;
|
||||
s32 h_synx;
|
||||
u32 secure_key;
|
||||
struct synx_session ex_session_id;
|
||||
struct synx_client *ex_client;
|
||||
struct synx_handle_coredata *synx_data;
|
||||
struct synx_coredata *synx_obj = NULL;
|
||||
|
||||
if (!params)
|
||||
return NULL;
|
||||
|
||||
h_synx = params->h_synx;
|
||||
secure_key = params->secure_key;
|
||||
|
||||
ex_session_id.client_id = secure_key & SYNX_CLIENT_HANDLE_MASK;
|
||||
key = (secure_key >> SYNX_CLIENT_HANDLE_SHIFT) & SYNX_SECURE_KEY_MASK;
|
||||
|
||||
/* get the client exporting the synx handle */
|
||||
ex_client = synx_get_client(ex_session_id);
|
||||
if (!ex_client) {
|
||||
pr_err("sess: %u invalid import handle %d and/or key %u\n",
|
||||
ex_session_id.client_id, h_synx, secure_key);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
synx_data = synx_util_obtain_handle(ex_client, h_synx);
|
||||
if (!synx_data) {
|
||||
pr_err("[sess: %u] invalid import handle %d\n",
|
||||
ex_client->id, h_synx);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mutex_lock(&ex_client->synx_table_lock[h_synx]);
|
||||
/* need to check whether import is accounted for in import_refcount */
|
||||
if (synx_data->synx_obj &&
|
||||
key && (key == synx_data->key) &&
|
||||
kref_read(&synx_data->import_refcount)) {
|
||||
if (synx_util_is_merged_object(synx_data->synx_obj)) {
|
||||
/*
|
||||
* need to copy the synx coredata for merged object
|
||||
* as we need to clean up the coredata on release
|
||||
* since fence array cannot be registered with
|
||||
* synx_fence_release function.
|
||||
*/
|
||||
synx_obj = kzalloc(sizeof(*synx_obj), GFP_KERNEL);
|
||||
if (synx_obj)
|
||||
memcpy(synx_obj, synx_data->synx_obj,
|
||||
sizeof(*synx_obj));
|
||||
} else {
|
||||
synx_obj = synx_data->synx_obj;
|
||||
}
|
||||
/* release the reference obtained during export */
|
||||
kref_put(&synx_data->import_refcount,
|
||||
synx_util_destroy_import_handle);
|
||||
pr_debug("sess: %u handle %d import successful\n",
|
||||
ex_client->id, h_synx);
|
||||
}
|
||||
mutex_unlock(&ex_client->synx_table_lock[h_synx]);
|
||||
|
||||
fail:
|
||||
synx_put_client(ex_client);
|
||||
return synx_obj;
|
||||
}
|
||||
|
||||
int synx_util_export_object(struct synx_client *client,
|
||||
struct synx_export_params *params)
|
||||
{
|
||||
int rc = 0;
|
||||
u16 key = 0;
|
||||
s32 h_synx;
|
||||
struct synx_handle_coredata *synx_data;
|
||||
|
||||
if (!params || !params->secure_key)
|
||||
return -EINVAL;
|
||||
|
||||
h_synx = params->h_synx;
|
||||
synx_data = synx_util_obtain_handle(client, h_synx);
|
||||
if (!synx_data) {
|
||||
pr_err("[sess: %u] invalid export handle %d\n",
|
||||
client->id, h_synx);
|
||||
rc = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mutex_lock(&client->synx_table_lock[h_synx]);
|
||||
/* need to check whether import is accounted for in import_refcount */
|
||||
if (synx_data->synx_obj) {
|
||||
/* generate the key on the first export of the handle */
|
||||
if (!synx_data->key) {
|
||||
kref_init(&synx_data->import_refcount);
|
||||
while (!key)
|
||||
get_random_bytes(&key, sizeof(key));
|
||||
synx_data->key = key;
|
||||
pr_debug("[sess: %u] import refcount initialized\n",
|
||||
client->id);
|
||||
} else {
|
||||
kref_get(&synx_data->import_refcount);
|
||||
key = synx_data->key;
|
||||
}
|
||||
/*
|
||||
* to make sure the synx is not lost if the process
|
||||
* dies or synx is released before any other process
|
||||
* gets a chance to import it.
|
||||
* The assumption is that an import will match this
|
||||
* and account for the extra reference. Otherwise,
|
||||
* this will be released upon client uninit.
|
||||
*/
|
||||
synx_util_get_object(synx_data->synx_obj);
|
||||
|
||||
/* encode the key and the client id to user provided variable */
|
||||
*params->secure_key = key;
|
||||
*params->secure_key <<= SYNX_CLIENT_HANDLE_SHIFT;
|
||||
*params->secure_key |= client->id;
|
||||
|
||||
pr_debug("[sess: %u] handle %d export successful with key %u\n",
|
||||
client->id, params->h_synx, *params->secure_key);
|
||||
} else {
|
||||
pr_err("[sess: %u] invalid export object %d\n",
|
||||
client->id, h_synx);
|
||||
rc = -EINVAL;
|
||||
}
|
||||
mutex_unlock(&client->synx_table_lock[h_synx]);
|
||||
|
||||
fail:
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct synx_client *synx_get_client(struct synx_session session_id)
|
||||
{
|
||||
struct synx_client_metadata *client_metadata;
|
||||
struct synx_client *client;
|
||||
u32 id = session_id.client_id;
|
||||
|
||||
if (id >= SYNX_MAX_CLIENTS) {
|
||||
pr_err("%s: invalid session handle %u from pid: %d\n",
|
||||
__func__, id, current->pid);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mutex_lock(&synx_dev->dev_table_lock);
|
||||
client_metadata = &synx_dev->client_table[id];
|
||||
client = client_metadata->client;
|
||||
if (client)
|
||||
kref_get(&client_metadata->refcount);
|
||||
else
|
||||
pr_err("session %u not available, pid: %d\n",
|
||||
id, current->pid);
|
||||
mutex_unlock(&synx_dev->dev_table_lock);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
static void synx_client_destroy(struct kref *kref)
|
||||
{
|
||||
u32 i;
|
||||
struct synx_client_metadata *client_metadata =
|
||||
container_of(kref, struct synx_client_metadata, refcount);
|
||||
struct synx_client *client = client_metadata->client;
|
||||
struct synx_handle_coredata *synx_data;
|
||||
struct synx_coredata *synx_obj;
|
||||
struct dma_fence *fence;
|
||||
|
||||
/* TODO:can create a thread and handle it */
|
||||
|
||||
/* go over all the remaining synx obj handles and clear them */
|
||||
for (i = 0; i < SYNX_MAX_OBJS; i++) {
|
||||
synx_data = &client->synx_table[i];
|
||||
synx_obj = synx_data->synx_obj;
|
||||
/*
|
||||
* cleanup unreleased references by the client
|
||||
* Note: it is only safe to access synx_obj if
|
||||
* there are refcounts (internal, import)
|
||||
* remaining in the current handle, as it
|
||||
* gurantees corresponding reference to fence.
|
||||
*/
|
||||
if (synx_obj) {
|
||||
fence = synx_util_get_fence(synx_obj);
|
||||
while (kref_read(&synx_data->internal_refcount)) {
|
||||
kref_put(&synx_data->internal_refcount,
|
||||
synx_util_destroy_internal_handle);
|
||||
dma_fence_put(fence);
|
||||
}
|
||||
while (kref_read(&synx_data->import_refcount)) {
|
||||
kref_put(&synx_data->import_refcount,
|
||||
synx_util_destroy_import_handle);
|
||||
dma_fence_put(fence);
|
||||
}
|
||||
}
|
||||
mutex_destroy(&client->synx_table_lock[i]);
|
||||
}
|
||||
mutex_destroy(&client->event_q_lock);
|
||||
memset(client_metadata, 0, sizeof(*client_metadata));
|
||||
clear_bit(client->id, synx_dev->bitmap);
|
||||
|
||||
pr_debug("[sess: %u] session destroyed %s\n",
|
||||
client->id, client->name);
|
||||
kfree(client);
|
||||
}
|
||||
|
||||
void synx_put_client(struct synx_client *client)
|
||||
{
|
||||
struct synx_client_metadata *client_metadata;
|
||||
|
||||
if (!client) {
|
||||
pr_err("%s: invalid client ptr\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->id >= SYNX_MAX_CLIENTS) {
|
||||
pr_err("%s: session id %u invalid from pid: %d\n",
|
||||
__func__, client->id, current->pid);
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&synx_dev->dev_table_lock);
|
||||
client_metadata = &synx_dev->client_table[client->id];
|
||||
if (client_metadata->client == client)
|
||||
/* should not reference client after this call */
|
||||
kref_put(&client_metadata->refcount, synx_client_destroy);
|
||||
else
|
||||
pr_err("%s: invalid session %u from pid: %d\n",
|
||||
__func__, client->id, current->pid);
|
||||
mutex_unlock(&synx_dev->dev_table_lock);
|
||||
}
|
||||
|
||||
void synx_util_generate_timestamp(char *timestamp, size_t size)
|
||||
{
|
||||
struct timespec64 tv;
|
||||
struct tm tm;
|
||||
|
||||
ktime_get_real_ts64(&tv);
|
||||
time64_to_tm(tv.tv_sec, 0, &tm);
|
||||
snprintf(timestamp, size, "%02d-%02d %02d:%02d:%02d",
|
||||
tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
|
||||
tm.tm_min, tm.tm_sec);
|
||||
}
|
||||
|
||||
void synx_util_log_error(u32 client_id, s32 h_synx, s32 err)
|
||||
{
|
||||
struct error_node *err_node;
|
||||
|
||||
if (!synx_dev->debugfs_root)
|
||||
return;
|
||||
|
||||
err_node = kzalloc(sizeof(*err_node), GFP_KERNEL);
|
||||
if (!err_node)
|
||||
return;
|
||||
|
||||
err_node->client_id = client_id;
|
||||
err_node->error_code = err;
|
||||
err_node->h_synx = h_synx;
|
||||
synx_util_generate_timestamp(err_node->timestamp,
|
||||
sizeof(err_node->timestamp));
|
||||
mutex_lock(&synx_dev->error_lock);
|
||||
list_add(&err_node->node,
|
||||
&synx_dev->error_list);
|
||||
mutex_unlock(&synx_dev->error_lock);
|
||||
}
|
||||
365
drivers/media/platform/msm/synx/synx_util.h
Normal file
365
drivers/media/platform/msm/synx/synx_util.h
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __SYNX_UTIL_H__
|
||||
#define __SYNX_UTIL_H__
|
||||
|
||||
#include "synx_private.h"
|
||||
|
||||
extern struct synx_device *synx_dev;
|
||||
|
||||
/**
|
||||
* @brief: Function to check if the external obj type is valid
|
||||
*
|
||||
* @param type : External sync object type
|
||||
*
|
||||
* @return True if valid. False otherwise
|
||||
*/
|
||||
bool synx_util_is_valid_bind_type(u32 type);
|
||||
|
||||
/**
|
||||
* @brief: Function to get the synx object type
|
||||
*
|
||||
* @param synx_obj : Pointer to synx object
|
||||
*
|
||||
* @return Synx object type. Zero if invalid
|
||||
*/
|
||||
u32 synx_util_get_object_type(struct synx_coredata *synx_obj);
|
||||
|
||||
/**
|
||||
* @brief: Function to check if the synx object is a composite (merged)
|
||||
*
|
||||
* @param synx_obj : Pointer to synx object
|
||||
*
|
||||
* @return True if merged object. False otherwise
|
||||
*/
|
||||
bool synx_util_is_merged_object(struct synx_coredata *synx_obj);
|
||||
|
||||
/**
|
||||
* @brief: Function to get the dma fence backing the synx object
|
||||
*
|
||||
* @param synx_obj : Pointer to synx object
|
||||
*
|
||||
* @return Dma fence. NULL if synx_obj is invalid
|
||||
*/
|
||||
struct dma_fence *synx_util_get_fence(
|
||||
struct synx_coredata *synx_obj);
|
||||
|
||||
/**
|
||||
* @brief: Function to acquire reference to synx object
|
||||
*
|
||||
* This acquires additional reference to the synx object and should be
|
||||
* released using synx_util_put_object
|
||||
*
|
||||
* @param synx_obj : Pointer to synx object
|
||||
*/
|
||||
void synx_util_get_object(struct synx_coredata *synx_obj);
|
||||
|
||||
/**
|
||||
* @brief: Function to release reference to synx object
|
||||
*
|
||||
* This releases reference to the synx object and should be
|
||||
* matched with synx_util_get_object
|
||||
*
|
||||
* @param synx_obj : Pointer to synx object
|
||||
*/
|
||||
void synx_util_put_object(struct synx_coredata *synx_obj);
|
||||
|
||||
/**
|
||||
* @brief: Function to initialize synx object
|
||||
*
|
||||
* It also initializes the dma fence. This should be called only
|
||||
* for individual objects.
|
||||
*
|
||||
* @param synx_obj : Pointer to synx object
|
||||
* @param params : Pointer to create params
|
||||
* @param ops : Dma fence ops required for fence initialization
|
||||
*
|
||||
* @return Status of operation. Negative in case of error. Zero otherwise.
|
||||
*/
|
||||
int synx_util_init_coredata(struct synx_coredata *synx_obj,
|
||||
struct synx_create_params *params,
|
||||
struct dma_fence_ops *ops);
|
||||
|
||||
/**
|
||||
* @brief: Function to initialize a merged synx object.
|
||||
*
|
||||
* It also initializes dma fence array.
|
||||
*
|
||||
* @param synx_obj : Pointer to synx object
|
||||
* @param fences : Array of fence objects which will merged
|
||||
* or grouped together to a fence array
|
||||
* @param num_objs : Number of fence objects in the array
|
||||
*
|
||||
* @return Status of operation. Negative in case of error. Zero otherwise.
|
||||
*/
|
||||
int synx_util_init_group_coredata(struct synx_coredata *synx_obj,
|
||||
struct dma_fence **fences,
|
||||
u32 num_objs);
|
||||
|
||||
/**
|
||||
* @brief: Function to cleanup the synx object
|
||||
*
|
||||
* This function will be invoked when the dma fence ref count reaches 0.
|
||||
*
|
||||
* @param synx_obj : Pointer to synx object
|
||||
*/
|
||||
void synx_util_object_destroy(struct synx_coredata *synx_obj);
|
||||
|
||||
/**
|
||||
* @brief: Function to allocate synx_client_cb entry from cb table
|
||||
*
|
||||
* @param client : Pointer to client session info
|
||||
* @param cb_idx : Allocated index (filled by function)
|
||||
*
|
||||
* @return Status of operation. Negative in case of error. Zero otherwise.
|
||||
*/
|
||||
int synx_util_alloc_cb_entry(struct synx_client *client,
|
||||
u32 *cb_idx);
|
||||
|
||||
/**
|
||||
* @brief: Function to allocate synx_client_cb entry from cb table
|
||||
*
|
||||
* @param client : Pointer to client session info
|
||||
* @param data : Opaque payload data
|
||||
* @param cb_idx : Callback table index
|
||||
*
|
||||
* @return Status of operation. Negative in case of error. Zero otherwise.
|
||||
*/
|
||||
int synx_util_update_cb_entry(struct synx_client *client,
|
||||
void *data,
|
||||
u32 cb_idx);
|
||||
|
||||
/**
|
||||
* @brief: Function to initialize synx object handle for the client
|
||||
*
|
||||
* @param client : Pointer to client session info
|
||||
* @param synx_obj : Pointer to synx object
|
||||
* @param h_synx : Pointer to synx object handle (filled by function)
|
||||
*
|
||||
* @return Status of operation. Negative in case of error. Zero otherwise.
|
||||
*/
|
||||
int synx_util_init_handle(struct synx_client *client,
|
||||
struct synx_coredata *synx_obj,
|
||||
long *h_synx);
|
||||
|
||||
/**
|
||||
* @brief: Function to activate the synx object. Moves the synx from INVALID
|
||||
* state to ACTIVE state.
|
||||
*
|
||||
* @param synx_obj : Pointer to synx object
|
||||
*
|
||||
* @return Status of operation. Negative in case of error. Zero otherwise.
|
||||
*/
|
||||
int synx_util_activate(struct synx_coredata *synx_obj);
|
||||
|
||||
/**
|
||||
* @brief: Function to queue all the registered callbacks by clients for
|
||||
* the synx object
|
||||
*
|
||||
* @param synx_obj : Pointer to synx object
|
||||
* @param state : State of the object
|
||||
*/
|
||||
void synx_util_callback_dispatch(struct synx_coredata *synx_obj, u32 state);
|
||||
|
||||
/**
|
||||
* @brief: Function to dispatch client registered callback
|
||||
*
|
||||
* @param cb_dispatch : Pointer to work_struct that needs to be dispatched
|
||||
*/
|
||||
void synx_util_cb_dispatch(struct work_struct *cb_dispatch);
|
||||
|
||||
/**
|
||||
* @brief: Function to handle error during merging of synx objects
|
||||
*
|
||||
* Removes the references added on the dma fence objects
|
||||
*
|
||||
* @param client : Pointer to client session
|
||||
* @param h_synxs : Array of synx object handles to merge
|
||||
* @param num_objs : Number of synx obects in the array
|
||||
*
|
||||
* @return Status of operation. Negative in case of error. Zero otherwise.
|
||||
*/
|
||||
s32 synx_util_merge_error(struct synx_client *client,
|
||||
s32 *h_synxs,
|
||||
u32 num_objs);
|
||||
|
||||
/**
|
||||
* @brief: Function to validate synx merge arguments.
|
||||
*
|
||||
* It obtains the necessary references to the fence objects
|
||||
* and also removes duplicates (if any).
|
||||
*
|
||||
* @param client : Pointer to client session
|
||||
* @param h_synxs : Array of synx object handles to merge
|
||||
* @param num_objs : Number of synx objects in the array
|
||||
* @param fences : Address to a list of dma fence* array
|
||||
* @param fence_cnt : Number of fence objects in the fences array
|
||||
* (filled by the function)
|
||||
*
|
||||
* @return Status of operation. Negative in case of error. Zero otherwise.
|
||||
*/
|
||||
int synx_util_validate_merge(struct synx_client *client,
|
||||
s32 *h_synxs,
|
||||
u32 num_objs,
|
||||
struct dma_fence ***fences,
|
||||
u32 *fence_cnt);
|
||||
|
||||
/**
|
||||
* @brief: Function to obtain the state of synx object
|
||||
*
|
||||
* The lock associated with the synx obj should not be
|
||||
* held when invoking this function.
|
||||
* Use synx_util_get_object_status_locked for state enquiry
|
||||
* when holding lock
|
||||
*
|
||||
* @param synx_obj : Pointer to synx object
|
||||
*
|
||||
* @return Status of the synx object.
|
||||
*/
|
||||
u32 synx_util_get_object_status(struct synx_coredata *synx_obj);
|
||||
|
||||
/**
|
||||
* @brief: Function to obtain the state of synx object
|
||||
*
|
||||
* The lock associated with the synx obj should be held
|
||||
* when invoking this function.
|
||||
* Use synx_util_get_object_status for state enquiry
|
||||
* when not holding lock
|
||||
*
|
||||
* @param synx_obj : Pointer to synx object
|
||||
*
|
||||
* @return Status of the synx object.
|
||||
*/
|
||||
u32 synx_util_get_object_status_locked(struct synx_coredata *synx_obj);
|
||||
|
||||
/**
|
||||
* @brief: Function to obtain the synx object metadata for the client
|
||||
*
|
||||
* @param client : Pointer to client session
|
||||
* @param h_synx : Synx object handle
|
||||
*
|
||||
* @return Pointer to synx object metadata for the client.
|
||||
* NULL if invalid synx handle.
|
||||
*/
|
||||
struct synx_handle_coredata *synx_util_obtain_handle(
|
||||
struct synx_client *client, s32 h_synx);
|
||||
|
||||
/**
|
||||
* @brief: Function to acquire the synx object
|
||||
*
|
||||
* This function increments the reference count of the dma fence.
|
||||
*
|
||||
* @param client : Pointer to client session
|
||||
* @param h_synx : Synx object handle
|
||||
*
|
||||
* @return Pointer to synx object. NULL if invalid synx handle.
|
||||
*/
|
||||
struct synx_coredata *synx_util_acquire_object(
|
||||
struct synx_client *client, s32 h_synx);
|
||||
|
||||
/**
|
||||
* @brief: Function to release the synx object
|
||||
*
|
||||
* This function decrements the reference count of the dma fence and
|
||||
* implicitly calls up the synx object cleanup if count reaches 0.
|
||||
* Synx object should not be derefrenced in the function after
|
||||
* calling this.
|
||||
*
|
||||
* @param client : Pointer to client session
|
||||
* @param h_synx : Synx object handle
|
||||
*/
|
||||
void synx_util_release_object(struct synx_client *client, s32 h_synx);
|
||||
|
||||
/**
|
||||
* @brief: Function called implicitly when import refcount reaches zero
|
||||
*
|
||||
* @param kref : &synx_handle_coredata.import_refcount
|
||||
*/
|
||||
void synx_util_destroy_import_handle(struct kref *kref);
|
||||
|
||||
/**
|
||||
* @brief: Function called implicitly when internal refcount reaches zero
|
||||
*
|
||||
* @param kref : &synx_handle_coredata.internal_refcount
|
||||
*/
|
||||
void synx_util_destroy_internal_handle(struct kref *kref);
|
||||
|
||||
/**
|
||||
* @brief: Function to get the bind operations registered by external driver
|
||||
*
|
||||
* @param type : External sync type
|
||||
*
|
||||
* @return Bind operations. NULL if type is invalid
|
||||
*/
|
||||
struct bind_operations *synx_util_get_bind_ops(u32 type);
|
||||
|
||||
/**
|
||||
* @brief: Function to share the synx object to use by other clients
|
||||
*
|
||||
* Shares the synx object by acquiring additional reference to dma fence
|
||||
*
|
||||
* @param client : Pointer to client session
|
||||
* @param params : Pointer to export params struct
|
||||
*
|
||||
* @return Status of operation. Negative in case of error. Zero otherwise.
|
||||
*/
|
||||
int synx_util_export_object(struct synx_client *client,
|
||||
struct synx_export_params *params);
|
||||
|
||||
/**
|
||||
* @brief: Function to import the synx object for use by the client
|
||||
*
|
||||
* Obtains the synx object corresponding to the importing synx handle
|
||||
*
|
||||
* @param params : Pointer to import params struct
|
||||
*
|
||||
* @return Synx object pointer on success. NULL if import failed or invalid
|
||||
*/
|
||||
struct synx_coredata *synx_util_import_object(
|
||||
struct synx_import_params *params);
|
||||
|
||||
/**
|
||||
* @brief: Function to acquire the synx client session
|
||||
*
|
||||
* This increments the reference count of the client in device client_table
|
||||
*
|
||||
* @param session_id : Client session id
|
||||
*
|
||||
* @return Pointer to synx client. NULL if invalid session id.
|
||||
*/
|
||||
struct synx_client *synx_get_client(struct synx_session session_id);
|
||||
|
||||
/**
|
||||
* @brief: Function to release the synx client session
|
||||
*
|
||||
* This decrements the reference count of the client in device client_table and
|
||||
* invokes the client session cleanup if reference reaches zero.
|
||||
*
|
||||
* @param client : Pointer to client session structure
|
||||
*/
|
||||
void synx_put_client(struct synx_client *client);
|
||||
|
||||
/**
|
||||
* @brief: Function to generate timestamp
|
||||
*
|
||||
* @param timestamp : Timestamp string (filled by function)
|
||||
* @param size : Timestamp string size
|
||||
*/
|
||||
void synx_util_generate_timestamp(char *timestamp, size_t size);
|
||||
|
||||
/**
|
||||
* @brief: Function to log framework errors
|
||||
*
|
||||
* This saves the error in a global list and can be accessed using debugfs.
|
||||
* Needs to be added where we wish to trace the error.
|
||||
*
|
||||
* @param id : Client session id
|
||||
* @param h_synx : Synx object handle
|
||||
* @param err : Error code
|
||||
*/
|
||||
void synx_util_log_error(u32 id, s32 h_synx, s32 err);
|
||||
|
||||
#endif /* __SYNX_UTIL_H__ */
|
||||
Loading…
Reference in a new issue