/* * Copyright 2020 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 "mpp_serivce" #include #include #include #include #include "mpp_env.h" #include "mpp_mem.h" #include "mpp_debug.h" #include "mpp_common.h" #include "osal_2str.h" #include "mpp_device_debug.h" #include "mpp_service_api.h" #include "mpp_service_impl.h" #include "mpp_server.h" typedef struct MppServiceQueryCfg_t { RK_U32 cmd_butt; const char *name; } MppServiceQueryCfg; static const MppServiceQueryCfg query_cfg[] = { { MPP_CMD_QUERY_BASE, "query_cmd", }, { MPP_CMD_INIT_BASE, "init_cmd", }, { MPP_CMD_SEND_BASE, "query_cmd", }, { MPP_CMD_POLL_BASE, "init_cmd", }, { MPP_CMD_CONTROL_BASE, "control_cmd", }, }; static const RK_U32 query_count = MPP_ARRAY_ELEMS(query_cfg); const char *mpp_get_mpp_service_name(void) { static const char *mpp_service_name = NULL; static const char *mpp_service_dev[] = { "/dev/mpp_service", "/dev/mpp-service", }; if (mpp_service_name) return mpp_service_name; if (!access(mpp_service_dev[0], F_OK | R_OK | W_OK)) { mpp_service_name = mpp_service_dev[0]; } else if (!access(mpp_service_dev[1], F_OK | R_OK | W_OK)) mpp_service_name = mpp_service_dev[1]; return mpp_service_name; } RK_S32 mpp_service_ioctl(RK_S32 fd, RK_U32 cmd, RK_U32 size, void *param) { MppReqV1 mpp_req; memset(&mpp_req, 0, sizeof(mpp_req)); mpp_req.cmd = cmd; mpp_req.flag = 0; mpp_req.size = size; mpp_req.offset = 0; mpp_req.data_ptr = REQ_DATA_PTR(param); return (RK_S32)ioctl(fd, MPP_IOC_CFG_V1, &mpp_req); } RK_S32 mpp_service_ioctl_request(RK_S32 fd, MppReqV1 *req) { return (RK_S32)ioctl(fd, MPP_IOC_CFG_V1, req); } MPP_RET mpp_service_check_cmd_valid(RK_U32 cmd, const MppServiceCmdCap *cap) { RK_U32 found = 0; if (cap->support_cmd > 0) { found = (cmd < cap->query_cmd) ? 1 : 0; found = (cmd >= MPP_CMD_INIT_BASE && cmd < cap->init_cmd) ? 1 : found; found = (cmd >= MPP_CMD_SEND_BASE && cmd < cap->send_cmd) ? 1 : found; found = (cmd >= MPP_CMD_POLL_BASE && cmd < cap->poll_cmd) ? 1 : found; found = (cmd >= MPP_CMD_CONTROL_BASE && cmd < cap->ctrl_cmd) ? 1 : found; } else { /* old kernel before support_cmd query is valid */ found = (cmd >= MPP_CMD_INIT_BASE && cmd <= MPP_CMD_INIT_TRANS_TABLE) ? 1 : found; found = (cmd >= MPP_CMD_SEND_BASE && cmd <= MPP_CMD_SET_REG_ADDR_OFFSET) ? 1 : found; found = (cmd >= MPP_CMD_POLL_BASE && cmd <= MPP_CMD_POLL_HW_FINISH) ? 1 : found; found = (cmd >= MPP_CMD_CONTROL_BASE && cmd <= MPP_CMD_RELEASE_FD) ? 1 : found; } return found ? MPP_OK : MPP_NOK; } void check_mpp_service_cap(RK_U32 *codec_type, RK_U32 *hw_ids, MppServiceCmdCap *cap) { MppReqV1 mpp_req; RK_S32 fd = -1; RK_S32 ret = 0; RK_U32 *cmd_butt = &cap->query_cmd;; RK_U32 hw_support = 0; RK_U32 val; RK_U32 i; /* for device check on startup */ mpp_env_get_u32("mpp_device_debug", &mpp_device_debug, 0); *codec_type = 0; memset(hw_ids, 0, sizeof(RK_U32) * 32); /* check hw_support flag for valid client type */ fd = open(mpp_get_mpp_service_name(), O_RDWR | O_CLOEXEC); if (fd < 0) { mpp_err("open mpp_service to check cmd capability failed\n"); memset(cap, 0, sizeof(*cap)); return ; } ret = mpp_service_ioctl(fd, MPP_CMD_PROBE_HW_SUPPORT, 0, &hw_support); if (!ret) { mpp_dev_dbg_probe("vcodec_support %08x\n", hw_support); *codec_type = hw_support; } cap->support_cmd = !access("/proc/mpp_service/supports-cmd", F_OK) || !access("/proc/mpp_service/support_cmd", F_OK); if (cap->support_cmd) { for (i = 0; i < query_count; i++, cmd_butt++) { const MppServiceQueryCfg *cfg = &query_cfg[i]; memset(&mpp_req, 0, sizeof(mpp_req)); val = cfg->cmd_butt; mpp_req.cmd = MPP_CMD_QUERY_CMD_SUPPORT; mpp_req.data_ptr = REQ_DATA_PTR(&val); ret = (RK_S32)ioctl(fd, MPP_IOC_CFG_V1, &mpp_req); if (ret) mpp_err_f("query %-11s support error %s.\n", cfg->name, strerror(errno)); else { *cmd_butt = val; mpp_dev_dbg_probe("query %-11s support %04x\n", cfg->name, val); } } } close(fd); /* check each valid client type for hw_id */ /* kernel need to set client type then get hw_id */ for (i = 0; i < 32; i++) { if (hw_support & (1 << i)) { val = i; fd = open(mpp_get_mpp_service_name(), O_RDWR | O_CLOEXEC); if (fd < 0) { mpp_err("open mpp_service to check cmd capability failed\n"); break; } /* set client type first */ ret = mpp_service_ioctl(fd, MPP_CMD_INIT_CLIENT_TYPE, sizeof(val), &val); if (ret) { mpp_err("check valid client type %d failed\n", i); } else { /* then get hw_id */ ret = mpp_service_ioctl(fd, MPP_CMD_QUERY_HW_ID, sizeof(val), &val); if (!ret) { mpp_dev_dbg_probe("client %-10s hw_id %08x\n", strof_client_type((MppClientType)i), val); hw_ids[i] = val; } else mpp_err("check valid client %-10s for hw_id failed\n", strof_client_type((MppClientType)i)); } close(fd); } } } MppReqV1 *mpp_service_next_req(MppDevMppService *p) { MppReqV1 *mpp_req = NULL; if (p->req_cnt >= p->req_max) { mpp_dev_dbg_msg("enlarge request count %d -> %d\n", p->req_max, p->req_max * 2); p->reqs = mpp_realloc(p->reqs, MppReqV1, p->req_max * 2); if (NULL == p->reqs) { mpp_err_f("failed to enlarge request buffer\n"); return NULL; } p->req_max *= 2; } mpp_req = &p->reqs[p->req_cnt++]; return mpp_req; } RegOffsetInfo *mpp_service_next_reg_offset(MppDevMppService *p) { RegOffsetInfo *info = NULL; if (p->reg_offset_count + p->reg_offset_pos >= p->reg_offset_max) { mpp_dev_dbg_msg("enlarge reg offset count %d -> %d\n", p->reg_offset_max, p->reg_offset_max * 2); p->reg_offset_info = mpp_realloc(p->reg_offset_info, RegOffsetInfo, p->reg_offset_max * 2); if (NULL == p->reg_offset_info) { mpp_err_f("failed to enlarge request buffer\n"); return NULL; } p->reg_offset_max *= 2; } info = &p->reg_offset_info[p->reg_offset_count + p->reg_offset_pos]; mpp_dev_dbg_msg("reg offset %d : %d\n", p->reg_offset_pos, p->reg_offset_count); p->reg_offset_count++; return info; } RcbInfo *mpp_service_next_rcb_info(MppDevMppService *p) { RcbInfo *info = NULL; if (p->rcb_count + p->rcb_pos >= p->rcb_max) { mpp_dev_dbg_msg("enlarge rcb info count %d -> %d\n", p->rcb_max, p->rcb_max * 2); p->rcb_info = mpp_realloc(p->rcb_info, RcbInfo, p->rcb_max * 2); if (NULL == p->rcb_info) { mpp_err_f("failed to enlarge request buffer\n"); return NULL; } p->rcb_max *= 2; } info = &p->rcb_info[p->rcb_count + p->rcb_pos]; mpp_dev_dbg_msg("rcb info %d : %d\n", p->rcb_pos, p->rcb_count); p->rcb_count++; return info; } MPP_RET mpp_service_init(void *ctx, MppClientType type) { MppDevMppService *p = (MppDevMppService *)ctx; MPP_RET ret = MPP_NOK; p->cap = mpp_get_mpp_service_cmd_cap(); p->client = open(mpp_get_mpp_service_name(), O_RDWR | O_CLOEXEC); if (p->client < 0) { mpp_err("open mpp_service failed\n"); return ret; } /* set client type first */ ret = mpp_service_ioctl(p->client, MPP_CMD_INIT_CLIENT_TYPE, sizeof(type), &type); if (ret) mpp_err("set client type %d failed\n", type); mpp_assert(p->cap); if (MPP_OK == mpp_service_check_cmd_valid(MPP_CMD_SEND_CODEC_INFO, p->cap)) p->support_set_info = 1; if (MPP_OK == mpp_service_check_cmd_valid(MPP_CMD_SET_RCB_INFO, p->cap)) p->support_set_rcb_info = 1; if (MPP_OK == mpp_service_check_cmd_valid(MPP_CMD_POLL_HW_IRQ, p->cap)) p->support_hw_irq = 1; /* default server fd is the opened client fd */ p->client_type = type; p->server = p->client; p->batch_io = 0; p->serv_ctx = NULL; p->dev_cb = NULL; p->bat_cmd.flag = 0; p->bat_cmd.client = p->client; p->bat_cmd.ret = 0; p->req_max = MAX_REQ_NUM; p->reqs = mpp_malloc(MppReqV1, p->req_max); if (NULL == p->reqs) { mpp_err("create request buffer failed\n"); ret = MPP_ERR_MALLOC; } p->reg_offset_max = MAX_REG_OFFSET; p->reg_offset_info = mpp_malloc(RegOffsetInfo, p->reg_offset_max); if (NULL == p->reg_offset_info) { mpp_err("create register offset buffer failed\n"); ret = MPP_ERR_MALLOC; } p->reg_offset_pos = 0; p->reg_offset_count = 0; p->rcb_max = MAX_RCB_OFFSET; p->rcb_info = mpp_malloc(RcbInfo, p->rcb_max); if (NULL == p->rcb_info) { mpp_err("create rcb info buffer failed\n"); ret = MPP_ERR_MALLOC; } p->rcb_pos = 0; p->rcb_count = 0; return ret; } MPP_RET mpp_service_deinit(void *ctx) { MppDevMppService *p = (MppDevMppService *)ctx; if (p->batch_io) mpp_server_detach(p); if (p->client) close(p->client); MPP_FREE(p->reqs); MPP_FREE(p->reg_offset_info); MPP_FREE(p->rcb_info); return MPP_OK; } MPP_RET mpp_service_attach(void *ctx) { MppDevMppService *p = (MppDevMppService *)ctx; if (p->req_cnt) { mpp_err_f("can not switch on bat mode when service working\n"); return MPP_NOK; } if (!p->batch_io) mpp_server_attach(p); return MPP_OK; } MPP_RET mpp_service_detach(void *ctx) { MppDevMppService *p = (MppDevMppService *)ctx; if (p->req_cnt) { mpp_err_f("can not switch off bat mode when service working\n"); return MPP_NOK; } if (p->batch_io) mpp_server_detach(p); return MPP_OK; } MPP_RET mpp_service_delimit(void *ctx) { MppDevMppService *p = (MppDevMppService *)ctx; MppReqV1 *mpp_req = NULL; /* set fd trans info if needed */ if (p->reg_offset_count) { mpp_req = mpp_service_next_req(p); mpp_req->cmd = MPP_CMD_SET_REG_ADDR_OFFSET; mpp_req->flag = MPP_FLAGS_REG_OFFSET_ALONE; mpp_req->size = (p->reg_offset_count) * sizeof(RegOffsetInfo); mpp_req->offset = 0; mpp_req->data_ptr = REQ_DATA_PTR(&p->reg_offset_info[p->reg_offset_pos]); p->reg_offset_pos += p->reg_offset_count; p->reg_offset_count = 0; } /* set rcb offst info if needed */ if (p->rcb_count) { mpp_req = mpp_service_next_req(p); mpp_req->cmd = MPP_CMD_SET_RCB_INFO; mpp_req->flag = 0; mpp_req->size = p->rcb_count * sizeof(RcbInfo); mpp_req->offset = 0; mpp_req->data_ptr = REQ_DATA_PTR(&p->rcb_info[p->rcb_pos]); p->rcb_pos += p->rcb_count; p->rcb_count = 0; } mpp_req = mpp_service_next_req(p); mpp_req->cmd = MPP_CMD_SET_SESSION_FD; mpp_req->flag = MPP_FLAGS_MULTI_MSG; mpp_req->offset = 0; mpp_req->size = sizeof(p->bat_cmd); mpp_req->data_ptr = REQ_DATA_PTR(&p->bat_cmd); return MPP_OK; } MPP_RET mpp_service_set_cb_ctx(void *ctx, MppCbCtx *cb_ctx) { MppDevMppService *p = (MppDevMppService *)ctx; p->dev_cb = cb_ctx; return MPP_OK; } MPP_RET mpp_service_reg_wr(void *ctx, MppDevRegWrCfg *cfg) { MppDevMppService *p = (MppDevMppService *)ctx; MppReqV1 *mpp_req = mpp_service_next_req(p); mpp_req->cmd = MPP_CMD_SET_REG_WRITE; mpp_req->flag = 0; mpp_req->size = cfg->size; mpp_req->offset = cfg->offset; mpp_req->data_ptr = REQ_DATA_PTR(cfg->reg); return MPP_OK; } MPP_RET mpp_service_reg_rd(void *ctx, MppDevRegRdCfg *cfg) { MppDevMppService *p = (MppDevMppService *)ctx; MppReqV1 *mpp_req = mpp_service_next_req(p); mpp_req->cmd = MPP_CMD_SET_REG_READ; mpp_req->flag = 0; mpp_req->size = cfg->size; mpp_req->offset = cfg->offset; mpp_req->data_ptr = REQ_DATA_PTR(cfg->reg); return MPP_OK; } MPP_RET mpp_service_reg_offset(void *ctx, MppDevRegOffsetCfg *cfg) { MppDevMppService *p = (MppDevMppService *)ctx; RegOffsetInfo *info; RK_S32 i; if (!cfg->offset) return MPP_OK; if (p->reg_offset_count >= MAX_REG_OFFSET) { mpp_err_f("reach max offset definition\n", MAX_REG_OFFSET); return MPP_NOK; } for (i = 0; i < p->reg_offset_count; i++) { info = &p->reg_offset_info[p->reg_offset_pos + i]; if (info->reg_idx == cfg->reg_idx) { mpp_err_f("reg[%d] offset has been set, cover old %d -> %d\n", info->reg_idx, info->offset, cfg->offset); info->offset = cfg->offset; return MPP_OK; } } info = mpp_service_next_reg_offset(p);; info->reg_idx = cfg->reg_idx; info->offset = cfg->offset; return MPP_OK; } MPP_RET mpp_service_reg_offsets(void *ctx, MppDevRegOffCfgs *cfgs) { MppDevMppService *p = (MppDevMppService *)ctx; RegOffsetInfo *info; RK_S32 i; if (cfgs->count <= 0) return MPP_OK; if (p->reg_offset_count >= MAX_REG_OFFSET || p->reg_offset_count + cfgs->count >= MAX_REG_OFFSET) { mpp_err_f("reach max offset definition\n", MAX_REG_OFFSET); return MPP_NOK; } for (i = 0; i < cfgs->count; i++) { MppDevRegOffsetCfg *cfg = &cfgs->cfgs[i]; RK_S32 j; for (j = 0; j < p->reg_offset_count; j++) { info = &p->reg_offset_info[p->reg_offset_pos + j]; if (info->reg_idx == cfg->reg_idx) { mpp_err_f("reg[%d] offset has been set, cover old %d -> %d\n", info->reg_idx, info->offset, cfg->offset); info->offset = cfg->offset; continue; } } info = mpp_service_next_reg_offset(p);; info->reg_idx = cfg->reg_idx; info->offset = cfg->offset; } return MPP_OK; } MPP_RET mpp_service_rcb_info(void *ctx, MppDevRcbInfoCfg *cfg) { MppDevMppService *p = (MppDevMppService *)ctx; RK_U32 rcb_info_disable = 0; mpp_env_get_u32("disable_rcb_info", &rcb_info_disable, 0); if (rcb_info_disable) return MPP_OK; if (!p->support_set_rcb_info) return MPP_OK; if (p->rcb_count >= MAX_RCB_OFFSET) { mpp_err_f("reach max offset definition\n", MAX_RCB_OFFSET); return MPP_NOK; } RcbInfo *info = mpp_service_next_rcb_info(p); info->reg_idx = cfg->reg_idx; info->size = cfg->size; return MPP_OK; } MPP_RET mpp_service_set_info(void *ctx, MppDevInfoCfg *cfg) { MppDevMppService *p = (MppDevMppService *)ctx; if (!p->support_set_info) return MPP_OK; if (!p->info_count) memset(p->info, 0, sizeof(p->info)); memcpy(&p->info[p->info_count], cfg, sizeof(MppDevInfoCfg)); p->info_count++; return MPP_OK; } MPP_RET mpp_service_cmd_send(void *ctx) { MPP_RET ret = MPP_OK; MppDevMppService *p = (MppDevMppService *)ctx; if (p->req_cnt <= 0 || p->req_cnt > p->req_max) { mpp_err_f("ctx %p invalid request count %d\n", ctx, p->req_cnt); return MPP_ERR_VALUE; } if (p->info_count) { if (p->support_set_info) { MppReqV1 mpp_req; mpp_req.cmd = MPP_CMD_SEND_CODEC_INFO; mpp_req.flag = MPP_FLAGS_LAST_MSG; mpp_req.size = p->info_count * sizeof(p->info[0]); mpp_req.offset = 0; mpp_req.data_ptr = REQ_DATA_PTR(p->info); ret = mpp_service_ioctl_request(p->client, &mpp_req); if (ret) p->support_set_info = 0; } p->info_count = 0; } /* set fd trans info if needed */ if (p->reg_offset_count) { MppReqV1 *mpp_req = mpp_service_next_req(p); mpp_req->cmd = MPP_CMD_SET_REG_ADDR_OFFSET; mpp_req->flag = MPP_FLAGS_REG_OFFSET_ALONE; mpp_req->size = (p->reg_offset_count) * sizeof(RegOffsetInfo); mpp_req->offset = 0; mpp_req->data_ptr = REQ_DATA_PTR(&p->reg_offset_info[p->reg_offset_pos]); p->reg_offset_pos += p->reg_offset_count; } /* set rcb offst info if needed */ if (p->rcb_count) { MppReqV1 *mpp_req = mpp_service_next_req(p); mpp_req->cmd = MPP_CMD_SET_RCB_INFO; mpp_req->flag = 0; mpp_req->size = p->rcb_count * sizeof(RcbInfo); mpp_req->offset = 0; mpp_req->data_ptr = REQ_DATA_PTR(&p->rcb_info[p->rcb_pos]); p->rcb_pos += p->rcb_count; } /* setup flag for multi message request */ if (p->req_cnt > 1) { RK_S32 i; for (i = 0; i < p->req_cnt; i++) p->reqs[i].flag |= MPP_FLAGS_MULTI_MSG; } p->reqs[p->req_cnt - 1].flag |= MPP_FLAGS_LAST_MSG; if (p->batch_io) { ret = mpp_server_send_task(ctx); if (ret) mpp_err_f("send task to server ret %d\n", ret); } else { ret = mpp_service_ioctl_request(p->server, &p->reqs[0]); if (ret) { mpp_err_f("ioctl MPP_IOC_CFG_V1 failed ret %d errno %d %s\n", ret, errno, strerror(errno)); ret = errno; } } p->req_cnt = 0; p->reg_offset_count = 0; p->reg_offset_pos = 0; p->rcb_count = 0; p->rcb_pos = 0; p->rcb_count = 0; return ret; } MPP_RET mpp_service_cmd_poll(void *ctx, MppDevPollCfg *cfg) { MppDevMppService *p = (MppDevMppService *)ctx; MPP_RET ret = MPP_OK; if (p->batch_io) { ret = mpp_server_wait_task(ctx, 0); } else { MppReqV1 dev_req; memset(&dev_req, 0, sizeof(dev_req)); if (p->support_hw_irq && cfg) { dev_req.cmd = MPP_CMD_POLL_HW_IRQ; dev_req.flag |= MPP_FLAGS_LAST_MSG; dev_req.size = sizeof(*cfg) + cfg->count_max * sizeof(cfg->slice_info[0]); dev_req.offset = 0; dev_req.data_ptr = REQ_DATA_PTR(cfg); } else { dev_req.cmd = MPP_CMD_POLL_HW_FINISH; dev_req.flag |= MPP_FLAGS_LAST_MSG; if (cfg) { mpp_assert(cfg->count_max); if (cfg->count_max) { cfg->count_ret = 1; cfg->slice_info[0].val = 0; cfg->slice_info[0].last = 1; } } } ret = mpp_service_ioctl_request(p->server, &dev_req); if (ret) { mpp_err_f("ioctl MPP_IOC_CFG_V1 failed ret %d errno %d %s\n", ret, errno, strerror(errno)); ret = errno; } } return ret; } const MppDevApi mpp_service_api = { "mpp_service", sizeof(MppDevMppService), mpp_service_init, mpp_service_deinit, mpp_service_attach, mpp_service_detach, mpp_service_delimit, mpp_service_set_cb_ctx, mpp_service_reg_wr, mpp_service_reg_rd, mpp_service_reg_offset, mpp_service_reg_offsets, mpp_service_rcb_info, mpp_service_set_info, mpp_service_cmd_send, mpp_service_cmd_poll, };