| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * V4L2 deinterlacing support. |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (c) 2012 Vista Silicon S.L. |
|---|
| 5 | 6 | * Javier Martin <javier.martin@vista-silicon.com> |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 8 | | - * it under the terms of the GNU General Public License as published by the |
|---|
| 9 | | - * Free Software Foundation; either version 2 of the |
|---|
| 10 | | - * License, or (at your option) any later version |
|---|
| 11 | 7 | */ |
|---|
| 12 | 8 | |
|---|
| 13 | 9 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 41 | 37 | v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg) |
|---|
| 42 | 38 | |
|---|
| 43 | 39 | struct deinterlace_fmt { |
|---|
| 44 | | - char *name; |
|---|
| 45 | 40 | u32 fourcc; |
|---|
| 46 | 41 | /* Types the format can be used for */ |
|---|
| 47 | 42 | u32 types; |
|---|
| .. | .. |
|---|
| 49 | 44 | |
|---|
| 50 | 45 | static struct deinterlace_fmt formats[] = { |
|---|
| 51 | 46 | { |
|---|
| 52 | | - .name = "YUV 4:2:0 Planar", |
|---|
| 53 | 47 | .fourcc = V4L2_PIX_FMT_YUV420, |
|---|
| 54 | 48 | .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, |
|---|
| 55 | 49 | }, |
|---|
| 56 | 50 | { |
|---|
| 57 | | - .name = "YUYV 4:2:2", |
|---|
| 58 | 51 | .fourcc = V4L2_PIX_FMT_YUYV, |
|---|
| 59 | 52 | .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, |
|---|
| 60 | 53 | }, |
|---|
| .. | .. |
|---|
| 139 | 132 | }; |
|---|
| 140 | 133 | |
|---|
| 141 | 134 | struct deinterlace_ctx { |
|---|
| 135 | + struct v4l2_fh fh; |
|---|
| 142 | 136 | struct deinterlace_dev *dev; |
|---|
| 143 | 137 | |
|---|
| 144 | 138 | /* Abort requested by m2m */ |
|---|
| 145 | 139 | int aborting; |
|---|
| 146 | 140 | enum v4l2_colorspace colorspace; |
|---|
| 147 | 141 | dma_cookie_t cookie; |
|---|
| 148 | | - struct v4l2_m2m_ctx *m2m_ctx; |
|---|
| 149 | 142 | struct dma_interleaved_template *xt; |
|---|
| 150 | 143 | }; |
|---|
| 151 | 144 | |
|---|
| .. | .. |
|---|
| 157 | 150 | struct deinterlace_ctx *ctx = priv; |
|---|
| 158 | 151 | struct deinterlace_dev *pcdev = ctx->dev; |
|---|
| 159 | 152 | |
|---|
| 160 | | - if ((v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) > 0) |
|---|
| 161 | | - && (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) > 0) |
|---|
| 162 | | - && (atomic_read(&ctx->dev->busy) == 0)) { |
|---|
| 153 | + if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0 && |
|---|
| 154 | + v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) > 0 && |
|---|
| 155 | + !atomic_read(&ctx->dev->busy)) { |
|---|
| 163 | 156 | dprintk(pcdev, "Task ready\n"); |
|---|
| 164 | 157 | return 1; |
|---|
| 165 | 158 | } |
|---|
| .. | .. |
|---|
| 178 | 171 | |
|---|
| 179 | 172 | dprintk(pcdev, "Aborting task\n"); |
|---|
| 180 | 173 | |
|---|
| 181 | | - v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->m2m_ctx); |
|---|
| 174 | + v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->fh.m2m_ctx); |
|---|
| 182 | 175 | } |
|---|
| 183 | 176 | |
|---|
| 184 | 177 | static void dma_callback(void *data) |
|---|
| .. | .. |
|---|
| 189 | 182 | |
|---|
| 190 | 183 | atomic_set(&pcdev->busy, 0); |
|---|
| 191 | 184 | |
|---|
| 192 | | - src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); |
|---|
| 193 | | - dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); |
|---|
| 185 | + src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); |
|---|
| 186 | + dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); |
|---|
| 194 | 187 | |
|---|
| 195 | 188 | dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp; |
|---|
| 196 | 189 | dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; |
|---|
| .. | .. |
|---|
| 201 | 194 | v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); |
|---|
| 202 | 195 | v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); |
|---|
| 203 | 196 | |
|---|
| 204 | | - v4l2_m2m_job_finish(pcdev->m2m_dev, curr_ctx->m2m_ctx); |
|---|
| 197 | + v4l2_m2m_job_finish(pcdev->m2m_dev, curr_ctx->fh.m2m_ctx); |
|---|
| 205 | 198 | |
|---|
| 206 | 199 | dprintk(pcdev, "dma transfers completed.\n"); |
|---|
| 207 | 200 | } |
|---|
| .. | .. |
|---|
| 220 | 213 | dma_addr_t p_in, p_out; |
|---|
| 221 | 214 | enum dma_ctrl_flags flags; |
|---|
| 222 | 215 | |
|---|
| 223 | | - src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); |
|---|
| 224 | | - dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); |
|---|
| 216 | + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); |
|---|
| 217 | + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); |
|---|
| 225 | 218 | |
|---|
| 226 | 219 | s_q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_OUTPUT); |
|---|
| 227 | 220 | s_width = s_q_data->width; |
|---|
| .. | .. |
|---|
| 438 | 431 | static int vidioc_querycap(struct file *file, void *priv, |
|---|
| 439 | 432 | struct v4l2_capability *cap) |
|---|
| 440 | 433 | { |
|---|
| 441 | | - strlcpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver)); |
|---|
| 442 | | - strlcpy(cap->card, MEM2MEM_NAME, sizeof(cap->card)); |
|---|
| 443 | | - strlcpy(cap->bus_info, MEM2MEM_NAME, sizeof(cap->card)); |
|---|
| 444 | | - /* |
|---|
| 445 | | - * This is only a mem-to-mem video device. The capture and output |
|---|
| 446 | | - * device capability flags are left only for backward compatibility |
|---|
| 447 | | - * and are scheduled for removal. |
|---|
| 448 | | - */ |
|---|
| 449 | | - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | |
|---|
| 450 | | - V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; |
|---|
| 451 | | - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; |
|---|
| 452 | | - |
|---|
| 434 | + strscpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver)); |
|---|
| 435 | + strscpy(cap->card, MEM2MEM_NAME, sizeof(cap->card)); |
|---|
| 436 | + strscpy(cap->bus_info, MEM2MEM_NAME, sizeof(cap->bus_info)); |
|---|
| 453 | 437 | return 0; |
|---|
| 454 | 438 | } |
|---|
| 455 | 439 | |
|---|
| .. | .. |
|---|
| 474 | 458 | if (i < NUM_FORMATS) { |
|---|
| 475 | 459 | /* Format found */ |
|---|
| 476 | 460 | fmt = &formats[i]; |
|---|
| 477 | | - strlcpy(f->description, fmt->name, sizeof(f->description)); |
|---|
| 478 | 461 | f->pixelformat = fmt->fourcc; |
|---|
| 479 | 462 | return 0; |
|---|
| 480 | 463 | } |
|---|
| .. | .. |
|---|
| 500 | 483 | struct vb2_queue *vq; |
|---|
| 501 | 484 | struct deinterlace_q_data *q_data; |
|---|
| 502 | 485 | |
|---|
| 503 | | - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); |
|---|
| 486 | + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); |
|---|
| 504 | 487 | if (!vq) |
|---|
| 505 | 488 | return -EINVAL; |
|---|
| 506 | 489 | |
|---|
| .. | .. |
|---|
| 597 | 580 | struct deinterlace_q_data *q_data; |
|---|
| 598 | 581 | struct vb2_queue *vq; |
|---|
| 599 | 582 | |
|---|
| 600 | | - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); |
|---|
| 583 | + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); |
|---|
| 601 | 584 | if (!vq) |
|---|
| 602 | 585 | return -EINVAL; |
|---|
| 603 | 586 | |
|---|
| .. | .. |
|---|
| 670 | 653 | return ret; |
|---|
| 671 | 654 | } |
|---|
| 672 | 655 | |
|---|
| 673 | | -static int vidioc_reqbufs(struct file *file, void *priv, |
|---|
| 674 | | - struct v4l2_requestbuffers *reqbufs) |
|---|
| 675 | | -{ |
|---|
| 676 | | - struct deinterlace_ctx *ctx = priv; |
|---|
| 677 | | - |
|---|
| 678 | | - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); |
|---|
| 679 | | -} |
|---|
| 680 | | - |
|---|
| 681 | | -static int vidioc_querybuf(struct file *file, void *priv, |
|---|
| 682 | | - struct v4l2_buffer *buf) |
|---|
| 683 | | -{ |
|---|
| 684 | | - struct deinterlace_ctx *ctx = priv; |
|---|
| 685 | | - |
|---|
| 686 | | - return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); |
|---|
| 687 | | -} |
|---|
| 688 | | - |
|---|
| 689 | | -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) |
|---|
| 690 | | -{ |
|---|
| 691 | | - struct deinterlace_ctx *ctx = priv; |
|---|
| 692 | | - |
|---|
| 693 | | - return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); |
|---|
| 694 | | -} |
|---|
| 695 | | - |
|---|
| 696 | | -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) |
|---|
| 697 | | -{ |
|---|
| 698 | | - struct deinterlace_ctx *ctx = priv; |
|---|
| 699 | | - |
|---|
| 700 | | - return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); |
|---|
| 701 | | -} |
|---|
| 702 | | - |
|---|
| 703 | 656 | static int vidioc_streamon(struct file *file, void *priv, |
|---|
| 704 | 657 | enum v4l2_buf_type type) |
|---|
| 705 | 658 | { |
|---|
| .. | .. |
|---|
| 740 | 693 | return -EINVAL; |
|---|
| 741 | 694 | } |
|---|
| 742 | 695 | |
|---|
| 743 | | - return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); |
|---|
| 744 | | -} |
|---|
| 745 | | - |
|---|
| 746 | | -static int vidioc_streamoff(struct file *file, void *priv, |
|---|
| 747 | | - enum v4l2_buf_type type) |
|---|
| 748 | | -{ |
|---|
| 749 | | - struct deinterlace_ctx *ctx = priv; |
|---|
| 750 | | - |
|---|
| 751 | | - return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); |
|---|
| 696 | + return v4l2_m2m_streamon(file, ctx->fh.m2m_ctx, type); |
|---|
| 752 | 697 | } |
|---|
| 753 | 698 | |
|---|
| 754 | 699 | static const struct v4l2_ioctl_ops deinterlace_ioctl_ops = { |
|---|
| .. | .. |
|---|
| 764 | 709 | .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, |
|---|
| 765 | 710 | .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, |
|---|
| 766 | 711 | |
|---|
| 767 | | - .vidioc_reqbufs = vidioc_reqbufs, |
|---|
| 768 | | - .vidioc_querybuf = vidioc_querybuf, |
|---|
| 769 | | - |
|---|
| 770 | | - .vidioc_qbuf = vidioc_qbuf, |
|---|
| 771 | | - .vidioc_dqbuf = vidioc_dqbuf, |
|---|
| 712 | + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, |
|---|
| 713 | + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, |
|---|
| 714 | + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, |
|---|
| 715 | + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, |
|---|
| 716 | + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, |
|---|
| 717 | + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, |
|---|
| 772 | 718 | |
|---|
| 773 | 719 | .vidioc_streamon = vidioc_streamon, |
|---|
| 774 | | - .vidioc_streamoff = vidioc_streamoff, |
|---|
| 720 | + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, |
|---|
| 775 | 721 | }; |
|---|
| 776 | 722 | |
|---|
| 777 | 723 | |
|---|
| .. | .. |
|---|
| 835 | 781 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); |
|---|
| 836 | 782 | struct deinterlace_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); |
|---|
| 837 | 783 | |
|---|
| 838 | | - v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf); |
|---|
| 784 | + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); |
|---|
| 839 | 785 | } |
|---|
| 840 | 786 | |
|---|
| 841 | 787 | static const struct vb2_ops deinterlace_qops = { |
|---|
| .. | .. |
|---|
| 853 | 799 | int ret; |
|---|
| 854 | 800 | |
|---|
| 855 | 801 | src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; |
|---|
| 856 | | - src_vq->io_modes = VB2_MMAP | VB2_USERPTR; |
|---|
| 802 | + src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; |
|---|
| 857 | 803 | src_vq->drv_priv = ctx; |
|---|
| 858 | 804 | src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); |
|---|
| 859 | 805 | src_vq->ops = &deinterlace_qops; |
|---|
| .. | .. |
|---|
| 872 | 818 | return ret; |
|---|
| 873 | 819 | |
|---|
| 874 | 820 | dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
|---|
| 875 | | - dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; |
|---|
| 821 | + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; |
|---|
| 876 | 822 | dst_vq->drv_priv = ctx; |
|---|
| 877 | 823 | dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); |
|---|
| 878 | 824 | dst_vq->ops = &deinterlace_qops; |
|---|
| .. | .. |
|---|
| 901 | 847 | if (!ctx) |
|---|
| 902 | 848 | return -ENOMEM; |
|---|
| 903 | 849 | |
|---|
| 904 | | - file->private_data = ctx; |
|---|
| 850 | + v4l2_fh_init(&ctx->fh, video_devdata(file)); |
|---|
| 851 | + file->private_data = &ctx->fh; |
|---|
| 905 | 852 | ctx->dev = pcdev; |
|---|
| 906 | 853 | |
|---|
| 907 | | - ctx->m2m_ctx = v4l2_m2m_ctx_init(pcdev->m2m_dev, ctx, &queue_init); |
|---|
| 908 | | - if (IS_ERR(ctx->m2m_ctx)) { |
|---|
| 909 | | - int ret = PTR_ERR(ctx->m2m_ctx); |
|---|
| 854 | + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(pcdev->m2m_dev, ctx, &queue_init); |
|---|
| 855 | + if (IS_ERR(ctx->fh.m2m_ctx)) { |
|---|
| 856 | + int ret = PTR_ERR(ctx->fh.m2m_ctx); |
|---|
| 910 | 857 | |
|---|
| 911 | 858 | kfree(ctx); |
|---|
| 912 | 859 | return ret; |
|---|
| .. | .. |
|---|
| 920 | 867 | } |
|---|
| 921 | 868 | |
|---|
| 922 | 869 | ctx->colorspace = V4L2_COLORSPACE_REC709; |
|---|
| 870 | + v4l2_fh_add(&ctx->fh); |
|---|
| 923 | 871 | |
|---|
| 924 | | - dprintk(pcdev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx); |
|---|
| 872 | + dprintk(pcdev, "Created instance %p, m2m_ctx: %p\n", |
|---|
| 873 | + ctx, ctx->fh.m2m_ctx); |
|---|
| 925 | 874 | |
|---|
| 926 | 875 | return 0; |
|---|
| 927 | 876 | } |
|---|
| .. | .. |
|---|
| 933 | 882 | |
|---|
| 934 | 883 | dprintk(pcdev, "Releasing instance %p\n", ctx); |
|---|
| 935 | 884 | |
|---|
| 936 | | - v4l2_m2m_ctx_release(ctx->m2m_ctx); |
|---|
| 885 | + v4l2_fh_del(&ctx->fh); |
|---|
| 886 | + v4l2_fh_exit(&ctx->fh); |
|---|
| 887 | + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); |
|---|
| 937 | 888 | kfree(ctx->xt); |
|---|
| 938 | 889 | kfree(ctx); |
|---|
| 939 | 890 | |
|---|
| 940 | 891 | return 0; |
|---|
| 941 | 892 | } |
|---|
| 942 | 893 | |
|---|
| 943 | | -static __poll_t deinterlace_poll(struct file *file, |
|---|
| 944 | | - struct poll_table_struct *wait) |
|---|
| 945 | | -{ |
|---|
| 946 | | - struct deinterlace_ctx *ctx = file->private_data; |
|---|
| 947 | | - __poll_t ret; |
|---|
| 948 | | - |
|---|
| 949 | | - mutex_lock(&ctx->dev->dev_mutex); |
|---|
| 950 | | - ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); |
|---|
| 951 | | - mutex_unlock(&ctx->dev->dev_mutex); |
|---|
| 952 | | - |
|---|
| 953 | | - return ret; |
|---|
| 954 | | -} |
|---|
| 955 | | - |
|---|
| 956 | | -static int deinterlace_mmap(struct file *file, struct vm_area_struct *vma) |
|---|
| 957 | | -{ |
|---|
| 958 | | - struct deinterlace_ctx *ctx = file->private_data; |
|---|
| 959 | | - |
|---|
| 960 | | - return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); |
|---|
| 961 | | -} |
|---|
| 962 | | - |
|---|
| 963 | 894 | static const struct v4l2_file_operations deinterlace_fops = { |
|---|
| 964 | 895 | .owner = THIS_MODULE, |
|---|
| 965 | 896 | .open = deinterlace_open, |
|---|
| 966 | 897 | .release = deinterlace_release, |
|---|
| 967 | | - .poll = deinterlace_poll, |
|---|
| 898 | + .poll = v4l2_m2m_fop_poll, |
|---|
| 968 | 899 | .unlocked_ioctl = video_ioctl2, |
|---|
| 969 | | - .mmap = deinterlace_mmap, |
|---|
| 900 | + .mmap = v4l2_m2m_fop_mmap, |
|---|
| 970 | 901 | }; |
|---|
| 971 | 902 | |
|---|
| 972 | 903 | static const struct video_device deinterlace_videodev = { |
|---|
| .. | .. |
|---|
| 976 | 907 | .minor = -1, |
|---|
| 977 | 908 | .release = video_device_release_empty, |
|---|
| 978 | 909 | .vfl_dir = VFL_DIR_M2M, |
|---|
| 910 | + .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, |
|---|
| 979 | 911 | }; |
|---|
| 980 | 912 | |
|---|
| 981 | 913 | static const struct v4l2_m2m_ops m2m_ops = { |
|---|
| .. | .. |
|---|
| 1021 | 953 | vfd->lock = &pcdev->dev_mutex; |
|---|
| 1022 | 954 | vfd->v4l2_dev = &pcdev->v4l2_dev; |
|---|
| 1023 | 955 | |
|---|
| 1024 | | - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); |
|---|
| 956 | + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); |
|---|
| 1025 | 957 | if (ret) { |
|---|
| 1026 | 958 | v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n"); |
|---|
| 1027 | 959 | goto unreg_dev; |
|---|