blob: 6f5a0a672ad87c8079d246fba95c6f3eb3207b47 [file] [log] [blame]
/*
* linux/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
*
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
* Kamil Debski, <k.debski@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/videodev2.h>
#include <linux/workqueue.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
#include <media/videobuf2-core.h>
#include "s5p_mfc_common.h"
#include "s5p_mfc_ctrl.h"
#include "s5p_mfc_debug.h"
#include "s5p_mfc_dec.h"
#include "s5p_mfc_intr.h"
#include "s5p_mfc_opr.h"
#include "s5p_mfc_pm.h"
static struct s5p_mfc_fmt formats[] = {
{
.name = "4:2:0 2 Planes 16x16 Tiles",
.fourcc = V4L2_PIX_FMT_NV12MT_16X16,
.codec_mode = S5P_MFC_CODEC_NONE,
.type = MFC_FMT_RAW,
.num_planes = 2,
.versions = MFC_V6_BIT | MFC_V7_BIT,
},
{
.name = "4:2:0 2 Planes 64x32 Tiles",
.fourcc = V4L2_PIX_FMT_NV12MT,
.codec_mode = S5P_MFC_CODEC_NONE,
.type = MFC_FMT_RAW,
.num_planes = 2,
.versions = MFC_V5_BIT,
},
{
.name = "4:2:0 2 Planes Y/CbCr",
.fourcc = V4L2_PIX_FMT_NV12M,
.codec_mode = S5P_MFC_CODEC_NONE,
.type = MFC_FMT_RAW,
.num_planes = 2,
.versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,
},
{
.name = "4:2:0 2 Planes Y/CrCb",
.fourcc = V4L2_PIX_FMT_NV21M,
.codec_mode = S5P_MFC_CODEC_NONE,
.type = MFC_FMT_RAW,
.num_planes = 2,
.versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,
},
{
.name = "H264 Encoded Stream",
.fourcc = V4L2_PIX_FMT_H264,
.codec_mode = S5P_MFC_CODEC_H264_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
MFC_V8_BIT,
.resolution = {
{
.stepwise.max_width = 1920,
.stepwise.max_height = 1088,
},
{
.stepwise.max_width = 8192,
.stepwise.max_height = 8192,
},
{
.stepwise.max_width = 8192,
.stepwise.max_height = 8192,
},
{
.stepwise.max_width = 8192,
.stepwise.max_height = 8192,
},
},
},
{
.name = "H264/MVC Encoded Stream",
.fourcc = V4L2_PIX_FMT_H264_MVC,
.codec_mode = S5P_MFC_CODEC_H264_MVC_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,
.resolution = {
{},
{
.stepwise.max_width = 1920,
.stepwise.max_height = 1088,
},
{
.stepwise.max_width = 1920,
.stepwise.max_height = 1088,
},
{
.stepwise.max_width = 1920,
.stepwise.max_height = 1088,
},
},
},
{
.name = "H263 Encoded Stream",
.fourcc = V4L2_PIX_FMT_H263,
.codec_mode = S5P_MFC_CODEC_H263_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
MFC_V8_BIT,
.resolution = {
{
.stepwise.max_width = 1920,
.stepwise.max_height = 1088,
},
{
.stepwise.max_width = 1152,
.stepwise.max_height = 1088,
},
{
.stepwise.max_width = 2048,
.stepwise.max_height = 1152,
},
{
.stepwise.max_width = 2048,
.stepwise.max_height = 1152,
},
},
},
{
.name = "MPEG1 Encoded Stream",
.fourcc = V4L2_PIX_FMT_MPEG1,
.codec_mode = S5P_MFC_CODEC_MPEG2_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
MFC_V8_BIT,
.resolution = {
{
.stepwise.max_width = 1920,
.stepwise.max_height = 1088,
},
{
.stepwise.max_width = 1920,
.stepwise.max_height = 1088,
},
{
.stepwise.max_width = 2048,
.stepwise.max_height = 2048,
},
{
.stepwise.max_width = 2048,
.stepwise.max_height = 2048,
},
},
},
{
.name = "MPEG2 Encoded Stream",
.fourcc = V4L2_PIX_FMT_MPEG2,
.codec_mode = S5P_MFC_CODEC_MPEG2_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
MFC_V8_BIT,
.resolution = {
{
.stepwise.max_width = 1920,
.stepwise.max_height = 1088,
},
{
.stepwise.max_width = 1920,
.stepwise.max_height = 1088,
},
{
.stepwise.max_width = 2048,
.stepwise.max_height = 2048,
},
{
.stepwise.max_width = 2048,
.stepwise.max_height = 2048,
},
},
},
{
.name = "MPEG4 Encoded Stream",
.fourcc = V4L2_PIX_FMT_MPEG4,
.codec_mode = S5P_MFC_CODEC_MPEG4_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
MFC_V8_BIT,
.resolution = {
{
.stepwise.max_width = 1920,
.stepwise.max_height = 1088,
},
{
.stepwise.max_width = 1920,
.stepwise.max_height = 1088,
},
{
.stepwise.max_width = 2048,
.stepwise.max_height = 2048,
},
{
.stepwise.max_width = 2048,
.stepwise.max_height = 2048,
},
},
},
{
.name = "XviD Encoded Stream",
.fourcc = V4L2_PIX_FMT_XVID,
.codec_mode = S5P_MFC_CODEC_MPEG4_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
MFC_V8_BIT,
.resolution = {
{
.stepwise.max_width = 1920,
.stepwise.max_height = 1088,
},
{
.stepwise.max_width = 1920,
.stepwise.max_height = 1088,
},
{
.stepwise.max_width = 2048,
.stepwise.max_height = 2048,
},
{
.stepwise.max_width = 2048,
.stepwise.max_height = 2048,
},
},
},
{
.name = "VC1 Encoded Stream",
.fourcc = V4L2_PIX_FMT_VC1_ANNEX_G,
.codec_mode = S5P_MFC_CODEC_VC1_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
MFC_V8_BIT,
.resolution = {
{
.stepwise.max_width = 1920,
.stepwise.max_height = 1088,
},
{
.stepwise.max_width = 1920,
.stepwise.max_height = 1088,
},
{
.stepwise.max_width = 2048,
.stepwise.max_height = 2032,
},
{
.stepwise.max_width = 2048,
.stepwise.max_height = 2032,
},
},
},
{
.name = "VC1 RCV Encoded Stream",
.fourcc = V4L2_PIX_FMT_VC1_ANNEX_L,
.codec_mode = S5P_MFC_CODEC_VC1RCV_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
MFC_V8_BIT,
.resolution = {
{
.stepwise.max_width = 1920,
.stepwise.max_height = 1088,
},
{
.stepwise.max_width = 1920,
.stepwise.max_height = 1088,
},
{
.stepwise.max_width = 2048,
.stepwise.max_height = 2032,
},
{
.stepwise.max_width = 2048,
.stepwise.max_height = 2032,
},
},
},
{
.name = "VP8 Encoded Stream",
.fourcc = V4L2_PIX_FMT_VP8,
.codec_mode = S5P_MFC_CODEC_VP8_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,
.resolution = {
{},
{
.stepwise.max_width = 8192,
.stepwise.max_height = 8192,
},
{
.stepwise.max_width = 8192,
.stepwise.max_height = 8192,
},
{
.stepwise.max_width = 8192,
.stepwise.max_height = 8192,
},
},
},
};
#define NUM_FORMATS ARRAY_SIZE(formats)
/* Find selected format description */
static struct s5p_mfc_fmt *find_format(unsigned int fourcc, unsigned int t)
{
unsigned int i;
for (i = 0; i < NUM_FORMATS; i++) {
if (formats[i].fourcc == fourcc && formats[i].type == t)
return &formats[i];
}
return NULL;
}
static struct mfc_control controls[] = {
{
.id = V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "H264 Display Delay",
.minimum = 0,
.maximum = 16383,
.step = 1,
.default_value = 0,
},
{
.id = V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "H264 Display Delay Enable",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Mpeg4 Loop Filter Enable",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Slice Interface Enable",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0,
},
{
.id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Minimum number of cap bufs",
.minimum = 1,
.maximum = 32,
.step = 1,
.default_value = 1,
.is_volatile = 1,
},
};
#define NUM_CTRLS ARRAY_SIZE(controls)
/* Check whether a context should be run on hardware */
static bool s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
{
assert_spin_locked(&ctx->dev->irqlock);
switch (ctx->state) {
/* Context is to acquire instance ID */
case MFCINST_INIT:
return true;
/* Context is to parse header */
case MFCINST_GOT_INST:
if (ctx->src_queue_cnt >= 1)
return true;
break;
/* Context is to set buffers */
case MFCINST_HEAD_PARSED:
if (ctx->src_queue_cnt >= 1 &&
ctx->capture_state == QUEUE_BUFS_MMAPED)
return true;
break;
/* Context is to decode a frame */
case MFCINST_RUNNING:
if (ctx->src_queue_cnt >= 1 &&
ctx->dst_queue_cnt >= ctx->dpb_count &&
!ctx->stopping)
return true;
break;
/* Context is to return last frame */
case MFCINST_FINISHING:
return !ctx->stopping;
/* Context is to free instance ID */
case MFCINST_RETURN_INST:
return true;
/* DPB flush in progress */
case MFCINST_FLUSH:
return true;
/* Resolution change flush in progress */
case MFCINST_RES_CHANGE_INIT:
case MFCINST_RES_CHANGE_FLUSH:
return true;
/* Resolution change flush finished */
case MFCINST_RES_CHANGE_END:
if (ctx->src_queue_cnt >= 1)
return true;
break;
default:
break;
}
mfc_debug(2, "ctx is not ready\n");
return false;
}
static int s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
assert_spin_locked(&dev->irqlock);
ctx->img_width = s5p_mfc_hw_call(dev->mfc_ops, get_img_width, dev);
ctx->img_height = s5p_mfc_hw_call(dev->mfc_ops, get_img_height, dev);
s5p_mfc_hw_call(dev->mfc_ops, dec_calc_dpb_size, ctx);
if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_H264) {
u32 crop_h, crop_v;
u32 left, right, top, bottom;
crop_h = s5p_mfc_hw_call(dev->mfc_ops, get_crop_info_h, ctx);
crop_v = s5p_mfc_hw_call(dev->mfc_ops, get_crop_info_v, ctx);
right = crop_h >> S5P_FIMV_SHARED_CROP_RIGHT_SHIFT;
left = crop_h & S5P_FIMV_SHARED_CROP_LEFT_MASK;
bottom = crop_v >> S5P_FIMV_SHARED_CROP_BOTTOM_SHIFT;
top = crop_v & S5P_FIMV_SHARED_CROP_TOP_MASK;
ctx->crop_left = left;
ctx->crop_top = top;
ctx->crop_width = ctx->img_width - left - right;
ctx->crop_height = ctx->img_height - top - bottom;
mfc_debug(2, "Cropping info [h264]: l=%d t=%d w=%d h=%d (r=%d b=%d fw=%d fh=%d\n",
left, top, ctx->crop_width, ctx->crop_height,
right, bottom, ctx->buf_width, ctx->buf_height);
} else {
ctx->crop_left = 0;
ctx->crop_top = 0;
ctx->crop_width = ctx->img_width;
ctx->crop_height = ctx->img_height;
mfc_debug(2, "Cropping info: w=%d h=%d fw=%d fh=%d\n",
ctx->crop_width, ctx->crop_height,
ctx->buf_width, ctx->buf_height);
}
ctx->dpb_count = s5p_mfc_hw_call(dev->mfc_ops, get_dpb_count, dev);
ctx->mv_count = s5p_mfc_hw_call(dev->mfc_ops, get_mv_count, dev);
if (ctx->img_width == 0 || ctx->img_height == 0) {
ctx->state = MFCINST_ERROR;
} else {
if (ctx->state == MFCINST_RES_CHANGE_END) {
struct v4l2_event ev;
ctx->capture_state = QUEUE_FREE;
memset(&ev, 0, sizeof(struct v4l2_event));
ev.type = V4L2_EVENT_RESOLUTION_CHANGE;
v4l2_event_queue_fh(&ctx->fh, &ev);
}
ctx->state = MFCINST_HEAD_PARSED;
}
if ((ctx->codec_mode == S5P_MFC_CODEC_H264_DEC ||
ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC ||
ctx->codec_mode == S5P_MFC_CODEC_VP8_DEC) &&
!list_empty(&ctx->src_queue)) {
struct s5p_mfc_buf *src_buf;
src_buf = list_entry(ctx->src_queue.next,
struct s5p_mfc_buf, list);
if (s5p_mfc_hw_call(dev->mfc_ops, get_consumed_stream,
dev) <
src_buf->b->v4l2_planes[0].bytesused)
ctx->head_processed = 0;
else
ctx->head_processed = 1;
} else {
ctx->head_processed = 1;
}
return 0;
}
static void s5p_mfc_handle_frame_all_extracted(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_buf *dst_buf;
struct s5p_mfc_dev *dev = ctx->dev;
assert_spin_locked(&dev->irqlock);
ctx->state = MFCINST_FINISHED;
ctx->sequence++;
while (!list_empty(&ctx->dst_queue)) {
dst_buf = list_entry(ctx->dst_queue.next,
struct s5p_mfc_buf, list);
mfc_debug(2, "Cleaning up buffer: %d\n",
dst_buf->b->v4l2_buf.index);
vb2_set_plane_payload(dst_buf->b, 0, 0);
vb2_set_plane_payload(dst_buf->b, 1, 0);
list_del(&dst_buf->list);
ctx->dst_queue_cnt--;
dst_buf->b->v4l2_buf.sequence = (ctx->sequence++);
if (s5p_mfc_hw_call(dev->mfc_ops, get_pic_type_top, ctx) ==
s5p_mfc_hw_call(dev->mfc_ops, get_pic_type_bot, ctx))
dst_buf->b->v4l2_buf.field = V4L2_FIELD_NONE;
else
dst_buf->b->v4l2_buf.field = V4L2_FIELD_INTERLACED;
ctx->dec_dst_flag &= ~(1 << dst_buf->b->v4l2_buf.index);
vb2_buffer_done(dst_buf->b, VB2_BUF_STATE_DONE);
}
}
static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_buf *dst_buf, *src_buf;
size_t dec_y_addr;
unsigned int frame_type;
assert_spin_locked(&dev->irqlock);
/* Make sure we actually have a new frame before continuing. */
frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_dec_frame_type, dev);
dec_y_addr = s5p_mfc_hw_call(dev->mfc_ops, get_dec_y_adr, dev);
/* Copy timestamp / timecode from decoded src to dst and set
appropriate flags. */
src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0)
== dec_y_addr) {
dst_buf->b->v4l2_buf.timecode =
src_buf->b->v4l2_buf.timecode;
dst_buf->b->v4l2_buf.timestamp =
src_buf->b->v4l2_buf.timestamp;
switch (frame_type) {
case S5P_FIMV_DECODE_FRAME_I_FRAME:
dst_buf->b->v4l2_buf.flags |=
V4L2_BUF_FLAG_KEYFRAME;
break;
case S5P_FIMV_DECODE_FRAME_P_FRAME:
dst_buf->b->v4l2_buf.flags |=
V4L2_BUF_FLAG_PFRAME;
break;
case S5P_FIMV_DECODE_FRAME_B_FRAME:
dst_buf->b->v4l2_buf.flags |=
V4L2_BUF_FLAG_BFRAME;
break;
default:
/* Don't know how to handle
S5P_FIMV_DECODE_FRAME_OTHER_FRAME. */
mfc_debug(2, "Unexpected frame type: %d\n",
frame_type);
}
break;
}
}
}
static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err)
{
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_buf *dst_buf;
size_t dspl_y_addr;
unsigned int frame_type;
unsigned int index;
assert_spin_locked(&dev->irqlock);
dspl_y_addr = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_y_adr, dev);
frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_disp_frame_type, ctx);
/* If frame is same as previous then skip and do not dequeue */
if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED) {
if (!ctx->after_packed_pb)
ctx->sequence++;
ctx->after_packed_pb = 0;
return;
}
ctx->sequence++;
/* The MFC returns address of the buffer, now we have to
* check which videobuf does it correspond to */
list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
/* Check if this is the buffer we're looking for */
if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0)
== dspl_y_addr) {
list_del(&dst_buf->list);
ctx->dst_queue_cnt--;
dst_buf->b->v4l2_buf.sequence = ctx->sequence;
if (s5p_mfc_hw_call(dev->mfc_ops,
get_pic_type_top, ctx) ==
s5p_mfc_hw_call(dev->mfc_ops,
get_pic_type_bot, ctx))
dst_buf->b->v4l2_buf.field = V4L2_FIELD_NONE;
else
dst_buf->b->v4l2_buf.field =
V4L2_FIELD_INTERLACED;
vb2_set_plane_payload(dst_buf->b, 0, ctx->luma_size);
vb2_set_plane_payload(dst_buf->b, 1, ctx->chroma_size);
clear_bit(dst_buf->b->v4l2_buf.index,
&ctx->dec_dst_flag);
vb2_buffer_done(dst_buf->b,
err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
index = dst_buf->b->v4l2_buf.index;
break;
}
}
}
static int s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx, unsigned int reason,
unsigned int err)
{
struct s5p_mfc_dev *dev = ctx->dev;
unsigned int dst_frame_status;
unsigned int dec_frame_status;
struct s5p_mfc_buf *src_buf;
unsigned int res_change;
unsigned int index;
assert_spin_locked(&dev->irqlock);
dst_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev)
& S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
dec_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dec_status, dev)
& S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
res_change = (s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev)
& S5P_FIMV_DEC_STATUS_RESOLUTION_MASK)
>> S5P_FIMV_DEC_STATUS_RESOLUTION_SHIFT;
mfc_debug(2, "Frame Status: %x\n", dst_frame_status);
if (ctx->state == MFCINST_RES_CHANGE_INIT)
ctx->state = MFCINST_RES_CHANGE_FLUSH;
if (res_change == S5P_FIMV_RES_INCREASE ||
res_change == S5P_FIMV_RES_DECREASE) {
mfc_debug(2, "Resolution change detected: %x\n", res_change);
ctx->state = MFCINST_RES_CHANGE_INIT;
return 0;
}
if (ctx->dpb_flush_flag)
ctx->dpb_flush_flag = 0;
/* All frames remaining in the buffer have been extracted */
if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) {
if (ctx->state == MFCINST_RES_CHANGE_FLUSH) {
s5p_mfc_handle_frame_all_extracted(ctx);
ctx->state = MFCINST_RES_CHANGE_END;
return 0;
} else {
s5p_mfc_handle_frame_all_extracted(ctx);
}
}
if (dec_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY)
s5p_mfc_handle_frame_copy_time(ctx);
/* A frame has been decoded and is in the buffer */
if (dst_frame_status == S5P_FIMV_DEC_STATUS_DISPLAY_ONLY ||
dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY) {
s5p_mfc_handle_frame_new(ctx, err);
} else {
mfc_debug(2, "No frame decode\n");
}
/* Mark source buffer as complete */
if (dst_frame_status != S5P_FIMV_DEC_STATUS_DISPLAY_ONLY
&& !list_empty(&ctx->src_queue)) {
src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf,
list);
ctx->consumed_stream += s5p_mfc_hw_call(dev->mfc_ops,
get_consumed_stream, dev);
if (ctx->codec_mode == S5P_MFC_CODEC_MPEG4_DEC &&
ctx->consumed_stream + STUFF_BYTE <
src_buf->b->v4l2_planes[0].bytesused) {
/* Run MFC again on the same buffer */
mfc_debug(2, "Running again the same buffer\n");
ctx->after_packed_pb = 1;
} else {
index = src_buf->b->v4l2_buf.index;
mfc_debug(2, "MFC needs next buffer\n");
ctx->consumed_stream = 0;
if (src_buf->flags & MFC_BUF_FLAG_EOS)
ctx->state = MFCINST_FINISHING;
list_del(&src_buf->list);
ctx->src_queue_cnt--;
if (s5p_mfc_hw_call(dev->mfc_ops, err_dec, err) > 0)
vb2_buffer_done(src_buf->b,
VB2_BUF_STATE_ERROR);
else
vb2_buffer_done(src_buf->b, VB2_BUF_STATE_DONE);
}
}
return 0;
}
static struct s5p_mfc_codec_ops decoder_codec_ops = {
.post_seq_start = s5p_mfc_handle_seq_done,
.post_frame_start = s5p_mfc_handle_frame,
.ctx_ready = s5p_mfc_ctx_ready,
};
/* Query capabilities of the device */
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
struct s5p_mfc_dev *dev = video_drvdata(file);
strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1);
strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
cap->bus_info[0] = 0;
cap->version = KERNEL_VERSION(1, 0, 0);
/*
* This is only a mem-to-mem video device. The capture and output
* device capability flags are left only for backward compatibility
* and are scheduled for removal.
*/
cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING |
V4L2_CAP_VIDEO_CAPTURE_MPLANE |
V4L2_CAP_VIDEO_OUTPUT_MPLANE;
return 0;
}
/* Enumerate format */
static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f,
bool out)
{
struct s5p_mfc_dev *dev = video_drvdata(file);
struct s5p_mfc_fmt *fmt;
int i, j = 0;
for (i = 0; i < ARRAY_SIZE(formats); ++i) {
if (out && formats[i].type != MFC_FMT_DEC)
continue;
else if (!out && formats[i].type != MFC_FMT_RAW)
continue;
else if ((dev->variant->version_bit & formats[i].versions) == 0)
continue;
if (j == f->index)
break;
++j;
}
if (i == ARRAY_SIZE(formats))
return -EINVAL;
fmt = &formats[i];
strlcpy(f->description, fmt->name, sizeof(f->description));
f->pixelformat = fmt->fourcc;
return 0;
}
static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
struct v4l2_fmtdesc *f)
{
return vidioc_enum_fmt(file, f, false);
}
static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
return vidioc_enum_fmt(file, f, true);
}
/* Get format */
static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
struct v4l2_pix_format_mplane *pix_mp;
mfc_debug_enter();
pix_mp = &f->fmt.pix_mp;
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
(ctx->state < MFCINST_HEAD_PARSED || ctx->state >=
MFCINST_RES_CHANGE_INIT)) {
/* If the MFC is parsing the header,
* so wait until it is finished */
s5p_mfc_wait_for_done_ctx(ctx);
}
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
ctx->state >= MFCINST_HEAD_PARSED &&
ctx->state < MFCINST_ERROR) {
/* This is run on CAPTURE (decode output) */
/* Width and height are set to the dimensions of the buffer,
The movie's dimensions may be smaller; the cropping rectangle
required should be queried with VIDIOC_G_CROP. */
pix_mp->width = ctx->buf_width;
pix_mp->height = ctx->buf_height;
pix_mp->field = V4L2_FIELD_NONE;
pix_mp->num_planes = 2;
/* Set pixelformat to the format in which MFC
outputs the decoded frame */
pix_mp->pixelformat = ctx->dst_fmt->fourcc;
pix_mp->plane_fmt[0].bytesperline = ctx->buf_width;
pix_mp->plane_fmt[0].sizeimage = ctx->luma_size;
pix_mp->plane_fmt[1].bytesperline = ctx->buf_width;
pix_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
/* This is run on OUTPUT
The buffer contains compressed image
so width and height have no meaning */
pix_mp->width = 0;
pix_mp->height = 0;
pix_mp->field = V4L2_FIELD_NONE;
pix_mp->plane_fmt[0].bytesperline = ctx->dec_src_buf_size;
pix_mp->plane_fmt[0].sizeimage = ctx->dec_src_buf_size;
pix_mp->pixelformat = ctx->src_fmt->fourcc;
pix_mp->num_planes = ctx->src_fmt->num_planes;
} else {
mfc_err("Format could not be read\n");
mfc_debug(2, "%s-- with error\n", __func__);
return -EINVAL;
}
mfc_debug_leave();
return 0;
}
/* Try format */
static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
struct s5p_mfc_dev *dev = video_drvdata(file);
struct s5p_mfc_fmt *fmt;
mfc_debug(2, "Type is %d\n", f->type);
if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
fmt = find_format(f->fmt.pix_mp.pixelformat, MFC_FMT_DEC);
if (!fmt) {
mfc_err("Unsupported format for source.\n");
return -EINVAL;
}
if (fmt->codec_mode == S5P_FIMV_CODEC_NONE) {
mfc_err("Unknown codec\n");
return -EINVAL;
}
if ((dev->variant->version_bit & fmt->versions) == 0) {
mfc_err("Unsupported format by this MFC version.\n");
return -EINVAL;
}
} else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
fmt = find_format(f->fmt.pix_mp.pixelformat, MFC_FMT_RAW);
if (!fmt) {
mfc_err("Unsupported format for destination.\n");
return -EINVAL;
}
if ((dev->variant->version_bit & fmt->versions) == 0) {
mfc_err("Unsupported format by this MFC version.\n");
return -EINVAL;
}
}
return 0;
}
/* Set format */
static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
struct s5p_mfc_dev *dev = video_drvdata(file);
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
int ret = 0;
struct v4l2_pix_format_mplane *pix_mp;
mfc_debug_enter();
ret = vidioc_try_fmt(file, priv, f);
pix_mp = &f->fmt.pix_mp;
if (ret)
return ret;
if (vb2_is_streaming(&ctx->vq_src) || vb2_is_streaming(&ctx->vq_dst)) {
v4l2_err(&dev->v4l2_dev, "%s queue already streaming\n",
__func__);
ret = -EBUSY;
goto out;
}
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
if (vb2_is_busy(&ctx->vq_dst)) {
v4l2_err(&dev->v4l2_dev, "%s queue buffers already requested\n",
__func__);
ret = -EBUSY;
goto out;
}
/* dst_fmt is validated by call to vidioc_try_fmt */
ctx->dst_fmt = find_format(f->fmt.pix_mp.pixelformat,
MFC_FMT_RAW);
ret = 0;
goto out;
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
if (vb2_is_busy(&ctx->vq_src)) {
v4l2_err(&dev->v4l2_dev, "%s queue buffers already requested\n",
__func__);
ret = -EBUSY;
goto out;
}
/* src_fmt is validated by call to vidioc_try_fmt */
ctx->src_fmt = find_format(f->fmt.pix_mp.pixelformat,
MFC_FMT_DEC);
ctx->codec_mode = ctx->src_fmt->codec_mode;
mfc_debug(2, "The codec number is: %d\n", ctx->codec_mode);
pix_mp->height = 0;
pix_mp->width = 0;
if (pix_mp->plane_fmt[0].sizeimage)
ctx->dec_src_buf_size = pix_mp->plane_fmt[0].sizeimage;
else
pix_mp->plane_fmt[0].sizeimage = ctx->dec_src_buf_size =
DEF_CPB_SIZE;
pix_mp->plane_fmt[0].bytesperline = 0;
ctx->state = MFCINST_INIT;
ret = 0;
goto out;
} else {
mfc_err("Wrong type error for S_FMT : %d", f->type);
ret = -EINVAL;
goto out;
}
out:
mfc_debug_leave();
return ret;
}
static int reqbufs_output(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx,
struct v4l2_requestbuffers *reqbufs)
{
int ret = 0;
if (reqbufs->count == 0) {
mfc_debug(2, "Freeing buffers\n");
ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
if (ret)
goto out;
s5p_mfc_close_mfc_inst(dev, ctx);
ctx->src_bufs_cnt = 0;
ctx->output_state = QUEUE_FREE;
} else if (ctx->output_state == QUEUE_FREE) {
/* Can only request buffers when we have a valid format set. */
WARN_ON(ctx->src_bufs_cnt != 0);
if (ctx->state != MFCINST_INIT) {
mfc_err("Reqbufs called in an invalid state\n");
ret = -EINVAL;
goto out;
}
mfc_debug(2, "Allocating %d buffers for OUTPUT queue\n",
reqbufs->count);
ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
if (ret)
goto out;
ret = s5p_mfc_open_mfc_inst(dev, ctx);
if (ret) {
reqbufs->count = 0;
vb2_reqbufs(&ctx->vq_src, reqbufs);
goto out;
}
ctx->output_state = QUEUE_BUFS_REQUESTED;
} else {
mfc_err("Buffers have already been requested\n");
ret = -EINVAL;
}
out:
if (ret)
mfc_err("Failed allocating buffers for OUTPUT queue\n");
return ret;
}
static int reqbufs_capture(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx,
struct v4l2_requestbuffers *reqbufs)
{
int ret = 0;
if (reqbufs->count == 0) {
mfc_debug(2, "Freeing buffers\n");
ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
if (ret) {
mfc_err("vb2_reqbufs(0) on CAPTURE queue failed\n");
return ret;
}
s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx);
ctx->dst_bufs_cnt = 0;
return 0;
}
if (ctx->capture_state != QUEUE_FREE) {
mfc_err("Buffers have already been requested\n");
return -EINVAL;
}
WARN_ON(ctx->dst_bufs_cnt != 0);
mfc_debug(2, "Allocating %d buffers for CAPTURE queue\n",
reqbufs->count);
ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
if (ret) {
mfc_err("vb2_reqbufs() on CAPTURE queue failed\n");
return ret;
}
ctx->total_dpb_count = reqbufs->count;
ret = s5p_mfc_hw_call(dev->mfc_ops, alloc_codec_buffers, ctx);
if (ret) {
mfc_err("Failed to allocate decoding buffers\n");
ret = -ENOMEM;
goto err_reqbufs;
}
WARN_ON(ctx->dst_bufs_cnt != ctx->total_dpb_count);
ctx->capture_state = QUEUE_BUFS_MMAPED;
s5p_mfc_try_ctx(ctx);
ret = s5p_mfc_wait_for_done_ctx(ctx);
if (ret) {
mfc_err("CMD_INIT_BUFFERS failed\n");
ret = -EIO;
goto err_alloc;
}
return 0;
err_alloc:
ctx->capture_state = QUEUE_FREE;
s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx);
err_reqbufs:
reqbufs->count = 0;
vb2_reqbufs(&ctx->vq_dst, reqbufs);
return ret;
}
/* Reqeust buffers */
static int vidioc_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *reqbufs)
{
struct s5p_mfc_dev *dev = video_drvdata(file);
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
return reqbufs_output(dev, ctx, reqbufs);
} else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
return reqbufs_capture(dev, ctx, reqbufs);
} else {
mfc_err("Invalid type requested\n");
return -EINVAL;
}
}
/* Query buffer */
static int vidioc_querybuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
int ret;
int i;
mfc_debug(2, "State: %d, buf->type: %d\n", ctx->state, buf->type);
if (ctx->state == MFCINST_GOT_INST &&
buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
ret = vb2_querybuf(&ctx->vq_src, buf);
} else if (ctx->state == MFCINST_RUNNING &&
buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
ret = vb2_querybuf(&ctx->vq_dst, buf);
for (i = 0; i < buf->length; i++)
buf->m.planes[i].m.mem_offset += DST_QUEUE_OFF_BASE;
} else {
mfc_err("vidioc_querybuf called in an inappropriate state\n");
ret = -EINVAL;
}
mfc_debug_leave();
return ret;
}
/* Queue a buffer */
static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
if (ctx->state == MFCINST_ERROR) {
mfc_err("Call on QBUF after unrecoverable error\n");
return -EIO;
}
if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
return vb2_qbuf(&ctx->vq_src, buf);
else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return vb2_qbuf(&ctx->vq_dst, buf);
return -EINVAL;
}
/* Dequeue a buffer */
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
{
const struct v4l2_event ev = {
.type = V4L2_EVENT_EOS
};
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
int ret;
if (ctx->state == MFCINST_ERROR) {
mfc_err("Call on DQBUF after unrecoverable error\n");
return -EIO;
}
if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
ret = vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
ret = vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
if (ret == 0 && ctx->state == MFCINST_FINISHED &&
list_empty(&ctx->vq_dst.done_list))
v4l2_event_queue_fh(&ctx->fh, &ev);
} else {
ret = -EINVAL;
}
return ret;
}
/* Export DMA buffer */
static int vidioc_expbuf(struct file *file, void *priv,
struct v4l2_exportbuffer *eb)
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
if (eb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
return vb2_expbuf(&ctx->vq_src, eb);
if (eb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return vb2_expbuf(&ctx->vq_dst, eb);
return -EINVAL;
}
/* Stream on */
static int vidioc_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
int ret = -EINVAL;
mfc_debug_enter();
if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
ret = vb2_streamon(&ctx->vq_src, type);
else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
ret = vb2_streamon(&ctx->vq_dst, type);
mfc_debug_leave();
return ret;
}
/* Stream off, which equals to a pause */
static int vidioc_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
return vb2_streamoff(&ctx->vq_src, type);
else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return vb2_streamoff(&ctx->vq_dst, type);
return -EINVAL;
}
/* Set controls - v4l2 control framework */
static int s5p_mfc_dec_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
switch (ctrl->id) {
case V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY:
ctx->display_delay = ctrl->val;
break;
case V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE:
ctx->display_delay_enable = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
ctx->loop_filter_mpeg4 = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:
ctx->slice_interface = ctrl->val;
break;
default:
mfc_err("Invalid control 0x%08x\n", ctrl->id);
return -EINVAL;
}
return 0;
}
static int s5p_mfc_dec_g_v_ctrl(struct v4l2_ctrl *ctrl)
{
struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
struct s5p_mfc_dev *dev = ctx->dev;
switch (ctrl->id) {
case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
if (ctx->state >= MFCINST_HEAD_PARSED &&
ctx->state < MFCINST_ERROR) {
ctrl->val = ctx->dpb_count;
break;
} else if (ctx->state != MFCINST_INIT &&
ctx->state != MFCINST_RES_CHANGE_END) {
v4l2_err(&dev->v4l2_dev, "Decoding not initialised\n");
return -EINVAL;
}
/* Should wait for the header to be parsed */
s5p_mfc_wait_for_done_ctx(ctx);
if (ctx->state >= MFCINST_HEAD_PARSED &&
ctx->state < MFCINST_ERROR) {
ctrl->val = ctx->dpb_count;
} else {
v4l2_err(&dev->v4l2_dev, "Decoding not initialised\n");
return -EINVAL;
}
break;
}
return 0;
}
static const struct v4l2_ctrl_ops s5p_mfc_dec_ctrl_ops = {
.s_ctrl = s5p_mfc_dec_s_ctrl,
.g_volatile_ctrl = s5p_mfc_dec_g_v_ctrl,
};
/* Get cropping information */
static int vidioc_g_crop(struct file *file, void *priv,
struct v4l2_crop *cr)
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
if (ctx->state >= MFCINST_HEAD_PARSED && ctx->state < MFCINST_ERROR) {
cr->c.left = ctx->crop_left;
cr->c.top = ctx->crop_top;
cr->c.width = ctx->crop_width;
cr->c.height = ctx->crop_height;
return 0;
}
mfc_err("Cannot get crop\n");
return -EINVAL;
}
int vidioc_decoder_cmd(struct file *file, void *priv,
struct v4l2_decoder_cmd *cmd)
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_buf *buf;
unsigned long flags;
switch (cmd->cmd) {
case V4L2_ENC_CMD_STOP:
if (cmd->flags != 0)
return -EINVAL;
if (!vb2_is_streaming(&ctx->vq_src))
return -EINVAL;
spin_lock_irqsave(&dev->irqlock, flags);
if (list_empty(&ctx->src_queue)) {
mfc_err("EOS: empty src queue, entering finishing state");
ctx->state = MFCINST_FINISHING;
spin_unlock_irqrestore(&dev->irqlock, flags);
s5p_mfc_try_ctx(ctx);
} else {
mfc_err("EOS: marking last buffer of stream");
buf = list_entry(ctx->src_queue.prev,
struct s5p_mfc_buf, list);
if (buf->flags & MFC_BUF_FLAG_USED)
ctx->state = MFCINST_FINISHING;
else
buf->flags |= MFC_BUF_FLAG_EOS;
spin_unlock_irqrestore(&dev->irqlock, flags);
}
break;
default:
return -EINVAL;
}
return 0;
}
static int vidioc_subscribe_event(struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub)
{
switch (sub->type) {
case V4L2_EVENT_EOS:
return v4l2_event_subscribe(fh, sub, 2, NULL);
case V4L2_EVENT_RESOLUTION_CHANGE:
return v4l2_event_subscribe(fh, sub, 2, NULL);
default:
return -EINVAL;
}
}
static int vidioc_enum_framesizes(struct file *file, void *priv,
struct v4l2_frmsizeenum *fsize)
{
struct s5p_mfc_dev *dev = video_drvdata(file);
struct s5p_mfc_fmt *mfc_format;
unsigned int version_index = fls(dev->variant->version_bit) - 1;
if (fsize->index != 0)
return -EINVAL;
mfc_format = find_format(fsize->pixel_format, MFC_FMT_DEC);
if (!mfc_format || !(mfc_format->versions & dev->variant->version_bit))
return -EINVAL;
memcpy(fsize, &mfc_format->resolution[version_index],
sizeof(struct v4l2_frmsizeenum));
fsize->pixel_format = mfc_format->fourcc;
fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
fsize->stepwise.min_width = 32;
fsize->stepwise.min_height = 32;
fsize->stepwise.step_width = 16;
fsize->stepwise.step_height = 16;
return 0;
}
/* v4l2_ioctl_ops */
static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
.vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
.vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
.vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
.vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt,
.vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt,
.vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt,
.vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_expbuf = vidioc_expbuf,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_g_crop = vidioc_g_crop,
.vidioc_decoder_cmd = vidioc_decoder_cmd,
.vidioc_subscribe_event = vidioc_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
.vidioc_enum_framesizes = vidioc_enum_framesizes,
};
static int s5p_mfc_queue_setup(struct vb2_queue *vq,
const struct v4l2_format *fmt, unsigned int *buf_count,
unsigned int *plane_count, unsigned int psize[],
void *allocators[])
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
struct s5p_mfc_dev *dev = ctx->dev;
/* Video output for decoding (source)
* this can be set after getting an instance */
if (ctx->state == MFCINST_INIT &&
vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
/* A single plane is required for input */
*plane_count = 1;
if (*buf_count < 1)
*buf_count = 1;
if (*buf_count > MFC_MAX_BUFFERS)
*buf_count = MFC_MAX_BUFFERS;
/* Video capture for decoding (destination)
* this can be set after the header was parsed */
} else if (ctx->state == MFCINST_HEAD_PARSED &&
vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
/* Output plane count is 2 - one for Y and one for CbCr */
*plane_count = 2;
/* Setup buffer count */
if (*buf_count < ctx->dpb_count)
*buf_count = ctx->dpb_count;
if (*buf_count > ctx->dpb_count + MFC_MAX_EXTRA_DPB)
*buf_count = ctx->dpb_count + MFC_MAX_EXTRA_DPB;
if (*buf_count > MFC_MAX_BUFFERS)
*buf_count = MFC_MAX_BUFFERS;
} else {
mfc_err("State seems invalid. State = %d, vq->type = %d\n",
ctx->state, vq->type);
return -EINVAL;
}
mfc_debug(2, "Buffer count=%d, plane count=%d\n",
*buf_count, *plane_count);
if (ctx->state == MFCINST_HEAD_PARSED &&
vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
psize[0] = ctx->luma_size;
psize[1] = ctx->chroma_size;
if (IS_MFCV8(dev)) {
/* MFCv8 needs additional 64 bytes for luma,chroma dpb*/
psize[0] += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8;
psize[1] += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8;
}
if (IS_MFCV6(dev))
allocators[0] =
ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
else
allocators[0] =
ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
allocators[1] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
ctx->state == MFCINST_INIT) {
psize[0] = ctx->dec_src_buf_size;
allocators[0] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
} else {
mfc_err("This video node is dedicated to decoding. Decoding not initalised\n");
return -EINVAL;
}
return 0;
}
static void s5p_mfc_unlock(struct vb2_queue *q)
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
struct s5p_mfc_dev *dev = ctx->dev;
mutex_unlock(&dev->mfc_mutex);
}
static void s5p_mfc_lock(struct vb2_queue *q)
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
struct s5p_mfc_dev *dev = ctx->dev;
mutex_lock(&dev->mfc_mutex);
}
static int s5p_mfc_buf_init(struct vb2_buffer *vb)
{
struct vb2_queue *vq = vb->vb2_queue;
struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
unsigned int i;
if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
if (ctx->capture_state == QUEUE_BUFS_MMAPED)
return 0;
for (i = 0; i <= ctx->src_fmt->num_planes ; i++) {
if (IS_ERR_OR_NULL(ERR_PTR(
vb2_dma_contig_plane_dma_addr(vb, i)))) {
mfc_err("Plane mem not allocated\n");
return -EINVAL;
}
}
if (vb2_plane_size(vb, 0) < ctx->luma_size ||
vb2_plane_size(vb, 1) < ctx->chroma_size) {
mfc_err("Plane buffer (CAPTURE) is too small\n");
return -EINVAL;
}
i = vb->v4l2_buf.index;
ctx->dst_bufs[i].b = vb;
ctx->dst_bufs[i].cookie.raw.luma =
vb2_dma_contig_plane_dma_addr(vb, 0);
ctx->dst_bufs[i].cookie.raw.chroma =
vb2_dma_contig_plane_dma_addr(vb, 1);
ctx->dst_bufs_cnt++;
} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
if (IS_ERR_OR_NULL(ERR_PTR(
vb2_dma_contig_plane_dma_addr(vb, 0)))) {
mfc_err("Plane memory not allocated\n");
return -EINVAL;
}
if (vb2_plane_size(vb, 0) < ctx->dec_src_buf_size) {
mfc_err("Plane buffer (OUTPUT) is too small\n");
return -EINVAL;
}
i = vb->v4l2_buf.index;
ctx->src_bufs[i].b = vb;
ctx->src_bufs[i].cookie.stream =
vb2_dma_contig_plane_dma_addr(vb, 0);
ctx->src_bufs_cnt++;
} else {
mfc_err("s5p_mfc_buf_init: unknown queue type\n");
return -EINVAL;
}
return 0;
}
static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
if (ctx->state == MFCINST_FINISHING ||
ctx->state == MFCINST_FINISHED)
ctx->state = MFCINST_RUNNING;
/* If context is ready then dev = work->data;schedule it to run */
s5p_mfc_try_ctx(ctx);
return 0;
}
static int s5p_mfc_stop_streaming(struct vb2_queue *q)
{
unsigned long flags;
struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
struct s5p_mfc_dev *dev = ctx->dev;
ctx->stopping = true;
s5p_mfc_wait_for_done_ctx(ctx);
if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
enum s5p_mfc_inst_state state;
bool flush = false;
spin_lock_irqsave(&dev->irqlock, flags);
s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->dst_queue,
&ctx->vq_dst);
INIT_LIST_HEAD(&ctx->dst_queue);
ctx->dst_queue_cnt = 0;
ctx->dpb_flush_flag = 1;
ctx->dec_dst_flag = 0;
state = ctx->state;
if (state == MFCINST_RUNNING || state == MFCINST_FINISHING) {
ctx->state = MFCINST_FLUSH;
flush = true;
}
spin_unlock_irqrestore(&dev->irqlock, flags);
if (flush) {
s5p_mfc_try_ctx(ctx);
if (s5p_mfc_wait_for_done_ctx(ctx))
mfc_err("Err flushing buffers\n");
}
}
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
spin_lock_irqsave(&dev->irqlock, flags);
s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->src_queue,
&ctx->vq_src);
INIT_LIST_HEAD(&ctx->src_queue);
ctx->src_queue_cnt = 0;
spin_unlock_irqrestore(&dev->irqlock, flags);
}
ctx->stopping = false;
return 0;
}
static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
{
struct vb2_queue *vq = vb->vb2_queue;
struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
struct s5p_mfc_dev *dev = ctx->dev;
unsigned long flags;
struct s5p_mfc_buf *mfc_buf;
if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
mfc_buf = &ctx->src_bufs[vb->v4l2_buf.index];
mfc_buf->flags &= ~MFC_BUF_FLAG_USED;
spin_lock_irqsave(&dev->irqlock, flags);
list_add_tail(&mfc_buf->list, &ctx->src_queue);
ctx->src_queue_cnt++;
spin_unlock_irqrestore(&dev->irqlock, flags);
} else if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
mfc_buf = &ctx->dst_bufs[vb->v4l2_buf.index];
mfc_buf->flags &= ~MFC_BUF_FLAG_USED;
/* Mark destination as available for use by MFC */
spin_lock_irqsave(&dev->irqlock, flags);
set_bit(vb->v4l2_buf.index, &ctx->dec_dst_flag);
list_add_tail(&mfc_buf->list, &ctx->dst_queue);
ctx->dst_queue_cnt++;
spin_unlock_irqrestore(&dev->irqlock, flags);
} else {
mfc_err("Unsupported buffer type (%d)\n", vq->type);
}
s5p_mfc_try_ctx(ctx);
}
static struct vb2_ops s5p_mfc_dec_qops = {
.queue_setup = s5p_mfc_queue_setup,
.wait_prepare = s5p_mfc_unlock,
.wait_finish = s5p_mfc_lock,
.buf_init = s5p_mfc_buf_init,
.start_streaming = s5p_mfc_start_streaming,
.stop_streaming = s5p_mfc_stop_streaming,
.buf_queue = s5p_mfc_buf_queue,
};
struct s5p_mfc_codec_ops *get_dec_codec_ops(void)
{
return &decoder_codec_ops;
}
struct vb2_ops *get_dec_queue_ops(void)
{
return &s5p_mfc_dec_qops;
}
const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void)
{
return &s5p_mfc_dec_ioctl_ops;
}
#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2CLASS(x) == V4L2_CTRL_CLASS_MPEG) \
&& V4L2_CTRL_DRIVER_PRIV(x))
int s5p_mfc_dec_ctrls_setup(struct s5p_mfc_ctx *ctx)
{
struct v4l2_ctrl_config cfg;
int i;
v4l2_ctrl_handler_init(&ctx->ctrl_handler, NUM_CTRLS);
if (ctx->ctrl_handler.error) {
mfc_err("v4l2_ctrl_handler_init failed\n");
return ctx->ctrl_handler.error;
}
for (i = 0; i < NUM_CTRLS; i++) {
if (IS_MFC51_PRIV(controls[i].id)) {
memset(&cfg, 0, sizeof(struct v4l2_ctrl_config));
cfg.ops = &s5p_mfc_dec_ctrl_ops;
cfg.id = controls[i].id;
cfg.min = controls[i].minimum;
cfg.max = controls[i].maximum;
cfg.def = controls[i].default_value;
cfg.name = controls[i].name;
cfg.type = controls[i].type;
cfg.step = controls[i].step;
cfg.menu_skip_mask = 0;
ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
&cfg, NULL);
} else {
ctx->ctrls[i] = v4l2_ctrl_new_std(&ctx->ctrl_handler,
&s5p_mfc_dec_ctrl_ops,
controls[i].id, controls[i].minimum,
controls[i].maximum, controls[i].step,
controls[i].default_value);
}
if (ctx->ctrl_handler.error) {
mfc_err("Adding control (%d) failed\n", i);
return ctx->ctrl_handler.error;
}
if (controls[i].is_volatile && ctx->ctrls[i])
ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE;
}
return 0;
}
void s5p_mfc_dec_ctrls_delete(struct s5p_mfc_ctx *ctx)
{
int i;
v4l2_ctrl_handler_free(&ctx->ctrl_handler);
for (i = 0; i < NUM_CTRLS; i++)
ctx->ctrls[i] = NULL;
}
int s5p_mfc_dec_init(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
ctx->src_fmt = find_format(GET_MFC_DEF_FMT(dev, SRC_FMT_DEC),
MFC_FMT_DEC);
if (!ctx->src_fmt) {
mfc_err("Source format unsupported.\n");
return -EINVAL;
}
ctx->dst_fmt = find_format(GET_MFC_DEF_FMT(dev, DST_FMT_DEC),
MFC_FMT_RAW);
if (!ctx->dst_fmt) {
mfc_err("Destination format unsupported.\n");
return -EINVAL;
}
mfc_debug(2, "Default src_fmt is %x, dest_fmt is %x\n",
(unsigned int)ctx->src_fmt, (unsigned int)ctx->dst_fmt);
return 0;
}