My review also imply Kamil's original patch.
Jeongtae Park wrote:
> Multi Format Codec v5.1 is a module available on S5PC110 and S5PC210
> Samsung SoCs. Hardware is capable of handling a range of video codecs
> and this driver provides V4L2 interface for video decoding & encoding.
>
> Reviewed-by: Peter Oh <[email protected]>
> Signed-off-by: Jeongtae Park <[email protected]>
> ---
<snip>
> +
> +/* Display status */
> +#define S5P_FIMV_DEC_STATUS_DECODING_ONLY 0
> +#define S5P_FIMV_DEC_STATUS_DECODING_DISPLAY 1
> +#define S5P_FIMV_DEC_STATUS_DISPLAY_ONLY 2
> +#define S5P_FIMV_DEC_STATUS_DECODING_EMPTY 3
> +#define S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK 7
> +#define S5P_FIMV_DEC_STATUS_PROGRESSIVE (0<<3)
> +#define S5P_FIMV_DEC_STATUS_INTERLACE (1<<3)
> +#define S5P_FIMV_DEC_STATUS_INTERLACE_MASK (1<<3)
> +#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_TWO (0<<4)
> +#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_FOUR (1<<4)
> +#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_MASK (1<<4)
> +#define S5P_FIMV_DEC_STATUS_CRC_GENERATED (1<<5)
> +#define S5P_FIMV_DEC_STATUS_CRC_NOT_GENERATED (0<<5)
> +#define S5P_FIMV_DEC_STATUS_CRC_MASK (1<<5)
Use like (0 << 3), (1 << 3) ...
<snip>
> +/* Enumerate format */
> +static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out,
> + enum s5p_mfc_node_type
> node)
> +{
> + struct s5p_mfc_fmt *fmt;
> + int i, j = 0;
> +
> + if (node == MFCNODE_INVALID)
> + return 0;
> +
> + for (i = 0; i < ARRAY_SIZE(formats); ++i) {
> + if (mplane && formats[i].num_planes == 1)
> + continue;
> + else if (!mplane && formats[i].num_planes > 1)
> + continue;
> + if (node == MFCNODE_DECODER) {
> + if (out && formats[i].type != MFC_FMT_DEC)
> + continue;
> + else if (!out && formats[i].type != MFC_FMT_RAW)
> + continue;
> + } else if (node == MFCNODE_ENCODER) {
> + if (out && formats[i].type != MFC_FMT_RAW)
> + continue;
> + else if (!out && formats[i].type != MFC_FMT_ENC)
> + continue;
> + }
> +
> + if (j == f->index) {
> + fmt = &formats[i];
> + strlcpy(f->description, fmt->name,
> + sizeof(f->description));
> + f->pixelformat = fmt->fourcc;
> +
> + return 0;
> + }
> +
> + ++j;
> + }
> +
> + return -EINVAL;
> +}
> +
At a glance, no needed to use j variable.
if (i == f->index) instead of if (j == f->index)
<snip>
> +/* Get format */
> +static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format
*f)
> +{
> + struct s5p_mfc_ctx *ctx = priv;
> +
> + mfc_debug_enter();
> + mfc_debug("f->type = %d ctx->state = %d\n", f->type, ctx->state);
> + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
> + ctx->state == MFCINST_GOT_INST) {
> + /* If the MFC is parsing the header,
> + * so wait until it is finished */
> + s5p_mfc_clean_ctx_int_flags(ctx);
> + s5p_mfc_wait_for_done_ctx(ctx,
> S5P_FIMV_R2H_CMD_SEQ_DONE_RET,
> + 1);
> + }
> + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
> + ctx->state >= MFCINST_HEAD_PARSED &&
> + ctx->state <= MFCINST_ABORT) {
> + /* This is run on CAPTURE (deocde output) */
> + /* Width and height are set to the dimensions
> + of the movie, the buffer is bigger and
> + further processing stages should crop to this
> + rectangle. */
> + f->fmt.pix_mp.width = ctx->buf_width;
> + f->fmt.pix_mp.height = ctx->buf_height;
> + f->fmt.pix_mp.field = V4L2_FIELD_NONE;
> + f->fmt.pix_mp.num_planes = 2;
> + /* Set pixelformat to the format in which MFC
> + outputs the decoded frame */
> + f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT;
> + f->fmt.pix_mp.plane_fmt[0].bytesperline = ctx->buf_width;
> + f->fmt.pix_mp.plane_fmt[0].sizeimage = ctx->luma_size;
> + f->fmt.pix_mp.plane_fmt[1].bytesperline = ctx->buf_width;
> + f->fmt.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 */
> + f->fmt.pix_mp.width = 1;
> + f->fmt.pix_mp.height = 1;
> + f->fmt.pix_mp.field = V4L2_FIELD_NONE;
> + f->fmt.pix_mp.plane_fmt[0].bytesperline = ctx-
> >dec_src_buf_size;
> + f->fmt.pix_mp.plane_fmt[0].sizeimage =
ctx->dec_src_buf_size;
> + f->fmt.pix_mp.pixelformat = ctx->src_fmt->fourcc;
> + f->fmt.pix_mp.num_planes = ctx->src_fmt->num_planes;
> + } else {
> + mfc_err("Format could not be read\n");
> + mfc_debug("%s-- with error\n", __func__);
> + return -EINVAL;
> + }
> + mfc_debug_leave();
> + return 0;
> +}
> +
How about using size 0 instead of 1 for no meaning value?
In my opinion f->fmt.pix_mp.plane_fmt[1].bytesperline should be
(ctx->buf_width >> 1), isn't it ?
<snip>
> +static int vidioc_try_fmt_enc(struct file *file, void *priv, struct
v4l2_format *f)
> +{
> + struct s5p_mfc_fmt *fmt;
> +
> + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> + fmt = find_format(f, MFC_FMT_ENC);
> + if (!fmt) {
> + mfc_err("failed to try output format\n");
> + return -EINVAL;
> + }
> +
> + if (f->fmt.pix_mp.plane_fmt[0].sizeimage == 0) {
> + mfc_err("must be set encoding output size\n");
> + return -EINVAL;
> + }
> +
> + f->fmt.pix_mp.plane_fmt[0].bytesperline =
> + f->fmt.pix_mp.plane_fmt[0].sizeimage;
> + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> + fmt = find_format(f, MFC_FMT_RAW);
> + if (!fmt) {
> + mfc_err("failed to try output format\n");
> + return -EINVAL;
> + }
> +
> + if (fmt->num_planes != f->fmt.pix_mp.num_planes) {
> + mfc_err("failed to try output format\n");
> + return -EINVAL;
> + }
> + } else {
> + mfc_err("invalid buf type\n");
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
How about check the valid range ?
This driver already has pix_limit variable in variant structure.
<snip>
> +static int vidioc_s_fmt_enc(struct file *file, void *priv, struct
v4l2_format *f)
> +{
> + struct s5p_mfc_ctx *ctx = priv;
> + unsigned long flags;
> + int ret = 0;
> + struct s5p_mfc_fmt *fmt;
> +
> + mfc_debug_enter();
> +
> + ret = vidioc_try_fmt_enc(file, priv, f);
> + if (ret)
> + return ret;
> +
> + if (ctx->vq_src.streaming || ctx->vq_dst.streaming) {
> + v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
> + ret = -EBUSY;
> + goto out;
> + }
> +
> + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> + fmt = find_format(f, MFC_FMT_ENC);
> + if (!fmt) {
> + mfc_err("failed to set capture format\n");
> + return -EINVAL;
> + }
> + ctx->state = MFCINST_INIT;
> +
> + ctx->dst_fmt = fmt;
> + ctx->codec_mode = ctx->dst_fmt->codec_mode;
> + mfc_debug("codec number: %d\n", ctx->dst_fmt->codec_mode);
> +
> + ctx->enc_dst_buf_size =
f->fmt.pix_mp.plane_fmt[0].sizeimage;
> + f->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
> +
> + ctx->dst_bufs_cnt = 0;
> + ctx->capture_state = QUEUE_FREE;
> +
> + s5p_mfc_alloc_instance_buffer(ctx);
> +
> + spin_lock_irqsave(&dev->condlock, flags);
> + set_bit(ctx->num, &dev->ctx_work_bits);
> + spin_unlock_irqrestore(&dev->condlock, flags);
> +
> + s5p_mfc_clean_ctx_int_flags(ctx);
> + s5p_mfc_try_run();
> + if (s5p_mfc_wait_for_done_ctx(ctx, \
> + S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET,
> 1)) {
> + /* Error or timeout */
> + mfc_err("Error getting instance from hardware.\n");
> + s5p_mfc_release_instance_buffer(ctx);
> + ret = -EIO;
> + goto out;
> + }
> + mfc_debug("Got instance number: %d\n", ctx->inst_no);
> + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> + fmt = find_format(f, MFC_FMT_RAW);
> + if (!fmt) {
> + mfc_err("failed to set output format\n");
> + return -EINVAL;
> + }
> +
> + if (fmt->num_planes != f->fmt.pix_mp.num_planes) {
> + mfc_err("failed to set output format\n");
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + ctx->src_fmt = fmt;
> + ctx->img_width = f->fmt.pix_mp.width;
> + ctx->img_height = f->fmt.pix_mp.height;
> +
> + mfc_debug("codec number: %d\n", ctx->src_fmt->codec_mode);
> + mfc_debug("fmt - w: %d, h: %d, ctx - w: %d, h: %d\n",
> + f->fmt.pix_mp.width, f->fmt.pix_mp.height,
> + ctx->img_width, ctx->img_height);
> +
> + ctx->buf_width = f->fmt.pix_mp.plane_fmt[0].bytesperline;
> + ctx->luma_size = f->fmt.pix_mp.plane_fmt[0].sizeimage;
> + ctx->buf_width = f->fmt.pix_mp.plane_fmt[1].bytesperline;
> + ctx->chroma_size = f->fmt.pix_mp.plane_fmt[1].sizeimage;
> +
> + ctx->src_bufs_cnt = 0;
> + ctx->output_state = QUEUE_FREE;
> + } else {
> + mfc_err("invalid buf type\n");
> + return -EINVAL;
> + }
> +out:
> + mfc_debug_leave();
> + return ret;
> +}
> +
ctx->buf_width is overwritten by " f->fmt.pix_mp.plane_fmt[1].bytesperline".
<snip>
> +static int vidioc_reqbufs_enc(struct file *file, void *priv,
> + struct v4l2_requestbuffers
*reqbufs)
> +{
> + struct s5p_mfc_ctx *ctx = priv;
> + int ret = 0;
> +
> + mfc_debug_enter();
> +
> + mfc_debug("type: %d\n", reqbufs->memory);
> +
> + /* if memory is not mmp or userptr return error */
> + if ((reqbufs->memory != V4L2_MEMORY_MMAP) &&
> + (reqbufs->memory != V4L2_MEMORY_USERPTR))
> + return -EINVAL;
> +
> + if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> + if (ctx->capture_state != QUEUE_FREE) {
> + mfc_err("invalid capture state: %d\n", ctx-
> >capture_state);
> + return -EINVAL;
> + }
> +
> + ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
> + if (ret != 0) {
> + mfc_err("error in vb2_reqbufs() for E(D)\n");
> + return ret;
> + }
> + ctx->capture_state = QUEUE_BUFS_REQUESTED;
> +
> + ret = s5p_mfc_alloc_codec_buffers(ctx);
> + if (ret) {
> + mfc_err("Failed to allocate encoding buffers.\n");
> + reqbufs->count = 0;
> + ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
> + return -ENOMEM;
> + }
> + } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> {
> + if (ctx->output_state != QUEUE_FREE) {
> + mfc_err("invalid output state: %d\n",
ctx->output_state);
> + return -EINVAL;
> + }
> +
> + ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
> + if (ret != 0) {
> + mfc_err("error in vb2_reqbufs() for E(S)\n");
> + return ret;
> + }
> + ctx->output_state = QUEUE_BUFS_REQUESTED;
> + } else {
> + mfc_err("invalid buf type\n");
> + return -EINVAL;
> + }
> +
> + mfc_debug("--\n");
> +
> + return ret;
> +}
> +
if (ctx->capture_state != QUEUE_FREE), conditional statement looks strange.
If REQBUFS(0) directly follows REQBUFS(n), state is QUEUE_BUFS_REQUESTED.
So in that case REQBUFS(0) will be failed.
<snip>
> +static int set_ctrl_val_enc(struct s5p_mfc_ctx *ctx, struct v4l2_control
*ctrl)
> +{
> + struct s5p_mfc_enc_params *p = &ctx->enc_params;
> + int ret = 0;
> +
> + switch (ctrl->id) {
> + case V4L2_CID_CODEC_MFC51_ENC_GOP_SIZE:
> + p->gop_size = ctrl->value;
> + break;
> + case V4L2_CID_CODEC_MFC51_ENC_MULTI_SLICE_MODE:
> + p->slice_mode = ctrl->value;
> + break;
> + case V4L2_CID_CODEC_MFC51_ENC_MULTI_SLICE_MB:
> + p->slice_mb = ctrl->value;
> + break;
> + case V4L2_CID_CODEC_MFC51_ENC_MULTI_SLICE_BIT:
> + p->slice_bit = ctrl->value;
> + break;
> + case V4L2_CID_CODEC_MFC51_ENC_INTRA_REFRESH_MB:
> + p->intra_refresh_mb = ctrl->value;
> + break;
> + case V4L2_CID_CODEC_MFC51_ENC_PAD_CTRL_ENABLE:
> + p->pad = ctrl->value;
> + break;
<snip>
> + case V4L2_CID_CODEC_MFC51_ENC_H264_I_PERIOD:
> + p->codec.h264.open_gop_size = ctrl->value;
> + break;
> + default:
> + v4l2_err(&dev->v4l2_dev, "Invalid control\n");
> + ret = -EINVAL;
> + }
> +
> + return ret;
> +}
> +
How about use MACRO like cmd_input_size in
./drivers/media/video/v4l2-ioctl.c.
<snip>
> +static int vidioc_g_ext_ctrls(struct file *file, void *priv,
> + struct v4l2_ext_controls *f)
> +{
> + struct s5p_mfc_ctx *ctx = priv;
> + struct v4l2_ext_control *ext_ctrl;
> + struct v4l2_control ctrl;
> + int i;
> + int ret = 0;
> +
> + if (s5p_mfc_get_node_type(file) != MFCNODE_ENCODER)
> + return -EINVAL;
> +
> + if (f->ctrl_class != V4L2_CTRL_CLASS_CODEC)
> + return -EINVAL;
> +
> + for (i = 0; i < f->count; i++) {
> + ext_ctrl = (f->controls + i);
> +
> + ctrl.id = ext_ctrl->id;
> +
> + ret = get_ctrl_val_enc(ctx, &ctrl);
> + if (ret == 0) {
> + ext_ctrl->value = ctrl.value;
> + } else {
> + f->error_idx = i;
> + break;
> + }
> +
> + mfc_debug("[%d] id: 0x%08x, value: %d", i, ext_ctrl->id,
> + ext_ctrl->value);
> + }
> +
> + return ret;
> +}
> +
How about use vidioc_g_ext_ctrls_enc instead of vidioc_g_ext_ctrls ?
Because the function is only for encoder.
vidioc_s_ext_ctrls also.
<snip>
> +/* Get cropping information */
> +static int vidioc_g_crop(struct file *file, void *priv,
> + struct v4l2_crop *cr)
> +{
> + struct s5p_mfc_ctx *ctx = priv;
> + u32 left, right, top, bottom;
> +
> + mfc_debug_enter();
> + if (ctx->state != MFCINST_HEAD_PARSED &&
> + ctx->state != MFCINST_RUNNING && ctx->state != MFCINST_FINISHING
> + && ctx->state != MFCINST_FINISHED)
> {
> + mfc_debug("%s-- with error\n", __func__);
> + return -EINVAL;
> + }
Keep indent "}"
<snip>
> +static int s5p_mfc_enc_queue_setup(struct vb2_queue *vq,
> + unsigned int *buf_count, unsigned int *plane_count,
> + unsigned long psize[], void *allocators[])
> +{
> + struct s5p_mfc_ctx *ctx = vq->drv_priv;
> + int i;
> +
> + mfc_debug_enter();
> +
> + if (ctx->state != MFCINST_GOT_INST) {
> + mfc_err("inavlid state: %d\n", ctx->state);
> + return -EINVAL;
> + }
> +
> + if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> + if (ctx->dst_fmt)
> + *plane_count = ctx->dst_fmt->num_planes;
> + else
> + *plane_count = MFC_ENC_CAP_PLANE_COUNT;
> +
> + if (*buf_count < 1)
> + *buf_count = 1;
> + if (*buf_count > MFC_MAX_BUFFERS)
> + *buf_count = MFC_MAX_BUFFERS;
> +
> + psize[0] = ctx->enc_dst_buf_size;
> + allocators[0] = ctx->dev-
> >alloc_ctx[MFC_CMA_BANK1_ALLOC_CTX];
> + } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> + if (ctx->src_fmt)
> + *plane_count = ctx->src_fmt->num_planes;
> + else
> + *plane_count = MFC_ENC_OUT_PLANE_COUNT;
> +
> + if (*buf_count < 1)
> + *buf_count = 1;
> + if (*buf_count > MFC_MAX_BUFFERS)
> + *buf_count = MFC_MAX_BUFFERS;
> +
> + psize[0] = ctx->luma_size;
> + psize[1] = ctx->chroma_size;
> + allocators[0] = ctx->dev-
> >alloc_ctx[MFC_CMA_BANK2_ALLOC_CTX];
> + allocators[1] = ctx->dev-
> >alloc_ctx[MFC_CMA_BANK2_ALLOC_CTX];
> +
> + } else {
> + mfc_err("inavlid queue type: %d\n", vq->type);
> + return -EINVAL;
> + }
> +
> + mfc_debug("buf_count: %d, plane_count: %d\n", *buf_count,
> *plane_count);
> + for (i = 0; i < *plane_count; i++)
> + mfc_debug("plane[%d] size=%lu\n", i, psize[i]);
> +
> + mfc_debug_leave();
> +
> + return 0;
> +}
> +
Below two lines are dead line.
else
*plane_count = MFC_ENC_OUT_PLANE_COUNT;
Because ctx->src_fmt is set in s5p_mfc_open.
<snip>
> +/* Let the streaming begin. */
> +static int s5p_mfc_start_streaming(struct vb2_queue *q)
> +{
> + struct s5p_mfc_ctx *ctx = q->drv_priv;
> +
> + unsigned long flags;
> + /* If context is ready then schedule it to run */
> + if (s5p_mfc_ctx_ready(ctx)) {
> + spin_lock_irqsave(&dev->condlock, flags);
> + set_bit(ctx->num, &dev->ctx_work_bits);
> + spin_unlock_irqrestore(&dev->condlock, flags);
> + }
> +
> + s5p_mfc_try_run();
> + return 0;
> +}
> +
In my opinion s5p_mfc_start_streaming is useless.
Because in the following sequence s5p_mfc_try_run will be called in
s5p_mfc_enc_buf_queue without s5p_mfc_start_streaming.
VIDIOC_STREAMON -> vidioc_streamon -> vb2_streamon -> __enqueue_in_driver ->
buf_queue -> s5p_mfc_enc_buf_queue
<snip>
> +/* Open an MFC node */
> +static int s5p_mfc_open(struct file *file)
> +{
> + struct s5p_mfc_ctx *ctx = NULL;
> + struct vb2_queue *q;
> + unsigned long flags;
> + int ret = 0;
> +
> + mfc_debug_enter();
> + dev->num_inst++; /* It is guarded by mfc_mutex in vfd */
> + /* Allocate memory for context */
> + ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
> + if (!ctx) {
> + mfc_err("Not enough memory.\n");
> + ret = -ENOMEM;
> + goto out_open;
> + }
> + file->private_data = ctx;
> + ctx->dev = dev;
> + INIT_LIST_HEAD(&ctx->src_queue);
> + INIT_LIST_HEAD(&ctx->dst_queue);
> + ctx->src_queue_cnt = 0;
> + ctx->dst_queue_cnt = 0;
> + /* Get context number */
> + ctx->num = 0;
> + while (dev->ctx[ctx->num]) {
> + ctx->num++;
> + if (ctx->num >= MFC_NUM_CONTEXTS) {
> + mfc_err("Too many open contexts.\n");
> + ret = -EAGAIN;
> + goto out_open;
> + }
> + }
> + /* Mark context as idle */
> + spin_lock_irqsave(&dev->condlock, flags);
> + clear_bit(ctx->num, &dev->ctx_work_bits);
> + spin_unlock_irqrestore(&dev->condlock, flags);
> + dev->ctx[ctx->num] = ctx;
> + if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
> + ctx->type = MFCINST_DECODER;
> + ctx->c_ops = &decoder_codec_ops;
> + /* Default format */
> + ctx->src_fmt = &formats[DEC_DEF_SRC_FMT];
> + ctx->dst_fmt = &formats[DEC_DEF_DST_FMT];
> + } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
> + ctx->type = MFCINST_ENCODER;
> + ctx->c_ops = &encoder_codec_ops;
> + /* Default format */
> + ctx->src_fmt = &formats[ENC_DEF_SRC_FMT];
> + ctx->dst_fmt = &formats[ENC_DEF_DST_FMT];
> + } else {
> + ret = -ENOENT;
> + goto out_open;
> + }
> + ctx->inst_no = -1;
> + /* Load firmware if this is the first instance */
> + if (dev->num_inst == 1) {
> + dev->watchdog_timer.expires = jiffies +
> +
> msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
> + add_timer(&dev->watchdog_timer);
> +
> + /* Load the FW */
> + ret = s5p_mfc_alloc_firmware(dev);
> + if (ret != 0)
> + goto out_open_2a;
> + ret = s5p_mfc_load_firmware(dev);
> + if (ret != 0)
> + goto out_open_2;
> + mfc_debug("Enabling clocks.\n");
> + clk_enable(dev->clock1);
> + clk_enable(dev->clock2);
> + /* Init the FW */
> + ret = s5p_mfc_init_hw(dev);
> + if (ret != 0)
> + goto out_open_3;
> + }
> +
> + /* Init videobuf2 queue for CAPTURE */
> + q = &ctx->vq_dst;
> + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> + q->drv_priv = ctx;
> + if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
> + q->io_modes = VB2_MMAP;
> + q->ops = &s5p_mfc_qops;
> + } else {
> + q->io_modes = VB2_MMAP | VB2_USERPTR;
> + q->ops = &s5p_mfc_enc_qops;
> + }
> + q->mem_ops = &vb2_cma_memops;
> + ret = vb2_queue_init(q);
> + if (ret) {
> + mfc_err("Failed to initialize videobuf2 queue(capture)\n");
> + goto out_open_3;
> + }
> +
> + /* Init videobuf2 queue for OUTPUT */
> + q = &ctx->vq_src;
> + q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> + q->io_modes = VB2_MMAP;
> + q->drv_priv = ctx;
> + if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
> + q->io_modes = VB2_MMAP;
> + q->ops = &s5p_mfc_qops;
> + } else {
> + q->io_modes = VB2_MMAP | VB2_USERPTR;
> + q->ops = &s5p_mfc_enc_qops;
> + }
> + q->mem_ops = &vb2_cma_memops;
> + ret = vb2_queue_init(q);
> + if (ret) {
> + mfc_err("Failed to initialize videobuf2 queue(output)\n");
> + goto out_open_3;
> + }
> + init_waitqueue_head(&ctx->queue);
> + mfc_debug("%s-- (via irq_cleanup_hw)\n", __func__);
> + return ret;
> + /* Deinit when failure occured */
> +out_open_3:
> + if (dev->num_inst == 1) {
> + clk_disable(dev->clock1);
> + clk_disable(dev->clock2);
> + s5p_mfc_release_firmware(dev);
> + }
> +out_open_2:
> + s5p_mfc_release_firmware(dev);
> +out_open_2a:
> + dev->ctx[ctx->num] = 0;
> + kfree(ctx);
> + del_timer_sync(&dev->watchdog_timer);
> +out_open:
> + dev->num_inst--;
> + mfc_debug_leave();
> + return ret;
> +}
You had better use atomic operation for increment and decrement instead
dev->num_inst++.
In my opinion -EBUSY is good for return value instead of -EAGAIN if ctx->num
is exceed MFC_NUM_CONTEXTS.
In my opinion, " /* Load firmware if this is the first instance */" is not
suitable comment.
Because clk_enable is included in the conditional statements.
How about exchange init videobuf2 sequence OUTPUT, CAPTURE ?
Because OUTPUT is the source of device and CAPTURE is the destination of
device.
<snip>
> +/* MFC probe function */
> +static int s5p_mfc_probe(struct platform_device *pdev)
> +{
> + struct video_device *vfd;
> + struct resource *res;
> + int ret = -ENOENT;
> + size_t size;
> +
> + pr_debug("%s++\n", __func__);
> + dev = kzalloc(sizeof *dev, GFP_KERNEL);
> + if (!dev) {
> + dev_err(&pdev->dev, "Not enough memory for MFC device.\n");
> + return -ENOMEM;
> + }
> +
> + spin_lock_init(&dev->irqlock);
> + spin_lock_init(&dev->condlock);
> + dev_dbg(&pdev->dev, "Initialised spin lock\n");
> + dev->plat_dev = pdev;
> + if (!dev->plat_dev) {
> + dev_err(&pdev->dev, "No platform data specified\n");
> + ret = -ENODEV;
> + goto free_dev;
> + }
> + dev_dbg(&pdev->dev, "Getting clocks\n");
> + dev->clock1 = clk_get(&pdev->dev, "sclk_mfc");
> + dev->clock2 = clk_get(&pdev->dev, "mfc");
> + if (IS_ERR(dev->clock1) || IS_ERR(dev->clock2)) {
> + dev_err(&pdev->dev, "failed to get mfc clock source\n");
> + goto free_clk;
> + }
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (res == NULL) {
> + dev_err(&pdev->dev, "failed to get memory region
resource.\n");
> + ret = -ENOENT;
> + goto probe_out1;
> + }
> + size = (res->end - res->start) + 1;
> + dev->mfc_mem = request_mem_region(res->start, size, pdev->name);
> + if (dev->mfc_mem == NULL) {
> + dev_err(&pdev->dev, "failed to get memory region.\n");
> + ret = -ENOENT;
> + goto probe_out2;
> + }
> + dev->base_virt_addr = ioremap(dev->mfc_mem->start,
> + dev->mfc_mem->end - dev->mfc_mem->start + 1);
> + if (dev->base_virt_addr == NULL) {
> + dev_err(&pdev->dev, "failed to ioremap address region.\n");
> + ret = -ENOENT;
> + goto probe_out3;
> + }
> + dev->regs_base = dev->base_virt_addr;
> + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> + if (res == NULL) {
> + dev_err(&pdev->dev, "failed to get irq resource.\n");
> + ret = -ENOENT;
> + goto probe_out4;
> + }
> + dev->irq = res->start;
> + ret = request_irq(dev->irq, s5p_mfc_irq, IRQF_DISABLED, pdev->name,
> +
> dev);
> + if (ret != 0) {
> + dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
> + goto probe_out5;
> + }
> + dev->mfc_mutex = kmalloc(sizeof(struct mutex), GFP_KERNEL);
> + if (dev->mfc_mutex == NULL) {
> + dev_err(&pdev->dev, "Memory allocation failed\n");
> + ret = -ENOMEM;
> + goto probe_out6;
> + }
> + mutex_init(dev->mfc_mutex);
> + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
> + if (ret)
> + goto probe_out7;
> + init_waitqueue_head(&dev->queue);
> +
> + /* decoder */
> + vfd = video_device_alloc();
> + if (!vfd) {
> + v4l2_err(&dev->v4l2_dev, "Failed to allocate video
device\n");
> + ret = -ENOMEM;
> + goto unreg_dev;
> + }
> + *vfd = s5p_mfc_videodev;
> + vfd->lock = dev->mfc_mutex;
> + vfd->v4l2_dev = &dev->v4l2_dev;
> + snprintf(vfd->name, sizeof(vfd->name), "%s", s5p_mfc_videodev.name);
> +
> + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
> + if (ret) {
> + v4l2_err(&dev->v4l2_dev, "Failed to register video
device\n");
> + video_device_release(vfd);
> + goto rel_vdev_dec;
> + }
> + v4l2_info(&dev->v4l2_dev,
> + "MFC decoder device registered as /dev/video%d\n",
> + vfd->num);
> +
> + dev->vfd_dec = vfd;
> +
> + /* encoder */
> + vfd = video_device_alloc();
> + if (!vfd) {
> + v4l2_err(&dev->v4l2_dev,
> + "Failed to allocate video device\n");
> + ret = -ENOMEM;
> + goto unreg_vdev_dec;
> + }
> + *vfd = s5p_mfc_enc_videodev;
> + vfd->lock = dev->mfc_mutex;
> + vfd->v4l2_dev = &dev->v4l2_dev;
> + snprintf(vfd->name, sizeof(vfd->name), "%s",
> s5p_mfc_enc_videodev.name);
> +
> + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
> + if (ret) {
> + v4l2_err(&dev->v4l2_dev, "Failed to register video
device\n");
> + video_device_release(vfd);
> + goto rel_vdev_enc;
> + }
> + v4l2_info(&dev->v4l2_dev,
> + "MFC encoder device registered as /dev/video%d\n",
> + vfd->num);
> +
> + dev->vfd_enc = vfd;
> +
> + video_set_drvdata(vfd, dev);
> +
> + platform_set_drvdata(pdev, dev);
> + dev->hw_lock = 0;
> + dev->watchdog_workqueue = create_singlethread_workqueue("s5p-mfc");
> + INIT_WORK(&dev->watchdog_work, s5p_mfc_watchdog_worker);
> + atomic_set(&dev->watchdog_cnt, 0);
> + init_timer(&dev->watchdog_timer);
> + dev->watchdog_timer.data = 0;
> + dev->watchdog_timer.function = s5p_mfc_watchdog;
> +
> + dev->alloc_ctx = vb2_cma_init_multi(&pdev->dev,
> MFC_CMA_ALLOC_CTX_NUM,
> + s5p_mem_types,
> s5p_mem_alignments);
> + if (IS_ERR(dev->alloc_ctx)) {
> + mfc_err("Couldn't prepare allocator ctx.\n");
> + ret = PTR_ERR(dev->alloc_ctx);
> + goto alloc_ctx_fail;
> + }
> +
> + pr_debug("%s--\n", __func__);
> + return 0;
> +
> +/* Deinit MFC if probe had failed */
> +alloc_ctx_fail:
> + video_unregister_device(dev->vfd_enc);
> +rel_vdev_enc:
> + video_device_release(dev->vfd_enc);
> +unreg_vdev_dec:
> + video_unregister_device(dev->vfd_dec);
> +rel_vdev_dec:
> + video_device_release(dev->vfd_dec);
> +unreg_dev:
> + v4l2_device_unregister(&dev->v4l2_dev);
> +probe_out7:
> + if (dev->mfc_mutex) {
> + mutex_destroy(dev->mfc_mutex);
> + kfree(dev->mfc_mutex);
> + }
> +probe_out6:
> + free_irq(dev->irq, dev);
> +probe_out5:
> +probe_out4:
> + iounmap(dev->base_virt_addr);
> + dev->base_virt_addr = NULL;
> +probe_out3:
> + release_resource(dev->mfc_mem);
> + kfree(dev->mfc_mem);
> +probe_out2:
> +probe_out1:
> + clk_put(dev->clock1);
> + clk_put(dev->clock2);
> +free_clk:
> +
> +free_dev:
> + kfree(dev);
> + pr_debug("%s-- with error\n", __func__);
> + return ret;
> +}
> +
What's the diff btw dev->regs_base and dev->base_virt_addr ?
In your implementation I cannot find video_get_drvdata.
Is video_set_drvdata needed ?
<snip>
> +#define MFC_NUM_CONTEXTS 4
How about use MFC_NUM_INSTANT instead MFC_NUM_CONTEXTS ?
Because too many terms can make confusion.
<snip>
> +/**
> + * struct s5p_mfc_buf - MFC buffer
> + *
> + */
> +struct s5p_mfc_buf {
> + struct list_head list;
> + struct vb2_buffer *b;
> + union {
> + struct {
> + size_t luma;
> + size_t chroma;
> + } raw;
> + size_t stream;
> + } paddr;
> +};
How about use cookie instead paddr ?
<snip>
> +/**
> + * struct s5p_mfc_ctx - This struct contains the instance context
> + */
> +struct s5p_mfc_ctx {
> + struct s5p_mfc_dev *dev;
> + int num;
> +
> + int int_cond;
> + int int_type;
> + unsigned int int_err;
> + wait_queue_head_t queue;
> +
> + struct s5p_mfc_fmt *src_fmt;
> + struct s5p_mfc_fmt *dst_fmt;
> +
> + struct vb2_queue vq_src;
> + struct vb2_queue vq_dst;
> +
> + struct list_head src_queue;
> + struct list_head dst_queue;
> +
> + unsigned int src_queue_cnt;
> + unsigned int dst_queue_cnt;
> +
> + enum s5p_mfc_inst_type type;
> + enum s5p_mfc_inst_state state;
> + int inst_no;
> +
> + /* Decoder parameters */
> + int img_width;
> + int img_height;
> + int buf_width;
> + int buf_height;
> + int dpb_count;
> + int total_dpb_count;
> +
> + int luma_size;
> + int chroma_size;
> + int mv_size;
> +
> + unsigned long consumed_stream;
> + int slice_interface;
> +
> + /* Buffers */
> + void *port_a_buf;
> + size_t port_a_phys;
> + size_t port_a_size;
> +
> + void *port_b_buf;
> + size_t port_b_phys;
> + size_t port_b_size;
> +
> +
> + enum s5p_mfc_queue_state capture_state;
> + enum s5p_mfc_queue_state output_state;
> +
> + struct s5p_mfc_buf src_bufs[MFC_MAX_BUFFERS];
> + int src_bufs_cnt;
> + struct s5p_mfc_buf dst_bufs[MFC_MAX_BUFFERS];
> + int dst_bufs_cnt;
> +
> + unsigned int sequence;
> + unsigned long dec_dst_flag;
> + size_t dec_src_buf_size;
> +
> + /* Control values */
> + int codec_mode;
> + __u32 pix_format;
> + int loop_filter_mpeg4;
> + int display_delay;
> +
> + /* Buffers */
> + void *instance_buf;
> + size_t instance_phys;
> + size_t instance_size;
> +
> + void *desc_buf;
> + size_t desc_phys;
> +
> + void *shared_buf;
> + size_t shared_phys;
> + void *shared_virt;
> +
> + /* Encoder parameters */
> + struct s5p_mfc_enc_params enc_params;
> +
> + size_t enc_dst_buf_size;
> +
> + int frame_count;
> + enum v4l2_codec_mfc5x_enc_frame_type frame_type;
> + enum v4l2_codec_mfc5x_enc_force_frame_type force_frame_type;
> +
> + struct s5p_mfc_codec_ops *c_ops;
> +};
> +
Data structure is too big. How about split it like as below ?
struct s5p_mfc_ctx {
struct s5p_mfc_dec *dec;
struct s5p_mfc_dec *enc;
....
};
Or like this:
struct s5p_mfc_ctx {
union {
struct s5p_mfc_dec *dec;
struct s5p_mfc_dec *enc;
};
....
};
What's the difference btw num and inst_no ?
It looks very similar.
And how about define common data structure for source, destination about
size, format ... like mfc_frame ?
It can make simplify the structure.
<snip>
> +static struct v4l2_queryctrl s5p_mfc_ctrls[] = {
> +/* For decoding */
> + {
> + .id = V4L2_CID_CODEC_DISPLAY_DELAY,
> + .type = V4L2_CTRL_TYPE_INTEGER,
> + .name = "",
> + .minimum = 0,
> + .maximum = 16383,
> + .step = 1,
> + .default_value = 0,
> + },
> + {
> + .id = V4L2_CID_CODEC_LOOP_FILTER_MPEG4_ENABLE,
> + .type = V4L2_CTRL_TYPE_BOOLEAN,
> + .name = "Mpeg4 Loop Filter Enable",
> + .minimum = 0,
> + .maximum = 1,
> + .step = 1,
> + .default_value = 0,
> + },
> + {
> + .id = V4L2_CID_CODEC_SLICE_INTERFACE,
> + .type = V4L2_CTRL_TYPE_BOOLEAN,
> + .name = "Slice Interface Enable",
> + .minimum = 0,
> + .maximum = 1,
> + .step = 1,
> + .default_value = 0,
> + },
> +/* For encoding */
> + {
> + .id = V4L2_CID_CODEC_MFC51_ENC_GOP_SIZE,
> + .type = V4L2_CTRL_TYPE_INTEGER,
> + .name = "The period of intra frame",
> + .minimum = 0,
> + .maximum = (1 << 16) - 1,
> + .step = 1,
> + .default_value = 0,
> + },
You had better use control-framework.
<snip>
> +/* Allocate firmware */
> +int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev)
> +{
> + int err;
> + struct cma_info mem_info_f, mem_info_a, mem_info_b;
How about exchange above two lines ?
<snip>
> --
> 1.6.2.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html