231 lines
5.1 KiB
C
Executable file
231 lines
5.1 KiB
C
Executable file
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Samsung Exynos SoC series dsp driver
|
|
*
|
|
* Copyright (c) 2019 Samsung Electronics Co., Ltd.
|
|
* http://www.samsung.com/
|
|
*/
|
|
|
|
#include <linux/device.h>
|
|
#include <linux/io.h>
|
|
|
|
#include "dsp-log.h"
|
|
#include "dsp-binary.h"
|
|
|
|
static struct device *binary_dev;
|
|
|
|
static int __dsp_binary_check_dev(void)
|
|
{
|
|
dsp_enter();
|
|
if (!binary_dev) {
|
|
dsp_err("binary_dev is NULL\n");
|
|
return -EFAULT;
|
|
}
|
|
dsp_leave();
|
|
return 0;
|
|
}
|
|
|
|
static void __dsp_binary_set_full_name(const char *name, char *postfix,
|
|
const char *extension, char *full_name)
|
|
{
|
|
dsp_enter();
|
|
if (postfix && (postfix[0] != '\0'))
|
|
snprintf(full_name, DSP_BINARY_NAME_SIZE, "%s_%s.%s",
|
|
name, postfix, extension);
|
|
else if (extension)
|
|
snprintf(full_name, DSP_BINARY_NAME_SIZE, "%s.%s",
|
|
name, extension);
|
|
else
|
|
snprintf(full_name, DSP_BINARY_NAME_SIZE, "%s", name);
|
|
dsp_leave();
|
|
}
|
|
|
|
int dsp_binary_load(const char *name, char *postfix, const char *extension,
|
|
void *target, size_t size, size_t *loaded_size)
|
|
{
|
|
int ret;
|
|
char full_name[DSP_BINARY_NAME_SIZE];
|
|
const struct firmware *fw_blob;
|
|
|
|
dsp_enter();
|
|
ret = __dsp_binary_check_dev();
|
|
if (ret)
|
|
goto p_err_dev;
|
|
|
|
__dsp_binary_set_full_name(name, postfix, extension, full_name);
|
|
|
|
if (!target) {
|
|
ret = -EINVAL;
|
|
dsp_err("dest address must be not NULL[%s]\n", full_name);
|
|
goto p_err_target;
|
|
}
|
|
|
|
ret = request_firmware_direct(&fw_blob, full_name, binary_dev);
|
|
if (ret < 0) {
|
|
dsp_err("Failed to request binary[%s](%d)\n", full_name, ret);
|
|
goto p_err_req;
|
|
}
|
|
|
|
if (fw_blob->size > size) {
|
|
ret = -EIO;
|
|
dsp_err("binary(%s) size is over(%zu/%zu)\n",
|
|
full_name, fw_blob->size, size);
|
|
goto p_err_size;
|
|
}
|
|
|
|
memcpy(target, fw_blob->data, fw_blob->size);
|
|
if (loaded_size)
|
|
*loaded_size = fw_blob->size;
|
|
dsp_info("binary[%s/%zu] is loaded\n", full_name, fw_blob->size);
|
|
release_firmware(fw_blob);
|
|
dsp_leave();
|
|
return 0;
|
|
p_err_size:
|
|
release_firmware(fw_blob);
|
|
p_err_req:
|
|
p_err_target:
|
|
p_err_dev:
|
|
return ret;
|
|
}
|
|
|
|
int dsp_binary_master_load(const char *name, char *postfix,
|
|
const char *extension, void __iomem *target, size_t size,
|
|
size_t *loaded_size)
|
|
{
|
|
int ret;
|
|
char full_name[DSP_BINARY_NAME_SIZE];
|
|
const struct firmware *fw_blob;
|
|
unsigned int remain = 0;
|
|
|
|
dsp_enter();
|
|
ret = __dsp_binary_check_dev();
|
|
if (ret)
|
|
goto p_err_dev;
|
|
|
|
__dsp_binary_set_full_name(name, postfix, extension, full_name);
|
|
|
|
if (!target) {
|
|
ret = -EINVAL;
|
|
dsp_err("dest address must be not NULL[%s]\n", full_name);
|
|
goto p_err_target;
|
|
}
|
|
|
|
ret = request_firmware_direct(&fw_blob, full_name, binary_dev);
|
|
if (ret < 0) {
|
|
dsp_err("Failed to request binary[%s](%d)\n", full_name, ret);
|
|
goto p_err_req;
|
|
}
|
|
|
|
if (fw_blob->size > size) {
|
|
ret = -EIO;
|
|
dsp_err("binary(%s) size is over(%zu/%zu)\n",
|
|
full_name, fw_blob->size, size);
|
|
goto p_err_size;
|
|
}
|
|
|
|
__iowrite32_copy(target, fw_blob->data, fw_blob->size >> 2);
|
|
if (fw_blob->size & 0x3) {
|
|
memcpy(&remain, fw_blob->data + (fw_blob->size & ~0x3),
|
|
fw_blob->size & 0x3);
|
|
writel(remain, target + (fw_blob->size & ~0x3));
|
|
}
|
|
|
|
if (loaded_size)
|
|
*loaded_size = fw_blob->size;
|
|
dsp_info("binary[%s/%zu] is loaded\n", full_name, fw_blob->size);
|
|
release_firmware(fw_blob);
|
|
dsp_leave();
|
|
return 0;
|
|
p_err_size:
|
|
release_firmware(fw_blob);
|
|
p_err_req:
|
|
p_err_target:
|
|
p_err_dev:
|
|
return ret;
|
|
}
|
|
|
|
int dsp_binary_alloc_load(const char *name, char *postfix,
|
|
const char *extension, void **target, size_t *loaded_size)
|
|
{
|
|
int ret;
|
|
char full_name[DSP_BINARY_NAME_SIZE];
|
|
const struct firmware *fw_blob;
|
|
|
|
dsp_enter();
|
|
ret = __dsp_binary_check_dev();
|
|
if (ret)
|
|
goto p_err_dev;
|
|
|
|
__dsp_binary_set_full_name(name, postfix, extension, full_name);
|
|
|
|
if (!target) {
|
|
ret = -EINVAL;
|
|
dsp_err("dest address must be not NULL[%s]\n", full_name);
|
|
goto p_err_target;
|
|
}
|
|
|
|
ret = request_firmware_direct(&fw_blob, full_name, binary_dev);
|
|
if (ret < 0) {
|
|
dsp_err("Failed to request binary[%s](%d)\n", full_name, ret);
|
|
goto p_err_req;
|
|
}
|
|
|
|
*target = vmalloc(fw_blob->size);
|
|
if (!(*target)) {
|
|
ret = -ENOMEM;
|
|
dsp_err("Failed to allocate target for binary[%s](%zu)\n",
|
|
full_name, fw_blob->size);
|
|
goto p_err_alloc;
|
|
}
|
|
|
|
memcpy(*target, fw_blob->data, fw_blob->size);
|
|
if (loaded_size)
|
|
*loaded_size = fw_blob->size;
|
|
dsp_info("binary[%s/%zu] is loaded\n", full_name, fw_blob->size);
|
|
release_firmware(fw_blob);
|
|
dsp_leave();
|
|
return 0;
|
|
p_err_alloc:
|
|
release_firmware(fw_blob);
|
|
p_err_req:
|
|
p_err_target:
|
|
p_err_dev:
|
|
return ret;
|
|
}
|
|
|
|
int dsp_binary_load_async(const char *name, char *postfix,
|
|
const char *extension, void *context,
|
|
void (*cont)(const struct firmware *fw, void *context))
|
|
{
|
|
int ret;
|
|
char full_name[DSP_BINARY_NAME_SIZE];
|
|
|
|
dsp_enter();
|
|
ret = __dsp_binary_check_dev();
|
|
if (ret)
|
|
goto p_err;
|
|
|
|
__dsp_binary_set_full_name(name, postfix, extension, full_name);
|
|
|
|
ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, full_name,
|
|
binary_dev, GFP_KERNEL, context, cont);
|
|
if (ret < 0) {
|
|
dsp_err("Failed to request binary asynchronously[%s](%d)\n",
|
|
full_name, ret);
|
|
goto p_err;
|
|
}
|
|
|
|
dsp_info("binary[%s] is being loaded...\n", full_name);
|
|
dsp_leave();
|
|
return 0;
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
int dsp_binary_init(void *dev)
|
|
{
|
|
dsp_enter();
|
|
binary_dev = dev;
|
|
dsp_leave();
|
|
return 0;
|
|
}
|