brcm2708: update to latest patches from RPi Foundation
[openwrt/openwrt.git] / target / linux / brcm2708 / patches-4.19 / 950-0729-media-bcm2835-unicam-Rework-to-not-cache-the-list-of.patch
diff --git a/target/linux/brcm2708/patches-4.19/950-0729-media-bcm2835-unicam-Rework-to-not-cache-the-list-of.patch b/target/linux/brcm2708/patches-4.19/950-0729-media-bcm2835-unicam-Rework-to-not-cache-the-list-of.patch
new file mode 100644 (file)
index 0000000..3a63871
--- /dev/null
@@ -0,0 +1,387 @@
+From 2c51b8e533a8b43bde18072c9dbbd0fc5084bbe7 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.org>
+Date: Wed, 2 Oct 2019 17:40:38 +0100
+Subject: [PATCH] media: bcm2835-unicam: Rework to not cache the list
+ of active fmts
+
+Some sensors will change Bayer order based on H & V flips,
+therefore collecting the list of formats at async_bound has
+problems.
+
+Enumerate the formats from the sensor every time.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
+---
+ .../media/platform/bcm2835/bcm2835-unicam.c   | 246 ++++++++++--------
+ 1 file changed, 136 insertions(+), 110 deletions(-)
+
+--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c
++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
+@@ -363,8 +363,6 @@ struct unicam_device {
+       /* Used to store current mbus frame format */
+       struct v4l2_mbus_framefmt m_fmt;
+-      struct unicam_fmt active_fmts[MAX_POSSIBLE_PIX_FMTS];
+-      int num_active_fmt;
+       unsigned int virtual_channel;
+       enum v4l2_mbus_type bus_type;
+       /*
+@@ -455,48 +453,30 @@ static int find_mbus_depth_by_code(u32 c
+       return 0;
+ }
+-static const struct unicam_fmt *find_format_by_code(struct unicam_device *dev,
+-                                                  u32 code)
++static const struct unicam_fmt *find_format_by_code(u32 code)
+ {
+-      const struct unicam_fmt *fmt;
+       unsigned int k;
+-      for (k = 0; k < dev->num_active_fmt; k++) {
+-              fmt = &dev->active_fmts[k];
+-              if (fmt->code == code)
+-                      return fmt;
++      for (k = 0; k < ARRAY_SIZE(formats); k++) {
++              if (formats[k].code == code)
++                      return &formats[k];
+       }
+       return NULL;
+ }
+-static const struct unicam_fmt *find_format_by_pix(struct unicam_device *dev,
+-                                                 u32 pixelformat)
++static const struct unicam_fmt *find_format_by_pix(u32 pixelformat)
+ {
+-      const struct unicam_fmt *fmt;
+       unsigned int k;
+-      for (k = 0; k < dev->num_active_fmt; k++) {
+-              fmt = &dev->active_fmts[k];
+-              if (fmt->fourcc == pixelformat)
+-                      return fmt;
++      for (k = 0; k < ARRAY_SIZE(formats); k++) {
++              if (formats[k].fourcc == pixelformat)
++                      return &formats[k];
+       }
+       return NULL;
+ }
+-static void dump_active_formats(struct unicam_device *dev)
+-{
+-      int i;
+-
+-      for (i = 0; i < dev->num_active_fmt; i++) {
+-              unicam_dbg(3, dev, "active_fmt[%d] (%p) is code %04x, fourcc " V4L2_FOURCC_CONV ", depth %d\n",
+-                         i, &dev->active_fmts[i], dev->active_fmts[i].code,
+-                         V4L2_FOURCC_CONV_ARGS(dev->active_fmts[i].fourcc),
+-                         dev->active_fmts[i].depth);
+-      }
+-}
+-
+ static inline unsigned int bytes_per_line(u32 width,
+                                         const struct unicam_fmt *fmt)
+ {
+@@ -726,14 +706,40 @@ static int unicam_enum_fmt_vid_cap(struc
+                                  struct v4l2_fmtdesc *f)
+ {
+       struct unicam_device *dev = video_drvdata(file);
++      struct v4l2_subdev_mbus_code_enum mbus_code;
+       const struct unicam_fmt *fmt = NULL;
++      int index = 0;
++      int ret = 0;
++      int i;
+-      if (f->index >= dev->num_active_fmt)
+-              return -EINVAL;
++      /* Loop whilst the sensor driver says it has more formats, but add a
++       * failsafe against a dodgy driver at 128 (more than any sensor will
++       * ever sensibly advertise)
++       */
++      for (i = 0; !ret && i < 128 ; i++) {
++              memset(&mbus_code, 0, sizeof(mbus_code));
++              mbus_code.index = i;
+-      fmt = &dev->active_fmts[f->index];
++              ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code,
++                                     NULL, &mbus_code);
++              if (ret < 0) {
++                      unicam_dbg(2, dev,
++                                 "subdev->enum_mbus_code idx %d returned %d - index invalid\n",
++                                 i, ret);
++                      return -EINVAL;
++              }
+-      f->pixelformat = fmt->fourcc;
++              fmt = find_format_by_code(mbus_code.code);
++              if (fmt) {
++                      if (fmt->fourcc) {
++                              if (index == f->index) {
++                                      f->pixelformat = fmt->fourcc;
++                                      break;
++                              }
++                              index++;
++                      }
++              }
++      }
+       return 0;
+ }
+@@ -748,6 +754,39 @@ static int unicam_g_fmt_vid_cap(struct f
+       return 0;
+ }
++static
++const struct unicam_fmt *get_first_supported_format(struct unicam_device *dev)
++{
++      struct v4l2_subdev_mbus_code_enum mbus_code;
++      const struct unicam_fmt *fmt = NULL;
++      int ret;
++      int j;
++
++      for (j = 0; ret != -EINVAL && ret != -ENOIOCTLCMD; ++j) {
++              memset(&mbus_code, 0, sizeof(mbus_code));
++              mbus_code.index = j;
++              ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, NULL,
++                                     &mbus_code);
++              if (ret < 0) {
++                      unicam_dbg(2, dev,
++                                 "subdev->enum_mbus_code idx %d returned %d - continue\n",
++                                 j, ret);
++                      continue;
++              }
++
++              unicam_dbg(2, dev, "subdev %s: code: %04x idx: %d\n",
++                         dev->sensor->name, mbus_code.code, j);
++
++              fmt = find_format_by_code(mbus_code.code);
++              unicam_dbg(2, dev, "fmt %04x returned as %p, V4L2 FOURCC %04x, csi_dt %02X\n",
++                         mbus_code.code, fmt, fmt ? fmt->fourcc : 0,
++                         fmt ? fmt->csi_dt : 0);
++              if (fmt)
++                      return fmt;
++      }
++
++      return NULL;
++}
+ static int unicam_try_fmt_vid_cap(struct file *file, void *priv,
+                                 struct v4l2_format *f)
+ {
+@@ -759,13 +798,15 @@ static int unicam_try_fmt_vid_cap(struct
+       struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+       int ret;
+-      fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat);
++      fmt = find_format_by_pix(f->fmt.pix.pixelformat);
+       if (!fmt) {
+-              unicam_dbg(3, dev, "Fourcc format (0x%08x) not found. Use default of %08X\n",
+-                         f->fmt.pix.pixelformat, dev->active_fmts[0].fourcc);
++              /* Pixel format not supported by unicam. Choose the first
++               * supported format, and let the sensor choose something else.
++               */
++              unicam_dbg(3, dev, "Fourcc format (0x%08x) not found. Use first format.\n",
++                         f->fmt.pix.pixelformat);
+-              /* Just get the first one enumerated */
+-              fmt = &dev->active_fmts[0];
++              fmt = &formats[0];
+               f->fmt.pix.pixelformat = fmt->fourcc;
+       }
+@@ -785,6 +826,40 @@ static int unicam_try_fmt_vid_cap(struct
+               unicam_info(dev, "Sensor trying to send interlaced video - results may be unpredictable\n");
+       v4l2_fill_pix_format(&f->fmt.pix, &sd_fmt.format);
++      if (mbus_fmt->code != fmt->code) {
++              /* Sensor has returned an alternate format */
++              fmt = find_format_by_code(mbus_fmt->code);
++              if (!fmt) {
++                      /* The alternate format is one unicam can't support.
++                       * Find the first format that is supported by both, and
++                       * then set that.
++                       */
++                      fmt = get_first_supported_format(dev);
++                      mbus_fmt->code = fmt->code;
++
++                      ret = v4l2_subdev_call(dev->sensor, pad, set_fmt,
++                                             dev->sensor_config, &sd_fmt);
++                      if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
++                              return ret;
++
++                      if (mbus_fmt->field != V4L2_FIELD_NONE)
++                              unicam_info(dev, "Sensor trying to send interlaced video - results may be unpredictable\n");
++
++                      v4l2_fill_pix_format(&f->fmt.pix, &sd_fmt.format);
++
++                      if (mbus_fmt->code != fmt->code) {
++                              /* We've set a format that the sensor reports
++                               * as being supported, but it refuses to set it.
++                               * Not much else we can do.
++                               * Assume that the sensor driver may accept the
++                               * format when it is set (rather than tried).
++                               */
++                              unicam_err(dev, "Sensor won't accept default format, and Unicam can't support sensor default\n");
++                      }
++              }
++
++              f->fmt.pix.pixelformat = fmt->fourcc;
++      }
+       return unicam_calc_format_size_bpl(dev, fmt, f);
+ }
+@@ -805,10 +880,18 @@ static int unicam_s_fmt_vid_cap(struct f
+       if (ret < 0)
+               return ret;
+-      fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat);
++      fmt = find_format_by_pix(f->fmt.pix.pixelformat);
+       if (!fmt) {
+-              /* Unknown pixel format - adopt a default */
+-              fmt = &dev->active_fmts[0];
++              /* Unknown pixel format - adopt a default.
++               * This shouldn't happen as try_fmt should have resolved any
++               * issues first.
++               */
++              fmt = get_first_supported_format(dev);
++              if (!fmt)
++                      /* It shouldn't be possible to get here with no
++                       * supported formats
++                       */
++                      return -EINVAL;
+               f->fmt.pix.pixelformat = fmt->fourcc;
+               return -EINVAL;
+       }
+@@ -944,6 +1027,7 @@ static void unicam_set_packing_config(st
+                       unpack = UNICAM_PUM_NONE;
+                       break;
+               }
++
+               switch (v4l2_depth) {
+               case 8:
+                       pack = UNICAM_PPM_PACK8;
+@@ -1439,7 +1523,7 @@ static int unicam_enum_framesizes(struct
+       int ret;
+       /* check for valid format */
+-      fmt = find_format_by_pix(dev, fsize->pixel_format);
++      fmt = find_format_by_pix(fsize->pixel_format);
+       if (!fmt) {
+               unicam_dbg(3, dev, "Invalid pixel code: %x\n",
+                          fsize->pixel_format);
+@@ -1478,7 +1562,7 @@ static int unicam_enum_frameintervals(st
+       };
+       int ret;
+-      fmt = find_format_by_pix(dev, fival->pixel_format);
++      fmt = find_format_by_pix(fival->pixel_format);
+       if (!fmt)
+               return -EINVAL;
+@@ -1742,27 +1826,6 @@ static const struct v4l2_ioctl_ops unica
+       .vidioc_unsubscribe_event       = v4l2_event_unsubscribe,
+ };
+-/*
+- * Adds an entry to the active_fmts array
+- * Returns non-zero if attempting to write off the end of the array.
+- */
+-static int unicam_add_active_format(struct unicam_device *unicam,
+-                                  const struct unicam_fmt *fmt)
+-{
+-      //Ensure we don't run off the end of the array.
+-      if (unicam->num_active_fmt >= MAX_POSSIBLE_PIX_FMTS)
+-              return 1;
+-
+-      unicam->active_fmts[unicam->num_active_fmt] = *fmt;
+-      unicam_dbg(2, unicam,
+-                 "matched fourcc: " V4L2_FOURCC_CONV ": code: %04x idx: %d\n",
+-                 V4L2_FOURCC_CONV_ARGS(fmt->fourcc),
+-                 fmt->code, unicam->num_active_fmt);
+-      unicam->num_active_fmt++;
+-
+-      return 0;
+-}
+-
+ static int
+ unicam_async_bound(struct v4l2_async_notifier *notifier,
+                  struct v4l2_subdev *subdev,
+@@ -1770,9 +1833,6 @@ unicam_async_bound(struct v4l2_async_not
+ {
+       struct unicam_device *unicam = container_of(notifier->v4l2_dev,
+                                              struct unicam_device, v4l2_dev);
+-      struct v4l2_subdev_mbus_code_enum mbus_code;
+-      int ret = 0;
+-      int j;
+       if (unicam->sensor) {
+               unicam_info(unicam, "Rejecting subdev %s (Already set!!)",
+@@ -1783,47 +1843,6 @@ unicam_async_bound(struct v4l2_async_not
+       unicam->sensor = subdev;
+       unicam_dbg(1, unicam, "Using sensor %s for capture\n", subdev->name);
+-      /* Enumerate sub device formats and enable all matching local formats */
+-      unicam->num_active_fmt = 0;
+-      unicam_dbg(2, unicam, "Get supported formats...\n");
+-      for (j = 0; ret != -EINVAL && ret != -ENOIOCTLCMD; ++j) {
+-              const struct unicam_fmt *fmt = NULL;
+-              int k;
+-
+-              memset(&mbus_code, 0, sizeof(mbus_code));
+-              mbus_code.index = j;
+-              ret = v4l2_subdev_call(subdev, pad, enum_mbus_code,
+-                                     NULL, &mbus_code);
+-              if (ret < 0) {
+-                      unicam_dbg(2, unicam,
+-                                 "subdev->enum_mbus_code idx %d returned %d - continue\n",
+-                                 j, ret);
+-                      continue;
+-              }
+-
+-              unicam_dbg(2, unicam, "subdev %s: code: %04x idx: %d\n",
+-                         subdev->name, mbus_code.code, j);
+-
+-              for (k = 0; k < ARRAY_SIZE(formats); k++) {
+-                      if (mbus_code.code == formats[k].code) {
+-                              fmt = &formats[k];
+-                              break;
+-                      }
+-              }
+-              unicam_dbg(2, unicam, "fmt %04x returned as %p, V4L2 FOURCC %04x, csi_dt %02X\n",
+-                         mbus_code.code, fmt, fmt ? fmt->fourcc : 0,
+-                         fmt ? fmt->csi_dt : 0);
+-              if (fmt) {
+-                      if (unicam_add_active_format(unicam, fmt)) {
+-                              unicam_dbg(1, unicam, "Active fmt list truncated\n");
+-                              break;
+-                      }
+-              }
+-      }
+-      unicam_dbg(2, unicam,
+-                 "Done all formats\n");
+-      dump_active_formats(unicam);
+-
+       return 0;
+ }
+@@ -1849,10 +1868,17 @@ static int unicam_probe_complete(struct
+               return ret;
+       }
+-      fmt = find_format_by_code(unicam, mbus_fmt.code);
++      fmt = find_format_by_code(mbus_fmt.code);
+       if (!fmt) {
+-              /* Default image format not valid. Choose first active fmt. */
+-              fmt = &unicam->active_fmts[0];
++              /* Find the first format that the sensor and unicam both
++               * support
++               */
++              fmt = get_first_supported_format(unicam);
++
++              if (!fmt)
++                      /* No compatible formats */
++                      return -EINVAL;
++
+               mbus_fmt.code = fmt->code;
+               ret = __subdev_set_format(unicam, &mbus_fmt);
+               if (ret)