Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f9f715a9 authored by Andrzej Hajda's avatar Andrzej Hajda Committed by Mauro Carvalho Chehab
Browse files

[media] s5p-mfc: added support for end of stream handling in MFC encoder



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: default avatarAndrzej Hajda <a.hajda@samsung.com>
Signed-off-by: default avatarKyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: default avatarSylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 5565a2ad
Loading
Loading
Loading
Loading
+43 −0
Original line number Diff line number Diff line
@@ -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);
@@ -889,9 +929,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,
+4 −1
Original line number Diff line number Diff line
@@ -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;
};

/**
+5 −1
Original line number Diff line number Diff line
@@ -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);
+2 −2
Original line number Diff line number Diff line
@@ -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);
+85 −21
Original line number Diff line number Diff line
@@ -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, NULL);
	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++;
		}
		spin_unlock_irqrestore(&dev->irqlock, flags);
	} else {
		mfc_err("unsupported buffer type (%d)\n", vq->type);
Loading