| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Support eMMa-PrP through mem2mem framework. |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 10 | 11 | * |
|---|
| 11 | 12 | * Copyright (c) 2011 Vista Silicon S.L. |
|---|
| 12 | 13 | * Javier Martin <javier.martin@vista-silicon.com> |
|---|
| 13 | | - * |
|---|
| 14 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 15 | | - * it under the terms of the GNU General Public License as published by the |
|---|
| 16 | | - * Free Software Foundation; either version 2 of the |
|---|
| 17 | | - * License, or (at your option) any later version |
|---|
| 18 | 14 | */ |
|---|
| 19 | 15 | #include <linux/module.h> |
|---|
| 20 | 16 | #include <linux/clk.h> |
|---|
| .. | .. |
|---|
| 124 | 120 | #define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27) |
|---|
| 125 | 121 | #define PRP_CNTL_CH2B1EN (1 << 29) |
|---|
| 126 | 122 | #define PRP_CNTL_CH2B2EN (1 << 30) |
|---|
| 127 | | -#define PRP_CNTL_CH2FEN (1 << 31) |
|---|
| 123 | +#define PRP_CNTL_CH2FEN (1UL << 31) |
|---|
| 128 | 124 | |
|---|
| 129 | 125 | #define PRP_SIZE_HEIGHT(x) (x) |
|---|
| 130 | 126 | #define PRP_SIZE_WIDTH(x) ((x) << 16) |
|---|
| .. | .. |
|---|
| 149 | 145 | #define PRP_INTR_ST_CH2OVF (1 << 8) |
|---|
| 150 | 146 | |
|---|
| 151 | 147 | struct emmaprp_fmt { |
|---|
| 152 | | - char *name; |
|---|
| 153 | 148 | u32 fourcc; |
|---|
| 154 | 149 | /* Types the format can be used for */ |
|---|
| 155 | 150 | u32 types; |
|---|
| .. | .. |
|---|
| 157 | 152 | |
|---|
| 158 | 153 | static struct emmaprp_fmt formats[] = { |
|---|
| 159 | 154 | { |
|---|
| 160 | | - .name = "YUV 4:2:0 Planar", |
|---|
| 161 | 155 | .fourcc = V4L2_PIX_FMT_YUV420, |
|---|
| 162 | 156 | .types = MEM2MEM_CAPTURE, |
|---|
| 163 | 157 | }, |
|---|
| 164 | 158 | { |
|---|
| 165 | | - .name = "4:2:2, packed, YUYV", |
|---|
| 166 | 159 | .fourcc = V4L2_PIX_FMT_YUYV, |
|---|
| 167 | 160 | .types = MEM2MEM_OUTPUT, |
|---|
| 168 | 161 | }, |
|---|
| .. | .. |
|---|
| 214 | 207 | }; |
|---|
| 215 | 208 | |
|---|
| 216 | 209 | struct emmaprp_ctx { |
|---|
| 210 | + struct v4l2_fh fh; |
|---|
| 217 | 211 | struct emmaprp_dev *dev; |
|---|
| 218 | 212 | /* Abort requested by m2m */ |
|---|
| 219 | 213 | int aborting; |
|---|
| 220 | 214 | struct emmaprp_q_data q_data[2]; |
|---|
| 221 | | - struct v4l2_m2m_ctx *m2m_ctx; |
|---|
| 222 | 215 | }; |
|---|
| 223 | 216 | |
|---|
| 224 | 217 | static struct emmaprp_q_data *get_q_data(struct emmaprp_ctx *ctx, |
|---|
| .. | .. |
|---|
| 247 | 240 | |
|---|
| 248 | 241 | dprintk(pcdev, "Aborting task\n"); |
|---|
| 249 | 242 | |
|---|
| 250 | | - v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->m2m_ctx); |
|---|
| 243 | + v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->fh.m2m_ctx); |
|---|
| 251 | 244 | } |
|---|
| 252 | 245 | |
|---|
| 253 | 246 | static inline void emmaprp_dump_regs(struct emmaprp_dev *pcdev) |
|---|
| .. | .. |
|---|
| 282 | 275 | dma_addr_t p_in, p_out; |
|---|
| 283 | 276 | u32 tmp; |
|---|
| 284 | 277 | |
|---|
| 285 | | - src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); |
|---|
| 286 | | - dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); |
|---|
| 278 | + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); |
|---|
| 279 | + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); |
|---|
| 287 | 280 | |
|---|
| 288 | 281 | s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); |
|---|
| 289 | 282 | s_width = s_q_data->width; |
|---|
| .. | .. |
|---|
| 357 | 350 | pr_err("PrP bus error occurred, this transfer is probably corrupted\n"); |
|---|
| 358 | 351 | writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL); |
|---|
| 359 | 352 | } else if (irqst & PRP_INTR_ST_CH2B1CI) { /* buffer ready */ |
|---|
| 360 | | - src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); |
|---|
| 361 | | - dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); |
|---|
| 353 | + src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); |
|---|
| 354 | + dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); |
|---|
| 362 | 355 | |
|---|
| 363 | 356 | dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp; |
|---|
| 364 | 357 | dst_vb->flags &= |
|---|
| .. | .. |
|---|
| 375 | 368 | } |
|---|
| 376 | 369 | } |
|---|
| 377 | 370 | |
|---|
| 378 | | - v4l2_m2m_job_finish(pcdev->m2m_dev, curr_ctx->m2m_ctx); |
|---|
| 371 | + v4l2_m2m_job_finish(pcdev->m2m_dev, curr_ctx->fh.m2m_ctx); |
|---|
| 379 | 372 | return IRQ_HANDLED; |
|---|
| 380 | 373 | } |
|---|
| 381 | 374 | |
|---|
| .. | .. |
|---|
| 385 | 378 | static int vidioc_querycap(struct file *file, void *priv, |
|---|
| 386 | 379 | struct v4l2_capability *cap) |
|---|
| 387 | 380 | { |
|---|
| 388 | | - strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1); |
|---|
| 389 | | - strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1); |
|---|
| 390 | | - cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; |
|---|
| 391 | | - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; |
|---|
| 381 | + strscpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver)); |
|---|
| 382 | + strscpy(cap->card, MEM2MEM_NAME, sizeof(cap->card)); |
|---|
| 392 | 383 | return 0; |
|---|
| 393 | 384 | } |
|---|
| 394 | 385 | |
|---|
| .. | .. |
|---|
| 413 | 404 | if (i < NUM_FORMATS) { |
|---|
| 414 | 405 | /* Format found */ |
|---|
| 415 | 406 | fmt = &formats[i]; |
|---|
| 416 | | - strlcpy(f->description, fmt->name, sizeof(f->description) - 1); |
|---|
| 417 | 407 | f->pixelformat = fmt->fourcc; |
|---|
| 418 | 408 | return 0; |
|---|
| 419 | 409 | } |
|---|
| .. | .. |
|---|
| 439 | 429 | struct vb2_queue *vq; |
|---|
| 440 | 430 | struct emmaprp_q_data *q_data; |
|---|
| 441 | 431 | |
|---|
| 442 | | - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); |
|---|
| 432 | + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); |
|---|
| 443 | 433 | if (!vq) |
|---|
| 444 | 434 | return -EINVAL; |
|---|
| 445 | 435 | |
|---|
| .. | .. |
|---|
| 544 | 534 | struct vb2_queue *vq; |
|---|
| 545 | 535 | int ret; |
|---|
| 546 | 536 | |
|---|
| 547 | | - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); |
|---|
| 537 | + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); |
|---|
| 548 | 538 | if (!vq) |
|---|
| 549 | 539 | return -EINVAL; |
|---|
| 550 | 540 | |
|---|
| .. | .. |
|---|
| 600 | 590 | return vidioc_s_fmt(priv, f); |
|---|
| 601 | 591 | } |
|---|
| 602 | 592 | |
|---|
| 603 | | -static int vidioc_reqbufs(struct file *file, void *priv, |
|---|
| 604 | | - struct v4l2_requestbuffers *reqbufs) |
|---|
| 605 | | -{ |
|---|
| 606 | | - struct emmaprp_ctx *ctx = priv; |
|---|
| 607 | | - |
|---|
| 608 | | - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); |
|---|
| 609 | | -} |
|---|
| 610 | | - |
|---|
| 611 | | -static int vidioc_querybuf(struct file *file, void *priv, |
|---|
| 612 | | - struct v4l2_buffer *buf) |
|---|
| 613 | | -{ |
|---|
| 614 | | - struct emmaprp_ctx *ctx = priv; |
|---|
| 615 | | - |
|---|
| 616 | | - return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); |
|---|
| 617 | | -} |
|---|
| 618 | | - |
|---|
| 619 | | -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) |
|---|
| 620 | | -{ |
|---|
| 621 | | - struct emmaprp_ctx *ctx = priv; |
|---|
| 622 | | - |
|---|
| 623 | | - return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); |
|---|
| 624 | | -} |
|---|
| 625 | | - |
|---|
| 626 | | -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) |
|---|
| 627 | | -{ |
|---|
| 628 | | - struct emmaprp_ctx *ctx = priv; |
|---|
| 629 | | - |
|---|
| 630 | | - return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); |
|---|
| 631 | | -} |
|---|
| 632 | | - |
|---|
| 633 | | -static int vidioc_streamon(struct file *file, void *priv, |
|---|
| 634 | | - enum v4l2_buf_type type) |
|---|
| 635 | | -{ |
|---|
| 636 | | - struct emmaprp_ctx *ctx = priv; |
|---|
| 637 | | - |
|---|
| 638 | | - return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); |
|---|
| 639 | | -} |
|---|
| 640 | | - |
|---|
| 641 | | -static int vidioc_streamoff(struct file *file, void *priv, |
|---|
| 642 | | - enum v4l2_buf_type type) |
|---|
| 643 | | -{ |
|---|
| 644 | | - struct emmaprp_ctx *ctx = priv; |
|---|
| 645 | | - |
|---|
| 646 | | - return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); |
|---|
| 647 | | -} |
|---|
| 648 | | - |
|---|
| 649 | 593 | static const struct v4l2_ioctl_ops emmaprp_ioctl_ops = { |
|---|
| 650 | 594 | .vidioc_querycap = vidioc_querycap, |
|---|
| 651 | 595 | |
|---|
| .. | .. |
|---|
| 659 | 603 | .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, |
|---|
| 660 | 604 | .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, |
|---|
| 661 | 605 | |
|---|
| 662 | | - .vidioc_reqbufs = vidioc_reqbufs, |
|---|
| 663 | | - .vidioc_querybuf = vidioc_querybuf, |
|---|
| 664 | | - |
|---|
| 665 | | - .vidioc_qbuf = vidioc_qbuf, |
|---|
| 666 | | - .vidioc_dqbuf = vidioc_dqbuf, |
|---|
| 667 | | - |
|---|
| 668 | | - .vidioc_streamon = vidioc_streamon, |
|---|
| 669 | | - .vidioc_streamoff = vidioc_streamoff, |
|---|
| 606 | + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, |
|---|
| 607 | + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, |
|---|
| 608 | + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, |
|---|
| 609 | + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, |
|---|
| 610 | + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, |
|---|
| 611 | + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, |
|---|
| 612 | + .vidioc_streamon = v4l2_m2m_ioctl_streamon, |
|---|
| 613 | + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, |
|---|
| 670 | 614 | }; |
|---|
| 671 | 615 | |
|---|
| 672 | 616 | |
|---|
| .. | .. |
|---|
| 726 | 670 | { |
|---|
| 727 | 671 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); |
|---|
| 728 | 672 | struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); |
|---|
| 729 | | - v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf); |
|---|
| 673 | + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); |
|---|
| 730 | 674 | } |
|---|
| 731 | 675 | |
|---|
| 732 | 676 | static const struct vb2_ops emmaprp_qops = { |
|---|
| .. | .. |
|---|
| 744 | 688 | int ret; |
|---|
| 745 | 689 | |
|---|
| 746 | 690 | src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; |
|---|
| 747 | | - src_vq->io_modes = VB2_MMAP | VB2_USERPTR; |
|---|
| 691 | + src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; |
|---|
| 748 | 692 | src_vq->drv_priv = ctx; |
|---|
| 749 | 693 | src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); |
|---|
| 750 | 694 | src_vq->ops = &emmaprp_qops; |
|---|
| .. | .. |
|---|
| 758 | 702 | return ret; |
|---|
| 759 | 703 | |
|---|
| 760 | 704 | dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
|---|
| 761 | | - dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; |
|---|
| 705 | + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; |
|---|
| 762 | 706 | dst_vq->drv_priv = ctx; |
|---|
| 763 | 707 | dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); |
|---|
| 764 | 708 | dst_vq->ops = &emmaprp_qops; |
|---|
| .. | .. |
|---|
| 782 | 726 | if (!ctx) |
|---|
| 783 | 727 | return -ENOMEM; |
|---|
| 784 | 728 | |
|---|
| 785 | | - file->private_data = ctx; |
|---|
| 729 | + v4l2_fh_init(&ctx->fh, video_devdata(file)); |
|---|
| 730 | + file->private_data = &ctx->fh; |
|---|
| 786 | 731 | ctx->dev = pcdev; |
|---|
| 787 | 732 | |
|---|
| 788 | 733 | if (mutex_lock_interruptible(&pcdev->dev_mutex)) { |
|---|
| .. | .. |
|---|
| 790 | 735 | return -ERESTARTSYS; |
|---|
| 791 | 736 | } |
|---|
| 792 | 737 | |
|---|
| 793 | | - ctx->m2m_ctx = v4l2_m2m_ctx_init(pcdev->m2m_dev, ctx, &queue_init); |
|---|
| 738 | + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(pcdev->m2m_dev, ctx, &queue_init); |
|---|
| 794 | 739 | |
|---|
| 795 | | - if (IS_ERR(ctx->m2m_ctx)) { |
|---|
| 796 | | - int ret = PTR_ERR(ctx->m2m_ctx); |
|---|
| 740 | + if (IS_ERR(ctx->fh.m2m_ctx)) { |
|---|
| 741 | + int ret = PTR_ERR(ctx->fh.m2m_ctx); |
|---|
| 797 | 742 | |
|---|
| 798 | 743 | mutex_unlock(&pcdev->dev_mutex); |
|---|
| 799 | 744 | kfree(ctx); |
|---|
| .. | .. |
|---|
| 804 | 749 | clk_prepare_enable(pcdev->clk_emma_ahb); |
|---|
| 805 | 750 | ctx->q_data[V4L2_M2M_SRC].fmt = &formats[1]; |
|---|
| 806 | 751 | ctx->q_data[V4L2_M2M_DST].fmt = &formats[0]; |
|---|
| 752 | + v4l2_fh_add(&ctx->fh); |
|---|
| 807 | 753 | mutex_unlock(&pcdev->dev_mutex); |
|---|
| 808 | 754 | |
|---|
| 809 | | - dprintk(pcdev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx); |
|---|
| 755 | + dprintk(pcdev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->fh.m2m_ctx); |
|---|
| 810 | 756 | |
|---|
| 811 | 757 | return 0; |
|---|
| 812 | 758 | } |
|---|
| .. | .. |
|---|
| 821 | 767 | mutex_lock(&pcdev->dev_mutex); |
|---|
| 822 | 768 | clk_disable_unprepare(pcdev->clk_emma_ahb); |
|---|
| 823 | 769 | clk_disable_unprepare(pcdev->clk_emma_ipg); |
|---|
| 824 | | - v4l2_m2m_ctx_release(ctx->m2m_ctx); |
|---|
| 770 | + v4l2_fh_del(&ctx->fh); |
|---|
| 771 | + v4l2_fh_exit(&ctx->fh); |
|---|
| 772 | + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); |
|---|
| 825 | 773 | mutex_unlock(&pcdev->dev_mutex); |
|---|
| 826 | 774 | kfree(ctx); |
|---|
| 827 | 775 | |
|---|
| 828 | 776 | return 0; |
|---|
| 829 | 777 | } |
|---|
| 830 | 778 | |
|---|
| 831 | | -static __poll_t emmaprp_poll(struct file *file, |
|---|
| 832 | | - struct poll_table_struct *wait) |
|---|
| 833 | | -{ |
|---|
| 834 | | - struct emmaprp_dev *pcdev = video_drvdata(file); |
|---|
| 835 | | - struct emmaprp_ctx *ctx = file->private_data; |
|---|
| 836 | | - __poll_t res; |
|---|
| 837 | | - |
|---|
| 838 | | - mutex_lock(&pcdev->dev_mutex); |
|---|
| 839 | | - res = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); |
|---|
| 840 | | - mutex_unlock(&pcdev->dev_mutex); |
|---|
| 841 | | - return res; |
|---|
| 842 | | -} |
|---|
| 843 | | - |
|---|
| 844 | | -static int emmaprp_mmap(struct file *file, struct vm_area_struct *vma) |
|---|
| 845 | | -{ |
|---|
| 846 | | - struct emmaprp_dev *pcdev = video_drvdata(file); |
|---|
| 847 | | - struct emmaprp_ctx *ctx = file->private_data; |
|---|
| 848 | | - int ret; |
|---|
| 849 | | - |
|---|
| 850 | | - if (mutex_lock_interruptible(&pcdev->dev_mutex)) |
|---|
| 851 | | - return -ERESTARTSYS; |
|---|
| 852 | | - ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); |
|---|
| 853 | | - mutex_unlock(&pcdev->dev_mutex); |
|---|
| 854 | | - return ret; |
|---|
| 855 | | -} |
|---|
| 856 | | - |
|---|
| 857 | 779 | static const struct v4l2_file_operations emmaprp_fops = { |
|---|
| 858 | 780 | .owner = THIS_MODULE, |
|---|
| 859 | 781 | .open = emmaprp_open, |
|---|
| 860 | 782 | .release = emmaprp_release, |
|---|
| 861 | | - .poll = emmaprp_poll, |
|---|
| 783 | + .poll = v4l2_m2m_fop_poll, |
|---|
| 862 | 784 | .unlocked_ioctl = video_ioctl2, |
|---|
| 863 | | - .mmap = emmaprp_mmap, |
|---|
| 785 | + .mmap = v4l2_m2m_fop_mmap, |
|---|
| 864 | 786 | }; |
|---|
| 865 | 787 | |
|---|
| 866 | 788 | static const struct video_device emmaprp_videodev = { |
|---|
| .. | .. |
|---|
| 870 | 792 | .minor = -1, |
|---|
| 871 | 793 | .release = video_device_release, |
|---|
| 872 | 794 | .vfl_dir = VFL_DIR_M2M, |
|---|
| 795 | + .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, |
|---|
| 873 | 796 | }; |
|---|
| 874 | 797 | |
|---|
| 875 | 798 | static const struct v4l2_m2m_ops m2m_ops = { |
|---|
| .. | .. |
|---|
| 946 | 869 | goto rel_vdev; |
|---|
| 947 | 870 | } |
|---|
| 948 | 871 | |
|---|
| 949 | | - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); |
|---|
| 872 | + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); |
|---|
| 950 | 873 | if (ret) { |
|---|
| 951 | 874 | v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n"); |
|---|
| 952 | 875 | goto rel_m2m; |
|---|