All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 0/2] BCM283x Camera Receiver driver
@ 2017-06-14 15:15 Dave Stevenson
  2017-06-14 15:15 ` [RFC 1/2] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver Dave Stevenson
                   ` (3 more replies)
  0 siblings, 4 replies; 35+ messages in thread
From: Dave Stevenson @ 2017-06-14 15:15 UTC (permalink / raw)
  To: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel; +Cc: Dave Stevenson

Hi All.

This is adding a V4L2 subdevice driver for the CSI2/CCP2 camera
receiver peripheral on BCM283x, as used on Raspberry Pi.

v4l2-compliance results depend on the sensor subdevice this is
connected to. It passes the basic tests cleanly with TC358743,
but objects with OV5647
fail: v4l2-test-controls.cpp(574): g_ext_ctrls does not support count == 0
Neither OV5647 nor Unicam support any controls.

I must admit to not having got OV5647 to stream with the current driver
register settings. It works with a set of register settings for VGA RAW10.
I also have a couple of patches pending for OV5647, but would like to
understand the issues better before sending them out.

Two queries I do have in V4L2-land:
- When s_dv_timings or s_std is called, is the format meant to
  be updated automatically? Even if we're already streaming?
  Some existing drivers seem to, but others don't.
- With s_fmt, is sizeimage settable by the application in the same
  way as bytesperline? yavta allows you to specify it on the command
  line, whilst v4l2-ctl doesn't. Some of the other parts of the Pi
  firmware have a requirement that the buffer is a multiple of 16 lines
  high, which can be matched by V4L2 if we can over-allocate the
  buffers by the app specifying sizeimage. But if I allow that,
  then I get a v4l2-compliance failure as the size doesn't get
  reset when switching from RGB3 to UYVY as it takes the request as
  a request to over-allocate.

Apologies if I've messed up in sending these patches - so many ways
to do something.

Thanks in advance.
  Dave

Dave Stevenson (2):
  [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
  [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface

 .../devicetree/bindings/media/bcm2835-unicam.txt   |   76 +
 drivers/media/platform/Kconfig                     |    1 +
 drivers/media/platform/Makefile                    |    2 +
 drivers/media/platform/bcm2835/Kconfig             |   14 +
 drivers/media/platform/bcm2835/Makefile            |    3 +
 drivers/media/platform/bcm2835/bcm2835-unicam.c    | 2100 ++++++++++++++++++++
 drivers/media/platform/bcm2835/vc4-regs-unicam.h   |  257 +++
 7 files changed, 2453 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] 35+ messages in thread

* [RFC 1/2] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
  2017-06-14 15:15 [RFC 0/2] BCM283x Camera Receiver driver Dave Stevenson
@ 2017-06-14 15:15 ` Dave Stevenson
  2017-06-15  6:34   ` Stefan Wahren
  2017-06-15 12:59   ` Sakari Ailus
  2017-06-14 15:15 ` [RFC 2/2] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface Dave Stevenson
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 35+ messages in thread
From: Dave Stevenson @ 2017-06-14 15:15 UTC (permalink / raw)
  To: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel; +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   | 76 ++++++++++++++++++++++
 1 file changed, 76 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..cc5a451
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
@@ -0,0 +1,76 @@
+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.
+
+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.
+
+Optional properties
+===================
+- max-data-lanes: the hardware can support varying numbers of clock lanes.
+		  This value is the maximum number supported by this instance.
+		  Known values of 2 or 4. Default is 2.
+
+
+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.
+
+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>;
+
+			endpoint {
+				remote-endpoint = <&tc358743_0>;
+
+			};
+		};
+	};
+
+	i2c0: i2c@7e205000 {
+
+		tc358743: tc358743@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>;
+					clock-lanes = <0>;
+					data-lanes = <1 2 3 4>;
+					clock-noncontinuous;
+					link-frequencies =
+						/bits/ 64 <297000000>;
+				};
+			};
+		};
+	};
-- 
2.7.4

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

* [RFC 2/2] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
  2017-06-14 15:15 [RFC 0/2] BCM283x Camera Receiver driver Dave Stevenson
  2017-06-14 15:15 ` [RFC 1/2] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver Dave Stevenson
@ 2017-06-14 15:15 ` Dave Stevenson
  2017-06-15  7:12   ` Hans Verkuil
  2017-06-14 15:42 ` [RFC 0/2] BCM283x Camera Receiver driver Hans Verkuil
  2017-08-28 14:15 ` Hans Verkuil
  3 siblings, 1 reply; 35+ messages in thread
From: Dave Stevenson @ 2017-06-14 15:15 UTC (permalink / raw)
  To: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel; +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                  |    2 +
 drivers/media/platform/bcm2835/Kconfig           |   14 +
 drivers/media/platform/bcm2835/Makefile          |    3 +
 drivers/media/platform/bcm2835/bcm2835-unicam.c  | 2100 ++++++++++++++++++++++
 drivers/media/platform/bcm2835/vc4-regs-unicam.h |  257 +++
 6 files changed, 2377 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 8da521a..1111aa9 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -135,6 +135,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 6bbdf94..9c5e412 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -81,3 +81,5 @@ obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC)	+= mtk-vcodec/
 obj-$(CONFIG_VIDEO_MEDIATEK_MDP)	+= mtk-mdp/
 
 obj-$(CONFIG_VIDEO_MEDIATEK_JPEG)	+= mtk-jpeg/
+
+obj-y					+= bcm2835/
diff --git a/drivers/media/platform/bcm2835/Kconfig b/drivers/media/platform/bcm2835/Kconfig
new file mode 100644
index 0000000..9f9be9e
--- /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_BCM2708 || ARCH_BCM2709 || 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..26039da
--- /dev/null
+++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
@@ -0,0 +1,2100 @@
+/*
+ * 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.
+ *
+ * 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-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. Max width is therefore determined by
+ * that 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	((65536 / 4) - 1)
+#define MAX_HEIGHT	MAX_WIDTH
+
+#define DEFAULT_MAX_DATA_LANES	2
+
+/*
+ * 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_RGGB,
+		.code		= MEDIA_BUS_FMT_SRGGB16_1X16,
+		.depth		= 12,
+		.csi_dt		= 0x2c,
+	}, {
+		.fourcc		= PIX_FMT_ALL_GBRG,
+		.code		= MEDIA_BUS_FMT_SGBRG16_1X16,
+		.depth		= 12,
+		.csi_dt		= 0x2c,
+	}, {
+		.fourcc		= PIX_FMT_ALL_GBRG,
+		.code		= MEDIA_BUS_FMT_SGRBG16_1X16,
+		.depth		= 12,
+		.csi_dt		= 0x2c,
+	}, {
+		.fourcc		= PIX_FMT_ALL_GRBG,
+		.code		= MEDIA_BUS_FMT_SGRBG16_1X16,
+		.depth		= 12,
+		.csi_dt		= 0x2c,
+	},
+};
+
+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;
+
+	unsigned int periph_max_data_lanes;
+};
+
+#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;
+	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);
+}
+
+/* Format setup functions */
+
+static int find_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 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);
+}
+
+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 char *fourcc_to_str(u32 fmt)
+{
+	static char code[5];
+
+	code[0] = (unsigned char)(fmt & 0xff);
+	code[1] = (unsigned char)((fmt >> 8) & 0xff);
+	code[2] = (unsigned char)((fmt >> 16) & 0xff);
+	code[3] = (unsigned char)((fmt >> 24) & 0xff);
+	code[4] = '\0';
+
+	return code;
+}
+
+static void dump_active_formats(struct unicam_device *dev)
+{
+	int i;
+
+	for (i = 0; i < dev->num_active_fmt; i++) {
+		unicam_dbg(3, dev, "active_fmt[%d] (%p) is code %04X, fourcc %s, depth %d\n",
+			   i, &dev->active_fmts[i], dev->active_fmts[i].code,
+			   fourcc_to_str(dev->active_fmts[i].fourcc),
+			   dev->active_fmts[i].depth);
+	}
+}
+
+static inline int bytes_per_line(u32 width, const struct unicam_fmt *fmt)
+{
+	/* Stride must be a multiple of 16. */
+	return ALIGN((width * fmt->depth) >> 3,  16);
+}
+
+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;
+
+	*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;
+	struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+	int ret;
+
+	sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	sd_fmt.pad = 0;
+	*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(struct unicam_device *dev,
+				   const struct unicam_fmt *fmt,
+				   struct v4l2_format *f)
+{
+	int min_bytesperline, min_sizeimage;
+
+	if (!fmt) {
+		unicam_dbg(3, dev, "No unicam_fmt provided!\n");
+		return -EINVAL;
+	}
+
+	v4l_bound_align_image(&f->fmt.pix.width, 16, MAX_WIDTH, 2,
+			      &f->fmt.pix.height, 16, 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 = ALIGN(f->fmt.pix.bytesperline, 16);
+	else
+		f->fmt.pix.bytesperline = min_bytesperline;
+
+	min_sizeimage = f->fmt.pix.height *
+			f->fmt.pix.bytesperline;
+	if (f->fmt.pix.sizeimage < min_sizeimage)
+		f->fmt.pix.sizeimage = min_sizeimage;
+
+	unicam_dbg(1, dev, "width %d, fmt %s, depth %d\n",
+		   f->fmt.pix.width, fourcc_to_str(f->fmt.pix.pixelformat),
+		   fmt->depth);
+	unicam_dbg(3, dev, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n",
+		   __func__, fourcc_to_str(f->fmt.pix.pixelformat),
+		   f->fmt.pix.width, f->fmt.pix.height,
+		   f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+
+	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);
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+			    V4L2_CAP_READWRITE;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+	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;
+	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	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;
+	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.\n",
+			   f->fmt.pix.pixelformat);
+
+		/* Just get the first one enumerated */
+		fmt = &dev->active_fmts[0];
+		f->fmt.pix.pixelformat = fmt->fourcc;
+	}
+
+	sd_fmt.which = V4L2_SUBDEV_FORMAT_TRY;
+	sd_fmt.pad = 0;
+
+	v4l2_fill_mbus_format(mbus_fmt, &f->fmt.pix, fmt->code);
+
+	ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_config,
+			       &sd_fmt);
+	if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
+		return ret;
+
+	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(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;
+
+	if (vb2_is_busy(q)) {
+		unicam_dbg(3, dev, "%s device busy\n", __func__);
+		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);
+	dump_active_formats(dev);
+
+	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;
+	}
+
+	v4l2_fill_pix_format(&dev->v_fmt.fmt.pix, &mbus_fmt);
+	dev->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	dev->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
+	dev->v_fmt.fmt.pix.bytesperline = f->fmt.pix.bytesperline;
+	dev->v_fmt.fmt.pix.sizeimage = f->fmt.pix.sizeimage;
+	unicam_calc_format_size(dev, fmt, &dev->v_fmt);
+
+	unicam_dbg(3, dev,
+		   "%s %dx%d, mbus_fmt %s, V4L2 pix %s. About to overwrite pix with %s\n",
+		   __func__,
+		   dev->v_fmt.fmt.pix.width,
+		   dev->v_fmt.fmt.pix.height,
+		   fourcc_to_str(mbus_fmt.code),
+		   fourcc_to_str(dev->v_fmt.fmt.pix.pixelformat),
+		   fourcc_to_str(fmt->fourcc));
+
+	dev->fmt = fmt;
+	dev->m_fmt = mbus_fmt;
+	*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_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);
+	set_field(&val, UNICAM_CPM_CSI2, UNICAM_CPM_MASK);
+	set_field(&val, UNICAM_DCM_STROBE, 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;
+	set_field(&val, 1, UNICAM_CLE);
+	set_field(&val, 1, UNICAM_CLLPE);
+	reg_write(cfg, UNICAM_CLK, val);
+
+	/* Enable required data lanes */
+	val = 0;
+	set_field(&val, 1, UNICAM_DLE);
+	set_field(&val, 1, UNICAM_DLLPE);
+	reg_write(cfg, UNICAM_DAT0, val);
+
+	if (dev->active_data_lanes == 1)
+		val = 0;
+	reg_write(cfg, UNICAM_DAT1, val);
+
+	if (dev->cfg.periph_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->cfg.periph_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);
+	if (list_empty(&dma_q->active)) {
+		spin_unlock_irqrestore(&dev->dma_queue_lock, flags);
+		unicam_dbg(3, dev, "buffer queue is empty\n");
+		return -EIO;
+	}
+
+	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 (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 (dev->active_data_lanes > dev->cfg.periph_max_data_lanes) {
+		unicam_err(dev, "Device has requested %u data lanes, which is >%u supported by peripheral",
+			   dev->active_data_lanes,
+			   dev->cfg.periph_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, pad, dv_timings_cap)) {
+		inp->capabilities = V4L2_IN_CAP_DV_TIMINGS;
+		inp->std = 0;
+	} else if (v4l2_subdev_has_op(dev->sensor, video, querystd)) {
+		inp->capabilities = V4L2_IN_CAP_STD;
+		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)
+{
+	struct unicam_device *dev = video_drvdata(file);
+	*i = dev->input;
+	return 0;
+}
+
+static int unicam_s_input(struct file *file, void *priv, unsigned int i)
+{
+	struct unicam_device *dev = video_drvdata(file);
+	int ret;
+
+	if (v4l2_subdev_has_op(dev->sensor, video, s_routing))
+		ret =  v4l2_subdev_call(dev->sensor, video, s_routing, i, 0, 0);
+	else
+		ret = -EINVAL;	/* v4l2-compliance insists on -EINVAL */
+
+	/* Must always be able to set input to 0 */
+	if (!i)
+		ret = 0;
+	if (!ret)
+		dev->input = i;
+
+	return ret;
+}
+
+static int unicam_querystd(struct file *file, void *priv,
+			   v4l2_std_id *std)
+{
+	struct unicam_device *dev = video_drvdata(file);
+	int ret;
+
+	ret = v4l2_subdev_call(dev->sensor, video, querystd, std);
+	return ret;
+}
+
+static int unicam_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+	struct unicam_device *dev = video_drvdata(file);
+	int ret;
+
+	ret = v4l2_subdev_call(dev->sensor, video, g_std, std);
+	return ret;
+}
+
+static int unicam_s_std(struct file *file, void *priv, v4l2_std_id std)
+{
+	struct unicam_device *dev = video_drvdata(file);
+	int ret;
+
+	ret = v4l2_subdev_call(dev->sensor, video, s_std, std);
+	return ret;
+}
+
+static int unicam_s_edid(struct file *file, void *priv, struct v4l2_edid *edid)
+{
+	struct unicam_device *dev = video_drvdata(file);
+	int ret;
+
+	ret =  v4l2_subdev_call(dev->sensor, pad, set_edid, edid);
+
+	return ret;
+}
+
+static int unicam_g_edid(struct file *file, void *priv, struct v4l2_edid *edid)
+{
+	struct unicam_device *dev = video_drvdata(file);
+	int ret;
+
+	ret =  v4l2_subdev_call(dev->sensor, pad, get_edid, edid);
+
+	return ret;
+}
+
+static int unicam_g_dv_timings(struct file *file, void *priv,
+			       struct v4l2_dv_timings *timings)
+{
+	struct unicam_device *dev = video_drvdata(file);
+	int ret;
+
+	ret =  v4l2_subdev_call(dev->sensor, video, g_dv_timings, timings);
+
+	return ret;
+}
+
+static int unicam_s_dv_timings(struct file *file, void *priv,
+			       struct v4l2_dv_timings *timings)
+{
+	struct unicam_device *dev = video_drvdata(file);
+	int ret;
+
+	ret =  v4l2_subdev_call(dev->sensor, video, s_dv_timings, timings);
+	if (ret)
+		return ret;
+
+	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);
+	int ret;
+
+	ret =  v4l2_subdev_call(dev->sensor, video, query_dv_timings, timings);
+
+	return ret;
+}
+
+static int unicam_enum_dv_timings(struct file *file, void *priv,
+				  struct v4l2_enum_dv_timings *timings)
+{
+	struct unicam_device *dev = video_drvdata(file);
+	int ret;
+
+	ret =  v4l2_subdev_call(dev->sensor, pad, enum_dv_timings, timings);
+
+	return ret;
+}
+
+static int unicam_dv_timings_cap(struct file *file, void *priv,
+				 struct v4l2_dv_timings_cap *cap)
+{
+	struct unicam_device *dev = video_drvdata(file);
+	int ret;
+
+	ret =  v4l2_subdev_call(dev->sensor, pad, dv_timings_cap, cap);
+
+	return ret;
+}
+
+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 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		= v4l2_ctrl_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;
+
+	unicam_dbg(1, unicam, "unicam_async_bound\n");
+
+	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++) {
+					*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",
+						   fourcc_to_str(fmt->fourcc),
+						   fmt->code,
+						   unicam->num_active_fmt);
+					unicam->num_active_fmt++;
+					active_fmt++;
+				}
+			} else {
+				*active_fmt = *fmt;
+				unicam_dbg(2, unicam,
+					   "matched fourcc: %s: code: %04x idx: %d\n",
+					   fourcc_to_str(fmt->fourcc),
+					   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;
+	const struct unicam_fmt *fmt;
+	struct v4l2_mbus_framefmt mbus_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;
+	}
+
+	/* Save current subdev format */
+	v4l2_fill_pix_format(&unicam->v_fmt.fmt.pix, &mbus_fmt);
+	unicam->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	unicam->v_fmt.fmt.pix.pixelformat  = fmt->fourcc;
+	unicam->v_fmt.fmt.pix.bytesperline = 0;
+	unicam_calc_format_size(unicam, fmt, &unicam->v_fmt);
+	unicam->fmt = fmt;
+	unicam->m_fmt = mbus_fmt;
+
+	if (v4l2_subdev_has_op(unicam->sensor, video, querystd)) {
+		if (v4l2_subdev_has_op(unicam->sensor, video, g_tvnorms)) {
+			v4l2_std_id tvnorms;
+
+			ret = v4l2_subdev_call(unicam->sensor, video,
+					       g_tvnorms, &tvnorms);
+			if (ret) {
+				unicam_dbg(1, unicam, "Sensor supports g_tvnorms, but returned error\n");
+				unicam->video_dev.tvnorms |= V4L2_STD_ALL;
+			} else {
+				unicam->video_dev.tvnorms |= tvnorms;
+			}
+		} else {
+			unicam->video_dev.tvnorms |= V4L2_STD_ALL;
+		}
+	}
+
+	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->dev_debug = 0xff;
+	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;
+	}
+
+	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 struct device_node *
+of_get_port(const struct device_node *parent)
+{
+	struct device_node *port = NULL;
+
+	if (!parent)
+		return NULL;
+
+	port = of_get_child_by_name(parent, "port");
+
+	return port;
+}
+
+static struct device_node *
+of_get_next_endpoint(const struct device_node *parent,
+		     struct device_node *prev)
+{
+	struct device_node *ep = NULL;
+
+	if (!parent)
+		return NULL;
+
+	do {
+		ep = of_get_next_child(parent, prev);
+		if (!ep)
+			return NULL;
+		prev = ep;
+	} while (of_node_cmp(ep->name, "endpoint") != 0);
+
+	return ep;
+}
+
+static int of_unicam_connect_subdevs(struct unicam_device *dev)
+{
+	struct platform_device *pdev = dev->pdev;
+	struct device_node *parent, *ep_node = NULL, *port = NULL,
+			*remote_ep = NULL, *sensor_node = NULL;
+	struct v4l2_fwnode_endpoint *endpoint;
+	struct v4l2_async_subdev *asd;
+	int ret = -EINVAL;
+	unsigned int lane;
+	struct v4l2_async_subdev **subdevs = NULL;
+
+	parent = pdev->dev.of_node;
+
+	asd = &dev->asd;
+	endpoint = &dev->endpoint;
+
+	dev->cfg.periph_max_data_lanes = DEFAULT_MAX_DATA_LANES;
+	of_property_read_u32(parent, "max_data_lanes",
+			     &dev->cfg.periph_max_data_lanes);
+
+	unicam_dbg(3, dev, "Scanning Port node for csi2 port\n");
+	port = of_get_port(parent);
+	if (!port) {
+		unicam_dbg(1, dev, "No port node found for csi2 port\n");
+		goto cleanup_exit;
+	}
+
+	if (!port) {
+		unicam_dbg(1, dev, "No port node matches csi2 port\n");
+		goto cleanup_exit;
+	}
+
+	unicam_dbg(3, dev, "Scanning sub-device for csi2 port\n");
+
+	ep_node = of_get_next_endpoint(port, ep_node);
+	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);
+
+	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_parse_phandle(ep_node, "remote-endpoint", 0);
+	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), endpoint);
+	unicam_dbg(3, dev, "parsed remote_ep to endpoint. nr_of_link_frequencies %u, bus_type %u\n",
+		   endpoint->nr_of_link_frequencies, endpoint->bus_type);
+
+	if (endpoint->bus_type != V4L2_MBUS_CSI2) {
+		/* We can support CCP2 too, but no enum defined for that. */
+		unicam_err(dev, "sub-device %s is not a CSI2 device %d\n",
+			   sensor_node->name, endpoint->bus_type);
+		goto cleanup_exit;
+	}
+
+	/* Store bus type - CSI2 vs CCP2 */
+	dev->bus_type = endpoint->bus_type;
+	unicam_dbg(3, dev, "bus_type is %d\n", dev->bus_type);
+
+	/* Store Virtual Channel number */
+	dev->virtual_channel = endpoint->base.id;
+
+	/* Store number of data lanes */
+	dev->max_data_lanes = endpoint->bus.mipi_csi2.num_data_lanes;
+
+	if (dev->max_data_lanes > dev->cfg.periph_max_data_lanes) {
+		/* We can support CCP2 too, but no enum defined for that. */
+		unicam_err(dev, "Subdevice %s wants too many data lanes (%u > %u)\n",
+			   sensor_node->name, dev->max_data_lanes,
+			   dev->cfg.periph_max_data_lanes);
+		goto cleanup_exit;
+	}
+	for (lane = 0; lane < dev->max_data_lanes; lane++) {
+		if (endpoint->bus.mipi_csi2.data_lanes[lane] != lane + 1) {
+			unicam_err(dev, "Subdevice %s - incompatible data lane config\n",
+				   sensor_node->name);
+			goto cleanup_exit;
+		}
+	}
+
+	unicam_dbg(3, dev, "v4l2-endpoint: CSI2\n");
+	unicam_dbg(3, dev, "Virtual Channel=%d\n", dev->virtual_channel);
+	unicam_dbg(3, dev, "flags=0x%08x\n", endpoint->bus.mipi_csi2.flags);
+	unicam_dbg(3, dev, "clock_lane=%d\n",
+		   endpoint->bus.mipi_csi2.clock_lane);
+	unicam_dbg(3, dev, "num_data_lanes=%d\n",
+		   endpoint->bus.mipi_csi2.num_data_lanes);
+	unicam_dbg(3, dev, "data_lanes= <\n");
+	for (lane = 0; lane < endpoint->bus.mipi_csi2.num_data_lanes; lane++)
+		unicam_dbg(3, dev, "\t%u\n",
+			   endpoint->bus.mipi_csi2.data_lanes[lane]);
+	unicam_dbg(3, dev, "\t>\n");
+
+	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);
+	if (port)
+		of_node_put(port);
+
+	return ret;
+}
+
+/*
+ * unicam_probe : This function creates device entries by register
+ * itself to the V4L2 driver and initializes fields of each
+ * device objects
+ */
+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 */
+	/*
+	 * Currently the subdev with the largest number of controls (13) is
+	 * ov6550. So let's pick 16 as a hint for the control handler. Note
+	 * that this is a hint only: too large and you waste some memory, too
+	 * small and there is a (very) small performance hit when looking up
+	 * controls in the internal hash.
+	 */
+	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;
+}
+
+/*
+ * unicam_remove : It un-register device from V4L2 driver
+ */
+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..793e42a
--- /dev/null
+++ b/drivers/media/platform/bcm2835/vc4-regs-unicam.h
@@ -0,0 +1,257 @@
+/*
+ *  Copyright (C) 2017 Raspberry Pi Trading.
+ *
+ * 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.
+ */
+
+/*
+ * Modified from brcm_usrlib/dag/vmcsx/vcinclude/hardware_vc4.h to
+ * just have the offset, and use the BIT and GENMASK kernel macros.
+ */
+
+#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 Broadcom datasheets */
+/* 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] 35+ messages in thread

* Re: [RFC 0/2] BCM283x Camera Receiver driver
  2017-06-14 15:15 [RFC 0/2] BCM283x Camera Receiver driver Dave Stevenson
  2017-06-14 15:15 ` [RFC 1/2] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver Dave Stevenson
  2017-06-14 15:15 ` [RFC 2/2] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface Dave Stevenson
@ 2017-06-14 15:42 ` Hans Verkuil
  2017-06-14 16:29   ` Dave Stevenson
  2017-08-28 14:15 ` Hans Verkuil
  3 siblings, 1 reply; 35+ messages in thread
