static int v4l_g_crop(const struct v4l2_ioctl_ops *ops,
                                struct file *file, void *fh, void *arg)
 {
+       struct video_device *vfd = video_devdata(file);
        struct v4l2_crop *p = arg;
        struct v4l2_selection s = {
                .type = p->type,
        else
                s.target = V4L2_SEL_TGT_CROP;
 
+       if (test_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags))
+               s.target = s.target == V4L2_SEL_TGT_COMPOSE ?
+                       V4L2_SEL_TGT_CROP : V4L2_SEL_TGT_COMPOSE;
+
        ret = v4l_g_selection(ops, file, fh, &s);
 
        /* copying results to old structure on success */
 static int v4l_s_crop(const struct v4l2_ioctl_ops *ops,
                                struct file *file, void *fh, void *arg)
 {
+       struct video_device *vfd = video_devdata(file);
        struct v4l2_crop *p = arg;
        struct v4l2_selection s = {
                .type = p->type,
        else
                s.target = V4L2_SEL_TGT_CROP;
 
+       if (test_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags))
+               s.target = s.target == V4L2_SEL_TGT_COMPOSE ?
+                       V4L2_SEL_TGT_CROP : V4L2_SEL_TGT_COMPOSE;
+
        return v4l_s_selection(ops, file, fh, &s);
 }
 
 static int v4l_cropcap(const struct v4l2_ioctl_ops *ops,
                                struct file *file, void *fh, void *arg)
 {
+       struct video_device *vfd = video_devdata(file);
        struct v4l2_cropcap *p = arg;
        struct v4l2_selection s = { .type = p->type };
        int ret = 0;
        else
                s.target = V4L2_SEL_TGT_CROP_BOUNDS;
 
+       if (test_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags))
+               s.target = s.target == V4L2_SEL_TGT_COMPOSE_BOUNDS ?
+                       V4L2_SEL_TGT_CROP_BOUNDS : V4L2_SEL_TGT_COMPOSE_BOUNDS;
+
        ret = v4l_g_selection(ops, file, fh, &s);
        if (ret)
                return ret;
        p->bounds = s.r;
 
        /* obtaining defrect */
-       if (V4L2_TYPE_IS_OUTPUT(p->type))
+       if (s.target == V4L2_SEL_TGT_COMPOSE_BOUNDS)
                s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT;
        else
                s.target = V4L2_SEL_TGT_CROP_DEFAULT;
 
  *     indicates that file->private_data points to &struct v4l2_fh.
  *     This flag is set by the core when v4l2_fh_init() is called.
  *     All new drivers should use it.
+ * @V4L2_FL_QUIRK_INVERTED_CROP:
+ *     some old M2M drivers use g/s_crop/cropcap incorrectly: crop and
+ *     compose are swapped. If this flag is set, then the selection
+ *     targets are swapped in the g/s_crop/cropcap functions in v4l2-ioctl.c.
+ *     This allows those drivers to correctly implement the selection API,
+ *     but the old crop API will still work as expected in order to preserve
+ *     backwards compatibility.
+ *     Never set this flag for new drivers.
  */
 enum v4l2_video_device_flags {
-       V4L2_FL_REGISTERED      = 0,
-       V4L2_FL_USES_V4L2_FH    = 1,
+       V4L2_FL_REGISTERED              = 0,
+       V4L2_FL_USES_V4L2_FH            = 1,
+       V4L2_FL_QUIRK_INVERTED_CROP     = 2,
 };
 
 /* Priority helper functions */