/* * 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 #include #include #include #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; }