Signed-off-by: Seung-Woo Kim <sw0312.kim@xxxxxxxxxxx>
[mszyprow: adapted to v4.14 code base, rewrote and extended commit message,
added checks for changing buffer addresses, added bidirectional queue
flags and comments]
Signed-off-by: Marek Szyprowski <m.szyprowski@xxxxxxxxxxx>
---
v2:
- fixed copy/paste bug, which broke encoding support (thanks to Marian
Mihailescu for reporting it)
- added checks for changing buffers DMA addresses
- added bidirectional queue flags
v1:
- inital version
---
drivers/media/platform/s5p-mfc/s5p_mfc.c | 23 +++++-
drivers/media/platform/s5p-mfc/s5p_mfc_dec.c | 111 +++++++++++++++++++--------
drivers/media/platform/s5p-mfc/s5p_mfc_enc.c | 64 +++++++++++----
3 files changed, 147 insertions(+), 51 deletions(-)
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index 1839a86cc2a5..f1ab8d198158 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -754,6 +754,7 @@ static int s5p_mfc_open(struct file *file)
struct s5p_mfc_dev *dev = video_drvdata(file);
struct s5p_mfc_ctx *ctx = NULL;
struct vb2_queue *q;
+ unsigned int io_modes;
int ret = 0;
mfc_debug_enter();
@@ -839,16 +840,25 @@ static int s5p_mfc_open(struct file *file)
if (ret)
goto err_init_hw;
}
+
+ io_modes = VB2_MMAP;
+ if (exynos_is_iommu_available(&dev->plat_dev->dev) || !IS_TWOPORT(dev))
+ io_modes |= VB2_USERPTR | VB2_DMABUF;
+
/* Init videobuf2 queue for CAPTURE */
q = &ctx->vq_dst;
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->io_modes = io_modes;
+ /*
+ * Destination buffers are always bidirectional, they use used as
+ * reference data, which require READ access
+ */
+ q->bidirectional = true;
q->drv_priv = &ctx->fh;
q->lock = &dev->mfc_mutex;
if (vdev == dev->vfd_dec) {
- q->io_modes = VB2_MMAP;
q->ops = get_dec_queue_ops();
} else if (vdev == dev->vfd_enc) {
- q->io_modes = VB2_MMAP | VB2_USERPTR;
q->ops = get_enc_queue_ops();
} else {
ret = -ENOENT;
@@ -869,13 +879,18 @@ static int s5p_mfc_open(struct file *file)
/* Init videobuf2 queue for OUTPUT */
q = &ctx->vq_src;
q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ q->io_modes = io_modes;
+ /*
+ * MFV v5 performs write operations on source data, so make queue
+ * bidirectional to avoid IOMMU protection fault.
+ */
+ if (!IS_MFCV6_PLUS(dev))
+ q->bidirectional = true;
q->drv_priv = &ctx->fh;
q->lock = &dev->mfc_mutex;
if (vdev == dev->vfd_dec) {
- q->io_modes = VB2_MMAP;
q->ops = get_dec_queue_ops();
} else if (vdev == dev->vfd_enc) {
- q->io_modes = VB2_MMAP | VB2_USERPTR;
q->ops = get_enc_queue_ops();
} else {
ret = -ENOENT;
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
index e3e5c442902a..26ee8315e2cf 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
@@ -551,14 +551,27 @@ static int reqbufs_capture(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx,
goto out;
}
- WARN_ON(ctx->dst_bufs_cnt != ctx->total_dpb_count);
- ctx->capture_state = QUEUE_BUFS_MMAPED;
+ if (reqbufs->memory == V4L2_MEMORY_MMAP) {
+ if (ctx->dst_bufs_cnt == ctx->total_dpb_count) {
+ ctx->capture_state = QUEUE_BUFS_MMAPED;
+ } else {
+ mfc_err("Not all buffers passed to buf_init\n");
+ reqbufs->count = 0;
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ s5p_mfc_hw_call(dev->mfc_ops,
+ release_codec_buffers, ctx);
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
- s5p_mfc_wait_for_done_ctx(ctx, S5P_MFC_R2H_CMD_INIT_BUFFERS_RET,
- 0);
+ if (reqbufs->memory == V4L2_MEMORY_MMAP) {
+ s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_MFC_R2H_CMD_INIT_BUFFERS_RET, 0);
+ }
} else {
mfc_err("Buffers have already been requested\n");
ret = -EINVAL;
@@ -576,15 +589,19 @@ static int vidioc_reqbufs(struct file *file, void *priv,
{
struct s5p_mfc_dev *dev = video_drvdata(file);
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
-
- if (reqbufs->memory != V4L2_MEMORY_MMAP) {
- mfc_debug(2, "Only V4L2_MEMORY_MMAP is supported\n");
- return -EINVAL;
- }
+ int ret;
if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = vb2_verify_memory_type(&ctx->vq_src, reqbufs->memory,
+ reqbufs->type);
+ if (ret)
+ return ret;
return reqbufs_output(dev, ctx, reqbufs);
} else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ ret = vb2_verify_memory_type(&ctx->vq_dst, reqbufs->memory,
+ reqbufs->type);
+ if (ret)
+ return ret;
return reqbufs_capture(dev, ctx, reqbufs);
} else {
mfc_err("Invalid type requested\n");
@@ -600,16 +617,20 @@ static int vidioc_querybuf(struct file *file, void *priv,
int ret;
int i;
- if (buf->memory != V4L2_MEMORY_MMAP) {
- mfc_err("Only mmaped buffers can be used\n");
- return -EINVAL;
- }
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_verify_memory_type(&ctx->vq_src, buf->memory,
+ buf->type);
+ if (ret)
+ return ret;
ret = vb2_querybuf(&ctx->vq_src, buf);
} else if (ctx->state == MFCINST_RUNNING &&
buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ ret = vb2_verify_memory_type(&ctx->vq_dst, buf->memory,
+ buf->type);
+ if (ret)
+ return ret;
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;
@@ -940,10 +961,12 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq,
else
alloc_devs[0] = ctx->dev->mem_dev[BANK_R_CTX];
alloc_devs[1] = ctx->dev->mem_dev[BANK_L_CTX];
+ memset(ctx->dst_bufs, 0, sizeof(ctx->dst_bufs));
} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
ctx->state == MFCINST_INIT) {
psize[0] = ctx->dec_src_buf_size;
alloc_devs[0] = ctx->dev->mem_dev[BANK_L_CTX];
+ memset(ctx->src_bufs, 0, sizeof(ctx->src_bufs));
} else {
mfc_err("This video node is dedicated to decoding. Decoding not initialized\n");
return -EINVAL;
@@ -959,30 +982,35 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb)
unsigned int i;
if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ dma_addr_t luma, chroma;
+
if (ctx->capture_state == QUEUE_BUFS_MMAPED)
return 0;
- for (i = 0; i < ctx->dst_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");
+
+ luma = vb2_dma_contig_plane_dma_addr(vb, 0);
+ chroma = vb2_dma_contig_plane_dma_addr(vb, 1);
+ if (!luma || !chroma) {
+ mfc_err("Plane mem not allocated\n");
return -EINVAL;
}
+
i = vb->index;
+ if ((ctx->dst_bufs[i].cookie.raw.luma &&
+ ctx->dst_bufs[i].cookie.raw.luma != luma) ||
+ (ctx->dst_bufs[i].cookie.raw.chroma &&
+ ctx->dst_bufs[i].cookie.raw.chroma != chroma)) {
+ mfc_err("Changing CAPTURE buffer address during straming is not possible\n");
+ return -EINVAL;
+ }
+
ctx->dst_bufs[i].b = vbuf;
- 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[i].cookie.raw.luma = luma;
+ ctx->dst_bufs[i].cookie.raw.chroma = chroma;
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)))) {
+ dma_addr_t stream = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ if (!stream) {
mfc_err("Plane memory not allocated\n");
return -EINVAL;
}
@@ -992,9 +1020,14 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb)
}
i = vb->index;
+ if (ctx->src_bufs[i].cookie.stream &&
+ ctx->src_bufs[i].cookie.stream != stream) {
+ mfc_err("Changing OUTPUT buffer address during straming is not possible\n");
+ return -EINVAL;
+ }
+
ctx->src_bufs[i].b = vbuf;
- ctx->src_bufs[i].cookie.stream =
- vb2_dma_contig_plane_dma_addr(vb, 0);
+ ctx->src_bufs[i].cookie.stream = stream;
ctx->src_bufs_cnt++;
} else {
mfc_err("s5p_mfc_buf_init: unknown queue type\n");
@@ -1071,6 +1104,7 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
struct s5p_mfc_dev *dev = ctx->dev;
unsigned long flags;
struct s5p_mfc_buf *mfc_buf;
+ int wait_flag = 0;
if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
mfc_buf = &ctx->src_bufs[vb->index];
@@ -1088,12 +1122,25 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
list_add_tail(&mfc_buf->list, &ctx->dst_queue);
ctx->dst_queue_cnt++;
spin_unlock_irqrestore(&dev->irqlock, flags);
+ if ((vq->memory == V4L2_MEMORY_USERPTR ||
+ vq->memory == V4L2_MEMORY_DMABUF) &&
+ ctx->dst_queue_cnt == ctx->total_dpb_count)
+ ctx->capture_state = QUEUE_BUFS_MMAPED;
} else {
mfc_err("Unsupported buffer type (%d)\n", vq->type);
}
- if (s5p_mfc_ctx_ready(ctx))
+ if (s5p_mfc_ctx_ready(ctx)) {
set_work_bit_irqsave(ctx);
+ if ((vq->memory == V4L2_MEMORY_USERPTR ||
+ vq->memory == V4L2_MEMORY_DMABUF) &&
+ ctx->state == MFCINST_HEAD_PARSED &&
+ ctx->capture_state == QUEUE_BUFS_MMAPED)
+ wait_flag = 1;
+ }
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ if (wait_flag)
+ s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_MFC_R2H_CMD_INIT_BUFFERS_RET, 0);
}
static struct vb2_ops s5p_mfc_dec_qops = {
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
index 7b041e5ee4be..33fc3f3ef48a 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
@@ -1125,11 +1125,11 @@ static int vidioc_reqbufs(struct file *file, void *priv,
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
int ret = 0;
- /* 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) {
+ ret = vb2_verify_memory_type(&ctx->vq_dst, reqbufs->memory,
+ reqbufs->type);
+ if (ret)
+ return ret;
if (reqbufs->count == 0) {
mfc_debug(2, "Freeing buffers\n");
ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
@@ -1159,6 +1159,10 @@ static int vidioc_reqbufs(struct file *file, void *priv,
return -ENOMEM;
}
} else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = vb2_verify_memory_type(&ctx->vq_src, reqbufs->memory,
+ reqbufs->type);
+ if (ret)
+ return ret;
if (reqbufs->count == 0) {
mfc_debug(2, "Freeing buffers\n");
ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
@@ -1190,6 +1194,8 @@ static int vidioc_reqbufs(struct file *file, void *priv,
mfc_err("error in vb2_reqbufs() for E(S)\n");
return ret;
}
+ if (reqbufs->memory != V4L2_MEMORY_MMAP)
+ ctx->src_bufs_cnt = reqbufs->count;
ctx->output_state = QUEUE_BUFS_REQUESTED;
} else {
mfc_err("invalid buf type\n");
@@ -1204,11 +1210,11 @@ static int vidioc_querybuf(struct file *file, void *priv,
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
int ret = 0;
- /* if memory is not mmp or userptr return error */
- if ((buf->memory != V4L2_MEMORY_MMAP) &&
- (buf->memory != V4L2_MEMORY_USERPTR))
- return -EINVAL;
if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ ret = vb2_verify_memory_type(&ctx->vq_dst, buf->memory,
+ buf->type);
+ if (ret)
+ return ret;
if (ctx->state != MFCINST_GOT_INST) {
mfc_err("invalid context state: %d\n", ctx->state);
return -EINVAL;
@@ -1220,6 +1226,10 @@ static int vidioc_querybuf(struct file *file, void *priv,
}
buf->m.planes[0].m.mem_offset += DST_QUEUE_OFF_BASE;
} else if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = vb2_verify_memory_type(&ctx->vq_src, buf->memory,
+ buf->type);
+ if (ret)
+ return ret;
ret = vb2_querybuf(&ctx->vq_src, buf);
if (ret != 0) {
mfc_err("error in vb2_querybuf() for E(S)\n");
@@ -1828,6 +1838,7 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq,
*buf_count = MFC_MAX_BUFFERS;
psize[0] = ctx->enc_dst_buf_size;
alloc_devs[0] = ctx->dev->mem_dev[BANK_L_CTX];
+ memset(ctx->dst_bufs, 0, sizeof(ctx->dst_bufs));
} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
if (ctx->src_fmt)
*plane_count = ctx->src_fmt->num_planes;
@@ -1849,6 +1860,7 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq,
alloc_devs[0] = ctx->dev->mem_dev[BANK_R_CTX];
alloc_devs[1] = ctx->dev->mem_dev[BANK_R_CTX];
}
+ memset(ctx->src_bufs, 0, sizeof(ctx->src_bufs));
} else {
mfc_err("invalid queue type: %d\n", vq->type);
return -EINVAL;
@@ -1865,25 +1877,47 @@ static int s5p_mfc_buf_init(struct vb2_buffer *vb)
int ret;
if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ dma_addr_t stream;
+
ret = check_vb_with_fmt(ctx->dst_fmt, vb);
if (ret < 0)
return ret;
+
+ stream = vb2_dma_contig_plane_dma_addr(vb, 0);
i = vb->index;
+ if (ctx->dst_bufs[i].cookie.stream &&
+ ctx->src_bufs[i].cookie.stream != stream) {
+ mfc_err("Changing CAPTURE buffer address during straming is not possible\n");
+ return -EINVAL;
+ }
+
ctx->dst_bufs[i].b = vbuf;
- ctx->dst_bufs[i].cookie.stream =
- vb2_dma_contig_plane_dma_addr(vb, 0);
+ ctx->dst_bufs[i].cookie.stream = stream;
ctx->dst_bufs_cnt++;
} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ dma_addr_t luma, chroma;
+
ret = check_vb_with_fmt(ctx->src_fmt, vb);
if (ret < 0)
return ret;
+
+ luma = vb2_dma_contig_plane_dma_addr(vb, 0);
+ chroma = vb2_dma_contig_plane_dma_addr(vb, 1);
+
i = vb->index;
+ if ((ctx->src_bufs[i].cookie.raw.luma &&
+ ctx->src_bufs[i].cookie.raw.luma != luma) ||
+ (ctx->src_bufs[i].cookie.raw.chroma &&
+ ctx->src_bufs[i].cookie.raw.chroma != chroma)) {
+ mfc_err("Changing OUTPUT buffer address during straming is not possible\n");
+ return -EINVAL;
+ }
+
ctx->src_bufs[i].b = vbuf;
- ctx->src_bufs[i].cookie.raw.luma =
- vb2_dma_contig_plane_dma_addr(vb, 0);
- ctx->src_bufs[i].cookie.raw.chroma =
- vb2_dma_contig_plane_dma_addr(vb, 1);
- ctx->src_bufs_cnt++;
+ ctx->src_bufs[i].cookie.raw.luma = luma;
+ ctx->src_bufs[i].cookie.raw.chroma = chroma;
+ if (vb->memory == V4L2_MEMORY_MMAP)
+ ctx->src_bufs_cnt++;
} else {
mfc_err("invalid queue type: %d\n", vq->type);
return -EINVAL;