/*
|
* Copyright 2015 Rockchip Electronics Co. LTD
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*/
|
|
#define MODULE_TAG "vpu"
|
|
#include <sys/ioctl.h>
|
#include <fcntl.h>
|
#include <errno.h>
|
#include <string.h>
|
|
#include "vpu.h"
|
#include "rk_mpi.h"
|
|
#include "mpp_env.h"
|
#include "mpp_debug.h"
|
#include "mpp_common.h"
|
#include "mpp_platform.h"
|
|
#include "mpp_service.h"
|
#include "vcodec_service.h"
|
|
#define VPU_EXTRA_INFO_SIZE 12
|
#define VPU_EXTRA_INFO_MAGIC (0x4C4A46)
|
#define VPU_MPP_FLAGS_MULTI_MSG (0x00000001)
|
#define VPU_MPP_FLAGS_LAST_MSG (0x00000002)
|
|
#define MPX_PATCH_NUM 16
|
|
typedef struct VpuPatchInfo_t {
|
RK_U32 reg_idx;
|
RK_U32 offset;
|
} VpuPatchInfo;
|
|
typedef struct VpuExtraInfo_t {
|
RK_U32 magic; // Fix magic value 0x4C4A46
|
RK_U32 count; // valid patch info count
|
VpuPatchInfo patchs[MPX_PATCH_NUM];
|
} VpuExtraInfo;
|
|
typedef struct VPUReq {
|
RK_U32 *req;
|
RK_U32 size;
|
} VPUReq_t;
|
|
static RK_U32 vpu_debug = 0;
|
|
/* 0 original version, > 1 for others version */
|
static RK_S32 ioctl_version = 0;
|
|
static RK_S32 vpu_api_set_client_type(int dev, RK_S32 client_type)
|
{
|
static RK_S32 vpu_api_ioctl_version = -1;
|
RK_S32 ret;
|
|
if (ioctl_version > 0) {
|
MppReqV1 mpp_req;
|
RK_U32 vcodec_type;
|
RK_U32 client_data;
|
|
vcodec_type = mpp_get_vcodec_type();
|
|
switch (client_type) {
|
case VPU_ENC:
|
if (vcodec_type & HAVE_VDPU1)
|
client_data = VPU_CLIENT_VEPU1;
|
else if (vcodec_type & HAVE_VDPU2)
|
client_data = VPU_CLIENT_VEPU2;
|
break;
|
default:
|
break;
|
}
|
|
mpp_req.cmd = MPP_CMD_INIT_CLIENT_TYPE;
|
mpp_req.flag = 0;
|
mpp_req.size = sizeof(client_data);
|
mpp_req.offset = 0;
|
mpp_req.data_ptr = REQ_DATA_PTR(&client_data);
|
ret = (RK_S32)ioctl(dev, MPP_IOC_CFG_V1, &mpp_req);
|
} else {
|
if (vpu_api_ioctl_version < 0) {
|
ret = ioctl(dev, VPU_IOC_SET_CLIENT_TYPE, client_type);
|
if (!ret) {
|
vpu_api_ioctl_version = 0;
|
} else {
|
ret = ioctl(dev, VPU_IOC_SET_CLIENT_TYPE_U32, client_type);
|
if (!ret)
|
vpu_api_ioctl_version = 1;
|
}
|
|
if (ret)
|
mpp_err_f("can not find valid client type ioctl\n");
|
|
mpp_assert(ret == 0);
|
} else {
|
RK_U32 cmd = (vpu_api_ioctl_version == 0) ?
|
(VPU_IOC_SET_CLIENT_TYPE) :
|
(VPU_IOC_SET_CLIENT_TYPE_U32);
|
|
ret = ioctl(dev, cmd, client_type);
|
}
|
}
|
|
if (ret)
|
mpp_err_f("set client type failed ret %d errno %d\n", ret, errno);
|
|
return ret;
|
}
|
|
|
int VPUClientInit(VPU_CLIENT_TYPE type)
|
{
|
int ret;
|
int fd;
|
const char *path;
|
MppCtxType ctx_type;
|
MppCodingType coding = MPP_VIDEO_CodingAutoDetect;
|
|
switch (type) {
|
case VPU_DEC_HEVC:
|
coding = MPP_VIDEO_CodingHEVC;
|
ctx_type = MPP_CTX_DEC;
|
type = VPU_DEC;
|
break;
|
case VPU_DEC_AVS:
|
coding = MPP_VIDEO_CodingAVS;
|
ctx_type = MPP_CTX_DEC;
|
type = VPU_DEC;
|
break;
|
case VPU_DEC_RKV:
|
type = VPU_DEC;
|
ctx_type = MPP_CTX_DEC;
|
break;
|
case VPU_DEC:
|
case VPU_DEC_PP:
|
case VPU_PP:
|
ctx_type = MPP_CTX_DEC;
|
break;
|
case VPU_ENC:
|
case VPU_ENC_RKV:
|
ctx_type = MPP_CTX_ENC;
|
break;
|
default:
|
return -1;
|
break;
|
}
|
|
path = mpp_get_vcodec_dev_name(ctx_type, coding);
|
fd = open(path, O_RDWR | O_CLOEXEC);
|
|
mpp_env_get_u32("vpu_debug", &vpu_debug, 0);
|
|
ioctl_version = mpp_get_ioctl_version();
|
|
if (fd == -1) {
|
mpp_err_f("failed to open %s, errno = %d, error msg: %s\n",
|
path, errno, strerror(errno));
|
return -1;
|
}
|
|
ret = vpu_api_set_client_type(fd, type);
|
if (ret) {
|
return -2;
|
}
|
|
return fd;
|
}
|
|
RK_S32 VPUClientRelease(int socket)
|
{
|
int fd = socket;
|
if (fd > 0) {
|
close(fd);
|
}
|
return VPU_SUCCESS;
|
}
|
|
RK_S32 VPUClientSendReg(int socket, RK_U32 *regs, RK_U32 nregs)
|
{
|
int fd = socket;
|
RK_S32 ret;
|
VPUReq_t req;
|
|
if (vpu_debug) {
|
RK_U32 i;
|
|
for (i = 0; i < nregs; i++)
|
mpp_log("set reg[%03d]: %08x\n", i, regs[i]);
|
}
|
|
if (ioctl_version > 0) {
|
MppReqV1 reqs[3];
|
RK_U32 reg_size = nregs;
|
|
VpuExtraInfo *extra_info = (VpuExtraInfo*)(regs + (nregs - VPU_EXTRA_INFO_SIZE));
|
|
reqs[0].cmd = MPP_CMD_SET_REG_WRITE;
|
reqs[0].flag = 0;
|
reqs[0].offset = 0;
|
reqs[0].size = reg_size * sizeof(RK_U32);
|
reqs[0].data_ptr = REQ_DATA_PTR((void*)regs);
|
reqs[0].flag |= VPU_MPP_FLAGS_MULTI_MSG;
|
|
reqs[1].cmd = MPP_CMD_SET_REG_READ;
|
reqs[1].flag = 0;
|
reqs[1].offset = 0;
|
reqs[1].size = reg_size * sizeof(RK_U32);
|
reqs[1].data_ptr = REQ_DATA_PTR((void*)regs);
|
|
if (extra_info && extra_info->magic == VPU_EXTRA_INFO_MAGIC) {
|
reg_size = nregs - VPU_EXTRA_INFO_SIZE;
|
reqs[2].cmd = MPP_CMD_SET_REG_ADDR_OFFSET;
|
reqs[2].flag = 0;
|
reqs[2].offset = 0;
|
reqs[2].size = extra_info->count * sizeof(extra_info->patchs[0]);
|
reqs[2].data_ptr = REQ_DATA_PTR((void *)&extra_info->patchs[0]);
|
|
reqs[0].size = reg_size * sizeof(RK_U32);
|
reqs[1].size = reg_size * sizeof(RK_U32);
|
reqs[1].flag |= VPU_MPP_FLAGS_MULTI_MSG;
|
reqs[2].flag |= VPU_MPP_FLAGS_LAST_MSG;
|
ret = (RK_S32)ioctl(fd, MPP_IOC_CFG_V1, &reqs);
|
} else {
|
MppReqV1 reqs_tmp[2];
|
reqs[1].flag |= VPU_MPP_FLAGS_LAST_MSG;
|
memcpy(reqs_tmp, reqs, sizeof(MppReqV1) * 2);
|
ret = (RK_S32)ioctl(fd, MPP_IOC_CFG_V1, &reqs_tmp);
|
}
|
|
} else {
|
nregs *= sizeof(RK_U32);
|
req.req = regs;
|
req.size = nregs;
|
ret = (RK_S32)ioctl(fd, VPU_IOC_SET_REG, &req);
|
}
|
|
if (ret)
|
mpp_err_f("ioctl VPU_IOC_SET_REG failed ret %d errno %d %s\n", ret, errno, strerror(errno));
|
|
return ret;
|
}
|
|
RK_S32 VPUClientSendReg2(RK_S32 socket, RK_S32 offset, RK_S32 size, void *param)
|
{
|
RK_S32 ret = 0;
|
|
if (param == NULL) {
|
mpp_err_f("input param is NULL");
|
return 1;
|
}
|
|
ret = (RK_S32)ioctl(socket, VPU_IOC_WRITE(offset, size), param);
|
if (ret)
|
mpp_err_f("ioctl VPU_IOC_WRITE failed ret %d", ret);
|
|
return ret;
|
}
|
|
RK_S32 VPUClientWaitResult(int socket, RK_U32 *regs, RK_U32 nregs, VPU_CMD_TYPE *cmd, RK_S32 *len)
|
{
|
int fd = socket;
|
RK_S32 ret;
|
VPUReq_t req;
|
(void)len;
|
|
if (ioctl_version > 0) {
|
MppReqV1 mpp_req;
|
RK_U32 reg_size = nregs;
|
VpuExtraInfo *extra_info = (VpuExtraInfo*)(regs + (nregs - VPU_EXTRA_INFO_SIZE));
|
|
if (extra_info && extra_info->magic == VPU_EXTRA_INFO_MAGIC) {
|
reg_size -= 2;
|
} else {
|
reg_size -= VPU_EXTRA_INFO_SIZE;
|
}
|
|
mpp_req.cmd = MPP_CMD_POLL_HW_FINISH;
|
mpp_req.flag = 0;
|
mpp_req.offset = 0;
|
mpp_req.size = reg_size * sizeof(RK_U32);
|
mpp_req.data_ptr = REQ_DATA_PTR((void*)regs);
|
ret = (RK_S32)ioctl(fd, MPP_IOC_CFG_V1, &mpp_req);
|
} else {
|
nregs *= sizeof(RK_U32);
|
req.req = regs;
|
req.size = nregs;
|
|
ret = (RK_S32)ioctl(fd, VPU_IOC_GET_REG, &req);
|
}
|
|
if (ret) {
|
mpp_err_f("ioctl VPU_IOC_GET_REG failed ret %d errno %d %s\n", ret, errno, strerror(errno));
|
*cmd = VPU_SEND_CONFIG_ACK_FAIL;
|
} else
|
*cmd = VPU_SEND_CONFIG_ACK_OK;
|
|
if (vpu_debug) {
|
RK_U32 i;
|
|
for (i = 0; i < nregs; i++) {
|
mpp_log("get reg[%03d]: %08x\n", i, regs[i]);
|
}
|
}
|
|
return ret;
|
}
|
|
RK_S32 VPUClientGetHwCfg(int socket, RK_U32 *cfg, RK_U32 cfg_size)
|
{
|
int fd = socket;
|
RK_S32 ret;
|
VPUReq_t req;
|
req.req = cfg;
|
req.size = cfg_size;
|
ret = (RK_S32)ioctl(fd, VPU_IOC_GET_HW_FUSE_STATUS, &req);
|
if (ret)
|
mpp_err_f("ioctl VPU_IOC_GET_HW_FUSE_STATUS failed ret %d\n", ret);
|
|
return ret;
|
}
|
|
RK_U32 VPUCheckSupportWidth()
|
{
|
VPUHwDecConfig_t hwCfg;
|
int fd = -1;
|
fd = open("/dev/vpu_service", O_RDWR | O_CLOEXEC);
|
if (fd < 0) {
|
fd = open("/dev/vpu-service", O_RDWR | O_CLOEXEC);
|
}
|
memset(&hwCfg, 0, sizeof(VPUHwDecConfig_t));
|
if (fd >= 0) {
|
if (VPUClientGetHwCfg(fd, (RK_U32*)&hwCfg, sizeof(hwCfg))) {
|
mpp_err_f("Get HwCfg failed\n");
|
close(fd);
|
return -1;
|
}
|
close(fd);
|
fd = -1;
|
}
|
return hwCfg.maxDecPicWidth;
|
}
|
|
RK_S32 VPUClientGetIOMMUStatus()
|
{
|
return 1;
|
}
|