From: Hans Verkuil @ 2017-06-14 15:42 UTC (permalink / raw)
  To: Dave Stevenson, linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

Hi Dave,

How does this driver relate to this staging driver:

drivers/staging/vc04_services/bcm2835-camera/

It's not obvious to me.

On 06/14/2017 05:15 PM, Dave Stevenson wrote:
> Hi All.
> 
> This is adding a V4L2 subdevice driver for the CSI2/CCP2 camera
> receiver peripheral on BCM283x, as used on Raspberry Pi.
> 
> v4l2-compliance results depend on the sensor subdevice this is
> connected to. It passes the basic tests cleanly with TC358743,
> but objects with OV5647
> fail: v4l2-test-controls.cpp(574): g_ext_ctrls does not support count == 0
> Neither OV5647 nor Unicam support any controls.

Are you compiling v4l2-compliance from the v4l-utils git repo? If not,
then please do so and run again. The version packaged by distros tends
to be seriously outdated.

> 
> I must admit to not having got OV5647 to stream with the current driver
> register settings. It works with a set of register settings for VGA RAW10.
> I also have a couple of patches pending for OV5647, but would like to
> understand the issues better before sending them out.
> 
> Two queries I do have in V4L2-land:
> - When s_dv_timings or s_std is called, is the format meant to
>    be updated automatically?

Yes. Exception is if the new timings/std is exactly the same as the old
timings/std, in that case you can just return 0 and do nothing.

 > Even if we're already streaming?

That's not allowed. Return -EBUSY in that case.

>    Some existing drivers seem to, but others don't.
> - With s_fmt, is sizeimage settable by the application in the same
>    way as bytesperline?

No, the driver will fill in this field, overwriting anything the
application put there.

bytesperline IS settable, but most drivers will ignore what userspace
did and overwrite this as well.

Normally the driver knows about HW requirements and will set sizeimage
to something that will work (e.g. make sure it is a multiple of 16 lines).


 > yavta allows you to specify it on the command
>    line, whilst v4l2-ctl doesn't. Some of the other parts of the Pi
>    firmware have a requirement that the buffer is a multiple of 16 lines
>    high, which can be matched by V4L2 if we can over-allocate the
>    buffers by the app specifying sizeimage. But if I allow that,
>    then I get a v4l2-compliance failure as the size doesn't get
>    reset when switching from RGB3 to UYVY as it takes the request as
>    a request to over-allocate.
> 
> Apologies if I've messed up in sending these patches - so many ways
> to do something.

It looks fine at a glance.

I will probably review this on Friday or Monday. But I need some clarification
of the difference between this and the staging driver first.

Thanks!

	Hans

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

* Re: [RFC 0/2] BCM283x Camera Receiver driver
  2017-06-14 15:42 ` [RFC 0/2] BCM283x Camera Receiver driver Hans Verkuil
@ 2017-06-14 16:29   ` Dave Stevenson
  2017-06-14 17:38     ` Hans Verkuil
  0 siblings, 1 reply; 35+ messages in thread
From: Dave Stevenson @ 2017-06-14 16:29 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

Hi Hans.

On 14 June 2017 at 16:42, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> Hi Dave,
>
> How does this driver relate to this staging driver:
>
> drivers/staging/vc04_services/bcm2835-camera/
>
> It's not obvious to me.

drivers/staging/vc04_services/bcm2835-camera/ is using the VideoCore
firmware to control Unicam, ISP, and all the tuner algorithms. The ARM
gets delivered fully processed buffers from the VideoCore side. The
firmware only has drivers for the Omnivision OV5647 and Sony IMX219
(and an unsupported one for the Toshiba TC358743).

This driver is solely the Unicam block, reading the data in over
CSI2/CCP2 from the sensor and writing it to memory. No ISP or control
loops.
Other than power management, this driver is running solely on the ARM
with no involvement from the VideoCore firmware.
The sensor driver is whatever suitable V4L2 subdevice driver you fancy
attaching (as long as it supports CSI2, or eventually CCP2).

> On 06/14/2017 05:15 PM, Dave Stevenson wrote:
>>
>> Hi All.
>>
>> This is adding a V4L2 subdevice driver for the CSI2/CCP2 camera
>> receiver peripheral on BCM283x, as used on Raspberry Pi.
>>
>> v4l2-compliance results depend on the sensor subdevice this is
>> connected to. It passes the basic tests cleanly with TC358743,
>> but objects with OV5647
>> fail: v4l2-test-controls.cpp(574): g_ext_ctrls does not support count == 0
>> Neither OV5647 nor Unicam support any controls.
>
>
> Are you compiling v4l2-compliance from the v4l-utils git repo? If not,
> then please do so and run again. The version packaged by distros tends
> to be seriously outdated.

Yes, I'm building from v4l-utils.git.
I updated within the last week, although you appear to have added 2
commits since (both CEC related).
I'm on "ef074cf media-ctl: add colorimetry support"

>> I must admit to not having got OV5647 to stream with the current driver
>> register settings. It works with a set of register settings for VGA RAW10.
>> I also have a couple of patches pending for OV5647, but would like to
>> understand the issues better before sending them out.
>>
>> Two queries I do have in V4L2-land:
>> - When s_dv_timings or s_std is called, is the format meant to
>>    be updated automatically?
>
>
> Yes. Exception is if the new timings/std is exactly the same as the old
> timings/std, in that case you can just return 0 and do nothing.

OK, can do that.

>> Even if we're already streaming?
>
> That's not allowed. Return -EBUSY in that case.

Also reasonable.
So if the TC358743 flags a source change we have to stop streaming,
set the new timings (which will update the format), and start up again
with fresh buffers. That's what I was expecting, but wanted to
confirm.

>>    Some existing drivers seem to, but others don't.
>> - With s_fmt, is sizeimage settable by the application in the same
>>    way as bytesperline?
>
>
> No, the driver will fill in this field, overwriting anything the
> application put there.
>
> bytesperline IS settable, but most drivers will ignore what userspace
> did and overwrite this as well.
>
> Normally the driver knows about HW requirements and will set sizeimage
> to something that will work (e.g. make sure it is a multiple of 16 lines).

There are subtly different requirements in different hardware blocks :-(
eg Unicam needs bytesperline to be a multiple of 16 bytes,whilst the
ISP requires a multiple of 32.
The vertical padding is generally where we're doing software
processing on the VideoCore side as it's easier to just leave the the
16 way SIMD processor running all 16 ways, hence needing scratch space
to avoid reading beyond buffers.

The main consumer is likely to be the ISP and that doesn't need
vertical context, so I'll look at removing the requirement there
rather than forcing it in this driver.
As long as we can set bytesperline (which is already supported) then
that requirement of the ISP is already handled.

>> yavta allows you to specify it on the command
>>
>>    line, whilst v4l2-ctl doesn't. Some of the other parts of the Pi
>>    firmware have a requirement that the buffer is a multiple of 16 lines
>>    high, which can be matched by V4L2 if we can over-allocate the
>>    buffers by the app specifying sizeimage. But if I allow that,
>>    then I get a v4l2-compliance failure as the size doesn't get
>>    reset when switching from RGB3 to UYVY as it takes the request as
>>    a request to over-allocate.
>>
>> Apologies if I've messed up in sending these patches - so many ways
>> to do something.
>
>
> It looks fine at a glance.
>
> I will probably review this on Friday or Monday. But I need some
> clarification
> of the difference between this and the staging driver first.

Thanks. Hopefully I've given you that clarification above.

I'll fix the s_dv_timings and s_std handling, and s_fmt of sizeimage,
but will wait for other review comments before sending a v2.

  Dave

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

* Re: [RFC 0/2] BCM283x Camera Receiver driver
  2017-06-14 16:29   ` Dave Stevenson
@ 2017-06-14 17:38     ` Hans Verkuil
  2017-06-14 21:03       ` Dave Stevenson
  0 siblings, 1 reply; 35+ messages in thread
From: Hans Verkuil @ 2017-06-14 17:38 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

On 06/14/2017 06:29 PM, Dave Stevenson wrote:
> Hi Hans.
> 
> On 14 June 2017 at 16:42, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>> Hi Dave,
>>
>> How does this driver relate to this staging driver:
>>
>> drivers/staging/vc04_services/bcm2835-camera/
>>
>> It's not obvious to me.
> 
> drivers/staging/vc04_services/bcm2835-camera/ is using the VideoCore
> firmware to control Unicam, ISP, and all the tuner algorithms. The ARM
> gets delivered fully processed buffers from the VideoCore side. The
> firmware only has drivers for the Omnivision OV5647 and Sony IMX219
> (and an unsupported one for the Toshiba TC358743).
> 
> This driver is solely the Unicam block, reading the data in over
> CSI2/CCP2 from the sensor and writing it to memory. No ISP or control
> loops.
> Other than power management, this driver is running solely on the ARM
> with no involvement from the VideoCore firmware.
> The sensor driver is whatever suitable V4L2 subdevice driver you fancy
> attaching (as long as it supports CSI2, or eventually CCP2).

What is the interaction between these two drivers? Can they co-exist?
I would expect them to be mutually exclusive.

> 
>> On 06/14/2017 05:15 PM, Dave Stevenson wrote:
>>>
>>> Hi All.
>>>
>>> This is adding a V4L2 subdevice driver for the CSI2/CCP2 camera
>>> receiver peripheral on BCM283x, as used on Raspberry Pi.
>>>
>>> v4l2-compliance results depend on the sensor subdevice this is
>>> connected to. It passes the basic tests cleanly with TC358743,
>>> but objects with OV5647
>>> fail: v4l2-test-controls.cpp(574): g_ext_ctrls does not support count == 0
>>> Neither OV5647 nor Unicam support any controls.
>>
>>
>> Are you compiling v4l2-compliance from the v4l-utils git repo? If not,
>> then please do so and run again. The version packaged by distros tends
>> to be seriously outdated.
> 
> Yes, I'm building from v4l-utils.git.
> I updated within the last week, although you appear to have added 2
> commits since (both CEC related).
> I'm on "ef074cf media-ctl: add colorimetry support"

But the line with that error is at line number 587 in my repo, not 574.
So I'm a bit suspicious.

Anyway, can you give the output of 'v4l2-ctl -l'?

> 
>>> I must admit to not having got OV5647 to stream with the current driver
>>> register settings. It works with a set of register settings for VGA RAW10.
>>> I also have a couple of patches pending for OV5647, but would like to
>>> understand the issues better before sending them out.
>>>
>>> Two queries I do have in V4L2-land:
>>> - When s_dv_timings or s_std is called, is the format meant to
>>>     be updated automatically?
>>
>>
>> Yes. Exception is if the new timings/std is exactly the same as the old
>> timings/std, in that case you can just return 0 and do nothing.
> 
> OK, can do that.
> 
>>> Even if we're already streaming?
>>
>> That's not allowed. Return -EBUSY in that case.
> 
> Also reasonable.
> So if the TC358743 flags a source change we have to stop streaming,
> set the new timings (which will update the format), and start up again
> with fresh buffers. That's what I was expecting, but wanted to
> confirm.

Correct. In theory there are ways around this provided the buffers are
large enough to accommodate the new format size, but nobody actually
supports that (might change in the not-to-distant future).

