All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/4] BCM283x Camera Receiver driver
@ 2017-09-13 15:07 Dave Stevenson
       [not found] ` <cover.1505314390.git.dave.stevenson@raspberrypi.org>
  2017-09-13 15:49 ` [PATCH v2 0/4] BCM283x Camera Receiver driver Dave Stevenson
  0 siblings, 2 replies; 20+ messages in thread
From: Dave Stevenson @ 2017-09-13 15:07 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Rob Herring, Hans Verkuil, Eric Anholt,
	Stefan Wahren, Sakari Ailus, linux-media, linux-rpi-kernel,
	devicetree
  Cc: Dave Stevenson

Hi All.

This is v2 for adding a V4L2 subdevice driver for the CSI2/CCP2 camera
receiver peripheral on BCM283x, as used on Raspberry Pi.
Sorry for the delay since v1 - other tasks assigned, got sucked
into investigating why some devices were misbehaving, and
picking up on new features that had been added to the tree (eg CCP2).

v4l2-compliance results depend on the sensor subdevice connected to.
I have results for TC358743, ADV7282M, and OV5647 that I'll
send them as a follow up email.

OV647 and ADV7282M are now working with this driver, as well as TC358743.
v1 of the driver only supported continuous clock mode which Unicam was
failing to lock on to correctly.
The driver now checks the clock mode and adjusts termination accordingly.
Something is still a little off for OV5647, but I'll investigate that
later.

As per the v1 discussion with Hans, I have added text describing the
differences between this driver and the one in staging/vc04_service.
Addressing some of the issues in the bcm2835-camera driver is on my to-do
list, and I'll add similar text there when I'm dealing with that.

For those wanting to see the driver in context,
https://github.com/6by9/upstream-linux/tree/unicam is the linux-media
tree with my mods on top. It also includes a couple of TC358743 and
OV5647 driver updates that I'll send to the list in the next few days.

Thanks in advance.
  Dave

Changes from v1 to v2:
- Broken out a new helper function v4l2_fourcc2s as requested by Hans.
- Documented difference between this driver and the bcm2835-camera driver
  in staging/vc04_services.
- Corrected handling of s_dv_timings and s_std to update the current format
  but only if not streaming. This refactored some of the s_fmt code to
  remove duplication.
- Updated handling of sizeimage to include vertical padding. (Not updated
  the bytesperline calcs as the app can override).
- Added support for continuous clock mode (requires changes to lane
  termination configuration).
- Add support for CCP2 as Sakari's patches to support it have now been merged.
  I don't have a suitable sensor to test it with at present, but all settings
  have been taken from a known working configuration. If people would prefer
  I remove this until it has been proved against hardware then I'm happy to
  do so.
- Updated DT bindings to use <data-lanes> on the Unicam node to set the
  maximum number of lanes present instead of a having a custom property.
  Documents the mandatory endpoint properties.
- Removed RAW16 from the list of input formats as it isn't defined in the
  CSI-2 spec. The peripheral can still unpack the other Bayer formats to
  a 16 bit/pixel packing though.
- Added a log-status handler to get the status from the sensor.
- Automatically switch away from any interlaced formats reported via g_fmt,
  or that are attempted to be set via try/s_fmt.
- Addressed other more minor code review comments from v1.

