Message ID | 1344602845-2831-1-git-send-email-a.hajda@samsung.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Hi Andrzej, Thank you for your patch. Best wishes, -- Kamil Debski Linux Platform Group Samsung Poland R&D Center > From: Andrzej Hajda [mailto:a.hajda@samsung.com] > Sent: 10 August 2012 14:47 > > s5p-mfc encoder after receiving V4L2_ENC_CMD_STOP command > will instruct MFC device to release all encoded frames. > After dequeuing last encoded frame driver will generate > V4L2_EVENT_EOS event. > > Signed-off-by: Andrzej Hajda <a.hajda@samsung.com> > Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Acked-by: Kamil Debski <k.debski@samsung.com> > --- > I have removed bank2 alignement fix from this patch. > It is is already done by patch 's5p-mfc: Fix second memory bank alignment'. > --- > drivers/media/video/s5p-mfc/s5p_mfc.c | 43 +++++++++++ > drivers/media/video/s5p-mfc/s5p_mfc_common.h | 5 +- > drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c | 6 +- > drivers/media/video/s5p-mfc/s5p_mfc_dec.c | 4 +- > drivers/media/video/s5p-mfc/s5p_mfc_enc.c | 106 +++++++++++++++++++++----- > drivers/media/video/s5p-mfc/s5p_mfc_opr.c | 48 +++++++++--- > 6 files changed, 177 insertions(+), 35 deletions(-) > > diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c b/drivers/media/video/s5p- > mfc/s5p_mfc.c > index 9bb68e7..dc9355c 100644 > --- a/drivers/media/video/s5p-mfc/s5p_mfc.c > +++ b/drivers/media/video/s5p-mfc/s5p_mfc.c > @@ -19,6 +19,7 @@ > #include <linux/sched.h> > #include <linux/slab.h> > #include <linux/videodev2.h> > +#include <media/v4l2-event.h> > #include <linux/workqueue.h> > #include <media/videobuf2-core.h> > #include "regs-mfc.h" > @@ -539,6 +540,40 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx > *ctx, > } > } > > +static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx, > + unsigned int reason, unsigned int err) > +{ > + struct s5p_mfc_dev *dev = ctx->dev; > + struct s5p_mfc_buf *mb_entry; > + > + mfc_debug(2, "Stream completed"); > + > + s5p_mfc_clear_int_flags(dev); > + ctx->int_type = reason; > + ctx->int_err = err; > + ctx->state = MFCINST_FINISHED; > + > + spin_lock(&dev->irqlock); > + if (!list_empty(&ctx->dst_queue)) { > + mb_entry = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, > + list); > + list_del(&mb_entry->list); > + ctx->dst_queue_cnt--; > + vb2_set_plane_payload(mb_entry->b, 0, 0); > + vb2_buffer_done(mb_entry->b, VB2_BUF_STATE_DONE); > + } > + spin_unlock(&dev->irqlock); > + > + clear_work_bit(ctx); > + > + if (test_and_clear_bit(0, &dev->hw_lock) == 0) > + WARN_ON(1); > + > + s5p_mfc_clock_off(); > + wake_up(&ctx->queue); > + s5p_mfc_try_run(dev); > +} > + > /* Interrupt processing */ > static irqreturn_t s5p_mfc_irq(int irq, void *priv) > { > @@ -614,6 +649,11 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv) > case S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET: > s5p_mfc_handle_init_buffers(ctx, reason, err); > break; > + > + case S5P_FIMV_R2H_CMD_ENC_COMPLETE_RET: > + s5p_mfc_handle_stream_complete(ctx, reason, err); > + break; > + > default: > mfc_debug(2, "Unknown int reason\n"); > s5p_mfc_clear_int_flags(dev); > @@ -882,9 +922,12 @@ static unsigned int s5p_mfc_poll(struct file *file, > goto end; > } > mutex_unlock(&dev->mfc_mutex); > + poll_wait(file, &ctx->fh.wait, wait); > poll_wait(file, &src_q->done_wq, wait); > poll_wait(file, &dst_q->done_wq, wait); > mutex_lock(&dev->mfc_mutex); > + if (v4l2_event_pending(&ctx->fh)) > + rc |= POLLPRI; > spin_lock_irqsave(&src_q->done_lock, flags); > if (!list_empty(&src_q->done_list)) > src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer, > diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_common.h > b/drivers/media/video/s5p-mfc/s5p_mfc_common.h > index bd5706a..8871f0d 100644 > --- a/drivers/media/video/s5p-mfc/s5p_mfc_common.h > +++ b/drivers/media/video/s5p-mfc/s5p_mfc_common.h > @@ -146,6 +146,9 @@ enum s5p_mfc_decode_arg { > MFC_DEC_RES_CHANGE, > }; > > +#define MFC_BUF_FLAG_USED (1 << 0) > +#define MFC_BUF_FLAG_EOS (1 << 1) > + > struct s5p_mfc_ctx; > > /** > @@ -161,7 +164,7 @@ struct s5p_mfc_buf { > } raw; > size_t stream; > } cookie; > - int used; > + int flags; > }; > > /** > diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c > b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c > index 08a5cfe..002af2b 100644 > --- a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c > +++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c > @@ -98,7 +98,11 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) > release_firmware(fw_blob); > return -EIO; > } > - dev->bank2 = bank2_base_phys; > + /* Valid buffers passed to MFC encoder with LAST_FRAME command > + * should not have address of bank2 - MFC will treat it as a null frame. > + * To avoid such situation we set bank2 address below the pool address. > + */ > + dev->bank2 = bank2_base_phys - (1 << MFC_BASE_ALIGN_ORDER); > memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size); > wmb(); > release_firmware(fw_blob); > diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c > b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c > index c5d567f..65be806 100644 > --- a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c > +++ b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c > @@ -936,14 +936,14 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb) > > if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { > mfc_buf = &ctx->src_bufs[vb->v4l2_buf.index]; > - mfc_buf->used = 0; > + 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->used = 0; > + 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); > diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c > b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c > index aa1c244..602bc7d 100644 > --- a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c > +++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c > @@ -21,6 +21,7 @@ > #include <linux/sched.h> > #include <linux/version.h> > #include <linux/videodev2.h> > +#include <media/v4l2-event.h> > #include <linux/workqueue.h> > #include <media/v4l2-ctrls.h> > #include <media/videobuf2-core.h> > @@ -576,9 +577,9 @@ static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx) > if (ctx->state == MFCINST_RUNNING && > ctx->src_queue_cnt >= 1 && ctx->dst_queue_cnt >= 1) > return 1; > - /* context is ready to encode remain frames */ > + /* context is ready to encode remaining frames */ > if (ctx->state == MFCINST_FINISHING && > - ctx->src_queue_cnt >= 1 && ctx->dst_queue_cnt >= 1) > + ctx->dst_queue_cnt >= 1) > return 1; > mfc_debug(2, "ctx is not ready\n"); > return 0; > @@ -724,7 +725,7 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx) > if ((ctx->src_queue_cnt > 0) && (ctx->state == MFCINST_RUNNING)) { > mb_entry = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, > list); > - if (mb_entry->used) { > + if (mb_entry->flags & MFC_BUF_FLAG_USED) { > list_del(&mb_entry->list); > ctx->src_queue_cnt--; > list_add_tail(&mb_entry->list, &ctx->ref_queue); > @@ -1119,27 +1120,43 @@ static int vidioc_qbuf(struct file *file, void *priv, > struct v4l2_buffer *buf) > mfc_err("Call on QBUF after unrecoverable error\n"); > return -EIO; > } > - if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) > + if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { > + if (ctx->state == MFCINST_FINISHING) { > + mfc_err("Call on QBUF after EOS command\n"); > + return -EIO; > + } > return vb2_qbuf(&ctx->vq_src, buf); > - else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) > + } 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) > - return vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK); > - else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) > - return vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK); > - return -EINVAL; > + 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; > } > > /* Stream on */ > @@ -1471,6 +1488,57 @@ static int vidioc_g_parm(struct file *file, void *priv, > return 0; > } > > +int vidioc_encoder_cmd(struct file *file, void *priv, > + struct v4l2_encoder_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 (!ctx->vq_src.streaming) > + return -EINVAL; > + > + spin_lock_irqsave(&dev->irqlock, flags); > + if (list_empty(&ctx->src_queue)) { > + mfc_debug(2, "EOS: empty src queue, entering finishing > state"); > + ctx->state = MFCINST_FINISHING; > + spin_unlock_irqrestore(&dev->irqlock, flags); > + s5p_mfc_try_run(dev); > + } else { > + mfc_debug(2, "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, > + struct v4l2_event_subscription *sub) > +{ > + switch (sub->type) { > + case V4L2_EVENT_EOS: > + return v4l2_event_subscribe(fh, sub, 2); > + default: > + return -EINVAL; > + } > +} > + > static const struct v4l2_ioctl_ops s5p_mfc_enc_ioctl_ops = { > .vidioc_querycap = vidioc_querycap, > .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, > @@ -1491,6 +1559,9 @@ static const struct v4l2_ioctl_ops s5p_mfc_enc_ioctl_ops = > { > .vidioc_streamoff = vidioc_streamoff, > .vidioc_s_parm = vidioc_s_parm, > .vidioc_g_parm = vidioc_g_parm, > + .vidioc_encoder_cmd = vidioc_encoder_cmd, > + .vidioc_subscribe_event = vidioc_subscribe_event, > + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, > }; > > static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb) > @@ -1706,7 +1777,7 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb) > } > if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { > mfc_buf = &ctx->dst_bufs[vb->v4l2_buf.index]; > - mfc_buf->used = 0; > + mfc_buf->flags &= ~MFC_BUF_FLAG_USED; > /* Mark destination as available for use by MFC */ > spin_lock_irqsave(&dev->irqlock, flags); > list_add_tail(&mfc_buf->list, &ctx->dst_queue); > @@ -1714,17 +1785,10 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb) > spin_unlock_irqrestore(&dev->irqlock, flags); > } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { > mfc_buf = &ctx->src_bufs[vb->v4l2_buf.index]; > - mfc_buf->used = 0; > + mfc_buf->flags &= ~MFC_BUF_FLAG_USED; > spin_lock_irqsave(&dev->irqlock, flags); > - if (vb->v4l2_planes[0].bytesused == 0) { > - mfc_debug(1, "change state to FINISHING\n"); > - ctx->state = MFCINST_FINISHING; > - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); > - cleanup_ref_queue(ctx); > - } else { > - list_add_tail(&mfc_buf->list, &ctx->src_queue); > - ctx->src_queue_cnt++; > - } > + list_add_tail(&mfc_buf->list, &ctx->src_queue); > + ctx->src_queue_cnt++; > spin_unlock_irqrestore(&dev->irqlock, flags); > } else { > mfc_err("unsupported buffer type (%d)\n", vq->type); > diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c > b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c > index e6217cb..9986db0 100644 > --- a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c > +++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c > @@ -1075,14 +1075,21 @@ int s5p_mfc_init_encode(struct s5p_mfc_ctx *ctx) > int s5p_mfc_encode_one_frame(struct s5p_mfc_ctx *ctx) > { > struct s5p_mfc_dev *dev = ctx->dev; > + int cmd; > /* memory structure cur. frame */ > if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M) > mfc_write(dev, 0, S5P_FIMV_ENC_MAP_FOR_CUR); > else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT) > mfc_write(dev, 3, S5P_FIMV_ENC_MAP_FOR_CUR); > s5p_mfc_set_shared_buffer(ctx); > - mfc_write(dev, (S5P_FIMV_CH_FRAME_START << 16 & 0x70000) | > - (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID); > + > + if (ctx->state == MFCINST_FINISHING) > + cmd = S5P_FIMV_CH_LAST_FRAME; > + else > + cmd = S5P_FIMV_CH_FRAME_START; > + mfc_write(dev, ((cmd & S5P_FIMV_CH_MASK) << S5P_FIMV_CH_SHIFT) > + | (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID); > + > return 0; > } > > @@ -1133,7 +1140,7 @@ static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, > int last_frame) > } > /* Get the next source buffer */ > temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); > - temp_vb->used = 1; > + temp_vb->flags |= MFC_BUF_FLAG_USED; > s5p_mfc_set_dec_stream_buffer(ctx, > vb2_dma_contig_plane_dma_addr(temp_vb->b, 0), ctx->consumed_stream, > temp_vb->b->v4l2_planes[0].bytesused); > @@ -1160,7 +1167,7 @@ static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) > unsigned int dst_size; > > spin_lock_irqsave(&dev->irqlock, flags); > - if (list_empty(&ctx->src_queue)) { > + if (list_empty(&ctx->src_queue) && ctx->state != MFCINST_FINISHING) { > mfc_debug(2, "no src buffers\n"); > spin_unlock_irqrestore(&dev->irqlock, flags); > return -EAGAIN; > @@ -1170,19 +1177,40 @@ static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) > spin_unlock_irqrestore(&dev->irqlock, flags); > return -EAGAIN; > } > - src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); > - src_mb->used = 1; > - src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0); > - src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1); > - s5p_mfc_set_enc_frame_buffer(ctx, src_y_addr, src_c_addr); > + if (list_empty(&ctx->src_queue)) { > + /* send null frame */ > + s5p_mfc_set_enc_frame_buffer(ctx, dev->bank2, dev->bank2); > + src_mb = NULL; > + } else { > + src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, > + list); > + src_mb->flags |= MFC_BUF_FLAG_USED; > + if (src_mb->b->v4l2_planes[0].bytesused == 0) { > + /* send null frame */ > + s5p_mfc_set_enc_frame_buffer(ctx, dev->bank2, > + dev->bank2); > + ctx->state = MFCINST_FINISHING; > + } else { > + src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, > + 0); > + src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, > + 1); > + s5p_mfc_set_enc_frame_buffer(ctx, src_y_addr, > + src_c_addr); > + if (src_mb->flags & MFC_BUF_FLAG_EOS) > + ctx->state = MFCINST_FINISHING; > + } > + } > dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); > - dst_mb->used = 1; > + dst_mb->flags |= MFC_BUF_FLAG_USED; > dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0); > dst_size = vb2_plane_size(dst_mb->b, 0); > s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size); > spin_unlock_irqrestore(&dev->irqlock, flags); > dev->curr_ctx = ctx->num; > s5p_mfc_clean_ctx_int_flags(ctx); > + mfc_debug(2, "encoding buffer with index=%d state=%d", > + src_mb ? src_mb->b->v4l2_buf.index : -1, ctx->state); > s5p_mfc_encode_one_frame(ctx); > return 0; > } > -- > 1.7.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c b/drivers/media/video/s5p-mfc/s5p_mfc.c index 9bb68e7..dc9355c 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc.c @@ -19,6 +19,7 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/videodev2.h> +#include <media/v4l2-event.h> #include <linux/workqueue.h> #include <media/videobuf2-core.h> #include "regs-mfc.h" @@ -539,6 +540,40 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx, } } +static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx, + unsigned int reason, unsigned int err) +{ + struct s5p_mfc_dev *dev = ctx->dev; + struct s5p_mfc_buf *mb_entry; + + mfc_debug(2, "Stream completed"); + + s5p_mfc_clear_int_flags(dev); + ctx->int_type = reason; + ctx->int_err = err; + ctx->state = MFCINST_FINISHED; + + spin_lock(&dev->irqlock); + if (!list_empty(&ctx->dst_queue)) { + mb_entry = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, + list); + list_del(&mb_entry->list); + ctx->dst_queue_cnt--; + vb2_set_plane_payload(mb_entry->b, 0, 0); + vb2_buffer_done(mb_entry->b, VB2_BUF_STATE_DONE); + } + spin_unlock(&dev->irqlock); + + clear_work_bit(ctx); + + if (test_and_clear_bit(0, &dev->hw_lock) == 0) + WARN_ON(1); + + s5p_mfc_clock_off(); + wake_up(&ctx->queue); + s5p_mfc_try_run(dev); +} + /* Interrupt processing */ static irqreturn_t s5p_mfc_irq(int irq, void *priv) { @@ -614,6 +649,11 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv) case S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET: s5p_mfc_handle_init_buffers(ctx, reason, err); break; + + case S5P_FIMV_R2H_CMD_ENC_COMPLETE_RET: + s5p_mfc_handle_stream_complete(ctx, reason, err); + break; + default: mfc_debug(2, "Unknown int reason\n"); s5p_mfc_clear_int_flags(dev); @@ -882,9 +922,12 @@ static unsigned int s5p_mfc_poll(struct file *file, goto end; } mutex_unlock(&dev->mfc_mutex); + poll_wait(file, &ctx->fh.wait, wait); poll_wait(file, &src_q->done_wq, wait); poll_wait(file, &dst_q->done_wq, wait); mutex_lock(&dev->mfc_mutex); + if (v4l2_event_pending(&ctx->fh)) + rc |= POLLPRI; spin_lock_irqsave(&src_q->done_lock, flags); if (!list_empty(&src_q->done_list)) src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer, diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_common.h b/drivers/media/video/s5p-mfc/s5p_mfc_common.h index bd5706a..8871f0d 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/video/s5p-mfc/s5p_mfc_common.h @@ -146,6 +146,9 @@ enum s5p_mfc_decode_arg { MFC_DEC_RES_CHANGE, }; +#define MFC_BUF_FLAG_USED (1 << 0) +#define MFC_BUF_FLAG_EOS (1 << 1) + struct s5p_mfc_ctx; /** @@ -161,7 +164,7 @@ struct s5p_mfc_buf { } raw; size_t stream; } cookie; - int used; + int flags; }; /** diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c index 08a5cfe..002af2b 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c @@ -98,7 +98,11 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) release_firmware(fw_blob); return -EIO; } - dev->bank2 = bank2_base_phys; + /* Valid buffers passed to MFC encoder with LAST_FRAME command + * should not have address of bank2 - MFC will treat it as a null frame. + * To avoid such situation we set bank2 address below the pool address. + */ + dev->bank2 = bank2_base_phys - (1 << MFC_BASE_ALIGN_ORDER); memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size); wmb(); release_firmware(fw_blob); diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c index c5d567f..65be806 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c @@ -936,14 +936,14 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb) if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { mfc_buf = &ctx->src_bufs[vb->v4l2_buf.index]; - mfc_buf->used = 0; + 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->used = 0; + 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); diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c index aa1c244..602bc7d 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c @@ -21,6 +21,7 @@ #include <linux/sched.h> #include <linux/version.h> #include <linux/videodev2.h> +#include <media/v4l2-event.h> #include <linux/workqueue.h> #include <media/v4l2-ctrls.h> #include <media/videobuf2-core.h> @@ -576,9 +577,9 @@ static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx) if (ctx->state == MFCINST_RUNNING && ctx->src_queue_cnt >= 1 && ctx->dst_queue_cnt >= 1) return 1; - /* context is ready to encode remain frames */ + /* context is ready to encode remaining frames */ if (ctx->state == MFCINST_FINISHING && - ctx->src_queue_cnt >= 1 && ctx->dst_queue_cnt >= 1) + ctx->dst_queue_cnt >= 1) return 1; mfc_debug(2, "ctx is not ready\n"); return 0; @@ -724,7 +725,7 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx) if ((ctx->src_queue_cnt > 0) && (ctx->state == MFCINST_RUNNING)) { mb_entry = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); - if (mb_entry->used) { + if (mb_entry->flags & MFC_BUF_FLAG_USED) { list_del(&mb_entry->list); ctx->src_queue_cnt--; list_add_tail(&mb_entry->list, &ctx->ref_queue); @@ -1119,27 +1120,43 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) mfc_err("Call on QBUF after unrecoverable error\n"); return -EIO; } - if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (ctx->state == MFCINST_FINISHING) { + mfc_err("Call on QBUF after EOS command\n"); + return -EIO; + } return vb2_qbuf(&ctx->vq_src, buf); - else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + } 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) - return vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK); - else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - return vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK); - return -EINVAL; + 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; } /* Stream on */ @@ -1471,6 +1488,57 @@ static int vidioc_g_parm(struct file *file, void *priv, return 0; } +int vidioc_encoder_cmd(struct file *file, void *priv, + struct v4l2_encoder_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 (!ctx->vq_src.streaming) + return -EINVAL; + + spin_lock_irqsave(&dev->irqlock, flags); + if (list_empty(&ctx->src_queue)) { + mfc_debug(2, "EOS: empty src queue, entering finishing state"); + ctx->state = MFCINST_FINISHING; + spin_unlock_irqrestore(&dev->irqlock, flags); + s5p_mfc_try_run(dev); + } else { + mfc_debug(2, "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, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 2); + default: + return -EINVAL; + } +} + static const struct v4l2_ioctl_ops s5p_mfc_enc_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, @@ -1491,6 +1559,9 @@ static const struct v4l2_ioctl_ops s5p_mfc_enc_ioctl_ops = { .vidioc_streamoff = vidioc_streamoff, .vidioc_s_parm = vidioc_s_parm, .vidioc_g_parm = vidioc_g_parm, + .vidioc_encoder_cmd = vidioc_encoder_cmd, + .vidioc_subscribe_event = vidioc_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb) @@ -1706,7 +1777,7 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb) } if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { mfc_buf = &ctx->dst_bufs[vb->v4l2_buf.index]; - mfc_buf->used = 0; + mfc_buf->flags &= ~MFC_BUF_FLAG_USED; /* Mark destination as available for use by MFC */ spin_lock_irqsave(&dev->irqlock, flags); list_add_tail(&mfc_buf->list, &ctx->dst_queue); @@ -1714,17 +1785,10 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&dev->irqlock, flags); } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { mfc_buf = &ctx->src_bufs[vb->v4l2_buf.index]; - mfc_buf->used = 0; + mfc_buf->flags &= ~MFC_BUF_FLAG_USED; spin_lock_irqsave(&dev->irqlock, flags); - if (vb->v4l2_planes[0].bytesused == 0) { - mfc_debug(1, "change state to FINISHING\n"); - ctx->state = MFCINST_FINISHING; - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); - cleanup_ref_queue(ctx); - } else { - list_add_tail(&mfc_buf->list, &ctx->src_queue); - ctx->src_queue_cnt++; - } + list_add_tail(&mfc_buf->list, &ctx->src_queue); + ctx->src_queue_cnt++; spin_unlock_irqrestore(&dev->irqlock, flags); } else { mfc_err("unsupported buffer type (%d)\n", vq->type); diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c index e6217cb..9986db0 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c @@ -1075,14 +1075,21 @@ int s5p_mfc_init_encode(struct s5p_mfc_ctx *ctx) int s5p_mfc_encode_one_frame(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; + int cmd; /* memory structure cur. frame */ if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M) mfc_write(dev, 0, S5P_FIMV_ENC_MAP_FOR_CUR); else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT) mfc_write(dev, 3, S5P_FIMV_ENC_MAP_FOR_CUR); s5p_mfc_set_shared_buffer(ctx); - mfc_write(dev, (S5P_FIMV_CH_FRAME_START << 16 & 0x70000) | - (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID); + + if (ctx->state == MFCINST_FINISHING) + cmd = S5P_FIMV_CH_LAST_FRAME; + else + cmd = S5P_FIMV_CH_FRAME_START; + mfc_write(dev, ((cmd & S5P_FIMV_CH_MASK) << S5P_FIMV_CH_SHIFT) + | (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID); + return 0; } @@ -1133,7 +1140,7 @@ static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame) } /* Get the next source buffer */ temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); - temp_vb->used = 1; + temp_vb->flags |= MFC_BUF_FLAG_USED; s5p_mfc_set_dec_stream_buffer(ctx, vb2_dma_contig_plane_dma_addr(temp_vb->b, 0), ctx->consumed_stream, temp_vb->b->v4l2_planes[0].bytesused); @@ -1160,7 +1167,7 @@ static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) unsigned int dst_size; spin_lock_irqsave(&dev->irqlock, flags); - if (list_empty(&ctx->src_queue)) { + if (list_empty(&ctx->src_queue) && ctx->state != MFCINST_FINISHING) { mfc_debug(2, "no src buffers\n"); spin_unlock_irqrestore(&dev->irqlock, flags); return -EAGAIN; @@ -1170,19 +1177,40 @@ static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) spin_unlock_irqrestore(&dev->irqlock, flags); return -EAGAIN; } - src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); - src_mb->used = 1; - src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0); - src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1); - s5p_mfc_set_enc_frame_buffer(ctx, src_y_addr, src_c_addr); + if (list_empty(&ctx->src_queue)) { + /* send null frame */ + s5p_mfc_set_enc_frame_buffer(ctx, dev->bank2, dev->bank2); + src_mb = NULL; + } else { + src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, + list); + src_mb->flags |= MFC_BUF_FLAG_USED; + if (src_mb->b->v4l2_planes[0].bytesused == 0) { + /* send null frame */ + s5p_mfc_set_enc_frame_buffer(ctx, dev->bank2, + dev->bank2); + ctx->state = MFCINST_FINISHING; + } else { + src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, + 0); + src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, + 1); + s5p_mfc_set_enc_frame_buffer(ctx, src_y_addr, + src_c_addr); + if (src_mb->flags & MFC_BUF_FLAG_EOS) + ctx->state = MFCINST_FINISHING; + } + } dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); - dst_mb->used = 1; + dst_mb->flags |= MFC_BUF_FLAG_USED; dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0); dst_size = vb2_plane_size(dst_mb->b, 0); s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size); spin_unlock_irqrestore(&dev->irqlock, flags); dev->curr_ctx = ctx->num; s5p_mfc_clean_ctx_int_flags(ctx); + mfc_debug(2, "encoding buffer with index=%d state=%d", + src_mb ? src_mb->b->v4l2_buf.index : -1, ctx->state); s5p_mfc_encode_one_frame(ctx); return 0; }