> 
>>>     Some existing drivers seem to, but others don't.
>>> - With s_fmt, is sizeimage settable by the application in the same
>>>     way as bytesperline?
>>
>>
>> No, the driver will fill in this field, overwriting anything the
>> application put there.
>>
>> bytesperline IS settable, but most drivers will ignore what userspace
>> did and overwrite this as well.
>>
>> Normally the driver knows about HW requirements and will set sizeimage
>> to something that will work (e.g. make sure it is a multiple of 16 lines).
> 
> There are subtly different requirements in different hardware blocks :-(
> eg Unicam needs bytesperline to be a multiple of 16 bytes,whilst the
> ISP requires a multiple of 32.
> The vertical padding is generally where we're doing software
> processing on the VideoCore side as it's easier to just leave the the
> 16 way SIMD processor running all 16 ways, hence needing scratch space
> to avoid reading beyond buffers.
> 
> The main consumer is likely to be the ISP and that doesn't need
> vertical context, so I'll look at removing the requirement there
> rather than forcing it in this driver.
> As long as we can set bytesperline (which is already supported) then
> that requirement of the ISP is already handled.

It's up to you, but given the large sizes of the buffers the extra bit
of padding doesn't really matter all that much. And if it would make it
easier to digest by other HW blocks, then I would just add the padding.

Note that users can also call VIDIOC_CREATE_BUFS instead of VIDIOC_REQBUFS
in order to request buffers of a larger-than-needed size. I'm not sure if
you are aware of that.

> 
>>> yavta allows you to specify it on the command
>>>
>>>     line, whilst v4l2-ctl doesn't. Some of the other parts of the Pi
>>>     firmware have a requirement that the buffer is a multiple of 16 lines
>>>     high, which can be matched by V4L2 if we can over-allocate the
>>>     buffers by the app specifying sizeimage. But if I allow that,
>>>     then I get a v4l2-compliance failure as the size doesn't get
>>>     reset when switching from RGB3 to UYVY as it takes the request as
>>>     a request to over-allocate.
>>>
>>> Apologies if I've messed up in sending these patches - so many ways
>>> to do something.
>>
>>
>> It looks fine at a glance.
>>
>> I will probably review this on Friday or Monday. But I need some
>> clarification
>> of the difference between this and the staging driver first.
> 
> Thanks. Hopefully I've given you that clarification above.
> 
> I'll fix the s_dv_timings and s_std handling, and s_fmt of sizeimage,
> but will wait for other review comments before sending a v2.

OK. As said, I'll take a closer look on Friday or Monday.

Regards,

	Hans

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

* Re: [RFC 0/2] BCM283x Camera Receiver driver
  2017-06-14 17:38     ` Hans Verkuil
@ 2017-06-14 21:03       ` Dave Stevenson
  2017-06-15  7:17         ` Hans Verkuil
  0 siblings, 1 reply; 35+ messages in thread
From: Dave Stevenson @ 2017-06-14 21:03 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

On 14 June 2017 at 18:38, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> On 06/14/2017 06:29 PM, Dave Stevenson wrote:
>>
>> Hi Hans.
>>
>> On 14 June 2017 at 16:42, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>>
>>> Hi Dave,
>>>
>>> How does this driver relate to this staging driver:
>>>
>>> drivers/staging/vc04_services/bcm2835-camera/
>>>
>>> It's not obvious to me.
>>
>>
>> drivers/staging/vc04_services/bcm2835-camera/ is using the VideoCore
>> firmware to control Unicam, ISP, and all the tuner algorithms. The ARM
>> gets delivered fully processed buffers from the VideoCore side. The
>> firmware only has drivers for the Omnivision OV5647 and Sony IMX219
>> (and an unsupported one for the Toshiba TC358743).
>>
>> This driver is solely the Unicam block, reading the data in over
>> CSI2/CCP2 from the sensor and writing it to memory. No ISP or control
>> loops.
>> Other than power management, this driver is running solely on the ARM
>> with no involvement from the VideoCore firmware.
>> The sensor driver is whatever suitable V4L2 subdevice driver you fancy
>> attaching (as long as it supports CSI2, or eventually CCP2).
>
>
> What is the interaction between these two drivers? Can they co-exist?
> I would expect them to be mutually exclusive.

Mutually exclusive for the same Unicam instance, yes.

There are two Unicam instances on all BCM283x chips and both are
brought out on the Compute Modules. You could run bcm2835-unicam on
one and bcm2835-camera on the other if you so wished.
The firmware checks whether the csi nodes in the device tree are
active, and won't touch them if they are.

>>
>>> On 06/14/2017 05:15 PM, Dave Stevenson wrote:
>>>>
>>>>
>>>> Hi All.
>>>>
>>>> This is adding a V4L2 subdevice driver for the CSI2/CCP2 camera
>>>> receiver peripheral on BCM283x, as used on Raspberry Pi.
>>>>
>>>> v4l2-compliance results depend on the sensor subdevice this is
>>>> connected to. It passes the basic tests cleanly with TC358743,
>>>> but objects with OV5647
>>>> fail: v4l2-test-controls.cpp(574): g_ext_ctrls does not support count ==
>>>> 0
>>>> Neither OV5647 nor Unicam support any controls.
>>>
>>>
>>>
>>> Are you compiling v4l2-compliance from the v4l-utils git repo? If not,
>>> then please do so and run again. The version packaged by distros tends
>>> to be seriously outdated.
>>
>>
>> Yes, I'm building from v4l-utils.git.
>> I updated within the last week, although you appear to have added 2
>> commits since (both CEC related).
>> I'm on "ef074cf media-ctl: add colorimetry support"
>
>
> But the line with that error is at line number 587 in my repo, not 574.
> So I'm a bit suspicious.
>
> Anyway, can you give the output of 'v4l2-ctl -l'?

Will do tomorrow. I'll update to the latest v4l2-conformance too to
avoid confusion.

>>
>>>> I must admit to not having got OV5647 to stream with the current driver
>>>> register settings. It works with a set of register settings for VGA
>>>> RAW10.
>>>> I also have a couple of patches pending for OV5647, but would like to
>>>> understand the issues better before sending them out.
>>>>
>>>> Two queries I do have in V4L2-land:
>>>> - When s_dv_timings or s_std is called, is the format meant to
>>>>     be updated automatically?
>>>
>>>
>>>
>>> Yes. Exception is if the new timings/std is exactly the same as the old
>>> timings/std, in that case you can just return 0 and do nothing.
>>
>>
>> OK, can do that.
>>
>>>> Even if we're already streaming?
>>>
>>>
>>> That's not allowed. Return -EBUSY in that case.
>>
>>
>> Also reasonable.
>> So if the TC358743 flags a source change we have to stop streaming,
>> set the new timings (which will update the format), and start up again
>> with fresh buffers. That's what I was expecting, but wanted to
>> confirm.
>
>
> Correct. In theory there are ways around this provided the buffers are
> large enough to accommodate the new format size, but nobody actually
> supports that (might change in the not-to-distant future).
>
>>
>>>>     Some existing drivers seem to, but others don't.
>>>> - With s_fmt, is sizeimage settable by the application in the same
>>>>     way as bytesperline?
>>>
>>>
>>>
>>> No, the driver will fill in this field, overwriting anything the
>>> application put there.
>>>
>>> bytesperline IS settable, but most drivers will ignore what userspace
>>> did and overwrite this as well.
>>>
>>> Normally the driver knows about HW requirements and will set sizeimage
>>> to something that will work (e.g. make sure it is a multiple of 16
>>> lines).
>>
>>
>> There are subtly different requirements in different hardware blocks :-(
>> eg Unicam needs bytesperline to be a multiple of 16 bytes,whilst the
>> ISP requires a multiple of 32.
>> The vertical padding is generally where we're doing software
>> processing on the VideoCore side as it's easier to just leave the the
>> 16 way SIMD processor running all 16 ways, hence needing scratch space
>> to avoid reading beyond buffers.
>>
>> The main consumer is likely to be the ISP and that doesn't need
>> vertical context, so I'll look at removing the requirement there
>> rather than forcing it in this driver.
>> As long as we can set bytesperline (which is already supported) then
>> that requirement of the ISP is already handled.
>
>
> It's up to you, but given the large sizes of the buffers the extra bit
> of padding doesn't really matter all that much. And if it would make it
> easier to digest by other HW blocks, then I would just add the padding.
>
> Note that users can also call VIDIOC_CREATE_BUFS instead of VIDIOC_REQBUFS
> in order to request buffers of a larger-than-needed size. I'm not sure if
> you are aware of that.

I'll have a look in both cases.

Forcing the user to have to create the buffers in a particular manner
feels a little nasty, but then again it is only the app/downstream
processing that really knows what buffer size is required there.
Unicam can then be set up just enforcing its own requirements. Perhaps
that is the better way around.

>>
>>>> yavta allows you to specify it on the command
>>>>
>>>>     line, whilst v4l2-ctl doesn't. Some of the other parts of the Pi
>>>>     firmware have a requirement that the buffer is a multiple of 16
>>>> lines
>>>>     high, which can be matched by V4L2 if we can over-allocate the
>>>>     buffers by the app specifying sizeimage. But if I allow that,
>>>>     then I get a v4l2-compliance failure as the size doesn't get
>>>>     reset when switching from RGB3 to UYVY as it takes the request as
>>>>     a request to over-allocate.
>>>>
>>>> Apologies if I've messed up in sending these patches - so many ways
>>>> to do something.
>>>
>>>
>>>
>>> It looks fine at a glance.
>>>
>>> I will probably review this on Friday or Monday. But I need some
>>> clarification
>>> of the difference between this and the staging driver first.
>>
>>
>> Thanks. Hopefully I've given you that clarification above.
>>
>> I'll fix the s_dv_timings and s_std handling, and s_fmt of sizeimage,
>> but will wait for other review comments before sending a v2.
>
>
> OK. As said, I'll take a closer look on Friday or Monday.

Thank you. It is much appreciated.

  Dave

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

* Re: [RFC 1/2] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
  2017-06-14 15:15 ` [RFC 1/2] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver Dave Stevenson
@ 2017-06-15  6:34   ` Stefan Wahren
  2017-06-15 12:35     ` Dave Stevenson
  2017-06-15 12:59   ` Sakari Ailus
  1 sibling, 1 reply; 35+ messages in thread
From: Stefan Wahren @ 2017-06-15  6:34 UTC (permalink / raw)
  To: Dave Stevenson, linux-media, Mauro Carvalho Chehab,
	linux-rpi-kernel, Hans Verkuil

Hi Dave,

Am 14.06.2017 um 17:15 schrieb 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>

please add the devicetree guys in CC for the binding.

> ---
>  .../devicetree/bindings/media/bcm2835-unicam.txt   | 76 ++++++++++++++++++++++
>  1 file changed, 76 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..cc5a451
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
> @@ -0,0 +1,76 @@
> +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.

It would be nice to add some of your explanations to Hans in this
document or into the driver.

> +
> +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.
> +
> +Optional properties
> +===================
> +- max-data-lanes: the hardware can support varying numbers of clock lanes.
> +		  This value is the maximum number supported by this instance.
> +		  Known values of 2 or 4. Default is 2.

AFAIK, this isn't a common property yet. So possibly a vendor prefix
must be added.

> +
> +
> +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.
> +
> +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>;
> +
> +			endpoint {
> +				remote-endpoint = <&tc358743_0>;
> +
> +			};
> +		};
> +	};
> +
> +	i2c0: i2c@7e205000 {
> +
> +		tc358743: tc358743@0f {

Usually the node name should describe the function of the node for example:

tc358743: csi-hdmi-bridge@0f

Best regards
Stefan

> +			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>;
> +					clock-lanes = <0>;
> +					data-lanes = <1 2 3 4>;
> +					clock-noncontinuous;
> +					link-frequencies =
> +						/bits/ 64 <297000000>;
> +				};
> +			};
> +		};
> +	};

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

* Re: [RFC 2/2] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
  2017-06-14 15:15 ` [RFC 2/2] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface Dave Stevenson
@ 2017-06-15  7:12   ` Hans Verkuil
  2017-06-15  7:20     ` Stefan Wahren
  2017-06-15 13:38     ` Dave Stevenson
  0 siblings, 2 replies; 35+ messages in thread
From: Hans Verkuil @ 2017-06-15  7:12 UTC (permalink / raw)
  To: Dave Stevenson, linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

Hi Dave,

Here is a quick review of this driver. Once a v2 is posted I'll do a more thorough
check.

On 06/14/2017 05:15 PM, Dave Stevenson wrote:
> 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                  |    2 +
>   drivers/media/platform/bcm2835/Kconfig           |   14 +
>   drivers/media/platform/bcm2835/Makefile          |    3 +
>   drivers/media/platform/bcm2835/bcm2835-unicam.c  | 2100 ++++++++++++++++++++++
>   drivers/media/platform/bcm2835/vc4-regs-unicam.h |  257 +++
>   6 files changed, 2377 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 8da521a..1111aa9 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -135,6 +135,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 6bbdf94..9c5e412 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -81,3 +81,5 @@ obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC)	+= mtk-vcodec/
>   obj-$(CONFIG_VIDEO_MEDIATEK_MDP)	+= mtk-mdp/
>   
>   obj-$(CONFIG_VIDEO_MEDIATEK_JPEG)	+= mtk-jpeg/
> +
> +obj-y					+= bcm2835/
> diff --git a/drivers/media/platform/bcm2835/Kconfig b/drivers/media/platform/bcm2835/Kconfig
> new file mode 100644
> index 0000000..9f9be9e
> --- /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_BCM2708 || ARCH_BCM2709 || ARCH_BCM2835 || COMPILE_TEST

So this block is available on other broadcom SoCs as well? Not just the 2835?
Should the description of this Kconfig be adapted?

> +	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..26039da
> --- /dev/null
> +++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
> @@ -0,0 +1,2100 @@
> +/*
> + * 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.
> + *
> + * 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-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. Max width is therefore determined by
> + * that 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	((65536 / 4) - 1)
> +#define MAX_HEIGHT	MAX_WIDTH
> +
> +#define DEFAULT_MAX_DATA_LANES	2
> +
> +/*
> + * 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_RGGB,
> +		.code		= MEDIA_BUS_FMT_SRGGB16_1X16,
> +		.depth		= 12,
> +		.csi_dt		= 0x2c,
> +	}, {
> +		.fourcc		= PIX_FMT_ALL_GBRG,
> +		.code		= MEDIA_BUS_FMT_SGBRG16_1X16,
> +		.depth		= 12,
> +		.csi_dt		= 0x2c,
> +	}, {
> +		.fourcc		= PIX_FMT_ALL_GBRG,
> +		.code		= MEDIA_BUS_FMT_SGRBG16_1X16,
> +		.depth		= 12,
> +		.csi_dt		= 0x2c,
> +	}, {
> +		.fourcc		= PIX_FMT_ALL_GRBG,
> +		.code		= MEDIA_BUS_FMT_SGRBG16_1X16,
> +		.depth		= 12,
> +		.csi_dt		= 0x2c,
> +	},
> +};
> +
> +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},*/

Why is this commented out? Either uncomment, add a proper comment explaining why
or remove it.

> +	{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;
> +
> +	unsigned int periph_max_data_lanes;
> +};
> +
> +#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;
> +	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);
> +}
> +
> +/* Format setup functions */
> +
> +static int find_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 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);
> +}
> +
> +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 char *fourcc_to_str(u32 fmt)
> +{
> +	static char code[5];
> +
> +	code[0] = (unsigned char)(fmt & 0xff);
> +	code[1] = (unsigned char)((fmt >> 8) & 0xff);
> +	code[2] = (unsigned char)((fmt >> 16) & 0xff);
> +	code[3] = (unsigned char)((fmt >> 24) & 0xff);
> +	code[4] = '\0';
> +
> +	return code;
> +}

I understand that there can be two instances of this device, so using a static char is
wrong.

I think this function can be a useful addition to v4l2-common.c/h.

BTW, v4l2-ctl uses this function to convert a fourcc to a string:

std::string fcc2s(unsigned int val)
{
         std::string s;

         s += val & 0x7f;
         s += (val >> 8) & 0x7f;
         s += (val >> 16) & 0x7f;
         s += (val >> 24) & 0x7f;
         if (val & (1 << 31))
                 s += "-BE";
         return s;
}

Bit 31 has special meaning.

Proposal for v4l2-common.h:

#define V4L2_FOURCC_MAX_SIZE 8
char *v4l2_fourcc2s(u32 fourcc, char *buf);

This function fills in buf and returns buf.

> +
> +static void dump_active_formats(struct unicam_device *dev)
> +{
> +	int i;
> +
> +	for (i = 0; i < dev->num_active_fmt; i++) {
> +		unicam_dbg(3, dev, "active_fmt[%d] (%p) is code %04X, fourcc %s, depth %d\n",
> +			   i, &dev->active_fmts[i], dev->active_fmts[i].code,
> +			   fourcc_to_str(dev->active_fmts[i].fourcc),
> +			   dev->active_fmts[i].depth);
> +	}
> +}
> +
> +static inline int bytes_per_line(u32 width, const struct unicam_fmt *fmt)
> +{
> +	/* Stride must be a multiple of 16. */
> +	return ALIGN((width * fmt->depth) >> 3,  16);
> +}
> +
> +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;
> +
> +	*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;
> +	struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
> +	int ret;
> +
> +	sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +	sd_fmt.pad = 0;
> +	*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(struct unicam_device *dev,
> +				   const struct unicam_fmt *fmt,
> +				   struct v4l2_format *f)
> +{
> +	int min_bytesperline, min_sizeimage;
> +
> +	if (!fmt) {
> +		unicam_dbg(3, dev, "No unicam_fmt provided!\n");
> +		return -EINVAL;
> +	}
> +
> +	v4l_bound_align_image(&f->fmt.pix.width, 16, MAX_WIDTH, 2,
> +			      &f->fmt.pix.height, 16, 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 = ALIGN(f->fmt.pix.bytesperline, 16);
> +	else
> +		f->fmt.pix.bytesperline = min_bytesperline;
> +
> +	min_sizeimage = f->fmt.pix.height *
> +			f->fmt.pix.bytesperline;
> +	if (f->fmt.pix.sizeimage < min_sizeimage)
> +		f->fmt.pix.sizeimage = min_sizeimage;

As mentioned in the earlier discussion apps can't set this.

> +
> +	unicam_dbg(1, dev, "width %d, fmt %s, depth %d\n",
> +		   f->fmt.pix.width, fourcc_to_str(f->fmt.pix.pixelformat),
> +		   fmt->depth);
> +	unicam_dbg(3, dev, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n",
> +		   __func__, fourcc_to_str(f->fmt.pix.pixelformat),
> +		   f->fmt.pix.width, f->fmt.pix.height,
> +		   f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
> +
> +	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);
> +	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
> +			    V4L2_CAP_READWRITE;
> +	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;

This has changed. Instead set the device_caps field in struct video_device.
The v4l2 core will then fill in these two fields for you based on the struct
video_device device_caps field.

> +	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;
> +	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

No need to set type.

> +	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;
> +	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.\n",
> +			   f->fmt.pix.pixelformat);
> +
> +		/* Just get the first one enumerated */
> +		fmt = &dev->active_fmts[0];
> +		f->fmt.pix.pixelformat = fmt->fourcc;
> +	}
> +
> +	sd_fmt.which = V4L2_SUBDEV_FORMAT_TRY;
> +	sd_fmt.pad = 0;
> +
> +	v4l2_fill_mbus_format(mbus_fmt, &f->fmt.pix, fmt->code);
> +
> +	ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_config,
> +			       &sd_fmt);
> +	if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
> +		return ret;
> +
> +	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(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;
> +
> +	if (vb2_is_busy(q)) {
> +		unicam_dbg(3, dev, "%s device busy\n", __func__);

debug message seems pointless since the error code indicates this already.

> +		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);
> +	dump_active_formats(dev);
> +
> +	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;
> +	}
> +
> +	v4l2_fill_pix_format(&dev->v_fmt.fmt.pix, &mbus_fmt);
> +	dev->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	dev->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
> +	dev->v_fmt.fmt.pix.bytesperline = f->fmt.pix.bytesperline;
> +	dev->v_fmt.fmt.pix.sizeimage = f->fmt.pix.sizeimage;
> +	unicam_calc_format_size(dev, fmt, &dev->v_fmt);
> +
> +	unicam_dbg(3, dev,
> +		   "%s %dx%d, mbus_fmt %s, V4L2 pix %s. About to overwrite pix with %s\n",
> +		   __func__,
> +		   dev->v_fmt.fmt.pix.width,
> +		   dev->v_fmt.fmt.pix.height,
> +		   fourcc_to_str(mbus_fmt.code),
> +		   fourcc_to_str(dev->v_fmt.fmt.pix.pixelformat),
> +		   fourcc_to_str(fmt->fourcc));
> +
> +	dev->fmt = fmt;
> +	dev->m_fmt = mbus_fmt;
> +	*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_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);
> +	set_field(&val, UNICAM_CPM_CSI2, UNICAM_CPM_MASK);
> +	set_field(&val, UNICAM_DCM_STROBE, 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;
> +	set_field(&val, 1, UNICAM_CLE);
> +	set_field(&val, 1, UNICAM_CLLPE);
> +	reg_write(cfg, UNICAM_CLK, val);
> +
> +	/* Enable required data lanes */
> +	val = 0;
> +	set_field(&val, 1, UNICAM_DLE);
> +	set_field(&val, 1, UNICAM_DLLPE);
> +	reg_write(cfg, UNICAM_DAT0, val);
> +
> +	if (dev->active_data_lanes == 1)
> +		val = 0;
> +	reg_write(cfg, UNICAM_DAT1, val);
> +
> +	if (dev->cfg.periph_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->cfg.periph_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);
> +	if (list_empty(&dma_q->active)) {
> +		spin_unlock_irqrestore(&dev->dma_queue_lock, flags);
> +		unicam_dbg(3, dev, "buffer queue is empty\n");
> +		return -EIO;

This can't happen. This function will only be called if at least 2 buffers
have been queued (the value you set as min_buffers_needed).

> +	}
> +
> +	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 (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 (dev->active_data_lanes > dev->cfg.periph_max_data_lanes) {
> +		unicam_err(dev, "Device has requested %u data lanes, which is >%u supported by peripheral",
> +			   dev->active_data_lanes,
> +			   dev->cfg.periph_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, pad, dv_timings_cap)) {

Use s_dv_timings instead of dv_timings_cap.

> +		inp->capabilities = V4L2_IN_CAP_DV_TIMINGS;
> +		inp->std = 0;
> +	} else if (v4l2_subdev_has_op(dev->sensor, video, querystd)) {

Use s_std instead of querystd. FYI: querystd is not always implemented.

> +		inp->capabilities = V4L2_IN_CAP_STD;
> +		inp->std = V4L2_STD_ALL;

This needs to come from the g_tvnorms op.

> +	} 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)
> +{
> +	struct unicam_device *dev = video_drvdata(file);
> +	*i = dev->input;
> +	return 0;
> +}
> +
> +static int unicam_s_input(struct file *file, void *priv, unsigned int i)
> +{
> +	struct unicam_device *dev = video_drvdata(file);
> +	int ret;
> +
> +	if (v4l2_subdev_has_op(dev->sensor, video, s_routing))
> +		ret =  v4l2_subdev_call(dev->sensor, video, s_routing, i, 0, 0);
> +	else
> +		ret = -EINVAL;	/* v4l2-compliance insists on -EINVAL */

Drop this if-else entirely. s_routing makes really no sense when using a device
tree. In this particular case there really is just one input, period.

> +
> +	/* Must always be able to set input to 0 */
> +	if (!i)
> +		ret = 0;
> +	if (!ret)
> +		dev->input = i;
> +
> +	return ret;
> +}
> +
> +static int unicam_querystd(struct file *file, void *priv,
> +			   v4l2_std_id *std)
> +{
> +	struct unicam_device *dev = video_drvdata(file);
> +	int ret;
> +
> +	ret = v4l2_subdev_call(dev->sensor, video, querystd, std);
> +	return ret;

No need for the local ret variable.

> +}
> +
> +static int unicam_g_std(struct file *file, void *priv, v4l2_std_id *std)
> +{
> +	struct unicam_device *dev = video_drvdata(file);
> +	int ret;
> +
> +	ret = v4l2_subdev_call(dev->sensor, video, g_std, std);
> +	return ret;
> +}
> +
> +static int unicam_s_std(struct file *file, void *priv, v4l2_std_id std)
> +{
> +	struct unicam_device *dev = video_drvdata(file);
> +	int ret;
> +
> +	ret = v4l2_subdev_call(dev->sensor, video, s_std, std);
> +	return ret;
> +}
> +
> +static int unicam_s_edid(struct file *file, void *priv, struct v4l2_edid *edid)
> +{
> +	struct unicam_device *dev = video_drvdata(file);
> +	int ret;
> +
> +	ret =  v4l2_subdev_call(dev->sensor, pad, set_edid, edid);
> +
> +	return ret;
> +}
> +
> +static int unicam_g_edid(struct file *file, void *priv, struct v4l2_edid *edid)
> +{
> +	struct unicam_device *dev = video_drvdata(file);
> +	int ret;
> +
> +	ret =  v4l2_subdev_call(dev->sensor, pad, get_edid, edid);
> +
> +	return ret;
> +}
> +
> +static int unicam_g_dv_timings(struct file *file, void *priv,
> +			       struct v4l2_dv_timings *timings)
> +{
> +	struct unicam_device *dev = video_drvdata(file);
> +	int ret;
> +
> +	ret =  v4l2_subdev_call(dev->sensor, video, g_dv_timings, timings);
> +
> +	return ret;
> +}
> +
> +static int unicam_s_dv_timings(struct file *file, void *priv,
> +			       struct v4l2_dv_timings *timings)
> +{
> +	struct unicam_device *dev = video_drvdata(file);
> +	int ret;
> +
> +	ret =  v4l2_subdev_call(dev->sensor, video, s_dv_timings, timings);
> +	if (ret)
> +		return ret;
> +
> +	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);
> +	int ret;
> +
> +	ret =  v4l2_subdev_call(dev->sensor, video, query_dv_timings, timings);
> +
> +	return ret;
> +}
> +
> +static int unicam_enum_dv_timings(struct file *file, void *priv,
> +				  struct v4l2_enum_dv_timings *timings)
> +{
> +	struct unicam_device *dev = video_drvdata(file);
> +	int ret;
> +
> +	ret =  v4l2_subdev_call(dev->sensor, pad, enum_dv_timings, timings);
> +
> +	return ret;
> +}
> +
> +static int unicam_dv_timings_cap(struct file *file, void *priv,
> +				 struct v4l2_dv_timings_cap *cap)
> +{
> +	struct unicam_device *dev = video_drvdata(file);
> +	int ret;
> +
> +	ret =  v4l2_subdev_call(dev->sensor, pad, dv_timings_cap, cap);
> +
> +	return ret;
> +}
> +
> +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 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		= v4l2_ctrl_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;
> +
> +	unicam_dbg(1, unicam, "unicam_async_bound\n");
> +
> +	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++) {
> +					*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",
> +						   fourcc_to_str(fmt->fourcc),
> +						   fmt->code,
> +						   unicam->num_active_fmt);
> +					unicam->num_active_fmt++;
> +					active_fmt++;
> +				}
> +			} else {
> +				*active_fmt = *fmt;
> +				unicam_dbg(2, unicam,
> +					   "matched fourcc: %s: code: %04x idx: %d\n",
> +					   fourcc_to_str(fmt->fourcc),
> +					   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;
> +	const struct unicam_fmt *fmt;
> +	struct v4l2_mbus_framefmt mbus_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;
> +	}
> +
> +	/* Save current subdev format */
> +	v4l2_fill_pix_format(&unicam->v_fmt.fmt.pix, &mbus_fmt);
> +	unicam->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	unicam->v_fmt.fmt.pix.pixelformat  = fmt->fourcc;
> +	unicam->v_fmt.fmt.pix.bytesperline = 0;
> +	unicam_calc_format_size(unicam, fmt, &unicam->v_fmt);
> +	unicam->fmt = fmt;
> +	unicam->m_fmt = mbus_fmt;
> +
> +	if (v4l2_subdev_has_op(unicam->sensor, video, querystd)) {

Check against s_std

> +		if (v4l2_subdev_has_op(unicam->sensor, video, g_tvnorms)) {
> +			v4l2_std_id tvnorms;
> +
> +			ret = v4l2_subdev_call(unicam->sensor, video,
> +					       g_tvnorms, &tvnorms);
> +			if (ret) {
> +				unicam_dbg(1, unicam, "Sensor supports g_tvnorms, but returned error\n");
> +				unicam->video_dev.tvnorms |= V4L2_STD_ALL;

I recommend a WARN_ON and an error return. This really should not happen.
If it does, then it is a subdev bug.

> +			} else {
> +				unicam->video_dev.tvnorms |= tvnorms;
> +			}
> +		} else {
> +			unicam->video_dev.tvnorms |= V4L2_STD_ALL;

Same here.

> +		}
> +	}

Based on the subdev capabilities (sensor, STD, DV_TIMINGS) you should disable ioctls
by calling v4l2_disable_ioctl. See e.g. drivers/media/platform/vivid/vivid-core.c.

> +
> +	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->dev_debug = 0xff;

Drop this line. Shouldn't be set by drivers.

But you should set vdev->device_caps here.

> +	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;
> +	}
> +
> +	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 struct device_node *
> +of_get_port(const struct device_node *parent)
> +{
> +	struct device_node *port = NULL;
> +
> +	if (!parent)
> +		return NULL;
> +
> +	port = of_get_child_by_name(parent, "port");
> +
> +	return port;
> +}
> +
> +static struct device_node *
> +of_get_next_endpoint(const struct device_node *parent,
> +		     struct device_node *prev)
> +{
> +	struct device_node *ep = NULL;
> +
> +	if (!parent)
> +		return NULL;
> +
> +	do {
> +		ep = of_get_next_child(parent, prev);
> +		if (!ep)
> +			return NULL;
> +		prev = ep;
> +	} while (of_node_cmp(ep->name, "endpoint") != 0);
> +
> +	return ep;
> +}
> +
> +static int of_unicam_connect_subdevs(struct unicam_device *dev)
> +{
> +	struct platform_device *pdev = dev->pdev;
> +	struct device_node *parent, *ep_node = NULL, *port = NULL,
> +			*remote_ep = NULL, *sensor_node = NULL;
> +	struct v4l2_fwnode_endpoint *endpoint;
> +	struct v4l2_async_subdev *asd;
> +	int ret = -EINVAL;
> +	unsigned int lane;
> +	struct v4l2_async_subdev **subdevs = NULL;
> +
> +	parent = pdev->dev.of_node;
> +
> +	asd = &dev->asd;
> +	endpoint = &dev->endpoint;
> +
> +	dev->cfg.periph_max_data_lanes = DEFAULT_MAX_DATA_LANES;
> +	of_property_read_u32(parent, "max_data_lanes",
> +			     &dev->cfg.periph_max_data_lanes);
> +
> +	unicam_dbg(3, dev, "Scanning Port node for csi2 port\n");
> +	port = of_get_port(parent);
> +	if (!port) {
> +		unicam_dbg(1, dev, "No port node found for csi2 port\n");
> +		goto cleanup_exit;
> +	}
> +
> +	if (!port) {
> +		unicam_dbg(1, dev, "No port node matches csi2 port\n");
> +		goto cleanup_exit;
> +	}
> +
> +	unicam_dbg(3, dev, "Scanning sub-device for csi2 port\n");
> +
> +	ep_node = of_get_next_endpoint(port, ep_node);
> +	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);
> +
> +	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_parse_phandle(ep_node, "remote-endpoint", 0);
> +	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), endpoint);
> +	unicam_dbg(3, dev, "parsed remote_ep to endpoint. nr_of_link_frequencies %u, bus_type %u\n",
> +		   endpoint->nr_of_link_frequencies, endpoint->bus_type);
> +
> +	if (endpoint->bus_type != V4L2_MBUS_CSI2) {
> +		/* We can support CCP2 too, but no enum defined for that. */
> +		unicam_err(dev, "sub-device %s is not a CSI2 device %d\n",
> +			   sensor_node->name, endpoint->bus_type);
> +		goto cleanup_exit;
> +	}
> +
> +	/* Store bus type - CSI2 vs CCP2 */
> +	dev->bus_type = endpoint->bus_type;
> +	unicam_dbg(3, dev, "bus_type is %d\n", dev->bus_type);
> +
> +	/* Store Virtual Channel number */
> +	dev->virtual_channel = endpoint->base.id;
> +
> +	/* Store number of data lanes */
> +	dev->max_data_lanes = endpoint->bus.mipi_csi2.num_data_lanes;
> +
> +	if (dev->max_data_lanes > dev->cfg.periph_max_data_lanes) {
> +		/* We can support CCP2 too, but no enum defined for that. */
> +		unicam_err(dev, "Subdevice %s wants too many data lanes (%u > %u)\n",
> +			   sensor_node->name, dev->max_data_lanes,
> +			   dev->cfg.periph_max_data_lanes);
> +		goto cleanup_exit;
> +	}
> +	for (lane = 0; lane < dev->max_data_lanes; lane++) {
> +		if (endpoint->bus.mipi_csi2.data_lanes[lane] != lane + 1) {
> +			unicam_err(dev, "Subdevice %s - incompatible data lane config\n",
> +				   sensor_node->name);
> +			goto cleanup_exit;
> +		}
> +	}
> +
> +	unicam_dbg(3, dev, "v4l2-endpoint: CSI2\n");
> +	unicam_dbg(3, dev, "Virtual Channel=%d\n", dev->virtual_channel);
> +	unicam_dbg(3, dev, "flags=0x%08x\n", endpoint->bus.mipi_csi2.flags);
> +	unicam_dbg(3, dev, "clock_lane=%d\n",
> +		   endpoint->bus.mipi_csi2.clock_lane);
> +	unicam_dbg(3, dev, "num_data_lanes=%d\n",
> +		   endpoint->bus.mipi_csi2.num_data_lanes);
> +	unicam_dbg(3, dev, "data_lanes= <\n");
> +	for (lane = 0; lane < endpoint->bus.mipi_csi2.num_data_lanes; lane++)
> +		unicam_dbg(3, dev, "\t%u\n",
> +			   endpoint->bus.mipi_csi2.data_lanes[lane]);
> +	unicam_dbg(3, dev, "\t>\n");
> +
> +	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);
> +	if (port)
> +		of_node_put(port);
> +
> +	return ret;
> +}
> +
> +/*
> + * unicam_probe : This function creates device entries by register
> + * itself to the V4L2 driver and initializes fields of each
> + * device objects
> + */
> +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 */
> +	/*
> +	 * Currently the subdev with the largest number of controls (13) is
> +	 * ov6550. So let's pick 16 as a hint for the control handler. Note
> +	 * that this is a hint only: too large and you waste some memory, too
> +	 * small and there is a (very) small performance hit when looking up
> +	 * controls in the internal hash.
> +	 */
> +	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;
> +}
> +
> +/*
> + * unicam_remove : It un-register device from V4L2 driver
> + */
> +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..793e42a
> --- /dev/null
> +++ b/drivers/media/platform/bcm2835/vc4-regs-unicam.h
> @@ -0,0 +1,257 @@
> +/*
> + *  Copyright (C) 2017 Raspberry Pi Trading.
> + *
> + * 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.
> + */
> +
> +/*
> + * Modified from brcm_usrlib/dag/vmcsx/vcinclude/hardware_vc4.h to
> + * just have the offset, and use the BIT and GENMASK kernel macros.
> + */
> +
> +#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 Broadcom datasheets */
> +/* 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
> 

Overall this looks pretty good!

Regards,

	Hans

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

* Re: [RFC 0/2] BCM283x Camera Receiver driver
  2017-06-14 21:03       ` Dave Stevenson
@ 2017-06-15  7:17         ` Hans Verkuil
  2017-06-15 12:29           ` Dave Stevenson
  0 siblings, 1 reply; 35+ messages in thread
From: Hans Verkuil @ 2017-06-15  7:17 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

On 06/14/2017 11:03 PM, Dave Stevenson wrote:
> On 14 June 2017 at 18:38, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>> On 06/14/2017 06:29 PM, Dave Stevenson wrote:
>>>
>>> Hi Hans.
>>>
>>> On 14 June 2017 at 16:42, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>>>
>>>> Hi Dave,
>>>>
>>>> How does this driver relate to this staging driver:
>>>>
>>>> drivers/staging/vc04_services/bcm2835-camera/
>>>>
>>>> It's not obvious to me.
>>>
>>>
>>> drivers/staging/vc04_services/bcm2835-camera/ is using the VideoCore
>>> firmware to control Unicam, ISP, and all the tuner algorithms. The ARM
>>> gets delivered fully processed buffers from the VideoCore side. The
>>> firmware only has drivers for the Omnivision OV5647 and Sony IMX219
>>> (and an unsupported one for the Toshiba TC358743).
>>>
>>> This driver is solely the Unicam block, reading the data in over
>>> CSI2/CCP2 from the sensor and writing it to memory. No ISP or control
>>> loops.
>>> Other than power management, this driver is running solely on the ARM
>>> with no involvement from the VideoCore firmware.
>>> The sensor driver is whatever suitable V4L2 subdevice driver you fancy
>>> attaching (as long as it supports CSI2, or eventually CCP2).
>>
>>
>> What is the interaction between these two drivers? Can they co-exist?
>> I would expect them to be mutually exclusive.
> 
> Mutually exclusive for the same Unicam instance, yes.
> 
> There are two Unicam instances on all BCM283x chips and both are
> brought out on the Compute Modules. You could run bcm2835-unicam on
> one and bcm2835-camera on the other if you so wished.
> The firmware checks whether the csi nodes in the device tree are
> active, and won't touch them if they are.

It would be good if this explanation is mentioned both in the driver code
and (I think) in the bindings document of *both* drivers. This setup is
unusual, so some extra documentation isn't amiss.

Regards,

	Hans

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

* Re: [RFC 2/2] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
  2017-06-15  7:12   ` Hans Verkuil
@ 2017-06-15  7:20     ` Stefan Wahren
  2017-06-15 13:38     ` Dave Stevenson
  1 sibling, 0 replies; 35+ messages in thread
From: Stefan Wahren @ 2017-06-15  7:20 UTC (permalink / raw)
  To: Hans Verkuil, Dave Stevenson, linux-media, Mauro Carvalho Chehab,
	linux-rpi-kernel

Am 15.06.2017 um 09:12 schrieb Hans Verkuil:
> Hi Dave,
>
> Here is a quick review of this driver. Once a v2 is posted I'll do a
> more thorough
> check.
>
> On 06/14/2017 05:15 PM, Dave Stevenson wrote:
>> 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                  |    2 +
>>   drivers/media/platform/bcm2835/Kconfig           |   14 +
>>   drivers/media/platform/bcm2835/Makefile          |    3 +
>>   drivers/media/platform/bcm2835/bcm2835-unicam.c  | 2100
>> ++++++++++++++++++++++
>>   drivers/media/platform/bcm2835/vc4-regs-unicam.h |  257 +++
>>   6 files changed, 2377 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 8da521a..1111aa9 100644
>> --- a/drivers/media/platform/Kconfig
>> +++ b/drivers/media/platform/Kconfig
>> @@ -135,6 +135,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 6bbdf94..9c5e412 100644
>> --- a/drivers/media/platform/Makefile
>> +++ b/drivers/media/platform/Makefile
>> @@ -81,3 +81,5 @@ obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC)    += mtk-vcodec/
>>   obj-$(CONFIG_VIDEO_MEDIATEK_MDP)    += mtk-mdp/
>>     obj-$(CONFIG_VIDEO_MEDIATEK_JPEG)    += mtk-jpeg/
>> +
>> +obj-y                    += bcm2835/
>> diff --git a/drivers/media/platform/bcm2835/Kconfig
>> b/drivers/media/platform/bcm2835/Kconfig
>> new file mode 100644
>> index 0000000..9f9be9e
>> --- /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_BCM2708 || ARCH_BCM2709 || ARCH_BCM2835 ||
>> COMPILE_TEST
>

ARCH_BCM2708 and ARCH_BCM2709 isn't available upstream and should be
dropped.

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

* Re: [RFC 0/2] BCM283x Camera Receiver driver
  2017-06-15  7:17         ` Hans Verkuil
@ 2017-06-15 12:29           ` Dave Stevenson
  2017-06-15 12:30             ` Hans Verkuil
  0 siblings, 1 reply; 35+ messages in thread
From: Dave Stevenson @ 2017-06-15 12:29 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

On 15 June 2017 at 08:17, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> On 06/14/2017 11:03 PM, Dave Stevenson wrote:
>>
>> On 14 June 2017 at 18:38, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>>
>>> On 06/14/2017 06:29 PM, Dave Stevenson wrote:
>>>>
>>>>
>>>> Hi Hans.
>>>>
>>>> On 14 June 2017 at 16:42, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>>>>
>>>>>
>>>>> Hi Dave,
>>>>>
>>>>> How does this driver relate to this staging driver:
>>>>>
>>>>> drivers/staging/vc04_services/bcm2835-camera/
>>>>>
>>>>> It's not obvious to me.
>>>>
>>>>
>>>>
>>>> drivers/staging/vc04_services/bcm2835-camera/ is using the VideoCore
>>>> firmware to control Unicam, ISP, and all the tuner algorithms. The ARM
>>>> gets delivered fully processed buffers from the VideoCore side. The
>>>> firmware only has drivers for the Omnivision OV5647 and Sony IMX219
>>>> (and an unsupported one for the Toshiba TC358743).
>>>>
>>>> This driver is solely the Unicam block, reading the data in over
>>>> CSI2/CCP2 from the sensor and writing it to memory. No ISP or control
>>>> loops.
>>>> Other than power management, this driver is running solely on the ARM
>>>> with no involvement from the VideoCore firmware.
>>>> The sensor driver is whatever suitable V4L2 subdevice driver you fancy
>>>> attaching (as long as it supports CSI2, or eventually CCP2).
>>>
>>>
>>>
>>> What is the interaction between these two drivers? Can they co-exist?
>>> I would expect them to be mutually exclusive.
>>
>>
>> Mutually exclusive for the same Unicam instance, yes.
>>
>> There are two Unicam instances on all BCM283x chips and both are
>> brought out on the Compute Modules. You could run bcm2835-unicam on
>> one and bcm2835-camera on the other if you so wished.
>> The firmware checks whether the csi nodes in the device tree are
>> active, and won't touch them if they are.
>
>
> It would be good if this explanation is mentioned both in the driver code
> and (I think) in the bindings document of *both* drivers. This setup is
> unusual, so some extra documentation isn't amiss.

OK, will add it to this driver for V2.
The other driver cleanups are on my to-do list. Would you object
horrendously if I deferred adding the explanation to that driver to
when I'm doing those cleanups?

> Regards,
>
>         Hans

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

* Re: [RFC 0/2] BCM283x Camera Receiver driver
  2017-06-15 12:29           ` Dave Stevenson
@ 2017-06-15 12:30             ` Hans Verkuil
  0 siblings, 0 replies; 35+ messages in thread
From: Hans Verkuil @ 2017-06-15 12:30 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

On 06/15/17 14:29, Dave Stevenson wrote:
> On 15 June 2017 at 08:17, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>> On 06/14/2017 11:03 PM, Dave Stevenson wrote:
>>>
>>> On 14 June 2017 at 18:38, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>>>
>>>> On 06/14/2017 06:29 PM, Dave Stevenson wrote:
>>>>>
>>>>>
>>>>> Hi Hans.
>>>>>
>>>>> On 14 June 2017 at 16:42, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>>>>>
>>>>>>
>>>>>> Hi Dave,
>>>>>>
>>>>>> How does this driver relate to this staging driver:
>>>>>>
>>>>>> drivers/staging/vc04_services/bcm2835-camera/
>>>>>>
>>>>>> It's not obvious to me.
>>>>>
>>>>>
>>>>>
>>>>> drivers/staging/vc04_services/bcm2835-camera/ is using the VideoCore
>>>>> firmware to control Unicam, ISP, and all the tuner algorithms. The ARM
>>>>> gets delivered fully processed buffers from the VideoCore side. The
>>>>> firmware only has drivers for the Omnivision OV5647 and Sony IMX219
>>>>> (and an unsupported one for the Toshiba TC358743).
>>>>>
>>>>> This driver is solely the Unicam block, reading the data in over
>>>>> CSI2/CCP2 from the sensor and writing it to memory. No ISP or control
>>>>> loops.
>>>>> Other than power management, this driver is running solely on the ARM
>>>>> with no involvement from the VideoCore firmware.
>>>>> The sensor driver is whatever suitable V4L2 subdevice driver you fancy
>>>>> attaching (as long as it supports CSI2, or eventually CCP2).
>>>>
>>>>
>>>>
>>>> What is the interaction between these two drivers? Can they co-exist?
>>>> I would expect them to be mutually exclusive.
>>>
>>>
>>> Mutually exclusive for the same Unicam instance, yes.
>>>
>>> There are two Unicam instances on all BCM283x chips and both are
>>> brought out on the Compute Modules. You could run bcm2835-unicam on
>>> one and bcm2835-camera on the other if you so wished.
>>> The firmware checks whether the csi nodes in the device tree are
>>> active, and won't touch them if they are.
>>
>>
>> It would be good if this explanation is mentioned both in the driver code
>> and (I think) in the bindings document of *both* drivers. This setup is
>> unusual, so some extra documentation isn't amiss.
> 
> OK, will add it to this driver for V2.
> The other driver cleanups are on my to-do list. Would you object
> horrendously if I deferred adding the explanation to that driver to
> when I'm doing those cleanups?

That's OK.

Just mention in the cover letter of the v2 patch series that you are planning
to do this.

	Hans

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

* Re: [RFC 1/2] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
  2017-06-15  6:34   ` Stefan Wahren
@ 2017-06-15 12:35     ` Dave Stevenson
  0 siblings, 0 replies; 35+ messages in thread
From: Dave Stevenson @ 2017-06-15 12:35 UTC (permalink / raw)
  To: Stefan Wahren
  Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel, Hans Verkuil

Hi Stefan.
Thanks for taking the time to review this.

On 15 June 2017 at 07:34, Stefan Wahren <stefan.wahren@i2se.com> wrote:
> Hi Dave,
>
> Am 14.06.2017 um 17:15 schrieb 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>
>
> please add the devicetree guys in CC for the binding.

Will do for V2.

>> ---
>>  .../devicetree/bindings/media/bcm2835-unicam.txt   | 76 ++++++++++++++++++++++
>>  1 file changed, 76 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..cc5a451
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>> @@ -0,0 +1,76 @@
>> +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.
>
> It would be nice to add some of your explanations to Hans in this
> document or into the driver.

Will do.

>> +
>> +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.
>> +
>> +Optional properties
>> +===================
>> +- max-data-lanes: the hardware can support varying numbers of clock lanes.
>> +               This value is the maximum number supported by this instance.
>> +               Known values of 2 or 4. Default is 2.
>
> AFAIK, this isn't a common property yet. So possibly a vendor prefix
> must be added.

I think yoiu are correct that it isn't a common property. I was
wanting to cover the situation on the majority of the Pi boards where
Unicam1 has been used which can handle 4 lanes, but only 2 have been
broken out to the camera connector.
Happy to add a vendor prefix in V2.

>> +
>> +
>> +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.
>> +
>> +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>;
>> +
>> +                     endpoint {
>> +                             remote-endpoint = <&tc358743_0>;
>> +
>> +                     };
>> +             };
>> +     };
>> +
>> +     i2c0: i2c@7e205000 {
>> +
>> +             tc358743: tc358743@0f {
>
> Usually the node name should describe the function of the node for example:
>
> tc358743: csi-hdmi-bridge@0f

Will do.

> Best regards
> Stefan
>
>> +                     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>;
>> +                                     clock-lanes = <0>;
>> +                                     data-lanes = <1 2 3 4>;
>> +                                     clock-noncontinuous;
>> +                                     link-frequencies =
>> +                                             /bits/ 64 <297000000>;
>> +                             };
>> +                     };
>> +             };
>> +     };

