Jetpack/kernel/nvidia/drivers/platform/tegra/nvadsp/app.c

1009 lines
25 KiB
C

/*
* run_app.c
*
* ADSP OS App management
*
* Copyright (C) 2014-2018, NVIDIA Corporation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/platform_device.h>
#include <linux/tegra_nvadsp.h>
#include <linux/dma-mapping.h>
#include <linux/completion.h>
#include <linux/workqueue.h>
#include <linux/firmware.h>
#include <linux/dma-buf.h>
#include <linux/slab.h>
#include <linux/elf.h>
#include "aram_manager.h"
#include "os.h"
#include "dev.h"
#include "adsp_shared_struct.h"
#define DYN_APP_EXTN ".elf"
/*
* structure to hold the list of app binaries loaded and
* its associated instances.
*/
struct nvadsp_app_service {
char name[NVADSP_NAME_SZ];
struct list_head node;
int instance;
struct mutex lock;
struct list_head app_head;
const uint32_t token;
const struct app_mem_size *mem_size;
int generated_instance_id;
struct adsp_module *mod;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
#endif
};
/* nvadsp app loader private structure */
struct nvadsp_app_priv_struct {
struct platform_device *pdev;
struct completion os_load_complete;
struct nvadsp_mbox mbox;
struct list_head service_list;
struct mutex service_lock_list;
#ifdef CONFIG_DEBUG_FS
struct dentry *adsp_app_debugfs_root;
#endif
};
static struct nvadsp_app_priv_struct priv;
static void delete_app_instance(nvadsp_app_info_t *);
#ifdef CONFIG_DEBUG_FS
static int dump_binary_in_2bytes_app_file_node(struct seq_file *s, void *data)
{
struct nvadsp_app_service *ser = s->private;
struct adsp_module *mod = ser->mod;
u32 adsp_ptr;
u16 *ptr;
int i;
adsp_ptr = mod->adsp_module_ptr;
ptr = (u16 *)mod->module_ptr;
for (i = 0; i < mod->size; i += 2)
seq_printf(s, "0x%x : 0x%04x\n", adsp_ptr + i, *(ptr + i));
return 0;
}
static int dump_binary_in_words_app_file_node(struct seq_file *s, void *data)
{
struct nvadsp_app_service *ser = s->private;
struct adsp_module *mod = ser->mod;
u32 adsp_ptr;
u32 *ptr;
int i;
adsp_ptr = mod->adsp_module_ptr;
ptr = (u32 *)mod->module_ptr;
for (i = 0; i < mod->size; i += 4)
seq_printf(s, "0x%x : 0x%08x\n", adsp_ptr + i, *(ptr + i));
return 0;
}
static int host_load_addr_app_file_node(struct seq_file *s, void *data)
{
struct nvadsp_app_service *ser = s->private;
struct adsp_module *mod = ser->mod;
seq_printf(s, "%p\n", mod->module_ptr);
return 0;
}
static int adsp_load_addr_app_file_node(struct seq_file *s, void *data)
{
struct nvadsp_app_service *ser = s->private;
struct adsp_module *mod = ser->mod;
seq_printf(s, "0x%x\n", mod->adsp_module_ptr);
return 0;
}
static int size_app_file_node(struct seq_file *s, void *data)
{
struct nvadsp_app_service *ser = s->private;
struct adsp_module *mod = ser->mod;
seq_printf(s, "%lu\n", mod->size);
return 0;
}
static int version_app_file_node(struct seq_file *s, void *data)
{
struct nvadsp_app_service *ser = s->private;
struct adsp_module *mod = ser->mod;
seq_printf(s, "%s\n", strcmp(mod->version, "") ? mod->version : "unavailable");
return 0;
}
static int dram_app_file_node(struct seq_file *s, void *data)
{
const struct app_mem_size *mem_size = s->private;
seq_printf(s, "%llu\n", mem_size->dram);
return 0;
}
static int dram_shared_app_file_node(struct seq_file *s, void *data)
{
const struct app_mem_size *mem_size = s->private;
seq_printf(s, "%llu\n", mem_size->dram_shared);
return 0;
}
static int dram_shared_wc_app_file_node(struct seq_file *s, void *data)
{
const struct app_mem_size *mem_size = s->private;
seq_printf(s, "%llu\n", mem_size->dram_shared_wc);
return 0;
}
static int aram_app_file_node(struct seq_file *s, void *data)
{
const struct app_mem_size *mem_size = s->private;
seq_printf(s, "%llu\n", mem_size->aram);
return 0;
}
static int aram_exclusive_app_file_node(struct seq_file *s, void *data)
{
const struct app_mem_size *mem_size = s->private;
seq_printf(s, "%llu\n", mem_size->aram_x);
return 0;
}
#define ADSP_APP_CREATE_FOLDER(x, root) \
do {\
x = debugfs_create_dir(#x, root); \
if (IS_ERR_OR_NULL(x)) { \
dev_err(dev, "unable to create app %s folder\n", #x); \
ret = -ENOENT; \
goto rm_debug_root; \
} \
} while (0)
#define ADSP_APP_CREATE_FILE(x, priv, root) \
do { \
if (IS_ERR_OR_NULL(debugfs_create_file(#x, S_IRUSR, root, \
priv, &x##_node_operations))) { \
dev_err(dev, "unable tp create app %s file\n", #x); \
ret = -ENOENT; \
goto rm_debug_root; \
} \
} while (0)
#define ADSP_APP_FILE_OPERATION(x) \
static int x##_open(struct inode *inode, struct file *file) \
{ \
return single_open(file, x##_app_file_node, inode->i_private); \
} \
\
static const struct file_operations x##_node_operations = { \
.open = x##_open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release, \
};
ADSP_APP_FILE_OPERATION(dump_binary_in_2bytes);
ADSP_APP_FILE_OPERATION(dump_binary_in_words);
ADSP_APP_FILE_OPERATION(host_load_addr);
ADSP_APP_FILE_OPERATION(adsp_load_addr);
ADSP_APP_FILE_OPERATION(size);
ADSP_APP_FILE_OPERATION(version);
ADSP_APP_FILE_OPERATION(dram);
ADSP_APP_FILE_OPERATION(dram_shared);
ADSP_APP_FILE_OPERATION(dram_shared_wc);
ADSP_APP_FILE_OPERATION(aram);
ADSP_APP_FILE_OPERATION(aram_exclusive);
static int create_adsp_app_debugfs(struct nvadsp_app_service *ser)
{
struct app_mem_size *mem_size = (struct app_mem_size *)ser->mem_size;
struct device *dev = &priv.pdev->dev;
struct dentry *instance_mem_sizes;
struct dentry *root;
int ret = 0;
root = debugfs_create_dir(ser->name,
priv.adsp_app_debugfs_root);
if (IS_ERR_OR_NULL(root)) {
ret = -EINVAL;
goto err_out;
}
ADSP_APP_CREATE_FILE(dump_binary_in_2bytes, ser, root);
ADSP_APP_CREATE_FILE(dump_binary_in_words, ser, root);
ADSP_APP_CREATE_FILE(host_load_addr, ser, root);
ADSP_APP_CREATE_FILE(adsp_load_addr, ser, root);
ADSP_APP_CREATE_FILE(size, ser, root);
ADSP_APP_CREATE_FILE(version, ser, root);
ADSP_APP_CREATE_FOLDER(instance_mem_sizes, root);
ADSP_APP_CREATE_FILE(dram, mem_size, instance_mem_sizes);
ADSP_APP_CREATE_FILE(dram_shared, mem_size, instance_mem_sizes);
ADSP_APP_CREATE_FILE(dram_shared_wc, mem_size, instance_mem_sizes);
ADSP_APP_CREATE_FILE(aram, mem_size, instance_mem_sizes);
ADSP_APP_CREATE_FILE(aram_exclusive, mem_size, instance_mem_sizes);
root = ser->debugfs;
return 0;
rm_debug_root:
debugfs_remove_recursive(root);
err_out:
return ret;
}
static int __init adsp_app_debug_init(struct dentry *root)
{
priv.adsp_app_debugfs_root = debugfs_create_dir("adsp_apps", root);
return IS_ERR_OR_NULL(priv.adsp_app_debugfs_root) ? -ENOMEM : 0;
}
#endif /* CONFIG_DEBUG_FS */
static struct nvadsp_app_service *get_loaded_service(const char *appfile)
{
struct device *dev = &priv.pdev->dev;
struct nvadsp_app_service *ser;
list_for_each_entry(ser, &priv.service_list, node) {
if (!strcmp(appfile, ser->name)) {
dev_dbg(dev, "module %s already loaded\n", appfile);
return ser;
}
}
dev_dbg(dev, "module %s will be loaded\n", appfile);
return NULL;
}
static inline void extract_appname(char *appname, const char *appfile)
{
char *token = strstr(appfile, DYN_APP_EXTN);
int len = token ? token - appfile : strlen(appfile);
strncpy(appname, appfile, len);
appname[len] = '\0';
}
static nvadsp_app_handle_t app_load(const char *appfile,
struct adsp_shared_app *shared_app, bool dynamic)
{
struct nvadsp_drv_data *drv_data;
struct device *dev = &priv.pdev->dev;
char appname[NVADSP_NAME_SZ] = { };
struct nvadsp_app_service *ser;
drv_data = platform_get_drvdata(priv.pdev);
extract_appname(appname, appfile);
mutex_lock(&priv.service_lock_list);
ser = get_loaded_service(appname);
if (!ser) {
/* dynamic loading is disabled when running in secure mode */
if (drv_data->adsp_os_secload && dynamic)
goto err;
dev_dbg(dev, "loading app %s %s\n", appfile, appname);
ser = devm_kzalloc(dev, sizeof(*ser), GFP_KERNEL);
if (!ser)
goto err;
strlcpy(ser->name, appname, NVADSP_NAME_SZ);
/*load the module in to memory */
ser->mod = dynamic ?
load_adsp_dynamic_module(appfile, appfile, dev) :
load_adsp_static_module(appfile, shared_app, dev);
if (IS_ERR_OR_NULL(ser->mod))
goto err_free_service;
ser->mem_size = &ser->mod->mem_size;
mutex_init(&ser->lock);
INIT_LIST_HEAD(&ser->app_head);
/* add the app instance service to the list */
list_add_tail(&ser->node, &priv.service_list);
#ifdef CONFIG_DEBUG_FS
create_adsp_app_debugfs(ser);
#endif
dev_dbg(dev, "loaded app %s\n", ser->name);
}
mutex_unlock(&priv.service_lock_list);
return ser;
err_free_service:
devm_kfree(dev, ser);
err:
mutex_unlock(&priv.service_lock_list);
return NULL;
}
nvadsp_app_handle_t nvadsp_app_load(const char *appname, const char *appfile)
{
struct nvadsp_drv_data *drv_data;
if (IS_ERR_OR_NULL(priv.pdev)) {
pr_err("ADSP Driver is not initialized\n");
return NULL;
}
drv_data = platform_get_drvdata(priv.pdev);
if (!drv_data->adsp_os_running)
return NULL;
return app_load(appfile, NULL, true);
}
EXPORT_SYMBOL(nvadsp_app_load);
static void free_instance_memory(nvadsp_app_info_t *app,
const struct app_mem_size *sz)
{
adsp_app_mem_t *mem = &app->mem;
adsp_app_iova_mem_t *iova_mem = &app->iova_mem;
if (mem->dram) {
nvadsp_free_coherent(sz->dram, mem->dram, iova_mem->dram);
mem->dram = NULL;
iova_mem->dram = 0;
}
if (mem->shared) {
nvadsp_free_coherent(sz->dram_shared, mem->shared,
iova_mem->shared);
mem->shared = NULL;
iova_mem->shared = 0;
}
if (mem->shared_wc) {
nvadsp_free_coherent(sz->dram_shared_wc, mem->shared_wc,
iova_mem->shared_wc);
mem->shared_wc = NULL;
iova_mem->shared_wc = 0;
}
if (mem->aram_flag)
nvadsp_aram_release(mem->aram);
else if (mem->aram)
nvadsp_free_coherent(sz->aram, mem->aram, iova_mem->aram);
mem->aram = NULL;
iova_mem->aram = 0;
mem->aram_flag = 0;
if (mem->aram_x_flag) {
nvadsp_aram_release(mem->aram_x);
mem->aram_x = NULL;
iova_mem->aram_x = 0;
mem->aram_flag = 0;
}
}
static int create_instance_memory(nvadsp_app_info_t *app,
const struct app_mem_size *sz)
{
adsp_app_iova_mem_t *iova_mem = &app->iova_mem;
struct device *dev = &priv.pdev->dev;
adsp_app_mem_t *mem = &app->mem;
char name[NVADSP_NAME_SZ];
void *aram_handle;
dma_addr_t da;
snprintf(name, NVADSP_NAME_SZ, "%s:%d", app->name, app->instance_id);
if (sz->dram) {
mem->dram = nvadsp_alloc_coherent(sz->dram, &da, GFP_KERNEL);
iova_mem->dram = (uint32_t)da;
if (!mem->dram) {
dev_err(dev, "app %s dram alloc failed\n",
name);
goto end;
}
dev_dbg(dev, "%s :: mem.dram %p 0x%x\n", name,
mem->dram, iova_mem->dram);
}
if (sz->dram_shared) {
mem->shared = nvadsp_alloc_coherent(sz->dram_shared,
&da, GFP_KERNEL);
if (!mem->shared) {
dev_err(dev, "app %s shared dram alloc failed\n",
name);
goto end;
}
iova_mem->shared = (uint32_t)da;
dev_dbg(dev, "%s :: mem.shared %p 0x%x\n", name,
mem->shared, iova_mem->shared);
}
if (sz->dram_shared_wc) {
mem->shared_wc = nvadsp_alloc_coherent(sz->dram_shared_wc,
&da, GFP_KERNEL);
if (!mem->shared_wc) {
dev_err(dev, "app %s shared dram wc alloc failed\n",
name);
goto end;
}
iova_mem->shared_wc = (uint32_t)da;
dev_dbg(dev, "%s :: mem.shared_wc %p 0x%x\n", name,
mem->shared_wc, iova_mem->shared_wc);
}
if (sz->aram) {
aram_handle = nvadsp_aram_request(name, sz->aram);
if (!IS_ERR_OR_NULL(aram_handle)) {
iova_mem->aram = nvadsp_aram_get_address(aram_handle);
mem->aram = aram_handle;
iova_mem->aram_flag = mem->aram_flag = 1;
dev_dbg(dev, "%s aram %x\n", name, iova_mem->aram);
} else {
dev_dbg(dev, "app %s no ARAM memory ! using DRAM\n",
name);
mem->aram = nvadsp_alloc_coherent(sz->aram,
&da, GFP_KERNEL);
if (!mem->aram) {
iova_mem->aram_flag = mem->aram_flag = 0;
dev_err(dev,
"app %s aram memory alloc failed\n",
name);
goto end;
}
iova_mem->aram = (uint32_t)da;
dev_dbg(dev, "%s :: mem.aram %p 0x%x\n", name,
mem->aram, iova_mem->aram);
}
}
if (sz->aram_x) {
aram_handle = nvadsp_aram_request(name, sz->aram);
if (!IS_ERR_OR_NULL(aram_handle)) {
iova_mem->aram_x = nvadsp_aram_get_address(aram_handle);
mem->aram_x = aram_handle;
iova_mem->aram_x_flag = mem->aram_x_flag = 1;
dev_dbg(dev, "aram_x %x\n", iova_mem->aram_x);
} else {
iova_mem->aram_x = 0;
iova_mem->aram_x_flag = mem->aram_x_flag = 0;
dev_err(dev, "app %s aram x memory alloc failed\n",
name);
}
}
return 0;
end:
free_instance_memory(app, sz);
return -ENOMEM;
}
static void fill_app_instance_data(nvadsp_app_info_t *app,
struct nvadsp_app_service *ser, nvadsp_app_args_t *app_args,
struct run_app_instance_data *data, uint32_t stack_sz)
{
adsp_app_iova_mem_t *iova_mem = &app->iova_mem;
data->adsp_mod_ptr = ser->mod->adsp_module_ptr;
/* copy the iova address to adsp so that adsp can access the memory */
data->dram_data_ptr = iova_mem->dram;
data->dram_shared_ptr = iova_mem->shared;
data->dram_shared_wc_ptr = iova_mem->shared_wc;
data->aram_ptr = iova_mem->aram;
data->aram_flag = iova_mem->aram_flag;
data->aram_x_ptr = iova_mem->aram_x;
data->aram_x_flag = iova_mem->aram_x_flag;
if (app_args)
memcpy(&data->app_args, app_args, sizeof(nvadsp_app_args_t));
/*
* app on adsp holds the reference of host app instance to communicate
* back when completed. This way we do not need to iterate through the
* list to find the instance.
*/
data->host_ref = (uint64_t)app;
/* copy instance mem_size */
memcpy(&data->mem_size, ser->mem_size, sizeof(struct app_mem_size));
}
static nvadsp_app_info_t *create_app_instance(nvadsp_app_handle_t handle,
nvadsp_app_args_t *app_args, struct run_app_instance_data *data,
app_complete_status_notifier notifier, uint32_t stack_size)
{
struct nvadsp_app_service *ser = (void *)handle;
struct device *dev = &priv.pdev->dev;
nvadsp_app_info_t *app;
int *state;
int *id;
app = kzalloc(sizeof(*app), GFP_KERNEL);
if (unlikely(!app)) {
dev_err(dev, "cannot allocate memory for app %s instance\n",
ser->name);
goto err_value;
}
/* set the instance name with the app name */
app->name = ser->name;
/* associate a unique id */
id = (int *)&app->instance_id;
*id = ser->generated_instance_id++;
/*
* hold the pointer to the service, to dereference later during deinit
*/
app->handle = ser;
/* create the instance memory required by the app instance */
if (create_instance_memory(app, ser->mem_size)) {
dev_err(dev, "instance creation failed for app %s:%d\n",
app->name, app->instance_id);
goto free_app;
}
/* assign the stack that is needed by the app */
data->stack_size = stack_size;
/* set the state to INITIALIZED. No need to do it in a spin lock */
state = (int *)&app->state;
*state = NVADSP_APP_STATE_INITIALIZED;
/* increment instance count and add the app instance to service list */
mutex_lock(&ser->lock);
list_add_tail(&app->node, &ser->app_head);
ser->instance++;
mutex_unlock(&ser->lock);
fill_app_instance_data(app, ser, app_args, data, stack_size);
init_completion(&app->wait_for_app_start);
init_completion(&app->wait_for_app_complete);
set_app_complete_notifier(app, notifier);
dev_dbg(dev, "app %s instance %d initilized\n",
app->name, app->instance_id);
dev_dbg(dev, "app %s has %d instances\n", ser->name, ser->instance);
goto end;
free_app:
kfree(app);
err_value:
app = ERR_PTR(-ENOMEM);
end:
return app;
}
nvadsp_app_info_t __must_check *nvadsp_app_init(nvadsp_app_handle_t handle,
nvadsp_app_args_t *args)
{
struct nvadsp_app_shared_msg_pool *msg_pool;
struct nvadsp_shared_mem *shared_mem;
union app_loader_message *message;
struct nvadsp_drv_data *drv_data;
struct app_loader_data *data;
nvadsp_app_info_t *app;
msgq_t *msgq_send;
int *state;
unsigned long flags;
if (IS_ERR_OR_NULL(priv.pdev)) {
pr_err("ADSP Driver is not initialized\n");
goto err;
}
drv_data = platform_get_drvdata(priv.pdev);
if (!drv_data->adsp_os_running)
goto err;
if (IS_ERR_OR_NULL(handle))
goto err;
message = kzalloc(sizeof(*message), GFP_KERNEL);
if (!message)
goto err;
shared_mem = drv_data->shared_adsp_os_data;
msg_pool = &shared_mem->app_shared_msg_pool;
msgq_send = &msg_pool->app_loader_send_message.msgq;
data = &message->data;
app = create_app_instance(handle, args, &data->app_init, NULL, 0);
if (IS_ERR_OR_NULL(app)) {
kfree(message);
goto err;
}
app->priv = data;
data->app_init.message = ADSP_APP_INIT;
message->msgq_msg.size = MSGQ_MSG_PAYLOAD_WSIZE(*message);
spin_lock_irqsave(&drv_data->mbox_lock, flags);
msgq_queue_message(msgq_send, &message->msgq_msg);
spin_unlock_irqrestore(&drv_data->mbox_lock, flags);
if (app->return_status) {
state = (int *)&app->state;
*state = NVADSP_APP_STATE_STARTED;
}
nvadsp_mbox_send(&priv.mbox, 0, NVADSP_MBOX_SMSG, false, 0);
wait_for_completion(&app->wait_for_app_start);
init_completion(&app->wait_for_app_start);
return app;
err:
return ERR_PTR(-ENOMEM);
}
EXPORT_SYMBOL(nvadsp_app_init);
static int start_app_on_adsp(nvadsp_app_info_t *app,
union app_loader_message *message, bool block)
{
struct nvadsp_app_shared_msg_pool *msg_pool;
struct device *dev = &priv.pdev->dev;
struct nvadsp_shared_mem *shared_mem;
struct nvadsp_drv_data *drv_data;
msgq_t *msgq_send;
int *state;
unsigned long flags;
drv_data = platform_get_drvdata(priv.pdev);
shared_mem = drv_data->shared_adsp_os_data;
msg_pool = &shared_mem->app_shared_msg_pool;
msgq_send = &msg_pool->app_loader_send_message.msgq;
message->msgq_msg.size = MSGQ_MSG_PAYLOAD_WSIZE(*message);
spin_lock_irqsave(&drv_data->mbox_lock, flags);
msgq_queue_message(msgq_send, &message->msgq_msg);
spin_unlock_irqrestore(&drv_data->mbox_lock, flags);
state = (int *)&app->state;
*state = NVADSP_APP_STATE_STARTED;
nvadsp_mbox_send(&priv.mbox, 0, NVADSP_MBOX_SMSG, false, 0);
if (block) {
wait_for_completion(&app->wait_for_app_start);
if (app->return_status) {
dev_err(dev, "%s app instance %d failed to start\n",
app->name, app->instance_id);
state = (int *)&app->state;
*state = NVADSP_APP_STATE_INITIALIZED;
}
}
return app->return_status;
}
int nvadsp_app_start(nvadsp_app_info_t *app)
{
union app_loader_message *message = app->priv;
struct app_loader_data *data = &message->data;
struct nvadsp_drv_data *drv_data;
int ret = -EINVAL;
if (IS_ERR_OR_NULL(app))
return -EINVAL;
if (IS_ERR_OR_NULL(priv.pdev)) {
pr_err("ADSP Driver is not initialized\n");
goto err;
}
drv_data = platform_get_drvdata(priv.pdev);
if (!drv_data->adsp_os_running)
goto err;
data->app_init.message = ADSP_APP_START;
data->app_init.adsp_ref = app->token;
data->app_init.stack_size = app->stack_size;
ret = start_app_on_adsp(app, app->priv, true);
err:
return ret;
}
EXPORT_SYMBOL(nvadsp_app_start);
nvadsp_app_info_t *nvadsp_run_app(nvadsp_os_handle_t os_handle,
const char *appfile, nvadsp_app_args_t *app_args,
app_complete_status_notifier notifier, uint32_t stack_sz, bool block)
{
union app_loader_message message = {};
nvadsp_app_handle_t service_handle;
struct nvadsp_drv_data *drv_data;
nvadsp_app_info_t *info = NULL;
struct app_loader_data *data;
struct device *dev;
int ret;
if (IS_ERR_OR_NULL(priv.pdev)) {
pr_err("ADSP Driver is not initialized\n");
info = ERR_PTR(-EINVAL);
goto end;
}
drv_data = platform_get_drvdata(priv.pdev);
dev = &priv.pdev->dev;
if (!drv_data->adsp_os_running)
goto end;
if (IS_ERR_OR_NULL(appfile))
goto end;
data = &message.data;
service_handle = app_load(appfile, NULL, true);
if (!service_handle) {
dev_err(dev, "unable to load the app %s\n", appfile);
goto end;
}
info = create_app_instance(service_handle, app_args,
&data->app_init, notifier, stack_sz);
if (IS_ERR_OR_NULL(info)) {
dev_err(dev, "unable to create instance for app %s\n", appfile);
goto end;
}
data->app_init.message = RUN_ADSP_APP;
ret = start_app_on_adsp(info, &message, block);
if (ret) {
delete_app_instance(info);
info = NULL;
}
end:
return info;
}
EXPORT_SYMBOL(nvadsp_run_app);
static void delete_app_instance(nvadsp_app_info_t *app)
{
struct nvadsp_app_service *ser =
(struct nvadsp_app_service *)app->handle;
struct device *dev = &priv.pdev->dev;
dev_dbg(dev, "%s:freeing app %s:%d\n",
__func__, app->name, app->instance_id);
/* update the service app instance manager atomically */
mutex_lock(&ser->lock);
ser->instance--;
list_del(&app->node);
mutex_unlock(&ser->lock);
/* free instance memory */
free_instance_memory(app, ser->mem_size);
kfree(app->priv);
kfree(app);
}
void nvadsp_exit_app(nvadsp_app_info_t *app, bool terminate)
{
int *state;
if (IS_ERR_OR_NULL(priv.pdev)) {
pr_err("ADSP Driver is not initialized\n");
return;
}
if (IS_ERR_OR_NULL(app))
return;
/* TODO: add termination if possible to kill thread on adsp */
if (app->state == NVADSP_APP_STATE_STARTED) {
wait_for_completion(&app->wait_for_app_complete);
state = (int *)&app->state;
*state = NVADSP_APP_STATE_INITIALIZED;
}
delete_app_instance(app);
}
EXPORT_SYMBOL(nvadsp_exit_app);
int nvadsp_app_deinit(nvadsp_app_info_t *app)
{
nvadsp_exit_app(app, false);
return 0;
}
EXPORT_SYMBOL(nvadsp_app_deinit);
int nvadsp_app_stop(nvadsp_app_info_t *app)
{
return -ENOENT;
}
EXPORT_SYMBOL(nvadsp_app_stop);
void nvadsp_app_unload(nvadsp_app_handle_t handle)
{
struct nvadsp_drv_data *drv_data;
struct nvadsp_app_service *ser;
struct device *dev;
if (!priv.pdev) {
pr_err("ADSP Driver is not initialized\n");
return;
}
drv_data = platform_get_drvdata(priv.pdev);
dev = &priv.pdev->dev;
if (!drv_data->adsp_os_running)
return;
if (IS_ERR_OR_NULL(handle))
return;
ser = (struct nvadsp_app_service *)handle;
if (!ser->mod->dynamic)
return;
mutex_lock(&priv.service_lock_list);
if (ser->instance) {
dev_err(dev, "cannot unload app %s, has instances %d\n",
ser->name, ser->instance);
return;
}
list_del(&ser->node);
#ifdef CONFIG_DEBUG_FS
debugfs_remove_recursive(ser->debugfs);
#endif
unload_adsp_module(ser->mod);
devm_kfree(dev, ser);
mutex_unlock(&priv.service_lock_list);
}
EXPORT_SYMBOL(nvadsp_app_unload);
static status_t nvadsp_app_receive_handler(uint32_t msg, void *hdata)
{
union app_complete_status_message message = { };
struct nvadsp_app_shared_msg_pool *msg_pool;
struct app_complete_status_data *data;
struct nvadsp_shared_mem *shared_mem;
struct nvadsp_drv_data *drv_data;
struct platform_device *pdev;
nvadsp_app_info_t *app;
struct device *dev;
msgq_t *msgq_recv;
uint32_t *token;
pdev = hdata;
dev = &pdev->dev;
drv_data = platform_get_drvdata(pdev);
shared_mem = drv_data->shared_adsp_os_data;
msg_pool = &shared_mem->app_shared_msg_pool;
msgq_recv = &msg_pool->app_loader_recv_message.msgq;
data = &message.complete_status_data;
message.msgq_msg.size = MSGQ_MSG_PAYLOAD_WSIZE(*data);
if (msgq_dequeue_message(msgq_recv, &message.msgq_msg)) {
dev_err(dev, "unable to dequeue app status message\n");
return 0;
}
app = (nvadsp_app_info_t *)data->host_ref;
app->return_status = data->status;
app->status_msg = data->header.message;
token = (uint32_t *)&app->token;
*token = data->adsp_ref;
if (app->complete_status_notifier) {
app->complete_status_notifier(app,
app->status_msg, app->return_status);
}
switch (data->header.message) {
case ADSP_APP_START_STATUS:
complete_all(&app->wait_for_app_start);
break;
case ADSP_APP_COMPLETE_STATUS:
complete_all(&app->wait_for_app_complete);
break;
}
return 0;
}
int load_adsp_static_apps(void)
{
struct nvadsp_app_shared_msg_pool *msg_pool;
struct nvadsp_shared_mem *shared_mem;
struct nvadsp_drv_data *drv_data;
struct platform_device *pdev;
struct device *dev;
msgq_t *msgq_recv;
pdev = priv.pdev;
dev = &pdev->dev;
drv_data = platform_get_drvdata(pdev);
shared_mem = drv_data->shared_adsp_os_data;
msg_pool = &shared_mem->app_shared_msg_pool;
msgq_recv = &msg_pool->app_loader_recv_message.msgq;
while (1) {
union app_complete_status_message message = { };
struct adsp_static_app_data *data;
struct adsp_shared_app *shared_app;
char *name;
data = &message.static_app_data;
message.msgq_msg.size = MSGQ_MSG_PAYLOAD_WSIZE(*data);
if (msgq_dequeue_message(msgq_recv, &message.msgq_msg)) {
dev_err(dev, "dequeue of static apps failed\n");
return -EINVAL;
}
shared_app = &data->shared_app;
name = shared_app->name;
if (!shared_app->mod_ptr)
break;
/* Skip Start on boot apps */
if (shared_app->flags & ADSP_APP_FLAG_START_ON_BOOT)
continue;
app_load(name, shared_app, false);
}
return 0;
}
int __init nvadsp_app_module_probe(struct platform_device *pdev)
{
#ifdef CONFIG_DEBUG_FS
struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev);
#endif
uint16_t mbox_id = APP_LOADER_MBOX_ID;
struct device *dev = &pdev->dev;
int ret;
dev_info(dev, "%s\n", __func__);
ret = nvadsp_mbox_open(&priv.mbox, &mbox_id,
"app_service", nvadsp_app_receive_handler, pdev);
if (ret) {
dev_err(dev, "unable to open mailbox\n");
goto end;
}
priv.pdev = pdev;
INIT_LIST_HEAD(&priv.service_list);
init_completion(&priv.os_load_complete);
mutex_init(&priv.service_lock_list);
#ifdef CONFIG_DEBUG_FS
if (adsp_app_debug_init(drv_data->adsp_debugfs_root))
dev_err(&pdev->dev, "unable to create adsp apps debugfs\n");
#endif
end:
return ret;
}