blob: 899b846ff9b640ad3491de792707ca8cbe7755b5 [file] [log] [blame]
/*
* Samsung Exynos SoC series VPU driver
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/syscalls.h>
#include <linux/vmalloc.h>
#include "vpu-config.h"
#include "vpu-binary.h"
static noinline_for_stack long __get_file_size(struct file *file)
{
struct kstat st;
if (vfs_getattr(&file->f_path, &st))
return -1;
if (!S_ISREG(st.mode))
return -1;
if (st.size != (long)st.size)
return -1;
return st.size;
}
int vpu_binary_init(struct vpu_binary *binary,
struct device *dev,
char *fpath1,
char *fpath2,
char *fname)
{
int ret = 0;
BUG_ON(!binary);
BUG_ON(!dev);
binary->dev = dev;
binary->image_size = 0;
snprintf(binary->fpath1, sizeof(binary->fpath1), "%s%s", fpath1, fname);
snprintf(binary->fpath2, sizeof(binary->fpath2), "%s%s", fpath2, fname);
return ret;
}
int vpu_binary_g_size(struct vpu_binary *binary, size_t *size)
{
int ret = 0;
mm_segment_t old_fs;
struct file *fp;
size_t fsize = 0;
old_fs = get_fs();
set_fs(KERNEL_DS);
fp = filp_open(binary->fpath1, O_RDONLY, 0);
if (IS_ERR_OR_NULL(fp)) {
set_fs(old_fs);
ret = -EINVAL;
goto p_err;
}
fsize = __get_file_size(fp);
if (fsize <= 0) {
vpu_err("__get_file_size is fail(%ld)\n", fsize);
filp_close(fp, current->files);
set_fs(old_fs);
ret = -EINVAL;
goto p_err;
}
p_err:
*size = fsize;
return ret;
}
int vpu_binary_read(struct vpu_binary *binary,
void *target,
size_t target_size)
{
int ret = 0;
const struct firmware *fw_blob;
u8 *buf;
struct file *fp;
mm_segment_t old_fs;
long fsize, nread;
BUG_ON(!binary);
old_fs = get_fs();
set_fs(KERNEL_DS);
fp = filp_open(binary->fpath1, O_RDONLY, 0);
if (IS_ERR_OR_NULL(fp)) {
fp = filp_open(binary->fpath2, O_RDONLY, 0);
if (IS_ERR_OR_NULL(fp)) {
set_fs(old_fs);
goto request_fw;
}
}
fsize = __get_file_size(fp);
if (fsize <= 0) {
vpu_err("__get_file_size is fail(%ld)\n", fsize);
ret = -EBADF;
goto p_err;
}
buf = vmalloc(fsize);
if (!buf) {
vpu_err("vmalloc is fail\n");
ret = -ENOMEM;
goto p_err;
}
nread = kernel_read(fp, 0, buf, fsize);
if (nread != fsize) {
vpu_err("kernel_read is fail(%ld != %ld)\n", nread, fsize);
ret = -EIO;
goto p_err;
}
if (fsize > target_size) {
vpu_err("image size is over(%ld > %ld)\n", fsize, target_size);
ret = -EIO;
goto p_err;
}
binary->image_size = fsize;
/* no cache operation, because target is sram of vpu */
#ifdef CONFIG_EXYNOS_VPU_HARDWARE
memcpy(target, (void *)buf, fsize);
#endif
vpu_info("FW(%s, %ld) were applied successfully.\n", binary->fpath1, fsize);
p_err:
vfree(buf);
filp_close(fp, current->files);
set_fs(old_fs);
return ret;
request_fw:
ret = request_firmware(&fw_blob, VPU_FW_NAME, binary->dev);
if (ret) {
vpu_err("request_firmware(%s) is fail(%d)", binary->fpath2, ret);
ret = -EINVAL;
goto request_err;
}
if (!fw_blob) {
vpu_err("fw_blob is NULL\n");
ret = -EINVAL;
goto request_err;
}
if (!fw_blob->data) {
vpu_err("fw_blob->data is NULL\n");
ret = -EINVAL;
goto request_err;
}
if (fw_blob->size > target_size) {
vpu_err("image size is over(%ld > %ld)\n", binary->image_size, target_size);
ret = -EIO;
goto p_err;
}
binary->image_size = fw_blob->size;
#ifdef CONFIG_EXYNOS_VPU_HARDWARE
memcpy(target, fw_blob->data, fw_blob->size);
#endif
vpu_info("Binay(%s, %ld) were applied successfully.\n", binary->fpath2, fw_blob->size);
request_err:
release_firmware(fw_blob);
return ret;
}
int vpu_binary_write(struct vpu_binary *binary,
void *target,
size_t target_size)
{
int ret = 0;
struct file *fp;
mm_segment_t old_fs;
long nwrite;
BUG_ON(!binary);
old_fs = get_fs();
set_fs(KERNEL_DS);
fp = filp_open(binary->fpath1, O_RDWR | O_CREAT, 0);
if (IS_ERR_OR_NULL(fp)) {
set_fs(old_fs);
vpu_err("filp_open is fail(%p)\n", fp);
ret = -EBADF;
goto p_err;
}
nwrite = kernel_write(fp, target, target_size, 0);
if (nwrite != target_size) {
filp_close(fp, current->files);
set_fs(old_fs);
vpu_err("kernel_write is fail(%ld != %ld)\n", nwrite, target_size);
ret = -EIO;
goto p_err;
}
binary->image_size = target_size;
vpu_info("Binay(%s, %ld) were applied successfully.\n", binary->fpath1, target_size);
filp_close(fp, current->files);
set_fs(old_fs);
p_err:
return ret;
}