Thanks
  Dave

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

* Re: [RFC 1/2] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
  2017-06-14 15:15 ` [RFC 1/2] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver Dave Stevenson
  2017-06-15  6:34   ` Stefan Wahren
@ 2017-06-15 12:59   ` Sakari Ailus
  2017-06-15 16:15     ` Dave Stevenson
  1 sibling, 1 reply; 35+ messages in thread
From: Sakari Ailus @ 2017-06-15 12:59 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

Hi Dave,

Thanks for the set!

On Wed, Jun 14, 2017 at 04:15:46PM +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   | 76 ++++++++++++++++++++++
>  1 file changed, 76 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..cc5a451
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
> @@ -0,0 +1,76 @@
> +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.
> +
> +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.
> +
> +Optional properties
> +===================
> +- max-data-lanes: the hardware can support varying numbers of clock lanes.
> +		  This value is the maximum number supported by this instance.
> +		  Known values of 2 or 4. Default is 2.

Please use "data-lanes" endpoint property instead. This is the number of
connected physical lanes and specific to the hardware.

Could you also document which endpoint properties are mandatory and which
ones optional?

> +
> +
> +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.
> +
> +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>;
> +
> +			endpoint {
> +				remote-endpoint = <&tc358743_0>;
> +

Extra newline. Don't you need any other properties here?

> +			};
> +		};
> +	};
> +
> +	i2c0: i2c@7e205000 {
> +
> +		tc358743: tc358743@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>;

This one needs to refer to the endpoint, just as the one in the CSI-2
receiver does.

> +					clock-lanes = <0>;
> +					data-lanes = <1 2 3 4>;
> +					clock-noncontinuous;
> +					link-frequencies =
> +						/bits/ 64 <297000000>;
> +				};
> +			};
> +		};
> +	};

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [RFC 2/2] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
  2017-06-15  7:12   ` Hans Verkuil
  2017-06-15  7:20     ` Stefan Wahren
@ 2017-06-15 13:38     ` Dave Stevenson
  2017-06-15 14:14       ` Hans Verkuil
  2017-06-15 14:49       ` Stefan Wahren
  1 sibling, 2 replies; 35+ messages in thread
From: Dave Stevenson @ 2017-06-15 13:38 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

Hi Hans.

"On 15 June 2017 at 08:12, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> Hi Dave,
>
> Here is a quick review of this driver. Once a v2 is posted I'll do a more
> thorough
> check.

Thank you. I wasn't expecting such a quick response.

> On 06/14/2017 05:15 PM, Dave Stevenson wrote:
>>
>> 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                  |    2 +
>>   drivers/media/platform/bcm2835/Kconfig           |   14 +
>>   drivers/media/platform/bcm2835/Makefile          |    3 +
>>   drivers/media/platform/bcm2835/bcm2835-unicam.c  | 2100
>> ++++++++++++++++++++++
>>   drivers/media/platform/bcm2835/vc4-regs-unicam.h |  257 +++
>>   6 files changed, 2377 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 8da521a..1111aa9 100644
>> --- a/drivers/media/platform/Kconfig
>> +++ b/drivers/media/platform/Kconfig
>> @@ -135,6 +135,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 6bbdf94..9c5e412 100644
>> --- a/drivers/media/platform/Makefile
>> +++ b/drivers/media/platform/Makefile
>> @@ -81,3 +81,5 @@ obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC)   += mtk-vcodec/
>>   obj-$(CONFIG_VIDEO_MEDIATEK_MDP)      += mtk-mdp/
>>     obj-$(CONFIG_VIDEO_MEDIATEK_JPEG)   += mtk-jpeg/
>> +
>> +obj-y                                  += bcm2835/
>> diff --git a/drivers/media/platform/bcm2835/Kconfig
>> b/drivers/media/platform/bcm2835/Kconfig
>> new file mode 100644
>> index 0000000..9f9be9e
>> --- /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_BCM2708 || ARCH_BCM2709 || ARCH_BCM2835 ||
>> COMPILE_TEST
>
>
> So this block is available on other broadcom SoCs as well? Not just the
> 2835?
> Should the description of this Kconfig be adapted?

As Stefan has noted, BCM2708 and BCM2709 are only in the downstream
tree and will be removed.

Techincally this block is also present in a few other Broadcom SoCs
that use VideoCore4 (eg BCM28155), however 283x is the only chip
family that I can support.
There are Broadcom folk supporting 281xx, so I will cc them when I
come to adding an entry in MAINTAINERS (I've already communicated with
Eric, and he was happy to defer this driver off to me. Exact details
to be agreed).

>> +       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..26039da
>> --- /dev/null
>> +++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
>> @@ -0,0 +1,2100 @@
>> +/*
>> + * 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.
>> + *
>> + * 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-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. Max width is therefore determined by
>> + * that 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      ((65536 / 4) - 1)
>> +#define MAX_HEIGHT     MAX_WIDTH
>> +
>> +#define DEFAULT_MAX_DATA_LANES 2
>> +
>> +/*
>> + * 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_RGGB,
>> +               .code           = MEDIA_BUS_FMT_SRGGB16_1X16,
>> +               .depth          = 12,
>> +               .csi_dt         = 0x2c,
>> +       }, {
>> +               .fourcc         = PIX_FMT_ALL_GBRG,
>> +               .code           = MEDIA_BUS_FMT_SGBRG16_1X16,
>> +               .depth          = 12,
>> +               .csi_dt         = 0x2c,
>> +       }, {
>> +               .fourcc         = PIX_FMT_ALL_GBRG,
>> +               .code           = MEDIA_BUS_FMT_SGRBG16_1X16,
>> +               .depth          = 12,
>> +               .csi_dt         = 0x2c,
>> +       }, {
>> +               .fourcc         = PIX_FMT_ALL_GRBG,
>> +               .code           = MEDIA_BUS_FMT_SGRBG16_1X16,
>> +               .depth          = 12,
>> +               .csi_dt         = 0x2c,
>> +       },
>> +};
>> +
>> +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},*/
>
>
> Why is this commented out? Either uncomment, add a proper comment explaining
> why
> or remove it.

I was developing this against the Pi specific tree, and that is still
on 4.9 which didn't have several of the 16 bit Bayer formats. I see
that Sakari has added them (thank you Sakari), so I can uncomment
them.

>> +       {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;
>> +
>> +       unsigned int periph_max_data_lanes;
>> +};
>> +
>> +#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;
>> +       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);
>> +}
>> +
>> +/* Format setup functions */
>> +
>> +static int find_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 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);
>> +}
>> +
>> +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 char *fourcc_to_str(u32 fmt)
>> +{
>> +       static char code[5];
>> +
>> +       code[0] = (unsigned char)(fmt & 0xff);
>> +       code[1] = (unsigned char)((fmt >> 8) & 0xff);
>> +       code[2] = (unsigned char)((fmt >> 16) & 0xff);
>> +       code[3] = (unsigned char)((fmt >> 24) & 0xff);
>> +       code[4] = '\0';
>> +
>> +       return code;
>> +}
>
>
> I understand that there can be two instances of this device, so using a
> static char is
> wrong.
>
> I think this function can be a useful addition to v4l2-common.c/h.
>
> BTW, v4l2-ctl uses this function to convert a fourcc to a string:
>
> std::string fcc2s(unsigned int val)
> {
>         std::string s;
>
>         s += val & 0x7f;
>         s += (val >> 8) & 0x7f;
>         s += (val >> 16) & 0x7f;
>         s += (val >> 24) & 0x7f;
>         if (val & (1 << 31))
>                 s += "-BE";
>         return s;
> }
>
> Bit 31 has special meaning.
>
> Proposal for v4l2-common.h:
>
> #define V4L2_FOURCC_MAX_SIZE 8
> char *v4l2_fourcc2s(u32 fourcc, char *buf);
>
> This function fills in buf and returns buf.

OK, I'll do this as an extra patch.

>> +
>> +static void dump_active_formats(struct unicam_device *dev)
>> +{
>> +       int i;
>> +
>> +       for (i = 0; i < dev->num_active_fmt; i++) {
>> +               unicam_dbg(3, dev, "active_fmt[%d] (%p) is code %04X,
>> fourcc %s, depth %d\n",
>> +                          i, &dev->active_fmts[i],
>> dev->active_fmts[i].code,
>> +                          fourcc_to_str(dev->active_fmts[i].fourcc),
>> +                          dev->active_fmts[i].depth);
>> +       }
>> +}
>> +
>> +static inline int bytes_per_line(u32 width, const struct unicam_fmt *fmt)
>> +{
>> +       /* Stride must be a multiple of 16. */
>> +       return ALIGN((width * fmt->depth) >> 3,  16);
>> +}
>> +
>> +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;
>> +
>> +       *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;
>> +       struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
>> +       int ret;
>> +
>> +       sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
>> +       sd_fmt.pad = 0;
>> +       *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(struct unicam_device *dev,
>> +                                  const struct unicam_fmt *fmt,
>> +                                  struct v4l2_format *f)
>> +{
>> +       int min_bytesperline, min_sizeimage;
>> +
>> +       if (!fmt) {
>> +               unicam_dbg(3, dev, "No unicam_fmt provided!\n");
>> +               return -EINVAL;
>> +       }
>> +
>> +       v4l_bound_align_image(&f->fmt.pix.width, 16, MAX_WIDTH, 2,
>> +                             &f->fmt.pix.height, 16, 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 = ALIGN(f->fmt.pix.bytesperline,
>> 16);
>> +       else
>> +               f->fmt.pix.bytesperline = min_bytesperline;
>> +
>> +       min_sizeimage = f->fmt.pix.height *
>> +                       f->fmt.pix.bytesperline;
>> +       if (f->fmt.pix.sizeimage < min_sizeimage)
>> +               f->fmt.pix.sizeimage = min_sizeimage;
>
>
> As mentioned in the earlier discussion apps can't set this.

Yes, I'll update that.

>> +
>> +       unicam_dbg(1, dev, "width %d, fmt %s, depth %d\n",
>> +                  f->fmt.pix.width,
>> fourcc_to_str(f->fmt.pix.pixelformat),
>> +                  fmt->depth);
>> +       unicam_dbg(3, dev, "%s: fourcc: %s size: %dx%d bpl:%d
>> img_size:%d\n",
>> +                  __func__, fourcc_to_str(f->fmt.pix.pixelformat),
>> +                  f->fmt.pix.width, f->fmt.pix.height,
>> +                  f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
>> +
>> +       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);
>> +       cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
>> +                           V4L2_CAP_READWRITE;
>> +       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
>
>
> This has changed. Instead set the device_caps field in struct video_device.
> The v4l2 core will then fill in these two fields for you based on the struct
> video_device device_caps field.

OK, will adopt the new mechanism.

>> +       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;
>> +       f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>
>
> No need to set type.

Done.

>> +       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;
>> +       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.\n",
>> +                          f->fmt.pix.pixelformat);
>> +
>> +               /* Just get the first one enumerated */
>> +               fmt = &dev->active_fmts[0];
>> +               f->fmt.pix.pixelformat = fmt->fourcc;
>> +       }
>> +
>> +       sd_fmt.which = V4L2_SUBDEV_FORMAT_TRY;
>> +       sd_fmt.pad = 0;
>> +
>> +       v4l2_fill_mbus_format(mbus_fmt, &f->fmt.pix, fmt->code);
>> +
>> +       ret = v4l2_subdev_call(dev->sensor, pad, set_fmt,
>> dev->sensor_config,
>> +                              &sd_fmt);
>> +       if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
>> +               return ret;
>> +
>> +       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(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;
>> +
>> +       if (vb2_is_busy(q)) {
>> +               unicam_dbg(3, dev, "%s device busy\n", __func__);
>
>
> debug message seems pointless since the error code indicates this already.

Done.

>> +               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);
>> +       dump_active_formats(dev);
>> +
>> +       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;
>> +       }
>> +
>> +       v4l2_fill_pix_format(&dev->v_fmt.fmt.pix, &mbus_fmt);
>> +       dev->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>> +       dev->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
>> +       dev->v_fmt.fmt.pix.bytesperline = f->fmt.pix.bytesperline;
>> +       dev->v_fmt.fmt.pix.sizeimage = f->fmt.pix.sizeimage;
>> +       unicam_calc_format_size(dev, fmt, &dev->v_fmt);
>> +
>> +       unicam_dbg(3, dev,
>> +                  "%s %dx%d, mbus_fmt %s, V4L2 pix %s. About to overwrite
>> pix with %s\n",
>> +                  __func__,
>> +                  dev->v_fmt.fmt.pix.width,
>> +                  dev->v_fmt.fmt.pix.height,
>> +                  fourcc_to_str(mbus_fmt.code),
>> +                  fourcc_to_str(dev->v_fmt.fmt.pix.pixelformat),
>> +                  fourcc_to_str(fmt->fourcc));
>> +
>> +       dev->fmt = fmt;
>> +       dev->m_fmt = mbus_fmt;
>> +       *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_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);
>> +       set_field(&val, UNICAM_CPM_CSI2, UNICAM_CPM_MASK);
>> +       set_field(&val, UNICAM_DCM_STROBE, 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;
>> +       set_field(&val, 1, UNICAM_CLE);
>> +       set_field(&val, 1, UNICAM_CLLPE);
>> +       reg_write(cfg, UNICAM_CLK, val);
>> +
>> +       /* Enable required data lanes */
>> +       val = 0;
>> +       set_field(&val, 1, UNICAM_DLE);
>> +       set_field(&val, 1, UNICAM_DLLPE);
>> +       reg_write(cfg, UNICAM_DAT0, val);
>> +
>> +       if (dev->active_data_lanes == 1)
>> +               val = 0;
>> +       reg_write(cfg, UNICAM_DAT1, val);
>> +
>> +       if (dev->cfg.periph_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->cfg.periph_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);
>> +       if (list_empty(&dma_q->active)) {
>> +               spin_unlock_irqrestore(&dev->dma_queue_lock, flags);
>> +               unicam_dbg(3, dev, "buffer queue is empty\n");
>> +               return -EIO;
>
>
> This can't happen. This function will only be called if at least 2 buffers
> have been queued (the value you set as min_buffers_needed).

OK, I'll remove it.

>> +       }
>> +
>> +       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 (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 (dev->active_data_lanes > dev->cfg.periph_max_data_lanes) {
>> +               unicam_err(dev, "Device has requested %u data lanes, which
>> is >%u supported by peripheral",
>> +                          dev->active_data_lanes,
>> +                          dev->cfg.periph_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, pad, dv_timings_cap)) {
>
>
> Use s_dv_timings instead of dv_timings_cap.

Done.

>> +               inp->capabilities = V4L2_IN_CAP_DV_TIMINGS;
>> +               inp->std = 0;
>> +       } else if (v4l2_subdev_has_op(dev->sensor, video, querystd)) {
>
>
> Use s_std instead of querystd. FYI: querystd is not always implemented.

Done.

>> +               inp->capabilities = V4L2_IN_CAP_STD;
>> +               inp->std = V4L2_STD_ALL;
>
>
> This needs to come from the g_tvnorms op.
>
>> +       } 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)
>> +{
>> +       struct unicam_device *dev = video_drvdata(file);
>> +       *i = dev->input;
>> +       return 0;
>> +}
>> +
>> +static int unicam_s_input(struct file *file, void *priv, unsigned int i)
>> +{
>> +       struct unicam_device *dev = video_drvdata(file);
>> +       int ret;
>> +
>> +       if (v4l2_subdev_has_op(dev->sensor, video, s_routing))
>> +               ret =  v4l2_subdev_call(dev->sensor, video, s_routing, i,
>> 0, 0);
>> +       else
>> +               ret = -EINVAL;  /* v4l2-compliance insists on -EINVAL */
>
>
> Drop this if-else entirely. s_routing makes really no sense when using a
> device
> tree. In this particular case there really is just one input, period.

I added this due to the ADV7282-M analogue to CSI bridge chip (uses
adv7180.c driver). It uses s_routing to select the physical input /
input type.
If this is dropped, what is the correct mechanism for selecting the
input? Unless I've missed it, s_routing is not a call that is exposed
to userspace, so we're stuck with composite input 1.

I had asked this question in previously [1], and whilst Sakari had
kindly replied with "s_routing() video op as it stands now is awful, I
hope no-one uses it", the fact is that it is used.

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

>> +
>> +       /* Must always be able to set input to 0 */
>> +       if (!i)
>> +               ret = 0;
>> +       if (!ret)
>> +               dev->input = i;
>> +
>> +       return ret;
>> +}
>> +
>> +static int unicam_querystd(struct file *file, void *priv,
>> +                          v4l2_std_id *std)
>> +{
>> +       struct unicam_device *dev = video_drvdata(file);
>> +       int ret;
>> +
>> +       ret = v4l2_subdev_call(dev->sensor, video, querystd, std);
>> +       return ret;
>
>
> No need for the local ret variable

Done here and in the next few functions.

>> +}
>> +
>> +static int unicam_g_std(struct file *file, void *priv, v4l2_std_id *std)
>> +{
>> +       struct unicam_device *dev = video_drvdata(file);
>> +       int ret;
>> +
>> +       ret = v4l2_subdev_call(dev->sensor, video, g_std, std);
>> +       return ret;
>> +}
>> +
>> +static int unicam_s_std(struct file *file, void *priv, v4l2_std_id std)
>> +{
>> +       struct unicam_device *dev = video_drvdata(file);
>> +       int ret;
>> +
>> +       ret = v4l2_subdev_call(dev->sensor, video, s_std, std);
>> +       return ret;
>> +}
>> +
>> +static int unicam_s_edid(struct file *file, void *priv, struct v4l2_edid
>> *edid)
>> +{
>> +       struct unicam_device *dev = video_drvdata(file);
>> +       int ret;
>> +
>> +       ret =  v4l2_subdev_call(dev->sensor, pad, set_edid, edid);
>> +
>> +       return ret;
>> +}
>> +
>> +static int unicam_g_edid(struct file *file, void *priv, struct v4l2_edid
>> *edid)
>> +{
>> +       struct unicam_device *dev = video_drvdata(file);
>> +       int ret;
>> +
>> +       ret =  v4l2_subdev_call(dev->sensor, pad, get_edid, edid);
>> +
>> +       return ret;
>> +}
>> +
>> +static int unicam_g_dv_timings(struct file *file, void *priv,
>> +                              struct v4l2_dv_timings *timings)
>> +{
>> +       struct unicam_device *dev = video_drvdata(file);
>> +       int ret;
>> +
>> +       ret =  v4l2_subdev_call(dev->sensor, video, g_dv_timings,
>> timings);
>> +
>> +       return ret;
>> +}
>> +
>> +static int unicam_s_dv_timings(struct file *file, void *priv,
>> +                              struct v4l2_dv_timings *timings)
>> +{
>> +       struct unicam_device *dev = video_drvdata(file);
>> +       int ret;
>> +
>> +       ret =  v4l2_subdev_call(dev->sensor, video, s_dv_timings,
>> timings);
>> +       if (ret)
>> +               return ret;
>> +
>> +       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);
>> +       int ret;
>> +
>> +       ret =  v4l2_subdev_call(dev->sensor, video, query_dv_timings,
>> timings);
>> +
>> +       return ret;
>> +}
>> +
>> +static int unicam_enum_dv_timings(struct file *file, void *priv,
>> +                                 struct v4l2_enum_dv_timings *timings)
>> +{
>> +       struct unicam_device *dev = video_drvdata(file);
>> +       int ret;
>> +
>> +       ret =  v4l2_subdev_call(dev->sensor, pad, enum_dv_timings,
>> timings);
>> +
>> +       return ret;
>> +}
>> +
>> +static int unicam_dv_timings_cap(struct file *file, void *priv,
>> +                                struct v4l2_dv_timings_cap *cap)
>> +{
>> +       struct unicam_device *dev = video_drvdata(file);
>> +       int ret;
>> +
>> +       ret =  v4l2_subdev_call(dev->sensor, pad, dv_timings_cap, cap);
>> +
>> +       return ret;
>> +}
>> +
>> +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 void unicam_notify(struct v4l2_subdev *sd,
>> +                         unsigned int notification, void *arg)
>> +{

For some reason your email has got truncated at this point.
Copying from spinics the bits that you commented on, and apologies if
formatting goes wrong.

>>    +    }
>>    +
>>    +    /* Save current subdev format */
>>    +    v4l2_fill_pix_format(&unicam->v_fmt.fmt.pix, &mbus_fmt);
>>    +    unicam->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>    +    unicam->v_fmt.fmt.pix.pixelformat  = fmt->fourcc;
>>    +    unicam->v_fmt.fmt.pix.bytesperline = 0;
>>    +    unicam_calc_format_size(unicam, fmt, &unicam->v_fmt);
>>    +    unicam->fmt = fmt;
>>    +    unicam->m_fmt = mbus_fmt;
>>    +
>>    +    if (v4l2_subdev_has_op(unicam->sensor, video, querystd)) {
>>
>
> Check against s_std

Done.

>>    +        if (v4l2_subdev_has_op(unicam->sensor, video, g_tvnorms)) {
>>    +            v4l2_std_id tvnorms;
>>    +
>>    +            ret = v4l2_subdev_call(unicam->sensor, video,
>>    +                           g_tvnorms, &tvnorms);
>>    +            if (ret) {
>>    +                unicam_dbg(1, unicam, "Sensor supports g_tvnorms, but returned error\n");
>>    +                unicam->video_dev.tvnorms |= V4L2_STD_ALL;
>>
>
> I recommend a WARN_ON and an error return. This really should not happen.
> If it does, then it is a subdev bug.

OK, WARN and return an error.

>>    +            } else {
>>    +                unicam->video_dev.tvnorms |= tvnorms;
>>    +            }
>>    +        } else {
>>    +            unicam->video_dev.tvnorms |= V4L2_STD_ALL;
>>
>
> Same here.

Done

>>    +        }
>>    +    }
>>
>
> Based on the subdev capabilities (sensor, STD, DV_TIMINGS) you should disable ioctls
> by calling v4l2_disable_ioctl. See e.g. drivers/media/platform/vivid/vivid-core.c.

OK, will do.

>>    +    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->dev_debug = 0xff;
>>
> Drop this line. Shouldn't be set by drivers.

Sorry, my debug that got left in :-(

> But you should set vdev->device_caps here.

Done.

>
> Overall this looks pretty good!
>

That's reassuring to hear after having had a fair few battles getting
it to this point.
I'll sort a V2 and try to get it out tomorrow.

  Dave

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

* Re: [RFC 2/2] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
  2017-06-15 13:38     ` Dave Stevenson
