blob: 7a4274d4f0f857a9b416375b7fa5d87dd640ef0d [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-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_b0.h"
unsigned int vpu_dbg_level_decoder = 0;
static void vpu_api_event_handler(struct vpu_ctx *ctx, u_int32 uStrIdx, u_int32 uEvent, u_int32 *event_data);
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 add_eos(struct vpu_ctx *ctx, u_int32 uStrBufIdx);
static void v4l2_update_stream_addr(struct vpu_ctx *ctx, uint32_t uStrBufIdx);
static char *cmd2str[] = {
"VID_API_CMD_NULL", /*0x0*/
"VID_API_CMD_PARSE_NEXT_SEQ", /*0x1*/
"VID_API_CMD_PARSE_NEXT_I",
"VID_API_CMD_PARSE_NEXT_IP",
"VID_API_CMD_PARSE_NEXT_ANY",
"VID_API_CMD_DEC_PIC",
"VID_API_CMD_UPDATE_ES_WR_PTR",
"VID_API_CMD_UPDATE_ES_RD_PTR",
"VID_API_CMD_UPDATE_UDATA",
"VID_API_CMD_GET_FSINFO",
"VID_API_CMD_SKIP_PIC",
"VID_API_CMD_DEC_CHUNK", /*0x0b*/
"VID_API_CMD_UNDEFINED",
"VID_API_CMD_UNDEFINED",
"VID_API_CMD_UNDEFINED",
"VID_API_CMD_UNDEFINED",
"VID_API_CMD_START", /*0x10*/
"VID_API_CMD_STOP",
"VID_API_CMD_ABORT",
"VID_API_CMD_RST_BUF",
"VID_API_CMD_UNDEFINED",
"VID_API_CMD_FS_RELEASE",
"VID_API_CMD_MEM_REGION_ATTACH",
"VID_API_CMD_MEM_REGION_DETACH",
"VID_API_CMD_MVC_VIEW_SELECT",
"VID_API_CMD_FS_ALLOC", /*0x19*/
"VID_API_CMD_UNDEFINED",
"VID_API_CMD_UNDEFINED",
"VID_API_CMD_DBG_GET_STATUS", /*0x1C*/
"VID_API_CMD_DBG_START_LOG",
"VID_API_CMD_DBG_STOP_LOG",
"VID_API_CMD_DBG_DUMP_LOG",
"VID_API_CMD_YUV_READY", /*0x20*/
};
static char *event2str[] = {
"VID_API_EVENT_NULL", /*0x0*/
"VID_API_EVENT_RESET_DONE", /*0x1*/
"VID_API_EVENT_SEQ_HDR_FOUND",
"VID_API_EVENT_PIC_HDR_FOUND",
"VID_API_EVENT_PIC_DECODED",
"VID_API_EVENT_FIFO_LOW",
"VID_API_EVENT_FIFO_HIGH",
"VID_API_EVENT_FIFO_EMPTY",
"VID_API_EVENT_FIFO_FULL",
"VID_API_EVENT_BS_ERROR",
"VID_API_EVENT_UDATA_FIFO_UPTD",
"VID_API_EVENT_RES_CHANGE",
"VID_API_EVENT_FIFO_OVF",
"VID_API_EVENT_CHUNK_DECODED", /*0x0D*/
"VID_API_EVENT_UNDEFINED",
"VID_API_EVENT_UNDEFINED",
"VID_API_EVENT_REQ_FRAME_BUFF", /*0x10*/
"VID_API_EVENT_FRAME_BUFF_RDY",
"VID_API_EVENT_REL_FRAME_BUFF",
"VID_API_EVENT_STR_BUF_RST",
"VID_API_EVENT_RET_PING",
"VID_API_EVENT_QMETER",
"VID_API_EVENT_STR_FMT_CHANGED",
"VID_API_EVENT_MIPS_XCPT",
"VID_API_EVENT_START_DONE",
"VID_API_EVENT_STOPPED",
"VID_API_EVENT_ABORT_DONE",
"VID_API_EVENT_FINISHED",
"VID_API_EVENT_DBG_STAT_UPDATE",
"VID_API_EVENT_DBG_LOG_STARTED",
"VID_API_EVENT_DBG_LOG_STOPPED",
"VID_API_EVENT_DBG_LOG_UPFATED",
"VID_API_EVENT_DBG_MSG_DEC", /*0x20*/
"VID_API_EVENT_DEC_SC_ERR",
"VID_API_EVENT_CQ_FIFO_DUMP",
"VID_API_EVENT_DBG_FIFO_DUMP",
"VID_API_EVENT_DEC_CHECK_RES",
"VID_API_EVENT_DEC_CFG_INFO", /*0x25*/
};
static char *bufstat[] = {
"FRAME_ALLOC",
"FRAME_FREE",
"FRAME_DECODED",
"FRAME_READY",
"FRAME_RELEASE",
};
static void vpu_log_event(u_int32 uEvent, u_int32 ctxid)
{
if (uEvent > sizeof(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 > sizeof(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);
}
#ifdef DEBUG
static void vpu_log_stat(u_int32 status, u_int32 bufferid, u_int32 ctxid)
{
if (status > sizeof(bufstat)-1)
vpu_dbg(LVL_INFO, "buffer status: 0x%X, buffer id:%d ctx id:%d\n", status, bufferid, ctxid);
else
vpu_dbg(LVL_INFO, "buffer status: %s, buffer id:%d ctx id:%d\n", bufstat[status], bufferid, ctxid);
}
#endif
static int find_buffer_id(struct vpu_ctx *ctx, u_int32 addr)
{
struct vb2_data_req *p_data_req;
u_int32 LumaAddr;
u_int32 *pphy_address;
u_int32 i;
for (i = 0; i < VPU_MAX_BUFFER; i++) {
p_data_req = &ctx->q_data[V4L2_DST].vb2_reqs[i];
if (p_data_req->vb2_buf != NULL) {
pphy_address = (u_int32 *)vb2_plane_cookie(p_data_req->vb2_buf, 0);
LumaAddr = *pphy_address;
if (LumaAddr == addr - ctx->dev->cm_offset)
return i;
}
}
vpu_dbg(LVL_ERR, "error: %s() can't find suitable id based on address(0x%x)\n", __func__, addr);
return -1;
}
static void MU_sendMesgToFW(void __iomem *base, MSG_Type type, uint32_t value)
{
MU_SendMessage(base, 1, value);
MU_SendMessage(base, 0, type);
}
#ifdef DEBUG
static void vpu_log_shared_mem(struct vpu_ctx *ctx)
{
struct vpu_dev *dev = ctx->dev;
struct shared_addr *This = &dev->shared_mem;
pDEC_RPC_HOST_IFACE pSharedInterface = (pDEC_RPC_HOST_IFACE)This->shared_mem_vir;
MediaIPFW_Video_BufDesc *pMsgDesc = &pSharedInterface->StreamMsgBufferDesc;
MediaIPFW_Video_BufDesc *pCmdDesc = &pSharedInterface->StreamCmdBufferDesc;
pSTREAM_BUFFER_DESCRIPTOR_TYPE pStrBufDesc;
u_int32 index = ctx->str_index;
vpu_dbg(LVL_INFO, "msg: wr: 0x%x, rd: 0x%x, cmd: wr : 0x%x, rd: 0x%x\n",
pMsgDesc->uWrPtr, pMsgDesc->uRdPtr, pCmdDesc->uWrPtr, pCmdDesc->uRdPtr);
pStrBufDesc = dev->regs_base + DEC_MFD_XREG_SLV_BASE + MFD_MCX + MFD_MCX_OFF * index;
vpu_dbg(LVL_INFO, "data: wptr(0x%x) rptr(0x%x) start(0x%x) end(0x%x) uStrIdx(%d)\n",
pStrBufDesc->wptr, pStrBufDesc->rptr, pStrBufDesc->start, pStrBufDesc->end, index);
}
#endif
/*
* v4l2 ioctl() operation
*
*/
static struct vpu_v4l2_fmt formats_compressed_dec[] = {
{
.name = "H264 Encoded Stream",
.fourcc = V4L2_PIX_FMT_H264,
.num_planes = 1,
.vdec_std = VPU_VIDEO_AVC,
},
{
.name = "VC1 Encoded Stream",
.fourcc = V4L2_PIX_FMT_VC1_ANNEX_G,
.num_planes = 1,
.vdec_std = VPU_VIDEO_VC1,
},
{
.name = "VC1 RCV Encoded Stream",
.fourcc = V4L2_PIX_FMT_VC1_ANNEX_L,
.num_planes = 1,
.vdec_std = VPU_VIDEO_VC1,
},
{
.name = "MPEG2 Encoded Stream",
.fourcc = V4L2_PIX_FMT_MPEG2,
.num_planes = 1,
.vdec_std = VPU_VIDEO_MPEG2,
},
{
.name = "AVS Encoded Stream",
.fourcc = VPU_PIX_FMT_AVS,
.num_planes = 1,
.vdec_std = VPU_VIDEO_AVS,
},
{
.name = "MPEG4 ASP Encoded Stream",
.fourcc = VPU_PIX_FMT_ASP,
.num_planes = 1,
.vdec_std = VPU_VIDEO_ASP,
},
{
.name = "JPEG stills",
.fourcc = V4L2_PIX_FMT_JPEG,
.num_planes = 1,
.vdec_std = VPU_VIDEO_JPEG,
},
{
.name = "RV8 Encoded Stream",
.fourcc = VPU_PIX_FMT_RV8,
.num_planes = 1,
.vdec_std = VPU_VIDEO_RV8,
},
{
.name = "RV9 Encoded Stream",
.fourcc = VPU_PIX_FMT_RV9,
.num_planes = 1,
.vdec_std = VPU_VIDEO_RV9,
},
{
.name = "VP6 Encoded Stream",
.fourcc = VPU_PIX_FMT_VP6,
.num_planes = 1,
.vdec_std = VPU_VIDEO_VP6,
},
{
.name = "VP6 SPK Encoded Stream",
.fourcc = VPU_PIX_FMT_SPK,
.num_planes = 1,
.vdec_std = VPU_VIDEO_SPK,
},
{
.name = "VP8 Encoded Stream",
.fourcc = V4L2_PIX_FMT_VP8,
.num_planes = 1,
.vdec_std = VPU_VIDEO_VP8,
},
{
.name = "H264/MVC Encoded Stream",
.fourcc = V4L2_PIX_FMT_H264_MVC,
.num_planes = 1,
.vdec_std = VPU_VIDEO_AVC_MVC,
},
{
.name = "H265 HEVC Encoded Stream",
.fourcc = VPU_PIX_FMT_HEVC,
.num_planes = 1,
.vdec_std = VPU_VIDEO_HEVC,
},
{
.name = "VP9 Encoded Stream",
.fourcc = VPU_PIX_FMT_VP9,
.num_planes = 1,
.vdec_std = VPU_VIDEO_VP9,
},
{
.name = "Logo",
.fourcc = VPU_PIX_FMT_LOGO,
.num_planes = 1,
.vdec_std = VPU_VIDEO_UNDEFINED,
},
};
static struct vpu_v4l2_fmt formats_yuv_dec[] = {
{
.name = "4:2:0 2 Planes Y/CbCr",
.fourcc = V4L2_PIX_FMT_NV12,
.num_planes = 2,
.vdec_std = VPU_PF_YUV420_SEMIPLANAR,
},
};
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 B0", sizeof(cap->driver) - 1);
strlcpy(cap->card, "vpu B0", 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_dec))
return -EINVAL;
fmt = &formats_yuv_dec[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_dec))
return -EINVAL;
fmt = &formats_compressed_dec[f->index];
strlcpy(f->description, fmt->name, sizeof(f->description));
f->pixelformat = fmt->fourcc;
f->flags |= V4L2_FMT_FLAG_COMPRESSED;
return 0;
}
static void caculate_frame_size(struct vpu_ctx *ctx)
{
u_int32 width = ctx->pSeqinfo->uHorRes;
u_int32 height = ctx->pSeqinfo->uVerRes;
u_int32 luma_size;
u_int32 chroma_size;
u_int32 chroma_height;
bool bfield = false; //WARN need get it
bool bOffsetPadding = false; //WARN need get it
u_int32 uVertAlign = 256-1;
struct queue_data *q_data;
q_data = &ctx->q_data[V4L2_DST];
width = ((width + uVertAlign) & ~uVertAlign);
q_data->stride = width;
if (bfield)
height = ctx->pSeqinfo->uVerRes >> 0x1;
if (bOffsetPadding) {
height = ((height + 0xF) & 0xFFFFFFF0);
height += 0x10;
}
chroma_height = height >> 1;
height = ((height + uVertAlign) & ~uVertAlign);
chroma_height = ((chroma_height + uVertAlign) & ~uVertAlign);
luma_size = width * height;
chroma_size = width * chroma_height;
ctx->q_data[V4L2_DST].sizeimage[0] = luma_size;
ctx->q_data[V4L2_DST].sizeimage[1] = chroma_size;
}
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_CAPTURE_MPLANE) {
pix_mp->pixelformat = V4L2_PIX_FMT_NV12;
pix_mp->width = ctx->pSeqinfo->uHorRes;
pix_mp->height = ctx->pSeqinfo->uVerRes;
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].bytesperline = ctx->q_data[V4L2_DST].stride;
pix_mp->plane_fmt[i].sizeimage = ctx->q_data[V4L2_DST].sizeimage[i];
}
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_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 = 0;
pix_mp->pixelformat = ctx->q_data[V4L2_SRC].fourcc;
pix_mp->num_planes = 1;
} else
return -EINVAL;
return 0;
}
static void set_video_standard(struct queue_data *q_data,
struct v4l2_format *f,
struct vpu_v4l2_fmt *pformat_table,
uint32_t table_size)
{
unsigned int i;
for (i = 0; i < table_size; i++) {
if (pformat_table[i].fourcc == f->fmt.pix_mp.pixelformat)
q_data->vdec_std = pformat_table[i].vdec_std;
}
}
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;
u_int32 i;
vpu_dbg(LVL_INFO, "%s()\n", __func__);
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
q_data = &ctx->q_data[V4L2_DST];
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].bytesperline = ctx->q_data[V4L2_DST].stride;
pix_mp->plane_fmt[i].sizeimage = ctx->q_data[V4L2_DST].sizeimage[i];
}
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;
q_data->rect.left = 0;
q_data->rect.top = 0;
q_data->rect.width = pix_mp->width;
q_data->rect.height = pix_mp->height;
set_video_standard(q_data, f, formats_yuv_dec, ARRAY_SIZE(formats_yuv_dec));
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
q_data = &ctx->q_data[V4L2_SRC];
q_data->fourcc = pix_mp->pixelformat;
q_data->sizeimage[0] = pix_mp->plane_fmt[0].sizeimage;
set_video_standard(q_data, f, formats_compressed_dec, ARRAY_SIZE(formats_compressed_dec));
} 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 void alloc_mbi_buffer(struct vpu_ctx *ctx,
struct queue_data *This,
u_int32 count)
{
u_int32 uAlign = 0x800-1;
u_int32 mbi_num;
u_int32 mbi_size;
u_int32 i;
if (count >= MAX_MBI_NUM)
mbi_num = MAX_MBI_NUM;
else
mbi_num = count;
ctx->mbi_num = mbi_num;
mbi_size = (This->sizeimage[0]+This->sizeimage[1])/4;
mbi_size = ((mbi_size + uAlign) & ~uAlign);
ctx->mbi_size = mbi_size;
for (i = 0; i < mbi_num; i++) {
ctx->mbi_dma_virt[i] = dma_alloc_coherent(&ctx->dev->plat_dev->dev,
ctx->mbi_size,
(dma_addr_t *)&ctx->mbi_dma_phy[i],
GFP_KERNEL | GFP_DMA32
);
if (!ctx->mbi_dma_virt[i])
vpu_dbg(LVL_ERR, "error: %s() mbi buffer alloc size(%x) fail!\n", __func__, mbi_size);
}
}
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;
u_int32 i;
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);
if (!ret) {
if (reqbuf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
for (i = 0; i < reqbuf->count; i++)
q_data->vb2_reqs[i].status = FRAME_ALLOC;
alloc_mbi_buffer(ctx, q_data, reqbuf->count);
}
} else
vpu_dbg(LVL_ERR, "error: %s() can't request (%d) buffer\n", __func__, reqbuf->count);
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];
v4l2_update_stream_addr(ctx, 0);
} 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);
v4l2_update_stream_addr(ctx, 0);
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);
v4l2_update_stream_addr(ctx, 0);
if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
if (ctx->pSeqinfo->uBitDepthLuma > 8)
buf->reserved = 1;
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_OUTPUT_MPLANE) {
table_size = ARRAY_SIZE(formats_compressed_dec);
if (!format_is_support(formats_compressed_dec, table_size, f))
return -EINVAL;
} else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
table_size = ARRAY_SIZE(formats_yuv_dec);
if (!format_is_support(formats_yuv_dec, 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
)
{
struct vpu_ctx *ctx = v4l2_fh_to_ctx(fh);
vpu_dbg(LVL_INFO, "%s()\n", __func__);
cr->c.left = 0;
cr->c.top = 0;
cr->c.width = ctx->pSeqinfo->uHorDecodeRes;
cr->c.height = ctx->pSeqinfo->uVerDecodeRes;
return 0;
}
static int v4l2_ioctl_decoder_cmd(struct file *file,
void *fh,
struct v4l2_decoder_cmd *cmd
)
{
struct vpu_ctx *ctx = v4l2_fh_to_ctx(fh);
vpu_dbg(LVL_INFO, "%s()\n", __func__);
switch (cmd->cmd) {
case V4L2_DEC_CMD_START:
break;
case V4L2_DEC_CMD_STOP: {
vpu_dbg(LVL_INFO, "receive V4L2_DEC_CMD_STOP\n");
v4l2_vpu_send_cmd(ctx, ctx->str_index, VID_API_CMD_STOP, 0, NULL);
} break;
case V4L2_DEC_CMD_PAUSE:
break;
case V4L2_DEC_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);
if(i == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
wake_up_interruptible(&ctx->buffer_wq);
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);
if(i == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
if (!ctx->firmware_stopped) {
ctx->wait_rst_done = true;
v4l2_vpu_send_cmd(ctx, ctx->str_index, VID_API_CMD_ABORT, 0, NULL);
add_eos(ctx, 0);
wait_for_completion(&ctx->completion);
}
return ret;
}
static const struct v4l2_ioctl_ops v4l2_decoder_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_decoder_cmd = v4l2_ioctl_decoder_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,
};
// Set/Get controls - v4l2 control framework
static struct vpu_v4l2_control vpu_controls_dec[] = {
{
.id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.minimum = 1,
.maximum = 32,
.step = 1,
.default_value = 4,
.is_volatile = true,
},
};
#define NUM_CTRLS_DEC ARRAY_SIZE(vpu_controls_dec)
static int v4l2_dec_s_ctrl(struct v4l2_ctrl *ctrl)
{
switch (ctrl->id) {
default:
vpu_dbg(LVL_INFO, "%s() Invalid control(%d)\n",
__func__, ctrl->id);
return -EINVAL;
}
return 0;
}
static int v4l2_dec_g_v_ctrl(struct v4l2_ctrl *ctrl)
{
struct vpu_ctx *ctx = v4l2_ctrl_to_ctx(ctrl);
vpu_dbg(LVL_INFO, "%s() control(%d)\n",
__func__, ctrl->id);
switch (ctrl->id) {
case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
ctrl->val = ctx->pSeqinfo->uNumDPBFrms;
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_dec_ctrl_ops = {
.s_ctrl = v4l2_dec_s_ctrl,
.g_volatile_ctrl = v4l2_dec_g_v_ctrl,
};
static int ctrls_setup_decoder(struct vpu_ctx *This)
{
int i;
v4l2_ctrl_handler_init(&This->ctrl_handler,
NUM_CTRLS_DEC + 1
);
if (This->ctrl_handler.error) {
vpu_dbg(LVL_INFO, "%s() v4l2_ctrl_handler_init failed(%d)\n",
__func__, This->ctrl_handler.error);
return This->ctrl_handler.error;
} else {
vpu_dbg(LVL_INFO, "%s() v4l2_ctrl_handler_init ctrls(%ld)\n",
__func__, NUM_CTRLS_DEC);
This->ctrl_inited = true;
}
for (i = 0; i < NUM_CTRLS_DEC; i++) {
This->ctrls[i] = v4l2_ctrl_new_std(&This->ctrl_handler,
&vpu_dec_ctrl_ops,
vpu_controls_dec[i].id,
vpu_controls_dec[i].minimum,
vpu_controls_dec[i].maximum,
vpu_controls_dec[i].step,
vpu_controls_dec[i].default_value
);
if (This->ctrl_handler.error ||
!This->ctrls[i]
) {
vpu_dbg(LVL_INFO, "%s() v4l2_ctrl_new_std failed(%d) This->ctrls[%d](%p)\n",
__func__, This->ctrl_handler.error, i, This->ctrls[i]);
return This->ctrl_handler.error;
}
if (vpu_controls_dec[i].is_volatile &&
This->ctrls[i]
)
This->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE;
}
v4l2_ctrl_handler_setup(&This->ctrl_handler);
return 0;
}
static void ctrls_delete_decoder(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 < NUM_CTRLS_DEC; i++)
This->ctrls[i] = NULL;
}
static void add_eos(struct vpu_ctx *ctx, u_int32 uStrBufIdx)
{
struct vpu_dev *dev = ctx->dev;
pSTREAM_BUFFER_DESCRIPTOR_TYPE pStrBufDesc;
struct queue_data *q_data = &ctx->q_data[V4L2_SRC];
uint32_t start;
uint32_t end;
uint32_t wptr;
uint32_t rptr;
uint8_t *pbbuffer;
uint8_t buffer[MIN_SPACE];
uint32_t *plbuffer = (uint32_t *)buffer;
uint32_t last;
uint32_t last2 = 0x0;
uint32_t i;
vpu_dbg(LVL_INFO, "enter %s\n", __func__);
pStrBufDesc = ctx->dev->regs_base + DEC_MFD_XREG_SLV_BASE + MFD_MCX + MFD_MCX_OFF * ctx->str_index;
start = pStrBufDesc->start;
end = pStrBufDesc->end;
wptr = pStrBufDesc->wptr;
rptr = pStrBufDesc->rptr;
pbbuffer = (uint8_t *)(ctx->stream_buffer_virt + wptr - start);
switch (q_data->vdec_std) {
case VPU_VIDEO_AVC:
last = 0x0B010000;
break;
case VPU_VIDEO_VC1:
last = 0x0a010000;
break;
case VPU_VIDEO_MPEG2:
last = 0xb7010000;
break;
case VPU_VIDEO_ASP:
last = 0xb1010000;
break;
case VPU_VIDEO_SPK:
case VPU_VIDEO_VP6:
case VPU_VIDEO_VP8:
case VPU_VIDEO_RV8:
case VPU_VIDEO_RV9:
last = 0x34010000;
break;
case VPU_VIDEO_HEVC:
last = 0x4A010000;
last2 = 0x20;
break;
default:
last = 0x0;
break;
}
plbuffer[0] = last;
plbuffer[1] = last2;
for (i = 2; i < MIN_SPACE >> 2; i++)
plbuffer[i] = 0;
if ((wptr == rptr) || (wptr > rptr)) {
if (end - wptr >= MIN_SPACE) {
memcpy(pbbuffer, buffer, MIN_SPACE);
wptr += MIN_SPACE;
if (wptr == end)
wptr = start;
} else {
memcpy(pbbuffer, buffer, end-wptr);
memcpy(ctx->stream_buffer_virt, buffer + (end-wptr), MIN_SPACE - (end-wptr));
wptr = start + MIN_SPACE-(end-wptr);
}
} else {
memcpy(pbbuffer, buffer, MIN_SPACE);
wptr += MIN_SPACE;
}
pStrBufDesc->wptr = wptr;
dev->shared_mem.pSharedInterface->pStreamBuffDesc[ctx->str_index][uStrBufIdx] =
(VPU_REG_BASE + DEC_MFD_XREG_SLV_BASE + MFD_MCX + MFD_MCX_OFF * ctx->str_index);
vpu_dbg(LVL_INFO, "add eos MCX address virt=%p, phy=0x%x, index=%d\n", pStrBufDesc, dev->shared_mem.pSharedInterface->pStreamBuffDesc[ctx->str_index][uStrBufIdx], ctx->str_index);
}
TB_API_DEC_FMT vpu_format_remap(uint32_t vdec_std)
{
TB_API_DEC_FMT malone_format = VSys_FrmtNull;
switch (vdec_std) {
case VPU_VIDEO_AVC:
malone_format = VSys_AvcFrmt;
vpu_dbg(LVL_INFO, "format translated to AVC");
break;
case VPU_VIDEO_VC1:
malone_format = VSys_Vc1Frmt;
vpu_dbg(LVL_INFO, "format translated to VC1");
break;
case VPU_VIDEO_MPEG2:
malone_format = VSys_Mp2Frmt;
vpu_dbg(LVL_INFO, "format translated to MP2");
break;
case VPU_VIDEO_AVS:
malone_format = VSys_AvsFrmt;
vpu_dbg(LVL_INFO, "format translated to AVS");
break;
case VPU_VIDEO_ASP:
malone_format = VSys_AspFrmt;
vpu_dbg(LVL_INFO, "format translated to ASP");
break;
case VPU_VIDEO_JPEG:
malone_format = VSys_JpgFrmt;
vpu_dbg(LVL_INFO, "format translated to JPG");
break;
case VPU_VIDEO_VP6:
malone_format = VSys_Vp6Frmt;
vpu_dbg(LVL_INFO, "format translated to VP6");
break;
case VPU_VIDEO_SPK:
malone_format = VSys_SpkFrmt;
vpu_dbg(LVL_INFO, "format translated to SPK");
break;
case VPU_VIDEO_VP8:
malone_format = VSys_Vp8Frmt;
vpu_dbg(LVL_INFO, "format translated to VP8");
break;
case VPU_VIDEO_HEVC:
malone_format = VSys_HevcFrmt;
vpu_dbg(LVL_INFO, "format translated to HEVC");
break;
case VPU_VIDEO_RV8:
malone_format = VSys_RvFrmt;
vpu_dbg(LVL_INFO, "format translated to RV");
break;
case VPU_VIDEO_RV9:
malone_format = VSys_RvFrmt;
vpu_dbg(LVL_INFO, "format translated to RV");
break;
case VPU_VIDEO_AVC_MVC:
malone_format = VSys_AvcFrmt;
vpu_dbg(LVL_INFO, "format translated to AVC");
break;
default:
malone_format = VSys_FrmtNull;
vpu_dbg(LVL_INFO, "unspport format");
break;
}
vpu_dbg(LVL_INFO, "\n");
return malone_format;
}
static void v4l2_vpu_send_cmd(struct vpu_ctx *ctx, uint32_t idx, uint32_t cmdid, uint32_t cmdnum, uint32_t *local_cmddata)
{
vpu_log_cmd(cmdid, idx);
mutex_lock(&ctx->dev->cmd_mutex);
rpc_send_cmd_buf(&ctx->dev->shared_mem, idx, cmdid, cmdnum, local_cmddata);
mutex_unlock(&ctx->dev->cmd_mutex);
MU_SendMessage(ctx->dev->mu_base_virtaddr, 0, COMMAND);
}
static void transfer_buffer_to_firmware(struct vpu_ctx *ctx, void *input_buffer, uint32_t buffer_size, uint32_t vdec_std)
{
pSTREAM_BUFFER_DESCRIPTOR_TYPE pStrBufDesc;
u_int32 uStrBufIdx = 0; //set to be default 0, FIX_ME later
MediaIPFW_Video_UData *pUdataBuf =
&ctx->dev->shared_mem.pSharedInterface->UDataBuffer[ctx->str_index];
pDEC_RPC_HOST_IFACE pSharedInterface = ctx->dev->shared_mem.pSharedInterface;
unsigned int *CurrStrfg = &pSharedInterface->StreamConfig[ctx->str_index];
vpu_dbg(LVL_INFO, "enter %s, start_flag %d, index=%d\n", __func__, ctx->start_flag, ctx->str_index);
if (ctx->stream_buffer_size < buffer_size + MIN_SPACE)
vpu_dbg(LVL_INFO, "circular buffer size is too small\n");
memcpy(ctx->stream_buffer_virt, input_buffer, buffer_size);
vpu_dbg(LVL_INFO, "transfer data from virt 0x%p: size:%d\n", ctx->stream_buffer_virt, buffer_size);
pStrBufDesc = ctx->dev->regs_base + DEC_MFD_XREG_SLV_BASE + MFD_MCX + MFD_MCX_OFF * ctx->str_index;
// CAUTION: wptr must not be end
pStrBufDesc->wptr = ctx->stream_buffer_phy + buffer_size - ctx->dev->cm_offset;
pStrBufDesc->rptr = ctx->stream_buffer_phy - ctx->dev->cm_offset;
pStrBufDesc->start = ctx->stream_buffer_phy - ctx->dev->cm_offset;
pStrBufDesc->end = ctx->stream_buffer_phy + ctx->stream_buffer_size - ctx->dev->cm_offset;
pStrBufDesc->LWM = 0x01;
ctx->dev->shared_mem.pSharedInterface->pStreamBuffDesc[ctx->str_index][uStrBufIdx] =
(VPU_REG_BASE + DEC_MFD_XREG_SLV_BASE + MFD_MCX + MFD_MCX_OFF * ctx->str_index);
vpu_dbg(LVL_INFO, "transfer MCX address virt=%p, phy=0x%x, index=%d\n", pStrBufDesc, ctx->dev->shared_mem.pSharedInterface->pStreamBuffDesc[ctx->str_index][uStrBufIdx], ctx->str_index);
pUdataBuf->uUDataBase = ctx->udata_buffer_phy - ctx->dev->cm_offset;
pUdataBuf->uUDataSlotSize = ctx->udata_buffer_size;
VID_STREAM_CONFIG_FORMAT_SET(vpu_format_remap(vdec_std), CurrStrfg);
}
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]);
struct vb2_data_req *p_data_req;
void *data_mapped;
uint32_t buffer_size = vb->planes[0].bytesused;
data_mapped = (void *)vb2_plane_vaddr(vb, 0);
if (ctx->start_flag == true) {
transfer_buffer_to_firmware(ctx, data_mapped, buffer_size, This->vdec_std);
#ifdef HANDLE_EOS
if (vb->planes[0].bytesused < vb->planes[0].length)
add_eos(ctx, 0);
#endif
v4l2_vpu_send_cmd(ctx, ctx->str_index, VID_API_CMD_START, 0, NULL);
down(&This->drv_q_lock);
p_data_req = list_first_entry(&This->drv_q,
typeof(*p_data_req), list);
list_del(&p_data_req->list);
vb2_buffer_done(p_data_req->vb2_buf,
VB2_BUF_STATE_DONE
);
up(&This->drv_q_lock);
ctx->start_flag = false;
}
}
static int update_stream_addr(struct vpu_ctx *ctx, void *input_buffer, uint32_t buffer_size, uint32_t uStrBufIdx)
{
struct vpu_dev *dev = ctx->dev;
uint32_t index = ctx->str_index;
pSTREAM_BUFFER_DESCRIPTOR_TYPE pStrBufDesc;
uint32_t nfreespace = 0;
uint32_t wptr;
uint32_t rptr;
uint32_t start;
uint32_t end;
void *wptr_virt;
uint32_t ret = 1;
vpu_dbg(LVL_INFO, "enter %s\n", __func__);
// changed to virtual address and back
pStrBufDesc = dev->regs_base + DEC_MFD_XREG_SLV_BASE + MFD_MCX + MFD_MCX_OFF * index;
vpu_dbg(LVL_INFO, "%s wptr(%x) rptr(%x) start(%x) end(%x) uStrBufIdx(%d)\n",
__func__,
pStrBufDesc->wptr,
pStrBufDesc->rptr,
pStrBufDesc->start,
pStrBufDesc->end,
uStrBufIdx
);
wptr = pStrBufDesc->wptr;
rptr = pStrBufDesc->rptr;
start = pStrBufDesc->start;
end = pStrBufDesc->end;
wptr_virt = (void *)ctx->stream_buffer_virt + wptr - start;
vpu_dbg(LVL_INFO, "update_stream_addr down\n");
if (wptr == rptr)
nfreespace = end - start;
if (wptr < rptr)
nfreespace = rptr - wptr;
if (wptr > rptr)
nfreespace = (end - wptr) + (rptr - start);
if (nfreespace-buffer_size < MIN_SPACE)
return 0;
if (nfreespace >= buffer_size) {
if ((wptr == rptr) || (wptr > rptr)) {
if (end - wptr >= buffer_size) {
memcpy(wptr_virt, input_buffer, buffer_size);
wptr += buffer_size;
if (wptr == end)
wptr = start;
} else {
memcpy(wptr_virt, input_buffer, end-wptr);
memcpy(ctx->stream_buffer_virt, input_buffer + (end-wptr), buffer_size - (end-wptr));
wptr = start + buffer_size - (end-wptr);
}
} else {
memcpy(wptr_virt, input_buffer, buffer_size);
wptr += buffer_size;
}
} else {
vpu_dbg(LVL_INFO, "buffer_full: the circular buffer freespace < buffer_size, treat as full");
return 0; //do not consider this situation now
}
pStrBufDesc->wptr = wptr;
vpu_dbg(LVL_INFO, "update_stream_addr up, wptr 0x%x\n", wptr);
dev->shared_mem.pSharedInterface->pStreamBuffDesc[index][uStrBufIdx] =
(VPU_REG_BASE + DEC_MFD_XREG_SLV_BASE + MFD_MCX + MFD_MCX_OFF * index);
vpu_dbg(LVL_INFO, "update address virt=%p, phy=0x%x, index=%d\n", pStrBufDesc, dev->shared_mem.pSharedInterface->pStreamBuffDesc[ctx->str_index][uStrBufIdx], ctx->str_index);
return ret;
}
//warn uStrIdx need to refine how to handle it
static void v4l2_update_stream_addr(struct vpu_ctx *ctx, uint32_t uStrBufIdx)
{
struct vb2_data_req *p_data_req;
struct queue_data *This = &ctx->q_data[V4L2_SRC];
void *input_buffer;
uint32_t buffer_size;
down(&This->drv_q_lock);
while (!list_empty(&This->drv_q)) {
p_data_req = list_first_entry(&This->drv_q,
typeof(*p_data_req), list);
buffer_size = p_data_req->vb2_buf->planes[0].bytesused;
input_buffer = (void *)vb2_plane_vaddr(p_data_req->vb2_buf, 0);
if (!update_stream_addr(ctx, input_buffer, buffer_size, uStrBufIdx)) {
up(&This->drv_q_lock);
vpu_dbg(LVL_INFO, " %s no space to write\n", __func__);
return;
}
#ifdef HANDLE_EOS
if (buffer_size < p_data_req->vb2_buf->planes[0].length)
add_eos(ctx, uStrBufIdx); //WARN EOS should be refined later, as need to add eos by userspace or pass a flag to deal with when buffer_size == p_data_req->vb2_buf->planes[0].length
#endif
list_del(&p_data_req->list);
vb2_buffer_done(p_data_req->vb2_buf,
VB2_BUF_STATE_DONE
);
}
up(&This->drv_q_lock);
}
static void report_buffer_done(struct vpu_ctx *ctx, void *frame_info)
{
struct vb2_data_req *p_data_req, *p_temp;
struct queue_data *This = &ctx->q_data[V4L2_DST];
u_int32 *FrameInfo = (u_int32 *)frame_info;
u_int32 fs_id = FrameInfo[0x0];
uint32_t stride = FrameInfo[3];
bool b10BitFormat = (ctx->pSeqinfo->uBitDepthLuma >> 8) || (ctx->pSeqinfo->uBitDepthChroma >> 8);
u_int32 buffer_id;
vpu_dbg(LVL_INFO, "report_buffer_done fs_id=%d, ulFsLumaBase[0]=%x, stride=%d, b10BitFormat=%d\n", fs_id, FrameInfo[1], stride, b10BitFormat);
v4l2_update_stream_addr(ctx, 0);
buffer_id = find_buffer_id(ctx, FrameInfo[1]);
if (buffer_id != fs_id)
vpu_dbg(LVL_ERR, "error: find buffer_id(%d) and firmware return id(%d) doesn't match\n",
buffer_id, fs_id);
if (ctx->q_data[V4L2_DST].vb2_reqs[buffer_id].status == FRAME_DECODED)
ctx->q_data[V4L2_DST].vb2_reqs[buffer_id].status = FRAME_READY;
else
vpu_dbg(LVL_ERR, "error: buffer(%d) need to set FRAME_READY, but previous state %s is not FRAME_DECODED\n",
buffer_id, bufstat[ctx->q_data[V4L2_DST].vb2_reqs[buffer_id].status]);
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) {
if(p_data_req->id == fs_id)
list_del(&p_data_req->list);
}
}
p_data_req = &This->vb2_reqs[buffer_id];
p_data_req->vb2_buf->planes[0].bytesused = This->sizeimage[0];
p_data_req->vb2_buf->planes[1].bytesused = This->sizeimage[1];
if(p_data_req->vb2_buf->state == VB2_BUF_STATE_ACTIVE)
vb2_buffer_done(p_data_req->vb2_buf,
VB2_BUF_STATE_DONE
);
else vpu_dbg(LVL_ERR, "error: check buffer(%d) state(%d)\n", buffer_id, p_data_req->vb2_buf->state);
up(&This->drv_q_lock);
vpu_dbg(LVL_INFO, "leave %s\n", __func__);
}
static void vpu_api_event_handler(struct vpu_ctx *ctx, u_int32 uStrIdx, u_int32 uEvent, u_int32 *event_data)
{
struct vpu_dev *dev = ctx->dev;
pDEC_RPC_HOST_IFACE pSharedInterface = (pDEC_RPC_HOST_IFACE)dev->shared_mem.shared_mem_vir;
vpu_log_event(uEvent, uStrIdx);
switch (uEvent) {
case VID_API_EVENT_START_DONE:
break;
case VID_API_EVENT_STOPPED: {
const struct v4l2_event ev = {
.type = V4L2_EVENT_EOS
};
v4l2_event_queue_fh(&ctx->fh, &ev);
ctx->firmware_stopped = true;
vpu_dbg(LVL_INFO, "send V4L2_EVENT_EOS\n");
}
break;
case VID_API_EVENT_RESET_DONE:
break;
case VID_API_EVENT_PIC_DECODED: {
MediaIPFW_Video_QMeterInfo *pQMeterInfo = (MediaIPFW_Video_QMeterInfo *)dev->shared_mem.qmeter_mem_vir;
MediaIPFW_Video_PicInfo *pPicInfo = (MediaIPFW_Video_PicInfo *)dev->shared_mem.pic_mem_vir;
MediaIPFW_Video_PicDispInfo *pDispInfo = &pPicInfo[uStrIdx].DispInfo;
MediaIPFW_Video_PicPerfInfo *pPerfInfo = &pPicInfo[uStrIdx].PerfInfo;
MediaIPFW_Video_PicPerfDcpInfo *pPerfDcpInfo = &pPicInfo[uStrIdx].PerfDcpInfo;
u_int32 buffer_id;
vpu_dbg(LVL_INFO, "PICINFO GET: uPicType:%d uPicStruct:%d uPicStAddr:0x%x uFrameStoreID:%d uPercentInErr:%d, uRbspBytesCount=%d, ulLumBaseAddr[0]=%x, pQMeterInfo:%p, pPicInfo:%p, pDispInfo:%p, pPerfInf:%p, pPerfDcpInfo:%p\n",
pPicInfo[uStrIdx].uPicType, pPicInfo[uStrIdx].uPicStruct,
pPicInfo[uStrIdx].uPicStAddr, pPicInfo[uStrIdx].uFrameStoreID,
pPicInfo[uStrIdx].uPercentInErr, pPerfInfo->uRbspBytesCount, event_data[0],
pQMeterInfo, pPicInfo, pDispInfo, pPerfInfo, pPerfDcpInfo);
buffer_id = find_buffer_id(ctx, event_data[0]);
if (buffer_id != pPicInfo[uStrIdx].uFrameStoreID)
vpu_dbg(LVL_ERR, "error: VID_API_EVENT_PIC_DECODED address and id doesn't match\n");
if (ctx->q_data[V4L2_DST].vb2_reqs[buffer_id].status == FRAME_FREE)
ctx->q_data[V4L2_DST].vb2_reqs[buffer_id].status = FRAME_DECODED;
else
vpu_dbg(LVL_ERR, "error: buffer(%d) need to set FRAME_DECODED, but previous state %s is not FRAME_FREE\n",
buffer_id, bufstat[ctx->q_data[V4L2_DST].vb2_reqs[buffer_id].status]);
}
break;
case VID_API_EVENT_SEQ_HDR_FOUND: {
MediaIPFW_Video_SeqInfo *pSeqInfo = (MediaIPFW_Video_SeqInfo *)dev->shared_mem.seq_mem_vir;
// MediaIPFW_Video_FrameBuffer *pStreamFrameBuffer = &pSharedInterface->StreamFrameBuffer[uStrIdx];
// MediaIPFW_Video_FrameBuffer *pStreamDCPBuffer = &pSharedInterface->StreamDCPBuffer[uStrIdx];
MediaIPFW_Video_PitchInfo *pStreamPitchInfo = &pSharedInterface->StreamPitchInfo[uStrIdx];
unsigned int num = pSharedInterface->SeqInfoTabDesc.uNumSizeDescriptors;
if (ctx->pSeqinfo == NULL)
ctx->pSeqinfo = kzalloc(sizeof(MediaIPFW_Video_SeqInfo), GFP_KERNEL);
else
vpu_dbg(LVL_INFO, "pSeqinfo is not NULL, need not to realloc\n");
memcpy(ctx->pSeqinfo, &pSeqInfo[ctx->str_index], sizeof(MediaIPFW_Video_SeqInfo));
caculate_frame_size(ctx);
vpu_dbg(LVL_INFO, "SEQINFO GET: uHorRes:%d uVerRes:%d uHorDecodeRes:%d uVerDecodeRes:%d uNumDPBFrms:%d, num=%d\n",
ctx->pSeqinfo->uHorRes, ctx->pSeqinfo->uVerRes,
ctx->pSeqinfo->uHorDecodeRes, ctx->pSeqinfo->uVerDecodeRes,
ctx->pSeqinfo->uNumDPBFrms, num);
if (ctx->b_firstseq) {
const struct v4l2_event ev = {
.type = V4L2_EVENT_SOURCE_CHANGE,
.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION
};
v4l2_event_queue_fh(&ctx->fh, &ev);
pStreamPitchInfo->uFramePitch = 0x4000;
ctx->b_firstseq = false;
}
}
break;
case VID_API_EVENT_PIC_HDR_FOUND:
break;
case VID_API_EVENT_REQ_FRAME_BUFF: {
MEDIA_PLAYER_FSREQ *pFSREQ = (MEDIA_PLAYER_FSREQ *)event_data;
u_int32 local_cmddata[10];
struct vb2_data_req *p_data_req, *p_temp;
struct queue_data *This = &ctx->q_data[V4L2_DST];
u_int32 LumaAddr, ChromaAddr;
u_int32 *pphy_address;
struct vb2_data_req;
void *dcp_dma_virt;
dma_addr_t dcp_dma_phy;
bool buffer_flag = false;
vpu_dbg(LVL_INFO, "VID_API_EVENT_REQ_FRAME_BUFF, type=%d, size=%ld\n", pFSREQ->eType, sizeof(MEDIA_PLAYER_FSREQ));
if (pFSREQ->eType == MEDIAIP_DCP_REQ) {
if (ctx->dcp_count >= MAX_DCP_NUM)
vpu_dbg(LVL_ERR, "error: request dcp buffers number is over MAX_DCP_NUM\n");
ctx->uDCPSize = DCP_SIZE;
dcp_dma_virt = dma_alloc_coherent(&ctx->dev->plat_dev->dev,
ctx->uDCPSize,
(dma_addr_t *)&dcp_dma_phy,
GFP_KERNEL | GFP_DMA32
);
if (!dcp_dma_virt)
vpu_dbg(LVL_ERR, "error: %s() dcp buffer alloc size(%x) fail!\n", __func__, DCP_SIZE);
ctx->dcp_dma_virt[ctx->dcp_count] = dcp_dma_virt;
ctx->dcp_dma_phy[ctx->dcp_count] = dcp_dma_phy;
local_cmddata[0] = ctx->dcp_count;
local_cmddata[1] = dcp_dma_phy - ctx->dev->cm_offset;
local_cmddata[2] = DCP_SIZE;
local_cmddata[3] = 0;
local_cmddata[4] = 0;
local_cmddata[5] = 0;
local_cmddata[6] = pFSREQ->eType;
v4l2_vpu_send_cmd(ctx, uStrIdx, VID_API_CMD_FS_ALLOC, 7, local_cmddata);
vpu_dbg(LVL_INFO, "VID_API_CMD_FS_ALLOC, eType=%d, index=%d\n", pFSREQ->eType, ctx->dcp_count);
ctx->dcp_count++;
} else if (pFSREQ->eType == MEDIAIP_MBI_REQ) {
if (ctx->mbi_count >= ctx->mbi_num)
vpu_dbg(LVL_ERR, "error: request mbi buffers number(%d) is over allocted buffer number(%d)\n",
ctx->mbi_count, ctx->mbi_num);
local_cmddata[0] = ctx->mbi_count;
local_cmddata[1] = ctx->mbi_dma_phy[ctx->mbi_count] - ctx->dev->cm_offset;
local_cmddata[2] = ctx->mbi_size;
local_cmddata[3] = 0;
local_cmddata[4] = 0;
local_cmddata[5] = 0;
local_cmddata[6] = pFSREQ->eType;
v4l2_vpu_send_cmd(ctx, uStrIdx, VID_API_CMD_FS_ALLOC, 7, local_cmddata);
vpu_dbg(LVL_INFO, "VID_API_CMD_FS_ALLOC, eType=%d, index=%d\n", pFSREQ->eType, ctx->mbi_count);
ctx->mbi_count++;
} else {
#if 0
while (timeout_count < MAX_TIMEOUT_COUNT) {
if (!wait_event_interruptible_timeout(ctx->buffer_wq,
!list_empty(&This->drv_q),
msecs_to_jiffies(1000)))
vpu_dbg(LVL_ERR, " error: wait_event_interruptible_timeout wait timeout\n");
else {
vpu_dbg(LVL_INFO, " wait_event_interruptible_timeout list is not empty now\n");
if (!list_empty(&This->drv_q))
break;
}
if (!list_empty(&This->drv_q))
break;
timeout_count++;
}
#endif
wait_event_interruptible(ctx->buffer_wq,
((!list_empty(&This->drv_q)) || (ctx->wait_rst_done==true)));
if (!list_empty(&This->drv_q)) {
down(&This->drv_q_lock);
list_for_each_entry_safe(p_data_req, p_temp, &This->drv_q, list) {
if (p_data_req->status == FRAME_ALLOC
|| p_data_req->status == FRAME_RELEASE){
pphy_address = (u_int32 *)vb2_plane_cookie(p_data_req->vb2_buf, 0);
LumaAddr = *pphy_address;
pphy_address = (u_int32 *)vb2_plane_cookie(p_data_req->vb2_buf, 1);
ChromaAddr = *pphy_address;
vpu_dbg(LVL_INFO, "%s :LumaAddr(%x) ChromaAddr(%x) buf_id (%d)\n",
__func__,
LumaAddr,
ChromaAddr,
p_data_req->id
);
local_cmddata[0] = p_data_req->id;
local_cmddata[1] = LumaAddr - ctx->dev->cm_offset;
local_cmddata[2] = local_cmddata[1] + This->sizeimage[0]/2;
local_cmddata[3] = ChromaAddr - ctx->dev->cm_offset;
local_cmddata[4] = local_cmddata[3] + This->sizeimage[1]/2;
local_cmddata[5] = ctx->q_data[V4L2_DST].stride;
local_cmddata[6] = pFSREQ->eType;
//WARN :need to check the call back VID_API_EVENT_REL_FRAME_BUFF later, when it is received, the corepond id can be released, now just do a temporary workaround
if (p_data_req->status == FRAME_RELEASE)
v4l2_vpu_send_cmd(ctx, uStrIdx, VID_API_CMD_FS_RELEASE, 1, &p_data_req->id);
v4l2_vpu_send_cmd(ctx, uStrIdx, VID_API_CMD_FS_ALLOC, 7, local_cmddata);
p_data_req->status = FRAME_FREE;
vpu_dbg(LVL_INFO, "VID_API_CMD_FS_ALLOC, data_req->vb2_buf=%p, data_req->id=%d\n", p_data_req->vb2_buf, p_data_req->id);
list_del(&p_data_req->list);
buffer_flag = true;
break;
} else {
vpu_dbg(LVL_ERR, "error: buffer %d status=0x%x is not right, find next\n", p_data_req->id, p_data_req->status);
continue;
}
}
up(&This->drv_q_lock);
if (buffer_flag == false)
vpu_dbg(LVL_ERR, "error: don't find the right buffer for VID_API_CMD_FS_ALLOC\n");
} else if(ctx->wait_rst_done) {
u_int32 i;
for(i=0; i< VPU_MAX_BUFFER; i++) {
p_data_req = &This->vb2_reqs[i];
if(p_data_req->status==FRAME_RELEASE)
break;
}
pphy_address = (u_int32 *)vb2_plane_cookie(p_data_req->vb2_buf, 0);
LumaAddr = *pphy_address;
pphy_address = (u_int32 *)vb2_plane_cookie(p_data_req->vb2_buf, 1);
ChromaAddr = *pphy_address;
vpu_dbg(LVL_INFO, "%s :LumaAddr(%x) ChromaAddr(%x) buf_id (%d)\n",
__func__,
LumaAddr,
ChromaAddr,
p_data_req->id
);
local_cmddata[0] = p_data_req->id;
local_cmddata[1] = LumaAddr - ctx->dev->cm_offset;
local_cmddata[2] = local_cmddata[1] + This->sizeimage[0]/2;
local_cmddata[3] = ChromaAddr - ctx->dev->cm_offset;
local_cmddata[4] = local_cmddata[3] + This->sizeimage[1]/2;
local_cmddata[5] = ctx->q_data[V4L2_DST].stride;
local_cmddata[6] = pFSREQ->eType;
//WARN :need to check the call back VID_API_EVENT_REL_FRAME_BUFF later, when it is received, the corepond id can be released, now just do a temporary workaround
if (p_data_req->status == FRAME_RELEASE)
v4l2_vpu_send_cmd(ctx, uStrIdx, VID_API_CMD_FS_RELEASE, 1, &p_data_req->id);
v4l2_vpu_send_cmd(ctx, uStrIdx, VID_API_CMD_FS_ALLOC, 7, local_cmddata);
p_data_req->status = FRAME_FREE;
vpu_dbg(LVL_INFO, "VID_API_CMD_FS_ALLOC, data_req->vb2_buf=%p, data_req->id=%d\n", p_data_req->vb2_buf, p_data_req->id);
} else
vpu_dbg(LVL_ERR, "error: wait timeout, but the list is still empty");
}
}
break;
case VID_API_EVENT_REL_FRAME_BUFF: {
MEDIA_PLAYER_FSREL *fsrel = (MEDIA_PLAYER_FSREL *)event_data;
struct queue_data *This = &ctx->q_data[V4L2_DST];
struct vb2_data_req *p_data_req;
if (fsrel->eType == MEDIAIP_FRAME_REQ) {
p_data_req = &This->vb2_reqs[fsrel->uFSIdx];
if (p_data_req->status == FRAME_READY)
p_data_req->status = FRAME_RELEASE;
else {
if (ctx->wait_rst_done == true) {
p_data_req->status = FRAME_RELEASE;
down(&This->drv_q_lock);
list_add_tail(&p_data_req->list, &This->drv_q);
up(&This->drv_q_lock);
} else
vpu_dbg(LVL_ERR, "error: normal release need to set status to FRAME_RELEASE but previous status %s is not FRAME_READY\n", bufstat[p_data_req->status]);
}
}
vpu_dbg(LVL_INFO, "VID_API_EVENT_REL_FRAME_BUFF uFSIdx=%d, eType=%d, size=%ld\n", fsrel->uFSIdx, fsrel->eType, sizeof(MEDIA_PLAYER_FSREL));
} break;
case VID_API_EVENT_FRAME_BUFF_RDY: {
u_int32 *FrameInfo = (u_int32 *)event_data;
report_buffer_done(ctx, FrameInfo);
}
break;
case VID_API_EVENT_CHUNK_DECODED:
break;
case VID_API_EVENT_FIFO_LOW: {
struct vpu_dev *dev = ctx->dev;
u_int32 uStrBufIdx = 0; //use buffer 0 for the stream
pSTREAM_BUFFER_DESCRIPTOR_TYPE pStrBufDesc;
pStrBufDesc = dev->regs_base + DEC_MFD_XREG_SLV_BASE + MFD_MCX + MFD_MCX_OFF * ctx->str_index;
vpu_dbg(LVL_INFO, "%s wptr(%x) rptr(%x) start(%x) end(%x) uStrIdx(%d)\n",
__func__,
pStrBufDesc->wptr,
pStrBufDesc->rptr,
pStrBufDesc->start,
pStrBufDesc->end,
uStrIdx
);
v4l2_update_stream_addr(ctx, uStrBufIdx);
}
break;
case VID_API_EVENT_FIFO_HIGH:
break;
case VID_API_EVENT_FIFO_EMPTY:
break;
case VID_API_EVENT_FIFO_FULL:
break;
case VID_API_EVENT_FIFO_OVF:
break;
case VID_API_EVENT_BS_ERROR:
break;
case VID_API_EVENT_UDATA_FIFO_UPTD:
break;
case VID_API_EVENT_DBG_STAT_UPDATE:
break;
case VID_API_EVENT_DBG_LOG_STARTED:
break;
case VID_API_EVENT_DBG_LOG_STOPPED:
break;
case VID_API_EVENT_ABORT_DONE: {
pSTREAM_BUFFER_DESCRIPTOR_TYPE pStrBufDesc;
pStrBufDesc = dev->regs_base + DEC_MFD_XREG_SLV_BASE + MFD_MCX + MFD_MCX_OFF * ctx->str_index;
vpu_dbg(LVL_INFO, "%s wptr(%x) rptr(%x) start(%x) end(%x)\n",
__func__,
pStrBufDesc->wptr,
pStrBufDesc->rptr,
pStrBufDesc->start,
pStrBufDesc->end
);
pStrBufDesc->wptr = pStrBufDesc->start;
v4l2_vpu_send_cmd(ctx, uStrIdx, VID_API_CMD_RST_BUF, 0, NULL);
}
break;
case VID_API_EVENT_RES_CHANGE:
{
const struct v4l2_event ev = {
.type = V4L2_EVENT_SOURCE_CHANGE,
.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION
};
v4l2_event_queue_fh(&ctx->fh, &ev);
}
break;
case VID_API_EVENT_STR_BUF_RST: {
pSTREAM_BUFFER_DESCRIPTOR_TYPE pStrBufDesc;
struct vb2_data_req *p_data_req;
u_int32 i;
pStrBufDesc = dev->regs_base + DEC_MFD_XREG_SLV_BASE + MFD_MCX + MFD_MCX_OFF * ctx->str_index;
vpu_dbg(LVL_INFO, "%s wptr(%x) rptr(%x) start(%x) end(%x)\n",
__func__,
pStrBufDesc->wptr,
pStrBufDesc->rptr,
pStrBufDesc->start,
pStrBufDesc->end
);
ctx->wait_rst_done = false;
for (i = 0; i < VPU_MAX_BUFFER; i++) {
p_data_req = &ctx->q_data[V4L2_DST].vb2_reqs[i];
if (p_data_req->vb2_buf != NULL)
if (p_data_req->status != FRAME_RELEASE)
vpu_dbg(LVL_ERR, "error: buffer(%d) status is %s when receive VID_API_EVENT_STR_BUF_RST\n", i, bufstat[p_data_req->status]);
}
complete(&ctx->completion);
}
break;
case VID_API_EVENT_RET_PING:
break;
case VID_API_EVENT_STR_FMT_CHANGE:
break;
case VID_API_EVENT_FINISHED:
v4l2_vpu_send_cmd(ctx, uStrIdx, VID_API_CMD_STOP, 0, NULL);
break;
default:
break;
}
vpu_dbg(LVL_INFO, "leave %s, uEvent %d\n", __func__, uEvent);
}
//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-mu0-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, "error: 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, "error: 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(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(This) == API_MSG_AVAILABLE) {
rpc_receive_msg_buf(This, &msg);
ctx = dev->ctx[msg.idx];
vpu_api_event_handler(ctx, msg.idx, msg.msgid, msg.msgdata);
}
if (rpc_MediaIPFW_Video_message_check(This) == API_MSG_BUFFER_ERROR)
vpu_dbg(LVL_ERR, "error: message size is too big to handle\n");
}
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)
) {
if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_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;
}
} else {
*plane_count = 1;
psize[0] = This->sizeimage[0];
}
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
)
{
int ret = 0;
vpu_dbg(LVL_INFO, "%s() is called\n", __func__);
return ret;
}
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 = NULL;
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);
vpu_dbg(LVL_INFO, "before c_port_buf_queue up, vq->type=%d, vb->index=%d\n", vq->type, vb->index);
up(&This->drv_q_lock);
vpu_dbg(LVL_INFO, "c_port_buf_queue up\n");
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__);
}
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;
u_int32 i;
vpu_dbg(LVL_INFO, "%s()\n", __func__);
for (i = 0; i < VPU_MAX_BUFFER; i++)
This->vb2_reqs[i].status = 0;
// 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, "error: %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 flush_drv_q(struct queue_data *This)
{
struct vb2_data_req *p_data_req = NULL;
struct vb2_data_req *p_temp;
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);
vb2_buffer_done(p_data_req->vb2_buf, VB2_BUF_STATE_ERROR);
}
}
INIT_LIST_HEAD(&This->drv_q);
up(&This->drv_q_lock);
}
static void release_queue_data(struct vpu_ctx *ctx)
{
struct queue_data *This = &ctx->q_data[V4L2_SRC];
if (This->vb2_q_inited) {
flush_drv_q(This);
vb2_queue_release(&This->vb2_q);
}
This = &ctx->q_data[V4L2_DST];
if (This->vb2_q_inited) {
flush_drv_q(This);
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, "error: %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(0x2d040000, 4);
writel(This->m0_p_fw_space_phy, csr_offset);
csr_cpuwait = ioremap(0x2d040004, 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->ctrl_inited = false;
ctrls_setup_decoder(ctx);
ctx->fh.ctrl_handler = &ctx->ctrl_handler;
ctx->dev = dev;
ctx->str_index = idx;
dev->ctx[idx] = ctx;
ctx->b_firstseq = true;
ctx->start_flag = true;
ctx->wait_rst_done = false;
ctx->firmware_stopped = false;
ctx->pSeqinfo = kzalloc(sizeof(MediaIPFW_Video_SeqInfo), GFP_KERNEL);
if (!ctx->pSeqinfo)
vpu_dbg(LVL_ERR, "error: pSeqinfo alloc fail\n");
init_queue_data(ctx);
init_waitqueue_head(&ctx->buffer_wq);
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);
rpc_set_stream_cfg_value(dev->shared_mem.pSharedInterface, ctx->str_index);
for (i = 0; i < MAX_DCP_NUM; i++) {
ctx->dcp_dma_virt[i] = NULL;
ctx->dcp_dma_phy[i] = 0;
}
ctx->dcp_count = 0;
for (i = 0; i < MAX_MBI_NUM; i++) {
ctx->mbi_dma_virt[i] = NULL;
ctx->mbi_dma_phy[i] = 0;
}
ctx->mbi_count = 0;
ctx->mbi_num = 0;
ctx->mbi_size = 0;
ctx->stream_buffer_size = MAX_BUFFER_SIZE;
ctx->stream_buffer_virt = dma_alloc_coherent(&ctx->dev->plat_dev->dev,
ctx->stream_buffer_size,
(dma_addr_t *)&ctx->stream_buffer_phy,
GFP_KERNEL | GFP_DMA32
);
if (!ctx->stream_buffer_virt)
vpu_dbg(LVL_ERR, "error: %s() stream buffer alloc size(%x) fail!\n", __func__, ctx->stream_buffer_size);
else
vpu_dbg(LVL_INFO, "%s() stream_buffer_size(%d) stream_buffer_virt(%p) stream_buffer_phy(%p)\n",
__func__, ctx->stream_buffer_size, ctx->stream_buffer_virt, (void *)ctx->stream_buffer_phy);
ctx->udata_buffer_size = UDATA_BUFFER_SIZE;
ctx->udata_buffer_virt = dma_alloc_coherent(&ctx->dev->plat_dev->dev,
ctx->udata_buffer_size,
(dma_addr_t *)&ctx->udata_buffer_phy,
GFP_KERNEL | GFP_DMA32
);
if (!ctx->udata_buffer_virt)
vpu_dbg(LVL_ERR, "error: %s() udata buffer alloc size(%x) fail!\n", __func__, ctx->udata_buffer_size);
else
vpu_dbg(LVL_INFO, "%s() udata_buffer_size(%d) udata_buffer_virt(%p) udata_buffer_phy(%p)\n",
__func__, ctx->udata_buffer_size, ctx->udata_buffer_virt, (void *)ctx->udata_buffer_phy);
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_decoder(ctx);
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
clear_bit(ctx->str_index, &dev->instance_mask);
for (i = 0; i < MAX_DCP_NUM; i++)
if (ctx->dcp_dma_virt[i] != NULL)
dma_free_coherent(&ctx->dev->plat_dev->dev,
ctx->uDCPSize,
ctx->dcp_dma_virt[i],
ctx->dcp_dma_phy[i]
);
for (i = 0; i < MAX_MBI_NUM; i++)
if (ctx->mbi_dma_virt[i] != NULL)
dma_free_coherent(&ctx->dev->plat_dev->dev,
ctx->mbi_size,
ctx->mbi_dma_virt[i],
ctx->mbi_dma_phy[i]
);
if (ctx->stream_buffer_virt)
dma_free_coherent(&ctx->dev->plat_dev->dev,
ctx->stream_buffer_size,
ctx->stream_buffer_virt,
ctx->stream_buffer_phy
);
if (ctx->udata_buffer_virt)
dma_free_coherent(&ctx->dev->plat_dev->dev,
ctx->udata_buffer_size,
ctx->udata_buffer_virt,
ctx->udata_buffer_phy
);
if (ctx->pSeqinfo) {
kfree(ctx->pSeqinfo);
ctx->pSeqinfo = NULL;
}
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_decoder_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_decoder = {
.name = "vpu decoder",
.fops = &v4l2_decoder_fops,
.ioctl_ops = &v4l2_decoder_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, "error: --- set_vpu_pwr no IPC handle\n");
goto set_vpu_pwrexit;
}
// Power on or off PID0, DEC, ENC
sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_VPU_PID0, pm);
if (sciErr != SC_ERR_NONE) {
vpu_dbg(LVL_ERR, "error: --- 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, "error: --- 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, "error: --- 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, "error: --- 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, "error: --- 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, "error: --- 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, "error: --- 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, "error: --- 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_DEC, pm);
if (sciErr != SC_ERR_NONE) {
vpu_dbg(LVL_ERR, "error: --- sc_pm_set_resource_power_mode(SC_R_VPU_DEC,%d) SCI error! (%d)\n", sciErr, pm);
goto set_vpu_pwrexit;
}
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
sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_VPU_DEC_0, pm);
if (sciErr != SC_ERR_NONE) {
vpu_dbg(LVL_ERR, "error: --- sc_pm_set_resource_power_mode(SC_R_VPU_DEC_0,%d) SCI error! (%d)\n", sciErr, pm);
goto set_vpu_pwrexit;
}
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;
}
#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, "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);
if (ret)
vpu_dbg(LVL_ERR, "error: 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_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 = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dev->regs_base)) {
vpu_dbg(LVL_ERR, "error: %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;
dev->cm_offset = 0;
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, "error: %s unable to register v4l2 dev\n", __func__);
return ret;
}
platform_set_drvdata(pdev, dev);
dev->pvpu_decoder_dev = video_device_alloc();
if (dev->pvpu_decoder_dev) {
strncpy(dev->pvpu_decoder_dev->name, v4l2_videodevice_decoder.name, sizeof(v4l2_videodevice_decoder.name));
dev->pvpu_decoder_dev->fops = v4l2_videodevice_decoder.fops;
dev->pvpu_decoder_dev->ioctl_ops = v4l2_videodevice_decoder.ioctl_ops;
dev->pvpu_decoder_dev->release = video_device_release;
dev->pvpu_decoder_dev->vfl_dir = v4l2_videodevice_decoder.vfl_dir;
dev->pvpu_decoder_dev->v4l2_dev = &dev->v4l2_dev;
video_set_drvdata(dev->pvpu_decoder_dev, dev);
if (video_register_device(dev->pvpu_decoder_dev,
VFL_TYPE_GRABBER,
-1)) {
vpu_dbg(LVL_ERR, "error: %s unable to register video decoder device\n",
__func__
);
video_device_release(dev->pvpu_decoder_dev);
dev->pvpu_decoder_dev = NULL;
} else {
vpu_dbg(LVL_INFO, "error: %s register video decoder device\n",
__func__
);
}
}
if (!dev->mu_ipcHandle) {
ret = sc_ipc_getMuID(&mu_id);
if (ret) {
vpu_dbg(LVL_ERR, "error: --- 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, "error: --- sc_ipc_getMuID() cannot open MU channel to SCU error! (%d)\n", ret);
return ret;
}
}
vpu_enable_hw(dev);
mutex_init(&dev->dev_mutex);
mutex_init(&dev->cmd_mutex);
dev->fw_is_ready = false;
dev->workqueue = alloc_workqueue("vpu", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
if (!dev->workqueue) {
vpu_dbg(LVL_ERR, "error: %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, "error: %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, "error: 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, "error: failed to remap space for rpc shared memory\n");
memset_io(dev->m0_rpc_virt, 0, SHARED_SIZE);
#ifdef CM4
rpc_init_shared_memory(&dev->shared_mem, dev->m0_rpc_phy, dev->m0_rpc_virt, SHARED_SIZE);
#else
rpc_init_shared_memory(&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(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;
dev->m0_rpc_virt = NULL;
dev->m0_rpc_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_decoder_dev))
video_unregister_device(dev->pvpu_decoder_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-decoder", },
{ .compatible = "nxp,imx8qxp-vpu-decoder", },
{}
}
MODULE_DEVICE_TABLE(of, vpu_of_match);
static struct platform_driver vpu_driver = {
.probe = vpu_probe,
.remove = vpu_remove,
.driver = {
.name = "vpu-b0",
.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_decoder, int, 0644);
MODULE_PARM_DESC(vpu_dbg_level_decoder, "Debug level (0-2)");