*/
 static void determine_valid_ioctls(struct video_device *vdev)
 {
+       const u32 vid_caps = V4L2_CAP_VIDEO_CAPTURE |
+                            V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+                            V4L2_CAP_VIDEO_OUTPUT |
+                            V4L2_CAP_VIDEO_OUTPUT_MPLANE |
+                            V4L2_CAP_VIDEO_M2M | V4L2_CAP_VIDEO_M2M_MPLANE;
+       const u32 meta_caps = V4L2_CAP_META_CAPTURE |
+                             V4L2_CAP_META_OUTPUT;
        DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);
        const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops;
-       bool is_vid = vdev->vfl_type == VFL_TYPE_GRABBER;
+       bool is_vid = vdev->vfl_type == VFL_TYPE_GRABBER &&
+                     (vdev->device_caps & vid_caps);
        bool is_vbi = vdev->vfl_type == VFL_TYPE_VBI;
        bool is_radio = vdev->vfl_type == VFL_TYPE_RADIO;
        bool is_sdr = vdev->vfl_type == VFL_TYPE_SDR;
        bool is_tch = vdev->vfl_type == VFL_TYPE_TOUCH;
+       bool is_meta = vdev->vfl_type == VFL_TYPE_GRABBER &&
+                      (vdev->device_caps & meta_caps);
        bool is_rx = vdev->vfl_dir != VFL_DIR_TX;
        bool is_tx = vdev->vfl_dir != VFL_DIR_RX;
 
                set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls);
 
        if (is_vid || is_tch) {
-               /* video and metadata specific ioctls */
+               /* video and touch specific ioctls */
                if ((is_rx && (ops->vidioc_enum_fmt_vid_cap ||
-                              ops->vidioc_enum_fmt_vid_overlay ||
-                              ops->vidioc_enum_fmt_meta_cap)) ||
-                   (is_tx && (ops->vidioc_enum_fmt_vid_out ||
-                              ops->vidioc_enum_fmt_meta_out)))
+                              ops->vidioc_enum_fmt_vid_overlay)) ||
+                   (is_tx && ops->vidioc_enum_fmt_vid_out))
                        set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
                if ((is_rx && (ops->vidioc_g_fmt_vid_cap ||
                               ops->vidioc_g_fmt_vid_cap_mplane ||
-                              ops->vidioc_g_fmt_vid_overlay ||
-                              ops->vidioc_g_fmt_meta_cap)) ||
+                              ops->vidioc_g_fmt_vid_overlay)) ||
                    (is_tx && (ops->vidioc_g_fmt_vid_out ||
                               ops->vidioc_g_fmt_vid_out_mplane ||
-                              ops->vidioc_g_fmt_vid_out_overlay ||
-                              ops->vidioc_g_fmt_meta_out)))
+                              ops->vidioc_g_fmt_vid_out_overlay)))
                         set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
                if ((is_rx && (ops->vidioc_s_fmt_vid_cap ||
                               ops->vidioc_s_fmt_vid_cap_mplane ||
-                              ops->vidioc_s_fmt_vid_overlay ||
-                              ops->vidioc_s_fmt_meta_cap)) ||
+                              ops->vidioc_s_fmt_vid_overlay)) ||
                    (is_tx && (ops->vidioc_s_fmt_vid_out ||
                               ops->vidioc_s_fmt_vid_out_mplane ||
-                              ops->vidioc_s_fmt_vid_out_overlay ||
-                              ops->vidioc_s_fmt_meta_out)))
+                              ops->vidioc_s_fmt_vid_out_overlay)))
                         set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
                if ((is_rx && (ops->vidioc_try_fmt_vid_cap ||
                               ops->vidioc_try_fmt_vid_cap_mplane ||
-                              ops->vidioc_try_fmt_vid_overlay ||
-                              ops->vidioc_try_fmt_meta_cap)) ||
+                              ops->vidioc_try_fmt_vid_overlay)) ||
                    (is_tx && (ops->vidioc_try_fmt_vid_out ||
                               ops->vidioc_try_fmt_vid_out_mplane ||
-                              ops->vidioc_try_fmt_vid_out_overlay ||
-                              ops->vidioc_try_fmt_meta_out)))
+                              ops->vidioc_try_fmt_vid_out_overlay)))
                         set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
                SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay);
                SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
                        set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls);
                SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection);
                SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection);