@ 2017-06-15 14:14       ` Hans Verkuil
  2017-06-15 15:11         ` Dave Stevenson
  2017-06-15 14:49       ` Stefan Wahren
  1 sibling, 1 reply; 35+ messages in thread
From: Hans Verkuil @ 2017-06-15 14:14 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

On 06/15/17 15:38, Dave Stevenson wrote:
> Hi Hans.
> 
> "On 15 June 2017 at 08:12, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>> Hi Dave,
>>
>> Here is a quick review of this driver. Once a v2 is posted I'll do a more
>> thorough
>> check.
> 
> Thank you. I wasn't expecting such a quick response.
> 
>> On 06/14/2017 05:15 PM, Dave Stevenson wrote:
>>>
>>> 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                  |    2 +
>>>   drivers/media/platform/bcm2835/Kconfig           |   14 +
>>>   drivers/media/platform/bcm2835/Makefile          |    3 +
>>>   drivers/media/platform/bcm2835/bcm2835-unicam.c  | 2100
>>> ++++++++++++++++++++++
>>>   drivers/media/platform/bcm2835/vc4-regs-unicam.h |  257 +++
>>>   6 files changed, 2377 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
>>>
>>> +static int unicam_s_input(struct file *file, void *priv, unsigned int i)
>>> +{
>>> +       struct unicam_device *dev = video_drvdata(file);
>>> +       int ret;
>>> +
>>> +       if (v4l2_subdev_has_op(dev->sensor, video, s_routing))
>>> +               ret =  v4l2_subdev_call(dev->sensor, video, s_routing, i,
>>> 0, 0);
>>> +       else
>>> +               ret = -EINVAL;  /* v4l2-compliance insists on -EINVAL */
>>
>>
>> Drop this if-else entirely. s_routing makes really no sense when using a
>> device
>> tree. In this particular case there really is just one input, period.
> 
> I added this due to the ADV7282-M analogue to CSI bridge chip (uses
> adv7180.c driver). It uses s_routing to select the physical input /
> input type.
> If this is dropped, what is the correct mechanism for selecting the
> input? Unless I've missed it, s_routing is not a call that is exposed
> to userspace, so we're stuck with composite input 1.
> 
> I had asked this question in previously [1], and whilst Sakari had
> kindly replied with "s_routing() video op as it stands now is awful, I
> hope no-one uses it", the fact is that it is used.
> 
> [1] http://www.spinics.net/lists/linux-media/msg115550.html

s_routing was developed for USB and PCI(e) devices and predates the device tree.
Basically USB and PCI drivers will have card definitions where USB/PCI card IDs
are mapped to card descriptions, and that includes information on the various
inputs (composite, S-Video, etc) that are available on the backplane and how those
physical connectors are hooked up to the pins on the video ICs.

The enum/s/g_input ioctls all show the end-user view, i.e. they enumerate the
inputs on the backpanel of the product. The s_routing op was created to map
such inputs to actual pins on the ICs.

For platform devices we would do this in the device tree today, but some of
the necessary bindings are still missing. Specifically those for connectors,
AFAIK those are not yet defined. It's been discussed, but never finalized.

So if this was done correctly you would use the connector endpoints in the
device tree to enumerate the inputs and use how they are connected to the
other blocks as the routing information (i.e. pad number).

I would say that is the advanced course and to do this later.

Do you even have hardware where you can switch between inputs?

Regards,

	Hans

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

* Re: [RFC 2/2] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
  2017-06-15 13:38     ` Dave Stevenson
  2017-06-15 14:14       ` Hans Verkuil
@ 2017-06-15 14:49       ` Stefan Wahren
  2017-06-15 15:40         ` Dave Stevenson
  1 sibling, 1 reply; 35+ messages in thread
From: Stefan Wahren @ 2017-06-15 14:49 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: Hans Verkuil, Mauro Carvalho Chehab, linux-rpi-kernel, linux-media

Hi Dave,

Am 15.06.2017 um 15:38 schrieb Dave Stevenson:
> Hi Hans.
>
> "On 15 June 2017 at 08:12, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>> Hi Dave,
>>
>> Here is a quick review of this driver. Once a v2 is posted I'll do a more
>> thorough
>> check.
> Thank you. I wasn't expecting such a quick response.
>
>> On 06/14/2017 05:15 PM, Dave Stevenson wrote:
>>> ...
>>>
>>> +
>>> +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},*/
>>
>> Why is this commented out? Either uncomment, add a proper comment explaining
>> why
>> or remove it.
> I was developing this against the Pi specific tree, and that is still
> on 4.9 which didn't have several of the 16 bit Bayer formats. I see
> that Sakari has added them (thank you Sakari), so I can uncomment
> them.

does this series work with Linux Mainline (incl. bcm283x dts files)?

In case not, please tell what is missing?

Regards
Stefan

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

* Re: [RFC 2/2] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
  2017-06-15 14:14       ` Hans Verkuil
@ 2017-06-15 15:11         ` Dave Stevenson
  2017-06-16 14:05           ` Hans Verkuil
  0 siblings, 1 reply; 35+ messages in thread
From: Dave Stevenson @ 2017-06-15 15:11 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

On 15 June 2017 at 15:14, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> On 06/15/17 15:38, Dave Stevenson wrote:
>> Hi Hans.
>>
>> "On 15 June 2017 at 08:12, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>> Hi Dave,
>>>
>>> Here is a quick review of this driver. Once a v2 is posted I'll do a more
>>> thorough
>>> check.
>>
>> Thank you. I wasn't expecting such a quick response.
>>
>>> On 06/14/2017 05:15 PM, Dave Stevenson wrote:
>>>>
>>>> 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                  |    2 +
>>>>   drivers/media/platform/bcm2835/Kconfig           |   14 +
>>>>   drivers/media/platform/bcm2835/Makefile          |    3 +
>>>>   drivers/media/platform/bcm2835/bcm2835-unicam.c  | 2100
>>>> ++++++++++++++++++++++
>>>>   drivers/media/platform/bcm2835/vc4-regs-unicam.h |  257 +++
>>>>   6 files changed, 2377 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
>>>>
>>>> +static int unicam_s_input(struct file *file, void *priv, unsigned int i)
>>>> +{
>>>> +       struct unicam_device *dev = video_drvdata(file);
>>>> +       int ret;
>>>> +
>>>> +       if (v4l2_subdev_has_op(dev->sensor, video, s_routing))
>>>> +               ret =  v4l2_subdev_call(dev->sensor, video, s_routing, i,
>>>> 0, 0);
>>>> +       else
>>>> +               ret = -EINVAL;  /* v4l2-compliance insists on -EINVAL */
>>>
>>>
>>> Drop this if-else entirely. s_routing makes really no sense when using a
>>> device
>>> tree. In this particular case there really is just one input, period.
>>
>> I added this due to the ADV7282-M analogue to CSI bridge chip (uses
>> adv7180.c driver). It uses s_routing to select the physical input /
>> input type.
>> If this is dropped, what is the correct mechanism for selecting the
>> input? Unless I've missed it, s_routing is not a call that is exposed
>> to userspace, so we're stuck with composite input 1.
>>
>> I had asked this question in previously [1], and whilst Sakari had
>> kindly replied with "s_routing() video op as it stands now is awful, I
>> hope no-one uses it", the fact is that it is used.
>>
>> [1] http://www.spinics.net/lists/linux-media/msg115550.html
>
> s_routing was developed for USB and PCI(e) devices and predates the device tree.
> Basically USB and PCI drivers will have card definitions where USB/PCI card IDs
> are mapped to card descriptions, and that includes information on the various
> inputs (composite, S-Video, etc) that are available on the backplane and how those
> physical connectors are hooked up to the pins on the video ICs.
>
> The enum/s/g_input ioctls all show the end-user view, i.e. they enumerate the
> inputs on the backpanel of the product. The s_routing op was created to map
> such inputs to actual pins on the ICs.
>
> For platform devices we would do this in the device tree today, but some of
> the necessary bindings are still missing. Specifically those for connectors,
> AFAIK those are not yet defined. It's been discussed, but never finalized.
>
> So if this was done correctly you would use the connector endpoints in the
> device tree to enumerate the inputs and use how they are connected to the
> other blocks as the routing information (i.e. pad number).
>
> I would say that is the advanced course and to do this later.

Certainly the advanced course, but I'm still not seeing how that all
hangs together.

To me that all sounds like stuff that ought to be within the ADV
driver? From my perspective as the CSI-2 receiver I only have one
input.
So how does the application select between those inputs?

Having had a bit of a grep I think the tvp5150 driver is doing what
you're suggesting. However that appears to force you into using the
media controller API. Is that not overkill particularly from an
application perspective?

You also say above that enum/s/g_input is all about switching between
physical connectors, and that's what I'm doing. I'm now getting lost
as to what is intended

> Do you even have hardware where you can switch between inputs?

I have an ADV7282-M eval board [1] sitting in front of me, with the
I2C and CSI-2 output hooked up to a butchered Pi camera board.
6 phono inputs set up as 2 single-ended composite inputs, and 2 pairs
of differentital composite inputs. It can be reconfigured to take
s-video, or component (although it needs a couple of surface mount
resistors changed to do component).
1 CSI-2 (single lane) output going to the Pi.

I haven't got it producing CSI data that the Pi is happy with yet, but
hopefully that is only a matter of time.
The I2C comms is all working.
With the driver as it stands I can use the s_input ioctl to send the
I2C for the relevant input. It was a few weeks ago I last played with
it, but IIRC it did appear to be detecting the incoming video standard
correctly on each of composite 1 and composite 2 (I haven't got
s-video or differential hooked up).

  Dave

[1] http://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/EVAL-ADV7282MEBZ.html

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

* Re: [RFC 2/2] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
  2017-06-15 14:49       ` Stefan Wahren
@ 2017-06-15 15:40         ` Dave Stevenson
  0 siblings, 0 replies; 35+ messages in thread
From: Dave Stevenson @ 2017-06-15 15:40 UTC (permalink / raw)
  To: Stefan Wahren
  Cc: Hans Verkuil, Mauro Carvalho Chehab, linux-rpi-kernel, linux-media

Hi Stefan.

On 15 June 2017 at 15:49, Stefan Wahren <stefan.wahren@i2se.com> wrote:
> Hi Dave,
>
> Am 15.06.2017 um 15:38 schrieb Dave Stevenson:
>> Hi Hans.
>>
>> "On 15 June 2017 at 08:12, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>> Hi Dave,
>>>
>>> Here is a quick review of this driver. Once a v2 is posted I'll do a more
>>> thorough
>>> check.
>> Thank you. I wasn't expecting such a quick response.
>>
>>> On 06/14/2017 05:15 PM, Dave Stevenson wrote:
>>>> ...
>>>>
>>>> +
>>>> +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},*/
>>>
>>> Why is this commented out? Either uncomment, add a proper comment explaining
>>> why
>>> or remove it.
>> I was developing this against the Pi specific tree, and that is still
>> on 4.9 which didn't have several of the 16 bit Bayer formats. I see
>> that Sakari has added them (thank you Sakari), so I can uncomment
>> them.
>
> does this series work with Linux Mainline (incl. bcm283x dts files)?
>
> In case not, please tell what is missing?

I switched about a week or so back onto mainline, partly to pick up
and use the recent fwnode parsing changes within V4L2.
This driver is working with Mainline and bcm283x.dts once the relevant
DT sections are added.

The DT changes aren't in a state to post as a patch set as yet. The
main stumbiling block is that the camera I2C is BSC0 but typically on
GPIOs 28&29 instead of 0&1 (44&45 on Pi3). Swapping to 28&29 would be
a significant change in behaviour so wouldn't be acceptable.
If it can be knocked in to shape then the i2c-mux-pinctrl driver
appears to do what is required to make 0&1 and 28&29 appear as 2
independent I2C buses, but I haven't had the time as yet to finesse
that. It needs some care as 44&45 (needed for the camera on Pi3) are
used for the SMSC9514 clock and audio on Pi2, so the configuration
needs to be sorted per platform. (switching off the 9514 clock results
in no ethernet or USB, so generally means time for a swift reboot -
been there, done that).

The DT diffs I have for running on a Pi2 are:
diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi
b/arch/arm/boot/dts/bcm2835-rpi.dtsi
index a7b5ce1..1f24219 100644
--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi
+++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi
@@ -46,7 +46,7 @@

 &i2c0 {
        pinctrl-names = "default";
-       pinctrl-0 = <&i2c0_gpio0>;
+       pinctrl-0 = <&i2c0_gpio28>;
        status = "okay";
        clock-frequency = <100000>;
 };
@@ -106,3 +106,11 @@
 &dsi1 {
        power-domains = <&power RPI_POWER_DOMAIN_DSI1>;
 };
+
+&csi0 {
+       power-domains = <&power RPI_POWER_DOMAIN_UNICAM0>;
+};
+
+&csi1 {
+       power-domains = <&power RPI_POWER_DOMAIN_UNICAM1>;
+};
diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi
index 561f27d..4c575e4 100644
--- a/arch/arm/boot/dts/bcm283x.dtsi
+++ b/arch/arm/boot/dts/bcm283x.dtsi
@@ -512,6 +512,34 @@
                        status = "disabled";
                };

+               csi0: csi0@7e800000 {
+                       compatible = "brcm,bcm2835-unicam";
+                       reg = <0x7e800000 0x800>,
+                             <0x7e802000 0x4>;
+                       interrupts = <2 6>;
+                       clocks = <&clocks BCM2835_CLOCK_CAM0>;
+                       clock-names = "lp_clock";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       #clock-cells = <1>;
+
+                       status = "disabled";
+               };
+
+               csi1: csi1@7e801000 {
+                       compatible = "brcm,bcm2835-unicam";
+                       reg = <0x7e801000 0x800>,
+                             <0x7e802004 0x4>;
+                       interrupts = <2 7>;
+                       clocks = <&clocks BCM2835_CLOCK_CAM1>;
+                       clock-names = "lp_clock";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       #clock-cells = <1>;
+
+                       status = "disabled";
+               };
+
                i2c1: i2c@7e804000 {
                        compatible = "brcm,bcm2835-i2c";
                        reg = <0x7e804000 0x1000>;

and then an overlay to add the TC358743
diff --git a/arch/arm/boot/dts/overlays/tc358743-overlay.dts
b/arch/arm/boot/dts/overlays/tc358743-overlay.dts
new file mode 100644
index 0000000..b205ca1
--- /dev/null
+++ b/arch/arm/boot/dts/overlays/tc358743-overlay.dts
@@ -0,0 +1,72 @@
+// Definitions for Toshiba TC358743 HDMI to CSI2 bridge on VC I2C bus
+/dts-v1/;
+/plugin/;
+
+/{
+       compatible = "brcm,bcm2708";
+
+       fragment@0 {
+               target = <&i2c_vc>;
+               __overlay__ {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       status = "okay";
+
+                       tc358743@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: endpoint {
+                                               remote-endpoint = <&csi1>;
+                                               clock-lanes = <0>;
+                                               data-lanes = <1 2>;
+                                               clock-noncontinuous;
+                                               link-frequencies =
/bits/ 64 <297000000>;
+                                       };
+                               };
+                       };
+               };
+       };
+
+       fragment@1 {
+               target = <&csi1>;
+               __overlay__ {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       status = "okay";
+
+                       port {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+
+                               endpoint {
+                                       remote-endpoint = <&tc358743>;
+                               };
+                       };
+               };
+       };
+};

Apologies for the formatting - it'll obviously be cleaned up before
submitting as a patch.

  Dave

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

