blob: fd8e3f2a4a44053cf8f9e3cd450085887291e5f1 [file] [log] [blame]
/*
* 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 = 0;
#ifdef DUMP_DATA
#define DATA_NUM 10
#endif
/*
* 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,
},
{
.name = "VC1 Encoded Stream",
.fourcc = V4L2_PIX_FMT_VC1_ANNEX_G,
.num_planes = 1,
.venc_std = VPU_VIDEO_VC1,
},
{
.name = "VC1 RCV Encoded Stream",
.fourcc = V4L2_PIX_FMT_VC1_ANNEX_L,
.num_planes = 1,
.venc_std = VPU_VIDEO_VC1,
},
{
.name = "MPEG2 Encoded Stream",
.fourcc = V4L2_PIX_FMT_MPEG2,
.num_planes = 1,
.venc_std = VPU_VIDEO_MPEG2,
},
{
.name = "AVS Encoded Stream",
.fourcc = VPU_PIX_FMT_AVS,
.num_planes = 1,
.venc_std = VPU_VIDEO_AVS,
},
{
.name = "MPEG4 ASP Encoded Stream",
.fourcc = VPU_PIX_FMT_ASP,
.num_planes = 1,
.venc_std = VPU_VIDEO_ASP,
},
{
.name = "JPEG stills",
.fourcc = V4L2_PIX_FMT_JPEG,
.num_planes = 1,
.venc_std = VPU_VIDEO_JPEG,
},
{
.name = "RV8 Encoded Stream",
.fourcc = VPU_PIX_FMT_RV8,
.num_planes = 1,
.venc_std = VPU_VIDEO_RV8,
},
{
.name = "RV9 Encoded Stream",
.fourcc = VPU_PIX_FMT_RV9,
.num_planes = 1,
.venc_std = VPU_VIDEO_RV9,
},
{
.name = "VP6 Encoded Stream",
.fourcc = VPU_PIX_FMT_VP6,
.num_planes = 1,
.venc_std = VPU_VIDEO_VP6,
},
{
.name = "VP6 SPK Encoded Stream",
.fourcc = VPU_PIX_FMT_SPK,
.num_planes = 1,
.venc_std = VPU_VIDEO_SPK,
},
{
.name = "VP8 Encoded Stream",
.fourcc = V4L2_PIX_FMT_VP8,
.num_planes = 1,
.venc_std = VPU_VIDEO_VP8,
},
{
.name = "H264/MVC Encoded Stream",
.fourcc = V4L2_PIX_FMT_H264_MVC,
.num_planes = 1,
.venc_std = VPU_VIDEO_AVC_MVC,
},
{
.name = "H265 HEVC Encoded Stream",
.fourcc = VPU_PIX_FMT_HEVC,
.num_planes = 1,
.venc_std = VPU_VIDEO_HEVC,
},
{
.name = "VP9 Encoded Stream",
.fourcc = VPU_PIX_FMT_VP9,
.num_planes = 1,
.venc_std = VPU_VIDEO_VP9,
},
{
.name = "Logo",
.fourcc = VPU_PIX_FMT_LOGO,
.num_planes = 1,
.venc_std = VPU_VIDEO_UNDEFINED,
},
};
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 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_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_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_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_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 = 0;
pix_mp->height = 0;
pix_mp->field = V4L2_FIELD_ANY;
pix_mp->plane_fmt[0].bytesperline = 0;
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;
pENC_RPC_HOST_IFACE pSharedInterface = ctx->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],
ctx->dev->shared_mem.base_offset);
pEncParam = (pMEDIAIP_ENC_PARAM)phy_to_virt(pEncCtrlInterface->pEncParam,
ctx->dev->shared_mem.base_offset);
pEncExpertModeParam = (pMEDIAIP_ENC_EXPERT_MODE_PARAM)phy_to_virt(pEncCtrlInterface->pEncExpertModeParam,
ctx->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;
} 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->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);
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
)
{
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) {
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
)
{
vpu_dbg(LVL_INFO, "%s()\n", __func__);
switch (cmd->cmd) {
case V4L2_ENC_CMD_START:
break;
case V4L2_ENC_CMD_STOP:
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()\n", __func__);
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);
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()\n", __func__);
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_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);
pENC_RPC_HOST_IFACE pSharedInterface = ctx->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],
ctx->dev->shared_mem.base_offset);
pEncParam = (pMEDIAIP_ENC_PARAM)phy_to_virt(pEncCtrlInterface->pEncParam,
ctx->dev->shared_mem.base_offset);
pEncExpertModeParam = (pMEDIAIP_ENC_EXPERT_MODE_PARAM)phy_to_virt(pEncCtrlInterface->pEncExpertModeParam,
ctx->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 = 200;
pEncParam->uMaxBitRate = 4000;
pEncParam->uMinBitRate = 50;
}
} 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)
pEncParam->eProfile = MEDIAIP_ENC_PROF_H264_BP;
break;
case V4L2_CID_MPEG_VIDEO_BITRATE:
pEncParam->uTargetBitrate = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
pEncParam->uGopBLength = 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 const struct v4l2_ctrl_ops vpu_enc_ctrl_ops = {
.s_ctrl = v4l2_enc_s_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_CBR);
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_BASELINE
);
v4l2_ctrl_new_std(&ctx->ctrl_handler, &vpu_enc_ctrl_ops,
V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1, 100);
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);
}
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)
{
rpc_send_cmd_buf_encoder(&ctx->dev->shared_mem, idx, cmdid, cmdnum, local_cmddata);
MU_SendMessage(ctx->dev->mu_base_virtaddr, 0, COMMAND);
}
/**the function is used for to convert yuv420p to yuv420sp
* yyyy yyyy
* uu vv
* ->
* yyyy yyyy
* uv uv
**/
static void convert_feed_stream(struct queue_data *This, struct vb2_buffer *vb)
{
u_int8 *y_start;
u_int8 *u_start;
u_int8 *v_start;
u_int8 *uv_temp;
u_int32 i, j;
u_int32 height = This->height;
u_int32 width = This->width;
u_int32 y_size, uv_size;
y_size = height * width;
uv_size = height * width/2;
uv_temp = kmalloc(sizeof(u_int8)*uv_size, GFP_KERNEL);
y_start = (u_int8 *)vb2_plane_vaddr(vb, 0);
u_start = y_start + y_size;
v_start = y_start + y_size*5/4;
for (i = 0, j = 0; j < uv_size; j += 2, i++) {
uv_temp[j] = u_start[i];
uv_temp[j+1] = v_start[i];
}
memcpy(u_start, uv_temp, sizeof(u_int8)*uv_size);
kfree(uv_temp);
}
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;
pENC_RPC_HOST_IFACE pSharedInterface = ctx->dev->shared_mem.pSharedInterface;
pMEDIA_ENC_API_CONTROL_INTERFACE pEncCtrlInterface;
u_int32 uStrIdx = 0;
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],
ctx->dev->shared_mem.base_offset);
pEncStrBuffDesc = (pBUFFER_DESCRIPTOR_TYPE)phy_to_virt(pEncCtrlInterface->pEncStreamBufferDesc,
ctx->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,
ctx->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
v4l2_vpu_send_cmd(ctx, 0, 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];
pENC_RPC_HOST_IFACE pSharedInterface = ctx->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],
ctx->dev->shared_mem.base_offset);
pParamYuvBuffDesc = (pMEDIAIP_ENC_YUV_BUFFER_DESC)phy_to_virt(pEncCtrlInterface->pEncYUVBufferDesc,
ctx->dev->shared_mem.base_offset);
wait_event_interruptible(ctx->buffer_wq_input,
!list_empty(&This->drv_q)
);
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
convert_feed_stream(This, p_data_req->vb2_buf);
pphy_address = (u_int32 *)vb2_plane_cookie(p_data_req->vb2_buf, 0);
pParamYuvBuffDesc->uLumaBase = *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;
pENC_RPC_HOST_IFACE pSharedInterface = ctx->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],
ctx->dev->shared_mem.base_offset);
pEncStrBuffDesc = (pBUFFER_DESCRIPTOR_TYPE)phy_to_virt(pEncCtrlInterface->pEncStreamBufferDesc,
ctx->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);
wait_event_interruptible(ctx->buffer_wq_output,
!list_empty(&This->drv_q)
);
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);
//memcpy to vb2 buffer from encpicinfo
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;
pENC_RPC_HOST_IFACE pSharedInterface = ctx->dev->shared_mem.pSharedInterface;
u_int32 i;
pEncCtrlInterface = (pMEDIA_ENC_API_CONTROL_INTERFACE)phy_to_virt(pSharedInterface->pEncCtrlInterface[ctx->str_index],
ctx->dev->shared_mem.base_offset);
pEncMemPool = (pMEDIAIP_ENC_MEM_POOL)phy_to_virt(pEncCtrlInterface->pEncMemPool,
ctx->dev->shared_mem.base_offset);
for (i = 0; i < req_data->uEncFrmNum; i++) {
ctx->encFrame[i].size = ((req_data->uEncFrmSize + (~req_data->uAlignmentMask))&req_data->uAlignmentMask);
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 - ctx->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 + (~req_data->uAlignmentMask))&req_data->uAlignmentMask);
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 - ctx->dev->m0_p_fw_space_phy;
#endif
pEncMemPool->tRefFrameBuffers[i].uMemSize = ctx->refFrame[i].size;
}
ctx->actFrame.size = ((req_data->uActBufSize + (~req_data->uAlignmentMask))&req_data->uAlignmentMask);
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 - ctx->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_dbg(LVL_INFO, "vpu_encoder_event_handler is called\n");
if (uStrIdx < VID_API_NUM_STREAMS) {
switch (uEvent) {
case VID_API_ENC_EVENT_START_DONE: {
vpu_dbg(LVL_INFO, "VID_API_ENC_EVENT_START_DONE : Encoder configuration complete\n");
update_yuv_addr(ctx, 0);
v4l2_vpu_send_cmd(ctx, 0, 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, "VID_API_ENC_EVENT_MEM_REQUEST: need to request memory\n");
vpu_dbg(LVL_INFO, "uEncFrmSize = %d, uEncFrmNum=%d, uRefFrmSize=%d, uRefFrmNum=%d, uActBufSize=%d, uAlignmentMask=0x%x\n", req_data->uEncFrmSize, req_data->uEncFrmNum, req_data->uRefFrmSize, req_data->uRefFrmNum, req_data->uActBufSize, req_data->uAlignmentMask);
enc_mem_alloc(ctx, req_data);
//update_yuv_addr(ctx,0);
v4l2_vpu_send_cmd(ctx, 0, GTB_ENC_CMD_STREAM_START, 0, NULL);
} break;
case VID_API_ENC_EVENT_PARA_UPD_DONE: {
vpu_dbg(LVL_INFO, "VID_API_ENC_EVENT_PARA_UPD_DONE : Parameter update complete\n");
} 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, "VID_API_ENC_EVENT_FRAME_DONE : Encode picture done\n");
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];
vb2_buffer_done(p_data_req->vb2_buf,
VB2_BUF_STATE_DONE
);
} break;
case VID_API_ENC_EVENT_STOP_DONE:
vpu_dbg(LVL_INFO, "VID_API_ENC_EVENT_STOP_DONE : Stop done\n");
break;
case VID_API_ENC_EVENT_FRAME_INPUT_DONE: {
vpu_dbg(LVL_INFO, "VID_API_ENC_EVENT_FRAME_INPUT_DONE : Input done\n");
update_yuv_addr(ctx, 0);
v4l2_vpu_send_cmd(ctx, 0, GTB_ENC_CMD_FRAME_ENCODE, 0, NULL);
} break;
case VID_API_ENC_EVENT_TERMINATE_DONE:
vpu_dbg(LVL_INFO, "VID_API_ENC_EVENT_TERMINATE_DONE : Codec terminated\n");
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 vpu_dev *dev = This;
u32 msg;
MU_ReceiveMsg(dev->mu_base_virtaddr, 0, &msg);
if (msg == 0xaa) {
#ifdef CM4
MU_sendMesgToFW(dev->mu_base_virtaddr, RPC_BUF_OFFSET, dev->m0_rpc_phy); //CM4 use absolute address
#else
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);
#endif
MU_sendMesgToFW(dev->mu_base_virtaddr, INIT_DONE, 2);
} else
schedule_work(&dev->msg_work);
return IRQ_HANDLED;
}
/* Initialization of the MU code. */
static int vpu_mu_init(struct vpu_dev *dev)
{
struct device_node *np;
unsigned int vpu_mu_id;
u32 irq;
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, "fsl,imx8-mu1-vpu-m0");
if (!np) {
vpu_dbg(LVL_ERR, "error: Cannot find MU entry in device tree\n");
return -EINVAL;
}
#endif
dev->mu_base_virtaddr = of_iomap(np, 0);
WARN_ON(!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;
}
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 *)dev);
if (ret) {
vpu_dbg(LVL_ERR, "request_irq failed %d, error = %d\n", irq, ret);
return -EINVAL;
}
if (!dev->vpu_mu_init) {
MU_Init(dev->mu_base_virtaddr);
MU_EnableRxFullInt(dev->mu_base_virtaddr, 0);
dev->vpu_mu_init = 1;
}
return ret;
}
static int vpu_next_free_instance(struct vpu_dev *dev)
{
int idx = ffz(dev->instance_mask);
if (idx < 0 || idx > VPU_MAX_NUM_STREAMS)
return -EBUSY;
return idx;
}
extern u_int32 rpc_MediaIPFW_Video_message_check_encoder(struct shared_addr *This);
static void vpu_msg_run_work(struct work_struct *work)
{
struct vpu_dev *dev = container_of(work, struct vpu_dev, 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);
ctx = dev->ctx[msg.idx];
vpu_api_event_handler(ctx, msg.idx, msg.msgid, msg.msgdata);
}
if (rpc_MediaIPFW_Video_message_check_encoder(This) == API_MSG_BUFFER_ERROR)
vpu_dbg(LVL_ERR, "MSG num is too big to handle");
}
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 {
*plane_count = 1;
psize[0] = This->sizeimage[0] + This->sizeimage[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)
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(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)
{
unsigned char *image;
unsigned int FW_Size = 0;
void *csr_offset, *csr_cpuwait;
int ret = 0;
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->m0_p_fw_space_vir,
image,
FW_Size
);
#ifdef CM4
boot_CM4_up(This, This->m0_p_fw_space_vir);
#else
csr_offset = ioremap(0x2d050000, 4);
writel(This->m0_p_fw_space_phy, csr_offset);
csr_cpuwait = ioremap(0x2d050004, 4);
writel(0x0, csr_cpuwait);
#endif
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;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
idx = vpu_next_free_instance(dev);
if (idx < 0) {
ret = idx;
return ret;
}
set_bit(idx, &dev->instance_mask);
init_completion(&ctx->completion);
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;
dev->ctx[idx] = ctx;
ctx->b_firstseq = true;
ctx->start_flag = true;
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->fw_is_ready) {
ret = vpu_firmware_download(dev);
if (ret) {
vpu_dbg(LVL_ERR, "error: vpu_firmware_download fail\n");
mutex_unlock(&dev->dev_mutex);
return ret;
}
dev->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;
}
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);
u_int32 i;
release_queue_data(ctx);
ctrls_delete_encoder(ctx);
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
clear_bit(ctx->str_index, &dev->instance_mask);
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
);
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 | POLLWRNORM;
} 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,
};
static int set_vpu_pwr(sc_ipc_t ipcHndl,
sc_pm_power_mode_t pm
)
{
int rv = -1;
sc_err_t sciErr;
vpu_dbg(LVL_INFO, "%s()\n", __func__);
if (!ipcHndl) {
vpu_dbg(LVL_ERR, "--- set_vpu_pwr no IPC handle\n");
goto set_vpu_pwrexit;
}
// Power on or off PID0, ENC
sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_VPU_PID0, pm);
if (sciErr != SC_ERR_NONE) {
vpu_dbg(LVL_ERR, "--- sc_pm_set_resource_power_mode(SC_R_VPU_PID0,%d) SCI error! (%d)\n", sciErr, pm);
goto set_vpu_pwrexit;
}
sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_VPU_PID1, pm);
if (sciErr != SC_ERR_NONE) {
vpu_dbg(LVL_ERR, "--- sc_pm_set_resource_power_mode(SC_R_VPU_PID1,%d) SCI error! (%d)\n", sciErr, pm);
goto set_vpu_pwrexit;
}
sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_VPU_PID2, pm);
if (sciErr != SC_ERR_NONE) {
vpu_dbg(LVL_ERR, "--- sc_pm_set_resource_power_mode(SC_R_VPU_PID2,%d) SCI error! (%d)\n", sciErr, pm);
goto set_vpu_pwrexit;
}
sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_VPU_PID3, pm);
if (sciErr != SC_ERR_NONE) {
vpu_dbg(LVL_ERR, "--- sc_pm_set_resource_power_mode(SC_R_VPU_PID3,%d) SCI error! (%d)\n", sciErr, pm);
goto set_vpu_pwrexit;
}
sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_VPU_PID4, pm);
if (sciErr != SC_ERR_NONE) {
vpu_dbg(LVL_ERR, "--- sc_pm_set_resource_power_mode(SC_R_VPU_PID4,%d) SCI error! (%d)\n", sciErr, pm);
goto set_vpu_pwrexit;
}
sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_VPU_PID5, pm);
if (sciErr != SC_ERR_NONE) {
vpu_dbg(LVL_ERR, "--- sc_pm_set_resource_power_mode(SC_R_VPU_PID5,%d) SCI error! (%d)\n", sciErr, pm);
goto set_vpu_pwrexit;
}
sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_VPU_PID6, pm);
if (sciErr != SC_ERR_NONE) {
vpu_dbg(LVL_ERR, "--- sc_pm_set_resource_power_mode(SC_R_VPU_PID6,%d) SCI error! (%d)\n", sciErr, pm);
goto set_vpu_pwrexit;
}
sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_VPU_PID7, pm);
if (sciErr != SC_ERR_NONE) {
vpu_dbg(LVL_ERR, "--- sc_pm_set_resource_power_mode(SC_R_VPU_PID7,%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, "--- sc_pm_set_resource_power_mode(SC_R_VPU_ENC,%d) SCI error! (%d)\n", sciErr, pm);
goto set_vpu_pwrexit;
}
#else
sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_VPU_ENC_0, pm);
if (sciErr != SC_ERR_NONE) {
vpu_dbg(LVL_ERR, "--- sc_pm_set_resource_power_mode(SC_R_VPU_ENC_0,%d) SCI error! (%d)\n", sciErr, pm);
goto set_vpu_pwrexit;
}
#endif
rv = 0;
set_vpu_pwrexit:
return rv;
}
static void vpu_set_power(struct vpu_dev *dev, bool on)
{
int ret;
if (on) {
ret = set_vpu_pwr(dev->mu_ipcHandle, SC_PM_PW_MODE_ON);
if (ret)
vpu_dbg(LVL_ERR, "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);
if (ret)
vpu_dbg(LVL_ERR, "failed to power off\n");
}
}
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(0x1f, This->regs_base + DEC_MFD_XREG_SLV_BASE + MFD_BLK_CTRL + MFD_BLK_CTRL_MFD_SYS_CLOCK_ENABLE_SET);
writel(0xffffffff, This->regs_base + DEC_MFD_XREG_SLV_BASE + MFD_BLK_CTRL + MFD_BLK_CTRL_MFD_SYS_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);
writel(0xffffffff, This->regs_base + DEC_MFD_XREG_SLV_BASE + MFD_BLK_CTRL + MFD_BLK_CTRL_MFD_SYS_RESET_CLR);
}
static int vpu_enable_hw(struct vpu_dev *This)
{
vpu_dbg(LVL_INFO, "%s()\n", __func__);
vpu_set_power(This, true);
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;
}
clk_set_rate(This->vpu_clk, 600000000);
clk_prepare_enable(This->vpu_clk);
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);
}
clk_disable_unprepare(This->vpu_clk);
if (This->vpu_clk) {
clk_put(This->vpu_clk);
This->vpu_clk = NULL;
}
vpu_set_power(This, false);
}
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;
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) {
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->m0_p_fw_space_phy = reserved_res.start;
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->m0_rpc_phy = reserved_res.start;
} 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;
}
}
vpu_enable_hw(dev);
mutex_init(&dev->dev_mutex);
dev->fw_is_ready = false;
dev->workqueue = alloc_workqueue("vpu", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
if (!dev->workqueue) {
vpu_dbg(LVL_ERR, "%s unable to alloc workqueue\n", __func__);
ret = -ENOMEM;
return ret;
}
INIT_WORK(&dev->msg_work, vpu_msg_run_work);
#ifdef CM4
ret = power_CM4_up(dev);
if (ret) {
vpu_dbg(LVL_ERR, "error: failed to power on CM4\n");
return ret;
}
#endif
ret = vpu_mu_init(dev);
if (ret) {
vpu_dbg(LVL_ERR, "%s vpu mu init failed\n", __func__);
return ret;
}
//firmware space for M0
dev->m0_p_fw_space_vir = ioremap(dev->m0_p_fw_space_phy,
M0_BOOT_SIZE
);
if (!dev->m0_p_fw_space_vir)
vpu_dbg(LVL_ERR, "failed to remap space for M0 firmware\n");
memset_io(dev->m0_p_fw_space_vir, 0, M0_BOOT_SIZE);
dev->m0_rpc_virt = ioremap(dev->m0_rpc_phy,
SHARED_SIZE
);
if (!dev->m0_rpc_virt)
vpu_dbg(LVL_ERR, "failed to remap space for shared memory\n");
memset_io(dev->m0_rpc_virt, 0, SHARED_SIZE);
#ifdef CM4
rpc_init_shared_memory_encoder(&dev->shared_mem, dev->m0_rpc_phy, dev->m0_rpc_virt, SHARED_SIZE);
#else
rpc_init_shared_memory_encoder(&dev->shared_mem, dev->m0_rpc_phy - dev->m0_p_fw_space_phy, dev->m0_rpc_virt, SHARED_SIZE);
#endif
rpc_set_system_cfg_value_encoder(dev->shared_mem.pSharedInterface, VPU_REG_BASE);
return 0;
}
static int vpu_remove(struct platform_device *pdev)
{
struct vpu_dev *dev = platform_get_drvdata(pdev);
destroy_workqueue(dev->workqueue);
if (dev->m0_p_fw_space_vir)
iounmap(dev->m0_p_fw_space_vir);
if (dev->m0_pfw) {
release_firmware(dev->m0_pfw);
dev->m0_pfw = NULL;
}
dev->m0_p_fw_space_vir = NULL;
dev->m0_p_fw_space_phy = 0;
if (dev->shared_mem.shared_mem_vir)
iounmap(dev->shared_mem.shared_mem_vir);
dev->shared_mem.shared_mem_vir = NULL;
dev->shared_mem.shared_mem_phy = 0;
vpu_disable_hw(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;
}
static int vpu_suspend(struct device *dev)
{
return 0;
}
static int vpu_resume(struct device *dev)
{
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-vpu-encoder", },
{ .compatible = "nxp,imx8qxp-vpu-encoder", },
{}
}
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)");