[PATCH 1/6] staging: Import the BCM2835 MMAL-based V4L2 camera driver.
Michael Zoran
mzoran at crowfest.net
Sun Feb 5 15:13:44 PST 2017
Dave: I'd personally like to see the current version of vchi stabbed,
poisoned, beaten, shot, chained and thrown in a river.:)
Would it be possible to find another transport for this driver to
remove the dependency on VCHI? Given the process of improving drivers
in stagging, I don't expect VCHI to ever make it out of staging in it's
current form either. One possibility is mbox, but it has the limitation
of one one request at a time.
Also, you mention protecting IP and that V4L expects everything to be
exposed. How does V4L2 work if I have a hybrid software/hardware video
capture device?
In the case of other drivers in linux that upload a large firmware blob
into who knows where, how does the open source part of the driver hook
into the firmware blob?
On Sun, 2017-02-05 at 22:15 +0000, Dave Stevenson wrote:
> Hi Mauro.
>
> I'm going to stick my head above the parapet as one of the original
> authors back when I worked at Broadcom.
> As it happens I started working at Raspberry Pi last Monday, so that
> puts me in a place where I can work on this again a bit more. (The
> last
> two years have been just a spare time support role).
> Whilst I have done kernel development work in various roles, it's
> all
> been downstream so I've not been that active on these lists before.
>
> All formatting/checkpatch comments noted.
> Checkpatch was whinging when this was first written around December
> 2013
> about long lines, so many got broken up to shut it up. Views on code
> style and checkpatch seem to have changed a little since then.
> I thought we had made checkpatch happy before the driver was pushed,
> but
> with some of the comments still having // style I guess some slipped
> through the net.
> Yes chunks of this could do with refactoring to reduce the levels of
> indentation - always more to do.
> If I've removed any formatting/style type comments in my cuts it's
> not
> because I'm ignoring them, just that they're not something that
> needs
> discussion (just fixing). I've only taken out the really big lumps
> of
> code with no comments on.
>
> Newbie question: if this has already been merged to staging, where am
> I
> looking for the relevant tree to add patches on top of?
> git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
> branch
> staging-next?
>
> Responses to the rest inline.
> TL;DR answer is that you are seeing the top edge of a full ISP
> processing pipe and optional encoders running on the GPU, mainly as
> there are blocks that can't be exposed for IP reasons (Raspberry Pi
> only
> being the customer not silicon vendor constrains what can and can't
> be
> made public).
> That doesn't seem to fit very well into V4L2 which expects that it
> can
> see all the detail, so there are a few nasty spots to shoe-horn it
> in.
> If there are better ways to solve the problems, then I'm open to
> them.
>
> Thanks
> Dave
>
>
> On 03/02/17 18:59, Mauro Carvalho Chehab wrote:
> > HI Eric,
> >
> > Em Fri, 27 Jan 2017 13:54:58 -0800
> > Eric Anholt <eric at anholt.net> escreveu:
> >
> > > - Supports raw YUV capture, preview, JPEG and H264.
> > > - Uses videobuf2 for data transfer, using dma_buf.
> > > - Uses 3.6.10 timestamping
> > > - Camera power based on use
> > > - Uses immutable input mode on video encoder
> > >
> > > This code comes from the Raspberry Pi kernel tree (rpi-4.9.y) as
> > > of
> > > a15ba877dab4e61ea3fc7b006e2a73828b083c52.
> >
> > First of all, thanks for that! Having an upstream driver for the
> > RPi camera is something that has been long waited!
> >
> > Greg was kick on merging it on staging ;) Anyway, the real review
> > will happen when the driver becomes ready to be promoted out of
> > staging. When you address the existing issues and get it ready to
> > merge, please send the patch with such changes to linux-media ML.
> > I'll do a full review on it by then.
>
> Is that even likely given the dependence on VCHI? I wasn't expecting
> VCHI to leave staging, which would force this to remain too.
>
> > Still, let me do a quick review on this driver, specially at the
> > non-MMAL code.
> >
> > >
> > > Signed-off-by: Eric Anholt <eric at anholt.net>
> > > ---
> > > .../media/platform/bcm2835/bcm2835-camera.c | 2016
> > > ++++++++++++++++++++
> > > .../media/platform/bcm2835/bcm2835-camera.h | 145 ++
> > > drivers/staging/media/platform/bcm2835/controls.c | 1345
> > > +++++++++++++
> > > .../staging/media/platform/bcm2835/mmal-common.h | 53 +
> > > .../media/platform/bcm2835/mmal-encodings.h | 127 ++
> > > .../media/platform/bcm2835/mmal-msg-common.h | 50 +
> > > .../media/platform/bcm2835/mmal-msg-format.h | 81 +
> > > .../staging/media/platform/bcm2835/mmal-msg-port.h | 107 ++
> > > drivers/staging/media/platform/bcm2835/mmal-msg.h | 404 ++++
> > > .../media/platform/bcm2835/mmal-parameters.h | 689
> > > +++++++
> > > .../staging/media/platform/bcm2835/mmal-vchiq.c | 1916
> > > +++++++++++++++++++
> > > .../staging/media/platform/bcm2835/mmal-vchiq.h | 178 ++
> > > 12 files changed, 7111 insertions(+)
> > > create mode 100644
> > > drivers/staging/media/platform/bcm2835/bcm2835-camera.c
> > > create mode 100644
> > > drivers/staging/media/platform/bcm2835/bcm2835-camera.h
> > > create mode 100644
> > > drivers/staging/media/platform/bcm2835/controls.c
> > > create mode 100644 drivers/staging/media/platform/bcm2835/mmal-
> > > common.h
> > > create mode 100644 drivers/staging/media/platform/bcm2835/mmal-
> > > encodings.h
> > > create mode 100644 drivers/staging/media/platform/bcm2835/mmal-
> > > msg-common.h
> > > create mode 100644 drivers/staging/media/platform/bcm2835/mmal-
> > > msg-format.h
> > > create mode 100644 drivers/staging/media/platform/bcm2835/mmal-
> > > msg-port.h
> > > create mode 100644 drivers/staging/media/platform/bcm2835/mmal-
> > > msg.h
> > > create mode 100644 drivers/staging/media/platform/bcm2835/mmal-
> > > parameters.h
> > > create mode 100644 drivers/staging/media/platform/bcm2835/mmal-
> > > vchiq.c
> > > create mode 100644 drivers/staging/media/platform/bcm2835/mmal-
> > > vchiq.h
> > >
> > > diff --git a/drivers/staging/media/platform/bcm2835/bcm2835-
> > > camera.c b/drivers/staging/media/platform/bcm2835/bcm2835-
> > > camera.c
> > > new file mode 100644
> > > index 000000000000..4f03949aecf3
> > > --- /dev/null
> > > +++ b/drivers/staging/media/platform/bcm2835/bcm2835-camera.c
> > > @@ -0,0 +1,2016 @@
> > > +/*
> > > + * Broadcom BM2835 V4L2 driver
> > > + *
> > > + * Copyright © 2013 Raspberry Pi (Trading) Ltd.
> > > + *
> > > + * This file is subject to the terms and conditions of the GNU
> > > General Public
> > > + * License. See the file COPYING in the main directory of this
> > > archive
> > > + * for more details.
> > > + *
> > > + * Authors: Vincent Sanders <vincent.sanders at collabora.co.uk>
> > > + * Dave Stevenson <dsteve at broadcom.com>
> > > + * Simon Mellor <simellor at broadcom.com>
> > > + * Luke Diamand <luked at broadcom.com>
>
> All of these are now dead email addresses.
> Mine could be updated to dave.stevenson at raspberrypi.org, but the
> others
> should probably be deleted.
>
> > > + */
> > > +
> > > +#include <linux/errno.h>
> > > +#include <linux/kernel.h>
> > > +#include <linux/module.h>
> > > +#include <linux/slab.h>
> > > +#include <media/videobuf2-vmalloc.h>
> > > +#include <media/videobuf2-dma-contig.h>
> > > +#include <media/v4l2-device.h>
> > > +#include <media/v4l2-ioctl.h>
> > > +#include <media/v4l2-ctrls.h>
> > > +#include <media/v4l2-fh.h>
> > > +#include <media/v4l2-event.h>
> > > +#include <media/v4l2-common.h>
> > > +#include <linux/delay.h>
> > > +
> > > +#include "mmal-common.h"
> > > +#include "mmal-encodings.h"
> > > +#include "mmal-vchiq.h"
> > > +#include "mmal-msg.h"
> > > +#include "mmal-parameters.h"
> > > +#include "bcm2835-camera.h"
> > > +
> > > +#define BM2835_MMAL_VERSION "0.0.2"
> > > +#define BM2835_MMAL_MODULE_NAME "bcm2835-v4l2"
> > > +#define MIN_WIDTH 32
> > > +#define MIN_HEIGHT 32
> > > +#define MIN_BUFFER_SIZE (80*1024)
> > > +
> > > +#define MAX_VIDEO_MODE_WIDTH 1280
> > > +#define MAX_VIDEO_MODE_HEIGHT 720
> >
> > Hmm... Doesn't the max resolution depend on the sensor?
> >
> > > +
> > > +#define MAX_BCM2835_CAMERAS 2
> > > +
> > > +MODULE_DESCRIPTION("Broadcom 2835 MMAL video capture");
> > > +MODULE_AUTHOR("Vincent Sanders");
> > > +MODULE_LICENSE("GPL");
> > > +MODULE_VERSION(BM2835_MMAL_VERSION);
> > > +
> > > +int bcm2835_v4l2_debug;
> > > +module_param_named(debug, bcm2835_v4l2_debug, int, 0644);
> > > +MODULE_PARM_DESC(bcm2835_v4l2_debug, "Debug level 0-2");
> > > +
> > > +#define UNSET (-1)
> > > +static int video_nr[] = {[0 ... (MAX_BCM2835_CAMERAS - 1)] =
> > > UNSET };
> > > +module_param_array(video_nr, int, NULL, 0644);
> > > +MODULE_PARM_DESC(video_nr, "videoX start numbers, -1 is
> > > autodetect");
> > > +
> > > +static int max_video_width = MAX_VIDEO_MODE_WIDTH;
> > > +static int max_video_height = MAX_VIDEO_MODE_HEIGHT;
> > > +module_param(max_video_width, int, S_IRUSR | S_IWUSR | S_IRGRP |
> > > S_IROTH);
> > > +MODULE_PARM_DESC(max_video_width, "Threshold for video mode");
> > > +module_param(max_video_height, int, S_IRUSR | S_IWUSR | S_IRGRP
> > > | S_IROTH);
> > > +MODULE_PARM_DESC(max_video_height, "Threshold for video mode");
> >
> > That seems a terrible hack! let the user specify the resolution via
> > modprobe parameter... That should depend on the hardware
> > capabilities
> > instead.
>
> This is sitting on top of an OpenMaxIL style camera component
> (though
> accessed via MMAL - long story, but basically MMAL removed a bundle
> of
> the ugly/annoying parts of IL).
> It has the extension above V1.1.2 that you have a preview port,
> video
> capture port, and stills capture port. Stills captures have
> additional
> processing stages to improve image quality, whilst video has to
> maintain
> framerate.
>
> If you're asked for YUV or RGB frame, how do you choose between video
> or
> stills? That's what is being set with these parameters, not the
> sensor
> resolution. Having independent stills and video processing options
> doesn't appear to be something that is supported in V4L2, but I'm
> open
> to suggestions.
> There were thoughts that they could be exposed as different
> /dev/videoN
> devices, but that then poses a quandry to the client app as to which
> node to open, so complicates the client significantly. On the plus
> side
> it would then allow for things like zero shutter lag captures, and
> stills during video, where you want multiple streams (apparently)
> simultaneously, but is that worth the complexity? The general view
> was no.
>
> > > +
> > > +/* Gstreamer bug https://bugzilla.gnome.org/show_bug.cgi?id=7265
> > > 21
> > > + * v4l2src does bad (and actually wrong) things when the
> > > vidioc_enum_framesizes
> > > + * function says type V4L2_FRMSIZE_TYPE_STEPWISE, which we do by
> > > default.
> > > + * It's happier if we just don't say anything at all, when it
> > > then
> > > + * sets up a load of defaults that it thinks might work.
> > > + * If gst_v4l2src_is_broken is non-zero, then we remove the
> > > function from
> > > + * our function table list (actually switch to an alternate set,
> > > but same
> > > + * result).
> > > + */
> > > +static int gst_v4l2src_is_broken;
> > > +module_param(gst_v4l2src_is_broken, int, S_IRUSR | S_IWUSR |
> > > S_IRGRP | S_IROTH);
> > > +MODULE_PARM_DESC(gst_v4l2src_is_broken, "If non-zero, enable
> > > workaround for Gstreamer");
> >
> > Not sure if I liked this hack here. AFAIKT, GStreamer fixed the bug
> > with
> > V4L2_FRMSIZE_TYPE_STEPWISE already.
>
> I will double check on Monday. The main Raspberry Pi distribution is
> based on Debian, so packages can be quite out of date. This bug
> certainly affected Wheezy, but I don't know for certain about
> Jessie.
> Sid still hasn't been adopted.
>
> Also be aware that exactly the same issue of not supporting
> V4L2_FRMSIZE_TYPE_STEPWISE affects Chromium for WebRTC, and they
> seem
> not to be too bothered about fixing it -
> https://bugs.chromium.org/p/chromium/issues/detail?id=249953
> Now admittedly it's not the kernel's responsibility to work around
> application issues, but if it hobbles a board then that is an issue.
>
> > > +
> > > +/* global device data array */
> > > +static struct bm2835_mmal_dev *gdev[MAX_BCM2835_CAMERAS];
> > > +
> > > +#define FPS_MIN 1
> > > +#define FPS_MAX 90
> > > +
> > > +/* timeperframe: min/max and default */
> > > +static const struct v4l2_fract
> > > + tpf_min = {.numerator = 1, .denominat
> > > or = FPS_MAX},
> > > + tpf_max = {.numerator = 1, .denominat
> > > or = FPS_MIN},
> > > + tpf_default = {.numerator = 1000, .denominator =
> > > 30000};
> > > +
> > > +/* video formats */
> > > +static struct mmal_fmt formats[] = {
> > > + {
> > > + .name = "4:2:0, planar, YUV",
> > > + .fourcc = V4L2_PIX_FMT_YUV420,
> > > + .flags = 0,
> > > + .mmal = MMAL_ENCODING_I420,
> > > + .depth = 12,
> > > + .mmal_component = MMAL_COMPONENT_CAMERA,
> > > + .ybbp = 1,
> >
> > Alignment here should be two tabs, instead.
> >
> > > + },
> > > + {
> >
> > I prefer if you use, instead:
> >
> > }, {
> >
> > > + .name = "4:2:2, packed, YUYV",
> > > + .fourcc = V4L2_PIX_FMT_YUYV,
> > > + .flags = 0,
> > > + .mmal = MMAL_ENCODING_YUYV,
> > > + .depth = 16,
> > > + .mmal_component = MMAL_COMPONENT_CAMERA,
> > > + .ybbp = 2,
> > > + },
> > > + {
> > > + .name = "RGB24 (LE)",
> > > + .fourcc = V4L2_PIX_FMT_RGB24,
> > > + .flags = 0,
> > > + .mmal = MMAL_ENCODING_RGB24,
> > > + .depth = 24,
> > > + .mmal_component = MMAL_COMPONENT_CAMERA,
> > > + .ybbp = 3,
> > > + },
> > > + {
> > > + .name = "JPEG",
> > > + .fourcc = V4L2_PIX_FMT_JPEG,
> > > + .flags = V4L2_FMT_FLAG_COMPRESSED,
> > > + .mmal = MMAL_ENCODING_JPEG,
> > > + .depth = 8,
> > > + .mmal_component = MMAL_COMPONENT_IMAGE_ENCODE,
> > > + .ybbp = 0,
> > > + },
> > > + {
> > > + .name = "H264",
> > > + .fourcc = V4L2_PIX_FMT_H264,
> > > + .flags = V4L2_FMT_FLAG_COMPRESSED,
> > > + .mmal = MMAL_ENCODING_H264,
> > > + .depth = 8,
> > > + .mmal_component = MMAL_COMPONENT_VIDEO_ENCODE,
> > > + .ybbp = 0,
> > > + },
> > > + {
> > > + .name = "MJPEG",
> > > + .fourcc = V4L2_PIX_FMT_MJPEG,
> > > + .flags = V4L2_FMT_FLAG_COMPRESSED,
> > > + .mmal = MMAL_ENCODING_MJPEG,
> > > + .depth = 8,
> > > + .mmal_component = MMAL_COMPONENT_VIDEO_ENCODE,
> > > + .ybbp = 0,
> > > + },
> > > + {
> > > + .name = "4:2:2, packed, YVYU",
> > > + .fourcc = V4L2_PIX_FMT_YVYU,
> > > + .flags = 0,
> > > + .mmal = MMAL_ENCODING_YVYU,
> > > + .depth = 16,
> > > + .mmal_component = MMAL_COMPONENT_CAMERA,
> > > + .ybbp = 2,
> > > + },
> > > + {
> > > + .name = "4:2:2, packed, VYUY",
> > > + .fourcc = V4L2_PIX_FMT_VYUY,
> > > + .flags = 0,
> > > + .mmal = MMAL_ENCODING_VYUY,
> > > + .depth = 16,
> > > + .mmal_component = MMAL_COMPONENT_CAMERA,
> > > + .ybbp = 2,
> > > + },
> > > + {
> > > + .name = "4:2:2, packed, UYVY",
> > > + .fourcc = V4L2_PIX_FMT_UYVY,
> > > + .flags = 0,
> > > + .mmal = MMAL_ENCODING_UYVY,
> > > + .depth = 16,
> > > + .mmal_component = MMAL_COMPONENT_CAMERA,
> > > + .ybbp = 2,
> > > + },
> > > + {
> > > + .name = "4:2:0, planar, NV12",
> > > + .fourcc = V4L2_PIX_FMT_NV12,
> > > + .flags = 0,
> > > + .mmal = MMAL_ENCODING_NV12,
> > > + .depth = 12,
> > > + .mmal_component = MMAL_COMPONENT_CAMERA,
> > > + .ybbp = 1,
> > > + },
> > > + {
> > > + .name = "RGB24 (BE)",
> > > + .fourcc = V4L2_PIX_FMT_BGR24,
> > > + .flags = 0,
> > > + .mmal = MMAL_ENCODING_BGR24,
> > > + .depth = 24,
> > > + .mmal_component = MMAL_COMPONENT_CAMERA,
> > > + .ybbp = 3,
> > > + },
> > > + {
> > > + .name = "4:2:0, planar, YVU",
> > > + .fourcc = V4L2_PIX_FMT_YVU420,
> > > + .flags = 0,
> > > + .mmal = MMAL_ENCODING_YV12,
> > > + .depth = 12,
> > > + .mmal_component = MMAL_COMPONENT_CAMERA,
> > > + .ybbp = 1,
> > > + },
> > > + {
> > > + .name = "4:2:0, planar, NV21",
> > > + .fourcc = V4L2_PIX_FMT_NV21,
> > > + .flags = 0,
> > > + .mmal = MMAL_ENCODING_NV21,
> > > + .depth = 12,
> > > + .mmal_component = MMAL_COMPONENT_CAMERA,
> > > + .ybbp = 1,
> > > + },
> > > + {
> > > + .name = "RGB32 (BE)",
> > > + .fourcc = V4L2_PIX_FMT_BGR32,
> > > + .flags = 0,
> > > + .mmal = MMAL_ENCODING_BGRA,
> > > + .depth = 32,
> > > + .mmal_component = MMAL_COMPONENT_CAMERA,
> > > + .ybbp = 4,
> > > + },
> > > +};
> > > +
> > > +static struct mmal_fmt *get_format(struct v4l2_format *f)
> > > +{
> > > + struct mmal_fmt *fmt;
> > > + unsigned int k;
> > > +
> > > + for (k = 0; k < ARRAY_SIZE(formats); k++) {
> > > + fmt = &formats[k];
> > > + if (fmt->fourcc == f->fmt.pix.pixelformat)
> > > + break;
> > > + }
> > > +
> > > + if (k == ARRAY_SIZE(formats))
> > > + return NULL;
> >
> > Again, doesn't the formats depend on the camera sensor module?
>
> Not in this case.
> You're at the end of a full ISP processing pipe, and there is the
> option
> for including either JPEG, MJPEG, or H264 encoding on the end. It is
> supported to ask the camera component which formats it supports, but
> you'll still need a conversion table from those MMAL types to V4L2
> enums, and options for adding the encoded formats.
>
> > > +
> > > + return &formats[k];
> > > +}
> > > +
> > > +/* -----------------------------------------------------------
> > > -------
> > > + Videobuf queue operations
> > > + -------------------------------------------------------------
> > > -----*/
> > > +
> > > +static int queue_setup(struct vb2_queue *vq,
> > > + unsigned int *nbuffers, unsigned int
> > > *nplanes,
> > > + unsigned int sizes[], struct device
> > > *alloc_ctxs[])
> > > +{
> > > + struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq);
> > > + unsigned long size;
> > > +
> > > + /* refuse queue setup if port is not configured */
> > > + if (dev->capture.port == NULL) {
> > > + v4l2_err(&dev->v4l2_dev,
> > > + "%s: capture port not configured\n",
> > > __func__);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + size = dev->capture.port->current_buffer.size;
> > > + if (size == 0) {
> > > + v4l2_err(&dev->v4l2_dev,
> > > + "%s: capture port buffer size is
> > > zero\n", __func__);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + if (*nbuffers < (dev->capture.port->current_buffer.num +
> > > 2))
> > > + *nbuffers = (dev->capture.port-
> > > >current_buffer.num + 2);
> > > +
> > > + *nplanes = 1;
> > > +
> > > + sizes[0] = size;
> > > +
> > > + /*
> > > + * videobuf2-vmalloc allocator is context-less so no
> > > need to set
> > > + * alloc_ctxs array.
> > > + */
> > > +
> > > + v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s:
> > > dev:%p\n",
> > > + __func__, dev);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int buffer_prepare(struct vb2_buffer *vb)
> > > +{
> > > + struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vb-
> > > >vb2_queue);
> > > + unsigned long size;
> > > +
> > > + v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s:
> > > dev:%p\n",
> > > + __func__, dev);
> > > +
> > > + BUG_ON(dev->capture.port == NULL);
> > > + BUG_ON(dev->capture.fmt == NULL);
> >
> > Please don't use BUG()/BUG_ON(), except if the driver would be
> > doing
> > something wrong enough to justify crashing the Kernel. That's not
> > the case here. Instead, returning -ENODEV should be enough.
> >
> > > +
> > > + size = dev->capture.stride * dev->capture.height;
> > > + if (vb2_plane_size(vb, 0) < size) {
> > > + v4l2_err(&dev->v4l2_dev,
> > > + "%s data will not fit into plane (%lu <
> > > %lu)\n",
> > > + __func__, vb2_plane_size(vb, 0), size);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static inline bool is_capturing(struct bm2835_mmal_dev *dev)
> > > +{
> > > + return dev->capture.camera_port ==
> > > + &dev->
> > > + component[MMAL_COMPONENT_CAMERA]-
> > > >output[MMAL_CAMERA_PORT_CAPTURE];
> >
> > Weird indentation. Just merge everything on a single line.
> >
> >
> > > +}
> > > +
> > > +static void buffer_cb(struct vchiq_mmal_instance *instance,
> > > + struct vchiq_mmal_port *port,
> > > + int status,
> > > + struct mmal_buffer *buf,
> > > + unsigned long length, u32 mmal_flags, s64
> > > dts, s64 pts)
> > > +{
> > > + struct bm2835_mmal_dev *dev = port->cb_ctx;
> > > +
> > > + v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev,
> > > + "%s: status:%d, buf:%p, length:%lu, flags %u,
> > > pts %lld\n",
> > > + __func__, status, buf, length, mmal_flags,
> > > pts);
> > > +
> > > + if (status != 0) {
> > > + /* error in transfer */
> > > + if (buf != NULL) {
> > > + /* there was a buffer with the error so
> > > return it */
> > > + vb2_buffer_done(&buf->vb.vb2_buf,
> > > VB2_BUF_STATE_ERROR);
> > > + }
> > > + return;
> > > + } else if (length == 0) {
> >
> > Doesn't need an else above. That would remove one indentation
> > level,
> > with is a good thing.
> >
> > > + /* stream ended */
> > > + if (buf != NULL) {
> > > + /* this should only ever happen if the
> > > port is
> > > + * disabled and there are buffers still
> > > queued
> > > + */
> > > + vb2_buffer_done(&buf->vb.vb2_buf,
> > > VB2_BUF_STATE_ERROR);
> > > + pr_debug("Empty buffer");
> > > + } else if (dev->capture.frame_count) {
> > > + /* grab another frame */
> > > + if (is_capturing(dev)) {
> > > + pr_debug("Grab another frame");
> > > + vchiq_mmal_port_parameter_set(
> > > + instance,
> > > + dev->capture.
> > > + camera_port,
> > > + MMAL_PARAMETER_CAPTURE,
> > > + &dev->capture.
> > > + frame_count,
> > > + sizeof(dev-
> > > >capture.frame_count));
> > > + }
> > > + } else {
> > > + /* signal frame completion */
> > > + complete(&dev->capture.frame_cmplt);
> > > + }
> >
> > Better to add a return here and avoid the else below. That makes it
> > more readable, and avoid weird line breakages due to 80 column
> > soft-limit.
> >
> > > + } else {
> > > + if (dev->capture.frame_count) {
> > > + if (dev->capture.vc_start_timestamp !=
> > > -1 &&
> > > + pts != 0) {
> > > + struct timeval timestamp;
> > > + s64 runtime_us = pts -
> > > + dev-
> > > >capture.vc_start_timestamp;
> >
> > Please either put the statement on a single line or indent the
> > second
> > like with the argument after the equal operator.
> >
> > > + u32 div = 0;
> > > + u32 rem = 0;
> > > +
> > > + div =
> > > + div_u64_rem(runtime_us,
> > > USEC_PER_SEC, &rem);
> > > + timestamp.tv_sec =
> > > + dev-
> > > >capture.kernel_start_ts.tv_sec + div;
> > > + timestamp.tv_usec =
> > > + dev-
> > > >capture.kernel_start_ts.tv_usec + rem;
> >
> > Please don't break the lines.
> > > +
> > > + if (timestamp.tv_usec >=
> > > + USEC_PER_SEC) {
> >
> > I suspect you could put it on a single line.
> >
> > > + timestamp.tv_sec++;
> > > + timestamp.tv_usec -=
> > > + USEC_PER_SEC;
> > > + }
> > > + v4l2_dbg(1, bcm2835_v4l2_debug,
> > > &dev->v4l2_dev,
> > > + "Convert start time
> > > %d.%06d and %llu "
> > > + "with offset %llu to
> > > %d.%06d\n",
> >
> > Don't break strings on multiple lines.
> >
> > > + (int)dev-
> > > >capture.kernel_start_ts.
> > > + tv_sec,
> > > + (int)dev-
> > > >capture.kernel_start_ts.
> > > + tv_usec,
> > > + dev-
> > > >capture.vc_start_timestamp, pts,
> > > + (int)timestamp.tv_sec,
> > > + (int)timestamp.tv_usec)
> > > ;
> > > + buf->vb.vb2_buf.timestamp =
> > > timestamp.tv_sec * 1000000000ULL +
> > > + timestamp.tv_usec *
> > > 1000ULL;
> >
> > Not sure if I understood the above logic... Why don't you just do
> > buf->vb.vb2_buf.timestamp = ktime_get_ns();
>
> What's the processing latency through the ISP and optional
> H264/MJPG/JPEG encode to get to this point? Typically you're looking
> at
> 30-80ms depending on exposure time and various other factors, which
> would be enough to put A/V sync out if not compensated for.
>
> The GPU side is timestamping all buffers with the CSI frame start
> interrupt timestamp, but based on the GPU STC. There is a MMAL call
> to
> read the GPU STC which is made at streamon (stored in
> dev->capture.vc_start_timestamp), and therefore this is taking a
> delta
> from there to get a more accurate timestamp.
> (An improvement would be to reread it every N seconds to ensure
> there
> was no drift, but the Linux kernel tick is actually off the same
> clock,
> so it is only clock corrections that would introduce a drift).
> As I understand it UVC is doing a similar thing, although it is
> trying
> to compensate for clock drift too.
>
> Now one could argue that ideally you want the timestamp for the start
> of
> exposure, but there is no event outside of the sensor to trigger
> that.
> You could compute it, but the exposure time control loop is running
> on
> the GPU so the kernel doesn't know the exposure time. It's also a bit
> of
> a funny thing anyway when dealing with rolling shutter sensors and
> therefore considering which line you want the start of exposure for.
>
> >
> > > + } else {
> > > + buf->vb.vb2_buf.timestamp =
> > > ktime_get_ns();
> > > + }
> > > +
> > > + vb2_set_plane_payload(&buf->vb.vb2_buf,
> > > 0, length);
> > > + vb2_buffer_done(&buf->vb.vb2_buf,
> > > VB2_BUF_STATE_DONE);
> > > +
> > > + if (mmal_flags &
> > > MMAL_BUFFER_HEADER_FLAG_EOS &&
> > > + is_capturing(dev)) {
> > > + v4l2_dbg(1, bcm2835_v4l2_debug,
> > > &dev->v4l2_dev,
> > > + "Grab another frame as
> > > buffer has EOS");
> > > + vchiq_mmal_port_parameter_set(
> > > + instance,
> > > + dev->capture.
> > > + camera_port,
> > > + MMAL_PARAMETER_CAPTURE,
> > > + &dev->capture.
> > > + frame_count,
> > > + sizeof(dev-
> > > >capture.frame_count));
> > > + }
> > > + } else {
> > > + /* signal frame completion */
> > > + vb2_buffer_done(&buf->vb.vb2_buf,
> > > VB2_BUF_STATE_ERROR);
> > > + complete(&dev->capture.frame_cmplt);
> >
> > I would move the error condition to happen before and just return,
> > in order to reduce the indentation.
> >
> > > + }
> > > + }
> > > +}
> > > +
>
> <snip>
>
> > > +static int vidioc_enum_fmt_vid_cap(struct file *file, void
> > > *priv,
> > > + struct v4l2_fmtdesc *f)
> > > +{
> > > + struct mmal_fmt *fmt;
> > > +
> > > + if (f->index >= ARRAY_SIZE(formats))
> > > + return -EINVAL;
> > > +
> > > + fmt = &formats[f->index];
> >
> > Shouldn't this be checking if the sensor is the Sony one or the
> > Omnivision?
> >
> > Same applies to g_fmt and s_fmt.
>
> Not when the ISP is in the way. This is effectively the list of
> output
> formats from the ISP (and optional encoders), not the sensor.
>
> > > +
> > > + strlcpy(f->description, fmt->name, sizeof(f-
> > > >description));
> > > + f->pixelformat = fmt->fourcc;
> > > + f->flags = fmt->flags;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
> > > + struct v4l2_format *f)
> > > +{
> > > + struct bm2835_mmal_dev *dev = video_drvdata(file);
> > > +
> > > + f->fmt.pix.width = dev->capture.width;
> > > + f->fmt.pix.height = dev->capture.height;
> > > + f->fmt.pix.field = V4L2_FIELD_NONE;
> > > + f->fmt.pix.pixelformat = dev->capture.fmt->fourcc;
> > > + f->fmt.pix.bytesperline = dev->capture.stride;
> > > + f->fmt.pix.sizeimage = dev->capture.buffersize;
> > > +
> > > + if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_RGB24)
> > > + f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
> > > + else if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_JPEG)
> > > + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
> > > + else
> > > + f->fmt.pix.colorspace =
> > > V4L2_COLORSPACE_SMPTE170M;
> > > + f->fmt.pix.priv = 0;
> > > +
> > > + v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev-
> > > >v4l2_dev, &f->fmt.pix,
> > > + __func__);
> > > + return 0;
> > > +}
> > > +
> > > +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
> > > + struct v4l2_format *f)
> > > +{
> > > + struct bm2835_mmal_dev *dev = video_drvdata(file);
> > > + struct mmal_fmt *mfmt;
> > > +
> > > + mfmt = get_format(f);
> > > + if (!mfmt) {
> > > + v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev,
> > > + "Fourcc format (0x%08x) unknown.\n",
> > > + f->fmt.pix.pixelformat);
> > > + f->fmt.pix.pixelformat = formats[0].fourcc;
> > > + mfmt = get_format(f);
> > > + }
> > > +
> > > + f->fmt.pix.field = V4L2_FIELD_NONE;
> > > +
> > > + v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev,
> > > + "Clipping/aligning %dx%d format %08X\n",
> > > + f->fmt.pix.width, f->fmt.pix.height, f-
> > > >fmt.pix.pixelformat);
> > > +
> > > + v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, dev-
> > > >max_width, 1,
> > > + &f->fmt.pix.height, MIN_HEIGHT,
> > > dev->max_height,
> > > + 1, 0);
> >
> > Hmm... that looks weird... For YUY formats, the step is usually 2
> > or 4.
> > Also, as most cameras use internally a bayer sensor, they don't
> > allow
> > aligning to 1, except when then have scallers.
>
> Correct. It should be multiples of 2 in either direction.
>
> > > + f->fmt.pix.bytesperline = f->fmt.pix.width * mfmt->ybbp;
> > > +
> > > + /* Image buffer has to be padded to allow for alignment,
> > > even though
> > > + * we then remove that padding before delivering the
> > > buffer.
> > > + */
> > > + f->fmt.pix.sizeimage = ((f->fmt.pix.height+15)&~15) *
> > > + (((f->fmt.pix.width+31)&~31) * mfmt-
> > > >depth) >> 3;
> >
> > It seems that you're fixing the bug at the steps used by
> > v4l_bound_align_image() by rounding up the buffer size. That's
> > wrong!
> > Just ensure that the width/height will be a valid resolution and
> > remove this hack.
>
> No, this is working around the fact that very few clients respect
> bytesperline (eg QV4L2 and libv4lconvert for many of the formats).
>
> The ISP needs to be writing to buffers with the stride being a
> multiple
> of 32, and height a multiple of 16 (and that includes between planes
> of
> YUV420). V4L2 appears not to allow that, therefore there is then a
> second operation run in-place on the buffer to remove that padding,
> but
> the buffer needs to be sized sufficiently to handle the padded image
> first.
> I had a conversation with Hans back in 2013 with regard this, and
> there
> wasn't a good solution proposed. It could potentially be specified
> using
> the cropping API, but that pushes the responsibility back onto every
> client app to drive things in a very specific manner. If they don't
> respect bytesperline they are even less likely to handle cropping.
> You could restrict the resolution to being a multiple of 32 on the
> width
> and 16 on the height, but in doing so you're not exposing the full
> capabilities.
>
> I'm open to suggestions as to how V4L2 can do this without just
> beating
> up client apps who do the wrong thing.
>
> Multiplanar formats seem not to be an option as the ISP is expecting
> one
> contiguous buffer to be provided to take all the planes, but the
> multiplanar stuff supplies multiple independent buffers. Again
> please
> correct me if I'm wrong on that.
>
> > > +
> > > + if ((mfmt->flags & V4L2_FMT_FLAG_COMPRESSED) &&
> > > + f->fmt.pix.sizeimage < MIN_BUFFER_SIZE)
> > > + f->fmt.pix.sizeimage = MIN_BUFFER_SIZE;
> > > +
> > > + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24)
> > > + f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
> > > + else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG)
> > > + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
> > > + else
> > > + f->fmt.pix.colorspace =
> > > V4L2_COLORSPACE_SMPTE170M;
> > > + f->fmt.pix.priv = 0;
> > > +
> > > + v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev,
> > > + "Now %dx%d format %08X\n",
> > > + f->fmt.pix.width, f->fmt.pix.height, f-
> > > >fmt.pix.pixelformat);
> > > +
> > > + v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev-
> > > >v4l2_dev, &f->fmt.pix,
> > > + __func__);
> > > + return 0;
> > > +}
> > > +
> > > +static int mmal_setup_components(struct bm2835_mmal_dev *dev,
> > > + struct v4l2_format *f)
> > > +{
> > > + int ret;
> > > + struct vchiq_mmal_port *port = NULL, *camera_port =
> > > NULL;
> > > + struct vchiq_mmal_component *encode_component = NULL;
> > > + struct mmal_fmt *mfmt = get_format(f);
> > > +
> > > + BUG_ON(!mfmt);
> > > +
> > > + if (dev->capture.encode_component) {
> > > + v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev,
> > > + "vid_cap - disconnect previous
> > > tunnel\n");
> > > +
> > > + /* Disconnect any previous connection */
> > > + vchiq_mmal_port_connect_tunnel(dev->instance,
> > > + dev-
> > > >capture.camera_port, NULL);
> > > + dev->capture.camera_port = NULL;
> > > + ret = vchiq_mmal_component_disable(dev-
> > > >instance,
> > > + dev->capture.
> > > + encode_compon
> > > ent);
> > > + if (ret)
> > > + v4l2_err(&dev->v4l2_dev,
> > > + "Failed to disable encode
> > > component %d\n",
> > > + ret);
> > > +
> > > + dev->capture.encode_component = NULL;
> > > + }
> > > + /* format dependant port setup */
> > > + switch (mfmt->mmal_component) {
> > > + case MMAL_COMPONENT_CAMERA:
> > > + /* Make a further decision on port based on
> > > resolution */
> > > + if (f->fmt.pix.width <= max_video_width
> > > + && f->fmt.pix.height <= max_video_height)
> > > + camera_port = port =
> > > + &dev-
> > > >component[MMAL_COMPONENT_CAMERA]->
> > > + output[MMAL_CAMERA_PORT_VIDEO];
> > > + else
> > > + camera_port = port =
> > > + &dev-
> > > >component[MMAL_COMPONENT_CAMERA]->
> > > + output[MMAL_CAMERA_PORT_CAPTURE];
> >
> > Not sure if I got this... What are you intending to do here?
>
> As noted above, what do you consider a still when dealing with raw
> RGB
> or YUV buffers. This is switching between video and stills quality
> processing based on resolution.
>
> > > + break;
> > > + case MMAL_COMPONENT_IMAGE_ENCODE:
> > > + encode_component = dev-
> > > >component[MMAL_COMPONENT_IMAGE_ENCODE];
> > > + port = &dev-
> > > >component[MMAL_COMPONENT_IMAGE_ENCODE]->output[0];
> > > + camera_port =
> > > + &dev->component[MMAL_COMPONENT_CAMERA]->
> > > + output[MMAL_CAMERA_PORT_CAPTURE];
> > > + break;
>
> <snip>
>
> > > +/* timeperframe is arbitrary and continous */
> > > +static int vidioc_enum_frameintervals(struct file *file, void
> > > *priv,
> > > + struct
> > > v4l2_frmivalenum *fival)
> > > +{
> > > + struct bm2835_mmal_dev *dev = video_drvdata(file);
> > > + int i;
> > > +
> > > + if (fival->index)
> > > + return -EINVAL;
> > > +
> > > + for (i = 0; i < ARRAY_SIZE(formats); i++)
> > > + if (formats[i].fourcc == fival->pixel_format)
> > > + break;
> > > + if (i == ARRAY_SIZE(formats))
> > > + return -EINVAL;
> > > +
> > > + /* regarding width & height - we support any within
> > > range */
> > > + if (fival->width < MIN_WIDTH || fival->width > dev-
> > > >max_width ||
> > > + fival->height < MIN_HEIGHT || fival->height > dev-
> > > >max_height)
> > > + return -EINVAL;
> > > +
> > > + fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
> >
> > That seems wrong! Webcam sensors usually require a multiple of at
> > least 2
> > for both horizontal and vertical resolutions, due to the way the
> > pixels
> > are packaged internally.
> >
> > Typically, only analog TV uses V4L2_FRMIVAL_TYPE_CONTINUOUS.
> >
> > Ok, if you're using expensive sensors with sophisticated scalers on
> > it, you could have a continuous resolution, but I doubt this is the
> > case here.
>
> Isn't this frame interval, not resolution, although yes, it ought to
> sanity check the resolution to be a multiple of 2 in each direction.
>
> With regard frame interval, it could be specified as STEPWISE with
> an
> increment of 32.5usecs or 18.9usecs (the default line time for
> ov5647
> and imx219 respectively), but to most people that would count as
> continuous.
>
> There is the added complication that the GPU code will select the
> most
> appropriate sensor mode (about 7 are defined) based on the frame
> rate
> and resolution requested, and each of the modes has different line
> times. Reading it back would be possible but just seemed excessive.
>
>
> I'm curious now, how does analogue TV count as CONTINUOUS when surely
> it
> isn't something that can be set on a tuner that is only relaying the
> received video signal.
>
> > > +
> > > + /* fill in stepwise (step=1.0 is requred by V4L2 spec)
> > > */
> > > + fival->stepwise.min = tpf_min;
> > > + fival->stepwise.max = tpf_max;
> > > + fival->stepwise.step = (struct v4l2_fract) {1, 1};
> > > +
> > > + return 0;
> > > +}
> > > +
>
> <snip>
>
> > > +/* Returns the number of cameras, and also the max resolution
> > > supported
> > > + * by those cameras.
> > > + */
> > > +static int get_num_cameras(struct vchiq_mmal_instance *instance,
> > > + unsigned int resolutions[][2], int num_resolutions)
> > > +{
> > > + int ret;
> > > + struct vchiq_mmal_component *cam_info_component;
> > > + struct mmal_parameter_camera_info_t cam_info = {0};
> > > + int param_size = sizeof(cam_info);
> > > + int i;
> > > +
> > > + /* create a camera_info component */
> > > + ret = vchiq_mmal_component_init(instance, "camera_info",
> > > + &cam_info_component);
> > > + if (ret < 0)
> > > + /* Unusual failure - let's guess one camera. */
> > > + return 1;
> >
> > Hmm... what happens if no cameras are plugged to RPi?
>
> More that this query wasn't available on early GPU firmware versions
> -
> it was added in 2016 when the IMX219 camera support was added.
> If there are genuinely no cameras connected, then the camera
> component
> create at a later stage will fail and that it also handled.
>
> > > +
> > > + if (vchiq_mmal_port_parameter_get(instance,
> > > + &cam_info_component-
> > > >control,
> > > + MMAL_PARAMETER_CAMERA_
> > > INFO,
> > > + &cam_info,
> > > + ¶m_size)) {
> > > + pr_info("Failed to get camera info\n");
> > > + }
> > > + for (i = 0;
> > > + i < (cam_info.num_cameras > num_resolutions ?
> > > + num_resolutions :
> > > + cam_info.num_cameras);
> > > + i++) {
> > > + resolutions[i][0] =
> > > cam_info.cameras[i].max_width;
> > > + resolutions[i][1] =
> > > cam_info.cameras[i].max_height;
> > > + }
> > > +
> > > + vchiq_mmal_component_finalise(instance,
> > > + cam_info_component);
> > > +
> > > + return cam_info.num_cameras;
> > > +}
> > > +
> > > +static int set_camera_parameters(struct vchiq_mmal_instance
> > > *instance,
> > > + struct vchiq_mmal_component
> > > *camera,
> > > + struct bm2835_mmal_dev *dev)
> > > +{
> > > + int ret;
> > > + struct mmal_parameter_camera_config cam_config = {
> > > + .max_stills_w = dev->max_width,
> > > + .max_stills_h = dev->max_height,
> > > + .stills_yuv422 = 1,
> > > + .one_shot_stills = 1,
> > > + .max_preview_video_w = (max_video_width > 1920)
> > > ?
> > > + max_video_width
> > > : 1920,
> > > + .max_preview_video_h = (max_video_height > 1088)
> > > ?
> > > + max_video_height
> > > : 1088,
> >
> > Hmm... why do you need to limit the max resolution to 1920x1088? Is
> > it
> > a limit of the MMAL/firmware?
>
> Memory usage.
> Video mode runs as an optimised pipeline so requires multiple frame
> buffers.
> Stills mode typically has to stop the sensor, reprogram for full res
> mode, stream for one frame, and then stops the sensor again,
> therefore
> only one stills res buffer is required.
> If you've specified video mode to run at more than 1080P, then the
> GPU
> needs to be told up front so that it can allocate the extra memory.
>
> > > + .num_preview_video_frames = 3,
> > > + .stills_capture_circular_buffer_height = 0,
> > > + .fast_preview_resume = 0,
> > > + .use_stc_timestamp =
> > > MMAL_PARAM_TIMESTAMP_MODE_RAW_STC
> > > + };
> > > +
> > > + ret = vchiq_mmal_port_parameter_set(instance, &camera-
> > > >control,
> > > + MMAL_PARAMETER_CAMER
> > > A_CONFIG,
> > > + &cam_config,
> > > sizeof(cam_config));
> > > + return ret;
> > > +}
> > > +
> > > +#define MAX_SUPPORTED_ENCODINGS 20
> > > +
> > > +/* MMAL instance and component init */
> > > +static int __init mmal_init(struct bm2835_mmal_dev *dev)
> > > +{
> > > + int ret;
> > > + struct mmal_es_format *format;
> > > + u32 bool_true = 1;
> > > + u32 supported_encodings[MAX_SUPPORTED_ENCODINGS];
> > > + int param_size;
> > > + struct vchiq_mmal_component *camera;
> > > +
> > > + ret = vchiq_mmal_init(&dev->instance);
> > > + if (ret < 0)
> > > + return ret;
> > > +
> > > + /* get the camera component ready */
> > > + ret = vchiq_mmal_component_init(dev->instance,
> > > "ril.camera",
> > > + &dev-
> > > >component[MMAL_COMPONENT_CAMERA]);
> > > + if (ret < 0)
> > > + goto unreg_mmal;
> > > +
> > > + camera = dev->component[MMAL_COMPONENT_CAMERA];
> > > + if (camera->outputs < MMAL_CAMERA_PORT_COUNT) {
> > > + ret = -EINVAL;
> > > + goto unreg_camera;
> > > + }
> > > +
> > > + ret = set_camera_parameters(dev->instance,
> > > + camera,
> > > + dev);
> > > + if (ret < 0)
> > > + goto unreg_camera;
> > > +
> > > + /* There was an error in the firmware that meant the
> > > camera component
> > > + * produced BGR instead of RGB.
> > > + * This is now fixed, but in order to support the old
> > > firmwares, we
> > > + * have to check.
> > > + */
> > > + dev->rgb_bgr_swapped = true;
> > > + param_size = sizeof(supported_encodings);
> > > + ret = vchiq_mmal_port_parameter_get(dev->instance,
> > > + &camera->output[MMAL_CAMERA_PORT_CAPTURE],
> > > + MMAL_PARAMETER_SUPPORTED_ENCODINGS,
> > > + &supported_encodings,
> > > + ¶m_size);
> > > + if (ret == 0) {
> > > + int i;
> > > +
> > > + for (i = 0; i < param_size/sizeof(u32); i++) {
> > > + if (supported_encodings[i] ==
> > > MMAL_ENCODING_BGR24) {
> > > + /* Found BGR24 first - old
> > > firmware. */
> > > + break;
> > > + }
> > > + if (supported_encodings[i] ==
> > > MMAL_ENCODING_RGB24) {
> > > + /* Found RGB24 first
> > > + * new firmware, so use RGB24.
> > > + */
> > > + dev->rgb_bgr_swapped = false;
> > > + break;
> > > + }
> > > + }
> > > + }
> > > + format = &camera-
> > > >output[MMAL_CAMERA_PORT_PREVIEW].format;
> > > +
> > > + format->encoding = MMAL_ENCODING_OPAQUE;
> > > + format->encoding_variant = MMAL_ENCODING_I420;
> > > +
> > > + format->es->video.width = 1024;
> > > + format->es->video.height = 768;
> >
> > Shouldn't this be checking if the hardware supports 1024x768?
> > Same note for similar parameters below.
>
> All the supported sensors can do 1024x768 JPEG. This is just setting
> up
> some defaults.
>
> > > + format->es->video.crop.x = 0;
> > > + format->es->video.crop.y = 0;
> > > + format->es->video.crop.width = 1024;
> > > + format->es->video.crop.height = 768;
> > > + format->es->video.frame_rate.num = 0; /* Rely on
> > > fps_range */
> > > + format->es->video.frame_rate.den = 1;
> > > +
> > > + format = &camera->output[MMAL_CAMERA_PORT_VIDEO].format;
> > > +
> > > + format->encoding = MMAL_ENCODING_OPAQUE;
> > > + format->encoding_variant = MMAL_ENCODING_I420;
> > > +
> > > + format->es->video.width = 1024;
> > > + format->es->video.height = 768;
> > > + format->es->video.crop.x = 0;
> > > + format->es->video.crop.y = 0;
> > > + format->es->video.crop.width = 1024;
> > > + format->es->video.crop.height = 768;
> > > + format->es->video.frame_rate.num = 0; /* Rely on
> > > fps_range */
> > > + format->es->video.frame_rate.den = 1;
> > > +
> > > + vchiq_mmal_port_parameter_set(dev->instance,
> > > + &camera->output[MMAL_CAMERA_PORT_VIDEO],
> > > + MMAL_PARAMETER_NO_IMAGE_PADDING,
> > > + &bool_true, sizeof(bool_true));
> > > +
> > > + format = &camera-
> > > >output[MMAL_CAMERA_PORT_CAPTURE].format;
> > > +
> > > + format->encoding = MMAL_ENCODING_OPAQUE;
> > > +
> > > + format->es->video.width = 2592;
> > > + format->es->video.height = 1944;
> >
> > Shouldn't this be checking if the hardware supports such
> > resolution?
> > Where this magic numbers came from? Why is it different than the
> > previous
> > resolution?
>
> Video vs stills port.
> TBH I'd actually want to double check whether this is necessary as I
> thought it went through the standard s_fmt path to set up the
> default
> mode, and that would do all this anyway.
>
> > > + format->es->video.crop.x = 0;
> > > + format->es->video.crop.y = 0;
> > > + format->es->video.crop.width = 2592;
> > > + format->es->video.crop.height = 1944;
> > > + format->es->video.frame_rate.num = 0; /* Rely on
> > > fps_range */
> > > + format->es->video.frame_rate.den = 1;
> > > +
> > > + dev->capture.width = format->es->video.width;
> > > + dev->capture.height = format->es->video.height;
> > > + dev->capture.fmt = &formats[0];
> > > + dev->capture.encode_component = NULL;
> > > + dev->capture.timeperframe = tpf_default;
> > > + dev->capture.enc_profile =
> > > V4L2_MPEG_VIDEO_H264_PROFILE_HIGH;
> > > + dev->capture.enc_level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0;
> > > +
>
> <snip>
>
> > > +int bm2835_mmal_set_all_camera_controls(struct bm2835_mmal_dev
> > > *dev)
> > > +{
> > > + int c;
> > > + int ret = 0;
> > > +
> > > + for (c = 0; c < V4L2_CTRL_COUNT; c++) {
> > > + if ((dev->ctrls[c]) && (v4l2_ctrls[c].setter)) {
> > > + ret = v4l2_ctrls[c].setter(dev, dev-
> > > >ctrls[c],
> > > + &v4l2_ctrls[c
> > > ]);
> > > + if (!v4l2_ctrls[c].ignore_errors && ret)
> > > {
> > > + v4l2_dbg(1, bcm2835_v4l2_debug,
> > > &dev->v4l2_dev,
> > > + "Failed when setting
> > > default values for ctrl %d\n",
> > > + c);
> > > + break;
> > > + }
> > > + }
> > > + }
> >
> > There's something weird here... it is exposing all controls without
> > checking if the hardware supports them. Does the VC4 firmware
> > emulate the parameters on sensors that don't support? Otherwise,
> > you'll need to query the hardware (or use DT) and only expose the
> > controls that
> > are provided by the given camera module.
>
> You're at the end of the ISP. Everything except flips, exposure time
> and
> analogue gain are implemented in the ISP so therefore they are
> supported.
> All sensors are expected to support flips, exposure time and
> analogue
> gain correctly (otherwise I complain to whoever wrote the camera
> driver!).
>
> <snip>
>
> > > +/* data in message, memcpy from packet into output buffer */
> > > +static int inline_receive(struct vchiq_mmal_instance *instance,
> > > + struct mmal_msg *msg,
> > > + struct mmal_msg_context *msg_context)
> > > +{
> > > + unsigned long flags = 0;
> > > +
> > > + /* take buffer from queue */
> > > + spin_lock_irqsave(&msg_context->u.bulk.port->slock,
> > > flags);
> > > + if (list_empty(&msg_context->u.bulk.port->buffers)) {
> > > + spin_unlock_irqrestore(&msg_context-
> > > >u.bulk.port->slock, flags);
> > > + pr_err("buffer list empty trying to receive
> > > inline\n");
> > > +
> > > + /* todo: this is a serious error, we should
> > > never have
> > > + * commited a buffer_to_host operation to the
> > > mmal
> > > + * port without the buffer to back it up (with
> > > + * underflow handling) and there is no obvious
> > > way to
> > > + * deal with this. Less bad than the bulk case
> > > as we
> > > + * can just drop this on the floor
> > > but...unhelpful
> > > + */
> >
> > If the bug is serious enough to corrupt memory, better to call
> > BUG(),
> > as otherwise it could do insane things, including corrupting a
> > dirty
> > disk cache - with could result on filesystem corruption.
>
> I'd need to check exactly what the situation is here. It's been a
> while
> since I've looked at the buffer handling code, but will review and
> make
> it a BUG_ON if appropriate.
>
> > > + return -EINVAL;
> > > + }
> > > +
>
> <snip>
>
> _______________________________________________
> linux-rpi-kernel mailing list
> linux-rpi-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rpi-kernel
More information about the linux-rpi-kernel
mailing list