* Re: [RFC 1/2] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
  2017-06-15 12:59   ` Sakari Ailus
@ 2017-06-15 16:15     ` Dave Stevenson
  2017-06-16  9:57       ` Sakari Ailus
  0 siblings, 1 reply; 35+ messages in thread
From: Dave Stevenson @ 2017-06-15 16:15 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

Hi Sakari.

Thanks for the review.

On 15 June 2017 at 13:59, Sakari Ailus <sakari.ailus@iki.fi> wrote:
> Hi Dave,
>
> Thanks for the set!
>
> On Wed, Jun 14, 2017 at 04:15:46PM +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   | 76 ++++++++++++++++++++++
>>  1 file changed, 76 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..cc5a451
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>> @@ -0,0 +1,76 @@
>> +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.
>> +
>> +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.
>> +
>> +Optional properties
>> +===================
>> +- max-data-lanes: the hardware can support varying numbers of clock lanes.
>> +               This value is the maximum number supported by this instance.
>> +               Known values of 2 or 4. Default is 2.
>
> Please use "data-lanes" endpoint property instead. This is the number of
> connected physical lanes and specific to the hardware.

I'll rethink/test, but to explain what I was intending to achieve:

Registers UNICAM_DAT2 and UNICAM_DAT3 are not valid for instances of
the hardware that only have two lanes instantiated in silicon.
In the case of the whole BCM283x family, Unicam0 ony has 2 lanes
instantiated, whilst Unicam1 has the maximum 4 lanes. (Lower
resolution front cameras would connect to Unicam0, whilst the higher
resolution back cameras would go to Unicam1).

To further confuse matters, on the Pi platforms (other than the
Compute Module), it is Unicam1 that is brought out to the camera
connector but with only 2 lanes wired up.

I was intending to make it possible for the driver to avoid writing to
invalid registers, and also describe the platform limitations to allow
sanity checking.
I haven't tested against Unicam0 as yet to see what actually happens
if we try to write UNICAM_DAT2 or UNICAM_DAT3. If the hardware
silently swallows it then that requirement is null and void. I'll do
some testing tomorrow.
The second bit comes down to how friendly an error you want should
someone write an invalid DT with 'data-lanes' greater than can be
supported by the platform.

> Could you also document which endpoint properties are mandatory and which
> ones optional?

Will do, although I'm not sure there are any required properties.

>> +
>> +
>> +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.
>> +
>> +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>;
>> +
>> +                     endpoint {
>> +                             remote-endpoint = <&tc358743_0>;
>> +
>
> Extra newline. Don't you need any other properties here?

Newline done.
As above, I don't believe there are any other properties required, but
will double check. What extras were you expecting to see there?

>> +                     };
>> +             };
>> +     };
>> +
>> +     i2c0: i2c@7e205000 {
>> +
>> +             tc358743: tc358743@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>;
>
> This one needs to refer to the endpoint, just as the one in the CSI-2
> receiver does.

OK.
(I'm suspecting very few drivers actually follow that link, but I'll correct).

>> +                                     clock-lanes = <0>;
>> +                                     data-lanes = <1 2 3 4>;

Oops, DT author beware! That should only have 2 lanes defined (not
that the TC358743 driver looks at it beyond checking it is non-zero).

>> +                                     clock-noncontinuous;
>> +                                     link-frequencies =
>> +                                             /bits/ 64 <297000000>;
>> +                             };
>> +                     };
>> +             };
>> +     };
>
> --
> Kind regards,
>
> Sakari Ailus
> e-mail: sakari.ailus@iki.fi     XMPP: sailus@retiisi.org.uk

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

* Re: [RFC 1/2] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
  2017-06-15 16:15     ` Dave Stevenson
@ 2017-06-16  9:57       ` Sakari Ailus
  2017-06-16 14:40         ` Dave Stevenson
  0 siblings, 1 reply; 35+ messages in thread
From: Sakari Ailus @ 2017-06-16  9:57 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

Hi Dave,

On Thu, Jun 15, 2017 at 05:15:04PM +0100, Dave Stevenson wrote:
> Hi Sakari.
> 
> Thanks for the review.
> 
> On 15 June 2017 at 13:59, Sakari Ailus <sakari.ailus@iki.fi> wrote:
> > Hi Dave,
> >
> > Thanks for the set!
> >
> > On Wed, Jun 14, 2017 at 04:15:46PM +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   | 76 ++++++++++++++++++++++
> >>  1 file changed, 76 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..cc5a451
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
> >> @@ -0,0 +1,76 @@
> >> +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.
> >> +
> >> +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.
> >> +
> >> +Optional properties
> >> +===================
> >> +- max-data-lanes: the hardware can support varying numbers of clock lanes.
> >> +               This value is the maximum number supported by this instance.
> >> +               Known values of 2 or 4. Default is 2.
> >
> > Please use "data-lanes" endpoint property instead. This is the number of
> > connected physical lanes and specific to the hardware.
> 
> I'll rethink/test, but to explain what I was intending to achieve:
> 
> Registers UNICAM_DAT2 and UNICAM_DAT3 are not valid for instances of
> the hardware that only have two lanes instantiated in silicon.
> In the case of the whole BCM283x family, Unicam0 ony has 2 lanes
> instantiated, whilst Unicam1 has the maximum 4 lanes. (Lower
> resolution front cameras would connect to Unicam0, whilst the higher
> resolution back cameras would go to Unicam1).
> 
> To further confuse matters, on the Pi platforms (other than the
> Compute Module), it is Unicam1 that is brought out to the camera
> connector but with only 2 lanes wired up.

This information should be present in the DT. I.e. the data-lanes property.

