| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * V4L2 Deinterlacer Subdev for Freescale i.MX5/6 SOC |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (c) 2017 Mentor Graphics Inc. |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 7 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 8 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 9 | | - * (at your option) any later version. |
|---|
| 10 | 6 | */ |
|---|
| 11 | | -#include <linux/delay.h> |
|---|
| 12 | | -#include <linux/interrupt.h> |
|---|
| 13 | | -#include <linux/module.h> |
|---|
| 14 | | -#include <linux/platform_device.h> |
|---|
| 15 | | -#include <linux/sched.h> |
|---|
| 16 | | -#include <linux/slab.h> |
|---|
| 17 | | -#include <linux/timer.h> |
|---|
| 18 | 7 | #include <media/v4l2-ctrls.h> |
|---|
| 19 | 8 | #include <media/v4l2-device.h> |
|---|
| 20 | 9 | #include <media/v4l2-ioctl.h> |
|---|
| .. | .. |
|---|
| 60 | 49 | /* |
|---|
| 61 | 50 | * Min/Max supported width and heights. |
|---|
| 62 | 51 | */ |
|---|
| 63 | | -#define MIN_W 176 |
|---|
| 64 | | -#define MIN_H 144 |
|---|
| 52 | +#define MIN_W 32 |
|---|
| 53 | +#define MIN_H 32 |
|---|
| 65 | 54 | #define MAX_W_VDIC 968 |
|---|
| 66 | 55 | #define MAX_H_VDIC 2048 |
|---|
| 67 | 56 | #define W_ALIGN 4 /* multiple of 16 pixels */ |
|---|
| .. | .. |
|---|
| 69 | 58 | #define S_ALIGN 1 /* multiple of 2 */ |
|---|
| 70 | 59 | |
|---|
| 71 | 60 | struct vdic_priv { |
|---|
| 72 | | - struct device *dev; |
|---|
| 73 | | - struct ipu_soc *ipu; |
|---|
| 74 | | - struct imx_media_dev *md; |
|---|
| 61 | + struct device *ipu_dev; |
|---|
| 62 | + struct ipu_soc *ipu; |
|---|
| 63 | + |
|---|
| 75 | 64 | struct v4l2_subdev sd; |
|---|
| 76 | 65 | struct media_pad pad[VDIC_NUM_PADS]; |
|---|
| 77 | | - int ipu_id; |
|---|
| 78 | 66 | |
|---|
| 79 | 67 | /* lock to protect all members below */ |
|---|
| 80 | 68 | struct mutex lock; |
|---|
| .. | .. |
|---|
| 149 | 137 | struct ipuv3_channel *ch; |
|---|
| 150 | 138 | struct ipu_vdi *vdi; |
|---|
| 151 | 139 | |
|---|
| 152 | | - priv->ipu = priv->md->ipu[priv->ipu_id]; |
|---|
| 153 | | - |
|---|
| 154 | 140 | vdi = ipu_vdi_get(priv->ipu); |
|---|
| 155 | 141 | if (IS_ERR(vdi)) { |
|---|
| 156 | 142 | v4l2_err(&priv->sd, "failed to get VDIC\n"); |
|---|
| .. | .. |
|---|
| 219 | 205 | |
|---|
| 220 | 206 | switch (priv->fieldtype) { |
|---|
| 221 | 207 | case V4L2_FIELD_SEQ_TB: |
|---|
| 222 | | - prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0); |
|---|
| 223 | | - curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs; |
|---|
| 224 | | - next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); |
|---|
| 225 | | - break; |
|---|
| 226 | 208 | case V4L2_FIELD_SEQ_BT: |
|---|
| 227 | 209 | prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + fs; |
|---|
| 228 | 210 | curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); |
|---|
| 229 | 211 | next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs; |
|---|
| 230 | 212 | break; |
|---|
| 213 | + case V4L2_FIELD_INTERLACED_TB: |
|---|
| 231 | 214 | case V4L2_FIELD_INTERLACED_BT: |
|---|
| 215 | + case V4L2_FIELD_INTERLACED: |
|---|
| 232 | 216 | prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + is; |
|---|
| 233 | 217 | curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); |
|---|
| 234 | 218 | next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is; |
|---|
| 235 | 219 | break; |
|---|
| 236 | 220 | default: |
|---|
| 237 | | - /* assume V4L2_FIELD_INTERLACED_TB */ |
|---|
| 238 | | - prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0); |
|---|
| 239 | | - curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is; |
|---|
| 240 | | - next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); |
|---|
| 241 | | - break; |
|---|
| 221 | + /* |
|---|
| 222 | + * can't get here, priv->fieldtype can only be one of |
|---|
| 223 | + * the above. This is to quiet smatch errors. |
|---|
| 224 | + */ |
|---|
| 225 | + return; |
|---|
| 242 | 226 | } |
|---|
| 243 | 227 | |
|---|
| 244 | 228 | ipu_cpmem_set_buffer(priv->vdi_in_ch_p, 0, prev_phys); |
|---|
| .. | .. |
|---|
| 263 | 247 | |
|---|
| 264 | 248 | memset(&image, 0, sizeof(image)); |
|---|
| 265 | 249 | image.pix = vdev->fmt.fmt.pix; |
|---|
| 250 | + image.rect = vdev->compose; |
|---|
| 266 | 251 | /* one field to VDIC channels */ |
|---|
| 267 | 252 | image.pix.height /= 2; |
|---|
| 268 | | - image.rect.width = image.pix.width; |
|---|
| 269 | | - image.rect.height = image.pix.height; |
|---|
| 253 | + image.rect.height /= 2; |
|---|
| 270 | 254 | image.phys0 = phys0; |
|---|
| 271 | 255 | image.phys1 = phys1; |
|---|
| 272 | 256 | |
|---|
| .. | .. |
|---|
| 517 | 501 | if (priv->stream_count != !enable) |
|---|
| 518 | 502 | goto update_count; |
|---|
| 519 | 503 | |
|---|
| 520 | | - dev_dbg(priv->dev, "stream %s\n", enable ? "ON" : "OFF"); |
|---|
| 504 | + dev_dbg(priv->ipu_dev, "%s: stream %s\n", sd->name, |
|---|
| 505 | + enable ? "ON" : "OFF"); |
|---|
| 521 | 506 | |
|---|
| 522 | 507 | if (enable) |
|---|
| 523 | 508 | ret = vdic_start(priv); |
|---|
| .. | .. |
|---|
| 563 | 548 | if (code->pad >= VDIC_NUM_PADS) |
|---|
| 564 | 549 | return -EINVAL; |
|---|
| 565 | 550 | |
|---|
| 566 | | - return imx_media_enum_ipu_format(&code->code, code->index, CS_SEL_YUV); |
|---|
| 551 | + return imx_media_enum_ipu_formats(&code->code, code->index, |
|---|
| 552 | + PIXFMT_SEL_YUV); |
|---|
| 567 | 553 | } |
|---|
| 568 | 554 | |
|---|
| 569 | 555 | static int vdic_get_fmt(struct v4l2_subdev *sd, |
|---|
| .. | .. |
|---|
| 598 | 584 | { |
|---|
| 599 | 585 | struct v4l2_mbus_framefmt *infmt; |
|---|
| 600 | 586 | |
|---|
| 601 | | - *cc = imx_media_find_ipu_format(sdformat->format.code, CS_SEL_YUV); |
|---|
| 587 | + *cc = imx_media_find_ipu_format(sdformat->format.code, |
|---|
| 588 | + PIXFMT_SEL_YUV); |
|---|
| 602 | 589 | if (!*cc) { |
|---|
| 603 | 590 | u32 code; |
|---|
| 604 | 591 | |
|---|
| 605 | | - imx_media_enum_ipu_format(&code, 0, CS_SEL_YUV); |
|---|
| 606 | | - *cc = imx_media_find_ipu_format(code, CS_SEL_YUV); |
|---|
| 592 | + imx_media_enum_ipu_formats(&code, 0, PIXFMT_SEL_YUV); |
|---|
| 593 | + *cc = imx_media_find_ipu_format(code, PIXFMT_SEL_YUV); |
|---|
| 607 | 594 | sdformat->format.code = (*cc)->codes[0]; |
|---|
| 608 | 595 | } |
|---|
| 609 | 596 | |
|---|
| .. | .. |
|---|
| 623 | 610 | &sdformat->format.height, |
|---|
| 624 | 611 | MIN_H, MAX_H_VDIC, H_ALIGN, S_ALIGN); |
|---|
| 625 | 612 | |
|---|
| 626 | | - imx_media_fill_default_mbus_fields(&sdformat->format, infmt, |
|---|
| 627 | | - true); |
|---|
| 628 | | - |
|---|
| 629 | 613 | /* input must be interlaced! Choose SEQ_TB if not */ |
|---|
| 630 | 614 | if (!V4L2_FIELD_HAS_BOTH(sdformat->format.field)) |
|---|
| 631 | 615 | sdformat->format.field = V4L2_FIELD_SEQ_TB; |
|---|
| 632 | 616 | break; |
|---|
| 633 | 617 | } |
|---|
| 618 | + |
|---|
| 619 | + imx_media_try_colorimetry(&sdformat->format, true); |
|---|
| 634 | 620 | } |
|---|
| 635 | 621 | |
|---|
| 636 | 622 | static int vdic_set_fmt(struct v4l2_subdev *sd, |
|---|
| .. | .. |
|---|
| 692 | 678 | struct v4l2_subdev *remote_sd; |
|---|
| 693 | 679 | int ret = 0; |
|---|
| 694 | 680 | |
|---|
| 695 | | - dev_dbg(priv->dev, "link setup %s -> %s", remote->entity->name, |
|---|
| 696 | | - local->entity->name); |
|---|
| 681 | + dev_dbg(priv->ipu_dev, "%s: link setup %s -> %s", |
|---|
| 682 | + sd->name, remote->entity->name, local->entity->name); |
|---|
| 697 | 683 | |
|---|
| 698 | 684 | mutex_lock(&priv->lock); |
|---|
| 699 | 685 | |
|---|
| .. | .. |
|---|
| 752 | 738 | remote_sd = media_entity_to_v4l2_subdev(remote->entity); |
|---|
| 753 | 739 | |
|---|
| 754 | 740 | /* direct pad must connect to a CSI */ |
|---|
| 755 | | - if (!(remote_sd->grp_id & IMX_MEDIA_GRP_ID_CSI) || |
|---|
| 741 | + if (!(remote_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_CSI) || |
|---|
| 756 | 742 | remote->index != CSI_SRC_PAD_DIRECT) { |
|---|
| 757 | 743 | ret = -EINVAL; |
|---|
| 758 | 744 | goto out; |
|---|
| .. | .. |
|---|
| 826 | 812 | switch (fi->pad) { |
|---|
| 827 | 813 | case VDIC_SINK_PAD_DIRECT: |
|---|
| 828 | 814 | case VDIC_SINK_PAD_IDMAC: |
|---|
| 829 | | - /* No limits on input frame interval */ |
|---|
| 815 | + /* No limits on valid input frame intervals */ |
|---|
| 816 | + if (fi->interval.numerator == 0 || |
|---|
| 817 | + fi->interval.denominator == 0) |
|---|
| 818 | + fi->interval = priv->frame_interval[fi->pad]; |
|---|
| 830 | 819 | /* Reset output interval */ |
|---|
| 831 | 820 | *output_fi = fi->interval; |
|---|
| 832 | 821 | if (priv->csi_direct) |
|---|
| .. | .. |
|---|
| 854 | 843 | return ret; |
|---|
| 855 | 844 | } |
|---|
| 856 | 845 | |
|---|
| 857 | | -/* |
|---|
| 858 | | - * retrieve our pads parsed from the OF graph by the media device |
|---|
| 859 | | - */ |
|---|
| 860 | 846 | static int vdic_registered(struct v4l2_subdev *sd) |
|---|
| 861 | 847 | { |
|---|
| 862 | 848 | struct vdic_priv *priv = v4l2_get_subdevdata(sd); |
|---|
| 863 | 849 | int i, ret; |
|---|
| 864 | 850 | u32 code; |
|---|
| 865 | 851 | |
|---|
| 866 | | - /* get media device */ |
|---|
| 867 | | - priv->md = dev_get_drvdata(sd->v4l2_dev->dev); |
|---|
| 868 | | - |
|---|
| 869 | 852 | for (i = 0; i < VDIC_NUM_PADS; i++) { |
|---|
| 870 | | - priv->pad[i].flags = (i == VDIC_SRC_PAD_DIRECT) ? |
|---|
| 871 | | - MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK; |
|---|
| 872 | | - |
|---|
| 873 | 853 | code = 0; |
|---|
| 874 | 854 | if (i != VDIC_SINK_PAD_IDMAC) |
|---|
| 875 | | - imx_media_enum_ipu_format(&code, 0, CS_SEL_YUV); |
|---|
| 855 | + imx_media_enum_ipu_formats(&code, 0, PIXFMT_SEL_YUV); |
|---|
| 876 | 856 | |
|---|
| 877 | 857 | /* set a default mbus format */ |
|---|
| 878 | 858 | ret = imx_media_init_mbus_fmt(&priv->format_mbus[i], |
|---|
| .. | .. |
|---|
| 890 | 870 | |
|---|
| 891 | 871 | priv->active_input_pad = VDIC_SINK_PAD_DIRECT; |
|---|
| 892 | 872 | |
|---|
| 893 | | - ret = vdic_init_controls(priv); |
|---|
| 894 | | - if (ret) |
|---|
| 895 | | - return ret; |
|---|
| 896 | | - |
|---|
| 897 | | - ret = media_entity_pads_init(&sd->entity, VDIC_NUM_PADS, priv->pad); |
|---|
| 898 | | - if (ret) |
|---|
| 899 | | - v4l2_ctrl_handler_free(&priv->ctrl_hdlr); |
|---|
| 900 | | - |
|---|
| 901 | | - return ret; |
|---|
| 873 | + return vdic_init_controls(priv); |
|---|
| 902 | 874 | } |
|---|
| 903 | 875 | |
|---|
| 904 | 876 | static void vdic_unregistered(struct v4l2_subdev *sd) |
|---|
| .. | .. |
|---|
| 937 | 909 | .unregistered = vdic_unregistered, |
|---|
| 938 | 910 | }; |
|---|
| 939 | 911 | |
|---|
| 940 | | -static int imx_vdic_probe(struct platform_device *pdev) |
|---|
| 912 | +struct v4l2_subdev *imx_media_vdic_register(struct v4l2_device *v4l2_dev, |
|---|
| 913 | + struct device *ipu_dev, |
|---|
| 914 | + struct ipu_soc *ipu, |
|---|
| 915 | + u32 grp_id) |
|---|
| 941 | 916 | { |
|---|
| 942 | | - struct imx_media_internal_sd_platformdata *pdata; |
|---|
| 943 | 917 | struct vdic_priv *priv; |
|---|
| 944 | | - int ret; |
|---|
| 918 | + int i, ret; |
|---|
| 945 | 919 | |
|---|
| 946 | | - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); |
|---|
| 920 | + priv = devm_kzalloc(ipu_dev, sizeof(*priv), GFP_KERNEL); |
|---|
| 947 | 921 | if (!priv) |
|---|
| 948 | | - return -ENOMEM; |
|---|
| 922 | + return ERR_PTR(-ENOMEM); |
|---|
| 949 | 923 | |
|---|
| 950 | | - platform_set_drvdata(pdev, &priv->sd); |
|---|
| 951 | | - priv->dev = &pdev->dev; |
|---|
| 952 | | - |
|---|
| 953 | | - pdata = priv->dev->platform_data; |
|---|
| 954 | | - priv->ipu_id = pdata->ipu_id; |
|---|
| 924 | + priv->ipu_dev = ipu_dev; |
|---|
| 925 | + priv->ipu = ipu; |
|---|
| 955 | 926 | |
|---|
| 956 | 927 | v4l2_subdev_init(&priv->sd, &vdic_subdev_ops); |
|---|
| 957 | 928 | v4l2_set_subdevdata(&priv->sd, priv); |
|---|
| 958 | 929 | priv->sd.internal_ops = &vdic_internal_ops; |
|---|
| 959 | 930 | priv->sd.entity.ops = &vdic_entity_ops; |
|---|
| 960 | 931 | priv->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; |
|---|
| 961 | | - priv->sd.dev = &pdev->dev; |
|---|
| 962 | | - priv->sd.owner = THIS_MODULE; |
|---|
| 932 | + priv->sd.owner = ipu_dev->driver->owner; |
|---|
| 963 | 933 | priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; |
|---|
| 964 | | - /* get our group id */ |
|---|
| 965 | | - priv->sd.grp_id = pdata->grp_id; |
|---|
| 966 | | - strncpy(priv->sd.name, pdata->sd_name, sizeof(priv->sd.name)); |
|---|
| 934 | + priv->sd.grp_id = grp_id; |
|---|
| 935 | + imx_media_grp_id_to_sd_name(priv->sd.name, sizeof(priv->sd.name), |
|---|
| 936 | + priv->sd.grp_id, ipu_get_num(ipu)); |
|---|
| 967 | 937 | |
|---|
| 968 | 938 | mutex_init(&priv->lock); |
|---|
| 969 | 939 | |
|---|
| 970 | | - ret = v4l2_async_register_subdev(&priv->sd); |
|---|
| 940 | + for (i = 0; i < VDIC_NUM_PADS; i++) |
|---|
| 941 | + priv->pad[i].flags = (i == VDIC_SRC_PAD_DIRECT) ? |
|---|
| 942 | + MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK; |
|---|
| 943 | + |
|---|
| 944 | + ret = media_entity_pads_init(&priv->sd.entity, VDIC_NUM_PADS, |
|---|
| 945 | + priv->pad); |
|---|
| 971 | 946 | if (ret) |
|---|
| 972 | 947 | goto free; |
|---|
| 973 | 948 | |
|---|
| 974 | | - return 0; |
|---|
| 949 | + ret = v4l2_device_register_subdev(v4l2_dev, &priv->sd); |
|---|
| 950 | + if (ret) |
|---|
| 951 | + goto free; |
|---|
| 952 | + |
|---|
| 953 | + return &priv->sd; |
|---|
| 975 | 954 | free: |
|---|
| 976 | 955 | mutex_destroy(&priv->lock); |
|---|
| 977 | | - return ret; |
|---|
| 956 | + return ERR_PTR(ret); |
|---|
| 978 | 957 | } |
|---|
| 979 | 958 | |
|---|
| 980 | | -static int imx_vdic_remove(struct platform_device *pdev) |
|---|
| 959 | +int imx_media_vdic_unregister(struct v4l2_subdev *sd) |
|---|
| 981 | 960 | { |
|---|
| 982 | | - struct v4l2_subdev *sd = platform_get_drvdata(pdev); |
|---|
| 983 | 961 | struct vdic_priv *priv = v4l2_get_subdevdata(sd); |
|---|
| 984 | 962 | |
|---|
| 985 | 963 | v4l2_info(sd, "Removing\n"); |
|---|
| 986 | 964 | |
|---|
| 987 | | - v4l2_async_unregister_subdev(sd); |
|---|
| 965 | + v4l2_device_unregister_subdev(sd); |
|---|
| 988 | 966 | mutex_destroy(&priv->lock); |
|---|
| 989 | 967 | media_entity_cleanup(&sd->entity); |
|---|
| 990 | 968 | |
|---|
| 991 | 969 | return 0; |
|---|
| 992 | 970 | } |
|---|
| 993 | | - |
|---|
| 994 | | -static const struct platform_device_id imx_vdic_ids[] = { |
|---|
| 995 | | - { .name = "imx-ipuv3-vdic" }, |
|---|
| 996 | | - { }, |
|---|
| 997 | | -}; |
|---|
| 998 | | -MODULE_DEVICE_TABLE(platform, imx_vdic_ids); |
|---|
| 999 | | - |
|---|
| 1000 | | -static struct platform_driver imx_vdic_driver = { |
|---|
| 1001 | | - .probe = imx_vdic_probe, |
|---|
| 1002 | | - .remove = imx_vdic_remove, |
|---|
| 1003 | | - .id_table = imx_vdic_ids, |
|---|
| 1004 | | - .driver = { |
|---|
| 1005 | | - .name = "imx-ipuv3-vdic", |
|---|
| 1006 | | - }, |
|---|
| 1007 | | -}; |
|---|
| 1008 | | -module_platform_driver(imx_vdic_driver); |
|---|
| 1009 | | - |
|---|
| 1010 | | -MODULE_DESCRIPTION("i.MX VDIC subdev driver"); |
|---|
| 1011 | | -MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>"); |
|---|
| 1012 | | -MODULE_LICENSE("GPL"); |
|---|
| 1013 | | -MODULE_ALIAS("platform:imx-ipuv3-vdic"); |
|---|