media: s5p-mfc: Add support for V4L2_MEMORY_DMABUF type
authorMarek Szyprowski <m.szyprowski@samsung.com>
Fri, 20 Oct 2017 09:33:47 +0000 (11:33 +0200)
committerJunghoon Kim <jhoon20.kim@samsung.com>
Thu, 14 Feb 2019 05:56:42 +0000 (14:56 +0900)
MFC driver supports only MMAP operation mode mainly due to the hardware
restrictions of the addresses of the DMA buffers (MFC v5 hardware can
access buffers only in 128MiB memory region starting from the base address
of its firmware). When IOMMU is available, this requirement is easily
fulfilled even for the buffers located anywhere in the memory - typically
by mapping them in the DMA address space as close as possible to the
firmware. Later hardware revisions don't have this limitations at all.

The second limitation of the MFC hardware related to the memory buffers
is constant buffer address. Once the hardware has been initialized for
operation on given buffer set, the addresses of the buffers cannot be
changed.

With the above assumptions, a limited support for USERPTR and DMABUF
operation modes can be added. The main requirement is to have all buffers
known when starting hardware. This has been achieved by postponing
hardware initialization once all the DMABUF or USERPTR buffers have been
queued for the first time. Once then, buffers cannot be modified to point
to other memory area.

This patch also removes unconditional USERPTR operation mode from encoder
video node, because it doesn't work with v5 MFC hardware without IOMMU
being enabled.

In case of MFC v5 a bidirectional queue flag has to be enabled as a
workaround of the strange hardware behavior - MFC performs a few writes
to source data during the operation.

Signed-off-by: Seung-Woo Kim <sw0312.kim@samsung.com>
[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@samsung.com>
drivers/media/platform/s5p-mfc/s5p_mfc.c
drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
drivers/media/platform/s5p-mfc/s5p_mfc_enc.c

index 2e2f915..236a237 100644 (file)
@@ -755,6 +755,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();
@@ -840,16 +841,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;
@@ -870,13 +880,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;
index 8937b0a..2b3e447 100644 (file)
@@ -546,14 +546,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;
@@ -571,15 +584,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");
@@ -595,16 +612,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;
@@ -935,10 +956,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;
@@ -954,30 +977,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;
                }
@@ -987,9 +1015,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");
@@ -1066,6 +1099,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];
@@ -1083,12 +1117,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 = {
index 2a5fd7c..6d72321 100644 (file)
@@ -1130,11 +1130,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);
@@ -1164,6 +1164,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);
@@ -1195,6 +1199,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");
@@ -1209,11 +1215,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;
@@ -1225,6 +1231,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");
@@ -1833,6 +1843,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;
@@ -1854,6 +1865,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;
@@ -1870,25 +1882,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;