mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-08 12:58:12 -04:00
msm: synx: Porting synx driver to 5.x kernel
Synx driver manages synchronization and dependency handling across heterogeneous cores. Modified kernel APIs with support for kernel clients. Changes to synx handle creation, synx object handling and cleanup. Support for building synx driver as loadable module. Change-Id: I2d70d753872467895ef4cf5a85326b7a92310986 Signed-off-by: Sumukh Hallymysore Ravindra <shallymy@codeaurora.org>
This commit is contained in:
parent
1250336256
commit
4f5d895ef8
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