Dave Stevenson (4):
  [media] v4l2-common: Add helper function for fourcc to string
  [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
  [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
  MAINTAINERS: Add entry for BCM2835 camera driver

 .../devicetree/bindings/media/bcm2835-unicam.txt   |  107 +
 MAINTAINERS                                        |    7 +
 drivers/media/platform/Kconfig                     |    1 +
 drivers/media/platform/Makefile                    |    1 +
 drivers/media/platform/bcm2835/Kconfig             |   14 +
 drivers/media/platform/bcm2835/Makefile            |    3 +
 drivers/media/platform/bcm2835/bcm2835-unicam.c    | 2192 ++++++++++++++++++++
 drivers/media/platform/bcm2835/vc4-regs-unicam.h   |  264 +++
 drivers/media/v4l2-core/v4l2-common.c              |   18 +
 include/media/v4l2-common.h                        |    3 +
 10 files changed, 2610 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/bcm2835-unicam.txt
 create mode 100644 drivers/media/platform/bcm2835/Kconfig
 create mode 100644 drivers/media/platform/bcm2835/Makefile
 create mode 100644 drivers/media/platform/bcm2835/bcm2835-unicam.c
 create mode 100644 drivers/media/platform/bcm2835/vc4-regs-unicam.h

-- 
2.7.4

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v2 1/4] [media] v4l2-common: Add helper function for fourcc to string
       [not found] ` <cover.1505314390.git.dave.stevenson@raspberrypi.org>
@ 2017-09-13 15:07   ` Dave Stevenson
  2017-09-13 15:07   ` [PATCH v2 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver Dave Stevenson
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 20+ messages in thread
From: Dave Stevenson @ 2017-09-13 15:07 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Rob Herring, Hans Verkuil, Eric Anholt,
	Stefan Wahren, Sakari Ailus, linux-media, linux-rpi-kernel,
	devicetree
  Cc: Dave Stevenson

New helper function char *v4l2_fourcc2s(u32 fourcc, char *buf)
that converts a fourcc into a nice printable version.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
---
 drivers/media/v4l2-core/v4l2-common.c | 18 ++++++++++++++++++
 include/media/v4l2-common.h           |  3 +++
 2 files changed, 21 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index a5ea1f5..0219895 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -405,3 +405,21 @@ void v4l2_get_timestamp(struct timeval *tv)
 	tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
 }
 EXPORT_SYMBOL_GPL(v4l2_get_timestamp);
+
+char *v4l2_fourcc2s(u32 fourcc, char *buf)
+{
+	buf[0] = fourcc & 0x7f;
+	buf[1] = (fourcc >> 8) & 0x7f;
+	buf[2] = (fourcc >> 16) & 0x7f;
+	buf[3] = (fourcc >> 24) & 0x7f;
+	if (fourcc & (1 << 31)) {
+		buf[4] = '-';
+		buf[5] = 'B';
+		buf[6] = 'E';
+		buf[7] = '\0';
+	} else {
+		buf[4] = '\0';
+	}
+	return buf;
+}
+EXPORT_SYMBOL_GPL(v4l2_fourcc2s);
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index aac8b7b..5b0fff9 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -264,4 +264,7 @@ const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
 
 void v4l2_get_timestamp(struct timeval *tv);
 
+#define V4L2_FOURCC_MAX_SIZE 8
+char *v4l2_fourcc2s(u32 fourcc, char *buf);
+
 #endif /* V4L2_COMMON_H_ */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
       [not found] ` <cover.1505314390.git.dave.stevenson@raspberrypi.org>
  2017-09-13 15:07   ` [PATCH v2 1/4] [media] v4l2-common: Add helper function for fourcc to string Dave Stevenson
@ 2017-09-13 15:07   ` Dave Stevenson
       [not found]     ` <9ad8b23d5c394b64ed02f9a5ebc49209696a5ace.1505314390.git.dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
  2017-09-13 15:07   ` [PATCH v2 3/4] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface Dave Stevenson
  2017-09-13 15:07   ` [PATCH v2 4/4] MAINTAINERS: Add entry for BCM2835 camera driver Dave Stevenson
  3 siblings, 1 reply; 20+ messages in thread
From: Dave Stevenson @ 2017-09-13 15:07 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Rob Herring, Hans Verkuil, Eric Anholt,
	Stefan Wahren, Sakari Ailus, linux-media, linux-rpi-kernel,
	devicetree
  Cc: Dave Stevenson

Document the DT bindings for the CSI2/CCP2 receiver peripheral
(known as Unicam) on BCM283x SoCs.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
---
 .../devicetree/bindings/media/bcm2835-unicam.txt   | 107 +++++++++++++++++++++
 1 file changed, 107 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/bcm2835-unicam.txt

diff --git a/Documentation/devicetree/bindings/media/bcm2835-unicam.txt b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
new file mode 100644
index 0000000..2ee5af7
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
@@ -0,0 +1,107 @@
+Broadcom BCM283x Camera Interface (Unicam)
+------------------------------------------
+
+The Unicam block on BCM283x SoCs is the receiver for either
+CSI-2 or CCP2 data from image sensors or similar devices.
+
+There are two camera drivers in the kernel for BCM283x - this one
+and bcm2835-camera (currently in staging).
+
+This driver is purely the kernel controlling the Unicam peripheral - there
+is no involvement with the VideoCore firmware. Unicam receives CSI-2
+(or CCP2) data and writes it into SDRAM. There is no additional processing
+performed.
+It should be possible to connect it to any sensor with a
+suitable output interface and V4L2 subdevice driver.
+
+bcm2835-camera uses the VideoCore firmware to control the sensor,
+Unicam, ISP, and various tuner control loops. Fully processed frames are
+delivered to the driver by the firmware. It only has sensor drivers
+for Omnivision OV5647, and Sony IMX219 sensors, and is closed source.
+
+The two drivers are mutually exclusive for the same Unicam instance.
+The firmware checks the device tree configuration during boot. If
+it finds device tree nodes called csi0 or csi1 then it will block the
+firmware from accessing the peripheral, and bcm2835-camera will
+not be able to stream data.
+It should be possible to use bcm2835-camera on one camera interface
+and bcm2835-unicam on the other interface if there is a need to.
+
+Required properties:
+===================
+- compatible	: must be "brcm,bcm2835-unicam".
+- reg		: physical base address and length of the register sets for the
+		  device.
+- interrupts	: should contain the IRQ line for this Unicam instance.
+- clocks	: list of clock specifiers, corresponding to entries in
+		  clock-names property.
+- clock-names	: must contain an "lp_clock" entry, matching entries
+		  in the clocks property.
+
+Unicam supports a single port node. It should contain one 'port' child node
+with child 'endpoint' node. Please refer to the bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Within the endpoint node, the following properties are mandatory:
+- remote-endpoint	: links to the source device endpoint.
+- data-lanes		: An array denoting how many data lanes are physically
+			  present for this CSI-2 receiver instance. This can
+			  be limited by either the SoC itself, or by the
+			  breakout on the platform.
+			  Lane reordering is not supported, so lanes must be
+			  in order, starting at 1.
+
+Lane reordering is not supported on the clock lane, so the optional property
+"clock-lane" will implicitly be <0>.
+Similarly lane inversion is not supported, therefore "lane-polarities" will
+implicitly be <0 0 0 0 0>.
+Neither of these values will be checked.
+
+Example:
+	csi1: csi@7e801000 {
+		compatible = "brcm,bcm2835-unicam";
+		reg = <0x7e801000 0x800>,
+		      <0x7e802004 0x4>;
+		interrupts = <2 7>;
+		clocks = <&clocks BCM2835_CLOCK_CAM1>;
+		clock-names = "lp_clock";
+
+		port {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			csi1_ep: endpoint {
+				remote-endpoint = <&tc358743_0>;
+				data-lanes = <1 2>;
+			};
+		};
+	};
+
+	i2c0: i2c@7e205000 {
+
+		tc358743: csi-hdmi-bridge@0f {
+			compatible = "toshiba,tc358743";
+			reg = <0x0f>;
+			status = "okay";
+
+			clocks = <&tc358743_clk>;
+			clock-names = "refclk";
+
+			tc358743_clk: bridge-clk {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <27000000>;
+			};
+
+			port {
+				tc358743_0: endpoint {
+					remote-endpoint = <&csi1_ep>;
+					clock-lanes = <0>;
+					data-lanes = <1 2>;
+					clock-noncontinuous;
+					link-frequencies =
+						/bits/ 64 <297000000>;
+				};
+			};
+		};
+	};
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 3/4] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
       [not found] ` <cover.1505314390.git.dave.stevenson@raspberrypi.org>
  2017-09-13 15:07   ` [PATCH v2 1/4] [media] v4l2-common: Add helper function for fourcc to string Dave Stevenson
  2017-09-13 15:07   ` [PATCH v2 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver Dave Stevenson
@ 2017-09-13 15:07   ` Dave Stevenson
       [not found]     ` <0d4dc119a2ba4917e0fecfe5084425830ec53ccb.1505314390.git.dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
  2017-09-13 15:07   ` [PATCH v2 4/4] MAINTAINERS: Add entry for BCM2835 camera driver Dave Stevenson
  3 siblings, 1 reply; 20+ messages in thread
From: Dave Stevenson @ 2017-09-13 15:07 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Rob Herring, Hans Verkuil, Eric Anholt,
	Stefan Wahren, Sakari Ailus, linux-media, linux-rpi-kernel,
	devicetree
  Cc: Dave Stevenson

Add driver for the Unicam camera receiver block on
BCM283x processors.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
---
 drivers/media/platform/Kconfig                   |    1 +
 drivers/media/platform/Makefile                  |    1 +
 drivers/media/platform/bcm2835/Kconfig           |   14 +
 drivers/media/platform/bcm2835/Makefile          |    3 +
 drivers/media/platform/bcm2835/bcm2835-unicam.c  | 2192 ++++++++++++++++++++++
 drivers/media/platform/bcm2835/vc4-regs-unicam.h |  264 +++
 6 files changed, 2475 insertions(+)
 create mode 100644 drivers/media/platform/bcm2835/Kconfig
 create mode 100644 drivers/media/platform/bcm2835/Makefile
 create mode 100644 drivers/media/platform/bcm2835/bcm2835-unicam.c
 create mode 100644 drivers/media/platform/bcm2835/vc4-regs-unicam.h

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 7e7cc49..1e5f004 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -150,6 +150,7 @@ source "drivers/media/platform/am437x/Kconfig"
 source "drivers/media/platform/xilinx/Kconfig"
 source "drivers/media/platform/rcar-vin/Kconfig"
 source "drivers/media/platform/atmel/Kconfig"
+source "drivers/media/platform/bcm2835/Kconfig"
 
 config VIDEO_TI_CAL
 	tristate "TI CAL (Camera Adaptation Layer) driver"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index c1ef946..045de3f 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -90,3 +90,4 @@ obj-$(CONFIG_VIDEO_QCOM_CAMSS)		+= qcom/camss-8x16/
 obj-$(CONFIG_VIDEO_QCOM_VENUS)		+= qcom/venus/
 
 obj-y					+= meson/
+obj-y					+= bcm2835/
diff --git a/drivers/media/platform/bcm2835/Kconfig b/drivers/media/platform/bcm2835/Kconfig
new file mode 100644
index 0000000..6a74842
--- /dev/null
+++ b/drivers/media/platform/bcm2835/Kconfig
@@ -0,0 +1,14 @@
+# Broadcom VideoCore4 V4L2 camera support
+
+config VIDEO_BCM2835_UNICAM
+	tristate "Broadcom BCM2835 Unicam video capture driver"
+	depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	depends on ARCH_BCM2835 || COMPILE_TEST
+	select VIDEOBUF2_DMA_CONTIG
+	select V4L2_FWNODE
+	---help---
+	  Say Y here to enable V4L2 subdevice for CSI2 receiver.
+	  This is a V4L2 subdevice that interfaces directly to the VC4 peripheral.
+
+	   To compile this driver as a module, choose M here. The module
+	   will be called bcm2835-unicam.
diff --git a/drivers/media/platform/bcm2835/Makefile b/drivers/media/platform/bcm2835/Makefile
new file mode 100644
index 0000000..a98aba0
--- /dev/null
+++ b/drivers/media/platform/bcm2835/Makefile
@@ -0,0 +1,3 @@
+# Makefile for BCM2835 Unicam driver
+
+obj-$(CONFIG_VIDEO_BCM2835_UNICAM) += bcm2835-unicam.o
diff --git a/drivers/media/platform/bcm2835/bcm2835-unicam.c b/drivers/media/platform/bcm2835/bcm2835-unicam.c
new file mode 100644
index 0000000..5b1adc3
--- /dev/null
+++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
@@ -0,0 +1,2192 @@
+/*
+ * BCM2835 Unicam capture Driver
+ *
+ * Copyright (C) 2017 - Raspberry Pi (Trading) Ltd.
+ *
+ * Dave Stevenson <dave.stevenson@raspberrypi.org>
+ *
+ * Based on TI am437x driver by Benoit Parrot and Lad, Prabhakar and
+ * TI CAL camera interface driver by Benoit Parrot.
+ *
+ *
+ * There are two camera drivers in the kernel for BCM283x - this one
+ * and bcm2835-camera (currently in staging).
+ *
+ * This driver is purely the kernel control the Unicam peripheral - there
+ * is no involvement with the VideoCore firmware. Unicam receives CSI-2
+ * or CCP2 data and writes it into SDRAM. The only processing options are
+ * to repack Bayer data into an alternate format, and applying windowing
+ * (currently not implemented).
+ * It should be possible to connect it to any sensor with a
+ * suitable output interface and V4L2 subdevice driver.
+ *
+ * bcm2835-camera uses with the VideoCore firmware to control the sensor,
+ * Unicam, ISP, and all tuner control loops. Fully processed frames are
+ * delivered to the driver by the firmware. It only has sensor drivers
+ * for Omnivision OV5647, and Sony IMX219 sensors.
+ *
+ * The two drivers are mutually exclusive for the same Unicam instance.
+ * The VideoCore firmware checks the device tree configuration during boot.
+ * If it finds device tree nodes called csi0 or csi1 it will block the
+ * firmware from accessing the peripheral, and bcm2835-camera will
+ * not be able to stream data.
+ *
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fwnode.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "vc4-regs-unicam.h"
+
+#define UNICAM_MODULE_NAME	"unicam"
+#define UNICAM_VERSION		"0.1.0"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level 0-3");
+
+#define unicam_dbg(level, dev, fmt, arg...)	\
+		v4l2_dbg(level, debug, &(dev)->v4l2_dev, fmt, ##arg)
+#define unicam_info(dev, fmt, arg...)	\
+		v4l2_info(&(dev)->v4l2_dev, fmt, ##arg)
+#define unicam_err(dev, fmt, arg...)	\
+		v4l2_err(&(dev)->v4l2_dev, fmt, ##arg)
+
+/*
+ * Stride is a 16 bit register, but also has to be a multiple of 16.
+ */
+#define BPL_ALIGNMENT		16
+#define MAX_BYTESPERLINE	((1 << 16) - BPL_ALIGNMENT)
+/*
+ * Max width is therefore determined by the max stride divided by
+ * the number of bits per pixel. Take 32bpp as a
+ * worst case.
+ * No imposed limit on the height, so adopt a square image for want
+ * of anything better.
+ */
+#define MAX_WIDTH	(MAX_BYTESPERLINE / 4)
+#define MAX_HEIGHT	MAX_WIDTH
+/* Define a nominal minimum image size */
+#define MIN_WIDTH	16
+#define MIN_HEIGHT	16
+/*
+ * Whilst Unicam doesn't require any additional padding on the image
+ * height, various other parts of the BCM283x frameworks require a multiple
+ * of 16.
+ * Seeing as image buffers are significantly larger than this extra
+ * padding, add it in order to simplify integration.
+ */
+#define HEIGHT_ALIGNMENT	16
+
+/*
+ * struct unicam_fmt - Unicam media bus format information
+ * @pixelformat: V4L2 pixel format FCC identifier.
+ * @code: V4L2 media bus format code.
+ * @depth: Bits per pixel (when stored in memory).
+ * @csi_dt: CSI data type.
+ */
+struct unicam_fmt {
+	u32	fourcc;
+	u32	code;
+	u8	depth;
+	u8	csi_dt;
+};
+
+/*
+ * The peripheral can unpack and repack between several of
+ * the Bayer raw formats, so any Bayer format can be advertised
+ * as the same Bayer order in each of the supported bit depths.
+ * Use lower case to avoid clashing with V4L2_PIX_FMT_SGBRG8
+ * formats.
+ */
+#define PIX_FMT_ALL_BGGR  v4l2_fourcc('b', 'g', 'g', 'r')
+#define PIX_FMT_ALL_RGGB  v4l2_fourcc('r', 'g', 'g', 'b')
+#define PIX_FMT_ALL_GBRG  v4l2_fourcc('g', 'b', 'r', 'g')
+#define PIX_FMT_ALL_GRBG  v4l2_fourcc('g', 'r', 'b', 'g')
+
+static const struct unicam_fmt formats[] = {
+	/* YUV Formats */
+	{
+		.fourcc		= V4L2_PIX_FMT_YUYV,
+		.code		= MEDIA_BUS_FMT_YUYV8_2X8,
+		.depth		= 16,
+		.csi_dt		= 0x1e,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_UYVY,
+		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
+		.depth		= 16,
+		.csi_dt		= 0x1e,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_YVYU,
+		.code		= MEDIA_BUS_FMT_YVYU8_2X8,
+		.depth		= 16,
+		.csi_dt		= 0x1e,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_VYUY,
+		.code		= MEDIA_BUS_FMT_VYUY8_2X8,
+		.depth		= 16,
+		.csi_dt		= 0x1e,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_YUYV,
+		.code		= MEDIA_BUS_FMT_YUYV8_1X16,
+		.depth		= 16,
+		.csi_dt		= 0x1e,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_UYVY,
+		.code		= MEDIA_BUS_FMT_UYVY8_1X16,
+		.depth		= 16,
+		.csi_dt		= 0x1e,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_YVYU,
+		.code		= MEDIA_BUS_FMT_YVYU8_1X16,
+		.depth		= 16,
+		.csi_dt		= 0x1e,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_VYUY,
+		.code		= MEDIA_BUS_FMT_VYUY8_1X16,
+		.depth		= 16,
+		.csi_dt		= 0x1e,
+	}, {
+	/* RGB Formats */
+		.fourcc		= V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
+		.code		= MEDIA_BUS_FMT_RGB565_2X8_LE,
+		.depth		= 16,
+		.csi_dt		= 0x22,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
+		.code		= MEDIA_BUS_FMT_RGB565_2X8_BE,
+		.depth		= 16,
+		.csi_dt		= 0x22
+	}, {
+		.fourcc		= V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
+		.code		= MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
+		.depth		= 16,
+		.csi_dt		= 0x21,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
+		.code		= MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
+		.depth		= 16,
+		.csi_dt		= 0x21,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_RGB24, /* rgb */
+		.code		= MEDIA_BUS_FMT_RGB888_1X24,
+		.depth		= 24,
+		.csi_dt		= 0x24,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_BGR24, /* bgr */
+		.code		= MEDIA_BUS_FMT_BGR888_1X24,
+		.depth		= 24,
+		.csi_dt		= 0x24,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_RGB32, /* argb */
+		.code		= MEDIA_BUS_FMT_ARGB8888_1X32,
+		.depth		= 32,
+		.csi_dt		= 0x0,
+	}, {
+	/* Bayer Formats */
+		.fourcc		= PIX_FMT_ALL_BGGR,
+		.code		= MEDIA_BUS_FMT_SBGGR8_1X8,
+		.depth		= 8,
+		.csi_dt		= 0x2a,
+	}, {
+		.fourcc		= PIX_FMT_ALL_GBRG,
+		.code		= MEDIA_BUS_FMT_SGBRG8_1X8,
+		.depth		= 8,
+		.csi_dt		= 0x2a,
+	}, {
+		.fourcc		= PIX_FMT_ALL_GRBG,
+		.code		= MEDIA_BUS_FMT_SGRBG8_1X8,
+		.depth		= 8,
+		.csi_dt		= 0x2a,
+	}, {
+		.fourcc		= PIX_FMT_ALL_RGGB,
+		.code		= MEDIA_BUS_FMT_SRGGB8_1X8,
+		.depth		= 8,
+		.csi_dt		= 0x2a,
+	}, {
+		.fourcc		= PIX_FMT_ALL_BGGR,
+		.code		= MEDIA_BUS_FMT_SBGGR10_1X10,
+		.depth		= 10,
+		.csi_dt		= 0x2b,
+	}, {
+		.fourcc		= PIX_FMT_ALL_GBRG,
+		.code		= MEDIA_BUS_FMT_SGBRG10_1X10,
+		.depth		= 10,
+		.csi_dt		= 0x2b,
+	}, {
+		.fourcc		= PIX_FMT_ALL_GRBG,
+		.code		= MEDIA_BUS_FMT_SGRBG10_1X10,
+		.depth		= 10,
+		.csi_dt		= 0x2b,
+	}, {
+		.fourcc		= PIX_FMT_ALL_RGGB,
+		.code		= MEDIA_BUS_FMT_SRGGB10_1X10,
+		.depth		= 10,
+		.csi_dt		= 0x2b,
+	}, {
+		.fourcc		= PIX_FMT_ALL_BGGR,
+		.code		= MEDIA_BUS_FMT_SBGGR12_1X12,
+		.depth		= 12,
+		.csi_dt		= 0x2c,
+	}, {
+		.fourcc		= PIX_FMT_ALL_GBRG,
+		.code		= MEDIA_BUS_FMT_SGBRG12_1X12,
+		.depth		= 12,
+		.csi_dt		= 0x2c,
+	}, {
+		.fourcc		= PIX_FMT_ALL_GRBG,
+		.code		= MEDIA_BUS_FMT_SGRBG12_1X12,
+		.depth		= 12,
+		.csi_dt		= 0x2c,
+	}, {
+		.fourcc		= PIX_FMT_ALL_RGGB,
+		.code		= MEDIA_BUS_FMT_SRGGB12_1X12,
+		.depth		= 12,
+		.csi_dt		= 0x2c,
+	}, {
+		.fourcc		= PIX_FMT_ALL_BGGR,
+		.code		= MEDIA_BUS_FMT_SBGGR14_1X14,
+		.depth		= 14,
+		.csi_dt		= 0x2d,
+	}, {
+		.fourcc		= PIX_FMT_ALL_GBRG,
+		.code		= MEDIA_BUS_FMT_SGBRG14_1X14,
+		.depth		= 14,
+		.csi_dt		= 0x2d,
+	}, {
+		.fourcc		= PIX_FMT_ALL_GRBG,
+		.code		= MEDIA_BUS_FMT_SGRBG14_1X14,
+		.depth		= 14,
+		.csi_dt		= 0x2d,
+	}, {
+		.fourcc		= PIX_FMT_ALL_RGGB,
+		.code		= MEDIA_BUS_FMT_SRGGB14_1X14,
+		.depth		= 14,
+		.csi_dt		= 0x2d,
+	},
+};
+
+struct bayer_fmt {
+	u32 fourcc;
+	u8 depth;
+};
+
+const struct bayer_fmt all_bayer_bggr[] = {
+	{V4L2_PIX_FMT_SBGGR8,	8},
+	{V4L2_PIX_FMT_SBGGR10P,	10},
+	{V4L2_PIX_FMT_SBGGR12,	12},
+	{V4L2_PIX_FMT_SBGGR16,	16},
+	{0,			0}
+};
+
+const struct bayer_fmt all_bayer_rggb[] = {
+	{V4L2_PIX_FMT_SRGGB8,	8},
+	{V4L2_PIX_FMT_SRGGB10P,	10},
+	{V4L2_PIX_FMT_SRGGB12,	12},
+	{V4L2_PIX_FMT_SRGGB16,	16},
+	{0,			0}
+};
+
+const struct bayer_fmt all_bayer_gbrg[] = {
+	{V4L2_PIX_FMT_SGBRG8,	8},
+	{V4L2_PIX_FMT_SGBRG10P,	10},
+	{V4L2_PIX_FMT_SGBRG12,	12},
+	{V4L2_PIX_FMT_SGBRG16,	16},
+	{0,			0}
+};
+
+const struct bayer_fmt all_bayer_grbg[] = {
+	{V4L2_PIX_FMT_SGRBG8,	8},
+	{V4L2_PIX_FMT_SGRBG10P,	10},
+	{V4L2_PIX_FMT_SGRBG12,	12},
+	{V4L2_PIX_FMT_SGRBG16,	16},
+	{0,			0}
+};
+
+struct unicam_dmaqueue {
+	struct list_head	active;
+};
+
+struct unicam_buffer {
+	struct vb2_v4l2_buffer vb;
+	struct list_head list;
+};
+
+struct unicam_cfg {
+	/* peripheral base address */
+	void __iomem *base;
+	/* clock gating base address */
+	void __iomem *clk_gate_base;
+	unsigned int irq;
+};
+
+#define MAX_POSSIBLE_FMTS \
+		(ARRAY_SIZE(formats) + \
+		ARRAY_SIZE(all_bayer_bggr) + \
+		ARRAY_SIZE(all_bayer_rggb) + \
+		ARRAY_SIZE(all_bayer_grbg) + \
+		ARRAY_SIZE(all_bayer_gbrg))
+
+struct unicam_device {
+	/* V4l2 specific parameters */
+	/* Identifies video device for this channel */
+	struct video_device video_dev;
+	struct v4l2_ctrl_handler ctrl_handler;
+
+	struct v4l2_fwnode_endpoint endpoint;
+
+	struct v4l2_async_subdev asd;
+
+	/* unicam cfg */
+	struct unicam_cfg cfg;
+	/* clock handle */
+	struct clk *clock;
+	/* V4l2 device */
+	struct v4l2_device v4l2_dev;
+	/* parent device */
+	struct platform_device *pdev;
+	/* subdevice async Notifier */
+	struct v4l2_async_notifier notifier;
+	unsigned int sequence;
+
+	/* ptr to  sub device */
+	struct v4l2_subdev *sensor;
+	/* Pad config for the sensor */
+	struct v4l2_subdev_pad_config *sensor_config;
+	/* current input at the sub device */
+	int current_input;
+
+	/* Pointer pointing to current v4l2_buffer */
+	struct unicam_buffer *cur_frm;
+	/* Pointer pointing to next v4l2_buffer */
+	struct unicam_buffer *next_frm;
+
+	/* video capture */
+	const struct unicam_fmt	*fmt;
+	/* Used to store current pixel format */
+	struct v4l2_format v_fmt;
+	/* Used to store current mbus frame format */
+	struct v4l2_mbus_framefmt m_fmt;
+
+	struct unicam_fmt active_fmts[MAX_POSSIBLE_FMTS];
+	int num_active_fmt;
+	unsigned int virtual_channel;
+	enum v4l2_mbus_type bus_type;
+	/*
+	 * Stores bus.mipi_csi2.flags for CSI2 sensors, or
+	 * bus.mipi_csi1.strobe for CCP2.
+	 */
+	unsigned int bus_flags;
+	unsigned int max_data_lanes;
+	unsigned int active_data_lanes;
+
+	struct v4l2_rect crop;
+
+	/* Currently selected input on subdev */
+	int input;
+
+	/* Buffer queue used in video-buf */
+	struct vb2_queue buffer_queue;
+	/* Queue of filled frames */
+	struct unicam_dmaqueue dma_queue;
+	/* IRQ lock for DMA queue */
+	spinlock_t dma_queue_lock;
+	/* lock used to access this structure */
+	struct mutex lock;
+	/* Flag to denote that we are processing buffers */
+	int streaming;
+};
+
+/* Hardware access */
+#define clk_write(dev, val) writel((val) | 0x5a000000, (dev)->clk_gate_base)
+#define clk_read(dev) readl((dev)->clk_gate_base)
+
+#define reg_read(dev, offset) readl((dev)->base + (offset))
+#define reg_write(dev, offset, val) writel(val, (dev)->base + (offset))
+
+#define reg_read_field(dev, offset, mask) get_field(reg_read((dev), (offset), \
+						    mask))
+
+static inline int get_field(u32 value, u32 mask)
+{
+	return (value & mask) >> __ffs(mask);
+}
+
+static inline void set_field(u32 *valp, u32 field, u32 mask)
+{
+	u32 val = *valp;
+
+	val &= ~mask;
+	val |= (field << __ffs(mask)) & mask;
+	*valp = val;
+}
+
+static inline void reg_write_field(struct unicam_cfg *dev, u32 offset,
+				   u32 field, u32 mask)
+{
+	u32 val = reg_read((dev), (offset));
+
+	set_field(&val, field, mask);
+	reg_write((dev), (offset), val);
+}
+
+/* Power management functions */
+static inline int unicam_runtime_get(struct unicam_device *dev)
+{
+	int r;
+
+	r = pm_runtime_get_sync(&dev->pdev->dev);
+
+	return r;
+}
+
+static inline void unicam_runtime_put(struct unicam_device *dev)
+{
+	pm_runtime_put_sync(&dev->pdev->dev);
+}
+
+/* Format setup functions */
+static int find_mbus_depth_by_code(u32 code)
+{
+	const struct unicam_fmt *fmt;
+	unsigned int k;
+
+	for (k = 0; k < ARRAY_SIZE(formats); k++) {
+		fmt = &formats[k];
+		if (fmt->code == code)
+			return fmt->depth;
+	}
+
+	return 0;
+}
+
+static const struct unicam_fmt *find_format_by_code(struct unicam_device *dev,
+						    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;
+	}
+
+	return NULL;
+}
+
+static const struct unicam_fmt *find_format_by_pix(struct unicam_device *dev,
+						   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;
+	}
+
+	return NULL;
+}
+
+static void dump_active_formats(struct unicam_device *dev)
+{
+	int i;
+	char fourcc_str[V4L2_FOURCC_MAX_SIZE];
+
+	for (i = 0; i < dev->num_active_fmt; i++) {
+		unicam_dbg(3, dev, "active_fmt[%d] (%p) is code %04X, fourcc %s, depth %d\n",
+			   i, &dev->active_fmts[i], dev->active_fmts[i].code,
+			   v4l2_fourcc2s(dev->active_fmts[i].fourcc,
+					 fourcc_str),
+			   dev->active_fmts[i].depth);
+	}
+}
+
+static inline unsigned int bytes_per_line(u32 width,
+					  const struct unicam_fmt *fmt)
+{
+	return ALIGN((width * fmt->depth) >> 3,  BPL_ALIGNMENT);
+}
+
+static int __subdev_get_format(struct unicam_device *dev,
+			       struct v4l2_mbus_framefmt *fmt)
+{
+	struct v4l2_subdev_format sd_fmt = {0};
+	struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+	int ret;
+
+	sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	sd_fmt.pad = 0;
+
+	ret = v4l2_subdev_call(dev->sensor, pad, get_fmt, dev->sensor_config,
+			       &sd_fmt);
+	if (ret < 0)
+		return ret;
+
+	if (mbus_fmt->field != V4L2_FIELD_NONE) {
+		/*
+		 * No support for receiving interlaced video, so try to
+		 * disable it if reported.
+		 */
+		mbus_fmt->field = V4L2_FIELD_NONE;
+		ret = v4l2_subdev_call(dev->sensor, pad, set_fmt,
+				       dev->sensor_config,
+				       &sd_fmt);
+	}
+	*fmt = *mbus_fmt;
+
+	unicam_dbg(1, dev, "%s %dx%d code:%04X\n", __func__,
+		   fmt->width, fmt->height, fmt->code);
+
+	return 0;
+}
+
+static int __subdev_set_format(struct unicam_device *dev,
+			       struct v4l2_mbus_framefmt *fmt)
+{
+	struct v4l2_subdev_format sd_fmt = {
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+	};
+	struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+	int ret;
+
+	*mbus_fmt = *fmt;
+
+	ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_config,
+			       &sd_fmt);
+	if (ret < 0)
+		return ret;
+
+	unicam_dbg(1, dev, "%s %dx%d code:%04X\n", __func__,
+		   fmt->width, fmt->height, fmt->code);
+
+	return 0;
+}
+
+static int unicam_calc_format_size_bpl(struct unicam_device *dev,
+				       const struct unicam_fmt *fmt,
+				       struct v4l2_format *f)
+{
+	unsigned int min_bytesperline;
+	char fourcc_str[V4L2_FOURCC_MAX_SIZE];
+
+	if (!fmt) {
+		unicam_dbg(3, dev, "No unicam_fmt provided!\n");
+		return -EINVAL;
+	}
+
+	v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 2,
+			      &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 0,
+			      0);
+
+	min_bytesperline = bytes_per_line(f->fmt.pix.width, fmt);
+
+	if (f->fmt.pix.bytesperline > min_bytesperline &&
+	    f->fmt.pix.bytesperline <= MAX_BYTESPERLINE)
+		f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.bytesperline,
+						BPL_ALIGNMENT);
+	else
+		f->fmt.pix.bytesperline = min_bytesperline;
+
+	/* Align height up for compatibility with other hardware blocks */
+	f->fmt.pix.sizeimage = ALIGN(f->fmt.pix.height, HEIGHT_ALIGNMENT) *
+			       f->fmt.pix.bytesperline;
+
+	unicam_dbg(3, dev, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n",
+		   __func__, v4l2_fourcc2s(f->fmt.pix.pixelformat, fourcc_str),
+		   f->fmt.pix.width, f->fmt.pix.height,
+		   f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+
+	return 0;
+}
+
+static int unicam_reset_format(struct unicam_device *dev)
+{
+	struct v4l2_mbus_framefmt mbus_fmt;
+	int ret;
+
+	ret = __subdev_get_format(dev, &mbus_fmt);
+	if (ret) {
+		unicam_err(dev, "Failed to get_format - ret %d\n", ret);
+		return ret;
+	}
+
+	if (mbus_fmt.code != dev->fmt->code) {
+		unicam_err(dev, "code mismatch - fmt->code %08X, mbus_fmt.code %08X\n",
+			   dev->fmt->code, mbus_fmt.code);
+		return ret;
+	}
+
+	v4l2_fill_pix_format(&dev->v_fmt.fmt.pix, &mbus_fmt);
+	dev->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+	unicam_calc_format_size_bpl(dev, dev->fmt, &dev->v_fmt);
+
+	dev->m_fmt = mbus_fmt;
+
+	return 0;
+}
+
+static void unicam_wr_dma_addr(struct unicam_device *dev, unsigned int dmaaddr)
+{
+	unicam_dbg(1, dev, "wr_dma_addr %08X-%08X\n",
+		   dmaaddr, dmaaddr + dev->v_fmt.fmt.pix.sizeimage);
+	reg_write(&dev->cfg, UNICAM_IBSA0, dmaaddr);
+	reg_write(&dev->cfg,
+		  UNICAM_IBEA0,
+		  dmaaddr + dev->v_fmt.fmt.pix.sizeimage);
+	reg_write(&dev->cfg, UNICAM_DBSA0, (uint32_t)dmaaddr);
+	reg_write(&dev->cfg, UNICAM_DBEA0, (uint32_t)dmaaddr + (16 << 10));
+}
+
+static inline void unicam_schedule_next_buffer(struct unicam_device *dev)
+{
+	struct unicam_dmaqueue *dma_q = &dev->dma_queue;
+	struct unicam_buffer *buf;
+	unsigned long addr;
+
+	buf = list_entry(dma_q->active.next, struct unicam_buffer, list);
+	dev->next_frm = buf;
+	list_del(&buf->list);
+
+	addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+	unicam_wr_dma_addr(dev, addr);
+}
+
+static inline void unicam_process_buffer_complete(struct unicam_device *dev)
+{
+	dev->cur_frm->vb.field = dev->m_fmt.field;
+	dev->cur_frm->vb.sequence = dev->sequence++;
+
+	vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
+	dev->cur_frm = dev->next_frm;
+}
+
+/*
+ * unicam_isr : ISR handler for unicam capture
+ * @irq: irq number
+ * @dev_id: dev_id ptr
+ *
+ * It changes status of the captured buffer, takes next buffer from the queue
+ * and sets its address in unicam registers
+ */
+static irqreturn_t unicam_isr(int irq, void *dev)
+{
+	struct unicam_device *unicam = (struct unicam_device *)dev;
+	int ista, sta;
+	struct unicam_cfg *cfg = &unicam->cfg;
+	struct unicam_dmaqueue *dma_q = &unicam->dma_queue;
+
+	/*
+	 * Don't service interrupts if not streaming.
+	 * Avoids issues if the VPU should enable the
+	 * peripheral without the kernel knowing (that
+	 * shouldn't happen, but causes issues if it does).
+	 */
+	if (!unicam->streaming)
+		return IRQ_HANDLED;
+
+	sta = reg_read(cfg, UNICAM_STA);
+	/* Write value back to clear the interrupts */
+	reg_write(cfg, UNICAM_STA, sta);
+
+	ista = reg_read(cfg, UNICAM_ISTA);
+	/* Write value back to clear the interrupts */
+	reg_write(cfg, UNICAM_ISTA, ista);
+
+	if (!(sta && (UNICAM_IS | UNICAM_PI0)))
+		return IRQ_HANDLED;
+
+	if (ista & UNICAM_FSI) {
+		/*
+		 * Timestamp is to be when the first data byte was captured,
+		 * aka frame start.
+		 */
+		if (unicam->cur_frm)
+			unicam->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns();
+	}
+	if (ista & UNICAM_FEI || sta & UNICAM_PI0) {
+		/*
+		 * Ensure we have swapped buffers already as we can't
+		 * stop the peripheral. Overwrite the frame we've just
+		 * captured instead.
+		 */
+		if (unicam->cur_frm &&
+		    unicam->cur_frm != unicam->next_frm)
+			unicam_process_buffer_complete(unicam);
+	}
+
+	if (ista & (UNICAM_FSI | UNICAM_LCI)) {
+		spin_lock(&unicam->dma_queue_lock);
+		if (!list_empty(&dma_q->active) &&
+		    unicam->cur_frm == unicam->next_frm)
+			unicam_schedule_next_buffer(unicam);
+		spin_unlock(&unicam->dma_queue_lock);
+	}
+
+	if (reg_read(&unicam->cfg, UNICAM_ICTL) & UNICAM_FCM) {
+		/* Switch out of trigger mode if selected */
+		reg_write_field(&unicam->cfg, UNICAM_ICTL, 1, UNICAM_TFC);
+		reg_write_field(&unicam->cfg, UNICAM_ICTL, 0, UNICAM_FCM);
+	}
+	return IRQ_HANDLED;
+}
+
+static int unicam_querycap(struct file *file, void *priv,
+			   struct v4l2_capability *cap)
+{
+	struct unicam_device *dev = video_drvdata(file);
+
+	strlcpy(cap->driver, UNICAM_MODULE_NAME, sizeof(cap->driver));
+	strlcpy(cap->card, UNICAM_MODULE_NAME, sizeof(cap->card));
+
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+		 "platform:%s", dev->v4l2_dev.name);
+
+	return 0;
+}
+
+static int unicam_enum_fmt_vid_cap(struct file *file, void  *priv,
+				   struct v4l2_fmtdesc *f)
+{
+	struct unicam_device *dev = video_drvdata(file);
+	const struct unicam_fmt *fmt = NULL;
+
+	if (f->index >= dev->num_active_fmt)
+		return -EINVAL;
+
+	fmt = &dev->active_fmts[f->index];
+
+	f->pixelformat = fmt->fourcc;
+
+	return 0;
+}
+
+static int unicam_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct unicam_device *dev = video_drvdata(file);
+
+	*f = dev->v_fmt;
+
+	return 0;
+}
+
+static int unicam_try_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct unicam_device *dev = video_drvdata(file);
+	const struct unicam_fmt *fmt;
+	struct v4l2_subdev_format sd_fmt = {
+		.which = V4L2_SUBDEV_FORMAT_TRY,
+	};
+	struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+	int ret;
+
+	fmt = find_format_by_pix(dev, 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);
+
+		/* Just get the first one enumerated */
+		fmt = &dev->active_fmts[0];
+		f->fmt.pix.pixelformat = fmt->fourcc;
+	}
+
+	v4l2_fill_mbus_format(mbus_fmt, &f->fmt.pix, fmt->code);
+	/*
+	 * No support for receiving interlaced video, so never
+	 * request it from the sensor subdev.
+	 */
+	mbus_fmt->field = V4L2_FIELD_NONE;
+
+	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);
+	/*
+	 * Use current colorspace for now, it will get
+	 * updated properly during s_fmt
+	 */
+	f->fmt.pix.colorspace = dev->v_fmt.fmt.pix.colorspace;
+	return unicam_calc_format_size_bpl(dev, fmt, f);
+}
+
+static int unicam_s_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct unicam_device *dev = video_drvdata(file);
+	struct vb2_queue *q = &dev->buffer_queue;
+	const struct unicam_fmt *fmt;
+	struct v4l2_mbus_framefmt mbus_fmt = {0};
+	int ret;
+	char fourcc_str[V4L2_FOURCC_MAX_SIZE];
+
+	if (vb2_is_busy(q))
+		return -EBUSY;
+
+	ret = unicam_try_fmt_vid_cap(file, priv, f);
+	if (ret < 0)
+		return ret;
+
+	fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat);
+	if (!fmt) {
+		/* Unknown pixel format - adopt a default */
+		fmt = &dev->active_fmts[0];
+		f->fmt.pix.pixelformat = fmt->fourcc;
+		return -EINVAL;
+	}
+
+	v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code);
+
+	ret = __subdev_set_format(dev, &mbus_fmt);
+	if (ret) {
+		unicam_dbg(3, dev,
+			   "%s __subdev_set_format failed %d\n",
+			   __func__, ret);
+		return ret;
+	}
+
+	/* Just double check nothing has gone wrong */
+	if (mbus_fmt.code != fmt->code) {
+		unicam_dbg(3, dev,
+			   "%s subdev changed format on us, this should not happen\n",
+			   __func__);
+		return -EINVAL;
+	}
+
+	dev->fmt = fmt;
+	dev->v_fmt.fmt.pix.pixelformat = f->fmt.pix.pixelformat;
+	dev->v_fmt.fmt.pix.bytesperline = f->fmt.pix.bytesperline;
+	unicam_reset_format(dev);
+
+	unicam_dbg(3, dev,
+		   "%s %dx%d, mbus_fmt %08X, V4L2 pix %s.\n",
+		   __func__,
+		   dev->v_fmt.fmt.pix.width,
+		   dev->v_fmt.fmt.pix.height,
+		   mbus_fmt.code,
+		   v4l2_fourcc2s(dev->v_fmt.fmt.pix.pixelformat,
+				 fourcc_str));
+
+	*f = dev->v_fmt;
+
+	return 0;
+}
+
+static int unicam_queue_setup(struct vb2_queue *vq,
+			      unsigned int *nbuffers,
+			      unsigned int *nplanes,
+			      unsigned int sizes[],
+			      struct device *alloc_devs[])
+{
+	struct unicam_device *dev = vb2_get_drv_priv(vq);
+	unsigned int size = dev->v_fmt.fmt.pix.sizeimage;
+
+	if (vq->num_buffers + *nbuffers < 3)
+		*nbuffers = 3 - vq->num_buffers;
+
+	if (*nplanes) {
+		if (sizes[0] < size) {
+			unicam_err(dev, "sizes[0] %i < size %u\n",
+				   sizes[0], size);
+			return -EINVAL;
+		}
+		size = sizes[0];
+	}
+
+	*nplanes = 1;
+	sizes[0] = size;
+
+	return 0;
+}
+
+static int unicam_buffer_prepare(struct vb2_buffer *vb)
+{
+	struct unicam_device *dev = vb2_get_drv_priv(vb->vb2_queue);
+	struct unicam_buffer *buf = container_of(vb, struct unicam_buffer,
+					      vb.vb2_buf);
+	unsigned long size;
+
+	if (WARN_ON(!dev->fmt))
+		return -EINVAL;
+
+	size = dev->v_fmt.fmt.pix.sizeimage;
+	if (vb2_plane_size(vb, 0) < size) {
+		unicam_err(dev, "data will not fit into plane (%lu < %lu)\n",
+			   vb2_plane_size(vb, 0), size);
+		return -EINVAL;
+	}
+
+	vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
+	return 0;
+}
+
+static void unicam_buffer_queue(struct vb2_buffer *vb)
+{
+	struct unicam_device *dev = vb2_get_drv_priv(vb->vb2_queue);
+	struct unicam_buffer *buf = container_of(vb, struct unicam_buffer,
+					      vb.vb2_buf);
+	struct unicam_dmaqueue *dma_queue = &dev->dma_queue;
+	unsigned long flags = 0;
+
+	/* recheck locking */
+	spin_lock_irqsave(&dev->dma_queue_lock, flags);
+	list_add_tail(&buf->list, &dma_queue->active);
+	spin_unlock_irqrestore(&dev->dma_queue_lock, flags);
+}
+
+static void unicam_wr_dma_config(struct unicam_device *dev,
+				 unsigned int stride)
+{
+	reg_write(&dev->cfg, UNICAM_IBLS, stride);
+}
+
+static void unicam_set_packing_config(struct unicam_device *dev)
+{
+	int pack, unpack;
+	u32 val;
+	int mbus_depth = find_mbus_depth_by_code(dev->fmt->code);
+	int v4l2_depth = dev->fmt->depth;
+
+	if (mbus_depth == v4l2_depth) {
+		unpack = UNICAM_PUM_NONE;
+		pack = UNICAM_PPM_NONE;
+	} else {
+		switch (mbus_depth) {
+		case 8:
+			unpack = UNICAM_PUM_UNPACK8;
+			break;
+		case 10:
+			unpack = UNICAM_PUM_UNPACK10;
+			break;
+		case 12:
+			unpack = UNICAM_PUM_UNPACK12;
+			break;
+		case 14:
+			unpack = UNICAM_PUM_UNPACK14;
+			break;
+		case 16:
+			unpack = UNICAM_PUM_UNPACK16;
+			break;
+		default:
+			unpack = UNICAM_PUM_NONE;
+			break;
+		}
+		switch (v4l2_depth) {
+		case 8:
+			pack = UNICAM_PPM_PACK8;
+			break;
+		case 10:
+			pack = UNICAM_PPM_PACK10;
+			break;
+		case 12:
+			pack = UNICAM_PPM_PACK12;
+			break;
+		case 14:
+			pack = UNICAM_PPM_PACK14;
+			break;
+		case 16:
+			pack = UNICAM_PPM_PACK16;
+			break;
+		default:
+			pack = UNICAM_PPM_NONE;
+			break;
+		}
+	}
+
+	val = 0;
+	set_field(&val, 2, UNICAM_DEBL_MASK);
+	set_field(&val, unpack, UNICAM_PUM_MASK);
+	set_field(&val, pack, UNICAM_PPM_MASK);
+	reg_write(&dev->cfg, UNICAM_IPIPE, val);
+}
+
+static void unicam_cfg_image_id(struct unicam_device *dev)
+{
+	struct unicam_cfg *cfg = &dev->cfg;
+
+	if (dev->bus_type == V4L2_MBUS_CSI2) {
+		/* CSI2 mode */
+		reg_write(cfg, UNICAM_IDI0,
+			  (dev->virtual_channel << 6) |
+			  dev->fmt->csi_dt);
+	} else {
+		/* CCP2 mode */
+		reg_write(cfg, UNICAM_IDI0,
+			  (0x80 | dev->fmt->csi_dt));
+	}
+}
+
+void unicam_start_rx(struct unicam_device *dev, unsigned long addr)
+{
+	u32 val;
+	unsigned int i;
+	struct unicam_cfg *cfg = &dev->cfg;
+	int line_int_freq = dev->v_fmt.fmt.pix.height >> 2;
+
+	if (line_int_freq < 128)
+		line_int_freq = 128;
+
+	/* Enable lane clocks */
+	val = 1;
+	for (i = 0; i < dev->active_data_lanes; i++)
+		val = val << 2 | 1;
+	clk_write(cfg, val);
+
+	/* Basic init */
+	reg_write(cfg, UNICAM_CTRL, UNICAM_MEM);
+
+	/* Enable analogue control, and leave in reset. */
+	val = UNICAM_AR;
+	set_field(&val, 7, UNICAM_CTATADJ_MASK);
+	set_field(&val, 7, UNICAM_PTATADJ_MASK);
+	reg_write(cfg, UNICAM_ANA, val);
+	usleep_range(1000, 2000);
+
+	/* Come out of reset */
+	reg_write_field(cfg, UNICAM_ANA, 0, UNICAM_AR);
+
+	/* Peripheral reset */
+	reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_CPR);
+	reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPR);
+
+	reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPE);
+
+	/* Enable Rx control. */
+	val = reg_read(cfg, UNICAM_CTRL);
+	if (dev->bus_type == V4L2_MBUS_CSI2) {
+		set_field(&val, UNICAM_CPM_CSI2, UNICAM_CPM_MASK);
+		set_field(&val, UNICAM_DCM_STROBE, UNICAM_DCM_MASK);
+	} else {
+		set_field(&val, UNICAM_CPM_CCP2, UNICAM_CPM_MASK);
+		set_field(&val, dev->bus_flags, UNICAM_DCM_MASK);
+	}
+	set_field(&val, 0xF, UNICAM_PFT_MASK);
+	set_field(&val, 128, UNICAM_OET_MASK);
+	reg_write(cfg, UNICAM_CTRL, val);
+
+	reg_write(cfg, UNICAM_IHWIN, 0);
+	reg_write(cfg, UNICAM_IVWIN, 0);
+
+	val = reg_read(&dev->cfg, UNICAM_PRI);
+	set_field(&val, 0, UNICAM_BL_MASK);
+	set_field(&val, 0, UNICAM_BS_MASK);
+	set_field(&val, 0xE, UNICAM_PP_MASK);
+	set_field(&val, 8, UNICAM_NP_MASK);
+	set_field(&val, 2, UNICAM_PT_MASK);
+	set_field(&val, 1, UNICAM_PE);
+	reg_write(cfg, UNICAM_PRI, val);
+
+	reg_write_field(cfg, UNICAM_ANA, 0, UNICAM_DDL);
+
+	/* Always start in trigger frame capture mode (UNICAM_FCM set) */
+	val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_FCM;
+	set_field(&val,  line_int_freq, UNICAM_LCIE_MASK);
+	reg_write(cfg, UNICAM_ICTL, val);
+	reg_write(cfg, UNICAM_STA, UNICAM_STA_MASK_ALL);
+	reg_write(cfg, UNICAM_ISTA, UNICAM_ISTA_MASK_ALL);
+
+	/* tclk_term_en */
+	reg_write_field(cfg, UNICAM_CLT, 2, UNICAM_CLT1_MASK);
+	/* tclk_settle */
+	reg_write_field(cfg, UNICAM_CLT, 6, UNICAM_CLT2_MASK);
+	/* td_term_en */
+	reg_write_field(cfg, UNICAM_DLT, 2, UNICAM_DLT1_MASK);
+	/* ths_settle */
+	reg_write_field(cfg, UNICAM_DLT, 6, UNICAM_DLT2_MASK);
+	/* trx_enable */
+	reg_write_field(cfg, UNICAM_DLT, 0, UNICAM_DLT3_MASK);
+
+	reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_SOE);
+
+	val = 0;
+	set_field(&val, 1, UNICAM_PCE);
+	set_field(&val, 1, UNICAM_GI);
+	set_field(&val, 1, UNICAM_CPH);
+	set_field(&val, 0, UNICAM_PCVC_MASK);
+	set_field(&val, 1, UNICAM_PCDT_MASK);
+	reg_write(cfg, UNICAM_CMP0, val);
+
+	/* Enable clock lane */
+	val = 0;
+	if (dev->bus_type == V4L2_MBUS_CSI2) {
+		/* CSI2 */
+		set_field(&val, 1, UNICAM_CLE);
+		set_field(&val, 1, UNICAM_CLLPE);
+		if (dev->bus_flags & V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) {
+			set_field(&val, 1, UNICAM_CLTRE);
+			set_field(&val, 1, UNICAM_CLHSE);
+		}
+	} else {
+		/* CCP2 */
+		set_field(&val, 1, UNICAM_CLE);
+		set_field(&val, 1, UNICAM_CLHSE);
+		set_field(&val, 1, UNICAM_CLTRE);
+	}
+	reg_write(cfg, UNICAM_CLK, val);
+
+	/* Enable required data lanes */
+	val = 0;
+	if (dev->bus_type == V4L2_MBUS_CSI2) {
+		/* CSI2 */
+		set_field(&val, 1, UNICAM_DLE);
+		set_field(&val, 1, UNICAM_DLLPE);
+		if (dev->bus_flags & V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) {
+			set_field(&val, 1, UNICAM_DLTRE);
+			set_field(&val, 1, UNICAM_DLHSE);
+		}
+	} else {
+		/* CCP2 */
+		set_field(&val, 1, UNICAM_DLE);
+		set_field(&val, 1, UNICAM_DLHSE);
+		set_field(&val, 1, UNICAM_DLTRE);
+	}
+	reg_write(cfg, UNICAM_DAT0, val);
+
+	if (dev->active_data_lanes == 1)
+		val = 0;
+	reg_write(cfg, UNICAM_DAT1, val);
+
+	if (dev->max_data_lanes > 2) {
+		if (dev->active_data_lanes == 2)
+			val = 0;
+		reg_write(cfg, UNICAM_DAT2, val);
+
+		if (dev->active_data_lanes == 3)
+			val = 0;
+		reg_write(cfg, UNICAM_DAT3, val);
+	}
+
+	unicam_wr_dma_config(dev, dev->v_fmt.fmt.pix.bytesperline);
+	unicam_wr_dma_addr(dev, addr);
+	unicam_set_packing_config(dev);
+	unicam_cfg_image_id(dev);
+
+	val = 0;
+	set_field(&val, 0, UNICAM_EDL_MASK);
+	reg_write(cfg, UNICAM_DCS, val);
+
+	val = reg_read(cfg, UNICAM_MISC);
+	set_field(&val, 1, UNICAM_FL0);
+	set_field(&val, 1, UNICAM_FL1);
+	reg_write(cfg, UNICAM_MISC, val);
+
+	reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_CPE);
+
+	reg_write_field(cfg, UNICAM_ICTL, 1, UNICAM_LIP_MASK);
+
+	reg_write_field(cfg, UNICAM_DCS, 1, UNICAM_LDP);
+
+	/*
+	 * Enable trigger only for the first frame to
+	 * sync correctly to the FS from the source.
+	 */
+	reg_write_field(cfg, UNICAM_ICTL, 1, UNICAM_TFC);
+}
+
+static void unicam_disable(struct unicam_device *dev)
+{
+	struct unicam_cfg *cfg = &dev->cfg;
+
+	/* Analogue lane control disable */
+	reg_write_field(cfg, UNICAM_ANA, 1, UNICAM_DDL);
+
+	/* Stop the output engine */
+	reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_SOE);
+
+	/* Disable the data lanes. */
+	reg_write(cfg, UNICAM_DAT0, 0);
+	reg_write(cfg, UNICAM_DAT1, 0);
+
+	if (dev->max_data_lanes > 2) {
+		reg_write(cfg, UNICAM_DAT2, 0);
+		reg_write(cfg, UNICAM_DAT3, 0);
+	}
+
+	/* Peripheral reset */
+	reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_CPR);
+	usleep_range(50, 100);
+	reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPR);
+
+	/* Disable peripheral */
+	reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPE);
+
+	/* Disable all lane clocks */
+	clk_write(cfg, 0);
+}
+
+static int unicam_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct unicam_device *dev = vb2_get_drv_priv(vq);
+	struct unicam_dmaqueue *dma_q = &dev->dma_queue;
+	struct unicam_buffer *buf, *tmp;
+	unsigned long addr = 0;
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&dev->dma_queue_lock, flags);
+	buf = list_entry(dma_q->active.next, struct unicam_buffer, list);
+	dev->cur_frm = buf;
+	dev->next_frm = buf;
+	list_del(&buf->list);
+	spin_unlock_irqrestore(&dev->dma_queue_lock, flags);
+
+	addr = vb2_dma_contig_plane_dma_addr(&dev->cur_frm->vb.vb2_buf, 0);
+	dev->sequence = 0;
+
+	ret = unicam_runtime_get(dev);
+	if (ret < 0) {
+		unicam_dbg(3, dev, "unicam_runtime_get failed\n");
+		goto err_release_buffers;
+	}
+
+	dev->active_data_lanes = dev->max_data_lanes;
+	if (dev->bus_type == V4L2_MBUS_CSI2 &&
+	    v4l2_subdev_has_op(dev->sensor, video, g_mbus_config)) {
+		struct v4l2_mbus_config mbus_config;
+
+		ret = v4l2_subdev_call(dev->sensor, video, g_mbus_config,
+				       &mbus_config);
+		if (ret < 0) {
+			unicam_dbg(3, dev, "g_mbus_config failed\n");
+			goto err_pm_put;
+		}
+
+		switch (mbus_config.flags & V4L2_MBUS_CSI2_LANES) {
+		case V4L2_MBUS_CSI2_1_LANE:
+			dev->active_data_lanes = 1;
+			break;
+		case V4L2_MBUS_CSI2_2_LANE:
+			dev->active_data_lanes = 2;
+			break;
+		case V4L2_MBUS_CSI2_3_LANE:
+			dev->active_data_lanes = 3;
+			break;
+		case V4L2_MBUS_CSI2_4_LANE:
+			dev->active_data_lanes = 4;
+			break;
+		default:
+			unicam_err(dev, "Invalid CSI2 lane flag value - %X\n",
+				   mbus_config.flags & V4L2_MBUS_CSI2_LANES);
+			break;
+		}
+
+		if ((mbus_config.flags ^ dev->bus_flags) &
+					(V4L2_MBUS_CSI2_CONTINUOUS_CLOCK |
+					 V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK))
+			unicam_err(dev, "g_mbus_format returned different clocking mode to DT\n");
+	}
+	if (dev->active_data_lanes > dev->max_data_lanes) {
+		unicam_err(dev, "Device has requested %u data lanes, which is >%u configured in DT\n",
+			   dev->active_data_lanes,
+			   dev->max_data_lanes);
+		ret = -EINVAL;
+		goto err_pm_put;
+	}
+
+	unicam_dbg(1, dev, "Running with %u data lanes\n",
+		   dev->active_data_lanes);
+
+	ret = clk_set_rate(dev->clock, 100 * 1000 * 1000);
+	if (ret) {
+		unicam_err(dev, "failed to set up clock\n");
+		goto err_pm_put;
+	}
+
+	ret = clk_prepare_enable(dev->clock);
+	if (ret) {
+		unicam_err(dev, "Failed to enable CSI clock: %d\n", ret);
+		goto err_pm_put;
+	}
+	ret = v4l2_subdev_call(dev->sensor, core, s_power, 1);
+	if (ret < 0 && ret != -ENOIOCTLCMD) {
+		unicam_err(dev, "power on failed in subdev\n");
+		goto err_clock_unprepare;
+	}
+	dev->streaming = 1;
+
+	unicam_start_rx(dev, addr);
+
+	ret = v4l2_subdev_call(dev->sensor, video, s_stream, 1);
+	if (ret < 0) {
+		unicam_err(dev, "stream on failed in subdev\n");
+		goto err_disable_unicam;
+	}
+
+	return 0;
+
+err_disable_unicam:
+	unicam_disable(dev);
+	v4l2_subdev_call(dev->sensor, core, s_power, 0);
+err_clock_unprepare:
+	clk_disable_unprepare(dev->clock);
+err_pm_put:
+	unicam_runtime_put(dev);
+err_release_buffers:
+	list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+	}
+	if (dev->cur_frm != dev->next_frm)
+		vb2_buffer_done(&dev->next_frm->vb.vb2_buf,
+				VB2_BUF_STATE_QUEUED);
+	vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+	dev->next_frm = NULL;
+	dev->cur_frm = NULL;
+
+	return ret;
+}
+
+static void unicam_stop_streaming(struct vb2_queue *vq)
+{
+	struct unicam_device *dev = vb2_get_drv_priv(vq);
+	struct unicam_dmaqueue *dma_q = &dev->dma_queue;
+	struct unicam_buffer *buf, *tmp;
+	unsigned long flags;
+
+	if (v4l2_subdev_call(dev->sensor, video, s_stream, 0) < 0)
+		unicam_err(dev, "stream off failed in subdev\n");
+
+	unicam_disable(dev);
+
+	/* Release all active buffers */
+	spin_lock_irqsave(&dev->dma_queue_lock, flags);
+	list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+	}
+
+	if (dev->cur_frm == dev->next_frm) {
+		vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+	} else {
+		vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+		vb2_buffer_done(&dev->next_frm->vb.vb2_buf,
+				VB2_BUF_STATE_ERROR);
+	}
+	dev->cur_frm = NULL;
+	dev->next_frm = NULL;
+	spin_unlock_irqrestore(&dev->dma_queue_lock, flags);
+
+	if (v4l2_subdev_has_op(dev->sensor, core, s_power)) {
+		if (v4l2_subdev_call(dev->sensor, core, s_power, 0) < 0)
+			unicam_err(dev, "power off failed in subdev\n");
+	}
+
+	clk_disable_unprepare(dev->clock);
+	unicam_runtime_put(dev);
+}
+
+static int unicam_enum_input(struct file *file, void *priv,
+			     struct v4l2_input *inp)
+{
+	struct unicam_device *dev = video_drvdata(file);
+
+	if (inp->index != 0)
+		return -EINVAL;
+
+	inp->type = V4L2_INPUT_TYPE_CAMERA;
+	if (v4l2_subdev_has_op(dev->sensor, video, s_dv_timings)) {
+		inp->capabilities = V4L2_IN_CAP_DV_TIMINGS;
+		inp->std = 0;
+	} else if (v4l2_subdev_has_op(dev->sensor, video, s_std)) {
+		inp->capabilities = V4L2_IN_CAP_STD;
+		if (v4l2_subdev_call(dev->sensor, video, g_tvnorms, &inp->std)
+					< 0)
+			inp->std = V4L2_STD_ALL;
+	} else {
+		inp->capabilities = 0;
+		inp->std = 0;
+	}
+	sprintf(inp->name, "Camera 0");
+	return 0;
+}
+
+static int unicam_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	*i = 0;
+
+	return 0;
+}
+
+static int unicam_s_input(struct file *file, void *priv, unsigned int i)
+{
+	/*
+	 * FIXME: Ideally we would like to be able to query the sensor
+	 * for information over the input connectors it supports, and
+	 * map that through in to a call to video_ops->s_routing.
+	 * There is no infrastructure support for defining that within
+	 * devicetree at present. Until that is implemented we can't
+	 * map a user physical connector number to s_routing input number.
+	 */
+	if (i > 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int unicam_querystd(struct file *file, void *priv,
+			   v4l2_std_id *std)
+{
+	struct unicam_device *dev = video_drvdata(file);
+
+	return v4l2_subdev_call(dev->sensor, video, querystd, std);
+}
+
+static int unicam_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+	struct unicam_device *dev = video_drvdata(file);
+
+	return v4l2_subdev_call(dev->sensor, video, g_std, std);
+}
+
+static int unicam_s_std(struct file *file, void *priv, v4l2_std_id std)
+{
+	struct unicam_device *dev = video_drvdata(file);
+	int ret;
+	v4l2_std_id current_std;
+
+	ret = v4l2_subdev_call(dev->sensor, video, g_std, &current_std);
+	if (ret)
+		return ret;
+
+	if (std == current_std)
+		return 0;
+
+	if (vb2_is_busy(&dev->buffer_queue))
+		return -EBUSY;
+
+	ret = v4l2_subdev_call(dev->sensor, video, s_std, std);
+
+	/* Force recomputation of bytesperline */
+	dev->v_fmt.fmt.pix.bytesperline = 0;
+
+	unicam_reset_format(dev);
+
+	return ret;
+}
+
+static int unicam_s_edid(struct file *file, void *priv, struct v4l2_edid *edid)
+{
+	struct unicam_device *dev = video_drvdata(file);
+
+	return v4l2_subdev_call(dev->sensor, pad, set_edid, edid);
+}
+
+static int unicam_g_edid(struct file *file, void *priv, struct v4l2_edid *edid)
+{
+	struct unicam_device *dev = video_drvdata(file);
+
+	return v4l2_subdev_call(dev->sensor, pad, get_edid, edid);
+}
+
+static int unicam_g_dv_timings(struct file *file, void *priv,
+			       struct v4l2_dv_timings *timings)
+{
+	struct unicam_device *dev = video_drvdata(file);
+
+	return v4l2_subdev_call(dev->sensor, video, g_dv_timings, timings);
+}
+
+static int unicam_s_dv_timings(struct file *file, void *priv,
+			       struct v4l2_dv_timings *timings)
+{
+	struct unicam_device *dev = video_drvdata(file);
+	struct v4l2_dv_timings current_timings;
+	int ret;
+
+	ret = v4l2_subdev_call(dev->sensor, video, g_dv_timings,
+			       &current_timings);
+
+	if (v4l2_match_dv_timings(timings, &current_timings, 0, false))
+		return 0;
+
+	if (vb2_is_busy(&dev->buffer_queue))
+		return -EBUSY;
+
+	ret = v4l2_subdev_call(dev->sensor, video, s_dv_timings, timings);
+
+	/* Force recomputation of bytesperline */
+	dev->v_fmt.fmt.pix.bytesperline = 0;
+
+	unicam_reset_format(dev);
+
+	return ret;
+}
+
+static int unicam_query_dv_timings(struct file *file, void *priv,
+				   struct v4l2_dv_timings *timings)
+{
+	struct unicam_device *dev = video_drvdata(file);
+
+	return v4l2_subdev_call(dev->sensor, video, query_dv_timings, timings);
+}
+
+static int unicam_enum_dv_timings(struct file *file, void *priv,
+				  struct v4l2_enum_dv_timings *timings)
+{
+	struct unicam_device *dev = video_drvdata(file);
+
+	return v4l2_subdev_call(dev->sensor, pad, enum_dv_timings, timings);
+}
+
+static int unicam_dv_timings_cap(struct file *file, void *priv,
+				 struct v4l2_dv_timings_cap *cap)
+{
+	struct unicam_device *dev = video_drvdata(file);
+
+	return v4l2_subdev_call(dev->sensor, pad, dv_timings_cap, cap);
+}
+
+static int unicam_subscribe_event(struct v4l2_fh *fh,
+				  const struct v4l2_event_subscription *sub)
+{
+	switch (sub->type) {
+	case V4L2_EVENT_SOURCE_CHANGE:
+		return v4l2_event_subscribe(fh, sub, 4, NULL);
+	}
+
+	return v4l2_ctrl_subscribe_event(fh, sub);
+}
+
+static int unicam_log_status(struct file *file, void *fh)
+{
+	struct unicam_device *dev = video_drvdata(file);
+	/* status for sub devices */
+	v4l2_device_call_all(&dev->v4l2_dev, 0, core, log_status);
+
+	return 0;
+}
+
+static void unicam_notify(struct v4l2_subdev *sd,
+			  unsigned int notification, void *arg)
+{
+	struct unicam_device *dev =
+		container_of(sd->v4l2_dev, struct unicam_device, v4l2_dev);
+
+	switch (notification) {
+	case V4L2_DEVICE_NOTIFY_EVENT:
+		v4l2_event_queue(&dev->video_dev, arg);
+		break;
+	default:
+		break;
+	}
+}
+
+static const struct vb2_ops unicam_video_qops = {
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+	.queue_setup		= unicam_queue_setup,
+	.buf_prepare		= unicam_buffer_prepare,
+	.buf_queue		= unicam_buffer_queue,
+	.start_streaming	= unicam_start_streaming,
+	.stop_streaming		= unicam_stop_streaming,
+};
+
+/* unicam capture driver file operations */
+static const struct v4l2_file_operations unicam_fops = {
+	.owner		= THIS_MODULE,
+	.open           = v4l2_fh_open,
+	.release        = vb2_fop_release,
+	.read		= vb2_fop_read,
+	.poll		= vb2_fop_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= vb2_fop_mmap,
+};
+
+/* unicam capture ioctl operations */
+static const struct v4l2_ioctl_ops unicam_ioctl_ops = {
+	.vidioc_querycap		= unicam_querycap,
+	.vidioc_enum_fmt_vid_cap	= unicam_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap		= unicam_g_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap		= unicam_s_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap		= unicam_try_fmt_vid_cap,
+
+	.vidioc_enum_input		= unicam_enum_input,
+	.vidioc_g_input			= unicam_g_input,
+	.vidioc_s_input			= unicam_s_input,
+
+	.vidioc_querystd		= unicam_querystd,
+	.vidioc_s_std			= unicam_s_std,
+	.vidioc_g_std			= unicam_g_std,
+
+	.vidioc_g_edid			= unicam_g_edid,
+	.vidioc_s_edid			= unicam_s_edid,
+
+	.vidioc_s_dv_timings		= unicam_s_dv_timings,
+	.vidioc_g_dv_timings		= unicam_g_dv_timings,
+	.vidioc_query_dv_timings	= unicam_query_dv_timings,
+	.vidioc_enum_dv_timings		= unicam_enum_dv_timings,
+	.vidioc_dv_timings_cap		= unicam_dv_timings_cap,
+
+	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
+	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
+	.vidioc_querybuf		= vb2_ioctl_querybuf,
+	.vidioc_qbuf			= vb2_ioctl_qbuf,
+	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
+	.vidioc_expbuf			= vb2_ioctl_expbuf,
+	.vidioc_streamon		= vb2_ioctl_streamon,
+	.vidioc_streamoff		= vb2_ioctl_streamoff,
+
+	.vidioc_log_status		= unicam_log_status,
+	.vidioc_subscribe_event		= unicam_subscribe_event,
+	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
+};
+
+static int
+unicam_async_bound(struct v4l2_async_notifier *notifier,
+		   struct v4l2_subdev *subdev,
+		   struct v4l2_async_subdev *asd)
+{
+	struct unicam_device *unicam = container_of(notifier->v4l2_dev,
+					       struct unicam_device, v4l2_dev);
+	struct v4l2_subdev_mbus_code_enum mbus_code;
+	int j;
+	struct unicam_fmt *active_fmt;
+	int ret = 0;
+
+	if (unicam->sensor) {
+		unicam_info(unicam, "Rejecting subdev %s (Already set!!)",
+			    subdev->name);
+		return 0;
+	}
+
+	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;
+	active_fmt = &unicam->active_fmts[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) {
+			const struct bayer_fmt *fmt_list = NULL;
+
+			switch (fmt->fourcc) {
+			case PIX_FMT_ALL_BGGR:
+				fmt_list = all_bayer_bggr;
+				break;
+			case PIX_FMT_ALL_RGGB:
+				fmt_list = all_bayer_rggb;
+				break;
+			case PIX_FMT_ALL_GBRG:
+				fmt_list = all_bayer_gbrg;
+				break;
+			case PIX_FMT_ALL_GRBG:
+				fmt_list = all_bayer_grbg;
+				break;
+			}
+			if (fmt_list) {
+				for (k = 0; fmt_list[k].fourcc; k++) {
+					char fourcc_str[V4L2_FOURCC_MAX_SIZE];
+
+					*active_fmt = *fmt;
+					active_fmt->fourcc = fmt_list[k].fourcc;
+					active_fmt->depth = fmt_list[k].depth;
+					unicam_dbg(2, unicam,
+						   "matched fourcc: %s: code: %04x idx: %d\n",
+						   v4l2_fourcc2s(fmt->fourcc,
+								 fourcc_str),
+						   fmt->code,
+						   unicam->num_active_fmt);
+					unicam->num_active_fmt++;
+					active_fmt++;
+				}
+			} else {
+				char fourcc_str[V4L2_FOURCC_MAX_SIZE];
+
+				*active_fmt = *fmt;
+				unicam_dbg(2, unicam,
+					   "matched fourcc: %s: code: %04x idx: %d\n",
+					   v4l2_fourcc2s(fmt->fourcc,
+							 fourcc_str),
+					   fmt->code,
+					   unicam->num_active_fmt);
+				unicam->num_active_fmt++;
+				active_fmt++;
+			}
+		}
+	}
+	unicam_dbg(2, unicam,
+		   "Done all formats\n");
+	dump_active_formats(unicam);
+
+	return 0;
+}
+
+static int unicam_probe_complete(struct unicam_device *unicam)
+{
+	struct video_device *vdev;
+	struct vb2_queue *q;
+	struct v4l2_mbus_framefmt mbus_fmt = {0};
+	const struct unicam_fmt *fmt;
+	int ret;
+
+	v4l2_set_subdev_hostdata(unicam->sensor, unicam);
+
+	unicam->v4l2_dev.notify = unicam_notify;
+
+	unicam->sensor_config = v4l2_subdev_alloc_pad_config(unicam->sensor);
+	if (!unicam->sensor_config)
+		return -ENOMEM;
+
+	ret = __subdev_get_format(unicam, &mbus_fmt);
+	if (ret) {
+		unicam_err(unicam, "Failed to get_format - ret %d\n", ret);
+		return ret;
+	}
+
+	fmt = find_format_by_code(unicam, mbus_fmt.code);
+	if (!fmt) {
+		unicam_dbg(3, unicam, "mbus code format (0x%08x) not found.\n",
+			   mbus_fmt.code);
+		return -EINVAL;
+	}
+	unicam->fmt = fmt;
+	unicam->v_fmt.fmt.pix.pixelformat  = fmt->fourcc;
+
+	/* Read current subdev format */
+	unicam_reset_format(unicam);
+
+	if (v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
+		v4l2_std_id tvnorms;
+
+		if (WARN_ON(!v4l2_subdev_has_op(unicam->sensor, video,
+						g_tvnorms)))
+			/*
+			 * Subdevice should not advertise querystd but not
+			 * g_tvnorms
+			 */
+			return -EINVAL;
+
+		ret = v4l2_subdev_call(unicam->sensor, video,
+				       g_tvnorms, &tvnorms);
+		if (WARN_ON(ret))
+			return -EINVAL;
+		unicam->video_dev.tvnorms |= tvnorms;
+	}
+
+	spin_lock_init(&unicam->dma_queue_lock);
+	mutex_init(&unicam->lock);
+
+	/* Add controls from the subdevice */
+	ret = v4l2_ctrl_add_handler(&unicam->ctrl_handler,
+				    unicam->sensor->ctrl_handler, NULL);
+	if (ret < 0)
+		return ret;
+
+	q = &unicam->buffer_queue;
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
+	q->drv_priv = unicam;
+	q->ops = &unicam_video_qops;
+	q->mem_ops = &vb2_dma_contig_memops;
+	q->buf_struct_size = sizeof(struct unicam_buffer);
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->lock = &unicam->lock;
+	q->min_buffers_needed = 2;
+	q->dev = &unicam->pdev->dev;
+
+	ret = vb2_queue_init(q);
+	if (ret) {
+		unicam_err(unicam, "vb2_queue_init() failed\n");
+		return ret;
+	}
+
+	INIT_LIST_HEAD(&unicam->dma_queue.active);
+
+	vdev = &unicam->video_dev;
+	strlcpy(vdev->name, UNICAM_MODULE_NAME, sizeof(vdev->name));
+	vdev->release = video_device_release_empty;
+	vdev->fops = &unicam_fops;
+	vdev->ioctl_ops = &unicam_ioctl_ops;
+	vdev->v4l2_dev = &unicam->v4l2_dev;
+	vdev->vfl_dir = VFL_DIR_RX;
+	vdev->queue = q;
+	vdev->lock = &unicam->lock;
+	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+			    V4L2_CAP_READWRITE;
+
+	video_set_drvdata(vdev, unicam);
+	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+	if (ret) {
+		unicam_err(unicam,
+			   "Unable to register video device.\n");
+		return ret;
+	}
+
+	if (!v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
+		v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_STD);
+		v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_STD);
+		v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_ENUMSTD);
+		v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_QUERYSTD);
+	}
+	if (!v4l2_subdev_has_op(unicam->sensor, video, s_dv_timings)) {
+		v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_EDID);
+		v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_EDID);
+		v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_DV_TIMINGS_CAP);
+		v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_DV_TIMINGS);
+		v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_DV_TIMINGS);
+		v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_ENUM_DV_TIMINGS);
+		v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_QUERY_DV_TIMINGS);
+	}
+
+	ret = v4l2_device_register_subdev_nodes(&unicam->v4l2_dev);
+	if (ret) {
+		unicam_err(unicam,
+			   "Unable to register subdev nodes.\n");
+		video_unregister_device(&unicam->video_dev);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int unicam_async_complete(struct v4l2_async_notifier *notifier)
+{
+	struct unicam_device *unicam = container_of(notifier->v4l2_dev,
+					struct unicam_device, v4l2_dev);
+
+	return unicam_probe_complete(unicam);
+}
+
+static int of_unicam_connect_subdevs(struct unicam_device *dev)
+{
+	struct platform_device *pdev = dev->pdev;
+	struct device_node *parent, *ep_node = NULL, *remote_ep = NULL,
+			*sensor_node = NULL;
+	struct v4l2_fwnode_endpoint *ep;
+	struct v4l2_async_subdev *asd;
+	int ret = -EINVAL;
+	unsigned int lane;
+	struct v4l2_async_subdev **subdevs = NULL;
+	unsigned int peripheral_data_lanes;
+
+	parent = pdev->dev.of_node;
+
+	asd = &dev->asd;
+	ep = &dev->endpoint;
+
+	ep_node = of_graph_get_next_endpoint(parent, NULL);
+	if (!ep_node) {
+		unicam_dbg(3, dev, "can't get next endpoint\n");
+		goto cleanup_exit;
+	}
+
+	unicam_dbg(3, dev, "ep_node is %s\n",
+		   ep_node->name);
+
+	v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), ep);
+
+	for (lane = 0; lane < ep->bus.mipi_csi2.num_data_lanes; lane++) {
+		if (ep->bus.mipi_csi2.data_lanes[lane] != lane + 1) {
+			unicam_err(dev, "Local endpoint - data lane reordering not supported\n");
+			goto cleanup_exit;
+		}
+	}
+
+	peripheral_data_lanes = ep->bus.mipi_csi2.num_data_lanes;
+
+	sensor_node = of_graph_get_remote_port_parent(ep_node);
+	if (!sensor_node) {
+		unicam_dbg(3, dev, "can't get remote parent\n");
+		goto cleanup_exit;
+	}
+	unicam_dbg(3, dev, "sensor_node is %s\n",
+		   sensor_node->name);
+	asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
+	asd->match.fwnode.fwnode = of_fwnode_handle(sensor_node);
+
+	remote_ep = of_graph_get_remote_endpoint(ep_node);
+	if (!remote_ep) {
+		unicam_dbg(3, dev, "can't get remote-endpoint\n");
+		goto cleanup_exit;
+	}
+	unicam_dbg(3, dev, "remote_ep is %s\n",
+		   remote_ep->name);
+	v4l2_fwnode_endpoint_parse(of_fwnode_handle(remote_ep), ep);
+	unicam_dbg(3, dev, "parsed remote_ep to endpoint. nr_of_link_frequencies %u, bus_type %u\n",
+		   ep->nr_of_link_frequencies, ep->bus_type);
+
+	switch (ep->bus_type) {
+	case V4L2_MBUS_CSI2:
+		if (ep->bus.mipi_csi2.num_data_lanes >
+				peripheral_data_lanes) {
+			unicam_err(dev, "Subdevice %s wants too many data lanes (%u > %u)\n",
+				   sensor_node->name,
+				   ep->bus.mipi_csi2.num_data_lanes,
+				   peripheral_data_lanes);
+			goto cleanup_exit;
+		}
+		for (lane = 0;
+		     lane < ep->bus.mipi_csi2.num_data_lanes;
+		     lane++) {
+			if (ep->bus.mipi_csi2.data_lanes[lane] != lane + 1) {
+				unicam_err(dev, "Subdevice %s - incompatible data lane config\n",
+					   sensor_node->name);
+				goto cleanup_exit;
+			}
+		}
+		dev->max_data_lanes = ep->bus.mipi_csi2.num_data_lanes;
+		dev->bus_flags = ep->bus.mipi_csi2.flags;
+		break;
+	case V4L2_MBUS_CCP2:
+		if (ep->bus.mipi_csi1.clock_lane != 0 ||
+		    ep->bus.mipi_csi1.data_lane != 1) {
+			unicam_err(dev, "Subdevice %s incompatible lane config\n",
+				   sensor_node->name);
+			goto cleanup_exit;
+		}
+		dev->max_data_lanes = 1;
+		dev->bus_flags = ep->bus.mipi_csi1.strobe;
+		break;
+	default:
+		/* Unsupported bus type */
+		unicam_err(dev, "sub-device %s is not a CSI2 or CCP2 device %d\n",
+			   sensor_node->name, ep->bus_type);
+		goto cleanup_exit;
+	}
+
+	/* Store bus type - CSI2 or CCP2 */
+	dev->bus_type = ep->bus_type;
+	unicam_dbg(3, dev, "bus_type is %d\n", dev->bus_type);
+
+	/* Store Virtual Channel number */
+	dev->virtual_channel = ep->base.id;
+
+	unicam_dbg(3, dev, "v4l2-endpoint: %s\n",
+		   dev->bus_type == V4L2_MBUS_CSI2 ? "CSI2" : "CCP2");
+	unicam_dbg(3, dev, "Virtual Channel=%d\n", dev->virtual_channel);
+	if (dev->bus_type == V4L2_MBUS_CSI2)
+		unicam_dbg(3, dev, "flags=0x%08x\n",
+			   ep->bus.mipi_csi2.flags);
+	unicam_dbg(3, dev, "num_data_lanes=%d\n", dev->max_data_lanes);
+
+	unicam_dbg(1, dev, "found sub-device %s\n",
+		   sensor_node->name);
+
+	subdevs = devm_kzalloc(&dev->pdev->dev, sizeof(*subdevs), GFP_KERNEL);
+	if (!subdevs) {
+		ret = -ENOMEM;
+		goto cleanup_exit;
+	}
+	subdevs[0] = asd;
+	dev->notifier.subdevs = subdevs;
+	dev->notifier.num_subdevs = 1;
+	dev->notifier.bound = unicam_async_bound;
+	dev->notifier.complete = unicam_async_complete;
+	ret = v4l2_async_notifier_register(&dev->v4l2_dev,
+					   &dev->notifier);
+	if (ret) {
+		unicam_err(dev, "Error registering async notifier - ret %d\n",
+			   ret);
+		ret = -EINVAL;
+	}
+
+cleanup_exit:
+	if (remote_ep)
+		of_node_put(remote_ep);
+	if (sensor_node)
+		of_node_put(sensor_node);
+	if (ep_node)
+		of_node_put(ep_node);
+
+	return ret;
+}
+
+static int unicam_probe(struct platform_device *pdev)
+{
+	struct unicam_cfg *unicam_cfg;
+	struct unicam_device *unicam;
+	struct v4l2_ctrl_handler *hdl;
+	struct resource	*res;
+	int ret;
+
+	unicam = devm_kzalloc(&pdev->dev, sizeof(*unicam), GFP_KERNEL);
+	if (!unicam)
+		return -ENOMEM;
+
+	unicam->pdev = pdev;
+	unicam_cfg = &unicam->cfg;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	unicam_cfg->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(unicam_cfg->base)) {
+		unicam_err(unicam, "Failed to get main io block\n");
+		return PTR_ERR(unicam_cfg->base);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	unicam_cfg->clk_gate_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(unicam_cfg->clk_gate_base)) {
+		unicam_err(unicam, "Failed to get 2nd io block\n");
+		return PTR_ERR(unicam_cfg->clk_gate_base);
+	}
+
+	unicam->clock = devm_clk_get(&pdev->dev, "lp_clock");
+	if (IS_ERR(unicam->clock)) {
+		unicam_err(unicam, "Failed to get clock\n");
+		return PTR_ERR(unicam->clock);
+	}
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret <= 0) {
+		dev_err(&pdev->dev, "No IRQ resource\n");
+		return -ENODEV;
+	}
+	unicam_cfg->irq = ret;
+
+	ret = devm_request_irq(&pdev->dev, unicam_cfg->irq, unicam_isr, 0,
+			       "unicam_capture0", unicam);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to request interrupt\n");
+		return -EINVAL;
+	}
+
+	ret = v4l2_device_register(&pdev->dev, &unicam->v4l2_dev);
+	if (ret) {
+		unicam_err(unicam,
+			   "Unable to register v4l2 device.\n");
+		return ret;
+	}
+
+	/* Reserve space for the controls */
+	hdl = &unicam->ctrl_handler;
+	ret = v4l2_ctrl_handler_init(hdl, 16);
+	if (ret < 0)
+		goto probe_out_v4l2_unregister;
+	unicam->v4l2_dev.ctrl_handler = hdl;
+
+	/* set the driver data in platform device */
+	platform_set_drvdata(pdev, unicam);
+
+	ret = of_unicam_connect_subdevs(unicam);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to connect subdevs\n");
+		goto free_hdl;
+	}
+
+	/* Enabling module functional clock */
+	pm_runtime_enable(&pdev->dev);
+
+	return 0;
+
+free_hdl:
+	v4l2_ctrl_handler_free(hdl);
+probe_out_v4l2_unregister:
+	v4l2_device_unregister(&unicam->v4l2_dev);
+	return ret;
+}
+
+static int unicam_remove(struct platform_device *pdev)
+{
+	struct unicam_device *unicam = platform_get_drvdata(pdev);
+
+	unicam_dbg(2, unicam, "%s\n", __func__);
+
+	pm_runtime_disable(&pdev->dev);
+
+	v4l2_async_notifier_unregister(&unicam->notifier);
+	v4l2_ctrl_handler_free(&unicam->ctrl_handler);
+	v4l2_device_unregister(&unicam->v4l2_dev);
+	video_unregister_device(&unicam->video_dev);
+	if (unicam->sensor_config)
+		v4l2_subdev_free_pad_config(unicam->sensor_config);
+
+	return 0;
+}
+
+static const struct of_device_id unicam_of_match[] = {
+	{ .compatible = "brcm,bcm2835-unicam", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, unicam_of_match);
+
+static struct platform_driver unicam_driver = {
+	.probe		= unicam_probe,
+	.remove		= unicam_remove,
+	.driver = {
+		.name	= UNICAM_MODULE_NAME,
+		.of_match_table = of_match_ptr(unicam_of_match),
+	},
+};
+
+module_platform_driver(unicam_driver);
+
+MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.org>");
+MODULE_DESCRIPTION("BCM2835 Unicam driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(UNICAM_VERSION);
diff --git a/drivers/media/platform/bcm2835/vc4-regs-unicam.h b/drivers/media/platform/bcm2835/vc4-regs-unicam.h
new file mode 100644
index 0000000..28e9a75
--- /dev/null
+++ b/drivers/media/platform/bcm2835/vc4-regs-unicam.h
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2017 Raspberry Pi Trading.
+ * Dave Stevenson <dave.stevenson@raspberrypi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef VC4_REGS_UNICAM_H
+#define VC4_REGS_UNICAM_H
+
+/*
+ * The following values are taken from files found within the code drop
+ * made by Broadcom for the BCM21553 Graphics Driver, predominantly in
+ * brcm_usrlib/dag/vmcsx/vcinclude/hardware_vc4.h.
+ * They have been modified to be only the register offset.
+ */
+#define UNICAM_CTRL	0x000
+#define UNICAM_STA	0x004
+#define UNICAM_ANA	0x008
+#define UNICAM_PRI	0x00c
+#define UNICAM_CLK	0x010
+#define UNICAM_CLT	0x014
+#define UNICAM_DAT0	0x018
+#define UNICAM_DAT1	0x01c
+#define UNICAM_DAT2	0x020
+#define UNICAM_DAT3	0x024
+#define UNICAM_DLT	0x028
+#define UNICAM_CMP0	0x02c
+#define UNICAM_CMP1	0x030
+#define UNICAM_CAP0	0x034
+#define UNICAM_CAP1	0x038
+#define UNICAM_ICTL	0x100
+#define UNICAM_ISTA	0x104
+#define UNICAM_IDI0	0x108
+#define UNICAM_IPIPE	0x10c
+#define UNICAM_IBSA0	0x110
+#define UNICAM_IBEA0	0x114
+#define UNICAM_IBLS	0x118
+#define UNICAM_IBWP	0x11c
+#define UNICAM_IHWIN	0x120
+#define UNICAM_IHSTA	0x124
+#define UNICAM_IVWIN	0x128
+#define UNICAM_IVSTA	0x12c
+#define UNICAM_ICC	0x130
+#define UNICAM_ICS	0x134
+#define UNICAM_IDC	0x138
+#define UNICAM_IDPO	0x13c
+#define UNICAM_IDCA	0x140
+#define UNICAM_IDCD	0x144
+#define UNICAM_IDS	0x148
+#define UNICAM_DCS	0x200
+#define UNICAM_DBSA0	0x204
+#define UNICAM_DBEA0	0x208
+#define UNICAM_DBWP	0x20c
+#define UNICAM_DBCTL	0x300
+#define UNICAM_IBSA1	0x304
+#define UNICAM_IBEA1	0x308
+#define UNICAM_IDI1	0x30c
+#define UNICAM_DBSA1	0x310
+#define UNICAM_DBEA1	0x314
+#define UNICAM_MISC	0x400
+
+/*
+ * The following bitmasks are from the kernel released by Broadcom
+ * for Android - https://android.googlesource.com/kernel/bcm/
+ * The Rhea, Hawaii, and Java chips all contain the same VideoCore4
+ * Unicam block as BCM2835, as defined in eg
+ * arch/arm/mach-rhea/include/mach/rdb_A0/brcm_rdb_cam.h and similar.
+ * Values reworked to use the kernel BIT and GENMASK macros.
+ *
+ * Some of the bit mnenomics have been amended to match the datasheet.
+ */
+/* UNICAM_CTRL Register */
+#define UNICAM_CPE		BIT(0)
+#define UNICAM_MEM		BIT(1)
+#define UNICAM_CPR		BIT(2)
+#define UNICAM_CPM_MASK		GENMASK(3, 3)
+#define UNICAM_CPM_CSI2		0
+#define UNICAM_CPM_CCP2		1
+#define UNICAM_SOE		BIT(4)
+#define UNICAM_DCM_MASK		GENMASK(5, 5)
+#define UNICAM_DCM_STROBE	0
+#define UNICAM_DCM_DATA		1
+#define UNICAM_SLS		BIT(6)
+#define UNICAM_PFT_MASK		GENMASK(11, 8)
+#define UNICAM_OET_MASK		GENMASK(20, 12)
+
+/* UNICAM_STA Register */
+#define UNICAM_SYN		BIT(0)
+#define UNICAM_CS		BIT(1)
+#define UNICAM_SBE		BIT(2)
+#define UNICAM_PBE		BIT(3)
+#define UNICAM_HOE		BIT(4)
+#define UNICAM_PLE		BIT(5)
+#define UNICAM_SSC		BIT(6)
+#define UNICAM_CRCE		BIT(7)
+#define UNICAM_OES		BIT(8)
+#define UNICAM_IFO		BIT(9)
+#define UNICAM_OFO		BIT(10)
+#define UNICAM_BFO		BIT(11)
+#define UNICAM_DL		BIT(12)
+#define UNICAM_PS		BIT(13)
+#define UNICAM_IS		BIT(14)
+#define UNICAM_PI0		BIT(15)
+#define UNICAM_PI1		BIT(16)
+#define UNICAM_FSI_S		BIT(17)
+#define UNICAM_FEI_S		BIT(18)
+#define UNICAM_LCI_S		BIT(19)
+#define UNICAM_BUF0_RDY		BIT(20)
+#define UNICAM_BUF0_NO		BIT(21)
+#define UNICAM_BUF1_RDY		BIT(22)
+#define UNICAM_BUF1_NO		BIT(23)
+#define UNICAM_DI		BIT(24)
+
+#define UNICAM_STA_MASK_ALL \
+		(UNICAM_DL + \
+		UNICAM_SBE + \
+		UNICAM_PBE + \
+		UNICAM_HOE + \
+		UNICAM_PLE + \
+		UNICAM_SSC + \
+		UNICAM_CRCE + \
+		UNICAM_IFO + \
+		UNICAM_OFO + \
+		UNICAM_PS + \
+		UNICAM_PI0 + \
+		UNICAM_PI1)
+
+/* UNICAM_ANA Register */
+#define UNICAM_APD		BIT(0)
+#define UNICAM_BPD		BIT(1)
+#define UNICAM_AR		BIT(2)
+#define UNICAM_DDL		BIT(3)
+#define UNICAM_CTATADJ_MASK	GENMASK(7, 4)
+#define UNICAM_PTATADJ_MASK	GENMASK(11, 8)
+
+/* UNICAM_PRI Register */
+#define UNICAM_PE		BIT(0)
+#define UNICAM_PT_MASK		GENMASK(2, 1)
+#define UNICAM_NP_MASK		GENMASK(7, 4)
+#define UNICAM_PP_MASK		GENMASK(11, 8)
+#define UNICAM_BS_MASK		GENMASK(15, 12)
+#define UNICAM_BL_MASK		GENMASK(17, 16)
+
+/* UNICAM_CLK Register */
+#define UNICAM_CLE		BIT(0)
+#define UNICAM_CLPD		BIT(1)
+#define UNICAM_CLLPE		BIT(2)
+#define UNICAM_CLHSE		BIT(3)
+#define UNICAM_CLTRE		BIT(4)
+#define UNICAM_CLAC_MASK	GENMASK(8, 5)
+#define UNICAM_CLSTE		BIT(29)
+
+/* UNICAM_CLT Register */
+#define UNICAM_CLT1_MASK	GENMASK(7, 0)
+#define UNICAM_CLT2_MASK	GENMASK(15, 8)
+
+/* UNICAM_DATn Registers */
+#define UNICAM_DLE		BIT(0)
+#define UNICAM_DLPD		BIT(1)
+#define UNICAM_DLLPE		BIT(2)
+#define UNICAM_DLHSE		BIT(3)
+#define UNICAM_DLTRE		BIT(4)
+#define UNICAM_DLSM		BIT(5)
+#define UNICAM_DLFO		BIT(28)
+#define UNICAM_DLSTE		BIT(29)
+
+#define UNICAM_DAT_MASK_ALL (UNICAM_DLSTE + UNICAM_DLFO)
+
+/* UNICAM_DLT Register */
+#define UNICAM_DLT1_MASK	GENMASK(7, 0)
+#define UNICAM_DLT2_MASK	GENMASK(15, 8)
+#define UNICAM_DLT3_MASK	GENMASK(23, 16)
+
+/* UNICAM_ICTL Register */
+#define UNICAM_FSIE		BIT(0)
+#define UNICAM_FEIE		BIT(1)
+#define UNICAM_IBOB		BIT(2)
+#define UNICAM_FCM		BIT(3)
+#define UNICAM_TFC		BIT(4)
+#define UNICAM_LIP_MASK		GENMASK(6, 5)
+#define UNICAM_LCIE_MASK	GENMASK(28, 16)
+
+/* UNICAM_IDI0/1 Register */
+#define UNICAM_ID0_MASK		GENMASK(7, 0)
+#define UNICAM_ID1_MASK		GENMASK(15, 8)
+#define UNICAM_ID2_MASK		GENMASK(23, 16)
+#define UNICAM_ID3_MASK		GENMASK(31, 24)
+
+/* UNICAM_ISTA Register */
+#define UNICAM_FSI		BIT(0)
+#define UNICAM_FEI		BIT(1)
+#define UNICAM_LCI		BIT(2)
+
+#define UNICAM_ISTA_MASK_ALL (UNICAM_FSI + UNICAM_FEI + UNICAM_LCI)
+
+/* UNICAM_IPIPE Register */
+#define UNICAM_PUM_MASK		GENMASK(2, 0)
+		/* Unpacking modes */
+		#define UNICAM_PUM_NONE		0
+		#define UNICAM_PUM_UNPACK6	1
+		#define UNICAM_PUM_UNPACK7	2
+		#define UNICAM_PUM_UNPACK8	3
+		#define UNICAM_PUM_UNPACK10	4
+		#define UNICAM_PUM_UNPACK12	5
+		#define UNICAM_PUM_UNPACK14	6
+		#define UNICAM_PUM_UNPACK16	7
+#define UNICAM_DDM_MASK		GENMASK(6, 3)
+#define UNICAM_PPM_MASK		GENMASK(9, 7)
+		/* Packing modes */
+		#define UNICAM_PPM_NONE		0
+		#define UNICAM_PPM_PACK8	1
+		#define UNICAM_PPM_PACK10	2
+		#define UNICAM_PPM_PACK12	3
+		#define UNICAM_PPM_PACK14	4
+		#define UNICAM_PPM_PACK16	5
+#define UNICAM_DEM_MASK		GENMASK(11, 10)
+#define UNICAM_DEBL_MASK	GENMASK(14, 12)
+#define UNICAM_ICM_MASK		GENMASK(16, 15)
+#define UNICAM_IDM_MASK		GENMASK(17, 17)
+
+/* UNICAM_ICC Register */
+#define UNICAM_ICFL_MASK	GENMASK(4, 0)
+#define UNICAM_ICFH_MASK	GENMASK(9, 5)
+#define UNICAM_ICST_MASK	GENMASK(12, 10)
+#define UNICAM_ICLT_MASK	GENMASK(15, 13)
+#define UNICAM_ICLL_MASK	GENMASK(31, 16)
+
+/* UNICAM_DCS Register */
+#define UNICAM_DIE		BIT(0)
+#define UNICAM_DIM		BIT(1)
+#define UNICAM_DBOB		BIT(3)
+#define UNICAM_FDE		BIT(4)
+#define UNICAM_LDP		BIT(5)
+#define UNICAM_EDL_MASK		GENMASK(15, 8)
+
+/* UNICAM_DBCTL Register */
+#define UNICAM_DBEN		BIT(0)
+#define UNICAM_BUF0_IE		BIT(1)
+#define UNICAM_BUF1_IE		BIT(2)
+
+/* UNICAM_CMP[0,1] register */
+#define UNICAM_PCE		BIT(31)
+#define UNICAM_GI		BIT(9)
+#define UNICAM_CPH		BIT(8)
+#define UNICAM_PCVC_MASK	GENMASK(7, 6)
+#define UNICAM_PCDT_MASK	GENMASK(5, 0)
+
+/* UNICAM_MISC register */
+#define UNICAM_FL0		BIT(6)
+#define UNICAM_FL1		BIT(9)
+
+#endif
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 4/4] MAINTAINERS: Add entry for BCM2835 camera driver
       [not found] ` <cover.1505314390.git.dave.stevenson@raspberrypi.org>
                     ` (2 preceding siblings ...)
  2017-09-13 15:07   ` [PATCH v2 3/4] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface Dave Stevenson
@ 2017-09-13 15:07   ` Dave Stevenson
  3 siblings, 0 replies; 20+ messages in thread
From: Dave Stevenson @ 2017-09-13 15:07 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Rob Herring, Hans Verkuil, Eric Anholt,
	Stefan Wahren, Sakari Ailus, linux-media, linux-rpi-kernel,
	devicetree
  Cc: Dave Stevenson

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index eb930eb..b47ddaa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2713,6 +2713,13 @@ S:	Maintained
 N:	bcm2835
 F:	drivers/staging/vc04_services
 
+BROADCOM BCM2835 CAMERA DRIVER
+M:	Dave Stevenson <dave.stevenson@raspberrypi.org>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+F:	drivers/media/platform/bcm2835/
+F:	Documentation/devicetree/bindings/media/bcm2835-unicam.txt
+
 BROADCOM BCM47XX MIPS ARCHITECTURE
 M:	Hauke Mehrtens <hauke@hauke-m.de>
 M:	Rafał Miłecki <zajec5@gmail.com>
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 0/4] BCM283x Camera Receiver driver
  2017-09-13 15:07 [PATCH v2 0/4] BCM283x Camera Receiver driver Dave Stevenson
       [not found] ` <cover.1505314390.git.dave.stevenson@raspberrypi.org>
@ 2017-09-13 15:49 ` Dave Stevenson
  2017-09-22 11:00   ` Hans Verkuil
  1 sibling, 1 reply; 20+ messages in thread
From: Dave Stevenson @ 2017-09-13 15:49 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Hans Verkuil, linux-media, linux-rpi-kernel

(dropping to linux-media and linux-rpi-kernel mailing lists as the
device tree folk aren't going to be bothered by v4l2-compliance
results)

v4l2-compliance results:

TC358743 (having loaded an EDID config)

v4l2-compliance SHA   : f6ecbc90656815d91dc6ba90aac0ad8193a14b38

Driver Info:
    Driver name   : unicam
    Card type     : unicam
    Bus info      : platform:unicam 3f801000.csi1
    Driver version: 4.13.0
    Capabilities  : 0x85200001
        Video Capture
        Read/Write
        Streaming
        Extended Pix Format
        Device Capabilities
    Device Caps   : 0x05200001
        Video Capture
        Read/Write
        Streaming
        Extended Pix Format

Compliance test for device /dev/video0 (not using libv4l2):

Required ioctls:
    test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
    test second video open: OK
    test VIDIOC_QUERYCAP: OK
    test VIDIOC_G/S_PRIORITY: OK
    test for unlimited opens: OK

Debug ioctls:
    test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
    test VIDIOC_LOG_STATUS: OK

Input ioctls:
    test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
    test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
    test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
    test VIDIOC_ENUMAUDIO: OK (Not Supported)
    test VIDIOC_G/S/ENUMINPUT: OK
    test VIDIOC_G/S_AUDIO: OK (Not Supported)
    Inputs: 1 Audio Inputs: 0 Tuners: 0

Output ioctls:
    test VIDIOC_G/S_MODULATOR: OK (Not Supported)
    test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
    test VIDIOC_ENUMAUDOUT: OK (Not Supported)
    test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
    test VIDIOC_G/S_AUDOUT: OK (Not Supported)
    Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
    test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
    test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK
    test VIDIOC_DV_TIMINGS_CAP: OK
    test VIDIOC_G/S_EDID: OK

Test input 0:

    Control ioctls:
        test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
        test VIDIOC_QUERYCTRL: OK
        test VIDIOC_G/S_CTRL: OK
        test VIDIOC_G/S/TRY_EXT_CTRLS: OK
        test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
        test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
        Standard Controls: 3 Private Controls: 2

    Format ioctls:
        test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
        test VIDIOC_G/S_PARM: OK (Not Supported)
        test VIDIOC_G_FBUF: OK (Not Supported)
        test VIDIOC_G_FMT: OK
        test VIDIOC_TRY_FMT: OK
        test VIDIOC_S_FMT: OK
        test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
        test Cropping: OK (Not Supported)
        test Composing: OK (Not Supported)
        test Scaling: OK (Not Supported)

    Codec ioctls:
        test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
        test VIDIOC_G_ENC_INDEX: OK (Not Supported)
        test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

    Buffer ioctls:
        test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
        test VIDIOC_EXPBUF: OK

Test input 0:

Stream using all formats:
    test MMAP for Format RGB3, Frame Size 1920x1080:
        Stride 5760, Field None: OK
        Stride 5824, Field None: OK
    test MMAP for Format UYVY, Frame Size 1920x1080:
        Stride 3840, Field None: OK
        Stride 3904, Field None: OK

Total: 47, Succeeded: 47, Failed: 0, Warnings: 0

------
ADV7282-M
Minor hack required to select the first valid input (in my case
CVBS_AIN1). The hardware default is DIFF_CVBS_AIN1_AIN2.

v4l2-compliance SHA   : f6ecbc90656815d91dc6ba90aac0ad8193a14b38

Driver Info:
    Driver name   : unicam
    Card type     : unicam
    Bus info      : platform:unicam 3f801000.csi1
    Driver version: 4.13.0
    Capabilities  : 0x85200001
        Video Capture
        Read/Write
        Streaming
        Extended Pix Format
        Device Capabilities
    Device Caps   : 0x05200001
        Video Capture
        Read/Write
        Streaming
        Extended Pix Format

Compliance test for device /dev/video0 (not using libv4l2):

Required ioctls:
    test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
    test second video open: OK
    test VIDIOC_QUERYCAP: OK
    test VIDIOC_G/S_PRIORITY: OK
    test for unlimited opens: OK

Debug ioctls:
    test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
    test VIDIOC_LOG_STATUS: OK

Input ioctls:
    test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
    test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
    test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
    test VIDIOC_ENUMAUDIO: OK (Not Supported)
    test VIDIOC_G/S/ENUMINPUT: OK
    test VIDIOC_G/S_AUDIO: OK (Not Supported)
    Inputs: 1 Audio Inputs: 0 Tuners: 0

Output ioctls:
    test VIDIOC_G/S_MODULATOR: OK (Not Supported)
    test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
    test VIDIOC_ENUMAUDOUT: OK (Not Supported)
    test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
    test VIDIOC_G/S_AUDOUT: OK (Not Supported)
    Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
    test VIDIOC_ENUM/G/S/QUERY_STD: OK
    test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
    test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
    test VIDIOC_G/S_EDID: OK (Not Supported)

Test input 0:

    Control ioctls:
        test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
        test VIDIOC_QUERYCTRL: OK
        test VIDIOC_G/S_CTRL: OK
        test VIDIOC_G/S/TRY_EXT_CTRLS: OK
        test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
        test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
        Standard Controls: 5 Private Controls: 1

    Format ioctls:
        test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
        test VIDIOC_G/S_PARM: OK
        test VIDIOC_G_FBUF: OK (Not Supported)
        test VIDIOC_G_FMT: OK
        test VIDIOC_TRY_FMT: OK
        test VIDIOC_S_FMT: OK
        test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
        test Cropping: OK (Not Supported)
        test Composing: OK (Not Supported)
        test Scaling: OK (Not Supported)

    Codec ioctls:
        test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
        test VIDIOC_G_ENC_INDEX: OK (Not Supported)
        test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

    Buffer ioctls:
Retrieved std of 0000B000
        test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
        test VIDIOC_EXPBUF: OK

Test input 0:

Stream using all formats:
    test MMAP for Format UYVY, Frame Size 720x480:
        Stride 1440, Field None: OK
        Stride 1504, Field None: OK

Total: 45, Succeeded: 45, Failed: 0, Warnings: 0

-------
OV5647

v4l2-compliance SHA   : f6ecbc90656815d91dc6ba90aac0ad8193a14b38

Driver Info:
    Driver name   : unicam
    Card type     : unicam
    Bus info      : platform:unicam 3f801000.csi1
    Driver version: 4.13.0
    Capabilities  : 0x85200001
        Video Capture
        Read/Write
        Streaming
        Extended Pix Format
        Device Capabilities
    Device Caps   : 0x05200001
        Video Capture
        Read/Write
        Streaming
        Extended Pix Format

Compliance test for device /dev/video0 (not using libv4l2):

Required ioctls:
    test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
    test second video open: OK
    test VIDIOC_QUERYCAP: OK
    test VIDIOC_G/S_PRIORITY: OK
    test for unlimited opens: OK

Debug ioctls:
    test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
    test VIDIOC_LOG_STATUS: OK

Input ioctls:
    test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
    test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
    test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
    test VIDIOC_ENUMAUDIO: OK (Not Supported)
    test VIDIOC_G/S/ENUMINPUT: OK
    test VIDIOC_G/S_AUDIO: OK (Not Supported)
    Inputs: 1 Audio Inputs: 0 Tuners: 0

Output ioctls:
    test VIDIOC_G/S_MODULATOR: OK (Not Supported)
    test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
    test VIDIOC_ENUMAUDOUT: OK (Not Supported)
    test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
    test VIDIOC_G/S_AUDOUT: OK (Not Supported)
    Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
    test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
    test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
    test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
    test VIDIOC_G/S_EDID: OK (Not Supported)

Test input 0:

    Control ioctls:
        test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
        test VIDIOC_QUERYCTRL: OK
        test VIDIOC_G/S_CTRL: OK
        fail: v4l2-test-controls.cpp(587): g_ext_ctrls does not
support count == 0
        test VIDIOC_G/S/TRY_EXT_CTRLS: FAIL
        test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
        test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
        Standard Controls: 0 Private Controls: 0

    Format ioctls:
        test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
        test VIDIOC_G/S_PARM: OK (Not Supported)
        test VIDIOC_G_FBUF: OK (Not Supported)
        test VIDIOC_G_FMT: OK
        test VIDIOC_TRY_FMT: OK
        test VIDIOC_S_FMT: OK
        test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
        test Cropping: OK (Not Supported)
        test Composing: OK (Not Supported)
        test Scaling: OK (Not Supported)

    Codec ioctls:
        test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
        test VIDIOC_G_ENC_INDEX: OK (Not Supported)
        test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

    Buffer ioctls:
        test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
        test VIDIOC_EXPBUF: OK

Test input 0:

Stream using all formats:
    test MMAP for Format BA81, Frame Size 640x480:
        Stride 640, Field None: OK
        Stride 704, Field None: OK
    test MMAP for Format pBAA, Frame Size 640x480:
        Stride 800, Field None: OK
        Stride 864, Field None: OK
    test MMAP for Format BG12, Frame Size 640x480:
        Stride 960, Field None: OK
        Stride 1024, Field None: OK
    test MMAP for Format BYR2, Frame Size 640x480:
        Stride 1280, Field None: OK
        Stride 1344, Field None: OK

Total: 51, Succeeded: 50, Failed: 1, Warnings: 0


Hans previously requested the output of "v4l2-ctl -l" for this case:
pi@raspberrypi:~/v4l-utils/utils/v4l2-ctl $ ./v4l2-ctl -l
pi@raspberrypi:~/v4l-utils/utils/v4l2-ctl $
ie nothing - the sub device driver has no controls registered, and
that is what causes the failure:
        fail: v4l2-test-controls.cpp(587): g_ext_ctrls does not
support count == 0
        test VIDIOC_G/S/TRY_EXT_CTRLS: FAIL
I don't know what the correct behaviour should be in those
circumstances, but it isn't really a failure of this driver.

  Dave

On 13 September 2017 at 16:07, Dave Stevenson
<dave.stevenson@raspberrypi.org> wrote:
> Hi All.
>
> This is v2 for adding a V4L2 subdevice driver for the CSI2/CCP2 camera
> receiver peripheral on BCM283x, as used on Raspberry Pi.
> Sorry for the delay since v1 - other tasks assigned, got sucked
> into investigating why some devices were misbehaving, and
> picking up on new features that had been added to the tree (eg CCP2).
>
> v4l2-compliance results depend on the sensor subdevice connected to.
> I have results for TC358743, ADV7282M, and OV5647 that I'll
> send them as a follow up email.
>
> OV647 and ADV7282M are now working with this driver, as well as TC358743.
> v1 of the driver only supported continuous clock mode which Unicam was
> failing to lock on to correctly.
> The driver now checks the clock mode and adjusts termination accordingly.
> Something is still a little off for OV5647, but I'll investigate that
> later.
>
> As per the v1 discussion with Hans, I have added text describing the
> differences between this driver and the one in staging/vc04_service.
> Addressing some of the issues in the bcm2835-camera driver is on my to-do
> list, and I'll add similar text there when I'm dealing with that.
>
> For those wanting to see the driver in context,
> https://github.com/6by9/upstream-linux/tree/unicam is the linux-media
> tree with my mods on top. It also includes a couple of TC358743 and
> OV5647 driver updates that I'll send to the list in the next few days.
>
> Thanks in advance.
>   Dave
>
> Changes from v1 to v2:
> - Broken out a new helper function v4l2_fourcc2s as requested by Hans.
> - Documented difference between this driver and the bcm2835-camera driver
>   in staging/vc04_services.
> - Corrected handling of s_dv_timings and s_std to update the current format
>   but only if not streaming. This refactored some of the s_fmt code to
>   remove duplication.
> - Updated handling of sizeimage to include vertical padding. (Not updated
>   the bytesperline calcs as the app can override).
> - Added support for continuous clock mode (requires changes to lane
>   termination configuration).
> - Add support for CCP2 as Sakari's patches to support it have now been merged.
>   I don't have a suitable sensor to test it with at present, but all settings
>   have been taken from a known working configuration. If people would prefer
>   I remove this until it has been proved against hardware then I'm happy to
>   do so.
> - Updated DT bindings to use <data-lanes> on the Unicam node to set the
>   maximum number of lanes present instead of a having a custom property.
>   Documents the mandatory endpoint properties.
> - Removed RAW16 from the list of input formats as it isn't defined in the
>   CSI-2 spec. The peripheral can still unpack the other Bayer formats to
>   a 16 bit/pixel packing though.
> - Added a log-status handler to get the status from the sensor.
> - Automatically switch away from any interlaced formats reported via g_fmt,
>   or that are attempted to be set via try/s_fmt.
> - Addressed other more minor code review comments from v1.
>
> Dave Stevenson (4):
>   [media] v4l2-common: Add helper function for fourcc to string
>   [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
>   [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
>   MAINTAINERS: Add entry for BCM2835 camera driver
>
>  .../devicetree/bindings/media/bcm2835-unicam.txt   |  107 +
>  MAINTAINERS                                        |    7 +
>  drivers/media/platform/Kconfig                     |    1 +
>  drivers/media/platform/Makefile                    |    1 +
>  drivers/media/platform/bcm2835/Kconfig             |   14 +
>  drivers/media/platform/bcm2835/Makefile            |    3 +
>  drivers/media/platform/bcm2835/bcm2835-unicam.c    | 2192 ++++++++++++++++++++
>  drivers/media/platform/bcm2835/vc4-regs-unicam.h   |  264 +++
>  drivers/media/v4l2-core/v4l2-common.c              |   18 +
>  include/media/v4l2-common.h                        |    3 +
>  10 files changed, 2610 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>  create mode 100644 drivers/media/platform/bcm2835/Kconfig
>  create mode 100644 drivers/media/platform/bcm2835/Makefile
>  create mode 100644 drivers/media/platform/bcm2835/bcm2835-unicam.c
>  create mode 100644 drivers/media/platform/bcm2835/vc4-regs-unicam.h
>
> --
> 2.7.4
>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 3/4] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
  2017-09-13 15:07   ` [PATCH v2 3/4] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface Dave Stevenson
@ 2017-09-18 18:18         ` Eric Anholt
  0 siblings, 0 replies; 20+ messages in thread
From: Eric Anholt @ 2017-09-18 18:18 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Rob Herring, Hans Verkuil, Stefan Wahren,
	Sakari Ailus, linux-media-u79uwXL29TY76Z2rM5mHXA,
	linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: Dave Stevenson

[-- Attachment #1: Type: text/plain, Size: 5467 bytes --]

Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org> writes:
> diff --git a/drivers/media/platform/bcm2835/bcm2835-unicam.c b/drivers/media/platform/bcm2835/bcm2835-unicam.c
> new file mode 100644
> index 0000000..5b1adc3
> --- /dev/null
> +++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
> @@ -0,0 +1,2192 @@
> +/*
> + * BCM2835 Unicam capture Driver
> + *
> + * Copyright (C) 2017 - Raspberry Pi (Trading) Ltd.
> + *
> + * Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
> + *
> + * Based on TI am437x driver by Benoit Parrot and Lad, Prabhakar and
> + * TI CAL camera interface driver by Benoit Parrot.
> + *

Possible future improvement: this description of the driver is really
nice and could be turned into kernel-doc.

> + * There are two camera drivers in the kernel for BCM283x - this one
> + * and bcm2835-camera (currently in staging).
> + *
> + * This driver is purely the kernel control the Unicam peripheral - there

Maybe "This driver directly controls..."?

> + * is no involvement with the VideoCore firmware. Unicam receives CSI-2
> + * or CCP2 data and writes it into SDRAM. The only processing options are
> + * to repack Bayer data into an alternate format, and applying windowing
> + * (currently not implemented).
> + * It should be possible to connect it to any sensor with a
> + * suitable output interface and V4L2 subdevice driver.
> + *
> + * bcm2835-camera uses with the VideoCore firmware to control the sensor,

"uses the"

> + * Unicam, ISP, and all tuner control loops. Fully processed frames are
> + * delivered to the driver by the firmware. It only has sensor drivers
> + * for Omnivision OV5647, and Sony IMX219 sensors.
> + *
> + * The two drivers are mutually exclusive for the same Unicam instance.
> + * The VideoCore firmware checks the device tree configuration during boot.
> + * If it finds device tree nodes called csi0 or csi1 it will block the
> + * firmware from accessing the peripheral, and bcm2835-camera will
> + * not be able to stream data.

Thanks for describing this here!

> +/*
> + * The peripheral can unpack and repack between several of
> + * the Bayer raw formats, so any Bayer format can be advertised
> + * as the same Bayer order in each of the supported bit depths.
> + * Use lower case to avoid clashing with V4L2_PIX_FMT_SGBRG8
> + * formats.
> + */
> +#define PIX_FMT_ALL_BGGR  v4l2_fourcc('b', 'g', 'g', 'r')
> +#define PIX_FMT_ALL_RGGB  v4l2_fourcc('r', 'g', 'g', 'b')
> +#define PIX_FMT_ALL_GBRG  v4l2_fourcc('g', 'b', 'r', 'g')
> +#define PIX_FMT_ALL_GRBG  v4l2_fourcc('g', 'r', 'b', 'g')

Should thes fourccs be defined in a common v4l2 header, to reserve it
from clashing with others later?

This is really the only question I have about this driver before seeing
it merged.  As far as me wearing my platform maintainer hat, I'm happy
with the driver, and my other little notes are optional.

> +static int unicam_probe(struct platform_device *pdev)
> +{
> +	struct unicam_cfg *unicam_cfg;
> +	struct unicam_device *unicam;
> +	struct v4l2_ctrl_handler *hdl;
> +	struct resource	*res;
> +	int ret;
> +
> +	unicam = devm_kzalloc(&pdev->dev, sizeof(*unicam), GFP_KERNEL);
> +	if (!unicam)
> +		return -ENOMEM;
> +
> +	unicam->pdev = pdev;
> +	unicam_cfg = &unicam->cfg;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	unicam_cfg->base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(unicam_cfg->base)) {
> +		unicam_err(unicam, "Failed to get main io block\n");
> +		return PTR_ERR(unicam_cfg->base);
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	unicam_cfg->clk_gate_base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(unicam_cfg->clk_gate_base)) {
> +		unicam_err(unicam, "Failed to get 2nd io block\n");
> +		return PTR_ERR(unicam_cfg->clk_gate_base);
> +	}
> +
> +	unicam->clock = devm_clk_get(&pdev->dev, "lp_clock");
> +	if (IS_ERR(unicam->clock)) {
> +		unicam_err(unicam, "Failed to get clock\n");
> +		return PTR_ERR(unicam->clock);
> +	}
> +
> +	ret = platform_get_irq(pdev, 0);
> +	if (ret <= 0) {
> +		dev_err(&pdev->dev, "No IRQ resource\n");
> +		return -ENODEV;
> +	}
> +	unicam_cfg->irq = ret;
> +
> +	ret = devm_request_irq(&pdev->dev, unicam_cfg->irq, unicam_isr, 0,
> +			       "unicam_capture0", unicam);

Looks like there's no need to keep "irq" in the device private struct.

> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to request interrupt\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = v4l2_device_register(&pdev->dev, &unicam->v4l2_dev);
> +	if (ret) {
> +		unicam_err(unicam,
> +			   "Unable to register v4l2 device.\n");
> +		return ret;
> +	}
> +
> +	/* Reserve space for the controls */
> +	hdl = &unicam->ctrl_handler;
> +	ret = v4l2_ctrl_handler_init(hdl, 16);
> +	if (ret < 0)
> +		goto probe_out_v4l2_unregister;
> +	unicam->v4l2_dev.ctrl_handler = hdl;
> +
> +	/* set the driver data in platform device */
> +	platform_set_drvdata(pdev, unicam);
> +
> +	ret = of_unicam_connect_subdevs(unicam);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to connect subdevs\n");
> +		goto free_hdl;
> +	}
> +
> +	/* Enabling module functional clock */
> +	pm_runtime_enable(&pdev->dev);

I think pm_runtime is only controlling the power domain for the PHY, not
the clock (which you're handling manually).

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 3/4] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
@ 2017-09-18 18:18         ` Eric Anholt
  0 siblings, 0 replies; 20+ messages in thread
From: Eric Anholt @ 2017-09-18 18:18 UTC (permalink / raw)
  To: Dave Stevenson, Mauro Carvalho Chehab, Rob Herring, Hans Verkuil,
	Stefan Wahren, Sakari Ailus, linux-media, linux-rpi-kernel,
	devicetree
  Cc: Dave Stevenson

[-- Attachment #1: Type: text/plain, Size: 5419 bytes --]

Dave Stevenson <dave.stevenson@raspberrypi.org> writes:
> diff --git a/drivers/media/platform/bcm2835/bcm2835-unicam.c b/drivers/media/platform/bcm2835/bcm2835-unicam.c
> new file mode 100644
> index 0000000..5b1adc3
> --- /dev/null
> +++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
> @@ -0,0 +1,2192 @@
> +/*
> + * BCM2835 Unicam capture Driver
> + *
> + * Copyright (C) 2017 - Raspberry Pi (Trading) Ltd.
> + *
> + * Dave Stevenson <dave.stevenson@raspberrypi.org>
> + *
> + * Based on TI am437x driver by Benoit Parrot and Lad, Prabhakar and
> + * TI CAL camera interface driver by Benoit Parrot.
> + *

Possible future improvement: this description of the driver is really
nice and could be turned into kernel-doc.

> + * There are two camera drivers in the kernel for BCM283x - this one
> + * and bcm2835-camera (currently in staging).
> + *
> + * This driver is purely the kernel control the Unicam peripheral - there

Maybe "This driver directly controls..."?

> + * is no involvement with the VideoCore firmware. Unicam receives CSI-2
> + * or CCP2 data and writes it into SDRAM. The only processing options are
> + * to repack Bayer data into an alternate format, and applying windowing
> + * (currently not implemented).
> + * It should be possible to connect it to any sensor with a
> + * suitable output interface and V4L2 subdevice driver.
> + *
> + * bcm2835-camera uses with the VideoCore firmware to control the sensor,

"uses the"

> + * Unicam, ISP, and all tuner control loops. Fully processed frames are
> + * delivered to the driver by the firmware. It only has sensor drivers
> + * for Omnivision OV5647, and Sony IMX219 sensors.
> + *
> + * The two drivers are mutually exclusive for the same Unicam instance.
> + * The VideoCore firmware checks the device tree configuration during boot.
> + * If it finds device tree nodes called csi0 or csi1 it will block the
> + * firmware from accessing the peripheral, and bcm2835-camera will
> + * not be able to stream data.

Thanks for describing this here!

> +/*
> + * The peripheral can unpack and repack between several of
> + * the Bayer raw formats, so any Bayer format can be advertised
> + * as the same Bayer order in each of the supported bit depths.
> + * Use lower case to avoid clashing with V4L2_PIX_FMT_SGBRG8
> + * formats.
> + */
> +#define PIX_FMT_ALL_BGGR  v4l2_fourcc('b', 'g', 'g', 'r')
> +#define PIX_FMT_ALL_RGGB  v4l2_fourcc('r', 'g', 'g', 'b')
> +#define PIX_FMT_ALL_GBRG  v4l2_fourcc('g', 'b', 'r', 'g')
> +#define PIX_FMT_ALL_GRBG  v4l2_fourcc('g', 'r', 'b', 'g')

Should thes fourccs be defined in a common v4l2 header, to reserve it
from clashing with others later?

This is really the only question I have about this driver before seeing
it merged.  As far as me wearing my platform maintainer hat, I'm happy
with the driver, and my other little notes are optional.

> +static int unicam_probe(struct platform_device *pdev)
> +{
> +	struct unicam_cfg *unicam_cfg;
> +	struct unicam_device *unicam;
> +	struct v4l2_ctrl_handler *hdl;
> +	struct resource	*res;
> +	int ret;
> +
> +	unicam = devm_kzalloc(&pdev->dev, sizeof(*unicam), GFP_KERNEL);
> +	if (!unicam)
> +		return -ENOMEM;
> +
> +	unicam->pdev = pdev;
> +	unicam_cfg = &unicam->cfg;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	unicam_cfg->base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(unicam_cfg->base)) {
> +		unicam_err(unicam, "Failed to get main io block\n");
> +		return PTR_ERR(unicam_cfg->base);
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	unicam_cfg->clk_gate_base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(unicam_cfg->clk_gate_base)) {
> +		unicam_err(unicam, "Failed to get 2nd io block\n");
> +		return PTR_ERR(unicam_cfg->clk_gate_base);
> +	}
> +
> +	unicam->clock = devm_clk_get(&pdev->dev, "lp_clock");
> +	if (IS_ERR(unicam->clock)) {
> +		unicam_err(unicam, "Failed to get clock\n");
> +		return PTR_ERR(unicam->clock);
> +	}
> +
> +	ret = platform_get_irq(pdev, 0);
> +	if (ret <= 0) {
> +		dev_err(&pdev->dev, "No IRQ resource\n");
> +		return -ENODEV;
> +	}
> +	unicam_cfg->irq = ret;
> +
> +	ret = devm_request_irq(&pdev->dev, unicam_cfg->irq, unicam_isr, 0,
> +			       "unicam_capture0", unicam);

Looks like there's no need to keep "irq" in the device private struct.

> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to request interrupt\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = v4l2_device_register(&pdev->dev, &unicam->v4l2_dev);
> +	if (ret) {
> +		unicam_err(unicam,
> +			   "Unable to register v4l2 device.\n");
> +		return ret;
> +	}
> +
> +	/* Reserve space for the controls */
> +	hdl = &unicam->ctrl_handler;
> +	ret = v4l2_ctrl_handler_init(hdl, 16);
> +	if (ret < 0)
> +		goto probe_out_v4l2_unregister;
> +	unicam->v4l2_dev.ctrl_handler = hdl;
> +
> +	/* set the driver data in platform device */
> +	platform_set_drvdata(pdev, unicam);
> +
> +	ret = of_unicam_connect_subdevs(unicam);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to connect subdevs\n");
> +		goto free_hdl;
> +	}
> +
> +	/* Enabling module functional clock */
> +	pm_runtime_enable(&pdev->dev);

I think pm_runtime is only controlling the power domain for the PHY, not
the clock (which you're handling manually).

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 3/4] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
  2017-09-18 18:18         ` Eric Anholt
@ 2017-09-19  9:50             ` Dave Stevenson
  -1 siblings, 0 replies; 20+ messages in thread
From: Dave Stevenson @ 2017-09-19  9:50 UTC (permalink / raw)
  To: Eric Anholt
  Cc: Mauro Carvalho Chehab, Rob Herring, Hans Verkuil, Stefan Wahren,
	Sakari Ailus, linux-media-u79uwXL29TY76Z2rM5mHXA,
	linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Eric.

Thanks for the review.

On 18 September 2017 at 19:18, Eric Anholt <eric-WhKQ6XTQaPysTnJN9+BGXg@public.gmane.org> wrote:
> Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org> writes:
>> diff --git a/drivers/media/platform/bcm2835/bcm2835-unicam.c b/drivers/media/platform/bcm2835/bcm2835-unicam.c
>> new file mode 100644
>> index 0000000..5b1adc3
>> --- /dev/null
>> +++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
>> @@ -0,0 +1,2192 @@
>> +/*
>> + * BCM2835 Unicam capture Driver
>> + *
>> + * Copyright (C) 2017 - Raspberry Pi (Trading) Ltd.
>> + *
>> + * Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
>> + *
>> + * Based on TI am437x driver by Benoit Parrot and Lad, Prabhakar and
>> + * TI CAL camera interface driver by Benoit Parrot.
>> + *
>
> Possible future improvement: this description of the driver is really
> nice and could be turned into kernel-doc.

Documentation?! Surely not :-)
For now I'll leave it as a task for another day.

>> + * There are two camera drivers in the kernel for BCM283x - this one
>> + * and bcm2835-camera (currently in staging).
>> + *
>> + * This driver is purely the kernel control the Unicam peripheral - there
>
> Maybe "This driver directly controls..."?

Will do in v3.

>> + * is no involvement with the VideoCore firmware. Unicam receives CSI-2
>> + * or CCP2 data and writes it into SDRAM. The only processing options are
>> + * to repack Bayer data into an alternate format, and applying windowing
>> + * (currently not implemented).
>> + * It should be possible to connect it to any sensor with a
>> + * suitable output interface and V4L2 subdevice driver.
>> + *
>> + * bcm2835-camera uses with the VideoCore firmware to control the sensor,
>
> "uses the"

Will do in v3.

>> + * Unicam, ISP, and all tuner control loops. Fully processed frames are
>> + * delivered to the driver by the firmware. It only has sensor drivers
>> + * for Omnivision OV5647, and Sony IMX219 sensors.
>> + *
>> + * The two drivers are mutually exclusive for the same Unicam instance.
>> + * The VideoCore firmware checks the device tree configuration during boot.
>> + * If it finds device tree nodes called csi0 or csi1 it will block the
>> + * firmware from accessing the peripheral, and bcm2835-camera will
>> + * not be able to stream data.
>
> Thanks for describing this here!
>
>> +/*
>> + * The peripheral can unpack and repack between several of
>> + * the Bayer raw formats, so any Bayer format can be advertised
>> + * as the same Bayer order in each of the supported bit depths.
>> + * Use lower case to avoid clashing with V4L2_PIX_FMT_SGBRG8
>> + * formats.
>> + */
>> +#define PIX_FMT_ALL_BGGR  v4l2_fourcc('b', 'g', 'g', 'r')
>> +#define PIX_FMT_ALL_RGGB  v4l2_fourcc('r', 'g', 'g', 'b')
>> +#define PIX_FMT_ALL_GBRG  v4l2_fourcc('g', 'b', 'r', 'g')
>> +#define PIX_FMT_ALL_GRBG  v4l2_fourcc('g', 'r', 'b', 'g')
>
> Should thes fourccs be defined in a common v4l2 header, to reserve it
> from clashing with others later?

I'm only using them as flags and probably in a manner that nothing
else is likely to copy, so it seems a little excessive to put them in
a common header.
Perhaps it's better to switch to 0xFFFFFFF0 to 0xFFFFFFF3 or other
value that won't come up as a fourcc under any normal circumstance.
Any thoughts from other people?

> This is really the only question I have about this driver before seeing
> it merged.  As far as me wearing my platform maintainer hat, I'm happy
> with the driver, and my other little notes are optional.
>
>> +static int unicam_probe(struct platform_device *pdev)
>> +{
>> +     struct unicam_cfg *unicam_cfg;
>> +     struct unicam_device *unicam;
>> +     struct v4l2_ctrl_handler *hdl;
>> +     struct resource *res;
>> +     int ret;
>> +
>> +     unicam = devm_kzalloc(&pdev->dev, sizeof(*unicam), GFP_KERNEL);
>> +     if (!unicam)
>> +             return -ENOMEM;
>> +
>> +     unicam->pdev = pdev;
>> +     unicam_cfg = &unicam->cfg;
>> +
>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +     unicam_cfg->base = devm_ioremap_resource(&pdev->dev, res);
>> +     if (IS_ERR(unicam_cfg->base)) {
>> +             unicam_err(unicam, "Failed to get main io block\n");
>> +             return PTR_ERR(unicam_cfg->base);
>> +     }
>> +
>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>> +     unicam_cfg->clk_gate_base = devm_ioremap_resource(&pdev->dev, res);
>> +     if (IS_ERR(unicam_cfg->clk_gate_base)) {
>> +             unicam_err(unicam, "Failed to get 2nd io block\n");
>> +             return PTR_ERR(unicam_cfg->clk_gate_base);
>> +     }
>> +
>> +     unicam->clock = devm_clk_get(&pdev->dev, "lp_clock");
>> +     if (IS_ERR(unicam->clock)) {
>> +             unicam_err(unicam, "Failed to get clock\n");
>> +             return PTR_ERR(unicam->clock);
>> +     }
>> +
>> +     ret = platform_get_irq(pdev, 0);
>> +     if (ret <= 0) {
>> +             dev_err(&pdev->dev, "No IRQ resource\n");
>> +             return -ENODEV;
>> +     }
>> +     unicam_cfg->irq = ret;
>> +
>> +     ret = devm_request_irq(&pdev->dev, unicam_cfg->irq, unicam_isr, 0,
>> +                            "unicam_capture0", unicam);
>
> Looks like there's no need to keep "irq" in the device private struct.

Agreed. I'll remove in v3.

>> +     if (ret) {
>> +             dev_err(&pdev->dev, "Unable to request interrupt\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     ret = v4l2_device_register(&pdev->dev, &unicam->v4l2_dev);
>> +     if (ret) {
>> +             unicam_err(unicam,
>> +                        "Unable to register v4l2 device.\n");
>> +             return ret;
>> +     }
>> +
>> +     /* Reserve space for the controls */
>> +     hdl = &unicam->ctrl_handler;
>> +     ret = v4l2_ctrl_handler_init(hdl, 16);
>> +     if (ret < 0)
>> +             goto probe_out_v4l2_unregister;
>> +     unicam->v4l2_dev.ctrl_handler = hdl;
>> +
>> +     /* set the driver data in platform device */
>> +     platform_set_drvdata(pdev, unicam);
>> +
>> +     ret = of_unicam_connect_subdevs(unicam);
>> +     if (ret) {
>> +             dev_err(&pdev->dev, "Failed to connect subdevs\n");
>> +             goto free_hdl;
>> +     }
>> +
>> +     /* Enabling module functional clock */
>> +     pm_runtime_enable(&pdev->dev);
>
> I think pm_runtime is only controlling the power domain for the PHY, not
> the clock (which you're handling manually).

You're right. Copy and paste from the driver I'd based this on.
Will correct in v3.

  Dave
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 3/4] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
@ 2017-09-19  9:50             ` Dave Stevenson
  0 siblings, 0 replies; 20+ messages in thread
From: Dave Stevenson @ 2017-09-19  9:50 UTC (permalink / raw)
  To: Eric Anholt
  Cc: Mauro Carvalho Chehab, Rob Herring, Hans Verkuil, Stefan Wahren,
	Sakari Ailus, linux-media, linux-rpi-kernel, devicetree

Hi Eric.

Thanks for the review.

On 18 September 2017 at 19:18, Eric Anholt <eric@anholt.net> wrote:
> Dave Stevenson <dave.stevenson@raspberrypi.org> writes:
>> diff --git a/drivers/media/platform/bcm2835/bcm2835-unicam.c b/drivers/media/platform/bcm2835/bcm2835-unicam.c
>> new file mode 100644
>> index 0000000..5b1adc3
>> --- /dev/null
>> +++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
>> @@ -0,0 +1,2192 @@
>> +/*
>> + * BCM2835 Unicam capture Driver
>> + *
>> + * Copyright (C) 2017 - Raspberry Pi (Trading) Ltd.
>> + *
>> + * Dave Stevenson <dave.stevenson@raspberrypi.org>
>> + *
>> + * Based on TI am437x driver by Benoit Parrot and Lad, Prabhakar and
>> + * TI CAL camera interface driver by Benoit Parrot.
>> + *
>
> Possible future improvement: this description of the driver is really
> nice and could be turned into kernel-doc.

Documentation?! Surely not :-)
For now I'll leave it as a task for another day.

>> + * There are two camera drivers in the kernel for BCM283x - this one
>> + * and bcm2835-camera (currently in staging).
>> + *
>> + * This driver is purely the kernel control the Unicam peripheral - there
>
> Maybe "This driver directly controls..."?

Will do in v3.

>> + * is no involvement with the VideoCore firmware. Unicam receives CSI-2
>> + * or CCP2 data and writes it into SDRAM. The only processing options are
>> + * to repack Bayer data into an alternate format, and applying windowing
>> + * (currently not implemented).
>> + * It should be possible to connect it to any sensor with a
>> + * suitable output interface and V4L2 subdevice driver.
>> + *
>> + * bcm2835-camera uses with the VideoCore firmware to control the sensor,
>
> "uses the"

Will do in v3.

>> + * Unicam, ISP, and all tuner control loops. Fully processed frames are
>> + * delivered to the driver by the firmware. It only has sensor drivers
>> + * for Omnivision OV5647, and Sony IMX219 sensors.
>> + *
>> + * The two drivers are mutually exclusive for the same Unicam instance.
>> + * The VideoCore firmware checks the device tree configuration during boot.
>> + * If it finds device tree nodes called csi0 or csi1 it will block the
>> + * firmware from accessing the peripheral, and bcm2835-camera will
>> + * not be able to stream data.
>
> Thanks for describing this here!
>
>> +/*
>> + * The peripheral can unpack and repack between several of
>> + * the Bayer raw formats, so any Bayer format can be advertised
>> + * as the same Bayer order in each of the supported bit depths.
>> + * Use lower case to avoid clashing with V4L2_PIX_FMT_SGBRG8
>> + * formats.
>> + */
>> +#define PIX_FMT_ALL_BGGR  v4l2_fourcc('b', 'g', 'g', 'r')
>> +#define PIX_FMT_ALL_RGGB  v4l2_fourcc('r', 'g', 'g', 'b')
>> +#define PIX_FMT_ALL_GBRG  v4l2_fourcc('g', 'b', 'r', 'g')
>> +#define PIX_FMT_ALL_GRBG  v4l2_fourcc('g', 'r', 'b', 'g')
>
> Should thes fourccs be defined in a common v4l2 header, to reserve it
> from clashing with others later?

I'm only using them as flags and probably in a manner that nothing
else is likely to copy, so it seems a little excessive to put them in
a common header.
Perhaps it's better to switch to 0xFFFFFFF0 to 0xFFFFFFF3 or other
value that won't come up as a fourcc under any normal circumstance.
Any thoughts from other people?

> This is really the only question I have about this driver before seeing
> it merged.  As far as me wearing my platform maintainer hat, I'm happy
> with the driver, and my other little notes are optional.
>
>> +static int unicam_probe(struct platform_device *pdev)
>> +{
>> +     struct unicam_cfg *unicam_cfg;
>> +     struct unicam_device *unicam;
>> +     struct v4l2_ctrl_handler *hdl;
>> +     struct resource *res;
>> +     int ret;
>> +
>> +     unicam = devm_kzalloc(&pdev->dev, sizeof(*unicam), GFP_KERNEL);
>> +     if (!unicam)
>> +             return -ENOMEM;
>> +
>> +     unicam->pdev = pdev;
>> +     unicam_cfg = &unicam->cfg;
>> +
>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +     unicam_cfg->base = devm_ioremap_resource(&pdev->dev, res);
>> +     if (IS_ERR(unicam_cfg->base)) {
>> +             unicam_err(unicam, "Failed to get main io block\n");
>> +             return PTR_ERR(unicam_cfg->base);
>> +     }
>> +
>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>> +     unicam_cfg->clk_gate_base = devm_ioremap_resource(&pdev->dev, res);
>> +     if (IS_ERR(unicam_cfg->clk_gate_base)) {
>> +             unicam_err(unicam, "Failed to get 2nd io block\n");
>> +             return PTR_ERR(unicam_cfg->clk_gate_base);
>> +     }
>> +
>> +     unicam->clock = devm_clk_get(&pdev->dev, "lp_clock");
>> +     if (IS_ERR(unicam->clock)) {
>> +             unicam_err(unicam, "Failed to get clock\n");
>> +             return PTR_ERR(unicam->clock);
>> +     }
>> +
>> +     ret = platform_get_irq(pdev, 0);
>> +     if (ret <= 0) {
>> +             dev_err(&pdev->dev, "No IRQ resource\n");
>> +             return -ENODEV;
>> +     }
>> +     unicam_cfg->irq = ret;
>> +
>> +     ret = devm_request_irq(&pdev->dev, unicam_cfg->irq, unicam_isr, 0,
>> +                            "unicam_capture0", unicam);
>
> Looks like there's no need to keep "irq" in the device private struct.

Agreed. I'll remove in v3.

>> +     if (ret) {
>> +             dev_err(&pdev->dev, "Unable to request interrupt\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     ret = v4l2_device_register(&pdev->dev, &unicam->v4l2_dev);
>> +     if (ret) {
>> +             unicam_err(unicam,
>> +                        "Unable to register v4l2 device.\n");
>> +             return ret;
>> +     }
>> +
>> +     /* Reserve space for the controls */
>> +     hdl = &unicam->ctrl_handler;
>> +     ret = v4l2_ctrl_handler_init(hdl, 16);
>> +     if (ret < 0)
>> +             goto probe_out_v4l2_unregister;
>> +     unicam->v4l2_dev.ctrl_handler = hdl;
>> +
>> +     /* set the driver data in platform device */
>> +     platform_set_drvdata(pdev, unicam);
>> +
>> +     ret = of_unicam_connect_subdevs(unicam);
>> +     if (ret) {
>> +             dev_err(&pdev->dev, "Failed to connect subdevs\n");
>> +             goto free_hdl;
>> +     }
>> +
>> +     /* Enabling module functional clock */
>> +     pm_runtime_enable(&pdev->dev);
>
> I think pm_runtime is only controlling the power domain for the PHY, not
> the clock (which you're handling manually).

You're right. Copy and paste from the driver I'd based this on.
Will correct in v3.

  Dave

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 3/4] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
  2017-09-19  9:50             ` Dave Stevenson
  (?)
@ 2017-09-19 10:20             ` Hans Verkuil
  2017-09-19 11:42               ` Dave Stevenson
  -1 siblings, 1 reply; 20+ messages in thread
From: Hans Verkuil @ 2017-09-19 10:20 UTC (permalink / raw)
  To: Dave Stevenson, Eric Anholt
  Cc: Mauro Carvalho Chehab, Rob Herring, Hans Verkuil, Stefan Wahren,
	Sakari Ailus, linux-media, linux-rpi-kernel, devicetree

On 09/19/17 11:50, Dave Stevenson wrote:
> Hi Eric.
> 
> Thanks for the review.
> 
> On 18 September 2017 at 19:18, Eric Anholt <eric@anholt.net> wrote:
>> Dave Stevenson <dave.stevenson@raspberrypi.org> writes:
>>> diff --git a/drivers/media/platform/bcm2835/bcm2835-unicam.c b/drivers/media/platform/bcm2835/bcm2835-unicam.c
>>> new file mode 100644
>>> index 0000000..5b1adc3
>>> --- /dev/null
>>> +++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
>>> @@ -0,0 +1,2192 @@
>>> +/*
>>> + * BCM2835 Unicam capture Driver
>>> + *
>>> + * Copyright (C) 2017 - Raspberry Pi (Trading) Ltd.
>>> + *
>>> + * Dave Stevenson <dave.stevenson@raspberrypi.org>
>>> + *
>>> + * Based on TI am437x driver by Benoit Parrot and Lad, Prabhakar and
>>> + * TI CAL camera interface driver by Benoit Parrot.
>>> + *
>>
>> Possible future improvement: this description of the driver is really
>> nice and could be turned into kernel-doc.
> 
> Documentation?! Surely not :-)
> For now I'll leave it as a task for another day.
> 
>>> + * There are two camera drivers in the kernel for BCM283x - this one
>>> + * and bcm2835-camera (currently in staging).
>>> + *
>>> + * This driver is purely the kernel control the Unicam peripheral - there
>>
>> Maybe "This driver directly controls..."?
> 
> Will do in v3.
> 
>>> + * is no involvement with the VideoCore firmware. Unicam receives CSI-2
>>> + * or CCP2 data and writes it into SDRAM. The only processing options are
>>> + * to repack Bayer data into an alternate format, and applying windowing
>>> + * (currently not implemented).
>>> + * It should be possible to connect it to any sensor with a
>>> + * suitable output interface and V4L2 subdevice driver.
>>> + *
>>> + * bcm2835-camera uses with the VideoCore firmware to control the sensor,
>>
>> "uses the"
> 
> Will do in v3.
> 
>>> + * Unicam, ISP, and all tuner control loops. Fully processed frames are
>>> + * delivered to the driver by the firmware. It only has sensor drivers
>>> + * for Omnivision OV5647, and Sony IMX219 sensors.
>>> + *
>>> + * The two drivers are mutually exclusive for the same Unicam instance.
>>> + * The VideoCore firmware checks the device tree configuration during boot.
>>> + * If it finds device tree nodes called csi0 or csi1 it will block the
>>> + * firmware from accessing the peripheral, and bcm2835-camera will
>>> + * not be able to stream data.
>>
>> Thanks for describing this here!
>>
>>> +/*
>>> + * The peripheral can unpack and repack between several of
>>> + * the Bayer raw formats, so any Bayer format can be advertised
>>> + * as the same Bayer order in each of the supported bit depths.
>>> + * Use lower case to avoid clashing with V4L2_PIX_FMT_SGBRG8
>>> + * formats.
>>> + */
>>> +#define PIX_FMT_ALL_BGGR  v4l2_fourcc('b', 'g', 'g', 'r')
>>> +#define PIX_FMT_ALL_RGGB  v4l2_fourcc('r', 'g', 'g', 'b')
>>> +#define PIX_FMT_ALL_GBRG  v4l2_fourcc('g', 'b', 'r', 'g')
>>> +#define PIX_FMT_ALL_GRBG  v4l2_fourcc('g', 'r', 'b', 'g')
>>
>> Should thes fourccs be defined in a common v4l2 header, to reserve it
>> from clashing with others later?
> 
> I'm only using them as flags and probably in a manner that nothing
> else is likely to copy, so it seems a little excessive to put them in
> a common header.
> Perhaps it's better to switch to 0xFFFFFFF0 to 0xFFFFFFF3 or other
> value that won't come up as a fourcc under any normal circumstance.
> Any thoughts from other people?

I think that's better, yes.

> 
>> This is really the only question I have about this driver before seeing
>> it merged.  As far as me wearing my platform maintainer hat, I'm happy
>> with the driver, and my other little notes are optional.
>>
>>> +static int unicam_probe(struct platform_device *pdev)
>>> +{
>>> +     struct unicam_cfg *unicam_cfg;
>>> +     struct unicam_device *unicam;
>>> +     struct v4l2_ctrl_handler *hdl;
>>> +     struct resource *res;
>>> +     int ret;
>>> +
>>> +     unicam = devm_kzalloc(&pdev->dev, sizeof(*unicam), GFP_KERNEL);
>>> +     if (!unicam)
>>> +             return -ENOMEM;
>>> +
>>> +     unicam->pdev = pdev;
>>> +     unicam_cfg = &unicam->cfg;
>>> +
>>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> +     unicam_cfg->base = devm_ioremap_resource(&pdev->dev, res);
>>> +     if (IS_ERR(unicam_cfg->base)) {
>>> +             unicam_err(unicam, "Failed to get main io block\n");
>>> +             return PTR_ERR(unicam_cfg->base);
>>> +     }
>>> +
>>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>>> +     unicam_cfg->clk_gate_base = devm_ioremap_resource(&pdev->dev, res);
>>> +     if (IS_ERR(unicam_cfg->clk_gate_base)) {
>>> +             unicam_err(unicam, "Failed to get 2nd io block\n");
>>> +             return PTR_ERR(unicam_cfg->clk_gate_base);
>>> +     }
>>> +
>>> +     unicam->clock = devm_clk_get(&pdev->dev, "lp_clock");
>>> +     if (IS_ERR(unicam->clock)) {
>>> +             unicam_err(unicam, "Failed to get clock\n");
>>> +             return PTR_ERR(unicam->clock);
>>> +     }
>>> +
>>> +     ret = platform_get_irq(pdev, 0);
>>> +     if (ret <= 0) {
>>> +             dev_err(&pdev->dev, "No IRQ resource\n");
>>> +             return -ENODEV;
>>> +     }
>>> +     unicam_cfg->irq = ret;
>>> +
>>> +     ret = devm_request_irq(&pdev->dev, unicam_cfg->irq, unicam_isr, 0,
>>> +                            "unicam_capture0", unicam);
>>
>> Looks like there's no need to keep "irq" in the device private struct.
> 
> Agreed. I'll remove in v3.
> 
>>> +     if (ret) {
>>> +             dev_err(&pdev->dev, "Unable to request interrupt\n");
>>> +             return -EINVAL;
>>> +     }
>>> +
>>> +     ret = v4l2_device_register(&pdev->dev, &unicam->v4l2_dev);
>>> +     if (ret) {
>>> +             unicam_err(unicam,
>>> +                        "Unable to register v4l2 device.\n");
>>> +             return ret;
>>> +     }
>>> +
>>> +     /* Reserve space for the controls */
>>> +     hdl = &unicam->ctrl_handler;
>>> +     ret = v4l2_ctrl_handler_init(hdl, 16);
>>> +     if (ret < 0)
>>> +             goto probe_out_v4l2_unregister;
>>> +     unicam->v4l2_dev.ctrl_handler = hdl;
>>> +
>>> +     /* set the driver data in platform device */
>>> +     platform_set_drvdata(pdev, unicam);
>>> +
>>> +     ret = of_unicam_connect_subdevs(unicam);
>>> +     if (ret) {
>>> +             dev_err(&pdev->dev, "Failed to connect subdevs\n");
>>> +             goto free_hdl;
>>> +     }
>>> +
>>> +     /* Enabling module functional clock */
>>> +     pm_runtime_enable(&pdev->dev);
>>
>> I think pm_runtime is only controlling the power domain for the PHY, not
>> the clock (which you're handling manually).
> 
> You're right. Copy and paste from the driver I'd based this on.
> Will correct in v3.
> 
>   Dave
> 

Dave, I plan to review this Friday or Monday. It would help me if you could
post a v3 before Friday so that I'm reviewing the latest code.

It would be great if you can also post your tc358743 patches. I have an RPi
with a tc358743 attached, so it would be very useful if I can review and test
both this driver and the tc358743 changes.

I also have a selfish motive: I want to do a CEC demo next week during the
Kernel Recipes conference with my RPi/tc358743 and your driver. It's nice if
I can use the latest version for that.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 3/4] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
  2017-09-19 10:20             ` Hans Verkuil
@ 2017-09-19 11:42               ` Dave Stevenson
  0 siblings, 0 replies; 20+ messages in thread
From: Dave Stevenson @ 2017-09-19 11:42 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Eric Anholt, Mauro Carvalho Chehab, Rob Herring, Hans Verkuil,
	Stefan Wahren, Sakari Ailus, linux-media, linux-rpi-kernel,
	devicetree

Hi Hans.

Thanks for the response.

On 19 September 2017 at 11:20, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> On 09/19/17 11:50, Dave Stevenson wrote:
>> Hi Eric.
>>
>> Thanks for the review.
>>
>> On 18 September 2017 at 19:18, Eric Anholt <eric@anholt.net> wrote:
>>> Dave Stevenson <dave.stevenson@raspberrypi.org> writes:
>>>> diff --git a/drivers/media/platform/bcm2835/bcm2835-unicam.c b/drivers/media/platform/bcm2835/bcm2835-unicam.c
>>>> new file mode 100644
>>>> index 0000000..5b1adc3
>>>> --- /dev/null
>>>> +++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
>>>> @@ -0,0 +1,2192 @@
>>>> +/*
>>>> + * BCM2835 Unicam capture Driver
>>>> + *
>>>> + * Copyright (C) 2017 - Raspberry Pi (Trading) Ltd.
>>>> + *
>>>> + * Dave Stevenson <dave.stevenson@raspberrypi.org>
>>>> + *
>>>> + * Based on TI am437x driver by Benoit Parrot and Lad, Prabhakar and
>>>> + * TI CAL camera interface driver by Benoit Parrot.
>>>> + *
>>>
>>> Possible future improvement: this description of the driver is really
>>> nice and could be turned into kernel-doc.
>>
>> Documentation?! Surely not :-)
>> For now I'll leave it as a task for another day.
>>
>>>> + * There are two camera drivers in the kernel for BCM283x - this one
>>>> + * and bcm2835-camera (currently in staging).
>>>> + *
>>>> + * This driver is purely the kernel control the Unicam peripheral - there
>>>
>>> Maybe "This driver directly controls..."?
>>
>> Will do in v3.
>>
>>>> + * is no involvement with the VideoCore firmware. Unicam receives CSI-2
>>>> + * or CCP2 data and writes it into SDRAM. The only processing options are
>>>> + * to repack Bayer data into an alternate format, and applying windowing
>>>> + * (currently not implemented).
>>>> + * It should be possible to connect it to any sensor with a
>>>> + * suitable output interface and V4L2 subdevice driver.
>>>> + *
>>>> + * bcm2835-camera uses with the VideoCore firmware to control the sensor,
>>>
>>> "uses the"
>>
>> Will do in v3.
>>
>>>> + * Unicam, ISP, and all tuner control loops. Fully processed frames are
>>>> + * delivered to the driver by the firmware. It only has sensor drivers
>>>> + * for Omnivision OV5647, and Sony IMX219 sensors.
>>>> + *
>>>> + * The two drivers are mutually exclusive for the same Unicam instance.
>>>> + * The VideoCore firmware checks the device tree configuration during boot.
>>>> + * If it finds device tree nodes called csi0 or csi1 it will block the
>>>> + * firmware from accessing the peripheral, and bcm2835-camera will
>>>> + * not be able to stream data.
>>>
>>> Thanks for describing this here!
>>>
>>>> +/*
>>>> + * The peripheral can unpack and repack between several of
>>>> + * the Bayer raw formats, so any Bayer format can be advertised
>>>> + * as the same Bayer order in each of the supported bit depths.
>>>> + * Use lower case to avoid clashing with V4L2_PIX_FMT_SGBRG8
>>>> + * formats.
>>>> + */
>>>> +#define PIX_FMT_ALL_BGGR  v4l2_fourcc('b', 'g', 'g', 'r')
>>>> +#define PIX_FMT_ALL_RGGB  v4l2_fourcc('r', 'g', 'g', 'b')
>>>> +#define PIX_FMT_ALL_GBRG  v4l2_fourcc('g', 'b', 'r', 'g')
>>>> +#define PIX_FMT_ALL_GRBG  v4l2_fourcc('g', 'r', 'b', 'g')
>>>
>>> Should thes fourccs be defined in a common v4l2 header, to reserve it
>>> from clashing with others later?
>>
>> I'm only using them as flags and probably in a manner that nothing
>> else is likely to copy, so it seems a little excessive to put them in
>> a common header.
>> Perhaps it's better to switch to 0xFFFFFFF0 to 0xFFFFFFF3 or other
>> value that won't come up as a fourcc under any normal circumstance.
>> Any thoughts from other people?
>
> I think that's better, yes.

OK, happy to do that.

>>
>>> This is really the only question I have about this driver before seeing
>>> it merged.  As far as me wearing my platform maintainer hat, I'm happy
>>> with the driver, and my other little notes are optional.
>>>
>>>> +static int unicam_probe(struct platform_device *pdev)
>>>> +{
>>>> +     struct unicam_cfg *unicam_cfg;
>>>> +     struct unicam_device *unicam;
>>>> +     struct v4l2_ctrl_handler *hdl;
>>>> +     struct resource *res;
>>>> +     int ret;
>>>> +
>>>> +     unicam = devm_kzalloc(&pdev->dev, sizeof(*unicam), GFP_KERNEL);
>>>> +     if (!unicam)
>>>> +             return -ENOMEM;
>>>> +
>>>> +     unicam->pdev = pdev;
>>>> +     unicam_cfg = &unicam->cfg;
>>>> +
>>>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>> +     unicam_cfg->base = devm_ioremap_resource(&pdev->dev, res);
>>>> +     if (IS_ERR(unicam_cfg->base)) {
>>>> +             unicam_err(unicam, "Failed to get main io block\n");
>>>> +             return PTR_ERR(unicam_cfg->base);
>>>> +     }
>>>> +
>>>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>>>> +     unicam_cfg->clk_gate_base = devm_ioremap_resource(&pdev->dev, res);
>>>> +     if (IS_ERR(unicam_cfg->clk_gate_base)) {
>>>> +             unicam_err(unicam, "Failed to get 2nd io block\n");
>>>> +             return PTR_ERR(unicam_cfg->clk_gate_base);
>>>> +     }
>>>> +
>>>> +     unicam->clock = devm_clk_get(&pdev->dev, "lp_clock");
>>>> +     if (IS_ERR(unicam->clock)) {
>>>> +             unicam_err(unicam, "Failed to get clock\n");
>>>> +             return PTR_ERR(unicam->clock);
>>>> +     }
>>>> +
>>>> +     ret = platform_get_irq(pdev, 0);
>>>> +     if (ret <= 0) {
>>>> +             dev_err(&pdev->dev, "No IRQ resource\n");
>>>> +             return -ENODEV;
>>>> +     }
>>>> +     unicam_cfg->irq = ret;
>>>> +
>>>> +     ret = devm_request_irq(&pdev->dev, unicam_cfg->irq, unicam_isr, 0,
>>>> +                            "unicam_capture0", unicam);
>>>
>>> Looks like there's no need to keep "irq" in the device private struct.
>>
>> Agreed. I'll remove in v3.
>>
>>>> +     if (ret) {
>>>> +             dev_err(&pdev->dev, "Unable to request interrupt\n");
>>>> +             return -EINVAL;
>>>> +     }
>>>> +
>>>> +     ret = v4l2_device_register(&pdev->dev, &unicam->v4l2_dev);
>>>> +     if (ret) {
>>>> +             unicam_err(unicam,
>>>> +                        "Unable to register v4l2 device.\n");
>>>> +             return ret;
>>>> +     }
>>>> +
>>>> +     /* Reserve space for the controls */
>>>> +     hdl = &unicam->ctrl_handler;
>>>> +     ret = v4l2_ctrl_handler_init(hdl, 16);
>>>> +     if (ret < 0)
>>>> +             goto probe_out_v4l2_unregister;
>>>> +     unicam->v4l2_dev.ctrl_handler = hdl;
>>>> +
>>>> +     /* set the driver data in platform device */
>>>> +     platform_set_drvdata(pdev, unicam);
>>>> +
>>>> +     ret = of_unicam_connect_subdevs(unicam);
>>>> +     if (ret) {
>>>> +             dev_err(&pdev->dev, "Failed to connect subdevs\n");
>>>> +             goto free_hdl;
>>>> +     }
>>>> +
>>>> +     /* Enabling module functional clock */
>>>> +     pm_runtime_enable(&pdev->dev);
>>>
>>> I think pm_runtime is only controlling the power domain for the PHY, not
>>> the clock (which you're handling manually).
>>
>> You're right. Copy and paste from the driver I'd based this on.
>> Will correct in v3.
>>
>>   Dave
>>
>
> Dave, I plan to review this Friday or Monday. It would help me if you could
> post a v3 before Friday so that I'm reviewing the latest code.

OK, will do.
I'll hold off until tomorrow morning in the hope of some response on
the DT side.

> It would be great if you can also post your tc358743 patches. I have an RPi
> with a tc358743 attached, so it would be very useful if I can review and test
> both this driver and the tc358743 changes.

I'll sort those today. They're all pretty trivial, and are mainly
allow more combinations of framerate and resolution to work. I've
still to do an exhaustive test though, and the Tosh chip can be a
little fickle at times.

> I also have a selfish motive: I want to do a CEC demo next week during the
> Kernel Recipes conference with my RPi/tc358743 and your driver. It's nice if
> I can use the latest version for that.

Feel free to be selfish :-)
I should say that my testing has been done against an in-house
designed board, but the B101 seems to be nigh on identical. The fact
that it worked with the V1 patch set should hopefully mean that there
aren't any issues.

Thanks
  Dave

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
  2017-09-13 15:07   ` [PATCH v2 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver Dave Stevenson
@ 2017-09-19 14:32         ` Rob Herring
  0 siblings, 0 replies; 20+ messages in thread
From: Rob Herring @ 2017-09-19 14:32 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: Mauro Carvalho Chehab, Hans Verkuil, Eric Anholt, Stefan Wahren,
	Sakari Ailus, linux-media-u79uwXL29TY76Z2rM5mHXA,
	linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Wed, Sep 13, 2017 at 04:07:47PM +0100, Dave Stevenson wrote:
> Document the DT bindings for the CSI2/CCP2 receiver peripheral
> (known as Unicam) on BCM283x SoCs.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
> ---
>  .../devicetree/bindings/media/bcm2835-unicam.txt   | 107 +++++++++++++++++++++
>  1 file changed, 107 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/bcm2835-unicam.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/bcm2835-unicam.txt b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
> new file mode 100644
> index 0000000..2ee5af7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
> @@ -0,0 +1,107 @@
> +Broadcom BCM283x Camera Interface (Unicam)
> +------------------------------------------
> +
> +The Unicam block on BCM283x SoCs is the receiver for either
> +CSI-2 or CCP2 data from image sensors or similar devices.
> +
> +There are two camera drivers in the kernel for BCM283x - this one
> +and bcm2835-camera (currently in staging).

Linux detail that is n/a for bindings.

> +
> +This driver is purely the kernel controlling the Unicam peripheral - there

Bindings describe h/w blocks, not drivers.

> +is no involvement with the VideoCore firmware. Unicam receives CSI-2
> +(or CCP2) data and writes it into SDRAM. There is no additional processing
> +performed.
> +It should be possible to connect it to any sensor with a
> +suitable output interface and V4L2 subdevice driver.
> +
> +bcm2835-camera uses the VideoCore firmware to control the sensor,
> +Unicam, ISP, and various tuner control loops. Fully processed frames are
> +delivered to the driver by the firmware. It only has sensor drivers
> +for Omnivision OV5647, and Sony IMX219 sensors, and is closed source.
> +
> +The two drivers are mutually exclusive for the same Unicam instance.
> +The firmware checks the device tree configuration during boot. If
> +it finds device tree nodes called csi0 or csi1 then it will block the
> +firmware from accessing the peripheral, and bcm2835-camera will
> +not be able to stream data.

All interesting, but irrelavent to the binding other than the part about 
node name. Reword to just state the requirements to get the firmware to 
ignore things.

> +It should be possible to use bcm2835-camera on one camera interface
> +and bcm2835-unicam on the other interface if there is a need to.

For upstream, I don't think we care to support that. We don't need 2 
bindings.

> +
> +Required properties:
> +===================
> +- compatible	: must be "brcm,bcm2835-unicam".
> +- reg		: physical base address and length of the register sets for the
> +		  device.
> +- interrupts	: should contain the IRQ line for this Unicam instance.
> +- clocks	: list of clock specifiers, corresponding to entries in
> +		  clock-names property.
> +- clock-names	: must contain an "lp_clock" entry, matching entries
> +		  in the clocks property.

_clock is redundant.

> +
> +Unicam supports a single port node. It should contain one 'port' child node
> +with child 'endpoint' node. Please refer to the bindings defined in
> +Documentation/devicetree/bindings/media/video-interfaces.txt.
> +
> +Within the endpoint node, the following properties are mandatory:
> +- remote-endpoint	: links to the source device endpoint.
> +- data-lanes		: An array denoting how many data lanes are physically
> +			  present for this CSI-2 receiver instance. This can
> +			  be limited by either the SoC itself, or by the
> +			  breakout on the platform.
> +			  Lane reordering is not supported, so lanes must be
> +			  in order, starting at 1.

Just refer to docs for standard properties. Just add any info on limits 
of values like number of cells.


> +
> +Lane reordering is not supported on the clock lane, so the optional property
> +"clock-lane" will implicitly be <0>.
> +Similarly lane inversion is not supported, therefore "lane-polarities" will
> +implicitly be <0 0 0 0 0>.
> +Neither of these values will be checked.
> +
> +Example:
> +	csi1: csi@7e801000 {

I thought the node had to be called csi0 or csi1. The label (csi1) will 
be gone from the compiled dtb.

> +		compatible = "brcm,bcm2835-unicam";
> +		reg = <0x7e801000 0x800>,
> +		      <0x7e802004 0x4>;
> +		interrupts = <2 7>;
> +		clocks = <&clocks BCM2835_CLOCK_CAM1>;
> +		clock-names = "lp_clock";
> +
> +		port {
> +			#address-cells = <1>;
> +			#size-cells = <0>;

Don't need these with a single endpoint.

> +
> +			csi1_ep: endpoint {
> +				remote-endpoint = <&tc358743_0>;
> +				data-lanes = <1 2>;
> +			};
> +		};
> +	};
> +
> +	i2c0: i2c@7e205000 {
> +
> +		tc358743: csi-hdmi-bridge@0f {
> +			compatible = "toshiba,tc358743";
> +			reg = <0x0f>;
> +			status = "okay";

Don't show status in examples.

> +
> +			clocks = <&tc358743_clk>;
> +			clock-names = "refclk";
> +
> +			tc358743_clk: bridge-clk {
> +				compatible = "fixed-clock";
> +				#clock-cells = <0>;
> +				clock-frequency = <27000000>;
> +			};
> +
> +			port {
> +				tc358743_0: endpoint {
> +					remote-endpoint = <&csi1_ep>;
> +					clock-lanes = <0>;
> +					data-lanes = <1 2>;
> +					clock-noncontinuous;
> +					link-frequencies =
> +						/bits/ 64 <297000000>;
> +				};
> +			};
> +		};
> +	};
> -- 
> 2.7.4
> 
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
@ 2017-09-19 14:32         ` Rob Herring
  0 siblings, 0 replies; 20+ messages in thread
From: Rob Herring @ 2017-09-19 14:32 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: Mauro Carvalho Chehab, Hans Verkuil, Eric Anholt, Stefan Wahren,
	Sakari Ailus, linux-media, linux-rpi-kernel, devicetree

On Wed, Sep 13, 2017 at 04:07:47PM +0100, Dave Stevenson wrote:
> Document the DT bindings for the CSI2/CCP2 receiver peripheral
> (known as Unicam) on BCM283x SoCs.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
> ---
>  .../devicetree/bindings/media/bcm2835-unicam.txt   | 107 +++++++++++++++++++++
>  1 file changed, 107 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/bcm2835-unicam.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/bcm2835-unicam.txt b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
> new file mode 100644
> index 0000000..2ee5af7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
> @@ -0,0 +1,107 @@
> +Broadcom BCM283x Camera Interface (Unicam)
> +------------------------------------------
> +
> +The Unicam block on BCM283x SoCs is the receiver for either
> +CSI-2 or CCP2 data from image sensors or similar devices.
> +
> +There are two camera drivers in the kernel for BCM283x - this one
> +and bcm2835-camera (currently in staging).

Linux detail that is n/a for bindings.

> +
> +This driver is purely the kernel controlling the Unicam peripheral - there

Bindings describe h/w blocks, not drivers.

> +is no involvement with the VideoCore firmware. Unicam receives CSI-2
> +(or CCP2) data and writes it into SDRAM. There is no additional processing
> +performed.
> +It should be possible to connect it to any sensor with a
> +suitable output interface and V4L2 subdevice driver.
> +
> +bcm2835-camera uses the VideoCore firmware to control the sensor,
> +Unicam, ISP, and various tuner control loops. Fully processed frames are
> +delivered to the driver by the firmware. It only has sensor drivers
> +for Omnivision OV5647, and Sony IMX219 sensors, and is closed source.
> +
> +The two drivers are mutually exclusive for the same Unicam instance.
> +The firmware checks the device tree configuration during boot. If
> +it finds device tree nodes called csi0 or csi1 then it will block the
> +firmware from accessing the peripheral, and bcm2835-camera will
> +not be able to stream data.

All interesting, but irrelavent to the binding other than the part about 
node name. Reword to just state the requirements to get the firmware to 
ignore things.

> +It should be possible to use bcm2835-camera on one camera interface
> +and bcm2835-unicam on the other interface if there is a need to.

For upstream, I don't think we care to support that. We don't need 2 
bindings.

> +
> +Required properties:
> +===================
> +- compatible	: must be "brcm,bcm2835-unicam".
> +- reg		: physical base address and length of the register sets for the
> +		  device.
> +- interrupts	: should contain the IRQ line for this Unicam instance.
> +- clocks	: list of clock specifiers, corresponding to entries in
> +		  clock-names property.
> +- clock-names	: must contain an "lp_clock" entry, matching entries
> +		  in the clocks property.

_clock is redundant.

> +
> +Unicam supports a single port node. It should contain one 'port' child node
> +with child 'endpoint' node. Please refer to the bindings defined in
> +Documentation/devicetree/bindings/media/video-interfaces.txt.
> +
> +Within the endpoint node, the following properties are mandatory:
> +- remote-endpoint	: links to the source device endpoint.
> +- data-lanes		: An array denoting how many data lanes are physically
> +			  present for this CSI-2 receiver instance. This can
> +			  be limited by either the SoC itself, or by the
> +			  breakout on the platform.
> +			  Lane reordering is not supported, so lanes must be
> +			  in order, starting at 1.

Just refer to docs for standard properties. Just add any info on limits 
of values like number of cells.


> +
> +Lane reordering is not supported on the clock lane, so the optional property
> +"clock-lane" will implicitly be <0>.
> +Similarly lane inversion is not supported, therefore "lane-polarities" will
> +implicitly be <0 0 0 0 0>.
> +Neither of these values will be checked.
> +
> +Example:
> +	csi1: csi@7e801000 {

I thought the node had to be called csi0 or csi1. The label (csi1) will 
be gone from the compiled dtb.

> +		compatible = "brcm,bcm2835-unicam";
> +		reg = <0x7e801000 0x800>,
> +		      <0x7e802004 0x4>;
> +		interrupts = <2 7>;
> +		clocks = <&clocks BCM2835_CLOCK_CAM1>;
> +		clock-names = "lp_clock";
> +
> +		port {
> +			#address-cells = <1>;
> +			#size-cells = <0>;

Don't need these with a single endpoint.

> +
> +			csi1_ep: endpoint {
> +				remote-endpoint = <&tc358743_0>;
> +				data-lanes = <1 2>;
> +			};
> +		};
> +	};
> +
> +	i2c0: i2c@7e205000 {
> +
> +		tc358743: csi-hdmi-bridge@0f {
> +			compatible = "toshiba,tc358743";
> +			reg = <0x0f>;
> +			status = "okay";

Don't show status in examples.

> +
> +			clocks = <&tc358743_clk>;
> +			clock-names = "refclk";
> +
> +			tc358743_clk: bridge-clk {
> +				compatible = "fixed-clock";
> +				#clock-cells = <0>;
> +				clock-frequency = <27000000>;
> +			};
> +
> +			port {
> +				tc358743_0: endpoint {
> +					remote-endpoint = <&csi1_ep>;
> +					clock-lanes = <0>;
> +					data-lanes = <1 2>;
> +					clock-noncontinuous;
> +					link-frequencies =
> +						/bits/ 64 <297000000>;
> +				};
> +			};
> +		};
> +	};
> -- 
> 2.7.4
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
  2017-09-19 14:32         ` Rob Herring
@ 2017-09-19 15:12           ` Dave Stevenson
  -1 siblings, 0 replies; 20+ messages in thread
From: Dave Stevenson @ 2017-09-19 15:12 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mauro Carvalho Chehab, Hans Verkuil, Eric Anholt, Stefan Wahren,
	Sakari Ailus, linux-media-u79uwXL29TY76Z2rM5mHXA,
	linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Rob.

Thanks for the review.

On 19 September 2017 at 15:32, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On Wed, Sep 13, 2017 at 04:07:47PM +0100, Dave Stevenson wrote:
>> Document the DT bindings for the CSI2/CCP2 receiver peripheral
>> (known as Unicam) on BCM283x SoCs.
>>
>> Signed-off-by: Dave Stevenson <dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
>> ---
>>  .../devicetree/bindings/media/bcm2835-unicam.txt   | 107 +++++++++++++++++++++
>>  1 file changed, 107 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>>
>> diff --git a/Documentation/devicetree/bindings/media/bcm2835-unicam.txt b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>> new file mode 100644
>> index 0000000..2ee5af7
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>> @@ -0,0 +1,107 @@
>> +Broadcom BCM283x Camera Interface (Unicam)
>> +------------------------------------------
>> +
>> +The Unicam block on BCM283x SoCs is the receiver for either
>> +CSI-2 or CCP2 data from image sensors or similar devices.
>> +
>> +There are two camera drivers in the kernel for BCM283x - this one
>> +and bcm2835-camera (currently in staging).
>
> Linux detail that is n/a for bindings.
>
>> +
>> +This driver is purely the kernel controlling the Unicam peripheral - there
>
> Bindings describe h/w blocks, not drivers.
>
>> +is no involvement with the VideoCore firmware. Unicam receives CSI-2
>> +(or CCP2) data and writes it into SDRAM. There is no additional processing
>> +performed.
>> +It should be possible to connect it to any sensor with a
>> +suitable output interface and V4L2 subdevice driver.
>> +
>> +bcm2835-camera uses the VideoCore firmware to control the sensor,
>> +Unicam, ISP, and various tuner control loops. Fully processed frames are
>> +delivered to the driver by the firmware. It only has sensor drivers
>> +for Omnivision OV5647, and Sony IMX219 sensors, and is closed source.
>> +
>> +The two drivers are mutually exclusive for the same Unicam instance.
>> +The firmware checks the device tree configuration during boot. If
>> +it finds device tree nodes called csi0 or csi1 then it will block the
>> +firmware from accessing the peripheral, and bcm2835-camera will
>> +not be able to stream data.
>
> All interesting, but irrelavent to the binding other than the part about
> node name. Reword to just state the requirements to get the firmware to
> ignore things.

Hans had suggested it was potentially appropriate for DT bindings too
on last review [1] (which I apologise for missing off devicetree folk
from), but I'll remove all mention of Linux/driver detail for v3.
That will leave only a reworded version of the bit about making the
firmware ignore the blocks.

[1] http://www.spinics.net/lists/linux-media/msg117047.html

>> +It should be possible to use bcm2835-camera on one camera interface
>> +and bcm2835-unicam on the other interface if there is a need to.
>
> For upstream, I don't think we care to support that. We don't need 2
> bindings.

I'll remove as irrelevant, but in that case wouldn't you have 2
bindings - one to bcm2835-camera which references the vchi node to the
GPU, and one to bcm2835-unicam that gives the full block config as
being discussed in this doc.
(Currently bcm2835-camera is a platform device so has no binding doc).
This probably isn't the place for such a discussion.

>> +
>> +Required properties:
>> +===================
>> +- compatible : must be "brcm,bcm2835-unicam".
>> +- reg                : physical base address and length of the register sets for the
>> +               device.
>> +- interrupts : should contain the IRQ line for this Unicam instance.
>> +- clocks     : list of clock specifiers, corresponding to entries in
>> +               clock-names property.
>> +- clock-names        : must contain an "lp_clock" entry, matching entries
>> +               in the clocks property.
>
> _clock is redundant.

So just "lp" as the clock name? Will assume so.

>> +
>> +Unicam supports a single port node. It should contain one 'port' child node
>> +with child 'endpoint' node. Please refer to the bindings defined in
>> +Documentation/devicetree/bindings/media/video-interfaces.txt.
>> +
>> +Within the endpoint node, the following properties are mandatory:
>> +- remote-endpoint    : links to the source device endpoint.
>> +- data-lanes         : An array denoting how many data lanes are physically
>> +                       present for this CSI-2 receiver instance. This can
>> +                       be limited by either the SoC itself, or by the
>> +                       breakout on the platform.
>> +                       Lane reordering is not supported, so lanes must be
>> +                       in order, starting at 1.
>
> Just refer to docs for standard properties. Just add any info on limits
> of values like number of cells.

So again from v1 Sakari had asked for a statement of mandatory
properties for the endpoint [1].
Would you be happy with something like:
"Within the endpoint node the "remote-endpoint" and "data-lanes"
properties are mandatory. Lane reordering is not supported so the
lanes must be in order, starting at 1. The number of data lanes should
represent the number of lanes usable on the platform, which may be
limited by the SoC or the platform breakout."

[1] http://www.spinics.net/lists/linux-media/msg117080.html

>> +
>> +Lane reordering is not supported on the clock lane, so the optional property
>> +"clock-lane" will implicitly be <0>.
>> +Similarly lane inversion is not supported, therefore "lane-polarities" will
>> +implicitly be <0 0 0 0 0>.
>> +Neither of these values will be checked.
>> +
>> +Example:
>> +     csi1: csi@7e801000 {
>
> I thought the node had to be called csi0 or csi1. The label (csi1) will
> be gone from the compiled dtb.

Yes, typo :-(

>> +             compatible = "brcm,bcm2835-unicam";
>> +             reg = <0x7e801000 0x800>,
>> +                   <0x7e802004 0x4>;
>> +             interrupts = <2 7>;
>> +             clocks = <&clocks BCM2835_CLOCK_CAM1>;
>> +             clock-names = "lp_clock";
>> +
>> +             port {
>> +                     #address-cells = <1>;
>> +                     #size-cells = <0>;
>
> Don't need these with a single endpoint.

OK.

>> +
>> +                     csi1_ep: endpoint {
>> +                             remote-endpoint = <&tc358743_0>;
>> +                             data-lanes = <1 2>;
>> +                     };
>> +             };
>> +     };
>> +
>> +     i2c0: i2c@7e205000 {
>> +
>> +             tc358743: csi-hdmi-bridge@0f {
>> +                     compatible = "toshiba,tc358743";
>> +                     reg = <0x0f>;
>> +                     status = "okay";
>
> Don't show status in examples.

OK.

>> +
>> +                     clocks = <&tc358743_clk>;
>> +                     clock-names = "refclk";
>> +
>> +                     tc358743_clk: bridge-clk {
>> +                             compatible = "fixed-clock";
>> +                             #clock-cells = <0>;
>> +                             clock-frequency = <27000000>;
>> +                     };
>> +
>> +                     port {
>> +                             tc358743_0: endpoint {
>> +                                     remote-endpoint = <&csi1_ep>;
>> +                                     clock-lanes = <0>;
>> +                                     data-lanes = <1 2>;
>> +                                     clock-noncontinuous;
>> +                                     link-frequencies =
>> +                                             /bits/ 64 <297000000>;
>> +                             };
>> +                     };
>> +             };
>> +     };
>> --
>> 2.7.4
>>

Thanks.
  Dave
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
@ 2017-09-19 15:12           ` Dave Stevenson
  0 siblings, 0 replies; 20+ messages in thread
From: Dave Stevenson @ 2017-09-19 15:12 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mauro Carvalho Chehab, Hans Verkuil, Eric Anholt, Stefan Wahren,
	Sakari Ailus, linux-media, linux-rpi-kernel, devicetree

Hi Rob.

Thanks for the review.

On 19 September 2017 at 15:32, Rob Herring <robh@kernel.org> wrote:
> On Wed, Sep 13, 2017 at 04:07:47PM +0100, Dave Stevenson wrote:
>> Document the DT bindings for the CSI2/CCP2 receiver peripheral
>> (known as Unicam) on BCM283x SoCs.
>>
>> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
>> ---
>>  .../devicetree/bindings/media/bcm2835-unicam.txt   | 107 +++++++++++++++++++++
>>  1 file changed, 107 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>>
>> diff --git a/Documentation/devicetree/bindings/media/bcm2835-unicam.txt b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>> new file mode 100644
>> index 0000000..2ee5af7
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>> @@ -0,0 +1,107 @@
>> +Broadcom BCM283x Camera Interface (Unicam)
>> +------------------------------------------
>> +
>> +The Unicam block on BCM283x SoCs is the receiver for either
>> +CSI-2 or CCP2 data from image sensors or similar devices.
>> +
>> +There are two camera drivers in the kernel for BCM283x - this one
>> +and bcm2835-camera (currently in staging).
>
> Linux detail that is n/a for bindings.
>
>> +
>> +This driver is purely the kernel controlling the Unicam peripheral - there
>
> Bindings describe h/w blocks, not drivers.
>
>> +is no involvement with the VideoCore firmware. Unicam receives CSI-2
>> +(or CCP2) data and writes it into SDRAM. There is no additional processing
>> +performed.
>> +It should be possible to connect it to any sensor with a
>> +suitable output interface and V4L2 subdevice driver.
>> +
>> +bcm2835-camera uses the VideoCore firmware to control the sensor,
>> +Unicam, ISP, and various tuner control loops. Fully processed frames are
>> +delivered to the driver by the firmware. It only has sensor drivers
>> +for Omnivision OV5647, and Sony IMX219 sensors, and is closed source.
>> +
>> +The two drivers are mutually exclusive for the same Unicam instance.
>> +The firmware checks the device tree configuration during boot. If
>> +it finds device tree nodes called csi0 or csi1 then it will block the
>> +firmware from accessing the peripheral, and bcm2835-camera will
>> +not be able to stream data.
>
> All interesting, but irrelavent to the binding other than the part about
> node name. Reword to just state the requirements to get the firmware to
> ignore things.

Hans had suggested it was potentially appropriate for DT bindings too
on last review [1] (which I apologise for missing off devicetree folk
from), but I'll remove all mention of Linux/driver detail for v3.
That will leave only a reworded version of the bit about making the
firmware ignore the blocks.

[1] http://www.spinics.net/lists/linux-media/msg117047.html

>> +It should be possible to use bcm2835-camera on one camera interface
>> +and bcm2835-unicam on the other interface if there is a need to.
>
> For upstream, I don't think we care to support that. We don't need 2
> bindings.

I'll remove as irrelevant, but in that case wouldn't you have 2
bindings - one to bcm2835-camera which references the vchi node to the
GPU, and one to bcm2835-unicam that gives the full block config as
being discussed in this doc.
(Currently bcm2835-camera is a platform device so has no binding doc).
This probably isn't the place for such a discussion.

>> +
>> +Required properties:
>> +===================
>> +- compatible : must be "brcm,bcm2835-unicam".
>> +- reg                : physical base address and length of the register sets for the
>> +               device.
>> +- interrupts : should contain the IRQ line for this Unicam instance.
>> +- clocks     : list of clock specifiers, corresponding to entries in
>> +               clock-names property.
>> +- clock-names        : must contain an "lp_clock" entry, matching entries
>> +               in the clocks property.
>
> _clock is redundant.

So just "lp" as the clock name? Will assume so.

>> +
>> +Unicam supports a single port node. It should contain one 'port' child node
>> +with child 'endpoint' node. Please refer to the bindings defined in
>> +Documentation/devicetree/bindings/media/video-interfaces.txt.
>> +
>> +Within the endpoint node, the following properties are mandatory:
>> +- remote-endpoint    : links to the source device endpoint.
>> +- data-lanes         : An array denoting how many data lanes are physically
>> +                       present for this CSI-2 receiver instance. This can
>> +                       be limited by either the SoC itself, or by the
>> +                       breakout on the platform.
>> +                       Lane reordering is not supported, so lanes must be
>> +                       in order, starting at 1.
>
> Just refer to docs for standard properties. Just add any info on limits
> of values like number of cells.

So again from v1 Sakari had asked for a statement of mandatory
properties for the endpoint [1].
Would you be happy with something like:
"Within the endpoint node the "remote-endpoint" and "data-lanes"
properties are mandatory. Lane reordering is not supported so the
lanes must be in order, starting at 1. The number of data lanes should
represent the number of lanes usable on the platform, which may be
limited by the SoC or the platform breakout."

[1] http://www.spinics.net/lists/linux-media/msg117080.html

>> +
>> +Lane reordering is not supported on the clock lane, so the optional property
>> +"clock-lane" will implicitly be <0>.
>> +Similarly lane inversion is not supported, therefore "lane-polarities" will
>> +implicitly be <0 0 0 0 0>.
>> +Neither of these values will be checked.
>> +
>> +Example:
>> +     csi1: csi@7e801000 {
>
> I thought the node had to be called csi0 or csi1. The label (csi1) will
> be gone from the compiled dtb.

Yes, typo :-(

>> +             compatible = "brcm,bcm2835-unicam";
>> +             reg = <0x7e801000 0x800>,
>> +                   <0x7e802004 0x4>;
>> +             interrupts = <2 7>;
>> +             clocks = <&clocks BCM2835_CLOCK_CAM1>;
>> +             clock-names = "lp_clock";
>> +
>> +             port {
>> +                     #address-cells = <1>;
>> +                     #size-cells = <0>;
>
> Don't need these with a single endpoint.

OK.

>> +
>> +                     csi1_ep: endpoint {
>> +                             remote-endpoint = <&tc358743_0>;
>> +                             data-lanes = <1 2>;
>> +                     };
>> +             };
>> +     };
>> +
>> +     i2c0: i2c@7e205000 {
>> +
>> +             tc358743: csi-hdmi-bridge@0f {
>> +                     compatible = "toshiba,tc358743";
>> +                     reg = <0x0f>;
>> +                     status = "okay";
>
> Don't show status in examples.

OK.

>> +
>> +                     clocks = <&tc358743_clk>;
>> +                     clock-names = "refclk";
>> +
>> +                     tc358743_clk: bridge-clk {
>> +                             compatible = "fixed-clock";
>> +                             #clock-cells = <0>;
>> +                             clock-frequency = <27000000>;
>> +                     };
>> +
>> +                     port {
>> +                             tc358743_0: endpoint {
>> +                                     remote-endpoint = <&csi1_ep>;
>> +                                     clock-lanes = <0>;
>> +                                     data-lanes = <1 2>;
>> +                                     clock-noncontinuous;
>> +                                     link-frequencies =
>> +                                             /bits/ 64 <297000000>;
>> +                             };
>> +                     };
>> +             };
>> +     };
>> --
>> 2.7.4
>>

Thanks.
  Dave

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 0/4] BCM283x Camera Receiver driver
  2017-09-13 15:49 ` [PATCH v2 0/4] BCM283x Camera Receiver driver Dave Stevenson
@ 2017-09-22 11:00   ` Hans Verkuil
  2017-09-22 16:31     ` Dave Stevenson
  0 siblings, 1 reply; 20+ messages in thread
From: Hans Verkuil @ 2017-09-22 11:00 UTC (permalink / raw)
  To: Dave Stevenson, Mauro Carvalho Chehab, Hans Verkuil, linux-media,
	linux-rpi-kernel

On 13/09/17 17:49, Dave Stevenson wrote:
> OV5647
> 
> v4l2-compliance SHA   : f6ecbc90656815d91dc6ba90aac0ad8193a14b38
> 
> Driver Info:
>     Driver name   : unicam
>     Card type     : unicam
>     Bus info      : platform:unicam 3f801000.csi1
>     Driver version: 4.13.0
>     Capabilities  : 0x85200001
>         Video Capture
>         Read/Write
>         Streaming
>         Extended Pix Format
>         Device Capabilities
>     Device Caps   : 0x05200001
>         Video Capture
>         Read/Write
>         Streaming
>         Extended Pix Format
> 
> Compliance test for device /dev/video0 (not using libv4l2):
> 
> Required ioctls:
>     test VIDIOC_QUERYCAP: OK
> 
> Allow for multiple opens:
>     test second video open: OK
>     test VIDIOC_QUERYCAP: OK
>     test VIDIOC_G/S_PRIORITY: OK
>     test for unlimited opens: OK
> 
> Debug ioctls:
>     test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
>     test VIDIOC_LOG_STATUS: OK
> 
> Input ioctls:
>     test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
>     test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
>     test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
>     test VIDIOC_ENUMAUDIO: OK (Not Supported)
>     test VIDIOC_G/S/ENUMINPUT: OK
>     test VIDIOC_G/S_AUDIO: OK (Not Supported)
>     Inputs: 1 Audio Inputs: 0 Tuners: 0
> 
> Output ioctls:
>     test VIDIOC_G/S_MODULATOR: OK (Not Supported)
>     test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
>     test VIDIOC_ENUMAUDOUT: OK (Not Supported)
>     test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
>     test VIDIOC_G/S_AUDOUT: OK (Not Supported)
>     Outputs: 0 Audio Outputs: 0 Modulators: 0
> 
> Input/Output configuration ioctls:
>     test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
>     test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
>     test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
>     test VIDIOC_G/S_EDID: OK (Not Supported)
> 
> Test input 0:
> 
>     Control ioctls:
>         test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
>         test VIDIOC_QUERYCTRL: OK
>         test VIDIOC_G/S_CTRL: OK
>         fail: v4l2-test-controls.cpp(587): g_ext_ctrls does not
> support count == 0

Huh. The issue here is that there are no controls at all, but the
control API is present. The class_check() function in v4l2-ctrls.c expects
that there are controls and if not it returns -EINVAL, causing this test
to fail.

Try to apply this patch:

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index dd1db678718c..4e53a8654690 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -2818,7 +2818,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
 static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
 {
 	if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL)
-		return list_empty(&hdl->ctrl_refs) ? -EINVAL : 0;
+		return 0;
 	return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL;
 }


and see if it will pass the compliance test. There may be other issues.
I think that the compliance test should handle the case where there are no
controls, so this is a good test.

That said, another solution is that the driver detects that there are no
controls in unicam_probe_complete() and sets unicam->v4l2_dev.ctrl_handler
to NULL.

I think you should do this in v4. Having control ioctls but no actual controls
is not wrong as such, but it is a bit misleading towards the application.

But let's make sure that v4l2-compliance can handle this case first.

Regards,

	Hans

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 0/4] BCM283x Camera Receiver driver
  2017-09-22 11:00   ` Hans Verkuil
@ 2017-09-22 16:31     ` Dave Stevenson
  2017-09-22 17:17       ` Hans Verkuil
  0 siblings, 1 reply; 20+ messages in thread
From: Dave Stevenson @ 2017-09-22 16:31 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Mauro Carvalho Chehab, Hans Verkuil, linux-media, linux-rpi-kernel

On 22 September 2017 at 12:00, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> On 13/09/17 17:49, Dave Stevenson wrote:
>> OV5647
>>
>> v4l2-compliance SHA   : f6ecbc90656815d91dc6ba90aac0ad8193a14b38
>>
>> Driver Info:
>>     Driver name   : unicam
>>     Card type     : unicam
>>     Bus info      : platform:unicam 3f801000.csi1
>>     Driver version: 4.13.0
>>     Capabilities  : 0x85200001
>>         Video Capture
>>         Read/Write
>>         Streaming
>>         Extended Pix Format
>>         Device Capabilities
>>     Device Caps   : 0x05200001
>>         Video Capture
>>         Read/Write
>>         Streaming
>>         Extended Pix Format
>>
>> Compliance test for device /dev/video0 (not using libv4l2):
>>
>> Required ioctls:
>>     test VIDIOC_QUERYCAP: OK
>>
>> Allow for multiple opens:
>>     test second video open: OK
>>     test VIDIOC_QUERYCAP: OK
>>     test VIDIOC_G/S_PRIORITY: OK
>>     test for unlimited opens: OK
>>
>> Debug ioctls:
>>     test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
>>     test VIDIOC_LOG_STATUS: OK
>>
>> Input ioctls:
>>     test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
>>     test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
>>     test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
>>     test VIDIOC_ENUMAUDIO: OK (Not Supported)
>>     test VIDIOC_G/S/ENUMINPUT: OK
>>     test VIDIOC_G/S_AUDIO: OK (Not Supported)
>>     Inputs: 1 Audio Inputs: 0 Tuners: 0
>>
>> Output ioctls:
>>     test VIDIOC_G/S_MODULATOR: OK (Not Supported)
>>     test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
>>     test VIDIOC_ENUMAUDOUT: OK (Not Supported)
>>     test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
>>     test VIDIOC_G/S_AUDOUT: OK (Not Supported)
>>     Outputs: 0 Audio Outputs: 0 Modulators: 0
>>
>> Input/Output configuration ioctls:
>>     test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
>>     test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
>>     test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
>>     test VIDIOC_G/S_EDID: OK (Not Supported)
>>
>> Test input 0:
>>
>>     Control ioctls:
>>         test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
>>         test VIDIOC_QUERYCTRL: OK
>>         test VIDIOC_G/S_CTRL: OK
>>         fail: v4l2-test-controls.cpp(587): g_ext_ctrls does not
>> support count == 0
>
> Huh. The issue here is that there are no controls at all, but the
> control API is present. The class_check() function in v4l2-ctrls.c expects
> that there are controls and if not it returns -EINVAL, causing this test
> to fail.
>
> Try to apply this patch:
>
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
> index dd1db678718c..4e53a8654690 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
> @@ -2818,7 +2818,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>  static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
>  {
>         if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL)
> -               return list_empty(&hdl->ctrl_refs) ? -EINVAL : 0;
> +               return 0;
>         return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL;
>  }
>
>
> and see if it will pass the compliance test. There may be other issues.
> I think that the compliance test should handle the case where there are no
> controls, so this is a good test.

Fails.
        fail: v4l2-test-controls.cpp(589): g_ext_ctrls worked even
when no controls are present
        test VIDIOC_G/S/TRY_EXT_CTRLS: FAIL

> That said, another solution is that the driver detects that there are no
> controls in unicam_probe_complete() and sets unicam->v4l2_dev.ctrl_handler
> to NULL.
>
> I think you should do this in v4. Having control ioctls but no actual controls
> is not wrong as such, but it is a bit misleading towards the application.

OK, will do.

  Dave

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 0/4] BCM283x Camera Receiver driver
  2017-09-22 16:31     ` Dave Stevenson
@ 2017-09-22 17:17       ` Hans Verkuil
  2017-09-25  9:02         ` Dave Stevenson
  0 siblings, 1 reply; 20+ messages in thread
From: Hans Verkuil @ 2017-09-22 17:17 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: Mauro Carvalho Chehab, Hans Verkuil, linux-media, linux-rpi-kernel

On 22/09/17 18:31, Dave Stevenson wrote:
> On 22 September 2017 at 12:00, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>> On 13/09/17 17:49, Dave Stevenson wrote:
>>> OV5647
>>>
>>> v4l2-compliance SHA   : f6ecbc90656815d91dc6ba90aac0ad8193a14b38
>>>
>>> Driver Info:
>>>     Driver name   : unicam
>>>     Card type     : unicam
>>>     Bus info      : platform:unicam 3f801000.csi1
>>>     Driver version: 4.13.0
>>>     Capabilities  : 0x85200001
>>>         Video Capture
>>>         Read/Write
>>>         Streaming
>>>         Extended Pix Format
>>>         Device Capabilities
>>>     Device Caps   : 0x05200001
>>>         Video Capture
>>>         Read/Write
>>>         Streaming
>>>         Extended Pix Format
>>>
>>> Compliance test for device /dev/video0 (not using libv4l2):
>>>
>>> Required ioctls:
>>>     test VIDIOC_QUERYCAP: OK
>>>
>>> Allow for multiple opens:
>>>     test second video open: OK
>>>     test VIDIOC_QUERYCAP: OK
>>>     test VIDIOC_G/S_PRIORITY: OK
>>>     test for unlimited opens: OK
>>>
>>> Debug ioctls:
>>>     test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
>>>     test VIDIOC_LOG_STATUS: OK
>>>
>>> Input ioctls:
>>>     test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
>>>     test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
>>>     test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
>>>     test VIDIOC_ENUMAUDIO: OK (Not Supported)
>>>     test VIDIOC_G/S/ENUMINPUT: OK
>>>     test VIDIOC_G/S_AUDIO: OK (Not Supported)
>>>     Inputs: 1 Audio Inputs: 0 Tuners: 0
>>>
>>> Output ioctls:
>>>     test VIDIOC_G/S_MODULATOR: OK (Not Supported)
>>>     test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
>>>     test VIDIOC_ENUMAUDOUT: OK (Not Supported)
>>>     test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
>>>     test VIDIOC_G/S_AUDOUT: OK (Not Supported)
>>>     Outputs: 0 Audio Outputs: 0 Modulators: 0
>>>
>>> Input/Output configuration ioctls:
>>>     test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
>>>     test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
>>>     test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
>>>     test VIDIOC_G/S_EDID: OK (Not Supported)
>>>
>>> Test input 0:
>>>
>>>     Control ioctls:
>>>         test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
>>>         test VIDIOC_QUERYCTRL: OK
>>>         test VIDIOC_G/S_CTRL: OK
>>>         fail: v4l2-test-controls.cpp(587): g_ext_ctrls does not
>>> support count == 0
>>
>> Huh. The issue here is that there are no controls at all, but the
>> control API is present. The class_check() function in v4l2-ctrls.c expects
>> that there are controls and if not it returns -EINVAL, causing this test
>> to fail.
>>
>> Try to apply this patch:
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
>> index dd1db678718c..4e53a8654690 100644
>> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
>> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
>> @@ -2818,7 +2818,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>>  static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
>>  {
>>         if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL)
>> -               return list_empty(&hdl->ctrl_refs) ? -EINVAL : 0;
>> +               return 0;
>>         return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL;
>>  }
>>
>>
>> and see if it will pass the compliance test. There may be other issues.
>> I think that the compliance test should handle the case where there are no
>> controls, so this is a good test.
> 
> Fails.
>         fail: v4l2-test-controls.cpp(589): g_ext_ctrls worked even
> when no controls are present
>         test VIDIOC_G/S/TRY_EXT_CTRLS: FAIL

Try this patch:

-----------------
diff --git a/utils/v4l2-compliance/v4l2-test-controls.cpp b/utils/v4l2-compliance/v4l2-test-controls.cpp
index 7514459f..508daf05 100644
--- a/utils/v4l2-compliance/v4l2-test-controls.cpp
+++ b/utils/v4l2-compliance/v4l2-test-controls.cpp
@@ -585,8 +585,6 @@ int testExtendedControls(struct node *node)
 		return ret;
 	if (ret)
 		return fail("g_ext_ctrls does not support count == 0\n");
-	if (node->controls.empty())
-		return fail("g_ext_ctrls worked even when no controls are present\n");
 	if (ctrls.which)
 		return fail("field which changed\n");
 	if (ctrls.count)
@@ -600,8 +598,6 @@ int testExtendedControls(struct node *node)
 		return ret;
 	if (ret)
 		return fail("try_ext_ctrls does not support count == 0\n");
-	if (node->controls.empty())
-		return fail("try_ext_ctrls worked even when no controls are present\n");
 	if (ctrls.which)
 		return fail("field which changed\n");
 	if (ctrls.count)
@@ -687,6 +683,8 @@ int testExtendedControls(struct node *node)
 	}

 	ctrls.which = 0;
+	ctrls.count = 1;
+	ctrls.controls = &ctrl;
 	ctrl.id = 0;
 	ctrl.size = 0;
 	ret = doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls);
@@ -745,6 +743,9 @@ int testExtendedControls(struct node *node)
 	if (ret)
 		return fail("could not set all controls\n");

+	if (!which)
+		return 0;
+
 	ctrls.which = which;
 	ret = doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls);
 	if (ret && !multiple_classes)
----------------

Hans

> 
>> That said, another solution is that the driver detects that there are no
>> controls in unicam_probe_complete() and sets unicam->v4l2_dev.ctrl_handler
>> to NULL.
>>
>> I think you should do this in v4. Having control ioctls but no actual controls
>> is not wrong as such, but it is a bit misleading towards the application.
> 
> OK, will do.
> 
>   Dave
> 

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 0/4] BCM283x Camera Receiver driver
  2017-09-22 17:17       ` Hans Verkuil
@ 2017-09-25  9:02         ` Dave Stevenson
  0 siblings, 0 replies; 20+ messages in thread
From: Dave Stevenson @ 2017-09-25  9:02 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Mauro Carvalho Chehab, Hans Verkuil, linux-media, linux-rpi-kernel

On 22 September 2017 at 18:17, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> On 22/09/17 18:31, Dave Stevenson wrote:
>> On 22 September 2017 at 12:00, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>> On 13/09/17 17:49, Dave Stevenson wrote:
>>>> OV5647
>>>>
>>>> v4l2-compliance SHA   : f6ecbc90656815d91dc6ba90aac0ad8193a14b38
>>>>
>>>> Driver Info:
>>>>     Driver name   : unicam
>>>>     Card type     : unicam
>>>>     Bus info      : platform:unicam 3f801000.csi1
>>>>     Driver version: 4.13.0
>>>>     Capabilities  : 0x85200001
>>>>         Video Capture
>>>>         Read/Write
>>>>         Streaming
>>>>         Extended Pix Format
>>>>         Device Capabilities
>>>>     Device Caps   : 0x05200001
>>>>         Video Capture
>>>>         Read/Write
>>>>         Streaming
>>>>         Extended Pix Format
>>>>
>>>> Compliance test for device /dev/video0 (not using libv4l2):
>>>>
>>>> Required ioctls:
>>>>     test VIDIOC_QUERYCAP: OK
>>>>
>>>> Allow for multiple opens:
>>>>     test second video open: OK
>>>>     test VIDIOC_QUERYCAP: OK
>>>>     test VIDIOC_G/S_PRIORITY: OK
>>>>     test for unlimited opens: OK
>>>>
>>>> Debug ioctls:
>>>>     test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
>>>>     test VIDIOC_LOG_STATUS: OK
>>>>
>>>> Input ioctls:
>>>>     test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
>>>>     test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
>>>>     test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
>>>>     test VIDIOC_ENUMAUDIO: OK (Not Supported)
>>>>     test VIDIOC_G/S/ENUMINPUT: OK
>>>>     test VIDIOC_G/S_AUDIO: OK (Not Supported)
>>>>     Inputs: 1 Audio Inputs: 0 Tuners: 0
>>>>
>>>> Output ioctls:
>>>>     test VIDIOC_G/S_MODULATOR: OK (Not Supported)
>>>>     test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
>>>>     test VIDIOC_ENUMAUDOUT: OK (Not Supported)
>>>>     test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
>>>>     test VIDIOC_G/S_AUDOUT: OK (Not Supported)
>>>>     Outputs: 0 Audio Outputs: 0 Modulators: 0
>>>>
>>>> Input/Output configuration ioctls:
>>>>     test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
>>>>     test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
>>>>     test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
>>>>     test VIDIOC_G/S_EDID: OK (Not Supported)
>>>>
>>>> Test input 0:
>>>>
>>>>     Control ioctls:
>>>>         test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
>>>>         test VIDIOC_QUERYCTRL: OK
>>>>         test VIDIOC_G/S_CTRL: OK
>>>>         fail: v4l2-test-controls.cpp(587): g_ext_ctrls does not
>>>> support count == 0
>>>
>>> Huh. The issue here is that there are no controls at all, but the
>>> control API is present. The class_check() function in v4l2-ctrls.c expects
>>> that there are controls and if not it returns -EINVAL, causing this test
>>> to fail.
>>>
>>> Try to apply this patch:
>>>
>>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>>> ---
>>> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
>>> index dd1db678718c..4e53a8654690 100644
>>> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
>>> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
>>> @@ -2818,7 +2818,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>>>  static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
>>>  {
>>>         if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL)
>>> -               return list_empty(&hdl->ctrl_refs) ? -EINVAL : 0;
>>> +               return 0;
>>>         return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL;
>>>  }
>>>
>>>
>>> and see if it will pass the compliance test. There may be other issues.
>>> I think that the compliance test should handle the case where there are no
>>> controls, so this is a good test.
>>
>> Fails.
>>         fail: v4l2-test-controls.cpp(589): g_ext_ctrls worked even
>> when no controls are present
>>         test VIDIOC_G/S/TRY_EXT_CTRLS: FAIL
>
> Try this patch:
>
> -----------------
> diff --git a/utils/v4l2-compliance/v4l2-test-controls.cpp b/utils/v4l2-compliance/v4l2-test-controls.cpp
> index 7514459f..508daf05 100644
> --- a/utils/v4l2-compliance/v4l2-test-controls.cpp
> +++ b/utils/v4l2-compliance/v4l2-test-controls.cpp
> @@ -585,8 +585,6 @@ int testExtendedControls(struct node *node)
>                 return ret;
>         if (ret)
>                 return fail("g_ext_ctrls does not support count == 0\n");
> -       if (node->controls.empty())
> -               return fail("g_ext_ctrls worked even when no controls are present\n");
>         if (ctrls.which)
>                 return fail("field which changed\n");
>         if (ctrls.count)
> @@ -600,8 +598,6 @@ int testExtendedControls(struct node *node)
>                 return ret;
>         if (ret)
>                 return fail("try_ext_ctrls does not support count == 0\n");
> -       if (node->controls.empty())
> -               return fail("try_ext_ctrls worked even when no controls are present\n");
>         if (ctrls.which)
>                 return fail("field which changed\n");
>         if (ctrls.count)
> @@ -687,6 +683,8 @@ int testExtendedControls(struct node *node)
>         }
>
>         ctrls.which = 0;
> +       ctrls.count = 1;
> +       ctrls.controls = &ctrl;
>         ctrl.id = 0;
>         ctrl.size = 0;
>         ret = doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls);
> @@ -745,6 +743,9 @@ int testExtendedControls(struct node *node)
>         if (ret)
>                 return fail("could not set all controls\n");
>
> +       if (!which)
> +               return 0;
> +
>         ctrls.which = which;
>         ret = doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls);
>         if (ret && !multiple_classes)

