summaryrefslogtreecommitdiff
path: root/drivers/mxc/vpu-encoder-b0/vpu_encoder_b0.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mxc/vpu-encoder-b0/vpu_encoder_b0.c')
-rw-r--r--drivers/mxc/vpu-encoder-b0/vpu_encoder_b0.c2408
1 files changed, 2408 insertions, 0 deletions
diff --git a/drivers/mxc/vpu-encoder-b0/vpu_encoder_b0.c b/drivers/mxc/vpu-encoder-b0/vpu_encoder_b0.c
new file mode 100644
index 000000000000..c57cdf563670
--- /dev/null
+++ b/drivers/mxc/vpu-encoder-b0/vpu_encoder_b0.c
@@ -0,0 +1,2408 @@
+/*
+ * 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 <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/videodev2.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/file.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+#include <linux/platform_data/dma-imx.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/pm_runtime.h>
+#include <linux/mx8_mu.h>
+#include <linux/uaccess.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-dma-sg.h>
+
+#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)");
+