-       } else if (is_vbi) {
+       }
+       if (is_meta && is_rx) {
+               /* metadata capture specific ioctls */
+               SET_VALID_IOCTL(ops, VIDIOC_ENUM_FMT, vidioc_enum_fmt_meta_cap);
+               SET_VALID_IOCTL(ops, VIDIOC_G_FMT, vidioc_g_fmt_meta_cap);
+               SET_VALID_IOCTL(ops, VIDIOC_S_FMT, vidioc_s_fmt_meta_cap);
+               SET_VALID_IOCTL(ops, VIDIOC_TRY_FMT, vidioc_try_fmt_meta_cap);
+       } else if (is_meta && is_tx) {
+               /* metadata output specific ioctls */
+               SET_VALID_IOCTL(ops, VIDIOC_ENUM_FMT, vidioc_enum_fmt_meta_out);
+               SET_VALID_IOCTL(ops, VIDIOC_G_FMT, vidioc_g_fmt_meta_out);
+               SET_VALID_IOCTL(ops, VIDIOC_S_FMT, vidioc_s_fmt_meta_out);
+               SET_VALID_IOCTL(ops, VIDIOC_TRY_FMT, vidioc_try_fmt_meta_out);
+       }
+       if (is_vbi) {
                /* vbi specific ioctls */
                if ((is_rx && (ops->vidioc_g_fmt_vbi_cap ||
                               ops->vidioc_g_fmt_sliced_vbi_cap)) ||
                        set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
        }
 
-       if (is_vid || is_vbi || is_sdr || is_tch) {
-               /* ioctls valid for video, metadata, vbi or sdr */
+       if (is_vid || is_vbi || is_sdr || is_tch || is_meta) {
+               /* ioctls valid for video, vbi, sdr, touch and metadata */
                SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs);
                SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf);
                SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf);
                SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff);
        }
 
-       if (is_vid || is_vbi || is_tch) {
-               /* ioctls valid for video or vbi */
+       if (is_vid || is_vbi || is_tch || is_meta) {
+               /* ioctls valid for video, vbi, touch and metadata */
                if (ops->vidioc_s_std)
                        set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls);
                SET_VALID_IOCTL(ops, VIDIOC_S_STD, vidioc_s_std);
 
 
 static int check_fmt(struct file *file, enum v4l2_buf_type type)
 {
+       const u32 vid_caps = V4L2_CAP_VIDEO_CAPTURE |
+                            V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+                            V4L2_CAP_VIDEO_OUTPUT |
+                            V4L2_CAP_VIDEO_OUTPUT_MPLANE |
+                            V4L2_CAP_VIDEO_M2M | V4L2_CAP_VIDEO_M2M_MPLANE;
+       const u32 meta_caps = V4L2_CAP_META_CAPTURE |
+                             V4L2_CAP_META_OUTPUT;
        struct video_device *vfd = video_devdata(file);
        const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
-       bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
+       bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER &&
+                     (vfd->device_caps & vid_caps);
        bool is_vbi = vfd->vfl_type == VFL_TYPE_VBI;
        bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
        bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
+       bool is_meta = vfd->vfl_type == VFL_TYPE_GRABBER &&
+                      (vfd->device_caps & meta_caps);
        bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
        bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
 
                        return 0;
                break;
        case V4L2_BUF_TYPE_META_CAPTURE:
-               if (is_vid && is_rx && ops->vidioc_g_fmt_meta_cap)
+               if (is_meta && is_rx && ops->vidioc_g_fmt_meta_cap)
                        return 0;
                break;
        case V4L2_BUF_TYPE_META_OUTPUT:
-               if (is_vid && is_tx && ops->vidioc_g_fmt_meta_out)
+               if (is_meta && is_tx && ops->vidioc_g_fmt_meta_out)
                        return 0;
                break;
        default: