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