438 lines
9.5 KiB
C
Executable file
438 lines
9.5 KiB
C
Executable file
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Samsung Exynos SoC series dsp driver
|
|
*
|
|
* Copyright (c) 2020 Samsung Electronics Co., Ltd.
|
|
* http://www.samsung.com/
|
|
*/
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/io.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include "dsp-log.h"
|
|
#include "dsp-hw-common-system.h"
|
|
#include "dsp-hw-common-mailbox.h"
|
|
|
|
struct dsp_mailbox_pool *dsp_hw_common_mailbox_alloc_pool(
|
|
struct dsp_mailbox *mbox, unsigned int size)
|
|
{
|
|
int ret;
|
|
struct dsp_mailbox_pool_manager *pmgr;
|
|
unsigned int aligned_size;
|
|
struct dsp_mailbox_pool *pool;
|
|
unsigned long flags;
|
|
|
|
dsp_enter();
|
|
pmgr = mbox->pool_manager;
|
|
|
|
if (!size) {
|
|
ret = -EINVAL;
|
|
dsp_err("size of mailbox pool must be not zero\n");
|
|
goto p_err;
|
|
}
|
|
|
|
aligned_size = size + pmgr->block_size - 1;
|
|
if (aligned_size < size) {
|
|
ret = -EINVAL;
|
|
dsp_err("size overflowed(%#x/%#x/%#x)\n",
|
|
aligned_size, size, pmgr->block_size);
|
|
goto p_err;
|
|
}
|
|
|
|
pool = kzalloc(sizeof(*pool), GFP_KERNEL);
|
|
if (!pool) {
|
|
ret = -ENOMEM;
|
|
dsp_err("Failed to alloc pool header\n");
|
|
goto p_err;
|
|
}
|
|
pool->owner = pmgr;
|
|
pool->block_count = aligned_size / pmgr->block_size;
|
|
|
|
spin_lock_irqsave(&pmgr->slock, flags);
|
|
pool->block_start = dsp_util_bitmap_set_region(&pmgr->pool_map,
|
|
pool->block_count);
|
|
if (pool->block_start < 0) {
|
|
spin_unlock_irqrestore(&pmgr->slock, flags);
|
|
ret = pool->block_start;
|
|
dsp_err("Failed to allocate pool bitmap(%d)\n", ret);
|
|
goto p_err_bitmap;
|
|
}
|
|
|
|
list_add_tail(&pool->list, &pmgr->pool_list);
|
|
pmgr->pool_count++;
|
|
spin_unlock_irqrestore(&pmgr->slock, flags);
|
|
|
|
pool->size = pmgr->block_size * pool->block_count;
|
|
pool->used_size = size;
|
|
pool->iova = pmgr->iova + (unsigned int)pool->block_start *
|
|
pmgr->block_size;
|
|
pool->kva = pmgr->kva + (unsigned int)pool->block_start *
|
|
pmgr->block_size;
|
|
|
|
dsp_leave();
|
|
return pool;
|
|
p_err_bitmap:
|
|
kfree(pool);
|
|
p_err:
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
void dsp_hw_common_mailbox_free_pool(struct dsp_mailbox *mbox,
|
|
struct dsp_mailbox_pool *pool)
|
|
{
|
|
struct dsp_mailbox_pool_manager *pmgr;
|
|
unsigned long flags;
|
|
|
|
dsp_enter();
|
|
pmgr = pool->owner;
|
|
|
|
spin_lock_irqsave(&pmgr->slock, flags);
|
|
pmgr->pool_count--;
|
|
list_del(&pool->list);
|
|
dsp_util_bitmap_clear_region(&pmgr->pool_map, pool->block_start,
|
|
pool->block_count);
|
|
spin_unlock_irqrestore(&pmgr->slock, flags);
|
|
|
|
kfree(pool);
|
|
dsp_leave();
|
|
}
|
|
|
|
void dsp_hw_common_mailbox_dump_pool(struct dsp_mailbox *mbox,
|
|
struct dsp_mailbox_pool *pool)
|
|
{
|
|
size_t idx;
|
|
|
|
dsp_enter();
|
|
dsp_notice("[DSP_MAILBOX_DUMP_POOL][dump][%5zdbytes]\n",
|
|
pool->used_size);
|
|
for (idx = 0; idx < (pool->used_size >> 2); idx++) {
|
|
dsp_notice("[DSP_MAILBOX_DUMP_POOL][%4zu][0x%8x]\n", idx,
|
|
*((unsigned int *)pool->kva + idx));
|
|
}
|
|
|
|
dsp_leave();
|
|
}
|
|
|
|
static int __dsp_hw_common_mailbox_send_mail(struct dsp_mailbox *mbox,
|
|
struct dsp_mailbox_to_fw *mail)
|
|
{
|
|
int ret;
|
|
unsigned int repeat = 100;
|
|
|
|
dsp_enter();
|
|
mutex_lock(&mbox->lock);
|
|
while (1) {
|
|
ret = dsp_util_queue_check_full(mbox->to_fw);
|
|
if (!ret || !repeat)
|
|
break;
|
|
repeat--;
|
|
udelay(10);
|
|
}
|
|
|
|
if (ret) {
|
|
ret = -EBUSY;
|
|
dsp_err("There is no free space in the mailbox(%d)\n", ret);
|
|
dsp_util_queue_dump(mbox->to_fw);
|
|
goto p_err_full;
|
|
}
|
|
|
|
ret = dsp_util_queue_enqueue(mbox->to_fw, mail, sizeof(*mail));
|
|
if (ret)
|
|
goto p_err_enqueue;
|
|
|
|
mutex_unlock(&mbox->lock);
|
|
mbox->sys->interface.ops->send_irq(&mbox->sys->interface,
|
|
BIT(DSP_TO_CC_INT_MAILBOX));
|
|
dsp_leave();
|
|
return 0;
|
|
p_err_enqueue:
|
|
p_err_full:
|
|
mutex_unlock(&mbox->lock);
|
|
return ret;
|
|
}
|
|
|
|
int dsp_hw_common_mailbox_send_message(struct dsp_mailbox *mbox,
|
|
unsigned int message_id)
|
|
{
|
|
int ret;
|
|
struct dsp_mailbox_to_fw mail = { 0, };
|
|
|
|
dsp_enter();
|
|
mail.mailbox_version = mbox->mailbox_version;
|
|
mail.message_version = mbox->message_version;
|
|
mail.message_id = message_id;
|
|
|
|
ret = __dsp_hw_common_mailbox_send_mail(mbox, &mail);
|
|
if (ret)
|
|
goto p_err_send;
|
|
|
|
dsp_leave();
|
|
return 0;
|
|
p_err_send:
|
|
return ret;
|
|
}
|
|
|
|
int dsp_hw_common_mailbox_send_task(struct dsp_mailbox *mbox,
|
|
struct dsp_task *task)
|
|
{
|
|
int ret;
|
|
struct dsp_mailbox_pool *pool;
|
|
struct dsp_mailbox_to_fw mail = { 0, };
|
|
unsigned long flags;
|
|
|
|
dsp_enter();
|
|
pool = task->pool;
|
|
|
|
mail.mailbox_version = mbox->mailbox_version;
|
|
mail.message_version = mbox->message_version;
|
|
mail.task_id = task->id;
|
|
mail.pool_iova = pool->iova;
|
|
mail.pool_size = (unsigned int)pool->size;
|
|
mail.message_id = task->message_id;
|
|
mail.message_size = (unsigned int)pool->used_size;
|
|
|
|
if (mail.message_version != task->message_version) {
|
|
dsp_err("message_version is different(FW:%u/USER:%u)\n",
|
|
mail.message_version, task->message_version);
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
spin_lock_irqsave(&task->owner->slock, flags);
|
|
ret = dsp_task_trans_ready_to_process(task);
|
|
if (ret) {
|
|
task->result = ret;
|
|
dsp_task_trans_any_to_complete(task);
|
|
spin_unlock_irqrestore(&task->owner->slock, flags);
|
|
goto p_err;
|
|
}
|
|
spin_unlock_irqrestore(&task->owner->slock, flags);
|
|
|
|
ret = __dsp_hw_common_mailbox_send_mail(mbox, &mail);
|
|
if (ret)
|
|
goto p_err_send;
|
|
|
|
dsp_time_get_timestamp(&pool->time, TIMESTAMP_START);
|
|
dsp_leave();
|
|
return 0;
|
|
p_err_send:
|
|
task->result = ret;
|
|
spin_lock_irqsave(&task->owner->slock, flags);
|
|
dsp_task_trans_process_to_complete(task);
|
|
spin_unlock_irqrestore(&task->owner->slock, flags);
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
int dsp_hw_common_mailbox_receive_task(struct dsp_mailbox *mbox)
|
|
{
|
|
int ret;
|
|
struct dsp_mailbox_pool *pool;
|
|
struct dsp_mailbox_to_host mail;
|
|
struct dsp_task *task;
|
|
unsigned long flags;
|
|
|
|
dsp_enter();
|
|
__ioread32_copy(&mail, mbox->to_host, sizeof(mail) >> 2);
|
|
|
|
ret = mbox->ops->check_version(mbox, mail.mailbox_version,
|
|
mail.message_version);
|
|
if (ret)
|
|
goto p_err;
|
|
|
|
dsp_task_manager_lock(&flags);
|
|
|
|
task = dsp_task_get_process_by_id(mail.task_id);
|
|
if (!task) {
|
|
dsp_task_manager_unlock(flags);
|
|
ret = -EINVAL;
|
|
dsp_err("response including wrong task id was sent(%u)\n",
|
|
mail.task_id);
|
|
goto p_err;
|
|
}
|
|
|
|
pool = task->pool;
|
|
task->result = mail.task_ret;
|
|
dsp_task_trans_process_to_complete(task);
|
|
dsp_time_get_timestamp(&pool->time, TIMESTAMP_END);
|
|
dsp_task_manager_unlock(flags);
|
|
|
|
dsp_task_manager_wake_up();
|
|
|
|
dsp_leave();
|
|
return 0;
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
int dsp_hw_common_mailbox_check_version(struct dsp_mailbox *mbox,
|
|
unsigned int mbox_ver, unsigned int msg_ver)
|
|
{
|
|
int ret;
|
|
|
|
dsp_enter();
|
|
if (!(mbox_ver > DSP_MAILBOX_VERSION_START &&
|
|
mbox_ver < DSP_MAILBOX_VERSION_END)) {
|
|
ret = -EINVAL;
|
|
dsp_err("mailbox is not a supported version(v%u/v%u ~ v%u)\n",
|
|
mbox_ver,
|
|
DSP_MAILBOX_VERSION_START + 1,
|
|
DSP_MAILBOX_VERSION_END - 1);
|
|
goto p_err;
|
|
}
|
|
|
|
if (!(msg_ver > DSP_MESSAGE_VERSION_START &&
|
|
msg_ver < DSP_MESSAGE_VERSION_END)) {
|
|
ret = -EINVAL;
|
|
dsp_err("message is not a supported version(v%u/v%u ~ v%u)\n",
|
|
msg_ver,
|
|
DSP_MESSAGE_VERSION_START + 1,
|
|
DSP_MESSAGE_VERSION_END - 1);
|
|
goto p_err;
|
|
}
|
|
|
|
dsp_leave();
|
|
return 0;
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
int dsp_hw_common_mailbox_stop(struct dsp_mailbox *mbox)
|
|
{
|
|
dsp_enter();
|
|
mbox->to_fw = NULL;
|
|
mbox->to_host = NULL;
|
|
dsp_leave();
|
|
return 0;
|
|
}
|
|
|
|
static int __dsp_hw_common_mailbox_pool_manager_init(struct dsp_mailbox *mbox)
|
|
{
|
|
int ret;
|
|
struct dsp_memory *mem;
|
|
struct dsp_priv_mem *pmem;
|
|
struct dsp_mailbox_pool_manager *pmgr;
|
|
|
|
dsp_enter();
|
|
mem = &mbox->sys->memory;
|
|
pmem = &mem->priv_mem[mem->shared_id.mbox_pool];
|
|
pmgr = mbox->pool_manager;
|
|
|
|
pmgr->pool_count = 0;
|
|
pmgr->size = pmem->size;
|
|
pmgr->iova = pmem->iova;
|
|
pmgr->kva = pmem->kvaddr;
|
|
|
|
pmgr->block_size = SZ_1K;
|
|
pmgr->block_count = (unsigned int)(pmgr->size / pmgr->block_size);
|
|
pmgr->used_count = 0;
|
|
|
|
ret = dsp_util_bitmap_init(&pmgr->pool_map, "pool_bitmap",
|
|
pmgr->block_count);
|
|
if (ret)
|
|
goto p_err;
|
|
|
|
dsp_leave();
|
|
return 0;
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
int dsp_hw_common_mailbox_open(struct dsp_mailbox *mbox)
|
|
{
|
|
dsp_enter();
|
|
mbox->mailbox_version = 0;
|
|
mbox->message_version = 0;
|
|
__dsp_hw_common_mailbox_pool_manager_init(mbox);
|
|
dsp_leave();
|
|
return 0;
|
|
}
|
|
|
|
static void __dsp_hw_common_mailbox_pool_manager_deinit(
|
|
struct dsp_mailbox_pool_manager *pmgr)
|
|
{
|
|
struct dsp_mailbox_pool *pool, *temp;
|
|
|
|
dsp_enter();
|
|
list_for_each_entry_safe(pool, temp, &pmgr->pool_list, list)
|
|
dsp_mailbox_free_pool(pool);
|
|
|
|
dsp_util_bitmap_deinit(&pmgr->pool_map);
|
|
dsp_leave();
|
|
}
|
|
|
|
int dsp_hw_common_mailbox_close(struct dsp_mailbox *mbox)
|
|
{
|
|
dsp_enter();
|
|
__dsp_hw_common_mailbox_pool_manager_deinit(mbox->pool_manager);
|
|
dsp_leave();
|
|
return 0;
|
|
}
|
|
|
|
int dsp_hw_common_mailbox_probe(struct dsp_mailbox *mbox, void *sys)
|
|
{
|
|
int ret;
|
|
struct dsp_mailbox_pool_manager *pmgr;
|
|
|
|
dsp_enter();
|
|
mbox->sys = sys;
|
|
|
|
mutex_init(&mbox->lock);
|
|
pmgr = kzalloc(sizeof(*mbox->pool_manager), GFP_KERNEL);
|
|
if (!pmgr) {
|
|
ret = -ENOMEM;
|
|
dsp_err("Failed to alloc pool manager\n");
|
|
goto p_err;
|
|
}
|
|
spin_lock_init(&pmgr->slock);
|
|
INIT_LIST_HEAD(&pmgr->pool_list);
|
|
pmgr->mbox = mbox;
|
|
mbox->pool_manager = pmgr;
|
|
|
|
dsp_leave();
|
|
return 0;
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
void dsp_hw_common_mailbox_remove(struct dsp_mailbox *mbox)
|
|
{
|
|
dsp_enter();
|
|
kfree(mbox->pool_manager);
|
|
mutex_destroy(&mbox->lock);
|
|
dsp_leave();
|
|
}
|
|
|
|
int dsp_hw_common_mailbox_set_ops(struct dsp_mailbox *mbox, const void *ops)
|
|
{
|
|
dsp_enter();
|
|
mbox->ops = ops;
|
|
dsp_leave();
|
|
return 0;
|
|
}
|
|
|
|
struct dsp_mailbox_pool *dsp_mailbox_alloc_pool(struct dsp_mailbox *mbox,
|
|
unsigned int size)
|
|
{
|
|
dsp_check();
|
|
return mbox->ops->alloc_pool(mbox, size);
|
|
}
|
|
|
|
void dsp_mailbox_free_pool(struct dsp_mailbox_pool *pool)
|
|
{
|
|
struct dsp_mailbox *mbox = pool->owner->mbox;
|
|
|
|
dsp_enter();
|
|
mbox->ops->free_pool(mbox, pool);
|
|
dsp_leave();
|
|
}
|
|
|
|
void dsp_mailbox_dump_pool(struct dsp_mailbox_pool *pool)
|
|
{
|
|
struct dsp_mailbox *mbox = pool->owner->mbox;
|
|
|
|
dsp_enter();
|
|
mbox->ops->dump_pool(mbox, pool);
|
|
dsp_leave();
|
|
}
|