v4l2_fwnode_endpoint_alloc_parse() can obtain that from DT, among other
things (I haven't checked the second patch yet).

> 
> I was intending to make it possible for the driver to avoid writing to
> invalid registers, and also describe the platform limitations to allow
> sanity checking.
> I haven't tested against Unicam0 as yet to see what actually happens
> if we try to write UNICAM_DAT2 or UNICAM_DAT3. If the hardware
> silently swallows it then that requirement is null and void. I'll do
> some testing tomorrow.
> The second bit comes down to how friendly an error you want should
> someone write an invalid DT with 'data-lanes' greater than can be
> supported by the platform.

I'd expect things not to work if there's a grave error in DT. I guess you
could assume something but the fact is that you know you don't know.

> 
> > Could you also document which endpoint properties are mandatory and which
> > ones optional?
> 
> Will do, although I'm not sure there are any required properties.

data-lanes, among other things?

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [RFC 2/2] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
  2017-06-15 15:11         ` Dave Stevenson
@ 2017-06-16 14:05           ` Hans Verkuil
  2017-06-16 15:55             ` Dave Stevenson
  0 siblings, 1 reply; 35+ messages in thread
From: Hans Verkuil @ 2017-06-16 14:05 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

On 06/15/17 17:11, Dave Stevenson wrote:
> On 15 June 2017 at 15:14, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>> On 06/15/17 15:38, Dave Stevenson wrote:
>>> Hi Hans.
>>>
>>> "On 15 June 2017 at 08:12, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>>> Hi Dave,
>>>>
>>>> Here is a quick review of this driver. Once a v2 is posted I'll do a more
>>>> thorough
>>>> check.
>>>
>>> Thank you. I wasn't expecting such a quick response.
>>>
>>>> On 06/14/2017 05:15 PM, Dave Stevenson wrote:
>>>>>
>>>>> 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                  |    2 +
>>>>>   drivers/media/platform/bcm2835/Kconfig           |   14 +
>>>>>   drivers/media/platform/bcm2835/Makefile          |    3 +
>>>>>   drivers/media/platform/bcm2835/bcm2835-unicam.c  | 2100
>>>>> ++++++++++++++++++++++
>>>>>   drivers/media/platform/bcm2835/vc4-regs-unicam.h |  257 +++
>>>>>   6 files changed, 2377 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
>>>>>
>>>>> +static int unicam_s_input(struct file *file, void *priv, unsigned int i)
>>>>> +{
>>>>> +       struct unicam_device *dev = video_drvdata(file);
>>>>> +       int ret;
>>>>> +
>>>>> +       if (v4l2_subdev_has_op(dev->sensor, video, s_routing))
>>>>> +               ret =  v4l2_subdev_call(dev->sensor, video, s_routing, i,
>>>>> 0, 0);
>>>>> +       else
>>>>> +               ret = -EINVAL;  /* v4l2-compliance insists on -EINVAL */
>>>>
>>>>
>>>> Drop this if-else entirely. s_routing makes really no sense when using a
>>>> device
>>>> tree. In this particular case there really is just one input, period.
>>>
>>> I added this due to the ADV7282-M analogue to CSI bridge chip (uses
>>> adv7180.c driver). It uses s_routing to select the physical input /
>>> input type.
>>> If this is dropped, what is the correct mechanism for selecting the
>>> input? Unless I've missed it, s_routing is not a call that is exposed
>>> to userspace, so we're stuck with composite input 1.
>>>
>>> I had asked this question in previously [1], and whilst Sakari had
>>> kindly replied with "s_routing() video op as it stands now is awful, I
>>> hope no-one uses it", the fact is that it is used.
>>>
>>> [1] http://www.spinics.net/lists/linux-media/msg115550.html
>>
>> s_routing was developed for USB and PCI(e) devices and predates the device tree.
>> Basically USB and PCI drivers will have card definitions where USB/PCI card IDs
>> are mapped to card descriptions, and that includes information on the various
>> inputs (composite, S-Video, etc) that are available on the backplane and how those
>> physical connectors are hooked up to the pins on the video ICs.
>>
>> The enum/s/g_input ioctls all show the end-user view, i.e. they enumerate the
>> inputs on the backpanel of the product. The s_routing op was created to map
>> such inputs to actual pins on the ICs.
>>
>> For platform devices we would do this in the device tree today, but some of
>> the necessary bindings are still missing. Specifically those for connectors,
>> AFAIK those are not yet defined. It's been discussed, but never finalized.
>>
>> So if this was done correctly you would use the connector endpoints in the
>> device tree to enumerate the inputs and use how they are connected to the
>> other blocks as the routing information (i.e. pad number).
>>
>> I would say that is the advanced course and to do this later.
> 
> Certainly the advanced course, but I'm still not seeing how that all
> hangs together.
> 
> To me that all sounds like stuff that ought to be within the ADV
> driver? From my perspective as the CSI-2 receiver I only have one
> input.

As a csi receiver, yes. But the adv has a mux (or at least the adv7180
does) where it can switch between multiple Composite or S-Video inputs.
s_routing controls the mux.

But the end-user knows nothing about the internal routing, he only knows
about the connectors on the board. So with VIDIOC_ENUMINPUTS you can
see which inputs there are to choose from with names corresponding to
the labels on the backplane or in the user manual. With VIDIOC_S_INPUT
he can select an input, and the device tree then has to provide the
necessary information on how an input connector is hooked up to the adv.

> So how does the application select between those inputs?
> 
> Having had a bit of a grep I think the tvp5150 driver is doing what
> you're suggesting. However that appears to force you into using the
> media controller API. Is that not overkill particularly from an
> application perspective?

Ah, nice. There are actually already connector bindings. I'd forgotten
those went in.

Anyway, the tvp5150 is used in two situations:

either as part of the USB non-MC device, or as part of a platform MC device.

You have a platform non-MC device. The tvp5150 doesn't support that, but
if it did, then the DT parsing could wouldn't be under CONFIG_MEDIA_CONTROLLER.

It should really be under CONFIG_OF since it is (mostly) unrelated to the MC.

But we're missing support for this scenario. For USB/PCI boards we have
card descriptions whose information is used in s_routing, for MC-devices
we leave it up to the user to set the routing (tvp5150_link_setup). But
we don't have anything for a non-MC device that use the device tree.

All the information is there, but we're missing infrastructure to
give the connector information to the main V4L2 driver so it can implement
the input ioctls, and to tell the subdev driver how to program the mux.

> 
> You also say above that enum/s/g_input is all about switching between
> physical connectors, and that's what I'm doing. I'm now getting lost
> as to what is intended

Physical connectors as in connectors that users related to: Composite
inputs, S-Video, HDMI, etc. Not pins on a chip. How those connectors
are routed to pins on a chip is something that differs per board, so
that information belongs in the device tree.

I hope this clarifies it a bit.

Regards,

	Hans

> 
>> Do you even have hardware where you can switch between inputs?
> 
> I have an ADV7282-M eval board [1] sitting in front of me, with the
> I2C and CSI-2 output hooked up to a butchered Pi camera board.
> 6 phono inputs set up as 2 single-ended composite inputs, and 2 pairs
> of differentital composite inputs. It can be reconfigured to take
> s-video, or component (although it needs a couple of surface mount
> resistors changed to do component).
> 1 CSI-2 (single lane) output going to the Pi.
> 
> I haven't got it producing CSI data that the Pi is happy with yet, but
> hopefully that is only a matter of time.
> The I2C comms is all working.
> With the driver as it stands I can use the s_input ioctl to send the
> I2C for the relevant input. It was a few weeks ago I last played with
> it, but IIRC it did appear to be detecting the incoming video standard
> correctly on each of composite 1 and composite 2 (I haven't got
> s-video or differential hooked up).
> 
>   Dave
> 
> [1] http://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/EVAL-ADV7282MEBZ.html
> 

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

* Re: [RFC 1/2] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
  2017-06-16  9:57       ` Sakari Ailus
@ 2017-06-16 14:40         ` Dave Stevenson
  2017-06-16 20:57           ` Sakari Ailus
  0 siblings, 1 reply; 35+ messages in thread
From: Dave Stevenson @ 2017-06-16 14:40 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

Hi Sakari

On 16 June 2017 at 10:57, Sakari Ailus <sakari.ailus@iki.fi> wrote:
> Hi Dave,
>
> On Thu, Jun 15, 2017 at 05:15:04PM +0100, Dave Stevenson wrote:
>> Hi Sakari.
>>
>> Thanks for the review.
>>
>> On 15 June 2017 at 13:59, Sakari Ailus <sakari.ailus@iki.fi> wrote:
>> > Hi Dave,
>> >
>> > Thanks for the set!
>> >
>> > On Wed, Jun 14, 2017 at 04:15:46PM +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   | 76 ++++++++++++++++++++++
>> >>  1 file changed, 76 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..cc5a451
>> >> --- /dev/null
>> >> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
>> >> @@ -0,0 +1,76 @@
>> >> +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.
>> >> +
>> >> +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.
>> >> +
>> >> +Optional properties
>> >> +===================
>> >> +- max-data-lanes: the hardware can support varying numbers of clock lanes.
>> >> +               This value is the maximum number supported by this instance.
>> >> +               Known values of 2 or 4. Default is 2.
>> >
>> > Please use "data-lanes" endpoint property instead. This is the number of
>> > connected physical lanes and specific to the hardware.
>>
>> I'll rethink/test, but to explain what I was intending to achieve:
>>
>> Registers UNICAM_DAT2 and UNICAM_DAT3 are not valid for instances of
>> the hardware that only have two lanes instantiated in silicon.
>> In the case of the whole BCM283x family, Unicam0 ony has 2 lanes
>> instantiated, whilst Unicam1 has the maximum 4 lanes. (Lower
>> resolution front cameras would connect to Unicam0, whilst the higher
>> resolution back cameras would go to Unicam1).
>>
>> To further confuse matters, on the Pi platforms (other than the
>> Compute Module), it is Unicam1 that is brought out to the camera
>> connector but with only 2 lanes wired up.
>
> This information should be present in the DT. I.e. the data-lanes property.
>
> v4l2_fwnode_endpoint_alloc_parse() can obtain that from DT, among other
> things (I haven't checked the second patch yet).

I was interpreting data-lanes as being a function of the CSI-2 source
only, not the CSI-2 sink.
I haven't seen any examples where it has been it has been set on a
sink, but if that is valid then it sounds like a sensible thing to do.
...
OK, I'd missed it in video_interfaces.txt where it has been set for
the sh-mobile-csi2 endpoint (not that there appears to be a driver
advertising sh-mobile-csi2 as a compatible string anymore - it was
removed in 4.9).

It sounds like adopting a sink endpoint property of "data-lanes"  is
reasonable. Make it optional with the driver adopting a default "<1
2>" and you'll cover 99% of the cases.
On Compute Modules it can be overridden to "<1 2 3 4>" for Unicam1.
Selecting more lanes on Unicam0 is just an error that the DT author
has to sort out.

>>
>> I was intending to make it possible for the driver to avoid writing to
>> invalid registers, and also describe the platform limitations to allow
>> sanity checking.
>> I haven't tested against Unicam0 as yet to see what actually happens
>> if we try to write UNICAM_DAT2 or UNICAM_DAT3. If the hardware
>> silently swallows it then that requirement is null and void. I'll do
>> some testing tomorrow.
>> The second bit comes down to how friendly an error you want should
>> someone write an invalid DT with 'data-lanes' greater than can be
>> supported by the platform.
>
> I'd expect things not to work if there's a grave error in DT. I guess you
> could assume something but the fact is that you know you don't know.
>>
>> > Could you also document which endpoint properties are mandatory and which
>> > ones optional?
>>
>> Will do, although I'm not sure there are any required properties.
>
> data-lanes, among other things?

Looking through struct v4l2_fwnode_bus_mipi_csi2 for the parsed result
of v4l2_fwnode_endpoint_parse_csi_bus:
- V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK  /
V4L2_MBUS_CSI2_CONTINUOUS_CLOCK are the only flags set. The clock is
generated by the CSI2 source, so not relevant to configure it on the
sink.
- data_lane has been discussed above. Lane reordering is not supported
on the hardware so only the number of entries is of any importance,
although consecutive lanes can be checked. If a default is adopted by
the driver, then it is not mandatory.
- the clock lane is on a dedicated pair of pins, so it will always be
<0>. Little point in checking it.
- num_data_lanes comes out of data-lane. Let the driver adopt a default.
- lane polarity inversion is not supported by the hardware, so it will
always be an array of 0's. Little point in checking.

"data-lanes" can be mandatory if you think that is of any benefit, but
personally I don't see it. The other properties give no benefit, so I
think I am correct with only remote-endpoint being present.

Thanks,
  Dave

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

* Re: [RFC 2/2] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
  2017-06-16 14:05           ` Hans Verkuil
@ 2017-06-16 15:55             ` Dave Stevenson
  2017-06-16 16:08               ` Hans Verkuil
  0 siblings, 1 reply; 35+ messages in thread
From: Dave Stevenson @ 2017-06-16 15:55 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

On 16 June 2017 at 15:05, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> On 06/15/17 17:11, Dave Stevenson wrote:
>> On 15 June 2017 at 15:14, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>> On 06/15/17 15:38, Dave Stevenson wrote:
>>>> Hi Hans.
>>>>
>>>> "On 15 June 2017 at 08:12, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>>>> Hi Dave,
>>>>>
>>>>> Here is a quick review of this driver. Once a v2 is posted I'll do a more
>>>>> thorough
>>>>> check.
>>>>
>>>> Thank you. I wasn't expecting such a quick response.
>>>>
>>>>> On 06/14/2017 05:15 PM, Dave Stevenson wrote:
>>>>>>
>>>>>> 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                  |    2 +
>>>>>>   drivers/media/platform/bcm2835/Kconfig           |   14 +
>>>>>>   drivers/media/platform/bcm2835/Makefile          |    3 +
>>>>>>   drivers/media/platform/bcm2835/bcm2835-unicam.c  | 2100
>>>>>> ++++++++++++++++++++++
>>>>>>   drivers/media/platform/bcm2835/vc4-regs-unicam.h |  257 +++
>>>>>>   6 files changed, 2377 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
>>>>>>
>>>>>> +static int unicam_s_input(struct file *file, void *priv, unsigned int i)
>>>>>> +{
>>>>>> +       struct unicam_device *dev = video_drvdata(file);
>>>>>> +       int ret;
>>>>>> +
>>>>>> +       if (v4l2_subdev_has_op(dev->sensor, video, s_routing))
>>>>>> +               ret =  v4l2_subdev_call(dev->sensor, video, s_routing, i,
>>>>>> 0, 0);
>>>>>> +       else
>>>>>> +               ret = -EINVAL;  /* v4l2-compliance insists on -EINVAL */
>>>>>
>>>>>
>>>>> Drop this if-else entirely. s_routing makes really no sense when using a
>>>>> device
>>>>> tree. In this particular case there really is just one input, period.
>>>>
>>>> I added this due to the ADV7282-M analogue to CSI bridge chip (uses
>>>> adv7180.c driver). It uses s_routing to select the physical input /
>>>> input type.
>>>> If this is dropped, what is the correct mechanism for selecting the
>>>> input? Unless I've missed it, s_routing is not a call that is exposed
>>>> to userspace, so we're stuck with composite input 1.
>>>>
>>>> I had asked this question in previously [1], and whilst Sakari had
>>>> kindly replied with "s_routing() video op as it stands now is awful, I
>>>> hope no-one uses it", the fact is that it is used.
>>>>
>>>> [1] http://www.spinics.net/lists/linux-media/msg115550.html
>>>
>>> s_routing was developed for USB and PCI(e) devices and predates the device tree.
>>> Basically USB and PCI drivers will have card definitions where USB/PCI card IDs
>>> are mapped to card descriptions, and that includes information on the various
>>> inputs (composite, S-Video, etc) that are available on the backplane and how those
>>> physical connectors are hooked up to the pins on the video ICs.
>>>
>>> The enum/s/g_input ioctls all show the end-user view, i.e. they enumerate the
>>> inputs on the backpanel of the product. The s_routing op was created to map
>>> such inputs to actual pins on the ICs.
>>>
>>> For platform devices we would do this in the device tree today, but some of
>>> the necessary bindings are still missing. Specifically those for connectors,
>>> AFAIK those are not yet defined. It's been discussed, but never finalized.
>>>
>>> So if this was done correctly you would use the connector endpoints in the
>>> device tree to enumerate the inputs and use how they are connected to the
>>> other blocks as the routing information (i.e. pad number).
>>>
>>> I would say that is the advanced course and to do this later.
>>
>> Certainly the advanced course, but I'm still not seeing how that all
>> hangs together.
>>
>> To me that all sounds like stuff that ought to be within the ADV
>> driver? From my perspective as the CSI-2 receiver I only have one
>> input.
>
> As a csi receiver, yes. But the adv has a mux (or at least the adv7180
> does) where it can switch between multiple Composite or S-Video inputs.
> s_routing controls the mux.
>
> But the end-user knows nothing about the internal routing, he only knows
> about the connectors on the board. So with VIDIOC_ENUMINPUTS you can
> see which inputs there are to choose from with names corresponding to
> the labels on the backplane or in the user manual. With VIDIOC_S_INPUT
> he can select an input, and the device tree then has to provide the
> necessary information on how an input connector is hooked up to the adv.
>
>> So how does the application select between those inputs?
>>
>> Having had a bit of a grep I think the tvp5150 driver is doing what
>> you're suggesting. However that appears to force you into using the
>> media controller API. Is that not overkill particularly from an
>> application perspective?
>
> Ah, nice. There are actually already connector bindings. I'd forgotten
> those went in.
>
> Anyway, the tvp5150 is used in two situations:
>
> either as part of the USB non-MC device, or as part of a platform MC device.
>
> You have a platform non-MC device. The tvp5150 doesn't support that, but
> if it did, then the DT parsing could wouldn't be under CONFIG_MEDIA_CONTROLLER.
>
> It should really be under CONFIG_OF since it is (mostly) unrelated to the MC.
>
> But we're missing support for this scenario. For USB/PCI boards we have
> card descriptions whose information is used in s_routing, for MC-devices
> we leave it up to the user to set the routing (tvp5150_link_setup). But
> we don't have anything for a non-MC device that use the device tree.
>
> All the information is there, but we're missing infrastructure to
> give the connector information to the main V4L2 driver so it can implement
> the input ioctls, and to tell the subdev driver how to program the mux.

That's the bit that I was stumbling over - there is a bit of
infrastructure missing.

So I'm reading that as the adv driver is incorrect in that it is just
exposing all the potential input configurations. It really needs the
DT connectors bit adding to refine the input selection down to the
actually available connectors on the physical board.
In my eval board case, DT should describe input connectors for 4
inputs, corresponding to CVBS_AIN1, CVBS_AIN2, DIFF_CVBS_AIN3_AIN4,
and DIFF_CVBS_AIN7_AIN8. The fact that I could externally connect
adapters to combine AIN1 and AIN2 to become s-video isn't relevant (or
at least secondary).

If we have a bit of infrastructure missing, what is the correct way
forward for this driver right now?
I was comparing against the am437x-vfpe, davinci/vpif_capture, and
blackfin/bfin_capture drivers. All of them in some shape or form take
VIDIOC_S_INPUT and end up calling s_routing. It may be incorrect, but
that is how several platforms are using it. Is it acceptable to adopt
this incorrect behaviour until the infrastructure changes are all
done?

As a relative mainline kernel newbie I'm slightly reluctant to
volunteer to implement the missing infrastructure, but with some
guidance in advance I'm willing to put together a patchset for
discussion, and adapt the adv driver to use DT connectors.

Then again the dtbindings for connectors in tvp5150 got reverted
although the driver didn't:
31e717d [media] Revert "[media] tvp5150: document input connectors DT bindings"
   "There are still ongoing discussions about how the input connectors
    will be supported by the Media Controller framework so until that
    is settled, it is better to revert the connectors portion of the
    bindings to avoid known to be broken bindings docs to hit mainline."

Did things ever get settled [1], or did it slip through the cracks?
There don't appear to be any input connector bindings described
anywhere I can find them.
This sounds like a topic to be discussed in a new thread rather than
hidden within a patch review.

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

>>
>> You also say above that enum/s/g_input is all about switching between
>> physical connectors, and that's what I'm doing. I'm now getting lost
>> as to what is intended
>
> Physical connectors as in connectors that users related to: Composite
> inputs, S-Video, HDMI, etc. Not pins on a chip. How those connectors
> are routed to pins on a chip is something that differs per board, so
> that information belongs in the device tree.
>
> I hope this clarifies it a bit.

The waters are still a little murky, but getting clearer. Thanks for
taking the time to try and clarify it.

I've been distracted by other things today, and combined with these
ongoing discissions I'm afraid V2 is going to be delayed until Monday
or Tuesday. Sorry about that.

Cheers.
  Dave

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

* Re: [RFC 2/2] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
  2017-06-16 15:55             ` Dave Stevenson
@ 2017-06-16 16:08               ` Hans Verkuil
  2017-06-16 16:47                 ` Dave Stevenson
  0 siblings, 1 reply; 35+ messages in thread
From: Hans Verkuil @ 2017-06-16 16:08 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

On 06/16/2017 05:55 PM, Dave Stevenson wrote:
> On 16 June 2017 at 15:05, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>> On 06/15/17 17:11, Dave Stevenson wrote:
>>> On 15 June 2017 at 15:14, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>>> On 06/15/17 15:38, Dave Stevenson wrote:
>>>>> Hi Hans.
>>>>>
>>>>> "On 15 June 2017 at 08:12, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>>>>> Hi Dave,
>>>>>>
>>>>>> Here is a quick review of this driver. Once a v2 is posted I'll do a more
>>>>>> thorough
>>>>>> check.
>>>>>
>>>>> Thank you. I wasn't expecting such a quick response.
>>>>>
>>>>>> On 06/14/2017 05:15 PM, Dave Stevenson wrote:
>>>>>>>
>>>>>>> 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                  |    2 +
>>>>>>>    drivers/media/platform/bcm2835/Kconfig           |   14 +
>>>>>>>    drivers/media/platform/bcm2835/Makefile          |    3 +
>>>>>>>    drivers/media/platform/bcm2835/bcm2835-unicam.c  | 2100
>>>>>>> ++++++++++++++++++++++
>>>>>>>    drivers/media/platform/bcm2835/vc4-regs-unicam.h |  257 +++
>>>>>>>    6 files changed, 2377 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
>>>>>>>
>>>>>>> +static int unicam_s_input(struct file *file, void *priv, unsigned int i)
>>>>>>> +{
>>>>>>> +       struct unicam_device *dev = video_drvdata(file);
>>>>>>> +       int ret;
>>>>>>> +
>>>>>>> +       if (v4l2_subdev_has_op(dev->sensor, video, s_routing))
>>>>>>> +               ret =  v4l2_subdev_call(dev->sensor, video, s_routing, i,
>>>>>>> 0, 0);
>>>>>>> +       else
>>>>>>> +               ret = -EINVAL;  /* v4l2-compliance insists on -EINVAL */
>>>>>>
>>>>>>
>>>>>> Drop this if-else entirely. s_routing makes really no sense when using a
>>>>>> device
>>>>>> tree. In this particular case there really is just one input, period.
>>>>>
>>>>> I added this due to the ADV7282-M analogue to CSI bridge chip (uses
>>>>> adv7180.c driver). It uses s_routing to select the physical input /
>>>>> input type.
>>>>> If this is dropped, what is the correct mechanism for selecting the
>>>>> input? Unless I've missed it, s_routing is not a call that is exposed
>>>>> to userspace, so we're stuck with composite input 1.
>>>>>
>>>>> I had asked this question in previously [1], and whilst Sakari had
>>>>> kindly replied with "s_routing() video op as it stands now is awful, I
>>>>> hope no-one uses it", the fact is that it is used.
>>>>>
>>>>> [1] http://www.spinics.net/lists/linux-media/msg115550.html
>>>>
>>>> s_routing was developed for USB and PCI(e) devices and predates the device tree.
>>>> Basically USB and PCI drivers will have card definitions where USB/PCI card IDs
>>>> are mapped to card descriptions, and that includes information on the various
>>>> inputs (composite, S-Video, etc) that are available on the backplane and how those
>>>> physical connectors are hooked up to the pins on the video ICs.
>>>>
>>>> The enum/s/g_input ioctls all show the end-user view, i.e. they enumerate the
>>>> inputs on the backpanel of the product. The s_routing op was created to map
>>>> such inputs to actual pins on the ICs.
>>>>
>>>> For platform devices we would do this in the device tree today, but some of
>>>> the necessary bindings are still missing. Specifically those for connectors,
>>>> AFAIK those are not yet defined. It's been discussed, but never finalized.
>>>>
>>>> So if this was done correctly you would use the connector endpoints in the
>>>> device tree to enumerate the inputs and use how they are connected to the
>>>> other blocks as the routing information (i.e. pad number).
>>>>
>>>> I would say that is the advanced course and to do this later.
>>>
>>> Certainly the advanced course, but I'm still not seeing how that all
>>> hangs together.
>>>
>>> To me that all sounds like stuff that ought to be within the ADV
>>> driver? From my perspective as the CSI-2 receiver I only have one
>>> input.
>>
>> As a csi receiver, yes. But the adv has a mux (or at least the adv7180
>> does) where it can switch between multiple Composite or S-Video inputs.
>> s_routing controls the mux.
>>
>> But the end-user knows nothing about the internal routing, he only knows
>> about the connectors on the board. So with VIDIOC_ENUMINPUTS you can
>> see which inputs there are to choose from with names corresponding to
>> the labels on the backplane or in the user manual. With VIDIOC_S_INPUT
>> he can select an input, and the device tree then has to provide the
>> necessary information on how an input connector is hooked up to the adv.
>>
>>> So how does the application select between those inputs?
>>>
>>> Having had a bit of a grep I think the tvp5150 driver is doing what
>>> you're suggesting. However that appears to force you into using the
>>> media controller API. Is that not overkill particularly from an
>>> application perspective?
>>
>> Ah, nice. There are actually already connector bindings. I'd forgotten
>> those went in.
>>
>> Anyway, the tvp5150 is used in two situations:
>>
>> either as part of the USB non-MC device, or as part of a platform MC device.
>>
>> You have a platform non-MC device. The tvp5150 doesn't support that, but
>> if it did, then the DT parsing could wouldn't be under CONFIG_MEDIA_CONTROLLER.
>>
>> It should really be under CONFIG_OF since it is (mostly) unrelated to the MC.
>>
>> But we're missing support for this scenario. For USB/PCI boards we have
>> card descriptions whose information is used in s_routing, for MC-devices
>> we leave it up to the user to set the routing (tvp5150_link_setup). But
>> we don't have anything for a non-MC device that use the device tree.
>>
>> All the information is there, but we're missing infrastructure to
>> give the connector information to the main V4L2 driver so it can implement
>> the input ioctls, and to tell the subdev driver how to program the mux.
> 
> That's the bit that I was stumbling over - there is a bit of
> infrastructure missing.
> 
> So I'm reading that as the adv driver is incorrect in that it is just
> exposing all the potential input configurations. It really needs the
> DT connectors bit adding to refine the input selection down to the
> actually available connectors on the physical board.
> In my eval board case, DT should describe input connectors for 4
> inputs, corresponding to CVBS_AIN1, CVBS_AIN2, DIFF_CVBS_AIN3_AIN4,
> and DIFF_CVBS_AIN7_AIN8. The fact that I could externally connect
> adapters to combine AIN1 and AIN2 to become s-video isn't relevant (or
> at least secondary).
> 
> If we have a bit of infrastructure missing, what is the correct way
> forward for this driver right now?
> I was comparing against the am437x-vfpe, davinci/vpif_capture, and
> blackfin/bfin_capture drivers. All of them in some shape or form take
> VIDIOC_S_INPUT and end up calling s_routing. It may be incorrect, but
> that is how several platforms are using it. Is it acceptable to adopt
> this incorrect behaviour until the infrastructure changes are all
> done?
> 
> As a relative mainline kernel newbie I'm slightly reluctant to
> volunteer to implement the missing infrastructure, but with some
> guidance in advance I'm willing to put together a patchset for
> discussion, and adapt the adv driver to use DT connectors.
> 
> Then again the dtbindings for connectors in tvp5150 got reverted
> although the driver didn't:
> 31e717d [media] Revert "[media] tvp5150: document input connectors DT bindings"
>     "There are still ongoing discussions about how the input connectors
>      will be supported by the Media Controller framework so until that
>      is settled, it is better to revert the connectors portion of the
>      bindings to avoid known to be broken bindings docs to hit mainline."
> 
> Did things ever get settled [1], or did it slip through the cracks?
> There don't appear to be any input connector bindings described
> anywhere I can find them.
> This sounds like a topic to be discussed in a new thread rather than
> hidden within a patch review.

This basically ran out of steam. Lack of time and resources prevented this
from going forward.

Do you *need* to have support for the adv in the first version of this
driver? Basically for drivers like this we have good support for sensors,
and we can handle video receiver devices as long as there is no need to
call s_routing.

Why not get that done first?

> 
> [1] http://www.spinics.net/lists/linux-media/msg97792.html
> 
>>>
>>> You also say above that enum/s/g_input is all about switching between
>>> physical connectors, and that's what I'm doing. I'm now getting lost
>>> as to what is intended
>>
>> Physical connectors as in connectors that users related to: Composite
>> inputs, S-Video, HDMI, etc. Not pins on a chip. How those connectors
>> are routed to pins on a chip is something that differs per board, so
>> that information belongs in the device tree.
>>
>> I hope this clarifies it a bit.
> 
> The waters are still a little murky, but getting clearer. Thanks for
> taking the time to try and clarify it.
> 
> I've been distracted by other things today, and combined with these
> ongoing discissions I'm afraid V2 is going to be delayed until Monday
> or Tuesday. Sorry about that.

No problem.

Regards,

	Hans

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

* Re: [RFC 2/2] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
  2017-06-16 16:08               ` Hans Verkuil
@ 2017-06-16 16:47                 ` Dave Stevenson
  0 siblings, 0 replies; 35+ messages in thread
From: Dave Stevenson @ 2017-06-16 16:47 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

On 16 June 2017 at 17:08, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> On 06/16/2017 05:55 PM, Dave Stevenson wrote:
>>
>> On 16 June 2017 at 15:05, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>>
>>> On 06/15/17 17:11, Dave Stevenson wrote:
>>>>
>>>> On 15 June 2017 at 15:14, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>>>>
>>>>> On 06/15/17 15:38, Dave Stevenson wrote:
>>>>>>
>>>>>> Hi Hans.
>>>>>>
>>>>>> "On 15 June 2017 at 08:12, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>>>>>>
>>>>>>> Hi Dave,
>>>>>>>
>>>>>>> Here is a quick review of this driver. Once a v2 is posted I'll do a
>>>>>>> more
>>>>>>> thorough
>>>>>>> check.
>>>>>>
>>>>>>
>>>>>> Thank you. I wasn't expecting such a quick response.
>>>>>>
>>>>>>> On 06/14/2017 05:15 PM, Dave Stevenson wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>> 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                  |    2 +
>>>>>>>>    drivers/media/platform/bcm2835/Kconfig           |   14 +
>>>>>>>>    drivers/media/platform/bcm2835/Makefile          |    3 +
>>>>>>>>    drivers/media/platform/bcm2835/bcm2835-unicam.c  | 2100
>>>>>>>> ++++++++++++++++++++++
>>>>>>>>    drivers/media/platform/bcm2835/vc4-regs-unicam.h |  257 +++
>>>>>>>>    6 files changed, 2377 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
>>>>>>>>
>>>>>>>> +static int unicam_s_input(struct file *file, void *priv, unsigned
>>>>>>>> int i)
>>>>>>>> +{
>>>>>>>> +       struct unicam_device *dev = video_drvdata(file);
>>>>>>>> +       int ret;
>>>>>>>> +
>>>>>>>> +       if (v4l2_subdev_has_op(dev->sensor, video, s_routing))
>>>>>>>> +               ret =  v4l2_subdev_call(dev->sensor, video,
>>>>>>>> s_routing, i,
>>>>>>>> 0, 0);
>>>>>>>> +       else
>>>>>>>> +               ret = -EINVAL;  /* v4l2-compliance insists on
>>>>>>>> -EINVAL */
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> Drop this if-else entirely. s_routing makes really no sense when
>>>>>>> using a
>>>>>>> device
>>>>>>> tree. In this particular case there really is just one input, period.
>>>>>>
>>>>>>
>>>>>> I added this due to the ADV7282-M analogue to CSI bridge chip (uses
>>>>>> adv7180.c driver). It uses s_routing to select the physical input /
>>>>>> input type.
>>>>>> If this is dropped, what is the correct mechanism for selecting the
>>>>>> input? Unless I've missed it, s_routing is not a call that is exposed
>>>>>> to userspace, so we're stuck with composite input 1.
>>>>>>
>>>>>> I had asked this question in previously [1], and whilst Sakari had
>>>>>> kindly replied with "s_routing() video op as it stands now is awful, I
>>>>>> hope no-one uses it", the fact is that it is used.
>>>>>>
>>>>>> [1] http://www.spinics.net/lists/linux-media/msg115550.html
>>>>>
>>>>>
>>>>> s_routing was developed for USB and PCI(e) devices and predates the
>>>>> device tree.
>>>>> Basically USB and PCI drivers will have card definitions where USB/PCI
>>>>> card IDs
>>>>> are mapped to card descriptions, and that includes information on the
>>>>> various
>>>>> inputs (composite, S-Video, etc) that are available on the backplane
>>>>> and how those
>>>>> physical connectors are hooked up to the pins on the video ICs.
>>>>>
>>>>> The enum/s/g_input ioctls all show the end-user view, i.e. they
>>>>> enumerate the
>>>>> inputs on the backpanel of the product. The s_routing op was created to
>>>>> map
>>>>> such inputs to actual pins on the ICs.
>>>>>
>>>>> For platform devices we would do this in the device tree today, but
>>>>> some of
>>>>> the necessary bindings are still missing. Specifically those for
>>>>> connectors,
>>>>> AFAIK those are not yet defined. It's been discussed, but never
>>>>> finalized.
>>>>>
>>>>> So if this was done correctly you would use the connector endpoints in
>>>>> the
>>>>> device tree to enumerate the inputs and use how they are connected to
>>>>> the
>>>>> other blocks as the routing information (i.e. pad number).
>>>>>
>>>>> I would say that is the advanced course and to do this later.
>>>>
>>>>
>>>> Certainly the advanced course, but I'm still not seeing how that all
>>>> hangs together.
>>>>
>>>> To me that all sounds like stuff that ought to be within the ADV
>>>> driver? From my perspective as the CSI-2 receiver I only have one
>>>> input.
>>>
>>>
>>> As a csi receiver, yes. But the adv has a mux (or at least the adv7180
>>> does) where it can switch between multiple Composite or S-Video inputs.
>>> s_routing controls the mux.
>>>
>>> But the end-user knows nothing about the internal routing, he only knows
>>> about the connectors on the board. So with VIDIOC_ENUMINPUTS you can
>>> see which inputs there are to choose from with names corresponding to
>>> the labels on the backplane or in the user manual. With VIDIOC_S_INPUT
>>> he can select an input, and the device tree then has to provide the
>>> necessary information on how an input connector is hooked up to the adv.
>>>
>>>> So how does the application select between those inputs?
>>>>
>>>> Having had a bit of a grep I think the tvp5150 driver is doing what
>>>> you're suggesting. However that appears to force you into using the
>>>> media controller API. Is that not overkill particularly from an
>>>> application perspective?
>>>
>>>
>>> Ah, nice. There are actually already connector bindings. I'd forgotten
>>> those went in.
>>>
>>> Anyway, the tvp5150 is used in two situations:
>>>
>>> either as part of the USB non-MC device, or as part of a platform MC
>>> device.
>>>
>>> You have a platform non-MC device. The tvp5150 doesn't support that, but
>>> if it did, then the DT parsing could wouldn't be under
>>> CONFIG_MEDIA_CONTROLLER.
>>>
>>> It should really be under CONFIG_OF since it is (mostly) unrelated to the
>>> MC.
>>>
>>> But we're missing support for this scenario. For USB/PCI boards we have
>>> card descriptions whose information is used in s_routing, for MC-devices
>>> we leave it up to the user to set the routing (tvp5150_link_setup). But
>>> we don't have anything for a non-MC device that use the device tree.
>>>
>>> All the information is there, but we're missing infrastructure to
>>> give the connector information to the main V4L2 driver so it can
>>> implement
>>> the input ioctls, and to tell the subdev driver how to program the mux.
>>
>>
>> That's the bit that I was stumbling over - there is a bit of
>> infrastructure missing.
>>
>> So I'm reading that as the adv driver is incorrect in that it is just
>> exposing all the potential input configurations. It really needs the
>> DT connectors bit adding to refine the input selection down to the
>> actually available connectors on the physical board.
>> In my eval board case, DT should describe input connectors for 4
>> inputs, corresponding to CVBS_AIN1, CVBS_AIN2, DIFF_CVBS_AIN3_AIN4,
>> and DIFF_CVBS_AIN7_AIN8. The fact that I could externally connect
>> adapters to combine AIN1 and AIN2 to become s-video isn't relevant (or
>> at least secondary).
>>
>> If we have a bit of infrastructure missing, what is the correct way
>> forward for this driver right now?
>> I was comparing against the am437x-vfpe, davinci/vpif_capture, and
>> blackfin/bfin_capture drivers. All of them in some shape or form take
>> VIDIOC_S_INPUT and end up calling s_routing. It may be incorrect, but
>> that is how several platforms are using it. Is it acceptable to adopt
>> this incorrect behaviour until the infrastructure changes are all
>> done?
>>
>> As a relative mainline kernel newbie I'm slightly reluctant to
>> volunteer to implement the missing infrastructure, but with some
>> guidance in advance I'm willing to put together a patchset for
>> discussion, and adapt the adv driver to use DT connectors.
>>
>> Then again the dtbindings for connectors in tvp5150 got reverted
>> although the driver didn't:
>> 31e717d [media] Revert "[media] tvp5150: document input connectors DT
>> bindings"
>>     "There are still ongoing discussions about how the input connectors
>>      will be supported by the Media Controller framework so until that
>>      is settled, it is better to revert the connectors portion of the
>>      bindings to avoid known to be broken bindings docs to hit mainline."
>>
>> Did things ever get settled [1], or did it slip through the cracks?
>> There don't appear to be any input connector bindings described
>> anywhere I can find them.
>> This sounds like a topic to be discussed in a new thread rather than
>> hidden within a patch review.
>
>
> This basically ran out of steam. Lack of time and resources prevented this
> from going forward.

No problem, I can relate to that.

> Do you *need* to have support for the adv in the first version of this
> driver? Basically for drivers like this we have good support for sensors,
> and we can handle video receiver devices as long as there is no need to
> call s_routing.
>
> Why not get that done first?

I don't *need* to have support for any specific devices at V1, but
obviously I needed something to hook up as a source.
TC358743, OV5647, and ADV7282-M were the devices I had available to
test with, and the first two of those particularly made sense as they
are easily available in a form to connect to the Pi (Auvidea B101/B102
boards, and the V1 Pi camera). The ADV7282 is a nice one to add as it
then gives drivers supporting each of enum/s/g_std,
enum/g/s_dv_timings, and a sensor supporting neither. I was trying to
support the full feature set of each of them.

My issue was that the approach seemingly taken by other existing
drivers wasn't acceptable here. It looks like I'd missed that when
used as platform devices the drivers I'd looked at had routing
information and were doing the mapping required. My bad.

I'll remove the s_routing call for now and add a FIXME detailing the issue.

Thanks for your help and patience over this.

  Dave

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

* Re: [RFC 1/2] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
  2017-06-16 14:40         ` Dave Stevenson
@ 2017-06-16 20:57           ` Sakari Ailus
  0 siblings, 0 replies; 35+ messages in thread
From: Sakari Ailus @ 2017-06-16 20:57 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel, devicetree

Hi Dave,

(Cc the devicetree list, I missed that earlier. It's DT binding
documentation so that list should be cc'd.)

On Fri, Jun 16, 2017 at 03:40:02PM +0100, Dave Stevenson wrote:
> Hi Sakari
> 
> On 16 June 2017 at 10:57, Sakari Ailus <sakari.ailus@iki.fi> wrote:
> > Hi Dave,
> >
> > On Thu, Jun 15, 2017 at 05:15:04PM +0100, Dave Stevenson wrote:
> >> Hi Sakari.
> >>
> >> Thanks for the review.
> >>
> >> On 15 June 2017 at 13:59, Sakari Ailus <sakari.ailus@iki.fi> wrote:
> >> > Hi Dave,
> >> >
> >> > Thanks for the set!
> >> >
> >> > On Wed, Jun 14, 2017 at 04:15:46PM +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   | 76 ++++++++++++++++++++++
> >> >>  1 file changed, 76 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..cc5a451
> >> >> --- /dev/null
> >> >> +++ b/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
> >> >> @@ -0,0 +1,76 @@
> >> >> +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.
> >> >> +
> >> >> +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.
> >> >> +
> >> >> +Optional properties
> >> >> +===================
> >> >> +- max-data-lanes: the hardware can support varying numbers of clock lanes.
> >> >> +               This value is the maximum number supported by this instance.
> >> >> +               Known values of 2 or 4. Default is 2.
> >> >
> >> > Please use "data-lanes" endpoint property instead. This is the number of
> >> > connected physical lanes and specific to the hardware.
> >>
> >> I'll rethink/test, but to explain what I was intending to achieve:
> >>
> >> Registers UNICAM_DAT2 and UNICAM_DAT3 are not valid for instances of
> >> the hardware that only have two lanes instantiated in silicon.
> >> In the case of the whole BCM283x family, Unicam0 ony has 2 lanes
> >> instantiated, whilst Unicam1 has the maximum 4 lanes. (Lower
> >> resolution front cameras would connect to Unicam0, whilst the higher
> >> resolution back cameras would go to Unicam1).
> >>
> >> To further confuse matters, on the Pi platforms (other than the
> >> Compute Module), it is Unicam1 that is brought out to the camera
> >> connector but with only 2 lanes wired up.
> >
> > This information should be present in the DT. I.e. the data-lanes property.
> >
> > v4l2_fwnode_endpoint_alloc_parse() can obtain that from DT, among other
> > things (I haven't checked the second patch yet).
> 
> I was interpreting data-lanes as being a function of the CSI-2 source
> only, not the CSI-2 sink.
> I haven't seen any examples where it has been it has been set on a
> sink, but if that is valid then it sounds like a sensible thing to do.
> ...
> OK, I'd missed it in video_interfaces.txt where it has been set for
> the sh-mobile-csi2 endpoint (not that there appears to be a driver
> advertising sh-mobile-csi2 as a compatible string anymore - it was
> removed in 4.9).
> 
> It sounds like adopting a sink endpoint property of "data-lanes"  is
> reasonable. Make it optional with the driver adopting a default "<1
> 2>" and you'll cover 99% of the cases.
> On Compute Modules it can be overridden to "<1 2 3 4>" for Unicam1.
> Selecting more lanes on Unicam0 is just an error that the DT author
> has to sort out.

Sounds good. There are relevant examples in e.g. here:

Documentation/devicetree/bindings/media/ti,omap3isp.txt
Documentation/devicetree/bindings/media/i2c/nokia,smia.txt


> >>
> >> I was intending to make it possible for the driver to avoid writing to
> >> invalid registers, and also describe the platform limitations to allow
> >> sanity checking.
> >> I haven't tested against Unicam0 as yet to see what actually happens
> >> if we try to write UNICAM_DAT2 or UNICAM_DAT3. If the hardware
> >> silently swallows it then that requirement is null and void. I'll do
> >> some testing tomorrow.
> >> The second bit comes down to how friendly an error you want should
> >> someone write an invalid DT with 'data-lanes' greater than can be
> >> supported by the platform.
> >
> > I'd expect things not to work if there's a grave error in DT. I guess you
> > could assume something but the fact is that you know you don't know.
> >>
> >> > Could you also document which endpoint properties are mandatory and which
> >> > ones optional?
> >>
> >> Will do, although I'm not sure there are any required properties.
> >
> > data-lanes, among other things?
> 
> Looking through struct v4l2_fwnode_bus_mipi_csi2 for the parsed result
> of v4l2_fwnode_endpoint_parse_csi_bus:
> - V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK  /
> V4L2_MBUS_CSI2_CONTINUOUS_CLOCK are the only flags set. The clock is
> generated by the CSI2 source, so not relevant to configure it on the
> sink.
> - data_lane has been discussed above. Lane reordering is not supported
> on the hardware so only the number of entries is of any importance,
> although consecutive lanes can be checked. If a default is adopted by
> the driver, then it is not mandatory.

I think it'd be good to make that explicit albeit not all drivers do. AFAIK
all of them are sensor drivers though, which in practice means there's only
a single configuration (all lanes in use).

> - the clock lane is on a dedicated pair of pins, so it will always be
> <0>. Little point in checking it.

Ack.

> - num_data_lanes comes out of data-lane. Let the driver adopt a default.
> - lane polarity inversion is not supported by the hardware, so it will
> always be an array of 0's. Little point in checking.

Ack.

> 
> "data-lanes" can be mandatory if you think that is of any benefit, but
> personally I don't see it. The other properties give no benefit, so I
> think I am correct with only remote-endpoint being present.

The DT should describe hardware. You can have defaults, but in case when the
parameter is a numerical value (such as the number of lanes), omitting the
property for one particular value is a bit odd IMO.

I wonder what others think.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [RFC 0/2] BCM283x Camera Receiver driver
  2017-06-14 15:15 [RFC 0/2] BCM283x Camera Receiver driver Dave Stevenson
                   ` (2 preceding siblings ...)
  2017-06-14 15:42 ` [RFC 0/2] BCM283x Camera Receiver driver Hans Verkuil
@ 2017-08-28 14:15 ` Hans Verkuil
  2017-08-30  9:40   ` Dave Stevenson
  3 siblings, 1 reply; 35+ messages in thread
From: Hans Verkuil @ 2017-08-28 14:15 UTC (permalink / raw)
  To: Dave Stevenson, linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

Hi Dave,

What is the status of this work? I ask because I tried to use this driver
plus my tc358743 on my rpi-2b without any luck. Specifically the tc358843
isn't able to read from the i2c bus.

This is probably a bug in my dts, if you have a tree somewhere containing
a working dts for this, then that would be very helpful.

Regards,

	Hans

On 14/06/17 17:15, Dave Stevenson wrote:
> Hi All.
> 
> This is adding a V4L2 subdevice driver for the CSI2/CCP2 camera
> receiver peripheral on BCM283x, as used on Raspberry Pi.
> 
> v4l2-compliance results depend on the sensor subdevice this is
> connected to. It passes the basic tests cleanly with TC358743,
> but objects with OV5647
> fail: v4l2-test-controls.cpp(574): g_ext_ctrls does not support count == 0
> Neither OV5647 nor Unicam support any controls.
> 
> I must admit to not having got OV5647 to stream with the current driver
> register settings. It works with a set of register settings for VGA RAW10.
> I also have a couple of patches pending for OV5647, but would like to
> understand the issues better before sending them out.
> 
> Two queries I do have in V4L2-land:
> - When s_dv_timings or s_std is called, is the format meant to
>   be updated automatically? Even if we're already streaming?
>   Some existing drivers seem to, but others don't.
> - With s_fmt, is sizeimage settable by the application in the same
>   way as bytesperline? yavta allows you to specify it on the command
>   line, whilst v4l2-ctl doesn't. Some of the other parts of the Pi
>   firmware have a requirement that the buffer is a multiple of 16 lines
>   high, which can be matched by V4L2 if we can over-allocate the
>   buffers by the app specifying sizeimage. But if I allow that,
>   then I get a v4l2-compliance failure as the size doesn't get
>   reset when switching from RGB3 to UYVY as it takes the request as
>   a request to over-allocate.
> 
> Apologies if I've messed up in sending these patches - so many ways
> to do something.
> 
> Thanks in advance.
>   Dave
> 
> Dave Stevenson (2):
>   [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
>   [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
> 
>  .../devicetree/bindings/media/bcm2835-unicam.txt   |   76 +
>  drivers/media/platform/Kconfig                     |    1 +
>  drivers/media/platform/Makefile                    |    2 +
>  drivers/media/platform/bcm2835/Kconfig             |   14 +
>  drivers/media/platform/bcm2835/Makefile            |    3 +
>  drivers/media/platform/bcm2835/bcm2835-unicam.c    | 2100 ++++++++++++++++++++
>  drivers/media/platform/bcm2835/vc4-regs-unicam.h   |  257 +++
>  7 files changed, 2453 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
> 

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

* Re: [RFC 0/2] BCM283x Camera Receiver driver
  2017-08-28 14:15 ` Hans Verkuil
@ 2017-08-30  9:40   ` Dave Stevenson
  2017-08-30 10:45     ` Hans Verkuil
  0 siblings, 1 reply; 35+ messages in thread
From: Dave Stevenson @ 2017-08-30  9:40 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

Hi Hans.

On 28 August 2017 at 15:15, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> Hi Dave,
>
> What is the status of this work? I ask because I tried to use this driver
> plus my tc358743 on my rpi-2b without any luck. Specifically the tc358843
> isn't able to read from the i2c bus.

I was on other things until last week, but will try to get a V2 sorted
either this week or early next.
The world moved on slightly too, so there are a few more updates
around fwnode stuff that I ought to adopt.

> This is probably a bug in my dts, if you have a tree somewhere containing
> a working dts for this, then that would be very helpful.

Almost certainly just pin ctrl on the I2C bus. The default for i2c0 is
normally to GPIOs 0&1 as that is exposed on the 40 pin header
(physical pins 27&28). The camera is on GPIOs 28&29 (alt0) for the
majority of Pi models (not the Pi3, or the early model B).

I generally do my config via the DT overlays used in the Pi specific
kernels, but that isn't so directly relevant to mainline.
My 4.9 based WIP project that I'm using on a Pi2 is at
https://github.com/6by9/linux/commits/unicam_wip. Mods to:
- arch/arm/boot/dts/bcm283x.dtsi add the CSI nodes
- arch/arm/boot/dts/overlays/tc358743-overlay.dts adds the relevant
config for TC358743
- /arch/arm/boot/dts/overlays/i2c0-bcm2708-overlay.dts is used with
the pins_28_29 parameter to select 28&29 (that needs updating as it
now uses the bcm2835-i2c driver, not bcm2708-i2c)
Sorry it's piece-meal, but that should give you the bits required.

I hope that helps.
  Dave

> Regards,
>
>         Hans
>
> On 14/06/17 17:15, Dave Stevenson wrote:
>> Hi All.
>>
>> This is adding a V4L2 subdevice driver for the CSI2/CCP2 camera
>> receiver peripheral on BCM283x, as used on Raspberry Pi.
>>
>> v4l2-compliance results depend on the sensor subdevice this is
>> connected to. It passes the basic tests cleanly with TC358743,
>> but objects with OV5647
>> fail: v4l2-test-controls.cpp(574): g_ext_ctrls does not support count == 0
>> Neither OV5647 nor Unicam support any controls.
>>
>> I must admit to not having got OV5647 to stream with the current driver
>> register settings. It works with a set of register settings for VGA RAW10.
>> I also have a couple of patches pending for OV5647, but would like to
>> understand the issues better before sending them out.
>>
>> Two queries I do have in V4L2-land:
>> - When s_dv_timings or s_std is called, is the format meant to
>>   be updated automatically? Even if we're already streaming?
>>   Some existing drivers seem to, but others don't.
>> - With s_fmt, is sizeimage settable by the application in the same
>>   way as bytesperline? yavta allows you to specify it on the command
>>   line, whilst v4l2-ctl doesn't. Some of the other parts of the Pi
>>   firmware have a requirement that the buffer is a multiple of 16 lines
>>   high, which can be matched by V4L2 if we can over-allocate the
>>   buffers by the app specifying sizeimage. But if I allow that,
>>   then I get a v4l2-compliance failure as the size doesn't get
>>   reset when switching from RGB3 to UYVY as it takes the request as
>>   a request to over-allocate.
>>
>> Apologies if I've messed up in sending these patches - so many ways
>> to do something.
>>
>> Thanks in advance.
>>   Dave
>>
>> Dave Stevenson (2):
>>   [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver
>>   [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface
>>
>>  .../devicetree/bindings/media/bcm2835-unicam.txt   |   76 +
>>  drivers/media/platform/Kconfig                     |    1 +
>>  drivers/media/platform/Makefile                    |    2 +
>>  drivers/media/platform/bcm2835/Kconfig             |   14 +
>>  drivers/media/platform/bcm2835/Makefile            |    3 +
>>  drivers/media/platform/bcm2835/bcm2835-unicam.c    | 2100 ++++++++++++++++++++
>>  drivers/media/platform/bcm2835/vc4-regs-unicam.h   |  257 +++
>>  7 files changed, 2453 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
>>
>

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

* Re: [RFC 0/2] BCM283x Camera Receiver driver
  2017-08-30  9:40   ` Dave Stevenson
@ 2017-08-30 10:45     ` Hans Verkuil
  2017-08-30 11:04       ` Stefan Wahren
  2017-08-30 11:04       ` Dave Stevenson
  0 siblings, 2 replies; 35+ messages in thread
From: Hans Verkuil @ 2017-08-30 10:45 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

On 30/08/17 11:40, Dave Stevenson wrote:
> Hi Hans.
> 
> On 28 August 2017 at 15:15, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>> Hi Dave,
>>
>> What is the status of this work? I ask because I tried to use this driver
>> plus my tc358743 on my rpi-2b without any luck. Specifically the tc358843
>> isn't able to read from the i2c bus.
> 
> I was on other things until last week, but will try to get a V2 sorted
> either this week or early next.
> The world moved on slightly too, so there are a few more updates
> around fwnode stuff that I ought to adopt.
> 
>> This is probably a bug in my dts, if you have a tree somewhere containing
>> a working dts for this, then that would be very helpful.
> 
> Almost certainly just pin ctrl on the I2C bus. The default for i2c0 is
> normally to GPIOs 0&1 as that is exposed on the 40 pin header
> (physical pins 27&28). The camera is on GPIOs 28&29 (alt0) for the
> majority of Pi models (not the Pi3, or the early model B).

Yep, that was the culprit!

I now see the tc, but streaming doesn't work yet. I'm not getting any
interrupts in the unicam driver.

BTW, when s_dv_timings is called, then you need to update the v4l2_format
as well to the new width and height. I noticed that that didn't happen.

Anyway, this is good enough for me for now since I want to add CEC support
to the tc driver, and I do not need streaming for that...

Regards,

	Hans

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

* Re: [RFC 0/2] BCM283x Camera Receiver driver
  2017-08-30 10:45     ` Hans Verkuil
@ 2017-08-30 11:04       ` Stefan Wahren
  2017-08-30 11:04       ` Dave Stevenson
  1 sibling, 0 replies; 35+ messages in thread
From: Stefan Wahren @ 2017-08-30 11:04 UTC (permalink / raw)
  To: Hans Verkuil, Dave Stevenson
  Cc: Mauro Carvalho Chehab, linux-rpi-kernel, linux-media

Hi Hans,

Am 30.08.2017 um 12:45 schrieb Hans Verkuil:
> On 30/08/17 11:40, Dave Stevenson wrote:
>> Hi Hans.
>>
>> On 28 August 2017 at 15:15, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>> Hi Dave,
>>>
>>> What is the status of this work? I ask because I tried to use this driver
>>> plus my tc358743 on my rpi-2b without any luck. Specifically the tc358843
>>> isn't able to read from the i2c bus.
>> I was on other things until last week, but will try to get a V2 sorted
>> either this week or early next.
>> The world moved on slightly too, so there are a few more updates
>> around fwnode stuff that I ought to adopt.
>>
>>> This is probably a bug in my dts, if you have a tree somewhere containing
>>> a working dts for this, then that would be very helpful.
>> Almost certainly just pin ctrl on the I2C bus. The default for i2c0 is
>> normally to GPIOs 0&1 as that is exposed on the 40 pin header
>> (physical pins 27&28). The camera is on GPIOs 28&29 (alt0) for the
>> majority of Pi models (not the Pi3, or the early model B).
> Yep, that was the culprit!
>
> I now see the tc, but streaming doesn't work yet. I'm not getting any
> interrupts in the unicam driver.
>
> BTW, when s_dv_timings is called, then you need to update the v4l2_format
> as well to the new width and height. I noticed that that didn't happen.
>
> Anyway, this is good enough for me for now since I want to add CEC support
> to the tc driver, and I do not need streaming for that...

i'm not sure this is related, but there is an issue in VCHIQ with 
HIGHMEM and VMSPLIT on RPi 2 [1].

Maybe the mentioned kmap fix could be helpful.

Btw: The VCHIQ driver in Dave's repo is a bit outdated.

[1] - https://github.com/raspberrypi/linux/issues/1996

>
> Regards,
>
> 	Hans
>
> _______________________________________________
> linux-rpi-kernel mailing list
> linux-rpi-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rpi-kernel

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

* Re: [RFC 0/2] BCM283x Camera Receiver driver
  2017-08-30 10:45     ` Hans Verkuil
  2017-08-30 11:04       ` Stefan Wahren
@ 2017-08-30 11:04       ` Dave Stevenson
  2017-08-30 11:25         ` Hans Verkuil
  2017-08-31 14:18         ` Hans Verkuil
  1 sibling, 2 replies; 35+ messages in thread
From: Dave Stevenson @ 2017-08-30 11:04 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

On 30 August 2017 at 11:45, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> On 30/08/17 11:40, Dave Stevenson wrote:
>> Hi Hans.
>>
>> On 28 August 2017 at 15:15, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>> Hi Dave,
>>>
>>> What is the status of this work? I ask because I tried to use this driver
>>> plus my tc358743 on my rpi-2b without any luck. Specifically the tc358843
>>> isn't able to read from the i2c bus.
>>
>> I was on other things until last week, but will try to get a V2 sorted
>> either this week or early next.
>> The world moved on slightly too, so there are a few more updates
>> around fwnode stuff that I ought to adopt.
>>
>>> This is probably a bug in my dts, if you have a tree somewhere containing
>>> a working dts for this, then that would be very helpful.
>>
>> Almost certainly just pin ctrl on the I2C bus. The default for i2c0 is
>> normally to GPIOs 0&1 as that is exposed on the 40 pin header
>> (physical pins 27&28). The camera is on GPIOs 28&29 (alt0) for the
>> majority of Pi models (not the Pi3, or the early model B).
>
> Yep, that was the culprit!

Great. I like easy ones :-)

> I now see the tc, but streaming doesn't work yet. I'm not getting any
> interrupts in the unicam driver.

What resolution were you streaming at? There are a couple of things
that come to mind and I don't have solutions for yet.
Firstly the TC358743 driver assumes that all 4 CSI lanes are
available, whilst the Pi only has 2 lanes exposed. IIRC 1080P30 RGB
just trickles over into wanting 3 lanes with the default link
frequencies and you indeed don't get anything if one or more lane is
physically not connected.
Secondly was the FIFOCTL register set via state->pdata.fifo_level that
we discussed before. The default is 16, which is too small for some of
the smaller resolutions. 300 seems reasonable. You do get frames in
that situation, but they're generally corrupt.
I'm intending to try and make the TC358743 more flexible than just
accepting >720P, and if I can support multiple link frequencies then
so much the better as 1080P50 UYVY should be just possible on 2 lane,
however the lower resolutions are unlikely to work due to FIFO
underflow.

> BTW, when s_dv_timings is called, then you need to update the v4l2_format
> as well to the new width and height. I noticed that that didn't happen.

Yes, that came up in the previous discussions and is already fixed.

> Anyway, this is good enough for me for now since I want to add CEC support
> to the tc driver, and I do not need streaming for that...

That sounds fun.
  Dave

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

* Re: [RFC 0/2] BCM283x Camera Receiver driver
  2017-08-30 11:04       ` Dave Stevenson
@ 2017-08-30 11:25         ` Hans Verkuil
  2017-08-31 14:18         ` Hans Verkuil
  1 sibling, 0 replies; 35+ messages in thread
From: Hans Verkuil @ 2017-08-30 11:25 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

On 30/08/17 13:04, Dave Stevenson wrote:
> On 30 August 2017 at 11:45, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>> On 30/08/17 11:40, Dave Stevenson wrote:
>>> Hi Hans.
>>>
>>> On 28 August 2017 at 15:15, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>>> Hi Dave,
>>>>
>>>> What is the status of this work? I ask because I tried to use this driver
>>>> plus my tc358743 on my rpi-2b without any luck. Specifically the tc358843
>>>> isn't able to read from the i2c bus.
>>>
>>> I was on other things until last week, but will try to get a V2 sorted
>>> either this week or early next.
>>> The world moved on slightly too, so there are a few more updates
>>> around fwnode stuff that I ought to adopt.
>>>
>>>> This is probably a bug in my dts, if you have a tree somewhere containing
>>>> a working dts for this, then that would be very helpful.
>>>
>>> Almost certainly just pin ctrl on the I2C bus. The default for i2c0 is
>>> normally to GPIOs 0&1 as that is exposed on the 40 pin header
>>> (physical pins 27&28). The camera is on GPIOs 28&29 (alt0) for the
>>> majority of Pi models (not the Pi3, or the early model B).
>>
>> Yep, that was the culprit!
> 
> Great. I like easy ones :-)
> 
>> I now see the tc, but streaming doesn't work yet. I'm not getting any
>> interrupts in the unicam driver.
> 
> What resolution were you streaming at? There are a couple of things
> that come to mind and I don't have solutions for yet.

I tried 720p60 (both RGB and YUV) and 640x480p60 (aka VGA).

I gather that the last probably won't work, but 720p60 should?

If time permits I'll try to look into this a bit more later today. First CEC
though :-)

Regards,

	Hans

> Firstly the TC358743 driver assumes that all 4 CSI lanes are
> available, whilst the Pi only has 2 lanes exposed. IIRC 1080P30 RGB
> just trickles over into wanting 3 lanes with the default link
> frequencies and you indeed don't get anything if one or more lane is
> physically not connected.
> Secondly was the FIFOCTL register set via state->pdata.fifo_level that
> we discussed before. The default is 16, which is too small for some of
> the smaller resolutions. 300 seems reasonable. You do get frames in
> that situation, but they're generally corrupt.
> I'm intending to try and make the TC358743 more flexible than just
> accepting >720P, and if I can support multiple link frequencies then
> so much the better as 1080P50 UYVY should be just possible on 2 lane,
> however the lower resolutions are unlikely to work due to FIFO
> underflow.
> 
>> BTW, when s_dv_timings is called, then you need to update the v4l2_format
>> as well to the new width and height. I noticed that that didn't happen.
> 
> Yes, that came up in the previous discussions and is already fixed.
> 
>> Anyway, this is good enough for me for now since I want to add CEC support
>> to the tc driver, and I do not need streaming for that...
> 
> That sounds fun.
>   Dave
> 

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

* Re: [RFC 0/2] BCM283x Camera Receiver driver
  2017-08-30 11:04       ` Dave Stevenson
  2017-08-30 11:25         ` Hans Verkuil
@ 2017-08-31 14:18         ` Hans Verkuil
  1 sibling, 0 replies; 35+ messages in thread
From: Hans Verkuil @ 2017-08-31 14:18 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: linux-media, Mauro Carvalho Chehab, linux-rpi-kernel

On 30/08/17 13:04, Dave Stevenson wrote:
> On 30 August 2017 at 11:45, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>> On 30/08/17 11:40, Dave Stevenson wrote:
>>> Hi Hans.
>>>
>>> On 28 August 2017 at 15:15, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>>> Hi Dave,
>>>>
>>>> What is the status of this work? I ask because I tried to use this driver
>>>> plus my tc358743 on my rpi-2b without any luck. Specifically the tc358843
>>>> isn't able to read from the i2c bus.
>>>
>>> I was on other things until last week, but will try to get a V2 sorted
>>> either this week or early next.
>>> The world moved on slightly too, so there are a few more updates
>>> around fwnode stuff that I ought to adopt.
>>>
>>>> This is probably a bug in my dts, if you have a tree somewhere containing
>>>> a working dts for this, then that would be very helpful.
>>>
>>> Almost certainly just pin ctrl on the I2C bus. The default for i2c0 is
>>> normally to GPIOs 0&1 as that is exposed on the 40 pin header
>>> (physical pins 27&28). The camera is on GPIOs 28&29 (alt0) for the
>>> majority of Pi models (not the Pi3, or the early model B).
>>
>> Yep, that was the culprit!
> 
> Great. I like easy ones :-)
> 
>> I now see the tc, but streaming doesn't work yet. I'm not getting any
>> interrupts in the unicam driver.
> 
> What resolution were you streaming at? There are a couple of things
> that come to mind and I don't have solutions for yet.
> Firstly the TC358743 driver assumes that all 4 CSI lanes are
> available, whilst the Pi only has 2 lanes exposed. IIRC 1080P30 RGB
> just trickles over into wanting 3 lanes with the default link
> frequencies and you indeed don't get anything if one or more lane is
> physically not connected.
> Secondly was the FIFOCTL register set via state->pdata.fifo_level that
> we discussed before. The default is 16, which is too small for some of
> the smaller resolutions. 300 seems reasonable. You do get frames in
> that situation, but they're generally corrupt.
> I'm intending to try and make the TC358743 more flexible than just
> accepting >720P, and if I can support multiple link frequencies then
> so much the better as 1080P50 UYVY should be just possible on 2 lane,
> however the lower resolutions are unlikely to work due to FIFO
> underflow.

Found the cause: bcm2835-rpi.dtsi had csi power-domains entries that
I missed. So the csi wasn't powered on :-)

After adding that it now works!

Regards,

	Hans

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

end of thread, other threads:[~2017-08-31 14:18 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-14 15:15 [RFC 0/2] BCM283x Camera Receiver driver Dave Stevenson
2017-06-14 15:15 ` [RFC 1/2] [media] dt-bindings: Document BCM283x CSI2/CCP2 receiver Dave Stevenson
2017-06-15  6:34   ` Stefan Wahren
2017-06-15 12:35     ` Dave Stevenson
2017-06-15 12:59   ` Sakari Ailus
2017-06-15 16:15     ` Dave Stevenson
2017-06-16  9:57       ` Sakari Ailus
2017-06-16 14:40         ` Dave Stevenson
2017-06-16 20:57           ` Sakari Ailus
2017-06-14 15:15 ` [RFC 2/2] [media] bcm2835-unicam: Driver for CCP2/CSI2 camera interface Dave Stevenson
2017-06-15  7:12   ` Hans Verkuil
2017-06-15  7:20     ` Stefan Wahren
2017-06-15 13:38     ` Dave Stevenson
2017-06-15 14:14       ` Hans Verkuil
2017-06-15 15:11         ` Dave Stevenson
2017-06-16 14:05           ` Hans Verkuil
2017-06-16 15:55             ` Dave Stevenson
2017-06-16 16:08               ` Hans Verkuil
2017-06-16 16:47                 ` Dave Stevenson
2017-06-15 14:49       ` Stefan Wahren
2017-06-15 15:40         ` Dave Stevenson
2017-06-14 15:42 ` [RFC 0/2] BCM283x Camera Receiver driver Hans Verkuil
2017-06-14 16:29   ` Dave Stevenson
2017-06-14 17:38     ` Hans Verkuil
2017-06-14 21:03       ` Dave Stevenson
2017-06-15  7:17         ` Hans Verkuil
2017-06-15 12:29           ` Dave Stevenson
2017-06-15 12:30             ` Hans Verkuil
2017-08-28 14:15 ` Hans Verkuil
2017-08-30  9:40   ` Dave Stevenson
2017-08-30 10:45     ` Hans Verkuil
2017-08-30 11:04       ` Stefan Wahren
2017-08-30 11:04       ` Dave Stevenson
2017-08-30 11:25         ` Hans Verkuil
2017-08-31 14:18         ` Hans Verkuil

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.