Thanks Hans. That passes.
I'll leave you to work out the correct thing to apply for compliance,
and will fix the unicam driver in v4 to disable the control handler if
the subdevice registers no controls.

  Dave

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2017-09-25  9:02 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-09-13 15:07 [PATCH v2 0/4] BCM283x Camera Receiver driver Dave Stevenson
     [not found] ` <cover.1505314390.git.dave.stevenson@raspberrypi.org>
2017-09-13 15:07   ` [PATCH v2 1/4] [media] v4l2-common: Add helper function for fourcc to string Dave Stevenson
2017-09-13 15:07   ` [PATCH v2 2/4] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver Dave Stevenson
     [not found]     ` <9ad8b23d5c394b64ed02f9a5ebc49209696a5ace.1505314390.git.dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
2017-09-19 14:32       ` Rob Herring
2017-09-19 14:32         ` Rob Herring
2017-09-19 15:12         ` Dave Stevenson
2017-09-19 15:12           ` Dave Stevenson
2017-09-13 15:07   ` [PATCH v2 3/4] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface Dave Stevenson
     [not found]     ` <0d4dc119a2ba4917e0fecfe5084425830ec53ccb.1505314390.git.dave.stevenson-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
2017-09-18 18:18       ` Eric Anholt
2017-09-18 18:18         ` Eric Anholt
     [not found]         ` <87k20vswdv.fsf-WhKQ6XTQaPysTnJN9+BGXg@public.gmane.org>
2017-09-19  9:50           ` Dave Stevenson
2017-09-19  9:50             ` Dave Stevenson
2017-09-19 10:20             ` Hans Verkuil
2017-09-19 11:42               ` Dave Stevenson
2017-09-13 15:07   ` [PATCH v2 4/4] MAINTAINERS: Add entry for BCM2835 camera driver Dave Stevenson
2017-09-13 15:49 ` [PATCH v2 0/4] BCM283x Camera Receiver driver Dave Stevenson
2017-09-22 11:00   ` Hans Verkuil
2017-09-22 16:31     ` Dave Stevenson
2017-09-22 17:17       ` Hans Verkuil
2017-09-25  9:02         ` Dave Stevenson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.