/* * Copyright 2018 NXP */ /* * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ /*! * @file vpu_encoder_b0.c * * copyright here may be changed later * * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vpu_encoder_b0.h" unsigned int vpu_dbg_level_encoder = 1; #ifdef DUMP_DATA #define DATA_NUM 10 #endif static char *mu_cmp[] = { "fsl,imx8-mu1-vpu-m0", "fsl,imx8-mu2-vpu-m0" }; // H264 level is maped like level 5.1 to uLevel 51, except level 1b to uLevel 14 u_int32 h264_lvl[] = {10, 14, 11, 12, 13, 20, 21, 22, 30, 31, 32, 40, 41, 42, 50, 51}; static char *cmd2str[] = { "GTB_ENC_CMD_NOOP", /*0x0*/ "GTB_ENC_CMD_STREAM_START", "GTB_ENC_CMD_FRAME_ENCODE", "GTB_ENC_CMD_FRAME_SKIP", "GTB_ENC_CMD_STREAM_STOP", "GTB_ENC_CMD_PARAMETER_UPD", "GTB_ENC_CMD_TERMINATE", "GTB_ENC_CMD_SNAPSHOT", "GTB_ENC_CMD_ROLL_SNAPSHOT", "GTB_ENC_CMD_LOCK_SCHEDULER", "GTB_ENC_CMD_UNLOCK_SCHEDULER", "GTB_ENC_CMD_CONFIGURE_CODEC", "GTB_ENC_CMD_DEAD_MARK", }; static char *event2str[] = { "VID_API_EVENT_UNDEFINED", /*0x1*/ "VID_API_ENC_EVENT_RESET_DONE", /*0x1*/ "VID_API_ENC_EVENT_START_DONE", "VID_API_ENC_EVENT_STOP_DONE", "VID_API_ENC_EVENT_TERMINATE_DONE", "VID_API_ENC_EVENT_FRAME_INPUT_DONE", "VID_API_ENC_EVENT_FRAME_DONE", "VID_API_ENC_EVENT_FRAME_RELEASE", "VID_API_ENC_EVENT_PARA_UPD_DONE", "VID_API_ENC_EVENT_MEM_REQUEST", }; static void vpu_log_event(u_int32 uEvent, u_int32 ctxid) { if (uEvent > ARRAY_SIZE(event2str)-1) vpu_dbg(LVL_INFO, "reveive event: 0x%X, ctx id:%d\n", uEvent, ctxid); else vpu_dbg(LVL_INFO, "recevie event: %s, ctx id:%d\n", event2str[uEvent], ctxid); } static void vpu_log_cmd(u_int32 cmdid, u_int32 ctxid) { if (cmdid > ARRAY_SIZE(cmd2str)-1) vpu_dbg(LVL_INFO, "send cmd: 0x%X, ctx id:%d\n", cmdid, ctxid); else vpu_dbg(LVL_INFO, "send cmd: %s ctx id:%d\n", cmd2str[cmdid], ctxid); } /* * v4l2 ioctl() operation * */ static struct vpu_v4l2_fmt formats_compressed_enc[] = { { .name = "H264 Encoded Stream", .fourcc = V4L2_PIX_FMT_H264, .num_planes = 1, .venc_std = VPU_VIDEO_AVC, }, }; static struct vpu_v4l2_fmt formats_yuv_enc[] = { { .name = "4:2:0 2 Planes Y/CbCr", .fourcc = V4L2_PIX_FMT_NV12, .num_planes = 2, .venc_std = VPU_PF_YUV420_SEMIPLANAR, }, }; static void v4l2_vpu_send_cmd(struct vpu_ctx *ctx, uint32_t idx, uint32_t cmdid, uint32_t cmdnum, uint32_t *local_cmddata); static void MU_sendMesgToFW(void __iomem *base, MSG_Type type, uint32_t value) { MU_SendMessage(base, 1, value); MU_SendMessage(base, 0, type); } static int v4l2_ioctl_querycap(struct file *file, void *fh, struct v4l2_capability *cap ) { vpu_dbg(LVL_INFO, "%s()\n", __func__); strncpy(cap->driver, "vpu encoder", sizeof(cap->driver) - 1); strlcpy(cap->card, "vpu encoder", sizeof(cap->card)); strlcpy(cap->bus_info, "platform:", sizeof(cap->bus_info)); cap->version = KERNEL_VERSION(0, 0, 1); cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } static int v4l2_ioctl_enum_fmt_vid_cap_mplane(struct file *file, void *fh, struct v4l2_fmtdesc *f ) { struct vpu_v4l2_fmt *fmt; vpu_dbg(LVL_INFO, "%s()\n", __func__); if (f->index >= ARRAY_SIZE(formats_compressed_enc)) return -EINVAL; fmt = &formats_compressed_enc[f->index]; strlcpy(f->description, fmt->name, sizeof(f->description)); f->pixelformat = fmt->fourcc; f->flags |= V4L2_FMT_FLAG_COMPRESSED; return 0; } static int v4l2_ioctl_enum_fmt_vid_out_mplane(struct file *file, void *fh, struct v4l2_fmtdesc *f ) { struct vpu_v4l2_fmt *fmt; vpu_dbg(LVL_INFO, "%s()\n", __func__); if (f->index >= ARRAY_SIZE(formats_yuv_enc)) return -EINVAL; fmt = &formats_yuv_enc[f->index]; strlcpy(f->description, fmt->name, sizeof(f->description)); f->pixelformat = fmt->fourcc; return 0; } static int v4l2_ioctl_g_fmt(struct file *file, void *fh, struct v4l2_format *f ) { struct vpu_ctx *ctx = v4l2_fh_to_ctx(fh); struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; unsigned int i; vpu_dbg(LVL_INFO, "%s()\n", __func__); if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { pix_mp->pixelformat = V4L2_PIX_FMT_NV12; pix_mp->width = ctx->q_data[V4L2_SRC].width; pix_mp->height = ctx->q_data[V4L2_SRC].height; pix_mp->field = V4L2_FIELD_ANY; pix_mp->num_planes = 2; pix_mp->colorspace = V4L2_COLORSPACE_REC709; for (i = 0; i < pix_mp->num_planes; i++) pix_mp->plane_fmt[i].sizeimage = ctx->q_data[V4L2_SRC].sizeimage[i]; } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { pix_mp->width = ctx->q_data[V4L2_DST].width; pix_mp->height = ctx->q_data[V4L2_DST].height; pix_mp->field = V4L2_FIELD_ANY; pix_mp->plane_fmt[0].bytesperline = ctx->q_data[V4L2_DST].width; pix_mp->plane_fmt[0].sizeimage = ctx->q_data[V4L2_DST].sizeimage[0]; pix_mp->pixelformat = V4L2_PIX_FMT_H264; pix_mp->num_planes = 1; } else return -EINVAL; return 0; } static void get_param_from_v4l2(pMEDIAIP_ENC_PARAM pEncParam, struct v4l2_pix_format_mplane *pix_mp, struct vpu_ctx *ctx ) { //get the param and update gpParameters pEncParam->eCodecMode = MEDIAIP_ENC_FMT_H264; pEncParam->tEncMemDesc.uMemPhysAddr = ctx->encoder_mem.phy_addr; pEncParam->tEncMemDesc.uMemVirtAddr = ctx->encoder_mem.phy_addr; pEncParam->tEncMemDesc.uMemSize = ctx->encoder_mem.size; pEncParam->uFrameRate = 30; pEncParam->uSrcStride = pix_mp->width; pEncParam->uSrcWidth = pix_mp->width; pEncParam->uSrcHeight = pix_mp->height; pEncParam->uSrcOffset_x = 0; pEncParam->uSrcOffset_y = 0; pEncParam->uSrcCropWidth = pix_mp->width; pEncParam->uSrcCropHeight = pix_mp->height; pEncParam->uOutWidth = pix_mp->width; pEncParam->uOutHeight = pix_mp->height; pEncParam->uLowLatencyMode = 0; pEncParam->uIFrameInterval = 10; vpu_dbg(LVL_INFO, "eCodecMode(%d) eProfile(%d) uSrcStride(%d) uSrcWidth(%d) uSrcHeight(%d) uSrcOffset_x(%d) uSrcOffset_y(%d) uSrcCropWidth(%d) uSrcCropHeight(%d) uOutWidth(%d) uOutHeight(%d) uGopBLength(%d) uLowLatencyMode(%d) uInitSliceQP(%d) uIFrameInterval(%d) eBitRateMode(%d) uTargetBitrate(%d) uMaxBitRate(%d) uMinBitRate(%d) uFrameRate(%d)\n", pEncParam->eCodecMode, pEncParam->eProfile, pEncParam->uSrcStride, pEncParam->uSrcWidth, pEncParam->uSrcHeight, pEncParam->uSrcOffset_x, pEncParam->uSrcOffset_y, pEncParam->uSrcCropWidth, pEncParam->uSrcCropHeight, pEncParam->uOutWidth, pEncParam->uOutHeight, pEncParam->uGopBLength, pEncParam->uLowLatencyMode, pEncParam->uInitSliceQP, pEncParam->uIFrameInterval, pEncParam->eBitRateMode, pEncParam->uTargetBitrate, pEncParam->uMaxBitRate, pEncParam->uMinBitRate, pEncParam->uFrameRate); } static void *phy_to_virt(u_int32 src, unsigned long long offset) { void *result; result = (void *)(src + offset); return result; } static int v4l2_ioctl_s_fmt(struct file *file, void *fh, struct v4l2_format *f ) { struct vpu_ctx *ctx = v4l2_fh_to_ctx(fh); int ret = 0; struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; struct queue_data *q_data; struct core_device *dev = &ctx->dev->core_dev[ctx->core_id]; pENC_RPC_HOST_IFACE pSharedInterface = dev->shared_mem.pSharedInterface; pMEDIA_ENC_API_CONTROL_INTERFACE pEncCtrlInterface; pMEDIAIP_ENC_PARAM pEncParam; pMEDIAIP_ENC_EXPERT_MODE_PARAM pEncExpertModeParam; u_int32 i; pEncCtrlInterface = (pMEDIA_ENC_API_CONTROL_INTERFACE)phy_to_virt(pSharedInterface->pEncCtrlInterface[ctx->str_index], dev->shared_mem.base_offset); pEncParam = (pMEDIAIP_ENC_PARAM)phy_to_virt(pEncCtrlInterface->pEncParam, dev->shared_mem.base_offset); pEncExpertModeParam = (pMEDIAIP_ENC_EXPERT_MODE_PARAM)phy_to_virt(pEncCtrlInterface->pEncExpertModeParam, dev->shared_mem.base_offset); vpu_dbg(LVL_INFO, "%s()\n", __func__); if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { q_data = &ctx->q_data[V4L2_SRC]; get_param_from_v4l2(pEncParam, pix_mp, ctx); q_data->fourcc = pix_mp->pixelformat; q_data->width = pix_mp->width; q_data->height = pix_mp->height; q_data->rect.left = 0; q_data->rect.top = 0; q_data->rect.width = pix_mp->width; q_data->rect.height = pix_mp->height; q_data->sizeimage[0] = pix_mp->width * pix_mp->height; q_data->sizeimage[1] = pix_mp->width * pix_mp->height / 2; pix_mp->num_planes = 2; for (i = 0; i < pix_mp->num_planes; i++) pix_mp->plane_fmt[i].sizeimage = q_data->sizeimage[i]; } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { q_data = &ctx->q_data[V4L2_DST]; q_data->fourcc = pix_mp->pixelformat; q_data->width = pix_mp->width; q_data->height = pix_mp->height; q_data->sizeimage[0] = pix_mp->plane_fmt[0].sizeimage; } else ret = -EINVAL; return ret; } static int v4l2_ioctl_expbuf(struct file *file, void *fh, struct v4l2_exportbuffer *buf ) { struct vpu_ctx *ctx = v4l2_fh_to_ctx(fh); struct queue_data *q_data; vpu_dbg(LVL_INFO, "%s()\n", __func__); if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) q_data = &ctx->q_data[V4L2_SRC]; else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) q_data = &ctx->q_data[V4L2_DST]; else return -EINVAL; return (vb2_expbuf(&q_data->vb2_q, buf )); } static int v4l2_ioctl_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub ) { vpu_dbg(LVL_INFO, "%s()\n", __func__); switch (sub->type) { case V4L2_EVENT_EOS: return v4l2_event_subscribe(fh, sub, 0, NULL); case V4L2_EVENT_SOURCE_CHANGE: return v4l2_src_change_event_subscribe(fh, sub); default: return -EINVAL; } } static int v4l2_ioctl_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *reqbuf ) { struct vpu_ctx *ctx = v4l2_fh_to_ctx(fh); struct queue_data *q_data; int ret; vpu_dbg(LVL_INFO, "%s()\n", __func__); if (reqbuf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) q_data = &ctx->q_data[V4L2_SRC]; else if (reqbuf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) q_data = &ctx->q_data[V4L2_DST]; else return -EINVAL; ret = vb2_reqbufs(&q_data->vb2_q, reqbuf); vpu_dbg(LVL_INFO, "%s() c_port_req_buf(%d)\n", __func__, ret); return ret; } static int v4l2_ioctl_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf ) { struct vpu_ctx *ctx = v4l2_fh_to_ctx(fh); struct queue_data *q_data; unsigned int i; int ret; vpu_dbg(LVL_INFO, "%s()\n", __func__); if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) q_data = &ctx->q_data[V4L2_SRC]; else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) q_data = &ctx->q_data[V4L2_DST]; else return -EINVAL; ret = vb2_querybuf(&q_data->vb2_q, buf); if (!ret) { if (buf->memory == V4L2_MEMORY_MMAP) { if (V4L2_TYPE_IS_MULTIPLANAR(buf->type)) { for (i = 0; i < buf->length; i++) buf->m.planes[i].m.mem_offset |= (q_data->type << MMAP_BUF_TYPE_SHIFT); } else buf->m.offset |= (q_data->type << MMAP_BUF_TYPE_SHIFT); } } return ret; } static int v4l2_ioctl_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf ) { struct vpu_ctx *ctx = v4l2_fh_to_ctx(fh); struct queue_data *q_data; int ret; vpu_dbg(LVL_INFO, "%s()\n", __func__); if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) q_data = &ctx->q_data[V4L2_SRC]; else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) q_data = &ctx->q_data[V4L2_DST]; else return -EINVAL; ret = vb2_qbuf(&q_data->vb2_q, buf); if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) wake_up_interruptible(&ctx->buffer_wq_output); else wake_up_interruptible(&ctx->buffer_wq_input); return ret; } static int v4l2_ioctl_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf ) { struct vpu_ctx *ctx = v4l2_fh_to_ctx(fh); struct queue_data *q_data; int ret; vpu_dbg(LVL_INFO, "%s()\n", __func__); if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) q_data = &ctx->q_data[V4L2_SRC]; else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) q_data = &ctx->q_data[V4L2_DST]; else return -EINVAL; ret = vb2_dqbuf(&q_data->vb2_q, buf, file->f_flags & O_NONBLOCK); if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) buf->flags = q_data->vb2_reqs[buf->index].buffer_flags; return ret; } static bool format_is_support(struct vpu_v4l2_fmt *format_table, unsigned int table_size, struct v4l2_format *f) { unsigned int i; for (i = 0; i < table_size; i++) { if (format_table[i].fourcc == f->fmt.pix_mp.pixelformat) return true; } return false; } static int v4l2_ioctl_try_fmt(struct file *file, void *fh, struct v4l2_format *f ) { struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; unsigned int table_size; if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { table_size = ARRAY_SIZE(formats_compressed_enc); if (!format_is_support(formats_compressed_enc, table_size, f)) return -EINVAL; } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { pix_mp->field = V4L2_FIELD_ANY; pix_mp->colorspace = V4L2_COLORSPACE_REC709; table_size = ARRAY_SIZE(formats_yuv_enc); if (!format_is_support(formats_yuv_enc, table_size, f)) return -EINVAL; } else return -EINVAL; return 0; } static int v4l2_ioctl_g_crop(struct file *file, void *fh, struct v4l2_crop *cr ) { vpu_dbg(LVL_INFO, "%s()\n", __func__); cr->c.left = 0; cr->c.top = 0; cr->c.width = 0; cr->c.height = 0; return 0; } static int v4l2_ioctl_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *cmd ) { struct vpu_ctx *ctx = v4l2_fh_to_ctx(fh); u_int32 uStrIdx = ctx->str_index; vpu_dbg(LVL_INFO, "%s()\n", __func__); switch (cmd->cmd) { case V4L2_ENC_CMD_START: break; case V4L2_ENC_CMD_STOP: ctx->forceStop = true; v4l2_vpu_send_cmd(ctx, uStrIdx, GTB_ENC_CMD_STREAM_STOP, 0, NULL); wake_up_interruptible(&ctx->buffer_wq_input); wake_up_interruptible(&ctx->buffer_wq_output); break; case V4L2_ENC_CMD_PAUSE: break; case V4L2_ENC_CMD_RESUME: break; default: return -EINVAL; } return 0; } static int v4l2_ioctl_streamon(struct file *file, void *fh, enum v4l2_buf_type i ) { struct vpu_ctx *ctx = v4l2_fh_to_ctx(fh); struct queue_data *q_data; int ret; vpu_dbg(LVL_INFO, "%s(), type=%d\n", __func__, i); if (i == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) q_data = &ctx->q_data[V4L2_SRC]; else if (i == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) q_data = &ctx->q_data[V4L2_DST]; else return -EINVAL; ret = vb2_streamon(&q_data->vb2_q, i); ctx->forceStop = false; return ret; } static int v4l2_ioctl_streamoff(struct file *file, void *fh, enum v4l2_buf_type i ) { struct vpu_ctx *ctx = v4l2_fh_to_ctx(fh); struct queue_data *q_data; int ret; vpu_dbg(LVL_INFO, "%s(), type=%d\n", __func__, i); if (i == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) q_data = &ctx->q_data[V4L2_SRC]; else if (i == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) q_data = &ctx->q_data[V4L2_DST]; else return -EINVAL; if (!ctx->start_flag) { if (!ctx->forceStop) { ctx->forceStop = true; v4l2_vpu_send_cmd(ctx, ctx->str_index, GTB_ENC_CMD_STREAM_STOP, 0, NULL); wake_up_interruptible(&ctx->buffer_wq_input); wake_up_interruptible(&ctx->buffer_wq_output); } if (!ctx->firmware_stopped) wait_for_completion(&ctx->stop_cmp); ctx->start_flag = true; } ret = vb2_streamoff(&q_data->vb2_q, i); return ret; } static const struct v4l2_ioctl_ops v4l2_encoder_ioctl_ops = { .vidioc_querycap = v4l2_ioctl_querycap, .vidioc_enum_fmt_vid_cap_mplane = v4l2_ioctl_enum_fmt_vid_cap_mplane, .vidioc_enum_fmt_vid_out_mplane = v4l2_ioctl_enum_fmt_vid_out_mplane, .vidioc_g_fmt_vid_cap_mplane = v4l2_ioctl_g_fmt, .vidioc_g_fmt_vid_out_mplane = v4l2_ioctl_g_fmt, .vidioc_try_fmt_vid_cap_mplane = v4l2_ioctl_try_fmt, .vidioc_try_fmt_vid_out_mplane = v4l2_ioctl_try_fmt, .vidioc_s_fmt_vid_cap_mplane = v4l2_ioctl_s_fmt, .vidioc_s_fmt_vid_out_mplane = v4l2_ioctl_s_fmt, .vidioc_expbuf = v4l2_ioctl_expbuf, .vidioc_g_crop = v4l2_ioctl_g_crop, .vidioc_encoder_cmd = v4l2_ioctl_encoder_cmd, .vidioc_subscribe_event = v4l2_ioctl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, .vidioc_reqbufs = v4l2_ioctl_reqbufs, .vidioc_querybuf = v4l2_ioctl_querybuf, .vidioc_qbuf = v4l2_ioctl_qbuf, .vidioc_dqbuf = v4l2_ioctl_dqbuf, .vidioc_streamon = v4l2_ioctl_streamon, .vidioc_streamoff = v4l2_ioctl_streamoff, }; static int v4l2_enc_s_ctrl(struct v4l2_ctrl *ctrl) { struct vpu_ctx *ctx = v4l2_ctrl_to_ctx(ctrl); struct core_device *dev = &ctx->dev->core_dev[ctx->core_id]; pENC_RPC_HOST_IFACE pSharedInterface = dev->shared_mem.pSharedInterface; pMEDIA_ENC_API_CONTROL_INTERFACE pEncCtrlInterface; pMEDIAIP_ENC_PARAM pEncParam; pMEDIAIP_ENC_EXPERT_MODE_PARAM pEncExpertModeParam; pEncCtrlInterface = (pMEDIA_ENC_API_CONTROL_INTERFACE)phy_to_virt(pSharedInterface->pEncCtrlInterface[ctx->str_index], dev->shared_mem.base_offset); pEncParam = (pMEDIAIP_ENC_PARAM)phy_to_virt(pEncCtrlInterface->pEncParam, dev->shared_mem.base_offset); pEncExpertModeParam = (pMEDIAIP_ENC_EXPERT_MODE_PARAM)phy_to_virt(pEncCtrlInterface->pEncExpertModeParam, dev->shared_mem.base_offset); vpu_dbg(LVL_INFO, "s_ctrl: id = %d, val = %d\n", ctrl->id, ctrl->val); switch (ctrl->id) { case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: { if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) { pEncParam->eBitRateMode = MEDIAIP_ENC_BITRATECONTROLMODE_CONSTANT_QP; // Not used for CQ mode - set zero pEncParam->uTargetBitrate = 0; pEncParam->uMaxBitRate = 0; pEncParam->uMinBitRate = 0; } else if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) { pEncParam->eBitRateMode = MEDIAIP_ENC_BITRATECONTROLMODE_CBR; if (!pEncParam->uTargetBitrate) { // Setup some default values if not set, these should really be // resolution specific pEncParam->uTargetBitrate = ctx->q_data[V4L2_SRC].height * ctx->q_data[V4L2_SRC].width * 12 * 30 / 100000; pEncParam->uMaxBitRate = ctx->q_data[V4L2_SRC].height * ctx->q_data[V4L2_SRC].width * 12 * 30 / 10000; pEncParam->uMinBitRate = ctx->q_data[V4L2_SRC].height * ctx->q_data[V4L2_SRC].width * 12 * 30 / 1000000; } } else // Only CQ and CBR supported at present, force CQ mode pEncParam->eBitRateMode = MEDIAIP_ENC_BITRATECONTROLMODE_CONSTANT_QP; } break; case V4L2_CID_MPEG_VIDEO_H264_PROFILE: if ((V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE == ctrl->val) || (V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE == ctrl->val)) pEncParam->eProfile = MEDIAIP_ENC_PROF_H264_BP; else if (V4L2_MPEG_VIDEO_H264_PROFILE_MAIN == ctrl->val) pEncParam->eProfile = MEDIAIP_ENC_PROF_H264_MP; else if (V4L2_MPEG_VIDEO_H264_PROFILE_HIGH == ctrl->val) pEncParam->eProfile = MEDIAIP_ENC_PROF_H264_HP; else { vpu_dbg(LVL_INFO, "not support h264 profile %d, set default: BP\n", ctrl->val); pEncParam->eProfile = MEDIAIP_ENC_PROF_H264_BP; } break; case V4L2_CID_MPEG_VIDEO_H264_LEVEL: pEncParam->uLevel = h264_lvl[ctrl->val]; vpu_dbg(LVL_INFO, "V4L2_CID_MPEG_VIDEO_H264_LEVEL set val = %d\n", ctrl->val); break; case V4L2_CID_MPEG_VIDEO_BITRATE: pEncParam->uTargetBitrate = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_GOP_SIZE: pEncParam->uIFrameInterval = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: pEncParam->uInitSliceQP = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: pEncParam->uInitSliceQP = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: pEncParam->uInitSliceQP = ctrl->val; break; } return 0; } static int v4l2_enc_g_ctrl(struct v4l2_ctrl *ctrl) { vpu_dbg(LVL_INFO, "g_ctrl: id = %d\n", ctrl->id); switch (ctrl->id) { case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: ctrl->val = MIN_BUFFER_COUNT; break; default: vpu_dbg(LVL_INFO, "%s() Invalid control(%d)\n", __func__, ctrl->id); return -EINVAL; } return 0; } static const struct v4l2_ctrl_ops vpu_enc_ctrl_ops = { .s_ctrl = v4l2_enc_s_ctrl, .g_volatile_ctrl = v4l2_enc_g_ctrl, }; static void vpu_encoder_ctrls(struct vpu_ctx *ctx) { v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &vpu_enc_ctrl_ops, V4L2_CID_MPEG_VIDEO_BITRATE_MODE, V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0x0, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &vpu_enc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH, 0x0, V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE ); v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &vpu_enc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_LEVEL, V4L2_MPEG_VIDEO_H264_LEVEL_5_1, 0x0, V4L2_MPEG_VIDEO_H264_LEVEL_1_0 ); v4l2_ctrl_new_std(&ctx->ctrl_handler, &vpu_enc_ctrl_ops, V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1, 0); v4l2_ctrl_new_std(&ctx->ctrl_handler, &vpu_enc_ctrl_ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 60, 1, 16); v4l2_ctrl_new_std(&ctx->ctrl_handler, &vpu_enc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 0, 51, 1, 25); v4l2_ctrl_new_std(&ctx->ctrl_handler, &vpu_enc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 0, 51, 1, 25); v4l2_ctrl_new_std(&ctx->ctrl_handler, &vpu_enc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, 0, 51, 1, 25); v4l2_ctrl_new_std(&ctx->ctrl_handler, &vpu_enc_ctrl_ops, V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 0, 32, 1, MIN_BUFFER_COUNT); } static int ctrls_setup_encoder(struct vpu_ctx *ctx) { v4l2_ctrl_handler_init(&ctx->ctrl_handler, 2); vpu_encoder_ctrls(ctx); if (ctx->ctrl_handler.error) { vpu_dbg(LVL_ERR, "control initialization error (%d)", ctx->ctrl_handler.error); return -EINVAL; } else ctx->ctrl_inited = true; return v4l2_ctrl_handler_setup(&ctx->ctrl_handler); } static void ctrls_delete_encoder(struct vpu_ctx *This) { int i; if (This->ctrl_inited) { v4l2_ctrl_handler_free(&This->ctrl_handler); This->ctrl_inited = false; } for (i = 0; i < 2; i++) This->ctrls[i] = NULL; } static void v4l2_vpu_send_cmd(struct vpu_ctx *ctx, uint32_t idx, uint32_t cmdid, uint32_t cmdnum, uint32_t *local_cmddata) { struct core_device *dev = &ctx->dev->core_dev[ctx->core_id]; vpu_log_cmd(cmdid, idx); mutex_lock(&dev->cmd_mutex); rpc_send_cmd_buf_encoder(&dev->shared_mem, idx, cmdid, cmdnum, local_cmddata); mutex_unlock(&dev->cmd_mutex); mb(); MU_SendMessage(dev->mu_base_virtaddr, 0, COMMAND); } static void v4l2_transfer_buffer_to_firmware(struct queue_data *This, struct vb2_buffer *vb) { struct vpu_ctx *ctx = container_of(This, struct vpu_ctx, q_data[V4L2_SRC]); #ifdef DUMP_DATA char *read_data; u_int32 read_idx; #endif pBUFFER_DESCRIPTOR_TYPE pEncStrBuffDesc; pMEDIAIP_ENC_EXPERT_MODE_PARAM pEncExpertModeParam; struct core_device *dev = &ctx->dev->core_dev[ctx->core_id]; pENC_RPC_HOST_IFACE pSharedInterface = dev->shared_mem.pSharedInterface; pMEDIA_ENC_API_CONTROL_INTERFACE pEncCtrlInterface; u_int32 uStrIdx = ctx->str_index; vpu_dbg(LVL_INFO, "ENC_RPC_HOST_IFACE(%ld)MEDIA_ENC_API_CONTROL_INTERFACE(%ld) EncYUVBufferDesc(%ld) expertParam(%ld) encparam(%ld) MEDIAIP_ENC_FMT(%ld)\n", sizeof(ENC_RPC_HOST_IFACE), sizeof(MEDIA_ENC_API_CONTROL_INTERFACE), sizeof(BUFFER_DESCRIPTOR_TYPE), sizeof(MEDIAIP_ENC_EXPERT_MODE_PARAM), sizeof(MEDIAIP_ENC_PARAM), sizeof(MEDIAIP_ENC_FMT) ); if (ctx->start_flag == true) { pEncCtrlInterface = (pMEDIA_ENC_API_CONTROL_INTERFACE)phy_to_virt(pSharedInterface->pEncCtrlInterface[uStrIdx], dev->shared_mem.base_offset); pEncStrBuffDesc = (pBUFFER_DESCRIPTOR_TYPE)phy_to_virt(pEncCtrlInterface->pEncStreamBufferDesc, dev->shared_mem.base_offset); pEncStrBuffDesc->start = ctx->encoder_stream.phy_addr; pEncStrBuffDesc->wptr = pEncStrBuffDesc->start; pEncStrBuffDesc->rptr = pEncStrBuffDesc->start; pEncStrBuffDesc->end = ctx->encoder_stream.phy_addr + ctx->encoder_stream.size; vpu_dbg(LVL_INFO, "pEncStrBuffDesc->start=%x, pEncStrBuffDesc->wptr=0x%x, pEncStrBuffDesc->rptr=%x, pEncStrBuffDesc->end=%x\n", pEncStrBuffDesc->start, pEncStrBuffDesc->wptr, pEncStrBuffDesc->rptr, pEncStrBuffDesc->end); pEncExpertModeParam = (pMEDIAIP_ENC_EXPERT_MODE_PARAM)phy_to_virt(pEncCtrlInterface->pEncExpertModeParam, dev->shared_mem.base_offset); pEncExpertModeParam->Calib.mem_chunk_phys_addr = ctx->encoder_mem.phy_addr; pEncExpertModeParam->Calib.mem_chunk_virt_addr = ctx->encoder_mem.phy_addr; pEncExpertModeParam->Calib.mem_chunk_size = ctx->encoder_mem.size; pEncExpertModeParam->Calib.cb_base = ctx->encoder_stream.phy_addr; pEncExpertModeParam->Calib.cb_size = ctx->encoder_stream.size; #ifdef DUMP_DATA read_data = (char *)vb2_plane_vaddr(vb, 0); vpu_dbg(LVL_INFO, "transfer data from virt 0x%p: ", read_data); for (read_idx = 0; read_idx < DATA_NUM; read_idx++) vpu_dbg(LVL_INFO, " 0x%x", read_data[read_idx]); vpu_dbg(LVL_INFO, "\n"); #endif vpu_dbg(LVL_INFO, "enter %s, start_flag %d, index=%d,firmware_started=%d\n", __func__, ctx->start_flag, ctx->str_index, dev->firmware_started); vpu_dbg(LVL_ALL, "vpu encoder firmware version is %d.%d.%d\n", (pSharedInterface->FWVersion & 0x00ff0000) >> 16, (pSharedInterface->FWVersion & 0x0000ff00) >> 8, pSharedInterface->FWVersion & 0x000000ff); v4l2_vpu_send_cmd(ctx, ctx->str_index, GTB_ENC_CMD_CONFIGURE_CODEC, 0, NULL); vpu_dbg(LVL_INFO, "send command GTB_ENC_CMD_CONFIGURE_CODEC\n"); ctx->start_flag = false; } } static bool update_yuv_addr(struct vpu_ctx *ctx, u_int32 uStrIdx) { bool bGotAFrame = FALSE; struct vb2_data_req *p_data_req; struct queue_data *This = &ctx->q_data[V4L2_SRC]; struct core_device *dev = &ctx->dev->core_dev[ctx->core_id]; pENC_RPC_HOST_IFACE pSharedInterface = dev->shared_mem.pSharedInterface; pMEDIA_ENC_API_CONTROL_INTERFACE pEncCtrlInterface; pMEDIAIP_ENC_YUV_BUFFER_DESC pParamYuvBuffDesc; u_int32 *pphy_address; #ifdef DUMP_DATA char *read_data; u_int32 read_idx; #endif pEncCtrlInterface = (pMEDIA_ENC_API_CONTROL_INTERFACE)phy_to_virt(pSharedInterface->pEncCtrlInterface[uStrIdx], dev->shared_mem.base_offset); pParamYuvBuffDesc = (pMEDIAIP_ENC_YUV_BUFFER_DESC)phy_to_virt(pEncCtrlInterface->pEncYUVBufferDesc, dev->shared_mem.base_offset); while (1) { if (!wait_event_interruptible_timeout(ctx->buffer_wq_input, (!list_empty(&This->drv_q)) || ctx->forceStop, msecs_to_jiffies(5000))) { if (!ctx->forceStop) vpu_dbg(LVL_ERR, " warn: yuv wait_event_interruptible_timeout wait 5s timeout\n"); else break; } else break; } if (ctx->forceStop) return false; down(&This->drv_q_lock); if (!list_empty(&This->drv_q)) { p_data_req = list_first_entry(&This->drv_q, typeof(*p_data_req), list); #ifdef DUMP_DATA read_data = (char *)vb2_plane_vaddr(p_data_req->vb2_buf, 0); vpu_dbg(LVL_INFO, "transfer data from virt 0x%p: ", read_data); for (read_idx = 0; read_idx < DATA_NUM; read_idx++) vpu_dbg(LVL_INFO, " 0x%x", read_data[read_idx]); vpu_dbg(LVL_INFO, "\n"); #endif pphy_address = (u_int32 *)vb2_plane_cookie(p_data_req->vb2_buf, 0); pParamYuvBuffDesc->uLumaBase = *pphy_address; pphy_address = (u_int32 *)vb2_plane_cookie(p_data_req->vb2_buf, 1); pParamYuvBuffDesc->uChromaBase = *pphy_address; /* Not sure what the test should be here for a valid frame return from vb2_plane_cookie */ if (pParamYuvBuffDesc->uLumaBase != 0) bGotAFrame = TRUE; /* keeps increasing, so just a frame input count rather than a Frame buffer ID */ pParamYuvBuffDesc->uFrameID = p_data_req->id; list_del(&p_data_req->list); } up(&This->drv_q_lock); return bGotAFrame; } static void report_stream_done(struct vpu_ctx *ctx, MEDIAIP_ENC_PIC_INFO *pEncPicInfo) { struct vb2_data_req *p_data_req; struct queue_data *This = &ctx->q_data[V4L2_DST]; u_int32 wptr; u_int32 rptr; u_int32 start; u_int32 end; void *data_mapped; u_int32 length; u_int32 data_length = 0; void *rptr_virt; pBUFFER_DESCRIPTOR_TYPE pEncStrBuffDesc; pMEDIA_ENC_API_CONTROL_INTERFACE pEncCtrlInterface; struct core_device *dev = &ctx->dev->core_dev[ctx->core_id]; pENC_RPC_HOST_IFACE pSharedInterface = dev->shared_mem.pSharedInterface; /* Windsor stream buffer descriptor * pEncStrBuffDesc = &RecCmdData.tEncStreamBufferDesc; * * VPU driver stream buffer descriptor with full address * pVpuEncStrBuffDesc * * * Note the wprt is updated prior to calling this function */ pEncCtrlInterface = (pMEDIA_ENC_API_CONTROL_INTERFACE)phy_to_virt(pSharedInterface->pEncCtrlInterface[ctx->str_index], dev->shared_mem.base_offset); pEncStrBuffDesc = (pBUFFER_DESCRIPTOR_TYPE)phy_to_virt(pEncCtrlInterface->pEncStreamBufferDesc, dev->shared_mem.base_offset); wptr = pEncStrBuffDesc->wptr | 0x80000000; rptr = pEncStrBuffDesc->rptr | 0x80000000; start = pEncStrBuffDesc->start | 0x80000000; end = pEncStrBuffDesc->end | 0x80000000; rptr_virt = ctx->encoder_stream.virt_addr + rptr - start; vpu_dbg(LVL_INFO, "report_stream_done eptr=%x, rptr=%x, start=%x, end=%x\n", wptr, rptr, start, end); while (1) { if (!wait_event_interruptible_timeout(ctx->buffer_wq_output, (!list_empty(&This->drv_q)), msecs_to_jiffies(5000))) { if (!ctx->forceStop) vpu_dbg(LVL_ERR, " warn: stream wait_event_interruptible_timeout wait 5s timeout\n"); else break; } else break; } if (!list_empty(&This->drv_q)) { down(&This->drv_q_lock); vpu_dbg(LVL_INFO, "report_stream_done down\n"); p_data_req = list_first_entry(&This->drv_q, typeof(*p_data_req), list); vpu_dbg(LVL_INFO, "%s :p_data_req(%p)\n", __func__, p_data_req); vpu_dbg(LVL_INFO, "%s buf_id %d\n", __func__, p_data_req->vb2_buf->index); // Calculate length - the amount of space remaining in output stream buffer length = p_data_req->vb2_buf->planes[0].length; data_mapped = (void *)vb2_plane_vaddr(p_data_req->vb2_buf, 0); if (wptr == rptr && rptr != start) data_length = end - start; else if (rptr < wptr) data_length = wptr - rptr; else if (rptr > wptr) data_length = (end - rptr) + (wptr - start); //update the bytesused for the output buffer if (data_length >= length) p_data_req->vb2_buf->planes[0].bytesused = length; else p_data_req->vb2_buf->planes[0].bytesused = data_length; vpu_dbg(LVL_INFO, "%s data_length %d, length %d\n", __func__, data_length, length); /* Following calculations determine how much data we can transfer into p_vb2_buf * and then only copy that ammount, so rptr is the actual consumed ammount at the end*/ if ((wptr == rptr) || (rptr > wptr)) { if (end - rptr >= length) { memcpy(data_mapped, rptr_virt, length); rptr += length; if (rptr == end) rptr = start; } else { memcpy(data_mapped, rptr_virt, end-rptr); if ((length-(end-rptr)) >= (wptr-start)) { memcpy(data_mapped + (end-rptr), ctx->encoder_stream.virt_addr, wptr-start); rptr = wptr; } else { memcpy(data_mapped + (end-rptr), ctx->encoder_stream.virt_addr, length-(end-rptr)); rptr = start+length-(end-rptr); } } } else { if (wptr - rptr >= length) { memcpy(data_mapped, rptr_virt, length); rptr += length; } else { memcpy(data_mapped, rptr_virt, wptr - rptr); rptr = wptr; } } /* Update VPU stream buffer descriptor and Windsor FW stream buffer descriptors respectively*/ pEncStrBuffDesc->rptr = rptr; list_del(&p_data_req->list); up(&This->drv_q_lock); if (pEncPicInfo->ePicType == MEDIAIP_ENC_PIC_TYPE_IDR_FRAME || pEncPicInfo->ePicType == MEDIAIP_ENC_PIC_TYPE_I_FRAME) p_data_req->buffer_flags = V4L2_BUF_FLAG_KEYFRAME; else if (pEncPicInfo->ePicType == MEDIAIP_ENC_PIC_TYPE_P_FRAME) p_data_req->buffer_flags = V4L2_BUF_FLAG_PFRAME; else if (pEncPicInfo->ePicType == MEDIAIP_ENC_PIC_TYPE_B_FRAME) p_data_req->buffer_flags = V4L2_BUF_FLAG_BFRAME; //memcpy to vb2 buffer from encpicinfo if (p_data_req->vb2_buf->state == VB2_BUF_STATE_ACTIVE) vb2_buffer_done(p_data_req->vb2_buf, VB2_BUF_STATE_DONE); } vpu_dbg(LVL_INFO, "report_buffer_done return\n"); } static void enc_mem_alloc(struct vpu_ctx *ctx, MEDIAIP_ENC_MEM_REQ_DATA *req_data) { pMEDIA_ENC_API_CONTROL_INTERFACE pEncCtrlInterface; pMEDIAIP_ENC_MEM_POOL pEncMemPool; struct core_device *core_dev = &ctx->dev->core_dev[ctx->core_id]; pENC_RPC_HOST_IFACE pSharedInterface = core_dev->shared_mem.pSharedInterface; u_int32 i; pEncCtrlInterface = (pMEDIA_ENC_API_CONTROL_INTERFACE)phy_to_virt(pSharedInterface->pEncCtrlInterface[ctx->str_index], core_dev->shared_mem.base_offset); pEncMemPool = (pMEDIAIP_ENC_MEM_POOL)phy_to_virt(pEncCtrlInterface->pEncMemPool, core_dev->shared_mem.base_offset); for (i = 0; i < req_data->uEncFrmNum; i++) { ctx->encFrame[i].size = req_data->uEncFrmSize; if (!ctx->encFrame[i].virt_addr) { ctx->encFrame[i].virt_addr = dma_alloc_coherent(&ctx->dev->plat_dev->dev, ctx->encFrame[i].size, (dma_addr_t *)&ctx->encFrame[i].phy_addr, GFP_KERNEL | GFP_DMA32 ); if (!ctx->encFrame[i].virt_addr) vpu_dbg(LVL_ERR, "%s() encFrame alloc size(%x) fail!\n", __func__, ctx->encFrame[i].size); else vpu_dbg(LVL_INFO, "%s() encFrame size(%d) encFrame virt(%p) encFrame phy(%p)\n", __func__, ctx->encFrame[i].size, ctx->encFrame[i].virt_addr, (void *)ctx->encFrame[i].phy_addr); } pEncMemPool->tEncFrameBuffers[i].uMemPhysAddr = ctx->encFrame[i].phy_addr; #ifdef CM4 pEncMemPool->tEncFrameBuffers[i].uMemVirtAddr = ctx->encFrame[i].phy_addr; #else pEncMemPool->tEncFrameBuffers[i].uMemVirtAddr = ctx->encFrame[i].phy_addr - core_dev->m0_p_fw_space_phy; #endif pEncMemPool->tEncFrameBuffers[i].uMemSize = ctx->encFrame[i].size; } for (i = 0; i < req_data->uRefFrmNum; i++) { ctx->refFrame[i].size = req_data->uRefFrmSize; if (!ctx->refFrame[i].virt_addr) { ctx->refFrame[i].virt_addr = dma_alloc_coherent(&ctx->dev->plat_dev->dev, ctx->refFrame[i].size, (dma_addr_t *)&ctx->refFrame[i].phy_addr, GFP_KERNEL | GFP_DMA32 ); if (!ctx->refFrame[i].virt_addr) vpu_dbg(LVL_ERR, "%s() refFrame alloc size(%x) fail!\n", __func__, ctx->refFrame[i].size); else vpu_dbg(LVL_INFO, "%s() refFrame size(%d) refFrame virt(%p) refFrame phy(%p)\n", __func__, ctx->refFrame[i].size, ctx->refFrame[i].virt_addr, (void *)ctx->refFrame[i].phy_addr); } pEncMemPool->tRefFrameBuffers[i].uMemPhysAddr = ctx->refFrame[i].phy_addr; #ifdef CM4 pEncMemPool->tRefFrameBuffers[i].uMemVirtAddr = ctx->refFrame[i].phy_addr; #else pEncMemPool->tRefFrameBuffers[i].uMemVirtAddr = ctx->refFrame[i].phy_addr - core_dev->m0_p_fw_space_phy; #endif pEncMemPool->tRefFrameBuffers[i].uMemSize = ctx->refFrame[i].size; } ctx->actFrame.size = req_data->uActBufSize; if (!ctx->actFrame.virt_addr) { ctx->actFrame.virt_addr = dma_alloc_coherent(&ctx->dev->plat_dev->dev, ctx->actFrame.size, (dma_addr_t *)&ctx->actFrame.phy_addr, GFP_KERNEL | GFP_DMA32 ); if (!ctx->actFrame.virt_addr) vpu_dbg(LVL_ERR, "%s() actFrame alloc size(%x) fail!\n", __func__, ctx->actFrame.size); else vpu_dbg(LVL_INFO, "%s() actFrame size(%d) actFrame virt(%p) actFrame phy(%p)\n", __func__, ctx->actFrame.size, ctx->actFrame.virt_addr, (void *)ctx->actFrame.phy_addr); } pEncMemPool->tActFrameBufferArea.uMemPhysAddr = ctx->actFrame.phy_addr; #ifdef CM4 pEncMemPool->tActFrameBufferArea.uMemVirtAddr = ctx->actFrame.phy_addr; #else pEncMemPool->tActFrameBufferArea.uMemVirtAddr = ctx->actFrame.phy_addr - core_dev->m0_p_fw_space_phy; #endif pEncMemPool->tActFrameBufferArea.uMemSize = ctx->actFrame.size; } static void vpu_api_event_handler(struct vpu_ctx *ctx, u_int32 uStrIdx, u_int32 uEvent, u_int32 *event_data) { vpu_log_event(uEvent, uStrIdx); if (uStrIdx < VID_API_NUM_STREAMS) { switch (uEvent) { case VID_API_ENC_EVENT_START_DONE: { if (update_yuv_addr(ctx, uStrIdx)) v4l2_vpu_send_cmd(ctx, uStrIdx, GTB_ENC_CMD_FRAME_ENCODE, 0, NULL); } break; case VID_API_ENC_EVENT_MEM_REQUEST: { MEDIAIP_ENC_MEM_REQ_DATA *req_data = (MEDIAIP_ENC_MEM_REQ_DATA *)event_data; vpu_dbg(LVL_INFO, "uEncFrmSize = %d, uEncFrmNum=%d, uRefFrmSize=%d, uRefFrmNum=%d, uActBufSize=%d\n", req_data->uEncFrmSize, req_data->uEncFrmNum, req_data->uRefFrmSize, req_data->uRefFrmNum, req_data->uActBufSize); enc_mem_alloc(ctx, req_data); //update_yuv_addr(ctx,0); v4l2_vpu_send_cmd(ctx, uStrIdx, GTB_ENC_CMD_STREAM_START, 0, NULL); } break; case VID_API_ENC_EVENT_PARA_UPD_DONE: break; case VID_API_ENC_EVENT_FRAME_DONE: { MEDIAIP_ENC_PIC_INFO *pEncPicInfo = (MEDIAIP_ENC_PIC_INFO *)event_data; vpu_dbg(LVL_INFO, "VID_API_ENC_EVENT_FRAME_DONE pEncPicInfo->uPicEncodDone=%d: Encode picture done\n", pEncPicInfo->uPicEncodDone); if (pEncPicInfo->uPicEncodDone) { #ifdef TB_REC_DBG vpu_dbg(LVL_INFO, " - Frame ID : 0x%x\n", pEncPicInfo->uFrameID); vpu_dbg(LVL_INFO, " - Picture Type : %s\n", pEncPicInfo->ePicType == MEDIAIP_ENC_PIC_TYPE_B_FRAME ? "B picture" : pEncPicInfo->ePicType == MEDIAIP_ENC_PIC_TYPE_P_FRAME ? "P picture" : pEncPicInfo->ePicType == MEDIAIP_ENC_PIC_TYPE_I_FRAME ? "I picture" : pEncPicInfo->ePicType == MEDIAIP_ENC_PIC_TYPE_IDR_FRAME ? "IDR picture" : "BI picture"); vpu_dbg(LVL_INFO, " - Skipped frame : 0x%x\n", pEncPicInfo->uSkippedFrame); vpu_dbg(LVL_INFO, " - Frame size : 0x%x\n", pEncPicInfo->uFrameSize); vpu_dbg(LVL_INFO, " - Frame CRC : 0x%x\n", pEncPicInfo->uFrameCrc); #endif /* Sync the write pointer to the local view of it */ report_stream_done(ctx, pEncPicInfo); } } break; case VID_API_ENC_EVENT_FRAME_RELEASE: { struct queue_data *This = &ctx->q_data[V4L2_SRC]; u_int32 *uFrameID = (u_int32 *)event_data; struct vb2_data_req *p_data_req; vpu_dbg(LVL_INFO, "VID_API_ENC_EVENT_FRAME_RELEASE : Frame release - uFrameID = 0x%x\n", *uFrameID); p_data_req = &This->vb2_reqs[*uFrameID]; if (p_data_req->vb2_buf->state == VB2_BUF_STATE_ACTIVE) vb2_buffer_done(p_data_req->vb2_buf, VB2_BUF_STATE_DONE ); } break; case VID_API_ENC_EVENT_STOP_DONE: { const struct v4l2_event ev = { .type = V4L2_EVENT_EOS }; ctx->firmware_stopped = true; complete_all(&ctx->stop_cmp); v4l2_event_queue_fh(&ctx->fh, &ev); } break; case VID_API_ENC_EVENT_FRAME_INPUT_DONE: { if (update_yuv_addr(ctx, uStrIdx)) v4l2_vpu_send_cmd(ctx, uStrIdx, GTB_ENC_CMD_FRAME_ENCODE, 0, NULL); } break; case VID_API_ENC_EVENT_TERMINATE_DONE: break; default: vpu_dbg(LVL_INFO, "........unknown event : 0x%x\n", uEvent); break; } } } //This code is added for MU static irqreturn_t fsl_vpu_mu_isr(int irq, void *This) { struct core_device *dev = This; u32 msg; MU_ReceiveMsg(dev->mu_base_virtaddr, 0, &msg); if (msg == 0xaa) { MU_sendMesgToFW(dev->mu_base_virtaddr, PRINT_BUF_OFFSET, dev->m0_rpc_phy - dev->m0_p_fw_space_phy + M0_PRINT_OFFSET); MU_sendMesgToFW(dev->mu_base_virtaddr, RPC_BUF_OFFSET, dev->m0_rpc_phy - dev->m0_p_fw_space_phy); //CM0 use relative address MU_sendMesgToFW(dev->mu_base_virtaddr, BOOT_ADDRESS, dev->m0_p_fw_space_phy); MU_sendMesgToFW(dev->mu_base_virtaddr, INIT_DONE, 2); } else if (msg == 0x55) { dev->firmware_started = true; complete(&dev->start_cmp); } else if (msg == 0xA5) { /*receive snapshot done msg and wakeup complete to suspend*/ complete(&dev->snap_done_cmp); } else schedule_work(&dev->msg_work); return IRQ_HANDLED; } /* Initialization of the MU code. */ static int vpu_mu_init(struct vpu_dev *dev, u_int32 id) { struct device_node *np; unsigned int vpu_mu_id; u32 irq; struct core_device *core_dev = &dev->core_dev[id]; int ret = 0; /* * Get the address of MU to be used for communication with the M0 core */ #ifdef CM4 np = of_find_compatible_node(NULL, NULL, "fsl,imx8-mu0-vpu-m4"); if (!np) { vpu_dbg(LVL_ERR, "error: Cannot find MU entry in device tree\n"); return -EINVAL; } #else np = of_find_compatible_node(NULL, NULL, mu_cmp[id]); if (!np) { vpu_dbg(LVL_ERR, "error: Cannot find MU entry in device tree\n"); return -EINVAL; } #endif core_dev->mu_base_virtaddr = of_iomap(np, 0); WARN_ON(!core_dev->mu_base_virtaddr); ret = of_property_read_u32_index(np, "fsl,vpu_ap_mu_id", 0, &vpu_mu_id); if (ret) { vpu_dbg(LVL_ERR, "Cannot get mu_id %d\n", ret); return -EINVAL; } core_dev->vpu_mu_id = vpu_mu_id; irq = of_irq_get(np, 0); ret = devm_request_irq(&dev->plat_dev->dev, irq, fsl_vpu_mu_isr, IRQF_EARLY_RESUME, "vpu_mu_isr", (void *)core_dev); if (ret) { vpu_dbg(LVL_ERR, "request_irq failed %d, error = %d\n", irq, ret); return -EINVAL; } if (!core_dev->vpu_mu_init) { MU_Init(core_dev->mu_base_virtaddr); MU_EnableRxFullInt(core_dev->mu_base_virtaddr, 0); core_dev->vpu_mu_init = 1; } return ret; } static int vpu_next_free_instance(struct vpu_dev *dev, struct vpu_ctx *ctx) { int idx; int idx0 = hweight32(dev->core_dev[0].instance_mask); int idx1 = hweight32(dev->core_dev[1].instance_mask); if (idx0 <= idx1 && idx0 < VPU_MAX_NUM_STREAMS) { idx = ffz(dev->core_dev[0].instance_mask); ctx->core_id = 0; } else if (idx1 < VPU_MAX_NUM_STREAMS) { idx = ffz(dev->core_dev[1].instance_mask); ctx->core_id = 1; } else return -EBUSY; return idx; } static void send_msg_queue(struct vpu_ctx *ctx, struct event_msg *msg) { u_int32 ret; ret = kfifo_in(&ctx->msg_fifo, msg, sizeof(struct event_msg)); if (ret != sizeof(struct event_msg)) vpu_dbg(LVL_ERR, "There is no memory for msg fifo, ret=%d\n", ret); } static bool receive_msg_queue(struct vpu_ctx *ctx, struct event_msg *msg) { u_int32 ret; if (kfifo_len(&ctx->msg_fifo) >= sizeof(*msg)) { ret = kfifo_out(&ctx->msg_fifo, msg, sizeof(*msg)); if (ret != sizeof(*msg)) { vpu_dbg(LVL_ERR, "kfifo_out has error, ret=%d\n", ret); return false; } else return true; } else return false; } extern u_int32 rpc_MediaIPFW_Video_message_check_encoder(struct shared_addr *This); static void vpu_msg_run_work(struct work_struct *work) { struct core_device *dev = container_of(work, struct core_device, msg_work); struct vpu_ctx *ctx; struct event_msg msg; struct shared_addr *This = &dev->shared_mem; while (rpc_MediaIPFW_Video_message_check_encoder(This) == API_MSG_AVAILABLE) { rpc_receive_msg_buf_encoder(This, &msg); mutex_lock(&dev->core_mutex); ctx = dev->ctx[msg.idx]; if (ctx != NULL) { mutex_lock(&ctx->instance_mutex); if (!ctx->ctx_released) { send_msg_queue(ctx, &msg); queue_work(ctx->instance_wq, &ctx->instance_work); } mutex_unlock(&ctx->instance_mutex); } mutex_unlock(&dev->core_mutex); } if (rpc_MediaIPFW_Video_message_check_encoder(This) == API_MSG_BUFFER_ERROR) vpu_dbg(LVL_ERR, "MSG num is too big to handle"); } static void vpu_msg_instance_work(struct work_struct *work) { struct vpu_ctx *ctx = container_of(work, struct vpu_ctx, instance_work); struct event_msg msg; while (receive_msg_queue(ctx, &msg)) vpu_api_event_handler(ctx, msg.idx, msg.msgid, msg.msgdata); } static int vpu_queue_setup(struct vb2_queue *vq, unsigned int *buf_count, unsigned int *plane_count, unsigned int psize[], struct device *allocators[]) { struct queue_data *This = (struct queue_data *)vq->drv_priv; vpu_dbg(LVL_INFO, "%s() is called\n", __func__); if ((vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) || (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ) { *plane_count = 1; psize[0] = This->sizeimage[0];//check alignment } else { if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { *plane_count = 2; psize[0] = This->sizeimage[0];//check alignment psize[1] = This->sizeimage[1];//check colocated_size } else { psize[0] = This->sizeimage[0] + This->sizeimage[1]; *plane_count = 1; } } return 0; } static int vpu_buf_prepare(struct vb2_buffer *vb) { vpu_dbg(LVL_INFO, "%s() is called\n", __func__); return 0; } static int vpu_start_streaming(struct vb2_queue *q, unsigned int count ) { vpu_dbg(LVL_INFO, "%s() is called\n", __func__); return 0; } static void vpu_stop_streaming(struct vb2_queue *q) { struct queue_data *This = (struct queue_data *)q->drv_priv; struct vb2_data_req *p_data_req; struct vb2_data_req *p_temp; struct vb2_buffer *vb; vpu_dbg(LVL_INFO, "%s() is called\n", __func__); down(&This->drv_q_lock); if (!list_empty(&This->drv_q)) { list_for_each_entry_safe(p_data_req, p_temp, &This->drv_q, list) { vpu_dbg(LVL_INFO, "%s(%d) - list_del(%p)\n", __func__, p_data_req->id, p_data_req ); list_del(&p_data_req->list); } } if (!list_empty(&q->queued_list)) list_for_each_entry(vb, &q->queued_list, queued_entry) if (vb->state == VB2_BUF_STATE_ACTIVE) vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); INIT_LIST_HEAD(&This->drv_q); up(&This->drv_q_lock); } static void vpu_buf_queue(struct vb2_buffer *vb) { struct vb2_queue *vq = vb->vb2_queue; struct queue_data *This = (struct queue_data *)vq->drv_priv; struct vb2_data_req *data_req; vpu_dbg(LVL_INFO, "%s() is called\n", __func__); down(&This->drv_q_lock); vpu_dbg(LVL_INFO, "c_port_buf_queue down\n"); data_req = &This->vb2_reqs[vb->index]; data_req->vb2_buf = vb; data_req->id = vb->index; list_add_tail(&data_req->list, &This->drv_q); up(&This->drv_q_lock); vpu_dbg(LVL_INFO, "c_port_buf_queue up vq->type=%d\n", vq->type); if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) v4l2_transfer_buffer_to_firmware(This, vb); } static void vpu_prepare(struct vb2_queue *q) { vpu_dbg(LVL_INFO, "%s() is called\n", __func__); } static void vpu_finish(struct vb2_queue *q) { vpu_dbg(LVL_INFO, "%s() is called\n", __func__); } static struct vb2_ops v4l2_qops = { .queue_setup = vpu_queue_setup, .wait_prepare = vpu_prepare, .wait_finish = vpu_finish, .buf_prepare = vpu_buf_prepare, .start_streaming = vpu_start_streaming, .stop_streaming = vpu_stop_streaming, .buf_queue = vpu_buf_queue, }; static void init_vb2_queue(struct queue_data *This, unsigned int type, struct vpu_ctx *ctx) { struct vb2_queue *vb2_q = &This->vb2_q; int ret; vpu_dbg(LVL_INFO, "%s()\n", __func__); // initialze driver queue INIT_LIST_HEAD(&This->drv_q); // initialize vb2 queue vb2_q->type = type; vb2_q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; vb2_q->gfp_flags = GFP_DMA32; vb2_q->ops = &v4l2_qops; vb2_q->drv_priv = This; vb2_q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops; vb2_q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; vb2_q->dev = &ctx->dev->plat_dev->dev; ret = vb2_queue_init(vb2_q); if (ret) vpu_dbg(LVL_ERR, "%s vb2_queue_init() failed (%d)!\n", __func__, ret ); else This->vb2_q_inited = true; } static void init_queue_data(struct vpu_ctx *ctx) { init_vb2_queue(&ctx->q_data[V4L2_SRC], V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, ctx); ctx->q_data[V4L2_SRC].type = V4L2_SRC; sema_init(&ctx->q_data[V4L2_SRC].drv_q_lock, 1); init_vb2_queue(&ctx->q_data[V4L2_DST], V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, ctx); ctx->q_data[V4L2_DST].type = V4L2_DST; sema_init(&ctx->q_data[V4L2_DST].drv_q_lock, 1); } static void release_queue_data(struct vpu_ctx *ctx) { struct queue_data *This = &ctx->q_data[V4L2_SRC]; if (This->vb2_q_inited) vb2_queue_release(&This->vb2_q); This = &ctx->q_data[V4L2_DST]; if (This->vb2_q_inited) vb2_queue_release(&This->vb2_q); } #ifdef CM4 static int power_CM4_up(struct vpu_dev *dev) { sc_ipc_t ipcHndl; sc_rsrc_t core_rsrc, mu_rsrc = -1; ipcHndl = dev->mu_ipcHandle; core_rsrc = SC_R_M4_0_PID0; mu_rsrc = SC_R_M4_0_MU_1A; if (sc_pm_set_resource_power_mode(ipcHndl, core_rsrc, SC_PM_PW_MODE_ON) != SC_ERR_NONE) { vpu_dbg(LVL_ERR, "error: failed to power up core_rsrc\n"); return -EIO; } if (mu_rsrc != -1) { if (sc_pm_set_resource_power_mode(ipcHndl, mu_rsrc, SC_PM_PW_MODE_ON) != SC_ERR_NONE) { vpu_dbg(LVL_ERR, "error: failed to power up mu_rsrc\n"); return -EIO; } } return 0; } static int boot_CM4_up(struct vpu_dev *dev, void *boot_addr) { sc_ipc_t ipcHndl; sc_rsrc_t core_rsrc; sc_faddr_t aux_core_ram; void *core_ram_vir; u32 size; ipcHndl = dev->mu_ipcHandle; core_rsrc = SC_R_M4_0_PID0; aux_core_ram = 0x34FE0000; size = SZ_128K; core_ram_vir = ioremap_wc(aux_core_ram, size ); if (!core_ram_vir) vpu_dbg(LVL_ERR, "error: failed to remap space for core ram\n"); memcpy((void *)core_ram_vir, (void *)boot_addr, size); if (sc_pm_cpu_start(ipcHndl, core_rsrc, true, aux_core_ram) != SC_ERR_NONE) { vpu_dbg(LVL_ERR, "error: failed to start core_rsrc\n"); return -EIO; } return 0; } #endif static int vpu_firmware_download(struct vpu_dev *This, u_int32 core_id) { unsigned char *image; unsigned int FW_Size = 0; void *csr_offset, *csr_cpuwait; int ret = 0; char *p = This->core_dev[core_id].m0_p_fw_space_vir; ret = request_firmware((const struct firmware **)&This->m0_pfw, M0FW_FILENAME, This->generic_dev ); if (ret) { vpu_dbg(LVL_ERR, "%s() request fw %s failed(%d)\n", __func__, M0FW_FILENAME, ret); if (This->m0_pfw) { release_firmware(This->m0_pfw); This->m0_pfw = NULL; } return ret; } else { vpu_dbg(LVL_INFO, "%s() request fw %s got size(%d)\n", __func__, M0FW_FILENAME, (int)This->m0_pfw->size); image = (uint8_t *)This->m0_pfw->data; FW_Size = This->m0_pfw->size; } memcpy(This->core_dev[core_id].m0_p_fw_space_vir, image, FW_Size ); if (This->plat_type == IMX8QM) { //encoder use core 1,2 if (core_id == 0) { p[16] = IMX8QM; p[17] = core_id + 1; csr_offset = ioremap(0x2d090000, 4); writel(This->core_dev[core_id].m0_p_fw_space_phy, csr_offset); csr_cpuwait = ioremap(0x2d090004, 4); writel(0x0, csr_cpuwait); } else { p[16] = IMX8QM; p[17] = core_id + 1; csr_offset = ioremap(0x2d0a0000, 4); writel(This->core_dev[core_id].m0_p_fw_space_phy, csr_offset); csr_cpuwait = ioremap(0x2d0a0004, 4); writel(0x0, csr_cpuwait); } } else { p[16] = IMX8QXP; p[17] = core_id + 1; csr_offset = ioremap(0x2d050000, 4); writel(This->core_dev[core_id].m0_p_fw_space_phy, csr_offset); csr_cpuwait = ioremap(0x2d050004, 4); writel(0x0, csr_cpuwait); } return ret; } static int v4l2_open(struct file *filp) { struct video_device *vdev = video_devdata(filp); struct vpu_dev *dev = video_get_drvdata(vdev); struct vpu_ctx *ctx = NULL; int idx; int ret; u_int32 i; vpu_dbg(LVL_INFO, "%s()\n", __func__); pm_runtime_get_sync(dev->generic_dev); ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; mutex_lock(&dev->dev_mutex); idx = vpu_next_free_instance(dev, ctx); if (idx < 0) { ret = idx; mutex_unlock(&dev->dev_mutex); goto err_find_index; } set_bit(idx, &dev->core_dev[ctx->core_id].instance_mask); mutex_unlock(&dev->dev_mutex); init_completion(&ctx->completion); init_completion(&ctx->stop_cmp); v4l2_fh_init(&ctx->fh, video_devdata(filp)); filp->private_data = &ctx->fh; v4l2_fh_add(&ctx->fh); ctx->str_index = idx; ctx->dev = dev; ctx->ctrl_inited = false; ctrls_setup_encoder(ctx); ctx->fh.ctrl_handler = &ctx->ctrl_handler; ctx->instance_wq = alloc_workqueue("vpu_instance", WQ_UNBOUND | WQ_MEM_RECLAIM, 1); if (!ctx->instance_wq) { vpu_dbg(LVL_ERR, "error: %s unable to alloc workqueue for ctx\n", __func__); ret = -ENOMEM; goto err_alloc; } INIT_WORK(&ctx->instance_work, vpu_msg_instance_work); mutex_init(&ctx->instance_mutex); if (kfifo_alloc(&ctx->msg_fifo, sizeof(struct event_msg) * VID_API_MESSAGE_LIMIT, GFP_KERNEL)) { vpu_dbg(LVL_ERR, "fail to alloc fifo when open\n"); ret = -ENOMEM; goto err_alloc; } dev->core_dev[ctx->core_id].ctx[idx] = ctx; ctx->b_firstseq = true; ctx->start_flag = true; ctx->forceStop = false; ctx->firmware_stopped = false; ctx->ctx_released = false; init_queue_data(ctx); init_waitqueue_head(&ctx->buffer_wq_output); init_waitqueue_head(&ctx->buffer_wq_input); mutex_lock(&dev->dev_mutex); if (!dev->core_dev[ctx->core_id].fw_is_ready) { ret = vpu_firmware_download(dev, ctx->core_id); if (ret) { vpu_dbg(LVL_ERR, "error: vpu_firmware_download fail\n"); mutex_unlock(&dev->dev_mutex); goto err_firmware_load; } if (!ctx->dev->core_dev[ctx->core_id].firmware_started) wait_for_completion(&ctx->dev->core_dev[ctx->core_id].start_cmp); dev->core_dev[ctx->core_id].fw_is_ready = true; } mutex_unlock(&dev->dev_mutex); ctx->encoder_stream.size = STREAM_SIZE; ctx->encoder_stream.virt_addr = dma_alloc_coherent(&ctx->dev->plat_dev->dev, ctx->encoder_stream.size, (dma_addr_t *)&ctx->encoder_stream.phy_addr, GFP_KERNEL | GFP_DMA32 ); if (!ctx->encoder_stream.virt_addr) vpu_dbg(LVL_ERR, "%s() encoder stream buffer alloc size(%x) fail!\n", __func__, ctx->encoder_stream.size); else vpu_dbg(LVL_INFO, "%s() encoder_stream_size(%d) encoder_stream_virt(%p) encoder_stream_phy(%p)\n", __func__, ctx->encoder_stream.size, ctx->encoder_stream.virt_addr, (void *)ctx->encoder_stream.phy_addr); ctx->encoder_mem.size = 0; ctx->encoder_mem.virt_addr = NULL; ctx->encoder_mem.phy_addr = 0; for (i = 0; i < MEDIAIP_MAX_NUM_WINDSOR_SRC_FRAMES; i++) { ctx->encFrame[i].virt_addr = NULL; ctx->encFrame[i].phy_addr = 0; ctx->encFrame[i].size = 0; } for (i = 0; i < MEDIAIP_MAX_NUM_WINDSOR_REF_FRAMES; i++) { ctx->refFrame[i].virt_addr = NULL; ctx->refFrame[i].phy_addr = 0; ctx->refFrame[i].size = 0; } ctx->actFrame.virt_addr = NULL; ctx->actFrame.phy_addr = 0; ctx->actFrame.size = 0; return 0; err_firmware_load: release_queue_data(ctx); ctrls_delete_encoder(ctx); v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); clear_bit(ctx->str_index, &dev->core_dev[ctx->core_id].instance_mask); err_find_index: pm_runtime_put_sync(dev->generic_dev); kfree(ctx); return ret; err_alloc: pm_runtime_put_sync(dev->generic_dev); kfree(ctx); return ret; } static int v4l2_release(struct file *filp) { struct video_device *vdev = video_devdata(filp); struct vpu_dev *dev = video_get_drvdata(vdev); struct vpu_ctx *ctx = v4l2_fh_to_ctx(filp->private_data); struct core_device *core_dev = &dev->core_dev[ctx->core_id]; u_int32 i; vpu_dbg(LVL_INFO, "%s()\n", __func__); if (!ctx->forceStop && !ctx->start_flag) { //need send stop if app call release without calling of V4L2_ENC_CMD_STOP ctx->forceStop = true; v4l2_vpu_send_cmd(ctx, ctx->str_index, GTB_ENC_CMD_STREAM_STOP, 0, NULL); wake_up_interruptible(&ctx->buffer_wq_input); wake_up_interruptible(&ctx->buffer_wq_output); } if (!ctx->firmware_stopped && ctx->start_flag == false) wait_for_completion(&ctx->stop_cmp); release_queue_data(ctx); ctrls_delete_encoder(ctx); v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); mutex_lock(&core_dev->core_mutex); clear_bit(ctx->str_index, &core_dev->instance_mask); mutex_unlock(&core_dev->core_mutex); dma_free_coherent(&ctx->dev->plat_dev->dev, ctx->encoder_stream.size, ctx->encoder_stream.virt_addr, ctx->encoder_stream.phy_addr ); for (i = 0; i < MEDIAIP_MAX_NUM_WINDSOR_SRC_FRAMES; i++) if (ctx->encFrame[i].virt_addr != NULL) dma_free_coherent(&ctx->dev->plat_dev->dev, ctx->encFrame[i].size, ctx->encFrame[i].virt_addr, ctx->encFrame[i].phy_addr ); for (i = 0; i < MEDIAIP_MAX_NUM_WINDSOR_REF_FRAMES; i++) if (ctx->refFrame[i].virt_addr != NULL) dma_free_coherent(&ctx->dev->plat_dev->dev, ctx->refFrame[i].size, ctx->refFrame[i].virt_addr, ctx->refFrame[i].phy_addr ); if (ctx->actFrame.virt_addr != NULL) dma_free_coherent(&ctx->dev->plat_dev->dev, ctx->actFrame.size, ctx->actFrame.virt_addr, ctx->actFrame.phy_addr ); mutex_lock(&ctx->instance_mutex); ctx->ctx_released = true; kfifo_free(&ctx->msg_fifo); destroy_workqueue(ctx->instance_wq); mutex_unlock(&ctx->instance_mutex); mutex_lock(&core_dev->core_mutex); if (!(core_dev->hang_mask & (1 << ctx->str_index))) // judge the path is hang or not, if hang, don't clear clear_bit(ctx->str_index, &core_dev->instance_mask); // dev->ctx[ctx->str_index] = NULL; mutex_unlock(&core_dev->core_mutex); pm_runtime_put_sync(dev->generic_dev); kfree(ctx); return 0; } static unsigned int v4l2_poll(struct file *filp, poll_table *wait) { struct vpu_ctx *ctx = v4l2_fh_to_ctx(filp->private_data); struct vb2_queue *src_q, *dst_q; unsigned int rc = 0; vpu_dbg(LVL_INFO, "%s()\n", __func__); if (ctx) { poll_wait(filp, &ctx->fh.wait, wait); if (v4l2_event_pending(&ctx->fh)) { vpu_dbg(LVL_INFO, "%s() v4l2_event_pending\n", __func__); rc |= POLLPRI; } src_q = &ctx->q_data[V4L2_SRC].vb2_q; dst_q = &ctx->q_data[V4L2_DST].vb2_q; if ((!src_q->streaming || list_empty(&src_q->queued_list)) && (!dst_q->streaming || list_empty(&dst_q->queued_list))) { return rc; } poll_wait(filp, &src_q->done_wq, wait); if (!list_empty(&src_q->done_list)) rc |= POLLOUT | POLLWRNORM; poll_wait(filp, &dst_q->done_wq, wait); if (!list_empty(&dst_q->done_list)) rc |= POLLIN | POLLRDNORM; } else rc = POLLERR; return rc; } static int v4l2_mmap(struct file *filp, struct vm_area_struct *vma) { long ret = -EPERM; unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; struct queue_data *q_data; enum QUEUE_TYPE type; struct vpu_ctx *ctx = v4l2_fh_to_ctx(filp->private_data); vpu_dbg(LVL_INFO, "%s()\n", __func__); if (ctx) { type = offset >> MMAP_BUF_TYPE_SHIFT; q_data = &ctx->q_data[type]; offset &= ~MMAP_BUF_TYPE_MASK; offset = offset >> PAGE_SHIFT; vma->vm_pgoff = offset; ret = vb2_mmap(&q_data->vb2_q, vma ); } return ret; } static const struct v4l2_file_operations v4l2_encoder_fops = { .owner = THIS_MODULE, .open = v4l2_open, .unlocked_ioctl = video_ioctl2, .release = v4l2_release, .poll = v4l2_poll, .mmap = v4l2_mmap, }; static struct video_device v4l2_videodevice_encoder = { .name = "vpu encoder", .fops = &v4l2_encoder_fops, .ioctl_ops = &v4l2_encoder_ioctl_ops, .vfl_dir = VFL_DIR_M2M, }; #if 1 static int set_vpu_pwr(sc_ipc_t ipcHndl, sc_pm_power_mode_t pm, u_int32 core_id ) { int rv = -1; sc_err_t sciErr; vpu_dbg(LVL_INFO, "%s()\n", __func__); if (!ipcHndl) { vpu_dbg(LVL_ERR, "error: --- set_vpu_pwr no IPC handle\n"); goto set_vpu_pwrexit; } // Power on or off VPU, ENC and MU1 sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_VPU, pm); if (sciErr != SC_ERR_NONE) { vpu_dbg(LVL_ERR, "error: --- sc_pm_set_resource_power_mode(SC_R_VPU,%d) SCI error! (%d)\n", sciErr, pm); goto set_vpu_pwrexit; } #ifdef TEST_BUILD sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_VPU_ENC, pm); if (sciErr != SC_ERR_NONE) { vpu_dbg(LVL_ERR, "error: --- sc_pm_set_resource_power_mode(SC_R_VPU_ENC,%d) SCI error! (%d)\n", sciErr, pm); goto set_vpu_pwrexit; } #else if (core_id == 0) { sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_VPU_ENC_0, pm); if (sciErr != SC_ERR_NONE) { vpu_dbg(LVL_ERR, "error: --- sc_pm_set_resource_power_mode(SC_R_VPU_ENC_0,%d) SCI error! (%d)\n", sciErr, pm); goto set_vpu_pwrexit; } } else { sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_VPU_ENC_1, pm); if (sciErr != SC_ERR_NONE) { vpu_dbg(LVL_ERR, "error: --- sc_pm_set_resource_power_mode(SC_R_VPU_ENC_1,%d) SCI error! (%d)\n", sciErr, pm); goto set_vpu_pwrexit; } } #endif if (core_id == 0) { sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_VPU_MU_1, pm); if (sciErr != SC_ERR_NONE) { vpu_dbg(LVL_ERR, "error: --- sc_pm_set_resource_power_mode(SC_R_VPU_MU_1,%d) SCI error! (%d)\n", sciErr, pm); goto set_vpu_pwrexit; } } else { sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_VPU_MU_2, pm); if (sciErr != SC_ERR_NONE) { vpu_dbg(LVL_ERR, "error: --- sc_pm_set_resource_power_mode(SC_R_VPU_MU_2,%d) SCI error! (%d)\n", sciErr, pm); goto set_vpu_pwrexit; } } rv = 0; set_vpu_pwrexit: return rv; } static void vpu_set_power(struct vpu_dev *dev, bool on, u_int32 core_id) { int ret; if (on) { ret = set_vpu_pwr(dev->mu_ipcHandle, SC_PM_PW_MODE_ON, core_id); if (ret) vpu_dbg(LVL_ERR, "error: failed to power on\n"); pm_runtime_get_sync(dev->generic_dev); } else { pm_runtime_put_sync_suspend(dev->generic_dev); ret = set_vpu_pwr(dev->mu_ipcHandle, SC_PM_PW_MODE_OFF, core_id); if (ret) vpu_dbg(LVL_ERR, "error: failed to power off\n"); } } #endif static void vpu_setup(struct vpu_dev *This) { uint32_t read_data = 0; vpu_dbg(LVL_INFO, "enter %s\n", __func__); writel(0x1, This->regs_base + SCB_XREG_SLV_BASE + SCB_SCB_BLK_CTRL + SCB_BLK_CTRL_SCB_CLK_ENABLE_SET); writel(0xffffffff, This->regs_base + 0x70190); writel(0xffffffff, This->regs_base + SCB_XREG_SLV_BASE + SCB_SCB_BLK_CTRL + SCB_BLK_CTRL_XMEM_RESET_SET); writel(0xE, This->regs_base + SCB_XREG_SLV_BASE + SCB_SCB_BLK_CTRL + SCB_BLK_CTRL_SCB_CLK_ENABLE_SET); writel(0x7, This->regs_base + SCB_XREG_SLV_BASE + SCB_SCB_BLK_CTRL + SCB_BLK_CTRL_CACHE_RESET_SET); writel(0x102, This->regs_base + XMEM_CONTROL); read_data = readl(This->regs_base+0x70108); vpu_dbg(LVL_IRQ, "%s read_data=%x\n", __func__, read_data); } static void vpu_reset(struct vpu_dev *This) { vpu_dbg(LVL_INFO, "enter %s\n", __func__); writel(0x7, This->regs_base + SCB_XREG_SLV_BASE + SCB_SCB_BLK_CTRL + SCB_BLK_CTRL_CACHE_RESET_CLR); } static int vpu_enable_hw(struct vpu_dev *This) { vpu_dbg(LVL_INFO, "%s()\n", __func__); #if 0 This->vpu_clk = clk_get(&This->plat_dev->dev, "vpu_encoder_clk"); if (IS_ERR(This->vpu_clk)) { vpu_dbg(LVL_ERR, "vpu_clk get error\n"); return -ENOENT; } #endif vpu_setup(This); return 0; } static void vpu_disable_hw(struct vpu_dev *This) { vpu_reset(This); if (This->regs_base) { devm_iounmap(&This->plat_dev->dev, This->regs_base); } #if 0 if (This->vpu_clk) { clk_put(This->vpu_clk); } #endif } static int vpu_probe(struct platform_device *pdev) { struct vpu_dev *dev; struct resource *res; struct device_node *np = pdev->dev.of_node; struct device_node *reserved_node; struct resource reserved_res; unsigned int mu_id; u_int32 core_type; u_int32 i; int ret; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; dev->plat_dev = pdev; dev->generic_dev = &pdev->dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); dev->regs_base = ioremap(ENC_REG_BASE, 0x1000000); if (!dev->regs_base) { vpu_dbg(LVL_ERR, "%s could not map regs_base\n", __func__); return PTR_ERR(dev->regs_base); } if (np) { ret = of_property_read_u32(np, "core_type", &core_type); if (ret) { vpu_dbg(LVL_ERR, "error: Cannot get core num %d\n", ret); return -EINVAL; } if (core_type == 2) dev->plat_type = IMX8QM; else dev->plat_type = IMX8QXP; reserved_node = of_parse_phandle(np, "boot-region", 0); if (!reserved_node) { vpu_dbg(LVL_ERR, "error: boot-region of_parse_phandle error\n"); return -ENODEV; } if (of_address_to_resource(reserved_node, 0, &reserved_res)) { vpu_dbg(LVL_ERR, "error: boot-region of_address_to_resource error\n"); return -EINVAL; } dev->core_dev[0].m0_p_fw_space_phy = reserved_res.start; dev->core_dev[1].m0_p_fw_space_phy = reserved_res.start + M0_BOOT_SIZE; reserved_node = of_parse_phandle(np, "rpc-region", 0); if (!reserved_node) { vpu_dbg(LVL_ERR, "error: rpc-region of_parse_phandle error\n"); return -ENODEV; } if (of_address_to_resource(reserved_node, 0, &reserved_res)) { vpu_dbg(LVL_ERR, "error: rpc-region of_address_to_resource error\n"); return -EINVAL; } dev->core_dev[0].m0_rpc_phy = reserved_res.start; dev->core_dev[1].m0_rpc_phy = reserved_res.start + SHARED_SIZE; } else vpu_dbg(LVL_ERR, "error: %s of_node is NULL\n", __func__); ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) { vpu_dbg(LVL_ERR, "%s unable to register v4l2 dev\n", __func__); return ret; } platform_set_drvdata(pdev, dev); dev->pvpu_encoder_dev = video_device_alloc(); if (dev->pvpu_encoder_dev) { strncpy(dev->pvpu_encoder_dev->name, v4l2_videodevice_encoder.name, sizeof(v4l2_videodevice_encoder.name)); dev->pvpu_encoder_dev->fops = v4l2_videodevice_encoder.fops; dev->pvpu_encoder_dev->ioctl_ops = v4l2_videodevice_encoder.ioctl_ops; dev->pvpu_encoder_dev->release = video_device_release; dev->pvpu_encoder_dev->vfl_dir = v4l2_videodevice_encoder.vfl_dir; dev->pvpu_encoder_dev->v4l2_dev = &dev->v4l2_dev; video_set_drvdata(dev->pvpu_encoder_dev, dev); if (video_register_device(dev->pvpu_encoder_dev, VFL_TYPE_GRABBER, -1)) { vpu_dbg(LVL_ERR, "%s unable to register video encoder device\n", __func__ ); video_device_release(dev->pvpu_encoder_dev); dev->pvpu_encoder_dev = NULL; } else { vpu_dbg(LVL_INFO, "%s register video encoder device\n", __func__ ); } } if (!dev->mu_ipcHandle) { ret = sc_ipc_getMuID(&mu_id); if (ret) { vpu_dbg(LVL_ERR, "--- sc_ipc_getMuID() cannot obtain mu id SCI error! (%d)\n", ret); return ret; } ret = sc_ipc_open(&dev->mu_ipcHandle, mu_id); if (ret) { vpu_dbg(LVL_ERR, "--- sc_ipc_getMuID() cannot open MU channel to SCU error! (%d)\n", ret); return ret; } } if (core_type == 2) { vpu_set_power(dev, true, 0); vpu_set_power(dev, true, 1); } else vpu_set_power(dev, true, 0); vpu_enable_hw(dev); pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); mutex_init(&dev->dev_mutex); for (i = 0; i < core_type; i++) { struct core_device *core_dev; core_dev = &dev->core_dev[i]; mutex_init(&core_dev->core_mutex); mutex_init(&core_dev->cmd_mutex); init_completion(&core_dev->start_cmp); init_completion(&core_dev->snap_done_cmp); core_dev->firmware_started = false; core_dev->fw_is_ready = false; core_dev->workqueue = alloc_workqueue("vpu", WQ_UNBOUND | WQ_MEM_RECLAIM, 1); if (!core_dev->workqueue) { vpu_dbg(LVL_ERR, "%s unable to alloc workqueue\n", __func__); ret = -ENOMEM; return ret; } INIT_WORK(&core_dev->msg_work, vpu_msg_run_work); ret = vpu_mu_init(dev, i); if (ret) { vpu_dbg(LVL_ERR, "%s vpu mu init failed\n", __func__); return ret; } //firmware space for M0 core_dev->m0_p_fw_space_vir = ioremap_wc(core_dev->m0_p_fw_space_phy, M0_BOOT_SIZE ); if (!core_dev->m0_p_fw_space_vir) vpu_dbg(LVL_ERR, "failed to remap space for M0 firmware\n"); memset_io(core_dev->m0_p_fw_space_vir, 0, M0_BOOT_SIZE); core_dev->m0_rpc_virt = ioremap_wc(core_dev->m0_rpc_phy, SHARED_SIZE ); if (!core_dev->m0_rpc_virt) vpu_dbg(LVL_ERR, "failed to remap space for shared memory\n"); memset_io(core_dev->m0_rpc_virt, 0, SHARED_SIZE); rpc_init_shared_memory_encoder(&core_dev->shared_mem, core_dev->m0_rpc_phy - core_dev->m0_p_fw_space_phy, core_dev->m0_rpc_virt, SHARED_SIZE); rpc_set_system_cfg_value_encoder(core_dev->shared_mem.pSharedInterface, VPU_REG_BASE, i); } pm_runtime_put_sync(&pdev->dev); return 0; } static int vpu_remove(struct platform_device *pdev) { struct vpu_dev *dev = platform_get_drvdata(pdev); u_int32 core_num; u_int32 i; if (dev->plat_type == IMX8QM) { destroy_workqueue(dev->core_dev[0].workqueue); destroy_workqueue(dev->core_dev[1].workqueue); core_num = 2; } else { destroy_workqueue(dev->core_dev[0].workqueue); core_num = 1; } for (i = 0; i < core_num; i++) { if (dev->core_dev[i].m0_p_fw_space_vir) iounmap(dev->core_dev[i].m0_p_fw_space_vir); dev->core_dev[i].m0_p_fw_space_vir = NULL; dev->core_dev[i].m0_p_fw_space_phy = 0; if (dev->core_dev[i].shared_mem.shared_mem_vir) iounmap(dev->core_dev[i].shared_mem.shared_mem_vir); dev->core_dev[i].shared_mem.shared_mem_vir = NULL; dev->core_dev[i].shared_mem.shared_mem_phy = 0; } if (dev->m0_pfw) { release_firmware(dev->m0_pfw); dev->m0_pfw = NULL; } vpu_disable_hw(dev); pm_runtime_disable(&pdev->dev); if (video_get_drvdata(dev->pvpu_encoder_dev)) video_unregister_device(dev->pvpu_encoder_dev); v4l2_device_unregister(&dev->v4l2_dev); return 0; } static int vpu_runtime_suspend(struct device *dev) { return 0; } static int vpu_runtime_resume(struct device *dev) { return 0; } #if 0 static int vpu_suspend(struct device *dev) { return 0; } static int vpu_resume(struct device *dev) { return 0; } #endif #define CHECK_BIT(var, pos) (((var) >> (pos)) & 1) static void v4l2_vpu_send_snapshot(struct core_device *dev) { int i = 0; int strIdx = (~dev->hang_mask) & (dev->instance_mask); /*figure out the first available instance*/ for (i = 0; i < VPU_MAX_NUM_STREAMS; i++) { if (CHECK_BIT(strIdx, i)) { strIdx = i; break; } } v4l2_vpu_send_cmd(dev->ctx[strIdx], strIdx, GTB_ENC_CMD_SNAPSHOT, 0, NULL); } static int vpu_suspend(struct device *dev) { struct vpu_dev *vpudev = (struct vpu_dev *)dev_get_drvdata(dev); struct core_device *core_dev; u_int32 core_num, i; if (vpudev->plat_type == IMX8QM) core_num = 2; else core_num = 1; for (i = 0; i < core_num; i++) { vpu_set_power(vpudev, false, i); core_dev = &vpudev->core_dev[i]; if (core_dev->hang_mask != core_dev->instance_mask) { /*if there is an available device, send snapshot command to firmware*/ v4l2_vpu_send_snapshot(core_dev); if (!wait_for_completion_timeout(&core_dev->snap_done_cmp, msecs_to_jiffies(1000))) { vpu_dbg(LVL_ERR, "error: wait for vpu encoder snapdone event timeout!\n"); return -1; } } } // vpu_set_power(vpudev, false); return 0; } static int vpu_resume(struct device *dev) { struct vpu_dev *vpudev = (struct vpu_dev *)dev_get_drvdata(dev); void *csr_offset, *csr_cpuwait; struct core_device *core_dev; u_int32 core_num; u_int32 i; if (vpudev->plat_type == IMX8QM) { core_num = 2; vpu_set_power(vpudev, true, 0); vpu_set_power(vpudev, true, 1); vpu_enable_hw(vpudev); } else { core_num = 1; vpu_set_power(vpudev, true, 0); vpu_enable_hw(vpudev); } for (i = 0; i < core_num; i++) { core_dev = &vpudev->core_dev[i]; MU_Init(core_dev->mu_base_virtaddr); MU_EnableRxFullInt(core_dev->mu_base_virtaddr, 0); if (core_dev->hang_mask == core_dev->instance_mask) { /*no instance is active before suspend, do reset*/ core_dev->fw_is_ready = false; core_dev->firmware_started = false; rpc_init_shared_memory_encoder(&core_dev->shared_mem, core_dev->m0_rpc_phy - core_dev->m0_p_fw_space_phy, core_dev->m0_rpc_virt, SHARED_SIZE); rpc_set_system_cfg_value_encoder(core_dev->shared_mem.pSharedInterface, VPU_REG_BASE, i); } else { /*resume*/ if (vpudev->plat_type == IMX8QXP) { csr_offset = ioremap(0x2d050000, 4); writel(core_dev->m0_p_fw_space_phy, csr_offset); csr_cpuwait = ioremap(0x2d050004, 4); writel(0x0, csr_cpuwait); } else { if (i == 0) { csr_offset = ioremap(0x2d090000, 4); writel(core_dev->m0_p_fw_space_phy, csr_offset); csr_cpuwait = ioremap(0x2d090004, 4); writel(0x0, csr_cpuwait); } else { csr_offset = ioremap(0x2d0a0000, 4); writel(core_dev->m0_p_fw_space_phy, csr_offset); csr_cpuwait = ioremap(0x2d0a0004, 4); writel(0x0, csr_cpuwait); } } /*wait for firmware resotre done*/ if (!wait_for_completion_timeout(&core_dev->start_cmp, msecs_to_jiffies(1000))) { vpu_dbg(LVL_ERR, "error: wait for vpu encoder resume done timeout!\n"); return -1; } } } return 0; } static const struct dev_pm_ops vpu_pm_ops = { SET_RUNTIME_PM_OPS(vpu_runtime_suspend, vpu_runtime_resume, NULL) SET_SYSTEM_SLEEP_PM_OPS(vpu_suspend, vpu_resume) }; static const struct of_device_id vpu_of_match[] = { { .compatible = "nxp,imx8qm-b0-vpuenc", }, { .compatible = "nxp,imx8qxp-b0-vpuenc", }, {} } MODULE_DEVICE_TABLE(of, vpu_of_match); static struct platform_driver vpu_driver = { .probe = vpu_probe, .remove = vpu_remove, .driver = { .name = "vpu-b0-encoder", .of_match_table = vpu_of_match, .pm = &vpu_pm_ops, }, }; module_platform_driver(vpu_driver); MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_DESCRIPTION("Linux VPU driver for Freescale i.MX/MXC"); MODULE_LICENSE("GPL"); module_param(vpu_dbg_level_encoder, int, 0644); MODULE_PARM_DESC(vpu_dbg_level_encoder, "Debug level (0-2)");